Skip to content

Commit 7974959

Browse files
committed
Reorganise the next item rules into subdirectories
1 parent d79cfb6 commit 7974959

File tree

19 files changed

+361
-350
lines changed

19 files changed

+361
-350
lines changed

src/next_item_rules/NextItemRules.jl

Lines changed: 27 additions & 162 deletions
Original file line numberDiff line numberDiff line change
@@ -46,167 +46,32 @@ export AbilityCovarianceStateCriteria, StateCriteria, ItemCriteria
4646
export InformationMatrixCriteria
4747
export ScalarizedStateCriteron, ScalarizedItemCriteron
4848

49-
"""
50-
$(TYPEDEF)
51-
52-
Abstract base type for all item selection rules. All descendants of this type
53-
are expected to implement the interface
54-
`(rule::NextItemRule)(responses::TrackedResponses, items::AbstractItemBank)::Int`
55-
56-
$(FUNCTIONNAME)(bits...; ability_estimator=nothing, parallel=true)
57-
58-
Implicit constructor for $(FUNCTIONNAME). Uses any given `NextItemRule` or
59-
delegates to `ItemStrategyNextItemRule`.
60-
"""
61-
abstract type NextItemRule <: CatConfigBase end
62-
63-
function NextItemRule(bits...;
64-
ability_estimator = nothing,
65-
ability_tracker = nothing,
66-
parallel = true)
67-
@returnsome find1_instance(NextItemRule, bits)
68-
@returnsome ItemStrategyNextItemRule(bits...,
69-
ability_estimator = ability_estimator,
70-
ability_tracker = ability_tracker,
71-
parallel = parallel)
72-
end
73-
74-
include("./random.jl")
75-
include("./information.jl")
76-
include("./information_special.jl")
77-
include("./objective_function.jl")
78-
include("./expectation.jl")
79-
80-
const default_prior = IntegralCoeffs.Prior(Cauchy(5, 2))
81-
82-
function choose_item_1ply(objective::ItemCriterionT,
83-
responses::TrackedResponseT,
84-
items::AbstractItemBank)::Tuple{
85-
Int,
86-
Float64
87-
} where {ItemCriterionT <: ItemCriterion, TrackedResponseT <: TrackedResponses}
88-
#pre_next_item(expectation_tracker, items)
89-
objective_state = init_thread(objective, responses)
90-
min_obj_idx::Int = -1
91-
min_obj_val::Float64 = Inf
92-
for item_idx in eachindex(items)
93-
# TODO: Add these back in
94-
#@init irf_states_storage = zeros(Int, length(responses) + 1)
95-
if (findfirst(idx -> idx == item_idx, responses.responses.indices) !== nothing)
96-
continue
97-
end
98-
99-
obj_val = objective(objective_state, responses, item_idx)
100-
101-
if obj_val <= min_obj_val
102-
min_obj_val = obj_val
103-
min_obj_idx = item_idx
104-
end
105-
end
106-
return (min_obj_idx, min_obj_val)
107-
end
108-
109-
function init_thread(::ItemCriterion, ::TrackedResponses)
110-
nothing
111-
end
112-
113-
"""
114-
$(TYPEDEF)
115-
"""
116-
abstract type NextItemStrategy <: CatConfigBase end
117-
118-
function NextItemStrategy(; parallel = true)
119-
ExhaustiveSearch(parallel)
120-
end
121-
122-
function NextItemStrategy(bits...; parallel = true)
123-
@returnsome find1_instance(NextItemStrategy, bits)
124-
@returnsome find1_type(NextItemStrategy, bits) typ->typ(; parallel = parallel)
125-
@returnsome NextItemStrategy(; parallel = parallel)
126-
end
127-
128-
"""
129-
$(TYPEDEF)
130-
$(TYPEDFIELDS)
131-
132-
"""
133-
@with_kw struct ExhaustiveSearch <: NextItemStrategy
134-
parallel::Bool = false
135-
end
136-
137-
"""
138-
$(TYPEDEF)
139-
$(TYPEDFIELDS)
140-
141-
`ItemStrategyNextItemRule` which together with a `NextItemStrategy` acts as an
142-
adapter by which an `ItemCriterion` can serve as a `NextItemRule`.
143-
144-
$(FUNCTIONNAME)(bits...; ability_estimator=nothing, parallel=true)
145-
146-
Implicit constructor for $(FUNCTIONNAME). Will default to
147-
`ExhaustiveSearch` when no `NextItemStrategy` is given.
148-
"""
149-
struct ItemStrategyNextItemRule{
150-
NextItemStrategyT <: NextItemStrategy,
151-
ItemCriterionT <: ItemCriterion
152-
} <: NextItemRule
153-
strategy::NextItemStrategyT
154-
criterion::ItemCriterionT
155-
end
156-
157-
function ItemStrategyNextItemRule(bits...;
158-
parallel = true,
159-
ability_estimator = nothing,
160-
ability_tracker = nothing)
161-
strategy = NextItemStrategy(bits...; parallel = parallel)
162-
criterion = ItemCriterion(bits...;
163-
ability_estimator = ability_estimator,
164-
ability_tracker = ability_tracker)
165-
if strategy !== nothing && criterion !== nothing
166-
return ItemStrategyNextItemRule(strategy, criterion)
167-
end
168-
end
169-
170-
function (rule::ItemStrategyNextItemRule{ExhaustiveSearch, ItemCriterionT})(responses,
171-
items) where {ItemCriterionT <: ItemCriterion}
172-
#, rule.strategy.parallel
173-
choose_item_1ply(rule.criterion, responses, items)[1]
174-
end
175-
176-
function (item_criterion::ItemCriterion)(::Nothing, tracked_responses, item_idx)
177-
item_criterion(tracked_responses, item_idx)
178-
end
179-
180-
function (item_criterion::ItemCriterion)(tracked_responses, item_idx)
181-
criterion_state = init_thread(item_criterion, tracked_responses)
182-
if criterion_state === nothing
183-
error("Tried to run an state-requiring item criterion $(typeof(item_criterion)), but init_thread(...) returned nothing")
184-
end
185-
item_criterion(criterion_state, tracked_responses, item_idx)
186-
end
187-
188-
function compute_criteria(
189-
criterion::ItemCriterionT,
190-
responses::TrackedResponseT,
191-
items::AbstractItemBank
192-
) where {ItemCriterionT <: ItemCriterion, TrackedResponseT <: TrackedResponses}
193-
objective_state = init_thread(criterion, responses)
194-
return [criterion(objective_state, responses, item_idx)
195-
for item_idx in eachindex(items)]
196-
end
197-
198-
function compute_criteria(
199-
rule::ItemStrategyNextItemRule{StrategyT, ItemCriterionT},
200-
responses,
201-
items
202-
) where {StrategyT, ItemCriterionT <: ItemCriterion}
203-
compute_criteria(rule.criterion, responses, items)
204-
end
205-
206-
include("./mirt.jl")
207-
include("./aliases.jl")
208-
include("./preallocate.jl")
209-
210-
include("./ka.jl")
49+
# Prelude
50+
include("./prelude/abstract.jl")
51+
include("./prelude/next_item_rule.jl")
52+
include("./prelude/strategy.jl")
53+
include("./prelude/criteria.jl")
54+
include("./prelude/preallocate.jl")
55+
56+
# Selection strategies
57+
include("./strategies/random.jl")
58+
include("./strategies/exhaustive.jl")
59+
60+
# Combinators
61+
include("./combinators/expectation.jl")
62+
include("./combinators/scalarizers.jl")
63+
64+
# Criteria
65+
include("./criteria/item/information_special.jl")
66+
include("./criteria/item/information_support.jl")
67+
include("./criteria/item/information.jl")
68+
include("./criteria/item/urry.jl")
69+
include("./criteria/state/ability_variance.jl")
70+
71+
# Porcelain
72+
include("./porcelain/aliases.jl")
73+
74+
# Experimental
75+
include("./experimental/ka.jl")
21176

21277
end
Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
struct DeterminantScalarizer <: MatrixScalarizer end
2+
(::DeterminantScalarizer)(mat) = det(mat)
3+
4+
struct TraceScalarizer <: MatrixScalarizer end
5+
(::TraceScalarizer)(mat) = tr(mat)
6+
7+
struct ScalarizedItemCriteron{
8+
ItemCriteriaT <: ItemCriteria,
9+
MatrixScalarizerT <: MatrixScalarizer
10+
} <: ItemCriterion
11+
criteria::ItemCriteriaT
12+
scalarizer::MatrixScalarizerT
13+
end
14+
15+
function (ssc::ScalarizedItemCriteron)(tracked_responses, item_idx)
16+
res = ssc.criteria(
17+
init_thread(ssc.criteria, tracked_responses), tracked_responses, item_idx) |>
18+
ssc.scalarizer
19+
if !should_minimize(ssc.criteria)
20+
res = -res
21+
end
22+
res
23+
end
24+
25+
struct ScalarizedStateCriteron{
26+
StateCriteriaT <: StateCriteria,
27+
MatrixScalarizerT <: MatrixScalarizer
28+
} <: StateCriterion
29+
criteria::StateCriteriaT
30+
scalarizer::MatrixScalarizerT
31+
end
32+
33+
function (ssc::ScalarizedStateCriteron)(tracked_responses)
34+
res = ssc.criteria(tracked_responses) |> ssc.scalarizer
35+
if !should_minimize(ssc.criteria)
36+
res = -res
37+
end
38+
res
39+
end
40+
41+
struct WeightedStateCriteria{InnerT <: StateCriteria} <: StateCriteria
42+
weights::Vector{Float64}
43+
criteria::InnerT
44+
end
45+
46+
function (wsc::WeightedStateCriteria)(tracked_responses, item_idx)
47+
wsc.weights' * wsc.criteria(tracked_responses, item_idx) * wsc.weights
48+
end
49+
50+
struct WeightedItemCriteria{InnerT <: ItemCriteria} <: ItemCriteria
51+
weights::Vector{Float64}
52+
criteria::InnerT
53+
end
54+
55+
function (wsc::WeightedItemCriteria)(tracked_responses, item_idx)
56+
wsc.weights' * wsc.criteria(tracked_responses, item_idx) * wsc.weights
57+
end
Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
# TODO: Should have Variants for point ability versus distribution ability
2+
struct InformationItemCriterion{AbilityEstimatorT <: PointAbilityEstimator, F} <:
3+
ItemCriterion
4+
ability_estimator::AbilityEstimatorT
5+
expected_item_information::F
6+
end
7+
8+
function InformationItemCriterion(ability_estimator)
9+
InformationItemCriterion(ability_estimator, expected_item_information)
10+
end
11+
12+
function (item_criterion::InformationItemCriterion)(tracked_responses::TrackedResponses,
13+
item_idx)
14+
ability = maybe_tracked_ability_estimate(tracked_responses,
15+
item_criterion.ability_estimator)
16+
ir = ItemResponse(tracked_responses.item_bank, item_idx)
17+
return -item_criterion.expected_item_information(ir, ability)
18+
end
19+
20+
struct InformationMatrixCriteria{AbilityEstimatorT <: AbilityEstimator, F} <: ItemCriteria
21+
ability_estimator::AbilityEstimatorT
22+
expected_item_information::F
23+
end
24+
25+
function InformationMatrixCriteria(ability_estimator)
26+
InformationMatrixCriteria(ability_estimator, expected_item_information)
27+
end
28+
29+
function init_thread(item_criterion::InformationMatrixCriteria,
30+
responses::TrackedResponses)
31+
# TODO: No need to do this one per thread. It just need to be done once per
32+
# θ update.
33+
# TODO: Update this to use track!(...) mechanism
34+
ability = maybe_tracked_ability_estimate(responses, item_criterion.ability_estimator)
35+
responses_information(responses.item_bank, responses.responses, ability)
36+
end
37+
38+
function (item_criterion::InformationMatrixCriteria)(acc_info::Matrix{Float64},
39+
tracked_responses::TrackedResponses,
40+
item_idx)
41+
# TODO: Add in information from the prior
42+
ability = maybe_tracked_ability_estimate(
43+
tracked_responses, item_criterion.ability_estimator)
44+
return acc_info .+
45+
item_criterion.expected_item_information(
46+
ItemResponse(tracked_responses.item_bank, item_idx), ability)
47+
end
48+
49+
should_minimize(::InformationMatrixCriteria) = false
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
"""
2+
$(TYPEDEF)
3+
$(TYPEDFIELDS)
4+
5+
This item criterion just picks the item with the raw difficulty closest to the
6+
current ability estimate.
7+
"""
8+
struct UrryItemCriterion{AbilityEstimatorT <: PointAbilityEstimator} <: ItemCriterion
9+
ability_estimator::AbilityEstimatorT
10+
end
11+
12+
# TODO: Slow + poor error handling
13+
function raw_difficulty(item_bank, item_idx)
14+
item_params(item_bank, item_idx).difficulty
15+
end
16+
17+
function (item_criterion::UrryItemCriterion)(tracked_responses::TrackedResponses, item_idx)
18+
ability = maybe_tracked_ability_estimate(tracked_responses,
19+
item_criterion.ability_estimator)
20+
diff = raw_difficulty(tracked_responses.item_bank, item_idx)
21+
abs(ability - diff)
22+
end

0 commit comments

Comments
 (0)