I am writing a constrained integer optimization and am having trouble with formulating constraints. Here is a summary of my optimization: (Schedule is my own function that returns a single int value)
Objective: MIN (1500 * x1) + (625 * x2) +(100 * x3)
Constraints:
schedule(x1,x2,x3) >=480
x2 > x1
x2 > x3
Bounds: (5<= x1 <=50), (5<= x2 <=100), (1<= x3 <=20)
What is the best way to group the hard constraints to put into diffev2 because constraint1 uses a function so cannot be written in a symbolic way?
Below is a rough attempt at this. Another minor issue I have experienced is bounds being breached (values were negative) so if there are any red flags with how I have bounded it please do say. Very new to Mystic as well so don't worry about explaining things in simple terms.
import mystic as ms
import mystic.symbolic as msym
import numpy as np
def objective(x):
rounded = np.around(x)
integer= rounded.astype(int)
x1,x2,x3 = integer
return (1500 * x1) + (625 * x2) +(100 * x3)
bounds = [(5,50),(5,100),(1,20)]
def constraint1(x):
rounded = np.around(x)
integer= rounded.astype(int)
x1,x2,x3 = integer
return 240-schedule(x1,x2,x3)
eqns = '''
x2 > x1
x2 > x3
'''
cons = msym.generate_constraint(msym.generate_solvers(msym.simplify(eqns)))
constraint = ms.constraints.and_(cons, constraint1) #I know this is wrong but I want to join them
from mystic.solvers import diffev2
from mystic.monitors import VerboseMonitor
mon = VerboseMonitor(10)
result = diffev2(objective,x0=bounds, bounds=bounds, constraints=constraint, npop=50, gtol=200, \
disp=False, full_output=True, itermon=mon)
I'd slightly rewrite it like this...
Objective: MIN (1500 * x0) + (625 * x1) + (100 * x2)
Constraints: schedule(x0,x1,x2) >= 480 x1 > x0 x1 > x2 x0, x1, x2 are integers
Bounds: (5 <= x0 <=50), (5 <= x1 <=100), (1 <= x2 <=20)
Here, we will define schedule in a function, as well as the bounds and the objective.
Then we start building the constraints. Constraints functions are always
x' = constraints(x)
(i.e. they take a parameter vector, and return a parameter vector). Thus, we have two special cases here: (1) symbolic equations, for which we will buildx' = cons(x)
, and the constraint on the output ofschedule
-- which needs a bit of round-about logic to construct.Note that I've rewritten the symbolic constraints to have a different variable on the left side each time. This is because without using
and_
, the constraint that is built will just use the last one. So, usingand_
forces an optimization to be run so that both are respected. I make it easier to solve, by not reusing variables on the left-hand side. Lastly, I check that the constraints work as expected.Note also I didn't use the integer constraint just yet. We can apply that when we put all the constraints together.
To apply a constraint on the output, we first build a penalty, then convert it to a constraint. It's not efficient, as it again takes an internal optimization.
Then we put it all together, again using
and_
.Lastly, we solve.
And again with a different solver...
You'll note that the optimizer found better values than what we started with by just applying the constraint, but didn't search very much. In tightly constrained spaces, it is often the case that the solution is found right away. You'll have to investigate a bit more to see if this is indeed the global minimum.