{
"cells": [
{
"cell_type": "markdown",
"metadata": {},
"source": [
"```{index} single: AMPL; sets\n",
"```\n",
"```{index} single: AMPL; parameters\n",
"```\n",
"```{index} single: solver; highs\n",
"```\n",
"```{index} single: application; production planning\n",
"```\n",
"```{index} pandas dataframe\n",
"```\n",
"\n",
"# BIM production revisited"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"# install dependencies and select solver\n",
"%pip install -q amplpy\n",
"\n",
"SOLVER = \"highs\"\n",
"\n",
"from amplpy import AMPL, ampl_notebook\n",
"\n",
"ampl = ampl_notebook(\n",
" modules=[\"highs\"], # modules to install\n",
" license_uuid=\"default\", # license to use\n",
") # instantiate AMPL object and register magics"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Problem description\n",
"\n",
"We consider BIM raw material planning, but now with more sophisticated pricing and acquisition protocols. There are now three suppliers, each of which can deliver the following materials:\n",
" - A: **silicon**, **germanium** and **plastic**\n",
" - B: **copper**\n",
" - C: all of the above\n",
" \n",
"For the suppliers, the following conditions apply. Copper should be acquired in multiples of 100 gram, since it is delivered in sheets of 100 gram. Unitary materials such as silicon, germanium and plastic may be acquired in any number, but the price is in batches of 100. Meaning that 30 units of silicon with 10 units of germanium and 50 units of plastic cost as much as 1 unit of silicon but half as much as 30 units of silicon with 30 units of germanium and 50 units of plastic. Furthermore, supplier C sells all materials and offers a discount if purchased together: 100 gram of copper and a batch of unitary material cost just 7. This set price is only applied to pairs, meaning that 100 gram of copper and 2 batches cost 13.\n",
"\n",
"The summary of the prices in € is given in the following table:\n",
"\n",
"|Supplier|Copper per sheet of 100 gram|Batch of units|Together|\n",
"|:-------|---------------------:|-----------------:|-------:|\n",
"| A | - | 5 | - |\n",
"| B | 3 | - | - |\n",
"| C | 4 | 6 | 7 |\n",
"\n",
"Next, for stocked products inventory costs are incurred, whose summary is given in the following table:\n",
"\n",
"|Copper per 10 gram| Silicon per unit| Germanium per unit|Plastic per unit|\n",
"|---:|-------:|---:|-----:|\n",
"| 0.1| 0.02 |0.02| 0.02 |\n",
"\n",
"The holding price of copper is per 10 gram and the copper stocked is rounded up to multiples of 10 grams, meaning that 12 grams pay for 20. \n",
"\n",
"The capacity limitations of the warehouse allow for a maximum of $10$ kilogram of copper in stock at any moment, but there are no practical limitations to the number of units of unitary products in stock.\n",
"\n",
"Recall that BIM has the following stock at the beginning of the year:\n",
"\n",
"|Copper |Silicon |Germanium |Plastic|\n",
"|---:|-------:|---:|-----:|\n",
"| 480| 1000 |1500| 1750 |\n",
"\n",
"The company would like to have at least the following stock at the end of the year:\n",
"\n",
"|Copper |Silicon |Germanium |Plastic|\n",
"|---:|-------:|---:|-----:|\n",
"| 200| 500 | 500| 1000 |\n",
"\n",
"The goal is to build an optimization model using the data above and solve it to minimize the acquisition and holding costs of the products while meeting the required quantities for production. The production is made-to-order, meaning that no inventory of chips is kept.\n"
]
},
{
"cell_type": "code",
"execution_count": 91,
"metadata": {
"colab": {
"base_uri": "https://localhost:8080/",
"height": 143
},
"id": "rvWwY74i7qEy",
"outputId": "47766087-eb87-44e7-d3e2-ac2c00af78f4"
},
"outputs": [
{
"data": {
"text/html": [
"
\n",
"\n",
"
\n",
" \n",
" \n",
" \n",
" Jan \n",
" Feb \n",
" Mar \n",
" Apr \n",
" May \n",
" Jun \n",
" Jul \n",
" Aug \n",
" Sep \n",
" Oct \n",
" Nov \n",
" Dec \n",
" \n",
" \n",
" chip \n",
" \n",
" \n",
" \n",
" \n",
" \n",
" \n",
" \n",
" \n",
" \n",
" \n",
" \n",
" \n",
" \n",
" \n",
" \n",
" \n",
" logic \n",
" 88 \n",
" 125 \n",
" 260 \n",
" 217 \n",
" 238 \n",
" 286 \n",
" 248 \n",
" 238 \n",
" 265 \n",
" 293 \n",
" 259 \n",
" 244 \n",
" \n",
" \n",
" memory \n",
" 47 \n",
" 62 \n",
" 81 \n",
" 65 \n",
" 95 \n",
" 118 \n",
" 86 \n",
" 89 \n",
" 82 \n",
" 82 \n",
" 84 \n",
" 66 \n",
" \n",
" \n",
"
\n",
"
"
],
"text/plain": [
" Jan Feb Mar Apr May Jun Jul Aug Sep Oct Nov Dec\n",
"chip \n",
"logic 88 125 260 217 238 286 248 238 265 293 259 244\n",
"memory 47 62 81 65 95 118 86 89 82 82 84 66"
]
},
"metadata": {},
"output_type": "display_data"
}
],
"source": [
"from io import StringIO\n",
"import pandas as pd\n",
"\n",
"demand_data = \"\"\"\n",
"chip, Jan, Feb, Mar, Apr, May, Jun, Jul, Aug, Sep, Oct, Nov, Dec\n",
"logic, 88, 125, 260, 217, 238, 286, 248, 238, 265, 293, 259, 244\n",
"memory, 47, 62, 81, 65, 95, 118, 86, 89, 82, 82, 84, 66\n",
"\"\"\"\n",
"\n",
"demand_chips = pd.read_csv(StringIO(demand_data), index_col=\"chip\")\n",
"display(demand_chips)"
]
},
{
"cell_type": "code",
"execution_count": 92,
"metadata": {
"colab": {
"base_uri": "https://localhost:8080/",
"height": 174
},
"id": "I0Wl0BXOlv6j",
"outputId": "2f785ea2-a004-4cd6-a65b-75eee443ef71"
},
"outputs": [
{
"data": {
"text/html": [
"\n",
"\n",
"
\n",
" \n",
" \n",
" \n",
" logic \n",
" memory \n",
" \n",
" \n",
" \n",
" \n",
" silicon \n",
" 1 \n",
" 0 \n",
" \n",
" \n",
" plastic \n",
" 1 \n",
" 1 \n",
" \n",
" \n",
" copper \n",
" 4 \n",
" 2 \n",
" \n",
" \n",
" germanium \n",
" 0 \n",
" 1 \n",
" \n",
" \n",
"
\n",
"
"
],
"text/plain": [
" logic memory\n",
"silicon 1 0\n",
"plastic 1 1\n",
"copper 4 2\n",
"germanium 0 1"
]
},
"metadata": {},
"output_type": "display_data"
}
],
"source": [
"use = dict()\n",
"use[\"logic\"] = {\"silicon\": 1, \"plastic\": 1, \"copper\": 4}\n",
"use[\"memory\"] = {\"germanium\": 1, \"plastic\": 1, \"copper\": 2}\n",
"use = pd.DataFrame.from_dict(use).fillna(0).astype(int)\n",
"display(use)"
]
},
{
"cell_type": "code",
"execution_count": 93,
"metadata": {
"colab": {
"base_uri": "https://localhost:8080/",
"height": 174
},
"id": "m8daTOpBlv6k",
"outputId": "3bff6de3-8759-4dde-95f9-321d3231ccc7"
},
"outputs": [
{
"data": {
"text/html": [
"\n",
"\n",
"
\n",
" \n",
" \n",
" \n",
" Jan \n",
" Feb \n",
" Mar \n",
" Apr \n",
" May \n",
" Jun \n",
" Jul \n",
" Aug \n",
" Sep \n",
" Oct \n",
" Nov \n",
" Dec \n",
" \n",
" \n",
" \n",
" \n",
" silicon \n",
" 88 \n",
" 125 \n",
" 260 \n",
" 217 \n",
" 238 \n",
" 286 \n",
" 248 \n",
" 238 \n",
" 265 \n",
" 293 \n",
" 259 \n",
" 244 \n",
" \n",
" \n",
" plastic \n",
" 135 \n",
" 187 \n",
" 341 \n",
" 282 \n",
" 333 \n",
" 404 \n",
" 334 \n",
" 327 \n",
" 347 \n",
" 375 \n",
" 343 \n",
" 310 \n",
" \n",
" \n",
" copper \n",
" 446 \n",
" 624 \n",
" 1202 \n",
" 998 \n",
" 1142 \n",
" 1380 \n",
" 1164 \n",
" 1130 \n",
" 1224 \n",
" 1336 \n",
" 1204 \n",
" 1108 \n",
" \n",
" \n",
" germanium \n",
" 47 \n",
" 62 \n",
" 81 \n",
" 65 \n",
" 95 \n",
" 118 \n",
" 86 \n",
" 89 \n",
" 82 \n",
" 82 \n",
" 84 \n",
" 66 \n",
" \n",
" \n",
"
\n",
"
"
],
"text/plain": [
" Jan Feb Mar Apr May Jun Jul Aug Sep Oct Nov \\\n",
"silicon 88 125 260 217 238 286 248 238 265 293 259 \n",
"plastic 135 187 341 282 333 404 334 327 347 375 343 \n",
"copper 446 624 1202 998 1142 1380 1164 1130 1224 1336 1204 \n",
"germanium 47 62 81 65 95 118 86 89 82 82 84 \n",
"\n",
" Dec \n",
"silicon 244 \n",
"plastic 310 \n",
"copper 1108 \n",
"germanium 66 "
]
},
"metadata": {},
"output_type": "display_data"
}
],
"source": [
"demand = use.dot(demand_chips)\n",
"display(demand)"
]
},
{
"cell_type": "code",
"execution_count": 94,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"Overwriting BIMproduction_v1.mod\n"
]
}
],
"source": [
"%%writefile BIMproduction_v1.mod\n",
"\n",
"set periods ordered;\n",
"set products;\n",
"set supplying_batches;\n",
"set supplying_copper;\n",
"set unitary_products;\n",
"\n",
"param delta{products, periods}; # demand\n",
"param pi{supplying_batches}; # aquisition price\n",
"param kappa{supplying_copper}; # price of copper sheet\n",
"param beta;\n",
"param gamma{products}; # unitary holding costs\n",
"param Alpha{products}; # initial stock\n",
"param Omega{products}; # desired stock\n",
"param copper_bucket_size;\n",
"param stock_limit;\n",
"param batch_size;\n",
"param copper_sheet_mass;\n",
"\n",
"var y{periods, supplying_copper} integer >= 0;\n",
"var x{unitary_products, periods, supplying_batches} >= 0;\n",
"var ss{products, periods} >= 0;\n",
"var uu{products, periods} >= 0;\n",
"var bb{periods, supplying_batches} integer >= 0;\n",
"var pp{periods} integer >= 0;\n",
"var rr{periods} integer >= 0;\n",
"\n",
"s.t. units_in_batches {t in periods, s in supplying_batches}:\n",
" sum{p in unitary_products} x[p,t,s] <= batch_size * bb[t,s];\n",
"s.t. copper_in_buckets {t in periods}:\n",
" ss['copper', t] <= copper_bucket_size * rr[t];\n",
"s.t. inventory_capacity {t in periods}:\n",
" ss['copper', t] <= stock_limit;\n",
"s.t. pairs_in_batches {t in periods}:\n",
" pp[t] <= bb[t,'C'];\n",
"s.t. pairs_in_sheets {t in periods}:\n",
" pp[t] <= y[t,'C'];\n",
"s.t. bought {t in periods, p in products}:\n",
" (if p == 'copper' then\n",
" copper_sheet_mass * sum{s in supplying_copper} y[t,s]\n",
" else\n",
" sum{s in supplying_batches} x[p,t,s])\n",
" == uu[p,t];\n",
"\n",
"var acquisition_cost =\n",
" sum{t in periods}(\n",
" sum{s in supplying_batches} pi[s] * bb[t,s] +\n",
" sum{s in supplying_copper} kappa[s] * y[t,s] -\n",
" beta * pp[t]\n",
" );\n",
"var inventory_cost =\n",
" sum{t in periods}(\n",
" gamma['copper'] * rr[t] + \n",
" sum{p in unitary_products} gamma[p] * ss[p,t]\n",
" );\n",
"\n",
"minimize total_cost: acquisition_cost + inventory_cost;\n",
" \n",
"s.t. balance {p in products, t in periods}:\n",
" (if t == first(periods) then\n",
" Alpha[p]\n",
" else\n",
" ss[p,prev(t)]) +\n",
" uu[p,t] == delta[p,t] + ss[p,t];\n",
"s.t. finish {p in products}:\n",
" ss[p, last(periods)] >= Omega[p];"
]
},
{
"cell_type": "code",
"execution_count": 95,
"metadata": {},
"outputs": [],
"source": [
"def BIMproduction_v1(\n",
" demand,\n",
" existing,\n",
" desired,\n",
" stock_limit,\n",
" supplying_copper,\n",
" supplying_batches,\n",
" price_copper_sheet,\n",
" price_batch,\n",
" discounted_price,\n",
" batch_size,\n",
" copper_sheet_mass,\n",
" copper_bucket_size,\n",
" unitary_products,\n",
" unitary_holding_costs,\n",
"):\n",
" m = AMPL()\n",
" m.read(\"BIMproduction_v1.mod\")\n",
"\n",
" m.set[\"periods\"] = demand.columns\n",
" m.set[\"products\"] = demand.index\n",
" m.param[\"delta\"] = demand\n",
"\n",
" m.set[\"supplying_batches\"] = supplying_batches\n",
" m.param[\"pi\"] = price_batch\n",
"\n",
" m.set[\"supplying_copper\"] = supplying_copper\n",
" m.param[\"kappa\"] = price_copper_sheet\n",
"\n",
" m.param[\"beta\"] = price_batch[\"C\"] + price_copper_sheet[\"C\"] - discounted_price\n",
"\n",
" m.set[\"unitary_products\"] = unitary_products\n",
" m.param[\"gamma\"] = unitary_holding_costs\n",
"\n",
" m.param[\"Alpha\"] = existing\n",
" m.param[\"Omega\"] = desired\n",
"\n",
" m.param[\"batch_size\"] = batch_size\n",
" m.param[\"copper_bucket_size\"] = copper_bucket_size\n",
" m.param[\"stock_limit\"] = stock_limit\n",
" m.param[\"copper_sheet_mass\"] = copper_sheet_mass\n",
"\n",
" return m"
]
},
{
"cell_type": "code",
"execution_count": 96,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"HiGHS 1.5.1: tech:outlev=1\n",
"Running HiGHS 1.5.1 [date: 2023-06-22, git hash: 93f1876]\n",
"Copyright (c) 2023 HiGHS under MIT licence terms\n",
"Presolving model\n",
"152 rows, 236 cols, 444 nonzeros\n",
"113 rows, 197 cols, 366 nonzeros\n",
"96 rows, 156 cols, 285 nonzeros\n",
"\n",
"Solving MIP model with:\n",
" 96 rows\n",
" 156 cols (0 binary, 72 integer, 23 implied int., 61 continuous)\n",
" 285 nonzeros\n",
"\n",
" Nodes | B&B Tree | Objective Bounds | Dynamic Constraints | Work \n",
" Proc. InQueue | Leaves Expl. | BestBound BestSol Gap | Cuts InLp Confl. | LpIters Time\n",
"\n",
" 0 0 0 0.00% -inf inf inf 0 0 0 0 0.0s\n",
" R 0 0 0 0.00% 109104 114244 4.50% 0 0 0 67 0.1s\n",
" L 0 0 0 0.00% 109957.085557 110216 0.23% 926 61 0 331 0.9s\n",
"\n",
"2.8% inactive integer columns, restarting\n",
"Model after restart has 92 rows, 147 cols (11 bin., 58 int., 21 impl., 57 cont.), and 270 nonzeros\n",
"\n",
" 0 0 0 0.00% 109957.143248 110216 0.23% 32 0 0 466 0.9s\n",
" 0 0 0 0.00% 110065.335725 110216 0.14% 32 15 2 500 0.9s\n",
" L 0 0 0 0.00% 110065.431752 110216 0.14% 48 16 2 503 1.0s\n",
"\n",
"13.0% inactive integer columns, restarting\n",
"Model after restart has 71 rows, 118 cols (12 bin., 46 int., 7 impl., 53 cont.), and 218 nonzeros\n",
"\n",
" 0 0 0 0.00% 110065.431752 110216 0.14% 16 0 0 695 1.0s\n",
" 0 0 0 0.00% 110065.431752 110216 0.14% 16 15 0 718 1.0s\n",
"\n",
"13.8% inactive integer columns, restarting\n",
"Model after restart has 57 rows, 96 cols (3 bin., 42 int., 6 impl., 45 cont.), and 173 nonzeros\n",
"\n",
" 0 0 0 0.00% 110091.208095 110216 0.11% 11 0 0 783 1.1s\n",
" 0 0 0 0.00% 110091.208095 110216 0.11% 11 11 0 796 1.1s\n",
"\n",
"Solving report\n",
" Status Optimal\n",
" Primal bound 110216\n",
" Dual bound 110206.854428\n",
" Gap 0.0083% (tolerance: 0.01%)\n",
" Solution status feasible\n",
" 110216 (objective)\n",
" 0 (bound viol.)\n",
" 2.30926389122e-14 (int. viol.)\n",
" 0 (row viol.)\n",
" Timing 1.31 (total)\n",
" 0.00 (presolve)\n",
" 0.00 (postsolve)\n",
" Nodes 11\n",
" LP iterations 1510 (total)\n",
" 453 (strong br.)\n",
" 347 (separation)\n",
" 466 (heuristics)\n",
"HiGHS 1.5.1: optimal solution; objective 110216\n",
"1510 simplex iterations\n",
"11 branching nodes\n",
"absmipgap=9.14557, relmipgap=8.29786e-05\n"
]
}
],
"source": [
"m1 = BIMproduction_v1(\n",
" demand=demand,\n",
" existing={\"silicon\": 1000, \"germanium\": 1500, \"plastic\": 1750, \"copper\": 4800},\n",
" desired={\"silicon\": 500, \"germanium\": 500, \"plastic\": 1000, \"copper\": 2000},\n",
" stock_limit=10000,\n",
" supplying_copper=[\"B\", \"C\"],\n",
" supplying_batches=[\"A\", \"C\"],\n",
" price_copper_sheet={\"B\": 300, \"C\": 400},\n",
" price_batch={\"A\": 500, \"C\": 600},\n",
" discounted_price=700,\n",
" batch_size=100,\n",
" copper_sheet_mass=100,\n",
" copper_bucket_size=10,\n",
" unitary_products=[\"silicon\", \"germanium\", \"plastic\"],\n",
" unitary_holding_costs={\"copper\": 10, \"silicon\": 2, \"germanium\": 2, \"plastic\": 2},\n",
")\n",
"\n",
"m1.option[\"solver\"] = SOLVER\n",
"m1.option[\"highs_options\"] = \"outlev=1\"\n",
"m1.solve()"
]
},
{
"cell_type": "code",
"execution_count": 97,
"metadata": {},
"outputs": [
{
"data": {
"text/html": [
"\n",
"\n",
"
\n",
" \n",
" \n",
" \n",
" Jan \n",
" Feb \n",
" Mar \n",
" Apr \n",
" May \n",
" Jun \n",
" Jul \n",
" Aug \n",
" Sep \n",
" Oct \n",
" Nov \n",
" Dec \n",
" \n",
" \n",
" \n",
" \n",
" silicon \n",
" 912.0 \n",
" 787.0 \n",
" 527.0 \n",
" 310.0 \n",
" 72.0 \n",
" 15.0 \n",
" 0.0 \n",
" 0.0 \n",
" 0.0 \n",
" 56.0 \n",
" 54.0 \n",
" 500.0 \n",
" \n",
" \n",
" plastic \n",
" 1615.0 \n",
" 1428.0 \n",
" 1087.0 \n",
" 805.0 \n",
" 472.0 \n",
" 68.0 \n",
" 1.0 \n",
" 36.0 \n",
" 24.0 \n",
" 0.0 \n",
" 0.0 \n",
" 1000.0 \n",
" \n",
" \n",
" copper \n",
" 4354.0 \n",
" 3730.0 \n",
" 2528.0 \n",
" 1530.0 \n",
" 388.0 \n",
" 8.0 \n",
" 44.0 \n",
" 14.0 \n",
" 90.0 \n",
" 54.0 \n",
" 50.0 \n",
" 2042.0 \n",
" \n",
" \n",
" germanium \n",
" 1453.0 \n",
" 1391.0 \n",
" 1310.0 \n",
" 1245.0 \n",
" 1150.0 \n",
" 1032.0 \n",
" 946.0 \n",
" 857.0 \n",
" 775.0 \n",
" 693.0 \n",
" 609.0 \n",
" 543.0 \n",
" \n",
" \n",
"
\n",
"
"
],
"text/plain": [
" Jan Feb Mar Apr May Jun Jul Aug \\\n",
"silicon 912.0 787.0 527.0 310.0 72.0 15.0 0.0 0.0 \n",
"plastic 1615.0 1428.0 1087.0 805.0 472.0 68.0 1.0 36.0 \n",
"copper 4354.0 3730.0 2528.0 1530.0 388.0 8.0 44.0 14.0 \n",
"germanium 1453.0 1391.0 1310.0 1245.0 1150.0 1032.0 946.0 857.0 \n",
"\n",
" Sep Oct Nov Dec \n",
"silicon 0.0 56.0 54.0 500.0 \n",
"plastic 24.0 0.0 0.0 1000.0 \n",
"copper 90.0 54.0 50.0 2042.0 \n",
"germanium 775.0 693.0 609.0 543.0 "
]
},
"metadata": {},
"output_type": "display_data"
}
],
"source": [
"stock = m1.var[\"ss\"].to_pandas().unstack()\n",
"stock.columns = stock.columns.get_level_values(1)\n",
"stock = stock.reindex(index=demand.index, columns=demand.columns)\n",
"display(stock)"
]
},
{
"cell_type": "code",
"execution_count": 98,
"metadata": {},
"outputs": [
{
"data": {
"image/png": "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WxUqVFD//v31yy+/BIdI/OlPf9LkyZPVoUMHPfLII/r888+LFUtWVpaio6N15ZVXnt9FRbBi9cCoUKGCLrnkkpB98fHxqlq1anD/kCFDNHr0aFWpUkWJiYm6++67lZqaqvbt20uSunXrpqZNm6p///564okntH//fj300EMaMWJEsAfF8OHD9eyzz2rMmDEaPHiwVq1apSVLlmj58uXhuGYAAAAAgFUc7hM9IXSix0D2kSNKrFChdHoMOEpmfsRdu3bp2muv1V133aVHH31UVapU0ccff6whQ4bI6/XK7XZr6NChSktL0/Lly7VixQpNmTJF06ZN0913312kv1HYaITyJOz3kBkzZujaa69Vnz591LFjRyUlJemNN94IHo+OjtY777yj6Ohopaam6vbbb9eAAQM0ceLEYJsGDRpo+fLlysjIUMuWLTVt2jS9+OKLSktLC3e4AAAAAIDSZBgnhnHk/zjcodsl+VPMeSHWr18fsr1u3To1atSowEoamzZtUiAQ0LRp09S+fXs1btxYe/fuLXC+5ORkDR8+XG+88Ybuu+8+vfDCC5Ikp/PE4gin69mRr3nz5goEAlq9enWxrqEsOadlVE+WmZkZsh0XF6fZs2cHJyo5nXr16undd98943k7deqkLVu2nG94AAAAAACck927d2v06NG68847tXnzZj3zzDOaNm1agXYNGzaUz+fTM888o+uuu05r1qzRc889F9Jm1KhR6tGjhxo3bqzffvtNH3zwQXCVk3r16skwDL3zzju65ppr5HK5gquX5Ktfv74GDhyowYMHByfx/P7773Xw4EHdfPPNJZcEG7HPrB4AAAAAANjIgAED5PF4dNlll2nEiBG65557NGzYsALtWrZsqenTp2vq1Km65JJLtHDhwuDSp/ny8vI0YsQIpaSkqHv37mrcuLHmzJkjSapdu7YmTJigP//5z6pZs6ZGjhx52njmzp2rG2+8UX/84x918cUX64477tCxY8fCf+E2dd49MAAAAAAAKIscDodmzpwZXEnkZLt27QrZvvfee0NWIpGk/v37B39/5plnzvi3Hn74YT388MMh+0434mH69OmaPn16EaIve+iBAQAAAAAAbI8CBgAAAAAAsD2GkAAAAAAAcIpTh2/AevTAAAAAAAAAtkcBAwAAAAAA2B4FDAAAAAAAYHvMgYFz5vF7rA7hjPx+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QgUNHDhQDzzwgD744AN9+eWXGjJkiKKiomQYhho3bqx+/fppwIABeuONN7Rz505t2LBBU6ZM0fLly8MeT6NGjZSRkaFPPvlE27dv15133qkDB85v9pCGDRsqOTlZ48eP1zfffKPly5dr2rRpYYo4/ChgAAAAAABwGtOnT1dqaqquvfZade3aVR06dFBKSori4uIknZjUc8CAAbrvvvvUpEkT9e7dO6THRjg99NBDatOmjdLS0tSpUyclJSWpd+/e53VOh8OhV199VV999ZVatGihqVOnavLkyeEJuAQwhAQAAAAAgNOoUKGCFi5cGNw+duyYJkyYoGHDhkk6UQCYMGGCJkyYcNrbDxo0SIMGDSqwf9euXQX2nbrEa/369UP2ValSRW+99dYZ4121apWys7ND9p16m1P/TocOHfT5558X2uZ01zB+/HiNHz8+ZN/8+fPPGFs4UMAAAAAAAOA0tmzZoq+++kqXXXaZDh8+rIkTJ0qSevXqZXFk5RMFDAAAAAAACvHUU09px44dcjqdatu2rT766CNVq1bN6rDKJQoYAAAAAACcRuvWrbVp0yarw8B/MYknAAAAAACwPQoYAAAAAIASd+rkkSg/wvVvTwEDAAAAAFBioqOjJUler9fiSGCV/H/7/PvCuWIODAAAAABAiYmJiZHb7dZPP/0kh8OhqKj/fY8eCATk9Xp1/PjxkP04N3bMZyAQ0E8//SS3262YmPMrQVDAAAAAAACUGMMwdMEFF2jnzp36/vvvQ46ZpimPxyOXyyXDMCyKsOywaz6joqJUt27d846JAgYAAAAAoEQ5nU41atSowDASn8+nDz/8UB07dpTD4bAourLDrvl0Op1h6RFCAQMAAAAAUOKioqIUFxcXsi86Olp+v19xcXG2+sAdqcp6PotVApk7d65atGihxMREJSYmKjU1Vf/+97+Dx48fP64RI0aoatWqSkhIUJ8+fXTgwIGQc+zevVs9e/aU2+1WjRo19MADD8jv94e0yczMVJs2bRQbG6uGDRtq/vz5536FAAAAAAAg4hWrgFGnTh09/vjj2rRpkz799FN17txZvXr10pdffilJuvfee/X2229r6dKlWr16tfbu3asbbrghePu8vDz17NlTXq9Xn3zyiRYsWKD58+dr3LhxwTY7d+5Uz549ddVVVykrK0ujRo3S0KFDlZ6eHqZLBgAA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27ZttXz5cn3xxRcaNmyY+vfvrw0bNoS0W7BggeLj47V+/Xo98cQTmjhxojIyMiyK2r58Pp/S0tJUoUIFffTRR1qzZo0SEhLUvXv3kALFypUrg9X0V199VW+88YYmTJhgYeTW6t+/vz766CPt3r1bkvTPf/5T9evXV5s2bULaFef+6nQ6tWbNGj333HOldh12cOTIES1dulS33367rr76ah0+fFgfffRRgXZPPvmkWrZsqS1btujPf/6z7rnnngKP6fHjx+v666/X1q1bNXjw4NK6BFsI133ypptuUl5enpYtWxa8zcGDB7V8+fJyl9Nz9cEHH+jbb7/VBx98oAULFmj+/Pl0+0VYFPX58kyys7N13XXXqXnz5tq8ebMmTZqksWPHllDE9pWUlKR169bp+++/L7TNwoULNW7cOD366KPavn27HnvsMT388MMFer488MADuu+++7Rlyxalpqbquuuu0y+//FLSl2ArixYtUuPGjXXdddcVOHbffffpl19+Cb5mL1++XNdff72uueYabdmyRStXrtRll10mSXrjjTdUp04dTZw4MdhjuDwaNWqU/H6/nnnmmdMeDwQC6tWrl3799VetXr1aGRkZ+u6773TLLbdIkho3bqx27dpp4cKFIbdbuHChbrvtthKP385cLpeGDx+uNWvW6ODBg5LO/lg/evSorrzySv34449atmyZPvvsM40ZM0aBQMDKSykaE6Xugw8+MCWZv/32W6Ftevbsad53333B7SuvvNL83e9+F9Lm0ksvNceOHVtSYdpafg7j4+NDfn7++Wfz73//u9mkSRMzEAgE2+fm5poul8tMT083TdM0Bw4caFapUsU8duxYsM3cuXPNhIQEMy8vr9Svx0on3x979+5tTpgwwTRN07zqqqvMWbNmmW+++aZ5tqeK091fW7duXaJx29nzzz9vtmrVKrh9zz33mAMHDgxpU69ePbN79+4h+2655RazR48ewW1J5qhRo0o0VjsqifvkXXfdFZLbadOmmRdeeGHI80R5cOrrzyOPPGK2bNkypM2MGTPMevXqBbcHDhxo1qtXz/T7/cF9N910k3nLLbeUQsSR4Vzz2qtXr1KL0a6K8nwpyXzzzTdD9lWsWNGcN2+eaZonXr+rVq1qejye4PEXXnjBlGRu2bKlhCK3n71795rt27c3JZmNGzc2Bw4caL722msh72suuugic9GiRSG3mzRpkpmammqapmnu3LnTlGQ+/vjjweM+n8+sU6eOOXXq1NK5EJu4+OKLC32M/vrrr6akYE5SU1PNfv36FXquevXqmTNmzCiBKO3v5OfH5557zqxSpYp56NAh0zRDH8crVqwwo6Ojzd27dwdv++WXX5qSzA0bNpimeeJ59KKLLgoe37FjhynJ3L59e+ldkIXO9Bny3//+tynJXL9+vWmaZ3+s/+1vfzMrVKhg/vLLLyUed7jRA8MG8vLyNGnSJDVv3lxVqlRRQkKC0tPTg9865mvRokXI9gUXXBCsspVXH330kbKysoI/lStX1meffab//Oc/qlChghISEpSQkKAqVaro+PHj+vbbb4O3bdmypdxud3A7NTVVR48e1Z49e6y4FFsYPHiw5s+fr++++05r165Vv379CrQp6v21bdu2pRW27bz88su6/fbbg9u33367li5dqiNHjoS0S01NLbC9ffv2kH3t2rUruUAjQLjuk3fccYdWrFihH3/8UZI0f/58DRo0qNx25S2uZs2aKTo6OrjN6w/CpajPl2eyY8cOtWjRQnFxccF9+d9+lycXXHCB1q5dq61bt+qee+6R3+/XwIED1b17dwUCAR07dkzffvuthgwZEnx/lJCQoMmTJ4e8P5JCX59iYmLUrl27Aq9P5YFpmkVql5WVpS5dupRwNJFvyJAhqlq1qqZOnVrg2Pbt25WcnKzk5OTgvqZNm6pSpUrB+96tt96qXbt2ad26dZJO9DJo06aNLr744tK5ABvLv68ahlGkx3pWVpZat26tKlWqWBn2OYmxOgCc6EY+a9YszZw5U82bN1d8fLxGjRpVYD4Gh8MRsm0YRmR08ylBDRo0UKVKlUL2HT16VG3bti3QxUySqlevXkqRRaYePXpo2LBhGjJkiK677jpVrVq1QJui3l/j4+NLK2xb2bZtm9atW6cNGzaEdGHOy8vT4sWLdccddxTrfOU1j/nCdZ9s3bq1WrZsqVdeeUXdunXTl19+qeXLl5fmpdhSVFRUgTfop5vbgtef4ilqXsu7oj5fGoZBPovhkksu0SWXXKI//vGPGj58uK644gqtXr1aTZs2lXRipZLLL7885DYnFyhxQuPGjQst2uTvb9y4saQTXfhxdjExMXr00Uc1aNAgjRw5sti3T0pKUufOnbVo0SK1b99eixYtKjDvQ3mVf5+sX79+cOLeMz3WI/k+Sw8MG1izZo169eql22+/XS1bttSFF16or7/+2uqwIlabNm30zTffqEaNGmrYsGHIT8WKFYPtPvvsM3k8nuD2unXrlJCQEFL5LW9iYmI0YMAAZWZmFjo3APfXM3vppZfUsWNHffbZZyG9g0aPHq2XXnoppG3+Nwgnb6ekpJRmuLYXzvvk0KFDNX/+fM2bN09du3Yt14/1fNWrV9f+/ftDPhyWp0kPSwp5LZqiPl9Wr149ZN6Ab775Rjk5OcHtJk2aaOvWrSGTA27cuLF0LsLm8osWx44dU82aNVWrVi199913Bd4fNWjQIOR2J78++f1+bdq0qdy9Pt1666365ptv9Pbbbxc4Nm3aNFWtWlVXX321pBO9pFeuXFnouZxOZ4ElRMurm266Sc2aNSsw71xKSor27NkT0hN627ZtOnToUPB+LEn9+vXTa6+9prVr1+q7777TrbfeWmqx25XH49Hzzz+vjh07qnr16kV6rLdo0UJZWVn69ddfLY6++Chg2ECjRo2UkZGhTz75RNu3b9edd94ZnHEXxdevXz9Vq1ZNvXr10kcffaSdO3cqMzNTf/rTn/TDDz8E23m9Xg0ZMkTbtm3Tu+++q0ceeUQjR45UVFT5flhMmjRJP/30U6Erh3B/LZzP59Pf//539e3bN/gNWP7P0KFDtX79en355ZfB9mvWrNETTzyhr7/+WrNnz9bSpUt1zz33WHgF9hSu++Rtt92mH374QS+88AKTd/5Xp06d9NNPP+mJJ57Qt99+q9mzZ+vf//631WFFPPJ6dsV5vuzcubOeffZZbdmyRZ9++qmGDx8e0ivotttuUyAQ0LBhw7R9+3alp6frqaeekqRyNUzsrrvu0qRJk7RmzRp9//33WrdunQYMGKDq1asHh4RMmDBBU6ZM0dNPP62vv/5aW7du1bx58zR9+vSQc82ePVtvvvmmvvrqK40YMUK//fZbuXvevPXWW3X99ddr4MCBeumll7Rr1y59/vnnuvPOO7Vs2TK9+OKLwV6SjzzyiF599VU98sgj2r59u7Zu3RoyTKJ+/fr68MMP9eOPP+rnn3+26pJs4/HHH9fLL78csopL165d1bx5c/Xr10+bN2/Whg0bNGDAAF155ZUhw2lvuOEGHTlyRHfddZeuuuoq1apVy4pLsNTBgwe1f/9+ffPNN1q8eLE6dOign3/+WXPnzg22OdtjvW/fvkpKSlLv3r21Zs0afffdd/rnP/+ptWvXWnVZRVa+P6lZJL/bbUzMiRE8Dz30kNq0aaO0tDR16tQpeGfCuXG73frwww9Vt25d3XDDDUpJSdGQIUN0/PhxJSYmBtt16dJFjRo1UseOHXXLLbfo97//PUvT6sS3BNWqVSv0TR/318ItW7ZMv/zyS3DptJOlpKQoJSUl5FvF++67T59++qlat26tyZMna/r06eVyydmzCdd9smLFiurTp48SEhLK7X321NeflJQUzZkzR7Nnz1bLli21YcMG3X///VaGGJHIa/EV5/ly2rRpSk5O1hVXXKHbbrtN999/f8gcVomJiXr77beVlZWlVq1a6a9//avGjRsnSSHzYpR1Xbt21bp163TTTTepcePG6tOnj+Li4rRy5crg8LuhQ4fqxRdf1Lx589S8eXNdeeWVmj9/foEeGI8//rgef/xxtWzZUh9//LGWLVumatWqWXFZljEMQ0uWLNFf/vIXzZgxQ02aNNEVV1yh77//XpmZmSGvI506ddLSpUu1bNkytWrVSp07dw5ZnW3ixInatWuXLrroIoYz60RRsnPnzvL7/cF9hmHoX//6lypXrqyOHTuqa9euuvDCC/Xaa6+F3LZChQq67rrr9Nlnn512XqzyoEmTJqpVq5batm2rxx9/XF27dtUXX3wR0lPlbI91p9OpFStWqEaNGrrmmmvUvHlzPf744xExnMwwizo7DcImf1xncSaoAlC21K9fX6NGjdKoUaOsDqVc6dKli5o1a6ann37a6lAswetPySCv9rNw4UL94Q9/0OHDhyN6rHdp27Vrlxo0aKAtW7aoVatWVocDAAUwiWcpys3N1bfffqtnn32WmYoBoBT99ttvyszMVGZmpubMmWN1OKWO15+SQV7t45VXXtGFF16o2rVr67PPPtPYsWN18803U7wAgDKGISSl6N///rcuv/xyxcfHl9tv/wDACq1bt9agQYM0depUNWnSxOpwSh2vPyWDvNrH/v37dfvttyslJUX33nuvbrrpJj3//PNWhwUACDOGkAAAAAAAANujBwYAAAAAALA9ChgAAAAAAMD2KGAAAAAAAADbo4ABAAAAAABsjwIGAAAAAACwPQoYAAAAAADA9ihgAAAAAAAA26OAAQAAAAAAbO//AZZN4ZuRmf+9AAAAAElFTkSuQmCC",
"text/plain": [
""
]
},
"metadata": {},
"output_type": "display_data"
}
],
"source": [
"import matplotlib.pyplot as plt, numpy as np\n",
"\n",
"stock.T.plot(drawstyle=\"steps-mid\", grid=True, figsize=(13, 4))\n",
"plt.xticks(np.arange(len(stock.columns)), stock.columns)\n",
"plt.show()"
]
},
{
"cell_type": "code",
"execution_count": 99,
"metadata": {},
"outputs": [
{
"data": {
"text/html": [
"\n",
"\n",
"
\n",
" \n",
" \n",
" \n",
" Jan \n",
" Feb \n",
" Mar \n",
" Apr \n",
" May \n",
" Jun \n",
" Jul \n",
" Aug \n",
" Sep \n",
" Oct \n",
" Nov \n",
" Dec \n",
" \n",
" \n",
" \n",
" \n",
" pp.val \n",
" 0.0 \n",
" 0.0 \n",
" 0.0 \n",
" 0.0 \n",
" 0.0 \n",
" 3.0 \n",
" 5.0 \n",
" 6.0 \n",
" 6.0 \n",
" 7.0 \n",
" 6.0 \n",
" 20.0 \n",
" \n",
" \n",
"
\n",
"
"
],
"text/plain": [
" Jan Feb Mar Apr May Jun Jul Aug Sep Oct Nov Dec\n",
"pp.val 0.0 0.0 0.0 0.0 0.0 3.0 5.0 6.0 6.0 7.0 6.0 20.0"
]
},
"execution_count": 99,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"m1.var[\"pp\"].to_pandas().T"
]
},
{
"cell_type": "code",
"execution_count": 100,
"metadata": {},
"outputs": [
{
"data": {
"text/html": [
"\n",
"\n",
"
\n",
" \n",
" \n",
" \n",
" Jan \n",
" Feb \n",
" Mar \n",
" Apr \n",
" May \n",
" Jun \n",
" Jul \n",
" Aug \n",
" Sep \n",
" Oct \n",
" Nov \n",
" Dec \n",
" \n",
" \n",
" \n",
" \n",
" silicon \n",
" 0.0 \n",
" 0.0 \n",
" 0.0 \n",
" 0.0 \n",
" 0.0 \n",
" 229.0 \n",
" 233.0 \n",
" 238.0 \n",
" 265.0 \n",
" 349.0 \n",
" 257.0 \n",
" 690.0 \n",
" \n",
" \n",
" plastic \n",
" 0.0 \n",
" 0.0 \n",
" 0.0 \n",
" 0.0 \n",
" 0.0 \n",
" 0.0 \n",
" 267.0 \n",
" 362.0 \n",
" 335.0 \n",
" 351.0 \n",
" 343.0 \n",
" 1310.0 \n",
" \n",
" \n",
" copper \n",
" 0.0 \n",
" 0.0 \n",
" 0.0 \n",
" 0.0 \n",
" 0.0 \n",
" 1000.0 \n",
" 1200.0 \n",
" 1100.0 \n",
" 1300.0 \n",
" 1300.0 \n",
" 1200.0 \n",
" 3100.0 \n",
" \n",
" \n",
" germanium \n",
" 0.0 \n",
" 0.0 \n",
" 0.0 \n",
" 0.0 \n",
" 0.0 \n",
" 0.0 \n",
" 0.0 \n",
" 0.0 \n",
" 0.0 \n",
" 0.0 \n",
" 0.0 \n",
" 0.0 \n",
" \n",
" \n",
"
\n",
"
"
],
"text/plain": [
" Jan Feb Mar Apr May Jun Jul Aug Sep \\\n",
"silicon 0.0 0.0 0.0 0.0 0.0 229.0 233.0 238.0 265.0 \n",
"plastic 0.0 0.0 0.0 0.0 0.0 0.0 267.0 362.0 335.0 \n",
"copper 0.0 0.0 0.0 0.0 0.0 1000.0 1200.0 1100.0 1300.0 \n",
"germanium 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 \n",
"\n",
" Oct Nov Dec \n",
"silicon 349.0 257.0 690.0 \n",
"plastic 351.0 343.0 1310.0 \n",
"copper 1300.0 1200.0 3100.0 \n",
"germanium 0.0 0.0 0.0 "
]
},
"metadata": {},
"output_type": "display_data"
}
],
"source": [
"df = m1.var[\"uu\"].to_pandas().unstack()\n",
"df.columns = df.columns.get_level_values(1)\n",
"df = df.reindex(index=demand.index, columns=demand.columns)\n",
"display(df)"
]
},
{
"cell_type": "code",
"execution_count": 101,
"metadata": {},
"outputs": [
{
"data": {
"text/html": [
"\n",
"\n",
"
\n",
" \n",
" \n",
" \n",
" Jan \n",
" Feb \n",
" Mar \n",
" Apr \n",
" May \n",
" Jun \n",
" Jul \n",
" Aug \n",
" Sep \n",
" Oct \n",
" Nov \n",
" Dec \n",
" \n",
" \n",
" \n",
" \n",
" A \n",
" 0.0 \n",
" 0.0 \n",
" 0.0 \n",
" 0.0 \n",
" 0.0 \n",
" 0.0 \n",
" 0.0 \n",
" 0.0 \n",
" 0.0 \n",
" 0.0 \n",
" 0.0 \n",
" 0.0 \n",
" \n",
" \n",
" C \n",
" 0.0 \n",
" 0.0 \n",
" 0.0 \n",
" 0.0 \n",
" 0.0 \n",
" 3.0 \n",
" 5.0 \n",
" 6.0 \n",
" 6.0 \n",
" 7.0 \n",
" 6.0 \n",
" 20.0 \n",
" \n",
" \n",
"
\n",
"
"
],
"text/plain": [
" Jan Feb Mar Apr May Jun Jul Aug Sep Oct Nov Dec\n",
"A 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0\n",
"C 0.0 0.0 0.0 0.0 0.0 3.0 5.0 6.0 6.0 7.0 6.0 20.0"
]
},
"metadata": {},
"output_type": "display_data"
}
],
"source": [
"b = m1.var[\"bb\"].to_pandas().unstack().T\n",
"b.index = b.index.get_level_values(1)\n",
"b = b.reindex(columns=demand.columns)\n",
"b.index.names = [None]\n",
"display(b)"
]
},
{
"cell_type": "code",
"execution_count": 102,
"metadata": {},
"outputs": [
{
"data": {
"text/html": [
"\n",
"\n",
"
\n",
" \n",
" \n",
" \n",
" Jan \n",
" Feb \n",
" Mar \n",
" Apr \n",
" May \n",
" Jun \n",
" Jul \n",
" Aug \n",
" Sep \n",
" Oct \n",
" Nov \n",
" Dec \n",
" \n",
" \n",
" \n",
" \n",
" B \n",
" 0.0 \n",
" 0.0 \n",
" 0.0 \n",
" 0.0 \n",
" 0.0 \n",
" 7.0 \n",
" 7.0 \n",
" 5.0 \n",
" 7.0 \n",
" 6.0 \n",
" 6.0 \n",
" 11.0 \n",
" \n",
" \n",
" C \n",
" 0.0 \n",
" 0.0 \n",
" 0.0 \n",
" 0.0 \n",
" 0.0 \n",
" 3.0 \n",
" 5.0 \n",
" 6.0 \n",
" 6.0 \n",
" 7.0 \n",
" 6.0 \n",
" 20.0 \n",
" \n",
" \n",
"
\n",
"
"
],
"text/plain": [
" Jan Feb Mar Apr May Jun Jul Aug Sep Oct Nov Dec\n",
"B 0.0 0.0 0.0 0.0 0.0 7.0 7.0 5.0 7.0 6.0 6.0 11.0\n",
"C 0.0 0.0 0.0 0.0 0.0 3.0 5.0 6.0 6.0 7.0 6.0 20.0"
]
},
"metadata": {},
"output_type": "display_data"
}
],
"source": [
"y = m1.var[\"y\"].to_pandas().unstack().T\n",
"y.index = y.index.get_level_values(1)\n",
"y = y.reindex(columns=demand.columns)\n",
"y.index.names = [None]\n",
"display(y)"
]
},
{
"cell_type": "code",
"execution_count": 103,
"metadata": {},
"outputs": [
{
"data": {
"text/html": [
"\n",
"\n",
"
\n",
" \n",
" \n",
" \n",
" \n",
" Jan \n",
" Feb \n",
" Mar \n",
" Apr \n",
" May \n",
" Jun \n",
" Jul \n",
" Aug \n",
" Sep \n",
" Oct \n",
" Nov \n",
" Dec \n",
" \n",
" \n",
" materials \n",
" supplier \n",
" \n",
" \n",
" \n",
" \n",
" \n",
" \n",
" \n",
" \n",
" \n",
" \n",
" \n",
" \n",
" \n",
" \n",
" \n",
" \n",
" germanium \n",
" A \n",
" 0.0 \n",
" 0.0 \n",
" 0.0 \n",
" 0.0 \n",
" 0.0 \n",
" 0.0 \n",
" 0.0 \n",
" 0.0 \n",
" 0.0 \n",
" 0.0 \n",
" 0.0 \n",
" 0.0 \n",
" \n",
" \n",
" C \n",
" 0.0 \n",
" 0.0 \n",
" 0.0 \n",
" 0.0 \n",
" 0.0 \n",
" 0.0 \n",
" 0.0 \n",
" 0.0 \n",
" 0.0 \n",
" 0.0 \n",
" 0.0 \n",
" 0.0 \n",
" \n",
" \n",
" plastic \n",
" A \n",
" 0.0 \n",
" 0.0 \n",
" 0.0 \n",
" 0.0 \n",
" 0.0 \n",
" 0.0 \n",
" 0.0 \n",
" 0.0 \n",
" 0.0 \n",
" 0.0 \n",
" 0.0 \n",
" 0.0 \n",
" \n",
" \n",
" C \n",
" 0.0 \n",
" 0.0 \n",
" 0.0 \n",
" 0.0 \n",
" 0.0 \n",
" 0.0 \n",
" 267.0 \n",
" 362.0 \n",
" 335.0 \n",
" 351.0 \n",
" 343.0 \n",
" 1310.0 \n",
" \n",
" \n",
" silicon \n",
" A \n",
" 0.0 \n",
" 0.0 \n",
" 0.0 \n",
" 0.0 \n",
" 0.0 \n",
" 0.0 \n",
" 0.0 \n",
" 0.0 \n",
" 0.0 \n",
" 0.0 \n",
" 0.0 \n",
" 0.0 \n",
" \n",
" \n",
" C \n",
" 0.0 \n",
" 0.0 \n",
" 0.0 \n",
" 0.0 \n",
" 0.0 \n",
" 229.0 \n",
" 233.0 \n",
" 238.0 \n",
" 265.0 \n",
" 349.0 \n",
" 257.0 \n",
" 690.0 \n",
" \n",
" \n",
"
\n",
"
"
],
"text/plain": [
" Jan Feb Mar Apr May Jun Jul Aug Sep \\\n",
"materials supplier \n",
"germanium A 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 \n",
" C 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 \n",
"plastic A 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 \n",
" C 0.0 0.0 0.0 0.0 0.0 0.0 267.0 362.0 335.0 \n",
"silicon A 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 \n",
" C 0.0 0.0 0.0 0.0 0.0 229.0 233.0 238.0 265.0 \n",
"\n",
" Oct Nov Dec \n",
"materials supplier \n",
"germanium A 0.0 0.0 0.0 \n",
" C 0.0 0.0 0.0 \n",
"plastic A 0.0 0.0 0.0 \n",
" C 351.0 343.0 1310.0 \n",
"silicon A 0.0 0.0 0.0 \n",
" C 349.0 257.0 690.0 "
]
},
"metadata": {},
"output_type": "display_data"
}
],
"source": [
"x = m1.var[\"x\"].to_pandas().unstack(1)\n",
"x.columns = x.columns.get_level_values(1)\n",
"x = x.reindex(columns=demand.columns)\n",
"x.index.names = [\"materials\", \"supplier\"]\n",
"display(x)"
]
}
],
"metadata": {
"kernelspec": {
"display_name": "Python 3 (ipykernel)",
"language": "python",
"name": "python3"
},
"language_info": {
"codemirror_mode": {
"name": "ipython",
"version": 3
},
"file_extension": ".py",
"mimetype": "text/x-python",
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython3",
"version": "3.10.6"
}
},
"nbformat": 4,
"nbformat_minor": 4
}