Mercurial > code > home > repos > homeauto
changeset 1592:d7b66234064b
pure reordering of funcs to make the next diffs smaller
author | drewp@bigasterisk.com |
---|---|
date | Sat, 04 Sep 2021 23:18:44 -0700 |
parents | 668958454ae2 |
children | b0df43d5494c |
files | service/mqtt_to_rdf/inference.py |
diffstat | 1 files changed, 208 insertions(+), 208 deletions(-) [+] |
line wrap: on
line diff
--- a/service/mqtt_to_rdf/inference.py Sat Sep 04 15:34:29 2021 -0700 +++ b/service/mqtt_to_rdf/inference.py Sat Sep 04 23:18:44 2021 -0700 @@ -35,6 +35,176 @@ vars: Dict[Variable, Node] +inferredFuncs = { + ROOM['asFarenheit'], + MATH['sum'], +} +filterFuncs = { + MATH['greaterThan'], +} + + +def withBinding(toBind: Graph, bindings: Dict[BindableTerm, Node], includeStaticStmts=True) -> Iterator[Triple]: + for stmt in toBind: + stmt = list(stmt) + static = True + for i, term in enumerate(stmt): + if isinstance(term, (Variable, BNode)): + stmt[i] = bindings[term] + static = False + else: + if includeStaticStmts or not static: + yield cast(Triple, stmt) + + +def verifyBinding(lhs: Graph, binding: Dict[BindableTerm, Node], workingSet: Graph, usedByFuncs: Graph) -> bool: + """Can this lhs be true all at once in workingSet? Does it match with these bindings?""" + boundLhs = list(withBinding(lhs, binding)) + boundUsedByFuncs = list(withBinding(usedByFuncs, binding)) + if log.isEnabledFor(logging.DEBUG): + log.debug(f' verify all bindings against this lhs:') + for stmt in boundLhs: + log.debug(f' {stmt}') + + log.debug(f' and against this workingSet:') + for stmt in workingSet: + log.debug(f' {stmt}') + + log.debug(f' ignoring these usedByFuncs:') + for stmt in boundUsedByFuncs: + log.debug(f' {stmt}') + # The static stmts in lhs are obviously going + # to match- we only need to verify the ones + # that needed bindings. + for stmt in boundLhs: #withBinding(lhs, binding, includeStaticStmts=False): + log.debug(f' check for {stmt}') + + if stmt[1] in filterFuncs: + if not mathTest(*stmt): + log.debug(f' binding was invalid because {stmt}) is not true') + return False + elif stmt in boundUsedByFuncs: + pass + elif stmt in workingSet: + pass + else: + log.debug(f' binding was invalid because {stmt}) cannot be true') + return False + return True + + +def findCandidateBindings(lhs: Graph, workingSet: Graph) -> Iterator[Dict[BindableTerm, Node]]: + """bindings that fit the LHS of a rule, using statements from workingSet and functions + from LHS""" + varsToBind: Set[BindableTerm] = set() + staticRuleStmts = Graph() + for ruleStmt in lhs: + varsInStmt = [v for v in ruleStmt if isinstance(v, (Variable, BNode))] + varsToBind.update(varsInStmt) + if (not varsInStmt # ok + #and not any(isinstance(t, BNode) for t in ruleStmt) # approx + ): + staticRuleStmts.add(ruleStmt) + + log.debug(f' varsToBind: {sorted(varsToBind)}') + + if someStaticStmtDoesntMatch(staticRuleStmts, workingSet): + log.debug(f' someStaticStmtDoesntMatch: {graphDump(staticRuleStmts)}') + return + + # the total set of terms each variable could possibly match + candidateTermMatches: Dict[BindableTerm, Set[Node]] = findCandidateTermMatches(lhs, workingSet) + + orderedVars, orderedValueSets = organize(candidateTermMatches) + + log.debug(f' candidate terms:') + log.debug(f' {orderedVars=}') + log.debug(f' {orderedValueSets=}') + + for i, perm in enumerate(itertools.product(*orderedValueSets)): + binding: Dict[BindableTerm, Node] = dict(zip(orderedVars, perm)) + log.debug('') + log.debug(f' ** trying {binding=}') + usedByFuncs = Graph() + for v, val, used in inferredFuncBindings(lhs, binding): # loop this until it's done + log.debug(f' inferredFuncBindings tells us {v}={val}') + binding[v] = val + usedByFuncs += used + if len(binding) != len(varsToBind): + log.debug(f' binding is incomplete, needs {varsToBind}') + + continue + if not verifyBinding(lhs, binding, workingSet, usedByFuncs): # fix this + log.debug(f' this binding did not verify') + continue + yield binding + + +def someStaticStmtDoesntMatch(staticRuleStmts, workingSet): + for ruleStmt in staticRuleStmts: + if ruleStmt not in workingSet: + log.debug(f' {ruleStmt} not in working set- skip rule') + + return True + return False + + +def findCandidateTermMatches(lhs: Graph, workingSet: Graph) -> Dict[BindableTerm, Set[Node]]: + candidateTermMatches: Dict[BindableTerm, Set[Node]] = defaultdict(set) + lhsBnodes: Set[BNode] = set() + for lhsStmt in lhs: + for trueStmt in workingSet: + log.debug(f' lhsStmt={graphDump([lhsStmt])} trueStmt={graphDump([trueStmt])}') + bindingsFromStatement: Dict[Variable, Set[Node]] = {} + for lhsTerm, trueTerm in zip(lhsStmt, trueStmt): + # log.debug(f' test {lhsTerm=} {trueTerm=}') + if isinstance(lhsTerm, BNode): + lhsBnodes.add(lhsTerm) + elif isinstance(lhsTerm, Variable): + bindingsFromStatement.setdefault(lhsTerm, set()).add(trueTerm) + elif lhsTerm != trueTerm: + break + else: + for v, vals in bindingsFromStatement.items(): + candidateTermMatches[v].update(vals) + + for trueStmt in itertools.chain(workingSet, lhs): + for b in lhsBnodes: + for t in [trueStmt[0], trueStmt[2]]: + if isinstance(t, (URIRef, BNode)): + candidateTermMatches[b].add(t) + return candidateTermMatches + + +def inferredFuncObject(subj, pred, graph, bindings) -> Tuple[Literal, Graph]: + """return result from like `(1 2) math:sum ?out .` plus a graph of all the + statements involved in that function rule (including the bound answer""" + used = Graph() + if pred == ROOM['asFarenheit']: + obj = Literal(Decimal(subj.toPython()) * 9 / 5 + 32) + elif pred == MATH['sum']: + operands, operandsStmts = parseList(graph, subj) + # shouldn't be redoing this here + operands = [bindings[o] if isinstance(o, Variable) else o for o in operands] + log.debug(f' sum {[op.toPython() for op in operands]}') + used += operandsStmts + obj = Literal(sum(op.toPython() for op in operands)) + else: + raise NotImplementedError(pred) + + used.add((subj, pred, obj)) + return obj, used + + +def mathTest(subj, pred, obj): + x = subj.toPython() + y = obj.toPython() + if pred == MATH['greaterThan']: + return x > y + else: + raise NotImplementedError(pred) + + class Inference: def __init__(self) -> None: @@ -93,6 +263,33 @@ return out +def applyRule(lhs: Graph, rhs: Graph, workingSet: Graph, implied: Graph): + for bindings in findCandidateBindings(lhs, workingSet): + log.debug(f' rule gave {bindings=}') + for lhsBoundStmt in withBinding(lhs, bindings): + workingSet.add(lhsBoundStmt) + for newStmt in withBinding(rhs, bindings): + workingSet.add(newStmt) + implied.add(newStmt) + + +def parseList(graph, subj) -> Tuple[List[Node], Set[Triple]]: + out = [] + used = set() + cur = subj + while True: + # bug: mishandles empty list + out.append(graph.value(cur, RDF.first)) + used.add((cur, RDF.first, out[-1])) + + next = graph.value(cur, RDF.rest) + used.add((cur, RDF.rest, next)) + cur = next + if cur == RDF.nil: + break + return out, used + + def graphDump(g: Union[Graph, List[Triple]]): if not isinstance(g, Graph): g2 = Graph() @@ -106,61 +303,18 @@ return ' '.join(lines) -def applyRule(lhs: Graph, rhs: Graph, workingSet: Graph, implied: Graph): - for bindings in findCandidateBindings(lhs, workingSet): - log.debug(f' rule gave {bindings=}') - for lhsBoundStmt in withBinding(lhs, bindings): - workingSet.add(lhsBoundStmt) - for newStmt in withBinding(rhs, bindings): - workingSet.add(newStmt) - implied.add(newStmt) - - -def findCandidateBindings(lhs: Graph, workingSet: Graph) -> Iterator[Dict[BindableTerm, Node]]: - """bindings that fit the LHS of a rule, using statements from workingSet and functions - from LHS""" - varsToBind: Set[BindableTerm] = set() - staticRuleStmts = Graph() - for ruleStmt in lhs: - varsInStmt = [v for v in ruleStmt if isinstance(v, (Variable, BNode))] - varsToBind.update(varsInStmt) - if (not varsInStmt # ok - #and not any(isinstance(t, BNode) for t in ruleStmt) # approx - ): - staticRuleStmts.add(ruleStmt) - - log.debug(f' varsToBind: {sorted(varsToBind)}') +def organize(candidateTermMatches: Dict[BindableTerm, Set[Node]]) -> Tuple[List[BindableTerm], List[List[Node]]]: + items = list(candidateTermMatches.items()) + items.sort() + orderedVars: List[BindableTerm] = [] + orderedValueSets: List[List[Node]] = [] + for v, vals in items: + orderedVars.append(v) + orderedValues: List[Node] = list(vals) + orderedValues.sort(key=str) + orderedValueSets.append(orderedValues) - if someStaticStmtDoesntMatch(staticRuleStmts, workingSet): - log.debug(f' someStaticStmtDoesntMatch: {graphDump(staticRuleStmts)}') - return - - # the total set of terms each variable could possibly match - candidateTermMatches: Dict[BindableTerm, Set[Node]] = findCandidateTermMatches(lhs, workingSet) - - orderedVars, orderedValueSets = organize(candidateTermMatches) - - log.debug(f' candidate terms:') - log.debug(f' {orderedVars=}') - log.debug(f' {orderedValueSets=}') - - for i, perm in enumerate(itertools.product(*orderedValueSets)): - binding: Dict[BindableTerm, Node] = dict(zip(orderedVars, perm)) - log.debug('') - log.debug(f' ** trying {binding=}') - usedByFuncs = Graph() - for v, val, used in inferredFuncBindings(lhs, binding): # loop this until it's done - log.debug(f' inferredFuncBindings tells us {v}={val}') - binding[v] = val - usedByFuncs += used - if len(binding) != len(varsToBind): - log.debug(f' binding is incomplete, needs {varsToBind}') - - continue - if not verifyBinding(lhs, binding, workingSet, usedByFuncs): # fix this - log.debug(f' this binding did not verify') - continue - yield binding + return orderedVars, orderedValueSets def inferredFuncBindings(lhs: Graph, bindingsBefore) -> Iterator[Tuple[Variable, Node, Graph]]: @@ -180,162 +334,8 @@ yield var, resultObject, usedByFunc -def findCandidateTermMatches(lhs: Graph, workingSet: Graph) -> Dict[BindableTerm, Set[Node]]: - candidateTermMatches: Dict[BindableTerm, Set[Node]] = defaultdict(set) - lhsBnodes: Set[BNode] = set() - for lhsStmt in lhs: - for trueStmt in workingSet: - log.debug(f' lhsStmt={graphDump([lhsStmt])} trueStmt={graphDump([trueStmt])}') - bindingsFromStatement: Dict[Variable, Set[Node]] = {} - for lhsTerm, trueTerm in zip(lhsStmt, trueStmt): - # log.debug(f' test {lhsTerm=} {trueTerm=}') - if isinstance(lhsTerm, BNode): - lhsBnodes.add(lhsTerm) - elif isinstance(lhsTerm, Variable): - bindingsFromStatement.setdefault(lhsTerm, set()).add(trueTerm) - elif lhsTerm != trueTerm: - break - else: - for v, vals in bindingsFromStatement.items(): - candidateTermMatches[v].update(vals) - - for trueStmt in itertools.chain(workingSet, lhs): - for b in lhsBnodes: - for t in [trueStmt[0], trueStmt[2]]: - if isinstance(t, (URIRef, BNode)): - candidateTermMatches[b].add(t) - return candidateTermMatches - - -def withBinding(toBind: Graph, bindings: Dict[BindableTerm, Node], includeStaticStmts=True) -> Iterator[Triple]: - for stmt in toBind: - stmt = list(stmt) - static = True - for i, term in enumerate(stmt): - if isinstance(term, (Variable, BNode)): - stmt[i] = bindings[term] - static = False - else: - if includeStaticStmts or not static: - yield cast(Triple, stmt) - - -def verifyBinding(lhs: Graph, binding: Dict[BindableTerm, Node], workingSet: Graph, usedByFuncs: Graph) -> bool: - """Can this lhs be true all at once in workingSet? Does it match with these bindings?""" - boundLhs = list(withBinding(lhs, binding)) - boundUsedByFuncs = list(withBinding(usedByFuncs, binding)) - if log.isEnabledFor(logging.DEBUG): - log.debug(f' verify all bindings against this lhs:') - for stmt in boundLhs: - log.debug(f' {stmt}') - - log.debug(f' and against this workingSet:') - for stmt in workingSet: - log.debug(f' {stmt}') - - log.debug(f' ignoring these usedByFuncs:') - for stmt in boundUsedByFuncs: - log.debug(f' {stmt}') - # The static stmts in lhs are obviously going - # to match- we only need to verify the ones - # that needed bindings. - for stmt in boundLhs: #withBinding(lhs, binding, includeStaticStmts=False): - log.debug(f' check for {stmt}') - - if stmt[1] in filterFuncs: - if not mathTest(*stmt): - log.debug(f' binding was invalid because {stmt}) is not true') - return False - elif stmt in boundUsedByFuncs: - pass - elif stmt in workingSet: - pass - else: - log.debug(f' binding was invalid because {stmt}) cannot be true') - return False - return True - - -inferredFuncs = { - ROOM['asFarenheit'], - MATH['sum'], -} -filterFuncs = { - MATH['greaterThan'], -} - - def isStatic(spo: Triple): for t in spo: if isinstance(t, (Variable, BNode)): return False return True - - -def inferredFuncObject(subj, pred, graph, bindings) -> Tuple[Literal, Graph]: - """return result from like `(1 2) math:sum ?out .` plus a graph of all the - statements involved in that function rule (including the bound answer""" - used = Graph() - if pred == ROOM['asFarenheit']: - obj = Literal(Decimal(subj.toPython()) * 9 / 5 + 32) - elif pred == MATH['sum']: - operands, operandsStmts = parseList(graph, subj) - # shouldn't be redoing this here - operands = [bindings[o] if isinstance(o, Variable) else o for o in operands] - log.debug(f' sum {[op.toPython() for op in operands]}') - used += operandsStmts - obj = Literal(sum(op.toPython() for op in operands)) - else: - raise NotImplementedError(pred) - - used.add((subj, pred, obj)) - return obj, used - - -def parseList(graph, subj) -> Tuple[List[Node], Set[Triple]]: - out = [] - used = set() - cur = subj - while True: - # bug: mishandles empty list - out.append(graph.value(cur, RDF.first)) - used.add((cur, RDF.first, out[-1])) - - next = graph.value(cur, RDF.rest) - used.add((cur, RDF.rest, next)) - cur = next - if cur == RDF.nil: - break - return out, used - - -def mathTest(subj, pred, obj): - x = subj.toPython() - y = obj.toPython() - if pred == MATH['greaterThan']: - return x > y - else: - raise NotImplementedError(pred) - - -def organize(candidateTermMatches: Dict[BindableTerm, Set[Node]]) -> Tuple[List[BindableTerm], List[List[Node]]]: - items = list(candidateTermMatches.items()) - items.sort() - orderedVars: List[BindableTerm] = [] - orderedValueSets: List[List[Node]] = [] - for v, vals in items: - orderedVars.append(v) - orderedValues: List[Node] = list(vals) - orderedValues.sort(key=str) - orderedValueSets.append(orderedValues) - - return orderedVars, orderedValueSets - - -def someStaticStmtDoesntMatch(staticRuleStmts, workingSet): - for ruleStmt in staticRuleStmts: - if ruleStmt not in workingSet: - log.debug(f' {ruleStmt} not in working set- skip rule') - - return True - return False