Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Support
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in
Toggle navigation
T
trs
Project overview
Project overview
Details
Activity
Releases
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Issues
0
Issues
0
List
Boards
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Analytics
Analytics
CI / CD
Repository
Value Stream
Wiki
Wiki
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
Taddeüs Kroes
trs
Commits
c5b2a5c6
Commit
c5b2a5c6
authored
Jan 22, 2012
by
Sander Mathijs van Veen
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Added support for fractions with negation and addition of negative numbers.
parent
d5c29c2b
Changes
11
Hide whitespace changes
Inline
Side-by-side
Showing
11 changed files
with
106 additions
and
22 deletions
+106
-22
src/node.py
src/node.py
+7
-0
src/rules/fractions.py
src/rules/fractions.py
+32
-10
src/rules/groups.py
src/rules/groups.py
+1
-0
src/rules/numerics.py
src/rules/numerics.py
+8
-2
src/rules/poly.py
src/rules/poly.py
+11
-4
tests/parser.py
tests/parser.py
+2
-0
tests/test_leiden_oefenopgave.py
tests/test_leiden_oefenopgave.py
+3
-1
tests/test_rewrite.py
tests/test_rewrite.py
+4
-0
tests/test_rules_fractions.py
tests/test_rules_fractions.py
+28
-1
tests/test_rules_numerics.py
tests/test_rules_numerics.py
+10
-2
tests/test_rules_poly.py
tests/test_rules_poly.py
+0
-2
No files found.
src/node.py
View file @
c5b2a5c6
...
...
@@ -189,6 +189,9 @@ class ExpressionNode(Node, ExpressionBase):
>>> n2 = N('*', N('^', r, e), c)
>>> n2.extract_polynome()
(c, r, e)
>>> n3 = N('-', r)
>>> n3.extract_polynome()
(1, -r, 1)
"""
# TODO: change "get_polynome" -> "extract_polynome".
# TODO: change retval of c * r ^ e to (c, r, e).
...
...
@@ -198,6 +201,10 @@ class ExpressionNode(Node, ExpressionBase):
if
self
.
is_power
():
return
(
ExpressionLeaf
(
1
),
self
[
0
],
self
[
1
])
# rule: -r -> (1, r, 1)
if
self
.
is_op
(
OP_NEG
):
return
(
ExpressionLeaf
(
1
),
-
self
[
0
],
ExpressionLeaf
(
1
))
if
self
.
op
!=
OP_MUL
:
return
...
...
src/rules/fractions.py
View file @
c5b2a5c6
from
itertools
import
combinations
from
.utils
import
nary_node
,
least_common_multiple
from
..node
import
ExpressionLeaf
as
L
,
OP_DIV
,
OP_ADD
,
OP_MUL
from
..node
import
ExpressionLeaf
as
L
,
OP_DIV
,
OP_ADD
,
OP_MUL
,
OP_NEG
from
..possibilities
import
Possibility
as
P
,
MESSAGES
from
..translate
import
_
...
...
@@ -63,15 +63,23 @@ def match_add_constant_fractions(node):
1 / 2 + 3 / 4 -> 2 / 4 + 3 / 4 # Equalize denominators
2 / 4 + 3 / 4 -> 5 / 4 # Equal denominators, so nominators can
# be added
2 / 2 - 3 / 4 -> 4 / 4 - 3 / 4 # Equalize denominators
2 / 4 - 3 / 4 -> -1 / 4 # Equal denominators, so nominators can
# be subtracted
"""
assert
node
.
is_op
(
OP_ADD
)
p
=
[]
fractions
=
filter
(
lambda
n
:
n
.
is_op
(
OP_DIV
),
node
.
get_scope
())
def
is_division
(
node
):
return
node
.
is_op
(
OP_DIV
)
or
\
(
node
.
is_op
(
OP_NEG
)
and
node
[
0
].
is_op
(
OP_DIV
))
fractions
=
filter
(
is_division
,
node
.
get_scope
())
for
a
,
b
in
combinations
(
fractions
,
2
):
na
,
da
=
a
nb
,
db
=
b
na
,
da
=
a
if
a
.
is_op
(
OP_DIV
)
else
a
[
0
]
nb
,
db
=
b
if
b
.
is_op
(
OP_DIV
)
else
b
[
0
]
if
da
==
db
:
# Equal denominators, add nominators to create a single fraction
...
...
@@ -96,28 +104,40 @@ def equalize_denominators(root, args):
scope
=
root
.
get_scope
()
for
fraction
in
args
[:
2
]:
n
,
d
=
fraction
n
,
d
=
fraction
[
0
]
if
fraction
.
is_op
(
OP_NEG
)
else
fraction
mult
=
denom
/
d
.
value
if
mult
!=
1
:
n
=
L
(
n
.
value
*
mult
)
if
n
.
is_numeric
()
else
L
(
mult
)
*
n
scope
[
scope
.
index
(
fraction
)]
=
n
/
L
(
d
.
value
*
mult
)
if
fraction
.
is_op
(
OP_NEG
):
scope
[
scope
.
index
(
fraction
)]
=
-
(
n
/
L
(
d
.
value
*
mult
))
else
:
scope
[
scope
.
index
(
fraction
)]
=
n
/
L
(
d
.
value
*
mult
)
return
nary_node
(
'+'
,
scope
)
def
add_nominators
(
root
,
args
):
"""
a / b + c / b -> (a + c) / b
a / b + c / b -> (a + c) / b
a / b + (-c / b) -> (a + (-c)) / b
"""
# TODO: is 'add' Appropriate when rewriting to "(a + (-c)) / b"?
ab
,
cb
=
args
a
,
b
=
ab
c
=
cb
[
0
]
if
cb
[
0
].
is_op
(
OP_NEG
):
c
=
cb
[
0
][
0
]
substitution
=
(
a
+
(
-
c
))
/
b
else
:
c
=
cb
[
0
]
substitution
=
(
a
+
c
)
/
b
scope
=
root
.
get_scope
()
# Replace the left node with the new expression
scope
[
scope
.
index
(
ab
)]
=
(
a
+
c
)
/
b
scope
[
scope
.
index
(
ab
)]
=
substitution
# Remove the right node
scope
.
remove
(
cb
)
...
...
@@ -127,8 +147,10 @@ def add_nominators(root, args):
def
match_expand_and_add_fractions
(
node
):
"""
a * b / c + d * b / c -> (a + d) * (b / c)
a * b / c + d * b / c -> (a + d) * (b / c)
a * b / c + (- d * b / c) -> (a + (-d)) * (b / c)
"""
# TODO: is 'add' Appropriate when rewriting to "(a + (-d)) / * (b / c)"?
assert
node
.
is_op
(
OP_MUL
)
p
=
[]
...
...
src/rules/groups.py
View file @
c5b2a5c6
...
...
@@ -18,6 +18,7 @@ def match_combine_groups(node):
ab + 2ab -> 3ab
ab + ba -> 2ab
"""
# TODO: handle OP_NEG nodes
assert
node
.
is_op
(
OP_ADD
)
p
=
[]
...
...
src/rules/numerics.py
View file @
c5b2a5c6
from
itertools
import
combinations
from
.utils
import
nary_node
from
..node
import
ExpressionLeaf
as
Leaf
,
OP_DIV
,
OP_MUL
from
..node
import
ExpressionLeaf
as
Leaf
,
OP_DIV
,
OP_MUL
,
OP_NEG
from
..possibilities
import
Possibility
as
P
,
MESSAGES
from
..translate
import
_
...
...
@@ -11,10 +11,16 @@ def add_numerics(root, args):
Combine two constants to a single constant in an n-ary addition.
Example:
2 + 3 -> 5
2 + 3 -> 5
2 + -3 -> -1
-2 + 3 -> 1
-2 + -3 -> -5
"""
n0
,
n1
,
c0
,
c1
=
args
c0
=
(
-
c0
[
0
].
value
)
if
c0
.
is_op
(
OP_NEG
)
else
c0
.
value
c1
=
(
-
c1
[
0
].
value
)
if
c1
.
is_op
(
OP_NEG
)
else
c1
.
value
scope
=
root
.
get_scope
()
# Replace the left node with the new expression
...
...
src/rules/poly.py
View file @
c5b2a5c6
from
itertools
import
combinations
from
..node
import
OP_ADD
from
..node
import
OP_ADD
,
OP_NEG
from
..possibilities
import
Possibility
as
P
,
MESSAGES
from
.utils
import
nary_node
from
.numerics
import
add_numerics
def
is_numeric_or_negated_numeric
(
n
):
return
n
.
is_numeric
()
or
(
n
.
is_op
(
OP_NEG
)
and
n
[
0
].
is_numeric
())
def
match_combine_polynomes
(
node
,
verbose
=
False
):
"""
n + exp + m -> exp + (n + m)
...
...
@@ -49,9 +53,12 @@ def match_combine_polynomes(node, verbose=False):
# roots, or: same root and exponent -> combine coefficients.
# TODO: Addition with zero, e.g. a + 0 -> a
if
c0
==
1
and
c1
==
1
and
e0
==
1
and
e1
==
1
\
and
r0
.
is_numeric
()
and
r1
.
is_numeric
():
# 2 + 3 -> 5
p
.
append
(
P
(
node
,
add_numerics
,
(
n0
,
n1
,
r0
.
value
,
r1
.
value
)))
and
all
(
map
(
is_numeric_or_negated_numeric
,
[
r0
,
r1
])):
# 2 + 3 -> 5
# 2 + -3 -> -1
# -2 + 3 -> 1
# -2 + -3 -> -5
p
.
append
(
P
(
node
,
add_numerics
,
(
n0
,
n1
,
r0
,
r1
)))
elif
c0
.
is_numeric
()
and
c1
.
is_numeric
()
and
r0
==
r1
and
e0
==
e1
:
# 2a + 2a -> 4a
# a + 2a -> 3a
...
...
tests/parser.py
View file @
c5b2a5c6
...
...
@@ -120,6 +120,8 @@ def apply_expressions(base_class, expressions, fail=True, silent=False,
if
fail
:
raise
def
graph
(
parser
,
*
exp
,
**
kwargs
):
return
generate_graph
(
ParserWrapper
(
parser
,
**
kwargs
).
run
(
exp
))
...
...
tests/test_leiden_oefenopgave.py
View file @
c5b2a5c6
...
...
@@ -32,7 +32,9 @@ class TestLeidenOefenopgave(TestCase):
(
'2/15 + 1/4'
,
'8 / 60 + 15 / 60'
),
(
'8/60 + 15/60'
,
'(8 + 15) / 60'
),
(
'(8 + 15) / 60'
,
'23 / 60'
),
# FIXME: ('2/7 - 4/11', '-6 / 77'),
(
'2/7 - 4/11'
,
'22 / 77 + -28 / 77'
),
(
'22/77 + -28/77'
,
'(22 + -28) / 77'
),
(
'(22 + -28)/77'
,
'-6 / 77'
),
# FIXME: ('(7/3) * (3/5)', '7 / 5'),
# FIXME: ('(3/4) / (5/6)', '9 / 10'),
# FIXME: ('1/4 * 1/x', '1 / (4x)'),
...
...
tests/test_rewrite.py
View file @
c5b2a5c6
...
...
@@ -24,3 +24,7 @@ class TestRewrite(TestCase):
def
test_addition_identifiers_rewrite
(
self
):
self
.
assertRewrite
([
'2 + 3a + 4'
,
'6 + 3a'
])
def
test_division_rewrite
(
self
):
self
.
assertRewrite
([
'2/7 - 4/11'
,
'22 / 77 + -28 / 77'
,
'(22 + -28) / 77'
,
'-6 / 77'
])
tests/test_rules_fractions.py
View file @
c5b2a5c6
...
...
@@ -68,6 +68,19 @@ class TestRulesFractions(RulesTestCase):
self
.
assertEqualPos
(
possibilities
,
[
P
(
root
,
add_nominators
,
(
n1
,
n3
))])
def
test_add_constant_fractions_with_negation
(
self
):
a
,
b
,
c
,
l1
,
l2
,
l3
,
l4
=
tree
(
'a,b,c,1,2,3,4'
)
(((
n0
,
n1
),
n2
),
n3
),
n4
=
root
=
a
+
l2
/
l2
+
b
+
(
-
l3
/
l4
)
+
c
possibilities
=
match_add_constant_fractions
(
root
)
self
.
assertEqualPos
(
possibilities
,
[
P
(
root
,
equalize_denominators
,
(
n1
,
n3
,
4
))])
(((
n0
,
n1
),
n2
),
n3
),
n4
=
root
=
a
+
l2
/
l4
+
b
+
(
-
l3
/
l4
)
+
c
possibilities
=
match_add_constant_fractions
(
root
)
self
.
assertEqualPos
(
possibilities
,
[
P
(
root
,
add_nominators
,
(
n1
,
n3
))])
def
test_equalize_denominators
(
self
):
a
,
b
,
l1
,
l2
,
l3
,
l4
=
tree
(
'a,b,1,2,3,4'
)
...
...
@@ -79,8 +92,22 @@ class TestRulesFractions(RulesTestCase):
self
.
assertEqualNodes
(
equalize_denominators
(
root
,
(
n0
,
n1
,
4
)),
(
l2
*
a
)
/
l4
+
b
/
l4
)
#2 / 2 - 3 / 4 -> 4 / 4 - 3 / 4 # Equalize denominators
n0
,
n1
=
root
=
l1
/
l2
+
(
-
l3
/
l4
)
self
.
assertEqualNodes
(
equalize_denominators
(
root
,
(
n0
,
n1
,
4
)),
l2
/
l4
+
(
-
l3
/
l4
))
#2 / 2 - 3 / 4 -> 4 / 4 - 3 / 4 # Equalize denominators
n0
,
n1
=
root
=
a
/
l2
+
(
-
b
/
l4
)
self
.
assertEqualNodes
(
equalize_denominators
(
root
,
(
n0
,
n1
,
4
)),
(
l2
*
a
)
/
l4
+
(
-
b
/
l4
))
def
test_add_nominators
(
self
):
a
,
b
,
c
=
tree
(
'a,b,c'
)
n0
,
n1
=
root
=
a
/
b
+
c
/
b
self
.
assertEqualNodes
(
add_nominators
(
root
,
(
n0
,
n1
)),
(
a
+
c
)
/
b
)
#2 / 4 + 3 / -4 -> 2 / 4 + -3 / 4
#2 / 4 - 3 / 4 -> -1 / 4 # Equal denominators, so nominators can
n0
,
n1
=
root
=
a
/
b
+
(
-
c
/
b
)
self
.
assertEqualNodes
(
add_nominators
(
root
,
(
n0
,
n1
)),
(
a
+
(
-
c
))
/
b
)
tests/test_rules_numerics.py
View file @
c5b2a5c6
...
...
@@ -10,8 +10,16 @@ class TestRulesNumerics(RulesTestCase):
def
test_add_numerics
(
self
):
l0
,
a
,
l1
=
tree
(
'1,a,2'
)
self
.
assertEqual
(
add_numerics
(
l0
+
l1
,
(
l0
,
l1
,
1
,
2
)),
3
)
self
.
assertEqual
(
add_numerics
(
l0
+
a
+
l1
,
(
l0
,
l1
,
1
,
2
)),
L
(
3
)
+
a
)
self
.
assertEqual
(
add_numerics
(
l0
+
l1
,
(
l0
,
l1
,
L
(
1
),
L
(
2
))),
3
)
self
.
assertEqual
(
add_numerics
(
l0
+
a
+
l1
,
(
l0
,
l1
,
L
(
1
),
L
(
2
))),
L
(
3
)
+
a
)
def
test_add_numerics_negations
(
self
):
l0
,
a
,
l1
=
tree
(
'1,a,2'
)
self
.
assertEqual
(
add_numerics
(
l0
+
-
l1
,
(
l0
,
-
l1
,
L
(
1
),
-
L
(
2
))),
-
1
)
self
.
assertEqual
(
add_numerics
(
l0
+
a
+
-
l1
,
(
l0
,
-
l1
,
L
(
1
),
-
L
(
2
))),
L
(
-
1
)
+
a
)
def
test_match_divide_numerics
(
self
):
a
,
b
,
i2
,
i3
,
i6
,
f1
,
f2
,
f3
=
tree
(
'a,b,2,3,6,1.0,2.0,3.0'
)
...
...
tests/test_rules_poly.py
View file @
c5b2a5c6
...
...
@@ -36,14 +36,12 @@ class TestRulesPoly(RulesTestCase):
self
.
assertEqualPos
(
possibilities
,
[
P
(
root
,
combine_polynomes
,
(
a1
,
a2
,
2
,
1
,
'a'
,
3
))])
def
test_identifiers_coeff_exponent_both
(
self
):
a1
,
a2
=
root
=
tree
(
'2a3+2a3'
)
possibilities
=
match_combine_polynomes
(
root
)
self
.
assertEqualPos
(
possibilities
,
[
P
(
root
,
combine_polynomes
,
(
a1
,
a2
,
2
,
2
,
'a'
,
3
))])
def
test_basic_subexpressions
(
self
):
a_b
,
c
,
d
=
tree
(
'a+b,c,d'
)
left
,
right
=
root
=
tree
(
'(a+b)^d + (a+b)^d'
)
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment