(tons/acre)| Yield for corn
(tons/acre) | Yield for beets
(tons/acre) |\n", "| :-- | :-: | :-: | :-: |\n", "| Good weather | 3 | 3.6 | 24 |\n", "| Average weather | 2.5 | 3 | 20 |\n", "| Bad weather | 2 | 2.4 | 16 |\n", "\n", "We first consider the case in which all the prices are fixed and not weather-dependent. The following table summarizes the data.\n", "\n", "| Commodity | Sell
Price
(euro/ton) | Market
Demand
(tons) | Excess Price | Buy
Price
(euro/ton) | Cattle Feed
Required
(tons) | Planting
Cost
(euro/acre) |\n", "| :-- | :--: | :--: | :--: | :--: | :--: | :--: |\n", "| Wheat | 170 | - | _ | 238 | 200 | 150 |\n", "| Corn | 150 | - | - | 210 | 240 | 230 |\n", "| Beets | 36 | 6000 | 10 | 0 | - | 260 |\n" ] }, { "cell_type": "markdown", "id": "4b603542-8a50-42dc-8327-412d771d8beb", "metadata": {}, "source": [ "## Data Modeling" ] }, { "cell_type": "code", "execution_count": 3, "id": "d747fa6a-117a-4828-804b-01a0fa7da3ba", "metadata": {}, "outputs": [ { "data": { "text/html": [ "
\n", "\n", "\n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", "
wheatcornbeets
good3.03.624.0
average2.53.020.0
poor2.02.416.0
\n", "
" ], "text/plain": [ " wheat corn beets\n", "good 3.0 3.6 24.0\n", "average 2.5 3.0 20.0\n", "poor 2.0 2.4 16.0" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "yields = pd.DataFrame(\n", " {\n", " \"good\": {\"wheat\": 3.0, \"corn\": 3.6, \"beets\": 24},\n", " \"average\": {\"wheat\": 2.5, \"corn\": 3.0, \"beets\": 20},\n", " \"poor\": {\"wheat\": 2.0, \"corn\": 2.4, \"beets\": 16},\n", " }\n", ").T\n", "display(yields)" ] }, { "cell_type": "code", "execution_count": 4, "id": "b2e950c8-7a4e-41e1-b0a3-940bed3714c2", "metadata": {}, "outputs": [ { "data": { "text/html": [ "
\n", "\n", "\n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", "
wheat150.0170.0238.0200.020000.00.0
corn230.0150.0210.0240.020000.00.0
beets260.036.020000.00.06000.010.0
\n", "
\n", "\n", "\n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", "
profit
scenario
mean118600.0
\n", "
" ], "text/plain": [ " profit\n", "scenario \n", "mean 118600.0" ] }, "metadata": {}, "output_type": "display_data" }, { "data": { "text/html": [ "
\n", "\n", "\n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", "
acres
crop
beets300
corn80
wheat120
\n", "
" ], "text/plain": [ " acres\n", "crop \n", "beets 300\n", "corn 80\n", "wheat 120" ] }, "metadata": {}, "output_type": "display_data" }, { "data": { "text/html": [ "
\n", "\n", "\n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", "
scenariocrop
meanbeets6000.006000.0078000216000.0138000.0
corn240.000.00184000.0-18400.0
wheat300.00100.001800017000.0-1000.0
\n", "
" ], "text/plain": [ " grow buy sell excess expense revenue profit\n", "scenario crop \n", "mean beets 6000.0 0 6000.0 0 78000 216000.0 138000.0\n", " corn 240.0 0 0.0 0 18400 0.0 -18400.0\n", " wheat 300.0 0 100.0 0 18000 17000.0 -1000.0" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "m = farmer(crops, pd.DataFrame(yields.mean(), columns=[\"mean\"]).T)\n", "\n", "m.eval(\"maximize objective: sum{s in SCENARIOS} scenario_profit[s];\")\n", "m.get_output(\"solve;\")\n", "\n", "farm_report(m)" ] }, { "cell_type": "markdown", "id": "fd7d91a0-843a-4e9f-8d6c-a38363ab8f32", "metadata": {}, "source": [ "## 2. Stochastic Solution\n" ] }, { "cell_type": "markdown", "id": "86d44583-34a3-49f5-8c91-511c1501731f", "metadata": {}, "source": [ "The problem statement asks for a number of different analyses. In a consulting situation, it is possible the client would ask more \"what if\" questions after hearing the initial results. For these reasons, we build a function that returns an AMPL model and add the variables and expressions needed to address all parts of the problem. " ] }, { "cell_type": "code", "execution_count": 9, "id": "8c8c2d36-4f70-4e1a-a5f2-a45dc0a013ed", "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Objective = 108390.00\n" ] }, { "data": { "text/html": [ "
\n", "\n", "\n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", "
profit
scenario
average109350.0
good167000.0
poor48820.0
\n", "
" ], "text/plain": [ " profit\n", "scenario \n", "average 109350.0\n", "good 167000.0\n", "poor 48820.0" ] }, "metadata": {}, "output_type": "display_data" }, { "data": { "text/html": [ "
\n", "\n", "\n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", "
acres
crop
beets250
corn80
wheat170
\n", "
" ], "text/plain": [ " acres\n", "crop \n", "beets 250\n", "corn 80\n", "wheat 170" ] }, "metadata": {}, "output_type": "display_data" }, { "data": { "text/html": [ "
\n", "\n", "\n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", "
scenariocrop
averagebeets5000.00.05000.0065000.0180000.0115000.0
corn240.00.00.0018400.00.0-18400.0
wheat425.00.0225.0025500.038250.012750.0
goodbeets6000.00.06000.0065000.0216000.0151000.0
corn288.00.048.0018400.07200.0-11200.0
wheat510.00.0310.0025500.052700.027200.0
poorbeets4000.00.04000.0065000.0144000.079000.0
corn192.048.00.0028480.00.0-28480.0
wheat340.00.0140.0025500.023800.0-1700.0
\n", "
" ], "text/plain": [ " grow buy sell excess expense revenue profit\n", "scenario crop \n", "average beets 5000.0 0.0 5000.0 0 65000.0 180000.0 115000.0\n", " corn 240.0 0.0 0.0 0 18400.0 0.0 -18400.0\n", " wheat 425.0 0.0 225.0 0 25500.0 38250.0 12750.0\n", "good beets 6000.0 0.0 6000.0 0 65000.0 216000.0 151000.0\n", " corn 288.0 0.0 48.0 0 18400.0 7200.0 -11200.0\n", " wheat 510.0 0.0 310.0 0 25500.0 52700.0 27200.0\n", "poor beets 4000.0 0.0 4000.0 0 65000.0 144000.0 79000.0\n", " corn 192.0 48.0 0.0 0 28480.0 0.0 -28480.0\n", " wheat 340.0 0.0 140.0 0 25500.0 23800.0 -1700.0" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "# maximize mean profit\n", "m = farmer(crops, yields)\n", "\n", "m.eval(\n", " \"maximize objective: (sum{s in SCENARIOS} scenario_profit[s]) / card(SCENARIOS);\"\n", ")\n", "m.get_output(\"solve;\")\n", "\n", "farm_report(m)" ] }, { "cell_type": "markdown", "id": "b724882a-b695-40e9-bcab-753b4110bf48", "metadata": {}, "source": [ "## 3. Worst Case Solution" ] }, { "cell_type": "code", "execution_count": 10, "id": "8eb8181a-fe4b-4e19-adb8-11ee3aad1620", "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Objective = 59950.00\n" ] }, { "data": { "text/html": [ "
\n", "\n", "\n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", "
profit
scenario
average59950.0
good59950.0
poor59950.0
\n", "
" ], "text/plain": [ " profit\n", "scenario \n", "average 59950.0\n", "good 59950.0\n", "poor 59950.0" ] }, "metadata": {}, "output_type": "display_data" }, { "data": { "text/html": [ "
\n", "\n", "\n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", "
acres
crop
beets375.0
corn25.0
wheat100.0
\n", "
" ], "text/plain": [ " acres\n", "crop \n", "beets 375.0\n", "corn 25.0\n", "wheat 100.0" ] }, "metadata": {}, "output_type": "display_data" }, { "data": { "text/html": [ "
\n", "\n", "\n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", "
scenariocrop
averagebeets7500.00.05301.9230772198.07692397500.0212850.0115350.0
corn75.0165.00.0000000.00000040400.00.0-40400.0
wheat250.00.00.00000050.00000015000.00.0-15000.0
goodbeets9000.00.04603.8461544396.15384697500.0209700.0112200.0
corn90.0150.00.0000000.00000037250.00.0-37250.0
wheat300.00.00.000000100.00000015000.00.0-15000.0
poorbeets6000.00.06000.0000000.00000097500.0216000.0118500.0
corn60.0180.00.0000000.00000043550.00.0-43550.0
wheat200.00.00.0000000.00000015000.00.0-15000.0
\n", "
" ], "text/plain": [ " grow buy sell excess expense revenue \n", "scenario crop \n", "average beets 7500.0 0.0 5301.923077 2198.076923 97500.0 212850.0 \\\n", " corn 75.0 165.0 0.000000 0.000000 40400.0 0.0 \n", " wheat 250.0 0.0 0.000000 50.000000 15000.0 0.0 \n", "good beets 9000.0 0.0 4603.846154 4396.153846 97500.0 209700.0 \n", " corn 90.0 150.0 0.000000 0.000000 37250.0 0.0 \n", " wheat 300.0 0.0 0.000000 100.000000 15000.0 0.0 \n", "poor beets 6000.0 0.0 6000.000000 0.000000 97500.0 216000.0 \n", " corn 60.0 180.0 0.000000 0.000000 43550.0 0.0 \n", " wheat 200.0 0.0 0.000000 0.000000 15000.0 0.0 \n", "\n", " profit \n", "scenario crop \n", "average beets 115350.0 \n", " corn -40400.0 \n", " wheat -15000.0 \n", "good beets 112200.0 \n", " corn -37250.0 \n", " wheat -15000.0 \n", "poor beets 118500.0 \n", " corn -43550.0 \n", " wheat -15000.0 " ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "# find worst case profit\n", "m = farmer(crops, yields)\n", "\n", "m.eval(\"var worst_case_profit;\")\n", "m.eval(\n", " \"s.t. lower_bound_profit{s in SCENARIOS}: worst_case_profit <= scenario_profit[s];\"\n", ")\n", "m.eval(\"maximize objective: worst_case_profit;\")\n", "m.get_output(\"solve;\")\n", "\n", "farm_report(m)" ] }, { "cell_type": "code", "execution_count": 11, "id": "73a50577-21cc-476f-a1ad-7b705a2a20f7", "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Objective = 86600.00\n" ] }, { "data": { "text/html": [ "
\n", "\n", "\n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", "
profit
scenario
average86600.0
good113250.0
poor59950.0
\n", "
" ], "text/plain": [ " profit\n", "scenario \n", "average 86600.0\n", "good 113250.0\n", "poor 59950.0" ] }, "metadata": {}, "output_type": "display_data" }, { "data": { "text/html": [ "
\n", "\n", "\n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", "
acres
crop
beets375.0
corn25.0
wheat100.0
\n", "
" ], "text/plain": [ " acres\n", "crop \n", "beets 375.0\n", "corn 25.0\n", "wheat 100.0" ] }, "metadata": {}, "output_type": "display_data" }, { "data": { "text/html": [ "
\n", "\n", "\n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", "