Skip to content

Commit 0ea42a7

Browse files
authored
Merge pull request #44 from MastodonC/feature/validate-transitions
Feature/validate transitions
2 parents 9174dd1 + 17b876e commit 0ea42a7

6 files changed

Lines changed: 82 additions & 75 deletions

File tree

src/clj/witan/send/schemas.clj

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -110,10 +110,11 @@
110110

111111
(def ValidSettingAcademicYears
112112
(make-ordered-ds-schema [[:setting s/Keyword]
113-
[:setting->group s/Str]
113+
[:setting-group s/Str]
114114
[:min-academic-year AcademicYear]
115115
[:max-academic-year AcademicYear]
116-
[:needs s/Str]]))
116+
[:needs s/Str]
117+
[:setting->setting s/Str]]))
117118

118119
(def PopulationByCalendarAndAcademicYear
119120
{CalendarYear {AcademicYear N}})

src/clj/witan/send/send.clj

Lines changed: 28 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -216,9 +216,9 @@
216216
(update-ifelse-assoc assoc-first-state second-state + diff)))
217217
transitions))
218218

219-
(defn prep-inputs [initial-state splice-ncy valid-states transition-matrix transition-matrix-filtered
220-
population valid-setting-academic-years original-transitions setting-cost
221-
filter-transitions-from]
219+
(defn prep-inputs [initial-state splice-ncy valid-states valid-transitions transition-matrix
220+
transition-matrix-filtered population valid-setting-academic-years
221+
original-transitions setting-cost filter-transitions-from]
222222
(let [start-map {:population-by-age-state initial-state
223223
:valid-setting-academic-years valid-setting-academic-years
224224
:transition-matrix original-transitions
@@ -246,19 +246,19 @@
246246
(p/alpha-params-joiner-states valid-states (u/transitions-map transition-matrix-filtered)))
247247

248248
:mover-beta-params (stitch-ay-state-params splice-ncy
249-
(p/beta-params-movers valid-states transition-matrix)
250-
(p/beta-params-movers valid-states transition-matrix-filtered))
249+
(p/beta-params-movers valid-states valid-transitions transition-matrix)
250+
(p/beta-params-movers valid-states valid-transitions transition-matrix-filtered))
251251
:mover-state-alphas (stitch-ay-state-params splice-ncy
252-
(p/alpha-params-movers valid-states transition-matrix)
253-
(p/alpha-params-movers valid-states transition-matrix-filtered))})
252+
(p/alpha-params-movers valid-states valid-transitions transition-matrix)
253+
(p/alpha-params-movers valid-states valid-transitions transition-matrix-filtered))})
254254
(merge start-map
255255
{:joiner-beta-params (p/beta-params-joiners valid-states
256256
transition-matrix
257257
(ds/row-maps population))
258258
:leaver-beta-params (p/beta-params-leavers valid-states transition-matrix)
259259
:joiner-state-alphas (p/alpha-params-joiner-states valid-states (u/transitions-map transition-matrix))
260-
:mover-beta-params (p/beta-params-movers valid-states transition-matrix)
261-
:mover-state-alphas (p/alpha-params-movers valid-states transition-matrix)}))))
260+
:mover-beta-params (p/beta-params-movers valid-states valid-transitions transition-matrix)
261+
:mover-state-alphas (p/alpha-params-movers valid-states valid-transitions transition-matrix)}))))
262262

263263
(defn build-states-to-change [settings-to-change valid-needs ages years]
264264
(if (= :nil (-> settings-to-change
@@ -307,10 +307,12 @@
307307
(let [original-transitions transition-matrix
308308
ages (distinct (map :academic-year (ds/row-maps population)))
309309
years (distinct (map :calendar-year (ds/row-maps population)))
310-
valid-needs (->> (ds/row-maps valid-setting-academic-years)
311-
(states/calculate-valid-needs-from-setting-academic-years))
312-
_ (when (not= 1 modify-transition-by)
313-
(prn (str "Modifying transitions by " modify-transition-by)))
310+
initialise-validation (ds/row-maps valid-setting-academic-years)
311+
valid-transitions (states/calculate-valid-mover-transitions initialise-validation)
312+
valid-needs (states/calculate-valid-needs-from-setting-academic-years initialise-validation)
313+
valid-settings (states/calculate-valid-settings-from-setting-academic-years initialise-validation)
314+
valid-states (states/calculate-valid-states-from-setting-academic-years initialise-validation)
315+
valid-year-settings (states/calculate-valid-year-settings-from-setting-academic-years initialise-validation)
314316
states-to-change (when (not= 1 modify-transition-by)
315317
(build-states-to-change settings-to-change valid-needs ages years))
316318
transition-matrix (ds/row-maps transition-matrix)
@@ -319,40 +321,28 @@
319321
u/full-transitions-map)
320322
result (reduce (fn [m k] (modify-transitions m k * modify-transition-by)) convert states-to-change)]
321323
(mapcat (fn [[k v]] (u/back-to-transitions-matrix k v)) result)))
322-
_ (if (nil? modified-transition-matrix)
323-
(prn "Using input transitions matrix")
324-
(prn "Using modified transitions matrix"))
325-
transitions (if (nil? modified-transition-matrix)
326-
(u/transitions-map transition-matrix)
327-
(u/transitions-map modified-transition-matrix))
324+
_ (if modified-transition-matrix
325+
(prn "Using modified transitions matrix")
326+
(prn "Using input transitions matrix"))
327+
transitions (if modified-transition-matrix
328+
(u/transitions-map modified-transition-matrix)
329+
(u/transitions-map transition-matrix))
328330
transition-matrix-filtered (when filter-transitions-from
329331
(mapcat (fn [year] (filter #(= (:calendar-year %) year) (or modified-transition-matrix transition-matrix))) filter-transitions-from))
330-
331-
initial-state (initialise-model (ds/row-maps initial-send-population))
332-
333-
valid-settings (->> (ds/row-maps valid-setting-academic-years)
334-
(states/calculate-valid-settings-from-setting-academic-years))
335-
336-
valid-states (->> (ds/row-maps valid-setting-academic-years)
337-
(states/calculate-valid-states-from-setting-academic-years))
338-
339-
valid-year-settings (->> (ds/row-maps valid-setting-academic-years)
340-
(states/calculate-valid-year-settings-from-setting-academic-years))]
341-
332+
initial-state (initialise-model (ds/row-maps initial-send-population))]
342333
(when (not= 1 modify-transition-by)
343334
(report/info "Modified transitions by " (report/bold modify-transition-by)))
344-
(if (nil? modified-transition-matrix)
345-
(report/info "Used " (report/bold "input") " transitions matrix\n")
346-
(report/info "Used " (report/bold "modified") " transitions matrix\n"))
347-
335+
(if modified-transition-matrix
336+
(report/info "Used " (report/bold "modified") " transitions matrix\n")
337+
(report/info "Used " (report/bold "input") " transitions matrix\n"))
348338
(s/validate (sc/SENDPopulation+ valid-settings) initial-send-population)
349339
(s/validate (sc/TransitionsMap+ valid-needs valid-settings) transitions)
350340
(s/validate (sc/NeedSettingCost+ valid-needs valid-settings) setting-cost)
351-
{:standard-projection (prep-inputs initial-state splice-ncy valid-states transition-matrix transition-matrix-filtered
341+
{:standard-projection (prep-inputs initial-state splice-ncy valid-states valid-transitions transition-matrix transition-matrix-filtered
352342
population valid-setting-academic-years original-transitions setting-cost
353343
filter-transitions-from)
354344
:scenario-projection (when modified-transition-matrix
355-
(prep-inputs initial-state splice-ncy valid-states modified-transition-matrix
345+
(prep-inputs initial-state splice-ncy valid-states valid-transitions modified-transition-matrix
356346
transition-matrix-filtered population valid-setting-academic-years
357347
original-transitions setting-cost filter-transitions-from))
358348
:modify-transition-by modify-transition-by
@@ -541,7 +531,7 @@
541531
(report/info (report/bold "Not every historic transition present in projection!") "Consider checking valid state input.\n"))
542532
(when output
543533
(let [valid-settings (assoc (->> (ds/row-maps valid-setting-academic-years)
544-
(reduce #(assoc %1 (:setting %2) (:setting->group %2)) {}))
534+
(reduce #(assoc %1 (:setting %2) (:setting-group %2)) {}))
545535
:NON-SEND "Other" )
546536
years (sort (distinct (map :calendar-year transitions-data)))
547537
initial-projection-year (+ 1 (last years))

src/clj/witan/send/utils.clj

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -366,3 +366,8 @@
366366

367367
(defn projection->transitions [[state sum]]
368368
(repeat sum (projection-state-to-map state)))
369+
370+
(defn keep-duplicates [seq]
371+
(for [[id freq] (frequencies seq)
372+
:when (> freq 1)]
373+
id))

src/cljc/witan/send/params.cljc

Lines changed: 20 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -247,10 +247,10 @@
247247
{} valid-states)))
248248

249249
#_(defn beta-params-movers [valid-states transitions]
250-
(weighted-beta-params valid-states transitions (some-fn joiner? leaver?) mover?))
250+
(weighted-beta-params valid-states transitions (some-fn joiner? leaver?) mover?))
251251

252252
#_(defn alpha-params-movers [valid-states valid-year-settings transitions]
253-
(weighted-alpha-params valid-states valid-year-settings transitions mover?))
253+
(weighted-alpha-params valid-states valid-year-settings transitions mover?))
254254

255255
(defn alpha-params-joiner-states [valid-states transitions]
256256
(weighted-joiner-state-alpha-params valid-states transitions))
@@ -275,9 +275,12 @@
275275
population-per-calendar-year (calculate-population-per-calendar-year population)]
276276
(weighted-joiner-beta-params valid-states joiners-per-calendar-year population-per-calendar-year)))
277277

278+
(defn any-valid-transitions? [state valid-transitions]
279+
(< 1 (count (get valid-transitions (second (s/need-setting state))))))
280+
278281
(defn beta-params-movers
279282
"calculates the rate of the likelihood of a state transitioning for an academic year"
280-
[valid-states transitions]
283+
[valid-states valid-transitions transitions]
281284
(let [academic-years (->> (map first valid-states)
282285
(distinct)
283286
(sort))
@@ -300,16 +303,18 @@
300303
{} observations)
301304
prior-per-year (continue-for-latter-ays prior-per-year academic-years)]
302305
(reduce (fn [coll [ay state]]
303-
(if-let [beta-params (merge-with +
304-
(get-in observations [ay state])
305-
(get prior-per-year ay))]
306-
(assoc coll [ay state] beta-params)
306+
(if (any-valid-transitions? state valid-transitions)
307+
(if-let [beta-params (merge-with +
308+
(get-in observations [ay state])
309+
(get prior-per-year ay))]
310+
(assoc coll [ay state] beta-params)
311+
coll)
307312
coll))
308313
{} valid-states)))
309314

310315
(defn alpha-params-movers
311316
"calculates the rate of transitions to a new state at academic year X for state Y"
312-
[valid-states transitions]
317+
[valid-states valid-transitions transitions]
313318
(let [academic-years (->> (map first valid-states)
314319
(distinct)
315320
(sort))
@@ -340,10 +345,15 @@
340345
valid-settings (s/calculate-valid-settings-for-need-ay valid-states)]
341346
(reduce (fn [coll [ay state]]
342347
(let [[need setting] (s/need-setting state)
348+
valid-trans (get valid-transitions setting)
343349
obs (get-in observations [ay state])
344350
ay-obs (get observations-per-ay ay)
345-
settings (get valid-settings [ay need])
346-
prior (->> (zipmap (vec settings) (repeat (/ 1.0 (count settings))))
351+
settings (->> (get valid-settings [ay need])
352+
vec
353+
(into valid-trans)
354+
u/keep-duplicates
355+
vec)
356+
prior (->> (zipmap settings (repeat (/ 1.0 (count settings))))
347357
(merge-with + ay-obs))
348358
prior (dissoc prior setting)
349359
total (->> (vals prior) (apply +))

src/cljc/witan/send/states.cljc

Lines changed: 6 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -52,14 +52,12 @@
5252
(update coll [ay need] (fnil conj #{}) setting)))
5353
{} valid-states))
5454

55-
(defn calculate-valid-mover-transitions [valid-states]
56-
(let [valid-need-settings (calculate-valid-settings-for-need-ay valid-states)]
57-
(mapcat (fn [[ay state']]
58-
(let [[need _] (need-setting state')
59-
settings (get valid-need-settings [(inc ay) need])]
60-
(for [setting settings]
61-
(vector ay state' (state need setting)))))
62-
valid-states)))
55+
(defn aggregate-setting->setting [setting setting->setting]
56+
(hash-map setting (mapv keyword (str/split setting->setting #","))))
57+
58+
(defn calculate-valid-mover-transitions [valid-setting-academic-years]
59+
(reduce into {} (map (fn [row] (aggregate-setting->setting (:setting row) (:setting->setting row)))
60+
valid-setting-academic-years)))
6361

6462
(defn transitions->initial-state
6563
[transitions]

test/witan/send/params_test.clj

Lines changed: 20 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -8,21 +8,21 @@
88
[witan.send.test-utils :as tu]))
99

1010
(def valid-setting-academic-years
11-
[{:setting :CC, :min-academic-year -4, :max-academic-year 0, :needs "ASD,HI,M,MLD,MSI,OTH,PD,PMLD,SEMH,SLCN,SLD,SPLD,VI"}
12-
{:setting :EO, :min-academic-year -1, :max-academic-year 14, :needs "ASD,HI,M,MLD,MSI,OTH,PD,PMLD,SEMH,SLCN,SLD,SPLD,VI"}
13-
{:setting :FEC, :min-academic-year 12, :max-academic-year 20, :needs "ASD,HI,M,MLD,MSI,OTH,PD,PMLD,SEMH,SLCN,SLD,SPLD,VI"}
14-
{:setting :IMS, :min-academic-year -3, :max-academic-year 14, :needs "ASD,HI,M,MLD,MSI,OTH,PD,PMLD,SEMH,SLCN,SLD,SPLD,VI"}
15-
{:setting :IN, :min-academic-year -4, :max-academic-year 0, :needs "ASD,HI,M,MLD,MSI,OTH,PD,PMLD,SEMH,SLCN,SLD,SPLD,VI"}
16-
{:setting :ISC, :min-academic-year 15, :max-academic-year 20, :needs "ASD,HI,M,MLD,MSI,OTH,PD,PMLD,SEMH,SLCN,SLD,SPLD,VI"}
17-
{:setting :ISCR, :min-academic-year 15, :max-academic-year 20, :needs "ASD,HI,M,MLD,MSI,OTH,PD,PMLD,SEMH,SLCN,SLD,SPLD,VI"}
18-
{:setting :ISS, :min-academic-year 0, :max-academic-year 14, :needs "ASD,HI,M,MLD,MSI,OTH,PD,PMLD,SEMH,SLCN,SLD,SPLD,VI"}
19-
{:setting :ISSR, :min-academic-year 0, :max-academic-year 14, :needs "ASD,HI,M,MLD,MSI,OTH,PD,PMLD,SEMH,SLCN,SLD,SPLD,VI"}
20-
{:setting :IT, :min-academic-year 2, :max-academic-year 15, :needs "ASD,HI,M,MLD,MSI,OTH,PD,PMLD,SEMH,SLCN,SLD,SPLD,VI"}
21-
{:setting :MMS, :min-academic-year -3, :max-academic-year 14, :needs "ASD,HI,M,MLD,MSI,OTH,PD,PMLD,SEMH,SLCN,SLD,SPLD,VI"}
22-
{:setting :MSS, :min-academic-year -3, :max-academic-year 14, :needs "ASD,HI,M,MLD,MSI,OTH,PD,PMLD,SEMH,SLCN,SLD,SPLD,VI"}
23-
{:setting :MU, :min-academic-year -1, :max-academic-year 14, :needs "ASD,HI,M,MLD,MSI,OTH,PD,PMLD,SEMH,SLCN,SLD,SPLD,VI"}
24-
{:setting :OOE, :min-academic-year 6, :max-academic-year 20, :needs "ASD,HI,M,MLD,MSI,OTH,PD,PMLD,SEMH,SLCN,SLD,SPLD,VI"}
25-
{:setting :PRU, :min-academic-year 2, :max-academic-year 14, :needs "ASD,HI,M,MLD,MSI,OTH,PD,PMLD,SEMH,SLCN,SLD,SPLD,VI"}])
11+
[{:setting :CC, :setting->setting "CC,EO,FEC,IMS,IN,ISC,ISCR,ISS,ISSR,IT,MMS,MSS,MU,OOE,PRU" :min-academic-year -4, :max-academic-year 0, :needs "ASD,HI,M,MLD,MSI,OTH,PD,PMLD,SEMH,SLCN,SLD,SPLD,VI"}
12+
{:setting :EO, :setting->setting "CC,EO,FEC,IMS,IN,ISC,ISCR,ISS,ISSR,IT,MMS,MSS,MU,OOE,PRU" :min-academic-year -1, :max-academic-year 14, :needs "ASD,HI,M,MLD,MSI,OTH,PD,PMLD,SEMH,SLCN,SLD,SPLD,VI"}
13+
{:setting :FEC, :setting->setting "CC,EO,FEC,IMS,IN,ISC,ISCR,ISS,ISSR,IT,MMS,MSS,MU,OOE,PRU" :min-academic-year 12, :max-academic-year 20, :needs "ASD,HI,M,MLD,MSI,OTH,PD,PMLD,SEMH,SLCN,SLD,SPLD,VI"}
14+
{:setting :IMS, :setting->setting "CC,EO,FEC,IMS,IN,ISC,ISCR,ISS,ISSR,IT,MMS,MSS,MU,OOE,PRU" :min-academic-year -3, :max-academic-year 14, :needs "ASD,HI,M,MLD,MSI,OTH,PD,PMLD,SEMH,SLCN,SLD,SPLD,VI"}
15+
{:setting :IN, :setting->setting "CC,EO,FEC,IMS,IN,ISC,ISCR,ISS,ISSR,IT,MMS,MSS,MU,OOE,PRU" :min-academic-year -4, :max-academic-year 0, :needs "ASD,HI,M,MLD,MSI,OTH,PD,PMLD,SEMH,SLCN,SLD,SPLD,VI"}
16+
{:setting :ISC, :setting->setting "CC,EO,FEC,IMS,IN,ISC,ISCR,ISS,ISSR,IT,MMS,MSS,MU,OOE,PRU" :min-academic-year 15, :max-academic-year 20, :needs "ASD,HI,M,MLD,MSI,OTH,PD,PMLD,SEMH,SLCN,SLD,SPLD,VI"}
17+
{:setting :ISCR, :setting->setting "CC,EO,FEC,IMS,IN,ISC,ISCR,ISS,ISSR,IT,MMS,MSS,MU,OOE,PRU" :min-academic-year 15, :max-academic-year 20, :needs "ASD,HI,M,MLD,MSI,OTH,PD,PMLD,SEMH,SLCN,SLD,SPLD,VI"}
18+
{:setting :ISS, :setting->setting "CC,EO,FEC,IMS,IN,ISC,ISCR,ISS,ISSR,IT,MMS,MSS,MU,OOE,PRU" :min-academic-year 0, :max-academic-year 14, :needs "ASD,HI,M,MLD,MSI,OTH,PD,PMLD,SEMH,SLCN,SLD,SPLD,VI"}
19+
{:setting :ISSR, :setting->setting "CC,EO,FEC,IMS,IN,ISC,ISCR,ISS,ISSR,IT,MMS,MSS,MU,OOE,PRU" :min-academic-year 0, :max-academic-year 14, :needs "ASD,HI,M,MLD,MSI,OTH,PD,PMLD,SEMH,SLCN,SLD,SPLD,VI"}
20+
{:setting :IT, :setting->setting "CC,EO,FEC,IMS,IN,ISC,ISCR,ISS,ISSR,IT,MMS,MSS,MU,OOE,PRU" :min-academic-year 2, :max-academic-year 15, :needs "ASD,HI,M,MLD,MSI,OTH,PD,PMLD,SEMH,SLCN,SLD,SPLD,VI"}
21+
{:setting :MMS, :setting->setting "CC,EO,FEC,IMS,IN,ISC,ISCR,ISS,ISSR,IT,MMS,MSS,MU,OOE,PRU" :min-academic-year -3, :max-academic-year 14, :needs "ASD,HI,M,MLD,MSI,OTH,PD,PMLD,SEMH,SLCN,SLD,SPLD,VI"}
22+
{:setting :MSS, :setting->setting "CC,EO,FEC,IMS,IN,ISC,ISCR,ISS,ISSR,IT,MMS,MSS,MU,OOE,PRU" :min-academic-year -3, :max-academic-year 14, :needs "ASD,HI,M,MLD,MSI,OTH,PD,PMLD,SEMH,SLCN,SLD,SPLD,VI"}
23+
{:setting :MU, :setting->setting "CC,EO,FEC,IMS,IN,ISC,ISCR,ISS,ISSR,IT,MMS,MSS,MU,OOE,PRU" :min-academic-year -1, :max-academic-year 14, :needs "ASD,HI,M,MLD,MSI,OTH,PD,PMLD,SEMH,SLCN,SLD,SPLD,VI"}
24+
{:setting :OOE, :setting->setting "CC,EO,FEC,IMS,IN,ISC,ISCR,ISS,ISSR,IT,MMS,MSS,MU,OOE,PRU" :min-academic-year 6, :max-academic-year 20, :needs "ASD,HI,M,MLD,MSI,OTH,PD,PMLD,SEMH,SLCN,SLD,SPLD,VI"}
25+
{:setting :PRU, :setting->setting "CC,EO,FEC,IMS,IN,ISC,ISCR,ISS,ISSR,IT,MMS,MSS,MU,OOE,PRU" :min-academic-year 2, :max-academic-year 14, :needs "ASD,HI,M,MLD,MSI,OTH,PD,PMLD,SEMH,SLCN,SLD,SPLD,VI"}])
2626

2727
(def population-dataset
2828
(tu/csv-to-dataset "data/demo/population.csv" sc/PopulationDataset))
@@ -34,6 +34,9 @@
3434
(-> valid-setting-academic-years
3535
(s/calculate-valid-states-from-setting-academic-years)))
3636

37+
(def valid-transitions
38+
(s/calculate-valid-mover-transitions valid-setting-academic-years))
39+
3740
(def valid-year-settings
3841
(s/calculate-valid-year-settings-from-setting-academic-years valid-setting-academic-years))
3942

@@ -51,7 +54,7 @@
5154

5255
(testing "Positive mover state alphas for every year with >1 setting"
5356
(let [transitions {}
54-
params (sut/alpha-params-movers valid-states transitions)]
57+
params (sut/alpha-params-movers valid-states valid-transitions transitions)]
5558
(is (empty? (remove (fn [[academic-year state]]
5659
(let [alphas (get params [academic-year state])]
5760
(and (pos? (count alphas))
@@ -71,7 +74,7 @@
7174

7275
(testing "Positive mover beta params for every valid state"
7376
(let [transitions {}
74-
params (sut/beta-params-movers valid-states transitions)]
77+
params (sut/beta-params-movers valid-states valid-transitions transitions)]
7578
(is (every? (fn [[_ betas]]
7679
(and (pos? (:alpha betas))
7780
(pos? (:beta betas))))

0 commit comments

Comments
 (0)