Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 10 additions & 10 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,13 +17,13 @@ tag. They depend on each other by version, so install the ones you use
**and the packages they depend on** from the same tag:

```bash
pip install "lambdafeedback-slr-parsing @ git+https://github.com/lambda-feedback/lambda-feedback-packages.git@v0.3.0-rc.3#subdirectory=slr_parsing"
pip install "lambdafeedback-criteria @ git+https://github.com/lambda-feedback/lambda-feedback-packages.git@v0.3.0-rc.3#subdirectory=criteria"
pip install "lambdafeedback-expression-parsing @ git+https://github.com/lambda-feedback/lambda-feedback-packages.git@v0.3.0-rc.3#subdirectory=expression_parsing"
pip install "lambdafeedback-units @ git+https://github.com/lambda-feedback/lambda-feedback-packages.git@v0.3.0-rc.3#subdirectory=units"
pip install "lambdafeedback-slr-parsing @ git+https://github.com/lambda-feedback/lambda-feedback-packages.git@v0.3.0-rc.4#subdirectory=slr_parsing"
pip install "lambdafeedback-criteria @ git+https://github.com/lambda-feedback/lambda-feedback-packages.git@v0.3.0-rc.4#subdirectory=criteria"
pip install "lambdafeedback-expression-parsing @ git+https://github.com/lambda-feedback/lambda-feedback-packages.git@v0.3.0-rc.4#subdirectory=expression_parsing"
pip install "lambdafeedback-physical-quantities @ git+https://github.com/lambda-feedback/lambda-feedback-packages.git@v0.3.0-rc.4#subdirectory=physical_quantities"
```

With Poetry: `lambdafeedback-units = { git = "https://github.com/lambda-feedback/lambda-feedback-packages.git", tag = "v0.3.0-rc.3", subdirectory = "units" }`, and likewise for each package.
With Poetry: `lambdafeedback-physical-quantities = { git = "https://github.com/lambda-feedback/lambda-feedback-packages.git", tag = "v0.3.0-rc.4", subdirectory = "physical_quantities" }`, and likewise for each package.

## Packages

Expand All @@ -32,7 +32,7 @@ With Poetry: `lambdafeedback-units = { git = "https://github.com/lambda-feedback
| `slr_parsing` | `lambdafeedback-slr-parsing` | `lambdafeedback.slr_parsing` | Generic SLR(1) parser engine | — |
| `criteria` | `lambdafeedback-criteria` | `lambdafeedback.criteria` | Criteria DSL parser and evaluation graph | `slr_parsing` |
| `expression_parsing` | `lambdafeedback-expression-parsing` | `lambdafeedback.expression_parsing` | Safe SymPy parsing, preprocessing and LaTeX input/output | `slr_parsing`, `sympy`, `latex2sympy2_extended` |
| `units` | `lambdafeedback-units` | `lambdafeedback.units` | Unit-system data, physical-quantity parsing and dimensional analysis | `slr_parsing`, `expression_parsing`, `sympy` |
| `physical_quantities` | `lambdafeedback-physical-quantities` | `lambdafeedback.physical_quantities` | Unit-system data, physical-quantity parsing and dimensional analysis | `slr_parsing`, `expression_parsing`, `sympy` |

```python
from lambdafeedback.expression_parsing import (
Expand All @@ -41,7 +41,7 @@ from lambdafeedback.expression_parsing import (
parse_expression,
preprocess_expression,
)
from lambdafeedback.units import QuantityParams, parse_quantity
from lambdafeedback.physical_quantities import QuantityParams, parse_quantity

params = ExpressionParams.from_dict(evaluation_params) # JSON parameters → typed
pre = preprocess_expression(response, params, name="response") # aliases, brackets, |x|
Expand All @@ -54,7 +54,7 @@ quantity = parse_quantity("9.81 m/s^2", QuantityParams.from_dict(evaluation_para
from [toolkit-python](https://github.com/lambda-feedback/toolkit-python), with
`CriteriaGraph.export_feedback` for criteria feedback.

Each package uses a `src/` layout: `units/src/lambdafeedback/units/`.
Each package uses a `src/` layout: `physical_quantities/src/lambdafeedback/physical_quantities/`.
There is deliberately no `lambdafeedback/__init__.py`; the namespace is
[PEP 420](https://peps.python.org/pep-0420/), so the separately installed
packages merge under one import root.
Expand All @@ -69,7 +69,7 @@ that installs every package in develop mode, plus the tooling, into one venv:
```bash
poetry install # creates the venv with all packages + dev tools
poetry run scripts/test.sh -q # every package's tests (one pytest process each)
poetry run pytest units/tests # a single package
poetry run pytest physical_quantities/tests # a single package
poetry run ruff check . && poetry run ruff format --check .
poetry run mypy
poetry run python tools/parity/check.py # diff behaviour against the v0.1 baseline
Expand Down Expand Up @@ -121,7 +121,7 @@ attached to a GitHub Release (marked as a pre-release for pre-release tags).
## Logging

The packages log at DEBUG only, through loggers named after their modules
(`lambdafeedback.expression_parsing.preprocessing`, `lambdafeedback.units.quantity`, ...):
(`lambdafeedback.expression_parsing.preprocessing`, `lambdafeedback.physical_quantities.quantity`, ...):
how a response was preprocessed and read, how a quantity was split into value
and unit, which evaluations a criteria graph ran. Failures are exceptions, not
log records. To see them:
Expand Down
2 changes: 1 addition & 1 deletion criteria/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# Changelog: lambdafeedback-criteria

## 0.3.0 (unreleased; pre-release 0.3.0rc3, tag v0.3.0-rc.3)
## 0.3.0 (unreleased; pre-release 0.3.0rc4, tag v0.3.0-rc.4)

The first public release, after the extraction refactor. The import path is now `lambdafeedback.criteria` (was `criteria`). There was no 0.2.0 release: all four `lambdafeedback-*` packages share one version, and the first release is 0.3.0. Supports Python 3.12 to 3.14 (3.11 is not supported); `__version__` gives the installed version.

Expand Down
2 changes: 1 addition & 1 deletion criteria/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ evaluation function.
From git (not on PyPI yet):

```bash
pip install "lambdafeedback-criteria @ git+https://github.com/lambda-feedback/lambda-feedback-packages.git@v0.3.0-rc.3#subdirectory=criteria"
pip install "lambdafeedback-criteria @ git+https://github.com/lambda-feedback/lambda-feedback-packages.git@v0.3.0-rc.4#subdirectory=criteria"
```

Install the packages it depends on (`slr_parsing`) from the same tag; see the [repository README](../README.md#install).
Expand Down
4 changes: 2 additions & 2 deletions criteria/pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ build-backend = "poetry.core.masonry.api"

[project]
name = "lambdafeedback-criteria"
version = "0.3.0rc3"
version = "0.3.0rc4"
description = "Criteria parser and criteria graphs that turn evaluations into feedback, for Lambda Feedback evaluation functions."
readme = "README.md"
license = "MIT"
Expand All @@ -25,7 +25,7 @@ classifiers = [
]
requires-python = ">=3.12"
dependencies = [
"lambdafeedback-slr-parsing (>=0.3.0rc3,<0.4.0)",
"lambdafeedback-slr-parsing (>=0.3.0rc4,<0.4.0)",
]

[project.urls]
Expand Down
6 changes: 3 additions & 3 deletions expression_parsing/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# Changelog: lambdafeedback-expression-parsing

## 0.3.0 (unreleased; pre-release 0.3.0rc3, tag v0.3.0-rc.3)
## 0.3.0 (unreleased; pre-release 0.3.0rc4, tag v0.3.0-rc.4)

The first public release. Further tidy-up after the 0.2.0 extraction: previewing a response and mapping failures to learner-facing feedback text are app-layer concerns, so they move out of this package. `__version__` gives the installed version.

Expand Down Expand Up @@ -59,9 +59,9 @@ The first public release. Further tidy-up after the 0.2.0 extraction: previewing
- (since 0.3.0rc1) `parse_latex` leaves division between numbers unevaluated, as the lambda-feedback fork did: `\frac{10}{2}`, `10/2` and `10 \div 2` give `10/2`, and `\frac{3}{6}` gives `3/6`. In rc1 latex2sympy2_extended divided two integers as a `Rational` (`5` and `1/2`), so a "written as" criterion couldn't tell `\frac{3}{6}` from `\frac{1}{2}`. `2\frac{1}{2}` is the product `2*1/2` again (rc1 read it as the mixed number `5/2`).
- `parse_expression` leaves every SymPy function unevaluated, not only the ones SymPy's own `evaluate=False` covers (trigonometric, `exp`, `log`, ...). `factorial`, `binomial` and the number-theory functions computed while parsing: `10000000!` took minutes, and `100000!` gave an integer too long to print. As a result `5!` now parses as `factorial(5)`, not `120`, like `sin(0)` stays `sin(0)`; with `simplify` they are still computed. `parse_latex` likewise leaves `\binom` unevaluated (`\binom{5}{2}` gives `binomial(5, 2)`, not `10`): latex2sympy2_extended computed it, and `\binom{100000000}{50000000}` didn't finish. Operator classes called by name (`Pow`, `Add`, `Eq`, ...) are left unevaluated too: `Pow(9,Pow(9,9))` never finished, in every mode.
- **One LaTeX differential broke every later LaTeX parse in the process.** latex2sympy2 (and the lambda-feedback fork) overwrote a module-level variable when parsing `\mathrm{d}x`, after which every `parse_latex` call failed with "argument of type 'Symbol' is not iterable"; in a warm evaluation-function container, one learner's answer broke LaTeX for every later request. latex2sympy2_extended keeps no state between calls. (Upstream issue OrangeX4/latex2sympy#4, closed unfixed.)
- `substitute` scales with the input's length. It tried every substitution at every position, and a `FunctionName` copied the rest of the input on each check, so a long response with many substitutions was slow: 100000 characters with the units package's ~1000 reserved names took over 10 s. Substitutions are now indexed by their first character (keeping their order, so results are unchanged).
- `substitute` scales with the input's length. It tried every substitution at every position, and a `FunctionName` copied the rest of the input on each check, so a long response with many substitutions was slow: 100000 characters with the physical quantities package's ~1000 reserved names took over 10 s. Substitutions are now indexed by their first character (keeping their order, so results are unchanged).
- `is_complex_number_on_exponential_form` took exponential time on inputs like `2*exp(` followed by a run of digits (26 digits took over 10 s), which a learner's response could trigger through "written as" criteria. `NUMBER_REGEX` and `NONNEGATIVE_NUMBER_REGEX` could match an empty string after a digit, and the check repeated them; they now never match empty (they accept the same numbers), and the complex-form checks match each number atomically. `is_complex_number_on_cartesian_form` no longer accepts an empty string or a lone `+`, and both checks no longer accept numbers run together (`5E7e7`, `exp04I`). `generate_arbitrary_number_pattern_matcher` matches each number atomically too: a pattern whose numbers are separated only by spaces (`1 2 3`) took polynomial time on a long run of digits (1000 digits: over 8 s), and matched a single number (`3`) through the empty-match bug.
- `ExpressionParams` (and so `SympyParsingConfig`, and subclasses such as `lambdafeedback.units.QuantityParams`) can be pickled and deep-copied. `symbols` is a read-only `MappingProxyType`, which pickle can't handle; it is stored as a `dict` and wrapped again on unpickling, without validating again.
- `ExpressionParams` (and so `SympyParsingConfig`, and subclasses such as `lambdafeedback.physical_quantities.QuantityParams`) can be pickled and deep-copied. `symbols` is a read-only `MappingProxyType`, which pickle can't handle; it is stored as a `dict` and wrapped again on unpickling, without validating again.
- A symbol code that merely contains `lambda` (e.g. `lambda_1`) keeps its name. Every substitution's replacement had `lambda` rewritten to `lamda`, so `lambda_1` became `lamda_1`, which was then split as `lamda*_1` and lost its LaTeX.
- A task symbol whose code is a Python keyword (`in` for inches, `del`, `as`, ...) parses as that symbol, keeping its LaTeX and assumptions; it used to raise (or, for `as`, become `a*s`).
- `sympy_to_latex` prints symbols that have assumptions (from `symbol_assumptions`) with the task's LaTeX. The printer matched symbols by equality, and `Symbol('x', positive=True)` is not `Symbol('x')`.
Expand Down
2 changes: 1 addition & 1 deletion expression_parsing/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ functions such as compareExpressions.
From git (not on PyPI yet):

```bash
pip install "lambdafeedback-expression-parsing @ git+https://github.com/lambda-feedback/lambda-feedback-packages.git@v0.3.0-rc.3#subdirectory=expression_parsing"
pip install "lambdafeedback-expression-parsing @ git+https://github.com/lambda-feedback/lambda-feedback-packages.git@v0.3.0-rc.4#subdirectory=expression_parsing"
```

Install the packages it depends on (`slr_parsing`) from the same tag; see the [repository README](../README.md#install).
Expand Down
4 changes: 2 additions & 2 deletions expression_parsing/pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ build-backend = "poetry.core.masonry.api"

[project]
name = "lambdafeedback-expression-parsing"
version = "0.3.0rc3"
version = "0.3.0rc4"
description = "Safe SymPy parsing and preprocessing of learner-written mathematical expressions, for Lambda Feedback evaluation functions."
readme = "README.md"
license = "MIT"
Expand All @@ -27,7 +27,7 @@ classifiers = [
]
requires-python = ">=3.12"
dependencies = [
"lambdafeedback-slr-parsing (>=0.3.0rc3,<0.4.0)",
"lambdafeedback-slr-parsing (>=0.3.0rc4,<0.4.0)",
"sympy (>=1.14,<2.0)",
"latex2sympy2-extended[antlr4-13-2] (>=1.11.0,<2.0)",
]
Expand Down
11 changes: 6 additions & 5 deletions units/CHANGELOG.md → physical_quantities/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,11 +1,12 @@
# Changelog: lambdafeedback-units
# Changelog: lambdafeedback-physical-quantities

## 0.3.0 (unreleased; pre-release 0.3.0rc3, tag v0.3.0-rc.3)
## 0.3.0 (unreleased; pre-release 0.3.0rc4, tag v0.3.0-rc.4)

The first public release. This is a further tidy-up after the 0.2.0 extraction: the package now only does unit conversion. Package metadata (MIT licence, README, URLs) is in place for PyPI. Python 3.13 and later is supported, now that `lambdafeedback-expression-parsing` no longer depends on latex2sympy2. SymPy 1.14 or later is required (was 1.12): 1.12 predates Python 3.12, and a configuration with a declared function (`symbol_assumptions` `function`) can only be pickled from 1.14.

### Breaking

- (since 0.3.0rc4) **Renamed from `lambdafeedback-units` to `lambdafeedback-physical-quantities`.** The import path is `lambdafeedback.physical_quantities` (was `lambdafeedback.units`), and the repository subdirectory is `physical_quantities` (was `units`). The API is otherwise unchanged.
- Requires Python 3.12 or later (3.11 is not supported).
- `PhysicalQuantity.standard_values` is a tuple of expressions (several for a value such as `{1, 2}`), replacing `standard_value`, which was one expression or a set.

Expand All @@ -20,7 +21,7 @@ The first public release. This is a further tidy-up after the 0.2.0 extraction:
- An unknown `strictness` raises `QuantityParamsError` (with `.field`). It is still an `ExpressionParsingError`, so existing handlers keep working.
- `UNIT_SETS` and `CONVERSION_TO_BASE_SI` are read-only mappings (`MappingProxyType`). Assigning to them raises `TypeError`, where before it would have silently changed every cached parser.
- `parse_quantity` and `preprocess_quantity` now take `QuantityParams` only; the `Mapping[str, Any]` (raw dict) fallback is gone, along with the `as_quantity_params` helper. Convert evaluation-function parameters explicitly at the call site with `QuantityParams.from_dict(evaluation_params)`.
- `preview_function` and `fix_exponents` have moved out of this package entirely, to compareExpressions (`app/context/physical_quantity_preview.py`): building a LaTeX/SymPy preview string is an app-layer display concern, not unit conversion. Import them from there instead of `lambdafeedback.units`.
- `preview_function` and `fix_exponents` have moved out of this package entirely, to compareExpressions (`app/context/physical_quantity_preview.py`): building a LaTeX/SymPy preview string is an app-layer display concern, not unit conversion. Import them from there instead of `lambdafeedback.physical_quantities`.
- `preprocess_quantity` now returns a plain `str` (`expression_parsing.preprocess_expression`, which it matches the shape of, no longer returns `Preprocessed`).
- `FeedbackTag` is removed, matching `expression_parsing`'s move from feedback tags to typed exceptions: mapping a parsing fact to feedback text is an app-layer concern, not this package's. `PhysicalQuantity.messages` (`(message_id, FeedbackTag)` pairs) is replaced by `PhysicalQuantity.reverted_units: tuple[RevertedUnit, ...]` — plain `before`/`marked`/`after` facts about unit-like text found inside a value, with no tag name or synthetic id. This isn't a raised exception (unlike `expression_parsing`'s failures): it's non-fatal and can fire multiple times per response, so it stays a collection.
- **`PhysicalQuantity` is a frozen result.** Its attributes can't be set (`dataclasses.FrozenInstanceError`); `value`, `unit` and `ast_root` are still ordinary parse trees. `parse_quantity` builds it through the new `PhysicalQuantity.from_tree(name, params, ast_root, parser)`, which runs each step (splitting value from unit, restoring the value's text, LaTeX, SI forms) as a separate function; the constructor now takes every attribute. Quantities still compare by identity, and pickle and deep-copy without a custom `__getstate__`/`__setstate__`.
Expand All @@ -29,7 +30,7 @@ The first public release. This is a further tidy-up after the 0.2.0 extraction:

### Added

- DEBUG logging through module loggers (`lambdafeedback.units.*`), with a `NullHandler` on the package logger; nothing is logged at INFO or above. See the repository README. `parse_quantity` logs the normalised text and the value/unit split.
- DEBUG logging through module loggers (`lambdafeedback.physical_quantities.*`), with a `NullHandler` on the package logger; nothing is logged at INFO or above. See the repository README. `parse_quantity` logs the normalised text and the value/unit split.
- `DisallowedUnitError` (see Breaking).
- `PhysicalQuantity.base_dimension`: the dimension without the unit's factor (`5 km/h` gives `length/time`, where `dimension` is `10**3*length/((3600*time))`), and `same_dimension(a, b)` to compare two quantities' dimensions.
- `MAX_INPUT_LENGTH` (10,000 characters): `parse_quantity` raises `QuantityParseError` for longer input, and `preprocess_quantity` returns it unchanged. This bounds the running time on any input.
Expand Down Expand Up @@ -62,7 +63,7 @@ The first public release. This is a further tidy-up after the 0.2.0 extraction:

### Changed

- `quantity` keeps the public `PhysicalQuantity`, `RevertedUnit`, `parse_quantity` and `same_dimension`; the steps of reading a quantity are in private modules (`_split`, `_latex`, `_conversion`). Import public names from `lambdafeedback.units`: `MAX_UNIT_EXPONENT` is no longer in `lambdafeedback.units.quantity`.
- `quantity` keeps the public `PhysicalQuantity`, `RevertedUnit`, `parse_quantity` and `same_dimension`; the steps of reading a quantity are in private modules (`_split`, `_latex`, `_conversion`). Import public names from `lambdafeedback.physical_quantities`: `MAX_UNIT_EXPONENT` is no longer in `lambdafeedback.physical_quantities.quantity`.
- The regular expressions in preprocessing and parsing are replaced by string scanners, with the same results. The quantity grammar's tokens are `slr_parsing.Lexeme`s (requires `lambdafeedback-slr-parsing` with literal token patterns).

### Deprecated
Expand Down
File renamed without changes.
Loading
Loading