comparison service/mqtt_to_rdf/inference.py @ 1692:2883da14847c

debugging and cleanup, as i looked for a bug
author drewp@bigasterisk.com
date Sat, 25 Sep 2021 22:20:00 -0700
parents aa35ae7a1acc
children 0455a1e18e4f
comparison
equal deleted inserted replaced
1691:37710d28890b 1692:2883da14847c
5 import itertools 5 import itertools
6 import logging 6 import logging
7 import time 7 import time
8 from collections import defaultdict 8 from collections import defaultdict
9 from dataclasses import dataclass 9 from dataclasses import dataclass
10 from typing import Dict, Iterator, List, Optional, Union, cast 10 from typing import Dict, Iterator, List, Optional, Tuple, Union, cast
11 11
12 from prometheus_client import Histogram, Summary 12 from prometheus_client import Histogram, Summary
13 from rdflib import Graph, Namespace 13 from rdflib import Graph, Namespace
14 from rdflib.graph import ConjunctiveGraph 14 from rdflib.graph import ConjunctiveGraph
15 from rdflib.term import Node, URIRef, Variable 15 from rdflib.term import Node, URIRef, Variable
129 for functionType in functionsFor(pred): 129 for functionType in functionsFor(pred):
130 fn = functionType(self.lhsChunk) 130 fn = functionType(self.lhsChunk)
131 ringlog.debug(f'{INDENT*7} ChunkLooper{self._shortId} advanceWithFunctions, {functionType=}') 131 ringlog.debug(f'{INDENT*7} ChunkLooper{self._shortId} advanceWithFunctions, {functionType=}')
132 132
133 try: 133 try:
134 134 log.debug(f'fn.bind {self._prevBindings()} ...')
135 #fullBinding = self._prevBindings().copy()
135 newBinding = fn.bind(self._prevBindings()) 136 newBinding = fn.bind(self._prevBindings())
137 log.debug(f'...makes {newBinding=}')
136 except BindingUnknown: 138 except BindingUnknown:
137 pass 139 pass
138 else: 140 else:
139 if newBinding is not None: 141 if newBinding is not None:
140 if self._testAndKeepNewBinding(newBinding): 142 if self._testAndKeepNewBinding(newBinding):
141 return True 143 return True
142 144
143 return False 145 return False
144 146
145 def _testAndKeepNewBinding(self, newBinding): 147 def _testAndKeepNewBinding(self, newBinding: CandidateBinding):
146 fullBinding: CandidateBinding = self._prevBindings().copy() 148 fullBinding: CandidateBinding = self._prevBindings().copy()
147 fullBinding.addNewBindings(newBinding) 149 fullBinding.addNewBindings(newBinding)
148 isNew = fullBinding not in self._seenBindings 150 isNew = fullBinding not in self._seenBindings
149 ringlog.debug(f'{INDENT*7} {self} considering {newBinding=} to make {fullBinding}. {isNew=}') 151 ringlog.debug(f'{INDENT*7} {self} considering {newBinding=} to make {fullBinding}. {isNew=}')
150 if isNew: 152 if isNew:
219 try: 221 try:
220 chunkStack = self._assembleRings(knownTrue, stats) 222 chunkStack = self._assembleRings(knownTrue, stats)
221 except NoOptions: 223 except NoOptions:
222 ringlog.debug(f'{INDENT*5} start up with no options; 0 bindings') 224 ringlog.debug(f'{INDENT*5} start up with no options; 0 bindings')
223 return 225 return
226 log.debug('')
227 log.debug('')
224 self._debugChunkStack('time to spin: initial odometer is', chunkStack) 228 self._debugChunkStack('time to spin: initial odometer is', chunkStack)
225 self._assertAllRingsAreValid(chunkStack) 229 self._assertAllRingsAreValid(chunkStack)
226 230
227 lastRing = chunkStack[-1] 231 lastRing = chunkStack[-1]
228 iterCount = 0 232 iterCount = 0
266 log.debug(f'{INDENT*4} stats={dict(stats)}') 270 log.debug(f'{INDENT*4} stats={dict(stats)}')
267 odolog.debug(f'{INDENT*3} build new ChunkLooper stack') 271 odolog.debug(f'{INDENT*3} build new ChunkLooper stack')
268 chunks = list(self.graph.patternChunks.union(self.graph.chunksUsedByFuncs)) 272 chunks = list(self.graph.patternChunks.union(self.graph.chunksUsedByFuncs))
269 chunks.sort(key=None) 273 chunks.sort(key=None)
270 odolog.info(f' {INDENT*3} taking permutations of {len(chunks)=}') 274 odolog.info(f' {INDENT*3} taking permutations of {len(chunks)=}')
271 for i, perm in enumerate(itertools.permutations(chunks)): 275
276 permsTried = 0
277
278 for perm in self._partitionedGraphPermutations():
272 looperRings: List[ChunkLooper] = [] 279 looperRings: List[ChunkLooper] = []
273 prev: Optional[ChunkLooper] = None 280 prev: Optional[ChunkLooper] = None
274 if odolog.isEnabledFor(logging.DEBUG): 281 if odolog.isEnabledFor(logging.DEBUG):
275 odolog.debug(f'{INDENT*4} [perm {i}] try rule chunks in this order: {" THEN ".join(repr(p) for p in perm)}') 282 odolog.debug(
283 f'{INDENT*4} [perm {permsTried}] try rule chunks in this order: {" THEN ".join(repr(p) for p in perm)}')
276 284
277 for ruleChunk in perm: 285 for ruleChunk in perm:
278 try: 286 try:
279 # These are getting rebuilt a lot which takes time. It would 287 # These are getting rebuilt a lot which takes time. It would
280 # be nice if they could accept a changing `prev` order 288 # be nice if they could accept a changing `prev` order
288 else: 296 else:
289 # bug: At this point we've only shown that these are valid 297 # bug: At this point we've only shown that these are valid
290 # starting rings. The rules might be tricky enough that this 298 # starting rings. The rules might be tricky enough that this
291 # permutation won't get us to the solution. 299 # permutation won't get us to the solution.
292 return looperRings 300 return looperRings
293 if i > 50000: 301 if permsTried > 50000:
294 raise NotImplementedError(f'trying too many permutations {len(chunks)=}') 302 raise NotImplementedError(f'trying too many permutations {len(chunks)=}')
303 permsTried += 1
295 304
296 odolog.debug(f'{INDENT*5} no perms worked- rule cannot match anything') 305 odolog.debug(f'{INDENT*5} no perms worked- rule cannot match anything')
297 raise NoOptions() 306 raise NoOptions()
307
308 def _unpartitionedGraphPermutations(self) -> Iterator[Tuple[Chunk, ...]]:
309 for perm in itertools.permutations(sorted(list(self.graph.patternChunks.union(self.graph.chunksUsedByFuncs)))):
310 yield perm
311
312 def _partitionedGraphPermutations(self) -> Iterator[Tuple[Chunk, ...]]:
313 """always puts function chunks after pattern chunks
314
315 (and, if we cared, static chunks could go before that. Currently they're
316 culled out elsewhere, but that's done as a special case)
317 """
318 tupleOfNoChunks: Tuple[Chunk, ...] = ()
319 pats = sorted(self.graph.patternChunks)
320 funcs = sorted(self.graph.chunksUsedByFuncs)
321 for patternPart in itertools.permutations(pats) if pats else [tupleOfNoChunks]:
322 for funcPart in itertools.permutations(funcs) if funcs else [tupleOfNoChunks]:
323 perm = patternPart + funcPart
324 yield perm
298 325
299 def _advanceTheStack(self, looperRings: List[ChunkLooper]) -> bool: 326 def _advanceTheStack(self, looperRings: List[ChunkLooper]) -> bool:
300 carry = True # last elem always must advance 327 carry = True # last elem always must advance
301 for i, ring in reversed(list(enumerate(looperRings))): 328 for i, ring in reversed(list(enumerate(looperRings))):
302 # unlike normal odometer, advancing any earlier ring could invalidate later ones 329 # unlike normal odometer, advancing any earlier ring could invalidate later ones
303 if carry: 330 if carry:
304 odolog.debug(f'{INDENT*4} advanceAll [{i}] {ring} carry/advance') 331 odolog.debug(f'{INDENT*4} advanceAll [ring={i}] {ring} carry/advance')
305 ring.advance() 332 ring.advance()
306 carry = False 333 carry = False
307 if ring.pastEnd(): 334 if ring.pastEnd():
308 if ring is looperRings[0]: 335 if ring is looperRings[0]:
309 allRingsDone = [r.pastEnd() for r in looperRings] 336 allRingsDone = [r.pastEnd() for r in looperRings]
310 odolog.debug(f'{INDENT*4} advanceAll [{i}] {ring} says we done {allRingsDone=}') 337 odolog.debug(f'{INDENT*5} advanceAll [ring={i}] {ring} says we done {allRingsDone=}')
311 return True 338 return True
312 odolog.debug(f'{INDENT*4} advanceAll [{i}] {ring} restart') 339 odolog.debug(f'{INDENT*5} advanceAll [ring={i}] {ring} restart')
313 ring.restart() 340 ring.restart()
314 carry = True 341 carry = True
315 return False 342 return False
316 343
317 def _assertAllRingsAreValid(self, looperRings): 344 def _assertAllRingsAreValid(self, looperRings):