Exemplo n.º 1
0
def linearize(expr):
    """Returns the tangent approximation to the expression.

    Gives an elementwise lower (upper) bound for convex (concave)
    expressions. No guarantees for non-DCP expressions.

    Args:
        expr: An expression.

    Returns:
        An affine expression.
    """
    if expr.is_affine():
        return expr
    else:
        if expr.value is None:
            raise ValueError(
                "Cannot linearize non-affine expression with missing variable values."
            )
        tangent = np.real(expr.value)  #+ np.imag(expr.value)
        grad_map = expr.grad
        for var in expr.variables():
            if grad_map[var] is None:
                return None
            complex_flag = False
            if var.is_complex() or np.any(np.iscomplex(grad_map[var])):
                complex_flag = True
            if var.ndim > 1:
                temp = cvx.reshape(cvx.vec(var - var.value),
                                   (var.shape[0] * var.shape[1], 1))
                if complex_flag:
                    flattened = np.transpose(np.real(grad_map[var])) @ cvx.real(temp) + \
                    np.transpose(np.imag(grad_map[var])) @ cvx.imag(temp)
                else:
                    flattened = np.transpose(np.real(grad_map[var])) @ temp
                tangent = tangent + cvx.reshape(flattened, expr.shape)
            elif var.size > 1:
                if complex_flag:
                    tangent = tangent + np.transpose(np.real(grad_map[var])) @ (cvx.real(var) - np.real(var.value)) \
                    + np.transpose(np.imag(grad_map[var])) @ (cvx.imag(var) - np.imag(var.value))
                else:
                    tangent = tangent + np.transpose(np.real(
                        grad_map[var])) @ (var - var.value)
            else:
                if complex_flag:
                    tangent = tangent + np.real(grad_map[var]) * (cvx.real(var) - np.real(var.value)) \
                    + np.imag(grad_map[var]) * (cvx.imag(var) - np.imag(var.value))
                else:
                    tangent = tangent + np.real(
                        grad_map[var]) * (var - var.value)
        return tangent
Exemplo n.º 2
0
    def test_hermitian(self):
        """Test Hermitian variables.
        """
        with self.assertRaises(Exception) as cm:
            v = Variable((4, 3), hermitian=True)
        self.assertEqual(
            str(cm.exception),
            "Invalid dimensions (4, 3). Must be a square matrix.")

        v = Variable((2, 2), hermitian=True)
        assert v.is_hermitian()
        # v = Variable((2,2), PSD=True)
        # assert v.is_symmetric()
        # v = Variable((2,2), NSD=True)
        # assert v.is_symmetric()
        v = Variable((2, 2), diag=True)
        assert v.is_hermitian()

        v = Variable((2, 2), hermitian=True)
        expr = v + v
        assert expr.is_hermitian()
        expr = -v
        assert expr.is_hermitian()
        expr = v.T
        assert expr.is_hermitian()
        expr = cp.real(v)
        assert expr.is_hermitian()
        expr = cp.imag(v)
        assert expr.is_hermitian()
        expr = cp.conj(v)
        assert expr.is_hermitian()
        expr = cp.promote(Variable(), (2, 2))
        assert expr.is_hermitian()
 def solve(self, solver='OLE'):
     '''
     Method to solve the model
     Args:
         solver:'OLE' Ordinary Least Squares method
         'Huber': Uses Huber smoother to down estimate the outliers.
     '''
     W = cp.Variable((self.N, 1), complex=True)
     if solver.upper() == 'OLE':  # Ordinary least squares
         _objective = cp.Minimize(cp.sum_squares(self.ALPHA @ W + self.A))
     elif solver.upper(
     ) == 'HUBER':  # TODO test Huber solver for robust optimization
         _real = cp.real(self.ALPHA @ W + self.A)
         _imag = cp.imag(self.ALPHA @ W + self.A)
         _objective = cp.Minimize(
             cp.sum_squares(cp.huber(cp.hstack([_real, _imag]), M=0)))
     elif solver.upper() == 'WLS':  #  TODO test weighted least squares
         _objective = cp.Minimize(
             cp.sum_squares(cp.diag(self.C) @ (self.ALPHA @ W + self.A)))
     else:
         raise tools.CustomError('Unrecognized Solver name')
     prob = cp.Problem(_objective)
     prob.solve()
     self.W = W.value
     return W.value
Exemplo n.º 4
0
 def test_psd(self) -> None:
     """Test Hermitian variables.
     """
     X = Variable((2, 2), hermitian=True)
     prob = Problem(cp.Minimize(cp.imag(X[1, 0])), [X >> 0, X[0, 0] == -1])
     prob.solve(solver="SCS")
     assert prob.status is cp.INFEASIBLE
Exemplo n.º 5
0
    def test_symmetric(self) -> None:
        """Test symmetric variables.
        """
        with self.assertRaises(Exception) as cm:
            v = Variable((4, 3), symmetric=True)
        self.assertEqual(
            str(cm.exception),
            "Invalid dimensions (4, 3). Must be a square matrix.")

        v = Variable((2, 2), symmetric=True)
        assert v.is_symmetric()
        v = Variable((2, 2), PSD=True)
        assert v.is_symmetric()
        v = Variable((2, 2), NSD=True)
        assert v.is_symmetric()
        v = Variable((2, 2), diag=True)
        assert v.is_symmetric()
        assert self.a.is_symmetric()
        assert not self.A.is_symmetric()

        v = Variable((2, 2), symmetric=True)
        expr = v + v
        assert expr.is_symmetric()
        expr = -v
        assert expr.is_symmetric()
        expr = v.T
        assert expr.is_symmetric()
        expr = cp.real(v)
        assert expr.is_symmetric()
        expr = cp.imag(v)
        assert expr.is_symmetric()
        expr = cp.conj(v)
        assert expr.is_symmetric()
        expr = cp.promote(Variable(), (2, 2))
        assert expr.is_symmetric()
Exemplo n.º 6
0
 def test_hermitian(self):
     """Test Hermitian variables.
     """
     X = Variable((2, 2), hermitian=True)
     prob = Problem(cvx.Minimize(cvx.imag(X[1, 0])),
                    [X[0, 0] == 2, X[1, 1] == 3, X[0, 1] == 1+1j])
     prob.solve()
     self.assertItemsAlmostEqual(X.value, [2, 1-1j, 1+1j, 3])
Exemplo n.º 7
0
    def test_pnorm(self):
        """Test complex with pnorm.
        """
        x = Variable((1, 2), complex=True)
        prob = Problem(cvx.Maximize(cvx.sum(cvx.imag(x) + cvx.real(x))), [cvx.norm1(x) <= 2])
        result = prob.solve()
        self.assertAlmostEqual(result, 2*np.sqrt(2))
        val = np.ones(2)*np.sqrt(2)/2
        # self.assertItemsAlmostEqual(x.value, val + 1j*val)

        x = Variable((2, 2), complex=True)
        prob = Problem(cvx.Maximize(cvx.sum(cvx.imag(x) + cvx.real(x))),
                       [cvx.pnorm(x, p=2) <= np.sqrt(8)])
        result = prob.solve()
        self.assertAlmostEqual(result, 8)
        val = np.ones((2, 2))
        self.assertItemsAlmostEqual(x.value, val + 1j*val)
Exemplo n.º 8
0
 def test_abs(self):
     """Test with absolute value.
     """
     x = Variable(2, complex=True)
     prob = Problem(cvx.Maximize(cvx.sum(cvx.imag(x) + cvx.real(x))), [cvx.abs(x) <= 2])
     result = prob.solve()
     self.assertAlmostEqual(result, 4*np.sqrt(2))
     val = np.ones(2)*np.sqrt(2)
     self.assertItemsAlmostEqual(x.value, val + 1j*val)
Exemplo n.º 9
0
 def test_params(self):
     """Test with parameters.
     """
     p = cvx.Parameter(imag=True, value=1j)
     x = Variable(2, complex=True)
     prob = Problem(cvx.Maximize(cvx.sum(cvx.imag(x) + cvx.real(x))), [cvx.abs(p*x) <= 2])
     result = prob.solve()
     self.assertAlmostEqual(result, 4*np.sqrt(2))
     val = np.ones(2)*np.sqrt(2)
     self.assertItemsAlmostEqual(x.value, val + 1j*val)
Exemplo n.º 10
0
 def test_imag(self):
     """Test imag.
     """
     A = np.ones((2, 2))
     expr = Constant(A) + 2j*Constant(A)
     expr = cvx.imag(expr)
     assert expr.is_real()
     assert not expr.is_complex()
     assert not expr.is_imag()
     self.assertItemsAlmostEqual(expr.value, 2*A)
Exemplo n.º 11
0
def trace_constraint(mat_r: Variable, mat_i: Variable, trace: complex) -> List[Constraint]:
    """Return CVXPY trace constraints for a complex matrix.

    Args:
        mat_r: The CVXPY variable for the real part of the matrix.
        mat_i: The CVXPY variable for the complex part of the matrix.
        trace: The value for the trace constraint.

    Returns:
        A list of constraints on the real and imaginary parts.
    """
    return [cvxpy.trace(mat_r) == cvxpy.real(trace), cvxpy.trace(mat_i) == cvxpy.imag(trace)]
Exemplo n.º 12
0
    def test_real(self):
        """Test real.
        """
        A = np.ones((2, 2))
        expr = Constant(A) + 1j*Constant(A)
        expr = cvx.real(expr)
        assert expr.is_real()
        assert not expr.is_complex()
        assert not expr.is_imag()
        self.assertItemsAlmostEqual(expr.value, A)

        x = Variable(complex=True)
        expr = cvx.imag(x) + cvx.real(x)
        assert expr.is_real()
Exemplo n.º 13
0
    def test_affine_atoms_canon(self):
        """Test canonicalization for affine atoms.
        """
        # Scalar.
        x = Variable()
        expr = cvx.imag(x + 1j * x)
        prob = Problem(Minimize(expr), [x >= 0])
        result = prob.solve()
        self.assertAlmostEqual(result, 0)
        self.assertAlmostEqual(x.value, 0)

        x = Variable(imag=True)
        expr = 1j * x
        prob = Problem(Minimize(expr), [cvx.imag(x) <= 1])
        result = prob.solve()
        self.assertAlmostEqual(result, -1)
        self.assertAlmostEqual(x.value, 1j)

        x = Variable(2)
        expr = x / 1j
        prob = Problem(Minimize(expr[0] * 1j + expr[1] * 1j),
                       [cvx.real(x + 1j) >= 1])
        result = prob.solve()
        self.assertAlmostEqual(result, -np.inf)
        prob = Problem(Minimize(expr[0] * 1j + expr[1] * 1j),
                       [cvx.real(x + 1j) <= 1])
        result = prob.solve()
        self.assertAlmostEqual(result, -2)
        self.assertItemsAlmostEqual(x.value, [1, 1])
        prob = Problem(
            Minimize(expr[0] * 1j + expr[1] * 1j),
            [cvx.real(x + 1j) >= 1, cvx.conj(x) <= 0])
        result = prob.solve()
        self.assertAlmostEqual(result, np.inf)

        x = Variable((2, 2))
        y = Variable((3, 2), complex=True)
        expr = cvx.vstack([x, y])
        prob = Problem(Minimize(cvx.sum(cvx.imag(cvx.conj(expr)))),
                       [x == 0, cvx.real(y) == 0,
                        cvx.imag(y) <= 1])
        result = prob.solve()
        self.assertAlmostEqual(result, -6)
        self.assertItemsAlmostEqual(y.value, 1j * np.ones((3, 2)))
        self.assertItemsAlmostEqual(x.value, np.zeros((2, 2)))

        x = Variable((2, 2))
        y = Variable((3, 2), complex=True)
        expr = cvx.vstack([x, y])
        prob = Problem(Minimize(cvx.sum(cvx.imag(expr.H))),
                       [x == 0, cvx.real(y) == 0,
                        cvx.imag(y) <= 1])
        result = prob.solve()
        self.assertAlmostEqual(result, -6)
        self.assertItemsAlmostEqual(y.value, 1j * np.ones((3, 2)))
        self.assertItemsAlmostEqual(x.value, np.zeros((2, 2)))
Exemplo n.º 14
0
def HCRB_sdp(rho, drho, solve="MOSEK", verbose_state=False):
    """
    Calculate the HCRB of the statistical model define by rho and its derivatives.

    The derivation of this formulation of the HCRB and more details can be found in the paper:

    - [published](https://journals.aps.org/prl/abstract/10.1103/PhysRevLett.123.200503)
    - [arxiv](https://arxiv.org/abs/1906.05724)

    This current implementation currently assumes QuTip Qobjs for rho and drho.
    # TODO: tweak the code to take qobj or numpy arrays.

    Inputs:

    - rho: Qobj, parameter encoded state
    - drho: list[Qobj], dervatives of rho in the direction of the paramaters of interest
    - solve, SDP solver options: "MOSEK", "SCS" and "CVXOPT" [more details](https://www.cvxpy.org/tutorial/advanced/index.html)
    - verbose_state, if True then switches the SDP solver to verbose mode

    returns:

    The HRCB (float) for the statistical model defined by the state and its derivatives.

    """

    # The SDP calculation is very sensitive to the input state being exactly hermitian.
    rho = (rho.dag() + rho) / 2

    d = rho.dims[0][0]
    npar = len(drho)

    D, Vi = np.linalg.eigh(rho.full())

    D = np.real(D)

    Vi = Vi[:, ::-1]
    D = D[::-1]

    Vi = qt.Qobj(Vi, dims=[[2] * n, [2] * n])

    snonzero, rnk = rank(D)

    solver_options = {"MOSEK": cp.MOSEK, "CVXOPT": cp.CVXOPT, "SCS": cp.SCS}

    maskDiag = np.diag(
        np.ndarray.flatten(
            np.concatenate(
                (np.ones([rnk, 1],
                         dtype=bool), np.zeros([d - rnk, 1], dtype=bool)))))
    maskRank = np.concatenate((
        np.concatenate(
            (
                np.triu(np.ones(rnk, dtype=bool), 1),
                np.zeros([rnk, d - rnk], dtype=bool),
            ),
            axis=1,
        ),
        np.zeros([d - rnk, d], dtype=bool),
    ))
    maskKern = np.concatenate((
        np.concatenate(
            (np.zeros([rnk, rnk],
                      dtype=bool), np.ones([rnk, d - rnk], dtype=bool)),
            axis=1,
        ),
        np.zeros([d - rnk, d], dtype=bool),
    ))

    fulldim = 2 * rnk * d - rnk**2

    drhomat = np.zeros((fulldim, npar), dtype=np.complex_)

    for i in range(npar):
        drho[i] = (drho[i].dag() + drho[i]) / 2
        eigdrho = (Vi.dag()) * drho[i] * Vi
        eigdrho = eigdrho.full()
        ak = eigdrho[maskKern]
        ak = ak.reshape((rnk, d - rnk)).transpose()
        ak = ak.reshape((rnk * (d - rnk)))

        row = np.concatenate((
            eigdrho[maskDiag],
            np.real(eigdrho[maskRank]),
            np.imag(eigdrho[maskRank]),
            np.real(ak),
            np.imag(ak),
        ))
        drhomat[:, i] = row

    S = SmatRank(snonzero, d, rnk, fulldim)
    S = (S.transpose().conjugate() + S) / 2

    R = Rmat(S)

    effdim = R.shape[0]
    idd = np.diag(
        np.ndarray.flatten(
            np.concatenate((np.ones((rnk)), 2 * np.ones((fulldim - rnk))))))

    V = cp.Variable((npar, npar), PSD=True)
    X = cp.Variable((fulldim, npar))

    A = cp.vstack([
        cp.hstack([V, X.T @ R.conjugate().transpose()]),
        cp.hstack([R @ X, np.identity(effdim)]),
    ])

    constraints = [
        cp.vstack([
            cp.hstack([cp.real(A), -cp.imag(A)]),
            cp.hstack([cp.imag(A), cp.real(A)])
        ]) >> 0,
        X.T @ idd @ drhomat == np.identity(3),
    ]

    obj = cp.Minimize(cp.trace(V))
    prob = cp.Problem(obj, constraints)
    prob.solve(solver=solver_options.get(solve, cp.SCS), verbose=verbose_state)
    out = prob.value
    return out
def getControllerParams(y, M):
    """
    This function generates the response time optimal controller parameters for a permanent
    magnet motor, resulting in zero torque ripple under no measurement noise. It is assumed, for simplicity,
    that the control inputs are the currents instead of voltages. These currents, once known, can be
    used to determine the control voltage inputs.
    :param y: This is the torque vs theta function, which makes the permanent magnet motor
    model nonlinear.
    :param M: This is the order of the Fourier series approximation of the nonlinearity.
    :return: Returns the controller parameters, without the proportionality constant.
    """
    plt.figure()
    plt.plot(np.linspace(-np.pi, np.pi, len(y)), y)
    plt.title('The plot of f(theta)')

    p = np.zeros(M)
    q = np.zeros(M)
    for k in range(M):
        p[k] = 2 * (np.cos(
            (k + 1) * np.linspace(-np.pi, np.pi, len(y))) @ y) / len(y)
        q[k] = 2 * (np.sin(
            (k + 1) * np.linspace(-np.pi, np.pi, len(y))) @ y) / len(y)

    s = np.zeros(len(y))
    x = np.linspace(-np.pi, np.pi, len(y))
    for k in range(len(y)):
        s[k] = (np.cos(np.linspace(1, M, M) * x[k]) @ p +
                np.sin(np.linspace(1, M, M) * x[k]) @ q)

    plt.figure()
    plt.plot(s)
    plt.plot(y)
    plt.title('Comparison with the Fourier truncation')

    Z = np.zeros([4 * M + 1, 2 * M])

    #cos and cos
    for k in range(1, M + 1):
        for l in range(1, M + 1):
            if (l == k):
                Z[0, l - 1] = Z[0, l - 1] + p[k - 1] / 2
                Z[2 * (k + l) - 1,
                  l - 1] = Z[2 * (k + l) - 1, l - 1] + p[k - 1] / 2
            else:
                Z[2 * (k + l) - 1,
                  l - 1] = Z[2 * (k + l) - 1, l - 1] + p[k - 1] / 2
                Z[2 * np.abs(k - l) - 1,
                  l - 1] = Z[2 * abs(k - l) - 1, l - 1] + p[k - 1] / 2

    #cos and sin
    for k in range(1, M + 1):
        for l in range(1, M + 1):
            if (l == k):
                Z[2 * (k + l) + 1 - 1,
                  l + M - 1] = Z[2 * (k + l) + 1 - 1, l + M - 1] + p[k - 1] / 2
            else:
                Z[2 * (k + l) + 1 - 1,
                  l + M - 1] = Z[2 * (k + l) + 1 - 1, l + M - 1] + p[k - 1] / 2
                Z[2 * abs(k - l) + 1 - 1,
                  l + M - 1] = Z[2 * abs(k - l) + 1 - 1,
                                 l + M - 1] + np.sign(l - k) * p[k - 1] / 2

    # sin and cos
    for k in range(1, M + 1):
        for l in range(1, M + 1):
            if (l == k):
                Z[2 * (k + l) + 1 - 1,
                  l - 1] = Z[2 * (k + l) + 1 - 1, l - 1] + q[k - 1] / 2
            else:
                Z[2 * (k + l) + 1 - 1,
                  l - 1] = Z[2 * (k + l) + 1 - 1, l - 1] + q[k - 1] / 2
                Z[2 * np.abs(k - l) + 1 - 1,
                  l - 1] = Z[2 * np.abs(k - l) + 1 - 1,
                             l - 1] + np.sign(k - l) * q[k - 1] / 2

    # sin and sin
    for k in range(1, M + 1):
        for l in range(1, M + 1):
            if (l == k):
                Z[1 - 1, l + M - 1] = Z[1 - 1, l + M - 1] + q[k - 1] / 2
                Z[2 * (k + l) - 1,
                  l + M - 1] = Z[2 * (k + l) - 1, l + M - 1] - q[k - 1] / 2
            else:
                Z[2 * (k + l) - 1,
                  l + M - 1] = Z[2 * (k + l) - 1, l + M - 1] - q[k - 1] / 2
                Z[2 * abs(k - l) - 1,
                  l + M - 1] = Z[2 * abs(k - l) - 1, l + M - 1] + q[k - 1] / 2

    A = np.zeros([4 * M, 4 * M])
    for i in range(1, 2 * M + 1):
        A[2 * (i - 1), 2 * (i - 1)] = np.cos(2 * np.pi * i / 3)
        A[2 * (i - 1), 2 * i - 1] = np.sin(2 * np.pi * i / 3)
        A[2 * i - 1, 2 * (i - 1)] = -np.sin(2 * np.pi * i / 3)
        A[2 * i - 1, 2 * i - 1] = np.cos(2 * np.pi * i / 3)

    A = np.vstack((np.hstack(
        (1, np.zeros(4 * M))), np.hstack((np.zeros([4 * M, 1]), A))))

    B = np.zeros([4 * M, 4 * M])
    for i in range(1, 2 * M + 1):
        B[2 * (i - 1), 2 * (i - 1)] = np.cos(4 * np.pi * i / 3)
        B[2 * (i - 1), 2 * i - 1] = np.sin(4 * np.pi * i / 3)
        B[2 * i - 1, 2 * (i - 1)] = -np.sin(4 * np.pi * i / 3)
        B[2 * i - 1, 2 * i - 1] = np.cos(4 * np.pi * i / 3)

    B = np.vstack((np.hstack(
        (1, np.zeros(4 * M))), np.hstack((np.zeros([4 * M, 1]), B))))

    G = np.hstack((Z, A @ Z, B @ Z))

    As1 = np.vstack((np.hstack(
        (np.zeros(M - 1), 0)), np.hstack((np.eye(M - 1), np.zeros([M - 1,
                                                                   1])))))
    Bs1 = np.vstack((1, np.zeros([M - 1, 1])))
    As2 = np.vstack((np.hstack(
        (np.zeros(M - 1), 0)), np.hstack((np.eye(M - 1), np.zeros([M - 1,
                                                                   1])))))
    Bs2 = np.vstack((1, np.zeros([M - 1, 1])))
    As3 = np.vstack((np.hstack(
        (np.zeros(M - 1), 0)), np.hstack((np.eye(M - 1), np.zeros([M - 1,
                                                                   1])))))
    Bs3 = np.vstack((1, np.zeros([M - 1, 1])))

    p1 = cvx.Variable((1, M))
    q1 = cvx.Variable((1, M))
    p2 = cvx.Variable((1, M))
    q2 = cvx.Variable((1, M))
    p3 = cvx.Variable((1, M))
    q3 = cvx.Variable((1, M))
    Q1l = cvx.Variable((M, M), hermitian=True)
    Q2l = cvx.Variable((M, M), hermitian=True)
    Q3l = cvx.Variable((M, M), hermitian=True)
    Q1u = cvx.Variable((M, M), hermitian=True)
    Q2u = cvx.Variable((M, M), hermitian=True)
    Q3u = cvx.Variable((M, M), hermitian=True)
    Ds1u = cvx.Variable()
    Ds2u = cvx.Variable()
    Ds3u = cvx.Variable()
    Ds1l = cvx.Variable()
    Ds2l = cvx.Variable()
    Ds3l = cvx.Variable()
    z = cvx.Variable()
    Cs1u = cvx.Variable((1, M), complex=True)
    Cs2u = cvx.Variable((1, M), complex=True)
    Cs3u = cvx.Variable((1, M), complex=True)
    Cs1l = cvx.Variable((1, M), complex=True)
    Cs2l = cvx.Variable((1, M), complex=True)
    Cs3l = cvx.Variable((1, M), complex=True)
    r1u = cvx.Variable((1, M), complex=True)
    r2u = cvx.Variable((1, M), complex=True)
    r3u = cvx.Variable((1, M), complex=True)
    r1l = cvx.Variable((1, M), complex=True)
    r2l = cvx.Variable((1, M), complex=True)
    r3l = cvx.Variable((1, M), complex=True)

    constraints = []
    constraints = constraints + [
        G @ (cvx.hstack((p1, q1, p2, q2, p3, q3)).T) == np.vstack(
            (1, np.zeros([4 * M, 1])))
    ]
    constraints = constraints + [
        cvx.real(r1u) == -p1 / 2,
        cvx.imag(r1u) == q1 / 2
    ]
    constraints = constraints + [
        cvx.real(r2u) == -p2 / 2,
        cvx.imag(r2u) == q2 / 2
    ]
    constraints = constraints + [
        cvx.real(r3u) == -p3 / 2,
        cvx.imag(r3u) == q3 / 2
    ]
    constraints = constraints + [
        cvx.real(r1l) == p1 / 2,
        cvx.imag(r1l) == -q1 / 2
    ]
    constraints = constraints + [
        cvx.real(r2l) == p2 / 2,
        cvx.imag(r2l) == -q2 / 2
    ]
    constraints = constraints + [
        cvx.real(r3l) == p3 / 2,
        cvx.imag(r3l) == -q3 / 2
    ]
    constraints = constraints + [Cs1u == r1u]
    constraints = constraints + [Cs2u == r2u]
    constraints = constraints + [Cs3u == r3u]
    constraints = constraints + [Cs1l == r1l]
    constraints = constraints + [Cs2l == r2l]
    constraints = constraints + [Cs3l == r3l]
    constraints = constraints + [Ds1u == z / 2]
    constraints = constraints + [Ds2u == z / 2]
    constraints = constraints + [Ds3u == z / 2]
    constraints = constraints + [Ds1l == z / 2]
    constraints = constraints + [Ds2l == z / 2]
    constraints = constraints + [Ds3l == z / 2]
    constraints = constraints + [
        cvx.vstack(
            (cvx.hstack((Q1u - (As1.T) @ Q1u @ As1, -(As1.T) @ Q1u @ Bs1)),
             cvx.hstack((-cvx.conj(
                 (As1.T) @ Q1u @ Bs1).T, -(Bs1.T) @ Q1u @ Bs1)))) + cvx.vstack(
                     (cvx.hstack((np.zeros([M, M]), -cvx.conj(Cs1u).T)),
                      cvx.hstack(
                          (-Cs1u, cvx.reshape(Ds1u + Ds1u, [1, 1]))))) >> 0
    ]
    constraints = constraints + [
        cvx.vstack(
            (cvx.hstack((Q2u - (As2.T) @ Q2u @ As2, -(As2.T) @ Q2u @ Bs2)),
             cvx.hstack((-cvx.conj(
                 (As2.T) @ Q2u @ Bs2).T, -(Bs2.T) @ Q2u @ Bs2)))) + cvx.vstack(
                     (cvx.hstack((np.zeros([M, M]), -cvx.conj(Cs2u).T)),
                      cvx.hstack(
                          (-Cs2u, cvx.reshape(Ds2u + Ds2u, [1, 1]))))) >> 0
    ]
    constraints = constraints + [
        cvx.vstack(
            (cvx.hstack((Q3u - (As3.T) @ Q3u @ As3, -(As3.T) @ Q3u @ Bs3)),
             cvx.hstack((-cvx.conj(
                 (As3.T) @ Q3u @ Bs3).T, -(Bs3.T) @ Q3u @ Bs3)))) + cvx.vstack(
                     (cvx.hstack((np.zeros([M, M]), -cvx.conj(Cs3u).T)),
                      cvx.hstack(
                          (-Cs3u, cvx.reshape(Ds3u + Ds3u, [1, 1]))))) >> 0
    ]
    constraints = constraints + [
        cvx.vstack(
            (cvx.hstack((Q1l - (As1.T) @ Q1l @ As1, -(As1.T) @ Q1l @ Bs1)),
             cvx.hstack((-cvx.conj(
                 (As1.T) @ Q1l @ Bs1).T, -(Bs1.T) @ Q1l @ Bs1)))) + cvx.vstack(
                     (cvx.hstack((np.zeros([M, M]), -cvx.conj(Cs1l).T)),
                      cvx.hstack(
                          (-Cs1l, cvx.reshape(Ds1l + Ds1l, [1, 1]))))) >> 0
    ]
    constraints = constraints + [
        cvx.vstack(
            (cvx.hstack((Q2l - (As2.T) @ Q2l @ As2, -(As2.T) @ Q2l @ Bs2)),
             cvx.hstack((-cvx.conj(
                 (As2.T) @ Q2l @ Bs2).T, -(Bs2.T) @ Q2l @ Bs2)))) + cvx.vstack(
                     (cvx.hstack((np.zeros([M, M]), -cvx.conj(Cs2l).T)),
                      cvx.hstack(
                          (-Cs2l, cvx.reshape(Ds2l + Ds2l, [1, 1]))))) >> 0
    ]
    constraints = constraints + [
        cvx.vstack(
            (cvx.hstack((Q3l - (As3.T) @ Q3l @ As3, -(As3.T) @ Q3l @ Bs3)),
             cvx.hstack((-cvx.conj(
                 (As3.T) @ Q3l @ Bs3).T, -(Bs3.T) @ Q3l @ Bs3)))) + cvx.vstack(
                     (cvx.hstack((np.zeros([M, M]), -cvx.conj(Cs3l).T)),
                      cvx.hstack(
                          (-Cs3l, cvx.reshape(Ds3l + Ds3l, [1, 1]))))) >> 0
    ]

    prob = cvx.Problem(cvx.Minimize(z), constraints)
    prob.solve(solver=cvx.SCS, verbose=True)

    return p, q, z.value, p1.value, p2.value, p3.value, q1.value, q2.value, q3.value
Exemplo n.º 16
0
def naghol_sdp(phi, dphi, d, solve="MOSEK", verbose_state=False):
    """
    args:
    -phi : qutip quantum object, pure normalised state, of size (d, 1)
    -dphi: qutip quantum object, vector of derivatives, of size (d, 1) x npar, where npar is the number of parameters
    -d   : integer             , dimension of Hilbert space

    default args:
    -solver        : string, string of solver, default is MOSEK, can also be CVXOPT, SCS. More details: https://www.cvxpy.org/tutorial/advanced/index.html
    -verbose_state : bool  , option if you want the cvxpy solver to turn maximal information or not. verbose_state = true prints max info to terminal

    returns: float, Holevo-Cramer-Rao bound of the statistical model defined by the encoded state and derivatives

    """
    # dictionary of SDP solvers
    solver_options = {"MOSEK": cp.MOSEK, "CVXOPT": cp.CVXOPT, "SCS": cp.SCS}

    # define the number of parameters
    npar = len(dphi)

    # here we define the matrix of horizontal lifts-Lmat
    # The horizontal lift is defined as: 2(|\partial_i \phi>  − <\phi| \partial_i \phi>|\phi> )
    psidpsi = phi.dag() * dphi
    pardphi = phi * psidpsi

    Lmat = 2 * (dphi - pardphi)

    # convert quantum objects to numpy arrays
    # n.b. this should be sped up by just calling phi.full(), will do this and test asap
    psi = np.zeros(d, dtype=complex)
    for i in range(d):
        psi[i] = phi[i][0][0]

    # same here as above with horizontal lifts
    Lmatt = np.zeros((d, npar), dtype=complex)
    for i in range(d):
        for j in range(npar):
            Lmatt[i, j] = Lmat[j][i]

    # set up SDP as defined in the readme/paper

    # set up variables 'variance matrix' V and 'derivative variable matrices' X
    V = cp.Variable((npar, npar), PSD=True)
    X = cp.Variable((d, npar), complex=True)

    # set up SD-matrix to optimise
    A = cp.vstack([cp.hstack([V, X.H]), cp.hstack([X, np.identity(d)])])

    # constraints need to be split because cvxpy will only accept a real valued SD-matrix
    constraints = [
        cp.vstack([
            cp.hstack([cp.real(A), -cp.imag(A)]),
            cp.hstack([cp.imag(A), cp.real(A)])
        ]) >> 0,
        cp.real(X.H @ Lmatt) == np.identity(3),
        ((psi.transpose()).conjugate() @ X) == 0,
    ]

    # define objective function
    obj = cp.Minimize(cp.trace(V))

    # define problem
    prob = cp.Problem(obj, constraints)

    # solve problem
    prob.solve(solver=solver_options[solve], verbose=verbose_state)

    # extract and return value
    out = prob.value

    return out