org.ggp.base.util.gdl.model.assignments.AssignmentsImpl Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of alloy-ggp-base Show documentation
Show all versions of alloy-ggp-base Show documentation
A modified version of the GGP-Base library for Alloy.
The newest version!
/**
*
*/
package org.ggp.base.util.gdl.model.assignments;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.PriorityQueue;
import java.util.Set;
import javax.annotation.Nullable;
import org.ggp.base.util.gdl.GdlUtils;
import org.ggp.base.util.gdl.grammar.GdlConstant;
import org.ggp.base.util.gdl.grammar.GdlDistinct;
import org.ggp.base.util.gdl.grammar.GdlFunction;
import org.ggp.base.util.gdl.grammar.GdlLiteral;
import org.ggp.base.util.gdl.grammar.GdlPool;
import org.ggp.base.util.gdl.grammar.GdlProposition;
import org.ggp.base.util.gdl.grammar.GdlRelation;
import org.ggp.base.util.gdl.grammar.GdlRule;
import org.ggp.base.util.gdl.grammar.GdlSentence;
import org.ggp.base.util.gdl.grammar.GdlTerm;
import org.ggp.base.util.gdl.grammar.GdlVariable;
import org.ggp.base.util.gdl.model.SentenceForm;
import org.ggp.base.util.gdl.model.SimpleSentenceForm;
import org.ggp.base.util.gdl.transforms.CommonTransforms;
import org.ggp.base.util.gdl.transforms.ConstantChecker;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.Lists;
public class AssignmentsImpl implements Assignments {
private boolean empty;
private boolean allDone = false;
//Contains all the assignments of variables we could make
private Map headAssignment;
private List varsToAssign;
private List> valuesToIterate;
private List valuesToCompute;
private List indicesToChangeWhenNull; //See note below
private List distincts;
private List varsToChangePerDistinct; //indexing same as distincts
/*
* What does indicesToChangeWhenNull do? Well, sometimes after incrementing
* part of the iterator, we find that a function being used to define a slot
* in the tuple has no value corresponding to its inputs (the inputs are
* outside the function's domain). In that case, we set the value to null,
* then leave it to the makeNextAssignmentValid() method to deal with it.
* We want to increment something in the input, but we need to know what
* in the input we should increment (i.e. which is the rightmost slot in
* the function's input). This is recorded in indicesToChangeWhenNull. If
* a slot is not defined by a function, then presumably it will not be null,
* so its value here is unimportant. Setting its value to -1 would help
* catch errors.
*/
private List>> tuplesBySource; //indexed by conjunct
private List sourceDefiningSlot; //indexed by var slot
private List> varsChosenBySource; //indexed by conjunct, then slot
private List> putDontCheckBySource; //indexed by conjunct, then slot
/**
* Creates an Assignments object that generates AssignmentIterators.
* These can be used to efficiently iterate over all possible assignments
* for variables in a given rule.
*
* @param headAssignment An assignment of variables whose values should be
* fixed. May be empty.
* @param rule The rule whose assignments we want to iterate over.
* @param varDomains A map containing the possible values for each variable
* in the rule. (All such values are GdlConstants.)
* @param functionInfoMap
* @param completedSentenceFormValues
*/
public AssignmentsImpl(Map headAssignment,
GdlRule rule, Map> varDomains,
Map functionInfoMap,
@Nullable Map> completedSentenceFormValues) {
empty = false;
this.headAssignment = headAssignment;
//We first have to find the remaining variables in the body
varsToAssign = GdlUtils.getVariables(rule);
//Remove all the duplicates; we do, however, want to keep the ordering
List newVarsToAssign = new ArrayList();
for(GdlVariable v : varsToAssign)
if(!newVarsToAssign.contains(v))
newVarsToAssign.add(v);
varsToAssign = newVarsToAssign;
varsToAssign.removeAll(headAssignment.keySet());
//varsToAssign is set at this point
//We see if iterating over entire tuples will give us a
//better result, and we look for the best way of doing that.
//Let's get the domains of the variables
//Map> varDomains = model.getVarDomains(rule);
//Since we're looking at a particular rule, we can do this one step better
//by looking at the domain of the head, which may be more restrictive
//and taking the intersections of the two domains where applicable
//Map> headVarDomains = model.getVarDomainsInSentence(rule.getHead());
//We can run the A* search for a good set of source conjuncts
//at this point, then use the result to build the rest.
Map completedSentenceFormSizes = new HashMap();
if(completedSentenceFormValues != null)
for(SentenceForm form : completedSentenceFormValues.keySet())
completedSentenceFormSizes.put(form, completedSentenceFormValues.get(form).size());
Map varDomainSizes = new HashMap();
for(GdlVariable var : varDomains.keySet())
varDomainSizes.put(var, varDomains.get(var).size());
IterationOrderCandidate bestOrdering;
bestOrdering = getBestIterationOrderCandidate(rule, varDomains,/*model,*/ functionInfoMap, completedSentenceFormSizes, headAssignment, false); //TODO: True here?
//Want to replace next few things with order
//Need a few extra things to handle the use of iteration over existing tuples
varsToAssign = bestOrdering.getVariableOrdering();
//For each of these vars, we have to find one or the other.
//Let's start by finding all the domains, a task already done.
valuesToIterate = Lists.newArrayListWithCapacity(varsToAssign.size());
for(GdlVariable var : varsToAssign) {
if(varDomains.containsKey(var)) {
if(!varDomains.get(var).isEmpty())
valuesToIterate.add(ImmutableList.copyOf(varDomains.get(var)));
else
valuesToIterate.add(ImmutableList.of(GdlPool.getConstant("0")));
} else {
valuesToIterate.add(ImmutableList.of(GdlPool.getConstant("0")));
}
}
//Okay, the iteration-over-domain is done.
//Now let's look at sourced iteration.
sourceDefiningSlot = new ArrayList(varsToAssign.size());
for(int i = 0; i < varsToAssign.size(); i++) {
sourceDefiningSlot.add(-1);
}
//We also need to convert values into tuples
//We should do so while constraining to any constants in the conjunct
//Let's convert the conjuncts
List sourceConjuncts = bestOrdering.getSourceConjuncts();
tuplesBySource = Lists.newArrayListWithCapacity(sourceConjuncts.size());//new ArrayList>>(sourceConjuncts.size());
varsChosenBySource = Lists.newArrayListWithCapacity(sourceConjuncts.size());//new ArrayList>(sourceConjuncts.size());
putDontCheckBySource = Lists.newArrayListWithCapacity(sourceConjuncts.size());//new ArrayList>(sourceConjuncts.size());
for(int j = 0; j < sourceConjuncts.size(); j++) {
GdlSentence sourceConjunct = sourceConjuncts.get(j);
SentenceForm form = SimpleSentenceForm.create(sourceConjunct);
//flatten into a tuple
List conjunctTuple = GdlUtils.getTupleFromSentence(sourceConjunct);
//Go through the vars/constants in the tuple
List constraintSlots = new ArrayList();
List constraintValues = new ArrayList();
List varsChosen = new ArrayList();
List putDontCheck = new ArrayList();
for(int i = 0; i < conjunctTuple.size(); i++) {
GdlTerm term = conjunctTuple.get(i);
if(term instanceof GdlConstant) {
constraintSlots.add(i);
constraintValues.add((GdlConstant) term);
//TODO: What if tuple size ends up being 0?
//Need to keep that in mind
} else if(term instanceof GdlVariable) {
int varIndex = varsToAssign.indexOf(term);
varsChosen.add(varIndex);
if(sourceDefiningSlot.get(varIndex) == -1) {
//We define it
sourceDefiningSlot.set(varIndex, j);
putDontCheck.add(true);
} else {
//It's an overlap; we just check for consistency
putDontCheck.add(false);
}
} else {
throw new RuntimeException("Function returned in tuple");
}
}
varsChosenBySource.add(ImmutableList.copyOf(varsChosen));
putDontCheckBySource.add(ImmutableList.copyOf(putDontCheck));
//Now we put the tuples together
//We use constraintSlots and constraintValues to check that the
//tuples have compatible values
Collection sentences = completedSentenceFormValues.get(form);
List> tuples = Lists.newArrayList();
byTuple: for(GdlSentence sentence : sentences) {
//Check that it doesn't conflict with our headAssignment
if (!headAssignment.isEmpty()) {
Map tupleAssignment = GdlUtils.getAssignmentMakingLeftIntoGroundRight(sourceConjunct, sentence);
for (GdlVariable var : headAssignment.keySet()) {
if (tupleAssignment.containsKey(var)
&& tupleAssignment.get(var) != headAssignment.get(var)) {
continue byTuple;
}
}
}
List longTuple = GdlUtils.getTupleFromGroundSentence(sentence);
List shortTuple = new ArrayList(varsChosen.size());
for(int c = 0; c < constraintSlots.size(); c++) {
int slot = constraintSlots.get(c);
GdlConstant value = constraintValues.get(c);
if(!longTuple.get(slot).equals(value))
continue byTuple;
}
int c = 0;
for(int s = 0; s < longTuple.size(); s++) {
//constraintSlots is sorted in ascending order
if(c < constraintSlots.size()
&& constraintSlots.get(c) == s)
c++;
else
shortTuple.add(longTuple.get(s));
}
//The tuple fits the source conjunct
tuples.add(ImmutableList.copyOf(shortTuple));
}
//sortTuples(tuples); //Needed? Useful? Not sure. Probably not?
tuplesBySource.add(ImmutableList.copyOf(tuples));
}
//We now want to see which we can give assignment functions to
valuesToCompute = new ArrayList(varsToAssign.size());
for(@SuppressWarnings("unused") GdlVariable var : varsToAssign) {
valuesToCompute.add(null);
}
indicesToChangeWhenNull = new ArrayList(varsToAssign.size());
for(int i = 0; i < varsToAssign.size(); i++) {
//Change itself, why not?
//Actually, instead let's try -1, to catch bugs better
indicesToChangeWhenNull.add(-1);
}
//Now we have our functions already selected by the ordering
//bestOrdering.functionalConjunctIndices;
//Make AssignmentFunctions out of the ordering
List functionalConjuncts = bestOrdering.getFunctionalConjuncts();
// System.out.println("functionalConjuncts: " + functionalConjuncts);
for(int i = 0; i < functionalConjuncts.size(); i++) {
GdlSentence functionalConjunct = functionalConjuncts.get(i);
if(functionalConjunct != null) {
//These are the only ones that could be constant functions
SentenceForm conjForm = SimpleSentenceForm.create(functionalConjunct);
FunctionInfo functionInfo = null;
if(functionInfoMap != null)
functionInfo = functionInfoMap.get(conjForm);
if(functionInfo != null) {
//Now we need to figure out which variables are involved
//and which are suitable as functional outputs.
//1) Which vars are in this conjunct?
List varsInSentence = GdlUtils.getVariables(functionalConjunct);
//2) Of these vars, which is "rightmost"?
GdlVariable rightmostVar = getRightmostVar(varsInSentence);
//3) Is it only used once in the relation?
if(Collections.frequency(varsInSentence, rightmostVar) != 1)
continue; //Can't use it
//4) Which slot is it used in in the relation?
//5) Build an AssignmentFunction if appropriate.
// This should be able to translate from values of
// the other variables to the value of the wanted
// variable.
AssignmentFunction function = AssignmentFunction.create((GdlRelation)functionalConjunct, functionInfo, rightmostVar, varsToAssign, headAssignment);
//We don't guarantee that this works until we check
if(!function.functional())
continue;
int index = varsToAssign.indexOf(rightmostVar);
valuesToCompute.set(index, function);
Set remainingVarsInSentence = new HashSet(varsInSentence);
remainingVarsInSentence.remove(rightmostVar);
GdlVariable nextRightmostVar = getRightmostVar(remainingVarsInSentence);
indicesToChangeWhenNull.set(index, varsToAssign.indexOf(nextRightmostVar));
}
}
}
//We now have the remainingVars also assigned their domains
//We also cover the distincts here
//Assume these are just variables and constants
distincts = new ArrayList();
for(GdlLiteral literal : rule.getBody()) {
if(literal instanceof GdlDistinct)
distincts.add((GdlDistinct) literal);
}
computeVarsToChangePerDistinct();
//Need to add "distinct" restrictions to head assignment, too...
checkDistinctsAgainstHead();
//We are ready for iteration
// System.out.println("headAssignment: " + headAssignment);
// System.out.println("varsToAssign: " + varsToAssign);
// System.out.println("valuesToCompute: " + valuesToCompute);
// System.out.println("sourceDefiningSlot: " + sourceDefiningSlot);
}
private GdlVariable getRightmostVar(Collection vars) {
GdlVariable rightmostVar = null;
for(GdlVariable var : varsToAssign)
if(vars.contains(var))
rightmostVar = var;
return rightmostVar;
}
public AssignmentsImpl() {
//The assignment is impossible; return nothing
empty = true;
}
@SuppressWarnings("unchecked")
public AssignmentsImpl(GdlRule rule, /*SentenceModel model,*/ Map> varDomains,
Map functionInfoMap,
Map> completedSentenceFormValues) {
this(Collections.EMPTY_MAP, rule, varDomains, functionInfoMap, completedSentenceFormValues);
}
private void checkDistinctsAgainstHead() {
for(GdlDistinct distinct : distincts) {
GdlTerm term1 = CommonTransforms.replaceVariables(distinct.getArg1(), headAssignment);
GdlTerm term2 = CommonTransforms.replaceVariables(distinct.getArg2(), headAssignment);
if(term1.equals(term2)) {
//This fails
empty = true;
allDone = true;
}
}
}
@Override
public Iterator