|
1 | 1 | ## [Unreleased] |
2 | 2 |
|
| 3 | +### Improvements |
| 4 | + |
| 5 | +- **Value Resolution from Equality Assumptions**: When an equality assumption |
| 6 | + is made via `ce.assume(['Equal', symbol, value])`, the symbol now correctly |
| 7 | + evaluates to the assumed value. Previously, the symbol would remain unchanged |
| 8 | + even after the assumption. |
| 9 | + |
| 10 | + ```javascript |
| 11 | + ce.assume(ce.box(['Equal', 'one', 1])); |
| 12 | + ce.box('one').evaluate(); // → 1 (was: 'one') |
| 13 | + ce.box(['Equal', 'one', 1]).evaluate(); // → True (was: ['Equal', 'one', 1]) |
| 14 | + ce.box(['Equal', 'one', 0]).evaluate(); // → False |
| 15 | + ce.box('one').type.matches('integer'); // → true |
| 16 | + ``` |
| 17 | + |
| 18 | + This also fixes comparison evaluation: `Equal(symbol, assumed_value)` now |
| 19 | + correctly evaluates to `True` instead of staying symbolic. |
| 20 | + |
| 21 | +- **Inequality Evaluation Using Assumptions**: When an inequality assumption |
| 22 | + is made (e.g., `ce.assume(['Greater', 'x', 4])`), inequality comparisons |
| 23 | + can now use transitive reasoning to determine results. |
| 24 | + |
| 25 | + ```javascript |
| 26 | + ce.assume(ce.box(['Greater', 'x', 4])); |
| 27 | + ce.box(['Greater', 'x', 0]).evaluate(); // → True (x > 4 > 0) |
| 28 | + ce.box(['Less', 'x', 0]).evaluate(); // → False |
| 29 | + ce.box('x').isGreater(0); // → true |
| 30 | + ce.box('x').isPositive; // → true |
| 31 | + ``` |
| 32 | + |
| 33 | + This works by extracting lower/upper bounds from inequality assumptions |
| 34 | + and using them during comparison operations. |
| 35 | + |
| 36 | +- **Type Inference from Assumptions**: When assumptions are made, symbol types |
| 37 | + are now correctly inferred. Inequality assumptions (`>`, `<`, `>=`, `<=`) set |
| 38 | + the symbol's type to `real`, and equality assumptions infer the type from the |
| 39 | + value (e.g., equal to an integer means type `integer`). |
| 40 | + |
| 41 | + ```javascript |
| 42 | + ce.assume(ce.box(['Greater', 'x', 4])); |
| 43 | + ce.box('x').type.toString(); // → 'real' (was: 'unknown') |
| 44 | + |
| 45 | + ce.assume(ce.box(['Equal', 'one', 1])); |
| 46 | + ce.box('one').type.toString(); // → 'integer' (was: 'unknown') |
| 47 | + ``` |
| 48 | + |
| 49 | +- **Tautology and Contradiction Detection**: `ce.assume()` now returns |
| 50 | + `'tautology'` for redundant assumptions that are already implied by existing |
| 51 | + assumptions, and `'contradiction'` for assumptions that conflict with |
| 52 | + existing ones. |
| 53 | + |
| 54 | + ```javascript |
| 55 | + ce.assume(ce.box(['Greater', 'x', 4])); |
| 56 | + |
| 57 | + // Redundant assumption (x > 4 implies x > 0) |
| 58 | + ce.assume(ce.box(['Greater', 'x', 0])); // → 'tautology' (was: 'ok') |
| 59 | + |
| 60 | + // Conflicting assumption (x > 4 contradicts x < 0) |
| 61 | + ce.assume(ce.box(['Less', 'x', 0])); // → 'contradiction' |
| 62 | + |
| 63 | + // Same assumption repeated |
| 64 | + ce.assume(ce.box(['Equal', 'one', 1])); |
| 65 | + ce.assume(ce.box(['Equal', 'one', 1])); // → 'tautology' |
| 66 | + |
| 67 | + // Conflicting equality |
| 68 | + ce.assume(ce.box(['Less', 'one', 0])); // → 'contradiction' |
| 69 | + ``` |
| 70 | + |
| 71 | +### Bug Fixes |
| 72 | + |
| 73 | +- **forget() Now Clears Assumed Values**: Fixed an issue where `ce.forget()` did not |
| 74 | + clear values that were set by equality assumptions. After calling |
| 75 | + `ce.assume(['Equal', 'x', 5])` followed by `ce.forget('x')`, the symbol would |
| 76 | + incorrectly still evaluate to `5`. Now `forget()` properly clears values from |
| 77 | + all evaluation context frames. |
| 78 | + |
| 79 | + ```javascript |
| 80 | + ce.assume(ce.box(['Equal', 'x', 5])); |
| 81 | + ce.box('x').evaluate(); // → 5 |
| 82 | + ce.forget('x'); |
| 83 | + ce.box('x').evaluate(); // → 'x' (was: 5) |
| 84 | + ``` |
| 85 | + |
| 86 | +- **Scoped Assumptions Now Clean Up on popScope()**: Fixed an issue where |
| 87 | + assumptions made inside a nested scope would persist after `popScope()` was |
| 88 | + called. Values set by assumptions are now properly scoped to where the |
| 89 | + assumption was made, and are automatically removed when the scope exits. |
| 90 | + |
| 91 | + ```javascript |
| 92 | + ce.pushScope(); |
| 93 | + ce.assume(ce.box(['Equal', 'y', 10])); |
| 94 | + ce.box('y').evaluate(); // → 10 |
| 95 | + ce.popScope(); |
| 96 | + ce.box('y').evaluate(); // → 'y' (was: 10) |
| 97 | + ``` |
| 98 | + |
| 99 | +- **Extraneous Root Filtering for Sqrt Equations**: Fixed an issue where solving |
| 100 | + square root equations could return extraneous roots. When solving equations |
| 101 | + like `√x = x - 2` or `√x - x + 2 = 0` using the quadratic substitution method |
| 102 | + (u = √x → solve for u → x = u²), the solver could return roots that satisfy |
| 103 | + the transformed equation but not the original. The `validateRoots()` function |
| 104 | + now correctly validates candidate solutions against the original expression |
| 105 | + before any algebraic transformations (clearing denominators, harmonization), |
| 106 | + properly filtering out extraneous roots. |
| 107 | + |
| 108 | + Examples of equations that now correctly filter extraneous roots: |
| 109 | + - `√x = x - 2` → returns `[4]` (filters out x=1) |
| 110 | + - `√x + x - 2 = 0` → returns `[1]` (filters out x=4) |
| 111 | + - `√x - x + 2 = 0` → returns `[4]` (filters out x=1) |
| 112 | + - `x - 2√x - 3 = 0` → returns `[9]` (filters out x=1) |
| 113 | + - `2x + 3√x - 2 = 0` → returns `[1/4]` (filters out x=4) |
| 114 | + |
| 115 | +### Testing |
| 116 | + |
| 117 | +- **Pattern Matching with Repeated Wildcards**: Added comprehensive tests |
| 118 | + verifying that the pattern matching system correctly handles wildcards that |
| 119 | + appear multiple times in a pattern. When a named wildcard like `_x` appears |
| 120 | + in multiple positions, the matcher correctly ensures all occurrences match |
| 121 | + the same expression. This works with: |
| 122 | + - Nested function arguments (e.g., `['Multiply', '_x', ['Ln', '_x']]`) |
| 123 | + - Multiple nesting levels (3+ levels deep) |
| 124 | + - Commutative operators (handles reordering) |
| 125 | + - Canonical expressions (from parsed LaTeX) |
| 126 | + - Complex sub-expressions (matching entire sub-trees) |
| 127 | + |
3 | 128 | ### New Features |
4 | 129 |
|
5 | 130 | #### Subscripts and Indexing |
|
0 commit comments