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

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,..].

Related

infinite value while using cumulative constraint

I am stuck with a cumulative constraint I seem not to use properly, I seek for help ! :)
I have tasks with precedence and, for some of them, required & forbidden resources.
I need to decide when to start a task & who to assign to it.
To do so, I'm using an array of decision variable resource_allocation:
array[Tasks, Resources] of var 0..1: resource_allocation; %Selection of resources per task.
To manage the required/forbidden resources, I used the following:
% Mandatory & Forbidden constraint allocation
constraint forall(t in Tasks, r in resource_required[t])(resource_allocation[t,r]=1);
constraint forall(t in Tasks, r in resource_forbidden[t])(resource_allocation[t,r]=0);
resource_required being set of int storing the resources number that are required/forbidden.
Each resource represents 1 worker, and each worker can only perform one task at a time, so I am trying to state that, for every resources, the cumsum of allocation can at max be 1.
It might be important to note that start is also a decision variable.
% Constraint allocated to only one task at a time
constraint forall(t in Resources)(
cumulative(start, duration, [resource_allocation[t, r] | t in Tasks], 1)
);
Doing so, I always end up with the following error
JC:70.12-82
in call 'cumulative'
cumulative:21-3.49-7
in binary '/\' operator expression
cumulative:25-3.49-7
in if-then-else expression
cumulative:26-5.48-9
in binary '/\' operator expression
cumulative:30-5.48-9
in if-then-else expression
cumulative:47.7-32
in call 'fzn_cumulative'
fzn_cumulative:4-9.20-17
in let expression
fzn_cumulative:8-13.20-17
in if-then-else expression
fzn_cumulative:10-17.19-17
in let expression
fzn_cumulative:12.21-74
in variable declaration for 'late'
in call 'max'
with i = <expression>
MiniZinc: evaluation error: arithmetic operation on infinite value
Process finished with non-zero exit code 1.
I need a little guidance, I looked in the source code of fzn_cumulative, but I don't get what is going on.
Thanks !
You might consider to limit the domains of your decisions variables.
int is unlimited (disregarding the limited number of bits per int) and may lead to overflow situations or complaints about infinite values.
include "globals.mzn";
set of int: Tasks = 1..3;
set of int: Resources = 1..3;
set of int: Durations = 1..10;
set of int: Times = 1..1000;
% Use this editor as a MiniZinc scratch book
array[Tasks, Resources] of var 0..1: resource_allocation; %Selection of resources per task.
array [Tasks] of var Times: start;
array [Tasks] of var Durations: duration;
array [Tasks] of set of Resources: resource_required = [{1,2},{2,3},{3}];
array [Tasks] of set of Resources: resource_forbidden = [{},{1},{1}];
% Mandatory & Forbidden constraint allocation
constraint forall(t in Tasks, r in resource_required[t])(resource_allocation[t,r]=1);
constraint forall(t in Tasks, r in resource_forbidden[t])(resource_allocation[t,r]=0);
% Constraint allocated to only one task at a time
% Changed "t in Resources" to "r in Resources"
constraint forall(r in Resources)(
cumulative(start, duration, [resource_allocation[t, r] | t in Tasks], 1)
);

Find the smallest alldifferent array whose sum is n

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.

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
)
;

Overriding `Comparison method violates its general contract` exception

I have a comparator like this:
lazy val seq = mapping.toSeq.sortWith { case ((_, set1), (_, set2)) =>
// Just propose all the most connected nodes first to the users
// But also allow less connected nodes to pop out sometimes
val popOutChance = random.nextDouble <= 0.1D && set2.size > 5
if (popOutChance) set1.size < set2.size else set1.size > set2.size
}
It is my intention to compare sets sizes such that smaller sets may appear higher in a sorted list with 10% chance.
But compiler does not let me do that and throws an Exception: java.lang.IllegalArgumentException: Comparison method violates its general contract! once I try to use it in runtime. How can I override it?
I think the problem here is that, every time two elements are compared, the outcome is random, thus violating the transitive property required of a comparator function in any sorting algorithm.
For example, let's say that some instance a compares as less than b, and then b compares as less than c. These results should imply that a compares as less than c. However, since your comparisons are stochastic, you can't guarantee that outcome. In fact, you can't even guarantee that a will be less than b next time they're compared.
So don't do that. No sort algorithm can handle it. (Such an approach also violates the referential transparency principle of functional programming and will make your program much harder to reason about.)
Instead, what you need to do is to decorate your map's members with a randomly assigned weighting - before attempting to sort them - so that they can be sorted consistently. However, since this happens at the start of a sort operation, the result of the sort will be different each time, which I think is what you're looking for.
It's not clear what type mapping has in your example, but it appears to be something like: Map[Any, Set[_]]. (You can replace the types as required - it's not that important to this approach. For example, say mapping actually has the type Map[String, Set[SomeClass]], then you would replace references below to Any with String and Set[_] to Set[SomeClass].)
First, we'll create a case class that we'll use to score and compare the map elements. Then we'll map the contents of mapping to a sequence of elements of this case class. Next, we sort those elements. Finally, we extract the tuple from the decorated class. The result should look something like this:
final case class Decorated(x: (Any, Set[_]), rand: Double = random.nextDouble)
extends Ordered[Decorated] {
// Calculate a rank for this element. You'll need to change this to suit your precise
// requirements. Here, if rand is less than 0.1 (a 10% chance), I'm adding 5 to the size;
// otherwise, I'll report the actual size. This allows transitive comparisons, since
// rand doesn't change once defined. Values are negated so bigger sets come to the fore
// when sorted.
private def rank: Int = {
if(rand < 0.1) -(x._2.size + 5)
else -x._2.size
}
// Compare this element with another, by their ranks.
override def compare(that: Decorated): Int = rank.compare(that.rank)
}
// Now sort your mapping elements as follows and convert back to tuples.
lazy val seq = mapping.map(x => Decorated(x)).toSeq.sorted.map(_.x)
This should put the elements with larger sets towards the front, but there's 10% chance that sets appear 5 bigger and so move up the list. The result will be different each time the last line is re-executed, since map will create new random values for each element. However, during sorting, the ranks will be fixed and will not change.
(Note that I'm setting the rank to a negative value. The Ordered[T] trait sorts elements in ascending order, so that - if we sorted purely by set size - smaller sets would come before larger sets. By negating the rank value, sorting will put larger sets before smaller sets. If you don't want this behavior, remove the negations.)

Assigning times to events

include "globals.mzn";
%Data
time_ID = [11,12,13,14,15];
eventId = [0011, 0012, 0013, 0021, 0022, 0031, 0041, 0051, 0061, 0071];
int:ntime = 5;
int:nevent = 10;
set of int: events =1..nevent;
set of int: time = 1..ntime;
array[1..nevent] of int:eventId;
array[1..nevent] of var time:event_time;
array[1..ntime] of int:time_ID;
solve satisfy;
constraint
forall(event in eventId)(
exists(t in time_ID)(
event_time[event] = t ));
output[ show(event_time) ];
I'm trying to assign times to an event using the code above.
But rather than randomly assign times to the events, it returns an error " array access out of bounds"
How can I make it select randomly from the time array?
Thank you
The error was because you tried to assign the index 11 (the first element in eventId array) in "event_time" array.
The assigment of just 1's is correct since you haven't done any other constraints on the "event_time" array. If you set the number of solutions to - say - 3 you will see other solutions. And, in fact, the constraint as it stand now is not really meaningful since it just ensures that there is some assignment to the elements in "event_time", but this constraint is handled by the domain of "event_time" (i.e. that all indices are in the range 1..ntime).