File size: 6,577 Bytes
6d8ed8c
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
from unittest import TestCase
from unittest.mock import patch

import numpy as np
from qiskit import QuantumCircuit

import adaptaqc.utils.circuit_operations as co
from adaptaqc.backends.python_default_backends import QASM_SIM, SV_SIM, MPS_SIM
from adaptaqc.compilers.adapt.adapt_compiler import AdaptCompiler
from adaptaqc.compilers.approximate_compiler import ApproximateCompiler


@patch.multiple(ApproximateCompiler, __abstractmethods__=set())
class TestApproximateCompiler(TestCase):
    def setUp(self) -> None:
        self.qc = QuantumCircuit(1)

    def test_when_init_with_mps_backend_then_mps_backend_flag_true(self):
        self.assertTrue(ApproximateCompiler(self.qc, MPS_SIM).is_aer_mps_backend)

    def test_when_init_with_sv_backend_then_sv_backend_flag_true(self):
        self.assertTrue(ApproximateCompiler(self.qc, SV_SIM).is_statevector_backend)

    def test_given_global_cost_and_SV_backend_when_evaluate_cost_then_correct_function_called(
        self,
    ):
        compiler = ApproximateCompiler(QuantumCircuit(1), SV_SIM)
        with patch.object(compiler.backend, "evaluate_global_cost") as mock:
            compiler.evaluate_cost()
        mock.assert_called_once()

    def test_given_global_cost_and_MPS_backend_when_evaluate_cost_then_correct_function_called(
        self,
    ):
        compiler = ApproximateCompiler(QuantumCircuit(1), MPS_SIM)
        with patch.object(compiler.backend, "evaluate_global_cost") as mock:
            compiler.evaluate_cost()
        mock.assert_called_once()

    def test_given_global_cost_and_QASM_backend_when_evaluate_cost_then_correct_function_called(
        self,
    ):
        compiler = ApproximateCompiler(QuantumCircuit(1), QASM_SIM)
        with patch.object(compiler.backend, "evaluate_global_cost") as mock:
            compiler.evaluate_cost()
        mock.assert_called_once()

    def test_given_local_cost_and_SV_backend_when_evaluate_cost_then_correct_function_called(
        self,
    ):
        compiler = ApproximateCompiler(
            QuantumCircuit(1), SV_SIM, optimise_local_cost=True
        )
        with patch.object(compiler.backend, "evaluate_local_cost") as mock:
            compiler.evaluate_cost()
        mock.assert_called_once()

    def test_given_local_cost_and_MPS_backend_when_evaluate_cost_then_correct_function_called(
        self,
    ):
        compiler = ApproximateCompiler(
            QuantumCircuit(1), MPS_SIM, optimise_local_cost=True
        )
        with patch.object(compiler.backend, "evaluate_local_cost") as mock:
            compiler.evaluate_cost()
        mock.assert_called_once()

    def test_given_local_cost_and_QASM_backend_when_evaluate_cost_then_correct_function_called(
        self,
    ):
        compiler = ApproximateCompiler(
            QuantumCircuit(1), QASM_SIM, optimise_local_cost=True
        )
        with patch.object(compiler.backend, "evaluate_local_cost") as mock:
            compiler.evaluate_cost()
        mock.assert_called_once()

    def test_given_random_circuit_when_evaluate_local_cost_all_three_methods_return_same_cost(
        self,
    ):
        qc = co.create_random_initial_state_circuit(4)

        compiler_sv = AdaptCompiler(qc, backend=SV_SIM, optimise_local_cost=True)
        compiler_mps = AdaptCompiler(qc, backend=MPS_SIM, optimise_local_cost=True)
        compiler_qasm = AdaptCompiler(qc, backend=QASM_SIM, optimise_local_cost=True)

        cost_sv = compiler_sv.evaluate_cost()
        cost_mps = compiler_mps.evaluate_cost()
        cost_qasm = compiler_qasm.evaluate_cost()

        # Looser pass threshold for qasm because it includes some form of noise
        np.testing.assert_almost_equal(cost_sv, cost_mps, decimal=5)
        np.testing.assert_almost_equal(cost_sv, cost_qasm, decimal=2)
        np.testing.assert_almost_equal(cost_mps, cost_qasm, decimal=2)

    def test_given_random_circuit_when_evaluate_global_cost_all_three_methods_return_same_cost(
        self,
    ):
        qc = co.create_random_initial_state_circuit(4)

        compiler_sv = AdaptCompiler(qc, backend=SV_SIM)
        compiler_mps = AdaptCompiler(qc, backend=MPS_SIM)
        compiler_qasm = AdaptCompiler(qc, backend=QASM_SIM)

        cost_sv = compiler_sv.evaluate_cost()
        cost_mps = compiler_mps.evaluate_cost()
        cost_qasm = compiler_qasm.evaluate_cost()

        # Looser pass threshold for qasm because it includes some form of noise
        np.testing.assert_almost_equal(cost_sv, cost_mps, decimal=5)
        np.testing.assert_almost_equal(cost_sv, cost_qasm, decimal=2)
        np.testing.assert_almost_equal(cost_mps, cost_qasm, decimal=2)

    def test_given_simple_states_when_evaluate_global_and_local_costs_then_correct_value(
        self,
    ):
        # Analytically calculable costs:
        # |0000> global=0, local=0
        # |1010> global=1, local=1/2
        # 4-qubit GHZ global=1/2, local=1/2
        # |++++> global=15/16, local=1/2
        # Using equations 9 and 11 from arXiv:1908.04416

        analytic_costs = [0, 0, 1, 1 / 2, 1 / 2, 1 / 2, 15 / 16, 1 / 2]
        adapt_costs = []

        zero = QuantumCircuit(4)

        neel = QuantumCircuit(4)
        neel.x([0, 2])

        ghz = QuantumCircuit(4)
        ghz.h(0)
        for i in range(3):
            ghz.cx(0, i + 1)

        hadamard = QuantumCircuit(4)
        hadamard.h([0, 1, 2, 3])

        circuits = [zero, neel, ghz, hadamard]

        for circuit in circuits:
            for optimise_local_cost in [False, True]:
                compiler = AdaptCompiler(
                    circuit, backend=SV_SIM, optimise_local_cost=optimise_local_cost
                )
                cost = compiler.evaluate_cost()
                adapt_costs.append(cost)

        np.testing.assert_allclose(adapt_costs, analytic_costs)

    def test_given_random_circuit_when_calculate_cost_local_less_or_equal_to_global(
        self,
    ):
        qc = co.create_random_initial_state_circuit(4)

        compiler_local = AdaptCompiler(qc, backend=SV_SIM, optimise_local_cost=True)
        compiler_global = AdaptCompiler(qc, backend=SV_SIM)

        cost_local = compiler_local.evaluate_cost()
        cost_global = compiler_global.evaluate_cost()

        self.assertLessEqual(cost_local, cost_global)

    @patch("qiskit.QuantumCircuit.set_matrix_product_state")
    def test_set_matrix_product_state_used_when_mps_backend(
        self, mock_set_matrix_product_state
    ):
        ApproximateCompiler(QuantumCircuit(1), MPS_SIM)
        mock_set_matrix_product_state.assert_called_once()