The decision boundaries sit at plus and minus B(t), so the separation
between them is 2 B(t). All three components below name the separation
bs, as wiener() does, and set B(0) = bs / 2, so a boundary
separation estimated here is directly comparable with one from the
analytic family.
Details
gddm_bound_constant()B(t) = bs / 2. Free parameterbs, log link.gddm_bound_exponential()B(t) = (bs / 2) exp(-t / tau). Free parametersbsand the time constanttau, both log link. A largetauis a bound that barely collapses, which is why it is where the fit starts. Note PyDDM parameterizes the same bound by a rate, the reciprocal oftau.gddm_bound_linear()B(t) = (bs / 2) (1 - kappa t / t_max), wherekappais the fraction of the bound lost by the end of the modeled window. Free parametersbs(log link) andkappa(logit link). The usual spelling of a linear collapse clips the bound at zero, which is a comparison against a parameter and cannot be taped; bounding the fraction below one instead keeps the boundary strictly positive across the window by construction, with no clipping.
Writing your own
A bound term is the value of gddm_bound_term(). Its fn is called on
the tape as fn(t, p, ctl) for a plain number t, and returns a list
with B, the boundary at that time, and dlogB, its logarithmic
derivative B'(t) / B(t). The solver needs the second because the
change of variable that pins the walls contributes a -y B'(t)/B(t)
term to the drift; supplying it rather than differencing B keeps the
rescaled drift exact.