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()