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()