Hello,
I hope this quesition is not too long to read through, but I already simplified as much as I could. The following model seeks the minimal costs of a certain mix of products. Costs are only caused by raw materials and manufacturing (manfucturing as a whole). The catch is, that each product can be produced using alternatives of bill-of-materials.
My questions are (also see code below):
1. How do you define a sparse variable matrix using 2 or more parameter sets?
2. How do you slice 2 variables at the same time to create an inflow-/outflow-balance constraint?
* Bill of Materials;
Data BoM;
Input @1 Material $3. @5 Alternative $2. @8 Component $3. @12 Quantity ;
Datalines;
200 02 460 0.7
200 02 400 0.3
200 03 460 0.4
200 03 463 0.4
200 03 401 0.2
202 01 460 0.4
202 01 465 0.3
202 01 409 0.3
202 02 465 0.8
202 02 409 0.2
460 01 410 0.5
460 01 411 0.5
460 02 410 0.7
460 02 411 0.3
460 03 469 1
460 04 400 0.9
460 04 402 0.1
463 01 426 1
465 01 433 0.5
465 01 434 0.5
465 02 434 1
;
Run;
* Demand, costs;
Data D;
Input Product $3. Demand Manufacturing_Costs;
Datalines;
200 100 10
202 70 8
;
Run;
* Supply, costs;
Data S;
Input Raw_Material $3. Supply Input_Costs;
Datalines;
400 30 8
401 100 12
402 50 15
409 40 4
410 34 7
411 80 8
426 40 12
433 50 13
434 60 14
;
Run;
Proc Optmodel;
* read BoM;
Set <Str, Str, Str> _BOM_;
Number Qty{_BOM_};
Read Data BOM NoMiss Into _BOM_=[Material Alternative Component] Qty = Quantity;
Set _Materials_ = SetOf{<m_out, a_out, m_in> in _BOM_} m_out;
Set _Alternatives_ = SetOf{<m_out, a_out, m_in> in _BOM_} a_out;
Set _Components_ = SetOf{<m_out, a_out, m_in> in _BOM_} m_in;
* read Demand;
Set <Str> _Products_;
Number Demand{_Products_};
Number Manuf_Costs{_Products_};
Read Data D Into _Products_=[Product] Demand Manuf_Costs = Manufacturing_Costs;
* read Supply;
Set <Str> _Inputs_;
Number Supply{_Inputs_};
Number Input_Costs{_Inputs_};
Read Data S Into _Inputs_=[Raw_Material] Supply Input_Costs;
* create variables;
Var Flow_Input{_Inputs_} >= 0; * raw material qty used;
Print Supply Input_Costs Flow_Input;
Var Flow_Output{_Products_} >= 0; * products made;
* manufacturing/BoM variable;
* *************;
* QUESTION -1-: Sparse variable definition;
* *************;
Var X{_Materials_, _Alternatives_, _Materials_, _Alternatives_} >= 0; * 1st pair inputs, 2nd pair outputs;
* .. X: for example Material x with Alternative x goes into Material y with Alternative y;
* How do I define this variable properly, for example a material cannot have itself as component?;
* Constraints;
Constraint Input_Balance{i in _Inputs_}: Flow_Input[i] <= Supply[i];
Constraint Output_Balance{p in _Products_}: Flow_Output[p] >= Demand[p];
* *************;
* QUESTION -2-: Define constraint with 2 slices;
* *************;
Constraint Manufacturing_Balance{m_in, a_in in {_Materials_, _Alternatives_}}:
Sum{<(m_in), (a_in), m_out, a_out> in {_Materials_, _Alternatives_, _Materials_, _Alternatives_}}
(X[m_in, a_in, m_out, a_out] + Flow_Input[m_in] ) =
Sum{<m_out, a_out, (m_in), (a_in)> in {_Materials_, _Alternatives_, _Materials_, _Alternatives_}}
(X[m_out, a_out, m_in, a_in] + Flow_Output[m_out] )
;
* How to rectify a constraint, if I have to use 2 slices?;
* Objective;
Min Total_Costs = Sum{i in _Inputs_} Flow_Input[i] * Input_Costs[i] +
Sum{p in _Products_} Flow_Output[p] * Manuf_Costs[p];
*Expand;
Solve;
Quit;
Grateful for any hint. Thx & kind regards
You can correct your syntax in a couple of different ways:
Constraint Manufacturing_Balance{<m_in, a_in> in {_Materials_, _Alternatives_}}:
Constraint Manufacturing_Balance{m_in in _Materials_, a_in in _Alternatives_}:
But the resulting constraint yields errors because some of the indices for Flow_Input and Flow_Output don't exist.
If I understand correctly, you can model this as a side-constrained network problem as follows:
/* network formulation */
set NODES = _Inputs_ union (union {<m_out, a_out, m_in> in _BOM_} {m_out, m_in}) union _Products_;
num supply_demand {i in NODES} = if i in _Inputs_ then Supply[i] else if i in _Products_ then -Demand[i] else 0;
set ARCS = setof {<m_out, a_out, m_in> in _BOM_} <m_in, m_out, a_out>;
num cost {<i,j,k> in ARCS} = (if i in _Inputs_ then Input_Costs[i]) + (if j in _Products_ then Manuf_Costs[j]);
var Flow {ARCS} >= 0;
con FlowBalance {i in NODES}:
sum {<(i),j,k> in ARCS} Flow[i,j,k] - sum {<j,(i),k> in ARCS} Flow[j,i,k] <= supply_demand[i];
min TotalCost = sum {<i,j,k> in ARCS} cost[i,j,k] * Flow[i,j,k];
/* side constraints */
num proportion {<i,j,k> in ARCS} = Qty[j,k,i];
set MA = setof {<m_out, a_out, m_in> in _BOM_} <m_out, a_out>;
var X {MA};
con Linking {<i,j,k> in ARCS}:
Flow[i,j,k] = proportion[i,j,k] * X[j,k];
Please let me know whether the resulting solution makes sense.
You can correct your syntax in a couple of different ways:
Constraint Manufacturing_Balance{<m_in, a_in> in {_Materials_, _Alternatives_}}:
Constraint Manufacturing_Balance{m_in in _Materials_, a_in in _Alternatives_}:
But the resulting constraint yields errors because some of the indices for Flow_Input and Flow_Output don't exist.
If I understand correctly, you can model this as a side-constrained network problem as follows:
/* network formulation */
set NODES = _Inputs_ union (union {<m_out, a_out, m_in> in _BOM_} {m_out, m_in}) union _Products_;
num supply_demand {i in NODES} = if i in _Inputs_ then Supply[i] else if i in _Products_ then -Demand[i] else 0;
set ARCS = setof {<m_out, a_out, m_in> in _BOM_} <m_in, m_out, a_out>;
num cost {<i,j,k> in ARCS} = (if i in _Inputs_ then Input_Costs[i]) + (if j in _Products_ then Manuf_Costs[j]);
var Flow {ARCS} >= 0;
con FlowBalance {i in NODES}:
sum {<(i),j,k> in ARCS} Flow[i,j,k] - sum {<j,(i),k> in ARCS} Flow[j,i,k] <= supply_demand[i];
min TotalCost = sum {<i,j,k> in ARCS} cost[i,j,k] * Flow[i,j,k];
/* side constraints */
num proportion {<i,j,k> in ARCS} = Qty[j,k,i];
set MA = setof {<m_out, a_out, m_in> in _BOM_} <m_out, a_out>;
var X {MA};
con Linking {<i,j,k> in ARCS}:
Flow[i,j,k] = proportion[i,j,k] * X[j,k];
Please let me know whether the resulting solution makes sense.
The model works fine now. Hope this question is not over the top. If I have to add a dimension to the nodes, because the materials can be produced at different sites (like below). How can I combine the plant-material combiniations from diffeent input data sets to a 2-dimensional node-variable/-matrix?
Data BoM;
Input @1 Plant_Mat $4. @6 Material $3. @10 Alternative $2. @13 Plant_Comp $4. @18 Component $3. @17 Quantity;
Datalines;
AAAA 200 02 AAAA 460 0.7
AAAA 200 02 AAAA 400 0.3
AAAA 200 03 AAAA 460 0.4
AAAA 200 03 AAAA 463 0.4
AAAA 200 03 AAAA 401 0.2
AAAA 202 01 AAAA 460 0.4
AAAA 202 01 AAAA 465 0.3
AAAA 202 01 AAAA 409 0.3
AAAA 202 02 AAAA 465 0.8
AAAA 202 02 AAAA 409 0.2
AAAA 460 01 AAAA 410 0.5
AAAA 460 01 AAAA 411 0.5
AAAA 460 03 AAAA 469 1
AAAA 460 04 AAAA 400 0.9
AAAA 460 04 AAAA 402 0.1
AAAA 463 01 AAAA 426 1
AAAA 465 01 AAAA 433 0.5
AAAA 465 01 AAAA 434 0.5
AAAA 465 02 AAAA 434 1
YYYY 200 02 YYYY 460 0.7
YYYY 200 02 YYYY 400 0.3
YYYY 460 01 YYYY 410 0.5
YYYY 460 01 YYYY 411 0.5
YYYY 460 02 YYYY 410 0.7
YYYY 460 02 YYYY 411 0.3
;
Run;
Data D;
Input @1 Plant $4. @6 Product $3. @10 Demand @14 Manufacturing_Costs;
Datalines;
AAAA 200 100 10
YYYY 200 50 4
AAAA 202 70 8
;
Run;
Data S;
Input @1 dummy $4. @6 Raw_Material $3. @10 Supply @14 Input_Costs;
Datalines;
SUPL 400 30 8
SUPL 401 100 12
SUPL 402 50 15
SUPL 409 40 4
SUPL 410 34 7
SUPL 411 80 8
SUPL 426 40 12
SUPL 433 50 13
SUPL 434 60 14
;
Run;
Proc Optmodel;
* read BoM;
Set <Str, Str, Str, Str, Str> _BoM_; * (Plant, Material), Alternative, (Plant, Component);
Number Qty{_BoM_};
Read Data BOM NoMiss Into _BoM_=[Plant_Mat Material Alternative Plant_Comp Component] Qty = Quantity;
Set _PlantsMat_ = SetOf{<p_out, m_out, a_out, p_in, m_in> in _BoM_} p_out;
Set _Materials_ = SetOf{<p_out, m_out, a_out, p_in, m_in> in _BoM_} m_out;
Set _Alternatives_ = SetOf{<p_out, m_out, a_out, p_in, m_in> in _BoM_} a_out;
Set _PlantsComp_ = SetOf{<p_out, m_out, a_out, p_in, m_in> in _BoM_} p_in;
Set _Components_ = SetOf{<p_out, m_out, a_out, p_in, m_in> in _BoM_} m_in;
*Set _Plants_ = _PlantsMat_ union _PlantsComp_;
* read Demand;
Set <Str, Str> _Plant_Products_;
Number Demand{_Plant_Products_};
Number Manuf_Costs{_Plant_Products_};
Read Data D Into _Plant_Products_=[Plant Product] Demand Manuf_Costs = Manufacturing_Costs;
* read Supply;
Set <Str, Str> _Plant_Inputs_;
Number Supply{_Plant_Inputs_};
Number Input_Costs{_Plant_Inputs_};
Read Data S Into _Plant_Inputs_=[dummy Raw_Material] Supply Input_Costs;
Set _Suppliers_ = SetOf{<s, r> in _Plant_Inputs_} s;
Set _Plants_ = _PlantsMat_ union _PlantsComp_ union _Suppliers_; * this does not work;
Var Test_Var(_Plants_); * because this gives an error;
Print Test_Var; * want: SUPL, AAAA, YYYY;
* Syntax error, expecting one of the following: <=, >=, INIT, {. ;
Print Supply Input_Costs;
* Network Model;
Set _dummy_Plant_Materials_ = SetOf{<p_out, m_out, a_out, p_in, m_in> in _BoM_} <p_out, m_out>;
Set _dummy_Plant_Components_ = SetOf{<p_out, m_out, a_out, p_in, m_in> in _BoM_} <p_in, m_in>;
Set _NODES_ = _Plant_Inputs_ union _dummy_Plant_Materials_ union _dummy_Plant_Components_ ;
* Syntax error, expecting one of the following: <=, >=, INIT, {. ;
* ...;
Quit;
The only error was your use of () instead of {} in the VAR statement. The correct syntax is:
Var Test_Var{_Plants_};
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.