示例#1
0
        def jac(v):
            alf = self.alpha[0, 1] / self.alpha[0, 0]
            gdc = self._g_idx
            A, R = unvec(v)
            x_mat = np.array(np.bmat([[2 * alf * A, -R.T], [R,
                                                            zeros((k, k))]]))
            exh = expm(x_mat)
            ex = expm((1 - 2 * alf) * A)

            blk = np.zeros_like(exh)
            blk[:d, :] = (ex * lbd[None, :]) @ ex.T @ exh[:, :d].T
            blkA = (lbd[:, None] * ex.T) @ exh[:, :d].T @ X2 @ exh[:, :d]

            fexh = 2 * expm_frechet(x_mat, blk @ X2)[1]
            fex = 2 * expm_frechet((1 - 2 * alf) * A, blkA)[1]

            for r in range(1, p + 1):
                if r not in gdc:
                    continue
                br, er = gdc[r]
                fexh[br:br, br:br] = 0
                fex[br:br, br:br] = 0

            return vec(
                (1 - 2 * alf) * asym(fex) + 2 * alf * asym(fexh[:d, :d]),
                fexh[d:, :d] - fexh[:d, d:].T)
示例#2
0
文件: propcomp.py 项目: qutip/qutip
    def _compute_prop_grad(self, k, j, compute_prop=True):
        """
        Calculate the gradient of propagator wrt the control amplitude
        in the timeslot using the expm_frechet method
        The propagtor is calculated (almost) for 'free' in this method
        and hence it is returned if compute_prop==True
        Returns:
            [prop], prop_grad
        """
        dyn = self.parent

        if dyn.oper_dtype == Qobj:
            A = dyn._get_phased_dyn_gen(k).full() * dyn.tau[k]
            E = dyn._get_phased_ctrl_dyn_gen(k, j).full() * dyn.tau[k]
            if compute_prop:
                prop_dense, prop_grad_dense = la.expm_frechet(A, E)
                prop = Qobj(prop_dense, dims=dyn.dyn_dims)
                prop_grad = Qobj(prop_grad_dense, dims=dyn.dyn_dims)
            else:
                prop_grad_dense = la.expm_frechet(A, E, compute_expm=False)
                prop_grad = Qobj(prop_grad_dense, dims=dyn.dyn_dims)
        else:
            A = dyn._get_phased_dyn_gen(k) * dyn.tau[k]
            E = dyn._get_phased_ctrl_dyn_gen(k, j) * dyn.tau[k]
            if compute_prop:
                prop, prop_grad = la.expm_frechet(A, E)
            else:
                prop_grad = la.expm_frechet(A, E, compute_expm=False)
        if compute_prop:
            return prop, prop_grad
        else:
            return prop_grad
示例#3
0
def em_objective_for_aitken(
        T, node_to_idx, site_weights,
        m,
        transq_unscaled, transp,
        interact_trans, interact_dwell,
        data,
        root_distn1d,
        trans_out, dwell_out,
        scale,
        ):
    """
    Recast EM as a fixed-point problem.
    
    This approach is inspired by the introduction of the following paper.
    A QUASI-NEWTON ACCELERATION OF THE EM ALGORITHM
    Kenneth Lange
    1995

    """
    # Unpack some stuff.
    nsites, nnodes, nstates = data.shape
    n = nstates

    # Scale the rate matrices according to the edge ratios.
    transq = transq_unscaled * scale[:, None, None]

    # Compute the probability transition matrix arrays
    # and the interaction matrix arrays.
    trans_indicator = np.ones((n, n)) - np.identity(n)
    dwell_indicator = np.identity(n)
    for edge in T.edges():
        na, nb = edge
        eidx = node_to_idx[nb] - 1
        Q = transq[eidx]
        transp[eidx] = expm(Q)
        interact_trans[eidx] = expm_frechet(
                Q, Q * trans_indicator, compute_expm=False)
        interact_dwell[eidx] = expm_frechet(
                Q, Q * dwell_indicator, compute_expm=False)

    # Compute the expectations.
    validation = 0
    expectation_step(
            m.indices, m.indptr,
            transp, transq,
            interact_trans, interact_dwell,
            data,
            root_distn1d,
            trans_out, dwell_out,
            validation,
            )

    # Compute the per-edge ratios.
    trans_sum = (trans_out * site_weights[:, None]).sum(axis=0)
    dwell_sum = (dwell_out * site_weights[:, None]).sum(axis=0)
    scaling_ratios = trans_sum / -dwell_sum

    # Return the new scaling factors.
    return scale * scaling_ratios
示例#4
0
 def test_problematic_matrix(self):
     # this test case uncovered a bug which has since been fixed
     A = np.array([[1.50591997, 1.93537998], [0.41203263, 0.23443516]], dtype=float)
     E = np.array([[1.87864034, 2.07055038], [1.34102727, 0.67341123]], dtype=float)
     A_norm_1 = scipy.linalg.norm(A, 1)
     sps_expm, sps_frechet = expm_frechet(A, E, method="SPS")
     blockEnlarge_expm, blockEnlarge_frechet = expm_frechet(A, E, method="blockEnlarge")
     assert_allclose(sps_expm, blockEnlarge_expm)
     assert_allclose(sps_frechet, blockEnlarge_frechet)
示例#5
0
 def test_medium_matrix(self):
     # profile this to see the speed difference
     n = 1000
     A = np.random.exponential(size=(n, n))
     E = np.random.exponential(size=(n, n))
     sps_expm, sps_frechet = expm_frechet(A, E, method="SPS")
     blockEnlarge_expm, blockEnlarge_frechet = expm_frechet(A, E, method="blockEnlarge")
     assert_allclose(sps_expm, blockEnlarge_expm)
     assert_allclose(sps_frechet, blockEnlarge_frechet)
示例#6
0
 def test_medium_matrix(self):
     # profile this to see the speed difference
     n = 1000
     A = np.random.exponential(size=(n, n))
     E = np.random.exponential(size=(n, n))
     sps_expm, sps_frechet = expm_frechet(A, E, method='SPS')
     blockEnlarge_expm, blockEnlarge_frechet = expm_frechet(
         A, E, method='blockEnlarge')
     assert_allclose(sps_expm, blockEnlarge_expm)
     assert_allclose(sps_frechet, blockEnlarge_frechet)
示例#7
0
文件: vqa.py 项目: BoxiLi/qutip-qip
    def get_unitary_derivative(self, angles, term_index=0):
        """
        Compute the derivative of the block's unitary with respect to its
        free parameter, assuming it is of the form :math:`e^{-i \\theta H}`
        for a free parameter theta. If the block's operator is a
        :obj:`ParameterizedHamiltonian`, use the Frechet derivative of the
        exponential function.

        Parameters
        ----------
        angle: list of float
            free parameters to take derivatives with respect to
        term_index: int, optional
            Index of Parameterized Hamiltonian term that specifies the matrix
            direction in which to take the derivative.

        Returns
        -------
        derivative: float
        """
        if self.is_unitary or self.is_native_gate:
            raise ValueError(
                "Can only take derivative of block specified "
                "by Hamiltonians or ParameterizedHamiltonian instances.")
        if isinstance(self.operator, ParameterizedHamiltonian):
            arg = -1j * self.operator.get_hamiltonian(angles)
            direction = -1j * self.operator.p_terms[term_index]
            return Qobj(
                expm_frechet(arg.full(), direction.full(), compute_expm=False),
                dims=direction.dims,
            )
        if len(angles) != 1:
            raise ValueError("Expected a single angle for non-"
                             "ParameterizedHamiltonian instance.")
        return self.get_unitary(angles) * -1j * self.operator
def _check_hky_transition_expectations(t):
    n = 4
    kappa = 3.3
    nt_probs = np.array([0.1, 0.2, 0.3, 0.4])

    # Get an HKY rate matrix with arbitrary expected rate.
    pre_Q = hkymodel.get_pre_Q(kappa, nt_probs)

    # Rescale the rate matrix to have expected rate of 1.0.
    rates = pre_Q.sum(axis=1)
    expected_rate = rates.dot(nt_probs)
    pre_Q /= expected_rate

    # Convert the pre-rate matrix to an actual rate matrix
    # by subtracting row sums from the diagonal.
    rates = pre_Q.sum(axis=1)
    Q = pre_Q - np.diag(rates)

    # Create the transition probability matrix over time t.
    P = expm(Q*t)
    assert_allclose(P.sum(axis=1), 1)

    # Create a joint state distribution matrix.
    J = np.diag(nt_probs).dot(P)
    assert_allclose(J.sum(), 1)

    # Get the expm frechet matrix.
    C = pre_Q * t
    S = expm_frechet(Q*t, C, compute_expm=False)

    # Get the weighted sum of entries of S.
    expectation_a = ((S / P) * J).sum()
    assert_allclose(expectation_a, t)

    # Try an equivalent calculation which does not use P or J.
    expectation_b = np.diag(nt_probs).dot(S).sum()
    assert_allclose(expectation_b, t)

    # Use the library function.
    T = nx.DiGraph()
    root = 'N0'
    edge = ('N0', 'N1')
    T.add_edge(*edge)
    edge_to_Q = {edge : Q * t}
    edge_to_combination = {edge : pre_Q * t}
    root_distn = nt_probs
    data_weight_pairs = []
    for sa, sb in product(range(n), repeat=2):
        vec_a = np.zeros(n)
        vec_a[sa] = 1
        vec_b = np.zeros(n)
        vec_b[sb] = 1
        data = {'N0' : vec_a, 'N1' : vec_b}
        weight = J[sa, sb]
        data_weight_pairs.append((data, weight))
    edge_to_expectation = get_edge_to_expectation(
            T, root, edge_to_Q, edge_to_combination,
            root_distn, data_weight_pairs)
    expectation_c = edge_to_expectation[edge]
    assert_allclose(expectation_c, t)
 def test_small_norm_expm_frechet(self):
     # methodically test matrices with a range of norms, for better coverage
     M_original = np.array([
         [1, 2, 3, 4],
         [5, 6, 7, 8],
         [0, 0, 1, 2],
         [0, 0, 5, 6],
         ], dtype=float)
     A_original = np.array([
         [1, 2],
         [5, 6],
         ], dtype=float)
     E_original = np.array([
         [3, 4],
         [7, 8],
         ], dtype=float)
     A_original_norm_1 = scipy.linalg.norm(A_original, 1)
     selected_m_list = [1, 3, 5, 7, 9, 11, 13, 15]
     m_neighbor_pairs = zip(selected_m_list[:-1], selected_m_list[1:])
     for ma, mb in m_neighbor_pairs:
         ell_a = scipy.linalg._expm_frechet.ell_table_61[ma]
         ell_b = scipy.linalg._expm_frechet.ell_table_61[mb]
         target_norm_1 = 0.5 * (ell_a + ell_b)
         scale = target_norm_1 / A_original_norm_1
         M = scale * M_original
         A = scale * A_original
         E = scale * E_original
         expected_expm = scipy.linalg.expm(A)
         expected_frechet = scipy.linalg.expm(M)[:2, 2:]
         observed_expm, observed_frechet = expm_frechet(A, E)
         assert_allclose(expected_expm, observed_expm)
         assert_allclose(expected_frechet, observed_frechet)
 def test_fuzz(self):
     # try a bunch of crazy inputs
     rfuncs = (
             np.random.uniform,
             np.random.normal,
             np.random.standard_cauchy,
             np.random.exponential)
     ntests = 100
     for i in range(ntests):
         rfunc = random.choice(rfuncs)
         target_norm_1 = random.expovariate(1.0)
         n = random.randrange(2, 16)
         A_original = rfunc(size=(n,n))
         E_original = rfunc(size=(n,n))
         A_original_norm_1 = scipy.linalg.norm(A_original, 1)
         scale = target_norm_1 / A_original_norm_1
         A = scale * A_original
         E = scale * E_original
         M = np.vstack([
             np.hstack([A, E]),
             np.hstack([np.zeros_like(A), A])])
         expected_expm = scipy.linalg.expm(A)
         expected_frechet = scipy.linalg.expm(M)[:n, n:]
         observed_expm, observed_frechet = expm_frechet(A, E)
         assert_allclose(expected_expm, observed_expm)
         assert_allclose(expected_frechet, observed_frechet)
 def test_small_norm_expm_frechet(self):
     # methodically test matrices with a range of norms, for better coverage
     M_original = np.array([
         [1, 2, 3, 4],
         [5, 6, 7, 8],
         [0, 0, 1, 2],
         [0, 0, 5, 6],
         ], dtype=float)
     A_original = np.array([
         [1, 2],
         [5, 6],
         ], dtype=float)
     E_original = np.array([
         [3, 4],
         [7, 8],
         ], dtype=float)
     A_original_norm_1 = scipy.linalg.norm(A_original, 1)
     selected_m_list = [1, 3, 5, 7, 9, 11, 13, 15]
     m_neighbor_pairs = zip(selected_m_list[:-1], selected_m_list[1:])
     for ma, mb in m_neighbor_pairs:
         ell_a = scipy.linalg._expm_frechet.ell_table_61[ma]
         ell_b = scipy.linalg._expm_frechet.ell_table_61[mb]
         target_norm_1 = 0.5 * (ell_a + ell_b)
         scale = target_norm_1 / A_original_norm_1
         M = scale * M_original
         A = scale * A_original
         E = scale * E_original
         expected_expm = scipy.linalg.expm(A)
         expected_frechet = scipy.linalg.expm(M)[:2, 2:]
         observed_expm, observed_frechet = expm_frechet(A, E)
         assert_allclose(expected_expm, observed_expm)
         assert_allclose(expected_frechet, observed_frechet)
 def test_fuzz(self):
     # try a bunch of crazy inputs
     rfuncs = (
             np.random.uniform,
             np.random.normal,
             np.random.standard_cauchy,
             np.random.exponential)
     ntests = 100
     for i in range(ntests):
         rfunc = random.choice(rfuncs)
         target_norm_1 = random.expovariate(1.0)
         n = random.randrange(2, 16)
         A_original = rfunc(size=(n,n))
         E_original = rfunc(size=(n,n))
         A_original_norm_1 = scipy.linalg.norm(A_original, 1)
         scale = target_norm_1 / A_original_norm_1
         A = scale * A_original
         E = scale * E_original
         M = np.vstack([
             np.hstack([A, E]),
             np.hstack([np.zeros_like(A), A])])
         expected_expm = scipy.linalg.expm(A)
         expected_frechet = scipy.linalg.expm(M)[:n, n:]
         observed_expm, observed_frechet = expm_frechet(A, E)
         assert_allclose(expected_expm, observed_expm)
         assert_allclose(expected_frechet, observed_frechet)
 def test_problematic_matrix(self):
     # this test case uncovered a bug which has since been fixed
     A = np.array([
             [1.50591997, 1.93537998],
             [0.41203263, 0.23443516],
             ], dtype=float)
     E = np.array([
             [1.87864034, 2.07055038],
             [1.34102727, 0.67341123],
             ], dtype=float)
     A_norm_1 = scipy.linalg.norm(A, 1)
     sps_expm, sps_frechet = expm_frechet(
             A, E, method='SPS')
     blockEnlarge_expm, blockEnlarge_frechet = expm_frechet(
             A, E, method='blockEnlarge')
     assert_allclose(sps_expm, blockEnlarge_expm)
     assert_allclose(sps_frechet, blockEnlarge_frechet)
示例#14
0
    def compute_prop_grad(self, k, j, compute_prop=True):
        """
        Calculate the gradient of propagator wrt the control amplitude
        in the timeslot using the expm_frechet method
        The propagtor is calculated (almost) for 'free' in this method
        and hence it is returned if compute_prop==True
        Returns:
            [prop], prop_grad
        """
        dyn = self.parent
        A = dyn.get_dyn_gen(k)*dyn.tau[k]
        E = dyn.get_ctrl_dyn_gen(j)*dyn.tau[k]

        if compute_prop:
            prop, propGrad = la.expm_frechet(A, E)
            return prop, propGrad
        else:
            propGrad = la.expm_frechet(A, E, compute_expm=False)
            return propGrad
示例#15
0
    def compute_prop_grad(self, k, j, compute_prop=True):
        """
        Calculate the gradient of propagator wrt the control amplitude
        in the timeslot using the expm_frechet method
        The propagtor is calculated (almost) for 'free' in this method
        and hence it is returned if compute_prop==True
        Returns:
            [prop], prop_grad
        """
        dyn = self.parent
        A = dyn.get_dyn_gen(k) * dyn.tau[k]
        E = dyn.get_ctrl_dyn_gen(j) * dyn.tau[k]

        if compute_prop:
            prop, propGrad = la.expm_frechet(A, E)
            return prop, propGrad
        else:
            propGrad = la.expm_frechet(A, E, compute_expm=False)
            return propGrad
示例#16
0
 def test_expm_frechet(self):
     # a test of the basic functionality
     M = np.array([[1, 2, 3, 4], [5, 6, 7, 8], [0, 0, 1, 2], [0, 0, 5, 6]], dtype=float)
     A = np.array([[1, 2], [5, 6]], dtype=float)
     E = np.array([[3, 4], [7, 8]], dtype=float)
     expected_expm = scipy.linalg.expm(A)
     expected_frechet = scipy.linalg.expm(M)[:2, 2:]
     for kwargs in ({}, {"method": "SPS"}, {"method": "blockEnlarge"}):
         observed_expm, observed_frechet = expm_frechet(A, E, **kwargs)
         assert_allclose(expected_expm, observed_expm)
         assert_allclose(expected_frechet, observed_frechet)
示例#17
0
        def jac(v):
            A, R = unvec(v)
            ex1 = expm((1 - 2 * alf) * A)

            mat = np.array(bmat([[2 * alf * A, -R.T], [R, np.zeros((k, k))]]))
            E = np.array(
                bmat([[ex1 @ Y1.T @ Y, ex1 @ Y1.T @ Q],
                      [np.zeros_like(R), np.zeros((k, k))]]))

            ex2, fe2 = expm_frechet(mat, E)
            M = ex2[:d, :d]
            N = ex2[d:, :d]
            YMQN = (Y @ M + Q @ N)

            partA = asym((1 - 2 * alf) * expm_frechet(
                (1 - 2 * alf) * A, Y1.T @ YMQN)[1])

            partA += 2 * alf * asym(fe2[:d, :d])
            partR = -(fe2[:d, d:].T - fe2[d:, :d])

            return vec(partA, partR)
示例#18
0
def test_dexpm_2x2(random_2x2):
    X = random_2x2
    dX = np.random.random((10, 2, 2))

    dM_truth = np.zeros((10, 2, 2))
    for n in range(10):
        M_truth, dM_truth[n] = expm_frechet(X, dX[n])

    M, dM = dexpm_2x2(X, dX)

    assert np.allclose(M_truth, M)
    assert np.allclose(dM_truth, dM)
        def jac(v):
            eta_P, R = unvec(v)
            A = beta/alpha[1]*(X.Pinv@eta_P - [email protected])
            x_mat = np.array(
                np.bmat([[2*alf*A, -R.T], [R, zeros((k, k))]]))
            ex1 = expm(X.Pinv@eta_P)
            ex3 = expm((1-2*alf)*A)
            exmat = expm(x_mat)
            efPinvD = expm_frechet(
                X.Pinv@eta_P,
                X.P @ ex1 @ X.P - ex3.T @ exmat[:, :p].T @ YPY_rl @
                exmat[:, :p] @ ex3 @ X.P)
            efxmat = expm_frechet(
                x_mat,
                np.bmat([[
                    ex3 @ X.P @ ex1 @
                    ex3.T @ exmat[:, :p].T @ YPY_rl +
                    ex3 @ X.P @ ex1 @ ex3.T @ exmat[:, :p].T @ YPY_rl],
                         [np.zeros((k, p+k))]]))

            efA1 = expm_frechet(
                (1-2*alf)*A,
                (1-2*alf)*beta/alpha[1]*(
                    X.P @ ex1 @
                    ex3.T @ exmat[:, :p].T @ YPY_rl @ exmat[:, :p]))

            efA2 = expm_frechet(
                (1-2*alf)*A, exmat[:, :p].T @ YPY_rl @
                exmat[:, :p] @ ex3 @ X.P @ ex1)

            grP = 2*efPinvD[1] @ X.Pinv
            grP += -2*2*alf*beta/alpha[1]*(efxmat[1][:p, :p] @ X.Pinv -
                                           X.Pinv @ efxmat[1][:p, :p])
            grP += -2*efA1[1] @ X.Pinv + 2*X.Pinv @ efA1[1]
            grP += 2*(1-2*alf)*beta/alpha[1] * (
                ex3.T @ efA2[1]@ ex3.T @ X.Pinv - X.Pinv @ ex3.T @ efA2[1]@ ex3.T)
            grR = -2*(-efxmat[1][p:, :p] + efxmat[1][:p, p:].T)
            return vec(sym(grP), grR)
示例#20
0
    def test_expm_jacobian_vector_product(self):
        n = 4
        x = numpy.random.randn(n, n)
        E = numpy.random.randn(n, n)

        # use algopy to get the jacobian vector product
        ax = UTPM.init_jac_vec(x.flatten(), E.flatten())
        ay = expm(ax.reshape((n, n))).reshape((n*n,))
        g1 = UTPM.extract_jac_vec(ay)

        # compute the jacobian vector product directly using expm_frechet
        M = expm_frechet(x, E, compute_expm=False).flatten()

        assert_allclose(g1, M, rtol=1e-6)
示例#21
0
    def test_expm_jacobian_vector_product(self):
        n = 4
        x = numpy.random.randn(n, n)
        E = numpy.random.randn(n, n)

        # use algopy to get the jacobian vector product
        ax = UTPM.init_jac_vec(x.flatten(), E.flatten())
        ay = expm(ax.reshape((n, n))).reshape((n * n, ))
        g1 = UTPM.extract_jac_vec(ay)

        # compute the jacobian vector product directly using expm_frechet
        M = expm_frechet(x, E, compute_expm=False).flatten()

        assert_allclose(g1, M, rtol=1e-6)
示例#22
0
    def get_traj_stats(self, J_other):
        n = self.nstates

        # compute the observed initial distribution
        distn = J_other.sum(axis=1)

        # compute conditional expected dwell times
        dwell = np.zeros(n)
        for i in range(n):
            E = np.zeros((n, n), dtype=float)
            E[i, i] = 1
            interact = expm_frechet(self.Q, E, compute_expm=False)
            dwell[i] = (J_other * interact / self.P).sum()
        assert_allclose(dwell.sum(), 1)

        # compute conditional expected transition counts
        trans = np.zeros((n, n), dtype=float)
        for i in range(n):
            E = np.zeros((n, n), dtype=float)
            E[i, 1-i] = 1
            interact = expm_frechet(self.Q, self.Q*E, compute_expm=False)
            trans[i, 1-i] = (J_other * interact / self.P).sum()

        return distn, dwell, trans
示例#23
0
 def _expm_grad(op, grad):
     # We want the backward-mode gradient (left multiplication).
     # Let X be the NxN input matrix.
     # Let J(X) be the the N^2xN^2 complete Jacobian matrix of expm at X.
     # Let Y be the NxN previous gradient in the backward AD (left multiplication)
     # We have
     # unvec( ( vec(Y)^T . J(X) )^T )
     #   = unvec( J(X)^T . vec(Y) )
     #   = unvec( J(X^T) . vec(Y) )
     # where the last part (if I am not mistaken) holds in the case of the
     # exponential and other matrix power series.
     # It can be seen that this is now the forward-mode derivative
     # (right multiplication) applied to the Jacobian of the transpose.
     grad_func = lambda x, y: expm_frechet(x, y, compute_expm=False)
     return tf.py_func(grad_func, [tf.transpose(op.inputs[0]), grad],
                       tf.float64)
示例#24
0
    def test_expm_jacobian(self):
        n = 4
        x = numpy.random.randn(n, n)

        # use algopy to get the jacobian
        ax = UTPM.init_jacobian(x)
        ay = expm(ax)
        g1 = UTPM.extract_jacobian(ay)

        # compute the jacobian directly using expm_frechet
        M = numpy.zeros((n, n, n*n))
        ident = numpy.identity(n*n)
        for i in range(n*n):
            E = ident[i].reshape(n, n)
            M[:, :, i] = expm_frechet(x, E, compute_expm=False)

        assert_allclose(g1, M, rtol=1e-6)
示例#25
0
    def test_expm_jacobian(self):
        n = 4
        x = numpy.random.randn(n, n)

        # use algopy to get the jacobian
        ax = UTPM.init_jacobian(x)
        ay = expm(ax)
        g1 = UTPM.extract_jacobian(ay)

        # compute the jacobian directly using expm_frechet
        M = numpy.zeros((n, n, n * n))
        ident = numpy.identity(n * n)
        for i in range(n * n):
            E = ident[i].reshape(n, n)
            M[:, :, i] = expm_frechet(x, E, compute_expm=False)

        assert_allclose(g1, M, rtol=1e-6)
示例#26
0
def disc_d_state(A: np.ndarray, dA: np.ndarray, dt: float = 1.0) -> Tuple[np.ndarray, np.ndarray]:
    """Discretize the state matrix and its derivative

    Args:
        A: State matrix
        dA: Derivative state matrix
        dt: Sampling time

    Returns:
        2-elements tuple containing
            - Ad: Discrete state matrix
            - dAd: Derivative discrete state matrix
        """
    nj, nx, _ = dA.shape
    dAd = np.zeros((nj, nx, nx))
    for n in range(nj):
        if dA[n].any() or n == 0:
            Ad, dAd[n] = expm_frechet(A * dt, dA[n] * dt)

    return Ad, dAd
示例#27
0
    def _disc_dQ_mfd(self, dt: np.ndarray, QQ: np.ndarray, dA: np.ndarray,
                     dQQ: np.ndarray) -> Tuple[np.ndarray, np.ndarray]:
        """Discretization partial derivative of the diffusion matrix by MFD

        Args:
            dt: Sampling time
            QQ: Diffusion matrix
            dA: Jacobian state matrix
            dQQ: Jacobian diffusion matrix

        Returns:
            2-elements tuple containing
                - **QQd**: Process noise covariance matrix
                - **dQQd**: Jacobian process noise covariance matrix
        """

        N = dA.shape[0]

        AA = np.zeros((2 * self.nx, 2 * self.nx))
        AA[:self.nx, :self.nx] = self.A
        AA[:self.nx, self.nx:] = QQ
        AA[self.nx:, self.nx:] = -self.A.T

        dAA = np.zeros((N, 2 * self.nx, 2 * self.nx))
        dAA[:, :self.nx, :self.nx] = dA
        dAA[:, :self.nx, self.nx:] = dQQ
        dAA[:, self.nx:, self.nx:] = -dA.swapaxes(1, 2)

        dAAd = np.zeros_like(dAA)
        for n in range(N):
            if np.any(dAA[n]):
                AAd, dAAd[n] = expm_frechet(AA * dt, dAA[n] * dt)

        AdT = AAd[:self.nx, :self.nx].T
        QQd = AAd[:self.nx, self.nx:] @ AdT

        dAd = dAAd[:, :self.nx, :self.nx]
        dQQd = dAAd[:, :self.nx, self.
                    nx:] @ AdT + AAd[:self.nx, self.nx:] @ dAd.swapaxes(1, 2)

        return QQd, dQQd
示例#28
0
    def _compute_prop_grad(self, k, j, compute_prop=True):
        """
        Calculate the gradient of propagator wrt the control amplitude
        in the timeslot using the expm_frechet method
        The propagtor is calculated (almost) for 'free' in this method
        and hence it is returned if compute_prop==True
        Returns:
            [prop], prop_grad
        """
        dyn = self.parent

        if dyn.oper_dtype == Qobj:
            A = dyn._get_phased_dyn_gen(k).full()*dyn.tau[k]
            E = dyn._get_phased_ctrl_dyn_gen(k, j).full()*dyn.tau[k]
            if compute_prop:
                prop_dense, prop_grad_dense = la.expm_frechet(A, E)
                prop = Qobj(prop_dense, dims=dyn.dyn_dims)
                prop_grad = Qobj(prop_grad_dense,
                                            dims=dyn.dyn_dims)
            else:
                prop_grad_dense = la.expm_frechet(A, E, compute_expm=False)
                prop_grad = Qobj(prop_grad_dense,
                                            dims=dyn.dyn_dims)
        elif dyn.oper_dtype == np.ndarray:
            A = dyn._get_phased_dyn_gen(k)*dyn.tau[k]
            E = dyn._get_phased_ctrl_dyn_gen(k, j)*dyn.tau[k]
            if compute_prop:
                prop, prop_grad = la.expm_frechet(A, E)
            else:
                prop_grad = la.expm_frechet(A, E,
                                                    compute_expm=False)
        else:
            # Assuming some sparse matrix
            spcls = dyn._dyn_gen[k].__class__
            A = (dyn._get_phased_dyn_gen(k)*dyn.tau[k]).toarray()
            E = (dyn._get_phased_ctrl_dyn_gen(k, j)*dyn.tau[k]).toarray()
            if compute_prop:
                prop_dense, prop_grad_dense = la.expm_frechet(A, E)
                prop = spcls(prop_dense)
                prop_grad = spcls(prop_grad_dense)
            else:
                prop_grad_dense = la.expm_frechet(A, E, compute_expm=False)
                prop_grad = spcls(prop_grad_dense)

        if compute_prop:
            return prop, prop_grad
        else:
            return prop_grad
示例#29
0
def disc_d_diffusion_mfd(
    A: np.ndarray, Q: np.ndarray, dA: np.ndarray, dQ: np.ndarray, dt: float = 1.0
) -> Tuple[np.ndarray, np.ndarray]:
    """Discretize diffusion matrix and its derivative by matrix fraction decomposition

    Args:
        A: State matrix
        Q: Diffusion matrix
        dA: Derivative state matrix
        dQ: Derivative diffusion matrix
        dt: Sampling time

    Returns:
        2-elements tuple containing
            - Process noise covariance matrix
            - Derivative process noise covariance matrix
    """
    if not Q.any():
        return Q

    nj, nx, _ = dA.shape

    F = block_diag(A, -A.T)
    F[:nx, nx:] = Q
    Fd = expm(F * dt)[:nx, :]

    dF = np.zeros((nj, 2 * nx, 2 * nx))
    dF[:, :nx, :nx] = dA
    dF[:, :nx, nx:] = dQ
    dF[:, nx:, nx:] = -dA.swapaxes(1, 2)

    dFd = np.zeros((nj, nx, 2 * nx))
    for n in range(nj):
        if dF[n].any():
            dFd[n] = expm_frechet(F * dt, dF[n] * dt, compute_expm=False)[:nx, :]

    Fd12 = Fd[:, nx:]
    Fd11 = Fd[:, :nx].T

    return Fd12 @ Fd11, dFd[:, :, nx:] @ Fd11 + Fd12 @ dFd[:, :, :nx].swapaxes(1, 2)
示例#30
0
    def _compute_prop_grad(self, k, j, compute_prop=True):
        """
        Calculate the gradient of propagator wrt the control amplitude
        in the timeslot using the expm_frechet method
        The propagtor is calculated (almost) for 'free' in this method
        and hence it is returned if compute_prop==True
        Returns:
            [prop], prop_grad
        """
        dyn = self.parent

        if dyn.oper_dtype == Qobj:
            A = dyn._get_phased_dyn_gen(k).full() * dyn.tau[k]
            E = dyn._get_phased_ctrl_dyn_gen(j).full() * dyn.tau[k]
            if compute_prop:
                prop_dense, prop_grad_dense = la.expm_frechet(A, E)
                dyn._prop[k] = Qobj(prop_dense, dims=dyn.dyn_dims)
                dyn._prop_grad[k, j] = Qobj(prop_grad_dense, dims=dyn.dyn_dims)
            else:
                prop_grad_dense = la.expm_frechet(A, E, compute_expm=False)
                dyn._prop_grad[k, j] = Qobj(prop_grad_dense, dims=dyn.dyn_dims)
        elif dyn.oper_dtype == np.ndarray:
            A = dyn._get_phased_dyn_gen(k) * dyn.tau[k]
            E = dyn._get_phased_ctrl_dyn_gen(j) * dyn.tau[k]
            if compute_prop:
                dyn._prop[k], dyn._prop_grad[k, j] = la.expm_frechet(A, E)
            else:
                dyn._prop_grad[k, j] = la.expm_frechet(A,
                                                       E,
                                                       compute_expm=False)
        else:
            # Assuming some sparse matrix
            spcls = dyn._dyn_gen[k].__class__
            A = (dyn._get_phased_dyn_gen(k) * dyn.tau[k]).toarray()
            E = (dyn._get_phased_ctrl_dyn_gen(j) * dyn.tau[k]).toarray()
            if compute_prop:
                prop_dense, prop_grad_dense = la.expm_frechet(A, E)
                dyn._prop[k] = spcls(prop_dense)
                dyn._prop_grad[k, j] = spcls(prop_grad_dense)
            else:
                prop_grad_dense = la.expm_frechet(A, E, compute_expm=False)
                dyn._prop_grad[k, j] = spcls(prop_grad_dense)

        if compute_prop:
            return dyn._prop[k], dyn._prop_grad[k, j]
        else:
            return dyn._prop_grad[k, j]
 def test_expm_frechet(self):
     # a test of the basic functionality
     M = np.array([
         [1, 2, 3, 4],
         [5, 6, 7, 8],
         [0, 0, 1, 2],
         [0, 0, 5, 6],
         ], dtype=float)
     A = np.array([
         [1, 2],
         [5, 6],
         ], dtype=float)
     E = np.array([
         [3, 4],
         [7, 8],
         ], dtype=float)
     expected_expm = scipy.linalg.expm(A)
     expected_frechet = scipy.linalg.expm(M)[:2, 2:]
     for kwargs in ({}, {'method':'SPS'}, {'method':'blockEnlarge'}):
         observed_expm, observed_frechet = expm_frechet(A, E, **kwargs)
         assert_allclose(expected_expm, observed_expm)
         assert_allclose(expected_frechet, observed_frechet)
示例#32
0
def do_cythonized_em(T, root,
        edge_to_rate, edge_to_Q, root_distn1d,
        data_prob_pairs, guess_edge_to_rate):
    """
    Try the Cython implementation.
    def expectation_step(
            idx_t[:] csr_indices, # (nnodes-1,)
            idx_t[:] csr_indptr, # (nnodes+1,)
            cnp.float_t[:, :, :] transp, # (nnodes-1, nstates, nstates)
            cnp.float_t[:, :, :] transq, # (nnodes-1, nstates, nstates)
            cnp.float_t[:, :, :] interact_trans, # (nnodes-1, nstates, nstates)
            cnp.float_t[:, :, :] interact_dwell, # (nnodes-1, nstates, nstates)
            cnp.float_t[:, :, :] data, # (nsites, nnodes, nstates)
            cnp.float_t[:] root_distn, # (nstates,)
            cnp.float_t[:, :] trans_out, # (nsites, nnodes-1)
            cnp.float_t[:, :] dwell_out, # (nsites, nnodes-1)
            int validation=1,
            ):

    """
    # Define a toposort node ordering and a corresponding csr matrix.
    nodes = nx.topological_sort(T, [root])
    node_to_idx = dict((na, i) for i, na in enumerate(nodes))
    m = nx.to_scipy_sparse_matrix(T, nodes)

    # Stack the transition rate matrices into a single array.
    nnodes = len(nodes)
    nstates = root_distn1d.shape[0]
    n = nstates
    transq = np.empty((nnodes-1, nstates, nstates), dtype=float)
    for (na, nb), Q in edge_to_Q.items():
        edge_idx = node_to_idx[nb] - 1
        transq[edge_idx] = Q

    # Allocate a transition probability matrix array
    # and some interaction matrix arrays.
    transp = np.empty_like(transq)
    interact_trans = np.empty_like(transq)
    interact_dwell = np.empty_like(transq)

    # Stack the data into a single array,
    # and construct an array of site weights.
    nsites = len(data_prob_pairs)
    datas, probs = zip(*data_prob_pairs)
    site_weights = np.array(probs, dtype=float)
    data = np.empty((nsites, nnodes, nstates), dtype=float)
    for site_index, site_data in enumerate(datas):
        for i, na in enumerate(nodes):
            data[site_index, i] = site_data[na]

    # Initialize expectation arrays.
    trans_out = np.empty((nsites, nnodes-1), dtype=float)
    dwell_out = np.empty((nsites, nnodes-1), dtype=float)

    # Initialize the per-edge rate matrix scaling factor guesses.
    scaling_guesses = np.empty(nnodes-1, dtype=float)
    scaling_ratios = np.ones(nnodes-1, dtype=float)
    for (na, nb), rate in guess_edge_to_rate.items():
        eidx = node_to_idx[nb] - 1
        scaling_guesses[eidx] = rate

    # Pre-scale the rate matrix.
    transq *= scaling_guesses[:, None, None]

    # Do the EM iterations.
    nsteps = 1000
    for em_iteration_index in range(nsteps):

        # Scale the rate matrices according to the edge ratios.
        transq *= scaling_ratios[:, None, None]

        # Compute the probability transition matrix arrays
        # and the interaction matrix arrays.
        trans_indicator = np.ones((n, n)) - np.identity(n)
        dwell_indicator = np.identity(n)
        for edge in T.edges():
            na, nb = edge
            eidx = node_to_idx[nb] - 1
            Q = transq[eidx]
            #print(edge, 'Q:')
            #print(Q)
            transp[eidx] = expm(Q)
            interact_trans[eidx] = expm_frechet(
                    Q, Q * trans_indicator, compute_expm=False)
            interact_dwell[eidx] = expm_frechet(
                    Q, Q * dwell_indicator, compute_expm=False)

        # Compute the expectations.
        validation = 1
        expectation_step(
                m.indices, m.indptr,
                transp, transq,
                interact_trans, interact_dwell,
                data,
                root_distn1d,
                trans_out, dwell_out,
                validation,
                )

        # Compute the per-edge ratios.
        trans_sum = (trans_out * site_weights[:, None]).sum(axis=0)
        dwell_sum = (dwell_out * site_weights[:, None]).sum(axis=0)
        scaling_ratios = trans_sum / -dwell_sum
        scaling_guesses *= scaling_ratios

        # Report the guesses.
        if not (em_iteration_index+1) % 100:
            print(em_iteration_index+1)
            for edge in T.edges():
                na, nb = edge
                eidx = node_to_idx[nb] - 1
                print(edge, scaling_guesses[eidx])
            print()
示例#33
0
def disc_d_state_input_expm(
    A: np.ndarray,
    B: np.ndarray,
    dA: np.ndarray,
    dB: np.ndarray,
    dt: float = 1.0,
    order_hold: int = 0,
) -> Tuple[np.ndarray, np.ndarray, np.ndarray, np.ndarray, np.ndarray, np.ndarray]:
    """Discretize the state and input matrices, and their derivatives with the matrix exponential

    Args:
        A: State matrix
        B: Input matrix
        dA: Derivative state matrix
        dB: Derivative input matrix
        dt: Sampling time
        order_hold: zero order hold = 0 or first order hold = 1

    Returns:
        6-elements tuple containing
            - Ad: Discrete state matrix
            - B0d: Discrete input matrix (zero order hold)
            - B1d: Discrete input matrix (first order hold)
            - dAd: Derivative discrete state matrix
            - dB0d: Derivative discrete input matrix (zero order hold)
            - dB1d: Derivative discrete input matrix (first order hold)
    """
    nj, nx, nu = dB.shape

    if order_hold == 0:
        F = np.zeros((nx + nu, nx + nu))
        dF = np.zeros((nj, nx + nu, nx + nu))
        dFd = np.zeros((nj, nx + nu, nx + nu))

        F[:nx, :nx] = A
        F[:nx, nx:] = B
        dF[:, :nx, :nx] = dA
        dF[:, :nx, nx:] = dB

        for n in range(nj):
            if dF[n].any() or n == 0:
                Fd, dFd[n] = expm_frechet(F * dt, dF[n] * dt)

        Ad, B0d = np.split(Fd[:nx, :], indices_or_sections=[nx], axis=1)
        dAd, dB0d = np.split(dFd[:, :nx, :], indices_or_sections=[nx], axis=2)
        B1d = np.zeros((nx, nu))
        dB1d = np.zeros((nj, nx, nu))
    else:
        F = np.zeros((nx + 2 * nu, nx + 2 * nu))
        dF = np.zeros((nj, nx + 2 * nu, nx + 2 * nu))
        dFd = np.zeros((nj, nx + 2 * nu, nx + 2 * nu))

        F[:nx, :nx] = A
        F[:nx, nx : nx + nu] = B
        F[nx : nx + nu, nx + nu :] = np.eye(nu)
        dF[:, :nx, :nx] = dA
        dF[:, :nx, nx : nx + nu] = dB

        for n in range(nj):
            if dF[n].any() or n == 0:
                Fd, dFd[n] = expm_frechet(F * dt, dF[n] * dt)

        Ad, B0d, B1d = np.split(Fd[:nx, :], indices_or_sections=[nx, nx + nu], axis=1)
        dAd, dB0d, dB1d = np.split(dFd[:, :nx, :], indices_or_sections=[nx, nx + nu], axis=2)

    return Ad, B0d, B1d, dAd, dB0d, dB1d
示例#34
0
def dexp(X, G):
    """Evaluate the derivative of the matrix exponential at
    X in direction G.
    """
    return np.array([expm_frechet(X[i], G[i])[1] for i in range(X.shape[0])])
示例#35
0
    def dexp(self,
             direction: 'DenseOperator',
             tau: complex = 1,
             compute_expm: bool = False,
             method: str = "spectral",
             is_skew_hermitian: bool = False,
             epsilon: float = 1e-10) \
            -> Union['DenseOperator', Tuple['DenseOperator']]:
        """
        Frechet derivative of the matrix exponential.

        Parameters
        ----------
        direction: DenseOperator
            Direction in which the frechet derivative is calculated. Must be of
            the same shape as self.

        tau: complex
            The matrix is multiplied by tau before calculating the exponential.

        compute_expm: bool
            If true, then the matrix exponential is calculated and returned as
            well.

        method: string
            Numerical method used for the calculation of the matrix
            exponential.
            Currently the following are implemented:
            - 'Frechet': Uses the scipy linalg matrix exponential for
            simultaniously calculation of the frechet derivative expm_frechet
            - 'approx': Approximates the Derivative by finite differences.
            - 'first_order': First order taylor approximation
            - 'second_order': Second order taylor approximation
            - 'third_order': Third order taylor approximation
            - 'spectral': Use the self implemented spectral decomposition

        is_skew_hermitian: bool
            Only required, for the method 'spectral'. If set to True, then the
            matrix is assumed to be skew hermitian in the spectral
            decomposition.

        epsilon: float
            Width of the finite difference. Only relevant for the method
            'approx'.

        Returns
        -------
        prop: DenseOperator
            The matrix exponential. Only returned if compute_expm is True!
        prop_grad: DenseOperator
            The frechet derivative d exp(Ax + B)/dx at x=0 where A is the
            direction and B is the matrix stored in self.

        Raises
        ------
        NotImplementedError:
            If the method given as parameter is not implemented.

        """
        prop = None

        if type(direction) != DenseOperator:
            direction = DenseOperator(direction)

        if method == "Frechet":
            a = self.data * tau
            e = direction.data * tau
            if compute_expm:
                prop, prop_grad = la.expm_frechet(a, e, compute_expm=True)
            else:
                prop_grad = la.expm_frechet(a, e, compute_expm=False)

        elif method == "spectral":
            if compute_expm:
                prop, prop_grad = self._dexp_diagonalization(
                    direction=direction,
                    tau=tau,
                    is_skew_hermitian=is_skew_hermitian,
                    compute_expm=compute_expm)
            else:
                prop_grad = self._dexp_diagonalization(
                    direction=direction,
                    tau=tau,
                    is_skew_hermitian=is_skew_hermitian,
                    compute_expm=compute_expm)

        elif method == "approx":
            d_m = (self.data + epsilon * direction.data) * tau
            dprop = la.expm(d_m)
            prop = self.exp(tau)
            prop_grad = (dprop - prop) * (1 / epsilon)

        elif method == "first_order":
            if compute_expm:
                prop = self.exp(tau)
            prop_grad = direction.data * tau

        elif method == "second_order":
            if compute_expm:
                prop = self.exp(tau)
            prop_grad = direction.data * tau
            prop_grad += (self.data @ direction.data +
                          direction.data @ self.data) * (tau * tau * 0.5)

        elif method == "third_order":
            if compute_expm:
                prop = self.exp(tau)
            prop_grad = direction.data * tau
            prop_grad += (self.data @ direction.data +
                          direction.data @ self.data) * tau * tau * 0.5
            prop_grad += (self.data @ self.data @ direction.data +
                          direction.data @ self.data @ self.data +
                          self.data @ direction.data @ self.data) * (
                              tau * tau * tau * 0.16666666666666666)
        else:
            raise NotImplementedError('The specified method ' + method +
                                      "is not implemented!")
        if compute_expm:
            if type(prop) != DenseOperator:
                prop = DenseOperator(prop)
        if type(prop_grad) != DenseOperator:
            prop_grad = DenseOperator(prop_grad)
        if compute_expm:
            return prop, prop_grad
        else:
            return prop_grad
示例#36
0
    def _lti_jacobian_disc(self, dt):
        """Discretization of augmented LTI state-space model

        Args:
            dt: sampling time

        Returns:
            Ad: Discrete state matrix
            B0d, B1d: Discrete input matrix
            Qd: Upper Cholesky factor process noise covariance
            dAd: Derivative discrete state matrix
            dB0d, dB1d: Derivative discrete input matrix
            dQd: Derivative upper Cholesky factor process noise covariance
        """
        names = self.parameters.names_free
        N = len(names)

        # Discrete state matrix and its derivative
        self._AA[:N, :self.Nx, :self.Nx] = self.A
        self._AA[:N, self.Nx:, self.Nx:] = self.A
        self._AA[:N, self.Nx:, :self.Nx] = [self.dA[n] for n in names]
        dA = self._AA[:N, self.Nx:, :self.Nx]

        Ad = expm(self.A * dt)
        for i in range(N):
            self._AAd[i, :self.Nx, :self.Nx] = Ad
            self._AAd[i, self.Nx:, self.Nx:] = Ad
            if not np.all(dA[i, :, :] == 0):
                self._AAd[i, self.Nx:, :self.Nx] = (expm_frechet(
                    self.A * dt, dA[i, :, :] * dt, 'SPS', False))
        dAd = self._AAd[:N, self.Nx:, :self.Nx]

        if not np.all(self.Q == 0):
            dQ = np.asarray([self.dQ[k] for k in names])

            # transform to covariance matrix
            Q2 = self.Q.T @ self.Q
            dQ2 = dQ.swapaxes(1, 2) @ self.Q + self.Q.T @ dQ

            # covariance matrix of the process noise
            Qd2 = lyap(self.A, -Q2 + Ad @ Q2 @ Ad.T)

            eq = (-dA @ Qd2 - Qd2 @ dA.swapaxes(1, 2) - dQ2 + dAd @ Q2 @ Ad.T +
                  Ad @ dQ2 @ Ad.T + Ad @ Q2 @ dAd.swapaxes(1, 2))

            # Derivative process noise covariance
            dQd2 = np.asarray([lyap(self.A, eq[i, :, :]) for i in range(N)])

            # Get Cholesky factor
            Qd = pseudo_cholesky(Qd2)
            tmp = solve(Qd.T, solve(Qd.T, dQd2).swapaxes(1, 2))
            dQd = (np.triu(tmp, 1) + self._Ixx[0, :self.Nx, :self.Nx] * 0.5 *
                   tmp.diagonal(0, 1, 2)[:, np.newaxis, :]) @ Qd
        else:
            Qd = self._0xx[0, :, :]
            dQd = self._0xx[:N, :, :]

        if self.Nu != 0:
            # Discrete input matrices and their derivatives
            self._BB[:N, :self.Nx, :] = self.B
            self._BB[:N, self.Nx:, :] = [self.dB[k] for k in names]

            AA = self._AA[:N, :, :]
            AAd = self._AAd[:N, :, :]
            BB = self._BB[:N, :, :]
            bis = solve(AA, AAd - self._Ixx[:N, :, :])
            BB0d = bis @ BB
            B0d = BB0d[0, :self.Nx, :]
            dB0d = BB0d[:, self.Nx:, :]

            if self.hold_order == 'foh':
                BBd1_free = solve(AA, -bis + AAd * dt) @ BB
                B1d = BBd1_free[0, :self.Nx, :]
                dB1d = BBd1_free[:, self.Nx:, :]
            else:
                B1d = self._0xu[0, :, :]
                dB1d = self._0xu[:N, :, :]
        else:
            B0d = self._0xu[0, :, :]
            dB0d = self._0xu[:N, :, :]
            B1d = B0d
            dB1d = dB0d

        return Ad, B0d, B1d, Qd, dAd, dB0d, dB1d, dQd
示例#37
0
def em_function(
        T, node_to_idx, site_weights,
        m,
        transq_unscaled,
        data,
        root_distn1d,
        mem,
        use_log_scale,
        scale,
        ):
    """
    This is purely for edge rate scale and does not consider other parameters.

    Parameters
    ----------
    T : x
        x
    node_to_idx : x
        x
    site_weights : x
        x
    m : x
        x
    transq_unscaled : x
        x
    data : x
        x
    root_distn1d : x
        x
    mem : x
        x
    use_log_scale : bool
        True if the scale argument is in logarithmic units.
    scale : 1d float ndarray
        Edge rate scaling factors, possibly in logarithmic units.

    Returns
    -------
    scale : 1d float ndarray
        Updated edge rate scaling factors, possibly in logarithmic units.

    """
    #TODO improve docstring and add unit tests

    # Transform the scaling factors if necessary.
    if use_log_scale:
        log_scale = scale
        scale = np.exp(scale)

    # Unpack some stuff.
    nsites, nnodes, nstates = data.shape

    # Scale the rate matrices according to the edge ratios.
    # Use in-place operations to avoid unnecessary memory copies.
    #transq = transq_unscaled * scale[:, None, None]
    mem.transq[...] = transq_unscaled
    mem.transq *= scale[:, None, None]

    # Compute the probability transition matrix arrays
    # and the interaction matrix arrays.
    for edge in T.edges():
        na, nb = edge
        eidx = node_to_idx[nb] - 1
        Q = mem.transq[eidx]
        mem.transp[eidx] = expm(Q)
        mem.interact_trans[eidx] = expm_frechet(
                Q, Q * mem.trans_indicator, compute_expm=False)
        mem.interact_dwell[eidx] = expm_frechet(
                Q, Q * mem.dwell_indicator, compute_expm=False)

    # Compute the expectations using Cython.
    validation = 0
    expectation_step(
            m.indices, m.indptr,
            mem.transp, mem.transq,
            mem.interact_trans, mem.interact_dwell,
            data,
            root_distn1d,
            mem.trans_out, mem.dwell_out,
            validation,
            )

    # Compute the per-edge ratios.
    trans_sum = (mem.trans_out * site_weights[:, None]).sum(axis=0)
    dwell_sum = (mem.dwell_out * site_weights[:, None]).sum(axis=0)
    if use_log_scale:
        log_scaling_ratios = np.log(trans_sum) - np.log(-dwell_sum)
        return log_scale + log_scaling_ratios
    else:
        scaling_ratios = trans_sum / -dwell_sum
        return scale * scaling_ratios
示例#38
0
def do_em(T, root, edge_to_rate, edge_to_Q, root_distn1d,
        data_prob_pairs, guess_edge_to_rate):
    # Extract the number of states.
    n = root_distn1d.shape[0]

    # Do the EM iterations.
    nsteps = 3
    for em_iteration_index in range(nsteps):
        
        # Compute the scaled edge-specific transition rate matrices.
        edge_to_scaled_Q = {}
        for edge in T.edges():
            rate = guess_edge_to_rate[edge]
            Q = edge_to_Q[edge]
            edge_to_scaled_Q[edge] = rate * Q

        # Compute the edge-specific transition probability matrices.
        edge_to_P = {}
        for edge in T.edges():
            scaled_Q = edge_to_scaled_Q[edge]
            #print(edge, 'Q:')
            #print(scaled_Q)
            P = expm(scaled_Q)
            edge_to_P[edge] = P

        # For each edge, compute the interaction matrices
        # corresponding to all transition counts and dwell times.
        trans_indicator = np.ones((n, n)) - np.identity(n)
        dwell_indicator = np.identity(n)
        edge_to_interact_trans = {}
        edge_to_interact_dwell = {}
        for edge in T.edges():

            # extract edge-specific transition matrices
            Q = edge_to_scaled_Q[edge]
            P = edge_to_P[edge]

            # compute the transition interaction matrix
            interact = expm_frechet(Q, Q * trans_indicator, compute_expm=False)
            edge_to_interact_trans[edge] = interact

            # compute the dwell interaction matrix
            interact = expm_frechet(Q, Q * dwell_indicator, compute_expm=False)
            edge_to_interact_dwell[edge] = interact

        # Initialize edge-specific summaries.
        edge_to_trans_expectation = defaultdict(float)
        edge_to_dwell_expectation = defaultdict(float)

        # Compute the edge-specific summaries
        # conditional on the current edge-specific rate guesses
        # and on the leaf state distribution computed
        # from the true edge-specific rates.
        for node_to_data_fvec1d, lhood in data_prob_pairs:

            # Compute the joint endpoint state distribution for each edge.
            edge_to_J = get_edge_to_distn2d(
                    T, edge_to_P, root, root_distn1d, node_to_data_fvec1d)
            
            # For each edge, compute the transition expectation contribution
            # and compute the dwell expectation contribution.
            # These will be scaled by the data likelihood.
            for edge in T.edges():

                # extract some edge-specific matrices
                P = edge_to_P[edge]
                J = edge_to_J[edge]
                #print(edge)
                #print(P)
                #print(J)
                #print()

                # transition contribution
                interact = edge_to_interact_trans[edge]
                total = 0
                for i in range(n):
                    for j in range(n):
                        if J[i, j]:
                            coeff = J[i, j] / P[i, j]
                            total += coeff * interact[i, j]
                edge_to_trans_expectation[edge] += lhood * total

                # dwell contribution
                interact = edge_to_interact_dwell[edge]
                total = 0
                for i in range(n):
                    for j in range(n):
                        if J[i, j]:
                            coeff = J[i, j] / P[i, j]
                            total += coeff * interact[i, j]
                edge_to_dwell_expectation[edge] += lhood * total

        # According to EM, update each edge-specific rate guess
        # using a ratio of transition and dwell expectations.
        for edge in T.edges():
            trans = edge_to_trans_expectation[edge]
            dwell = edge_to_dwell_expectation[edge]
            ratio = trans / -dwell
            old_rate = guess_edge_to_rate[edge] 
            new_rate = old_rate * ratio
            guess_edge_to_rate[edge] = new_rate
            #print(edge, trans, dwell, ratio, old_rate, new_rate)
            print(edge, trans, dwell, new_rate)
        print()
示例#39
0
    def _lti_jacobian_disc(
        self, dt: float, dA: np.ndarray, dB: np.ndarray, dQ: np.ndarray
    ) -> Tuple[np.ndarray, np.ndarray, np.ndarray, np.ndarray, np.ndarray,
               np.ndarray, np.ndarray, np.ndarray, ]:
        """Discretization of augmented LTI state-space model

        Args:
            dt: Sampling time
            dA: Jacobian state matrix
            dB: Jacobian input matrix
            dQ: Jacobian Wiener process scaling matrix

        Returns:
            8-elements tuple containing
                - **Ad**: Discrete state matrix
                - **B0d**: Discrete input matrix (zero order hold)
                - **B1d**: Discrete input matrix (first order hold)
                - **Qd**: Upper Cholesky factor of the process noise covariance matrix
                - **dAd**: Jacobian discrete state matrix
                - **dB0d**: Jacobian discrete input matrix (zero order hold)
                - **dB1d**: Jacobian discrete input matrix (first order hold)
                - **dQd**: Jacobian of the upper Cholesky factor of the process noise covariance
        """

        N = dA.shape[0]

        if self.nu == 0:
            B0d = np.zeros((self.nx, self.nu))
            B1d = B0d

            Ad = expm(self.A * dt)
            dAd = np.zeros((N, self.nx, self.nx))
            for n in range(N):
                if np.any(dA[n]):
                    dAd[n] = expm_frechet(self.A * dt,
                                          dA[n] * dt,
                                          compute_expm=False)

            dB0d = np.zeros((N, self.nx, self.nu))
            dB1d = dB0d

        else:
            if self.hold_order == 0:

                AA = np.zeros((self.nx + self.nu, self.nx + self.nu))
                AA[:self.nx, :self.nx] = self.A
                AA[:self.nx, self.nx:] = self.B

                dAA = np.zeros((N, self.nx + self.nu, self.nx + self.nu))
                dAA[:, :self.nx, :self.nx] = dA
                dAA[:, :self.nx, self.nx:] = dB

                dAAd = np.zeros_like(dAA)
                for n in range(N):
                    if np.any(dAA[n]):
                        AAd, dAAd[n] = expm_frechet(AA * dt, dAA[n] * dt)

                Ad = AAd[:self.nx, :self.nx]
                B0d = AAd[:self.nx, self.nx:]
                dAd = dAAd[:, :self.nx, :self.nx]
                dB0d = dAAd[:, :self.nx, self.nx:]
                B1d = np.zeros((self.nx, self.nu))
                dB1d = np.zeros((N, self.nx, self.nu))
            else:
                AA = np.zeros((self.nx + 2 * self.nu, self.nx + 2 * self.nu))
                AA[:self.nx, :self.nx] = self.A
                AA[:self.nx, self.nx:self.nx + self.nu] = self.B
                AA[self.nx:self.nx + self.nu,
                   self.nx + self.nu:] = np.eye(self.nu)

                dAA = np.zeros(
                    (N, self.nx + 2 * self.nu, self.nx + 2 * self.nu))
                dAA[:, :self.nx, :self.nx] = dA
                dAA[:, :self.nx, self.nx:self.nx + self.nu] = dB

                dAAd = np.zeros_like(dAA)
                for n in range(N):
                    if np.any(dAA[n]):
                        AAd, dAAd[n] = expm_frechet(AA * dt, dAA[n] * dt)

                Ad = AAd[:self.nx, :self.nx]
                B0d = AAd[:self.nx, self.nx:self.nx + self.nu]
                B1d = AAd[:self.nx, self.nx + self.nu:]

                dAd = dAAd[:, :self.nx, :self.nx]
                dB0d = dAAd[:, :self.nx, self.nx:self.nx + self.nu]
                dB1d = dAAd[:, :self.nx, self.nx + self.nu:]

        if np.any(self.Q):

            # transform to covariance matrix
            QQ = self.Q.T @ self.Q
            dQQ = dQ.swapaxes(1, 2) @ self.Q + self.Q.T @ dQ

            if self.method == 'mfd':
                QQd, dQQd = self._disc_dQ_mfd(dt, QQ, dA, dQQ)
            elif self.method == 'lyapunov':
                QQd, dQQd = self._disc_dQ_lyapunov(dt, QQ, dA, dQQ, Ad, dAd)
            else:
                raise ValueError('`Invalid discretization method`')

            Qd = nearest_cholesky(QQd)
            try:
                tmp = np.linalg.solve(
                    Qd.T,
                    np.linalg.solve(Qd.T, dQQd).swapaxes(1, 2))
            except (LinAlgError, RuntimeError):
                inv_Qd = np.linalg.pinv(Qd)
                tmp = inv_Qd.T @ dQQd @ inv_Qd

            dQd = (np.triu(tmp, 1) + np.eye(self.nx) / 2 *
                   tmp.diagonal(0, 1, 2)[:, None, :]) @ Qd
        else:
            Qd = np.zeros((self.nx, self.nx))
            dQd = np.zeros((N, self.nx, self.nx))

        return Ad, B0d, B1d, Qd, dAd, dB0d, dB1d, dQd
示例#40
0
def s_expm(s_y, y, a):
    return sla.expm_frechet(a, s_y.T, compute_expm=False).T