Ejemplo n.º 1
0
def test_mutation_free_estimation():
    """
    Make sure the estimation routines do not mutate the programs the user sends
    This is accomplished by a deep copy in `estimate_pauli_sum'.
    """
    prog = Program().inst(I(0))
    pauli_sum = sX(0)  # measure in the X-basis

    # set up fake QVM
    fakeQVM = Mock(spec=QVMConnection())
    fakeQVM.run = Mock(return_value=[[0], [1]])

    expected_value, estimator_variance, total_shots = \
        estimate_locally_commuting_operator(prog, PauliSum([pauli_sum]),
                                            1.0E-3, quantum_resource=fakeQVM)

    # make sure RY(-pi/2) 0\nMEASURE 0 [0] was not added to the program the
    # user sees
    assert prog.out() == 'I 0\n'
def test_QAOACostFunctionOnWFSim_get_wavefunction():
    sim = WavefunctionSimulator()
    # ham = PauliSum.from_compact_str("0.7*Z0*Z1 + 1.2*Z0*Z2")
    term1 = PauliTerm("Z", 0, 0.7) * PauliTerm("Z", 1)
    term2 = PauliTerm("Z", 0, 1.2) * PauliTerm("Z", 2)
    ham = PauliSum([term1, term2])
    timesteps = 2
    params = StandardWithBiasParams\
        .linear_ramp_from_hamiltonian(ham, timesteps)
    cost_function = QAOACostFunctionOnWFSim(ham,
                                            params=params,
                                            sim=sim,
                                            scalar_cost_function=True,
                                            nshots=100)
    wf = cost_function.get_wavefunction(params.raw())
    assert np.allclose(wf.probabilities(),
                       np.array([0.01, 0.308, 0.053, 0.13,
                                 0.13, 0.053, 0.308, 0.01]),
                       rtol=1e-2, atol=0.005)
Ejemplo n.º 3
0
    def get_operator_expectation_value(
            self, state_circuit: Circuit,
            operator: QubitPauliOperator) -> complex:
        """Calculates the expectation value of the given circuit with respect to the
        operator using the built-in QVM functionality

        :param state_circuit: Circuit that generates the desired state
            :math:`\\left|\\psi\\right>`.
        :type state_circuit: Circuit
        :param operator: Operator :math:`H`.
        :type operator: QubitPauliOperator
        :return: :math:`\\left<\\psi | H | \\psi \\right>`
        :rtype: complex
        """
        prog = tk_to_pyquil(state_circuit)
        pauli_sum = PauliSum([
            self._gen_PauliTerm(term, coeff)
            for term, coeff in operator._dict.items()
        ])
        return complex(self._sim.expectation(prog, pauli_sum))
def create_cost_hamiltonian(weights):
    """
    Translating the distances between cities into the cost hamiltonian.
    """
    cost_operators = []
    number_of_cities = weights.shape[0]
    for t in range(number_of_cities - 1):
        for city_1 in range(number_of_cities):
            for city_2 in range(number_of_cities):

                # If these aren't the same city and they have an edge connecting them
                distance = weights[city_1, city_2]
                if city_1 != city_2 and distance != 0.0:
                    qubit_1 = t * number_of_cities + city_1
                    qubit_2 = (t + 1) * number_of_cities + city_2
                    cost_operators.append(
                        PauliTerm("Z", qubit_1, distance) *
                        PauliTerm("Z", qubit_2))
    cost_hamiltonian = [PauliSum(cost_operators)]
    return cost_hamiltonian
Ejemplo n.º 5
0
def test_pauli_sum_from_str():
    # this also tests PauliTerm.from_compact_str() since it gets called
    Sum = (1.5 + .5j) * sX(0) * sZ(2) + 0.7 * sZ(1)
    another_str = "(1.5 + 0.5j)*X0*Z2+.7*Z1"
    assert PauliSum.from_compact_str(str(Sum)) == Sum
    assert PauliSum.from_compact_str(Sum.compact_str()) == Sum
    assert PauliSum.from_compact_str(another_str) == Sum

    # test sums of length one
    Sum = PauliSum([1 * sY(0) * sY(1)])
    the_str = "1*Y0*Y1"
    assert PauliSum.from_compact_str(the_str) == Sum

    # test sums containing the identity
    Sum = (1.5 + .5j) * sX(0) * sZ(2) + 0.7 * sI(1)
    the_str = "(1.5 + 0.5j)*X0*Z2+.7*I"
    assert PauliSum.from_compact_str(the_str) == Sum

    # test the simplification (both in sums and products)
    Sum = PauliSum([2 * sY(1)])
    the_str = "1*Y0*X0 + (0+1j)*Z0 + 2*Y1"
    assert PauliSum.from_compact_str(the_str) == Sum
Ejemplo n.º 6
0
def lifted_pauli(pauli_sum: Union[PauliSum, PauliTerm],
                 qubits: List[int]) -> np.ndarray:
    """
    Takes a PauliSum object along with a list of
    qubits and returns a matrix corresponding the tensor representation of the
    object.

    Useful for generating the full Hamiltonian after a particular fermion to
    pauli transformation. For example:

    Converting a PauliSum X0Y1 + Y1X0 into the matrix

    .. code-block:: python

       [[ 0.+0.j,  0.+0.j,  0.+0.j,  0.-2.j],
        [ 0.+0.j,  0.+0.j,  0.+0.j,  0.+0.j],
        [ 0.+0.j,  0.+0.j,  0.+0.j,  0.+0.j],
        [ 0.+2.j,  0.+0.j,  0.+0.j,  0.+0.j]]

    Developer note: Quil and the QVM like qubits to be ordered such that qubit 0 is on the right.
    Therefore, in ``qubit_adjacent_lifted_gate``, ``lifted_pauli``, and ``lifted_state_operator``,
    we build up the lifted matrix by performing the kronecker product from right to left.

    :param pauli_sum: Pauli representation of an operator
    :param qubits: list of qubits in the order they will be represented in the resultant matrix.
    :return: matrix representation of the pauli_sum operator
    """
    if isinstance(pauli_sum, PauliTerm):
        pauli_sum = PauliSum([pauli_sum])

    n_qubits = len(qubits)
    result_hilbert = np.zeros((2**n_qubits, 2**n_qubits), dtype=np.complex128)
    # left kronecker product corresponds to the correct basis ordering
    for term in pauli_sum.terms:
        term_hilbert = np.array([1])
        for qubit in qubits:
            term_hilbert = np.kron(QUANTUM_GATES[term[qubit]], term_hilbert)

        result_hilbert += term_hilbert * term.coefficient

    return result_hilbert
Ejemplo n.º 7
0
def test_qaoa_on_qvm():
    # ham = PauliSum.from_compact_str("(-1.0)*Z0*Z1 + 0.8*Z0 + (-0.5)*Z1")
    term1 = PauliTerm("Z", 0, -1) * PauliTerm("Z", 1)
    term2 = PauliTerm("Z", 0, 0.8)
    term3 = PauliTerm("Z", 1, -0.5)
    ham = PauliSum([term1, term2, term3])
    params = FourierParams.linear_ramp_from_hamiltonian(ham, n_steps=10, q=2)
    p0 = params.raw()
    cost_fun = QAOACostFunctionOnQVM(ham,
                                     params,
                                     "2q-qvm",
                                     scalar_cost_function=True,
                                     nshots=4,
                                     base_numshots=50)
    out = minimize(cost_fun,
                   p0,
                   tol=2e-1,
                   method="Cobyla",
                   options={"maxiter": 100})
    assert np.allclose(out["fun"], -1.3, rtol=1.1)
    assert out["success"]
Ejemplo n.º 8
0
def test_qaoa_on_wfsim():
    # ham = PauliSum.from_compact_str("(-1.0)*Z0*Z1 + 0.8*Z0 + (-0.5)*Z1")
    term1 = PauliTerm("Z", 0, -1) * PauliTerm("Z", 1)
    term2 = PauliTerm("Z", 0, 0.8)
    term3 = PauliTerm("Z", 1, -0.5)
    ham = PauliSum([term1, term2, term3])

    params = FourierWithBiasParams.linear_ramp_from_hamiltonian(ham,
                                                                n_steps=10,
                                                                q=2)
    p0 = params.raw()
    cost_fun = QAOACostFunctionOnWFSim(ham, params, scalar_cost_function=True)
    out = minimize(cost_fun,
                   p0,
                   tol=1e-3,
                   method="Cobyla",
                   options={"maxiter": 500})
    wf = cost_fun.get_wavefunction(params.raw())
    assert np.allclose(out["fun"], -1.3, rtol=1.1)
    assert out["success"]
    assert np.allclose(wf.probabilities(), [0, 0, 0, 1], rtol=1.5, atol=0.05)
    def create_penalty_operators_for_qubit_range(self, range_of_qubits):
        cost_operators = []
        tsp_matrix = TSP_utilities.get_tsp_matrix(self.nodes_array)
        weight = -100 * np.max(tsp_matrix)
        # weight = -0.5
        for i in range_of_qubits:
            if i == range_of_qubits[0]:
                z_term = PauliTerm("Z", i, weight)
                all_ones_term = PauliTerm("I", 0, 0.5 * weight) - PauliTerm(
                    "Z", i, 0.5 * weight)
            else:
                z_term = z_term * PauliTerm("Z", i)
                all_ones_term = all_ones_term * (PauliTerm("I", 0, 0.5) -
                                                 PauliTerm("Z", i, 0.5))

        z_term = PauliSum([z_term])
        cost_operators.append(
            PauliTerm("I", 0, weight) - z_term +
            self.all_ones_coefficient * all_ones_term)

        return cost_operators
Ejemplo n.º 10
0
def test_imaginary_removal():
    """
    remove terms with imaginary coefficients from a pauli sum
    """
    test_term = 0.25 * sX(1) * sZ(2) * sX(3) + 0.25j * sX(1) * sZ(2) * sY(3)
    test_term += -0.25j * sY(1) * sZ(2) * sX(3) + 0.25 * sY(1) * sZ(2) * sY(3)
    true_term = 0.25 * sX(1) * sZ(2) * sX(3) + 0.25 * sY(1) * sZ(2) * sY(3)
    assert remove_imaginary_terms(test_term) == true_term

    test_term = (0.25 + 1j) * sX(0) * sZ(2) + 1j * sZ(2)
    # is_identity in pyquil apparently thinks zero is identity
    assert remove_imaginary_terms(test_term) == 0.25 * sX(0) * sZ(2)

    test_term = 0.25 * sX(0) * sZ(2) + 1j * sZ(2)
    assert remove_imaginary_terms(test_term) == PauliSum([0.25 * sX(0) * sZ(2)])

    with pytest.raises(TypeError):
        remove_imaginary_terms(5)

    with pytest.raises(TypeError):
        remove_imaginary_terms(sX(0))
Ejemplo n.º 11
0
def test_measure_observables_many_progs(forest):
    expts = [
        ExperimentSetting(sI(), o1 * o2)
        for o1, o2 in itertools.product([sI(0), sX(0), sY(0), sZ(0)], [sI(1), sX(1), sY(1), sZ(1)])
    ]

    qc = get_qc('2q-qvm')
    qc.qam.random_seed = 51
    for prog in _random_2q_programs():
        suite = TomographyExperiment(expts, program=prog, qubits=[0, 1])
        assert len(suite) == 4 * 4
        gsuite = group_experiments(suite)
        assert len(gsuite) == 3 * 3  # can get all the terms with I for free in this case

        wfn = WavefunctionSimulator()
        wfn_exps = {}
        for expt in expts:
            wfn_exps[expt] = wfn.expectation(gsuite.program, PauliSum([expt.out_operator]))

        for res in measure_observables(qc, gsuite, n_shots=1_000):
            np.testing.assert_allclose(wfn_exps[res.setting], res.expectation, atol=0.1)
Ejemplo n.º 12
0
def random_hamiltonian(reg: List[Union[int, QubitPlaceholder]]) -> PauliSum:
    """
    Creates a random cost hamiltonian, diagonal in the computational basis:

     - Randomly selects which qubits that will have a bias term, then assigns
       them a bias coefficient.
     - Randomly selects which qubit pairs will have a coupling term, then
       assigns them a coupling coefficient.

     In both cases, the random coefficient is drawn from the uniform
     distribution on the interval [0,1).

    Parameters
    ----------
    reg:
        register to build the hamiltonian on.

    Returns
    -------
    PauliSum:
        A hamiltonian with random couplings and biases, as a PauliSum object.

    """
    hamiltonian = []

    n_biases = np.random.randint(len(reg))
    bias_qubits = random.sample(reg, n_biases)
    bias_coeffs = np.random.rand(n_biases)

    for qubit, coeff in zip(bias_qubits, bias_coeffs):
        hamiltonian.append(PauliTerm("Z", qubit, coeff))

    for q1, q2 in itertools.combinations(reg, 2):
        are_coupled = np.random.randint(2)
        if are_coupled:
            couple_coeff = np.random.rand()
            hamiltonian.append(
                PauliTerm("Z", q1, couple_coeff) * PauliTerm("Z", q2))

    return PauliSum(hamiltonian)
Ejemplo n.º 13
0
def hamiltonian_from_distance_matrix(dist, biases=None) -> PauliSum:
    """
    Generates a Hamiltonian from a distance matrix and a numpy array of single qubit bias terms where the i'th indexed value
    of in biases is applied to the i'th qubit.

    Parameters
    ----------
    dist:
        A 2-dimensional square matrix where entries in row i, column j represent the distance between node i and node j.
    biases:
        A dictionary of floats, with keys indicating the qubits with bias terms, and corresponding values being the bias coefficients.

    Returns
    -------
    hamiltonian:
        A PauliSum object modelling the Hamiltonian of the system
    """

    pauli_list = list()
    m, n = dist.shape

    #allows tolerance for both matrices and dataframes
    if isinstance(dist, pd.DataFrame):
        dist = dist.values

    if biases:
        if not isinstance(biases, type(dict())):
            raise ValueError('biases must be of type dict()')
        for key in biases:
            term = PauliTerm('Z', key, biases[key])
            pauli_list.append(term)

        # pairwise interactions
    for i in range(m):
        for j in range(n):
            if i < j:
                term = PauliTerm('Z', i, dist[i][j]) * PauliTerm('Z', j)
                pauli_list.append(term)

    return PauliSum(pauli_list)
def maxcut_qaoa(graph, steps=1, rand_seed=None, connection=None, samples=None,
                initial_beta=None, initial_gamma=None, minimizer_kwargs=None,
                vqe_option=None):

    if not isinstance(graph, nx.Graph) and isinstance(graph, list):
        maxcut_graph = nx.Graph()
        for edge in graph:
            maxcut_graph.add_edge(*edge)
        graph = maxcut_graph.copy()
        
    cost_operators = []
    driver_operators = []
    for i, j in graph.edges():
        weight = graph.get_edge_data(i,j)['weight']/largest_weight
        print(weight)
        cost_operators.append(PauliTerm("Z", i, weight)*PauliTerm("Z", j) + PauliTerm("I", 0, -weight))
    for i in graph.nodes():
        driver_operators.append(PauliSum([PauliTerm("X", i, -1.0)]))

    if connection is None:
        connection = get_qc(f"{len(graph.nodes)}q-qvm")

    if minimizer_kwargs is None:
        minimizer_kwargs = {'method': 'Nelder-Mead',
                            'options': {'ftol': 1.0e-2, 'xtol': 1.0e-2,
                                        'disp': False}}
    if vqe_option is None:
        vqe_option = {'disp': print, 'return_all': True,
                      'samples': samples}

    qaoa_inst = QAOA(connection, list(graph.nodes()), steps=steps, cost_ham=cost_operators,
                     ref_ham=driver_operators, store_basis=True,
                     rand_seed=rand_seed,
                     init_betas=initial_beta,
                     init_gammas=initial_gamma,
                     minimizer=minimize,
                     minimizer_kwargs=minimizer_kwargs,
                     vqe_options=vqe_option)

    return qaoa_inst
Ejemplo n.º 15
0
def test_extended_get_constraints():
    weights = [0.1, 0.3, 0.5, -0.7]
    term1 = PauliTerm.from_list([("Z", 0), ("Z", 1)], 0.1)
    term2 = PauliTerm.from_list([("Z", 1), ("Z", 2)], 0.3)
    term3 = PauliTerm.from_list([("Z", 2), ("Z", 3)], 0.5)
    term4 = PauliTerm.from_list([("Z", 3), ("Z", 0)], -0.7)
    ham = PauliSum([term1, term2, term3, term4])
    p = 2

    params = ExtendedParams.linear_ramp_from_hamiltonian(ham, p)

    actual_constraints = params.get_constraints()

    expected_constraints = [(0, 2 * np.pi), (0, 2 * np.pi), (0, 2 * np.pi),
                            (0, 2 * np.pi), (0, 2 * np.pi), (0, 2 * np.pi),
                            (0, 2 * np.pi), (0, 2 * np.pi),
                            (0, 2 * np.pi / weights[0]),
                            (0, 2 * np.pi / weights[1]),
                            (0, 2 * np.pi / weights[2]),
                            (0, 2 * np.pi / weights[3]),
                            (0, 2 * np.pi / weights[0]),
                            (0, 2 * np.pi / weights[1]),
                            (0, 2 * np.pi / weights[2]),
                            (0, 2 * np.pi / weights[3])]

    assert (np.allclose(expected_constraints, actual_constraints))

    cost_function = QAOACostFunctionOnWFSim(ham, params)
    np.random.seed(0)
    random_angles = np.random.uniform(-100, 100, size=len(params.raw()))
    value = cost_function(random_angles)

    normalised_angles = [
        random_angles[i] % actual_constraints[i][1]
        for i in range(len(params.raw()))
    ]
    normalised_value = cost_function(normalised_angles)

    assert (np.allclose(value, normalised_value))
Ejemplo n.º 16
0
def test_pauli_sum():
    q_plus = 0.5 * PauliTerm('X', 0) + 0.5j * PauliTerm('Y', 0)
    the_sum = q_plus * PauliSum([PauliTerm('X', 0)])
    term_strings = map(lambda x: str(x), the_sum.terms)
    assert '0.5*I' in term_strings
    assert '(0.5+0j)*Z0' in term_strings
    assert len(term_strings) == 2
    assert len(the_sum.terms) == 2

    the_sum = q_plus * PauliTerm('X', 0)
    term_strings = map(lambda x: str(x), the_sum.terms)
    assert '0.5*I' in term_strings
    assert '(0.5+0j)*Z0' in term_strings
    assert len(term_strings) == 2
    assert len(the_sum.terms) == 2

    the_sum = PauliTerm('X', 0) * q_plus
    term_strings = map(lambda x: str(x), the_sum.terms)
    assert '0.5*I' in term_strings
    assert '(-0.5+0j)*Z0' in term_strings
    assert len(term_strings) == 2
    assert len(the_sum.terms) == 2
Ejemplo n.º 17
0
def test_mutation_free_estimation():
    """
    Make sure the estimation routines do not mutate the programs the user sends.

    This is accomplished by a deep copy in `estimate_pauli_sum'.
    """
    prog = Program().inst(I(0))
    pauli_sum = sX(0)  # measure in the X-basis

    # set up fake QVM
    with patch("pyquil.api.QuantumComputer") as qc:
        # Mock the response
        qc.run.return_value = [[0], [1]]

    _, _, _ = estimate_locally_commuting_operator(prog,
                                                  pauli_sum=PauliSum(
                                                      [pauli_sum]),
                                                  variance_bound=1.0E-3,
                                                  quantum_resource=qc)

    # make sure RY(-pi/2) 0\nMEASURE 0 [0] was not added to the program the user sees
    assert prog.out() == 'I 0\n'
Ejemplo n.º 18
0
    def create_phase_separator(self):
        """
        Creates phase-separation operators, which depend on the objective function.
        """
        cost_operators = []
        reduced_distance_matrix = np.delete(self.distance_matrix,
                                            self.starting_node,
                                            axis=0)
        reduced_distance_matrix = np.delete(reduced_distance_matrix,
                                            self.starting_node,
                                            axis=1)
        for t in range(self.reduced_number_of_nodes - 1):
            for city_1 in range(self.reduced_number_of_nodes):
                for city_2 in range(self.reduced_number_of_nodes):
                    if city_1 != city_2:
                        distance = reduced_distance_matrix[city_1, city_2]
                        qubit_1 = t * (self.reduced_number_of_nodes) + city_1
                        qubit_2 = (t +
                                   1) * (self.reduced_number_of_nodes) + city_2
                        cost_operators.append(
                            PauliTerm("Z", qubit_1, distance) *
                            PauliTerm("Z", qubit_2))

        costs_to_starting_node = np.delete(
            self.distance_matrix[:, self.starting_node], self.starting_node)

        for city in range(self.reduced_number_of_nodes):
            distance_from_0 = -costs_to_starting_node[city]
            qubit = city
            cost_operators.append(PauliTerm("Z", qubit, distance_from_0))

        for city in range(self.reduced_number_of_nodes):
            distance_from_0 = -costs_to_starting_node[city]
            qubit = self.number_of_qubits - (
                self.reduced_number_of_nodes) + city
            cost_operators.append(PauliTerm("Z", qubit, distance_from_0))

        phase_separator = [PauliSum(cost_operators)]
        return phase_separator
Ejemplo n.º 19
0
    def test_time_evolution_derivatives(self):
        # Given
        hamiltonian = PauliSum([
            PauliTerm('X', 0) * PauliTerm('X', 1),
            PauliTerm('Y', 0, 0.5) * PauliTerm('Y', 1),
            PauliTerm('Z', 0, 0.3) * PauliTerm('Z', 1)
        ])
        time = 0.4
        order = 3
        reference_factors_1 = [1.0 / order, 0.5 / order, 0.3 / order] * 3
        reference_factors_2 = [-1.0 * x for x in reference_factors_1]

        # When
        derivatives, factors = time_evolution_derivatives(hamiltonian,
                                                          time,
                                                          trotter_order=order)

        # Then
        self.assertEqual(len(derivatives), order * 2 * len(hamiltonian.terms))
        self.assertEqual(len(factors), order * 2 * len(hamiltonian.terms))
        self.assertListEqual(reference_factors_1, factors[0:18:2])
        self.assertListEqual(reference_factors_2, factors[1:18:2])
    def create_phase_separator(self):
        cost_operators = []
        for t in range(len(self.nodes_array) - 1):
            for city_1 in range(len(self.nodes_array)):
                for city_2 in range(len(self.nodes_array)):
                    if city_1 != city_2:
                        tsp_matrix = TSP_utilities.get_tsp_matrix(
                            self.nodes_array)
                        distance = tsp_matrix[city_1, city_2]
                        qubit_1 = t * len(self.nodes_array) + city_1
                        qubit_2 = (t + 1) * len(self.nodes_array) + city_2
                        cost_operators.append(
                            PauliTerm("Z", qubit_1, distance) *
                            PauliTerm("Z", qubit_2))

        for city in range(len(self.costs_to_starting_node)):
            distance_from_0 = -self.costs_to_starting_node[city]
            qubit = city
            cost_operators.append(PauliTerm("Z", qubit, distance_from_0))

        phase_separator = [PauliSum(cost_operators)]
        return phase_separator
Ejemplo n.º 21
0
def qubitop_to_pyquilpauli(qubit_operator):
    """
    Convert an OpenFermion QubitOperator to a PauliSum

    :param QubitOperator qubit_operator: OpenFermion QubitOperator to convert to a pyquil.PauliSum
    
    :return: PauliSum representing the qubit operator
    :rtype: PauliSum
    """
    if not isinstance(qubit_operator, QubitOperator):
        raise TypeError("qubit_operator must be a OpenFermion "
                        "QubitOperator object")

    transformed_term = PauliSum([PauliTerm("I", 0, 0.0)])
    for qubit_terms, coefficient in qubit_operator.terms.items():
        base_term = PauliTerm('I', 0)
        for tensor_term in qubit_terms:
            base_term *= PauliTerm(tensor_term[1], tensor_term[0])

        transformed_term += base_term * coefficient

    return transformed_term
def estimate_general_psum_symmeterized(program,
                                       pauli_sum,
                                       variance_bound,
                                       quantum_resource,
                                       confusion_mat_dict=None,
                                       sequential=False):
    """
    Estimate the expected value of a Pauli sum to fixed precision.

    :param program: state preparation program
    :param pauli_sum: pauli sum of operators to estimate expected value
    :param variance_bound: variance bound on the estimator
    :param quantum_resource: quantum abstract machine object
    :return: expected value, estimator variance, total number of experiments
    """
    if sequential:
        expected_value = 0
        estimator_variance = 0
        total_shots = 0
        variance_bound_per_term = variance_bound / len(pauli_sum)
        for term in pauli_sum:
            exp_v, exp_var, exp_shots = \
                estimate_locally_commuting_operator_symmeterized(
                program, PauliSum([term]), variance_bound_per_term,
                quantum_resource, confusion_mat_dict=confusion_mat_dict)
            expected_value += exp_v
            estimator_variance += exp_var
            total_shots += exp_shots

        return expected_value, estimator_variance, total_shots

    else:
        return estimate_locally_commuting_operator_symmeterized(
            program,
            pauli_sum,
            variance_bound,
            quantum_resource,
            confusion_mat_dict=confusion_mat_dict)
Ejemplo n.º 23
0
def ring_of_disagrees(n: int) -> PauliSum:
    """
    Builds the cost Hamiltonian for the "Ring of Disagrees" described in the original QAOA paper (https://arxiv.org/abs/1411.4028),
    for the specified number of vertices n.

    Parameters
    ----------
    n:
        Number of vertices in the ring

    Returns
    -------
    hamiltonian:
        The cost Hamiltonian representing the ring, as a PauliSum object.

    """

    hamiltonian = []
    for i in range(n - 1):
        hamiltonian.append(PauliTerm("Z", i, 0.5) * PauliTerm("Z", i + 1))
    hamiltonian.append(PauliTerm("Z", n - 1, 0.5) * PauliTerm("Z", 0))

    return PauliSum(hamiltonian)
def test_expectation(forest: ForestConnection):
    # The forest fixture (argument) to this test is to ensure this is
    # skipped when a forest web api key is unavailable. You could also
    # pass it to the constructor of WavefunctionSimulator() but it is not
    # necessary.
    wfnsim = WavefunctionSimulator()
    bell = Program(
        H(0),
        CNOT(0, 1),
    )
    expects = wfnsim.expectation(bell, [
        sZ(0) * sZ(1),
        sZ(0),
        sZ(1),
        sX(0) * sX(1),
    ])
    assert expects.size == 4
    np.testing.assert_allclose(expects, [1, 0, 0, 1])

    pauli_sum = PauliSum([sZ(0) * sZ(1)])
    expects = wfnsim.expectation(bell, pauli_sum)
    assert expects.size == 1
    np.testing.assert_allclose(expects, [1])
Ejemplo n.º 25
0
def random_hamiltonian(nqubits: int) -> PauliSum:
    """
    Creates a random cost hamiltonian, diagonal in the computational basis:

     - Randomly selects which qubits that will have a bias term, then assigns them a bias coefficient.
     - Randomly selects which qubit pairs will have a coupling term, then assigns them a coupling coefficient.

     In both cases, the random coefficient is drawn from the uniform distribution on the interval [0,1).

    Parameters
    ----------
    nqubits:
        The desired number of qubits.

    Returns
    -------
    hamiltonian:
        A hamiltonian with random couplings and biases, as a PauliSum object.

    """
    hamiltonian = []

    numb_biases = np.random.randint(nqubits)
    bias_qubits = np.random.choice(nqubits, numb_biases, replace=False)
    bias_coeffs = np.random.rand(numb_biases)
    for i in range(numb_biases):
        hamiltonian.append(PauliTerm("Z", int(bias_qubits[i]), bias_coeffs[i]))

    for i in range(nqubits):
        for j in range(i + 1, nqubits):
            are_coupled = np.random.randint(2)
            if are_coupled:
                couple_coeff = np.random.rand()
                hamiltonian.append(
                    PauliTerm("Z", i, couple_coeff) * PauliTerm("Z", j))

    return PauliSum(hamiltonian)
Ejemplo n.º 26
0
def sampling_expectation_z_base(hamiltonian: PauliSum,
                                bitstrings: np.array) -> Tuple[float, float]:
    """Calculates the energy expectation value of ``bitstrings`` w.r.t ``ham``.

    Warning
    -------
    This function assumes, that all terms in ``hamiltonian`` commute trivially
    _and_ that the ``bitstrings`` were measured in their basis.

    Parameters
    ----------
    param hamiltonian:
        The hamiltonian
    param bitstrings: 2D arry or list
        The measurement outcomes. One column per qubit.

    Returns
    -------
    tuple (expectation_value, variance/(n-1))
    """

    # this dictionary maps from qubit indices to indices of the bitstrings
    # This is neccesary, because hamiltonian might not act on all qubits.
    # E.g. if hamiltonian = X0 + 1.0*Z2 bitstrings is a 2 x numshots array
    index_lut = {q: i for (i, q) in enumerate(hamiltonian.get_qubits())}
    if bitstrings.ndim == 2:
        energies = np.zeros(bitstrings.shape[0])
    else:
        energies = np.array([0])
    for term in hamiltonian:
        sign = np.zeros_like(energies)
        for factor in term:
            sign += bitstrings[:, index_lut[factor[0]]]
        energies += term.coefficient.real * (-1)**sign

    return (np.mean(energies),
            np.var(energies) / (bitstrings.shape[0] - 1))
Ejemplo n.º 27
0
def vqe_parametric(vqe_strategy, vqe_tomography, vqe_method):
    """
    Initialize a VQE experiment with a custom hamiltonian
    given as constant input
    """

    _vqe = None

    if vqe_strategy == "custom_program":
        custom_ham = PauliSum([PauliTerm(*x) for x in HAMILTONIAN])
        _vqe = VQEexperiment(hamiltonian=custom_ham,
                             method=vqe_method,
                             strategy=vqe_strategy,
                             parametric=True,
                             tomography=vqe_tomography,
                             shotN=NSHOTS_FLOAT)
    elif vqe_strategy == "HF":
        cwd = os.path.abspath(os.path.dirname(__file__))
        fname = os.path.join(cwd, "resources", "H.hdf5")
        molecule = MolecularData(filename=fname)
        _vqe = VQEexperiment(molecule=molecule,
                             method=vqe_method,
                             strategy=vqe_strategy,
                             parametric=True,
                             tomography=vqe_tomography,
                             shotN=NSHOTS_FLOAT)
    elif vqe_strategy == "UCCSD":
        cwd = os.path.abspath(os.path.dirname(__file__))
        fname = os.path.join(cwd, "resources", "H2.hdf5")
        molecule = MolecularData(filename=fname)
        _vqe = VQEexperiment(molecule=molecule,
                             method=vqe_method,
                             strategy=vqe_strategy,
                             parametric=True,
                             tomography=vqe_tomography,
                             shotN=NSHOTS_FLOAT)
    return _vqe
Ejemplo n.º 28
0
def lifted_pauli(pauli_sum: Union[PauliSum, PauliTerm], qubits: List[int]):
    """
    Takes a PauliSum object along with a list of
    qubits and returns a matrix corresponding the tensor representation of the
    object.

    Useful for generating the full Hamiltonian after a particular fermion to
    pauli transformation. For example:

    Converting a PauliSum X0Y1 + Y1X0 into the matrix

    .. code-block:: python

       [[ 0.+0.j,  0.+0.j,  0.+0.j,  0.-2.j],
        [ 0.+0.j,  0.+0.j,  0.+0.j,  0.+0.j],
        [ 0.+0.j,  0.+0.j,  0.+0.j,  0.+0.j],
        [ 0.+2.j,  0.+0.j,  0.+0.j,  0.+0.j]]

    :param pauli_sum: Pauli representation of an operator
    :param qubits: list of qubits in the order they will be represented in the resultant matrix.
    :returns: matrix representation of the pauli_sum operator
    """
    if isinstance(pauli_sum, PauliTerm):
        pauli_sum = PauliSum([pauli_sum])

    n_qubits = len(qubits)
    result_hilbert = np.zeros((2 ** n_qubits, 2 ** n_qubits), dtype=np.complex128)
    # left kronecker product corresponds to the correct basis ordering
    for term in pauli_sum.terms:
        term_hilbert = np.array([1])
        for qubit in qubits:
            term_hilbert = np.kron(QUANTUM_GATES[term[qubit]], term_hilbert)

        result_hilbert += term_hilbert * term.coefficient

    return result_hilbert
Ejemplo n.º 29
0
def pauli_matrix(pauli_sum: PauliSum, qubit_mapping: Dict = {}) -> np.array:
    """Create the matrix representation of pauli_sum.

    Parameters
    ----------
    qubit_mapping:
        A dictionary-like object that maps from :py:class`QubitPlaceholder` to
        :py:class:`int`

    Returns
    -------
    np.matrix:
        A matrix representing the PauliSum
    """

    # get unmapped Qubits and check that all QubitPlaceholders are mapped
    unmapped_qubits = {*pauli_sum.get_qubits()} - qubit_mapping.keys()
    if not all(isinstance(q, int) for q in unmapped_qubits):
        raise ValueError("Not all QubitPlaceholders are mapped")

    # invert qubit_mapping and assert its injectivity
    inv_mapping = dict([v, k] for k, v in qubit_mapping.items())
    if len(inv_mapping) is not len(qubit_mapping):
        raise ValueError("qubit_mapping must be injective")

    # add unmapped qubits to the inverse mapping, ensuring we don't have
    # a list entry twice
    for q in unmapped_qubits:
        if q not in inv_mapping.keys():
            inv_mapping[q] = q
        else:
            raise ValueError("qubit_mapping maps to qubit already in use")

    qubit_list = [inv_mapping[k] for k in sorted(inv_mapping.keys())]
    matrix = lifted_pauli(pauli_sum, qubit_list)
    return matrix
Ejemplo n.º 30
0
class TestTimeEvolutionDerivatives:
    @pytest.fixture(
        params=[
            PauliSum(
                [
                    PauliTerm("X", 0) * PauliTerm("X", 1),
                    PauliTerm("Y", 0, 0.5) * PauliTerm("Y", 1),
                    PauliTerm("Z", 0, 0.3) * PauliTerm("Z", 1),
                ]
            ),
            QubitOperator("[X0 X1] + 0.5[Y0 Y1] + 0.3[Z0 Z1]"),
        ]
    )
    def hamiltonian(self, request):
        return request.param

    @pytest.mark.parametrize("time", [0.4, sympy.Symbol("t")])
    def test_time_evolution_derivatives_gives_correct_number_of_derivatives_and_factors(
        self, time, hamiltonian
    ):
        order = 3
        reference_factors_1 = [1.0 / order, 0.5 / order, 0.3 / order] * 3
        reference_factors_2 = [-1.0 * x for x in reference_factors_1]

        derivatives, factors = time_evolution_derivatives(
            hamiltonian, time, trotter_order=order
        )
        if isinstance(hamiltonian, QubitOperator):
            terms = list(hamiltonian.get_operators())
        elif isinstance(hamiltonian, PauliSum):
            terms = hamiltonian.terms

        assert len(derivatives) == order * 2 * len(terms)
        assert len(factors) == order * 2 * len(terms)
        assert factors[0:18:2] == reference_factors_1
        assert factors[1:18:2] == reference_factors_2