diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..6d5b2a9 --- /dev/null +++ b/.gitignore @@ -0,0 +1,6 @@ +*.jl.cov +*.jl.*.cov +*.jl.mem +deps/deps.jl + +Manifest.toml \ No newline at end of file diff --git a/.travis.yml b/.travis.yml new file mode 100644 index 0000000..eaa46d1 --- /dev/null +++ b/.travis.yml @@ -0,0 +1,30 @@ +## Documentation: http://docs.travis-ci.com/user/languages/julia/ +language: julia +os: + - linux + - osx +julia: + - 1.0 + - nightly +notifications: + email: false +git: + depth: 99999999 + +## uncomment the following lines to allow failures on nightly julia +## (tests will run but not make your overall status red) +#matrix: +# allow_failures: +# - julia: nightly + +## uncomment and modify the following lines to manually install system packages +#addons: +# apt: # apt-get for linux +# packages: +# - gfortran +#before_script: # homebrew for mac +# - if [ $TRAVIS_OS_NAME = osx ]; then brew install gcc; fi + +## uncomment the following lines to override the default test script +script: + - julia -e 'using Pkg; Pkg.activate(pwd()); Pkg.test()' \ No newline at end of file diff --git a/Project.toml b/Project.toml index 1a56e1b..6490d1f 100644 --- a/Project.toml +++ b/Project.toml @@ -1,6 +1,10 @@ -authors = ["Johanni Brea "] name = "ReinforcementLearningBase" uuid = "9b2b9cba-ac73-11e8-02b1-9f0869453fc0" +authors = ["Johanni Brea ", "Jun Tian "] version = "0.1.0" -[deps] +[extras] +Test = "8dfed614-e22c-5e08-85e1-65c5234f0b40" + +[targets] +test = ["Test"] diff --git a/README.md b/README.md new file mode 100644 index 0000000..00e1d15 --- /dev/null +++ b/README.md @@ -0,0 +1,5 @@ +# ReinforcementLearningBase.jl + +[![Build Status](https://travis-ci.com/JuliaReinforcementLearning/ReinforcementLearningBase.jl.svg?branch=master)](https://travis-ci.com/JuliaReinforcementLearning/ReinforcementLearningBase.jl) + +ReinforcementLearningBase.jl holds the common types and utility functions to be shared by other components in ReinforcementLearning ecosystem. \ No newline at end of file diff --git a/REQUIRE b/REQUIRE new file mode 100644 index 0000000..05b5ab4 --- /dev/null +++ b/REQUIRE @@ -0,0 +1 @@ +julia 1.0 diff --git a/src/ReinforcementLearningBase.jl b/src/ReinforcementLearningBase.jl index 3658dac..48b828c 100644 --- a/src/ReinforcementLearningBase.jl +++ b/src/ReinforcementLearningBase.jl @@ -1,5 +1,6 @@ module ReinforcementLearningBase -greet() = print("Hello World!") +include("spaces/space.jl") +include("abstractenv.jl") end # module diff --git a/src/abstractenv.jl b/src/abstractenv.jl new file mode 100644 index 0000000..5b15396 --- /dev/null +++ b/src/abstractenv.jl @@ -0,0 +1,17 @@ +export AbstractEnv +abstract type AbstractEnv end + +"Get current state of an environment" +function getstate end + +"Reset an environment to the initial state" +function reset! end + +"Take an action in an environment and return the current state" +function interact! end + +"Get the action space of an environment" +function actionspace end + +"Plot the current state of an environment" +function plotenv end \ No newline at end of file diff --git a/src/spaces/abstractspace.jl b/src/spaces/abstractspace.jl new file mode 100644 index 0000000..224435e --- /dev/null +++ b/src/spaces/abstractspace.jl @@ -0,0 +1,15 @@ +abstract type AbstractSpace end + +""" + sample(s::AbstractSpace) + +Get a random sample from `s`. +""" +function sample end + +""" + occursin(x, s::AbstractSpace) + +Return wheather `x` is a valid sample in space `s`. +""" +function occursin end \ No newline at end of file diff --git a/src/spaces/boxspace.jl b/src/spaces/boxspace.jl new file mode 100644 index 0000000..338f7ec --- /dev/null +++ b/src/spaces/boxspace.jl @@ -0,0 +1,34 @@ +"A box in R^n." +struct BoxSpace{T <: Number,N} <: AbstractSpace + low::Array{T,N} + high::Array{T,N} +end + +size(s::BoxSpace) = size(s.low) + +""" + BoxSpace(low::Number, high::Number, size::Tuple{Vararg{Int}}=(1,)) + +```julia +BoxSpace(-1, 1) +BoxSpace(-1, 1, (2,3)) +``` +""" +BoxSpace(low::Number, high::Number, size::Tuple{Vararg{Int}}=(1,)) = BoxSpace(fill(low, size), fill(high, size)) + +""" + BoxSpace(low::Array{<:Number}, high::Array{<:Number}) + +```julia +BoxSpace([0, 0, 0], [1, 2, 3]) +``` +""" +BoxSpace(low::Array{<:Number}, high::Array{<:Number}) = size(low) == size(high) && BoxSpace(low, high) + +==(x::BoxSpace, y::BoxSpace) = x.low == y.low && x.high == y.high + +sample(s::BoxSpace) = map((l, h) -> l + rand() * (h - l), s.low, s.high) + +occursin(xs::Array{<:Number}, s::BoxSpace) = size(s) == size(xs) && + all(map((a, b, c) -> a ≤ b ≤ c, s.low, xs, s.high)) +occursin(x::Number, s::BoxSpace) = all(map((l, h) -> l ≤ x ≤ h, s.low, s.high)) \ No newline at end of file diff --git a/src/spaces/discretespace.jl b/src/spaces/discretespace.jl new file mode 100644 index 0000000..0783b0e --- /dev/null +++ b/src/spaces/discretespace.jl @@ -0,0 +1,9 @@ +struct DiscreteSpace <: AbstractSpace + n::Int + offset::Int +end + +size(d::DiscreteSpace) = 1 +sample(d::DiscreteSpace) = rand(d.offset : d.n + d.offset - 1) +occursin(x::Int, d::DiscreteSpace) = d.offset ≤ x < d.offset + d.n +==(x::DiscreteSpace, y::DiscreteSpace) = x.n == y.n && x.offset == y.offset \ No newline at end of file diff --git a/src/spaces/multibinaryspace.jl b/src/spaces/multibinaryspace.jl new file mode 100644 index 0000000..6050ab6 --- /dev/null +++ b/src/spaces/multibinaryspace.jl @@ -0,0 +1,11 @@ +struct MultiBinarySpace <: AbstractSpace + size::Tuple{Vararg{Int}} +end + +MultiBinarySpace(sz::Vararg{Int}) = MultiBinarySpace(sz) + +size(s::MultiBinarySpace) = s.size + +sample(s::MultiBinarySpace) = rand(Bool, s.size...) +occursin(x::Array{Bool}, s::MultiBinarySpace) = size(s) == size(x) +==(x::MultiBinarySpace, y::MultiBinarySpace) = x.size == y.size \ No newline at end of file diff --git a/src/spaces/multidiscretespace.jl b/src/spaces/multidiscretespace.jl new file mode 100644 index 0000000..4f7617a --- /dev/null +++ b/src/spaces/multidiscretespace.jl @@ -0,0 +1,13 @@ +struct MultiDiscreteSpace{N} <:AbstractSpace + counts::Array{Int, N} + offset::Int +end + +size(s::MultiDiscreteSpace) = size(s.counts) + +"To compat with Python, here we start with 0" +sample(s::MultiDiscreteSpace) = map(x -> rand(s.offset : x + s.offset -1), s.counts) +occursin(x::Int, s::MultiDiscreteSpace) = all(e -> s.offset ≤ x < e + s.offset, s.counts) +occursin(xs::Array{Int}, s::MultiDiscreteSpace) = size(s) == size(xs) && + all(map((e, x) -> s.offset ≤ x < e + s.offset , s.counts, xs)) +==(x::MultiDiscreteSpace, y::MultiDiscreteSpace) = x.counts == y.counts && x.offset == y.offset \ No newline at end of file diff --git a/src/spaces/space.jl b/src/spaces/space.jl new file mode 100644 index 0000000..bf140a5 --- /dev/null +++ b/src/spaces/space.jl @@ -0,0 +1,26 @@ +import Base:occursin, size, == +export AbstractSpace, + BoxSpace, + DiscreteSpace, + MultiBinarySpace, + MultiDiscreteSpace, + sample + +include("abstractspace.jl") +include("boxspace.jl") +include("discretespace.jl") +include("multibinaryspace.jl") +include("multidiscretespace.jl") + +# Tuple Support +sample(s::Tuple{Vararg{<:AbstractSpace}}) = map(sample, s) +occursin(a::Tuple, b::Tuple{Vararg{<:AbstractSpace}}) = length(a) == length(b) && + all(map((x, y) -> occursin(x, y), a, b)) + +# Dict Support +sample(s::Dict{String}) = Dict(map((k, v) -> (k, sample(v)), s)) +occursin(a::Dict{String}, b::Dict{String}) = length(a) == length(b) && + all(p -> haskey(a, p.first) ? + occursin(a[p.first], p.second) : + false, + b) \ No newline at end of file diff --git a/test/runtests.jl b/test/runtests.jl new file mode 100644 index 0000000..098fd48 --- /dev/null +++ b/test/runtests.jl @@ -0,0 +1,4 @@ +using ReinforcementLearningBase +using Test + +include("space.jl") \ No newline at end of file diff --git a/test/space.jl b/test/space.jl new file mode 100644 index 0000000..ddf8024 --- /dev/null +++ b/test/space.jl @@ -0,0 +1,76 @@ +@testset "Space" begin + +@testset "BoxSpace" begin + @test occursin(0.5, BoxSpace(0, 1)) == true + @test occursin(0.0, BoxSpace(0, 1)) == true + @test occursin(1.0, BoxSpace(0, 1)) == true + @test occursin(-1.0, BoxSpace(0, 1)) == false + @test occursin(-Inf, BoxSpace(0, 1)) == false + @test occursin([0.5], BoxSpace(0, 1)) == true + + @test occursin([0, 0], BoxSpace([-1, -2], [1, 2])) == true + @test occursin([0, 3], BoxSpace([-1, -2], [1, 2])) == false + @test occursin([0, 0], BoxSpace([-1, -2], [1, 2])) == true +end + +@testset "DiscreteSpace" begin + @test occursin(0, DiscreteSpace(10, 0)) == true + @test occursin(5, DiscreteSpace(10, 0)) == true + @test occursin(10, DiscreteSpace(10, 0)) == false +end + +@testset "MultiBinarySpace" begin + @test occursin([true false; true false], MultiBinarySpace(2,2)) == true + @test occursin([true false], MultiBinarySpace(2,2)) == false +end + +@testset "MultiDiscreteSpace" begin + @test occursin(0, MultiDiscreteSpace([2,3,2], 0)) == true + @test occursin(1, MultiDiscreteSpace([2,3,2], 0)) == true + @test occursin(2, MultiDiscreteSpace([2,3,2], 0)) == false + @test occursin([1,1,1], MultiDiscreteSpace([2,3,2], 0)) == true + @test occursin([0,0,0], MultiDiscreteSpace([2,3,2], 0)) == true + @test occursin([3,3,3], MultiDiscreteSpace([2,3,2], 0)) == false +end + +@testset "Space Tuple" begin + @test occursin(([0.5], 5, [true true; true true], [1, 1]), + (BoxSpace(0,1), DiscreteSpace(5, 0), MultiBinarySpace(2,2), MultiDiscreteSpace([2,2], 0))) == false + @test occursin(([0.5], 0, [true true; true true], [1, 1]), + (BoxSpace(0,1), DiscreteSpace(5, 0), MultiBinarySpace(2,2), MultiDiscreteSpace([2,2], 0))) == true + @test occursin((), + (BoxSpace(0,1), DiscreteSpace(5, 0), MultiBinarySpace(2,2), MultiDiscreteSpace([2,2], 0))) == false +end + +@testset "Space Dict" begin + @test occursin( + Dict( + "sensors" => Dict( + "position" => [-10, 0, 10], + "velocity" => [0.1, 0.2, 0.3], + "front_cam" => (rand(10, 10, 3), rand(10, 10, 3)), + "rear_cam" => rand(10,10,3)), + "ext_controller" => [2, 1, 1], + "inner_state" => Dict( + "charge" => 35, + "system_checks" => rand(Bool, 10), + "job_status" => Dict( + "task" => 3, + "progress" => 23))), + Dict( + "sensors" => Dict( + "position"=> BoxSpace(-100, 100, (3,)), + "velocity"=> BoxSpace(-1, 1, (3,)), + "front_cam"=> (BoxSpace(0, 1, (10, 10, 3)), + BoxSpace(0, 1, (10, 10, 3))), + "rear_cam" => BoxSpace(0, 1, (10, 10, 3))), + "ext_controller" => MultiDiscreteSpace([5, 2, 2], 0), + "inner_state" => Dict( + "charge" => DiscreteSpace(100, 0), + "system_checks" => MultiBinarySpace(10), + "job_status" => Dict( + "task" => DiscreteSpace(5, 0), + "progress" => BoxSpace(0, 100))))) == true +end + +end \ No newline at end of file