org.chocosolver.solver.constraints.extension.hybrid.HybridTuples Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of choco-solver Show documentation
Show all versions of choco-solver Show documentation
Open-source constraint solver.
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.extension.hybrid;
import org.chocosolver.solver.constraints.Operator;
import org.chocosolver.solver.exception.SolverException;
import org.chocosolver.util.objects.setDataStructures.iterable.IntIterableRangeSet;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
/**
* A class to define hybrid tuples, that is, tuples containing expressions of the following forms:
*
* - any()
* - eq(2),
* - gt(col(0), 1))
* - ...
*
*
* This kind of tuples makes possible to expression extension constraint in a very compact way.
* Instead of only listing all possible combinations (or forbidden ones), one can also
* define relationships between a variables and a value or relationships between a variable and other variables.
*
* For instance, declaring that 3 variables must be equal can be defined as:
*
* {@code
*
* HybridTuples tuples = new HybridTuples();
* tuples.add(any(), col(0), col(0));
* model.table(new IntVar[]{x, y, z}, tuples).post();
* }
*
*
* @author Charles Prud'homme
* @since 13/02/2023
*/
public class HybridTuples {
/**
* List of hybrid tuples declared
*/
protected final List hybridTuples;
/**
* For sanity check only
*/
private int arity;
/**
* Create an empty structure that stores hybrid tuples
*/
public HybridTuples() {
this.hybridTuples = new ArrayList<>();
}
/**
* Add a hybrid tuple to this storage.
*
* A hybrid tuple is an expression on column/variable that makes possible to define
* basic yet expressive expression like {@code gt(col(0).add(1))}.
*
* @param tuple the hybrid tuple as a set of expressions
* @throws SolverException if the tuple does not the match the arity of previously declared ones.
*/
public void add(ISupportable... tuple) {
if (hybridTuples.size() == 0) {
arity = tuple.length;
} else if (arity != tuple.length) {
throw new SolverException("The given tuple does not match the arity: " + arity);
}
this.hybridTuples.add(convert(tuple));
}
private ISupportable[] convert(ISupportable[] tuple) {
//noinspection unchecked
List[] t = new ArrayList[tuple.length];
for (int i = 0; i < tuple.length; i++) {
t[i] = new ArrayList<>();
}
for (int i = 0; i < tuple.length; i++) {
t[i].add(tuple[i]);
if (tuple[i] instanceof ISupportable.UnCol) {
ISupportable.UnCol col = (ISupportable.UnCol) tuple[i];
ISupportable h;
switch (col.op) {
case EQ:
h = new ISupportable.NaEqXYC(i, col.anInt, col.inc);
break;
case NQ:
h = new ISupportable.NaNqXYC(i, col.anInt, col.inc);
break;
case LE:
h = new ISupportable.NaLqXYC(i, col.anInt, col.inc);
break;
case GE:
h = new ISupportable.NaGqXYC(i, col.anInt, col.inc);
break;
default:
throw new UnsupportedOperationException();
}
t[i].remove(t[i].size() - 1);
t[i].add(h);
t[col.anInt].add(h);
}// else do nothing
}
for (int i = 0; i < t.length; i++) {
if (t[i].size() > 1) {
ISupportable.Many m = ISupportable.Many.merge(t[i].get(0), t[i].get(1));
for (int j = 2; j < t[i].size(); j++) {
m = ISupportable.Many.merge(m, t[i].get(j));
}
t[i].clear();
t[i].add(m);
}
}
return Arrays.stream(t).map(l -> l.get(0)).toArray(ISupportable[]::new);
}
/**
* Add many hybrid tuples at once
*
* @param tuples hybrid tuples
* @throws SolverException if one tuple does not the match the arity of previously declared ones.
*/
public void add(ISupportable[]... tuples) {
for (ISupportable[] tuple : tuples) {
add(tuple);
}
}
/**
* @return the current tuples as an array of expressions.
*/
public ISupportable[][] toArray() {
return hybridTuples.toArray(new ISupportable[0][0]);
}
/**
* @return the arity of the tuples, that is the number of variables it requires
*/
public int arity() {
return hybridTuples.get(0).length;
}
/**
* @return the number of tuples declared in this collection
*/
public int nbTuples() {
return hybridTuples.size();
}
//////////////////////// DSL ////////////////////////
/**
* @return an expression that indicates that no restriction exists on this column/variable
*/
public static ISupportable any() {
return new ISupportable.UnAny();
}
/**
* @param idx index of a column/variable position. The first column/variable has the index 0.
* @return refer to a specific (different) column/variable
* @implNote As is, this expression is equivalent to eq(col(idx))
*/
public static ISupportable.UnCol col(int idx) {
return new ISupportable.UnCol(idx, 0, Operator.EQ);
}
/**
* @param val an integer
* @return an expression that ensures that the column/variable is equal to the value val
*/
public static ISupportable eq(int val) {
return new ISupportable.UnEqXC(val);
}
/**
* @param val an integer
* @return an expression that ensures that the column/variable is different from
* the value val
*/
public static ISupportable ne(int val) {
return new ISupportable.UnNqXC(val);
}
/**
* @param val an integer
* @return an expression that ensures that the column/variable is greater or
* equal to the value val
*/
public static ISupportable ge(int val) {
return new ISupportable.UnGqXC(val);
}
/**
* @param val an integer
* @return an expression that ensures that the column/variable is strictly greater than
* the value val
*/
public static ISupportable gt(int val) {
return new ISupportable.UnGqXC(val + 1);
}
/**
* @param val an integer
* @return an expression that ensures that the column/variable is less than or
* equal to the value val
*/
public static ISupportable le(int val) {
return new ISupportable.UnLqXC(val);
}
/**
* @param val an integer
* @return an expression that ensures that the column/variable is strictly less than
* the value val
*/
public static ISupportable lt(int val) {
return new ISupportable.UnLqXC(val - 1);
}
/**
* @param values set of values
* @return an expression that ensures that the column/variable intersects the set values
*/
public static ISupportable in(int... values) {
IntIterableRangeSet set = new IntIterableRangeSet(values);
return new ISupportable.UnXInS(set);
}
/**
* @param values set of values
* @return an expression that ensures that the column/variable does not intersect the set values
*/
public static ISupportable nin(int... values) {
IntIterableRangeSet set = new IntIterableRangeSet(values);
set.flip();
return new ISupportable.UnXInS(set);
}
/**
* @param col a reference to a column arithmetical expression
* @param inc value to be added or subtracted to col
* @return an expression that ensures that the column/variable is equal to col + inc
*/
public static ISupportable eq(ISupportable.UnCol col, int inc) {
ISupportable.UnCol copy = col.copy();
copy.op(Operator.EQ);
copy.add(inc);
return copy;
}
/**
* @param col a reference to a column arithmetical expression
* @return an expression that ensures that the column/variable is not equal to col
*/
public static ISupportable ne(ISupportable.UnCol col) {
return ne(col, 0);
}
/**
* @param col a reference to a column arithmetical expression
* @param inc value to be added or subtracted to col
* @return an expression that ensures that the column/variable is not equal to col + inc
*/
public static ISupportable ne(ISupportable.UnCol col, int inc) {
ISupportable.UnCol copy = col.copy();
copy.op(Operator.NQ);
copy.add(inc);
return copy;
}
/**
* @param col a reference to a column arithmetical expression
* @return an expression that ensures that the column/variable is greater than
* or equal to col + inc
*/
public static ISupportable ge(ISupportable.UnCol col) {
return ge(col, 0);
}
/**
* @param col a reference to a column arithmetical expression
* @return an expression that ensures that the column/variable is greater than
* or equal to col
*/
public static ISupportable ge(ISupportable.UnCol col, int inc) {
ISupportable.UnCol copy = col.copy();
copy.op(Operator.GE);
copy.add(inc);
return copy;
}
/**
* @param col a reference to a column arithmetical expression
* @return an expression that ensures that the column/variable is greater than col
*/
public static ISupportable gt(ISupportable.UnCol col) {
return ge(col, 1);
}
/**
* @param col a reference to a column arithmetical expression
* @return an expression that ensures that the column/variable is greater than col + inc
*/
public static ISupportable gt(ISupportable.UnCol col, int inc) {
return ge(col, inc + 1);
}
/**
* @param col a reference to a column arithmetical expression
* @return an expression that ensures that the column/variable is less than
* or equal to col
*/
public static ISupportable le(ISupportable.UnCol col) {
return le(col, 0);
}
/**
* @param col a reference to a column arithmetical expression
* @return an expression that ensures that the column/variable is less than
* or equal to col + inc
*/
public static ISupportable le(ISupportable.UnCol col, int inc) {
ISupportable.UnCol copy = col.copy();
copy.op(Operator.LE);
copy.add(inc);
return copy;
}
/**
* @param col a reference to a column arithmetical expression
* @return an expression that ensures that the column/variable is less than col
*/
public static ISupportable lt(ISupportable.UnCol col) {
return le(col, -1);
}
/**
* @param col a reference to a column arithmetical expression
* @return an expression that ensures that the column/variable is less than col + inc
*/
public static ISupportable lt(ISupportable.UnCol col, int inc) {
return le(col, inc - 1);
}
}