Can the CP solver be initialised at a specific point? - python

I am using the CP-Sat solver to optimise a timetable I am making. However, this now takes a long time to solve. Is it possible to seed the solver with an old result, to act as a starting point, with the goal of reducing the time required to find the optimal result?

Take a look at this solution hinting example:
https://github.com/google/or-tools/blob/stable/ortools/sat/docs/model.md#solution-hinting
num_vals = 3
x = model.NewIntVar(0, num_vals - 1, 'x')
y = model.NewIntVar(0, num_vals - 1, 'y')
z = model.NewIntVar(0, num_vals - 1, 'z')
model.Add(x != y)
model.Maximize(x + 2 * y + 3 * z)
# Solution hinting: x <- 1, y <- 2
model.AddHint(x, 1)
model.AddHint(y, 2)
Edit: you should also try to
Reduce the amount of variables.
Reduce the domain of the integer variables.
Run the solver with multiples threads usingsolver.parameters.num_search_workers = 8.
Prefer boolean over integer variables/contraints.
Set redundant constraints and/or symmetry breaking constraints.
Segregate your problem and merge the results.

Related

Z3 Not Showing Infinity on Unbounded Optimization in Python

I am new to Z3 and trying the examples found here, implementing the examples in python. When I try the examples in the "Unbounded Objectives" section I get seemingly random integer values (not 'oo'). For the following code:
x, y = Ints('x y')
opt = Optimize()
opt.add(x < 2)
opt.add((y - x) > 1)
opt.maximize(x + y)
print(opt.check())
print(opt.model())
I get the output:
sat
[y = 5, x = 1]
But the problem is unbounded, I expect it to give me y equal to infinity. A more simple example:
x, y, profit = Ints('x y profit')
opt = Optimize()
opt.add(profit == 2*x + y)
opt.maximize(profit)
print(opt.check())
print(opt.model())
This example gives me:
sat
[x = 0, y = 0, profit = 0]
My question is: Why am I not getting infinity here? Am I doing something wrong or is this the output I should expect from Z3 with python when I give it an unbounded optimization problem?
I am using python 3.9 on Pycharm 2021.2.1, Z3 version 4.8.15.
You're not quite using the API correctly. You should use the value function on the optimization goal instead. For instance, your second query is coded as:
from z3 import *
x, y, profit = Ints('x y profit')
opt = Optimize()
opt.add(profit == 2*x + y)
maxProfit = opt.maximize(profit)
print(opt.check())
print("maxProfit =", maxProfit.value())
This prints:
sat
maxProfit = oo
Note that when the optimization result is oo, then the model values for x/y/profit etc., are irrelevant. (They are no longer integers.) If the optimization result is a finite value, then you can look at opt.model() to figure out what assignment to your variables achieved the optimal goal. So, the values of x/y and profit you get in your example, while printed as 0, are meaningless; since the goal is not a proper integer value.

Scipy minimise, How to get an int value array as an output

I have this kind of data :
import random
data=random.sample(range(1, 100), 5)
x= [-1,1,1,-1,1]
def f(x,data):
prod=[a * b for a, b in zip(data, x)]
result=abs(sum(prod))
return result
I Would like to find the best x composed of -1 or 1 to minimize the value of f(x)
Maybe we can use scipy.minimise() but how can we add the -1 or 1 as a constrain on the value inside of x ?
Does somebody have an idea ?
You want to solve a mixed-integer linear programming problem (MILP), which aren't supported yet by scipy.optimize.
However, you can use a modelling package like PuLP to formulate your MILP and pass it to a MILP solver. Note that your MIP can be formulated as
(P)
min |f(x)| = |d_0 * x_0 + ... + d_n * x_n|
s.t. x_i ∈ {-1, 1} ∀ i = 0,...,n
which is the same as
(P')
min |f(x)| = |d_0 * (2*x_0 - 1) + ... + d_n * (2*x_n - 1)|
s.t. x_i ∈ {0, 1} ∀ i = 0,...,n
and can be implemented like this
min abs_obj
s.t. f(x) <= abs_obj
f(x) >= -1.0*abs_obj
x_i ∈ {0, 1} ∀ i = 0,...,n
In code:
import pulp
import random
data = random.sample(range(1, 100), 5)
# pulp model
mdl = pulp.LpProblem("our_model", sense=pulp.LpMinimize)
# the binary variables x
x = pulp.LpVariable.dicts("x", range(5), cat="Binary")
# the variable that stores the absolute value of the objective
abs_obj = pulp.LpVariable("abs_obj")
# set the MIP objective
mdl += abs_obj
# Define the objective: |f(x)| = abs_obj
mdl += pulp.lpSum((2 * x[i] - 1) * data[i] for i in range(5)) <= abs_obj
mdl += pulp.lpSum((2 * x[i] - 1) * data[i] for i in range(5)) >= -1.0*abs_obj
# solve the problem
mdl.solve()
# your solution
signs = [1 if var.varValue > 0 else -1 for var in x.values()]
Alternatively, if you don't want to use another package, you can use scipy.optimize.minimize and implement a simple penalty method. Thereby you solve the problem (P') by solving the penalty problem
min |f(x)| + Ɛ * (x_0 * (1 - x_0) + ... + x_n * (1 - x_n))
with 0 <= x_i <= 1
where Ɛ is a given penalty parameter. Here, the idea is that the right penalty term equals zero for an integer solution.
Note that as the case may be that you need to solve a sequence of penalty problems to achieve convergence to an integer solution. Thus, I'd highly recommend sticking to a MILP solver instead of implementing a penalty method on your own.
Yes, you can do it using scipy.optimize.minimize:
from scipy.optimize import minimize
minimize(f, [0] * len(data), args=data, bounds=[(-1, 1)] * len(data))
This call minimizes f which you defined in the original post.
It passes a zero array as an initial guess for the minimization problem.
The argument f requires is 'data' which is specified by the argument 'args'.
The constraints you want are specified by the argument 'bounds' as a list of min/max tuples with the length of the input data.

Minimizing the sum of 3 variables subject to equality and integrality constraints

I am working on a programming (using Python) problem where I have to solve the following type of linear equation in 3 variables:
x, y, z are all integers.
Equation example: 2x + 5y + 8z = 14
Condition: Minimize x + y + z
I have been trying to search for an algorithm for finding a solution to this, in an optimum way. If anybody has any idea please guide me through algorithm or code-sources.
I am just curious, what can be done if this problem is extrapolated to n variables?
I don't want to use hit & trial loops to keep checking for values. Also, there may be a scenario that equation has no solution.
UPDATE
Adding lower bounds condition:
x, y, z >= 0
x, y, z are natural
Any triple (x, y, z), with z = (14 - 2x - 5y) / 8, satisfies your constraint.
Note that x + y + (14 - 2x - 5y) / 8 is unbounded from below. This function decreases when each of x and y decrease, with no finite minimum.
You have an equality-constrained integer program (IP) in just 3 dimensions. The equality constraint 2 x + 5 y + 8 z = 14 defines a plane in 3-dimensional space. Parametrizing it,
x = 7 - 2.5 u - 4 v
y = u
z = v
we obtain an unconstrained IP in 2 dimensions. Given the integrality constraints, we have u <- {0,2} and v <- {0,1}. Enumerating all four (u,v) pairs, we conclude that the minimum is 4 and that it is attained at (u,v) = (2,0) and (u,v) = (0,1), which correspond to (x,y,z) = (2,2,0) and (x,y,z) = (3,0,1), respectively.
Using PuLP to solve the integer program:
from pulp import *
# decision variables
x = LpVariable("x", 0, None, LpInteger)
y = LpVariable("y", 0, None, LpInteger)
z = LpVariable("z", 0, None, LpInteger)
# define integer program (IP)
prob = LpProblem("problem", LpMinimize)
prob += x+y+z # objective function
prob += 2*x + 5*y + 8*z == 14 # equality constraint
# solve IP
prob.solve()
# print results
print LpStatus[prob.status]
print value(x)
print value(y)
print value(z)
which produces x = 3, y = 0 and z = 1.
Another tool to solve this type of problems is SCIP. There is also an easy to use Python interface available on GitHub: PySCIPOpt.
In general (mixed) integer programming problems are very hard to solve (NP complexity) and often even simple looking instances with only a few variables and constraints can take hours to prove the optimal solution.
From your first equation:
x = (14 - 5y - 8x) / 2
so, you now only need to minimize
(14 - 5y - 8z) / 2 + y + z
which is
(14 - 3y - 6z) / 2
But we can ignore the ' / 2' part for minimization purposes.
Presumably, there must be some other constraints on your problem, since as described the solution is that both y and z may grow without bound.
I do not know any general fast solution for n variables, or not using hit & trail loops. But for the given specific equation 2x + 5y + 8z = 14, there maybe some shortcut based on observation.
Notice that the range is very small for any possible solutions:
0<=x<=7, 0<=y<=2, 0<=z<=1
Also other than x = 7, you have at least to use 2 variables.
(x+y+z = 7 for this case)
Let's find what we got if using only 2 variables:
If you choose to use (x,z) or (y,z), as z can only be 1, x or y is trivial.
(x+y+z = 4 for (x,z), no solution for (y,z))
If you choose to use (x,y), as x's coefficient is even and y's coefficient is odd, you must choose even number of y to achieve an even R.H.S. (14). Which means y must be 2, x is then trivial.
(x+y+z = 4 for this case)
Let's find what we got if using all 3 variables:
Similarly, z must be 1, so basically it's using 2 variables (x,y) to achieve 14-8 = 6 which is even.
Again we use similar argument, so we must choose even number of y which is 2, however at this point 2y + 1z > 14 already, which means there is no solution using all 3 variables.
Therefore simply by logic, reduce the equation by using 1 or 2 variables, we can find that minimum x+y+z is 4 to achieve 14 (x=3,y=0,z=1 or x=2,y=2,z=0)

ODEs with infinite initlal condition in python

I have a second order differential equation that I want to solve it in python. The problem is that for one of the variables I don't have the initial condition in 0 but only the value at infinity. Can one tell me what parameters I should provide for scipy.integrate.odeint ? Can it be solved?
Equation:
Theta needs to be found in terms of time. Its first derivative is equal to zero at t=0. theta is not known at t=0 but it goes to zero at sufficiently large time. all the rest is known. As an approximate I can be set to zero, thus removing the second order derivative which should make the problem easier.
This is far from being a full answer, but is posted here on the OP's request.
The method I described in the comment is what is known as a shooting method, that allows converting a boundary value problem into an initial value problem. For convenience, I am going to rename your function theta as y. To solve your equation numerically, you would first turn it into a first order system, using two auxiliary function, z1 = y and z2 = y', and so your current equation
I y'' + g y' + k y = f(y, t)
would be rewitten as the system
z1' = z2
z2' = f(z1, t) - g z2 - k z1
and your boundary conditions are
z1(inf) = 0
z2(0) = 0
So first we set up the function to compute the derivative of your new vectorial function:
def deriv(z, t) :
return np.array([z[1],
f(z[0], t) - g * z[1] - k * z[0]])
If we had a condition z1[0] = a we could solve this numerically between t = 0 and t = 1000, and get the value of y at the last time as something like
def y_at_inf(a) :
return scipy.integrate.odeint(deriv, np.array([a, 0]),
np.linspace(0, 1000, 10000))[0][-1, 0]
So now all we need to know is what value of a makes y = 0 at t = 1000, our poor man's infinity, with
a = scipy.optimize.root(y_at_inf, [1])

Python Pulp using with Matrices

I am still very new to Python, after years and years of Matlab. I am trying to use Pulp to set up an integer linear program.
Given an array of numbers:
{P[i]:i=1...N}
I want to maximize:
sum( x_i P_i )
subject to the constraints
A x <= b
A_eq x = b_eq
and with bounds (vector based bounds)
LB <= x <= UB
In pulp however, I don't see how to do vector declarations properly. I was using:
RANGE = range(numpy.size(P))
x = pulp.LpVariable.dicts("x", LB_ind, UB_ind, "Integer")
where I can only enter individual bounds (so only 1 number).
prob = pulp.LpProblem("Test", pulp.LpMaximize)
prob += pulp.lpSum([Prices[i]*Dispatch[i] for i in RANGE])
and for the constraints, do I really have to do this line per line? It seems that I am missing something. I would appreciate some help. The documentation discusses a short example. The number of variables in my case is a few thousand.
You can set the lowBound and upBound on variables after the initialization.
You can create an array of variables with
LB[i] <= x[i] <= UB[i]
with the following code.
x = pulp.LpVariable.dicts("x", RANGE, cat="Integer")
for i in x.viewkeys():
x[i].lowBound = LB_ind[i]
x[i].upBound = UB_ind[i]
The second parameter to LpVariable.dict is the index set of the decision variables, not their lower bounds.
For the first question, you can do it like this in some other problem.
students = range(96)
group = range(24)
var = lp.LpVariable.dicts("if_i_in_group_j", ((i, j) for i in students for j in group),cat='binary')

Categories

Resources