From a40297ca1785d62d8030f7c722e1d59c36bf568e Mon Sep 17 00:00:00 2001 From: jaysingh79 Date: Sat, 22 Aug 2026 21:23:49 +0530 Subject: [PATCH 1/2] Fix UnbalancedSinkhornTransport transform failing on nx.array_equal (closes #650) fit() now initializes the backend attribute nx before the check_params gate, matching the behavior of BaseTransport.fit. Previously, a fit call with missing parameters left self.nx as None, causing any subsequent transform() call to crash with: AttributeError: 'NoneType' object has no attribute 'array_equal' Adds a non-regression test covering both incomplete fit and a separate transform call. --- ot/da.py | 2 ++ test/test_da.py | 28 ++++++++++++++++++++++++++++ 2 files changed, 30 insertions(+) diff --git a/ot/da.py b/ot/da.py index 7b4ed7ba2..7e1a7491f 100644 --- a/ot/da.py +++ b/ot/da.py @@ -2287,6 +2287,8 @@ class label Returns self. """ + self._get_backend(Xs, ys, Xt, yt) + # check the necessary inputs parameters are here if check_params(Xs=Xs, Xt=Xt): super(UnbalancedSinkhornTransport, self).fit(Xs, ys, Xt, yt) diff --git a/test/test_da.py b/test/test_da.py index df224a247..0a89ebb79 100644 --- a/test/test_da.py +++ b/test/test_da.py @@ -483,6 +483,34 @@ def test_unbalanced_sinkhorn_transport_class(nx): assert len(otda.log_.keys()) != 0 +@pytest.skip_backend("jax") +@pytest.skip_backend("tf") +def test_unbalanced_sinkhorn_transport_nx_initialized(nx): + """non-regression test for issue #650 + + fit must always initialize the backend attribute nx, even when called + with missing parameters, so that transform fails on a clear error + instead of 'NoneType' object has no attribute 'array_equal' + """ + + ns = 50 + Xs, ys = make_data_classif("3gauss", ns) + Xt, yt = make_data_classif("3gauss2", ns) + + Xs, ys, Xt, yt = nx.from_numpy(Xs, ys, Xt, yt) + + # incomplete fit (Xt missing) still initializes the backend + otda = ot.da.UnbalancedSinkhornTransport() + otda.fit(Xs=Xs) + assert otda.nx is not None + + # complete fit followed by a separate transform call + otda = ot.da.UnbalancedSinkhornTransport() + otda.fit(Xs=Xs, Xt=Xt) + transp_Xs = otda.transform(Xs) + assert_equal(transp_Xs.shape, Xs.shape) + + @pytest.skip_backend("jax") @pytest.skip_backend("tf") def test_emd_transport_class(nx): From b203821d5966eec24c28f745360ef12c9b55f22b Mon Sep 17 00:00:00 2001 From: jaysingh79 Date: Thu, 27 Aug 2026 13:56:29 +0530 Subject: [PATCH 2/2] Add RELEASES entry for UnbalancedSinkhornTransport nx fix (PR #837, Issue #650) --- RELEASES.md | 1 + 1 file changed, 1 insertion(+) diff --git a/RELEASES.md b/RELEASES.md index 040b35af3..bca507e94 100644 --- a/RELEASES.md +++ b/RELEASES.md @@ -10,6 +10,7 @@ - Fix mean centering in `ot.dr.fda` and `ot.dr.wda`: `np.mean(X)` returned a scalar instead of the per-feature mean, so `proj` did not center the data as documented. In `ot.dr.fda` the same pattern in the class means made the between-class scatter matrix independent of which features separate the classes, and FDA returned a non-discriminant direction (PR #840) - `ot.dr.fda` and `ot.dr.wda` no longer modify the input array `X` in place (PR #840) +- Fix `UnbalancedSinkhornTransport` `transform` failing with `AttributeError: 'NoneType' object has no attribute 'array_equal'` when `fit` was called with missing parameters (PR #837, Issue #650) ## 0.9.7.post1