changeset 1614:97b2c3cfdb83

refactor: inline an odometer algorithm in place of itertools.product
author drewp@bigasterisk.com
date Mon, 06 Sep 2021 23:04:34 -0700
parents 03ed8c9abd5b
children bcfa368e5498
files service/mqtt_to_rdf/inference.py
diffstat 1 files changed, 24 insertions(+), 6 deletions(-) [+]
line wrap: on
line diff
--- a/service/mqtt_to_rdf/inference.py	Mon Sep 06 23:03:51 2021 -0700
+++ b/service/mqtt_to_rdf/inference.py	Mon Sep 06 23:04:34 2021 -0700
@@ -87,16 +87,34 @@
     def _possibleBindings(self, workingSet, stats) -> Iterator['BoundLhs']:
         """this yields at least the working bindings, and possibly others"""
         candidateTermMatches: Dict[BindableTerm, Set[Node]] = self._allCandidateTermMatches(workingSet)
+        for bindRow in self._product(candidateTermMatches):
+            try:
+                yield BoundLhs(self, bindRow)
+            except EvaluationFailed:
+                stats['permCountFailingEval'] += 1
 
+    def _product(self, candidateTermMatches: Dict[BindableTerm, Set[Node]]) -> Iterator[CandidateBinding]:
         orderedVars, orderedValueSets = _organize(candidateTermMatches)
         self._logCandidates(orderedVars, orderedValueSets)
-
         log.debug(f'{INDENT*3} trying all permutations:')
-        for valueSet in itertools.product(*orderedValueSets):
-            try:
-                yield BoundLhs(self, CandidateBinding(dict(zip(orderedVars, valueSet))))
-            except EvaluationFailed:
-                stats['permCountFailingEval'] += 1
+        if not orderedValueSets:
+            yield CandidateBinding({})
+            return
+        if not orderedValueSets or not all(orderedValueSets):
+            # some var or bnode has no options at all
+            return
+        rings: List[Iterator[Node]] = [itertools.cycle(valSet) for valSet in orderedValueSets]
+        currentSet: List[Node] = [next(ring) for ring in rings]
+        starts = [valSet[-1] for valSet in orderedValueSets]
+        while True:
+            for col, curr in enumerate(currentSet):
+                currentSet[col] = next(rings[col])
+                log.debug(repr(currentSet))
+                yield CandidateBinding(dict(zip(orderedVars, currentSet)))
+                if curr is not starts[col]:
+                    break
+                if col == len(orderedValueSets) - 1:
+                    return
 
     def _allCandidateTermMatches(self, workingSet: ReadOnlyWorkingSet) -> Dict[BindableTerm, Set[Node]]:
         """the total set of terms each variable could possibly match"""