fr.boreal.model.partition.TermPartition Maven / Gradle / Ivy
The newest version!
package fr.boreal.model.partition;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Comparator;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Optional;
import java.util.Set;
import java.util.stream.Collectors;
import fr.boreal.model.logicalElements.api.Substitution;
import fr.boreal.model.logicalElements.api.Term;
import fr.boreal.model.logicalElements.api.Variable;
import fr.boreal.model.logicalElements.impl.SubstitutionImpl;
import fr.boreal.model.query.api.FOQuery;
import fr.boreal.model.rule.api.Rule;
/**
* @author Guillaume Pérution-Kihli
* @author Florent Tornil
*
* Partition of terms
* Adds method specific for terms in addition to partition ones
*/
public class TermPartition extends Partition {
/**
* Cache for valid property
*/
Map valid = new HashMap<>();
/**
* Cache for associated substitution property
*/
Map, Substitution> associatedSubstitution = new HashMap<>();
/**
* Comparator used when choosing a representative of a class
* Chooses constants first
* @return the comparator
*/
private static Comparator getComparator() {
return((t1, t2) -> {
if((t1.isFrozen(null)) && (t2.isFrozen(null))) {
return 0;
} else if(t1.isFrozen(null)) {
return -1;
} else if(t2.isFrozen(null)) {
return 1;
}
return 0;
});
}
/**
* Creates a new empty TermPartition
*/
public TermPartition () {
super(getComparator());
}
/**
* Creates a new TermPartition with the given elements, each in its own class
* @param initialElements the initial elements of this partition
*/
public TermPartition (Set initialElements) {
super(initialElements, getComparator());
}
/**
* Creates a new TermPartition with the given classes
* @param partition the initial elements represented as multiple partitions
*/
public TermPartition (Collection> partition) {
super(partition, getComparator());
}
/**
* Creates the TermPartition induced by a substitution
* I.e., foreach variable v mapped to a term t through the substitution,
* v and t are in the same class, and the partition is the finest for
* this property
* @param substitution the substitution to transform into a partition
*/
public TermPartition (Substitution substitution) {
for (Map.Entry mapTo: substitution.toMap().entrySet()) {
this.union(mapTo.getKey(), mapTo.getValue());
}
}
/**
* Creates a new TermPartition, copying the given one
* @param toCopy partition to copy
*/
public TermPartition (Partition toCopy) {
super(toCopy, getComparator());
}
////////////////////
// PUBLIC METHODS //
////////////////////
/**
* @param rule a rule
* @return false iff a class of the receiving partition
* contains two constants,
* or contains two existential variable of R,
* or contains a constant and an existential variable of R,
* or contains an existential variable of R and a frontier variable of R
*/
public synchronized boolean isValid(Rule rule) {
if (!valid.containsKey(rule)) {
boolean result = this.isValid(rule, null);
valid.put(rule, result);
return result;
} else {
return valid.get(rule);
}
}
/**
* Takes into account answer variables of a context query, preventing them to be associated with an existential variable of the rule
* @param rule a rule
* @param context the query in which to test the validity
* @return false iff a class of the receiving partition
* contains two constants,
* or contains two existential variable of R,
* or contains a constant and an existential variable of R,
* or contains an existential variable of R and a frontier variable of R,
* or an existential and an answer variable of the context
*/
public boolean isValid(Rule rule, FOQuery> context) {
for (Collection cl : this.getClasses()) {
boolean hasCst = false, hasExistFromRule= false, hasFr = false, hasAnsVar = false;
for (Term term : cl) {
if (term.isFrozen(null)) {
if (hasCst || hasExistFromRule) {
valid.put(rule, false);
return false;
}
hasCst = true;
}
else if (rule.getExistentials().contains(term)) {
if (hasExistFromRule || hasCst || hasFr || hasAnsVar) {
return false;
}
hasExistFromRule = true;
}
else if (rule.getFrontier().contains(term)) {
if (hasExistFromRule) {
return false;
}
hasFr = true;
} else if(context != null && context.getAnswerVariables().contains(term)) {
if (hasExistFromRule) {
return false;
}
hasAnsVar = true;
}
}
}
return true;
}
/**
* @param sep a set of variables
* @param rule a rule
* @return the subset of sep containing variables that are in the same class as
* an existential variable of rule.
*/
public Set getSeparatingStickyVariables(Set sep, Rule rule) {
Set separatingStickyVariables = new HashSet<>();
for (Collection classs : this.getClasses()) {
if (classs.stream().anyMatch(rule.getExistentials()::contains)) {
classs.stream().filter(sep::contains).map(t -> (Variable)t).forEach(separatingStickyVariables::add);
}
}
return separatingStickyVariables;
}
/**
* Compute the substitution associated with this partition.
* This method computes a substitution by choosing one representative term by
* class.
* The representative is chosen for each class as :
* 1. The constant (if any)
* 2. The answer variable of the context (if any)
* 3. The variables of the context (if any)
* 4. A random term of the class
*
* Return an empty optional if the partition contain two constants in the same class
* @param context a query for context (answer variables)
* @return the substitution associated with this partition in the given context
*/
public synchronized Optional getAssociatedSubstitution(FOQuery> context) {
if (!associatedSubstitution.containsKey(context)) {
Substitution subs = new SubstitutionImpl();
Collection contextAnswerVariables = new ArrayList<>();
Collection contextVariables = new ArrayList<>();
if(context != null) {
contextAnswerVariables = context.getAnswerVariables();
contextVariables = context.getFormula().getVariables();
}
for (Collection classs : getClasses()) {
Term representative = null;
for(Term term : classs) {
// initialize the representative of the class with the context-free representative of the partition
// constant if any then any variable
if(representative == null) {
representative = this.getRepresentative(term);
}
// Only one constant should exist in a given class
if((term.isFrozen(null)) && !representative.equals(term)) {
return Optional.empty();
}
// If the representative is a variable
// Prioritize answer variables of the context
// and then other variables of the context
if(representative.isVariable()
&& ! contextAnswerVariables.contains(representative)
&& contextVariables.contains(term)) {
representative = term;
}
}
for(Term term : classs) {
if(term.isVariable()) {
subs.add((Variable)term, representative);
}
}
}
associatedSubstitution.put(context, subs);
return Optional.of(subs);
} else {
return Optional.ofNullable(associatedSubstitution.get(context));
}
}
/**
*
* @param s a substitution
* @return a new partition that represents this partition on which the given substitution have been applied
*/
public TermPartition createImageWith(Substitution s) {
TermPartition p = new TermPartition();
for(Set elements : this.getClasses()) {
p.addClass(elements.stream().map(s::createImageOf).collect(Collectors.toSet()));
}
return p;
}
@Override
protected void eraseMemoizedValues () {
super.eraseMemoizedValues();
valid = new HashMap<>();
associatedSubstitution = new HashMap<>();
}
}