I am working on a optimization problem and am wondering why the solution is so far from optimum.
As a part of it i made the objective function much easier so it is easy to analytically tell where the solver should allocate more resources: in this simplified version it is easy to tell that i should maximize w.r.t constraints inv2 for days near the end of the month since the coefficient for inv2 is higher than inv1 and the day coefficient is positive.
The solver does not solve this however. It also seems strange that it does not use the whole resource = 100000 thus violates this constraint(last one).
I think i have made a small but vital error in the code somewhere.
So Before digging deep into differential evolutional algorithms i thought i ask here.
Why does the solver violate the constraint and find such an suboptimal solution?
import pandas
from datetime import timedelta
from datetime import datetime
from dateutil.tz import gettz
import mystic
from mystic.symbolic import simplify
from mystic.symbolic import generate_constraint, generate_solvers
from mystic.monitors import VerboseMonitor
from mystic.symbolic import symbolic_bounds
horizon = 30
nowianatimezone = datetime.now(tz=gettz(name='Europe/Stockholm'))
datesbetweendates = pandas.date_range(start=nowianatimezone,
end=nowianatimezone + timedelta(days=horizon),
freq='d')
daysforcalculation = datesbetweendates.day.tolist()
def objectiveforoneday(inv1, inv2, day):
return 1.1 * inv1 + 1.4 * inv2 + 10 * day
def objective(x):
tbret = 0
for idx, day in enumerate(daysforcalculation):
inv1 = x[idx * 2]
inv2 = x[(idx * 2) + 1]
tbret += objectiveforoneday(inv1=inv1,
inv2=inv2,
day=day)
return tbret
constraints = '''
x0 >= 250.0
x1 >= 100.0
x2 >= 250.0
x3 >= 100.0
x4 >= 250.0
x5 >= 100.0
x6 >= 250.0
x7 >= 100.0
x8 >= 250.0
x9 >= 100.0
x10 >= 250.0
x11 >= 100.0
x12 >= 250.0
x13 >= 100.0
x14 >= 250.0
x15 >= 100.0
x16 >= 250.0
x17 >= 100.0
x18 >= 250.0
x19 >= 100.0
x20 >= 250.0
x21 >= 100.0
x22 >= 250.0
x23 >= 100.0
x24 >= 250.0
x25 >= 100.0
x26 >= 250.0
x27 >= 100.0
x28 >= 250.0
x29 >= 100.0
x30 >= 250.0
x31 >= 100.0
x32 >= 250.0
x33 >= 100.0
x34 >= 250.0
x35 >= 100.0
x36 >= 250.0
x37 >= 100.0
x38 >= 250.0
x39 >= 100.0
x40 >= 250.0
x41 >= 100.0
x42 >= 250.0
x43 >= 100.0
x44 >= 250.0
x45 >= 100.0
x46 >= 250.0
x47 >= 100.0
x48 >= 250.0
x49 >= 100.0
x50 >= 250.0
x51 >= 100.0
x52 >= 250.0
x53 >= 100.0
x54 >= 250.0
x55 >= 100.0
x56 >= 250.0
x57 >= 100.0
x58 >= 250.0
x59 >= 100.0
x60 >= 250.0
x61 >= 100.0
x0 <= 16666.666666666668
x1 <= 16666.666666666668
x2 <= 16666.666666666668
x3 <= 16666.666666666668
x4 <= 16666.666666666668
x5 <= 16666.666666666668
x6 <= 16666.666666666668
x7 <= 16666.666666666668
x8 <= 16666.666666666668
x9 <= 16666.666666666668
x10 <= 16666.666666666668
x11 <= 16666.666666666668
x12 <= 16666.666666666668
x13 <= 16666.666666666668
x14 <= 16666.666666666668
x15 <= 16666.666666666668
x16 <= 16666.666666666668
x17 <= 16666.666666666668
x18 <= 16666.666666666668
x19 <= 16666.666666666668
x20 <= 16666.666666666668
x21 <= 16666.666666666668
x22 <= 16666.666666666668
x23 <= 16666.666666666668
x24 <= 16666.666666666668
x25 <= 16666.666666666668
x26 <= 16666.666666666668
x27 <= 16666.666666666668
x28 <= 16666.666666666668
x29 <= 16666.666666666668
x30 <= 16666.666666666668
x31 <= 16666.666666666668
x32 <= 16666.666666666668
x33 <= 16666.666666666668
x34 <= 16666.666666666668
x35 <= 16666.666666666668
x36 <= 16666.666666666668
x37 <= 16666.666666666668
x38 <= 16666.666666666668
x39 <= 16666.666666666668
x40 <= 16666.666666666668
x41 <= 16666.666666666668
x42 <= 16666.666666666668
x43 <= 16666.666666666668
x44 <= 16666.666666666668
x45 <= 16666.666666666668
x46 <= 16666.666666666668
x47 <= 16666.666666666668
x48 <= 16666.666666666668
x49 <= 16666.666666666668
x50 <= 16666.666666666668
x51 <= 16666.666666666668
x52 <= 16666.666666666668
x53 <= 16666.666666666668
x54 <= 16666.666666666668
x55 <= 16666.666666666668
x56 <= 16666.666666666668
x57 <= 16666.666666666668
x58 <= 16666.666666666668
x59 <= 16666.666666666668
x60 <= 16666.666666666668
x61 <= 16666.666666666668
x0 + x1 + x2 + x3 + x4 + x5 + x6 + x7 + x8 + x9 + x10 + x11 + x12 + x13 + x14 + x15 + x16 + x17 + x18 + x19 + x20 + x21 + x22 + x23 + x24 + x25 + x26 + x27 + x28 + x29 + x30 + x31 + x32 + x33 + x34 + x35 + x36 + x37 + x38 + x39 + x40 + x41 + x42 + x43 + x44 + x45 + x46 + x47 + x48 + x49 + x50 + x51 + x52 + x53 + x54 + x55 + x56 + x57 + x58 + x59 + x60 + x61 == 100000'''
constraintsimplified = simplify(constraints,
all=True)
generatedconstraints = generate_constraint(generate_solvers(constraintsimplified))
initvals = [0.0 for _ in range(len(daysforcalculation) * 2)]
stepmon=VerboseMonitor(10)
import numpy as np
cost = lambda x: -objective(x)
optimum = mystic.solvers.diffev2(cost,
x0=initvals,
constraints=generatedconstraints,
# bounds=bounds,
#full_output=True,
#stepmon=stepmon,
#disp=True,
npop=100,
maxiter=10000)
optimalvalueslst = optimum[0]
# gives us
array([16666.66666667, 100. , 250. , 100. ,
250. , 100. , 250. , 100. ,
250. , 100. , 250. , 100. ,
250. , 100. , 250. , 100. ,
250. , 100. , 250. , 100. ,
250. , 100. , 250. , 100. ,
250. , 100. , 250. , 100. ,
250. , 100. , 250. , 100. ,
250. , 100. , 250. , 100. ,
250. , 100. , 250. , 100. ,
250. , 100. , 250. , 100. ,
250. , 100. , 250. , 100. ,
250. , 100. , 250. , 100. ,
250. , 100. , 250. , 100. ,
250. , 100. , 250. , 100. ,
250. , 100. ])
I'm the
mysticauthor. The constraints are violated because they are not orthogonal. By default,mysticsolves constraints sequentially. Here's the relevant doc fromgenerate_constraints:So, to solve the constraints in parallel you need to use
join, like this:Otherwise, your code should be fine. I'm running it right now, and it's pretty slow... so you might think about using a parallel map (with the
mapkeyword). I'd also repeat the all the bounds information you have in the constraints as bounds and pass the bounds to the solver using theboundskeyword as well -- that way, the DE solver won't have to work as hard to generate solutions for the constraints that are inside the bounds.EDIT: I came back to this, took a closer look at your code, and tried a bit harder to diagnose what's actually going on.
First, I'll take my advice of moving the equality constraint to a penalty:
You might notice that I've used
itermonso theMonitoris used, and I've setx0=boundsso the DE has better variety in the population when it starts. Also, I've increased the DE capacity tonpop=400andgtol=200, so the DE has a better chance of finding a solution. It does a decent job, but you'll notice that the penalty is still slightly violated. The equality constraint is a really hard one to satisfy for 62 "randomish" numbers. You can see this by just trying the sum in the constraint, and it still fails for DE.Basically, it fails to find a solution to the one constraint. You will get an
inf(i.e. constraints are violated) even when the sum is 99999.999999999998. Like I said, the equality constraint here is a difficult one for DE to generate... so it needs help. Help can come in terms of a penalty, or potentially, by only working over integers (as that seems like it could provide a decent solution). Or, we might try a different solver than DE.By the way, you don't have to specify the bounds in the constraints, there are other functions in
mysticthat can do that. Your equality sum constraint is also available inmystic. It can be efficient to build them all in one go like you did, but maybe not as convenient. Here's an alternate version of the sum constraint and the bounds constraint. I'm also going to bring in some integer constraints to help us.In the above,
normedis the same as the constraint built from the equality insums, andboundsconstrainis the same as the constraint built from all of the bounds inequalities inconstraints. I won't use these, but I wanted to point them out.So, now I'll try constraining to integers, and penalizing against the exactly matching the sum. I'm also using the
tightrangekeyword (it does the same thing as theboundsconstrainconstraint).This works pretty well, and the sum is just barely violated. So, I think with a little more tuning of the DE parameters you should be able to get it perfect. To confirm that the constraints work, we can try a different solver with the same basic constraint setup.
So, in this case, the constraints are perfectly satisfied, but it might be in a local minimum as I used a non-global solver. Either way, the last two runs tell me that with a bit more work tuning the solver, or manipulating how the constraints/penalties/bounds are applied, this is a hard, but solvable problem, and with tuning of the DE parameters, or potentially using one of the ensemble solvers, you can get the global optimum.