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

org.chocosolver.solver.constraints.ISetConstraintFactory Maven / Gradle / Ivy

There is a newer version: 4.10.17
Show newest version
/*
 * This file is part of choco-solver, http://choco-solver.org/
 *
 * Copyright (c) 2022, 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;

import org.chocosolver.solver.Model;
import org.chocosolver.solver.constraints.set.*;
import org.chocosolver.solver.variables.BoolVar;
import org.chocosolver.solver.variables.IntVar;
import org.chocosolver.solver.variables.SetVar;
import org.chocosolver.util.tools.ArrayUtils;

/**
 * Interface to make constraints over SetVar
 *
 * A kind of factory relying on interface default implementation to allow (multiple) inheritance
 *
 * @author Jean-Guillaume FAGES
 * @since 4.0.0
 */
public interface ISetConstraintFactory {

	//***********************************************************************************
	// BASICS : union/inter/subset/card
	//***********************************************************************************

	/**
	 * Creates a constraint ensuring that union is exactly the union of values taken by ints,
	 *
	 * @param ints  an array of integer variables
	 * @param union a set variable
	 * @return a constraint ensuring that union = {x | x in ints}
	 */
	default Constraint union(IntVar[] ints, SetVar union) {
		return new Constraint(ConstraintsName.SETINTVALUESUNION
				, new PropSetIntValuesUnion(ints, union)
				, new PropSetIntValuesUnion(ints, union)
		);
	}

	/**
	 * Creates a constraint which ensures that the union of sets is equal to unionSet
	 *
	 * @param sets an array of set variables
	 * @param unionSet set variable representing the union of sets
	 * @return A constraint ensuring that the union of sets is equal to unionSet
	 */
	default Constraint union(SetVar[] sets, SetVar unionSet) {
		return new Constraint(ConstraintsName.SETUNION, new PropUnion(sets, unionSet), new PropUnion(sets, unionSet));
	}

	/**
	 * Creates a constraint which ensures that the intersection of sets is equal to intersectionSet
	 *
	 * @param sets an array of set variables
	 * @param intersectionSet a set variable representing the intersection of sets
	 * @return A constraint ensuring that the intersection of sets is equal to intersectionSet
	 */
	default Constraint intersection(SetVar[] sets, SetVar intersectionSet) {
		return intersection(sets, intersectionSet, false);
	}

	/**
	 * Creates a constraint which ensures that the intersection of sets is equal to intersectionSet
	 *
	 * @param sets an array of set variables
	 * @param intersectionSet a set variable representing the intersection of sets
         * @param boundConsistent adds an additional propagator to guarantee BC
	 * @return A constraint ensuring that the intersection of sets is equal to intersectionSet
	 */
	default Constraint intersection(SetVar[] sets, SetVar intersectionSet, boolean boundConsistent) {
		if (sets.length == 0) {
			throw new IllegalArgumentException("The intersection of zero sets is undefined.");
		}
		if (boundConsistent) {
			return new Constraint(ConstraintsName.SETINTERSECTION,
				new PropIntersection(sets, intersectionSet),
				sets.length == 1
					? new PropAllEqual(new SetVar[]{sets[0], intersectionSet})
					: new PropIntersectionFilterSets(sets, intersectionSet));
		} else {
			return new Constraint(ConstraintsName.SETINTERSECTION, new PropIntersection(sets, intersectionSet));
		}
	}

	/**
	 * Creates a constraint establishing that sets[i] is a subset of sets[j] if isets[i] is a subset of sets[j] if isets
	 * |{s in sets where |s|=0}| = nbEmpty
	 *
	 * @param sets an array of set variables
	 * @param nbEmpty integer restricting the number of empty sets in sets
	 * @return A constraint ensuring that |{s in sets where |s|=0}| = nbEmpty
	 */
	default Constraint nbEmpty(SetVar[] sets, int nbEmpty) {
		return nbEmpty(sets, sets[0].getModel().intVar(nbEmpty));
	}

	/**
	 * Creates a constraint counting the number of empty sets sets
	 * |{s in sets where |s|=0}| = nbEmpty
	 *
	 * @param sets an array of set variables
	 * @param nbEmpty integer variable restricting the number of empty sets in sets
	 * @return A constraint ensuring that |{s in sets where |s|=0}| = nbEmpty
	 */
	default Constraint nbEmpty(SetVar[] sets, IntVar nbEmpty) {
		return new Constraint(ConstraintsName.SETNBEMPTY, new PropNbEmpty(sets, nbEmpty));
	}

	/**
	 * Creates a constraint linking set1 and set2 with an index offset :
	 * x in set1 <=> x+offset in set2
	 *
	 * @param set1 a set variable
	 * @param set2 a set variable
	 * @param offset offset index
	 * @return a constraint ensuring that x in set1 <=> x+offset in set2
	 */
	default Constraint offSet(SetVar set1, SetVar set2, int offset) {
		return new Constraint(ConstraintsName.SETOFFSET, new PropOffSet(set1, set2, offset));
	}

	/**
	 * Creates a constraint preventing set to be empty
	 *
	 * @param set a SetVar which should not be empty
	 * @return a constraint ensuring that set is not empty
	 */
	default Constraint notEmpty(SetVar set) {
		return new Constraint(ConstraintsName.SETNOTEMPTY, new PropNotEmpty(set));
	}

	//***********************************************************************************
	// SUM
	//***********************************************************************************

	/**
	 * Creates a constraint summing elements of set
	 * sum{i | i in set} = sum
	 *
	 * @param set       a set variable
	 * @param sum       an integer variable representing sum{i | i in set}
	 * @return a constraint ensuring that sum{i | i in set} = sum
	 */
	default Constraint sum(SetVar set, IntVar sum) {
		return sumElements(set,null,0,sum);
	}

	/**
	 * Creates a constraint summing weights given by a set of indices:
	 * sum{weights[i] | i in indices} = sum
	 *
	 * Also ensures that elements in indices belong to [0, weights.length-1]
	 *
	 * @param indices   a set variable
	 * @param weights   integers representing the weight of each element in indices
	 * @param sum       an integer variable representing sum{weights[i] | i in indices}
	 * @return a constraint ensuring that sum{weights[i] | i in indices} = sum
	 */
	default Constraint sumElements(SetVar indices, int[] weights, IntVar sum) {
		return sumElements(indices,weights,0,sum);
	}

	/**
	 * Creates a constraint summing weights given by a set of indices:
	 * sum{weights[i-offset] | i in indices} = sum
	 *
	 * Also ensures that elements in indices belong to [offset, offset+weights.length-1]
	 *
	 * @param indices   a set variable
	 * @param weights   integers representing the weight of each element in indices
	 * @param offset    offset index : should be 0 by default
	 *                  but generally 1 with MiniZinc API
	 *                  which counts from 1 to n instead of counting from 0 to n-1 (Java standard)
	 * @param sum       an integer variable representing sum{weights[i-offset] | i in indices}
	 * @return a constraint ensuring that sum{weights[i-offset] | i in indices} = sum
	 */
	default Constraint sumElements(SetVar indices, int[] weights, int offset, IntVar sum) {
		return new Constraint(ConstraintsName.SETSUM, new PropSumOfElements(indices, weights, offset, sum));
	}

	//***********************************************************************************
	// MAX - MIN
	//***********************************************************************************

	/**
	 * Creates a constraint over the maximum element in a set:
	 * max{i | i in set} = maxElementValue
	 *
	 * @param set             a set variable
	 * @param maxElementValue an integer variable representing the maximum element in set
	 * @param notEmpty         true : the set variable cannot be empty
	 *                         false : the set may be empty (if set is empty, this constraint is not applied)
	 * @return a constraint ensuring that max{i | i in set} = maxElementValue
	 */
	default Constraint max(SetVar set, IntVar maxElementValue, boolean notEmpty) {
		return max(set, null, 0, maxElementValue, notEmpty);
	}

	/**
	 * Creates a constraint over the maximum element induces by a set:
	 * max{weights[i-offset] | i in indices} = maxElementValue
	 *
	 * @param indices           a set variable containing elements in range [offset,weights.length-1+offset]
	 * @param weights           integers representing the weight of each element in indices
	 * @param offset            offset index : should be 0 by default
	 *                          but generally 1 with MiniZinc API
	 *                          which counts from 1 to n instead of counting from 0 to n-1 (Java standard)
	 * @param maxElementValue an integer variable representing the maximum weight induced by indices
	 * @param notEmpty         true : the set variable cannot be empty
	 *                         false : the set may be empty (if indices is empty, this constraint is not applied)
	 * @return a constraint ensuring that max{weights[i-offset] | i in indices} = maxElementValue
	 */
	default Constraint max(SetVar indices, int[] weights, int offset, IntVar maxElementValue, boolean notEmpty) {
		if (notEmpty) {
			return new Constraint(ConstraintsName.SETMAX, new PropNotEmpty(indices), new PropMaxElement(indices, weights, offset, maxElementValue, true));
		} else {
			return new Constraint(ConstraintsName.SETMAX, new PropMaxElement(indices, weights, offset, maxElementValue, false));
		}
	}

	/**
	 * Creates a constraint over the minimum element in a set:
	 * min{i | i in set} = minElementValue
	 *
	 * @param set             a set variable
	 * @param minElementValue an integer variable representing the minimum element in set
	 * @param notEmpty         true : the set variable cannot be empty
	 *                         false : the set may be empty (if set is empty, this constraint is not applied)
	 * @return a constraint ensuring that min{i | i in set} = minElementValue
	 */
	default Constraint min(SetVar set, IntVar minElementValue, boolean notEmpty) {
		return min(set, null, 0, minElementValue, notEmpty);
	}

	/**
	 * Creates a constraint over the minimum element induces by a set:
	 * min{weights[i-offset] | i in indices} = minElementValue
	 *
	 * @param indices           a set variable containing elements in range [offset,weights.length-1+offset]
	 * @param weights           integers representing the weight of each element in indices
	 * @param offset            offset index : should be 0 by default
	 *                          but generally 1 with MiniZinc API
	 *                          which counts from 1 to n instead of counting from 0 to n-1 (Java standard)
	 * @param minElementValue an integer variable representing the minimum weight induced by indices
	 * @param notEmpty         true : the set variable cannot be empty
	 *                         false : the set may be empty (if indices is empty, this constraint is not applied)
	 * @return a constraint ensuring that min{weights[i-offset] | i in indices} = minElementValue
	 */
	default Constraint min(SetVar indices, int[] weights, int offset, IntVar minElementValue, boolean notEmpty) {
		if (notEmpty) {
			return new Constraint(ConstraintsName.SETMIN, new PropNotEmpty(indices), new PropMinElement(indices, weights, offset, minElementValue, true));
		} else {
			return new Constraint(ConstraintsName.SETMIN, new PropMinElement(indices, weights, offset, minElementValue, false));
		}
	}

	//***********************************************************************************
	// CHANNELING CONSTRAINTS : bool/int/graph
	//***********************************************************************************

	/**
	 * Creates a constraint channeling a set variable with boolean variables :
	 * i in set <=> bools[i] = TRUE
	 *
	 * @param bools	   an array of boolean variables
	 * @param set      a set variable
	 * @return a constraint ensuring that i in set <=> bools[i] = TRUE
	 */
	default Constraint setBoolsChanneling(BoolVar[] bools, SetVar set) {
		return setBoolsChanneling(bools,set,0);
	}

	/**
	 * Creates a constraint channeling a set variable with boolean variables :
	 * i in set <=> bools[i-offset] = TRUE
	 *
	 * @param bools	   an array of boolean variables
	 * @param set      a set variable
	 * @param offset   offset index : should be 0 by default
	 *                 but generally 1 with MiniZinc API
	 *                 which counts from 1 to n instead of counting from 0 to n-1 (Java standard)
	 * @return a constraint ensuring that i in set <=> bools[i-offset] = TRUE
	 */
	default Constraint setBoolsChanneling(BoolVar[] bools, SetVar set, int offset) {
		return new Constraint(ConstraintsName.SETBOOLCHANNELING, new PropBoolChannel(set, bools, offset));
	}

	/**
	 * Creates a constraint channeling set variables and integer variables :
	 * x in sets[y] <=> ints[x] = y
	 *
	 * @param sets     an array of set variables
	 * @param ints     an array of integer variables
	 * @return a constraint ensuring that x in sets[y] <=> ints[x] = y
	 */
	default Constraint setsIntsChanneling(SetVar[] sets, IntVar[] ints) {
		return setsIntsChanneling(sets,ints,0,0);
	}

	/**
	 * Creates a constraint channeling set variables and integer variables :
	 * x in sets[y-offset1] <=> ints[x-offset2] = y
	 *
	 * @param sets     an array of set variables
	 * @param ints     an array of integer variables
	 * @param offset1  offset index : should be 0 by default
	 *                 but generally 1 with MiniZinc API
	 *                 which counts from 1 to n instead of counting from 0 to n-1 (Java standard)
	 * @param offset2 offset index : should be 0 by default
	 *                 but generally 1 with MiniZinc API
	 *                 which counts from 1 to n instead of counting from 0 to n-1 (Java standard)
	 * @return a constraint ensuring that x in sets[y-offset1] <=> ints[x-offset2] = y
	 */
	default Constraint setsIntsChanneling(SetVar[] sets, IntVar[] ints, int offset1, int offset2) {
		return new Constraint(ConstraintsName.SETINTCHANNELING,
				new PropIntChannel(sets, ints, offset1, offset2),
				new PropIntChannel(sets, ints, offset1, offset2), new PropAllDisjoint(sets)
		);
	}

	//***********************************************************************************
	// MINIZINC API
	//***********************************************************************************

	/**
	 * Creates a constraint stating that the intersection of set1 and set2 should be empty
	 * Note that they can be both empty
	 *
	 * @param set1 a set variable
	 * @param set2 a set variable
	 * @return a constraint ensuring that set1 and set2 are disjoint (empty intersection)
	 */
	default Constraint disjoint(SetVar set1, SetVar set2) {
		return allDisjoint(set1, set2);
	}

	/**
	 * Creates a constraint stating that the intersection of sets should be empty
	 * Note that there can be multiple empty sets
	 *
	 * @param sets an array of disjoint set variables
	 * @return a constraint ensuring that sets are all disjoint (empty intersection)
	 */
	default Constraint allDisjoint(SetVar... sets) {
		return new Constraint(ConstraintsName.SETALLDISJOINT, new PropAllDisjoint(sets));
	}

	/**
	 * Creates a constraint stating that sets should all be different (not necessarily disjoint)
	 * Note that there cannot be more than one empty set
	 *
	 * @param sets an array of different set variables
	 * @return a constraint ensuring that sets are all different
	 */
	default Constraint allDifferent(SetVar... sets) {
		return new Constraint(ConstraintsName.SETALLDIFFERENT, new PropAllDiff(sets),
				new PropAllDiff(sets), new PropAtMost1Empty(sets)
		);
	}

	/**
	 * Creates a constraint stating that sets  should be all equal
	 *
	 * @param sets an array of set variables to be equal
	 * @return a constraint ensuring that all sets in sets are equal
	 */
	default Constraint allEqual(SetVar... sets) {
		return new Constraint(ConstraintsName.SETALLEQUAL, new PropAllEqual(sets));
	}

	/**
	 * Creates a constraint stating that partitions universe into sets:
	 * union(sets) = universe
	 * intersection(sets) = {}
	 *
	 * @param sets     an array of set variables whose values are subsets of universe
	 * @param universe a set variable representing the union of sets
	 * @return a constraint which ensures that sets forms a partition of universe
	 */
	default Constraint partition(SetVar[] sets, SetVar universe) {
		Constraint allDisjoint = allDisjoint(sets);
		allDisjoint.ignore();
		return new Constraint(ConstraintsName.SETPARTITION, ArrayUtils.append(
				allDisjoint.getPropagators(),
				new Propagator[]{new PropUnion(sets, universe), new PropUnion(sets, universe)}
		));
	}

	/**
	 * Creates a constraint stating that :
	 * x in sets[y-offset1] <=> y in invSets[x-offset2]
	 *
	 * @param sets         an array of set variables
	 * @param invSets an array of set variables
	 * @param offset1     offset index : should be 0 by default
	 *                     but generally 1 with MiniZinc API
	 *                     which counts from 1 to n instead of counting from 0 to n-1 (Java standard)
	 * @param offset2     offset index : should be 0 by default
	 *                     but generally 1 with MiniZinc API
	 *                     which counts from 1 to n instead of counting from 0 to n-1 (Java standard)
	 * @return a constraint ensuring that x in sets[y-offset1] <=> y in invSets[x-offset2]
	 */
	default Constraint inverseSet(SetVar[] sets, SetVar[] invSets, int offset1, int offset2) {
		return new Constraint(ConstraintsName.SETINVERSE, new PropInverse(sets, invSets, offset1, offset2));
	}

	/**
	 * Creates a constraint stating that sets are symmetric sets:
	 * x in sets[y] <=> y in sets[x]
	 *
	 * @param sets   an array of set variables
	 * @return a constraint ensuring that x in sets[y] <=> y in sets[x]
	 */
	default Constraint symmetric(SetVar... sets) {
		return symmetric(sets,0);
	}

	/**
	 * Creates a constraint stating that sets are symmetric sets:
	 * x in sets[y-offset] <=> y in sets[x-offset]
	 *
	 * @param sets   an array of set variables
	 * @param offset offset index : should be 0 by default
	 *               but generally 1 with MiniZinc API
	 *               which counts from 1 to n instead of counting from 0 to n-1 (Java standard)
	 * @return a constraint ensuring that x in sets[y-offset] <=> y in sets[x-offset]
	 */
	default Constraint symmetric(SetVar[] sets, int offset) {
		return new Constraint(ConstraintsName.SETSYMMETRIC, new PropSymmetric(sets, offset));
	}

	/**
	 * Creates a constraint enabling to retrieve an element set in sets:
	 * sets[index] = set
	 *
	 * @param index  an integer variable pointing to set's index into array sets
	 * @param sets   an array of set variables representing possible values for set
	 * @param set    a set variable equal to sets[index]
	 * @return a constraint ensuring that sets[index] = set
	 */
	default Constraint element(IntVar index, SetVar[] sets, SetVar set) {
		return element(index,sets,0,set);
	}

	/**
	 * Creates a constraint enabling to retrieve an element set in sets:
	 * sets[index-offset] = set
	 *
	 * @param index  an integer variable pointing to set's index into array sets
	 * @param sets   an array of set variables representing possible values for set
	 * @param offset offset index : should be 0 by default
	 *               but generally 1 with MiniZinc API
	 *               which counts from 1 to n instead of counting from 0 to n-1 (Java standard)
	 * @param set    a set variable equal to sets[index-offset]
	 * @return a constraint ensuring that sets[index-offset] = set
	 */
	default Constraint element(IntVar index, SetVar[] sets, int offset, SetVar set) {
		return new Constraint(ConstraintsName.SETELEMENT, new PropElement(index, sets, offset, set), new PropElement(index, sets, offset, set));
	}

	/**
	 * Creates a member constraint stating that set belongs to sets
	 *
	 * @param sets set variables representing possible values for set
	 * @param set  a set variable which takes its value in sets
	 * @return a constraint ensuring that set belongs to sets
	 */
	default Constraint member(SetVar[] sets, SetVar set) {
		IntVar index = set.getModel().intVar("idx_tmp", 0, sets.length - 1, false);
		return element(index, sets, 0, set);
	}

	/**
	 * Creates a member constraint stating that the value of intVar is in set
	 *
	 * @param intVar an integer variables which takes its value in set
	 * @param set    a set variables containing possible values for intVar
	 * @return a constraint ensuring that the value of intVar is in set
	 */
	default Constraint member(final IntVar intVar, final SetVar set) {
		if(intVar.isInstantiated()){
			return member(intVar.getValue(),set);
		}else {
			return new Constraint(ConstraintsName.SETMEMBER,
					intVar.hasEnumeratedDomain() ?
							new PropIntEnumMemberSet(set, intVar) :
							new PropIntBoundedMemberSet(set, intVar)) {
				@Override
				public Constraint makeOpposite() {
					return notMember(intVar, set);
				}
			};
		}
	}

	/**
	 * Creates a member constraint stating that the constant cst is in set
	 *
	 * @param cst an integer
	 * @param set a set variable
	 * @return a constraint ensuring that cst is in set
	 */
	default Constraint member(final int cst, final SetVar set) {
		return new Constraint(ConstraintsName.SETMEMBER,new PropIntCstMemberSet(set, cst)) {
			@Override
			public Constraint makeOpposite() {
				return notMember(cst, set);
			}
		};
	}

	/**
	 * Creates a member constraint stating that the value of intVar is not in set
	 *
	 * @param intVar an integer variables which does not take its values in set
	 * @param set     a set variables representing impossible values for intVar
	 * @return a constraint ensuring that the value of intVar is not in set
	 */
	default Constraint notMember(final IntVar intVar, final SetVar set) {
		if(intVar.isInstantiated()){
			return notMember(intVar.getValue(),set);
		}else {
			IntVar integer = intVar;
			if (!intVar.hasEnumeratedDomain()) {
				Model s = intVar.getModel();
				integer = s.intVar("enumViewOf(" + intVar.getName() + ")", intVar.getLB(), intVar.getUB(), false);
				s.arithm(integer, "=", intVar).post();
			}
			return new Constraint(ConstraintsName.SETNOTMEMBER,
					new PropNotMemberIntSet(integer, set),
					new PropNotMemberSetInt(integer, set)) {
				@Override
				public Constraint makeOpposite() {
					return member(intVar, set);
				}
			};
		}
	}

	/**
	 * Creates a member constraint stating that the constant cst is not in set
	 *
	 * @param cst an integer
	 * @param set a set variable
	 * @return a constraint ensuring that cst is not in set
	 */
	default Constraint notMember(final int cst, final SetVar set) {
		return new Constraint(ConstraintsName.SETNOTMEMBER,new PropIntCstNotMemberSet(set, cst)) {
			@Override
			public Constraint makeOpposite() {
				return member(cst, set);
			}
		};
	}
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy