|
15 | 15 |
|
16 | 16 | from ... import abc
|
17 | 17 | from ...firstorder import And, _F, Not, Or, _T
|
18 |
| -from .atomic import AtomicFormula, DEFINITE, Eq, Ge, Le, Gt, Lt, Ne, SortKey, Term, Variable |
| 18 | +from .atomic import AtomicFormula, DEFINITE, Eq, Ge, Le, Gt, Lt, Ne, Term, Variable |
| 19 | +from .substitution import _SubstValue, _Substitution # type: ignore |
19 | 20 | from .typing import Formula
|
20 | 21 |
|
21 | 22 | from ...support.tracing import trace # noqa
|
@@ -65,10 +66,17 @@ def __post_init__(self) -> None:
|
65 | 66 | assert self.ropen or self.end is not oo, self
|
66 | 67 | assert all(self.start < x < self.end for x in self.exc), self
|
67 | 68 |
|
68 |
| - def __str__(self): |
| 69 | + def __str__(self) -> str: |
69 | 70 | left = '(' if self.lopen else '['
|
| 71 | + start = '-oo' if self.start == -oo else str(self.start) |
| 72 | + end = 'oo' if self.end == oo else str(self.end) |
70 | 73 | right = ')' if self.ropen else ']'
|
71 |
| - return f'{left}{self.start}, {self.end}{right} \\ {self.exc}' |
| 74 | + exc_entries = {str(q) for q in self.exc} |
| 75 | + if exc_entries: |
| 76 | + exc = f' \\ {{{", ".join(exc_entries)}}}' |
| 77 | + else: |
| 78 | + exc = '' |
| 79 | + return f'{left}{start}, {end}{right}{exc}' |
72 | 80 |
|
73 | 81 | def intersection(self, other: Self) -> Self:
|
74 | 82 | if self.start < other.start:
|
@@ -128,113 +136,6 @@ def transform(self, scale: mpq, shift: mpq) -> Self:
|
128 | 136 | return self.__class__(lopen, start, end, ropen, exc)
|
129 | 137 |
|
130 | 138 |
|
131 |
| -@dataclass(frozen=True) |
132 |
| -class _SubstValue: |
133 |
| - coefficient: mpq |
134 |
| - variable: Optional[Variable] |
135 |
| - |
136 |
| - def __eq__(self, other: Self) -> bool: # type: ignore[override] |
137 |
| - if self.coefficient != other.coefficient: |
138 |
| - return False |
139 |
| - if self.variable is None and other.variable is None: |
140 |
| - return True |
141 |
| - if self.variable is None or other.variable is None: |
142 |
| - return False |
143 |
| - return self.variable.sort_key() == other.variable.sort_key() |
144 |
| - |
145 |
| - def __post_init__(self) -> None: |
146 |
| - assert self.coefficient != 0 or self.variable is None |
147 |
| - |
148 |
| - @lru_cache(maxsize=CACHE_SIZE) |
149 |
| - def as_term(self) -> Term: |
150 |
| - if self.variable is None: |
151 |
| - return Term(self.coefficient) |
152 |
| - else: |
153 |
| - return self.coefficient * self.variable |
154 |
| - |
155 |
| - |
156 |
| -@dataclass(unsafe_hash=True) |
157 |
| -class _Substitution: |
158 |
| - parents: dict[SortKey[Variable], _SubstValue] = field(default_factory=dict) |
159 |
| - |
160 |
| - def __iter__(self) -> Iterator[tuple[Variable, _SubstValue]]: |
161 |
| - for key in self.parents: |
162 |
| - yield key.term, self.internal_find(key.term) |
163 |
| - |
164 |
| - def as_gb(self, ignore: Optional[Variable] = None) -> list[Term]: |
165 |
| - """Convert this :class:._Substitution` into a Gröbner basis that can be |
166 |
| - used as an argument to reduction methods. |
167 |
| - """ |
168 |
| - G = [] |
169 |
| - for var, val in self: |
170 |
| - if ignore is None or var.sort_key() != ignore.sort_key(): |
171 |
| - G.append(var - val.as_term()) |
172 |
| - return G |
173 |
| - |
174 |
| - def copy(self) -> Self: |
175 |
| - return self.__class__(self.parents.copy()) |
176 |
| - |
177 |
| - def find(self, v: Variable) -> _SubstValue: |
178 |
| - return self.internal_find(v) |
179 |
| - |
180 |
| - def internal_find(self, v: Optional[Variable]) -> _SubstValue: |
181 |
| - if v is None: |
182 |
| - return _SubstValue(mpq(1), None) |
183 |
| - sort_key = Variable.sort_key(v) |
184 |
| - try: |
185 |
| - parent = self.parents[sort_key] |
186 |
| - except KeyError: |
187 |
| - return _SubstValue(mpq(1), v) |
188 |
| - root = self.internal_find(parent.variable) |
189 |
| - root = _SubstValue(parent.coefficient * root.coefficient, root.variable) |
190 |
| - self.parents[sort_key] = root |
191 |
| - return root |
192 |
| - |
193 |
| - def union(self, val1: _SubstValue, val2: _SubstValue) -> None: |
194 |
| - root1 = self.internal_find(val1.variable) |
195 |
| - root2 = self.internal_find(val2.variable) |
196 |
| - c1 = val1.coefficient * root1.coefficient |
197 |
| - c2 = val2.coefficient * root2.coefficient |
198 |
| - if root1.variable is not None and root2.variable is not None: |
199 |
| - sort_key1 = Variable.sort_key(root1.variable) |
200 |
| - sort_key2 = Variable.sort_key(root2.variable) |
201 |
| - if sort_key1 == sort_key2: |
202 |
| - if c1 != c2: |
203 |
| - self.parents[sort_key1] = _SubstValue(mpq(0), None) |
204 |
| - elif sort_key1 < sort_key2: # type: ignore |
205 |
| - self.parents[sort_key2] = _SubstValue(c1 / c2, root1.variable) |
206 |
| - else: |
207 |
| - self.parents[sort_key1] = _SubstValue(c2 / c1, root2.variable) |
208 |
| - elif root1.variable is None and root2.variable is not None: |
209 |
| - sort_key2 = Variable.sort_key(root2.variable) |
210 |
| - self.parents[sort_key2] = _SubstValue(c1 / c2, None) |
211 |
| - elif root1.variable is not None and root2.variable is None: |
212 |
| - sort_key1 = Variable.sort_key(root1.variable) |
213 |
| - self.parents[sort_key1] = _SubstValue(c2 / c1, None) |
214 |
| - else: |
215 |
| - if c1 != c2: |
216 |
| - raise InternalRepresentation.Inconsistent() |
217 |
| - |
218 |
| - def is_redundant(self, val1: _SubstValue, val2: _SubstValue) -> Optional[bool]: |
219 |
| - """Check if the equation ``val1 == val2`` is redundant modulo self. |
220 |
| - """ |
221 |
| - root1 = self.internal_find(val1.variable) |
222 |
| - root2 = self.internal_find(val2.variable) |
223 |
| - c1 = val1.coefficient * root1.coefficient |
224 |
| - c2 = val2.coefficient * root2.coefficient |
225 |
| - if root1.variable is None and root2.variable is None: |
226 |
| - return c1 == c2 |
227 |
| - if root1.variable is None or root2.variable is None: |
228 |
| - return None |
229 |
| - if root1.variable == root2.variable and c1 == c2: |
230 |
| - return True |
231 |
| - else: |
232 |
| - return None |
233 |
| - |
234 |
| - def equations(self) -> Iterator[Eq]: |
235 |
| - raise NotImplementedError() |
236 |
| - |
237 |
| - |
238 | 139 | @dataclass
|
239 | 140 | class _BasicKnowledge:
|
240 | 141 |
|
@@ -429,6 +330,10 @@ def __iter__(self) -> Iterator[_BasicKnowledge]:
|
429 | 330 | for t, range_ in self.dict_.items():
|
430 | 331 | yield _BasicKnowledge(t, range_)
|
431 | 332 |
|
| 333 | + def __str__(self) -> str: |
| 334 | + entries = [str(key) + ' in ' + str(range) for key, range in self.dict_.items()] |
| 335 | + return f'{{{", ".join(entries)}}}' |
| 336 | + |
432 | 337 | def add(self, bknowl: _BasicKnowledge) -> None:
|
433 | 338 | bknowl = self.prune(bknowl)
|
434 | 339 | self.dict_[bknowl.term] = bknowl.range
|
@@ -638,18 +543,18 @@ def create_initial_representation(self, assume: Iterable[AtomicFormula]) \
|
638 | 543 | ir = InternalRepresentation(_options=self._options)
|
639 | 544 | for atom in assume:
|
640 | 545 | simplified_atom = self._simpl_at(atom, And, explode_always=False)
|
641 |
| - match simplified_atom: |
642 |
| - case AtomicFormula(): |
643 |
| - ir.add(And, [simplified_atom]) |
644 |
| - case And(args=args): |
645 |
| - assert all(isinstance(arg, AtomicFormula) for arg in args) |
646 |
| - ir.add(And, args) |
647 |
| - case _T(): |
648 |
| - continue |
649 |
| - case _F(): |
650 |
| - raise InternalRepresentation.Inconsistent() |
651 |
| - case _: |
652 |
| - assert False, simplified_atom |
| 546 | + if Formula.is_atomic(simplified_atom): |
| 547 | + ir.add(And, [simplified_atom]) |
| 548 | + elif Formula.is_and(simplified_atom): |
| 549 | + args = simplified_atom.args |
| 550 | + assert all(isinstance(arg, AtomicFormula) for arg in args) |
| 551 | + ir.add(And, args) |
| 552 | + elif Formula.is_true(simplified_atom): |
| 553 | + continue |
| 554 | + elif Formula.is_false(simplified_atom): |
| 555 | + raise InternalRepresentation.Inconsistent() |
| 556 | + else: |
| 557 | + assert False, simplified_atom |
653 | 558 | return ir
|
654 | 559 |
|
655 | 560 | def _post_process(self, f: Formula) -> Formula:
|
|
0 commit comments