-
Notifications
You must be signed in to change notification settings - Fork 2
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat(rules): add MultiplicativeInverse rule (#19)
* feat(rules): add MultiplicativeInverse rule - for converting division into multiplication with the reciprocal, unlocking distributive property, commutative, etc. - `a / b = a * (1 / b)`
- Loading branch information
1 parent
ce56d37
commit 40a2d88
Showing
17 changed files
with
287 additions
and
37 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,17 @@ | ||
The `Multiplicative Inverse` rule converts division operations into multiplication by the reciprocal. This transformation can simplify the structure of mathematical expressions and prepare them for further simplification. | ||
|
||
This rule is expressed with the equation `a / b = a * (1 / b)` | ||
|
||
**Convert Division to Multiplication by Reciprocal** | ||
|
||
This handles the `a / b` conversion to `a * (1 / b)`. | ||
|
||
**Handle Division by a Negative Denominator** | ||
|
||
When the denominator is negative, the rule handles it by negating the numerator and converting the division into multiplication by the positive reciprocal of the denominator. | ||
|
||
This handles the `4 / -(2 + 3)` conversion to `4 * -1 / (2 + 3)` | ||
|
||
### Examples | ||
|
||
`rule_tests:multiplicative_inverse` |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,77 @@ | ||
from typing import Optional | ||
|
||
from ..expressions import ( | ||
ConstantExpression, | ||
DivideExpression, | ||
MathExpression, | ||
MultiplyExpression, | ||
NegateExpression, | ||
) | ||
from ..rule import BaseRule, ExpressionChangeRule | ||
|
||
_OP_DIVISION_EXPRESSION = "division-expression" | ||
_OP_DIVISION_NEGATIVE_DENOMINATOR = "division-negative-denominator" | ||
|
||
|
||
class MultiplicativeInverseRule(BaseRule): | ||
"""Convert division operations to multiplication by the reciprocal.""" | ||
|
||
@property | ||
def name(self) -> str: | ||
return "Multiplicative Inverse" | ||
|
||
@property | ||
def code(self) -> str: | ||
return "MI" | ||
|
||
def get_type(self, node: MathExpression) -> Optional[str]: | ||
"""Determine the configuration of the tree for this transformation. | ||
Support different types of tree configurations based on the division operation: | ||
- DivisionExpression restated as multiplication by reciprocal | ||
- DivisionNegativeDenominator is a division by a negative term | ||
""" | ||
is_division = isinstance(node, DivideExpression) | ||
if not is_division: | ||
return None | ||
|
||
# Division where the denominator is negative (e.g., (2 + 3z) / -z) | ||
if isinstance(node.right, NegateExpression): | ||
return _OP_DIVISION_NEGATIVE_DENOMINATOR | ||
|
||
# If none of the above, it's a general division expression | ||
return _OP_DIVISION_EXPRESSION | ||
|
||
def can_apply_to(self, node: MathExpression) -> bool: | ||
tree_type = self.get_type(node) | ||
return tree_type is not None | ||
|
||
def apply_to(self, node: MathExpression) -> ExpressionChangeRule: | ||
change = super().apply_to(node) | ||
tree_type = self.get_type(node) | ||
assert tree_type is not None, "call can_apply_to before applying a rule" | ||
change.save_parent() # connect result to node.parent | ||
|
||
assert node.left is not None, "Division must have a left child" | ||
assert node.right is not None, "Division must have a right child" | ||
|
||
# For negative denominator, negate the numerator and use the positive reciprocal | ||
if tree_type == _OP_DIVISION_NEGATIVE_DENOMINATOR: | ||
assert isinstance( | ||
node.right, NegateExpression | ||
), "Right child must be a NegateExpression" | ||
child = node.right.get_child() | ||
assert child is not None, "NegateExpression must have a child" | ||
result = MultiplyExpression( | ||
node.left.clone(), | ||
DivideExpression(ConstantExpression(-1), child.clone()), | ||
) | ||
# Multiply the numerator by the reciprocal of the denominator | ||
else: | ||
result = MultiplyExpression( | ||
node.left.clone(), | ||
DivideExpression(ConstantExpression(1), node.right.clone()), | ||
) | ||
|
||
result.set_changed() # mark this node as changed for visualization | ||
return change.done(result) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,25 @@ | ||
{ | ||
"valid": [ | ||
{ | ||
"input": "4 / -(2 + 3)", | ||
"output": "4 * -1 / (2 + 3)" | ||
}, | ||
{ | ||
"input": "(21x^3 - 35x^2) / 7x", | ||
"output": "(21x^3 - 35x^2) * 1 / 7x" | ||
}, | ||
{ | ||
"input": "(x^2 + 4x + 4) / (2x - 2)", | ||
"output": "(x^2 + 4x + 4) * 1 / (2x - 2)" | ||
}, | ||
{ | ||
"input": "(2 + 3x) / 2x", | ||
"output": "(2 + 3x) * 1 / 2x" | ||
}, | ||
{ | ||
"input": "((x + 1) / -(y + 2))", | ||
"output": "(x + 1) * -1 / (y + 2)" | ||
} | ||
], | ||
"invalid": [] | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,44 @@ | ||
```python | ||
|
||
import mathy_core.rules.multiplicative_inverse | ||
``` | ||
The `Multiplicative Inverse` rule converts division operations into multiplication by the reciprocal. This transformation can simplify the structure of mathematical expressions and prepare them for further simplification. | ||
|
||
This rule is expressed with the equation `a / b = a * (1 / b)` | ||
|
||
**Convert Division to Multiplication by Reciprocal** | ||
|
||
This handles the `a / b` conversion to `a * (1 / b)`. | ||
|
||
**Handle Division by a Negative Denominator** | ||
|
||
When the denominator is negative, the rule handles it by negating the numerator and converting the division into multiplication by the positive reciprocal of the denominator. | ||
|
||
This handles the `4 / -(2 + 3)` conversion to `4 * -1 / (2 + 3)` | ||
|
||
### Examples | ||
|
||
`rule_tests:multiplicative_inverse` | ||
|
||
|
||
## API | ||
|
||
|
||
## MultiplicativeInverseRule | ||
```python | ||
MultiplicativeInverseRule(self, args, kwargs) | ||
``` | ||
Convert division operations to multiplication by the reciprocal. | ||
### get_type | ||
```python | ||
MultiplicativeInverseRule.get_type( | ||
self, | ||
node: mathy_core.expressions.MathExpression, | ||
) -> Optional[str] | ||
``` | ||
Determine the configuration of the tree for this transformation. | ||
|
||
Support different types of tree configurations based on the division operation: | ||
- DivisionExpression restated as multiplication by reciprocal | ||
- DivisionNegativeDenominator is a division by a negative term | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.