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.
Files changed (54) hide show
  1. scipplan/__init__.py +5 -0
  2. scipplan/config.py +152 -0
  3. scipplan/helpers.py +30 -0
  4. scipplan/parse_model.py +275 -0
  5. scipplan/plan_model.py +257 -0
  6. scipplan/scipplan.py +196 -0
  7. scipplan/translation/constants_infection_1.txt +17 -0
  8. scipplan/translation/constants_navigation_1.txt +2 -0
  9. scipplan/translation/constants_navigation_2.txt +2 -0
  10. scipplan/translation/constants_navigation_3.txt +1 -0
  11. scipplan/translation/constants_pandemic_1.txt +17 -0
  12. scipplan/translation/goals_infection_1.txt +3 -0
  13. scipplan/translation/goals_navigation_1.txt +2 -0
  14. scipplan/translation/goals_navigation_2.txt +2 -0
  15. scipplan/translation/goals_navigation_3.txt +2 -0
  16. scipplan/translation/goals_pandemic_1.txt +3 -0
  17. scipplan/translation/initials_infection_1.txt +5 -0
  18. scipplan/translation/initials_navigation_1.txt +4 -0
  19. scipplan/translation/initials_navigation_2.txt +4 -0
  20. scipplan/translation/initials_navigation_3.txt +4 -0
  21. scipplan/translation/initials_pandemic_1.txt +5 -0
  22. scipplan/translation/instantaneous_constraints_infection_1.txt +12 -0
  23. scipplan/translation/instantaneous_constraints_navigation_1.txt +9 -0
  24. scipplan/translation/instantaneous_constraints_navigation_2.txt +10 -0
  25. scipplan/translation/instantaneous_constraints_navigation_3.txt +11 -0
  26. scipplan/translation/instantaneous_constraints_pandemic_1.txt +12 -0
  27. scipplan/translation/pvariables_infection_1.txt +12 -0
  28. scipplan/translation/pvariables_navigation_1.txt +8 -0
  29. scipplan/translation/pvariables_navigation_2.txt +8 -0
  30. scipplan/translation/pvariables_navigation_3.txt +8 -0
  31. scipplan/translation/pvariables_pandemic_1.txt +12 -0
  32. scipplan/translation/reward_infection_1.txt +1 -0
  33. scipplan/translation/reward_navigation_1.txt +1 -0
  34. scipplan/translation/reward_navigation_2.txt +1 -0
  35. scipplan/translation/reward_navigation_3.txt +1 -0
  36. scipplan/translation/reward_pandemic_1.txt +1 -0
  37. scipplan/translation/temporal_constraints_infection_1.txt +1 -0
  38. scipplan/translation/temporal_constraints_navigation_1.txt +6 -0
  39. scipplan/translation/temporal_constraints_navigation_2.txt +6 -0
  40. scipplan/translation/temporal_constraints_navigation_3.txt +7 -0
  41. scipplan/translation/temporal_constraints_pandemic_1.txt +1 -0
  42. scipplan/translation/transitions_infection_1.txt +7 -0
  43. scipplan/translation/transitions_navigation_1.txt +4 -0
  44. scipplan/translation/transitions_navigation_2.txt +4 -0
  45. scipplan/translation/transitions_navigation_3.txt +4 -0
  46. scipplan/translation/transitions_pandemic_1.txt +7 -0
  47. scipplan/variables.py +91 -0
  48. scipplan/zero_crossing.py +28 -0
  49. scipplan-0.1.0a0.dist-info/LICENSE +19 -0
  50. scipplan-0.1.0a0.dist-info/METADATA +215 -0
  51. scipplan-0.1.0a0.dist-info/RECORD +54 -0
  52. scipplan-0.1.0a0.dist-info/WHEEL +6 -0
  53. scipplan-0.1.0a0.dist-info/entry_points.txt +2 -0
  54. 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,17 @@
1
+ b1 = 0.2
2
+ b2 = 0.1
3
+ c = 0.15
4
+ N = 2000.0
5
+ D = 150.0
6
+
7
+ Makespan = 180.0
8
+
9
+ Epsilon = 1.0
10
+
11
+ Duration = 100.0
12
+ DurationMin = 30.0
13
+ DurationMax = 90.0
14
+
15
+ Percentage = 0.8
16
+
17
+ InitialInfected = 50.0
@@ -0,0 +1,2 @@
1
+ Epsilon = config_epsilon
2
+ bigM = config_bigM
@@ -0,0 +1,2 @@
1
+ bigM = config_bigM
2
+ Epsilon = config_epsilon
@@ -0,0 +1 @@
1
+ Epsilon = config_epsilon
@@ -0,0 +1,17 @@
1
+ b1 = 0.2
2
+ b2 = 0.1
3
+ c = 0.15
4
+ N = 2000.0
5
+ D = 150.0
6
+
7
+ Makespan = 180.0
8
+
9
+ Epsilon = 1.0
10
+
11
+ Duration = 100.0
12
+ DurationMin = 30.0
13
+ DurationMax = 90.0
14
+
15
+ Percentage = 0.8
16
+
17
+ InitialInfected = 50.0
@@ -0,0 +1,3 @@
1
+ Removed >= N * Percentage
2
+
3
+ TotalTime == Makespan
@@ -0,0 +1,2 @@
1
+ Location_x == 8.0
2
+ Location_y == 8.0
@@ -0,0 +1,2 @@
1
+ Location_x == 8.0
2
+ Location_y == 8.0
@@ -0,0 +1,2 @@
1
+ Location_x == 8.0
2
+ Location_y == 8.0
@@ -0,0 +1,3 @@
1
+ Removed >= N * Percentage
2
+
3
+ TotalTime == Makespan
@@ -0,0 +1,5 @@
1
+ Susceptible == N - InitialInfected
2
+ Infected == InitialInfected
3
+ Removed == 0.0
4
+
5
+ TotalTime == 0.0
@@ -0,0 +1,4 @@
1
+ Location_x == 0.0
2
+ Location_y == 0.0
3
+ Speed_x == 0.0
4
+ Speed_y == 0.0
@@ -0,0 +1,4 @@
1
+ Location_x == 0.0
2
+ Location_y == 0.0
3
+ Speed_x == 0.0
4
+ Speed_y == 0.0
@@ -0,0 +1,4 @@
1
+ Location_x == 0.0
2
+ Location_y == 0.0
3
+ Speed_x == 0.0
4
+ Speed_y == 0.0
@@ -0,0 +1,5 @@
1
+ Susceptible == N - InitialInfected
2
+ Infected == InitialInfected
3
+ Removed == 0.0
4
+
5
+ TotalTime == 0.0
@@ -0,0 +1,12 @@
1
+ b - b1 + Lockdown >= 0.0
2
+ b - b1 - Lockdown <= 0.0
3
+
4
+ b - b2 + (1 - Lockdown) >= 0.0
5
+ b - b2 - (1 - Lockdown) <= 0.0
6
+
7
+
8
+ b <= b1
9
+ b >= b2
10
+
11
+ Dt >= DurationMin
12
+ Dt <= DurationMax
@@ -0,0 +1,9 @@
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 <= 4.0) or (Location_x >= 6.0) or (Location_y <= 4.0) or (Location_y >= 6.0)
@@ -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,12 @@
1
+ b - b1 + Lockdown >= 0.0
2
+ b - b1 - Lockdown <= 0.0
3
+
4
+ b - b2 + (1 - Lockdown) >= 0.0
5
+ b - b2 - (1 - Lockdown) <= 0.0
6
+
7
+
8
+ b <= b1
9
+ b >= b2
10
+
11
+ Dt >= DurationMin
12
+ Dt <= DurationMax
@@ -0,0 +1,12 @@
1
+ state_continuous: Susceptible
2
+ state_continuous: Infected
3
+ state_continuous: Removed
4
+
5
+ state_continuous: TotalTime
6
+
7
+ action_boolean: Lockdown
8
+ action_continuous: Dt
9
+
10
+ auxiliary_continuous: b
11
+
12
+
@@ -0,0 +1,8 @@
1
+ action_continuous: Accelerate_x
2
+ action_continuous: Accelerate_y
3
+ action_continuous: Dt
4
+ action_boolean: Mode
5
+ state_continuous: Location_x
6
+ state_continuous: Location_y
7
+ state_continuous: Speed_x
8
+ state_continuous: Speed_y
@@ -0,0 +1,8 @@
1
+ action_continuous: Accelerate_x
2
+ action_continuous: Accelerate_y
3
+ action_continuous: Dt
4
+ state_continuous: Location_x
5
+ state_continuous: Location_y
6
+ state_continuous: Speed_x
7
+ state_continuous: Speed_y
8
+ action_boolean: Mode
@@ -0,0 +1,8 @@
1
+ action_continuous: Accelerate_x
2
+ action_continuous: Accelerate_y
3
+ action_boolean: Mode
4
+ action_continuous: Dt
5
+ state_continuous: Location_x
6
+ state_continuous: Location_y
7
+ state_continuous: Speed_x
8
+ state_continuous: Speed_y
@@ -0,0 +1,12 @@
1
+ state_continuous: Susceptible
2
+ state_continuous: Infected
3
+ state_continuous: Removed
4
+
5
+ state_continuous: TotalTime
6
+
7
+ action_boolean: Lockdown
8
+ action_continuous: Dt
9
+
10
+ auxiliary_continuous: b
11
+
12
+
@@ -0,0 +1 @@
1
+ -1.0 * Lockdown * Dt