JuMP - Having both Heuristic and LazyConstraints Callbacks on integer nodes - callback

I am sorry, this time I could not produce a MWE. I tried but could not.
In JuMP Julia, I wanted to know if it is possible to get both Heuristic and LazyConstraints Callbacks on integer nodes. On the following NOT MWE code:
function call_back_benders_two_opt(cb_data)
status = callback_node_status(cb_data, m)
if status != MOI.CALLBACK_NODE_STATUS_FRACTIONAL
#show status
end
if status == MOI.CALLBACK_NODE_STATUS_INTEGER
x_cb = zeros(Bool, n, n)
for i in 1:n
for j in i+1:n
x_cb[i,j] = Bool(round(callback_value(cb_data, x[i, j])))
end
end
if sum(x_cb) > 0
ŷ = [Bool(round(callback_value(cb_data, y[i,i]))) for i in 1:n]
ring_edges = create_ring_edges_lazy(cb_data, x, n)
is_valid = validate_solution_lazy(ring_edges, ŷ, n)
if is_valid
x′, y_sp, sp_cost = compute_sp_res(x_cb, ŷ, V, n, tildeV, r, s)
hubs, master_cost = compute_master_res(x_cb, ŷ, V, n, tildeV, o, r)
x_opt = run_two_opt_wiki(x_cb, x′, hubs, sp_cost+master_cost, pars, n, r, rp, tildeV)[1]
for i in V
status = MOI.submit(
m, MOI.HeuristicSolution(cb_data), [x[i,j] for j in i+1:n], [x_opt[i,j] for j in i+1:n]
)
end
println("I submitted a heuristic solution, and the status was: ", status)
end
end
end
MOI.set(m, MOI.HeuristicCallback(), call_back_benders_two_opt)
MOI.set(m, MOI.LazyConstraintCallback(), call_back_benders)
My problem is that the Heuristic Callback is only called on Fractional nodes. Indeed line #show status never prints meaning that status is always MOI.CALLBACK_NODE_STATUS_FRACTIONAL while I wanted to call my HeuristicCallback on Integer nodes. Please note that on Integer Nodes, I do successfully add Lazy Constraints. So my concern is having both Heuristic Callback AND Lazy Constraints called on integer nodes. Is there something I am missing?
Please note that I use both Heuristic Callbacks and Lazy Constraints from this Julia Doc page.
EDIT: Thanks to #mattmilten's answer, I am editing my post. I then tried to put the Heuristic Callbacks inside the Lazy Constraints Callback as you can not have two callbacks in JuMP.
function call_back_benders(cb_data)
ŷ = [Bool(round(callback_value(cb_data, y[i,i]))) for i in 1:n]
start_time_sp = time()
for i in 1:n
for j in i+1:n
x̂[i,j] = Bool(round(callback_value(cb_data, x[i, j])))
end
end
ring_edges = create_ring_edges_lazy(cb_data, x, n)
is_valid = validate_solution_lazy(ring_edges, ŷ, n)
if !is_valid
nsubtour_cons = create_subtour_constraint_lazy(m, cb_data, x, y, n, ring_edges, nsubtour_cons)
else
λ0 = callback_value(cb_data, λ)
start_time_sp = time()
if pars.sp_solve == "poly"
λ_val, α, γ, β, δ = sp_optimize_poly(ŷ, x̂, V, tildeV, rp, s, pars.sp_solve)
else
λ_val, α, γ, β, δ = sp_optimize_ilp_dual(ŷ, x̂, V, tildeV, rp, s, pars.log_level, gurobi_env)
end
sp_time += time() - start_time_sp
if λ0 < λ_val
RHS = sum([2(1-y[i,i])α[i] for i in V]) +
sum([(x[mima(β[j][1],j)] + x[mima(β[j][2],j)] - 1)rp[mima(β[j][1],β[j][2])]
for j in tildeV if length(β[j]) > 0]) -
sum([y[j,j]γ[i,j] for i in V, j in V if i != j])
pars.log_level > 1 && #info "Optimality cut found"
nopt_cons += 1
con = #build_constraint(λ ≥ RHS)
MOI.submit(m, MOI.LazyConstraint(cb_data), con)
################ 2-opt
status = callback_node_status(cb_data, m)
if status != MOI.CALLBACK_NODE_STATUS_FRACTIONAL
#show status
end
if status == MOI.CALLBACK_NODE_STATUS_INTEGER
if sum(x̂) > 0
x′, y_sp, sp_cost = compute_sp_res(x̂, ŷ, V, n, tildeV, r, s)
hubs, master_cost = compute_master_res(x̂, ŷ, V, n, tildeV, o, r)
x_opt = run_two_opt_wiki(x̂, x′, hubs, sp_cost+master_cost, pars, n, r, rp, tildeV)[1]
for i in V
status = MOI.submit(
m, MOI.HeuristicSolution(cb_data), [x[i,j] for j in i+1:n], [x_opt[i,j] for j in i+1:n]
)
end
println("I submitted a heuristic solution, and the status was: ", status)
end
end
end
end
MOI.set(m, MOI.LazyConstraintCallback(), call_back_benders)
However, Julia tells me this is an invalid Callback Usage:
ERROR: InvalidCallbackUsage: Cannot submit MathOptInterface.HeuristicSolution{Gurobi.CallbackData}(Gurobi.CallbackData( sense : minimize
number of variables = 14952
number of linear constraints = 103
number of quadratic constraints = 0
number of sos constraints = 0
number of non-zero coeffs = 14952
number of non-zero qp objective terms = 0
number of non-zero qp constraint terms = 0
, Ptr{Nothing} #0x000055755c429d40, 4)) inside a
MathOptInterface.LazyConstraintCallback().
Does it mean I can not use solver-independent callback from JuMP?

In Gurobi, only one callback function can be passed to the solver. Inside this function, you can define multiple actions for the different scenarios by separating them via the where argument, as demonstrated in this Python example:
def mycallback(model, where):
if where == GRB.Callback.PRESOLVE:
# Presolve callback
cdels = model.cbGet(GRB.Callback.PRE_COLDEL)
rdels = model.cbGet(GRB.Callback.PRE_ROWDEL)
if cdels or rdels:
print(f"{cdel} columns and {rdel} rows are removed")
elif where == GRB.Callback.SIMPLEX:
# Simplex callback
itcnt = model.cbGet(GRB.Callback.SPX_ITRCNT)
print(f"{itcnt} simplex iterations")
elif where == GRB.Callback.MIP:
# General MIP callback
pass
elif where == GRB.Callback.MIPSOL:
# MIP solution callback
pass
elif where == GRB.Callback.MIPNODE:
# MIP node callback
pass
This should also be possible with JuMP but you need to define the callbacks yourself and combine them into a single function. The JuMP documentation explicitly warns that only one callback can be defined using the set function:
You can only set each callback once. Calling set twice will over-write the earlier callback. In addition, if you use a solver-independent callback, you cannot set a solver-dependent callback.

Related

Append in Julia

I am new to Julia. A was curious how do I append values. I want b to grow every time the boolian value is True. And then to output its size.
function f(img_deformed, img)
s = size(img)[1]
for i in range(1,s,step=1)
for j in range(1,s,step=1)
b = img_deformed[i,j] == img[i,j]
end
end
return b
end
If you want b to be a vector that tracks the number of times that the equality in your for loop is satisfied, you can use push!:
function f(img_deformed, img)
s = size(img)[1]
b = Vector{Bool}() # Can also use Bool[]
for i in range(1,s,step=1)
for j in range(1,s,step=1)
if img_deformed[i,j] == img[i,j]
push!(b, true)
end
end
end
return length(b)
end
However, if all you really care about is the number of trues, it's easier (and almost certainly better) to just use b as a counter:
function f(img_deformed, img)
s = size(img)[1]
b = 0
for i in range(1,s,step=1)
for j in range(1,s,step=1)
if img_deformed[i,j] == img[i,j]
b += 1
end
end
end
return b
end
Some minor style points: s = size(img)[1] is equivalent to s = size(img, 1), and the range(1, s, step=1) is equivalent to 1:s, so your code could be written slightly more simply as
function f(img_deformed, img)
s = size(img, 1)
b = 0
r = 1:s
for i in r
for j in r
if img_deformed[i,j] == img[i,j]
b += 1
end
end
end
return b
end
However, that doesn't address a potential mistake in the original code: unless you know that img will always be a square matrix, using the same range (1:s) for both for loops is not guaranteed to be correct. To avoid this problem, you can use axes:
function f(img_deformed, img)
b = 0
for j in axes(img, 2)
for i in axes(img, 1)
if img_deformed[i,j] == img[i,j]
b += 1
end
end
end
return b
end
Notice here that I've chosen to loop over the columns first; this is a good practice in Julia, since arrays are stored in column-major order (see the explanation here from the manual).
Note also that using img to control the values that we loop over implicitly assumes that size(img) == size(img_deformed). Without knowing more about what this function is intended for, it's hard to suggest how to deal with that, but if you can assume that the two matrices should be the same size, you can add a check at the top of f(), e.g.
function f(img_deformed, img)
#assert size(img) == size(img_deformed)
# rest of code is the same
end

Finding smallest value for parameterised answer that satisfies condition

I want to find the smallest integer l that satisfies l^2 >= x, and mod(l,2)=0.
In the following example x=75, and hence l=10, since the previous even number doesn't fulfil the inequation: 8^2 <= 75 <= 10^2
I have tried this (ignoring the even-number requirement, which I can't to work):
syms l integer
eqn1 = l^2 >= 75;
% eqn2 = mod(l,2) == 0;
[sol_l, param, cond] = solve(eqn1, l, 'ReturnConditions', true);
But this does not give me anything helpful directly:
sol_l =
k
param =
k
cond =
(75^(1/2) <= k | k <= -75^(1/2)) & in(k, 'integer')
I would like to evaluate the conditions on the parameter and find the smallest value that satisfies the conditions.
Also, I would like to enforce the mod(l,2)=0 condition somehow, but I don't seem to get that work.
Using the solve for this task is like using a cannon to kill a mosquito. Actually, the answer of Lidia Parrilla is good and fast, although it can be simplified as follows:
l = ceil(sqrt(x));
if (mod(x,2) ~= 0)
l = l + 1;
end
% if x = 75, then l = 10
But I would like to point out something that no one else noticed. The condition provided by the solve function for l^2 >= 75 is:
75^(1/2) <= k | k <= -75^(1/2)
and it's absolutely correct. Since l is being raised to the power of 2, and since a negative number raised to the power of 2 produces a positive number, the equation will always have two distinct solutions: a negative one and a positive one.
For x = 75, the solutions will be l = 10 and l = -10. So, if you want to find the smallest number (and a negative number is always smaller than a positive one), the right solution will be:
l = ceil(sqrt(x));
if (mod(x,2) ~= 0)
l = l + 1;
end
l = l * -1;
If you want to return both solutions, the result will be:
l_pos = ceil(sqrt(x));
if (mod(x,2) ~= 0)
l_pos = l_pos + 1;
end
l_neg = l_pos * -1;
l = [l_neg l_pos];
I guess the easiest solution without employing the inequality and solve function would be to find the exact solution to your equation l^2 >= x, and then finding the next even integer. The code would look like this:
x = 75;
y = ceil(sqrt(x)); %Ceil finds the next bigger integer
if(~mod(y,2)) %If it's even, we got the solution
sol = y;
else %If not, get the next integer
sol = y+1;
end
The previous code gives the correct solution to the provided example (x = 75; sol = 10)

Fibonacci function not accepting 0 and not displaying the last term only

I wrote a function that displays the Fibonacci sequence up to the nth term. The code runs fine, but I want to make two changes and am not sure how to do so.
Here is my code:
function [ F ] = get_fib( k )
F(1) = 1;
F(2) = 1;
i = 3;
while k >= i;
F(i) = F(i-1) + F(i-2);
i = i + 1;
end
end
The first problem is that the code does not accept 0 as an input. I tried changing the function to:
function [ F ] = get_fib( k )
F(0) = 0;
F(1) = 1;
F(2) = 1;
i = 3;
while k >= i;
F(i) = F(i-1) + F(i-2);
i = i + 1;
end
end
But the following error appears:
Attempted to access F(0); index must be a positive integer or logical.
Error in get_fib (line 2)
F(0) = 0;
I would also like the code to display the last term in the sequence, rather than the entire sequence.
I changed the function to:
function [ F ] = get_fib( k );
F(1) = 1;
F(2) = 1;
i = 3;
while k >= i;
F(i) = F(i-1) + F(i-2);
i = i + 1;
end
term = F(k)
end
but the sequence it still being assigned to ans.
How can I make my function accept 0 as an argument and only display the last term of the sequence?
First let's get your function to output just the last item in the sequence. You're setting term like this:
term = F(k);
Which is good (notice I added the ; at the end, though). But the return value from your function is F. You need to change it to term.
function [ term ] = get_fib( k )
%// ^^^^- change this ^-- semicolon not necessary here
Now, to handle an input of 0, you can add a special check for zero:
function [ term ] = get_fib( k )
if k == 0
term = [];
return;
end
while k >= i
%// ^-- semicolon not needed here, either
<the rest of your code>
The semicolons after the function header and the while statement don't hurt anything, they just represent empty statements. But they might be misleading, so it's best to remove them.
The semicolon after the assignment to term prevents the ans = ... line from printing out to the console.
To address your first problem, F(0) is not a valid call. This is because MATLAB indexing starts from 1. In other words, the first element of a matrix is index 1. There is no 0th index of a matrix in MATLAB. See here for why MATLAB indexing starts from 1. What I would recommend to address this is to shift your output array by one index.
Thus, your code for the function should be:
function [ F ] = get_fib( k )
k = k + 1
F(1) = 0; % 0th Fibonacci term
F(2) = 1; % 1st Fibonacci term
F(3) = 1; % 2nd Fibonacci term
i = 4;
while k >= i
F(i) = F(i-1) + F(i-2);
i = i + 1;
end
term = F(k)
end
To address your second problem, it would depend on what you want. Do you (1) want the last term in the sequence to be returned when you call term = get_fib(k)or (2) want the last term of the sequence to be displayed and the entire sequence to be returned?
To achieve (1), fix the top line of your code to function term = F(k). To achieve (2), call the function with F = get_fib(some_number), as #rayryeng stated.
Since others already pointed out how to fix your code, I'd like to show you one approach to calculate the nth Fibonacci number without relying on the F(n-1) and F(n-2) terms calculation.
It involves the golden ratio, and you can read more about its relationship to the Fibonacci sequence here.
function [ F ] = get_fib( n )
% Changed your input variable from k to n (standard notation)
Phi = (1+sqrt(5))/2; % Golden ratio value
F = round((Phi^n - ((-1)^n)/(Phi^n))/sqrt(5)); %nth fibonacci number
end
Since you are only interested in the last value of the sequence, it could speed up the calculation for large values of n.
Note i have rounded the output (F) to avoid floating point arithmetic errors.

Plotting own function in scilab

Hey i have an issuse with plotting my own function in scilab.
I want to plot the following function
function f = test(n)
if n < 0 then
f(n) = 0;
elseif n <= 1 & n >= 0 then
f(n) = sin((%pi * n)/2);
else
f(n) = 1;
end
endfunction
followed by the the console command
x = [-2:0.1:2];
plot(x, test(x));
i loaded the function and get the following error
!--error 21
Invalid Index.
at line 7 of function lala called by :
plot(x, test(x))
Can you please tell me how i can fix this
So i now did it with a for loop. I don't think it is the best solution but i can't get the other ones running atm...
function f = test(n)
f = zeros(size(n));
t = length(n);
for i = 1:t
if n(i) < 0 then
f(i) = 0;
elseif n(i) <= 1 & n(i) >= 0
f(i) = sin((%pi * n(i)/2));
elseif n(i) > 1 then
f(i) = 1;
end
end
endfunction
I guess i need to find a source about this issue and get used with the features and perks matlab/scilab have to over :)
Thanks for the help tho
The original sin is
function f = test(n)
(...)
f(n) = (...)
(...)
endfunction
f is supposed to be the result of the function. Therefore, f(n) is not "the value that the function test takes on argument n", but "the n-th element of f". Scilab then handles this however it can; on your test case, it tries to access a non-integer index, which results in an error. Your loop solution solves the problem.
Replacing all three f(n) by f in your first formulation makes it into something that works... as long as the argument is a scalar (not an array).
If you want test to be able to accept vector arguments without making a loop, the problem is that n < 0 is a vector of the same size as n. My solution would use logical arrays for indexing each of the three conditions:
function f = test(n)
f = zeros(size(n));
negative = (n<0);//parentheses are optional, but I like them for readability
greater_than_1 = (n>1);
others = ~negative & ~greater_than_1;
f(isnegative)=0;
f(greater_than_1)=1;
f(others) = sin(%pi/2*n(others));
endfunction

How to show all the midpoints on my bisection code?

I have a code for finding the bisection (and it finally works!), but I need to include 3 more things:
output- Root History a vector containing the sequence of midpoints obtained by the algorithm
output- the absolute value of the function
f(x) at r, i.e., fRoot = f(r) input- max iterations
function [R, E] = myBisection(f, a, b, tol)
m = (a + b)/2;
R = m;
E = abs(f(m));
while E(end) > tol
if sign(f(a)) == sign(f(m))
a = m;
else
b = m;
end
m = (a + b)/2;
R = [R, m];
E = [E, abs(f(m))];
end
how do I do this? thanks!!
I have corrected indents an you can see that you've left out end from the end of the function. (it is optional but best not to leave those things out so you know you did not mean to write couple lines to the end but you forgot it.)
R and E should be returned now, if you call myBisection apropriately, that is
[R, E] = myBisection(f, a, b, tol);
If you just call
myBisection(f, a, b, tol)
it will only return R.
To add a limit on the number of iterations, you change while's condition like so:
iter=0;
while (E(end) > tol) && (iter<max_iter)
iter = iter+1;
% ...
end
or it is better to do it in a for loop, with an if plus break:
for iter=1:max_iter
if(E(end) <= tol), break, end;
% ...
end