Source code for moment_sos.reference

"""Interior-point reference (cvxpy + MOSEK/Clarabel): solves the *same* dual problem inf_theta U(theta) exactly,
i.e.   min sigma + sum_b w_b t_b   s.t.  Q_b(theta) + t_b I >= 0,  t_b >= 0   (t_b free for constant-trace blocks).
By Theorem 2 of the paper this equals the moment relaxation with trace cuts."""
from __future__ import annotations
import time
import numpy as np
import scipy.sparse as sp
from .relaxation import Relaxation


def _sym_index(I, J, s, off=0):
    idx = np.zeros((s, s), dtype=int)
    idx[I, J] = np.arange(len(I)) + off
    idx[J, I] = np.arange(len(I)) + off
    return idx


[docs] def solve_dual_ipm(R: Relaxation, solver=None, verbose=False): """Solve the exact dual min sigma + sum_b w_b t_b s.t. Q_b(theta) + t_b I >= 0 with an interior-point solver (cvxpy). Returns (value in the internal normalisation, seconds).""" import cvxpy as cp if solver is None: # MOSEK if available, else Clarabel (open-source interior point) import os solver = os.environ.get("MSOS_SOLVER") or ("MOSEK" if "MOSEK" in cp.installed_solvers() else "CLARABEL") ns, nu, npp = R.nslots, R.nu, R.np_ sigma = cp.Variable() q = cp.Variable(ns) u = cp.Variable(max(nu, 1)); p = cp.Variable(max(npp, 1)) cons = [] S = sp.csr_matrix((R.slot_w, (R.slot_g, np.arange(ns))), shape=(R.nmom, ns)) e0 = np.zeros(R.nmom); e0[R.empty_id] = 1.0 rexpr = -R.fvec + e0 * sigma if nu: rexpr = rexpr - sp.csr_matrix((R.Rloc[2], (R.Rloc[0], R.Rloc[1])), shape=(R.nmom, nu)) @ u if npp: rexpr = rexpr - sp.csr_matrix((R.Req[2], (R.Req[0], R.Req[1])), shape=(R.nmom, npp)) @ p cons.append(S @ q == rexpr) obj = sigma for k, b in enumerate(R.blocks): t = cp.Variable(nonneg=b["plus"]) obj = obj + b["weight"] * t if b["weight"] != 0 else obj s = b["size"] if b["kind"] == "moment": idx = _sym_index(R.cl_I[k], R.cl_J[k], s, R.cl_off[k]) Q = q[idx.reshape(-1)].reshape((s, s), order="C") else: info = R.ineq_info[k - len(R.cl_basis)] off = R.u_off[k - len(R.cl_basis)] idx = _sym_index(info["a"], info["b"], s, off) Q = u[idx.reshape(-1)].reshape((s, s), order="C") cons.append(Q + t * np.eye(s) >> 0) prob = cp.Problem(cp.Minimize(obj), cons) t0 = time.perf_counter() prob.solve(solver=solver, verbose=verbose) return float(prob.value), time.perf_counter() - t0