How do I implement presence_of(x) => absence_of(y) - python

I'm trying to solve a scheduling problem in Python using docplex.cp.
In my problem I have a constraint that states that if a given optional interval variable is present, then some other optional interval variable must be absent.
I have tried this:
mdl.add(
mdl.if_then(
mdl.presence_of(optional_interval_var1),
mdl.equal(mdl.presence_of(optional_interval_var2), 0)
)
)
But it doesn't seem to work. I find that this constraint isn't enforced in the solution provided by the solver.

You can use <= between presenceOf
For instance
from docplex.cp.model import *
model = CpoModel()
itvs1=interval_var(optional = True,
start = 1,
end = 10)
itvs2=interval_var(optional = True,
start = 10,
end = 15)
model.add(minimize(presence_of(itvs1)+presence_of(itvs2)))
#model.add(presence_of(itvs1)==1)
model.add(presence_of(itvs1)<=presence_of(itvs2))
# Solve the model
sol = model.solve(LogPeriod=1000000,trace_log=True)
res1=sol.get_var_solution(itvs1).is_present()
res2=sol.get_var_solution(itvs2).is_present()
print("itvs1 is present : ",res1)
print("itvs2 is present : ",res2)
gives
itvs1 is present : False
itvs2 is present : False
But if you uncomment
#model.add(presence_of(itvs1)==1)
then you get
itvs1 is present : True
itvs2 is present : True

Related

cplex changes the fixed variable after solving the problem in pyomo

I am trying to solve a MIP, I use pyomo, and Cplex(Interactive Optimizer 20.1.0.0) is solver.
The problem is that I want to fix some binary integer variables then solve the problem, and I used:
model.y[1,4].fix(1)
model.y[2,3].fix(0)
, but I have noticed that after solving the problem those fixed variables have changed to another values.
How can I say cplex to not change that fixed variables?
let me use the bus example for fixed start with pyomo
import pyomo.environ as pyo
from pyomo.opt import SolverFactory
opt = pyo.SolverFactory("cplex")
model = pyo.ConcreteModel()
model.nbBus = pyo.Var([40,30], domain=pyo.PositiveIntegers)
#fixed start
model.nbBus[40].fix(3)
# end of fixed start
model.OBJ = pyo.Objective(expr = 500*model.nbBus[40] + 400*model.nbBus[30])
model.Constraint1 = pyo.Constraint(expr = 40*model.nbBus[40] + 30*model.nbBus[30] >= 300)
results = opt.solve(model)
print("nbBus40=",int(model.nbBus[40].value))
print("nbBus30=",int(model.nbBus[30].value))
gives
nbBus40= 3
nbBus30= 6
whereas if you remove the fixed start you get
nbBus40= 6
nbBus30= 2
Since fixing variable didn't work for me, I tackle my problem with adding a new constraint to the model, and it works.
def yvar_fix(model, i, j ):
if (i,j) in y_set_init:
constraint = (model.y[i,j] == 1)
else:
constraint = (model.y[i,j] == 0)
return constraint
model.yvar_fix = pe.Constraint(model.edges, rule=yvar_fix)

Pyomo optimization Investments/Revenue

I'm new to Pyomo and I'm trying to optimise investments depending on budgets.
I have a total budget, and I want to find the best way to split the budget on the different medias.
eg: total_budget = 5000 --> tv = 3000, cinema = 500, radio = 1500.
I'm struggling "connecting" a Budget with a corresponding Revenue.
The medias have different return curves (It might be better to invest in a specific media until a certain budget is reached, then other medias).
The revenue for the different media is returned by a function like the following: tv_1k_revenue = calculate_revenue(budget=1000, media="tv")
Let say the only constraint I have is the total budget to simplify the problem (I can manage other constraints I think).
Here is my code so far:
model = pyo.ConcreteModel(doc="Optimization model")
# Declaration of possible budgets
model.S1 = Set(initialize=[*df.TV_Budget.values])
model.tv_budget = Var(model.S1, initialize=0.0)
model.S2 = Set(initialize=[*df.Cinema_Budget.values])
model.cinema_budget = Var(model.S2, initialize=0.0)
model.S3 = Set(initialize=[*df.Radio_Budget.values])
model.radio_budget = Var(model.S3, initialize=0.0)
# Objective function
def func_objective(model):
objective_expr = sum(model.tv_revenue +
model.cinema_revenue +
model.radio_revenue)
return objective_expr
model.objective = pyo.Objective(rule=func_objective, sense=pyo.maximize)
So my problem is, how do I declare model.tv_revenue, model.cinema_revenue, model.radio_revenue so I can optimise TV, Cinema and Radio budgets to maximize the total revenue generated by TV, Cinema, Radio?
Right now I created a DataFrame with a Budget and Revenue column for each media, but the best way should be using my calculate_revenue function and set bounds=(min_budget, max_budget) on each media budget.
Thank you for your help!
Thank you very much #AirSquid !
That's exactly it.
It does make a lot of sens to throw pandas in my case.
Also, Yes my revenue function is non-linear.
I might try to make a linear approximation and see if I can make that work.
I was going to try to declare my objective function as:
def func_objective(model):
objective_expr = sum([calculate_revenue(model.budget[media], media=media) for media in model.medias])
return objective_expr
model.objective = pyo.Objective(rule=func_objective, sense=pyo.maximize)
Would you know why I cannot declare it like this?
From what you are providing and your limited experience w/ pyomo, here's my recommendations...
You appear to have budgets and revenues, and those appear to be indexed by media type. It isn't clear what you are doing now with the indexing. So I would expect something like:
model.medias = pyo.Set(initialize=['radio', 'tv', ... ])
model.budget = pyo.Var(model.medias, domain=pyo.NonNegativeReals)
...
Throw pandas out the window. It is a great pkg, but not that helpful in setting up a model. Try something with just python dictionaries to hold your constants & parameters. (see some of my other examples if that is confusing).
The problem you will get to eventually, I'm betting, is that your revenue function is probably non-linear. Right? I would start with a simple linear approximation of it, see if you can get that model working, and then consider either making a piece-wise linear approximation or using a non-linear solver of some kind.
===================
Edit / Additional Info.
Regarding the obj function, you cannot just stuff in a reference to a non-linear function that returns a value. The objective needs to be a valid pyomo expression (linear or non-linear), comprised of model elements. I would start w/ something like this...
# media mix
import pyomo.environ as pyo
# data for linear approximations of form revenue = c1 * budget + c0
# media c0 c1
consts = { 'radio' : (4, 0.6),
'tv' : (12, 0.45)}
# a bunch of other parameters....?? limits, minimums, etc.
### MODEL
m = pyo.ConcreteModel('media mix')
### SETS
m.medias = pyo.Set(initialize=consts.keys())
### VARIABLES
m.budget = pyo.Var(m.medias, domain=pyo.NonNegativeReals)
### OBJ
m.obj = pyo.Objective(expr=sum(consts[media][1]*m.budget[media] + consts[media][0] for media in m.medias),
sense=pyo.maximize)
m.pprint()
Yields:
1 Set Declarations
medias : Size=1, Index=None, Ordered=Insertion
Key : Dimen : Domain : Size : Members
None : 1 : Any : 2 : {'radio', 'tv'}
1 Var Declarations
budget : Size=2, Index=medias
Key : Lower : Value : Upper : Fixed : Stale : Domain
radio : 0 : None : None : False : True : NonNegativeReals
tv : 0 : None : None : False : True : NonNegativeReals
1 Objective Declarations
obj : Size=1, Index=None, Active=True
Key : Active : Sense : Expression
None : True : maximize : 0.6*budget[radio] + 4 + 0.45*budget[tv] + 12
3 Declarations: medias budget obj

PYOMO Constraints - setting constraints over indexed variables

I have been trying to get into python optimization, and I have found that pyomo is probably the way to go; I had some experience with GUROBI as a student, but of course that is no longer possible, so I have to look into the open source options.
I basically want to perform an non-linear mixed integer problem in which I will minimized a certain ratio. The problem itself is setting up a power purchase agreement (PPA) in a renewable energy scenario. Depending on the electricity generated, you will have to either buy or sell electricity acording to the PPA.
The only starting data is the generation; the PPA is the main decision variable, but I will need others. "buy", "sell", "b1" and "b2" are unknown without the PPA value. These are the equations:
Equations that rule the problem (by hand).
Using pyomo, I was trying to set up the problem as:
# Dataframe with my Generation information:
January = Data['Full_Data'][(Data['Full_Data']['Month'] == 1) & (Data['Full_Data']['Year'] == 2011)]
Gen = January['Producible (MWh)']
Time = len(Generacion)
M=100
# Model variables and definition:
m = ConcreteModel()
m.IDX = range(time)
m.PPA = Var(initialize = 2.0, bounds =(1,7))
m.compra = Var(m.IDX, bounds = (0, None))
m.venta = Var(m.IDX, bounds = (0, None))
m.b1 = Var(m.IDX, within = Binary)
m.b2 = Var(m.IDX, within = Binary)
And then, the constraint; only the first one, as I was already getting errors:
m.b1_rule = Constraint(
expr = (((Gen[i] - PPA)/M for i in m.IDX) <= m.b1[i])
)
which gives me the error:
---------------------------------------------------------------------------
AttributeError Traceback (most recent call last)
<ipython-input-5-5d5f5584ebca> in <module>
1 m.b1_rule = Constraint(
----> 2 expr = (((Generacion[i] - PPA)/M for i in m.IDX) <= m.b1[i])
3 )
pyomo\core\expr\numvalue.pyx in pyomo.core.expr.numvalue.NumericValue.__ge__()
pyomo\core\expr\logical_expr.pyx in pyomo.core.expr.logical_expr._generate_relational_expression()
AttributeError: 'generator' object has no attribute 'is_expression_type'
I honestly have no idea what this means. I feel like this should be a simple problem, but I am strugling with the syntax. I basically have to apply a constraint to each individual data from "Generation", there is no sum involved; all constraints are 1-to-1 contraints set so that the physical energy requirements make sense.
How do I set up the constraints like this?
Thank you very much
You have a couple things to fix. First, the error you are getting is because you have "extra parenthesis" around an expression that python is trying to convert to a generator. So, step 1 is to remove the outer parenthesis, but that will not solve your issue.
You said you want to generate this constraint "for each" value of your index. Any time you want to generate copies of a constraint "for each" you will need to either do that by making a constraint list and adding to it with some kind of loop, or use a function-rule combination. There are examples of each in the pyomo documentation and plenty on this site (I have posted a ton if you look at some of my posts.) I would suggest the function-rule combo and you should end up with something like:
def my_constr(m, i):
return m.Gen[i] - m.PPA <= m.b1[i] * M
m.C1 = Constraint(m.IDX, rule=my_constr)

Mapping and iterating nested dictionaries

I am not too familiar with python but have a working understanding of the basics. I believe that I need dictionaries, but what I am currently doing is not working and likely very ineffective time-wise.
I am trying to create a cross matrix that links reviews between users given: the list of reviewers, their individual reviews, metadata related to the reviews.
NOTE : This is written in Python 2.7.10 - I cannot use Python 3 because outdated systems this will be run on, yada yada.
For initialization I have the following:
print '\nCompiling Review Maps... ';
LbidMap = {};
TbidMap = {};
for user in reviewer_idx :
for review in data['Reviewer Reviews'][user] :
reviewInfo = data['Review Information'][review];
stars = float(reviewInfo['stars']);
bid = reviewInfo['business_id'];
# Initialize lists where necessary
# !!!! I know this is probably not effective, but am unsure of
# a better method. Open to suggestions !!!!!
if bid not in LbidMap:
LbidMap[bid] = {};
TbidMap[bid] = {};
if stars not in LbidMap[bid] :
LbidMap[bid][stars] = {};
if user not in TbidMap[bid] :
TbidMap[bid][user] = {};
# Track information on ratings to each business
LbidMap[bid][stars][user] = review;
TbidMap[bid][user][review] = stars;
(where 'bid' is short for "Business ID", pos_list is an input given by user at runtime)
I then go on and try to create a mapping of users who gave a "positive" review to a business T who also gave business L a rating of X (e.g., 5 people rated business L 4/5 stars, how many of those people also gave a "positive" review to business T?)
For mapping I have the following:
# Determine and map all users who rated business L as rL
# and gave business T a positive rating
print '\nCross matching ratings across businesses';
cross_TrL = [];
for Tbid in TbidMap :
for Lbid in LbidMap :
# Ensure T and L aren't the same business
if Tbid != Lbid :
for stars in LbidMap[Lbid] :
starSum = len(LbidMap[Lbid][stars]);
posTbid = 0;
for user in LbidMap[Lbid][stars] :
if user in TbidMap[Tbid] :
rid = LbidMap[Lbid][stars][user];
print 'Tbid:%s Lbid:%s user:%s rid:%s'%(Tbid, Lbid, user, rid);
reviewRate = TbidMap[Tbid][user][rid];
# If true, then we have pos review for T from L
if reviewRate in pos_list :
posTbid += 1;
numerator = posTbid + 1;
denominator = starSum + 1;
probability = float(numerator) / denominator;
I currently receive the following error (print out of current vars also provided):
Tbid:OlpyplEJ_c_hFxyand_Wxw Lbid:W0eocyGliMbg8NScqERaiA user:Neal_1EVupQKZKv3NsC2DA rid:TAIDnnpBMR16BwZsap9uwA
Traceback (most recent call last):
File "run_edge_testAdvProb.py", line 90, in <module>
reviewRate = TbidMap[Tbid][user][rid];
KeyError: u'TAIDnnpBMR16BwZsap9uwA'
So, I know the KeyError is on what should be the rid (review ID) at that particular moment within TbidMap, however it seems to me that the Key was somehow not included within the first code block of initialization.
What am I doing wrong? Additionally, suggestions on how to improve clock cycles on the second code block is welcomed.
EDIT: I realized that I was trying to locate rid of Tbid using the rid from Lbid, however rid is unique to each review so you would not have a Tbid.rid == Lbid.rid.
Updated the second code block, as such:
cross_TrL = [];
for Tbid in TbidMap :
for Lbid in LbidMap :
# Ensure T and L aren't the same business
if Tbid != Lbid :
# Get numer of reviews at EACH STAR rate for L
for stars in LbidMap[Lbid] :
starSum = len(LbidMap[Lbid][stars]);
posTbid = 0;
# For each review check if user rated the Tbid
for Lreview in LbidMap[Lbid][stars] :
user = LbidMap[Lbid][stars][Lreview];
if user in TbidMap[Tbid] :
# user rev'd Tbid, get their Trid
# and see if they gave Tbid a pos rev
for Trid in TbidMap[Tbid][user] :
# Currently this does not account for multiple reviews
# given by the same person. Just want to get this
# working and then I'll minimize this
Tstar = TbidMap[Tbid][user][Trid];
print 'Tbid:%s Lbid:%s user:%s Trid:%s'%(Tbid, Lbid, user, Trid);
if Tstar in pos_list :
posTbid += 1;
numerator = posTbid + 1;
denominator = starSum + 1;
probability = float(numerator) / denominator;
evaluation = {'Tbid':Tbid, 'Lbid':Lbid, 'star':stars, 'prob':probability}
cross_TrL.append(evaluation);
Still slow, but I no longer receive the error.

Django searching for a string match in a list of dictionary which is returned from queryset.

I have something like this in my view.py
def calculateMark(mobile_a, mobile_b):
#variables
mobile_a = mobile_a
mobile_b = mobile_b
results_a = []
results_b = []
record_a = TechSpecificationAdd.objects.filter(mobile_name=mobile_a).values()
record_b = TechSpecificationAdd.objects.filter(mobile_name=mobile_b).values()
results_a += record_a
results_b += record_b
record_a = record_a[0]
record_b = record_b[0]
if 'Android' in record_a['os']:
os_mark_a = 8.9
elif 'Android' in record_b['os']:
os_mark_b = 8.9
elif 'iOS' in record_a['os']:
os_mark_a = 14
if 'iOS' in record_b['os']:
os_mark_b = 14
else:
os_mark_a = 1
os_mark_b = 1
calculateMark Function will calculate a mark for mobiles. Everything is ok but in case of Operating System (os) testing. Its returning always the else block.
The question is how can I search for a string like "ios" in my record['os'] field?
Correct me if I'm wrong, but I believe your problem is the if in the following line:
if 'iOS' in record_b['os']:
As a result of this code in every case other than iOS in record_b, both os_mark_a and os_mark_b will be set to 1. Even if they were set to another value before, they will be overwritten by 1. To correct this particular problem change the line to:
elif 'iOS' in record_b['os']:
There are other things I find weird:
In most cases you set just one of os_mark_b or os_mark_a, leaving
the other one unset. You could correct this for example by settings
both to a default value first.
You don't need to load the values to a dictionary using values() - just use .get() and use the resulting object directly. Among other benefits this would eliminate lines 6, 7, 10, 11, 12, 13 completely.
Calculations for A and B seem completely separate. It seems it would be wiser to create a smaller method that calucaltes mark for a single object.
Your method doesn't return anything. I presume that's only because it's a simplified example.
What's the following code is about? That doesn't do anything...
mobile_a = mobile_a
mobile_b = mobile_b
Update: Example of code without those issues:
def calculateMark(mobile):
record = TechSpecificationAdd.objects.get(mobile_name=mobile)
if 'Android' in record.os:
return 8.9
if 'iOS' in record.os:
return 14
return 1
Update 2: Also, since we already got so far in making this code better - this shouldn't be a separate function, but a method on a TechSpecificationAdd object.
class TechSpecificationAdd(models.Model):
# some fields
def calculateMark(self):
if 'Android' in self.os:
return 8.9
if 'iOS' in self.os:
return 14
return 1
You would then use this in your code like this:
record = TechSpecificationAdd.objects.get(mobile_name=mobile)
mark = record.calculateMark()

Categories

Resources