diff --git a/qctrlopencontrols/__init__.py b/qctrlopencontrols/__init__.py index 4f5fb900..e586f815 100644 --- a/qctrlopencontrols/__init__.py +++ b/qctrlopencontrols/__init__.py @@ -20,7 +20,7 @@ from .dynamic_decoupling_sequences import (DynamicDecouplingSequence, new_predefined_dds, - convert_dds_to_driven_controls) + convert_dds_to_driven_control) 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, diff --git a/qctrlopencontrols/driven_controls/driven_control.py b/qctrlopencontrols/driven_controls/driven_control.py index 18043bb3..874de8b6 100644 --- a/qctrlopencontrols/driven_controls/driven_control.py +++ b/qctrlopencontrols/driven_controls/driven_control.py @@ -31,54 +31,6 @@ UPPER_BOUND_DURATION, LOWER_BOUND_DURATION) -def get_plot_data_from_segments(segments): - """ - Generates arrays that can be used to produce a plot representing the shape of the driven control - constructed from the segments. - - Parameters - ---------- - segments : list - List of segments formatted as described in qctrlopencontrols.driven_controls.DrivenControl - - Returns - ------- - tuple - Tuple made up of arrays for plotting formatted as - (amplitude_x,amplitude_y,amplitude_z,time) where: - - amplitude_k is the amplitude values. - - times the time corresponding to each amplitude_k coordinate. - Note that plot will have repeated times and for amplitudes, this is because it is - expected that these coordinates are to be used with plotting software that 'joins - the dots' with linear lines between each coordinate. The time array gives the x - values for all the amplitude arrays, which give the y values. - - """ - segment_times = np.insert(np.cumsum(segments[:, 3]), 0, 0.) - coords = len(segment_times) - coord_amplitude_x = np.concatenate([[0.], segments[:, 0], [0.]]) - coord_amplitude_y = np.concatenate([[0.], segments[:, 1], [0.]]) - coord_amplitude_z = np.concatenate([[0.], segments[:, 2], [0.]]) - plot_time = [] - plot_amplitude_x = [] - plot_amplitude_y = [] - plot_amplitude_z = [] - for i in range(coords): - plot_time.append(segment_times[i]) - plot_time.append(segment_times[i]) - plot_amplitude_x.append(coord_amplitude_x[i]) - plot_amplitude_x.append(coord_amplitude_x[i + 1]) - plot_amplitude_y.append(coord_amplitude_y[i]) - plot_amplitude_y.append(coord_amplitude_y[i + 1]) - plot_amplitude_z.append(coord_amplitude_z[i]) - plot_amplitude_z.append(coord_amplitude_z[i + 1]) - - return (np.array(plot_amplitude_x), - np.array(plot_amplitude_y), - np.array(plot_amplitude_z), - np.array(plot_time)) - - class DrivenControl(QctrlObject): #pylint: disable=too-few-public-methods """ Creates a driven control. A driven is a set of segments made up of amplitude vectors @@ -545,43 +497,64 @@ def get_plot_formatted_arrays(self, coordinates=CARTESIAN, dimensionless_rabi_ra ArgumentsValueError Raised when an argument is invalid. """ + if coordinates not in [CARTESIAN, CYLINDRICAL]: + raise ArgumentsValueError( + 'Unsupported coordinates provided: ', + arguments={'coordinates': coordinates}) + if dimensionless_rabi_rate: normalizer = self.maximum_rabi_rate else: normalizer = 1 if coordinates == CARTESIAN: - (x_amplitudes, y_amplitudes, detunings, times) = get_plot_data_from_segments( - np.vstack((self.amplitude_x / normalizer, self.amplitude_y / normalizer, - self.detunings, self.durations)).T - ) + control_segments = np.vstack(( + self.amplitude_x / normalizer, + self.amplitude_y / normalizer, + self.detunings, + self.durations)).T + elif coordinates == CYLINDRICAL: + control_segments = np.vstack(( + self.rabi_rates / normalizer, + self.azimuthal_angles, + self.detunings, + self.durations)).T + + segment_times = np.insert(np.cumsum(control_segments[:, 3]), 0, 0.) + plot_time = (segment_times[:, np.newaxis] * np.ones((1, 2))).flatten() + plot_amplitude_x = control_segments[:, 0] + plot_amplitude_y = control_segments[:, 1] + plot_amplitude_z = control_segments[:, 2] + + plot_amplitude_x = np.concatenate( + ([0.], (plot_amplitude_x[:, np.newaxis] * np.ones((1, 2))).flatten(), [0.])) + plot_amplitude_y = np.concatenate( + ([0.], (plot_amplitude_y[:, np.newaxis] * np.ones((1, 2))).flatten(), [0.])) + plot_amplitude_z = np.concatenate( + ([0.], (plot_amplitude_z[:, np.newaxis] * np.ones((1, 2))).flatten(), [0.])) + + plot_dictionary = {} + if coordinates == CARTESIAN: plot_dictionary = { - 'amplitudes_x': x_amplitudes, - 'amplitudes_y': y_amplitudes, - 'detunings': detunings, - 'times': times - } + 'amplitudes_x': plot_amplitude_x, + 'amplitudes_y': plot_amplitude_y, + 'detunings': plot_amplitude_z, + 'times': plot_time} - elif coordinates == CYLINDRICAL: - (x_plot, y_plot, detunings, times) = get_plot_data_from_segments( - np.vstack((self.rabi_rates / normalizer, self.azimuthal_angles, - self.detunings, self.durations)).T - ) + if coordinates == CYLINDRICAL: + + x_plot = plot_amplitude_x + y_plot = plot_amplitude_y x_plot[np.equal(x_plot, -0.0)] = 0. y_plot[np.equal(y_plot, -0.0)] = 0. azimuthal_angles_plot = np.arctan2(y_plot, x_plot) amplitudes_plot = np.sqrt(np.abs(x_plot**2 + y_plot**2)) + plot_dictionary = { 'rabi_rates': amplitudes_plot, 'azimuthal_angles': azimuthal_angles_plot, - 'detunings': detunings, - 'times': times - } - else: - raise ArgumentsValueError( - 'Unsupported coordinates provided: ', - arguments={'coordinates': coordinates}) - + 'detunings': plot_amplitude_z, + 'times': plot_time} return plot_dictionary def __str__(self): diff --git a/qctrlopencontrols/dynamic_decoupling_sequences/__init__.py b/qctrlopencontrols/dynamic_decoupling_sequences/__init__.py index 3d2218a8..a71c2aa7 100644 --- a/qctrlopencontrols/dynamic_decoupling_sequences/__init__.py +++ b/qctrlopencontrols/dynamic_decoupling_sequences/__init__.py @@ -26,4 +26,4 @@ from .dynamic_decoupling_sequence import DynamicDecouplingSequence from .predefined import new_predefined_dds -from .driven_controls import convert_dds_to_driven_controls +from .driven_controls import convert_dds_to_driven_control diff --git a/qctrlopencontrols/dynamic_decoupling_sequences/driven_controls.py b/qctrlopencontrols/dynamic_decoupling_sequences/driven_controls.py index 63c8e820..228dc192 100644 --- a/qctrlopencontrols/dynamic_decoupling_sequences/driven_controls.py +++ b/qctrlopencontrols/dynamic_decoupling_sequences/driven_controls.py @@ -95,7 +95,7 @@ def _check_maximum_rotation_rate( 'allowed_maximum_detuning_rate': UPPER_BOUND_DETUNING_RATE}) -def convert_dds_to_driven_controls( +def convert_dds_to_driven_control( dynamic_decoupling_sequence=None, maximum_rabi_rate=2*np.pi, maximum_detuning_rate=2*np.pi, diff --git a/qctrlopencontrols/dynamic_decoupling_sequences/dynamic_decoupling_sequence.py b/qctrlopencontrols/dynamic_decoupling_sequences/dynamic_decoupling_sequence.py index 3fc10d32..61f13e0c 100644 --- a/qctrlopencontrols/dynamic_decoupling_sequences/dynamic_decoupling_sequence.py +++ b/qctrlopencontrols/dynamic_decoupling_sequences/dynamic_decoupling_sequence.py @@ -27,7 +27,7 @@ QCTRL_EXPANDED, CSV, CYLINDRICAL) from .constants import (UPPER_BOUND_OFFSETS, MATPLOTLIB) -from .driven_controls import convert_dds_to_driven_controls +from .driven_controls import convert_dds_to_driven_control class DynamicDecouplingSequence(QctrlObject): #pylint: disable=too-few-public-methods @@ -292,7 +292,7 @@ def export_to_file(self, filename=None, integration with Q-CTRL BLACK OPAL's 1-Qubit workspace. """ - driven_control = convert_dds_to_driven_controls( + driven_control = convert_dds_to_driven_control( dynamic_decoupling_sequence=self, maximum_rabi_rate=maximum_rabi_rate, maximum_detuning_rate=maximum_detuning_rate, diff --git a/tests/test_dynamical_decoupling.py b/tests/test_dynamical_decoupling.py index b0666dc4..e65ed4cd 100644 --- a/tests/test_dynamical_decoupling.py +++ b/tests/test_dynamical_decoupling.py @@ -22,7 +22,7 @@ import numpy as np from qctrlopencontrols.exceptions import ArgumentsValueError from qctrlopencontrols import ( - DynamicDecouplingSequence, convert_dds_to_driven_controls) + DynamicDecouplingSequence, convert_dds_to_driven_control) def _remove_file(filename): @@ -298,10 +298,10 @@ def test_conversion_to_driven_controls(): _maximum_rabi_rate = 20*np.pi _maximum_detuning_rate = 20*np.pi - driven_control = convert_dds_to_driven_controls(dd_sequence, - maximum_rabi_rate=_maximum_rabi_rate, - maximum_detuning_rate=_maximum_detuning_rate, - name=_name) + driven_control = convert_dds_to_driven_control(dd_sequence, + maximum_rabi_rate=_maximum_rabi_rate, + maximum_detuning_rate=_maximum_detuning_rate, + name=_name) assert np.allclose(driven_control.rabi_rates, np.array( [0., _maximum_rabi_rate, 0., 0., 0., @@ -336,7 +336,7 @@ def test_free_evolution_conversion(): _maximum_rabi_rate = 20 * np.pi _maximum_detuning_rate = 20 * np.pi - driven_control = convert_dds_to_driven_controls( + driven_control = convert_dds_to_driven_control( dd_sequence, maximum_rabi_rate=_maximum_rabi_rate, maximum_detuning_rate=_maximum_detuning_rate, @@ -368,7 +368,7 @@ def test_free_evolution_conversion(): _maximum_rabi_rate = 20 * np.pi _maximum_detuning_rate = 20 * np.pi - driven_control = convert_dds_to_driven_controls( + driven_control = convert_dds_to_driven_control( dd_sequence, maximum_rabi_rate=_maximum_rabi_rate, maximum_detuning_rate=_maximum_detuning_rate, @@ -405,10 +405,11 @@ def test_export_to_file(): _maximum_rabi_rate = 20 * np.pi _maximum_detuning_rate = 20 * np.pi - driven_control = convert_dds_to_driven_controls(dd_sequence, - maximum_rabi_rate=_maximum_rabi_rate, - maximum_detuning_rate=_maximum_detuning_rate, - name=_name) + driven_control = convert_dds_to_driven_control( + dd_sequence, + maximum_rabi_rate=_maximum_rabi_rate, + maximum_detuning_rate=_maximum_detuning_rate, + name=_name) _filename = 'dds_qctrl_cylindrical.csv' driven_control.export_to_file( @@ -448,6 +449,5 @@ def test_export_to_file(): _remove_file('dds_qctrl_cartesian.json') - if __name__ == '__main__': pass