0%

Exponential integration

Sim 1: Tau exponential integration time constant

Tau exponential integration time constant

tau := 10.0 // time constant
drive := 50.0
init := 0.0
val := init
var tauStr, driveStr, initStr, valAtTauStr string

##
totalTime := 100
driver := zeros(totalTime) // driver is what is driving the value
vals := zeros(totalTime) // values
exps := zeros(totalTime) // exponential function values
##

func valUpdate() {
    tauStr = fmt.Sprintf("Tau: %g", tau)
    driveStr = fmt.Sprintf("Driver: %g", drive)
    initStr = fmt.Sprintf("Init: %g", init)
    ##
    d := array(drive) // current drive
    iv := array(init)
    dv := d - iv
    v := array(init) // current
    ttau := array(tau)
    ##
    for t := range 100 {
        ##
        tv := array(t)
        driver[t] = d
        vals[t] = v
        v += (1.0 / ttau) * (d - v) // v moves toward d
        exps[t] = iv + dv * (1 - exp(-tv / ttau))
        ##
    }
    tint := int(tau)
    tval := vals.Float(tint)
    dist := drive - init
    pdist := 100.0
    if dist != 0 {
        pdist = 100 * (tval-init) / dist
    }
    valAtTauStr = fmt.Sprintf("<b>Value at Tau (%d) = %7.3g = %7.3g %% of way to Drive, %7.3g %% left to go</b>", tint, tval, pdist, 100 - pdist)
}

valUpdate()

plotStyler := func(s *plot.Style) {
    s.Range.SetMax(100).SetMin(0)
    s.Plot.XAxis.Label = "Time"
    s.Plot.XAxis.Range.SetMax(100).SetMin(0)
	s.Plot.Legend.Position.Left = true
}
plot.SetStyler(driver, plotStyler) 

fig1, pw := lab.NewPlotWidget(b)
dl := plots.NewLine(fig1, driver)
vl := plots.NewLine(fig1, vals)
el := plots.NewLine(fig1, exps)
fig1.Legend.Add("Driver", dl)
fig1.Legend.Add("Value", vl)
fig1.Legend.Add("Exp", el)

valAtTauTx := core.NewText(b)
valAtTauTx.Styler(func(s *styles.Style) {
    s.Min.X.Ch(80) // clean rendering with variable width content
})
core.Bind(&valAtTauStr, valAtTauTx)

func updt() {
    valUpdate()
    dl.SetData(driver)
    vl.SetData(vals)
    el.SetData(exps)
    valAtTauTx.UpdateRender()
    pw.NeedsRender()
}

func addSlider(label *string, val *float64, mxVal float32) {
    tx := core.NewText(b)
    tx.Styler(func(s *styles.Style) {
        s.Min.X.Ch(40)  // clean rendering with variable width content
    })
    core.Bind(label, tx)
	sld := core.NewSlider(b).SetMin(1).SetMax(mxVal).SetStep(1).SetEnforceStep(true)
	sld.SendChangeOnInput()
	sld.OnChange(func(e events.Event) {
		updt()
		tx.UpdateRender()
	})
	core.Bind(val, sld)
}

addSlider(&tauStr, &tau, 50)
addSlider(&initStr, &init, 100)
addSlider(&driveStr, &drive, 100)

Sim 1 provides an illustration of the process of online exponential integration governed by a time constant tau (\(\tau\)), where a given value is updated at each time step to move some fraction of the way toward a “driving” value, where this fraction is \(\frac{1}{\tau}\):

Eq 1: Exponential integration

\[ v(t+1) = v(t) + \frac{1}{\tau} (d - v(t)) \]

This type of equation is used almost everywhere in Axon, and is a basic consequence of many different types of physical processes, including Ohm’s law for the influence of current on electrical potential, which is the core equation driving our model of the neuron.

Chemical reactions also obey the same kind of function, governed by a tau factor that represents the speed of the chemical reaction. For example, two initial “ingredients” A, B can be combined into a compound AB by a chemical reaction with a forward rate constant of \(K_f\):

Eq 2: Forward chemical reaction

\[ A + B \; {K_f \atop \rightarrow} AB \]

As the reaction proceeds, there is less of the raw ingredients available, so it naturally slows down as a function of the amount of these ingredients available, resulting in the same dynamic as Eq 1.

Value at Tau

As you can see in Eq 1 as you experiment with different Tau, Init, and Driver values, it is always about 64% of the way to the driver after Tau time steps of updating. This can be computed directly from the exponential function that can compute the value directly at any given time step (which is plotted as Exp in Sim 1):

Eq 3: Exponential function

\[ v(t) = v(0) + (d - v(0)) \left(1 - e^{-t/\tau} \right) \]

where \(v(0)\) is the Init initial value at time step 0. If we set this to 0, and evaluate at \(t=\tau\), this becomes:

Eq 4: Value at t = tau

\[ v(\tau) = d \left(1 - e^{-1} \right) \approx 1 - 1/2.72 \approx 0.632.. \]

The small discrepancy in reported values is due to the numerical differences from iterating the update equation (Eq 1) at discrete time intervals, versus the fully continuous update computed by the exponential function (due to calculus and all that).

In the simplest terms, you can think of \(\tau\) as the amount of time it takes to get roughly 2⁄3 of the way to the driving value.

Half-life

Another value you have probably heard of is the half-life of an exponential process (radioactive decay also follows this same function), which is when the value is 50% of the way to the driving value. This happens when \(t = ln(2) \tau\) or roughly \(t = 0.7 \tau\), as you can see by setting \(v(t) = \frac{1}{2}d\) and solving for t:

Eq 5: Half-life time

\[ \frac{1}{2} d = d \left(1 - e^{-t/\tau} \right) \]

\[ \left(1 - e^{-t/\tau} \right) = \frac{1}{2} \]

take the natural log of both sides:

\[ \frac{-t}{\tau} = ln(1) - ln(2) \]

\[ t = ln(2) \tau \]

See this Wikipedia page for more information.