From 6a249a80d7de62e99452b98bdd3237b6cf97e578 Mon Sep 17 00:00:00 2001 From: s-heppner Date: Sun, 2 Nov 2025 13:36:57 +0100 Subject: [PATCH] smr_alignment.MetricAlignmentOptimizer: Make entropy penalty convex Previously, `MetricAlignmentOptimizer.optimize_weights()` used the `cvxpy` built-in `cvxpy.entr(x)` entropy penalty. What I didn't realise until I ran the optimizer on real data, is that this function is actually concave. Therefore, it is possible, that the overall optimization problem is not convex anymore, messing up our convex solver. This adapts the entropy penalty to be a basic L2 penalty, which is convex. Fixes #16 --- smr_alignment/src/smr_alignment/metric_alignment.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/smr_alignment/src/smr_alignment/metric_alignment.py b/smr_alignment/src/smr_alignment/metric_alignment.py index 4fdff74..cce28b8 100644 --- a/smr_alignment/src/smr_alignment/metric_alignment.py +++ b/smr_alignment/src/smr_alignment/metric_alignment.py @@ -147,8 +147,10 @@ def optimize_weights( loss = cp.sum(cp.pos(margins)) # piecewise-linear convex if entropy_lambda > 0: - # cp.entr(x) = x*log(x); we add small offset to keep it defined near zero - loss += entropy_lambda * cp.sum(cp.entr(w + 1e-16)) + # we add small offset to keep it defined near zero + n = len(metrics) + u = np.full(n, 1.0 / n) + loss += entropy_lambda * cp.sum_squares(w - u) constraints = [cp.sum(w) == 1.0] # cp.sum(w) == 1.0 in CVXPY doesn’t return a plain bool at runtime, # it returns a Constraint object, confusing type checkers. Therefore, the "type: ignore" below.