Skip to content

Commit a63b33e

Browse files
committed
fisher fixes
1 parent 44ee726 commit a63b33e

File tree

3 files changed

+40
-30
lines changed

3 files changed

+40
-30
lines changed

libfabulouscatpy/cat/itemselectors/fisher.py

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -88,9 +88,7 @@ def criterion(self, scoring: BayesianScoring, items: list[dict], scale=None) ->
8888

8989
return fish_scored
9090

91-
92-
93-
class StochasticFisherItemSelector(ItemSelector):
91+
class StochasticFisherItemSelector(FisherItemSelector):
9492

9593
description = """Selection based on Fisher information"""
9694

libfabulouscatpy/cat/itemselectors/globalinfo.py

Lines changed: 30 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -10,32 +10,32 @@
1010
# of Health and Human Services, which is making the software available to the
1111
# public for any commercial or non-commercial purpose under the following
1212
# open-source BSD license.
13-
#
13+
#
1414
# Redistribution and use in source and binary forms, with or without
1515
# modification, are permitted provided that the following conditions are met:
16-
#
16+
#
1717
# (1) Redistributions of source code must retain this copyright
1818
# notice, this list of conditions and the following disclaimer.
19-
#
19+
#
2020
# (2) Redistributions in binary form must reproduce this copyright
2121
# notice, this list of conditions and the following disclaimer in the
2222
# documentation and/or other materials provided with the distribution.
23-
#
23+
#
2424
# (3) Neither the names of the National Institutes of Health Clinical
2525
# Center, the National Institutes of Health, the U.S. Department of
2626
# Health and Human Services, nor the names of any of the software
2727
# developers may be used to endorse or promote products derived from
2828
# this software without specific prior written permission.
29-
#
29+
#
3030
# (4) Please acknowledge NIHCC as the source of this software by including
3131
# the phrase "Courtesy of the U.S. National Institutes of Health Clinical
3232
# Center"or "Source: U.S. National Institutes of Health Clinical Center."
33-
#
33+
#
3434
# THIS SOFTWARE IS PROVIDED BY THE U.S. GOVERNMENT AND CONTRIBUTORS "AS
3535
# IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
3636
# TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
3737
# PARTICULAR PURPOSE ARE DISCLAIMED.
38-
#
38+
#
3939
# You are under no obligation whatsoever to provide any bug fixes,
4040
# patches, or upgrades to the features, functionality or performance of
4141
# the source code ("Enhancements") to anyone; however, if you choose to
@@ -68,14 +68,14 @@ def __init__(self, scoring, deterministic=True, hybrid=False, **kwargs):
6868
self.hybrid = hybrid
6969
self.deterministic = deterministic
7070

71-
def criterion(self, scoring: BayesianScoring, items: list[dict], scale=None) -> dict[str: Any]:
72-
71+
def criterion(
72+
self, scoring: BayesianScoring, items: list[dict], scale=None
73+
) -> dict[str:Any]:
7374
"""
7475
Parameters: session: instance of CatSession
7576
Returns: item dictionary entry or None
7677
"""
7778

78-
7979
unresponded = [i for i in items if "scales" in i.keys()]
8080
in_scale = [i for i in unresponded if scale in i["scales"].keys()]
8181

@@ -106,19 +106,25 @@ def criterion(self, scoring: BayesianScoring, items: list[dict], scale=None) ->
106106
)[
107107
:, unresponded_ndx, :
108108
] #
109-
109+
110110
p_itemized = np.exp(lp_itemized)
111111
pi_density = scoring.scores[scale].density
112112

113-
criterion = np.sum(p_itemized*(lp_itemized - lp_point)*pi_density[:, np.newaxis, np.newaxis], axis=0)
113+
criterion = np.trapz(
114+
p_itemized
115+
* (lp_itemized - lp_point)
116+
* pi_density[:, np.newaxis, np.newaxis],
117+
x=scoring.interpolation_pts[scale],
118+
axis=0,
119+
)
114120
criterion = np.sum(criterion, axis=-1)
115-
criterion = dict(zip([x['item'] for x in items], criterion))
121+
criterion = dict(zip([x["item"] for x in items], criterion))
116122
return criterion
117-
123+
118124
def _next_scored_item(
119125
self, tracker: CatSessionTracker, scale=None
120-
) -> dict[str : dict[str:Any]]:
121-
126+
) -> dict[str : dict[str:Any]]:
127+
122128
scale = self.next_scale(tracker)
123129
un_items = self.un_items(tracker, scale)
124130

@@ -131,9 +137,9 @@ def _next_scored_item(
131137
trait = 0.0 if trait is None else trait
132138
error = tracker.errors[scale]
133139
error = 100.0 if error is None else error
134-
135-
criterion = self.criterion(scoring=self.scoring, items = un_items, scale=scale)
136-
valid_items = [x['item'] for x in un_items]
140+
141+
criterion = self.criterion(scoring=self.scoring, items=un_items, scale=scale)
142+
valid_items = [x["item"] for x in un_items]
137143
items = []
138144
Delta = []
139145
for k, v in criterion.items():
@@ -143,15 +149,15 @@ def _next_scored_item(
143149
if len(items) == 0:
144150
return {}
145151
Delta -= np.max(Delta)
146-
probs = np.exp(Delta/self.temperature)
152+
probs = np.exp(Delta / self.temperature)
147153
probs /= np.sum(probs)
148-
149-
if self.deterministic or (self.hybrid and ((self.scoring.n_scored[scale] > 3))):
154+
155+
if self.deterministic or (self.hybrid and ((self.scoring.n_scored[scale] > 3))):
150156
ndx = np.argmax(probs)
151157
else:
152158
ndx = np.random.choice(np.arange(len(criterion.keys())), p=probs)
153159
result = list(criterion.keys())[ndx]
154160
for i in un_items:
155-
if i['item'] == result:
161+
if i["item"] == result:
156162
return i
157-
return {}
163+
return {}

libfabulouscatpy/cat/itemselectors/kl.py

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -104,10 +104,14 @@ def criterion(self, scoring: BayesianScoring, items: list[dict], scale=None) ->
104104
p_itemized = np.exp(lp_itemized)
105105
pi_density = scoring.scores[scale].density
106106

107-
lp_infty = lp_itemized + energy[:, np.newaxis, np.newaxis]
107+
# lp_infty = lp_itemized + energy[:, np.newaxis, np.newaxis]
108+
lp_infty = 0.5*lp_itemized + energy[:, np.newaxis, np.newaxis] * (lp_itemized.shape[1] - 1)/2
108109
# N_grid x N_item x K
109110
expected_lp_infty = np.sum(lp_infty*p_itemized, axis=-1, keepdims=True)
110111
expected_lp_infty = np.sum(expected_lp_infty, axis=-2, keepdims=True)
112+
# shift it so that it is closer to the running estimate
113+
114+
111115
# N_grid x 1 x 1
112116
pi_infty = np.exp(expected_lp_infty - np.max(expected_lp_infty, axis=0, keepdims=True))
113117
pi_infty /= np.trapz(
@@ -223,15 +227,17 @@ def criterion(self, scoring: BayesianScoring, items: list[dict], scale=None) ->
223227
p_itemized = np.exp(lp_itemized)
224228
pi_density = scoring.scores[scale].density
225229

226-
lp_infty = lp_itemized + energy[:, np.newaxis, np.newaxis]
230+
# lp_infty = lp_itemized + energy[:, np.newaxis, np.newaxis]
231+
lp_infty = 0.5*lp_itemized + energy[:, np.newaxis, np.newaxis] * (lp_itemized.shape[1] - 1)/2
232+
227233
# N_grid x N_item x K
228234
expected_lp_infty = np.sum(lp_infty*p_itemized, axis=-1, keepdims=True)
229235
# N_grid x N_item x 1
230236
expected_lp_infty = np.sum(expected_lp_infty, axis=-2, keepdims=True)
231237
pi_infty = np.exp(expected_lp_infty - np.max(expected_lp_infty, axis=0, keepdims=True))
232238
pi_infty /= np.trapz(
233239
y=pi_infty, x=scoring.interpolation_pts[scale], axis=0
234-
) # N_grid x N_item x 1
240+
) # N_grid x 1 x 1
235241
##########
236242
# $\pi_\infty$ is computed
237243
########

0 commit comments

Comments
 (0)