Results and Simulations

Why OneDimGrid objects?

The model generator requires a OneDimGrid objects from AdaptiveDensityApproximation.jl (see Models). For this, the actual weights of the grid do not matter. The model generator returns a new parameter array and a corresponding model function. It is possible to set parameter values in this array and to simulate dose-response curves with the corresponding, returned model function.

But AdaptiveDensityApproximation.jl offers additional methods, e.g. the name-giving adaptive approximation of densities or additional density conversion / analysis tools. For this reason, internal methods use OneDimGrid objects and construct the model functions from scratch for the calculation of dose-response curves. This is, among others, the reason why most functions (e.g. the convenience functions of AntibodyMethodsDoseResponseConvenience) return or expect those grids.

Tip

Although not necessary for all analyses, it is highly recommended to use the OneDimGrid workflow, as advanced methods of this package will expect grids.

Simulating the $K_\tau$-density

To simulate a dose-response curve with a binding model, a density $g(K_\tau)$ needs to be defined. This can be done with any Julia function, e.g. a probability density function:

using AntibodyMethodsDoseResponseConvenience
using Distributions

p(x) = pdf(Normal(1e-5,3e-6),x)
plot(p, xaxis = :log, xlims = [1e-10,1e-2], fill = 0, label = "density")
Example block output

The plot function can be used without importing Plots.jl here, as AntibodyMethodsDoseResponseConvenience does this in the background automatically.

Next, a OndDimGrid needs to be defined to approximate the density:

grid = create_grid(LogRange(1e-10,1e-2,50))
approximate_density!(grid, p, volume_normalization = true)
AdaptiveDensityApproximation.OneDimGrid{Dict{String, AdaptiveDensityApproximation.OneDimBlock}}(Dict{String, AdaptiveDensityApproximation.OneDimBlock}("Py0BNchFQf" => AdaptiveDensityApproximation.OneDimBlock("Py0BNchFQf", AdaptiveDensityApproximation.Interval{Float64, Float64}(5.428675439323859e-6, 7.906043210907702e-6), ["jfTeBXDoEg", "8z4dGbONb5"], 0.17774921175278285), "Q8fAIep6Jc" => AdaptiveDensityApproximation.OneDimBlock("Q8fAIep6Jc", AdaptiveDensityApproximation.Interval{Float64, Float64}(3.088843596477472e-10, 4.4984326689694345e-10), ["AYTiIryE1y", "cmxkzIZuRR"], 7.249653871503817e-8), "y2TGbJxgAu" => AdaptiveDensityApproximation.OneDimBlock("y2TGbJxgAu", AdaptiveDensityApproximation.Interval{Float64, Float64}(6.250551925273976e-9, 9.102981779915227e-9), ["AQyHI5Vwg7", "0xFgvaA3oG"], 1.4789710064812532e-6), "8z4dGbONb5" => AdaptiveDensityApproximation.OneDimBlock("8z4dGbONb5", AdaptiveDensityApproximation.Interval{Float64, Float64}(7.906043210907702e-6, 1.1513953993264481e-5), ["Py0BNchFQf", "4osC0nsuWv"], 0.47754627394444393), "t6ewiPuZZp" => AdaptiveDensityApproximation.OneDimBlock("t6ewiPuZZp", AdaptiveDensityApproximation.Interval{Float64, Float64}(7.543120063354622e-5, 0.00010985411419875583), ["ZIIh34wveR", "n8zp8Ukqm8"], 7.49239388000171e-165), "1biI23x2xg" => AdaptiveDensityApproximation.OneDimBlock("1biI23x2xg", AdaptiveDensityApproximation.Interval{Float64, Float64}(3.5564803062231284e-5, 5.1794746792312233e-5), ["LUpqOc7Q4V", "ZIIh34wveR"], 9.239703011997424e-28), "n4okjrhlJN" => AdaptiveDensityApproximation.OneDimBlock("n4okjrhlJN", AdaptiveDensityApproximation.Interval{Float64, Float64}(1.6768329368110066e-5, 2.4420530945486548e-5), ["4osC0nsuWv", "LUpqOc7Q4V"], 0.0019927865651641007), "AQyHI5Vwg7" => AdaptiveDensityApproximation.OneDimBlock("AQyHI5Vwg7", AdaptiveDensityApproximation.Interval{Float64, Float64}(4.291934260128778e-9, 6.250551925273976e-9), ["rQqe6GYIwS", "y2TGbJxgAu"], 1.0128247873519783e-6), "UeJO6UXx60" => AdaptiveDensityApproximation.OneDimBlock("UeJO6UXx60", AdaptiveDensityApproximation.Interval{Float64, Float64}(0.0004941713361323844, 0.0007196856730011522), ["JgJ6VHiAEr", "yxnatgV1ZU"], 0.0), "m5iatcC06R" => AdaptiveDensityApproximation.OneDimBlock("m5iatcC06R", AdaptiveDensityApproximation.Interval{Float64, Float64}(2.8117686979742307e-8, 4.094915062380419e-8), ["ooC6C4AHo7", "lAgRxvt15w"], 6.854143539396169e-6)…))
volume_normalization = true

Without volume_normalization = true (the default), the density function itself is approximated (e.g. by using the function values at the centers of the respective intervals). The volume normalization approximates the area under the curve by using function value × interval length, which in this case corresponds to the number of epitopes with $K_\tau$ in the respective interval. This is the correct choice for simulations, as the weights of grids are assumed ot be the numbers of epitopes and not the density values.

To inspect the grid, the plotting recipe from AdaptiveDensityApproximationRecipes.jl can be used (as described in Models):

using AdaptiveDensityApproximationRecipes
plot(grid, xaxis = :log)
Example block output
y-axis scale

Since the grid approximates the area under the density curve (weights correspond to the height of the bars), the scale of the $y$-axis is different, compared to the density plot.

Simulating the dose-response curve

With the OneDimeGrid object grid, a dose-response can be simulated, using DoseResponseResult:

concentrations = LogRange(1e-8,1e-2,16)

simulation_result = DoseResponseResult(grid,concentrations;
			model = accumulation_model,
			offset = 0
		)

scatter(simulation_result, xaxis = :log)
Example block output
DoseResponseResult

DoseResponseResult objects are used to construct and store dose-response curves, resulting from grids. This can be either because a grid was used to simulate a $K_\tau$-density or because the result of a model-fit was stored in a grid. The package AntibodyMethodsDoseResponseRecipes.jl provides a plotting recipe for DoseResponseResult objects.

filter_zeros

The same filter_zeros keyword that is used for FittingData plots can also be used for DoseResponseResult plots (see Measurement Data plotting).

Importing fitting results

Instead of simulating a dose-response curve, the OneDimGrid and DoseResponseResult types can also be used to create dose-response curves from a fitting result. Since no data has been fitted yet, we construct a result from the weights of the simulation grid above:

parameters = export_weights(grid)
push!(parameters, 0.2) # Add offset parameter
50-element Vector{Float64}:
 2.346375744346489e-8
 3.417353548949335e-8
 4.977309020637278e-8
 7.249653871503817e-8
 1.0560053397755286e-7
 1.5383426307070923e-7
 2.2412770711543273e-7
 3.2660201444611645e-7
 4.76058019338558e-7
 6.941803541492311e-7
 ⋮
 0.0
 0.0
 0.0
 0.0
 0.0
 0.0
 0.0
 0.0
 0.2

We assume now, that the gird was used to obtain the model function and that the parameters are the result of the model fit. In this case, the offset keyword was used. To import the result into the grid, the import_weights! functions can be used:

import_weights!(grid, parameters[1:end-1])
AdaptiveDensityApproximation.OneDimGrid{Dict{String, AdaptiveDensityApproximation.OneDimBlock}}(Dict{String, AdaptiveDensityApproximation.OneDimBlock}("Py0BNchFQf" => AdaptiveDensityApproximation.OneDimBlock("Py0BNchFQf", AdaptiveDensityApproximation.Interval{Float64, Float64}(5.428675439323859e-6, 7.906043210907702e-6), ["jfTeBXDoEg", "8z4dGbONb5"], 0.17774921175278285), "Q8fAIep6Jc" => AdaptiveDensityApproximation.OneDimBlock("Q8fAIep6Jc", AdaptiveDensityApproximation.Interval{Float64, Float64}(3.088843596477472e-10, 4.4984326689694345e-10), ["AYTiIryE1y", "cmxkzIZuRR"], 7.249653871503817e-8), "y2TGbJxgAu" => AdaptiveDensityApproximation.OneDimBlock("y2TGbJxgAu", AdaptiveDensityApproximation.Interval{Float64, Float64}(6.250551925273976e-9, 9.102981779915227e-9), ["AQyHI5Vwg7", "0xFgvaA3oG"], 1.4789710064812532e-6), "8z4dGbONb5" => AdaptiveDensityApproximation.OneDimBlock("8z4dGbONb5", AdaptiveDensityApproximation.Interval{Float64, Float64}(7.906043210907702e-6, 1.1513953993264481e-5), ["Py0BNchFQf", "4osC0nsuWv"], 0.47754627394444393), "t6ewiPuZZp" => AdaptiveDensityApproximation.OneDimBlock("t6ewiPuZZp", AdaptiveDensityApproximation.Interval{Float64, Float64}(7.543120063354622e-5, 0.00010985411419875583), ["ZIIh34wveR", "n8zp8Ukqm8"], 7.49239388000171e-165), "1biI23x2xg" => AdaptiveDensityApproximation.OneDimBlock("1biI23x2xg", AdaptiveDensityApproximation.Interval{Float64, Float64}(3.5564803062231284e-5, 5.1794746792312233e-5), ["LUpqOc7Q4V", "ZIIh34wveR"], 9.239703011997424e-28), "n4okjrhlJN" => AdaptiveDensityApproximation.OneDimBlock("n4okjrhlJN", AdaptiveDensityApproximation.Interval{Float64, Float64}(1.6768329368110066e-5, 2.4420530945486548e-5), ["4osC0nsuWv", "LUpqOc7Q4V"], 0.0019927865651641007), "AQyHI5Vwg7" => AdaptiveDensityApproximation.OneDimBlock("AQyHI5Vwg7", AdaptiveDensityApproximation.Interval{Float64, Float64}(4.291934260128778e-9, 6.250551925273976e-9), ["rQqe6GYIwS", "y2TGbJxgAu"], 1.0128247873519783e-6), "UeJO6UXx60" => AdaptiveDensityApproximation.OneDimBlock("UeJO6UXx60", AdaptiveDensityApproximation.Interval{Float64, Float64}(0.0004941713361323844, 0.0007196856730011522), ["JgJ6VHiAEr", "yxnatgV1ZU"], 0.0), "m5iatcC06R" => AdaptiveDensityApproximation.OneDimBlock("m5iatcC06R", AdaptiveDensityApproximation.Interval{Float64, Float64}(2.8117686979742307e-8, 4.094915062380419e-8), ["ooC6C4AHo7", "lAgRxvt15w"], 6.854143539396169e-6)…))
Why end-1 ?

Since the offset keyword was used for the creation of model functions (in our scenario), the parameters array has one additional element at the end, the offset, which does not belong to the grid. If no offset is used, all parameters should be imported:

import_weights!(grid, parameters)

In fact, using the wrong selection of parameters usually leads to a DimensionMismatch error, as the number of parameters and the number of intervals in the grid do not match.

julia> import_weights!(grid, parameters) # Wrong parameter selection should fail.ERROR: DimensionMismatch: The number of blocks in the grid and the number of weights to be imported do not match

After importing the parameters into the grid, the dose-response curve can be constructed as before (for any concentrations, not only the data concentrations):

concentrations = LogRange(1e-8,1e-2,16)

result = DoseResponseResult(grid,concentrations;
			model = accumulation_model,
			offset = parameters[end]
		)

scatter(result, xaxis = :log, ylims = [0,1.4])
Example block output

Observe the offset = parameters[end] part at the end of the DoseResponseResult call. In our scenario, the parameters array contains the offset as last element.