def __init__(self, handler, rule_factory_cb=None): self._handler = handler self._knowledge_base = ruly.KnowledgeBase(*handler.rules) self._factory_cb = rule_factory_cb all_outputs = set(itertools.chain(*handler.dependencies.keys())) all_inputs = set(itertools.chain(*handler.dependencies.values())) self._inputs = all_inputs - all_outputs
def decide(self, inputs, decision): """Attempts to solve for decision based on given inputs. May create new rules if the factory creates them. Args: inputs (Dict[str, Any]): name-value pairs of all inputs decision (str): name of the decision that should be resolved Returns: Any: calculated decision Raises: ruly_dmn.HitPolicyViolation: raised if hit policy violation is detected""" rules = list(self._knowledge_base.rules) if self._factory_cb is None: rule_factory = _ConsoleRuleFactory(self._handler) else: rule_factory = self._factory_cb(self._handler) def post_eval_cb(state, output_name, fired_rules): fired_rules = _resolve_hit_policy( fired_rules, self._handler.hit_policies[output_name]) new_rule = rule_factory.create_rule(state, fired_rules, output_name) if new_rule is not None and new_rule not in rules: if len(fired_rules) == 0: rules.append(new_rule) else: rules.insert(rules.index(fired_rules[0]), new_rule) raise _CancelEvaluationException() elif len(fired_rules) > 0: state = dict(state, **fired_rules[0].consequent) return state state = None rule_count = len(rules) rules_changed = False while state is None: try: state = ruly.backward_chain(self._knowledge_base, decision, post_eval_cb=post_eval_cb, **inputs) except _CancelEvaluationException: if len(rules) == rule_count: break else: rules_changed = True self._knowledge_base = ruly.KnowledgeBase(*rules) if rules_changed: self._handler.update(self._knowledge_base) return state[decision]
def test_backward_chain(): kb = ruly.KnowledgeBase( 'IF sound="croak" AND behavior="eats flies" THEN animal="frog"', 'IF sound="chirp" AND behavior="sings" THEN animal="canary"', 'IF animal="frog" THEN color="green"', 'IF animal="canary" THEN color="yellow"') state = ruly.backward_chain(kb, 'color', sound='croak', behavior='eats flies') assert state['color'] == 'green' state = ruly.backward_chain(kb, 'color', sound='chirp', behavior='sings') assert state['color'] == 'yellow'
def test_numeric(): kb = ruly.KnowledgeBase( 'IF weight>=100 THEN animal="elephant"', 'IF weight>=50 AND weight<100 THEN animal="horse"', 'IF weight>=25 AND weight<50 THEN animal="dog"', 'IF weight>0 AND weight<25 THEN animal="mouse"') state = ruly.backward_chain(kb, 'animal', weight=145) assert state['animal'] == 'elephant' state = ruly.backward_chain(kb, 'animal', weight=59) assert state['animal'] == 'horse' state = ruly.backward_chain(kb, 'animal', weight=37) assert state['animal'] == 'dog' state = ruly.backward_chain(kb, 'animal', weight=12) assert state['animal'] == 'mouse'
def test_bc_post_eval(): kb = ruly.KnowledgeBase( 'IF sound="croak" AND behavior="eats flies" THEN animal="frog"', 'IF sound="chirp" THEN animal="bird"', 'IF sound="chirp" AND behavior="sings" THEN animal="canary"', 'IF animal="frog" THEN color="green"', 'IF animal="canary" THEN color="yellow"', 'IF animal="bird" THEN color="n/a"') def post_eval_cb(state, output_name, fired_rules): if len(fired_rules) == 0: return dict(state, **{output_name: 'really n/a'}) elif len(fired_rules) == 1: selected_rule = fired_rules[0] else: selected_rule = max( fired_rules, key=lambda r: len(ruly.get_rule_depending_variables(r))) new_state = dict(state) new_state.update(selected_rule.consequent) return new_state state = ruly.backward_chain(kb, 'color', post_eval_cb=post_eval_cb, sound='chirp', behavior='sings') assert state['color'] == 'yellow' state = ruly.backward_chain(kb, 'color', post_eval_cb=post_eval_cb, sound='chirp') assert state['color'] == 'n/a' state = ruly.backward_chain(kb, 'color', post_eval_cb=post_eval_cb, sound='roar') assert state['color'] == 'really n/a'
def test_knowledge_base_parses(): rule_str = 'IF rule_engine="ruly" THEN usability="awesome"' rule = ruly.parse(rule_str) knowledge_base = ruly.KnowledgeBase(rule_str) assert knowledge_base.rules == (rule, )