|
1 | 1 | """ |
2 | | - AbstractPBMApplicator(θP::AbstractVector, θMs::AbstractMatrix, xP::AbstractMatrix) |
3 | | -
|
4 | 2 | Abstraction of applying a process-based model with |
5 | | -global parameters, `x`, site-specific parameters, `θMs` (sites in columns), |
| 3 | +global parameters, `θP`, site-specific parameters, `θMs` (sites in columns), |
6 | 4 | and site-specific model drivers, `xP` (sites in columns), |
7 | 5 | It returns a matrix of predictions sites in columns. |
8 | 6 |
|
9 | | -Specific implementations need to implement function `apply_model(app, θP, θMs, xP)`. |
| 7 | +Specific implementations need to provide function `apply_model(app, θP, θMs, xP)`. |
| 8 | +where |
| 9 | +- `θsP` and `θsMs` are shaped according to the output of `generate_ζ`, i.e. |
| 10 | +`(n_site_pred x n_par x n_MC)`. |
| 11 | +- Results are of shape `(n_obs x n_site_pred x n_MC)`. |
| 12 | +
|
| 13 | +They may also provide function `apply_model(app, θP, θMs, xP)` for a sample |
| 14 | +of parameters, i.e. where an additional dimension is added to both `θP` and `θMs`. |
| 15 | +However, there is a default implementation that mapreduces across these dimensions. |
| 16 | +
|
10 | 17 | Provided are implementations |
11 | | -- `NullPBMApplicator`: returning its input `θMs` for testing |
12 | 18 | - `PBMSiteApplicator`: based on a function that computes predictions per site |
13 | 19 | - `PBMPopulationApplicator`: based on a function that computes predictions for entire population |
| 20 | +- `NullPBMApplicator`: returning its input `θMs` for testing |
| 21 | +- `PlainPBMApplicator`: based on a function that takes the same arguments as `apply_model` |
14 | 22 | """ |
15 | 23 | abstract type AbstractPBMApplicator end |
16 | 24 |
|
17 | 25 | # function apply_model end # already defined in ModelApplicator.jl for ML model |
18 | 26 |
|
19 | | -function (app::AbstractPBMApplicator)(θP::AbstractVector, θMs::AbstractMatrix, xP::AbstractMatrix) |
| 27 | +function (app::AbstractPBMApplicator)(θP::AbstractArray, θMs::AbstractArray, xP::AbstractMatrix) |
20 | 28 | apply_model(app, θP, θMs, xP) |
21 | 29 | end |
22 | 30 |
|
| 31 | +""" |
| 32 | + apply_model(app::AbstractPBMApplicator, θsP::AbstractVector, θsMs::AbstractMatrix, xP::AbstractMatrix) |
| 33 | + apply_model(app::AbstractPBMApplicator, θsP::AbstractMatrix, θsMs::AbstractArray{ET,3}, xP) |
| 34 | +
|
| 35 | +The first variant calls the PBM for one batch of sites. |
| 36 | +
|
| 37 | +The second variant calls the PBM for a sample of batches, and stack results. |
| 38 | +The default implementation mapreduces the last dimension of `θsP` and θ`sMs` calling the |
| 39 | +first variant of `apply_model` for each sample. |
| 40 | +""" |
| 41 | +# docu in struct |
| 42 | +function apply_model(app::AbstractPBMApplicator, θsP::AbstractMatrix, θsMs::AbstractArray{ET,3}, xP) where ET |
| 43 | + # stack does not work on GPU, see specialized method for GPUArrays below |
| 44 | + y_pred = stack( |
| 45 | + map(eachcol(CA.getdata(θsP)), eachslice(CA.getdata(θsMs), dims=3)) do θP, θMs |
| 46 | + y_global, y_pred_i = app(θP, θMs, xP) |
| 47 | + y_pred_i |
| 48 | + end) |
| 49 | +end |
| 50 | +function apply_model(app::AbstractPBMApplicator, θsP::GPUArraysCore.AbstractGPUMatrix, θsMs::GPUArraysCore.AbstractGPUArray{ET,3}, xP) where ET |
| 51 | + # stack does not work on GPU, need to resort to slower mapreduce |
| 52 | + # for type stability, apply f at first iterate to supply init to mapreduce |
| 53 | + P1, Pit = Iterators.peel(eachcol(CA.getdata(θsP))); |
| 54 | + Ms1, Msit = Iterators.peel(eachslice(CA.getdata(θsMs), dims=3)); |
| 55 | + y1 = apply_model(app, P1, Ms1, xP)[2] |
| 56 | + y1a = reshape(y1, size(y1)..., 1) # add one dimension |
| 57 | + y_pred = mapreduce((a,b) -> cat(a,b; dims=3), Pit, Msit; init=y1a) do θP, θMs |
| 58 | + y_global, y_pred_i = app(θP, θMs, xP) |
| 59 | + y_pred_i |
| 60 | + end |
| 61 | +end |
| 62 | + |
| 63 | + |
| 64 | + |
23 | 65 |
|
24 | 66 | """ |
25 | 67 | NullPBMApplicator() |
@@ -119,8 +161,8 @@ struct PBMPopulationApplicator{MFT, IPT, IT, IXT, F} <: AbstractPBMApplicator |
119 | 161 | int_xP::IXT |
120 | 162 | end |
121 | 163 |
|
122 | | -# let fmap not descend into isP |
123 | | -# @functor PBMPopulationApplicator (θFixm, ) |
| 164 | +# let fmap not descend into isP, because indexing with isP on cpu is faster |
| 165 | +@functor PBMPopulationApplicator (θFixm, ) |
124 | 166 |
|
125 | 167 | """ |
126 | 168 | PBMPopulationApplicator(fθpop, n_batch; θP, θM, θFix, xPvec) |
@@ -167,7 +209,13 @@ function apply_model(app::PBMPopulationApplicator, θP::AbstractVector, θMs::Ab |
167 | 209 | "or compute PBM on CPU.") |
168 | 210 | end |
169 | 211 | # repeat θP and concatenate with |
| 212 | + # Main.@infiltrate_main |
| 213 | + # repeat is 2x slower for Vector and 100 times slower (with allocation) on GPU |
| 214 | + # app.isP on CPU is slightly faster than app.isP on GPU |
| 215 | + #@benchmark CA.getdata(θP[app.isP]) |
| 216 | + #@benchmark CA.getdata(repeat(θP', size(θMs,1))) |
170 | 217 | local θ = hcat(CA.getdata(θP[app.isP]), CA.getdata(θMs), app.θFixm) |
| 218 | + #local θ = hcat(CA.getdata(repeat(θP', size(θMs,1))), CA.getdata(θMs), app.θFixm) |
171 | 219 | local θc = app.intθ(CA.getdata(θ)) |
172 | 220 | local xPc = app.int_xP(CA.getdata(xP)) |
173 | 221 | local pred_sites = app.fθpop(θc, xPc) |
|
0 commit comments