Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

SDE #1884

Merged
merged 96 commits into from Oct 5, 2022
Merged

SDE #1884

merged 96 commits into from Oct 5, 2022

Conversation

boeschf
Copy link
Contributor

@boeschf boeschf commented Apr 15, 2022

Stochastic Differential Equations

Introduces sources of white noise to arbor (and nmodl) and addresses issues #1643 and #1655. Both point and density mechanisms may now use white noise as part of the state updates, turning the ODEs effectively into SDEs. The noise sources are provided per connection end point (point mech.) or control volume (density mech.) and are uncorrelated (spatially, temporally and across different mechanism instantiations)

Background

SDE's can be described in their differential form (informal engineering notation) as

$$ X^\prime = f\left(X(t),t\right) + \sum_i g_i\left( X(t), t\right) W_i(t)$$

with (standard) white noises $W_i$. More accurately, this can be expressed as integral equation,

$$ X(t+s) = X(t) + \int_{t}^{t+s} f\left( X(\tau), \tau\right) d\tau + \sum_i \int_t^{t+s} g_i\left( X(\tau), \tau \right)dB_i(\tau)$$

with (standard) Brownian motions $B_i$ (also referred to as Wiener processes). The white noise $W_i$ are stationary, Gaussian processes with $μ=0$ and $σ^2=1$. The stochastic integrals are interpreted in the Itô sense.

Systems of stochastic differential equations can be solved using a few different methods. We chose the Euler-Maruyama method here (based on Taylor-Itô expansion) for its simplicity and generality. The main drawback of this approach is accuracy (first order only). While there exist other methods (analytical and numerical), these are not general and not straight-forward to extend to systems of SDEs. Future work could be done to investigate if applying more accurate methods in corner-cases makes sense.

nmodl

The white noise sources can be defined in the model files using a WHITE_NOISE block:

   WHITE_NOISE {
       a b 
       c
   }

Arbitrary white noise variables can be declared afterwards (a, b, c in the example above).

If the state is updated by involving at least one of the declared white noise variables the system is considered to be stochastic:

   DERIVATIVE state {
       s' = f + g*a
   }

The solver method must then accordingly set to stochastic:

   BREAKPOINT {
       SOLVE state METHOD stochastic
   }

Simulation

The simulation class takes an optional seed argument at construction which sets the global seed for the random number generator. The default value is 0. In order to make construction of the simulation object more convenient, a builder class is added.

modcc

modcc gains a new solver method for Euler-Maruyama. The implementation required extension of the expressions and tokens to handle white noise and seed values. The code generation in the cpu and gpu printers is pretty straight-forward and akin to looking up state variables.

ABI

ABI changes by extending the struct arb_mechanism_ppack to accomodate for the random numbers. This requires a bump of the ABI version.

Arbor

The main changes in Arbor concern the backend implementations of the shared_state. Generally, we want to generate a batch of new random numbers before the advance_state procedure is invoked. As those random numbers need to be independent, we use the counter-based pseudo random number generators from Random123. We can prime the counters and keys with the following identifiers to guarantee independent random values:

  • global seed value (per simulation)
  • global cell id
  • per-cell connection end point index (point mech.) or per-cell control volume index (density mech)
  • mechanism id
  • per-mechanism variable identifier
  • per-mechanism simulation time (in the form of an integral counter)

The random123 library usually provides more than one independent random values per input (4 in our case as of now). In order to make use of these values, the backend caches the number and updates the numbers only every 4 steps. Cpu, cpu-vectorized and gpu variants should produce the same random values. In order to guarantee correct random numbers, synapse collapsing is disabled.

@brenthuisman

This comment was marked as resolved.

@jlubo

This comment was marked as resolved.

@boeschf boeschf marked this pull request as ready for review July 14, 2022 16:21
arch_gpu.data_[i],
arch_cpu.data_[i],
128,
4*std::numeric_limits<arb_value_type>::epsilon()
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So, the GPU pRNG is within $4\epsilon$ of the CPU version?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yes (absolute tolerance)

test/unit/test_sde.cpp Outdated Show resolved Hide resolved
const auto num_rv = store.random_numbers_d_[0].size();
const auto width_padded = store.value_width_padded;
const auto width = m.ppack_.width;
#ifdef ARB_ARBOR_NO_GPU_RAND
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is my only painpoint left:
Could we make it so that shared state doesn't need to know about this flag? Currently it's used in three
four different places, which might cause issues down the road. How about delegating these things to the
pRNG impl

  • writing the values (here)
  • setting gid/idx (ll 358)
  • allocating (ll 396)/ filling (ll 477) the indices

via the use of an appropriate interface. In said interface we can make the decision once.

Copy link
Contributor

@thorstenhater thorstenhater left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🎉 👍 🎉
A massive effort and a very cool addition to Arbor! Thanks!

@boeschf boeschf merged commit c976c66 into arbor-sim:master Oct 5, 2022
brenthuisman added a commit that referenced this pull request Nov 15, 2022
- Correct `.gitmodules`
- Update all git submodules to latest released versions, except google-benchmark
- Have CHANGELOG
- Breaking changes since v0.7:
  - A change in API: `arbor.cable_cell` has the labels and decor arguments swapped. I.e.: `(tree, labels, decor)`
-> `(tree, decor, label)`. Labels are now optional.
  - Mechanism ABI version is bumped to 0.3.1. #1884
  - Remove the `generate-catalogue` script.  `modcc` accepts now a list of NMODL files and is able to spit out a catalogue.cpp file
  - Rename spike detector -> threshold detector
  - Remove access to time `t` in NMODL.
- Major dependency version bumps:
  - GCC: 9 and up
  - CUDA: 11 and up
  - Clang: 10 and up
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

5 participants