JAX 内部机制:primitives#

JAX primitives 简介#

JAX primitive 是 JAX 程序的基本计算单元。本文档解释了 JAX primitive 必须支持的接口,以使 JAX 能够执行其所有转换(这不是一份操作指南)。

例如,乘加操作可以使用低层 jax.lax.* primitives(类似于 XLA 运算符封装器)或 jax.extend.core.Primitive("multiply_add") 来实现,如下文所示。

JAX 能够获取这些 primitive 操作序列,并通过其可组合的 Python 函数转换来对其进行转换,例如 jax.jit()jax.grad()jax.vmap()。JAX 以*JAX 可追踪*的方式实现这些转换。这意味着当 Python 函数执行时,它对数据应用的操作只有以下两种:

  • 数据属性检查: 数据信息,例如形状或类型;或者

  • JAX primitives: 这些是本教程中介绍的 JAX 特殊操作。

JAX primitives 知道如何操作具体数据值和抽象 JAX 值。JAX 可以使用抽象参数调用*可追踪的 JAX 函数*。例如,JAX 抽象值 — ShapedArray(float32[2,2]) — 捕获值的类型和形状,但不捕获具体数据值。

JAX 转换后的函数本身必须是可追踪的 JAX 函数,*以确保这些转换是可组合的*,例如 jax.jit(jax.jacfwd(jax.grad(f)))

JAX 提供了与大多数 XLA 操作对应的预定义 primitives,包括加法、矩阵乘法、sin、cos 和索引。

此外,JAX 还提供了基于 JAX primitives 的 NumPy 函数实现。这意味着*使用 JAX 实现的 NumPy 的 Python 程序是 JAX 可追踪的,因此也是可转换的*。其他库可以通过使用 JAX primitives 实现它们来使其可追踪。

此外,JAX primitives 的集合是可扩展的,因此您可以定义一个封装函数行为的新 primitive,而不是使用预定义 JAX primitives 重新实现函数。

考虑以下示例:您想向 JAX 添加对一个具有三个参数的乘加函数的支持,其数学定义为 multiply_add(x, y, z) = x * y + z。此函数作用于 3 个形状相同的浮点值张量,并逐点执行操作。您可以通过以下方式实现:

使用现有 JAX primitives#

定义新函数最简单的方法是使用 JAX primitives 来编写它们,或者使用其他本身由 JAX primitives 编写的函数来编写,例如 jax.lax() 模块中定义的函数。

from jax._src.lax import lax
from jax._src import api

def multiply_add_lax(x, y, z):
  """Implementation of multiply-add using the `jax.lax` primitives."""
  return lax.add(lax.mul(x, y), z)


def square_add_lax(a, b):
  """A square-add function using the newly defined multiply-add."""
  return multiply_add_lax(a, a, b)

print("square_add_lax = ", square_add_lax(2., 10.))
# Differentiate w.r.t. the first argument
print("grad(square_add_lax) = ", api.grad(square_add_lax, argnums=0)(2.0, 10.))
square_add_lax =  14.0
grad(square_add_lax) =  4.0

为了理解 JAX 内部如何使用 primitives,添加一些用于追踪函数调用的辅助工具

#@title Helper functions (execute this cell)
import functools
import traceback

_indentation = 0
def _trace(msg=None):
    """Print a message at current indentation."""
    if msg is not None:
        print("  " * _indentation + msg)

def _trace_indent(msg=None):
    """Print a message and then indent the rest."""
    global _indentation
    _trace(msg)
    _indentation = 1 + _indentation

def _trace_unindent(msg=None):
    """Unindent then print a message."""
    global _indentation
    _indentation = _indentation - 1
    _trace(msg)

def trace(name):
  """A decorator for functions to trace arguments and results."""

  def trace_func(func):  # pylint: disable=missing-docstring
    def pp(v):
        """Print certain values more succinctly"""
        vtype = str(type(v))
        if "jax._src.xla_bridge._JaxComputationBuilder" in vtype:
            return "<JaxComputationBuilder>"
        elif "jaxlib._jax_.XlaOp" in vtype:
            return "<XlaOp at 0x{:x}>".format(id(v))
        elif ("partial_eval.JaxprTracer" in vtype or
              "batching.BatchTracer" in vtype or
              "ad.JVPTracer" in vtype):
            return "Traced<{}>".format(v.aval)
        elif isinstance(v, tuple):
            return "({})".format(pp_values(v))
        else:
            return str(v)
    def pp_values(args):
        return ", ".join([pp(arg) for arg in args])

    @functools.wraps(func)
    def func_wrapper(*args):
      _trace_indent("call {}({})".format(name, pp_values(args)))
      res = func(*args)
      _trace_unindent("|<- {} = {}".format(name, pp(res)))
      return res

    return func_wrapper

  return trace_func

class expectNotImplementedError(object):
  """Context manager to check for NotImplementedError."""
  def __enter__(self): pass
  def __exit__(self, type, value, tb):
    global _indentation
    _indentation = 0
    if type is NotImplementedError:
      print("\nFound expected exception:")
      traceback.print_exc(limit=3)
      return True
    elif type is None:  # No exception
      assert False, "Expected NotImplementedError"
    else:
      return False

您可以使用其他已使用 jax.lax() primitives 编写的函数,例如 jax.numpy 中的函数,而不是直接使用 jax.lax() primitives。

import jax.numpy as jnp
import numpy as np

@trace("multiply_add_numpy")
def multiply_add_numpy(x, y, z):
    return jnp.add(jnp.multiply(x, y), z)

@trace("square_add_numpy")
def square_add_numpy(a, b):
    return multiply_add_numpy(a, a, b)

print("\nNormal evaluation:")
print("square_add_numpy = ", square_add_numpy(2., 10.))
print("\nGradient evaluation:")
print("grad(square_add_numpy) = ", api.grad(square_add_numpy)(2.0, 10.))
Normal evaluation:
call square_add_numpy(2.0, 10.0)
  call multiply_add_numpy(2.0, 2.0, 10.0)
  |<- multiply_add_numpy = 14.0
|<- square_add_numpy = 14.0
square_add_numpy =  14.0

Gradient evaluation:
call square_add_numpy(LinearizeTracer<~float32[]>, 10.0)
  call multiply_add_numpy(LinearizeTracer<~float32[]>, LinearizeTracer<~float32[]>, 10.0)
  |<- multiply_add_numpy = LinearizeTracer<~float32[]>
|<- square_add_numpy = LinearizeTracer<~float32[]>
grad(square_add_numpy) =  4.0

请注意,在计算 jax.grad() 的过程中,JAX 会使用特殊参数 ConcreteArray(...)(在本 Colab 中进一步描述)调用 square_add_numpymultiply_add_numpy。重要的是要记住,可追踪的 JAX 函数不仅必须能够作用于具体参数,还必须能够作用于 JAX 可能用于抽象函数执行的特殊抽象参数。

只要函数是使用 JAX primitives 编写的,就满足 JAX 可追踪性属性。

定义新的 JAX primitives#

添加乘加功能支持的正确方法是使用现有的 JAX primitives,如上所示。然而,为了演示 JAX primitives 的工作原理,假设您想为乘加功能向 JAX 添加一个新的 primitive。

from jax.extend import core

multiply_add_p = core.Primitive("multiply_add")  # Create the primitive

@trace("multiply_add_prim")
def multiply_add_prim(x, y, z):
  """The JAX-traceable way to use the JAX primitive.

  Note that the traced arguments must be passed as positional arguments
  to `bind`.
  """
  return multiply_add_p.bind(x, y, z)

@trace("square_add_prim")
def square_add_prim(a, b):
  """A square-add function implemented using the new JAX-primitive."""
  return multiply_add_prim(a, a, b)

如果您尝试调用新定义的函数,将会收到错误,因为您尚未告知 JAX 有关新 primitive 语义的任何信息。

with expectNotImplementedError():
  square_add_prim(2., 10.)
call square_add_prim(2.0, 10.0)
  call multiply_add_prim(2.0, 2.0, 10.0)

Found expected exception:
Traceback (most recent call last):
  File "/tmp/ipykernel_1806/2844449444.py", line 2, in <module>
    square_add_prim(2., 10.)
  File "/tmp/ipykernel_1806/3854395562.py", line 48, in func_wrapper
    res = func(*args)
          ^^^^^^^^^^^
  File "/tmp/ipykernel_1806/3275395289.py", line 17, in square_add_prim
    return multiply_add_prim(a, a, b)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^
NotImplementedError: Evaluation rule for 'multiply_add' not implemented

Primal 求值规则#

@trace("multiply_add_impl")
def multiply_add_impl(x, y, z):
  """Concrete implementation of the primitive.

  This function does not need to be JAX traceable.

  Args:
    x, y, z: The concrete arguments of the primitive. Will only be called with
      concrete values.

  Returns:
    the concrete result of the primitive.
  """
  # Note: you can use the ordinary (non-JAX) NumPy, which is not JAX-traceable.
  return np.add(np.multiply(x, y), z)

# Now, register the primal implementation with JAX:
multiply_add_p.def_impl(multiply_add_impl)
<function __main__.multiply_add_impl(x, y, z)>
assert square_add_prim(2., 10.) == 14.
call square_add_prim(2.0, 10.0)
  call multiply_add_prim(2.0, 2.0, 10.0)
    call multiply_add_impl(2.0, 2.0, 10.0)
    |<- multiply_add_impl = 14.0
  |<- multiply_add_prim = 14.0
|<- square_add_prim = 14.0

使用 jit 时会发生什么#

现在,如果您尝试使用 jit,将会收到 NotImplementedError 错误

with expectNotImplementedError():
  api.jit(square_add_prim)(2., 10.)
call square_add_prim(JitTracer<~float32[]>, JitTracer<~float32[]>)
  call multiply_add_prim(JitTracer<~float32[]>, JitTracer<~float32[]>, JitTracer<~float32[]>)

Found expected exception:
Traceback (most recent call last):
  File "/tmp/ipykernel_1806/1813425700.py", line 2, in <module>
    api.jit(square_add_prim)(2., 10.)
  File "/home/docs/checkouts/readthedocs.org/user_builds/jax/envs/latest/lib/python3.12/site-packages/jax/_src/traceback_util.py", line 180, in reraise_with_filtered_traceback
    return fun(*args, **kwargs)
           ^^^^^^^^^^^^^^^^^^^^
  File "/home/docs/checkouts/readthedocs.org/user_builds/jax/envs/latest/lib/python3.12/site-packages/jax/_src/pjit.py", line 259, in cache_miss
    _python_pjit_helper(fun, jit_info, *args, **kwargs)
NotImplementedError: Abstract evaluation for 'multiply_add' not implemented

抽象求值规则#

为了对函数进行 JIT 编译以及其他转换,JAX 首先仅使用参数的形状和类型对其进行抽象求值。这种抽象求值有多种目的

  • 获取计算中使用的 JAX primitives 序列。此序列将被编译。

  • 计算计算中使用的所有向量和操作的形状和类型。

例如,一个包含 3 个元素的向量的抽象可以是 ShapedArray(float32[3]),也可以是 ConcreteArray([1., 2., 3.])。在后一种情况下,JAX 将实际的具体值封装为抽象值。

from jax import core

@trace("multiply_add_abstract_eval")
def multiply_add_abstract_eval(xs, ys, zs):
  """Abstract evaluation of the primitive.

  This function does not need to be JAX traceable. It will be invoked with
  abstractions of the actual arguments

  Args:
    xs, ys, zs: Abstractions of the arguments.

  Result:
    a ShapedArray for the result of the primitive.
  """
  assert xs.shape == ys.shape
  assert xs.shape == zs.shape
  return core.ShapedArray(xs.shape, xs.dtype)

# Now, register the abstract evaluation with JAX:
multiply_add_p.def_abstract_eval(multiply_add_abstract_eval)
<function __main__.multiply_add_abstract_eval(xs, ys, zs)>

如果您再次尝试应用 jit,您可以检查抽象求值的过程,但您会收到另一个关于缺少实际 XLA 编译规则的错误

with expectNotImplementedError():
  api.jit(square_add_prim)(2., 10.)
call square_add_prim(JitTracer<~float32[]>, JitTracer<~float32[]>)
  call multiply_add_prim(JitTracer<~float32[]>, JitTracer<~float32[]>, JitTracer<~float32[]>)
    call multiply_add_abstract_eval(~float32[], ~float32[], ~float32[])
    |<- multiply_add_abstract_eval = float32[]
  |<- multiply_add_prim = JitTracer<float32[]>
|<- square_add_prim = JitTracer<float32[]>

Found expected exception:
Traceback (most recent call last):
  File "<frozen runpy>", line 198, in _run_module_as_main
  File "<frozen runpy>", line 88, in _run_code
  File "/home/docs/checkouts/readthedocs.org/user_builds/jax/envs/latest/lib/python3.12/site-packages/ipykernel_launcher.py", line 18, in <module>
    app.launch_new_instance()
jax._src.source_info_util.JaxStackTraceBeforeTransformation: NotImplementedError: MLIR translation rule for primitive 'multiply_add' not found for platform cpu

The preceding stack trace is the source of the JAX operation that, once transformed by JAX, triggered the following exception.

--------------------

The above exception was the direct cause of the following exception:

Traceback (most recent call last):
  File "/tmp/ipykernel_1806/1813425700.py", line 2, in <module>
    api.jit(square_add_prim)(2., 10.)
  File "/home/docs/checkouts/readthedocs.org/user_builds/jax/envs/latest/lib/python3.12/site-packages/jax/_src/traceback_util.py", line 180, in reraise_with_filtered_traceback
    return fun(*args, **kwargs)
           ^^^^^^^^^^^^^^^^^^^^
  File "/home/docs/checkouts/readthedocs.org/user_builds/jax/envs/latest/lib/python3.12/site-packages/jax/_src/pjit.py", line 259, in cache_miss
    _python_pjit_helper(fun, jit_info, *args, **kwargs)
NotImplementedError: MLIR translation rule for primitive 'multiply_add' not found for platform cpu

XLA 编译规则#

JAX 编译通过将每个 primitive 编译成 XLA 操作图来工作。

这是向 JAX 添加新功能的最大的障碍,因为 XLA 操作集是有限的,并且 JAX 已经为其中大多数操作定义了 primitives。但是,XLA 包含一个 CustomCall 操作,可用于封装使用 C++ 定义的任意功能。

from jax._src.lib.mlir.dialects import hlo

@trace("multiply_add_lowering")
def multiply_add_lowering(ctx, xc, yc, zc):
  """The compilation to XLA of the primitive.

  Given an mlir.ir.Value for each argument, return the mlir.ir.Values for
  the results of the function.

  Does not need to be a JAX-traceable function.
  """
  return [hlo.AddOp(hlo.MulOp(xc, yc), zc).result]

# Now, register the lowering rule with JAX.
# For GPU, refer to the https://jax.net.cn/en/latest/Custom_Operation_for_GPUs.html
from jax.interpreters import mlir

mlir.register_lowering(multiply_add_p, multiply_add_lowering, platform='cpu')

您现在将成功应用 jax.jit。请注意,JAX 首先会抽象地评估函数,这会触发 multiply_add_abstract_eval 函数,然后编译其遇到的 primitives 集合,包括 multiply_add。此时 JAX 会调用 multiply_add_lowering

assert api.jit(lambda x, y: square_add_prim(x, y))(2., 10.) == 14.
call square_add_prim(JitTracer<~float32[]>, JitTracer<~float32[]>)
  call multiply_add_prim(JitTracer<~float32[]>, JitTracer<~float32[]>, JitTracer<~float32[]>)
  |<- multiply_add_prim = JitTracer<float32[]>
|<- square_add_prim = JitTracer<float32[]>
call multiply_add_lowering(LoweringRuleContext(module_context=ModuleContext(context=<jax._src.interpreters.mlir.JaxIrContext object at 0x71e7d2f8a2d0>, module=<jaxlib.mlir._mlir_libs._mlir.ir.Module object at 0x71e7d2fb42c0>, ip=<jaxlib.mlir._mlir_libs._mlir.ir.InsertionPoint object at 0x71e7d2fb4210>, symbol_table=<jaxlib.mlir._mlir_libs._mlir.ir.SymbolTable object at 0x71e7d2f98e40>, platforms=('cpu',), backend=<jaxlib._jax.Client object at 0x71e7eb49fed0>, axis_context=ShardingContext(num_devices=1, device_assignment=None, abstract_mesh=None), keepalives=[], channel_iterator=count(1), host_callbacks=[], shape_poly_state=<jax._src.interpreters.mlir.ShapePolyLoweringState object at 0x71e7d2f98e30>, all_default_mem_kind=True, lowering_cache={}, cached_primitive_lowerings={}, traceback_caches=TracebackCaches(traceback_cache={<jaxlib._jax.Traceback object at 0x5b39a6655140>: loc(callsite("multiply_add_prim"("/tmp/ipykernel_1806/3275395289.py":12:9 to :37) at callsite("trace.<locals>.trace_func.<locals>.func_wrapper"("/tmp/ipykernel_1806/3854395562.py":48:12 to :23) at callsite("square_add_prim"("/tmp/ipykernel_1806/3275395289.py":17:9 to :35) at callsite("trace.<locals>.trace_func.<locals>.func_wrapper"("/tmp/ipykernel_1806/3854395562.py":48:12 to :23) at callsite("<lambda>"("/tmp/ipykernel_1806/1570919344.py":1:28 to :49) at callsite("<module>"("/tmp/ipykernel_1806/1570919344.py":1:7 to :59) at callsite("InteractiveShell.run_code"("/home/docs/checkouts/readthedocs.org/user_builds/jax/envs/latest/lib/python3.12/site-packages/IPython/core/interactiveshell.py":3699:20 to :69) at callsite("InteractiveShell.run_ast_nodes"("/home/docs/checkouts/readthedocs.org/user_builds/jax/envs/latest/lib/python3.12/site-packages/IPython/core/interactiveshell.py":3639:19 to :64) at callsite("InteractiveShell.run_cell_async"("/home/docs/checkouts/readthedocs.org/user_builds/jax/envs/latest/lib/python3.12/site-packages/IPython/core/interactiveshell.py":3394:29 to 3395:85) at "_pseudo_sync_runner"("/home/docs/checkouts/readthedocs.org/user_builds/jax/envs/latest/lib/python3.12/site-packages/IPython/core/async_helpers.py":128:8 to :23)))))))))))}, location_cache={(<code object multiply_add_prim at 0x71e7eb35a880, file "/tmp/ipykernel_1806/3275395289.py", line 5>, 44): loc("multiply_add_prim"("/tmp/ipykernel_1806/3275395289.py":12:9 to :37)), (<code object func_wrapper at 0x71e7eb79b470, file "/tmp/ipykernel_1806/3854395562.py", line 45>, 76): loc("trace.<locals>.trace_func.<locals>.func_wrapper"("/tmp/ipykernel_1806/3854395562.py":48:12 to :23)), (<code object square_add_prim at 0x71e7e80f9290, file "/tmp/ipykernel_1806/3275395289.py", line 14>, 24): loc("square_add_prim"("/tmp/ipykernel_1806/3275395289.py":17:9 to :35)), (<code object <lambda> at 0x71e7e80faa30, file "/tmp/ipykernel_1806/1570919344.py", line 1>, 22): loc("<lambda>"("/tmp/ipykernel_1806/1570919344.py":1:28 to :49)), (<code object <module> at 0x71e7e8a57730, file "/tmp/ipykernel_1806/1570919344.py", line 1>, 42): loc("<module>"("/tmp/ipykernel_1806/1570919344.py":1:7 to :59)), (<code object run_code at 0x5b39a4873430, file "/home/docs/checkouts/readthedocs.org/user_builds/jax/envs/latest/lib/python3.12/site-packages/IPython/core/interactiveshell.py", line 3663>, 252): loc("InteractiveShell.run_code"("/home/docs/checkouts/readthedocs.org/user_builds/jax/envs/latest/lib/python3.12/site-packages/IPython/core/interactiveshell.py":3699:20 to :69)), (<code object run_ast_nodes at 0x5b39a4a38620, file "/home/docs/checkouts/readthedocs.org/user_builds/jax/envs/latest/lib/python3.12/site-packages/IPython/core/interactiveshell.py", line 3540>, 1132): loc("InteractiveShell.run_ast_nodes"("/home/docs/checkouts/readthedocs.org/user_builds/jax/envs/latest/lib/python3.12/site-packages/IPython/core/interactiveshell.py":3639:19 to :64)), (<code object run_cell_async at 0x5b39a4a3ad00, file "/home/docs/checkouts/readthedocs.org/user_builds/jax/envs/latest/lib/python3.12/site-packages/IPython/core/interactiveshell.py", line 3231>, 1104): loc("InteractiveShell.run_cell_async"("/home/docs/checkouts/readthedocs.org/user_builds/jax/envs/latest/lib/python3.12/site-packages/IPython/core/interactiveshell.py":3394:29 to 3395:85)), (<code object _pseudo_sync_runner at 0x71e8279c1530, file "/home/docs/checkouts/readthedocs.org/user_builds/jax/envs/latest/lib/python3.12/site-packages/IPython/core/async_helpers.py", line 119>, 28): loc("_pseudo_sync_runner"("/home/docs/checkouts/readthedocs.org/user_builds/jax/envs/latest/lib/python3.12/site-packages/IPython/core/async_helpers.py":128:8 to :23))}, canonical_name_cache={'/tmp/ipykernel_1806/3275395289.py': '/tmp/ipykernel_1806/3275395289.py', '/tmp/ipykernel_1806/3854395562.py': '/tmp/ipykernel_1806/3854395562.py', '/tmp/ipykernel_1806/1570919344.py': '/tmp/ipykernel_1806/1570919344.py', '/home/docs/checkouts/readthedocs.org/user_builds/jax/envs/latest/lib/python3.12/site-packages/IPython/core/interactiveshell.py': '/home/docs/checkouts/readthedocs.org/user_builds/jax/envs/latest/lib/python3.12/site-packages/IPython/core/interactiveshell.py', '/home/docs/checkouts/readthedocs.org/user_builds/jax/envs/latest/lib/python3.12/site-packages/IPython/core/async_helpers.py': '/home/docs/checkouts/readthedocs.org/user_builds/jax/envs/latest/lib/python3.12/site-packages/IPython/core/async_helpers.py'}, is_user_file_cache={'/home/docs/checkouts/readthedocs.org/user_builds/jax/envs/latest/lib/python3.12/site-packages/jax/_src/source_info_util.py': False, '/home/docs/checkouts/readthedocs.org/user_builds/jax/envs/latest/lib/python3.12/site-packages/jax/_src/interpreters/partial_eval.py': False, '/home/docs/checkouts/readthedocs.org/user_builds/jax/envs/latest/lib/python3.12/site-packages/jax/_src/core.py': False, '/tmp/ipykernel_1806/3275395289.py': True, '/tmp/ipykernel_1806/3854395562.py': True, '/tmp/ipykernel_1806/1570919344.py': True, '/home/docs/checkouts/readthedocs.org/user_builds/jax/envs/latest/lib/python3.12/site-packages/jax/_src/linear_util.py': False, '/home/docs/checkouts/readthedocs.org/user_builds/jax/envs/latest/lib/python3.12/site-packages/jax/_src/api_util.py': False, '/home/docs/checkouts/readthedocs.org/user_builds/jax/envs/latest/lib/python3.12/site-packages/jax/_src/profiler.py': False, '/home/docs/checkouts/readthedocs.org/user_builds/jax/envs/latest/lib/python3.12/site-packages/jax/_src/pjit.py': False, '/home/docs/checkouts/readthedocs.org/user_builds/jax/envs/latest/lib/python3.12/site-packages/jax/_src/traceback_util.py': False, '/home/docs/checkouts/readthedocs.org/user_builds/jax/envs/latest/lib/python3.12/site-packages/IPython/core/interactiveshell.py': True, '/home/docs/checkouts/readthedocs.org/user_builds/jax/envs/latest/lib/python3.12/site-packages/IPython/core/async_helpers.py': True}), lowering_parameters=LoweringParameters(override_lowering_rules=None, global_constant_computation=False, for_export=False, export_ignore_forward_compatibility=False)), name_stack=NameStack(stack=()), traceback=None, primitive=multiply_add, avals_in=(ShapedArray(float32[], weak_type=True), ShapedArray(float32[], weak_type=True), ShapedArray(float32[], weak_type=True)), avals_out=[ShapedArray(float32[])], tokens_in=<jax._src.interpreters.mlir.TokenSet object at 0x71e7d2f999a0>, tokens_out=None, axis_size_env=None, dim_var_values=[], jaxpr_eqn_ctx=JaxprEqnContext(compute_type=None, threefry_partitionable=True, cur_abstract_mesh=AbstractMesh((), axis_types=()), xla_metadata=None), platforms=None), Value(<block argument> of type 'tensor<f32>' at index: 0), Value(<block argument> of type 'tensor<f32>' at index: 1), Value(<block argument> of type 'tensor<f32>' at index: 2))
|<- multiply_add_lowering = [<jaxlib.mlir._mlir_libs._mlir.ir.OpResult object at 0x71e7d2fb8d70>]

下面是 jit 的另一个用法,您只对第一个参数进行编译。请注意 square_add_prim 的第二个参数是如何具体的,这导致 multiply_add_abstract_eval 的第三个参数为 ConcreteArray。请注意,multiply_add_abstract_eval 可以与 ShapedArrayConcreteArray 一起使用。

assert api.jit(lambda x, y: square_add_prim(x, y),
               static_argnums=1)(2., 10.) == 14.
call square_add_prim(JitTracer<~float32[]>, 10.0)
  call multiply_add_prim(JitTracer<~float32[]>, JitTracer<~float32[]>, 10.0)
  |<- multiply_add_prim = JitTracer<float32[]>
|<- square_add_prim = JitTracer<float32[]>
call multiply_add_lowering(LoweringRuleContext(module_context=ModuleContext(context=<jax._src.interpreters.mlir.JaxIrContext object at 0x71e7d2f8abd0>, module=<jaxlib.mlir._mlir_libs._mlir.ir.Module object at 0x71e7d2fb4e00>, ip=<jaxlib.mlir._mlir_libs._mlir.ir.InsertionPoint object at 0x71e7d2fb4da0>, symbol_table=<jaxlib.mlir._mlir_libs._mlir.ir.SymbolTable object at 0x71e7d2f9a820>, platforms=('cpu',), backend=<jaxlib._jax.Client object at 0x71e7eb49fed0>, axis_context=ShardingContext(num_devices=1, device_assignment=None, abstract_mesh=None), keepalives=[], channel_iterator=count(1), host_callbacks=[], shape_poly_state=<jax._src.interpreters.mlir.ShapePolyLoweringState object at 0x71e7d2f9a7e0>, all_default_mem_kind=True, lowering_cache={}, cached_primitive_lowerings={}, traceback_caches=TracebackCaches(traceback_cache={<jaxlib._jax.Traceback object at 0x5b39a652cb60>: loc(callsite("multiply_add_prim"("/tmp/ipykernel_1806/3275395289.py":12:9 to :37) at callsite("trace.<locals>.trace_func.<locals>.func_wrapper"("/tmp/ipykernel_1806/3854395562.py":48:12 to :23) at callsite("square_add_prim"("/tmp/ipykernel_1806/3275395289.py":17:9 to :35) at callsite("trace.<locals>.trace_func.<locals>.func_wrapper"("/tmp/ipykernel_1806/3854395562.py":48:12 to :23) at callsite("<lambda>"("/tmp/ipykernel_1806/1192749400.py":1:28 to :49) at callsite("<module>"("/tmp/ipykernel_1806/1192749400.py":1:7 to 2:41) at callsite("InteractiveShell.run_code"("/home/docs/checkouts/readthedocs.org/user_builds/jax/envs/latest/lib/python3.12/site-packages/IPython/core/interactiveshell.py":3699:20 to :69) at callsite("InteractiveShell.run_ast_nodes"("/home/docs/checkouts/readthedocs.org/user_builds/jax/envs/latest/lib/python3.12/site-packages/IPython/core/interactiveshell.py":3639:19 to :64) at callsite("InteractiveShell.run_cell_async"("/home/docs/checkouts/readthedocs.org/user_builds/jax/envs/latest/lib/python3.12/site-packages/IPython/core/interactiveshell.py":3394:29 to 3395:85) at "_pseudo_sync_runner"("/home/docs/checkouts/readthedocs.org/user_builds/jax/envs/latest/lib/python3.12/site-packages/IPython/core/async_helpers.py":128:8 to :23)))))))))))}, location_cache={(<code object multiply_add_prim at 0x71e7eb35a880, file "/tmp/ipykernel_1806/3275395289.py", line 5>, 44): loc("multiply_add_prim"("/tmp/ipykernel_1806/3275395289.py":12:9 to :37)), (<code object func_wrapper at 0x71e7eb79b470, file "/tmp/ipykernel_1806/3854395562.py", line 45>, 76): loc("trace.<locals>.trace_func.<locals>.func_wrapper"("/tmp/ipykernel_1806/3854395562.py":48:12 to :23)), (<code object square_add_prim at 0x71e7e80f9290, file "/tmp/ipykernel_1806/3275395289.py", line 14>, 24): loc("square_add_prim"("/tmp/ipykernel_1806/3275395289.py":17:9 to :35)), (<code object <lambda> at 0x71e7e80fa5d0, file "/tmp/ipykernel_1806/1192749400.py", line 1>, 22): loc("<lambda>"("/tmp/ipykernel_1806/1192749400.py":1:28 to :49)), (<code object <module> at 0x71e7e810fcc0, file "/tmp/ipykernel_1806/1192749400.py", line 1>, 46): loc("<module>"("/tmp/ipykernel_1806/1192749400.py":1:7 to 2:41)), (<code object run_code at 0x5b39a4873430, file "/home/docs/checkouts/readthedocs.org/user_builds/jax/envs/latest/lib/python3.12/site-packages/IPython/core/interactiveshell.py", line 3663>, 252): loc("InteractiveShell.run_code"("/home/docs/checkouts/readthedocs.org/user_builds/jax/envs/latest/lib/python3.12/site-packages/IPython/core/interactiveshell.py":3699:20 to :69)), (<code object run_ast_nodes at 0x5b39a4a38620, file "/home/docs/checkouts/readthedocs.org/user_builds/jax/envs/latest/lib/python3.12/site-packages/IPython/core/interactiveshell.py", line 3540>, 1132): loc("InteractiveShell.run_ast_nodes"("/home/docs/checkouts/readthedocs.org/user_builds/jax/envs/latest/lib/python3.12/site-packages/IPython/core/interactiveshell.py":3639:19 to :64)), (<code object run_cell_async at 0x5b39a4a3ad00, file "/home/docs/checkouts/readthedocs.org/user_builds/jax/envs/latest/lib/python3.12/site-packages/IPython/core/interactiveshell.py", line 3231>, 1104): loc("InteractiveShell.run_cell_async"("/home/docs/checkouts/readthedocs.org/user_builds/jax/envs/latest/lib/python3.12/site-packages/IPython/core/interactiveshell.py":3394:29 to 3395:85)), (<code object _pseudo_sync_runner at 0x71e8279c1530, file "/home/docs/checkouts/readthedocs.org/user_builds/jax/envs/latest/lib/python3.12/site-packages/IPython/core/async_helpers.py", line 119>, 28): loc("_pseudo_sync_runner"("/home/docs/checkouts/readthedocs.org/user_builds/jax/envs/latest/lib/python3.12/site-packages/IPython/core/async_helpers.py":128:8 to :23))}, canonical_name_cache={'/tmp/ipykernel_1806/3275395289.py': '/tmp/ipykernel_1806/3275395289.py', '/tmp/ipykernel_1806/3854395562.py': '/tmp/ipykernel_1806/3854395562.py', '/tmp/ipykernel_1806/1192749400.py': '/tmp/ipykernel_1806/1192749400.py', '/home/docs/checkouts/readthedocs.org/user_builds/jax/envs/latest/lib/python3.12/site-packages/IPython/core/interactiveshell.py': '/home/docs/checkouts/readthedocs.org/user_builds/jax/envs/latest/lib/python3.12/site-packages/IPython/core/interactiveshell.py', '/home/docs/checkouts/readthedocs.org/user_builds/jax/envs/latest/lib/python3.12/site-packages/IPython/core/async_helpers.py': '/home/docs/checkouts/readthedocs.org/user_builds/jax/envs/latest/lib/python3.12/site-packages/IPython/core/async_helpers.py'}, is_user_file_cache={'/home/docs/checkouts/readthedocs.org/user_builds/jax/envs/latest/lib/python3.12/site-packages/jax/_src/source_info_util.py': False, '/home/docs/checkouts/readthedocs.org/user_builds/jax/envs/latest/lib/python3.12/site-packages/jax/_src/interpreters/partial_eval.py': False, '/home/docs/checkouts/readthedocs.org/user_builds/jax/envs/latest/lib/python3.12/site-packages/jax/_src/core.py': False, '/tmp/ipykernel_1806/3275395289.py': True, '/tmp/ipykernel_1806/3854395562.py': True, '/tmp/ipykernel_1806/1192749400.py': True, '/home/docs/checkouts/readthedocs.org/user_builds/jax/envs/latest/lib/python3.12/site-packages/jax/_src/linear_util.py': False, '/home/docs/checkouts/readthedocs.org/user_builds/jax/envs/latest/lib/python3.12/site-packages/jax/_src/api_util.py': False, '/home/docs/checkouts/readthedocs.org/user_builds/jax/envs/latest/lib/python3.12/site-packages/jax/_src/profiler.py': False, '/home/docs/checkouts/readthedocs.org/user_builds/jax/envs/latest/lib/python3.12/site-packages/jax/_src/pjit.py': False, '/home/docs/checkouts/readthedocs.org/user_builds/jax/envs/latest/lib/python3.12/site-packages/jax/_src/traceback_util.py': False, '/home/docs/checkouts/readthedocs.org/user_builds/jax/envs/latest/lib/python3.12/site-packages/IPython/core/interactiveshell.py': True, '/home/docs/checkouts/readthedocs.org/user_builds/jax/envs/latest/lib/python3.12/site-packages/IPython/core/async_helpers.py': True}), lowering_parameters=LoweringParameters(override_lowering_rules=None, global_constant_computation=False, for_export=False, export_ignore_forward_compatibility=False)), name_stack=NameStack(stack=()), traceback=None, primitive=multiply_add, avals_in=(ShapedArray(float32[], weak_type=True), ShapedArray(float32[], weak_type=True), ShapedArray(float32[], weak_type=True)), avals_out=[ShapedArray(float32[])], tokens_in=<jax._src.interpreters.mlir.TokenSet object at 0x71e7d2f9b290>, tokens_out=None, axis_size_env=None, dim_var_values=[], jaxpr_eqn_ctx=JaxprEqnContext(compute_type=None, threefry_partitionable=True, cur_abstract_mesh=AbstractMesh((), axis_types=()), xla_metadata=None), platforms=None), Value(<block argument> of type 'tensor<f32>' at index: 0), Value(<block argument> of type 'tensor<f32>' at index: 1), Value(<block argument> of type 'tensor<f32>' at index: 2))
|<- multiply_add_lowering = [<jaxlib.mlir._mlir_libs._mlir.ir.OpResult object at 0x71e7d2fbb1b0>]

前向微分#

JAX 以雅可比向量积(JVP)的形式实现了前向微分(您可以在高级自动微分中了解更多信息)。

如果您尝试计算 jvp 函数,将会收到错误,因为您尚未告知 JAX 如何对 multiply_add primitive 进行微分。

# The second argument is set to `(2., 10.)` values where you
# evaluate the Jacobian, and the third argument `(1., 1.)`
# contains the values of the tangents for the arguments.
with expectNotImplementedError():
  api.jvp(square_add_prim, (2., 10.), (1., 1.))
call square_add_prim(Traced<~float32[]>, Traced<~float32[]>)
  call multiply_add_prim(Traced<~float32[]>, Traced<~float32[]>, Traced<~float32[]>)

Found expected exception:
Traceback (most recent call last):
  File "/tmp/ipykernel_1806/459539105.py", line 5, in <module>
    api.jvp(square_add_prim, (2., 10.), (1., 1.))
  File "/home/docs/checkouts/readthedocs.org/user_builds/jax/envs/latest/lib/python3.12/site-packages/jax/_src/traceback_util.py", line 180, in reraise_with_filtered_traceback
    return fun(*args, **kwargs)
           ^^^^^^^^^^^^^^^^^^^^
  File "/home/docs/checkouts/readthedocs.org/user_builds/jax/envs/latest/lib/python3.12/site-packages/jax/_src/api.py", line 1849, in jvp
    return _jvp(lu.wrap_init(fun, debug_info=debug_info("jvp", fun, primals, {})),
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
NotImplementedError: Differentiation rule for 'multiply_add' not implemented
from jax.interpreters import ad

@trace("multiply_add_value_and_jvp")
def multiply_add_value_and_jvp(arg_values, arg_tangents):
  """Evaluates the primal output and the tangents (Jacobian-vector product).

  Given values of the arguments and perturbation of the arguments (tangents),
  compute the output of the primitive and the perturbation of the output.

  This method must be JAX-traceable. JAX may invoke it with abstract values
  for the arguments and tangents.

  Args:
    arg_values: A tuple of arguments
    arg_tangents: A tuple with the tangents of the arguments. The tuple has
      the same length as the arg_values. Some of the tangents may also be the
      special value `ad.Zero` to specify a zero tangent

  Returns:
     A pair of the primal output and the tangent.
  """
  x, y, z = arg_values
  xt, yt, zt = arg_tangents
  _trace("Primal evaluation:")
  # Now, you have a JAX-traceable computation of the output.
  # Normally, you can use the multiply add (`ma`) primitive itself to compute the primal output.
  primal_out = multiply_add_prim(x, y, z)

  _trace("Tangent evaluation:")
  # You must use a JAX-traceable way to compute the tangent. It turns out that
  # the output tangent can be computed as (xt * y + x * yt + zt),
  # which you can implement in a JAX-traceable way using the same "multiply_add_prim" primitive.

  # You do need to deal specially with `Zero`. Here, you just turn it into a
  # proper tensor of 0s (of the same shape as 'x').
  # An alternative would be to check for `Zero` and perform algebraic
  # simplification of the output tangent computation.
  def make_zero(tan):
    return lax.zeros_like_array(x) if type(tan) is ad.Zero else tan

  output_tangent = multiply_add_prim(make_zero(xt), y, multiply_add_prim(x, make_zero(yt), make_zero(zt)))
  return (primal_out, output_tangent)

# Register the forward differentiation rule with JAX:
ad.primitive_jvps[multiply_add_p] = multiply_add_value_and_jvp
# Tangent is: xt*y + x*yt + zt = 1.*2. + 2.*1. + 1. = 5.
assert api.jvp(square_add_prim, (2., 10.), (1., 1.)) == (14., 5.)
call square_add_prim(Traced<~float32[]>, Traced<~float32[]>)
  call multiply_add_prim(Traced<~float32[]>, Traced<~float32[]>, Traced<~float32[]>)
    call multiply_add_value_and_jvp((2.0, 2.0, 10.0), (1.0, 1.0, 1.0))
      Primal evaluation:
      call multiply_add_prim(2.0, 2.0, 10.0)
        call multiply_add_impl(2.0, 2.0, 10.0)
        |<- multiply_add_impl = 14.0
      |<- multiply_add_prim = 14.0
      Tangent evaluation:
      call multiply_add_prim(2.0, 1.0, 1.0)
        call multiply_add_impl(2.0, 1.0, 1.0)
        |<- multiply_add_impl = 3.0
      |<- multiply_add_prim = 3.0
      call multiply_add_prim(1.0, 2.0, 3.0)
        call multiply_add_impl(1.0, 2.0, 3.0)
        |<- multiply_add_impl = 5.0
      |<- multiply_add_prim = 5.0
    |<- multiply_add_value_and_jvp = (14.0, 5.0)
  |<- multiply_add_prim = Traced<float32[]>
|<- square_add_prim = Traced<float32[]>

前向微分的 JIT 编译#

您可以将 jit 应用于前向微分函数

assert api.jit(lambda arg_values, arg_tangents:
                   api.jvp(square_add_prim, arg_values, arg_tangents))(
         (2., 10.), (1., 1.)) == (14., 5.)
call square_add_prim(Traced<~float32[]>, Traced<~float32[]>)
  call multiply_add_prim(Traced<~float32[]>, Traced<~float32[]>, Traced<~float32[]>)
    call multiply_add_value_and_jvp((JitTracer<~float32[]>, JitTracer<~float32[]>, JitTracer<~float32[]>), (JitTracer<~float32[]>, JitTracer<~float32[]>, JitTracer<~float32[]>))
      Primal evaluation:
      call multiply_add_prim(JitTracer<~float32[]>, JitTracer<~float32[]>, JitTracer<~float32[]>)
      |<- multiply_add_prim = JitTracer<float32[]>
      Tangent evaluation:
      call multiply_add_prim(JitTracer<~float32[]>, JitTracer<~float32[]>, JitTracer<~float32[]>)
      |<- multiply_add_prim = JitTracer<float32[]>
      call multiply_add_prim(JitTracer<~float32[]>, JitTracer<~float32[]>, JitTracer<float32[]>)
        call multiply_add_abstract_eval(~float32[], ~float32[], float32[])
        |<- multiply_add_abstract_eval = float32[]
      |<- multiply_add_prim = JitTracer<float32[]>
    |<- multiply_add_value_and_jvp = (JitTracer<float32[]>, JitTracer<float32[]>)
  |<- multiply_add_prim = Traced<float32[]>
|<- square_add_prim = Traced<float32[]>
call multiply_add_lowering(LoweringRuleContext(module_context=ModuleContext(context=<jax._src.interpreters.mlir.JaxIrContext object at 0x71e7d2ffa5d0>, module=<jaxlib.mlir._mlir_libs._mlir.ir.Module object at 0x71e7d2ff3330>, ip=<jaxlib.mlir._mlir_libs._mlir.ir.InsertionPoint object at 0x71e7d2ff32d0>, symbol_table=<jaxlib.mlir._mlir_libs._mlir.ir.SymbolTable object at 0x71e7d2e000c0>, platforms=('cpu',), backend=<jaxlib._jax.Client object at 0x71e7eb49fed0>, axis_context=ShardingContext(num_devices=1, device_assignment=None, abstract_mesh=None), keepalives=[], channel_iterator=count(1), host_callbacks=[], shape_poly_state=<jax._src.interpreters.mlir.ShapePolyLoweringState object at 0x71e7d2e00290>, all_default_mem_kind=True, lowering_cache={}, cached_primitive_lowerings={}, traceback_caches=TracebackCaches(traceback_cache={<jaxlib._jax.Traceback object at 0x5b39a6e03880>: loc(callsite("multiply_add_prim"("/tmp/ipykernel_1806/3275395289.py":12:9 to :37) at callsite("trace.<locals>.trace_func.<locals>.func_wrapper"("/tmp/ipykernel_1806/3854395562.py":48:12 to :23) at callsite("multiply_add_value_and_jvp"("/tmp/ipykernel_1806/3621920682.py":27:15 to :41) at callsite("trace.<locals>.trace_func.<locals>.func_wrapper"("/tmp/ipykernel_1806/3854395562.py":48:12 to :23) at callsite("multiply_add_prim"("/tmp/ipykernel_1806/3275395289.py":12:9 to :37) at callsite("trace.<locals>.trace_func.<locals>.func_wrapper"("/tmp/ipykernel_1806/3854395562.py":48:12 to :23) at callsite("square_add_prim"("/tmp/ipykernel_1806/3275395289.py":17:9 to :35) at callsite("trace.<locals>.trace_func.<locals>.func_wrapper"("/tmp/ipykernel_1806/3854395562.py":48:12 to :23) at callsite("<lambda>"("/tmp/ipykernel_1806/1223862052.py":2:19 to :69) at "<module>"("/tmp/ipykernel_1806/1223862052.py":1:7 to 3:29)))))))))))}, location_cache={(<code object multiply_add_prim at 0x71e7eb35a880, file "/tmp/ipykernel_1806/3275395289.py", line 5>, 44): loc("multiply_add_prim"("/tmp/ipykernel_1806/3275395289.py":12:9 to :37)), (<code object func_wrapper at 0x71e7eb79b470, file "/tmp/ipykernel_1806/3854395562.py", line 45>, 76): loc("trace.<locals>.trace_func.<locals>.func_wrapper"("/tmp/ipykernel_1806/3854395562.py":48:12 to :23)), (<code object multiply_add_value_and_jvp at 0x71e7e8aafb90, file "/tmp/ipykernel_1806/3621920682.py", line 3>, 72): loc("multiply_add_value_and_jvp"("/tmp/ipykernel_1806/3621920682.py":27:15 to :41)), (<code object square_add_prim at 0x71e7e80f9290, file "/tmp/ipykernel_1806/3275395289.py", line 14>, 24): loc("square_add_prim"("/tmp/ipykernel_1806/3275395289.py":17:9 to :35)), (<code object <lambda> at 0x71e7e8a57930, file "/tmp/ipykernel_1806/1223862052.py", line 1>, 52): loc("<lambda>"("/tmp/ipykernel_1806/1223862052.py":2:19 to :69)), (<code object <module> at 0x71e7e8a57e30, file "/tmp/ipykernel_1806/1223862052.py", line 1>, 42): loc("<module>"("/tmp/ipykernel_1806/1223862052.py":1:7 to 3:29))}, canonical_name_cache={'/tmp/ipykernel_1806/3275395289.py': '/tmp/ipykernel_1806/3275395289.py', '/tmp/ipykernel_1806/3854395562.py': '/tmp/ipykernel_1806/3854395562.py', '/tmp/ipykernel_1806/3621920682.py': '/tmp/ipykernel_1806/3621920682.py', '/tmp/ipykernel_1806/1223862052.py': '/tmp/ipykernel_1806/1223862052.py'}, is_user_file_cache={'/home/docs/checkouts/readthedocs.org/user_builds/jax/envs/latest/lib/python3.12/site-packages/jax/_src/source_info_util.py': False, '/home/docs/checkouts/readthedocs.org/user_builds/jax/envs/latest/lib/python3.12/site-packages/jax/_src/interpreters/partial_eval.py': False, '/home/docs/checkouts/readthedocs.org/user_builds/jax/envs/latest/lib/python3.12/site-packages/jax/_src/core.py': False, '/tmp/ipykernel_1806/3275395289.py': True, '/tmp/ipykernel_1806/3854395562.py': True, '/tmp/ipykernel_1806/3621920682.py': True, '/home/docs/checkouts/readthedocs.org/user_builds/jax/envs/latest/lib/python3.12/site-packages/jax/_src/interpreters/ad.py': False, '/home/docs/checkouts/readthedocs.org/user_builds/jax/envs/latest/lib/python3.12/site-packages/jax/_src/linear_util.py': False, '/home/docs/checkouts/readthedocs.org/user_builds/jax/envs/latest/lib/python3.12/site-packages/jax/_src/api_util.py': False, '/home/docs/checkouts/readthedocs.org/user_builds/jax/envs/latest/lib/python3.12/site-packages/jax/_src/api.py': False, '/home/docs/checkouts/readthedocs.org/user_builds/jax/envs/latest/lib/python3.12/site-packages/jax/_src/traceback_util.py': False, '/tmp/ipykernel_1806/1223862052.py': True, '/home/docs/checkouts/readthedocs.org/user_builds/jax/envs/latest/lib/python3.12/site-packages/jax/_src/profiler.py': False, '/home/docs/checkouts/readthedocs.org/user_builds/jax/envs/latest/lib/python3.12/site-packages/jax/_src/pjit.py': False}), lowering_parameters=LoweringParameters(override_lowering_rules=None, global_constant_computation=False, for_export=False, export_ignore_forward_compatibility=False)), name_stack=NameStack(stack=()), traceback=None, primitive=multiply_add, avals_in=(ShapedArray(float32[], weak_type=True), ShapedArray(float32[], weak_type=True), ShapedArray(float32[], weak_type=True)), avals_out=[ShapedArray(float32[])], tokens_in=<jax._src.interpreters.mlir.TokenSet object at 0x71e7d2e00ef0>, tokens_out=None, axis_size_env=None, dim_var_values=[], jaxpr_eqn_ctx=JaxprEqnContext(compute_type=None, threefry_partitionable=True, cur_abstract_mesh=AbstractMesh((), axis_types=()), xla_metadata=None), platforms=None), Value(<block argument> of type 'tensor<f32>' at index: 0), Value(<block argument> of type 'tensor<f32>' at index: 1), Value(<block argument> of type 'tensor<f32>' at index: 2))
|<- multiply_add_lowering = [<jaxlib.mlir._mlir_libs._mlir.ir.OpResult object at 0x71e7d2e046b0>]
call multiply_add_lowering(LoweringRuleContext(module_context=ModuleContext(context=<jax._src.interpreters.mlir.JaxIrContext object at 0x71e7d2ffa5d0>, module=<jaxlib.mlir._mlir_libs._mlir.ir.Module object at 0x71e7d2ff3330>, ip=<jaxlib.mlir._mlir_libs._mlir.ir.InsertionPoint object at 0x71e7d2ff32d0>, symbol_table=<jaxlib.mlir._mlir_libs._mlir.ir.SymbolTable object at 0x71e7d2e000c0>, platforms=('cpu',), backend=<jaxlib._jax.Client object at 0x71e7eb49fed0>, axis_context=ShardingContext(num_devices=1, device_assignment=None, abstract_mesh=None), keepalives=[], channel_iterator=count(1), host_callbacks=[], shape_poly_state=<jax._src.interpreters.mlir.ShapePolyLoweringState object at 0x71e7d2e00290>, all_default_mem_kind=True, lowering_cache={LoweringCacheKey(primitive=multiply_add, eqn_ctx=JaxprEqnContext(compute_type=None, threefry_partitionable=True, cur_abstract_mesh=AbstractMesh((), axis_types=()), xla_metadata=None), avals_in=(ShapedArray(float32[], weak_type=True), ShapedArray(float32[], weak_type=True), ShapedArray(float32[], weak_type=True)), effects=frozenset(), params=FrozenDict({}), platforms=('cpu',)): LoweringCacheValue(func=<jaxlib.mlir.dialects.func.FuncOp object at 0x71e7d2ff33e0>, output_types=[RankedTensorType(tensor<f32>)], inline=True)}, cached_primitive_lowerings={}, traceback_caches=TracebackCaches(traceback_cache={<jaxlib._jax.Traceback object at 0x5b39a6e03880>: loc(callsite("multiply_add_prim"("/tmp/ipykernel_1806/3275395289.py":12:9 to :37) at callsite("trace.<locals>.trace_func.<locals>.func_wrapper"("/tmp/ipykernel_1806/3854395562.py":48:12 to :23) at callsite("multiply_add_value_and_jvp"("/tmp/ipykernel_1806/3621920682.py":27:15 to :41) at callsite("trace.<locals>.trace_func.<locals>.func_wrapper"("/tmp/ipykernel_1806/3854395562.py":48:12 to :23) at callsite("multiply_add_prim"("/tmp/ipykernel_1806/3275395289.py":12:9 to :37) at callsite("trace.<locals>.trace_func.<locals>.func_wrapper"("/tmp/ipykernel_1806/3854395562.py":48:12 to :23) at callsite("square_add_prim"("/tmp/ipykernel_1806/3275395289.py":17:9 to :35) at callsite("trace.<locals>.trace_func.<locals>.func_wrapper"("/tmp/ipykernel_1806/3854395562.py":48:12 to :23) at callsite("<lambda>"("/tmp/ipykernel_1806/1223862052.py":2:19 to :69) at "<module>"("/tmp/ipykernel_1806/1223862052.py":1:7 to 3:29))))))))))), <jaxlib._jax.Traceback object at 0x5b39a61f8f90>: loc(callsite("multiply_add_prim"("/tmp/ipykernel_1806/3275395289.py":12:9 to :37) at callsite("trace.<locals>.trace_func.<locals>.func_wrapper"("/tmp/ipykernel_1806/3854395562.py":48:12 to :23) at callsite("multiply_add_value_and_jvp"("/tmp/ipykernel_1806/3621920682.py":41:55 to :105) at callsite("trace.<locals>.trace_func.<locals>.func_wrapper"("/tmp/ipykernel_1806/3854395562.py":48:12 to :23) at callsite("multiply_add_prim"("/tmp/ipykernel_1806/3275395289.py":12:9 to :37) at callsite("trace.<locals>.trace_func.<locals>.func_wrapper"("/tmp/ipykernel_1806/3854395562.py":48:12 to :23) at callsite("square_add_prim"("/tmp/ipykernel_1806/3275395289.py":17:9 to :35) at callsite("trace.<locals>.trace_func.<locals>.func_wrapper"("/tmp/ipykernel_1806/3854395562.py":48:12 to :23) at callsite("<lambda>"("/tmp/ipykernel_1806/1223862052.py":2:19 to :69) at "<module>"("/tmp/ipykernel_1806/1223862052.py":1:7 to 3:29))))))))))), <jaxlib._jax.Traceback object at 0x5b39a61bca80>: loc(callsite("multiply_add_prim"("/tmp/ipykernel_1806/3275395289.py":12:9 to :37) at callsite("trace.<locals>.trace_func.<locals>.func_wrapper"("/tmp/ipykernel_1806/3854395562.py":48:12 to :23) at callsite("multiply_add_value_and_jvp"("/tmp/ipykernel_1806/3621920682.py":41:19 to :106) at callsite("trace.<locals>.trace_func.<locals>.func_wrapper"("/tmp/ipykernel_1806/3854395562.py":48:12 to :23) at callsite("multiply_add_prim"("/tmp/ipykernel_1806/3275395289.py":12:9 to :37) at callsite("trace.<locals>.trace_func.<locals>.func_wrapper"("/tmp/ipykernel_1806/3854395562.py":48:12 to :23) at callsite("square_add_prim"("/tmp/ipykernel_1806/3275395289.py":17:9 to :35) at callsite("trace.<locals>.trace_func.<locals>.func_wrapper"("/tmp/ipykernel_1806/3854395562.py":48:12 to :23) at callsite("<lambda>"("/tmp/ipykernel_1806/1223862052.py":2:19 to :69) at "<module>"("/tmp/ipykernel_1806/1223862052.py":1:7 to 3:29)))))))))))}, location_cache={(<code object multiply_add_prim at 0x71e7eb35a880, file "/tmp/ipykernel_1806/3275395289.py", line 5>, 44): loc("multiply_add_prim"("/tmp/ipykernel_1806/3275395289.py":12:9 to :37)), (<code object func_wrapper at 0x71e7eb79b470, file "/tmp/ipykernel_1806/3854395562.py", line 45>, 76): loc("trace.<locals>.trace_func.<locals>.func_wrapper"("/tmp/ipykernel_1806/3854395562.py":48:12 to :23)), (<code object multiply_add_value_and_jvp at 0x71e7e8aafb90, file "/tmp/ipykernel_1806/3621920682.py", line 3>, 72): loc("multiply_add_value_and_jvp"("/tmp/ipykernel_1806/3621920682.py":27:15 to :41)), (<code object square_add_prim at 0x71e7e80f9290, file "/tmp/ipykernel_1806/3275395289.py", line 14>, 24): loc("square_add_prim"("/tmp/ipykernel_1806/3275395289.py":17:9 to :35)), (<code object <lambda> at 0x71e7e8a57930, file "/tmp/ipykernel_1806/1223862052.py", line 1>, 52): loc("<lambda>"("/tmp/ipykernel_1806/1223862052.py":2:19 to :69)), (<code object <module> at 0x71e7e8a57e30, file "/tmp/ipykernel_1806/1223862052.py", line 1>, 42): loc("<module>"("/tmp/ipykernel_1806/1223862052.py":1:7 to 3:29)), (<code object multiply_add_value_and_jvp at 0x71e7e8aafb90, file "/tmp/ipykernel_1806/3621920682.py", line 3>, 180): loc("multiply_add_value_and_jvp"("/tmp/ipykernel_1806/3621920682.py":41:55 to :105)), (<code object multiply_add_value_and_jvp at 0x71e7e8aafb90, file "/tmp/ipykernel_1806/3621920682.py", line 3>, 188): loc("multiply_add_value_and_jvp"("/tmp/ipykernel_1806/3621920682.py":41:19 to :106))}, canonical_name_cache={'/tmp/ipykernel_1806/3275395289.py': '/tmp/ipykernel_1806/3275395289.py', '/tmp/ipykernel_1806/3854395562.py': '/tmp/ipykernel_1806/3854395562.py', '/tmp/ipykernel_1806/3621920682.py': '/tmp/ipykernel_1806/3621920682.py', '/tmp/ipykernel_1806/1223862052.py': '/tmp/ipykernel_1806/1223862052.py'}, is_user_file_cache={'/home/docs/checkouts/readthedocs.org/user_builds/jax/envs/latest/lib/python3.12/site-packages/jax/_src/source_info_util.py': False, '/home/docs/checkouts/readthedocs.org/user_builds/jax/envs/latest/lib/python3.12/site-packages/jax/_src/interpreters/partial_eval.py': False, '/home/docs/checkouts/readthedocs.org/user_builds/jax/envs/latest/lib/python3.12/site-packages/jax/_src/core.py': False, '/tmp/ipykernel_1806/3275395289.py': True, '/tmp/ipykernel_1806/3854395562.py': True, '/tmp/ipykernel_1806/3621920682.py': True, '/home/docs/checkouts/readthedocs.org/user_builds/jax/envs/latest/lib/python3.12/site-packages/jax/_src/interpreters/ad.py': False, '/home/docs/checkouts/readthedocs.org/user_builds/jax/envs/latest/lib/python3.12/site-packages/jax/_src/linear_util.py': False, '/home/docs/checkouts/readthedocs.org/user_builds/jax/envs/latest/lib/python3.12/site-packages/jax/_src/api_util.py': False, '/home/docs/checkouts/readthedocs.org/user_builds/jax/envs/latest/lib/python3.12/site-packages/jax/_src/api.py': False, '/home/docs/checkouts/readthedocs.org/user_builds/jax/envs/latest/lib/python3.12/site-packages/jax/_src/traceback_util.py': False, '/tmp/ipykernel_1806/1223862052.py': True, '/home/docs/checkouts/readthedocs.org/user_builds/jax/envs/latest/lib/python3.12/site-packages/jax/_src/profiler.py': False, '/home/docs/checkouts/readthedocs.org/user_builds/jax/envs/latest/lib/python3.12/site-packages/jax/_src/pjit.py': False}), lowering_parameters=LoweringParameters(override_lowering_rules=None, global_constant_computation=False, for_export=False, export_ignore_forward_compatibility=False)), name_stack=NameStack(stack=()), traceback=None, primitive=multiply_add, avals_in=(ShapedArray(float32[], weak_type=True), ShapedArray(float32[], weak_type=True), ShapedArray(float32[])), avals_out=[ShapedArray(float32[])], tokens_in=<jax._src.interpreters.mlir.TokenSet object at 0x71e7d2e01190>, tokens_out=None, axis_size_env=None, dim_var_values=[], jaxpr_eqn_ctx=JaxprEqnContext(compute_type=None, threefry_partitionable=True, cur_abstract_mesh=AbstractMesh((), axis_types=()), xla_metadata=None), platforms=None), Value(<block argument> of type 'tensor<f32>' at index: 0), Value(<block argument> of type 'tensor<f32>' at index: 1), Value(<block argument> of type 'tensor<f32>' at index: 2))
|<- multiply_add_lowering = [<jaxlib.mlir._mlir_libs._mlir.ir.OpResult object at 0x71e7e80cc0f0>]

请注意,首先,您抽象地评估 multiply_add_value_and_jvp,这反过来又抽象地评估了 primal 和 tangent 求值(共 3 次调用 ma primitive)。然后,您编译该 primitive 的 3 个出现位置。

反向微分#

如果您现在尝试使用反向微分,您会注意到 JAX 首先使用 multiply_add_value_and_jvp 计算抽象值的前向微分,但随后会遇到 NotImplementedError

在计算反向微分时,JAX 首先对前向微分代码 multiply_add_value_and_jvp 执行抽象求值,以获取计算输出切线的 primitives 追踪。

  • 请注意,JAX 使用具体值进行微分点,并使用抽象值进行切线来执行此抽象求值。

  • 请注意,JAX 为与 ma 的第三个参数对应的切线使用了特殊的抽象切线值 Zero。这反映了您不对 square_add_prim 的第二个参数进行微分,该参数流向 multiply_add_prim 的第三个参数。

  • 另请注意,在切线的抽象求值过程中,您将值 0.0 作为第三个参数的切线传入。这是因为在 multiply_add_value_and_jvp 的定义中使用了 make_zero 函数。

# This is reverse differentiation w.r.t. the first argument of `square_add_prim`
with expectNotImplementedError():
  api.grad(square_add_prim)(2., 10.)
call square_add_prim(LinearizeTracer<~float32[]>, 10.0)
  call multiply_add_prim(LinearizeTracer<~float32[]>, LinearizeTracer<~float32[]>, 10.0)
    call multiply_add_value_and_jvp((2.0, 2.0, 10.0), (Traced<~float32[]>, Traced<~float32[]>, Zero(~float32[])))
      Primal evaluation:
      call multiply_add_prim(2.0, 2.0, 10.0)
        call multiply_add_impl(2.0, 2.0, 10.0)
        |<- multiply_add_impl = 14.0
      |<- multiply_add_prim = 14.0
      Tangent evaluation:
      call multiply_add_prim(2.0, Traced<~float32[]>, 0.0)
        call multiply_add_abstract_eval(~float32[], ~float32[], ~float32[])
        |<- multiply_add_abstract_eval = float32[]
      |<- multiply_add_prim = Traced<float32[]>
      call multiply_add_prim(Traced<~float32[]>, 2.0, Traced<float32[]>)
        call multiply_add_abstract_eval(~float32[], ~float32[], float32[])
        |<- multiply_add_abstract_eval = float32[]
      |<- multiply_add_prim = Traced<float32[]>
    |<- multiply_add_value_and_jvp = (14.0, Traced<float32[]>)
    call multiply_add_abstract_eval(~float32[], ~float32[], ~float32[])
    |<- multiply_add_abstract_eval = float32[]
    call multiply_add_abstract_eval(~float32[], ~float32[], float32[])
    |<- multiply_add_abstract_eval = float32[]
  |<- multiply_add_prim = LinearizeTracer<float32[]>
|<- square_add_prim = LinearizeTracer<float32[]>

Found expected exception:
Traceback (most recent call last):
  File "/home/docs/checkouts/readthedocs.org/user_builds/jax/envs/latest/lib/python3.12/site-packages/jax/_src/interpreters/ad.py", line 487, in get_primitive_transpose
    return primitive_transposes[p]
           ~~~~~~~~~~~~~~~~~~~~^^^
KeyError: multiply_add

The above exception was the direct cause of the following exception:

Traceback (most recent call last):
  File "<frozen runpy>", line 198, in _run_module_as_main
  File "<frozen runpy>", line 88, in _run_code
  File "/home/docs/checkouts/readthedocs.org/user_builds/jax/envs/latest/lib/python3.12/site-packages/ipykernel_launcher.py", line 18, in <module>
    app.launch_new_instance()
jax._src.source_info_util.JaxStackTraceBeforeTransformation: NotImplementedError: Transpose rule (for reverse-mode differentiation) for 'multiply_add' not implemented

The preceding stack trace is the source of the JAX operation that, once transformed by JAX, triggered the following exception.

--------------------

The above exception was the direct cause of the following exception:

Traceback (most recent call last):
  File "/tmp/ipykernel_1806/2155094905.py", line 3, in <module>
    api.grad(square_add_prim)(2., 10.)
  File "/home/docs/checkouts/readthedocs.org/user_builds/jax/envs/latest/lib/python3.12/site-packages/jax/_src/traceback_util.py", line 180, in reraise_with_filtered_traceback
    return fun(*args, **kwargs)
           ^^^^^^^^^^^^^^^^^^^^
  File "/home/docs/checkouts/readthedocs.org/user_builds/jax/envs/latest/lib/python3.12/site-packages/jax/_src/api.py", line 406, in grad_f
    _, g = value_and_grad_f(*args, **kwargs)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
NotImplementedError: Transpose rule (for reverse-mode differentiation) for 'multiply_add' not implemented

上述错误是因为 JAX 缺少一个部分,无法使用前向微分代码计算反向微分。

转置#

如前所述,在计算反向微分时,JAX 会获得一个使用前向微分计算切线的 primitives 追踪。然后,**JAX 会抽象地向后解释此追踪**,并对每个 primitive 应用一个**转置规则**。

为了理解正在发生的事情,考虑一个更简单的函数示例 f(x, y) = x * y + y。假设您需要在点 (2., 4.) 处进行微分。JAX 将根据输入 xtyt 的切线,生成 ft 的以下 JVP 切线计算:

   a = xt * 4.
   b = 2. * yt
   c = a + b
   ft = c + yt

根据构造,切线计算始终是输入切线的线性函数。切线计算中可能出现的唯一非线性运算符是乘法,但此时其中一个操作数是常数。

JAX 将通过反向处理 JVP 计算来生成反向微分计算。对于切线计算中的每个操作,它会使用操作结果的伴随切线来累积该操作所用变量的伴随切线

  # Initialize cotangents of inputs and intermediate variables:
  xct = yct = act = bct = cct = 0.
  # Initialize cotangent of the output:
  fct = 1.
  # Process `ft = c + yt`:
  cct += fct
  yct += fct
  # Process `c = a + b`:
  act += cct
  bct += cct
  # Process `b = 2. * yt`:
  yct += 2. * bct
  # Process `a = xt * 4.`:
  xct += act * 4.

可以验证此计算产生 xct = 4.yct = 3.,它们是函数 f 的偏导数。

JAX 知道 JVP 计算中可能出现的每个 primitive 如何进行转置。概念上,如果 primitive p(x, y, z)x 值为常数时,其参数 yz 是线性的,例如 p(x, y, z) = y*cy + z*cz,那么该 primitive 的转置是

p_transpose(out_ct, x, _, _) = (None, out_ct*cy, out_ct*cz)

请注意,p_transpose 接受 primitive 输出的伴随切线以及与 primitive 的每个参数对应的值。对于线性参数,转置会得到一个未定义的 _ 值,而对于其他参数则得到实际的常数。转置会为 primitive 的每个参数返回一个伴随切线值,对于常数参数则返回 None 值。

具体来说

 add_transpose(out_ct, _, _) = (out_ct, out_ct)
 mult_transpose(out_ct, x, _) = (None, x * out_ct)
 mult_transpose(out_ct, _, y) = (out_ct * y, None)
@trace("multiply_add_transpose")
def multiply_add_transpose(ct, x, y, z):
  """Evaluates the transpose of a linear primitive.

  This method is only used when computing the backward gradient following
  `value_and_jvp`, and is only needed for primitives that are used in the JVP
  calculation for some other primitive. You need a transposition for `multiply_add_prim`,
  because you have used `multiply_add_prim` in the computation of the `output_tangent` in
  `multiply_add_value_and_jvp`.

  In this case, multiply_add is not a linear primitive. However, it is used linearly
  w.r.t. tangents in `multiply_add_value_and_jvp`:
       `output_tangent(xt, yt, zt) = multiply_add_prim(xt, y, multiply_add_prim(x, yt, zt))`.

  Always one of the first two multiplicative arguments is a constant.

  Args:
      ct: The cotangent of the output of the primitive.
      x, y, z: The values of the arguments. The arguments that are used linearly
        get an ad.UndefinedPrimal value. The other arguments get a constant
        value.

  Returns:
      A tuple with the cotangent of the inputs, with the value None
      corresponding to the constant arguments.
  """
  if not ad.is_undefined_primal(x):
    # This use of multiply_add is with a constant "x".
    assert ad.is_undefined_primal(y)
    ct_y = ad.Zero(y.aval) if type(ct) is ad.Zero else multiply_add_prim(x, ct, lax.zeros_like_array(x))
    res = None, ct_y, ct
  else:
    # This use of multiply_add is with a constant "y".
    assert ad.is_undefined_primal(x)
    ct_x = ad.Zero(x.aval) if type(ct) is ad.Zero else multiply_add_prim(ct, y, lax.zeros_like_array(y))
    res = ct_x, None, ct
  return res

ad.primitive_transposes[multiply_add_p] = multiply_add_transpose

现在您可以完成 grad 的运行

assert api.grad(square_add_prim)(2., 10.) == 4.
call square_add_prim(LinearizeTracer<~float32[]>, 10.0)
  call multiply_add_prim(LinearizeTracer<~float32[]>, LinearizeTracer<~float32[]>, 10.0)
    call multiply_add_value_and_jvp((2.0, 2.0, 10.0), (Traced<~float32[]>, Traced<~float32[]>, Zero(~float32[])))
      Primal evaluation:
      call multiply_add_prim(2.0, 2.0, 10.0)
        call multiply_add_impl(2.0, 2.0, 10.0)
        |<- multiply_add_impl = 14.0
      |<- multiply_add_prim = 14.0
      Tangent evaluation:
      call multiply_add_prim(2.0, Traced<~float32[]>, 0.0)
        call multiply_add_abstract_eval(~float32[], ~float32[], ~float32[])
        |<- multiply_add_abstract_eval = float32[]
      |<- multiply_add_prim = Traced<float32[]>
      call multiply_add_prim(Traced<~float32[]>, 2.0, Traced<float32[]>)
        call multiply_add_abstract_eval(~float32[], ~float32[], float32[])
        |<- multiply_add_abstract_eval = float32[]
      |<- multiply_add_prim = Traced<float32[]>
    |<- multiply_add_value_and_jvp = (14.0, Traced<float32[]>)
  |<- multiply_add_prim = LinearizeTracer<float32[]>
|<- square_add_prim = LinearizeTracer<float32[]>
call multiply_add_transpose(1.0, UndefinedPrimal(~float32[]), 2.0, UndefinedPrimal(float32[]))
  call multiply_add_prim(1.0, 2.0, 0.0)
    call multiply_add_impl(1.0, 2.0, 0.0)
    |<- multiply_add_impl = 2.0
  |<- multiply_add_prim = 2.0
|<- multiply_add_transpose = (2.0, None, 1.0)
call multiply_add_transpose(1.0, 2.0, UndefinedPrimal(~float32[]), 0.0)
  call multiply_add_prim(2.0, 1.0, 0.0)
    call multiply_add_impl(2.0, 1.0, 0.0)
    |<- multiply_add_impl = 2.0
  |<- multiply_add_prim = 2.0
|<- multiply_add_transpose = (None, 2.0, 1.0)

请注意对 multiply_add_transpose 的两次调用。它们对应于 multiply_add_value_and_jvp 中计算 output_tangentmultiply_add_prim 的两次使用。第一次转置调用对应于 multiply_add_prim 的最后一次使用:multiply_add_prim(xt, y, ...),其中 y 是常数 2.0

反向微分的 JIT 编译#

请注意,multiply_add_value_and_jvp 的抽象求值只使用了抽象值。与此同时,在没有 JIT 的情况下,您使用了 ConcreteArray

assert api.jit(api.grad(square_add_prim))(2., 10.) == 4.
call square_add_prim(LinearizeTracer<~float32[]>, JitTracer<~float32[]>)
  call multiply_add_prim(LinearizeTracer<~float32[]>, LinearizeTracer<~float32[]>, JitTracer<~float32[]>)
    call multiply_add_value_and_jvp((JitTracer<~float32[]>, JitTracer<~float32[]>, JitTracer<~float32[]>), (Traced<~float32[]>, Traced<~float32[]>, Zero(~float32[])))
      Primal evaluation:
      call multiply_add_prim(JitTracer<~float32[]>, JitTracer<~float32[]>, JitTracer<~float32[]>)
      |<- multiply_add_prim = JitTracer<float32[]>
      Tangent evaluation:
      call multiply_add_prim(JitTracer<~float32[]>, Traced<~float32[]>, JitTracer<~float32[]>)
        call multiply_add_abstract_eval(~float32[], ~float32[], ~float32[])
        |<- multiply_add_abstract_eval = float32[]
      |<- multiply_add_prim = Traced<float32[]>
      call multiply_add_prim(Traced<~float32[]>, JitTracer<~float32[]>, Traced<float32[]>)
        call multiply_add_abstract_eval(~float32[], ~float32[], float32[])
        |<- multiply_add_abstract_eval = float32[]
      |<- multiply_add_prim = Traced<float32[]>
    |<- multiply_add_value_and_jvp = (JitTracer<float32[]>, Traced<float32[]>)
  |<- multiply_add_prim = LinearizeTracer<float32[]>
|<- square_add_prim = LinearizeTracer<float32[]>
call multiply_add_transpose(JitTracer<float32[]>, UndefinedPrimal(~float32[]), JitTracer<~float32[]>, UndefinedPrimal(float32[]))
  call multiply_add_prim(JitTracer<float32[]>, JitTracer<~float32[]>, JitTracer<~float32[]>)
    call multiply_add_abstract_eval(float32[], ~float32[], ~float32[])
    |<- multiply_add_abstract_eval = float32[]
  |<- multiply_add_prim = JitTracer<float32[]>
|<- multiply_add_transpose = (JitTracer<float32[]>, None, JitTracer<float32[]>)
call multiply_add_transpose(JitTracer<float32[]>, JitTracer<~float32[]>, UndefinedPrimal(~float32[]), JitTracer<~float32[]>)
  call multiply_add_prim(JitTracer<~float32[]>, JitTracer<float32[]>, JitTracer<~float32[]>)
    call multiply_add_abstract_eval(~float32[], float32[], ~float32[])
    |<- multiply_add_abstract_eval = float32[]
  |<- multiply_add_prim = JitTracer<float32[]>
|<- multiply_add_transpose = (None, JitTracer<float32[]>, JitTracer<float32[]>)
call multiply_add_lowering(LoweringRuleContext(module_context=ModuleContext(context=<jax._src.interpreters.mlir.JaxIrContext object at 0x71e7d2e5ce50>, module=<jaxlib.mlir._mlir_libs._mlir.ir.Module object at 0x71e7d2e4f510>, ip=<jaxlib.mlir._mlir_libs._mlir.ir.InsertionPoint object at 0x71e7d2e4ee20>, symbol_table=<jaxlib.mlir._mlir_libs._mlir.ir.SymbolTable object at 0x71e7d2e652f0>, platforms=('cpu',), backend=<jaxlib._jax.Client object at 0x71e7eb49fed0>, axis_context=ShardingContext(num_devices=1, device_assignment=None, abstract_mesh=None), keepalives=[], channel_iterator=count(1), host_callbacks=[], shape_poly_state=<jax._src.interpreters.mlir.ShapePolyLoweringState object at 0x71e7d2e65e50>, all_default_mem_kind=True, lowering_cache={}, cached_primitive_lowerings={}, traceback_caches=TracebackCaches(traceback_cache={<jaxlib._jax.Traceback object at 0x5b39a7004420>: loc(callsite("multiply_add_prim"("/tmp/ipykernel_1806/3275395289.py":12:9 to :37) at callsite("trace.<locals>.trace_func.<locals>.func_wrapper"("/tmp/ipykernel_1806/3854395562.py":48:12 to :23) at callsite("multiply_add_value_and_jvp"("/tmp/ipykernel_1806/3621920682.py":41:19 to :106) at callsite("trace.<locals>.trace_func.<locals>.func_wrapper"("/tmp/ipykernel_1806/3854395562.py":48:12 to :23) at callsite("multiply_add_prim"("/tmp/ipykernel_1806/3275395289.py":12:9 to :37) at callsite("trace.<locals>.trace_func.<locals>.func_wrapper"("/tmp/ipykernel_1806/3854395562.py":48:12 to :23) at callsite("square_add_prim"("/tmp/ipykernel_1806/3275395289.py":17:9 to :35) at callsite("trace.<locals>.trace_func.<locals>.func_wrapper"("/tmp/ipykernel_1806/3854395562.py":48:12 to :23) at callsite("<module>"("/tmp/ipykernel_1806/3085343041.py":1:7 to :50) at "InteractiveShell.run_code"("/home/docs/checkouts/readthedocs.org/user_builds/jax/envs/latest/lib/python3.12/site-packages/IPython/core/interactiveshell.py":3699:20 to :69)))))))))))}, location_cache={(<code object multiply_add_prim at 0x71e7eb35a880, file "/tmp/ipykernel_1806/3275395289.py", line 5>, 44): loc("multiply_add_prim"("/tmp/ipykernel_1806/3275395289.py":12:9 to :37)), (<code object func_wrapper at 0x71e7eb79b470, file "/tmp/ipykernel_1806/3854395562.py", line 45>, 76): loc("trace.<locals>.trace_func.<locals>.func_wrapper"("/tmp/ipykernel_1806/3854395562.py":48:12 to :23)), (<code object multiply_add_value_and_jvp at 0x71e7e8aafb90, file "/tmp/ipykernel_1806/3621920682.py", line 3>, 188): loc("multiply_add_value_and_jvp"("/tmp/ipykernel_1806/3621920682.py":41:19 to :106)), (<code object square_add_prim at 0x71e7e80f9290, file "/tmp/ipykernel_1806/3275395289.py", line 14>, 24): loc("square_add_prim"("/tmp/ipykernel_1806/3275395289.py":17:9 to :35)), (<code object <module> at 0x71e7d2e6c270, file "/tmp/ipykernel_1806/3085343041.py", line 1>, 70): loc("<module>"("/tmp/ipykernel_1806/3085343041.py":1:7 to :50)), (<code object run_code at 0x5b39a4873430, file "/home/docs/checkouts/readthedocs.org/user_builds/jax/envs/latest/lib/python3.12/site-packages/IPython/core/interactiveshell.py", line 3663>, 252): loc("InteractiveShell.run_code"("/home/docs/checkouts/readthedocs.org/user_builds/jax/envs/latest/lib/python3.12/site-packages/IPython/core/interactiveshell.py":3699:20 to :69))}, canonical_name_cache={'/tmp/ipykernel_1806/3275395289.py': '/tmp/ipykernel_1806/3275395289.py', '/tmp/ipykernel_1806/3854395562.py': '/tmp/ipykernel_1806/3854395562.py', '/tmp/ipykernel_1806/3621920682.py': '/tmp/ipykernel_1806/3621920682.py', '/tmp/ipykernel_1806/3085343041.py': '/tmp/ipykernel_1806/3085343041.py', '/home/docs/checkouts/readthedocs.org/user_builds/jax/envs/latest/lib/python3.12/site-packages/IPython/core/interactiveshell.py': '/home/docs/checkouts/readthedocs.org/user_builds/jax/envs/latest/lib/python3.12/site-packages/IPython/core/interactiveshell.py'}, is_user_file_cache={'/home/docs/checkouts/readthedocs.org/user_builds/jax/envs/latest/lib/python3.12/site-packages/jax/_src/source_info_util.py': False, '/home/docs/checkouts/readthedocs.org/user_builds/jax/envs/latest/lib/python3.12/site-packages/jax/_src/interpreters/partial_eval.py': False, '/home/docs/checkouts/readthedocs.org/user_builds/jax/envs/latest/lib/python3.12/site-packages/jax/_src/core.py': False, '/tmp/ipykernel_1806/3275395289.py': True, '/tmp/ipykernel_1806/3854395562.py': True, '/tmp/ipykernel_1806/3621920682.py': True, '/home/docs/checkouts/readthedocs.org/user_builds/jax/envs/latest/lib/python3.12/site-packages/jax/_src/linear_util.py': False, '/home/docs/checkouts/readthedocs.org/user_builds/jax/envs/latest/lib/python3.12/site-packages/jax/_src/interpreters/ad.py': False, '/home/docs/checkouts/readthedocs.org/user_builds/jax/envs/latest/lib/python3.12/site-packages/jax/_src/api_util.py': False, '/home/docs/checkouts/readthedocs.org/user_builds/jax/envs/latest/lib/python3.12/site-packages/jax/_src/api.py': False, '/home/docs/checkouts/readthedocs.org/user_builds/jax/envs/latest/lib/python3.12/site-packages/jax/_src/traceback_util.py': False, '/home/docs/checkouts/readthedocs.org/user_builds/jax/envs/latest/lib/python3.12/site-packages/jax/_src/profiler.py': False, '/home/docs/checkouts/readthedocs.org/user_builds/jax/envs/latest/lib/python3.12/site-packages/jax/_src/pjit.py': False, '/tmp/ipykernel_1806/3085343041.py': True, '/home/docs/checkouts/readthedocs.org/user_builds/jax/envs/latest/lib/python3.12/site-packages/IPython/core/interactiveshell.py': True}), lowering_parameters=LoweringParameters(override_lowering_rules=None, global_constant_computation=False, for_export=False, export_ignore_forward_compatibility=False)), name_stack=NameStack(stack=()), traceback=None, primitive=multiply_add, avals_in=(ShapedArray(float32[]), ShapedArray(float32[], weak_type=True), ShapedArray(float32[], weak_type=True)), avals_out=[ShapedArray(float32[])], tokens_in=<jax._src.interpreters.mlir.TokenSet object at 0x71e7d2e663c0>, tokens_out=None, axis_size_env=None, dim_var_values=[], jaxpr_eqn_ctx=JaxprEqnContext(compute_type=None, threefry_partitionable=True, cur_abstract_mesh=AbstractMesh((), axis_types=()), xla_metadata=None), platforms=None), Value(<block argument> of type 'tensor<f32>' at index: 0), Value(<block argument> of type 'tensor<f32>' at index: 1), Value(<block argument> of type 'tensor<f32>' at index: 2))
|<- multiply_add_lowering = [<jaxlib.mlir._mlir_libs._mlir.ir.OpResult object at 0x71e7d2e71df0>]
call multiply_add_lowering(LoweringRuleContext(module_context=ModuleContext(context=<jax._src.interpreters.mlir.JaxIrContext object at 0x71e7d2e5ce50>, module=<jaxlib.mlir._mlir_libs._mlir.ir.Module object at 0x71e7d2e4f510>, ip=<jaxlib.mlir._mlir_libs._mlir.ir.InsertionPoint object at 0x71e7d2e4ee20>, symbol_table=<jaxlib.mlir._mlir_libs._mlir.ir.SymbolTable object at 0x71e7d2e652f0>, platforms=('cpu',), backend=<jaxlib._jax.Client object at 0x71e7eb49fed0>, axis_context=ShardingContext(num_devices=1, device_assignment=None, abstract_mesh=None), keepalives=[], channel_iterator=count(1), host_callbacks=[], shape_poly_state=<jax._src.interpreters.mlir.ShapePolyLoweringState object at 0x71e7d2e65e50>, all_default_mem_kind=True, lowering_cache={LoweringCacheKey(primitive=multiply_add, eqn_ctx=JaxprEqnContext(compute_type=None, threefry_partitionable=True, cur_abstract_mesh=AbstractMesh((), axis_types=()), xla_metadata=None), avals_in=(ShapedArray(float32[]), ShapedArray(float32[], weak_type=True), ShapedArray(float32[], weak_type=True)), effects=frozenset(), params=FrozenDict({}), platforms=('cpu',)): LoweringCacheValue(func=<jaxlib.mlir.dialects.func.FuncOp object at 0x71e7d2e4f430>, output_types=[RankedTensorType(tensor<f32>)], inline=True)}, cached_primitive_lowerings={}, traceback_caches=TracebackCaches(traceback_cache={<jaxlib._jax.Traceback object at 0x5b39a7004420>: loc(callsite("multiply_add_prim"("/tmp/ipykernel_1806/3275395289.py":12:9 to :37) at callsite("trace.<locals>.trace_func.<locals>.func_wrapper"("/tmp/ipykernel_1806/3854395562.py":48:12 to :23) at callsite("multiply_add_value_and_jvp"("/tmp/ipykernel_1806/3621920682.py":41:19 to :106) at callsite("trace.<locals>.trace_func.<locals>.func_wrapper"("/tmp/ipykernel_1806/3854395562.py":48:12 to :23) at callsite("multiply_add_prim"("/tmp/ipykernel_1806/3275395289.py":12:9 to :37) at callsite("trace.<locals>.trace_func.<locals>.func_wrapper"("/tmp/ipykernel_1806/3854395562.py":48:12 to :23) at callsite("square_add_prim"("/tmp/ipykernel_1806/3275395289.py":17:9 to :35) at callsite("trace.<locals>.trace_func.<locals>.func_wrapper"("/tmp/ipykernel_1806/3854395562.py":48:12 to :23) at callsite("<module>"("/tmp/ipykernel_1806/3085343041.py":1:7 to :50) at "InteractiveShell.run_code"("/home/docs/checkouts/readthedocs.org/user_builds/jax/envs/latest/lib/python3.12/site-packages/IPython/core/interactiveshell.py":3699:20 to :69))))))))))), <jaxlib._jax.Traceback object at 0x5b39a6fb2e50>: loc(callsite("multiply_add_prim"("/tmp/ipykernel_1806/3275395289.py":12:9 to :37) at callsite("trace.<locals>.trace_func.<locals>.func_wrapper"("/tmp/ipykernel_1806/3854395562.py":48:12 to :23) at callsite("multiply_add_value_and_jvp"("/tmp/ipykernel_1806/3621920682.py":41:55 to :105) at callsite("trace.<locals>.trace_func.<locals>.func_wrapper"("/tmp/ipykernel_1806/3854395562.py":48:12 to :23) at callsite("multiply_add_prim"("/tmp/ipykernel_1806/3275395289.py":12:9 to :37) at callsite("trace.<locals>.trace_func.<locals>.func_wrapper"("/tmp/ipykernel_1806/3854395562.py":48:12 to :23) at callsite("square_add_prim"("/tmp/ipykernel_1806/3275395289.py":17:9 to :35) at callsite("trace.<locals>.trace_func.<locals>.func_wrapper"("/tmp/ipykernel_1806/3854395562.py":48:12 to :23) at callsite("<module>"("/tmp/ipykernel_1806/3085343041.py":1:7 to :50) at "InteractiveShell.run_code"("/home/docs/checkouts/readthedocs.org/user_builds/jax/envs/latest/lib/python3.12/site-packages/IPython/core/interactiveshell.py":3699:20 to :69)))))))))))}, location_cache={(<code object multiply_add_prim at 0x71e7eb35a880, file "/tmp/ipykernel_1806/3275395289.py", line 5>, 44): loc("multiply_add_prim"("/tmp/ipykernel_1806/3275395289.py":12:9 to :37)), (<code object func_wrapper at 0x71e7eb79b470, file "/tmp/ipykernel_1806/3854395562.py", line 45>, 76): loc("trace.<locals>.trace_func.<locals>.func_wrapper"("/tmp/ipykernel_1806/3854395562.py":48:12 to :23)), (<code object multiply_add_value_and_jvp at 0x71e7e8aafb90, file "/tmp/ipykernel_1806/3621920682.py", line 3>, 188): loc("multiply_add_value_and_jvp"("/tmp/ipykernel_1806/3621920682.py":41:19 to :106)), (<code object square_add_prim at 0x71e7e80f9290, file "/tmp/ipykernel_1806/3275395289.py", line 14>, 24): loc("square_add_prim"("/tmp/ipykernel_1806/3275395289.py":17:9 to :35)), (<code object <module> at 0x71e7d2e6c270, file "/tmp/ipykernel_1806/3085343041.py", line 1>, 70): loc("<module>"("/tmp/ipykernel_1806/3085343041.py":1:7 to :50)), (<code object run_code at 0x5b39a4873430, file "/home/docs/checkouts/readthedocs.org/user_builds/jax/envs/latest/lib/python3.12/site-packages/IPython/core/interactiveshell.py", line 3663>, 252): loc("InteractiveShell.run_code"("/home/docs/checkouts/readthedocs.org/user_builds/jax/envs/latest/lib/python3.12/site-packages/IPython/core/interactiveshell.py":3699:20 to :69)), (<code object multiply_add_value_and_jvp at 0x71e7e8aafb90, file "/tmp/ipykernel_1806/3621920682.py", line 3>, 180): loc("multiply_add_value_and_jvp"("/tmp/ipykernel_1806/3621920682.py":41:55 to :105))}, canonical_name_cache={'/tmp/ipykernel_1806/3275395289.py': '/tmp/ipykernel_1806/3275395289.py', '/tmp/ipykernel_1806/3854395562.py': '/tmp/ipykernel_1806/3854395562.py', '/tmp/ipykernel_1806/3621920682.py': '/tmp/ipykernel_1806/3621920682.py', '/tmp/ipykernel_1806/3085343041.py': '/tmp/ipykernel_1806/3085343041.py', '/home/docs/checkouts/readthedocs.org/user_builds/jax/envs/latest/lib/python3.12/site-packages/IPython/core/interactiveshell.py': '/home/docs/checkouts/readthedocs.org/user_builds/jax/envs/latest/lib/python3.12/site-packages/IPython/core/interactiveshell.py'}, is_user_file_cache={'/home/docs/checkouts/readthedocs.org/user_builds/jax/envs/latest/lib/python3.12/site-packages/jax/_src/source_info_util.py': False, '/home/docs/checkouts/readthedocs.org/user_builds/jax/envs/latest/lib/python3.12/site-packages/jax/_src/interpreters/partial_eval.py': False, '/home/docs/checkouts/readthedocs.org/user_builds/jax/envs/latest/lib/python3.12/site-packages/jax/_src/core.py': False, '/tmp/ipykernel_1806/3275395289.py': True, '/tmp/ipykernel_1806/3854395562.py': True, '/tmp/ipykernel_1806/3621920682.py': True, '/home/docs/checkouts/readthedocs.org/user_builds/jax/envs/latest/lib/python3.12/site-packages/jax/_src/linear_util.py': False, '/home/docs/checkouts/readthedocs.org/user_builds/jax/envs/latest/lib/python3.12/site-packages/jax/_src/interpreters/ad.py': False, '/home/docs/checkouts/readthedocs.org/user_builds/jax/envs/latest/lib/python3.12/site-packages/jax/_src/api_util.py': False, '/home/docs/checkouts/readthedocs.org/user_builds/jax/envs/latest/lib/python3.12/site-packages/jax/_src/api.py': False, '/home/docs/checkouts/readthedocs.org/user_builds/jax/envs/latest/lib/python3.12/site-packages/jax/_src/traceback_util.py': False, '/home/docs/checkouts/readthedocs.org/user_builds/jax/envs/latest/lib/python3.12/site-packages/jax/_src/profiler.py': False, '/home/docs/checkouts/readthedocs.org/user_builds/jax/envs/latest/lib/python3.12/site-packages/jax/_src/pjit.py': False, '/tmp/ipykernel_1806/3085343041.py': True, '/home/docs/checkouts/readthedocs.org/user_builds/jax/envs/latest/lib/python3.12/site-packages/IPython/core/interactiveshell.py': True}), lowering_parameters=LoweringParameters(override_lowering_rules=None, global_constant_computation=False, for_export=False, export_ignore_forward_compatibility=False)), name_stack=NameStack(stack=()), traceback=None, primitive=multiply_add, avals_in=(ShapedArray(float32[], weak_type=True), ShapedArray(float32[]), ShapedArray(float32[], weak_type=True)), avals_out=[ShapedArray(float32[])], tokens_in=<jax._src.interpreters.mlir.TokenSet object at 0x71e7d2e66600>, tokens_out=None, axis_size_env=None, dim_var_values=[], jaxpr_eqn_ctx=JaxprEqnContext(compute_type=None, threefry_partitionable=True, cur_abstract_mesh=AbstractMesh((), axis_types=()), xla_metadata=None), platforms=None), Value(<block argument> of type 'tensor<f32>' at index: 0), Value(<block argument> of type 'tensor<f32>' at index: 1), Value(<block argument> of type 'tensor<f32>' at index: 2))
|<- multiply_add_lowering = [<jaxlib.mlir._mlir_libs._mlir.ir.OpResult object at 0x71e7d2e720b0>]

批处理#

批处理转换将逐点计算转换为向量上的计算。如果您现在尝试,将会收到 NotImplementedError 错误

# The arguments are two vectors instead of two scalars.
with expectNotImplementedError():
  api.vmap(square_add_prim, in_axes=0, out_axes=0)(np.array([2., 3.]),
                                               np.array([10., 20.]))
call square_add_prim(Traced<float32[]>, Traced<float32[]>)
  call multiply_add_prim(Traced<float32[]>, Traced<float32[]>, Traced<float32[]>)

Found expected exception:
Traceback (most recent call last):
  File "/tmp/ipykernel_1806/1080163607.py", line 3, in <module>
    api.vmap(square_add_prim, in_axes=0, out_axes=0)(np.array([2., 3.]),
  File "/home/docs/checkouts/readthedocs.org/user_builds/jax/envs/latest/lib/python3.12/site-packages/jax/_src/traceback_util.py", line 180, in reraise_with_filtered_traceback
    return fun(*args, **kwargs)
           ^^^^^^^^^^^^^^^^^^^^
  File "/home/docs/checkouts/readthedocs.org/user_builds/jax/envs/latest/lib/python3.12/site-packages/jax/_src/api.py", line 1112, in vmap_f
    out_flat = batching.batch(
               ^^^^^^^^^^^^^^^
NotImplementedError: Batching rule for 'multiply_add' not implemented

您需要指导 JAX 如何评估 primitive 的批处理版本。在这个特定案例中,multiply_add_prim 已经对任何维度的输入向量进行逐点操作,因此批处理版本可以使用相同的 multiply_add_prim 实现。

from jax.interpreters import batching

@trace("multiply_add_batch")
def multiply_add_batch(vector_arg_values, batch_axes):
  """Computes the batched version of the primitive.

  This must be a JAX-traceable function.

  Since the `multiply_add primitive` already operates point-wise on arbitrary
  dimension tensors, to batch it you can use the primitive itself. This works as
  long as both the inputs have the same dimensions and are batched along the
  same axes. The result is batched along the axis that the inputs are batched.

  Args:
    vector_arg_values: A tuple of two arguments, each being a tensor of matching
      shape.
    batch_axes: The axes that are being batched. See vmap documentation.

  Returns:
    A tuple of the result, and the result axis that was batched.
  """
  assert batch_axes[0] == batch_axes[1]
  assert batch_axes[0] == batch_axes[2]
  _trace("Using multiply_add to compute the batch:")
  res = multiply_add_prim(*vector_arg_values)
  return res, batch_axes[0]


batching.primitive_batchers[multiply_add_p] = multiply_add_batch
assert np.allclose(api.vmap(square_add_prim, in_axes=0, out_axes=0)(
  np.array([2., 3.]),
  np.array([10., 20.])),
  [14., 29.])
call square_add_prim(Traced<float32[]>, Traced<float32[]>)
  call multiply_add_prim(Traced<float32[]>, Traced<float32[]>, Traced<float32[]>)
    call multiply_add_batch(([2. 3.], [2. 3.], [10. 20.]), (0, 0, 0))
      Using multiply_add to compute the batch:
      call multiply_add_prim([2. 3.], [2. 3.], [10. 20.])
        call multiply_add_impl([2. 3.], [2. 3.], [10. 20.])
        |<- multiply_add_impl = [14. 29.]
      |<- multiply_add_prim = [14. 29.]
    |<- multiply_add_batch = ([14. 29.], 0)
  |<- multiply_add_prim = Traced<float32[]>
|<- square_add_prim = Traced<float32[]>

批处理的 JIT 编译#

下面是 JIT 应用于批处理的示例

assert np.allclose(api.jit(api.vmap(square_add_prim, in_axes=0, out_axes=0))
                    (np.array([2., 3.]),
                     np.array([10., 20.])),
                    [14., 29.])
call square_add_prim(Traced<float32[]>, Traced<float32[]>)
  call multiply_add_prim(Traced<float32[]>, Traced<float32[]>, Traced<float32[]>)
    call multiply_add_batch((JitTracer<float32[2]>, JitTracer<float32[2]>, JitTracer<float32[2]>), (0, 0, 0))
      Using multiply_add to compute the batch:
      call multiply_add_prim(JitTracer<float32[2]>, JitTracer<float32[2]>, JitTracer<float32[2]>)
        call multiply_add_abstract_eval(float32[2], float32[2], float32[2])
        |<- multiply_add_abstract_eval = float32[2]
      |<- multiply_add_prim = JitTracer<float32[2]>
    |<- multiply_add_batch = (JitTracer<float32[2]>, 0)
  |<- multiply_add_prim = Traced<float32[]>
|<- square_add_prim = Traced<float32[]>
call multiply_add_lowering(LoweringRuleContext(module_context=ModuleContext(context=<jax._src.interpreters.mlir.JaxIrContext object at 0x71e7d2e4b4d0>, module=<jaxlib.mlir._mlir_libs._mlir.ir.Module object at 0x71e7d2e4da30>, ip=<jaxlib.mlir._mlir_libs._mlir.ir.InsertionPoint object at 0x71e7d2e4ffa0>, symbol_table=<jaxlib.mlir._mlir_libs._mlir.ir.SymbolTable object at 0x71e7d2e036c0>, platforms=('cpu',), backend=<jaxlib._jax.Client object at 0x71e7eb49fed0>, axis_context=ShardingContext(num_devices=1, device_assignment=None, abstract_mesh=None), keepalives=[], channel_iterator=count(1), host_callbacks=[], shape_poly_state=<jax._src.interpreters.mlir.ShapePolyLoweringState object at 0x71e7d2e02870>, all_default_mem_kind=True, lowering_cache={}, cached_primitive_lowerings={}, traceback_caches=TracebackCaches(traceback_cache={<jaxlib._jax.Traceback object at 0x5b39a6f97c30>: loc(callsite("multiply_add_prim"("/tmp/ipykernel_1806/3275395289.py":12:9 to :37) at callsite("trace.<locals>.trace_func.<locals>.func_wrapper"("/tmp/ipykernel_1806/3854395562.py":48:12 to :23) at callsite("multiply_add_batch"("/tmp/ipykernel_1806/2856830658.py":25:8 to :45) at callsite("trace.<locals>.trace_func.<locals>.func_wrapper"("/tmp/ipykernel_1806/3854395562.py":48:12 to :23) at callsite("multiply_add_prim"("/tmp/ipykernel_1806/3275395289.py":12:9 to :37) at callsite("trace.<locals>.trace_func.<locals>.func_wrapper"("/tmp/ipykernel_1806/3854395562.py":48:12 to :23) at callsite("square_add_prim"("/tmp/ipykernel_1806/3275395289.py":17:9 to :35) at callsite("trace.<locals>.trace_func.<locals>.func_wrapper"("/tmp/ipykernel_1806/3854395562.py":48:12 to :23) at callsite("<module>"("/tmp/ipykernel_1806/1392464762.py":1:19 to 3:42) at "InteractiveShell.run_code"("/home/docs/checkouts/readthedocs.org/user_builds/jax/envs/latest/lib/python3.12/site-packages/IPython/core/interactiveshell.py":3699:20 to :69)))))))))))}, location_cache={(<code object multiply_add_prim at 0x71e7eb35a880, file "/tmp/ipykernel_1806/3275395289.py", line 5>, 44): loc("multiply_add_prim"("/tmp/ipykernel_1806/3275395289.py":12:9 to :37)), (<code object func_wrapper at 0x71e7eb79b470, file "/tmp/ipykernel_1806/3854395562.py", line 45>, 76): loc("trace.<locals>.trace_func.<locals>.func_wrapper"("/tmp/ipykernel_1806/3854395562.py":48:12 to :23)), (<code object multiply_add_batch at 0x71e7e8a10c10, file "/tmp/ipykernel_1806/2856830658.py", line 3>, 88): loc("multiply_add_batch"("/tmp/ipykernel_1806/2856830658.py":25:8 to :45)), (<code object square_add_prim at 0x71e7e80f9290, file "/tmp/ipykernel_1806/3275395289.py", line 14>, 24): loc("square_add_prim"("/tmp/ipykernel_1806/3275395289.py":17:9 to :35)), (<code object <module> at 0x71e7e8aaf3c0, file "/tmp/ipykernel_1806/1392464762.py", line 1>, 166): loc("<module>"("/tmp/ipykernel_1806/1392464762.py":1:19 to 3:42)), (<code object run_code at 0x5b39a4873430, file "/home/docs/checkouts/readthedocs.org/user_builds/jax/envs/latest/lib/python3.12/site-packages/IPython/core/interactiveshell.py", line 3663>, 252): loc("InteractiveShell.run_code"("/home/docs/checkouts/readthedocs.org/user_builds/jax/envs/latest/lib/python3.12/site-packages/IPython/core/interactiveshell.py":3699:20 to :69))}, canonical_name_cache={'/tmp/ipykernel_1806/3275395289.py': '/tmp/ipykernel_1806/3275395289.py', '/tmp/ipykernel_1806/3854395562.py': '/tmp/ipykernel_1806/3854395562.py', '/tmp/ipykernel_1806/2856830658.py': '/tmp/ipykernel_1806/2856830658.py', '/tmp/ipykernel_1806/1392464762.py': '/tmp/ipykernel_1806/1392464762.py', '/home/docs/checkouts/readthedocs.org/user_builds/jax/envs/latest/lib/python3.12/site-packages/IPython/core/interactiveshell.py': '/home/docs/checkouts/readthedocs.org/user_builds/jax/envs/latest/lib/python3.12/site-packages/IPython/core/interactiveshell.py'}, is_user_file_cache={'/home/docs/checkouts/readthedocs.org/user_builds/jax/envs/latest/lib/python3.12/site-packages/jax/_src/source_info_util.py': False, '/home/docs/checkouts/readthedocs.org/user_builds/jax/envs/latest/lib/python3.12/site-packages/jax/_src/interpreters/partial_eval.py': False, '/home/docs/checkouts/readthedocs.org/user_builds/jax/envs/latest/lib/python3.12/site-packages/jax/_src/core.py': False, '/tmp/ipykernel_1806/3275395289.py': True, '/tmp/ipykernel_1806/3854395562.py': True, '/tmp/ipykernel_1806/2856830658.py': True, '/home/docs/checkouts/readthedocs.org/user_builds/jax/envs/latest/lib/python3.12/site-packages/jax/_src/interpreters/batching.py': False, '/home/docs/checkouts/readthedocs.org/user_builds/jax/envs/latest/lib/python3.12/site-packages/jax/_src/linear_util.py': False, '/home/docs/checkouts/readthedocs.org/user_builds/jax/envs/latest/lib/python3.12/site-packages/jax/_src/api.py': False, '/home/docs/checkouts/readthedocs.org/user_builds/jax/envs/latest/lib/python3.12/site-packages/jax/_src/traceback_util.py': False, '/home/docs/checkouts/readthedocs.org/user_builds/jax/envs/latest/lib/python3.12/site-packages/jax/_src/api_util.py': False, '/home/docs/checkouts/readthedocs.org/user_builds/jax/envs/latest/lib/python3.12/site-packages/jax/_src/profiler.py': False, '/home/docs/checkouts/readthedocs.org/user_builds/jax/envs/latest/lib/python3.12/site-packages/jax/_src/pjit.py': False, '/tmp/ipykernel_1806/1392464762.py': True, '/home/docs/checkouts/readthedocs.org/user_builds/jax/envs/latest/lib/python3.12/site-packages/IPython/core/interactiveshell.py': True}), lowering_parameters=LoweringParameters(override_lowering_rules=None, global_constant_computation=False, for_export=False, export_ignore_forward_compatibility=False)), name_stack=NameStack(stack=()), traceback=None, primitive=multiply_add, avals_in=(ShapedArray(float32[2]), ShapedArray(float32[2]), ShapedArray(float32[2])), avals_out=[ShapedArray(float32[2])], tokens_in=<jax._src.interpreters.mlir.TokenSet object at 0x71e7d2e67740>, tokens_out=None, axis_size_env=None, dim_var_values=[], jaxpr_eqn_ctx=JaxprEqnContext(compute_type=None, threefry_partitionable=True, cur_abstract_mesh=AbstractMesh((), axis_types=()), xla_metadata=None), platforms=None), Value(<block argument> of type 'tensor<2xf32>' at index: 0), Value(<block argument> of type 'tensor<2xf32>' at index: 1), Value(<block argument> of type 'tensor<2xf32>' at index: 2))
|<- multiply_add_lowering = [<jaxlib.mlir._mlir_libs._mlir.ir.OpResult object at 0x71e7d2fb94f0>]