Skip to content

Commit d86e53d

Browse files
committed
2 parents 6a987d6 + efbaaf7 commit d86e53d

13 files changed

Lines changed: 1558 additions & 400 deletions

File tree

CHANGELOG.md

Lines changed: 125 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,130 @@
11
## [Unreleased]
22

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+
3128
### New Features
4129

5130
#### Subscripts and Indexing

0 commit comments

Comments
 (0)