BIM production variants#

# install dependencies and select solver
%pip install -q amplpy numpy matplotlib scikit-learn yfinance

SOLVER = "highs"

from amplpy import AMPL, ampl_notebook

ampl = ampl_notebook(
    modules=["highs"],  # modules to install
    license_uuid="default",  # license to use
)  # instantiate AMPL object and register magics

Two variants of the BIM problem: fractional objective and additional fixed costs#

Recall the BIM production model introduced earlier here, that is

\[\begin{split} \begin{array}{rrcrclr} \max \quad & 12x_1 & + & 9x_2 \\ \text{s.t.} \quad & x_1 & & & \leq & 1000 &\text{(silicon)}\\ & & & x_2 & \leq & 1500 &\text{(germanium)}\\ & x_1 & + & x_2 & \leq & 1750 &\text{(plastic)}\\ & 4x_1 & + & 2x_2 & \leq & 4800 &\text{(copper)}\\ & x_1 & , & x_2 & \geq & 0. \end{array} \end{split}\]

Assume the pair \((12,9)\) reflects the sales price (revenues) in € and not the profits made per unit produced. We then need to account for the production costs. Suppose that the production costs for \((x_1,x_2)\) chips are equal to a fixed cost of 100 (independent of the number of units produced) plus \(7/6 x_1\) plus \(5/6 x_2\). It is reasonable to maximize the difference between the revenues and the costs. This approach yields the following linear model:

%%writefile BIM_with_revenues_minus_costs.mod

var x1 >= 0;
var x2 >= 0;

var revenue = 12 * x1 + 9 * x2;
var variable_cost = 7/6 * x1 + 5/6 * x2;

param fixed_cost default 100;

maximize profit: revenue - variable_cost - fixed_cost;

s.t. silicon: x1 <= 1000;
s.t. germanium: x2 <= 1500;
s.t. plastic: x1 + x2 <= 1750;
s.t. copper: 4 * x1 + 2 * x2 <= 4800;
Writing BIM_with_revenues_minus_costs.mod
def BIM_with_revenues_minus_costs():
    m = AMPL()
    m.read("BIM_with_revenues_minus_costs.mod")

    return m


BIM_linear = BIM_with_revenues_minus_costs()
BIM_linear.option["solver"] = SOLVER
BIM_linear.solve()

print(
    "x=({:.1f},{:.1f}) value={:.3f} revenue={:.2f} cost={:.2f}".format(
        BIM_linear.var["x1"].value(),
        BIM_linear.var["x2"].value(),
        BIM_linear.obj["profit"].value(),
        BIM_linear.var["revenue"].value(),
        BIM_linear.var["variable_cost"].value()
        + BIM_linear.param["fixed_cost"].value(),
    )
)
HiGHS 1.5.1:HiGHS 1.5.1: optimal solution; objective 15925
2 simplex iterations
0 barrier iterations
x=(650.0,1100.0) value=15925.000 revenue=17700.00 cost=1775.00

This first model has the same optimal solution as the original BIM model, namely \((650,1100)\) with a revenue of \(17700\) and a cost of \(1775\).

Alternatively, we may aim to optimize the efficiency of the plan, expressed as the ratio between the revenues and the costs:

\[\begin{split} \begin{array}{lll} \max \quad & {\dfrac{12x_1+9x_2}{7/6x_1 + 5/6x_2 + 100}} \\ \text{s.t.} \quad & x_1 \leq 1000 &\text{(silicon)}\\ & x_2 \leq 1500 &\text{(germanium)}\\ & x_1 + x_2 \leq 1750 &\text{(plastic)}\\ & 4x_1 + 2x_2 \leq 4800 &\text{(copper)}\\ & x_1 , x_2 \geq 0. \end{array} \end{split}\]

In order to solve this second version we need to deal with the fraction appearing in the objective function by introducing an auxiliary variable \(t \geq 0\). More specifically, we reformulate the model as follows

\[\begin{split} \begin{array}{rrcrcrclr} \max \quad & 12y_1 & + & 9y_2 \\ \text{s.t.} \quad & y_1 & & & & & \leq & 1000 \cdot t &\text{(silicon)}\\ & & & y_2 & & & \leq & 1500 \cdot t &\text{(germanium)}\\ & y_1 & + & y_2 & & & \leq & 1750 \cdot t &\text{(plastic)}\\ & 4y_1 & + & 2y_2 & & & \leq & 4800 \cdot t &\text{(copper)}\\ &7/6y_1 & + &5/6y_2 & + & 100y & = & 1 & \text{(fraction)} \\ & y_1 & , & y_2 & , & t & \geq & 0 \\ \end{array} \end{split}\]

Despite the change of variables, we can always recover the solution as \((x_1,x_2)= (y_1/t,y_2/t)\).

%%writefile BIM_with_revenues_over_costs.mod

var y1 >= 0;
var y2 >= 0;
var t >= 0;

var revenue = 12 * y1 + 9 * y2;
var variable_cost = 7/6 * y1 + 5/6 * y2;

param fixed_cost default 100;

maximize profit: revenue;

s.t. silicon: y1 <= 1000 * t;
s.t. germanium: y2 <= 1500 * t;
s.t. plastic: y1 + y2 <= 1750 * t;
s.t. copper: 4 * y1 + 2 * y2 <= 4800 * t;

s.t. frac: variable_cost + fixed_cost * t == 1;
Writing BIM_with_revenues_over_costs.mod
def BIM_with_revenues_over_costs():
    m = AMPL()
    m.read("BIM_with_revenues_over_costs.mod")

    return m


BIM_fractional = BIM_with_revenues_over_costs()
BIM_fractional.option["solver"] = SOLVER
BIM_fractional.solve()

t = BIM_fractional.var["t"].value()
y1 = BIM_fractional.var["y1"].value()
y2 = BIM_fractional.var["y2"].value()
profit = BIM_fractional.obj["profit"].value()
variable_cost = BIM_fractional.var["variable_cost"].value()
fixed_cost = BIM_fractional.param["fixed_cost"].value()
revenue = BIM_fractional.var["revenue"].value()

print(
    "x=({:.1f},{:.1f}) value={:.3f} revenue={:.2f} cost={:.2f}".format(
        y1 / t,
        y2 / t,
        profit / (variable_cost + fixed_cost * t),
        revenue / t,
        variable_cost / t + fixed_cost,
    )
)
HiGHS 1.5.1: HiGHS 1.5.1: optimal solution; objective 10.05076142
4 simplex iterations
0 barrier iterations
x=(250.0,1500.0) value=10.051 revenue=16500.00 cost=1641.67

The second model has optimal solution \((250,1500)\) with a revenue of \(16500\) and a cost of \(1641.667\).

The efficiency, measured as the ratio of revenue over costs for the optimal solution, is different for the two models. For the first model the efficiency is equal to \(\frac{17700}{1775}=9.972\), which is strictly smaller than that of the second model, that is \(\frac{16500}{1641.667}=10.051\).