Example 3
In this example, we will demonstrate how to use ACT-R's blending mechanism in the context of a decision making. In the decision making task, a person repeatedly chooses between three gambles. The model assumes a person encodes a memory specifying the option and the experienced outcome.
Load Packages
First, we will load the required packages.
using ACTRModels
using Random
using Plots
Random.seed!(87545)
Random.TaskLocalRNG()
Define Parameters
Next, we will define the following parameters:
mmp=true
: partial matching enablednoise=true
: noise added to memory activationδ=1.0
: mismatch penalty parameters=0.20
: logistic scalar for memory activation noise
Θ = (mmp=true, noise=true, δ=1.0, s=0.20)
(mmp = true, noise = true, δ = 1.0, s = 0.2)
Populate Declarative Memory
The code below defines six chunks, two chunks for each option. The option
slot indexes the option and the value
slot stores the experienced outcome. The parameter bl
is the base-level constant for a given chunk. We assume that the chunks have different activation for the purpose of illustration.
# create chunks of declarative knowledge
chunks = [Chunk(;option=1, value = 4.0, bl=1.6),
Chunk(;option=1, value = 2.0, bl=1.5),
Chunk(;option=2, value = 3.5, bl=1.2),
Chunk(;option=2, value = 4.0, bl=1.6),
Chunk(;option=3, value = 2.0, bl=1.0),
Chunk(;option=3, value = 3.0, bl=1.75)]
# initialize declarative memory
declarative = Declarative(memory=chunks)
Declarative{Chunk{NamedTuple{(:option, :value), Tuple{Int64, Float64}}, Float64}, Tuple{Symbol, Symbol}, BufferState}(Chunk{NamedTuple{(:option, :value), Tuple{Int64, Float64}}, Float64}[Chunk{NamedTuple{(:option, :value), Tuple{Int64, Float64}}, Float64}(1, 1.0, 0.0, 1, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, (option = 1, value = 4.0), 0, [0.0], Float64[], 1.6), Chunk{NamedTuple{(:option, :value), Tuple{Int64, Float64}}, Float64}(1, 1.0, 0.0, 1, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, (option = 1, value = 2.0), 0, [0.0], Float64[], 1.5), Chunk{NamedTuple{(:option, :value), Tuple{Int64, Float64}}, Float64}(1, 1.0, 0.0, 1, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, (option = 2, value = 3.5), 0, [0.0], Float64[], 1.2), Chunk{NamedTuple{(:option, :value), Tuple{Int64, Float64}}, Float64}(1, 1.0, 0.0, 1, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, (option = 2, value = 4.0), 0, [0.0], Float64[], 1.6), Chunk{NamedTuple{(:option, :value), Tuple{Int64, Float64}}, Float64}(1, 1.0, 0.0, 1, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, (option = 3, value = 2.0), 0, [0.0], Float64[], 1.0), Chunk{NamedTuple{(:option, :value), Tuple{Int64, Float64}}, Float64}(1, 1.0, 0.0, 1, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, (option = 3, value = 3.0), 0, [0.0], Float64[], 1.75)], (:isa, :retrieved), Chunk{NamedTuple{(:option, :value), Tuple{Int64, Float64}}, Float64}[#undef], BufferState(false, false, true))
Define ACT-R Model
In the code below, we will create an ACT-R model object by passing the delcarative memory object and parameters to the ACTR
constructor.
# create an ACT-R object with activation noise and partial matching
actr = ACTR(;declarative, Θ...)
ACTR{String, Declarative{Chunk{NamedTuple{(:option, :value), Tuple{Int64, Float64}}, Float64}, Tuple{Symbol, Symbol}, BufferState}, Imaginal{Chunk, Float64, BufferState}, Nothing, Nothing, Goal{Chunk, BufferState}, Nothing, Nothing, Dict{String, VisualObject}, Parms{Float64, typeof(ACTRModels.default_dissim_func), typeof(ACTRModels.spreading_activation!), typeof(ACTRModels.utility_match), NamedTuple{(), Tuple{}}}, ACTRModels.Scheduler, Random.TaskLocalRNG}("model1", Declarative{Chunk{NamedTuple{(:option, :value), Tuple{Int64, Float64}}, Float64}, Tuple{Symbol, Symbol}, BufferState}(Chunk{NamedTuple{(:option, :value), Tuple{Int64, Float64}}, Float64}[Chunk{NamedTuple{(:option, :value), Tuple{Int64, Float64}}, Float64}(1, 1.0, 0.0, 1, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, (option = 1, value = 4.0), 0, [0.0], Float64[], 1.6), Chunk{NamedTuple{(:option, :value), Tuple{Int64, Float64}}, Float64}(1, 1.0, 0.0, 1, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, (option = 1, value = 2.0), 0, [0.0], Float64[], 1.5), Chunk{NamedTuple{(:option, :value), Tuple{Int64, Float64}}, Float64}(1, 1.0, 0.0, 1, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, (option = 2, value = 3.5), 0, [0.0], Float64[], 1.2), Chunk{NamedTuple{(:option, :value), Tuple{Int64, Float64}}, Float64}(1, 1.0, 0.0, 1, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, (option = 2, value = 4.0), 0, [0.0], Float64[], 1.6), Chunk{NamedTuple{(:option, :value), Tuple{Int64, Float64}}, Float64}(1, 1.0, 0.0, 1, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, (option = 3, value = 2.0), 0, [0.0], Float64[], 1.0), Chunk{NamedTuple{(:option, :value), Tuple{Int64, Float64}}, Float64}(1, 1.0, 0.0, 1, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, (option = 3, value = 3.0), 0, [0.0], Float64[], 1.75)], (:isa, :retrieved), Chunk{NamedTuple{(:option, :value), Tuple{Int64, Float64}}, Float64}[#undef], BufferState(false, false, true)), Imaginal{Chunk, Float64, BufferState}(Chunk[], BufferState(false, false, true), 1.0, Int64[]), nothing, nothing, Goal{Chunk, BufferState}(Chunk[], BufferState(false, false, true)), nothing, nothing, Dict{String, VisualObject}(), Parms{Float64, typeof(ACTRModels.default_dissim_func), typeof(ACTRModels.spreading_activation!), typeof(ACTRModels.utility_match), NamedTuple{(), Tuple{}}}(0.5, 0.0, 0.2, 0.0, 1.0, 1.0, 0.0, 0.0, ACTRModels.default_dissim_func, ACTRModels.spreading_activation!, ACTRModels.utility_match, 1.0, 0.0, 0.0, 0.2, 1.0, -100.0, -100.0, 1.0, 1.0, 1.0, 1.0, false, true, false, true, false, false, 0.28284271247461906, NamedTuple()), ACTRModels.Scheduler(0.0), Random.TaskLocalRNG())
Run Simulation
The code below simulates the model $10,000$ times for each option and stores the blended values.
n_sim = 10_000
blended_slots = :value
request = (option=1,)
blended_values1 = map(_ -> blend_chunks(actr, blended_slots; request...), 1:n_sim)
request = (option=2,)
blended_values2 = map(_ -> blend_chunks(actr, blended_slots; request...), 1:n_sim)
request = (option=3,)
blended_values3 = map(_ -> blend_chunks(actr, blended_slots; request...), 1:n_sim)
10000-element Vector{Float64}:
2.6793117665380883
3.0045988653358524
2.9798303615851673
2.7689520696618652
2.4252535204986505
3.044840848565642
3.0687768965530324
2.787063079261041
3.1892010533044255
2.99864601918356
⋮
2.640498240800614
2.9449185187976417
3.0059698244534614
2.8542188851643653
2.9872973588852894
2.3127354543097054
2.989814252399894
3.0423003313083212
3.0068536237380363
Plot Results
The distribution of blended values for each option is plotted below.
histogram(blended_values1, norm=true, xlabel="Blended Values", ylabel="Density", label="Option 1", alpha=.75)
histogram!(blended_values2, norm=true, xlabel="Blended Values", ylabel="Density", label="Option 2", alpha=.75)
histogram!(blended_values3, norm=true, xlabel="Blended Values", ylabel="Density", label="Option 3", alpha=.75)