Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
49 commits
Select commit Hold shift + click to select a range
e6b9912
working version of dds to cirq objects
May 22, 2019
f902915
cirq module added to qctrlopencontrols
May 22, 2019
3a2b22f
Merge branch 'circuit-update' into cirq
May 22, 2019
d70f62f
linted
May 22, 2019
f78932a
tests for cirq circuits added
May 22, 2019
224fff9
docstring fixed/modified
May 22, 2019
08bc84b
first draft of the notebook example added
May 22, 2019
4afc86b
modified test method; added cirq dependency in setup
May 22, 2019
a1833c8
notebook updated; test docstring modified
May 22, 2019
c0e332f
section for schedule added
May 22, 2019
be172b0
Cirq docstring fixed
May 23, 2019
5a851b7
a link from cirq_circuit docstring removed
May 23, 2019
c452889
typos corrected
May 23, 2019
6ab61ed
Return docstring fixed
May 23, 2019
38be35f
Merge branch 'master' into cirq
michaelhush May 23, 2019
176b107
pre-post gate behaviour changed; tests modified and all pass
May 23, 2019
4b59627
notebooks checked and updated
May 23, 2019
22b0cdc
notebooks checked and updated
May 23, 2019
133b91b
module docstring fixed
May 23, 2019
171dba9
merging upates related to pre-post gate behaviour
May 23, 2019
ac5e0e7
cirq conversion method update with new changes of pre-post gate behav…
May 23, 2019
b59cab6
cirq example notebook updated with pre-post gate behaviour
May 23, 2019
0f03a7d
linted
May 23, 2019
3c86754
cirq conversion method fixed according to updated behaviour wrt pre-p…
May 24, 2019
166e897
algorithm keyword docstring update
May 24, 2019
31daa26
Update cirq notebook with minor changes in the text
May 24, 2019
9f0b945
merged with master
May 24, 2019
af55d6e
Merge branch 'cirq' of github.com:qctrl/python-open-controls into cirq
May 24, 2019
2486644
docstring for pre-post rotation modified slightly
May 24, 2019
7ea6d18
harrys comments applied on qiskit method too
May 24, 2019
46a94ea
notebooks renamed, titles updated
May 24, 2019
4b295d2
readme file updated with the new names of the notebook files and adde…
May 24, 2019
e2cd204
circuit, schedule separated; tests pass
May 27, 2019
228f256
module dosctring updated
May 27, 2019
edd85f2
cirq notebook updated with separate method for circuit and schedule; …
May 27, 2019
b3ffaed
both notebooks: some latex formatting fixed; qiskit notebook- gate_ti…
May 27, 2019
463f518
notebook updates merged
May 27, 2019
25cda21
cirq files dosctring udpated to only contain either circuit or schedu…
May 27, 2019
ef95119
Minor text correction in qiskit
May 29, 2019
f27def8
Minor link fix in notebook
michaelhush May 30, 2019
9355810
get methods removed from qiskit; all methods are now self contained
May 30, 2019
8dc9294
minor text changes in cirq
May 30, 2019
529507a
linted
May 30, 2019
7f9d2a2
id gate insert logic fixed
May 30, 2019
048a160
merged changes after notebooks changed
May 30, 2019
ab842fe
cirq notebook run again after fixing the id gate insert logic
May 30, 2019
b56bc2a
Merge branch 'cirq' of github.com:qctrl/python-open-controls into cirq
May 30, 2019
6f04813
cirq notebook synced with remote
May 30, 2019
f22f5ee
get_circuit_list finally removed from all files
May 30, 2019
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 6 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,12 @@ Q-CTRL Open Controls can create a large library of standard DDS which can be exp

#### Export a DDS to Qiskit

[`examples/running_a_dds_on_ibm_q.ipynb`](examples/running_a_dds_on_ibm_q.ipynb) demonstrates how to take a DDS and convert it to a Qiskit circuit so it can be run on IBM's quantum computers. It also demonstrates using a DDS to improve the performance of a quantum circuit execution by extending the coherence time of a qubit.
[`examples/export_a_dds_on_qiskit.ipynb`](examples/export_a_dds_on_qiskit.ipynb) demonstrates how to take a DDS and convert it to a Qiskit circuit so it can be run on IBM's quantum computers. It also demonstrates using a DDS to improve the performance of a quantum circuit execution by extending the coherence time of a qubit.

#### Export a DDS to Cirq

[`examples/export_a_dds_on_cirq.ipynb`](examples/export_a_dds_on_cirq.ipynb) demonstrates how to take a DDS and convert it to a Cirq circuit or schdule. It also shows how to run a circuit or schedule in a Cirq simulator.


## Contributing

Expand Down
91 changes: 49 additions & 42 deletions examples/creating_a_dds.ipynb

Large diffs are not rendered by default.

362 changes: 362 additions & 0 deletions examples/export_a_dds_to_cirq.ipynb

Large diffs are not rendered by default.

769 changes: 769 additions & 0 deletions examples/export_a_dds_to_qiskit.ipynb

Large diffs are not rendered by default.

801 changes: 0 additions & 801 deletions examples/running_a_dds_on_ibm_q.ipynb

This file was deleted.

2 changes: 2 additions & 0 deletions qctrlopencontrols/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,3 +23,5 @@
convert_dds_to_driven_controls)
from .driven_controls import DrivenControl, new_predefined_driven_control
from .qiskit import convert_dds_to_quantum_circuit
from .cirq import (convert_dds_to_cirq_circuit,
convert_dds_to_cirq_schedule)
Original file line number Diff line number Diff line change
Expand Up @@ -13,23 +13,10 @@
# limitations under the License.

"""
================
qiskit.constants
================
=============
cirq module
=============
"""

import numpy as np

FIX_DURATION_UNITARY = 'fixed duration unitary'
"""Algorithm to convert a DDS to Quantum circuit
where the unitaries are considered as gates with finite duration
"""

INSTANT_UNITARY = 'instant unitary'
"""Algorithm to convert a DDS to Quantum circuit where the
unitaties are considered as instantaneous operation.
"""

DEFAULT_PRE_POST_GATE_PARAMETERS = (np.pi / 2, -np.pi / 2, np.pi / 2)
"""Parameters of a default U3 gate for pre-post rotation for circuits.
"""
from .circuit import convert_dds_to_cirq_circuit
from .schedule import convert_dds_to_cirq_schedule
203 changes: 203 additions & 0 deletions qctrlopencontrols/cirq/circuit.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,203 @@
# Copyright 2019 Q-CTRL Pty Ltd & Q-CTRL Inc
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

"""
============
cirq.circuit
============
"""

import numpy as np

import cirq

from qctrlopencontrols.dynamic_decoupling_sequences import DynamicDecouplingSequence
from qctrlopencontrols.exceptions import ArgumentsValueError
from qctrlopencontrols.globals import (FIX_DURATION_UNITARY, INSTANT_UNITARY)


def convert_dds_to_cirq_circuit(
dynamic_decoupling_sequence,
target_qubits=None,
gate_time=0.1,
add_measurement=True,
algorithm=INSTANT_UNITARY):

"""Converts a Dynamic Decoupling Sequence into quantum circuit
as defined in cirq

Parameters
----------
dynamic_decoupling_sequence : DynamicDecouplingSequence
The dynamic decoupling sequence
target_qubits : list, optional
List of target qubits for the sequence operation; the qubits must be
cirq.Qid type; defaults to None in which case a 1-D lattice of one
qubit is used (indexed as 0).
gate_time : float, optional
Time (in seconds) delay introduced by a gate; defaults to 0.1
add_measurement : bool, optional
If True, the circuit contains a measurement operation for each of the
target qubits. Measurement from each of the qubits is associated
with a string as key. The string is formatted as 'qubit-X' where
X is a number between 0 and len(target_qubits).
algorithm : str, optional
One of 'fixed duration unitary' or 'instant unitary'; In the case of
'fixed duration unitary', the sequence operations are assumed to be
taking the amount of gate_time while 'instant unitary' assumes the sequence
operations are instantaneous (and hence does not contribute to the delay between
offsets). Defaults to 'instant unitary'.

Returns
-------
cirq.Circuit
The circuit containing gates corresponding to sequence operation.

Raises
------
ArgumentsValueError
If any of the input parameters result in an invalid operation.

Notes
-----

Dynamic Decoupling Sequences (DDS) consist of idealized pulse operation. Theoretically,
these operations (pi-pulses in X,Y or Z) occur instantaneously. However, in practice,
pulses require time. Therefore, this method of converting an idealized sequence
results to a circuit that is only an approximate implementation of the idealized sequence.

In idealized definition of DDS, `offsets` represents the instances within sequence
`duration` where a pulse occurs instantaneously. A series of appropriate circuit components
is placed in order to represent these pulses.

In 'standard circuit', the `gaps` or idle time in between active pulses are filled up
with `identity` gates. Each identity gate introduces a delay of `gate_time`. In this
implementation, the number of identity gates is determined by
:math:`np.int(np.floor(offset_distance / gate_time))`. As a consequence,
:math:`np.int(np.floor(offset_distance / gate_time))`. As a consequence,
the duration of the real-circuit is :math:`gate_time \\times number_of_identity_gates +
pulse_gate_time \\times number_of_pulses`.

Q-CTRL Open Controls support operation resulting in rotation around at most one axis at
any offset.
"""

if dynamic_decoupling_sequence is None:
raise ArgumentsValueError('No dynamic decoupling sequence provided.',
{'dynamic_decoupling_sequence': dynamic_decoupling_sequence})

if not isinstance(dynamic_decoupling_sequence, DynamicDecouplingSequence):
raise ArgumentsValueError('Dynamical decoupling sequence is not recognized.'
'Expected DynamicDecouplingSequence instance',
{'type(dynamic_decoupling_sequence)':
type(dynamic_decoupling_sequence)})

if gate_time <= 0:
raise ArgumentsValueError(
'Time delay of gates must be greater than zero.',
{'gate_time': gate_time})

if target_qubits is None:
target_qubits = [cirq.LineQubit(0)]

if algorithm not in [FIX_DURATION_UNITARY, INSTANT_UNITARY]:
raise ArgumentsValueError('Algorithm must be one of {} or {}'.format(
INSTANT_UNITARY, FIX_DURATION_UNITARY), {'algorithm': algorithm})

unitary_time = 0.
if algorithm == FIX_DURATION_UNITARY:
unitary_time = gate_time

rabi_rotations = dynamic_decoupling_sequence.rabi_rotations
azimuthal_angles = dynamic_decoupling_sequence.azimuthal_angles
detuning_rotations = dynamic_decoupling_sequence.detuning_rotations

if len(rabi_rotations.shape) == 1:
rabi_rotations = rabi_rotations[np.newaxis, :]
if len(azimuthal_angles.shape) == 1:
azimuthal_angles = azimuthal_angles[np.newaxis, :]
if len(detuning_rotations.shape) == 1:
detuning_rotations = detuning_rotations[np.newaxis, :]

operations = np.vstack((rabi_rotations, azimuthal_angles, detuning_rotations))
offsets = dynamic_decoupling_sequence.offsets

time_covered = 0
circuit = cirq.Circuit()
for operation_idx in range(operations.shape[1]):

offset_distance = offsets[operation_idx] - time_covered

if np.isclose(offset_distance, 0.0):
offset_distance = 0.0

if offset_distance < 0:
raise ArgumentsValueError("Offsets cannot be placed properly",
{'sequence_operations': operations})

if offset_distance > 0:
while (time_covered+gate_time) <= offsets[operation_idx]:
gate_list = []
for qubit in target_qubits:
gate_list.append(cirq.I(qubit))
time_covered += gate_time
circuit.append(gate_list)

rabi_rotation = operations[0, operation_idx]
azimuthal_angle = operations[1, operation_idx]
x_rotation = rabi_rotation * np.cos(azimuthal_angle)
y_rotation = rabi_rotation * np.sin(azimuthal_angle)
z_rotation = operations[2, operation_idx]

rotations = np.array([x_rotation, y_rotation, z_rotation])
zero_pulses = np.isclose(rotations, 0.0).astype(np.int)
nonzero_pulse_counts = 3 - np.sum(zero_pulses)
if nonzero_pulse_counts > 1:
raise ArgumentsValueError(
'Open Controls support a sequence with one '
'valid pulse at any offset. Found sequence '
'with multiple rotation operations at an offset.',
{'dynamic_decoupling_sequence': str(dynamic_decoupling_sequence),
'offset': dynamic_decoupling_sequence.offsets[operation_idx],
'rabi_rotation': dynamic_decoupling_sequence.rabi_rotations[
operation_idx],
'azimuthal_angle': dynamic_decoupling_sequence.azimuthal_angles[
operation_idx],
'detuning_rotaion': dynamic_decoupling_sequence.detuning_rotations[
operation_idx]}
)

gate_list = []
for qubit in target_qubits:
if nonzero_pulse_counts == 0:
gate_list.append(cirq.I(qubit))
else:
if not np.isclose(rotations[0], 0.0):
gate_list.append(cirq.Rx(rotations[0])(qubit))
elif not np.isclose(rotations[1], 0.0):
gate_list.append(cirq.Ry(rotations[1])(qubit))
elif not np.isclose(rotations[2], 0.):
gate_list.append(cirq.Rz(rotations[2])(qubit))
circuit.append(gate_list)
if np.isclose(np.sum(rotations), 0.0):
time_covered = offsets[operation_idx]
else:
time_covered = offsets[operation_idx] + unitary_time
if add_measurement:
gate_list = []
for idx, qubit in enumerate(target_qubits):
gate_list.append(cirq.measure(qubit, key='qubit-{}'.format(idx)))
circuit.append(gate_list)

return circuit
Loading