Skip to content

Commit f32600e

Browse files
fix(rules): make commutative swap choose the closest sibling
- previously it would swap the siblings blindly and you'd end up with these unnatural "Tree swinging" actions that would move whole chunks of the tree around. - when ordering terms we don't move five things around at a time, we move one thing. - add test cases to ensure that the commutative property can be used to reorder groups of terms - better error logging for test failures
1 parent 684191d commit f32600e

File tree

6 files changed

+86
-28
lines changed

6 files changed

+86
-28
lines changed

libraries/mathy_python/mathy/rules/commutative_property.py

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -89,13 +89,11 @@ def apply_to(self, node: MathExpression):
8989
node.set_right(a)
9090
node.set_left(b)
9191
else:
92-
# If it's another add, just swap their
92+
# If it's another add, swap their
9393
# children directly to avoid inner-nesting.
94-
one = a.left
9594
two = a.right
9695
three = node.right
97-
a.set_left(three)
98-
a.set_right(one)
96+
a.set_right(three)
9997
node.set_right(two)
10098

10199
node.set_changed()

libraries/mathy_python/mathy/testing.py

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -74,11 +74,12 @@ def run_rule_tests(name, rule_class, callback=None):
7474
before = expression.clone()
7575
print(ex)
7676
if "target" in ex:
77+
target = ex["target"]
7778
nodes = rule.find_nodes(expression)
7879
targets = [n.raw for n in nodes]
79-
node = [n for n in nodes if n.raw == ex["target"]]
80+
node = [n for n in nodes if n.raw == target]
8081
targets = "\n".join(targets)
81-
assert len(node) > 0, f"could not find target node. targets are:\n{targets}"
82+
assert len(node) > 0, f"could not find target: {target}. targets are:\n{targets}"
8283
node = node[0]
8384
else:
8485
node = rule.find_node(expression)

libraries/mathy_python/tests/rules/associative_property.json

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,17 @@
11
{
22
"valid": [
3+
{
4+
"input": "4x^3 + g + 5y + 7p^4 + x^3 + 12p^4",
5+
"output": "4x^3 + g + 5y + 7p^4 + (x^3 + 12p^4)",
6+
"target": "4x^3 + g + 5y + 7p^4 + x^3"
7+
},
38
{
49
"input": "1f + 98i + 3f + 14t",
510
"output": "1f + (98i + 3f) + 14t",
611
"target": "1f + 98i"
712
},
813
{
9-
"input": "8x^2 * (9b) * 7",
14+
"input": "8x^2 * (9b * 7)",
1015
"output": "(8x^2 * 9b) * 7",
1116
"target": "9b * 7"
1217
},
@@ -31,4 +36,4 @@
3136
"input": "2"
3237
}
3338
]
34-
}
39+
}

libraries/mathy_python/tests/rules/commutative_property.json

Lines changed: 65 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,71 @@
11
{
22
"valid": [
3-
{
3+
{
4+
"input": "2x + 1y^3 + 7j + -2q + 93m + 6x",
5+
"target": "2x + 1y^3 + 7j + -2q + 93m + 6x",
6+
"output": "2x + 1y^3 + 7j + -2q + 6x + 93m",
7+
"why": "use commute to group like terms step 1"
8+
},
9+
{
10+
"input": "2x + 1y^3 + 7j + -2q + 6x + 93m",
11+
"target": "2x + 1y^3 + 7j + -2q + 6x",
12+
"output": "2x + 1y^3 + 7j + 6x + -2q + 93m",
13+
"why": "use commute to group like terms step 2"
14+
},
15+
{
16+
"input": "2x + 1y^3 + 7j + 6x + -2q + 93m",
17+
"target": "2x + 1y^3 + 7j + 6x",
18+
"output": "2x + 1y^3 + 6x + 7j + -2q + 93m",
19+
"why": "use commute to group like terms step 3"
20+
},
21+
{
22+
"input": "2x + 1y^3 + 6x + 7j + -2q + 93m",
23+
"target": "2x + 1y^3 + 6x",
24+
"output": "2x + 6x + 1y^3 + 7j + -2q + 93m",
25+
"why": "use commute to group like terms step 4"
26+
},
27+
{
28+
"input": "12x * 10y",
29+
"output": "(x * 12) * 10y",
30+
"args": {
31+
"preferred": false
32+
}
33+
},
34+
{
35+
"input": "4x^3 + g + 5y + 7p^4 + x^3 + 12p^4",
36+
"target": "4x^3",
37+
"output": "x^3 * 4 + g + 5y + 7p^4 + x^3 + 12p^4"
38+
},
39+
{
40+
"input": "4x^3 + g + 5y + 7p^4 + x^3 + 12p^4",
41+
"target": "4x^3 + g",
42+
"output": "g + 4x^3 + 5y + 7p^4 + x^3 + 12p^4"
43+
},
44+
{
45+
"input": "4x^3 + g + 5y + 7p^4 + x^3 + 12p^4",
46+
"target": "4x^3 + g + 5y",
47+
"output": "4x^3 + 5y + g + 7p^4 + x^3 + 12p^4"
48+
},
49+
{
50+
"input": "4x^3 + g + 5y + 7p^4 + x^3 + 12p^4",
51+
"target": "4x^3 + g + 5y + 7p^4",
52+
"output": "4x^3 + g + 7p^4 + 5y + x^3 + 12p^4"
53+
},
54+
{
55+
"input": "4x^3 + g + 5y + 7p^4 + x^3 + 12p^4",
56+
"target": "4x^3 + g + 5y + 7p^4 + x^3",
57+
"output": "4x^3 + g + 5y + x^3 + 7p^4 + 12p^4"
58+
},
59+
{
60+
"input": "4x^3 + g + 5y + 7p^4 + x^3 + 12p^4",
61+
"target": "4x^3 + g + 5y + 7p^4 + x^3 + 12p^4",
62+
"output": "4x^3 + g + 5y + 7p^4 + 12p^4 + x^3"
63+
},
64+
{
465
"input": "2530z + 1m + 3.5x + 2z + 8.9c",
566
"target": "2530z + 1m + 3.5x",
6-
"output": "3.5x + 2530z + 1m + 2z + 8.9c"
67+
"output": "2530z + 3.5x + 1m + 2z + 8.9c",
68+
"why": "swapping middle children shouldn't introduce paren nesting"
769
},
870
{
971
"input": "(5 + 12) * a",
@@ -18,13 +80,6 @@
1880
"preferred": false
1981
}
2082
},
21-
{
22-
"input": "12x * 10y",
23-
"output": "(x * 12) * 10y",
24-
"args": {
25-
"preferred": false
26-
}
27-
},
2883
{
2984
"input": "4 + 17",
3085
"output": "17 + 4"
@@ -66,4 +121,4 @@
66121
"input": "7 / x"
67122
}
68123
]
69-
}
124+
}

libraries/mathy_python/tests/rules/constants_simplify.json

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -93,7 +93,6 @@
9393
"output": "42x^3 + 30x * x * z + x * 48z^2",
9494
"why": "Because my rule implementation sucks and can't deal with this kind of nesting... should be: can skip siblings to combine constants"
9595
},
96-
9796
{
9897
"input": "13f^4 * (5f^4 + 7)",
9998
"why": "can't simplify nested child because the nested group has a different priority"
@@ -117,4 +116,4 @@
117116
"input": "x - 2"
118117
}
119118
]
120-
}
119+
}

libraries/mathy_python/tests/test_rules.py

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -22,49 +22,49 @@
2222
from ..mathy.testing import init_rule_for_test, run_rule_tests
2323

2424

25-
def test_associative_property():
25+
def test_rules_associative_property():
2626
def debug(ex):
2727
pass
2828

2929
run_rule_tests("associative_property", AssociativeSwapRule, debug)
3030

3131

32-
def test_commutative_property():
32+
def test_rules_commutative_property():
3333
def debug(ex):
3434
pass
3535

3636
run_rule_tests("commutative_property", CommutativeSwapRule, debug)
3737

3838

39-
def test_constants_simplify():
39+
def test_rules_constants_simplify():
4040
def debug(ex):
4141
pass
4242

4343
run_rule_tests("constants_simplify", ConstantsSimplifyRule, debug)
4444

4545

46-
def test_distributive_factor_out():
46+
def test_rules_distributive_factor_out():
4747
def debug(ex):
4848
pass
4949

5050
run_rule_tests("distributive_factor_out", DistributiveFactorOutRule, debug)
5151

5252

53-
def test_distributive_multiply_across():
53+
def test_rules_distributive_multiply_across():
5454
def debug(ex):
5555
pass
5656

5757
run_rule_tests("distributive_multiply_across", DistributiveMultiplyRule, debug)
5858

5959

60-
def test_variable_multiply():
60+
def test_rules_variable_multiply():
6161
def debug(ex):
6262
pass
6363

6464
run_rule_tests("variable_multiply", VariableMultiplyRule, debug)
6565

6666

67-
def test_rule_can_apply_to():
67+
def test_rules_rule_can_apply_to():
6868
parser = ExpressionParser()
6969
expression = parser.parse("7 + 4x - 2")
7070

@@ -78,7 +78,7 @@ def test_rule_can_apply_to():
7878
assert type(action.can_apply_to(expression)) == bool
7979

8080

81-
def test_like_terms_compare():
81+
def test_rules_like_terms_compare():
8282
parser = ExpressionParser()
8383
expr = parser.parse("10 + (7x + 6x)")
8484
terms = get_terms(expr)

0 commit comments

Comments
 (0)