Model components¶
Drift rate¶
-
class
pyddm.models.drift.
Drift
(**kwargs)[source]¶ Bases:
pyddm.models.base.Dependence
Subclass this to specify how drift rate varies with position and time.
This abstract class provides the methods which define a dependence of drift on x and t. To subclass it, implement get_drift. Since it inherits from Dependence, subclasses must also assign a name and required_parameters (see documentation for Dependence.)
-
get_drift
(t, x, conditions, **kwargs)[source]¶ Calculate the instantaneous drift rate.
This function must be redefined in subclasses.
It may take several arguments:
- t - The time at which drift should be calculated
- x - The particle position (or 1-dimensional NDArray of particle positions) at which drift should be calculated
- conditions - A dictionary describing the task conditions
It should return a number or an NDArray (the same as x) indicating the drift rate at that particular time, position(s), and task conditions.
Definitions of this method in subclasses should only have arguments for needed variables and should always be followed by “**kwargs”. For example, if the function does not depend on t or x but does depend on task conditions, this should be:
def get_drift(self, conditions, **kwargs):Of course, the function would still work properly if x were included as an argument, but this convention allows PyDDM to automatically select the best simulation methods for the model.
If a function depends on x, it should return a scalar if x is a scalar, or an NDArray of the same size as x if x is an NDArray. If the function does not depend on x, it should return a scalar. (The purpose of this is a dramatic speed increase by using numpy vectorization.)
-
get_flux
(x_bound, t, dx, dt, conditions, **kwargs)[source]¶ The drift component of flux across the boundary at position x_bound at time t.
Flux here is essentially the amount of the mass of the PDF that is past the boundary point x_bound.
There is generally no need to redefine this method in subclasses.
-
get_matrix
(x, t, dx, dt, conditions, implicit=False, **kwargs)[source]¶ The drift component of the implicit method diffusion matrix across the domain x at time t.
x should be a length N ndarray of all positions in the grid. t should be the time in seconds at which to calculate drift. dt and dx should be the simulations timestep and grid step conditions should be the conditions at which to calculate drift
Returns a sparse NxN matrix as a PyDDM TriDiagMatrix object.
There is generally no need to redefine this method in subclasses.
-
-
class
pyddm.models.drift.
DriftConstant
(**kwargs)[source]¶ Bases:
pyddm.models.drift.Drift
Drift dependence: drift rate is constant throughout the simulation.
Only take one parameter: drift, the constant drift rate.
Note that this is a special case of DriftLinear.
Example usage:
drift = DriftConstant(drift=0.3)
-
class
pyddm.models.drift.
DriftLinear
(**kwargs)[source]¶ Bases:
pyddm.models.drift.Drift
Drift dependence: drift rate varies linearly with position and time.
Take three parameters:
- drift - The starting drift rate
- x - The coefficient by which drift varies with x
- t - The coefficient by which drift varies with t
Example usage:
drift = DriftLinear(drift=0.5, t=0, x=-1) # Leaky integratordrift = DriftLinear(drift=0.8, t=0, x=0.4) # Unstable integratordrift = DriftLinear(drift=0, t=1, x=0.4) # Urgency function
Noise¶
-
class
pyddm.models.noise.
Noise
(**kwargs)[source]¶ Bases:
pyddm.models.base.Dependence
Subclass this to specify how noise level varies with position and time.
This abstract class provides the methods which define a dependence of noise on x and t. To subclass it, implement get_noise. Since it inherits from Dependence, subclasses must also assign a name and required_parameters (see documentation for Dependence.)
-
get_flux
(x_bound, t, dx, dt, conditions, **kwargs)[source]¶ The diffusion component of flux across the boundary at position x_bound at time t.
Flux here is essentially the amount of the mass of the PDF that is past the boundary point x_bound at time t (in seconds).
Note that under the central scheme we want to use x at half-grid from the boundary. This is however cleaner and justifiable using forward/backward scheme.
There is generally no need to redefine this method in subclasses.
-
get_matrix
(x, t, dx, dt, conditions, implicit=False, **kwargs)[source]¶ The diffusion component of the implicit method diffusion matrix across the domain x at time t.
x should be a length N ndarray of all positions in the grid. t should be the time in seconds at which to calculate noise. dt and dx should be the simulations timestep and grid step conditions should be the conditions at which to calculate noise
Returns a sparse NxN matrix as a PyDDM TriDiagMatrix object.
There is generally no need to redefine this method in subclasses.
-
get_noise
(conditions, **kwargs)[source]¶ Calculate the instantaneous noise (standard deviation of noise).
This function must be redefined in subclasses.
It may take several arguments:
- t - The time at which noise should be calculated
- x - The particle position (or 1-dimensional NDArray of particle positions) at which noise should be calculated
- conditions - A dictionary describing the task conditions
It should return a number or an NDArray (the same as x) indicating the standard deviation of the noise at that particular time, position(s), and task conditions.
Definitions of this method in subclasses should only have arguments for needed variables and should always be followed by “**kwargs”. For example, if the function does not depend on t or x but does depend on task conditions, this should be:
def get_noise(self, conditions, **kwargs):Of course, the function would still work properly if x were included as an argument, but this convention allows PyDDM to automatically select the best simulation methods for the model.
If a function depends on x, it should return a scalar if x is a scalar, or an NDArray of the same size as x if x is an NDArray. If the function does not depend on x, it should return a scalar. (The purpose of this is a dramatic speed increase by using numpy vectorization.)
-
-
class
pyddm.models.noise.
NoiseConstant
(**kwargs)[source]¶ Bases:
pyddm.models.noise.Noise
Noise level is constant over time.
Only take one parameter: noise, the standard deviation of the noise.
Note that this is a special case of NoiseLinear.
Example usage:
noise = NoiseConstant(noise=0.5)
-
class
pyddm.models.noise.
NoiseLinear
(**kwargs)[source]¶ Bases:
pyddm.models.noise.Noise
Noise level varies linearly with position and time.
Take three parameters:
- noise - The inital noise standard deviation
- x - The coefficient by which noise standard deviation varies with x
- t - The coefficient by which noise standard deviation varies with t
Example usage:
noise = NoiseLinear(noise=0.5, x=0, t=.1) # Noise increases over time
Bounds¶
-
class
pyddm.models.bound.
Bound
(**kwargs)[source]¶ Bases:
pyddm.models.base.Dependence
Subclass this to specify how bounds vary with time.
This abstract class provides the methods which define a dependence of the bounds on t. To subclass it, implement get_bound. All bounds must be symmetric, so the lower bound is -get_bound.
Also, since it inherits from Dependence, subclasses must also assign a name and required_parameters (see documentation for Dependence.)
-
get_bound
(t, conditions, **kwargs)[source]¶ Calculate the bounds which particles cross to determine response time.
This function must be redefined in subclasses.
It may take up to two arguments:
- t - The time at which bound should be calculated
- conditions - A dictionary describing the task conditions
It should return a non-negative number indicating the upper bound at that particular time, and task conditions. The lower bound is taken to be the negative of the upper bound.
Definitions of this method in subclasses should only have arguments for needed variables and should always be followed by “**kwargs”. For example, if the function does not depend on task conditions but does depend on time, this should be:
def get_bound(self, t, **kwargs):Of course, the function would still work properly if conditions were included as an argument, but this convention allows PyDDM to automatically select the best simulation methods for the model.
-
-
class
pyddm.models.bound.
BoundConstant
(**kwargs)[source]¶ Bases:
pyddm.models.bound.Bound
Bound dependence: bound is constant throuhgout the simulation.
Takes only one parameter: B, the constant bound.
Example usage:
bound = BoundConstant(B=1.5) # Bound at 1.5 and -1.5
-
class
pyddm.models.bound.
BoundCollapsingLinear
(**kwargs)[source]¶ Bases:
pyddm.models.bound.Bound
Bound dependence: bound collapses linearly over time.
Takes two parameters:
- B - the bound at time t = 0.
- t - the slope, i.e. the coefficient of time, should be greater than zero.
Example usage:
bound = BoundCollapsingLinear(B=1, t=.5) # Collapsing at .5 units per second
-
class
pyddm.models.bound.
BoundCollapsingExponential
(**kwargs)[source]¶ Bases:
pyddm.models.bound.Bound
Bound dependence: bound collapses exponentially over time.
Takes two parameters:
- B - the bound at time t = 0.
- tau - one divided by the time constant for the collapse. 0 gives constant bounds.
Example usage:
bound = BoundCollapsingExponential(B=1, tau=2.1) # Collapsing with time constant 1/2.1
Initial Conditions (IC)¶
-
class
pyddm.models.ic.
InitialCondition
(**kwargs)[source]¶ Bases:
pyddm.models.base.Dependence
Subclass this to compute the initial conditions of the simulation.
This abstract class describes initial PDF at the beginning of a simulation. To subclass it, implement get_IC(x).
Also, since it inherits from Dependence, subclasses must also assign a name and required_parameters (see documentation for Dependence.)
-
class
pyddm.models.ic.
ICPointSourceCenter
(**kwargs)[source]¶ Bases:
pyddm.models.ic.InitialCondition
Initial condition: a dirac delta function in the center of the domain.
Example usage:
ic = ICPointSourceCenter()
-
class
pyddm.models.ic.
ICPoint
(**kwargs)[source]¶ Bases:
pyddm.models.ic.InitialCondition
Initial condition: any point.
Example usage:
ic = ICPoint(x0=.2)
-
class
pyddm.models.ic.
ICPointRatio
(**kwargs)[source]¶ Bases:
pyddm.models.ic.InitialCondition
Initial condition: any point expressed as a ratio between bounds, from -1 to 1.
Example usage:
ic = ICPointRatio(x0=-.2)The advantage of ICPointRatio over ICPoint is that, as long as x0 is greater than -1 and less than 1, the starting point will always stay within the bounds, even when bounds are being fit.
-
class
pyddm.models.ic.
ICUniform
(**kwargs)[source]¶ Bases:
pyddm.models.ic.InitialCondition
Initial condition: a uniform distribution.
Example usage:
ic = ICUniform()
-
class
pyddm.models.ic.
ICRange
(**kwargs)[source]¶ Bases:
pyddm.models.ic.InitialCondition
Initial condition: a bounded uniform distribution with range from -sz to sz.
Example usage:
ic = ICRange(sz=.3)
-
class
pyddm.models.ic.
ICGaussian
(**kwargs)[source]¶ Bases:
pyddm.models.ic.InitialCondition
Initial condition: a Gaussian distribution with a specified standard deviation.
Example usage:
ic = ICRange(sz=.3)
-
pyddm.models.ic.
ICArbitrary
(dist)[source]¶ Generate an IC object from an arbitrary distribution.
dist should be a 1 dimensional numpy array which sums to 1.
Note that ICArbitrary is a function, not an InitialCondition object, so it cannot be passed directly. It returns an instance of a an InitialCondition object which can be passed. So in place of, e.g. ICUniform(). In practice, the user should not notice a difference, and this function can thus be used in place of an InitialCondition object.
Example usage:
import scipy.statsic = ICArbitrary(dist=scipy.stats.binom.pmf(n=200, p=.4, k=range(0, 201))) # Binomial distributionimport numpy as npic = ICArbitrary(dist=np.asarray([0]*100+[1]+[0]*100)) # Equivalent to ICPointSourceCenter for dx=.01
Overlay¶
-
class
pyddm.models.overlay.
Overlay
(**kwargs)[source]¶ Bases:
pyddm.models.base.Dependence
Subclasses can modify distributions after they have been generated.
This abstract class provides the methods which define how a distribution should be modified after solving the model, for example for a mixture model. To subclass it, implement apply.
Also, since it inherits from Dependence, subclasses must also assign a name and required_parameters (see documentation for Dependence.)
-
apply
(solution)[source]¶ Apply the overlay to a Solution object.
This function must be redefined in subclasses.
This function takes a Solution object as its argument and returns a Solution object which was modified in some way. Often times, this will be by modifying solution.corr and solution.choice_lower. See the documentation for Solution for more information about this object.
Note that while this does not take conditions as an argument, conditions may still be accessed via solution.conditions.
Conceptually, this function performs some transformation on the simulated response time (first passage time) distributions. It is especially useful for non-decision times and mixture models, potentially in a parameter-dependent or condition-dependent manner.
-
apply_trajectory
(trajectory, model, rk4, seed, conditions={})[source]¶ Apply the overlay to a simulated decision variable trajectory.
This function is optional and may be redefined in subclasses. It is expected to implement the same mechanism as the method “apply”, but to do so on simulated trajectories (i.e. from Model.simulate_trial) instead of on a Solution object.
This function takes the t domain, the trajectory itself, and task conditions. It returns the modified trajectory.
-
-
class
pyddm.models.overlay.
OverlayNone
(**kwargs)[source]¶ Bases:
pyddm.models.overlay.Overlay
No overlay. An identity function for Solutions.
Example usage:
overlay = OverlayNone()
-
class
pyddm.models.overlay.
OverlayChain
(**kwargs)[source]¶ Bases:
pyddm.models.overlay.Overlay
Join together multiple overlays.
Unlike other model components, Overlays are not mutually exclusive. It is possible to transform the output solution many times. Thus, this allows joining together multiple Overlay objects into a single object.
It accepts one parameter: overlays. This should be a list of Overlay objects, in the order which they should be applied to the Solution object.
One key technical caveat is that the overlays which are chained together may not have the same parameter names. Parameter names must be given different names in order to be a part of the same overlay. This allows those parameters to be accessed by their name inside of an OverlayChain object.
Example usage:
overlay = OverlayChain(overlays=[OverlayNone(), OverlayNone(), OverlayNone()]) # Still equivalent to OverlayNoneoverlay = OverlayChain(overlays=[OverlayPoissonMixture(pmixturecoef=.01, rate=1),OverlayUniformMixture(umixturecoef=.01)]) # Apply a Poission mixture and then a Uniform mixture
-
class
pyddm.models.overlay.
OverlayUniformMixture
(**kwargs)[source]¶ Bases:
pyddm.models.overlay.Overlay
A uniform mixture distribution.
The output distribution should be umixturecoef*100 percent uniform distribution and (1-umixturecoef)*100 percent of the distribution to which this overlay is applied.
A mixture with the uniform distribution can be used to confer robustness when fitting using likelihood.
Example usage:
overlay = OverlayUniformMixture(umixturecoef=.01)
-
class
pyddm.models.overlay.
OverlayExponentialMixture
(**kwargs)[source]¶ Bases:
pyddm.models.overlay.Overlay
An exponential mixture distribution.
The output distribution should be pmixturecoef*100 percent exponential distribution and (1-umixturecoef)*100 percent of the distribution to which this overlay is applied.
A mixture with the exponential distribution can be used to confer robustness when fitting using likelihood.
Note that this is called OverlayPoissonMixture and not OverlayExponentialMixture because the exponential distribution is formed from a Poisson process, i.e. modeling a uniform lapse rate.
Example usage:
overlay = OverlayPoissonMixture(pmixturecoef=.02, rate=1)
-
class
pyddm.models.overlay.
OverlayNonDecision
(**kwargs)[source]¶ Bases:
pyddm.models.overlay.Overlay
Add a non-decision time
This shifts the reaction time distribution by nondectime seconds in order to create a non-decision time.
Example usage:
overlay = OverlayNonDecision(nondectime=.2)This can also be subclassed to allow easily shifting the non-decision time. When subclassing, override the get_nondecision_time(self, conditions) method to be any function you wish, using both conditions and parameters.
-
class
pyddm.models.overlay.
OverlayNonDecisionGamma
(**kwargs)[source]¶ Bases:
pyddm.models.overlay.Overlay
Add a gamma-distributed non-decision time
This shifts the reaction time distribution by an amount of time specified by the gamma distribution with shape parameter shape (sometimes called “k”) and scale parameter scale (sometimes called “theta”). The distribution is then further shifted by nondectime seconds.
Example usage:
overlay = OverlayNonDecisionGamma(nondectime=.2, shape=1.5, scale=.05)This can also be subclassed to allow easily shifting the non-decision time. When subclassing, override the get_nondecision_time(self, conditions) method to be any function you wish, using both conditions and parameters.
-
class
pyddm.models.overlay.
OverlayNonDecisionUniform
(**kwargs)[source]¶ Bases:
pyddm.models.overlay.Overlay
Add a uniformly-distributed non-decision time.
The center of the distribution of non-decision times is at nondectime, and it extends halfwidth on each side.
Example usage:
overlay = OverlayNonDecisionUniform(nondectime=.2, halfwidth=.02)This can also be subclassed to allow easily shifting the non-decision time. When subclassing, override the get_nondecision_time(self, conditions) method to be any function you wish, using both conditions and parameters.
Loss function (for optimization)¶
-
class
pyddm.models.loss.
LossFunction
(sample, required_conditions=None, method=None, **kwargs)[source]¶ Bases:
object
An abstract class for a function to assess goodness of fit.
This is an abstract class for describing how well data fits a model.
When subclasses are initialized, they will be initialized with the Sample object to which the model should be fit. Because the data will not change but the model will change, this is specified with initialization.
The optional required_conditions argument limits the stratification of sample by conditions to only the conditions mentioned in required_conditions. This decreases computation time by only solving the model for the condition names listed in required_conditions. For example, a simple DDM with no drift and constant variaince would mean required_conditions is an empty list.
The optional method argument can be “analytical”, “numerical”, “cn”, “implicit”, or “explicit”.
This will automatically parallelize if set_N_cpus() has been called.
-
cache_by_conditions
(model)[source]¶ Solve the model for all relevant conditions.
Solve model for each combination of conditions found within the dataset. For example, if required_conditions is [“hand”, “color”], and hand can be left or right and color can be blue or green, solves the model for: hand=left and color=blue; hand=right and color=blue; hand=left and color=green, hand=right and color=green.
This is a convenience function for defining new loss functions. There is generally no need to redefine this function in subclasses.
-
-
class
pyddm.models.loss.
LossSquaredError
(sample, required_conditions=None, method=None, **kwargs)[source]¶ Bases:
pyddm.models.loss.LossFunction
Squared-error loss function
-
class
pyddm.models.loss.
LossLikelihood
(sample, required_conditions=None, method=None, **kwargs)[source]¶ Bases:
pyddm.models.loss.LossFunction
Likelihood loss function
-
class
pyddm.models.loss.
LossBIC
(sample, required_conditions=None, method=None, **kwargs)[source]¶ Bases:
pyddm.models.loss.LossLikelihood
BIC loss function, functionally equivalent to LossLikelihood
-
class
pyddm.models.loss.
LossRobustLikelihood
(sample, required_conditions=None, method=None, **kwargs)[source]¶ Bases:
pyddm.models.loss.LossLikelihood
Likelihood loss function which will not fail for infinite likelihoods.
Usually you will want to use LossLikelihood instead. See the FAQs in the documentation for more information on how this differs from LossLikelihood.
-
class
pyddm.models.loss.
LossRobustBIC
(sample, required_conditions=None, method=None, **kwargs)[source]¶ Bases:
pyddm.models.loss.LossBIC
BIC loss function which will not fail for infinite likelihoods.
Usually you will want to use LossBIC instead. See the FAQs in the documentation for more information on how this differs from LossBIC.
Base¶
-
class
pyddm.models.base.
Dependence
(**kwargs)[source]¶ Bases:
object
An abstract class describing how one variable depends on other variables.
This is an abstract class which is inherrited by other abstract classes only, and has the highest level machinery for describing how one variable depends on others. For example, an abstract class that inherits from Dependence might describe how the drift rate may change throughout the simulation depending on the value of x and t, and then this class would be inherited by a concrete class describing an implementation. For example, the relationship between drift rate and x could be linear, exponential, etc., and each of these would be a subsubclass of Dependence.
In order to subclass Dependence, you must set the (static) class variable depname, which gives an alpha-numeric string describing which variable could potentially depend on other variables.
Each subsubclass of dependence must also define two (static) class variables. First, it must define name, which is an alpha-numeric plus underscores name of what the algorithm is, and also required_parameters, a python list of names (strings) for the parameters that must be passed to this algorithm. (This does not include globally-relevant variables like dt, it only includes variables relevant to a particular instance of the algorithm.) An optional (static) class variable is default_parameters, which is a dictionary indexed by the parameter names from required_parameters. Any parameters referenced here will be given a default value.
Dependence will check to make sure all of the required parameters have been supplied, with the exception of those which have default versions. It also provides other convenience and safety features, such as allowing tests for equality of derived algorithms and for ensuring extra parameters were not assigned.