1515"""
1616Driven control module.
1717"""
18+ import csv
1819import json
19- from typing import Optional
20+ from typing import (
21+ Dict ,
22+ Optional ,
23+ )
2024
2125import numpy as np
2226
23- from ..exceptions import ArgumentsValueError
2427from ..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
0 commit comments