Minzinc: Using switching between objectives dynamically - minizinc

I have a minizinc model for scheduling a project that currently optimises for total makespan.
However, sometimes the total cost of the scheduled project is more important.
I would like to:
Use a value in my .dzn (i.e. objective, 1 = makespan, 2 = costs, 3 = some other objective)
based on that variable switch between different objectives/solve commands in my .mzn
I tried to use if <bool-exp> then <exp-1> else <exp-2> endif but that doesnt seem to work for the search sequence/ objective.
Is there a way to do this?

Here are some examples what you can do.
If neither works for you, please give more context (preferably a complete MiniZinc model + data) what you try to do and the error message you get.
What I know there is no way to place the complete solve expression in a if-then-endif expression. I.e. the following is not possible
% ...
if mode = 1 then
solve :: int_search(x,first_fail, indomain_split) minimize z1
elseif mode = 2 then
solve :: int_search(y,anti_first_fail, indomain_min) maximize z2
else
solve satisfy
endif
;
First two variants which might work for simpler cases.
Approach 1
Using an variable for the optimal value (here z) to be minimized and then place the value in an if-then-else-endif clause. Note that you have to be careful to get the sign correct for the different cases. Also, the domain of z must be selected to cover the appropriate case.
% int: mode = 1;
% int: mode = 2;
% int: mode = 3;
int: mode = 4;
var 0..10: x;
var 0..10: y;
var int: z;
constraint
if mode = 1 then
z in 0..100 /\
z = x*2*y
elseif mode = 2 then
z in 0..20 /\
z = x+y
elseif mode = 3 then
z in -100..0 /\
z = -(x*2*y)
else
z in -20..0 /\
z = -(x+y)
endif
;
solve minimize z;
Approach 2
A similar idea is to move the if-then-else-endif clause to the solve part:
int: mode = 1;
% int: mode = 2;
% int: mode = 3;
% int: mode = 4;
var 0..10: x;
var 0..10: y;
solve minimize
if mode = 1 then
x*y
elseif mode = 2 then
x+y
elseif mode = 3 then
-x*y
else
-(x+y)
endif;
The drawback of this version is the there is no variable (z) where one can set a proper domain.
Approach 3
A third - and then one I would use for more complex models - is to use the MiniZinc/Python interface: https://minizinc-python.readthedocs.io/en/latest/ .

Related

Is there any way to define a variable from a formula depending on what variables are given?

Imagine you have the following formula:
a=4*b*c^2
Is there any way in Matlab to program this is in a way that the if 2 of 3 variables are provided, Matlab will solve and provide the missing one?
Because the only alternative I am seeing is using switch-case and solving the equation myself.
if isempty(a)
switchVar=1
elseif isempty(b)
switchVar=2;
else
switchVar=3;
end
switch switchVar
case 1
a=4*b*c^2;
case 2
b=a/4/c^2;
case 3
c=sqrt(a/4/b);
end
Thanky you very much in advance!
For a numeric (rather than symbolic) solution...
You can do this with some faffing around and anonymous functions. See the comments for details:
% For the target function: 0 = 4*b*c - a
% Let x = [a,b,c]
% Define the values we know about, i.e. not "c"
% You could put any values in for the known variables, and NaN for the unknown.
x0 = [5, 10, NaN];
% Define an index for the unknown, and then clear any NaNs
idx = isnan(x0);
x0(idx) = 0;
% Make sure we have 1 unknown
assert( nnz( idx ) == 1 );
% Define a function which handles which elements of "x"
% actually influence the equation
X = #(x,ii) ( x*idx(ii) + x0(ii)*(~idx(ii)) );
% Define the function to solve, 0 = 4*b*c - a = 4*x(2)*x(3)^2 - x(1) = ...
f = #(x) 4 * X(x,2) * X(x,3).^2 - X(x,1);
% Solve using fzero. Note this requires an initial guess
x0(idx) = fzero( f, 1 );
We can check these results are correct by plotting the function for a range of c values, and checking the intersection with the x-axis aligns to the output x0(3):
c = -1:0.01:1;
y = 4*x(2)*c.^2-x(1);
figure
hold on
plot(t,y);
plot(t,y.*0,'r');
plot(x0(3),0,'ok','markersize',10,'linewidth',2)
Note that there were 2 valid solutions, since this is a quadratic. The initial condition provided to fzero will largely dictate which solution is found.
Edit:
You can condense this down a bit with some tweaks to my earlier syntax:
% Define all initial conditions. This includes known variable values
% i.e. a = 5, b = 10
% As well as the initial guess for unknown variable values
% i.e. c = 1 (maybe? ish?)
x0 = [5, 10, 1];
% Specify the index of the unknown variable in x
idx = 3;
% Define the helper function which handles the influence of each variable
X = #(x,ii) x*(ii==idx) + x0(ii)*(ii~=idx);
% Define the function to solve, as before
f = #(x) 4 * X(x,2) * X(x,3).^2 - X(x,1);
% Solve
x0(idx) = fzero( f, x0(idx) )
This approach has the benefit that you can just change idx (and re-run the definition steps for X and f) to switch the variable of choice!
First, specify the given known variables
Then rewrite the equation as 0 = 4*b*c - a
Finally use solve to find the missing value
Code is as follows
syms a b c
% define known variable
a = 2; c = 5;
% equation rewritten
f = 4*b*c^2 - a == 0;
missing_value = solve(f);

Implementing Simplex Method infinite loop

I am trying to implement a simplex algorithm following the rules I was given at my optimization course. The problem is
min c'*x s.t.
Ax = b
x >= 0
All vectors are assumes to be columns, ' denotes the transpose. The algorithm should also return the solution to dual LP. The rules to follow are:
Here, A_J denotes columns from A with indices in J and x_J, x_K denotes elements of vector x with indices in J or K respectively. Vector a_s is column s of matrix A.
Now I do not understand how this algorithm takes care of condition x >= 0, but I decided to give it a try and follow it step by step. I used Matlab for this and got the following code.
X = zeros(n, 1);
Y = zeros(m, 1);
% i. Choose starting basis J and K = {1,2,...,n} \ J
J = [4 5 6] % for our problem
K = setdiff(1:n, J)
% this while is for goto
while 1
% ii. Solve system A_J*\bar{x}_J = b.
xbar = A(:,J) \ b
% iii. Calculate value of criterion function with respect to current x_J.
fval = c(J)' * xbar
% iv. Calculate dual solution y from A_J^T*y = c_J.
y = A(:,J)' \ c(J)
% v. Calculate \bar{c}^T = c_K^T - u^T A_K. If \bar{c}^T >= 0, we have
% found the optimal solution. If not, select the smallest s \in K, such
% that c_s < 0. Variable x_s enters basis.
cbar = c(K)' - c(J)' * inv(A(:,J)) * A(:,K)
cbar = cbar'
tmp = findnegative(cbar)
if tmp == -1 % we have found the optimal solution since cbar >= 0
X(J) = xbar;
Y = y;
FVAL = fval;
return
end
s = findnegative(c, K) %x_s enters basis
% vi. Solve system A_J*\bar{a} = a_s. If \bar{a} <= 0, then the problem is
% unbounded.
abar = A(:,J) \ A(:,s)
if findpositive(abar) == -1 % we failed to find positive number
disp('The problem is unbounded.')
return;
end
% vii. Calculate v = \bar{x}_J / \bar{a} and find the smallest rho \in J,
% such that v_rho > 0. Variable x_rho exits basis.
v = xbar ./ abar
rho = J(findpositive(v))
% viii. Update J and K and goto ii.
J = setdiff(J, rho)
J = union(J, s)
K = setdiff(K, s)
K = union(K, rho)
end
Functions findpositive(x) and findnegative(x, S) return the first index of positive or negative value in x. S is the set of indices, over which we look at. If S is omitted, whole vector is checked. Semicolons are omitted for debugging purposes.
The problem I tested this code on is
c = [-3 -1 -3 zeros(1,3)];
A = [2 1 1; 1 2 3; 2 2 1];
A = [A eye(3)];
b = [2; 5; 6];
The reason for zeros(1,3) and eye(3) is that the problem is inequalities and we need slack variables. I have set starting basis to [4 5 6] because the notes say that starting basis should be set to slack variables.
Now, what happens during execution is that on first run of while, variable with index 1 enters basis (in Matlab, indices go from 1 on) and 4 exits it and that is reasonable. On the second run, 2 enters the basis (since it is the smallest index such that c(idx) < 0 and 1 leaves it. But now on the next iteration, 1 enters basis again and I understand why it enters, because it is the smallest index, such that c(idx) < 0. But here the looping starts. I assume that should not have happened, but following the rules I cannot see how to prevent this.
I guess that there has to be something wrong with my interpretation of the notes but I just cannot see where I am wrong. I also remember that when we solved LP on the paper, we were updating our subjective function on each go, since when a variable entered basis, we removed it from the subjective function and expressed that variable in subj. function with the expression from one of the equalities, but I assume that is different algorithm.
Any remarks or help will be highly appreciated.
The problem has been solved. Turned out that the point 7 in the notes was wrong. Instead, point 7 should be

Optimization with Minizinc - Only print optimal solutions

at the moment I'm having a closer look on Minizinc.
Minizinc is showing all valid solutions of my model in the output window when solving the model. I'm a bit confused because I did not ask minizinc to solve the model as a satisfaction problem.
Is there a possibility that only optimal solutions are shown?
Thanks for your answers.
best regards
What is your solve statement? If it's solve satisfy then you ask for all solutions. If it's solve minimize x or solve maximize x then the solver will show the progression of optimal solutions, with the optimal solution is shown last.
With OptiMathSAT version 1.5.1, one can now print all same-cost optimal solutions of a given FlatZinc formula using option
-opt.fzn.all_solutions=[BOOL]
e.g. take the following test.fzn file
var 0..3: x ::output_var;
var 0..3: y ::output_var;
var bool: r1 ::output_var;
var bool: r2 ::output_var;
constraint int_lin_le_reif([1, 1, -1], [x, y, 4], 0, r1);
constraint int_lin_le_reif([1, 1, -1], [x, y, 2], 0, r2);
constraint bool_or(r1, r2, true);
solve maximize x;
and solve it as follows:
~$ ../bin/optimathsat -input=fzn -opt.fzn.all_solutions=True < test.fzn
% allsat model
x = 3;
y = 0;
r1 = true;
r2 = false;
----------
% allsat model
x = 3;
y = 1;
r1 = true;
r2 = false;
----------
=========
The maximum value for x is 3, hence the solver prints all-and-only those models of test.fzn for which x is equal to 3.
Naturally, as mentioned by #hakank in his answer, one might be interested in the progression of solutions towards the optimal one. This can also be done in OptiMathSAT using the option
-opt.fzn.partial_solutions=[BOOL]
On the example above, that would yield
~$ ../bin/optimathsat -input=fzn -opt.fzn.partial_solutions=True < test.fzn
% objective: x (model)
x = 2;
y = 0;
r1 = true;
r2 = true;
----------
% objective: x (model)
x = 3;
y = 0;
r1 = true;
r2 = false;
----------
% objective: x (optimal model)
x = 3;
y = 0;
r1 = true;
r2 = false;
----------
=========
Here the optimization search finds two different models: an initial one in which x is equal to 2, and an optimal one in which x is equal to 3. The latter model gets printed twice: the first time as soon as it is found, the second time when the solver is able to prove that it is actually the optimal solution of the problem.

Avoid for-loop to increase performance

Is it possible to obtain the matrix A in a more efficient way than using a for loop?
a = 6; % constant
b = 2; % constant
s = 0.1; % possible to change
I = 12; % possible to change
A = zeros(a,I+1);
A(:,1) = rand(a,1); % some initial value
B = rand(b,I);
% possible to avoid for-loop to increase performance?
for i = 1:I
A(:,i+1) = fun(A(:,i),B(:,i), a, s);
end
The function fun is given as
function [AOut] = fun(AIn, B, a, s)
AOut = zeros(a,1);
AOut(1) = AIn(1) + AIn(4)*s*cos(AIn(3));
AOut(2) = AIn(2) + AIn(4)*s*sin(AIn(3));
AOut(3) = AIn(3) + AIn(4)*AIn(6)*s;
AOut(4) = AIn(4) + AIn(5)*s;
AOut(5) = AIn(5) + B(1);
AOut(6) = AIn(6) + B(2);
end
i dont think you can optimize the loop in regards to effiency as the last values are needed for calculating the next. #Tony Tannous showed you a nice way how to get rid of the loop in your code. For better performance at high values of I you can change fun() to:
function [AOut] = fun2(AIn, B, a, s)
AOut=AIn+ [AIn(4)*s*cos(AIn(3)) ;...
AIn(4)*s*sin(AIn(3)) ;...
AIn(4)*AIn(6)*s ;...
AIn(5)*s ; ...
B(1) ; ...
B(2)];
end
First of all, if a != n
You will get an error:
Subscripted assignment dimension mismatch.
So you must be cautious
And, to get rid of the loop you can do this:
EDIT:
Apparently i isn't getting modified on the right side. I'll try to fix it and reedit my answer.
anyway, to get rid of the loop you can still use A(:, i:1:I)
i = 1;
A(:, i:1:I) = fun(A(:,i),B(:,i), a, s);
If you have any further question, please ask!

Counting Number of Specific Outputs of a Function

If I have a matrix and I want to apply a function to each row of the matrix. This function has three possible outputs, either x = 0, x = 1, or x > 0. There's a couple things I'm running into trouble with...
1) The cases that output x = 1 or x > 0 are different and I'm not sure how to differentiate between the two when writing my script.
2) My function isn't counting correctly? I think this might be a problem with how I have my loop set up?
This is what I've come up with. Logically, I feel like this should work (except for the hiccup w/ the first problem I've stated)
[m n] = size(matrix);
a = 0; b = 0; c = 0;
for i = 1 : m
x(i) = function(matrix(m,:));
if x > 0
a = a + 1;
end
if x == 0
b = b + 1;
end
if x == 1
c = c + 1;
end
end
First you probably have an error in line 4. It probably should be i instead of m.
x(i) = function(matrix(i,:));
You can calculate a, b and c out of the loop:
a = sum(x>0);
b = sum(x==0);
c = sum(x==1);
If you want to distinguish x==1 and x>0 then may be with sum(xor(x==1,x>0)).
Also you may have problem with precision error when comparing double values with 0 and 1.