{
  "__type": "IngestedDoc",
  "__tag": 4010,
  "_content": {},
  "_ordered_sections": [],
  "item_file": null,
  "item_line": null,
  "item_type": null,
  "aliases": [],
  "example_section_data": {
    "__type": "Section",
    "__tag": 4015,
    "children": [],
    "title": [],
    "level": 0,
    "target": null
  },
  "see_also": [],
  "signature": null,
  "references": null,
  "qa": "dev:api-dev:array_api",
  "arbitrary": [
    {
      "__type": "Section",
      "__tag": 4015,
      "children": [
        {
          "__type": "Admonition",
          "__tag": 4056,
          "kind": "note",
          "base_type": "note",
          "children": [
            {
              "__type": "AdmonitionTitle",
              "__tag": 4055,
              "children": [
                {
                  "__type": "Text",
                  "__tag": 4046,
                  "value": "note Array API standard support is still experimental and hidden behind an"
                }
              ]
            },
            {
              "__type": "Paragraph",
              "__tag": 4045,
              "children": [
                {
                  "__type": "Text",
                  "__tag": 4046,
                  "value": "environment variable. Only a small part of the public API is covered right now."
                }
              ]
            }
          ]
        },
        {
          "__type": "Paragraph",
          "__tag": 4045,
          "children": [
            {
              "__type": "Text",
              "__tag": 4046,
              "value": "This guide describes how to "
            },
            {
              "__type": "Strong",
              "__tag": 4048,
              "children": [
                {
                  "__type": "Text",
                  "__tag": 4046,
                  "value": "use"
                }
              ]
            },
            {
              "__type": "Text",
              "__tag": 4046,
              "value": " and "
            },
            {
              "__type": "Strong",
              "__tag": 4048,
              "children": [
                {
                  "__type": "Text",
                  "__tag": 4046,
                  "value": "add support for"
                }
              ]
            },
            {
              "__type": "Text",
              "__tag": 4046,
              "value": " the "
            },
            {
              "__type": "Link",
              "__tag": 4049,
              "children": [
                {
                  "__type": "Text",
                  "__tag": 4046,
                  "value": "Python array API standard"
                }
              ],
              "url": "https://data-apis.org/array-api/latest/index.html",
              "title": ""
            },
            {
              "__type": "Text",
              "__tag": 4046,
              "value": ". This standard allows users to use any array API compatible array library with parts of SciPy out of the box."
            }
          ]
        },
        {
          "__type": "Paragraph",
          "__tag": 4045,
          "children": [
            {
              "__type": "Text",
              "__tag": 4046,
              "value": "The "
            },
            {
              "__type": "Link",
              "__tag": 4049,
              "children": [
                {
                  "__type": "Text",
                  "__tag": 4046,
                  "value": "RFC"
                }
              ],
              "url": "https://github.com/scipy/scipy/issues/18286",
              "title": ""
            },
            {
              "__type": "Text",
              "__tag": 4046,
              "value": " defines how SciPy implements support for the standard, with the main principle being "
            },
            {
              "__type": "Emphasis",
              "__tag": 4047,
              "children": [
                {
                  "__type": "Text",
                  "__tag": 4046,
                  "value": "\"array type in equals array type out\""
                }
              ]
            },
            {
              "__type": "Text",
              "__tag": 4046,
              "value": ". In addition, the implementation does more strict validation of allowed array-like inputs, e.g. rejecting numpy matrix and masked array instances, and arrays with object dtype."
            }
          ]
        },
        {
          "__type": "Paragraph",
          "__tag": 4045,
          "children": [
            {
              "__type": "Text",
              "__tag": 4046,
              "value": "In the following, an array API compatible namespace is noted as "
            },
            {
              "__type": "InlineCode",
              "__tag": 4051,
              "value": "xp"
            },
            {
              "__type": "Text",
              "__tag": 4046,
              "value": "."
            }
          ]
        }
      ],
      "title": [
        {
          "__type": "Text",
          "__tag": 4046,
          "value": "Support for the array API standard"
        }
      ],
      "level": 0,
      "target": "dev-arrayapi"
    },
    {
      "__type": "Section",
      "__tag": 4015,
      "children": [
        {
          "__type": "Paragraph",
          "__tag": 4045,
          "children": [
            {
              "__type": "Text",
              "__tag": 4046,
              "value": "To enable the array API standard support, an environment variable must be set before importing SciPy:"
            }
          ]
        },
        {
          "__type": "Code",
          "__tag": 4050,
          "value": "export SCIPY_ARRAY_API=1",
          "execution_status": null,
          "out": ""
        },
        {
          "__type": "Paragraph",
          "__tag": 4045,
          "children": [
            {
              "__type": "Text",
              "__tag": 4046,
              "value": "This both enables array API standard support and the more strict input validation for array-like arguments. "
            },
            {
              "__type": "Emphasis",
              "__tag": 4047,
              "children": [
                {
                  "__type": "Text",
                  "__tag": 4046,
                  "value": "Note that this environment variable is\nmeant to be temporary, as a way to make incremental changes and merge them into\n``main`` without affecting backwards compatibility immediately. We do not\nintend to keep this environment variable around long-term."
                }
              ]
            }
          ]
        },
        {
          "__type": "Paragraph",
          "__tag": 4045,
          "children": [
            {
              "__type": "Text",
              "__tag": 4046,
              "value": "This clustering example shows usage with PyTorch tensors as inputs and return values:"
            }
          ]
        },
        {
          "__type": "Code",
          "__tag": 4050,
          "value": ">>> import torch\n>>> from scipy.cluster.vq import vq\n>>> code_book = torch.tensor([[1., 1., 1.],\n...                           [2., 2., 2.]])\n>>> features  = torch.tensor([[1.9, 2.3, 1.7],\n...                           [1.5, 2.5, 2.2],\n...                           [0.8, 0.6, 1.7]])\n>>> code, dist = vq(features, code_book)\n>>> code\ntensor([1, 1, 0], dtype=torch.int32)\n>>> dist\ntensor([0.4359, 0.7348, 0.8307])",
          "execution_status": null,
          "out": ""
        },
        {
          "__type": "Paragraph",
          "__tag": 4045,
          "children": [
            {
              "__type": "Text",
              "__tag": 4046,
              "value": "Note that the above example works for PyTorch CPU tensors. For GPU tensors or CuPy arrays, the expected result for "
            },
            {
              "__type": "InlineCode",
              "__tag": 4051,
              "value": "vq"
            },
            {
              "__type": "Text",
              "__tag": 4046,
              "value": " is a "
            },
            {
              "__type": "InlineCode",
              "__tag": 4051,
              "value": "TypeError"
            },
            {
              "__type": "Text",
              "__tag": 4046,
              "value": ", because "
            },
            {
              "__type": "InlineCode",
              "__tag": 4051,
              "value": "vq"
            },
            {
              "__type": "Text",
              "__tag": 4046,
              "value": " uses compiled code in its implementation, which won't work on GPU, and there are currently no GPU specific implementations to delegate to."
            }
          ]
        },
        {
          "__type": "Paragraph",
          "__tag": 4045,
          "children": [
            {
              "__type": "Text",
              "__tag": 4046,
              "value": "More strict array input validation will reject "
            },
            {
              "__type": "InlineCode",
              "__tag": 4051,
              "value": "np.matrix"
            },
            {
              "__type": "Text",
              "__tag": 4046,
              "value": " and "
            },
            {
              "__type": "InlineCode",
              "__tag": 4051,
              "value": "np.ma.MaskedArray"
            },
            {
              "__type": "Text",
              "__tag": 4046,
              "value": " instances, as well as arrays with "
            },
            {
              "__type": "InlineCode",
              "__tag": 4051,
              "value": "object"
            },
            {
              "__type": "Text",
              "__tag": 4046,
              "value": " dtype:"
            }
          ]
        },
        {
          "__type": "Code",
          "__tag": 4050,
          "value": ">>> import numpy as np\n>>> from scipy.cluster.vq import vq\n>>> code_book = np.array([[1., 1., 1.],\n...                       [2., 2., 2.]])\n>>> features  = np.array([[1.9, 2.3, 1.7],\n...                       [1.5, 2.5, 2.2],\n...                       [0.8, 0.6, 1.7]])\n>>> vq(features, code_book)\n(array([1, 1, 0], dtype=int32), array([0.43588989, 0.73484692, 0.83066239]))\n\n>>> # The above uses numpy arrays; trying to use np.matrix instances or object\n>>> # arrays instead will yield an exception with `SCIPY_ARRAY_API=1`:\n>>> vq(np.asmatrix(features), code_book)\n...\nTypeError: 'numpy.matrix' are not supported\n\n>>> vq(np.ma.asarray(features), code_book)\n...\nTypeError: 'numpy.ma.MaskedArray' are not supported\n\n>>> vq(features.astype(np.object_), code_book)\n...\nTypeError: object arrays are not supported",
          "execution_status": null,
          "out": ""
        }
      ],
      "title": [
        {
          "__type": "Text",
          "__tag": 4046,
          "value": "Using array API standard support"
        }
      ],
      "level": 1,
      "target": null
    },
    {
      "__type": "Section",
      "__tag": 4015,
      "children": [
        {
          "__type": "Code",
          "__tag": 4050,
          "value": "=========  =========  =========\nLibrary    CPU        GPU\n=========  =========  =========\nNumPy      ✅         n/a\nCuPy       n/a        ✅\nPyTorch    ✅         ✅\nJAX        ⚠️ no JIT  ⛔\nDask       ⛔         n/a\n=========  =========  =========",
          "execution_status": null,
          "out": ""
        },
        {
          "__type": "Paragraph",
          "__tag": 4045,
          "children": [
            {
              "__type": "Text",
              "__tag": 4046,
              "value": "In the example above, the feature has some support for NumPy, CuPy, PyTorch, and JAX arrays, but no support for Dask arrays. Some backends, like JAX and PyTorch, natively support multiple devices (CPU and GPU), but SciPy support for such arrays may be limited; for instance, this SciPy feature is only expected to work with JAX arrays located on the CPU. Additionally, some backends can have major caveats; in the example the function will fail when running inside "
            },
            {
              "__type": "InlineCode",
              "__tag": 4051,
              "value": "jax.jit"
            },
            {
              "__type": "Text",
              "__tag": 4046,
              "value": ". Additional caveats may be listed in the docstring of the function."
            }
          ]
        },
        {
          "__type": "Paragraph",
          "__tag": 4045,
          "children": [
            {
              "__type": "Text",
              "__tag": 4046,
              "value": "Some functions also note support for "
            },
            {
              "__type": "Link",
              "__tag": 4049,
              "children": [
                {
                  "__type": "Text",
                  "__tag": 4046,
                  "value": "MArray"
                }
              ],
              "url": "https://mdhaber.github.io/marray/tutorial.html",
              "title": ""
            },
            {
              "__type": "Text",
              "__tag": 4046,
              "value": ", a library that add a \"missing data\" awareness to the array library of your choice. MArray is not an independent array library; rather, it wraps the namespace of an array API compatible library to add \"mask\" support. Consequently, where MArray support is noted, it is supported in conjunction with all backend/device combinations marked as supported in the capabilities table."
            }
          ]
        },
        {
          "__type": "Paragraph",
          "__tag": 4045,
          "children": [
            {
              "__type": "Text",
              "__tag": 4046,
              "value": "While the elements of the table marked with \"n/a\" are inherently out of scope, we are continually working on filling in the rest. Dask wrapping around backends other than NumPy (notably, CuPy) is currently out of scope but this may change in the future."
            }
          ]
        },
        {
          "__type": "Paragraph",
          "__tag": 4045,
          "children": [
            {
              "__type": "Text",
              "__tag": 4046,
              "value": "Please see "
            },
            {
              "__type": "Link",
              "__tag": 4049,
              "children": [
                {
                  "__type": "Text",
                  "__tag": 4046,
                  "value": "the tracker issue"
                }
              ],
              "url": "https://github.com/scipy/scipy/issues/18867",
              "title": ""
            },
            {
              "__type": "Text",
              "__tag": 4046,
              "value": " for updates."
            }
          ]
        }
      ],
      "title": [
        {
          "__type": "Text",
          "__tag": 4046,
          "value": "Example capabilities table"
        }
      ],
      "level": 1,
      "target": null
    },
    {
      "__type": "Section",
      "__tag": 4015,
      "children": [
        {
          "__type": "Paragraph",
          "__tag": 4045,
          "children": [
            {
              "__type": "Text",
              "__tag": 4046,
              "value": "A key part of the support for the array API standard and specific compatibility functions for Numpy, CuPy and PyTorch is provided through "
            },
            {
              "__type": "Link",
              "__tag": 4049,
              "children": [
                {
                  "__type": "Text",
                  "__tag": 4046,
                  "value": "array-api-compat"
                }
              ],
              "url": "https://github.com/data-apis/array-api-compat",
              "title": ""
            },
            {
              "__type": "Text",
              "__tag": 4046,
              "value": ". This package is included in the SciPy codebase via a git submodule (under "
            },
            {
              "__type": "InlineCode",
              "__tag": 4051,
              "value": "scipy/_lib"
            },
            {
              "__type": "Text",
              "__tag": 4046,
              "value": "), so no new dependencies are introduced."
            }
          ]
        },
        {
          "__type": "Paragraph",
          "__tag": 4045,
          "children": [
            {
              "__type": "InlineCode",
              "__tag": 4051,
              "value": "array-api-compat"
            },
            {
              "__type": "Text",
              "__tag": 4046,
              "value": " provides generic utility functions and adds aliases such as "
            },
            {
              "__type": "InlineCode",
              "__tag": 4051,
              "value": "xp.concat"
            },
            {
              "__type": "Text",
              "__tag": 4046,
              "value": " (which, for numpy, mapped to "
            },
            {
              "__type": "InlineCode",
              "__tag": 4051,
              "value": "np.concatenate"
            },
            {
              "__type": "Text",
              "__tag": 4046,
              "value": " before NumPy added "
            },
            {
              "__type": "InlineCode",
              "__tag": 4051,
              "value": "np.concat"
            },
            {
              "__type": "Text",
              "__tag": 4046,
              "value": " in NumPy 2.0). This allows using a uniform API across NumPy, PyTorch, CuPy and JAX (with other libraries, such as Dask, being worked on)."
            }
          ]
        },
        {
          "__type": "Paragraph",
          "__tag": 4045,
          "children": [
            {
              "__type": "Text",
              "__tag": 4046,
              "value": "When the environment variable isn't set and hence array API standard support in SciPy is disabled, we still use the wrapped version of the NumPy namespace, which is "
            },
            {
              "__type": "InlineCode",
              "__tag": 4051,
              "value": "array_api_compat.numpy"
            },
            {
              "__type": "Text",
              "__tag": 4046,
              "value": ". That should not change behavior of SciPy functions, as it's effectively the existing "
            },
            {
              "__type": "InlineCode",
              "__tag": 4051,
              "value": "numpy"
            },
            {
              "__type": "Text",
              "__tag": 4046,
              "value": " namespace with a number of aliases added and a handful of functions amended/added for array API standard support. When support is enabled, "
            },
            {
              "__type": "InlineCode",
              "__tag": 4051,
              "value": "xp = array_namespace(input)"
            },
            {
              "__type": "Text",
              "__tag": 4046,
              "value": " will be the standard-compatible namespace matching the input array type to a function (e.g., if the input to "
            },
            {
              "__type": "InlineRole",
              "__tag": 4003,
              "value": "cluster.vq.kmeans",
              "domain": null,
              "role": null,
              "inventory": null
            },
            {
              "__type": "Text",
              "__tag": 4046,
              "value": " is a PyTorch tensor, then "
            },
            {
              "__type": "InlineCode",
              "__tag": 4051,
              "value": "xp"
            },
            {
              "__type": "Text",
              "__tag": 4046,
              "value": " is "
            },
            {
              "__type": "InlineCode",
              "__tag": 4051,
              "value": "array_api_compat.torch"
            },
            {
              "__type": "Text",
              "__tag": 4046,
              "value": ")."
            }
          ]
        }
      ],
      "title": [
        {
          "__type": "Text",
          "__tag": 4046,
          "value": "Implementation notes"
        }
      ],
      "level": 1,
      "target": "dev-arrayapi_implementation_notes"
    },
    {
      "__type": "Section",
      "__tag": 4015,
      "children": [
        {
          "__type": "Paragraph",
          "__tag": 4045,
          "children": [
            {
              "__type": "Text",
              "__tag": 4046,
              "value": "As much as possible, new code added to SciPy should try to follow as closely as possible the array API standard (these functions typically are best-practice idioms for NumPy usage as well). By following the standard, effectively adding support for the array API standard is typically straightforward, and we ideally don't need to maintain any customization."
            }
          ]
        },
        {
          "__type": "Paragraph",
          "__tag": 4045,
          "children": [
            {
              "__type": "Text",
              "__tag": 4046,
              "value": "Various helper functions are available in "
            },
            {
              "__type": "InlineCode",
              "__tag": 4051,
              "value": "scipy._lib._array_api"
            },
            {
              "__type": "Text",
              "__tag": 4046,
              "value": " - please see the "
            },
            {
              "__type": "InlineCode",
              "__tag": 4051,
              "value": "__all__"
            },
            {
              "__type": "Text",
              "__tag": 4046,
              "value": " in that module for a list of current helpers, and their docstrings for more information."
            }
          ]
        },
        {
          "__type": "Paragraph",
          "__tag": 4045,
          "children": [
            {
              "__type": "Text",
              "__tag": 4046,
              "value": "To add support to a SciPy function which is defined in a "
            },
            {
              "__type": "InlineCode",
              "__tag": 4051,
              "value": ".py"
            },
            {
              "__type": "Text",
              "__tag": 4046,
              "value": " file, what you have to change is:"
            }
          ]
        },
        {
          "__type": "BulletList",
          "__tag": 4053,
          "ordered": true,
          "start": 1,
          "children": [
            {
              "__type": "ListItem",
              "__tag": 4054,
              "children": [
                {
                  "__type": "Paragraph",
                  "__tag": 4045,
                  "children": [
                    {
                      "__type": "Text",
                      "__tag": 4046,
                      "value": "Input array validation,"
                    }
                  ]
                }
              ]
            },
            {
              "__type": "ListItem",
              "__tag": 4054,
              "children": [
                {
                  "__type": "Paragraph",
                  "__tag": 4045,
                  "children": [
                    {
                      "__type": "Text",
                      "__tag": 4046,
                      "value": "Using "
                    },
                    {
                      "__type": "InlineCode",
                      "__tag": 4051,
                      "value": "xp"
                    },
                    {
                      "__type": "Text",
                      "__tag": 4046,
                      "value": " rather "
                    },
                    {
                      "__type": "InlineCode",
                      "__tag": 4051,
                      "value": "np"
                    },
                    {
                      "__type": "Text",
                      "__tag": 4046,
                      "value": " functions,"
                    }
                  ]
                }
              ]
            },
            {
              "__type": "ListItem",
              "__tag": 4054,
              "children": [
                {
                  "__type": "Paragraph",
                  "__tag": 4045,
                  "children": [
                    {
                      "__type": "Text",
                      "__tag": 4046,
                      "value": "When calling into compiled code, convert the array to a NumPy array before    and convert it back to the input array type after."
                    }
                  ]
                }
              ]
            }
          ]
        },
        {
          "__type": "Paragraph",
          "__tag": 4045,
          "children": [
            {
              "__type": "Text",
              "__tag": 4046,
              "value": "Input array validation uses the following pattern     "
            }
          ]
        },
        {
          "__type": "Code",
          "__tag": 4050,
          "value": "xp = array_namespace(arr) # where arr is the input array\n# alternatively, if there are multiple array inputs, include them all:\nxp = array_namespace(arr1, arr2)\n\n# replace np.asarray with xp.asarray\narr = xp.asarray(arr)\n# uses of non-standard parameters of np.asarray can be replaced with _asarray\narr = _asarray(arr, order='C', dtype=xp.float64, xp=xp)",
          "execution_status": null,
          "out": ""
        },
        {
          "__type": "Paragraph",
          "__tag": 4045,
          "children": [
            {
              "__type": "Text",
              "__tag": 4046,
              "value": "Note that if one input is a non-NumPy array type, all array-like inputs have to be of that type; trying to mix non-NumPy arrays with lists, Python scalars or other arbitrary Python objects will raise an exception. For NumPy arrays, those types will continue to be accepted for backwards compatibility reasons."
            }
          ]
        },
        {
          "__type": "Paragraph",
          "__tag": 4045,
          "children": [
            {
              "__type": "Text",
              "__tag": 4046,
              "value": "If a function calls into a compiled code just once, use the following pattern     "
            }
          ]
        },
        {
          "__type": "Code",
          "__tag": 4050,
          "value": "x = np.asarray(x)  # convert to numpy right before compiled call(s)\ny = _call_compiled_code(x)\ny = xp.asarray(y)  # convert back to original array type",
          "execution_status": null,
          "out": ""
        },
        {
          "__type": "Paragraph",
          "__tag": 4045,
          "children": [
            {
              "__type": "Text",
              "__tag": 4046,
              "value": "If there are multiple calls to compiled code, ensure doing the conversion just once to avoid too much overhead."
            }
          ]
        },
        {
          "__type": "Paragraph",
          "__tag": 4045,
          "children": [
            {
              "__type": "Text",
              "__tag": 4046,
              "value": "Here is an example for a hypothetical public SciPy function "
            },
            {
              "__type": "InlineCode",
              "__tag": 4051,
              "value": "toto"
            },
            {
              "__type": "Text",
              "__tag": 4046,
              "value": "    "
            }
          ]
        },
        {
          "__type": "Code",
          "__tag": 4050,
          "value": "def toto(a, b):\n    a = np.asarray(a)\n    b = np.asarray(b, copy=True)\n\n    c = np.sum(a) - np.prod(b)\n\n    # this is some C or Cython call\n    d = cdist(c)\n\n    return d",
          "execution_status": null,
          "out": ""
        },
        {
          "__type": "Paragraph",
          "__tag": 4045,
          "children": [
            {
              "__type": "Text",
              "__tag": 4046,
              "value": "You would convert this like so    "
            }
          ]
        },
        {
          "__type": "Code",
          "__tag": 4050,
          "value": "def toto(a, b):\n    xp = array_namespace(a, b)\n    a = xp.asarray(a)\n    b = xp.asarray(b, copy=True)\n\n    c = xp.sum(a) - xp.prod(b)\n\n    # this is some C or Cython call\n    c = np.asarray(c)\n    d = cdist(c)\n    d = xp.asarray(d)\n\n    return d",
          "execution_status": null,
          "out": ""
        },
        {
          "__type": "Paragraph",
          "__tag": 4045,
          "children": [
            {
              "__type": "Text",
              "__tag": 4046,
              "value": "Going through compiled code requires going back to a NumPy array, because SciPy's extension modules only work with NumPy arrays (or memoryviews in the case of Cython). For arrays on CPU, the conversions should be zero-copy, while on GPU and other devices the attempt at conversion will raise an exception. The reason for that is that silent data transfer between devices is considered bad practice, as it is likely to be a large and hard-to-detect performance bottleneck."
            }
          ]
        },
        {
          "__type": "Paragraph",
          "__tag": 4045,
          "children": [
            {
              "__type": "Text",
              "__tag": 4046,
              "value": "In some cases, compiled code can be supported on alternative backends through delegation to native implementations. Such delegation has currently been set up in "
            },
            {
              "__type": "CrossRef",
              "__tag": 4002,
              "value": "fft",
              "reference": {
                "__type": "LocalRef",
                "__tag": 4022,
                "kind": "module",
                "path": "scipy.fft"
              },
              "kind": "module"
            },
            {
              "__type": "Text",
              "__tag": 4046,
              "value": ", "
            },
            {
              "__type": "CrossRef",
              "__tag": 4002,
              "value": "ndimage",
              "reference": {
                "__type": "LocalRef",
                "__tag": 4022,
                "kind": "module",
                "path": "scipy.ndimage"
              },
              "kind": "module"
            },
            {
              "__type": "Text",
              "__tag": 4046,
              "value": ", "
            },
            {
              "__type": "CrossRef",
              "__tag": 4002,
              "value": "signal",
              "reference": {
                "__type": "LocalRef",
                "__tag": 4022,
                "kind": "module",
                "path": "scipy.signal"
              },
              "kind": "module"
            },
            {
              "__type": "Text",
              "__tag": 4046,
              "value": " (see "
            },
            {
              "__type": "CrossRef",
              "__tag": 4002,
              "value": "array_api_support_signal_caveats",
              "reference": {
                "__type": "LocalRef",
                "__tag": 4022,
                "kind": "docs",
                "path": "dev:api-dev:array_api_modules_tables:signal"
              },
              "kind": "exists"
            },
            {
              "__type": "Text",
              "__tag": 4046,
              "value": "), and "
            },
            {
              "__type": "CrossRef",
              "__tag": 4002,
              "value": "special",
              "reference": {
                "__type": "LocalRef",
                "__tag": 4022,
                "kind": "module",
                "path": "scipy.special"
              },
              "kind": "module"
            },
            {
              "__type": "Text",
              "__tag": 4046,
              "value": ", though there is not yet a standard approach, and support in each module has mostly evolved separately."
            }
          ]
        },
        {
          "__type": "Paragraph",
          "__tag": 4045,
          "children": [
            {
              "__type": "Text",
              "__tag": 4046,
              "value": "There is also some effort being put into expanding access to native implementations, such as the "
            },
            {
              "__type": "Link",
              "__tag": 4049,
              "children": [
                {
                  "__type": "Text",
                  "__tag": 4046,
                  "value": "xsf project"
                }
              ],
              "url": "https://github.com/scipy/xsf/issues/1",
              "title": ""
            },
            {
              "__type": "Text",
              "__tag": 4046,
              "value": " to establish a library of mathematical special function implementations which support both CPU and GPU."
            }
          ]
        }
      ],
      "title": [
        {
          "__type": "Text",
          "__tag": 4046,
          "value": "Adding array API standard support to a SciPy function"
        }
      ],
      "level": 1,
      "target": "dev-arrayapi_adding_support"
    },
    {
      "__type": "Section",
      "__tag": 4015,
      "children": [
        {
          "__type": "Paragraph",
          "__tag": 4045,
          "children": [
            {
              "__type": "Text",
              "__tag": 4046,
              "value": "JAX was designed with deliberate restrictions to make code easier to reason about and exploits this to better support features like JIT-compilation and autodifferentiation. The most relevant restrictions for SciPy developers are:"
            }
          ]
        },
        {
          "__type": "BulletList",
          "__tag": 4053,
          "ordered": false,
          "start": 1,
          "children": [
            {
              "__type": "ListItem",
              "__tag": 4054,
              "children": [
                {
                  "__type": "Paragraph",
                  "__tag": 4045,
                  "children": [
                    {
                      "__type": "Text",
                      "__tag": 4046,
                      "value": "JAX arrays are immutable. Rather than performing in-place updates of arrays, one   can use the "
                    },
                    {
                      "__type": "Link",
                      "__tag": 4049,
                      "children": [
                        {
                          "__type": "Text",
                          "__tag": 4046,
                          "value": "at"
                        }
                      ],
                      "url": "https://docs.jax.dev/en/latest/_autosummary/jax.numpy.ndarray.at.html",
                      "title": ""
                    },
                    {
                      "__type": "Text",
                      "__tag": 4046,
                      "value": "   property to transform an array in an equivalent way. Inside a JIT compiled function,   an expression like "
                    },
                    {
                      "__type": "InlineCode",
                      "__tag": 4051,
                      "value": "x = x.at[idx].set(y)"
                    },
                    {
                      "__type": "Text",
                      "__tag": 4046,
                      "value": " will be applied in-place under the hood."
                    }
                  ]
                }
              ]
            },
            {
              "__type": "ListItem",
              "__tag": 4054,
              "children": [
                {
                  "__type": "Paragraph",
                  "__tag": 4045,
                  "children": [
                    {
                      "__type": "Text",
                      "__tag": 4046,
                      "value": "Functions using the JAX JIT must be functionally pure. They cannot have side   effects, cannot mutate data, and their outputs must be determined completely by their   inputs. Raising a Python exception is a side-effect that is not permitted within a JITed   function."
                    }
                  ]
                }
              ]
            },
            {
              "__type": "ListItem",
              "__tag": 4054,
              "children": [
                {
                  "__type": "Paragraph",
                  "__tag": 4045,
                  "children": [
                    {
                      "__type": "Text",
                      "__tag": 4046,
                      "value": "Within the JIT, value based control flow with Python "
                    },
                    {
                      "__type": "InlineCode",
                      "__tag": 4051,
                      "value": "if"
                    },
                    {
                      "__type": "Text",
                      "__tag": 4046,
                      "value": " statements is not permitted.   Only static properties of arrays such as their "
                    },
                    {
                      "__type": "InlineCode",
                      "__tag": 4051,
                      "value": "shape"
                    },
                    {
                      "__type": "Text",
                      "__tag": 4046,
                      "value": " and "
                    },
                    {
                      "__type": "InlineCode",
                      "__tag": 4051,
                      "value": "dtype"
                    },
                    {
                      "__type": "Text",
                      "__tag": 4046,
                      "value": " are permitted to be   used with "
                    },
                    {
                      "__type": "InlineCode",
                      "__tag": 4051,
                      "value": "if"
                    },
                    {
                      "__type": "Text",
                      "__tag": 4046,
                      "value": ". "
                    },
                    {
                      "__type": "Link",
                      "__tag": 4049,
                      "children": [
                        {
                          "__type": "Text",
                          "__tag": 4046,
                          "value": "xp.where"
                        }
                      ],
                      "url": "https://data-apis.org/array-api/2024.12/API_specification/generated/array_api.where.html#where",
                      "title": ""
                    },
                    {
                      "__type": "Text",
                      "__tag": 4046,
                      "value": "   and "
                    },
                    {
                      "__type": "Link",
                      "__tag": 4049,
                      "children": [
                        {
                          "__type": "Text",
                          "__tag": 4046,
                          "value": "array_api_extra.apply_where"
                        }
                      ],
                      "url": "https://data-apis.org/array-api-extra/generated/array_api_extra.apply_where.html",
                      "title": ""
                    },
                    {
                      "__type": "Text",
                      "__tag": 4046,
                      "value": " are   provide some basic control flow that works with the JIT."
                    }
                  ]
                }
              ]
            },
            {
              "__type": "ListItem",
              "__tag": 4054,
              "children": [
                {
                  "__type": "Paragraph",
                  "__tag": 4045,
                  "children": [
                    {
                      "__type": "Text",
                      "__tag": 4046,
                      "value": "Within the JIT, the shapes of output arrays cannot depend dynamically on the "
                    },
                    {
                      "__type": "Emphasis",
                      "__tag": 4047,
                      "children": [
                        {
                          "__type": "Text",
                          "__tag": 4046,
                          "value": "values"
                        }
                      ]
                    },
                    {
                      "__type": "Text",
                      "__tag": 4046,
                      "value": " in input arrays."
                    }
                  ]
                }
              ]
            }
          ]
        },
        {
          "__type": "Paragraph",
          "__tag": 4045,
          "children": [
            {
              "__type": "Text",
              "__tag": 4046,
              "value": "See "
            },
            {
              "__type": "Link",
              "__tag": 4049,
              "children": [
                {
                  "__type": "Text",
                  "__tag": 4046,
                  "value": "Common Gotchas in JAX"
                }
              ],
              "url": "https://docs.jax.dev/en/latest/notebooks/Common_Gotchas_in_JAX.html",
              "title": ""
            },
            {
              "__type": "Text",
              "__tag": 4046,
              "value": " for more information."
            }
          ]
        },
        {
          "__type": "Paragraph",
          "__tag": 4045,
          "children": [
            {
              "__type": "Strong",
              "__tag": 4048,
              "children": [
                {
                  "__type": "Text",
                  "__tag": 4046,
                  "value": "Recommendations for developers:"
                }
              ]
            }
          ]
        },
        {
          "__type": "BulletList",
          "__tag": 4053,
          "ordered": false,
          "start": 1,
          "children": [
            {
              "__type": "ListItem",
              "__tag": 4054,
              "children": [
                {
                  "__type": "Paragraph",
                  "__tag": 4045,
                  "children": [
                    {
                      "__type": "Text",
                      "__tag": 4046,
                      "value": "To work around the mutability restriction, developers adding JAX support   to SciPy functions which make in-place updates should use   "
                    },
                    {
                      "__type": "Link",
                      "__tag": 4049,
                      "children": [
                        {
                          "__type": "Text",
                          "__tag": 4046,
                          "value": "array_api_extra.at"
                        }
                      ],
                      "url": "https://data-apis.org/array-api-extra/generated/array_api_extra.at.html",
                      "title": ""
                    },
                    {
                      "__type": "Text",
                      "__tag": 4046,
                      "value": "   which works for all array API compatible backends, delegating to JAX's "
                    },
                    {
                      "__type": "InlineCode",
                      "__tag": 4051,
                      "value": "at"
                    },
                    {
                      "__type": "Text",
                      "__tag": 4046,
                      "value": " for   JAX arrays and performing regular in-place operations with "
                    },
                    {
                      "__type": "InlineCode",
                      "__tag": 4051,
                      "value": "__setitem__"
                    },
                    {
                      "__type": "Text",
                      "__tag": 4046,
                      "value": " for other kinds   of arrays."
                    }
                  ]
                }
              ]
            },
            {
              "__type": "ListItem",
              "__tag": 4054,
              "children": [
                {
                  "__type": "Paragraph",
                  "__tag": 4045,
                  "children": [
                    {
                      "__type": "Text",
                      "__tag": 4046,
                      "value": "The restriction that functions be functionally pure to support the JAX JIT necessitates   that input-validation that raises with bad input must be skipped when "
                    },
                    {
                      "__type": "InlineCode",
                      "__tag": 4051,
                      "value": "xp"
                    },
                    {
                      "__type": "Text",
                      "__tag": 4046,
                      "value": " is JAX."
                    }
                  ]
                }
              ]
            },
            {
              "__type": "ListItem",
              "__tag": 4054,
              "children": [
                {
                  "__type": "Paragraph",
                  "__tag": 4045,
                  "children": [
                    {
                      "__type": "Text",
                      "__tag": 4046,
                      "value": "Compiled functions "
                    },
                    {
                      "__type": "InlineCode",
                      "__tag": 4051,
                      "value": "f"
                    },
                    {
                      "__type": "Text",
                      "__tag": 4046,
                      "value": " which cannot be supported on JAX through delegation to a native   implementation can potentially still be supported through the use of   "
                    },
                    {
                      "__type": "Link",
                      "__tag": 4049,
                      "children": [
                        {
                          "__type": "Text",
                          "__tag": 4046,
                          "value": "array_api_extra.lazy_apply"
                        }
                      ],
                      "url": "https://data-apis.org/array-api-extra/generated/array_api_extra.lazy_apply.html",
                      "title": ""
                    },
                    {
                      "__type": "Text",
                      "__tag": 4046,
                      "value": ", which uses JAX's "
                    },
                    {
                      "__type": "Link",
                      "__tag": 4049,
                      "children": [
                        {
                          "__type": "Text",
                          "__tag": 4046,
                          "value": "pure_callback"
                        }
                      ],
                      "url": "https://docs.jax.dev/en/latest/_autosummary/jax.pure_callback.html",
                      "title": ""
                    },
                    {
                      "__type": "Text",
                      "__tag": 4046,
                      "value": "   mechanism to enable calling Python functions within JIT-ed JAX functions."
                    }
                  ]
                },
                {
                  "__type": "Paragraph",
                  "__tag": 4045,
                  "children": [
                    {
                      "__type": "Text",
                      "__tag": 4046,
                      "value": "Using "
                    },
                    {
                      "__type": "InlineCode",
                      "__tag": 4051,
                      "value": "lazy_apply"
                    },
                    {
                      "__type": "Text",
                      "__tag": 4046,
                      "value": ", the example function "
                    },
                    {
                      "__type": "InlineCode",
                      "__tag": 4051,
                      "value": "toto"
                    },
                    {
                      "__type": "Text",
                      "__tag": 4046,
                      "value": " might be made compatible   with the JAX JIT in the following way      "
                    }
                  ]
                },
                {
                  "__type": "Code",
                  "__tag": 4050,
                  "value": "def toto(a, b):\n    xp = array_namespace(a, b)\n    a = xp.asarray(a)\n    b = xp.asarray(b, copy=True)\n\n    c = xp.sum(a) - xp.prod(b)\n\n    # this is some C or Cython call\n    # as_numpy=True tells lazy_apply to convert to and from NumPy.\n    d = xpx.lazy_apply(cdist, c, as_numpy=True)\n\n    return d",
                  "execution_status": null,
                  "out": ""
                },
                {
                  "__type": "Paragraph",
                  "__tag": 4045,
                  "children": [
                    {
                      "__type": "InlineCode",
                      "__tag": 4051,
                      "value": "lazy_apply"
                    },
                    {
                      "__type": "Text",
                      "__tag": 4046,
                      "value": " can be used so long as "
                    },
                    {
                      "__type": "InlineCode",
                      "__tag": 4051,
                      "value": "f"
                    },
                    {
                      "__type": "Text",
                      "__tag": 4046,
                      "value": " is a pure function for which the output   shape can be determined knowing only the input shapes."
                    }
                  ]
                }
              ]
            },
            {
              "__type": "ListItem",
              "__tag": 4054,
              "children": [
                {
                  "__type": "Paragraph",
                  "__tag": 4045,
                  "children": [
                    {
                      "__type": "Link",
                      "__tag": 4049,
                      "children": [
                        {
                          "__type": "Text",
                          "__tag": 4046,
                          "value": "xp.where"
                        }
                      ],
                      "url": "https://data-apis.org/array-api/2024.12/API_specification/generated/array_api.where.html#where",
                      "title": ""
                    },
                    {
                      "__type": "Text",
                      "__tag": 4046,
                      "value": "   and "
                    },
                    {
                      "__type": "Link",
                      "__tag": 4049,
                      "children": [
                        {
                          "__type": "Text",
                          "__tag": 4046,
                          "value": "array_api_extra.apply_where"
                        }
                      ],
                      "url": "https://data-apis.org/array-api-extra/generated/array_api_extra.apply_where.html",
                      "title": ""
                    },
                    {
                      "__type": "Text",
                      "__tag": 4046,
                      "value": " provide a level of basic control flow that works with the JIT   and in some cases these can be used to replace the value dependent use of "
                    },
                    {
                      "__type": "InlineCode",
                      "__tag": 4051,
                      "value": "if"
                    },
                    {
                      "__type": "Text",
                      "__tag": 4046,
                      "value": ". In   some cases its also possible to wrap code using "
                    },
                    {
                      "__type": "InlineCode",
                      "__tag": 4051,
                      "value": "if"
                    },
                    {
                      "__type": "Text",
                      "__tag": 4046,
                      "value": " within a pure function and use   "
                    },
                    {
                      "__type": "InlineCode",
                      "__tag": 4051,
                      "value": "lazy_apply"
                    },
                    {
                      "__type": "Text",
                      "__tag": 4046,
                      "value": "."
                    }
                  ]
                }
              ]
            }
          ]
        },
        {
          "__type": "Paragraph",
          "__tag": 4045,
          "children": [
            {
              "__type": "Strong",
              "__tag": 4048,
              "children": [
                {
                  "__type": "Text",
                  "__tag": 4046,
                  "value": "JAX Eager:"
                }
              ]
            }
          ]
        },
        {
          "__type": "Paragraph",
          "__tag": 4045,
          "children": [
            {
              "__type": "Text",
              "__tag": 4046,
              "value": "It is also possible to run JAX in eager-mode without the JIT (in fact this is the default behavior when "
            },
            {
              "__type": "InlineCode",
              "__tag": 4051,
              "value": "@jax.jit"
            },
            {
              "__type": "Text",
              "__tag": 4046,
              "value": " is not applied). Eager-mode comes with serious performance limitations and is typically only used to debug functions which are ultimately intended to be run with the JIT. Do not be tempted to attempt to distinguish whether JAX is being used with the JIT in order to bypass some restrictions while running with eager-mode. There is no way to make this distinction using JAX's public API, and any means of determining if JAX is running with the JIT would necessarily involve implementation details that SciPy should not rely upon. In general, support for eager-mode is not a high-value target, and it is not considered a good use of developer time to put significant effort into enabling eager-only support."
            }
          ]
        }
      ],
      "title": [
        {
          "__type": "Text",
          "__tag": 4046,
          "value": "A note on JAX support"
        }
      ],
      "level": 2,
      "target": "dev-arrayapi_jax_support"
    },
    {
      "__type": "Section",
      "__tag": 4015,
      "children": [
        {
          "__type": "Paragraph",
          "__tag": 4045,
          "children": [
            {
              "__type": "Text",
              "__tag": 4046,
              "value": "MArray wraps array API compatible namespaces, so it is common for an array API compatible function to execute "
            },
            {
              "__type": "Emphasis",
              "__tag": 4047,
              "children": [
                {
                  "__type": "Text",
                  "__tag": 4046,
                  "value": "without warnings or errors"
                }
              ]
            },
            {
              "__type": "Text",
              "__tag": 4046,
              "value": " provided MArray input. "
            },
            {
              "__type": "Strong",
              "__tag": 4048,
              "children": [
                {
                  "__type": "Text",
                  "__tag": 4046,
                  "value": "This does\nnecessarily mean that the function supports MArray input."
                }
              ]
            },
            {
              "__type": "Text",
              "__tag": 4046,
              "value": " The mask of MArrays is used to denote missing values; therefore, to consider a function compatible with MArray, numerical output with masked input must equal the numerical output expected if the masked values were removed entirely. MArray provides primitives to facilitate this, but typically, implementations must be generalized to ensure that this property holds."
            }
          ]
        },
        {
          "__type": "Paragraph",
          "__tag": 4045,
          "children": [
            {
              "__type": "Text",
              "__tag": 4046,
              "value": "For instance, suppose that the function "
            },
            {
              "__type": "InlineCode",
              "__tag": 4051,
              "value": "mean"
            },
            {
              "__type": "Text",
              "__tag": 4046,
              "value": " were not defined by the array API standard, and it had been implemented in SciPy as    "
            }
          ]
        },
        {
          "__type": "Code",
          "__tag": 4050,
          "value": "def mean(x, axis=0):\n    xp = array_namespace(x)\n    n = x.shape[axis]\n    return xp.sum(x, axis=axis) / n",
          "execution_status": null,
          "out": ""
        },
        {
          "__type": "Paragraph",
          "__tag": 4045,
          "children": [
            {
              "__type": "Text",
              "__tag": 4046,
              "value": "This implementation assumes that the denominator used to normalize the sum will be the same for each axis-slice: the length of the array along the "
            },
            {
              "__type": "InlineCode",
              "__tag": 4051,
              "value": "axis"
            },
            {
              "__type": "Text",
              "__tag": 4046,
              "value": ". This assumption is not valid for an array with masked values. Although MArray will ensure that masked values are not considered when computing the sum, the implementation still needs to be generalized to normalize by the "
            },
            {
              "__type": "Emphasis",
              "__tag": 4047,
              "children": [
                {
                  "__type": "Text",
                  "__tag": 4046,
                  "value": "number of non-masked elements"
                }
              ]
            },
            {
              "__type": "Text",
              "__tag": 4046,
              "value": " in a slice    "
            }
          ]
        },
        {
          "__type": "Code",
          "__tag": 4050,
          "value": "from scipy._lib._array_api import _count_nonmasked\n\ndef mean(x, axis=0):\n    xp = array_namespace(x)\n    # n = x.shape[axis]\n    n = _count_nonmasked(x, axis=axis, keepdims=True, xp=xp)\n    return xp.sum(x, axis=axis) / n",
          "execution_status": null,
          "out": ""
        },
        {
          "__type": "Paragraph",
          "__tag": 4045,
          "children": [
            {
              "__type": "Text",
              "__tag": 4046,
              "value": "Using counts of non-masked elements instead of slice lengths was by far the most common generalization needed to add MArray support throughout "
            },
            {
              "__type": "CrossRef",
              "__tag": 4002,
              "value": "scipy.stats",
              "reference": {
                "__type": "LocalRef",
                "__tag": 4022,
                "kind": "module",
                "path": "scipy.stats"
              },
              "kind": "module"
            },
            {
              "__type": "Text",
              "__tag": 4046,
              "value": ", and for many functions, it was the only generalization needed. Other common changes included:"
            }
          ]
        },
        {
          "__type": "BulletList",
          "__tag": 4053,
          "ordered": false,
          "start": 1,
          "children": [
            {
              "__type": "ListItem",
              "__tag": 4054,
              "children": [
                {
                  "__type": "Paragraph",
                  "__tag": 4045,
                  "children": [
                    {
                      "__type": "Text",
                      "__tag": 4046,
                      "value": "separate consideration of "
                    },
                    {
                      "__type": "Emphasis",
                      "__tag": 4047,
                      "children": [
                        {
                          "__type": "Text",
                          "__tag": 4046,
                          "value": "both"
                        }
                      ]
                    },
                    {
                      "__type": "Text",
                      "__tag": 4046,
                      "value": " the length along an axis and the count of non-masked   values (e.g., see "
                    },
                    {
                      "__type": "CrossRef",
                      "__tag": 4002,
                      "value": "scipy.stats.cramervonmises_2samp",
                      "reference": {
                        "__type": "RefInfo",
                        "__tag": 4000,
                        "module": "scipy",
                        "version": "?",
                        "kind": "module",
                        "path": "scipy.stats._hypotests:cramervonmises_2samp"
                      },
                      "kind": "module"
                    },
                    {
                      "__type": "Text",
                      "__tag": 4046,
                      "value": "),"
                    }
                  ]
                }
              ]
            },
            {
              "__type": "ListItem",
              "__tag": 4054,
              "children": [
                {
                  "__type": "Paragraph",
                  "__tag": 4045,
                  "children": [
                    {
                      "__type": "Text",
                      "__tag": 4046,
                      "value": "sharing a common mask between \"paired-data\" input arrays using   "
                    },
                    {
                      "__type": "InlineCode",
                      "__tag": 4051,
                      "value": "scipy._lib._array_api._share_masks"
                    },
                    {
                      "__type": "Text",
                      "__tag": 4046,
                      "value": " (e.g., see "
                    },
                    {
                      "__type": "CrossRef",
                      "__tag": 4002,
                      "value": "scipy.stats.spearmanrho",
                      "reference": {
                        "__type": "RefInfo",
                        "__tag": 4000,
                        "module": "scipy",
                        "version": "?",
                        "kind": "module",
                        "path": "scipy.stats._correlation:spearmanrho"
                      },
                      "kind": "module"
                    },
                    {
                      "__type": "Text",
                      "__tag": 4046,
                      "value": "),"
                    }
                  ]
                }
              ]
            },
            {
              "__type": "ListItem",
              "__tag": 4054,
              "children": [
                {
                  "__type": "Paragraph",
                  "__tag": 4045,
                  "children": [
                    {
                      "__type": "Text",
                      "__tag": 4046,
                      "value": "using "
                    },
                    {
                      "__type": "InlineCode",
                      "__tag": 4051,
                      "value": "scipy._lib._array_api._masked_apply"
                    },
                    {
                      "__type": "Text",
                      "__tag": 4046,
                      "value": " to evaluate an elementwise function   on all data, then re-applying the common mask of the inputs to the output (e.g., see   "
                    },
                    {
                      "__type": "CrossRef",
                      "__tag": 4002,
                      "value": "scipy.stats.cramervonmises",
                      "reference": {
                        "__type": "RefInfo",
                        "__tag": 4000,
                        "module": "scipy",
                        "version": "?",
                        "kind": "module",
                        "path": "scipy.stats._hypotests:cramervonmises"
                      },
                      "kind": "module"
                    },
                    {
                      "__type": "Text",
                      "__tag": 4046,
                      "value": "), and"
                    }
                  ]
                }
              ]
            },
            {
              "__type": "ListItem",
              "__tag": 4054,
              "children": [
                {
                  "__type": "Paragraph",
                  "__tag": 4045,
                  "children": [
                    {
                      "__type": "Text",
                      "__tag": 4046,
                      "value": "distinguishing between (built-in) "
                    },
                    {
                      "__type": "InlineCode",
                      "__tag": 4051,
                      "value": "sum"
                    },
                    {
                      "__type": "Text",
                      "__tag": 4046,
                      "value": ", which propagates a mask, and "
                    },
                    {
                      "__type": "InlineCode",
                      "__tag": 4051,
                      "value": "xp.sum"
                    },
                    {
                      "__type": "Text",
                      "__tag": 4046,
                      "value": ",   which ignores masked values (e.g., see "
                    },
                    {
                      "__type": "CrossRef",
                      "__tag": 4002,
                      "value": "scipy.stats.chisquare",
                      "reference": {
                        "__type": "RefInfo",
                        "__tag": 4000,
                        "module": "scipy",
                        "version": "?",
                        "kind": "module",
                        "path": "scipy.stats._stats_py:chisquare"
                      },
                      "kind": "module"
                    },
                    {
                      "__type": "Text",
                      "__tag": 4046,
                      "value": ")."
                    }
                  ]
                }
              ]
            }
          ]
        },
        {
          "__type": "Paragraph",
          "__tag": 4045,
          "children": [
            {
              "__type": "Text",
              "__tag": 4046,
              "value": "In most cases, use of "
            },
            {
              "__type": "InlineCode",
              "__tag": 4051,
              "value": "_count_nonmasked"
            },
            {
              "__type": "Text",
              "__tag": 4046,
              "value": ", "
            },
            {
              "__type": "InlineCode",
              "__tag": 4051,
              "value": "_share_masks"
            },
            {
              "__type": "Text",
              "__tag": 4046,
              "value": ", and "
            },
            {
              "__type": "InlineCode",
              "__tag": 4051,
              "value": "_masked_apply"
            },
            {
              "__type": "Text",
              "__tag": 4046,
              "value": " has been sufficient to generalize functions without treating MArray input as a special case or accessing the "
            },
            {
              "__type": "InlineCode",
              "__tag": 4051,
              "value": "mask"
            },
            {
              "__type": "Text",
              "__tag": 4046,
              "value": " or "
            },
            {
              "__type": "InlineCode",
              "__tag": 4051,
              "value": "data"
            },
            {
              "__type": "Text",
              "__tag": 4046,
              "value": " attributes of an MArray directly. In exceptional cases, "
            },
            {
              "__type": "InlineCode",
              "__tag": 4051,
              "value": "scipy._lib._array_api.is_marray"
            },
            {
              "__type": "Text",
              "__tag": 4046,
              "value": " is available to determine when the namespace has been wrapped with MArray, and the namespace of the underlying array library can be accessed using the "
            },
            {
              "__type": "InlineCode",
              "__tag": 4051,
              "value": "_xp"
            },
            {
              "__type": "Text",
              "__tag": 4046,
              "value": " attribute of an MArray variable (e.g. see "
            },
            {
              "__type": "CrossRef",
              "__tag": 4002,
              "value": "scipy.stats.mode",
              "reference": {
                "__type": "RefInfo",
                "__tag": 4000,
                "module": "scipy",
                "version": "?",
                "kind": "module",
                "path": "scipy.stats._stats_py:mode"
              },
              "kind": "module"
            },
            {
              "__type": "Text",
              "__tag": 4046,
              "value": ")."
            }
          ]
        },
        {
          "__type": "Admonition",
          "__tag": 4056,
          "kind": "warning",
          "base_type": "warning",
          "children": [
            {
              "__type": "AdmonitionTitle",
              "__tag": 4055,
              "children": [
                {
                  "__type": "Text",
                  "__tag": 4046,
                  "value": "warning "
                }
              ]
            },
            {
              "__type": "Paragraph",
              "__tag": 4045,
              "children": [
                {
                  "__type": "Text",
                  "__tag": 4046,
                  "value": "NumPy masked arrays tend to mask the output of calculations that would otherwise return infinite or NaN output. This is unsafe because subsequent calculations will treat these \"invalid\" outputs as \"missing\", erroneously producing seemingly valid numerical output when the correct calculation would propagate the infinite or NaN value. When adding MArray support to SciPy functions, ensure that masked values of the output arise due to masked input (e.g., the output of an elementwise function on a masked input value is masked; the output of a reducing function on an all-masked slice is masked) and "
                },
                {
                  "__type": "Emphasis",
                  "__tag": 4047,
                  "children": [
                    {
                      "__type": "Text",
                      "__tag": 4046,
                      "value": "only"
                    }
                  ]
                },
                {
                  "__type": "Text",
                  "__tag": 4046,
                  "value": " due to masked input."
                }
              ]
            }
          ]
        }
      ],
      "title": [
        {
          "__type": "Text",
          "__tag": 4046,
          "value": "A note on MArray support"
        }
      ],
      "level": 2,
      "target": "dev-arrayapi_marray_support"
    },
    {
      "__type": "Section",
      "__tag": 4015,
      "children": [
        {
          "__type": "Paragraph",
          "__tag": 4045,
          "children": [
            {
              "__type": "Text",
              "__tag": 4046,
              "value": "The Array API Standard allows conforming libraries to have "
            },
            {
              "__type": "Link",
              "__tag": 4049,
              "children": [
                {
                  "__type": "Text",
                  "__tag": 4046,
                  "value": "default datatypes"
                }
              ],
              "url": "https://data-apis.org/array-api/latest/API_specification/data_types.html#default-data-types",
              "title": ""
            },
            {
              "__type": "Text",
              "__tag": 4046,
              "value": " for integers and real and complex floating point numbers which differ from the "
            },
            {
              "__type": "InlineCode",
              "__tag": 4051,
              "value": "int64"
            },
            {
              "__type": "Text",
              "__tag": 4046,
              "value": ", "
            },
            {
              "__type": "InlineCode",
              "__tag": 4051,
              "value": "float64"
            },
            {
              "__type": "Text",
              "__tag": 4046,
              "value": ", "
            },
            {
              "__type": "InlineCode",
              "__tag": 4051,
              "value": "complex128"
            },
            {
              "__type": "Text",
              "__tag": 4046,
              "value": " defaults used by NumPy. Our aim is to have array API supporting SciPy functions with array inputs have behavior which is independent of the default dtype to the extent that this is practical. This means that any when using array creation functions from the "
            },
            {
              "__type": "InlineCode",
              "__tag": 4051,
              "value": "xp"
            },
            {
              "__type": "Text",
              "__tag": 4046,
              "value": " namespace such as "
            },
            {
              "__type": "InlineCode",
              "__tag": 4051,
              "value": "xp.zeros"
            },
            {
              "__type": "Text",
              "__tag": 4046,
              "value": " or "
            },
            {
              "__type": "InlineCode",
              "__tag": 4051,
              "value": "xp.arange"
            },
            {
              "__type": "Text",
              "__tag": 4046,
              "value": ", one should take care to explicitly set a dtype with the "
            },
            {
              "__type": "InlineCode",
              "__tag": 4051,
              "value": "dtype"
            },
            {
              "__type": "Text",
              "__tag": 4046,
              "value": " kwarg; otherwise, the result will depend on the default dtype."
            }
          ]
        },
        {
          "__type": "Paragraph",
          "__tag": 4045,
          "children": [
            {
              "__type": "Text",
              "__tag": 4046,
              "value": "Note that SciPy is currently only tested in CI on platforms/backends/backend-configurations where a "
            },
            {
              "__type": "InlineCode",
              "__tag": 4051,
              "value": "float64"
            },
            {
              "__type": "Text",
              "__tag": 4046,
              "value": " dtype is available. At the moment, "
            },
            {
              "__type": "InlineCode",
              "__tag": 4051,
              "value": "float32"
            },
            {
              "__type": "Text",
              "__tag": 4046,
              "value": "-only support varies from function to function and is not well-documented. Some examples of "
            },
            {
              "__type": "InlineCode",
              "__tag": 4051,
              "value": "float32"
            },
            {
              "__type": "Text",
              "__tag": 4046,
              "value": "-only backends are JAX in its "
            },
            {
              "__type": "Link",
              "__tag": 4049,
              "children": [
                {
                  "__type": "Text",
                  "__tag": 4046,
                  "value": "default configuration"
                }
              ],
              "url": "https://docs.jax.dev/en/latest/default_dtypes.html",
              "title": ""
            },
            {
              "__type": "Text",
              "__tag": 4046,
              "value": ", and PyTorch with the "
            },
            {
              "__type": "Link",
              "__tag": 4049,
              "children": [
                {
                  "__type": "Text",
                  "__tag": 4046,
                  "value": "Metal performance shader backend"
                }
              ],
              "url": "https://pytorch.org/blog/introducing-accelerated-pytorch-training-on-mac/",
              "title": ""
            },
            {
              "__type": "Text",
              "__tag": 4046,
              "value": " on ARM Mac. We are open to expanding and improving "
            },
            {
              "__type": "InlineCode",
              "__tag": 4051,
              "value": "float32"
            },
            {
              "__type": "Text",
              "__tag": 4046,
              "value": "-only support in cases where this is feasible and there is sufficient user interest."
            }
          ]
        },
        {
          "__type": "Paragraph",
          "__tag": 4045,
          "children": [
            {
              "__type": "Text",
              "__tag": 4046,
              "value": "It is therefore recommended that those using SciPy with the JAX backend set the X64 flag by one of the means JAX provides since the default "
            },
            {
              "__type": "InlineCode",
              "__tag": 4051,
              "value": "float32"
            },
            {
              "__type": "Text",
              "__tag": 4046,
              "value": "-only configuration is not currently tested."
            }
          ]
        },
        {
          "__type": "Paragraph",
          "__tag": 4045,
          "children": [
            {
              "__type": "Text",
              "__tag": 4046,
              "value": "Though not directly related to default dtypes, it may be useful to know that JAX defaults to using "
            },
            {
              "__type": "Link",
              "__tag": 4049,
              "children": [
                {
                  "__type": "Text",
                  "__tag": 4046,
                  "value": "TensorFloat32"
                }
              ],
              "url": "https://github.com/jax-ml/jax/issues/4873",
              "title": ""
            },
            {
              "__type": "Text",
              "__tag": 4046,
              "value": " precision for matrix multiplication of "
            },
            {
              "__type": "InlineCode",
              "__tag": 4051,
              "value": "float32"
            },
            {
              "__type": "Text",
              "__tag": 4046,
              "value": " arrays on GPUs that support TensorFloat32. The half-precision mantissas used in this format can cause accuracy issues in scientific applications. SciPy sets "
            },
            {
              "__type": "Link",
              "__tag": 4049,
              "children": [
                {
                  "__type": "Text",
                  "__tag": 4046,
                  "value": "jax_default_matmul_precision"
                }
              ],
              "url": "https://docs.jax.dev/en/latest/_autosummary/jax.default_matmul_precision.html",
              "title": ""
            },
            {
              "__type": "Text",
              "__tag": 4046,
              "value": " to "
            },
            {
              "__type": "InlineCode",
              "__tag": 4051,
              "value": "\"float32\""
            },
            {
              "__type": "Text",
              "__tag": 4046,
              "value": " in its JAX GPU tests and we recommend this configuration for users of the JAX backend. This configuration option does not affect matrix multiplications of "
            },
            {
              "__type": "InlineCode",
              "__tag": 4051,
              "value": "float64"
            },
            {
              "__type": "Text",
              "__tag": 4046,
              "value": " arrays when the X64 flag is enabled."
            }
          ]
        }
      ],
      "title": [
        {
          "__type": "Text",
          "__tag": 4046,
          "value": "Default Datatypes"
        }
      ],
      "level": 2,
      "target": "dev-arrayapi_default_dtype"
    },
    {
      "__type": "Section",
      "__tag": 4015,
      "children": [
        {
          "__type": "Paragraph",
          "__tag": 4045,
          "children": [
            {
              "__type": "Text",
              "__tag": 4046,
              "value": "For array creation functions without array inputs, adding array API standard support can be accomplished by adding keyword only arguments "
            },
            {
              "__type": "InlineCode",
              "__tag": 4051,
              "value": "xp"
            },
            {
              "__type": "Text",
              "__tag": 4046,
              "value": " and "
            },
            {
              "__type": "InlineCode",
              "__tag": 4051,
              "value": "device"
            },
            {
              "__type": "Text",
              "__tag": 4046,
              "value": " to specify the desired backend and device respectively. See for instance "
            },
            {
              "__type": "CrossRef",
              "__tag": 4002,
              "value": "buttap",
              "reference": {
                "__type": "RefInfo",
                "__tag": 4000,
                "module": "scipy",
                "version": "?",
                "kind": "module",
                "path": "scipy.signal._filter_design:buttap"
              },
              "kind": "module"
            },
            {
              "__type": "Text",
              "__tag": 4046,
              "value": " which constructs the analog prototype of an Nth-order Butterworth filter. It may also be desirable to add a "
            },
            {
              "__type": "InlineCode",
              "__tag": 4051,
              "value": "dtype"
            },
            {
              "__type": "Text",
              "__tag": 4046,
              "value": " kwarg to control the output "
            },
            {
              "__type": "InlineCode",
              "__tag": 4051,
              "value": "dtype"
            },
            {
              "__type": "Text",
              "__tag": 4046,
              "value": " for such functions."
            }
          ]
        },
        {
          "__type": "Paragraph",
          "__tag": 4045,
          "children": [
            {
              "__type": "Text",
              "__tag": 4046,
              "value": "Note that none of these keyword arguments are necessary when there are array arguments from which the backend, device, and desired output dtype can be inferred (ideally, output dtype should follow input dtypes through the expected "
            },
            {
              "__type": "Link",
              "__tag": 4049,
              "children": [
                {
                  "__type": "Text",
                  "__tag": 4046,
                  "value": "type promotion rules"
                }
              ],
              "url": "https://data-apis.org/array-api/latest/API_specification/type_promotion.html",
              "title": ""
            },
            {
              "__type": "Text",
              "__tag": 4046,
              "value": "). For the sake of API simplicity and consistency and in the spirit of \"There should be one-- and preferably only one --obvious way to do it.\", it is recommended to avoid the use of these kwargs in functions which take at least one array argument."
            }
          ]
        },
        {
          "__type": "Paragraph",
          "__tag": 4045,
          "children": [
            {
              "__type": "Text",
              "__tag": 4046,
              "value": "It is still under debate how array creation functions without array inputs should behave with respect to "
            },
            {
              "__type": "CrossRef",
              "__tag": 4002,
              "value": "default dtypes",
              "reference": {
                "__type": "LocalRef",
                "__tag": 4022,
                "kind": "docs",
                "path": "dev:api-dev:array_api"
              },
              "kind": "exists"
            },
            {
              "__type": "Text",
              "__tag": 4046,
              "value": ". Should they respect default dtype or should the output dtype be fixed across backends and defaults? Should there be a "
            },
            {
              "__type": "InlineCode",
              "__tag": 4051,
              "value": "dtype"
            },
            {
              "__type": "Text",
              "__tag": 4046,
              "value": " kwarg for controlling the output dtype or is being able to apply "
            },
            {
              "__type": "InlineCode",
              "__tag": 4051,
              "value": "xp.astype"
            },
            {
              "__type": "Text",
              "__tag": 4046,
              "value": " on the output sufficient? Since there is not yet a consistent pattern to follow, for now its important to clearly document how such functions behave with respect to the default dtype in the "
            },
            {
              "__type": "CrossRef",
              "__tag": 4002,
              "value": "extra_note",
              "reference": {
                "__type": "LocalRef",
                "__tag": 4022,
                "kind": "docs",
                "path": "dev:api-dev:array_api"
              },
              "kind": "exists"
            },
            {
              "__type": "Text",
              "__tag": 4046,
              "value": " described below."
            }
          ]
        }
      ],
      "title": [
        {
          "__type": "Text",
          "__tag": 4046,
          "value": "Array creation functions without array inputs"
        }
      ],
      "level": 2,
      "target": null
    },
    {
      "__type": "Section",
      "__tag": 4015,
      "children": [
        {
          "__type": "Paragraph",
          "__tag": 4045,
          "children": [
            {
              "__type": "Text",
              "__tag": 4046,
              "value": "Support for alternative array API standard backends can be registered and documented using the "
            },
            {
              "__type": "InlineCode",
              "__tag": 4051,
              "value": "xp_capabilities"
            },
            {
              "__type": "Text",
              "__tag": 4046,
              "value": " decorator which has the following signature    "
            }
          ]
        },
        {
          "__type": "Code",
          "__tag": 4050,
          "value": "def xp_capabilities(\n    *,\n    # Alternative capabilities table.\n    # Used only for testing this decorator.\n    capabilities_table=None,\n    # Generate pytest.mark.skip/xfail_xp_backends.\n    # See documentation in conftest.py.\n    # lists of tuples [(module name, reason), ...]\n    skip_backends=(), xfail_backends=(),\n    cpu_only=False, np_only=False, reason=None,\n    out_of_scope=False, exceptions=(),\n    # lists of tuples [(module name, reason), ...]\n    warnings=(),\n    # xpx.testing.lazy_xp_function kwargs.\n    # Refer to array-api-extra documentation.\n    allow_dask_compute=False, jax_jit=True,\n    # Extra note to inject into the docstring\n    extra_note=None,\n    # Dictionary mapping method names to dictionaries of method\n    # specific capabilities for use when when xp_capabilities is\n    # applied to a class with varying capabilities per method\n    method_capabilities=None,\n    # Whether the function supports MArrays (used only in documentation)\n    marray=False,\n):",
          "execution_status": null,
          "out": ""
        },
        {
          "__type": "Paragraph",
          "__tag": 4045,
          "children": [
            {
              "__type": "Text",
              "__tag": 4046,
              "value": "This is available in "
            },
            {
              "__type": "InlineCode",
              "__tag": 4051,
              "value": "scipy._lib._array_api"
            },
            {
              "__type": "Text",
              "__tag": 4046,
              "value": " and can be applied to functions, methods, and classes to declare the current extent of their array API standard support. For the sake of brevity, in the remainder of this document, we write as if "
            },
            {
              "__type": "InlineCode",
              "__tag": 4051,
              "value": "xp_capabilities"
            },
            {
              "__type": "Text",
              "__tag": 4046,
              "value": " only applies to functions."
            }
          ]
        },
        {
          "__type": "Paragraph",
          "__tag": 4045,
          "children": [
            {
              "__type": "Text",
              "__tag": 4046,
              "value": "The "
            },
            {
              "__type": "InlineCode",
              "__tag": 4051,
              "value": "xp_capabilities"
            },
            {
              "__type": "Text",
              "__tag": 4046,
              "value": " decorator is what inserts the capabilities table into docstrings. It also allows developers to tag tests with the "
            },
            {
              "__type": "InlineCode",
              "__tag": 4051,
              "value": "@make_xp_test_case"
            },
            {
              "__type": "Text",
              "__tag": 4046,
              "value": " decorator or apply "
            },
            {
              "__type": "InlineCode",
              "__tag": 4051,
              "value": "make_xp_pytest_param"
            },
            {
              "__type": "Text",
              "__tag": 4046,
              "value": " to pytest parameters to automatically generate backend specific SKIP/XFAIL markers, and setting up testing that functions work with the JAX JIT or work in Dask lazily (i.e. without materializing arrays with "
            },
            {
              "__type": "InlineCode",
              "__tag": 4051,
              "value": "dask.compute"
            },
            {
              "__type": "Text",
              "__tag": 4046,
              "value": " or otherwise triggering computation with "
            },
            {
              "__type": "InlineCode",
              "__tag": 4051,
              "value": "dask.persist"
            },
            {
              "__type": "Text",
              "__tag": 4046,
              "value": ")."
            }
          ]
        },
        {
          "__type": "Admonition",
          "__tag": 4056,
          "kind": "warning",
          "base_type": "warning",
          "children": [
            {
              "__type": "AdmonitionTitle",
              "__tag": 4055,
              "children": [
                {
                  "__type": "Text",
                  "__tag": 4046,
                  "value": "warning "
                }
              ]
            },
            {
              "__type": "Paragraph",
              "__tag": 4045,
              "children": [
                {
                  "__type": "Text",
                  "__tag": 4046,
                  "value": "The modification of docstrings by "
                },
                {
                  "__type": "InlineCode",
                  "__tag": 4051,
                  "value": "xp_capabilities"
                },
                {
                  "__type": "Text",
                  "__tag": 4046,
                  "value": " can potentially break intersphinx references because it currently has the side effect of replacing implicit roles with "
                },
                {
                  "__type": "InlineCode",
                  "__tag": 4051,
                  "value": ":func:"
                },
                {
                  "__type": "Text",
                  "__tag": 4046,
                  "value": ". This can be avoided by explicitly setting the role for references to classes and methods that are outside of SciPy. The following snippet is taken from the docstring for "
                },
                {
                  "__type": "CrossRef",
                  "__tag": 4002,
                  "value": "detrend",
                  "reference": {
                    "__type": "RefInfo",
                    "__tag": 4000,
                    "module": "scipy",
                    "version": "?",
                    "kind": "module",
                    "path": "scipy.signal._signaltools:detrend"
                  },
                  "kind": "module"
                },
                {
                  "__type": "Text",
                  "__tag": 4046,
                  "value": " where the role "
                },
                {
                  "__type": "InlineCode",
                  "__tag": 4051,
                  "value": ":meth:"
                },
                {
                  "__type": "Text",
                  "__tag": 4046,
                  "value": " for a class method is needed."
                }
              ]
            },
            {
              "__type": "Code",
              "__tag": 4050,
              "value": "See Also\n--------\n:meth:`numpy.polynomial.polynomial.Polynomial.fit` : Create least squares fit polynomial",
              "execution_status": null,
              "out": ""
            }
          ]
        },
        {
          "__type": "Paragraph",
          "__tag": 4045,
          "children": [
            {
              "__type": "Text",
              "__tag": 4046,
              "value": "Note that in some modules a systematic process for delegation to native implementations is set up, where functions are replaced with wrappers that perform delegation. In this case, "
            },
            {
              "__type": "InlineCode",
              "__tag": 4051,
              "value": "xp_capabilities"
            },
            {
              "__type": "Text",
              "__tag": 4046,
              "value": " is not always applied as a decorator with "
            },
            {
              "__type": "InlineCode",
              "__tag": 4051,
              "value": "@"
            },
            {
              "__type": "Text",
              "__tag": 4046,
              "value": " syntax, but may instead be applied programatically on the wrappers. When working on array API standard support within a module, its important to be aware of how such delegation is set up, if any, and how "
            },
            {
              "__type": "InlineCode",
              "__tag": 4051,
              "value": "xp_capabilities"
            },
            {
              "__type": "Text",
              "__tag": 4046,
              "value": " is being applied. A common practice currently is to have a file, "
            },
            {
              "__type": "InlineCode",
              "__tag": 4051,
              "value": "_support_alternative_backends.py"
            },
            {
              "__type": "Text",
              "__tag": 4046,
              "value": " within a module that sets up such delegation. See for instance "
            },
            {
              "__type": "Link",
              "__tag": 4049,
              "children": [
                {
                  "__type": "Text",
                  "__tag": 4046,
                  "value": "scipy/signal/_support_alternative_backends.py"
                }
              ],
              "url": "https://github.com/scipy/scipy/blob/main/scipy/signal/_support_alternative_backends.py",
              "title": ""
            },
            {
              "__type": "Text",
              "__tag": 4046,
              "value": "."
            }
          ]
        }
      ],
      "title": [
        {
          "__type": "Text",
          "__tag": 4046,
          "value": "Documenting array API standard support"
        }
      ],
      "level": 1,
      "target": null
    },
    {
      "__type": "Section",
      "__tag": 4015,
      "children": [
        {
          "__type": "Paragraph",
          "__tag": 4045,
          "children": [
            {
              "__type": "Text",
              "__tag": 4046,
              "value": "Using "
            },
            {
              "__type": "InlineCode",
              "__tag": 4051,
              "value": "xp_capabilities"
            },
            {
              "__type": "Text",
              "__tag": 4046,
              "value": "  with no arguments, like this    "
            }
          ]
        },
        {
          "__type": "Code",
          "__tag": 4050,
          "value": "@xp_capabilities()\ndef my_function(x):\n    ...",
          "execution_status": null,
          "out": ""
        },
        {
          "__type": "Paragraph",
          "__tag": 4045,
          "children": [
            {
              "__type": "Text",
              "__tag": 4046,
              "value": "declares that a function works on all supported backends, on JAX with the JIT and lazily in Dask. This is most likely to occur if a function is written entirely in terms of the array API standard as described earlier in this document. Such functions are commonly referred to as array-agnostic. For functions which are written mostly in terms of the array API standard, but include calls to compiled code sandwiched between conversions to and from NumPy, "
            },
            {
              "__type": "InlineCode",
              "__tag": 4051,
              "value": "xp_capabilities"
            },
            {
              "__type": "Text",
              "__tag": 4046,
              "value": " should be given the "
            },
            {
              "__type": "InlineCode",
              "__tag": 4051,
              "value": "cpu_only=True"
            },
            {
              "__type": "Text",
              "__tag": 4046,
              "value": " option. Backends which are supported on GPU by such a function "
            },
            {
              "__type": "InlineCode",
              "__tag": 4051,
              "value": "f"
            },
            {
              "__type": "Text",
              "__tag": 4046,
              "value": " through delegation to a native implementation can be specified with the "
            },
            {
              "__type": "InlineCode",
              "__tag": 4051,
              "value": "exceptions"
            },
            {
              "__type": "Text",
              "__tag": 4046,
              "value": " kwarg, which in this case takes a list of strings specifying GPU-capable backends. The currently supported string values when using "
            },
            {
              "__type": "InlineCode",
              "__tag": 4051,
              "value": "cpu_only=True"
            },
            {
              "__type": "Text",
              "__tag": 4046,
              "value": " are "
            },
            {
              "__type": "InlineCode",
              "__tag": 4051,
              "value": "\"cupy\""
            },
            {
              "__type": "Text",
              "__tag": 4046,
              "value": ", "
            },
            {
              "__type": "InlineCode",
              "__tag": 4051,
              "value": "\"torch\""
            },
            {
              "__type": "Text",
              "__tag": 4046,
              "value": ", and "
            },
            {
              "__type": "InlineCode",
              "__tag": 4051,
              "value": "\"jax.numpy\""
            },
            {
              "__type": "Text",
              "__tag": 4046,
              "value": "."
            }
          ]
        },
        {
          "__type": "Paragraph",
          "__tag": 4045,
          "children": [
            {
              "__type": "Text",
              "__tag": 4046,
              "value": "It is recommended to reserve "
            },
            {
              "__type": "InlineCode",
              "__tag": 4051,
              "value": "cpu_only=False"
            },
            {
              "__type": "Text",
              "__tag": 4046,
              "value": " (the default) for array-agnostic functions which are expected to work on all array API compliant backends, including ones not tested in SciPy and ones that do not yet exist. If a function is supported on GPU on all tested backends through delegation to respective native implementations, one should use "
            },
            {
              "__type": "InlineCode",
              "__tag": 4051,
              "value": "cpu_only=True"
            },
            {
              "__type": "Text",
              "__tag": 4046,
              "value": " while listing each backend in the list of "
            },
            {
              "__type": "InlineCode",
              "__tag": 4051,
              "value": "exceptions"
            },
            {
              "__type": "Text",
              "__tag": 4046,
              "value": " like so    "
            }
          ]
        },
        {
          "__type": "Code",
          "__tag": 4050,
          "value": "@xp_capabilities(cpu_only=True, exceptions=[\"cupy\"])",
          "execution_status": null,
          "out": ""
        },
        {
          "__type": "Paragraph",
          "__tag": 4045,
          "children": [
            {
              "__type": "Text",
              "__tag": 4046,
              "value": "When setting "
            },
            {
              "__type": "InlineCode",
              "__tag": 4051,
              "value": "cpu_only=True"
            },
            {
              "__type": "Text",
              "__tag": 4046,
              "value": ", one may list a reason by passing a string with the "
            },
            {
              "__type": "InlineCode",
              "__tag": 4051,
              "value": "reason"
            },
            {
              "__type": "Text",
              "__tag": 4046,
              "value": " kwarg. This can be helpful for documenting why something is not supported for other developers. The reason will appear in the "
            },
            {
              "__type": "InlineCode",
              "__tag": 4051,
              "value": "pytest"
            },
            {
              "__type": "Text",
              "__tag": 4046,
              "value": " output when the SciPy test suite is run with "
            },
            {
              "__type": "InlineCode",
              "__tag": 4051,
              "value": "pytest"
            },
            {
              "__type": "Text",
              "__tag": 4046,
              "value": " in verbose mode (with the "
            },
            {
              "__type": "InlineCode",
              "__tag": 4051,
              "value": "-v"
            },
            {
              "__type": "Text",
              "__tag": 4046,
              "value": " flag)."
            }
          ]
        }
      ],
      "title": [
        {
          "__type": "Text",
          "__tag": 4046,
          "value": "Basic behavior"
        }
      ],
      "level": 2,
      "target": null
    },
    {
      "__type": "Section",
      "__tag": 4015,
      "children": [
        {
          "__type": "Paragraph",
          "__tag": 4045,
          "children": [
            {
              "__type": "Text",
              "__tag": 4046,
              "value": "One may declare a function as not supporting the JAX JIT with the option "
            },
            {
              "__type": "InlineCode",
              "__tag": 4051,
              "value": "jax_jit=False"
            },
            {
              "__type": "Text",
              "__tag": 4046,
              "value": ". See the earlier note on "
            },
            {
              "__type": "CrossRef",
              "__tag": 4002,
              "value": "JAX support",
              "reference": {
                "__type": "LocalRef",
                "__tag": 4022,
                "kind": "docs",
                "path": "dev:api-dev:array_api"
              },
              "kind": "exists"
            },
            {
              "__type": "Text",
              "__tag": 4046,
              "value": " for more information."
            }
          ]
        }
      ],
      "title": [
        {
          "__type": "Text",
          "__tag": 4046,
          "value": "JAX JIT"
        }
      ],
      "level": 2,
      "target": null
    },
    {
      "__type": "Section",
      "__tag": 4015,
      "children": [
        {
          "__type": "Paragraph",
          "__tag": 4045,
          "children": [
            {
              "__type": "Text",
              "__tag": 4046,
              "value": "Functions which do not support the array API standard through the means described earlier in this document should either receive the "
            },
            {
              "__type": "InlineCode",
              "__tag": 4051,
              "value": "np_only=True"
            },
            {
              "__type": "Text",
              "__tag": 4046,
              "value": " option or the "
            },
            {
              "__type": "InlineCode",
              "__tag": 4051,
              "value": "out_of_scope=True"
            },
            {
              "__type": "Text",
              "__tag": 4046,
              "value": " option. The former should be used for functions which are not currently supported but which are considered in-scope for future support."
            }
          ]
        },
        {
          "__type": "Paragraph",
          "__tag": 4045,
          "children": [
            {
              "__type": "Text",
              "__tag": 4046,
              "value": "Functions for which array API support has "
            },
            {
              "__type": "Emphasis",
              "__tag": 4047,
              "children": [
                {
                  "__type": "Text",
                  "__tag": 4046,
                  "value": "not"
                }
              ]
            },
            {
              "__type": "Text",
              "__tag": 4046,
              "value": " been added following the procedures described earlier in this document, but for which delegation to a native implementation has been set up for one or more array API backends, should still use "
            },
            {
              "__type": "InlineCode",
              "__tag": 4051,
              "value": "np_only=True"
            },
            {
              "__type": "Text",
              "__tag": 4046,
              "value": " in their "
            },
            {
              "__type": "InlineCode",
              "__tag": 4051,
              "value": "xp_capabilities"
            },
            {
              "__type": "Text",
              "__tag": 4046,
              "value": " entries. Just as for "
            },
            {
              "__type": "InlineCode",
              "__tag": 4051,
              "value": "cpu_only=True"
            },
            {
              "__type": "Text",
              "__tag": 4046,
              "value": ", exceptions can be passed with the "
            },
            {
              "__type": "InlineCode",
              "__tag": 4051,
              "value": "exceptions"
            },
            {
              "__type": "Text",
              "__tag": 4046,
              "value": " kwarg (and also just as for "
            },
            {
              "__type": "InlineCode",
              "__tag": 4051,
              "value": "cpu_only=True"
            },
            {
              "__type": "Text",
              "__tag": 4046,
              "value": " one can pass a reason with the "
            },
            {
              "__type": "InlineCode",
              "__tag": 4051,
              "value": "reason"
            },
            {
              "__type": "Text",
              "__tag": 4046,
              "value": " kwarg)    "
            }
          ]
        },
        {
          "__type": "Code",
          "__tag": 4050,
          "value": "@xp_capabilities(\n    np_only=True, exceptions=[\"cupy\"],\n    reason=\"not converted yet but has CuPy delegation.\"\n)",
          "execution_status": null,
          "out": ""
        },
        {
          "__type": "Paragraph",
          "__tag": 4045,
          "children": [
            {
              "__type": "Text",
              "__tag": 4046,
              "value": "Valid strings to pass in the exceptions list are "
            },
            {
              "__type": "InlineCode",
              "__tag": 4051,
              "value": "\"array_api_strict\""
            },
            {
              "__type": "Text",
              "__tag": 4046,
              "value": ", "
            },
            {
              "__type": "InlineCode",
              "__tag": 4051,
              "value": "\"cupy\""
            },
            {
              "__type": "Text",
              "__tag": 4046,
              "value": ", "
            },
            {
              "__type": "InlineCode",
              "__tag": 4051,
              "value": "\"dask.array\""
            },
            {
              "__type": "Text",
              "__tag": 4046,
              "value": ", "
            },
            {
              "__type": "InlineCode",
              "__tag": 4051,
              "value": "\"jax.numpy\""
            },
            {
              "__type": "Text",
              "__tag": 4046,
              "value": ", and "
            },
            {
              "__type": "InlineCode",
              "__tag": 4051,
              "value": "\"torch\""
            },
            {
              "__type": "Text",
              "__tag": 4046,
              "value": "."
            }
          ]
        },
        {
          "__type": "Paragraph",
          "__tag": 4045,
          "children": [
            {
              "__type": "Text",
              "__tag": 4046,
              "value": "If "
            },
            {
              "__type": "InlineCode",
              "__tag": 4051,
              "value": "np_only=True"
            },
            {
              "__type": "Text",
              "__tag": 4046,
              "value": " and "
            },
            {
              "__type": "InlineCode",
              "__tag": 4051,
              "value": "\"torch\""
            },
            {
              "__type": "Text",
              "__tag": 4046,
              "value": " or "
            },
            {
              "__type": "InlineCode",
              "__tag": 4051,
              "value": "\"jax.numpy\""
            },
            {
              "__type": "Text",
              "__tag": 4046,
              "value": " is added to the lists of exceptions, it will be declared as supported on both CPU and GPU."
            }
          ]
        },
        {
          "__type": "Target",
          "__tag": 4061,
          "label": "dev-arrayapi_jax_jit_no_gpu",
          "url": null
        },
        {
          "__type": "Paragraph",
          "__tag": 4045,
          "children": [
            {
              "__type": "InlineCode",
              "__tag": 4051,
              "value": "out_of_scope=True"
            },
            {
              "__type": "Text",
              "__tag": 4046,
              "value": " signals that there is no intention to ever provide array API support for a given function. There is not yet a formal policy for which functions should be considered out-of-scope. Some general rules of thumb that are being followed are to exclude:"
            }
          ]
        },
        {
          "__type": "BulletList",
          "__tag": 4053,
          "ordered": false,
          "start": 1,
          "children": [
            {
              "__type": "ListItem",
              "__tag": 4054,
              "children": [
                {
                  "__type": "Paragraph",
                  "__tag": 4045,
                  "children": [
                    {
                      "__type": "Text",
                      "__tag": 4046,
                      "value": "functions which do not operate on arrays such as "
                    },
                    {
                      "__type": "CrossRef",
                      "__tag": 4002,
                      "value": "scipy.constants.value",
                      "reference": {
                        "__type": "LocalRef",
                        "__tag": 4022,
                        "kind": "docs",
                        "path": "dev:api-dev:..:..:reference:generated:scipy.constants.value"
                      },
                      "kind": "docs"
                    }
                  ]
                }
              ]
            },
            {
              "__type": "ListItem",
              "__tag": 4054,
              "children": [
                {
                  "__type": "Paragraph",
                  "__tag": 4045,
                  "children": [
                    {
                      "__type": "Text",
                      "__tag": 4046,
                      "value": "functions which are too implementation specific such as those in "
                    },
                    {
                      "__type": "CrossRef",
                      "__tag": 4002,
                      "value": "scipy.linalg.blas",
                      "reference": {
                        "__type": "LocalRef",
                        "__tag": 4022,
                        "kind": "module",
                        "path": "scipy.linalg.blas"
                      },
                      "kind": "module"
                    },
                    {
                      "__type": "Text",
                      "__tag": 4046,
                      "value": " which give direct wrappers to low-level BLAS routines."
                    }
                  ]
                }
              ]
            },
            {
              "__type": "ListItem",
              "__tag": 4054,
              "children": [
                {
                  "__type": "Paragraph",
                  "__tag": 4045,
                  "children": [
                    {
                      "__type": "Text",
                      "__tag": 4046,
                      "value": "functions which would inherently be very difficult or even impossible to compute efficiently on accelerated computing devices."
                    }
                  ]
                }
              ]
            }
          ]
        },
        {
          "__type": "Paragraph",
          "__tag": 4045,
          "children": [
            {
              "__type": "Text",
              "__tag": 4046,
              "value": "As an example, the contents of "
            },
            {
              "__type": "CrossRef",
              "__tag": 4002,
              "value": "scipy.odr",
              "reference": {
                "__type": "RefInfo",
                "__tag": 4000,
                "module": "scipy",
                "version": "?",
                "kind": "module",
                "path": "scipy.odr"
              },
              "kind": "module"
            },
            {
              "__type": "Text",
              "__tag": 4046,
              "value": " are considered out-of-scope for a combination of reasons 2 and 3 above. "
            },
            {
              "__type": "CrossRef",
              "__tag": 4002,
              "value": "scipy.odr",
              "reference": {
                "__type": "RefInfo",
                "__tag": 4000,
                "module": "scipy",
                "version": "?",
                "kind": "module",
                "path": "scipy.odr"
              },
              "kind": "module"
            },
            {
              "__type": "Text",
              "__tag": 4046,
              "value": " essentially provides a direct wrapper of the monolithic ODRPACK Fortran library, and its API is tied to the structure of this monolithic library. An efficient GPU accelerated implementation of nonlinear weighted orthogonal distance regression would benefit from not having to support an API so tightly coupled to ODRPACK but is also a challenging problem in its own right."
            }
          ]
        },
        {
          "__type": "Paragraph",
          "__tag": 4045,
          "children": [
            {
              "__type": "Text",
              "__tag": 4046,
              "value": "(Since the previous paragraph was written "
            },
            {
              "__type": "CrossRef",
              "__tag": 4002,
              "value": "scipy.odr",
              "reference": {
                "__type": "RefInfo",
                "__tag": 4000,
                "module": "scipy",
                "version": "?",
                "kind": "module",
                "path": "scipy.odr"
              },
              "kind": "module"
            },
            {
              "__type": "Text",
              "__tag": 4046,
              "value": " has been slated for deprecation. Things that are deprecated are inherently out-of-scope)."
            }
          ]
        },
        {
          "__type": "Paragraph",
          "__tag": 4045,
          "children": [
            {
              "__type": "Text",
              "__tag": 4046,
              "value": "Considerations of what to consider in-scope are evolving, and something which is now considered out-of-scope may be decided to be in-scope in the future if sufficient user interest and feasability are demonstrated."
            }
          ]
        }
      ],
      "title": [
        {
          "__type": "Text",
          "__tag": 4046,
          "value": "Unsupported functions"
        }
      ],
      "level": 2,
      "target": null
    },
    {
      "__type": "Section",
      "__tag": 4015,
      "children": [
        {
          "__type": "Paragraph",
          "__tag": 4045,
          "children": [
            {
              "__type": "Text",
              "__tag": 4046,
              "value": "One may pass lists of tuples of backend string, reason pairs to "
            },
            {
              "__type": "InlineCode",
              "__tag": 4051,
              "value": "xp_capabilities"
            },
            {
              "__type": "Text",
              "__tag": 4046,
              "value": " with the "
            },
            {
              "__type": "InlineCode",
              "__tag": 4051,
              "value": "skip_backends"
            },
            {
              "__type": "Text",
              "__tag": 4046,
              "value": " and "
            },
            {
              "__type": "InlineCode",
              "__tag": 4051,
              "value": "xfail_backends"
            },
            {
              "__type": "Text",
              "__tag": 4046,
              "value": " kwargs. The valid backend strings are "
            },
            {
              "__type": "InlineCode",
              "__tag": 4051,
              "value": "\"array_api_strict\""
            },
            {
              "__type": "Text",
              "__tag": 4046,
              "value": ", "
            },
            {
              "__type": "InlineCode",
              "__tag": 4051,
              "value": "\"cupy\""
            },
            {
              "__type": "Text",
              "__tag": 4046,
              "value": ", "
            },
            {
              "__type": "InlineCode",
              "__tag": 4051,
              "value": "\"dask.array\""
            },
            {
              "__type": "Text",
              "__tag": 4046,
              "value": ", "
            },
            {
              "__type": "InlineCode",
              "__tag": 4051,
              "value": "\"jax.numpy\""
            },
            {
              "__type": "Text",
              "__tag": 4046,
              "value": " and "
            },
            {
              "__type": "InlineCode",
              "__tag": 4051,
              "value": "\"torch\""
            },
            {
              "__type": "Text",
              "__tag": 4046,
              "value": " (note that one should almost never want to skip tests for "
            },
            {
              "__type": "Link",
              "__tag": 4049,
              "children": [
                {
                  "__type": "Text",
                  "__tag": 4046,
                  "value": "array_api_strict"
                }
              ],
              "url": "https://data-apis.org/array-api-strict/",
              "title": ""
            },
            {
              "__type": "Text",
              "__tag": 4046,
              "value": " as failures with this backend most likely indicate a failure to correctly follow the array API standard)."
            }
          ]
        },
        {
          "__type": "Paragraph",
          "__tag": 4045,
          "children": [
            {
              "__type": "Text",
              "__tag": 4046,
              "value": "Any backend passed in such a way with either kwarg is declared as unsupported with both CPU and GPU. The difference between "
            },
            {
              "__type": "InlineCode",
              "__tag": 4051,
              "value": "skip_backends"
            },
            {
              "__type": "Text",
              "__tag": 4046,
              "value": " and "
            },
            {
              "__type": "InlineCode",
              "__tag": 4051,
              "value": "xfail_backends"
            },
            {
              "__type": "Text",
              "__tag": 4046,
              "value": " is that for tests using the "
            },
            {
              "__type": "InlineCode",
              "__tag": 4051,
              "value": "xp"
            },
            {
              "__type": "Text",
              "__tag": 4046,
              "value": " fixture, "
            },
            {
              "__type": "InlineCode",
              "__tag": 4051,
              "value": "skip_backends"
            },
            {
              "__type": "Text",
              "__tag": 4046,
              "value": " adds "
            },
            {
              "__type": "InlineCode",
              "__tag": 4051,
              "value": "pytest.skip"
            },
            {
              "__type": "Text",
              "__tag": 4046,
              "value": " markers for backends and the corresponding tests are skipped entirely, while with "
            },
            {
              "__type": "InlineCode",
              "__tag": 4051,
              "value": "xfail_backends"
            },
            {
              "__type": "Text",
              "__tag": 4046,
              "value": ", "
            },
            {
              "__type": "InlineCode",
              "__tag": 4051,
              "value": "pytest.xfail"
            },
            {
              "__type": "Text",
              "__tag": 4046,
              "value": " markers are added, and tests are still run but expected to fail."
            }
          ]
        },
        {
          "__type": "Paragraph",
          "__tag": 4045,
          "children": [
            {
              "__type": "Text",
              "__tag": 4046,
              "value": "One example in which it is pertinent to use "
            },
            {
              "__type": "InlineCode",
              "__tag": 4051,
              "value": "skip_backends"
            },
            {
              "__type": "Text",
              "__tag": 4046,
              "value": " is for functions which otherwise support the array API standard, but use features which are not available on a particular backend, such as mutation of arrays through item assignment, which is not supported in JAX. For instance the following can be used to signify a function which is otherwise array-agnostic, but uses in-place item-assignment    "
            }
          ]
        },
        {
          "__type": "Code",
          "__tag": 4050,
          "value": "@xp_capabilities(\n    skip_backends=[(\"jax.numpy\", \"in-place item assignment\")]\n)\ndef function_with_internal_mutation(x):\n    ...",
          "execution_status": null,
          "out": ""
        },
        {
          "__type": "Paragraph",
          "__tag": 4045,
          "children": [
            {
              "__type": "Text",
              "__tag": 4046,
              "value": "Another case is when there is a bug in the corresponding array library, in which case the "
            },
            {
              "__type": "InlineCode",
              "__tag": 4051,
              "value": "reason"
            },
            {
              "__type": "Text",
              "__tag": 4046,
              "value": " string should contain a link to the upstream issue."
            }
          ]
        },
        {
          "__type": "Paragraph",
          "__tag": 4045,
          "children": [
            {
              "__type": "Text",
              "__tag": 4046,
              "value": "In the caveat above about functions with JAX JIT support but no JAX GPU support we discussed the edge-case of a function which has not been given array API standard support in the usual way, is available on JAX through delegation to a native implementation which supports "
            },
            {
              "__type": "InlineCode",
              "__tag": 4051,
              "value": "jax.jit"
            },
            {
              "__type": "Text",
              "__tag": 4046,
              "value": ", but does not work on the GPU. For now, such situations can in principle be handled by using "
            },
            {
              "__type": "InlineCode",
              "__tag": 4051,
              "value": "cpu_only=True"
            },
            {
              "__type": "Text",
              "__tag": 4046,
              "value": " and passing in any backends which are not supported even on CPU to "
            },
            {
              "__type": "InlineCode",
              "__tag": 4051,
              "value": "skip_backends"
            },
            {
              "__type": "Text",
              "__tag": 4046,
              "value": "    "
            }
          ]
        },
        {
          "__type": "Code",
          "__tag": 4050,
          "value": "  @xp_capabilities(\n      cpu_only=True,\n      skip_backends=[\n          (\"array_api_strict\", \"not supported\"),\n\t  (\"cupy\", \"not supported\"),\n\t  (\"dask.array\", \"not supported\"),\n\t  (\"torch\", \"not supported\"),\n      ]\n  )\n  def oddball_function(x):\n      ...",
          "execution_status": null,
          "out": ""
        },
        {
          "__type": "Paragraph",
          "__tag": 4045,
          "children": [
            {
              "__type": "Text",
              "__tag": 4046,
              "value": "Such situations are hopefully rare enough that special handling isn't needed. "
            },
            {
              "__type": "InlineCode",
              "__tag": 4051,
              "value": "xp_capabilities"
            },
            {
              "__type": "Text",
              "__tag": 4046,
              "value": " has evolved naturally over time to meet developer needs; good suggestions for ways to improve developer ergonomics are welcome."
            }
          ]
        }
      ],
      "title": [
        {
          "__type": "InlineCode",
          "__tag": 4051,
          "value": "skip_backends"
        },
        {
          "__type": "Text",
          "__tag": 4046,
          "value": " and "
        },
        {
          "__type": "InlineCode",
          "__tag": 4051,
          "value": "xfail_backends"
        }
      ],
      "level": 2,
      "target": "dev-arrayapi_skip_xfail_backends"
    },
    {
      "__type": "Section",
      "__tag": 4015,
      "children": [
        {
          "__type": "Paragraph",
          "__tag": 4045,
          "children": [
            {
              "__type": "Text",
              "__tag": 4046,
              "value": "The default, "
            },
            {
              "__type": "InlineCode",
              "__tag": 4051,
              "value": "allow_dask_compute=False"
            },
            {
              "__type": "Text",
              "__tag": 4046,
              "value": " declares that a function works lazily in Dask and will not materialize any Dask arrays with "
            },
            {
              "__type": "InlineCode",
              "__tag": 4051,
              "value": "dask.compute"
            },
            {
              "__type": "Text",
              "__tag": 4046,
              "value": " or otherwise initiate computation with "
            },
            {
              "__type": "InlineCode",
              "__tag": 4051,
              "value": "dask.persist"
            },
            {
              "__type": "Text",
              "__tag": 4046,
              "value": ". Use "
            },
            {
              "__type": "InlineCode",
              "__tag": 4051,
              "value": "allow_dask_compute=True"
            },
            {
              "__type": "Text",
              "__tag": 4046,
              "value": " to declare that a function supports Dask arrays but not lazily. Developers can also pass an integer to give a cap for the number of combined calls to "
            },
            {
              "__type": "InlineCode",
              "__tag": 4051,
              "value": "dask.compute"
            },
            {
              "__type": "Text",
              "__tag": 4046,
              "value": " and "
            },
            {
              "__type": "InlineCode",
              "__tag": 4051,
              "value": "dask.persist"
            },
            {
              "__type": "Text",
              "__tag": 4046,
              "value": " that are allowed. If a function is not array-agnostic, then it will typically be the case that "
            },
            {
              "__type": "InlineCode",
              "__tag": 4051,
              "value": "allow_dask_compute=True"
            },
            {
              "__type": "Text",
              "__tag": 4046,
              "value": " should be set, unless Dask specific codepaths have been added."
            }
          ]
        },
        {
          "__type": "Paragraph",
          "__tag": 4045,
          "children": [
            {
              "__type": "Text",
              "__tag": 4046,
              "value": "Dask support is currently deprioritized due to structural barriers that make the development of meaningful Dask support particularly challenging. At present, developers should feel free to reflexively add "
            },
            {
              "__type": "InlineCode",
              "__tag": 4051,
              "value": "skip_backends=[(\"dask.array\", \"deprioritized\")]"
            },
            {
              "__type": "Text",
              "__tag": 4046,
              "value": " to the "
            },
            {
              "__type": "InlineCode",
              "__tag": 4051,
              "value": "xp_capabilities"
            },
            {
              "__type": "Text",
              "__tag": 4046,
              "value": " entry of any function they are working on. Reprioritization may be considered in the future if a champion emerges and the structural outlook improves. See "
            },
            {
              "__type": "Link",
              "__tag": 4049,
              "children": [
                {
                  "__type": "Text",
                  "__tag": 4046,
                  "value": "RFC: Should Dask support remain a priority? #24205"
                }
              ],
              "url": "https://github.com/scipy/scipy/issues/24205",
              "title": ""
            },
            {
              "__type": "Text",
              "__tag": 4046,
              "value": " for relevant discussion."
            }
          ]
        }
      ],
      "title": [
        {
          "__type": "Text",
          "__tag": 4046,
          "value": "Dask Compute"
        }
      ],
      "level": 2,
      "target": null
    },
    {
      "__type": "Section",
      "__tag": 4015,
      "children": [
        {
          "__type": "Paragraph",
          "__tag": 4045,
          "children": [
            {
              "__type": "Text",
              "__tag": 4046,
              "value": "Some functions may be supported on an alternative backend, but only in particular cases, perhaps only for some values of a kwarg, for real arrays but not complex ones, or only for arrays with fewer than a given number of dimensions. Such caveats should be supplied with the "
            },
            {
              "__type": "InlineCode",
              "__tag": 4051,
              "value": "extra_note"
            },
            {
              "__type": "Text",
              "__tag": 4046,
              "value": " kwarg of "
            },
            {
              "__type": "InlineCode",
              "__tag": 4051,
              "value": "xp_capabilities"
            },
            {
              "__type": "Text",
              "__tag": 4046,
              "value": ". Note that the implementation of "
            },
            {
              "__type": "InlineCode",
              "__tag": 4051,
              "value": "extra_note"
            },
            {
              "__type": "Text",
              "__tag": 4046,
              "value": " simply inserts a string directly into the Notes section of the docstring, and one must be careful about whitespace. This is perhaps best demonstrated with an example    "
            }
          ]
        },
        {
          "__type": "Code",
          "__tag": 4050,
          "value": "uses_choose_conv_extra_note = (\n  \"\"\"CuPy does not support inputs with ``ndim>1`` when ``method=\"auto\"``\n  but does support higher dimensional arrays for ``method=\"direct\"``\n  and ``method=\"fft\"``.\n\n  \"\"\"\n)",
          "execution_status": null,
          "out": ""
        }
      ],
      "title": [
        {
          "__type": "InlineCode",
          "__tag": 4051,
          "value": "extra_note"
        }
      ],
      "level": 2,
      "target": "dev-arrayapi_extra_note"
    },
    {
      "__type": "Section",
      "__tag": 4015,
      "children": [
        {
          "__type": "Paragraph",
          "__tag": 4045,
          "children": [
            {
              "__type": "Text",
              "__tag": 4046,
              "value": "Use "
            },
            {
              "__type": "InlineCode",
              "__tag": 4051,
              "value": "marray=True"
            },
            {
              "__type": "Text",
              "__tag": 4046,
              "value": " to document support for MArrays below the backend capabilities table. Unless noted in "
            },
            {
              "__type": "InlineCode",
              "__tag": 4051,
              "value": "extra_note"
            },
            {
              "__type": "Text",
              "__tag": 4046,
              "value": ", MArrays are understood to be supported by all features of the function and in conjunction with all supported backends."
            }
          ]
        }
      ],
      "title": [
        {
          "__type": "InlineCode",
          "__tag": 4051,
          "value": "marray"
        }
      ],
      "level": 2,
      "target": "dev-arrayapi_marray"
    },
    {
      "__type": "Section",
      "__tag": 4015,
      "children": [
        {
          "__type": "Paragraph",
          "__tag": 4045,
          "children": [
            {
              "__type": "Text",
              "__tag": 4046,
              "value": "For classes with array API standard support, one must apply "
            },
            {
              "__type": "InlineCode",
              "__tag": 4051,
              "value": "xp_capabilities"
            },
            {
              "__type": "Text",
              "__tag": 4046,
              "value": " once to the class itself, not separately to individual methods. The class level capabilities should be decided based on best judgment of which backends are generally usable with the class in a holistic sense. If individual methods differ in their capabilities, this can be specified using the "
            },
            {
              "__type": "InlineCode",
              "__tag": 4051,
              "value": "method_capabilities"
            },
            {
              "__type": "Text",
              "__tag": 4046,
              "value": " kwarg of "
            },
            {
              "__type": "InlineCode",
              "__tag": 4051,
              "value": "xp_capabilities"
            },
            {
              "__type": "Text",
              "__tag": 4046,
              "value": " like in the example below    "
            }
          ]
        },
        {
          "__type": "Code",
          "__tag": 4050,
          "value": "  @xp_capabilities(\n      method_capabilities={\n          \"__init__\": dict(jax_jit=False),\n\t  \"bar\": dict(cpu_only=True, exceptions=[\"cupy\"], jax_jit=False),\n      }\n  )\n  class Foo:\n      def __init__(self, x):\n          ...\n      def bar(self, y):\n          # not array-agnostic but has delegation to CuPy to set up\n\t  ...\n      def baz(self, y):\n          # array-agnostic method\n\t  ...",
          "execution_status": null,
          "out": ""
        },
        {
          "__type": "Paragraph",
          "__tag": 4045,
          "children": [
            {
              "__type": "Text",
              "__tag": 4046,
              "value": "Adding "
            },
            {
              "__type": "InlineCode",
              "__tag": 4051,
              "value": "method_capabilities"
            },
            {
              "__type": "Text",
              "__tag": 4046,
              "value": " makes no changes to the documentation but does make it possible to access method level capabilities when adding tests and to test class methods with the JAX JIT. Documentation of method specific support and limitations should be added to the "
            },
            {
              "__type": "InlineCode",
              "__tag": 4051,
              "value": "extra_note"
            },
            {
              "__type": "Text",
              "__tag": 4046,
              "value": " described above."
            }
          ]
        },
        {
          "__type": "Paragraph",
          "__tag": 4045,
          "children": [
            {
              "__type": "InlineCode",
              "__tag": 4051,
              "value": "method_capabilities"
            },
            {
              "__type": "Text",
              "__tag": 4046,
              "value": " should be a dictionary mapping method names to dictionaries with keys corresponding to the usual arguments of "
            },
            {
              "__type": "InlineCode",
              "__tag": 4051,
              "value": "xp_capabilities"
            },
            {
              "__type": "Text",
              "__tag": 4046,
              "value": ". Keys that are not supplied in the inner dictionaries will be filled with the "
            },
            {
              "__type": "InlineCode",
              "__tag": 4051,
              "value": "xp_capabilities"
            },
            {
              "__type": "Text",
              "__tag": 4046,
              "value": " default values. Entries in "
            },
            {
              "__type": "InlineCode",
              "__tag": 4051,
              "value": "method_capabilities"
            },
            {
              "__type": "Text",
              "__tag": 4046,
              "value": " completely override the class level capabilities entry so that one can declare that some methods are supported on backends for which the class itself is considered unsupported; this is useful for incremental development. If a method has no corresponding entry in "
            },
            {
              "__type": "InlineCode",
              "__tag": 4051,
              "value": "method_capabilities"
            },
            {
              "__type": "Text",
              "__tag": 4046,
              "value": ", then by default, its capabilities will be the same as the class level capabilities."
            }
          ]
        }
      ],
      "title": [
        {
          "__type": "Text",
          "__tag": 4046,
          "value": "Applying "
        },
        {
          "__type": "InlineCode",
          "__tag": 4051,
          "value": "xp_capabilities"
        },
        {
          "__type": "Text",
          "__tag": 4046,
          "value": " to classes"
        }
      ],
      "level": 2,
      "target": null
    },
    {
      "__type": "Section",
      "__tag": 4015,
      "children": [
        {
          "__type": "Paragraph",
          "__tag": 4045,
          "children": [
            {
              "__type": "Text",
              "__tag": 4046,
              "value": "To run a test on multiple array backends, you should add the "
            },
            {
              "__type": "InlineCode",
              "__tag": 4051,
              "value": "xp"
            },
            {
              "__type": "Text",
              "__tag": 4046,
              "value": " fixture to it. "
            },
            {
              "__type": "InlineCode",
              "__tag": 4051,
              "value": "xp"
            },
            {
              "__type": "Text",
              "__tag": 4046,
              "value": " currently supports testing with the following backends:"
            }
          ]
        },
        {
          "__type": "BulletList",
          "__tag": 4053,
          "ordered": false,
          "start": 1,
          "children": [
            {
              "__type": "ListItem",
              "__tag": 4054,
              "children": [
                {
                  "__type": "Paragraph",
                  "__tag": 4045,
                  "children": [
                    {
                      "__type": "Link",
                      "__tag": 4049,
                      "children": [
                        {
                          "__type": "Text",
                          "__tag": 4046,
                          "value": "array_api_strict"
                        }
                      ],
                      "url": "https://data-apis.org/array-api-strict/",
                      "title": ""
                    }
                  ]
                }
              ]
            },
            {
              "__type": "ListItem",
              "__tag": 4054,
              "children": [
                {
                  "__type": "Paragraph",
                  "__tag": 4045,
                  "children": [
                    {
                      "__type": "Link",
                      "__tag": 4049,
                      "children": [
                        {
                          "__type": "Text",
                          "__tag": 4046,
                          "value": "cupy"
                        }
                      ],
                      "url": "https://cupy.dev/",
                      "title": ""
                    }
                  ]
                }
              ]
            },
            {
              "__type": "ListItem",
              "__tag": 4054,
              "children": [
                {
                  "__type": "Paragraph",
                  "__tag": 4045,
                  "children": [
                    {
                      "__type": "Link",
                      "__tag": 4049,
                      "children": [
                        {
                          "__type": "Text",
                          "__tag": 4046,
                          "value": "dask.array"
                        }
                      ],
                      "url": "https://www.dask.org/",
                      "title": ""
                    }
                  ]
                }
              ]
            },
            {
              "__type": "ListItem",
              "__tag": 4054,
              "children": [
                {
                  "__type": "Paragraph",
                  "__tag": 4045,
                  "children": [
                    {
                      "__type": "Link",
                      "__tag": 4049,
                      "children": [
                        {
                          "__type": "Text",
                          "__tag": 4046,
                          "value": "jax.numpy"
                        }
                      ],
                      "url": "https://docs.jax.dev/en/latest/",
                      "title": ""
                    },
                    {
                      "__type": "Text",
                      "__tag": 4046,
                      "value": ","
                    }
                  ]
                }
              ]
            },
            {
              "__type": "ListItem",
              "__tag": 4054,
              "children": [
                {
                  "__type": "Paragraph",
                  "__tag": 4045,
                  "children": [
                    {
                      "__type": "Link",
                      "__tag": 4049,
                      "children": [
                        {
                          "__type": "Text",
                          "__tag": 4046,
                          "value": "numpy"
                        }
                      ],
                      "url": "https://numpy.org/",
                      "title": ""
                    }
                  ]
                }
              ]
            },
            {
              "__type": "ListItem",
              "__tag": 4054,
              "children": [
                {
                  "__type": "Paragraph",
                  "__tag": 4045,
                  "children": [
                    {
                      "__type": "Link",
                      "__tag": 4049,
                      "children": [
                        {
                          "__type": "Text",
                          "__tag": 4046,
                          "value": "torch"
                        }
                      ],
                      "url": "https://pytorch.org/",
                      "title": ""
                    }
                  ]
                }
              ]
            }
          ]
        },
        {
          "__type": "Paragraph",
          "__tag": 4045,
          "children": [
            {
              "__type": "InlineCode",
              "__tag": 4051,
              "value": "xp"
            },
            {
              "__type": "Text",
              "__tag": 4046,
              "value": " is a "
            },
            {
              "__type": "Link",
              "__tag": 4049,
              "children": [
                {
                  "__type": "Text",
                  "__tag": 4046,
                  "value": "pytest fixture"
                }
              ],
              "url": "https://docs.pytest.org/en/6.2.x/fixture.html",
              "title": ""
            },
            {
              "__type": "Text",
              "__tag": 4046,
              "value": " which is parameterized over all currently installed backends among those listed above. Note that "
            },
            {
              "__type": "InlineCode",
              "__tag": 4051,
              "value": "xp"
            },
            {
              "__type": "Text",
              "__tag": 4046,
              "value": " takes values from the set of \"raw\" namespaces, not the wrapped namespaces from "
            },
            {
              "__type": "CrossRef",
              "__tag": 4002,
              "value": "array_api_compat",
              "reference": {
                "__type": "LocalRef",
                "__tag": 4022,
                "kind": "docs",
                "path": "dev:api-dev:array_api"
              },
              "kind": "exists"
            },
            {
              "__type": "Text",
              "__tag": 4046,
              "value": "."
            }
          ]
        },
        {
          "__type": "Paragraph",
          "__tag": 4045,
          "children": [
            {
              "__type": "InlineCode",
              "__tag": 4051,
              "value": "scipy._lib._array_api"
            },
            {
              "__type": "Text",
              "__tag": 4046,
              "value": " provides the "
            },
            {
              "__type": "InlineCode",
              "__tag": 4051,
              "value": "make_xp_test_case"
            },
            {
              "__type": "Text",
              "__tag": 4046,
              "value": " decorator, and the "
            },
            {
              "__type": "InlineCode",
              "__tag": 4051,
              "value": "make_xp_pytest_param"
            },
            {
              "__type": "Text",
              "__tag": 4046,
              "value": " and "
            },
            {
              "__type": "InlineCode",
              "__tag": 4051,
              "value": "make_xp_pytest_marks"
            },
            {
              "__type": "Text",
              "__tag": 4046,
              "value": " functions to declare which functions are being tested by a test.  These draw on the "
            },
            {
              "__type": "InlineCode",
              "__tag": 4051,
              "value": "xp_capabilities"
            },
            {
              "__type": "Text",
              "__tag": 4046,
              "value": " entries for a function (or in some cases those for a list of functions) to insert the relevant backend specific skip and xfail markers."
            }
          ]
        },
        {
          "__type": "Paragraph",
          "__tag": 4045,
          "children": [
            {
              "__type": "Strong",
              "__tag": 4048,
              "children": [
                {
                  "__type": "Text",
                  "__tag": 4046,
                  "value": "make_xp_test_case:"
                }
              ]
            }
          ]
        },
        {
          "__type": "Paragraph",
          "__tag": 4045,
          "children": [
            {
              "__type": "Text",
              "__tag": 4046,
              "value": "In most cases, developers should use "
            },
            {
              "__type": "InlineCode",
              "__tag": 4051,
              "value": "make_xp_test_case"
            },
            {
              "__type": "Text",
              "__tag": 4046,
              "value": ", which is applied as a decorator to a test function, test method, or entire test class. Applying it to a test class is equivalent to applying it to each method separately. The decorator can be applied at both the class and method level as below    "
            }
          ]
        },
        {
          "__type": "Code",
          "__tag": 4050,
          "value": "@make_xp_test_case(my_function)\nclass TestMyFunction:\n    def test1(self, xp):\n        ...\n\n    @make_xp_test_case(other_function)\n    def test_integration_with_other_function(self, xp)\n        ...",
          "execution_status": null,
          "out": ""
        },
        {
          "__type": "Paragraph",
          "__tag": 4045,
          "children": [
            {
              "__type": "Text",
              "__tag": 4046,
              "value": "Applying "
            },
            {
              "__type": "InlineCode",
              "__tag": 4051,
              "value": "@make_xp_test_case(my_function)"
            },
            {
              "__type": "Text",
              "__tag": 4046,
              "value": " to "
            },
            {
              "__type": "InlineCode",
              "__tag": 4051,
              "value": "TestMyFunction"
            },
            {
              "__type": "Text",
              "__tag": 4046,
              "value": " causes all skips and xfails from the "
            },
            {
              "__type": "InlineCode",
              "__tag": 4051,
              "value": "xp_capabilities"
            },
            {
              "__type": "Text",
              "__tag": 4046,
              "value": " entry for "
            },
            {
              "__type": "InlineCode",
              "__tag": 4051,
              "value": "my_function"
            },
            {
              "__type": "Text",
              "__tag": 4046,
              "value": " to be applied to all methods in the class. Additional applications of "
            },
            {
              "__type": "InlineCode",
              "__tag": 4051,
              "value": "@make_xp_test_case"
            },
            {
              "__type": "Text",
              "__tag": 4046,
              "value": " to individual methods will add additional skips and xfails and not override the class level decorator. Below is an equivalent way to write the above example. This style can become unwieldy when there are many methods in a class testing the same function.    "
            }
          ]
        },
        {
          "__type": "Code",
          "__tag": 4050,
          "value": "class TestMyFunction:\n    @make_xp_test_case(my_function)\n    def test1(self, xp):\n        ...\n\n    @make_xp_test_case(my_function, other_function)\n    def test_integration_with_other_function(self, xp)\n        ...",
          "execution_status": null,
          "out": ""
        },
        {
          "__type": "Paragraph",
          "__tag": 4045,
          "children": [
            {
              "__type": "Strong",
              "__tag": 4048,
              "children": [
                {
                  "__type": "Text",
                  "__tag": 4046,
                  "value": "make_xp_pytest_param:"
                }
              ]
            }
          ]
        },
        {
          "__type": "Paragraph",
          "__tag": 4045,
          "children": [
            {
              "__type": "InlineCode",
              "__tag": 4051,
              "value": "make_xp_pytest_param"
            },
            {
              "__type": "Text",
              "__tag": 4046,
              "value": " covers the situation where a common test body is parametrized over a list of functions using "
            },
            {
              "__type": "InlineCode",
              "__tag": 4051,
              "value": "pytest.mark.parametrize"
            },
            {
              "__type": "Text",
              "__tag": 4046,
              "value": ". It is not used as frequently as "
            },
            {
              "__type": "InlineCode",
              "__tag": 4051,
              "value": "make_xp_test_case"
            },
            {
              "__type": "Text",
              "__tag": 4046,
              "value": " but this kind of situation is not too uncommon.    "
            }
          ]
        },
        {
          "__type": "Code",
          "__tag": 4050,
          "value": "@pytest.mark.parametrize(\n    \"func\",\n    [make_xp_pytest_param(func) for func in tested_funcs]\n)\ndef test_foo(func, xp):\n    ...",
          "execution_status": null,
          "out": ""
        },
        {
          "__type": "Paragraph",
          "__tag": 4045,
          "children": [
            {
              "__type": "Text",
              "__tag": 4046,
              "value": "Without access to "
            },
            {
              "__type": "InlineCode",
              "__tag": 4051,
              "value": "make_xp_pytest_param"
            },
            {
              "__type": "Text",
              "__tag": 4046,
              "value": ", one might instead have to do something like    "
            }
          ]
        },
        {
          "__type": "Code",
          "__tag": 4050,
          "value": "@make_xp_test_case(*test_funcs)\n@pytest.mark.parametrize(\n    \"func\", tested_funcs\n)\ndef test_foo(func, xp):\n    ...",
          "execution_status": null,
          "out": ""
        },
        {
          "__type": "Paragraph",
          "__tag": 4045,
          "children": [
            {
              "__type": "Text",
              "__tag": 4046,
              "value": "But then "
            },
            {
              "__type": "InlineCode",
              "__tag": 4051,
              "value": "test_foo"
            },
            {
              "__type": "Text",
              "__tag": 4046,
              "value": " would take on the collective skips and xfails for all of the functions in "
            },
            {
              "__type": "InlineCode",
              "__tag": 4051,
              "value": "test_funcs"
            },
            {
              "__type": "Text",
              "__tag": 4046,
              "value": " taken together, leading to tests being run with unnecessary skips and xfails."
            }
          ]
        },
        {
          "__type": "Paragraph",
          "__tag": 4045,
          "children": [
            {
              "__type": "Text",
              "__tag": 4046,
              "value": "Unlike "
            },
            {
              "__type": "InlineCode",
              "__tag": 4051,
              "value": "make_xp_test_case"
            },
            {
              "__type": "Text",
              "__tag": 4046,
              "value": ", only a single function can be passed to any given call to "
            },
            {
              "__type": "InlineCode",
              "__tag": 4051,
              "value": "make_xp_pytest_param"
            },
            {
              "__type": "Text",
              "__tag": 4046,
              "value": ". Additional arguments specify additional parameters for "
            },
            {
              "__type": "InlineCode",
              "__tag": 4051,
              "value": "pytest.mark.parametrize"
            },
            {
              "__type": "Text",
              "__tag": 4046,
              "value": ", such as in the example below    "
            }
          ]
        },
        {
          "__type": "Code",
          "__tag": 4050,
          "value": "  @pytest.mark.parametrize(\n      \"func,norm\",\n      [\n          make_xp_pytest_param(func, norm)\n\t  for func, norm in it.product(tested_functions, [True, False])\n      ]\n   )\n   def test_normed_foo(func, norm, xp):\n       ...",
          "execution_status": null,
          "out": ""
        },
        {
          "__type": "Paragraph",
          "__tag": 4045,
          "children": [
            {
              "__type": "Strong",
              "__tag": 4048,
              "children": [
                {
                  "__type": "Text",
                  "__tag": 4046,
                  "value": "make_xp_pytest_marks:"
                }
              ]
            }
          ]
        },
        {
          "__type": "Paragraph",
          "__tag": 4045,
          "children": [
            {
              "__type": "InlineCode",
              "__tag": 4051,
              "value": "make_xp_pytest_marks"
            },
            {
              "__type": "Text",
              "__tag": 4046,
              "value": " is rarely used. It directly returns a list of pytest marks which can be used with the "
            },
            {
              "__type": "InlineCode",
              "__tag": 4051,
              "value": "pytestmark = ..."
            },
            {
              "__type": "Text",
              "__tag": 4046,
              "value": " variable to set marks for all tests in a file."
            }
          ]
        },
        {
          "__type": "Paragraph",
          "__tag": 4045,
          "children": [
            {
              "__type": "Strong",
              "__tag": 4048,
              "children": [
                {
                  "__type": "Text",
                  "__tag": 4046,
                  "value": "Strict checks:"
                }
              ]
            }
          ]
        },
        {
          "__type": "Paragraph",
          "__tag": 4045,
          "children": [
            {
              "__type": "Text",
              "__tag": 4046,
              "value": "The "
            },
            {
              "__type": "InlineCode",
              "__tag": 4051,
              "value": "xp"
            },
            {
              "__type": "Text",
              "__tag": 4046,
              "value": " fixture should almost always be used along with "
            },
            {
              "__type": "InlineCode",
              "__tag": 4051,
              "value": "make_xp_test_case"
            },
            {
              "__type": "Text",
              "__tag": 4046,
              "value": " or one of the similar functions listed above and the "
            },
            {
              "__type": "InlineCode",
              "__tag": 4051,
              "value": "xp"
            },
            {
              "__type": "Text",
              "__tag": 4046,
              "value": " fixture has strict checks to enforce this. If one had accidentally written    "
            }
          ]
        },
        {
          "__type": "Code",
          "__tag": 4050,
          "value": "@pytest.mark.parametrize(\n    \"func\", tested_funcs\n)\ndef test_foo(func, xp):\n    ...",
          "execution_status": null,
          "out": ""
        },
        {
          "__type": "Paragraph",
          "__tag": 4045,
          "children": [
            {
              "__type": "Text",
              "__tag": 4046,
              "value": "without using "
            },
            {
              "__type": "InlineCode",
              "__tag": 4051,
              "value": "make_xp_pytest_param"
            },
            {
              "__type": "Text",
              "__tag": 4046,
              "value": " then running this test would result in an error with the the message    "
            }
          ]
        },
        {
          "__type": "Code",
          "__tag": 4050,
          "value": "ERROR scipy/my_module/tests/test_foo.py::test_foo[numpy] - UserWarning: test uses `xp`\nfixture without drawing from `xp_capabilities`  but is not explicitly marked with ``pytest.mark...",
          "execution_status": null,
          "out": ""
        },
        {
          "__type": "Paragraph",
          "__tag": 4045,
          "children": [
            {
              "__type": "Text",
              "__tag": 4046,
              "value": "Since "
            },
            {
              "__type": "InlineCode",
              "__tag": 4051,
              "value": "xp_capabilities"
            },
            {
              "__type": "Text",
              "__tag": 4046,
              "value": " is used to declare alternative backend support for the purpose of both testing and documentation, this strict check in the "
            },
            {
              "__type": "InlineCode",
              "__tag": 4051,
              "value": "xp"
            },
            {
              "__type": "Text",
              "__tag": 4046,
              "value": " fixture ensures that documentation of tested array API capabilities does not become out-of-date. There may be cases where one intentionally does cannot or does not want to use "
            },
            {
              "__type": "InlineCode",
              "__tag": 4051,
              "value": "make_xp_test_case"
            },
            {
              "__type": "Text",
              "__tag": 4046,
              "value": " or an equivalent, such as for private functions which do not have associated "
            },
            {
              "__type": "InlineCode",
              "__tag": 4051,
              "value": "xp_capabilities"
            },
            {
              "__type": "Text",
              "__tag": 4046,
              "value": " entries. To bypass the strict checks, one can explicitly mark a test with "
            },
            {
              "__type": "InlineCode",
              "__tag": 4051,
              "value": "@pytest.mark.uses_xp_capabilities(False)"
            },
            {
              "__type": "Text",
              "__tag": 4046,
              "value": ". An optional "
            },
            {
              "__type": "InlineCode",
              "__tag": 4051,
              "value": "reason"
            },
            {
              "__type": "Text",
              "__tag": 4046,
              "value": " string can be passed to this mark. Tests of private functionality for which there are no relevant "
            },
            {
              "__type": "InlineCode",
              "__tag": 4051,
              "value": "xp_capabilities"
            },
            {
              "__type": "Text",
              "__tag": 4046,
              "value": " entries, one should use "
            },
            {
              "__type": "InlineCode",
              "__tag": 4051,
              "value": "reason=\"private\""
            },
            {
              "__type": "Text",
              "__tag": 4046,
              "value": ".    "
            }
          ]
        },
        {
          "__type": "Code",
          "__tag": 4050,
          "value": "pytest.mark.uses_xp_capabilities(False, reason=\"private\")\ndef test_private_toto_helper(xp):\n    ...",
          "execution_status": null,
          "out": ""
        }
      ],
      "title": [
        {
          "__type": "Text",
          "__tag": 4046,
          "value": "Adding tests"
        }
      ],
      "level": 1,
      "target": "dev-arrayapi_adding_tests"
    },
    {
      "__type": "Section",
      "__tag": 4015,
      "children": [
        {
          "__type": "Paragraph",
          "__tag": 4045,
          "children": [
            {
              "__type": "Text",
              "__tag": 4046,
              "value": "Note that tests for MArray support are not added automatically by any of the mechanisms above; support must be tested manually. A common pattern is property-based testing: rather than testing the output of a function with particular MArray input against manually calculated reference values, compare the output of the function with randomly-generated MArray input against the output of a reference implementation, such as the same function with "
            },
            {
              "__type": "InlineCode",
              "__tag": 4051,
              "value": "nan_policy='omit'"
            },
            {
              "__type": "Text",
              "__tag": 4046,
              "value": " or with the masked data removed programmatically. For reducing functions, it is also important to test the behavior of the function when some slices have insufficient (e.g. zero or one) element remaining after masked elements have been removed. Typically, MArray tests use the "
            },
            {
              "__type": "InlineCode",
              "__tag": 4051,
              "value": "xp"
            },
            {
              "__type": "Text",
              "__tag": 4046,
              "value": " fixture to test MArray used "
            },
            {
              "__type": "Emphasis",
              "__tag": 4047,
              "children": [
                {
                  "__type": "Text",
                  "__tag": 4046,
                  "value": "in conjunction"
                }
              ]
            },
            {
              "__type": "Text",
              "__tag": 4046,
              "value": " with all array backends supported by the function being tested. See "
            },
            {
              "__type": "InlineCode",
              "__tag": 4051,
              "value": "scipy/stats/tests/test_marray.py"
            },
            {
              "__type": "Text",
              "__tag": 4046,
              "value": " for examples."
            }
          ]
        }
      ],
      "title": [
        {
          "__type": "Text",
          "__tag": 4046,
          "value": "MArray testing"
        }
      ],
      "level": 2,
      "target": null
    },
    {
      "__type": "Section",
      "__tag": 4015,
      "children": [
        {
          "__type": "Paragraph",
          "__tag": 4045,
          "children": [
            {
              "__type": "Text",
              "__tag": 4046,
              "value": "Though most of the time it's sufficient to use "
            },
            {
              "__type": "InlineCode",
              "__tag": 4051,
              "value": "make_xp_test_case"
            },
            {
              "__type": "Text",
              "__tag": 4046,
              "value": " and "
            },
            {
              "__type": "InlineCode",
              "__tag": 4051,
              "value": "make_xp_pytest_param"
            },
            {
              "__type": "Text",
              "__tag": 4046,
              "value": ", the following "
            },
            {
              "__type": "InlineCode",
              "__tag": 4051,
              "value": "pytest"
            },
            {
              "__type": "Text",
              "__tag": 4046,
              "value": " markers are available and can be added directly to tests. ("
            },
            {
              "__type": "InlineCode",
              "__tag": 4051,
              "value": "make_xp_test_case"
            },
            {
              "__type": "Text",
              "__tag": 4046,
              "value": " and its equivalents provide a declarative means of adding "
            },
            {
              "__type": "InlineCode",
              "__tag": 4051,
              "value": "skip_xp_backends"
            },
            {
              "__type": "Text",
              "__tag": 4046,
              "value": " and "
            },
            {
              "__type": "InlineCode",
              "__tag": 4051,
              "value": "xfail_xp_backends"
            },
            {
              "__type": "Text",
              "__tag": 4046,
              "value": " markers)."
            }
          ]
        },
        {
          "__type": "BulletList",
          "__tag": 4053,
          "ordered": false,
          "start": 1,
          "children": [
            {
              "__type": "ListItem",
              "__tag": 4054,
              "children": [
                {
                  "__type": "Paragraph",
                  "__tag": 4045,
                  "children": [
                    {
                      "__type": "InlineCode",
                      "__tag": 4051,
                      "value": "skip_xp_backends(backend=None, reason=None, np_only=False, cpu_only=False, eager_only=False, exceptions=None)"
                    },
                    {
                      "__type": "Text",
                      "__tag": 4046,
                      "value": ":   skip certain backends or categories of backends.   See docstring of "
                    },
                    {
                      "__type": "InlineCode",
                      "__tag": 4051,
                      "value": "scipy.conftest.skip_or_xfail_xp_backends"
                    },
                    {
                      "__type": "Text",
                      "__tag": 4046,
                      "value": " for information on how   to use this marker to skip tests."
                    }
                  ]
                }
              ]
            },
            {
              "__type": "ListItem",
              "__tag": 4054,
              "children": [
                {
                  "__type": "Paragraph",
                  "__tag": 4045,
                  "children": [
                    {
                      "__type": "InlineCode",
                      "__tag": 4051,
                      "value": "xfail_xp_backends(backend=None, reason=None, np_only=False, cpu_only=False, eager_only=False, exceptions=None)"
                    },
                    {
                      "__type": "Text",
                      "__tag": 4046,
                      "value": ":   xfail certain backends or categories of backends.   See docstring of "
                    },
                    {
                      "__type": "InlineCode",
                      "__tag": 4051,
                      "value": "scipy.conftest.skip_or_xfail_xp_backends"
                    },
                    {
                      "__type": "Text",
                      "__tag": 4046,
                      "value": " for information on how   to use this marker to xfail tests."
                    }
                  ]
                }
              ]
            },
            {
              "__type": "ListItem",
              "__tag": 4054,
              "children": [
                {
                  "__type": "Paragraph",
                  "__tag": 4045,
                  "children": [
                    {
                      "__type": "InlineCode",
                      "__tag": 4051,
                      "value": "skip_xp_invalid_arg"
                    },
                    {
                      "__type": "Text",
                      "__tag": 4046,
                      "value": " is used to skip tests that use arguments which   are invalid when "
                    },
                    {
                      "__type": "InlineCode",
                      "__tag": 4051,
                      "value": "SCIPY_ARRAY_API"
                    },
                    {
                      "__type": "Text",
                      "__tag": 4046,
                      "value": " is enabled. For instance, some tests of   "
                    },
                    {
                      "__type": "CrossRef",
                      "__tag": 4002,
                      "value": "scipy.stats",
                      "reference": {
                        "__type": "LocalRef",
                        "__tag": 4022,
                        "kind": "module",
                        "path": "scipy.stats"
                      },
                      "kind": "module"
                    },
                    {
                      "__type": "Text",
                      "__tag": 4046,
                      "value": " functions pass masked arrays to the function being tested, but   masked arrays are incompatible with the array API. Use of the   "
                    },
                    {
                      "__type": "InlineCode",
                      "__tag": 4051,
                      "value": "skip_xp_invalid_arg"
                    },
                    {
                      "__type": "Text",
                      "__tag": 4046,
                      "value": " decorator allows these tests to protect against   regressions when "
                    },
                    {
                      "__type": "InlineCode",
                      "__tag": 4051,
                      "value": "SCIPY_ARRAY_API"
                    },
                    {
                      "__type": "Text",
                      "__tag": 4046,
                      "value": " is not used without resulting in failures   when "
                    },
                    {
                      "__type": "InlineCode",
                      "__tag": 4051,
                      "value": "SCIPY_ARRAY_API"
                    },
                    {
                      "__type": "Text",
                      "__tag": 4046,
                      "value": " is used. In time, we will want these functions to emit   deprecation warnings when they receive array API invalid input, and this   decorator will check that the deprecation warning is emitted without it   causing the test to fail. When "
                    },
                    {
                      "__type": "InlineCode",
                      "__tag": 4051,
                      "value": "SCIPY_ARRAY_API=1"
                    },
                    {
                      "__type": "Text",
                      "__tag": 4046,
                      "value": " behavior becomes the   default and only behavior, these tests (and the decorator itself) will be   removed."
                    }
                  ]
                }
              ]
            },
            {
              "__type": "ListItem",
              "__tag": 4054,
              "children": [
                {
                  "__type": "Paragraph",
                  "__tag": 4045,
                  "children": [
                    {
                      "__type": "InlineCode",
                      "__tag": 4051,
                      "value": "array_api_backends"
                    },
                    {
                      "__type": "Text",
                      "__tag": 4046,
                      "value": ": this marker is automatically added by the "
                    },
                    {
                      "__type": "InlineCode",
                      "__tag": 4051,
                      "value": "xp"
                    },
                    {
                      "__type": "Text",
                      "__tag": 4046,
                      "value": " fixture to   all tests that use it. This is useful e.g. to select all and only such tests      "
                    }
                  ]
                },
                {
                  "__type": "Code",
                  "__tag": 4050,
                  "value": "spin test -b all -m array_api_backends",
                  "execution_status": null,
                  "out": ""
                }
              ]
            },
            {
              "__type": "ListItem",
              "__tag": 4054,
              "children": [
                {
                  "__type": "Paragraph",
                  "__tag": 4045,
                  "children": [
                    {
                      "__type": "InlineCode",
                      "__tag": 4051,
                      "value": "uses_xp_capabilities(status, funcs=None, reason=None)"
                    },
                    {
                      "__type": "Text",
                      "__tag": 4046,
                      "value": ": discussed above.   "
                    },
                    {
                      "__type": "InlineCode",
                      "__tag": 4051,
                      "value": "make_xp_test_case"
                    },
                    {
                      "__type": "Text",
                      "__tag": 4046,
                      "value": " and its equivalents apply the marker   "
                    },
                    {
                      "__type": "InlineCode",
                      "__tag": 4051,
                      "value": "uses_xp_capabilities(True)"
                    },
                    {
                      "__type": "Text",
                      "__tag": 4046,
                      "value": " and direct use of "
                    },
                    {
                      "__type": "InlineCode",
                      "__tag": 4051,
                      "value": "uses_xp_capabilities(False)"
                    },
                    {
                      "__type": "Text",
                      "__tag": 4046,
                      "value": "   can be used to declare a test intentionally does not use "
                    },
                    {
                      "__type": "InlineCode",
                      "__tag": 4051,
                      "value": "make_xp_test_case"
                    },
                    {
                      "__type": "Text",
                      "__tag": 4046,
                      "value": "   or one of its equivalents."
                    }
                  ]
                }
              ]
            }
          ]
        }
      ],
      "title": [
        {
          "__type": "Text",
          "__tag": 4046,
          "value": "Directly adding pytest markers"
        }
      ],
      "level": 2,
      "target": null
    },
    {
      "__type": "Section",
      "__tag": 4015,
      "children": [
        {
          "__type": "Paragraph",
          "__tag": 4045,
          "children": [
            {
              "__type": "Text",
              "__tag": 4046,
              "value": "For a public function "
            },
            {
              "__type": "InlineCode",
              "__tag": 4051,
              "value": "f"
            },
            {
              "__type": "Text",
              "__tag": 4046,
              "value": ", "
            },
            {
              "__type": "InlineCode",
              "__tag": 4051,
              "value": "skip_xp_backends"
            },
            {
              "__type": "Text",
              "__tag": 4046,
              "value": " and "
            },
            {
              "__type": "InlineCode",
              "__tag": 4051,
              "value": "xfail_xp_backends"
            },
            {
              "__type": "Text",
              "__tag": 4046,
              "value": " should only be used directly for backend related skips and xfails which are needed for the specific test but which do not reflect the general capabilities of "
            },
            {
              "__type": "InlineCode",
              "__tag": 4051,
              "value": "f"
            },
            {
              "__type": "Text",
              "__tag": 4046,
              "value": ". Reasons to directly use "
            },
            {
              "__type": "InlineCode",
              "__tag": 4051,
              "value": "skip_xp_backends"
            },
            {
              "__type": "Text",
              "__tag": 4046,
              "value": " include when:"
            }
          ]
        },
        {
          "__type": "BulletList",
          "__tag": 4053,
          "ordered": true,
          "start": 1,
          "children": [
            {
              "__type": "ListItem",
              "__tag": 4054,
              "children": [
                {
                  "__type": "Paragraph",
                  "__tag": 4045,
                  "children": [
                    {
                      "__type": "Text",
                      "__tag": 4046,
                      "value": "the test body itself contains unsupported functionality (though one should    try to avoid this whenever possible, see the subsection on testing    practice below)."
                    }
                  ]
                }
              ]
            },
            {
              "__type": "ListItem",
              "__tag": 4054,
              "children": [
                {
                  "__type": "Paragraph",
                  "__tag": 4045,
                  "children": [
                    {
                      "__type": "InlineCode",
                      "__tag": 4051,
                      "value": "f"
                    },
                    {
                      "__type": "Text",
                      "__tag": 4046,
                      "value": " is only partially supported on a backend and the test relies on    cases which are not supported, e.g. tests involving complex values for    functions which only support real values on a given backend, tests involving    higher dimensional arrays for functions which only support arrays of size 2d    or less on a given backend."
                    }
                  ]
                }
              ]
            },
            {
              "__type": "ListItem",
              "__tag": 4054,
              "children": [
                {
                  "__type": "Paragraph",
                  "__tag": 4045,
                  "children": [
                    {
                      "__type": "Text",
                      "__tag": 4046,
                      "value": "the test exposes a bug in "
                    },
                    {
                      "__type": "InlineCode",
                      "__tag": 4051,
                      "value": "f"
                    },
                    {
                      "__type": "Text",
                      "__tag": 4046,
                      "value": " on a given backend which crashes test    execution."
                    }
                  ]
                }
              ]
            }
          ]
        },
        {
          "__type": "Paragraph",
          "__tag": 4045,
          "children": [
            {
              "__type": "Text",
              "__tag": 4046,
              "value": "For tests exposing bugs on alternative backends that do not crash test execution, such as bugs that lead to numerical errors, it is preferable to use "
            },
            {
              "__type": "InlineCode",
              "__tag": 4051,
              "value": "xfail_xp_backends"
            },
            {
              "__type": "Text",
              "__tag": 4046,
              "value": " so we can be notified with an "
            },
            {
              "__type": "InlineCode",
              "__tag": 4051,
              "value": "XPASS"
            },
            {
              "__type": "Text",
              "__tag": 4046,
              "value": " when the upstream bug is fixed."
            }
          ]
        },
        {
          "__type": "Paragraph",
          "__tag": 4045,
          "children": [
            {
              "__type": "InlineCode",
              "__tag": 4051,
              "value": "xfail_xp_backends"
            },
            {
              "__type": "Text",
              "__tag": 4046,
              "value": " should not be used for test failures for an alternative backend which are actually unrelated to "
            },
            {
              "__type": "InlineCode",
              "__tag": 4051,
              "value": "f"
            },
            {
              "__type": "Text",
              "__tag": 4046,
              "value": " but are instead due to bugs outside "
            },
            {
              "__type": "InlineCode",
              "__tag": 4051,
              "value": "f"
            },
            {
              "__type": "Text",
              "__tag": 4046,
              "value": " exposed by other parts of the test body. To avoid such situations, we recommend as a general practice to attempt to isolate use of the alternative backend only to the function "
            },
            {
              "__type": "InlineCode",
              "__tag": 4051,
              "value": "f"
            },
            {
              "__type": "Text",
              "__tag": 4046,
              "value": " being tested with a caveat that there are situations where or it is necessary or desired to do otherwise: see the section on "
            },
            {
              "__type": "CrossRef",
              "__tag": 4002,
              "value": "backend isolation",
              "reference": {
                "__type": "LocalRef",
                "__tag": 4022,
                "kind": "docs",
                "path": "dev:api-dev:array_api"
              },
              "kind": "exists"
            },
            {
              "__type": "Text",
              "__tag": 4046,
              "value": " below for more information."
            }
          ]
        },
        {
          "__type": "Paragraph",
          "__tag": 4045,
          "children": [
            {
              "__type": "Text",
              "__tag": 4046,
              "value": "Tests which are inherently NumPy only should avoid the "
            },
            {
              "__type": "InlineCode",
              "__tag": 4051,
              "value": "xp"
            },
            {
              "__type": "Text",
              "__tag": 4046,
              "value": " fixture altogether rather than using it with an "
            },
            {
              "__type": "InlineCode",
              "__tag": 4051,
              "value": "np_only=True"
            },
            {
              "__type": "Text",
              "__tag": 4046,
              "value": " skip marker."
            }
          ]
        },
        {
          "__type": "Paragraph",
          "__tag": 4045,
          "children": [
            {
              "__type": "Text",
              "__tag": 4046,
              "value": "Note that, in one case, "
            },
            {
              "__type": "InlineCode",
              "__tag": 4051,
              "value": "xp_capabilities"
            },
            {
              "__type": "Text",
              "__tag": 4046,
              "value": " offers more granularity than "
            },
            {
              "__type": "InlineCode",
              "__tag": 4051,
              "value": "skip_xp_backends"
            },
            {
              "__type": "Text",
              "__tag": 4046,
              "value": " and "
            },
            {
              "__type": "InlineCode",
              "__tag": 4051,
              "value": "xfail_xp_backends"
            },
            {
              "__type": "Text",
              "__tag": 4046,
              "value": ". "
            },
            {
              "__type": "InlineCode",
              "__tag": 4051,
              "value": "xp_capabilities"
            },
            {
              "__type": "Text",
              "__tag": 4046,
              "value": " allows developers to separately declare support for the JAX JIT and support for lazy computation with Dask with the respective "
            },
            {
              "__type": "InlineCode",
              "__tag": 4051,
              "value": "jax_jit"
            },
            {
              "__type": "Text",
              "__tag": 4046,
              "value": " and "
            },
            {
              "__type": "InlineCode",
              "__tag": 4051,
              "value": "allow_dask_compute"
            },
            {
              "__type": "Text",
              "__tag": 4046,
              "value": " kwargs.  "
            },
            {
              "__type": "InlineCode",
              "__tag": 4051,
              "value": "skip_xp_backends"
            },
            {
              "__type": "Text",
              "__tag": 4046,
              "value": " ("
            },
            {
              "__type": "InlineCode",
              "__tag": 4051,
              "value": "xfail_xp_backends"
            },
            {
              "__type": "Text",
              "__tag": 4046,
              "value": ") offers only an "
            },
            {
              "__type": "InlineCode",
              "__tag": 4051,
              "value": "eager_only"
            },
            {
              "__type": "Text",
              "__tag": 4046,
              "value": " kwarg which can only add skips (xfails) for both the JAX JIT and lazy Dask together. The current state is that one cannot add test specific skips (xfails) for the JAX JIT without also adding them for lazy Dask and vice versa. This is a known limitation that arose through the historical process through which "
            },
            {
              "__type": "InlineCode",
              "__tag": 4051,
              "value": "xp_capabilities"
            },
            {
              "__type": "Text",
              "__tag": 4046,
              "value": ", "
            },
            {
              "__type": "InlineCode",
              "__tag": 4051,
              "value": "skip_xp_backends"
            },
            {
              "__type": "Text",
              "__tag": 4046,
              "value": ", and "
            },
            {
              "__type": "InlineCode",
              "__tag": 4051,
              "value": "xfail_xp_backends"
            },
            {
              "__type": "Text",
              "__tag": 4046,
              "value": " were developed, and it may be addressed in the future if there is sufficient developer need."
            }
          ]
        }
      ],
      "title": [
        {
          "__type": "Text",
          "__tag": 4046,
          "value": "Test specific skips and xfails"
        }
      ],
      "level": 2,
      "target": null
    },
    {
      "__type": "Section",
      "__tag": 4015,
      "children": [
        {
          "__type": "Paragraph",
          "__tag": 4045,
          "children": [
            {
              "__type": "InlineCode",
              "__tag": 4051,
              "value": "scipy._lib._array_api"
            },
            {
              "__type": "Text",
              "__tag": 4046,
              "value": " contains array-agnostic assertions such as "
            },
            {
              "__type": "InlineCode",
              "__tag": 4051,
              "value": "xp_assert_close"
            },
            {
              "__type": "Text",
              "__tag": 4046,
              "value": " which can be used to replace assertions from "
            },
            {
              "__type": "CrossRef",
              "__tag": 4002,
              "value": "numpy.testing",
              "reference": {
                "__type": "RefInfo",
                "__tag": 4000,
                "module": "numpy",
                "version": "?",
                "kind": "module",
                "path": "numpy.testing"
              },
              "kind": "module"
            },
            {
              "__type": "Text",
              "__tag": 4046,
              "value": "."
            }
          ]
        },
        {
          "__type": "Paragraph",
          "__tag": 4045,
          "children": [
            {
              "__type": "Text",
              "__tag": 4046,
              "value": "When these assertions are executed within a test that uses the "
            },
            {
              "__type": "InlineCode",
              "__tag": 4051,
              "value": "xp"
            },
            {
              "__type": "Text",
              "__tag": 4046,
              "value": " fixture, they enforce that the namespaces of both the actual and desired arrays match the namespace which was set by the fixture. Tests without the "
            },
            {
              "__type": "InlineCode",
              "__tag": 4051,
              "value": "xp"
            },
            {
              "__type": "Text",
              "__tag": 4046,
              "value": " fixture infer the namespace from the desired array. This machinery can be overridden by explicitly passing the "
            },
            {
              "__type": "InlineCode",
              "__tag": 4051,
              "value": "xp="
            },
            {
              "__type": "Text",
              "__tag": 4046,
              "value": " parameter to the assertion functions."
            }
          ]
        }
      ],
      "title": [
        {
          "__type": "Text",
          "__tag": 4046,
          "value": "Array-agnostic assertions"
        }
      ],
      "level": 2,
      "target": null
    },
    {
      "__type": "Section",
      "__tag": 4015,
      "children": [
        {
          "__type": "Paragraph",
          "__tag": 4045,
          "children": [
            {
              "__type": "Text",
              "__tag": 4046,
              "value": "The following examples demonstrate how to use direct markers together with "
            },
            {
              "__type": "InlineCode",
              "__tag": 4051,
              "value": "make_xp_test_case"
            },
            {
              "__type": "Text",
              "__tag": 4046,
              "value": "    "
            }
          ]
        },
        {
          "__type": "Code",
          "__tag": 4050,
          "value": "  from scipy.conftest import skip_xp_invalid_arg\n  from scipy._lib._array_api import xp_assert_close, make_xp_test_case\n\n  @make_xp_test_case(toto)\n  class TestToto:\n      def test_toto_list_input(self):\n      # This test is inherently NumPy only so avoids the xp fixture altogether.\n          a = [1., 2., 3.]\n          b = [0., 2., 5.]\n          xp_assert_close(toto(a, b), np.array(a))\n  ...\n      @pytest.mark.skip_xp_backends(\n          'cupy',\n\t  reason='cupy does not support inputs with ndim>2'\n      )\n      def test_toto2(self, xp):\n          ...\n  ...\n      # Do not run when SCIPY_ARRAY_API=1 is used since calling toto on masked\n      # arrays will raise in this case.\n      @skip_xp_invalid_arg\n      def test_toto_masked_array(self):\n          ...",
          "execution_status": null,
          "out": ""
        }
      ],
      "title": [
        {
          "__type": "Text",
          "__tag": 4046,
          "value": "Examples"
        }
      ],
      "level": 2,
      "target": null
    },
    {
      "__type": "Section",
      "__tag": 4015,
      "children": [
        {
          "__type": "Paragraph",
          "__tag": 4045,
          "children": [
            {
              "__type": "Text",
              "__tag": 4046,
              "value": "After applying these markers, either through "
            },
            {
              "__type": "InlineCode",
              "__tag": 4051,
              "value": "make_xp_test_case"
            },
            {
              "__type": "Text",
              "__tag": 4046,
              "value": " or one of its equvilents, or directly, "
            },
            {
              "__type": "InlineCode",
              "__tag": 4051,
              "value": "spin test"
            },
            {
              "__type": "Text",
              "__tag": 4046,
              "value": " can be used with the option "
            },
            {
              "__type": "InlineCode",
              "__tag": 4051,
              "value": "-b"
            },
            {
              "__type": "Text",
              "__tag": 4046,
              "value": " or "
            },
            {
              "__type": "InlineCode",
              "__tag": 4051,
              "value": "--array-api-backend"
            },
            {
              "__type": "Text",
              "__tag": 4046,
              "value": "    "
            }
          ]
        },
        {
          "__type": "Code",
          "__tag": 4050,
          "value": "spin test -b numpy -b torch -s cluster",
          "execution_status": null,
          "out": ""
        },
        {
          "__type": "Paragraph",
          "__tag": 4045,
          "children": [
            {
              "__type": "Text",
              "__tag": 4046,
              "value": "This automatically sets "
            },
            {
              "__type": "InlineCode",
              "__tag": 4051,
              "value": "SCIPY_ARRAY_API"
            },
            {
              "__type": "Text",
              "__tag": 4046,
              "value": " appropriately and will cause tests with the "
            },
            {
              "__type": "InlineCode",
              "__tag": 4051,
              "value": "xp"
            },
            {
              "__type": "Text",
              "__tag": 4046,
              "value": " fixture to run only for the selected backends to be collected. Valid backends are "
            },
            {
              "__type": "InlineCode",
              "__tag": 4051,
              "value": "numpy"
            },
            {
              "__type": "Text",
              "__tag": 4046,
              "value": ", "
            },
            {
              "__type": "InlineCode",
              "__tag": 4051,
              "value": "array_api_strict"
            },
            {
              "__type": "Text",
              "__tag": 4046,
              "value": ", "
            },
            {
              "__type": "InlineCode",
              "__tag": 4051,
              "value": "cupy"
            },
            {
              "__type": "Text",
              "__tag": 4046,
              "value": ", "
            },
            {
              "__type": "InlineCode",
              "__tag": 4051,
              "value": "dask.array"
            },
            {
              "__type": "Text",
              "__tag": 4046,
              "value": ", "
            },
            {
              "__type": "InlineCode",
              "__tag": 4051,
              "value": "jax.numpy"
            },
            {
              "__type": "Text",
              "__tag": 4046,
              "value": ", and "
            },
            {
              "__type": "InlineCode",
              "__tag": 4051,
              "value": "torch"
            },
            {
              "__type": "Text",
              "__tag": 4046,
              "value": ". One may also use the "
            },
            {
              "__type": "InlineCode",
              "__tag": 4051,
              "value": "-m array_api_backends"
            },
            {
              "__type": "Text",
              "__tag": 4046,
              "value": " option to restrict collection to only tests using the "
            },
            {
              "__type": "InlineCode",
              "__tag": 4051,
              "value": "xp"
            },
            {
              "__type": "Text",
              "__tag": 4046,
              "value": " fixture. For instance the following command causes pytest to only collect tests using the "
            },
            {
              "__type": "InlineCode",
              "__tag": 4051,
              "value": "xp"
            },
            {
              "__type": "Text",
              "__tag": 4046,
              "value": " fixture with the CuPy backend    "
            }
          ]
        },
        {
          "__type": "Code",
          "__tag": 4050,
          "value": "spin test -b cupy -m array_api_backends",
          "execution_status": null,
          "out": ""
        },
        {
          "__type": "Paragraph",
          "__tag": 4045,
          "children": [
            {
              "__type": "Text",
              "__tag": 4046,
              "value": "To test a library that has multiple devices with a non-default device, a second environment variable ("
            },
            {
              "__type": "InlineCode",
              "__tag": 4051,
              "value": "SCIPY_DEVICE"
            },
            {
              "__type": "Text",
              "__tag": 4046,
              "value": ", only used in the test suite) can be set. Valid values depend on the array library under test, e.g. for PyTorch, valid values are "
            },
            {
              "__type": "InlineCode",
              "__tag": 4051,
              "value": "\"cpu\", \"cuda\", \"mps\""
            },
            {
              "__type": "Text",
              "__tag": 4046,
              "value": ". To run the test suite with the PyTorch MPS backend, use: "
            },
            {
              "__type": "InlineCode",
              "__tag": 4051,
              "value": "SCIPY_DEVICE=mps spin test -b torch"
            },
            {
              "__type": "Text",
              "__tag": 4046,
              "value": "."
            }
          ]
        },
        {
          "__type": "Paragraph",
          "__tag": 4045,
          "children": [
            {
              "__type": "Text",
              "__tag": 4046,
              "value": "Note that in SciPy's GitHub Actions workflows, there are regular tests with array-api-strict, Dask, PyTorch, and JAX on CPU, and tests with CuPy, PyTorch, and JAX on GPU."
            }
          ]
        },
        {
          "__type": "Paragraph",
          "__tag": 4045,
          "children": [
            {
              "__type": "Text",
              "__tag": 4046,
              "value": "A third environment variable ("
            },
            {
              "__type": "InlineCode",
              "__tag": 4051,
              "value": "SCIPY_DEFAULT_DTYPE"
            },
            {
              "__type": "Text",
              "__tag": 4046,
              "value": ", again only used in the test suite) can be used to control the "
            },
            {
              "__type": "CrossRef",
              "__tag": 4002,
              "value": "default dtype",
              "reference": {
                "__type": "LocalRef",
                "__tag": 4022,
                "kind": "docs",
                "path": "dev:api-dev:array_api"
              },
              "kind": "exists"
            },
            {
              "__type": "Text",
              "__tag": 4046,
              "value": " used by "
            },
            {
              "__type": "InlineCode",
              "__tag": 4051,
              "value": "torch"
            },
            {
              "__type": "Text",
              "__tag": 4046,
              "value": " in tests. Valid values are "
            },
            {
              "__type": "InlineCode",
              "__tag": 4051,
              "value": "\"float64\""
            },
            {
              "__type": "Text",
              "__tag": 4046,
              "value": " and "
            },
            {
              "__type": "InlineCode",
              "__tag": 4051,
              "value": "\"float32\""
            },
            {
              "__type": "Text",
              "__tag": 4046,
              "value": ". If "
            },
            {
              "__type": "InlineCode",
              "__tag": 4051,
              "value": "SCIPY_DEFAULT_DTYPE"
            },
            {
              "__type": "Text",
              "__tag": 4046,
              "value": " is unset, then "
            },
            {
              "__type": "InlineCode",
              "__tag": 4051,
              "value": "torch"
            },
            {
              "__type": "Text",
              "__tag": 4046,
              "value": "'s default dtype will be "
            },
            {
              "__type": "InlineCode",
              "__tag": 4051,
              "value": "float64"
            },
            {
              "__type": "Text",
              "__tag": 4046,
              "value": "."
            }
          ]
        },
        {
          "__type": "Paragraph",
          "__tag": 4045,
          "children": [
            {
              "__type": "Text",
              "__tag": 4046,
              "value": "The intention behind testing with different default dtypes is primarily to catch subtle bugs that can arise with the "
            },
            {
              "__type": "InlineCode",
              "__tag": 4051,
              "value": "torch"
            },
            {
              "__type": "Text",
              "__tag": 4046,
              "value": " backend due to internal array creation that does not explicitly specify a dtype. The intention is not to implicitly test that functions are numerically accurate with both "
            },
            {
              "__type": "InlineCode",
              "__tag": 4051,
              "value": "float32"
            },
            {
              "__type": "Text",
              "__tag": 4046,
              "value": " and "
            },
            {
              "__type": "InlineCode",
              "__tag": 4051,
              "value": "float64"
            },
            {
              "__type": "Text",
              "__tag": 4046,
              "value": " inputs, or that input dtype controls output dtype. These tasks, if done, should instead be accomplished mindfully by explicitly setting dtypes within tests. For the sake of consistency, tests intended to be run with the "
            },
            {
              "__type": "InlineCode",
              "__tag": 4051,
              "value": "torch"
            },
            {
              "__type": "Text",
              "__tag": 4046,
              "value": " backend should not use array creation functions without explicitly setting the dtype. At the time of writing, there are many tests in the test suite which do not follow this practice, and this could be a good source of first issues for new contributors."
            }
          ]
        }
      ],
      "title": [
        {
          "__type": "Text",
          "__tag": 4046,
          "value": "Running tests"
        }
      ],
      "level": 2,
      "target": null
    },
    {
      "__type": "Section",
      "__tag": 4015,
      "children": [
        {
          "__type": "Paragraph",
          "__tag": 4045,
          "children": [
            {
              "__type": "Text",
              "__tag": 4046,
              "value": "It is not permitted for a function "
            },
            {
              "__type": "InlineCode",
              "__tag": 4051,
              "value": "f"
            },
            {
              "__type": "Text",
              "__tag": 4046,
              "value": " to have an "
            },
            {
              "__type": "InlineCode",
              "__tag": 4051,
              "value": "xp_capabilities"
            },
            {
              "__type": "Text",
              "__tag": 4046,
              "value": " entry advertising some level of alternative backend support if there are no tests in the test suite for "
            },
            {
              "__type": "InlineCode",
              "__tag": 4051,
              "value": "f"
            },
            {
              "__type": "Text",
              "__tag": 4046,
              "value": " that use the "
            },
            {
              "__type": "InlineCode",
              "__tag": 4051,
              "value": "xp"
            },
            {
              "__type": "Text",
              "__tag": 4046,
              "value": " fixture along with "
            },
            {
              "__type": "InlineCode",
              "__tag": 4051,
              "value": "make_xp_test_case(f)"
            },
            {
              "__type": "Text",
              "__tag": 4046,
              "value": " or one of its equivalents. All documented alternative backend support must be tested and the only valid "
            },
            {
              "__type": "InlineCode",
              "__tag": 4051,
              "value": "xp_capabilities"
            },
            {
              "__type": "Text",
              "__tag": 4046,
              "value": " entries for a function without tests of the form described above are "
            },
            {
              "__type": "InlineCode",
              "__tag": 4051,
              "value": "np_only=True"
            },
            {
              "__type": "Text",
              "__tag": 4046,
              "value": " with no "
            },
            {
              "__type": "InlineCode",
              "__tag": 4051,
              "value": "exceptions"
            },
            {
              "__type": "Text",
              "__tag": 4046,
              "value": ", or "
            },
            {
              "__type": "InlineCode",
              "__tag": 4051,
              "value": "out_of_scope=True"
            },
            {
              "__type": "Text",
              "__tag": 4046,
              "value": "."
            }
          ]
        },
        {
          "__type": "Paragraph",
          "__tag": 4045,
          "children": [
            {
              "__type": "Text",
              "__tag": 4046,
              "value": "The command    "
            }
          ]
        },
        {
          "__type": "Code",
          "__tag": 4050,
          "value": "spin check --xp-markers",
          "execution_status": null,
          "out": ""
        },
        {
          "__type": "Paragraph",
          "__tag": 4045,
          "children": [
            {
              "__type": "Text",
              "__tag": 4046,
              "value": "can be used to check that all functions advertising alternative backend support have at least one test using the "
            },
            {
              "__type": "InlineCode",
              "__tag": 4051,
              "value": "xp"
            },
            {
              "__type": "Text",
              "__tag": 4046,
              "value": " fixture that draws from "
            },
            {
              "__type": "InlineCode",
              "__tag": 4051,
              "value": "xp_capabilities"
            },
            {
              "__type": "Text",
              "__tag": 4046,
              "value": " through "
            },
            {
              "__type": "InlineCode",
              "__tag": 4051,
              "value": "make_xp_test_case"
            },
            {
              "__type": "Text",
              "__tag": 4046,
              "value": " or one of its equivalents. This check is run in CI."
            }
          ]
        }
      ],
      "title": [
        {
          "__type": "Text",
          "__tag": 4046,
          "value": "Tests are not optional"
        }
      ],
      "level": 2,
      "target": null
    },
    {
      "__type": "Section",
      "__tag": 4015,
      "children": [
        {
          "__type": "Paragraph",
          "__tag": 4045,
          "children": [
            {
              "__type": "Text",
              "__tag": 4046,
              "value": "In most cases, it's important that for any supported function "
            },
            {
              "__type": "InlineCode",
              "__tag": 4051,
              "value": "f"
            },
            {
              "__type": "Text",
              "__tag": 4046,
              "value": ", there exist tests using the "
            },
            {
              "__type": "InlineCode",
              "__tag": 4051,
              "value": "xp"
            },
            {
              "__type": "Text",
              "__tag": 4046,
              "value": " fixture that restrict use of alternative backends to only the function "
            },
            {
              "__type": "InlineCode",
              "__tag": 4051,
              "value": "f"
            },
            {
              "__type": "Text",
              "__tag": 4046,
              "value": " being tested. Other functions evaluated within a test, for the purpose of producing reference values, inputs, round-trip calculations, etc. should instead use the NumPy backend. This helps ensure that any failures that occur on a backend actually relate to the function of interest, and avoids the need to skip backends due to lack of support for functions other than "
            },
            {
              "__type": "InlineCode",
              "__tag": 4051,
              "value": "f"
            },
            {
              "__type": "Text",
              "__tag": 4046,
              "value": ". Property based integration tests which check that some invariant holds using the same alternative backend across different functions can also have value, giving a window into the general health of backend support for a module, but in order to ensure the test suite actually reflects the state of backend support for each function, it's usually vital to have tests which isolate use of the alternative backend only to the function being tested."
            }
          ]
        },
        {
          "__type": "Paragraph",
          "__tag": 4045,
          "children": [
            {
              "__type": "Text",
              "__tag": 4046,
              "value": "To help facilitate such backend isolation, there is a function "
            },
            {
              "__type": "InlineCode",
              "__tag": 4051,
              "value": "_xp_copy_to_numpy"
            },
            {
              "__type": "Text",
              "__tag": 4046,
              "value": " in "
            },
            {
              "__type": "InlineCode",
              "__tag": 4051,
              "value": "scipy._lib._array_api"
            },
            {
              "__type": "Text",
              "__tag": 4046,
              "value": " which can copy an arbitrary "
            },
            {
              "__type": "InlineCode",
              "__tag": 4051,
              "value": "xp"
            },
            {
              "__type": "Text",
              "__tag": 4046,
              "value": " array to a NumPy array, bypassing any device transfer guards, while preserving dtypes. It is essential that this function is only used in tests. Attempts to copy a device array to NumPy outside of tests should fail, because otherwise it is opaque as to whether a function is working on GPU or not. Creation of input arrays and reference output arrays, and computations that verify that the output of the function being tested satisfies an invariant (such as round trip tests that a function composed with its inverse gives the identity function), should all be done with NumPy (using the "
            },
            {
              "__type": "InlineCode",
              "__tag": 4051,
              "value": "_xp_copy_to_numpy"
            },
            {
              "__type": "Text",
              "__tag": 4046,
              "value": " function if necessary)."
            }
          ]
        },
        {
          "__type": "Paragraph",
          "__tag": 4045,
          "children": [
            {
              "__type": "Text",
              "__tag": 4046,
              "value": "Such backend isolation should not be applied blindly. Consider for example a vectorized root finding function like "
            },
            {
              "__type": "CrossRef",
              "__tag": 4002,
              "value": "scipy.optimize.elementwise.find_root",
              "reference": {
                "__type": "RefInfo",
                "__tag": 4000,
                "module": "scipy",
                "version": "?",
                "kind": "module",
                "path": "scipy.optimize._elementwise:find_root"
              },
              "kind": "module"
            },
            {
              "__type": "Text",
              "__tag": 4046,
              "value": ". When testing such a function on alternative backends, isolating use of the alternative backend only to "
            },
            {
              "__type": "InlineCode",
              "__tag": 4051,
              "value": "find_root"
            },
            {
              "__type": "Text",
              "__tag": 4046,
              "value": " by using an input callable "
            },
            {
              "__type": "InlineCode",
              "__tag": 4051,
              "value": "f"
            },
            {
              "__type": "Text",
              "__tag": 4046,
              "value": " (the function for which roots are sought) that converts to and from NumPy would not be desirable since since "
            },
            {
              "__type": "InlineCode",
              "__tag": 4051,
              "value": "find_root"
            },
            {
              "__type": "Text",
              "__tag": 4046,
              "value": " and "
            },
            {
              "__type": "InlineCode",
              "__tag": 4051,
              "value": "f"
            },
            {
              "__type": "Text",
              "__tag": 4046,
              "value": " are so tightly coupled in this case. In other cases, a function "
            },
            {
              "__type": "InlineCode",
              "__tag": 4051,
              "value": "h"
            },
            {
              "__type": "Text",
              "__tag": 4046,
              "value": " used in the tests of a function "
            },
            {
              "__type": "InlineCode",
              "__tag": 4051,
              "value": "g"
            },
            {
              "__type": "Text",
              "__tag": 4046,
              "value": " may be known to be so simple and rock solid that there is no point in going through the trouble of backend isolation. Maintainers are free to use their discretion to decide whether backend isolation is necessary or desirable."
            }
          ]
        }
      ],
      "title": [
        {
          "__type": "Text",
          "__tag": 4046,
          "value": "Backend isolation in tests"
        }
      ],
      "level": 2,
      "target": "dev-arrayapi_backend_isolation"
    },
    {
      "__type": "Section",
      "__tag": 4015,
      "children": [
        {
          "__type": "Paragraph",
          "__tag": 4045,
          "children": [
            {
              "__type": "Text",
              "__tag": 4046,
              "value": "The "
            },
            {
              "__type": "Link",
              "__tag": 4049,
              "children": [
                {
                  "__type": "Text",
                  "__tag": 4046,
                  "value": "JAX JIT compiler"
                }
              ],
              "url": "https://jax.readthedocs.io/en/latest/jit-compilation.html",
              "title": ""
            },
            {
              "__type": "Text",
              "__tag": 4046,
              "value": " introduces special restrictions to all code wrapped by "
            },
            {
              "__type": "InlineCode",
              "__tag": 4051,
              "value": "@jax.jit"
            },
            {
              "__type": "Text",
              "__tag": 4046,
              "value": ", which are not present when running JAX in eager mode. Notably, boolean masks in "
            },
            {
              "__type": "InlineCode",
              "__tag": 4051,
              "value": "__getitem__"
            },
            {
              "__type": "Text",
              "__tag": 4046,
              "value": " and "
            },
            {
              "__type": "InlineCode",
              "__tag": 4051,
              "value": ".at"
            },
            {
              "__type": "Text",
              "__tag": 4046,
              "value": " aren't supported, and you can't materialize the arrays by applying "
            },
            {
              "__type": "InlineCode",
              "__tag": 4051,
              "value": "bool()"
            },
            {
              "__type": "Text",
              "__tag": 4046,
              "value": ", "
            },
            {
              "__type": "InlineCode",
              "__tag": 4051,
              "value": "float()"
            },
            {
              "__type": "Text",
              "__tag": 4046,
              "value": ", "
            },
            {
              "__type": "InlineCode",
              "__tag": 4051,
              "value": "np.asarray()"
            },
            {
              "__type": "Text",
              "__tag": 4046,
              "value": " etc. to them."
            }
          ]
        },
        {
          "__type": "Paragraph",
          "__tag": 4045,
          "children": [
            {
              "__type": "Text",
              "__tag": 4046,
              "value": "To properly test scipy with JAX, the tested scipy functions must be wrapped with "
            },
            {
              "__type": "InlineCode",
              "__tag": 4051,
              "value": "@jax.jit"
            },
            {
              "__type": "Text",
              "__tag": 4046,
              "value": " before they are called by the unit tests. This is done automatically when using "
            },
            {
              "__type": "InlineCode",
              "__tag": 4051,
              "value": "make_xp_test_case"
            },
            {
              "__type": "Text",
              "__tag": 4046,
              "value": " and its friends when the associated "
            },
            {
              "__type": "InlineCode",
              "__tag": 4051,
              "value": "xp_capabilities"
            },
            {
              "__type": "Text",
              "__tag": 4046,
              "value": " entry (or entries) have "
            },
            {
              "__type": "InlineCode",
              "__tag": 4051,
              "value": "jax_jit=True"
            },
            {
              "__type": "Text",
              "__tag": 4046,
              "value": "    "
            }
          ]
        },
        {
          "__type": "Code",
          "__tag": 4050,
          "value": "from scipy._lib._array_api import make_xp_test_case, xp_assert_close\nfrom scipy.mymodule import toto\n\n@make_xp_test_case(toto)\ndef test_toto(xp):\n   a = xp.asarray([3, 10, 5, 16, 8, 4, 2, 1, ])\n   b = xp.asarray([3, 5, 8, 4, 2, 1])\n   # When xp==jax.numpy, toto is wrapped with @jax.jit\n   # so long as the xp_capabilities entry for toto has\n   # jax_jit=True.\n   xp_assert_close(toto(a), b)",
          "execution_status": null,
          "out": ""
        },
        {
          "__type": "Paragraph",
          "__tag": 4045,
          "children": [
            {
              "__type": "Text",
              "__tag": 4046,
              "value": "To achieve this for private functions without "
            },
            {
              "__type": "InlineCode",
              "__tag": 4051,
              "value": "xp_capabilities"
            },
            {
              "__type": "Text",
              "__tag": 4046,
              "value": " entries, you should tag them as follows in your test module    "
            }
          ]
        },
        {
          "__type": "Code",
          "__tag": 4050,
          "value": "from scipy._lib._array_api import xp_assert_close\nfrom scipy._external.array_api_extra.testing import lazy_xp_function\nfrom scipy.mymodule import _private_toto_helper\n\nlazy_xp_function(_private_toto_helper)\n\n@pytest.mark.uses_xp_capabilities(False, reason=\"private\")\ndef test_private_toto_helper(xp):\n    a = xp.asarray([1, 2, 3])\n    b = xp.asarray([0, 2, 5])\n    # When xp==jax.numpy, _private_toto_helper is wrapped with @jax.jit\n    xp_assert_close(_private_toto_helper(a, b), a)",
          "execution_status": null,
          "out": ""
        },
        {
          "__type": "Admonition",
          "__tag": 4056,
          "kind": "warning",
          "base_type": "warning",
          "children": [
            {
              "__type": "AdmonitionTitle",
              "__tag": 4055,
              "children": [
                {
                  "__type": "Text",
                  "__tag": 4046,
                  "value": "warning "
                }
              ]
            },
            {
              "__type": "Paragraph",
              "__tag": 4045,
              "children": [
                {
                  "__type": "Text",
                  "__tag": 4046,
                  "value": "If instead of importing the functions from "
                },
                {
                  "__type": "InlineCode",
                  "__tag": 4051,
                  "value": "scipy.mymodule"
                },
                {
                  "__type": "Text",
                  "__tag": 4046,
                  "value": ", the above example imported "
                },
                {
                  "__type": "InlineCode",
                  "__tag": 4051,
                  "value": "mymodule"
                },
                {
                  "__type": "Text",
                  "__tag": 4046,
                  "value": " and called "
                },
                {
                  "__type": "InlineCode",
                  "__tag": 4051,
                  "value": "toto"
                },
                {
                  "__type": "Text",
                  "__tag": 4046,
                  "value": " through the qualified name "
                },
                {
                  "__type": "InlineCode",
                  "__tag": 4051,
                  "value": "mymodule.toto"
                },
                {
                  "__type": "Text",
                  "__tag": 4046,
                  "value": ", "
                },
                {
                  "__type": "InlineCode",
                  "__tag": 4051,
                  "value": "@jax.jit"
                },
                {
                  "__type": "Text",
                  "__tag": 4046,
                  "value": " would not be applied to "
                },
                {
                  "__type": "InlineCode",
                  "__tag": 4051,
                  "value": "toto"
                },
                {
                  "__type": "Text",
                  "__tag": 4046,
                  "value": ".  This is due to an implementation specific quirk which limits the application of "
                },
                {
                  "__type": "InlineCode",
                  "__tag": 4051,
                  "value": "@jax.jit"
                },
                {
                  "__type": "Text",
                  "__tag": 4046,
                  "value": " only to functions in the globals of the module that defines the current test. If one wishes to use a pattern like "
                },
                {
                  "__type": "InlineCode",
                  "__tag": 4051,
                  "value": "mymodule.toto"
                },
                {
                  "__type": "Text",
                  "__tag": 4046,
                  "value": " in a test, one must define a variable "
                },
                {
                  "__type": "InlineCode",
                  "__tag": 4051,
                  "value": "lazy_xp_modules"
                },
                {
                  "__type": "Text",
                  "__tag": 4046,
                  "value": " at the top of the test file to specify additional places the testing framework should look for functions tagged with "
                },
                {
                  "__type": "InlineCode",
                  "__tag": 4051,
                  "value": "lazy_xp_function"
                },
                {
                  "__type": "Text",
                  "__tag": 4046,
                  "value": "    "
                }
              ]
            },
            {
              "__type": "Code",
              "__tag": 4050,
              "value": "import scipy.mymodule as mymodule\nfrom scipy._lib._array_api import make_xp_test_case, xp_assert_close\n\nlazy_xp_modules = [mymodule]\n\n@make_xp_test_case(mymodule.toto)\ndef test_toto(xp):\n    a = xp.asarray([3, 10, 5, 16, 8, 4, 2, 1, ])\n    b = xp.asarray([3, 5, 8, 4, 2, 1])\n    # When xp==jax.numpy, toto is wrapped with @jax.jit\n    # so long as the xp_capabilities entry for toto has\n    # jax_jit=True.\n    xp_assert_close(toto(a), b)",
              "execution_status": null,
              "out": ""
            },
            {
              "__type": "Paragraph",
              "__tag": 4045,
              "children": [
                {
                  "__type": "Text",
                  "__tag": 4046,
                  "value": "This can be slightly annoying to remember at first, but in practice isn't too bad once one gets in the habit of checking for this. The essential complexity of "
                },
                {
                  "__type": "InlineCode",
                  "__tag": 4051,
                  "value": "lazy_xp_function"
                },
                {
                  "__type": "Text",
                  "__tag": 4046,
                  "value": " is actually quite high, and the current design trades off on developer ergonomics to allow for a simpler implementation."
                }
              ]
            }
          ]
        },
        {
          "__type": "Paragraph",
          "__tag": 4045,
          "children": [
            {
              "__type": "Text",
              "__tag": 4046,
              "value": "Testing lazy evaluation with Dask works similarly, except "
            },
            {
              "__type": "InlineCode",
              "__tag": 4051,
              "value": "lazy_xp_function"
            },
            {
              "__type": "Text",
              "__tag": 4046,
              "value": " wraps functions with a decorator that disables "
            },
            {
              "__type": "InlineCode",
              "__tag": 4051,
              "value": "compute()"
            },
            {
              "__type": "Text",
              "__tag": 4046,
              "value": " and "
            },
            {
              "__type": "InlineCode",
              "__tag": 4051,
              "value": "persist()"
            },
            {
              "__type": "Text",
              "__tag": 4046,
              "value": " and ensures that exceptions and warnings are raised eagerly. Similarly as for the JAX JIT, "
            },
            {
              "__type": "InlineCode",
              "__tag": 4051,
              "value": "make_xp_test_case"
            },
            {
              "__type": "Text",
              "__tag": 4046,
              "value": " and friends will automatically do this when the associated "
            },
            {
              "__type": "InlineCode",
              "__tag": 4051,
              "value": "xp_capabilities"
            },
            {
              "__type": "Text",
              "__tag": 4046,
              "value": " entry has "
            },
            {
              "__type": "InlineCode",
              "__tag": 4051,
              "value": "allow_dask_compute=False"
            },
            {
              "__type": "Text",
              "__tag": 4046,
              "value": ". The same warning about requiring "
            },
            {
              "__type": "InlineCode",
              "__tag": 4051,
              "value": "lazy_xp_modules"
            },
            {
              "__type": "Text",
              "__tag": 4046,
              "value": " applies for tests Dask works with lazy evaluation just as it does for tests of the JAX JIT."
            }
          ]
        },
        {
          "__type": "Paragraph",
          "__tag": 4045,
          "children": [
            {
              "__type": "Text",
              "__tag": 4046,
              "value": "See full documentation "
            },
            {
              "__type": "Link",
              "__tag": 4049,
              "children": [
                {
                  "__type": "Text",
                  "__tag": 4046,
                  "value": "here"
                }
              ],
              "url": "https://data-apis.org/array-api-extra/generated/array_api_extra.testing.lazy_xp_function.html",
              "title": ""
            },
            {
              "__type": "Text",
              "__tag": 4046,
              "value": "."
            }
          ]
        }
      ],
      "title": [
        {
          "__type": "Text",
          "__tag": 4046,
          "value": "Testing the JAX JIT compiler (and lazy evaluation with Dask)"
        }
      ],
      "level": 2,
      "target": null
    },
    {
      "__type": "Section",
      "__tag": 4015,
      "children": [
        {
          "__type": "Paragraph",
          "__tag": 4045,
          "children": [
            {
              "__type": "Text",
              "__tag": 4046,
              "value": "To declare that a test is testing a particular method of a class, one can pass a tuple of the form "
            },
            {
              "__type": "InlineCode",
              "__tag": 4051,
              "value": "tuple[type, str]"
            },
            {
              "__type": "Text",
              "__tag": 4046,
              "value": " as an entry of "
            },
            {
              "__type": "InlineCode",
              "__tag": 4051,
              "value": "funcs"
            },
            {
              "__type": "Text",
              "__tag": 4046,
              "value": " in "
            },
            {
              "__type": "InlineCode",
              "__tag": 4051,
              "value": "make_xp_test_case"
            },
            {
              "__type": "Text",
              "__tag": 4046,
              "value": " and "
            },
            {
              "__type": "InlineCode",
              "__tag": 4051,
              "value": "make_xp_pytest_marks"
            },
            {
              "__type": "Text",
              "__tag": 4046,
              "value": " or as the argument "
            },
            {
              "__type": "InlineCode",
              "__tag": 4051,
              "value": "func"
            },
            {
              "__type": "Text",
              "__tag": 4046,
              "value": " of "
            },
            {
              "__type": "InlineCode",
              "__tag": 4051,
              "value": "make_xp_pytest_param"
            },
            {
              "__type": "Text",
              "__tag": 4046,
              "value": ". The tuple "
            },
            {
              "__type": "InlineCode",
              "__tag": 4051,
              "value": "(A, \"f\")"
            },
            {
              "__type": "Text",
              "__tag": 4046,
              "value": " signifies that one is testing the method "
            },
            {
              "__type": "InlineCode",
              "__tag": 4051,
              "value": "A.f"
            },
            {
              "__type": "Text",
              "__tag": 4046,
              "value": " of the class "
            },
            {
              "__type": "InlineCode",
              "__tag": 4051,
              "value": "A"
            },
            {
              "__type": "Text",
              "__tag": 4046,
              "value": ". Such a tuple is used rather than simply "
            },
            {
              "__type": "InlineCode",
              "__tag": 4051,
              "value": "A.f"
            },
            {
              "__type": "Text",
              "__tag": 4046,
              "value": " in order allow unambiguous specification of what is being tested in cases where a method is inherited from a parent class.    "
            }
          ]
        },
        {
          "__type": "Code",
          "__tag": 4050,
          "value": "@make_xp_test_case((Foo, \"bar\"))\ndef test_Foo_bar(xp):\n    ...",
          "execution_status": null,
          "out": ""
        },
        {
          "__type": "Paragraph",
          "__tag": 4045,
          "children": [
            {
              "__type": "Text",
              "__tag": 4046,
              "value": "When passing such a tuple to "
            },
            {
              "__type": "InlineCode",
              "__tag": 4051,
              "value": "make_xp_pytest_param"
            },
            {
              "__type": "Text",
              "__tag": 4046,
              "value": ", only the first entry of the tuple is actually used in the resulting pytest param    "
            }
          ]
        },
        {
          "__type": "Code",
          "__tag": 4050,
          "value": "  @pytest.mark.parametrize(\"cls\", [(A, \"f\"), (B, \"f\"), C])\n      def test(cls, xp):\n          # cls iterates over A, B, C.\n\t  ...",
          "execution_status": null,
          "out": ""
        },
        {
          "__type": "Paragraph",
          "__tag": 4045,
          "children": [
            {
              "__type": "Text",
              "__tag": 4046,
              "value": "When using such tuple arguments, the pytest skips and xfails will be taken from the class level capabilities, unless a method specific override was added in the "
            },
            {
              "__type": "InlineCode",
              "__tag": 4051,
              "value": "method_capabilities"
            },
            {
              "__type": "Text",
              "__tag": 4046,
              "value": " kwarg of "
            },
            {
              "__type": "InlineCode",
              "__tag": 4051,
              "value": "xp_capabilities"
            },
            {
              "__type": "Text",
              "__tag": 4046,
              "value": "."
            }
          ]
        },
        {
          "__type": "Paragraph",
          "__tag": 4045,
          "children": [
            {
              "__type": "Text",
              "__tag": 4046,
              "value": "If the capabilities for "
            },
            {
              "__type": "InlineCode",
              "__tag": 4051,
              "value": "(A, \"f\")"
            },
            {
              "__type": "Text",
              "__tag": 4046,
              "value": " have "
            },
            {
              "__type": "InlineCode",
              "__tag": 4051,
              "value": "jax_jit=True"
            },
            {
              "__type": "Text",
              "__tag": 4046,
              "value": " (or if Dask is not in "
            },
            {
              "__type": "InlineCode",
              "__tag": 4051,
              "value": "skip_backends"
            },
            {
              "__type": "Text",
              "__tag": 4046,
              "value": ") then using "
            },
            {
              "__type": "InlineCode",
              "__tag": 4051,
              "value": "@make_xp_test_case((A, \"f\"))"
            },
            {
              "__type": "Text",
              "__tag": 4046,
              "value": " or one of its equivalents will cause "
            },
            {
              "__type": "InlineCode",
              "__tag": 4051,
              "value": "lazy_xp_function"
            },
            {
              "__type": "Text",
              "__tag": 4046,
              "value": " to be applied to "
            },
            {
              "__type": "InlineCode",
              "__tag": 4051,
              "value": "(A, \"f\")"
            },
            {
              "__type": "Text",
              "__tag": 4046,
              "value": ". ("
            },
            {
              "__type": "InlineCode",
              "__tag": 4051,
              "value": "lazy_xp_function"
            },
            {
              "__type": "Text",
              "__tag": 4046,
              "value": " will in this case replace "
            },
            {
              "__type": "InlineCode",
              "__tag": 4051,
              "value": "A.f"
            },
            {
              "__type": "Text",
              "__tag": 4046,
              "value": " with a clone to avoid unintentional modification of a parent in cases where a method is inherited from a parent class)."
            }
          ]
        }
      ],
      "title": [
        {
          "__type": "Text",
          "__tag": 4046,
          "value": "Adding tests for class methods"
        }
      ],
      "level": 2,
      "target": null
    },
    {
      "__type": "Section",
      "__tag": 4015,
      "children": [
        {
          "__type": "Paragraph",
          "__tag": 4045,
          "children": [
            {
              "__type": "Text",
              "__tag": 4046,
              "value": "Here are some additional resources which motivated some design decisions and helped during the development phase:"
            }
          ]
        },
        {
          "__type": "BulletList",
          "__tag": 4053,
          "ordered": false,
          "start": 1,
          "children": [
            {
              "__type": "ListItem",
              "__tag": 4054,
              "children": [
                {
                  "__type": "Paragraph",
                  "__tag": 4045,
                  "children": [
                    {
                      "__type": "Text",
                      "__tag": 4046,
                      "value": "Initial "
                    },
                    {
                      "__type": "Link",
                      "__tag": 4049,
                      "children": [
                        {
                          "__type": "Text",
                          "__tag": 4046,
                          "value": "PR"
                        }
                      ],
                      "url": "https://github.com/tupui/scipy/pull/24",
                      "title": ""
                    },
                    {
                      "__type": "Text",
                      "__tag": 4046,
                      "value": " with some discussions"
                    }
                  ]
                }
              ]
            },
            {
              "__type": "ListItem",
              "__tag": 4054,
              "children": [
                {
                  "__type": "Paragraph",
                  "__tag": 4045,
                  "children": [
                    {
                      "__type": "Text",
                      "__tag": 4046,
                      "value": "Quick started from this "
                    },
                    {
                      "__type": "Link",
                      "__tag": 4049,
                      "children": [
                        {
                          "__type": "Text",
                          "__tag": 4046,
                          "value": "PR"
                        }
                      ],
                      "url": "https://github.com/scipy/scipy/pull/15395",
                      "title": ""
                    },
                    {
                      "__type": "Text",
                      "__tag": 4046,
                      "value": " and   some inspiration taken from   "
                    },
                    {
                      "__type": "Link",
                      "__tag": 4049,
                      "children": [
                        {
                          "__type": "Text",
                          "__tag": 4046,
                          "value": "scikit-learn"
                        }
                      ],
                      "url": "https://github.com/scikit-learn/scikit-learn/blob/main/sklearn/utils/_array_api.py",
                      "title": ""
                    },
                    {
                      "__type": "Text",
                      "__tag": 4046,
                      "value": "."
                    }
                  ]
                }
              ]
            },
            {
              "__type": "ListItem",
              "__tag": 4054,
              "children": [
                {
                  "__type": "Paragraph",
                  "__tag": 4045,
                  "children": [
                    {
                      "__type": "Link",
                      "__tag": 4049,
                      "children": [
                        {
                          "__type": "Text",
                          "__tag": 4046,
                          "value": "PR"
                        }
                      ],
                      "url": "https://github.com/scikit-learn/scikit-learn/issues/22352",
                      "title": ""
                    },
                    {
                      "__type": "Text",
                      "__tag": 4046,
                      "value": " adding Array   API support to scikit-learn"
                    }
                  ]
                }
              ]
            },
            {
              "__type": "ListItem",
              "__tag": 4054,
              "children": [
                {
                  "__type": "Paragraph",
                  "__tag": 4045,
                  "children": [
                    {
                      "__type": "Text",
                      "__tag": 4046,
                      "value": "Some other relevant scikit-learn PRs:   "
                    },
                    {
                      "__type": "Link",
                      "__tag": 4049,
                      "children": [
                        {
                          "__type": "Text",
                          "__tag": 4046,
                          "value": "#22554"
                        }
                      ],
                      "url": "https://github.com/scikit-learn/scikit-learn/pull/22554",
                      "title": ""
                    },
                    {
                      "__type": "Text",
                      "__tag": 4046,
                      "value": " and   "
                    },
                    {
                      "__type": "Link",
                      "__tag": 4049,
                      "children": [
                        {
                          "__type": "Text",
                          "__tag": 4046,
                          "value": "#25956"
                        }
                      ],
                      "url": "https://github.com/scikit-learn/scikit-learn/pull/25956",
                      "title": ""
                    }
                  ]
                }
              ]
            }
          ]
        },
        {
          "__type": "Target",
          "__tag": 4061,
          "label": "RFC",
          "url": "https://github.com/scipy/scipy/issues/18286"
        },
        {
          "__type": "Target",
          "__tag": 4061,
          "label": "the tracker issue",
          "url": "https://github.com/scipy/scipy/issues/18867"
        }
      ],
      "title": [
        {
          "__type": "Text",
          "__tag": 4046,
          "value": "Additional information"
        }
      ],
      "level": 1,
      "target": null
    },
    {
      "__type": "Section",
      "__tag": 4015,
      "children": [
        {
          "__type": "Paragraph",
          "__tag": 4045,
          "children": [
            {
              "__type": "Text",
              "__tag": 4046,
              "value": "The below tables show the current state of alternative backend support across SciPy's modules. Public functions, function-like callables, and classes are included in the tables. Parts of the public API which are deemed out-of-scope are excluded from consideration when calculating coverage percentages. If a module or submodule contains no in-scope functions, it is excluded from the tables. For example, "
            },
            {
              "__type": "CrossRef",
              "__tag": 4002,
              "value": "scipy.datasets",
              "reference": {
                "__type": "LocalRef",
                "__tag": 4022,
                "kind": "module",
                "path": "scipy.datasets"
              },
              "kind": "module"
            },
            {
              "__type": "Text",
              "__tag": 4046,
              "value": " is excluded because its contents are considered out-of-scope."
            }
          ]
        },
        {
          "__type": "BulletList",
          "__tag": 4053,
          "ordered": false,
          "start": 1,
          "children": [
            {
              "__type": "ListItem",
              "__tag": 4054,
              "children": [
                {
                  "__type": "Paragraph",
                  "__tag": 4045,
                  "children": [
                    {
                      "__type": "CrossRef",
                      "__tag": 4002,
                      "value": "Array API Standard Support: cluster.vq",
                      "reference": {
                        "__type": "LocalRef",
                        "__tag": 4022,
                        "kind": "docs",
                        "path": "dev:api-dev:array_api_modules_tables:cluster_vq"
                      },
                      "kind": "exists"
                    }
                  ]
                }
              ]
            },
            {
              "__type": "ListItem",
              "__tag": 4054,
              "children": [
                {
                  "__type": "Paragraph",
                  "__tag": 4045,
                  "children": [
                    {
                      "__type": "CrossRef",
                      "__tag": 4002,
                      "value": "Array API Standard Support: cluster.hierarchy",
                      "reference": {
                        "__type": "LocalRef",
                        "__tag": 4022,
                        "kind": "docs",
                        "path": "dev:api-dev:array_api_modules_tables:cluster_hierarchy"
                      },
                      "kind": "exists"
                    }
                  ]
                }
              ]
            },
            {
              "__type": "ListItem",
              "__tag": 4054,
              "children": [
                {
                  "__type": "Paragraph",
                  "__tag": 4045,
                  "children": [
                    {
                      "__type": "CrossRef",
                      "__tag": 4002,
                      "value": "Array API Standard Support: constants",
                      "reference": {
                        "__type": "LocalRef",
                        "__tag": 4022,
                        "kind": "docs",
                        "path": "dev:api-dev:array_api_modules_tables:constants"
                      },
                      "kind": "exists"
                    }
                  ]
                }
              ]
            },
            {
              "__type": "ListItem",
              "__tag": 4054,
              "children": [
                {
                  "__type": "Paragraph",
                  "__tag": 4045,
                  "children": [
                    {
                      "__type": "CrossRef",
                      "__tag": 4002,
                      "value": "Array API Standard Support: differentiate",
                      "reference": {
                        "__type": "LocalRef",
                        "__tag": 4022,
                        "kind": "docs",
                        "path": "dev:api-dev:array_api_modules_tables:differentiate"
                      },
                      "kind": "exists"
                    }
                  ]
                }
              ]
            },
            {
              "__type": "ListItem",
              "__tag": 4054,
              "children": [
                {
                  "__type": "Paragraph",
                  "__tag": 4045,
                  "children": [
                    {
                      "__type": "CrossRef",
                      "__tag": 4002,
                      "value": "Array API Standard Support: fft",
                      "reference": {
                        "__type": "LocalRef",
                        "__tag": 4022,
                        "kind": "docs",
                        "path": "dev:api-dev:array_api_modules_tables:fft"
                      },
                      "kind": "exists"
                    }
                  ]
                }
              ]
            },
            {
              "__type": "ListItem",
              "__tag": 4054,
              "children": [
                {
                  "__type": "Paragraph",
                  "__tag": 4045,
                  "children": [
                    {
                      "__type": "CrossRef",
                      "__tag": 4002,
                      "value": "Array API Standard Support: integrate",
                      "reference": {
                        "__type": "LocalRef",
                        "__tag": 4022,
                        "kind": "docs",
                        "path": "dev:api-dev:array_api_modules_tables:integrate"
                      },
                      "kind": "exists"
                    }
                  ]
                }
              ]
            },
            {
              "__type": "ListItem",
              "__tag": 4054,
              "children": [
                {
                  "__type": "Paragraph",
                  "__tag": 4045,
                  "children": [
                    {
                      "__type": "CrossRef",
                      "__tag": 4002,
                      "value": "Array API Standard Support: interpolate",
                      "reference": {
                        "__type": "LocalRef",
                        "__tag": 4022,
                        "kind": "docs",
                        "path": "dev:api-dev:array_api_modules_tables:interpolate"
                      },
                      "kind": "exists"
                    }
                  ]
                }
              ]
            },
            {
              "__type": "ListItem",
              "__tag": 4054,
              "children": [
                {
                  "__type": "Paragraph",
                  "__tag": 4045,
                  "children": [
                    {
                      "__type": "CrossRef",
                      "__tag": 4002,
                      "value": "Array API Standard Support: io",
                      "reference": {
                        "__type": "LocalRef",
                        "__tag": 4022,
                        "kind": "docs",
                        "path": "dev:api-dev:array_api_modules_tables:io"
                      },
                      "kind": "exists"
                    }
                  ]
                }
              ]
            },
            {
              "__type": "ListItem",
              "__tag": 4054,
              "children": [
                {
                  "__type": "Paragraph",
                  "__tag": 4045,
                  "children": [
                    {
                      "__type": "CrossRef",
                      "__tag": 4002,
                      "value": "Array API Standard Support: linalg",
                      "reference": {
                        "__type": "LocalRef",
                        "__tag": 4022,
                        "kind": "docs",
                        "path": "dev:api-dev:array_api_modules_tables:linalg"
                      },
                      "kind": "exists"
                    }
                  ]
                }
              ]
            },
            {
              "__type": "ListItem",
              "__tag": 4054,
              "children": [
                {
                  "__type": "Paragraph",
                  "__tag": 4045,
                  "children": [
                    {
                      "__type": "CrossRef",
                      "__tag": 4002,
                      "value": "Array API Standard Support: linalg.interpolative",
                      "reference": {
                        "__type": "LocalRef",
                        "__tag": 4022,
                        "kind": "docs",
                        "path": "dev:api-dev:array_api_modules_tables:linalg_interpolative"
                      },
                      "kind": "exists"
                    }
                  ]
                }
              ]
            },
            {
              "__type": "ListItem",
              "__tag": 4054,
              "children": [
                {
                  "__type": "Paragraph",
                  "__tag": 4045,
                  "children": [
                    {
                      "__type": "CrossRef",
                      "__tag": 4002,
                      "value": "Array API Standard Support: ndimage",
                      "reference": {
                        "__type": "LocalRef",
                        "__tag": 4022,
                        "kind": "docs",
                        "path": "dev:api-dev:array_api_modules_tables:ndimage"
                      },
                      "kind": "exists"
                    }
                  ]
                }
              ]
            },
            {
              "__type": "ListItem",
              "__tag": 4054,
              "children": [
                {
                  "__type": "Paragraph",
                  "__tag": 4045,
                  "children": [
                    {
                      "__type": "CrossRef",
                      "__tag": 4002,
                      "value": "Array API Standard Support: optimize",
                      "reference": {
                        "__type": "LocalRef",
                        "__tag": 4022,
                        "kind": "docs",
                        "path": "dev:api-dev:array_api_modules_tables:optimize"
                      },
                      "kind": "exists"
                    }
                  ]
                }
              ]
            },
            {
              "__type": "ListItem",
              "__tag": 4054,
              "children": [
                {
                  "__type": "Paragraph",
                  "__tag": 4045,
                  "children": [
                    {
                      "__type": "CrossRef",
                      "__tag": 4002,
                      "value": "Array API Standard Support: optimize.elementwise",
                      "reference": {
                        "__type": "LocalRef",
                        "__tag": 4022,
                        "kind": "docs",
                        "path": "dev:api-dev:array_api_modules_tables:optimize_elementwise"
                      },
                      "kind": "exists"
                    }
                  ]
                }
              ]
            },
            {
              "__type": "ListItem",
              "__tag": 4054,
              "children": [
                {
                  "__type": "Paragraph",
                  "__tag": 4045,
                  "children": [
                    {
                      "__type": "CrossRef",
                      "__tag": 4002,
                      "value": "Array API Standard Support: signal",
                      "reference": {
                        "__type": "LocalRef",
                        "__tag": 4022,
                        "kind": "docs",
                        "path": "dev:api-dev:array_api_modules_tables:signal"
                      },
                      "kind": "exists"
                    }
                  ]
                }
              ]
            },
            {
              "__type": "ListItem",
              "__tag": 4054,
              "children": [
                {
                  "__type": "Paragraph",
                  "__tag": 4045,
                  "children": [
                    {
                      "__type": "CrossRef",
                      "__tag": 4002,
                      "value": "Array API Standard Support: signal.windows",
                      "reference": {
                        "__type": "LocalRef",
                        "__tag": 4022,
                        "kind": "docs",
                        "path": "dev:api-dev:array_api_modules_tables:signal_windows"
                      },
                      "kind": "exists"
                    }
                  ]
                }
              ]
            },
            {
              "__type": "ListItem",
              "__tag": 4054,
              "children": [
                {
                  "__type": "Paragraph",
                  "__tag": 4045,
                  "children": [
                    {
                      "__type": "CrossRef",
                      "__tag": 4002,
                      "value": "Array API Standard Support: sparse",
                      "reference": {
                        "__type": "LocalRef",
                        "__tag": 4022,
                        "kind": "docs",
                        "path": "dev:api-dev:array_api_modules_tables:sparse"
                      },
                      "kind": "exists"
                    }
                  ]
                }
              ]
            },
            {
              "__type": "ListItem",
              "__tag": 4054,
              "children": [
                {
                  "__type": "Paragraph",
                  "__tag": 4045,
                  "children": [
                    {
                      "__type": "CrossRef",
                      "__tag": 4002,
                      "value": "Array API Standard Support: sparse.linalg",
                      "reference": {
                        "__type": "LocalRef",
                        "__tag": 4022,
                        "kind": "docs",
                        "path": "dev:api-dev:array_api_modules_tables:sparse_linalg"
                      },
                      "kind": "exists"
                    }
                  ]
                }
              ]
            },
            {
              "__type": "ListItem",
              "__tag": 4054,
              "children": [
                {
                  "__type": "Paragraph",
                  "__tag": 4045,
                  "children": [
                    {
                      "__type": "CrossRef",
                      "__tag": 4002,
                      "value": "Array API Standard Support: sparse.csgraph",
                      "reference": {
                        "__type": "LocalRef",
                        "__tag": 4022,
                        "kind": "docs",
                        "path": "dev:api-dev:array_api_modules_tables:sparse_csgraph"
                      },
                      "kind": "exists"
                    }
                  ]
                }
              ]
            },
            {
              "__type": "ListItem",
              "__tag": 4054,
              "children": [
                {
                  "__type": "Paragraph",
                  "__tag": 4045,
                  "children": [
                    {
                      "__type": "CrossRef",
                      "__tag": 4002,
                      "value": "Array API Standard Support: spatial",
                      "reference": {
                        "__type": "LocalRef",
                        "__tag": 4022,
                        "kind": "docs",
                        "path": "dev:api-dev:array_api_modules_tables:spatial"
                      },
                      "kind": "exists"
                    }
                  ]
                }
              ]
            },
            {
              "__type": "ListItem",
              "__tag": 4054,
              "children": [
                {
                  "__type": "Paragraph",
                  "__tag": 4045,
                  "children": [
                    {
                      "__type": "CrossRef",
                      "__tag": 4002,
                      "value": "Array API Standard Support: spatial.distance",
                      "reference": {
                        "__type": "LocalRef",
                        "__tag": 4022,
                        "kind": "docs",
                        "path": "dev:api-dev:array_api_modules_tables:spatial_distance"
                      },
                      "kind": "exists"
                    }
                  ]
                }
              ]
            },
            {
              "__type": "ListItem",
              "__tag": 4054,
              "children": [
                {
                  "__type": "Paragraph",
                  "__tag": 4045,
                  "children": [
                    {
                      "__type": "CrossRef",
                      "__tag": 4002,
                      "value": "Array API Standard Support: spatial.transform",
                      "reference": {
                        "__type": "LocalRef",
                        "__tag": 4022,
                        "kind": "docs",
                        "path": "dev:api-dev:array_api_modules_tables:spatial_transform"
                      },
                      "kind": "exists"
                    }
                  ]
                }
              ]
            },
            {
              "__type": "ListItem",
              "__tag": 4054,
              "children": [
                {
                  "__type": "Paragraph",
                  "__tag": 4045,
                  "children": [
                    {
                      "__type": "CrossRef",
                      "__tag": 4002,
                      "value": "Array API Standard Support: special",
                      "reference": {
                        "__type": "LocalRef",
                        "__tag": 4022,
                        "kind": "docs",
                        "path": "dev:api-dev:array_api_modules_tables:special"
                      },
                      "kind": "exists"
                    }
                  ]
                }
              ]
            },
            {
              "__type": "ListItem",
              "__tag": 4054,
              "children": [
                {
                  "__type": "Paragraph",
                  "__tag": 4045,
                  "children": [
                    {
                      "__type": "CrossRef",
                      "__tag": 4002,
                      "value": "Array API Standard Support: stats",
                      "reference": {
                        "__type": "LocalRef",
                        "__tag": 4022,
                        "kind": "docs",
                        "path": "dev:api-dev:array_api_modules_tables:stats"
                      },
                      "kind": "exists"
                    }
                  ]
                }
              ]
            },
            {
              "__type": "ListItem",
              "__tag": 4054,
              "children": [
                {
                  "__type": "Paragraph",
                  "__tag": 4045,
                  "children": [
                    {
                      "__type": "CrossRef",
                      "__tag": 4002,
                      "value": "Array API Standard Support: stats.contingency",
                      "reference": {
                        "__type": "LocalRef",
                        "__tag": 4022,
                        "kind": "docs",
                        "path": "dev:api-dev:array_api_modules_tables:stats_contingency"
                      },
                      "kind": "exists"
                    }
                  ]
                }
              ]
            },
            {
              "__type": "ListItem",
              "__tag": 4054,
              "children": [
                {
                  "__type": "Paragraph",
                  "__tag": 4045,
                  "children": [
                    {
                      "__type": "CrossRef",
                      "__tag": 4002,
                      "value": "Array API Standard Support: stats.qmc",
                      "reference": {
                        "__type": "LocalRef",
                        "__tag": 4022,
                        "kind": "docs",
                        "path": "dev:api-dev:array_api_modules_tables:stats_qmc"
                      },
                      "kind": "exists"
                    }
                  ]
                }
              ]
            }
          ]
        }
      ],
      "title": [
        {
          "__type": "Text",
          "__tag": 4046,
          "value": "API Coverage"
        }
      ],
      "level": 1,
      "target": "dev-arrayapi_coverage"
    },
    {
      "__type": "Section",
      "__tag": 4015,
      "children": [],
      "title": [
        {
          "__type": "Text",
          "__tag": 4046,
          "value": "Support on CPU"
        }
      ],
      "level": 2,
      "target": null
    },
    {
      "__type": "Section",
      "__tag": 4015,
      "children": [],
      "title": [
        {
          "__type": "Text",
          "__tag": 4046,
          "value": "Support on GPU"
        }
      ],
      "level": 2,
      "target": null
    },
    {
      "__type": "Section",
      "__tag": 4015,
      "children": [],
      "title": [
        {
          "__type": "Text",
          "__tag": 4046,
          "value": "Support with JIT"
        }
      ],
      "level": 2,
      "target": null
    }
  ],
  "local_refs": []
}