scipplan 0.1.0a1__tar.gz → 0.1.1a0__tar.gz

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 (44) hide show
  1. {scipplan-0.1.0a1/scipplan.egg-info → scipplan-0.1.1a0}/PKG-INFO +8 -4
  2. {scipplan-0.1.0a1 → scipplan-0.1.1a0}/README.md +6 -2
  3. {scipplan-0.1.0a1 → scipplan-0.1.1a0}/scipplan/__init__.py +2 -2
  4. {scipplan-0.1.0a1 → scipplan-0.1.1a0}/scipplan/config.py +6 -4
  5. {scipplan-0.1.0a1 → scipplan-0.1.1a0}/scipplan/parse_model.py +68 -24
  6. {scipplan-0.1.0a1 → scipplan-0.1.1a0}/scipplan/plan_model.py +11 -6
  7. {scipplan-0.1.0a1 → scipplan-0.1.1a0}/scipplan/scipplan.py +21 -8
  8. {scipplan-0.1.0a1 → scipplan-0.1.1a0/scipplan.egg-info}/PKG-INFO +8 -4
  9. {scipplan-0.1.0a1 → scipplan-0.1.1a0}/LICENSE +0 -0
  10. {scipplan-0.1.0a1 → scipplan-0.1.1a0}/MANIFEST.in +0 -0
  11. {scipplan-0.1.0a1 → scipplan-0.1.1a0}/scipplan/helpers.py +0 -0
  12. {scipplan-0.1.0a1 → scipplan-0.1.1a0}/scipplan/translation/constants_navigation_1.txt +0 -0
  13. {scipplan-0.1.0a1 → scipplan-0.1.1a0}/scipplan/translation/constants_navigation_2.txt +0 -0
  14. {scipplan-0.1.0a1 → scipplan-0.1.1a0}/scipplan/translation/constants_navigation_3.txt +0 -0
  15. {scipplan-0.1.0a1 → scipplan-0.1.1a0}/scipplan/translation/goals_navigation_1.txt +0 -0
  16. {scipplan-0.1.0a1 → scipplan-0.1.1a0}/scipplan/translation/goals_navigation_2.txt +0 -0
  17. {scipplan-0.1.0a1 → scipplan-0.1.1a0}/scipplan/translation/goals_navigation_3.txt +0 -0
  18. {scipplan-0.1.0a1 → scipplan-0.1.1a0}/scipplan/translation/initials_navigation_1.txt +0 -0
  19. {scipplan-0.1.0a1 → scipplan-0.1.1a0}/scipplan/translation/initials_navigation_2.txt +0 -0
  20. {scipplan-0.1.0a1 → scipplan-0.1.1a0}/scipplan/translation/initials_navigation_3.txt +0 -0
  21. {scipplan-0.1.0a1 → scipplan-0.1.1a0}/scipplan/translation/instantaneous_constraints_navigation_1.txt +0 -0
  22. {scipplan-0.1.0a1 → scipplan-0.1.1a0}/scipplan/translation/instantaneous_constraints_navigation_2.txt +0 -0
  23. {scipplan-0.1.0a1 → scipplan-0.1.1a0}/scipplan/translation/instantaneous_constraints_navigation_3.txt +0 -0
  24. {scipplan-0.1.0a1 → scipplan-0.1.1a0}/scipplan/translation/pvariables_navigation_1.txt +0 -0
  25. {scipplan-0.1.0a1 → scipplan-0.1.1a0}/scipplan/translation/pvariables_navigation_2.txt +0 -0
  26. {scipplan-0.1.0a1 → scipplan-0.1.1a0}/scipplan/translation/pvariables_navigation_3.txt +0 -0
  27. {scipplan-0.1.0a1 → scipplan-0.1.1a0}/scipplan/translation/reward_navigation_1.txt +0 -0
  28. {scipplan-0.1.0a1 → scipplan-0.1.1a0}/scipplan/translation/reward_navigation_2.txt +0 -0
  29. {scipplan-0.1.0a1 → scipplan-0.1.1a0}/scipplan/translation/reward_navigation_3.txt +0 -0
  30. {scipplan-0.1.0a1 → scipplan-0.1.1a0}/scipplan/translation/temporal_constraints_navigation_1.txt +0 -0
  31. {scipplan-0.1.0a1 → scipplan-0.1.1a0}/scipplan/translation/temporal_constraints_navigation_2.txt +0 -0
  32. {scipplan-0.1.0a1 → scipplan-0.1.1a0}/scipplan/translation/temporal_constraints_navigation_3.txt +0 -0
  33. {scipplan-0.1.0a1 → scipplan-0.1.1a0}/scipplan/translation/transitions_navigation_1.txt +0 -0
  34. {scipplan-0.1.0a1 → scipplan-0.1.1a0}/scipplan/translation/transitions_navigation_2.txt +0 -0
  35. {scipplan-0.1.0a1 → scipplan-0.1.1a0}/scipplan/translation/transitions_navigation_3.txt +0 -0
  36. {scipplan-0.1.0a1 → scipplan-0.1.1a0}/scipplan/variables.py +0 -0
  37. {scipplan-0.1.0a1 → scipplan-0.1.1a0}/scipplan/zero_crossing.py +0 -0
  38. {scipplan-0.1.0a1 → scipplan-0.1.1a0}/scipplan.egg-info/SOURCES.txt +0 -0
  39. {scipplan-0.1.0a1 → scipplan-0.1.1a0}/scipplan.egg-info/dependency_links.txt +0 -0
  40. {scipplan-0.1.0a1 → scipplan-0.1.1a0}/scipplan.egg-info/entry_points.txt +0 -0
  41. {scipplan-0.1.0a1 → scipplan-0.1.1a0}/scipplan.egg-info/not-zip-safe +0 -0
  42. {scipplan-0.1.0a1 → scipplan-0.1.1a0}/scipplan.egg-info/top_level.txt +0 -0
  43. {scipplan-0.1.0a1 → scipplan-0.1.1a0}/setup.cfg +0 -0
  44. {scipplan-0.1.0a1 → scipplan-0.1.1a0}/setup.py +0 -0
@@ -1,9 +1,9 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: scipplan
3
- Version: 0.1.0a1
3
+ Version: 0.1.1a0
4
4
  Summary: Metric Hybrid Factored Planning in Nonlinear Domains with Constraint Generation in Python.
5
5
  Author: Ari Gestetner, Buser Say
6
- Author-email: ages0001@student.monash.edu, buser.say@monash.edu
6
+ Author-email: ari.gestetner@monash.edu, buser.say@monash.edu
7
7
  License: MIT License
8
8
  Keywords: scip,automated planner
9
9
  Classifier: Development Status :: 3 - Alpha
@@ -121,13 +121,17 @@ state_continuous: Location_x
121
121
  state_continuous: Location_y
122
122
  state_continuous: Speed_x
123
123
  state_continuous: Speed_y
124
+ global_boolean: aux_1
124
125
  ```
125
- where the variable and value type of the variable is set in the format "{variable type}_{value type}" and the variable itself has to be in a Python compatible format (e.g. variables can't use - sign like `some-var` but can use _ like `some_var` as well as the dash sign ' cannot be used). The use of next state variables which is often written using the dash symbol will be explained further in the Transitions section.
126
+ where the variable and value type of the variable is set in the format "{variable type}_{value type}" and the variable itself has to be in a Python compatible format (e.g. variables can't use - symbol like `some-var` but can use _ like `some_var` as well as the dash sign ' cannot be used). The use of next state variables which is often written using the dash symbol will be explained further in the Transitions section.
126
127
  Additionally a variable for Dt has to be defined and has to be the same as the dt_var in the config object so if you would like to use a different variable name for Dt (e.g. dt) please also ensure you add it to the config via the `--dt-var` tag or the `dt_var` parameter.
128
+ The variable type `global` is used when you would like a non state variable to be equal over every time step over the horizon (e.g. if $v_t$ is a variable at time $t$ and $H$ is the horizon, then $v_1 = v_2 = \dots = v_{H+1}$).
129
+
127
130
  The available variable types are
128
131
  - state
129
132
  - action
130
133
  - auxiliary
134
+ - global
131
135
 
132
136
  The available value types are
133
137
  - continuos
@@ -144,7 +148,7 @@ Location_y == 0.0
144
148
  Speed_x == 0.0
145
149
  Speed_y == 0.0
146
150
  ```
147
- notice te use of teh constant value defined earlier in the constants file.
151
+ notice the use of the constant value defined earlier in the constants file.
148
152
  ### Goals
149
153
  The goals file should encode the final state values such that t=H+1, for example
150
154
  ```txt
@@ -102,13 +102,17 @@ state_continuous: Location_x
102
102
  state_continuous: Location_y
103
103
  state_continuous: Speed_x
104
104
  state_continuous: Speed_y
105
+ global_boolean: aux_1
105
106
  ```
106
- where the variable and value type of the variable is set in the format "{variable type}_{value type}" and the variable itself has to be in a Python compatible format (e.g. variables can't use - sign like `some-var` but can use _ like `some_var` as well as the dash sign ' cannot be used). The use of next state variables which is often written using the dash symbol will be explained further in the Transitions section.
107
+ where the variable and value type of the variable is set in the format "{variable type}_{value type}" and the variable itself has to be in a Python compatible format (e.g. variables can't use - symbol like `some-var` but can use _ like `some_var` as well as the dash sign ' cannot be used). The use of next state variables which is often written using the dash symbol will be explained further in the Transitions section.
107
108
  Additionally a variable for Dt has to be defined and has to be the same as the dt_var in the config object so if you would like to use a different variable name for Dt (e.g. dt) please also ensure you add it to the config via the `--dt-var` tag or the `dt_var` parameter.
109
+ The variable type `global` is used when you would like a non state variable to be equal over every time step over the horizon (e.g. if $v_t$ is a variable at time $t$ and $H$ is the horizon, then $v_1 = v_2 = \dots = v_{H+1}$).
110
+
108
111
  The available variable types are
109
112
  - state
110
113
  - action
111
114
  - auxiliary
115
+ - global
112
116
 
113
117
  The available value types are
114
118
  - continuos
@@ -125,7 +129,7 @@ Location_y == 0.0
125
129
  Speed_x == 0.0
126
130
  Speed_y == 0.0
127
131
  ```
128
- notice te use of teh constant value defined earlier in the constants file.
132
+ notice the use of the constant value defined earlier in the constants file.
129
133
  ### Goals
130
134
  The goals file should encode the final state values such that t=H+1, for example
131
135
  ```txt
@@ -1,5 +1,5 @@
1
- __version__ = "0.1.0alpha1"
1
+ __version__ = "0.1.1alpha0"
2
2
  print(f"SCIPPlan Version: {__version__}")
3
3
  __release__ = "v0.1.0"
4
4
  __author__ = "Ari Gestetner, Buser Say"
5
- __email__ = "ages0001@student.monash.edu, buser.say@monash.edu"
5
+ __email__ = "ari.gestetner@monash.edu, buser.say@monash.edu"
@@ -32,17 +32,17 @@ class Config:
32
32
  "gap": False
33
33
  }
34
34
  if self.horizon is None:
35
- print("Horizon is not provided, and is set to 1")
35
+ print("Horizon is not provided, and is set to 1. ")
36
36
  self.horizon = 1
37
37
  self._defaults["horizon"] = True
38
38
 
39
39
  if self.epsilon is None:
40
- print("Epsilon is not provided, and is set to 0.1")
40
+ print("Epsilon is not provided, and is set to 0.1. ")
41
41
  self.epsilon = 0.1
42
42
  self._defaults["epsilon"] = True
43
43
 
44
44
  if self.gap is None:
45
- print("Gap is not provided, and is set to 10.0%")
45
+ print("Gap is not provided, and is set to 10.0%. ")
46
46
  self.gap = 0.1
47
47
  self._defaults["gap"] = True
48
48
 
@@ -64,9 +64,11 @@ class Config:
64
64
  return dedent(text)
65
65
 
66
66
  def increment_horizon(self, value: int = 1):
67
- self._defaults["horizon"] = False
67
+ # self._defaults["horizon"] = False
68
68
  self.horizon += value
69
69
 
70
+ def get_defaults(self) -> dict[str, bool]:
71
+ return self._defaults
70
72
 
71
73
  @classmethod
72
74
  def get_config(cls) -> Config:
@@ -10,13 +10,68 @@ from math import exp, log, sqrt, sin, cos, isclose
10
10
 
11
11
  from pyscipopt.scip import Model, SumExpr
12
12
 
13
- def switch_comparator(comparator):
14
- if isinstance(comparator, ast.Eq): return ast.NotEq()
15
- if isinstance(comparator, ast.NotEq): return ast.Eq()
16
- if isinstance(comparator, ast.Lt): return ast.Gt()
17
- if isinstance(comparator, ast.LtE): return ast.GtE()
18
- if isinstance(comparator, ast.Gt): return ast.Lt()
19
- if isinstance(comparator, ast.GtE): return ast.LtE()
13
+
14
+ def linearise(expr: ast.Compare, aux_var: Variable) -> tuple[ast.Compare, ast.Compare]:
15
+ """linearise
16
+ This function linearises an inequality using an auxilary variable
17
+
18
+ The linearisation process is as follows.
19
+ If the expression is of the form of E1 <= E2, then we linearise by using the following expressions.
20
+ z=0 ==> E1 <= E2 and z=1 ==> E1 > E2 (equivalent to E1 >= E2 + feastol). This is then equivalent to,
21
+ E2 + feastol - M + z*M <= E1 <= E2 + zM.
22
+
23
+ Similarly, for E1 < E2 we have z=0 ==> E1 < E2 which is equivalent to E1 <= E2 - feastol + z*M
24
+ and z=1 ==> E1 >= E2.
25
+ Thus for E1 < E2 we have,
26
+ E2 + z*M - M <= E1 <= E2 + z*M - feastol.
27
+
28
+ If, however, the inequality is of the form of E1 >= E2 then we evaluate the expression, E2 <= E1.
29
+ Similarly, if the expression is E1 > E2 then we evaluate the expression E2 < E1.
30
+
31
+ :param expr: An inequality expression which is linearised.
32
+ :type expr: ast.Compare
33
+ :param aux_var: An auxiliary variable used when linearising the inequality to determine if the expression is true or false.
34
+ :type aux_var: Variable
35
+ :raises ValueError: If expr is not a valid inequality (i.e. doesn't use <, <=, > and >=)
36
+ :return: both the linearised inequalities
37
+ :rtype: tuple[ast.Compare, ast.Compare]
38
+ """
39
+ if not isinstance(expr.ops[0], (ast.Lt, ast.LtE, ast.Gt, ast.GtE)):
40
+ raise ValueError("Only <, <=, > or >= are allowed")
41
+ if isinstance(expr.ops[0], ast.GtE):
42
+ expr.left, expr.comparators[0] = expr.comparators[0], expr.left
43
+ expr.ops[0] = ast.LtE()
44
+ if isinstance(expr.ops[0], ast.Gt):
45
+ expr.left, expr.comparators[0] = expr.comparators[0], expr.left
46
+ expr.ops[0] = ast.Lt()
47
+
48
+ if isinstance(expr.ops[0], ast.LtE):
49
+ lhs = ast.BinOp(
50
+ left=expr.comparators[0],
51
+ op=ast.Add(),
52
+ right=ast.parse(f"feastol - bigM + {aux_var.name} * bigM").body[0].value
53
+ )
54
+ rhs = ast.BinOp(
55
+ left=expr.comparators[0],
56
+ op=ast.Add(),
57
+ right=ast.parse(f"{aux_var.name} * bigM").body[0].value
58
+ )
59
+ if isinstance(expr.ops[0], ast.Lt):
60
+ lhs = ast.BinOp(
61
+ left=expr.comparators[0],
62
+ op=ast.Add(),
63
+ right=ast.parse(f"{aux_var.name} * bigM - bigM").body[0].value
64
+ )
65
+ rhs = ast.BinOp(
66
+ left=expr.comparators[0],
67
+ op=ast.Add(),
68
+ right=ast.parse(f"{aux_var.name} * bigM - feastol").body[0].value
69
+ )
70
+ expr1 = ast.Compare(lhs, [ast.LtE()], [expr.left])
71
+ expr2 = ast.Compare(expr.left, [ast.LtE()], [rhs])
72
+ return expr1, expr2
73
+
74
+
20
75
 
21
76
  @dataclass
22
77
  class Expressions:
@@ -37,8 +92,8 @@ class Expressions:
37
92
 
38
93
  class ParserType(Enum):
39
94
  """ParserType
40
- "enum type CALCULATOR: Used to calculate using feastol
41
- "enum type PARSER: Used to parse an expression and create the correct minlp constraints
95
+ enum type CALCULATOR: Used to calculate using feastol
96
+ enum type PARSER: Used to parse an expression and create the correct minlp constraints
42
97
  """
43
98
  CALCULATOR = "calculator"
44
99
  PARSER = "parser"
@@ -152,22 +207,11 @@ class ParseModel:
152
207
  aux_vars.append(aux_var)
153
208
  self.variables[aux_var.name] = aux_var.model_var
154
209
 
155
- expr.left = ast.BinOp(
156
- left=expr.left,
157
- op=(ast.Add() if isinstance(expr.ops[0], (ast.Gt, ast.GtE)) else ast.Sub()),
158
- right=ast.parse(f"bigM * {aux_var.name}").body[0].value
159
- )
160
- self.expressions.add_expressions(self.evaluate(expr))
210
+ expr1, expr2 = linearise(expr, aux_var)
161
211
 
162
- expr.ops[0] = switch_comparator(expr.ops[0])
163
-
164
- expr.comparators[0] = ast.BinOp(
165
- left=expr.comparators[0],
166
- op=(ast.Add() if isinstance(expr.ops[0], (ast.Gt, ast.GtE)) else ast.Sub()),
167
- right=ast.parse(f"feastol - bigM").body[0].value
168
- )
169
-
170
- self.expressions.add_expressions(self.evaluate(expr))
212
+ self.expressions.add_expressions(self.evaluate(expr1))
213
+ self.expressions.add_expressions(self.evaluate(expr2))
214
+
171
215
  else:
172
216
  raise Exception("or expressions may only be made up of inequalities")
173
217
  lhs = SumExpr()
@@ -95,12 +95,17 @@ class PlanModel:
95
95
 
96
96
  self.var_names.add(name)
97
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
-
98
+ if vtype.startswith("global"):
99
+ var = Variable.create_var(self.model, name, vtype, "global", self.constants)
100
+ for t in range(self.config.horizon + 1):
101
+ variables[(name, t)] = var
102
+ else:
103
+ for t in range(self.config.horizon):
104
+ variables[(name, t)] = Variable.create_var(self.model, name, vtype, t, self.constants)
105
+ var_type = variables[(name, t)].var_type
106
+ if var_type is VarType.STATE:
107
+ variables[(name, self.config.horizon)] = Variable.create_var(self.model, name, vtype, self.config.horizon, self.constants)
108
+
104
109
  return variables
105
110
 
106
111
 
@@ -130,9 +130,10 @@ class SCIPPlan:
130
130
 
131
131
  @classmethod
132
132
  def solve(cls, config: Config) -> tuple[SCIPPlan, float]:
133
+ # Time total solve time including incrementing horizon
134
+ start_time = time.time()
133
135
  while True:
134
136
  model = SCIPPlan(config)
135
- start_time = time.time()
136
137
  try:
137
138
  print(f"Encoding the problem over horizon h={config.horizon}.")
138
139
  print("Solving the problem.")
@@ -144,12 +145,21 @@ class SCIPPlan:
144
145
  return model, solve_time
145
146
 
146
147
 
147
- except InfeasibilityError:
148
+ except InfeasibilityError:
149
+ if config.get_defaults().get("horizon") is False:
150
+ print(f"Horizon of h={model.config.horizon} is infeasible.")
151
+
152
+ solve_time = (time.time() - start_time)
153
+ print(f"Total time: {solve_time:.3f}")
154
+
155
+ raise InfeasibilityError
156
+
157
+
148
158
  # 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}")
159
+ print(f"Horizon of h={model.config.horizon} is infeasible, incrementing to h={model.config.horizon + 1}.")
150
160
  config.increment_horizon()
151
161
  if config.show_output is True:
152
- print(f"Horizon Time: {(time.time() - start_time): .3f} seconds")
162
+ print(f"Horizon Time: {(time.time() - start_time): .3f} seconds.")
153
163
 
154
164
 
155
165
  def save_values(self, iteration: int):
@@ -162,8 +172,11 @@ def main():
162
172
  print(f"PySCIPOpt Version: {version('pyscipopt')}\n")
163
173
  config = Config.get_config()
164
174
  print(config)
165
-
166
- plan, solve_time = SCIPPlan.solve(config)
175
+
176
+ try:
177
+ plan, solve_time = SCIPPlan.solve(config)
178
+ except InfeasibilityError:
179
+ return None
167
180
 
168
181
  if config.save_sols is True:
169
182
  write_to_csv("new_constraints", plan.new_constraints, config)
@@ -185,9 +198,9 @@ def main():
185
198
  for action_name in action_names:
186
199
  if action_name == config.dt_var:
187
200
  continue
188
- print(f"{action_name} at step {step} by value {plan.scip_model.getVal(plan.plan.variables[(action_name, step)].model_var):.3f}")
201
+ print(f"{action_name} at step {step} by value {plan.scip_model.getVal(plan.plan.variables[(action_name, step)].model_var):.3f}.")
189
202
 
190
- print(f"Dt at step {step} by value {plan.scip_model.getVal(plan.plan.variables[('Dt', step)].model_var):.3f} \n")
203
+ print(f"Dt at step {step} by value {plan.scip_model.getVal(plan.plan.variables[('Dt', step)].model_var):.3f}. \n")
191
204
 
192
205
  print(f"Total reward: {(plan.scip_model.getObjVal()):.3f}")
193
206
  print(f"Total time: {solve_time:.3f}")
@@ -1,9 +1,9 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: scipplan
3
- Version: 0.1.0a1
3
+ Version: 0.1.1a0
4
4
  Summary: Metric Hybrid Factored Planning in Nonlinear Domains with Constraint Generation in Python.
5
5
  Author: Ari Gestetner, Buser Say
6
- Author-email: ages0001@student.monash.edu, buser.say@monash.edu
6
+ Author-email: ari.gestetner@monash.edu, buser.say@monash.edu
7
7
  License: MIT License
8
8
  Keywords: scip,automated planner
9
9
  Classifier: Development Status :: 3 - Alpha
@@ -121,13 +121,17 @@ state_continuous: Location_x
121
121
  state_continuous: Location_y
122
122
  state_continuous: Speed_x
123
123
  state_continuous: Speed_y
124
+ global_boolean: aux_1
124
125
  ```
125
- where the variable and value type of the variable is set in the format "{variable type}_{value type}" and the variable itself has to be in a Python compatible format (e.g. variables can't use - sign like `some-var` but can use _ like `some_var` as well as the dash sign ' cannot be used). The use of next state variables which is often written using the dash symbol will be explained further in the Transitions section.
126
+ where the variable and value type of the variable is set in the format "{variable type}_{value type}" and the variable itself has to be in a Python compatible format (e.g. variables can't use - symbol like `some-var` but can use _ like `some_var` as well as the dash sign ' cannot be used). The use of next state variables which is often written using the dash symbol will be explained further in the Transitions section.
126
127
  Additionally a variable for Dt has to be defined and has to be the same as the dt_var in the config object so if you would like to use a different variable name for Dt (e.g. dt) please also ensure you add it to the config via the `--dt-var` tag or the `dt_var` parameter.
128
+ The variable type `global` is used when you would like a non state variable to be equal over every time step over the horizon (e.g. if $v_t$ is a variable at time $t$ and $H$ is the horizon, then $v_1 = v_2 = \dots = v_{H+1}$).
129
+
127
130
  The available variable types are
128
131
  - state
129
132
  - action
130
133
  - auxiliary
134
+ - global
131
135
 
132
136
  The available value types are
133
137
  - continuos
@@ -144,7 +148,7 @@ Location_y == 0.0
144
148
  Speed_x == 0.0
145
149
  Speed_y == 0.0
146
150
  ```
147
- notice te use of teh constant value defined earlier in the constants file.
151
+ notice the use of the constant value defined earlier in the constants file.
148
152
  ### Goals
149
153
  The goals file should encode the final state values such that t=H+1, for example
150
154
  ```txt
File without changes
File without changes
File without changes
File without changes