py-predicate 0.1__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- predicate/__init__.py +63 -0
- predicate/formatter/__init__.py +0 -0
- predicate/formatter/format_dot.py +138 -0
- predicate/formatter/format_json.py +45 -0
- predicate/negate.py +88 -0
- predicate/optimizer/__init__.py +0 -0
- predicate/optimizer/all_optimizer.py +31 -0
- predicate/optimizer/and_optimizer.py +121 -0
- predicate/optimizer/any_optimizer.py +30 -0
- predicate/optimizer/not_optimizer.py +57 -0
- predicate/optimizer/or_optimizer.py +125 -0
- predicate/optimizer/parser.py +74 -0
- predicate/optimizer/predicate_optimizer.py +63 -0
- predicate/optimizer/rules.py +69 -0
- predicate/optimizer/xor_optimizer.py +75 -0
- predicate/predicate.py +263 -0
- predicate/standard_predicates.py +89 -0
- py_predicate-0.1.dist-info/METADATA +64 -0
- py_predicate-0.1.dist-info/RECORD +20 -0
- py_predicate-0.1.dist-info/WHEEL +4 -0
predicate/__init__.py
ADDED
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
"""The py-predicate module."""
|
|
2
|
+
|
|
3
|
+
__version__ = "0.0.1"
|
|
4
|
+
|
|
5
|
+
from predicate.optimizer.predicate_optimizer import can_optimize, optimize
|
|
6
|
+
from predicate.predicate import (
|
|
7
|
+
AllPredicate,
|
|
8
|
+
AlwaysFalsePredicate,
|
|
9
|
+
AlwaysTruePredicate,
|
|
10
|
+
AndPredicate,
|
|
11
|
+
AnyPredicate,
|
|
12
|
+
EqPredicate,
|
|
13
|
+
FnPredicate,
|
|
14
|
+
GePredicate,
|
|
15
|
+
GtPredicate,
|
|
16
|
+
InPredicate,
|
|
17
|
+
IsEmptyPredicate,
|
|
18
|
+
IsNonePredicate,
|
|
19
|
+
IsNotNonePredicate,
|
|
20
|
+
LePredicate,
|
|
21
|
+
LtPredicate,
|
|
22
|
+
NePredicate,
|
|
23
|
+
NotInPredicate,
|
|
24
|
+
NotPredicate,
|
|
25
|
+
OrPredicate,
|
|
26
|
+
Predicate,
|
|
27
|
+
XorPredicate,
|
|
28
|
+
always_false_p,
|
|
29
|
+
always_true_p,
|
|
30
|
+
)
|
|
31
|
+
from predicate.standard_predicates import ge_p, gt_p, le_p, lt_p
|
|
32
|
+
|
|
33
|
+
__all__ = [
|
|
34
|
+
"ge_p",
|
|
35
|
+
"gt_p",
|
|
36
|
+
"le_p",
|
|
37
|
+
"lt_p",
|
|
38
|
+
"can_optimize",
|
|
39
|
+
"optimize",
|
|
40
|
+
"always_true_p",
|
|
41
|
+
"AllPredicate",
|
|
42
|
+
"AndPredicate",
|
|
43
|
+
"AnyPredicate",
|
|
44
|
+
"InPredicate",
|
|
45
|
+
"FnPredicate",
|
|
46
|
+
"GePredicate",
|
|
47
|
+
"GtPredicate",
|
|
48
|
+
"IsEmptyPredicate",
|
|
49
|
+
"IsNonePredicate",
|
|
50
|
+
"IsNotNonePredicate",
|
|
51
|
+
"LePredicate",
|
|
52
|
+
"LtPredicate",
|
|
53
|
+
"NePredicate",
|
|
54
|
+
"NotInPredicate",
|
|
55
|
+
"XorPredicate",
|
|
56
|
+
"always_false_p",
|
|
57
|
+
"AlwaysTruePredicate",
|
|
58
|
+
"AlwaysFalsePredicate",
|
|
59
|
+
"EqPredicate",
|
|
60
|
+
"NotPredicate",
|
|
61
|
+
"OrPredicate",
|
|
62
|
+
"Predicate",
|
|
63
|
+
]
|
|
File without changes
|
|
@@ -0,0 +1,138 @@
|
|
|
1
|
+
from itertools import count
|
|
2
|
+
|
|
3
|
+
import graphviz # type: ignore
|
|
4
|
+
|
|
5
|
+
from predicate import (
|
|
6
|
+
AllPredicate,
|
|
7
|
+
AlwaysFalsePredicate,
|
|
8
|
+
AlwaysTruePredicate,
|
|
9
|
+
AndPredicate,
|
|
10
|
+
AnyPredicate,
|
|
11
|
+
EqPredicate,
|
|
12
|
+
FnPredicate,
|
|
13
|
+
GePredicate,
|
|
14
|
+
GtPredicate,
|
|
15
|
+
InPredicate,
|
|
16
|
+
LePredicate,
|
|
17
|
+
LtPredicate,
|
|
18
|
+
NePredicate,
|
|
19
|
+
NotInPredicate,
|
|
20
|
+
NotPredicate,
|
|
21
|
+
OrPredicate,
|
|
22
|
+
Predicate,
|
|
23
|
+
XorPredicate,
|
|
24
|
+
)
|
|
25
|
+
from predicate.optimizer.predicate_optimizer import optimize
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
def to_dot(predicate: Predicate, predicate_string: str = "", show_optimized: bool = False):
|
|
29
|
+
graph_attr = {"label": predicate_string, "labelloc": "t"}
|
|
30
|
+
|
|
31
|
+
node_attr = {"shape": "rectangle", "style": "filled", "fillcolor": "#B7D7A8"}
|
|
32
|
+
|
|
33
|
+
edge_attr: dict = {}
|
|
34
|
+
|
|
35
|
+
dot = graphviz.Digraph(graph_attr=graph_attr, node_attr=node_attr, edge_attr=edge_attr)
|
|
36
|
+
|
|
37
|
+
node_nr = count()
|
|
38
|
+
|
|
39
|
+
render_original(dot, predicate, node_nr)
|
|
40
|
+
|
|
41
|
+
if show_optimized:
|
|
42
|
+
render_optimized(dot, predicate, node_nr)
|
|
43
|
+
|
|
44
|
+
return dot
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
def render(dot, predicate: Predicate, node_nr):
|
|
48
|
+
def add_node(name: str, *, label: str):
|
|
49
|
+
node = next(node_nr)
|
|
50
|
+
unique_name = f"{name}_{node}"
|
|
51
|
+
dot.node(unique_name, label=label)
|
|
52
|
+
return unique_name
|
|
53
|
+
|
|
54
|
+
def to_value(predicate: Predicate):
|
|
55
|
+
match predicate:
|
|
56
|
+
case AllPredicate(all_predicate):
|
|
57
|
+
node = add_node("all", label="∀")
|
|
58
|
+
child = to_value(all_predicate)
|
|
59
|
+
dot.edge(node, child)
|
|
60
|
+
return node
|
|
61
|
+
case AlwaysFalsePredicate():
|
|
62
|
+
return add_node("F", label="false")
|
|
63
|
+
case AlwaysTruePredicate():
|
|
64
|
+
return add_node("T", label="true")
|
|
65
|
+
case AndPredicate(left, right):
|
|
66
|
+
node = add_node("and", label="∧")
|
|
67
|
+
left_node = to_value(left)
|
|
68
|
+
right_node = to_value(right)
|
|
69
|
+
dot.edge(node, left_node)
|
|
70
|
+
dot.edge(node, right_node)
|
|
71
|
+
return node
|
|
72
|
+
case AnyPredicate(any_predicate):
|
|
73
|
+
node = add_node("any", label="∃")
|
|
74
|
+
child = to_value(any_predicate)
|
|
75
|
+
dot.edge(node, child)
|
|
76
|
+
return node
|
|
77
|
+
case EqPredicate(v):
|
|
78
|
+
return add_node("eq", label=f"x = {v}")
|
|
79
|
+
case FnPredicate(predicate_fn):
|
|
80
|
+
name = predicate_fn.__code__.co_name
|
|
81
|
+
# code = inspect.getsource(predicate_fn)
|
|
82
|
+
# m = re.match(r".*\(predicate_fn=(.*)\)", code)
|
|
83
|
+
return add_node("fn", label=f"fn: {name}")
|
|
84
|
+
case GePredicate(v):
|
|
85
|
+
return add_node("ge", label=f"x ≥ {v}")
|
|
86
|
+
case GtPredicate(v):
|
|
87
|
+
return add_node("gt", label=f"x > {v}")
|
|
88
|
+
case InPredicate(v):
|
|
89
|
+
items = ", ".join(str(item) for item in v)
|
|
90
|
+
return add_node("in", label=f"x ∈ {{{items}}}")
|
|
91
|
+
case LePredicate(v):
|
|
92
|
+
return add_node("le", label=f"x ≤ {v}")
|
|
93
|
+
case LtPredicate(v):
|
|
94
|
+
return add_node("lt", label=f"x < {v}")
|
|
95
|
+
case NotInPredicate(v):
|
|
96
|
+
items = ", ".join(str(item) for item in v)
|
|
97
|
+
return add_node("in", label=f"x ∉ {{{items}}}")
|
|
98
|
+
case NePredicate(v):
|
|
99
|
+
return add_node("ne", label=f"x ≠ {v}")
|
|
100
|
+
case NotPredicate(not_predicate):
|
|
101
|
+
child = to_value(not_predicate)
|
|
102
|
+
node = add_node("not", label="¬")
|
|
103
|
+
dot.edge(node, child)
|
|
104
|
+
return node
|
|
105
|
+
case OrPredicate(left, right):
|
|
106
|
+
node = add_node("or", label="∨")
|
|
107
|
+
left_node = to_value(left)
|
|
108
|
+
right_node = to_value(right)
|
|
109
|
+
dot.edge(node, left_node)
|
|
110
|
+
dot.edge(node, right_node)
|
|
111
|
+
return node
|
|
112
|
+
case XorPredicate(left, right):
|
|
113
|
+
node = add_node("xor", label="⊻")
|
|
114
|
+
left_node = to_value(left)
|
|
115
|
+
right_node = to_value(right)
|
|
116
|
+
dot.edge(node, left_node)
|
|
117
|
+
dot.edge(node, right_node)
|
|
118
|
+
return node
|
|
119
|
+
case _:
|
|
120
|
+
raise ValueError(f"Unknown predicate type {predicate}")
|
|
121
|
+
|
|
122
|
+
to_value(predicate)
|
|
123
|
+
|
|
124
|
+
|
|
125
|
+
def render_original(dot, predicate: Predicate, node_nr):
|
|
126
|
+
with dot.subgraph(name="cluster_original") as original:
|
|
127
|
+
original.attr(style="filled", color="lightgrey")
|
|
128
|
+
original.attr(label="Original predicate")
|
|
129
|
+
render(original, predicate, node_nr)
|
|
130
|
+
|
|
131
|
+
|
|
132
|
+
def render_optimized(dot, predicate: Predicate, node_nr):
|
|
133
|
+
optimized_predicate = optimize(predicate)
|
|
134
|
+
|
|
135
|
+
with dot.subgraph(name="cluster_optimized") as optimized:
|
|
136
|
+
optimized.attr(style="filled", color="lightgrey")
|
|
137
|
+
optimized.attr(label="Optimized predicate")
|
|
138
|
+
render(optimized, optimized_predicate, node_nr)
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
from typing import Any
|
|
2
|
+
|
|
3
|
+
from predicate import (
|
|
4
|
+
AllPredicate,
|
|
5
|
+
AlwaysFalsePredicate,
|
|
6
|
+
AlwaysTruePredicate,
|
|
7
|
+
AndPredicate,
|
|
8
|
+
AnyPredicate,
|
|
9
|
+
FnPredicate,
|
|
10
|
+
NePredicate,
|
|
11
|
+
NotPredicate,
|
|
12
|
+
OrPredicate,
|
|
13
|
+
Predicate,
|
|
14
|
+
XorPredicate,
|
|
15
|
+
)
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
def to_json(predicate: Predicate) -> dict[str, Any]:
|
|
19
|
+
def to_value(predicate) -> tuple[str, Any]:
|
|
20
|
+
match predicate:
|
|
21
|
+
case AllPredicate(all_predicate):
|
|
22
|
+
return "all", {"predicate": to_json(all_predicate)}
|
|
23
|
+
case AlwaysFalsePredicate():
|
|
24
|
+
return "false", False
|
|
25
|
+
case AlwaysTruePredicate():
|
|
26
|
+
return "true", True
|
|
27
|
+
case AndPredicate(left, right):
|
|
28
|
+
return "and", {"left": to_json(left), "right": to_json(right)}
|
|
29
|
+
case AnyPredicate(any_predicate):
|
|
30
|
+
return "any", {"predicate": to_json(any_predicate)}
|
|
31
|
+
case FnPredicate(predicate_fn):
|
|
32
|
+
name = predicate_fn.__code__.co_name
|
|
33
|
+
return "fn", {"name": name}
|
|
34
|
+
case NePredicate(v):
|
|
35
|
+
return "ne", {"v": v}
|
|
36
|
+
case NotPredicate(not_predicate):
|
|
37
|
+
return "not", {"predicate": to_json(not_predicate)}
|
|
38
|
+
case OrPredicate(left, right):
|
|
39
|
+
return "or", {"left": to_json(left), "right": to_json(right)}
|
|
40
|
+
case XorPredicate(left, right):
|
|
41
|
+
return "xor", {"left": to_json(left), "right": to_json(right)}
|
|
42
|
+
case _:
|
|
43
|
+
return "unknown", {}
|
|
44
|
+
|
|
45
|
+
return dict([to_value(predicate)])
|
predicate/negate.py
ADDED
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
from functools import singledispatch
|
|
2
|
+
|
|
3
|
+
from predicate import (
|
|
4
|
+
AlwaysFalsePredicate,
|
|
5
|
+
AlwaysTruePredicate,
|
|
6
|
+
EqPredicate,
|
|
7
|
+
GePredicate,
|
|
8
|
+
GtPredicate,
|
|
9
|
+
InPredicate,
|
|
10
|
+
IsNonePredicate,
|
|
11
|
+
IsNotNonePredicate,
|
|
12
|
+
LePredicate,
|
|
13
|
+
LtPredicate,
|
|
14
|
+
NePredicate,
|
|
15
|
+
NotInPredicate,
|
|
16
|
+
NotPredicate,
|
|
17
|
+
Predicate,
|
|
18
|
+
)
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
@singledispatch
|
|
22
|
+
def negate[T](predicate: Predicate[T]) -> Predicate[T]:
|
|
23
|
+
return NotPredicate(predicate=predicate)
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
@negate.register
|
|
27
|
+
def negate_is_not(predicate: NotPredicate) -> Predicate:
|
|
28
|
+
return predicate.predicate
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
@negate.register
|
|
32
|
+
def negate_is_false(_predicate: AlwaysFalsePredicate) -> Predicate:
|
|
33
|
+
return AlwaysTruePredicate()
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
@negate.register
|
|
37
|
+
def negate_is_true(_predicate: AlwaysTruePredicate) -> Predicate:
|
|
38
|
+
return AlwaysFalsePredicate()
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
@negate.register
|
|
42
|
+
def negate_eq(predicate: EqPredicate) -> Predicate:
|
|
43
|
+
return NePredicate(v=predicate.v)
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
@negate.register
|
|
47
|
+
def negate_ne(predicate: NePredicate) -> Predicate:
|
|
48
|
+
return EqPredicate(v=predicate.v)
|
|
49
|
+
|
|
50
|
+
|
|
51
|
+
@negate.register
|
|
52
|
+
def negate_gt(predicate: GtPredicate) -> Predicate:
|
|
53
|
+
return LePredicate(v=predicate.v)
|
|
54
|
+
|
|
55
|
+
|
|
56
|
+
@negate.register
|
|
57
|
+
def negate_ge(predicate: GePredicate) -> Predicate:
|
|
58
|
+
return LtPredicate(v=predicate.v)
|
|
59
|
+
|
|
60
|
+
|
|
61
|
+
@negate.register
|
|
62
|
+
def negate_in(predicate: InPredicate) -> Predicate:
|
|
63
|
+
return NotInPredicate(v=predicate.v)
|
|
64
|
+
|
|
65
|
+
|
|
66
|
+
@negate.register
|
|
67
|
+
def negate_not_in(predicate: NotInPredicate) -> Predicate:
|
|
68
|
+
return InPredicate(v=predicate.v)
|
|
69
|
+
|
|
70
|
+
|
|
71
|
+
@negate.register
|
|
72
|
+
def negate_lt(predicate: LtPredicate) -> Predicate:
|
|
73
|
+
return GePredicate(v=predicate.v)
|
|
74
|
+
|
|
75
|
+
|
|
76
|
+
@negate.register
|
|
77
|
+
def negate_le(predicate: LePredicate) -> Predicate:
|
|
78
|
+
return GtPredicate(v=predicate.v)
|
|
79
|
+
|
|
80
|
+
|
|
81
|
+
@negate.register
|
|
82
|
+
def negate_is_none(_predicate: IsNonePredicate) -> Predicate:
|
|
83
|
+
return IsNotNonePredicate()
|
|
84
|
+
|
|
85
|
+
|
|
86
|
+
@negate.register
|
|
87
|
+
def negate_is_not_none(_predicate: IsNotNonePredicate) -> Predicate:
|
|
88
|
+
return IsNonePredicate()
|
|
File without changes
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
from predicate.predicate import (
|
|
2
|
+
AllPredicate,
|
|
3
|
+
AlwaysFalsePredicate,
|
|
4
|
+
AlwaysTruePredicate,
|
|
5
|
+
AnyPredicate,
|
|
6
|
+
IsEmptyPredicate,
|
|
7
|
+
IsNonePredicate,
|
|
8
|
+
IsNotNonePredicate,
|
|
9
|
+
NotPredicate,
|
|
10
|
+
Predicate,
|
|
11
|
+
)
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
def optimize_all_predicate[T](predicate: AllPredicate[T]) -> Predicate[T]:
|
|
15
|
+
from predicate.optimizer.predicate_optimizer import optimize
|
|
16
|
+
|
|
17
|
+
optimized = optimize(predicate.predicate)
|
|
18
|
+
|
|
19
|
+
match optimized:
|
|
20
|
+
case AlwaysTruePredicate():
|
|
21
|
+
return AlwaysTruePredicate()
|
|
22
|
+
case AlwaysFalsePredicate():
|
|
23
|
+
return IsEmptyPredicate()
|
|
24
|
+
case NotPredicate(not_predicate):
|
|
25
|
+
return NotPredicate(predicate=AnyPredicate(predicate=not_predicate))
|
|
26
|
+
case IsNotNonePredicate():
|
|
27
|
+
return NotPredicate(predicate=AnyPredicate(predicate=IsNonePredicate()))
|
|
28
|
+
case _:
|
|
29
|
+
pass
|
|
30
|
+
|
|
31
|
+
return AllPredicate(predicate=optimized)
|
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
from predicate.predicate import (
|
|
2
|
+
AllPredicate,
|
|
3
|
+
AlwaysFalsePredicate,
|
|
4
|
+
AlwaysTruePredicate,
|
|
5
|
+
AndPredicate,
|
|
6
|
+
EqPredicate,
|
|
7
|
+
FnPredicate,
|
|
8
|
+
GePredicate,
|
|
9
|
+
InPredicate,
|
|
10
|
+
NePredicate,
|
|
11
|
+
NotInPredicate,
|
|
12
|
+
NotPredicate,
|
|
13
|
+
OrPredicate,
|
|
14
|
+
Predicate,
|
|
15
|
+
)
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
def optimize_and_predicate[T](predicate: AndPredicate[T]) -> Predicate[T]:
|
|
19
|
+
from predicate.negate import negate
|
|
20
|
+
from predicate.optimizer.predicate_optimizer import optimize
|
|
21
|
+
|
|
22
|
+
match left := predicate.left, right := predicate.right:
|
|
23
|
+
case OrPredicate(or_left, or_right), _:
|
|
24
|
+
match or_left, or_right:
|
|
25
|
+
case NotPredicate(not_predicate), _ if not_predicate == right: # (~p | q) & p == q & p
|
|
26
|
+
return AndPredicate(left=or_right, right=right)
|
|
27
|
+
case _, NotPredicate(not_predicate) if not_predicate == right: # (q | ~p) & p == q & p
|
|
28
|
+
return AndPredicate(left=or_left, right=right)
|
|
29
|
+
|
|
30
|
+
case _, OrPredicate():
|
|
31
|
+
return optimize_and_predicate(AndPredicate(left=right, right=left))
|
|
32
|
+
|
|
33
|
+
case _, _ if left == negate(right):
|
|
34
|
+
return AlwaysFalsePredicate() # p & ~p == False
|
|
35
|
+
|
|
36
|
+
match left := optimize(left), right := optimize(right):
|
|
37
|
+
case _, AlwaysFalsePredicate(): # p & False = False
|
|
38
|
+
return AlwaysFalsePredicate()
|
|
39
|
+
case AlwaysFalsePredicate(), _: # False & p = False
|
|
40
|
+
return AlwaysFalsePredicate()
|
|
41
|
+
case _, AlwaysTruePredicate(): # p & True == p
|
|
42
|
+
return optimize(left)
|
|
43
|
+
case AlwaysTruePredicate(), _: # True & p == p
|
|
44
|
+
return optimize(right)
|
|
45
|
+
|
|
46
|
+
case EqPredicate(v1), EqPredicate(v2) if v1 == v2:
|
|
47
|
+
# x = v1 & x = v2 & v1 == v2 => x = v1
|
|
48
|
+
return left
|
|
49
|
+
case EqPredicate(v1), EqPredicate(v2) if v1 != v2:
|
|
50
|
+
# x = v1 & x = v2 & v1 != v2 => False
|
|
51
|
+
return AlwaysFalsePredicate()
|
|
52
|
+
case EqPredicate(v1), GePredicate(v2) if v1 == v2:
|
|
53
|
+
# x = v1 & x >= v2 & v1 = v2 => x = v1
|
|
54
|
+
return left
|
|
55
|
+
case EqPredicate(v1), GePredicate(v2) if v1 < v2:
|
|
56
|
+
# x = v1 & x >= v2 & v1 < v2 => False
|
|
57
|
+
return AlwaysFalsePredicate()
|
|
58
|
+
case GePredicate(v1), GePredicate(v2):
|
|
59
|
+
# x >= v1 & x >= v2 => x >= max(v1, v2)
|
|
60
|
+
return GePredicate(v=max(v1, v2))
|
|
61
|
+
|
|
62
|
+
case FnPredicate(predicate_fn), EqPredicate(v):
|
|
63
|
+
return AlwaysTruePredicate() if predicate_fn(v) else AlwaysFalsePredicate()
|
|
64
|
+
|
|
65
|
+
case InPredicate(v1), InPredicate(v2):
|
|
66
|
+
v = v1 & v2
|
|
67
|
+
if not v:
|
|
68
|
+
return AlwaysFalsePredicate()
|
|
69
|
+
if len(v) == 1:
|
|
70
|
+
return EqPredicate(v=v.pop())
|
|
71
|
+
return InPredicate(v=v)
|
|
72
|
+
|
|
73
|
+
case InPredicate(v1), NotInPredicate(v2):
|
|
74
|
+
v = v1 - v2
|
|
75
|
+
if not v:
|
|
76
|
+
return AlwaysFalsePredicate()
|
|
77
|
+
if len(v) == 1:
|
|
78
|
+
return EqPredicate(v=v.pop())
|
|
79
|
+
return InPredicate(v=v)
|
|
80
|
+
|
|
81
|
+
case NotInPredicate(v1), NotInPredicate(v2):
|
|
82
|
+
v = v1 | v2
|
|
83
|
+
if not v:
|
|
84
|
+
return AlwaysTruePredicate()
|
|
85
|
+
if len(v) == 1:
|
|
86
|
+
return NePredicate(v=v.pop())
|
|
87
|
+
return NotInPredicate(v=v)
|
|
88
|
+
|
|
89
|
+
case AllPredicate(left_all), AllPredicate(right_all):
|
|
90
|
+
# All(p1) & All(p2) => All(p1 & p2)
|
|
91
|
+
return optimize(AllPredicate(predicate=optimize(AndPredicate(left=left_all, right=right_all))))
|
|
92
|
+
|
|
93
|
+
case _, _ if and_contains_negate(predicate, right):
|
|
94
|
+
return AlwaysFalsePredicate() # p & q & ... & ~p == False
|
|
95
|
+
|
|
96
|
+
case _, _ if and_contains_negate(predicate, left):
|
|
97
|
+
return AlwaysFalsePredicate() # q & p & ... & ~p == False
|
|
98
|
+
|
|
99
|
+
case _, _ if left == negate(right):
|
|
100
|
+
return AlwaysFalsePredicate() # p & ~p == False
|
|
101
|
+
|
|
102
|
+
case _, _ if left == right: # p & p == p
|
|
103
|
+
return left
|
|
104
|
+
|
|
105
|
+
case _:
|
|
106
|
+
# return AndPredicate(left=left, right=right)
|
|
107
|
+
return predicate
|
|
108
|
+
|
|
109
|
+
|
|
110
|
+
def and_contains_negate(predicate: AndPredicate, sub_predicate: Predicate) -> bool:
|
|
111
|
+
from predicate.negate import negate
|
|
112
|
+
|
|
113
|
+
match left := predicate.left, right := predicate.right:
|
|
114
|
+
case AndPredicate() as and_left, _:
|
|
115
|
+
return and_contains_negate(and_left, sub_predicate)
|
|
116
|
+
case _, AndPredicate() as and_right:
|
|
117
|
+
return and_contains_negate(and_right, sub_predicate)
|
|
118
|
+
case AndPredicate() as and_left, AndPredicate() as and_right:
|
|
119
|
+
return and_contains_negate(and_left, sub_predicate) or and_contains_negate(and_right, sub_predicate)
|
|
120
|
+
case _:
|
|
121
|
+
return negate(sub_predicate) in (left, right)
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
from predicate.predicate import (
|
|
2
|
+
AllPredicate,
|
|
3
|
+
AlwaysFalsePredicate,
|
|
4
|
+
AlwaysTruePredicate,
|
|
5
|
+
AnyPredicate,
|
|
6
|
+
EqPredicate,
|
|
7
|
+
NePredicate,
|
|
8
|
+
NotPredicate,
|
|
9
|
+
Predicate,
|
|
10
|
+
)
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
def optimize_any_predicate[T](predicate: AnyPredicate[T]) -> Predicate[T]:
|
|
14
|
+
from predicate.optimizer.predicate_optimizer import optimize
|
|
15
|
+
|
|
16
|
+
optimized = optimize(predicate.predicate)
|
|
17
|
+
|
|
18
|
+
match optimized:
|
|
19
|
+
case AlwaysTruePredicate():
|
|
20
|
+
return AlwaysTruePredicate()
|
|
21
|
+
case AlwaysFalsePredicate():
|
|
22
|
+
return AlwaysFalsePredicate()
|
|
23
|
+
case NePredicate(v):
|
|
24
|
+
return NotPredicate(predicate=AllPredicate(predicate=EqPredicate(v)))
|
|
25
|
+
case NotPredicate(not_predicate):
|
|
26
|
+
return NotPredicate(predicate=AllPredicate(predicate=optimize(not_predicate)))
|
|
27
|
+
case _:
|
|
28
|
+
pass
|
|
29
|
+
|
|
30
|
+
return AnyPredicate(predicate=optimized)
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
from predicate.predicate import (
|
|
2
|
+
AllPredicate,
|
|
3
|
+
AndPredicate,
|
|
4
|
+
AnyPredicate,
|
|
5
|
+
NotPredicate,
|
|
6
|
+
OrPredicate,
|
|
7
|
+
Predicate,
|
|
8
|
+
XorPredicate,
|
|
9
|
+
)
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
def optimize_not_predicate[T](predicate: NotPredicate[T]) -> Predicate[T]:
|
|
13
|
+
from predicate.negate import negate
|
|
14
|
+
from predicate.optimizer.predicate_optimizer import optimize
|
|
15
|
+
|
|
16
|
+
# ~~p == p
|
|
17
|
+
match predicate.predicate:
|
|
18
|
+
case NotPredicate(not_predicate):
|
|
19
|
+
return optimize(not_predicate)
|
|
20
|
+
|
|
21
|
+
optimized = optimize(predicate.predicate)
|
|
22
|
+
|
|
23
|
+
match optimized:
|
|
24
|
+
case AllPredicate(all_predicate):
|
|
25
|
+
match negate(all_predicate):
|
|
26
|
+
case _ as inverted:
|
|
27
|
+
return AnyPredicate(predicate=inverted)
|
|
28
|
+
|
|
29
|
+
case AndPredicate(left, right):
|
|
30
|
+
match left, right:
|
|
31
|
+
case _, NotPredicate(not_predicate):
|
|
32
|
+
return OrPredicate(left=negate(left), right=not_predicate) # ~(p & ~q) => ~p | q
|
|
33
|
+
case NotPredicate(not_predicate), _:
|
|
34
|
+
return OrPredicate(left=not_predicate, right=negate(right)) # ~(~p & q) => p | ~q
|
|
35
|
+
|
|
36
|
+
case AnyPredicate(any_predicate):
|
|
37
|
+
match negate(any_predicate):
|
|
38
|
+
case _ as inverted:
|
|
39
|
+
return AllPredicate(predicate=inverted)
|
|
40
|
+
|
|
41
|
+
case OrPredicate(left, right):
|
|
42
|
+
match left, right:
|
|
43
|
+
case _, NotPredicate(not_predicate):
|
|
44
|
+
return AndPredicate(left=negate(left), right=not_predicate) # ~(p | ~q) => ~p & q
|
|
45
|
+
case NotPredicate(not_predicate), _:
|
|
46
|
+
return AndPredicate(left=not_predicate, right=negate(right)) # ~(~p | q) => p & ~q
|
|
47
|
+
|
|
48
|
+
case XorPredicate(left, right):
|
|
49
|
+
match left, right:
|
|
50
|
+
case NotPredicate(not_predicate), _: # ~(~p ^ q) == p ^ q
|
|
51
|
+
return XorPredicate(left=not_predicate, right=right)
|
|
52
|
+
case _, NotPredicate(not_predicate): # ~(p ^ ~q) == p ^ q
|
|
53
|
+
return XorPredicate(left=left, right=not_predicate)
|
|
54
|
+
case _: # ~(p ^ q) == ~p ^ q
|
|
55
|
+
return XorPredicate(left=NotPredicate(predicate=left), right=right)
|
|
56
|
+
|
|
57
|
+
return negate(optimized)
|