【问题标题】:SAS Proc Optmodel Constraint SyntaxSAS Proc Optmodel 约束语法
【发布时间】:2015-03-14 02:31:20
【问题描述】:

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

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

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

price_down_or_same=价格不能反弹,因此它需要始终小于或大于前一周。所以,price(i)

这是我的尝试。提前感谢您的帮助。

*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;

【问题讨论】:

    标签: optimization sas


    【解决方案1】:

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

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

    /* 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;
    

    【讨论】:

    • 感谢您的详尽解释和解决方案。我曾尝试使用串联作为在过程中创建集合的一种方式。我现在意识到,这不是一个好方法。此外,我特别喜欢连接索引以获得 Price_down_or_same 约束的想法。这是非常好的东西。我非常感谢所有的帮助。
    • 另外,我喜欢使用约定来轻松区分参数、集合和输出的想法。好主意。
    • Leo,我想知道如何实现最低/最高降价。规定 new_price 必须是 a。与之前的价格相同或 b.至少比之前的价格低 10%(但不能超过,比如 50%)。示例:第 1 周的价格是 10 美元,那么第 2 周的新价格可以是 10 美元或 5 美元到 9.90 美元之间的任何值。这样,价格不会下降千分之一美分。有没有一种简单的方法来实现对 Price_down_or_the_same 约束的这种修改,还是更复杂?
    • 不客气!乐意效劳。这是一个有趣的模型。为了从您的评论中实施定价更改规则,您需要更改的不仅仅是 Price_down_or_the_same。 OPTMODEL的变化不难表达。不幸的是,新规则的基础数学更具挑战性。也可能需要一些业务背景才能使更改在实际问题规模下表现得不出所料。例如,也许这个模型的洞察力是,对于这些产品,折扣并不那么重要;或者可能是输入数据需要工作。
    • 再次感谢。我将继续研究如何在 optmodel 过程中制定这种“非此即彼”类型约束。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多