Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
42 commits
Select commit Hold shift + click to select a range
a419d90
Update gitignore
Apr 20, 2025
920cd88
item_response_function => item_response_functions
Apr 20, 2025
c3f4eb8
Add sequential next item strategies
May 29, 2025
f7d3b74
Adjust test_stateful_cat_item_bank_1d_dich_ib to test whole vec
May 29, 2025
e04a19f
Update compats
May 29, 2025
dac0c6b
Add watchdog, likelihood sampling, callback skipping to comparison
May 29, 2025
4526bba
Add compat layer
May 29, 2025
b8046ec
Add likelihood to Stateful
May 29, 2025
19c0db0
Add SafeLikelihoodEstimator
May 29, 2025
1a6b7d2
Various changes
May 29, 2025
cc964ef
Fix up comparison module directory
Jun 1, 2025
9055986
Remove dependency upon ResumableFunctions
Jun 8, 2025
8992a35
Fix some typos in MirtCAT compat
Jun 8, 2025
f3d9a6b
Add multidim to MirtCAT compat
Jun 8, 2025
b8675b9
Add RandomesqueStrategy
Jun 8, 2025
8b035bf
Make mirtcat compat tests be based on normal item bank
Jun 8, 2025
d82fb65
Reduce default margin in test_stateful_cat_item_bank_1d_dich_ib
Jun 8, 2025
86a3951
Add test_ability to TestExt
Jun 8, 2025
e48dd80
Move sim to own directory
Jun 8, 2025
8aab64f
Fix multidim expected_item_information
Jun 8, 2025
98c3b06
Use expected_item_information for previous items in multidim case (mi…
Jun 8, 2025
18d0488
Fix dispatch for criteria
Jun 8, 2025
869d699
Fix up and modularise tests
Jun 8, 2025
55525c8
Bump PsychometricsBazaarBase req to 0.8.4
Jun 8, 2025
3bed638
Fixes to randomesque
Jun 17, 2025
6dd9ce9
Add GreedyForcedContentBalancer and PointwiseNextItemRule
Jun 21, 2025
d9151d9
Mark some stuff as scalars for broadcasting
Jun 21, 2025
cf02e11
Additions and refactor
Jun 21, 2025
4f7d7db
Rename PiecewiseNextItemRule => FixedRuleSequencer
Jun 21, 2025
78aab07
Add/improve show(...) methods
Jun 21, 2025
42f4f34
Remove trailing space
Jun 21, 2025
ca8b99a
Add some docs to MemoryNextItemRule
Jun 21, 2025
a5fb03d
Rename PriorAbilityEstimator => PosteriorAbilityEstimator
Jun 21, 2025
7158dd6
Fix up import in Compat.MirtCAT
Jul 1, 2025
be8bcbf
Improve show methods
Jul 1, 2025
c21b14c
Improve CatRecorder
Jul 1, 2025
7a62509
More succinct naming
Jul 4, 2025
9231d4b
SimpleFunctionTerminationCondition => TerminationTest
Jul 4, 2025
5165509
More consistent path/module naming
Jul 4, 2025
921df96
Remove PushVectors
Jul 4, 2025
1382bde
Version bump => 0.4.0
Jul 4, 2025
3237b05
Fixup benchmark script
Jul 4, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 1 addition & 4 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,9 @@
*.jl.cov
*.jl.mem
Manifest.toml
!/docs/Manifest.toml
!/test/Manifest.toml
!/binder/Manifest.toml
/attic/
attic/
/.vscode/
/docs/attic/
/docs/build/
/docs/.CondaPkg/
/docs/LocalPreferences.toml
Expand Down
14 changes: 8 additions & 6 deletions Project.toml
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
name = "ComputerAdaptiveTesting"
uuid = "5a0d4f34-1f62-4a66-80fe-87aba0485488"
authors = ["Frankie Robertson"]
version = "0.3.2"
version = "0.4.0"

[deps]
Accessors = "7d9f7c33-5ae7-4f3b-8dc6-eff91059b697"
Expand All @@ -11,6 +11,7 @@ DataFrames = "a93c6f00-e57d-5684-b7b6-d8193f3e46c0"
Distributions = "31c24e10-a181-5473-b8eb-7969acd0382f"
DocStringExtensions = "ffbed154-4ef7-542d-bbb7-c09d3a79fcae"
EffectSizes = "e248de7e-9197-5860-972e-353a2af44d75"
ElasticArrays = "fdbdab4c-e67f-52f5-8c3f-e7b388dad3d4"
FillArrays = "1a297f60-69ca-5386-bcde-b61e274b549b"
FittedItemBanks = "3f797b09-34e4-41d7-acf6-3302ae3248a5"
ForwardDiff = "f6369f11-7733-5829-9624-2563aa707210"
Expand All @@ -23,6 +24,7 @@ MacroTools = "1914dd2f-81c6-5fcd-8719-6d5c9610ff09"
Mmap = "a63ad114-7e13-5084-954f-fe012c677804"
PrecompileTools = "aea7be01-6a6a-4083-8856-8a6e6704d82a"
PsychometricsBazaarBase = "b0d9cada-d963-45e9-a4c6-4746243987f1"
QuickHeaps = "30b38841-0f52-47f8-a5f8-18d5d4064379"
Random = "9a3f8284-a2c9-5f02-9a11-845980a1fd5c"
Reexport = "189a3867-3050-52da-a836-e630ba90ab69"
Setfield = "efcf1570-3423-57d1-acb7-fd33fddbac46"
Expand All @@ -46,8 +48,9 @@ DataFrames = "1.6.1"
Distributions = "^0.25.88"
DocStringExtensions = " ^0.9"
EffectSizes = "^1.0.1"
ElasticArrays = "1.2.12"
FillArrays = "0.13, 1.5.0"
FittedItemBanks = "^0.6.3"
FittedItemBanks = "^0.7.2"
ForwardDiff = "1"
HypothesisTests = "^0.10.12, ^0.11.0"
Interpolations = "^0.14, ^0.15"
Expand All @@ -59,10 +62,10 @@ MacroTools = "^0.5.6"
Mmap = "^1.11"
Optim = "1.7.3"
PrecompileTools = "1.2.1"
PsychometricsBazaarBase = "^0.8.1"
PsychometricsBazaarBase = "^0.8.4"
QuickHeaps = "0.2.2"
Random = "^1.11"
Reexport = "1"
ResumableFunctions = "^0.6"
Setfield = "^1"
SparseArrays = "^1.11"
StaticArrays = "1"
Expand All @@ -75,8 +78,7 @@ julia = "^1.11"
Aqua = "4c88cf16-eb10-579e-8560-4a9242c79595"
JET = "c3a54625-cd67-489e-a8e7-0a5a0ff4e31b"
Optim = "429524aa-4258-5aef-a3af-852621145aeb"
ResumableFunctions = "c5292f4c-5179-55e1-98c5-05642aab7184"
Test = "8dfed614-e22c-5e08-85e1-65c5234f0b40"

[targets]
test = ["Aqua", "JET", "Optim", "ResumableFunctions", "Test"]
test = ["Aqua", "JET", "Optim", "Test"]
9 changes: 4 additions & 5 deletions benchmark/benchmarks.jl
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,6 @@ using FittedItemBanks.DummyData: dummy_full, SimpleItemBankSpec, StdModel4PL
using ComputerAdaptiveTesting.Aggregators
using PsychometricsBazaarBase.Optimizers
using PsychometricsBazaarBase.Integrators: even_grid
using ComputerAdaptiveTesting.NextItemRules: mirtcat_quadpts
using ComputerAdaptiveTesting.NextItemRules: ExpectationBasedItemCriterion,
PointResponseExpectation
using ComputerAdaptiveTesting.NextItemRules
Expand All @@ -27,21 +26,21 @@ function prepare_4pls(group)
num_questions = 20,
num_testees = 1
)
integrator = even_grid(-6.0, 6.0, mirtcat_quadpts(1))
integrator = even_grid(-6.0, 6.0, 121)
optimizer = AbilityOptimizer(OneDimOptimOptimizer(-6.0, 6.0, NelderMead()))

dist_ability_estimator = PriorAbilityEstimator()
dist_ability_estimator = PosteriorAbilityEstimator()
ability_estimators = [
("mean", MeanAbilityEstimator(dist_ability_estimator, integrator)),
("mode", ModeAbilityEstimator(dist_ability_estimator, optimizer))
]
response_idxs = sample(rng, 1:20, 10)

for (est_nick, ability_estimator) in ability_estimators
next_item_rule = ItemStrategyNextItemRule(
next_item_rule = ItemCriterionRule(
ExhaustiveSearch(),
ExpectationBasedItemCriterion(PointResponseExpectation(ability_estimator),
AbilityVarianceStateCriterion(
AbilityVariance(
integrator, distribution_estimator(ability_estimator)))
)
next_item_rule = preallocate(next_item_rule)
Expand Down
14 changes: 7 additions & 7 deletions docs/examples/examples/ability_convergence_3pl.jl
Original file line number Diff line number Diff line change
Expand Up @@ -20,9 +20,9 @@ using Distributions: Normal, cdf
using AlgebraOfGraphics
using ComputerAdaptiveTesting
using ComputerAdaptiveTesting.Sim: auto_responder
using ComputerAdaptiveTesting.NextItemRules: AbilityVarianceStateCriterion
using ComputerAdaptiveTesting.TerminationConditions: FixedItemsTerminationCondition
using ComputerAdaptiveTesting.Aggregators: PriorAbilityEstimator,
using ComputerAdaptiveTesting.NextItemRules: AbilityVariance
using ComputerAdaptiveTesting.TerminationConditions: FixedLength
using ComputerAdaptiveTesting.Aggregators: PosteriorAbilityEstimator,
MeanAbilityEstimator, LikelihoodAbilityEstimator
using FittedItemBanks
using ComputerAdaptiveTesting.Responses: BooleanResponse
Expand All @@ -46,18 +46,18 @@ using FittedItemBanks.DummyData: dummy_full, std_normal, SimpleItemBankSpec, Std
# CatRecorder collects information which can be used to draw different types of plots.
max_questions = 99
integrator = FixedGKIntegrator(-6, 6, 80)
dist_ability_est = PriorAbilityEstimator(std_normal)
dist_ability_est = PosteriorAbilityEstimator(std_normal)
ability_estimator = MeanAbilityEstimator(dist_ability_est, integrator)
rules = CatRules(ability_estimator,
AbilityVarianceStateCriterion(dist_ability_est, integrator),
FixedItemsTerminationCondition(max_questions))
AbilityVariance(dist_ability_est, integrator),
FixedLength(max_questions))

points = 500
xs = range(-2.5, 2.5, length = points)
raw_estimator = LikelihoodAbilityEstimator()
recorder = CatRecorder(xs, responses, integrator, raw_estimator, ability_estimator)
for testee_idx in axes(responses, 2)
tracked_responses, θ = run_cat(CatLoopConfig(rules = rules,
tracked_responses, θ = run_cat(CatLoop(rules = rules,
get_response = auto_responder(@view responses[:, testee_idx]),
new_response_callback = (tracked_responses, terminating) -> recorder(tracked_responses,
testee_idx,
Expand Down
10 changes: 5 additions & 5 deletions docs/examples/examples/ability_convergence_mirt.jl
Original file line number Diff line number Diff line change
Expand Up @@ -21,8 +21,8 @@ using AlgebraOfGraphics
using ComputerAdaptiveTesting
using ComputerAdaptiveTesting.Sim: auto_responder
using ComputerAdaptiveTesting.NextItemRules: DRuleItemCriterion
using ComputerAdaptiveTesting.TerminationConditions: FixedItemsTerminationCondition
using ComputerAdaptiveTesting.Aggregators: PriorAbilityEstimator,
using ComputerAdaptiveTesting.TerminationConditions: FixedLength
using ComputerAdaptiveTesting.Aggregators: PosteriorAbilityEstimator,
MeanAbilityEstimator, LikelihoodAbilityEstimator
using FittedItemBanks
import PsychometricsBazaarBase.IntegralCoeffs
Expand All @@ -49,11 +49,11 @@ using ComputerAdaptiveTesting.Responses: BooleanResponse
# CatRecorder collects information which can be used to draw different types of plots.
max_questions = 9
integrator = CubaIntegrator([-6.0, -6.0], [6.0, 6.0], CubaVegas(); rtol = 1e-2)
ability_estimator = MeanAbilityEstimator(PriorAbilityEstimator(std_mv_normal(dims)),
ability_estimator = MeanAbilityEstimator(PosteriorAbilityEstimator(std_mv_normal(dims)),
integrator)
rules = CatRules(ability_estimator,
DRuleItemCriterion(ability_estimator),
FixedItemsTerminationCondition(max_questions))
FixedLength(max_questions))

# XXX: We shouldn't need to specify xs here since the distributions are not used -- rework
points = 3
Expand All @@ -67,7 +67,7 @@ recorder = CatRecorder(xs,
abilities)
for testee_idx in axes(responses, 2)
@debug "Running for testee" testee_idx
tracked_responses, θ = run_cat(CatLoopConfig(rules = rules,
tracked_responses, θ = run_cat(CatLoop(rules = rules,
get_response = auto_responder(@view responses[:, testee_idx]),
new_response_callback = (tracked_responses, terminating) -> recorder(tracked_responses,
testee_idx,
Expand Down
12 changes: 6 additions & 6 deletions docs/examples/examples/vocab_iq.jl
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,9 @@
# ---

#md # Running a CAT based based on real response data
#
#
# This example shows how to run a CAT end-to-end on real data.
#
#
# First a 1-dimensional IRT model is fitted based on open response data to the
# vocabulary IQ test using the IRTSupport package which internally, this uses
# the `mirt` R package. Next, the model is used to administer the test
Expand Down Expand Up @@ -37,13 +37,13 @@ function run_vocab_iq_cat()
item_bank, labels = get_item_bank()
integrator = FixedGKIntegrator(-6, 6, 61)
ability_integrator = AbilityIntegrator(integrator)
dist_ability_est = PriorAbilityEstimator(std_normal)
dist_ability_est = PosteriorAbilityEstimator(std_normal)
optimizer = AbilityOptimizer(OneDimOptimOptimizer(-6.0, 6.0, NelderMead()))
ability_estimator = ModeAbilityEstimator(dist_ability_est, optimizer)
@info "run_cat" ability_estimator
rules = CatRules(ability_estimator,
AbilityVarianceStateCriterion(dist_ability_est, ability_integrator),
FixedItemsTerminationCondition(45))
AbilityVariance(dist_ability_est, ability_integrator),
FixedLength(45))
function get_response(response_idx, response_name)
params = item_params(item_bank, response_idx)
println("Parameters for next question: $params")
Expand All @@ -63,7 +63,7 @@ function run_vocab_iq_cat()
println("Got ability estimate: $ability ± $var")
println("")
end
loop_config = CatLoopConfig(rules = rules,
loop_config = CatLoop(rules = rules,
get_response = get_response,
new_response_callback = new_response_callback)
run_cat(loop_config, item_bank)
Expand Down
2 changes: 1 addition & 1 deletion docs/src/api.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,5 +8,5 @@ CurrentModule = ComputerAdaptiveTesting
```

```@autodocs
Modules = [ComputerAdaptiveTesting, ComputerAdaptiveTesting.Aggregators, ComputerAdaptiveTesting.Responses, ComputerAdaptiveTesting.Sim, ComputerAdaptiveTesting.TerminationConditions, ComputerAdaptiveTesting.NextItemRules, ComputerAdaptiveTesting.CatConfig]
Modules = [ComputerAdaptiveTesting, ComputerAdaptiveTesting.Aggregators, ComputerAdaptiveTesting.Responses, ComputerAdaptiveTesting.Sim, ComputerAdaptiveTesting.TerminationConditions, ComputerAdaptiveTesting.NextItemRules, ComputerAdaptiveTesting.Rules]
```
14 changes: 7 additions & 7 deletions docs/src/creating_a_cat.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ The configuration of a CAT is built up as a tree of configuration structs.
These structs are all subtypes of `CatConfigBase`.

```@docs; canonical=false
ComputerAdaptiveTesting.CatConfig.CatConfigBase
ComputerAdaptiveTesting.ConfigBase.CatConfigBase
```

The constructors for the configuration structs in this package tend to have
Expand Down Expand Up @@ -59,7 +59,7 @@ next item selection rule, and the stopping rule. `CatRules` has explicit and
implicit constructors.

```@docs; canonical=false
ComputerAdaptiveTesting.CatConfig.CatRules
ComputerAdaptiveTesting.CatRules
```

### Next item selection with `NextItemRule`
Expand All @@ -79,13 +79,13 @@ ComputerAdaptiveTesting.NextItemRules.RandomNextItemRule

Other rules are created by combining a `ItemCriterion` -- which somehow rates
items according to how good they are -- with a `NextItemStrategy` using an
`ItemStrategyNextItemRule`, which acts as an adapter. The default
`ItemCriterionRule`, which acts as an adapter. The default
`NextItemStrategy` (and currently only) is `ExhaustiveSearch`. When using
the implicit constructors, `ItemCriterion` can therefore be used directly
without wrapping in any place an NextItemRule is expected.

```@docs; canonical=false
ComputerAdaptiveTesting.NextItemRules.ItemStrategyNextItemRule
ComputerAdaptiveTesting.NextItemRules.ItemCriterionRule
```

```@docs; canonical=false
Expand Down Expand Up @@ -114,17 +114,17 @@ takes a `ResponseExpectation`: either `PointResponseExpectation` or
good a particular state is in terms getting a good estimate of the test takers
ability. They look one ply ahead to get the expected value of the
``StateCriterion`` after selecting the given item. The
`AbilityVarianceStateCriterion` looks at the variance of the ability ``\theta``
`AbilityVariance` looks at the variance of the ability ``\theta``
estimate at that state.

### Stopping rules with `TerminationCondition`

Currently the only `TerminationCondition` is `FixedItemsTerminationCondition`, which ends the test after a fixed number of items.
Currently the only `TerminationCondition` is `FixedLength`, which ends the test after a fixed number of items.

```@docs; canonical=false
ComputerAdaptiveTesting.TerminationConditions.TerminationCondition
```

```@docs; canonical=false
ComputerAdaptiveTesting.TerminationConditions.FixedItemsTerminationCondition
ComputerAdaptiveTesting.TerminationConditions.FixedLength
```
4 changes: 2 additions & 2 deletions docs/src/stateful.md
Original file line number Diff line number Diff line change
Expand Up @@ -28,9 +28,9 @@ Stateful.get_ability
There is an implementation in terms of [CatRules](@ref):

```@docs
Stateful.StatefulCatConfig
Stateful.StatefulCatRules
```

## Usage

Just as [CatLoopConfig](@ref) can wrap [CatRules](@ref), you can also use it with any implementor of [Stateful.StatefulCat](@ref), and run using [Sim.run_cat](@ref).
Just as [CatLoop](@ref) can wrap [CatRules](@ref), you can also use it with any implementor of [Stateful.StatefulCat](@ref), and run using [Sim.run_cat](@ref).
4 changes: 2 additions & 2 deletions docs/src/using_your_cat.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,10 +9,10 @@ a number of ways you can use it.
This section covers a few.
See also the [Examples](@ref demo-page).

When you've set up your CAT using [CatRules](@ref), you can wrap it in a [CatLoopConfig](@ref) and run it with [run_cat](@ref).
When you've set up your CAT using [CatRules](@ref), you can wrap it in a [CatLoop](@ref) and run it with [run_cat](@ref).

```@docs; canonical=false
CatLoopConfig
CatLoop
run_cat
```

Expand Down
28 changes: 24 additions & 4 deletions ext/TestExt.jl
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ module TestExt

using Test
using ComputerAdaptiveTesting: Stateful
using FittedItemBanks: AbstractItemBank, ItemResponse, resp
using FittedItemBanks: AbstractItemBank, ItemResponse, resp_vec

export test_stateful_cat_1d_dich_ib, test_stateful_cat_item_bank_1d_dich_ib

Expand Down Expand Up @@ -96,18 +96,38 @@ function test_stateful_cat_item_bank_1d_dich_ib(
cat::Stateful.StatefulCat,
item_bank::AbstractItemBank,
points=[-.78, 0.0, .78],
margin=0.05,
margin=0.01,
)
if length(item_bank) != Stateful.item_bank_size(cat)
error("Item bank length does not match the cat's item bank size.")
end
for i in 1:length(item_bank)
for point in points
cat_prob = Stateful.item_response_function(cat, i, true, point)
ib_prob = resp(ItemResponse(item_bank, i), true, point)
cat_prob = Stateful.item_response_functions(cat, i, point)
ib_prob = resp_vec(ItemResponse(item_bank, i), point)
@test cat_prob ≈ ib_prob rtol=margin
end
end
end

function test_ability(
cat1::Stateful.StatefulCat,
cat2::Stateful.StatefulCat,
item_bank_length;
margin=0.01
)
if item_bank_length < 4
error("Item bank length must be at least 4.")
end
for cat in (cat1, cat2)
Stateful.add_response!(cat, 1, false)
Stateful.add_response!(cat, 2, true)
Stateful.add_response!(cat, 3, false)
Stateful.add_response!(cat, 4, true)
end
ability1 = Stateful.get_ability(cat1)
ability2 = Stateful.get_ability(cat2)
@test ability1[1] ≈ ability2[1] rtol=margin
end

end
2 changes: 1 addition & 1 deletion profile/next_items.jl
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ function get_ability_estimator(multidim)
integrator = FixedGKIntegrator(-6.0, 6.0)
dist = Normal()
end
return PriorAbilityEstimator(dist, integrator)
return PosteriorAbilityEstimator(dist, integrator)
end

function prepare_empty(item_bank, actual_responses, ability_tracker)
Expand Down
Loading
Loading