├── .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 |
--------------------------------------------------------------------------------