Skip to content

Commit

Permalink
Merge pull request #52 from biaslab/48-keyword-arguments-are-inconsis…
Browse files Browse the repository at this point in the history
…tent-throughout-the-package

Synchronized creation keywords and update docstrings
  • Loading branch information
wouterwln authored Jan 17, 2024
2 parents 26009da + 76d3d39 commit 7c0baab
Show file tree
Hide file tree
Showing 3 changed files with 79 additions and 40 deletions.
16 changes: 12 additions & 4 deletions src/abstractentity.jl
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,7 @@ function subscribe_to_observations!(entity::AbstractEntity, actor)
subscribe!(observations(entity), actor)
return actor
end

"""
Unsubscribes `receiver` from `emitter`. Any data sent from `emitter` to `receiver` will not be received by `receiver` after this function is called.
"""
Expand Down Expand Up @@ -101,11 +102,11 @@ The Markov Blankets for both entities will be subscribed to each other.
# Arguments
- `first::AbstractEntity{T,S,E}`: The entity to which `second` will be added.
- `second`: The entity to be added to `first`.
- `passive=false`: A boolean indicating whether `second` should be instantiated as an active entity.
- `is_active=false`: A boolean indicating whether `second` should be instantiated as an active entity.
"""
function add!(first::AbstractEntity{T,S,E}, second; active = false) where {T,S,E}
active_or_passive = active ? ActiveEntity() : PassiveEntity()
entity = create_entity(second, state_space(first), active_or_passive)
function add!(first::AbstractEntity{T,S,E}, second; is_active = false) where {T,S,E}
operation_type = is_active ? ActiveEntity() : PassiveEntity()
entity = create_entity(second, state_space(first), operation_type)
add!(first, entity)
return entity
end
Expand Down Expand Up @@ -162,6 +163,13 @@ function terminate!(entity::AbstractEntity)
terminate!(properties(entity))
end

"""
time_interval(::T)
Returns the default time interval for an entity of type `T`. By default, this function returns `1`. This is used
in discrete active entities to determine the elapsed time between state updates. Through dispatching, we can
define different time intervals for different entity types.
"""
time_interval(any) = 1

update!(any, elapsed_time) =
Expand Down
39 changes: 35 additions & 4 deletions src/rxentity.jl
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,16 @@ end

entity(actor::EntityActor) = actor.entity

"""
Rocket.on_next!(actor::EntityActor{ActiveEntity}, observation)})
Handles the logic for an incoming observation for an active entity. This means that the entity will update its state, incorporate the observation
into its state, and then send an action to all of its subscribers. The action is determined by the `what_to_send` function. Emissions can be filtered
by implementing the `emits` function.
This function is automatically called whenever the entity receives an observation on it's sensor. The `observation` will contain the data sent by the
emitter as well as a reference to the emitter itself.
"""
function Rocket.on_next!(actor::EntityActor{T,S,ActiveEntity} where {T,S}, observation)
subject = entity(actor)
update!(subject)
Expand All @@ -20,6 +30,11 @@ function Rocket.on_next!(actor::EntityActor{T,S,ActiveEntity} where {T,S}, obser
end
end

"""
Rocket.on_next!(actor::EntityActor{PassiveEntity}, observation)
Handles the logic for an incoming observation for a passive entity. This means that we will only incorporate the observation into the entity's state.
"""
function Rocket.on_next!(actor::EntityActor{T,S,PassiveEntity} where {T,S}, observation)
receive!(entity(actor), observation)
end
Expand Down Expand Up @@ -65,21 +80,37 @@ function terminate!(properties::EntityProperties)
terminate!(properties.entity_actor)
end

"""
RxEntity
The RxEntity is the vanilla implementation of an `AbstractEntity` that is used in most cases. It is a wrapper around an entity that adds the following functionality:
- A `MarkovBlanket` that contains the actuators and sensors of the entity
- A `EntityProperties` that contains the state space, whether or not the entity is active, and the real time factor
- A `EntityActor` that handles the logic for receiving observations and sending actions
"""
struct RxEntity{T,S,E} <: AbstractEntity{T,S,E}
decorated::T
markov_blanket::MarkovBlanket
properties::EntityProperties{S,E}
end

"""
create_entity(entity; is_discrete = false, is_active = false, real_time_factor = 1)
Creates an `RxEntity` that decorates a given entity. The `is_discrete` and `is_active` parameters determine whether or not the entity lives in a discrete or continuous state-space,
and whether or not the entity is active or passive. The `real_time_factor` parameter determines how fast the entity's clock ticks. This function can be used as a lower-level API
in order to create a more complex network of entities.
"""
function create_entity(
entity;
discrete::Bool = false,
is_discrete::Bool = false,
is_active::Bool = false,
real_time_factor = 1,
)
state_space = discrete ? DiscreteEntity() : ContinuousEntity()
active_or_passive = is_active ? ActiveEntity() : PassiveEntity()
return create_entity(entity, state_space, active_or_passive, real_time_factor)
state_space = is_discrete ? DiscreteEntity() : ContinuousEntity()
operation_type = is_active ? ActiveEntity() : PassiveEntity()
return create_entity(entity, state_space, operation_type, real_time_factor)
end

function create_entity(entity, state_space, active_or_passive, real_time_factor::Real = 1)
Expand Down
64 changes: 32 additions & 32 deletions test/entity_tests.jl
Original file line number Diff line number Diff line change
Expand Up @@ -325,20 +325,20 @@ end

include("mockenvironment.jl")
@testset "constructor" begin
e = create_entity(MockEntity(); discrete = true)
e = create_entity(MockEntity(); is_discrete = true)
@test e isa RxEnvironments.RxEntity{MockEntity}
end

@testset "markov blanket functionality" begin
let e = create_entity(MockEntity(); discrete = true)
let e = create_entity(MockEntity(); is_discrete = true)
@test e.markov_blanket isa RxEnvironments.MarkovBlanket
end
end

@testset "subscribe_to_observations!" begin
import RxEnvironments: observations, data
let first_entity = create_entity(MockEntity(); discrete = true)
let second_entity = create_entity(MockEntity(); discrete = true)
let first_entity = create_entity(MockEntity(); is_discrete = true)
let second_entity = create_entity(MockEntity(); is_discrete = true)
add!(first_entity, second_entity)
obs = keep(Any)
subscribe_to_observations!(first_entity, obs)
Expand Down Expand Up @@ -370,7 +370,7 @@ end

@testset "clock and time keeping" begin
import RxEnvironments: clock, add_elapsed_time!
let e = create_entity(MockEntity(); discrete = true)
let e = create_entity(MockEntity(); is_discrete = true)
obs = keep(Any)
subscribe_to_observations!(e, obs)

Expand All @@ -389,18 +389,18 @@ end
@testset "add subscriber" begin
# Test default case of two interacting entities

let first_entity = create_entity(MockEntity(); discrete = true)
let second_entity = create_entity(MockEntity(); discrete = true)
let first_entity = create_entity(MockEntity(); is_discrete = true)
let second_entity = create_entity(MockEntity(); is_discrete = true)
subscribe!(first_entity, second_entity)
@test !is_subscribed(first_entity, second_entity)
@test is_subscribed(second_entity, first_entity)
end
end

# Test case of three interacting entities
let first_entity = create_entity(MockEntity(); discrete = true)
let second_entity = create_entity(MockEntity(); discrete = true)
let third_entity = create_entity(MockEntity(); discrete = true)
let first_entity = create_entity(MockEntity(); is_discrete = true)
let second_entity = create_entity(MockEntity(); is_discrete = true)
let third_entity = create_entity(MockEntity(); is_discrete = true)
subscribe!(first_entity, second_entity)
subscribe!(second_entity, third_entity)
@test !is_subscribed(first_entity, second_entity)
Expand All @@ -412,7 +412,7 @@ end
end

# Test self-subscription
let first_entity = create_entity(MockEntity(); discrete = true)
let first_entity = create_entity(MockEntity(); is_discrete = true)
@test_throws RxEnvironments.SelfSubscriptionException subscribe!(
first_entity,
first_entity,
Expand All @@ -421,8 +421,8 @@ end
end

@testset "mutual subscribe" begin
let first_entity = create_entity(MockEntity(); discrete = true)
let second_entity = create_entity(MockEntity(); discrete = true)
let first_entity = create_entity(MockEntity(); is_discrete = true)
let second_entity = create_entity(MockEntity(); is_discrete = true)
add!(first_entity, second_entity)
@test is_subscribed(first_entity, second_entity)
@test is_subscribed(second_entity, first_entity)
Expand All @@ -431,8 +431,8 @@ end
end

@testset "unsubscribe" begin
let first_entity = create_entity(MockEntity(); discrete = true)
let second_entity = create_entity(MockEntity(); discrete = true)
let first_entity = create_entity(MockEntity(); is_discrete = true)
let second_entity = create_entity(MockEntity(); is_discrete = true)
first_obs = keep(Any)
second_obs = keep(Any)
subscribe_to_observations!(first_entity, first_obs)
Expand All @@ -457,8 +457,8 @@ end
import RxEnvironments: data

# Test simple case of two interacting entities
let first_entity = create_entity(MockEntity(); discrete = true)
let second_entity = create_entity(MockEntity(); discrete = true)
let first_entity = create_entity(MockEntity(); is_discrete = true)
let second_entity = create_entity(MockEntity(); is_discrete = true)
first_obs = keep(Any)
second_obs = keep(Any)
subscribe_to_observations!(first_entity, first_obs)
Expand Down Expand Up @@ -491,9 +491,9 @@ end
# Test case of three interacting entities
# Subscriptions:
# first_entity <-> second_entity <-> third_entity
let first_entity = create_entity(MockEntity(); discrete = true)
let second_entity = create_entity(MockEntity(); discrete = true)
let third_entity = create_entity(MockEntity(); discrete = true)
let first_entity = create_entity(MockEntity(); is_discrete = true)
let second_entity = create_entity(MockEntity(); is_discrete = true)
let third_entity = create_entity(MockEntity(); is_discrete = true)
first_obs = keep(Any)
second_obs = keep(Any)
third_obs = keep(Any)
Expand Down Expand Up @@ -534,7 +534,7 @@ end
end

@testset "send message" begin
let first_entity = create_entity(MockEntity(); discrete = true)
let first_entity = create_entity(MockEntity(); is_discrete = true)
obs = keep(Any)
subscribe!(first_entity, obs)
send!(obs, first_entity, 1)
Expand All @@ -548,8 +548,8 @@ end
import RxEnvironments: emits, decorated

# Test that by default we always emit
let first_entity = create_entity(MockEntity(); discrete = true)
let second_entity = create_entity(MockEntity(); discrete = true)
let first_entity = create_entity(MockEntity(); is_discrete = true)
let second_entity = create_entity(MockEntity(); is_discrete = true)
add!(first_entity, second_entity)
@test emits(decorated(first_entity), decorated(second_entity), nothing) ==
true
Expand All @@ -562,9 +562,9 @@ end
let first_entity = create_entity(
SelectiveSendingEntity();
is_active = true,
discrete = true,
is_discrete = true,
)
let second_entity = create_entity(SelectiveReceivingEntity(); discrete = true)
let second_entity = create_entity(SelectiveReceivingEntity(); is_discrete = true)
# Assert that we only block emission if the incoming message is `Nothing`
@test emits(decorated(first_entity), decorated(second_entity), nothing) ==
false
Expand All @@ -589,8 +589,8 @@ end
@testset "terminate!" begin
import RxEnvironments: terminate!, is_terminated

let first_entity = create_entity(MockEntity(); discrete = true)
let second_entity = create_entity(MockEntity(); discrete = true)
let first_entity = create_entity(MockEntity(); is_discrete = true)
let second_entity = create_entity(MockEntity(); is_discrete = true)
add!(first_entity, second_entity)

@test is_subscribed(first_entity, second_entity)
Expand All @@ -609,7 +609,7 @@ end
@testset "impossible to add timer" begin
import RxEnvironments: add_timer!

let e = create_entity(MockEntity(); discrete = true)
let e = create_entity(MockEntity(); is_discrete = true)
@test_throws MethodError add_timer!(e, 1000)
end
end
Expand All @@ -621,9 +621,9 @@ end
let env = create_entity(
SelectiveSendingEntity();
is_active = true,
discrete = true,
is_discrete = true,
)
agent = create_entity(SelectiveReceivingEntity(); discrete = true)
agent = create_entity(SelectiveReceivingEntity(); is_discrete = true)
add!(env, agent)

obs = keep(Any)
Expand All @@ -644,7 +644,7 @@ end

@testset "mix state-spaces" begin
# Test that mixing of state spaces is not allowed
let first_entity = create_entity(MockEntity(); discrete = true)
let first_entity = create_entity(MockEntity(); is_discrete = true)
let second_entity = create_entity(MockEntity())
@test_throws RxEnvironments.MixedStateSpaceException add!(
first_entity,
Expand All @@ -670,7 +670,7 @@ end
@test state_space(result) === RxEnvironments.ContinuousEntity()
end

let e = create_entity(MockEntity(); discrete = true)
let e = create_entity(MockEntity(); is_discrete = true)
result = add!(e, MockEntity())
@test state_space(result) === RxEnvironments.DiscreteEntity()
end
Expand Down

0 comments on commit 7c0baab

Please sign in to comment.