Swap test using different registers

Hey, I’m trying to perform a swap test on two quantum states that I’ve stored in two registers/ devices. I’ve initialized the devices as follows:

import pennylane as qml
from pennylane import numpy as np
from pennylane.templates import AmplitudeEmbedding

# define two random input vectors
inp1 = np.array([1,2])
inp2 = np.array([0,1])

qubit_number = int(np.ceil(np.log2(len(inp1))))

reg1 = qml.device('default.qubit', wires=qubit_number)
reg2 = qml.device('default.qubit', wires=qubit_number)
anc = qml.device('default.qubit', wires=1)

@qml.qnode(reg1)
def prepare_reg1(inp):
    AmplitudeEmbedding(features=inp, wires=range(qubit_number), normalize=True, pad_with=0.)
    return qml.expval(qml.PauliZ(0))

@qml.qnode(reg2)
def prepare_reg2(inp):
    AmplitudeEmbedding(features=inp, wires=range(qubit_number), normalize=True, pad_with=0.)
    return qml.expval(qml.PauliZ(0))

prepare_reg1(inp1)
prepare_reg2(inp2)

Now my question is, how do I perform the necessary CSWAP operations between my different registers/devices. Is there a way of concatenating the devices such that a swap test could be implemented, i.e generating a device in the state “|anc> @ |inp1> @ |inp2>”?

Thank you!

Hi @Thomas,

Welcome to the forum and thank you so much for your question! :slight_smile:

There is no way, unfortunately, to perform operations between quantum functions that are being executed on separate devices. The concept of a QNode necessitates that it takes classical inputs and provides classical outputs, so every quantum operation is applied within the QNode itself. Therefore the best approach might be to use a single device and a single QNode.

The following could be an approach for performing the swap test using a single device and a single QNode:

import pennylane as qml
from pennylane import numpy as np
from pennylane.templates import AmplitudeEmbedding

# define two random input vectors
inp1 = np.array([1,2,0,0])
inp2 = np.array([0,1,0,0])

qubit_number = int(np.ceil(np.log2(len(inp1))))

dev = qml.device('default.qubit', wires=3 * qubit_number)

@qml.qnode(dev)
def prepare_reg1(inp):
    AmplitudeEmbedding(features=inp, wires=range(qubit_number, 3 * qubit_number), normalize=True, pad_with=0.)
    
    ancillea = []
    for i in range(qubit_number):

        anc = i
        ancillea.append(anc)
        first_state = i+qubit_number
        second_state = i+ 2*qubit_number
        qml.CSWAP(wires=[anc, first_state, second_state])

    return qml.expval(qml.operation.Tensor(*[qml.PauliZ(i) for i in ancillea]))

input_state = np.concatenate([inp1, inp2])
prepare_reg1(input_state)

print(prepare_reg1.draw())
 0: ─────────────────────────────────╭C─────╭┤ ⟨Z ⊗ Z⟩ 
 1: ──────────────────────────╭C─────│──────╰┤ ⟨Z ⊗ Z⟩ 
 2: ──╭QubitStateVector(M0)──│──────├SWAP───┤         
 3: ──├QubitStateVector(M0)──├SWAP──│───────┤         
 4: ──├QubitStateVector(M0)──│──────╰SWAP───┤         
 5: ──╰QubitStateVector(M0)──╰SWAP──────────┤         
M0 =
[0.40824829 0.81649658 0.         0.         0.         0.40824829
 0.         0.         0.         0.         0.         0.
 0.         0.         0.         0.        ]

Note that you may want to update the type of statistics obtained on the ancillae qubits.

Let us know if this would be something that works for you or if you’d have any further questions!

Hey, thanks a lot! This looks already close to what I’m looking for. Actually, I’m trying to implement the swap test code on Wikipedia (https://en.wikipedia.org/wiki/Swap_test) to estimate the inner product of two vectors, thus, I guess I only need one ancilla qubit. However, I don’t quite get how you initialized the two input states. Is there a reason that you expanded the vectors with zeros so that they have length 4? Also, after concatenation and using an amplitude embedding, how can we be sure, that the two input vectors are stored in two different qubit registers? Right now I feel like this is not the case, or am I wrong?

Hi @Thomas,

I guess I only need one ancilla qubit

You’ll need one ancilla qubit for every pair of qubits that you would like to compare. So taking your original example, correct, you will only need one ancilla qubit.

Is there a reason that you expanded the vectors with zeros so that they have length 4?

Not at all, the expansion was done only so that we’d have a multiqubit basis state preparation. Just made that to generalize the idea for multiple ancillae.

Also, after concatenation and using an amplitude embedding, how can we be sure, that the two input vectors are stored in two different qubit registers? Right now I feel like this is not the case, or am I wrong?

This is guaranteed because of how amplitude embedding works. We provision a vector of features which will be encoded into the amplitudes of the qubits that we’ve specified. We are required to provide different wires and every feature is encoded into a separate qubit.

When we concatenate the two vector of features (each containing 2^n features), we shift to a higher-order feature space with dimension 2n. The concatenated vector will contain 2^{2n} features. When we then embed the concatenated features into a quantum circuit, the number of qubits needed for the encoding also increases (and will be 2n, equal to the dimension of the feature space).

In the n=1 case this will mean that we can concatenate two input vectors of length 2, to create a single feature vector of length 4. These features are then embedded into log_2 4=2 qubits. We can then compare the state of these two qubits by introducing an ancilla qubit and performing the SWAP test.