AttributeError: 'Scratchpad' object has no attribute 'ufunc'

Hello. I am new to PyMC. I wrote a state space model in a straight-forward fashion and ran it, then I encountered the error.
Minimal code is this:

import pymc as pm
import numpy as np

N = 32  # N >= 32 causes "AttributeError: 'Scratchpad' object has no attribute 'ufunc'"
Y = np.random.randint(0, 20, size=N)

with pm.Model() as model:
    alpha = [pm.Uniform('alpha[1]', 0, 10)]
    sigma = pm.Uniform('sigma', 0, 100)

    for i in range(1, N):
        alpha.append(pm.Normal(f'alpha[{i+1}]', mu=alpha[i-1], sigma=sigma))

    for i in range(N):
        l = pm.Deterministic(f'lambda[{i+1}]', alpha[i].exp())
        Yobs = pm.Poisson(f'Y[{i+1}]', mu=l, observed=Y[i])

with model:
    trace = pm.sample(1000, progressbar=False)

Here is the error message:

AttributeError: ‘Scratchpad’ object has no attribute ‘ufunc’

During handling of the above exception, another exception occurred:

Traceback (most recent call last):
File “/Users/ynomura/tmp/pymc_test4.py”, line 19, in
trace = pm.sample(1000, progressbar=False)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File “/Users/ynomura/miniforge3/envs/pymc_env/lib/python3.12/site-packages/pymc/sampling/mcmc.py”, line 745, in sample
initial_points, step = init_nuts(
^^^^^^^^^^
File “/Users/ynomura/miniforge3/envs/pymc_env/lib/python3.12/site-packages/pymc/sampling/mcmc.py”, line 1426, in init_nuts
initial_points = _init_jitter(
^^^^^^^^^^^^^
File “/Users/ynomura/miniforge3/envs/pymc_env/lib/python3.12/site-packages/pymc/sampling/mcmc.py”, line 1317, in _init_jitter
point = ipfn(seed)
^^^^^^^^^^
File “/Users/ynomura/miniforge3/envs/pymc_env/lib/python3.12/site-packages/pymc/initial_point.py”, line 169, in inner
values = func(*args, **kwargs)
^^^^^^^^^^^^^^^^^^^^^
File “/Users/ynomura/miniforge3/envs/pymc_env/lib/python3.12/site-packages/pytensor/compile/function/types.py”, line 970, in call
self.vm()
File “/Users/ynomura/miniforge3/envs/pymc_env/lib/python3.12/site-packages/pytensor/link/vm.py”, line 411, in call
raise_with_op(self.fgraph, node, thunk)
File “/Users/ynomura/miniforge3/envs/pymc_env/lib/python3.12/site-packages/pytensor/link/utils.py”, line 528, in raise_with_op
raise exc_value.with_traceback(exc_trace)
File “/Users/ynomura/miniforge3/envs/pymc_env/lib/python3.12/site-packages/pytensor/link/vm.py”, line 407, in call
thunk()
File “/Users/ynomura/miniforge3/envs/pymc_env/lib/python3.12/site-packages/pytensor/graph/op.py”, line 524, in rval
r = p(n, [x[0] for x in i], o)
^^^^^^^^^^^^^^^^^^^^^^^^^^
File “/Users/ynomura/miniforge3/envs/pymc_env/lib/python3.12/site-packages/pytensor/tensor/elemwise.py”, line 747, in perform
ufunc = node.tag.ufunc
^^^^^^^^^^^^^^
File “/Users/ynomura/miniforge3/envs/pymc_env/lib/python3.12/site-packages/pytensor/graph/utils.py”, line 286, in getattribute
return super().getattribute(name)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
AttributeError: ‘Scratchpad’ object has no attribute ‘ufunc’
Apply node that caused the error: Add(Mul.0, alpha[2]_jitter, alpha[3]_jitter, alpha[4]_jitter, alpha[5]_jitter, alpha[6]_jitter, alpha[7]_jitter, alpha[8]_jitter, alpha[9]_jitter, alpha[10]_jitter, alpha[11]_jitter, alpha[12]_jitter, alpha[13]_jitter, alpha[14]_jitter, alpha[15]_jitter, alpha[16]_jitter, alpha[17]_jitter, alpha[18]_jitter, alpha[19]_jitter, alpha[20]_jitter, alpha[21]_jitter, alpha[22]_jitter, alpha[23]_jitter, alpha[24]_jitter, alpha[25]_jitter, alpha[26]_jitter, alpha[27]_jitter, alpha[28]_jitter, alpha[29]_jitter, alpha[30]_jitter, alpha[31]_jitter, alpha[32]_jitter)
Toposort index: 65
Inputs types: [TensorType(float64, shape=()), TensorType(float64, shape=()), TensorType(float64, shape=()), TensorType(float64, shape=()), TensorType(float64, shape=()), TensorType(float64, shape=()), TensorType(float64, shape=()), TensorType(float64, shape=()), TensorType(float64, shape=()), TensorType(float64, shape=()), TensorType(float64, shape=()), TensorType(float64, shape=()), TensorType(float64, shape=()), TensorType(float64, shape=()), TensorType(float64, shape=()), TensorType(float64, shape=()), TensorType(float64, shape=()), TensorType(float64, shape=()), TensorType(float64, shape=()), TensorType(float64, shape=()), TensorType(float64, shape=()), TensorType(float64, shape=()), TensorType(float64, shape=()), TensorType(float64, shape=()), TensorType(float64, shape=()), TensorType(float64, shape=()), TensorType(float64, shape=()), TensorType(float64, shape=()), TensorType(float64, shape=()), TensorType(float64, shape=()), TensorType(float64, shape=()), TensorType(float64, shape=())]
Inputs shapes: [(), (), (), (), (), (), (), (), (), (), (), (), (), (), (), (), (), (), (), (), (), (), (), (), (), (), (), (), (), (), (), ()]
Inputs strides: [(), (), (), (), (), (), (), (), (), (), (), (), (), (), (), (), (), (), (), (), (), (), (), (), (), (), (), (), (), (), (), ()]
Inputs values: [array(7.28997374), array(0.38199763), array(-0.1476399), array(-0.24766453), array(-0.24406243), array(-0.58236784), array(-0.00086235), array(0.52218202), array(-0.27085093), array(0.35206935), array(0.86599777), array(-0.76071219), array(-0.13915781), array(-0.980413), array(0.68555598), array(-0.20443367), array(-0.09218605), array(-0.8407623), array(0.68579167), array(0.63068857), array(0.46683988), array(-0.01151997), array(0.66353266), array(-0.46197365), array(-0.00129028), array(-0.43414777), array(0.94450627), array(0.56740174), array(0.84685636), array(-0.29390383), array(0.95625211), array(-0.49638046)]
Outputs clients: [[‘output’]]

HINT: Re-running with most PyTensor optimizations disabled could provide a back-trace showing when this node was created. This can be done by setting the PyTensor flag ‘optimizer=fast_compile’. If that does not work, PyTensor optimizations can be disabled with ‘optimizer=None’.
HINT: Use the PyTensor flag exception_verbosity=high for a debug print-out and storage map footprint of this Apply node.

This error doesn’t occur if N <= 31.

I around the line causing this error in Elemwise.perform, elemwise.py in PyTensor and found that calling self.prerapre_node doesn’t set node.tag.ufunc.
There is a comment saying “NumPy ufunc support only up to 32 operands (inputs and outputs) But our c code support more.” in the top of prepare_node() but I couldn’t find the c code.

Is there any workaround?
(I know I can use pm.GaussianRandomWalk for this example, but that is not what I mean)

Thanks in advance.

PyMC version: 5.15.1
PyTensor version: 2.22.1
Python version: 3.12.3
I Installed PyMC with conda.

For looping in pytensor, you should use the scan Op. See here for the docs and examples of usage

Hi Jesse,

Thank you so much for your advice. I learned about pytensor.scan and the “boilerplate” code you provided in Declaring Priors using Loops for State Space Models - #2 by jessegrabowski, and they solved my problem.

Revised code which worked without error:

import numpy as np
import pymc as pm
from pymc.pytensorf import collect_default_updates
import pytensor
import pytensor.tensor as pt

N = 32
Y = np.random.randint(0, 20, size=N)

# Helper function for pm.CustomDist
n_steps = N - 1
def statespace_dist(mu_init, sigma_level, size):

    def grw_step(mu_tm1, sigma_level):
        mu_t = mu_tm1 + pm.Normal.dist(sigma=sigma_level)
        return mu_t, collect_default_updates(outputs=[mu_t])

    mu, updates = pytensor.scan(fn=grw_step, 
                                outputs_info=[{"initial": mu_init}],
                                non_sequences=[sigma_level], 
                                n_steps=n_steps,
                                name='statespace',
                                strict=True)

    return mu

with pm.Model() as model:
    alpha1 = pm.Uniform('alpha[1]', 0, 10)
    sigma = pm.Uniform('sigma', 0, 100)

    alpha = pm.CustomDist('alpha', alpha1, sigma, dist=statespace_dist)
    alpha = pt.concatenate([[alpha1], alpha])

    l = pm.Deterministic('lambda', alpha.exp())
    Yobs = pm.Poisson('Y', mu=l, observed=Y)

with model:
    trace = pm.sample(1000, progressbar=False)
1 Like