Skip to content

Commit

Permalink
Merge pull request #2 from tjstavenger-pnnl/code-review
Browse files Browse the repository at this point in the history
Code review
  • Loading branch information
tjstavenger-pnnl authored Nov 20, 2020
2 parents 5dccd9a + 9b639a5 commit 9f82b30
Show file tree
Hide file tree
Showing 8 changed files with 320 additions and 162 deletions.
4 changes: 2 additions & 2 deletions .github/workflows/python-package.yml
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,9 @@ name: Python package

on:
push:
branches: [ main ]
branches: [ '**' ]
pull_request:
branches: [ main ]
branches: [ '**' ]

jobs:
build:
Expand Down
3 changes: 3 additions & 0 deletions c2qa/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,3 +3,6 @@

from c2qa.circuit import CVCircuit
from c2qa.qumoderegister import QumodeRegister

import logging
logging.getLogger(__name__).addHandler(logging.NullHandler())
59 changes: 39 additions & 20 deletions c2qa/circuit.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,36 +3,55 @@
from c2qa.operators import CVOperators
from c2qa.qumoderegister import QumodeRegister
import numpy
import warnings


class CVCircuit(QuantumCircuit):
def __init__(self, qmr: QumodeRegister, qr: QuantumRegister, cr: ClassicalRegister, name: str = None):
super().__init__(qmr.qreg, qr, cr, name=name)
def __init__(self, *regs, name: str = None):
self.qmr = None
registers = []

self.qmr = qmr
self.qr = qr
self.cr = cr
for reg in regs:
if isinstance(reg, QumodeRegister):
if self.qmr is not None:
warnings.warn("More than one QumodeRegister provided. Using the last one for cutoff.", UserWarning)
self.qmr = reg
registers.append(self.qmr.qreg)
else:
registers.append(reg)

if self.qmr is None:
raise ValueError("At least one QumodeRegister must be provided.")

self.ops = CVOperators(self.qmr)
super().__init__(*registers, name=name)

def cv_initialize(self, fock_states):
""" Initialize qumodes to the given fock_states. """
for qumode, n in enumerate(fock_states):
if n >= self.qmr.cutoff:
raise ValueError("The parameter n should be lower than the cutoff")
self.ops = CVOperators(self.qmr.cutoff)

vector = numpy.zeros((self.qmr.cutoff,))
vector[n] = 1
def cv_initialize(self, fock_state, qumodes):
""" Initialize the qumode to a Fock state. """

super().initialize(vector, self.qmr[qumode])
# Qumodes are already represented as arrays of qubits,
# but if this is an array of arrays, then we are initializing multiple qumodes.
modes = qumodes
if not isinstance(qumodes[0], list):
modes = [qumodes]

if fock_state > self.qmr.cutoff:
raise ValueError("The given Fock state is greater than the cutoff.")

for qumode in modes:
value = numpy.zeros((self.qmr.cutoff,))
value[fock_state] = 1

super().initialize(value, [qumode])

def cv_conditional(self, name, op_a, op_b):
""" Make two operators conditional (i.e., controlled by qubit in either the 0 or 1 state) """
sub_qmr = QumodeRegister(self.qmr.num_qumodes, self.qmr.num_qubits_per_mode)
sub_qmr = QumodeRegister(1, self.qmr.num_qubits_per_mode)
sub_qr = QuantumRegister(1)
sub_circ = QuantumCircuit(sub_qmr.qreg, sub_qr, name=name)
sub_circ.append(UnitaryGate(op_a).control(num_ctrl_qubits=1, ctrl_state=0), [sub_qr[0]] + sub_qmr[0])
sub_circ.append(UnitaryGate(op_b).control(num_ctrl_qubits=1, ctrl_state=1), [sub_qr[0]] + sub_qmr[1])
sub_circ.append(UnitaryGate(op_b).control(num_ctrl_qubits=1, ctrl_state=1), [sub_qr[0]] + sub_qmr[0])

return sub_circ.to_instruction()

Expand All @@ -46,8 +65,8 @@ def cv_d(self, alpha, qumode):

self.unitary(obj=operator, qubits=qumode, label='D')

def cv_cnd_d(self, alpha, beta, ctrl, qumode_a, qumode_b):
self.append(self.cv_conditional('Dc', self.ops.d(alpha), self.ops.d(beta)), [ctrl] + qumode_a + qumode_b)
def cv_cnd_d(self, alpha, beta, ctrl, qumode_a):
self.append(self.cv_conditional('Dc', self.ops.d(alpha), self.ops.d(beta)), [ctrl] + qumode_a)

def cv_r(self, phi, qumode):
operator = self.ops.r(phi)
Expand All @@ -59,8 +78,8 @@ def cv_s(self, z, qumode):

self.unitary(obj=operator, qubits=qumode, label='S')

def cv_cnd_s(self, z_a, z_b, ctrl, qumode_a, qumode_b):
self.append(self.cv_conditional('Sc', self.ops.s(z_a), self.ops.s(z_b)), [ctrl] + qumode_a + qumode_b)
def cv_cnd_s(self, z_a, z_b, ctrl, qumode_a):
self.append(self.cv_conditional('Sc', self.ops.s(z_a), self.ops.s(z_b)), [ctrl] + qumode_a)

def cv_s2(self, z, qumode_a, qumode_b):
operator = self.ops.s2(z)
Expand Down
16 changes: 7 additions & 9 deletions c2qa/operators.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,9 @@


class CVOperators:
def __init__(self, qmr: QumodeRegister):
def __init__(self, cutoff: int):
# Annihilation operator
self.a = np.zeros((qmr.cutoff, qmr.cutoff))
for i in range(qmr.cutoff - 1):
self.a[i, i + 1] = np.sqrt(i + 1)
self.a = np.diag(np.sqrt(range(1, cutoff)), k=1)

# Creation operator
self.a_dag = self.a.conj().T
Expand All @@ -17,21 +15,21 @@ def __init__(self, qmr: QumodeRegister):
self.N = np.matmul(self.a_dag, self.a)

# 2-qumodes operators
eye = np.eye(qmr.cutoff)
eye = np.eye(cutoff)
self.a1 = np.kron(self.a, eye)
self.a2 = np.kron(eye, self.a)
self.a1_dag = self.a1.conj().T
self.a2_dag = self.a2.conj().T

def bs(self, phi):
def bs(self, g):
""" Two-mode beam splitter opertor """
a12dag = np.matmul(self.a1, self.a2_dag)
a1dag2 = np.matmul(self.a1_dag, self.a2)

# FIXME -- See Steve 5.4
# phi as g(t)
# - as +, but QisKit validates that not being unitary
arg = (phi * a12dag) - (np.conjugate(phi) * a1dag2)
arg = (g * -1j * a12dag) - (np.conjugate(g * -1j) * a1dag2)

return expm(arg)

Expand All @@ -55,14 +53,14 @@ def s(self, zeta):

return expm(arg)

def s2(self, zeta):
def s2(self, g):
""" Two-mode squeezing operator """
a12_dag = np.matmul(self.a1_dag, self.a2_dag)
a12 = np.matmul(self.a1, self.a2)

# FIXME -- See Steve 5.7
# zeta as g(t)
# use of imaginary, but QisKit validates that is not unitary
arg = (np.conjugate(zeta) * a12_dag) - (zeta * a12)
arg = (np.conjugate(g) * a12_dag) - (g * a12)

return expm(arg)
19 changes: 15 additions & 4 deletions c2qa/qumoderegister.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,19 @@ def __init__(self, num_qumodes: int, num_qubits_per_mode: int = 2, name: str = N
# (i.e., qmr[0] is represented by multiple qubits).
self.qreg = QuantumRegister(size=self.size, name=name)

def __getitem__(self, key: int):
start = self.num_qubits_per_mode * key
stop = start + self.num_qubits_per_mode
def __getitem__(self, key):
start = None
stop = self.size
step = None

return self.qreg[start:stop]
if isinstance(key, slice):
start = self.num_qubits_per_mode * key.start
stop = (self.num_qubits_per_mode * key.stop) + self.num_qubits_per_mode
step = (key.step * self.num_qubits_per_mode) if key.step else None
elif isinstance(key, int):
start = self.num_qubits_per_mode * key
stop = start + self.num_qubits_per_mode
else:
raise ValueError("Must provide slice or int.")

return self.qreg[start:stop:step]
31 changes: 31 additions & 0 deletions tests/test_circuit.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
import c2qa
import pytest
import qiskit


def test_no_registers():
with pytest.raises(ValueError):
c2qa.CVCircuit()


def test_only_quantumregister():
with pytest.raises(ValueError):
qr = qiskit.QuantumRegister(1)
c2qa.CVCircuit(qr)


def test_only_qumoderegister():
c2qa.CVCircuit(c2qa.QumodeRegister(1, 1))


def test_multiple_qumoderegisters():
with pytest.warns(UserWarning):
c2qa.CVCircuit(c2qa.QumodeRegister(1, 1), c2qa.QumodeRegister(1, 1))


def test_correct():
c2qa.CVCircuit(qiskit.QuantumRegister(1), c2qa.QumodeRegister(1, 1))


def test_with_classical():
c2qa.CVCircuit(qiskit.QuantumRegister(1), c2qa.QumodeRegister(1, 1), qiskit.ClassicalRegister(1))
Loading

0 comments on commit 9f82b30

Please sign in to comment.