comparison service/mqtt_to_rdf/stmt_chunk.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 1a7c1261302c
children 9d00adef0b22
comparison
equal deleted inserted replaced
1667:a2347393b43e 1668:89e53cb8a01c
17 ChunkPrimaryTriple = Tuple[Optional[Node], Node, Optional[Node]] 17 ChunkPrimaryTriple = Tuple[Optional[Node], Node, Optional[Node]]
18 18
19 19
20 @dataclass 20 @dataclass
21 class AlignedRuleChunk: 21 class AlignedRuleChunk:
22 """a possible association between a rule chunk and a workingSet chunk. Use 22 """a possible association between a rule chunk and a workingSet chunk. You can test
23 matches() to see if the rule actually fits (and then we might cache some of 23 whether the association would still be possible under various additional bindings."""
24 that work when computing the new bindings"""
25 ruleChunk: 'Chunk' 24 ruleChunk: 'Chunk'
26 workingSetChunk: 'Chunk' 25 workingSetChunk: 'Chunk'
27 26
28 def totalBindingIfThisStmtWereTrue(self, prevBindings: CandidateBinding) -> CandidateBinding: 27 def __post_init__(self):
29 outBinding = prevBindings.copy() 28 if not self.matches():
29 raise Inconsistent()
30
31 def newBindingIfMatched(self, prevBindings: CandidateBinding) -> CandidateBinding:
32 """supposing this rule did match the statement, what new bindings would
33 that produce?
34
35 raises Inconsistent if the existing bindings mean that our aligned
36 chunks can no longer match.
37 """
38 outBinding = CandidateBinding({})
30 for rt, ct in zip(self.ruleChunk._allTerms(), self.workingSetChunk._allTerms()): 39 for rt, ct in zip(self.ruleChunk._allTerms(), self.workingSetChunk._allTerms()):
31 if isinstance(rt, (Variable, BNode)): 40 if isinstance(rt, (Variable, BNode)):
41 if prevBindings.contains(rt) and prevBindings.applyTerm(rt) != ct:
42 msg = f'{rt=} {ct=} {prevBindings=}' if log.isEnabledFor(logging.DEBUG) else ''
43 raise Inconsistent(msg)
32 if outBinding.contains(rt) and outBinding.applyTerm(rt) != ct: 44 if outBinding.contains(rt) and outBinding.applyTerm(rt) != ct:
33 msg = f'{rt=} {ct=} {outBinding=}' if log.isEnabledFor(logging.DEBUG) else '' 45 # maybe this can happen, for stmts like ?x :a ?x .
34 raise Inconsistent(msg) 46 raise Inconsistent("outBinding inconsistent with itself")
35 outBinding.addNewBindings(CandidateBinding({rt: ct})) 47 outBinding.addNewBindings(CandidateBinding({rt: ct}))
48 else:
49 if rt != ct:
50 # getting here means prevBindings was set to something our
51 # rule statement disagrees with.
52 raise Inconsistent(f'{rt=} != {ct=}')
36 return outBinding 53 return outBinding
37 54
38 # could combine this and totalBindingIf into a single ChunkMatch object
39 def matches(self) -> bool: 55 def matches(self) -> bool:
40 """could this rule, with its BindableTerm wildcards, match workingSetChunk?""" 56 """could this rule, with its BindableTerm wildcards, match workingSetChunk?"""
41 for selfTerm, otherTerm in zip(self.ruleChunk._allTerms(), self.workingSetChunk._allTerms()): 57 for selfTerm, otherTerm in zip(self.ruleChunk._allTerms(), self.workingSetChunk._allTerms()):
42 if not isinstance(selfTerm, (Variable, BNode)) and selfTerm != otherTerm: 58 if not isinstance(selfTerm, (Variable, BNode)) and selfTerm != otherTerm:
43 return False 59 return False
93 # log.debug(f'{INDENT*6} computing {self}.ruleMatchesFrom({workingSet}') 109 # log.debug(f'{INDENT*6} computing {self}.ruleMatchesFrom({workingSet}')
94 allChunksIter = workingSet.allChunks() 110 allChunksIter = workingSet.allChunks()
95 if "stable failures please": 111 if "stable failures please":
96 allChunksIter = sorted(allChunksIter) 112 allChunksIter = sorted(allChunksIter)
97 for chunk in allChunksIter: 113 for chunk in allChunksIter:
98 aligned = AlignedRuleChunk(self, chunk) 114 try:
99 if aligned.matches(): 115 aligned = AlignedRuleChunk(self, chunk)
100 yield aligned 116 except Inconsistent:
117 continue
118 yield aligned
101 119
102 def __repr__(self): 120 def __repr__(self):
103 pre = ('+'.join('%s' % elem for elem in self.subjList) + '+' if self.subjList else '') 121 pre = ('+'.join('%s' % elem for elem in self.subjList) + '+' if self.subjList else '')
104 post = ('+' + '+'.join('%s' % elem for elem in self.objList) if self.objList else '') 122 post = ('+' + '+'.join('%s' % elem for elem in self.objList) if self.objList else '')
105 return pre + repr(self.primary) + post 123 return pre + repr(self.primary) + post
130 148
131 def applyChunky(cb: CandidateBinding, 149 def applyChunky(cb: CandidateBinding,
132 g: Iterable[AlignedRuleChunk], 150 g: Iterable[AlignedRuleChunk],
133 returnBoundStatementsOnly=True) -> Iterator[AlignedRuleChunk]: 151 returnBoundStatementsOnly=True) -> Iterator[AlignedRuleChunk]:
134 for aligned in g: 152 for aligned in g:
153 bound = aligned.ruleChunk.apply(cb, returnBoundStatementsOnly=returnBoundStatementsOnly)
135 try: 154 try:
136 bound = aligned.ruleChunk.apply(cb, returnBoundStatementsOnly=returnBoundStatementsOnly) 155 yield AlignedRuleChunk(bound, aligned.workingSetChunk)
137 except BindingUnknown: 156 except Inconsistent:
138 log.debug(f'{INDENT*7} CB.apply cant bind {aligned} using {cb.binding}') 157 pass
139
140 continue
141 log.debug(f'{INDENT*7} CB.apply took {aligned} to {bound}')
142
143 yield AlignedRuleChunk(bound, aligned.workingSetChunk)
144 158
145 159
146 class ChunkedGraph: 160 class ChunkedGraph:
147 """a Graph converts 1-to-1 with a ChunkedGraph, where the Chunks have 161 """a Graph converts 1-to-1 with a ChunkedGraph, where the Chunks have
148 combined some statements together. (The only exception is that bnodes for 162 combined some statements together. (The only exception is that bnodes for