oxmpl-py 0.1.0__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.
- oxmpl_py-0.1.0/PKG-INFO +11 -0
- oxmpl_py-0.1.0/oxmpl/Cargo.lock +157 -0
- oxmpl_py-0.1.0/oxmpl/Cargo.toml +12 -0
- oxmpl_py-0.1.0/oxmpl/src/base/error.rs +89 -0
- oxmpl_py-0.1.0/oxmpl/src/base/goal.rs +16 -0
- oxmpl_py-0.1.0/oxmpl/src/base/mod.rs +9 -0
- oxmpl_py-0.1.0/oxmpl/src/base/planner.rs +17 -0
- oxmpl_py-0.1.0/oxmpl/src/base/problem_definition.rs +9 -0
- oxmpl_py-0.1.0/oxmpl/src/base/space.rs +38 -0
- oxmpl_py-0.1.0/oxmpl/src/base/spaces/mod.rs +2 -0
- oxmpl_py-0.1.0/oxmpl/src/base/spaces/real_vector_state_space.rs +150 -0
- oxmpl_py-0.1.0/oxmpl/src/base/spaces/so2_state_space.rs +48 -0
- oxmpl_py-0.1.0/oxmpl/src/base/state.rs +70 -0
- oxmpl_py-0.1.0/oxmpl/src/base/unittests/rvss_tests.rs +304 -0
- oxmpl_py-0.1.0/oxmpl/src/base/unittests/so2ss_tests.rs +192 -0
- oxmpl_py-0.1.0/oxmpl/src/base/validity.rs +144 -0
- oxmpl_py-0.1.0/oxmpl/src/geometric/mod.rs +1 -0
- oxmpl_py-0.1.0/oxmpl/src/geometric/planners/mod.rs +1 -0
- oxmpl_py-0.1.0/oxmpl/src/geometric/planners/rrt/mod.rs +2 -0
- oxmpl_py-0.1.0/oxmpl/src/geometric/planners/rrt/rrt.rs +190 -0
- oxmpl_py-0.1.0/oxmpl/src/lib.rs +2 -0
- oxmpl_py-0.1.0/oxmpl/tests/rrt_tests.rs +195 -0
- oxmpl_py-0.1.0/oxmpl-py/Cargo.lock +280 -0
- oxmpl_py-0.1.0/oxmpl-py/Cargo.toml +16 -0
- oxmpl_py-0.1.0/oxmpl-py/src/base.rs +201 -0
- oxmpl_py-0.1.0/oxmpl-py/src/geometric.rs +31 -0
- oxmpl_py-0.1.0/oxmpl-py/src/lib.rs +19 -0
- oxmpl_py-0.1.0/oxmpl-py/src/macros.rs +63 -0
- oxmpl_py-0.1.0/oxmpl-py/tests/test_rrt_integration.py +119 -0
- oxmpl_py-0.1.0/pyproject.toml +22 -0
oxmpl_py-0.1.0/PKG-INFO
ADDED
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: oxmpl_py
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Classifier: Programming Language :: Rust
|
|
5
|
+
Classifier: Programming Language :: Python :: Implementation :: CPython
|
|
6
|
+
Classifier: Programming Language :: Python :: Implementation :: PyPy
|
|
7
|
+
Summary: Python Bindings for the OxMPL Library
|
|
8
|
+
Author: Junior Sundar <juniorsundar@gmail.com>
|
|
9
|
+
Author-email: Junior Sundar <juniorsundar@gmail.com>
|
|
10
|
+
License: BSD-3
|
|
11
|
+
Requires-Python: >=3.8
|
|
@@ -0,0 +1,157 @@
|
|
|
1
|
+
# This file is automatically @generated by Cargo.
|
|
2
|
+
# It is not intended for manual editing.
|
|
3
|
+
version = 4
|
|
4
|
+
|
|
5
|
+
[[package]]
|
|
6
|
+
name = "bitflags"
|
|
7
|
+
version = "2.9.1"
|
|
8
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
9
|
+
checksum = "1b8e56985ec62d17e9c1001dc89c88ecd7dc08e47eba5ec7c29c7b5eeecde967"
|
|
10
|
+
|
|
11
|
+
[[package]]
|
|
12
|
+
name = "cfg-if"
|
|
13
|
+
version = "1.0.1"
|
|
14
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
15
|
+
checksum = "9555578bc9e57714c812a1f84e4fc5b4d21fcb063490c624de019f7464c91268"
|
|
16
|
+
|
|
17
|
+
[[package]]
|
|
18
|
+
name = "getrandom"
|
|
19
|
+
version = "0.3.3"
|
|
20
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
21
|
+
checksum = "26145e563e54f2cadc477553f1ec5ee650b00862f0a58bcd12cbdc5f0ea2d2f4"
|
|
22
|
+
dependencies = [
|
|
23
|
+
"cfg-if",
|
|
24
|
+
"libc",
|
|
25
|
+
"r-efi",
|
|
26
|
+
"wasi",
|
|
27
|
+
]
|
|
28
|
+
|
|
29
|
+
[[package]]
|
|
30
|
+
name = "libc"
|
|
31
|
+
version = "0.2.172"
|
|
32
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
33
|
+
checksum = "d750af042f7ef4f724306de029d18836c26c1765a54a6a3f094cbd23a7267ffa"
|
|
34
|
+
|
|
35
|
+
[[package]]
|
|
36
|
+
name = "oxmpl"
|
|
37
|
+
version = "0.1.0"
|
|
38
|
+
dependencies = [
|
|
39
|
+
"rand",
|
|
40
|
+
]
|
|
41
|
+
|
|
42
|
+
[[package]]
|
|
43
|
+
name = "ppv-lite86"
|
|
44
|
+
version = "0.2.21"
|
|
45
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
46
|
+
checksum = "85eae3c4ed2f50dcfe72643da4befc30deadb458a9b590d720cde2f2b1e97da9"
|
|
47
|
+
dependencies = [
|
|
48
|
+
"zerocopy",
|
|
49
|
+
]
|
|
50
|
+
|
|
51
|
+
[[package]]
|
|
52
|
+
name = "proc-macro2"
|
|
53
|
+
version = "1.0.95"
|
|
54
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
55
|
+
checksum = "02b3e5e68a3a1a02aad3ec490a98007cbc13c37cbe84a3cd7b8e406d76e7f778"
|
|
56
|
+
dependencies = [
|
|
57
|
+
"unicode-ident",
|
|
58
|
+
]
|
|
59
|
+
|
|
60
|
+
[[package]]
|
|
61
|
+
name = "quote"
|
|
62
|
+
version = "1.0.40"
|
|
63
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
64
|
+
checksum = "1885c039570dc00dcb4ff087a89e185fd56bae234ddc7f056a945bf36467248d"
|
|
65
|
+
dependencies = [
|
|
66
|
+
"proc-macro2",
|
|
67
|
+
]
|
|
68
|
+
|
|
69
|
+
[[package]]
|
|
70
|
+
name = "r-efi"
|
|
71
|
+
version = "5.2.0"
|
|
72
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
73
|
+
checksum = "74765f6d916ee2faa39bc8e68e4f3ed8949b48cccdac59983d287a7cb71ce9c5"
|
|
74
|
+
|
|
75
|
+
[[package]]
|
|
76
|
+
name = "rand"
|
|
77
|
+
version = "0.9.1"
|
|
78
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
79
|
+
checksum = "9fbfd9d094a40bf3ae768db9361049ace4c0e04a4fd6b359518bd7b73a73dd97"
|
|
80
|
+
dependencies = [
|
|
81
|
+
"rand_chacha",
|
|
82
|
+
"rand_core",
|
|
83
|
+
]
|
|
84
|
+
|
|
85
|
+
[[package]]
|
|
86
|
+
name = "rand_chacha"
|
|
87
|
+
version = "0.9.0"
|
|
88
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
89
|
+
checksum = "d3022b5f1df60f26e1ffddd6c66e8aa15de382ae63b3a0c1bfc0e4d3e3f325cb"
|
|
90
|
+
dependencies = [
|
|
91
|
+
"ppv-lite86",
|
|
92
|
+
"rand_core",
|
|
93
|
+
]
|
|
94
|
+
|
|
95
|
+
[[package]]
|
|
96
|
+
name = "rand_core"
|
|
97
|
+
version = "0.9.3"
|
|
98
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
99
|
+
checksum = "99d9a13982dcf210057a8a78572b2217b667c3beacbf3a0d8b454f6f82837d38"
|
|
100
|
+
dependencies = [
|
|
101
|
+
"getrandom",
|
|
102
|
+
]
|
|
103
|
+
|
|
104
|
+
[[package]]
|
|
105
|
+
name = "syn"
|
|
106
|
+
version = "2.0.103"
|
|
107
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
108
|
+
checksum = "e4307e30089d6fd6aff212f2da3a1f9e32f3223b1f010fb09b7c95f90f3ca1e8"
|
|
109
|
+
dependencies = [
|
|
110
|
+
"proc-macro2",
|
|
111
|
+
"quote",
|
|
112
|
+
"unicode-ident",
|
|
113
|
+
]
|
|
114
|
+
|
|
115
|
+
[[package]]
|
|
116
|
+
name = "unicode-ident"
|
|
117
|
+
version = "1.0.18"
|
|
118
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
119
|
+
checksum = "5a5f39404a5da50712a4c1eecf25e90dd62b613502b7e925fd4e4d19b5c96512"
|
|
120
|
+
|
|
121
|
+
[[package]]
|
|
122
|
+
name = "wasi"
|
|
123
|
+
version = "0.14.2+wasi-0.2.4"
|
|
124
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
125
|
+
checksum = "9683f9a5a998d873c0d21fcbe3c083009670149a8fab228644b8bd36b2c48cb3"
|
|
126
|
+
dependencies = [
|
|
127
|
+
"wit-bindgen-rt",
|
|
128
|
+
]
|
|
129
|
+
|
|
130
|
+
[[package]]
|
|
131
|
+
name = "wit-bindgen-rt"
|
|
132
|
+
version = "0.39.0"
|
|
133
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
134
|
+
checksum = "6f42320e61fe2cfd34354ecb597f86f413484a798ba44a8ca1165c58d42da6c1"
|
|
135
|
+
dependencies = [
|
|
136
|
+
"bitflags",
|
|
137
|
+
]
|
|
138
|
+
|
|
139
|
+
[[package]]
|
|
140
|
+
name = "zerocopy"
|
|
141
|
+
version = "0.8.25"
|
|
142
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
143
|
+
checksum = "a1702d9583232ddb9174e01bb7c15a2ab8fb1bc6f227aa1233858c351a3ba0cb"
|
|
144
|
+
dependencies = [
|
|
145
|
+
"zerocopy-derive",
|
|
146
|
+
]
|
|
147
|
+
|
|
148
|
+
[[package]]
|
|
149
|
+
name = "zerocopy-derive"
|
|
150
|
+
version = "0.8.25"
|
|
151
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
152
|
+
checksum = "28a6e20d751156648aa063f3800b706ee209a32c0b4d9f24be3d980b01be55ef"
|
|
153
|
+
dependencies = [
|
|
154
|
+
"proc-macro2",
|
|
155
|
+
"quote",
|
|
156
|
+
"syn",
|
|
157
|
+
]
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
use std::{error, fmt};
|
|
2
|
+
|
|
3
|
+
#[derive(Debug, PartialEq)]
|
|
4
|
+
pub enum StateSpaceError {
|
|
5
|
+
/// The length of the provided bounds does not match the dimension.
|
|
6
|
+
DimensionMismatch { expected: usize, found: usize },
|
|
7
|
+
/// A lower bound is greater than or equal to its corresponding upper bound.
|
|
8
|
+
InvalidBound { lower: f64, upper: f64 },
|
|
9
|
+
/// A 0-dimensional space was requested without explicit (empty) bounds.
|
|
10
|
+
ZeroDimensionUnbounded,
|
|
11
|
+
}
|
|
12
|
+
impl fmt::Display for StateSpaceError {
|
|
13
|
+
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
|
14
|
+
match self {
|
|
15
|
+
Self::DimensionMismatch { expected, found } => write!(
|
|
16
|
+
f,
|
|
17
|
+
"provided bounds length ({found}) does not match specified dimension ({expected})."
|
|
18
|
+
),
|
|
19
|
+
Self::InvalidBound { lower, upper } => {
|
|
20
|
+
write!(
|
|
21
|
+
f,
|
|
22
|
+
"Lower bound {lower} is greater than upper bound {upper}."
|
|
23
|
+
)
|
|
24
|
+
}
|
|
25
|
+
Self::ZeroDimensionUnbounded => {
|
|
26
|
+
write!(f, "Cannot create 0-dimensional unbounded space.")
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
impl error::Error for StateSpaceError {}
|
|
32
|
+
|
|
33
|
+
#[derive(Debug, PartialEq)]
|
|
34
|
+
pub enum StateSamplingError {
|
|
35
|
+
/// When trying to sample a space that isn't bounded.
|
|
36
|
+
UnboundedDimension { dimension_index: usize },
|
|
37
|
+
/// The space or region to be sampled has no volume (e.g., all dimensions have min >= max).
|
|
38
|
+
ZeroVolume,
|
|
39
|
+
/// It was not possible to generate a sample that satisfies the goal conditions.
|
|
40
|
+
GoalRegionUnsatisfiable,
|
|
41
|
+
/// An iterative sampling attempt (like for a goal region) failed to find a sample within a set number of attempts.
|
|
42
|
+
GoalSamplingTimeout { attempts: u32 },
|
|
43
|
+
}
|
|
44
|
+
impl fmt::Display for StateSamplingError {
|
|
45
|
+
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
|
46
|
+
match self {
|
|
47
|
+
Self::UnboundedDimension { dimension_index } => {
|
|
48
|
+
write!(
|
|
49
|
+
f,
|
|
50
|
+
"Cannot sample uniformly because dimension {dimension_index} is unbounded."
|
|
51
|
+
)
|
|
52
|
+
}
|
|
53
|
+
Self::ZeroVolume => {
|
|
54
|
+
write!(f, "Cannot sample from a region with zero volume.")
|
|
55
|
+
}
|
|
56
|
+
Self::GoalRegionUnsatisfiable => {
|
|
57
|
+
write!(f, "Could not generate a sample from the goal region because its constraints may be unsatisfiable.")
|
|
58
|
+
}
|
|
59
|
+
Self::GoalSamplingTimeout { attempts } => {
|
|
60
|
+
write!(
|
|
61
|
+
f,
|
|
62
|
+
"Failed to generate a goal sample within {attempts} attempts."
|
|
63
|
+
)
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
impl error::Error for StateSamplingError {}
|
|
69
|
+
|
|
70
|
+
#[derive(Debug, PartialEq)]
|
|
71
|
+
pub enum PlanningError {
|
|
72
|
+
/// Solution search timed-out.
|
|
73
|
+
Timeout,
|
|
74
|
+
/// No solution found.
|
|
75
|
+
NoSolutionFound,
|
|
76
|
+
}
|
|
77
|
+
impl fmt::Display for PlanningError {
|
|
78
|
+
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
|
79
|
+
match self {
|
|
80
|
+
Self::Timeout => {
|
|
81
|
+
write!(f, "No solution found within timeout.")
|
|
82
|
+
}
|
|
83
|
+
Self::NoSolutionFound => {
|
|
84
|
+
write!(f, "No solution found.")
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
impl error::Error for PlanningError {}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
use crate::base::{error::StateSamplingError, state::State};
|
|
2
|
+
use rand::Rng;
|
|
3
|
+
|
|
4
|
+
pub trait Goal<S: State> {
|
|
5
|
+
fn is_satisfied(&self, state: &S) -> bool;
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
pub trait GoalRegion<S: State>: Goal<S> {
|
|
9
|
+
/// Returns the distance from a state to the goal region.
|
|
10
|
+
fn distance_goal(&self, state: &S) -> f64;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
pub trait GoalSampleableRegion<S: State>: GoalRegion<S> {
|
|
14
|
+
/// Generates a state from the goal region.
|
|
15
|
+
fn sample_goal(&self, rng: &mut impl Rng) -> Result<S, StateSamplingError>;
|
|
16
|
+
}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
use crate::base::{
|
|
2
|
+
error::PlanningError, goal::Goal, problem_definition::ProblemDefinition, space::StateSpace,
|
|
3
|
+
state::State, validity::StateValidityChecker,
|
|
4
|
+
};
|
|
5
|
+
use std::{sync::Arc, time::Duration};
|
|
6
|
+
|
|
7
|
+
#[derive(Clone)]
|
|
8
|
+
pub struct Path<S: State>(pub Vec<S>);
|
|
9
|
+
|
|
10
|
+
pub trait Planner<S: State, SP: StateSpace<StateType = S>, G: Goal<S>> {
|
|
11
|
+
fn setup(
|
|
12
|
+
&mut self,
|
|
13
|
+
problem_def: Arc<ProblemDefinition<S, SP, G>>,
|
|
14
|
+
validity_checker: Arc<dyn StateValidityChecker<S> + Send + Sync>,
|
|
15
|
+
);
|
|
16
|
+
fn solve(&mut self, timeout: Duration) -> Result<Path<S>, PlanningError>;
|
|
17
|
+
}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
use crate::base::{goal::Goal, space::StateSpace, state::State};
|
|
2
|
+
use std::sync::Arc;
|
|
3
|
+
|
|
4
|
+
pub struct ProblemDefinition<S: State, SP: StateSpace<StateType = S>, G: Goal<S>> {
|
|
5
|
+
pub space: Arc<SP>,
|
|
6
|
+
pub start_states: Vec<S>,
|
|
7
|
+
pub goal: Arc<G>,
|
|
8
|
+
// Potentially state_validity_checker here if not passed directly to planner
|
|
9
|
+
}
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
use rand::Rng;
|
|
2
|
+
|
|
3
|
+
use crate::base::{error::StateSamplingError, state::State};
|
|
4
|
+
|
|
5
|
+
pub use crate::base::spaces::{
|
|
6
|
+
real_vector_state_space::RealVectorStateSpace,
|
|
7
|
+
so2_state_space::SO2StateSpace,
|
|
8
|
+
};
|
|
9
|
+
|
|
10
|
+
pub trait StateSpace {
|
|
11
|
+
/// StateType defines what is acceptable in current StateSpace
|
|
12
|
+
type StateType: State;
|
|
13
|
+
/// Find distance between current state1 and target state2. Depends on StateSpace.
|
|
14
|
+
fn distance(&self, state1: &Self::StateType, state2: &Self::StateType) -> f64;
|
|
15
|
+
/// Find state interpolated between `from` and `to` states given 0<=`t`<=1.
|
|
16
|
+
fn interpolate(
|
|
17
|
+
&self,
|
|
18
|
+
from: &Self::StateType,
|
|
19
|
+
to: &Self::StateType,
|
|
20
|
+
t: f64,
|
|
21
|
+
state: &mut Self::StateType,
|
|
22
|
+
);
|
|
23
|
+
/// Modifies the given state to ensure it conforms to the space's defined bounds.
|
|
24
|
+
fn enforce_bounds(&self, state: &mut Self::StateType);
|
|
25
|
+
|
|
26
|
+
fn satisfies_bounds(&self, state: &Self::StateType) -> bool;
|
|
27
|
+
|
|
28
|
+
/// Generates a state uniformly at random from the entire state space.
|
|
29
|
+
fn sample_uniform(&self, rng: &mut impl Rng) -> Result<Self::StateType, StateSamplingError>;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
// #[cfg(test)]
|
|
33
|
+
// #[path = "unittests/rvss_tests.rs"]
|
|
34
|
+
// mod rvss_tests;
|
|
35
|
+
|
|
36
|
+
// #[cfg(test)
|
|
37
|
+
// #[path = "unittests/so2ss_tests.rs"]
|
|
38
|
+
// mod so2ss_tests;
|
|
@@ -0,0 +1,150 @@
|
|
|
1
|
+
use rand::Rng;
|
|
2
|
+
|
|
3
|
+
use crate::base::{
|
|
4
|
+
error::{StateSamplingError, StateSpaceError}, space::StateSpace, state::RealVectorState
|
|
5
|
+
};
|
|
6
|
+
|
|
7
|
+
pub struct RealVectorStateSpace {
|
|
8
|
+
/// n-Dimensionality of VectorStateSpace i.e. R^n.
|
|
9
|
+
pub dimension: usize,
|
|
10
|
+
pub bounds: Vec<(f64, f64)>,
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
impl RealVectorStateSpace {
|
|
14
|
+
pub fn new(
|
|
15
|
+
dimension: usize,
|
|
16
|
+
bounds_option: Option<Vec<(f64, f64)>>,
|
|
17
|
+
) -> Result<Self, StateSpaceError> {
|
|
18
|
+
let bounds = match bounds_option {
|
|
19
|
+
Some(explicit_bounds) => {
|
|
20
|
+
if explicit_bounds.len() != dimension {
|
|
21
|
+
return Err(StateSpaceError::DimensionMismatch {
|
|
22
|
+
expected: dimension,
|
|
23
|
+
found: explicit_bounds.len(),
|
|
24
|
+
});
|
|
25
|
+
}
|
|
26
|
+
for bound in &explicit_bounds {
|
|
27
|
+
if bound.0 >= bound.1 {
|
|
28
|
+
return Err(StateSpaceError::InvalidBound {
|
|
29
|
+
lower: bound.0,
|
|
30
|
+
upper: bound.1,
|
|
31
|
+
});
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
explicit_bounds
|
|
35
|
+
}
|
|
36
|
+
None => {
|
|
37
|
+
if dimension == 0 {
|
|
38
|
+
return Err(StateSpaceError::ZeroDimensionUnbounded);
|
|
39
|
+
}
|
|
40
|
+
vec![(f64::NEG_INFINITY, f64::INFINITY); dimension]
|
|
41
|
+
}
|
|
42
|
+
};
|
|
43
|
+
|
|
44
|
+
Ok(Self { dimension, bounds })
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
impl StateSpace for RealVectorStateSpace {
|
|
49
|
+
type StateType = RealVectorState;
|
|
50
|
+
|
|
51
|
+
/// Find distance between current state1 and target state2. Depends on StateSpace.
|
|
52
|
+
/// In RealVectorStateSpace, this refers to the L2-norm.
|
|
53
|
+
fn distance(&self, state1: &Self::StateType, state2: &Self::StateType) -> f64 {
|
|
54
|
+
assert_eq!(
|
|
55
|
+
state1.values.len(),
|
|
56
|
+
self.dimension,
|
|
57
|
+
"State1 has incorrect dimension for this space."
|
|
58
|
+
);
|
|
59
|
+
assert_eq!(
|
|
60
|
+
state2.values.len(),
|
|
61
|
+
self.dimension,
|
|
62
|
+
"State2 has incorrect dimension for this space."
|
|
63
|
+
);
|
|
64
|
+
state1
|
|
65
|
+
.values
|
|
66
|
+
.iter()
|
|
67
|
+
.zip(state2.values.iter())
|
|
68
|
+
.map(|(v1, v2)| (v1 - v2).powi(2))
|
|
69
|
+
.sum::<f64>()
|
|
70
|
+
.sqrt()
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
fn interpolate(
|
|
74
|
+
&self,
|
|
75
|
+
from: &Self::StateType,
|
|
76
|
+
to: &Self::StateType,
|
|
77
|
+
t: f64,
|
|
78
|
+
out_state: &mut Self::StateType,
|
|
79
|
+
) {
|
|
80
|
+
assert_eq!(
|
|
81
|
+
from.values.len(),
|
|
82
|
+
self.dimension,
|
|
83
|
+
"From-state has incorrect dimension."
|
|
84
|
+
);
|
|
85
|
+
assert_eq!(
|
|
86
|
+
to.values.len(),
|
|
87
|
+
self.dimension,
|
|
88
|
+
"To-state has incorrect dimension."
|
|
89
|
+
);
|
|
90
|
+
assert_eq!(
|
|
91
|
+
out_state.values.len(),
|
|
92
|
+
self.dimension,
|
|
93
|
+
"Out-state has incorrect dimension or not properly initialized."
|
|
94
|
+
);
|
|
95
|
+
for i in 0..from.values.len() {
|
|
96
|
+
out_state.values[i] = from.values[i] + (to.values[i] - from.values[i]) * t;
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
fn enforce_bounds(&self, state: &mut Self::StateType) {
|
|
101
|
+
if state.values.len() != self.dimension {
|
|
102
|
+
assert_eq!(
|
|
103
|
+
state.values.len(),
|
|
104
|
+
self.dimension,
|
|
105
|
+
"State and space dimension mismatch when enforcing bounds."
|
|
106
|
+
);
|
|
107
|
+
}
|
|
108
|
+
for (i, value) in state.values.iter_mut().enumerate() {
|
|
109
|
+
if i < self.bounds.len() {
|
|
110
|
+
let (lower, upper) = self.bounds[i];
|
|
111
|
+
*value = value.clamp(lower, upper);
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
fn satisfies_bounds(&self, state: &Self::StateType) -> bool {
|
|
117
|
+
if state.values.len() != self.dimension {
|
|
118
|
+
assert_eq!(
|
|
119
|
+
state.values.len(),
|
|
120
|
+
self.dimension,
|
|
121
|
+
"State and space dimension mismatch when checking bound satisfaction."
|
|
122
|
+
);
|
|
123
|
+
}
|
|
124
|
+
for i in 0..self.dimension {
|
|
125
|
+
let (lower, upper) = self.bounds[i];
|
|
126
|
+
if state.values[i] - f64::EPSILON > upper || state.values[i] + f64::EPSILON < lower {
|
|
127
|
+
return false;
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
true
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
fn sample_uniform(&self, rng: &mut impl Rng) -> Result<Self::StateType, StateSamplingError> {
|
|
134
|
+
let mut values = Vec::with_capacity(self.dimension);
|
|
135
|
+
|
|
136
|
+
for i in 0..self.dimension {
|
|
137
|
+
let (lower, upper) = self.bounds[i];
|
|
138
|
+
|
|
139
|
+
if !lower.is_finite() || !upper.is_finite() {
|
|
140
|
+
return Err(StateSamplingError::UnboundedDimension { dimension_index: i });
|
|
141
|
+
}
|
|
142
|
+
if lower >= upper {
|
|
143
|
+
return Err(StateSamplingError::ZeroVolume);
|
|
144
|
+
}
|
|
145
|
+
values.push(rng.random_range(lower..upper));
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
Ok(RealVectorState { values })
|
|
149
|
+
}
|
|
150
|
+
}
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
use std::f64::consts::PI;
|
|
2
|
+
use rand::Rng;
|
|
3
|
+
|
|
4
|
+
use crate::base::{error::StateSamplingError, space::StateSpace, state::SO2State};
|
|
5
|
+
|
|
6
|
+
pub struct SO2StateSpace {
|
|
7
|
+
// pub bounds: Vec<(f64, f64)>,
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
impl StateSpace for SO2StateSpace {
|
|
11
|
+
type StateType = SO2State;
|
|
12
|
+
|
|
13
|
+
fn distance(&self, state1: &Self::StateType, state2: &Self::StateType) -> f64 {
|
|
14
|
+
let mut diff = state1.value - state2.value;
|
|
15
|
+
diff = (diff + PI).rem_euclid(2.0 * PI) - PI;
|
|
16
|
+
diff.abs()
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
fn interpolate(
|
|
20
|
+
&self,
|
|
21
|
+
from: &Self::StateType,
|
|
22
|
+
to: &Self::StateType,
|
|
23
|
+
t: f64,
|
|
24
|
+
out_state: &mut Self::StateType,
|
|
25
|
+
) {
|
|
26
|
+
let mut diff_to_from = to.clone().normalise().value - from.clone().normalise().value;
|
|
27
|
+
if diff_to_from > PI {
|
|
28
|
+
diff_to_from -= 2.0 * PI;
|
|
29
|
+
} else if diff_to_from < -PI {
|
|
30
|
+
diff_to_from += 2.0 * PI;
|
|
31
|
+
}
|
|
32
|
+
out_state.value = from.value + diff_to_from * t;
|
|
33
|
+
out_state.value = out_state.normalise().value;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
// TODO
|
|
37
|
+
fn enforce_bounds(&self, _state: &mut Self::StateType) {}
|
|
38
|
+
|
|
39
|
+
// TODO
|
|
40
|
+
fn satisfies_bounds(&self, _state: &Self::StateType) -> bool {
|
|
41
|
+
false
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
fn sample_uniform(&self, rng: &mut impl Rng) -> Result<SO2State, StateSamplingError> {
|
|
45
|
+
todo!()
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
use std::f64::consts::PI;
|
|
2
|
+
|
|
3
|
+
pub trait State: Clone + Send + Sync + 'static {}
|
|
4
|
+
|
|
5
|
+
#[derive(Clone, Debug, PartialEq)]
|
|
6
|
+
pub struct RealVectorState {
|
|
7
|
+
pub values: Vec<f64>,
|
|
8
|
+
}
|
|
9
|
+
impl State for RealVectorState {}
|
|
10
|
+
|
|
11
|
+
#[derive(Clone, Debug, PartialEq)]
|
|
12
|
+
pub struct SO2State {
|
|
13
|
+
pub value: f64,
|
|
14
|
+
}
|
|
15
|
+
impl SO2State {
|
|
16
|
+
pub fn new(val: f64) -> Self {
|
|
17
|
+
SO2State {
|
|
18
|
+
value: (val + PI).rem_euclid(2.0 * PI) - PI,
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
pub fn normalise(&mut self) -> Self {
|
|
23
|
+
SO2State {
|
|
24
|
+
value: (self.value + PI).rem_euclid(2.0 * PI) - PI,
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
impl State for SO2State {}
|
|
29
|
+
|
|
30
|
+
#[cfg(test)]
|
|
31
|
+
mod tests {
|
|
32
|
+
use super::*;
|
|
33
|
+
|
|
34
|
+
#[test]
|
|
35
|
+
fn test_real_vector_state_creation() {
|
|
36
|
+
let state = RealVectorState {
|
|
37
|
+
values: vec![1.0, 2.0],
|
|
38
|
+
};
|
|
39
|
+
assert_eq!(state.values, vec![1.0, 2.0]);
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
#[test]
|
|
43
|
+
fn test_real_vector_state_clone() {
|
|
44
|
+
let state1 = RealVectorState {
|
|
45
|
+
values: vec![1.0, 2.0],
|
|
46
|
+
};
|
|
47
|
+
let state2 = state1.clone();
|
|
48
|
+
assert_eq!(state1, state2);
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
#[test]
|
|
52
|
+
fn test_so2_state_creation() {
|
|
53
|
+
let state = SO2State { value: 1.0 };
|
|
54
|
+
assert_eq!(state.value, 1.0);
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
#[test]
|
|
58
|
+
fn test_so2_state_clone() {
|
|
59
|
+
let state1 = SO2State { value: 1.0 };
|
|
60
|
+
let state2 = state1.clone();
|
|
61
|
+
assert_eq!(state1, state2);
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
#[test]
|
|
65
|
+
fn test_so2_state_normalise() {
|
|
66
|
+
let mut state1 = SO2State { value: 3.0 * PI / 2.0 };
|
|
67
|
+
let state2 = state1.normalise();
|
|
68
|
+
assert_eq!(state2.value, -PI / 2.0);
|
|
69
|
+
}
|
|
70
|
+
}
|