├── .codeclimate.yml ├── .gitignore ├── .travis.yml ├── Cargo.toml ├── LICENSE ├── README.md ├── logo.png ├── rustfmt.toml └── src ├── arrays.rs ├── discrete.rs ├── interval.rs ├── lib.rs ├── ops ├── intersection.rs ├── mod.rs └── union.rs ├── option.rs ├── partitions.rs ├── real.rs └── tuples.rs /.codeclimate.yml: -------------------------------------------------------------------------------- 1 | engines: 2 | fixme: 3 | enabled: true 4 | 5 | ratings: 6 | paths: 7 | - src/** 8 | - examples/** 9 | 10 | exclude_paths: 11 | - target/ 12 | - extern/ 13 | - Cargo.* 14 | - rustfmt.toml 15 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Generated by Cargo 2 | # will have compiled files and executables 3 | /target/ 4 | 5 | # Remove Cargo.lock from gitignore if creating an executable, leave it for libraries 6 | # More information here http://doc.crates.io/guide.html#cargotoml-vs-cargolock 7 | Cargo.lock 8 | 9 | # These are backup files generated by rustfmt 10 | **/*.rs.bk 11 | 12 | # This is a debugging file used for development 13 | examples/sandbox.rs 14 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: rust 2 | dist: trusty 3 | sudo: required 4 | 5 | # Cache cargo symbols for faster build 6 | cache: cargo 7 | 8 | addons: 9 | apt: 10 | packages: 11 | - libcurl4-openssl-dev 12 | - libelf-dev 13 | - libdw-dev 14 | - binutils-dev 15 | - gfortran 16 | - libopenblas-dev 17 | - cmake 18 | sources: 19 | - kalakris-cmake 20 | 21 | rust: 22 | - stable 23 | 24 | before_script: 25 | - export PATH=$HOME/.cargo/bin:$PATH 26 | - cargo install cargo-update || echo "cargo-update already installed" 27 | - cargo install cargo-travis || echo "cargo-travis already installed" 28 | - cargo install-update -a # update outdated cached binaries 29 | 30 | script: 31 | - | 32 | cargo build && 33 | cargo test #&& 34 | # cargo bench && 35 | # cargo --only stable doc 36 | 37 | after_success: 38 | - cargo coveralls 39 | - | 40 | [ $TRAVIS_BRANCH = master ] && 41 | [ $TRAVIS_PULL_REQUEST = false ] && 42 | cargo doc --no-deps && 43 | echo "" > target/doc/index.html && 44 | pip install --user ghp-import && 45 | /home/travis/.local/bin/ghp-import -n target/doc && 46 | git push -fq https://${GH_TOKEN}@github.com/${TRAVIS_REPO_SLUG}.git gh-pages 47 | 48 | env: 49 | global: 50 | - TRAVIS_CARGO_NIGHTLY_FEATURE="" 51 | - secure: AcBwzPJGszAqn4hW891+ZzbcxVA5wETXUmBg61cGKegHhFKBssB7HaNTcVX1pL3lHUiNGiLKReM+xbMPbth9c3ttz1SKlVsDJbQzt/lNaOjMmdkXd/0PNC7ZWT3yXx8sIbuyz2XCL6TW3dkvYt3G2+Ioy0BxeRepEbfhm1NEMWHoQXbLVkFiH4XtpfADY4DENWiuUbVA3/8+Qwc9R7i4mIf7J8EQWc1+HcNhuy556FrmkkAIDvRU2h0sl9HhjMrTHIKPalfXuriW4pQbZHH+JbA9Xh8MbX230PMqZaELxYkh020Fxy4z2z0VvISmLoMBFQb6JSh11AWQsFno1TZkVqhG2yl1tBPvdKgaqrBLZ8MsES8bG5i/LVDv2kM+dmbtWQmRmn6PnvBmJeJW5bbYx4F2PW57mDUebK/TKEF+1HbBR9KGm2fIihk46mvhxxpQ3ZE7A8lDs17Up9ZBhhYM5RJJ174Ls1Yn+qmtdhEAe5IJ4x5xRNYraTlKJHy2FCgqTeLEKB8TzDB0DUhaDMY6EnuOJdZFjEk5QSzSZJBKNSfyqO+N43vloWt3lszscekgQtYHATvDhylYFrIdRFPrqB3/BYIYOHEzZblTCSres8zmMBTfnWyAn37Z4y4fWsq3NUXcZkOd29HfjAi8qa7vVDQ4d1e1c0RCPOSKiCb7zP0= 52 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "spaces" 3 | description = "Set/space primitives for defining machine learning problems." 4 | 5 | version = "6.0.0" 6 | authors = ["Tom Spooner "] 7 | 8 | readme = "README.md" 9 | license-file = "LICENSE" 10 | 11 | keywords = ["geometry", "vector", "spaces", "machine", "learning"] 12 | 13 | repository = "https://github.com/tspooner/spaces" 14 | documentation = "https://docs.rs/spaces" 15 | 16 | [badges] 17 | travis-ci = { repository = "tspooner/spaces", branch = "master" } 18 | coveralls = { repository = "tspooner/spaces", branch = "master", service = "github" } 19 | 20 | [features] 21 | default = [] 22 | 23 | serde = ["intervals/serde"] 24 | 25 | [dependencies] 26 | intervals = "2.1" 27 | itertools = "0.10" 28 | num-traits = "0.2" 29 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2017 Tom Spooner 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | 2 | 3 | # spaces ([api](https://docs.rs/spaces)) 4 | 5 | [![Crates.io](https://img.shields.io/crates/v/spaces.svg)](https://crates.io/crates/spaces) 6 | [![Build Status](https://travis-ci.org/tspooner/spaces.svg?branch=master)](https://travis-ci.org/tspooner/spaces) 7 | [![Coverage Status](https://coveralls.io/repos/github/tspooner/spaces/badge.svg?branch=master)](https://coveralls.io/github/tspooner/spaces?branch=master) 8 | 9 | ## Overview 10 | `spaces` provides a set of set/space primitives for machine learning problems. 11 | Traits such as `Space`, and it's derivatives, may be used to define 12 | state/action spaces, for example. Mappings between different spaces may also be 13 | defined which simplify many common preprocessing tasks. 14 | 15 | ## Installation 16 | ```toml 17 | [dependencies] 18 | spaces = "6.0" 19 | ``` 20 | 21 | ## Contributing 22 | Pull requests are welcome. For major changes, please open an issue first to 23 | discuss what you would like to change. 24 | 25 | Please make sure to update tests as appropriate and adhere to the angularjs 26 | commit message conventions (see 27 | [here](https://gist.github.com/stephenparish/9941e89d80e2bc58a153)). 28 | 29 | ## License 30 | [MIT](https://choosealicense.com/licenses/mit/) 31 | -------------------------------------------------------------------------------- /logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tspooner/spaces/77c4cf506f2efac230078479a1dfe477d396f533/logo.png -------------------------------------------------------------------------------- /rustfmt.toml: -------------------------------------------------------------------------------- 1 | fn_single_line = true 2 | where_single_line = true 3 | imports_indent = "Block" 4 | imports_layout = "HorizontalVertical" 5 | match_block_trailing_comma = true 6 | normalize_comments = true 7 | reorder_imports = true 8 | use_try_shorthand = true 9 | wrap_comments = true 10 | -------------------------------------------------------------------------------- /src/arrays.rs: -------------------------------------------------------------------------------- 1 | use crate::{ 2 | prelude::*, 3 | ops::{UnionPair, IntersectionPair}, 4 | }; 5 | use std::{iter::Map, convert::TryInto}; 6 | use itertools::{Itertools, structs::MultiProduct}; 7 | 8 | impl Space for [D; N] { 9 | type Value = [D::Value; N]; 10 | 11 | fn is_empty(&self) -> bool { self.iter().any(|d| d.is_empty()) } 12 | 13 | fn contains(&self, val: &Self::Value) -> bool { 14 | self.iter().zip(val.iter()).all(|(d, x)| d.contains(x)) 15 | } 16 | } 17 | 18 | impl FiniteSpace for [D; N] { 19 | fn cardinality(&self) -> usize { self.iter().map(|d| d.cardinality()).product() } 20 | } 21 | 22 | impl IterableSpace for [D; N] 23 | where 24 | D::Value: Clone, 25 | D::ElemIter: Clone, 26 | { 27 | // TODO - Ideally, we would replace MultiProduct with an optimised implementation 28 | // for yielding arrays directly, not using an intermediate Vec. 29 | type ElemIter = Map< 30 | MultiProduct, 31 | fn(Vec) -> [D::Value; N] 32 | >; 33 | 34 | fn elements(&self) -> Self::ElemIter { 35 | let iters: Vec<_> = self.iter().map(|s| s.elements()).collect(); 36 | 37 | iters.into_iter().multi_cartesian_product().map(|x| { 38 | x.try_into().map_err(|_| ()).unwrap() 39 | }) 40 | } 41 | } 42 | 43 | impl Union for [D; N] 44 | where 45 | D: Space, 46 | S: Space, 47 | { 48 | type Output = UnionPair; 49 | 50 | fn union(self, rhs: S) -> Self::Output { UnionPair(self, rhs) } 51 | } 52 | 53 | impl Intersection for [D; N] 54 | where 55 | D: Space, 56 | S: Space, 57 | { 58 | type Output = IntersectionPair; 59 | 60 | fn intersect(self, rhs: S) -> Option { 61 | Some(IntersectionPair(self, rhs)) 62 | } 63 | } 64 | 65 | impl Closure for [D; N] { 66 | type Output = [D::Output; N]; 67 | 68 | fn closure(self) -> Self::Output { self.map(|d| d.closure()) } 69 | } 70 | 71 | #[cfg(test)] 72 | mod tests { 73 | use super::*; 74 | use crate::intervals::Interval; 75 | 76 | #[test] 77 | fn test_is_empty() { 78 | assert!([ 79 | Interval::open_unchecked(0.0f64, 0.0), 80 | Interval::open_unchecked(1.0, 1.0), 81 | ].is_empty()); 82 | 83 | assert!([ 84 | Interval::open_unchecked(0.0f64, 0.0), 85 | Interval::open_unchecked(1.0, 2.0), 86 | ].is_empty()); 87 | 88 | assert!(![ 89 | Interval::degenerate(0.0f64), 90 | Interval::unit() 91 | ].is_empty()); 92 | } 93 | 94 | #[test] 95 | fn test_contains() { 96 | let s = [ 97 | Interval::degenerate(0.0f64), 98 | Interval::unit() 99 | ]; 100 | 101 | for b in [0.0, 0.25, 0.5, 0.75, 1.0] { 102 | assert!(s.contains(&[0.0, b])); 103 | 104 | for a in [-1.0, 1.0] { 105 | assert!(!s.contains(&[a, b])); 106 | } 107 | } 108 | } 109 | 110 | #[test] 111 | fn test_card() { 112 | assert_eq!([ 113 | Interval::degenerate(1usize), 114 | Interval::degenerate(0), 115 | ].cardinality(), 1); 116 | 117 | assert_eq!([ 118 | Interval::lorc_unchecked(0usize, 2usize), 119 | Interval::lorc_unchecked(0usize, 2usize) 120 | ].cardinality(), 4); 121 | 122 | assert_eq!([ 123 | Interval::closed_unchecked(0usize, 2usize), 124 | Interval::closed_unchecked(0, 100) 125 | ].cardinality(), 303); 126 | } 127 | 128 | #[test] 129 | fn test_values() { 130 | let space = [Interval::closed_unchecked(0, 1), Interval::closed_unchecked(2, 3)]; 131 | let values: Vec<_> = space.elements().collect(); 132 | 133 | assert_eq!(values, vec![ 134 | [0, 2], 135 | [0, 3], 136 | [1, 2], 137 | [1, 3], 138 | ]) 139 | } 140 | 141 | #[test] 142 | fn test_union() { 143 | let x = [ 144 | Interval::closed_unchecked(0.0, 1.0), 145 | Interval::closed_unchecked(0.0, 1.0) 146 | ]; 147 | let y = [ 148 | Interval::closed_unchecked(5.0, 6.0), 149 | Interval::closed_unchecked(5.0, 6.0) 150 | ]; 151 | let z = x.union(y); 152 | 153 | assert!(z.contains(&[0.0, 0.0])); 154 | assert!(z.contains(&[1.0, 1.0])); 155 | assert!(z.contains(&[5.0, 5.0])); 156 | assert!(z.contains(&[6.0, 6.0])); 157 | 158 | assert!(!z.contains(&[0.0, 5.0])); 159 | assert!(!z.contains(&[1.0, 6.0])); 160 | } 161 | } 162 | -------------------------------------------------------------------------------- /src/discrete.rs: -------------------------------------------------------------------------------- 1 | //! Module for discrete scalar spaces. 2 | use crate::{intervals, ops}; 3 | use num_traits::{PrimInt, Signed, Unsigned}; 4 | 5 | /// Build a space representing binary (base-2) values. 6 | pub fn binary() -> Binary { 7 | intervals::Interval { 8 | left: intervals::bounds::Closed(false), 9 | right: intervals::bounds::Closed(true), 10 | } 11 | } 12 | 13 | pub type Binary = intervals::Closed; 14 | 15 | /// Build a space representing the set of signed integers. 16 | pub fn integers() -> Integers { intervals::Interval::unbounded() } 17 | 18 | pub type Integers = intervals::Unbounded; 19 | 20 | /// Build a space representing the set of non-zero signed integers. 21 | pub fn non_zero_integers() -> NonZeroIntegers { 22 | let x = intervals::Interval::right_open(V::zero()); 23 | let y = intervals::Interval::left_open(V::zero()); 24 | 25 | ops::UnionPair(x, y) 26 | } 27 | 28 | pub type NonZeroIntegers = ops::UnionPair, intervals::LeftOpen>; 29 | 30 | /// Build a space representing the set of unsigned integers. 31 | pub fn non_negative_integers() -> NonNegativeIntegers { 32 | intervals::Interval::left_closed(V::zero()) 33 | } 34 | 35 | pub type NonNegativeIntegers = intervals::LeftClosed; 36 | 37 | /// Build a space representing the set of unsigned integers. 38 | pub fn positive_integers() -> PositiveIntegers { 39 | intervals::Interval::left_open(V::zero()) 40 | } 41 | 42 | pub type PositiveIntegers = intervals::LeftOpen; 43 | 44 | /// Build a space representing the set of unsigned integers. 45 | pub fn non_positive_integers() -> NonPositiveIntegers { 46 | intervals::Interval::right_closed(V::zero()) 47 | } 48 | 49 | pub type NonPositiveIntegers = intervals::RightClosed; 50 | 51 | /// Build a space representing the set of unsigned integers. 52 | pub fn negative_integers() -> NegativeIntegers { 53 | intervals::Interval::right_open(V::zero()) 54 | } 55 | 56 | pub type NegativeIntegers = intervals::RightOpen; 57 | 58 | /// Build a space representing the set of natural numbers. 59 | pub fn naturals() -> Naturals { positive_integers() } 60 | 61 | pub type Naturals = PositiveIntegers; 62 | -------------------------------------------------------------------------------- /src/interval.rs: -------------------------------------------------------------------------------- 1 | use crate::{ 2 | Space, OrderedSpace, FiniteSpace, IterableSpace, 3 | ops::{Union, UnionPair, Intersection, Closure} 4 | }; 5 | use std::ops::{RangeInclusive, RangeTo, RangeFrom, RangeToInclusive}; 6 | use intervals::{Interval, bounds::{self, OpenOrClosed}}; 7 | 8 | /////////////////////////////////////////////////////////////////// 9 | // Core Implementations 10 | /////////////////////////////////////////////////////////////////// 11 | impl Space for Interval 12 | where 13 | L: bounds::Bound, 14 | R: bounds::Bound, 15 | 16 | L::Value: Clone, 17 | { 18 | type Value = L::Value; 19 | 20 | fn is_empty(&self) -> bool { 21 | match (self.left.value(), self.right.value()) { 22 | (Some(l), Some(r)) if !self.left.is_closed() && !self.right.is_closed() => l == r, 23 | _ => false, 24 | } 25 | } 26 | 27 | fn contains(&self, val: &L::Value) -> bool { 28 | use OpenOrClosed::*; 29 | 30 | let check_left = self.inf().map_or(true, |l| match l { 31 | Open(ref l) => val > l, 32 | Closed(ref l) => val >= l, 33 | }); 34 | let check_right = self.sup().map_or(true, |r| match r { 35 | Open(ref r) => val < r, 36 | Closed(ref r) => val <= r, 37 | }); 38 | 39 | check_left && check_right 40 | } 41 | } 42 | 43 | impl OrderedSpace for Interval 44 | where 45 | L: bounds::Bound, 46 | R: bounds::Bound, 47 | 48 | L::Value: Clone, 49 | { 50 | fn inf(&self) -> Option> { 51 | self.left.value().cloned().map(|l| if self.left.is_open() { 52 | OpenOrClosed::Open(l) 53 | } else { 54 | OpenOrClosed::Closed(l) 55 | }) 56 | } 57 | 58 | fn sup(&self) -> Option> { 59 | self.right.value().cloned().map(|r| if self.right.is_open() { 60 | OpenOrClosed::Open(r) 61 | } else { 62 | OpenOrClosed::Closed(r) 63 | }) 64 | } 65 | } 66 | 67 | macro_rules! impl_fs { 68 | ($v:ident; $left:ty, $right:ty; |$me:ident| $inner:block) => { 69 | impl<$v> FiniteSpace for Interval<$left, $right> 70 | where 71 | $v: num_traits::PrimInt, 72 | <$v as std::ops::Sub>::Output: num_traits::NumCast, 73 | { 74 | fn cardinality(&$me) -> usize { 75 | num_traits::NumCast::from($inner).unwrap() 76 | } 77 | } 78 | } 79 | } 80 | 81 | #[inline] 82 | fn card_oo(left: V, right: V) -> ::Output { 83 | let d = right - left; 84 | 85 | if d <= V::one() { V::zero() } else { d } 86 | } 87 | 88 | #[inline] 89 | fn card_co(left: V, right: V) -> ::Output { 90 | right - left 91 | } 92 | 93 | #[inline] 94 | fn card_cc(left: V, right: V) -> ::Output { 95 | right - left + V::one() 96 | } 97 | 98 | impl_fs!(V; bounds::Closed, bounds::Closed; |self| { card_cc(self.left.0, self.right.0) }); 99 | impl_fs!(V; bounds::Closed, bounds::Open; |self| { card_co(self.left.0, self.right.0) }); 100 | impl_fs!(V; bounds::Closed, bounds::OpenOrClosed; |self| { 101 | match self.right { 102 | bounds::OpenOrClosed::Open(r) => card_co(self.left.0, r), 103 | bounds::OpenOrClosed::Closed(r) => card_cc(self.left.0, r), 104 | } 105 | }); 106 | 107 | impl_fs!(V; bounds::Open, bounds::Closed; |self| { card_co(self.left.0, self.right.0) }); 108 | impl_fs!(V; bounds::Open, bounds::Open; |self| { card_oo(self.left.0, self.right.0) }); 109 | impl_fs!(V; bounds::Open, bounds::OpenOrClosed; |self| { 110 | match self.right { 111 | bounds::OpenOrClosed::Open(r) => card_oo(self.left.0, r), 112 | bounds::OpenOrClosed::Closed(r) => card_co(self.left.0, r), 113 | } 114 | }); 115 | 116 | impl_fs!(V; bounds::OpenOrClosed, bounds::Closed; |self| { 117 | match self.left { 118 | bounds::OpenOrClosed::Open(l) => card_co(l, self.right.0), 119 | bounds::OpenOrClosed::Closed(l) => card_cc(l, self.right.0), 120 | } 121 | }); 122 | impl_fs!(V; bounds::OpenOrClosed, bounds::Open; |self| { 123 | match self.left { 124 | bounds::OpenOrClosed::Open(l) => card_oo(l, self.right.0), 125 | bounds::OpenOrClosed::Closed(l) => card_co(l, self.right.0), 126 | } 127 | }); 128 | impl_fs!(V; bounds::OpenOrClosed, bounds::OpenOrClosed; |self| { 129 | use intervals::bounds::OpenOrClosed::{Open, Closed}; 130 | 131 | match (self.left, self.right) { 132 | (Open(l), Open(r)) => card_oo(l, r), 133 | (Closed(l), Open(r)) | (Open(l), Closed(r)) => card_co(l, r), 134 | (Closed(l), Closed(r)) => card_cc(l, r), 135 | } 136 | }); 137 | 138 | /////////////////////////////////////////////////////////////////// 139 | // Iter Implementations 140 | /////////////////////////////////////////////////////////////////// 141 | macro_rules! impl_iter { 142 | ($v:ident; $left:ty, $right:ty; |$me:ident| -> $out:ty $code:block) => { 143 | impl<$v> IterableSpace for Interval<$left, $right> 144 | where 145 | $v: num_traits::PrimInt, 146 | 147 | $out: Iterator, 148 | { 149 | type ElemIter = $out; 150 | 151 | fn elements(&$me) -> Self::ElemIter { $code } 152 | } 153 | } 154 | } 155 | 156 | // Closed + ... 157 | impl_iter!(V; bounds::Closed, bounds::Closed; |self| -> RangeInclusive { 158 | self.left.0..=self.right.0 159 | }); 160 | impl_iter!(V; bounds::Closed, bounds::Open; |self| -> RangeInclusive { 161 | self.left.0..=(self.right.0 - V::one()) 162 | }); 163 | impl_iter!(V; bounds::Closed, bounds::OpenOrClosed; |self| -> RangeInclusive { 164 | match self.right { 165 | OpenOrClosed::Open(r) => self.left.0..=(r - V::one()), 166 | OpenOrClosed::Closed(r) => self.left.0..=r, 167 | } 168 | }); 169 | 170 | impl IterableSpace for Interval, bounds::NoBound> 171 | where 172 | V: num_traits::PrimInt, 173 | 174 | RangeFrom: Iterator, 175 | { 176 | type ElemIter = RangeFrom; 177 | 178 | fn elements(&self) -> Self::ElemIter { 179 | self.left.0.. 180 | } 181 | } 182 | 183 | // Open + ... 184 | impl_iter!(V; bounds::Open, bounds::Closed; |self| -> RangeInclusive { 185 | (self.left.0 + V::one())..=self.right.0 186 | }); 187 | impl_iter!(V; bounds::Open, bounds::Open; |self| -> RangeInclusive { 188 | (self.left.0 + V::one())..=(self.right.0 - V::one()) 189 | }); 190 | impl_iter!(V; bounds::Open, bounds::OpenOrClosed; |self| -> RangeInclusive { 191 | let l = self.left.0 + V::one(); 192 | 193 | match self.right { 194 | OpenOrClosed::Open(r) => l..=(r - V::one()), 195 | OpenOrClosed::Closed(r) => l..=r, 196 | } 197 | }); 198 | 199 | impl IterableSpace for Interval, bounds::NoBound> 200 | where 201 | V: num_traits::PrimInt, 202 | 203 | RangeFrom: Iterator, 204 | { 205 | type ElemIter = RangeFrom; 206 | 207 | fn elements(&self) -> Self::ElemIter { 208 | (self.left.0 + V::one()).. 209 | } 210 | } 211 | 212 | // OpenOrClosed + ... 213 | impl_iter!(V; bounds::OpenOrClosed, bounds::Closed; |self| -> RangeInclusive { 214 | let r = self.right.0; 215 | 216 | match self.left { 217 | OpenOrClosed::Open(l) => (l + V::one())..=r, 218 | OpenOrClosed::Closed(l) => l..=r, 219 | } 220 | }); 221 | impl_iter!(V; bounds::OpenOrClosed, bounds::Open; |self| -> RangeInclusive { 222 | let r = self.right.0 - V::one(); 223 | 224 | match self.left { 225 | OpenOrClosed::Open(l) => (l + V::one())..=(r - V::one()), 226 | OpenOrClosed::Closed(l) => l..=r, 227 | } 228 | }); 229 | impl_iter!(V; bounds::OpenOrClosed, bounds::OpenOrClosed; |self| -> RangeInclusive { 230 | match (self.left, self.right) { 231 | (OpenOrClosed::Open(l), OpenOrClosed::Open(r)) => (l + V::one())..=(r - V::one()), 232 | (OpenOrClosed::Open(l), OpenOrClosed::Closed(r)) => (l + V::one())..=r, 233 | (OpenOrClosed::Closed(l), OpenOrClosed::Open(r)) => l..=(r - V::one()), 234 | (OpenOrClosed::Closed(l), OpenOrClosed::Closed(r)) => l..=r, 235 | } 236 | }); 237 | 238 | impl IterableSpace for Interval, bounds::NoBound> 239 | where 240 | V: num_traits::PrimInt, 241 | 242 | RangeFrom: Iterator, 243 | { 244 | type ElemIter = RangeFrom; 245 | 246 | fn elements(&self) -> Self::ElemIter { 247 | match self.left { 248 | OpenOrClosed::Open(l) => (l + V::one()).., 249 | OpenOrClosed::Closed(l) => l.., 250 | } 251 | } 252 | } 253 | 254 | // NoBound + ... 255 | impl IterableSpace for Interval, bounds::Closed> 256 | where 257 | V: num_traits::PrimInt, 258 | 259 | RangeToInclusive: Iterator, 260 | { 261 | type ElemIter = RangeToInclusive; 262 | 263 | fn elements(&self) -> Self::ElemIter { 264 | ..=self.right.0 265 | } 266 | } 267 | 268 | impl IterableSpace for Interval, bounds::Open> 269 | where 270 | V: num_traits::PrimInt, 271 | 272 | RangeTo: Iterator, 273 | { 274 | type ElemIter = RangeTo; 275 | 276 | fn elements(&self) -> Self::ElemIter { 277 | ..self.right.0 278 | } 279 | } 280 | 281 | impl IterableSpace for Interval, bounds::OpenOrClosed> 282 | where 283 | V: num_traits::PrimInt, 284 | 285 | RangeToInclusive: Iterator, 286 | { 287 | type ElemIter = RangeToInclusive; 288 | 289 | fn elements(&self) -> Self::ElemIter { 290 | match self.right { 291 | OpenOrClosed::Open(r) => ..=(r - V::one()), 292 | OpenOrClosed::Closed(r) => ..=r, 293 | } 294 | } 295 | } 296 | 297 | /////////////////////////////////////////////////////////////////// 298 | // Op Implementations 299 | /////////////////////////////////////////////////////////////////// 300 | impl Union> for Interval 301 | where 302 | L: bounds::Bound, 303 | R: bounds::Bound, 304 | 305 | LL: bounds::Bound, 306 | RR: bounds::Bound, 307 | 308 | Interval: Space, 309 | Interval: Space, 310 | { 311 | type Output = UnionPair, Interval>; 312 | 313 | fn union(self, rhs: Interval) -> Self::Output { UnionPair(self, rhs) } 314 | } 315 | 316 | impl Intersection> for Interval 317 | where 318 | L: bounds::Pinch, 319 | R: bounds::Pinch, 320 | 321 | L::Value: PartialOrd, 322 | 323 | LL: bounds::Bound, 324 | RR: bounds::Bound, 325 | 326 | Interval: crate::Space, 327 | Interval: crate::Space, 328 | 329 | intervals::IntersectionOf: crate::Space, 330 | bounds::Validator: bounds::ValidateBounds, 331 | { 332 | type Output = intervals::IntersectionOf; 333 | 334 | fn intersect(self, rhs: Interval) -> Option { 335 | Interval::intersect(self, rhs) 336 | } 337 | } 338 | 339 | impl Closure for Interval 340 | where 341 | L: bounds::Bound, 342 | R: bounds::Bound, 343 | 344 | Interval: Space, 345 | Interval: Space, 346 | { 347 | type Output = Interval; 348 | 349 | fn closure(self) -> Self::Output { 350 | Interval { 351 | left: self.left.with_limit_point(), 352 | right: self.right.with_limit_point(), 353 | } 354 | } 355 | } 356 | 357 | impl Closure for UnionPair, Interval> 358 | where 359 | L: bounds::Unroll, 360 | R: bounds::Unroll, 361 | 362 | LL: bounds::Bound, 363 | RR: bounds::Bound, 364 | 365 | L::Value: Clone, 366 | { 367 | type Output = crate::intervals::UnionClosureOf; 368 | 369 | fn closure(self) -> Self::Output { 370 | Interval::union_closure(self.0, self.1) 371 | } 372 | } 373 | 374 | #[cfg(test)] 375 | mod tests { 376 | use super::*; 377 | 378 | #[test] 379 | fn test_closed_intersection() { 380 | let a = Interval::closed_unchecked(0.0, 1.0); 381 | let b = Interval::closed_unchecked(1.0, 2.0); 382 | let c = Interval::closed_unchecked(2.0, 3.0); 383 | 384 | assert_eq!(a.intersect(a).unwrap(), a); 385 | 386 | assert_eq!(a.intersect(b).unwrap(), Interval::degenerate(1.0)); 387 | assert_eq!(b.intersect(c).unwrap(), Interval::degenerate(2.0)); 388 | 389 | assert_eq!(a.intersect(c), None); 390 | } 391 | 392 | #[test] 393 | fn test_iter_cc() { 394 | let vals: Vec<_> = Interval::closed_unchecked(0, 5).elements().collect(); 395 | 396 | assert_eq!(vals, vec![0, 1, 2, 3, 4, 5]); 397 | 398 | let vals: Vec<_> = Interval::new_unchecked( 399 | bounds::Closed(0), 400 | bounds::OpenOrClosed::Closed(5) 401 | ).elements().collect(); 402 | 403 | assert_eq!(vals, vec![0, 1, 2, 3, 4, 5]); 404 | 405 | let vals: Vec<_> = Interval::new_unchecked( 406 | bounds::OpenOrClosed::Closed(0), 407 | bounds::OpenOrClosed::Closed(5) 408 | ).elements().collect(); 409 | 410 | assert_eq!(vals, vec![0, 1, 2, 3, 4, 5]); 411 | } 412 | 413 | #[test] 414 | fn test_iter_co() { 415 | let vals: Vec<_> = Interval::lcro_unchecked(0, 5).elements().collect(); 416 | 417 | assert_eq!(vals, vec![0, 1, 2, 3, 4]); 418 | 419 | let vals: Vec<_> = Interval::new_unchecked( 420 | bounds::Closed(0), 421 | bounds::OpenOrClosed::Open(5) 422 | ).elements().collect(); 423 | 424 | assert_eq!(vals, vec![0, 1, 2, 3, 4]); 425 | 426 | let vals: Vec<_> = Interval::new_unchecked( 427 | bounds::OpenOrClosed::Closed(0), 428 | bounds::OpenOrClosed::Open(5) 429 | ).elements().collect(); 430 | 431 | assert_eq!(vals, vec![0, 1, 2, 3, 4]); 432 | } 433 | 434 | #[test] 435 | fn test_iter_oc() { 436 | let vals: Vec<_> = Interval::lorc_unchecked(0, 5).elements().collect(); 437 | 438 | assert_eq!(vals, vec![1, 2, 3, 4, 5]); 439 | 440 | let vals: Vec<_> = Interval::new_unchecked( 441 | bounds::Open(0), 442 | bounds::OpenOrClosed::Closed(5) 443 | ).elements().collect(); 444 | 445 | assert_eq!(vals, vec![1, 2, 3, 4, 5]); 446 | 447 | let vals: Vec<_> = Interval::new_unchecked( 448 | bounds::OpenOrClosed::Open(0), 449 | bounds::OpenOrClosed::Closed(5) 450 | ).elements().collect(); 451 | 452 | assert_eq!(vals, vec![1, 2, 3, 4, 5]); 453 | } 454 | 455 | #[test] 456 | fn test_iter_oo() { 457 | let vals: Vec<_> = Interval::open_unchecked(0, 5).elements().collect(); 458 | 459 | assert_eq!(vals, vec![1, 2, 3, 4]); 460 | 461 | let vals: Vec<_> = Interval::new_unchecked( 462 | bounds::Open(0), 463 | bounds::OpenOrClosed::Open(5) 464 | ).elements().collect(); 465 | 466 | assert_eq!(vals, vec![1, 2, 3, 4]); 467 | 468 | let vals: Vec<_> = Interval::new_unchecked( 469 | bounds::OpenOrClosed::Open(0), 470 | bounds::OpenOrClosed::Open(5) 471 | ).elements().collect(); 472 | 473 | assert_eq!(vals, vec![1, 2, 3, 4]); 474 | } 475 | } 476 | -------------------------------------------------------------------------------- /src/lib.rs: -------------------------------------------------------------------------------- 1 | //! Set/space primitives for defining machine learning problems. 2 | //! 3 | //! `spaces` provides set/space primitives to be used for defining properties of 4 | //! machine learning problems. Traits such as `Space`, and it's derivatives, may 5 | //! be used to define state/action spaces, for example. 6 | extern crate itertools; 7 | extern crate num_traits; 8 | 9 | pub mod discrete; 10 | pub mod real; 11 | 12 | pub extern crate intervals; 13 | 14 | use intervals::bounds::OpenOrClosed; 15 | 16 | mod arrays; 17 | mod interval; 18 | mod partitions; 19 | mod option; 20 | mod tuples; 21 | 22 | /////////////////////////////////////////////////////////////////////////// 23 | // Core Definitions 24 | /////////////////////////////////////////////////////////////////////////// 25 | /// Trait for types representing spaces (i.e. abstract collections). 26 | pub trait Space { 27 | /// The data representation for elements of the space. 28 | type Value; 29 | 30 | /// Return true if the space contains no values. 31 | /// 32 | /// ``` 33 | /// # extern crate spaces; 34 | /// # use spaces::{Space, ops::Intersection, real}; 35 | /// let space = real::reals::(); 36 | /// assert!(!space.is_empty()); 37 | /// 38 | /// let space = real::negative_reals::().intersect( 39 | /// real::positive_reals::() 40 | /// ); 41 | /// assert!(space.is_none()); 42 | /// ``` 43 | fn is_empty(&self) -> bool; 44 | 45 | /// Returns true iff `val` is contained within the space. 46 | fn contains(&self, val: &Self::Value) -> bool; 47 | } 48 | 49 | /// Trait for types representing ordered spaces. 50 | pub trait OrderedSpace: Space 51 | where Self::Value: PartialOrd 52 | { 53 | /// Return the infimum of the space, if it exists. 54 | fn inf(&self) -> Option>; 55 | 56 | /// Returns the supremum of the space, if it exists. 57 | fn sup(&self) -> Option>; 58 | 59 | /// Returns true iff `self` has a well-defined infimum. 60 | fn is_lower_bounded(&self) -> bool { self.inf().is_some() } 61 | 62 | /// Returns true iff `self` has a well-defined supremum. 63 | fn is_upper_bounded(&self) -> bool { self.sup().is_some() } 64 | 65 | /// Returns true iff `self` is bounded above and below. 66 | fn is_bounded(&self) -> bool { self.is_lower_bounded() && self.is_upper_bounded() } 67 | } 68 | 69 | /// Trait for defining spaces containing a finite set of values. 70 | pub trait FiniteSpace: Space { 71 | /// Return the cardinality of the space. 72 | /// 73 | /// The cardinality of a space is given by the number of elements 74 | /// contained within said set. 75 | fn cardinality(&self) -> usize; 76 | } 77 | 78 | /// Trait for `Space` types that have an associated value iterator. 79 | pub trait IterableSpace: Space { 80 | /// The associated iterator type. 81 | type ElemIter: Iterator; 82 | 83 | /// Return an iterator over the elements of this space. 84 | fn elements(&self) -> Self::ElemIter; 85 | } 86 | 87 | /////////////////////////////////////////////////////////////////////////// 88 | // Set Operations 89 | /////////////////////////////////////////////////////////////////////////// 90 | pub mod ops; 91 | 92 | /////////////////////////////////////////////////////////////////////////// 93 | // Prelude 94 | /////////////////////////////////////////////////////////////////////////// 95 | mod prelude { 96 | pub use super::{ 97 | ops::{Union, Intersection, Closure}, 98 | FiniteSpace, OrderedSpace, Space, IterableSpace, 99 | }; 100 | } 101 | -------------------------------------------------------------------------------- /src/ops/intersection.rs: -------------------------------------------------------------------------------- 1 | use crate::{Space, OrderedSpace}; 2 | use super::{OoC, LRB, min_val, max_val, Union, UnionPair}; 3 | 4 | fn clip_ooc(x: OoC, y: OoC, cmp: impl Fn(T, T) -> LRB) -> OoC { 5 | use crate::intervals::bounds::OpenOrClosed::*; 6 | 7 | match (x, y) { 8 | (Open(x), Open(y)) => cmp(x, y).translate(Open, Open, Open), 9 | (Open(x), Closed(y)) => cmp(x, y).translate(Open, Open, Closed), 10 | (Closed(x), Open(y)) => cmp(x, y).translate(Closed, Open, Open), 11 | (Closed(x), Closed(y)) => cmp(x, y).translate(Closed, Closed, Closed), 12 | } 13 | } 14 | 15 | /// Trait for types that support the intersect operation. 16 | /// 17 | /// The intersection of a collection of sets is the set containing all 18 | /// such elements that are present in each set within the collection. 19 | pub trait Intersection: Space { 20 | type Output: Space; 21 | 22 | fn intersect(self, rhs: Rhs) -> Option; 23 | } 24 | 25 | pub type IntersectionOf = >::Output; 26 | 27 | // TODO - Add warning to docstring that explains why this type should be avoided. 28 | // Namely, that you can lead to panic! when calling is_empty(). 29 | /// Type representing the intersection of two spaces. 30 | #[derive(Copy, Clone, PartialEq, Eq, Debug)] 31 | pub struct IntersectionPair>(pub A, pub B); 32 | 33 | impl Space for IntersectionPair 34 | where 35 | A: Space, 36 | B: Space, 37 | { 38 | type Value = A::Value; 39 | 40 | fn is_empty(&self) -> bool { 41 | if self.0.is_empty() || self.1.is_empty() { return true; } 42 | 43 | panic!( 44 | "It's not currently possible to evaluate IntersectionPair::is_empty when neither \ 45 | interior space are empty." 46 | ) 47 | } 48 | 49 | fn contains(&self, val: &A::Value) -> bool { self.0.contains(val) && self.1.contains(val) } 50 | } 51 | 52 | impl OrderedSpace for IntersectionPair 53 | where 54 | A: OrderedSpace, 55 | B: OrderedSpace, 56 | 57 | A::Value: PartialOrd, 58 | { 59 | fn inf(&self) -> Option> { 60 | match (self.0.inf(), self.1.inf()) { 61 | (Some(left), Some(right)) => Some(clip_ooc(left, right, min_val)), 62 | _ => None, 63 | } 64 | } 65 | 66 | fn sup(&self) -> Option> { 67 | match (self.0.sup(), self.1.sup()) { 68 | (Some(left), Some(right)) => Some(clip_ooc(left, right, max_val)), 69 | _ => None, 70 | } 71 | } 72 | } 73 | 74 | impl Union for IntersectionPair 75 | where 76 | A: Space, 77 | B: Space, 78 | Rhs: Space, 79 | { 80 | type Output = UnionPair; 81 | 82 | fn union(self, rhs: Rhs) -> Self::Output { UnionPair(self, rhs) } 83 | } 84 | 85 | impl Intersection for IntersectionPair 86 | where 87 | A: Space, 88 | B: Space, 89 | Rhs: Space, 90 | { 91 | type Output = IntersectionPair; 92 | 93 | fn intersect(self, rhs: Rhs) -> Option { 94 | if self.0.is_empty() || self.1.is_empty() { return None; } 95 | 96 | Some(IntersectionPair(self, rhs)) 97 | } 98 | } 99 | -------------------------------------------------------------------------------- /src/ops/mod.rs: -------------------------------------------------------------------------------- 1 | //! Module for operations acting on spaces. 2 | use crate::Space; 3 | 4 | type OoC = crate::intervals::bounds::OpenOrClosed; 5 | 6 | enum LRB { Left(T), Both(T), Right(T), } 7 | 8 | impl LRB { 9 | fn translate( 10 | self, 11 | left: impl FnOnce(T) -> OoC, 12 | both: impl FnOnce(T) -> OoC, 13 | right: impl FnOnce(T) -> OoC, 14 | ) -> OoC { 15 | match self { 16 | LRB::Left(x) => left(x), 17 | LRB::Both(x) => both(x), 18 | LRB::Right(x) => right(x), 19 | } 20 | } 21 | } 22 | 23 | fn min_val(x: T, y: T) -> LRB { 24 | if x < y { LRB::Left(x) } else if x == y { LRB::Both(x) } else { LRB::Right(y) } 25 | } 26 | 27 | fn max_val(x: T, y: T) -> LRB { 28 | if x < y { LRB::Right(y) } else if x == y { LRB::Both(x) } else { LRB::Left(x) } 29 | } 30 | 31 | /// Trait for types that have a well-defined closure. 32 | pub trait Closure: Space { 33 | type Output: Space; 34 | 35 | fn closure(self) -> Self::Output; 36 | } 37 | 38 | pub type ClosureOf = ::Output; 39 | 40 | mod union; 41 | pub use self::union::{Union, UnionOf, UnionClosureOf, UnionPair}; 42 | 43 | mod intersection; 44 | pub use self::intersection::{Intersection, IntersectionOf, IntersectionPair}; 45 | -------------------------------------------------------------------------------- /src/ops/union.rs: -------------------------------------------------------------------------------- 1 | use crate::{OrderedSpace, Space}; 2 | use super::{OoC, LRB, min_val, max_val, Intersection, IntersectionPair, Closure, ClosureOf}; 3 | 4 | fn clip_ooc(x: OoC, y: OoC, cmp: impl Fn(T, T) -> LRB) -> OoC { 5 | use crate::intervals::bounds::OpenOrClosed::*; 6 | 7 | match (x, y) { 8 | (Open(x), Open(y)) => cmp(x, y).translate(Open, Open, Open), 9 | (Open(x), Closed(y)) => cmp(x, y).translate(Open, Closed, Closed), 10 | (Closed(x), Open(y)) => cmp(x, y).translate(Closed, Closed, Open), 11 | (Closed(x), Closed(y)) => cmp(x, y).translate(Closed, Closed, Closed), 12 | } 13 | } 14 | 15 | /// Trait for types that support the union operation. 16 | /// 17 | /// The union of a collection of sets is the set containing all 18 | /// such elements that are present in at least one set within the collection. 19 | pub trait Union: Space { 20 | type Output: Space; 21 | 22 | fn union(self, rhs: Rhs) -> Self::Output; 23 | 24 | /// Compute the union-closure of the space. 25 | fn union_closure(self, rhs: Rhs) -> UnionClosureOf 26 | where 27 | Self: Sized, 28 | Self::Output: Closure, 29 | { 30 | self.union(rhs).closure() 31 | } 32 | } 33 | 34 | pub type UnionOf = >::Output; 35 | pub type UnionClosureOf = ClosureOf>; 36 | 37 | /// Type representing the union of two arbitrary spaces. 38 | #[derive(Copy, Clone, PartialEq, Eq, Debug)] 39 | pub struct UnionPair>(pub A, pub B); 40 | 41 | impl Space for UnionPair 42 | where 43 | A: Space, 44 | B: Space, 45 | { 46 | type Value = A::Value; 47 | 48 | fn is_empty(&self) -> bool { self.0.is_empty() && self.1.is_empty() } 49 | 50 | fn contains(&self, val: &A::Value) -> bool { self.0.contains(val) || self.1.contains(val) } 51 | } 52 | 53 | impl OrderedSpace for UnionPair 54 | where 55 | A: OrderedSpace, 56 | B: OrderedSpace, 57 | 58 | A::Value: PartialOrd, 59 | { 60 | fn inf(&self) -> Option> { 61 | match (self.0.inf(), self.1.inf()) { 62 | (Some(left), Some(right)) => Some(clip_ooc(left, right, min_val)), 63 | _ => None, 64 | } 65 | } 66 | 67 | fn sup(&self) -> Option> { 68 | match (self.0.sup(), self.1.sup()) { 69 | (Some(left), Some(right)) => Some(clip_ooc(left, right, max_val)), 70 | _ => None, 71 | } 72 | } 73 | } 74 | 75 | impl Union for UnionPair 76 | where 77 | A: Space, 78 | B: Space, 79 | C: Space, 80 | { 81 | type Output = UnionPair; 82 | 83 | fn union(self, rhs: C) -> Self::Output { UnionPair(self, rhs) } 84 | } 85 | 86 | impl Intersection for UnionPair 87 | where 88 | A: Space, 89 | B: Space, 90 | C: Space, 91 | { 92 | type Output = IntersectionPair; 93 | 94 | fn intersect(self, rhs: C) -> Option { 95 | let intersect = IntersectionPair(self, rhs); 96 | 97 | if intersect.is_empty() { None } else { Some(intersect) } 98 | } 99 | } 100 | 101 | impl std::fmt::Display for UnionPair 102 | where 103 | S: Space + std::fmt::Display, 104 | T: Space + std::fmt::Display, 105 | { 106 | fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { 107 | write!(f, "{} \u{222A} {}", self.0, self.1) 108 | } 109 | } 110 | 111 | #[cfg(test)] 112 | mod tests { 113 | use crate::intervals::Interval; 114 | use super::*; 115 | 116 | #[test] 117 | fn test_up_lower() { 118 | let pair = UnionPair( 119 | Interval::left_open(-1.0), 120 | Interval::left_closed(0.0) 121 | ); 122 | 123 | assert_eq!(pair.inf().unwrap(), OoC::Open(-1.0)); 124 | assert!(pair.sup().is_none()); 125 | 126 | let pair = UnionPair( 127 | Interval::left_closed(0.0), 128 | Interval::left_open(-1.0) 129 | ); 130 | 131 | assert_eq!(pair.inf().unwrap(), OoC::Open(-1.0)); 132 | assert!(pair.sup().is_none()); 133 | } 134 | 135 | #[test] 136 | fn test_up_upper() { 137 | let pair = UnionPair( 138 | Interval::right_open(1.0), 139 | Interval::right_closed(0.0) 140 | ); 141 | 142 | assert_eq!(pair.sup().unwrap(), OoC::Open(1.0)); 143 | assert!(pair.inf().is_none()); 144 | 145 | let pair = UnionPair( 146 | Interval::right_closed(0.0), 147 | Interval::right_open(1.0) 148 | ); 149 | 150 | assert_eq!(pair.sup().unwrap(), OoC::Open(1.0)); 151 | assert!(pair.inf().is_none()); 152 | } 153 | 154 | #[test] 155 | fn test_up_both() { 156 | let pair = UnionPair( 157 | Interval::open_unchecked(0.0, 1.0), 158 | Interval::closed_unchecked(-1.0, 1.0) 159 | ); 160 | 161 | assert_eq!(pair.inf().unwrap(), OoC::Closed(-1.0)); 162 | assert_eq!(pair.sup().unwrap(), OoC::Closed(1.0)); 163 | 164 | let pair = UnionPair( 165 | Interval::open_unchecked(-1.0, 1.0), 166 | Interval::closed_unchecked(0.0, 1.0) 167 | ); 168 | 169 | assert_eq!(pair.inf().unwrap(), OoC::Open(-1.0)); 170 | assert_eq!(pair.sup().unwrap(), OoC::Closed(1.0)); 171 | } 172 | } 173 | -------------------------------------------------------------------------------- /src/option.rs: -------------------------------------------------------------------------------- 1 | use crate::{ 2 | FiniteSpace, OrderedSpace, Space, IterableSpace, 3 | intervals::bounds::OpenOrClosed, 4 | ops::UnionPair, 5 | prelude::*, 6 | }; 7 | 8 | pub struct OptionIter(Option); 9 | 10 | impl Iterator for OptionIter { 11 | type Item = S::Value; 12 | 13 | fn next(&mut self) -> Option { 14 | self.0.as_mut().and_then(|iter| iter.next()) 15 | } 16 | } 17 | 18 | impl Space for Option { 19 | type Value = S::Value; 20 | 21 | fn is_empty(&self) -> bool { self.as_ref().map_or(true, |s| s.is_empty()) } 22 | 23 | fn contains(&self, value: &Self::Value) -> bool { 24 | self.as_ref().map_or(false, |s| s.contains(value)) 25 | } 26 | } 27 | 28 | impl OrderedSpace for Option 29 | where S::Value: PartialOrd 30 | { 31 | fn inf(&self) -> Option> { 32 | self.as_ref().and_then(|s| s.inf()) 33 | } 34 | 35 | fn sup(&self) -> Option> { 36 | self.as_ref().and_then(|s| s.sup()) 37 | } 38 | } 39 | 40 | impl FiniteSpace for Option { 41 | fn cardinality(&self) -> usize { self.as_ref().map_or(0, |s| s.cardinality()) } 42 | } 43 | 44 | impl IterableSpace for Option { 45 | type ElemIter = OptionIter; 46 | 47 | fn elements(&self) -> OptionIter { 48 | OptionIter(self.as_ref().map(|s| s.elements())) 49 | } 50 | } 51 | 52 | impl Closure for Option { 53 | type Output = Option; 54 | 55 | fn closure(self) -> Self::Output { 56 | self.map(|s| s.closure()) 57 | } 58 | } 59 | 60 | impl> Union for Option { 61 | type Output = UnionPair; 62 | 63 | fn union(self, rhs: T) -> Self::Output { UnionPair(self, rhs) } 64 | } 65 | 66 | impl, T: Space> Intersection for Option { 67 | type Output = S::Output; 68 | 69 | fn intersect(self, rhs: T) -> Option { 70 | self.and_then(|s| s.intersect(rhs)) 71 | } 72 | } 73 | 74 | #[cfg(test)] 75 | mod tests { 76 | use super::*; 77 | use crate::intervals::Interval; 78 | 79 | #[test] 80 | fn test_is_empty() { 81 | let s: Option> = None; 82 | 83 | assert!(s.is_empty()); 84 | assert!(Some(Interval::open_unchecked(0.0f64, 0.0)).is_empty()); 85 | assert!(!Some(Interval::degenerate(0.0)).is_empty()); 86 | } 87 | 88 | #[test] 89 | fn test_union() { 90 | let e: Option> = None; 91 | let a = e.union(Interval::degenerate(0.0)); 92 | let b = Some(crate::real::positive_reals()).union(Interval::degenerate(0.0)); 93 | 94 | assert!(!a.contains(&-1.0)); 95 | assert!(!b.contains(&-1.0)); 96 | 97 | assert!(a.contains(&0.0)); 98 | assert!(b.contains(&0.0)); 99 | 100 | assert!(!a.contains(&1.0)); 101 | assert!(b.contains(&1.0)); 102 | } 103 | 104 | #[test] 105 | fn test_intersect() { 106 | let e: Option> = None; 107 | let a = e.intersect(Interval::degenerate(0.0)); 108 | let b = Some(crate::real::positive_reals()).intersect(Interval::degenerate(0.0)); 109 | 110 | assert!(a.is_none()); 111 | assert!(b.is_none()); 112 | } 113 | } 114 | -------------------------------------------------------------------------------- /src/partitions.rs: -------------------------------------------------------------------------------- 1 | use crate::{ 2 | intervals::{partitions::{self, Partition}, bounds::OpenOrClosed}, 3 | FiniteSpace, 4 | OrderedSpace, 5 | Space, 6 | IterableSpace, 7 | }; 8 | use std::ops::Range; 9 | 10 | impl Space for partitions::Uniform { 11 | type Value = usize; 12 | 13 | fn is_empty(&self) -> bool { self.size > 0 } 14 | 15 | fn contains(&self, value: &usize) -> bool { value >= &0 && value < &self.size } 16 | } 17 | 18 | impl OrderedSpace for partitions::Uniform { 19 | fn inf(&self) -> Option> { 20 | Some(OpenOrClosed::Closed(0)) 21 | } 22 | 23 | fn sup(&self) -> Option> { 24 | Some(OpenOrClosed::Closed(self.size - 1)) 25 | } 26 | } 27 | 28 | impl FiniteSpace for partitions::Uniform { 29 | fn cardinality(&self) -> usize { self.size } 30 | } 31 | 32 | impl IterableSpace for partitions::Uniform { 33 | type ElemIter = Range; 34 | 35 | fn elements(&self) -> Self::ElemIter { 0..self.size } 36 | } 37 | 38 | impl Space for partitions::Declarative { 39 | type Value = usize; 40 | 41 | fn is_empty(&self) -> bool { self.len() > 0 } 42 | 43 | fn contains(&self, value: &usize) -> bool { value >= &0 && value < &self.len() } 44 | } 45 | 46 | impl OrderedSpace for partitions::Declarative { 47 | fn inf(&self) -> Option> { 48 | Some(OpenOrClosed::Closed(0)) 49 | } 50 | 51 | fn sup(&self) -> Option> { 52 | Some(OpenOrClosed::Closed(self.len() - 1)) 53 | } 54 | } 55 | 56 | impl FiniteSpace for partitions::Declarative { 57 | fn cardinality(&self) -> usize { self.len() } 58 | } 59 | 60 | impl IterableSpace for partitions::Declarative { 61 | type ElemIter = Range; 62 | 63 | fn elements(&self) -> Self::ElemIter { 0..N } 64 | } 65 | -------------------------------------------------------------------------------- /src/real.rs: -------------------------------------------------------------------------------- 1 | //! Module for real scalar spaces. 2 | use crate::intervals; 3 | use num_traits::real::Real; 4 | 5 | /// Build a space representing the set of all real numbers. 6 | pub fn reals() -> Reals { intervals::Interval::unbounded() } 7 | 8 | pub type Reals = intervals::Unbounded; 9 | 10 | /// Build a space representing the set of non-negative real numbers. 11 | pub fn non_negative_reals() -> NonNegativeReals { 12 | intervals::Interval::left_closed(V::zero()) 13 | } 14 | 15 | pub type NonNegativeReals = intervals::LeftClosed; 16 | 17 | /// Build a space representing the set of strictly positive real numbers. 18 | pub fn positive_reals() -> PositiveReals { intervals::Interval::left_open(V::zero()) } 19 | 20 | pub type PositiveReals = intervals::LeftOpen; 21 | 22 | /// Build a space representing the set of non-positive real numbers. 23 | pub fn non_positive_reals() -> NonPositiveReals { 24 | intervals::Interval::right_closed(V::zero()) 25 | } 26 | 27 | pub type NonPositiveReals = intervals::RightClosed; 28 | 29 | /// Build a space representing the set of strictly negative real numbers. 30 | pub fn negative_reals() -> NegativeReals { intervals::Interval::right_open(V::zero()) } 31 | 32 | pub type NegativeReals = intervals::RightOpen; 33 | -------------------------------------------------------------------------------- /src/tuples.rs: -------------------------------------------------------------------------------- 1 | use crate::prelude::*; 2 | 3 | macro_rules! stripped { 4 | (* $($rest: tt)*) => { 5 | $($rest)* 6 | }; 7 | (|| $($rest: tt)*) => { 8 | $($rest)* 9 | }; 10 | (&& $($rest: tt)*) => { 11 | $($rest)* 12 | }; 13 | } 14 | 15 | macro_rules! impl_tuple { 16 | ($n:literal; $(($tp:ident, $vp:ident)::$i:tt),+) => { 17 | impl<$($tp: Space),+> Space for ($($tp),+) { 18 | type Value = ($($tp::Value),+); 19 | 20 | fn is_empty(&self) -> bool { 21 | stripped!($(|| self.$i.is_empty())+) 22 | } 23 | 24 | fn contains(&self, val: &Self::Value) -> bool { 25 | stripped!($(&& self.$i.contains(&val.$i))+) 26 | } 27 | } 28 | 29 | impl<$($tp: FiniteSpace),+> FiniteSpace for ($($tp),+) { 30 | fn cardinality(&self) -> usize { 31 | stripped!($(* self.$i.cardinality())+) 32 | } 33 | } 34 | 35 | // impl<$($tp: Union),+> Union for ($($tp),+) { 36 | // fn union(self, other: &Self) -> Self { 37 | // ($(self.$i.union(&other.$i)),+).into() 38 | // } 39 | // } 40 | 41 | // impl<$($tp: Intersect),+> Intersect for ($($tp),+) { 42 | // fn intersect(self, other: &Self) -> Self { 43 | // ($(self.$i.intersect(&other.$i)),+).into() 44 | // } 45 | // } 46 | } 47 | } 48 | 49 | impl_tuple!(2; (D1, X1)::0, (D2, X2)::1); 50 | impl_tuple!(3; (D1, X1)::0, (D2, X2)::1, (D3, X3)::2); 51 | impl_tuple!(4; (D1, X1)::0, (D2, X2)::1, (D3, X3)::2, (D4, X4)::3); 52 | impl_tuple!(5; (D1, X1)::0, (D2, X2)::1, (D3, X3)::2, (D4, X4)::3, (D5, X5)::4); 53 | impl_tuple!(6; (D1, X1)::0, (D2, X2)::1, (D3, X3)::2, (D4, X4)::3, (D5, X5)::4, (D6, X6)::5); 54 | impl_tuple!(7; (D1, X1)::0, (D2, X2)::1, (D3, X3)::2, (D4, X4)::3, (D5, X5)::4, (D6, X6)::5, (D7, X7)::6); 55 | impl_tuple!(8; (D1, X1)::0, (D2, X2)::1, (D3, X3)::2, (D4, X4)::3, (D5, X5)::4, (D6, X6)::5, (D7, X7)::6, (D8, X8)::7); 56 | impl_tuple!(9; (D1, X1)::0, (D2, X2)::1, (D3, X3)::2, (D4, X4)::3, (D5, X5)::4, (D6, X6)::5, (D7, X7)::6, (D8, X8)::7, (D9, X9)::8); 57 | impl_tuple!(10; (D1, X1)::0, (D2, X2)::1, (D3, X3)::2, (D4, X4)::3, (D5, X5)::4, (D6, X6)::5, (D7, X7)::6, (D8, X8)::7, (D9, X9)::8, (D10, X10)::9); 58 | impl_tuple!(11; (D1, X1)::0, (D2, X2)::1, (D3, X3)::2, (D4, X4)::3, (D5, X5)::4, (D6, X6)::5, (D7, X7)::6, (D8, X8)::7, (D9, X9)::8, (D10, X10)::9, (D11, X11)::10); 59 | impl_tuple!(12; (D1, X1)::0, (D2, X2)::1, (D3, X3)::2, (D4, X4)::3, (D5, X5)::4, (D6, X6)::5, (D7, X7)::6, (D8, X8)::7, (D9, X9)::8, (D10, X10)::9, (D11, X11)::10, (D12, X12)::11); 60 | 61 | #[cfg(test)] 62 | mod tests { 63 | use super::*; 64 | use crate::intervals::Interval; 65 | 66 | #[test] 67 | fn test_cardinality() { 68 | let a = Interval::lorc_unchecked(0usize, 2usize); 69 | let b = a.clone(); 70 | 71 | assert_eq!((a, b).cardinality(), 4); 72 | } 73 | } 74 | --------------------------------------------------------------------------------