Ejemplo n.º 1
0
def one_particle(H: sparse.coo_matrix) -> pyquil.paulis.PauliSum:
    """
    Generates a PauliSum(pyquil) given a Hamiltonian-matrix.

    Creates a Hamiltonian operator from a (sparse) matrix. This function uses
    a one-particle formulation and, thus, requires N qubits for an N-dimensional
    Hilbert space.

    @author: Axel, Joel

    :param H: An array (array_like) representing the hamiltonian.
    :return: Hamiltonian as PyQuil PauliSum.
    """
    # Create the Hamiltonian with a and a^dagger
    Hamiltonian = FermionOperator()
    if sparse.issparse(H):
        H = H.tocoo()
        for i, j, data in zip(H.row, H.col, H.data):
            Hamiltonian += data * FermionOperator(((int(i), 1), (int(j), 0)))
    else:
        if not isinstance(H, np.ndarray):
            H = np.asanyarray(H)
        for i in range(H.shape[0]):
            for j in range(H.shape[1]):
                Hamiltonian += H[i, j] * FermionOperator(((i, 1), (j, 0)))

    Hamiltonian = jordan_wigner(Hamiltonian)
    Hamiltonian = qubitop_to_pyquilpauli(Hamiltonian)
    return Hamiltonian
Ejemplo n.º 2
0
def multi_particle(H: sparse.coo_matrix) -> pyquil.paulis.PauliSum:
    """
    Creates a Qubit-operator from a (sparse) matrix. This function uses
    (almost) all states and, thus, requires (approximately) log(N) qubits
    for an N-dimensional Hilbert space.

    The idea for converting a matrix element to an operator is to raise/lower
    the qubits differing between two states to convert one basis-state to the
    other. The qubits that are not negated must be checked to have the correct
    value (using an analogue of the counting operator) to not get extra terms
    (one could perhaps allow for extra terms and compensate for them later?).

    0 = up
    1 = down

    (1+Zi)/2 checks that qubit i is 0 (up)
    (1-Zi)/2 checks that qubit i is 1 (down)
    (Xi+1j*Yi)/2 lowers qubit from 1 (down) to 0 (up)
    (Xi-1j*Yi)/2 raises qubit from 0 (up) to 1 (down)

    @author: Joel

    :param H: An array (array-like) representing the hamiltonian.
    :return: Hamiltonian as PyQuil PauliSum.
    """
    # Convert to sparse coo_matrix
    if not sparse.issparse(H):
        H = sparse.coo_matrix(H)
    elif H.getformat() != "coo":
        H = H.tocoo()
    # The main part of the function
    H_op = QubitOperator()
    for i, j, data in zip(H.row, H.col, H.data):
        new_term = QubitOperator(())  # = I
        for qubit in range(int.bit_length(H.shape[0] - 1)):
            if (i ^ j) & (1 << qubit):
                # lower/raise qubit
                new_term *= QubitOperator((qubit, "X"), 1 / 2) + \
                            QubitOperator((qubit, "Y"),
                                          1j * (int(
                                              j & (1 << qubit) != 0) - 1 / 2))
            else:
                # check that qubit has correct value (same as i and j)
                new_term *= QubitOperator((), 1 / 2) + \
                            QubitOperator((qubit, "Z"),
                                          1 / 2 - int(j & (1 << qubit) != 0))
        H_op += data * new_term
    return qubitop_to_pyquilpauli(H_op)
Ejemplo n.º 3
0
    def compute_transition_matrix(self,
                                  knn: sparse.coo_matrix,
                                  x: np.ndarray,
                                  v: np.ndarray,
                                  epsilon: float = 0.0,
                                  reverse: bool = False) -> sparse.csr_matrix:
        """
		Compute a right-stochastic matrix representing transition probabilities from each node
		
		Args:
			knn        KNN graph (n_cells, n_cells)
			x          Embedding positions (n_cells, n_dims)
			v          Velocities on the embedding (n_cells, n_dims)
			reverse    Compute the reverse transition matrix (for backwards diffusion)

		Remarks:
			Computes a Markov transition matrix for the KNN graph. The probability of transition along an edge
			is proportional to the scalar projection of the velocity vector onto that edge, times the reciprocal
			of the edge length. Edges that get negative scalar projections are clipped to zero and the total
			non-zero outgoing edges are normalized to a sum of 1.0.
		"""
        # vertices for each edge
        knn = knn.tocoo()
        (v0, v1) = (knn.row, knn.col)

        # calculate edge unit vectors
        uv = x[v1] - x[
            v0]  # Vector corresponding to an edge from v0 to v1, shape (n_edges, n_dims)
        norms = np.linalg.norm(uv, axis=1)
        uv = uv / norms[:, None]  # Convert to unit vector

        # Project the velocity vectors onto edges, and clip to zero
        scalar_projection = np.array([a.dot(b) for a, b in zip(v[v0], uv)
                                      ])  # Shape: (n_edges)
        if reverse:
            scalar_projection = -scalar_projection
        scalar_projection += epsilon
        # scalar_projection += scalar_projection.min()
        np.clip(scalar_projection, a_min=0, a_max=None, out=scalar_projection)

        # Calculate transition probabilities
        p = scalar_projection * (1 / norms)
        tr = normalize(sparse.coo_matrix((p, (v0, v1))).tocsr(),
                       axis=1,
                       norm='l1')
        return tr