Skip to content

Commit 1221328

Browse files
authored
feat: Allow mypy to check untyped defs (#165)
1 parent e6fbce2 commit 1221328

File tree

3 files changed

+107
-145
lines changed

3 files changed

+107
-145
lines changed

mypy.ini

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
[mypy]
22
files = qctrlopencontrols/**/*.py,tests/**/*.py
3-
check_untyped_defs = False
3+
check_untyped_defs = True
44

55
[mypy-deprecation.*]
66
ignore_missing_imports = True

qctrlopencontrols/driven_controls/driven_control.py

Lines changed: 102 additions & 141 deletions
Original file line numberDiff line numberDiff line change
@@ -15,12 +15,15 @@
1515
"""
1616
Driven control module.
1717
"""
18+
import csv
1819
import json
19-
from typing import Optional
20+
from typing import (
21+
Dict,
22+
Optional,
23+
)
2024

2125
import numpy as np
2226

23-
from ..exceptions import ArgumentsValueError
2427
from ..utils import (
2528
Coordinate,
2629
FileFormat,
@@ -327,119 +330,88 @@ def duration(self) -> float:
327330

328331
return np.sum(self.durations)
329332

330-
def _qctrl_expanded_export_content(self, file_type, coordinates):
333+
def _qctrl_expanded_export_content(self, coordinates: str) -> Dict:
331334
"""
332335
Prepare the content to be saved in Q-CTRL expanded format.
333336
334337
Parameters
335338
----------
336-
file_type : str, optional
337-
One of 'CSV' or 'JSON'; defaults to 'CSV'.
338339
coordinates : str, optional
339-
Indicates the co-ordinate system requested. Must be one of
340-
'cylindrical', 'cartesian' or 'polar'; defaults to 'cylindrical'
340+
Indicates the co-ordinate system requested. Must be
341+
'cylindrical'or 'cartesian'. Defaults to 'cylindrical'.
341342
342343
Returns
343344
-------
344-
list or dict
345-
Based on file_type; list if 'CSV', dict if 'JSON'
345+
Dict
346+
A dictionary containing the information of the control.
346347
"""
347-
control_info = None
348-
amplitude_x = self.amplitude_x
349-
amplitude_y = self.amplitude_y
350-
if coordinates == Coordinate.CARTESIAN.value:
351-
if file_type == FileType.CSV.value:
352-
control_info = list()
353-
control_info.append(
354-
"amplitude_x,amplitude_y,detuning,duration,maximum_rabi_rate"
355-
)
356-
for segment_idx in range(self.number_of_segments):
357-
control_info.append(
358-
"{},{},{},{},{}".format(
359-
amplitude_x[segment_idx] / self.maximum_rabi_rate,
360-
amplitude_y[segment_idx] / self.maximum_rabi_rate,
361-
self.detunings[segment_idx],
362-
self.durations[segment_idx],
363-
self.maximum_rabi_rate,
364-
)
365-
)
366-
else:
367-
control_info = dict()
368-
if self.name is not None:
369-
control_info["name"] = self.name
370-
control_info["maximum_rabi_rate"] = self.maximum_rabi_rate
371-
control_info["amplitude_x"] = list(amplitude_x / self.maximum_rabi_rate)
372-
control_info["amplitude_y"] = list(amplitude_y / self.maximum_rabi_rate)
373-
control_info["detuning"] = list(self.detunings)
374-
control_info["duration"] = list(self.durations)
375348

376-
else:
349+
control_info = {
350+
"maximum_rabi_rate": self.maximum_rabi_rate,
351+
"detuning": list(self.detunings),
352+
"duration": list(self.durations),
353+
}
377354

378-
if file_type == FileType.CSV.value:
379-
control_info = list()
380-
control_info.append(
381-
"rabi_rate,azimuthal_angle,detuning,duration,maximum_rabi_rate"
382-
)
383-
for segment_idx in range(self.number_of_segments):
384-
control_info.append(
385-
"{},{},{},{},{}".format(
386-
self.rabi_rates[segment_idx] / self.maximum_rabi_rate,
387-
self.azimuthal_angles[segment_idx],
388-
self.detunings[segment_idx],
389-
self.durations[segment_idx],
390-
self.maximum_rabi_rate,
391-
)
392-
)
355+
if self.name is not None:
356+
control_info["name"] = self.name
393357

394-
else:
395-
control_info = dict()
396-
if self.name is not None:
397-
control_info["name"] = self.name
398-
control_info["maximum_rabi_rate"] = self.maximum_rabi_rate
399-
control_info["rabi_rates"] = list(
400-
self.rabi_rates / self.maximum_rabi_rate
401-
)
402-
control_info["azimuthal_angles"] = list(self.azimuthal_angles)
403-
control_info["detuning"] = list(self.detunings)
404-
control_info["duration"] = list(self.durations)
358+
if coordinates == Coordinate.CARTESIAN.value:
359+
control_info["amplitude_x"] = list(
360+
self.amplitude_x / self.maximum_rabi_rate
361+
)
362+
control_info["amplitude_y"] = list(
363+
self.amplitude_y / self.maximum_rabi_rate
364+
)
365+
else:
366+
control_info["rabi_rates"] = list(self.rabi_rates / self.maximum_rabi_rate)
367+
control_info["azimuthal_angles"] = list(self.azimuthal_angles)
405368

406369
return control_info
407370

408371
def _export_to_qctrl_expanded_format(
409372
self,
410-
filename=None,
373+
filename,
411374
file_type=FileType.CSV.value,
412375
coordinates=Coordinate.CYLINDRICAL.value,
413376
):
414-
"""Private method to save control in qctrl_expanded_format
377+
"""
378+
Saves control in qctrl_expanded_format.
415379
416380
Parameters
417381
----------
418-
filename : str, optional
382+
filename : str
419383
Name and path of the file to save the control into.
420-
Defaults to None
421384
file_type : str, optional
422385
One of 'CSV' or 'JSON'; defaults to 'CSV'.
423386
coordinates : str, optional
424387
Indicates the co-ordinate system requested. Must be one of
425388
'cylindrical', 'cartesian'; defaults to 'cylindrical'
426389
"""
427390

428-
control_info = self._qctrl_expanded_export_content(
429-
file_type=file_type, coordinates=coordinates
430-
)
391+
control_info = self._qctrl_expanded_export_content(coordinates=coordinates)
431392
if file_type == FileType.CSV.value:
432-
with open(filename, "wt") as handle:
433-
434-
control_info = "\n".join(control_info)
435-
handle.write(control_info)
393+
_ = control_info.pop("name")
394+
control_info["maximum_rabi_rate"] = [
395+
self.maximum_rabi_rate
396+
] * self.number_of_segments
397+
field_names = sorted(control_info.keys())
398+
399+
# note that the newline parameter here is necessary
400+
# see details at https://docs.python.org/3/library/csv.html#id3
401+
with open(filename, "w", newline="") as file:
402+
writer = csv.DictWriter(file, fieldnames=field_names)
403+
writer.writeheader()
404+
for index in range(self.number_of_segments):
405+
writer.writerow(
406+
{name: control_info[name][index] for name in field_names}
407+
)
436408
else:
437409
with open(filename, "wt") as handle:
438410
json.dump(control_info, handle, sort_keys=True, indent=4)
439411

440412
def export_to_file(
441413
self,
442-
filename=None,
414+
filename,
443415
file_format=FileFormat.QCTRL.value,
444416
file_type=FileType.CSV.value,
445417
coordinates=Coordinate.CYLINDRICAL.value,
@@ -460,11 +432,6 @@ def export_to_file(
460432
The coordinate system in which to save the control. Must be 'cylindrical' or
461433
'cartesian'. Defaults to 'cylindrical'.
462434
463-
Raises
464-
------
465-
ArgumentsValueError
466-
Raised if some of the parameters are invalid.
467-
468435
Notes
469436
-----
470437
The Q-CTRL expanded format is designed for direct integration of control solutions into
@@ -513,31 +480,26 @@ def export_to_file(
513480
_file_formats = [v.value for v in FileFormat]
514481
_coordinate_systems = [v.value for v in Coordinate]
515482

516-
if filename is None:
517-
raise ArgumentsValueError(
518-
"Invalid filename provided.", {"filename": filename}
519-
)
520-
521-
if file_format not in _file_formats:
522-
raise ArgumentsValueError(
523-
"Requested file format is not supported. Please use "
524-
"one of {}".format(_file_formats),
525-
{"file_format": file_format},
526-
)
483+
check_arguments(
484+
file_format in _file_formats,
485+
"Requested file format is not supported. Please use "
486+
"one of {}".format(_file_formats),
487+
{"file_format": file_format},
488+
)
527489

528-
if file_type not in _file_types:
529-
raise ArgumentsValueError(
530-
"Requested file type is not supported. Please use "
531-
"one of {}".format(_file_types),
532-
{"file_type": file_type},
533-
)
490+
check_arguments(
491+
file_type in _file_types,
492+
"Requested file type is not supported. Please use "
493+
"one of {}".format(_file_types),
494+
{"file_type": file_type},
495+
)
534496

535-
if coordinates not in _coordinate_systems:
536-
raise ArgumentsValueError(
537-
"Requested coordinate type is not supported. Please use "
538-
"one of {}".format(_coordinate_systems),
539-
{"coordinates": coordinates},
540-
)
497+
check_arguments(
498+
coordinates in _coordinate_systems,
499+
"Requested coordinate type is not supported. Please use "
500+
"one of {}".format(_coordinate_systems),
501+
{"coordinates": coordinates},
502+
)
541503

542504
if file_format == FileFormat.QCTRL.value:
543505
self._export_to_qctrl_expanded_format(
@@ -567,18 +529,13 @@ def export(
567529
method of the ``qctrl-visualizer`` package. It has keywords 'Rabi rate'
568530
and 'Detuning' for 'cylindrical' coordinates and 'X amplitude', 'Y amplitude',
569531
and 'Detuning' for 'cartesian' coordinates.
570-
571-
Raises
572-
------
573-
ArgumentsValueError
574-
Raised when an argument is invalid.
575532
"""
576533

577-
if coordinates not in [v.value for v in Coordinate]:
578-
raise ArgumentsValueError(
579-
"Unsupported coordinates provided: ",
580-
arguments={"coordinates": coordinates},
581-
)
534+
check_arguments(
535+
coordinates in [v.value for v in Coordinate],
536+
"Unsupported coordinates provided: ",
537+
{"coordinates": coordinates},
538+
)
582539

583540
if dimensionless_rabi_rate:
584541
normalizer = self.maximum_rabi_rate
@@ -618,46 +575,50 @@ def __str__(self):
618575
"""
619576
Prepares a friendly string format for a Driven Control.
620577
"""
621-
driven_control_string = list()
578+
driven_control = list()
622579

623580
if self.name is not None:
624-
driven_control_string.append("{}:".format(self.name))
581+
driven_control.append("{}:".format(self.name))
582+
583+
pretty_rabi_rates = ",".join(
584+
[
585+
str(rabi_rate / self.maximum_rabi_rate)
586+
if self.maximum_rabi_rate != 0
587+
else "0"
588+
for rabi_rate in self.rabi_rates
589+
]
590+
)
625591

626-
pretty_rabi_rates = [
627-
str(rabi_rate / self.maximum_rabi_rate)
628-
if self.maximum_rabi_rate != 0
629-
else "0"
630-
for rabi_rate in list(self.rabi_rates)
631-
]
632-
pretty_rabi_rates = ",".join(pretty_rabi_rates)
633-
pretty_azimuthal_angles = [
634-
str(azimuthal_angle / np.pi) for azimuthal_angle in self.azimuthal_angles
635-
]
636-
pretty_azimuthal_angles = ",".join(pretty_azimuthal_angles)
637-
pretty_detuning = [
638-
str(detuning / self.maximum_detuning) if self.maximum_detuning != 0 else "0"
639-
for detuning in list(self.detunings)
640-
]
641-
pretty_detuning = ",".join(pretty_detuning)
592+
pretty_azimuthal_angles = ",".join(
593+
[str(azimuthal_angle / np.pi) for azimuthal_angle in self.azimuthal_angles]
594+
)
642595

643-
pretty_durations = [
644-
str(duration / self.duration) for duration in self.durations
645-
]
646-
pretty_durations = ",".join(pretty_durations)
596+
pretty_detuning = ",".join(
597+
[
598+
str(detuning / self.maximum_detuning)
599+
if self.maximum_detuning != 0
600+
else "0"
601+
for detuning in self.detunings
602+
]
603+
)
604+
605+
pretty_durations = ",".join(
606+
[str(duration / self.duration) for duration in self.durations]
607+
)
647608

648-
driven_control_string.append(
609+
driven_control.append(
649610
"Rabi Rates = [{}] x {}".format(pretty_rabi_rates, self.maximum_rabi_rate)
650611
)
651-
driven_control_string.append(
612+
driven_control.append(
652613
"Azimuthal Angles = [{}] x pi".format(pretty_azimuthal_angles)
653614
)
654-
driven_control_string.append(
615+
driven_control.append(
655616
"Detunings = [{}] x {}".format(pretty_detuning, self.maximum_detuning)
656617
)
657-
driven_control_string.append(
618+
driven_control.append(
658619
"Durations = [{}] x {}".format(pretty_durations, self.duration)
659620
)
660-
driven_control_string = "\n".join(driven_control_string)
621+
driven_control_string = "\n".join(driven_control)
661622

662623
return driven_control_string
663624

qctrlopencontrols/dynamic_decoupling_sequences/predefined.py

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -186,7 +186,7 @@ def new_ramsey_sequence(duration, pre_post_rotation=False, name=None):
186186
offsets = duration * np.array([0.0, 1.0])
187187
rabi_rotations = np.array([np.pi / 2, np.pi / 2])
188188
azimuthal_angles = np.array([0.0, np.pi])
189-
detuning_rotations = np.zeros(offsets.shape)
189+
detuning_rotations = np.zeros((2,))
190190

191191
return DynamicDecouplingSequence(
192192
duration=duration,
@@ -915,12 +915,13 @@ def new_x_concatenated_sequence(
915915

916916
values, counts = np.unique(pos_cum_sum, return_counts=True)
917917

918-
offsets = [value for value, count in zip(values, counts) if count % 2 == 0]
918+
offsets = np.array(
919+
[value for value, count in zip(values, counts) if count % 2 == 0]
920+
)
919921

920922
if concatenation_order % 2 == 1:
921923
offsets = offsets[:-1]
922924

923-
offsets = np.array(offsets)
924925
rabi_rotations = np.zeros(offsets.shape)
925926
rabi_rotations[0:] = np.pi
926927
azimuthal_angles = np.zeros(offsets.shape)

0 commit comments

Comments
 (0)