@@ -183,18 +183,20 @@ is fixed.
183183
184184### When you want this
185185
186- A motivating workload: nested-Archimedean copula likelihoods with
187- per-observation censoring. Each observation needs a Bell-polynomial
188- expansion whose effective order equals its number of * uncensored* leaves,
189- which varies across the batch. Without ` effective_order ` , you face two
190- bad options:
186+ The expansion order ` K ` is part of the input shape, so a function
187+ ` jet ` 'd at one ` K ` re-traces and re-compiles for any other ` K ` . If the
188+ order you actually need varies across calls — different time steps in
189+ an integrator, different inputs in a batch, different convergence
190+ checks in an iterative method — you would otherwise face two bad
191+ options:
191192
192- - Pad to the maximum possible order for every observation — wasted work
193- on always-zero coefficients.
194- - Re-trace and re-compile per observation — fatal under high cardinality.
193+ - Pad every call to the maximum possible order. Wastes compute on
194+ coefficients you don't use.
195+ - Re-trace per order. Fatal whenever the order changes more often than
196+ the cost of compilation.
195197
196- ` effective_order ` lets you compile once at ` K = max possible order ` and
197- pay per-observation work proportional to that observation 's actual order.
198+ ` effective_order ` resolves both: compile once at `K = max possible
199+ order` and pay per-call work proportional to that call 's actual order.
198200
199201### Example
200202
@@ -205,30 +207,29 @@ from jet_array import jet
205207
206208K_MAX = 16 # static array size
207209
208- def slow_chain (x ):
209- # A composition that uses several primitives whose rules respect
210- # effective_order — exp/log/logistic are good candidates.
211- y = jnp.exp(x)
212- y = jnp.log1p(y)
213- y = jnp.tanh(y)
214- return y
215-
216210@jax.jit
217211def taylor (x , k_dyn ):
212+ """ Taylor series of exp(sin(x)) at x, computed up to order k_dyn,
213+ with a fixed array size of K_MAX so a single XLA program handles
214+ every order."""
218215 series_in = jnp.zeros(K_MAX ).at[0 ].set(1.0 )
219- return jet(slow_chain, (x,), (series_in,), effective_order = k_dyn)
220-
221- # Both calls use the same compiled XLA program.
222- # The second runs more work — it computes 12 coefficients instead of 4.
223- p1, s1 = taylor(jnp.asarray(0.5 ), jnp.array(4 ))
224- p2, s2 = taylor(jnp.asarray(0.5 ), jnp.array(12 ))
225- # s1[:4] is the answer you can use; s1[4:] is unspecified.
226- # s2[:12] is the answer; s2[12:] is unspecified.
216+ return jet(lambda x : jnp.exp(jnp.sin(x)), (x,), (series_in,),
217+ effective_order = k_dyn)
218+
219+ # Both calls hit the same compiled program; the second does more work.
220+ p1, s1 = taylor(jnp.asarray(0.5 ), jnp.array(4 )) # uses s1[:4]
221+ p2, s2 = taylor(jnp.asarray(0.5 ), jnp.array(12 )) # uses s2[:12]
227222```
228223
229- For workloads where the dynamic order varies per element of a
230- ` vmap ` -batched dimension, pass an array of ` effective_order ` values and
231- combine with ` jax.vmap ` .
224+ For batches where each element wants its own order, pass an array of
225+ ` effective_order ` values and combine with ` jax.vmap ` :
226+
227+ ``` python
228+ xs = jnp.linspace(0.0 , 1.0 , 100 )
229+ ks = jnp.array([3 , 5 , 8 , 12 ] * 25 ) # one per batch element
230+ batched = jax.vmap(taylor)(xs, ks)
231+ # batched.series[i, :ks[i]] is the answer for element i.
232+ ```
232233
233234### Caveats
234235
0 commit comments