def horn1(formal_concept, closure_operator, membership_oracle, equivalence_oracle=None): """Computes DG Basis for a given set of attributes using horn1 algorithm """ hypothesis = set() # NOTE: counter_example is a set while True: # sample `hypothesis` => set([a, e, d => c, b]) counter_example = equivalence_oracle(hypothesis, formal_concept, membership_oracle, closure_operator) # terminating condition if counter_example['value'] is None: break # starting edge case if len(hypothesis) == 0: hypothesis.add( imp.Implication(frozenset(counter_example['value']), frozenset(formal_concept.context.attributes))) continue for implication in hypothesis.copy(): # if an implication doesn't repect the counter example, # modify it's conclusion (also called strengthening) if not implication.is_respected(counter_example['value']): hypothesis.remove(implication) hypothesis.add( imp.Implication( frozenset(implication.premise), frozenset( implication.conclusion.intersection( counter_example['value'])))) else: # special_implication (A --> B) is the first implication # such that it's premise(A) is not a subset of # counter_example(C) and member(C ∩ A) is false. The latter # condition can also be interpreted as C ∩ A is not a model # of context(K) special_implication = imp.findSpecialImplication( hypothesis, membership_oracle, closure_operator, counter_example['value']) if special_implication: hypothesis.remove(special_implication) hypothesis.add( imp.Implication( frozenset(counter_example['value'].intersection( special_implication.premise)), frozenset( special_implication.conclusion.union( special_implication.premise.difference( counter_example['value']))))) else: hypothesis.add( imp.Implication( counter_example['value'], set(formal_concept.context.attributes))) return hypothesis
def process_modified_concept(p, m, min_mod_impl, mod_concepts, new_preclosed): # p is of the form (extent, intent) for i in min_mod_impl: if i.premise <= p[1]: # p[1] is no longer preclosed break else: # p[1] becomes psuedo-closed impl = imp.Implication(p[1].copy(), p[1].copy()) impl.get_conclusion().add(m) min_mod_impl.append(impl) new_preclosed.append((p[0], impl.premise, impl)) p[1].add(m) mod_concepts.append(p)
def process_stable_concept(p, m, extent, new_stable_impl, new_preclosed, closure): # p is of the form (extent, intent) new_extent = p[0] & extent new_premise = p[1].copy() new_premise.add(m) for i in new_stable_impl: if not i.is_respected(new_premise): break else: new_conclusion = closure(new_premise) if new_conclusion == new_premise: new_preclosed.append((new_extent, new_premise)) else: impl = imp.Implication(new_premise, new_conclusion) new_stable_impl.append(impl) new_preclosed.append((new_extent, impl.premise, impl))
def generalizedComputeDgBasis(attributes, aclose, close=closure_operators.simple_closure, imp_basis=[], cond=lambda x: True): """Computes the Duquenne-Guigues basis using optimized Ganter's algorithm. `aclose` is a closure operator on the set of attributes. """ relative_basis = [] a = close(set(), imp_basis) i = len(attributes) while len(a) < len(attributes): a_closed = set(aclose(a)) if a != a_closed and cond(a): relative_basis.append(imp.Implication(a.copy(), a_closed.copy())) if (a_closed - a) & set(attributes[:i]): a -= set(attributes[i:]) else: if len(a_closed) == len(attributes): return relative_basis a = a_closed i = len(attributes) for j in range(i - 1, -1, -1): m = attributes[j] if m in a: a.remove(m) else: b = close(a | set([m]), relative_basis + imp_basis) if not (b - a) & set(attributes[:j]): a = b i = j break return relative_basis
def pac_basis(formal_concept, closure_operator, membership_oracle, epsilon=0.1, delta=0.1): hypothesis = set() # NOTE: counter_example is a set i = 0 # number of queries spec = 0 weak = 0 no_resp = 0 t_spec = 0 t_weak = 0 t_no_resp = 0 while True: counter_example = oracle.approx_equivalent(set(hypothesis), membership_oracle, formal_concept, closure_operator, i, { 'no_resp': no_resp, 'spec': spec, 'weak': weak }, epsilon, delta) if counter_example['value'] is None: # TODO: Remove this print('# equivalence queries: {0}, # disrespect trigger: {3},\ # special trigger: {1}, # weak triggers: {2}'.format(i, t_spec, t_weak, t_no_resp)) break if len(hypothesis) == 0: hypothesis.add( imp.Implication(counter_example['value'], set(formal_concept.context.attributes))) continue i += 1 for implication in hypothesis.copy(): # if an implication doesn't repect the counter example, # modify it's conclusion (also called strengthening) if not implication.is_respected(counter_example['value']): hypothesis.remove(implication) hypothesis.add( imp.Implication( implication.premise, implication.conclusion.intersection( counter_example['value']))) no_resp += 1 t_no_resp += 1 weak = 0 spec = 0 else: # special_implication (A --> B) is the first implication # such that it's premise(A) is not a subset of # counter_example(C) and member(C ∩ A) is false. The latter # condition can also be interpreted as C ∩ A is not a model # of context(K) special_implication = imp.findSpecialImplication( hypothesis, membership_oracle, closure_operator, counter_example['value']) if special_implication: hypothesis.remove(special_implication) hypothesis.add( imp.Implication( counter_example['value'].intersection( special_implication.premise), special_implication.conclusion.union( special_implication.premise.difference( counter_example['value'])))) spec += 1 t_spec += 1 no_resp = 0 weak = 0 else: hypothesis.add( imp.Implication( counter_example['value'], set(formal_concept.context.attributes))) weak += 1 t_weak += 1 no_resp = 0 spec = 0 return hypothesis