Repository: Qiskit/qiskit

Issue:
Gaussian square parametric pulse documentation
<img width="561" alt="Screen Shot 2020-10-12 at 2 17 46 PM" src="https://user-images.githubusercontent.com/31739486/95913668-75878c00-0d59-11eb-84c9-070fa7108e88.png">


### What is the current behavior?

The formula above does not seem to implement a Gaussian square pulse



### Steps to reproduce the problem
After logging into the IBM Q experience account and selecting the 'ibmq_rome' backend, run (fixing indents):

```
backend_defaults = backend.defaults()
inst_map = backend_defaults.instruction_schedule_map
cx_default = inst_map.get('cx', qubits=(0,1))
CR_max_amp = np.max(cx_default.instructions[5][1].pulse.get_waveform().samples)
cx_duration = cx_default.instructions[5][1].pulse.duration
cx_risefall = int((cx_default.instructions[5][1].pulse.duration - cx_default.instructions[5][1].pulse.width)/2)
cx_sigma = cx_default.instructions[5][1].pulse.sigma
CR = []
for t in range(cx_duration):
    if t < cx_risefall:
        CR.append( CR_max_amp*np.exp(-0.5*((t-cx_risefall/2)**2)/(cx_sigma**2)) )
    elif t >= cx_risefall and t < cx_duration - cx_risefall:
        CR.append(CR_max_amp)
    elif t >= cx_duration - cx_risefall and t < cx_duration:
       CR.append( CR_max_amp*np.exp(-0.5*((t-(pulse_duration-cx_risefall)/2)**2)/cx_sigma**2) )
plt.plot(CR, label = 'from formula')
plt.plot(cx_default.instructions[5][1].pulse.get_waveform().samples, label = 'from backend')
plt.legend()
plt.show()
```

![gauss_square](https://user-images.githubusercontent.com/31739486/95914036-00688680-0d5a-11eb-866f-a57597f313be.png)


### What is the expected behavior?

Bugs in the code above aside, the two curves in the plot above should be identical. But they are not. Is the formula different?

### Suggested solutions

Report the correct formula for the Gaussian square pulse, the way it's created in the backend





Discussion:
# Update for Santa:elf:- QGoGP
✅ Issue reproducible and has interesting results on different backends! The formula indeed needs to be rechecked! Since `ibmq_rome` is retired, the next closest cousins I could find was `ibmq_manila`. All tests done taking `ibmq_manila` as reference

Updated fixed code with imports -
```
import matplotlib.pyplot as plt 
from qiskit import QuantumCircuit, transpile, Aer, IBMQ
from qiskit import pulse
# Loading your IBM Quantum account(s)
provider = IBMQ.load_account()

backend = provider.get_backend('ibmq_manila')
backend_defaults = backend.defaults()
inst_map = backend_defaults.instruction_schedule_map
cx_default = inst_map.get('cx', qubits=(0,1))
CR_max_amp = np.max(cx_default.instructions[5][1].pulse.get_waveform().samples)
cx_duration = cx_default.instructions[5][1].pulse.duration
cx_risefall = int((cx_default.instructions[5][1].pulse.duration - cx_default.instructions[5][1].pulse.width)/2)
cx_sigma = cx_default.instructions[5][1].pulse.sigma
CR = []
for t in range(cx_duration):
    if t < cx_risefall:
        CR.append( CR_max_amp*np.exp(-0.5*((t-cx_risefall/2)**2)/(cx_sigma**2)) )
    elif t >= cx_risefall and t < cx_duration - cx_risefall:
        CR.append(CR_max_amp)
    elif t >= cx_duration - cx_risefall and t < cx_duration:
       CR.append( CR_max_amp*np.exp(-0.5*((t-(cx_duration-cx_risefall)/2)**2)/cx_sigma**2) )

plt.plot(CR, label = 'from formula')
plt.plot(cx_default.instructions[5][1].pulse.get_waveform().samples, label = 'from backend')
plt.legend()
plt.show()
```
#### Plot achieved on `ibmq_manila` - 

![5226](https://user-images.githubusercontent.com/61380731/146238649-9bc059bb-2424-449b-ad01-b7eed9e70ed8.png)


Will update comment/add more comments if cause is found/fixed!

> Python version 3.9.7
> qiskit-terra version: 0.19.1




I am able to recreate the waveform with 
```python
import numpy as np
import matplotlib.pyplot as plt 
from qiskit import QuantumCircuit, transpile, Aer, IBMQ
from qiskit import pulse
# Loading your IBM Quantum account(s)
provider = IBMQ.load_account()

backend = provider.get_backend('ibmq_manila')
backend_defaults = backend.defaults()
inst_map = backend_defaults.instruction_schedule_map

cx_default = inst_map.get('cx', qubits=(0,1))
CR_max_amp = cx_default.instructions[5][1].pulse.amp
cx_duration = cx_default.instructions[5][1].pulse.duration
cx_risefall = int((cx_default.instructions[5][1].pulse.duration - cx_default.instructions[5][1].pulse.width)/2)
cx_sigma = cx_default.instructions[5][1].pulse.sigma

def old_formula(max_amp, duration, risefall, sigma):
    CR = []
    for t in range(cx_duration):
        if t < cx_risefall:
            CR.append( CR_max_amp*np.exp(-0.5*((t-cx_risefall/2)**2)/(cx_sigma**2)) )
        elif t >= cx_risefall and t < cx_duration - cx_risefall:
            CR.append(CR_max_amp)
        elif t >= cx_duration - cx_risefall and t < cx_duration:
           CR.append( CR_max_amp*np.exp(-0.5*((t-(cx_duration-cx_risefall)/2)**2)/cx_sigma**2) )
    return CR

def old_formula(max_amp, duration, risefall, sigma):
    CR = []
    for t in range(cx_duration):
        if t < cx_risefall:
            CR.append( CR_max_amp*np.exp(-0.5*((t-cx_risefall/2)**2)/(cx_sigma**2)) )
        elif t >= cx_risefall and t < cx_duration - cx_risefall:
            CR.append(CR_max_amp)
        elif t >= cx_duration - cx_risefall and t < cx_duration:
           CR.append( CR_max_amp*np.exp(-0.5*((t-(cx_duration-cx_risefall)/2)**2)/cx_sigma**2) )
    return CR

def gaussian_square_function(times, duration, risefall, sigma, lifted=False):
    CR = []
    for t in times:
        if t < cx_risefall:
            CR.append( np.exp(-0.5*((t-cx_risefall)**2)/(cx_sigma**2)) )
        elif t >= cx_risefall and t < cx_duration - cx_risefall:
            CR.append(1)
        elif t >= cx_duration - cx_risefall and t < cx_duration:
           CR.append( np.exp(-0.5*((t-(cx_duration-cx_risefall))**2)/cx_sigma**2) )
    return np.asarray(CR)

def gaussian_square(max_amp, duration, risefall, sigma, lifted=True):
    times = np.arange(0, cx_duration)
    CR = gaussian_square_function(times, max_amp, duration, risefall, sigma)
    if lifted:
        lift_val = gaussian_square_function([-1.0], duration, risefall, sigma)
        CR = (CR - lift_val) / (1-lift_val)
    CR = CR*max_amp
    return CR

plt.plot(old_formula(CR_max_amp, cx_duration, cx_risefall, cx_sigma), label = 'from formula')
plt.plot(gaussian_square(CR_max_amp, cx_duration, cx_risefall, cx_sigma), label = 'from new formula')
plt.plot(cx_default.instructions[5][1].pulse.get_waveform().samples, label = 'from backend')
plt.legend()
plt.show()
```

![image](https://user-images.githubusercontent.com/1553846/148115491-0035c4a0-333e-4470-bc89-e3c2bc57660a.png)

There are two problems with the current documentation.
1. There is a bug in `risefall/2` -> `risefall`, `(risefall+width)/2` -> `(risefall+width)`.
2. The `GaussianSquare` is really a lifted gaussian square function which removes initial pulse discontinuities and so should be updated as. `LiftedGaussianSquare(t) = (GaussianSquare(t)-GaussianSquare(1.0))/(1-GaussianSquare(-1.0))` where `GaussianSquare` is the corrected definition above. Perhaps this should have been named `LiftedGaussianSquare` to begin with, but it is a backwards compatibility issue now.

Please let me know if you wish to make the documentation updates @mrvee-qC, otherwise, I can take this on.
Please go ahead with the updates @taalexander! :) 
On inspecting the latest docs, problem 1. has already been corrected.

![Screen Shot 2022-01-04 at 5 44 31 PM](https://user-images.githubusercontent.com/1553846/148127876-48b69eaa-1812-4d30-be42-7255a4056f92.png)

I will add a note about the pulse being lifted.

Resolving patch:
diff --git a/qiskit/pulse/library/parametric_pulses.py b/qiskit/pulse/library/parametric_pulses.py
--- a/qiskit/pulse/library/parametric_pulses.py
+++ b/qiskit/pulse/library/parametric_pulses.py
@@ -101,12 +101,18 @@ def __hash__(self) -> int:
 
 
 class Gaussian(ParametricPulse):
-    """A truncated pulse envelope shaped according to the Gaussian function whose mean is centered
-    at the center of the pulse (duration / 2):
+    r"""A lifted and truncated pulse envelope shaped according to the Gaussian function whose
+    mean is centered at the center of the pulse (duration / 2):
 
     .. math::
 
-        f(x) = amp * exp( -(1/2) * (x - duration/2)^2 / sigma^2 )  ,  0 <= x < duration
+        f'(x) &= \exp\Bigl( -\frac12 \frac{{(x - \text{duration}/2)}^2}{\text{sigma}^2} \Bigr)\\
+        f(x) &= \text{amp} \times \frac{f'(x) - f'(-1)}{1-f'(-1)}, \quad 0 \le x < \text{duration}
+
+    where :math:`f'(x)` is the gaussian waveform without lifting or amplitude scaling.
+
+    This pulse would be more accurately named as ``LiftedGaussian``, however, for historical
+    and practical DSP reasons it has the name ``Gaussian``.
     """
 
     def __init__(
@@ -172,36 +178,45 @@ def __repr__(self) -> str:
 
 
 class GaussianSquare(ParametricPulse):
-    """A square pulse with a Gaussian shaped risefall on both sides. Either risefall_sigma_ratio
-     or width parameter has to be specified.
-
-    If risefall_sigma_ratio is not None and width is None:
-
-    :math:`risefall = risefall` _ :math:`to` _ :math:`sigma * sigma`
-
-    :math:`width = duration - 2 * risefall`
-
-    If width is not None and risefall_sigma_ratio is None:
+    # Not a raw string because we need to be able to split lines.
+    """A square pulse with a Gaussian shaped risefall on both sides lifted such that
+    its first sample is zero.
 
-    .. math::
-
-        risefall = (duration - width) / 2
+    Either the ``risefall_sigma_ratio`` or ``width`` parameter has to be specified.
 
-    In both cases, the pulse is defined as:
+    If ``risefall_sigma_ratio`` is not None and ``width`` is None:
 
     .. math::
 
-        0 <= x < risefall
+        \\text{risefall} &= \\text{risefall_sigma_ratio} \\times \\text{sigma}\\\\
+        \\text{width} &= \\text{duration} - 2 \\times \\text{risefall}
 
-        f(x) = amp * exp( -(1/2) * (x - risefall)^2 / sigma^2 )
+    If ``width`` is not None and ``risefall_sigma_ratio`` is None:
 
-        risefall <= x < risefall + width
+    .. math:: \\text{risefall} = \\frac{\\text{duration} - \\text{width}}{2}
 
-        f(x) = amp
+    In both cases, the lifted gaussian square pulse :math:`f'(x)` is defined as:
 
-        risefall + width <= x < duration
+    .. math::
 
-        f(x) = amp * exp( -(1/2) * (x - (risefall + width))^2 / sigma^2 )
+        f'(x) &= \\begin{cases}\
+            \\exp\\biggl(-\\frac12 \\frac{(x - \\text{risefall})^2}{\\text{sigma}^2}\\biggr)\
+                & x < \\text{risefall}\\\\
+            1\
+                & \\text{risefall} \\le x < \\text{risefall} + \\text{width}\\\\
+            \\exp\\biggl(-\\frac12\
+                    \\frac{{\\bigl(x - (\\text{risefall} + \\text{width})\\bigr)}^2}\
+                          {\\text{sigma}^2}\
+                    \\biggr)\
+                & \\text{risefall} + \\text{width} \\le x\
+        \\end{cases}\\\\
+        f(x) &= \\text{amp} \\times \\frac{f'(x) - f'(-1)}{1-f'(-1)},\
+            \\quad 0 \\le x < \\text{duration}
+
+    where :math:`f'(x)` is the gaussian square waveform without lifting or amplitude scaling.
+
+    This pulse would be more accurately named as ``LiftedGaussianSquare``, however, for historical
+    and practical DSP reasons it has the name ``GaussianSquare``.
     """
 
     def __init__(
@@ -323,21 +338,26 @@ def __repr__(self) -> str:
 
 
 class Drag(ParametricPulse):
-    r"""The Derivative Removal by Adiabatic Gate (DRAG) pulse is a standard Gaussian pulse
-    with an additional Gaussian derivative component. It is designed to reduce the frequency
-    spectrum of a normal gaussian pulse near the :math:`|1\rangle` - :math:`|2\rangle` transition,
-    reducing the chance of leakage to the :math:`|2\rangle` state.
+    """The Derivative Removal by Adiabatic Gate (DRAG) pulse is a standard Gaussian pulse
+    with an additional Gaussian derivative component and lifting applied.
 
-    .. math::
+    It is designed to reduce the frequency spectrum of a normal gaussian pulse near
+    the :math:`|1\\rangle\\leftrightarrow|2\\rangle` transition,
+    reducing the chance of leakage to the :math:`|2\\rangle` state.
 
-        f(x) = Gaussian + 1j * beta * d/dx [Gaussian]
-             = Gaussian + 1j * beta * (-(x - duration/2) / sigma^2) [Gaussian]
+    .. math::
 
-    where 'Gaussian' is:
+        g(x) &= \\exp\\Bigl(-\\frac12 \\frac{(x - \\text{duration}/2)^2}{\\text{sigma}^2}\\Bigr)\\\\
+        f'(x) &= g(x) + 1j \\times \\text{beta} \\times \\frac{\\mathrm d}{\\mathrm{d}x} g(x)\\\\
+              &= g(x) + 1j \\times \\text{beta} \\times\
+                    \\Bigl(-\\frac{x - \\text{duration}/2}{\\text{sigma}^2}\\Bigr)g(x)\\\\
+        f(x) &= \\text{amp}\\times\\frac{f'(x)-f'(-1)}{1-f'(-1)}, \\quad 0 \\le x < \\text{duration}
 
-    .. math::
+    where :math:`g(x)` is a standard unlifted gaussian waveform and
+    :math:`f'(x)` is the DRAG waveform without lifting or amplitude scaling.
 
-        Gaussian(x, amp, sigma) = amp * exp( -(1/2) * (x - duration/2)^2 / sigma^2 )
+    This pulse would be more accurately named as ``LiftedDrag``, however, for historical
+    and practical DSP reasons it has the name ``Drag``.
 
     References:
         1. |citation1|_


Test patch: