I am building an optimization model in pyomo and keep facing this error which I cannot solve. Here is the part where the error occurs:
model.ct2demand = ConstraintList()
for n in model.N:
for s in model.S:
for t in model.T:
for p in model.P:
lhs = model.f[p,t,s,n]*1000
rhs = model.y[p,t,s,n] model.sales[p,t,s,n] model.error[p,t,s,n]
model.ct2demand.add (lhs == rhs)
the variable f and error are multidimensional parameters (non-negative reals), which I entered as numpy arrays and think are causing this issue based on my research, but I haven't really figured out exactly why. y and sales are decision variables. And the following is the error message:
ValueError Traceback (most recent call last)
~\AppData\Local\Temp/ipykernel_21076/2031668078.py in <module>
422 for p in model.P:
423 lhs = model.f[p,t,s,n]
--> 424 rhs = model.y[p,t,s,n] model.sales[p,t,s,n] model.error[p,t,s,n]
425 model.ct2demand.add (lhs == rhs)
426
pyomo\core\expr\numvalue.pyx in pyomo.core.expr.numvalue.NumericValue.__add__()
pyomo\core\expr\numeric_expr.pyx in pyomo.core.expr.numeric_expr._generate_sum_expression()
pyomo\core\expr\numeric_expr.pyx in pyomo.core.expr.numeric_expr.SumExpression.add()
ValueError: The truth value of an array with more than one element is ambiguous. Use a.any() or a.all()
I did some research but still didn't really get what is causing the issue here. Any guidance would be highly appreciated!
CodePudding user response:
So here is what is going on. When you are initializing your parameter, you are failing to pass in a key:value pairing (like a dictionary) so pyomo
is just initializing each element in your parameter to the whole array which is clearly not what you want.
Most of the time, the indexing sets are not just counting numbers, so this doesn't really pop up too often, because if your index is {'A', 'B', 'C', ...} then it becomes clear that you must pass in a key:value mapping in order to straighten that out.
Here are a couple lines to demo what I'm talking about. Bottom line: get numpy
out of your model and make a dictionary of the stuff you want to use in the parameter. (This can also be done w/ pandas
, but it isn't necessary). List of commands at end if you wish to copy/replicate/experiment.
In [1]: import pyomo.environ as pyo
In [2]: import numpy as np
In [3]: np_data = np.array([[1, 2], [3, 4]])
In [4]: m = pyo.ConcreteModel()
In [5]: m.S = pyo.Set(initialize=range(2))
In [6]: m.T = pyo.Set(initialize=range(2))
In [7]: # let's make a parameter from the data...
In [8]: m.p = pyo.Param(m.S, m.T, initialize=np_data)
WARNING: DEPRECATED: The default domain for Param objects is 'Any'. However,
we will be changing that default to 'Reals' in the future. If you really
intend the domain of this Param (p) to be 'Any', you can suppress this
warning by explicitly specifying 'within=Any' to the Param constructor.
(deprecated in 5.6.9, will be removed in 6.0) (called from
/Library/Frameworks/Python.framework/Versions/3.9/lib/python3.9/site-
packages/pyomo/core/base/indexed_component.py:665)
In [9]: # note we get this odd warning about an "any" type. This is trigge
...: red by fact that we are initializing EACH item to the whole array
In [10]: m.p[0,1]
Out[10]:
array([[1, 2],
[3, 4]])
In [11]: m.p[1,1]
Out[11]:
array([[1, 2],
[3, 4]])
In [12]: # not good! pyomo wants a key:value pair
In [13]: my_better_data = {(0, 0) : 1,
...: (0, 1) : 2,
...: (1, 0) : 3,
...: (1, 1) : 4}
In [14]: m.p2 = pyo.Param(m.S, m.T, initialize=my_better_data)
In [15]: m.p2[0,1]
Out[15]: 2
In [16]: %hist
import pyomo.environ as pyo
import numpy as np
np_data = np.array([[1, 2], [3, 4]])
m = pyo.ConcreteModel()
m.S = pyo.Set(initialize=range(2))
m.T = pyo.Set(initialize=range(2))
# let's make a parameter from the data...
m.p = pyo.Param(m.S, m.T, initialize=np_data)
# note we get this odd warning about an "any" type. This is triggered by fact that we are initializing EACH item to the whole array
m.p[0,1]
m.p[1,1]
# not good! pyomo wants a key:value pair
my_better_data = {(0, 0) : 1,
(0, 1) : 2,
(1, 0) : 3,
(1, 1) : 4}
m.p2 = pyo.Param(m.S, m.T, initialize=my_better_data)
m.p2[0,1]
%hist
In [17]:
Edit to show conversion of multi-dim np array to dict
You can convert the np.array
back to a dictionary only in the special case where the indices all line up to equivalent sets very easily. just double check that the indexing is happening as you like by sampling a few values or printing out the parameter completely with model.p.pprint()
In [30]: data = list(range(12)) # some fake data
In [31]: data_np = np.array(data)
In [32]: data_np = data_np.reshape(3,2,2)
In [33]: data_np
Out[33]:
array([[[ 0, 1],
[ 2, 3]],
[[ 4, 5],
[ 6, 7]],
[[ 8, 9],
[10, 11]]])
In [34]: data_dict = dict(np.ndenumerate(data_np))
In [35]: data_dict
Out[35]:
{(0, 0, 0): 0,
(0, 0, 1): 1,
(0, 1, 0): 2,
(0, 1, 1): 3,
(1, 0, 0): 4,
(1, 0, 1): 5,
(1, 1, 0): 6,
(1, 1, 1): 7,
(2, 0, 0): 8,
(2, 0, 1): 9,
(2, 1, 0): 10,
(2, 1, 1): 11}
In [36]: