scipplan 0.1.0a0__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.
- scipplan-0.1.0a0/LICENSE +19 -0
- scipplan-0.1.0a0/MANIFEST.in +1 -0
- scipplan-0.1.0a0/PKG-INFO +215 -0
- scipplan-0.1.0a0/README.md +196 -0
- scipplan-0.1.0a0/scipplan/__init__.py +5 -0
- scipplan-0.1.0a0/scipplan/config.py +152 -0
- scipplan-0.1.0a0/scipplan/helpers.py +30 -0
- scipplan-0.1.0a0/scipplan/parse_model.py +275 -0
- scipplan-0.1.0a0/scipplan/plan_model.py +257 -0
- scipplan-0.1.0a0/scipplan/scipplan.py +196 -0
- scipplan-0.1.0a0/scipplan/translation/constants_navigation_1.txt +2 -0
- scipplan-0.1.0a0/scipplan/translation/constants_navigation_2.txt +2 -0
- scipplan-0.1.0a0/scipplan/translation/constants_navigation_3.txt +1 -0
- scipplan-0.1.0a0/scipplan/translation/goals_navigation_1.txt +2 -0
- scipplan-0.1.0a0/scipplan/translation/goals_navigation_2.txt +2 -0
- scipplan-0.1.0a0/scipplan/translation/goals_navigation_3.txt +2 -0
- scipplan-0.1.0a0/scipplan/translation/initials_navigation_1.txt +4 -0
- scipplan-0.1.0a0/scipplan/translation/initials_navigation_2.txt +4 -0
- scipplan-0.1.0a0/scipplan/translation/initials_navigation_3.txt +4 -0
- scipplan-0.1.0a0/scipplan/translation/instantaneous_constraints_navigation_1.txt +9 -0
- scipplan-0.1.0a0/scipplan/translation/instantaneous_constraints_navigation_2.txt +10 -0
- scipplan-0.1.0a0/scipplan/translation/instantaneous_constraints_navigation_3.txt +11 -0
- scipplan-0.1.0a0/scipplan/translation/pvariables_navigation_1.txt +8 -0
- scipplan-0.1.0a0/scipplan/translation/pvariables_navigation_2.txt +8 -0
- scipplan-0.1.0a0/scipplan/translation/pvariables_navigation_3.txt +8 -0
- scipplan-0.1.0a0/scipplan/translation/reward_navigation_1.txt +1 -0
- scipplan-0.1.0a0/scipplan/translation/reward_navigation_2.txt +1 -0
- scipplan-0.1.0a0/scipplan/translation/reward_navigation_3.txt +1 -0
- scipplan-0.1.0a0/scipplan/translation/temporal_constraints_navigation_1.txt +6 -0
- scipplan-0.1.0a0/scipplan/translation/temporal_constraints_navigation_2.txt +6 -0
- scipplan-0.1.0a0/scipplan/translation/temporal_constraints_navigation_3.txt +7 -0
- scipplan-0.1.0a0/scipplan/translation/transitions_navigation_1.txt +4 -0
- scipplan-0.1.0a0/scipplan/translation/transitions_navigation_2.txt +4 -0
- scipplan-0.1.0a0/scipplan/translation/transitions_navigation_3.txt +4 -0
- scipplan-0.1.0a0/scipplan/variables.py +91 -0
- scipplan-0.1.0a0/scipplan/zero_crossing.py +28 -0
- scipplan-0.1.0a0/scipplan.egg-info/PKG-INFO +215 -0
- scipplan-0.1.0a0/scipplan.egg-info/SOURCES.txt +42 -0
- scipplan-0.1.0a0/scipplan.egg-info/dependency_links.txt +1 -0
- scipplan-0.1.0a0/scipplan.egg-info/entry_points.txt +2 -0
- scipplan-0.1.0a0/scipplan.egg-info/not-zip-safe +1 -0
- scipplan-0.1.0a0/scipplan.egg-info/top_level.txt +1 -0
- scipplan-0.1.0a0/setup.cfg +4 -0
- scipplan-0.1.0a0/setup.py +48 -0
scipplan-0.1.0a0/LICENSE
ADDED
@@ -0,0 +1,19 @@
|
|
1
|
+
Copyright (c) 2024 Ari Gestetner
|
2
|
+
|
3
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
4
|
+
of this software and associated documentation files (the "Software"), to deal
|
5
|
+
in the Software without restriction, including without limitation the rights
|
6
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
7
|
+
copies of the Software, and to permit persons to whom the Software is
|
8
|
+
furnished to do so, subject to the following conditions:
|
9
|
+
|
10
|
+
The above copyright notice and this permission notice shall be included in all
|
11
|
+
copies or substantial portions of the Software.
|
12
|
+
|
13
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
14
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
15
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
16
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
17
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
18
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
19
|
+
SOFTWARE.
|
@@ -0,0 +1 @@
|
|
1
|
+
include translation/*.txt
|
@@ -0,0 +1,215 @@
|
|
1
|
+
Metadata-Version: 2.1
|
2
|
+
Name: scipplan
|
3
|
+
Version: 0.1.0a0
|
4
|
+
Summary: Metric Hybrid Factored Planning in Nonlinear Domains with Constraint Generation in Python.
|
5
|
+
Author: Ari Gestetner, Buser Say
|
6
|
+
Author-email: ages0001@student.monash.edu, buser.say@monash.edu
|
7
|
+
License: MIT License
|
8
|
+
Keywords: scip,automated planner
|
9
|
+
Classifier: Development Status :: 3 - Alpha
|
10
|
+
Classifier: Environment :: Console
|
11
|
+
Classifier: Intended Audience :: Science/Research
|
12
|
+
Classifier: License :: OSI Approved :: MIT License
|
13
|
+
Classifier: Natural Language :: English
|
14
|
+
Classifier: Operating System :: OS Independent
|
15
|
+
Classifier: Programming Language :: Python :: 3
|
16
|
+
Classifier: Topic :: Scientific/Engineering :: Artificial Intelligence
|
17
|
+
Description-Content-Type: text/markdown
|
18
|
+
License-File: LICENSE
|
19
|
+
|
20
|
+
# SCIPPlan
|
21
|
+
|
22
|
+
SCIPPlan [1,2,3] is a SCIP-based [4] hybrid planner for domains with i) mixed (i.e., real and/or discrete valued) state and action spaces, ii) nonlinear state transitions that are functions of time, and iii) general reward functions. SCIPPlan iteratively i) finds violated constraints (i.e., zero-crossings) by simulating the state transitions, and ii) adds the violated (symbolic) constraints back to its underlying optimisation model, until a valid plan is found.
|
23
|
+
|
24
|
+
## Example Domain: Navigation
|
25
|
+
|
26
|
+
<img src=./visualisation/scipplan_navigation_1.gif width="32%" height="32%"> <img src=./visualisation/scipplan_navigation_2.gif width="32%" height="32%"> <img src=./visualisation/scipplan_navigation_3.gif width="32%" height="32%">
|
27
|
+
|
28
|
+
|
29
|
+
Figure 1: Visualisation of different plans generated by SCIPPlan [1,2,3] for example navigation domains where the red square represents the agent, the blue shapes represent the obstacles, the gold star represents the goal location and the delta represents time. The agent can control its acceleration and the duration of its control input to modify its speed and location in order to navigate in a two-dimensional maze. The purpose of the domain is to find a path for the agent with minimum makespan such that the agent reaches its the goal without colliding with the obstacles.
|
30
|
+
|
31
|
+
Note that SCIPPlan does not linearise or discretise the domain to find a valid plan.
|
32
|
+
|
33
|
+
## Dependencies
|
34
|
+
|
35
|
+
i) Solver: SCIP (the current implementation uses the python interface to the SCIP solver, i.e., PySCIPOpt [5]). This version of SCIPPlan has only been tested on PySCIOpt>=4.0.0 using earlier an version of pyscipopt may result in unintended behaviour.
|
36
|
+
|
37
|
+
## Installing and Running SCIPPlan
|
38
|
+
In order to Install SCIPPlan you need to ensure you have a working version of the SCIP optimisation suite on your system which can be installed from [the SCIP website](https://www.scipopt.org). For more information about SCIP and PySCIPOpt refer to this [installation guide](https://github.com/scipopt/PySCIPOpt/blob/master/INSTALL.md).
|
39
|
+
|
40
|
+
After installing SCIP you will be able to install SCIPPlan using
|
41
|
+
```bash
|
42
|
+
pip install scipplan
|
43
|
+
```
|
44
|
+
Now you will be able to run some of the example domains which include
|
45
|
+
- Navigation (3 instances)
|
46
|
+
|
47
|
+
To run one of these examples all you need to do is run
|
48
|
+
```bash
|
49
|
+
scipplan -D navigation -I 1
|
50
|
+
```
|
51
|
+
which will run the 1st instance of the navigation domain. For more information regarding the available tags and what they mean run `scipplan --help`.
|
52
|
+
|
53
|
+
Alternatively you can import scipplan classes to run it using python.
|
54
|
+
```py
|
55
|
+
from scipplan.scipplan import SCIPPlan
|
56
|
+
from scipplan.config import Config
|
57
|
+
from scipplan.helpers import write_to_csv
|
58
|
+
```
|
59
|
+
this will import the only 2 classes and function needed to run SCIPPlan. Then to set the configuration either create an instance of the Config class by setting the params or by retrieving the cli input
|
60
|
+
```py
|
61
|
+
# Set params
|
62
|
+
config = Config(domain="navigation", instance=1)
|
63
|
+
# Retrieve cli args
|
64
|
+
config = Config.get_config()
|
65
|
+
```
|
66
|
+
after which you are able to solve problem by either using the solve or optimize methods
|
67
|
+
```py
|
68
|
+
# The optimize method just optimises the problem for the given horizon
|
69
|
+
plan = SCIPPlan(config)
|
70
|
+
plan.optimize()
|
71
|
+
# Class method which takes input the config, solves the problem
|
72
|
+
# with auto incrementing the horizon until a solution is found then
|
73
|
+
# returns the plan as well as the time taken to solve the problem
|
74
|
+
plan, solve_time = SCIPPlan.solve(config)
|
75
|
+
```
|
76
|
+
In order to save the generated constraints for the horizon solved as well as the results, use the following code
|
77
|
+
```py
|
78
|
+
write_to_csv("new_constraints", plan.new_constraints, config)
|
79
|
+
write_to_csv("results", plan.results_table, config)
|
80
|
+
```
|
81
|
+
|
82
|
+
|
83
|
+
|
84
|
+
## Custom Domains
|
85
|
+
If you would like to create your own domain you will need to create a directory named "translation" in the directory which scipplan is run from with txt files of the format "{translation type}\_{domain name}\_{instance number}.txt" (e.g. "pvariables_navigation_1.txt"). Note that the files in the translation directory will override any example files if they share domain name and instance number (e.g. navigation 1 would override the example).
|
86
|
+
|
87
|
+
For each domain instance the following translation files are required
|
88
|
+
- constants
|
89
|
+
- pvariables
|
90
|
+
- initials
|
91
|
+
- goals
|
92
|
+
- instantaneous_constraints
|
93
|
+
- temporal_constraints
|
94
|
+
- transitions
|
95
|
+
- reward
|
96
|
+
|
97
|
+
As a part of SCIPPlan, all the constraint files allow for the use of "and" and "or" expressions, polynomials and `exp`, `log`, `sqrt`, `sin` and `cos` functions (Note: `sin` and `cos` are only available in PySCIPOpt>=4.3.0 so if your version is below that you will not be able to use the trig functions unless you update).
|
98
|
+
|
99
|
+
### Constants
|
100
|
+
The constants file allows for constant values to be defined as a variable to be used in other files in the model. To use add constants as follows
|
101
|
+
```txt
|
102
|
+
HalfVal = 0.5
|
103
|
+
Epsilon = config_epsilon
|
104
|
+
bigM = config_bigM
|
105
|
+
```
|
106
|
+
|
107
|
+
Some config values can be accessed in the constants file to be used in other files and are available as
|
108
|
+
- `config_epsilon`
|
109
|
+
- `config_gap`
|
110
|
+
- `config_bigM`
|
111
|
+
Note that these variables are only accessible in the constants file and you will need to define a new constants variable to store the value
|
112
|
+
|
113
|
+
### Pvariables
|
114
|
+
When creating the pvariables file, the variables should be listed in the following format
|
115
|
+
```txt
|
116
|
+
action_continuous: Accelerate_x
|
117
|
+
action_continuous: Accelerate_y
|
118
|
+
action_continuous: Dt
|
119
|
+
action_boolean: Mode
|
120
|
+
state_continuous: Location_x
|
121
|
+
state_continuous: Location_y
|
122
|
+
state_continuous: Speed_x
|
123
|
+
state_continuous: Speed_y
|
124
|
+
```
|
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
|
+
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.
|
127
|
+
The available variable types are
|
128
|
+
- state
|
129
|
+
- action
|
130
|
+
- auxiliary
|
131
|
+
|
132
|
+
The available value types are
|
133
|
+
- continuos
|
134
|
+
- integer
|
135
|
+
- boolean
|
136
|
+
|
137
|
+
Please note that constants don't need to have their variable names defined in pvariables as they are defined in constants.
|
138
|
+
|
139
|
+
### Initials
|
140
|
+
The initials file defines the initial state values for time t=0, for example
|
141
|
+
```txt
|
142
|
+
Location_x == 0.0
|
143
|
+
Location_y == 0.0
|
144
|
+
Speed_x == 0.0
|
145
|
+
Speed_y == 0.0
|
146
|
+
```
|
147
|
+
notice te use of teh constant value defined earlier in the constants file.
|
148
|
+
### Goals
|
149
|
+
The goals file should encode the final state values such that t=H+1, for example
|
150
|
+
```txt
|
151
|
+
Location_x == 8.0
|
152
|
+
Location_y == 8.0
|
153
|
+
```
|
154
|
+
### Instantaneous Constraints
|
155
|
+
This is where the instantaneous constraints go.
|
156
|
+
An example is as follows
|
157
|
+
```txt
|
158
|
+
Location_x <= 10.0
|
159
|
+
Location_y <= 10.0
|
160
|
+
Location_x >= 0.0
|
161
|
+
Location_y >= 0.0
|
162
|
+
Accelerate_x <= 0.5
|
163
|
+
Accelerate_y <= 0.5
|
164
|
+
Accelerate_x >= -0.5
|
165
|
+
Accelerate_y >= -0.5
|
166
|
+
(Location_x <= 4.0) or (Location_x >= 6.0) or (Location_y <= 4.0) or (Location_y >= 6.0)
|
167
|
+
```
|
168
|
+
### Temporal Constraints
|
169
|
+
The temporal constraints are the constraints which SCIPPlan will ensure that the solution never violates by iterating through every epsilon value of Dt and checking for zero crossings. An example of a temporal constraint is as follows
|
170
|
+
```txt
|
171
|
+
Location_x + Speed_x*(Dt + Epsilon*Mode) + 0.5*Accelerate_x*(Dt + Epsilon*Mode)*(Dt + Epsilon*Mode) <= 10.0
|
172
|
+
Location_y + Speed_y*(Dt + Epsilon*Mode) + 0.5*Accelerate_y*(Dt + Epsilon*Mode)*(Dt + Epsilon*Mode) <= 10.0
|
173
|
+
Location_x + Speed_x*(Dt + Epsilon*Mode) + 0.5*Accelerate_x*(Dt + Epsilon*Mode)*(Dt + Epsilon*Mode) >= 0.0
|
174
|
+
Location_y + Speed_y*(Dt + Epsilon*Mode) + 0.5*Accelerate_y*(Dt + Epsilon*Mode)*(Dt + Epsilon*Mode) >= 0.0
|
175
|
+
|
176
|
+
((Location_x + Speed_x*(Dt + Epsilon*Mode) + 0.5*Accelerate_x*(Dt + Epsilon*Mode)*(Dt + Epsilon*Mode) <= 4.0) or
|
177
|
+
(Location_x + Speed_x*(Dt + Epsilon*Mode) + 0.5*Accelerate_x*(Dt + Epsilon*Mode)*(Dt + Epsilon*Mode) >= 6.0) or
|
178
|
+
(Location_y + Speed_y*(Dt + Epsilon*Mode) + 0.5*Accelerate_y*(Dt + Epsilon*Mode)*(Dt + Epsilon*Mode) <= 4.0) or
|
179
|
+
(Location_y + Speed_y*(Dt + Epsilon*Mode) + 0.5*Accelerate_y*(Dt + Epsilon*Mode)*(Dt + Epsilon*Mode) >= 6.0))
|
180
|
+
```
|
181
|
+
|
182
|
+
The use of mode switches are used in these constraints (`Dt + Epsilon*Mode`). Please note that the or expression is enclosed in round brackets which allows the constraints to be parsed as a singular expression
|
183
|
+
### Transitions
|
184
|
+
Transitions are to be added here with the following syntax.
|
185
|
+
For example $S_{t+1} = \frac 12 A_t\cdot t^2 + V_t\cdot t + S_t$.
|
186
|
+
Alternatively this can be written as $S' = \frac 12 A\cdot t^2 + V\cdot t + S$.
|
187
|
+
Since Python doesn't allow variables to use the ' symbol it should be replaced with `_dash`, for example
|
188
|
+
```txt
|
189
|
+
Location_x_dash - 1.0*Location_x - 1.0*Speed_x*(Dt + Epsilon*Mode) - 0.5*Accelerate_x*(Dt + Epsilon*Mode)*(Dt + Epsilon*Mode) == 0.0
|
190
|
+
Location_y_dash - 1.0*Location_y - 1.0*Speed_y*(Dt + Epsilon*Mode) - 0.5*Accelerate_y*(Dt + Epsilon*Mode)*(Dt + Epsilon*Mode) == 0.0
|
191
|
+
Speed_x_dash - 1.0*Speed_x - 1.0*Accelerate_x*(Dt + Epsilon*Mode) == 0.0
|
192
|
+
Speed_y_dash - 1.0*Speed_y - 1.0*Accelerate_y*(Dt + Epsilon*Mode) == 0.0
|
193
|
+
```
|
194
|
+
|
195
|
+
### Reward
|
196
|
+
As for the reward function, SCIPPlan maximises the reward thus if using a cost function it should be negated as per the example
|
197
|
+
```txt
|
198
|
+
-1.0*(Dt + Mode * Epsilon)
|
199
|
+
```
|
200
|
+
Only one reward function is able to be optimised for in SCIPPlan
|
201
|
+
|
202
|
+
## Citation
|
203
|
+
|
204
|
+
If you are using SCIPPlan, please cite the papers [1,2,3] and the underlying SCIP solver [4].
|
205
|
+
|
206
|
+
## References
|
207
|
+
[1] Buser Say and Scott Sanner. [Metric Nonlinear Hybrid Planning with Constraint Generation](http://icaps18.icaps-conference.org/fileadmin/alg/conferences/icaps18/workshops/workshop06/docs/proceedings.pdf#page=23). In PlanSOpt, pages 19-25, 2018.
|
208
|
+
|
209
|
+
[2] Buser Say and Scott Sanner. [Metric Hybrid Factored Planning in Nonlinear Domains with Constraint Generation](https://link.springer.com/chapter/10.1007/978-3-030-19212-9_33). In CPAIOR, pages 502-518, 2019.
|
210
|
+
|
211
|
+
[3] Buser Say. [Robust Metric Hybrid Planning in Stochastic Nonlinear Domains Using Mathematical Optimization](https://ojs.aaai.org/index.php/ICAPS/article/view/27216). In ICAPS, pages 375-383, 2023.
|
212
|
+
|
213
|
+
[4] [SCIP](https://www.scipopt.org/)
|
214
|
+
|
215
|
+
[5] [PySCIPOpt](https://github.com/SCIP-Interfaces/PySCIPOpt)
|
@@ -0,0 +1,196 @@
|
|
1
|
+
# SCIPPlan
|
2
|
+
|
3
|
+
SCIPPlan [1,2,3] is a SCIP-based [4] hybrid planner for domains with i) mixed (i.e., real and/or discrete valued) state and action spaces, ii) nonlinear state transitions that are functions of time, and iii) general reward functions. SCIPPlan iteratively i) finds violated constraints (i.e., zero-crossings) by simulating the state transitions, and ii) adds the violated (symbolic) constraints back to its underlying optimisation model, until a valid plan is found.
|
4
|
+
|
5
|
+
## Example Domain: Navigation
|
6
|
+
|
7
|
+
<img src=./visualisation/scipplan_navigation_1.gif width="32%" height="32%"> <img src=./visualisation/scipplan_navigation_2.gif width="32%" height="32%"> <img src=./visualisation/scipplan_navigation_3.gif width="32%" height="32%">
|
8
|
+
|
9
|
+
|
10
|
+
Figure 1: Visualisation of different plans generated by SCIPPlan [1,2,3] for example navigation domains where the red square represents the agent, the blue shapes represent the obstacles, the gold star represents the goal location and the delta represents time. The agent can control its acceleration and the duration of its control input to modify its speed and location in order to navigate in a two-dimensional maze. The purpose of the domain is to find a path for the agent with minimum makespan such that the agent reaches its the goal without colliding with the obstacles.
|
11
|
+
|
12
|
+
Note that SCIPPlan does not linearise or discretise the domain to find a valid plan.
|
13
|
+
|
14
|
+
## Dependencies
|
15
|
+
|
16
|
+
i) Solver: SCIP (the current implementation uses the python interface to the SCIP solver, i.e., PySCIPOpt [5]). This version of SCIPPlan has only been tested on PySCIOpt>=4.0.0 using earlier an version of pyscipopt may result in unintended behaviour.
|
17
|
+
|
18
|
+
## Installing and Running SCIPPlan
|
19
|
+
In order to Install SCIPPlan you need to ensure you have a working version of the SCIP optimisation suite on your system which can be installed from [the SCIP website](https://www.scipopt.org). For more information about SCIP and PySCIPOpt refer to this [installation guide](https://github.com/scipopt/PySCIPOpt/blob/master/INSTALL.md).
|
20
|
+
|
21
|
+
After installing SCIP you will be able to install SCIPPlan using
|
22
|
+
```bash
|
23
|
+
pip install scipplan
|
24
|
+
```
|
25
|
+
Now you will be able to run some of the example domains which include
|
26
|
+
- Navigation (3 instances)
|
27
|
+
|
28
|
+
To run one of these examples all you need to do is run
|
29
|
+
```bash
|
30
|
+
scipplan -D navigation -I 1
|
31
|
+
```
|
32
|
+
which will run the 1st instance of the navigation domain. For more information regarding the available tags and what they mean run `scipplan --help`.
|
33
|
+
|
34
|
+
Alternatively you can import scipplan classes to run it using python.
|
35
|
+
```py
|
36
|
+
from scipplan.scipplan import SCIPPlan
|
37
|
+
from scipplan.config import Config
|
38
|
+
from scipplan.helpers import write_to_csv
|
39
|
+
```
|
40
|
+
this will import the only 2 classes and function needed to run SCIPPlan. Then to set the configuration either create an instance of the Config class by setting the params or by retrieving the cli input
|
41
|
+
```py
|
42
|
+
# Set params
|
43
|
+
config = Config(domain="navigation", instance=1)
|
44
|
+
# Retrieve cli args
|
45
|
+
config = Config.get_config()
|
46
|
+
```
|
47
|
+
after which you are able to solve problem by either using the solve or optimize methods
|
48
|
+
```py
|
49
|
+
# The optimize method just optimises the problem for the given horizon
|
50
|
+
plan = SCIPPlan(config)
|
51
|
+
plan.optimize()
|
52
|
+
# Class method which takes input the config, solves the problem
|
53
|
+
# with auto incrementing the horizon until a solution is found then
|
54
|
+
# returns the plan as well as the time taken to solve the problem
|
55
|
+
plan, solve_time = SCIPPlan.solve(config)
|
56
|
+
```
|
57
|
+
In order to save the generated constraints for the horizon solved as well as the results, use the following code
|
58
|
+
```py
|
59
|
+
write_to_csv("new_constraints", plan.new_constraints, config)
|
60
|
+
write_to_csv("results", plan.results_table, config)
|
61
|
+
```
|
62
|
+
|
63
|
+
|
64
|
+
|
65
|
+
## Custom Domains
|
66
|
+
If you would like to create your own domain you will need to create a directory named "translation" in the directory which scipplan is run from with txt files of the format "{translation type}\_{domain name}\_{instance number}.txt" (e.g. "pvariables_navigation_1.txt"). Note that the files in the translation directory will override any example files if they share domain name and instance number (e.g. navigation 1 would override the example).
|
67
|
+
|
68
|
+
For each domain instance the following translation files are required
|
69
|
+
- constants
|
70
|
+
- pvariables
|
71
|
+
- initials
|
72
|
+
- goals
|
73
|
+
- instantaneous_constraints
|
74
|
+
- temporal_constraints
|
75
|
+
- transitions
|
76
|
+
- reward
|
77
|
+
|
78
|
+
As a part of SCIPPlan, all the constraint files allow for the use of "and" and "or" expressions, polynomials and `exp`, `log`, `sqrt`, `sin` and `cos` functions (Note: `sin` and `cos` are only available in PySCIPOpt>=4.3.0 so if your version is below that you will not be able to use the trig functions unless you update).
|
79
|
+
|
80
|
+
### Constants
|
81
|
+
The constants file allows for constant values to be defined as a variable to be used in other files in the model. To use add constants as follows
|
82
|
+
```txt
|
83
|
+
HalfVal = 0.5
|
84
|
+
Epsilon = config_epsilon
|
85
|
+
bigM = config_bigM
|
86
|
+
```
|
87
|
+
|
88
|
+
Some config values can be accessed in the constants file to be used in other files and are available as
|
89
|
+
- `config_epsilon`
|
90
|
+
- `config_gap`
|
91
|
+
- `config_bigM`
|
92
|
+
Note that these variables are only accessible in the constants file and you will need to define a new constants variable to store the value
|
93
|
+
|
94
|
+
### Pvariables
|
95
|
+
When creating the pvariables file, the variables should be listed in the following format
|
96
|
+
```txt
|
97
|
+
action_continuous: Accelerate_x
|
98
|
+
action_continuous: Accelerate_y
|
99
|
+
action_continuous: Dt
|
100
|
+
action_boolean: Mode
|
101
|
+
state_continuous: Location_x
|
102
|
+
state_continuous: Location_y
|
103
|
+
state_continuous: Speed_x
|
104
|
+
state_continuous: Speed_y
|
105
|
+
```
|
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
|
+
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.
|
108
|
+
The available variable types are
|
109
|
+
- state
|
110
|
+
- action
|
111
|
+
- auxiliary
|
112
|
+
|
113
|
+
The available value types are
|
114
|
+
- continuos
|
115
|
+
- integer
|
116
|
+
- boolean
|
117
|
+
|
118
|
+
Please note that constants don't need to have their variable names defined in pvariables as they are defined in constants.
|
119
|
+
|
120
|
+
### Initials
|
121
|
+
The initials file defines the initial state values for time t=0, for example
|
122
|
+
```txt
|
123
|
+
Location_x == 0.0
|
124
|
+
Location_y == 0.0
|
125
|
+
Speed_x == 0.0
|
126
|
+
Speed_y == 0.0
|
127
|
+
```
|
128
|
+
notice te use of teh constant value defined earlier in the constants file.
|
129
|
+
### Goals
|
130
|
+
The goals file should encode the final state values such that t=H+1, for example
|
131
|
+
```txt
|
132
|
+
Location_x == 8.0
|
133
|
+
Location_y == 8.0
|
134
|
+
```
|
135
|
+
### Instantaneous Constraints
|
136
|
+
This is where the instantaneous constraints go.
|
137
|
+
An example is as follows
|
138
|
+
```txt
|
139
|
+
Location_x <= 10.0
|
140
|
+
Location_y <= 10.0
|
141
|
+
Location_x >= 0.0
|
142
|
+
Location_y >= 0.0
|
143
|
+
Accelerate_x <= 0.5
|
144
|
+
Accelerate_y <= 0.5
|
145
|
+
Accelerate_x >= -0.5
|
146
|
+
Accelerate_y >= -0.5
|
147
|
+
(Location_x <= 4.0) or (Location_x >= 6.0) or (Location_y <= 4.0) or (Location_y >= 6.0)
|
148
|
+
```
|
149
|
+
### Temporal Constraints
|
150
|
+
The temporal constraints are the constraints which SCIPPlan will ensure that the solution never violates by iterating through every epsilon value of Dt and checking for zero crossings. An example of a temporal constraint is as follows
|
151
|
+
```txt
|
152
|
+
Location_x + Speed_x*(Dt + Epsilon*Mode) + 0.5*Accelerate_x*(Dt + Epsilon*Mode)*(Dt + Epsilon*Mode) <= 10.0
|
153
|
+
Location_y + Speed_y*(Dt + Epsilon*Mode) + 0.5*Accelerate_y*(Dt + Epsilon*Mode)*(Dt + Epsilon*Mode) <= 10.0
|
154
|
+
Location_x + Speed_x*(Dt + Epsilon*Mode) + 0.5*Accelerate_x*(Dt + Epsilon*Mode)*(Dt + Epsilon*Mode) >= 0.0
|
155
|
+
Location_y + Speed_y*(Dt + Epsilon*Mode) + 0.5*Accelerate_y*(Dt + Epsilon*Mode)*(Dt + Epsilon*Mode) >= 0.0
|
156
|
+
|
157
|
+
((Location_x + Speed_x*(Dt + Epsilon*Mode) + 0.5*Accelerate_x*(Dt + Epsilon*Mode)*(Dt + Epsilon*Mode) <= 4.0) or
|
158
|
+
(Location_x + Speed_x*(Dt + Epsilon*Mode) + 0.5*Accelerate_x*(Dt + Epsilon*Mode)*(Dt + Epsilon*Mode) >= 6.0) or
|
159
|
+
(Location_y + Speed_y*(Dt + Epsilon*Mode) + 0.5*Accelerate_y*(Dt + Epsilon*Mode)*(Dt + Epsilon*Mode) <= 4.0) or
|
160
|
+
(Location_y + Speed_y*(Dt + Epsilon*Mode) + 0.5*Accelerate_y*(Dt + Epsilon*Mode)*(Dt + Epsilon*Mode) >= 6.0))
|
161
|
+
```
|
162
|
+
|
163
|
+
The use of mode switches are used in these constraints (`Dt + Epsilon*Mode`). Please note that the or expression is enclosed in round brackets which allows the constraints to be parsed as a singular expression
|
164
|
+
### Transitions
|
165
|
+
Transitions are to be added here with the following syntax.
|
166
|
+
For example $S_{t+1} = \frac 12 A_t\cdot t^2 + V_t\cdot t + S_t$.
|
167
|
+
Alternatively this can be written as $S' = \frac 12 A\cdot t^2 + V\cdot t + S$.
|
168
|
+
Since Python doesn't allow variables to use the ' symbol it should be replaced with `_dash`, for example
|
169
|
+
```txt
|
170
|
+
Location_x_dash - 1.0*Location_x - 1.0*Speed_x*(Dt + Epsilon*Mode) - 0.5*Accelerate_x*(Dt + Epsilon*Mode)*(Dt + Epsilon*Mode) == 0.0
|
171
|
+
Location_y_dash - 1.0*Location_y - 1.0*Speed_y*(Dt + Epsilon*Mode) - 0.5*Accelerate_y*(Dt + Epsilon*Mode)*(Dt + Epsilon*Mode) == 0.0
|
172
|
+
Speed_x_dash - 1.0*Speed_x - 1.0*Accelerate_x*(Dt + Epsilon*Mode) == 0.0
|
173
|
+
Speed_y_dash - 1.0*Speed_y - 1.0*Accelerate_y*(Dt + Epsilon*Mode) == 0.0
|
174
|
+
```
|
175
|
+
|
176
|
+
### Reward
|
177
|
+
As for the reward function, SCIPPlan maximises the reward thus if using a cost function it should be negated as per the example
|
178
|
+
```txt
|
179
|
+
-1.0*(Dt + Mode * Epsilon)
|
180
|
+
```
|
181
|
+
Only one reward function is able to be optimised for in SCIPPlan
|
182
|
+
|
183
|
+
## Citation
|
184
|
+
|
185
|
+
If you are using SCIPPlan, please cite the papers [1,2,3] and the underlying SCIP solver [4].
|
186
|
+
|
187
|
+
## References
|
188
|
+
[1] Buser Say and Scott Sanner. [Metric Nonlinear Hybrid Planning with Constraint Generation](http://icaps18.icaps-conference.org/fileadmin/alg/conferences/icaps18/workshops/workshop06/docs/proceedings.pdf#page=23). In PlanSOpt, pages 19-25, 2018.
|
189
|
+
|
190
|
+
[2] Buser Say and Scott Sanner. [Metric Hybrid Factored Planning in Nonlinear Domains with Constraint Generation](https://link.springer.com/chapter/10.1007/978-3-030-19212-9_33). In CPAIOR, pages 502-518, 2019.
|
191
|
+
|
192
|
+
[3] Buser Say. [Robust Metric Hybrid Planning in Stochastic Nonlinear Domains Using Mathematical Optimization](https://ojs.aaai.org/index.php/ICAPS/article/view/27216). In ICAPS, pages 375-383, 2023.
|
193
|
+
|
194
|
+
[4] [SCIP](https://www.scipopt.org/)
|
195
|
+
|
196
|
+
[5] [PySCIPOpt](https://github.com/SCIP-Interfaces/PySCIPOpt)
|
@@ -0,0 +1,152 @@
|
|
1
|
+
from __future__ import annotations
|
2
|
+
|
3
|
+
from dataclasses import dataclass, field
|
4
|
+
from textwrap import dedent
|
5
|
+
import argparse
|
6
|
+
|
7
|
+
@dataclass
|
8
|
+
class Config:
|
9
|
+
"""
|
10
|
+
The Config class provides an interface to set the configuration for SCIPPlan.
|
11
|
+
Config can be set by creating an instance of the class adding the variables desired.
|
12
|
+
Alternatively, the `get_config` class method will return a config instance using variables set by the programs args (e.g. -D 'domain').
|
13
|
+
"""
|
14
|
+
domain: str
|
15
|
+
instance: int
|
16
|
+
horizon: int = field(default=None)
|
17
|
+
epsilon: float = field(default=None)
|
18
|
+
gap: float = field(default=None)
|
19
|
+
show_output: bool = False
|
20
|
+
save_sols: bool = False
|
21
|
+
bigM: float = 1000.0
|
22
|
+
dt_var: str = "Dt"
|
23
|
+
_defaults: dict[str, bool] = field(default_factory=dict, repr=False)
|
24
|
+
|
25
|
+
def __post_init__(self) -> None:
|
26
|
+
# Set all defaults to False and if the value is None then it will be updated to true
|
27
|
+
self._defaults = {
|
28
|
+
"domain": False,
|
29
|
+
"instance": False,
|
30
|
+
"horizon": False,
|
31
|
+
"epsilon": False,
|
32
|
+
"gap": False
|
33
|
+
}
|
34
|
+
if self.horizon is None:
|
35
|
+
print("Horizon is not provided, and is set to 1")
|
36
|
+
self.horizon = 1
|
37
|
+
self._defaults["horizon"] = True
|
38
|
+
|
39
|
+
if self.epsilon is None:
|
40
|
+
print("Epsilon is not provided, and is set to 0.1")
|
41
|
+
self.epsilon = 0.1
|
42
|
+
self._defaults["epsilon"] = True
|
43
|
+
|
44
|
+
if self.gap is None:
|
45
|
+
print("Gap is not provided, and is set to 10.0%")
|
46
|
+
self.gap = 0.1
|
47
|
+
self._defaults["gap"] = True
|
48
|
+
|
49
|
+
def __str__(self) -> str:
|
50
|
+
text = f"""
|
51
|
+
Configuration:
|
52
|
+
|
53
|
+
Display SCIP Output: {self.show_output}
|
54
|
+
Save Solutions: {self.show_output}
|
55
|
+
Dt Variable Name: {self.dt_var}
|
56
|
+
|
57
|
+
Domain (str): {self.domain}
|
58
|
+
Instance (int): {self.instance}
|
59
|
+
Horizon (int): {self.horizon} {'(default)' if self._defaults['horizon'] is True else ''}
|
60
|
+
Epsilon (float): {self.epsilon} {'(default)' if self._defaults['epsilon'] is True else ''}
|
61
|
+
Gap (float): {self.gap * 100}% {'(default)' if self._defaults['gap'] is True else ''}
|
62
|
+
BigM (float): {self.bigM}
|
63
|
+
"""
|
64
|
+
return dedent(text)
|
65
|
+
|
66
|
+
def increment_horizon(self, value: int = 1):
|
67
|
+
self._defaults["horizon"] = False
|
68
|
+
self.horizon += value
|
69
|
+
|
70
|
+
|
71
|
+
@classmethod
|
72
|
+
def get_config(cls) -> Config:
|
73
|
+
parser = argparse.ArgumentParser(
|
74
|
+
prog="SCIPPlan"
|
75
|
+
)
|
76
|
+
parser.add_argument(
|
77
|
+
"-D",
|
78
|
+
"--domain",
|
79
|
+
required=True,
|
80
|
+
type=str,
|
81
|
+
help="This variable is the name of the domain (e.g. pandemic or navigation)"
|
82
|
+
)
|
83
|
+
parser.add_argument(
|
84
|
+
"-I",
|
85
|
+
"--instance",
|
86
|
+
required=True,
|
87
|
+
type=int,
|
88
|
+
help="This is the instance number of the domain (e.g. navigation has instances 1, 2 and 3)"
|
89
|
+
)
|
90
|
+
parser.add_argument(
|
91
|
+
"-H",
|
92
|
+
"--horizon",
|
93
|
+
required=False,
|
94
|
+
# default=1,
|
95
|
+
type=int,
|
96
|
+
help="The initial horizon. The solve method will initially begin with this horizon until it finds a feasible solution"
|
97
|
+
)
|
98
|
+
parser.add_argument(
|
99
|
+
"-E",
|
100
|
+
"--epsilon",
|
101
|
+
required=False,
|
102
|
+
# default=0.1,
|
103
|
+
type=float,
|
104
|
+
help="SCIPPlan iteratively checks solution for violations at each epsilon value"
|
105
|
+
)
|
106
|
+
parser.add_argument(
|
107
|
+
"-G",
|
108
|
+
"--gap",
|
109
|
+
required=False,
|
110
|
+
# default=0.1,
|
111
|
+
type=float,
|
112
|
+
help="SCIP will search for solution with an optimality gap by at least this value"
|
113
|
+
)
|
114
|
+
|
115
|
+
parser.add_argument(
|
116
|
+
"--bigM",
|
117
|
+
required=False,
|
118
|
+
default=1000.0,
|
119
|
+
type=float,
|
120
|
+
help="A large value which is used for some constraint encoding formulations, defaults to 1000.0 and can be changed as needed"
|
121
|
+
)
|
122
|
+
|
123
|
+
parser.add_argument(
|
124
|
+
"--dt-var",
|
125
|
+
required=False,
|
126
|
+
default="Dt",
|
127
|
+
type=str,
|
128
|
+
help="When writing the constraints, dt_var is the variable name for Dt, defaults to 'Dt' and can be changed based on users preference (e.g. 'dt')"
|
129
|
+
)
|
130
|
+
|
131
|
+
parser.add_argument(
|
132
|
+
"--show-output",
|
133
|
+
action="store_true",
|
134
|
+
default=False,
|
135
|
+
help="Include this flag to show output from SCIP"
|
136
|
+
)
|
137
|
+
|
138
|
+
parser.add_argument(
|
139
|
+
"--save-sols",
|
140
|
+
action="store_true",
|
141
|
+
default=False,
|
142
|
+
help="Include this flag to save the solutions from each of the scipplan iterations as well as constraints generated (note, only saves for horizon which has been solved)"
|
143
|
+
)
|
144
|
+
|
145
|
+
args = parser.parse_args()
|
146
|
+
|
147
|
+
return Config(**vars(args))
|
148
|
+
|
149
|
+
|
150
|
+
if __name__ == "__main__":
|
151
|
+
|
152
|
+
print(Config.get_config())
|
@@ -0,0 +1,30 @@
|
|
1
|
+
import os
|
2
|
+
import csv
|
3
|
+
|
4
|
+
from typing import Generator
|
5
|
+
from .config import Config
|
6
|
+
|
7
|
+
class InfeasibilityError(Exception):
|
8
|
+
"""Raise this error when there are no valid solutions for the given horizon"""
|
9
|
+
|
10
|
+
def iterate(start: float, stop: float, step: float = 1) -> Generator[float, None, None]:
|
11
|
+
n = start
|
12
|
+
while n <= stop:
|
13
|
+
yield n
|
14
|
+
n += step
|
15
|
+
|
16
|
+
|
17
|
+
def list_accessible_files(directory):
|
18
|
+
try:
|
19
|
+
files = os.listdir(directory)
|
20
|
+
return files
|
21
|
+
except FileNotFoundError:
|
22
|
+
return [] # Return an empty list if the directory doesn't exist
|
23
|
+
|
24
|
+
def write_to_csv(file_name: str, data: list[dict], config: Config) -> None:
|
25
|
+
with open(f"{file_name}_{config.domain}_{config.instance}.csv", 'w', encoding='utf8', newline='') as output_file:
|
26
|
+
fc = csv.DictWriter(output_file,
|
27
|
+
fieldnames=data[0].keys(),
|
28
|
+
)
|
29
|
+
fc.writeheader()
|
30
|
+
fc.writerows(data)
|