Home > Net >  Pyomo Error: The truth value of an array with more than one element is ambiguous. Use a.any() or a.a
Pyomo Error: The truth value of an array with more than one element is ambiguous. Use a.any() or a.a

Time:03-02

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]: 
  • Related