[Pyomo]How to generate a constraints via a loop - pyomo

all
i want to create a set of constraints, as following:
Pg[0] <= Pmax[0]
Pg[1] <= Pmax[1]
Pg[3] <= Pmax[3]
Pg[4] <= Pmax[4]
Pg[5] <= Pmax[5]
and, my code is as following:
import pyomo.environ as pyo
model = pyo.ConcreteModel()
Ngen = 5
Pmax = [40, 170, 520, 200, 100]
model.Ngen = pyo.Set(dimen=Ngen)
model.Pg = pyo.Var(model.Ngen)
def Co1(model):
return ((model.Pg[ii] for ii in model.Ngen) <= (Pmax[jj] for jj in range(Ngen)))
model.Co1 = pyo.Constraint(rule=Co1)
but, the Python Console tells me:
" TypeError: '<=' not supported between instances of 'generator' and 'generator' "
HOW DO I RECORRECT this?
and the other quesiton, if the Pmax is not a list, but a numpy-ndarray. somethings will different?
thanks a lot!

The clue is in the fact that your constraint is defined over a set. Pyomo allows you to pass args to the Constraint constructor that define the set(s) of that constraint.
Therefore your final line should look something like:
model.Co1 = pyo.Constraint(model.Ngen, rule=Co1)
The second issue is the constraint expression itself. Now that this is defined with respect to a Set, you need only express the constraint in terms of a given index (say i), because the rule also expects arguments pertaining to each index of each Set of the Constraint:
def Co1(model, i):
return model.Pg[i] <= Pmax[i]
Finally, the Set you're defining is of size=5, not dimen=5. Sets are usually dimen=1 unless you're working with say arc links of a network in which case they would be of dimension 2. Since your set is defined over integers, the easiest way to add this is via RangeSet which defines all the integers from 1 to N:
model.Ngen = pyo.RangeSet(Ngen)
Given this, the only thing to change is that the list Pmax is 0-indexed and so our constraint will need to account for i being offset by one:
import pyomo.environ as pyo
model = pyo.ConcreteModel()
Ngen = 5
Pmax = [40, 170, 520, 200, 100]
model.Ngen = pyo.RangeSet(Ngen)
model.Pg = pyo.Var(model.Ngen)
def Co1(model, i):
return model.Pg[i] <= Pmax[i - 1]
model.Co1 = pyo.Constraint(model.Ngen, rule = Co1)
model.pprint()
# 1 RangeSet Declarations
# Ngen : Dim=0, Dimen=1, Size=5, Domain=Integers, Ordered=True, Bounds=(1, 5)
# Virtual
#
# 1 Var Declarations
# Pg : Size=5, Index=Ngen
# Key : Lower : Value : Upper : Fixed : Stale : Domain
# 1 : None : None : None : False : True : Reals
# 2 : None : None : None : False : True : Reals
# 3 : None : None : None : False : True : Reals
# 4 : None : None : None : False : True : Reals
# 5 : None : None : None : False : True : Reals
#
# 1 Constraint Declarations
# Co1 : Size=5, Index=Ngen, Active=True
# Key : Lower : Body : Upper : Active
# 1 : -Inf : Pg[1] : 40.0 : True
# 2 : -Inf : Pg[2] : 170.0 : True
# 3 : -Inf : Pg[3] : 520.0 : True
# 4 : -Inf : Pg[4] : 200.0 : True
# 5 : -Inf : Pg[5] : 100.0 : True
#
# 3 Declarations: Ngen Pg Co1
NumPy arrays can be sliced in exactly the same way as lists so the above constraint will still work fine if you change Pmax = np.array([40, 170, 520, 200, 100])

Related

Adding a variable to an existing constraint on pyomo

I have the constraint z_1 + z_2 = 1 in a concrete model. I want to add another variable z_n at the nth step of solving. E.g. the constraint would become z_1 + z_2 + z_3 = 1 in the first step of solving and z_1 + z_2 + z_3 + z_4 = 1 in the next step, and so on.
I really appreciate it if anyone has a suggestion
Thanks a lot!
I am using ConstraintList() method and one way that came to my mind was deleting the constraint in each step and rewriting that. But I am not sure how to do it and if it is efficient or not.
Thanks,
There are a lot of ways to do what you want. The simplest is to update the constraint in question with a new relational expression:
>>> import pyomo.environ as pyo
>>> m = pyo.ConcreteModel()
>>> m.z = pyo.Var(range(5))
>>> m.c = pyo.Constraint(expr=m.z[0] + m.z[1] == 1)
>>> m.c.pprint()
c : Size=1, Index=None, Active=True
Key : Lower : Body : Upper : Active
None : 1.0 : z[0] + z[1] : 1.0 : True
>>> m.c.set_value(m.c.body + m.z[2] == m.c.upper)
>>> m.c.pprint()
c : Size=1, Index=None, Active=True
Key : Lower : Body : Upper : Active
None : 1.0 : z[0] + z[1] + z[2] : 1.0 : True
>>> m.c.set_value(m.c.body + m.z[3] == m.c.upper)
>>> m.c.pprint()
c : Size=1, Index=None, Active=True
Key : Lower : Body : Upper : Active
None : 1.0 : z[0] + z[1] + z[2] + z[3] : 1.0 : True
Alternatively, an approach that requires less manipulation of the model is to use an Expression (named expression) component to hold the constraint body and just update that expression:
>>> m = pyo.ConcreteModel()
>>> m.z = pyo.Var(range(5))
>>> m.e = pyo.Expression(expr=m.z[0] + m.z[1])
>>> m.c = pyo.Constraint(expr=m.e == 1)
>>> m.c.pprint()
c : Size=1, Index=None, Active=True
Key : Lower : Body : Upper : Active
None : 1.0 : e : 1.0 : True
>>> m.e.pprint()
e : Size=1, Index=None
Key : Expression
None : z[0] + z[1]
>>> m.e.expr += m.z[2]
>>> m.c.pprint()
c : Size=1, Index=None, Active=True
Key : Lower : Body : Upper : Active
None : 1.0 : e : 1.0 : True
>>> m.e.pprint()
e : Size=1, Index=None
Key : Expression
None : z[0] + z[1] + z[2]
>>> m.e.expr += m.z[3]
>>> m.c.pprint()
c : Size=1, Index=None, Active=True
Key : Lower : Body : Upper : Active
None : 1.0 : e : 1.0 : True
>>> m.e.pprint()
e : Size=1, Index=None
Key : Expression
None : z[0] + z[1] + z[2] + z[3]

How can you appropriately index a pyomo parameter that is initialized with a list by a multi-dimensional pyomo set?

For my application, I am trying to initialize a parameter for a concrete model using a function rule that outputs a list. The pyomo set I am using to index this parameter is multi-dimensional. It is important for this set to be multi-dimensional because it makes accessing the data structure I am pulling values from for the parameter much simpler. However, when I attempt to index with the set this way, I receive an index error.
Here is a simple test code that illustrates my issue.
First, I have my imports as necessary
## Testing pyomo sets and indexing
import pyomo.environ as pyo
import numpy as np
import itertools as iter
Then I define a concrete model and some numpy arrays
M = pyo.ConcreteModel()
a = np.arange(3)
b = np.arange(3)
Using an itertools product, I generate a list of two-dimensional tuples
T = list(iter.product(a,b))
M.T = pyo.Set(initialize = T)
M.T.pprint()
The output of this print is
T : Size=1, Index=None, Ordered=Insertion
Key : Dimen : Domain : Size : Members
None : 2 : Any : 9 : {(0, 0), (0, 1), (0, 2), (1, 0), (1, 1), (1, 2), (2, 0), (2, 1), (2, 2)}
I define a function that returns a list and define a pyomo parameter that initializes with this function.
def paramListInitialize(M):
List = []
for i in M.T:
List.append(i)
return List
M.Param3 = pyo.Param(M.T, initialize = paramListInitialize(M))
I receive the following error.
ERROR: Rule failed for Param 'Param3' with index 0: KeyError: "Index '0' is
not valid for indexed component 'Param3'"
ERROR: Constructing component 'Param3' from data=None failed: KeyError: "Index
'0' is not valid for indexed component 'Param3'"
KeyError: "Index '0' is not valid for indexed component 'Param3'"
I am confused because I am able to define pyomo variable with this multi-dimensional index set and I can initialize parameters with lists if the index set in one dimensional; however, I am unable to get the pyomo object to associate the tuples in the pyomo set to the values in the initialization list.
It would be very helpful to know why this is not working for me or if there is another way to generate a multi-dimensional pyomo set that is not in the form of a list of tuples.
You need to look more closely at the dox on how to use a rule to initialize here.
The rule needs to return a single value for the index or indices passed in.
Here is an example using your 2-dim set:
import pyomo.environ as pyo
import numpy as np
import itertools as iter
m = pyo.ConcreteModel()
a = np.arange(3)
b = np.arange(3)
T = list(iter.product(a,b))
m.T = pyo.Set(initialize = T)
def example(m, a, b):
return a + b
m.p = pyo.Param(m.T, initialize=example)
m.pprint()
Yields:
1 Set Declarations
T : Size=1, Index=None, Ordered=Insertion
Key : Dimen : Domain : Size : Members
None : 2 : Any : 9 : {(0, 0), (0, 1), (0, 2), (1, 0), (1, 1), (1, 2), (2, 0), (2, 1), (2, 2)}
1 Param Declarations
p : Size=9, Index=T, Domain=Any, Default=None, Mutable=False
Key : Value
(0, 0) : 0
(0, 1) : 1
(0, 2) : 2
(1, 0) : 1
(1, 1) : 2
(1, 2) : 3
(2, 0) : 2
(2, 1) : 3
(2, 2) : 4
2 Declarations: T p

How to add to a set in pyomo

I am trying to run this code (some important part of the code is here):
Master = AbstractModel()
Master.intIndices = Set(initialize=INTVARS)
Master.constraintSet = Set(initialize=CONS)
Master.conIndices = Set(initialize=CONVARS)
Master.intPartList = Set()
Master.dualVarSet = Master.constraintSet * Master.intPartList
Master.theta = Var(domain=Reals, bounds = (None, None))
Master.intVars = Var(Master.intIndices, domain=NonNegativeIntegers, bounds=(0, 10))
Master.dualVars = Var(Master.dualVarSet, domain=Reals, bounds = (None, None))
max_iters = 1000
opt = SolverFactory("couenne")
for i in range(max_iters):
Master.intPartList.add(i)
but it shows me this error on the last line:
RuntimeError: Cannot access add on AbstractOrderedSimpleSet 'intPartList' before it has been constructed (initialized).
Can somebody help me?
You are not initializing Master.intPartList with any data, so you can't update it like that. However, if you make your model a concrete model, and supply an initialization for your set you can...
In [11]: from pyomo.environ import *
In [12]: m2 = ConcreteModel()
In [13]: m2.X = Set(initialize=[1,])
In [14]: m2.X.add(2)
Out[14]: 1
In [15]: m2.X.pprint()
X : Size=1, Index=None, Ordered=Insertion
Key : Dimen : Domain : Size : Members
None : 1 : Any : 2 : {1, 2}
In [16]:

from dictionary to csv but fail to be created

I have a list of dictionaries that contain bacterial name as keys, and as values a set of numbers identifying a DNA sequence. Unluckily, in some dictionaries there is a missing value, and the script fails to produce the csv. Can anyone give me an idea on how I can get around it? This is my script:
import glob, subprocess, sys, os, csv
from Bio import SeqIO, SearchIO
from Bio.Seq import Seq
from Bio.SeqRecord import SeqRecord
def allele():
folders=sorted(glob.glob('path_to_files'))
dict_list=[]
for folder in folders:
fasta_file=glob.glob(folder +'/file.fa')[0]
input_handle=open(fasta_file ,'r')
records=list(SeqIO.parse(input_handle, 'fasta'))
namelist=[]
record_dict={}
sampleID = os.path.basename(folder)
record_dict['sampleid']=sampleID
for record in records:
name=record.description.split('\t')
gene=record.id.split('_')
geneID=gene[0] + '_' +gene[1]
allele=gene[2]
record_dict[geneID]=allele
dict_list.append(record_dict)
header = dict_list[0].keys()
with open('path_to_files/mycsv.csv', 'w') as csv_output:
writer=csv.DictWriter(csv_output,header,delimiter='\t')
writer.writeheader()
for samp in dict_list:
writer.writerow(samp)
print 'start'
allele()
Also can I get any suggestion on how to identify those dictionaries whose values sequence are the same?
Thanks
Concerning your first question, I'd just get the shorter dict's and fill, the missing entry with something that your dictWriter works with (didn't use it, ever), I guess NaN may work.
The simple thing would look like
testDict1 = { "sampleid" : 0,
"bac_s1" : [1,2,4],
"bac_s2" : [1,2,12],
"bac_s3" : [1,3,12],
"bac_s4" : [1,6,12],
"bac_s5" : [1,9,14]
}
testDict2 = { "sampleid" : 1,
"bac_s1" : [1,2,4],
"bac_s2" : [1,3,12],
"bac_s3" : [1,3,12],
"bac_s5" : [2,9,14],
}
testDict3 = { "sampleid" : 2,
"bac_s1" : [3,2,4],
"bac_s2" : [4,2,12],
"bac_s3" : [5,3,12],
"bac_s4" : [1,6,12],
"bac_s5" : [1,9,14]
}
dictList = [ testdict1, testdict2, testdict3 ]
### modified from https://stackoverflow.com/a/16974075/803359
### note, does not tell you elements that are present in shortdict but missing in long dict
### i.e. for your purpose you have to assume that keys in short dict are really present in longdict
def missing_elements( longdict, shortdict ):
list1 = longdict.keys()
list2 = shortdict.keys()
assert len( list1 ) >= len( list2 )
return sorted( set( list1 ).difference( list2 ) )
### make the first dict the longest
dictList = sorted( dictList, key=len, reverse=True )
for myDict in dictList[1:]:
### compare all others to the first
missing = missing_elements( dictList[0], myDict )
print missing
### then you fill with something empty or NaN that works with your save-function
for m in missing:
myDict[m] = [ float( 'nan' ) ]
print " "
for myDict in dictList:
print myDict
print " "
which you can incorporate in your code easily.

Unsupported operand type(s) for +: 'function' and 'int'

I can't figure out why I'm getting this error:
Traceback (most recent call last):
File "C:/Users/tyler/Desktop/PycharmProjects/Arcade Game/AttackDirectory.py", line 63, in <module>
ThiefPdmg = randint(lambda x: (round(x*0.75)), lambda x: (round(x*1.00)))(Thief['stats']['Attack'])
File "C:\Python27\lib\random.py", line 242, in randint
return self.randrange(a, b+1)
TypeError: unsupported operand type(s) for +: 'function' and 'int'
I'm still learning python and I'm working on a battle system for a game. I'm sure the code is crude so if you have any way to make it less crude and possibly work better, I'm open to suggestions.
import random
import pygame
from random import randint
pygame.init()
Thief = {'name' : 'Thief',
'Class' : 'Player',
'Sub-Class' : 'Thief',
'lvl' : 1,
'xp' : 0,
'lvlNext' : 25,
'Weak' : {'Lightning'},
'Normal' : {'Shock'
'Burn'
'Water'
'Freeze'},
'Resistance' : {'Ice'
'Fire'},
'Null' : {'Poison'},
'stats': {'Attack' : 6,
'Magic' : 6,
'Speed' : 5,
'HP' : 20,
'MP' : 40}}
def ThiefLevelUp():
while Thief['lvl'] < 50 and Thief['xp'] >= Thief['lvlNext'] :
Thief['lvl']+= 1
xp = Thief['xp'] - Thief['lvlNext']
lvlNext = round(Thief['lvlNext'] * 1.5)
Thief['stats']['HP'] = Thief['stats']['HP'] + round(Thief['stats']['HP'] * .25)
print("Level Up!")
print('Level:', Thief['lvl'])
if Thief['xp'] < Thief['lvlNext']:
print('XP Needed:', Thief['xpNeeded'])
print('Exp:', Thief['xp'])
print('Strength:', Thief['stats']['PStrength'])
print('Prosperity:', Thief['stats']['PProsperity'])
print('Dexterity:', Thief['stats']['PDexterity'])
print('Vitality:', Thief['stats']['PVitality'])
print('Agility:', Thief['stats'][''])
Imp = {'name' : 'Imp',
'Class' : 'Enemy',
'lvl' : 1,
'xp' : 0,
'lvlNext' : 25,
'Weak' : {'Lightning'},
'Normal' : {'Fire',
'Ice',
'Poison',
'Freeze',
'Burn'},
'Resistance' : {},
'Null' : {},
'stats' : {'Attack' : 8,
'Speed' : 5,
'HP' : 25,
'MP' : 40}}
ThiefPdmg = randint(lambda x: (round(x*0.75)), lambda x: (round(x*1.00)))(Thief['stats']['Attack'])
ThiefMdmg = randint(lambda x: (round(x*0.75)), lambda x: (round(x*1.00)))(Thief['stats']['Magic'])
ThiefHP = Thief['stats']['HP']
ImpDMG = randint(lambda x: (round(x*0.75 / Thief['lvl'] + 1, lambda x: (round(x*0.75 / Thief['lvl'] + 1)) )))(Imp['stats']['Attack'])
ImpHP = Imp['stats']['HP']
def AtkMiss():
if random.random > .25:
Attack()
else:
print("You Missed!")
if random.random > .25:
ThiefHP - ImpDMG
ThiefHP = Thief['stats']['HP'] - ImpDMG
def Attack():
AttackerDMG = ThiefPdmg
print('You Attacked!')
if Thief['stats']['Speed'] >= Imp['stats']['Speed']:
ImpHP - ThiefPdmg
ImpHP = Imp['stats']['HP'] - ThiefPdmg
if random.random > .25:
ThiefHP - ImpDMG
ThiefHP = Thief['stats']['HP'] - ImpDMG
if Imp['stats']['Speed'] > Thief['stats']['Speed'] :
ThiefHP - ImpDMG
print('Thief Took {} Damage!'.format(ImpDMG))
if random.random > .25:
ImpHP - ThiefPdmg
print('Imp Took {} Damage!'.format(ThiefPdmg))
else:
print('The Attack Missed!')
if ImpHP <= 0:
print('{} Was Killed!'.format(Imp['name']))
if ThiefHP <= 0:
print('You Were Killed!')
def command():
cmd = raw_input('What Will You Do?')
if 'Atk' in cmd:
AtkMiss()
else:
Pass
def Battle():
Attackerhp = Thief['stats']['HP']
Defenderhp = Imp['stats']['HP']
print('An imp appeared!')
print(' ')
while Defenderhp and Attackerhp > 0:
command()
print('Your Health: {}'.format(ThiefHP))
print('Enemies Health: {}'.format(ImpHP))
if Defenderhp <= Defenderhp:
print('Took {} Damage!'.format(Enemy['name'], Dmg or Mdmg))
Where is the error coming from?
The randint [1] function expects for int numbers to be passed as its arguments. Instead, you are passing lambda functions which appear to be unnecessary. Instead of randint(lambda x: round(x*0.25), lambda x: round(x*0.75)), for example, just do randint(round(x*0.25), round(x*0.75)). Only use lambda where something expects a function to be passed to it, like a callback.
https://docs.python.org/2/library/random.html#random.randint