
fr.boreal.grd.impl.GRDImpl Maven / Gradle / Ivy
Show all versions of integraal-grd Show documentation
package fr.boreal.grd.impl;
import java.io.Serial;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.Set;
import org.jgrapht.Graph;
import org.jgrapht.alg.connectivity.GabowStrongConnectivityInspector;
import org.jgrapht.alg.interfaces.ShortestPathAlgorithm.SingleSourcePaths;
import org.jgrapht.alg.shortestpath.BellmanFordShortestPath;
import org.jgrapht.alg.shortestpath.NegativeCycleDetectedException;
import org.jgrapht.graph.DirectedPseudograph;
import com.google.common.collect.Lists;
import fr.boreal.grd.api.GraphOfFORuleDependencies;
import fr.boreal.grd.impl.dependency_checker.DependencyChecker;
import fr.boreal.grd.impl.dependency_checker.ProductivityChecker;
import fr.boreal.model.formula.api.FOFormula;
import fr.boreal.model.kb.api.RuleBase;
import fr.boreal.model.kb.impl.RuleBaseImpl;
import fr.boreal.model.query.api.FOQuery;
import fr.boreal.model.query.factory.FOQueryFactory;
import fr.boreal.model.rule.api.FORule;
import fr.boreal.model.rule.impl.FORuleImpl;
import fr.boreal.model.ruleCompilation.NoRuleCompilation;
import fr.boreal.model.ruleCompilation.api.RuleCompilation;
import fr.boreal.unifier.QueryUnifier;
import fr.boreal.unifier.QueryUnifierAlgorithm;
import fr.lirmm.boreal.util.Rules;
/**
* GRD implementation using unification
*
* Rules of this GRD can have negation as long as the negation is safe
*/
public class GRDImpl extends DirectedPseudograph implements GraphOfFORuleDependencies {
@Serial
private static final long serialVersionUID = -1382791441537653245L;
/**
* Rulebase represented by this GRD
*/
private final RuleBase rulebase;
/**
* Compilation used on the rulebase
*/
private final RuleCompilation compilation;
/**
* Dependency checkers for a more precise GRD
*/
private final Collection checkers;
/**
* Constructor initialized with the rules from the rulebase
* @param rulebase the ruleset to compute dependencies over
* @param compilation compilation used on the ruleset
* @param checkers checkers of the dependencies
*/
public GRDImpl(RuleBase rulebase, RuleCompilation compilation, Collection checkers) {
super(GRDEdge.class);
this.rulebase = rulebase;
this.compilation = compilation;
this.checkers = checkers;
for(FORule r : this.getRules()) {
this.addRule(r);
}
this.computeDependencies();
}
/**
* Constructor initialized with the rules from the rulebase no compilation and a productivity checker
* @param ruleBase the ruleset to compute dependencies over
*/
public GRDImpl(RuleBase ruleBase) {
this(ruleBase, NoRuleCompilation.instance(), List.of(ProductivityChecker.instance()));
}
/**
* Constructor initialized with the rules from the rulebase and a productivity checker
* @param ruleBase the ruleset to compute dependencies over
* @param compilation compilation used on the ruleset
*/
public GRDImpl(RuleBase ruleBase, RuleCompilation compilation) {
this(ruleBase, compilation, List.of(ProductivityChecker.instance()));
}
/**
* Constructor initialized with the rules from the rulebase and no compilation
* @param ruleBase the ruleset to compute dependencies over
* @param checkers checkers of the dependencies
*/
public GRDImpl(RuleBase ruleBase, Collection checkers) {
this(ruleBase, NoRuleCompilation.instance(), checkers);
}
//
//
//
@Override
public Collection getRules() {
return this.rulebase.getRules();
}
@Override
public Set getTriggeredRules(FORule src) {
Set triggeredRules = new HashSet<>();
for (GRDEdge edge : this.outgoingEdgesOf(src)) {
if(edge.isPositive()) {
triggeredRules.add(this.getEdgeTarget(edge));
}
}
return triggeredRules;
}
@Override
public Set getPreventedRules(FORule src) {
Set preventedRules = new HashSet<>();
for (GRDEdge edge : this.outgoingEdgesOf(src)) {
if(!edge.isPositive()) {
preventedRules.add(this.getEdgeTarget(edge));
}
}
return preventedRules;
}
@Override
public boolean isStratifiable() {
for(Graph component : this.getStronglyConnectedComponents()) {
for(GRDEdge edge : component.edgeSet()) {
if(!edge.isPositive()) {
return false;
}
}
}
return true;
}
@Override
public List getStratification() {
List stratum = new ArrayList<>();
for(Graph component : this.getStronglyConnectedComponents()) {
stratum.add(new RuleBaseImpl(component.vertexSet()));
}
return stratum;
}
@Override
public Optional> getSingleEvaluationStratification() {
List strates = new ArrayList();
GRDImpl graph = new GRDImpl(this.rulebase, this.compilation, this.checkers) {
@Override
public double getEdgeWeight(GRDEdge e) {
return -1;
}
};
BellmanFordShortestPath bellmanFordAlgorithm = new BellmanFordShortestPath<>(graph);
try {
FORule fictiveSourceNode = new FORuleImpl(null, null);
graph.addVertex(fictiveSourceNode);
for(FORule r : graph.vertexSet()) {
if(r != fictiveSourceNode) {
GRDEdge edge = new GRDEdge(true);
graph.addEdge(fictiveSourceNode, r, edge);
}
}
SingleSourcePaths shortestPaths = bellmanFordAlgorithm.getPaths(fictiveSourceNode);
graph.removeVertex(fictiveSourceNode);
Map> rulesByCost = new HashMap>();
for(FORule r : graph.vertexSet()) {
double cost = shortestPaths.getWeight(r);
Set rulesInCost = rulesByCost.getOrDefault(cost, new HashSet());
rulesInCost.add(r);
rulesByCost.put(cost, rulesInCost);
}
rulesByCost.keySet().stream()
.sorted()
.forEachOrdered(cost -> strates.add(new RuleBaseImpl(rulesByCost.get(cost))));
} catch (NegativeCycleDetectedException e) {
return Optional.empty();
}
return Optional.of(Lists.reverse(strates));
}
@Override
public Optional> getPseudoMinimalStratification() {
List stratum = new ArrayList<>();
BellmanFordShortestPath bellmanFordAlgorithm = new BellmanFordShortestPath<>(this);
try {
FORule fictiveSourceNode = new FORuleImpl(null, null);
this.addVertex(fictiveSourceNode);
for(FORule r : this.vertexSet()) {
if(r != fictiveSourceNode) {
GRDEdge edge = new GRDEdge(true);
this.addEdge(fictiveSourceNode, r, edge);
}
}
SingleSourcePaths shortestPaths = bellmanFordAlgorithm.getPaths(fictiveSourceNode);
this.removeVertex(fictiveSourceNode);
Map> rulesByCost = new HashMap<>();
for(FORule r : this.vertexSet()) {
double cost = shortestPaths.getWeight(r);
Set rulesInCost = rulesByCost.getOrDefault(cost, new HashSet<>());
rulesInCost.add(r);
rulesByCost.put(cost, rulesInCost);
}
rulesByCost.keySet().stream()
.sorted()
.forEachOrdered(cost -> stratum.add(new RuleBaseImpl(rulesByCost.get(cost))));
} catch (NegativeCycleDetectedException e) {
return Optional.empty();
}
return Optional.of(Lists.reverse(stratum));
}
//
//
//
@Override
public double getEdgeWeight(GRDEdge e) {
if(e.isPositive()) {
return 0;
} else {
return -1;
}
}
private void addRule(FORule r) {
this.addVertex(r);
}
private void computeDependencies() {
for(FORule r1 : this.vertexSet()) {
for(FORule r2 : this.vertexSet()) {
this.computeDependency(r1, r2);
}
}
}
private void computeDependency(FORule r1, FORule r2) {
this.computePositiveDependency(r1, r2);
this.computeNegativeDependency(r1, r2);
}
private void computePositiveDependency(FORule r1, FORule r2) {
FORule r1safe = Rules.freshRenaming(r1);
FORule r2safe = Rules.freshRenaming(r2);
FOFormula r2bodyPositive = Rules.getPositiveBodyPart(r2safe);
FOQuery> r2query = FOQueryFactory.instance().createOrGetQuery(r2bodyPositive, Set.of(), null);
QueryUnifierAlgorithm unifierAlgo = new QueryUnifierAlgorithm(this.compilation);
Collection unifiers = unifierAlgo.getMostGeneralSinglePieceUnifiers(r2query, r1safe);
for(QueryUnifier u : unifiers) {
boolean isValid = true;
for(DependencyChecker checker : this.checkers) {
if(!checker.isValidPositiveDependency(r1safe, r2safe, u)) {
isValid = false;
break;
}
}
if(isValid) {
this.addEdge(r1, r2, new GRDEdge(true));
return;
}
}
}
private void computeNegativeDependency(FORule r1, FORule r2) {
FORule r1safe = Rules.freshRenaming(r1);
FORule r2safe = Rules.freshRenaming(r2);
FOFormula r2bodyNegative = Rules.getNegativeBodyParts(r2safe);
FOQuery> r2query = FOQueryFactory.instance().createOrGetQuery(r2bodyNegative, Set.of(), null);
QueryUnifierAlgorithm unifierAlgo = new QueryUnifierAlgorithm(this.compilation);
Collection unifiers = unifierAlgo.getMostGeneralSinglePieceUnifiers(r2query, r1safe);
for(QueryUnifier u : unifiers) {
boolean isValid = true;
for(DependencyChecker checker : this.checkers) {
if(!checker.isValidNegativeDependency(r1safe, r2safe, u)) {
isValid = false;
break;
}
}
if(isValid) {
this.addEdge(r1, r2, new GRDEdge(false));
return;
}
}
}
private List> getStronglyConnectedComponents() {
return Lists.reverse(new GabowStrongConnectivityInspector<>(this).getStronglyConnectedComponents());
}
}