# The Fourier Transform

*This Jupyter notebook is part of a [collection of notebooks](../index.ipynb) in the bachelors module Signals and Systems, Communications Engineering, Universität Rostock. Please direct questions and suggestions to [Sascha.Spors@uni-rostock.de](mailto:Sascha.Spors@uni-rostock.de).*

## Theorems

The theorems of the Fourier transform relate basic time-domain operations to their equivalents in the Fourier domain. They are of use for the computation of Fourier transforms of signals composed from modified [standard signals](../continuous_signals/standard_signals.ipynb) and for the computation of the response of systems to an input signal. The theorems allow further to predict the consequences of modifying a signal or system by certain operations.

### Temporal Scaling Theorem

A signal $x(t)$ for which the Fourier transform $X(j \omega) = \mathcal{F} \{ x(t) \}$ is existing and known is given. The Fourier transform of the [temporally scaled signal](../continuous_signals/operations.ipynb#Temporal-Scaling) $x(a t)$ with $a \in \mathbb{R} \setminus \{0\}$ reads

\begin{equation}
\mathcal{F} \{ x(a t) \} = \frac{1}{|a|} \cdot X \left( \frac{j \omega}{a} \right)
\end{equation}

The Fourier transformation of a temporally scaled signal is given by inverse scaling of the Fourier transform of the unscaled signal and weighting with $\frac{1}{|a|}$. This relation is known as scaling theorem of the Fourier transform. It can be proven by introducing the scaled signal $x(a t)$ into the definition of the Fourier transformation together with the substitution $t' = a t$. Note that the cases $a<0$ and $a>0$ have to be regarded separately.

**Example**

According to the scaling theorem and the [Fourier transform of the rectangular signal](definition.ipynb#Transformation-of-the-Rectangular-Signal) $\text{rect}(t)$, the transform of the scaled rectangular signal $\text{rect}(a t)$ is given as

\begin{equation}
\mathcal{F} \{ \text{rect}(a t) \} = \frac{1}{|a|} \cdot \text{sinc} \left( \frac{\omega}{2 a} \right)
\end{equation}

This can be confirmed by explicitly calculating the Fourier transformation of $\text{rect}(a t)$

```python
%matplotlib inline
import sympy as sym
sym.init_printing()

def fourier_transform(x):
    return sym.transforms._fourier_transform(x, t, w, 1, -1, 'Fourier')

class rect(sym.Function):

    @classmethod
    def eval(cls, arg):
        return sym.Heaviside(arg + sym.S.Half) - sym.Heaviside(arg - sym.S.Half)

t, w = sym.symbols('t omega', real=True)
a = sym.symbols('a', positive=True)

x = rect(a*t)
X = fourier_transform(x)
X = X.rewrite(sym.sin).simplify()
X
```

For illustration both the signal $x(t)$ and its Fourier transform $X(j \omega)$ are plotted for a specific value of $a$

```python
values = {a: 1/2}

sym.plot(x.subs(values), (t, -5, 5), xlabel=r'$t$', ylabel=r'$\mathrm{rect}(a t)$');
sym.plot(X.subs(values), (w, -20, 20), xlabel=r'$\omega$', ylabel=r'$\mathcal{F} \{ \mathrm{rect}(a t) \}$');
```

**Exercise**

* Change the width $1/a$ of the rectangular signal. How does its spectrum change?
* On a qualitative level, what happens when $1/a$ increases towards infinity?

### Convolution Theorem

The [convolution theorem](https://en.wikipedia.org/wiki/Convolution_theorem) states that the Fourier transform of the convolution of two signals $x(t)$ and $y(t)$ is equal to the scalar multiplication of their Fourier transforms $X(j \omega) = \mathcal{F} \{ x(t) \}$ and $Y(j \omega) = \mathcal{F} \{ y(t) \}$

\begin{equation}
\mathcal{F} \{ x(t) * y(t) \} = X(j \omega) \cdot Y(j \omega)
\end{equation}

The theorem can be proven by introducing the [definition of the convolution](../systems_time_domain/convolution.ipynb) into the [definition of the Fourier transform](definition.ipynb) and changing the order of integration

\begin{align}
\mathcal{F} \{ x(t) * y(t) \} &= \int_{-\infty}^{\infty} \left( \int_{-\infty}^{\infty} x(\tau) \cdot y(t-\tau) \; d \tau \right) e^{-j \omega t} \; dt \\
&= \int_{-\infty}^{\infty} \left( \int_{-\infty}^{\infty} y(t-\tau) \, e^{-j \omega t} \; dt \right) x(\tau) \; d\tau \\
&= Y(j \omega) \cdot \int_{-\infty}^{\infty} x(\tau) \, e^{-j \omega \tau} \; d \tau \\
&= Y(j \omega) \cdot X(j \omega)
\end{align}

under the assumption that both Fourier transforms $X(j \omega)$ and $Y(j \omega)$ exist.

The convolution theorem is very useful in the context of linear time-invariant (LTI) systems. The output signal $y(t)$ of an LTI system is given as the convolution of the input signal $x(t)$ with the impulse response $h(t)$. Hence, the signals and the system can be represented equivalently in the time and frequency domain

where $H(j \omega) = \mathcal{F} \{ h(t) \}$.
Calculation of the system response by transforming the problem into the Fourier domain can be beneficial since this replaces the evaluation of the convolution integral by a scalar multiplication. The (inverse) Fourier transforms are known for many signals or can be derived by applying the properties and theorems to standard signals and their transforms. This procedure simplifies the calculation of the system response significantly in many cases.

The convolution theorem can also be useful to derive an unknown Fourier transform. The key is here to express the signal as convolution of two other signals for which the Fourier transforms are known. This is illustrated at the following example.

#### Transformation of the triangular signal

A prominent example for the derivation of a Fourier transform by the convolution theorem is the [trianguar signal](https://en.wikipedia.org/wiki/Triangular_function)

\begin{equation}
\Lambda(t) = \begin{cases} 1- |t| & \text{for } |t| < 1 \\ 0 & \text{otherwise} \end{cases}
\end{equation}

which can be expressed as the convolution of two rectangular signals

\begin{equation}
\Lambda(t) = \text{rect}(t) * \text{rect}(t)
\end{equation}

Applying the convolution theorem yields

\begin{equation}
\mathcal{F} \{ \Lambda(t) \} = \text{sinc}\left( \frac{\omega}{2} \right) \cdot \text{sinc}\left( \frac{\omega}{2} \right) = \text{sinc}^2 \left( \frac{\omega}{2} \right)
\end{equation}

Both the signal and its transform are plotted for illustration

```python
x = sym.Piecewise((1 - abs(t), abs(t) < 1), (0, True))
X = sym.sinc(w/2)**2

sym.plot(x, (t, -2, 2), xlabel=r'$t$', ylabel=r'$\Lambda(t)$')
sym.plot(X, (w, -20, 20), xlabel=r'$\omega$', ylabel=r'$\mathcal{F} \{ \Lambda(t) \}$');
```

**Exercise**

* Extend above example by temporal scaling of the triangular signal.
* Derive the Fourier transform of a trapezoidal shaped signal by convolving two rectangular signals with different width.

### Temporal Shift Theorem

The [temporal shift](../continuous_signals/operations.ipynb#Temporal-Shift) $x(t-\tau)$ of a signal $x(t)$ can be expressed by a convolution with a shifted Dirac impulse

\begin{equation}
x(t - \tau) = x(t) * \delta(t - \tau)
\end{equation}

for $\tau \in \mathbb{R}$. This follows from the sifting property of the Dirac impulse. Applying a Fourier transform to the left- and right-hand side and exploiting the convolution theorem yields

\begin{equation}
\mathcal{F} \{ x(t - \tau) \} = X(j \omega) \cdot e^{- j \omega \tau}
\end{equation}

where $X(j \omega) = \mathcal{F} \{ x(t) \}$. Note that $\mathcal{F} \{ \delta(t - \tau) \} = e^{- j \omega \tau}$ can be derived from the definition of the Fourier transform together with the sifting property of the Dirac impulse. Above relation is known as shift theorem of the Fourier transform.

Expressing $X(j \omega)$ by its absolute value $|X(j \omega)|$ and phase $\varphi(j \omega)$ results in

\begin{equation}
\mathcal{F} \{ x(t - \tau) \} = | X(j \omega) | \cdot e^{j (\varphi(j \omega) - \omega \tau)}
\end{equation}

Temporal shifting of a signal does not change the absolute value of its spectrum but it subtracts the linear contribution $\omega \tau$ from the phase of its spectrum.

### Differentiation Theorem

Let's assume a signal $x(t)$ whose temporal derivative $\frac{d x(t)}{dt}$ exists. Using the [derivation property of the Dirac impulse](../continuous_signals/standard_signals.ipynb#Dirac-Impulse), the derivative of the signal can be expressed by the convolution

\begin{equation}
\frac{d x(t)}{dt} = \frac{d \delta(t)}{dt} * x(t)
\end{equation}

Fourier transformation of the left- and right-hand side together with the [convolution theorem](#Convolution-Theorem) yields the Fourier transform of the derivative of $x(t)$

\begin{equation}
\mathcal{F} \left\{ \frac{d x(t)}{dt} \right\} = j \omega \cdot X(j \omega)
\end{equation}

where $X(j \omega) = \mathcal{F} \{ x(t) \}$. The Fourier transform $\mathcal{F} \{ \frac{d \delta(t)}{dt} \} = j \omega$ can be derived by applying the definition of the Fourier transform together with the derivation property of the Dirac impulse. Above result is known as differentiation theorem of the Fourier transform. It states that the differentiation of a signal in the time domain is equivalent to a multiplication of its spectrum by $j \omega$.

The main applications of the differentiation theorem are the transformation of differential equations or the derivation of transforms of signals which can be expressed as derivatives of other signals. This latter is illustrated by the following example.

#### Transformation of the sign signal

The derivative of the [sign signal](../continuous_signals/standard_signals.ipynb#Sign-Signal) $\text{sgn}(t)$ [is given by the Dirac impulse](https://en.wikipedia.org/wiki/Sign_function#Properties)

\begin{equation}
\frac{d}{dt} \text{sgn}(t) = 2 \cdot \delta(t)
\end{equation}

Fourier transformation of the left- and right-hand side and application of the differentiation theorem yields

\begin{equation}
\mathcal{F} \{ \text{sgn}(t) \} = \frac{2}{j \omega}
\end{equation}

The same result can be achieved by computing the transform in `SymPy`

```python
fourier_transform(sym.sign(t))
```

#### Transformation of the Heaviside signal

The transformation of the [Heaviside signal](../continuous_signals/standard_signals.ipynb#Heaviside-Signal) $\epsilon(t)$ is derived by expressing it in terms of the sign signal

\begin{equation}
\epsilon(t) = \frac{1}{2} ( \text{sgn}(t) + 1 )
\end{equation}

Using the linearity of the Fourier transform yields

\begin{equation}
\mathcal{F} \{ \epsilon(t) \} = \frac{1}{j \omega} + \pi \cdot \delta(\omega)
\end{equation}

The Fourier transformation of the Heaviside signal plays an important role in the context of [analytic signals](https://en.wikipedia.org/wiki/Analytic_signal) and their generation by the [Hilbert transform](https://en.wikipedia.org/wiki/Hilbert_transform).

### Integration Theorem

Let's assume a signal $x(t)$ for which the integral $\int_{-\infty}^{t} x(\tau) \; d\tau$ exists. The integration can be regarded as inverse operation to the temporal derivative. The integration can be represented as convolution with the rectangular signal $\epsilon(t)$ ([see integration theorem of the Laplace transform](../laplace_transform/theorems.ipynb#Integration-Theorem))

\begin{equation}
\int_{-\infty}^{t} x(\tau) \; d\tau = \int_{-\infty}^{\infty} x(\tau) \cdot \epsilon(t - \tau) \; d\tau = x(t) * \epsilon(t)
\end{equation}

Fourier transformation of the left- and right-hand side, application of the convolution theorem and using the Fourier transform of the Heaviside signal $\epsilon(t)$ yields the integration theorem of the Fourier transform

\begin{equation}
\mathcal{F} \left\{ \int_{-\infty}^{t} x(\tau) \; d\tau \right\}
= X(j \omega) * \left( \frac{1}{j \omega} + \pi \delta(\omega) \right)
= \frac{1}{j \omega} X(j \omega) + \pi X(0) \delta(\omega)
\end{equation}

**Example**

A signal whose amplitude increases linear over a finite temporal interval to a finite value can be defined as

\begin{equation}
x(t) = \begin{cases}
0 & \text{for } t < - \frac{1}{2} \\
t + \frac{1}{2} & \text{for } - \frac{1}{2} < t < \frac{1}{2} \\
1 & \text{for } t > \frac{1}{2}
\end{cases}
\end{equation}

Such functions are known as [ramp functions](https://en.wikipedia.org/wiki/Ramp_function). In order to derive the Fourier transform of the ramp signal $x(t)$ it is important to note that it can be expressed as integration over the rectangular signal $\text{rect}(t)$

\begin{equation}
x(t) = \int_{-\infty}^{t} \text{rect}(\tau) \; d\tau
\end{equation}

Now its straightforward to apply the integration theorem to yield the desired Fourier transform

\begin{equation}
X(j \omega) = \frac{1}{j \omega} \text{sinc}\left( \frac{\omega}{2} \right) + \pi \cdot \text{sinc}(0) \cdot \delta(\omega)
\end{equation}

### Multiplication Theorem

The Fourier transform of a multiplication of two signals $x(t)$ and $y(t)$ can be derived in a similar way as the convolution theorem by introducing the definition of the (inverse) Fourier transform and rearranging terms. Alternatively the duality of the Fourier transform can be applied to the [convolution theorem](#Convolution-Theorem). Both results in

\begin{equation}
\mathcal{F} \{ x(t) \cdot y(t) \} = \frac{1}{2 \pi} X(j \omega) * Y(j \omega)
\end{equation}

The Fourier transform of a multiplication of two signals $x(t) \cdot y(t)$ is given by the convolution of their Fourier transforms $X(j \omega) = \mathcal{F} \{ x(t) \}$ and $Y(j \omega) = \mathcal{F} \{ y(t) \}$ weighted with $\frac{1}{2 \pi}$. Note, the convolution is performed with respect to the angular frequency $\omega$.

Applications of the multiplication theorem include the modulation and windowing of signals. The former leads to the modulation theorem introduced later, the latter is illustrated by the following example.

**Example**

Windowing of signals is often applied to derive signals of finite duration from signals with infinite duration. Here the signal $x(t)$ is multiplied by a weighting function $w(t)$ in order to derive the finite length signal

\begin{equation}
y(t) = w(t) \cdot x(t)
\end{equation}

The following example considers the Fourier transform of a finite-length cosine signal. Following the concept of windowing, the signal can be defined as

\begin{equation}
y(t) = \text{rect}(a t) \cdot \cos(\omega_0 t)
\end{equation}

where $\text{rect}(a t)$ denotes the scaled [rectangular signal](../continuous_signals/standard_signals.ipynb#Rectangular-Signal) which is zero for $|t| > \frac{1}{2 a}$. The Fourier transform $Y(j \omega) = \mathcal{F} \{ y(t) \}$ is derived by the multiplication theorem, the [Fourier transform of the rectangular signal](definition.ipynb#Transformation-of-the-Rectangular-Signal), and the [Fourier transform of the cosine signal](properties.ipynb#Transformation-of-the-cosine-and-sine-signal) as

\begin{align}
Y(j \omega) &= \frac{1}{2 \pi} \cdot \frac{1}{|a|} \text{sinc}\left( \frac{\omega}{2 a} \right) * \pi \left( \delta(\omega + \omega_0) + \delta(\omega - \omega_0) \right) \\
&= \frac{1}{2 |a|} \left( \text{sinc}\left( \frac{\omega + \omega_0}{2 a} \right) + \text{sinc}\left( \frac{\omega - \omega_0}{2 a} \right) \right)
\end{align}

The latter identity results from the sifting property of the Dirac impulse. The convolution is computed in the following in `SymPy` by symbolic evaluation of the convolution integral.

```python
def convolve(x, y, var):
    tau = sym.symbols('tau')
    return sym.integrate(x.subs(var, tau) * y.subs(var, var - tau), (tau, -sym.oo, sym.oo))
```

Using above function, both the Fourier transform of the cosine signal and the rectangular window are convolved which each other

```python
w0, a = sym.symbols('omega0 a')

y = rect(a*t) * sym.cos(w0*t)
W = 1/abs(a) * sym.sinc(w/(2*a))
X = sym.pi*(sym.DiracDelta(w + w0) + sym.DiracDelta(w - w0))

Y = 1/(2*sym.pi) * convolve(W, X, w)
Y
```

Both the signal and its Fourier transform are plotted for specific values $\omega_0$, $a$

```python
values = {w0: 10, a: 1/2}

sym.plot(y.subs(values), (t, -5, 5), xlabel=r'$t$', ylabel=r'$y(t)$')
sym.plot(Y.subs(values), (w, -20, 20), xlabel=r'$\omega$', ylabel=r'$Y(j \omega)$');
```

**Exercise**

* Change the length $1/a$ of the signal by modifying the substitution in the example. How does the Fourier transform change if you decrease or increase the length $1/a$?

* What happens if you change the angular frequency $\omega_0$ of the signal?

* Assume a signal that is composed from a superposition of two finite length cosine signals with different frequencies. What qualitative condition has to hold that you can derive these frequencies from the spectrum of the superposition?

### Modulation Theorem

The complex modulation of a signal $x(t)$ is defined as $e^{j \omega_0 t} \cdot x(t)$ with $\omega_0 \in \mathbb{R}$. Its Fourier transform can be derived by applying the multiplication theorem

\begin{equation}
\mathcal{F} \left\{ e^{j \omega_0 t} \cdot x(t) \right\} = \frac{1}{2 \pi} \cdot 2 \pi \cdot \delta(\omega - \omega_0) * X(j \omega)
= X \big( j \, (\omega - \omega_0) \big)
\end{equation}

where $X(j \omega) = \mathcal{F} \{ x(t) \}$.  Above result states that the complex modulation of a signal leads to a shift of its spectrum. For $\omega_0 > 0$ the spectrum is shifted to higher frequencies. This lays the ground for the wireless transmission of signals. Above result is known as modulation theorem.

**Example**

Lets assume a signal $x(t)$ which has a triangular spectrum $X(j \omega) = 2 \pi \cdot \Lambda(\omega)$. Due to the definition of the triangular function, the spectrum has only non-zero contributions for $|\omega| < 1$. Applying the duality principle to the [Fourier transform of $\Lambda(t)$ derived above](#Transformation-of-the-triangular-signal) yields the corresponding signal $x(t) = \mathcal{F}^{-1} \{ X(j \omega) \}$

\begin{equation}
x(t) = \text{sinc}^2 \left( \frac{t}{2} \right)
\end{equation}

Now lets consider the complex modulation $y(t) = e^{-j \omega_0 t} \cdot x(t)$ of the signal $x(t)$. The Fourier transform of the modulated signal is given as

\begin{equation}
\mathcal{F} \{ e^{j \omega_0 t} \cdot x(t) \} = 2 \pi \cdot \Lambda(\omega - \omega_0)
\end{equation}

The spectrum of the modulated signal has non-zero contributions for $\omega_0 -1 < \omega < \omega_0 + 1$. For large $\omega_0$ it only occupies a small part of the spectrum. This is an important property of signals used for wireless transmission. The signal $y(t)$ is defined

```python
y = sym.exp(sym.I*w0*t) * sym.sinc(t/2)**2
y
```

and the real part of the complex modulated signal is plotted for illustration

```python
values = {w0: 5}
sym.plot(sym.re(y.subs(values)), (t, -20, 20), xlabel=r'$t$', ylabel=r'$\Re \{ y(t) \}$');
```

**Exercise**

* Derive the spectrum of the signal $x(t)$ modulated by $\cos(\omega_0 t)$. Hint: Express $\cos(\omega_0 t)$ by Euler's formula and exploit the linearity of the Fourier transform. What is different in the resulting spectrum when comparing to the result above?

### Parseval's Theorem

[Parseval's theorem](https://en.wikipedia.org/wiki/Parseval's_theorem) relates the energy of a signal in the time domain to its spectrum. The squared absolute value of a signal $x(t)$ represents its instantaneous power. It can be expressed as

\begin{equation}
| x(t) |^2 = x(t) \cdot x^*(t)
\end{equation}

where $x^*(t)$ denotes the complex conjugate of $x(t)$. Fourier transformation of the right-hand side and application of the multiplication theorem results in

\begin{equation}
\mathcal{F} \{ x(t) \cdot x^*(t) \} = \frac{1}{2 \pi} \cdot X(j \omega) * X^*(-j \omega)
\end{equation}

Introducing the definition of the Fourier transform and the convolution

\begin{equation}
\int_{-\infty}^{\infty} x(t) \cdot x^*(t) \, e^{-j \omega t} \; dt =
\frac{1}{2 \pi} \int_{-\infty}^{\infty} X(j \eta) \cdot X^*(-j (\omega - \eta)) \, d\eta
\end{equation}

and setting $\omega = 0$ yields the desired relation

\begin{equation}
\int_{-\infty}^{\infty} | x(t) |^2 \; dt = \frac{1}{2 \pi} \int_{-\infty}^{\infty} | X(j \omega) |^2 \; d\omega
\end{equation}

The integral over the squared absolute signal in the time-domain is equal to the integral over its squared absolute spectrum divided by $2 \pi$. Since the left-hand side represents the energy $E$ of the signal $x(t)$, Parseval's theorem states that the energy can be computed either in the time or spectral domain by integrating over the squared absolute value of the signal/spectrum. Parseval's theorem can be generalized to the [Plancherel theorem](https://en.wikipedia.org/wiki/Plancherel_theorem) which considers the product of two signals $x(t) \cdot y^*(t)$.

**Example**

The energy of the rectangular signal $\text{rect}(t)$ is computed in the time and spectral domain by evaluation of the respective integrals. First the time domain is evaluated

\begin{equation}
E_t = \int_{-\infty}^{\infty} | \text{rect}(t) |^2 \; dt = \int_{- \frac{1}{2}}^{\frac{1}{2}} 1 \; dt
\end{equation}

```python
E_t = sym.integrate(1, (t, -sym.S.Half, sym.S.Half))
E_t
```

The energy in the spectral domain is computed by evaluating

\begin{equation}
E_f = \frac{1}{2 \pi} \int_{-\infty}^{\infty} \left| \text{sinc}\left(\frac{\omega}{2}\right) \right|^2 \; d\omega
\end{equation}

```python
E_f = 1/(2*sym.pi) * sym.integrate(sym.sinc(w/2)**2, (w, -sym.oo, sym.oo))
E_f
```

**Copyright**

The notebooks are provided as [Open Educational Resource](https://de.wikipedia.org/wiki/Open_Educational_Resources). Feel free to use the notebooks for your own educational purposes. The text is licensed under [Creative Commons Attribution 4.0](https://creativecommons.org/licenses/by/4.0/), the code of the IPython examples under the [MIT license](https://opensource.org/licenses/MIT). Please attribute the work as follows: *Lecture Notes on Signals and Systems* by Sascha Spors.


```julia
using DifferentialEquations
using Plots
using LaTeXStrings
pgfplotsx()
```

    Plots.PGFPlotsXBackend()

Consider the simple reaction:

\begin{align}
    A + * &\longleftrightarrow A_*\\
    A_*   &\longleftrightarrow B + *\\
\end{align}

Both are elementary steps that occur in the liquid phase, and we will consider it in a few different solvent environments.  For this case, both reactions happen on similar time scales, and we'll consider the impact of solvation on degree of rate control.

```julia
function tCSTR(du, u, p, t)
    MAR = p["MAR"]
    PAR = p["PAR"]

    k10, k20, K10, K20, FAf, FBf, FS = PAR

    FA       = FAf
    thetaA   = u[:,1]
    FB       = FBf

    FT     = FA + FB + FS

    XA   = FA/FT
    XB   = FB/FT
    XS   = FS/FT

    thetastar = 1.0 .- thetaA

    #For A in solvent
    A12A = MAR[1]

    #For As in solvent
    A12As = MAR[2]

    #For B in solvent
    A12B = MAR[3]

    #For Transition State 1 in solvent
    A12TS1 = MAR[4]

    #For Transition State 2 in solvent
    A12TS2 = MAR[5]

    gammaA  = exp.(XS.^2*A12A)
    gammaAs = exp.(XS.^2*A12As)
    gammaB  = exp.(XS.^2*A12B)

    gammaTS1 = exp.(XS.^2*A12TS1)
    gammaTS2 = exp.(XS.^2*A12TS2)

    z1 = 1/K10*gammaAs./gammaA.*thetaA./XA./thetastar
    z2 = 1/K20*gammaB./gammaAs.*XB.*thetastar./thetaA

    r1 = k10*gammaA./gammaTS1.*XA.*thetastar.*(1 .- z1)
    r2 = k20*gammaAs./gammaTS2.*thetaA.*(1 .- z2)

    r2[isnan.(r2)] .= 0

    RAs =  r1[1] .- r2[1]

    du[1] = RAs
    return du, r1, r2, z1, z2
end
```

    tCSTR (generic function with 1 method)

```julia
function rate(p)
    MAR = p["MAR"]
    PAR = p["PAR"]

    k10, k20, K10, K20, FAf, FBf, FS = PAR

    FA       = FAf
    FB       = FBf

    FT     = FA + FB + FS

    XA   = FA/FT
    XB   = FB/FT
    XS   = FS/FT

    #For A in solvent
    A12A = MAR[1]

    #For As in solvent
    A12As = MAR[2]

    #For B in solvent
    A12B = MAR[3]

    #For Transition State 1 in solvent
    A12TS1 = MAR[4]

    #For Transition State 2 in solvent
    A12TS2 = MAR[5]

    gammaA  = exp.(XS.^2*A12A)
    gammaAs = exp.(XS.^2*A12As)
    gammaB  = exp.(XS.^2*A12B)

    gammaTS1 = exp.(XS.^2*A12TS1)
    gammaTS2 = exp.(XS.^2*A12TS2)

    rate = k10*k20*gammaA*gammaAs/gammaTS1/gammaTS2*XA/(k10/K10*gammaAs/gammaTS1 + k20*gammaAs/gammaTS2 + k10*gammaA/gammaTS1*XA)

    return rate
end
```

    rate (generic function with 1 method)

```julia
function DRC(p)
    MAR = p["MAR"]
    PAR = p["PAR"]

    k10, k20, K10, K20, FAf, FBf, FS = PAR

    FA       = FAf
    FB       = FBf

    FT     = FA + FB + FS

    XA   = FA/FT
    XB   = FB/FT
    XS   = FS/FT

    #For A in solvent
    A12A = MAR[1]

    #For As in solvent
    A12As = MAR[2]

    #For B in solvent
    A12B = MAR[3]

    #For Transition State 1 in solvent
    A12TS1 = MAR[4]

    #For Transition State 2 in solvent
    A12TS2 = MAR[5]

    gammaA  = exp.(XS.^2*A12A)
    gammaAs = exp.(XS.^2*A12As)
    gammaB  = exp.(XS.^2*A12B)

    gammaTS1 = exp.(XS.^2*A12TS1)
    gammaTS2 = exp.(XS.^2*A12TS2)

    DRCval = k20/k10*gammaAs/gammaTS2/(1/K10*gammaAs/gammaTS1 + k20/k10*gammaAs/gammaTS2 + gammaA/gammaTS1*XA)

    return DRCval
end
```

    DRC (generic function with 1 method)

```julia
K    = 1
Ns   = 0.0001
k10  = 0.10000
k20  = 100
K10  = 0.001
K20  = K/K10
V    = 1
FTOT = 100
tau  = 0.001

FAf     = 0.1
thetaA0 = 0.0
FBf     = 0.0

FS   = FTOT - FAf - FBf

var0     = [thetaA0]
span     = (0.0, 1000);
```

```julia
MARSET = zeros(5,)
MARVALS = range(-4, stop = 4, length = 50)

tfine  = range(0.0, stop = maximum(span), length = 1000)
r1out  = zeros(length(tfine), length(MARVALS), 5)
r2out  = zeros(length(tfine), length(MARVALS), 5)
z1out  = zeros(length(tfine), length(MARVALS), 5)
z2out  = zeros(length(tfine), length(MARVALS), 5)
cov    = zeros(length(tfine), length(MARVALS), 5)
XRC1out = zeros(length(MARVALS), 5)
XRC2out = zeros(length(MARVALS), 5)
#rSSout  = zeros(length(MARVALS), 5)
#XRC1SS  = zeros(length(MARVALS), 5)
#XRCA    = zeros(length(MARVALS), 5)

for j = 1:5
    MARSET    = zeros(5,)
    for i = 1:length(MARVALS)
        MARSET[j] = MARVALS[i]
        p0   = Dict("MAR" => MARSET, "PAR" => [k10, k20, K10, K20, FAf, FBf, FS])
        prob = ODEProblem(tCSTR, var0, span, p0)
        sol  = solve(prob, Rodas5(), abstol = 1e-16, reltol = 1e-16)
        solf = sol(tfine)
        thetaA  = solf[1,:]

        dut, rt1, rt2, zt1, zt2 = tCSTR([0.], thetaA, p0, tfine)

        cov[:, i, j]  = thetaA
        r1out[:, i, j] = rt1
        r2out[:, i, j] = rt2
        z1out[:, i, j] = zt1
        z2out[:, i, j] = zt2

        p1        = Dict("MAR" => MARSET, "PAR" => [1.0001*k10, k20, K10, K20, FAf, FBf, FS])
        p2        = Dict("MAR" => MARSET, "PAR" => [k10, 1.0001*k20, K10, K20, FAf, FBf, FS])
        pert1     = ODEProblem(tCSTR, var0, span, p1)
        pert2     = ODEProblem(tCSTR, var0, span, p2)
        solp1     = solve(pert1, Rodas5(), abstol = 1e-16, reltol = 1e-16)
        solp1f    = solp1(tfine)
        thetaAp1  = solp1f[1,:]
        solp2     = solve(pert2, Rodas5(), abstol = 1e-16, reltol = 1e-16)
        solp2f    = solp2(tfine)
        thetaAp2  = solp2f[1,:]

        dut1, rt11, rt21, zt11, zt21 = tCSTR([0.], thetaAp1, p1, tfine)
        dut2, rt12, rt22, zt12, zt22 = tCSTR([0.], thetaAp2, p2, tfine)
#       rSSout0  = rate(p0)
#       rSSout1  = rate(p1)
#       rSSout2  = rate(p2)

        XRC1  = k10/0.0001/k10*(rt21[end] - rt2[end])/rt2[end]
        XRC2  = k20/0.0001/k20*(rt22[end] - rt2[end])/rt2[end]
#       XRCSS = k10/0.0001/k10*(rSSout1 - rSSout0)/rSSout0
        XRC1out[i, j] = XRC1
        XRC2out[i, j] = XRC2
#       XRC1SS[i, j]  = XRCSS
#       XRCA[i, j]    = DRC(p0)
    end
end
```

```julia
plt1 = plot(
    xlim    = (-4, 4),
    xminorticks = 4,
    ylim = (0, 1),
    yminorticks = 4,
    tick_direction = :out,
    grid = :off,
    xguidefontsize = 26,
    yguidefontsize = 26,
    tickfontsize = 20,
    xlabel = L"\textrm{ln}(\gamma)",
    ylabel  = L"X_{RC_1}",
    fontfamily = "Times Roman",
    legendfontsize = 12,
    legend  = nothing,
    title = "(a)",
    titlelocation = :left,
    titlefontsize = 26
)

plt1 = plot!(plt1, MARVALS, XRC1out[:,1:2], lc = :white, markercolor = :white, markersize = [4 3], markershape = [:circle :square], labels = ["Solvate A" "Solvate A*"])
plt1 = plot!(plt1, MARVALS, XRC1out[:,3],   lc = :black,       ls = :solid,              lw = 1.0, label = "Solvate B")
plt1 = plot!(plt1, MARVALS, XRC1out[:,4:5], lc = [:blue :red], ls = [:dash :dashdotdot], lw = 1.0, labels = ["Solvate TS1" "Solvate TS2"])

display(plt1)
savefig(plt1, "CS41.pdf")
```

```julia
K    = 1
Ns   = 0.0001
k10  = 1e3
k20  = 1
K10  = 1e6
K20  = K/K10
V    = 1
FTOT = 100
tau  = 0.001

FAf     = 0.1
thetaA0 = 0.0
FBf     = 0.0

FS   = FTOT - FAf - FBf

var0     = [thetaA0]
span     = (0.0, 1000);
```

```julia
MARSET = zeros(5,)
MARVALS = range(-4, stop = 4, length = 50)

tfine  = range(0.0, stop = maximum(span), length = 1000)
r1out  = zeros(length(tfine), length(MARVALS), 5)
r2out  = zeros(length(tfine), length(MARVALS), 5)
z1out  = zeros(length(tfine), length(MARVALS), 5)
z2out  = zeros(length(tfine), length(MARVALS), 5)
cov    = zeros(length(tfine), length(MARVALS), 5)
XRC1out = zeros(length(MARVALS), 5)
XRC2out = zeros(length(MARVALS), 5)

for j = 1:5
    MARSET    = zeros(5,)
    for i = 1:length(MARVALS)
        MARSET[j] = MARVALS[i]
        p0   = Dict("MAR" => MARSET, "PAR" => [k10, k20, K10, K20, FAf, FBf, FS])
        prob = ODEProblem(tCSTR, var0, span, p0)
        sol  = solve(prob, Rodas5(), abstol = 1e-16, reltol = 1e-16)
        solf = sol(tfine)
        thetaA  = solf[1,:]

        dut, rt1, rt2, zt1, zt2 = tCSTR([0.], thetaA, p0, tfine)

        cov[:, i, j]  = thetaA
        r1out[:, i, j] = rt1
        r2out[:, i, j] = rt2
        z1out[:, i, j] = zt1
        z2out[:, i, j] = zt2

        p1        = Dict("MAR" => MARSET, "PAR" => [1.0001*k10, k20, K10, K20, FAf, FBf, FS])
        p2        = Dict("MAR" => MARSET, "PAR" => [k10, 1.0001*k20, K10, K20, FAf, FBf, FS])
        pert1     = ODEProblem(tCSTR, var0, span, p1)
        pert2     = ODEProblem(tCSTR, var0, span, p2)
        solp1     = solve(pert1, Rodas5(), abstol = 1e-16, reltol = 1e-16)
        solp1f    = solp1(tfine)
        thetaAp1  = solp1f[1,:]
        solp2     = solve(pert2, Rodas5(), abstol = 1e-16, reltol = 1e-16)
        solp2f    = solp2(tfine)
        thetaAp2  = solp2f[1,:]

        dut1, rt11, rt21, zt11, zt21 = tCSTR([0.], thetaAp1, p1, tfine)
        dut2, rt12, rt22, zt12, zt22 = tCSTR([0.], thetaAp2, p2, tfine)
        XRC1  = k10/0.0001/k10*(rt21[end] - rt2[end])/rt2[end]
        XRC2  = k20/0.0001/k20*(rt22[end] - rt2[end])/rt2[end]
        XRC1out[i, j] = XRC1
        XRC2out[i, j] = XRC2
    end
end
```

```julia
plt2 = plot(
    xlim    = (-4, 4),
    xminorticks = 4,
    ylim = (0, 1),
    yminorticks = 4,
    tick_direction = :out,
    grid = :off,
    xguidefontsize = 26,
    yguidefontsize = 26,
    tickfontsize = 20,
    xlabel = L"\textrm{ln}(\gamma)",
    ylabel  = L"X_{RC_1}",
    fontfamily = "Times Roman",
    legendfontsize = 12,
    legend  = nothing,
    title = "(c)",
    titlelocation = :left,
    titlefontsize = 26
)

plt2 = plot!(plt2, MARVALS, XRC1out[:,1:2], lc = :white, markercolor = :white, markersize = [4 3], markershape = [:circle :square], labels = ["Solvate A" "Solvate A*"])
plt2 = plot!(plt2, MARVALS, XRC1out[:,3],   lc = :black,       ls = :solid,              lw = 1.0, label = "Solvate B")
plt2 = plot!(plt2, MARVALS, XRC1out[:,4:5], lc = [:blue :red], ls = [:dash :dashdotdot], lw = 1.0, labels = ["Solvate TS1" "Solvate TS2"])
display(plt2)
savefig(plt2, "CS42.pdf")
```

```julia
K    = 1
Ns   = 0.0001
k10  = 166.5
k20  = 1
K10  = 200
K20  = K/K10
V    = 1
FTOT = 100
tau  = 0.001

FAf     = 0.1
thetaA0 = 0.0
FBf     = 0.0

FS   = FTOT - FAf - FBf

var0     = [thetaA0]
span     = (0.0, 1000);
```

```julia
MARSET = zeros(5,)
MARVALS = range(-4, stop = 4, length = 50)

tfine  = range(0.0, stop = maximum(span), length = 1000)
r1out  = zeros(length(tfine), length(MARVALS), 5)
r2out  = zeros(length(tfine), length(MARVALS), 5)
z1out  = zeros(length(tfine), length(MARVALS), 5)
z2out  = zeros(length(tfine), length(MARVALS), 5)
cov    = zeros(length(tfine), length(MARVALS), 5)
XRC1out = zeros(length(MARVALS), 5)
XRC2out = zeros(length(MARVALS), 5)

for j = 1:5
    MARSET    = zeros(5,)
    for i = 1:length(MARVALS)
        MARSET[j] = MARVALS[i]
        p0   = Dict("MAR" => MARSET, "PAR" => [k10, k20, K10, K20, FAf, FBf, FS])
        prob = ODEProblem(tCSTR, var0, span, p0)
        sol  = solve(prob, Rodas5(), abstol = 1e-16, reltol = 1e-16)
        solf = sol(tfine)
        thetaA  = solf[1,:]

        dut, rt1, rt2, zt1, zt2 = tCSTR([0.], thetaA, p0, tfine)

        cov[:, i, j]  = thetaA
        r1out[:, i, j] = rt1
        r2out[:, i, j] = rt2
        z1out[:, i, j] = zt1
        z2out[:, i, j] = zt2

        p1        = Dict("MAR" => MARSET, "PAR" => [1.0001*k10, k20, K10, K20, FAf, FBf, FS])
        p2        = Dict("MAR" => MARSET, "PAR" => [k10, 1.0001*k20, K10, K20, FAf, FBf, FS])
        pert1     = ODEProblem(tCSTR, var0, span, p1)
        pert2     = ODEProblem(tCSTR, var0, span, p2)
        solp1     = solve(pert1, Rodas5(), abstol = 1e-16, reltol = 1e-16)
        solp1f    = solp1(tfine)
        thetaAp1  = solp1f[1,:]
        solp2     = solve(pert2, Rodas5(), abstol = 1e-16, reltol = 1e-16)
        solp2f    = solp2(tfine)
        thetaAp2  = solp2f[1,:]

        dut1, rt11, rt21, zt11, zt21 = tCSTR([0.], thetaAp1, p1, tfine)
        dut2, rt12, rt22, zt12, zt22 = tCSTR([0.], thetaAp2, p2, tfine)
        XRC1  = k10/0.0001/k10*(rt21[end] - rt2[end])/rt2[end]
        XRC2  = k20/0.0001/k20*(rt22[end] - rt2[end])/rt2[end]
        XRC1out[i, j] = XRC1
        XRC2out[i, j] = XRC2
    end
end
```

```julia
plt3 = plot(
    xlim    = (-4, 4),
    xminorticks = 4,
    ylim = (0, 1),
    yminorticks = 4,
    tick_direction = :out,
    grid = :off,
    xguidefontsize = 26,
    yguidefontsize = 26,
    tickfontsize = 20,
    xlabel = L"\textrm{ln}(\gamma)",
    ylabel  = L"X_{RC_1}",
    fontfamily = "Times Roman",
    legendfontsize = 12,
    legend  = nothing,
    title = "(d)",
    titlelocation = :left,
    titlefontsize = 26
)

plt3 = plot!(plt3, MARVALS, XRC1out[:,1:2], lc = :white, markercolor = :white, markersize = [4 3], markershape = [:circle :square], labels = ["Solvate A" "Solvate A*"])
plt3 = plot!(plt3, MARVALS, XRC1out[:,3],   lc = :black,       ls = :solid,              lw = 1.0, label = "Solvate B")
plt3 = plot!(plt3, MARVALS, XRC1out[:,4:5], lc = [:blue :red], ls = [:dash :dashdotdot], lw = 1.0, labels = ["Solvate TS1" "Solvate TS2"])
display(plt3)
savefig(plt3, "CS43.pdf")
```

```julia
K    = 1
Ns   = 0.0001
k10  = 835
k20  = 1
K10  = 5000
K20  = K/K10
V    = 1
FTOT = 100
tau  = 0.001

FAf     = 0.1
thetaA0 = 0.0
FBf     = 0.0

FS   = FTOT - FAf - FBf

var0     = [thetaA0]
span     = (0.0, 1000);
```

```julia
MARSET = zeros(5,)
MARVALS = range(-4, stop = 4, length = 50)

tfine  = range(0.0, stop = maximum(span), length = 1000)
r1out  = zeros(length(tfine), length(MARVALS), 5)
r2out  = zeros(length(tfine), length(MARVALS), 5)
z1out  = zeros(length(tfine), length(MARVALS), 5)
z2out  = zeros(length(tfine), length(MARVALS), 5)
cov    = zeros(length(tfine), length(MARVALS), 5)
XRC1out = zeros(length(MARVALS), 5)
XRC2out = zeros(length(MARVALS), 5)

for j = 1:5
    MARSET    = zeros(5,)
    for i = 1:length(MARVALS)
        MARSET[j] = MARVALS[i]
        p0   = Dict("MAR" => MARSET, "PAR" => [k10, k20, K10, K20, FAf, FBf, FS])
        prob = ODEProblem(tCSTR, var0, span, p0)
        sol  = solve(prob, Rodas5(), abstol = 1e-16, reltol = 1e-16)
        solf = sol(tfine)
        thetaA  = solf[1,:]

        dut, rt1, rt2, zt1, zt2 = tCSTR([0.], thetaA, p0, tfine)

        cov[:, i, j]  = thetaA
        r1out[:, i, j] = rt1
        r2out[:, i, j] = rt2
        z1out[:, i, j] = zt1
        z2out[:, i, j] = zt2

        p1        = Dict("MAR" => MARSET, "PAR" => [1.0001*k10, k20, K10, K20, FAf, FBf, FS])
        p2        = Dict("MAR" => MARSET, "PAR" => [k10, 1.0001*k20, K10, K20, FAf, FBf, FS])
        pert1     = ODEProblem(tCSTR, var0, span, p1)
        pert2     = ODEProblem(tCSTR, var0, span, p2)
        solp1     = solve(pert1, Rodas5(), abstol = 1e-16, reltol = 1e-16)
        solp1f    = solp1(tfine)
        thetaAp1  = solp1f[1,:]
        solp2     = solve(pert2, Rodas5(), abstol = 1e-16, reltol = 1e-16)
        solp2f    = solp2(tfine)
        thetaAp2  = solp2f[1,:]

        dut1, rt11, rt21, zt11, zt21 = tCSTR([0.], thetaAp1, p1, tfine)
        dut2, rt12, rt22, zt12, zt22 = tCSTR([0.], thetaAp2, p2, tfine)
        XRC1  = k10/0.0001/k10*(rt21[end] - rt2[end])/rt2[end]
        XRC2  = k20/0.0001/k20*(rt22[end] - rt2[end])/rt2[end]
        XRC1out[i, j] = XRC1
        XRC2out[i, j] = XRC2
    end
end

# display(XRC1out)
```

```julia
plt4 = plot(
    xlim    = (-4, 4),
    xminorticks = 4,
    ylim = (0, 1),
    yminorticks = 4,
    tick_direction = :out,
    grid = :off,
    xguidefontsize = 26,
    yguidefontsize = 26,
    tickfontsize = 20,
    xlabel = L"\textrm{ln}(\gamma)",
    ylabel  = L"X_{RC_1}",
    fontfamily = "Times Roman",
    legendfontsize = 12,
    legend  = nothing,
    title = "(b)",
    titlelocation = :left,
    titlefontsize = 26
)

plt4 = plot!(plt4, MARVALS, XRC1out[:,1:2], lc = :white, markercolor = :white, markersize = [5 4], markershape = [:circle :square], labels = ["Solvate A" "Solvate A*"])
plt4 = plot!(plt4, MARVALS, XRC1out[:,3],   lc = :black,       ls = :solid,              lw = 1.0, label = "Solvate B")
plt4 = plot!(plt4, MARVALS, XRC1out[:,4:5], lc = [:blue :red], ls = [:dash :dashdotdot], lw = 1.0, labels = ["Solvate TS1" "Solvate TS2"])
display(plt4)
savefig(plt4, "CS44.pdf")
```

```julia
K    = 1
Ns   = 0.0001
k10  = 1e-4
k20  = 1
K10  = 1
K20  = K/K10
V    = 1
FTOT = 100
tau  = 0.001

FAf     = 0.1
thetaA0 = 0.0
FBf     = 0.0

FS   = FTOT - FAf - FBf

var0     = [thetaA0]
span     = (0.0, 1000);
```

```julia
MARSET = zeros(5,)
MARVALS = range(-4, stop = 4, length = 50)

tfine  = range(0.0, stop = maximum(span), length = 1000)
r1out  = zeros(length(tfine), length(MARVALS), 5)
r2out  = zeros(length(tfine), length(MARVALS), 5)
z1out  = zeros(length(tfine), length(MARVALS), 5)
z2out  = zeros(length(tfine), length(MARVALS), 5)
cov    = zeros(length(tfine), length(MARVALS), 5)
XRC1out = zeros(length(MARVALS), 5)
XRC2out = zeros(length(MARVALS), 5)

for j = 1:5
    MARSET    = zeros(5,)
    for i = 1:length(MARVALS)
        MARSET[j] = MARVALS[i]
        p0   = Dict("MAR" => MARSET, "PAR" => [k10, k20, K10, K20, FAf, FBf, FS])
        prob = ODEProblem(tCSTR, var0, span, p0)
        sol  = solve(prob, Rodas5(), abstol = 1e-16, reltol = 1e-16)
        solf = sol(tfine)
        thetaA  = solf[1,:]

        dut, rt1, rt2, zt1, zt2 = tCSTR([0.], thetaA, p0, tfine)

        cov[:, i, j]  = thetaA
        r1out[:, i, j] = rt1
        r2out[:, i, j] = rt2
        z1out[:, i, j] = zt1
        z2out[:, i, j] = zt2

        p1        = Dict("MAR" => MARSET, "PAR" => [1.0001*k10, k20, K10, K20, FAf, FBf, FS])
        p2        = Dict("MAR" => MARSET, "PAR" => [k10, 1.0001*k20, K10, K20, FAf, FBf, FS])
        pert1     = ODEProblem(tCSTR, var0, span, p1)
        pert2     = ODEProblem(tCSTR, var0, span, p2)
        solp1     = solve(pert1, Rodas5(), abstol = 1e-16, reltol = 1e-16)
        solp1f    = solp1(tfine)
        thetaAp1  = solp1f[1,:]
        solp2     = solve(pert2, Rodas5(), abstol = 1e-16, reltol = 1e-16)
        solp2f    = solp2(tfine)
        thetaAp2  = solp2f[1,:]

        dut1, rt11, rt21, zt11, zt21 = tCSTR([0.], thetaAp1, p1, tfine)
        dut2, rt12, rt22, zt12, zt22 = tCSTR([0.], thetaAp2, p2, tfine)
        XRC1  = k10/0.0001/k10*(rt21[end] - rt2[end])/rt2[end]
        XRC2  = k20/0.0001/k20*(rt22[end] - rt2[end])/rt2[end]
        XRC1out[i, j] = XRC1
        XRC2out[i, j] = XRC2
    end
end

# display(XRC1out)
```

```julia
plt5 = plot(
    xlim    = (-4, 4),
    xminorticks = 4,
    ylim = (0, 1),
    yminorticks = 4,
    tick_direction = :out,
    grid = :off,
    xguidefontsize = 26,
    yguidefontsize = 26,
    tickfontsize = 20,
    xlabel = L"\textrm{ln}(\gamma)",
    ylabel  = L"X_{RC_1}",
    fontfamily = "Times Roman",
    legendfontsize = 12,
    legend  = nothing,
    title = "(b)",
    titlelocation = :left,
    titlefontsize = 26
)

plt5 = plot!(plt5, MARVALS, XRC1out[:,1:2], lc = :white, markercolor = :white, markersize = [5 4], markershape = [:circle :square], labels = ["Solvate A" "Solvate A*"])
plt5 = plot!(plt5, MARVALS, XRC1out[:,3],   lc = :black,       ls = :solid,              lw = 1.0, label = "Solvate B")
plt5 = plot!(plt5, MARVALS, XRC1out[:,4:5], lc = [:blue :red], ls = [:dash :dashdotdot], lw = 1.0, labels = ["Solvate TS1" "Solvate TS2"])
display(plt5)
savefig(plt5, "CS45.pdf")
```


```python
import numpy as np
import math as mt
import sympy as sym
```

```python
theta = sym.Symbol('theta')
alpha = sym.Symbol('alpha')

costheta = sym.Symbol('costheta')
cosalpha = sym.Symbol('cosalpha')

sintheta = sym.Symbol('sintheta')
sinalpha = sym.Symbol('sinalpha')

l1 = sym.Symbol('l1')
l2 = sym.Symbol('l2')
```

```python
var('theta alpha l1 l2 costheta cosalpha sintheta sinalpha')
```

    (theta, alpha, l1, l2, costheta, cosalpha, sintheta, sinalpha)

```python
A_1 = np.array([[costheta, -sintheta, 0, l1*sintheta],[sintheta,costheta, 0, l1*costheta],[0,0,1,0],[0,0,0,1]])

A_2 = np.array([[cosalpha,-sinalpha, 0,l2*sinalpha],[sinalpha, cosalpha, 0, l2*cosalpha],[0,0,1,0],[0,0,0,1]])
```

```python
A_1 A_2
```

```python
a1 = matrix(SR, [[costheta, -sintheta, 0, l1*sintheta],[sintheta,costheta, 0, l1*costheta],[0,0,1,0],[0,0,0,1]])
a2 = matrix(SR,[[cosalpha,-sinalpha, 0,l2*sinalpha],[sinalpha, cosalpha, 0, l2*cosalpha],[0,0,1,0],[0,0,0,1]])
```

```python
a1*a2
```

    [                    cosalpha*costheta - sinalpha*sintheta                    -costheta*sinalpha - cosalpha*sintheta                                                         0 costheta*l2*sinalpha - cosalpha*l2*sintheta + l1*sintheta]
    [                    costheta*sinalpha + cosalpha*sintheta                     cosalpha*costheta - sinalpha*sintheta                                                         0 cosalpha*costheta*l2 + l2*sinalpha*sintheta + costheta*l1]
    [                                                        0                                                         0                                                         1                                                         0]
    [                                                        0                                                         0                                                         0                                                         1]

```python

```