From cd8156c17685746de20cb78400f9affab5e482ad Mon Sep 17 00:00:00 2001 From: lenamonj Date: Wed, 29 Jul 2026 09:50:32 -0400 Subject: [PATCH] [BUG] fix HRPOpt.optimize raising AttributeError under scipy >= 1.18 --- pypfopt/hierarchical_portfolio.py | 3 --- tests/test_hrp.py | 23 +++++++++++++++++++++++ 2 files changed, 23 insertions(+), 3 deletions(-) diff --git a/pypfopt/hierarchical_portfolio.py b/pypfopt/hierarchical_portfolio.py index 6dc76d07..248aac15 100644 --- a/pypfopt/hierarchical_portfolio.py +++ b/pypfopt/hierarchical_portfolio.py @@ -176,9 +176,6 @@ def optimize(self, linkage_method="single"): OrderedDict weights for the HRP portfolio """ - if linkage_method not in sch._LINKAGE_METHODS: - raise ValueError("linkage_method must be one recognised by scipy") - if self.returns is None: cov = self.cov_matrix corr = cov_to_corr(self.cov_matrix).round(6) diff --git a/tests/test_hrp.py b/tests/test_hrp.py index 369a1727..5b78984a 100644 --- a/tests/test_hrp.py +++ b/tests/test_hrp.py @@ -78,3 +78,26 @@ def test_quasi_dag(): hrp.optimize(linkage_method="single") clusters = hrp.clusters assert HRPOpt._get_quasi_diag(clusters)[:5] == [12, 6, 15, 14, 2] + + +def test_hrp_linkage_methods(): + # optimize() used to validate linkage_method against the private attribute + # sch._LINKAGE_METHODS, which scipy 1.18 removed, so every call raised + # AttributeError. scipy exposes no public list of linkage methods, so the + # seven documented by scipy.cluster.hierarchy.linkage are pinned here. + df = get_data() + returns = df.pct_change().dropna(how="all") + methods = [ + "single", + "complete", + "average", + "weighted", + "centroid", + "median", + "ward", + ] + for method in methods: + hrp = HRPOpt(returns) + w = hrp.optimize(linkage_method=method) + assert set(w.keys()) == set(returns.columns) + np.testing.assert_almost_equal(sum(w.values()), 1)