Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Missing JAX implementation of Eigvalsh #651

Open
ckrapu opened this issue Feb 25, 2024 · 6 comments
Open

Missing JAX implementation of Eigvalsh #651

ckrapu opened this issue Feb 25, 2024 · 6 comments

Comments

@ckrapu
Copy link

ckrapu commented Feb 25, 2024

Describe the issue:

TL;DR: missing op for CAR on jax backend on PyMC requires small tweak to CAR logp, new jax op in PyTensor.

If you try to use pm.CAR with nuts_sampler='numpyro' (Colab example here) you'll run into an error:

NotImplementedError: No JAX conversion for the given Op: Eigvalsh{lower=True}

This error arises because:

  1. jax.numpy version of eigvalsh doesn't have an entry for the linker to use. The only place we use eigvalsh is for the CAR logp.
  2. If you make a reasonable looking link for the eigvalsh op taking only a single argument (matching the jax.numpy signature), the logp call mentioned earlier will complain because it supplies 2 arguments instead of 1.

Note that the jax version of eigvalsh only takes a single array a while the PyTensor and SciPy allow for two array arguments, a,b. The logic already exists to toggle between the two signatures if b is a None tensor constant, so to get the whole thing to work we just need to tweak the logp call to use the None tensor instead of an identity matrix, i.e. (lam = pt.linalg.eigvalsh(DWD, NoneConst) instead of lam = pt.linalg.eigvalsh(DWD, np.eye(DWD.shape[0])) and also create the target in PyTensor. As far as I can tell, the PyTensor addition to pytensor.link.jax.dispatch.slinalg should look like this:

@jax_funcify.register(Eigvalsh)
def jax_funcify_Eigvalsh(op, **kwargs):
    if op.lower:
        UPLO = "L"
    else:
        UPLO = "U"

    def eigvalsh_jax(a):
        return jax.numpy.linalg.eigvalsh(a, UPLO=UPLO)
    
    return eigvalsh_jax

Any feedback on this would be greatly appreciated as I'd like to do a PR for it.

Reproducable code example:

import pymc as pm
import numpy as np

W = np.array([[0,1,0,0],
              [1,0,1,0],
              [0,1,0,1],
              [0,0,1,0]])

mu = np.zeros(4)

with pm.Model() as model:
    x = pm.CAR('x', mu=mu, W=W, alpha=0.5, tau=1.0)
    trace = pm.sample(5, nuts_sampler="numpyro")

Error message:

---------------------------------------------------------------------------
NotImplementedError                       Traceback (most recent call last)
<ipython-input-1-b51262a06e83> in <cell line: 15>()
     15 with pm.Model() as model:
     16     x = pm.CAR('x', mu=mu, W=W, alpha=0.5, tau=1.0)
---> 17     trace = pm.sample(5, nuts_sampler="numpyro")

9 frames
/usr/local/lib/python3.10/dist-packages/pymc/sampling/mcmc.py in sample(draws, tune, chains, cores, random_seed, progressbar, step, nuts_sampler, initvals, init, jitter_max_retries, n_init, trace, discard_tuned_samples, compute_convergence_checks, keep_warning_stat, return_inferencedata, idata_kwargs, nuts_sampler_kwargs, callback, mp_ctx, model, **kwargs)
    687                 "Model can not be sampled with NUTS alone. Your model is probably not continuous."
    688             )
--> 689         return _sample_external_nuts(
    690             sampler=nuts_sampler,
    691             draws=draws,

/usr/local/lib/python3.10/dist-packages/pymc/sampling/mcmc.py in _sample_external_nuts(sampler, draws, tune, chains, target_accept, random_seed, initvals, model, progressbar, idata_kwargs, nuts_sampler_kwargs, **kwargs)
    341         import pymc.sampling.jax as pymc_jax
    342 
--> 343         idata = pymc_jax.sample_numpyro_nuts(
    344             draws=draws,
    345             tune=tune,

/usr/local/lib/python3.10/dist-packages/pymc/sampling/jax.py in sample_numpyro_nuts(draws, tune, chains, target_accept, random_seed, initvals, jitter, model, var_names, progressbar, keep_untransformed, chain_method, postprocessing_backend, postprocessing_vectorize, idata_kwargs, nuts_kwargs, postprocessing_chunks)
    676     )
    677 
--> 678     logp_fn = get_jaxified_logp(model, negative_logp=False)
    679 
    680     nuts_kwargs = _update_numpyro_nuts_kwargs(nuts_kwargs)

/usr/local/lib/python3.10/dist-packages/pymc/sampling/jax.py in get_jaxified_logp(model, negative_logp)
    150     if not negative_logp:
    151         model_logp = -model_logp
--> 152     logp_fn = get_jaxified_graph(inputs=model.value_vars, outputs=[model_logp])
    153 
    154     def logp_fn_wrap(x):

/usr/local/lib/python3.10/dist-packages/pymc/sampling/jax.py in get_jaxified_graph(inputs, outputs)
    143 
    144     # We now jaxify the optimized fgraph
--> 145     return jax_funcify(fgraph)
    146 
    147 

/usr/lib/python3.10/functools.py in wrapper(*args, **kw)
    887                             '1 positional argument')
    888 
--> 889         return dispatch(args[0].__class__)(*args, **kw)
    890 
    891     funcname = getattr(func, '__name__', 'singledispatch function')

/usr/local/lib/python3.10/dist-packages/pytensor/link/jax/dispatch/basic.py in jax_funcify_FunctionGraph(fgraph, node, fgraph_name, **kwargs)
     49     **kwargs,
     50 ):
---> 51     return fgraph_to_python(
     52         fgraph,
     53         jax_funcify,

/usr/local/lib/python3.10/dist-packages/pytensor/link/utils.py in fgraph_to_python(fgraph, op_conversion_fn, type_conversion_fn, order, storage_map, fgraph_name, global_env, local_env, get_name_for_object, squeeze_output, **kwargs)
    732     body_assigns = []
    733     for node in order:
--> 734         compiled_func = op_conversion_fn(
    735             node.op, node=node, storage_map=storage_map, **kwargs
    736         )

/usr/lib/python3.10/functools.py in wrapper(*args, **kw)
    887                             '1 positional argument')
    888 
--> 889         return dispatch(args[0].__class__)(*args, **kw)
    890 
    891     funcname = getattr(func, '__name__', 'singledispatch function')

/usr/local/lib/python3.10/dist-packages/pytensor/link/jax/dispatch/basic.py in jax_funcify(op, node, storage_map, **kwargs)
     39 def jax_funcify(op, node=None, storage_map=None, **kwargs):
     40     """Create a JAX compatible function from an PyTensor `Op`."""
---> 41     raise NotImplementedError(f"No JAX conversion for the given `Op`: {op}")
     42 
     43 

NotImplementedError: No JAX conversion for the given `Op`: Eigvalsh{lower=True}

PyTensor version information:

floatX ({'float32', 'float16', 'float64'}) 
    Doc:  Default floating-point precision for python casts.

Note: float16 support is experimental, use at your own risk.
    Value:  float64

warn_float64 ({'pdb', 'warn', 'raise', 'ignore'}) 
    Doc:  Do an action when a tensor variable with float64 dtype is created.
    Value:  ignore

pickle_test_value (<bound method BoolParam._apply of <pytensor.configparser.BoolParam object at 0x7f8dfa462a10>>) 
    Doc:  Dump test values while pickling model. If True, test values will be dumped with model.
    Value:  True

cast_policy ({'numpy+floatX', 'custom'}) 
    Doc:  Rules for implicit type casting
    Value:  custom

deterministic ({'more', 'default'}) 
    Doc:  If `more`, sometimes we will select some implementation that are more deterministic, but slower.  Also see the dnn.conv.algo* flags to cover more cases.
    Value:  default

device (cpu)
    Doc:  Default device for computations. only cpu is supported for now
    Value:  cpu

force_device (<bound method BoolParam._apply of <pytensor.configparser.BoolParam object at 0x7f8dfa462b00>>) 
    Doc:  Raise an error if we can't use the specified device
    Value:  False

conv__assert_shape (<bound method BoolParam._apply of <pytensor.configparser.BoolParam object at 0x7f8dfa462b30>>) 
    Doc:  If True, AbstractConv* ops will verify that user-provided shapes match the runtime shapes (debugging option, may slow down compilation)
    Value:  False

print_global_stats (<bound method BoolParam._apply of <pytensor.configparser.BoolParam object at 0x7f8dfa462b60>>) 
    Doc:  Print some global statistics (time spent) at the end
    Value:  False

assert_no_cpu_op ({'pdb', 'warn', 'raise', 'ignore'}) 
    Doc:  Raise an error/warning if there is a CPU op in the computational graph.
    Value:  ignore

unpickle_function (<bound method BoolParam._apply of <pytensor.configparser.BoolParam object at 0x7f8dfa462bc0>>) 
    Doc:  Replace unpickled PyTensor functions with None. This is useful to unpickle old graphs that pickled them when it shouldn't
    Value:  True

<pytensor.configparser.ConfigParam object at 0x7f8dfa462bf0>
    Doc:  Default compilation mode
    Value:  Mode

cxx (<class 'str'>) 
    Doc:  The C++ compiler to use. Currently only g++ is supported, but supporting additional compilers should not be too difficult. If it is empty, no C++ code is compiled.
    Value:  /usr/bin/g++

linker ({'py', 'c|py_nogc', 'vm', 'cvm_nogc', 'c|py', 'cvm', 'c', 'vm_nogc'}) 
    Doc:  Default linker used if the pytensor flags mode is Mode
    Value:  cvm

allow_gc (<bound method BoolParam._apply of <pytensor.configparser.BoolParam object at 0x7f8dfa462e30>>) 
    Doc:  Do we default to delete intermediate results during PyTensor function calls? Doing so lowers the memory requirement, but asks that we reallocate memory at the next function call. This is implemented for the default linker, but may not work for all linkers.
    Value:  True

optimizer ({'o2', 'fast_run', 'fast_compile', 'o4', 'merge', 'unsafe', 'o3', 'o1', 'None'}) 
    Doc:  Default optimizer. If not None, will use this optimizer with the Mode
    Value:  o4

optimizer_verbose (<bound method BoolParam._apply of <pytensor.configparser.BoolParam object at 0x7f8dfa462c50>>) 
    Doc:  If True, we print all optimization being applied
    Value:  False

on_opt_error ({'pdb', 'ignore', 'raise', 'warn'}) 
    Doc:  What to do when an optimization crashes: warn and skip it, raise the exception, or fall into the pdb debugger.
    Value:  warn

nocleanup (<bound method BoolParam._apply of <pytensor.configparser.BoolParam object at 0x7f8dfa462e60>>) 
    Doc:  Suppress the deletion of code files that did not compile cleanly
    Value:  False

on_unused_input ({'ignore', 'raise', 'warn'}) 
    Doc:  What to do if a variable in the 'inputs' list of  pytensor.function() is not used in the graph.
    Value:  raise

gcc__cxxflags (<class 'str'>) 
    Doc:  Extra compiler flags for gcc
    Value:   -Wno-c++11-narrowing -fno-exceptions -fno-unwind-tables -fno-asynchronous-unwind-tables

cmodule__warn_no_version (<bound method BoolParam._apply of <pytensor.configparser.BoolParam object at 0x7f8dfa462ef0>>) 
    Doc:  If True, will print a warning when compiling one or more Op with C code that can't be cached because there is no c_code_cache_version() function associated to at least one of those Ops.
    Value:  False

cmodule__remove_gxx_opt (<bound method BoolParam._apply of <pytensor.configparser.BoolParam object at 0x7f8dfa462da0>>) 
    Doc:  If True, will remove the -O* parameter passed to g++.This is useful to debug in gdb modules compiled by PyTensor.The parameter -g is passed by default to g++
    Value:  False

cmodule__compilation_warning (<bound method BoolParam._apply of <pytensor.configparser.BoolParam object at 0x7f8dfa462ce0>>) 
    Doc:  If True, will print compilation warnings.
    Value:  False

cmodule__preload_cache (<bound method BoolParam._apply of <pytensor.configparser.BoolParam object at 0x7f8dfa462d10>>) 
    Doc:  If set to True, will preload the C module cache at import time
    Value:  False

cmodule__age_thresh_use (<class 'int'>) 
    Doc:  In seconds. The time after which PyTensor won't reuse a compile c module.
    Value:  2073600

cmodule__debug (<bound method BoolParam._apply of <pytensor.configparser.BoolParam object at 0x7f8dfa462dd0>>) 
    Doc:  If True, define a DEBUG macro (if not exists) for any compiled C code.
    Value:  False

compile__wait (<class 'int'>) 
    Doc:  Time to wait before retrying to acquire the compile lock.
    Value:  5

compile__timeout (<class 'int'>) 
    Doc:  In seconds, time that a process will wait before deciding to
    override an existing lock. An override only happens when the existing
    lock is held by the same owner *and* has not been 'refreshed' by this
    owner for more than this period. Refreshes are done every half timeout
    period for running processes.
    Value:  120

ctc__root (<class 'str'>) 
    Doc:  Directory which contains the root of Baidu CTC library. It is assumed         that the compiled library is either inside the build, lib or lib64         subdirectory, and the header inside the include directory.
    Value:  

tensor__cmp_sloppy (<class 'int'>) 
    Doc:  Relax pytensor.tensor.math._allclose (0) not at all, (1) a bit, (2) more
    Value:  0

lib__amblibm (<bound method BoolParam._apply of <pytensor.configparser.BoolParam object at 0x7f8dfa462fe0>>) 
    Doc:  Use amd's amdlibm numerical library
    Value:  False

tensor__insert_inplace_optimizer_validate_nb (<class 'int'>) 
    Doc:  -1: auto, if graph have less then 500 nodes 1, else 10
    Value:  -1

traceback__limit (<class 'int'>) 
    Doc:  The number of stack to trace. -1 mean all.
    Value:  8

traceback__compile_limit (<class 'int'>) 
    Doc:  The number of stack to trace to keep during compilation. -1 mean all. If greater then 0, will also make us save PyTensor internal stack trace.
    Value:  0

warn__ignore_bug_before ({'0.7', '1.0.1', '0.5', '0.4', '0.9', '1.0.2', '0.4.1', '0.8.1', '0.6', '1.0', '1.0.4', '1.0.3', 'None', '0.8', '0.8.2', '1.0.5', 'all', '0.3', '0.10'}) 
    Doc:  If 'None', we warn about all PyTensor bugs found by default. If 'all', we don't warn about PyTensor bugs found by default. If a version, we print only the warnings relative to PyTensor bugs found after that version. Warning for specific bugs can be configured with specific [warn] flags.
    Value:  0.9

exception_verbosity ({'low', 'high'}) 
    Doc:  If 'low', the text of exceptions will generally refer to apply nodes with short names such as Elemwise{add_no_inplace}. If 'high', some exceptions will also refer to apply nodes with long descriptions  like:
        A. Elemwise{add_no_inplace}
                B. log_likelihood_v_given_h
                C. log_likelihood_h
    Value:  low

print_test_value (<bound method BoolParam._apply of <pytensor.configparser.BoolParam object at 0x7f8dfa463100>>) 
    Doc:  If 'True', the __eval__ of an PyTensor variable will return its test_value when this is available. This has the practical consequence that, e.g., in debugging `my_var` will print the same as `my_var.tag.test_value` when a test value is defined.
    Value:  False

compute_test_value ({'pdb', 'warn', 'raise', 'ignore', 'off'}) 
    Doc:  If 'True', PyTensor will run each op at graph build time, using Constants, SharedVariables and the tag 'test_value' as inputs to the function. This helps the user track down problems in the graph before it gets optimized.
    Value:  off

compute_test_value_opt ({'pdb', 'warn', 'raise', 'ignore', 'off'}) 
    Doc:  For debugging PyTensor optimization only. Same as compute_test_value, but is used during PyTensor optimization
    Value:  off

check_input (<bound method BoolParam._apply of <pytensor.configparser.BoolParam object at 0x7f8dfa463190>>) 
    Doc:  Specify if types should check their input in their C code. It can be used to speed up compilation, reduce overhead (particularly for scalars) and reduce the number of generated C files.
    Value:  True

NanGuardMode__nan_is_error (<bound method BoolParam._apply of <pytensor.configparser.BoolParam object at 0x7f8dfa4631c0>>) 
    Doc:  Default value for nan_is_error
    Value:  True

NanGuardMode__inf_is_error (<bound method BoolParam._apply of <pytensor.configparser.BoolParam object at 0x7f8dfa4631f0>>) 
    Doc:  Default value for inf_is_error
    Value:  True

NanGuardMode__big_is_error (<bound method BoolParam._apply of <pytensor.configparser.BoolParam object at 0x7f8dfa463220>>) 
    Doc:  Default value for big_is_error
    Value:  True

NanGuardMode__action ({'pdb', 'raise', 'warn'}) 
    Doc:  What NanGuardMode does when it finds a problem
    Value:  raise

DebugMode__patience (<class 'int'>) 
    Doc:  Optimize graph this many times to detect inconsistency
    Value:  10

DebugMode__check_c (<bound method BoolParam._apply of <pytensor.configparser.BoolParam object at 0x7f8dfa4632b0>>) 
    Doc:  Run C implementations where possible
    Value:  True

DebugMode__check_py (<bound method BoolParam._apply of <pytensor.configparser.BoolParam object at 0x7f8dfa4632e0>>) 
    Doc:  Run Python implementations where possible
    Value:  True

DebugMode__check_finite (<bound method BoolParam._apply of <pytensor.configparser.BoolParam object at 0x7f8dfa463310>>) 
    Doc:  True -> complain about NaN/Inf results
    Value:  True

DebugMode__check_strides (<class 'int'>) 
    Doc:  Check that Python- and C-produced ndarrays have same strides. On difference: (0) - ignore, (1) warn, or (2) raise error
    Value:  0

DebugMode__warn_input_not_reused (<bound method BoolParam._apply of <pytensor.configparser.BoolParam object at 0x7f8dfa463370>>) 
    Doc:  Generate a warning when destroy_map or view_map says that an op works inplace, but the op did not reuse the input for its output.
    Value:  True

DebugMode__check_preallocated_output (<class 'str'>) 
    Doc:  Test thunks with pre-allocated memory as output storage. This is a list of strings separated by ":". Valid values are: "initial" (initial storage in storage map, happens with Scan),"previous" (previously-returned memory), "c_contiguous", "f_contiguous", "strided" (positive and negative strides), "wrong_size" (larger and smaller dimensions), and "ALL" (all of the above).
    Value:  

DebugMode__check_preallocated_output_ndim (<class 'int'>) 
    Doc:  When testing with "strided" preallocated output memory, test all combinations of strides over that number of (inner-most) dimensions. You may want to reduce that number to reduce memory or time usage, but it is advised to keep a minimum of 2.
    Value:  4

profiling__time_thunks (<bound method BoolParam._apply of <pytensor.configparser.BoolParam object at 0x7f8dfa463400>>) 
    Doc:  Time individual thunks when profiling
    Value:  True

profiling__n_apply (<class 'int'>) 
    Doc:  Number of Apply instances to print by default
    Value:  20

profiling__n_ops (<class 'int'>) 
    Doc:  Number of Ops to print by default
    Value:  20

profiling__output_line_width (<class 'int'>) 
    Doc:  Max line width for the profiling output
    Value:  512

profiling__min_memory_size (<class 'int'>) 
    Doc:  For the memory profile, do not print Apply nodes if the size
                 of their outputs (in bytes) is lower than this threshold
    Value:  1024

profiling__min_peak_memory (<bound method BoolParam._apply of <pytensor.configparser.BoolParam object at 0x7f8dfa4634f0>>) 
    Doc:  The min peak memory usage of the order
    Value:  False

profiling__destination (<class 'str'>) 
    Doc:  File destination of the profiling output
    Value:  stderr

profiling__debugprint (<bound method BoolParam._apply of <pytensor.configparser.BoolParam object at 0x7f8dfa463550>>) 
    Doc:  Do a debugprint of the profiled functions
    Value:  False

profiling__ignore_first_call (<bound method BoolParam._apply of <pytensor.configparser.BoolParam object at 0x7f8dfa463580>>) 
    Doc:  Do we ignore the first call of an PyTensor function.
    Value:  False

on_shape_error ({'raise', 'warn'}) 
    Doc:  warn: print a warning and use the default value. raise: raise an error
    Value:  warn

openmp (<bound method BoolParam._apply of <pytensor.configparser.BoolParam object at 0x7f8dfa4635e0>>) 
    Doc:  Allow (or not) parallel computation on the CPU with OpenMP. This is the default value used when creating an Op that supports OpenMP parallelization. It is preferable to define it via the PyTensor configuration file ~/.pytensorrc or with the environment variable PYTENSOR_FLAGS. Parallelization is only done for some operations that implement it, and even for operations that implement parallelism, each operation is free to respect this flag or not. You can control the number of threads used with the environment variable OMP_NUM_THREADS. If it is set to 1, we disable openmp in PyTensor by default.
    Value:  False

openmp_elemwise_minsize (<class 'int'>) 
    Doc:  If OpenMP is enabled, this is the minimum size of vectors for which the openmp parallelization is enabled in element wise ops.
    Value:  200000

optimizer_excluding (<class 'str'>) 
    Doc:  When using the default mode, we will remove optimizer with these tags. Separate tags with ':'.
    Value:  

optimizer_including (<class 'str'>) 
    Doc:  When using the default mode, we will add optimizer with these tags. Separate tags with ':'.
    Value:  

optimizer_requiring (<class 'str'>) 
    Doc:  When using the default mode, we will require optimizer with these tags. Separate tags with ':'.
    Value:  

optdb__position_cutoff (<class 'float'>) 
    Doc:  Where to stop earlier during optimization. It represent the position of the optimizer where to stop.
    Value:  inf

optdb__max_use_ratio (<class 'float'>) 
    Doc:  A ratio that prevent infinite loop in EquilibriumGraphRewriter.
    Value:  8.0

cycle_detection ({'regular', 'fast'}) 
    Doc:  If cycle_detection is set to regular, most inplaces are allowed,but it is slower. If cycle_detection is set to faster, less inplacesare allowed, but it makes the compilation faster.The interaction of which one give the lower peak memory usage iscomplicated and not predictable, so if you are close to the peakmemory usage, triyng both could give you a small gain.
    Value:  regular

check_stack_trace ({'log', 'off', 'raise', 'warn'}) 
    Doc:  A flag for checking the stack trace during the optimization process. default (off): does not check the stack trace of any optimization log: inserts a dummy stack trace that identifies the optimizationthat inserted the variable that had an empty stack trace.warn: prints a warning if a stack trace is missing and also a dummystack trace is inserted that indicates which optimization insertedthe variable that had an empty stack trace.raise: raises an exception if a stack trace is missing
    Value:  off

metaopt__verbose (<class 'int'>) 
    Doc:  0 for silent, 1 for only warnings, 2 for full output withtimings and selected implementation
    Value:  0

metaopt__optimizer_excluding (<class 'str'>) 
    Doc:  exclude optimizers with these tags. Separate tags with ':'.
    Value:  

metaopt__optimizer_including (<class 'str'>) 
    Doc:  include optimizers with these tags. Separate tags with ':'.
    Value:  

unittests__rseed (<class 'str'>) 
    Doc:  Seed to use for randomized unit tests. Special value 'random' means using a seed of None.
    Value:  666

warn__round (<bound method BoolParam._apply of <pytensor.configparser.BoolParam object at 0x7f8dfa463850>>) 
    Doc:  Warn when using `tensor.round` with the default mode. Round changed its default from `half_away_from_zero` to `half_to_even` to have the same default as NumPy.
    Value:  False

profile (<bound method BoolParam._apply of <pytensor.configparser.BoolParam object at 0x7f8dfa463880>>) 
    Doc:  If VM should collect profile information
    Value:  False

profile_optimizer (<bound method BoolParam._apply of <pytensor.configparser.BoolParam object at 0x7f8dfa4638b0>>) 
    Doc:  If VM should collect optimizer profile information
    Value:  False

profile_memory (<bound method BoolParam._apply of <pytensor.configparser.BoolParam object at 0x7f8dfa4638e0>>) 
    Doc:  If VM should collect memory profile information and print it
    Value:  False

<pytensor.configparser.ConfigParam object at 0x7f8dfa463910>
    Doc:  Useful only for the VM Linkers. When lazy is None, auto detect if lazy evaluation is needed and use the appropriate version. If the C loop isn't being used and lazy is True, use the Stack VM; otherwise, use the Loop VM.
    Value:  None

numba__vectorize_target ({'parallel', 'cuda', 'cpu'}) 
    Doc:  Default target for numba.vectorize.
    Value:  cpu

numba__fastmath (<bound method BoolParam._apply of <pytensor.configparser.BoolParam object at 0x7f8dfa463970>>) 
    Doc:  If True, use Numba's fastmath mode.
    Value:  True

numba__cache (<bound method BoolParam._apply of <pytensor.configparser.BoolParam object at 0x7f8dfa4639a0>>) 
    Doc:  If True, use Numba's file based caching.
    Value:  True

compiledir_format (<class 'str'>) 
    Doc:  Format string for platform-dependent compiled module subdirectory
(relative to base_compiledir). Available keys: device, gxx_version,
hostname, numpy_version, platform, processor, pytensor_version,
python_bitwidth, python_int_bitwidth, python_version, short_platform.
Defaults to compiledir_%(short_platform)s-%(processor)s-
%(python_version)s-%(python_bitwidth)s.
    Value:  compiledir_%(short_platform)s-%(processor)s-%(python_version)s-%(python_bitwidth)s

<pytensor.configparser.ConfigParam object at 0x7f8dfa463a60>
    Doc:  platform-independent root directory for compiled modules
    Value:  /home/ckrapu/.pytensor

<pytensor.configparser.ConfigParam object at 0x7f8dfa4639d0>
    Doc:  platform-dependent cache directory for compiled modules
    Value:  /home/ckrapu/.pytensor/compiledir_Linux-6.5--generic-x86_64-with-glibc2.35-x86_64-3.10.12-64

blas__ldflags (<class 'str'>) 
    Doc:  lib[s] to include for [Fortran] level-3 blas implementation
    Value:  -lopenblas -lgfortran -lgomp -lm -fopenmp -Wl,-rpath,/usr/lib/x86_64-linux-gnu

blas__check_openmp (<bound method BoolParam._apply of <pytensor.configparser.BoolParam object at 0x7f8daacfece0>>) 
    Doc:  Check for openmp library conflict.
WARNING: Setting this to False leaves you open to wrong results in blas-related operations.
    Value:  True

scan__allow_gc (<bound method BoolParam._apply of <pytensor.configparser.BoolParam object at 0x7f8d80020c40>>) 
    Doc:  Allow/disallow gc inside of Scan (default: False)
    Value:  False

scan__allow_output_prealloc (<bound method BoolParam._apply of <pytensor.configparser.BoolParam object at 0x7f8d8060cd60>>) 
    Doc:  Allow/disallow memory preallocation for outputs inside of scan (default: True)
    Value:  True

Context for the issue:

Blocker for using CAR on Jax backend

@ckrapu ckrapu added the bug Something isn't working label Feb 25, 2024
@ckrapu ckrapu changed the title BUG: Deploying conditional autoregression to Jax backend fails due to missing eigvalsh link BUG: PyMC bug for conditional autoregression fails due to missing eigvalsh link in PyTensor Feb 25, 2024
@ricardoV94 ricardoV94 added feature request jax backend compatibility and removed bug Something isn't working labels Feb 25, 2024
@ricardoV94 ricardoV94 changed the title BUG: PyMC bug for conditional autoregression fails due to missing eigvalsh link in PyTensor Missing JAX implementation of Eigvalsh Feb 26, 2024
@ricardoV94
Copy link
Member

ricardoV94 commented Feb 26, 2024

@ckrapu a PR to implement the JAX dispatch would be very helpful. The only thing about your implementation is that it should fail with a NotImplementedError when b is not a NoneConst (or return a JAX equivalent that is correct).

The second argument to the dispatch function is node, from which you should be able to check the type of the second input in node.inputs[1].type

@ricardoV94
Copy link
Member

Also it may be useful to add a helper function with the same signature as the scipy one that creates the Op with the lower flag and converts a Python None to the PyTensor type. Users shouldn't have to know about it.

@HarshvirSandhu
Copy link
Contributor

Is this issue still open? I would like to work on this

@ricardoV94
Copy link
Member

Is this issue still open? I would like to work on this

I think so, but the best way to answer that question is to create a minimal PyTensor graph that uses Eigvalsh and confirming it fails on JAX mode with NotImplementedError

@HarshvirSandhu
Copy link
Contributor

@ricardoV94
It does give NotImplementedError when mode='JAX'
Also, I'm a bit confused about how b can be NoneConst as constants are not allowed in a function param list

def _pfunc_param_to_in(param, strict=False, allow_downcast=None):
if isinstance(param, Constant):
raise TypeError("Constants not allowed in param list", param)

@ricardoV94
Copy link
Member

Those are two things. There are many constants in PyTensor graphs. It's just that when compiling a function you can't say you'll use a constant as an input, those are always implicit inputs

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

3 participants