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

org.chocosolver.solver.constraints.nary.clauses.ClauseBuilder Maven / Gradle / Ivy

The newest version!
/*
 * This file is part of choco-solver, http://choco-solver.org/
 *
 * Copyright (c) 2024, IMT Atlantique. All rights reserved.
 *
 * Licensed under the BSD 4-clause license.
 *
 * See LICENSE file in the project root for full license information.
 */
package org.chocosolver.solver.constraints.nary.clauses;

import gnu.trove.map.hash.TIntObjectHashMap;
import org.chocosolver.solver.Model;
import org.chocosolver.solver.learn.XParameters;
import org.chocosolver.solver.variables.BoolVar;
import org.chocosolver.solver.variables.IntVar;
import org.chocosolver.solver.variables.view.bool.BoolNotView;
import org.chocosolver.solver.variables.view.bool.BoolEqView;
import org.chocosolver.solver.variables.view.bool.BoolLeqView;
import org.chocosolver.util.objects.setDataStructures.iterable.IntIterableRangeSet;
import org.chocosolver.util.objects.setDataStructures.iterable.IntIterableSetUtils;
import org.chocosolver.util.tools.VariableUtils;

import java.util.*;

/**
 * A signed clause builder
 *
 * 

Project: choco-solver. * * @author Charles Prud'homme * @since 12/10/2016. */ public class ClauseBuilder { /** * When the nogood is true (based on variables declared domain) */ private static final short ALWAYSTRUE = 0b10; /** * * When the nogood is unknown */ private static final short UNKNOWN = 0b01; /** * When the nogood is false (based on variables declared domain) */ private static final short FALSE = 0b00; /** * Sets of variables */ private final Set vars; /** * Sets of forbidden values */ private final TIntObjectHashMap sets; /** * Status of the nogood to build */ private short status = FALSE; /** * Initial state of the variables */ private final TIntObjectHashMap initialDomains; /** * Nogood builder, to ease declaration of nogoods * * @param mModel model to declare the nogoods in */ public ClauseBuilder(Model mModel) { vars = new HashSet<>(); sets = new TIntObjectHashMap<>(); initialDomains = new TIntObjectHashMap<>(); Arrays.stream(mModel.retrieveIntVars(true)) .forEach(v -> initialDomains.put(v.getId(), new IntIterableRangeSet(v))); } /** * Add a literal (var ∈ set) in this, considering that the entry is only added once (no * need to perform internal operations). * The set can be recycle only after the call to {@link #buildNogood(Model)} * * @param var a variable * @param set a set of values * @return the nogood maker */ public ClauseBuilder put(IntVar var, IntIterableRangeSet set) { status |= UNKNOWN; if (var.isAConstant()) { if (!set.contains(var.getValue())) { //always true and ignore status |= ALWAYSTRUE; } // else, always false and ignore return this; } if (set.intersect(initialDomains.get(var.getId()))) { if (IntIterableSetUtils.includedIn(initialDomains.get(var.getId()), set)) { status |= ALWAYSTRUE; } this.vars.add(var); this.sets.put(var.getId(), set); } return this; } /** * Build the nogood in memory and post it to model. */ public void buildNogood(Model model) { if ((status & ALWAYSTRUE) == 0) { if ((status & UNKNOWN) != 0) { // at least one clause is unknown if (XParameters.ELIMINATE_VIEWS) eliminateViews(); vars.removeIf(var -> (sets.get(var.getId()).isEmpty())); IntVar[] _vars = vars.toArray(new IntVar[0]); Arrays.sort(_vars); // to avoid undeterministic behavior switch (vars.size()) { case 0: model.falseConstraint().post(); if (XParameters.PRINT_CLAUSE) model.getSolver().log().white().printf("learn: FALSE\n"); break; case 1: model.member(_vars[0], sets.get(_vars[0].getId())).post(); if (XParameters.PRINT_CLAUSE) model.getSolver().log().white().printf("learn: %s \u2208 %s\n", _vars[0], sets.get(_vars[0].getId())); break; default: IntIterableRangeSet[] ranges = new IntIterableRangeSet[_vars.length]; for (int i = 0; i < _vars.length; i++) { ranges[i] = sets.get(_vars[i].getId()); } model.getClauseConstraint().addClause(_vars, ranges); break; } } else { // always false model.falseConstraint().post(); throw new UnsupportedOperationException(); } }// else post nothing this.status = FALSE; this.vars.clear(); this.sets.clear(); } private void eliminateViews() { Stack keys = vars .stream() .filter(VariableUtils::isView) .collect( Stack::new, Stack::push, Vector::addAll ); while (!keys.isEmpty()) { IntVar v = keys.pop(); if (v instanceof BoolEqView) { eliminateEqView((BoolEqView) v, keys); } else if (v instanceof BoolLeqView) { eliminateLeqView((BoolLeqView) v, keys); } else if (v instanceof BoolNotView) { eliminateNotView((BoolNotView) v, keys); } } } private void eliminateEqView(BoolEqView ev, Stack keys) { IntIterableRangeSet set = sets.get(ev.getId()); assert set.size() == 1; assert set.min()>=0 && set.max() <=1; IntVar vv = ev.getVariable(); if (set.min() == 1) { if (vars.contains(vv)) { sets.get(vv.getId()).add(ev.cste); } else { put(vv, new IntIterableRangeSet(ev.cste)); if (VariableUtils.isView(vv)) { keys.push(vv); } } } else if (set.min() == 0) { IntIterableRangeSet es = initialDomains.get(vv.getId()).duplicate(); es.remove(ev.cste); if (vars.contains(vv)) { sets.get(vv.getId()).addAll(es); } else { put(vv, es); if (VariableUtils.isView(vv)) { keys.push(vv); } } } set.clear(); } private void eliminateLeqView(BoolLeqView lv, Stack keys) { IntIterableRangeSet set = sets.get(lv.getId()); assert set.size() == 1; assert set.min()>=0 && set.max() <=1; IntVar vv = lv.getVariable(); IntIterableRangeSet root = initialDomains.get(vv.getId()).duplicate(); if (set.min() == 1) { root.retainBetween(IntIterableRangeSet.MIN, lv.cste); } else if (set.min() == 0) { root.removeBetween(IntIterableRangeSet.MIN, lv.cste); } if (vars.contains(vv)) { sets.get(vv.getId()).addAll(root); } else { put(vv, root); if (VariableUtils.isView(vv)) { keys.push(vv); } } set.clear(); } private void eliminateNotView(BoolNotView nv, Stack keys) { IntIterableRangeSet set = sets.get(nv.getId()); BoolVar vv = nv.not(); assert set.size() == 1; int value = set.min(); if (vars.contains(vv)) { sets.get(vv.getId()).add(1 - value); } else { put(vv, new IntIterableRangeSet(1 - value)); if (VariableUtils.isView(vv)) { keys.push(vv); } } set.clear(); } public IntIterableRangeSet getInitialDomain(IntVar var) { return initialDomains.get(var.getId()); } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy