Mercurial > code > home > repos > homeauto
comparison service/reasoning/inference.py @ 1092:54de5144900d
switch from evtiming to greplin.scales. Optimize rules reader to reuse previous data (400ms -> 0.6ms)
Ignore-this: a655f4c56db51b09b3f14d7f09e354cb
darcs-hash:4ffd7012f404392375434243104eba065ffb8086
author | drewp <drewp@bigasterisk.com> |
---|---|
date | Mon, 09 May 2016 00:32:08 -0700 |
parents | cb7fa2f30df9 |
children | e03696277b32 |
comparison
equal
deleted
inserted
replaced
1091:352ecf3c9aea | 1092:54de5144900d |
---|---|
1 """ | 1 """ |
2 see ./reasoning for usage | 2 see ./reasoning for usage |
3 """ | 3 """ |
4 | 4 |
5 import sys, os | 5 import sys, os, contextlib |
6 try: | 6 try: |
7 from rdflib.Graph import Graph | 7 from rdflib.Graph import Graph |
8 except ImportError: | 8 except ImportError: |
9 from rdflib import Graph | 9 from rdflib import Graph |
10 | 10 |
16 from FuXi.Rete.RuleStore import N3RuleStore | 16 from FuXi.Rete.RuleStore import N3RuleStore |
17 | 17 |
18 from rdflib import plugin, Namespace | 18 from rdflib import plugin, Namespace |
19 from rdflib.store import Store | 19 from rdflib.store import Store |
20 | 20 |
21 sys.path.append('../../../ffg/ffg') | 21 from greplin import scales |
22 import evtiming | 22 STATS = scales.collection('/web', |
23 scales.PmfStat('readRules')) | |
23 | 24 |
24 from escapeoutputstatements import escapeOutputStatements | 25 from escapeoutputstatements import escapeOutputStatements |
25 ROOM = Namespace("http://projects.bigasterisk.com/room/") | 26 ROOM = Namespace("http://projects.bigasterisk.com/room/") |
26 | 27 |
28 def _loadAndEscape(ruleStore, n3, outputPatterns): | |
29 ruleGraph = Graph(ruleStore) | |
30 | |
31 # Can't escapeOutputStatements in the ruleStore since it | |
32 # doesn't support removals. Can't copy plainGraph into | |
33 # ruleGraph since something went wrong with traversing the | |
34 # triples inside quoted graphs, and I lose all the bodies | |
35 # of my rules. This serialize/parse version is very slow (400ms), | |
36 # but it only runs when the file changes. | |
37 plainGraph = Graph() | |
38 plainGraph.parse(StringInputSource(n3), format='n3') # for inference | |
39 escapeOutputStatements(plainGraph, outputPatterns=outputPatterns) | |
40 expandedN3 = plainGraph.serialize(format='n3') | |
41 | |
42 ruleGraph.parse(StringInputSource(expandedN3), format='n3') | |
43 | |
27 _rulesCache = (None, None, None, None) | 44 _rulesCache = (None, None, None, None) |
28 @evtiming.serviceLevel.timed('readRules') | |
29 def readRules(rulesPath, outputPatterns): | 45 def readRules(rulesPath, outputPatterns): |
30 """ | 46 """ |
31 returns (rulesN3, ruleGraph) | 47 returns (rulesN3, ruleStore) |
32 | 48 |
33 This includes escaping certain statements in the output | 49 This includes escaping certain statements in the output |
34 (implied) subgraaphs so they're not confused with input | 50 (implied) subgraaphs so they're not confused with input |
35 statements. | 51 statements. |
36 """ | 52 """ |
37 global _rulesCache | 53 global _rulesCache |
38 mtime = os.path.getmtime(rulesPath) | |
39 key = (rulesPath, mtime) | |
40 if _rulesCache[:2] == key: | |
41 _, _, rulesN3, expandedN3 = _rulesCache | |
42 else: | |
43 rulesN3 = open(rulesPath).read() # for web display | |
44 | 54 |
45 plainGraph = Graph() | 55 with STATS.readRules.time(): |
46 plainGraph.parse(StringInputSource(rulesN3), | 56 mtime = os.path.getmtime(rulesPath) |
47 format='n3') # for inference | 57 key = (rulesPath, mtime) |
48 escapeOutputStatements(plainGraph, outputPatterns=outputPatterns) | 58 if _rulesCache[:2] == key: |
49 expandedN3 = plainGraph.serialize(format='n3') | 59 _, _, rulesN3, ruleStore = _rulesCache |
50 _rulesCache = key + (rulesN3, expandedN3) | 60 else: |
61 rulesN3 = open(rulesPath).read() # for web display | |
51 | 62 |
52 # the rest needs to happen each time since inference is | 63 ruleStore = N3RuleStore() |
53 # consuming the ruleGraph somehow | 64 _loadAndEscape(ruleStore, rulesN3, outputPatterns) |
54 ruleStore = N3RuleStore() | 65 log.debug('%s rules' % len(ruleStore.rules)) |
55 ruleGraph = Graph(ruleStore) | 66 |
56 | 67 _rulesCache = key + (rulesN3, ruleStore) |
57 ruleGraph.parse(StringInputSource(expandedN3), format='n3') | 68 return rulesN3, ruleStore |
58 log.debug('%s rules' % len(ruleStore.rules)) | |
59 return rulesN3, ruleGraph | |
60 | 69 |
61 def infer(graph, rules): | 70 def infer(graph, rules): |
62 """ | 71 """ |
63 returns new graph of inferred statements | 72 returns new graph of inferred statements. Plain rete api seems to |
73 alter rules.formulae and rules.rules, but this function does not | |
74 alter the incoming rules object, so you can cache it. | |
64 """ | 75 """ |
65 # based on fuxi/tools/rdfpipe.py | 76 # based on fuxi/tools/rdfpipe.py |
66 store = plugin.get('IOMemory',Store)() | |
67 store.open('') | |
68 | |
69 target = Graph() | 77 target = Graph() |
70 tokenSet = generateTokenSet(graph) | 78 tokenSet = generateTokenSet(graph) |
71 network = ReteNetwork(rules, inferredTarget=target) | 79 with _dontChangeRulesStore(rules): |
72 network.feedFactsToAdd(tokenSet) | 80 network = ReteNetwork(rules, inferredTarget=target) |
73 | 81 network.feedFactsToAdd(tokenSet) |
74 store.rollback() | 82 |
75 return target | 83 return target |
76 | 84 |
85 @contextlib.contextmanager | |
86 def _dontChangeRulesStore(rules): | |
87 if not hasattr(rules, '_stashOriginalRules'): | |
88 rules._stashOriginalRules = rules.rules[:] | |
89 yield | |
90 for k in rules.formulae.keys(): | |
91 if not k.startswith('_:Formula'): | |
92 del rules.formulae[k] | |
93 rules.rules = rules._stashOriginalRules[:] | |
94 | |
77 import time, logging | 95 import time, logging |
78 log = logging.getLogger() | 96 log = logging.getLogger() |
79 def logTime(func): | 97 def logTime(func): |
80 def inner(*args, **kw): | 98 def inner(*args, **kw): |
81 t1 = time.time() | 99 t1 = time.time() |