Package solver.search.strategy.selectors.variables

Source Code of solver.search.strategy.selectors.variables.ImpactBased

/*
* Copyright (c) 1999-2014, Ecole des Mines de Nantes
* All rights reserved.
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
*     * Redistributions of source code must retain the above copyright
*       notice, this list of conditions and the following disclaimer.
*     * Redistributions in binary form must reproduce the above copyright
*       notice, this list of conditions and the following disclaimer in the
*       documentation and/or other materials provided with the distribution.
*     * Neither the name of the Ecole des Mines de Nantes nor the
*       names of its contributors may be used to endorse or promote products
*       derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND ANY
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE REGENTS AND CONTRIBUTORS BE LIABLE FOR ANY
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package solver.search.strategy.selectors.variables;

import gnu.trove.list.TIntList;
import gnu.trove.list.array.TIntArrayList;
import memory.IStateDouble;
import solver.ICause;
import solver.Solver;
import solver.exception.ContradictionException;
import solver.explanations.Deduction;
import solver.explanations.Explanation;
import solver.explanations.VariableState;
import solver.search.loop.ISearchLoop;
import solver.search.loop.monitors.IMonitorContradiction;
import solver.search.loop.monitors.IMonitorDownBranch;
import solver.search.loop.monitors.IMonitorRestart;
import solver.search.strategy.assignments.DecisionOperator;
import solver.search.strategy.decision.Decision;
import solver.search.strategy.decision.fast.FastDecision;
import solver.search.strategy.strategy.AbstractStrategy;
import solver.variables.IntVar;
import solver.variables.Variable;
import util.PoolManager;
import util.iterators.DisposableValueIterator;

import java.util.Random;

/**
* Implementation of the search described in:
* "Impact-Based Search Strategies for Constraint Programming",
* Philippe Refalo, CP2004.
* <br/>
* <p/>
* Impacts of variables are first computed on initialisation, (only 3 values are tested for bounded variables);
* Ties are broken randomly.
*
* @author Charles Prud'homme
* @since 21/09/12
*/
public class ImpactBased extends AbstractStrategy<IntVar> implements IMonitorDownBranch, IMonitorRestart,
        IMonitorContradiction, ICause {

    protected final int aging; // aging parameter
    protected double[][] Ilabel; // impact per labeling
    protected int[] offsets; // initial lower bound of each variable
    protected int split; // domains are divided into at most 2^s subdomains
    protected IStateDouble searchSpaceSize;

    protected int currentVar = -1, currentVal = -1;

    TIntList bests = new TIntArrayList();

    java.util.Random random; //  a random object to break ties

    protected int nodeImpact;

    PoolManager<FastDecision> decisionPool;

    protected Solver solver;

    protected boolean asgntFailed; // does the assignment leads to a failure

    protected boolean learnsAndFails; // does the learning pahse leads to a failure
    protected IntVar lAfVar; // the index of one of the variables involved into a failure during the learning phase

    protected long timeLimit = Integer.MAX_VALUE; // a time limit for init()

    /**
     * Create an Impact-based search strategy with Node Impact strategy.
     * <p/>
     * <b>"Impact-Based Search Strategies for Constraint Programming",
     * Philippe Refalo, CP2004.</b>
     *
     * @param ivariables variables of the problem (should be integers)
     * @param alpha      aging parameter
     * @param split      split parameter for subdomains computation
     * @param nodeImpact force update of impacts every <code>nodeImpact</code> nodes. Set value to 0 to avoid using it.
     * @param seed       a seed for random
     * @param initOnly   only apply the initialisation phase, do not update impact thereafter
     */
    public ImpactBased(IntVar[] ivariables, int alpha, int split, int nodeImpact, long seed, boolean initOnly) { //TODO: node impacts
        super(ivariables);
        this.solver = ivariables[0].getSolver();
        this.aging = alpha;
        this.split = (int) Math.pow(2, split);
        this.searchSpaceSize = solver.getEnvironment().makeFloat();
        random = new Random(seed);
        decisionPool = new PoolManager<FastDecision>();
        this.nodeImpact = nodeImpact;
        if (!initOnly) solver.plugMonitor(this);
    }

    @Override
    public Decision<IntVar> computeDecision(IntVar variable) {
        if (variable == null || variable.isInstantiated()) {
            return null;
        }
        if (currentVar == -1 || vars[currentVar] != variable) {
            // retrieve indice of the variable in vars
      for(int i=0;i<vars.length;i++){
        if(vars[i]==variable){
          currentVar = i;
        }
      }
            assert vars[currentVar] == variable;
        }
        bests.clear();
        double bestImpact = 1.0;
        if (variable.hasEnumeratedDomain()) {
            DisposableValueIterator it = variable.getValueIterator(true);
            int o = offsets[currentVar];
            while (it.hasNext()) {
                int val = it.next();
                double impact = Ilabel[currentVar][val - o];
                if (impact < bestImpact) {
                    bests.clear();
                    bests.add(val);
                    bestImpact = impact;
                } else if (impact == bestImpact) {
                    bests.add(val);
                }
            }
            it.dispose();

            currentVal = bests.get(random.nextInt(bests.size()));
        } else {
            int lb = variable.getLB();
            int ub = variable.getUB();
            currentVal = random.nextBoolean() ? lb : ub;
        }

        FastDecision currrent = decisionPool.getE();
        if (currrent == null) {
            currrent = new FastDecision(decisionPool);
        }
        currrent.set(variable, currentVal, DecisionOperator.int_eq);
        //System.out.printf("D: %d, %d: %s\n", currentVar, currentVal, best);
        return currrent;
    }

    @Override
    public Decision<IntVar> getDecision() {
        IntVar best = null;
        // 1. first select the variable with the largest impact
        bests.clear();
        double bestImpact = -Double.MAX_VALUE;
        for (int i = 0; i < vars.length; i++) {
            if (!vars[i].isInstantiated()) {
                double imp = computeImpact(i);
                if (imp > bestImpact) {
                    bests.clear();
                    bests.add(i);
                    bestImpact = imp;
                } else if (imp == bestImpact) {
                    bests.add(i);
                }
            }
        }
        if (bests.size() > 0) {
            // 2. select the variable
            currentVar = bests.get(random.nextInt(bests.size()));
            best = vars[currentVar];
        }
        return computeDecision(best);
    }

    public void setTimeLimit(long timeLimit) {
        if (timeLimit > -1) {
            this.timeLimit = timeLimit;
        }
    }

    @Override
    public void init() throws ContradictionException {
        long tl = System.currentTimeMillis() + this.timeLimit;
        // 0. Data structure construction
        Ilabel = new double[vars.length][];
        offsets = new int[vars.length];
        // 1. Estimation of assignment and variable impacts
        double before = searchSpaceSize();
        searchSpaceSize.set(before);
        learnsAndFails = false;
        loop:
        for (int i = 0; i < vars.length; i++) {
            IntVar v = vars[i];
            int offset = v.getLB();
            int UB = v.getUB();
            int dsz = UB - offset + 1;//v.getDomainSize();
            if (!v.isInstantiated()) { // if the variable is not instantiated
                Ilabel[i] = new double[v.hasEnumeratedDomain() ? dsz : 1];
                offsets[i] = offset;

                if (v.hasEnumeratedDomain()) {
                    if (v.getDomainSize() < split) { // try each value
                        DisposableValueIterator it = v.getValueIterator(true);
                        while (it.hasNext()) {
                            if (System.currentTimeMillis() > tl) {
                                break loop;
                            }
                            int a = it.next();
                            double im = computeImpact(v, a, before);
                            Ilabel[i][a - offset] = im;
                        }
                        it.dispose();
                    } else { // estimate per subdomains
                        int step = 0;
                        int size = dsz / split;
                        DisposableValueIterator it = v.getValueIterator(true);
                        while (it.hasNext()) {
                            if (System.currentTimeMillis() > tl) {
                                break loop;
                            }
                            int a = it.next();
                            double im;
                            if (step % size == 0) {
                                im = computeImpact(v, a, before);
                            } else {
                                im = Ilabel[i][a - 1 - offset];
                            }
                            Ilabel[i][a - offset] = im;
                            step++;
                        }
                        it.dispose();
                    }
                } else {
                    if (System.currentTimeMillis() > tl) {
                        break;
                    }
                    // A. choose 3 values in the domain to have an estimation of the impact
                    double i1 = computeImpact(v, v.getLB(), before);
                    double i2 = computeImpact(v, v.getUB(), before);
                    double i3 = computeImpact(v, (v.getLB() + v.getUB()) / 2, before);
                    Ilabel[i][0] = (i1 + i2 + i3) / 3d;
                }
            }
        }
        if (learnsAndFails) {
            // If the initialisation detects a failure, then the problem has no solution!
            learnsAndFails = false;
            solver.getEngine().fails(this, lAfVar, "Impact::init:: detect failures");
        } else if (System.currentTimeMillis() > tl) {
            LOGGER.debug("impact Search stops its init phase -- reach time limit!");
            for (int i = 0; i < vars.length; i++) {  // create arrays to avoid null pointer errors
                IntVar v = vars[i];
                int offset = v.getLB();
                int UB = v.getUB();
                int dsz = UB - offset + 1;//v.getDomainSize();
                if (!v.isInstantiated() && Ilabel[i] == null) {
                    Ilabel[i] = new double[v.hasEnumeratedDomain() ? dsz : 1];
                    offsets[i] = offset;
                }
            }
        }
    }


    @Override
    public void beforeDownLeftBranch() {
    }

    @Override
    public void afterDownLeftBranch() {
        if (currentVar > -1) { // if the decision was computed by another strategy
            if (asgntFailed) {
                updateImpact(1.0d, currentVar, currentVal);
            } else {
                double sssz = searchSpaceSize();
                updateImpact(sssz / searchSpaceSize.get(), currentVar, currentVal);
                searchSpaceSize.set(sssz);
            }
            currentVar = -1;
        }
        asgntFailed = false; // to handle cases where a contradiction was thrown, but the decision was computed outside
        reevaluateImpact();
    }

    @Override
    public void beforeDownRightBranch() {
    }

    @Override
    public void afterDownRightBranch() {
        reevaluateImpact();
    }

    @Override
    public void onContradiction(ContradictionException cex) {
        asgntFailed = true;
    }

    /**
     * Compute the impact of a <b>variable</b>
     *
     * @param idx index of the variable
     * @return the impact of the variable idx
     */
    protected double computeImpact(int idx) {
        IntVar var = vars[idx];
        if (var.hasEnumeratedDomain()) {
            int of = offsets[idx];
            DisposableValueIterator it = var.getValueIterator(true);
            double impact = 0.0;
            while (it.hasNext()) {
                int val = it.next();
                impact += Ilabel[idx][val - of];
            }
            it.dispose();
            return impact - var.getDomainSize();
        } else {
            return Ilabel[idx][0] - var.getDomainSize();
        }
    }

    /**
     * Compute the impact of an <b>assignment</b>
     *
     * @param v      the variable
     * @param a      the value
     * @param before search space size before the assignment
     * @return the impact I(v = a)
     */
    private double computeImpact(IntVar v, int a, double before) {
        solver.getEnvironment().worldPush();
        double after;
        try {
            v.instantiateTo(a, this);
            solver.getEngine().propagate();
            after = searchSpaceSize();
            solver.getEnvironment().worldPop();
            return 1.0d - (after / before);
        } catch (ContradictionException e) {
            solver.getEngine().flush();
            solver.getEnvironment().worldPop();
            // if the value leads to fail, then the value can be removed from the domain
            try {
                v.removeValue(a, this);
                solver.getEngine().propagate();
            } catch (ContradictionException ex) {
                learnsAndFails = true;
                lAfVar = v;
                solver.getEngine().flush();
            }
            return 1.0d;
        }
    }

    /**
     * Update the impact of an assignment I(v=a)
     *
     * @param nImpact new impact
     * @param varIdx  index of the variable
     * @param valIdx  index of the value
     */
    protected void updateImpact(double nImpact, int varIdx, int valIdx) {
        valIdx = Ilabel[varIdx].length > 1 ? valIdx - offsets[varIdx] : 0;
        double impact = Ilabel[varIdx][valIdx] * (aging - 1);
        impact += nImpact;
        impact /= aging;
        Ilabel[varIdx][valIdx] = impact;
    }

    /**
     * Compute the search space size
     *
     * @return search space size
     */
    protected double searchSpaceSize() {
        double size = 1;
        for (int i = 0; i < vars.length; i++) {
            size *= vars[i].getDomainSize();
            assert size > 0 : "Search space is not correct!";
        }
        if (size == Double.POSITIVE_INFINITY) {
            size = Double.MAX_VALUE;
        }
        return size;
    }

    protected void reevaluateImpact() {
        if (nodeImpact > 0 && solver.getMeasures().getNodeCount() % nodeImpact == 0) {
            double before = searchSpaceSize.get();
            learnsAndFails = false;
            for (int i = 0; i < vars.length; i++) {
                IntVar v = vars[i];
                int dsz = v.getDomainSize();
                if (!v.isInstantiated()) { // if the variable is not instantiated
                    int offset = v.getLB();
                    if (v.hasEnumeratedDomain()) {
                        if (v.getDomainSize() < split) { // try each value
                            DisposableValueIterator it = v.getValueIterator(true);
                            while (it.hasNext()) {
                                int a = it.next();
                                double im = computeImpact(v, a, before);
                                updateImpact(im, i, a);
                            }
                            it.dispose();
                        } else { // estimate per subdomains
                            int step = 0;
                            int size = dsz / split;
                            DisposableValueIterator it = v.getValueIterator(true);
                            while (it.hasNext()) {
                                int a = it.next();
                                double im;
                                if (step % size == 0) {
                                    im = computeImpact(v, a, before);
                                } else {
                                    im = Ilabel[i][a - 1 - offset];
                                }
                                updateImpact(im, i, a);
                                step++;
                            }
                            it.dispose();
                        }
                    } else {
                        // A. choose 3 values in the domain to have an estimation of the impact
                        double i1 = computeImpact(v, v.getLB(), before);
                        double i2 = computeImpact(v, v.getUB(), before);
                        double i3 = computeImpact(v, (v.getLB() + v.getUB()) / 2, before);
                        updateImpact((i1 + i2 + i3) / 3d, i, 0);
                    }
                }
            }
            if (learnsAndFails) {
                learnsAndFails = false;
                solver.getSearchLoop().moveTo(ISearchLoop.UP_BRANCH);
                //noinspection ThrowableResultOfMethodCallIgnored
                solver.getSearchLoop().getSMList().onContradiction(
            solver.getEngine().getContradictionException().set(this, lAfVar, "Impact::reevaluate:: detect failures")
        );
            }
        }
    }


    /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

    @Override
    public void explain(Deduction d, Explanation e) {
        // the current deduction is due to the current domain of the involved variables
        for (Variable v : this.vars) {
            v.explain(VariableState.DOM, e);
        }
    }

    @Override
    public void beforeRestart() {
    }

    @Override
    public void afterRestart() {
    }
}
TOP

Related Classes of solver.search.strategy.selectors.variables.ImpactBased

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.