This is a static, read-only snapshot — sliders and other controls will not respond.
» Back to notebook list
·
» Run it yourself
%23%20%2F%2F%2F%20script%0A%23%20%5Btool.marimo.opengraph%5D%0A%23%20title%20%3D%20%22MoselPy%3A%20Production%20Planning%22%0A%23%20description%20%3D%20%22Bicycles%2C%20glass%2C%20toys%2C%20fiber%2C%20assignment%22%0A%23%20image%20%3D%20%22__marimo__%2Fthumbnail-moselpy-production.svg%22%0A%23%20%2F%2F%2F%0A%0Aimport%20marimo%0A%0A__generated_with%20%3D%20%220.25.2%22%0Aapp%20%3D%20marimo.App()%0A%0A%0A%40app.cell%0Adef%20_()%3A%0A%20%20%20%20import%20marimo%20as%20mo%0A%0A%20%20%20%20return%20(mo%2C)%0A%0A%0A%40app.cell(hide_code%3DTrue)%0Adef%20_(mo)%3A%0A%20%20%20%20mo.md(r%22%22%22%0A%20%20%20%20%23%20**FICO%26reg%3B%20Xpress%20MoselPy%20-%20Chapter%20C%3A%20Production%20Planning**%0A%20%20%20%20%22%22%22)%0A%20%20%20%20return%0A%0A%0A%40app.cell(hide_code%3DTrue)%0Adef%20_(mo)%3A%0A%20%20%20%20mo.md(r%22%22%22%0A%20%20%20%20***moselpy_production_planning.py***%20-%20Production%20planning%20examples%20from%20the%20Xpress%20Applications%20Book%20-%20bicycle%2C%20glass%2C%20toy%20lorry%2C%20electronic%20components%2C%20fiberglass%2C%20and%20machine%20assignment%2C%20solved%20with%20MoselPy.%0A%0A%20%20%20%20%26copy%3B%20Copyright%202026%20Fair%20Isaac%20Corporation.%20The%20use%20of%20this%20example%20is%20subject%20to%20%5Blegal%20and%20license%20requirements%5D(https%3A%2F%2Fgithub.com%2Ffico-xpress%2Fxpress-community%23legal-and-license-requirements).%0A%20%20%20%20%22%22%22)%0A%20%20%20%20return%0A%0A%0A%40app.cell(hide_code%3DTrue)%0Adef%20_(mo)%3A%0A%20%20%20%20mo.md(r%22%22%22%0A%20%20%20%20The%20cell%20below%20imports%20the%20necessary%20libraries%20and%20set%20%60MODELS%60%20and%20%60DATA%60%2C%20the%20path%20variables%20pointing%20to%20the%20Mosel%20model%20files%20and%20data%20files%20used%20in%20this%20notebook.%0A%20%20%20%20%22%22%22)%0A%20%20%20%20return%0A%0A%0A%40app.cell%0Adef%20_(mo)%3A%0A%20%20%20%20%23%20On%20Linux%2C%20preload%20the%20Xpress%20solver%20library%20so%20the%20Mosel%20optimizer%20module%0A%20%20%20%20%23%20(mmxprs)%20can%20resolve%20it%20at%20runtime.%0A%20%20%20%20import%20sys%2C%20os%2C%20glob%2C%20ctypes%2C%20xpresslibs%0A%20%20%20%20if%20sys.platform%20%3D%3D%20%22linux%22%3A%0A%20%20%20%20%20%20%20%20for%20_lib%20in%20glob.glob(os.path.join(xpresslibs.solver_libs_dir%2C%20%22libxprs.so*%22))%3A%0A%20%20%20%20%20%20%20%20%20%20%20%20ctypes.CDLL(_lib%2C%20mode%3Dctypes.RTLD_GLOBAL)%0A%0A%20%20%20%20import%20moselpy%20as%20mp%0A%20%20%20%20from%20moselpy%20import%20ExecStatus%0A%20%20%20%20import%20matplotlib.pyplot%20as%20plt%0A%20%20%20%20import%20matplotlib.patches%20as%20mpatches%0A%20%20%20%20import%20numpy%20as%20np%0A%20%20%20%20import%20pandas%20as%20pd%0A%0A%20%20%20%20_root%20%3D%20mo.notebook_dir()%20%2F%20%22public%22%20%2F%20%22moselpy%22%0A%20%20%20%20MODELS%20%3D%20str(_root%20%2F%20%22models%22%20%2F%20%22production_planning%22)%0A%20%20%20%20DATA%20%20%20%3D%20str(_root%20%2F%20%22data%22%20%2F%20%22production_planning%22)%0A%0A%20%20%20%20print(f%22MoselPy%20%7Bmp.version%7D%20%20%7C%20%20Mosel%20%7Bmp.mosel_version%7D%20%20%7C%20%20Xpress%20%7Bmp.xpress_version%7D%22)%0A%20%20%20%20mp.config.set_default_stream(mp.StreamType.OUTPUT%2C%20%22null%3A%22)%0A%20%20%20%20return%20DATA%2C%20MODELS%2C%20mp%2C%20mpatches%2C%20np%2C%20pd%2C%20plt%0A%0A%0A%40app.cell(hide_code%3DTrue)%0Adef%20_(mo)%3A%0A%20%20%20%20mo.md(r%22%22%22%0A%20%20%20%20%23%23%20Production%20planning%0A%20%20%20%20%22%22%22)%0A%20%20%20%20return%0A%0A%0A%40app.cell(hide_code%3DTrue)%0Adef%20_(mo)%3A%0A%20%20%20%20mo.md(r%22%22%22%0A%20%20%20%20This%20notebook%20covers%20six%20LP%20and%20MIP%20production%20planning%20examples%20from%20Chapter%20C%20of%20the%20Xpress%20Applications%20Book%2C%20solved%20via%20%5BMoselPy%5D(https%3A%2F%2Fpypi.org%2Fproject%2Fmoselpy%2F).%20Examples%20range%20from%20simple%20single-product%20capacity%20planning%2C%20through%20multi-product%20glass%20production%20and%20string-indexed%20toy%20assembly%2C%20to%20electronic%20components%20sensitivity%20analysis%2C%20fiberglass%20transshipment%2C%20and%20machine%20assignment.%0A%0A%20%20%20%20%3E%20**Mosel%20source%20files%3A**%20%5B%60models%2Fproduction_planning%2F%60%5D(https%3A%2F%2Fgithub.com%2Ffico-xpress%2Fpython-notebooks%2Ftree%2Fmain%2Fmarimo%2Fpublic%2Fmoselpy%2Fmodels%2Fproduction_planning)%20%26nbsp%3B%7C%26nbsp%3B%20**Data%20files%3A**%20%5B%60data%2Fproduction_planning%2F%60%5D(https%3A%2F%2Fgithub.com%2Ffico-xpress%2Fpython-notebooks%2Ftree%2Fmain%2Fmarimo%2Fpublic%2Fmoselpy%2Fdata%2Fproduction_planning)%0A%0A%20%20%20%20%3E%20**Requirements%3A**%20%60pip%20install%20moselpy%20mosellibs%20xpresslibs%60%20-%20%60mosellibs%60%20provides%20the%20Mosel%20runtime%20and%20%60xpresslibs%60%20the%20Xpress%20solver%20libraries.%20No%20local%20Xpress%20installation%20is%20needed%20to%20run%20these%20notebooks%2C%20as%20long%20as%20the%20packages%20with%20libraries%20have%20been%20installed.%0A%0A%20%20%20%20%3E%20A%20local%20FICO%26reg%3B%20Xpress%20installation%20is%20recommended%20if%20you%20want%20access%20to%20the%20full%20set%20of%20examples%2C%20user%20guides%20and%20documentation%2C%20solver%20libraries%2C%20and%20Xpress%20Workbench.%20Obtain%20one%20via%20the%20%5BFICO%26reg%3B%20Xpress%20community%20license%5D(https%3A%2F%2Fwww.fico.com%2Fen%2Ffico-xpress-community-license)%20page.%20If%20a%20local%20installation%20is%20present%2C%20set%20%60XPRESSDIR%60%20before%20starting%20marimo%20to%20use%20it%20instead%20of%20the%20%60mosellibs%60%20and%20%60xpresslibs%60%20packages.%0A%0A%20%20%20%20%3E%20**MoselPy%20documentation%3A**%20%5BMoselPy%20User%20Guide%20and%20Reference%20Manual%5D(https%3A%2F%2Fwww.fico.com%2Ffico-xpress-optimization%2Fdocs%2Flatest%2Fmosel%2FMoselPy%2Fdhtml)%0A%0A%20%20%20%20%3E%20**Mosel%20examples%20on%20the%20web%3A**%20%5Bexamples.xpress.fico.com%5D(https%3A%2F%2Fexamples.xpress.fico.com%2Fexample.pl%3Fid%3Dmosel_app_3)%0A%0A%20%20%20%20%23%23%23%20MoselPy%20features%20demonstrated%20in%20this%20chapter%0A%0A%20%20%20%20-%20**Parametric%20capacity%20scan**%3A%20C-1%20loads%20the%20bicycle%20model%20once%20and%20runs%20with%20varying%20%60CAP%60%20values%20via%20%60exec_params%60%2C%20collecting%20total%20costs%20into%20a%20%60pd.Series%60.%0A%20%20%20%20-%20**Sensitivity%20analysis%20with%20%60pd.DataFrame%60%20pivot**%3A%20C-4%20varies%20%60CADD%60%20and%20%60CRED%60%20in%20a%20nested%20loop%20and%20collects%20total%20costs%20into%20a%202D%20heatmap.%0A%20%20%20%20-%20**Binary%20I%2FO**%3A%20C-5%20uses%20%60mp.write_binary()%60%20to%20write%20data%20to%20a%20%60.bin%60%20file%20and%20reads%20it%20via%20%60DATASOURCE%3D%22bin%3A...%22%60.%0A%20%20%20%20-%20**Infeasibility%20detection**%3A%20C-6%20demonstrates%20%60model.prob_status.is_solution_available%60%20by%20tightening%20machine%20capacities%20below%20feasible%20limits.%0A%20%20%20%20%22%22%22)%0A%20%20%20%20return%0A%0A%0A%40app.cell(hide_code%3DTrue)%0Adef%20_(mo)%3A%0A%20%20%20%20mo.md(r%22%22%22%0A%20%20%20%20%23%23%20C-1%20Bicycle%20production%20(LP)%0A%20%20%20%20%22%22%22)%0A%20%20%20%20return%0A%0A%0A%40app.cell(hide_code%3DTrue)%0Adef%20_(mo)%3A%0A%20%20%20%20mo.md(r%22%22%22%0A%20%20%20%20*Applications%20Book%20Chapter%20C%2C%20Example%201*%0A%0A%20%20%20%20%3E%20**Mosel%20model%3A**%20%5B%60c1bike_p.mos%60%5D(https%3A%2F%2Fgithub.com%2Ffico-xpress%2Fpython-notebooks%2Fblob%2Fmain%2Fmarimo%2Fpublic%2Fmoselpy%2Fmodels%2Fproduction_planning%2Fc1bike_p.mos)%0A%0A%20%20%20%20A%20sales%20forecast%20gives%20predicted%20bicycle%20demand%20for%20each%20of%2012%20months.%20Normal%20production%20capacity%20is%20%60CAP%60%20bikes%2Fmonth%3B%20overtime%20adds%20up%20to%2050%25%20more%20at%20higher%20cost.%20Excess%20bikes%20can%20be%20stored%20(cost%20%60CSTOCK%60%20per%20bike).%20Find%20the%20production%20plan%20(normal%2C%20overtime%2C%20stored)%20that%20minimizes%20total%20annual%20cost.%0A%0A%20%20%20%20**Sets%3A**%20%24%5Ctext%7BTIMES%7D%20%3D%201..12%24%0A%0A%20%20%20%20**Decision%20variables%3A**%20%24%5Cmathit%7Bpnorm%7D_t%20%5Cgeq%200%24%20(normal%20production%20in%20month%20%24t%24)%3B%20%24%5Cmathit%7Bpover%7D_t%20%5Cgeq%200%24%20(overtime%20production)%3B%20%24%5Cmathit%7Bstore%7D_t%20%5Cgeq%200%24%20(stock%20at%20end%20of%20month%20%24t%24).%0A%0A%20%20%20%20%24%24%0A%20%20%20%20%5Cbegin%7Balign*%7D%0A%20%20%20%20%5Ctext%7Bminimize%7D%20%20%20%5Cquad%20%26%20%5Csum_%7Bt%20%5Cin%20%5Ctext%7BTIMES%7D%7D%20(%5Ctext%7BCNORM%7D%20%5Ccdot%20%5Cmathit%7Bpnorm%7D_t%20%2B%20%5Ctext%7BCOVER%7D%20%5Ccdot%20%5Cmathit%7Bpover%7D_t%20%2B%20%5Ctext%7BCSTOCK%7D%20%5Ccdot%20%5Cmathit%7Bstore%7D_t)%20%5C%5C%0A%20%20%20%20%5Ctext%7Bsubject%20to%7D%20%5Cquad%20%26%20%5Cmathit%7Bpnorm%7D_t%20%2B%20%5Cmathit%7Bpover%7D_t%20%2B%20%5Cmathit%7Bstore%7D_%7Bt-1%7D%20%3D%20%5Ctext%7BDEM%7D_t%20%2B%20%5Cmathit%7Bstore%7D_t%20%5Cquad%20%5Cforall%5C%2C%20t%20%5Cquad%20(%5Cmathit%7Bstore%7D_0%20%3D%20%5Ctext%7BISTOCK%7D)%20%5C%5C%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%26%20%5Cmathit%7Bpnorm%7D_t%20%5Cleq%20%5Ctext%7BCAP%7D%20%5Cquad%20%5Cforall%5C%2C%20t%20%5C%5C%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%26%20%5Cmathit%7Bpover%7D_t%20%5Cleq%200.5%20%5Ccdot%20%5Ctext%7BCAP%7D%20%5Cquad%20%5Cforall%5C%2C%20t%0A%20%20%20%20%5Cend%7Balign*%7D%0A%20%20%20%20%24%24%0A%20%20%20%20%22%22%22)%0A%20%20%20%20return%0A%0A%0A%40app.cell%0Adef%20_()%3A%0A%20%20%20%20MONTHS%20%3D%20%7Bi%2B1%3A%20m%20for%20i%2C%20m%20in%20enumerate(%5B%22Jan%22%2C%22Feb%22%2C%22Mar%22%2C%22Apr%22%2C%22May%22%2C%22Jun%22%2C%22Jul%22%2C%22Aug%22%2C%22Sep%22%2C%22Oct%22%2C%22Nov%22%2C%22Dec%22%5D)%7D%0A%20%20%20%20DEM_BIKE%20%3D%20%7Bi%2B1%3A%20v%20for%20i%2C%20v%20in%20enumerate(%5B30000%2C15000%2C15000%2C25000%2C33000%2C40000%2C45000%2C45000%2C26000%2C14000%2C25000%2C30000%5D)%7D%0A%20%20%20%20CNORM%20%20%20%20%3D%2032%0A%20%20%20%20COVER%20%20%20%20%3D%2040%0A%20%20%20%20CSTOCK_B%20%3D%205%0A%20%20%20%20CAP_BASE%20%3D%2030000%0A%20%20%20%20ISTOCK_B%20%3D%202000%0A%20%20%20%20return%20CAP_BASE%2C%20CNORM%2C%20COVER%2C%20CSTOCK_B%2C%20DEM_BIKE%2C%20ISTOCK_B%2C%20MONTHS%0A%0A%0A%40app.cell(hide_code%3DTrue)%0Adef%20_(mo)%3A%0A%20%20%20%20mo.md(r%22%22%22%0A%20%20%20%20Compile%20and%20load%20%60c1bike_p.mos%60%20once.%20Then%20run%20it%20with%20the%20default%20data%20(CAP%20%3D%2030000)%20to%20get%20the%20base%20solution.%20The%20model%20returns%20%60obj_val%60%20and%20solution%20arrays%20%60sol_pnorm%60%2C%20%60sol_pover%60%2C%20%60sol_store%60%20(each%20a%20dict%20indexed%201..12).%0A%20%20%20%20%22%22%22)%0A%20%20%20%20return%0A%0A%0A%40app.cell%0Adef%20_(%0A%20%20%20%20CAP_BASE%2C%0A%20%20%20%20CNORM%2C%0A%20%20%20%20COVER%2C%0A%20%20%20%20CSTOCK_B%2C%0A%20%20%20%20DEM_BIKE%2C%0A%20%20%20%20ISTOCK_B%2C%0A%20%20%20%20MODELS%2C%0A%20%20%20%20MONTHS%2C%0A%20%20%20%20mp%2C%0A)%3A%0A%20%20%20%20mp.compile_model(f'%7BMODELS%7D%2Fc1bike_p.mos'%2C%20f'%7BMODELS%7D%2Fc1bike_p.bim')%0A%20%20%20%20model%20%3D%20mp.load_model(f'%7BMODELS%7D%2Fc1bike_p.bim')%0A%20%20%20%20model.set_default_stream(mp.StreamType.OUTPUT%2C%20'null%3A')%0A%20%20%20%20_output%20%3D%20model.run(exec_params%3D%7B'DATASOURCE'%3A%20'moselpy%3A'%2C%20'OUTFILE'%3A%20'moselpy%3A'%7D%2C%20input_data%3D%7B'DEM'%3A%20DEM_BIKE%2C%20'CNORM'%3A%20CNORM%2C%20'COVER'%3A%20COVER%2C%20'CSTOCK'%3A%20CSTOCK_B%2C%20'CAP'%3A%20CAP_BASE%2C%20'ISTOCK'%3A%20ISTOCK_B%2C%20'MONTHS'%3A%20MONTHS%7D)%0A%20%20%20%20if%20model.prob_status.is_solution_available%3A%0A%20%20%20%20%20%20%20%20obj_val%20%3D%20_output%5B'obj_val'%5D%0A%20%20%20%20%20%20%20%20sol_pnorm%20%3D%20_output%5B'sol_pnorm'%5D%0A%20%20%20%20%20%20%20%20sol_pover%20%3D%20_output%5B'sol_pover'%5D%0A%20%20%20%20%20%20%20%20sol_store%20%3D%20_output%5B'sol_store'%5D%0A%20%20%20%20%20%20%20%20_dem_list%20%3D%20list(DEM_BIKE.values())%0A%20%20%20%20%20%20%20%20print(f'Total%20cost%20(CAP%3D30000)%3A%20%7Bobj_val%3A%2C.0f%7D')%0A%20%20%20%20%20%20%20%20print()%20%20%23%20dict%3A%20month%20-%3E%20normal%20production%0A%20%20%20%20%20%20%20%20print(f'%7B'Month'%3A%3C5%7D%20%7B'Demand'%3A%3E8%7D%20%7B'Normal'%3A%3E8%7D%20%7B'Overtime'%3A%3E9%7D%20%7B'Stock'%3A%3E7%7D')%20%20%23%20dict%3A%20month%20-%3E%20overtime%20production%0A%20%20%20%20%20%20%20%20for%20t%20in%20range(1%2C%2013)%3A%20%20%23%20dict%3A%20month%20-%3E%20stock%20level%0A%20%20%20%20%20%20%20%20%20%20%20%20print(f'%20%20%7BMONTHS%5Bt%5D%3A%3C4%7D%20%7B_dem_list%5Bt%20-%201%5D%3A%3E8%2C%7D%20%7Bsol_pnorm%5Bt%5D%3A%3E8%2C.0f%7D%20%7Bsol_pover%5Bt%5D%3A%3E9%2C.0f%7D%20%7Bsol_store%5Bt%5D%3A%3E7%2C.0f%7D')%0A%20%20%20%20else%3A%0A%20%20%20%20%20%20%20%20print(f'No%20solution%20found%20(%7Bmodel.prob_status%7D)')%0A%20%20%20%20return%20model%2C%20obj_val%2C%20sol_pnorm%2C%20sol_pover%2C%20sol_store%0A%0A%0A%40app.cell(hide_code%3DTrue)%0Adef%20_(mo)%3A%0A%20%20%20%20mo.md(r%22%22%22%0A%20%20%20%20The%20compiled%20model%20is%20already%20loaded%20-%20no%20recompile%20needed.%20Run%20a%20parametric%20capacity%20loop%3A%20vary%20%60CAP%60%20from%2022000%20to%2036000%20in%20steps%20of%202000%2C%20passing%20each%20value%20as%20an%20%60exec_params%60%20override.%20The%20remaining%20data%20is%20passed%20via%20%60input_data%60%20as%20before.%20Collect%20the%20total%20cost%20at%20each%20capacity%20into%20a%20%60pd.Series%60.%0A%20%20%20%20%22%22%22)%0A%20%20%20%20return%0A%0A%0A%40app.cell%0Adef%20_(CNORM%2C%20COVER%2C%20CSTOCK_B%2C%20DEM_BIKE%2C%20ISTOCK_B%2C%20MONTHS%2C%20model%2C%20pd)%3A%0A%20%20%20%20cap_values%20%3D%20range(22000%2C%2038000%2C%202000)%0A%20%20%20%20costs%20%3D%20%7B%7D%0A%20%20%20%20for%20_cap%20in%20cap_values%3A%0A%20%20%20%20%20%20%20%20_out%20%3D%20model.run(exec_params%3D%7B'DATASOURCE'%3A%20'moselpy%3A'%2C%20'OUTFILE'%3A%20'moselpy%3A'%7D%2C%20input_data%3D%7B'DEM'%3A%20DEM_BIKE%2C%20'CNORM'%3A%20CNORM%2C%20'COVER'%3A%20COVER%2C%20'CSTOCK'%3A%20CSTOCK_B%2C%20'CAP'%3A%20_cap%2C%20'ISTOCK'%3A%20ISTOCK_B%2C%20'MONTHS'%3A%20MONTHS%7D)%0A%20%20%20%20%20%20%20%20if%20model.prob_status.is_solution_available%3A%0A%20%20%20%20%20%20%20%20%20%20%20%20costs%5B_cap%5D%20%3D%20_out%5B'obj_val'%5D%0A%20%20%20%20%20%20%20%20else%3A%0A%20%20%20%20%20%20%20%20%20%20%20%20costs%5B_cap%5D%20%3D%20float('nan')%0A%20%20%20%20cost_series%20%3D%20pd.Series(costs%2C%20name%3D'Total%20cost')%0A%20%20%20%20cost_series.index.name%20%3D%20'CAP'%0A%20%20%20%20print('Parametric%20capacity%20results%3A')%0A%20%20%20%20print(cost_series.apply(lambda%20v%3A%20f'%7Bv%3A%2C.0f%7D').to_string())%0A%20%20%20%20return%20(cost_series%2C)%0A%0A%0A%40app.cell(hide_code%3DTrue)%0Adef%20_(mo)%3A%0A%20%20%20%20mo.md(r%22%22%22%0A%20%20%20%20Two-panel%20chart%3A%20(left)%20stacked%20bar%20chart%20of%20normal%20production%2C%20overtime%2C%20and%20stock%20by%20month%20with%20the%20demand%20line%20overlaid%3B%20(right)%20total%20cost%20vs%20capacity%20from%20the%20parametric%20run%20above.%0A%20%20%20%20%22%22%22)%0A%20%20%20%20return%0A%0A%0A%40app.cell%0Adef%20_(%0A%20%20%20%20DEM_BIKE%2C%0A%20%20%20%20MONTHS%2C%0A%20%20%20%20cost_series%2C%0A%20%20%20%20mo%2C%0A%20%20%20%20np%2C%0A%20%20%20%20obj_val%2C%0A%20%20%20%20plt%2C%0A%20%20%20%20sol_pnorm%2C%0A%20%20%20%20sol_pover%2C%0A%20%20%20%20sol_store%2C%0A)%3A%0A%20%20%20%20months_list%20%3D%20list(MONTHS.values())%20%20%23%20%5B%22Jan%22%2C%20...%2C%20%22Dec%22%5D%0A%20%20%20%20_dem_list%20%3D%20list(DEM_BIKE.values())%20%20%23%20%5B30000%2C%20...%2C%2030000%5D%0A%20%20%20%20months_idx%20%3D%20range(1%2C%2013)%0A%20%20%20%20_fig%2C%20(_ax1%2C%20_ax2)%20%3D%20plt.subplots(1%2C%202%2C%20figsize%3D(14%2C%205))%0A%20%20%20%20norm_vals%20%3D%20%5Bsol_pnorm%5Bt%5D%20%2F%201000%20for%20t%20in%20months_idx%5D%0A%20%20%20%20over_vals%20%3D%20%5Bsol_pover%5Bt%5D%20%2F%201000%20for%20t%20in%20months_idx%5D%0A%20%20%20%20%23%20Left%3A%20production%20breakdown%0A%20%20%20%20store_vals%20%3D%20%5Bsol_store%5Bt%5D%20%2F%201000%20for%20t%20in%20months_idx%5D%0A%20%20%20%20dem_vals%20%3D%20%5Bd%20%2F%201000%20for%20d%20in%20_dem_list%5D%0A%20%20%20%20x%20%3D%20np.arange(12)%0A%20%20%20%20_ax1.bar(x%2C%20norm_vals%2C%20label%3D'Normal'%2C%20color%3D'%231565C0')%0A%20%20%20%20_ax1.bar(x%2C%20over_vals%2C%20bottom%3Dnorm_vals%2C%20label%3D'Overtime'%2C%20color%3D'%23FB8C00')%0A%20%20%20%20_ax1.bar(x%2C%20store_vals%2C%20bottom%3D%5Bn%20%2B%20o%20for%20n%2C%20o%20in%20zip(norm_vals%2C%20over_vals)%5D%2C%20label%3D'Stock'%2C%20color%3D'%2343A047'%2C%20alpha%3D0.7)%0A%20%20%20%20_ax1.plot(x%2C%20dem_vals%2C%20'k--'%2C%20linewidth%3D1.5%2C%20label%3D'Demand')%0A%20%20%20%20_ax1.set_xticks(x)%0A%20%20%20%20_ax1.set_xticklabels(months_list%2C%20rotation%3D45)%0A%20%20%20%20_ax1.set_ylabel('Bicycles%20(thousands)')%0A%20%20%20%20_ax1.set_title(f'C-1%20Bicycle%20production%20plan%20(CAP%3D30000%2C%20cost%3D%7Bobj_val%3A%2C.0f%7D)')%0A%20%20%20%20_ax1.legend(loc%3D'upper%20right'%2C%20fontsize%3D8)%0A%20%20%20%20_cap_list%20%3D%20list(cost_series.index)%0A%20%20%20%20cost_list%20%3D%20list(cost_series.values)%0A%20%20%20%20_ax2.plot(_cap_list%2C%20%5Bc%20%2F%201000000.0%20for%20c%20in%20cost_list%5D%2C%20'o-'%2C%20color%3D'%231565C0')%0A%20%20%20%20_ax2.axvline(30000%2C%20color%3D'gray'%2C%20linestyle%3D'--'%2C%20linewidth%3D1%2C%20label%3D'Base%20CAP%3D30000')%0A%20%20%20%20%23%20Right%3A%20cost%20vs%20capacity%0A%20%20%20%20_ax2.set_xlabel('Monthly%20capacity%20(bikes)')%0A%20%20%20%20_ax2.set_ylabel('Total%20cost%20(millions)')%0A%20%20%20%20_ax2.set_title('C-1%20Total%20cost%20vs%20normal%20capacity')%0A%20%20%20%20_ax2.legend()%0A%20%20%20%20plt.tight_layout()%0A%20%20%20%20mo.output.append(plt.gcf())%0A%20%20%20%20return%0A%0A%0A%40app.cell(hide_code%3DTrue)%0Adef%20_(mo)%3A%0A%20%20%20%20mo.md(r%22%22%22%0A%20%20%20%20%23%23%20C-2%20Glass%20production%20(LP)%0A%20%20%20%20%22%22%22)%0A%20%20%20%20return%0A%0A%0A%40app.cell(hide_code%3DTrue)%0Adef%20_(mo)%3A%0A%20%20%20%20mo.md(r%22%22%22%0A%20%20%20%20*Applications%20Book%20Chapter%20C%2C%20Example%202*%0A%0A%20%20%20%20%3E%20**Mosel%20model%3A**%20%5B%60c2glass_p.mos%60%5D(https%3A%2F%2Fgithub.com%2Ffico-xpress%2Fpython-notebooks%2Fblob%2Fmain%2Fmarimo%2Fpublic%2Fmoselpy%2Fmodels%2Fproduction_planning%2Fc2glass_p.mos)%0A%0A%20%20%20%20A%20French%20company%20produces%206%20types%20of%20drinking%20glasses%20over%20a%2012-week%20planning%20horizon.%20Each%20glass%20type%20has%20its%20own%20production%20cost%2C%20storage%20cost%2C%20worker%20time%2C%20machine%20time%2C%20and%20space%20requirement.%20Weekly%20worker%20hours%2C%20machine%20hours%2C%20and%20storage%20space%20are%20all%20limited.%20Determine%20how%20much%20of%20each%20type%20to%20produce%20each%20week%20to%20minimize%20total%20cost%20while%20meeting%20all%20demands%20and%20ending%20with%20minimum%20final%20stocks.%0A%0A%20%20%20%20**Sets%3A**%20%24%5Ctext%7BPRODS%7D%20%3D%201..6%24%2C%20%24%5Ctext%7BWEEKS%7D%20%3D%201..12%24%0A%0A%20%20%20%20**Decision%20variables%3A**%20%24%5Cmathit%7Bproduce%7D_%7Bp%2Ct%7D%20%5Cgeq%200%24%20(units%20of%20product%20%24p%24%20produced%20in%20week%20%24t%24)%3B%20%24%5Cmathit%7Bstore%7D_%7Bp%2Ct%7D%20%5Cgeq%200%24%20(stock%20at%20end%20of%20week%20%24t%24).%0A%0A%20%20%20%20%24%24%0A%20%20%20%20%5Cbegin%7Balign*%7D%0A%20%20%20%20%5Ctext%7Bminimize%7D%20%20%20%5Cquad%20%26%20%5Csum_%7Bp%2Ct%7D%20(%5Ctext%7BCPROD%7D_p%20%5Ccdot%20%5Cmathit%7Bproduce%7D_%7Bp%2Ct%7D%20%2B%20%5Ctext%7BCSTOCK%7D_p%20%5Ccdot%20%5Cmathit%7Bstore%7D_%7Bp%2Ct%7D)%20%5C%5C%0A%20%20%20%20%5Ctext%7Bsubject%20to%7D%20%5Cquad%20%26%20%5Cmathit%7Bstore%7D_%7Bp%2Ct%7D%20%3D%20%5Cmathit%7Bstore%7D_%7Bp%2Ct-1%7D%20%2B%20%5Cmathit%7Bproduce%7D_%7Bp%2Ct%7D%20-%20%5Ctext%7BDEM%7D_%7Bp%2Ct%7D%20%5Cquad%20%5Cforall%5C%2C%20p%2Ct%20%5Cquad%20(%5Cmathit%7Bstore%7D_%7Bp%2C0%7D%20%3D%20%5Ctext%7BISTOCK%7D_p)%20%5C%5C%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%26%20%5Cmathit%7Bstore%7D_%7Bp%2C12%7D%20%5Cgeq%20%5Ctext%7BFSTOCK%7D_p%20%5Cquad%20%5Cforall%5C%2C%20p%20%5C%5C%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%26%20%5Csum_p%20%5Ctext%7BTIMEW%7D_p%20%5Ccdot%20%5Cmathit%7Bproduce%7D_%7Bp%2Ct%7D%20%5Cleq%20%5Ctext%7BCAPW%7D%20%5Cquad%20%5Cforall%5C%2C%20t%20%5C%5C%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%26%20%5Csum_p%20%5Ctext%7BTIMEM%7D_p%20%5Ccdot%20%5Cmathit%7Bproduce%7D_%7Bp%2Ct%7D%20%5Cleq%20%5Ctext%7BCAPM%7D%20%5Cquad%20%5Cforall%5C%2C%20t%20%5C%5C%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%26%20%5Csum_p%20%5Ctext%7BSPACE%7D_p%20%5Ccdot%20%5Cmathit%7Bstore%7D_%7Bp%2Ct%7D%20%5Cleq%20%5Ctext%7BCAPS%7D%20%5Cquad%20%5Cforall%5C%2C%20t%0A%20%20%20%20%5Cend%7Balign*%7D%0A%20%20%20%20%24%24%0A%20%20%20%20%22%22%22)%0A%20%20%20%20return%0A%0A%0A%40app.cell%0Adef%20_(pd)%3A%0A%20%20%20%20PRODS_G%20%3D%20%5B1%2C2%2C3%2C4%2C5%2C6%5D%0A%20%20%20%20WEEKS_G%20%20%3D%20list(range(1%2C%2013))%0A%20%20%20%20DEM_G_DATA%20%3D%20%5B%0A%20%20%20%20%20%20%20%20%5B20%2C22%2C18%2C35%2C17%2C19%2C23%2C20%2C29%2C30%2C28%2C32%5D%2C%0A%20%20%20%20%20%20%20%20%5B17%2C19%2C23%2C20%2C11%2C10%2C12%2C34%2C21%2C23%2C30%2C12%5D%2C%0A%20%20%20%20%20%20%20%20%5B18%2C35%2C17%2C10%2C%209%2C21%2C23%2C15%2C10%2C%200%2C13%2C17%5D%2C%0A%20%20%20%20%20%20%20%20%5B31%2C45%2C24%2C38%2C41%2C20%2C19%2C37%2C28%2C12%2C30%2C37%5D%2C%0A%20%20%20%20%20%20%20%20%5B23%2C20%2C23%2C15%2C10%2C22%2C18%2C30%2C28%2C%207%2C15%2C10%5D%2C%0A%20%20%20%20%20%20%20%20%5B22%2C18%2C20%2C19%2C18%2C35%2C%200%2C28%2C12%2C30%2C21%2C23%5D%2C%0A%20%20%20%20%5D%0A%20%20%20%20DEM_G%20%20%20%20%3D%20pd.DataFrame(DEM_G_DATA%2C%20index%3DPRODS_G%2C%20columns%3DWEEKS_G)%0A%20%20%20%20CPROD_G%20%20%3D%20%7Bi%2B1%3A%20v%20for%20i%2C%20v%20in%20enumerate(%5B100%2C%2080%2C%20110%2C%2090%2C%20200%2C%20140%5D)%7D%0A%20%20%20%20CSTOCK_G%20%3D%20%7Bi%2B1%3A%20v%20for%20i%2C%20v%20in%20enumerate(%5B25%2C%2028%2C%2025%2C%2027%2C%2010%2C%2020%5D)%7D%0A%20%20%20%20FSTOCK_G%20%3D%20%7Bi%2B1%3A%20v%20for%20i%2C%20v%20in%20enumerate(%5B10%2C%2010%2C%2010%2C%2010%2C%2010%2C%2010%5D)%7D%0A%20%20%20%20ISTOCK_G%20%3D%20%7Bi%2B1%3A%20v%20for%20i%2C%20v%20in%20enumerate(%5B50%2C%2020%2C%200%2C%2015%2C%200%2C%2010%5D)%7D%0A%20%20%20%20CAPW_G%2C%20CAPM_G%2C%20CAPS_G%20%3D%20390%2C%20850%2C%201000%0A%20%20%20%20TIMEW_G%20%20%3D%20%7Bi%2B1%3A%20v%20for%20i%2C%20v%20in%20enumerate(%5B3%2C%203%2C%203%2C%202%2C%204%2C%204%5D)%7D%0A%20%20%20%20TIMEM_G%20%20%3D%20%7Bi%2B1%3A%20v%20for%20i%2C%20v%20in%20enumerate(%5B2%2C%201%2C%204%2C%208%2C%2011%2C%209%5D)%7D%0A%20%20%20%20SPACE_G%20%20%3D%20%7Bi%2B1%3A%20v%20for%20i%2C%20v%20in%20enumerate(%5B4%2C%205%2C%205%2C%206%2C%204%2C%209%5D)%7D%0A%20%20%20%20return%20(%0A%20%20%20%20%20%20%20%20CAPM_G%2C%0A%20%20%20%20%20%20%20%20CAPS_G%2C%0A%20%20%20%20%20%20%20%20CAPW_G%2C%0A%20%20%20%20%20%20%20%20CPROD_G%2C%0A%20%20%20%20%20%20%20%20CSTOCK_G%2C%0A%20%20%20%20%20%20%20%20DEM_G%2C%0A%20%20%20%20%20%20%20%20FSTOCK_G%2C%0A%20%20%20%20%20%20%20%20ISTOCK_G%2C%0A%20%20%20%20%20%20%20%20SPACE_G%2C%0A%20%20%20%20%20%20%20%20TIMEM_G%2C%0A%20%20%20%20%20%20%20%20TIMEW_G%2C%0A%20%20%20%20)%0A%0A%0A%40app.cell(hide_code%3DTrue)%0Adef%20_(mo)%3A%0A%20%20%20%20mo.md(r%22%22%22%0A%20%20%20%20Heatmap%20of%20weekly%20demand%20per%20glass%20type.%20Each%20row%20is%20a%20product%2C%20each%20column%20is%20a%20week.%20High-demand%20cells%20stand%20out%20immediately%20and%20help%20build%20intuition%20before%20seeing%20the%20optimized%20production%20plan.%0A%20%20%20%20%22%22%22)%0A%20%20%20%20return%0A%0A%0A%40app.cell%0Adef%20_(DEM_G%2C%20mo%2C%20plt)%3A%0A%20%20%20%20_fig%2C%20_ax%20%3D%20plt.subplots(figsize%3D(12%2C%204))%0A%20%20%20%20_im%20%3D%20_ax.imshow(DEM_G.values%2C%20cmap%3D'Blues'%2C%20aspect%3D'auto')%0A%20%20%20%20plt.colorbar(_im%2C%20ax%3D_ax%2C%20label%3D'Units%20demanded')%0A%20%20%20%20_ax.set_xticks(range(12))%0A%20%20%20%20_ax.set_xticklabels(%5Bf'Wk%20%7Bw%7D'%20for%20w%20in%20range(1%2C%2013)%5D%2C%20fontsize%3D8)%0A%20%20%20%20_ax.set_yticks(range(6))%0A%20%20%20%20_ax.set_yticklabels(%5Bf'Glass%20%7Bp%7D'%20for%20p%20in%20range(1%2C%207)%5D%2C%20fontsize%3D8)%0A%20%20%20%20for%20_p%20in%20range(6)%3A%0A%20%20%20%20%20%20%20%20for%20w%20in%20range(12)%3A%0A%20%20%20%20%20%20%20%20%20%20%20%20_ax.text(w%2C%20_p%2C%20str(DEM_G.iloc%5B_p%2C%20w%5D)%2C%20ha%3D'center'%2C%20va%3D'center'%2C%20fontsize%3D7%2C%20color%3D'white'%20if%20DEM_G.iloc%5B_p%2C%20w%5D%20%3E%2025%20else%20'black')%0A%20%20%20%20_ax.set_title('C-2%20Glass%20production%20-%20weekly%20demand%20by%20product')%0A%20%20%20%20plt.tight_layout()%0A%20%20%20%20mo.output.append(plt.gcf())%0A%20%20%20%20return%0A%0A%0A%40app.cell(hide_code%3DTrue)%0Adef%20_(mo)%3A%0A%20%20%20%20mo.md(r%22%22%22%0A%20%20%20%20Compile%20and%20run%20%60c2glass_p.mos%60.%20The%20model%20returns%20%60sol_produce%60%20and%20%60sol_store%60%20as%20dicts%20with%20tuple%20keys%20%60(product%2C%20week)%60.%20Reshape%20these%20into%20DataFrames%20(products%20as%20rows%2C%20weeks%20as%20columns)%20for%20inspection%20and%20visualization.%0A%20%20%20%20%22%22%22)%0A%20%20%20%20return%0A%0A%0A%40app.cell%0Adef%20_(%0A%20%20%20%20CAPM_G%2C%0A%20%20%20%20CAPS_G%2C%0A%20%20%20%20CAPW_G%2C%0A%20%20%20%20CPROD_G%2C%0A%20%20%20%20CSTOCK_G%2C%0A%20%20%20%20DEM_G%2C%0A%20%20%20%20FSTOCK_G%2C%0A%20%20%20%20ISTOCK_G%2C%0A%20%20%20%20MODELS%2C%0A%20%20%20%20SPACE_G%2C%0A%20%20%20%20TIMEM_G%2C%0A%20%20%20%20TIMEW_G%2C%0A%20%20%20%20mp%2C%0A%20%20%20%20pd%2C%0A)%3A%0A%20%20%20%20mp.compile_model(f'%7BMODELS%7D%2Fc2glass_p.mos'%2C%20f'%7BMODELS%7D%2Fc2glass_p.bim')%0A%20%20%20%20with%20mp.load_model(f'%7BMODELS%7D%2Fc2glass_p.bim')%20as%20model_1%3A%0A%20%20%20%20%20%20%20%20_output%20%3D%20model_1.run(exec_params%3D%7B'DATASOURCE'%3A%20'moselpy%3A'%2C%20'OUTFILE'%3A%20'moselpy%3A'%7D%2C%20input_data%3D%7B'DEM'%3A%20DEM_G%2C%20'CPROD'%3A%20CPROD_G%2C%20'CSTOCK'%3A%20CSTOCK_G%2C%20'FSTOCK'%3A%20FSTOCK_G%2C%20'ISTOCK'%3A%20ISTOCK_G%2C%20'CAPW'%3A%20CAPW_G%2C%20'CAPM'%3A%20CAPM_G%2C%20'CAPS'%3A%20CAPS_G%2C%20'TIMEW'%3A%20TIMEW_G%2C%20'TIMEM'%3A%20TIMEM_G%2C%20'SPACE'%3A%20SPACE_G%7D)%0A%20%20%20%20%20%20%20%20if%20model_1.prob_status.is_solution_available%3A%0A%20%20%20%20%20%20%20%20%20%20%20%20obj_val_1%20%3D%20_output%5B'obj_val'%5D%0A%20%20%20%20%20%20%20%20%20%20%20%20sol_produce%20%3D%20_output%5B'sol_produce'%5D%0A%20%20%20%20%20%20%20%20%20%20%20%20sol_store_1%20%3D%20_output%5B'sol_store'%5D%0A%20%20%20%20%20%20%20%20%20%20%20%20_NP%2C%20_NW%20%3D%20(6%2C%2012)%0A%20%20%20%20%20%20%20%20%20%20%20%20_produce_df%20%3D%20pd.DataFrame(%7Bw%3A%20%5Bsol_produce%5Bp%2C%20w%5D%20for%20p%20in%20range(1%2C%20_NP%20%2B%201)%5D%20for%20w%20in%20range(1%2C%20_NW%20%2B%201)%7D%2C%20index%3D%5Bf'Glass%20%7Bp%7D'%20for%20p%20in%20range(1%2C%20_NP%20%2B%201)%5D)%0A%20%20%20%20%20%20%20%20%20%20%20%20_produce_df.columns.name%20%3D%20'Week'%0A%20%20%20%20%20%20%20%20%20%20%20%20print(f'Total%20cost%3A%20%7Bobj_val_1%3A%2C.0f%7D')%0A%20%20%20%20%20%20%20%20%20%20%20%20print()%0A%20%20%20%20%20%20%20%20%20%20%20%20print('Production%20plan%20(units%20per%20week)%3A')%0A%20%20%20%20%20%20%20%20%20%20%20%20print(_produce_df.round(0).astype(int).to_string())%0A%20%20%20%20%20%20%20%20else%3A%0A%20%20%20%20%20%20%20%20%20%20%20%20print(f'No%20solution%20found%20(%7Bmodel_1.prob_status%7D)')%0A%20%20%20%20return%20obj_val_1%2C%20sol_produce%2C%20sol_store_1%0A%0A%0A%40app.cell(hide_code%3DTrue)%0Adef%20_(mo)%3A%0A%20%20%20%20mo.md(r%22%22%22%0A%20%20%20%20Two-panel%20dashboard%3A%20(top)%20stacked%20bar%20chart%20showing%20weekly%20production%20by%20glass%20type%3B%20(bottom)%20capacity%20utilization%20for%20workers%2C%20machines%2C%20and%20storage%20across%20weeks.%0A%20%20%20%20%22%22%22)%0A%20%20%20%20return%0A%0A%0A%40app.cell%0Adef%20_(%0A%20%20%20%20CAPM_G%2C%0A%20%20%20%20CAPS_G%2C%0A%20%20%20%20CAPW_G%2C%0A%20%20%20%20SPACE_G%2C%0A%20%20%20%20TIMEM_G%2C%0A%20%20%20%20TIMEW_G%2C%0A%20%20%20%20mo%2C%0A%20%20%20%20np%2C%0A%20%20%20%20obj_val_1%2C%0A%20%20%20%20plt%2C%0A%20%20%20%20sol_produce%2C%0A%20%20%20%20sol_store_1%2C%0A)%3A%0A%20%20%20%20_NP%2C%20_NW%20%3D%20(6%2C%2012)%0A%20%20%20%20prod_colors%20%3D%20plt.cm.tab10.colors%0A%20%20%20%20_fig%2C%20(_ax1%2C%20_ax2)%20%3D%20plt.subplots(2%2C%201%2C%20figsize%3D(13%2C%209))%0A%20%20%20%20weeks%20%3D%20list(range(1%2C%20_NW%20%2B%201))%0A%20%20%20%20bottoms%20%3D%20np.zeros(_NW)%0A%20%20%20%20for%20_p%20in%20range(1%2C%20_NP%20%2B%201)%3A%0A%20%20%20%20%20%20%20%20vals%20%3D%20np.array(%5Bsol_produce%5B_p%2C%20w%5D%20for%20w%20in%20weeks%5D)%0A%20%20%20%20%20%20%20%20_ax1.bar(weeks%2C%20vals%2C%20bottom%3Dbottoms%2C%20color%3Dprod_colors%5B(_p%20-%201)%20%25%2010%5D%2C%20label%3Df'Glass%20%7B_p%7D'%2C%20edgecolor%3D'white')%0A%20%20%20%20%20%20%20%20bottoms%20%3D%20bottoms%20%2B%20vals%0A%20%20%20%20_ax1.set_xticks(weeks)%0A%20%20%20%20_ax1.set_xlabel('Week')%0A%20%20%20%20_ax1.set_ylabel('Units%20produced')%0A%20%20%20%20_ax1.set_title(f'C-2%20Glass%20production%20plan%20-%20total%20cost%20%7Bobj_val_1%3A%2C.0f%7D')%0A%20%20%20%20_ax1.legend(loc%3D'upper%20right'%2C%20ncol%3D3%2C%20fontsize%3D8)%0A%20%20%20%20worker_util%20%3D%20%5Bsum((TIMEW_G%5Bp%5D%20*%20sol_produce%5Bp%2C%20w%5D%20for%20p%20in%20range(1%2C%20_NP%20%2B%201)))%20for%20w%20in%20weeks%5D%0A%20%20%20%20machine_util%20%3D%20%5Bsum((TIMEM_G%5Bp%5D%20*%20sol_produce%5Bp%2C%20w%5D%20for%20p%20in%20range(1%2C%20_NP%20%2B%201)))%20for%20w%20in%20weeks%5D%0A%20%20%20%20storage_util%20%3D%20%5Bsum((SPACE_G%5Bp%5D%20*%20sol_store_1%5Bp%2C%20w%5D%20for%20p%20in%20range(1%2C%20_NP%20%2B%201)))%20for%20w%20in%20weeks%5D%0A%20%20%20%20_ax2.plot(weeks%2C%20%5Bu%20%2F%20CAPW_G%20*%20100%20for%20u%20in%20worker_util%5D%2C%20'o-'%2C%20color%3D'%231565C0'%2C%20label%3Df'Workers%20(cap%3D%7BCAPW_G%7Dh)')%0A%20%20%20%20_ax2.plot(weeks%2C%20%5Bu%20%2F%20CAPM_G%20*%20100%20for%20u%20in%20machine_util%5D%2C%20's-'%2C%20color%3D'%23FB8C00'%2C%20label%3Df'Machines%20(cap%3D%7BCAPM_G%7Dh)')%0A%20%20%20%20_ax2.plot(weeks%2C%20%5Bu%20%2F%20CAPS_G%20*%20100%20for%20u%20in%20storage_util%5D%2C%20'%5E-'%2C%20color%3D'%2343A047'%2C%20label%3Df'Storage%20(cap%3D%7BCAPS_G%7D)')%0A%20%20%20%20_ax2.axhline(100%2C%20color%3D'red'%2C%20linestyle%3D'--'%2C%20linewidth%3D1%2C%20label%3D'100%25%20capacity')%0A%20%20%20%20_ax2.set_xticks(weeks)%0A%20%20%20%20_ax2.set_xlabel('Week')%0A%20%20%20%20_ax2.set_ylabel('Utilization%20(%25)')%0A%20%20%20%20_ax2.set_title('C-2%20Capacity%20utilization%20by%20resource')%0A%20%20%20%20_ax2.legend(loc%3D'upper%20right'%2C%20fontsize%3D8)%0A%20%20%20%20_ax2.set_ylim(0%2C%20115)%0A%20%20%20%20plt.tight_layout()%0A%20%20%20%20mo.output.append(plt.gcf())%0A%20%20%20%20return%0A%0A%0A%40app.cell(hide_code%3DTrue)%0Adef%20_(mo)%3A%0A%20%20%20%20mo.md(r%22%22%22%0A%20%20%20%20%23%23%20C-3%20Toy%20lorry%20production%20(MIP)%0A%20%20%20%20%22%22%22)%0A%20%20%20%20return%0A%0A%0A%40app.cell(hide_code%3DTrue)%0Adef%20_(mo)%3A%0A%20%20%20%20mo.md(r%22%22%22%0A%20%20%20%20*Applications%20Book%20Chapter%20C%2C%20Example%203*%0A%0A%20%20%20%20%3E%20**Mosel%20model%3A**%20%5B%60c3toy_p.mos%60%5D(https%3A%2F%2Fgithub.com%2Ffico-xpress%2Fpython-notebooks%2Fblob%2Fmain%2Fmarimo%2Fpublic%2Fmoselpy%2Fmodels%2Fproduction_planning%2Fc3toy_p.mos)%0A%0A%20%20%20%20A%20company%20produces%20two%20types%20of%20toy%20lorry%20(blue%20and%20red)%2C%20each%20requiring%2013%20components.%20Some%20components%20can%20be%20assembled%20in-house%3B%20others%20must%20be%20bought.%20Given%20equal%20demand%20for%20both%20lorry%20types%2C%20decide%20how%20many%20of%20each%20item%20to%20produce%20or%20buy%20to%20minimize%20total%20cost.%0A%0A%20%20%20%20The%20model%20uses%20**string-indexed%20sets**%20(%60ASMBL%60%2C%20%60PREPROD%60%2C%20%60FINAL%60%2C%20%60ITEMS%60)%20inferred%20from%20the%20data.%20All%20input%20arrays%20(%60DEM%60%2C%20%60CBUY%60%2C%20%60REQ%60%2C%20%60CPROD%60%2C%20%60CAP%60)%20are%20passed%20from%20Python%20via%20%60input_data%60.%20When%20results%20are%20returned%20to%20Python%2C%20all%20dictionaries%20use%20**string%20keys**%20matching%20the%20Mosel%20item%20names.%0A%0A%20%20%20%20**Sets%3A**%20%24%5Ctext%7BITEMS%7D%24%2C%20%24%5Ctext%7BFINAL%7D%20%5Csubset%20%5Ctext%7BITEMS%7D%24%2C%20%24%5Ctext%7BASMBL%7D%20%5Csubset%20%5Ctext%7BITEMS%7D%24%2C%20%24%5Ctext%7BPREPROD%7D%20%5Csubset%20%5Ctext%7BITEMS%7D%24%0A%0A%20%20%20%20**Decision%20variables%3A**%20%24%5Cmathit%7Bproduce%7D_p%20%5Cin%20%5Cmathbb%7BZ%7D_%7B%5Cgeq%200%7D%24%20for%20%24p%20%5Cin%20%5Ctext%7BASMBL%7D%24%3B%20%24%5Cmathit%7Bbuy%7D_p%20%5Cin%20%5Cmathbb%7BZ%7D_%7B%5Cgeq%200%7D%24%20for%20%24p%20%5Cin%20%5Ctext%7BPREPROD%7D%24.%0A%0A%20%20%20%20%24%24%0A%20%20%20%20%5Cbegin%7Balign*%7D%0A%20%20%20%20%5Ctext%7Bminimize%7D%20%20%20%5Cquad%20%26%20%5Csum_%7Bp%20%5Cin%20%5Ctext%7BPREPROD%7D%7D%20%5Ctext%7BCBUY%7D_p%20%5Ccdot%20%5Cmathit%7Bbuy%7D_p%20%2B%20%5Csum_%7Bp%20%5Cin%20%5Ctext%7BASMBL%7D%7D%20%5Ctext%7BCPROD%7D_p%20%5Ccdot%20%5Cmathit%7Bproduce%7D_p%20%5C%5C%0A%20%20%20%20%5Ctext%7Bsubject%20to%7D%20%5Cquad%20%26%20%5Cmathit%7Bproduce%7D_p%20%5Cgeq%20%5Ctext%7BDEM%7D_p%20%5Cquad%20%5Cforall%5C%2C%20p%20%5Cin%20%5Ctext%7BFINAL%7D%20%5C%5C%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%26%20%5Cmathit%7Bbuy%7D_p%20%2B%20%5Cmathit%7Bproduce%7D_p%20%5Ccdot%20%5Bp%20%5Cin%20%5Ctext%7BASMBL%7D%5D%20%5Cgeq%20%5Csum_%7Bq%20%5Cin%20%5Ctext%7BASMBL%7D%7D%20%5Ctext%7BREQ%7D_%7Bq%2Cp%7D%20%5Ccdot%20%5Cmathit%7Bproduce%7D_q%20%5Cquad%20%5Cforall%5C%2C%20p%20%5Cin%20%5Ctext%7BPREPROD%7D%20%5C%5C%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%26%20%5Cmathit%7Bproduce%7D_p%20%5Cleq%20%5Ctext%7BCAP%7D_p%20%5Cquad%20%5Cforall%5C%2C%20p%20%5Cin%20%5Ctext%7BASMBL%7D%0A%20%20%20%20%5Cend%7Balign*%7D%0A%20%20%20%20%24%24%0A%20%20%20%20%22%22%22)%0A%20%20%20%20return%0A%0A%0A%40app.cell%0Adef%20_()%3A%0A%20%20%20%20REQ_T%20%3D%20%7B%0A%20%20%20%20%20%20%20%20(%22axle%22%2C%22wheel%22)%3A2%2C%20(%22axle%22%2C%22steel%20bar%22)%3A1%2C%0A%20%20%20%20%20%20%20%20(%22assembled%20chassis%22%2C%22bumper%22)%3A2%2C%20(%22assembled%20chassis%22%2C%22axle%22)%3A2%2C%20(%22assembled%20chassis%22%2C%22chassis%22)%3A1%2C%0A%20%20%20%20%20%20%20%20(%22assembled%20cabin%22%2C%22cabin%22)%3A1%2C%20(%22assembled%20cabin%22%2C%22door%20window%22)%3A2%2C%20(%22assembled%20cabin%22%2C%22windscreen%22)%3A1%2C%0A%20%20%20%20%20%20%20%20(%22blue%20lorry%22%2C%22assembled%20chassis%22)%3A1%2C%20(%22blue%20lorry%22%2C%22container%22)%3A1%2C%20(%22blue%20lorry%22%2C%22assembled%20cabin%22)%3A1%2C%0A%20%20%20%20%20%20%20%20(%22blue%20lorry%22%2C%22blue%20motor%22)%3A1%2C%20(%22blue%20lorry%22%2C%22headlight%22)%3A2%2C%0A%20%20%20%20%20%20%20%20(%22red%20lorry%22%2C%22assembled%20chassis%22)%3A1%2C%20(%22red%20lorry%22%2C%22tank%22)%3A1%2C%20(%22red%20lorry%22%2C%22assembled%20cabin%22)%3A1%2C%0A%20%20%20%20%20%20%20%20(%22red%20lorry%22%2C%22red%20motor%22)%3A1%2C%20(%22red%20lorry%22%2C%22headlight%22)%3A2%2C%0A%20%20%20%20%7D%0A%20%20%20%20CBUY_T%20%3D%20%7B%0A%20%20%20%20%20%20%20%20%22wheel%22%3A0.30%2C%20%22steel%20bar%22%3A1%2C%20%22bumper%22%3A0.2%2C%20%22axle%22%3A12.75%2C%20%22chassis%22%3A0.8%2C%0A%20%20%20%20%20%20%20%20%22cabin%22%3A2.75%2C%20%22door%20window%22%3A0.1%2C%20%22windscreen%22%3A0.29%2C%20%22assembled%20chassis%22%3A30%2C%0A%20%20%20%20%20%20%20%20%22container%22%3A2.60%2C%20%22tank%22%3A3%2C%20%22assembled%20cabin%22%3A3%2C%20%22blue%20motor%22%3A1.65%2C%0A%20%20%20%20%20%20%20%20%22red%20motor%22%3A1.65%2C%20%22headlight%22%3A0.15%2C%0A%20%20%20%20%7D%0A%20%20%20%20CPROD_T%20%3D%20%7B%22axle%22%3A6.80%2C%20%22assembled%20chassis%22%3A3.55%2C%20%22assembled%20cabin%22%3A3.20%2C%20%22blue%20lorry%22%3A2.20%2C%20%22red%20lorry%22%3A2.60%7D%0A%20%20%20%20CAP_T%20%20%20%3D%20%7B%22axle%22%3A600%2C%20%20%22assembled%20chassis%22%3A4000%2C%20%20%22assembled%20cabin%22%3A3000%2C%20%20%22blue%20lorry%22%3A4000%2C%20%20%22red%20lorry%22%3A5000%7D%0A%20%20%20%20DEM_T%20%20%20%3D%20%7B%22blue%20lorry%22%3A3000%2C%20%22red%20lorry%22%3A3000%7D%0A%20%20%20%20return%20CAP_T%2C%20CBUY_T%2C%20CPROD_T%2C%20DEM_T%2C%20REQ_T%0A%0A%0A%40app.cell(hide_code%3DTrue)%0Adef%20_(mo)%3A%0A%20%20%20%20mo.md(r%22%22%22%0A%20%20%20%20Compile%20and%20run%20%60c3toy_p.mos%60.%20Because%20sets%20are%20string-indexed%2C%20%60sol_produce%60%20and%20%60sol_buy%60%20come%20back%20as%20Python%20dicts%20with%20string%20keys%20(e.g.%20%60sol_produce%5B%22blue%20lorry%22%5D%60%2C%20%60sol_buy%5B%22wheel%22%5D%60).%20No%20integer%20indices%20needed.%0A%20%20%20%20%22%22%22)%0A%20%20%20%20return%0A%0A%0A%40app.cell%0Adef%20_(CAP_T%2C%20CBUY_T%2C%20CPROD_T%2C%20DEM_T%2C%20MODELS%2C%20REQ_T%2C%20mp)%3A%0A%20%20%20%20mp.compile_model(f'%7BMODELS%7D%2Fc3toy_p.mos'%2C%20f'%7BMODELS%7D%2Fc3toy_p.bim')%0A%20%20%20%20with%20mp.load_model(f'%7BMODELS%7D%2Fc3toy_p.bim')%20as%20model_2%3A%0A%20%20%20%20%20%20%20%20_output%20%3D%20model_2.run(exec_params%3D%7B'DATASOURCE'%3A%20'moselpy%3A'%2C%20'OUTFILE'%3A%20'moselpy%3A'%7D%2C%20input_data%3D%7B'DEM'%3A%20DEM_T%2C%20'CBUY'%3A%20CBUY_T%2C%20'REQ'%3A%20REQ_T%2C%20'CPROD'%3A%20CPROD_T%2C%20'CAP'%3A%20CAP_T%7D)%0A%20%20%20%20%20%20%20%20if%20model_2.prob_status.is_solution_available%3A%0A%20%20%20%20%20%20%20%20%20%20%20%20obj_val_2%20%3D%20_output%5B'obj_val'%5D%0A%20%20%20%20%20%20%20%20%20%20%20%20sol_produce_1%20%3D%20_output%5B'sol_produce'%5D%0A%20%20%20%20%20%20%20%20%20%20%20%20sol_buy%20%3D%20_output%5B'sol_buy'%5D%0A%20%20%20%20%20%20%20%20%20%20%20%20print(f'Total%20cost%3A%20%7Bobj_val_2%3A%2C.2f%7D')%0A%20%20%20%20%20%20%20%20%20%20%20%20print()%0A%20%20%20%20%20%20%20%20%20%20%20%20print('Produce%20(assembled%20items)%3A')%0A%20%20%20%20%20%20%20%20%20%20%20%20for%20item%2C%20_qty%20in%20sorted(sol_produce_1.items())%3A%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20print(f'%20%20%7Bitem%3A%3C25%7D%3A%20%7B_qty%3A%3E6.0f%7D')%0A%20%20%20%20%20%20%20%20%20%20%20%20print()%0A%20%20%20%20%20%20%20%20%20%20%20%20print('Buy%20(purchased%20items)%3A')%0A%20%20%20%20%20%20%20%20%20%20%20%20for%20item%2C%20_qty%20in%20sorted(sol_buy.items())%3A%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20if%20_qty%20%3E%200.5%3A%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20print(f'%20%20%7Bitem%3A%3C25%7D%3A%20%7B_qty%3A%3E6.0f%7D')%0A%20%20%20%20%20%20%20%20else%3A%0A%20%20%20%20%20%20%20%20%20%20%20%20print(f'No%20solution%20found%20(%7Bmodel_2.prob_status%7D)')%0A%20%20%20%20return%20obj_val_2%2C%20sol_buy%2C%20sol_produce_1%0A%0A%0A%40app.cell(hide_code%3DTrue)%0Adef%20_(mo)%3A%0A%20%20%20%20mo.md(r%22%22%22%0A%20%20%20%20Bar%20chart%20showing%20production%20and%20purchase%20quantities%20by%20item%2C%20colored%20by%20type%20(assembled%20vs%20bought).%20Items%20with%20zero%20quantity%20are%20omitted.%0A%20%20%20%20%22%22%22)%0A%20%20%20%20return%0A%0A%0A%40app.cell%0Adef%20_(mo%2C%20mpatches%2C%20obj_val_2%2C%20plt%2C%20sol_buy%2C%20sol_produce_1)%3A%0A%20%20%20%20assembled_items%20%3D%20%7Bk%3A%20v%20for%20k%2C%20v%20in%20sol_produce_1.items()%20if%20v%20%3E%200.5%7D%0A%20%20%20%20bought_items%20%3D%20%7Bk%3A%20v%20for%20k%2C%20v%20in%20sol_buy.items()%20if%20v%20%3E%200.5%7D%0A%20%20%20%20all_items%20%3D%20list(assembled_items.keys())%20%2B%20list(bought_items.keys())%0A%20%20%20%20all_qtys%20%3D%20list(assembled_items.values())%20%2B%20list(bought_items.values())%0A%20%20%20%20colors%20%3D%20%5B'%231565C0'%5D%20*%20len(assembled_items)%20%2B%20%5B'%23FB8C00'%5D%20*%20len(bought_items)%0A%20%20%20%20_fig%2C%20_ax%20%3D%20plt.subplots(figsize%3D(12%2C%205))%0A%20%20%20%20bars%20%3D%20_ax.barh(all_items%2C%20all_qtys%2C%20color%3Dcolors%2C%20edgecolor%3D'white')%0A%20%20%20%20for%20bar%2C%20_qty%20in%20zip(bars%2C%20all_qtys)%3A%0A%20%20%20%20%20%20%20%20_ax.text(bar.get_width()%20%2B%2020%2C%20bar.get_y()%20%2B%20bar.get_height()%20%2F%202%2C%20f'%7B_qty%3A%2C.0f%7D'%2C%20va%3D'center'%2C%20fontsize%3D8)%0A%20%20%20%20blue_patch%20%3D%20mpatches.Patch(color%3D'%231565C0'%2C%20label%3D'Assembled%20in-house%20(ASMBL)')%0A%20%20%20%20orange_patch%20%3D%20mpatches.Patch(color%3D'%23FB8C00'%2C%20label%3D'Purchased%20(PREPROD)')%0A%20%20%20%20_ax.legend(handles%3D%5Bblue_patch%2C%20orange_patch%5D%2C%20loc%3D'lower%20right')%0A%20%20%20%20_ax.set_xlabel('Quantity')%0A%20%20%20%20_ax.set_title(f'C-3%20Toy%20lorry%20production%20plan%20-%20total%20cost%20%7Bobj_val_2%3A%2C.2f%7D')%0A%20%20%20%20plt.tight_layout()%0A%20%20%20%20mo.output.append(plt.gcf())%0A%20%20%20%20return%0A%0A%0A%40app.cell(hide_code%3DTrue)%0Adef%20_(mo)%3A%0A%20%20%20%20mo.md(r%22%22%22%0A%20%20%20%20%23%23%20C-4%20Electronic%20components%20(LP)%0A%20%20%20%20%22%22%22)%0A%20%20%20%20return%0A%0A%0A%40app.cell(hide_code%3DTrue)%0Adef%20_(mo)%3A%0A%20%20%20%20mo.md(r%22%22%22%0A%20%20%20%20*Applications%20Book%20Chapter%20C%2C%20Example%204*%0A%0A%20%20%20%20%3E%20**Mosel%20model%3A**%20%5B%60c4compo_p.mos%60%5D(https%3A%2F%2Fgithub.com%2Ffico-xpress%2Fpython-notebooks%2Fblob%2Fmain%2Fmarimo%2Fpublic%2Fmoselpy%2Fmodels%2Fproduction_planning%2Fc4compo_p.mos)%0A%0A%20%20%20%20A%20company%20produces%204%20electronic%20components%20(X43-M1%2C%20X43-M2%2C%20Y54-N1%2C%20Y54-N2)%20over%206%20months.%20The%20objective%20includes%20production%20costs%2C%20storage%20costs%2C%20and%20a%20penalty%20for%20changing%20the%20total%20production%20level%20between%20months%20(%60CADD%60%20for%20increases%2C%20%60CRED%60%20for%20decreases).%20Meet%20monthly%20demands%20while%20guaranteeing%20minimum%20final%20stocks.%0A%0A%20%20%20%20%60c4compo_p.mos%60%20exposes%20%60CADD%60%20and%20%60CRED%60%20as%20%60parameters%60%20so%20they%20can%20be%20overridden%20via%20%60exec_params%60%20from%20Python.%20This%20enables%20sensitivity%20analysis%20without%20recompiling.%0A%0A%20%20%20%20**Sets%3A**%20%24%5Ctext%7BPRODS%7D%20%3D%201..4%24%2C%20%24%5Ctext%7BMONTHS%7D%20%3D%201..6%24%0A%0A%20%20%20%20**Decision%20variables%3A**%20%24%5Cmathit%7Bproduce%7D_%7Bp%2Ct%7D%20%5Cgeq%200%24%3B%20%24%5Cmathit%7Bstore%7D_%7Bp%2Ct%7D%20%5Cgeq%200%24%3B%20%24%5Cmathit%7Badd%7D_t%20%5Cgeq%200%24%3B%20%24%5Cmathit%7Breduce%7D_t%20%5Cgeq%200%24.%0A%0A%20%20%20%20%24%24%0A%20%20%20%20%5Cbegin%7Balign*%7D%0A%20%20%20%20%5Ctext%7Bminimize%7D%20%20%20%5Cquad%20%26%20%5Csum_%7Bp%2Ct%7D%20(%5Ctext%7BCPROD%7D_p%20%5Ccdot%20%5Cmathit%7Bproduce%7D_%7Bp%2Ct%7D%20%2B%20%5Ctext%7BCSTOCK%7D_p%20%5Ccdot%20%5Cmathit%7Bstore%7D_%7Bp%2Ct%7D)%20%2B%20%5Csum_%7Bt%3D2%7D%5E%7B6%7D%20(%5Ctext%7BCADD%7D%20%5Ccdot%20%5Cmathit%7Badd%7D_t%20%2B%20%5Ctext%7BCRED%7D%20%5Ccdot%20%5Cmathit%7Breduce%7D_t)%20%5C%5C%0A%20%20%20%20%5Ctext%7Bsubject%20to%7D%20%5Cquad%20%26%20%5Cmathit%7Bstore%7D_%7Bp%2Ct%7D%20%3D%20%5Cmathit%7Bstore%7D_%7Bp%2Ct-1%7D%20%2B%20%5Cmathit%7Bproduce%7D_%7Bp%2Ct%7D%20-%20%5Ctext%7BDEM%7D_%7Bp%2Ct%7D%20%5Cquad%20%5Cforall%5C%2C%20p%2Ct%20%5C%5C%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%26%20%5Csum_p%20(%5Cmathit%7Bproduce%7D_%7Bp%2Ct%7D%20-%20%5Cmathit%7Bproduce%7D_%7Bp%2Ct-1%7D)%20%3D%20%5Cmathit%7Badd%7D_t%20-%20%5Cmathit%7Breduce%7D_t%20%5Cquad%20%5Cforall%5C%2C%20t%20%5Cgeq%202%20%5C%5C%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%26%20%5Cmathit%7Bstore%7D_%7Bp%2C6%7D%20%5Cgeq%20%5Ctext%7BFSTOCK%7D_p%20%5Cquad%20%5Cforall%5C%2C%20p%0A%20%20%20%20%5Cend%7Balign*%7D%0A%20%20%20%20%24%24%0A%20%20%20%20%22%22%22)%0A%20%20%20%20return%0A%0A%0A%40app.cell%0Adef%20_()%3A%0A%20%20%20%20CPROD_C4%20%20%3D%20%7B1%3A20%2C%202%3A25%2C%203%3A10%2C%204%3A15%7D%0A%20%20%20%20CSTOCK_C4%20%3D%20%7B1%3A0.4%2C%202%3A0.5%2C%203%3A0.3%2C%204%3A0.3%7D%0A%20%20%20%20DEM_C4%20%20%20%20%3D%20%7B(1%2C1)%3A1500%2C(1%2C2)%3A3000%2C(1%2C3)%3A2000%2C(1%2C4)%3A4000%2C(1%2C5)%3A2000%2C(1%2C6)%3A2500%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20(2%2C1)%3A1300%2C(2%2C2)%3A%20800%2C(2%2C3)%3A%20800%2C(2%2C4)%3A1000%2C(2%2C5)%3A1100%2C(2%2C6)%3A%20900%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20(3%2C1)%3A2200%2C(3%2C2)%3A1500%2C(3%2C3)%3A2900%2C(3%2C4)%3A1800%2C(3%2C5)%3A1200%2C(3%2C6)%3A2100%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20(4%2C1)%3A1400%2C(4%2C2)%3A1600%2C(4%2C3)%3A1500%2C(4%2C4)%3A1000%2C(4%2C5)%3A1100%2C(4%2C6)%3A1200%7D%0A%20%20%20%20ISTOCK_C4%20%3D%20%7B1%3A10%2C%202%3A0%2C%203%3A50%2C%204%3A0%7D%0A%20%20%20%20FSTOCK_C4%20%3D%20%7B1%3A50%2C%202%3A10%2C%203%3A30%2C%204%3A10%7D%0A%20%20%20%20PNAME_C4%20%20%3D%20%7B1%3A%22X43-M1%22%2C%202%3A%22X43-M2%22%2C%203%3A%22Y54-N1%22%2C%204%3A%22Y54-N2%22%7D%0A%20%20%20%20return%20CPROD_C4%2C%20CSTOCK_C4%2C%20DEM_C4%2C%20FSTOCK_C4%2C%20ISTOCK_C4%2C%20PNAME_C4%0A%0A%0A%40app.cell(hide_code%3DTrue)%0Adef%20_(mo)%3A%0A%20%20%20%20mo.md(r%22%22%22%0A%20%20%20%20Compile%20and%20load%20%60c4compo_p.mos%60%20once.%20Run%20the%20base%20case%20(CADD%3D1.0%2C%20CRED%3D0.5%20-%20the%20model%20parameter%20defaults)%20and%20display%20results.%0A%20%20%20%20%22%22%22)%0A%20%20%20%20return%0A%0A%0A%40app.cell%0Adef%20_(%0A%20%20%20%20CPROD_C4%2C%0A%20%20%20%20CSTOCK_C4%2C%0A%20%20%20%20DEM_C4%2C%0A%20%20%20%20FSTOCK_C4%2C%0A%20%20%20%20ISTOCK_C4%2C%0A%20%20%20%20MODELS%2C%0A%20%20%20%20PNAME_C4%2C%0A%20%20%20%20mp%2C%0A%20%20%20%20pd%2C%0A)%3A%0A%20%20%20%20mp.compile_model(f'%7BMODELS%7D%2Fc4compo_p.mos'%2C%20f'%7BMODELS%7D%2Fc4compo_p.bim')%0A%20%20%20%20model_c4%20%3D%20mp.load_model(f'%7BMODELS%7D%2Fc4compo_p.bim')%0A%20%20%20%20model_c4.set_default_stream(mp.StreamType.OUTPUT%2C%20'null%3A')%0A%20%20%20%20output_c4%20%3D%20model_c4.run(exec_params%3D%7B'DATASOURCE'%3A%20'moselpy%3A'%2C%20'OUTFILE'%3A%20'moselpy%3A'%7D%2C%20input_data%3D%7B'DEM'%3A%20DEM_C4%2C%20'CPROD'%3A%20CPROD_C4%2C%20'CSTOCK'%3A%20CSTOCK_C4%2C%20'ISTOCK'%3A%20ISTOCK_C4%2C%20'FSTOCK'%3A%20FSTOCK_C4%2C%20'PNAME'%3A%20PNAME_C4%7D)%0A%20%20%20%20if%20model_c4.prob_status.is_solution_available%3A%0A%20%20%20%20%20%20%20%20obj_val_c4%20%3D%20output_c4%5B'obj_val'%5D%0A%20%20%20%20%20%20%20%20sol_produce_2%20%3D%20output_c4%5B'sol_produce'%5D%0A%20%20%20%20%20%20%20%20sol_store_2%20%3D%20output_c4%5B'sol_store'%5D%0A%20%20%20%20%20%20%20%20pname_list%20%3D%20list(PNAME_C4.values())%0A%20%20%20%20%20%20%20%20_NP%2C%20NM%20%3D%20(4%2C%206)%0A%20%20%20%20%20%20%20%20_produce_df%20%3D%20pd.DataFrame(%7Bt%3A%20%5Bsol_produce_2%5Bp%2C%20t%5D%20for%20p%20in%20range(1%2C%20_NP%20%2B%201)%5D%20for%20t%20in%20range(1%2C%20NM%20%2B%201)%7D%2C%20index%3Dpname_list)%0A%20%20%20%20%20%20%20%20_produce_df.columns%20%3D%20%5Bf'M%7Bt%7D'%20for%20t%20in%20range(1%2C%20NM%20%2B%201)%5D%0A%20%20%20%20%20%20%20%20print(f'Total%20cost%20(CADD%3D1.0%2C%20CRED%3D0.5)%3A%20%7Bobj_val_c4%3A%2C.2f%7D')%0A%20%20%20%20%20%20%20%20print()%0A%20%20%20%20%20%20%20%20print('Production%20plan%3A')%0A%20%20%20%20%20%20%20%20print(_produce_df.round(0).astype(int).to_string())%0A%20%20%20%20else%3A%0A%20%20%20%20%20%20%20%20print(f'No%20solution%20found%20(%7Bmodel_c4.prob_status%7D)')%0A%20%20%20%20return%20(model_c4%2C)%0A%0A%0A%40app.cell(hide_code%3DTrue)%0Adef%20_(mo)%3A%0A%20%20%20%20mo.md(r%22%22%22%0A%20%20%20%20With%20the%20model%20already%20compiled%20and%20loaded%2C%20run%20a%20sensitivity%20analysis%20over%20%60CADD%60%20in%20%5B0.5%2C%201.0%2C%201.5%2C%202.0%5D%20and%20%60CRED%60%20in%20%5B0.25%2C%200.5%2C%200.75%2C%201.0%5D.%20For%20each%20combination%2C%20override%20both%20parameters%20via%20%60exec_params%60%20while%20passing%20the%20data%20via%20%60input_data%60.%20Collect%20the%20total%20cost%20into%20a%20%60pd.DataFrame%60%20pivot%20for%20visualization.%0A%20%20%20%20%22%22%22)%0A%20%20%20%20return%0A%0A%0A%40app.cell%0Adef%20_(%0A%20%20%20%20CPROD_C4%2C%0A%20%20%20%20CSTOCK_C4%2C%0A%20%20%20%20DEM_C4%2C%0A%20%20%20%20FSTOCK_C4%2C%0A%20%20%20%20ISTOCK_C4%2C%0A%20%20%20%20PNAME_C4%2C%0A%20%20%20%20model_c4%2C%0A%20%20%20%20pd%2C%0A)%3A%0A%20%20%20%20cadd_values%20%3D%20%5B0.5%2C%201.0%2C%201.5%2C%202.0%5D%0A%20%20%20%20cred_values%20%3D%20%5B0.25%2C%200.5%2C%200.75%2C%201.0%5D%0A%20%20%20%20results%20%3D%20%7B%7D%0A%20%20%20%20for%20_cadd%20in%20cadd_values%3A%0A%20%20%20%20%20%20%20%20for%20_cred%20in%20cred_values%3A%0A%20%20%20%20%20%20%20%20%20%20%20%20_out%20%3D%20model_c4.run(exec_params%3D%7B'DATASOURCE'%3A%20'moselpy%3A'%2C%20'OUTFILE'%3A%20'moselpy%3A'%2C%20'CADD'%3A%20_cadd%2C%20'CRED'%3A%20_cred%7D%2C%20input_data%3D%7B'DEM'%3A%20DEM_C4%2C%20'CPROD'%3A%20CPROD_C4%2C%20'CSTOCK'%3A%20CSTOCK_C4%2C%20'ISTOCK'%3A%20ISTOCK_C4%2C%20'FSTOCK'%3A%20FSTOCK_C4%2C%20'PNAME'%3A%20PNAME_C4%7D)%0A%20%20%20%20%20%20%20%20%20%20%20%20results%5B_cadd%2C%20_cred%5D%20%3D%20_out%5B'obj_val'%5D%20if%20model_c4.prob_status.is_solution_available%20else%20float('nan')%0A%20%20%20%20sens_df%20%3D%20pd.DataFrame(%5B%5Bresults%5Bcadd%2C%20cred%5D%20for%20cred%20in%20cred_values%5D%20for%20cadd%20in%20cadd_values%5D%2C%20index%3D%5Bf'CADD%3D%7Bv%7D'%20for%20v%20in%20cadd_values%5D%2C%20columns%3D%5Bf'CRED%3D%7Bv%7D'%20for%20v%20in%20cred_values%5D)%0A%20%20%20%20print('Total%20cost%20sensitivity%20analysis%20(CADD%20x%20CRED)%3A')%0A%20%20%20%20print(sens_df.round(2).to_string())%0A%20%20%20%20return%20cadd_values%2C%20cred_values%2C%20results%2C%20sens_df%0A%0A%0A%40app.cell(hide_code%3DTrue)%0Adef%20_(mo)%3A%0A%20%20%20%20mo.md(r%22%22%22%0A%20%20%20%20Heatmap%20of%20total%20cost%20across%20the%20(CADD%2C%20CRED)%20grid.%20Higher%20change-of-production%20costs%20lead%20to%20a%20smoother%20(more%20stable)%20production%20plan%20at%20higher%20total%20cost.%0A%20%20%20%20%22%22%22)%0A%20%20%20%20return%0A%0A%0A%40app.cell%0Adef%20_(cadd_values%2C%20cred_values%2C%20mo%2C%20plt%2C%20results%2C%20sens_df)%3A%0A%20%20%20%20_fig%2C%20_ax%20%3D%20plt.subplots(figsize%3D(7%2C%205))%0A%20%20%20%20_im%20%3D%20_ax.imshow(sens_df.values%2C%20cmap%3D'YlOrRd'%2C%20aspect%3D'auto')%0A%20%20%20%20_fig.colorbar(_im%2C%20ax%3D_ax%2C%20label%3D'Total%20cost')%0A%20%20%20%20_ax.set_xticks(range(len(cred_values)))%0A%20%20%20%20_ax.set_xticklabels(%5Bf'%7Bv%7D'%20for%20v%20in%20cred_values%5D)%0A%20%20%20%20_ax.set_yticks(range(len(cadd_values)))%0A%20%20%20%20_ax.set_yticklabels(%5Bf'%7Bv%7D'%20for%20v%20in%20cadd_values%5D)%0A%20%20%20%20_ax.set_xlabel('CRED%20(cost%20of%20production%20reduction)')%0A%20%20%20%20_ax.set_ylabel('CADD%20(cost%20of%20production%20increase)')%0A%20%20%20%20_ax.set_title('C-4%20Electronic%20components%20-%20total%20cost%20sensitivity')%0A%20%20%20%20for%20_i%2C%20_cadd%20in%20enumerate(cadd_values)%3A%0A%20%20%20%20%20%20%20%20for%20j%2C%20_cred%20in%20enumerate(cred_values)%3A%0A%20%20%20%20%20%20%20%20%20%20%20%20_val%20%3D%20results%5B_cadd%2C%20_cred%5D%0A%20%20%20%20%20%20%20%20%20%20%20%20_ax.text(j%2C%20_i%2C%20f'%7B_val%3A%2C.0f%7D'%2C%20ha%3D'center'%2C%20va%3D'center'%2C%20fontsize%3D8%2C%20color%3D'black'%20if%20_val%20%3C%20sens_df.values.max()%20*%200.75%20else%20'white')%0A%20%20%20%20plt.tight_layout()%0A%20%20%20%20mo.output.append(plt.gcf())%0A%20%20%20%20return%0A%0A%0A%40app.cell(hide_code%3DTrue)%0Adef%20_(mo)%3A%0A%20%20%20%20mo.md(r%22%22%22%0A%20%20%20%20%23%23%20C-5%20Fiberglass%20(LP)%0A%20%20%20%20%22%22%22)%0A%20%20%20%20return%0A%0A%0A%40app.cell(hide_code%3DTrue)%0Adef%20_(mo)%3A%0A%20%20%20%20mo.md(r%22%22%22%0A%20%20%20%20*Applications%20Book%20Chapter%20C%2C%20Example%205*%0A%0A%20%20%20%20%3E%20**Mosel%20model%3A**%20%5B%60c5fiber_p.mos%60%5D(https%3A%2F%2Fgithub.com%2Ffico-xpress%2Fpython-notebooks%2Fblob%2Fmain%2Fmarimo%2Fpublic%2Fmoselpy%2Fmodels%2Fproduction_planning%2Fc5fiber_p.mos)%0A%0A%20%20%20%20Six%20weeks%20of%20fiberglass%20production%20and%20storage%20must%20be%20planned.%20The%20problem%20is%20modelled%20as%20a%20**transshipment%20network**%3A%2012%20nodes%20where%20odd-numbered%20nodes%20are%20production%20nodes%20(supply)%20and%20even-numbered%20nodes%20are%20demand%20nodes.%20Arcs%20represent%20production%20(odd-to-even%2C%20cost%20%3D%20production%20cost)%20and%20storage%20(even-to-even%2B2%2C%20cost%20%3D%20storage%20cost).%0A%0A%20%20%20%20%60c5fiber_p.mos%60%20accepts%20%60DATASOURCE%60%20as%20a%20path%20to%20either%20a%20text%20%60.dat%60%20file%20or%20a%20Mosel%20binary%20%60.bin%60%20file.%20This%20section%20demonstrates%20%60mp.write_binary()%60%20to%20write%20data%20as%20a%20binary%20blob%2C%20then%20pass%20%60DATASOURCE%3D%22bin%3A...%22%60%20to%20the%20model.%0A%0A%20%20%20%20**Sets%3A**%20%24%5Ctext%7BNODES%7D%20%3D%201..12%24%20(odd%20%3D%20production%2C%20even%20%3D%20demand)%0A%0A%20%20%20%20**Decision%20variables%3A**%20%24%5Cmathit%7Bflow%7D_%7Bm%2Cn%7D%20%5Cgeq%200%24%20for%20each%20arc%20%24(m%2Cn)%24%20in%20%24%5Ctext%7BARC%7D%24%20(dynamic%20-%20only%20created%20where%20arc%20exists).%0A%0A%20%20%20%20%24%24%0A%20%20%20%20%5Cbegin%7Balign*%7D%0A%20%20%20%20%5Ctext%7Bminimize%7D%20%20%20%5Cquad%20%26%20%5Csum_%7B(m%2Cn)%20%5Cin%20%5Ctext%7BARC%7D%7D%20%5Ctext%7BARC%7D_%7Bm%2Cn%7D%20%5Ccdot%20%5Cmathit%7Bflow%7D_%7Bm%2Cn%7D%20%5C%5C%0A%20%20%20%20%5Ctext%7Bsubject%20to%7D%20%5Cquad%20%26%20%5Cmathit%7Bflow%7D_%7Bn-2%2Cn%7D%20%2B%20%5Cmathit%7Bflow%7D_%7Bn-1%2Cn%7D%20%3D%20%5Cmathit%7Bflow%7D_%7Bn%2Cn%2B2%7D%20%2B%20%5Ctext%7BWEIGHT%7D_n%20%5Cquad%20%5Cforall%5C%2C%20n%20%5Ctext%7B%20even%7D%20%5C%5C%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%26%20%5Cmathit%7Bflow%7D_%7Bn%2Cn%2B1%7D%20%5Cleq%20%5Ctext%7BWEIGHT%7D_n%20%5Cquad%20%5Cforall%5C%2C%20n%20%5Ctext%7B%20odd%7D%0A%20%20%20%20%5Cend%7Balign*%7D%0A%20%20%20%20%24%24%0A%20%20%20%20%22%22%22)%0A%20%20%20%20return%0A%0A%0A%40app.cell(hide_code%3DTrue)%0Adef%20_(mo)%3A%0A%20%20%20%20mo.md(r%22%22%22%0A%20%20%20%20First%2C%20define%20the%20arc%20and%20weight%20data%20as%20Python%20dicts.%20Write%20a%20Mosel%20binary%20file%20with%20%60mp.write_binary()%60%2C%20then%20compile%20and%20run%20the%20model%20with%20%60DATASOURCE%60%20pointing%20to%20the%20binary%20file%20via%20the%20%60%22bin%3A%22%60%20prefix.%20The%20model%20reads%20the%20data%20identically%20-%20the%20only%20difference%20from%20the%20text%20%60.dat%60%20is%20the%20file%20format.%0A%20%20%20%20%22%22%22)%0A%20%20%20%20return%0A%0A%0A%40app.cell%0Adef%20_(DATA%2C%20MODELS%2C%20mp)%3A%0A%20%20%20%20arc_data%20%3D%20%7B(1%2C%202)%3A%205%2C%20(3%2C%204)%3A%208%2C%20(5%2C%206)%3A%206%2C%20(7%2C%208)%3A%206%2C%20(9%2C%2010)%3A%207%2C%20(11%2C%2012)%3A%206%2C%20(2%2C%204)%3A%200.2%2C%20(4%2C%206)%3A%200.3%2C%20(6%2C%208)%3A%200.2%2C%20(8%2C%2010)%3A%200.25%2C%20(10%2C%2012)%3A%200.3%7D%0A%20%20%20%20weight_data%20%3D%20%7Bn%3A%20v%20for%20n%2C%20v%20in%20enumerate(%5B140%2C%20100%2C%20100%2C%20120%2C%20110%2C%20100%2C%20100%2C%2090%2C%20120%2C%20120%2C%20100%2C%20110%5D%2C%20start%3D1)%7D%0A%20%20%20%20bin_path%20%3D%20f'%7BDATA%7D%2Fc5fiber_p.bin'%0A%20%20%20%20mp.write_binary(bin_path%2C%20%7B'ARC'%3A%20arc_data%2C%20'WEIGHT'%3A%20weight_data%7D)%0A%20%20%20%20print(f'Binary%20file%20written%3A%20%7Bbin_path%7D')%0A%20%20%20%20mp.compile_model(f'%7BMODELS%7D%2Fc5fiber_p.mos'%2C%20f'%7BMODELS%7D%2Fc5fiber_p.bim')%0A%20%20%20%20with%20mp.load_model(f'%7BMODELS%7D%2Fc5fiber_p.bim')%20as%20model_3%3A%0A%20%20%20%20%20%20%20%20_output%20%3D%20model_3.run(exec_params%3D%7B'DATASOURCE'%3A%20f'bin%3A%7Bbin_path%7D'%2C%20'OUTFILE'%3A%20'moselpy%3A'%7D)%0A%20%20%20%20%20%20%20%20if%20model_3.prob_status.is_solution_available%3A%0A%20%20%20%20%20%20%20%20%20%20%20%20obj_val_3%20%3D%20_output%5B'obj_val'%5D%0A%20%20%20%20%20%20%20%20%20%20%20%20sol_flow%20%3D%20_output%5B'sol_flow'%5D%0A%20%20%20%20%20%20%20%20%20%20%20%20print()%0A%20%20%20%20%20%20%20%20%20%20%20%20print(f'Total%20cost%3A%20%7Bobj_val_3%3A%2C.2f%7D')%0A%20%20%20%20%20%20%20%20%20%20%20%20print()%0A%20%20%20%20%20%20%20%20%20%20%20%20print('Production%20and%20storage%20flows%3A')%0A%20%20%20%20%20%20%20%20%20%20%20%20for%20_week%20in%20range(1%2C%207)%3A%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20prod_node%20%3D%202%20*%20_week%20-%201%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20demand_node%20%3D%202%20*%20_week%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20production%20%3D%20sol_flow.get((prod_node%2C%20demand_node)%2C%200)%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20storage_out%20%3D%20sol_flow.get((demand_node%2C%20demand_node%20%2B%202)%2C%200)%20if%20demand_node%20%3C%2012%20else%200%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20storage_in%20%3D%20sol_flow.get((demand_node%20-%202%2C%20demand_node)%2C%200)%20if%20demand_node%20%3E%202%20else%200%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20print(f'%20%20Week%20%7B_week%7D%3A%20produced%3D%7Bproduction%3A.0f%7D%20%20stored_in%3D%7Bstorage_in%3A.0f%7D%20%20stored_out%3D%7Bstorage_out%3A.0f%7D')%0A%20%20%20%20%20%20%20%20else%3A%0A%20%20%20%20%20%20%20%20%20%20%20%20print(f'No%20solution%20found%20(%7Bmodel_3.prob_status%7D)')%0A%20%20%20%20return%20obj_val_3%2C%20sol_flow%0A%0A%0A%40app.cell(hide_code%3DTrue)%0Adef%20_(mo)%3A%0A%20%20%20%20mo.md(r%22%22%22%0A%20%20%20%20Network%20flow%20diagram%3A%2012%20nodes%20arranged%20in%20two%20rows%20(production%20nodes%20on%20top%2C%20demand%20nodes%20below).%20Arc%20width%20is%20proportional%20to%20flow%3B%20arc%20labels%20show%20cost%20and%20flow.%0A%20%20%20%20%22%22%22)%0A%20%20%20%20return%0A%0A%0A%40app.cell%0Adef%20_(mo%2C%20mpatches%2C%20obj_val_3%2C%20plt%2C%20sol_flow)%3A%0A%20%20%20%20WEEKS%20%3D%206%0A%20%20%20%20arc_costs%20%3D%20%7B(1%2C%202)%3A%205%2C%20(3%2C%204)%3A%208%2C%20(5%2C%206)%3A%206%2C%20(7%2C%208)%3A%206%2C%20(9%2C%2010)%3A%207%2C%20(11%2C%2012)%3A%206%2C%20(2%2C%204)%3A%200.2%2C%20(4%2C%206)%3A%200.3%2C%20(6%2C%208)%3A%200.2%2C%20(8%2C%2010)%3A%200.25%2C%20(10%2C%2012)%3A%200.3%7D%0A%20%20%20%20demands%20%3D%20%5B100%2C%20120%2C%20110%2C%20100%2C%2090%2C%20110%5D%0A%20%20%20%20cap_prod%20%3D%20%5B140%2C%20100%2C%20100%2C%20100%2C%20120%2C%20100%5D%0A%20%20%20%20_fig%2C%20_ax%20%3D%20plt.subplots(figsize%3D(14%2C%205))%0A%20%20%20%20node_x%20%3D%20%7Bn%3A%20(n%20-%201)%20%2F%2F%202%20*%202.0%20for%20n%20in%20range(1%2C%2013)%7D%0A%20%20%20%20node_y%20%3D%20%7Bn%3A%201.0%20if%20n%20%25%202%20%3D%3D%201%20else%200.0%20for%20n%20in%20range(1%2C%2013)%7D%0A%20%20%20%20node_colors%20%3D%20%7Bn%3A%20'%231565C0'%20if%20n%20%25%202%20%3D%3D%201%20else%20'%23FB8C00'%20for%20n%20in%20range(1%2C%2013)%7D%0A%20%20%20%20for%20n%20in%20range(1%2C%2013)%3A%0A%20%20%20%20%20%20%20%20_week%20%3D%20(n%20%2B%201)%20%2F%2F%202%0A%20%20%20%20%20%20%20%20label_top%20%3D%20f'P%7B_week%7D%5Cn(cap%3D%7Bcap_prod%5B_week%20-%201%5D%7D)'%20if%20n%20%25%202%20%3D%3D%201%20else%20f'D%7B_week%7D%5Cn(dem%3D%7Bdemands%5B_week%20-%201%5D%7D)'%0A%20%20%20%20%20%20%20%20_ax.scatter(node_x%5Bn%5D%2C%20node_y%5Bn%5D%2C%20s%3D1200%2C%20color%3Dnode_colors%5Bn%5D%2C%20zorder%3D3)%0A%20%20%20%20%20%20%20%20_ax.text(node_x%5Bn%5D%2C%20node_y%5Bn%5D%2C%20str(n)%2C%20ha%3D'center'%2C%20va%3D'center'%2C%20fontsize%3D9%2C%20fontweight%3D'bold'%2C%20color%3D'white')%0A%20%20%20%20%20%20%20%20_ax.text(node_x%5Bn%5D%2C%20node_y%5Bn%5D%20%2B%20(0.22%20if%20n%20%25%202%20%3D%3D%201%20else%20-0.22)%2C%20label_top%2C%20ha%3D'center'%2C%20va%3D'center'%2C%20fontsize%3D6.5%2C%20color%3D'gray')%0A%20%20%20%20for%20(_m%2C%20n)%2C%20cost%20in%20arc_costs.items()%3A%0A%20%20%20%20%20%20%20%20flow%20%3D%20sol_flow.get((_m%2C%20n)%2C%200)%0A%20%20%20%20%20%20%20%20if%20flow%20%3C%200.01%3A%0A%20%20%20%20%20%20%20%20%20%20%20%20continue%0A%20%20%20%20%20%20%20%20lw%20%3D%20max(1.0%2C%20flow%20%2F%2030)%0A%20%20%20%20%20%20%20%20color%20%3D%20'%231E88E5'%20if%20abs(node_y%5B_m%5D%20-%20node_y%5Bn%5D)%20%3E%200.1%20else%20'%2343A047'%0A%20%20%20%20%20%20%20%20_ax.annotate(''%2C%20xy%3D(node_x%5Bn%5D%2C%20node_y%5Bn%5D)%2C%20xytext%3D(node_x%5B_m%5D%2C%20node_y%5B_m%5D)%2C%20arrowprops%3Ddict(arrowstyle%3D'-%3E'%2C%20color%3Dcolor%2C%20lw%3Dlw))%0A%20%20%20%20%20%20%20%20mid_x%20%3D%20(node_x%5B_m%5D%20%2B%20node_x%5Bn%5D)%20%2F%202%0A%20%20%20%20%20%20%20%20mid_y%20%3D%20(node_y%5B_m%5D%20%2B%20node_y%5Bn%5D)%20%2F%202%20%2B%20(0.08%20if%20node_y%5B_m%5D%20!%3D%20node_y%5Bn%5D%20else%200.06)%0A%20%20%20%20%20%20%20%20_ax.text(mid_x%2C%20mid_y%2C%20f'c%3D%7Bcost%7D%5Cnf%3D%7Bflow%3A.0f%7D'%2C%20ha%3D'center'%2C%20va%3D'bottom'%2C%20fontsize%3D6%2C%20color%3Dcolor)%0A%20%20%20%20_ax.set_xlim(-0.7%2C%2011.2)%0A%20%20%20%20_ax.set_ylim(-0.5%2C%201.6)%0A%20%20%20%20_ax.axis('off')%0A%20%20%20%20prod_patch%20%3D%20mpatches.Patch(color%3D'%231565C0'%2C%20label%3D'Production%20node%20(odd)')%0A%20%20%20%20dem_patch%20%3D%20mpatches.Patch(color%3D'%23FB8C00'%2C%20label%3D'Demand%20node%20(even)')%0A%20%20%20%20prod_arc%20%3D%20mpatches.Patch(color%3D'%231E88E5'%2C%20label%3D'Production%20arc')%0A%20%20%20%20store_arc%20%3D%20mpatches.Patch(color%3D'%2343A047'%2C%20label%3D'Storage%20arc')%0A%20%20%20%20_ax.legend(handles%3D%5Bprod_patch%2C%20dem_patch%2C%20prod_arc%2C%20store_arc%5D%2C%20loc%3D'lower%20right'%2C%20fontsize%3D8)%0A%20%20%20%20_ax.set_title(f'C-5%20Fiberglass%20network%20flow%20-%20total%20cost%20%7Bobj_val_3%3A%2C.2f%7D')%0A%20%20%20%20plt.tight_layout()%0A%20%20%20%20mo.output.append(plt.gcf())%0A%20%20%20%20return%0A%0A%0A%40app.cell(hide_code%3DTrue)%0Adef%20_(mo)%3A%0A%20%20%20%20mo.md(r%22%22%22%0A%20%20%20%20%23%23%20C-6%20Machine%20assignment%20(MIP)%0A%20%20%20%20%22%22%22)%0A%20%20%20%20return%0A%0A%0A%40app.cell(hide_code%3DTrue)%0Adef%20_(mo)%3A%0A%20%20%20%20mo.md(r%22%22%22%0A%20%20%20%20*Applications%20Book%20Chapter%20C%2C%20Example%206*%0A%0A%20%20%20%20%3E%20**Mosel%20model%3A**%20%5B%60c6assign_p.mos%60%5D(https%3A%2F%2Fgithub.com%2Ffico-xpress%2Fpython-notebooks%2Fblob%2Fmain%2Fmarimo%2Fpublic%2Fmoselpy%2Fmodels%2Fproduction_planning%2Fc6assign_p.mos)%0A%0A%20%20%20%20Ten%20production%20batches%20must%20each%20be%20assigned%20to%20exactly%20one%20of%20five%20machines.%20Each%20machine-batch%20pair%20has%20a%20known%20duration%20and%20cost.%20Each%20machine%20has%20a%20maximum%20total%20capacity%20(total%20duration%20it%20can%20handle).%20Find%20the%20assignment%20that%20minimizes%20total%20production%20cost.%0A%0A%20%20%20%20Binary%20variable%20%24%5Cmathit%7Buse%7D_%7Bm%2Cp%7D%20%3D%201%24%20if%20machine%20%24m%24%20is%20assigned%20to%20batch%20%24p%24.%0A%0A%20%20%20%20**Sets%3A**%20%24%5Ctext%7BMACH%7D%20%3D%201..5%24%2C%20%24%5Ctext%7BPRODS%7D%20%3D%201..10%24%0A%0A%20%20%20%20**Decision%20variables%3A**%20%24%5Cmathit%7Buse%7D_%7Bm%2Cp%7D%20%5Cin%20%5C%7B0%2C1%5C%7D%24%0A%0A%20%20%20%20%24%24%0A%20%20%20%20%5Cbegin%7Balign*%7D%0A%20%20%20%20%5Ctext%7Bminimize%7D%20%20%20%5Cquad%20%26%20%5Csum_%7Bm%2Cp%7D%20%5Ctext%7BCOST%7D_%7Bm%2Cp%7D%20%5Ccdot%20%5Cmathit%7Buse%7D_%7Bm%2Cp%7D%20%5C%5C%0A%20%20%20%20%5Ctext%7Bsubject%20to%7D%20%5Cquad%20%26%20%5Csum_m%20%5Cmathit%7Buse%7D_%7Bm%2Cp%7D%20%3D%201%20%5Cquad%20%5Cforall%5C%2C%20p%20%5Cquad%20%5Ctext%7B(one%20machine%20per%20batch)%7D%20%5C%5C%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%26%20%5Csum_p%20%5Ctext%7BDUR%7D_%7Bm%2Cp%7D%20%5Ccdot%20%5Cmathit%7Buse%7D_%7Bm%2Cp%7D%20%5Cleq%20%5Ctext%7BCAP%7D_m%20%5Cquad%20%5Cforall%5C%2C%20m%20%5Cquad%20%5Ctext%7B(capacity%20limits)%7D%0A%20%20%20%20%5Cend%7Balign*%7D%0A%20%20%20%20%24%24%0A%20%20%20%20%22%22%22)%0A%20%20%20%20return%0A%0A%0A%40app.cell%0Adef%20_()%3A%0A%20%20%20%20NM_A%2C%20NB_A%20%3D%205%2C%2010%0A%20%20%20%20CAP_A%20%20%3D%20%7B1%3A18%2C%202%3A19%2C%203%3A25%2C%204%3A19%2C%205%3A20%7D%0A%20%20%20%20DUR_A%20%20%3D%20%7B(1%2C1)%3A8%2C(1%2C2)%3A15%2C(1%2C3)%3A14%2C(1%2C4)%3A23%2C(1%2C5)%3A8%2C(1%2C6)%3A16%2C(1%2C7)%3A8%2C(1%2C8)%3A25%2C(1%2C9)%3A9%2C(1%2C10)%3A17%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20(2%2C1)%3A15%2C(2%2C2)%3A7%2C(2%2C3)%3A23%2C(2%2C4)%3A22%2C(2%2C5)%3A11%2C(2%2C6)%3A11%2C(2%2C7)%3A12%2C(2%2C8)%3A10%2C(2%2C9)%3A17%2C(2%2C10)%3A16%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20(3%2C1)%3A21%2C(3%2C2)%3A20%2C(3%2C3)%3A6%2C(3%2C4)%3A22%2C(3%2C5)%3A24%2C(3%2C6)%3A10%2C(3%2C7)%3A24%2C(3%2C8)%3A9%2C(3%2C9)%3A21%2C(3%2C10)%3A14%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20(4%2C1)%3A20%2C(4%2C2)%3A11%2C(4%2C3)%3A8%2C(4%2C4)%3A14%2C(4%2C5)%3A9%2C(4%2C6)%3A5%2C(4%2C7)%3A6%2C(4%2C8)%3A19%2C(4%2C9)%3A19%2C(4%2C10)%3A7%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20(5%2C1)%3A8%2C(5%2C2)%3A13%2C(5%2C3)%3A13%2C(5%2C4)%3A13%2C(5%2C5)%3A10%2C(5%2C6)%3A20%2C(5%2C7)%3A25%2C(5%2C8)%3A16%2C(5%2C9)%3A16%2C(5%2C10)%3A17%7D%0A%20%20%20%20COST_A%20%3D%20%7B(1%2C1)%3A17%2C(1%2C2)%3A21%2C(1%2C3)%3A22%2C(1%2C4)%3A18%2C(1%2C5)%3A24%2C(1%2C6)%3A15%2C(1%2C7)%3A20%2C(1%2C8)%3A18%2C(1%2C9)%3A19%2C(1%2C10)%3A18%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20(2%2C1)%3A23%2C(2%2C2)%3A16%2C(2%2C3)%3A21%2C(2%2C4)%3A16%2C(2%2C5)%3A17%2C(2%2C6)%3A16%2C(2%2C7)%3A19%2C(2%2C8)%3A25%2C(2%2C9)%3A18%2C(2%2C10)%3A21%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20(3%2C1)%3A16%2C(3%2C2)%3A20%2C(3%2C3)%3A16%2C(3%2C4)%3A25%2C(3%2C5)%3A24%2C(3%2C6)%3A16%2C(3%2C7)%3A17%2C(3%2C8)%3A19%2C(3%2C9)%3A19%2C(3%2C10)%3A18%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20(4%2C1)%3A19%2C(4%2C2)%3A19%2C(4%2C3)%3A22%2C(4%2C4)%3A22%2C(4%2C5)%3A20%2C(4%2C6)%3A16%2C(4%2C7)%3A19%2C(4%2C8)%3A17%2C(4%2C9)%3A21%2C(4%2C10)%3A19%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20(5%2C1)%3A18%2C(5%2C2)%3A19%2C(5%2C3)%3A15%2C(5%2C4)%3A15%2C(5%2C5)%3A21%2C(5%2C6)%3A25%2C(5%2C7)%3A16%2C(5%2C8)%3A16%2C(5%2C9)%3A23%2C(5%2C10)%3A15%7D%0A%20%20%20%20return%20CAP_A%2C%20COST_A%2C%20DUR_A%2C%20NB_A%2C%20NM_A%0A%0A%0A%40app.cell(hide_code%3DTrue)%0Adef%20_(mo)%3A%0A%20%20%20%20mo.md(r%22%22%22%0A%20%20%20%20Compile%20and%20run%20%60c6assign_p.mos%60%20with%20the%20standard%20data.%20After%20the%20optimal%20solve%2C%20display%20the%20assignment%20and%20the%20duration%20utilization%20for%20each%20machine.%0A%20%20%20%20%22%22%22)%0A%20%20%20%20return%0A%0A%0A%40app.cell%0Adef%20_(CAP_A%2C%20COST_A%2C%20DUR_A%2C%20MODELS%2C%20NB_A%2C%20NM_A%2C%20mp)%3A%0A%20%20%20%20mp.compile_model(f'%7BMODELS%7D%2Fc6assign_p.mos'%2C%20f'%7BMODELS%7D%2Fc6assign_p.bim')%0A%20%20%20%20model_c6%20%3D%20mp.load_model(f'%7BMODELS%7D%2Fc6assign_p.bim')%0A%20%20%20%20model_c6.set_default_stream(mp.StreamType.OUTPUT%2C%20'null%3A')%0A%20%20%20%20output_c6%20%3D%20model_c6.run(exec_params%3D%7B'DATASOURCE'%3A%20'moselpy%3A'%2C%20'OUTFILE'%3A%20'moselpy%3A'%7D%2C%20input_data%3D%7B'CAP'%3A%20CAP_A%2C%20'DUR'%3A%20DUR_A%2C%20'COST'%3A%20COST_A%7D)%0A%20%20%20%20if%20model_c6.prob_status.is_solution_available%3A%0A%20%20%20%20%20%20%20%20obj_val_c6%20%3D%20output_c6%5B'obj_val'%5D%0A%20%20%20%20%20%20%20%20sol_use%20%3D%20output_c6%5B'sol_use'%5D%0A%20%20%20%20%20%20%20%20_cap_list%20%3D%20list(CAP_A.values())%0A%20%20%20%20%20%20%20%20_dur_rows%20%3D%20%5B%5BDUR_A%5Bm%2C%20p%5D%20for%20p%20in%20range(1%2C%20NB_A%20%2B%201)%5D%20for%20m%20in%20range(1%2C%20NM_A%20%2B%201)%5D%0A%20%20%20%20%20%20%20%20print(f'Total%20cost%3A%20%7Bobj_val_c6%3A.0f%7D')%0A%20%20%20%20%20%20%20%20print()%20%20%23%20dict%3A%20(machine%2C%20batch)%20-%3E%20real%0A%20%20%20%20%20%20%20%20print(f'%7B'Machine'%3A%3C10%7D%20%7B'Capacity'%3A%3E9%7D%20%7B'Used'%3A%3E6%7D%20%7B'Batches'%7D')%0A%20%20%20%20%20%20%20%20for%20_m%20in%20range(1%2C%20NM_A%20%2B%201)%3A%0A%20%20%20%20%20%20%20%20%20%20%20%20batches%20%3D%20%5Bp%20for%20p%20in%20range(1%2C%20NB_A%20%2B%201)%20if%20sol_use%5B_m%2C%20p%5D%20%3E%200.5%5D%0A%20%20%20%20%20%20%20%20%20%20%20%20_dur_used%20%3D%20sum((_dur_rows%5B_m%20-%201%5D%5Bp%20-%201%5D%20for%20p%20in%20batches))%0A%20%20%20%20%20%20%20%20%20%20%20%20print(f'%20%20Machine%20%7B_m%7D%20%20%20%7B_cap_list%5B_m%20-%201%5D%3A%3E7%7D%20%20%20%7B_dur_used%3A%3E4%7D%20%20%20%7Bbatches%7D')%0A%20%20%20%20else%3A%0A%20%20%20%20%20%20%20%20print(f'No%20solution%20found%20(%7Bmodel_c6.prob_status%7D)')%0A%20%20%20%20return%20model_c6%2C%20obj_val_c6%2C%20sol_use%0A%0A%0A%40app.cell(hide_code%3DTrue)%0Adef%20_(mo)%3A%0A%20%20%20%20mo.md(r%22%22%22%0A%20%20%20%20Demonstrating%20infeasibility%20detection%20via%20%60model.prob_status%60%3A%20reduce%20all%20machine%20capacities%20to%2010%20units.%20Batch%204%20requires%20a%20minimum%20of%2013%20time%20units%20on%20any%20machine%2C%20so%20it%20cannot%20be%20scheduled%20-%20the%20problem%20has%20no%20feasible%20solution.%20When%20the%20MIP%20is%20infeasible%2C%20Xpress%20stops%20without%20finding%20a%20solution%3A%20%60is_solution_available%60%20returns%20%60False%60%20and%20%60is_optimization_finished%60%20returns%20%60False%60.%20The%20%60prob_status%60%20check%20catches%20this%20cleanly.%0A%20%20%20%20%22%22%22)%0A%20%20%20%20return%0A%0A%0A%40app.cell%0Adef%20_(COST_A%2C%20DUR_A%2C%20NM_A%2C%20model_c6)%3A%0A%20%20%20%20CAP_TIGHT%20%3D%20%7Bm%3A%2010%20for%20m%20in%20range(1%2C%20NM_A%2B1)%7D%0A%0A%20%20%20%20out_tight%20%3D%20model_c6.run(%0A%20%20%20%20%20%20%20%20exec_params%3D%7B%22DATASOURCE%22%3A%20%22moselpy%3A%22%2C%20%22OUTFILE%22%3A%20%22moselpy%3A%22%7D%2C%0A%20%20%20%20%20%20%20%20input_data%3D%7B%22CAP%22%3A%20CAP_TIGHT%2C%20%22DUR%22%3A%20DUR_A%2C%20%22COST%22%3A%20COST_A%7D%2C%0A%20%20%20%20)%0A%20%20%20%20status%20%3D%20model_c6.prob_status%0A%0A%20%20%20%20print(f%22Status%20with%20tight%20capacities%20(CAP%3D%5B10%2C...%2C10%5D)%3A%22)%0A%20%20%20%20print(f%22%20%20is_solution_available%20%20%3A%20%7Bstatus.is_solution_available%7D%22)%0A%20%20%20%20print(f%22%20%20is_solution_optimal%20%20%20%20%3A%20%7Bstatus.is_solution_optimal%7D%22)%0A%20%20%20%20print(f%22%20%20is_optimization_finished%3A%20%7Bstatus.is_optimization_finished%7D%22)%0A%20%20%20%20print()%0A%20%20%20%20if%20not%20status.is_solution_available%3A%0A%20%20%20%20%20%20%20%20print(%22No%20feasible%20assignment%20exists%20with%20CAP%3D%5B10%2C...%2C10%5D.%22)%0A%20%20%20%20%20%20%20%20print(%22Batch%204%20requires%20at%20least%2013%20time%20units%20on%20any%20machine%2C%20exceeding%20the%20cap%20of%2010.%22)%0A%20%20%20%20else%3A%0A%20%20%20%20%20%20%20%20print(f%22Optimal%20cost%3A%20%7Bout_tight%5B'obj_val'%5D%3A.0f%7D%22)%0A%20%20%20%20return%0A%0A%0A%40app.cell(hide_code%3DTrue)%0Adef%20_(mo)%3A%0A%20%20%20%20mo.md(r%22%22%22%0A%20%20%20%20Heatmap%20of%20the%20assignment%20matrix%20(machines%20x%20batches).%20Green%20cells%20%3D%20assigned%20(use%3D1)%3B%20grey%20cells%20%3D%20not%20assigned%20(use%3D0).%20Machine%20capacity%20utilization%20shown%20on%20the%20right.%0A%20%20%20%20%22%22%22)%0A%20%20%20%20return%0A%0A%0A%40app.cell%0Adef%20_(CAP_A%2C%20DUR_A%2C%20NB_A%2C%20NM_A%2C%20mo%2C%20np%2C%20obj_val_c6%2C%20plt%2C%20sol_use)%3A%0A%20%20%20%20_cap_list%20%3D%20list(CAP_A.values())%0A%20%20%20%20_dur_rows%20%3D%20%5B%5BDUR_A%5Bm%2C%20p%5D%20for%20p%20in%20range(1%2C%20NB_A%20%2B%201)%5D%20for%20m%20in%20range(1%2C%20NM_A%20%2B%201)%5D%0A%20%20%20%20assign_matrix%20%3D%20np.array(%5B%5Bsol_use%5Bm%2C%20p%5D%20for%20p%20in%20range(1%2C%20NB_A%20%2B%201)%5D%20for%20m%20in%20range(1%2C%20NM_A%20%2B%201)%5D)%0A%20%20%20%20_fig%2C%20(_ax1%2C%20_ax2)%20%3D%20plt.subplots(1%2C%202%2C%20figsize%3D(13%2C%204)%2C%20gridspec_kw%3D%7B'width_ratios'%3A%20%5B3%2C%201%5D%7D)%0A%20%20%20%20_im%20%3D%20_ax1.imshow(assign_matrix%2C%20cmap%3D'RdYlGn'%2C%20vmin%3D0%2C%20vmax%3D1%2C%20aspect%3D'auto')%0A%20%20%20%20_ax1.set_xticks(range(NB_A))%0A%20%20%20%20_ax1.set_xticklabels(%5Bf'Batch%20%7Bp%7D'%20for%20p%20in%20range(1%2C%20NB_A%20%2B%201)%5D%2C%20rotation%3D45)%0A%20%20%20%20_ax1.set_yticks(range(NM_A))%0A%20%20%20%20_ax1.set_yticklabels(%5Bf'Machine%20%7Bm%7D'%20for%20m%20in%20range(1%2C%20NM_A%20%2B%201)%5D)%0A%20%20%20%20_ax1.set_title(f'C-6%20Machine%20assignment%20-%20total%20cost%20%7Bobj_val_c6%3A.0f%7D')%0A%20%20%20%20for%20_m%20in%20range(NM_A)%3A%0A%20%20%20%20%20%20%20%20for%20_p%20in%20range(NB_A)%3A%0A%20%20%20%20%20%20%20%20%20%20%20%20_val%20%3D%20assign_matrix%5B_m%2C%20_p%5D%0A%20%20%20%20%20%20%20%20%20%20%20%20_ax1.text(_p%2C%20_m%2C%20f'%7B_val%3A.0f%7D'%2C%20ha%3D'center'%2C%20va%3D'center'%2C%20fontsize%3D9%2C%20color%3D'black')%0A%20%20%20%20_dur_used%20%3D%20%5Bsum((_dur_rows%5Bm%20-%201%5D%5Bp%20-%201%5D%20for%20p%20in%20range(1%2C%20NB_A%20%2B%201)%20if%20sol_use%5Bm%2C%20p%5D%20%3E%200.5))%20for%20m%20in%20range(1%2C%20NM_A%20%2B%201)%5D%0A%20%20%20%20bar_colors%20%3D%20%5B'%2343A047'%20if%20du%20%3C%3D%20cap%20else%20'%23E53935'%20for%20du%2C%20cap%20in%20zip(_dur_used%2C%20_cap_list)%5D%0A%20%20%20%20_ax2.barh(range(NM_A)%2C%20_dur_used%2C%20color%3Dbar_colors%2C%20edgecolor%3D'white')%0A%20%20%20%20for%20_i%2C%20(du%2C%20_cap)%20in%20enumerate(zip(_dur_used%2C%20_cap_list))%3A%0A%20%20%20%20%23%20Capacity%20bar%0A%20%20%20%20%20%20%20%20_ax2.axhline(_i%2C%20color%3D'gray'%2C%20linewidth%3D0.3%2C%20alpha%3D0.5)%0A%20%20%20%20%20%20%20%20_ax2.text(du%20%2B%200.2%2C%20_i%2C%20f'%7Bdu%7D%2F%7B_cap%7D'%2C%20va%3D'center'%2C%20fontsize%3D8)%0A%20%20%20%20_ax2.set_yticks(range(NM_A))%0A%20%20%20%20_ax2.set_yticklabels(%5Bf'M%7Bm%7D'%20for%20m%20in%20range(1%2C%20NM_A%20%2B%201)%5D)%0A%20%20%20%20_ax2.set_xlabel('Duration%20used')%0A%20%20%20%20_ax2.set_title('Capacity%20utilization')%0A%20%20%20%20plt.tight_layout()%0A%20%20%20%20mo.output.append(plt.gcf())%0A%20%20%20%20return%0A%0A%0Aif%20__name__%20%3D%3D%20%22__main__%22%3A%0A%20%20%20%20app.run()%0A
22dbb04ac63fe394857b93a2ee165827