Skip to content

Commit

Permalink
Merge pull request #112 from ilabcode/dev
Browse files Browse the repository at this point in the history
Version 0.5.2
  • Loading branch information
PTWaade authored May 9, 2024
2 parents 4a487cc + bb44028 commit ce05a02
Show file tree
Hide file tree
Showing 26 changed files with 157 additions and 127 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/CI_full.yml
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ jobs:
with:
version: ${{ matrix.version }}
arch: ${{ matrix.arch }}
- uses: julia-actions/cache@v1
- uses: julia-actions/cache@v2
- uses: julia-actions/julia-buildpkg@v1
- uses: julia-actions/julia-runtest@v1
- uses: julia-actions/julia-processcoverage@v1
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/CI_small.yml
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ jobs:
with:
version: ${{ matrix.version }}
arch: ${{ matrix.arch }}
- uses: julia-actions/cache@v1
- uses: julia-actions/cache@v2
- uses: julia-actions/julia-buildpkg@v1
- uses: julia-actions/julia-runtest@v1
- uses: julia-actions/julia-processcoverage@v1
Expand Down
8 changes: 5 additions & 3 deletions Project.toml
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ authors = ["Peter Thestrup Waade ptw@cas.au.dk",
"Anna Hedvig Møller hedvig.2808@gmail.com",
"Jacopo Comoglio jacopo.comoglio@gmail.com",
"Christoph Mathys chmathys@cas.au.dk"]
version = "0.5.1"
version = "0.5.2"

[deps]
DataFrames = "a93c6f00-e57d-5684-b7b6-d8193f3e46c0"
Expand All @@ -16,9 +16,11 @@ Turing = "fce5fe82-541a-59a6-adf8-730c64b5f9a0"
RecipesBase = "3cdcf5f2-1ef4-517c-9805-6587b60abb01"

[compat]
DataFrames = "1"
DataFrames = "1.6"
Distributions = "0.25"
RecipesBase = "1"
Turing = "0.30"
Turing = "0.31"
julia = "1.9"
ReverseDiff = "1"
Distributed = "1.10"
Logging = "1.10"
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,8 @@
[![Stable](https://img.shields.io/badge/docs-stable-blue.svg)](https://ilabcode.github.io/ActionModels.jl)
[![Build Status](https://github.com/ilabcode/ActionModels.jl/actions/workflows/CI_full.yml/badge.svg?branch=main)](https://github.com/ilabcode/ActionModels.jl/actions/workflows/CI_full.yml?query=branch%3Amain)
[![Coverage](https://codecov.io/gh/ilabcode/ActionModels.jl/branch/main/graph/badge.svg?token=NVFiiPydFA)](https://codecov.io/gh/ilabcode/ActionModels.jl)
[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
[![License: GNU](https://img.shields.io/badge/License-GNU-yellow)](<https://www.gnu.org/licenses/>)
[![Aqua QA](https://raw.githubusercontent.com/JuliaTesting/Aqua.jl/master/badge.svg)](https://github.com/JuliaTesting/Aqua.jl)

## Welcome to the ActionModels.jl package!

Expand Down
1 change: 1 addition & 0 deletions docs/Project.toml
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
[deps]
ActionModels = "320cf53b-cc3b-4b34-9a10-0ecb113566a3"
Aqua = "4c88cf16-eb10-579e-8560-4a9242c79595"
CSV = "336ed68f-0bac-5ca0-87d4-7b16caf5d00b"
DataFrames = "a93c6f00-e57d-5684-b7b6-d8193f3e46c0"
Distributions = "31c24e10-a181-5473-b8eb-7969acd0382f"
Expand Down
3 changes: 1 addition & 2 deletions docs/src/Using_the_package/creating_own_action_model.jl
Original file line number Diff line number Diff line change
Expand Up @@ -155,8 +155,7 @@ end

#Set the parameters:

parameters =
Dict("learning_rate" => 1, "action_precision" => 1, ("initial", "value") => 0)
parameters = Dict("learning_rate" => 1, "action_precision" => 1, ("initial", "value") => 0)

# We set the initial state parameter for "value" state because we need a starting value in the update step.

Expand Down
12 changes: 2 additions & 10 deletions docs/src/Using_the_package/premade_agents_and_models.jl
Original file line number Diff line number Diff line change
Expand Up @@ -60,23 +60,15 @@ get_parameters(agent)

set_parameters!(
agent,
Dict(
"learning_rate" => 0.79,
"action_precision" => 0.60,
("initial", "value") => 1,
),
Dict("learning_rate" => 0.79, "action_precision" => 0.60, ("initial", "value") => 1),
)

# ## Defining an agent with custom parameter values

# If you know which parameter values you wish to use when defining your agent, you can specify them in the beginning as a dict() with parameter name as a string followed by the value.
agent_custom_parameters = premade_agent(
"premade_binary_rescorla_wagner_softmax",
Dict(
"learning_rate" => 0.7,
"action_precision" => 0.8,
("initial", "value") => 1,
),
Dict("learning_rate" => 0.7, "action_precision" => 0.8, ("initial", "value") => 1),
)

#and we can retrieve the new parameters with the get_parameters() function
Expand Down
3 changes: 1 addition & 2 deletions docs/src/Using_the_package/variations_of_util.jl
Original file line number Diff line number Diff line change
Expand Up @@ -88,8 +88,7 @@ actions = give_inputs!(agent, inputs)

# Set a prior for the parameter we wish to fit
using Distributions
priors =
Dict("action_precision" => Normal(1, 0.5), "learning_rate" => Normal(1, 0.1))
priors = Dict("action_precision" => Normal(1, 0.5), "learning_rate" => Normal(1, 0.1))

# Fit the model
fitted_model = fit_model(agent, priors, inputs, actions)
Expand Down
42 changes: 18 additions & 24 deletions docs/src/julia_src_files/creating_own_action_model.jl
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@ function continuous_rescorla_wagner_gaussian(agent::Agent, input::Real)

## Read in parameters from the agent
learning_rate = agent.parameters["learning_rate"]
action_noise = agent.parameters["action_noise"]
action_noise = agent.parameters["action_noise"]

## Read in states with an initial value
old_value = agent.states["value"]
Expand All @@ -67,32 +67,25 @@ function continuous_rescorla_wagner_gaussian(agent::Agent, input::Real)
action_distribution = Distributions.Normal(new_value, action_noise)

##Update the states and save them to agent's history
update_states!(agent, Dict(
"value" => new_value,
"input" => input,
))
update_states!(agent, Dict("value" => new_value, "input" => input))

## return the action distribution to sample actions from
return action_distribution
end


#-- define parameters and states --#
parameters = Dict(
"learning_rate" => 0.8,
"action_noise" => 1,
InitialStateParameter("value") => 0)
parameters =
Dict("learning_rate" => 0.8, "action_noise" => 1, InitialStateParameter("value") => 0)

states = Dict(
"value" => missing,
"input" => missing)
states = Dict("value" => missing, "input" => missing)

#-- create agent --#
agent = init_agent(
continuous_rescorla_wagner_gaussian,
parameters = parameters,
states = states
)
states = states,
)


#-- define observations --#
Expand Down Expand Up @@ -136,12 +129,15 @@ function binary_rescorla_wagner_softmax(agent::Agent, input::Union{Bool,Integer}
action_distribution = Distributions.Bernoulli(action_probability)

#Update states
update_states!(agent, Dict(
"value" => new_value,
"value_probability" => 1 / (1 + exp(-new_value)),
"action_probability" => action_probability,
"input" => input,
))
update_states!(
agent,
Dict(
"value" => new_value,
"value_probability" => 1 / (1 + exp(-new_value)),
"action_probability" => action_probability,
"input" => input,
),
)

return action_distribution
end
Expand All @@ -156,9 +152,7 @@ end
#Set the parameters:

parameters =
Dict("learning_rate" => 1,
"action_precision" => 1,
InitialStateParameter("value") => 0)
Dict("learning_rate" => 1, "action_precision" => 1, InitialStateParameter("value") => 0)

# We set the initial state parameter for "value" state because we need a starting value in the update step.

Expand All @@ -167,7 +161,7 @@ states = Dict(
"value" => missing,
"value_probability" => missing,
"action_probability" => missing,
"input" => missing
"input" => missing,
)

# And lastly the action model:
Expand Down
12 changes: 2 additions & 10 deletions docs/src/julia_src_files/premade_agents_and_models.jl
Original file line number Diff line number Diff line change
Expand Up @@ -60,23 +60,15 @@ get_parameters(agent)

set_parameters!(
agent,
Dict(
"learning_rate" => 0.79,
"action_precision" => 0.60,
("initial", "value") => 1,
),
Dict("learning_rate" => 0.79, "action_precision" => 0.60, ("initial", "value") => 1),
)

# ## Defining an agent with custom parameter values

# If you know which parameter values you wish to use when defining your agent, you can specify them in the beginning as a dict() with parameter name as a string followed by the value.
agent_custom_parameters = premade_agent(
"binary_rescorla_wagner_softmax",
Dict(
"learning_rate" => 0.7,
"action_precision" => 0.8,
("initial", "value") => 1,
),
Dict("learning_rate" => 0.7, "action_precision" => 0.8, ("initial", "value") => 1),
)

#and we can retrieve the new parameters with the get_parameters() function
Expand Down
3 changes: 1 addition & 2 deletions docs/src/julia_src_files/variations_of_util.jl
Original file line number Diff line number Diff line change
Expand Up @@ -88,8 +88,7 @@ actions = give_inputs!(agent, inputs)

# Set a prior for the parameter we wish to fit
using Distributions
priors =
Dict("action_precision" => Normal(1, 0.5), "learning_rate" => Normal(1, 0.1))
priors = Dict("action_precision" => Normal(1, 0.5), "learning_rate" => Normal(1, 0.1))

# Fit the model
fitted_model = fit_model(agent, priors, inputs, actions)
Expand Down
9 changes: 6 additions & 3 deletions src/ActionModels.jl
Original file line number Diff line number Diff line change
Expand Up @@ -9,14 +9,17 @@ export init_agent, premade_agent, warn_premade_defaults, multiple_actions, check
export create_agent_model, fit_model
export plot_parameter_distribution,
plot_predictive_simulation, plot_trajectory, plot_trajectory!
export get_history, get_states, get_parameters, set_parameters!, reset!, give_inputs!, single_input!
export get_history,
get_states, get_parameters, set_parameters!, reset!, give_inputs!, single_input!
export get_posteriors, update_states!, set_save_history!
export InitialStateParameter, ParameterGroup

#Load premade agents
function __init__()
premade_agents["binary_rescorla_wagner_softmax"] = premade_binary_rescorla_wagner_softmax
premade_agents["continuous_rescorla_wagner_gaussian"] = premade_continuous_rescorla_wagner_gaussian
premade_agents["binary_rescorla_wagner_softmax"] =
premade_binary_rescorla_wagner_softmax
premade_agents["continuous_rescorla_wagner_gaussian"] =
premade_continuous_rescorla_wagner_gaussian
end

#Types for agents and errors
Expand Down
6 changes: 4 additions & 2 deletions src/create_agent/init_agent.jl
Original file line number Diff line number Diff line change
Expand Up @@ -83,7 +83,9 @@ function init_agent(
action_model::Function;
substruct::Any = nothing,
parameters::Dict = Dict(),
parameter_groups::Union{ParameterGroup, Vector{ParameterGroup}} = Vector{ParameterGroup}(),
parameter_groups::Union{ParameterGroup,Vector{ParameterGroup}} = Vector{
ParameterGroup,
}(),
states::Union{Dict,Vector} = Dict(),
settings::Dict = Dict(),
save_history::Bool = true,
Expand Down Expand Up @@ -159,7 +161,7 @@ function init_agent(
value = parameter_group.value,
grouped_parameters = parameter_group.parameters,
)

#Set the parameters
set_parameters!(agent, parameter_group, parameter_group.value)

Expand Down
22 changes: 18 additions & 4 deletions src/fitting/create_model.jl
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ Create a Turing model object used for fitting an ActionModels agent.
multiple_actions::Bool,
impute_missing_actions::Bool,
)

#Initialize dictionaries for storing sampled parameters
multilevel_parameters = Dict()
agent_parameters = Dict()
Expand Down Expand Up @@ -155,17 +156,25 @@ Create a Turing model object used for fitting an ActionModels agent.

#Sample the action from the probability distribution
actions[group][timestep] ~ action_distribution


#Save the action to the agent in case it needs it in the future
agent.states["action"] = actions[group][timestep]


#If there are multiple actions
else

#Initialize empty vector for storing actions
actions = []

#Go through each separate action
for (action_idx, single_distribution) in enumerate(action_distribution)

# If missing actions are to be imputed, we do not care if the action exists
if !impute_missing_actions
# If we should not impute missing actions, we need to check if the action exists
@inbounds action_exists = !ismissing(actions[group][timestep, action_idx])
@inbounds action_exists =
!ismissing(actions[group][timestep, action_idx])
# if the action doesn't exist, we skip this timestep
if !action_exists
continue
Expand All @@ -174,8 +183,13 @@ Create a Turing model object used for fitting an ActionModels agent.

#Sample the action from the probability distribution
@inbounds actions[group][timestep, action_idx] ~ single_distribution


#Save the action
push!(actions, actions[group][timestep, action_idx])
end

#Save the action to the agent, for models that need previous action
agent.states["action"] = actions
end
end
end
Expand All @@ -191,4 +205,4 @@ Create a Turing model object used for fitting an ActionModels agent.
throw(e)
end
end
end
end
33 changes: 28 additions & 5 deletions src/fitting/fit_model.jl
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,11 @@ function fit_model(
input_cols::Vector = [:input],
action_cols::Vector = [:action],
fixed_parameters::Dict = Dict(),
sampler::Turing.Inference.InferenceAlgorithm = NUTS(-1, 0.65; adtype=AutoReverseDiff(true)),
sampler::Turing.Inference.InferenceAlgorithm = NUTS(
-1,
0.65;
adtype = AutoReverseDiff(true),
),
n_cores::Integer = 1,
n_iterations::Integer = 1000,
n_chains::Integer = 2,
Expand All @@ -56,7 +60,7 @@ function fit_model(
sampler_kwargs...,
)
### SETUP ###

#Convert column names to symbols
independent_group_cols = Symbol.(independent_group_cols)
multilevel_group_cols = Symbol.(multilevel_group_cols)
Expand Down Expand Up @@ -205,6 +209,13 @@ function fit_model(

#Load ActionModels, Turing and sister packages on worker processes
@everywhere @eval using ActionModels, Turing
if @isdefined HierarchicalGaussianFiltering
@everywhere @eval using HierarchicalGaussianFiltering
end
if @isdefined ActiveInference
@everywhere @eval using ActiveInference
end

#Broadcast necessary information to workers
@everywhere agent = $agent
@everywhere fit_info_all = $fit_info_all
Expand Down Expand Up @@ -323,7 +334,11 @@ function fit_model(
inputs::Array,
actions::Array;
fixed_parameters::Dict = Dict(),
sampler::Turing.Inference.InferenceAlgorithm = NUTS(-1, 0.65; adtype=AutoReverseDiff(true)),
sampler::Turing.Inference.InferenceAlgorithm = NUTS(
-1,
0.65;
adtype = AutoReverseDiff(true),
),
n_cores::Integer = 1,
n_iterations::Integer = 1000,
n_chains = 2,
Expand All @@ -334,8 +349,16 @@ function fit_model(
)

#Create column names
input_cols = map(x -> "input$x", 1:size(inputs, 2))
action_cols = map(x -> "action$x", 1:size(actions, 2))
input_cols = map(x -> "input$x", 1:length(first(inputs)))
action_cols = map(x -> "action$x", 1:length(first(actions)))

#Make vectors of vectors into arrays
if length(input_cols) > 1
inputs = mapreduce(permutedims, vcat, inputs)
end
if length(action_cols) > 1
actions = mapreduce(permutedims, vcat, actions)
end

#Create dataframe of the inputs and actions
data = DataFrame(hcat(inputs, actions), vcat(input_cols, action_cols))
Expand Down
Loading

0 comments on commit ce05a02

Please sign in to comment.