def _test_Lambdify_Piecewise(Lambdify): x = se.symbols('x') p = se.Piecewise((-x, x < 0), (x * x * x, True)) f = Lambdify([x], [p]) arr = np.linspace(3, 7) assert np.allclose(f(-arr).flat, arr, atol=1e-14, rtol=1e-15) assert np.allclose(f(arr).flat, arr**3, atol=1e-14, rtol=1e-15)
def piecewise_matrix(*piecewise_vector): # TODO testme # FIXME support non 2d matrices? dimensions = piecewise_vector[0][0].shape for m, condition in piecewise_vector: assert m.shape == dimensions matrix = se.zeros(*dimensions) for x in range(dimensions[0]): for y in range(dimensions[1]): piecewise_entry = [] for m, condition in piecewise_vector: piecewise_entry.append([m[x, y], condition]) matrix[x, y] = se.Piecewise(*piecewise_entry) return matrix
def __init__( self, duration: Union[int, ParameterExpression], amp: Union[complex, ParameterExpression], name: Optional[str] = None, limit_amplitude: Optional[bool] = None, ): """Create new pulse instance. Args: duration: Pulse length in terms of the sampling period `dt`. amp: The amplitude of the constant square pulse. name: Display name for this pulse envelope. limit_amplitude: If ``True``, then limit the amplitude of the waveform to 1. The default is ``True`` and the amplitude is constrained to 1. """ parameters = {"amp": amp} # Prepare symbolic expressions _t, _amp, _duration = sym.symbols("t, amp, duration") # Note this is implemented using Piecewise instead of just returning amp # directly because otherwise the expression has no t dependence and sympy's # lambdify will produce a function f that for an array t returns amp # instead of amp * np.ones(t.shape). This does not work well with # ParametricPulse.get_waveform(). # # See: https://github.com/sympy/sympy/issues/5642 envelope_expr = _amp * sym.Piecewise((1, sym.And(_t >= 0, _t <= _duration)), (0, True)) valid_amp_conditions_expr = sym.Abs(_amp) <= 1.0 super().__init__( pulse_type=self.__class__.__name__, duration=duration, parameters=parameters, name=name, limit_amplitude=limit_amplitude, envelope=envelope_expr, valid_amp_conditions=valid_amp_conditions_expr, ) self.validate_parameters()
def test_custom_pulse(self): """Test defining a custom pulse which is not in the form of amp * F(t).""" t, t1, t2, amp1, amp2 = sym.symbols("t, t1, t2, amp1, amp2") envelope = sym.Piecewise((amp1, sym.And(t > t1, t < t2)), (amp2, sym.true)) custom_pulse = SymbolicPulse( pulse_type="Custom", duration=100, parameters={ "t1": 30, "t2": 80, "amp1": 0.1j, "amp2": -0.1 }, envelope=envelope, ) waveform = custom_pulse.get_waveform() reference = np.concatenate( [-0.1 * np.ones(30), 0.1j * np.ones(50), -0.1 * np.ones(20)]) np.testing.assert_array_almost_equal(waveform.samples, reference)
def fmod(a, b): s = sign(a) a = Abs(a) b = Abs(b) f1 = a - (b * floor(a / b)) return s * se.Piecewise([0, Abs(a - b) < SMALL_NUMBER], [f1, True])
def __init__( self, duration: Union[int, ParameterExpression], amp: Union[complex, ParameterExpression], sigma: Union[float, ParameterExpression], width: Optional[Union[float, ParameterExpression]] = None, risefall_sigma_ratio: Optional[Union[float, ParameterExpression]] = None, name: Optional[str] = None, limit_amplitude: Optional[bool] = None, ): """Create new pulse instance. Args: duration: Pulse length in terms of the sampling period `dt`. amp: The amplitude of the Gaussian and of the square pulse. sigma: A measure of how wide or narrow the Gaussian risefall is; see the class docstring for more details. width: The duration of the embedded square pulse. risefall_sigma_ratio: The ratio of each risefall duration to sigma. name: Display name for this pulse envelope. limit_amplitude: If ``True``, then limit the amplitude of the waveform to 1. The default is ``True`` and the amplitude is constrained to 1. Raises: PulseError: When width and risefall_sigma_ratio are both empty or both non-empty. """ # Convert risefall_sigma_ratio into width which is defined in OpenPulse spec if width is None and risefall_sigma_ratio is None: raise PulseError( "Either the pulse width or the risefall_sigma_ratio parameter must be specified." ) if width is not None and risefall_sigma_ratio is not None: raise PulseError( "Either the pulse width or the risefall_sigma_ratio parameter can be specified" " but not both." ) if width is None and risefall_sigma_ratio is not None: width = duration - 2.0 * risefall_sigma_ratio * sigma parameters = {"amp": amp, "sigma": sigma, "width": width} # Prepare symbolic expressions _t, _duration, _amp, _sigma, _width = sym.symbols("t, duration, amp, sigma, width") _center = _duration / 2 _sq_t0 = _center - _width / 2 _sq_t1 = _center + _width / 2 _gaussian_ledge = _lifted_gaussian(_t, _sq_t0, -1, _sigma) _gaussian_redge = _lifted_gaussian(_t, _sq_t1, _duration + 1, _sigma) envelope_expr = _amp * sym.Piecewise( (_gaussian_ledge, _t <= _sq_t0), (_gaussian_redge, _t >= _sq_t1), (1, True) ) consts_expr = sym.And(_sigma > 0, _width >= 0, _duration >= _width) valid_amp_conditions_expr = sym.Abs(_amp) <= 1.0 super().__init__( pulse_type=self.__class__.__name__, duration=duration, parameters=parameters, name=name, limit_amplitude=limit_amplitude, envelope=envelope_expr, constraints=consts_expr, valid_amp_conditions=valid_amp_conditions_expr, ) self.validate_parameters()