Ancillary Subsystem Measurement then Trace Out

I have a system with N total qubits, N_A ancillary qubits, and N-N_A data qubits.

How would I be able to implement the following:

  • Take the partial measurement, (|0\rangle \langle 0|)^{\otimes^{N_A}}, on the ancillary subsystem
  • Trace out the ancillary qubits leaving the post-measurement state \rho
  • Measure the probability of each computational basis state given \rho

Any ideas or thoughts would be greatly appreciated.

Thanks!

Hi @James_Ellis,

I don’t think we have mid-circuit measurement or density matrix representations implemented in PennyLane yet.

That being said, we can still answer the question manually. For a simple two qubit system we could do something like:

dev = qml.device('default.qubit',wires=2)
@qml.qnode(dev)
def circuit():
    qml.RY(2.7, wires=0)
    qml.RY(2.1,wires=1)
    return qml.probs()

probs=circuit()
bitstrings = [format(i,'02b') for i in range(4)]

probsgiven0 =[]
for index,bitstring in enumerate(bitstrings):
    if bitstring[0]=='0':
        probsgiven0.append(probs[index])

probsgiven0 /= np.sum(probsgiven0)

print(probs)
print(bitstrings)
print(probsgiven0)
>>> [0.01  0.033 0.249 0.708]
>>> ['00', '01', '10', '11']
>>> [0.23255814   0.76744186]

Here we are just taking the probabilities of over all of the qubits and manually calculating the probability that the second qubit is in state 0 or 1 given that the first qubit was measured as 0.

This code goes through the trouble of using bit strings to give intuition about which qubits we are working with. But we could skip the bit strings and just be careful with our array indexing.

Let me know if this helps or if there are further questions!

2 Likes

Instead of the for loop, could we do something like probsgiven0 = probs[:(2**N / 2**N_A)] where N is the number of qubits, while N_A represents the number of ancillary qubits, assuming a partial measurement of (|0\rangle \langle 0|)^{\otimes^{N_A}}?

I also have another question, is there a way to implement this all within the circuit() function. When I try to save qml.probs() to variable within the circuit() function , I receive TypeError: 'MeasurementProcess' object is not subscriptable for the following code:

dev = qml.device("default.qubit", wires=n_qubits)
@qml.qnode(dev, interface='torch', diff_method="parameter-shift")

def quantum_circuit(input, weights):

    #Some parameterised quantum circuit 

    #Perform a partial measurement |0><0| tensorprod N_A on the ancillary subsystem
    probs = qml.probs(wires=list(range(n_qubits)))
    probsgiven0 = probs[:(2**(n_qubits - n_a_qubits))]
    probsgiven0 /= np.sum(probs)
    return probsgiven0

This should would work as long as the data qubits are in the bottom part of the qubit register. I also want to emphasize that these will be the probabilities when the N_A qubits are all measured as zero.

I don’t think it’s possible to use qml.probs this way. Would it be useful to call the parameterised circuit from inside a standard python function? Something like this:

dev = qml.device("default.qubit", wires=n_qubits)
@qml.qnode(dev, interface='torch', diff_method="parameter-shift")

def parameterised_circuit(input, weights):
    #some circuit
    return qml.probs()

def regular_function(input, weights):
   
    parameterised_circuit(input,weights)

    #Perform a partial measurement |0><0| tensorprod N_A on the ancillary subsystem
    probs = parameterised_circuit(input,weights)
    probsgiven0 = probs[:(2**(n_qubits - n_a_qubits))]
    probsgiven0 /= np.sum(list(probs))
    return probsgiven0
1 Like

Guys, I know the thread is old, but I was running some tests and I noticed that in James’ first answer, the probabilities are correctly re-normalized:

dev = qml.device('default.qubit',wires=2)
@qml.qnode(dev)
def circuit():
    qml.RY(2.7, wires=0)
    qml.RY(2.1,wires=1)
    return qml.probs()

probs=circuit()
bitstrings = [format(i,'02b') for i in range(4)]

probsgiven0 =[]
for index,bitstring in enumerate(bitstrings):
    if bitstring[0]=='0':
        probsgiven0.append(probs[index])

probsgiven0 /= np.sum(probsgiven0)

print(probs)
print(bitstrings)
print(probsgiven0)
>>> [0.01  0.033 0.249 0.708]
>>> ['00', '01', '10', '11']
>>> [0.23255814   0.76744186]

However, in the last answer this line does not give the correct normalized probas:

probsgiven0 /= np.sum(list(probs))

Since the sum of probs is always 1.0, I would just correct it to:

probsgiven0 /= torch.sum(probsgiven0)

Definitely an old thread, but I think you might be right. :thinking: Thanks for spotting it!
But it looks like torch.sum wants the input to be a torch.Tensor, so we could stick with NumPy here: probsgiven0 /= np.sum(probsgiven0).

Indeed numpy is the correct way for this snippet. I had put in torch format because this code is part of the QGAN demo - Quantum GANs (pennylane.ai), which uses tensors instead.

Good point, thanks for adding! :slight_smile: