SAS Proc Optmodel 约束语法

SAS Proc Optmodel Constraint Syntax

我有一个优化练习正在尝试完成,但又被语法困住了。下面是我的尝试,除了解决方案代码之外,我真的很想对语法进行全面的解释。我认为这是我遇到问题的特定索引部分。

问题: 我有一件商品希望在十周内售罄。我有一个历史趋势,并希望通过降低价格来改变该趋势。我想要最大保证金美元。下面的工作,但我想添加两个约束并且无法整理语法。我在代码中为这两个约束留出了空间,并简要解释了我认为它们可能是什么样子。下面是我需要每个约束做什么的更详细的解释。

inv_cap=每个地点的库存有限。我想全部卖掉。位置 1 为 800,位置 2 为 1200。列的总和 FRC_UNITS 应等于此金额,但不能超过此金额。

price_down_or_same=价格无法反弹,因此需要始终低于或高于前一周。所以,price(i)<=price(i-1) where i=week.

这是我的尝试。预先感谢您的帮助。

*read in data;
data opt_test_mkdown_raw;
    input 
        ITM_NBR
        ITM_DES_TXT $
        LCT_NBR
        WEEK
        LY_UNITS
        ELAST 
        COST
        PRICE
        TOTAL_INV;
cards;
1 stuff 1 1 300 1.2 6 10 800
1 stuff 1 2 150 1.2 6 10 800
1 stuff 1 3 100 1.2 6 10 800
1 stuff 1 4 60 1.2 6 10 800
1 stuff 1 5 40 1.2 6 10 800
1 stuff 1 6 20 1.2 6 10 800
1 stuff 1 7 10 1.2 6 10 800
1 stuff 1 8 10 1.2 6 10 800
1 stuff 1 9 5 1.2 6 10 800
1 stuff 1 10 1 1.2 6 10 800
1 stuff 2 1 400 1.1 6 9 1200
1 stuff 2 2 200 1.1 6 9 1200
1 stuff 2 3 100 1.1 6 9 1200
1 stuff 2 4 100 1.1 6 9 1200
1 stuff 2 5 100 1.1 6 9 1200
1 stuff 2 6 50 1.1 6 9 1200
1 stuff 2 7 20 1.1 6 9 1200
1 stuff 2 8 20 1.1 6 9 1200
1 stuff 2 9 5 1.1 6 9 1200
1 stuff 2 10 3 1.1 6 9 1200
;
run;

data opt_test_mkdown_raw;
    set opt_test_mkdown_raw;
    ITM_LCT_WK=cats(ITM_NBR, LCT_NBR, WEEK);
    ITM_LCT=cats(ITM_NBR, LCT_NBR);
run;

proc optmodel;

*set variables and inputs;

set<string> ITM_LCT_WK;
number ITM_NBR{ITM_LCT_WK};
string ITM_DES_TXT{ITM_LCT_WK};
string ITM_LCT{ITM_LCT_WK};
number LCT_NBR{ITM_LCT_WK};
number WEEK{ITM_LCT_WK}; 
number LY_UNITS{ITM_LCT_WK}; 
number ELAST{ITM_LCT_WK}; 
number COST{ITM_LCT_WK}; 
number PRICE{ITM_LCT_WK};
number TOTAL_INV{ITM_LCT_WK}; 

*read data into procedure;
read data opt_test_mkdown_raw into
    ITM_LCT_WK=[ITM_LCT_WK]
    ITM_NBR
    ITM_DES_TXT
    ITM_LCT
    LCT_NBR
    WEEK
    LY_UNITS
    ELAST
    COST
    PRICE
    TOTAL_INV; 

var NEW_PRICE{i in ITM_LCT_WK};
impvar FRC_UNITS{i in ITM_LCT_WK}=(1-(NEW_PRICE[i]-PRICE[i])*ELAST[i]/PRICE[i])*LY_UNITS[i];

con ceiling_price {i in ITM_LCT_WK}: NEW_PRICE[i]<=PRICE[i];
/*con inv_cap {j in ITM_LCT}: sum{i in ITM_LCT_WK}=I want this to be 800 for location 1 and 1200 for location 2;*/
con supply_last {i in ITM_LCT_WK}: FRC_UNITS[i]>=LY_UNITS[i];
/*con price_down_or_same {j in ITM_LCT} : NEW_PRICE[week]<=NEW_PRICE[week-1];*/

*state function to optimize;
max  margin=sum{i in ITM_LCT_WK}
    (NEW_PRICE[i]-COST[i])*(1-(NEW_PRICE[i]-PRICE[i])*ELAST[i]/PRICE[i])*LY_UNITS[i];

/*expand;*/
solve;

*write output dataset;
create data results_MKD_maxmargin
    from 
    [ITM_LCT_WK]={ITM_LCT_WK} 
    ITM_NBR
    ITM_DES_TXT
    LCT_NBR
    WEEK
    LY_UNITS
    FRC_UNITS
    ELAST
    COST
    PRICE
    NEW_PRICE
    TOTAL_INV; 

*write results to window;
print 
/*NEW_PRICE */
margin;
quit;

主要困难在于,在您的应用程序中,决策是由 (Item,Location) 对和周索引的,但在您的代码中您合并了 (Item,Location,Week) 三元组。我更喜欢数据步骤的使用,但此示例中的结果是您的代码无法引用特定的周数和特定的对。

对代码更改最少的修复方法是使用 OPTMODEL 可以为您计算的定义集和输入来添加这些关系。然后你就会知道哪些三元组指的是(项目,位置)对和周的每个组合:

/* This code creates a set version of the Item x Location pairs 
   that you already have as strings */ 
set ITM_LCTS = setof{ilw in ITM_LCT_WK} itm_lct[ilw];
/* For each Item x Location pair, define a set of which 
   Item x Location x Week entries refer to that Item x Location */
set ILWperIL{il in ITM_LCTS} = {ilw in ITM_LCT_WK: itm_lct[ilw] = il};

有了这个关系,您可以添加其他两个约束。

我保留了您的代码原样,但将我认为有用的约定应用于新代码,尤其是当有类似的名称如 itm_lct 和 ITM_LCTS:

  • 设置为全部大写;
  • 输入参数以小写字母开头;
  • 输出(vars、impvars 和约束)以大写开头 */

这是新的 OPTMODEL 代码:

proc optmodel;

*set variables and inputs;

set<string> ITM_LCT_WK;
number ITM_NBR{ITM_LCT_WK};
string ITM_DES_TXT{ITM_LCT_WK};
string ITM_LCT{ITM_LCT_WK};
number LCT_NBR{ITM_LCT_WK};
number WEEK{ITM_LCT_WK}; 
number LY_UNITS{ITM_LCT_WK}; 
number ELAST{ITM_LCT_WK}; 
number COST{ITM_LCT_WK}; 
number PRICE{ITM_LCT_WK};
number TOTAL_INV{ITM_LCT_WK}; 

*read data into procedure;
read data opt_test_mkdown_raw into
    ITM_LCT_WK=[ITM_LCT_WK]
    ITM_NBR
    ITM_DES_TXT
    ITM_LCT
    LCT_NBR
    WEEK
    LY_UNITS
    ELAST
    COST
    PRICE
    TOTAL_INV; 

var    NEW_PRICE{i in ITM_LCT_WK} <= price[i];
impvar FRC_UNITS{i in ITM_LCT_WK} = 
       (1-(NEW_PRICE[i]-PRICE[i])*ELAST[i]/PRICE[i]) * LY_UNITS[i];

* Moved to bound
con ceiling_price {i in ITM_LCT_WK}: NEW_PRICE[i] <= PRICE[i];

con supply_last{i in ITM_LCT_WK}: FRC_UNITS[i] >= LY_UNITS[i];

/* This code creates a set version of the Item x Location pairs 
   that you already have as strings */ 
set ITM_LCTS = setof{ilw in ITM_LCT_WK} itm_lct[ilw];
/* For each Item x Location pair, define a set of which 
   Item x Location x Week entries refer to that Item x Location */
set ILWperIL{il in ITM_LCTS} = {ilw in ITM_LCT_WK: itm_lct[ilw] = il};
/* I assume that for each item and location 
   the inventory is the same for all weeks for convenience,
   i.e., that is not a coincidence */
num inventory{il in ITM_LCTS} = max{ilw in ILWperIL[il]} total_inv[ilw];
con inv_cap {il in ITM_LCTS}: 
    sum{ilw in ILWperIL[il]} Frc_Units[ilw] = inventory[il]; 

num lastWeek = max{ilw in ITM_LCT_WK} week[ilw];
/* Concatenating indexes is not the prettiest, but gets the job done here*/
con Price_down_or_same {il in ITM_LCTS, w in 2 .. lastWeek}: 
    New_Price[il || w] <= New_Price[il || w - 1];*/

*state function to optimize;
max  margin=sum{i in ITM_LCT_WK}
    (NEW_PRICE[i]-COST[i])*(1-(NEW_PRICE[i]-PRICE[i])*ELAST[i]/PRICE[i])*LY_UNITS[i];

expand;
solve;

*write output dataset;
create data results_MKD_maxmargin
    from 
    [ITM_LCT_WK]={ITM_LCT_WK} 
    ITM_NBR
    ITM_DES_TXT
    LCT_NBR
    WEEK
    LY_UNITS
    FRC_UNITS
    ELAST
    COST
    PRICE
    NEW_PRICE
    TOTAL_INV; 

*write results to window;
print 
    NEW_PRICE FRC_UNITS
    margin
;

quit;