Repository: Qiskit/qiskit

Issue:
problems with `zeroed_width` argument in `pulse.continuous`
There seems to be some issues with initial/final discontinuity in some pulses in the pulse library.

```python
g_disc = discrete.gaussian(duration=240, amp=0.1, sigma=80)
g_disc.draw(scaling=0.2)
```
![image](https://user-images.githubusercontent.com/8622381/67459360-68ccce00-f606-11e9-8553-88ef3ba16514.png)

```python
g_disc = discrete.gaussian_square(duration=240, amp=0.1, sigma=15, risefall=20)
g_disc.draw(scaling=0.2)
```
![image](https://user-images.githubusercontent.com/8622381/67464065-a8001c80-f610-11e9-941d-14307a4e39f6.png)




Discussion:

Resolving patch:
diff --git a/qiskit/pulse/pulse_lib/continuous.py b/qiskit/pulse/pulse_lib/continuous.py
--- a/qiskit/pulse/pulse_lib/continuous.py
+++ b/qiskit/pulse/pulse_lib/continuous.py
@@ -20,6 +20,7 @@
 from typing import Union, Tuple, Optional
 
 import numpy as np
+from qiskit.pulse import PulseError
 
 
 def constant(times: np.ndarray, amp: complex) -> np.ndarray:
@@ -108,23 +109,22 @@ def _fix_gaussian_width(gaussian_samples, amp: float, center: float, sigma: floa
                         ret_scale_factor: bool = False) -> np.ndarray:
     r"""Enforce that the supplied gaussian pulse is zeroed at a specific width.
 
-    This is achieved by subtracting $\Omega_g(center \pm zeroed_width/2)$ from all samples.
+    This is achieved by subtracting $\Omega_g(center \pm zeroed_width)$ from all samples.
 
-    amp: Pulse amplitude at `2\times center+1`.
+    amp: Pulse amplitude at `center`.
     center: Center (mean) of pulse.
-    sigma: Width (standard deviation) of pulse.
-    zeroed_width: Subtract baseline to gaussian pulses to make sure
-             $\Omega_g(center \pm zeroed_width/2)=0$ is satisfied. This is used to avoid
-             large discontinuities at the start of a gaussian pulse. If unsupplied,
-             defaults to $2*(center+1)$ such that the samples are zero at $\Omega_g(-1)$.
-    rescale_amp: If `zeroed_width` is not `None` and `rescale_amp=True` the pulse will
-                 be rescaled so that $\Omega_g(center)-\Omega_g(center\pm zeroed_width/2)=amp$.
+    sigma: Standard deviation of pulse.
+    zeroed_width: Subtract baseline from gaussian pulses to make sure
+        $\Omega_g(center \pm zeroed_width/2)=0$ is satisfied. This is used to avoid
+        large discontinuities at the start of a gaussian pulse. If unsupplied,
+        defaults to $2*center$ such that $\Omega_g(0)=0$ and $\Omega_g(2*center)=0$.
+    rescale_amp: If True the pulse will be rescaled so that $\Omega_g(center)=amp$.
     ret_scale_factor: Return amplitude scale factor.
     """
     if zeroed_width is None:
-        zeroed_width = 2*(center+1)
+        zeroed_width = 2*center
 
-    zero_offset = gaussian(np.array([-zeroed_width/2]), amp, center, sigma)
+    zero_offset = gaussian(np.array([zeroed_width/2]), amp, 0, sigma)
     gaussian_samples -= zero_offset
     amp_scale_factor = 1.
     if rescale_amp:
@@ -146,16 +146,16 @@ def gaussian(times: np.ndarray, amp: complex, center: float, sigma: float,
     Args:
         times: Times to output pulse for.
         amp: Pulse amplitude at `center`. If `zeroed_width` is set pulse amplitude at center
-            will be $amp-\Omega_g(center\pm zeroed_width/2)$ unless `rescale_amp` is set,
+            will be $amp-\Omega_g(center \pm zeroed_width/2)$ unless `rescale_amp` is set,
             in which case all samples will be rescaled such that the center
             amplitude will be `amp`.
         center: Center (mean) of pulse.
         sigma: Width (standard deviation) of pulse.
-        zeroed_width: Subtract baseline to gaussian pulses to make sure
-                 $\Omega_g(center \pm zeroed_width/2)=0$ is satisfied. This is used to avoid
-                 large discontinuities at the start of a gaussian pulse.
+        zeroed_width: Subtract baseline from gaussian pulses to make sure
+            $\Omega_g(center \pm zeroed_width/2)=0$ is satisfied. This is used to avoid
+            large discontinuities at the start of a gaussian pulse.
         rescale_amp: If `zeroed_width` is not `None` and `rescale_amp=True` the pulse will
-                     be rescaled so that $\Omega_g(center)-\Omega_g(center\pm zeroed_width/2)=amp$.
+            be rescaled so that $\Omega_g(center)=amp$.
         ret_x: Return centered and standard deviation normalized pulse location.
                $x=(times-center)/sigma.
     """
@@ -190,12 +190,45 @@ def gaussian_deriv(times: np.ndarray, amp: complex, center: float, sigma: float,
     return gauss_deriv
 
 
+def _fix_sech_width(sech_samples, amp: float, center: float, sigma: float,
+                    zeroed_width: Optional[float] = None, rescale_amp: bool = False,
+                    ret_scale_factor: bool = False) -> np.ndarray:
+    r"""Enforce that the supplied sech pulse is zeroed at a specific width.
+
+    This is achieved by subtracting $\Omega_g(center \pm zeroed_width)$ from all samples.
+
+    amp: Pulse amplitude at `center`.
+    center: Center (mean) of pulse.
+    sigma: Standard deviation of pulse.
+    zeroed_width: Subtract baseline from sech pulses to make sure
+        $\Omega_g(center \pm zeroed_width/2)=0$ is satisfied. This is used to avoid
+        large discontinuities at the start of a sech pulse. If unsupplied,
+        defaults to $2*center$ such that $\Omega_g(0)=0$ and $\Omega_g(2*center)=0$.
+    rescale_amp: If True the pulse will be rescaled so that $\Omega_g(center)=amp$.
+    ret_scale_factor: Return amplitude scale factor.
+    """
+    if zeroed_width is None:
+        zeroed_width = 2*center
+
+    zero_offset = sech(np.array([zeroed_width/2]), amp, 0, sigma)
+    sech_samples -= zero_offset
+    amp_scale_factor = 1.
+    if rescale_amp:
+        amp_scale_factor = amp/(amp-zero_offset) if amp-zero_offset != 0 else 1.
+        sech_samples *= amp_scale_factor
+
+    if ret_scale_factor:
+        return sech_samples, amp_scale_factor
+    return sech_samples
+
+
 def sech_fn(x, *args, **kwargs):
     r"""Hyperbolic secant function"""
     return 1.0 / np.cosh(x, *args, **kwargs)
 
 
 def sech(times: np.ndarray, amp: complex, center: float, sigma: float,
+         zeroed_width: Optional[float] = None, rescale_amp: bool = False,
          ret_x: bool = False) -> Union[np.ndarray, Tuple[np.ndarray, np.ndarray]]:
     r"""Continuous unnormalized sech pulse.
 
@@ -204,13 +237,22 @@ def sech(times: np.ndarray, amp: complex, center: float, sigma: float,
         amp: Pulse amplitude at `center`.
         center: Center (mean) of pulse.
         sigma: Width (standard deviation) of pulse.
+        zeroed_width: Subtract baseline from pulse to make sure
+            $\Omega_g(center \pm zeroed_width/2)=0$ is satisfied. This is used to avoid
+            large discontinuities at the start and end of the pulse.
+        rescale_amp: If `zeroed_width` is not `None` and `rescale_amp=True` the pulse will
+            be rescaled so that $\Omega_g(center)=amp$.
         ret_x: Return centered and standard deviation normalized pulse location.
-               $x=(times-center)/sigma.
+            $x=(times-center)/sigma$.
     """
     times = np.asarray(times, dtype=np.complex_)
     x = (times-center)/sigma
     sech_out = amp*sech_fn(x).astype(np.complex_)
 
+    if zeroed_width is not None:
+        sech_out = _fix_sech_width(sech_out, amp=amp, center=center, sigma=sigma,
+                                   zeroed_width=zeroed_width, rescale_amp=rescale_amp)
+
     if ret_x:
         return sech_out, x
     return sech_out
@@ -234,7 +276,7 @@ def sech_deriv(times: np.ndarray, amp: complex, center: float, sigma: float,
     return sech_out_deriv
 
 
-def gaussian_square(times: np.ndarray, amp: complex, center: float, width: float,
+def gaussian_square(times: np.ndarray, amp: complex, center: float, square_width: float,
                     sigma: float, zeroed_width: Optional[float] = None) -> np.ndarray:
     r"""Continuous gaussian square pulse.
 
@@ -242,23 +284,27 @@ def gaussian_square(times: np.ndarray, amp: complex, center: float, width: float
         times: Times to output pulse for.
         amp: Pulse amplitude.
         center: Center of the square pulse component.
-        width: Width of the square pulse component.
-        sigma: Width (standard deviation) of gaussian rise/fall portion of the pulse.
+        square_width: Width of the square pulse component.
+        sigma: Standard deviation of Gaussian rise/fall portion of the pulse.
         zeroed_width: Subtract baseline of gaussian square pulse
-                      to enforce $\OmegaSquare(center \pm zeroed_width/2)=0$.
+            to enforce $\OmegaSquare(center \pm zeroed_width/2)=0$.
+
+    Raises:
+        PulseError: if zeroed_width is not compatible with square_width.
     """
-    square_start = center-width/2
-    square_stop = center+width/2
+    square_start = center-square_width/2
+    square_stop = center+square_width/2
     if zeroed_width:
-        zeroed_width = min(width, zeroed_width)
-        gauss_zeroed_width = zeroed_width-width
+        if zeroed_width < square_width:
+            raise PulseError("zeroed_width cannot be smaller than square_width.")
+        gaussian_zeroed_width = zeroed_width-square_width
     else:
-        gauss_zeroed_width = None
+        gaussian_zeroed_width = None
 
     funclist = [functools.partial(gaussian, amp=amp, center=square_start, sigma=sigma,
-                                  zeroed_width=gauss_zeroed_width, rescale_amp=True),
+                                  zeroed_width=gaussian_zeroed_width, rescale_amp=True),
                 functools.partial(gaussian, amp=amp, center=square_stop, sigma=sigma,
-                                  zeroed_width=gauss_zeroed_width, rescale_amp=True),
+                                  zeroed_width=gaussian_zeroed_width, rescale_amp=True),
                 functools.partial(constant, amp=amp)]
     condlist = [times <= square_start, times >= square_stop]
     return np.piecewise(times.astype(np.complex_), condlist, funclist)
@@ -281,11 +327,11 @@ def drag(times: np.ndarray, amp: complex, center: float, sigma: float, beta: flo
         beta: Y correction amplitude. For the SNO this is $\beta=-\frac{\lambda_1^2}{4\Delta_2}$.
             Where $\lambds_1$ is the relative coupling strength between the first excited and second
             excited states and $\Delta_2$ is the detuning between the respective excited states.
-        zeroed_width: Subtract baseline to gaussian pulses to make sure
-                 $\Omega_g(center \pm zeroed_width/2)=0$ is satisfied. This is used to avoid
-                 large discontinuities at the start of a gaussian pulse.
+        zeroed_width: Subtract baseline of gaussian pulse to make sure
+            $\Omega_g(center \pm zeroed_width/2)=0$ is satisfied. This is used to avoid
+            large discontinuities at the start of a gaussian pulse.
         rescale_amp: If `zeroed_width` is not `None` and `rescale_amp=True` the pulse will
-                     be rescaled so that $\Omega_g(center)-\Omega_g(center\pm zeroed_width/2)=amp$.
+            be rescaled so that $\Omega_g(center)=amp$.
 
     """
     gauss_deriv, gauss = gaussian_deriv(times, amp=amp, center=center, sigma=sigma,
diff --git a/qiskit/pulse/pulse_lib/discrete.py b/qiskit/pulse/pulse_lib/discrete.py
--- a/qiskit/pulse/pulse_lib/discrete.py
+++ b/qiskit/pulse/pulse_lib/discrete.py
@@ -16,7 +16,7 @@
 
 """Module for builtin discrete pulses.
 
-Note the sampling strategy use for all discrete pulses is `left`.
+Note the sampling strategy use for all discrete pulses is `midpoint`.
 """
 from typing import Optional
 
@@ -26,13 +26,13 @@
 from . import samplers
 
 
-_sampled_constant_pulse = samplers.left(continuous.constant)
+_sampled_constant_pulse = samplers.midpoint(continuous.constant)
 
 
 def constant(duration: int, amp: complex, name: Optional[str] = None) -> SamplePulse:
     """Generates constant-sampled `SamplePulse`.
 
-    Applies `left` sampling strategy to generate discrete pulse from continuous function.
+    Applies `midpoint` sampling strategy to generate discrete pulse from continuous function.
 
     Args:
         duration: Duration of pulse. Must be greater than zero.
@@ -42,7 +42,7 @@ def constant(duration: int, amp: complex, name: Optional[str] = None) -> SampleP
     return _sampled_constant_pulse(duration, amp, name=name)
 
 
-_sampled_zero_pulse = samplers.left(continuous.zero)
+_sampled_zero_pulse = samplers.midpoint(continuous.zero)
 
 
 def zero(duration: int, name: Optional[str] = None) -> SamplePulse:
@@ -55,14 +55,14 @@ def zero(duration: int, name: Optional[str] = None) -> SamplePulse:
     return _sampled_zero_pulse(duration, name=name)
 
 
-_sampled_square_pulse = samplers.left(continuous.square)
+_sampled_square_pulse = samplers.midpoint(continuous.square)
 
 
 def square(duration: int, amp: complex, period: float = None,
            phase: float = 0, name: Optional[str] = None) -> SamplePulse:
     """Generates square wave `SamplePulse`.
 
-    Applies `left` sampling strategy to generate discrete pulse from continuous function.
+    Applies `midpoint` sampling strategy to generate discrete pulse from continuous function.
 
     Args:
         duration: Duration of pulse. Must be greater than zero.
@@ -77,7 +77,7 @@ def square(duration: int, amp: complex, period: float = None,
     return _sampled_square_pulse(duration, amp, period, phase=phase, name=name)
 
 
-_sampled_sawtooth_pulse = samplers.left(continuous.sawtooth)
+_sampled_sawtooth_pulse = samplers.midpoint(continuous.sawtooth)
 
 
 def sawtooth(duration: int, amp: complex, period: float = None,
@@ -97,14 +97,14 @@ def sawtooth(duration: int, amp: complex, period: float = None,
     return _sampled_sawtooth_pulse(duration, amp, period, phase=phase, name=name)
 
 
-_sampled_triangle_pulse = samplers.left(continuous.triangle)
+_sampled_triangle_pulse = samplers.midpoint(continuous.triangle)
 
 
 def triangle(duration: int, amp: complex, period: float = None,
              phase: float = 0, name: Optional[str] = None) -> SamplePulse:
     """Generates triangle wave `SamplePulse`.
 
-    Applies `left` sampling strategy to generate discrete pulse from continuous function.
+    Applies `midpoint` sampling strategy to generate discrete pulse from continuous function.
 
     Args:
         duration: Duration of pulse. Must be greater than zero.
@@ -119,14 +119,14 @@ def triangle(duration: int, amp: complex, period: float = None,
     return _sampled_triangle_pulse(duration, amp, period, phase=phase, name=name)
 
 
-_sampled_cos_pulse = samplers.left(continuous.cos)
+_sampled_cos_pulse = samplers.midpoint(continuous.cos)
 
 
 def cos(duration: int, amp: complex, freq: float = None,
         phase: float = 0, name: Optional[str] = None) -> SamplePulse:
     """Generates cosine wave `SamplePulse`.
 
-    Applies `left` sampling strategy to generate discrete pulse from continuous function.
+    Applies `midpoint` sampling strategy to generate discrete pulse from continuous function.
 
     Args:
         duration: Duration of pulse. Must be greater than zero.
@@ -141,7 +141,7 @@ def cos(duration: int, amp: complex, freq: float = None,
     return _sampled_cos_pulse(duration, amp, freq, phase=phase, name=name)
 
 
-_sampled_sin_pulse = samplers.left(continuous.sin)
+_sampled_sin_pulse = samplers.midpoint(continuous.sin)
 
 
 def sin(duration: int, amp: complex, freq: float = None,
@@ -161,15 +161,16 @@ def sin(duration: int, amp: complex, freq: float = None,
     return _sampled_sin_pulse(duration, amp, freq, phase=phase, name=name)
 
 
-_sampled_gaussian_pulse = samplers.left(continuous.gaussian)
+_sampled_gaussian_pulse = samplers.midpoint(continuous.gaussian)
 
 
 def gaussian(duration: int, amp: complex, sigma: float, name: Optional[str] = None) -> SamplePulse:
     r"""Generates unnormalized gaussian `SamplePulse`.
 
-    Centered at `duration/2` and zeroed at `t=-1` to prevent large initial discontinuity.
+    Centered at `duration/2` and zeroed at `t=0` and `t=duration` to prevent large
+    initial/final discontinuities.
 
-    Applies `left` sampling strategy to generate discrete pulse from continuous function.
+    Applies `midpoint` sampling strategy to generate discrete pulse from continuous function.
 
     Integrated area under curve is $\Omega_g(amp, sigma) = amp \times np.sqrt(2\pi \sigma^2)$
 
@@ -180,19 +181,19 @@ def gaussian(duration: int, amp: complex, sigma: float, name: Optional[str] = No
         name: Name of pulse.
     """
     center = duration/2
-    zeroed_width = duration + 2
+    zeroed_width = duration
     return _sampled_gaussian_pulse(duration, amp, center, sigma,
                                    zeroed_width=zeroed_width, rescale_amp=True, name=name)
 
 
-_sampled_gaussian_deriv_pulse = samplers.left(continuous.gaussian_deriv)
+_sampled_gaussian_deriv_pulse = samplers.midpoint(continuous.gaussian_deriv)
 
 
 def gaussian_deriv(duration: int, amp: complex, sigma: float,
                    name: Optional[str] = None) -> SamplePulse:
     r"""Generates unnormalized gaussian derivative `SamplePulse`.
 
-    Applies `left` sampling strategy to generate discrete pulse from continuous function.
+    Applies `midpoint` sampling strategy to generate discrete pulse from continuous function.
 
     Args:
         duration: Duration of pulse. Must be greater than zero.
@@ -204,15 +205,15 @@ def gaussian_deriv(duration: int, amp: complex, sigma: float,
     return _sampled_gaussian_deriv_pulse(duration, amp, center, sigma, name=name)
 
 
-_sampled_sech_pulse = samplers.left(continuous.sech)
+_sampled_sech_pulse = samplers.midpoint(continuous.sech)
 
 
 def sech(duration: int, amp: complex, sigma: float, name: str = None) -> SamplePulse:
     r"""Generates unnormalized sech `SamplePulse`.
 
-    Centered at `duration/2` and zeroed at `t=-1` to prevent large initial discontinuity.
+    Centered at `duration/2` and zeroed at `t=0` to prevent large initial discontinuity.
 
-    Applies `left` sampling strategy to generate discrete pulse from continuous function.
+    Applies `midpoint` sampling strategy to generate discrete pulse from continuous function.
 
     Args:
         duration: Duration of pulse. Must be greater than zero.
@@ -221,17 +222,18 @@ def sech(duration: int, amp: complex, sigma: float, name: str = None) -> SampleP
         name: Name of pulse.
     """
     center = duration/2
+    zeroed_width = duration
     return _sampled_sech_pulse(duration, amp, center, sigma,
-                               name=name)
+                               zeroed_width=zeroed_width, rescale_amp=True, name=name)
 
 
-_sampled_sech_deriv_pulse = samplers.left(continuous.sech_deriv)
+_sampled_sech_deriv_pulse = samplers.midpoint(continuous.sech_deriv)
 
 
 def sech_deriv(duration: int, amp: complex, sigma: float, name: str = None) -> SamplePulse:
     r"""Generates unnormalized sech derivative `SamplePulse`.
 
-    Applies `left` sampling strategy to generate discrete pulse from continuous function.
+    Applies `midpoint` sampling strategy to generate discrete pulse from continuous function.
 
     Args:
         duration: Duration of pulse. Must be greater than zero.
@@ -243,43 +245,43 @@ def sech_deriv(duration: int, amp: complex, sigma: float, name: str = None) -> S
     return _sampled_sech_deriv_pulse(duration, amp, center, sigma, name=name)
 
 
-_sampled_gaussian_square_pulse = samplers.left(continuous.gaussian_square)
+_sampled_gaussian_square_pulse = samplers.midpoint(continuous.gaussian_square)
 
 
 def gaussian_square(duration: int, amp: complex, sigma: float,
                     risefall: int, name: Optional[str] = None) -> SamplePulse:
     """Generates gaussian square `SamplePulse`.
 
-    Centered at `duration/2` and zeroed at `t=-1` and `t=duration+1` to prevent
+    Centered at `duration/2` and zeroed at `t=0` and `t=duration` to prevent
     large initial/final discontinuities.
 
-    Applies `left` sampling strategy to generate discrete pulse from continuous function.
+    Applies `midpoint` sampling strategy to generate discrete pulse from continuous function.
 
     Args:
         duration: Duration of pulse. Must be greater than zero.
         amp: Pulse amplitude.
-        sigma: Width (standard deviation) of gaussian rise/fall portion of the pulse.
+        sigma: Width (standard deviation) of Gaussian rise/fall portion of the pulse.
         risefall: Number of samples over which pulse rise and fall happen. Width of
             square portion of pulse will be `duration-2*risefall`.
         name: Name of pulse.
     """
     center = duration/2
-    width = duration-2*risefall
-    zeroed_width = duration + 2
-    return _sampled_gaussian_square_pulse(duration, amp, center, width, sigma,
+    square_width = duration-2*risefall
+    zeroed_width = duration
+    return _sampled_gaussian_square_pulse(duration, amp, center, square_width, sigma,
                                           zeroed_width=zeroed_width, name=name)
 
 
-_sampled_drag_pulse = samplers.left(continuous.drag)
+_sampled_drag_pulse = samplers.midpoint(continuous.drag)
 
 
 def drag(duration: int, amp: complex, sigma: float, beta: float,
          name: Optional[str] = None) -> SamplePulse:
     r"""Generates Y-only correction DRAG `SamplePulse` for standard nonlinear oscillator (SNO) [1].
 
-    Centered at `duration/2` and zeroed at `t=-1` to prevent large initial discontinuity.
+    Centered at `duration/2` and zeroed at `t=0` to prevent large initial discontinuity.
 
-    Applies `left` sampling strategy to generate discrete pulse from continuous function.
+    Applies `midpoint` sampling strategy to generate discrete pulse from continuous function.
 
     [1] Gambetta, J. M., Motzoi, F., Merkel, S. T. & Wilhelm, F. K.
         Analytic control methods for high-fidelity unitary operations


Test patch:
diff --git a/test/python/pulse/test_discrete_pulses.py b/test/python/pulse/test_discrete_pulses.py
--- a/test/python/pulse/test_discrete_pulses.py
+++ b/test/python/pulse/test_discrete_pulses.py
@@ -29,7 +29,7 @@ def test_constant(self):
         """Test discrete sampled constant pulse."""
         amp = 0.5j
         duration = 10
-        times = np.arange(0, duration)
+        times = np.arange(0, duration) + 0.5  # to match default midpoint sampling strategy
         constant_ref = continuous.constant(times, amp=amp)
         constant_pulse = pulse_lib.constant(duration, amp=amp)
         self.assertIsInstance(constant_pulse, SamplePulse)
@@ -38,7 +38,7 @@ def test_constant(self):
     def test_zero(self):
         """Test discrete sampled constant pulse."""
         duration = 10
-        times = np.arange(0, duration)
+        times = np.arange(0, duration) + 0.5
         zero_ref = continuous.zero(times)
         zero_pulse = pulse_lib.zero(duration)
         self.assertIsInstance(zero_pulse, SamplePulse)
@@ -49,7 +49,7 @@ def test_square(self):
         amp = 0.5
         period = 5
         duration = 10
-        times = np.arange(0, duration)
+        times = np.arange(0, duration) + 0.5
         square_ref = continuous.square(times, amp=amp, period=period)
         square_pulse = pulse_lib.square(duration, amp=amp, period=period)
         self.assertIsInstance(square_pulse, SamplePulse)
@@ -66,7 +66,7 @@ def test_sawtooth(self):
         amp = 0.5
         period = 5
         duration = 10
-        times = np.arange(0, duration)
+        times = np.arange(0, duration) + 0.5
         sawtooth_ref = continuous.sawtooth(times, amp=amp, period=period)
         sawtooth_pulse = pulse_lib.sawtooth(duration, amp=amp, period=period)
         self.assertIsInstance(sawtooth_pulse, SamplePulse)
@@ -83,7 +83,7 @@ def test_triangle(self):
         amp = 0.5
         period = 5
         duration = 10
-        times = np.arange(0, duration)
+        times = np.arange(0, duration) + 0.5
         triangle_ref = continuous.triangle(times, amp=amp, period=period)
         triangle_pulse = pulse_lib.triangle(duration, amp=amp, period=period)
         self.assertIsInstance(triangle_pulse, SamplePulse)
@@ -101,7 +101,7 @@ def test_cos(self):
         period = 5
         freq = 1/period
         duration = 10
-        times = np.arange(0, duration)
+        times = np.arange(0, duration) + 0.5
         cos_ref = continuous.cos(times, amp=amp, freq=freq)
         cos_pulse = pulse_lib.cos(duration, amp=amp, freq=freq)
         self.assertIsInstance(cos_pulse, SamplePulse)
@@ -119,7 +119,7 @@ def test_sin(self):
         period = 5
         freq = 1/period
         duration = 10
-        times = np.arange(0, duration)
+        times = np.arange(0, duration) + 0.5
         sin_ref = continuous.sin(times, amp=amp, freq=freq)
         sin_pulse = pulse_lib.sin(duration, amp=amp, freq=freq)
         self.assertIsInstance(sin_pulse, SamplePulse)
@@ -137,9 +137,9 @@ def test_gaussian(self):
         sigma = 2
         duration = 10
         center = duration/2
-        times = np.arange(0, duration)
+        times = np.arange(0, duration) + 0.5
         gaussian_ref = continuous.gaussian(times, amp, center, sigma,
-                                           zeroed_width=2*(center+1), rescale_amp=True)
+                                           zeroed_width=2*center, rescale_amp=True)
         gaussian_pulse = pulse_lib.gaussian(duration, amp, sigma)
         self.assertIsInstance(gaussian_pulse, SamplePulse)
         np.testing.assert_array_almost_equal(gaussian_pulse.samples, gaussian_ref)
@@ -150,7 +150,7 @@ def test_gaussian_deriv(self):
         sigma = 2
         duration = 10
         center = duration/2
-        times = np.arange(0, duration)
+        times = np.arange(0, duration) + 0.5
         gaussian_deriv_ref = continuous.gaussian_deriv(times, amp, center, sigma)
         gaussian_deriv_pulse = pulse_lib.gaussian_deriv(duration, amp, sigma)
         self.assertIsInstance(gaussian_deriv_pulse, SamplePulse)
@@ -162,8 +162,9 @@ def test_sech(self):
         sigma = 2
         duration = 10
         center = duration/2
-        times = np.arange(0, duration)
-        sech_ref = continuous.sech(times, amp, center, sigma)
+        times = np.arange(0, duration) + 0.5
+        sech_ref = continuous.sech(times, amp, center, sigma,
+                                   zeroed_width=2*center, rescale_amp=True)
         sech_pulse = pulse_lib.sech(duration, amp, sigma)
         self.assertIsInstance(sech_pulse, SamplePulse)
         np.testing.assert_array_almost_equal(sech_pulse.samples, sech_ref)
@@ -174,7 +175,7 @@ def test_sech_deriv(self):
         sigma = 2
         duration = 10
         center = duration/2
-        times = np.arange(0, duration)
+        times = np.arange(0, duration) + 0.5
         sech_deriv_ref = continuous.sech_deriv(times, amp, center, sigma)
         sech_deriv_pulse = pulse_lib.sech_deriv(duration, amp, sigma)
         self.assertIsInstance(sech_deriv_pulse, SamplePulse)
@@ -189,7 +190,7 @@ def test_gaussian_square(self):
         center = duration/2
         width = duration-2*risefall
         center = duration/2
-        times = np.arange(0, duration)
+        times = np.arange(0, duration) + 0.5
         gaussian_square_ref = continuous.gaussian_square(times, amp, center, width, sigma)
         gaussian_square_pulse = pulse_lib.gaussian_square(duration, amp, sigma, risefall)
         self.assertIsInstance(gaussian_square_pulse, SamplePulse)
@@ -202,7 +203,7 @@ def test_drag(self):
         beta = 0
         duration = 10
         center = 10/2
-        times = np.arange(0, duration)
+        times = np.arange(0, duration) + 0.5
         # reference drag pulse
         drag_ref = continuous.drag(times, amp, center, sigma, beta=beta,
                                    zeroed_width=2*(center+1), rescale_amp=True)