Mercurial > code > home > repos > homeauto
comparison service/mqtt_to_rdf/inference.py @ 1668:89e53cb8a01c
fix some harder tests. Mostly, _advanceTheStack needed to spin the odometer rings starting from the other side, to get all the right combos
author | drewp@bigasterisk.com |
---|---|
date | Tue, 21 Sep 2021 22:29:19 -0700 |
parents | a2347393b43e |
children | 9d00adef0b22 |
comparison
equal
deleted
inserted
replaced
1667:a2347393b43e | 1668:89e53cb8a01c |
---|---|
59 | 59 |
60 def __post_init__(self): | 60 def __post_init__(self): |
61 self._shortId = next(_chunkLooperShortId) | 61 self._shortId = next(_chunkLooperShortId) |
62 self._alignedMatches = list(self.lhsChunk.ruleMatchesFrom(self.workingSet)) | 62 self._alignedMatches = list(self.lhsChunk.ruleMatchesFrom(self.workingSet)) |
63 | 63 |
64 self._current = CandidateBinding({}) # only ours- do not store prev, since it could change without us | 64 # only ours- do not store prev, since it could change without us |
65 self._current = CandidateBinding({}) | |
65 self._pastEnd = False | 66 self._pastEnd = False |
66 self._seenBindings: List[CandidateBinding] = [] # combined bindings (up to our ring) that we've returned | 67 self._seenBindings: List[CandidateBinding] = [] # combined bindings (up to our ring) that we've returned |
67 | 68 |
68 if ringlog.isEnabledFor(logging.DEBUG): | 69 if ringlog.isEnabledFor(logging.DEBUG): |
69 ringlog.debug('') | 70 ringlog.debug('') |
76 return CandidateBinding({}) | 77 return CandidateBinding({}) |
77 | 78 |
78 return self.prev.currentBinding() | 79 return self.prev.currentBinding() |
79 | 80 |
80 def advance(self): | 81 def advance(self): |
81 """update to a new set of bindings we haven't seen (since last restart), or go into pastEnd mode""" | 82 """update _current to a new set of valid bindings we haven't seen (since |
83 last restart), or go into pastEnd mode. Note that _current is just our | |
84 contribution, but returned valid bindings include all prev rings.""" | |
82 if self._pastEnd: | 85 if self._pastEnd: |
83 raise NotImplementedError('need restart') | 86 raise NotImplementedError('need restart') |
84 ringlog.debug('') | 87 ringlog.debug('') |
85 augmentedWorkingSet: List[AlignedRuleChunk] = [] | 88 augmentedWorkingSet: List[AlignedRuleChunk] = [] |
86 if self.prev is None: | 89 if self.prev is None: |
87 augmentedWorkingSet = self._alignedMatches | 90 augmentedWorkingSet = self._alignedMatches |
88 else: | 91 else: |
89 augmentedWorkingSet = list( | 92 augmentedWorkingSet = list( |
90 applyChunky(self.prev.currentBinding(), self._alignedMatches, returnBoundStatementsOnly=False)) | 93 applyChunky(self.prev.currentBinding(), self._alignedMatches, returnBoundStatementsOnly=False)) |
91 | 94 |
92 | |
93 if self._advanceWithPlainMatches(augmentedWorkingSet): | 95 if self._advanceWithPlainMatches(augmentedWorkingSet): |
94 ringlog.debug(f'{INDENT*6} <-- {self}.advance finished with plain matches') | 96 ringlog.debug(f'{INDENT*6} <-- {self}.advance finished with plain matches') |
95 return | 97 return |
96 | 98 |
97 if self._advanceWithFunctions(): | 99 if self._advanceWithFunctions(): |
106 for s in augmentedWorkingSet: | 108 for s in augmentedWorkingSet: |
107 ringlog.debug(f'{INDENT*8} {s}') | 109 ringlog.debug(f'{INDENT*8} {s}') |
108 | 110 |
109 for aligned in augmentedWorkingSet: | 111 for aligned in augmentedWorkingSet: |
110 try: | 112 try: |
111 fullBinding = aligned.totalBindingIfThisStmtWereTrue(self._prevBindings()) | 113 newBinding = aligned.newBindingIfMatched(self._prevBindings()) |
112 except Inconsistent: | 114 except Inconsistent as exc: |
113 ringlog.debug(f'{INDENT*7} ChunkLooper{self._shortId} - {aligned} would be inconsistent with prev bindings') | 115 ringlog.debug( |
116 f'{INDENT*7} ChunkLooper{self._shortId} - {aligned} would be inconsistent with prev bindings ({exc})') | |
114 continue | 117 continue |
115 | 118 |
116 newBinding = fullBinding.copy() | 119 if self._testAndKeepNewBinding(newBinding): |
117 newBinding.subtract(self._prevBindings()) | |
118 | |
119 ringlog.debug(f'{INDENT*7} {newBinding=} {self._seenBindings=}') | |
120 if fullBinding not in self._seenBindings: | |
121 self._seenBindings.append(fullBinding.copy()) | |
122 self._current = newBinding | |
123 ringlog.debug(f'{INDENT*7} new binding from {self} -> {fullBinding}') | |
124 return True | 120 return True |
125 return False | 121 return False |
126 | 122 |
127 def _advanceWithFunctions(self) -> bool: | 123 def _advanceWithFunctions(self) -> bool: |
128 pred: Node = self.lhsChunk.predicate | 124 pred: Node = self.lhsChunk.predicate |
140 newBinding = fn.bind(self._prevBindings()) | 136 newBinding = fn.bind(self._prevBindings()) |
141 except BindingUnknown: | 137 except BindingUnknown: |
142 pass | 138 pass |
143 else: | 139 else: |
144 if newBinding is not None: | 140 if newBinding is not None: |
145 fullBinding: CandidateBinding = self._prevBindings().copy() | 141 if self._testAndKeepNewBinding(newBinding): |
146 fullBinding.addNewBindings(newBinding) | |
147 if fullBinding not in self._seenBindings: | |
148 self._seenBindings.append(fullBinding) | |
149 self._current = newBinding | |
150 ringlog.debug(f'{INDENT*7} new binding from {self} -> {fullBinding}') | |
151 return True | 142 return True |
152 | 143 |
144 return False | |
145 | |
146 def _testAndKeepNewBinding(self, newBinding): | |
147 fullBinding: CandidateBinding = self._prevBindings().copy() | |
148 fullBinding.addNewBindings(newBinding) | |
149 isNew = fullBinding not in self._seenBindings | |
150 ringlog.debug(f'{INDENT*7} {self} considering {newBinding=} to make {fullBinding}. {isNew=}') | |
151 if isNew: | |
152 self._seenBindings.append(fullBinding.copy()) | |
153 self._current = newBinding | |
154 return True | |
153 return False | 155 return False |
154 | 156 |
155 def _boundOperands(self, operands) -> List[Node]: | 157 def _boundOperands(self, operands) -> List[Node]: |
156 pb: CandidateBinding = self._prevBindings() | 158 pb: CandidateBinding = self._prevBindings() |
157 | 159 |
294 | 296 |
295 odolog.debug(f'{INDENT*5} no perms worked- rule cannot match anything') | 297 odolog.debug(f'{INDENT*5} no perms worked- rule cannot match anything') |
296 raise NoOptions() | 298 raise NoOptions() |
297 | 299 |
298 def _advanceTheStack(self, looperRings: List[ChunkLooper]) -> bool: | 300 def _advanceTheStack(self, looperRings: List[ChunkLooper]) -> bool: |
299 carry = True # 1st elem always must advance | 301 carry = True # last elem always must advance |
300 for i, ring in enumerate(looperRings): | 302 for i, ring in reversed(list(enumerate(looperRings))): |
301 # unlike normal odometer, advancing any earlier ring could invalidate later ones | 303 # unlike normal odometer, advancing any earlier ring could invalidate later ones |
302 if carry: | 304 if carry: |
303 odolog.debug(f'{INDENT*4} advanceAll [{i}] {ring} carry/advance') | 305 odolog.debug(f'{INDENT*4} advanceAll [{i}] {ring} carry/advance') |
304 ring.advance() | 306 ring.advance() |
305 carry = False | 307 carry = False |
306 if ring.pastEnd(): | 308 if ring.pastEnd(): |
307 if ring is looperRings[-1]: | 309 if ring is looperRings[0]: |
308 allRingsDone = [r.pastEnd() for r in looperRings] | 310 allRingsDone = [r.pastEnd() for r in looperRings] |
309 odolog.debug(f'{INDENT*4} advanceAll [{i}] {ring} says we done {allRingsDone=}') | 311 odolog.debug(f'{INDENT*4} advanceAll [{i}] {ring} says we done {allRingsDone=}') |
310 return True | 312 return True |
311 odolog.debug(f'{INDENT*4} advanceAll [{i}] {ring} restart') | 313 odolog.debug(f'{INDENT*4} advanceAll [{i}] {ring} restart') |
312 ring.restart() | 314 ring.restart() |