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:

  1. mmp=true: partial matching enabled
  2. noise=true: noise added to memory activation
  3. δ=1.0: mismatch penalty parameter
  4. s=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.004598865335853
 2.9798303615851673
 2.7689520696618652
 2.4252535204986505
 3.044840848565642
 3.0687768965530324
 2.787063079261041
 3.1892010533044255
 2.99864601918356
 ⋮
 2.6404982408006146
 2.944918518797642
 3.0059698244534614
 2.8542188851643653
 2.9872973588852894
 2.3127354543097054
 2.9898142523998943
 3.0423003313083212
 3.0068536237380368

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)
Example block output