Search code examples
pyomo

Extract data from an output variable


I am trying to figure out a post-processing code to extract data from the output of an indexing-variable in PYOMO. The output of the variable is given below.

x : Size=24, Index=x_index
    Key                  : Lower : Value                  : Upper : Fixed : Stale : Domain
      ('alum', 'Face 1') :     0 :                    0.0 :     1 : False : False : Binary
      ('alum', 'Face 2') :     0 :                    0.0 :     1 : False : False : Binary
      ('alum', 'Face 3') :     0 :                    0.0 :     1 : False : False : Binary
      ('alum', 'Face 4') :     0 :                    0.0 :     1 : False : False : Binary
      ('alum', 'Face 5') :     0 :                    0.0 :     1 : False : False : Binary
      ('alum', 'Face 6') :     0 :                    0.0 :     1 : False : False : Binary
    ('copper', 'Face 1') :     0 :                    1.0 :     1 : False : False : Binary
    ('copper', 'Face 2') :     0 : 1.1025499604398013e-08 :     1 : False : False : Binary
    ('copper', 'Face 3') :     0 :     0.7535049595290465 :     1 : False : False : Binary
    ('copper', 'Face 4') :     0 : 1.0003766762453678e-08 :     1 : False : False : Binary
    ('copper', 'Face 5') :     0 : 1.0265826814190929e-08 :     1 : False : False : Binary
    ('copper', 'Face 6') :     0 :                    1.0 :     1 : False : False : Binary
     ('steel', 'Face 1') :     0 :                    0.0 :     1 : False : False : Binary
     ('steel', 'Face 2') :     0 :                    0.0 :     1 : False : False : Binary
     ('steel', 'Face 3') :     0 :                    0.0 :     1 : False : False : Binary
     ('steel', 'Face 4') :     0 :                    0.0 :     1 : False : False : Binary
     ('steel', 'Face 5') :     0 :                    0.0 :     1 : False : False : Binary
     ('steel', 'Face 6') :     0 :                    0.0 :     1 : False : False : Binary
      ('zinc', 'Face 1') :     0 : 1.0461836921235404e-08 :     1 : False : False : Binary
      ('zinc', 'Face 2') :     0 :                    1.0 :     1 : False : False : Binary
      ('zinc', 'Face 3') :     0 :    0.24649506011873923 :     1 : False : False : Binary
      ('zinc', 'Face 4') :     0 :                    1.0 :     1 : False : False : Binary
      ('zinc', 'Face 5') :     0 :                    1.0 :     1 : False : False : Binary
      ('zinc', 'Face 6') :     0 :  9.618909950291308e-09 :     1 : False : False : Binary

The expected output is a dictionary as shown below

selected_materials = {'Face 1':'copper',
                      'Face 2':'zinc',
                      'Face 3':'copper',
                      'Face 4':'zinc',
                      'Face 5':'zinc',
                      'Face 6':'copper' }

The idea is to choose a material for each face. The selection criteria is the maximum value obtained in the output variable 'x', for each key (combinations of material, face). Eg. For Face 1, the value is compared among 4 materials and the one with highest value is chosen.

My attempt:

I created a code to find the highest value among materials for an individual face as shown below.

max([pyo.value(mdl.x[m, 'Face 1']) for m in materials])

where m is a list as given below (defined in the initial step of the algorithm)

materials = ['steel', 'alum', 'copper', 'zinc']

But finding the material corresponding to the highest value seems challenging. If someone has an idea, kindly help me.

I would appreciate it if you could suggest me some better idea if any.


Solution

  • There are a couple ways to do this. First thing I'd do is pull the values out of the variables into a plain old python data structure, which makes it a bit easier to work with. There are probably a couple variants of the example below that you could implement, depending on how comfortable you are with comprehensions, etc.

    import pyomo.environ as pyo
    from collections import defaultdict
    from operator import itemgetter
    
    matls = ['steel', 'wood']
    
    faces = ['Face 1', 'Face 2']
    
    some_values = { ('steel', 'Face 1') : 1,
                    ('wood', 'Face 1')  : 2.2,
                    ('steel', 'Face 2') : 3.5,
                    ('wood', 'Face 2')  : 1.1}
    
    # PYOMO MODEL
    
    m = pyo.ConcreteModel()
    
    # Sets
    m.M = pyo.Set(initialize=matls)
    m.F = pyo.Set(initialize=faces)
    
    # Variables
    
    # Initializing just for the purposes of sorting later....normally NOT needed
    m.X = pyo.Var(m.M, m.F, domain=pyo.NonNegativeReals, initialize=some_values)
    
    
    m.pprint()
    
    # let's pull the values out into a list of tuples.
    # this isn't totally necessary, but it is pretty clear, and good staring place
    res = [ (face, matl, m.X[matl, face].value) for face in m.F for matl in m.M]
    
    for item in res:
        print(item)
    
    # ok... let's gather the results by face and then get the max.  (you could also sort the results or whatever...
    
    choices = defaultdict(list)
    for face, matl, score in res:
        choices[face].append( (matl, score) )
    
    # pick the max
    for face in choices:
        matl, score = max(choices[face], key=itemgetter(1)) 
        print(f'for face {face} the best material is: {matl} with score {score:.2f}')
    

    Yields:

    1 Var Declarations
        X : Size=4, Index=X_index
            Key                 : Lower : Value : Upper : Fixed : Stale : Domain
            ('steel', 'Face 1') :     0 :     1 :  None : False : False : NonNegativeReals
            ('steel', 'Face 2') :     0 :   3.5 :  None : False : False : NonNegativeReals
             ('wood', 'Face 1') :     0 :   2.2 :  None : False : False : NonNegativeReals
             ('wood', 'Face 2') :     0 :   1.1 :  None : False : False : NonNegativeReals
    
    4 Declarations: M F X_index X
    ('Face 1', 'steel', 1)
    ('Face 1', 'wood', 2.2)
    ('Face 2', 'steel', 3.5)
    ('Face 2', 'wood', 1.1)
    for face Face 1 the best material is: wood with score 2.20
    for face Face 2 the best material is: steel with score 3.50