def new_from_body( self, body: Body, arity: int = None, argument_types: Sequence[Type] = None, use_as_head_predicate: Predicate = None, ) -> Sequence[Atom]: """ Constructs all possible head atoms given a body of a clause If use_as_head_predicate is provided, it uses that. Then, it check is the FillerPredicate is instantiated with a fixed arity Then, it check if the arity argument is provided """ if use_as_head_predicate: return self._from_body_fixed_arity( body, arity=use_as_head_predicate.get_arity(), arg_types=use_as_head_predicate.get_arg_types(), use_as_head_predicate=use_as_head_predicate) elif self._arity is not None: # a specific arity is provided return self._from_body_fixed_arity(body, self._arity, argument_types) else: # min -max arity is provided if arity is not None: # specific arity is requests return self._from_body_fixed_arity(body, arity) else: heads = [] for i in range(self._min_arity, self._max_arity + 1): heads += self._from_body_fixed_arity(body, i) return heads
def _plain_extend_clause( clause: typing.Union[Clause, Body], predicate: Predicate, connected_clause: bool = True ) -> typing.Sequence[typing.Union[Clause, Body]]: """ Extends the clause with the predicate in every possible way (no bias) Arguments: clause: a clause to be extended predicate: a predicate to add to the clause """ if isinstance(clause, Body) and len(clause) == 0: head_variables = [chr(x) for x in range(ord("A"), ord("Z")) ][:predicate.get_arity()] possible_heads = [ Body(predicate(*list(x))) for x in combinations_with_replacement( head_variables, predicate.get_arity()) ] return possible_heads clause_variables: typing.Sequence[Variable] = clause.get_variables() used_variables = {x for x in clause_variables} pred_argument_types: typing.Sequence[Type] = predicate.get_arg_types() argument_matches = {} new_variables = set() # create new variable for each argument of a predicate for arg_ind in range(len(pred_argument_types)): new_var = new_variable(used_variables, pred_argument_types[arg_ind]) argument_matches[arg_ind] = [new_var] used_variables.add(new_var) new_variables.add(new_var) # check for potential match with other variables for clv_ind in range(len(clause_variables)): for arg_ind in range(len(pred_argument_types)): if clause_variables[clv_ind].get_type( ) == pred_argument_types[arg_ind]: argument_matches[arg_ind].append(clause_variables[clv_ind]) # do cross product of matches base_sets = [argument_matches[x] for x in range(len(pred_argument_types))] candidates: typing.List[typing.Union[Clause, Body]] = [] for arg_combo in product(*base_sets): new_clause = None if connected_clause and not all( [True if x in new_variables else False for x in arg_combo]): # check that the new literal is not disconnected from the rest of the clause new_clause = clause + predicate(*list(arg_combo)) elif not connected_clause: new_clause = clause + predicate(*list(arg_combo)) if new_clause is not None: candidates.append(new_clause) return candidates
def _plain_extend_negation_clause( clause: typing.Union[Clause, Body], predicate: Predicate ) -> typing.Sequence[typing.Union[Clause, Body]]: """ Extends a clause with the negation of a predicate (no new variables allowed) """ if isinstance(clause, Body): suitable_vars = clause.get_variables() else: suitable_vars = clause.get_body_variables() pred_argument_types: typing.Sequence[Type] = predicate.get_arg_types() argument_matches = {} # check for potential match with other variables for clv_ind in range(len(suitable_vars)): for arg_ind in range(len(pred_argument_types)): if suitable_vars[clv_ind].get_type() == pred_argument_types[arg_ind]: if arg_ind not in argument_matches: argument_matches[arg_ind] = [] argument_matches[arg_ind].append(suitable_vars[clv_ind]) base_sets = [argument_matches[x] for x in range(len(pred_argument_types))] candidates: typing.List[typing.Union[Clause, Body]] = [] for arg_combo in product(*base_sets): new_clause = clause + Not(predicate(*list(arg_combo))) candidates.append(new_clause) return candidates
def _from_body_fixed_arity( self, body: Body, arity: int = None, arg_types: Sequence[Type] = None, use_as_head_predicate: Predicate = None, ) -> Sequence[Atom]: """ Creates a head atom given the body of the clause :param body: :param arity: (optional) desired arity if specified with min/max when constructing the FillerPredicate :param arg_types: (optional) argument types to use :return: """ assert bool(arity) != bool(arg_types) vars = body.get_variables() if use_as_head_predicate and arg_types is None: arg_types = use_as_head_predicate.get_arg_types() if arg_types is None: base = [vars] * arity else: matches = {} for t_ind in range(len(arg_types)): matches[t_ind] = [] for v_ind in range(len(vars)): if vars[v_ind].get_type() == arg_types[t_ind]: matches[t_ind].append(vars[v_ind]) base = [matches[x] for x in range(arity)] heads = [] for comb in product(*base): self._instance_counter += 1 if use_as_head_predicate is not None: pred = use_as_head_predicate elif arg_types is None: pred = c_pred(f"{self._prefix_name}_{self._instance_counter}", arity) else: pred = c_pred( f"{self._prefix_name}_{self._instance_counter}", len(arg_types), arg_types, ) heads.append(Atom(pred, list(comb))) return heads
def is_created_by(self, predicate: Predicate) -> bool: """ checks if a given predicate is created by the FillerPredicate it does so by checking if the name of the predicate is in [name]_[number] format and the name is equal to self._prefix_name and number is <= self._instance_counter """ sp = predicate.get_name().split("_") if len(sp) != 2: return False else: if sp[0] == self._prefix_name and int( sp[1]) <= self._instance_counter: return True else: return False