| from __future__ import annotations
|
| import hashlib,json
|
| from .affordance_engine import mine_rules,RULE_META,GENERIC_TYPES
|
| from .claim_ir import claim_key,summary
|
|
|
| def _hash(x):return hashlib.sha256(json.dumps(x,sort_keys=True,ensure_ascii=False,separators=(',',':')).encode()).hexdigest()
|
|
|
| class IncrementalMiner:
|
| """Type-indexed agenda: only rules touched by changed interfaces are recomputed; downstream claim rules propagate once."""
|
| def __init__(self):
|
| self.type_sig={};self.claim_hash={};self.claims={};self.rule_claims={k:[] for k in RULE_META};self.recompute_count=0;self.rule_evaluations=0
|
| def _type_sigs(self,facts):
|
| grouped={}
|
| for t in GENERIC_TYPES:
|
| rows=[{k:f.get(k) for k in ('id','type','payload','assumptions','domain','provenance')} for f in facts if f.get('type')==t]
|
| if rows:grouped[t]=_hash(sorted(rows,key=lambda x:str(x.get('id'))))
|
| return grouped
|
| def update(self,facts:list[dict])->dict:
|
| sig=self._type_sigs(facts);changed={t for t in set(sig)|set(self.type_sig) if sig.get(t)!=self.type_sig.get(t)}
|
| if not changed:return {'changed':False,'delta':[],'recompute_count':self.recompute_count,'rule_evaluations':self.rule_evaluations}
|
| self.type_sig=sig;self.recompute_count+=1
|
| active={name for name,(ft,_) in RULE_META.items() if ft & changed}
|
|
|
| produced_kinds=set()
|
| for name in list(active):
|
| if name=='relation_extent':produced_kinds.add('relation_extent')
|
| active|={name for name,(_,ck) in RULE_META.items() if ck & produced_kinds}
|
| prior=[c for rows in self.rule_claims.values() for c in rows]
|
| out=mine_rules(facts,prior,active);self.rule_evaluations+=len(active)
|
| for name in active:self.rule_claims[name]=out.get(name,[])
|
|
|
| if 'relation_extent' in active and 'inverse_map' in active:
|
| prior=[c for rows in self.rule_claims.values() for c in rows]
|
| self.rule_claims['inverse_map']=mine_rules(facts,prior,{'inverse_map'}).get('inverse_map',[]);self.rule_evaluations+=1
|
| claims=[c for rows in self.rule_claims.values() for c in rows]
|
| current={};delta=[]
|
| for c in claims:
|
| key=claim_key(c);h=_hash(c);current[key]=c
|
| if self.claim_hash.get(key)!=h:delta.append({'key':key,'claim':c,'ir':summary(c)})
|
| self.claim_hash={k:_hash(v) for k,v in current.items()};self.claims=current
|
| return {'changed':True,'delta':delta,'recompute_count':self.recompute_count,'rule_evaluations':self.rule_evaluations}
|
| def full_results(self):return list(self.claims.values())
|
|
|