├── .gitignore ├── README.md ├── diet-ml ├── diet-model.gz ├── solution.csv ├── diet_nutrients.csv ├── kpis.csv ├── stats.csv ├── diet_food.csv ├── diet_food_nutrients.csv ├── model.py ├── ml-deploy.py ├── ml-submit.py └── main.py ├── warehouse-do ├── warehouse.pdf ├── warehouses.csv ├── supplyCosts.csv └── warehouse_ml.mod ├── warehouse-org ├── warehouse.pdf ├── .project ├── .oplproject ├── warehouse_data.mod ├── warehouse_cloud.dat └── warehouse_cloud.mod ├── warehouse-python-batch ├── mk-zip.sh ├── warehouses.csv ├── warehouse-model.tar.gz ├── submit-log.txt ├── supplyCosts.csv ├── deploy-log.txt ├── ml-deploy.py ├── warehouse_ml.mod └── ml-submit.py ├── warehouse-excel ├── warehouse_data.xlsx └── warehouse_ml.mod ├── mp-sample ├── solution.csv ├── diet_nutrients.csv ├── kpis.csv ├── diet_food.csv ├── diet_food_nutrients.csv ├── solution.json ├── model.py └── main.py ├── diet-python ├── diet_nutrients.csv ├── diet_food.csv ├── diet_food_nutrients.csv ├── model.py └── main.py ├── house-building ├── house-building1.ipynb └── house-building3.ipynb └── LICENSE /.gitignore: -------------------------------------------------------------------------------- 1 | /local 2 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # cplex-samples -------------------------------------------------------------------------------- /diet-ml/diet-model.gz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/makaishi2/cplex-samples/master/diet-ml/diet-model.gz -------------------------------------------------------------------------------- /warehouse-do/warehouse.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/makaishi2/cplex-samples/master/warehouse-do/warehouse.pdf -------------------------------------------------------------------------------- /warehouse-org/warehouse.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/makaishi2/cplex-samples/master/warehouse-org/warehouse.pdf -------------------------------------------------------------------------------- /warehouse-python-batch/mk-zip.sh: -------------------------------------------------------------------------------- 1 | export COPYFILE_DISABLE=1 2 | tar czvf warehouse-model.tar.gz warehouse_ml.mod 3 | 4 | -------------------------------------------------------------------------------- /warehouse-do/warehouses.csv: -------------------------------------------------------------------------------- 1 | name,capacity,fixedCost 2 | Bonn,1,30 3 | Bordeaux,4,30 4 | London,2,30 5 | Paris,1,30 6 | Rome,3,30 -------------------------------------------------------------------------------- /warehouse-excel/warehouse_data.xlsx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/makaishi2/cplex-samples/master/warehouse-excel/warehouse_data.xlsx -------------------------------------------------------------------------------- /warehouse-python-batch/warehouses.csv: -------------------------------------------------------------------------------- 1 | name,capacity,fixedCost 2 | Bonn,1,30 3 | Bordeaux,4,30 4 | London,2,30 5 | Paris,1,30 6 | Rome,3,30 -------------------------------------------------------------------------------- /warehouse-python-batch/warehouse-model.tar.gz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/makaishi2/cplex-samples/master/warehouse-python-batch/warehouse-model.tar.gz -------------------------------------------------------------------------------- /diet-ml/solution.csv: -------------------------------------------------------------------------------- 1 | name,value 2 | Spaghetti W/ Sauce,2.1551724137931036 3 | Chocolate Chip Cookies,10.0 4 | Lowfat Milk,1.8311671008899097 5 | Hotdog,0.9296975991385925 6 | -------------------------------------------------------------------------------- /mp-sample/solution.csv: -------------------------------------------------------------------------------- 1 | name,value 2 | Spaghetti W/ Sauce,2.1551724137931036 3 | Chocolate Chip Cookies,10.0 4 | Lowfat Milk,1.8311671008899097 5 | Hotdog,0.9296975991385925 6 | -------------------------------------------------------------------------------- /diet-ml/diet_nutrients.csv: -------------------------------------------------------------------------------- 1 | name,qmin,qmax 2 | Calories," 2000"," 2500" 3 | Calcium," 800"," 1600" 4 | Iron," 10"," 30" 5 | Vit_A," 5000"," 50000" 6 | Dietary_Fiber," 25"," 100" 7 | Carbohydrates," 0"," 300" 8 | Protein," 50"," 100" 9 | -------------------------------------------------------------------------------- /diet-python/diet_nutrients.csv: -------------------------------------------------------------------------------- 1 | name,qmin,qmax 2 | Calories," 2000"," 2500" 3 | Calcium," 800"," 1600" 4 | Iron," 10"," 30" 5 | Vit_A," 5000"," 50000" 6 | Dietary_Fiber," 25"," 100" 7 | Carbohydrates," 0"," 300" 8 | Protein," 50"," 100" 9 | -------------------------------------------------------------------------------- /mp-sample/diet_nutrients.csv: -------------------------------------------------------------------------------- 1 | name,qmin,qmax 2 | Calories," 2000"," 2500" 3 | Calcium," 800"," 1600" 4 | Iron," 10"," 30" 5 | Vit_A," 5000"," 50000" 6 | Dietary_Fiber," 25"," 100" 7 | Carbohydrates," 0"," 300" 8 | Protein," 50"," 100" 9 | -------------------------------------------------------------------------------- /diet-ml/kpis.csv: -------------------------------------------------------------------------------- 1 | Name,Value 2 | Total Calories,2000.0 3 | Total Calcium,800.0000000000001 4 | Total Iron,11.278317739831891 5 | Total Vit_A,8518.432542485823 6 | Total Dietary_Fiber,25.0 7 | Total Carbohydrates,256.80576358904455 8 | Total Protein,51.17372234135308 9 | Minimal cost,2.690409171696264 10 | -------------------------------------------------------------------------------- /diet-ml/stats.csv: -------------------------------------------------------------------------------- 1 | Name,Value 2 | STAT.cplex.size.integerVariables,0 3 | STAT.cplex.size.continousVariables,9 4 | STAT.cplex.size.linearConstraints,7 5 | STAT.cplex.size.booleanVariables,0 6 | STAT.cplex.size.constraints,7 7 | STAT.cplex.size.quadraticConstraints,0 8 | STAT.cplex.size.variables,9 9 | STAT.cplex.modelType,LP 10 | -------------------------------------------------------------------------------- /mp-sample/kpis.csv: -------------------------------------------------------------------------------- 1 | "NAME","VALUE" 2 | "Total Calories","2000.0" 3 | "Total Calcium","800.0000000000001" 4 | "Total Iron","11.278317739831891" 5 | "Total Vit_A","8518.432542485823" 6 | "Total Dietary_Fiber","25.0" 7 | "Total Carbohydrates","256.80576358904455" 8 | "Total Protein","51.17372234135308" 9 | "Minimal cost","2.690409171696264" 10 | -------------------------------------------------------------------------------- /warehouse-org/.project: -------------------------------------------------------------------------------- 1 | 2 | 3 | warehouse_cloud 4 | 5 | 6 | 7 | 8 | 9 | 10 | ilog.odms.ide.core.opl.project.nature 11 | 12 | 13 | -------------------------------------------------------------------------------- /warehouse-org/.oplproject: -------------------------------------------------------------------------------- 1 | 2 | 1.0 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /diet-ml/diet_food.csv: -------------------------------------------------------------------------------- 1 | name,unit_cost,qmin,qmax 2 | Roasted Chicken," 0.84"," 0"," 10" 3 | Spaghetti W/ Sauce," 0.78"," 0"," 10" 4 | "Tomato,Red,Ripe,Raw"," 0.27"," 0"," 10" 5 | "Apple,Raw,W/Skin"," .24"," 0"," 10" 6 | Grapes," 0.32"," 0"," 10" 7 | Chocolate Chip Cookies," 0.03"," 0"," 10" 8 | Lowfat Milk," 0.23"," 0"," 10" 9 | Raisin Brn," 0.34"," 0"," 10" 10 | Hotdog," 0.31"," 0"," 10" 11 | -------------------------------------------------------------------------------- /diet-python/diet_food.csv: -------------------------------------------------------------------------------- 1 | name,unit_cost,qmin,qmax 2 | Roasted Chicken," 0.84"," 0"," 10" 3 | Spaghetti W/ Sauce," 0.78"," 0"," 10" 4 | "Tomato,Red,Ripe,Raw"," 0.27"," 0"," 10" 5 | "Apple,Raw,W/Skin"," .24"," 0"," 10" 6 | Grapes," 0.32"," 0"," 10" 7 | Chocolate Chip Cookies," 0.03"," 0"," 10" 8 | Lowfat Milk," 0.23"," 0"," 10" 9 | Raisin Brn," 0.34"," 0"," 10" 10 | Hotdog," 0.31"," 0"," 10" 11 | -------------------------------------------------------------------------------- /mp-sample/diet_food.csv: -------------------------------------------------------------------------------- 1 | name,unit_cost,qmin,qmax 2 | Roasted Chicken," 0.84"," 0"," 10" 3 | Spaghetti W/ Sauce," 0.78"," 0"," 10" 4 | "Tomato,Red,Ripe,Raw"," 0.27"," 0"," 10" 5 | "Apple,Raw,W/Skin"," .24"," 0"," 10" 6 | Grapes," 0.32"," 0"," 10" 7 | Chocolate Chip Cookies," 0.03"," 0"," 10" 8 | Lowfat Milk," 0.23"," 0"," 10" 9 | Raisin Brn," 0.34"," 0"," 10" 10 | Hotdog," 0.31"," 0"," 10" 11 | -------------------------------------------------------------------------------- /warehouse-org/warehouse_data.mod: -------------------------------------------------------------------------------- 1 | 2 | tuple TWarehouse { 3 | key string name; 4 | int capacity; 5 | float fixedCost; 6 | } 7 | 8 | tuple TSupplyCost { 9 | key string warehouseName; 10 | key int storeId; 11 | float cost; 12 | } 13 | 14 | tuple TPlan { 15 | int nbStores; 16 | } 17 | 18 | TPlan plan = ...; 19 | {TWarehouse} warehouses = ...; 20 | {TSupplyCost} supplyCosts = ...; -------------------------------------------------------------------------------- /warehouse-python-batch/submit-log.txt: -------------------------------------------------------------------------------- 1 | $ python ml-submit.py 2 | 401c79b4-c4b5-4ad9-b1b3-9f6b013fbeee 3 | queued... 4 | queued... 5 | queued... 6 | queued... 7 | queued... 8 | { 9 | "completed_at": "2020-07-21T04:12:41.537Z", 10 | "running_at": "2020-07-21T04:12:40.449Z", 11 | "state": "completed" 12 | } 13 | warehouseName storeId 14 | 0 Bonn 4 15 | 1 Bordeaux 2 16 | 2 Bordeaux 6 17 | 3 Bordeaux 7 18 | 4 Bordeaux 9 19 | 5 London 8 20 | 6 London 10 21 | 7 Rome 1 22 | 8 Rome 3 23 | 9 Rome 5 24 | -------------------------------------------------------------------------------- /diet-ml/diet_food_nutrients.csv: -------------------------------------------------------------------------------- 1 | Food,Calories,Calcium,Iron,Vit_A,Dietary_Fiber,Carbohydrates,Protein 2 | Roasted Chicken," 277.4"," 21.9"," 1.8"," 77.4"," 0"," 0"," 42.2" 3 | Spaghetti W/ Sauce," 358.2"," 80.2"," 2.3"," 3055.2"," 11.6"," 58.3"," 8.2" 4 | "Tomato,Red,Ripe,Raw"," 25.8"," 6.2"," 0.6"," 766.3"," 1.4"," 5.7"," 1" 5 | "Apple,Raw,W/Skin"," 81.4"," 9.7"," 0.2"," 73.1"," 3.7"," 21"," 0.3" 6 | Grapes," 15.1"," 3.4"," 0.1"," 24"," 0.2"," 4.1"," 0.2" 7 | Chocolate Chip Cookies," 78.1"," 6.2"," 0.4"," 101.8"," 0"," 9.3"," 0.9" 8 | Lowfat Milk," 121.2"," 296.7"," 0.1"," 500.2"," 0"," 11.7"," 8.1" 9 | Raisin Brn," 115.1"," 12.9"," 16.8"," 1250.2"," 4"," 27.9"," 4" 10 | Hotdog," 242.1"," 23.5"," 2.3"," 0"," 0"," 18"," 10.4" 11 | -------------------------------------------------------------------------------- /diet-python/diet_food_nutrients.csv: -------------------------------------------------------------------------------- 1 | Food,Calories,Calcium,Iron,Vit_A,Dietary_Fiber,Carbohydrates,Protein 2 | Roasted Chicken," 277.4"," 21.9"," 1.8"," 77.4"," 0"," 0"," 42.2" 3 | Spaghetti W/ Sauce," 358.2"," 80.2"," 2.3"," 3055.2"," 11.6"," 58.3"," 8.2" 4 | "Tomato,Red,Ripe,Raw"," 25.8"," 6.2"," 0.6"," 766.3"," 1.4"," 5.7"," 1" 5 | "Apple,Raw,W/Skin"," 81.4"," 9.7"," 0.2"," 73.1"," 3.7"," 21"," 0.3" 6 | Grapes," 15.1"," 3.4"," 0.1"," 24"," 0.2"," 4.1"," 0.2" 7 | Chocolate Chip Cookies," 78.1"," 6.2"," 0.4"," 101.8"," 0"," 9.3"," 0.9" 8 | Lowfat Milk," 121.2"," 296.7"," 0.1"," 500.2"," 0"," 11.7"," 8.1" 9 | Raisin Brn," 115.1"," 12.9"," 16.8"," 1250.2"," 4"," 27.9"," 4" 10 | Hotdog," 242.1"," 23.5"," 2.3"," 0"," 0"," 18"," 10.4" 11 | -------------------------------------------------------------------------------- /mp-sample/diet_food_nutrients.csv: -------------------------------------------------------------------------------- 1 | Food,Calories,Calcium,Iron,Vit_A,Dietary_Fiber,Carbohydrates,Protein 2 | Roasted Chicken," 277.4"," 21.9"," 1.8"," 77.4"," 0"," 0"," 42.2" 3 | Spaghetti W/ Sauce," 358.2"," 80.2"," 2.3"," 3055.2"," 11.6"," 58.3"," 8.2" 4 | "Tomato,Red,Ripe,Raw"," 25.8"," 6.2"," 0.6"," 766.3"," 1.4"," 5.7"," 1" 5 | "Apple,Raw,W/Skin"," 81.4"," 9.7"," 0.2"," 73.1"," 3.7"," 21"," 0.3" 6 | Grapes," 15.1"," 3.4"," 0.1"," 24"," 0.2"," 4.1"," 0.2" 7 | Chocolate Chip Cookies," 78.1"," 6.2"," 0.4"," 101.8"," 0"," 9.3"," 0.9" 8 | Lowfat Milk," 121.2"," 296.7"," 0.1"," 500.2"," 0"," 11.7"," 8.1" 9 | Raisin Brn," 115.1"," 12.9"," 16.8"," 1250.2"," 4"," 27.9"," 4" 10 | Hotdog," 242.1"," 23.5"," 2.3"," 0"," 0"," 18"," 10.4" 11 | -------------------------------------------------------------------------------- /warehouse-do/supplyCosts.csv: -------------------------------------------------------------------------------- 1 | warehouseName,storeId,cost 2 | Bonn,1,20 3 | Bonn,2,28 4 | Bonn,3,74 5 | Bonn,4,2 6 | Bonn,5,46 7 | Bonn,6,42 8 | Bonn,7,1 9 | Bonn,8,10 10 | Bonn,9,93 11 | Bonn,10,47 12 | Bordeaux,1,24 13 | Bordeaux,2,27 14 | Bordeaux,3,97 15 | Bordeaux,4,55 16 | Bordeaux,5,96 17 | Bordeaux,6,22 18 | Bordeaux,7,5 19 | Bordeaux,8,73 20 | Bordeaux,9,35 21 | Bordeaux,10,65 22 | London,1,11 23 | London,2,82 24 | London,3,71 25 | London,4,73 26 | London,5,59 27 | London,6,29 28 | London,7,73 29 | London,8,13 30 | London,9,63 31 | London,10,55 32 | Paris,1,25 33 | Paris,2,83 34 | Paris,3,96 35 | Paris,4,69 36 | Paris,5,83 37 | Paris,6,87 38 | Paris,7,59 39 | Paris,8,43 40 | Paris,9,85 41 | Paris,10,71 42 | Rome,1,30 43 | Rome,2,74 44 | Rome,3,70 45 | Rome,4,61 46 | Rome,5,4 47 | Rome,6,59 48 | Rome,7,56 49 | Rome,8,96 50 | Rome,9,46 51 | Rome,10,95 -------------------------------------------------------------------------------- /warehouse-python-batch/supplyCosts.csv: -------------------------------------------------------------------------------- 1 | warehouseName,storeId,cost 2 | Bonn,1,20 3 | Bonn,2,28 4 | Bonn,3,74 5 | Bonn,4,2 6 | Bonn,5,46 7 | Bonn,6,42 8 | Bonn,7,1 9 | Bonn,8,10 10 | Bonn,9,93 11 | Bonn,10,47 12 | Bordeaux,1,24 13 | Bordeaux,2,27 14 | Bordeaux,3,97 15 | Bordeaux,4,55 16 | Bordeaux,5,96 17 | Bordeaux,6,22 18 | Bordeaux,7,5 19 | Bordeaux,8,73 20 | Bordeaux,9,35 21 | Bordeaux,10,65 22 | London,1,11 23 | London,2,82 24 | London,3,71 25 | London,4,73 26 | London,5,59 27 | London,6,29 28 | London,7,73 29 | London,8,13 30 | London,9,63 31 | London,10,55 32 | Paris,1,25 33 | Paris,2,83 34 | Paris,3,96 35 | Paris,4,69 36 | Paris,5,83 37 | Paris,6,87 38 | Paris,7,59 39 | Paris,8,43 40 | Paris,9,85 41 | Paris,10,71 42 | Rome,1,30 43 | Rome,2,74 44 | Rome,3,70 45 | Rome,4,61 46 | Rome,5,4 47 | Rome,6,59 48 | Rome,7,56 49 | Rome,8,96 50 | Rome,9,46 51 | Rome,10,95 -------------------------------------------------------------------------------- /mp-sample/solution.json: -------------------------------------------------------------------------------- 1 | {"CPLEXSolution": {"version": "1.0", "header": {"problemName": "diet", "objectiveValue": "2.6904091716962637", "solved_by": "cplex_local"}, "variables": [{"index": "1", "name": "Spaghetti W/ Sauce", "value": "2.1551724137931036", "reducedCost": 0}, {"index": "5", "name": "Chocolate Chip Cookies", "value": "10.0", "reducedCost": -0.06964156898661943}, {"index": "6", "name": "Lowfat Milk", "value": "1.8311671008899097", "reducedCost": 0}, {"index": "8", "name": "Hotdog", "value": "0.9296975991385925", "reducedCost": 0}], "linearConstraints": [{"name": null, "index": 0, "slack": -500.0, "dual": 0.0012549782286529975}, {"name": null, "index": 1, "slack": -800.0, "dual": 0.0002625434401323112}, {"name": null, "index": 2, "slack": -18.721682260168105}, {"name": null, "index": 3, "slack": -41481.56745751418}, {"name": null, "index": 4, "slack": -75.0, "dual": 0.026673346086024577}, {"name": null, "index": 5, "slack": -43.194236410955455}, {"name": null, "index": 6, "slack": -48.82627765864693}]}} -------------------------------------------------------------------------------- /diet-ml/model.py: -------------------------------------------------------------------------------- 1 | #dd-cell 2 | food = inputs['diet_food'] 3 | nutrients = inputs['diet_nutrients'] 4 | food_nutrients = inputs['diet_food_nutrients'] 5 | food_nutrients.set_index('Food', inplace=True) 6 | #dd-cell 7 | from docplex.mp.model import Model 8 | 9 | # Model 10 | mdl = Model(name='diet') 11 | 12 | # Create decision variables, limited to be >= Food.qmin and <= Food.qmax 13 | qty = food[['name', 'qmin', 'qmax']].copy() 14 | qty['var'] = qty.apply(lambda x: mdl.continuous_var(lb=x['qmin'], 15 | ub=x['qmax'], 16 | name=x['name']), 17 | axis=1) 18 | # make the name the index 19 | qty.set_index('name', inplace=True) 20 | 21 | # Limit range of nutrients, and mark them as KPIs 22 | for n in nutrients.itertuples(): 23 | amount = mdl.sum(qty.loc[f.name]['var'] * food_nutrients.loc[f.name][n.name] 24 | for f in food.itertuples()) 25 | mdl.add_range(n.qmin, amount, n.qmax) 26 | mdl.add_kpi(amount, publish_name='Total %s' % n.name) 27 | 28 | # Minimize cost 29 | obj = mdl.sum(qty.loc[f.name]['var'] * f.unit_cost for f in food.itertuples()) 30 | mdl.add_kpi(obj, publish_name="Minimal cost"); 31 | mdl.minimize(obj) 32 | 33 | mdl.print_information() 34 | #dd-markdown

Solve

35 | #dd-cell 36 | ok = mdl.solve() 37 | #dd-cell 38 | mdl.print_solution() 39 | #dd-markdown Make dataframe from solution 40 | #dd-cell 41 | import pandas 42 | import numpy 43 | 44 | solution_df = pandas.DataFrame(columns=['name', 'value']) 45 | 46 | for index, dvar in enumerate(mdl.solution.iter_variables()): 47 | solution_df.loc[index,'name'] = dvar.to_string() 48 | solution_df.loc[index,'value'] = dvar.solution_value 49 | #dd-cell 50 | solution_df 51 | #dd-cell 52 | outputs['solution'] = solution_df 53 | -------------------------------------------------------------------------------- /mp-sample/model.py: -------------------------------------------------------------------------------- 1 | #dd-cell 2 | food = inputs['diet_food'] 3 | nutrients = inputs['diet_nutrients'] 4 | food_nutrients = inputs['diet_food_nutrients'] 5 | food_nutrients.set_index('Food', inplace=True) 6 | #dd-cell 7 | from docplex.mp.model import Model 8 | 9 | # Model 10 | mdl = Model(name='diet') 11 | 12 | # Create decision variables, limited to be >= Food.qmin and <= Food.qmax 13 | qty = food[['name', 'qmin', 'qmax']].copy() 14 | qty['var'] = qty.apply(lambda x: mdl.continuous_var(lb=x['qmin'], 15 | ub=x['qmax'], 16 | name=x['name']), 17 | axis=1) 18 | # make the name the index 19 | qty.set_index('name', inplace=True) 20 | 21 | # Limit range of nutrients, and mark them as KPIs 22 | for n in nutrients.itertuples(): 23 | amount = mdl.sum(qty.loc[f.name]['var'] * food_nutrients.loc[f.name][n.name] 24 | for f in food.itertuples()) 25 | mdl.add_range(n.qmin, amount, n.qmax) 26 | mdl.add_kpi(amount, publish_name='Total %s' % n.name) 27 | 28 | # Minimize cost 29 | obj = mdl.sum(qty.loc[f.name]['var'] * f.unit_cost for f in food.itertuples()) 30 | mdl.add_kpi(obj, publish_name="Minimal cost"); 31 | mdl.minimize(obj) 32 | 33 | mdl.print_information() 34 | #dd-markdown

Solve

35 | #dd-cell 36 | ok = mdl.solve() 37 | #dd-cell 38 | mdl.print_solution() 39 | #dd-markdown Make dataframe from solution 40 | #dd-cell 41 | import pandas 42 | import numpy 43 | 44 | solution_df = pandas.DataFrame(columns=['name', 'value']) 45 | 46 | for index, dvar in enumerate(mdl.solution.iter_variables()): 47 | solution_df.loc[index,'name'] = dvar.to_string() 48 | solution_df.loc[index,'value'] = dvar.solution_value 49 | #dd-cell 50 | solution_df 51 | #dd-cell 52 | outputs['solution'] = solution_df 53 | -------------------------------------------------------------------------------- /diet-python/model.py: -------------------------------------------------------------------------------- 1 | #dd-cell 2 | food = inputs['diet_food'] 3 | nutrients = inputs['diet_nutrients'] 4 | food_nutrients = inputs['diet_food_nutrients'] 5 | food_nutrients.set_index('Food', inplace=True) 6 | #dd-cell 7 | from docplex.mp.model import Model 8 | 9 | # Model 10 | mdl = Model(name='diet') 11 | 12 | # Create decision variables, limited to be >= Food.qmin and <= Food.qmax 13 | qty = food[['name', 'qmin', 'qmax']].copy() 14 | qty['var'] = qty.apply(lambda x: mdl.continuous_var(lb=x['qmin'], 15 | ub=x['qmax'], 16 | name=x['name']), 17 | axis=1) 18 | # make the name the index 19 | qty.set_index('name', inplace=True) 20 | 21 | # Limit range of nutrients, and mark them as KPIs 22 | for n in nutrients.itertuples(): 23 | amount = mdl.sum(qty.loc[f.name]['var'] * food_nutrients.loc[f.name][n.name] 24 | for f in food.itertuples()) 25 | mdl.add_range(n.qmin, amount, n.qmax) 26 | mdl.add_kpi(amount, publish_name='Total %s' % n.name) 27 | 28 | # Minimize cost 29 | obj = mdl.sum(qty.loc[f.name]['var'] * f.unit_cost for f in food.itertuples()) 30 | mdl.add_kpi(obj, publish_name="Minimal cost"); 31 | mdl.minimize(obj) 32 | 33 | mdl.print_information() 34 | #dd-markdown

Solve

35 | #dd-cell 36 | ok = mdl.solve() 37 | #dd-cell 38 | mdl.print_solution() 39 | #dd-markdown Make dataframe from solution 40 | #dd-cell 41 | import pandas 42 | import numpy 43 | 44 | solution_df = pandas.DataFrame(columns=['name', 'value']) 45 | 46 | for index, dvar in enumerate(mdl.solution.iter_variables()): 47 | solution_df.loc[index,'name'] = dvar.to_string() 48 | solution_df.loc[index,'value'] = dvar.solution_value 49 | #dd-cell 50 | solution_df 51 | #dd-cell 52 | outputs['solution'] = solution_df 53 | -------------------------------------------------------------------------------- /warehouse-org/warehouse_cloud.dat: -------------------------------------------------------------------------------- 1 | // -------------------------------------------------------------------------- 2 | // Licensed Materials - Property of IBM 3 | // 4 | // 5725-A06 5725-A29 5724-Y48 5724-Y49 5724-Y54 5724-Y55 5 | // Copyright IBM Corporation 1998, 2013. All Rights Reserved. 6 | // 7 | // Note to U.S. Government Users Restricted Rights: 8 | // Use, duplication or disclosure restricted by GSA ADP Schedule 9 | // Contract with IBM Corp. 10 | // -------------------------------------------------------------------------- 11 | 12 | // 10 stores to be opened. 13 | plan = <10>; 14 | 15 | warehouses = 16 | { 17 | 18 | , 19 | , 20 | , 21 | , 22 | }; 23 | 24 | supplyCosts = 25 | { 26 | 27 | , 28 | , 29 | , 30 | , 31 | , 32 | , 33 | , 34 | , 35 | , 36 | // 37 | , 38 | , 39 | , 40 | , 41 | , 42 | , 43 | , 44 | , 45 | , 46 | , 47 | // 48 | , 49 | , 50 | , 51 | , 52 | , 53 | , 54 | , 55 | , 56 | , 57 | , 58 | // 59 | , 60 | , 61 | , 62 | , 63 | , 64 | , 65 | , 66 | , 67 | , 68 | , 69 | // 70 | , 71 | , 72 | , 73 | , 74 | , 75 | , 76 | , 77 | , 78 | , 79 | , 80 | 81 | }; 82 | -------------------------------------------------------------------------------- /warehouse-org/warehouse_cloud.mod: -------------------------------------------------------------------------------- 1 | // -------------------------------------------------------------------------- 2 | // Licensed Materials - Property of IBM 3 | // 4 | // 5725-A06 5725-A29 5724-Y48 5724-Y49 5724-Y54 5724-Y55 5 | // Copyright IBM Corporation 1998, 2013. All Rights Reserved. 6 | // 7 | // Note to U.S. Government Users Restricted Rights: 8 | // Use, duplication or disclosure restricted by GSA ADP Schedule 9 | // Contract with IBM Corp. 10 | // -------------------------------------------------------------------------- 11 | 12 | include "warehouse_data.mod"; 13 | 14 | 15 | range stores = 1..plan.nbStores; 16 | 17 | dvar boolean Open[ warehouses ]; 18 | dvar boolean Supply[ stores ][ warehouses ]; 19 | 20 | // expression 21 | dexpr float totalOpeningCost = sum( w in warehouses ) w.fixedCost * Open[w]; 22 | dexpr float totalSupplyCost = sum( w in warehouses , s in stores, k in supplyCosts : k.storeId == s && k.warehouseName == w.name ) 23 | Supply[s][w] * k.cost; 24 | 25 | minimize 26 | totalOpeningCost + totalSupplyCost; 27 | 28 | subject to { 29 | forall( s in stores ) 30 | ctEachStoreHasOneWarehouse: 31 | sum( w in warehouses ) Supply[s][w] == 1; 32 | 33 | forall( w in warehouses, s in stores ) 34 | ctUseOpenWarehouses: 35 | Supply[s][w] <= Open[w]; 36 | 37 | forall( w in warehouses ) 38 | ctMaxUseOfWarehouse: 39 | sum( s in stores) Supply[s][w] <= w.capacity; 40 | } 41 | 42 | 43 | {int} StoresSupplied[w in warehouses] = { s | s in stores : Supply[s][w] == 1 }; 44 | {string} OpenWarehouses = { w.name | w in warehouses : Open[w] == 1 }; 45 | tuple TSuppliedStore { 46 | string warehouseName; 47 | int storeId; 48 | } 49 | {TSuppliedStore} network; 50 | 51 | execute DISPLAY_RESULTS{ 52 | network.clear(); 53 | writeln("* Open Warehouses=", OpenWarehouses); 54 | for ( var w in warehouses) { 55 | if ( Open[w] ==1) { 56 | writeln("* stores supplied by ", w.name, ": ", StoresSupplied[w]); 57 | for (var s in stores) { 58 | if (Supply[s][w] == 1) { 59 | network.addOnly(w.name, s); 60 | } 61 | } 62 | } 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /warehouse-python-batch/deploy-log.txt: -------------------------------------------------------------------------------- 1 | $ python ml-deploy.py 2 | List Models 3 | ------------------------------------ ------------------------------------- ------------------------ ---------------- 4 | GUID NAME CREATED TYPE 5 | b862c975-dc26-4b0a-94d5-06f099fb5a51 Auto generated DO opl model 2020-07-19T06:59:38.305Z do-opl_12.10 6 | ------------------------------------ ------------------------------------- ------------------------ ---------------- 7 | List Deployments 8 | ------------------------------------ ------------------------------------------ ----- ------------------------ ------------- 9 | GUID NAME STATE CREATED ARTIFACT_TYPE 10 | 00616f70-64b0-46fb-97f1-3284bc8a6640 Auto generated DO opl deployment ready 2020-07-19T06:59:38.400Z model 11 | ------------------------------------ ------------------------------------------ ----- ------------------------ ------------- 12 | 211f73af-6a7c-445c-a5b8-ffa526fd703f 13 | 14 | 15 | ####################################################################################### 16 | 17 | Synchronous deployment creation for uid: '211f73af-6a7c-445c-a5b8-ffa526fd703f' started 18 | 19 | ####################################################################################### 20 | 21 | 22 | ready. 23 | 24 | 25 | ------------------------------------------------------------------------------------------------ 26 | Successfully finished deployment creation, deployment_uid='cf184900-bce6-4737-bfdb-ec8902057f35' 27 | ------------------------------------------------------------------------------------------------ 28 | 29 | 30 | cf184900-bce6-4737-bfdb-ec8902057f35 31 | ------------------------------------ ------------------------------------------ ----- ------------------------ ------------- 32 | GUID NAME STATE CREATED ARTIFACT_TYPE 33 | cf184900-bce6-4737-bfdb-ec8902057f35 WAREHOUSE OPL Deployment ready 2020-07-21T04:06:42.233Z model 34 | 00616f70-64b0-46fb-97f1-3284bc8a6640 Auto generated DO opl deployment ready 2020-07-19T06:59:38.400Z model 35 | ------------------------------------ ------------------------------------------ ----- ------------------------ ------------- 36 | -------------------------------------------------------------------------------- /warehouse-python-batch/ml-deploy.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | # コマンドによる事前準備 4 | # $ pip install watson-machine-learning-client-V4 5 | # $ MACでは次が重要 6 | # $ export COPYFILE_DISABLE=1 7 | # $ tar czvf warehouse-model.tar.gz warehouse_ml.mod 8 | 9 | import sys 10 | 11 | # Watson ML credentails 12 | apikey = 'xxxx' 13 | instance_id = 'xxxx' 14 | 15 | tarfile = 'warehouse-model.tar.gz' 16 | 17 | # -------------------------------------------------------- 18 | # メインルーチン 19 | # -------------------------------------------------------- 20 | if __name__ == '__main__': 21 | 22 | # 引数の受け取り 23 | argv = sys.argv 24 | argc = len(argv) 25 | 26 | wml_credentials = { 27 | "apikey": apikey, 28 | "instance_id": instance_id, 29 | "url": 'https://us-south.ml.cloud.ibm.com' 30 | } 31 | 32 | from watson_machine_learning_client import WatsonMachineLearningAPIClient 33 | client = WatsonMachineLearningAPIClient(wml_credentials) 34 | 35 | print('List Models') 36 | client.repository.list_models() 37 | 38 | print('List Deployments') 39 | client.deployments.list() 40 | 41 | # 登録に必要な情報の設定 42 | mdl_07_metadata = { 43 | client.repository.ModelMetaNames.NAME: "WAREHOUSE OPL", 44 | client.repository.ModelMetaNames.DESCRIPTION: "WAREHOUSE OPL", 45 | #client.repository.ModelMetaNames.TYPE: "do-docplex_12.10", 46 | client.repository.ModelMetaNames.TYPE: "do-opl_12.10", 47 | client.repository.ModelMetaNames.RUNTIME_UID: "do_12.10" 48 | } 49 | 50 | # モデルの登録 51 | model_details = client.repository.store_model(model=tarfile, meta_props=mdl_07_metadata) 52 | 53 | # モデルUIDの取得 54 | model_uid = client.repository.get_model_uid(model_details) 55 | print( model_uid ) 56 | 57 | # Webサービス化に必要な情報 58 | meta_props = { 59 | client.deployments.ConfigurationMetaNames.NAME: "WAREHOUSE OPL Deployment", 60 | client.deployments.ConfigurationMetaNames.DESCRIPTION: "WAREHOUSE OPL Deployment", 61 | client.deployments.ConfigurationMetaNames.BATCH: {}, 62 | client.deployments.ConfigurationMetaNames.COMPUTE: {'name': 'S', 'nodes': 1} 63 | } 64 | 65 | # Webサービス化 66 | deployment_details = client.deployments.create(model_uid, meta_props=meta_props) 67 | 68 | deployment_uid = client.deployments.get_uid(deployment_details) 69 | print( deployment_uid ) 70 | 71 | client.deployments.list() 72 | 73 | -------------------------------------------------------------------------------- /diet-ml/ml-deploy.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | # コマンドによる事前準備 4 | # $ pip install -U ibm-watson-machine-learning 5 | # $ MACでは次が重要 6 | # $ export COPYFILE_DISABLE=1 7 | # $ tar czvf diet-model.gz main.py model.py 8 | # main.py 共通に使われるmodel呼び出し用コード 9 | # model.py DO実装コード model.builderで動作確認したもの 10 | 11 | import sys 12 | 13 | # Watson ML credentails 14 | apikey = 'xxxx' 15 | location = 'us-south' 16 | 17 | tarfile = 'diet-model.gz' 18 | 19 | # -------------------------------------------------------- 20 | # メインルーチン 21 | # -------------------------------------------------------- 22 | if __name__ == '__main__': 23 | 24 | # 引数の受け取り 25 | argv = sys.argv 26 | argc = len(argv) 27 | 28 | 29 | wml_credentials = { 30 | "apikey": apikey, 31 | "url": 'https://' + location + '.ml.cloud.ibm.com' 32 | } 33 | 34 | from ibm_watson_machine_learning import APIClient 35 | client = APIClient(wml_credentials) 36 | 37 | client.spaces.list() 38 | space_id = 'xxxx' 39 | client.set.default_space(space_id) 40 | 41 | software_spec_uid = client.software_specifications.get_uid_by_name("do_12.10") 42 | print(software_spec_uid) 43 | 44 | # 登録に必要な情報の設定 45 | mdl_metadata = { 46 | client.repository.ModelMetaNames.NAME: "Diet Python", 47 | client.repository.ModelMetaNames.DESCRIPTION: "Diet Python", 48 | client.repository.ModelMetaNames.TYPE: "do-docplex_12.10", 49 | client.repository.ModelMetaNames.SOFTWARE_SPEC_UID: software_spec_uid 50 | } 51 | 52 | # モデルの登録 53 | model_details = client.repository.store_model(model=tarfile, meta_props=mdl_metadata) 54 | 55 | # モデルUIDの取得 56 | model_uid = client.repository.get_model_uid(model_details) 57 | print( model_uid ) 58 | 59 | # Webサービス化に必要な情報 60 | 61 | meta_props = { 62 | client.deployments.ConfigurationMetaNames.NAME: "Diet Python Web", 63 | client.deployments.ConfigurationMetaNames.DESCRIPTION: "Diet Python Web", 64 | client.deployments.ConfigurationMetaNames.BATCH: {}, 65 | client.deployments.ConfigurationMetaNames.HARDWARE_SPEC: {'name': 'S', 'nodes': 1} # S / M / XL 66 | } 67 | 68 | # Webサービス化 69 | deployment_details = client.deployments.create(model_uid, meta_props=meta_props) 70 | 71 | deployment_uid = client.deployments.get_uid(deployment_details) 72 | print( deployment_uid ) 73 | 74 | # Webサービスの一覧表示 75 | client.deployments.list() 76 | -------------------------------------------------------------------------------- /warehouse-do/warehouse_ml.mod: -------------------------------------------------------------------------------- 1 | // -------------------------------------------------------------------------- 2 | // Licensed Materials - Property of IBM 3 | // 4 | // 5725-A06 5725-A29 5724-Y48 5724-Y49 5724-Y54 5724-Y55 5 | // Copyright IBM Corporation 1998, 2013. All Rights Reserved. 6 | // 7 | // Note to U.S. Government Users Restricted Rights: 8 | // Use, duplication or disclosure restricted by GSA ADP Schedule 9 | // Contract with IBM Corp. 10 | // -------------------------------------------------------------------------- 11 | 12 | tuple TWarehouse { 13 | key string name; 14 | int capacity; 15 | float fixedCost; 16 | } 17 | {TWarehouse} warehouses = ...; 18 | 19 | tuple TSupplyCost { 20 | key string warehouseName; 21 | key int storeId; 22 | float cost; 23 | } 24 | {TSupplyCost} supplyCosts = ...; 25 | 26 | tuple TPlan { 27 | int nbStores; 28 | } 29 | 30 | // Excel化できないための一時対応 31 | TPlan plan = <10>; 32 | 33 | range stores = 1..plan.nbStores; 34 | 35 | dvar boolean Open[ warehouses ]; 36 | dvar boolean Supply[ stores ][ warehouses ]; 37 | 38 | // expression 39 | dexpr float totalOpeningCost = sum( w in warehouses ) w.fixedCost * Open[w]; 40 | dexpr float totalSupplyCost = sum( w in warehouses , s in stores, k in supplyCosts : k.storeId == s && k.warehouseName == w.name ) 41 | Supply[s][w] * k.cost; 42 | 43 | minimize 44 | totalOpeningCost + totalSupplyCost; 45 | 46 | subject to { 47 | forall( s in stores ) 48 | ctEachStoreHasOneWarehouse: 49 | sum( w in warehouses ) Supply[s][w] == 1; 50 | 51 | forall( w in warehouses, s in stores ) 52 | ctUseOpenWarehouses: 53 | Supply[s][w] <= Open[w]; 54 | 55 | forall( w in warehouses ) 56 | ctMaxUseOfWarehouse: 57 | sum( s in stores) Supply[s][w] <= w.capacity; 58 | } 59 | 60 | 61 | {int} StoresSupplied[w in warehouses] = { s | s in stores : Supply[s][w] == 1 }; 62 | {string} OpenWarehouses = { w.name | w in warehouses : Open[w] == 1 }; 63 | tuple TSuppliedStore { 64 | string warehouseName; 65 | int storeId; 66 | } 67 | {TSuppliedStore} network; 68 | 69 | execute DISPLAY_RESULTS{ 70 | network.clear(); 71 | writeln("* Open Warehouses=", OpenWarehouses); 72 | for ( var w in warehouses) { 73 | if ( Open[w] ==1) { 74 | writeln("* stores supplied by ", w.name, ": ", StoresSupplied[w]); 75 | for (var s in stores) { 76 | if (Supply[s][w] == 1) { 77 | network.addOnly(w.name, s); 78 | } 79 | } 80 | } 81 | } 82 | } 83 | -------------------------------------------------------------------------------- /warehouse-excel/warehouse_ml.mod: -------------------------------------------------------------------------------- 1 | // -------------------------------------------------------------------------- 2 | // Licensed Materials - Property of IBM 3 | // 4 | // 5725-A06 5725-A29 5724-Y48 5724-Y49 5724-Y54 5724-Y55 5 | // Copyright IBM Corporation 1998, 2013. All Rights Reserved. 6 | // 7 | // Note to U.S. Government Users Restricted Rights: 8 | // Use, duplication or disclosure restricted by GSA ADP Schedule 9 | // Contract with IBM Corp. 10 | // -------------------------------------------------------------------------- 11 | 12 | tuple TWarehouse { 13 | key string name; 14 | int capacity; 15 | float fixedCost; 16 | } 17 | {TWarehouse} warehouses = ...; 18 | 19 | tuple TSupplyCost { 20 | key string warehouseName; 21 | key int storeId; 22 | float cost; 23 | } 24 | {TSupplyCost} supplyCosts = ...; 25 | 26 | tuple TPlan { 27 | int nbStores; 28 | } 29 | 30 | // Excel化できないための一時対応 31 | TPlan plan = <10>; 32 | 33 | range stores = 1..plan.nbStores; 34 | 35 | dvar boolean Open[ warehouses ]; 36 | dvar boolean Supply[ stores ][ warehouses ]; 37 | 38 | // expression 39 | dexpr float totalOpeningCost = sum( w in warehouses ) w.fixedCost * Open[w]; 40 | dexpr float totalSupplyCost = sum( w in warehouses , s in stores, k in supplyCosts : k.storeId == s && k.warehouseName == w.name ) 41 | Supply[s][w] * k.cost; 42 | 43 | minimize 44 | totalOpeningCost + totalSupplyCost; 45 | 46 | subject to { 47 | forall( s in stores ) 48 | ctEachStoreHasOneWarehouse: 49 | sum( w in warehouses ) Supply[s][w] == 1; 50 | 51 | forall( w in warehouses, s in stores ) 52 | ctUseOpenWarehouses: 53 | Supply[s][w] <= Open[w]; 54 | 55 | forall( w in warehouses ) 56 | ctMaxUseOfWarehouse: 57 | sum( s in stores) Supply[s][w] <= w.capacity; 58 | } 59 | 60 | 61 | {int} StoresSupplied[w in warehouses] = { s | s in stores : Supply[s][w] == 1 }; 62 | {string} OpenWarehouses = { w.name | w in warehouses : Open[w] == 1 }; 63 | tuple TSuppliedStore { 64 | string warehouseName; 65 | int storeId; 66 | } 67 | {TSuppliedStore} network; 68 | 69 | execute DISPLAY_RESULTS{ 70 | network.clear(); 71 | writeln("* Open Warehouses=", OpenWarehouses); 72 | for ( var w in warehouses) { 73 | if ( Open[w] ==1) { 74 | writeln("* stores supplied by ", w.name, ": ", StoresSupplied[w]); 75 | for (var s in stores) { 76 | if (Supply[s][w] == 1) { 77 | network.addOnly(w.name, s); 78 | } 79 | } 80 | } 81 | } 82 | } 83 | -------------------------------------------------------------------------------- /warehouse-python-batch/warehouse_ml.mod: -------------------------------------------------------------------------------- 1 | // -------------------------------------------------------------------------- 2 | // Licensed Materials - Property of IBM 3 | // 4 | // 5725-A06 5725-A29 5724-Y48 5724-Y49 5724-Y54 5724-Y55 5 | // Copyright IBM Corporation 1998, 2013. All Rights Reserved. 6 | // 7 | // Note to U.S. Government Users Restricted Rights: 8 | // Use, duplication or disclosure restricted by GSA ADP Schedule 9 | // Contract with IBM Corp. 10 | // -------------------------------------------------------------------------- 11 | 12 | tuple TWarehouse { 13 | key string name; 14 | int capacity; 15 | float fixedCost; 16 | } 17 | {TWarehouse} warehouses = ...; 18 | 19 | tuple TSupplyCost { 20 | key string warehouseName; 21 | key int storeId; 22 | float cost; 23 | } 24 | {TSupplyCost} supplyCosts = ...; 25 | 26 | tuple TPlan { 27 | int nbStores; 28 | } 29 | 30 | // Excel化できないための一時対応 31 | TPlan plan = <10>; 32 | 33 | range stores = 1..plan.nbStores; 34 | 35 | dvar boolean Open[ warehouses ]; 36 | dvar boolean Supply[ stores ][ warehouses ]; 37 | 38 | // expression 39 | dexpr float totalOpeningCost = sum( w in warehouses ) w.fixedCost * Open[w]; 40 | dexpr float totalSupplyCost = sum( w in warehouses , s in stores, k in supplyCosts : k.storeId == s && k.warehouseName == w.name ) 41 | Supply[s][w] * k.cost; 42 | 43 | minimize 44 | totalOpeningCost + totalSupplyCost; 45 | 46 | subject to { 47 | forall( s in stores ) 48 | ctEachStoreHasOneWarehouse: 49 | sum( w in warehouses ) Supply[s][w] == 1; 50 | 51 | forall( w in warehouses, s in stores ) 52 | ctUseOpenWarehouses: 53 | Supply[s][w] <= Open[w]; 54 | 55 | forall( w in warehouses ) 56 | ctMaxUseOfWarehouse: 57 | sum( s in stores) Supply[s][w] <= w.capacity; 58 | } 59 | 60 | 61 | {int} StoresSupplied[w in warehouses] = { s | s in stores : Supply[s][w] == 1 }; 62 | {string} OpenWarehouses = { w.name | w in warehouses : Open[w] == 1 }; 63 | tuple TSuppliedStore { 64 | string warehouseName; 65 | int storeId; 66 | } 67 | {TSuppliedStore} network; 68 | 69 | execute DISPLAY_RESULTS{ 70 | network.clear(); 71 | writeln("* Open Warehouses=", OpenWarehouses); 72 | for ( var w in warehouses) { 73 | if ( Open[w] ==1) { 74 | writeln("* stores supplied by ", w.name, ": ", StoresSupplied[w]); 75 | for (var s in stores) { 76 | if (Supply[s][w] == 1) { 77 | network.addOnly(w.name, s); 78 | } 79 | } 80 | } 81 | } 82 | } 83 | -------------------------------------------------------------------------------- /warehouse-python-batch/ml-submit.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | # コマンドによる事前準備 4 | # pip install watson-machine-learning-client-V4 5 | 6 | import sys 7 | import pandas as pd 8 | 9 | # Watson ML credentails 10 | apikey = 'xxxx' 11 | instance_id = 'xxxx' 12 | 13 | # DO Deployment ID 14 | deployment_uid = 'xxxx' 15 | 16 | # Input CSV File 17 | input_data1 = 'warehouses.csv' 18 | input_data2 = 'supplyCosts.csv' 19 | 20 | # -------------------------------------------------------- 21 | # メインルーチン 22 | # -------------------------------------------------------- 23 | if __name__ == '__main__': 24 | 25 | # 引数の受け取り 26 | argv = sys.argv 27 | argc = len(argv) 28 | 29 | wml_credentials = { 30 | "apikey": apikey, 31 | "instance_id": instance_id, 32 | "url": 'https://us-south.ml.cloud.ibm.com' 33 | } 34 | 35 | from watson_machine_learning_client import WatsonMachineLearningAPIClient 36 | client = WatsonMachineLearningAPIClient(wml_credentials) 37 | 38 | input_df1 = pd.read_csv(input_data1) 39 | input_df2 = pd.read_csv(input_data2) 40 | 41 | solve_payload = { 42 | client.deployments.DecisionOptimizationMetaNames.INPUT_DATA: [ 43 | { 44 | "id": input_data1, 45 | "values" : input_df1 46 | }, 47 | { 48 | "id": input_data2, 49 | "values" : input_df2 50 | } 51 | ], 52 | client.deployments.DecisionOptimizationMetaNames.OUTPUT_DATA: [ 53 | { 54 | "id":".*\.csv" 55 | } 56 | ] 57 | } 58 | 59 | # DO Job 投入 60 | job_details = client.deployments.create_job(deployment_uid, solve_payload) 61 | job_uid = client.deployments.get_job_uid(job_details) 62 | print( job_uid ) 63 | 64 | 65 | # status確認 66 | from time import sleep 67 | while job_details['entity']['decision_optimization']['status']['state'] not in ['completed', 'failed', 'canceled']: 68 | print(job_details['entity']['decision_optimization']['status']['state'] + '...') 69 | sleep(5) 70 | job_details=client.deployments.get_job_details(job_uid) 71 | 72 | detail = job_details['entity']['decision_optimization']['output_data'] 73 | 74 | # 結果確認 75 | import json 76 | detail2 = job_details['entity']['decision_optimization'] 77 | 78 | # 最終ステータス表示 79 | print(json.dumps(detail2['status'], indent=2)) 80 | 81 | # 結果表示 82 | for item in detail: 83 | id = item['id'] 84 | fields = item['fields'] 85 | values = item['values'] 86 | df_work = pd.DataFrame(values, columns=fields) 87 | print(df_work.head(10)) 88 | -------------------------------------------------------------------------------- /diet-ml/ml-submit.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | # コマンドによる事前準備 4 | # $ pip install -U ibm-watson-machine-learning 5 | 6 | import sys 7 | 8 | # Watson ML credentails 9 | apikey = 'xxxx' 10 | location = 'us-south' 11 | 12 | import pandas as pd 13 | 14 | # Watson ML credentails 15 | 16 | # DO Deployment ID 17 | deployment_uid = 'xxxx' 18 | 19 | # Input CSV File 20 | input_data1 = 'diet_food.csv' 21 | input_data2 = 'diet_nutrients.csv' 22 | input_data3 = 'diet_food_nutrients.csv' 23 | 24 | # -------------------------------------------------------- 25 | # メインルーチン 26 | # -------------------------------------------------------- 27 | if __name__ == '__main__': 28 | 29 | # 引数の受け取り 30 | argv = sys.argv 31 | argc = len(argv) 32 | 33 | wml_credentials = { 34 | "apikey": apikey, 35 | "url": 'https://' + location + '.ml.cloud.ibm.com' 36 | } 37 | 38 | from ibm_watson_machine_learning import APIClient 39 | client = APIClient(wml_credentials) 40 | 41 | client.spaces.list() 42 | space_id = '20f3d4c5-1faa-4c80-a361-4da68d362b0f' 43 | client.set.default_space(space_id) 44 | 45 | input_df1 = pd.read_csv(input_data1) 46 | input_df2 = pd.read_csv(input_data2) 47 | input_df3 = pd.read_csv(input_data3) 48 | 49 | solve_payload = { 50 | client.deployments.DecisionOptimizationMetaNames.INPUT_DATA: [ 51 | { 52 | "id": input_data1, 53 | "values" : input_df1 54 | }, 55 | { 56 | "id": input_data2, 57 | "values" : input_df2 58 | }, 59 | { 60 | "id": input_data3, 61 | "values" : input_df3 62 | } 63 | ], 64 | client.deployments.DecisionOptimizationMetaNames.OUTPUT_DATA: [ 65 | { 66 | "id":".*\.csv" 67 | } 68 | ] 69 | } 70 | 71 | # DO Job 投入 72 | job_details = client.deployments.create_job(deployment_uid, solve_payload) 73 | job_uid = client.deployments.get_job_uid(job_details) 74 | print( job_uid ) 75 | 76 | 77 | # status確認 78 | from time import sleep 79 | while job_details['entity']['decision_optimization']['status']['state'] not in ['completed', 'failed', 'canceled']: 80 | print(job_details['entity']['decision_optimization']['status']['state'] + '...') 81 | sleep(5) 82 | job_details=client.deployments.get_job_details(job_uid) 83 | 84 | detail = job_details['entity']['decision_optimization']['output_data'] 85 | 86 | # 結果確認 87 | import json 88 | detail2 = job_details['entity']['decision_optimization'] 89 | 90 | # 最終ステータス表示 91 | print(json.dumps(detail2['status'], indent=2)) 92 | 93 | for item in detail: 94 | id = item['id'] 95 | fields = item['fields'] 96 | values = item['values'] 97 | df_work = pd.DataFrame(values, columns=fields) 98 | name = id[:id.index('.csv')] 99 | print('name = ', name) 100 | print(df_work.head()) 101 | df_work.to_csv(id, index=False) 102 | -------------------------------------------------------------------------------- /house-building/house-building1.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "markdown", 5 | "metadata": {}, 6 | "source": [ 7 | "# House Building1\n", 8 | "\n", 9 | "## 単純な問題\n", 10 | "各工程の期間、依存関係のみが制約" 11 | ] 12 | }, 13 | { 14 | "cell_type": "markdown", 15 | "metadata": {}, 16 | "source": [ 17 | "元リンク\n", 18 | "\n", 19 | "https://ibmdecisionoptimization.github.io/tutorials/html/Scheduling_Tutorial.html" 20 | ] 21 | }, 22 | { 23 | "cell_type": "markdown", 24 | "metadata": {}, 25 | "source": [ 26 | "API reference\n", 27 | "\n", 28 | "https://ibmdecisionoptimization.github.io/docplex-doc/cp/docplex.cp.expression.py.html" 29 | ] 30 | }, 31 | { 32 | "cell_type": "code", 33 | "execution_count": null, 34 | "metadata": {}, 35 | "outputs": [], 36 | "source": [ 37 | "# 工程名\n", 38 | "TaskNames = [\"masonry\", \"carpentry\", \"plumbing\", \n", 39 | " \"ceiling\", \"roofing\", \"painting\", \n", 40 | " \"windows\", \"facade\", \"garden\", \"moving\"]\n", 41 | "\n", 42 | "# 工程毎の期間\n", 43 | "Duration = [35, 15, 40, 15, 5, 10, 5, 10, 5, 5]\n", 44 | "\n", 45 | "# 工程間依存関係\n", 46 | "Precedences = [(\"masonry\", \"carpentry\"),(\"masonry\", \"plumbing\"),\n", 47 | " (\"masonry\", \"ceiling\"), (\"carpentry\", \"roofing\"),\n", 48 | " (\"ceiling\", \"painting\"), (\"roofing\", \"windows\"), \n", 49 | " (\"roofing\", \"facade\"), (\"plumbing\", \"facade\"),\n", 50 | " (\"roofing\", \"garden\"), (\"plumbing\", \"garden\"),\n", 51 | " (\"windows\", \"moving\"), (\"facade\", \"moving\"), \n", 52 | " (\"garden\", \"moving\"), (\"painting\", \"moving\")]" 53 | ] 54 | }, 55 | { 56 | "cell_type": "code", 57 | "execution_count": null, 58 | "metadata": {}, 59 | "outputs": [], 60 | "source": [ 61 | "# ライブラリのimportとモデルのインスタンス作成\n", 62 | "\n", 63 | "import sys\n", 64 | "from docplex.cp.model import *\n", 65 | "mdl0 = CpoModel()" 66 | ] 67 | }, 68 | { 69 | "cell_type": "code", 70 | "execution_count": null, 71 | "metadata": {}, 72 | "outputs": [], 73 | "source": [ 74 | "# 工程ごとの期間の定義\n", 75 | "\n", 76 | "# タスク名からInterval変数を引く辞書\n", 77 | "itvs = {}\n", 78 | "\n", 79 | "# Interval変数の定義\n", 80 | "# タスク名\n", 81 | "for i,taskName in enumerate(TaskNames):\n", 82 | " itvs[taskName] = mdl0.interval_var(size=Duration[i], name=taskName)" 83 | ] 84 | }, 85 | { 86 | "cell_type": "code", 87 | "execution_count": null, 88 | "metadata": {}, 89 | "outputs": [], 90 | "source": [ 91 | "# タスク間の依存関係定義\n", 92 | "\n", 93 | "for p in Precedences:\n", 94 | " mdl0.add(mdl0.end_before_start(itvs[p[0]], itvs[p[1]]) )" 95 | ] 96 | }, 97 | { 98 | "cell_type": "code", 99 | "execution_count": null, 100 | "metadata": {}, 101 | "outputs": [], 102 | "source": [ 103 | "# モデルを解く\n", 104 | "\n", 105 | "print(\"\\nSolving model....\")\n", 106 | "msol0 = mdl0.solve(TimeLimit=10)\n", 107 | "print(\"done\")" 108 | ] 109 | }, 110 | { 111 | "cell_type": "code", 112 | "execution_count": null, 113 | "metadata": {}, 114 | "outputs": [], 115 | "source": [ 116 | "# 結果表示\n", 117 | "\n", 118 | "for taskName in TaskNames:\n", 119 | " var_sol = msol0.get_var_solution(taskName)\n", 120 | " print(f\"{taskName} : {var_sol.get_start()}..{var_sol.get_end()}\")" 121 | ] 122 | }, 123 | { 124 | "cell_type": "code", 125 | "execution_count": null, 126 | "metadata": {}, 127 | "outputs": [], 128 | "source": [ 129 | "# グラフ化\n", 130 | "\n", 131 | "import docplex.cp.utils_visu as visu\n", 132 | "import matplotlib.pyplot as plt\n", 133 | "%matplotlib inline\n", 134 | "#Change the plot size\n", 135 | "from pylab import rcParams\n", 136 | "rcParams['figure.figsize'] = 15, 3\n", 137 | "\n", 138 | "for taskName in TaskNames:\n", 139 | " wt = msol0.get_var_solution(taskName)\n", 140 | " visu.interval(wt, 'lightblue', taskName) \n", 141 | "visu.show()" 142 | ] 143 | }, 144 | { 145 | "cell_type": "code", 146 | "execution_count": null, 147 | "metadata": {}, 148 | "outputs": [], 149 | "source": [] 150 | } 151 | ], 152 | "metadata": { 153 | "kernelspec": { 154 | "display_name": "Python 3", 155 | "language": "python", 156 | "name": "python3" 157 | }, 158 | "language_info": { 159 | "codemirror_mode": { 160 | "name": "ipython", 161 | "version": 3 162 | }, 163 | "file_extension": ".py", 164 | "mimetype": "text/x-python", 165 | "name": "python", 166 | "nbconvert_exporter": "python", 167 | "pygments_lexer": "ipython3", 168 | "version": "3.7.3" 169 | } 170 | }, 171 | "nbformat": 4, 172 | "nbformat_minor": 2 173 | } 174 | -------------------------------------------------------------------------------- /diet-ml/main.py: -------------------------------------------------------------------------------- 1 | from functools import partial, wraps 2 | import os 3 | from os.path import splitext 4 | import time 5 | import threading 6 | import traceback 7 | import sys 8 | import ntpath 9 | 10 | import pandas 11 | from six import iteritems 12 | 13 | from docplex.util.environment import get_environment 14 | 15 | output_lock = threading.Lock() 16 | 17 | 18 | def set_stop_callback(cb): 19 | env = get_environment() 20 | env.abort_callbacks += [cb] 21 | 22 | 23 | def get_all_inputs(): 24 | '''Utility method to read a list of files and return a tuple with all 25 | read data frames. 26 | Returns: 27 | a map { datasetname: data frame } 28 | ''' 29 | result = {} 30 | env = get_environment() 31 | for iname in [f for f in os.listdir('.') if splitext(f)[1] == '.csv']: 32 | with env.get_input_stream(iname) as in_stream: 33 | df = pandas.read_csv(in_stream) 34 | datasetname, _ = splitext(iname) 35 | result[datasetname] = df 36 | return result 37 | 38 | 39 | def callonce(f): 40 | @wraps(f) 41 | def wrapper(*args, **kwargs): 42 | if not wrapper.called: 43 | wrapper.called = True 44 | return f(*args, **kwargs) 45 | wrapper.called = False 46 | return wrapper 47 | 48 | 49 | @callonce 50 | def write_all_outputs(outputs): 51 | '''Write all dataframes in ``outputs`` as .csv. 52 | 53 | Args: 54 | outputs: The map of outputs 'outputname' -> 'output df' 55 | ''' 56 | global output_lock 57 | with output_lock: 58 | for (name, df) in iteritems(outputs): 59 | csv_file = '%s.csv' % name 60 | print(csv_file) 61 | with get_environment().get_output_stream(csv_file) as fp: 62 | if sys.version_info[0] < 3: 63 | fp.write(df.to_csv(index=False, encoding='utf8')) 64 | else: 65 | fp.write(df.to_csv(index=False).encode(encoding='utf8')) 66 | if len(outputs) == 0: 67 | print("Warning: no outputs written") 68 | 69 | 70 | def wait_and_save_all_cb(outputs): 71 | global output_lock 72 | # just wait for the output_lock to be available 73 | t = time.time() 74 | with output_lock: 75 | pass 76 | elapsed = time.time() - t 77 | # write outputs 78 | write_all_outputs(outputs) 79 | 80 | 81 | def get_line_of_model(n): 82 | env = get_environment() 83 | with env.get_input_stream('model.py') as m: 84 | lines = m.readlines() 85 | return lines[n - 1].decode("utf-8") 86 | 87 | 88 | class InterpreterError(Exception): 89 | pass 90 | 91 | if __name__ == '__main__': 92 | inputs = get_all_inputs() 93 | outputs = {} 94 | set_stop_callback(partial(wait_and_save_all_cb, outputs)) 95 | 96 | env = get_environment() 97 | # The IS_DODS env must be True for model.py if running in DODS 98 | os.environ['IS_DODS'] = 'True' 99 | # This allows docplex.mp to behave the same (publish kpis.csv and solution.json 100 | # if this script is run locally 101 | os.environ['DOCPLEX_CONTEXT'] = 'solver.auto_publish=True' 102 | with env.get_input_stream('model.py') as m: 103 | try: 104 | exec(m.read().decode('utf-8'), globals()) 105 | except SyntaxError as err: 106 | error_class = err.__class__.__name__ 107 | detail = err.args[0] 108 | line_number = err.lineno 109 | fileName = ntpath.basename(err.filename) 110 | # When the error occurs in model.py, there is no err.filename 111 | if fileName == "": 112 | fileName = "model.py" 113 | imsg = '\nFile "' + fileName + '", line %s\n' % line_number 114 | if err.text != None: 115 | imsg += err.text.rstrip() + '\n' 116 | spaces = ' ' * (err.offset - 1) if err.offset > 1 else '' 117 | imsg += spaces + "^\n" 118 | imsg += '%s: %s\n' % (error_class, detail) 119 | sys.tracebacklimit = 0 120 | raise InterpreterError(imsg) 121 | except Exception as err: 122 | error_class = err.__class__.__name__ 123 | detail = "" 124 | if(len(err.args) > 0): 125 | detail = err.args[0] 126 | cl, exc, tb = sys.exc_info() 127 | ttb = traceback.extract_tb(tb) 128 | if(len(ttb) > 1): 129 | for i in range(len(ttb)): 130 | fileName = ntpath.basename(ttb[i][0]) 131 | line = ttb[i][3] 132 | if(fileName == ""): 133 | fileName = "model.py" 134 | line = get_line_of_model(ttb[i][1]) 135 | ## need to use basename, otherwise we get the full path 136 | ttb[i] = (fileName, ttb[i][1], ttb[i][2], line) 137 | ttb = ttb[1:] 138 | s = traceback.format_list(ttb) 139 | imsg = '\n' + (''.join(s)) 140 | imsg += '%s: %s\n' % (error_class, detail) 141 | sys.tracebacklimit = 0 142 | raise InterpreterError(imsg) 143 | else: 144 | write_all_outputs(outputs) 145 | 146 | -------------------------------------------------------------------------------- /mp-sample/main.py: -------------------------------------------------------------------------------- 1 | from functools import partial, wraps 2 | import os 3 | from os.path import splitext 4 | import time 5 | import threading 6 | import traceback 7 | import sys 8 | import ntpath 9 | 10 | import pandas 11 | from six import iteritems 12 | 13 | from docplex.util.environment import get_environment 14 | 15 | output_lock = threading.Lock() 16 | 17 | 18 | def set_stop_callback(cb): 19 | env = get_environment() 20 | env.abort_callbacks += [cb] 21 | 22 | 23 | def get_all_inputs(): 24 | '''Utility method to read a list of files and return a tuple with all 25 | read data frames. 26 | Returns: 27 | a map { datasetname: data frame } 28 | ''' 29 | result = {} 30 | env = get_environment() 31 | for iname in [f for f in os.listdir('.') if splitext(f)[1] == '.csv']: 32 | with env.get_input_stream(iname) as in_stream: 33 | df = pandas.read_csv(in_stream) 34 | datasetname, _ = splitext(iname) 35 | result[datasetname] = df 36 | return result 37 | 38 | 39 | def callonce(f): 40 | @wraps(f) 41 | def wrapper(*args, **kwargs): 42 | if not wrapper.called: 43 | wrapper.called = True 44 | return f(*args, **kwargs) 45 | wrapper.called = False 46 | return wrapper 47 | 48 | 49 | @callonce 50 | def write_all_outputs(outputs): 51 | '''Write all dataframes in ``outputs`` as .csv. 52 | 53 | Args: 54 | outputs: The map of outputs 'outputname' -> 'output df' 55 | ''' 56 | global output_lock 57 | with output_lock: 58 | for (name, df) in iteritems(outputs): 59 | csv_file = '%s.csv' % name 60 | print(csv_file) 61 | with get_environment().get_output_stream(csv_file) as fp: 62 | if sys.version_info[0] < 3: 63 | fp.write(df.to_csv(index=False, encoding='utf8')) 64 | else: 65 | fp.write(df.to_csv(index=False).encode(encoding='utf8')) 66 | if len(outputs) == 0: 67 | print("Warning: no outputs written") 68 | 69 | 70 | def wait_and_save_all_cb(outputs): 71 | global output_lock 72 | # just wait for the output_lock to be available 73 | t = time.time() 74 | with output_lock: 75 | pass 76 | elapsed = time.time() - t 77 | # write outputs 78 | write_all_outputs(outputs) 79 | 80 | 81 | def get_line_of_model(n): 82 | env = get_environment() 83 | with env.get_input_stream('model.py') as m: 84 | lines = m.readlines() 85 | return lines[n - 1].decode("utf-8") 86 | 87 | 88 | class InterpreterError(Exception): 89 | pass 90 | 91 | if __name__ == '__main__': 92 | inputs = get_all_inputs() 93 | outputs = {} 94 | set_stop_callback(partial(wait_and_save_all_cb, outputs)) 95 | 96 | env = get_environment() 97 | # The IS_DODS env must be True for model.py if running in DODS 98 | os.environ['IS_DODS'] = 'True' 99 | # This allows docplex.mp to behave the same (publish kpis.csv and solution.json 100 | # if this script is run locally 101 | os.environ['DOCPLEX_CONTEXT'] = 'solver.auto_publish=True' 102 | with env.get_input_stream('model.py') as m: 103 | try: 104 | exec(m.read().decode('utf-8'), globals()) 105 | except SyntaxError as err: 106 | error_class = err.__class__.__name__ 107 | detail = err.args[0] 108 | line_number = err.lineno 109 | fileName = ntpath.basename(err.filename) 110 | # When the error occurs in model.py, there is no err.filename 111 | if fileName == "": 112 | fileName = "model.py" 113 | imsg = '\nFile "' + fileName + '", line %s\n' % line_number 114 | if err.text != None: 115 | imsg += err.text.rstrip() + '\n' 116 | spaces = ' ' * (err.offset - 1) if err.offset > 1 else '' 117 | imsg += spaces + "^\n" 118 | imsg += '%s: %s\n' % (error_class, detail) 119 | sys.tracebacklimit = 0 120 | raise InterpreterError(imsg) 121 | except Exception as err: 122 | error_class = err.__class__.__name__ 123 | detail = "" 124 | if(len(err.args) > 0): 125 | detail = err.args[0] 126 | cl, exc, tb = sys.exc_info() 127 | ttb = traceback.extract_tb(tb) 128 | if(len(ttb) > 1): 129 | for i in range(len(ttb)): 130 | fileName = ntpath.basename(ttb[i][0]) 131 | line = ttb[i][3] 132 | if(fileName == ""): 133 | fileName = "model.py" 134 | line = get_line_of_model(ttb[i][1]) 135 | ## need to use basename, otherwise we get the full path 136 | ttb[i] = (fileName, ttb[i][1], ttb[i][2], line) 137 | ttb = ttb[1:] 138 | s = traceback.format_list(ttb) 139 | imsg = '\n' + (''.join(s)) 140 | imsg += '%s: %s\n' % (error_class, detail) 141 | sys.tracebacklimit = 0 142 | raise InterpreterError(imsg) 143 | else: 144 | write_all_outputs(outputs) 145 | 146 | -------------------------------------------------------------------------------- /diet-python/main.py: -------------------------------------------------------------------------------- 1 | from functools import partial, wraps 2 | import os 3 | from os.path import splitext 4 | import time 5 | import threading 6 | import traceback 7 | import sys 8 | import ntpath 9 | 10 | import pandas 11 | from six import iteritems 12 | 13 | from docplex.util.environment import get_environment 14 | 15 | output_lock = threading.Lock() 16 | 17 | 18 | def set_stop_callback(cb): 19 | env = get_environment() 20 | env.abort_callbacks += [cb] 21 | 22 | 23 | def get_all_inputs(): 24 | '''Utility method to read a list of files and return a tuple with all 25 | read data frames. 26 | Returns: 27 | a map { datasetname: data frame } 28 | ''' 29 | result = {} 30 | env = get_environment() 31 | for iname in [f for f in os.listdir('.') if splitext(f)[1] == '.csv']: 32 | with env.get_input_stream(iname) as in_stream: 33 | df = pandas.read_csv(in_stream) 34 | datasetname, _ = splitext(iname) 35 | result[datasetname] = df 36 | return result 37 | 38 | 39 | def callonce(f): 40 | @wraps(f) 41 | def wrapper(*args, **kwargs): 42 | if not wrapper.called: 43 | wrapper.called = True 44 | return f(*args, **kwargs) 45 | wrapper.called = False 46 | return wrapper 47 | 48 | 49 | @callonce 50 | def write_all_outputs(outputs): 51 | '''Write all dataframes in ``outputs`` as .csv. 52 | 53 | Args: 54 | outputs: The map of outputs 'outputname' -> 'output df' 55 | ''' 56 | global output_lock 57 | with output_lock: 58 | for (name, df) in iteritems(outputs): 59 | csv_file = '%s.csv' % name 60 | print(csv_file) 61 | with get_environment().get_output_stream(csv_file) as fp: 62 | if sys.version_info[0] < 3: 63 | fp.write(df.to_csv(index=False, encoding='utf8')) 64 | else: 65 | fp.write(df.to_csv(index=False).encode(encoding='utf8')) 66 | if len(outputs) == 0: 67 | print("Warning: no outputs written") 68 | 69 | 70 | def wait_and_save_all_cb(outputs): 71 | global output_lock 72 | # just wait for the output_lock to be available 73 | t = time.time() 74 | with output_lock: 75 | pass 76 | elapsed = time.time() - t 77 | # write outputs 78 | write_all_outputs(outputs) 79 | 80 | 81 | def get_line_of_model(n): 82 | env = get_environment() 83 | with env.get_input_stream('model.py') as m: 84 | lines = m.readlines() 85 | return lines[n - 1].decode("utf-8") 86 | 87 | 88 | class InterpreterError(Exception): 89 | pass 90 | 91 | if __name__ == '__main__': 92 | inputs = get_all_inputs() 93 | outputs = {} 94 | set_stop_callback(partial(wait_and_save_all_cb, outputs)) 95 | 96 | env = get_environment() 97 | # The IS_DODS env must be True for model.py if running in DODS 98 | os.environ['IS_DODS'] = 'True' 99 | # This allows docplex.mp to behave the same (publish kpis.csv and solution.json 100 | # if this script is run locally 101 | os.environ['DOCPLEX_CONTEXT'] = 'solver.auto_publish=True' 102 | with env.get_input_stream('model.py') as m: 103 | try: 104 | exec(m.read().decode('utf-8'), globals()) 105 | except SyntaxError as err: 106 | error_class = err.__class__.__name__ 107 | detail = err.args[0] 108 | line_number = err.lineno 109 | fileName = ntpath.basename(err.filename) 110 | # When the error occurs in model.py, there is no err.filename 111 | if fileName == "": 112 | fileName = "model.py" 113 | imsg = '\nFile "' + fileName + '", line %s\n' % line_number 114 | if err.text != None: 115 | imsg += err.text.rstrip() + '\n' 116 | spaces = ' ' * (err.offset - 1) if err.offset > 1 else '' 117 | imsg += spaces + "^\n" 118 | imsg += '%s: %s\n' % (error_class, detail) 119 | sys.tracebacklimit = 0 120 | raise InterpreterError(imsg) 121 | except Exception as err: 122 | error_class = err.__class__.__name__ 123 | detail = "" 124 | if(len(err.args) > 0): 125 | detail = err.args[0] 126 | cl, exc, tb = sys.exc_info() 127 | ttb = traceback.extract_tb(tb) 128 | if(len(ttb) > 1): 129 | for i in range(len(ttb)): 130 | fileName = ntpath.basename(ttb[i][0]) 131 | line = ttb[i][3] 132 | if(fileName == ""): 133 | fileName = "model.py" 134 | line = get_line_of_model(ttb[i][1]) 135 | ## need to use basename, otherwise we get the full path 136 | ttb[i] = (fileName, ttb[i][1], ttb[i][2], line) 137 | ttb = ttb[1:] 138 | s = traceback.format_list(ttb) 139 | imsg = '\n' + (''.join(s)) 140 | imsg += '%s: %s\n' % (error_class, detail) 141 | sys.tracebacklimit = 0 142 | raise InterpreterError(imsg) 143 | else: 144 | write_all_outputs(outputs) 145 | 146 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | APPENDIX: How to apply the Apache License to your work. 179 | 180 | To apply the Apache License to your work, attach the following 181 | boilerplate notice, with the fields enclosed by brackets "[]" 182 | replaced with your own identifying information. (Don't include 183 | the brackets!) The text should be enclosed in the appropriate 184 | comment syntax for the file format. We also recommend that a 185 | file or class name and description of purpose be included on the 186 | same "printed page" as the copyright notice for easier 187 | identification within third-party archives. 188 | 189 | Copyright [yyyy] [name of copyright owner] 190 | 191 | Licensed under the Apache License, Version 2.0 (the "License"); 192 | you may not use this file except in compliance with the License. 193 | You may obtain a copy of the License at 194 | 195 | http://www.apache.org/licenses/LICENSE-2.0 196 | 197 | Unless required by applicable law or agreed to in writing, software 198 | distributed under the License is distributed on an "AS IS" BASIS, 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 200 | See the License for the specific language governing permissions and 201 | limitations under the License. 202 | -------------------------------------------------------------------------------- /house-building/house-building3.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "markdown", 5 | "metadata": {}, 6 | "source": [ 7 | "# House Building 3" 8 | ] 9 | }, 10 | { 11 | "cell_type": "markdown", 12 | "metadata": {}, 13 | "source": [ 14 | "## より複雑な問題" 15 | ] 16 | }, 17 | { 18 | "cell_type": "markdown", 19 | "metadata": {}, 20 | "source": [ 21 | "* 5件の家を、JoeとJimの2人で建てる \n", 22 | "* 工程ごとに担当者が決まっている\n", 23 | "* 家ごとにReleaseDate以降に着手する必要がある\n", 24 | "* 家ごとにDueDateが決まっている。遅れてもいいが、Weightで規定されるコストが余分にかかる\n", 25 | "* 評価関数は「遅延によるコスト」+「家ごとの建築期間」でこれを最小化する" 26 | ] 27 | }, 28 | { 29 | "cell_type": "markdown", 30 | "metadata": {}, 31 | "source": [ 32 | "元リンク\n", 33 | "\n", 34 | "https://ibmdecisionoptimization.github.io/tutorials/html/Scheduling_Tutorial.html" 35 | ] 36 | }, 37 | { 38 | "cell_type": "markdown", 39 | "metadata": {}, 40 | "source": [ 41 | "API reference\n", 42 | "\n", 43 | "https://ibmdecisionoptimization.github.io/docplex-doc/cp/docplex.cp.expression.py.html" 44 | ] 45 | }, 46 | { 47 | "cell_type": "code", 48 | "execution_count": 1, 49 | "metadata": {}, 50 | "outputs": [], 51 | "source": [ 52 | "# 工程名\n", 53 | "TaskNames = [\"masonry\", \"carpentry\", \"plumbing\", \n", 54 | " \"ceiling\", \"roofing\", \"painting\", \n", 55 | " \"windows\", \"facade\", \"garden\", \"moving\"]\n", 56 | "\n", 57 | "# 工程毎の期間\n", 58 | "Duration = [35, 15, 40, 15, 5, 10, 5, 10, 5, 5]\n", 59 | "\n", 60 | "# 工程間依存関係\n", 61 | "Precedences = [(\"masonry\", \"carpentry\"),(\"masonry\", \"plumbing\"),\n", 62 | " (\"masonry\", \"ceiling\"), (\"carpentry\", \"roofing\"),\n", 63 | " (\"ceiling\", \"painting\"), (\"roofing\", \"windows\"), \n", 64 | " (\"roofing\", \"facade\"), (\"plumbing\", \"facade\"),\n", 65 | " (\"roofing\", \"garden\"), (\"plumbing\", \"garden\"),\n", 66 | " (\"windows\", \"moving\"), (\"facade\", \"moving\"), \n", 67 | " (\"garden\", \"moving\"), (\"painting\", \"moving\")]\n", 68 | "\n", 69 | "# 家の件数\n", 70 | "NbHouses = 5\n", 71 | "\n", 72 | "# 作業者\n", 73 | "WorkerNames = [\"Joe\", \"Jim\"]\n", 74 | "\n", 75 | "# 工程毎の作業者分担\n", 76 | "Worker = {\"masonry\" : \"Joe\" , \n", 77 | " \"carpentry\": \"Joe\" , \n", 78 | " \"plumbing\" : \"Jim\" , \n", 79 | " \"ceiling\" : \"Jim\" , \n", 80 | " \"roofing\" : \"Joe\" , \n", 81 | " \"painting\" : \"Jim\" , \n", 82 | " \"windows\" : \"Jim\" , \n", 83 | " \"facade\" : \"Joe\" , \n", 84 | " \"garden\" : \"Joe\" , \n", 85 | " \"moving\" : \"Jim\"}\n", 86 | "\n", 87 | "# 着手日 (必ず守る必要あり)\n", 88 | "ReleaseDate = [ 0, 0, 151, 59, 243]\n", 89 | "\n", 90 | "# 終了日 (遅れていいがコストが発生)\n", 91 | "DueDate = [120, 212, 304, 181, 425]\n", 92 | "\n", 93 | "# 遅延コスト\n", 94 | "Weight = [100.0, 100.0, 100.0, 200.0, 100.0]\n", 95 | "\n", 96 | "# 家IDのリスト([0, 1, 2, 3, 4])を生成\n", 97 | "Houses = range(NbHouses)" 98 | ] 99 | }, 100 | { 101 | "cell_type": "code", 102 | "execution_count": 2, 103 | "metadata": {}, 104 | "outputs": [], 105 | "source": [ 106 | "# ライブラリのインポートとモデルインスタンスの生成\n", 107 | "\n", 108 | "import sys\n", 109 | "from docplex.cp.model import *\n", 110 | "mdl2 = CpoModel()" 111 | ] 112 | }, 113 | { 114 | "cell_type": "code", 115 | "execution_count": 3, 116 | "metadata": {}, 117 | "outputs": [], 118 | "source": [ 119 | "# housesは家毎のInterval変数の配列\n", 120 | "# 開始日はReleaseDateにより個別に指定\n", 121 | "\n", 122 | "houses = [mdl2.interval_var(start=(ReleaseDate[i], INTERVAL_MAX), name=\"house\"+str(i)) for i in Houses]" 123 | ] 124 | }, 125 | { 126 | "cell_type": "code", 127 | "execution_count": 4, 128 | "metadata": {}, 129 | "outputs": [], 130 | "source": [ 131 | "# _<タスク名>でタスクごとのラベルを振る\n", 132 | "# TaskNames_ids: タスク毎のラベルからタスクIDを取得する辞書\n", 133 | "# itvs: (house_id, task_id)から該当するInterval変数を取得する辞書\n", 134 | "# タスク毎の期間(size)はDurationで決められている値を初期設定する\n", 135 | "\n", 136 | "TaskNames_ids = {}\n", 137 | "itvs = {}\n", 138 | "for h in Houses:\n", 139 | " for i,t in enumerate(TaskNames):\n", 140 | " _name = str(h)+\"_\"+str(t)\n", 141 | " itvs[(h,t)] = mdl2.interval_var(size=Duration[i], name=_name)\n", 142 | " TaskNames_ids[_name] = i" 143 | ] 144 | }, 145 | { 146 | "cell_type": "code", 147 | "execution_count": 5, 148 | "metadata": {}, 149 | "outputs": [ 150 | { 151 | "name": "stdout", 152 | "output_type": "stream", 153 | "text": [ 154 | "\"3_masonry\" = intervalVar(size=35)\n" 155 | ] 156 | } 157 | ], 158 | "source": [ 159 | "# itvsの辞書の内容確認\n", 160 | "\n", 161 | "print(itvs[(3, 'masonry')])" 162 | ] 163 | }, 164 | { 165 | "cell_type": "code", 166 | "execution_count": 6, 167 | "metadata": {}, 168 | "outputs": [], 169 | "source": [ 170 | "# タスク間の依存関係定義\n", 171 | "\n", 172 | "for h in Houses:\n", 173 | " for p in Precedences:\n", 174 | " mdl2.add(mdl2.end_before_start(itvs[(h,p[0])], itvs[(h,p[1])]) )" 175 | ] 176 | }, 177 | { 178 | "cell_type": "code", 179 | "execution_count": 7, 180 | "metadata": {}, 181 | "outputs": [], 182 | "source": [ 183 | "# span制約の設定\n", 184 | "\n", 185 | "# 家のInterval変数は、個別タスクの集計で定まる。その関係をspan関数で設定\n", 186 | "for h in Houses:\n", 187 | " mdl2.add( mdl2.span(houses[h], [itvs[(h,t)] for t in TaskNames] ) )" 188 | ] 189 | }, 190 | { 191 | "cell_type": "code", 192 | "execution_count": 8, 193 | "metadata": {}, 194 | "outputs": [], 195 | "source": [ 196 | "# workersの定義\n", 197 | "\n", 198 | "# 作業者(Joe, Jim)ごとのタスクシーケンス変数として定義\n", 199 | "# シーケンス変数は、次のno_overlap関数で条件の定義として用いられる\n", 200 | "workers = {w : mdl2.sequence_var([ itvs[(h,t)] for h in Houses for t in TaskNames if Worker[t]==w ], \n", 201 | " name=\"workers_\"+w) for w in WorkerNames}" 202 | ] 203 | }, 204 | { 205 | "cell_type": "code", 206 | "execution_count": 9, 207 | "metadata": {}, 208 | "outputs": [ 209 | { 210 | "name": "stdout", 211 | "output_type": "stream", 212 | "text": [ 213 | "workers_Jim = sequenceVar([\"0_plumbing\", \"0_ceiling\", \"0_painting\", \"0_windows\", \"0_moving\", \"1_plumbing\", \"1_ceiling\", \"1_painting\", \"1_windows\", \"1_moving\", \"2_plumbing\", \"2_ceiling\", \"2_painting\", \"2_windows\", \"2_moving\", \"3_plumbing\", \"3_ceiling\", \"3_painting\", \"3_windows\", \"3_moving\", \"4_plumbing\", \"4_ceiling\", \"4_painting\", \"4_windows\", \"4_moving\"])\n" 214 | ] 215 | } 216 | ], 217 | "source": [ 218 | "# 結果の確認('Jim'に対する結果)\n", 219 | "\n", 220 | "print(workers['Jim'])" 221 | ] 222 | }, 223 | { 224 | "cell_type": "code", 225 | "execution_count": 10, 226 | "metadata": {}, 227 | "outputs": [], 228 | "source": [ 229 | "# 作業者は同時に一つのタスクしかできないことをno_overlap関数で表現する\n", 230 | "# (オリジナルのチュートリアルにあった遷移時間の条件は簡略化のため削除)\n", 231 | "\n", 232 | "for w in WorkerNames:\n", 233 | " mdl2.add( mdl2.no_overlap(workers[w]) )" 234 | ] 235 | }, 236 | { 237 | "cell_type": "code", 238 | "execution_count": 11, 239 | "metadata": {}, 240 | "outputs": [], 241 | "source": [ 242 | "# 目的関数の定義\n", 243 | "# 「遅延コスト」+「建築期間」を家ごとに集計した値を最小化する\n", 244 | "\n", 245 | "mdl2.add( \n", 246 | " mdl2.minimize( \n", 247 | " mdl2.sum(Weight[h] * mdl2.max([0, mdl2.end_of(houses[h])-DueDate[h]]) +\\\n", 248 | " mdl2.length_of(houses[h]) for h in Houses) \n", 249 | " ) \n", 250 | ")" 251 | ] 252 | }, 253 | { 254 | "cell_type": "code", 255 | "execution_count": 12, 256 | "metadata": {}, 257 | "outputs": [ 258 | { 259 | "name": "stdout", 260 | "output_type": "stream", 261 | "text": [ 262 | "\n", 263 | "Solving model....\n", 264 | "done\n" 265 | ] 266 | } 267 | ], 268 | "source": [ 269 | "# モデルを解く\n", 270 | "\n", 271 | "print(\"\\nSolving model....\")\n", 272 | "msol2 = mdl2.solve(FailLimit=30000)\n", 273 | "print(\"done\")" 274 | ] 275 | }, 276 | { 277 | "cell_type": "code", 278 | "execution_count": 13, 279 | "metadata": {}, 280 | "outputs": [ 281 | { 282 | "name": "stdout", 283 | "output_type": "stream", 284 | "text": [ 285 | "Cost will be 9820\n" 286 | ] 287 | } 288 | ], 289 | "source": [ 290 | "# 目的関数値の確認\n", 291 | "\n", 292 | "print(\"Cost will be \" + str(msol2.get_objective_values()[0]))" 293 | ] 294 | }, 295 | { 296 | "cell_type": "code", 297 | "execution_count": 14, 298 | "metadata": {}, 299 | "outputs": [ 300 | { 301 | "name": "stdout", 302 | "output_type": "stream", 303 | "text": [ 304 | "Joe 0_masonry 0 35\n", 305 | "Joe 0_carpentry 35 50\n", 306 | "Joe 0_roofing 50 55\n", 307 | "Joe 3_masonry 60 95\n", 308 | "Joe 0_facade 95 105\n", 309 | "Joe 0_garden 105 110\n", 310 | "Joe 3_carpentry 110 125\n", 311 | "Joe 3_roofing 125 130\n", 312 | "Joe 1_masonry 130 165\n", 313 | "Joe 3_facade 165 175\n", 314 | "Joe 3_garden 175 180\n", 315 | "Joe 1_carpentry 180 195\n", 316 | "Joe 1_roofing 195 200\n", 317 | "Joe 2_masonry 205 240\n", 318 | "Joe 1_garden 240 245\n", 319 | "Joe 1_facade 245 255\n", 320 | "Joe 2_carpentry 255 270\n", 321 | "Joe 2_roofing 270 275\n", 322 | "Joe 2_facade 300 310\n", 323 | "Joe 2_garden 310 315\n", 324 | "Joe 4_masonry 315 350\n", 325 | "Joe 4_carpentry 350 365\n", 326 | "Joe 4_roofing 365 370\n", 327 | "Joe 4_facade 390 400\n", 328 | "Joe 4_garden 400 405\n", 329 | "Jim 0_plumbing 35 75\n", 330 | "Jim 0_windows 75 80\n", 331 | "Jim 0_ceiling 80 95\n", 332 | "Jim 0_painting 95 105\n", 333 | "Jim 3_ceiling 105 120\n", 334 | "Jim 0_moving 120 125\n", 335 | "Jim 3_plumbing 125 165\n", 336 | "Jim 3_painting 165 175\n", 337 | "Jim 3_windows 175 180\n", 338 | "Jim 3_moving 180 185\n", 339 | "Jim 1_ceiling 185 200\n", 340 | "Jim 1_plumbing 200 240\n", 341 | "Jim 1_painting 240 250\n", 342 | "Jim 1_windows 250 255\n", 343 | "Jim 1_moving 255 260\n", 344 | "Jim 2_plumbing 260 300\n", 345 | "Jim 2_ceiling 300 315\n", 346 | "Jim 2_painting 315 325\n", 347 | "Jim 2_windows 325 330\n", 348 | "Jim 2_moving 330 335\n", 349 | "Jim 4_plumbing 350 390\n", 350 | "Jim 4_ceiling 390 405\n", 351 | "Jim 4_painting 405 415\n", 352 | "Jim 4_windows 415 420\n", 353 | "Jim 4_moving 420 425\n" 354 | ] 355 | } 356 | ], 357 | "source": [ 358 | "# Workerごとの詳細スケジュール確認\n", 359 | "\n", 360 | "for worker in WorkerNames:\n", 361 | " w_name = 'workers_' + worker\n", 362 | " sequence = msol2.get_var_solution(w_name)\n", 363 | " intervals = sequence.get_interval_variables()\n", 364 | " for interval in intervals:\n", 365 | " name = interval.get_name()\n", 366 | " start = interval.get_start()\n", 367 | " end = interval.get_end()\n", 368 | " print(worker, name, start, end)" 369 | ] 370 | }, 371 | { 372 | "cell_type": "code", 373 | "execution_count": 15, 374 | "metadata": {}, 375 | "outputs": [], 376 | "source": [ 377 | "# visu = reload(visu)" 378 | ] 379 | }, 380 | { 381 | "cell_type": "code", 382 | "execution_count": 16, 383 | "metadata": {}, 384 | "outputs": [], 385 | "source": [ 386 | "# グラフ表示\n", 387 | "\n", 388 | "# グラフ表示の準備\n", 389 | "import docplex.cp.utils_visu as visu\n", 390 | "import matplotlib.pyplot as plt\n", 391 | "%matplotlib inline\n", 392 | "from pylab import rcParams" 393 | ] 394 | }, 395 | { 396 | "cell_type": "code", 397 | "execution_count": 17, 398 | "metadata": {}, 399 | "outputs": [ 400 | { 401 | "data": { 402 | "image/png": "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\n", 403 | "text/plain": [ 404 | "
" 405 | ] 406 | }, 407 | "metadata": { 408 | "needs_background": "light" 409 | }, 410 | "output_type": "display_data" 411 | } 412 | ], 413 | "source": [ 414 | "# 家単位の建築スケジュール\n", 415 | "\n", 416 | "rcParams['figure.figsize'] = 15, 3\n", 417 | "\n", 418 | "for h in Houses:\n", 419 | " hkey = 'house' + str(h)\n", 420 | " wt = msol2.get_var_solution(hkey)\n", 421 | " visu.interval(wt, 'lightblue', hkey)\n", 422 | "visu.show()" 423 | ] 424 | }, 425 | { 426 | "cell_type": "code", 427 | "execution_count": 18, 428 | "metadata": {}, 429 | "outputs": [], 430 | "source": [ 431 | "visu = reload(visu)" 432 | ] 433 | }, 434 | { 435 | "cell_type": "code", 436 | "execution_count": 19, 437 | "metadata": {}, 438 | "outputs": [ 439 | { 440 | "data": { 441 | "image/png": "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\n", 442 | "text/plain": [ 443 | "
" 444 | ] 445 | }, 446 | "metadata": { 447 | "needs_background": "light" 448 | }, 449 | "output_type": "display_data" 450 | } 451 | ], 452 | "source": [ 453 | "# 作業者単位の建築スケジュール\n", 454 | "# ダブルクリックで拡大表示されます\n", 455 | "\n", 456 | "rcParams['figure.figsize'] = 80, 3\n", 457 | "\n", 458 | "for worker in WorkerNames:\n", 459 | " name = 'workers_' + worker\n", 460 | " seq = msol2.get_var_solution(name)\n", 461 | " visu.sequence(name=name, intervals=seq)\n", 462 | "visu.show()" 463 | ] 464 | }, 465 | { 466 | "cell_type": "code", 467 | "execution_count": null, 468 | "metadata": {}, 469 | "outputs": [], 470 | "source": [] 471 | }, 472 | { 473 | "cell_type": "code", 474 | "execution_count": null, 475 | "metadata": {}, 476 | "outputs": [], 477 | "source": [] 478 | } 479 | ], 480 | "metadata": { 481 | "kernelspec": { 482 | "display_name": "Python 3", 483 | "language": "python", 484 | "name": "python3" 485 | }, 486 | "language_info": { 487 | "codemirror_mode": { 488 | "name": "ipython", 489 | "version": 3 490 | }, 491 | "file_extension": ".py", 492 | "mimetype": "text/x-python", 493 | "name": "python", 494 | "nbconvert_exporter": "python", 495 | "pygments_lexer": "ipython3", 496 | "version": "3.7.3" 497 | } 498 | }, 499 | "nbformat": 4, 500 | "nbformat_minor": 2 501 | } 502 | --------------------------------------------------------------------------------