From 605d663fa68d821152ab3c7de89261be8ff06cdd Mon Sep 17 00:00:00 2001 From: geekyjackson <105396509+the-one-and-only-jackson@users.noreply.github.com> Date: Sat, 27 May 2023 16:29:38 -0600 Subject: [PATCH 1/8] Removed redundant argument type AbstractRange isa AbstractArray --- src/basic.jl | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/basic.jl b/src/basic.jl index 3bf5314..5d896cf 100644 --- a/src/basic.jl +++ b/src/basic.jl @@ -9,12 +9,12 @@ struct UnknownSpaceStyle <: AbstractSpaceStyle end Holy-style trait that describes whether the space is continuous, finite discrete, or an unknown type. See CommonRLInterface for a more detailed description of the styles. """ -SpaceStyle(space::Any) = UnknownSpaceStyle() +SpaceStyle(::Any) = UnknownSpaceStyle() SpaceStyle(::Tuple) = FiniteSpaceStyle() SpaceStyle(::NamedTuple) = FiniteSpaceStyle() -function SpaceStyle(x::Union{AbstractArray,AbstractDict,AbstractSet,AbstractRange}) +function SpaceStyle(x::Union{AbstractArray,AbstractDict,AbstractSet}) if Base.IteratorSize(x) isa Union{Base.HasLength, Base.HasShape} && length(x) < Inf return FiniteSpaceStyle() else From 462a7bc93add91fca6c121d335359d9fd2d2fd17 Mon Sep 17 00:00:00 2001 From: geekyjackson <105396509+the-one-and-only-jackson@users.noreply.github.com> Date: Sat, 27 May 2023 18:46:04 -0600 Subject: [PATCH 2/8] Update to RepeatedSpace and ArraySpace ArraySpace now constructs either a ReapeatedSpace or Box based on the base_space. --- src/CommonRLSpaces.jl | 1 + src/array.jl | 35 +++++++++++++++++++++++++++++------ test/array.jl | 10 ++++++---- 3 files changed, 36 insertions(+), 10 deletions(-) diff --git a/src/CommonRLSpaces.jl b/src/CommonRLSpaces.jl index 7b68225..f654587 100644 --- a/src/CommonRLSpaces.jl +++ b/src/CommonRLSpaces.jl @@ -22,6 +22,7 @@ include("basic.jl") export Box, + RepeatedSpace, ArraySpace include("array.jl") diff --git a/src/array.jl b/src/array.jl index 7f764e5..26e1872 100644 --- a/src/array.jl +++ b/src/array.jl @@ -52,17 +52,18 @@ Base.clamp(x::AbstractArray, b::Box) = clamp.(x, b.lower, b.upper) Base.convert(t::Type{<:Box}, i::ClosedInterval) = t(SA[minimum(i)], SA[maximum(i)]) +""" + RepeatedSpace(base_space, elsize) + +A RepeatedSpace reperesents a space of arrays with shape `elsize`, where each element of +the array is drawn from `base_space`. +""" struct RepeatedSpace{B, S<:Tuple} <: AbstractArraySpace base_space::B elsize::S end -""" - ArraySpace(base_space, size...) - -Create a space of Arrays with shape `size`, where each element of the array is drawn from `base_space`. -""" -ArraySpace(base_space, size...) = RepeatedSpace(base_space, size) +RepeatedSpace(base_size, elsize...) = RepeatedSpace(base_size, elsize) SpaceStyle(s::RepeatedSpace) = SpaceStyle(s.base_space) @@ -79,3 +80,25 @@ function bounds(s::RepeatedSpace) end Base.clamp(x::AbstractArray, s::RepeatedSpace) = map(entry -> clamp(entry, s.base_space), x) + +""" + ArraySpace(base_space, size...) + +Constructor for RepeatedSpace and Box. + +If `base_space` is an AbstractFloat or ClosedInterval return a Box (preferred), otherwise +return a RepeatedSpace. +""" +ArraySpace(base_space, size...) = RepeatedSpace(base_space, size) + +function ArraySpace(::Type{T}, size...) where {T<:AbstractFloat} + lower = fill(typemin(T), size) + upper = fill(typemax(T), size) + return Box(lower, upper) +end + +function ArraySpace(i::ClosedInterval, size...) + lower = fill(minimum(i), size) + upper = fill(maximum(T), size) + return Box(lower, upper) +end \ No newline at end of file diff --git a/test/array.jl b/test/array.jl index 30e3fec..7d258dd 100644 --- a/test/array.jl +++ b/test/array.jl @@ -61,9 +61,11 @@ @testset "Interval to box conversion" begin @test convert(Box, 1..2) == Box([1], [2]) end +end - @testset "ArraySpace with Range" begin - s = ArraySpace(1:5, 3, 4) +@testset "RepeatedSpace" begin + @testset "RepeatedSpace with Range" begin + s = RepeatedSpace(1:5, 3, 4) @test @inferred SpaceStyle(s) == FiniteSpaceStyle() @test eltype(s) <: AbstractMatrix{eltype(1:5)} @test @inferred ones(Int, 3, 4) in s @@ -74,8 +76,8 @@ @test @inferred elsize(s) == (3,4) end - @testset "ArraySpace with IntervalSet" begin - s = ArraySpace(1..5, 3, 4) + @testset "RepeatedSpace with IntervalSet" begin + s = RepeatedSpace(1..5, 3, 4) @test @inferred SpaceStyle(s) == ContinuousSpaceStyle() @test eltype(s) <: AbstractMatrix{Float64} @test @inferred ones(Float64, 3, 4) in s From b8e8dc7ed20f5171c2ac4331e13e58c1ca2561b9 Mon Sep 17 00:00:00 2001 From: geekyjackson <105396509+the-one-and-only-jackson@users.noreply.github.com> Date: Sat, 27 May 2023 19:30:27 -0600 Subject: [PATCH 3/8] Formatted code to enforce maximum line widths --- src/array.jl | 21 ++++++++++++++++----- src/basic.jl | 16 ++++++++++++---- src/product.jl | 36 ++++++++++++++++++++++++++---------- 3 files changed, 54 insertions(+), 19 deletions(-) diff --git a/src/array.jl b/src/array.jl index 26e1872..7740c64 100644 --- a/src/array.jl +++ b/src/array.jl @@ -1,12 +1,17 @@ abstract type AbstractArraySpace end -# Maybe AbstractArraySpace should have an eltype parameter so that you could call convert(AbstractArraySpace{Float32}, space) +# Maybe AbstractArraySpace should have an eltype parameter so that you could call +# convert(AbstractArraySpace{Float32}, space) """ Box(lower, upper) -A Box represents a space of real-valued arrays bounded element-wise above by `upper` and below by `lower`, e.g. `Box([-1, -2], [3, 4]` represents the two-dimensional vector space that is the Cartesian product of the two closed sets: ``[-1, 3] \\times [-2, 4]``. +A Box represents a space of real-valued arrays bounded element-wise above by `upper` and +below by `lower`, e.g. `Box([-1, -2], [3, 4]` represents the two-dimensional vector space +that is the Cartesian product of the two closed sets: ``[-1, 3] \\times [-2, 4]``. -The elements of a Box are always `AbstractArray`s with `AbstractFloat` elements. `Box`es always have `ContinuousSpaceStyle`, and products of `Box`es with `Box`es or `ClosedInterval`s are `Box`es when the dimensions are compatible. +The elements of a Box are always `AbstractArray`s with `AbstractFloat` elements. `Box`es +always have `ContinuousSpaceStyle`, and products of `Box`es with `Box`es or +`ClosedInterval`s are `Box`es when the dimensions are compatible. """ struct Box{A<:AbstractArray{<:AbstractFloat}} <: AbstractArraySpace lower::A @@ -67,11 +72,17 @@ RepeatedSpace(base_size, elsize...) = RepeatedSpace(base_size, elsize) SpaceStyle(s::RepeatedSpace) = SpaceStyle(s.base_space) -Base.rand(rng::AbstractRNG, sp::Random.SamplerTrivial{<:RepeatedSpace}) = rand(rng, sp[].base_space, sp[].elsize...) +function Base.rand(rng::AbstractRNG, sp::Random.SamplerTrivial{<:RepeatedSpace}) + return rand(rng, sp[].base_space, sp[].elsize...) +end Base.in(x::AbstractArray, s::RepeatedSpace) = all(entry in s.base_space for entry in x) + Base.eltype(s::RepeatedSpace) = AbstractArray{eltype(s.base_space), length(s.elsize)} -Base.eltype(s::RepeatedSpace{<:AbstractInterval}) = AbstractArray{Random.gentype(s.base_space), length(s.elsize)} +function Base.eltype(s::RepeatedSpace{<:AbstractInterval}) + return AbstractArray{Random.gentype(s.base_space), length(s.elsize)} +end + elsize(s::RepeatedSpace) = s.elsize function bounds(s::RepeatedSpace) diff --git a/src/basic.jl b/src/basic.jl index 5d896cf..79e6697 100644 --- a/src/basic.jl +++ b/src/basic.jl @@ -7,7 +7,8 @@ struct UnknownSpaceStyle <: AbstractSpaceStyle end """ SpaceStyle(space) -Holy-style trait that describes whether the space is continuous, finite discrete, or an unknown type. See CommonRLInterface for a more detailed description of the styles. +Holy-style trait that describes whether the space is continuous, finite discrete, or an +unknown type. See CommonRLInterface for a more detailed description of the styles. """ SpaceStyle(::Any) = UnknownSpaceStyle() @@ -31,7 +32,8 @@ promote_spacestyle(_, _) = UnknownSpaceStyle() # handle case of 3 or more promote_spacestyle(s1, s2, s3, others...) = foldl(promote_spacestyle, (s1, s2, s3, args...)) -"Return the size of the objects in a space. This is guaranteed to be defined if the objects in the space are arrays, but otherwise it may not be defined." +"Return the size of the objects in a space. This is guaranteed to be defined if the objects +in the space are arrays, but otherwise it may not be defined." function elsize end # note: different than Base.elsize """ @@ -39,7 +41,10 @@ function elsize end # note: different than Base.elsize Return a `Tuple` containing lower and upper bounds for the elements in a space. -For example, if `space` is a unit circle, `bounds(space)` will return `([-1.0, -1.0], [1.0, 1.0])`. This allows agents to choose policies that appropriately cover the space e.g. a normal distribution with a mean of `mean(bounds(space))` and a standard deviation of half the distance between the bounds. +For example, if `space` is a unit circle, `bounds(space)` will return `([-1.0, -1.0], +[1.0, 1.0])`. This allows agents to choose policies that appropriately cover the space +e.g. a normal distribution with a mean of `mean(bounds(space))` and a standard deviation +of half the distance between the bounds. `bounds` should be defined for ContinuousSpaceStyle spaces. @@ -56,7 +61,10 @@ function bounds end Return an element of `space` that is near `x`. -For example, if `space` is a unit circle, `clamp([2.0, 0.0], space)` might return `[1.0, 0.0]`. This allows for a convenient way for an agent to find a valid action if they sample actions from a distribution that doesn't match the space exactly (e.g. a normal distribution). +For example, if `space` is a unit circle, `clamp([2.0, 0.0], space)` might return `[1.0, +0.0]`. This allows for a convenient way for an agent to find a valid action if they sample +actions from a distribution that doesn't match the space exactly (e.g. a normal +distribution). """ function clamp end diff --git a/src/product.jl b/src/product.jl index f8e5506..2fb4532 100644 --- a/src/product.jl +++ b/src/product.jl @@ -1,8 +1,14 @@ -product(i1::ClosedInterval, i2::ClosedInterval) = Box(SA[minimum(i1), minimum(i2)], SA[maximum(i1), maximum(i2)]) +function product(i1::ClosedInterval, i2::ClosedInterval) + return Box(SA[minimum(i1), minimum(i2)], SA[maximum(i1), maximum(i2)]) +end product(b::Box, i::ClosedInterval) = product(b, convert(Box, i)) product(i::ClosedInterval, b::Box) = product(convert(Box, i), b) -product(b1::Box{<:AbstractVector}, b2::Box{<:AbstractVector}) = Box(vcat(b1.lower, b2.lower), vcat(b1.upper, b2.upper)) + +function product(b1::Box{<:AbstractVector}, b2::Box{<:AbstractVector}) + return Box(vcat(b1.lower, b2.lower), vcat(b1.upper, b2.upper)) +end + function product(b1::Box, b2::Box) if size(b1.lower, 2) == size(b2.lower, 2) # same number of columns return Box(vcat(b1.lower, b2.lower), vcat(b1.upper, b2.upper)) @@ -11,8 +17,8 @@ function product(b1::Box, b2::Box) end end -# handle case of 3 or more -product(s1, s2, s3, args...) = foldl(product, (s1, s2, s3, args...)) # not totally sure if this should be foldl or foldr +# handle case of 3 or more. Not totally sure if this should be foldl or foldr +product(s1, s2, s3, args...) = foldl(product, (s1, s2, s3, args...)) struct TupleProduct{T<:Tuple} ss::T @@ -21,7 +27,8 @@ end """ TupleProduct(space1, space2, ...) -Create a space representing the Cartesian product of the argument. Each element is a `Tuple` containing one element from each of the constituent spaces. +Create a space representing the Cartesian product of the argument. Each element is a `Tuple` +containing one element from each of the constituent spaces. Use `subspaces` to access a `Tuple` containing the constituent spaces. """ @@ -32,9 +39,15 @@ subspaces(s::TupleProduct) = s.ss product(s1::TupleProduct, s2::TupleProduct) = TupleProduct(subspaces(s1)..., subspaces(s2)...) -# handle any case not covered elsewhere by making a TupleProduct -# if one of the members is already a TupleProduct, we add put them together in a new "flat" TupleProduct -# note: if we had defined product(s1::TupleProduct, s2) it might be annoying because product(s1, s2::AnotherProduct) would be ambiguous with it +#= +Handle any case not covered elsewhere by making a TupleProduct + +If one of the members is already a TupleProduct, we add put them together in a new "flat" +TupleProduct + +Note: if we had defined product(s1::TupleProduct, s2) it might be annoying because +product(s1, s2::AnotherProduct) would be ambiguous with it +=# function product(s1, s2) if s1 isa TupleProduct return TupleProduct(subspaces(s1)..., s2) @@ -47,13 +60,16 @@ end SpaceStyle(s::TupleProduct) = promote_spacestyle(map(SpaceStyle, subspaces(s))...) -Base.rand(rng::AbstractRNG, sp::Random.SamplerTrivial{<:TupleProduct}) = map(s->rand(rng, s), subspaces(sp[])) +function Base.rand(rng::AbstractRNG, sp::Random.SamplerTrivial{<:TupleProduct}) + return map(s -> rand(rng, s), subspaces(sp[])) +end + function Base.in(element, space::TupleProduct) @assert length(element) == length(subspaces(space)) return all(element[i] in s for (i, s) in enumerate(subspaces(space))) end -Base.eltype(space::TupleProduct) = Tuple{map(eltype, subspaces(space))...} +Base.eltype(space::TupleProduct) = Tuple{map(eltype, subspaces(space))...} Base.length(space::TupleProduct) = mapreduce(length, *, subspaces(space)) Base.iterate(space, args...) = iterate(Iterators.product(subspaces(space)...), args...) From 889dbc5c4eb86c76ad6b62484cb0d1e8b4edbe38 Mon Sep 17 00:00:00 2001 From: geekyjackson <105396509+the-one-and-only-jackson@users.noreply.github.com> Date: Sat, 27 May 2023 21:43:10 -0600 Subject: [PATCH 4/8] Fixed Box construction Promotion should happen regardless of conversion to StaticArray. --- src/array.jl | 16 ++++++++++------ test/array.jl | 20 ++++++++++++++++++++ 2 files changed, 30 insertions(+), 6 deletions(-) diff --git a/src/array.jl b/src/array.jl index 7740c64..5868e3a 100644 --- a/src/array.jl +++ b/src/array.jl @@ -22,18 +22,22 @@ end function Box(lower, upper; convert_to_static::Bool=false) @assert size(lower) == size(upper) - sz = size(lower) - continuous_lower = convert(AbstractArray{float(eltype(lower))}, lower) - continuous_upper = convert(AbstractArray{float(eltype(upper))}, upper) + T = promote_type(eltype(lower), eltype(upper)) |> float + continuous_lower = convert(AbstractArray{T}, lower) + continuous_upper = convert(AbstractArray{T}, upper) if convert_to_static - final_lower = SArray{Tuple{sz...}}(continuous_lower) - final_upper = SArray{Tuple{sz...}}(continuous_upper) + final_lower = SArray{Tuple{size(continuous_lower)...}}(continuous_lower) + final_upper = SArray{Tuple{size(continuous_upper)...}}(continuous_upper) else - final_lower, final_upper = promote(continuous_lower, continuous_upper) + final_lower, final_upper = continuous_lower, continuous_upper end return Box{typeof(final_lower)}(final_lower, final_upper) end +function Base.:(==)(b1::T, b2::T) where {T <: Box} + return (b1.lower == b2.lower) && (b1.upper == b2.upper) +end + # By default, convert builtin arrays to static Box(lower::Array, upper::Array) = Box(lower, upper; convert_to_static=true) diff --git a/test/array.jl b/test/array.jl index 7d258dd..5e5f951 100644 --- a/test/array.jl +++ b/test/array.jl @@ -58,6 +58,26 @@ @test Box([1,2], [3,4]) != Box([1,3], [3,4]) end + @testset "Box type check" begin + T = [ + BigFloat, Float64, Float32, Float16, + BigInt, Int128, Int64, Int32, Int16, Int8, + UInt128, UInt64, UInt16, UInt32, UInt8 + ] + for T1 in T, T2 in T + x, y = [1,2], [3,4] + box = Box(T1.(x), T2.(y)) + T_goal = float(promote_type(T1, T2)) + box_goal = Box{SVector{2, T_goal}}( + SVector{2,T_goal}(T_goal.(x)), + SVector{2,T_goal}(T_goal.(y)) + ) + @testset "$T1, $T2" begin + @test box == box_goal + end + end + end + @testset "Interval to box conversion" begin @test convert(Box, 1..2) == Box([1], [2]) end From abe30f1da8b2a3130086d1ec9d4e88fbb218cc7d Mon Sep 17 00:00:00 2001 From: geekyjackson <105396509+the-one-and-only-jackson@users.noreply.github.com> Date: Sat, 27 May 2023 22:54:34 -0600 Subject: [PATCH 5/8] Updated random sampling of Box For added consistency with gymnasium: - Finite intervals are sampled uniformly, - Infinite intervals are sampled normally, - Semi-infinite intervals are sampled as shifted exponential distributions. --- Project.toml | 1 + src/CommonRLSpaces.jl | 1 + src/array.jl | 56 +++++++++++++++++++++++++++++++++++++++---- test/array.jl | 9 +++++++ test/runtests.jl | 2 ++ 5 files changed, 65 insertions(+), 4 deletions(-) diff --git a/Project.toml b/Project.toml index 6c0a2d4..bce2266 100644 --- a/Project.toml +++ b/Project.toml @@ -4,6 +4,7 @@ authors = ["Jun Tian and contributors"] version = "0.2.1" [deps] +Distributions = "31c24e10-a181-5473-b8eb-7969acd0382f" FillArrays = "1a297f60-69ca-5386-bcde-b61e274b549b" IntervalSets = "8197267c-284f-5f27-9208-e0e47529a953" Random = "9a3f8284-a2c9-5f02-9a11-845980a1fd5c" diff --git a/src/CommonRLSpaces.jl b/src/CommonRLSpaces.jl index f654587..c1c2a19 100644 --- a/src/CommonRLSpaces.jl +++ b/src/CommonRLSpaces.jl @@ -7,6 +7,7 @@ using Reexport using StaticArrays using FillArrays using Random +using Distributions import Base: clamp export diff --git a/src/array.jl b/src/array.jl index 5868e3a..c33a8ca 100644 --- a/src/array.jl +++ b/src/array.jl @@ -43,13 +43,61 @@ Box(lower::Array, upper::Array) = Box(lower, upper; convert_to_static=true) SpaceStyle(::Box) = ContinuousSpaceStyle() -function Base.rand(rng::AbstractRNG, sp::Random.SamplerTrivial{<:Box}) +""" + Base.rand(::AbstractRNG, ::Random.SamplerTrivial{<:Box}) + +Generate an array where each element is sampled from a dimension of a Box space. + + * Finite intervals [a,b] are sampled from uniform distributions. + * Semi-infinite intervals (a,Inf) and (-Inf,b) are sampled from shifted exponential distributions. + * Infinite intervals (-Inf,Inf) are sampled from normal distributions. + +#Example +```julia +julia> using Random: seed! + +julia> using Distributions: Uniform, Normal, Exponential + +julia> box = Box([-10, -Inf, 3], [10, Inf, Inf]) +Box{StaticArraysCore.SVector{3, Float64}}([-10.0, -Inf, 3.0], [10.0, Inf, Inf]) + +julia> seed!(0); rand(box) +3-element StaticArraysCore.SVector{3, Float64} with indices SOneTo(3): + -1.8860105821594164 + 0.13392275765318448 + 3.837385552384043 + +julia> seed!(0); [rand(Uniform(-10,10)), rand(Normal()), 3+rand(Exponential())] +3-element Vector{Float64}: + -1.8860105821594164 + 0.13392275765318448 + 3.837385552384043 +``` +""" +function Base.rand(rng::AbstractRNG, sp::Random.SamplerTrivial{Box{T}}) where {T} box = sp[] - return box.lower + rand_similar(rng, box.lower) .* (box.upper-box.lower) + x = [rand_interval(rng, lb, ub) for (lb, ub) in zip(box.lower, box.upper)] + return T(x) end -rand_similar(rng::AbstractRNG, a::StaticArray) = rand(rng, typeof(a)) -rand_similar(rng::AbstractRNG, a::AbstractArray) = rand(rng, eltype(a), size(a)...) +function rand_interval(rng::AbstractRNG, lb::T, ub::T) where {T <: Real} + offset, sign = zero(T), one(T) + + if isfinite(lb) && isfinite(ub) + dist = Uniform(lb, ub) + elseif isfinite(lb) && !isfinite(ub) + offset = lb + dist = Exponential(one(T)) + elseif !isfinite(lb) && isfinite(ub) + offset = ub + sign = -one(T) + dist = Exponential(one(T)) + else + dist = Normal(zero(T), one(T)) + end + + return offset + sign * rand(rng, dist) +end Base.in(x::AbstractArray, b::Box) = all(b.lower .<= x .<= b.upper) diff --git a/test/array.jl b/test/array.jl index 5e5f951..f0a84d8 100644 --- a/test/array.jl +++ b/test/array.jl @@ -78,6 +78,15 @@ end end + @testset "Box random sample" begin + box = Box([-10, -Inf, 3, -Inf], [10, Inf, Inf, 6]) + Random.seed!(0) + x = rand(box) + Random.seed!(0) + y = SA[rand(Uniform(-10, 10)), rand(Normal()), 3+rand(Exponential()), 6-rand(Exponential())] + @test x == y + end + @testset "Interval to box conversion" begin @test convert(Box, 1..2) == Box([1], [2]) end diff --git a/test/runtests.jl b/test/runtests.jl index 7cc5f6d..5380a7b 100644 --- a/test/runtests.jl +++ b/test/runtests.jl @@ -2,6 +2,8 @@ using CommonRLSpaces using Test using StaticArrays +using Distributions +using Random @testset "CommonRLSpaces.jl" begin include("basic.jl") From 235fb1cc1d456a9099c5c0f9cb5ed79e33c3043b Mon Sep 17 00:00:00 2001 From: geekyjackson <105396509+the-one-and-only-jackson@users.noreply.github.com> Date: Sun, 28 May 2023 16:05:32 -0600 Subject: [PATCH 6/8] Update README.md --- README.md | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/README.md b/README.md index 2e07329..23bb08b 100644 --- a/README.md +++ b/README.md @@ -41,7 +41,7 @@ Continuous spaces have some additional interface functions: - `bounds(space)` returns upper and lower bounds in a tuple. For example, if `space` is a unit circle, `bounds(space)` will return `([-1.0, -1.0], [1.0, 1.0])`. This allows agents to choose policies that appropriately cover the space e.g. a normal distribution with a mean of `mean(bounds(space))` and a standard deviation of half the distance between the bounds. - `clamp(x, space)` returns an element of `space` that is near `x`. i.e. if `space` is a unit circle, `clamp([2.0, 0.0], space)` might return `[1.0, 0.0]`. This allows for a convenient way for an agent to find a valid action if they sample actions from a distribution that doesn't match the space exactly (e.g. a normal distribution). -- `clamp!(x, space)`, similar to `clamp`, but clamps `x` in place. +- [Not implemented] `clamp!(x, space)`, similar to `clamp`, but clamps `x` in place. ### Hybrid spaces @@ -70,12 +70,12 @@ The `TupleSpaceProduct` constructor provides a specialized Cartesian product whe |Category|Style|Example| |:---|:----|:-----| -|Enumerable discrete space| `FiniteSpaceStyle{()}()` | `(:cat, :dog)`, `0:1`, `["a","b","c"]` | -|One dimensional continuous space| `ContinuousSpaceStyle{()}()` | `-1.2..3.3`, `Interval(1.0, 2.0)` | -|Multi-dimensional discrete space| `FiniteSpaceStyle{(3,4)}()` | `ArraySpace((:cat, :dog), 3, 4)`, `ArraySpace(0:1, 3, 4)`, `ArraySpace(1:2, 3, 4)`, `ArraySpace(Bool, 3, 4)`| -|Multi-dimensional variable discrete space| `FiniteSpaceStyle{(2,)}()` | `product((:cat, :dog), (:litchi, :longan, :mango))`, `product(-1:1, (false, true))`| -|Multi-dimensional continuous space| `ContinuousSpaceStyle{(2,)}()` or `ContinuousSpaceStyle{(3,4)}()` | `Box([-1.0, -2.0], [2.0, 4.0])`, `product(-1.2..3.3, -4.6..5.0)`, `ArraySpace(-1.2..3.3, 3, 4)`, `ArraySpace(Float32, 3, 4)` | -|Multi-dimensional hybrid space [planned for future]| `HybridSpaceStyle{(2,),()}()` | `product(-1.2..3.3, -4.6..5.0, [:cat, :dog])`, `product(Box([-1.0, -2.0], [2.0, 4.0]), [1,2,3])`| +|Enumerable discrete space| `FiniteSpaceStyle()` | `(:cat, :dog)`, `0:1`, `["a","b","c"]` | +|One dimensional continuous space| `ContinuousSpaceStyle()` | `-1.2..3.3`, `Interval(1.0, 2.0)` | +|Multi-dimensional discrete space| `FiniteSpaceStyle()` | `ArraySpace((:cat, :dog), 3, 4)`, `ArraySpace(0:1, 3, 4)`, `ArraySpace(1:2, 3, 4)`, `ArraySpace(Bool, 3, 4)`| +|Multi-dimensional variable discrete space| `FiniteSpaceStyle()` | `product((:cat, :dog), (:litchi, :longan, :mango))`, `product(-1:1, (false, true))`| +|Multi-dimensional continuous space| `ContinuousSpaceStyle()` or `ContinuousSpaceStyle()` | `Box([-1.0, -2.0], [2.0, 4.0])`, `product(-1.2..3.3, -4.6..5.0)`, `ArraySpace(-1.2..3.3, 3, 4)`, `ArraySpace(Float32, 3, 4)` | +|Multi-dimensional hybrid space [planned for future]| `HybridSpaceStyle()` | `product(-1.2..3.3, -4.6..5.0, [:cat, :dog])`, `product(Box([-1.0, -2.0], [2.0, 4.0]), [1,2,3])`| ### API From 08644e546b53563799c028c10b417d4d81b5ae8d Mon Sep 17 00:00:00 2001 From: geekyjackson <105396509+the-one-and-only-jackson@users.noreply.github.com> Date: Sun, 28 May 2023 16:56:31 -0600 Subject: [PATCH 7/8] Added HybridProductSpaceStyle --- src/CommonRLSpaces.jl | 1 + src/basic.jl | 9 ++++++--- test/product.jl | 11 ++++++++++- 3 files changed, 17 insertions(+), 4 deletions(-) diff --git a/src/CommonRLSpaces.jl b/src/CommonRLSpaces.jl index c1c2a19..9802b78 100644 --- a/src/CommonRLSpaces.jl +++ b/src/CommonRLSpaces.jl @@ -15,6 +15,7 @@ export AbstractSpaceStyle, FiniteSpaceStyle, ContinuousSpaceStyle, + HybridSpaceStyle, UnknownSpaceStyle, bounds, elsize diff --git a/src/basic.jl b/src/basic.jl index 79e6697..b0b398e 100644 --- a/src/basic.jl +++ b/src/basic.jl @@ -2,6 +2,7 @@ abstract type AbstractSpaceStyle end struct FiniteSpaceStyle <: AbstractSpaceStyle end struct ContinuousSpaceStyle <: AbstractSpaceStyle end +struct HybridProductSpaceStyle <: AbstractSpaceStyle end struct UnknownSpaceStyle <: AbstractSpaceStyle end """ @@ -25,12 +26,14 @@ end SpaceStyle(::AbstractInterval) = ContinuousSpaceStyle() -promote_spacestyle(::FiniteSpaceStyle, ::FiniteSpaceStyle) = FiniteSpaceStyle() -promote_spacestyle(::ContinuousSpaceStyle, ::ContinuousSpaceStyle) = ContinuousSpaceStyle() +const BASE_SPACE_STYLES = Union{FiniteSpaceStyle, ContinuousSpaceStyle, HybridProductSpaceStyle} +function promote_spacestyle(::T1, ::T2) where {T1 <: BASE_SPACE_STYLES, T2 <: BASE_SPACE_STYLES} + return (T1 == T2) ? T1() : HybridProductSpaceStyle() +end promote_spacestyle(_, _) = UnknownSpaceStyle() # handle case of 3 or more -promote_spacestyle(s1, s2, s3, others...) = foldl(promote_spacestyle, (s1, s2, s3, args...)) +promote_spacestyle(s1, s2, s3, args...) = foldl(promote_spacestyle, (s1, s2, s3, args...)) "Return the size of the objects in a space. This is guaranteed to be defined if the objects in the space are arrays, but otherwise it may not be defined." diff --git a/test/product.jl b/test/product.jl index 0c89c5f..f558a15 100644 --- a/test/product.jl +++ b/test/product.jl @@ -31,11 +31,20 @@ end @test @inferred rand(tp) in tp @test (1,3) in tp @test !((1,2) in tp) - @test_broken eltype(tp) == Tuple{Float64, Float64} + @test_broken eltype(tp) == Tuple{Float64, Float64} # IntervalSets eltype -> Int64 @test @inferred SpaceStyle(tp) == ContinuousSpaceStyle() @test @inferred bounds(tp) == ((1,3), (2,4)) @test @inferred bounds(TupleProduct(1..2, 3..4, 5..6)) == ((1,3,5), (2,4,6)) @test @inferred clamp((0,0), tp) == (1, 3) end +@testset "TupleProduct hybrid" begin + tp = TupleProduct(1.0..2.0, [3,4]) + @test @inferred rand(tp) in tp + @test (1.5,3) in tp + @test !((1.5,3.5) in tp) + @test eltype(tp) == Tuple{Float64, Int64} + @test @inferred SpaceStyle(tp) == HybridSpaceStyle() +end + From e3fd034ec79aa360e45cea5ff6a4422c501462c9 Mon Sep 17 00:00:00 2001 From: geekyjackson <105396509+the-one-and-only-jackson@users.noreply.github.com> Date: Fri, 2 Jun 2023 19:14:18 -0600 Subject: [PATCH 8/8] Documentation --- README.md | 4 ++-- docs/make.jl | 9 +++++--- docs/src/array.md | 10 +++++++++ docs/src/extensions.md | 2 ++ docs/src/index.md | 25 +++++++++++++++++++--- src/CommonRLSpaces.jl | 3 ++- src/array.jl | 47 +++++++++++++++++++++++------------------- src/basic.jl | 25 +++++++++++++++------- 8 files changed, 88 insertions(+), 37 deletions(-) create mode 100644 docs/src/array.md create mode 100644 docs/src/extensions.md diff --git a/README.md b/README.md index 23bb08b..2fc302f 100644 --- a/README.md +++ b/README.md @@ -74,8 +74,8 @@ The `TupleSpaceProduct` constructor provides a specialized Cartesian product whe |One dimensional continuous space| `ContinuousSpaceStyle()` | `-1.2..3.3`, `Interval(1.0, 2.0)` | |Multi-dimensional discrete space| `FiniteSpaceStyle()` | `ArraySpace((:cat, :dog), 3, 4)`, `ArraySpace(0:1, 3, 4)`, `ArraySpace(1:2, 3, 4)`, `ArraySpace(Bool, 3, 4)`| |Multi-dimensional variable discrete space| `FiniteSpaceStyle()` | `product((:cat, :dog), (:litchi, :longan, :mango))`, `product(-1:1, (false, true))`| -|Multi-dimensional continuous space| `ContinuousSpaceStyle()` or `ContinuousSpaceStyle()` | `Box([-1.0, -2.0], [2.0, 4.0])`, `product(-1.2..3.3, -4.6..5.0)`, `ArraySpace(-1.2..3.3, 3, 4)`, `ArraySpace(Float32, 3, 4)` | -|Multi-dimensional hybrid space [planned for future]| `HybridSpaceStyle()` | `product(-1.2..3.3, -4.6..5.0, [:cat, :dog])`, `product(Box([-1.0, -2.0], [2.0, 4.0]), [1,2,3])`| +|Multi-dimensional continuous space| `ContinuousSpaceStyle()` | `Box([-1.0, -2.0], [2.0, 4.0])`, `product(-1.2..3.3, -4.6..5.0)`, `ArraySpace(-1.2..3.3, 3, 4)`, `ArraySpace(Float32, 3, 4)` | +|Multi-dimensional hybrid space [planned for future]| `HybridProductSpaceStyle()` | `product(-1.2..3.3, -4.6..5.0, [:cat, :dog])`, `product(Box([-1.0, -2.0], [2.0, 4.0]), [1,2,3])`| ### API diff --git a/docs/make.jl b/docs/make.jl index 1110ad5..8fc0fe8 100644 --- a/docs/make.jl +++ b/docs/make.jl @@ -1,4 +1,5 @@ using CommonRLSpaces +using Random using Documenter DocMeta.setdocmeta!(CommonRLSpaces, :DocTestSetup, :(using CommonRLSpaces); recursive=true) @@ -6,19 +7,21 @@ DocMeta.setdocmeta!(CommonRLSpaces, :DocTestSetup, :(using CommonRLSpaces); recu makedocs(; modules=[CommonRLSpaces], authors="Jun Tian and contributors", - repo="https://github.com/Jun Tian/CommonRLSpaces.jl/blob/{commit}{path}#{line}", + repo="https://github.com/JuliaReinforcementLearning/CommonRLSpaces.jl/blob/{commit}{path}#{line}", sitename="CommonRLSpaces.jl", format=Documenter.HTML(; prettyurls=get(ENV, "CI", "false") == "true", - canonical="https://Jun Tian.github.io/CommonRLSpaces.jl", + canonical="https://github.com/JuliaReinforcementLearning/CommonRLSpaces.jl", assets=String[], ), pages=[ "Home" => "index.md", + "array.md", + "extensions.md" ], ) deploydocs(; - repo="github.com/Jun Tian/CommonRLSpaces.jl", + repo="https://github.com/JuliaReinforcementLearning/CommonRLSpaces.jl", devbranch="main", ) diff --git a/docs/src/array.md b/docs/src/array.md new file mode 100644 index 0000000..ab39391 --- /dev/null +++ b/docs/src/array.md @@ -0,0 +1,10 @@ +# Array Spaces + +```@docs +AbstractArraySpace +elsize +Box +Base.rand(::AbstractRNG, ::Random.SamplerTrivial{Box{T}}) where {T} +RepeatedSpace +ArraySpace +``` \ No newline at end of file diff --git a/docs/src/extensions.md b/docs/src/extensions.md new file mode 100644 index 0000000..8d150c0 --- /dev/null +++ b/docs/src/extensions.md @@ -0,0 +1,2 @@ +# Extensions + diff --git a/docs/src/index.md b/docs/src/index.md index 6b853d7..1c44398 100644 --- a/docs/src/index.md +++ b/docs/src/index.md @@ -4,11 +4,30 @@ CurrentModule = CommonRLSpaces # CommonRLSpaces -Documentation for [CommonRLSpaces](https://github.com/Jun Tian/CommonRLSpaces.jl). +Documentation for [CommonRLSpaces](https://github.com/JuliaReinforcementLearning/CommonRLSpaces.jl). -```@index -``` +## Space Styles ```@autodocs Modules = [CommonRLSpaces] +Filter = t -> typeof(t) === DataType && t <: AbstractSpaceStyle +``` + +```@docs +SpaceStyle ``` + +## Interface + +Common + - Base.in + - Base.rand - https://docs.julialang.org/en/v1/stdlib/Random/#Hooking-into-the-Random-API + - Base.eltype + - product + +Finite + - Base.collect + +Continuous + - bounds + - Base.clamp \ No newline at end of file diff --git a/src/CommonRLSpaces.jl b/src/CommonRLSpaces.jl index 9802b78..2547c66 100644 --- a/src/CommonRLSpaces.jl +++ b/src/CommonRLSpaces.jl @@ -15,8 +15,9 @@ export AbstractSpaceStyle, FiniteSpaceStyle, ContinuousSpaceStyle, - HybridSpaceStyle, + HybridProductSpaceStyle, UnknownSpaceStyle, + AbstractArraySpace, bounds, elsize diff --git a/src/array.jl b/src/array.jl index c33a8ca..51b04e9 100644 --- a/src/array.jl +++ b/src/array.jl @@ -1,7 +1,20 @@ +""" + AbstractArraySpace + +Abstract base class for Array Spaces. +""" abstract type AbstractArraySpace end # Maybe AbstractArraySpace should have an eltype parameter so that you could call # convert(AbstractArraySpace{Float32}, space) +""" + elsize(::AbstractArraySpace) + +Return the size of the objects in a space. +""" +function elsize end # note: different than Base.elsize + + """ Box(lower, upper) @@ -49,29 +62,21 @@ SpaceStyle(::Box) = ContinuousSpaceStyle() Generate an array where each element is sampled from a dimension of a Box space. * Finite intervals [a,b] are sampled from uniform distributions. - * Semi-infinite intervals (a,Inf) and (-Inf,b) are sampled from shifted exponential distributions. + * Semi-infinite intervals (a,Inf) and (-Inf,b) are sampled from shifted exponential + distributions. * Infinite intervals (-Inf,Inf) are sampled from normal distributions. -#Example -```julia -julia> using Random: seed! - -julia> using Distributions: Uniform, Normal, Exponential - -julia> box = Box([-10, -Inf, 3], [10, Inf, Inf]) -Box{StaticArraysCore.SVector{3, Float64}}([-10.0, -Inf, 3.0], [10.0, Inf, Inf]) - -julia> seed!(0); rand(box) -3-element StaticArraysCore.SVector{3, Float64} with indices SOneTo(3): - -1.8860105821594164 - 0.13392275765318448 - 3.837385552384043 - -julia> seed!(0); [rand(Uniform(-10,10)), rand(Normal()), 3+rand(Exponential())] -3-element Vector{Float64}: - -1.8860105821594164 - 0.13392275765318448 - 3.837385552384043 +# Example + +```@repl +using CommonRLSpaces +using Random: seed! +using Distributions: Uniform, Normal, Exponential +box = Box([-10, -Inf, 3], [10, Inf, Inf]) +seed!(0) +rand(box) +seed!(0) +[rand(Uniform(-10,10)), rand(Normal()), 3+rand(Exponential())] ``` """ function Base.rand(rng::AbstractRNG, sp::Random.SamplerTrivial{Box{T}}) where {T} diff --git a/src/basic.jl b/src/basic.jl index b0b398e..d1d2508 100644 --- a/src/basic.jl +++ b/src/basic.jl @@ -1,15 +1,29 @@ +""" + AbstractSpaceStyle +""" abstract type AbstractSpaceStyle end - +""" + FiniteSpaceStyle <: AbstractSpaceStyle +""" struct FiniteSpaceStyle <: AbstractSpaceStyle end +""" + ContinuousSpaceStyle <: AbstractSpaceStyle +""" struct ContinuousSpaceStyle <: AbstractSpaceStyle end +""" + HybridProductSpaceStyle <: AbstractSpaceStyle +""" struct HybridProductSpaceStyle <: AbstractSpaceStyle end +""" + UnknownSpaceStyle <: AbstractSpaceStyle +""" struct UnknownSpaceStyle <: AbstractSpaceStyle end """ - SpaceStyle(space) + SpaceStyle(::Any) :: AbstractSpaceStyle -Holy-style trait that describes whether the space is continuous, finite discrete, or an -unknown type. See CommonRLInterface for a more detailed description of the styles. +Holy-style trait that describes whether the space is continuous, finite discrete, hybrid +product, or an unknown type. """ SpaceStyle(::Any) = UnknownSpaceStyle() @@ -35,9 +49,6 @@ promote_spacestyle(_, _) = UnknownSpaceStyle() # handle case of 3 or more promote_spacestyle(s1, s2, s3, args...) = foldl(promote_spacestyle, (s1, s2, s3, args...)) -"Return the size of the objects in a space. This is guaranteed to be defined if the objects -in the space are arrays, but otherwise it may not be defined." -function elsize end # note: different than Base.elsize """ bounds(space)