/*******************************************************************************
* Copyright (c) 2004-2008 Gabor Bergmann and Daniel Varro
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License v. 2.0 which is available at
* http://www.eclipse.org/legal/epl-v20.html.
*
* SPDX-License-Identifier: EPL-2.0
*******************************************************************************/
package tools.refinery.viatra.runtime.matchers.planning;
import java.util.Arrays;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.WeakHashMap;
import java.util.stream.Collectors;
import tools.refinery.viatra.runtime.matchers.context.IQueryMetaContext;
import tools.refinery.viatra.runtime.matchers.planning.helpers.TypeHelper;
import tools.refinery.viatra.runtime.matchers.planning.operations.POperation;
import tools.refinery.viatra.runtime.matchers.planning.operations.PProject;
import tools.refinery.viatra.runtime.matchers.planning.operations.PStart;
import tools.refinery.viatra.runtime.matchers.psystem.PBody;
import tools.refinery.viatra.runtime.matchers.psystem.PConstraint;
import tools.refinery.viatra.runtime.matchers.psystem.PVariable;
import tools.refinery.viatra.runtime.matchers.psystem.TypeJudgement;
/**
* A plan representing a subset of (or possibly all the) constraints evaluated. A SubPlan instance is responsible for
* representing a state of the plan; but after it is initialized it is expected be immutable
* (exception: inferred constraints, see {@link #inferConstraint(PConstraint)}).
*
*
A SubPlan is constructed by applying a {@link POperation} on zero or more parent SubPlans.
* Important maintained information:
* - set of variables whose values are known when the runtime evaluation is at this stage,
*
- set of constraints that are known to hold true at this point.
*
*
* Recommended to instantiate via a {@link SubPlanFactory} or subclasses,
* so that query planners can subclass SubPlan if needed.
*
* @author Gabor Bergmann
*
*/
public class SubPlan {
private PBody body;
private List extends SubPlan> parentPlans;
private POperation operation;
private final Set visibleVariables;
private final Set allVariables;
private final Set introducedVariables; // delta compared to first parent
private Set allConstraints;
private Set deltaConstraints; // delta compared to all parents
private Set externallyInferredConstraints; // inferred in addition to direct consequences of the operation and parents
/**
* A SubPlan is constructed by applying a {@link POperation} on zero or more parent SubPlans.
*/
public SubPlan(PBody body, POperation operation, SubPlan... parentPlans) {
this(body, operation, Arrays.asList(parentPlans));
}
/**
* A SubPlan is constructed by applying a {@link POperation} on zero or more parent SubPlans.
*/
public SubPlan(PBody body, POperation operation, List extends SubPlan> parentPlans) {
super();
this.body = body;
this.parentPlans = parentPlans;
this.operation = operation;
this.externallyInferredConstraints = new HashSet();
this.deltaConstraints = new HashSet(operation.getDeltaConstraints());
this.allVariables = new HashSet();
for (PConstraint constraint: deltaConstraints) {
this.allVariables.addAll(constraint.getDeducedVariables());
}
this.allConstraints = new HashSet(deltaConstraints);
for (SubPlan parentPlan: parentPlans) {
this.allConstraints.addAll(parentPlan.getAllEnforcedConstraints());
this.allVariables.addAll(parentPlan.getAllDeducedVariables());
}
// TODO this is ugly a bit
if (operation instanceof PStart) {
this.visibleVariables = new HashSet(((PStart) operation).getAPrioriVariables());
this.allVariables.addAll(visibleVariables);
} else if (operation instanceof PProject) {
this.visibleVariables = new HashSet(((PProject) operation).getToVariables());
} else {
this.visibleVariables = new HashSet();
for (SubPlan parentPlan: parentPlans)
this.visibleVariables.addAll(parentPlan.getVisibleVariables());
for (PConstraint constraint: deltaConstraints)
this.visibleVariables.addAll(constraint.getDeducedVariables());
}
this.introducedVariables = new HashSet(this.visibleVariables);
if (!parentPlans.isEmpty())
introducedVariables.removeAll(parentPlans.get(0).getVisibleVariables());
operation.checkConsistency(this);
}
@Override
public String toString() {
return toLongString();
}
public String toShortString() {
return String.format("Plan{%s}:%s",
visibleVariables.stream().map(PVariable::getName).collect(Collectors.joining(",")),
operation.getShortName());
}
public String toLongString() {
return String.format("%s<%s>",
toShortString(),
parentPlans.stream().map(Object::toString).collect(Collectors.joining("; ")));
}
/**
* All constraints that are known to hold at this point
*/
public Set getAllEnforcedConstraints() {
return allConstraints;
}
/**
* The new constraints enforced at this stage of plan, that aren't yet enforced at parents
* (results are also included in {@link SubPlan#getAllEnforcedConstraints()})
*/
public Set getDeltaEnforcedConstraints() {
return deltaConstraints;
}
/**
* Indicate that a given constraint was found to be automatically satisfied at this point
* without additional operations.
* (results are also included in {@link SubPlan#getDeltaEnforcedConstraints()})
*
* Warning: not propagated automatically to child plans,
* so best to invoke before constructing further SubPlans.
*/
public void inferConstraint(PConstraint constraint) {
externallyInferredConstraints.add(constraint);
deltaConstraints.add(constraint);
allConstraints.add(constraint);
}
public PBody getBody() {
return body;
}
/**
* Variables which are assigned a value at this point
* (results are also included in {@link SubPlan#getAllDeducedVariables()})
*/
public Set getVisibleVariables() {
return visibleVariables;
}
/**
* Variables which have been assigned a value;
* includes visible variables (see {@link #getVisibleVariables()})
* and additionally any variables hidden by a projection (see {@link PProject}).
*/
public Set getAllDeducedVariables() {
return allVariables;
}
/**
* Delta compared to first parent: variables that are visible here but were not visible at the first parent.
*/
public Set getIntroducedVariables() {
return introducedVariables;
}
public List extends SubPlan> getParentPlans() {
return parentPlans;
}
public POperation getOperation() {
return operation;
}
/**
* The closure of all type judgments of enforced constraints at this point.
* No subsumption applied.
*/
public Set getAllImpliedTypeJudgements(IQueryMetaContext context) {
Set impliedJudgements = allImpliedTypeJudgements.get(context);
if (impliedJudgements == null) {
Set equivalentJudgements = TypeHelper.getDirectJudgements(getAllEnforcedConstraints(), context);
impliedJudgements = TypeHelper.typeClosure(equivalentJudgements, context);
allImpliedTypeJudgements.put(context, impliedJudgements);
}
return impliedJudgements;
}
private WeakHashMap> allImpliedTypeJudgements = new WeakHashMap>();
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result
+ ((operation == null) ? 0 : operation.hashCode());
result = prime * result
+ ((parentPlans == null) ? 0 : parentPlans.hashCode());
return result;
}
@Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (!(obj instanceof SubPlan))
return false;
SubPlan other = (SubPlan) obj;
if (operation == null) {
if (other.operation != null)
return false;
} else if (!operation.equals(other.operation))
return false;
if (parentPlans == null) {
if (other.parentPlans != null)
return false;
} else if (!parentPlans.equals(other.parentPlans))
return false;
return true;
}
}