Find the smallest alldifferent array whose sum is n - minizinc

This seems like such a simple problem, but I can't find a simple way to represent this in MiniZinc.
include "globals.mzn";
int: target;
int: max_length;
var 1..max_length: length;
array[1..length] of int: t;
constraint sum(t) = target;
constraint alldifferent(t);
solve minimize length;
This program errors with:
MiniZinc: type error: type-inst must be par set but is ``var set of int'
Is there a clean/simple way to represent this problem in MiniZinc?

Arrays in MiniZinc have a fixed size. The compiler is therefore saying that array[1..length] of int: t is not allowed, because length is a variable.
The alternative that MiniZinc offers is arrays with optional types, these are values that might exist. This means that when you write something like [t | t in 1..length], it will actually give you an array of 1..maxlength, but some elements can be marked as absent/<>.
For this particular problem you are also overlooking the fact that t should itself be a array of variables. The values of t are not yet known when at compile-time. A better way to formulate this problem would thus be to allow the values of t to be 0 when they are beyond the chosen length:
include "globals.mzn";
int: target;
int: max_length;
var 1..max_length: length;
array[1..max_length] of var int: t;
constraint sum(t) = target;
constraint alldifferent_except_0(t);
constraint forall(i in length+1..max_length) (t[i] = 0);
solve minimize length;
The next step to improve the model would be to ensure that the initial domain of t makes sense and instead of being all different, forcing an ordering would be equivalent, but eliminate some symmetry in the possible solutions.

Related

Few minizinc questions on constraints

A little bit of background. I'm trying to make a model for clustering a Design Structure Matrix(DSM). I made a draft model and have a couple of questions. Most of them are not directly related to DSM per se.
include "globals.mzn";
int: dsmSize = 7;
int: maxClusterSize = 7;
int: maxClusters = 4;
int: powcc = 2;
enum dsmElements = {A, B, C, D, E, F,G};
array[dsmElements, dsmElements] of int: dsm =
[|1,1,0,0,1,1,0
|0,1,0,1,0,0,1
|0,1,1,1,0,0,1
|0,1,1,1,1,0,1
|0,0,0,1,1,1,0
|1,0,0,0,1,1,0
|0,1,1,1,0,0,1|];
array[1..maxClusters] of var set of dsmElements: clusters;
array[1..maxClusters] of var int: clusterCard;
constraint forall(i in 1..maxClusters)(
clusterCard[i] = pow(card(clusters[i]), powcc)
);
% #1
% constraint forall(i, j in clusters where i != j)(card(i intersect j) == 0);
% #2
constraint forall(i, j in 1..maxClusters where i != j)(
card(clusters[i] intersect clusters[j]) == 0
);
% #3
% constraint all_different([i | i in clusters]);
constraint (clusters[1] union clusters[2] union clusters[3] union clusters[4]) = dsmElements;
var int: intraCost = sum(i in 1..maxClusters, j, k in clusters[i] where k != j)(
(dsm[j,k] + dsm[k,j]) * clusterCard[i]
) ;
var int: extraCost = sum(el in dsmElements,
c in clusters where card(c intersect {el}) = 0,
k,j in c)(
(dsm[j,k] + dsm[k,j]) * pow(card(dsmElements), powcc)
);
var int: TCC = trace("\(intraCost), \(extraCost)\n", intraCost+extraCost);
solve maximize TCC;
Question 1
I was under the impression, that constraints #1 and #2 are the same. However, seems like they are not. The question here is why? What is the difference?
Question 2
How can I replace constraint #2 with all_different? Does it make sense?
Question 3
Why the trace("\(intraCost), \(extraCost)\n", intraCost+extraCost); shows nothing in the output? The output I see using gecode is:
Running dsm.mzn
intraCost, extraCost
clusters = array1d(1..4, [{A, B, C, D, E, F, G}, {}, {}, {}]);
clusterCard = array1d(1..4, [49, 0, 0, 0]);
----------
<sipped to save space>
----------
clusters = array1d(1..4, [{B, C, D, G}, {A, E, F}, {}, {}]);
clusterCard = array1d(1..4, [16, 9, 0, 0]);
----------
==========
Finished in 5s 419msec
Question 4
The expression constraint (clusters[1] union clusters[2] union clusters[3] union clusters[4]) = dsmElements;, here I wanted to say that the union of all clusters should match the set of all nodes. Unfortunately, I did not find a way to make this big union more dynamic, so for now I just manually provide all clusters. Is there a way to make this expression return union of all sets from the array of sets?
Question 5
Basically, if I understand it correctly, for example from here, the Intra-cluster cost is the sum of all interactions within a cluster multiplied by the size of the cluster in some power, basically the cardinality of the set of nodes, that represents the cluster.
The Extra-cluster cost is a sum of interactions between some random element that does not belong to a cluster and all elements of that cluster multiplied by the cardinality of the whole space of nodes to some power.
The main question here is are the intraCost and extraCost I the model correct (they seem to be but still), and is there a better way to express these sums?
Thanks!
(Perhaps you would get more answers if you separate this into multiple questions.)
Question 3:
Here's an answer on the trace question:
When running the model, the trace actually shows this:
intraCost, extraCost
which is not what you expect, of course. Trace is in effect when creating the model, but at that stage there is no value of these two decision values and MiniZinc shows only the variable names. They got some values to show after the (first) solution is reached, and can then be shown in the output section.
trace is mostly used to see what's happening in loops where one can trace the (fixed) loop variables etc.
If you trace an array of decision variables then they will be represented in a different fashion, the array x will be shown as X_INTRODUCED_0_ etc.
And you can also use trace for domain reflection, e.g. using lb and ub to get the lower/upper value of the domain of a variable ("safe approximation of the bounds" as the documentation states it: https://www.minizinc.org/doc-2.5.5/en/predicates.html?highlight=ub_array). Here's an example which shows the domain of the intraCost variable:
constraint
trace("intraCost: \(lb(intraCost))..\(ub(intraCost))\n")
;
which shows
intraCost: -infinity..infinity
You can read a little more about trace here https://www.minizinc.org/doc-2.5.5/en/efficient.html?highlight=trace .
Update Answer to question 1, 2 and 4.
The constraint #1 and #2 means the same thing, i.e. that the elements in clusters should be disjoint. The #1 constraint is a little different in that it loops over decision variables while the #2 constraint use plain indices. One can guess that #2 is faster since #1 use the where i != j which must be translated to some extra constraints. (And using i < j instead should be a little faster.)
The all_different constraint states about the same and depending on the underlying solver it might be faster if it's translated to an efficient algorithm in the solver.
In the model there is also the following constraint which states that all elements must be used:
constraint (clusters[1] union clusters[2] union clusters[3] union clusters[4]) = dsmElements;
Apart from efficiency, all these constraints above can be replaced with one single constraint: partition_set which ensure that all elements in dsmElements must be used in clusters.
constraint partition_set(clusters,dsmElements);
It might be faster to also combine with the all_different constraint, but that has to be tested.

Why my array is of type var int instead of var set of int?

I have the following problem: I want to call the global constraint at_most but I got an error related to the signature
constraint forall(i in 0..w-1)(at_most(l_max, [board[i,j] | j in 0..l_max-1], 0..n));
the second argument does not match because it turns out to be var int instead of var set of int but I have previously defined board in this way:
set of int: VALUES = 0..n;
array[0..w-1,0..l_max-1] of var VALUES: board;
Just as a general message: at_most is among the list of deprecated constraints: https://www.minizinc.org/doc-2.5.5/en/lib-globals.html#deprecated-constraints.
Instead, you should use a count constraint. These constraints are more flexible and better supported by the solvers.
In this case there seems to be a misconception about what at_most does. At most only restrict the number of time a single value occurs. You are. however, giving it a full set of values.
If you are counting all the different values, then you instead can use global_cardinality_low_up. (You might also want to look at the closed version).
I think you meant to write the following constraint.
constraint forall(i in 0..w-1)(
global_cardinality_low_up([board[i,j] | j in 0..l_max-1], 0..n, [0 | i in 0..n], [l_max | i in 0..n])
);
This constraint insure that for the comprehensions the values in 0..n only occur l_max times.
Note that if you are using the comprehension to select a full row, then it would be better to use slice notation: board[i,..].

Minizinc: optimal ordering on table feature

I have a table with features = {A,B}. B is a column of integers. Scanning the table, when I have a value change in B column, I increment a variable "changes" of 1:
if data[i,B]!=data[i-1,B]
then changes=changes+1
I want to find an order that minimizes changes and at the same time keep the repetition of a value in B in [0,upper_bound].
I'm thinking to use an array as a decision variable where save the position j for the element i:
order[i]=j means i element in data is the j-th element in ordering.
How can I model with constraint? This is what I do until now:
array[1..n, Features] of int: data;
int: changes=0;
constraint
forall(i in 1..n) (
if data[i,B] != data[i-1,B] then
changes=changes+1
endif
)
;
minimize changes;
I think I'm wrong using changes as a constant variable, right? Thank you in advance.
In MiniZinc (and in constraint programming in general) you cannot increment a variable as changes=changes+1).
If changes is a variable used only for the total count of changes you can use sum instead, something like:
% ...
var 0..n: num_changes;
constraint
changes = sum([data[i,B] != data[i-1,B] | i in 2..n])
;
% ...
However, if you want to use the number of accumulated changes for each i then you have to create a changes array to collect the values for each step, e.g.
var[1..n-1] of var 0..n: changes;
% the total number of changes (to minimize)
var 0..n-1: total_changes = changes[n-1];
constraint
forall(i in 1..n-1) (
if data[i,B] != data[i-1,B] then
changes[i] = changes[i-1]+1
else
changes[i] = changes[i-1]
endif
)
;

Printing part of an array in MiniZinc

I have a MiniZinc model for wolf-goat-cabbage in which I store the locations of each entity in its own array, e.g., array[1..max] of Loc: wolf where Loc is defined as an enum: enum Loc = {left, rght}; and max is the maximum possible number of steps needed, e.g., 20..
To find a shortest plan I define a variable var 1..max: len; and constrain the end state to occur at step len.
constraint farmer[len] == left /\ wolf[len] == left /\ goat[len] == left /\ cabbage[len] == left
Then I ask for
solve minimize len
I get all the right answers.
I'd like to display the arrays from 1..len, but I can't find a way to do it. When I try, for example, to include in the output:
[ "\(wolf[n]), " | n in 1..max where n <= len ]
I get an error message saying that I can't display an array of opt string.
Is there a way to display only an initial portion of an array, where the length of the initial portion is determined by the model?
Thanks.
Did you try to fix the len variable in the output statement like n <= fix(len)?. See also What is the use of minizinc fix function?

Ambiguous use of "subscript"

I'm having an issue trying to access the nth element of a Range using subscripts. The code is super simple:
var range = 0..<9
var itemInRange = range[n] // n is some Int where 0 <= n < 9
The second line complains with the error Ambiguous use of "subscript", which I took to mean that Xcode wasn't clear what the type of the variable range is, and so it's unable to know which implementation of subscript to use. I tried to fix this by explicitly defining the type of range with
var range: Range<Int> = 0..<9
and
var firstInRange = (range as Range<Int>)[0]
but neither of these solved the problem. Is there a way to get Xcode to disambiguate the call to subscript?
You can create an array with the range and then pick an element from the array.
var range = [Int](0..<9)
var itemInRange = range[1]
From apple docs
A collection of consecutive discrete index values.
Like other collections, a range containing one element has an endIndex
that is the successor of its startIndex; and an empty range has
startIndex == endIndex.
Axiom: for any Range r, r[i] == i.
Therefore, if Element has a maximal value, it can serve as an
endIndex, but can never be contained in a Range.
It also follows from the axiom above that (-99..<100)[0] == 0. To
prevent confusion (because some expect the result to be -99), in a
context where Element is known to be an integer type, subscripting
with Element is a compile-time error:
// error: could not find an overload for 'subscript'...
print(Range(start: -99, end: 100)[0])
https://developer.apple.com/library/prerelease/mac/documentation/Swift/Reference/Swift_Range_Structure/index.html