fr.boreal.model.ruleCompilation.HierarchicalRuleCompilation Maven / Gradle / Ivy
The newest version!
package fr.boreal.model.ruleCompilation;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
import org.apache.commons.lang3.tuple.Pair;
import fr.boreal.model.kb.api.RuleBase;
import fr.boreal.model.logicalElements.api.Atom;
import fr.boreal.model.logicalElements.api.FunctionalTerm;
import fr.boreal.model.logicalElements.api.Predicate;
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.AtomImpl;
import fr.boreal.model.logicalElements.impl.SubstitutionImpl;
import fr.boreal.model.partition.TermPartition;
import fr.boreal.model.partition.TermPartitionFactory;
import fr.boreal.model.rule.api.FORule;
import fr.boreal.model.ruleCompilation.api.RuleCompilationResult;
import fr.boreal.model.ruleCompilation.api.RuleCompilation;
/**
* Version as implemented by Melanie in graal
*
* Compilation for : p(X, Y, ...) -> q(X, Y, ...)
* - atomic rules (head and body)
* - no existential variables
* - no constants
* - body and head atom have the same arity
* - body and head atom have exactly the same variables at the same position
*
* @author Florent Tornil
*/
public class HierarchicalRuleCompilation implements RuleCompilation {
private final Map> order;
/**
* Create a new empty compilation
*/
public HierarchicalRuleCompilation() {
this.order = new HashMap<>();
}
@Override
public void compile(RuleBase rb) {
List compilable = this.extractCompilable(rb);
this.computeOrder(compilable);
}
/**
*
* Creates a record with the partition of the compiled rules.
*
*/
@Override
public RuleCompilationResult compileAndGet(RuleBase rb) {
List snapshot_original_rulebase = new ArrayList<>(rb.getRules());
List compilable_rules = this.extractCompilable(rb);
List non_compilable_rules = new ArrayList<>(snapshot_original_rulebase);
non_compilable_rules.removeAll(compilable_rules);
this.computeOrder(compilable_rules);
return new RuleCompilationResult(this,compilable_rules, compilable_rules, non_compilable_rules);
}
@Override
public boolean isMoreSpecificThan(Atom a, Atom b) {
if(this.isCompatible(a.getPredicate(), b.getPredicate())) {
for (int i = 0; i < a.getTerms().length; i++) {
if (!a.getTerm(i).equals(b.getTerm(i))) {
return false;
}
}
return true;
} else {
return false;
}
}
@Override
public Set> unfold(Atom a) {
Set> res = new HashSet<>();
res.add(Pair.of(a, new SubstitutionImpl()));
for (Predicate p : this.getCompatiblePredicates(a.getPredicate())) {
Atom u = new AtomImpl(p, a.getTerms());
res.add(Pair.of(u, new SubstitutionImpl()));
}
return res;
}
@Override
public boolean isCompatible(Predicate p, Predicate q) {
return p.equals(q) || this.order.getOrDefault(p, new HashSet<>()).contains(q);
}
@Override
public Set getCompatiblePredicates(Predicate p) {
Set res = new HashSet<>();
res.add(p);
res.addAll(this.order.getOrDefault(p, new HashSet<>()));
return res;
}
@Override
public Set getHomomorphisms(Atom a, Atom b, Substitution s) {
Set res = new HashSet<>();
if(this.isCompatible(a.getPredicate(), b.getPredicate())) {
Substitution homomorphism = new SubstitutionImpl();
Iterator fatherTermsIt = List.of(a.getTerms()).iterator();
Iterator sonTermsIt = List.of(b.getTerms()).iterator();
while(fatherTermsIt.hasNext() && sonTermsIt.hasNext()) {
Term fatherTerm = fatherTermsIt.next();
Term sonTerm = sonTermsIt.next();
if(fatherTerm.isFrozen(s)) {
if(!s.createImageOf(fatherTerm).equals(sonTerm)) {
return res;
}
} else if (fatherTerm.isFunctionalTerm()) {
if(sonTerm.isFunctionalTerm()) {
Substitution innerRes = ((FunctionalTerm)fatherTerm).homomorphism((FunctionalTerm)sonTerm, s);
if(innerRes == null) {
return res;
} else {
homomorphism = homomorphism.merged(innerRes).orElse(null);
if(homomorphism == null) {
return res;
}
}
} else {
return res;
}
} else if (!homomorphism.keys().contains(fatherTerm)) {
homomorphism.add((Variable) fatherTerm, sonTerm);
} else if (!homomorphism.createImageOf(fatherTerm).equals(sonTerm)) {
return res;
}
}
res.add(homomorphism);
}
return res;
}
@Override
public Set getUnifications(Atom a, Atom b) {
Set res = new HashSet<>();
if (this.isCompatible(b.getPredicate(), a.getPredicate())) {
res.add(TermPartitionFactory.instance().getByPositionPartition(List.of(a, b)));
}
return res;
}
private List extractCompilable(RuleBase rb) {
Iterator ruleSet = rb.getRules().iterator();
List compilable = new ArrayList<>();
Set toRemove = new HashSet<>();
while (ruleSet.hasNext()) {
FORule r = ruleSet.next();
if (this.isCompilable(r)) {
compilable.add(r);
toRemove.add(r);
}
}
for(FORule r : toRemove) {
rb.remove(r);
}
return compilable;
}
private boolean isCompilable(FORule r) {
Collection bodyAtoms = r.getBody().asAtomSet();
if(bodyAtoms.size() != 1 ) {
return false;
}
Collection headAtoms = r.getBody().asAtomSet();
if(headAtoms.size() != 1 ) {
return false;
}
Term[] body = r.getBody().asAtomSet().iterator().next().getTerms();
Term[] head = r.getHead().asAtomSet().iterator().next().getTerms();
if(body.length != head.length) {
return false;
}
// Check if each variable is different
Set variables = new HashSet<>();
for(Term t : body) {
if(!variables.add(t)) {
return false;
}
}
for(int i = 0; i < body.length; i++) {
// Check for constants and same variables (include existential check)
if(!body[i].isVariable() || !body[i].equals(head[i])) {
return false;
}
}
return true;
}
private void computeOrder(List compilable) {
for(FORule rule : compilable) {
Predicate body = rule.getBody().asAtomSet().iterator().next().getPredicate();
Predicate head = rule.getHead().asAtomSet().iterator().next().getPredicate();
Set hierarchie = this.order.getOrDefault(head, new HashSet<>());
hierarchie.add(body);
this.order.put(head, hierarchie);
this.updateTransitiveClosure(body, head);
}
}
private void updateTransitiveClosure(Predicate body, Predicate head) {
// Added rule body -> head
// Add transitivity for X -> body -> head
// As X -> head
Set hierarchieBody = this.order.getOrDefault(body, new HashSet<>());
Set hierarchieHead = this.order.get(head);
hierarchieHead.addAll(hierarchieBody);
this.order.put(head, hierarchieHead);
// Added rule body -> head
// Add transitivity X -> head -> Y
// As X -> Y
for(Entry> entry : this.order.entrySet()) {
if(entry.getValue().contains(head)) {
Set newHierarchieX = entry.getValue();
newHierarchieX.addAll(hierarchieHead);
this.order.put(entry.getKey(), newHierarchieX);
}
}
}
}