Exemplo n.º 1
0
        def task(parameters):
            with tf.name_scope("circuit_A"):
                theta_input = tf.identity(self.theta_var)
                self.qc_a.set_parameters(self.theta_var)
                phi_a = self.qc_a.circuit(self.qc_a.phi)

                expval_layer_a = ExpectationValue(self.obs)
                expvals_a = expval_layer_a(phi_a)

                energy_a = self.energy_fn(expvals_a, parameters)

                grads_a = tf.stop_gradient(
                    tf.gradients(energy_a, self.theta_var))
            with tf.name_scope("circuit_B"):
                theta_prime = theta_input - self.ALPHA * grads_a[0]
                self.qc_b.set_parameters(theta_prime)
                phi_b = self.qc_b.circuit(self.qc_b.phi)

                expval_layer_b = ExpectationValue(self.obs)
                expvals_b = expval_layer_b(phi_b)

                energy_b = tf.reduce_mean(self.energy_fn(
                    expvals_b, parameters))

            return energy_a, energy_b
Exemplo n.º 2
0
def test_batching_works_correctly():
    N = 2
    qc = QuantumCircuit(N, [Hadamard(wires=[
        0,
    ]), Hadamard(wires=[
        1,
    ])],
                        device="CPU")
    obs = [Observable("x", wires=[
        0,
    ]), Observable("x", wires=[
        1,
    ])]
    phi = tf.placeholder(dtype=tf.complex64,
                         shape=(None, *[2 for _ in range(N)]))
    expval_layer = ExpectationValue(obs)

    phi_batch = qc.circuit(phi)
    expvals = expval_layer(phi_batch)

    # four basis states
    states = np.eye(int(2**N))
    qc.initialize()
    out_batch = qc._sess.run(
        [expvals], feed_dict={phi: states.reshape(-1,
                                                  *[2 for _ in range(N)])})[0]
    out_batch = out_batch.reshape((-1, ))
    assert np.allclose(out_batch, np.array([1, 1, 1, -1, -1, 1, -1, -1]))
Exemplo n.º 3
0
def test_phase():
    dev = qml.device("default.qubit", analytic=True, wires=1)
    theta = 0.2

    @qml.qnode(dev)
    def circuit():
        qml.PhaseShift(theta, wires=[
            0,
        ])
        return (qml.expval(qml.PauliX(0)))

    qc = QuantumCircuit(2, [
        Phase(wires=[
            0,
        ], value=[
            theta,
        ]),
    ],
                        device="CPU")
    obs = [Observable("x", wires=[
        0,
    ])]
    phi = qc.circuit(qc.phi)
    expval_layer = ExpectationValue(obs)
    qc.initialize()
    qc_tf = qc._sess.run(expval_layer(phi))
    qc_tf = qc_tf.flatten()
    qc_pl = circuit()
    assert all(np.isclose(qc_pl, qc_tf))
Exemplo n.º 4
0
def test_toffoli():
    dev = qml.device("default.qubit", analytic=True, wires=3)

    @qml.qnode(dev)
    def circuitx():
        qml.Hadamard(wires=0)
        qml.Hadamard(wires=1)
        qml.Hadamard(wires=2)
        qml.Toffoli(wires=[0, 1, 2])
        return qml.expval(qml.PauliX(2))

    @qml.qnode(dev)
    def circuity():
        qml.Hadamard(wires=0)
        qml.Hadamard(wires=1)
        qml.Hadamard(wires=2)
        qml.Toffoli(wires=[0, 1, 2])
        return qml.expval(qml.PauliY(2))

    @qml.qnode(dev)
    def circuitz():
        qml.Hadamard(wires=0)
        qml.Hadamard(wires=1)
        qml.Hadamard(wires=2)
        qml.Toffoli(wires=[0, 1, 2])
        return qml.expval(qml.PauliZ(2))

    qc_pl = np.array([circuitx(), circuity(), circuitz()])
    qc = QuantumCircuit(3, [
        Hadamard(wires=[
            0,
        ]),
        Hadamard(wires=[
            1,
        ]),
        Hadamard(wires=[
            2,
        ]),
        Toffoli(wires=[0, 2, 1])
    ],
                        device="CPU")
    obs = [
        Observable("x", wires=[
            2,
        ]),
        Observable("y", wires=[
            2,
        ]),
        Observable("z", wires=[
            2,
        ])
    ]
    phi = qc.circuit(qc.phi)
    expval_layer = ExpectationValue(obs)
    qc.initialize()
    qc_tf = qc._sess.run(expval_layer(phi))
    qc_tf = qc_tf.flatten()
    assert all(np.isclose(qc_pl, qc_tf))
Exemplo n.º 5
0
def test_rotations_and_cnot():
    dev = qml.device("default.qubit", analytic=True, wires=2)
    theta = 0.1
    phi = 0.2

    @qml.qnode(dev)
    def circuitx():
        qml.RX(theta, wires=0)
        qml.RY(phi, wires=1)
        qml.CNOT(wires=[1, 0])
        return qml.expval(qml.PauliX(0))

    @qml.qnode(dev)
    def circuity():
        qml.RX(theta, wires=0)
        qml.RY(phi, wires=1)
        qml.CNOT(wires=[1, 0])
        return qml.expval(qml.PauliY(0))

    @qml.qnode(dev)
    def circuitz():
        qml.RX(theta, wires=0)
        qml.RY(phi, wires=1)
        qml.CNOT(wires=[1, 0])
        return qml.expval(qml.PauliZ(0))

    qc_pl = np.array([circuitx(), circuity(), circuitz()])
    qc = QuantumCircuit(2, [
        RX(wires=[
            0,
        ], value=[theta]),
        RY(wires=[
            1,
        ], value=[phi]),
        CNOT(wires=[1, 0])
    ],
                        device="CPU")
    obs = [
        Observable("x", wires=[
            0,
        ]),
        Observable("y", wires=[
            0,
        ]),
        Observable("z", wires=[
            0,
        ])
    ]
    phi = qc.circuit(qc.phi)
    expval_layer = ExpectationValue(obs)
    qc.initialize()
    qc_tf = qc._sess.run(expval_layer(phi))
    qc_tf = qc_tf.flatten()
    assert all(np.isclose(qc_pl, qc_tf))
Exemplo n.º 6
0
    def _build_graph(self):
        """
        Build the computational tensorflow graph.

        Args:

            device (string):
                Device of choice for running tensorflow.

        Returns (inplace):
            None

        """

        if self.exact:
            with tf.name_scope("exact_hamiltonian"):
                self.hamiltonian.get_hamiltonian()
                self.gs_energy = self.hamiltonian.energies[0]
                self.gs_wavefunction = self.hamiltonian.gs

        with tf.name_scope("scipy_handle"):
            self.theta_ph = tf.compat.v1.placeholder(dtype=TF_FLOAT_DTYPE, shape=(self.qc.nparams))
            c = 0
            for g in self.qc.gates:
                if g.nparams > 0:
                    g.set_external_input(tf.reshape(self.theta_ph[c:c + g.nparams], (1, g.nparams, 1)))
                    c += g.nparams

        phi = self.qc.circuit(self.qc.execute())

        with tf.name_scope("train_step"):
            self.expectation_layer = ExpectationValue(self.obs)
            self.expvals = self.expectation_layer(phi)
            if self.feed_in_hamiltonian_terms:
                self.hamiltonian_terms = tf.compat.v1.placeholder(dtype=TF_FLOAT_DTYPE,
                                                                  shape=len(self.hamiltonian_terms),
                                                                  name='terms')
                self.energy = tf.reduce_sum(flatten(self.hamiltonian_terms) * flatten(self.expvals), name="energy")
            else:
                self.energy = tf.reduce_sum(
                    tf.constant(self.hamiltonian_terms.flatten(), dtype=TF_FLOAT_DTYPE) * flatten(self.expvals),
                    name="energy")

        if self.save_model:
            self.saver = tf.compat.v1.train.Saver([self.qc.circuit.trainable_variables], max_to_keep=4)

        self.qc.initialize()

        if self.load_model:
            out = self.saver.restore(self.qc._sess, self.tfcheckpoint_path + self.model_name)
            print("Loaded model from {}".format(out))
Exemplo n.º 7
0
def test_tutorial_1():
    gates = [Hadamard(wires=[
        0,
    ]), PauliZ(wires=[
        0,
    ])]
    qc = QuantumCircuit(nqubits=1, gates=gates, device="CPU")
    qc.initialize()
    phi = qc.circuit(qc.phi)

    qc_tf = qc._sess.run(phi)
    obs = [Observable("x", wires=[
        0,
    ])]
    expval_layer = ExpectationValue(obs)
    measurements = qc._sess.run(expval_layer(phi))
Exemplo n.º 8
0
    def _build_graph_multi_gpu(self, optimizer):
        """
        Build the computational tensorflow graph.

        Args:
            *optimizer (tf.optimizers.Optimizer)*:
                Desired optimizer to perform the QVE algorithm.

        Returns (inplace):
            None

        """
        assert self.qc.ngpus == self.ngpus, "Number of GPUs between QuantumCircuit and QuantumVariationalEigensolver not equal: {} and {}".format(
            self.qc.ngpus, self.ngpus)
        with tf.device('/GPU:0'):
            if self.exact:
                with tf.name_scope("exact_hamiltonian"):
                    self.hamiltonian.get_hamiltonian()
                    self.gs_energy = self.hamiltonian.energies[0]
                    self.gs_wavefunction = self.hamiltonian.gs

        phi = self.qc.execute()
        with tf.device('/GPU:0'):
            with tf.name_scope("train_step"):
                self.optimizer = optimizer
                self.expectation_layer = ExpectationValue(self.obs)
                self.expvals = self.expectation_layer(phi)
                if self.feed_in_hamiltonian_terms:
                    self.hamiltonian_terms_feed = tf.compat.v1.placeholder(dtype=TF_FLOAT_DTYPE,
                                                                           shape=len(self.hamiltonian_terms),
                                                                           name='terms')
                    self.energy = tf.reduce_sum(flatten(self.hamiltonian_terms_feed) * flatten(self.expvals),
                                                name="energy")
                else:
                    self.energy = tf.reduce_sum(
                        tf.constant(self.hamiltonian_terms.flatten(), dtype=TF_FLOAT_DTYPE) * flatten(self.expvals),
                        name="energy")
                self.train_step = self.optimizer.minimize(self.energy)

        if self.save_model:
            self.saver = tf.compat.v1.train.Saver([self.qc.circuit.trainable_variables], max_to_keep=4)
        self.qc.initialize()
        if self.load_model:
            out = self.saver.restore(self.qc._sess, self.tfcheckpoint_path + self.model_name)
            print("Loaded model from {}".format(out))
Exemplo n.º 9
0
def test_tutorial_2():
    gates = [
        Hadamard(wires=[
            0,
        ]),
        Phase(wires=[
            0,
        ], value=[np.pi / 8]),
        Hadamard(wires=[
            1,
        ]),
        Phase(wires=[
            1,
        ], value=[np.pi / 8]),
        CNOT(wires=[0, 1])
    ]

    qc = QuantumCircuit(nqubits=2, gates=gates, tensorboard=True, device="CPU")

    phi = qc.circuit(qc.phi)
    qc.circuit.summary()
    obs = [
        Observable("X", wires=[
            0,
        ]),
        Observable("x", wires=[
            1,
        ]),
        Observable("y", wires=[
            0,
        ]),
        Observable("y", wires=[
            1,
        ]),
        Observable("z", wires=[
            0,
        ]),
        Observable("z", wires=[
            1,
        ])
    ]
    expval_layer = ExpectationValue(obs)
    qc.initialize()
    measurements = qc._sess.run(expval_layer(phi))
Exemplo n.º 10
0
def test_batching_learning_observables():
    N = 3
    d = 13
    qc = QuantumCircuit(N, [Hadamard(wires=[
        0,
    ]), Hadamard(wires=[
        1,
    ])],
                        device="CPU")
    psi = tf.placeholder(dtype=tf.complex64,
                         shape=(None, *[2 for _ in range(N)]))
    phi_dim_1 = qc.circuit(qc.phi)
    phi_batch = qc.circuit(psi)
    qc.initialize()
    obs = [
        Observable("x", wires=[
            0,
        ]),
        Observable("x", wires=[
            1,
        ]),
        Observable("x", wires=[
            2,
        ]),
        Observable("y", wires=[
            0,
        ]),
        Observable("y", wires=[
            1,
        ]),
        Observable("y", wires=[
            2,
        ])
    ]
    expval_layer = ExpectationValue(obs)
    phi_feed = np.zeros([2 for _ in range(N)])
    phi_feed[0, ] = 1
    phi_feed = np.stack([phi_feed for _ in range(d)]).astype(np.complex64)
    out_1, out_batch = qc._sess.run(
        [expval_layer(phi_dim_1),
         expval_layer(phi_batch)],
        feed_dict={psi: phi_feed})
Exemplo n.º 11
0
def test_hybrid_neural_network_gradient_tracking():
    N = 2
    gates = [RX(wires=[
        0,
    ]), RX(wires=[
        1,
    ])]
    qc = QuantumCircuit(N, gates=gates, tensorboard=True, device="CPU")

    # Make a neural network
    NN = tf.keras.Sequential([
        tf.keras.layers.Dense(qc.nparams * 10,
                              activation=tf.keras.activations.tanh),
        tf.keras.layers.Dense(2, activation=tf.keras.activations.tanh),
        tf.keras.layers.Lambda(lambda x: x * 2 * np.pi)
    ])
    x_in = tf.ones((qc.nparams, 1)) * 0.01
    theta = NN(x_in)
    theta = tf.reduce_mean(theta, axis=0)
    theta = tf.reshape(theta, (1, -1, 1))

    qc.set_parameters(theta)
    phi = qc.circuit(qc.phi)
    obs = Observable("x", wires=[
        0,
    ]), Observable("x", wires=[
        1,
    ]), Observable("z", wires=[
        1,
    ])
    expval_layer = ExpectationValue(obs)
    expvals = expval_layer(phi)

    # Get energy for each timstep
    energy = tf.reduce_sum(expvals)

    opt = tf.compat.v1.train.AdamOptimizer(0.0001)
    grads = opt.compute_gradients(energy)
    qc.initialize()

    g = qc._sess.run(grads)
Exemplo n.º 12
0
def test_pauli_x():
    dev = qml.device("default.qubit", analytic=True, wires=1)

    @qml.qnode(dev)
    def circuitx():
        qml.PauliX(wires=0)
        return qml.expval(qml.PauliX(0))

    @qml.qnode(dev)
    def circuity():
        qml.PauliX(wires=0)
        return qml.expval(qml.PauliY(0))

    @qml.qnode(dev)
    def circuitz():
        qml.PauliX(wires=0)
        return qml.expval(qml.PauliZ(0))

    qc_pl = np.array([circuitx(), circuity(), circuitz()])
    qc = QuantumCircuit(1, [PauliX(wires=[
        0,
    ])], device="CPU")
    obs = [
        Observable("x", wires=[
            0,
        ]),
        Observable("y", wires=[
            0,
        ]),
        Observable("z", wires=[
            0,
        ])
    ]
    phi = qc.circuit(qc.phi)
    expval_layer = ExpectationValue(obs)
    qc.initialize()
    qc_tf = qc._sess.run(expval_layer(phi))
    qc_tf = qc_tf.flatten()
    assert all(np.isclose(qc_pl, qc_tf))
Exemplo n.º 13
0
def test_multiple_thetas():
    N = 3
    d = 13
    theta = tf.placeholder(dtype=tf.float32, shape=(None, 2, 1))
    qc = QuantumCircuit(N, [RX(wires=[
        0,
    ]), RX(wires=[
        1,
    ])],
                        batch_params=True,
                        device="CPU")
    qc.set_parameters(theta)
    phi_dim_1 = qc.circuit(qc.phi)
    obs = [
        Observable("x", wires=[
            0,
        ]),
        Observable("x", wires=[
            1,
        ]),
        Observable("x", wires=[
            2,
        ]),
        Observable("y", wires=[
            0,
        ]),
        Observable("y", wires=[
            1,
        ]),
        Observable("y", wires=[
            2,
        ])
    ]
    expval_layer = ExpectationValue(obs)

    expvals_dim_1 = expval_layer(phi_dim_1)
    qc.initialize()
    qc._sess.run([expvals_dim_1], feed_dict={theta: np.random.randn(d, 2, 1)})
Exemplo n.º 14
0
def test_swap():
    dev = qml.device("default.qubit", analytic=True, wires=2)
    theta = 0.2
    phi = 0.2
    delta = 0.2

    @qml.qnode(dev)
    def circuit():
        qml.Rot(theta, phi, delta, wires=0)
        qml.Rot(theta, phi, delta, wires=1)
        qml.SWAP(wires=[0, 1])
        return (
            qml.expval(qml.PauliX(0)),
            qml.expval(qml.PauliX(1)),
        )

    qc = QuantumCircuit(2, [
        R3(wires=[
            0,
        ], value=[theta, phi, delta]),
        R3(wires=[
            1,
        ], value=[theta, phi, delta]),
        Swap(wires=[0, 1])
    ],
                        device="CPU")
    obs = [Observable("x", wires=[
        0,
    ]), Observable("x", wires=[
        1,
    ])]
    psi = qc.circuit(qc.phi)
    expval_layer = ExpectationValue(obs)
    qc.initialize()
    qc_tf = qc._sess.run(expval_layer(psi))
    qc_tf = qc_tf.flatten()
    qc_pl = circuit()
    assert all(np.isclose(qc_pl, qc_tf))
Exemplo n.º 15
0
    def __init__(self,
                 nqubits: int,
                 qc: QuantumCircuit,
                 pdf: np.ndarray,
                 hamiltonian,
                 optimizer=None,
                 **kwargs):
        r"""
        Initialize

        Args:
            nqubits (int):
                The number of qubits in the system.

            qc (QuantumCircuit):
                Parametrized quantum circuit for :math:`N` spins.

            pdf (np.ndarray):
                array containing the target distribution :math:`q(x)`.

            optimizer (tf.optimizer.Optimizer):
                Tensorflow optimizer.

        Returns (inplace):
            None

        """
        self.nspins = nqubits
        self.qc = qc
        self.thermal = kwargs.pop('thermal', False)

        with tf.name_scope("target_expvals"):
            if self.thermal:
                assert qc.nqubits == self.nspins + 1, 'For thermal QBM the quantum circuit must have N+1 qubits.'
                self.qve = QuantumVariationalEigensolver(
                    hamiltonian,
                    qc,
                    device="GPU",
                    optimizer=optimizer,
                    exact=True,
                    feed_in_hamiltonian_terms=True,
                    thermal=self.thermal)
                assert len(pdf) == int(
                    2**(self.nspins + 1)
                ), 'Pdf must have length of 2**({}+1) for thermal QBM, received'.format(
                    self.nspins, len(pdf))

                target_phi = tf.cast(tf.reshape(tf.stack(
                    np.sqrt(pdf)), [1] + [2 for _ in range(self.nspins + 1)]),
                                     dtype=tf.complex64)
            else:
                self.qve = QuantumVariationalEigensolver(
                    hamiltonian,
                    qc,
                    device="GPU",
                    optimizer=optimizer,
                    exact=True,
                    feed_in_hamiltonian_terms=True,
                    thermal=self.thermal)
                target_phi = tf.cast(tf.reshape(tf.stack(
                    np.sqrt(pdf)), [1] + [2 for _ in range(self.nspins)]),
                                     dtype=tf.complex64)
            expval_layer = ExpectationValue(self.qve.obs)
            target_expvals = expval_layer(target_phi)
        self.target_expvals = self.qve.qc._sess.run(target_expvals).flatten()
        self.TRAIN = False
Exemplo n.º 16
0
    def get_statistics(self, plot=True):
        r"""
        Compare the ground state statistics of the ``QuantumCircuit`` with the exact ground state statistics.

        .. math::

            \text{MSE}_{h} = \frac{1}{M} \sum_i^M (\langle \sigma_i \rangle - \langle \hat{\sigma}_i \rangle)^2 \\
            \text{MSE}_{w} = \frac{1}{M} \sum_{i,j}^M (\langle \sigma_i \sigma_j  \rangle - \langle \hat{\sigma}_i \hat{\sigma}_j\rangle)^2

        Args:
            *plot (bool)*:
                Whether to plot the training schedule.

        Returns (dict):
            Dict with entries 'field' and 'coupling' with the respective MSE between the circuit and true statistics.

        """
        assert self.exact, "QVE needs to be called with argument exact=True to compare the statistics"
        # calculate expvals from groundstate wavefunction
        with tf.name_scope("exact_expvals"):
            gs = tf.reshape(tf.constant(self.gs_wavefunction, dtype=TF_COMPLEX_DTYPE),
                            [1] + [2 for _ in range(self.qc.nqubits)])
            expectation_layer = ExpectationValue(self.obs)
            exact_expvals = expectation_layer(gs)
        exact_expvals = self.qc._sess.run(flatten(exact_expvals))
        qc_expvals = self.qc._sess.run(flatten(self.expvals), feed_dict={self.theta_ph: self.final_theta})

        mse = {}
        possible_fields = ['x', 'y', 'z']
        available_fields = [f for f in possible_fields if f in self.hamiltonian.interaction_slices.keys()]
        if available_fields:
            fig_fields, ax_fields = plt.subplots(2, len(available_fields))
            ax_fields = ax_fields.reshape((2, -1))
            pauli_f_names = dict(zip(possible_fields, [r"$\sigma^{}_i$".format(s[0]) for s in possible_fields]))
            fields_vals_qc = {}
            fields_vals_exact = {}
            for i, f in enumerate(available_fields):
                fields_vals_qc[f] = qc_expvals[
                                    self.hamiltonian.interaction_slices[f][0]:self.hamiltonian.interaction_slices[f][1]]
                fields_vals_exact[f] = exact_expvals[
                                       self.hamiltonian.interaction_slices[f][0]:self.hamiltonian.interaction_slices[f][
                                           1]]

                field_mse = np.mean((fields_vals_qc[f] - fields_vals_exact[f]) ** 2)
                mse['field'] = field_mse
                print("Field statistics {} MSE: {}".format(f, field_mse))
                if plot:
                    # make the figure for the fields
                    norm = mpl.colors.Normalize(vmin=-1, vmax=1)
                    stats = np.zeros((self.qc.nqubits))
                    for j, link in enumerate(self.hamiltonian.link_order[f]):
                        stats[link] = fields_vals_exact[f][j]
                    m = ax_fields[0, i].matshow(stats.reshape(1, -1), vmin=-1, vmax=1)
                    ax_fields[0, i].set_title('{}: exact'.format(pauli_f_names[f]))
                    ax_fields[0, i].grid(b=True, color='black', linewidth=2)
                    ax_fields[0, i].set_yticks([])
                    ax_fields[0, i].set_xticks([])
                    divider = make_axes_locatable(ax_fields[0, i])
                    cax = divider.append_axes('right', size='5%', pad=0.05)
                    fig_fields.colorbar(m, cax=cax, orientation='vertical', norm=norm)

                    stats = np.zeros((self.qc.nqubits))
                    for j, link in enumerate(self.hamiltonian.link_order[f]):
                        stats[link] = fields_vals_qc[f][j]
                    m = ax_fields[1, i].matshow(stats.reshape(1, -1), vmin=-1, vmax=1)
                    ax_fields[1, i].set_title('{}: circuit'.format(pauli_f_names[f]))
                    ax_fields[1, i].grid(b=True, color='black', linewidth=2)
                    ax_fields[1, i].set_yticks([])
                    ax_fields[1, i].set_xticks([])
                    divider = make_axes_locatable(ax_fields[1, i])
                    cax = divider.append_axes('right', size='5%', pad=0.05)
                    fig_fields.colorbar(m, cax=cax, orientation='vertical', norm=norm)
        # get the couplings
        possible_couplings = [''.join(f) for f in it.product(['x', 'y', 'z'], repeat=2)]
        available_couplings = [f for f in possible_couplings if f in self.hamiltonian.interaction_slices.keys()]
        if available_couplings:
            fig_couplings, ax_couplings = plt.subplots(2, len(available_couplings))
            ax_couplings = ax_couplings.reshape((2, -1))
            pauli_c_names = dict(
                zip(possible_couplings, [r"$\sigma^{}_i\sigma^{}_j$".format(s[0], s[1]) for s in possible_couplings]))
            coupling_vals_qc = {}
            coupling_vals_exact = {}
            for i, f in enumerate(available_couplings):
                coupling_vals_qc[f] = qc_expvals[
                                      self.hamiltonian.interaction_slices[f][0]:self.hamiltonian.interaction_slices[f][
                                          1]]
                coupling_vals_exact[f] = exact_expvals[self.hamiltonian.interaction_slices[f][0]:
                                                       self.hamiltonian.interaction_slices[f][1]]
                coupling_mse = np.mean((coupling_vals_qc[f] - coupling_vals_exact[f]) ** 2)
                mse['coupling'] = coupling_mse
                print("Coupling statistics {} MSE: {}".format(f, coupling_mse))
                if plot:
                    # make the figure for the couplings
                    norm = mpl.colors.Normalize(vmin=-1, vmax=1)
                    stats = np.zeros((self.qc.nqubits, self.qc.nqubits))
                    for j, link in enumerate(self.hamiltonian.link_order[f]):
                        stats[link[0], link[1]] = coupling_vals_exact[f][j]
                    m = ax_couplings[0, i].matshow(stats, vmin=-1, vmax=1)
                    ax_couplings[0, i].set_title('{}: exact'.format(pauli_c_names[f]))
                    ax_couplings[0, i].grid(b=True, color='black', linewidth=2)
                    ax_couplings[0, i].set_yticks([])
                    ax_couplings[0, i].set_xticks([])

                    divider = make_axes_locatable(ax_couplings[0, i])
                    cax = divider.append_axes('right', size='5%', pad=0.05)
                    fig_couplings.colorbar(m, cax=cax, orientation='vertical', norm=norm)

                    # make the figure for the couplings
                    norm = mpl.colors.Normalize(vmin=-1, vmax=1)
                    stats = np.zeros((self.qc.nqubits, self.qc.nqubits))
                    for j, link in enumerate(self.hamiltonian.link_order[f]):
                        stats[link[0], link[1]] = coupling_vals_qc[f][j]
                    m = ax_couplings[1, i].matshow(stats, vmin=-1, vmax=1)
                    ax_couplings[1, i].set_title('{}: circuit'.format(pauli_c_names[f]))
                    ax_couplings[1, i].grid(b=True, color='black', linewidth=2)
                    ax_couplings[1, i].set_yticks([])
                    ax_couplings[1, i].set_xticks([])
                    divider = make_axes_locatable(ax_couplings[1, i])
                    cax = divider.append_axes('right', size='5%', pad=0.05)
                    fig_couplings.colorbar(m, cax=cax, orientation='vertical', norm=norm)
        return mse