Since non-linear programs are meant to be getting answers that are close rather than the best, I wanted to compare the performance between PROC OPTMODEL and PROC IML to see which one chooses the better answer most of the time, but I'm having trouble wrapping my head around formulating it. On another note, I did figure out the LPSOLVE function and it worked for another linear problem, but I'm having trouble following along this one.
The problem I'm trying to formulate is basically this:
The previous demand for apples and oranges is 60 and 30
The current price is $5, and both have to be priced the same
The elasticity coefficient is -2.5 and -1.5
I am allowed to only sell 100 total fruits
What price should I sell the fruits in order to maximize revenue?
As for the reason I went with NLPDD is because it's the only one that wasn't erroring out, but I'm not exactly sure if it's the correct one to use for this type of problem or if I'm formulating it correctly.
The expected output should be somewhere in the realm of $478 for total revenue at the price of $4.87, which would change the the total demand from 90 to 100.
This is the original PROC OPTMODEL that I'm trying to convert:
/*Previous Previous Price and Demand Data*/
data product_data;
input product $ prev_demand prev_price;
datalines;
Apples 60 5
Oranges 30 5
;
run;
/*Elasticity Data*/
data elasticity_data;
input basket $ elasticity;
datalines;
Apples -2.5
Oranges -1.5
;
run;
/*Supply Data*/
data supply_data;
input supply;
datalines;
100
;
run;
proc optmodel;
set <str> PRODUCTS;
num prev_demand {PRODUCTS};
num prev_price {PRODUCTS};
num supply;
read data product_data into PRODUCTS=[product] prev_demand prev_price;
num elasticity {PRODUCTS};
read data elasticity_data into [basket] elasticity;
read data supply_data(read=suply) into supply;
var Price >= 0;
var Demand {PRODUCTS} >= 0;
max TotalRevenue = sum {product in PRODUCTS} Demand[product] * Price;
con Demand_con {basket in PRODUCTS}:
(Demand[basket] - prev_demand[basket]) / prev_demand[basket]
= sum {PRODUCTS} elasticity[basket] * (Price - prev_price[basket]) / prev_price[basket];
con Supply_con:
sum {product in PRODUCTS} Demand[product] <= supply;
solve with NLP / algorithm=activeset MULTISTART SEED=7888422;
print Price Demand TotalRevenue;
print Demand_con.dual Supply_con.dual;
create data RECOMMENDATION from [basket]={product in PRODUCTS} Price=Price Demand=Demand;
expand;
RUN;
proc print data=RECOMMENDATION;
run;
Here is my attempt at doing this in IML, and I'm sure that a lot of this is a misunderstanding on my part on how this is suppose to work:
proc iml;
title 'Optimal Price Problem';
start Objective(x);
y1 = x[1] * x[2]; /*Apple: x1=Demand, x2=Price*/
y2 = x[1] * x[2]; /*Oranges: x1=Demand, x2=Price*/
f = sum(Y1 + Y2); /*obj: max TotalRevenue = sum {product in PRODUCTS} Demand[product] * Price;*/
return(f);
finish Objective;
start Constraints(x);
g = j(1,2,0.);
e = {-2.5,-1.5};
prev_price = {5};
prev_demand = {60,30};
g[1] = sum((x[1] - prev_demand) / prev_demand) = sum(e * (x[2] - prev_price) / prev_price); /*con1: Demand = prev_demand[i] * (1 + sum {j in PRODUCTS}elasticity[i,j] * (Price[j] - prev_price[j]) / prev_price[j]);*/
return(g);
finish Constraints;
bounds ={1 1 /*Lower bounds of Demand X[1] and PriceX[2]*/
,100 . /*Upper bounds of Demand X[1] and PriceX[2]*/
};
x = {1 1};
opt = {1 2};
call NLPDD (f,Soln,"Objective",x,opt,bounds) grd="Constraints";
print x;
print f;
print Soln;
quit;
call NLPDD ( ) is not designed for this kind of question. it is for continuous object function not DISCRETE(yours) .
For Linear Programming or 0-1 Programming in IML ,you could try
CALL LPSOLVE( )
or
CALL MILPSOLVE( )
But I think PROC OPTMODEL is the most efficient for OR .
@Ksharp wrote:call NLPDD ( ) is not designed for this kind of question. it is for continuous object function not DISCRETE(yours) .
For Linear Programming or 0-1 Programming in IML ,you could try
CALL LPSOLVE( )
or
CALL MILPSOLVE( )
But I think PROC OPTMODEL is the most efficient for OR .
I was under the assumption that it had to be a non-linear solver since that's how it works in PROC OPTMODEL, but now that I think of it, I'm thinking that nlpqua might be the right solver since this formula is quadratic. I'll give that a go, and see if it works.
call nlpqua () is not designed for Operation Research problem.
Hello Ksharp,
I know that I'm brining this back from the dead, but I wanted to post an alternative solution for those that may be trying to solve a problem like this on the University Edition of SAS. I give my thanks for bearing with me before. I admit that I was dead set on finding a way to solve the problem using one of the solvers that IML comes with, and quite honestly, I don't know how every solver works under the hood, so thanks for your patience with me.
As for the alternative solution, here is what I came up.
The idea is to:
1. Create a dataset with every price point within a range by doing a loop in a data step
2. Calculate the outcome of each price point within the loop of the data step
3. Constrain the possible outcomes with a where statement in PROC SQL
4. Sort in DESC order for the best outcome
5. Filter for the price that provided the best outcome
%LET prev_price=5;
%LET prev_apple_demand=60;
%LET prev_orange_demand=30;
%LET apple_elasticity=-2.5;
%LET orange_elasticity=-1.5;
%LET supply=100;
DATA Calc_Demand (drop=suggested_price0);
do suggested_price0=1 to 100000 by 1;
suggested_price=suggested_price0/100;
prev_price=&prev_price;
prev_apple_demand=&prev_apple_demand;
prev_orange_demand=&prev_orange_demand;
apple_elasticity=&apple_elasticity;
orange_elasticity=&orange_elasticity;
prev_total_revenue=(prev_price*prev_apple_demand)+(prev_price*prev_orange_demand);
apple_demand=prev_apple_demand*(1+(apple_elasticity*(suggested_price-prev_price)/prev_price)); /*SAS Example Formula*/
orange_demand=prev_orange_demand*(1+(orange_elasticity*(suggested_price-prev_price)/prev_price)); /*SAS Example Formula*/
total_demand=apple_demand+orange_demand;
total_revenue=(suggested_price*apple_demand)+(suggested_price*orange_demand);
output;
end;
RUN;
PROC SQL;
CREATE TABLE Constrain AS
SELECT t1.* FROM Calc_Demand t1
WHERE t1.total_demand <=100 /*supply constraint*/
AND t1.apple_demand > 0 /*demand constraint*/
AND t1.orange_demand > 0 /*demand constraint*/
AND t1.total_revenue>prev_total_revenue /*Improvement Constraint*/
AND ABS((t1.suggested_price-t1.prev_price)/t1.prev_price)<=0.15 /*business constraint of not allowing more than a 15% change*/
ORDER BY t1.total_revenue DESC;
QUIT;
PROC SQL;
CREATE TABLE WORK.OPTIMAL AS
SELECT Monotonic() AS Rank,
t1.suggested_price,
t1.prev_price,
t1.prev_apple_demand,
t1.prev_orange_demand,
t1.apple_elasticity,
t1.orange_elasticity,
t1.prev_total_revenue,
t1.apple_demand,
t1.orange_demand,
t1.total_demand,
t1.total_revenue
FROM WORK.CONSTRAIN t1
WHERE CALCULATED RANK=1;
QUIT;
TITLE;
TITLE1 "Optimal Price";
PROC PRINT DATA=WORK.OPTIMAL
OBS="Optimal Price"
LABEL;
VAR Rank suggested_price prev_price prev_apple_demand prev_orange_demand apple_elasticity orange_elasticity prev_total_revenue apple_demand orange_demand total_demand total_revenue;
RUN;
The output came out to be very close to what I was getting in PROC OPTMODEL, so this works. It would be nice if the University Edition of SAS would include OR since SAS has very rich documentation on how to solve OR problems.
SAS Innovate 2025 is scheduled for May 6-9 in Orlando, FL. Sign up to be first to learn about the agenda and registration!
Learn how to run multiple linear regression models with and without interactions, presented by SAS user Alex Chaplin.
Find more tutorials on the SAS Users YouTube channel.