C2QA / bosonic-qiskit

Extension of Qiskit to support hybrid boson-qubit simulations for the NQI C2QA effort.
BSD 2-Clause "Simplified" License
44 stars 8 forks source link

On documentation for the cutoff property in CVCircuit #86

Closed liu-zixiong closed 6 months ago

liu-zixiong commented 1 year ago

(1) Currently, within CVCircuit, the cutoff property is computed for a single QumodeRegister. (2) In particular, the cutoff is computed for a single qumode within the qumoderegister, as we find in QumodeRegister class that "self.cutoff = 2**self.num_qubits_per_qumode". So the size of QumodeRegister(2, 2) will involve 4 qubits, but qumode cutoff is calculated with 2 qubits.

However, the circuit sends a warning for case (1) only ["More than one QumodeRegister provided. Using the last one for cutoff.", circuit.py] and case (2) is not clarified to the user. I think this subtlety should be noted clearly, especially in CVOperators, given that the dimension of the attribute operators (a, a_dag, n, identity) are dependent on the cutoff. I found it more difficult to debug when defining new operators as I initially made the assumption that the attributes were applied on the entire register.

This might also be an issue when multiple qumoderegisters with differing cutoffs are created in a circuit, especially on operators like cv_bs which involves multiple qumodes. As issue #84 still exists, I can't initialize states onto the registers to check what actually happens. But we can still demonstrate that there is concerning behaviour with regards to dimensionality.

For the sake of testing, add in a print statement into bs() in operators.py

def bs(self, theta):
    self.a1 = scipy.sparse.kron(self.a, self.eye).tocsc()
    self.a2 = scipy.sparse.kron(self.eye, self.a).tocsc()
    self.a1_dag = self.a1.conjugate().transpose().tocsc()
    self.a2_dag = self.a2.conjugate().transpose().tocsc()

    a12dag = self.a1 * self.a2_dag
    a1dag2 = self.a1_dag * self.a2

    arg = theta * a1dag2 - numpy.conj(theta) * a12dag
    print(arg.shape) #<----- Added for sake of testing
    return scipy.sparse.linalg.expm(arg)

When we run the following two circuit cases, we observe that the dimension of the argument that is returned by bs is the same for both cases, even though the two circuits involve a different number of qubits.

import c2qa
if True:
    qmr = c2qa.QumodeRegister(2, 2)
    circuit = c2qa.CVCircuit(qmr)

    circuit.cv_bs(theta=1, qumode_a=qmr[0], qumode_b=qmr[1])

    _, result = c2qa.util.simulate(circuit, shots=1)

if True:
    qmr1 = c2qa.QumodeRegister(1, 1)
    qmr2 = c2qa.QumodeRegister(1, 2)
    circuit_bad = c2qa.CVCircuit(qmr1, qmr2)

    circuit_bad.cv_bs(theta=1, qumode_a=qmr1[0], qumode_b=qmr2[0])

    _, result = c2qa.util.simulate(circuit_bad, shots=1)

I'm not sure if this actually affects the computation of the results. But if this does pose an issue down the line after debugging #84, it might be beneficial to make the cutoff property more general so that it can be calculated separately for each qumode within each QumodeRegister, for multiple registers.

tjstavenger-pnnl commented 1 year ago

If we want to support cutoff values per QumodeRegister, we'll need to pass in the register index into our functions that create the operators. Or we'll need to develop a way to lookup which register contains the qubits (as we'd need for #84 too). That way we'd be able to get the register and lookup its individual cutoff instead of using the last register's cutoff everywhere.

We'd probably want to cache less of the matrices too (like a, a_dag, etc.) or we'd want to cache all of them as we'll need to lookup the correct matrices for those depending on the cutoff, too.

tjstavenger-pnnl commented 6 months ago

Made minor updates to documentation for where cutoff is stored. I believe work on this issue is complete now.