scipplan 0.1.0a0__py2.py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- scipplan/__init__.py +5 -0
- scipplan/config.py +152 -0
- scipplan/helpers.py +30 -0
- scipplan/parse_model.py +275 -0
- scipplan/plan_model.py +257 -0
- scipplan/scipplan.py +196 -0
- scipplan/translation/constants_infection_1.txt +17 -0
- scipplan/translation/constants_navigation_1.txt +2 -0
- scipplan/translation/constants_navigation_2.txt +2 -0
- scipplan/translation/constants_navigation_3.txt +1 -0
- scipplan/translation/constants_pandemic_1.txt +17 -0
- scipplan/translation/goals_infection_1.txt +3 -0
- scipplan/translation/goals_navigation_1.txt +2 -0
- scipplan/translation/goals_navigation_2.txt +2 -0
- scipplan/translation/goals_navigation_3.txt +2 -0
- scipplan/translation/goals_pandemic_1.txt +3 -0
- scipplan/translation/initials_infection_1.txt +5 -0
- scipplan/translation/initials_navigation_1.txt +4 -0
- scipplan/translation/initials_navigation_2.txt +4 -0
- scipplan/translation/initials_navigation_3.txt +4 -0
- scipplan/translation/initials_pandemic_1.txt +5 -0
- scipplan/translation/instantaneous_constraints_infection_1.txt +12 -0
- scipplan/translation/instantaneous_constraints_navigation_1.txt +9 -0
- scipplan/translation/instantaneous_constraints_navigation_2.txt +10 -0
- scipplan/translation/instantaneous_constraints_navigation_3.txt +11 -0
- scipplan/translation/instantaneous_constraints_pandemic_1.txt +12 -0
- scipplan/translation/pvariables_infection_1.txt +12 -0
- scipplan/translation/pvariables_navigation_1.txt +8 -0
- scipplan/translation/pvariables_navigation_2.txt +8 -0
- scipplan/translation/pvariables_navigation_3.txt +8 -0
- scipplan/translation/pvariables_pandemic_1.txt +12 -0
- scipplan/translation/reward_infection_1.txt +1 -0
- scipplan/translation/reward_navigation_1.txt +1 -0
- scipplan/translation/reward_navigation_2.txt +1 -0
- scipplan/translation/reward_navigation_3.txt +1 -0
- scipplan/translation/reward_pandemic_1.txt +1 -0
- scipplan/translation/temporal_constraints_infection_1.txt +1 -0
- scipplan/translation/temporal_constraints_navigation_1.txt +6 -0
- scipplan/translation/temporal_constraints_navigation_2.txt +6 -0
- scipplan/translation/temporal_constraints_navigation_3.txt +7 -0
- scipplan/translation/temporal_constraints_pandemic_1.txt +1 -0
- scipplan/translation/transitions_infection_1.txt +7 -0
- scipplan/translation/transitions_navigation_1.txt +4 -0
- scipplan/translation/transitions_navigation_2.txt +4 -0
- scipplan/translation/transitions_navigation_3.txt +4 -0
- scipplan/translation/transitions_pandemic_1.txt +7 -0
- scipplan/variables.py +91 -0
- scipplan/zero_crossing.py +28 -0
- scipplan-0.1.0a0.dist-info/LICENSE +19 -0
- scipplan-0.1.0a0.dist-info/METADATA +215 -0
- scipplan-0.1.0a0.dist-info/RECORD +54 -0
- scipplan-0.1.0a0.dist-info/WHEEL +6 -0
- scipplan-0.1.0a0.dist-info/entry_points.txt +2 -0
- scipplan-0.1.0a0.dist-info/top_level.txt +1 -0
scipplan/plan_model.py
ADDED
@@ -0,0 +1,257 @@
|
|
1
|
+
from .config import Config
|
2
|
+
from .variables import Variable, VarType
|
3
|
+
from .parse_model import ParseModel as PM, EvalParams
|
4
|
+
from .helpers import list_accessible_files
|
5
|
+
|
6
|
+
import math
|
7
|
+
import os
|
8
|
+
|
9
|
+
from pyscipopt.scip import Model
|
10
|
+
from pkg_resources import parse_version
|
11
|
+
from importlib.metadata import version
|
12
|
+
|
13
|
+
if parse_version(version("pyscipopt")) >= parse_version("4.3.0"):
|
14
|
+
from pyscipopt import quicksum, exp, log, sqrt, sin, cos
|
15
|
+
allow_trig_funcs = True
|
16
|
+
else:
|
17
|
+
from pyscipopt import quicksum, exp, log, sqrt
|
18
|
+
allow_trig_funcs = False
|
19
|
+
|
20
|
+
class PlanModel:
|
21
|
+
def __init__(self, config: Config) -> None:
|
22
|
+
self.config: Config = config
|
23
|
+
self.var_names: set[str] = set()
|
24
|
+
|
25
|
+
self.model = Model(f"{config.domain}_{config.instance}_{config.horizon}")
|
26
|
+
|
27
|
+
# Translation -> line_num -> horizon -> aux
|
28
|
+
self.aux_vars: dict[str, list[list[list]]] = {}
|
29
|
+
|
30
|
+
self.constants = self.encode_constants()
|
31
|
+
self.variables = self.encode_pvariables()
|
32
|
+
self.translations = self.encode_constraints()
|
33
|
+
|
34
|
+
self.rewards = self.encode_reward()
|
35
|
+
|
36
|
+
# Encode bounds on Dt
|
37
|
+
for h in range(self.config.horizon):
|
38
|
+
dt_var = self.variables[(self.config.dt_var, h)].model_var
|
39
|
+
self.model.addCons(dt_var >= 0.0, f"dt_{h}_lower_bound")
|
40
|
+
self.model.addCons(dt_var <= self.config.bigM, f"dt_{h}_upper_bound")
|
41
|
+
|
42
|
+
|
43
|
+
|
44
|
+
def encode_constants(self) -> dict[str, float]:
|
45
|
+
constants = {}
|
46
|
+
translation = "constants"
|
47
|
+
config_vals = {
|
48
|
+
# Since horizon can be incremented without this value being updated, it will be removed for the time being
|
49
|
+
# "config_horizon": self.config.horizon,
|
50
|
+
"config_epsilon": self.config.epsilon,
|
51
|
+
"config_gap": self.config.gap,
|
52
|
+
"config_bigM": self.config.bigM
|
53
|
+
}
|
54
|
+
|
55
|
+
|
56
|
+
with open(self.get_file_path(translation)) as f:
|
57
|
+
for line in f.readlines():
|
58
|
+
if line.strip() == "":
|
59
|
+
continue
|
60
|
+
|
61
|
+
var, val = line.strip().split("=")
|
62
|
+
var, val = var.strip(), val.strip()
|
63
|
+
|
64
|
+
val = val if val not in config_vals else config_vals[val]
|
65
|
+
|
66
|
+
try:
|
67
|
+
val = float(val)
|
68
|
+
except ValueError:
|
69
|
+
raise ValueError("Constants can only be floats, please reconfigure: ")
|
70
|
+
|
71
|
+
constants[var] = val
|
72
|
+
self.var_names.add(var)
|
73
|
+
|
74
|
+
constants["bigM"] = self.config.bigM
|
75
|
+
self.var_names.add("bigM")
|
76
|
+
return constants
|
77
|
+
|
78
|
+
|
79
|
+
def encode_pvariables(self) -> dict[tuple[str, int], Variable]:
|
80
|
+
variables: dict[tuple[str, int], Variable] = {}
|
81
|
+
for t in range(self.config.horizon):
|
82
|
+
for constant, val in self.constants.items():
|
83
|
+
variables[(constant, t)] = Variable.create_var(self.model, constant, "constant", t, self.constants)
|
84
|
+
var_type = variables[(constant, t)].var_type
|
85
|
+
|
86
|
+
translation = "pvariables"
|
87
|
+
with open(self.get_file_path(translation)) as f:
|
88
|
+
for line in f.readlines():
|
89
|
+
|
90
|
+
var = line.rstrip("\n").strip()
|
91
|
+
if var == "":
|
92
|
+
continue
|
93
|
+
vtype, name = var.split(": ")
|
94
|
+
vtype, name = vtype.strip(), name.strip()
|
95
|
+
|
96
|
+
self.var_names.add(name)
|
97
|
+
|
98
|
+
for t in range(self.config.horizon):
|
99
|
+
variables[(name, t)] = Variable.create_var(self.model, name, vtype, t, self.constants)
|
100
|
+
var_type = variables[(name, t)].var_type
|
101
|
+
if var_type is VarType.STATE:
|
102
|
+
variables[(name, self.config.horizon)] = Variable.create_var(self.model, name, vtype, self.config.horizon, self.constants)
|
103
|
+
|
104
|
+
return variables
|
105
|
+
|
106
|
+
|
107
|
+
|
108
|
+
def encode_constraints(self) -> dict[str, list[str]]:
|
109
|
+
translation_names = [
|
110
|
+
"initials",
|
111
|
+
"instantaneous_constraints",
|
112
|
+
"temporal_constraints",
|
113
|
+
"goals",
|
114
|
+
"transitions"
|
115
|
+
]
|
116
|
+
translations: dict[str, list[str]] = {}
|
117
|
+
for translation in translation_names:
|
118
|
+
translations[translation] = []
|
119
|
+
|
120
|
+
with open(self.get_file_path(translation)) as f:
|
121
|
+
for line in f.readlines():
|
122
|
+
expr = line.rstrip("\n").strip()
|
123
|
+
# If line is empty don't append
|
124
|
+
if expr == "":
|
125
|
+
continue
|
126
|
+
|
127
|
+
translations[translation].append(expr)
|
128
|
+
|
129
|
+
# Encode constraints into model
|
130
|
+
for cons_idx, (translation, constraints) in enumerate(translations.items()):
|
131
|
+
for idx, constraint in enumerate(constraints):
|
132
|
+
if translation == "initials":
|
133
|
+
exprs = PM(self.get_parser_params(horizon=0, add_aux_vars=True)).evaluate(constraint, horizon=0, expr_name=f"{translation}_{idx}_0")
|
134
|
+
|
135
|
+
if self.aux_vars.get(translation) is None: self.aux_vars[translation] = [None] * len(constraints)
|
136
|
+
if self.aux_vars[translation][idx] is None: self.aux_vars[translation][idx] = [None] * self.config.horizon
|
137
|
+
self.aux_vars[translation][idx][0] = exprs.aux_vars
|
138
|
+
|
139
|
+
for eqtn_idx, eqtn in enumerate(exprs):
|
140
|
+
self.model.addCons(eqtn, f"{translation}_{idx}_{eqtn_idx}")
|
141
|
+
|
142
|
+
elif translation == "goals":
|
143
|
+
# horizon - 1 is because the final action time is horizon - 1
|
144
|
+
exprs = PM(self.get_parser_params(horizon=self.config.horizon - 1, is_goal=True, add_aux_vars=True)).evaluate(constraint, horizon=self.config.horizon - 1, expr_name=f"{translation}_{idx}_{self.config.horizon - 1}")
|
145
|
+
|
146
|
+
if self.aux_vars.get(translation) is None: self.aux_vars[translation] = [None] * len(constraints)
|
147
|
+
if self.aux_vars[translation][idx] is None: self.aux_vars[translation][idx] = [None] * self.config.horizon
|
148
|
+
self.aux_vars[translation][idx][self.config.horizon - 1] = exprs.aux_vars
|
149
|
+
|
150
|
+
for eqtn_idx, eqtn in enumerate(exprs):
|
151
|
+
self.model.addCons(eqtn, f"{translation}_{idx}_{eqtn_idx}")
|
152
|
+
else:
|
153
|
+
for t in range(self.config.horizon):
|
154
|
+
exprs = PM(self.get_parser_params(horizon=t, add_aux_vars=True)).evaluate(constraint, horizon=t, expr_name=f"{translation}_{idx}_{t}")
|
155
|
+
|
156
|
+
if self.aux_vars.get(translation) is None: self.aux_vars[translation] = [None] * len(constraints)
|
157
|
+
if self.aux_vars[translation][idx] is None: self.aux_vars[translation][idx] = [None] * self.config.horizon
|
158
|
+
self.aux_vars[translation][idx][t] = exprs.aux_vars
|
159
|
+
|
160
|
+
for eqtn_idx, eqtn in enumerate(exprs):
|
161
|
+
self.model.addCons(eqtn, f"{translation}_{idx}_{eqtn_idx}")
|
162
|
+
|
163
|
+
return translations
|
164
|
+
|
165
|
+
def encode_reward(self):
|
166
|
+
objectives = [None] * self.config.horizon
|
167
|
+
translation = "reward"
|
168
|
+
with open(self.get_file_path(translation)) as f:
|
169
|
+
reward = f.readline().rstrip("\n")
|
170
|
+
for t in range(self.config.horizon):
|
171
|
+
objectives[t] = self.model.addVar(f"Obj_{t}", vtype="C", lb=None, ub=None)
|
172
|
+
# For the sake of similarity the reward is similar to constraint parsing, however, only one reward function is allowed
|
173
|
+
exprs = PM(self.get_parser_params(t)).evaluate(reward)
|
174
|
+
for expr_idx, expr in enumerate(exprs):
|
175
|
+
self.model.addCons(objectives[t] == expr, f"Obj_{t}_{expr_idx}")
|
176
|
+
|
177
|
+
self.model.setObjective(quicksum(objectives), "maximize")
|
178
|
+
|
179
|
+
return objectives
|
180
|
+
|
181
|
+
|
182
|
+
|
183
|
+
def get_parser_params(self, horizon: int, is_goal: bool = False, add_aux_vars: bool = False) -> EvalParams:
|
184
|
+
functions = {
|
185
|
+
"exp": exp,
|
186
|
+
"log": log,
|
187
|
+
"sqrt": sqrt,
|
188
|
+
}
|
189
|
+
if allow_trig_funcs:
|
190
|
+
functions["sin"] = sin
|
191
|
+
functions["cos"] = cos
|
192
|
+
|
193
|
+
variables = {}
|
194
|
+
operators = {}
|
195
|
+
if is_goal:
|
196
|
+
for name in self.var_names:
|
197
|
+
var = self.variables[(name, horizon)]
|
198
|
+
if var.var_type is VarType.STATE:
|
199
|
+
var = self.variables[(name, horizon + 1)]
|
200
|
+
variables[var.name] = var.model_var
|
201
|
+
else:
|
202
|
+
for name in self.var_names:
|
203
|
+
var = self.variables[(name, horizon)]
|
204
|
+
variables[var.name] = var.model_var
|
205
|
+
if var.var_type is VarType.STATE:
|
206
|
+
var = self.variables[(name, horizon + 1)]
|
207
|
+
variables[f"{var.name}_dash"] = var.model_var
|
208
|
+
|
209
|
+
return EvalParams.as_parser(variables, functions, operators, self.model, add_aux_vars)
|
210
|
+
|
211
|
+
|
212
|
+
def get_calc_params(self, horizon, dt) -> EvalParams:
|
213
|
+
functions = {
|
214
|
+
"exp": math.exp,
|
215
|
+
"log": math.log,
|
216
|
+
"sqrt": math.sqrt,
|
217
|
+
}
|
218
|
+
if allow_trig_funcs:
|
219
|
+
functions["sin"] = math.sin
|
220
|
+
functions["cos"] = math.cos
|
221
|
+
|
222
|
+
variables = {}
|
223
|
+
operators = {}
|
224
|
+
for name in self.var_names:
|
225
|
+
var = self.variables[(name, horizon)]
|
226
|
+
if var.var_type is VarType.CONSTANT:
|
227
|
+
variables[var.name] = var.model_var
|
228
|
+
else:
|
229
|
+
variables[var.name] = self.model.getVal(var.model_var)
|
230
|
+
|
231
|
+
if var.var_type is VarType.STATE:
|
232
|
+
var = self.variables[(name, horizon + 1)]
|
233
|
+
variables[f"{var.name}_dash"] = self.model.getVal(var.model_var)
|
234
|
+
|
235
|
+
variables[self.config.dt_var] = dt
|
236
|
+
|
237
|
+
|
238
|
+
return EvalParams.as_calculator(variables, functions, operators, self.model)
|
239
|
+
|
240
|
+
|
241
|
+
def get_file_path(self, translation: str) -> str:
|
242
|
+
path = f"{translation}_{self.config.domain}_{self.config.instance}.txt"
|
243
|
+
|
244
|
+
usr_files_path = os.path.join("./", "translation")
|
245
|
+
usr_files = list_accessible_files(usr_files_path)
|
246
|
+
|
247
|
+
pkg_files_path = os.path.join(os.path.dirname(__file__), "translation")
|
248
|
+
pkg_files = list_accessible_files(pkg_files_path)
|
249
|
+
|
250
|
+
|
251
|
+
if path in usr_files:
|
252
|
+
return os.path.join(usr_files_path, path)
|
253
|
+
elif path in pkg_files:
|
254
|
+
return os.path.join(pkg_files_path, path)
|
255
|
+
else:
|
256
|
+
raise Exception("Unkown file name, please enter a configuration for a valid domain instance in translation: ")
|
257
|
+
|
scipplan/scipplan.py
ADDED
@@ -0,0 +1,196 @@
|
|
1
|
+
from __future__ import annotations
|
2
|
+
|
3
|
+
import time
|
4
|
+
|
5
|
+
from .config import Config
|
6
|
+
from .plan_model import PlanModel
|
7
|
+
from .variables import VarType
|
8
|
+
from .zero_crossing import ZeroCrossing
|
9
|
+
from .parse_model import ParseModel as PM
|
10
|
+
from .helpers import InfeasibilityError, iterate, write_to_csv
|
11
|
+
|
12
|
+
from importlib.metadata import version
|
13
|
+
from pyscipopt.scip import Model
|
14
|
+
|
15
|
+
class SCIPPlan:
|
16
|
+
"""
|
17
|
+
SCIPPlan is a planner which optimises mixed integer non-linear programming problems over hybrid domains
|
18
|
+
|
19
|
+
In order to use SCIPPlan you must pass in as input a Config object which contains the configuration of the problem.
|
20
|
+
Then you may either use the optimize or solve methods.
|
21
|
+
|
22
|
+
The optimize method attempts to optimise the problem for the provided horizon.
|
23
|
+
If there are no feasible solutions, then the optimize method will raise InfeasibilityError.
|
24
|
+
|
25
|
+
The solve method attempts to solve the problem starting with the provided horizon.
|
26
|
+
If there are no feasible solutions for the current horizon then the configs horizon will be incremented.
|
27
|
+
After which the solve method will attempt to optimize the problem again.
|
28
|
+
"""
|
29
|
+
|
30
|
+
def __init__(self, config: Config) -> None:
|
31
|
+
self.config = config
|
32
|
+
self.plan = PlanModel(self.config)
|
33
|
+
self.scip_model = self.plan.model
|
34
|
+
|
35
|
+
self.scip_model.setRealParam("limits/gap", self.config.gap)
|
36
|
+
|
37
|
+
if config.show_output is False:
|
38
|
+
self.scip_model.hideOutput()
|
39
|
+
|
40
|
+
self.results_table = []
|
41
|
+
self.new_constraints = []
|
42
|
+
|
43
|
+
self.state_actions = []
|
44
|
+
|
45
|
+
|
46
|
+
def optimize(self):
|
47
|
+
iteration = 0
|
48
|
+
|
49
|
+
while True:
|
50
|
+
self.scip_model.optimize()
|
51
|
+
|
52
|
+
if len(self.scip_model.getSols()) == 0:
|
53
|
+
raise InfeasibilityError
|
54
|
+
|
55
|
+
zero_cross = self.check_violated_constraints(iteration)
|
56
|
+
|
57
|
+
self.save_values(iteration)
|
58
|
+
|
59
|
+
if zero_cross.is_violated is False:
|
60
|
+
return None
|
61
|
+
|
62
|
+
self.new_constraints.append({
|
63
|
+
"interval_start": zero_cross.start,
|
64
|
+
"interval_end": zero_cross.end,
|
65
|
+
"dt_interval": zero_cross.dt_interval,
|
66
|
+
"zero_crossing_coefficient": zero_cross.coef,
|
67
|
+
"new_dt_val": zero_cross.new_dt_val,
|
68
|
+
"horizon": zero_cross.horizon,
|
69
|
+
"iteration": zero_cross.iteration
|
70
|
+
})
|
71
|
+
|
72
|
+
if self.config.show_output is True:
|
73
|
+
print("\n\n")
|
74
|
+
print("New Constraints: \n")
|
75
|
+
for new_constraint in self.new_constraints:
|
76
|
+
print(new_constraint, end="\n\n")
|
77
|
+
print("\n\n")
|
78
|
+
|
79
|
+
self.scip_model.freeTransform()
|
80
|
+
|
81
|
+
for idx, constraint in enumerate(self.plan.translations["temporal_constraints"]):
|
82
|
+
t = zero_cross.horizon
|
83
|
+
aux_vars = self.plan.aux_vars["temporal_constraints"][idx][t]
|
84
|
+
params = self.plan.get_parser_params(horizon=t)
|
85
|
+
params.variables[self.config.dt_var] *= zero_cross.coef
|
86
|
+
exprs = PM(params).evaluate(constraint, aux_vars=aux_vars)
|
87
|
+
for eqtn_idx, eqtn in enumerate(exprs):
|
88
|
+
self.plan.model.addCons(eqtn, f"{constraint}_{idx}_{eqtn_idx}")
|
89
|
+
|
90
|
+
iteration += 1
|
91
|
+
|
92
|
+
|
93
|
+
def check_violated_constraints(self, iteration: int) -> ZeroCrossing:
|
94
|
+
is_violated = False
|
95
|
+
cross_interval = [-1.0 * self.config.epsilon, -1.0 * self.config.epsilon]
|
96
|
+
|
97
|
+
for h in range(self.config.horizon):
|
98
|
+
dt = self.scip_model.getVal(self.plan.variables[(self.config.dt_var, h)].model_var)
|
99
|
+
|
100
|
+
for constraint in self.plan.translations["temporal_constraints"]:
|
101
|
+
is_violated = False
|
102
|
+
|
103
|
+
for time in iterate(0, dt, self.config.epsilon):
|
104
|
+
pm = PM(self.plan.get_calc_params(horizon=h, dt=time))
|
105
|
+
exprs = pm.evaluate(constraint)
|
106
|
+
|
107
|
+
for eqtn_idx, constraint_eval in enumerate(exprs):
|
108
|
+
if constraint_eval is False:
|
109
|
+
if not is_violated:
|
110
|
+
# Set interval start when first part of zero crossing is found
|
111
|
+
is_violated = True
|
112
|
+
cross_interval[0] = time
|
113
|
+
|
114
|
+
# Keep updating end point until end of zero crossing or end of dt interval
|
115
|
+
cross_interval[1] = time
|
116
|
+
|
117
|
+
if is_violated and (constraint_eval is True or time + self.config.epsilon > dt):
|
118
|
+
return ZeroCrossing(
|
119
|
+
is_violated=True,
|
120
|
+
horizon=h,
|
121
|
+
iteration=iteration,
|
122
|
+
start=cross_interval[0],
|
123
|
+
end=cross_interval[1],
|
124
|
+
dt_interval=dt,
|
125
|
+
)
|
126
|
+
|
127
|
+
return ZeroCrossing(is_violated=False)
|
128
|
+
|
129
|
+
|
130
|
+
|
131
|
+
@classmethod
|
132
|
+
def solve(cls, config: Config) -> tuple[SCIPPlan, float]:
|
133
|
+
while True:
|
134
|
+
model = SCIPPlan(config)
|
135
|
+
start_time = time.time()
|
136
|
+
try:
|
137
|
+
print(f"Encoding the problem over horizon h={config.horizon}.")
|
138
|
+
print("Solving the problem.")
|
139
|
+
model.optimize()
|
140
|
+
|
141
|
+
solve_time = (time.time() - start_time)
|
142
|
+
# print(f"Total Time: {solve_time: .3f} seconds")
|
143
|
+
print("Problem solved. \n")
|
144
|
+
return model, solve_time
|
145
|
+
|
146
|
+
|
147
|
+
except InfeasibilityError:
|
148
|
+
# print("Problem is infeasible for the given horizon.")
|
149
|
+
print(f"Horizon of h={model.config.horizon} is infeasible, incrementing to h={model.config.horizon + 1}")
|
150
|
+
config.increment_horizon()
|
151
|
+
if config.show_output is True:
|
152
|
+
print(f"Horizon Time: {(time.time() - start_time): .3f} seconds")
|
153
|
+
|
154
|
+
|
155
|
+
def save_values(self, iteration: int):
|
156
|
+
for (name, h), var in self.plan.variables.items():
|
157
|
+
self.results_table.append(var.to_dict() | {"iteration": iteration})
|
158
|
+
|
159
|
+
|
160
|
+
def main():
|
161
|
+
print(f"SCIP Version: {Model().version()}")
|
162
|
+
print(f"PySCIPOpt Version: {version('pyscipopt')}\n")
|
163
|
+
config = Config.get_config()
|
164
|
+
print(config)
|
165
|
+
|
166
|
+
plan, solve_time = SCIPPlan.solve(config)
|
167
|
+
|
168
|
+
if config.save_sols is True:
|
169
|
+
write_to_csv("new_constraints", plan.new_constraints, config)
|
170
|
+
write_to_csv("results", plan.results_table, config)
|
171
|
+
print("Solutions saved: \n")
|
172
|
+
|
173
|
+
|
174
|
+
print("Plan: ")
|
175
|
+
|
176
|
+
# Get action variable names
|
177
|
+
action_names = [
|
178
|
+
var_name for var_name in plan.plan.var_names
|
179
|
+
if plan.plan.variables[(var_name, 0)].var_type is VarType.ACTION
|
180
|
+
]
|
181
|
+
|
182
|
+
action_names = sorted(action_names)
|
183
|
+
|
184
|
+
for step in range(plan.config.horizon):
|
185
|
+
for action_name in action_names:
|
186
|
+
if action_name == config.dt_var:
|
187
|
+
continue
|
188
|
+
print(f"{action_name} at step {step} by value {plan.scip_model.getVal(plan.plan.variables[(action_name, step)].model_var):.3f}")
|
189
|
+
|
190
|
+
print(f"Dt at step {step} by value {plan.scip_model.getVal(plan.plan.variables[('Dt', step)].model_var):.3f} \n")
|
191
|
+
|
192
|
+
print(f"Total reward: {(plan.scip_model.getObjVal()):.3f}")
|
193
|
+
print(f"Total time: {solve_time:.3f}")
|
194
|
+
|
195
|
+
if __name__ == "__main__":
|
196
|
+
main()
|
@@ -0,0 +1 @@
|
|
1
|
+
Epsilon = config_epsilon
|
@@ -0,0 +1,10 @@
|
|
1
|
+
Location_x <= 10.0
|
2
|
+
Location_y <= 10.0
|
3
|
+
Location_x >= 0.0
|
4
|
+
Location_y >= 0.0
|
5
|
+
Accelerate_x <= 0.5
|
6
|
+
Accelerate_y <= 0.5
|
7
|
+
Accelerate_x >= -0.5
|
8
|
+
Accelerate_y >= -0.5
|
9
|
+
(Location_x <= 2.0) or (Location_x >= 4.0) or (Location_y <= 1.0) or (Location_y >= 5.0)
|
10
|
+
(Location_x <= 5.0) or (Location_x >= 7.0) or (Location_y <= 5.0) or (Location_y >= 9.0)
|
@@ -0,0 +1,11 @@
|
|
1
|
+
Location_x <= 10.0
|
2
|
+
Location_y <= 10.0
|
3
|
+
Location_x >= 0.0
|
4
|
+
Location_y >= 0.0
|
5
|
+
Accelerate_x <= 0.5
|
6
|
+
Accelerate_y <= 0.5
|
7
|
+
Accelerate_x >= -0.5
|
8
|
+
Accelerate_y >= -0.5
|
9
|
+
(Location_x <= 2.0) or (Location_x >= 4.0) or (Location_y <= 1.0) or (Location_y >= 9.0)
|
10
|
+
(Location_x <= 4.0) or (Location_x >= 7.0) or (Location_y <= 7.0) or (Location_y >= 9.0)
|
11
|
+
(Location_x <= 4.0) or (Location_x >= 9.0) or (Location_y <= 2.0) or (Location_y >= 6.0)
|
@@ -0,0 +1 @@
|
|
1
|
+
-1.0 * Lockdown * Dt
|