comparison service/mqtt_to_rdf/lhs_evaluation.py @ 1651:20474ad4968e

WIP - functions are broken as i move most layers to work in Chunks not Triples A Chunk is a Triple plus any rdf lists.
author drewp@bigasterisk.com
date Sat, 18 Sep 2021 23:57:20 -0700
parents 3059f31b2dfa
children 7ec2483d61b5
comparison
equal deleted inserted replaced
1650:2061df259224 1651:20474ad4968e
1 from dataclasses import dataclass
2 import logging 1 import logging
3 from decimal import Decimal 2 from decimal import Decimal
4 from candidate_binding import CandidateBinding 3 from typing import (Dict, Iterator, List, Optional, Set, Tuple, Type, Union, cast)
5 from typing import Dict, Iterator, List, Optional, Set, Tuple, Type, Union, cast
6 4
7 from prometheus_client import Summary 5 from prometheus_client import Summary
8 from rdflib import RDF, Literal, Namespace, URIRef 6 from rdflib import RDF, Literal, Namespace, URIRef
9 from rdflib.graph import Graph 7 from rdflib.term import Node, Variable
10 from rdflib.term import BNode, Node, Variable
11 8
9 from candidate_binding import CandidateBinding
12 from inference_types import BindableTerm, Triple 10 from inference_types import BindableTerm, Triple
11 from stmt_chunk import Chunk, ChunkedGraph
13 12
14 log = logging.getLogger('infer') 13 log = logging.getLogger('infer')
15 14
16 INDENT = ' ' 15 INDENT = ' '
17 16
27 if not isinstance(val, (int, float, Decimal)): 26 if not isinstance(val, (int, float, Decimal)):
28 raise TypeError(f'expected number, got {val=}') 27 raise TypeError(f'expected number, got {val=}')
29 return val 28 return val
30 29
31 30
32 def parseList(graph, subj) -> Tuple[List[Node], Set[Triple]]: 31 def parseList(graph: ChunkedGraph, subj: Node) -> Tuple[List[Node], Set[Triple]]:
33 """"Do like Collection(g, subj) but also return all the 32 """"Do like Collection(g, subj) but also return all the
34 triples that are involved in the list""" 33 triples that are involved in the list"""
35 out = [] 34 out = []
36 used = set() 35 used = set()
37 cur = subj 36 cur = subj
61 60
62 class Function: 61 class Function:
63 """any rule stmt that runs a function (not just a statement match)""" 62 """any rule stmt that runs a function (not just a statement match)"""
64 pred: URIRef 63 pred: URIRef
65 64
66 def __init__(self, stmt: Triple, ruleGraph: Graph): 65 def __init__(self, chunk: Chunk, ruleGraph: ChunkedGraph):
67 self.stmt = stmt 66 self.chunk = chunk
68 if stmt[1] != self.pred: 67 if chunk.predicate != self.pred:
69 raise TypeError 68 raise TypeError
70 self.ruleGraph = ruleGraph 69 self.ruleGraph = ruleGraph
71 70
72 def getOperandNodes(self, existingBinding: CandidateBinding) -> List[Node]: 71 def getOperandNodes(self, existingBinding: CandidateBinding) -> List[Node]:
73 raise NotImplementedError 72 raise NotImplementedError
82 def bind(self, existingBinding: CandidateBinding) -> Optional[CandidateBinding]: 81 def bind(self, existingBinding: CandidateBinding) -> Optional[CandidateBinding]:
83 """either any new bindings this function makes (could be 0), or None if it doesn't match""" 82 """either any new bindings this function makes (could be 0), or None if it doesn't match"""
84 raise NotImplementedError 83 raise NotImplementedError
85 84
86 def valueInObjectTerm(self, value: Node) -> Optional[CandidateBinding]: 85 def valueInObjectTerm(self, value: Node) -> Optional[CandidateBinding]:
87 objVar = self.stmt[2] 86 objVar = self.chunk.primary[2]
88 if not isinstance(objVar, Variable): 87 if not isinstance(objVar, Variable):
89 raise TypeError(f'expected Variable, got {objVar!r}') 88 raise TypeError(f'expected Variable, got {objVar!r}')
90 return CandidateBinding({cast(BindableTerm, objVar): value}) 89 return CandidateBinding({cast(BindableTerm, objVar): value})
91 90
92 def usedStatements(self) -> Set[Triple]: 91 def usedStatements(self) -> Set[Triple]:
93 '''stmts in self.graph (not including self.stmt, oddly) that are part of 92 '''stmts in self.graph (not including self.stmt, oddly) that are part of
94 this function setup and aren't to be matched literally''' 93 this function setup and aren't to be matched literally'''
95 return set() 94 return set()
96 95
97 96
98 class SubjectFunction(Function): 97 class SubjectFunction(Function):
99 """function that depends only on the subject term""" 98 """function that depends only on the subject term"""
100 99
101 def getOperandNodes(self, existingBinding: CandidateBinding) -> List[Node]: 100 def getOperandNodes(self, existingBinding: CandidateBinding) -> List[Node]:
102 return [existingBinding.applyTerm(self.stmt[0])] 101 return [existingBinding.applyTerm(self.chunk.primary[0])]
103 102
104 103
105 class SubjectObjectFunction(Function): 104 class SubjectObjectFunction(Function):
106 """a filter function that depends on the subject and object terms""" 105 """a filter function that depends on the subject and object terms"""
107 106
108 def getOperandNodes(self, existingBinding: CandidateBinding) -> List[Node]: 107 def getOperandNodes(self, existingBinding: CandidateBinding) -> List[Node]:
109 return [existingBinding.applyTerm(self.stmt[0]), existingBinding.applyTerm(self.stmt[2])] 108 return [existingBinding.applyTerm(self.chunk.primary[0]), existingBinding.applyTerm(self.chunk.primary[2])]
110 109
111 110
112 class ListFunction(Function): 111 class ListFunction(Function):
113 """function that takes an rdf list as input""" 112 """function that takes an rdf list as input"""
114 113
115 def usedStatements(self) -> Set[Triple]: 114 def usedStatements(self) -> Set[Triple]:
116 _, used = parseList(self.ruleGraph, self.stmt[0]) 115 _, used = parseList(self.ruleGraph, self.chunk.primary[0])
117 return used 116 return used
118 117
119 def getOperandNodes(self, existingBinding: CandidateBinding) -> List[Node]: 118 def getOperandNodes(self, existingBinding: CandidateBinding) -> List[Node]:
120 operands, _ = parseList(self.ruleGraph, self.stmt[0]) 119 operands, _ = parseList(self.ruleGraph, self.chunk.primary[0])
121 return [existingBinding.applyTerm(x) for x in operands] 120 return [existingBinding.applyTerm(x) for x in operands]
122 121
123 122
124 @register 123 @register
125 class Gt(SubjectObjectFunction): 124 class Gt(SubjectObjectFunction):
147 146
148 def bind(self, existingBinding: CandidateBinding) -> Optional[CandidateBinding]: 147 def bind(self, existingBinding: CandidateBinding) -> Optional[CandidateBinding]:
149 f = Literal(sum(self.getNumericOperands(existingBinding))) 148 f = Literal(sum(self.getNumericOperands(existingBinding)))
150 return self.valueInObjectTerm(f) 149 return self.valueInObjectTerm(f)
151 150
151
152 ### registration is done 152 ### registration is done
153 153
154 _byPred: Dict[URIRef, Type[Function]] = dict((cls.pred, cls) for cls in registeredFunctionTypes) 154 _byPred: Dict[URIRef, Type[Function]] = dict((cls.pred, cls) for cls in registeredFunctionTypes)
155
156
155 def functionsFor(pred: URIRef) -> Iterator[Type[Function]]: 157 def functionsFor(pred: URIRef) -> Iterator[Type[Function]]:
156 try: 158 try:
157 yield _byPred[pred] 159 yield _byPred[pred]
158 except KeyError: 160 except KeyError:
159 return 161 return
160 162
161 163
162 def lhsStmtsUsedByFuncs(graph: Graph) -> Set[Triple]: 164 # def lhsStmtsUsedByFuncs(graph: ChunkedGraph) -> Set[Chunk]:
163 usedByFuncs: Set[Triple] = set() # don't worry about matching these 165 # usedByFuncs: Set[Triple] = set() # don't worry about matching these
164 for s in graph: 166 # for s in graph:
165 for cls in functionsFor(pred=s[1]): 167 # for cls in functionsFor(pred=s[1]):
166 usedByFuncs.update(cls(s, graph).usedStatements()) 168 # usedByFuncs.update(cls(s, graph).usedStatements())
167 return usedByFuncs 169 # return usedByFuncs
168 170
169 171
170 def rulePredicates() -> Set[URIRef]: 172 def rulePredicates() -> Set[URIRef]:
171 return set(c.pred for c in registeredFunctionTypes) 173 return set(c.pred for c in registeredFunctionTypes)