Merge pull request #489 from chisholm/fix_xform_followedby_order
Fix observation expression DNF transformer to preserve FOLLOWEDBY order.pull/1/head
commit
58b79597b6
|
@ -282,6 +282,7 @@ class AbsorptionTransformer(
|
||||||
|
|
||||||
A or (A and B) = A
|
A or (A and B) = A
|
||||||
A or (A followedby B) = A
|
A or (A followedby B) = A
|
||||||
|
A or (B followedby A) = A
|
||||||
|
|
||||||
Other variants do not hold for observation expressions.
|
Other variants do not hold for observation expressions.
|
||||||
"""
|
"""
|
||||||
|
@ -435,28 +436,35 @@ class DNFTransformer(ObservationExpressionTransformer):
|
||||||
|
|
||||||
A and (B or C) => (A and B) or (A and C)
|
A and (B or C) => (A and B) or (A and C)
|
||||||
A followedby (B or C) => (A followedby B) or (A followedby C)
|
A followedby (B or C) => (A followedby B) or (A followedby C)
|
||||||
|
(A or B) followedby C => (A followedby C) or (B followedby C)
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def __transform(self, ast):
|
def __transform(self, ast):
|
||||||
|
|
||||||
root_type = type(ast) # will be AST class for AND or FOLLOWEDBY
|
# If no OR children, nothing to do
|
||||||
changed = False
|
if any(
|
||||||
or_children = []
|
isinstance(child, OrObservationExpression)
|
||||||
other_children = []
|
for child in ast.operands
|
||||||
|
):
|
||||||
|
# When we distribute FOLLOWEDBY over OR, it is important to
|
||||||
|
# preserve the original FOLLOWEDBY order! We don't need to do that
|
||||||
|
# for AND, but we do it anyway because it doesn't hurt, and we can
|
||||||
|
# use the same code for both.
|
||||||
|
iterables = []
|
||||||
for child in ast.operands:
|
for child in ast.operands:
|
||||||
if isinstance(child, OrObservationExpression):
|
if isinstance(child, OrObservationExpression):
|
||||||
or_children.append(child.operands)
|
iterables.append(child.operands)
|
||||||
else:
|
else:
|
||||||
other_children.append(child)
|
iterables.append((child,))
|
||||||
|
|
||||||
if or_children:
|
root_type = type(ast) # will be AST class for AND or FOLLOWEDBY
|
||||||
distributed_children = [
|
distributed_children = [
|
||||||
root_type([
|
root_type([
|
||||||
_dupe_ast(sub_ast) for sub_ast in itertools.chain(
|
_dupe_ast(sub_ast) for sub_ast in itertools.chain(
|
||||||
other_children, prod_seq,
|
prod_seq,
|
||||||
)
|
)
|
||||||
])
|
])
|
||||||
for prod_seq in itertools.product(*or_children)
|
for prod_seq in itertools.product(*iterables)
|
||||||
]
|
]
|
||||||
|
|
||||||
# Need to recursively continue to distribute AND/FOLLOWEDBY over OR
|
# Need to recursively continue to distribute AND/FOLLOWEDBY over OR
|
||||||
|
@ -470,6 +478,7 @@ class DNFTransformer(ObservationExpressionTransformer):
|
||||||
|
|
||||||
else:
|
else:
|
||||||
result = ast
|
result = ast
|
||||||
|
changed = False
|
||||||
|
|
||||||
return result, changed
|
return result, changed
|
||||||
|
|
||||||
|
|
|
@ -223,6 +223,10 @@ def test_obs_absorb_not_equivalent(patt1, patt2):
|
||||||
"([a:b=1] OR [a:b=2]) FOLLOWEDBY ([a:b=3] OR [a:b=4])",
|
"([a:b=1] OR [a:b=2]) FOLLOWEDBY ([a:b=3] OR [a:b=4])",
|
||||||
"([a:b=1] FOLLOWEDBY [a:b=3]) OR ([a:b=1] FOLLOWEDBY [a:b=4]) OR ([a:b=2] FOLLOWEDBY [a:b=3]) OR ([a:b=2] FOLLOWEDBY [a:b=4])",
|
"([a:b=1] FOLLOWEDBY [a:b=3]) OR ([a:b=1] FOLLOWEDBY [a:b=4]) OR ([a:b=2] FOLLOWEDBY [a:b=3]) OR ([a:b=2] FOLLOWEDBY [a:b=4])",
|
||||||
),
|
),
|
||||||
|
(
|
||||||
|
"([a:b=1] OR [a:b=2]) FOLLOWEDBY ([a:b=5] AND [a:b=6])",
|
||||||
|
"([a:b=1] FOLLOWEDBY ([a:b=5] AND [a:b=6])) OR ([a:b=2] FOLLOWEDBY ([a:b=5] AND [a:b=6]))",
|
||||||
|
),
|
||||||
],
|
],
|
||||||
)
|
)
|
||||||
def test_obs_dnf_equivalent(patt1, patt2):
|
def test_obs_dnf_equivalent(patt1, patt2):
|
||||||
|
@ -243,6 +247,10 @@ def test_obs_dnf_equivalent(patt1, patt2):
|
||||||
"[a:b=1] WITHIN 2 SECONDS",
|
"[a:b=1] WITHIN 2 SECONDS",
|
||||||
"[a:b=1] REPEATS 2 TIMES",
|
"[a:b=1] REPEATS 2 TIMES",
|
||||||
),
|
),
|
||||||
|
(
|
||||||
|
"[a:b=1] FOLLOWEDBY ([a:b=2] OR [a:b=3])",
|
||||||
|
"([a:b=2] FOLLOWEDBY [a:b=1]) OR ([a:b=1] FOLLOWEDBY [a:b=3])",
|
||||||
|
),
|
||||||
],
|
],
|
||||||
)
|
)
|
||||||
def test_obs_not_equivalent(patt1, patt2):
|
def test_obs_not_equivalent(patt1, patt2):
|
||||||
|
|
Loading…
Reference in New Issue