|
38 | 38 | WAMF1, |
39 | 39 | ) |
40 | 40 | from ..exceptions import ArgumentsValueError |
| 41 | +from ..utils import check_arguments |
41 | 42 | from .driven_control import DrivenControl |
42 | 43 |
|
43 | 44 |
|
@@ -812,3 +813,128 @@ def _new_walsh_amplitude_modulated_filter_1_control( |
812 | 813 | durations=durations, |
813 | 814 | **kwargs, |
814 | 815 | ) |
| 816 | + |
| 817 | + |
| 818 | +def new_modulated_gaussian_control( |
| 819 | + maximum_rabi_rate: float, |
| 820 | + minimum_segment_duration: float, |
| 821 | + duration: float, |
| 822 | + modulation_frequency: float, |
| 823 | +) -> DrivenControl: |
| 824 | + """ |
| 825 | + Generate a Gaussian driven control sequence modulated by a sinusoidal signal at a specific |
| 826 | + frequency. |
| 827 | +
|
| 828 | + The net effect of this control sequence is an identity gate. |
| 829 | +
|
| 830 | + Parameters |
| 831 | + ---------- |
| 832 | + maximum_rabi_rate: float |
| 833 | + Maximum Rabi rate of the system. |
| 834 | +
|
| 835 | + minimum_segment_duration : float |
| 836 | + Minimum length of each segment in the control sequence. |
| 837 | +
|
| 838 | + duration : float |
| 839 | + Total duration of the control sequence. |
| 840 | +
|
| 841 | + modulation_frequency: float |
| 842 | + Frequency of the modulation sinusoidal signal. |
| 843 | +
|
| 844 | + Returns |
| 845 | + ------- |
| 846 | + DrivenControl |
| 847 | + A control sequence as an instance of DrivenControl. |
| 848 | + """ |
| 849 | + |
| 850 | + check_arguments( |
| 851 | + maximum_rabi_rate > 0.0, |
| 852 | + "Maximum Rabi rate must be greater than zero.", |
| 853 | + {"maximum_rabi_rate": maximum_rabi_rate}, |
| 854 | + ) |
| 855 | + |
| 856 | + check_arguments( |
| 857 | + minimum_segment_duration > 0.0, |
| 858 | + "Minimum segment duration must be greater than zero.", |
| 859 | + {"minimum_segment_duration": minimum_segment_duration}, |
| 860 | + ) |
| 861 | + |
| 862 | + check_arguments( |
| 863 | + duration > minimum_segment_duration, |
| 864 | + "Total duration must be greater than minimum segment duration.", |
| 865 | + {"duration": duration, "minimum_segment_duration": minimum_segment_duration,}, |
| 866 | + ) |
| 867 | + |
| 868 | + # default spread of the gaussian shaped pulse as a fraction of its duration |
| 869 | + _pulse_width = 0.1 |
| 870 | + |
| 871 | + # default mean of the gaussian shaped pulse as a fraction of its duration |
| 872 | + _pulse_mean = 0.5 |
| 873 | + |
| 874 | + min_required_upper_bound = np.sqrt(2 * np.pi) / (_pulse_width * duration) |
| 875 | + check_arguments( |
| 876 | + maximum_rabi_rate >= min_required_upper_bound, |
| 877 | + "Maximum Rabi rate must be large enough to permit a 2Pi rotation.", |
| 878 | + {"maximum_rabi_rate": maximum_rabi_rate}, |
| 879 | + extras={ |
| 880 | + "minimum required value for upper_bound " |
| 881 | + "(sqrt(2pi)/(0.1*maximum_duration))": min_required_upper_bound |
| 882 | + }, |
| 883 | + ) |
| 884 | + |
| 885 | + # work out exact segment duration |
| 886 | + segment_count = int(np.ceil(duration / minimum_segment_duration)) |
| 887 | + segment_duration = duration / segment_count |
| 888 | + segment_start_times = np.arange(segment_count) * segment_duration |
| 889 | + segment_midpoints = segment_start_times + segment_duration / 2 |
| 890 | + |
| 891 | + # prepare a base gaussian shaped pulse |
| 892 | + gaussian_mean = _pulse_mean * duration |
| 893 | + gaussian_width = _pulse_width * duration |
| 894 | + base_gaussian_segments = np.exp( |
| 895 | + -0.5 * ((segment_midpoints - gaussian_mean) / gaussian_width) ** 2 |
| 896 | + ) |
| 897 | + |
| 898 | + if modulation_frequency != 0: |
| 899 | + # prepare the modulation signals. We use sinusoids that are zero at the center of the pulse, |
| 900 | + # which ensures the pulses are antisymmetric about the center of the pulse and thus effect |
| 901 | + # a net zero rotation. |
| 902 | + modulation_signals = np.sin( |
| 903 | + 2.0 * np.pi * modulation_frequency * (segment_midpoints - duration / 2) |
| 904 | + ) |
| 905 | + # modulate the base gaussian |
| 906 | + modulated_gaussian_segments = base_gaussian_segments * modulation_signals |
| 907 | + |
| 908 | + # maximum segment value |
| 909 | + pulse_segments_maximum = np.max(modulated_gaussian_segments) |
| 910 | + # normalize to maximum Rabi rate |
| 911 | + modulated_gaussian_segments = ( |
| 912 | + maximum_rabi_rate * modulated_gaussian_segments / pulse_segments_maximum |
| 913 | + ) |
| 914 | + else: |
| 915 | + # for the zero-frequency pulse, we need to produce the largest possible full rotation (i.e. |
| 916 | + # multiple of 2pi) while respecting the maximum Rabi rate. Note that if the maximum Rabi |
| 917 | + # rate does not permit even a single rotation (which could happen to a small degree due to |
| 918 | + # discretization issues) then we allow values to exceed the maximum Rabi rate. |
| 919 | + normalized_gaussian_segments = base_gaussian_segments / np.max( |
| 920 | + base_gaussian_segments |
| 921 | + ) |
| 922 | + maximum_rotation_angle = ( |
| 923 | + segment_duration * np.sum(normalized_gaussian_segments) * maximum_rabi_rate |
| 924 | + ) |
| 925 | + maximum_full_rotation_angle = max( |
| 926 | + maximum_rotation_angle - maximum_rotation_angle % (2 * np.pi), 2 * np.pi |
| 927 | + ) |
| 928 | + modulated_gaussian_segments = ( |
| 929 | + normalized_gaussian_segments |
| 930 | + * maximum_rabi_rate |
| 931 | + * (maximum_full_rotation_angle / maximum_rotation_angle) |
| 932 | + ) |
| 933 | + |
| 934 | + azimuthal_angles = [0 if v >= 0 else np.pi for v in modulated_gaussian_segments] |
| 935 | + |
| 936 | + return DrivenControl( |
| 937 | + rabi_rates=np.abs(modulated_gaussian_segments), |
| 938 | + azimuthal_angles=azimuthal_angles, |
| 939 | + durations=np.array([segment_duration] * segment_count), |
| 940 | + ) |
0 commit comments