[{"id":"16839311085","type":"PullRequestReviewCommentEvent","actor":{"id":1953382,"login":"steppi","display_login":"steppi","gravatar_id":"","url":"https://api.github.com/users/steppi","avatar_url":"https://avatars.githubusercontent.com/u/1953382?"},"repo":{"id":832302800,"name":"scipy/xsf","url":"https://api.github.com/repos/scipy/xsf"},"payload":{"action":"created","comment":{"url":"https://api.github.com/repos/scipy/xsf/pulls/comments/4230541712","pull_request_review_id":5470592098,"id":4230541712,"node_id":"PRRC_kwDOMZvu0M78KPGQ","diff_hunk":"@@ -0,0 +1,129 @@\n+#pragma once\n+\n+#include \"config.h\"\n+\n+namespace xsf {\n+\n+namespace detail {\n+\n+    template <typename T, typename = void>\n+    constexpr inline bool valid_extent = false;\n+\n+    template <typename T>\n+    constexpr inline bool\n+        valid_extent<T, cxx::void_t<typename T::index_type, typename T::size_type, typename T::rank_type>> = true;\n+\n+} // namespace detail\n+\n+struct relaxed_layout {\n+    template <typename TExtents>\n+    class mapping {\n+      public:\n+        static_assert(detail::valid_extent<TExtents>, \"TExtents must be a valid extents type.\");\n+\n+        using extents_type = TExtents;\n+        using index_type = typename extents_type::index_type;\n+        using size_type = typename extents_type::size_type;\n+        using rank_type = typename extents_type::rank_type;\n+        using strides_type = cxx::array<index_type, extents_type::rank()>;\n+        using layout_type = relaxed_layout;\n+\n+      private:\n+        extents_type m_extents;\n+        strides_type m_strides;\n+        index_type m_offset{0};\n+\n+        template <cxx::size_t Rank, cxx::size_t MaxRank>\n+        struct rank_counter {};\n+\n+        template <cxx::size_t Rank, cxx::size_t MaxRank, typename TIndex, typename... TIndices>\n+        XSF_HOST_DEVICE constexpr auto\n+        compute_offset(rank_counter<Rank, MaxRank>, const TIndex &index, TIndices... indices) const noexcept {\n+            return static_cast<index_type>(index) * m_strides[Rank] +\n+                   compute_offset(rank_counter<Rank + 1, MaxRank>{}, indices...);\n+        }\n+\n+        template <typename TIndex>\n+        XSF_HOST_DEVICE constexpr auto compute_offset(\n+            rank_counter<extents_type::rank() - 1, extents_type::rank()>, const TIndex &index\n+        ) const noexcept {\n+            return static_cast<index_type>(index) * m_strides[extents_type::rank() - 1];\n+        }\n+\n+        XSF_HOST_DEVICE constexpr auto compute_offset(rank_counter<0, 0>) const noexcept { return index_type{0}; }\n+\n+      public:\n+        XSF_HOST_DEVICE mapping() = default;","path":"include/xsf/relaxed_layout.h","commit_id":"0ce5b61a503ce549e90f77d64fa7f90518f755eb","original_commit_id":"0ce5b61a503ce549e90f77d64fa7f90518f755eb","user":{"login":"steppi","id":1953382,"node_id":"MDQ6VXNlcjE5NTMzODI=","avatar_url":"https://avatars.githubusercontent.com/u/1953382?v=4","gravatar_id":"","url":"https://api.github.com/users/steppi","html_url":"https://github.com/steppi","followers_url":"https://api.github.com/users/steppi/followers","following_url":"https://api.github.com/users/steppi/following{/other_user}","gists_url":"https://api.github.com/users/steppi/gists{/gist_id}","starred_url":"https://api.github.com/users/steppi/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/steppi/subscriptions","organizations_url":"https://api.github.com/users/steppi/orgs","repos_url":"https://api.github.com/users/steppi/repos","events_url":"https://api.github.com/users/steppi/events{/privacy}","received_events_url":"https://api.github.com/users/steppi/received_events","type":"User","user_view_type":"public","site_admin":false},"body":"I don't think the current default constructor is going to do anything useful. Something like this might make more sense\n```suggestion\n        XSF_HOST_DEVICE mapping() : mapping(extents_type{}) {}\n```","created_at":"2026-10-09T13:21:26Z","updated_at":"2026-10-09T13:21:38Z","html_url":"https://github.com/scipy/xsf/pull/294#discussion_r4230541712","pull_request_url":"https://api.github.com/repos/scipy/xsf/pulls/294","_links":{"self":{"href":"https://api.github.com/repos/scipy/xsf/pulls/comments/4230541712"},"html":{"href":"https://github.com/scipy/xsf/pull/294#discussion_r4230541712"},"pull_request":{"href":"https://api.github.com/repos/scipy/xsf/pulls/294"}},"reactions":{"url":"https://api.github.com/repos/scipy/xsf/pulls/comments/4230541712/reactions","total_count":1,"+1":1,"-1":0,"laugh":0,"hooray":0,"confused":0,"heart":0,"rocket":0,"eyes":0},"original_position":56,"position":1,"subject_type":"line"},"pull_request":{"url":"https://api.github.com/repos/scipy/xsf/pulls/294","id":4675777534,"number":294,"head":{"ref":"feature/relaxed-mdspan-layout","sha":"80f07575fb0ceb710cfeb6be1466070e448ca963","repo":{"id":1365640351,"url":"https://api.github.com/repos/sbaldu/xsf","name":"xsf"}},"base":{"ref":"main","sha":"471c1b5a1e334a2773edaae9dbb215205368d466","repo":{"id":832302800,"url":"https://api.github.com/repos/scipy/xsf","name":"xsf"}}}},"public":true,"created_at":"2026-10-09T13:21:26Z","org":{"id":288277,"login":"scipy","gravatar_id":"","url":"https://api.github.com/orgs/scipy","avatar_url":"https://avatars.githubusercontent.com/u/288277?"}},{"id":"16838592065","type":"PullRequestEvent","actor":{"id":14386,"login":"martinus","display_login":"martinus","gravatar_id":"","url":"https://api.github.com/users/martinus","avatar_url":"https://avatars.githubusercontent.com/u/14386?"},"repo":{"id":1380897551,"name":"martinus/quetschn","url":"https://api.github.com/repos/martinus/quetschn"},"payload":{"action":"opened","number":149,"pull_request":{"url":"https://api.github.com/repos/martinus/quetschn/pulls/149","id":4803162880,"number":149,"head":{"ref":"fuzz-msan-cflite","sha":"6d474cc8ce7b3c6b934ba808015ff0324f1d30c2","repo":{"id":1380897551,"url":"https://api.github.com/repos/martinus/quetschn","name":"quetschn"}},"base":{"ref":"main","sha":"5342a9bde843f73f455cc944834ba724e8a4cc69","repo":{"id":1380897551,"url":"https://api.github.com/repos/martinus/quetschn","name":"quetschn"}}}},"public":true,"created_at":"2026-10-09T16:31:57Z"},{"id":"16837112503","type":"PullRequestEvent","actor":{"id":14386,"login":"martinus","display_login":"martinus","gravatar_id":"","url":"https://api.github.com/users/martinus","avatar_url":"https://avatars.githubusercontent.com/u/14386?"},"repo":{"id":1380897551,"name":"martinus/quetschn","url":"https://api.github.com/repos/martinus/quetschn"},"payload":{"action":"merged","number":148,"pull_request":{"url":"https://api.github.com/repos/martinus/quetschn/pulls/148","id":4801868057,"number":148,"head":{"ref":"docs-question-sent","sha":"25f656092bd5e3ac624326f353e977a860ffa86e","repo":{"id":1380897551,"url":"https://api.github.com/repos/martinus/quetschn","name":"quetschn"}},"base":{"ref":"main","sha":"c469a2329205791b56d7bad41c56cef5734e5163","repo":{"id":1380897551,"url":"https://api.github.com/repos/martinus/quetschn","name":"quetschn"}}}},"public":true,"created_at":"2026-10-09T15:01:33Z"},{"id":"23493024569","type":"PushEvent","actor":{"id":14386,"login":"martinus","display_login":"martinus","gravatar_id":"","url":"https://api.github.com/users/martinus","avatar_url":"https://avatars.githubusercontent.com/u/14386?"},"repo":{"id":1380897551,"name":"martinus/quetschn","url":"https://api.github.com/repos/martinus/quetschn"},"payload":{"repository_id":1380897551,"push_id":45854987717,"ref":"refs/heads/zram-phone-noise","head":"2d67f1addea561b71c950b70f0ff5e2b8f3ab941","before":"99c904a7d02b44d87aeb2fb805c0aa68f5496b87"},"public":true,"created_at":"2026-10-08T13:55:11Z"},{"id":"16836814691","type":"IssueCommentEvent","actor":{"id":1953382,"login":"steppi","display_login":"steppi","gravatar_id":"","url":"https://api.github.com/users/steppi","avatar_url":"https://avatars.githubusercontent.com/u/1953382?"},"repo":{"id":72523920,"name":"cupy/cupy","url":"https://api.github.com/repos/cupy/cupy"},"payload":{"action":"created","issue":{"url":"https://api.github.com/repos/cupy/cupy/issues/9959","repository_url":"https://api.github.com/repos/cupy/cupy","labels_url":"https://api.github.com/repos/cupy/cupy/issues/9959/labels{/name}","comments_url":"https://api.github.com/repos/cupy/cupy/issues/9959/comments","events_url":"https://api.github.com/repos/cupy/cupy/issues/9959/events","html_url":"https://github.com/cupy/cupy/pull/9959","id":4506751331,"node_id":"PR_kwDOBFKgkM7ejQPY","number":9959,"title":"Add gufunc-like core_ndim>0 support to ElementwiseKernel","user":{"login":"steppi","id":1953382,"node_id":"MDQ6VXNlcjE5NTMzODI=","avatar_url":"https://avatars.githubusercontent.com/u/1953382?v=4","gravatar_id":"","url":"https://api.github.com/users/steppi","html_url":"https://github.com/steppi","followers_url":"https://api.github.com/users/steppi/followers","following_url":"https://api.github.com/users/steppi/following{/other_user}","gists_url":"https://api.github.com/users/steppi/gists{/gist_id}","starred_url":"https://api.github.com/users/steppi/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/steppi/subscriptions","organizations_url":"https://api.github.com/users/steppi/orgs","repos_url":"https://api.github.com/users/steppi/repos","events_url":"https://api.github.com/users/steppi/events{/privacy}","received_events_url":"https://api.github.com/users/steppi/received_events","type":"User","user_view_type":"public","site_admin":false},"labels":[{"id":545923268,"node_id":"MDU6TGFiZWw1NDU5MjMyNjg=","url":"https://api.github.com/repos/cupy/cupy/labels/cat:feature","name":"cat:feature","color":"fbca04","default":false,"description":"New features/APIs","archived_at":null,"archived_by":null},{"id":1052989469,"node_id":"MDU6TGFiZWwxMDUyOTg5NDY5","url":"https://api.github.com/repos/cupy/cupy/labels/prio:medium","name":"prio:medium","color":"c686e0","default":false,"description":"","archived_at":null,"archived_by":null}],"state":"open","locked":false,"assignees":[{"login":"seberg","id":61977,"node_id":"MDQ6VXNlcjYxOTc3","avatar_url":"https://avatars.githubusercontent.com/u/61977?v=4","gravatar_id":"","url":"https://api.github.com/users/seberg","html_url":"https://github.com/seberg","followers_url":"https://api.github.com/users/seberg/followers","following_url":"https://api.github.com/users/seberg/following{/other_user}","gists_url":"https://api.github.com/users/seberg/gists{/gist_id}","starred_url":"https://api.github.com/users/seberg/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/seberg/subscriptions","organizations_url":"https://api.github.com/users/seberg/orgs","repos_url":"https://api.github.com/users/seberg/repos","events_url":"https://api.github.com/users/seberg/events{/privacy}","received_events_url":"https://api.github.com/users/seberg/received_events","type":"User","user_view_type":"public","site_admin":false}],"milestone":{"url":"https://api.github.com/repos/cupy/cupy/milestones/165","html_url":"https://github.com/cupy/cupy/milestone/165","labels_url":"https://api.github.com/repos/cupy/cupy/milestones/165/labels","id":15832676,"node_id":"MI_kwDOBFKgkM4A8ZZk","number":165,"title":"v14.3.0","description":"","creator":{"login":"leofang","id":5534781,"node_id":"MDQ6VXNlcjU1MzQ3ODE=","avatar_url":"https://avatars.githubusercontent.com/u/5534781?v=4","gravatar_id":"","url":"https://api.github.com/users/leofang","html_url":"https://github.com/leofang","followers_url":"https://api.github.com/users/leofang/followers","following_url":"https://api.github.com/users/leofang/following{/other_user}","gists_url":"https://api.github.com/users/leofang/gists{/gist_id}","starred_url":"https://api.github.com/users/leofang/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/leofang/subscriptions","organizations_url":"https://api.github.com/users/leofang/orgs","repos_url":"https://api.github.com/users/leofang/repos","events_url":"https://api.github.com/users/leofang/events{/privacy}","received_events_url":"https://api.github.com/users/leofang/received_events","type":"User","user_view_type":"public","site_admin":false},"open_issues":26,"closed_issues":94,"state":"open","created_at":"2026-05-07T14:18:53Z","updated_at":"2026-10-09T13:41:32Z","due_on":"2026-11-21T00:00:00Z","closed_at":null},"comments":17,"created_at":"2026-05-23T03:57:58Z","updated_at":"2026-10-09T16:09:40Z","closed_at":null,"assignee":{"login":"seberg","id":61977,"node_id":"MDQ6VXNlcjYxOTc3","avatar_url":"https://avatars.githubusercontent.com/u/61977?v=4","gravatar_id":"","url":"https://api.github.com/users/seberg","html_url":"https://github.com/seberg","followers_url":"https://api.github.com/users/seberg/followers","following_url":"https://api.github.com/users/seberg/following{/other_user}","gists_url":"https://api.github.com/users/seberg/gists{/gist_id}","starred_url":"https://api.github.com/users/seberg/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/seberg/subscriptions","organizations_url":"https://api.github.com/users/seberg/orgs","repos_url":"https://api.github.com/users/seberg/repos","events_url":"https://api.github.com/users/seberg/events{/privacy}","received_events_url":"https://api.github.com/users/seberg/received_events","type":"User","user_view_type":"public","site_admin":false},"issue_field_values":[],"type":null,"active_lock_reason":null,"draft":false,"pull_request":{"url":"https://api.github.com/repos/cupy/cupy/pulls/9959","html_url":"https://github.com/cupy/cupy/pull/9959","diff_url":"https://github.com/cupy/cupy/pull/9959.diff","patch_url":"https://github.com/cupy/cupy/pull/9959.patch","merged_at":null},"body":"Closes #9839 \r\n\r\nThis PR adds `cupy._core.BatchwiseKernel`, a gufunc like generalization of `ElementwiseKernel`. \r\n\r\n### API decisions\r\n\r\nHere is an example of the `BatchwiseKernel` in action\r\n\r\n```python\r\npreamble = \"\"\"\r\n#include <cupy/xsf/stats.h>\r\n#include <cupy/xsf/cupy.h>\r\n\"\"\"\r\n\r\ndef _poisson_binom_pmf_shape_mapper(in_core_shapes):\r\n    return ((in_core_shapes[0][0] + 1, ),)\r\n\r\n\r\n_poisson_binom_pmf_all = BatchwiseKernel(\r\n    in_params=\"T_1d p\",\r\n    out_params=\"T_1d out\",\r\n    operation=(\r\n        \"xsf::poisson_binom_pmf_all(xsf::as_mdspan(p), xsf::as_mdspan(out));\"\r\n    ),\r\n    name=\"cupy_poisson_binom_pmf_all\",\r\n    preamble=preamble,\r\n    core_shape_mapper=_poisson_binom_pmf_shape_mapper,\r\n)\r\n```\r\n\r\nA couple things that may be observed here:\r\n\r\n* The core dimensionality of inputs and outputs are specified by appending `_1d`, `_2d` etc.  to the type field.\r\n* The manner in which the core output shapes depend on the core input shapes is specified with a mandatory kwarg,`core_shape_mapper`, which takes a callable mapping a tuple of core shapes of the inputs to a tuple of core shapes of the outputs. In the above example, it maps a 1d input of n binomial success probabilities to a 1d output containing the n+1 values of the Poisson Binomial pmf for $k = 0\\ldots n$.\r\n* There are no gufunc signatures to parse. The nature of the gufunc is specified by the core dimensionalities of the inputs and outs, and the mandatory shape mapper. This was done for the sake of implementation simplicity.\r\n\r\nA few limitations deliberately imposed  for the sake of implementation simplicity which could potentially be removed in future work:\r\n* The `__call__` method takes `out` as a required argument. The caller is responsible for passing an output array of suitable `shape` and `dtype`.\r\n* The `reduce_dims` kwarg of `BatchwiseKernel` is completely ignored. I didn't want to have to grapple with making this work when there are nonzero core dimensions in this PR.\r\n\r\n### Internals\r\n\r\nNow for how this works internally:\r\n@seberg, your suggestion from here, https://github.com/cupy/cupy/issues/9839#issuecomment-4149965496, ended up working exceptionally well. I've added an optional `core_ndims` template argument to `CArray` with a default of 0. I then added overloads for `operator[]` which allow indexing only with the indices for batch dimensions to get a CArray of the core slice:\r\n\r\n<details>\r\n\r\n```C+++\r\n  template <typename Int, int C = _core_ndim>\r\n  __device__\r\n  typename cupy::type_traits::enable_if<(C > 0 && _ndim > C), CArray<T, C, _c_contiguous, _use_32bit_indexing, 0> >::type\r\n  operator[](const Int (&idx)[_ndim - C]) const {\r\n    // overload for case _core_ndim > 0. Indexing yields CArray for a core slice.\r\n    constexpr int batch_ndim = _ndim - C;\r\n    index_t diff = 0;\r\n    for (int dim = 0; dim < batch_ndim; ++dim) {\r\n      diff += static_cast<index_t>(strides_[dim]) * static_cast<index_t>(idx[dim]);\r\n    }\r\n\r\n    const char* ptr = reinterpret_cast<const char*>(data_);\r\n    T* new_head = const_cast<T*>(reinterpret_cast<const T*>(ptr + diff));\r\n\r\n    return CArray<T, C, _c_contiguous, _use_32bit_indexing, 0>(\r\n        new_head,\r\n        this->shape_ + batch_ndim,\r\n        this->strides_ + batch_ndim\r\n    );\r\n  }\r\n```\r\n\r\n</details>\r\n\r\nI then added core dimension awareness to `_ArgInfo` and updated its `get_c_type` member function to yield CArrays with the core dimension set to a nonzero value when needed:\r\n\r\n<details>\r\n\r\n```c+++\r\n    cdef str get_c_type(self, type_decls=None):\r\n        # Returns the C type representation.\r\n        if self.arg_kind == ARG_KIND_NDARRAY:\r\n            name = _get_typename(self.dtype, type_decls)\r\n            if self.core_ndim > 0:\r\n                name = 'CArray<%s, %d, %d, %d, %d>' % (\r\n                    name, self.ndim,\r\n                    self.c_contiguous, self.index_32_bits, self.core_ndim\r\n)\r\n            else:\r\n                name = 'CArray<%s, %d, %d, %d>' % (\r\n                    name, self.ndim,\r\n                    self.c_contiguous, self.index_32_bits)\r\n            return name\r\n        if self.arg_kind == ARG_KIND_SCALAR:\r\n            return _get_typename(self.dtype, type_decls)\r\n        if self.arg_kind == ARG_KIND_INDEXER:\r\n            return 'CIndexer<%d, %d>' % (self.ndim, self.index_32_bits)\r\n        if self.arg_kind == ARG_KIND_TEXTURE:\r\n            return 'cudaTextureObject_t'\r\n        assert False\r\n```\r\n\r\n</details>\r\n\r\nTo handle the broadcasting of inputs and outputs, I borrowed the logic used here in SciPy (https://github.com/scipy/scipy/blob/c7641ff184c1dcbf549acf71a3fb217ecb4b69a4/scipy/special/_ufunc_tools.py#L96-L111).\r\n\r\nI set the CIndexer up to loop through the batch dimensions, and everything fell out very nicely just as you said. I've abstracted out the common code shared between `ElementwiseKernel` and `BatchwiseKernel` into an `_XwiseKernel` base class.\r\n\r\n### New (private) functions\r\n\r\nI've used this BatchwiseKernel to add `scipy.special._spfun_stats._poisson_binom_pmf` and `scipy.special._spfun_stats._poisson_binom_cdf` . Like their counterparts in SciPy, I've made these functions private for now. The plan is for them to be used internally in `scipy.stats` and `cupyx.scipy.stats`. If they are ever added to SciPy's public API, they should be added to the public API over hear as well.\r\n\r\nSince these `xsf` kernels expect template args that behave like `mdspan`s, not `CArray`, I wrote an adapter that makes `mdspan` views of `CArrays` and added it to `xsf`. This is the `xsf::as_mdspan` from the above example. \r\n\r\n#### Disclaimer:\r\n~Currently this adapter depends on `mdspan` being available in `libcu++`,~ ~and from here https://nvidia.github.io/cccl/unstable/libcudacxx/extended_api/mdspan.html~ ~it seems that this is only the case for CUDA toolkit >= 13.0. This branch works for me locally because I have CUDA toolkit 13.0 installed,~  ~but some more work will be needed to make this work for CUDA toolkit 12.~ ~Instead of relying on `mdspan` from `libcu++`, it seems it should also be possible to use `xsf`'s vendored copy  `kokkos/mdspan` by making only a small number of modifications.~ ~I could also add a hand-rolled  swap in replacement for an `mdspan` view of a `CArray`~\r\n\r\nPlease disregard the above nonsense. I was basing it off of what I read here, https://nvidia.github.io/cccl/unstable/libcudacxx/extended_api/mdspan.html, but forgot that CuPy comes bundled with headers from cccl, so I think the users CUDA toolkit shouldn't matter. I haven't downgraded on my machine to confirm yet though.\r\n### Performance\r\n\r\nUsing the  benchmark script from the details section below (which was adapted from here (https://github.com/scipy/scipy/pull/24894)\r\n\r\n<details>\r\nimport numpy as np\r\nimport timeit\r\nimport cupy as cp\r\nfrom cupyx.scipy.special._spfun_stats import _poisson_binom_pmf, _poisson_binom_cdf\r\n\r\n\r\ndef run_benchmarks():\r\n    # Parameters from ASV\r\n    size_p_list = [10, 100]\r\n    batch_shapes = [(), (10,), (100,), (1000,), (10000,), (100000,)]\r\n    \r\n    rng = np.random.default_rng(12345678)\r\n    results = {\"PMF\": [], \"CDF\": []}\r\n\r\n    print(\"Running benchmarks...\")\r\n\r\n    for size_p in size_p_list:\r\n        for batch_shape in batch_shapes:\r\n            shape = batch_shape + (size_p,)\r\n            p_host = rng.uniform(size=shape)\r\n            k_host = rng.integers(0, size_p, size=(size_p,) + batch_shape)\r\n            p = cp.asarray(p_host, dtype=cp.float32)\r\n            k = cp.asarray(k_host)\r\n\r\n            n_iter = 1000 if np.prod(batch_shape) < 100 else 100\r\n            \r\n            for method in [\"PMF\", \"CDF\"]:\r\n                func = _poisson_binom_pmf if method == \"PMF\" else _poisson_binom_cdf\r\n\r\n                def sync_run():\r\n                    func(k, p)\r\n                    cp.cuda.Stream.null.synchronize()\r\n\r\n                sync_run()\r\n                \r\n                # --- 1. Measure Timing ---\r\n                timer = timeit.Timer(sync_run)\r\n\r\n                best_time = min(timer.repeat(repeat=3, number=n_iter)) / n_iter\r\n                \r\n                results[method].append({\r\n                    \"size_p\": size_p,\r\n                    \"batch\": str(batch_shape),\r\n                    \"time\": best_time * 1e6,  # μs\r\n                })\r\n\r\n    # Print grouped tables\r\n    for method in [\"PMF\", \"CDF\"]:\r\n        print(f\"\\n### {method} Results\")\r\n        # Added Memory column to header\r\n        header = f\"| {'size_p':<8} | {'batch_shape':<15} | {'Time (μs)':<12} |\"\r\n        sep = \"|-\" + \"-\"*8 + \"-|-\" + \"-\"*15 + \"-|-\" + \"-\"*12 + \"-|-\"\r\n        print(header)\r\n        print(sep)\r\n        \r\n        for entry in results[method]:\r\n            print(f\"| {entry['size_p']:<8} | {entry['batch']:<15} | {entry['time']:<12.3f} |\")\r\n\r\nif __name__ == \"__main__\":\r\n    run_benchmarks()\r\n</details>\r\n\r\nI observed the following results when using the new CuPy versions of the Poisson binom distribution functions using my workstation with an RTX A4000 GPU.\r\n\r\n##### PMF Results\r\n| size_p   | batch_shape     | Time (μs)    |\r\n|----------|-----------------|--------------|\r\n| 10       | ()              | 20.632       |\r\n| 10       | (10,)           | 21.520       |\r\n| 10       | (100,)          | 33.072       |\r\n| 10       | (1000,)         | 34.291       |\r\n| 10       | (10000,)        | 60.063       |\r\n| 10       | (100000,)       | 502.388      |\r\n| 100      | ()              | 259.886      |\r\n| 100      | (10,)           | 281.341      |\r\n| 100      | (100,)          | 1089.566     |\r\n| 100      | (1000,)         | 1403.064     |\r\n| 100      | (10000,)        | 5943.927     |\r\n| 100      | (100000,)       | 344125.726   |\r\n\r\n##### CDF Results\r\n| size_p   | batch_shape     | Time (μs)    |\r\n|----------|-----------------|--------------|\r\n| 10       | ()              | 20.656       |\r\n| 10       | (10,)           | 21.434       |\r\n| 10       | (100,)          | 34.231       |\r\n| 10       | (1000,)         | 30.837       |\r\n| 10       | (10000,)        | 63.387       |\r\n| 10       | (100000,)       | 549.062      |\r\n| 100      | ()              | 269.281      |\r\n| 100      | (10,)           | 291.093      |\r\n| 100      | (100,)          | 1099.431     |\r\n| 100      | (1000,)         | 1420.290     |\r\n| 100      | (10000,)        | 6083.359     |\r\n| 100      | (100000,)       | 344454.570   |\r\n\r\nThe corresponding results when using SciPy's versions of these functions are:\r\n\r\n#### PMF Results\r\n| size_p   | batch_shape     | Time (μs)    |\r\n|----------|-----------------|--------------|\r\n| 10       | ()              | 1.433        |\r\n| 10       | (10,)           | 10.262       |\r\n| 10       | (100,)          | 24.994       |\r\n| 10       | (1000,)         | 174.022      |\r\n| 10       | (10000,)        | 1666.020     |\r\n| 10       | (100000,)       | 16570.239    |\r\n| 100      | ()              | 13.215       |\r\n| 100      | (10,)           | 132.173      |\r\n| 100      | (100,)          | 1244.533     |\r\n| 100      | (1000,)         | 12384.710    |\r\n| 100      | (10000,)        | 124430.284   |\r\n| 100      | (100000,)       | 1250720.444  |\r\n\r\n#### CDF Results\r\n| size_p   | batch_shape     | Time (μs)    |\r\n|----------|-----------------|--------------|\r\n| 10       | ()              | 1.036        |\r\n| 10       | (10,)           | 10.146       |\r\n| 10       | (100,)          | 26.645       |\r\n| 10       | (1000,)         | 187.421      |\r\n| 10       | (10000,)        | 1801.604     |\r\n| 10       | (100000,)       | 17934.534    |\r\n| 100      | ()              | 13.359       |\r\n| 100      | (10,)           | 133.732      |\r\n| 100      | (100,)          | 1259.384     |\r\n| 100      | (1000,)         | 12536.783    |\r\n| 100      | (10000,)        | 125826.029   |\r\n| 100      | (100000,)       | 1265985.884  |\r\n\r\nup to a 30x speedup for the `size_p = 10`, `batch_shape=(100000,)` case. Not bad.\r\n\r\n### Tests\r\n\r\nI've added tests for `_poisson_binom_pmf` and `_poisson_binom_cdf`. Since these functions were just added for SciPy 1.18, which has a planned release date of June 18th, .I set `@testing.with_requires` like this `@testing.with_requires('scipy>=1.18.0-dev')` and found this does make the tests run when I have SciPy installed off of the dev branch. I will update this to `@testing.with_requires('scipy>=1.18.0)`.\r\n\r\n\r\n### Current status\r\n\r\nI'm marking this as draft ~due to it currently only supporting CUDA toolkit >=13.0, and~ because it depends on changes which have not landed in `xsf` yet (the `xsf` submodule is pinned to a feature branch on my `xsf` fork here). \r\n\r\nI'd appreciate feedback on the design and implementation. Does anyone have thoughts on whether this should be made public, or left as a private internal tool for implementing gufuncs from SciPy?\r\n\r\n\r\n\r\n\r\n","reactions":{"url":"https://api.github.com/repos/cupy/cupy/issues/9959/reactions","total_count":1,"+1":0,"-1":0,"laugh":0,"hooray":0,"confused":0,"heart":0,"rocket":1,"eyes":0},"timeline_url":"https://api.github.com/repos/cupy/cupy/issues/9959/timeline","performed_via_github_app":null,"state_reason":null},"comment":{"url":"https://api.github.com/repos/cupy/cupy/issues/comments/6084663318","html_url":"https://github.com/cupy/cupy/pull/9959#issuecomment-6084663318","issue_url":"https://api.github.com/repos/cupy/cupy/issues/9959","id":6084663318,"node_id":"IC_kwDOBFKgkM8AAAABaqyYFg","user":{"login":"steppi","id":1953382,"node_id":"MDQ6VXNlcjE5NTMzODI=","avatar_url":"https://avatars.githubusercontent.com/u/1953382?v=4","gravatar_id":"","url":"https://api.github.com/users/steppi","html_url":"https://github.com/steppi","followers_url":"https://api.github.com/users/steppi/followers","following_url":"https://api.github.com/users/steppi/following{/other_user}","gists_url":"https://api.github.com/users/steppi/gists{/gist_id}","starred_url":"https://api.github.com/users/steppi/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/steppi/subscriptions","organizations_url":"https://api.github.com/users/steppi/orgs","repos_url":"https://api.github.com/users/steppi/repos","events_url":"https://api.github.com/users/steppi/events{/privacy}","received_events_url":"https://api.github.com/users/steppi/received_events","type":"User","user_view_type":"public","site_admin":false},"created_at":"2026-10-09T16:09:40Z","updated_at":"2026-10-09T16:09:40Z","body":"Thanks to @sbaldu's great work (https://github.com/scipy/xsf/pull/294) we have a portable `xsf::relaxed_layout` now that can handle negative strides. I've moved `as_mdspan` for `CArray`s back to `xsf` and once again made it a friend of `CArray`. This `as_mdspan` uses `xsf::relaxed_layout`. I've also updated this PR to only add the public `poisson_binom_cdf` function from SciPy and have added a test that shows negative strides work correctly.\r\n\r\nIt looks like there are some pretty serious merge conflicts now following the refactor in https://github.com/cupy/cupy/pull/10337. I'll take a look and see if I can resolve them quickly today. There's also still a matter of updating the docs.","reactions":{"url":"https://api.github.com/repos/cupy/cupy/issues/comments/6084663318/reactions","total_count":2,"+1":0,"-1":0,"laugh":0,"hooray":1,"confused":0,"heart":0,"rocket":1,"eyes":0},"performed_via_github_app":null,"minimized":null}},"public":true,"created_at":"2026-10-09T16:09:40Z","org":{"id":23187665,"login":"cupy","gravatar_id":"","url":"https://api.github.com/orgs/cupy","avatar_url":"https://avatars.githubusercontent.com/u/23187665?"}},{"id":"23492183932","type":"CreateEvent","actor":{"id":14386,"login":"martinus","display_login":"martinus","gravatar_id":"","url":"https://api.github.com/users/martinus","avatar_url":"https://avatars.githubusercontent.com/u/14386?"},"repo":{"id":1380897551,"name":"martinus/quetschn","url":"https://api.github.com/repos/martinus/quetschn"},"payload":{"ref":"zram-phone-noise","ref_type":"branch","full_ref":"refs/heads/zram-phone-noise","master_branch":"main","description":"Fast compression for 4 KiB memory pages, built for Linux zram","pusher_type":"user"},"public":true,"created_at":"2026-10-08T13:06:19Z"},{"id":"16835663605","type":"PullRequestReviewCommentEvent","actor":{"id":1953382,"login":"steppi","display_login":"steppi","gravatar_id":"","url":"https://api.github.com/users/steppi","avatar_url":"https://avatars.githubusercontent.com/u/1953382?"},"repo":{"id":1365640351,"name":"sbaldu/xsf","url":"https://api.github.com/repos/sbaldu/xsf"},"payload":{"action":"created","comment":{"url":"https://api.github.com/repos/sbaldu/xsf/pulls/comments/4230254332","pull_request_review_id":5470224060,"id":4230254332,"node_id":"PRRC_kwDOUWYEn878JI78","diff_hunk":"@@ -63,8 +63,26 @@ struct relaxed_layout {\n             }\n         }","path":"include/xsf/relaxed_layout.h","commit_id":"c706e003526d906c714e86b9ecb4697e15ff3364","original_commit_id":"c706e003526d906c714e86b9ecb4697e15ff3364","user":{"login":"steppi","id":1953382,"node_id":"MDQ6VXNlcjE5NTMzODI=","avatar_url":"https://avatars.githubusercontent.com/u/1953382?v=4","gravatar_id":"","url":"https://api.github.com/users/steppi","html_url":"https://github.com/steppi","followers_url":"https://api.github.com/users/steppi/followers","following_url":"https://api.github.com/users/steppi/following{/other_user}","gists_url":"https://api.github.com/users/steppi/gists{/gist_id}","starred_url":"https://api.github.com/users/steppi/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/steppi/subscriptions","organizations_url":"https://api.github.com/users/steppi/orgs","repos_url":"https://api.github.com/users/steppi/repos","events_url":"https://api.github.com/users/steppi/events{/privacy}","received_events_url":"https://api.github.com/users/steppi/received_events","type":"User","user_view_type":"public","site_admin":false},"body":"Oops. The other way around actually.  `m_offset` is already initialized to 0 on what is now line 34 above, so I didn't need to include it in the constructor below. We don't use this constructor yet, but  it seems worth having.","created_at":"2026-10-09T12:49:01Z","updated_at":"2026-10-09T12:49:02Z","html_url":"https://github.com/sbaldu/xsf/pull/1#discussion_r4230254332","pull_request_url":"https://api.github.com/repos/sbaldu/xsf/pulls/1","_links":{"self":{"href":"https://api.github.com/repos/sbaldu/xsf/pulls/comments/4230254332"},"html":{"href":"https://github.com/sbaldu/xsf/pull/1#discussion_r4230254332"},"pull_request":{"href":"https://api.github.com/repos/sbaldu/xsf/pulls/1"}},"reactions":{"url":"https://api.github.com/repos/sbaldu/xsf/pulls/comments/4230254332/reactions","total_count":0,"+1":0,"-1":0,"laugh":0,"hooray":0,"confused":0,"heart":0,"rocket":0,"eyes":0},"in_reply_to_id":4228213662,"original_position":2,"position":1,"subject_type":"line"},"pull_request":{"url":"https://api.github.com/repos/sbaldu/xsf/pulls/1","id":4794763519,"number":1,"head":{"ref":"relaxed-mdspan-layout-tweaks","sha":"c706e003526d906c714e86b9ecb4697e15ff3364","repo":{"id":866581919,"url":"https://api.github.com/repos/steppi/xsf","name":"xsf"}},"base":{"ref":"feature/relaxed-mdspan-layout","sha":"e4334dc6bf4ea3d3b8a0797d62922833bf794b35","repo":{"id":1365640351,"url":"https://api.github.com/repos/sbaldu/xsf","name":"xsf"}}}},"public":true,"created_at":"2026-10-09T12:49:01Z"},{"id":"16834593795","type":"PullRequestReviewCommentEvent","actor":{"id":6570539,"login":"mdhaber","display_login":"mdhaber","gravatar_id":"","url":"https://api.github.com/users/mdhaber","avatar_url":"https://avatars.githubusercontent.com/u/6570539?"},"repo":{"id":1460385,"name":"scipy/scipy","url":"https://api.github.com/repos/scipy/scipy"},"payload":{"action":"created","comment":{"url":"https://api.github.com/repos/scipy/scipy/pulls/comments/4227296907","pull_request_review_id":5466542998,"id":4227296907,"node_id":"PRRC_kwDOABZIoc77926L","diff_hunk":"","path":"scipy/linalg/_basic.py","commit_id":"29c3eb318c5e59711659fc28cf6fa0327a16718b","original_commit_id":"29c3eb318c5e59711659fc28cf6fa0327a16718b","user":{"login":"mdhaber","id":6570539,"node_id":"MDQ6VXNlcjY1NzA1Mzk=","avatar_url":"https://avatars.githubusercontent.com/u/6570539?v=4","gravatar_id":"","url":"https://api.github.com/users/mdhaber","html_url":"https://github.com/mdhaber","followers_url":"https://api.github.com/users/mdhaber/followers","following_url":"https://api.github.com/users/mdhaber/following{/other_user}","gists_url":"https://api.github.com/users/mdhaber/gists{/gist_id}","starred_url":"https://api.github.com/users/mdhaber/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/mdhaber/subscriptions","organizations_url":"https://api.github.com/users/mdhaber/orgs","repos_url":"https://api.github.com/users/mdhaber/repos","events_url":"https://api.github.com/users/mdhaber/events{/privacy}","received_events_url":"https://api.github.com/users/mdhaber/received_events","type":"User","user_view_type":"public","site_admin":false},"body":"I haven't thought it through 100%, but you could also write a wrapper in `test_batch.py` that unpacks the tuple and returns the separate arguments? Then you test *that* function. I've done that sort of thing in `test_axis_nan_policy`.","created_at":"2026-10-09T06:29:54Z","updated_at":"2026-10-09T06:30:28Z","html_url":"https://github.com/scipy/scipy/pull/26103#discussion_r4227296907","pull_request_url":"https://api.github.com/repos/scipy/scipy/pulls/26103","_links":{"self":{"href":"https://api.github.com/repos/scipy/scipy/pulls/comments/4227296907"},"html":{"href":"https://github.com/scipy/scipy/pull/26103#discussion_r4227296907"},"pull_request":{"href":"https://api.github.com/repos/scipy/scipy/pulls/26103"}},"reactions":{"url":"https://api.github.com/repos/scipy/scipy/pulls/comments/4227296907/reactions","total_count":0,"+1":0,"-1":0,"laugh":0,"hooray":0,"confused":0,"heart":0,"rocket":0,"eyes":0},"in_reply_to_id":4065512410,"original_position":1,"position":1,"subject_type":"line"},"pull_request":{"url":"https://api.github.com/repos/scipy/scipy/pulls/26103","id":4449539892,"number":26103,"head":{"ref":"linalg_dtypes","sha":"b9e90a066cb6ea2cd4f5ec3b674586bb4ccd1313","repo":{"id":1109418570,"url":"https://api.github.com/repos/BasilLiekens/scipy","name":"scipy"}},"base":{"ref":"main","sha":"3526d4e3cd4a905ab244327e840cb8fff8623ba6","repo":{"id":1460385,"url":"https://api.github.com/repos/scipy/scipy","name":"scipy"}}}},"public":true,"created_at":"2026-10-09T06:29:54Z","org":{"id":288277,"login":"scipy","gravatar_id":"","url":"https://api.github.com/orgs/scipy","avatar_url":"https://avatars.githubusercontent.com/u/288277?"}},{"id":"23489669005","type":"DeleteEvent","actor":{"id":14386,"login":"martinus","display_login":"martinus","gravatar_id":"","url":"https://api.github.com/users/martinus","avatar_url":"https://avatars.githubusercontent.com/u/14386?"},"repo":{"id":1380897551,"name":"martinus/quetschn","url":"https://api.github.com/repos/martinus/quetschn"},"payload":{"ref":"seqlz-short-offset","ref_type":"branch","full_ref":"refs/heads/seqlz-short-offset","pusher_type":"user"},"public":true,"created_at":"2026-10-09T07:50:32Z"},{"id":"23489669106","type":"DeleteEvent","actor":{"id":14386,"login":"martinus","display_login":"martinus","gravatar_id":"","url":"https://api.github.com/users/martinus","avatar_url":"https://avatars.githubusercontent.com/u/14386?"},"repo":{"id":1380897551,"name":"martinus/quetschn","url":"https://api.github.com/repos/martinus/quetschn"},"payload":{"ref":"seqlz-strict-ends","ref_type":"branch","full_ref":"refs/heads/seqlz-strict-ends","pusher_type":"user"},"public":true,"created_at":"2026-10-09T07:50:32Z"},{"id":"23489668909","type":"DeleteEvent","actor":{"id":14386,"login":"martinus","display_login":"martinus","gravatar_id":"","url":"https://api.github.com/users/martinus","avatar_url":"https://avatars.githubusercontent.com/u/14386?"},"repo":{"id":1380897551,"name":"martinus/quetschn","url":"https://api.github.com/repos/martinus/quetschn"},"payload":{"ref":"seqlz-refill","ref_type":"branch","full_ref":"refs/heads/seqlz-refill","pusher_type":"user"},"public":true,"created_at":"2026-10-09T07:50:32Z"},{"id":"23489668312","type":"DeleteEvent","actor":{"id":14386,"login":"martinus","display_login":"martinus","gravatar_id":"","url":"https://api.github.com/users/martinus","avatar_url":"https://avatars.githubusercontent.com/u/14386?"},"repo":{"id":1380897551,"name":"martinus/quetschn","url":"https://api.github.com/repos/martinus/quetschn"},"payload":{"ref":"seqlz-inline-macros","ref_type":"branch","full_ref":"refs/heads/seqlz-inline-macros","pusher_type":"user"},"public":true,"created_at":"2026-10-09T07:50:31Z"},{"id":"23489666208","type":"DeleteEvent","actor":{"id":14386,"login":"martinus","display_login":"martinus","gravatar_id":"","url":"https://api.github.com/users/martinus","avatar_url":"https://avatars.githubusercontent.com/u/14386?"},"repo":{"id":1380897551,"name":"martinus/quetschn","url":"https://api.github.com/repos/martinus/quetschn"},"payload":{"ref":"plan-qpace-r12","ref_type":"branch","full_ref":"refs/heads/plan-qpace-r12","pusher_type":"user"},"public":true,"created_at":"2026-10-09T07:50:29Z"},{"id":"23489664919","type":"DeleteEvent","actor":{"id":14386,"login":"martinus","display_login":"martinus","gravatar_id":"","url":"https://api.github.com/users/martinus","avatar_url":"https://avatars.githubusercontent.com/u/14386?"},"repo":{"id":1380897551,"name":"martinus/quetschn","url":"https://api.github.com/repos/martinus/quetschn"},"payload":{"ref":"docs/lit-xor","ref_type":"branch","full_ref":"refs/heads/docs/lit-xor","pusher_type":"user"},"public":true,"created_at":"2026-10-09T07:50:28Z"},{"id":"23489664049","type":"DeleteEvent","actor":{"id":14386,"login":"martinus","display_login":"martinus","gravatar_id":"","url":"https://api.github.com/users/martinus","avatar_url":"https://avatars.githubusercontent.com/u/14386?"},"repo":{"id":1380897551,"name":"martinus/quetschn","url":"https://api.github.com/repos/martinus/quetschn"},"payload":{"ref":"ci-spec-links","ref_type":"branch","full_ref":"refs/heads/ci-spec-links","pusher_type":"user"},"public":true,"created_at":"2026-10-09T07:50:27Z"},{"id":"16828002255","type":"PullRequestReviewEvent","actor":{"id":1953382,"login":"steppi","display_login":"steppi","gravatar_id":"","url":"https://api.github.com/users/steppi","avatar_url":"https://avatars.githubusercontent.com/u/1953382?"},"repo":{"id":1365640351,"name":"sbaldu/xsf","url":"https://api.github.com/repos/sbaldu/xsf"},"payload":{"review":{"id":5470224060,"node_id":"PRR_kwDOUWYEn88AAAABRgz-vA","user":{"login":"steppi","id":1953382,"node_id":"MDQ6VXNlcjE5NTMzODI=","avatar_url":"https://avatars.githubusercontent.com/u/1953382?v=4","gravatar_id":"","url":"https://api.github.com/users/steppi","html_url":"https://github.com/steppi","followers_url":"https://api.github.com/users/steppi/followers","following_url":"https://api.github.com/users/steppi/following{/other_user}","gists_url":"https://api.github.com/users/steppi/gists{/gist_id}","starred_url":"https://api.github.com/users/steppi/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/steppi/subscriptions","organizations_url":"https://api.github.com/users/steppi/orgs","repos_url":"https://api.github.com/users/steppi/repos","events_url":"https://api.github.com/users/steppi/events{/privacy}","received_events_url":"https://api.github.com/users/steppi/received_events","type":"User","user_view_type":"public","site_admin":false},"body":null,"commit_id":"c706e003526d906c714e86b9ecb4697e15ff3364","state":"commented","html_url":"https://github.com/sbaldu/xsf/pull/1#pullrequestreview-5470224060","pull_request_url":"https://api.github.com/repos/sbaldu/xsf/pulls/1","_links":{"html":{"href":"https://github.com/sbaldu/xsf/pull/1#pullrequestreview-5470224060"},"pull_request":{"href":"https://api.github.com/repos/sbaldu/xsf/pulls/1"}},"submitted_at":"2026-10-09T12:49:01Z","updated_at":"2026-10-09T12:49:01Z"},"pull_request":{"url":"https://api.github.com/repos/sbaldu/xsf/pulls/1","id":4794763519,"number":1,"head":{"ref":"relaxed-mdspan-layout-tweaks","sha":"c706e003526d906c714e86b9ecb4697e15ff3364","repo":{"id":866581919,"url":"https://api.github.com/repos/steppi/xsf","name":"xsf"}},"base":{"ref":"feature/relaxed-mdspan-layout","sha":"e4334dc6bf4ea3d3b8a0797d62922833bf794b35","repo":{"id":1365640351,"url":"https://api.github.com/repos/sbaldu/xsf","name":"xsf"}}},"action":"created"},"public":true,"created_at":"2026-10-09T14:53:49Z"},{"id":"16827854036","type":"PullRequestReviewEvent","actor":{"id":1953382,"login":"steppi","display_login":"steppi","gravatar_id":"","url":"https://api.github.com/users/steppi","avatar_url":"https://avatars.githubusercontent.com/u/1953382?"},"repo":{"id":832302800,"name":"scipy/xsf","url":"https://api.github.com/repos/scipy/xsf"},"payload":{"review":{"id":5470592098,"node_id":"PRR_kwDOMZvu0M8AAAABRhKcYg","user":{"login":"steppi","id":1953382,"node_id":"MDQ6VXNlcjE5NTMzODI=","avatar_url":"https://avatars.githubusercontent.com/u/1953382?v=4","gravatar_id":"","url":"https://api.github.com/users/steppi","html_url":"https://github.com/steppi","followers_url":"https://api.github.com/users/steppi/followers","following_url":"https://api.github.com/users/steppi/following{/other_user}","gists_url":"https://api.github.com/users/steppi/gists{/gist_id}","starred_url":"https://api.github.com/users/steppi/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/steppi/subscriptions","organizations_url":"https://api.github.com/users/steppi/orgs","repos_url":"https://api.github.com/users/steppi/repos","events_url":"https://api.github.com/users/steppi/events{/privacy}","received_events_url":"https://api.github.com/users/steppi/received_events","type":"User","user_view_type":"public","site_admin":false},"body":null,"commit_id":"0ce5b61a503ce549e90f77d64fa7f90518f755eb","state":"commented","html_url":"https://github.com/scipy/xsf/pull/294#pullrequestreview-5470592098","pull_request_url":"https://api.github.com/repos/scipy/xsf/pulls/294","_links":{"html":{"href":"https://github.com/scipy/xsf/pull/294#pullrequestreview-5470592098"},"pull_request":{"href":"https://api.github.com/repos/scipy/xsf/pulls/294"}},"submitted_at":"2026-10-09T13:21:26Z","updated_at":"2026-10-09T13:21:26Z"},"pull_request":{"url":"https://api.github.com/repos/scipy/xsf/pulls/294","id":4675777534,"number":294,"head":{"ref":"feature/relaxed-mdspan-layout","sha":"80f07575fb0ceb710cfeb6be1466070e448ca963","repo":{"id":1365640351,"url":"https://api.github.com/repos/sbaldu/xsf","name":"xsf"}},"base":{"ref":"main","sha":"471c1b5a1e334a2773edaae9dbb215205368d466","repo":{"id":832302800,"url":"https://api.github.com/repos/scipy/xsf","name":"xsf"}}},"action":"created"},"public":true,"created_at":"2026-10-09T14:52:40Z","org":{"id":288277,"login":"scipy","gravatar_id":"","url":"https://api.github.com/orgs/scipy","avatar_url":"https://avatars.githubusercontent.com/u/288277?"}},{"id":"16826362911","type":"PullRequestEvent","actor":{"id":14386,"login":"martinus","display_login":"martinus","gravatar_id":"","url":"https://api.github.com/users/martinus","avatar_url":"https://avatars.githubusercontent.com/u/14386?"},"repo":{"id":1380897551,"name":"martinus/quetschn","url":"https://api.github.com/repos/martinus/quetschn"},"payload":{"action":"merged","number":147,"pull_request":{"url":"https://api.github.com/repos/martinus/quetschn/pulls/147","id":4800413264,"number":147,"head":{"ref":"kernel-copy-review","sha":"0fb90e85887be19c911c51ad256ada00ba7d7ced","repo":{"id":1380897551,"url":"https://api.github.com/repos/martinus/quetschn","name":"quetschn"}},"base":{"ref":"main","sha":"33fce5bcf1b1a135be757709711f962843cc73fc","repo":{"id":1380897551,"url":"https://api.github.com/repos/martinus/quetschn","name":"quetschn"}}}},"public":true,"created_at":"2026-10-09T13:12:45Z"},{"id":"16826340864","type":"PullRequestEvent","actor":{"id":14386,"login":"martinus","display_login":"martinus","gravatar_id":"","url":"https://api.github.com/users/martinus","avatar_url":"https://avatars.githubusercontent.com/u/14386?"},"repo":{"id":1380897551,"name":"martinus/quetschn","url":"https://api.github.com/repos/martinus/quetschn"},"payload":{"action":"opened","number":148,"pull_request":{"url":"https://api.github.com/repos/martinus/quetschn/pulls/148","id":4801868057,"number":148,"head":{"ref":"docs-question-sent","sha":"25f656092bd5e3ac624326f353e977a860ffa86e","repo":{"id":1380897551,"url":"https://api.github.com/repos/martinus/quetschn","name":"quetschn"}},"base":{"ref":"main","sha":"c469a2329205791b56d7bad41c56cef5734e5163","repo":{"id":1380897551,"url":"https://api.github.com/repos/martinus/quetschn","name":"quetschn"}}}},"public":true,"created_at":"2026-10-09T14:34:58Z"},{"id":"23485361389","type":"CreateEvent","actor":{"id":14386,"login":"martinus","display_login":"martinus","gravatar_id":"","url":"https://api.github.com/users/martinus","avatar_url":"https://avatars.githubusercontent.com/u/14386?"},"repo":{"id":506872145,"name":"martinus/unordered_dense","url":"https://api.github.com/repos/martinus/unordered_dense"},"payload":{"ref":"release-5.3.2","ref_type":"branch","full_ref":"refs/heads/release-5.3.2","master_branch":"main","description":"A fast & densely stored hashmap and hashset","pusher_type":"user"},"public":true,"created_at":"2026-10-09T09:34:25Z"},{"id":"23484930651","type":"PushEvent","actor":{"id":14386,"login":"martinus","display_login":"martinus","gravatar_id":"","url":"https://api.github.com/users/martinus","avatar_url":"https://avatars.githubusercontent.com/u/14386?"},"repo":{"id":1380897551,"name":"martinus/quetschn","url":"https://api.github.com/repos/martinus/quetschn"},"payload":{"repository_id":1380897551,"push_id":45807490417,"ref":"refs/heads/plan-qpace-r12","head":"f4041ec4a2683a9e24eafda3aab765179e4af6c1","before":"c9702f950971fa222565bb942075d6001e90cf38"},"public":true,"created_at":"2026-10-08T04:23:02Z"},{"id":"23483396908","type":"DeleteEvent","actor":{"id":14386,"login":"martinus","display_login":"martinus","gravatar_id":"","url":"https://api.github.com/users/martinus","avatar_url":"https://avatars.githubusercontent.com/u/14386?"},"repo":{"id":1380897551,"name":"martinus/quetschn","url":"https://api.github.com/repos/martinus/quetschn"},"payload":{"ref":"seqlz-lit-from-table","ref_type":"branch","full_ref":"refs/heads/seqlz-lit-from-table","pusher_type":"user"},"public":true,"created_at":"2026-10-09T09:25:05Z"},{"id":"23482266795","type":"CreateEvent","actor":{"id":14386,"login":"martinus","display_login":"martinus","gravatar_id":"","url":"https://api.github.com/users/martinus","avatar_url":"https://avatars.githubusercontent.com/u/14386?"},"repo":{"id":1380897551,"name":"martinus/quetschn","url":"https://api.github.com/repos/martinus/quetschn"},"payload":{"ref":"docs-phone-129","ref_type":"branch","full_ref":"refs/heads/docs-phone-129","master_branch":"main","description":"Fast compression for 4 KiB memory pages, built for Linux zram","pusher_type":"user"},"public":true,"created_at":"2026-10-08T11:57:27Z"},{"id":"23480767698","type":"PushEvent","actor":{"id":1953382,"login":"steppi","display_login":"steppi","gravatar_id":"","url":"https://api.github.com/users/steppi","avatar_url":"https://avatars.githubusercontent.com/u/1953382?"},"repo":{"id":832302800,"name":"scipy/xsf","url":"https://api.github.com/repos/scipy/xsf"},"payload":{"repository_id":832302800,"push_id":45965405347,"ref":"refs/heads/main","head":"ee78450927ad885eaad5b31469eaec2c9cf35f3f","before":"471c1b5a1e334a2773edaae9dbb215205368d466"},"public":true,"created_at":"2026-10-09T13:31:53Z","org":{"id":288277,"login":"scipy","gravatar_id":"","url":"https://api.github.com/orgs/scipy","avatar_url":"https://avatars.githubusercontent.com/u/288277?"}},{"id":"23479372676","type":"DeleteEvent","actor":{"id":14386,"login":"martinus","display_login":"martinus","gravatar_id":"","url":"https://api.github.com/users/martinus","avatar_url":"https://avatars.githubusercontent.com/u/14386?"},"repo":{"id":1380897551,"name":"martinus/quetschn","url":"https://api.github.com/repos/martinus/quetschn"},"payload":{"ref":"zstd-levels","ref_type":"branch","full_ref":"refs/heads/zstd-levels","pusher_type":"user"},"public":true,"created_at":"2026-10-09T07:50:33Z"},{"id":"23479370241","type":"DeleteEvent","actor":{"id":14386,"login":"martinus","display_login":"martinus","gravatar_id":"","url":"https://api.github.com/users/martinus","avatar_url":"https://avatars.githubusercontent.com/u/14386?"},"repo":{"id":1380897551,"name":"martinus/quetschn","url":"https://api.github.com/repos/martinus/quetschn"},"payload":{"ref":"seqlz-doc-codecs","ref_type":"branch","full_ref":"refs/heads/seqlz-doc-codecs","pusher_type":"user"},"public":true,"created_at":"2026-10-09T07:50:31Z"},{"id":"23479366480","type":"DeleteEvent","actor":{"id":14386,"login":"martinus","display_login":"martinus","gravatar_id":"","url":"https://api.github.com/users/martinus","avatar_url":"https://avatars.githubusercontent.com/u/14386?"},"repo":{"id":1380897551,"name":"martinus/quetschn","url":"https://api.github.com/repos/martinus/quetschn"},"payload":{"ref":"seqlz-constants","ref_type":"branch","full_ref":"refs/heads/seqlz-constants","pusher_type":"user"},"public":true,"created_at":"2026-10-09T07:50:30Z"},{"id":"23479365866","type":"DeleteEvent","actor":{"id":14386,"login":"martinus","display_login":"martinus","gravatar_id":"","url":"https://api.github.com/users/martinus","avatar_url":"https://avatars.githubusercontent.com/u/14386?"},"repo":{"id":1380897551,"name":"martinus/quetschn","url":"https://api.github.com/repos/martinus/quetschn"},"payload":{"ref":"plan-zram-rw-split","ref_type":"branch","full_ref":"refs/heads/plan-zram-rw-split","pusher_type":"user"},"public":true,"created_at":"2026-10-09T07:50:29Z"},{"id":"16822012272","type":"IssuesEvent","actor":{"id":14386,"login":"martinus","display_login":"martinus","gravatar_id":"","url":"https://api.github.com/users/martinus","avatar_url":"https://avatars.githubusercontent.com/u/14386?"},"repo":{"id":1380897551,"name":"martinus/quetschn","url":"https://api.github.com/repos/martinus/quetschn"},"payload":{"action":"closed","issue":{"url":"https://api.github.com/repos/martinus/quetschn/issues/127","repository_url":"https://api.github.com/repos/martinus/quetschn","labels_url":"https://api.github.com/repos/martinus/quetschn/issues/127/labels{/name}","comments_url":"https://api.github.com/repos/martinus/quetschn/issues/127/comments","events_url":"https://api.github.com/repos/martinus/quetschn/issues/127/events","html_url":"https://github.com/martinus/quetschn/issues/127","id":5756540278,"node_id":"I_kwDOUk7TD88AAAABVx3Vdg","number":127,"title":"seqlz: the kernel copy without 4.14, Fedora and VM rationale, and shorter functions","user":{"login":"martinus","id":14386,"node_id":"MDQ6VXNlcjE0Mzg2","avatar_url":"https://avatars.githubusercontent.com/u/14386?v=4","gravatar_id":"","url":"https://api.github.com/users/martinus","html_url":"https://github.com/martinus","followers_url":"https://api.github.com/users/martinus/followers","following_url":"https://api.github.com/users/martinus/following{/other_user}","gists_url":"https://api.github.com/users/martinus/gists{/gist_id}","starred_url":"https://api.github.com/users/martinus/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/martinus/subscriptions","organizations_url":"https://api.github.com/users/martinus/orgs","repos_url":"https://api.github.com/users/martinus/repos","events_url":"https://api.github.com/users/martinus/events{/privacy}","received_events_url":"https://api.github.com/users/martinus/received_events","type":"User","user_view_type":"public","site_admin":false},"labels":[],"state":"closed","locked":false,"assignees":[],"milestone":null,"comments":2,"created_at":"2026-10-08T05:38:26Z","updated_at":"2026-10-09T13:12:46Z","closed_at":"2026-10-09T13:12:46Z","assignee":null,"active_lock_reason":null,"sub_issues_summary":{"total":0,"completed":0,"percent_completed":0},"issue_dependencies_summary":{"blocked_by":0,"total_blocked_by":0,"blocking":0,"total_blocking":0},"body":"## What a reviewer will stumble over in the kernel copy\n\nNone of this is a bug. It is what makes a reviewer stop and ask.\n\n**Comments that justify code with an old kernel, a distribution or our VM.** A few examples:\n- `src/page_lz.h`: \"clang did not inline the kernel's memcpy() in the Mi 9T's 4.14\";\n- `src/page_lz.h`: register allocation \"in the kernel VM\";\n- `src/seqlz.c`: \"Fedora builds its kernel with -fsanitize=bounds-strict\";\n- `src/seqlz.c`: \"the phone's 4.14 kernel lacks some of the kernel's macros\".\n\nMainline doesn't care about 4.14 or Fedora's config, and timings in comments go stale. In `src/` they are useful. `port.py` should rewrite them for the kernel copy to the reason in general terms, e.g. \"so that the copy is always inlined, also with FORTIFY_SOURCE\".\n\n**Compiler builtins where the kernel has helpers.** `__builtin_ctzll`, `__builtin_clz`, `__builtin_bswap64` and `__builtin_memcpy` were kept on purpose in #108: x86-64's `__ffs()`/`fls()` are inline asm and changed the machine code, and FORTIFY_SOURCE keeps `memcpy()` from being inlined on some kernels. So don't switch. But each one needs a comment that a reviewer accepts, in the kernel copy too.\n\n**Code shape:**\n- Long functions: `decode_page()` is about 260 lines, `code_literals()` about 185 and `decode_literals()` about 170. Still open from #108.\n- Two implementations of the copy of a short repeating pattern: a multiply in `seqlz.c`'s decoder, shifts in `page_lz.h`. One helper, if it costs nothing measurable.\n- `static const u8 page_lz_step_for[8]` is defined in a header.\n- The `.inc` files become `.h` in the kernel copy, and they define non-static objects (`seqlz_default_own`, `seqlz_lit_sets`). They should be `.c` files, or included fragments with another suffix.\n- `noinline __aligned(64)` on `decode_literals()`: say why, or drop the alignment if it measures the same.\n- The kernel-doc has no `Context:` lines.\n- The backend returns `-EOPNOTSUPP` for a level it doesn't support; `lz4`'s backend returns `-EINVAL`.\n\n## Acceptance\n\n- No 4.14, Mi 9T, Fedora or VM rationale left in the kernel copy.\n- The builtins explained in it.\n- No object defined in a `.h` file.\n- `Context:` in the kernel-doc of the exported functions.\n- Code changes, the pattern helper and any split of the long functions, measured in the kernel VM with `MODE=swap` and on the phone as #108 did, and kept only where they cost nothing. Recorded in `docs/explored-designs.md`.\n- `tools/kernel-port/check.sh` passes.\n\nRelated: #108. Found in a review of `src/` and `docs/format.md` as the zram maintainers would read them.\n","reactions":{"url":"https://api.github.com/repos/martinus/quetschn/issues/127/reactions","total_count":0,"+1":0,"-1":0,"laugh":0,"hooray":0,"confused":0,"heart":0,"rocket":0,"eyes":0},"timeline_url":"https://api.github.com/repos/martinus/quetschn/issues/127/timeline","performed_via_github_app":null,"state_reason":"completed","pinned_comment":null}},"public":true,"created_at":"2026-10-09T13:13:16Z"},{"id":"23479316438","type":"DeleteEvent","actor":{"id":14386,"login":"martinus","display_login":"martinus","gravatar_id":"","url":"https://api.github.com/users/martinus","avatar_url":"https://avatars.githubusercontent.com/u/14386?"},"repo":{"id":1380897551,"name":"martinus/quetschn","url":"https://api.github.com/repos/martinus/quetschn"},"payload":{"ref":"kernel-copy-review","ref_type":"branch","full_ref":"refs/heads/kernel-copy-review","pusher_type":"user"},"public":true,"created_at":"2026-10-09T13:12:52Z"}]