if x:
y > z
(1-x) * large_num > z - y
It's a bit confusing, but it is valid.
My problem:
I want to do exactly that, but instead of an inequality (y > z), I want to do an equality (y == z)
So I want to write a constraint to implement:
if x:
y == z
How can I do that?
I am trying to implement:
if x:
y >= z
AND
y <= z
Since (y <=z) and (y >=z) are both true only if y == z, which is what I want.
To write this properly, I have:
x = cvxpy.Int(name='x')
y = cvxpy.Int(name='y')
z = cvxpy.Int(name='z')
large_num = 99999 # far larger than any realistic value of y or z
constraints = []
constraints.append((1-x) * large_num >= y - z)
constraints.append((1-x) * -1 * large_num <= y - z)
...
When I try this, the solver doesn't throw an error, and claims to have found the 'optimal' solution, but the constraint 'if x then y == z' is violated by the solution.
More specifically, it works when this is the only constraint, but when I have two independent constraints in this way (where it happens that at most one should be active at a time) then at least one of those constraints is violated. But the solver doesn't complain.
What is wrong with my approach? Is there a simpler way?
(I have tried with x as a bool too. Same result.)
import numpy
import cvxpy
from cvxopt.modeling import op
from cvxopt.modeling import variable, op, max, sum
num_states = 10 # each cvxpy variable is an array this long
min_on_time = 3 # if states turns from 0 to 1, it must remain 1 for at least 3 states
states = cvxpy.Bool(num_states,name='states')
time_remaining = cvxpy.Variable(num_states,name='time_remaining')
constr = [] # constraints list
# just forcing states to do something interesting,
# to get the behaviour I'm talking about
#for (i,s) in enumerate(states):
# if (i%5) <= 2:
# constr.append(s == 1)
# else:
# constr.append(s == 0)
# setup the first period
constr.append(time_remaining[0] == min_on_time)
# now is the tricky bit.
# I want to implement:
# if states[i-1]:
# time_remaining[i] = time_remaining[i-1] - 1
# else:
# time_remaining[i] = min_on_time * states[i]
large_num = num_states * 1e3
for i in range(num_states):
# if states[i-1] then time_remaining[i] = time_remaining[i-1] - solve_period_len
constr.append((1-states[i-1]) * large_num >= time_remaining[i-1] - 1 - time_remaining[i])
constr.append((1-states[i-1])*-1*large_num <= time_remaining[i-1] - 1 - time_remaining[i])
# if not states[i-1] then time_remaining[i] = min_on_time * states[i]
constr.append(states[i-1] * large_num >= min_on_time * states[i] - time_remaining[i])
constr.append(states[i-1] * -1*large_num <= min_on_time * states[i] - time_remaining[i])
for i in range(num_states):
constr.append(time_remaining[i] <= states[i] * large_num)
# setup some objective, I don't care what
obj = []
for (i,s) in enumerate(states):
if i%2:
obj.append(cvxpy.Maximize(s))
else:
obj.append(cvxpy.Maximize(-1*s))
problem = cvxpy.Problem(sum(obj), constr)
ret = problem.solve(verbose=True)
print('problem.status == %s' % problem.status)
print('problem.status == cvxpy.OPTIMAL : %s' % problem.status == cvxpy.OPTIMAL)
print('states: [%s]' % ','.join(['%2d' % round(x.value) for x in states]))
print('time_remaining: [%s]' % ','.join(['%2d' % round(x.value) for x in time_remaining]))
for i in range(num_states):
if round(states[i-1].value):
if round(time_remaining[i].value) != round(time_remaining[i-1].value) - 1:
print('invalid for i=%d' % i)
print(' constraint violated: if states[%d], then time_remaining[%d]==time_remaining[%d]-1' % (i,i,i-1))
print(' where:')
print(' states[%d]=%f' % (i,states[i].value))
print(' time_remaining[%d]=%f' % (i,time_remaining[i].value))
print(' time_remaining[%d]=%f' % (i-1,time_remaining[i-1].value))
else:
if round(time_remaining[i].value) != (min_on_time * round(states[i].value)):
print('invalid for i=%d' % i)
print(' constraint violated: time_remaining[%d] = min_on_time * states[%d]' % (i,i))
print(' where:')
print(' time_remaining[%d]=%f' % (i,time_remaining[i].value))
print(' min_on_time = %f' % min_on_time)
print(' states[%d]=%f' % (i,states[i].value))
print(' min_on_time * states[%d] = %f' % (i,min_on_time*states[i].value))
ECOS 2.0.4 - (C) embotech GmbH, Zurich Switzerland, 2012-15. Web: www.embotech.com/ECOS
It pcost dcost gap pres dres k/t mu step sigma IR | BT
0 -1.442e-02 -2.005e+03 +4e+03 1e-01 4e-01 1e+00 5e+01 --- --- 1 1 - | - -
1 +1.303e-01 -3.231e+03 +3e+03 3e-01 3e-01 2e+01 5e+01 0.2961 7e-01 1 0 0 | 0 0
2 +2.458e-02 -5.508e+02 +7e+02 5e-02 4e-02 1e+01 9e+00 0.8410 5e-02 1 0 0 | 0 0
3 +2.324e-03 -3.061e+01 +4e+01 3e-03 2e-03 9e-01 5e-01 0.9609 2e-02 1 0 0 | 0 0
4 +5.093e-05 -5.525e-01 +6e-01 5e-05 4e-05 2e-02 9e-03 0.9821 1e-04 1 0 0 | 0 0
5 +3.709e-05 -2.284e-01 +2e-01 2e-05 1e-05 8e-03 4e-03 0.6630 7e-02 1 0 0 | 0 0
6 +2.897e-04 -1.789e+00 +1e-01 3e-04 6e-05 2e-01 2e-03 0.9104 4e-01 1 0 0 | 0 0
7 +7.147e-05 +6.137e-01 +3e-03 6e-05 8e-06 9e-01 4e-05 0.9787 1e-04 1 1 1 | 0 0
8 +8.071e-05 +7.948e+01 +3e-05 5e-05 2e-05 8e+01 4e-07 0.9890 1e-04 1 0 0 | 0 0
9 +8.003e-05 +7.164e+03 +3e-07 5e-05 4e-05 7e+03 5e-09 0.9890 1e-04 1 0 0 | 0 0
PRIMAL INFEASIBLE (within feastol=3.4e-10).
Runtime: 0.000364 seconds.
problem.status == optimal
False
states: [ 1, 1, 1, 1, 0, 1, 1, 1, 0, 1]
time_remaining: [ 3, 2, 1, 0,-1, 3, 2, 1, 0, 4]
invalid for i=9
constraint violated: time_remaining[9] = min_on_time * states[9]
where:
time_remaining[9]=3.993470
min_on_time = 3.000000
states[9]=0.999999
min_on_time * states[9] = 2.999998
constr.append(states[i-1] * -1*large_num <= min_on_time * states[i] - time_remaining[i])
# end of MWE
apt-get install coinor-cbc
ret = problem.solve(solver=cvxpy.CBC)
problem.solve(solver=cvxpy.CBC)
matt@machine:~/muckaround/opt$ . ./worker-env/bin/activate(worker-env) matt@ip-10-61-176-23:~/muckaround/opt$ ./worker-env/bin/pip install cylpRequirement already satisfied: cylp in ./worker-env/lib/python2.7/site-packagesRequirement already satisfied: numpy>=1.5.0 in ./worker-env/lib/python2.7/site-packages (from cylp)Requirement already satisfied: scipy>=0.10.0 in ./worker-env/lib/python2.7/site-packages (from cylp)(worker-env) matt@machine:~/muckaround/opt$ ./worker-env/bin/pip install cvxoptRequirement already satisfied: cvxopt in ./worker-env/lib/python2.7/site-packages(worker-env) matt@machine:~/muckaround/opt$ ./worker-env/bin/pip install cvxpyRequirement already satisfied: cvxpy in ./worker-env/lib/python2.7/site-packagesRequirement already satisfied: six in ./worker-env/lib/python2.7/site-packages (from cvxpy)Requirement already satisfied: toolz in ./worker-env/lib/python2.7/site-packages (from cvxpy)Requirement already satisfied: multiprocess in ./worker-env/lib/python2.7/site-packages (from cvxpy)Requirement already satisfied: scs>=1.1.3 in ./worker-env/lib/python2.7/site-packages (from cvxpy)Requirement already satisfied: ecos>=2 in ./worker-env/lib/python2.7/site-packages (from cvxpy)Requirement already satisfied: numpy>=1.9 in ./worker-env/lib/python2.7/site-packages (from cvxpy)Requirement already satisfied: fastcache in ./worker-env/lib/python2.7/site-packages (from cvxpy)Requirement already satisfied: CVXcanon>=0.0.22 in ./worker-env/lib/python2.7/site-packages (from cvxpy)Requirement already satisfied: scipy>=0.15 in ./worker-env/lib/python2.7/site-packages (from cvxpy)Requirement already satisfied: dill>=0.2.6 in ./worker-env/lib/python2.7/site-packages (from multiprocess->cvxpy)(worker-env) matt@machine:~/muckaround/opt$ pythonPython 2.7.12 (default, Nov 19 2016, 06:48:10)[GCC 5.4.0 20160609] on linux2Type "help", "copyright", "credits" or "license" for more information.>>> from cylp.cy import CyClpSimplexTraceback (most recent call last): File "<stdin>", line 1, in <module> File "/home/matt/muckaround/opt/worker-env/local/lib/python2.7/site-packages/cylp/cy/__init__.py", line 1, in <module> from CyCoinIndexedVector import CyCoinIndexedVectorImportError: libCoinUtils.so.0: cannot open shared object file: No such file or directory>>>
import cylp
cvxpy.Maximize(states[0] - states[1] + states[2] - states[3] + states[4] - states[5] ...)
import numpy
import cvxpy
from cvxopt.modeling import op
from cvxopt.modeling import variable, op, max, sum
import cylp
# round up x to the next multiple of y
def round_up(x,y):
if x%y == 0:
result = x
else:
result = x - (x%y) + y
#print('round(%f,%f) == %f' % (x,y,result))
return result
def my_solve(solve_period_len, min_on_time, num_periods, prev_state, prev_on_time, obj1, obj2, tol=0,verbose=False):
states = cvxpy.Bool(num_periods,name='states')
time_remaining = cvxpy.Variable(num_periods,name='time_remaining')
constr = [] # constraints list
large_num = num_periods * 1e2
# setup the first period
if prev_state:
constr.append(time_remaining[0] == min_on_time - prev_on_time)
else:
constr.append(time_remaining[0] == min_on_time * states[0])
for i in range(num_periods):
if i > 0:
# if states[i-1] then time_remaining[i] = time_remaining[i-1] - solve_period_len
constr.append((1-states[i-1]) * large_num >= time_remaining[i-1] - solve_period_len - time_remaining[i])
constr.append((1-states[i-1])*-1*large_num <= time_remaining[i-1] - solve_period_len - time_remaining[i])
# if not states[i-1] then time_remaining[i] = min_on_time * states[i]
constr.append(states[i-1] * large_num >= min_on_time * states[i] - time_remaining[i])
constr.append(states[i-1] * -1*large_num <= min_on_time * states[i] - time_remaining[i])
for i in range(num_periods):
constr.append(time_remaining[i] <= states[i] * large_num)
# setup the objective function
obj = cvxpy.Maximize(0)
for (i,s) in enumerate(states):
if (i%obj1) < obj2:
obj += cvxpy.Maximize(states[i])
else:
obj += cvxpy.Maximize(-1*states[i])
problem = cvxpy.Problem(obj, constr)
ret = problem.solve()
##########
# Everything beyond here is just printing and verifying the answer
#########
print('problem.status == %s' % problem.status)
print('objectives: %s' % ','.join(['%d' % (2*(i%obj1 < obj2)-1) for i in range(num_periods)]))
rem_calc_valid_all = True
sw_off_constr_valid_all = True
for i in range(num_periods):
row_strs = ['period:%2d' % i]
if states[i].value == None:
print('states[%d]==None' % i)
return 0
state = round(states[i].value)
row_strs.append('state:%1d' % state)
t_rem_i = time_remaining[i].value
if t_rem_i != None:
row_strs.append('t_rem:%4d' % round(t_rem_i))
else:
row_strs.append('t_rem:None')
obj_i = (2*(i%obj1 < obj2)-1)
row_strs.append('obj:%d' % obj_i)
if i == 0:
prev_state_i = prev_state
else:
prev_state_i = states[i-1].value
# has the t_rem decreased and increased properly?
rem_calc_valid_i = True
if i == 0:
if prev_state_i:
if round(time_remaining[i].value) != (min_on_time - prev_on_time):
print('HERE A')
rem_calc_valid_i = False
elif round(states[0].value):
if round(time_remaining[i].value) != min_on_time:
print('HERE B')
rem_calc_valid_i = False
elif round(time_remaining[i].value) != 0:
rem_calc_valid_i = False
if i > 0:
if round(states[i-1].value):
if round(time_remaining[i].value) != round(time_remaining[i-1].value) - solve_period_len:
rem_calc_valid_i = False
else:
if round(states[i].value) and round(time_remaining[i].value) != (min_on_time * round(states[i].value)):
rem_calc_valid_i = False
row_strs.append('rem_calc_valid: %d' % rem_calc_valid_i)
# has the device switched off when t_rem > 0?
sw_off_constr_valid_i = True
if prev_state_i and (not round(states[i].value)) and (round(time_remaining[i].value) > 0):
sw_off_constr_valid_i = False
row_strs.append('sw_off_constr: %d' % sw_off_constr_valid_i)
overall_valid_i = rem_calc_valid_i and sw_off_constr_valid_i
row_strs.append('overall: %d' % overall_valid_i)
print(' | '.join(row_strs))
if not rem_calc_valid_i:
rem_calc_valid_all = False
if not sw_off_constr_valid_i:
sw_off_constr_valid_all = False
overall_valid = rem_calc_valid_all and sw_off_constr_valid_all
print('\nOverall valid: %d' % overall_valid)
return overall_valid
if __name__ == "__main__":
#main()
ret = my_solve(1, 3, 30, 0, 0, 3, 1)
assert(ret)