Mercurial > code > home > repos > homeauto
annotate 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 |
rev | line source |
---|---|
1605 | 1 import logging |
2 from decimal import Decimal | |
1651
20474ad4968e
WIP - functions are broken as i move most layers to work in Chunks not Triples
drewp@bigasterisk.com
parents:
1648
diff
changeset
|
3 from typing import (Dict, Iterator, List, Optional, Set, Tuple, Type, Union, cast) |
1605 | 4 |
5 from prometheus_client import Summary | |
1636 | 6 from rdflib import RDF, Literal, Namespace, URIRef |
1651
20474ad4968e
WIP - functions are broken as i move most layers to work in Chunks not Triples
drewp@bigasterisk.com
parents:
1648
diff
changeset
|
7 from rdflib.term import Node, Variable |
1605 | 8 |
1651
20474ad4968e
WIP - functions are broken as i move most layers to work in Chunks not Triples
drewp@bigasterisk.com
parents:
1648
diff
changeset
|
9 from candidate_binding import CandidateBinding |
1637 | 10 from inference_types import BindableTerm, Triple |
1651
20474ad4968e
WIP - functions are broken as i move most layers to work in Chunks not Triples
drewp@bigasterisk.com
parents:
1648
diff
changeset
|
11 from stmt_chunk import Chunk, ChunkedGraph |
1607
b21885181e35
more modules, types. Maybe less repeated computation on BoundLhs
drewp@bigasterisk.com
parents:
1605
diff
changeset
|
12 |
1605 | 13 log = logging.getLogger('infer') |
14 | |
15 INDENT = ' ' | |
16 | |
17 ROOM = Namespace("http://projects.bigasterisk.com/room/") | |
18 LOG = Namespace('http://www.w3.org/2000/10/swap/log#') | |
19 MATH = Namespace('http://www.w3.org/2000/10/swap/math#') | |
20 | |
21 | |
22 def numericNode(n: Node): | |
23 if not isinstance(n, Literal): | |
24 raise TypeError(f'expected Literal, got {n=}') | |
25 val = n.toPython() | |
26 if not isinstance(val, (int, float, Decimal)): | |
27 raise TypeError(f'expected number, got {val=}') | |
28 return val | |
29 | |
30 | |
1651
20474ad4968e
WIP - functions are broken as i move most layers to work in Chunks not Triples
drewp@bigasterisk.com
parents:
1648
diff
changeset
|
31 def parseList(graph: ChunkedGraph, subj: Node) -> Tuple[List[Node], Set[Triple]]: |
1605 | 32 """"Do like Collection(g, subj) but also return all the |
33 triples that are involved in the list""" | |
34 out = [] | |
35 used = set() | |
36 cur = subj | |
37 while cur != RDF.nil: | |
1634
ba59cfc3c747
hack math:sum in there. Test suite is passing except some slow performers
drewp@bigasterisk.com
parents:
1607
diff
changeset
|
38 elem = graph.value(cur, RDF.first) |
ba59cfc3c747
hack math:sum in there. Test suite is passing except some slow performers
drewp@bigasterisk.com
parents:
1607
diff
changeset
|
39 if elem is None: |
ba59cfc3c747
hack math:sum in there. Test suite is passing except some slow performers
drewp@bigasterisk.com
parents:
1607
diff
changeset
|
40 raise ValueError('bad list') |
ba59cfc3c747
hack math:sum in there. Test suite is passing except some slow performers
drewp@bigasterisk.com
parents:
1607
diff
changeset
|
41 out.append(elem) |
1605 | 42 used.add((cur, RDF.first, out[-1])) |
43 | |
44 next = graph.value(cur, RDF.rest) | |
1634
ba59cfc3c747
hack math:sum in there. Test suite is passing except some slow performers
drewp@bigasterisk.com
parents:
1607
diff
changeset
|
45 if next is None: |
ba59cfc3c747
hack math:sum in there. Test suite is passing except some slow performers
drewp@bigasterisk.com
parents:
1607
diff
changeset
|
46 raise ValueError('bad list') |
1605 | 47 used.add((cur, RDF.rest, next)) |
48 | |
49 cur = next | |
50 return out, used | |
1637 | 51 |
52 | |
53 registeredFunctionTypes: List[Type['Function']] = [] | |
54 | |
55 | |
56 def register(cls: Type['Function']): | |
57 registeredFunctionTypes.append(cls) | |
58 return cls | |
59 | |
60 | |
61 class Function: | |
62 """any rule stmt that runs a function (not just a statement match)""" | |
1640 | 63 pred: URIRef |
1637 | 64 |
1651
20474ad4968e
WIP - functions are broken as i move most layers to work in Chunks not Triples
drewp@bigasterisk.com
parents:
1648
diff
changeset
|
65 def __init__(self, chunk: Chunk, ruleGraph: ChunkedGraph): |
20474ad4968e
WIP - functions are broken as i move most layers to work in Chunks not Triples
drewp@bigasterisk.com
parents:
1648
diff
changeset
|
66 self.chunk = chunk |
20474ad4968e
WIP - functions are broken as i move most layers to work in Chunks not Triples
drewp@bigasterisk.com
parents:
1648
diff
changeset
|
67 if chunk.predicate != self.pred: |
1637 | 68 raise TypeError |
69 self.ruleGraph = ruleGraph | |
70 | |
71 def getOperandNodes(self, existingBinding: CandidateBinding) -> List[Node]: | |
72 raise NotImplementedError | |
73 | |
74 def getNumericOperands(self, existingBinding: CandidateBinding) -> List[Union[int, float, Decimal]]: | |
75 out = [] | |
76 for op in self.getOperandNodes(existingBinding): | |
77 out.append(numericNode(op)) | |
78 | |
79 return out | |
80 | |
81 def bind(self, existingBinding: CandidateBinding) -> Optional[CandidateBinding]: | |
82 """either any new bindings this function makes (could be 0), or None if it doesn't match""" | |
83 raise NotImplementedError | |
84 | |
85 def valueInObjectTerm(self, value: Node) -> Optional[CandidateBinding]: | |
1651
20474ad4968e
WIP - functions are broken as i move most layers to work in Chunks not Triples
drewp@bigasterisk.com
parents:
1648
diff
changeset
|
86 objVar = self.chunk.primary[2] |
1637 | 87 if not isinstance(objVar, Variable): |
88 raise TypeError(f'expected Variable, got {objVar!r}') | |
89 return CandidateBinding({cast(BindableTerm, objVar): value}) | |
90 | |
1648 | 91 def usedStatements(self) -> Set[Triple]: |
92 '''stmts in self.graph (not including self.stmt, oddly) that are part of | |
93 this function setup and aren't to be matched literally''' | |
94 return set() | |
1651
20474ad4968e
WIP - functions are broken as i move most layers to work in Chunks not Triples
drewp@bigasterisk.com
parents:
1648
diff
changeset
|
95 |
1637 | 96 |
97 class SubjectFunction(Function): | |
98 """function that depends only on the subject term""" | |
99 | |
100 def getOperandNodes(self, existingBinding: CandidateBinding) -> List[Node]: | |
1651
20474ad4968e
WIP - functions are broken as i move most layers to work in Chunks not Triples
drewp@bigasterisk.com
parents:
1648
diff
changeset
|
101 return [existingBinding.applyTerm(self.chunk.primary[0])] |
1637 | 102 |
103 | |
104 class SubjectObjectFunction(Function): | |
105 """a filter function that depends on the subject and object terms""" | |
106 | |
107 def getOperandNodes(self, existingBinding: CandidateBinding) -> List[Node]: | |
1651
20474ad4968e
WIP - functions are broken as i move most layers to work in Chunks not Triples
drewp@bigasterisk.com
parents:
1648
diff
changeset
|
108 return [existingBinding.applyTerm(self.chunk.primary[0]), existingBinding.applyTerm(self.chunk.primary[2])] |
1637 | 109 |
110 | |
111 class ListFunction(Function): | |
112 """function that takes an rdf list as input""" | |
113 | |
114 def usedStatements(self) -> Set[Triple]: | |
1651
20474ad4968e
WIP - functions are broken as i move most layers to work in Chunks not Triples
drewp@bigasterisk.com
parents:
1648
diff
changeset
|
115 _, used = parseList(self.ruleGraph, self.chunk.primary[0]) |
1637 | 116 return used |
117 | |
118 def getOperandNodes(self, existingBinding: CandidateBinding) -> List[Node]: | |
1651
20474ad4968e
WIP - functions are broken as i move most layers to work in Chunks not Triples
drewp@bigasterisk.com
parents:
1648
diff
changeset
|
119 operands, _ = parseList(self.ruleGraph, self.chunk.primary[0]) |
1637 | 120 return [existingBinding.applyTerm(x) for x in operands] |
121 | |
122 | |
123 @register | |
124 class Gt(SubjectObjectFunction): | |
125 pred = MATH['greaterThan'] | |
126 | |
127 def bind(self, existingBinding: CandidateBinding) -> Optional[CandidateBinding]: | |
128 [x, y] = self.getNumericOperands(existingBinding) | |
129 if x > y: | |
130 return CandidateBinding({}) # no new values; just allow matching to keep going | |
131 | |
132 | |
133 @register | |
134 class AsFarenheit(SubjectFunction): | |
135 pred = ROOM['asFarenheit'] | |
136 | |
137 def bind(self, existingBinding: CandidateBinding) -> Optional[CandidateBinding]: | |
138 [x] = self.getNumericOperands(existingBinding) | |
139 f = cast(Literal, Literal(Decimal(x) * 9 / 5 + 32)) | |
140 return self.valueInObjectTerm(f) | |
141 | |
142 | |
143 @register | |
144 class Sum(ListFunction): | |
145 pred = MATH['sum'] | |
146 | |
147 def bind(self, existingBinding: CandidateBinding) -> Optional[CandidateBinding]: | |
148 f = Literal(sum(self.getNumericOperands(existingBinding))) | |
149 return self.valueInObjectTerm(f) | |
150 | |
1651
20474ad4968e
WIP - functions are broken as i move most layers to work in Chunks not Triples
drewp@bigasterisk.com
parents:
1648
diff
changeset
|
151 |
1648 | 152 ### registration is done |
1637 | 153 |
1640 | 154 _byPred: Dict[URIRef, Type[Function]] = dict((cls.pred, cls) for cls in registeredFunctionTypes) |
1651
20474ad4968e
WIP - functions are broken as i move most layers to work in Chunks not Triples
drewp@bigasterisk.com
parents:
1648
diff
changeset
|
155 |
20474ad4968e
WIP - functions are broken as i move most layers to work in Chunks not Triples
drewp@bigasterisk.com
parents:
1648
diff
changeset
|
156 |
1640 | 157 def functionsFor(pred: URIRef) -> Iterator[Type[Function]]: |
158 try: | |
159 yield _byPred[pred] | |
160 except KeyError: | |
161 return | |
162 | |
1637 | 163 |
1651
20474ad4968e
WIP - functions are broken as i move most layers to work in Chunks not Triples
drewp@bigasterisk.com
parents:
1648
diff
changeset
|
164 # def lhsStmtsUsedByFuncs(graph: ChunkedGraph) -> Set[Chunk]: |
20474ad4968e
WIP - functions are broken as i move most layers to work in Chunks not Triples
drewp@bigasterisk.com
parents:
1648
diff
changeset
|
165 # usedByFuncs: Set[Triple] = set() # don't worry about matching these |
20474ad4968e
WIP - functions are broken as i move most layers to work in Chunks not Triples
drewp@bigasterisk.com
parents:
1648
diff
changeset
|
166 # for s in graph: |
20474ad4968e
WIP - functions are broken as i move most layers to work in Chunks not Triples
drewp@bigasterisk.com
parents:
1648
diff
changeset
|
167 # for cls in functionsFor(pred=s[1]): |
20474ad4968e
WIP - functions are broken as i move most layers to work in Chunks not Triples
drewp@bigasterisk.com
parents:
1648
diff
changeset
|
168 # usedByFuncs.update(cls(s, graph).usedStatements()) |
20474ad4968e
WIP - functions are broken as i move most layers to work in Chunks not Triples
drewp@bigasterisk.com
parents:
1648
diff
changeset
|
169 # return usedByFuncs |
1640 | 170 |
171 | |
172 def rulePredicates() -> Set[URIRef]: | |
1651
20474ad4968e
WIP - functions are broken as i move most layers to work in Chunks not Triples
drewp@bigasterisk.com
parents:
1648
diff
changeset
|
173 return set(c.pred for c in registeredFunctionTypes) |