0
votes

I'm currently a SAS user transitioning into Python, and I'm trying to convert a program that optimizes price given previous demand, price, and elasticity coefficients.

However, I ran into some trouble with formulating the demand constraint.

In this problem, I'm selling apples and/or oranges; however, I can only sell 100 items combined. Moreover, the price for both items are required to be the same price because I'm only allowed one price.

Previously, I had sold 60 apples for $5.00 and 30 Oranges for $5.00, and the elasticity coefficients were -2.5 and -1.5; however, I previously sold 90 fruits, but I'm allowed to sell 100 fruits combined. So what is the price needed to optimize revenue?

I'm using pyomocontrib-simplemodel from https://pyomocontrib-simplemodel.readthedocs.io/en/latest/ and the solver I'm using is the Ipopt binary from https://www.coin-or.org/download/binary/Ipopt/, and I ran the example problems with no issues.

I've tested various sample problems, and I'm able to run them fine.

In SAS, the formula that is used for the constraint originally came from here: http://support.sas.com/documentation/cdl/en/ormpex/66104/HTML/default/viewer.htm#ormpex_ex21_sect009.htm

I modified the SAS code for my needs to look like this (I verified that it works in SAS):

   con Demand_con {i in PRODUCTS}:
      (Demand[i] - prev_demand[i]) / prev_demand[i]  
     = sum {PRODUCTS} elasticity[i] * (Price - prev_price[i]) / prev_price[i];

However, I've been trying to replicate this constraint using the pyomocontrib-simplemodel package in Python.

In Pyomo, my attempt looks like this:

m += ((demand[i]-prev_demand[i])/prev_demand[i] for i in products) == sum(elasticity[i] * ( price - prev_price[i]) / prev_price[i] for i in products)

Here is the full code:

# Price Elasticity
from pyomo.contrib.simplemodel import *

# Input Variables
products = {'Apples','Oranges'}
prev_demand = {'Apples':60, 'Oranges':30}
elasticity = {'Apples':-2.5, 'Oranges':-1.5}
prev_price = {'Apples':5.00, 'Oranges':5.00}
supply = 100

# Create empty model
m = SimpleModel(maximize=True)

# Variables
price = m.var('price', within=NonNegativeReals)
demand = m.var('demand', products, within=NonNegativeReals)

# Objective
m += sum(price*demand[i] for i in products)

# Demand Constraint - Broken
m += ((demand[i]-prev_demand[i])/prev_demand[i] for i in products) == sum(elasticity[i] * ( price - prev_price[i]) / prev_price[i] for i in products) 

#Supply Constraint
m += sum(demand[i] for i in products) <= supply

# Optimize
status = m.solve('ipopt')

# Print the status of the solved NLP
print("Objective = %f" % value(m.objective()))

Here is the error that I'm getting when formulating the demand constraint: AttributeError: 'generator' object has no attribute 'is_expression_type'

I also tried this syntax:

# Demand Constraint - Broken
for i in products:
    m += ((demand[i]-prev_demand[i])/prev_demand[i]) == sum(elasticity[i] * ( price - prev_price[i]) / prev_price[i])

The error I get is: TypeError: 'ProductExpression' object is not iterable

The correct answer should be that the apples and oranges should be priced around at $4.87.

1

1 Answers

0
votes
    # Price Elasticity
    from pyomo.contrib.simplemodel import *

    # Input Variables
    products = {'Apples','Oranges'}
    prev_demand = {'Apples':60, 'Oranges':30}
    elasticity = {'Apples':-2.5, 'Oranges':-1.5}
    prev_price = {'Apples':5.00, 'Oranges':5.00}
    supply = 100

    # Create empty model
    m = SimpleModel(maximize=True)

    # Variables
    price = m.var('price', within=NonNegativeReals)
    demand = m.var('demand', products, within=NonNegativeReals)

    # Objective
    m += sum(price*demand[i] for i in products)


    # Demand Constraint - Broken
    for i in products:
        m += (demand[i]-prev_demand[i])/prev_demand[i] == sum(elasticity[i]  * ( price - prev_price[i])  / prev_price[i] for i in products)


    #Supply Constraint
    m += sum(demand[i] for i in products) <= supply

    # Optimize
    status = m.solve('ipopt')

    # Print the status of the solved NLP
    print("Status = %s" % status.solver.termination_condition)
    print("Objective = %f" % value(m.objective()))
    for i in products:
        print("%s = %f" % (demand[i], value(demand[i])))
    print("%s = %f" % (price, value(price)))

This got me this output:

Status = Optimal, Objective = 486.111115, demand[Apples] = 66.666667, demand[Oranges] = 33.333334, price = 4.86111

Close enough, and I'm sure commercial solvers likes XPRES instead of IPOPT could potentially output a different answer.