pymc-devs / pymc-devs/pytensor

Provide lower level Numba and Jax functions

Open
#222 4 comments 1 reaction 0 assignees View on GitHub

Nobody has claimed this yet.

backend compatibility feature request jax numba request discussion
Dominant language
Python
Stars
644
Forks
208
Avg merge
2d 14h
Merged PRs (30d)
16

Description

Description

pytensor.function() returns a class with a complicated __call__ method that puts inputs and allocates outputs in list like objects that are very much tuned to the C backend. This means that a generic function compiled to JAX or Numba will in general not work within a longer JAX / Numba workflow (e.g., calling vmap or grad on a compiled function).

We could provide a simpler jax_function and numba_function that do just that. In PyMC we implemented something like that for JAX: https://github.com/pymc-devs/pymc/blob/31c30dc1beea26e4bff52a93037540923feaaa84/pymc/sampling/jax.py#L108-L132

There is one obvious limitation which concerns the handling of shared variables and updates. Shared variables are global variables that are passed as inputs to the actual inner function but not provided explicitly by the user. Updates replace the original value of of shared variables by a (user-hidden) output of the function every time it is called.

A simple JAX/Numba PyTensor function with global variables and updates looks like this:

import pytensor
import pytensor.tensor as pt
import numpy as np

shared_y = pytensor.shared(np.ones((5,)))
x = pt.vector("x")
fn = pytensor.function([x], x + y, updates={y: y + 1}, mode="JAX")

And roughly translates to the following pseudo code:

global shared_y = np.ones((5,))

def fn(x):
  @jax.jit
  def inner_fn(x, y):
    return x + y, y + 1

  global shared_y
  out, update_y = inner_fn(x, shared_y)
  shared_y[:] = update_y
  return out

I don't think neither JAX nor Numba support stateful jitted functions, so users would need to work with the inner_fn directly.

https://numba.pydata.org/numba-doc/dev/user/faq.html#numba-doesn-t-seem-to-care-when-i-modify-a-global-variable

The proposal here is to give users easy access to the compiled (jitted or not) inner_fn

Contributor guide

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

Start by reading pytensor.function(), the call implementation in pytensor/compile/function/types.py, and the referenced PyMC sampling/jax.py implementation. Compare how shared variables and updates are handled, then define what access to the compiled inner function should provide for JAX and Numba users; done means the proposed lower-level functions are available without the stateful wrapper.

Written by the indexing model from the issue text.

Assessment

Tech stack
python
Domain
backend, compilers
Issue type
Feature
Difficulty
5/5
Estimated time
Over a week
Activity status
Quiet
Clarity
Mostly clear
Newbie friendliness
42/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.