All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.ggp.base.util.assignments.ComplexAssignmentIterationPlanFactory Maven / Gradle / Ivy

The newest version!
package org.ggp.base.util.assignments;

import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;

import org.ggp.base.util.AlloyUtils;
import org.ggp.base.util.Immutables;
import org.ggp.base.util.Pair;
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.GdlPool;
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.SentenceDomainModel;
import org.ggp.base.util.gdl.model.SentenceForm;
import org.ggp.base.util.gdl.model.SentenceFormDomain;
import org.ggp.base.util.graph.DirectedMinimumSpanningTreeFinder;

import com.google.common.base.Preconditions;
import com.google.common.collect.ArrayListMultimap;
import com.google.common.collect.HashBasedTable;
import com.google.common.collect.HashMultimap;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.ListMultimap;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import com.google.common.collect.SetMultimap;
import com.google.common.collect.Sets;
import com.google.common.collect.Table;
import com.google.common.collect.Tables;

//@NotThreadSafe
//TODO: Change name; this isn't as general as it could be
//TODO: This doesn't currently take into account FunctionInfo and thus is
//      pretty terrible.
public class ComplexAssignmentIterationPlanFactory {
    private final ImmutableList positiveConjuncts;
    private final ImmutableMap domains;
    private final ImmutableSet distinctConstraints;
    //What about negative conjuncts? The assignment iterator does at least have to
    //know about them, to know what variables to update

    //	private final List
    //Given one variable and its value, list possible values for another variable...
    //	SetMultimap variableDomains;

    public ComplexAssignmentIterationPlanFactory(
            ImmutableList positiveConjuncts,
            ImmutableMap domains,
            ImmutableSet distinctConstraints) {
        this.positiveConjuncts = positiveConjuncts;
        this.domains = domains;
        this.distinctConstraints = distinctConstraints;
    }

    public static ComplexAssignmentIterationPlanFactory create(GdlRule rule,
            SentenceDomainModel domainModel) {
        ImmutableList positiveConjuncts = rule.getBody().stream()
                .filter(literal -> (literal instanceof GdlSentence))
                .map(literal -> (GdlSentence) literal)
                .collect(Immutables.collectList());
        ImmutableSet distinctConstraints = rule.getBody().stream()
                .filter(literal -> (literal instanceof GdlDistinct))
                .map(literal -> (GdlDistinct) literal)
                .collect(Immutables.collectSet());
        Map domains = Maps.newHashMap();
        for (GdlSentence sentence : positiveConjuncts) {
            SentenceForm form = domainModel.getSentenceForm(sentence);
            domains.put(sentence, domainModel.getDomain(form));
        }
        return new ComplexAssignmentIterationPlanFactory(
                positiveConjuncts,
                ImmutableMap.copyOf(domains),
                distinctConstraints);
    }

    public NewAssignmentIterationPlan create() {
        //Do a sort of search through candidate plans...
        //That, or adapt the existing algorithm for finding a plan
        Set variablesToAssign = GdlUtils.getVariables(positiveConjuncts);

        SetMultimap variableDomains = computeVariableDomains(variablesToAssign);

        //From variable/value to list of values for another given variable
        Map>> possibleValuesGivenOtherVarByVar =
                computePossibleValuesGivenOtherVarByVar(variablesToAssign);

        limitPossibleValuesByVariableDomains(possibleValuesGivenOtherVarByVar, variableDomains);

        //Now this might be interesting...
        //What would be the thing we want to minimize here?
        //Basically, we'd have some ordering over the variables...
        //And then we'd want to assign each variable besides the first some "predecessor" from
        //earlier in the ordering
        //The predecessor would be used to assign values to the variable when its time comes
        //(so e.g. if it's a function, just one value given)

        /*
         * So we presumably want to minimize the number of value combinations we iterate over...
         * If we ignore variance within variables, that relationship looks something like...
         *
         * for x before y, we check the average domain size of y over the values of x
         *
         * Then we want to find the ordering that minimizes the product of these...
         *
         * This is a directed variant of the minimal spanning tree problem.
         *
         * We'll use an implementation of the Chu-Liu/Edmonds algorithm:
         * http://en.wikipedia.org/wiki/Edmonds%27_algorithm
         */

        GdlVariable dummyInitialVariable = createNewVariable(variablesToAssign);
        Set varsForDmst = Sets.newHashSet(variablesToAssign);
        varsForDmst.add(dummyInitialVariable);
        System.out.println("Positive conjuncts are: " + positiveConjuncts);
        Map, Double> weights = computeWeights(variableDomains, possibleValuesGivenOtherVarByVar, dummyInitialVariable);
        System.out.println("weights: " + weights);
        Map varSources = DirectedMinimumSpanningTreeFinder.findDmst(varsForDmst, dummyInitialVariable, weights);

        replaceWithDummyVarWherePossible(varSources, weights, dummyInitialVariable);

        //TODO: Turn this into an iteration plan
        return createPlan(varSources, dummyInitialVariable, variableDomains, possibleValuesGivenOtherVarByVar);

        //TODO: I'm worried this will do poorly where things like sums are involved, relative to the old approach...

        //Worth noting: This approach is not suited for a prover that's expected
        //to answer certain queries where certain values are filled in and others aren't.
        //Those will have different optimal plans (per query).


    }

    private void replaceWithDummyVarWherePossible(
            Map varSources,
            Map, Double> weights,
            GdlVariable dummyInitialVariable) {
        for (GdlVariable dependentVar : ImmutableList.copyOf(varSources.keySet())) {
            //			if (varSources.get(key))
            GdlVariable requiredVar = varSources.get(dependentVar);
            if (requiredVar != dummyInitialVariable) {
                double weightUsed = weights.get(Pair.of(requiredVar, dependentVar));
                double dummyWeight = weights.get(Pair.of(dummyInitialVariable, dependentVar));
                //				System.out.println("Weights are " + (weightUsed - dummyWeight));
                if (weightUsed >= dummyWeight) {
                    varSources.put(dependentVar, dummyInitialVariable);
                }
            }
        }
    }

    private NewAssignmentIterationPlan createPlan(
            Map varSources,
            GdlVariable dummyInitialVariable,
            SetMultimap variableDomains,
            Map>> possibleValuesGivenOtherVarByVar) {
        System.out.println("varSources: " + varSources);
        //TODO: Replace references with dummyInitialVariable if it doesn't change the weights?
        List assignmentOrder = toOrdering(varSources, dummyInitialVariable);
        List strategies = Lists.newArrayList();
        //		for (GdlVariable curVar : assignmentOrder) {
        for (int varIndex = 0; varIndex < assignmentOrder.size(); varIndex++) {
            GdlVariable curVar = assignmentOrder.get(varIndex);
            //Create a strategy
            final AssignmentStrategy strategy;
            if (varSources.get(curVar) == dummyInitialVariable) {
                List definedIndices = ImmutableList.of(varIndex);
                List> partialAssignments =
                        variableDomains.get(curVar).stream()
                        .map(constant -> ImmutableList.of(constant))
                        .collect(Immutables.collectList());
                //Just use the variable domain
                strategy = SimpleAssignmentStrategy.create(definedIndices, partialAssignments);
            } else {
                GdlVariable dependentVar = varSources.get(curVar);
                List dependentIndices = ImmutableList.of(assignmentOrder.indexOf(dependentVar));
                List definedIndices = ImmutableList.of(varIndex);

                //Get from possibleVars...
                Map, List>> contents = Maps.newHashMap();
                Map> possibleValues = possibleValuesGivenOtherVarByVar.get(curVar).row(dependentVar);
                for (Entry> entry : possibleValues.entrySet()) {
                    contents.put(ImmutableList.of(entry.getKey()),
                            entry.getValue().stream().map(constant -> ImmutableList.of(constant)).collect(Immutables.collectList()));
                }

                //Use something that depends on the ___
                strategy = DependentAssignmentStrategy.create(dependentIndices, definedIndices, contents);
            }
            strategies.add(strategy);
        }
        //		if (strategies.isEmpty()) {
        //			System.out.println("Strategies are empty, conjuncts are: " + positiveConjuncts);
        //		}
        return ComplexAssignmentIterationPlan.create(assignmentOrder, strategies);
    }

    private List toOrdering(
            Map varSources,
            GdlVariable dummyInitialVariable) {
        List ordering = Lists.newArrayList();
        Set alreadyAdded = Sets.newHashSet(dummyInitialVariable);
        while (ordering.size() < varSources.size()) {
            //TODO: Infinite loop here...
            for (Entry entry : varSources.entrySet()) {
                GdlVariable varToConsider = entry.getKey();
                GdlVariable dependency = entry.getValue();
                if (!alreadyAdded.contains(varToConsider) &&
                        alreadyAdded.contains(dependency)) {
                    ordering.add(varToConsider);
                    alreadyAdded.add(varToConsider);
                }
            }
            //			System.out.println("Ordering: " + ordering);
            //			System.out.println("Dummy variable: " + dummyInitialVariable);
            //			System.out.println("Var sources: " + varSources);
            //			System.out.println("Not already added: " + Sets.difference(alreadyAdded, varSources.keySet()));
        }
        return ordering;
    }

    //Note that we use the log of the average domain size, to minimize the products as opposed to the sums
    private Map, Double> computeWeights(
            SetMultimap variableDomains,
            Map>> possibleValuesGivenOtherVarByVar,
            GdlVariable dummyInitialVariable) {
        // Pairs are , i.e. 
        Map, Double> weights = Maps.newHashMap();

        //First, get the values for the dummy
        for (GdlVariable childVar : variableDomains.keySet()) {
            int domainSize = variableDomains.get(childVar).size();
            System.out.println("Domain size for " + childVar + ": " + domainSize + " -> " + Math.log(domainSize/1.0));
            weights.put(Pair.of(dummyInitialVariable, childVar), Math.log(domainSize/1.0));
        }

        //Now, for all the variable pairs...
        for (GdlVariable targetVariable : possibleValuesGivenOtherVarByVar.keySet()) {
            Table> possibleValuesGivenOtherVar = possibleValuesGivenOtherVarByVar.get(targetVariable);
            for (GdlVariable inputVariable : possibleValuesGivenOtherVar.rowKeySet()) {
                System.out.println("Computing average domain size for " + targetVariable + " given fixed " + inputVariable);
                double averageDomainSize = getAverageSize(possibleValuesGivenOtherVar.row(inputVariable));
                System.out.println("Average domain size: " + averageDomainSize + " -> " + Math.log(averageDomainSize));
                weights.put(Pair.of(inputVariable, targetVariable), Math.log(averageDomainSize));
            }
        }
        return weights;
    }

    private double getAverageSize(Map> row) {
        int sum = 0;
        for (Set domain : row.values()) {
            System.out.println("Adding domain " + domain);
            sum += domain.size();
        }
        System.out.println("Sum is " + sum + ", size is " + row.size());
        return sum / (double) row.size();
    }

    private GdlVariable createNewVariable(Set variablesToAssign) {
        for (int i = 0; true; i++) {
            GdlVariable varToTry = GdlPool.getVariable("?a" + i);
            if (!variablesToAssign.contains(varToTry)) {
                return varToTry;
            }
        }
    }

    private void limitPossibleValuesByVariableDomains(
            Map>> possibleValuesGivenOtherVarByVar,
            SetMultimap variableDomains) {
        for (GdlVariable var : possibleValuesGivenOtherVarByVar.keySet()) {
            Table> possibleValuesGivenOtherVar = possibleValuesGivenOtherVarByVar.get(var);
            Set basicDomain = variableDomains.get(var);
            for (Set subdomain : possibleValuesGivenOtherVar.values()) {
                subdomain.retainAll(basicDomain);
            }
        }
    }

    private Map>> computePossibleValuesGivenOtherVarByVar(Set variables) {
        Map>> result = Maps.newHashMap();
        for (GdlVariable targetVariable : variables) {
            Table>> domainsGivenOtherVars = HashBasedTable.create();
            //Okay...
            for (GdlSentence positiveConjunct : positiveConjuncts) {
                List tuple = GdlUtils.getTupleFromSentence(positiveConjunct);
                //				if (tuple.contains(targetVariable)) {
                SentenceFormDomain domain = domains.get(positiveConjunct);
                for (int j = 0; j < tuple.size(); j++) {
                    if (tuple.get(j) == targetVariable) {
                        //For each other variable in the sentence...
                        for (int i = 0; i < tuple.size(); i++) {
                            GdlTerm term = tuple.get(i);
                            if (term instanceof GdlVariable && term != targetVariable) {
                                GdlVariable inputVariable = (GdlVariable) term;
                                //If that slot has that value, what can the target variable be?
                                //TODO: Should we just skip this if it's a Cartesian domain?
                                Map> domainsForSlotGivenValuesOfOtherSlot =
                                        domain.getDomainsForSlotGivenValuesOfOtherSlot(j, i);

                                for (Entry> entry : domainsForSlotGivenValuesOfOtherSlot.entrySet()) {
                                    GdlConstant inputVariableValue = entry.getKey();
                                    Set outputDomain = entry.getValue();
                                    if (domainsGivenOtherVars.get(inputVariable, inputVariableValue) == null) {
                                        domainsGivenOtherVars.put(inputVariable, inputVariableValue, Lists.newArrayList());
                                    }
                                    domainsGivenOtherVars.get(inputVariable, inputVariableValue).add(outputDomain);
                                }
                            }
                        }
                    }
                }
            }

            Table> intersectionsGivenOtherVars = HashBasedTable.create(Tables.transformValues(
                    domainsGivenOtherVars, AlloyUtils::intersectAll));
            result.put(targetVariable, intersectionsGivenOtherVars);
        }
        //Do this part after the intersections
        for (GdlDistinct distinct : distinctConstraints) {
            if (distinct.getArg1() instanceof GdlVariable && distinct.getArg2() instanceof GdlVariable) {
                //TODO: Handle this case
                GdlVariable var1 = (GdlVariable) distinct.getArg1();
                GdlVariable var2 = (GdlVariable) distinct.getArg2();
                //It's okay to modify these directly, since we just copied over the table
                removeMatchingValues(result.get(var1).row(var2));
                removeMatchingValues(result.get(var2).row(var1));
            }
        }
        return result;
    }

    private void removeMatchingValues(Map> row) {
        for (Entry> entry : row.entrySet()) {
            entry.getValue().remove(entry.getKey());
        }
    }

    private SetMultimap computeVariableDomains(Set variables) {
        ListMultimap> domainsToIntersect = ArrayListMultimap.create();
        for (GdlSentence positiveConjunct : positiveConjuncts) {
            SentenceFormDomain domain = domains.get(positiveConjunct);
            List tuple = GdlUtils.getTupleFromSentence(positiveConjunct);
            Preconditions.checkState(tuple.size() == domain.getForm().getTupleSize());
            for (int i = 0; i < tuple.size(); i++) {
                GdlTerm term = tuple.get(i);
                if (term instanceof GdlVariable) {
                    GdlVariable var = (GdlVariable) term;
                    domainsToIntersect.put(var, domain.getDomainForSlot(i));
                }
            }
        }
        //Intersect the domains
        SetMultimap result = HashMultimap.create();
        domainsToIntersect.asMap().forEach((var, domains) -> {
            result.putAll(var, AlloyUtils.intersectAll(domains));
        });

        for (GdlDistinct distinct : distinctConstraints) {
            if (distinct.getArg1() instanceof GdlVariable && distinct.getArg2() instanceof GdlConstant) {
                result.remove(distinct.getArg1(), distinct.getArg2());
            } else if (distinct.getArg2() instanceof GdlVariable && distinct.getArg1() instanceof GdlConstant) {
                result.remove(distinct.getArg2(), distinct.getArg1());
            }
        }
        return result;
    }

}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy