|
1 | 1 | from ngclearn.components.jaxComponent import JaxComponent |
2 | | -from jax import numpy as jnp, jit |
| 2 | +from jax import numpy as jnp, jit, lax |
3 | 3 | from ngclearn import compilable, Compartment |
4 | 4 |
|
5 | 5 |
|
@@ -82,6 +82,7 @@ def __init__( |
82 | 82 | self.s = Compartment(jnp.zeros((self.batch_size, self.n_units))) |
83 | 83 | ## set up analytical state filters (to match RAFCell construction) |
84 | 84 | self.t_last_spike = Compartment(jnp.full((self.batch_size, self.n_units), -1.0)) |
| 85 | + self.next_spike_t = Compartment(jnp.full((self.batch_size, self.n_units), -1.0)) |
85 | 86 | self.j_v = Compartment(jnp.zeros((self.batch_size, self.n_units))) |
86 | 87 | self.j_w = Compartment(jnp.zeros((self.batch_size, self.n_units))) |
87 | 88 | self.last_t_eval = Compartment(jnp.zeros((self.batch_size, self.n_units))) |
@@ -111,10 +112,26 @@ def reset(self): # |
111 | 112 | self.v.set(jnp.zeros((self.batch_size, self.n_units))) |
112 | 113 | self.s.set(jnp.zeros((self.batch_size, self.n_units))) |
113 | 114 | self.t_last_spike.set(jnp.full((self.batch_size, self.n_units), -1.0)) |
| 115 | + self.next_spike_t.set(jnp.full((self.batch_size, self.n_units), -1.0)) |
114 | 116 | self.j_v.set(jnp.zeros((self.batch_size, self.n_units))) |
115 | 117 | self.j_w.set(jnp.zeros((self.batch_size, self.n_units))) |
116 | 118 | self.last_t_eval.set(jnp.zeros((self.batch_size, self.n_units))) |
117 | 119 |
|
| 120 | + @compilable |
| 121 | + def predict_next_spike(self, t_start): |
| 122 | + next_spike_t = RAFSRM._predict_next_spike( ## next-spike-time predictor |
| 123 | + t_start, |
| 124 | + self.j_v.get(), |
| 125 | + self.j_w.get(), |
| 126 | + self.tau_v, |
| 127 | + self.tau_w, |
| 128 | + self.omega, |
| 129 | + self.dampen_factor, |
| 130 | + self.thr, |
| 131 | + max_iters=10 |
| 132 | + ) |
| 133 | + self.next_spike_t.set(next_spike_t) ## store predicted next spike time(s) |
| 134 | + |
118 | 135 | @staticmethod |
119 | 136 | def _evaluate_SRM_filter( ## kernel co-routine |
120 | 137 | t, |
@@ -176,11 +193,18 @@ def _evaluate_SRM_filter( ## kernel co-routine |
176 | 193 | return v_total, new_v, new_w |
177 | 194 |
|
178 | 195 | @staticmethod |
179 | | - def predict_next_spike( |
180 | | - t_start, j_v, j_w, tau_v, tau_w, omega, dampen_factor, thr, max_iters=10 |
| 196 | + def _predict_next_spike( ## co-routine used to compute when a future spike will occur |
| 197 | + t_start, |
| 198 | + j_v, |
| 199 | + j_w, |
| 200 | + tau_v, |
| 201 | + tau_w, |
| 202 | + omega, |
| 203 | + dampen_factor, |
| 204 | + thr, |
| 205 | + max_iters=10 |
181 | 206 | ): |
182 | | - ## co-routine used to compute when a future spike will occur |
183 | | - ## based on a Newton-Raphson root finding process to predict when exactly |
| 207 | + ## NOTE: this co-routine is based on a Newton-Raphson root finding process to predict when exactly |
184 | 208 | ## continuous sub-threshold wave equation will cross threshold line |
185 | 209 |
|
186 | 210 | ## standardize SRM system parameters |
@@ -220,7 +244,7 @@ def scan_body(carry, _): ## set up scanner for this search process |
220 | 244 | return s_next, None |
221 | 245 |
|
222 | 246 | ## execute unrolled iteration passes completely w/in JAX/JIT compiler context |
223 | | - final_s, _ = jax.lax.scan(scan_body, s_guess, None, length=max_iters) |
| 247 | + final_s, _ = lax.scan(scan_body, s_guess, None, length=max_iters) |
224 | 248 | predicted_t = t_start + final_s ## compute absolute (executed) global clock projection coordinate |
225 | 249 | ## employ a safety check: |
226 | 250 | ### if initial slope is negative (or diverging), flag it as un-triggered (i.e., -1.0) |
|
0 commit comments