Ensure determinism

While our models are normally conceptualized as being stochastic, sometimes strict determinism is necessary. This is particularly true if you want to precisely replicate a run or for pedantic debugging. Strict determinism is actually quite difficult given the design decisions in jACT-R. The loose coupling of modules and asynchronous threading model make it possibly in only a very narrow sense. That having been said, the architecture has been designed to mitigate those problems as much as possible.

If you use the default modules, in a single model configuration, with programmatic perception, you can get deterministic runs. The moment another thread is introduced that manipulates the model at all, small non-determisms can arise. While jACT-R supports those manipulations in a thread-safe manner, it does so by queueing them up to be run on the model thread itself. This is a normal pattern for creating a model lock/semaphor when interfacing an experiment. In practice this manifests itself as a slight difference in the number of cycles executed between runs. Generally, 1 cycle over 6000, or 50ms over 5 minutes, simulated. In practice, this difference is usually insignificant.

If that is too much uncertainty, there are other solutions, both will kill your performance. You can either subsume the second thread within the model thread, or introduce specific synchronization points in model & experiment. In practice, only the second is feasible. The simplest mechanism is to synchronize the experiment with the stop of the model's cycle.  This forces the two threads into lock-step, removing the non-determism of the underlying thread timing.

jACT-R's default modules, when run in a single-threaded configuration, do run deterministically. The architecture tries to minimize non-determinisms introduced from other sources, but is ultimately designed to be performant under stochastic conditions.