class AssertAggregate(Assert): def __init__(self, **kwargs): self.map_reduce_values = {} for key, value in kwargs.items(): if isinstance(value, MapReduceFunc): del kwargs[key] subkeys = [] for n, map_value in enumerate(value.map_values): subkey = key if n == 0 else '{}_{}'.format(key, n+1) kwargs[subkey] = map_value subkeys.append(subkey) self.map_reduce_values[key] = (value, subkeys) super(AssertAggregate, self).__init__(**kwargs) def prepare_rule(self, rule): self.data.update({key : normalize_expr(value) for key, value in rule.groupby.iteritems()}) self.data[UNIQUE_INDEX_FIELD] = gensym() super(AssertAggregate, self).prepare_rule(rule) self._create_reduce_rule(rule) def _create_reduce_rule(self, map_rule): r1 = map_rule.target[0] r2 = map_rule.target[1] self.reduce_rule = Rule() self.reduce_rule.set_name('_Reduce') for field_name in map_rule.groupby: self.reduce_rule.add_variable(getattr(r1, field_name), getattr(r2, field_name)) self.reduce_rule.add_condition(fact_index(r1) != fact_index(r2)) reduce_kwargs = {} for field_name, (value, subfields) in self.map_reduce_values.iteritems(): v1 = [getattr(r1, field_name) for field_name in subfields] v2 = [getattr(r2, field_name) for field_name in subfields] if len(subfields) == 1: v1 = v1[0] v2 = v2[0] reduced = value._reduce(v1, v2) if len(subfields) == 1: reduced = [reduced] reduce_kwargs.update(dict(zip(subfields, reduced))) self.reduce_rule.add_action(Delete(r2)) self.reduce_rule.add_action(Update(r1, **reduce_kwargs)) self.reduce_rule.set_salience(map_rule.salience) map_rule.add_secondary_rule(self.reduce_rule)
def test_add_condition(self): print(">> Rule.add_condition(self, variable, value)") # Empty rule for i in range(self.__nb_unit_test): var = random.randint(0, self.__var_size) val = random.randint(0, self.__val_size) r = Rule(var, val) var = random.randint(0, self.__var_size) val = random.randint(0, self.__val_size) self.assertFalse(r.has_condition(var)) r.add_condition(var, val) self.assertEqual(r.get_condition(var), val) for i in range(self.__nb_unit_test): r = self.random_rule() if len(r.get_body()) == 0: i -= 1 continue conditions = [] for var, val in r.get_body(): conditions.append(var) var = conditions[0] while var in conditions: var = random.randint(0, self.__var_size) val = random.randint(0, self.__val_size) self.assertFalse(r.has_condition(var)) r.add_condition(var, val) self.assertEqual(r.get_condition(var), val)
def fit_var_val(variable, value, positives, negatives): """ Learn minimal rules that explain positive examples while consistent with negatives examples Args: variable: int variable id value: int variable value id positive: list of (list of int) States of the system where the variable takes this value in the next state negative: list of (list of int) States of the system where the variable does not take this value in the next state """ #eprint("Start learning of var="+str(variable)+", val="+str(value)) remaining = positives.copy() output = [] # exausting covering loop while len(remaining) > 0: #eprint("Remaining positives: "+str(remaining)) #eprint("Negatives: "+str(negatives)) target = remaining[0] #eprint("new target: "+str(target)) R = Rule(variable, value) #eprint(R.to_string()) # 1) Consistency: against negatives examples #--------------------------------------------- for neg in negatives: if R.matches(neg): # Cover a negative example #eprint(R.to_string() + " matches " + str(neg)) for var in range(0, len(target)): if not R.has_condition(var) and neg[var] != target[ var]: # free condition #eprint("adding condition "+str(var)+":"+str(var)+"="+str(target[var])) if target[ var] > -1: # Valid target value (-1 encode all value for partial state) R.add_condition( var, target[var] ) # add value of target positive example break # 2) Minimalize: only necessary conditions #------------------------------------------- reductible = True conditions = R.get_body().copy() for (var, val) in conditions: R.remove_condition(var) # Try remove condition conflict = False for neg in negatives: if R.matches(neg): # Cover a negative example conflict = True R.add_condition(var, val) # Cancel removal break # Add new minimal rule #eprint("New rule: "+R.to_string()) output.append(R) remaining.pop(0) # 3) Clean new covered positives examples #------------------------------------------ i = 0 while i < len(remaining): if R.matches(remaining[i]): #eprint("Covers "+str(remaining[i])) remaining.pop(i) else: i += 1 return output
def random(variables, values, rule_min_size, rule_max_size, delay=1): """ Generate a deterministic complete logic program with a random dynamics. For each variable of the system, each possible state of the system is matched by at least one rule. Args: variables: list of String Labels of the program variables values: list of list of String Domain of values that each variable can take rule_min_size: int minimal number of conditions in each rule rule_max_size: int maximal number of conditions in each rule (can be exceeded for completeness) delay: int maximal delay of the conditions of each rule """ extended_variables = variables.copy() extended_values = values.copy() # Delayed logic program: extend local herbrand base if delay > 1: for d in range(1, delay): extended_variables += [var + "_" + str(d) for var in variables] extended_values += values rules = [] p = LogicProgram(extended_variables, extended_values, []) states = p.states() # aggregated reversed time serie of size delay for s in states: for var in range(len(variables)): matching = False for r in rules: # check if matched if r.get_head_variable() == var and r.matches(s): matching = True break if not matching: # need new rule val = random.randint(0, len(values[var]) - 1) body_size = random.randint(rule_min_size, rule_max_size) new_rule = Rule(var, val, []) # Prevent cross-match for r in rules: if r.get_head_variable() == var and r.cross_matches( new_rule): # Search an available variable # Always exists since no rule matches s yet while True: (cond_var, cond_val) = random.choice(r.get_body()) if not new_rule.has_condition( cond_var) and cond_val != s[cond_var]: new_rule.add_condition( cond_var, s[cond_var]) break # Complete the rule body if needed while (new_rule.size() < body_size): # create body cond_var = random.randint(0, len(s) - 1) cond_val = s[cond_var] if new_rule.has_condition(cond_var): continue new_rule.add_condition(cond_var, cond_val) rules.append(new_rule) return LogicProgram(variables, values, rules)