>*This is an interactive `jupyter` notebook version of the paper [arXiv:1906.08263](https://arxiv.org/abs/1906.08263). Run the notebook to make the plots interactive. Google colab version is available [here]( https://colab.research.google.com/drive/10jw5T07mhvdP0hp60HqVqNqWK49Cfzby).*

##### Pre-loading packages and codes

If you can see this sentence, click the black triangle on the left to collapse this section.

```python
!pip install lenstronomy
```

    Collecting lenstronomy
      Downloading https://files.pythonhosted.org/packages/b0/47/a50157a9b34135debb75655201887a42d71c3a6ee6fa8406eb4a9e59b50d/lenstronomy-0.8.2.tar.gz (5.8MB)
         |████████████████████████████████| 5.8MB 4.3MB/s
    Requirement already satisfied: numpy>=1.13 in /usr/local/lib/python3.6/dist-packages (from lenstronomy) (1.16.4)
    Requirement already satisfied: scipy>=0.14.0 in /usr/local/lib/python3.6/dist-packages (from lenstronomy) (1.3.0)
    Collecting configparser (from lenstronomy)
      Downloading https://files.pythonhosted.org/packages/ba/05/6c96328e92e625fc31445d24d75a2c92ef9ba34fc5b037fe69693c362a0d/configparser-3.7.4-py2.py3-none-any.whl
    Building wheels for collected packages: lenstronomy
      Building wheel for lenstronomy (setup.py) ... done
      Stored in directory: /root/.cache/pip/wheels/58/ba/a1/983a88d921a559e52db01095b532a4113c9e545226a879d4ab
    Successfully built lenstronomy
    Installing collected packages: configparser, lenstronomy
    Successfully installed configparser-3.7.4 lenstronomy-0.8.2

```python
!pip install mgefit
```

    Collecting mgefit
      Downloading https://files.pythonhosted.org/packages/d8/8c/73f1bc792b947457c26bf568f87b8d977654f4643585315b43ecb4e25f45/mgefit-5.0.12.tar.gz (11.5MB)
         |████████████████████████████████| 11.5MB 4.7MB/s
    Requirement already satisfied: numpy in /usr/local/lib/python3.6/dist-packages (from mgefit) (1.16.4)
    Requirement already satisfied: scipy in /usr/local/lib/python3.6/dist-packages (from mgefit) (1.3.0)
    Requirement already satisfied: matplotlib in /usr/local/lib/python3.6/dist-packages (from mgefit) (3.0.3)
    Requirement already satisfied: kiwisolver>=1.0.1 in /usr/local/lib/python3.6/dist-packages (from matplotlib->mgefit) (1.1.0)
    Requirement already satisfied: cycler>=0.10 in /usr/local/lib/python3.6/dist-packages (from matplotlib->mgefit) (0.10.0)
    Requirement already satisfied: python-dateutil>=2.1 in /usr/local/lib/python3.6/dist-packages (from matplotlib->mgefit) (2.5.3)
    Requirement already satisfied: pyparsing!=2.0.4,!=2.1.2,!=2.1.6,>=2.0.1 in /usr/local/lib/python3.6/dist-packages (from matplotlib->mgefit) (2.4.0)
    Requirement already satisfied: setuptools in /usr/local/lib/python3.6/dist-packages (from kiwisolver>=1.0.1->matplotlib->mgefit) (41.0.1)
    Requirement already satisfied: six in /usr/local/lib/python3.6/dist-packages (from cycler>=0.10->matplotlib->mgefit) (1.12.0)
    Building wheels for collected packages: mgefit
      Building wheel for mgefit (setup.py) ... done
      Stored in directory: /root/.cache/pip/wheels/b6/ad/08/a25b9fd4e4419d304787593dda44651238bec609ca340e1187
    Successfully built mgefit
    Installing collected packages: mgefit
    Successfully installed mgefit-5.0.12

```python
## plot settings

import matplotlib.pyplot as plt
import seaborn as sns

sns.set(style='ticks', context='paper', #font='Helvetica',
        font_scale=1.5)
sns.set_style({"xtick.direction": "in","ytick.direction": "in",
               "axes.linewidth": 1.5})

def set_font_scale(font_scale):
    sns.set(style='ticks', context='paper', font='Times New Roman',
            font_scale=font_scale)
    sns.set_style({"xtick.direction": "in","ytick.direction": "in",
                   "axes.linewidth": 1.5,})

# from cb2
emerald = '#66c2a5' #sns.xkcd_rgb['emerald']
orange = '#fc8d62' #sns.xkcd_rgb['bright orange']
blue = '#8da0cb' #sns.xkcd_rgb['light purple']
pink = '#e78ac3'

cmap = sns.cubehelix_palette(start=0.5, rot=-1.5, gamma=1, hue=1, light=-0.,
                             dark=0.8, reverse=False, as_cmap=True)
```

```python
import lenstronomy

lenstronomy.__version__
```

    '0.8.2'

```python
import numpy as np
from ipywidgets import interact
from ipywidgets import FloatSlider
from ipywidgets import IntSlider

from lenstronomy.LensModel.Profiles.nfw_ellipse import NFW_ELLIPSE
from lenstronomy.LensModel.Profiles.sersic_ellipse_potential import SersicEllipse
from lenstronomy.Util.param_util import phi_q2_ellipticity

import matplotlib.gridspec as gridspec
import matplotlib.patches

sersic = SersicEllipse()

n = 50
width = 2
x = np.linspace(-width, width, n)
y = np.linspace(-width, width, n)

X, Y = np.meshgrid(x, y)
Xf = X.flatten()
Yf = Y.flatten()

def plot_potential_to_density_contours(q, n_sersic):
    fig = plt.figure(figsize=(8, 6))
    gs1 = gridspec.GridSpec(1, 2)
    gs1.update(left=0.0, right=1., wspace=0.0, hspace=0.05)
    axes = []
    axes.append(fig.add_subplot(gs1[0, 0]))
    axes.append(fig.add_subplot(gs1[0, 1]))

    r_sersic = 1
    k_eff = 5
    pa = 0

    e1, e2 = phi_q2_ellipticity(pa, q)

    f = sersic.function(Xf, Yf, n_sersic, r_sersic, k_eff, e1, e2)
    f = f.reshape((n, -1))

    f_xx, f_yy, _ = sersic.hessian(Xf, Yf, n_sersic, r_sersic, k_eff, e1, e2)
    kappa = (f_xx + f_yy).reshape((n, -1))

    levels = np.array([0, width-0.2]) #np.linspace(0, w-0.2, 5)
    pot_lines = sersic.function(levels, levels*0, n_sersic, r_sersic, k_eff,
                                e1, e2)

    p = .7
    pot_lines = np.linspace(pot_lines[0]**p, pot_lines[1]**p, 5)**(1/p)

    reds = [ '#fd8d3c', '#fc4e2a', '#e31a1c', '#b10026']
    reds.reverse()
    blues = ['#0c2c84', '#225ea8', '#1d91c0', '#41b6c4', '#7fcdbb']

    axes[0].contour(X, Y, f, levels=pot_lines[1:], linestyles=['--'],
                    linewidths=[1.3], colors=blues)
    axes[0].axis('equal')
    axes[0].set_xticks([])
    axes[0].set_yticks([])
    axes[0].set_xlim(-width, width)
    axes[0].set_ylim(-width, width)

    axes[0].annotate(r'$q=${:.2f}'.format(q), (-width*0.8, width*0.8))

    _f_xx, _f_yy, _ = sersic.hessian(levels, levels*0, n_sersic, r_sersic,
                                     k_eff, e1, e2)
    kappa_lines = _f_xx + _f_yy

    kappa_lines = 1/np.linspace(1/kappa_lines[1]**p,
                                1/kappa_lines[0]**p, 5)**(1/p)

    axes[1].contour(X, Y, kappa, linestyles=['-'], levels=kappa_lines[:-1],
                    linewidths=[1.3], colors=reds)
    axes[1].axis('equal')
    axes[1].set_xticks([])
    axes[1].set_yticks([])
    axes[1].set_xlim(-width, width)
    axes[1].set_ylim(-width, width)

    # 1. Get transformation operators for axis and figure
    ax0tr = axes[0].transData # Axis 0 -> Display
    ax1tr = axes[1].transData # Axis 1 -> Display
    figtr = fig.transFigure.inverted() # Display -> Figure
    # 2. Transform arrow start point from axis 0 to figure coordinates
    ptB = figtr.transform(ax0tr.transform((width-0.15, 0.)))
    # 3. Transform arrow end point from axis 1 to figure coordinates
    ptE = figtr.transform(ax1tr.transform((-width+0.18, 0.)))

    arrow = matplotlib.patches.FancyArrowPatch(
        ptB, ptE, transform=fig.transFigure,  # Place arrow in figure coord system
        fc = "white", edgecolor='k', connectionstyle="arc3,rad=0.0",
        arrowstyle='simple,head_length=0.45,head_width=.8,tail_width=0.35',
        alpha = 1., mutation_scale = 15., linewidth=1.1)
    # 5. Add patch to list of objects to draw onto the figure
    fig.patches.append(arrow)

    axes[0].set_title(r'Deflection potential')
    axes[1].set_title(r'Surface density')

    plt.show()
```

```python
from scipy.special import comb
import cmath

P = 10

## Euler nodes and weights
kes = np.arange(2*P+1)
betas = np.sqrt(2*P * np.log(10) / 3. + 1j * 2*np.pi * kes)
epsilons = np.zeros(2*P+1)

epsilons[0] = 0.5
epsilons[1:P+1] = 1.
epsilons[-1] = 1/2**P

for k in range(1, P):
    epsilons[2*P-k] = epsilons[2*P-k+1] + 1/2**P * comb(P, k)

etas = (-1)**kes * epsilons * 2 * np.sqrt(2*np.pi) * 10**(P/3)

def transform(func, sigmas, **kwargs):
    """
    """
    f_sigmas = np.zeros_like(sigmas)

    f_sigmas = np.sum(etas * func(sigmas[:,np.newaxis]*betas[np.newaxis, :],
                                  **kwargs).real,
                      axis=1
                     )

    return f_sigmas

def decompose(func, sigma_start=0.02, sigma_end=15, N_sigma=15, **kwargs):
    """
    Compute the amplitudes and sigmas of Gaussian components using the
    integral transform with Gaussian kernel. The returned values are in the
    convention of eq. (2.13).
    """
    sigmas = np.logspace(np.log10(sigma_start), np.log10(sigma_end), N_sigma)

    f_sigmas = transform(func, sigmas, **kwargs)

    # weighting for trapezoid method integral
    f_sigmas[0] *= 0.5
    f_sigmas[-1] *= 0.5

    del_log_sigma = np.abs(np.diff(np.log(sigmas)).mean())

    f_sigmas *= del_log_sigma / np.sqrt(2.*np.pi)

    return f_sigmas, sigmas

def sersic_func(R, n_sersic=1):
    """
    """
    b_n = 1.9992 * n_sersic - 0.3271

    return np.exp(-b_n*R**(1./n_sersic) + b_n)

def nfw_func(R, r_s=5, amp=1./1.39552425):
    """
    """
    shape = R.shape
    R = R.flatten()
    x = R/r_s

    f = np.empty(shape=x.shape, dtype=x.dtype)

    range1 = (x > 1.)
    if np.any(range1):
        s = x[range1]
        f[range1] = (1 - np.arccos(1/s) / np.sqrt(s*s - 1)) / (s*s - 1)

    range2 = (x < 1.)
    if np.any(range2):
        s = x[range2]
        f[range2] = (1 - np.arccosh(1/s) / np.sqrt(1 - s*s)) / (s*s - 1)

    range3 = np.logical_and(np.logical_not(range1), np.logical_not(range2))
    if np.any(range3):
        s = x[range3]
        f[range3] = 1./3.

    return amp * f.reshape(shape)

def sum_gauss_components(R, A_sigmas, sigmas):
    """
    """
    total = np.zeros_like(R)

    for i, r in enumerate(R):
        total[i] = np.sum(A_sigmas * np.exp(-r*r/2/sigmas**2))

    return total
```

```python
def plot_gauss_decompose(NFW=True, n_sersic=4, N_sigma=15,
                         sigma_start=0.02, sigma_end=15):
    """
    """
    blue = '#4575b4'
    red = '#d73027'

    r_s = 5

    sigs = np.logspace(-2, 2, 1000)
    f_sigs_sersic =  transform(sersic_func, sigs, n_sersic=n_sersic)
    f_sigs_nfw = transform(nfw_func, sigs, r_s=r_s)

    amps_sersic, sigmas_sersic = decompose(sersic_func, sigma_start=sigma_start,
                             sigma_end=sigma_end, N_sigma=N_sigma,
                             n_sersic=n_sersic)

    amps_nfw, sigmas_nfw = decompose(nfw_func, sigma_start=0.005*r_s,
                             sigma_end=50*r_s, N_sigma=15,
                             r_s=r_s)

    Rs = np.logspace(-1, 1, 500)
    sersic_approx = sum_gauss_components(Rs, amps_sersic, sigmas_sersic)
    nfw_approx = sum_gauss_components(Rs, amps_nfw, sigmas_nfw)

    sersic_true = sersic_func(Rs, n_sersic=n_sersic)
    nfw_true = nfw_func(Rs, r_s=r_s)

    fig = plt.figure(figsize=(12, 4))

    ax1 = fig.add_subplot(131)
    ax1.loglog(sigs, f_sigs_sersic, color=blue, lw=2., ls='-')

    for s in sigmas_sersic:
        ax1.axvline(s, .97, 1, color=blue)

    if NFW:
        ax1.loglog(sigs, f_sigs_nfw, color=red, lw=2., ls='--')

        for s in sigmas_nfw:
            ax1.axvline(s, .97, 1, color=red)

    ax1.set_ylim(1e-4, 1e3)
    ax1.set_xlim(1e-2, 1e2)
    #ax1.axis('equal')
    #ax1.legend()
    ax1.set_xlabel(r'$\sigma$')
    ax1.set_ylabel(r'$f\ (\sigma)$')

    ax2 = fig.add_subplot(132)
    #ax2.loglog(Rs, sersic_true, color='k', lw=2., ls='-')
    ax2.loglog(Rs, sersic_approx, color=blue, lw=2., ls='-')
    if NFW:
        ax2.loglog(Rs, nfw_approx, color=red, lw=2., ls='--')
    ax2.set_ylim(1e-4, 1e3)
    ax2.set_xlim(1e-1, 1e1)
    #ax2.axis('equal')
    #ax1.legend()
    ax2.set_xlabel(r'$R/R_{\rm eff}$')
    ax2.set_ylabel(r'$\Sigma_{\rm approx}$')

    ax3 = fig.add_subplot(133)
    ax3.plot(Rs, (sersic_true - sersic_approx)/np.sqrt(sersic_true)*100,
             color=blue, lw=2., ls='-',
             label=r'$n_{\rm Sersic}=$'+'{}'.format(n_sersic))
    if NFW:
        ax3.plot(Rs, (nfw_true - nfw_approx)/np.sqrt(nfw_true)*100,
                 color=red, lw=2., ls='--', label=r'Projected NFW')
    ax3.set_xscale('log')
    ax3.set_ylim(-2, 2)
    ax3.set_xlim(1e-1, 1e1)
    ax3.set_xlabel(r'$R/R_{\rm eff}$')
    ax3.set_ylabel(r'Residual / Noise')
    ax3.legend()

    fig.tight_layout()
    plt.show()
```

```python
from mgefit.mge_fit_1d import mge_fit_1d

def plot_mge_comparison(n_sersic=1, N_sigma=15):
    blue =  '#4575b4'

    R = np.logspace(-1, 1, 200)
    amps, sigs = decompose(sersic_func, N_sigma=N_sigma, n_sersic=n_sersic)

    sersic_approx = sum_gauss_components(R, amps, sigs)

    sersic_R = sersic_func(R, n_sersic=n_sersic)
    mge = mge_fit_1d(R, sersic_R, ngauss=N_sigma, plot=False, quiet=True)

    fig = plt.figure(figsize=(12, 4))

    ax1 = fig.add_subplot(131)
    ax1.clear()

    for a, s in zip(amps, sigs):
        ax1.loglog(R, a*np.exp(-R**2/2/s**2), color='#91bfdb',
                   ls=':', lw=1.5)

    ax1.loglog(R, sersic_approx, color=blue, lw=2,
               label=r'$n_{\rm Sersic}=$'+'{}'.format(n_sersic))
    ax1.set_xlim(1e-1, 1e1)
    ax1.set_ylim(1e-4, 1e3)
    ax1.set_xlabel(r'$R/R_{\rm eff}$')
    ax1.set_ylabel(r'Our method  $\Sigma_{\rm approx}$')
    ax1.legend()

    ax2 = fig.add_subplot(132)
    ax2.clear()
    ax2.loglog(mge.x, mge.gauss*mge.weights[None, :], 'lightgrey',
               lw=1.5, ls='-.')

    ax2.loglog(mge.x, mge.yfit, 'grey', lw=2, ls='--',
               label=r'$n_{\rm Sersic}=$'+'{}'.format(n_sersic))

    ax2.set_ylim(1e-4, 1e3)
    ax2.set_xlim(1e-1, 1e1)
    ax2.set_xlabel(r'$R/R_{\rm eff}$')
    ax2.set_ylabel(r"Cappellari (2002) $\Sigma_{\rm approx}$")
    ax2.legend()

    ax3 = fig.add_subplot(133)
    ax3.clear()
    ax3.plot(R, (sersic_R - sersic_approx)/np.sqrt(sersic_R)*100, color=blue,
             lw=2., ls='-', label='Our method')
    ax3.plot(mge.x, (mge.err)/np.sqrt(mge.y)*100, color='grey', lw=2, ls='--',
             label='Cappellari (2002)', zorder=-2)
    ax3.set_xscale('log')
    ax3.set_ylim(-2, 2)
    ax3.set_xlim(1e-1, 1e1)
    ax3.set_xlabel(r'$R/R_{\rm eff}$')
    ax3.set_ylabel(r'Residual / Noise')
    ax3.legend()

    fig.tight_layout()
    plt.show()
```

```python
from scipy.special import wofz
from copy import deepcopy

class EllipticalGaussianLens(object):
    """
    """
    def __init__(self, min_ellipticity=1e-5):
        """
        """
        self.min_ellipticity = 1e-5

    def quarantine(self, q):
        """
        """
        if 1 - q < self.min_ellipticity:
            q = 1 - self.min_ellipticity

        return q

    def get_deflection(self, x, y, amp, sigma, q):
        """
        """
        q = self.quarantine(q)

        _p = q / sigma / np.sqrt(2 * (1. - q**2))

        sig_func_re, sig_func_im = self.sigma_function(_p * x, _p * y, q)

        alpha_x = amp * sigma * np.sqrt(2*np.pi/(
                1.-q**2)) * sig_func_re
        alpha_y = - amp * sigma * np.sqrt(
            2 * np.pi / (1. - q ** 2)) * sig_func_im

        return alpha_x, alpha_y

    def get_convergence(self, x, y, amp, sigma, q):
        """
        """
        return amp * np.exp(-(q*q*x*x + y*y)/2./sigma/sigma)

    def get_shear(self, x, y, amp, sigma, q):
        """
        """
        q = self.quarantine(q)

        _p = q / sigma / np.sqrt(2 * (1. - q**2))
        sig_func_re, sig_func_im = self.sigma_function(_p * x, _p * y, q)

        kappa = self.get_convergence(x, y, amp, sigma, q)

        shear = - 1/(1-q*q) * ((1+q**2)*kappa - 2*q*amp + np.sqrt(
            2*np.pi) * q*q * amp * (x - 1j*y) / sigma / np.sqrt(1-q*q) * (
            sig_func_re - 1j*sig_func_im))

        return shear.real, -shear.imag

    def get_magnification(self, x, y, amp, sigma, q):
        """

        """
        kappa = self.get_convergence(x, y, amp, sigma, q)
        g1, g2 = self.get_shear(x, y, amp, sigma, q)

        return 1. / ((1.-kappa)**2 - (g1**2+g2**2))

    def sigma_function(self, x, y, q):
        """
        Compute the function varsigma(z; q) from equation (4.12).

        :param x: Real part of complex variable, x = Re(z)
        :type x: float or numpy.array
        :param y: Imaginary part of complex variable, y = Re(z)
        :type y: float or numpy.array
        :param q: Axis ratio.
        :type q: float
        :return: value of Sigma function, equation (4.12) from Shajib (2019)
        :rtype: tuple (type, type) with type being type(x) or type(y)
        """
        y_sign = np.sign(y)
        y_ = deepcopy(y) * y_sign
        z = x + 1j * y_
        zq = q * x + 1j * y_ / q

        w = wofz(z)
        wq = wofz(zq)

        # exponential factor in the 2nd term of eqn. (4.15) of Shajib (2019)
        exp_factor = np.exp(-x * x * (1 - q * q) - y_ * y_ * (1 / q / q - 1))

        sigma_func_real = w.imag - exp_factor * wq.imag
        sigma_func_imag = (- w.real + exp_factor * wq.real) * y_sign

        return sigma_func_real, sigma_func_imag

    def get_critical_curves(self, amp, sigma, q, compute_window=4,
                            start_scale=0.05, max_order=10):
        """
        This method is modified from a `lenstronomy` function written by
        Simon Birrer.

        :param amp:
        :param sigma:
        :param q:
        :param compute_window:
        :param tiling_scale:
        :return:
        """

        numPix = int(2 * compute_window / start_scale)

        arr = np.linspace(-compute_window, compute_window, numPix)
        x_grid_init, y_grid_init = np.meshgrid(arr, arr)

        mag_init = self.get_magnification(x_grid_init, y_grid_init,
                                          amp, sigma, q)

        x_crit_list = []
        y_crit_list = []

        # iterate through original triangles and return ra_crit, dec_crit list
        for i in range(numPix-1):
            for j in range(numPix-1):
                edge1 = [x_grid_init[i, j], y_grid_init[i, j], mag_init[i, j]]
                edge2 = [x_grid_init[i+1, j+1], y_grid_init[i+1, j+1],
                         mag_init[i+1, j+1]]
                edge_90_1 = [x_grid_init[i, j+1], y_grid_init[i, j+1],
                             mag_init[i, j+1]]
                edge_90_2 = [x_grid_init[i+1, j], y_grid_init[i+1, j],
                             mag_init[i+1, j]]
                x_crit, y_crit = self._tiling_crit(edge1, edge2, edge_90_1,
                                                   max_order, amp, sigma, q)
                x_crit_list += x_crit  # list addition
                y_crit_list += y_crit  # list addition
                x_crit, y_crit = self._tiling_crit(edge1, edge2, edge_90_2,
                                                   max_order, amp, sigma, q)
                x_crit_list += x_crit  # list addition
                y_crit_list += y_crit  # list addition

        return np.array(x_crit_list), np.array(y_crit_list)

    def _tiling_crit(self, edge1, edge2, edge_90, max_order, amp, sigma, q):
        """
        Tile a rectangular triangle and compares the signs of the magnification.
        This method is modified from a `lenstronomy` function written by Simon Birrer.

        :param edge1: [x_coordinate, y_coordinate, magnification]
        :param edge2: [x_coordinate, y_coordinate, magnification]
        :param edge_90: [x_coordinate, y_coordinate, magnification]
        :param max_order: maximal order to fold triangle
        :return:
        """
        x_1, y_1, mag_1 = edge1
        x_2, y_2, mag_2 = edge2
        x_3, y_3, mag_3 = edge_90
        sign_list = np.sign([mag_1, mag_2, mag_3])

        # if all signs are the same
        if sign_list[0] == sign_list[1] and sign_list[0] == sign_list[2]:
            return [], []
        else:
            # Split triangle along the long axis
            # execute tiling twice
            # add ra_crit and dec_crit together
            # if max depth has been reached, return the mean value in the triangle
            max_order -= 1

            if max_order <= 0:
                return [(x_1 + x_2 + x_3)/3], [(y_1 + y_2 + y_3)/3]
            else:
                # split triangle
                # find point in the middle of the long axis to split triangle
                x_90_ = (x_1 + x_2)/2
                y_90_ = (y_1 + y_2)/2
                mag_90_ = self.get_magnification(x_90_, y_90_, amp, sigma, q)
                edge_90_ = [x_90_, y_90_, mag_90_]
                x_crit, y_crit = self._tiling_crit(edge_90, edge1, edge_90_,
                                                   max_order, amp, sigma, q)
                x_crit_2, y_crit_2 = self._tiling_crit(edge_90, edge2, edge_90_,
                                                       max_order, amp, sigma, q)
                x_crit += x_crit_2
                y_crit += y_crit_2

                return x_crit, y_crit

    def get_critical_curves_and_caustics(self, amp, sigma, q, **kwargs):
        """
        """
        x_crit, y_crit = self.get_critical_curves(amp, sigma, q, **kwargs)

        alpha_x, alpha_y = self.get_deflection(x_crit, y_crit, amp, sigma, q)

        x_caus, y_caus = x_crit - alpha_x, y_crit - alpha_y

        return x_crit, y_crit, x_caus, y_caus
```

```python
from matplotlib.colors import LinearSegmentedColormap
from matplotlib.patches import Rectangle
from matplotlib.patches import Circle
from matplotlib.patches import FancyArrow
from matplotlib.legend_handler import HandlerPatch

class HandlerEllipse(HandlerPatch):
    """
    """
    def __init__(self):
        pass

    def create_artists(self, legend, orig_handle,
                       xdescent, ydescent, width, height, fontsize, trans):
        """
        """
        center = 0.5 * width - 0.5 * xdescent, 0.5 * height - 0.5 * ydescent
        p = mpatches.Ellipse(xy=center, width=width + xdescent,
                             height=height + ydescent)
        self.update_prop(p, orig_handle, legend)
        p.set_transform(trans)

        return [p]

def make_legend_arrow(legend, orig_handle,
                          xdescent, ydescent,
                          width, height, fontsize):
    """"""
    p = FancyArrow(0, 0.5*height, width, 0, width=1.5,
                   length_includes_head=True, head_width=0.5*height,
                   overhang=.1 )
    return p

def plot_gaussian_lens(amplitude=2., σ=1., q=0.5):
    """
    """
    amp = amplitude
    sigma = σ
    w = 4
    n = 11

    emerald = '#66c2a5' #sns.xkcd_rgb['emerald']
    orange = '#fc8d62' #sns.xkcd_rgb['bright orange']
    blue = '#8da0cb' #sns.xkcd_rgb['light purple']

    xs = np.linspace(-w, w, n)
    ys = np.linspace(-w, w, n)

    X, Y = np.meshgrid(xs, ys)

    gaussian_lens = EllipticalGaussianLens()

    kappa = gaussian_lens.get_convergence(X, Y, amp, sigma, q)
    alpha_x, alpha_y = gaussian_lens.get_deflection(X, Y, amp, sigma, q)

    x_01 = 0
    y_01 = w/4

    x_02 = 0
    y_02 = w/2

    x_03 = 0
    y_03 = w*3/4

    dx = 0.01
    dy = 0.01

    xs1 = []
    ys1 = []

    xs2 = []
    ys2 = []

    xs3 = []
    ys3 = []

    while y_01 >= 0.:
        xs1.append(x_01)
        ys1.append(y_01)

        alpha_x1, alpha_y1 = gaussian_lens.get_deflection(x_01, y_01, amp, sigma, q)

        y_01 += - alpha_x1 / alpha_y1 * dx
        x_01 += dx

    while y_02 >= 0.:
        xs2.append(x_02)
        ys2.append(y_02)

        alpha_x2, alpha_y2 = gaussian_lens.get_deflection(x_02, y_02, amp, sigma, q)

        y_02 += - alpha_x2 / alpha_y2 * dx
        x_02 += dx

    while y_03 >= 0.:
        xs3.append(x_03)
        ys3.append(y_03)

        alpha_x3, alpha_y3 = gaussian_lens.get_deflection(x_03, y_03, amp, sigma, q)
        y_03 += - alpha_x3 / alpha_y3 * dx
        x_03 += dx

    xs1 = np.array(xs1)
    ys1 = np.array(ys1)
    xs2 = np.array(xs2)
    ys2 = np.array(ys2)
    xs3 = np.array(xs3)
    ys3 = np.array(ys3)

    fullxs1 = np.append(xs1, xs1[::-1])
    fullys1 = np.append(ys1, -ys1[::-1])
    fullxs2 = np.append(xs2, xs2[::-1])
    fullys2 = np.append(ys2, -ys2[::-1])
    fullxs3 = np.append(xs3, xs3[::-1])
    fullys3 = np.append(ys3, -ys3[::-1])

    fullxs1 = np.append(fullxs1, -xs1)
    fullys1 = np.append(fullys1, -ys1)
    fullxs2 = np.append(fullxs2, -xs2)
    fullys2 = np.append(fullys2, -ys2)
    fullxs3 = np.append(fullxs3, -xs3)
    fullys3 = np.append(fullys3, -ys3)

    fullxs1 = np.append(fullxs1, -xs1[::-1])
    fullys1 = np.append(fullys1, ys1[::-1])
    fullxs2 = np.append(fullxs2, -xs2[::-1])
    fullys2 = np.append(fullys2, ys2[::-1])
    fullxs3 = np.append(fullxs3, -xs3[::-1])
    fullys3 = np.append(fullys3, ys3[::-1])

    x_crit, y_crit, x_caus, y_caus = gaussian_lens.get_critical_curves_and_caustics(amp, sigma, q)

    fig = plt.figure(figsize=(10, 5))
    ax = fig.add_subplot(121)

    cmap = LinearSegmentedColormap.from_list('mycmap', ['white', '#fdcdac'])

    ep1, = ax.plot(fullxs1, fullys1, color='#7570b3', ls='--', zorder=1, lw=1.5)
    ep2, = ax.plot(fullxs2, fullys2, color='#7570b3', ls='--', zorder=1, lw=1.5)
    ep3, = ax.plot(fullxs3, fullys3, color='#7570b3', ls='--', zorder=1, lw=1.5)

    ax.quiver(xs, ys, -alpha_x, -alpha_y, color=emerald, zorder=2,
              angles='xy', scale_units='xy', scale=4, label='deflection')
    ax.axis('equal')

    convergence = ax.imshow(np.arcsinh(np.arcsinh(np.arcsinh(kappa))),
                            interpolation='bicubic', cmap=cmap,
                            label='convergence', extent=(-w, w, -w, w))
    ax.set_xlabel(r'$x/\sigma$')
    ax.set_ylabel(r'$y/\sigma$')
    ax.set_yticks([-2, 0, 2])
    ax.set_xticks([-2, 0, 2])
    ax.set_xlim(-w, w)
    ax.set_ylim(-w, w)
    ax.set_aspect('auto')

    height = 40
    width = 1

    arrow = ax.arrow(100, 1000, 2.5, 0.6, label='My label', color=emerald)
    c = Circle((0.5, 0.5), 0.25, facecolor='#fdcdac',
                        edgecolor="none", linewidth=0.5)

    ax.legend([c, arrow, ep1],
              ['convergence', 'deflection', 'isopotential'],
              handler_map={
                  Rectangle: HandlerPatch(patch_func=HandlerEllipse),
                  FancyArrow : HandlerPatch(patch_func=make_legend_arrow),
              }, loc='upper right')

    ax1 = fig.add_subplot(122)

    _line = np.array([500, 600])

    ax1.plot(x_crit, y_crit, 'o', markersize=0.05, c='#222222')
    ax1.plot(_line, _line, c='#222222', label='critical curve')

    ax1.plot(x_caus, y_caus, 'o', c='#e78ac3', markersize=0.05)
    ax1.plot(_line, _line, c='#e78ac3', label='caustic')

    ax1.axis('equal')
    ax1.set_xlim(-w, w)
    ax1.set_ylim(-w, w)
    ax1.legend(loc='upper right')
    ax1.set_xlabel(r'$x/\sigma$')
    ax1.set_yticks([])
    ax1.set_xticks([-2, 0, 2])

    fig.subplots_adjust(wspace=0)

    plt.show()
```

# Unified lensing and kinematic analysis for _any_ elliptical mass profile

### Anowar J. Shajib <a href="https://orcid.org/0000-0002-5558-888X"></a>

_Department of Physics and Astronomy, University of California, Los Angeles, CA 90095-1547, US_

Email: ajshajib@astro.ucla.edu

>>## Abstract

>>We demonstrate an efficient method to compute the strong-gravitational-lensing deflection angle and magnification for any elliptical surface-density profile. This method solves a numerical hurdle in lens modelling that has lacked a general solution for nearly three decades. The hurdle emerges because it is prohibitive to derive analytic expressions of the lensing quantities for most elliptical mass profiles. In our method, we first decompose an elliptical mass profile into concentric Gaussian components. We introduce an integral transform that provides us with a fast and accurate algorithm for this Gaussian decomposition. We derive analytic expressions of the lensing quantities for a Gaussian component. As a result, we can compute these quantities for the total mass profile by adding up the contributions from the individual components. This lensing analysis self-consistently completes the kinematic description in terms of Gaussian components presented by Cappellari ([2008](http://adsabs.harvard.edu/abs/2008MNRAS.390...71C)). Our method is general without extra computational burden unlike other methods currently in use.

>>**Key words:** gravitational lensing: strong – galaxies: kinematics and dynamics – methods:analytical – methods: numerical – methods: data analysis

## 1 Introduction: precise computation made easy

Gravitational lensing (hereafter, lensing) has versatile applications in astrophysics and cosmology. Lensing is the effect when light bends while passing by a massive object. If two galaxies sit along the same line-of-sight of an observer, the background galaxy appears multiple times due to lensing. This system is called a galaxy-scale strong-lensing system (hereafter, lens). Lenses are useful to measure the Hubble constant $H_0$, dark matter subhalo mass function, dust characteristics in galaxies, mass of super-massive black holes, stellar initial mass function, etc. (e.g., Falco et al. [1999](http://adsabs.harvard.edu/abs/1999ApJ...523..617F), Peng et al. [2006](http://adsabs.harvard.edu/abs/2006ApJ...649..616P), Suyu et al. [2013](http://adsabs.harvard.edu/abs/2013ApJ...766...70S), Vegetti et al. [2014](http://adsabs.harvard.edu/abs/2014MNRAS.442.2017V), Schechter et al. [2014](http://adsabs.harvard.edu/abs/2014ApJ...793...96S)).

In several applications, stellar kinematics play a complementary role to lensing. Lensing-only observables suffer from mass-sheet degeneracy (MSD)—if a mass profile is appropriately rescaled after an infinite mass-sheet is added on top of it, then all the lensing observables stay invariant except the time delay (Falco et al. [1985](http://adsabs.harvard.edu/abs/1985ApJ...289L...1F), Schneider & Sluse [2014](http://adsabs.harvard.edu/abs/2014A%26A...564A.103S)). When a measured quantity depends on the mass profile, the MSD adds uncertainty to the measurement. Kinematics help break this degeneracy. Lensing observables probe the projected mass; kinematic observables probe the three-dimensional potential. Thus, the lensing–kinematics combination tightly constrains the mass profile, and enables us to robustly measure astrophysical and cosmological quantities (e.g., Treu et al. [2004](http://adsabs.harvard.edu/abs/2004ApJ...611..739T), Barnabe et al. [2011](http://adsabs.harvard.edu/abs/2011MNRAS.415.2215B)).</p>

In practice, it is difficult to compute lensing and kinematic quantities for elliptical mass profiles, which are required to describe the most common kind of lenses, i.e., elliptical galaxies. We need to integrate over density profiles while fitting a model to either the lensing or the kinematic data. For example, in lensing, the deflection angle is an integral over the surface density profile; in kinematics, the line-of-sight velocity dispersion is a double integral over three-dimensional mass and light profiles. For most elliptical density profiles, we are unable to express these integrals with elementary or special functions. Special functions are numerically well-studied, hence fast algorithms to compute them are usually available. Thus, these functions can be numerically convenient for evaluating model-predicted observables in large numbers (e.g., $\sim10^6$)  when sampling the lens model posterior with Markov chain Monte Carlo (MCMC) methods, or even when only searching for the best-fit model. Otherwise, it would be inefficient to numerically compute lensing and kinematic integrals for general elliptical profiles.

Usually, the numerical difficulties are circumvented through simplifying assumptions or approximations. We now outline common assumptions and approximations in kinematic and lensing analyses, noting that these can limit the accuracy and precision of the inferences.

Either axisymmetry or spherical symmetry is usually assumed for kinematic analysis. If we start with a surface density profile for lensing analysis, we need to deproject this profile along the line of sight to compute the kinematics. This deprojection has an infinite degeneracy (Contopoulos [1956](http://adsabs.harvard.edu/abs/1956ZA.....39..126C)). Therefore, it is necessary to choose a line-of-sight symmetry when deprojecting, for which either axisymmetry or spherical symmetry is often a convenient choice.

In lensing analysis, spherical symmetry is rarely sufficient and we need to consider ellipticity to achieve the required precision. All the lensing quantities are related to the deflection potential or its derivatives. The time delay depends on the deflection potential difference. The gradient of the deflection potential gives the deflection angle. The Hessian of the deflection potential relates to the surface density, the shear, and the magnification. To efficiently compute these quantities for the elliptical case, we can find the following three approximations in the literature:

1. **Ellipticity in deflection potential:** The gradient and the Hessian of an elliptical deflection potential can be easily computed through numerical differentiation (e.g., Kovner [1987](http://adsabs.harvard.edu/abs/1987Natur.325..507K), Golse & Kneib [2002](http://adsabs.harvard.edu/abs/2002A%26A...390..821G)). However, this solution is not general, as the surface density becomes dumbbell-shaped for an elliptical deflection potential with axis ratio $q \lesssim 0.6$ (Fig. [1](#scrollTo=YpMX6aGH9BWl), Kassiola & Kovner [1993](http://adsabs.harvard.edu/abs/1993ApJ...417..450K)). This oddly shaped surface-density is unphysical.

2. **Elliptical power-law profile:** We can efficiently compute the lensing quantities for the elliptical power-law profile using numerical approximation or analytical expressions (for the isothermal case, Korman [1994](http://adsabs.harvard.edu/abs/1994A%26A...284..285K); and for the general case, Barkana [1998](http://adsabs.harvard.edu/abs/1998ApJ...502..531B), Tessore & Metcalf [2015](http://adsabs.harvard.edu/abs/2015A%26A...580A..79T)). This profile can be sufficient to use in statistical studies that do not require detailed modelling of individual lenses (e.g., Koopmans et al. [2009](http://adsabs.harvard.edu/abs/2009ApJ...703L..51K), Sonnenfeld et al. [2013](http://adsabs.harvard.edu/abs/2013ApJ...777...97S)). However, adopting a power-law profile artificially breaks the MSD and it could potentially bias the $H_0$ measurement (e.g., Schneider & Sluse [2013](http://adsabs.harvard.edu/abs/2013A%26A...559A..37S), Sonnenfeld [2018](http://adsabs.harvard.edu/abs/2018MNRAS.474.4648S)). Therefore, we need to explore different, physically-motivated mass models, such as a _composite model_ that explicitly accounts for the luminous and the dark components (Suyu et al. [2014](http://adsabs.harvard.edu/abs/2014ApJ...788L..35S), Yıldırım et al. [2019](https://ui.adsabs.harvard.edu/abs/2019arXiv190407237Y)).

3. **Chameleon profile:** The Sersic profile well describes the surface brightness of a galaxy (Sersic [1968](http://adsabs.harvard.edu/abs/1968adga.book.....S)). The Chameleon profile approximates the Sersic profile within a few per cent (Dutton et al. [2011](http://adsabs.harvard.edu/abs/2011MNRAS.417.1621D)). We can efficiently compute lensing quantities for the Chameleon profile using analytic expressions. However, this profile only describes the baryonic component. The precise lensing analysis of elliptical Navarro–Frenk–White (NFW) profile for the dark component still lacks a general solution (Navarro et al. [1997](http://adsabs.harvard.edu/abs/1996ApJ...462..563N)).

In a nutshell, these approximations for elliptical lensing analysis are only applicable in restricted regimes as described above.

```python
interact(plot_potential_to_density_contours,
         q=FloatSlider(min=0.1, max=0.95, step=0.05, value=0.6, continuous_update=False),
         n_sersic=FloatSlider(min=1, max=6, step=0.5, value=4, continuous_update=False));

#plot_potential_to_density_contours(q=0.6, n_sersic=4)
```

<!--center></center-->

>**Figure 1:** Elliptical deflection potential (left column) producing dumbbell-shaped surface-density (right column). The dashed contours in the left column are isopotential curves for Sersic profile, and the solid contours in the right column are corresponding isodensity curves. The dumbbell shape in surface density is unphysical and it gets more pronounced for higher ellipticity in the deflection potential. Hence, we can not use elliptical deflection potential to simplify lensing analysis of moderately elliptical galaxies. We need to treat ellipticity in the surface density, not deflection potential, to make our lensing analysis generally consistent with our physical priors.

In this paper, we present a general method to precisely compute the gradient and the Hessian of the deflection potential for any elliptical surface-density profile. The method follows a ''divide and conquer'' strategy. We can approximately divide, or decompose, an elliptical profile into concentric Gaussian components (e.g., Bendinelli [1991](http://adsabs.harvard.edu/abs/1991ApJ...366..599B)). For this Gaussian decomposition, we devise a fast and accurate algorithm by introducing an integral transform. For each Gaussian component, we derive analytic expressions of the gradient and the Hessian of the deflection potential. For the deprojected Gaussian component, Cappellari ([2008]((http://adsabs.harvard.edu/abs/2008MNRAS.390...71C))) derives the line-of-sight velocity dispersion. We can combine the computed quantities from each Gaussian component back together to obtain these quantities for the total density profile. In this way, the lensing and kinematic descriptions are self-consistently unified. At the same time, this method is general, as we can apply it to lensing with any elliptical surface-density and to kinematics with either axisymmetry or spherical symmetry. Our method is more efficient than numerical integration to compute lensing quantities for an elliptical surface-density profile.

We organize this paper as follows. In Section [2](#scrollTo=DvRslW9qN1SL), we motivate the ''divide and conquer'' strategy behind our method and introduce an integral transform that provides a fast algorithm to decompose any elliptical surface-density profile into concentric Gaussian components. In Section [3](#scrollTo=alI-uL1t9HSR), we summarize the kinematic description of the Gaussian components from Cappellari ([2008](http://adsabs.harvard.edu/abs/2008MNRAS.390...71C)). In Section [4](#scrollTo=TzkbBKWhcIbn), we derive the gradient and the Hessian of the deflection potential for elliptical Gaussian surface-density. Next in Section [5](#scrollTo=uHG1Plf5apG9), we demonstrate a proof-of-concept for our method using simulated data. Then, we summarize the paper in Section [6](#scrollTo=oout9ImHc58C). Additionally in Appendix [A](#scrollTo=9qdjVclOfmVk), we prove some fundamental theorems for the integral transform introduced in Section [2](#scrollTo=DvRslW9qN1SL). This integral transform with a Gaussian kernel is the continuous case of concentric Gaussian decomposition. The theorems in Appendix [A](#scrollTo=9qdjVclOfmVk) establish the existence, uniqueness, and invertibility of this transform.

## 2 Decomposing an elliptical profile into concentric Gaussian components

We aim to decompose an elliptical surface-density profile into simpler functions to make the computation tractable. This function should be simple enough so that we can both

1. express the deflection angle in terms of elementary or special functions, and
2. easily deproject it into three-dimension and compute the enclosed mass for kinematic analysis.

The Gaussian function meets both of these criteria. We validate criterion (1) in Section [4.2](#scrollTo=slP9xat7i8YZ), where we express the deflection angle for an elliptical Gaussian surface-density profile with the complex error function. For criterion (2), deprojecting a two-dimensional Gaussian into three-dimension is straightforward, as the Abel inversion of a two-dimensional Gaussian is a three-dimensional Gaussian. The enclosed mass for a three-dimensional Gaussian has the form of the error function, which we can efficiently compute without integrating numerically. Given these points, we approximately decompose an elliptical surface-density profile as

\begin{align} \tag{2.1}\label{21}
	\Sigma(x, y) \approx \sum_{j=0}^J {\Sigma_0}_{j} \exp\left(-\frac{q^2x^2+y^2}{2 \sigma_j^2}\right),
\end{align}

where ${\Sigma_0}_ j$ is the amplitude of the $j$-th Gaussian, and all the components have a common axis ratio $q$. Similar decomposition into concentric Gaussian components has been used in the literature to fit the surface brightness profile of galaxies [called as the multi-Gaussian expansion (MGE) by Emsellem et al. ([1994](http://adsabs.harvard.edu/abs/1994A%26A...285..739E)), and the mixture-of-Gaussians by Hogg & Lang ([2013](http://adsabs.harvard.edu/abs/2013PASP..125..719H))]. The lensing and kinematic quantities of our interest—namely the gradient and the Hessian of the deflection potential, and the line-of-sight velocity dispersion—follow the principle of superposition. As a result, we can compute these quantities separately for each Gaussian component and then add them together to recover these quantities for the total surface-density profile (see Fig. [2](#scrollTo=M0C0Fo4jsRPe&line=5&uniqifier=1)).

<center></center>

>**Figure 2.** <a id="best_figure_ever"></a>"Divide and conquer'' strategy to compute lensing quantities for elliptical surface-density profile. In this figure, we choose the deflection field as the quantity of interest to illustrate the method. However, this method works equally well for other quantities such as the lensing shear and the line-of-sight velocity dispersion. The steps in the strategy are organized into columns and the arrows show the progression of the steps. We explain each step in the text at the top of the corresponding column.

We describe the kinematics and lensing analyses for the Gaussian components in Sections [3](#scrollTo=alI-uL1t9HSR) and [4](#scrollTo=TzkbBKWhcIbn), but first, we need a fast method to decompose an elliptical profile into concentric Gaussian components. Cappellari ([2002](http://adsabs.harvard.edu/abs/2002MNRAS.333..400C)) presents a method that uses non-linear optimization. However, this non-linear optimization method is computationally too expensive to implement within MCMC. Although, this method has been implemented to compute the kinematic observable while sampling from the lens model posterior (e.g., Birrer et al. [2019](http://adsabs.harvard.edu/abs/2018arXiv180901274B)). Computing the kinematic observable can involve at least one numerical integration, which is the main bottleneck in efficiency, not the non-linear optimization method for Gaussian decomposition. To make our lens-modelling method efficient, we require a **(i)** general, **(ii)** precise, and **(iii)** fast technique to decompose a function into concentric Gaussian components. In Sections [2.1](#scrollTo=bw1Bl8DWOSNv) and [2.2](#scrollTo=U-DGfxIw6q0g), we provide a technique that satisfies these three requirements.

### 2.1 An integral transform for fast Gaussian decomposition

Now, we introduce an integral transform with a Gaussian kernel. Using this transform, we obtain an algorithm to efficiently decompose an elliptical surface-density profile into concentric Gaussian components.

We start with the simple one-dimensional case of the integral transform. We aim to approximate a function $F(x)$ as a sum of Gaussian components as

\begin{equation} \tag{2.2}\label{22}
	F(x) \approx \sum_{n=0}^{N} A_n \exp \left( -\frac{x^2}{2\sigma_n^2} \right),
\end{equation}

where $A_n$ and $\sigma_n$ are respectively the amplitude and the standard deviation of the $n$-th Gaussian component. We can convert this discrete summation into a continuous integral by taking $N \to \infty$. Accordingly, we define the following integral transform:

\begin{equation} \tag{2.3} \label{23}
	F(x) \equiv \int_0^\infty \frac{f(\sigma)}{\sqrt{2 π} \sigma} \exp\left( -\frac{x^2}{2\sigma^2}\right) \mathrm{d} \sigma.
\end{equation}

Here, the amplitude $A_n$ is converted into a function $f(\sigma)/\sqrt{2 π}\sigma$. We call $F(x)$ as the _transform_ of $f(\sigma)$. We prove three fundamental properties of this integral transform in Appendix A. These three properties tell us that

1. this integral transform exists for most mass and light profiles of practical use,
2. the transform is unique for these functions, and
3.  the integral transform is invertible.

We call $f(\sigma)$ as the _inverse transform_ of $F(x)$. The inverse transform is given by

\begin{equation} \tag{2.4}\label{24}
	f(\sigma) = \frac{1}{\mathrm{i} \sigma^2} \sqrt{\frac{2}{π}} \int_{C} z F(z) \exp \left( \frac{z^2}{2 \sigma^2} \right) \mathrm{d} z.
\end{equation}

Here, $\mathrm{i}$ is the imaginary unit as $\mathrm{i} = \sqrt{-1}$. Also, we have extended $F(x)$ to some region on the complex plane and wrote it as $F(z)$, where $z$ is a complex variable. The contour $C$ for the integral lies within the region where $F(z)$ is defined (for details, see Appendix [A](#scrollTo=9qdjVclOfmVk) and Fig. [A1](#scrollTo=zBNYTOeevKkJ)). In Section [2.1.1](#scrollTo=vzcmlzwNAVdE), we provide an algorithm that does not require $C$ to be explicitly specified for computing the inverse transform $f(\sigma)$.

We can use the inverse transform to decompose a function into concentric Gaussian components and the forward transform to recover the original function by combining the Gaussian components. First in Section [2.1.1](#scrollTo=vzcmlzwNAVdE), we first provide an efficient algorithm to compute the inverse transform from equation ([2.4](#mjx-eqn-24)); then in Section [2.1.2](#scrollTo=QSdpwV_JB_45), we discuss a method for computing the forward transform from equation ([2.3](#mjx-eqn-23)).

#### 2.1.1 Computing the inverse transform

The integral transform in equation ([2.3](#mjx-eqn-23)) can be converted into a Laplace transform by suitable change of variables (Remark [A.9](#scrollTo=cmm1SJ9lvRYH)). Therefore, we can use any of the several algorithms available for inverse Laplace transform by appropriately changing the variables (for a simple overview of the algorithms, see Abate & Whitt [2006](http://pubsonline.informs.org/doi/10.1287/ijoc.1050.0137)). In this paper, we modify the Euler algorithm to approximate equation ([2.4](#mjx-eqn-24)) as

\begin{equation} \tag{2.5}\label{25}
	f(\sigma) \approx \sum_{n=0}^{2P}  \eta_n \textrm{Re} \left[ F \left( \sigma \chi_n \right) \right].
\end{equation}

(Abate et al. [2000](https://doi.org/10.1007/978-1-4757-4828-4_8)). Here, the weights $\eta_n$ and nodes $\chi_n$ can be complex-valued and they are independent of $f(\sigma)$. The weights and the nodes are given by

\begin{equation} \tag{2.6}\label{26}
\chi_n = \left[\frac{2P \log (10)}{3} + 2 π \mathrm{i} n \right]^{1/2}, \\
\eta_n = (-1)^n\ 2\sqrt{2 π}\ 10^{P/3} \xi_p, \\
\xi_0 = \frac{1}{2},\quad \xi_n = 1,\ 1\leq n \leq P,\quad \xi_{2P} = \frac{1}{2^P}, \\
\xi_{2P-n} = \xi_{2P-n+1} + 2^{-P} \binom Pn,\ 0 < n < P.
\end{equation}

 We can precompute the weights and the nodes just once before the MCMC sampling. In that way, computing them does not add any extra burden in computing the likelihood. The precision of the inverse transform is $\sim \mathcal{O}(10^{-0.6P})$ (Abate & Whitt [2006](http://pubsonline.informs.org/doi/10.1287/ijoc.1050.0137)). Therefore, the value of $P$ can be appropriately chosen to achieve a required precision. Note that the decimal precision of the machine sets an effective upper limit for $P$. For example, the precision will not improve with increasing $P$ when $P \gtrsim 12$ for 32-bit floating point number, and when $P \gtrsim 27$ for 64-bit floating point number.  Thus, equation ([2.5](#mjx-eqn-25)) gives a straightforward, fast, and precise algorithm to compute the inverse transform.

#### 2.1.2 Computing the forward transform

Let us approximate the forward transform integral such that we can recover $F(x)$ from only a finite number of $f(\sigma)$ values computed at fixed $\sigma$'s. This finite number should be on the order of tens to keep the lensing analysis computationally feasible, as we have to compute lensing quantities for each Gaussian component individually. We write equation ([2.3](#mjx-eqn-23)) as

\begin{equation} \tag{2.7}\label{27}
	F(x) = \frac{1}{\sqrt{2 π}} \int_0^{\infty} f(\sigma) \exp \left(-\frac{x^2}{2 \sigma^2} \right) \mathrm{d} (\log \sigma) \\
	\Rightarrow F(x) = \lim_{N \to \infty} \sum_{n=1}^N \frac{f(\sigma_n)}{\sqrt{2 π}} \exp \left(-\frac{x^2}{2\sigma_n^2} \right) \Delta(\log\sigma)_n \\
	\Rightarrow F(x) \approx \sum_{n=1}^{N} A_n \exp \left(-\frac{x^2}{2\sigma_n^2} \right).
\end{equation}

We have recovered the form of equation ([2.2](#mjx-eqn-22)) by taking logarithmically spaced $\sigma_n$. Here, the amplitudes are $A_n = w_n f(\sigma_n) \Delta (\log \sigma)_n/\sqrt{2\pi}$. The weights $w_n$ depend on the choice of the numerical integration method. We use the trapezoidal method with weights $w_1 = 0.5$, $w_n = 1$ for $1<n<N$, $w_N=0.5$, as the trapezoidal method is highly efficient to numerically compute integrals of this form (Goodwin [1949](https://www.cambridge.org/core/journals/mathematical-proceedings-of-the-cambridge-philosophical-society/article/evaluation-of-integrals-of-the-form/A7B5691B06809151BBB5807E9510AA4B)). As a result, equation ([2.7](#mjx-eqn-27)) efficiently recovers $F(x)$ from only a finite number of $f(\sigma)$ values.

As an example, we demonstrate the integral transform method to decompose the NFW profile and the Sersic profile into Gaussian components (Fig. [3](#scrollTo=W3KtTXnnVNAy)).  The two-dimensional projected NFW profile is given by

\begin{equation} \tag{2.8}\label{28}
	\Sigma_{\rm NFW} (R) = 	\left\{
		\begin{array} {ll}
			\frac{2 \rho_{\rm s} r_{\rm s}}{(R/r_{\rm s})^2 - 1}
\left[1 - \frac{ \sec^{-1} \left(R/r_{\rm s}\right)}{\sqrt{(R/r_{\rm s})^2-1}} \right]  & (R > r_{\rm s}), \\
			\frac{2}{3} \rho_{\rm s} r_{\rm s}  & (R = r_{\rm s}), \\
			\frac{2 \rho_{\rm s} r_{\rm s}}{(R/r_{\rm s})^2 - 1}
\left[ 1- \frac{ {\rm sech}^{-1} {(R/r_{\rm s}) } } {\sqrt{1-(R/r_{\rm s})^2}} \right]  & (R < r_{\rm s})
		\end{array}
	\right.
\end{equation}

(Bartelmann [1996](http://adsabs.harvard.edu/abs/1996A%26A...313..697B)). Here, $\rho_{\rm s}$ is the three-dimensional density normalization, and $r_{\rm s}$ is the scale radius. The Sersic profile is given by

\begin{equation} \tag{2.9}\label{29}
	\Sigma_{\rm \text{Sersic}}(R) = \Sigma_{\rm eff} \exp\left[ - b_n \left\{ (R/R_{\rm eff})^{1/n_{\rm \text{Sersic}}} - 1 \right\} \right]
\end{equation}

(Sersic [1968](http://adsabs.harvard.edu/abs/1968adga.book.....S)). Here, the normalizing factor $b_n$ ensures that half of the total projected mass is contained within the effective radius $R_{\rm eff}$.  We only need to compute as many $f(\sigma)$ values as the number of Gaussian components. We can appropriately choose this number to achieve the required precision for approximating the original function within a given range of $R$. In this example, we set $r_{\rm s}=5R_{\rm eff}$ and assume 1 per cent Poisson noise at $R = R_{\rm eff}$. Then, we can approximate both the projected NFW profile and the Sersic function within the noise level with only 15 Gaussian components in the range $0.1R_{\rm eff} \leq R \leq 10R_{\rm eff}$. The standard deviations $\sigma_n$ of the 15 Gaussians are logarithmically spaced between $0.005r_{\rm s}$ and $50r_{\rm s}$ for the NFW profile, and between $0.02R_{\rm eff}$ and $15R_{\rm eff}$ for the Sersic profile. Thus using the integral transform method, we can decompose a function into concentric Gaussian components within any required precision by appropriately choosing the component number $N$.

```python
interact(plot_gauss_decompose,
         n_sersic=FloatSlider(min=1., max=6., step=0.5, value=1., continuous_update=False),
         N_sigma=IntSlider(min=10, max=20, value=15, continuous_update=False),
         sigma_start=FloatSlider(min=0.01, max=0.05, step=0.005, value=0.02, continuous_update=False),
         sigma_end=FloatSlider(min=10, max=20, step=1, value=15, continuous_update=False));

#plot_gauss_decompose(n_sersic=1)
```

>**Figure 3.** Decomposing the Sersic profile and the projected NFW profile into concentric Gaussian components using the integral transform with a Gaussian kernel. The solid blue lines correspond to the Sersic profiles. The red, dashed lines correspond to the two-dimensional projected NFW profile. Both profiles are normalized to have $\Sigma (R_{\rm eff}) = 1$. **Left:** the inverse transform $f(\sigma)$ of the Sersic profiles and the projected NFW profile. Here, we choose the NFW scale radius $r_{\rm s} = 5R_{\rm eff}$. To decompose a function into 15 Gaussian components, we only need to compute $f(\sigma)$ at 15 points. These points are marked along the top border as blue ticks for the Sersic profiles and as red ticks for the NFW profile. **Center:** recovering the original profile as $\Sigma_{\rm approx}(R)$ using the forward transform by combining the 15 Gaussian components. We do not plot the true form of $\Sigma_{\rm \text{Sersic}}(R)$ or $\Sigma_{\rm \text{NFW}}(R)$, because they are visually almost indistinguishable from $\Sigma_{\rm approx}(R)$. **Right:** noise-normalized difference between the recovered profile $\Sigma_{\rm approx}(R)$ and the true form of $\Sigma_{\rm \text{Sersic}}(R)$ or $\Sigma_{\rm \text{NFW}}(R)$. We assume 1 per cent Poisson noise at $R = R_{\rm eff}$ to obtain the noise level for normalizing the residual. Our method approximates the NFW profile and the Sersic profile as a sum of 15 Gaussians within the noise level for $0.1R_{\rm eff} \leq R \leq 10R_{\rm eff}$.

>*(Interactive options are set only for the Sersic profile.)*

#### 2.1.3 The integral transform method is more efficient than the MGE method.

We compare our method to decompose a one-dimensional function into concentric Gaussian components with the MGE method (Fig. [4](#scrollTo=a2aIUCNQnzqG)). We use both methods to decompose the Sersic function into 15 Gaussian components. The MGE method approximates the Sersic function within the noise level up to $\sim 6R_{\rm eff}$, whereas our method approximates the Sersic function within the noise level up to 10$R_{\rm eff}$ with the same number of components. Albeit, we can increase the number of Gaussian components in the MGE method to reach the desired precision. In our method, the precision of the decomposition can be affected by both $P$ in equation ([2.5](#mjx-eqn-25)) and the number of Gaussians $N$ in equation ([2.7](#mjx-eqn-27)). However, if $P$ is appropriately chosen so that $10^{-0.6P}$ is sufficiently (e.g., by a factor of $\sim 10^{-2}$–$10^{-4}$) smaller than the required precision, then the precision predominantly depends on $N$. For lensing and kinematic analyses, increasing the number of Gaussian components $N$ introduces more computational burden than increasing $P$. Therefore, it is advisable to first choose a sufficiently large $P$ and then adjust the number of Gaussians to achieve the required precision. Note that the real power of our method is in its efficiency. A `python` implementation of our method is $\sim 10^3$ times faster than the MGE method to decompose a one-dimensional function into concentric Gaussian components with similar or better precision.

```python
# runtime of the integral transform method

%%timeit -n100 -r5

_ = decompose(sersic_func, N_sigma=15)
```

    100 loops, best of 5: 104 µs per loop

```python
from mgefit.mge_fit_1d import mge_fit_1d

R = np.logspace(-1, 1, 200)
sersic_R = sersic_func(R, n_sersic=1)
```

```python
# runtime of the MGE method

%%timeit -n10 -r5

_ = mge_fit_1d(R, sersic_R, ngauss=15, plot=False, quiet=True)
```

    10 loops, best of 5: 716 ms per loop

```python
interact(plot_mge_comparison,
         n_sersic=FloatSlider(min=1, max=6, step=0.5, value=1, continuous_update=False),
         N_sigma=IntSlider(min=10, max=20, value=15, continuous_update=False));

#plot_mge_comparison()
```

>**Figure 4.** Comparison between our method and the multi-Gaussian expansion (MGE) method from Cappellari ([2002](http://adsabs.harvard.edu/abs/2002MNRAS.333..400C)) to decompose a one-dimensional Sersic function into concentric Gaussian components. **Left:** the Sersic function (solid, blue line) approximated with 15 Gaussian components using our Gaussian decomposition method. The dotted, lighter-blue lines show the individual Gaussian components. Some of the Gaussian components are out of the figure range. **Center:** same as the left figure but using the MGE method with 15 Gaussian components. The dashed, grey line shows the Sersic function approximated by MGE and the dot-dashed, lighter-grey lines show individual Gaussian components. **Right:** comparison of the noise-normalized residual for the two methods. We assume 1 per cent Poisson noise at effective radius $R_{\rm eff}$ to obtain the noise level for normalizing the residual. The MGE method approximates the Sersic function within the noise level up to $\sim 6 R_{\rm eff}$, whereas our method approximates the Sersic function within the noise level up to $10R_{\rm eff}$. More importantly, our method is $\sim 10^3$ times faster than the MGE method to decompose a one-dimensional function into concentric Gaussian components.

### 2.2 Decomposing a two-dimensional elliptical profile with the one-dimensional transform

So far, we have discussed the one-dimensional case of the integral transform; now we show that the one-dimensional transform is sufficient to decompose a two-dimensional elliptical profile. We can extend the one-dimensional integral transform from equation ([2.3](#mjx-eqn-23)) into a two-dimensional integral transform for a function $f(\sigma_1, \sigma_2)$ as

\begin{equation} \tag{2.10}\label{210}
	F(x, y) =  \int_0^\infty \mathrm{d} \sigma_1 \int_0^\infty \mathrm{d} \sigma_2 \  \frac{f(\sigma_1, \sigma_2)}{2 π \sigma_1 \sigma_2}\ \exp\left( -\frac{x^2}{2\sigma_1^2} - \frac{y^2}{2\sigma_2^2} \right).
\end{equation}

If $F(x,y)$ is elliptically symmetric, then we can express it as $F(R)$ in terms of the _elliptical radius_ $R=\sqrt{q^2x^2+y^2}$ and axis ratio $q$. Then, we can write

\begin{equation} \tag{2.11}\label{211}
		F(x,y) = F\left(R(q)\right) = \int_0^{\infty} F\left(R(\varrho)\right)\ \delta(\varrho-q)\ \mathrm{d} \varrho \\
		= \int_0^{\infty} \mathrm{d} \varrho \ \delta(\varrho-q \mathcal{}) \int_0^{\infty} \mathrm{d} \sigma\  \frac{f_y(\sigma)}{\sqrt{2π}\sigma}\ \exp\left(-\frac{R(\varrho)^2}{2\sigma^2} \right),
\end{equation}

where $f_y(\sigma)$ is the inverse transform of $F(0,y)$. If we make the change of variables $\sigma_1 = \sigma/\varrho$, $\sigma_2=\sigma$, this integral becomes

\begin{equation} \tag{2.12}\label{212}
		F(x,y) = \frac{1}{2\pi} \int_0^{\infty} \mathrm{d} \sigma_1 \int_0^{\infty} \mathrm{d} \sigma_2 \frac{\sqrt{2π}q \ \delta(\sigma_2/\sigma_1-q)\ f_y(\sigma_2)}{\sigma_1 \sigma_2} \\
		\qquad\qquad\qquad\qquad \times \exp \left(-\frac{x^2}{2\sigma_1^2}-\frac{y^2}{2\sigma_2^2} \right).
\end{equation}

Because of the uniqueness property (Theorem [A.7](#scrollTo=ls5kM1XABtfj)), comparing equations ($\ref{210}$) and ($\ref{212}$) we can write

\begin{equation} \tag{2.13}\label{213}
	f(\sigma_1, \sigma_2) = 	\sqrt{2π}q \ \delta\left(\frac{\sigma_2}{\sigma_1}-q \right)f_y(\sigma_2).
\end{equation}

Therefore, for an elliptically symmetric function, it is sufficient to numerically compute the one-dimensional inverse transform $f_y(\sigma_2)$ along the $y$-axis. As a result, we can express a two-dimensional elliptical function as a sum of elliptical Gaussian components as

\begin{equation} \tag{2.14}\label{214}
	F(x, y) \approx	\sum_{n=1}^{N} A_n \exp \left( -\frac{q^2 x^2 + y^2}{2\sigma_n^2} \right).
\end{equation}

By now, we have shown that the integral transform method meets all of our three requirements for decomposing a function into concentric Gaussian components:

- **generality:** the method applies to most mass and light profiles of practical use,
- **precision:** the method achieves any required precision over a given range by appropriately choosing the number of Gaussian components, and
- **efficiency:** the method runs approximately $\sim 10^3$ times faster than the previously available method.

With these three requirements met, our lensing analysis method has cleared the first hurdle to be feasible in practice. In Section [4.2](#scrollTo=slP9xat7i8YZ), we show that we can efficiently compute the gradient and the Hessian of the deflection potential for an elliptical Gaussian surface-density. With that, our method also clears the final hurdle to be efficient. Next in Section [3](#scrollTo=alI-uL1t9HSR), we summarize the kinematic analysis for the Gaussian components from Cappellari ([2008](http://adsabs.harvard.edu/abs/2008MNRAS.390...71C)). Then in Section [4](#scrollTo=TzkbBKWhcIbn), we describe the lensing analysis for the Gaussian components and complete the unification of lensing and kinematic descriptions.

## 3 Kinematics of Gaussian components

Cappellari ([2008](http://adsabs.harvard.edu/abs/2008MNRAS.390...71C)) presents the Jeans anisotropic modelling of kinematics for a mass profile decomposed into concentric Gaussian components. We summarize the analysis here to complete our unified framework. The kinematic observable is the luminosity-weighted, line-of-sight velocity dispersion. The velocity dispersion can be an integrated measurement within a single aperture or it can be spatially resolved on the plane of the sky. To compute this quantity for a combination of mass and light profiles, we need to solve the Jeans equations. We can decompose the surface mass-density profile into concentric Gaussian components as

\begin{equation} \tag{3.1}\label{31}
	\Sigma (x, y) \approx \sum_{j=1}^J	{\Sigma_{0}}_{j} \exp \left( - \frac{q_j^2 x^2 + y^2}{2 \sigma_j^2} \right),
\end{equation}

and decompose the surface brightness profile into concentric Gaussian components as

\begin{equation} \tag{3.2}\label{32}
	I (x, y) \approx \sum_{k=1}^K	{I_{0}}_{k} \exp \left( - \frac{q_k^2 x^2 + y^2}{2 \sigma_k^2} \right).
\end{equation}

Here, we use different subscript letters to make the context of the Gaussian decomposition clear: we use the subscript $j$ for a component of the mass profile and the subscript $k$ for a component of the light profile. We have also allowed different ellipticity for each Gaussian component represented by $q_j$ or $q_k$. This is the most general case, for example, when structures with different ellipticities constitute the total mass or light distribution. We first need to deproject these two-dimensional profiles into three-dimension as the kinematic quantities depend on the three-dimensional distributions of mass and light. We assume axisymmetry or spherical symmetry for the deprojected three-dimensional structure to circumvent the infinite degeneracy in deprojection. First, we provide the kinematic analysis for the axisymmetric case in Section [3.1](#scrollTo=1rgbrAIw-HmT); then we do the same for the simpler case of spherical symmetry in Section [3.2](#scrollTo=orW-XSQB_wa0).

### 3.1 Axisymmetric case

For an axisymmetric system, the cylindrical coordinates $(R^{\prime}, z^{\prime}, \phi^{\prime})$ are the most suitable to express the Jeans equations. We use the prime symbol to denote the coordinates in the system's _symmetry-frame_, where the $z^{\prime}$-axis aligns with the axis of symmetry. We assign $(x, y, z)$ coordinates to the _sky frame_, where the $z$-axis aligns with the line of sight and the $x$-axis aligns with the projected major axis. If the galaxy is inclined by an angle $\iota$, then the $(x^{\prime}, y^{\prime}, z^{\prime})$ coordinates in the symmetry frame relate to the the $(x, y, z)$ coordinates in the sky frame as

\begin{equation} \tag{3.3}\label{33}
	\begin{pmatrix} x^{\prime} \\ y^{\prime} \\ z^{\prime}	\end{pmatrix}
	=
	\begin{pmatrix}
		1 &0 &0 \\ 0 &\cos \iota  &-\sin \iota \\ 0 &\sin \iota & \cos \iota
	\end{pmatrix}
	\begin{pmatrix} x \\ y \\ z	\end{pmatrix} .
\end{equation}

In the symmetry frame, equation ([3.1](#mjx-eqn-31)) deprojects into the mass density profile $\rho$ as

\begin{equation} \tag{3.4}\label{34}
	\rho(R^{\prime}, z^{\prime}) = \sum_{j=1}^J 	\frac{{q_j^{\prime}}^2 {\Sigma_{0}}_{j}}{\sqrt{2π} \sigma_j q_j} \exp \left( - \frac{{q_j^{\prime}}^2 {R^{\prime}}^2 + {z^{\prime}}^2}{2 \sigma_j^2} \right),
\end{equation}

and equation ([3.2](#mjx-eqn-32)) deprojects into the light density profile $l$ as

\begin{equation} \tag{3.5}\label{35}
	l (R^{\prime}, z^{\prime}) = \sum_{k=1}^K	\frac{{q_k^{\prime}}^2 {I_0}_{k}}{\sqrt{2π} \sigma_k q_k} \exp \left( - \frac{{q_k^{\prime}}^2 {R^{\prime}}^2 + {z^{\prime}}^2}{2 \sigma_k^2} \right).
\end{equation}

Here, the intrinsic axis ratio $q^{\prime}$ relates to the projected axis ratio $q$ as

\begin{equation} \tag{3.6}\label{36}
	q^{\prime} = \frac{\sqrt{q^2 - \cos^2 \iota}}{\sin \iota}.
\end{equation}

We can first solve the Jeans equations for these mass and light density profiles in the symmetry frame to get the intrinsic velocity dispersions and then integrate along the line of sight to obtain the line-of-sight velocity dispersion.

The Jeans equations to solve for an axisymmetric system are

\begin{equation} \tag{3.7}\label{37}
	\frac{b\ l\ \overline{v_{z^{\prime}}^2} - l\ \overline{v_{\phi^{\prime}}^2}}{R^{\prime}} + \frac{\partial \left( b\ l\ \overline{v_{z^{\prime}}^2} \right)}{\partial R^{\prime}} = - l \frac{\partial \Phi}{\partial R^{\prime}}, \\
	\frac{\partial \left( l\ \overline{v_{z^{\prime}}^2} \right)}{\partial z^{\prime}} = - l \frac{\partial \Phi}{\partial z^{\prime}}.
\end{equation}

Here,  the gravitational potential $\Phi$ relates to the three-dimensional mass density $\rho$ by $\nabla^2 \Phi = \rho$, and $b$ represents the anisotropy as in $\overline{v_{R^{\prime}}^2} = b \overline{v_{z^{\prime}}^2}$. We can let $b_k$ for each luminous Gaussian component have different values to approximate the luminosity-weighted anisotropy parameter as

\begin{equation} \tag{3.8}\label{38}
	\beta_{z^{\prime}} (R^{\prime}, z^{\prime}) \equiv 1 - \frac{\overline{v_{z^{\prime}}^2}}{\overline{v_{R^{\prime}}^2}} \approx 1 - \frac{\sum_k l_k}{\sum_k b_k l_k}
\end{equation}

(Binney & Mamon [1982](http://adsabs.harvard.edu/abs/1982MNRAS.200..361B), Cappllari [2008](http://adsabs.harvard.edu/abs/2008MNRAS.390...71C)). The line-of-sight second velocity moment for total mass profile obtained from solving the Jeans equations is given by

\begin{equation} \tag{3.9}\label{39}
	\overline{v_{\rm los}^2}	 (x, y) = 2 \sqrt{π} G \int_0^1 \sum_{j=1}^J \sum_{k=1}^K \frac{{q_j^{\prime}}^3 {q_k^{\prime}}^2 {\Sigma_0}_j {I_0}_k u^2}{\sigma_j q_j \sigma_k q_k} \\
	\times \frac{\sigma_k^2\left( \cos^2 \iota + b_k \sin^2 \iota \right) + \mathcal{D}x^2 \sin^2 \iota}{\left(1-\mathcal{C}u^2 \right)\sqrt{\left(\mathcal{A} + \mathcal{B} \cos^2 \iota \right) \left[1 - \left(1-{q_j^{\prime}}^2\right)u^2 \right]}} \\
	 \times \exp \left(-\mathcal{A} \left[x^2 + \frac{(\mathcal{A}+\mathcal{B})y^2}{\mathcal{A}+\mathcal{B}\cos^2 \iota} \right] \right) \mathrm{d} u,
\end{equation}

where $G$ is the gravitational constant and

\begin{equation} \tag{3.10}\label{310}
		\mathcal{A} = \frac{1}{2} \left( \frac{u^2 {q_j^{\prime}}^2}{\sigma_j^2} + \frac{{q_k^{\prime}}^2}{\sigma_k^2} \right), \\
		\mathcal{B} = \frac{1}{2}\left\{ \frac{1-{q_k^{\prime}}^2}{\sigma_k^2} + \frac{{q_j^{\prime}}^2\left(1-{q_j^{\prime}}^2\right)u^4}{\sigma_j^2 \left[ 1- \left(1-{q_j^{\prime}}^2\right) u^2\right] } \right\},\\
		\mathcal{C} = 1 - {q_j^{\prime}}^2 - \frac{{q_j^{\prime}}^2\sigma_k^2}{\sigma_j^2},\\
		\mathcal{D} = 1 - b_k{q_k^{\prime}}^2 - \left[(1-b_k)\mathcal{C} + (1-{q_j^{\prime}}^2)b_k \right] u^2
\end{equation}

(Cappellari [2008](http://adsabs.harvard.edu/abs/2008MNRAS.390...71C)). The line-of-sight velocity dispersion $\sigma_{\rm los}$ relates to the second velocity moment by $\overline{v^2_{\rm los}} = v_{\rm mean}^2 + \sigma^2_{\rm los}$, where $v_{\rm mean}$ is the stellar mean velocity.

### 3.2 Spherical case

If we assume the system is spherically symmetric, then the spherical coordinates $(r, \phi, \theta)$ are the most suitable to express the Jeans equations. In this coordinate system, the mass density profile deprojected from equation ([3.1](#mjx-eqn-31)) takes the form

\begin{equation} \tag{3.11}\label{311}
	\rho(r) = \sum_{j=1}^J \frac{{\Sigma_{0}}_j }{\sqrt{2 π} \sigma_j q_j} \exp \left( -\frac{r^2}{2 \sigma_j^2}\right),
\end{equation}

and the light density profile deprojected from equation ([3.2](#mjx-eqn-32)) turns into

\begin{equation} \tag{3.12}\label{312}
	l(r) = \sum_{k=1}^K \frac{{I_{0}}_k}{\sqrt{2 π} \sigma_k q_k} \exp \left( -\frac{r^2}{2 \sigma_k^2}\right).
\end{equation}

The projected axis ratio $q$ shows up in these equations to keep the total mass and luminosity conserved. We can express the three-dimensional enclosed mass for this density profile as

\begin{equation} \tag{3.13}\label{313}
	M(r) = \sum_{j=1}^J \frac{2π \sigma_j^2 {\Sigma_0}_j}{q_j} \left[ \mathrm{erf} \left( \frac{r}{\sqrt{2}\sigma_j} \right) - \sqrt{\frac{2}{π}} \frac{r}{\sigma_j} \exp \left( - \frac{r^2}{2 \sigma_j^2} \right) \right],
\end{equation}

where $\mathrm{erf}\ (x)$ is the error function. The spherical Jeans equation is

\begin{equation} \tag{3.14}\label{314}
	\frac{\mathrm{d} \left( l\ \overline{v_r^2}\right)}{\mathrm{d} r} + \frac{2 \beta\ l\  \overline{v_r^2}}{r} = - l \frac{\mathrm{d} \Phi}{\mathrm{d} r},
\end{equation}

where $\beta(r)$ is the anisotropy parameter given by

\begin{equation} \tag{3.15}\label{315}
	\beta(r) = 1 - {\overline{v_{\theta}^2}}/{\overline{v_{r}^2}}.
\end{equation}

Spherical symmetry imposes that  $\overline{v_{\theta}^2} = \overline{v_{\phi}^2}$. By solving the Jeans equation for the spherically symmetric case, we can obtain the line-of-sight second velocity moment as

\begin{equation}  \tag{3.16}\label{316}
	\overline{v_{\rm los}^2} (x, y) =  \frac{2G}{I(x, y)} \int_{\sqrt{x^2+y^2}}^\infty \mathcal{K}_\beta \left(\frac{r}{\sqrt{x^2+y^2}} \right)\ l(r)\ M(r)\ \frac{\mathrm{d} r}{r}
\end{equation}

(Mamon & Lokas [2005](http://adsabs.harvard.edu/abs/2005MNRAS.363..705M)). Here, the function $\mathcal{K}_\beta (\upsilon)$ depends on the form of the anisotropy parameter $\beta(r)$. For the isotropic case with $\beta = 0$, the function $\mathcal{K}_\beta$ shapes into

\begin{equation} \tag{3.17}\label{317}
	\mathcal{K}_{\beta} (\upsilon) = \sqrt{1 - \frac{1}{\upsilon^2}}.
\end{equation}

For the Osipkov-Merritt parameterization $\beta(r) = r^2/(r^2 + r_{\rm ani}^2)$, where $r_{\rm ani}$ is a scale radius, the function $\mathcal{K}_{\beta}$ takes the form

\begin{equation} \tag{3.18}\label{318}
	\mathcal{K}_{\beta} \left( \upsilon \right) = \frac{\upsilon_{\rm ani}^2 + 1/2}{(\upsilon_{\rm ani}+1)^{3/2}} \left( \frac{\upsilon^2 + \upsilon^2_{\rm ani}}{\upsilon} \right) \tan^{-1} \left(\sqrt{\frac{\upsilon^2 - 1}{\upsilon_{\rm ani}^2 + 1}}\right) \\
	 \qquad\qquad\qquad\qquad - \frac{1/2}{\upsilon_{\rm ani}^2 + 1} \sqrt{1 - \frac{1}{\upsilon^2}}
\end{equation}

with $\upsilon_{\rm ani} = r_{\rm ani}/\sqrt{x^2 + y^2}$  (Osipkov [1979](http://adsabs.harvard.edu/abs/1979PAZh....5...77O), Merritt [1985a](http://adsabs.harvard.edu/abs/1985MNRAS.214P..25M), [b](http://adsabs.harvard.edu/abs/1985AJ.....90.1027M)). See equation (A16) of Mamon & Lokas ([2005](http://adsabs.harvard.edu/abs/2005MNRAS.363..705M)) for the form of $\mathcal{K}_{\beta}$ corresponding to other parameterizations of $\beta(r)$. When assuming spherical symmetry is sufficient, we can use equation ([3.16](#mjx-eqn-316)) to compute the line-of-sight velocity dispersion in a much simpler way than the axisymmetric case \[cf. equation ([3.9](#mjx-eqn-3.9))\].

The kinematic description of an elliptical mass distribution by decomposing it into concentric Gaussian components is thus well developed in the literature. In the next section, we unify the lensing description with the kinematic description under the same framework.

## 4 Lensing by Gaussian components

In this section, we present the lensing analysis for an elliptical surface-density profile decomposed into concentric Gaussian components. In Section 2, we introduced an integral transform that efficiently decomposes an elliptical surface-density profile into Gaussian components as

\begin{equation} \tag{4.1}\label{41}
	\Sigma (x, y) \approx \sum_{j=1}^J	{\Sigma_{0}}_{j} \exp \left( - \frac{q_j^2 x^2 + y^2}{2 \sigma_j^2} \right)	.
\end{equation}

We can compute a lensing quantity for each individual Gaussian component, and then linearly add the contributions from all the components to obtain the total lensing quantity. For example, if $\mathbf{α}_j(x, y)$ is the deflection at position $(x, y)$ for the $j$-th Gaussian component, then the total deflection is simply given by $\mathbf{α} (x, y) = \sum_{j=1}^J \mathbf{α}_j (x, y)$. Therefore, it is sufficient to analyze the lensing properties of one elliptical Gaussian component. We use the complex formulation of lensing to solve the deflection integral for an elliptical Gaussian surface-density profile. Below, we first lay out the complex formalism of lensing in Section [4.1](#scrollTo=wACcjB-yg4s3); then we study the lensing properties of an elliptical Gaussian surface-density profile in Section [4.2](#scrollTo=slP9xat7i8YZ).

### 4.1 Complex formulation of lensing

The strong lensing effect is usually described using the vector formulation on the two-dimensional image plane. We first define the lensing quantities in the familiar vector formulation, then we translate them to the complex formulation. The convergence $\kappa$ is a dimensionless surface-density defined as $\kappa \equiv \Sigma/\Sigma_{\rm crit}$, where the critical density $\Sigma_{\rm crit}$ is given by

\begin{equation} \tag{4.2}\label{42}
	\Sigma_{\rm crit} = \frac{c^2 D_{\rm s}}{4 π G D_{\rm ds} D_{\rm d}}.
\end{equation}

Here,  $c$ is the speed of light. The three angular diameter distances are $D_{\rm d}$: between the observer and the deflector, $D_{\rm s}$: between the observer and the source, and $D_{\rm ds}$: between the deflector and the source. The convergence $\kappa$ relates to the vector deflection angle ${\mathbf{α}}$ as $\kappa = \nabla \cdot \mathbf{α} / 2$. The deflection angle $\mathbf{α}$ is the gradient of the deflection potential as $\mathbf{α} = \nabla \psi$, thus the convergence $\kappa$ relates to the deflection potential $\psi$ as $\kappa = \nabla^2 \psi / 2$. The Hessian matrix of the deflection potential is

\begin{equation} \tag{4.3}\label{43}
	\mathsf{H} = \begin{pmatrix}
 			\dfrac{\partial^2 \psi}{\partial^2 x} &\dfrac{\partial^2 \psi}{\partial x \partial y} \\
 			\dfrac{\partial^2 \psi}{\partial x \partial y} &\dfrac{\partial^2 \psi}{\partial^2 y}
 		\end{pmatrix}.
\end{equation}

The convergence $\kappa$, the shear parameters $(\gamma_1, \gamma_2)$, and the magnification $\mu$ relate to the Hessian, since we can express them as

\begin{equation} \tag{4.4}\label{44}
		\kappa = \frac{1}{2}\left(\frac{\partial^2 \psi}{\partial^2 x} + \frac{\partial^2 \psi}{\partial^2 y} \right), \\
		\gamma_1 = \frac{1}{2} \left(\frac{\partial^2 \psi}{\partial^2 x} - \frac{\partial^2 \psi}{\partial^2 y} \right), \\
		\gamma_2 = \frac{\partial^2 \psi}{\partial x \partial y}, \\
    \mu = \frac{1}{\det\ ({\mathsf{I}} - {\mathsf{H}})},
\end{equation}

where $\mathsf{I}$ is the identity matrix. Thus, if we start with a convergence $\kappa$ and derive the deflection $\mathbf{α}$ and the shear parameters $(\gamma_1, \gamma_2)$, then we know the gradient and the Hessian of the deflection potential.

Now we reformulate the lensing quantities on the complex plane. Following Bourassa et al. ([1973](http://adsabs.harvard.edu/abs/1973ApJ...185..747B)), we can express the deflection vector $\mathbf{α}$ as a complex quantity

\begin{equation} \tag{4.5}\label{45}
	\alpha(z) \equiv \alpha_x + \mathrm{i} \alpha_y,
\end{equation}

where the complex quantity $z = x + \mathrm{i}y$ corresponds to the position vector $\mathbf{r} = (x, y)$. We can define a complex deflection potential $\psi(z)$ with its real part equal to the usual deflection potential (Schramm [1990](http://adsabs.harvard.edu/abs/1990A%26A...231...19S)). Then, the complex deflection angle is the Wirtinger derivative of the deflection potential as

\begin{equation} \tag{4.6}\label{46}
	\alpha (z) = \frac{\partial \psi}{\partial x} + \mathrm{i} \frac{\partial \psi}{\partial y} = 2 \frac{\partial \psi}{\partial z^*}.
\end{equation}

We can express the convergence $\kappa$ as

\begin{equation} \tag{4.7}\label{47}
	\kappa = \frac{\partial \alpha^*}{\partial z^*}.
\end{equation}

Furthermore, the complex shear $\gamma \equiv \gamma_1 + \mathrm{i} \gamma_2$ satisfies the relation

\begin{equation} \tag{4.8}\label{48}
	\gamma^* = \frac{\partial \alpha^*}{\partial z}.
\end{equation}

Using this complex formulation, we analyze the lensing properties of an elliptical Gaussian convergence next in Section [4.2](#scrollTo=slP9xat7i8YZ).

### 4.2 Lensing by elliptical Gaussian convergence

We derive the deflection angle and shear for the elliptical Gaussian convergence

\begin{equation} \tag{4.9}\label{49}
	\kappa(R) = \kappa_0 \exp \left( -\frac{R^2}{2 \sigma^2}\right),
\end{equation}

where $R=\sqrt{q^2x^2 + y^2}$ is the elliptical radius. Using the complex formulation, the deflection angle for the elliptical convergence can be obtained from

\begin{equation} \tag{4.10}\label{410}
	\alpha^*(z) = 2\ \rm{sgn}(z) \ \int_0^{R(z)} \mathrm{d} \zeta \frac{\zeta \kappa(\zeta)}{\sqrt{q^2 z^2 - (1-q^2) \zeta^2}} \\
 = \frac{2 \kappa_0 \ }{ q z} \int_0^{R(z)} \mathrm{d} \zeta \frac{\zeta \exp (-\zeta^2/2\sigma^2)}{\sqrt{1 - {(1-q^2)\zeta^2}/{q^2 z^2}}},
\end{equation}

where $\textrm{sgn}(z) \equiv \sqrt{z^2}/z$ is the complex sign function, and $R(z) = \sqrt{q^2x^2 + y^2}$ is the semi-minor axis length for the ellipse with axis-ratio $q$ that goes through the point $z=x+iy$ (Bourassa & Kantowski [1975](http://adsabs.harvard.edu/abs/1975ApJ...195...13B), Bray [1984](http://adsabs.harvard.edu/abs/1984MNRAS.208..511B)). With changes of variables $s=1/2\sigma^2$, $t = (1-q^2)/q^2 z^2$, $\tau = \sqrt{1 - t\zeta^2}$, we can express equation ([4.10](#mjx-eqn-410)) as

\begin{equation} \tag{4.11}\label{411}
\alpha^*(z) =  \frac{2 \kappa_0 \ \mathrm{e}^{-s/t}}{q z t} \int_{\sqrt{1-tR(z)^2}}^{1} \mathrm{d} \tau \exp \left( \frac{s}{t} \tau^2 \right) \\
  = \frac{2\kappa_0 \ \mathrm{e}^{-s/t}}{ q z t} \left[\frac{1}{2}  \sqrt{\frac{π t}{s}}\ \mathrm{erfi} \left( \sqrt{\frac{s}{t}} \tau \right) \right]_{\sqrt{1-t(q^2x^2 + y^2)}}^{1} \\
=  \kappa_0 \sigma \sqrt{\frac{2 π}{1-q^2}} \exp \left( -\frac{q^2 z^2}{2\sigma^2(1 - q^2)}\right) \left[ \mathrm{erfi} \left( \frac{q z}{\sigma \sqrt{2(1-q^2)}} \right) \right. \\
		 \qquad \qquad \qquad \qquad - \left. \mathrm{erfi} \left( \frac{ q^2 x + i y}{\sigma \sqrt{2(1-q^2)}} \right) \right] \\
		= \kappa_0 \sigma \sqrt{\frac{2π}{1-q^2}}\ \varsigma(z;q),
\end{equation}

where $\mathrm{erfi} \ (z) \equiv -\mathrm{i}\ \mathrm{erf} \ (\mathrm{i}z)$ and we have defined the function

\begin{equation} \tag{4.12}\label{412}
	\varsigma (z; q) \equiv \exp \left( -\frac{q^2 z^2}{2\sigma^2(1 - q^2)}\right) \left[ \mathrm{erfi} \left( \frac{q z}{\sigma \sqrt{2(1-q^2)}} \right) \right. \\
		\qquad \qquad \qquad \qquad - \left. \mathrm{erfi} \left( \frac{ q^2 x + \mathrm{i} y}{\sigma \sqrt{2(1-q^2)}} \right) \right].
\end{equation}

We obtain the complex conjugate of the complex shear from equation ([4.8](#mjx-eqn-48)) as

\begin{equation} \tag{4.13}\label{413}
	\gamma^*(z) = -\frac{\kappa_0} {1-q^2} \left[ (1+q^2) \exp \left( - \frac{q^2x^2+y^2} {2\sigma^2} \right) \right.  - 2q \\
	\quad\ + \frac{\sqrt{2 π} q^2 z}{\sigma\sqrt{1-q^2}} \exp \left(-\frac{q^2 z^2}{2\sigma^2(1-q^2)}\right) \left\{ \mathrm{erfi} \left( \frac{qz} {\sigma\sqrt{2(1-q^2)}}\right) \right. \\
	\quad \left. \left. - \mathrm{erfi} \left( \frac{q^2x + \mathrm{i} y} {\sigma\sqrt{2(1-q^2)}}\right) \right\} \right] \\
	= -\frac{1}{1-q^2} \left[ (1+q^2) \kappa(x, y) - 2q\kappa_0  + \frac{\sqrt{2π} q^2\kappa_0 z}{\sigma \sqrt{1-q^2}}\ \varsigma(z;q) \right] .
\end{equation}

Both the deflection angle and the shear contain the function $\varsigma(z;q)$. This function relates to the Faddeeva function $w_{\rm F}(z)$. First, we write the function $\varsigma(z;q)$ as

\begin{equation} \tag{4.14}\label{414}
	\varsigma(z;q) = \varpi \left(\frac{q z}{\sigma \sqrt{2(1-q^2)}}; 1\right) - \varpi \left(\frac{q z}{\sigma \sqrt{2(1-q^2)}}; q \right)
\end{equation}

where $\varpi (z;q) = \exp\left(-z^2 \right)\ \rm{erfi} \left(qx + iy/q \right)$. We can express $\varpi(z;q)$ using the Faddeeva function $w_{\rm F}(z)$ as

\begin{equation} \tag{4.15}\label{415}
	\varpi (z;q) = 	\mathrm{e}^{-x^2 - 2\mathrm{i}xy} \mathrm{e}^{y^2} - \mathrm{i} \exp\left[ -x^2 (1-q^2) - y^2 (1/q^2 -1) \right] \\
	 \qquad \times w_{\rm F}(q x + \mathrm{i} y/q).
\end{equation}

Thus, we can compute the deflection angle and the shear using the Faddeeva function (Fig. [5](#scrollTo=MtnsSzoDteie)). Faddeeva function is a well-studied special function for its various applications in physics, for example, in radiative transfer and in plasma physics (Armstrong [1967](http://adsabs.harvard.edu/abs/1967JQSRT...7...61A), Jimenez-Dominguez et al. [1989](http://adsabs.harvard.edu/abs/1989NIMPA.278..625J)). We can readily compute $w_{\rm F}(z)$ in `python` using the `scipy.special.wofz` function. For some other popular programming languages, code-packages to compute this function are available at the web-address [http://ab-initio.mit.edu/Faddeeva](http://ab-initio.mit.edu/Faddeeva). In this paper, we use the algorithm outlined by Zaghloul ([2017](http://doi.acm.org/10.1145/3119904)) to compute $w_{\rm F}(z)$ with relative error less than $4\times10^{-5}$ over the whole complex plane. We state this algorithm in Appendix [B](#scrollTo=1LZhVuG8ft0R). A `python` implementation of this algorithm is approximately twice as fast as the function provided by `scipy`.  As a result, we can efficiently compute the gradient and Hessian of the deflection potential for an elliptical Gaussian convergence using equations ([4.11](#mjx-eqn-411)) and ([4.12](#mjx-eqn-412)).

```python
interact(plot_gaussian_lens,
         amplitude=FloatSlider(value=2., min=1., max=5, step=.5, continuous_update=False),
         σ=FloatSlider(value=1., min=.5, max=2, step=.1, continuous_update=False),
         q=FloatSlider(value=.5, min=.05, max=1., step=.05, continuous_update=False));

#plot_gaussian_lens()
```

>**Figure 5.** Lensing quantities for an elliptical Gaussian convergence profile. **Left:** convergence  (orange shade), deflection field (green arrows), isopotential contours (blue, dashed contours). The arrow directions are for the negative of the deflection angles and the lengths are shrunk by a factor of 4 for nicer visualization. **Right:** Critical curves (black lines) and corresponding caustics (pink lines). The solid-contour caustic corresponds to the solid-contour critical curve, similarly dot-dashed contours correspond to each other. We express the gradient and the Hessian of the deflection potential for an elliptical Gaussian convergence using the complex error function, as a result we can efficiently compute them.

Now, we turn our attention to computing the deflection potential. The deflection potential $\psi (z)$ is given by

\begin{equation}  \tag{4.16} \label{416}
	\psi(z) = \mathrm{Re} \left(\int^z_{0} \alpha^*(z^\prime)\ \mathrm{d} z^\prime \right),
\end{equation}

where we set $\psi(0) = 0$. Often times we are interested in the potential difference between two points $z_1$ and $z_2$ given by

\begin{equation} \tag{4.17}\label{417}
	\Delta \psi = \psi (z_2) - \psi (z_1) = \mathrm{Re} \left( \int_{z_1}^{z_2} \alpha^*(z^\prime)\ \mathrm{d} z^\prime \right).
\end{equation}

This integral is independent of the choice of a contour. We have to carry out this integral numerically. However, the number of times we need to compute it in most applications, e.g., for computing time delays, is much fewer than that for $\alpha(z)$. We can also numerically solve the Poisson equation $\nabla^2 \psi = 2\kappa$ using the Fourier transform of the deflection potential $\hat{\psi} \equiv \mathcal{F}[\psi]$ (van de Ven [2009](http://adsabs.harvard.edu/abs/2009MNRAS.398..607V)). This equation turns into $k^2 \hat{\psi} = 2\hat{\kappa}$ in the Fourier domain. The solution of the Poisson equation is then $\psi = \mathcal{F}^{-1}(2\hat{\kappa}/k^2)$. We can analytically compute the forward Fourier transform because the convergence has the Gaussian form. Then, we need to compute only the inverse transform numerically. Although obtaining the deflection potential necessitates a numerical integration or a numerical Fourier transform, we can keep the computational burden under control in most applications by computing this quantity only for a feasible number of models sampled from the lens model posterior.

## 5 Adding it all together: proof of concept

In this section, we demonstrate the feasibility of our method to model lenses. We first simulate synthetic data of a mock lens and then model the lens using our method. We use the publicly available lens-modelling software [`lenstronomy`](https://github.com/sibirrer/lenstronomy) to simulate the synthetic data and perform the model-fitting (Birrer & Amara [2018](http://adsabs.harvard.edu/abs/2018PDU....22..189B)). We added extra modules to `lenstronomy` to implement the lensing analysis presented in Section [4.2](#scrollTo=slP9xat7i8YZ).

For the mock strong lensing system, we adopt an elliptical NFW deflection potential for the dark component and an elliptical Chameleon convergence for the luminous component. We take realistic scale sizes and normalizations for these profiles. We choose the Chameleon profile for two reasons:

1. we can analytically simulate the data with ellipticity in the convergence, and
2. we know the Sersic-profile parameters that approximates the chosen Chameleon profile _a priori_, so we can check the fidelity of our method.

We parameterize the scaling of the NFW profile with two parameters: scale radius $r_{\rm s}$ and the deflection angle $\alpha_{\rm s}$ at $r_{\rm s}$. For a spherical NFW profile given by

\begin{equation} \tag{5.1}\label{51}
	\rho_{\rm NFW}(r) = \frac{\rho_{\rm s}}{(r/r_{\rm s})(1 + r/r_{\rm s})^2},
\end{equation}

the normalization $\rho_s$ relates to $\alpha_s$ as

\begin{equation} \tag{5.2}\label{52}
	\alpha_{\rm s} = \frac{4 \rho_{\rm s} r_{\rm s}^2}{D_{\rm d}\Sigma_{\rm crit}} \left( 1 - \ln 2 \right).
\end{equation}

(Meneghetti et al. [2003](http://adsabs.harvard.edu/abs/2003MNRAS.340..105M)). The elliptical Chameleon convergence is given by

\begin{equation} \tag{5.3}\label{53}
	\kappa_{\rm Chm} (x, y) = \frac{\kappa_0}{1 + q} \left[ \frac{1}{\sqrt{x^2+y^2/q^2 + 4w_{\rm c}^2/(1+q^2)}} \right. \\
	 \quad \quad \quad \quad \left. - \frac{1}{\sqrt{x^2+y^2/q^2 + 4w_{\rm t}^2/(1+q^2)}} \right]
\end{equation}

(Suyu et al. [2014](http://adsabs.harvard.edu/abs/2014ApJ...788L..35S)). We also add external shear to the mass profiles. Therefore, our fiducial lens mass profile has three components in total: elliptical NFW deflection potential, elliptical Chameleon convergence, and external shear.

We simulate data for this fiducial lens system with image quality similar to the _Hubble Space Telescope_ (_HST_) Wide-Field Camera 3 imaging in the F160W filter (see top-left panel of Fig. [6](#scrollTo=b4sIv2af2SHw)). We adopt 0.08 arcsec for the pixel size, 2197 s for exposure time, and a realistic point spread function (PSF) to achieve data quality similar to the lens sample presented by Shajib et al. ([2019](http://adsabs.harvard.edu/abs/2019MNRAS.483.5649S)).

We fit the synthetic data with a model composed of elliptical NFW deflection potential, elliptical Sersic convergence profile decomposed into concentric Gaussians, and external shear. Note that we take ellipticity in the deflection potential for the NFW profile due to a design restriction of `lenstronomy`. However, we can also extend an elliptical NFW convergence into Gaussians for lensing analysis in principle. For now, this limitation does not affect the point of this exercise to show that lensing analysis with Gaussian components is feasible. We take the PSF as known during model-fitting for simplicity. We also separately model the lens with the fiducial mass profiles for comparison. In both cases, the parameters for the light and the luminous mass profiles are joint except for the amplitudes. We fit the model to the data by using the MCMC method. For every sample point in the parameter space, we first decompose the elliptical Sersic profile into concentric Gaussian components using equations ([2.5](#mjx-eqn-25)) and ([2.7](#mjx-eqn-27)). Similar to the example in Section [2.1.2](#scrollTo=QSdpwV_JB_45), we take 15 Gaussian components with logarithmically spaced $\sigma$'s between $0.02R_{\rm eff}$ and $15 R_{\rm eff}$. These 15 Gaussian components approximate the Sersic function well within the noise level for $0.1R_{\rm eff} \leq R \leq 10R_{\rm eff}$ (Fig. [3](#scrollTo=W3KtTXnnVNAy)). We compute the gradient and the Hessian of the deflection potential for each Gaussian component. Finally, we add the contributions from all the individual components together to obtain these quantities for the total mass profile. These total quantities are used to compute the likelihood in the MCMC method for fitting the model to the data.

 Our method fits the synthetic data very well (see the 'Normalized Residuals' plot in Fig. [6](#scrollTo=b4sIv2af2SHw)). The fiducial Sersic profile parameters are also recovered with reasonable to high accuracies at the same time (Table [1](#scrollTo=0PjFuyYK2WiS)). The total runtime is only approximately three times longer than using the fiducial model with the Chameleon profile. This loss in efficiency is a reasonable tradeoff for generality. Thus, we have demonstrated a feasible implementation of our lensing analysis in lens modelling.

<center></center>

>**Figure 6.** Fitting synthetic lensing data with concentric Gaussian components of an elliptical Sersic profile for the luminous component. We fit the dark component with an elliptical NFW profile. The Sersic parameters for the lens light are joint with the luminous mass distribution except for the amplitudes letting the global mass-to-light ratio be a free parameter. We generated the synthetic data for a composite model with elliptical NFW and elliptical Chameleon profiles. In the 'Reconstructed source' plot, the pink contours outline the caustics. The blue star indicates the point source position.  The green arrows in the 'Convergence and deflection' plot represent negative deflection angles and they are shrunk by a factor of 4 for nicer visualization. The red dots in the 'Magnification model' plot point out the image positions. Our method of computing lensing quantities for the Sersic profile with ellipticity in the convergence works well as evident from the 'Normalized Residual' plot. This method only takes approximately three times longer than using the fiducial model with the Chameleon profile. Unlike the Chameleon profile, however, our method is general.

>>>**Table 1.** Fidelity of our lens modelling method. We simulate mock data with a fiducial model composed of elliptical Chameleon convergence, elliptical NFW deflection potential, and external shear. We test our method with the "Gaussian''-model: elliptical Sersic convergence decomposed into concentric Gaussians, elliptical NFW deflection potential, and external shear. The 'True' rows contain the mock values of the fiducial model parameters. The ;Gaussian-fit' rows contain the parameters of the "Gaussian''-model fit to the data. Similarly, the 'Fiducial-fit' rows contain the parameters of the fiducial model fit to the data. We do not provide uncertainty for the values that are accurate up to the displayed decimal point. The accuracy of our computational method with Gaussian components is comparable to that using the fiducial model.

<table>
  <tr>
    <td><b>Mass Profile</b></td>
    <td colspan="4"><b>Parameters</b></td>
  </tr>

  <tr>
    <td><b>Sersic</b></td>
    <td>$R_{\rm eff}$ (arcsec)</td>
    <td>$n_{\rm Sersic}$</td>
    <td>$q$<br/></td>
    <td>$\phi$ (deg)</td>
  </tr>
  <tr>
    <td>    True<sup>a</sup></td>
    <td>1.55</td>
    <td>3.09</td>
    <td>0.6</td>
    <td>45</td>
  </tr>
  <tr>
    <td>    Gaussian-fit</td>
    <td>1.51±0.02</td>
    <td>3.04±0.03</td>
    <td>0.61</td>
    <td>45.1±0.3</td>
  </tr>

  <tr>
    <td><b>Chameleon</b></td>
    <td>$w_{\rm t}$ (arcsec)</td>
    <td>$w_{\rm c}$ (arcsec)</td>
    <td>$q$</td>
    <td>$\phi$ (deg)</td>
  </tr>
  <tr>
    <td>    True</td>
    <td>0.038</td>
    <td>1.7</td>
    <td>0.6</td>
    <td>45</td>
  </tr>
  <tr>
    <td>    Fiducial-fit</td>
    <td>0.038</td>
    <td>1.7</td>
    <td>0.6</td>
    <td>45.0±0.2</td>
  </tr>

  <tr>
    <td><b>NFW</b></td>
    <td>$r_{\rm s}$ (arcsec)</td>
    <td>$\alpha_{\rm s}$ (arcsec)</td>
    <td>$q$</td>
    <td>$\phi$ (deg)</td>
  </tr>
  <tr>
    <td>    True</td>
    <td>5</td>
    <td>1</td>
    <td>0.9</td>
    <td>45</td>
  </tr>
  <tr>
    <td>    Gaussian-fit</td>
    <td>5.0</td>
    <td>0.99±0.03</td>
    <td>0.87±0.02</td>
    <td>44±2</td>
  </tr>
  <tr>
    <td>    Fiducial-fit</td>
    <td>5.0</td>
    <td>1.00±0.01</td>
    <td>0.90±0.01</td>
    <td>45±2</td>
  </tr>

  <tr>
    <td><b>External shear</b></td>
    <td>$\gamma$</td>
    <td>$\phi$ (deg)</td>
    <td>–</td>
    <td>–</td>
  </tr>
  <tr>
    <td>    True</td>
    <td>0.051</td>
    <td>5.7</td>
    <td>–</td>
    <td>–</td>
  </tr>
  <tr>
    <td>    Gaussian-fit</td>
    <td>0.056±0.002</td>
    <td>9±3</td>
    <td>–</td>
    <td>–</td>
  </tr>
  <tr>
    <td>    Fiducial-fit</td>
    <td>0.051±0.001</td>
    <td>6±2</td>
    <td>–</td>
    <td>–</td>
  </tr>
  <tr>
    <td colspan="5"><i>Notes.</i> <sup>a</sup>The true values of the Sersic-profile parameters correspond to the <br/> true values of the Chameleon-profile parameters.</td>
  </tr>
</table>

## 6 Conclusion: precision is feasible.

In this paper, we present a general method for precise lensing analysis of any elliptical convergence profile. Our method follows a "divide and conquer'' strategy. In our method, we first decompose an elliptical convergence profile into concentric Gaussian components as

\begin{equation} \tag{6.1}\label{61}
	\kappa(x, y) \approx \sum_{j=1}^J \kappa_{0j} \exp \left( -\frac{q^2 x^2 + y^2}{2 \sigma_j^2} \right)
\end{equation}

We then compute lensing quantities, e.g., the gradient and the Hessian of the deflection potential, for each Gaussian component. Finally, we add the lensing quantities from individual Gaussian components together to obtain these quantities for the total surface-density profile. Moreover, we can straightforwardly deproject a Gaussian component to obtain its corresponding three-dimensional density profile assuming either axisymmetry or spherical symmetry. Then, we can also compute the kinematic properties, such as the line-of-sight velocity dispersion, for each Gaussian component (Cappellari [2008](http://adsabs.harvard.edu/abs/2008MNRAS.390...71C)). We can then add the velocity dispersions from individual Gaussians together to obtain the total line-of-sight velocity dispersion. In this way, we self-consistently unify the lensing and kinematic descriptions of any elliptical mass profile.

We introduce an integral transform with a Gaussian kernel that leads us to a general, precise, and fast algorithm for decomposing a surface density profile into concentric Gaussian components. Without such an algorithm, decomposing into Gaussians would end up as a bottleneck in the lens modelling efficiency. We obtain the algorithm by first inverting the integral transform as

\begin{equation} \tag{6.2}\label{62}
	f(\sigma) = \frac{1}{\mathrm{i}\sigma^2} \sqrt{\frac{2}{π}} \int_{C} z F(z) \exp \left( \frac{z^2}{2 \sigma^2} \right)\ \mathrm{d} z.
\end{equation}

Although this is an integral, we provide a straightforward formula to compute $f(\sigma)$. The computed values of $f(\sigma)$ then quantify the amplitudes $\kappa_{0j}$ of the Gaussian components in equation ([6.1](#mjx-eqn-61)). As a result, this integral transform fulfills the three requirements for a decomposition algorithm to be **(i)** general, **(ii)** precise, and **(iii)** fast. To be specific, this decomposition algorithm is $\sim 10^3$ times faster than the MGE algorithm from Cappellari ([2002](http://adsabs.harvard.edu/abs/2002MNRAS.333..400C)). Consequently, our lensing analysis requires the same order of CPU time as other methods currently in use to model a lens with a composite mass profile. Thus, the integral transform enables the lens modelling with the Gaussian components to be efficient and, in turn, makes our unified framework for lensing and kinematic analysis of an elliptical mass profile feasible.

Our method enables precise lens modelling with an elliptical mass profile for several astrophysical applications. Specifically, our method gives an efficient method to model composite mass profiles with separate components for the baryonic matter and the dark matter. For example, the usual choices for these components are the Sersic and the NFW profiles; both are computationally difficult to directly implement in lens modelling for the elliptical case. Our method makes both of these profiles computationally tractable while achieving the required precision. Thus, our method will be useful in applications where a composite mass profile is essential for lens modelling, for example, in  detecting dark-matter substructure, in measuring the Hubble constant, and in testing massive elliptical-galaxy formation theories (e.g., Vegetti et al. [2012](http://adsabs.harvard.edu/abs/2012Natur.481..341V), Wong et al. [2017](http://adsabs.harvard.edu/abs/2017MNRAS.465.4895W), Nightingale et al. [2019](http://adsabs.harvard.edu/abs/2019arXiv190107801N)).

## Acknowledgements

AJS thanks Simon Birrer and Shouman Das for helpful discussions. AJS also thanks the anonymous referee for very useful suggestions that improved this paper. AJS expresses gratitude to Adriano Agnello, Simon Birrer, Xuheng Ding, Xinnan Du, Abhimat K. Gautam, Briley Lewis, Michael Topping, and Tommaso Treu for providing feedbacks that greatly improved the writing and the presentation of this paper. AJS acknowledges support by National Aeronautics and Space Administration (NASA) through Space Telescope Science Institute grant HST-GO-15320.

This research made use of `lenstronomy` (Birrer & Amara [2018](http://adsabs.harvard.edu/abs/2018PDU....22..189B)), `numpy` (Oliphant [2015](http://web.mit.edu/dvp/Public/numpybook.pdf)), `scipy` (Jones et al. [2001](http://www.scipy.org/)), `jupyter` (Kluyver et al. [2016](https://eprints.soton.ac.uk/403913/)), and `matplotlib` (Hunter [2007](http://adsabs.harvard.edu/abs/2007CSE.....9...90H)).

## Appendix A: Properties of the integral transform with a Gaussian kernel

In this appendix, we prove some fundamental properties of the integral transform with a Gaussian kernel. In three theorems, we prove that

1. the integral transform exists for a function with certain characteristics,
2. the transform is unique for a continuous function, and
3. the transform is invertible.

First, we define the integral transform.

**Definition A.1.** Define an integral transform $\mathcal{T}$ that takes a function $f(\sigma):\mathbb{R}_{\geq 0} \to \mathbb{R}$ to a function $F(z):\mathbb{C} \to \mathbb{C}$ as

\begin{equation} \tag{Α1}\label{A1}
		F(z) \equiv \mathcal{T}[f] (z) \equiv \frac{1}{\sqrt{2 π}} \int_0^\infty \frac{f(\sigma)}{\sigma} \exp\left( -\frac{z^2}{2\sigma^2}\right) \mathrm{d} \sigma.
\end{equation}

Next, we define a _transformable_ function in the context of this paper.

**Definition A.2.** A function $f(\sigma):\mathbb{R}_{\geq 0} \to \mathbb{R}$ is said to be transformable, if it satisfies the following conditions:

1. the function $f(\sigma)$ is piecewise continuous,
2. the function $f(\sigma) = \mathcal{O}(\exp(c/2\sigma^2))$ as $\sigma \to 0$, where $c \in  \mathbb{R}_{\geq 0}$,
3. the function $f(\sigma) = \mathcal{O}(\sigma^\lambda)$ with $\lambda < 0 $ as $\sigma \to \infty$.

We refer to these three conditions as the _transformability conditions_. The namesake for the transformable function is made clear next in Theorem [A.3](#scrollTo=it_f3KX03WcS).

**Theorem A.3. (Existence)** If $f(\sigma)$ is transformable, then its integral transform $F(z)$ exists in the region of convergence (ROC) $\mathrm{Re}(z^2) > c$.

_Proof._ Divide the integral in equation ([A1](#mjx-eqn-A1)) as

\begin{equation} \tag{A2}\label{A2}
F(z) = \frac{1}{\sqrt{2 π}} \left( \int_0^a \mathrm{d} \sigma + \int_a^b \mathrm{d} \sigma + \int_b^\infty \mathrm{d} \sigma \right) \frac{f(\sigma)}{\sigma} \exp\left( -\frac{z^2}{2\sigma^2}\right) \\
= \frac{1}{\sqrt{2 π}} \left( \mathcal{I}_1 + \mathcal{I}_2 + \mathcal{I}_3 \right),
\end{equation}

where $0<a<b<\infty$.

1. The integral $\mathcal{I}_2$ converges, as the integrand is piecewise continuous in $[a, b]$ according to transformability condition 1 in Definition A.2.
2. According to transformability condition 2, there exists $M_1 \in \mathbb{R}_{>0}$ such that $f(\sigma) \leq M_1 \exp (c/2\sigma^2)$ for $\sigma \leq a$. Then using Jensen's inequality, we have

\begin{equation} \tag{A3}\label{A3}
\newcommand{\re}{{\rm Re}}
\newcommand{\im}{{\rm Im}}
\newcommand{\dd}{\mathrm{d}}
\left|\mathcal{I}_1\right| \leq \int_0^a \left|\frac{f(\sigma)}{\sigma} \exp\left( -\frac{z^2}{2\sigma^2}\right)\right| \mathrm{d} \sigma \\
	\Rightarrow \left| \mathcal{I}_1 \right| \leq \int_0^a \left| \frac{M_1}{\sigma} \exp\left( - \frac{z^2 - c}{2\sigma^2}\right) \right| \mathrm{d} \sigma  \\
	  \Rightarrow \left|\mathcal{I}_1 \right| \leq M_1 \int_0^a \frac{1}{\sigma} \exp \left(-\frac{\re (z^2) - c}{2 \sigma^2} \right) \mathrm{d} \sigma.
\end{equation}

Therefore, the integral $\mathcal{I}_1$ converges in the region $\re (z^2) = x^2 - y^2 > c$, where $z = x + \mathrm{i}y$, $x \in \mathbb{R}, y \in \mathbb{R}$.

2. According to transformability condition 3, there exists $M_2 \in \mathbb{R}_{>0}$ such that $f(\sigma) \leq M_2 \sigma^{\lambda}$ for $\sigma \geq b$. Then, we have

\begin{equation} \tag{A4}\label{A4}
  \newcommand{\ra}{\right|}
  \newcommand{\la}{\left|}
		\la \mathcal{I}_3\ra \leq \int_b^\infty \la \frac{f(\sigma)}{\sigma} \exp \left( - \frac{z^2}{2 \sigma^2} \right) \ra \dd \sigma \\
		\Rightarrow \la \mathcal{I}_3 \ra \leq \int_b^\infty M_2 \sigma^{\lambda-1} \la \exp \left( - \frac{z^2}{2 \sigma^2}\right) \ra \dd \sigma \\
		\Rightarrow \la \mathcal{I}_3 \ra \leq M_2 \int_b^\infty \sigma^{\lambda-1} \dd \sigma = -M_2\frac{b^{\lambda}}{\lambda} < \infty.
\end{equation}

Here, we applied the inequality $\left|\exp (-z^2/2\sigma^2)\right| \leq 1$ for $\re (z^2) > c \geq 0$. As a result, the integral $\mathcal{I}_3$ converges.

Therefore, the transform $F(z)$ exists in the ROC $\re(z^2) > c$. $\square$

Fig. [A1](#scrollTo=zBNYTOeevKkJ) shows the ROC for the integral in equation ([A1](#mjx-eqn-A1)). We can extend the ROC by the following two corollaries.

<center></center>

>**Figure A1.** Region of convergence (shaded region) on the complex plane for the integral in equation ([A1](#mjx-eqn-A1)). The hyperbolic contour $C$ for the integral in equation ([A9](#mjx-eqn-A9)) is shown with solid black hyperbola.

**Corollary A.4.** If a transformable function $f(\sigma)$ additionally satisfies the condition $f(\sigma) = \mathcal{O} (\sigma^\beta \exp(c/2\sigma^2))$ with $\beta \geq 1$ as $\sigma \to 0$, then the integral in equation ([A1](#mjx-eqn-A1)) converges in the ROC $\re (z^2) \geq c$.

_Proof._ According to the additional condition, there exists $ M_3 \in \mathbb{R}_{>0}$ such that $f(\sigma) \leq M_3 \sigma^{\beta} \exp(c/2\sigma^2)$ for $\sigma \leq a$. Then, we can rewrite equation ([A3](#mjx-eqn-A3)) as

\begin{equation} \tag{A5}\label{A5}
	\la \mathcal{I}_1 \ra \leq \int_0^a \la \frac{f(\sigma)}{\sigma} \exp\left( -\frac{z^2}{2\sigma^2}\right) \ra \dd \sigma \\
	\Rightarrow \la \mathcal{I}_1 \ra \leq \int_0^a \la M_3 \sigma^{\beta - 1} \exp\left( - \frac{z^2 - c}{2\sigma^2}\right)
 \ra \dd \sigma  \\
	  \Rightarrow \la \mathcal{I}_1 \ra \leq M_3 \int_0^a \sigma^{\beta-1} \exp \left(-\frac{\re (z^2) - c}{2 \sigma^2} \right) \dd \sigma.
\end{equation}

For $\re (z^2) = c$, this becomes

\begin{equation} \tag{A6}\label{A6}
\la \mathcal{I}_1 \ra \leq M_3 \int_0^a \sigma^{\beta - 1} \dd \sigma = \frac{M_3 a^{\beta}}{\beta} < \infty.
\end{equation}

Therefore, the ROC for the integral in equation ([A1](#mjx-eqn-A1)) extends to $\re (z^2) \geq c$. $\square$

**Corollary A.5.** If a transformable function $f(\sigma)$ additionally satisfies $f(\sigma) = \mathcal{O} (\sigma^\beta)$ with $\beta \geq 1$ as $\sigma \to 0$, then the integral in equation ([A1](#mjx-eqn-A1)) converges in the ROC $\re (z^2) \geq 0$.

_Proof._ The proof is trivial with the substitution $c = 0$ in Corollary [A.4](#scrollTo=TXZfV0aMKNY9). $\square$

Next we prove the uniqueness theorem for the transform. First, we state a well-known proof of the following lemma for completeness.

**Lemma A.6.**  If $f(x)$ is continuous in $[0, 1]$, and $\int_0^1 x^n f(x)\ \dd x = 0$ for $n=0,1,2,\dots$, then $f(x)=0$.

_Proof._	From the Weierstrass approximation theorem, for any $ \epsilon>0$, there exists a polynomial $P_{\epsilon}(x)$ such that $\la{f(x)-P_{\epsilon}(x)}\ra< \epsilon$ for all $x \in [0,1]$. The hypothesis implies that $\int_0^1 P_{\epsilon}(x) f(x)\ \dd x  = 0$. By taking the limit $\epsilon \to 0$, this equation becomes $\int_0^1 f(x)f(x)\  \dd x = 0$. As $f(x)^2 \geq 0$, we have $f(x)=0$.  $\square$

**Theorem A.7. (Uniqueness)** If $f(\sigma)$ and $g(\sigma)$ are continuous, and $\mathcal{T}[f](z) = \mathcal{T}[g](z)$ for all $z$ in the ROC, then $f(\sigma)=g(\sigma)$.

_Proof._	Due to linearity, it is sufficient to prove that if $\mathcal{T}[f](z) = 0$, then $f(\sigma)=0$. Take $d$ such that the contour $\re (z^2) = d$ lies in the ROC. By making the change of variables $s=\exp(-1/2\sigma^2)$, for $z^2=d+n+1$ with $n=0,1,2,\dots$, we have

\begin{equation} \tag{A7}\label{A7}
  \newcommand{\uppi}{π}
			\mathcal{T}[f](z) = \frac{1}{\sqrt{2\uppi}} \int_0^\infty \frac{f(\sigma)}{\sigma} \exp \left( -\frac{d+n+1}{2\sigma^2}\right) \dd \sigma = 0 \\
			\Rightarrow \int_0^1 \left[ - \frac{s^d f(\sqrt{-1/2\log s})}{2 \sqrt{2\uppi} \log s} \right] s^n \dd s = 0.
\end{equation}

 This integral exists as $s \to 0$, because
	\begin{equation} \tag{A8}\label{A8}
		\lim_{s \to 0}	\left[ - \frac{s^d f(\sqrt{-1/2\log s})}{2 \sqrt{2\uppi} \log s} \right] = \lim_{\sigma \to 0} \left[ \frac{\sigma^2 f(\sigma)}{\sqrt{2 \uppi}} \exp \left(- \frac{d}{2\sigma^2} \right) \right] = 0.
	\end{equation}

Therefore, according to Lemma [A.6](#scrollTo=-0XsGwfH39cf), we have $f(\sigma)=0$. $\square$

**Theorem A.8. (Inversion)** If $F(z)$ is the transform of $f(\sigma)$, then $f(\sigma)$ is given by the inverse transform

\begin{equation} \tag{A9}\label{A9}
	f(\sigma)= \mathcal{T}^{-1}[F](\sigma) = \frac{1}{\mathrm{i}\sigma^2} \sqrt{\frac{2}{\uppi}} \int_{C} z F(z) \exp \left( \frac{z^2}{2 \sigma^2} \right) \dd z,
\end{equation}

where the contour $C$ is the hyperbola $\re (z^2) = d$ such that $C$ lies in ROC of $F(z)$.

_Proof._ Write equation ([A1](#mjx-eqn-A1)) for $z^2 = d$ as

\begin{equation}\tag{A10}\label{A10}
	F\left(\sqrt{d}\right) = \frac{1}{\sqrt{2 \uppi}} \int_0^\infty \frac{f(\sigma)}{\sigma} \exp\left( -\frac{d}{2\sigma^2}\right) \dd \sigma .
\end{equation}

With the change of variables $p = 1/\sigma^2$, this equation transforms into

\begin{equation} \tag{A11} \label{A11}
	F\left(\sqrt{d} \right) = \int_0^\infty g(p) \exp \left( -\frac{dp}{2} \right) \dd p,
\end{equation}

where
\begin{equation} \tag{A12}\label{A12}
g(p) = \frac{\sigma^2 f(\sigma)}{2 \sqrt{2 \uppi}}.
\end{equation}

Define a new function

\begin{equation} \tag{A13}\label{A13}
h(p) =
     \begin{cases}
       g(p) \exp \left( -\frac{dp}{2}\right), & p \geq 0,\\
       0, &p < 0. \\
     \end{cases}
\end{equation}

Theorem A.3 implies that $\int_{-\infty}^\infty \la{h(p)}\ra \ \dd p < \infty $, thus $h(p)$ belongs to the Lebesgue space $L^1 (\mathbb{R})$. Therefore, we can take the Fourier transform of $h(p)$ as

\begin{equation} \tag{A14}\label{A14}
	\hat{h}(\nu) = \frac{1}{\sqrt{2 \uppi}} \int_{-\infty}^{\infty} h(p) \mathrm{e}^{-\mathrm{i}p\nu} \dd p \\
	=\frac{1}{\sqrt{2 \uppi}} \int_{0}^{\infty} g(p) \exp \left( -\frac{(d+2\mathrm{i}\nu)p}{2}\right) \dd p \\
	= \frac{1}{\sqrt{2 \uppi}} F\left( \sqrt{d+2\mathrm{i}\nu} \right),
\end{equation}

where we used equation ([A11](#mjx-eqn-A11)) for the substitution in the last line. Now, take the inverse Fourier transform of $\hat{h}(\nu)$ as

\begin{equation} \tag{A15}\label{A15}
	h(p) = \frac{1}{\sqrt{2\uppi}} \int_{-\infty}^{\infty} \hat{h} (\nu) e^{\mathrm{i} p \nu} \dd \nu \\
	\Rightarrow g(p) \exp \left( - \frac{dp}{2} \right) = \frac{1}{2 \uppi} \int_{-\infty}^{\infty} F\left( \sqrt{d + 2 \mathrm{i} \nu} \right) e^{\mathrm{i} p \nu} \dd \nu \\
	\Rightarrow \frac{\sigma^2 f(\sigma)}{2 \sqrt{2 \uppi}} = \frac{1}{2 \uppi} \int_{-\infty}^{\infty} F\left( \sqrt{d + 2 \mathrm{i} \nu} \right) \exp \left(\frac{d + 2 \mathrm{i} \nu}{2\sigma^2} \right) \dd \nu \\
	\Rightarrow f(\sigma) = \frac{1}{\mathrm{i}\sigma^2} \sqrt{\frac{2}{\uppi}} \int_{C} z F(z) \exp \left( \frac{z^2}{2 \sigma^2} \right) \dd z.
\end{equation}

Here, we used the substitution of variable $z^2 = d+2 \mathrm{i} \nu$ in the last line, which transforms the integral path to the hyperbolic contour $C$ given by $\re (z^2) = d$. $\square$

**Remark A.9.** Equation ([A11](#mjx-eqn-A11)) has the form of a Laplace transform. Therefore, the integral transform with a Gaussian kernel for a transformable function can be converted into a Laplace transform by suitable change of variables.

## Appendix B: Efficient algorithm to compute the Faddeeva function

In this appendix, we provide an efficient `python` [function](#scrollTo=FmSoVRxSnR1R) to compute the Faddeeva function $w_{\rm F}(z)$  based on the algorithm from Zaghloul ([2017](http://doi.acm.org/10.1145/3119904)). The relative error of this algorithm is less than  $4\times 10^{-5}$ over the whole complex plane.

```python
def wofz_approx(z):
    """
    Compute the Faddeeva function w(z) using the approximation given in
    Zaghloul (2017).

    :param z: complex number or array
    :type z: complex
    :return: w_f
    :rtype: complex
    """
    sqrt_pi = 1 / np.sqrt(np.pi)
    i_sqrt_pi = 1j * sqrt_pi

    wz = np.empty_like(z)

    z_imag2 = z.imag ** 2
    abs_z2 = z.real ** 2 + z_imag2

    reg1 = (abs_z2 >= 38000.)

    if np.any(reg1):
        wz[reg1] = i_sqrt_pi / z[reg1]

    reg2 = (256. <= abs_z2) & (abs_z2 < 38000.)
    if np.any(reg2):
        t = z[reg2]
        wz[reg2] = i_sqrt_pi * t / (t * t - 0.5)

    reg3 = (62. <= abs_z2) & (abs_z2 < 256.)
    if np.any(reg3):
        t = z[reg3]
        wz[reg3] = (i_sqrt_pi / t) * (1 + 0.5 / (t * t - 1.5))

    reg4 = (30. <= abs_z2) & (abs_z2 < 62.) & (z_imag2 >= 1e-13)
    if np.any(reg4):
        t = z[reg4]
        tt = t * t
        wz[reg4] = (i_sqrt_pi * t) * (tt - 2.5) / (tt * (tt - 3.) + 0.75)

    reg5 = (62. > abs_z2) & np.logical_not(reg4) & (abs_z2 > 2.5) & (
              z_imag2 < 0.072)
    if np.any(reg5):
        t = z[reg5]
        u = -t * t
        f1 = sqrt_pi
        f2 = 1
        s1 = [1.320522, 35.7668, 219.031, 1540.787, 3321.99, 36183.31]
        s2 = [1.841439, 61.57037, 364.2191, 2186.181, 9022.228, 24322.84,
            32066.6]

        for s in s1:
            f1 = s - f1 * u
        for s in s2:
            f2 = s - f2 * u

        wz[reg5] = np.exp(u) + 1j * t * f1 / f2

    reg6 = (30.0 > abs_z2) & np.logical_not(reg5)
    if np.any(reg6):
        t3 = - 1j * z[reg6]

        f1 = sqrt_pi
        f2 = 1
        s1 = [5.9126262, 30.180142, 93.15558, 181.92853, 214.38239,
            122.60793]
        s2 = [10.479857, 53.992907, 170.35400, 348.70392, 457.33448,
            352.73063, 122.60793]

        for s in s1:
            f1 = f1 * t3 + s
        for s in s2:
            f2 = f2 * t3 + s

        wz[reg6] = f1 / f2

    return wz
```

Unified lensing and kinematic analysis.ipynb