###  ¿ Cómo se mueve un péndulo ?

> Se dice que un sistema cualquiera, mecánico, eléctrico, neumático, etc., es un oscilador armónico si, cuando se deja en libertad fuera de su posición de equilibrio, vuelve hacia ella describiendo oscilaciones sinusoidales, o sinusoidales amortiguadas en torno a dicha posición estable.
- https://es.wikipedia.org/wiki/Oscilador_armónico

Referencias:
   - http://matplotlib.org
   - https://seaborn.pydata.org
   - http://www.numpy.org
   - http://ipywidgets.readthedocs.io/en/latest/index.html

**En realidad esto es el estudio de oscilaciones. **
___

Los sistemas mas sencillos a estudiar en oscilaciones son el sistema ` masa-resorte` y el `péndulo simple`.

\begin{align}
\ddot{x} +  \omega_{0}^2 x &= 0, \quad \omega_{0} = \sqrt{\frac{k}{m}}\notag\\
\ddot{\theta} + \omega_{0}^{2}\, \theta &= 0, \quad\mbox{donde}\quad \omega_{0}^2 = \frac{g}{l}
\end{align}

### Sistema `masa-resorte`

La solución a este sistema `masa-resorte`se explica en términos de la segunda ley de Newton. Para este caso, si la masa permanece constante y solo consideramos la dirección en $x$. Entonces,

\begin{equation}
F = m\ddot{x} = m \frac{d\dot{x}}{dt} = m \frac{d^2x}{dt^2}
\end{equation}

¿ Cuál es la fuerza? ** Ley de Hooke! **
\begin{equation}
F = -k x, \quad k > 0
\end{equation}
Vemos que la fuerza se opone al desplazamiento y su intensidad es proporcional al mismo.  Y $k$ es la constante elástica o recuperadora del resorte.

Cuya solución se escribe como
\begin{equation}
x(t) = A \cos(\omega_{o} t) + B \sin(\omega_{o} t)
\end{equation}
Y su primera derivada (velocidad) sería
\begin{equation}
\dot{x}(t) = \omega_{0}[- A \sin(\omega_{0} t) + B\cos(\omega_{0}t)]
\end{equation}

- **¿Cómo se ven las gráficas de `x vs t`, tanto para la posición y velocidad?**

_Esta instrucción es para que las gráficas aparezcan dentro de este entorno._

```python
%matplotlib inline
```

_Esta es la librería con todas las instrucciones para realizar gráficos. _

```python
import matplotlib.pyplot as plt
```

```python
import matplotlib as mpl
label_size = 14
mpl.rcParams['xtick.labelsize'] = label_size
mpl.rcParams['ytick.labelsize'] = label_size
```

 _Y esta es la librería con todas las funciones matemáticas necesarias._

```python
import numpy as np
```

```python
t = np.linspace(0, 50, 100)
plt.figure(figsize = (7, 4))
plt.plot(t, .5*np.cos(.5 *t) + .1 * np.sin(.5 *t), '-', lw = 1, ms = 4,
         label = '$x(t)$')
plt.plot(t, .5*.1*np.cos(.5*t) - .5*.5 * np.sin(.5*t), 'ro-', lw = 1, ms = 4,
         label = r'$\dot{x(t)}$')
plt.xlabel('$t$', fontsize = 20)
plt.show()
```

```python
plt.figure(figsize = (7, 4))
plt.scatter(t, .5*np.cos(.5 *t) + .1 * np.sin(.5 *t), lw = 0, c = 'red',
            label = '$x(t)$')
plt.plot(t, .5*np.cos(.5 *t) + .1 * np.sin(.5 *t), 'r-', lw = 1)
plt.scatter(t, .5*.1*np.cos(.5*t) - .5*.5 * np.sin(.5*t), lw = 0, c = 'b',
            label = r'$\dot{x(t)}$')
plt.plot(t, .5*.1*np.cos(.5*t) - .5*.5 * np.sin(.5*t), 'b-', lw = 1)
plt.xlabel('$t$', fontsize = 20)
plt.legend(loc = 'best')
plt.show()
```

Y si consideramos un conjunto de frecuencias de oscilación, entonces

```python
frecuencias = np.array([.1, .2 , .5, .6])
plt.figure(figsize = (7, 4))
for f in frecuencias:
    plt.plot(t, .5 * np.cos(f * t) + .1 * np.sin(f * t), '*-')
plt.xlabel('$t$', fontsize = 16)
plt.ylabel('$x(t)$', fontsize = 16)
plt.title('Oscilaciones', fontsize = 16)
plt.show()
```

Estos colores, son el default de `matplotlib`, sin embargo existe otra librería dedicada, entre otras cosas, a la presentación de gráficos.

```python
import seaborn as sns
sns.set(style='ticks', palette='Set2')
```

```python
frecuencias = np.array([.1, .2 , .5, .6])
plt.figure(figsize = (7, 4))
for f in frecuencias:
    plt.plot(t, .5 * np.cos(f * t) + .1 * np.sin(f * t), 'o-',
             label = '$\omega_0 = %s$'%f)
plt.xlabel('$t$', fontsize = 16)
plt.ylabel('$x(t)$', fontsize = 16)
plt.title('Oscilaciones', fontsize = 16)
plt.legend(loc='center left', bbox_to_anchor=(1.05, 0.5), prop={'size': 14})
plt.show()
```

Si queremos tener manipular un poco mas las cosas, hacemos uso de lo siguiente:

```python
from ipywidgets import *
```

```python
def masa_resorte(t = 0):
    fig = plt.figure()
    ax = fig.add_subplot(1, 1, 1)
    ax.plot(.5 * np.cos(.5 * t) + .1 * np.sin(.5 * t), [0],  'ko', ms = 10)
    ax.set_xlim(xmin = -0.6, xmax = .6)
    ax.axvline(x=0, color = 'r')
    ax.axhline(y=0, color = 'grey', lw = 1)
    fig.canvas.draw()

interact(masa_resorte, t = (0, 50,.01));
```

La opción de arriba generalmente será lenta, así que lo recomendable es usar `interact_manual`.

```python
def masa_resorte(t = 0):
    fig = plt.figure()
    ax = fig.add_subplot(1, 1, 1)
    ax.plot(.5 * np.cos(.5 * t) + .1 * np.sin(.5 * t), [0],  'ko', ms = 10)
    ax.set_xlim(xmin = -0.6, xmax = .6)
    ax.axvline(x=0, color = 'r')
    ax.axhline(y=0, color = 'grey', lw = 1)
    fig.canvas.draw()
interact_manual(masa_resorte, t = (0, 50,.01));
```

### Péndulo simple

Ahora, si fijamos nuestra atención al movimiento de un péndulo simple _(oscilaciones pequeñas)_. La ecuación diferencial a resolver tiene la misma forma. La diferencia más evidente es como hemos definido a $\omega_{0}$. Esto quiere decir que,

\begin{equation}
\theta(t) = A\cos(\omega_{0} t) + B\sin(\omega_{0}t)
\end{equation}

Si graficamos la ecuación de arriba vamos a encontrar un comportamiento muy similar al ya discutido anteriormente. Es por ello que ahora veremos el movimiento en el plano $xy$. Es decir,

\begin{align}
x &= l \sin(\theta), \quad
y = l \cos(\theta)
\end{align}

```python
def theta_t(a, b, g, l, t):
    omega_0 = np.sqrt(g/l)
    return a * np.cos(omega_0 * t) + b * np.sin(omega_0 * t)
```

```python
def pendulo_simple(t = 0):
    fig = plt.figure(figsize = (5,5))
    ax = fig.add_subplot(1, 1, 1)
    x = 2 * np.sin(theta_t(.4, .6, 9.8, 2, t))
    y =  - 2 * np.cos(theta_t(.4, .6, 9.8, 2, t))
    ax.plot(x, y, 'ko', ms = 10)
    ax.plot([0], [0], 'rD')
    ax.plot([0, x ], [0, y], 'k-', lw = 1)
    ax.set_xlim(xmin = -2.2, xmax = 2.2)
    ax.set_ylim(ymin = -2.2, ymax = .2)
    fig.canvas.draw()
interact_manual(pendulo_simple, t = (0, 10,.01));
```

### Condiciones iniciales

Realmente lo que se tiene que resolver es,

\begin{equation}
\theta(t) = \theta(0) \cos(\omega_{0} t) + \frac{\dot{\theta}(0)}{\omega_{0}} \sin(\omega_{0} t)
\end{equation}

> **Actividad.** Modificar el programa anterior para incorporar las condiciones iniciales.

```python
# Solución:
def theta_t():

    return
```

```python
def pendulo_simple(t = 0):
    fig = plt.figure(figsize = (5,5))
    ax = fig.add_subplot(1, 1, 1)
    x = 2 * np.sin(theta_t( , t))
    y =  - 2 * np.cos(theta_t(, t))
    ax.plot(x, y, 'ko', ms = 10)
    ax.plot([0], [0], 'rD')
    ax.plot([0, x ], [0, y], 'k-', lw = 1)
    ax.set_xlim(xmin = -2.2, xmax = 2.2)
    ax.set_ylim(ymin = -2.2, ymax = .2)
    fig.canvas.draw()
interact_manual(pendulo_simple, t = (0, 10,.01));
```

### Espacio fase  $(q, p)$

Recuerden el momento lineal se define como
$
 p = m v,
$
donde $m$ es la masa y $v$ la velocidad. Y la posición y velocidad para el sistema `masa-resorte` se escriben como:

\begin{align}
x(t) &= x(0) \cos(\omega_{o} t) + \frac{\dot{x}(0)}{\omega_{0}} \sin(\omega_{o} t)\\
\dot{x}(t) &= -\omega_{0}x(0) \sin(\omega_{0} t) + \dot{x}(0)\cos(\omega_{0}t)]
\end{align}

```python
k = 3  #constante elástica [N]/[m]
m = 1 # [kg]
omega_0 = np.sqrt(k/m)
x_0 = .5
x_0_dot = .1
```

```python
t = np.linspace(0, 50, 300)
```

```python
x_t = x_0 *np.cos(omega_0 *t) + (x_0_dot/omega_0) * np.sin(omega_0 *t)
x_t_dot = -omega_0 * x_0 * np.sin(omega_0 * t) + x_0_dot * np.cos(omega_0 * t)
```

```python
plt.figure(figsize = (7, 4))
plt.plot(t, x_t, label = '$x(t)$', lw = 1)
plt.plot(t, x_t_dot, label = '$\dot{x}(t)$', lw = 1)
plt.legend(loc='center left', bbox_to_anchor=(1.01, 0.5), prop={'size': 14})
plt.xlabel('$t$', fontsize = 18)
plt.show()
```

```python
plt.figure(figsize = (5, 5))
plt.plot(x_t, x_t_dot/omega_0, 'ro', ms = 2)
plt.xlabel('$x(t)$', fontsize = 18)
plt.ylabel('$\dot{x}(t)/\omega_0$', fontsize = 18)
plt.show()
```

```python
plt.figure(figsize = (5, 5))
plt.scatter(x_t,  x_t_dot/omega_0, cmap = 'viridis', c = x_t_dot, s = 8, lw = 0)
plt.xlabel('$x(t)$', fontsize = 18)
plt.ylabel('$\dot{x}(t)/\omega_0$', fontsize = 18)
plt.show()
```

#### Multiples condiciones iniciales

```python
k = 3  #constante elástica [N]/[m]
m = 1 # [kg]
omega_0 = np.sqrt(k/m)
```

```python
t = np.linspace(0, 50, 50)
```

```python
x_0s = np.array([.7, .5, .25, .1])
x_0s_dot = np.array([.2, .1, .05, .01])
cmaps = np.array(['viridis', 'inferno', 'magma', 'plasma'])
```

```python
plt.figure(figsize = (6, 6))
for indx, x_0 in enumerate(x_0s):
    x_t = x_0 *np.cos(omega_0 *t) + (x_0s_dot[indx]/omega_0) * np.sin(omega_0 *t)
    x_t_dot = -omega_0 * x_0 * np.sin(omega_0 * t) + x_0s_dot[indx] * np.cos(omega_0 * t)
    plt.scatter(x_t,  x_t_dot/omega_0, cmap = cmaps[indx],
                c = x_t_dot, s = 10,
                lw = 0)
    plt.xlabel('$x(t)$', fontsize = 18)
    plt.ylabel('$\dot{x}(t)/\omega_0$', fontsize = 18)
    #plt.legend(loc='center left', bbox_to_anchor=(1.05, 0.5))
```

Trayectorias del oscilador armónico simple en el espacio fase $(x,\, \dot{x}\,/\omega_0)$ para diferentes valores de la energía.

Created with Jupyter by Lázaro Alonso.

```python

```


Trusted Notebook" width="500 px" align="left">

## _*The Simon Algorithm*_

The latest version of this notebook is available on https://github.com/qiskit/qiskit-tutorial.

The Simon algorithm is an example that shows a quantum algorithm can solve a problem exponentially efficient than any classical algorithms. Like the Grover search, it depends on the existence of a blackbox (or, oracle) function that returns a predefined output over specific input or query. In the query-complexity setting, one cares only about how many queries are required to solve a specific problem, but does not care how the blackbox is realized. However, in this tutorial we have to implement it using the unit gates available in QISKit, just like we have done with the Grover search.

We first describe the problem addressed by the Simon algorithm, show the steps of the algorithm and the construction of the blackbox function, and present the experimental results on simulators and real devices.

***
### Contributors
Rudy Raymond

## The Problem

The Simon algorithm deals with finding a hidden integer $s \in \{0,1\}^n$ from an oracle $f_s$ that satisfies $f_s(x) = f_s(y)$ if and only if $y = x \oplus s$ for all $x \in \{0,1\}^n$.  Here, the $\oplus$ is the bitwise XOR operation. Thus, if $s = 0\ldots 0$, i.e., the all-zero bitstring, then $f_s$ is a 1-to-1 (or, permutation) function. Otherwise, if $s \neq 0\ldots 0$, then $f_s$ is a 2-to-1 function.

The Simon algorithm can find the hidden integer using only $O(n)$ queries to the blackbox function, while any classical algorithms require $\Omega(\sqrt{2^n})$ queries.

## The Algorithm to Find the Hidden Integer

The Simon algorithm finds the hidden integer by combining quantum algorithm with postprocessing on classical computers as below.

1. Prepare two quantum registers each of length $n$ that are initialized to all-zero bitstring: the first one as input and the second one as output of the blackbox function.
$$
|0\rangle |0\rangle
$$

2. Apply Hadamard gates to the first register to create superposition of all possible inputs.
$$
H^{\otimes n} |0\rangle |0\rangle = \frac{1}{\sqrt{2^n}} \sum_{x=0}^{2^n-1} |x\rangle |0\rangle
$$

3. Query the blackbox function to obtain the answer to queries on the second register.
$$
\frac{1}{\sqrt{2^n}} \sum_{x=0}^{2^n-1} U_{f_s}|x\rangle |0\rangle = \frac{1}{\sqrt{2^n}} \sum_{x=0}^{2^n-1} |x\rangle |f_s(x)\rangle
$$

4. Apply Hadamard gates to the first register.
$$
\frac{1}{\sqrt{2^n}} \sum_{x=0}^{2^n-1} H^{\otimes n}|x\rangle |f_s(x)\rangle = \frac{1}{2^n} \sum_{y=0}^{2^n-1}\sum_{x=0}^{2^n-1} (-1)^{x \cdot y}|y\rangle |f_s(x)\rangle = \frac{1}{2^n} \sum_{y=0}^{2^n-1} |y\rangle \sum_{x=0}^{2^n-1} ( (-1)^{x \cdot y} + (-1)^{(x\oplus s) \cdot y} )  |f_s(x)\rangle
$$

    Notice that at the right-hand side of the above equation, because $(-1)^{(x\oplus s) \cdot y} = (-1)^{x\cdot y +  s \cdot y}$ we can conclude that the probability amplitude of the basis state $|y\rangle |f_s(x)\rangle$ is $(-1)^{x\cdot y} (1 + (-1)^{s \cdot y}  )$, which is zero if and only if $s \cdot y = 1$. Thus, measuring the first register will always give $y$ such that $s \cdot y = 0$. Moreover, we can obtain many different $y$'s by repeating Step 1 to 4.

5. Repeat Step 1 to 4 for $m$ times to obtain $y_1, y_2, \ldots, y_m$.

6. **(Classical post-processing)** Let $\mathbf{Y}$ be an $m\times n$ matrix whose $i$-th row is $y_i$ in Step 5, and $\vec{s}$ be the column vector whose $j$-th element is the $j$-th bit of $s$. Solve the following system of linear equations to obtain $s$.
$$
\mathbf{Y} \vec{s} = 0
$$

## The Circuit

We now implement the Simon algorithm with Qiskit by first preparing the environment.

```python
#initialization
import sys, getpass
import matplotlib.pyplot as plt
%matplotlib inline
import numpy as np

# importing Qiskit
from qiskit import QuantumCircuit, ClassicalRegister, QuantumRegister
from qiskit import available_backends, execute, register, get_backend, compile

try:
    sys.path.append("../../") # go to parent dir
    import Qconfig
    qx_config = {
        "APItoken": Qconfig.APItoken,
        "url": Qconfig.config['url']}
    print('Qconfig loaded from %s.' % Qconfig.__file__)
except:
    APItoken = getpass.getpass('Please input your token and hit enter: ')
    qx_config = {
        "APItoken": APItoken,
        "url":"https://quantumexperience.ng.bluemix.net/api"}
    print('Qconfig.py not found in qiskit-tutorial directory; Qconfig loaded using user input.')

# import basic plot tools
from qiskit.tools.visualization import plot_histogram
```

    Please input your token and hit enter: ········
    Qconfig.py not found in qiskit-tutorial directory; Qconfig loaded using user input.

We then set the hidden bitstring $s$ that will be used to construct the circuit of the blackbox function (whose details will be given later). The number of qubits used in the experiment is twice the length of the bitstring $s$.

```python
s = "010101" # the hidden bitstring

assert 1 < len(s) < 20, "The length of s must be between 2 and 19"

for c in s:
    assert c == "0" or c == "1", "s must be a bitstring of '0' and '1'"

n = len(s) #the length of the bitstring
```

We then use Qiskit to create the circuit of the Simon algorithm prior the querying the blackbox function.

```python
# Step 1
# Creating registers
# qubits for querying the oracle and recording its output
qr = QuantumRegister(2*n)
# for recording the measurement on the first register of qr
cr = ClassicalRegister(n)

circuitName = "Simon"
simonCircuit = QuantumCircuit(qr, cr)

# Step 2
# Apply Hadamard gates before querying the oracle
for i in range(n):
    simonCircuit.h(qr[i])

# Apply barrier to mark the beginning of the blackbox function
simonCircuit.barrier()
```

    <qiskit.extensions.standard.barrier.Barrier at 0x10bb5dba8>

### Constructing a Circuit for the Blackbox Function

We now details the construction of 1-to-1 and 2-to-1 permutation circuit of the blackbox function. Let us assume the blackbox function receive $|x\rangle|0\rangle$ as input. With regards to a predetermined $s$, the blackbox function writes its output to the second register so that it transforms the input to $|x\rangle|f_s(x)\rangle$ such that $f(x) = f(x\oplus s)$ for all $x \in \{0,1\}^n$.

Such a blackbox function can be realized by the following procedures.

-  Copy the content of the first register to the second register.
$$
|x\rangle|0\rangle \rightarrow |x\rangle|x\rangle
$$

-  **(Creating 1-to-1 or 2-to-1 mapping)** If $s$ is not all-zero, then there is the least index $j$ so that $s_j = 1$. If $x_j = 0$, then XOR the second register with $s$. Otherwise, do not change the second register.
$$
|x\rangle|x\rangle \rightarrow |x\rangle|x \oplus s\rangle~\mbox{if}~x_j = 0~\mbox{for the least index j}
$$

-  **(Creating random permutation)** Randomly permute and flip the qubits of the second register.
$$
|x\rangle|y\rangle \rightarrow |x\rangle|f_s(y)\rangle
$$

Below is the circuit of the blacbox function based on the above procedures.

```python
# Step 3 query the blackbox function
# copy the content of the first register to the second register
for i in range(n):
    simonCircuit.cx(qr[i], qr[n+i])

# get the least index j such that s_j is "1"
j = -1
for i, c in enumerate(s):
    if c == "1":
        j = i
        break

# Creating 1-to-1 or 2-to-1 mapping with the j-th qubit of x as control to XOR the second register with s
for i, c in enumerate(s):
    if c == "1" and j >= 0:
        simonCircuit.cx(qr[j], qr[n+i]) #the i-th qubit is flipped if s_i is 1

# get random permutation of n qubits
perm = list(np.random.permutation(n))

#initial position
init = list(range(n))
i = 0
while i < n:
    if init[i] != perm[i]:
        k = perm.index(init[i])
        simonCircuit.swap(qr[n+i], qr[n+k]) #swap qubits
        init[i], init[k] = init[k], init[i] #marked swapped qubits
    else:
        i += 1

# randomly flip the qubit
for i in range(n):
    if np.random.random() > 0.5:
        simonCircuit.x(qr[n+i])

# Apply the barrier to mark the end of the blackbox function
simonCircuit.barrier()
```

    <qiskit.extensions.standard.barrier.Barrier at 0x10bb7c7b8>

Now we can continue with the steps of the Simon algorithm: applying the Hadamard gates to the first register and measure.

```python
# Step 4 apply Hadamard gates to the first register
for i in range(n):
    simonCircuit.h(qr[i])

# Step 5 perform measurement on the first register
for i in range(n):
    simonCircuit.measure(qr[i], cr[i])

#draw the circuit
from qiskit.tools.visualization import circuit_drawer
circuit_drawer(simonCircuit)
```

## Experimenting with Simulators

We show the experiments of finding the hidden integer with simulators.

```python
# use local simulator
backend = "local_qasm_simulator"
# the number of shots is twice the length of the bitstring
shots = 2*n
job = execute(simonCircuit, backend=backend, shots=shots)
answer = job.result().get_counts()

plot_histogram(answer)
```

We can see that the results of the measurements are the basis whose inner product with the hidden string $s$ are zero.

*(Notice that the basis on the label of the x-axis in the above plot are numbered from right to left instead of from left to right that we used for $s$)*

Gathering the measurement results, we proceed to post-processing with computations that can be done on classical computers.

### Post Processing with Gaussian Elimination

The post processing is done with Gaussian elimination to solve the system of linear equations to determine $s$.

```python
# Post-processing step
# Constructing the system of linear equations Y s = 0
# By k[::-1], we reverse the order of the bitstring
lAnswer = [ (k[::-1],v) for k,v in answer.items() if k != "0"*n  ] #excluding the trivial all-zero
#Sort the basis by their probabilities
lAnswer.sort(key = lambda x: x[1], reverse=True)

Y = []
for k, v in lAnswer:
    Y.append( [ int(c) for c in k ] )

#import tools from sympy
from sympy import Matrix, pprint, MatrixSymbol, expand, mod_inverse

Y = Matrix(Y)

#pprint(Y)

#Perform Gaussian elimination on Y
Y_transformed = Y.rref(iszerofunc=lambda x: x % 2==0) # linear algebra on GF(2)

#to convert rational and negatives in rref of linear algebra on GF(2)
def mod(x,modulus):
    numer, denom = x.as_numer_denom()
    return numer*mod_inverse(denom,modulus) % modulus

Y_new = Y_transformed[0].applyfunc(lambda x: mod(x,2)) #must takecare of negatives and fractional values
#pprint(Y_new)

print("The hidden bistring s[ 0 ], s[ 1 ]....s[",n-1,"] is the one satisfying the following system of linear equations:")
rows, cols = Y_new.shape
for r in range(rows):
    Yr = [ "s[ "+str(i)+" ]" for i, v in enumerate(list(Y_new[r,:])) if v == 1 ]
    if len(Yr) > 0:
        tStr = " + ".join(Yr)
        print(tStr, "= 0")

```

    The hidden bistring s[ 0 ], s[ 1 ]....s[ 5 ] is the one satisfying the following system of linear equations:
    s[ 0 ] = 0
    s[ 1 ] + s[ 5 ] = 0
    s[ 2 ] = 0
    s[ 3 ] + s[ 5 ] = 0
    s[ 4 ] = 0

As seen above, the system of linear equations is satisfied by the hidden integer $s$. Notice that there can be more than one solutions to the system. In fact, all-zero bitstring is a trivial solution to the system of linear equations. But by having more samples one can narrow down the candidates of the solution, and then test the solution by querying the blackbock in the classical manner.

## Experimenting with Real Devices

We see how one can still find out the hidden integer by running the Simon algorithm on real devices. Due to imperfect quantum computers, obtaining the conclusion is not as easy as done with the simulator of perfect quantum computers.

We first register the token and query the available backends.

```python
#register token to connect with remote backends
register(qx_config['APItoken'], qx_config['url'])
available_backends()
```

    ['ibmqx4',
     'local_clifford_simulator',
     'local_qasm_simulator',
     'ibmqx5',
     'ibmqx2',
     'ibmq_qasm_simulator',
     'local_statevector_simulator',
     'local_unitary_simulator']

```python
import time # for sleep

#Use one of the available backends
backend = "ibmqx5"

# show the status of the backend
print("Status of", backend, "is", get_backend(backend).status)

shots = 10*n #run more experiments to be certain
max_credits = 3          # Maximum number of credits to spend on executions.

if get_backend(backend).status["operational"] is True:

    job_exp = execute(simonCircuit, backend=backend, shots=shots, max_credits=max_credits)

    lapse = 0
    interval = 10
    while not job_exp.done:
        print('Status @ {} seconds'.format(interval * lapse))
        print(job_exp.status)
        time.sleep(interval)
        lapse += 1
    print(job_exp.status)

    results = job_exp.result()
    answer = results.get_counts(simonCircuit)

    plot_histogram(answer)

    # Post-processing step
    # Constructing the system of linear equations Y s = 0
    # By k[::-1], we reverse the order of the bitstring
    lAnswer = [ (k[::-1][:n],v) for k,v in answer.items() ] #excluding the qubits that are not part of the inputs
    #Sort the basis by their probabilities
    lAnswer.sort(key = lambda x: x[1], reverse=True)

    Y = []
    for k, v in lAnswer:
        Y.append( [ int(c) for c in k ] )

    Y = Matrix(Y)

    #Perform Gaussian elimination on Y
    Y_transformed = Y.rref(iszerofunc=lambda x: x % 2==0) # linear algebra on GF(2)
    Y_new = Y_transformed[0].applyfunc(lambda x: mod(x,2)) #must takecare of negatives and fractional values
    #pprint(Y_new)

    print("The hidden bistring s[ 0 ], s[ 1 ]....s[",n-1,"] is the one satisfying the following system of linear equations:")
    rows, cols = Y_new.shape
    for r in range(rows):
        Yr = [ "s[ "+str(i)+" ]" for i, v in enumerate(list(Y_new[r,:])) if v == 1 ]
        if len(Yr) > 0:
            tStr = " + ".join(Yr)
            print(tStr, "= 0")

```

## References

[1] "[On the power of quantum computation](https://epubs.siam.org/doi/abs/10.1137/S0097539796298637)", Daniel R. Simon, SIAM J. Comput., 26(5), 1474–1483 (1997)

```python

```


```
from sympy import symbols, eye, collect
from sympy.matrices import Matrix
```

```
b1, b2, b3 = symbols('b1, b2, b3', real=True)
a11, a12, a13, a21, a22, a23, a31, a32, a33 = symbols('a11, a12, a13, a21, a22, a23, a31, a32, a33', real=True)
L = symbols('L', real=True)

b = Matrix([b1, b2, b3])
A = Matrix([[a11, a12, a13],
            [a21, a22, a23],
            [a31, a32, a33]])

C = L * eye(3) + (1 - L) * A

p = collect((L * (1 - L) * b.T * C.adjugate() * b)[0].expand(), L)
p_prime = p.diff(L)

q = collect(C.det(), L)
q_prime = q.diff(L)

f = p / q

h = collect((p_prime * q - p * q_prime).expand(), L)
```

```
print p
```

    L**4*(-a11*a22*b3**2 + a11*a23*b2*b3 + a11*a32*b2*b3 - a11*a33*b2**2 + a11*b2**2 + a11*b3**2 + a12*a21*b3**2 - a12*a23*b1*b3 - a12*a31*b2*b3 + a12*a33*b1*b2 - a12*b1*b2 - a13*a21*b2*b3 + a13*a22*b1*b3 + a13*a31*b2**2 - a13*a32*b1*b2 - a13*b1*b3 - a21*a32*b1*b3 + a21*a33*b1*b2 - a21*b1*b2 + a22*a31*b1*b3 - a22*a33*b1**2 + a22*b1**2 + a22*b3**2 - a23*a31*b1*b2 + a23*a32*b1**2 - a23*b2*b3 - a31*b1*b3 - a32*b2*b3 + a33*b1**2 + a33*b2**2 - b1**2 - b2**2 - b3**2) + L**3*(3*a11*a22*b3**2 - 3*a11*a23*b2*b3 - 3*a11*a32*b2*b3 + 3*a11*a33*b2**2 - 2*a11*b2**2 - 2*a11*b3**2 - 3*a12*a21*b3**2 + 3*a12*a23*b1*b3 + 3*a12*a31*b2*b3 - 3*a12*a33*b1*b2 + 2*a12*b1*b2 + 3*a13*a21*b2*b3 - 3*a13*a22*b1*b3 - 3*a13*a31*b2**2 + 3*a13*a32*b1*b2 + 2*a13*b1*b3 + 3*a21*a32*b1*b3 - 3*a21*a33*b1*b2 + 2*a21*b1*b2 - 3*a22*a31*b1*b3 + 3*a22*a33*b1**2 - 2*a22*b1**2 - 2*a22*b3**2 + 3*a23*a31*b1*b2 - 3*a23*a32*b1**2 + 2*a23*b2*b3 + 2*a31*b1*b3 + 2*a32*b2*b3 - 2*a33*b1**2 - 2*a33*b2**2 + b1**2 + b2**2 + b3**2) + L**2*(-3*a11*a22*b3**2 + 3*a11*a23*b2*b3 + 3*a11*a32*b2*b3 - 3*a11*a33*b2**2 + a11*b2**2 + a11*b3**2 + 3*a12*a21*b3**2 - 3*a12*a23*b1*b3 - 3*a12*a31*b2*b3 + 3*a12*a33*b1*b2 - a12*b1*b2 - 3*a13*a21*b2*b3 + 3*a13*a22*b1*b3 + 3*a13*a31*b2**2 - 3*a13*a32*b1*b2 - a13*b1*b3 - 3*a21*a32*b1*b3 + 3*a21*a33*b1*b2 - a21*b1*b2 + 3*a22*a31*b1*b3 - 3*a22*a33*b1**2 + a22*b1**2 + a22*b3**2 - 3*a23*a31*b1*b2 + 3*a23*a32*b1**2 - a23*b2*b3 - a31*b1*b3 - a32*b2*b3 + a33*b1**2 + a33*b2**2) + L*(a11*a22*b3**2 - a11*a23*b2*b3 - a11*a32*b2*b3 + a11*a33*b2**2 - a12*a21*b3**2 + a12*a23*b1*b3 + a12*a31*b2*b3 - a12*a33*b1*b2 + a13*a21*b2*b3 - a13*a22*b1*b3 - a13*a31*b2**2 + a13*a32*b1*b2 + a21*a32*b1*b3 - a21*a33*b1*b2 - a22*a31*b1*b3 + a22*a33*b1**2 + a23*a31*b1*b2 - a23*a32*b1**2)

```
print q
```

    L**3*(-a11*a22*a33 + a11*a22 + a11*a23*a32 + a11*a33 - a11 + a12*a21*a33 - a12*a21 - a12*a23*a31 - a13*a21*a32 + a13*a22*a31 - a13*a31 + a22*a33 - a22 - a23*a32 - a33 + 1) + L**2*(3*a11*a22*a33 - 2*a11*a22 - 3*a11*a23*a32 - 2*a11*a33 + a11 - 3*a12*a21*a33 + 2*a12*a21 + 3*a12*a23*a31 + 3*a13*a21*a32 - 3*a13*a22*a31 + 2*a13*a31 - 2*a22*a33 + a22 + 2*a23*a32 + a33) + L*(-3*a11*a22*a33 + a11*a22 + 3*a11*a23*a32 + a11*a33 + 3*a12*a21*a33 - a12*a21 - 3*a12*a23*a31 - 3*a13*a21*a32 + 3*a13*a22*a31 - a13*a31 + a22*a33 - a23*a32) + a11*a22*a33 - a11*a23*a32 - a12*a21*a33 + a12*a23*a31 + a13*a21*a32 - a13*a22*a31

```
coeff = [h.coeff(L, 6), h.coeff(L, 5), h.coeff(L, 4), h.coeff(L, 3), h.coeff(L, 2), h.coeff(L, 1), h.coeff(L, 0)]
print coeff
```

    [a11**2*a22**2*a33*b3**2 - a11**2*a22**2*b3**2 - a11**2*a22*a23*a32*b3**2 - a11**2*a22*a23*a33*b2*b3 + a11**2*a22*a23*b2*b3 - a11**2*a22*a32*a33*b2*b3 + a11**2*a22*a32*b2*b3 + a11**2*a22*a33**2*b2**2 - 2*a11**2*a22*a33*b2**2 - 2*a11**2*a22*a33*b3**2 + a11**2*a22*b2**2 + 2*a11**2*a22*b3**2 + a11**2*a23**2*a32*b2*b3 + a11**2*a23*a32**2*b2*b3 - a11**2*a23*a32*a33*b2**2 + a11**2*a23*a32*b2**2 + a11**2*a23*a32*b3**2 + a11**2*a23*a33*b2*b3 - a11**2*a23*b2*b3 + a11**2*a32*a33*b2*b3 - a11**2*a32*b2*b3 - a11**2*a33**2*b2**2 + 2*a11**2*a33*b2**2 + a11**2*a33*b3**2 - a11**2*b2**2 - a11**2*b3**2 - 2*a11*a12*a21*a22*a33*b3**2 + 2*a11*a12*a21*a22*b3**2 + a11*a12*a21*a23*a32*b3**2 + a11*a12*a21*a23*a33*b2*b3 - a11*a12*a21*a23*b2*b3 + a11*a12*a21*a32*a33*b2*b3 - a11*a12*a21*a32*b2*b3 - a11*a12*a21*a33**2*b2**2 + 2*a11*a12*a21*a33*b2**2 + 2*a11*a12*a21*a33*b3**2 - a11*a12*a21*b2**2 - 2*a11*a12*a21*b3**2 + a11*a12*a22*a23*a31*b3**2 + a11*a12*a22*a23*a33*b1*b3 - a11*a12*a22*a23*b1*b3 + a11*a12*a22*a31*a33*b2*b3 - a11*a12*a22*a31*b2*b3 - a11*a12*a22*a33**2*b1*b2 + 2*a11*a12*a22*a33*b1*b2 - a11*a12*a22*b1*b2 - a11*a12*a23**2*a31*b2*b3 - a11*a12*a23**2*a32*b1*b3 - 2*a11*a12*a23*a31*a32*b2*b3 + a11*a12*a23*a31*a33*b2**2 - a11*a12*a23*a31*b2**2 - a11*a12*a23*a31*b3**2 + a11*a12*a23*a32*a33*b1*b2 - a11*a12*a23*a32*b1*b2 - a11*a12*a23*a33*b1*b3 + a11*a12*a23*b1*b3 - a11*a12*a31*a33*b2*b3 + a11*a12*a31*b2*b3 + a11*a12*a33**2*b1*b2 - 2*a11*a12*a33*b1*b2 + a11*a12*b1*b2 + a11*a13*a21*a22*a32*b3**2 + a11*a13*a21*a22*a33*b2*b3 - a11*a13*a21*a22*b2*b3 - 2*a11*a13*a21*a23*a32*b2*b3 - a11*a13*a21*a32**2*b2*b3 + a11*a13*a21*a32*a33*b2**2 - a11*a13*a21*a32*b2**2 - a11*a13*a21*a32*b3**2 - a11*a13*a21*a33*b2*b3 + a11*a13*a21*b2*b3 - a11*a13*a22**2*a31*b3**2 - a11*a13*a22**2*a33*b1*b3 + a11*a13*a22**2*b1*b3 + a11*a13*a22*a23*a31*b2*b3 + a11*a13*a22*a23*a32*b1*b3 + a11*a13*a22*a31*a32*b2*b3 - 2*a11*a13*a22*a31*a33*b2**2 + 2*a11*a13*a22*a31*b2**2 + 2*a11*a13*a22*a31*b3**2 + a11*a13*a22*a32*a33*b1*b2 - a11*a13*a22*a32*b1*b2 + 2*a11*a13*a22*a33*b1*b3 - 2*a11*a13*a22*b1*b3 + a11*a13*a23*a31*a32*b2**2 - a11*a13*a23*a31*b2*b3 - a11*a13*a23*a32**2*b1*b2 - a11*a13*a23*a32*b1*b3 - a11*a13*a31*a32*b2*b3 + 2*a11*a13*a31*a33*b2**2 - 2*a11*a13*a31*b2**2 - a11*a13*a31*b3**2 - a11*a13*a32*a33*b1*b2 + a11*a13*a32*b1*b2 - a11*a13*a33*b1*b3 + a11*a13*b1*b3 + a11*a21*a22*a32*a33*b1*b3 - a11*a21*a22*a32*b1*b3 - a11*a21*a22*a33**2*b1*b2 + 2*a11*a21*a22*a33*b1*b2 - a11*a21*a22*b1*b2 - a11*a21*a23*a32**2*b1*b3 + a11*a21*a23*a32*a33*b1*b2 - a11*a21*a23*a32*b1*b2 - a11*a21*a32*a33*b1*b3 + a11*a21*a32*b1*b3 + a11*a21*a33**2*b1*b2 - 2*a11*a21*a33*b1*b2 + a11*a21*b1*b2 - a11*a22**2*a31*a33*b1*b3 + a11*a22**2*a31*b1*b3 + a11*a22**2*a33**2*b1**2 - 2*a11*a22**2*a33*b1**2 - 2*a11*a22**2*a33*b3**2 + a11*a22**2*b1**2 + 2*a11*a22**2*b3**2 + a11*a22*a23*a31*a32*b1*b3 + a11*a22*a23*a31*a33*b1*b2 - a11*a22*a23*a31*b1*b2 - 2*a11*a22*a23*a32*a33*b1**2 + 2*a11*a22*a23*a32*b1**2 + 2*a11*a22*a23*a32*b3**2 + 2*a11*a22*a23*a33*b2*b3 - 2*a11*a22*a23*b2*b3 + 2*a11*a22*a31*a33*b1*b3 - 2*a11*a22*a31*b1*b3 + 2*a11*a22*a32*a33*b2*b3 - 2*a11*a22*a32*b2*b3 - 2*a11*a22*a33**2*b1**2 - 2*a11*a22*a33**2*b2**2 + 4*a11*a22*a33*b1**2 + 4*a11*a22*a33*b2**2 + 4*a11*a22*a33*b3**2 - 2*a11*a22*b1**2 - 2*a11*a22*b2**2 - 4*a11*a22*b3**2 - a11*a23**2*a31*a32*b1*b2 + a11*a23**2*a32**2*b1**2 - 2*a11*a23**2*a32*b2*b3 - a11*a23*a31*a32*b1*b3 - a11*a23*a31*a33*b1*b2 + a11*a23*a31*b1*b2 - 2*a11*a23*a32**2*b2*b3 + 2*a11*a23*a32*a33*b1**2 + 2*a11*a23*a32*a33*b2**2 - 2*a11*a23*a32*b1**2 - 2*a11*a23*a32*b2**2 - 2*a11*a23*a32*b3**2 - 2*a11*a23*a33*b2*b3 + 2*a11*a23*b2*b3 - a11*a31*a33*b1*b3 + a11*a31*b1*b3 - 2*a11*a32*a33*b2*b3 + 2*a11*a32*b2*b3 + a11*a33**2*b1**2 + 2*a11*a33**2*b2**2 - 2*a11*a33*b1**2 - 4*a11*a33*b2**2 - 2*a11*a33*b3**2 + a11*b1**2 + 2*a11*b2**2 + 2*a11*b3**2 + a12**2*a21**2*a33*b3**2 - a12**2*a21**2*b3**2 - a12**2*a21*a23*a31*b3**2 - a12**2*a21*a23*a33*b1*b3 + a12**2*a21*a23*b1*b3 - a12**2*a21*a31*a33*b2*b3 + a12**2*a21*a31*b2*b3 + a12**2*a21*a33**2*b1*b2 - 2*a12**2*a21*a33*b1*b2 + a12**2*a21*b1*b2 + a12**2*a23**2*a31*b1*b3 + a12**2*a23*a31**2*b2*b3 - a12**2*a23*a31*a33*b1*b2 + a12**2*a23*a31*b1*b2 - a12*a13*a21**2*a32*b3**2 - a12*a13*a21**2*a33*b2*b3 + a12*a13*a21**2*b2*b3 + a12*a13*a21*a22*a31*b3**2 + a12*a13*a21*a22*a33*b1*b3 - a12*a13*a21*a22*b1*b3 + a12*a13*a21*a23*a31*b2*b3 + a12*a13*a21*a23*a32*b1*b3 + a12*a13*a21*a31*a32*b2*b3 + a12*a13*a21*a31*a33*b2**2 - a12*a13*a21*a31*b2**2 - a12*a13*a21*a31*b3**2 - 2*a12*a13*a21*a32*a33*b1*b2 + 2*a12*a13*a21*a32*b1*b2 - a12*a13*a21*a33*b1*b3 + a12*a13*a21*b1*b3 - 2*a12*a13*a22*a23*a31*b1*b3 - a12*a13*a22*a31**2*b2*b3 + a12*a13*a22*a31*a33*b1*b2 - a12*a13*a22*a31*b1*b2 - a12*a13*a23*a31**2*b2**2 + a12*a13*a23*a31*a32*b1*b2 + 2*a12*a13*a23*a31*b1*b3 + a12*a13*a31**2*b2*b3 - a12*a13*a31*a33*b1*b2 + a12*a13*a31*b1*b2 - a12*a21**2*a32*a33*b1*b3 + a12*a21**2*a32*b1*b3 + a12*a21**2*a33**2*b1*b2 - 2*a12*a21**2*a33*b1*b2 + a12*a21**2*b1*b2 + a12*a21*a22*a31*a33*b1*b3 - a12*a21*a22*a31*b1*b3 - a12*a21*a22*a33**2*b1**2 + 2*a12*a21*a22*a33*b1**2 + 2*a12*a21*a22*a33*b3**2 - a12*a21*a22*b1**2 - 2*a12*a21*a22*b3**2 + a12*a21*a23*a31*a32*b1*b3 - 2*a12*a21*a23*a31*a33*b1*b2 + 2*a12*a21*a23*a31*b1*b2 + a12*a21*a23*a32*a33*b1**2 - a12*a21*a23*a32*b1**2 - a12*a21*a23*a32*b3**2 - a12*a21*a23*a33*b2*b3 + a12*a21*a23*b2*b3 - a12*a21*a31*a33*b1*b3 + a12*a21*a31*b1*b3 - a12*a21*a32*a33*b2*b3 + a12*a21*a32*b2*b3 + a12*a21*a33**2*b1**2 + a12*a21*a33**2*b2**2 - 2*a12*a21*a33*b1**2 - 2*a12*a21*a33*b2**2 - 2*a12*a21*a33*b3**2 + a12*a21*b1**2 + a12*a21*b2**2 + 2*a12*a21*b3**2 - a12*a22*a23*a31**2*b1*b3 + a12*a22*a23*a31*a33*b1**2 - a12*a22*a23*a31*b1**2 - a12*a22*a23*a31*b3**2 - a12*a22*a23*a33*b1*b3 + a12*a22*a23*b1*b3 - a12*a22*a31*a33*b2*b3 + a12*a22*a31*b2*b3 + a12*a22*a33**2*b1*b2 - 2*a12*a22*a33*b1*b2 + a12*a22*b1*b2 + a12*a23**2*a31**2*b1*b2 - a12*a23**2*a31*a32*b1**2 + a12*a23**2*a31*b2*b3 + a12*a23**2*a32*b1*b3 + a12*a23*a31**2*b1*b3 + 2*a12*a23*a31*a32*b2*b3 - a12*a23*a31*a33*b1**2 - a12*a23*a31*a33*b2**2 + a12*a23*a31*b1**2 + a12*a23*a31*b2**2 + a12*a23*a31*b3**2 - a12*a23*a32*a33*b1*b2 + a12*a23*a32*b1*b2 + a12*a23*a33*b1*b3 - a12*a23*b1*b3 + a12*a31*a33*b2*b3 - a12*a31*b2*b3 - a12*a33**2*b1*b2 + 2*a12*a33*b1*b2 - a12*b1*b2 + a13**2*a21**2*a32*b2*b3 - a13**2*a21*a22*a31*b2*b3 - a13**2*a21*a22*a32*b1*b3 - a13**2*a21*a31*a32*b2**2 + a13**2*a21*a31*b2*b3 + a13**2*a21*a32**2*b1*b2 + a13**2*a21*a32*b1*b3 + a13**2*a22**2*a31*b1*b3 + a13**2*a22*a31**2*b2**2 - a13**2*a22*a31*a32*b1*b2 - 2*a13**2*a22*a31*b1*b3 - a13**2*a31**2*b2**2 + a13**2*a31*a32*b1*b2 + a13**2*a31*b1*b3 + a13*a21**2*a32**2*b1*b3 - a13*a21**2*a32*a33*b1*b2 + a13*a21**2*a32*b1*b2 - 2*a13*a21*a22*a31*a32*b1*b3 + a13*a21*a22*a31*a33*b1*b2 - a13*a21*a22*a31*b1*b2 + a13*a21*a22*a32*a33*b1**2 - a13*a21*a22*a32*b1**2 - a13*a21*a22*a32*b3**2 - a13*a21*a22*a33*b2*b3 + a13*a21*a22*b2*b3 + a13*a21*a23*a31*a32*b1*b2 - a13*a21*a23*a32**2*b1**2 + 2*a13*a21*a23*a32*b2*b3 + 2*a13*a21*a31*a32*b1*b3 - a13*a21*a31*a33*b1*b2 + a13*a21*a31*b1*b2 + a13*a21*a32**2*b2*b3 - a13*a21*a32*a33*b1**2 - a13*a21*a32*a33*b2**2 + a13*a21*a32*b1**2 + a13*a21*a32*b2**2 + a13*a21*a32*b3**2 + a13*a21*a33*b2*b3 - a13*a21*b2*b3 + a13*a22**2*a31**2*b1*b3 - a13*a22**2*a31*a33*b1**2 + a13*a22**2*a31*b1**2 + a13*a22**2*a31*b3**2 + a13*a22**2*a33*b1*b3 - a13*a22**2*b1*b3 - a13*a22*a23*a31**2*b1*b2 + a13*a22*a23*a31*a32*b1**2 - a13*a22*a23*a31*b2*b3 - a13*a22*a23*a32*b1*b3 - 2*a13*a22*a31**2*b1*b3 - a13*a22*a31*a32*b2*b3 + 2*a13*a22*a31*a33*b1**2 + 2*a13*a22*a31*a33*b2**2 - 2*a13*a22*a31*b1**2 - 2*a13*a22*a31*b2**2 - 2*a13*a22*a31*b3**2 - a13*a22*a32*a33*b1*b2 + a13*a22*a32*b1*b2 - 2*a13*a22*a33*b1*b3 + 2*a13*a22*b1*b3 + a13*a23*a31**2*b1*b2 - a13*a23*a31*a32*b1**2 - a13*a23*a31*a32*b2**2 + a13*a23*a31*b2*b3 + a13*a23*a32**2*b1*b2 + a13*a23*a32*b1*b3 + a13*a31**2*b1*b3 + a13*a31*a32*b2*b3 - a13*a31*a33*b1**2 - 2*a13*a31*a33*b2**2 + a13*a31*b1**2 + 2*a13*a31*b2**2 + a13*a31*b3**2 + a13*a32*a33*b1*b2 - a13*a32*b1*b2 + a13*a33*b1*b3 - a13*b1*b3 - a21*a22*a32*a33*b1*b3 + a21*a22*a32*b1*b3 + a21*a22*a33**2*b1*b2 - 2*a21*a22*a33*b1*b2 + a21*a22*b1*b2 + a21*a23*a32**2*b1*b3 - a21*a23*a32*a33*b1*b2 + a21*a23*a32*b1*b2 + a21*a32*a33*b1*b3 - a21*a32*b1*b3 - a21*a33**2*b1*b2 + 2*a21*a33*b1*b2 - a21*b1*b2 + a22**2*a31*a33*b1*b3 - a22**2*a31*b1*b3 - a22**2*a33**2*b1**2 + 2*a22**2*a33*b1**2 + a22**2*a33*b3**2 - a22**2*b1**2 - a22**2*b3**2 - a22*a23*a31*a32*b1*b3 - a22*a23*a31*a33*b1*b2 + a22*a23*a31*b1*b2 + 2*a22*a23*a32*a33*b1**2 - 2*a22*a23*a32*b1**2 - a22*a23*a32*b3**2 - a22*a23*a33*b2*b3 + a22*a23*b2*b3 - 2*a22*a31*a33*b1*b3 + 2*a22*a31*b1*b3 - a22*a32*a33*b2*b3 + a22*a32*b2*b3 + 2*a22*a33**2*b1**2 + a22*a33**2*b2**2 - 4*a22*a33*b1**2 - 2*a22*a33*b2**2 - 2*a22*a33*b3**2 + 2*a22*b1**2 + a22*b2**2 + 2*a22*b3**2 + a23**2*a31*a32*b1*b2 - a23**2*a32**2*b1**2 + a23**2*a32*b2*b3 + a23*a31*a32*b1*b3 + a23*a31*a33*b1*b2 - a23*a31*b1*b2 + a23*a32**2*b2*b3 - 2*a23*a32*a33*b1**2 - a23*a32*a33*b2**2 + 2*a23*a32*b1**2 + a23*a32*b2**2 + a23*a32*b3**2 + a23*a33*b2*b3 - a23*b2*b3 + a31*a33*b1*b3 - a31*b1*b3 + a32*a33*b2*b3 - a32*b2*b3 - a33**2*b1**2 - a33**2*b2**2 + 2*a33*b1**2 + 2*a33*b2**2 + a33*b3**2 - b1**2 - b2**2 - b3**2, -6*a11**2*a22**2*a33*b3**2 + 4*a11**2*a22**2*b3**2 + 6*a11**2*a22*a23*a32*b3**2 + 6*a11**2*a22*a23*a33*b2*b3 - 4*a11**2*a22*a23*b2*b3 + 6*a11**2*a22*a32*a33*b2*b3 - 4*a11**2*a22*a32*b2*b3 - 6*a11**2*a22*a33**2*b2**2 + 10*a11**2*a22*a33*b2**2 + 10*a11**2*a22*a33*b3**2 - 4*a11**2*a22*b2**2 - 6*a11**2*a22*b3**2 - 6*a11**2*a23**2*a32*b2*b3 - 6*a11**2*a23*a32**2*b2*b3 + 6*a11**2*a23*a32*a33*b2**2 - 6*a11**2*a23*a32*b2**2 - 6*a11**2*a23*a32*b3**2 - 4*a11**2*a23*a33*b2*b3 + 2*a11**2*a23*b2*b3 - 4*a11**2*a32*a33*b2*b3 + 2*a11**2*a32*b2*b3 + 4*a11**2*a33**2*b2**2 - 6*a11**2*a33*b2**2 - 4*a11**2*a33*b3**2 + 2*a11**2*b2**2 + 2*a11**2*b3**2 + 12*a11*a12*a21*a22*a33*b3**2 - 8*a11*a12*a21*a22*b3**2 - 6*a11*a12*a21*a23*a32*b3**2 - 6*a11*a12*a21*a23*a33*b2*b3 + 4*a11*a12*a21*a23*b2*b3 - 6*a11*a12*a21*a32*a33*b2*b3 + 4*a11*a12*a21*a32*b2*b3 + 6*a11*a12*a21*a33**2*b2**2 - 10*a11*a12*a21*a33*b2**2 - 10*a11*a12*a21*a33*b3**2 + 4*a11*a12*a21*b2**2 + 6*a11*a12*a21*b3**2 - 6*a11*a12*a22*a23*a31*b3**2 - 6*a11*a12*a22*a23*a33*b1*b3 + 4*a11*a12*a22*a23*b1*b3 - 6*a11*a12*a22*a31*a33*b2*b3 + 4*a11*a12*a22*a31*b2*b3 + 6*a11*a12*a22*a33**2*b1*b2 - 10*a11*a12*a22*a33*b1*b2 + 4*a11*a12*a22*b1*b2 + 6*a11*a12*a23**2*a31*b2*b3 + 6*a11*a12*a23**2*a32*b1*b3 + 12*a11*a12*a23*a31*a32*b2*b3 - 6*a11*a12*a23*a31*a33*b2**2 + 6*a11*a12*a23*a31*b2**2 + 6*a11*a12*a23*a31*b3**2 - 6*a11*a12*a23*a32*a33*b1*b2 + 6*a11*a12*a23*a32*b1*b2 + 4*a11*a12*a23*a33*b1*b3 - 2*a11*a12*a23*b1*b3 + 4*a11*a12*a31*a33*b2*b3 - 2*a11*a12*a31*b2*b3 - 4*a11*a12*a33**2*b1*b2 + 6*a11*a12*a33*b1*b2 - 2*a11*a12*b1*b2 - 6*a11*a13*a21*a22*a32*b3**2 - 6*a11*a13*a21*a22*a33*b2*b3 + 4*a11*a13*a21*a22*b2*b3 + 12*a11*a13*a21*a23*a32*b2*b3 + 6*a11*a13*a21*a32**2*b2*b3 - 6*a11*a13*a21*a32*a33*b2**2 + 6*a11*a13*a21*a32*b2**2 + 6*a11*a13*a21*a32*b3**2 + 4*a11*a13*a21*a33*b2*b3 - 2*a11*a13*a21*b2*b3 + 6*a11*a13*a22**2*a31*b3**2 + 6*a11*a13*a22**2*a33*b1*b3 - 4*a11*a13*a22**2*b1*b3 - 6*a11*a13*a22*a23*a31*b2*b3 - 6*a11*a13*a22*a23*a32*b1*b3 - 6*a11*a13*a22*a31*a32*b2*b3 + 12*a11*a13*a22*a31*a33*b2**2 - 10*a11*a13*a22*a31*b2**2 - 10*a11*a13*a22*a31*b3**2 - 6*a11*a13*a22*a32*a33*b1*b2 + 4*a11*a13*a22*a32*b1*b2 - 10*a11*a13*a22*a33*b1*b3 + 6*a11*a13*a22*b1*b3 - 6*a11*a13*a23*a31*a32*b2**2 + 4*a11*a13*a23*a31*b2*b3 + 6*a11*a13*a23*a32**2*b1*b2 + 6*a11*a13*a23*a32*b1*b3 + 4*a11*a13*a31*a32*b2*b3 - 8*a11*a13*a31*a33*b2**2 + 6*a11*a13*a31*b2**2 + 4*a11*a13*a31*b3**2 + 4*a11*a13*a32*a33*b1*b2 - 2*a11*a13*a32*b1*b2 + 4*a11*a13*a33*b1*b3 - 2*a11*a13*b1*b3 - 6*a11*a21*a22*a32*a33*b1*b3 + 4*a11*a21*a22*a32*b1*b3 + 6*a11*a21*a22*a33**2*b1*b2 - 10*a11*a21*a22*a33*b1*b2 + 4*a11*a21*a22*b1*b2 + 6*a11*a21*a23*a32**2*b1*b3 - 6*a11*a21*a23*a32*a33*b1*b2 + 6*a11*a21*a23*a32*b1*b2 + 4*a11*a21*a32*a33*b1*b3 - 2*a11*a21*a32*b1*b3 - 4*a11*a21*a33**2*b1*b2 + 6*a11*a21*a33*b1*b2 - 2*a11*a21*b1*b2 + 6*a11*a22**2*a31*a33*b1*b3 - 4*a11*a22**2*a31*b1*b3 - 6*a11*a22**2*a33**2*b1**2 + 10*a11*a22**2*a33*b1**2 + 10*a11*a22**2*a33*b3**2 - 4*a11*a22**2*b1**2 - 6*a11*a22**2*b3**2 - 6*a11*a22*a23*a31*a32*b1*b3 - 6*a11*a22*a23*a31*a33*b1*b2 + 4*a11*a22*a23*a31*b1*b2 + 12*a11*a22*a23*a32*a33*b1**2 - 10*a11*a22*a23*a32*b1**2 - 10*a11*a22*a23*a32*b3**2 - 10*a11*a22*a23*a33*b2*b3 + 6*a11*a22*a23*b2*b3 - 10*a11*a22*a31*a33*b1*b3 + 6*a11*a22*a31*b1*b3 - 10*a11*a22*a32*a33*b2*b3 + 6*a11*a22*a32*b2*b3 + 10*a11*a22*a33**2*b1**2 + 10*a11*a22*a33**2*b2**2 - 16*a11*a22*a33*b1**2 - 16*a11*a22*a33*b2**2 - 16*a11*a22*a33*b3**2 + 6*a11*a22*b1**2 + 6*a11*a22*b2**2 + 8*a11*a22*b3**2 + 6*a11*a23**2*a31*a32*b1*b2 - 6*a11*a23**2*a32**2*b1**2 + 10*a11*a23**2*a32*b2*b3 + 6*a11*a23*a31*a32*b1*b3 + 4*a11*a23*a31*a33*b1*b2 - 2*a11*a23*a31*b1*b2 + 10*a11*a23*a32**2*b2*b3 - 10*a11*a23*a32*a33*b1**2 - 10*a11*a23*a32*a33*b2**2 + 8*a11*a23*a32*b1**2 + 10*a11*a23*a32*b2**2 + 10*a11*a23*a32*b3**2 + 6*a11*a23*a33*b2*b3 - 2*a11*a23*b2*b3 + 4*a11*a31*a33*b1*b3 - 2*a11*a31*b1*b3 + 6*a11*a32*a33*b2*b3 - 2*a11*a32*b2*b3 - 4*a11*a33**2*b1**2 - 6*a11*a33**2*b2**2 + 6*a11*a33*b1**2 + 8*a11*a33*b2**2 + 6*a11*a33*b3**2 - 2*a11*b1**2 - 2*a11*b2**2 - 2*a11*b3**2 - 6*a12**2*a21**2*a33*b3**2 + 4*a12**2*a21**2*b3**2 + 6*a12**2*a21*a23*a31*b3**2 + 6*a12**2*a21*a23*a33*b1*b3 - 4*a12**2*a21*a23*b1*b3 + 6*a12**2*a21*a31*a33*b2*b3 - 4*a12**2*a21*a31*b2*b3 - 6*a12**2*a21*a33**2*b1*b2 + 10*a12**2*a21*a33*b1*b2 - 4*a12**2*a21*b1*b2 - 6*a12**2*a23**2*a31*b1*b3 - 6*a12**2*a23*a31**2*b2*b3 + 6*a12**2*a23*a31*a33*b1*b2 - 6*a12**2*a23*a31*b1*b2 + 6*a12*a13*a21**2*a32*b3**2 + 6*a12*a13*a21**2*a33*b2*b3 - 4*a12*a13*a21**2*b2*b3 - 6*a12*a13*a21*a22*a31*b3**2 - 6*a12*a13*a21*a22*a33*b1*b3 + 4*a12*a13*a21*a22*b1*b3 - 6*a12*a13*a21*a23*a31*b2*b3 - 6*a12*a13*a21*a23*a32*b1*b3 - 6*a12*a13*a21*a31*a32*b2*b3 - 6*a12*a13*a21*a31*a33*b2**2 + 4*a12*a13*a21*a31*b2**2 + 4*a12*a13*a21*a31*b3**2 + 12*a12*a13*a21*a32*a33*b1*b2 - 10*a12*a13*a21*a32*b1*b2 + 6*a12*a13*a21*a33*b1*b3 - 4*a12*a13*a21*b1*b3 + 12*a12*a13*a22*a23*a31*b1*b3 + 6*a12*a13*a22*a31**2*b2*b3 - 6*a12*a13*a22*a31*a33*b1*b2 + 6*a12*a13*a22*a31*b1*b2 + 6*a12*a13*a23*a31**2*b2**2 - 6*a12*a13*a23*a31*a32*b1*b2 - 10*a12*a13*a23*a31*b1*b3 - 4*a12*a13*a31**2*b2*b3 + 4*a12*a13*a31*a33*b1*b2 - 4*a12*a13*a31*b1*b2 + 6*a12*a21**2*a32*a33*b1*b3 - 4*a12*a21**2*a32*b1*b3 - 6*a12*a21**2*a33**2*b1*b2 + 10*a12*a21**2*a33*b1*b2 - 4*a12*a21**2*b1*b2 - 6*a12*a21*a22*a31*a33*b1*b3 + 4*a12*a21*a22*a31*b1*b3 + 6*a12*a21*a22*a33**2*b1**2 - 10*a12*a21*a22*a33*b1**2 - 10*a12*a21*a22*a33*b3**2 + 4*a12*a21*a22*b1**2 + 6*a12*a21*a22*b3**2 - 6*a12*a21*a23*a31*a32*b1*b3 + 12*a12*a21*a23*a31*a33*b1*b2 - 10*a12*a21*a23*a31*b1*b2 - 6*a12*a21*a23*a32*a33*b1**2 + 4*a12*a21*a23*a32*b1**2 + 4*a12*a21*a23*a32*b3**2 + 6*a12*a21*a23*a33*b2*b3 - 4*a12*a21*a23*b2*b3 + 6*a12*a21*a31*a33*b1*b3 - 4*a12*a21*a31*b1*b3 + 6*a12*a21*a32*a33*b2*b3 - 4*a12*a21*a32*b2*b3 - 6*a12*a21*a33**2*b1**2 - 6*a12*a21*a33**2*b2**2 + 10*a12*a21*a33*b1**2 + 10*a12*a21*a33*b2**2 + 8*a12*a21*a33*b3**2 - 4*a12*a21*b1**2 - 4*a12*a21*b2**2 - 4*a12*a21*b3**2 + 6*a12*a22*a23*a31**2*b1*b3 - 6*a12*a22*a23*a31*a33*b1**2 + 6*a12*a22*a23*a31*b1**2 + 6*a12*a22*a23*a31*b3**2 + 4*a12*a22*a23*a33*b1*b3 - 2*a12*a22*a23*b1*b3 + 4*a12*a22*a31*a33*b2*b3 - 2*a12*a22*a31*b2*b3 - 4*a12*a22*a33**2*b1*b2 + 6*a12*a22*a33*b1*b2 - 2*a12*a22*b1*b2 - 6*a12*a23**2*a31**2*b1*b2 + 6*a12*a23**2*a31*a32*b1**2 - 6*a12*a23**2*a31*b2*b3 - 4*a12*a23**2*a32*b1*b3 - 6*a12*a23*a31**2*b1*b3 - 10*a12*a23*a31*a32*b2*b3 + 6*a12*a23*a31*a33*b1**2 + 6*a12*a23*a31*a33*b2**2 - 6*a12*a23*a31*b1**2 - 6*a12*a23*a31*b2**2 - 6*a12*a23*a31*b3**2 + 4*a12*a23*a32*a33*b1*b2 - 4*a12*a23*a32*b1*b2 - 2*a12*a23*a33*b1*b3 - 2*a12*a31*a33*b2*b3 + 2*a12*a33**2*b1*b2 - 2*a12*a33*b1*b2 - 6*a13**2*a21**2*a32*b2*b3 + 6*a13**2*a21*a22*a31*b2*b3 + 6*a13**2*a21*a22*a32*b1*b3 + 6*a13**2*a21*a31*a32*b2**2 - 4*a13**2*a21*a31*b2*b3 - 6*a13**2*a21*a32**2*b1*b2 - 6*a13**2*a21*a32*b1*b3 - 6*a13**2*a22**2*a31*b1*b3 - 6*a13**2*a22*a31**2*b2**2 + 6*a13**2*a22*a31*a32*b1*b2 + 10*a13**2*a22*a31*b1*b3 + 4*a13**2*a31**2*b2**2 - 4*a13**2*a31*a32*b1*b2 - 4*a13**2*a31*b1*b3 - 6*a13*a21**2*a32**2*b1*b3 + 6*a13*a21**2*a32*a33*b1*b2 - 6*a13*a21**2*a32*b1*b2 + 12*a13*a21*a22*a31*a32*b1*b3 - 6*a13*a21*a22*a31*a33*b1*b2 + 6*a13*a21*a22*a31*b1*b2 - 6*a13*a21*a22*a32*a33*b1**2 + 6*a13*a21*a22*a32*b1**2 + 6*a13*a21*a22*a32*b3**2 + 4*a13*a21*a22*a33*b2*b3 - 2*a13*a21*a22*b2*b3 - 6*a13*a21*a23*a31*a32*b1*b2 + 6*a13*a21*a23*a32**2*b1**2 - 10*a13*a21*a23*a32*b2*b3 - 10*a13*a21*a31*a32*b1*b3 + 4*a13*a21*a31*a33*b1*b2 - 4*a13*a21*a31*b1*b2 - 6*a13*a21*a32**2*b2*b3 + 6*a13*a21*a32*a33*b1**2 + 6*a13*a21*a32*a33*b2**2 - 6*a13*a21*a32*b1**2 - 6*a13*a21*a32*b2**2 - 6*a13*a21*a32*b3**2 - 2*a13*a21*a33*b2*b3 - 6*a13*a22**2*a31**2*b1*b3 + 6*a13*a22**2*a31*a33*b1**2 - 6*a13*a22**2*a31*b1**2 - 6*a13*a22**2*a31*b3**2 - 4*a13*a22**2*a33*b1*b3 + 2*a13*a22**2*b1*b3 + 6*a13*a22*a23*a31**2*b1*b2 - 6*a13*a22*a23*a31*a32*b1**2 + 6*a13*a22*a23*a31*b2*b3 + 4*a13*a22*a23*a32*b1*b3 + 10*a13*a22*a31**2*b1*b3 + 6*a13*a22*a31*a32*b2*b3 - 10*a13*a22*a31*a33*b1**2 - 10*a13*a22*a31*a33*b2**2 + 10*a13*a22*a31*b1**2 + 8*a13*a22*a31*b2**2 + 10*a13*a22*a31*b3**2 + 4*a13*a22*a32*a33*b1*b2 - 2*a13*a22*a32*b1*b2 + 6*a13*a22*a33*b1*b3 - 2*a13*a22*b1*b3 - 4*a13*a23*a31**2*b1*b2 + 4*a13*a23*a31*a32*b1**2 + 4*a13*a23*a31*a32*b2**2 - 4*a13*a23*a31*b2*b3 - 4*a13*a23*a32**2*b1*b2 - 4*a13*a23*a32*b1*b3 - 4*a13*a31**2*b1*b3 - 4*a13*a31*a32*b2*b3 + 4*a13*a31*a33*b1**2 + 6*a13*a31*a33*b2**2 - 4*a13*a31*b1**2 - 4*a13*a31*b2**2 - 4*a13*a31*b3**2 - 2*a13*a32*a33*b1*b2 - 2*a13*a33*b1*b3 + 4*a21*a22*a32*a33*b1*b3 - 2*a21*a22*a32*b1*b3 - 4*a21*a22*a33**2*b1*b2 + 6*a21*a22*a33*b1*b2 - 2*a21*a22*b1*b2 - 4*a21*a23*a32**2*b1*b3 + 4*a21*a23*a32*a33*b1*b2 - 4*a21*a23*a32*b1*b2 - 2*a21*a32*a33*b1*b3 + 2*a21*a33**2*b1*b2 - 2*a21*a33*b1*b2 - 4*a22**2*a31*a33*b1*b3 + 2*a22**2*a31*b1*b3 + 4*a22**2*a33**2*b1**2 - 6*a22**2*a33*b1**2 - 4*a22**2*a33*b3**2 + 2*a22**2*b1**2 + 2*a22**2*b3**2 + 4*a22*a23*a31*a32*b1*b3 + 4*a22*a23*a31*a33*b1*b2 - 2*a22*a23*a31*b1*b2 - 8*a22*a23*a32*a33*b1**2 + 6*a22*a23*a32*b1**2 + 4*a22*a23*a32*b3**2 + 4*a22*a23*a33*b2*b3 - 2*a22*a23*b2*b3 + 6*a22*a31*a33*b1*b3 - 2*a22*a31*b1*b3 + 4*a22*a32*a33*b2*b3 - 2*a22*a32*b2*b3 - 6*a22*a33**2*b1**2 - 4*a22*a33**2*b2**2 + 8*a22*a33*b1**2 + 6*a22*a33*b2**2 + 6*a22*a33*b3**2 - 2*a22*b1**2 - 2*a22*b2**2 - 2*a22*b3**2 - 4*a23**2*a31*a32*b1*b2 + 4*a23**2*a32**2*b1**2 - 4*a23**2*a32*b2*b3 - 4*a23*a31*a32*b1*b3 - 2*a23*a31*a33*b1*b2 - 4*a23*a32**2*b2*b3 + 6*a23*a32*a33*b1**2 + 4*a23*a32*a33*b2**2 - 4*a23*a32*b1**2 - 4*a23*a32*b2**2 - 4*a23*a32*b3**2 - 2*a23*a33*b2*b3 - 2*a31*a33*b1*b3 - 2*a32*a33*b2*b3 + 2*a33**2*b1**2 + 2*a33**2*b2**2 - 2*a33*b1**2 - 2*a33*b2**2 - 2*a33*b3**2, 15*a11**2*a22**2*a33*b3**2 - 6*a11**2*a22**2*b3**2 - 15*a11**2*a22*a23*a32*b3**2 - 15*a11**2*a22*a23*a33*b2*b3 + 6*a11**2*a22*a23*b2*b3 - 15*a11**2*a22*a32*a33*b2*b3 + 6*a11**2*a22*a32*b2*b3 + 15*a11**2*a22*a33**2*b2**2 - 20*a11**2*a22*a33*b2**2 - 20*a11**2*a22*a33*b3**2 + 6*a11**2*a22*b2**2 + 6*a11**2*a22*b3**2 + 15*a11**2*a23**2*a32*b2*b3 + 15*a11**2*a23*a32**2*b2*b3 - 15*a11**2*a23*a32*a33*b2**2 + 14*a11**2*a23*a32*b2**2 + 14*a11**2*a23*a32*b3**2 + 6*a11**2*a23*a33*b2*b3 + 6*a11**2*a32*a33*b2*b3 - 6*a11**2*a33**2*b2**2 + 6*a11**2*a33*b2**2 + 6*a11**2*a33*b3**2 - a11**2*b2**2 - a11**2*b3**2 - 30*a11*a12*a21*a22*a33*b3**2 + 12*a11*a12*a21*a22*b3**2 + 15*a11*a12*a21*a23*a32*b3**2 + 15*a11*a12*a21*a23*a33*b2*b3 - 6*a11*a12*a21*a23*b2*b3 + 15*a11*a12*a21*a32*a33*b2*b3 - 6*a11*a12*a21*a32*b2*b3 - 15*a11*a12*a21*a33**2*b2**2 + 20*a11*a12*a21*a33*b2**2 + 20*a11*a12*a21*a33*b3**2 - 6*a11*a12*a21*b2**2 - 6*a11*a12*a21*b3**2 + 15*a11*a12*a22*a23*a31*b3**2 + 15*a11*a12*a22*a23*a33*b1*b3 - 6*a11*a12*a22*a23*b1*b3 + 15*a11*a12*a22*a31*a33*b2*b3 - 6*a11*a12*a22*a31*b2*b3 - 15*a11*a12*a22*a33**2*b1*b2 + 20*a11*a12*a22*a33*b1*b2 - 6*a11*a12*a22*b1*b2 - 15*a11*a12*a23**2*a31*b2*b3 - 15*a11*a12*a23**2*a32*b1*b3 - 30*a11*a12*a23*a31*a32*b2*b3 + 15*a11*a12*a23*a31*a33*b2**2 - 14*a11*a12*a23*a31*b2**2 - 14*a11*a12*a23*a31*b3**2 + 15*a11*a12*a23*a32*a33*b1*b2 - 14*a11*a12*a23*a32*b1*b2 - 6*a11*a12*a23*a33*b1*b3 - 6*a11*a12*a31*a33*b2*b3 + 6*a11*a12*a33**2*b1*b2 - 6*a11*a12*a33*b1*b2 + a11*a12*b1*b2 + 15*a11*a13*a21*a22*a32*b3**2 + 15*a11*a13*a21*a22*a33*b2*b3 - 6*a11*a13*a21*a22*b2*b3 - 30*a11*a13*a21*a23*a32*b2*b3 - 15*a11*a13*a21*a32**2*b2*b3 + 15*a11*a13*a21*a32*a33*b2**2 - 14*a11*a13*a21*a32*b2**2 - 14*a11*a13*a21*a32*b3**2 - 6*a11*a13*a21*a33*b2*b3 - 15*a11*a13*a22**2*a31*b3**2 - 15*a11*a13*a22**2*a33*b1*b3 + 6*a11*a13*a22**2*b1*b3 + 15*a11*a13*a22*a23*a31*b2*b3 + 15*a11*a13*a22*a23*a32*b1*b3 + 15*a11*a13*a22*a31*a32*b2*b3 - 30*a11*a13*a22*a31*a33*b2**2 + 20*a11*a13*a22*a31*b2**2 + 20*a11*a13*a22*a31*b3**2 + 15*a11*a13*a22*a32*a33*b1*b2 - 6*a11*a13*a22*a32*b1*b2 + 20*a11*a13*a22*a33*b1*b3 - 6*a11*a13*a22*b1*b3 + 15*a11*a13*a23*a31*a32*b2**2 - 6*a11*a13*a23*a31*b2*b3 - 15*a11*a13*a23*a32**2*b1*b2 - 14*a11*a13*a23*a32*b1*b3 - 6*a11*a13*a31*a32*b2*b3 + 12*a11*a13*a31*a33*b2**2 - 6*a11*a13*a31*b2**2 - 6*a11*a13*a31*b3**2 - 6*a11*a13*a32*a33*b1*b2 - 6*a11*a13*a33*b1*b3 + a11*a13*b1*b3 + 15*a11*a21*a22*a32*a33*b1*b3 - 6*a11*a21*a22*a32*b1*b3 - 15*a11*a21*a22*a33**2*b1*b2 + 20*a11*a21*a22*a33*b1*b2 - 6*a11*a21*a22*b1*b2 - 15*a11*a21*a23*a32**2*b1*b3 + 15*a11*a21*a23*a32*a33*b1*b2 - 14*a11*a21*a23*a32*b1*b2 - 6*a11*a21*a32*a33*b1*b3 + 6*a11*a21*a33**2*b1*b2 - 6*a11*a21*a33*b1*b2 + a11*a21*b1*b2 - 15*a11*a22**2*a31*a33*b1*b3 + 6*a11*a22**2*a31*b1*b3 + 15*a11*a22**2*a33**2*b1**2 - 20*a11*a22**2*a33*b1**2 - 20*a11*a22**2*a33*b3**2 + 6*a11*a22**2*b1**2 + 6*a11*a22**2*b3**2 + 15*a11*a22*a23*a31*a32*b1*b3 + 15*a11*a22*a23*a31*a33*b1*b2 - 6*a11*a22*a23*a31*b1*b2 - 30*a11*a22*a23*a32*a33*b1**2 + 20*a11*a22*a23*a32*b1**2 + 20*a11*a22*a23*a32*b3**2 + 20*a11*a22*a23*a33*b2*b3 - 6*a11*a22*a23*b2*b3 + 20*a11*a22*a31*a33*b1*b3 - 6*a11*a22*a31*b1*b3 + 20*a11*a22*a32*a33*b2*b3 - 6*a11*a22*a32*b2*b3 - 20*a11*a22*a33**2*b1**2 - 20*a11*a22*a33**2*b2**2 + 24*a11*a22*a33*b1**2 + 24*a11*a22*a33*b2**2 + 24*a11*a22*a33*b3**2 - 6*a11*a22*b1**2 - 6*a11*a22*b2**2 - 4*a11*a22*b3**2 - 15*a11*a23**2*a31*a32*b1*b2 + 15*a11*a23**2*a32**2*b1**2 - 20*a11*a23**2*a32*b2*b3 - 14*a11*a23*a31*a32*b1*b3 - 6*a11*a23*a31*a33*b1*b2 - 20*a11*a23*a32**2*b2*b3 + 20*a11*a23*a32*a33*b1**2 + 20*a11*a23*a32*a33*b2**2 - 12*a11*a23*a32*b1**2 - 18*a11*a23*a32*b2**2 - 18*a11*a23*a32*b3**2 - 6*a11*a23*a33*b2*b3 - 2*a11*a23*b2*b3 - 6*a11*a31*a33*b1*b3 + a11*a31*b1*b3 - 6*a11*a32*a33*b2*b3 - 2*a11*a32*b2*b3 + 6*a11*a33**2*b1**2 + 6*a11*a33**2*b2**2 - 6*a11*a33*b1**2 - 4*a11*a33*b2**2 - 6*a11*a33*b3**2 + a11*b1**2 + 15*a12**2*a21**2*a33*b3**2 - 6*a12**2*a21**2*b3**2 - 15*a12**2*a21*a23*a31*b3**2 - 15*a12**2*a21*a23*a33*b1*b3 + 6*a12**2*a21*a23*b1*b3 - 15*a12**2*a21*a31*a33*b2*b3 + 6*a12**2*a21*a31*b2*b3 + 15*a12**2*a21*a33**2*b1*b2 - 20*a12**2*a21*a33*b1*b2 + 6*a12**2*a21*b1*b2 + 15*a12**2*a23**2*a31*b1*b3 + 15*a12**2*a23*a31**2*b2*b3 - 15*a12**2*a23*a31*a33*b1*b2 + 14*a12**2*a23*a31*b1*b2 - 15*a12*a13*a21**2*a32*b3**2 - 15*a12*a13*a21**2*a33*b2*b3 + 6*a12*a13*a21**2*b2*b3 + 15*a12*a13*a21*a22*a31*b3**2 + 15*a12*a13*a21*a22*a33*b1*b3 - 6*a12*a13*a21*a22*b1*b3 + 15*a12*a13*a21*a23*a31*b2*b3 + 15*a12*a13*a21*a23*a32*b1*b3 + 15*a12*a13*a21*a31*a32*b2*b3 + 15*a12*a13*a21*a31*a33*b2**2 - 6*a12*a13*a21*a31*b2**2 - 6*a12*a13*a21*a31*b3**2 - 30*a12*a13*a21*a32*a33*b1*b2 + 20*a12*a13*a21*a32*b1*b2 - 14*a12*a13*a21*a33*b1*b3 + 6*a12*a13*a21*b1*b3 - 30*a12*a13*a22*a23*a31*b1*b3 - 15*a12*a13*a22*a31**2*b2*b3 + 15*a12*a13*a22*a31*a33*b1*b2 - 14*a12*a13*a22*a31*b1*b2 - 15*a12*a13*a23*a31**2*b2**2 + 15*a12*a13*a23*a31*a32*b1*b2 + 20*a12*a13*a23*a31*b1*b3 + 6*a12*a13*a31**2*b2*b3 - 6*a12*a13*a31*a33*b1*b2 + 6*a12*a13*a31*b1*b2 - 15*a12*a21**2*a32*a33*b1*b3 + 6*a12*a21**2*a32*b1*b3 + 15*a12*a21**2*a33**2*b1*b2 - 20*a12*a21**2*a33*b1*b2 + 6*a12*a21**2*b1*b2 + 15*a12*a21*a22*a31*a33*b1*b3 - 6*a12*a21*a22*a31*b1*b3 - 15*a12*a21*a22*a33**2*b1**2 + 20*a12*a21*a22*a33*b1**2 + 20*a12*a21*a22*a33*b3**2 - 6*a12*a21*a22*b1**2 - 6*a12*a21*a22*b3**2 + 15*a12*a21*a23*a31*a32*b1*b3 - 30*a12*a21*a23*a31*a33*b1*b2 + 20*a12*a21*a23*a31*b1*b2 + 15*a12*a21*a23*a32*a33*b1**2 - 6*a12*a21*a23*a32*b1**2 - 6*a12*a21*a23*a32*b3**2 - 14*a12*a21*a23*a33*b2*b3 + 6*a12*a21*a23*b2*b3 - 14*a12*a21*a31*a33*b1*b3 + 6*a12*a21*a31*b1*b3 - 14*a12*a21*a32*a33*b2*b3 + 6*a12*a21*a32*b2*b3 + 14*a12*a21*a33**2*b1**2 + 14*a12*a21*a33**2*b2**2 - 18*a12*a21*a33*b1**2 - 18*a12*a21*a33*b2**2 - 12*a12*a21*a33*b3**2 + 5*a12*a21*b1**2 + 5*a12*a21*b2**2 + 2*a12*a21*b3**2 - 15*a12*a22*a23*a31**2*b1*b3 + 15*a12*a22*a23*a31*a33*b1**2 - 14*a12*a22*a23*a31*b1**2 - 14*a12*a22*a23*a31*b3**2 - 6*a12*a22*a23*a33*b1*b3 - 6*a12*a22*a31*a33*b2*b3 + 6*a12*a22*a33**2*b1*b2 - 6*a12*a22*a33*b1*b2 + a12*a22*b1*b2 + 15*a12*a23**2*a31**2*b1*b2 - 15*a12*a23**2*a31*a32*b1**2 + 14*a12*a23**2*a31*b2*b3 + 6*a12*a23**2*a32*b1*b3 + 14*a12*a23*a31**2*b1*b3 + 20*a12*a23*a31*a32*b2*b3 - 14*a12*a23*a31*a33*b1**2 - 14*a12*a23*a31*a33*b2**2 + 12*a12*a23*a31*b1**2 + 12*a12*a23*a31*b2**2 + 12*a12*a23*a31*b3**2 - 6*a12*a23*a32*a33*b1*b2 + 6*a12*a23*a32*b1*b2 + 3*a12*a23*b1*b3 + 3*a12*a31*b2*b3 - 2*a12*a33*b1*b2 + a12*b1*b2 + 15*a13**2*a21**2*a32*b2*b3 - 15*a13**2*a21*a22*a31*b2*b3 - 15*a13**2*a21*a22*a32*b1*b3 - 15*a13**2*a21*a31*a32*b2**2 + 6*a13**2*a21*a31*b2*b3 + 15*a13**2*a21*a32**2*b1*b2 + 14*a13**2*a21*a32*b1*b3 + 15*a13**2*a22**2*a31*b1*b3 + 15*a13**2*a22*a31**2*b2**2 - 15*a13**2*a22*a31*a32*b1*b2 - 20*a13**2*a22*a31*b1*b3 - 6*a13**2*a31**2*b2**2 + 6*a13**2*a31*a32*b1*b2 + 6*a13**2*a31*b1*b3 + 15*a13*a21**2*a32**2*b1*b3 - 15*a13*a21**2*a32*a33*b1*b2 + 14*a13*a21**2*a32*b1*b2 - 30*a13*a21*a22*a31*a32*b1*b3 + 15*a13*a21*a22*a31*a33*b1*b2 - 14*a13*a21*a22*a31*b1*b2 + 15*a13*a21*a22*a32*a33*b1**2 - 14*a13*a21*a22*a32*b1**2 - 14*a13*a21*a22*a32*b3**2 - 6*a13*a21*a22*a33*b2*b3 + 15*a13*a21*a23*a31*a32*b1*b2 - 15*a13*a21*a23*a32**2*b1**2 + 20*a13*a21*a23*a32*b2*b3 + 20*a13*a21*a31*a32*b1*b3 - 6*a13*a21*a31*a33*b1*b2 + 6*a13*a21*a31*b1*b2 + 14*a13*a21*a32**2*b2*b3 - 14*a13*a21*a32*a33*b1**2 - 14*a13*a21*a32*a33*b2**2 + 12*a13*a21*a32*b1**2 + 12*a13*a21*a32*b2**2 + 12*a13*a21*a32*b3**2 + 3*a13*a21*b2*b3 + 15*a13*a22**2*a31**2*b1*b3 - 15*a13*a22**2*a31*a33*b1**2 + 14*a13*a22**2*a31*b1**2 + 14*a13*a22**2*a31*b3**2 + 6*a13*a22**2*a33*b1*b3 - 15*a13*a22*a23*a31**2*b1*b2 + 15*a13*a22*a23*a31*a32*b1**2 - 14*a13*a22*a23*a31*b2*b3 - 6*a13*a22*a23*a32*b1*b3 - 20*a13*a22*a31**2*b1*b3 - 14*a13*a22*a31*a32*b2*b3 + 20*a13*a22*a31*a33*b1**2 + 20*a13*a22*a31*a33*b2**2 - 18*a13*a22*a31*b1**2 - 12*a13*a22*a31*b2**2 - 18*a13*a22*a31*b3**2 - 6*a13*a22*a32*a33*b1*b2 - 6*a13*a22*a33*b1*b3 - 2*a13*a22*b1*b3 + 6*a13*a23*a31**2*b1*b2 - 6*a13*a23*a31*a32*b1**2 - 6*a13*a23*a31*a32*b2**2 + 6*a13*a23*a31*b2*b3 + 6*a13*a23*a32**2*b1*b2 + 6*a13*a23*a32*b1*b3 + 6*a13*a31**2*b1*b3 + 6*a13*a31*a32*b2*b3 - 6*a13*a31*a33*b1**2 - 6*a13*a31*a33*b2**2 + 5*a13*a31*b1**2 + 2*a13*a31*b2**2 + 5*a13*a31*b3**2 + 3*a13*a32*b1*b2 + a13*a33*b1*b3 + a13*b1*b3 - 6*a21*a22*a32*a33*b1*b3 + 6*a21*a22*a33**2*b1*b2 - 6*a21*a22*a33*b1*b2 + a21*a22*b1*b2 + 6*a21*a23*a32**2*b1*b3 - 6*a21*a23*a32*a33*b1*b2 + 6*a21*a23*a32*b1*b2 + 3*a21*a32*b1*b3 - 2*a21*a33*b1*b2 + a21*b1*b2 + 6*a22**2*a31*a33*b1*b3 - 6*a22**2*a33**2*b1**2 + 6*a22**2*a33*b1**2 + 6*a22**2*a33*b3**2 - a22**2*b1**2 - a22**2*b3**2 - 6*a22*a23*a31*a32*b1*b3 - 6*a22*a23*a31*a33*b1*b2 + 12*a22*a23*a32*a33*b1**2 - 6*a22*a23*a32*b1**2 - 6*a22*a23*a32*b3**2 - 6*a22*a23*a33*b2*b3 + a22*a23*b2*b3 - 6*a22*a31*a33*b1*b3 - 2*a22*a31*b1*b3 - 6*a22*a32*a33*b2*b3 + a22*a32*b2*b3 + 6*a22*a33**2*b1**2 + 6*a22*a33**2*b2**2 - 4*a22*a33*b1**2 - 6*a22*a33*b2**2 - 6*a22*a33*b3**2 + a22*b2**2 + 6*a23**2*a31*a32*b1*b2 - 6*a23**2*a32**2*b1**2 + 6*a23**2*a32*b2*b3 + 6*a23*a31*a32*b1*b3 + 3*a23*a31*b1*b2 + 6*a23*a32**2*b2*b3 - 6*a23*a32*a33*b1**2 - 6*a23*a32*a33*b2**2 + 2*a23*a32*b1**2 + 5*a23*a32*b2**2 + 5*a23*a32*b3**2 + a23*a33*b2*b3 + a23*b2*b3 + a31*a33*b1*b3 + a31*b1*b3 + a32*a33*b2*b3 + a32*b2*b3 - a33**2*b1**2 - a33**2*b2**2 + a33*b3**2, -20*a11**2*a22**2*a33*b3**2 + 4*a11**2*a22**2*b3**2 + 20*a11**2*a22*a23*a32*b3**2 + 20*a11**2*a22*a23*a33*b2*b3 - 4*a11**2*a22*a23*b2*b3 + 20*a11**2*a22*a32*a33*b2*b3 - 4*a11**2*a22*a32*b2*b3 - 20*a11**2*a22*a33**2*b2**2 + 20*a11**2*a22*a33*b2**2 + 20*a11**2*a22*a33*b3**2 - 4*a11**2*a22*b2**2 - 2*a11**2*a22*b3**2 - 20*a11**2*a23**2*a32*b2*b3 - 20*a11**2*a23*a32**2*b2*b3 + 20*a11**2*a23*a32*a33*b2**2 - 16*a11**2*a23*a32*b2**2 - 16*a11**2*a23*a32*b3**2 - 4*a11**2*a23*a33*b2*b3 - 2*a11**2*a23*b2*b3 - 4*a11**2*a32*a33*b2*b3 - 2*a11**2*a32*b2*b3 + 4*a11**2*a33**2*b2**2 - 2*a11**2*a33*b2**2 - 4*a11**2*a33*b3**2 + 40*a11*a12*a21*a22*a33*b3**2 - 8*a11*a12*a21*a22*b3**2 - 20*a11*a12*a21*a23*a32*b3**2 - 20*a11*a12*a21*a23*a33*b2*b3 + 4*a11*a12*a21*a23*b2*b3 - 20*a11*a12*a21*a32*a33*b2*b3 + 4*a11*a12*a21*a32*b2*b3 + 20*a11*a12*a21*a33**2*b2**2 - 20*a11*a12*a21*a33*b2**2 - 20*a11*a12*a21*a33*b3**2 + 4*a11*a12*a21*b2**2 + 2*a11*a12*a21*b3**2 - 20*a11*a12*a22*a23*a31*b3**2 - 20*a11*a12*a22*a23*a33*b1*b3 + 4*a11*a12*a22*a23*b1*b3 - 20*a11*a12*a22*a31*a33*b2*b3 + 4*a11*a12*a22*a31*b2*b3 + 20*a11*a12*a22*a33**2*b1*b2 - 20*a11*a12*a22*a33*b1*b2 + 4*a11*a12*a22*b1*b2 + 20*a11*a12*a23**2*a31*b2*b3 + 20*a11*a12*a23**2*a32*b1*b3 + 40*a11*a12*a23*a31*a32*b2*b3 - 20*a11*a12*a23*a31*a33*b2**2 + 16*a11*a12*a23*a31*b2**2 + 16*a11*a12*a23*a31*b3**2 - 20*a11*a12*a23*a32*a33*b1*b2 + 16*a11*a12*a23*a32*b1*b2 + 4*a11*a12*a23*a33*b1*b3 + 2*a11*a12*a23*b1*b3 + 4*a11*a12*a31*a33*b2*b3 + 2*a11*a12*a31*b2*b3 - 4*a11*a12*a33**2*b1*b2 + 2*a11*a12*a33*b1*b2 - 20*a11*a13*a21*a22*a32*b3**2 - 20*a11*a13*a21*a22*a33*b2*b3 + 4*a11*a13*a21*a22*b2*b3 + 40*a11*a13*a21*a23*a32*b2*b3 + 20*a11*a13*a21*a32**2*b2*b3 - 20*a11*a13*a21*a32*a33*b2**2 + 16*a11*a13*a21*a32*b2**2 + 16*a11*a13*a21*a32*b3**2 + 4*a11*a13*a21*a33*b2*b3 + 2*a11*a13*a21*b2*b3 + 20*a11*a13*a22**2*a31*b3**2 + 20*a11*a13*a22**2*a33*b1*b3 - 4*a11*a13*a22**2*b1*b3 - 20*a11*a13*a22*a23*a31*b2*b3 - 20*a11*a13*a22*a23*a32*b1*b3 - 20*a11*a13*a22*a31*a32*b2*b3 + 40*a11*a13*a22*a31*a33*b2**2 - 20*a11*a13*a22*a31*b2**2 - 20*a11*a13*a22*a31*b3**2 - 20*a11*a13*a22*a32*a33*b1*b2 + 4*a11*a13*a22*a32*b1*b2 - 20*a11*a13*a22*a33*b1*b3 + 2*a11*a13*a22*b1*b3 - 20*a11*a13*a23*a31*a32*b2**2 + 4*a11*a13*a23*a31*b2*b3 + 20*a11*a13*a23*a32**2*b1*b2 + 16*a11*a13*a23*a32*b1*b3 + 4*a11*a13*a31*a32*b2*b3 - 8*a11*a13*a31*a33*b2**2 + 2*a11*a13*a31*b2**2 + 4*a11*a13*a31*b3**2 + 4*a11*a13*a32*a33*b1*b2 + 2*a11*a13*a32*b1*b2 + 4*a11*a13*a33*b1*b3 - 20*a11*a21*a22*a32*a33*b1*b3 + 4*a11*a21*a22*a32*b1*b3 + 20*a11*a21*a22*a33**2*b1*b2 - 20*a11*a21*a22*a33*b1*b2 + 4*a11*a21*a22*b1*b2 + 20*a11*a21*a23*a32**2*b1*b3 - 20*a11*a21*a23*a32*a33*b1*b2 + 16*a11*a21*a23*a32*b1*b2 + 4*a11*a21*a32*a33*b1*b3 + 2*a11*a21*a32*b1*b3 - 4*a11*a21*a33**2*b1*b2 + 2*a11*a21*a33*b1*b2 + 20*a11*a22**2*a31*a33*b1*b3 - 4*a11*a22**2*a31*b1*b3 - 20*a11*a22**2*a33**2*b1**2 + 20*a11*a22**2*a33*b1**2 + 20*a11*a22**2*a33*b3**2 - 4*a11*a22**2*b1**2 - 2*a11*a22**2*b3**2 - 20*a11*a22*a23*a31*a32*b1*b3 - 20*a11*a22*a23*a31*a33*b1*b2 + 4*a11*a22*a23*a31*b1*b2 + 40*a11*a22*a23*a32*a33*b1**2 - 20*a11*a22*a23*a32*b1**2 - 20*a11*a22*a23*a32*b3**2 - 20*a11*a22*a23*a33*b2*b3 + 2*a11*a22*a23*b2*b3 - 20*a11*a22*a31*a33*b1*b3 + 2*a11*a22*a31*b1*b3 - 20*a11*a22*a32*a33*b2*b3 + 2*a11*a22*a32*b2*b3 + 20*a11*a22*a33**2*b1**2 + 20*a11*a22*a33**2*b2**2 - 16*a11*a22*a33*b1**2 - 16*a11*a22*a33*b2**2 - 16*a11*a22*a33*b3**2 + 2*a11*a22*b1**2 + 2*a11*a22*b2**2 + 20*a11*a23**2*a31*a32*b1*b2 - 20*a11*a23**2*a32**2*b1**2 + 20*a11*a23**2*a32*b2*b3 + 16*a11*a23*a31*a32*b1*b3 + 4*a11*a23*a31*a33*b1*b2 + 2*a11*a23*a31*b1*b2 + 20*a11*a23*a32**2*b2*b3 - 20*a11*a23*a32*a33*b1**2 - 20*a11*a23*a32*a33*b2**2 + 8*a11*a23*a32*b1**2 + 14*a11*a23*a32*b2**2 + 14*a11*a23*a32*b3**2 + 2*a11*a23*a33*b2*b3 + 2*a11*a23*b2*b3 + 4*a11*a31*a33*b1*b3 + 2*a11*a32*a33*b2*b3 + 2*a11*a32*b2*b3 - 4*a11*a33**2*b1**2 - 2*a11*a33**2*b2**2 + 2*a11*a33*b1**2 + 2*a11*a33*b3**2 - 20*a12**2*a21**2*a33*b3**2 + 4*a12**2*a21**2*b3**2 + 20*a12**2*a21*a23*a31*b3**2 + 20*a12**2*a21*a23*a33*b1*b3 - 4*a12**2*a21*a23*b1*b3 + 20*a12**2*a21*a31*a33*b2*b3 - 4*a12**2*a21*a31*b2*b3 - 20*a12**2*a21*a33**2*b1*b2 + 20*a12**2*a21*a33*b1*b2 - 4*a12**2*a21*b1*b2 - 20*a12**2*a23**2*a31*b1*b3 - 20*a12**2*a23*a31**2*b2*b3 + 20*a12**2*a23*a31*a33*b1*b2 - 16*a12**2*a23*a31*b1*b2 + 20*a12*a13*a21**2*a32*b3**2 + 20*a12*a13*a21**2*a33*b2*b3 - 4*a12*a13*a21**2*b2*b3 - 20*a12*a13*a21*a22*a31*b3**2 - 20*a12*a13*a21*a22*a33*b1*b3 + 4*a12*a13*a21*a22*b1*b3 - 20*a12*a13*a21*a23*a31*b2*b3 - 20*a12*a13*a21*a23*a32*b1*b3 - 20*a12*a13*a21*a31*a32*b2*b3 - 20*a12*a13*a21*a31*a33*b2**2 + 4*a12*a13*a21*a31*b2**2 + 4*a12*a13*a21*a31*b3**2 + 40*a12*a13*a21*a32*a33*b1*b2 - 20*a12*a13*a21*a32*b1*b2 + 16*a12*a13*a21*a33*b1*b3 - 4*a12*a13*a21*b1*b3 + 40*a12*a13*a22*a23*a31*b1*b3 + 20*a12*a13*a22*a31**2*b2*b3 - 20*a12*a13*a22*a31*a33*b1*b2 + 16*a12*a13*a22*a31*b1*b2 + 20*a12*a13*a23*a31**2*b2**2 - 20*a12*a13*a23*a31*a32*b1*b2 - 20*a12*a13*a23*a31*b1*b3 - 4*a12*a13*a31**2*b2*b3 + 4*a12*a13*a31*a33*b1*b2 - 4*a12*a13*a31*b1*b2 + 20*a12*a21**2*a32*a33*b1*b3 - 4*a12*a21**2*a32*b1*b3 - 20*a12*a21**2*a33**2*b1*b2 + 20*a12*a21**2*a33*b1*b2 - 4*a12*a21**2*b1*b2 - 20*a12*a21*a22*a31*a33*b1*b3 + 4*a12*a21*a22*a31*b1*b3 + 20*a12*a21*a22*a33**2*b1**2 - 20*a12*a21*a22*a33*b1**2 - 20*a12*a21*a22*a33*b3**2 + 4*a12*a21*a22*b1**2 + 2*a12*a21*a22*b3**2 - 20*a12*a21*a23*a31*a32*b1*b3 + 40*a12*a21*a23*a31*a33*b1*b2 - 20*a12*a21*a23*a31*b1*b2 - 20*a12*a21*a23*a32*a33*b1**2 + 4*a12*a21*a23*a32*b1**2 + 4*a12*a21*a23*a32*b3**2 + 16*a12*a21*a23*a33*b2*b3 - 4*a12*a21*a23*b2*b3 + 16*a12*a21*a31*a33*b1*b3 - 4*a12*a21*a31*b1*b3 + 16*a12*a21*a32*a33*b2*b3 - 4*a12*a21*a32*b2*b3 - 16*a12*a21*a33**2*b1**2 - 16*a12*a21*a33**2*b2**2 + 14*a12*a21*a33*b1**2 + 14*a12*a21*a33*b2**2 + 8*a12*a21*a33*b3**2 - 2*a12*a21*b1**2 - 2*a12*a21*b2**2 + 20*a12*a22*a23*a31**2*b1*b3 - 20*a12*a22*a23*a31*a33*b1**2 + 16*a12*a22*a23*a31*b1**2 + 16*a12*a22*a23*a31*b3**2 + 4*a12*a22*a23*a33*b1*b3 + 2*a12*a22*a23*b1*b3 + 4*a12*a22*a31*a33*b2*b3 + 2*a12*a22*a31*b2*b3 - 4*a12*a22*a33**2*b1*b2 + 2*a12*a22*a33*b1*b2 - 20*a12*a23**2*a31**2*b1*b2 + 20*a12*a23**2*a31*a32*b1**2 - 16*a12*a23**2*a31*b2*b3 - 4*a12*a23**2*a32*b1*b3 - 16*a12*a23*a31**2*b1*b3 - 20*a12*a23*a31*a32*b2*b3 + 16*a12*a23*a31*a33*b1**2 + 16*a12*a23*a31*a33*b2**2 - 10*a12*a23*a31*b1**2 - 10*a12*a23*a31*b2**2 - 10*a12*a23*a31*b3**2 + 4*a12*a23*a32*a33*b1*b2 - 4*a12*a23*a32*b1*b2 + 2*a12*a23*a33*b1*b3 - 2*a12*a23*b1*b3 + 2*a12*a31*a33*b2*b3 - 2*a12*a31*b2*b3 - 2*a12*a33**2*b1*b2 + 2*a12*a33*b1*b2 - 20*a13**2*a21**2*a32*b2*b3 + 20*a13**2*a21*a22*a31*b2*b3 + 20*a13**2*a21*a22*a32*b1*b3 + 20*a13**2*a21*a31*a32*b2**2 - 4*a13**2*a21*a31*b2*b3 - 20*a13**2*a21*a32**2*b1*b2 - 16*a13**2*a21*a32*b1*b3 - 20*a13**2*a22**2*a31*b1*b3 - 20*a13**2*a22*a31**2*b2**2 + 20*a13**2*a22*a31*a32*b1*b2 + 20*a13**2*a22*a31*b1*b3 + 4*a13**2*a31**2*b2**2 - 4*a13**2*a31*a32*b1*b2 - 4*a13**2*a31*b1*b3 - 20*a13*a21**2*a32**2*b1*b3 + 20*a13*a21**2*a32*a33*b1*b2 - 16*a13*a21**2*a32*b1*b2 + 40*a13*a21*a22*a31*a32*b1*b3 - 20*a13*a21*a22*a31*a33*b1*b2 + 16*a13*a21*a22*a31*b1*b2 - 20*a13*a21*a22*a32*a33*b1**2 + 16*a13*a21*a22*a32*b1**2 + 16*a13*a21*a22*a32*b3**2 + 4*a13*a21*a22*a33*b2*b3 + 2*a13*a21*a22*b2*b3 - 20*a13*a21*a23*a31*a32*b1*b2 + 20*a13*a21*a23*a32**2*b1**2 - 20*a13*a21*a23*a32*b2*b3 - 20*a13*a21*a31*a32*b1*b3 + 4*a13*a21*a31*a33*b1*b2 - 4*a13*a21*a31*b1*b2 - 16*a13*a21*a32**2*b2*b3 + 16*a13*a21*a32*a33*b1**2 + 16*a13*a21*a32*a33*b2**2 - 10*a13*a21*a32*b1**2 - 10*a13*a21*a32*b2**2 - 10*a13*a21*a32*b3**2 + 2*a13*a21*a33*b2*b3 - 2*a13*a21*b2*b3 - 20*a13*a22**2*a31**2*b1*b3 + 20*a13*a22**2*a31*a33*b1**2 - 16*a13*a22**2*a31*b1**2 - 16*a13*a22**2*a31*b3**2 - 4*a13*a22**2*a33*b1*b3 - 2*a13*a22**2*b1*b3 + 20*a13*a22*a23*a31**2*b1*b2 - 20*a13*a22*a23*a31*a32*b1**2 + 16*a13*a22*a23*a31*b2*b3 + 4*a13*a22*a23*a32*b1*b3 + 20*a13*a22*a31**2*b1*b3 + 16*a13*a22*a31*a32*b2*b3 - 20*a13*a22*a31*a33*b1**2 - 20*a13*a22*a31*a33*b2**2 + 14*a13*a22*a31*b1**2 + 8*a13*a22*a31*b2**2 + 14*a13*a22*a31*b3**2 + 4*a13*a22*a32*a33*b1*b2 + 2*a13*a22*a32*b1*b2 + 2*a13*a22*a33*b1*b3 + 2*a13*a22*b1*b3 - 4*a13*a23*a31**2*b1*b2 + 4*a13*a23*a31*a32*b1**2 + 4*a13*a23*a31*a32*b2**2 - 4*a13*a23*a31*b2*b3 - 4*a13*a23*a32**2*b1*b2 - 4*a13*a23*a32*b1*b3 - 4*a13*a31**2*b1*b3 - 4*a13*a31*a32*b2*b3 + 4*a13*a31*a33*b1**2 + 2*a13*a31*a33*b2**2 - 2*a13*a31*b1**2 - 2*a13*a31*b3**2 + 2*a13*a32*a33*b1*b2 - 2*a13*a32*b1*b2 + 4*a21*a22*a32*a33*b1*b3 + 2*a21*a22*a32*b1*b3 - 4*a21*a22*a33**2*b1*b2 + 2*a21*a22*a33*b1*b2 - 4*a21*a23*a32**2*b1*b3 + 4*a21*a23*a32*a33*b1*b2 - 4*a21*a23*a32*b1*b2 + 2*a21*a32*a33*b1*b3 - 2*a21*a32*b1*b3 - 2*a21*a33**2*b1*b2 + 2*a21*a33*b1*b2 - 4*a22**2*a31*a33*b1*b3 - 2*a22**2*a31*b1*b3 + 4*a22**2*a33**2*b1**2 - 2*a22**2*a33*b1**2 - 4*a22**2*a33*b3**2 + 4*a22*a23*a31*a32*b1*b3 + 4*a22*a23*a31*a33*b1*b2 + 2*a22*a23*a31*b1*b2 - 8*a22*a23*a32*a33*b1**2 + 2*a22*a23*a32*b1**2 + 4*a22*a23*a32*b3**2 + 4*a22*a23*a33*b2*b3 + 2*a22*a31*a33*b1*b3 + 2*a22*a31*b1*b3 + 4*a22*a32*a33*b2*b3 - 2*a22*a33**2*b1**2 - 4*a22*a33**2*b2**2 + 2*a22*a33*b2**2 + 2*a22*a33*b3**2 - 4*a23**2*a31*a32*b1*b2 + 4*a23**2*a32**2*b1**2 - 4*a23**2*a32*b2*b3 - 4*a23*a31*a32*b1*b3 + 2*a23*a31*a33*b1*b2 - 2*a23*a31*b1*b2 - 4*a23*a32**2*b2*b3 + 2*a23*a32*a33*b1**2 + 4*a23*a32*a33*b2**2 - 2*a23*a32*b2**2 - 2*a23*a32*b3**2, 15*a11**2*a22**2*a33*b3**2 - a11**2*a22**2*b3**2 - 15*a11**2*a22*a23*a32*b3**2 - 15*a11**2*a22*a23*a33*b2*b3 + a11**2*a22*a23*b2*b3 - 15*a11**2*a22*a32*a33*b2*b3 + a11**2*a22*a32*b2*b3 + 15*a11**2*a22*a33**2*b2**2 - 10*a11**2*a22*a33*b2**2 - 10*a11**2*a22*a33*b3**2 + a11**2*a22*b2**2 + 15*a11**2*a23**2*a32*b2*b3 + 15*a11**2*a23*a32**2*b2*b3 - 15*a11**2*a23*a32*a33*b2**2 + 9*a11**2*a23*a32*b2**2 + 9*a11**2*a23*a32*b3**2 + a11**2*a23*a33*b2*b3 + a11**2*a23*b2*b3 + a11**2*a32*a33*b2*b3 + a11**2*a32*b2*b3 - a11**2*a33**2*b2**2 + a11**2*a33*b3**2 - 30*a11*a12*a21*a22*a33*b3**2 + 2*a11*a12*a21*a22*b3**2 + 15*a11*a12*a21*a23*a32*b3**2 + 15*a11*a12*a21*a23*a33*b2*b3 - a11*a12*a21*a23*b2*b3 + 15*a11*a12*a21*a32*a33*b2*b3 - a11*a12*a21*a32*b2*b3 - 15*a11*a12*a21*a33**2*b2**2 + 10*a11*a12*a21*a33*b2**2 + 10*a11*a12*a21*a33*b3**2 - a11*a12*a21*b2**2 + 15*a11*a12*a22*a23*a31*b3**2 + 15*a11*a12*a22*a23*a33*b1*b3 - a11*a12*a22*a23*b1*b3 + 15*a11*a12*a22*a31*a33*b2*b3 - a11*a12*a22*a31*b2*b3 - 15*a11*a12*a22*a33**2*b1*b2 + 10*a11*a12*a22*a33*b1*b2 - a11*a12*a22*b1*b2 - 15*a11*a12*a23**2*a31*b2*b3 - 15*a11*a12*a23**2*a32*b1*b3 - 30*a11*a12*a23*a31*a32*b2*b3 + 15*a11*a12*a23*a31*a33*b2**2 - 9*a11*a12*a23*a31*b2**2 - 9*a11*a12*a23*a31*b3**2 + 15*a11*a12*a23*a32*a33*b1*b2 - 9*a11*a12*a23*a32*b1*b2 - a11*a12*a23*a33*b1*b3 - a11*a12*a23*b1*b3 - a11*a12*a31*a33*b2*b3 - a11*a12*a31*b2*b3 + a11*a12*a33**2*b1*b2 + 15*a11*a13*a21*a22*a32*b3**2 + 15*a11*a13*a21*a22*a33*b2*b3 - a11*a13*a21*a22*b2*b3 - 30*a11*a13*a21*a23*a32*b2*b3 - 15*a11*a13*a21*a32**2*b2*b3 + 15*a11*a13*a21*a32*a33*b2**2 - 9*a11*a13*a21*a32*b2**2 - 9*a11*a13*a21*a32*b3**2 - a11*a13*a21*a33*b2*b3 - a11*a13*a21*b2*b3 - 15*a11*a13*a22**2*a31*b3**2 - 15*a11*a13*a22**2*a33*b1*b3 + a11*a13*a22**2*b1*b3 + 15*a11*a13*a22*a23*a31*b2*b3 + 15*a11*a13*a22*a23*a32*b1*b3 + 15*a11*a13*a22*a31*a32*b2*b3 - 30*a11*a13*a22*a31*a33*b2**2 + 10*a11*a13*a22*a31*b2**2 + 10*a11*a13*a22*a31*b3**2 + 15*a11*a13*a22*a32*a33*b1*b2 - a11*a13*a22*a32*b1*b2 + 10*a11*a13*a22*a33*b1*b3 + 15*a11*a13*a23*a31*a32*b2**2 - a11*a13*a23*a31*b2*b3 - 15*a11*a13*a23*a32**2*b1*b2 - 9*a11*a13*a23*a32*b1*b3 - a11*a13*a31*a32*b2*b3 + 2*a11*a13*a31*a33*b2**2 - a11*a13*a31*b3**2 - a11*a13*a32*a33*b1*b2 - a11*a13*a32*b1*b2 - a11*a13*a33*b1*b3 + 15*a11*a21*a22*a32*a33*b1*b3 - a11*a21*a22*a32*b1*b3 - 15*a11*a21*a22*a33**2*b1*b2 + 10*a11*a21*a22*a33*b1*b2 - a11*a21*a22*b1*b2 - 15*a11*a21*a23*a32**2*b1*b3 + 15*a11*a21*a23*a32*a33*b1*b2 - 9*a11*a21*a23*a32*b1*b2 - a11*a21*a32*a33*b1*b3 - a11*a21*a32*b1*b3 + a11*a21*a33**2*b1*b2 - 15*a11*a22**2*a31*a33*b1*b3 + a11*a22**2*a31*b1*b3 + 15*a11*a22**2*a33**2*b1**2 - 10*a11*a22**2*a33*b1**2 - 10*a11*a22**2*a33*b3**2 + a11*a22**2*b1**2 + 15*a11*a22*a23*a31*a32*b1*b3 + 15*a11*a22*a23*a31*a33*b1*b2 - a11*a22*a23*a31*b1*b2 - 30*a11*a22*a23*a32*a33*b1**2 + 10*a11*a22*a23*a32*b1**2 + 10*a11*a22*a23*a32*b3**2 + 10*a11*a22*a23*a33*b2*b3 + 10*a11*a22*a31*a33*b1*b3 + 10*a11*a22*a32*a33*b2*b3 - 10*a11*a22*a33**2*b1**2 - 10*a11*a22*a33**2*b2**2 + 4*a11*a22*a33*b1**2 + 4*a11*a22*a33*b2**2 + 4*a11*a22*a33*b3**2 - 15*a11*a23**2*a31*a32*b1*b2 + 15*a11*a23**2*a32**2*b1**2 - 10*a11*a23**2*a32*b2*b3 - 9*a11*a23*a31*a32*b1*b3 - a11*a23*a31*a33*b1*b2 - a11*a23*a31*b1*b2 - 10*a11*a23*a32**2*b2*b3 + 10*a11*a23*a32*a33*b1**2 + 10*a11*a23*a32*a33*b2**2 - 2*a11*a23*a32*b1**2 - 4*a11*a23*a32*b2**2 - 4*a11*a23*a32*b3**2 - a11*a31*a33*b1*b3 + a11*a33**2*b1**2 + 15*a12**2*a21**2*a33*b3**2 - a12**2*a21**2*b3**2 - 15*a12**2*a21*a23*a31*b3**2 - 15*a12**2*a21*a23*a33*b1*b3 + a12**2*a21*a23*b1*b3 - 15*a12**2*a21*a31*a33*b2*b3 + a12**2*a21*a31*b2*b3 + 15*a12**2*a21*a33**2*b1*b2 - 10*a12**2*a21*a33*b1*b2 + a12**2*a21*b1*b2 + 15*a12**2*a23**2*a31*b1*b3 + 15*a12**2*a23*a31**2*b2*b3 - 15*a12**2*a23*a31*a33*b1*b2 + 9*a12**2*a23*a31*b1*b2 - 15*a12*a13*a21**2*a32*b3**2 - 15*a12*a13*a21**2*a33*b2*b3 + a12*a13*a21**2*b2*b3 + 15*a12*a13*a21*a22*a31*b3**2 + 15*a12*a13*a21*a22*a33*b1*b3 - a12*a13*a21*a22*b1*b3 + 15*a12*a13*a21*a23*a31*b2*b3 + 15*a12*a13*a21*a23*a32*b1*b3 + 15*a12*a13*a21*a31*a32*b2*b3 + 15*a12*a13*a21*a31*a33*b2**2 - a12*a13*a21*a31*b2**2 - a12*a13*a21*a31*b3**2 - 30*a12*a13*a21*a32*a33*b1*b2 + 10*a12*a13*a21*a32*b1*b2 - 9*a12*a13*a21*a33*b1*b3 + a12*a13*a21*b1*b3 - 30*a12*a13*a22*a23*a31*b1*b3 - 15*a12*a13*a22*a31**2*b2*b3 + 15*a12*a13*a22*a31*a33*b1*b2 - 9*a12*a13*a22*a31*b1*b2 - 15*a12*a13*a23*a31**2*b2**2 + 15*a12*a13*a23*a31*a32*b1*b2 + 10*a12*a13*a23*a31*b1*b3 + a12*a13*a31**2*b2*b3 - a12*a13*a31*a33*b1*b2 + a12*a13*a31*b1*b2 - 15*a12*a21**2*a32*a33*b1*b3 + a12*a21**2*a32*b1*b3 + 15*a12*a21**2*a33**2*b1*b2 - 10*a12*a21**2*a33*b1*b2 + a12*a21**2*b1*b2 + 15*a12*a21*a22*a31*a33*b1*b3 - a12*a21*a22*a31*b1*b3 - 15*a12*a21*a22*a33**2*b1**2 + 10*a12*a21*a22*a33*b1**2 + 10*a12*a21*a22*a33*b3**2 - a12*a21*a22*b1**2 + 15*a12*a21*a23*a31*a32*b1*b3 - 30*a12*a21*a23*a31*a33*b1*b2 + 10*a12*a21*a23*a31*b1*b2 + 15*a12*a21*a23*a32*a33*b1**2 - a12*a21*a23*a32*b1**2 - a12*a21*a23*a32*b3**2 - 9*a12*a21*a23*a33*b2*b3 + a12*a21*a23*b2*b3 - 9*a12*a21*a31*a33*b1*b3 + a12*a21*a31*b1*b3 - 9*a12*a21*a32*a33*b2*b3 + a12*a21*a32*b2*b3 + 9*a12*a21*a33**2*b1**2 + 9*a12*a21*a33**2*b2**2 - 4*a12*a21*a33*b1**2 - 4*a12*a21*a33*b2**2 - 2*a12*a21*a33*b3**2 - 15*a12*a22*a23*a31**2*b1*b3 + 15*a12*a22*a23*a31*a33*b1**2 - 9*a12*a22*a23*a31*b1**2 - 9*a12*a22*a23*a31*b3**2 - a12*a22*a23*a33*b1*b3 - a12*a22*a23*b1*b3 - a12*a22*a31*a33*b2*b3 - a12*a22*a31*b2*b3 + a12*a22*a33**2*b1*b2 + 15*a12*a23**2*a31**2*b1*b2 - 15*a12*a23**2*a31*a32*b1**2 + 9*a12*a23**2*a31*b2*b3 + a12*a23**2*a32*b1*b3 + 9*a12*a23*a31**2*b1*b3 + 10*a12*a23*a31*a32*b2*b3 - 9*a12*a23*a31*a33*b1**2 - 9*a12*a23*a31*a33*b2**2 + 3*a12*a23*a31*b1**2 + 3*a12*a23*a31*b2**2 + 3*a12*a23*a31*b3**2 - a12*a23*a32*a33*b1*b2 + a12*a23*a32*b1*b2 - a12*a23*a33*b1*b3 - a12*a31*a33*b2*b3 + a12*a33**2*b1*b2 + 15*a13**2*a21**2*a32*b2*b3 - 15*a13**2*a21*a22*a31*b2*b3 - 15*a13**2*a21*a22*a32*b1*b3 - 15*a13**2*a21*a31*a32*b2**2 + a13**2*a21*a31*b2*b3 + 15*a13**2*a21*a32**2*b1*b2 + 9*a13**2*a21*a32*b1*b3 + 15*a13**2*a22**2*a31*b1*b3 + 15*a13**2*a22*a31**2*b2**2 - 15*a13**2*a22*a31*a32*b1*b2 - 10*a13**2*a22*a31*b1*b3 - a13**2*a31**2*b2**2 + a13**2*a31*a32*b1*b2 + a13**2*a31*b1*b3 + 15*a13*a21**2*a32**2*b1*b3 - 15*a13*a21**2*a32*a33*b1*b2 + 9*a13*a21**2*a32*b1*b2 - 30*a13*a21*a22*a31*a32*b1*b3 + 15*a13*a21*a22*a31*a33*b1*b2 - 9*a13*a21*a22*a31*b1*b2 + 15*a13*a21*a22*a32*a33*b1**2 - 9*a13*a21*a22*a32*b1**2 - 9*a13*a21*a22*a32*b3**2 - a13*a21*a22*a33*b2*b3 - a13*a21*a22*b2*b3 + 15*a13*a21*a23*a31*a32*b1*b2 - 15*a13*a21*a23*a32**2*b1**2 + 10*a13*a21*a23*a32*b2*b3 + 10*a13*a21*a31*a32*b1*b3 - a13*a21*a31*a33*b1*b2 + a13*a21*a31*b1*b2 + 9*a13*a21*a32**2*b2*b3 - 9*a13*a21*a32*a33*b1**2 - 9*a13*a21*a32*a33*b2**2 + 3*a13*a21*a32*b1**2 + 3*a13*a21*a32*b2**2 + 3*a13*a21*a32*b3**2 - a13*a21*a33*b2*b3 + 15*a13*a22**2*a31**2*b1*b3 - 15*a13*a22**2*a31*a33*b1**2 + 9*a13*a22**2*a31*b1**2 + 9*a13*a22**2*a31*b3**2 + a13*a22**2*a33*b1*b3 + a13*a22**2*b1*b3 - 15*a13*a22*a23*a31**2*b1*b2 + 15*a13*a22*a23*a31*a32*b1**2 - 9*a13*a22*a23*a31*b2*b3 - a13*a22*a23*a32*b1*b3 - 10*a13*a22*a31**2*b1*b3 - 9*a13*a22*a31*a32*b2*b3 + 10*a13*a22*a31*a33*b1**2 + 10*a13*a22*a31*a33*b2**2 - 4*a13*a22*a31*b1**2 - 2*a13*a22*a31*b2**2 - 4*a13*a22*a31*b3**2 - a13*a22*a32*a33*b1*b2 - a13*a22*a32*b1*b2 + a13*a23*a31**2*b1*b2 - a13*a23*a31*a32*b1**2 - a13*a23*a31*a32*b2**2 + a13*a23*a31*b2*b3 + a13*a23*a32**2*b1*b2 + a13*a23*a32*b1*b3 + a13*a31**2*b1*b3 + a13*a31*a32*b2*b3 - a13*a31*a33*b1**2 - a13*a32*a33*b1*b2 - a21*a22*a32*a33*b1*b3 - a21*a22*a32*b1*b3 + a21*a22*a33**2*b1*b2 + a21*a23*a32**2*b1*b3 - a21*a23*a32*a33*b1*b2 + a21*a23*a32*b1*b2 - a21*a32*a33*b1*b3 + a21*a33**2*b1*b2 + a22**2*a31*a33*b1*b3 + a22**2*a31*b1*b3 - a22**2*a33**2*b1**2 + a22**2*a33*b3**2 - a22*a23*a31*a32*b1*b3 - a22*a23*a31*a33*b1*b2 - a22*a23*a31*b1*b2 + 2*a22*a23*a32*a33*b1**2 - a22*a23*a32*b3**2 - a22*a23*a33*b2*b3 - a22*a32*a33*b2*b3 + a22*a33**2*b2**2 + a23**2*a31*a32*b1*b2 - a23**2*a32**2*b1**2 + a23**2*a32*b2*b3 + a23*a31*a32*b1*b3 - a23*a31*a33*b1*b2 + a23*a32**2*b2*b3 - a23*a32*a33*b2**2, -6*a11**2*a22**2*a33*b3**2 + 6*a11**2*a22*a23*a32*b3**2 + 6*a11**2*a22*a23*a33*b2*b3 + 6*a11**2*a22*a32*a33*b2*b3 - 6*a11**2*a22*a33**2*b2**2 + 2*a11**2*a22*a33*b2**2 + 2*a11**2*a22*a33*b3**2 - 6*a11**2*a23**2*a32*b2*b3 - 6*a11**2*a23*a32**2*b2*b3 + 6*a11**2*a23*a32*a33*b2**2 - 2*a11**2*a23*a32*b2**2 - 2*a11**2*a23*a32*b3**2 + 12*a11*a12*a21*a22*a33*b3**2 - 6*a11*a12*a21*a23*a32*b3**2 - 6*a11*a12*a21*a23*a33*b2*b3 - 6*a11*a12*a21*a32*a33*b2*b3 + 6*a11*a12*a21*a33**2*b2**2 - 2*a11*a12*a21*a33*b2**2 - 2*a11*a12*a21*a33*b3**2 - 6*a11*a12*a22*a23*a31*b3**2 - 6*a11*a12*a22*a23*a33*b1*b3 - 6*a11*a12*a22*a31*a33*b2*b3 + 6*a11*a12*a22*a33**2*b1*b2 - 2*a11*a12*a22*a33*b1*b2 + 6*a11*a12*a23**2*a31*b2*b3 + 6*a11*a12*a23**2*a32*b1*b3 + 12*a11*a12*a23*a31*a32*b2*b3 - 6*a11*a12*a23*a31*a33*b2**2 + 2*a11*a12*a23*a31*b2**2 + 2*a11*a12*a23*a31*b3**2 - 6*a11*a12*a23*a32*a33*b1*b2 + 2*a11*a12*a23*a32*b1*b2 - 6*a11*a13*a21*a22*a32*b3**2 - 6*a11*a13*a21*a22*a33*b2*b3 + 12*a11*a13*a21*a23*a32*b2*b3 + 6*a11*a13*a21*a32**2*b2*b3 - 6*a11*a13*a21*a32*a33*b2**2 + 2*a11*a13*a21*a32*b2**2 + 2*a11*a13*a21*a32*b3**2 + 6*a11*a13*a22**2*a31*b3**2 + 6*a11*a13*a22**2*a33*b1*b3 - 6*a11*a13*a22*a23*a31*b2*b3 - 6*a11*a13*a22*a23*a32*b1*b3 - 6*a11*a13*a22*a31*a32*b2*b3 + 12*a11*a13*a22*a31*a33*b2**2 - 2*a11*a13*a22*a31*b2**2 - 2*a11*a13*a22*a31*b3**2 - 6*a11*a13*a22*a32*a33*b1*b2 - 2*a11*a13*a22*a33*b1*b3 - 6*a11*a13*a23*a31*a32*b2**2 + 6*a11*a13*a23*a32**2*b1*b2 + 2*a11*a13*a23*a32*b1*b3 - 6*a11*a21*a22*a32*a33*b1*b3 + 6*a11*a21*a22*a33**2*b1*b2 - 2*a11*a21*a22*a33*b1*b2 + 6*a11*a21*a23*a32**2*b1*b3 - 6*a11*a21*a23*a32*a33*b1*b2 + 2*a11*a21*a23*a32*b1*b2 + 6*a11*a22**2*a31*a33*b1*b3 - 6*a11*a22**2*a33**2*b1**2 + 2*a11*a22**2*a33*b1**2 + 2*a11*a22**2*a33*b3**2 - 6*a11*a22*a23*a31*a32*b1*b3 - 6*a11*a22*a23*a31*a33*b1*b2 + 12*a11*a22*a23*a32*a33*b1**2 - 2*a11*a22*a23*a32*b1**2 - 2*a11*a22*a23*a32*b3**2 - 2*a11*a22*a23*a33*b2*b3 - 2*a11*a22*a31*a33*b1*b3 - 2*a11*a22*a32*a33*b2*b3 + 2*a11*a22*a33**2*b1**2 + 2*a11*a22*a33**2*b2**2 + 6*a11*a23**2*a31*a32*b1*b2 - 6*a11*a23**2*a32**2*b1**2 + 2*a11*a23**2*a32*b2*b3 + 2*a11*a23*a31*a32*b1*b3 + 2*a11*a23*a32**2*b2*b3 - 2*a11*a23*a32*a33*b1**2 - 2*a11*a23*a32*a33*b2**2 - 6*a12**2*a21**2*a33*b3**2 + 6*a12**2*a21*a23*a31*b3**2 + 6*a12**2*a21*a23*a33*b1*b3 + 6*a12**2*a21*a31*a33*b2*b3 - 6*a12**2*a21*a33**2*b1*b2 + 2*a12**2*a21*a33*b1*b2 - 6*a12**2*a23**2*a31*b1*b3 - 6*a12**2*a23*a31**2*b2*b3 + 6*a12**2*a23*a31*a33*b1*b2 - 2*a12**2*a23*a31*b1*b2 + 6*a12*a13*a21**2*a32*b3**2 + 6*a12*a13*a21**2*a33*b2*b3 - 6*a12*a13*a21*a22*a31*b3**2 - 6*a12*a13*a21*a22*a33*b1*b3 - 6*a12*a13*a21*a23*a31*b2*b3 - 6*a12*a13*a21*a23*a32*b1*b3 - 6*a12*a13*a21*a31*a32*b2*b3 - 6*a12*a13*a21*a31*a33*b2**2 + 12*a12*a13*a21*a32*a33*b1*b2 - 2*a12*a13*a21*a32*b1*b2 + 2*a12*a13*a21*a33*b1*b3 + 12*a12*a13*a22*a23*a31*b1*b3 + 6*a12*a13*a22*a31**2*b2*b3 - 6*a12*a13*a22*a31*a33*b1*b2 + 2*a12*a13*a22*a31*b1*b2 + 6*a12*a13*a23*a31**2*b2**2 - 6*a12*a13*a23*a31*a32*b1*b2 - 2*a12*a13*a23*a31*b1*b3 + 6*a12*a21**2*a32*a33*b1*b3 - 6*a12*a21**2*a33**2*b1*b2 + 2*a12*a21**2*a33*b1*b2 - 6*a12*a21*a22*a31*a33*b1*b3 + 6*a12*a21*a22*a33**2*b1**2 - 2*a12*a21*a22*a33*b1**2 - 2*a12*a21*a22*a33*b3**2 - 6*a12*a21*a23*a31*a32*b1*b3 + 12*a12*a21*a23*a31*a33*b1*b2 - 2*a12*a21*a23*a31*b1*b2 - 6*a12*a21*a23*a32*a33*b1**2 + 2*a12*a21*a23*a33*b2*b3 + 2*a12*a21*a31*a33*b1*b3 + 2*a12*a21*a32*a33*b2*b3 - 2*a12*a21*a33**2*b1**2 - 2*a12*a21*a33**2*b2**2 + 6*a12*a22*a23*a31**2*b1*b3 - 6*a12*a22*a23*a31*a33*b1**2 + 2*a12*a22*a23*a31*b1**2 + 2*a12*a22*a23*a31*b3**2 - 6*a12*a23**2*a31**2*b1*b2 + 6*a12*a23**2*a31*a32*b1**2 - 2*a12*a23**2*a31*b2*b3 - 2*a12*a23*a31**2*b1*b3 - 2*a12*a23*a31*a32*b2*b3 + 2*a12*a23*a31*a33*b1**2 + 2*a12*a23*a31*a33*b2**2 - 6*a13**2*a21**2*a32*b2*b3 + 6*a13**2*a21*a22*a31*b2*b3 + 6*a13**2*a21*a22*a32*b1*b3 + 6*a13**2*a21*a31*a32*b2**2 - 6*a13**2*a21*a32**2*b1*b2 - 2*a13**2*a21*a32*b1*b3 - 6*a13**2*a22**2*a31*b1*b3 - 6*a13**2*a22*a31**2*b2**2 + 6*a13**2*a22*a31*a32*b1*b2 + 2*a13**2*a22*a31*b1*b3 - 6*a13*a21**2*a32**2*b1*b3 + 6*a13*a21**2*a32*a33*b1*b2 - 2*a13*a21**2*a32*b1*b2 + 12*a13*a21*a22*a31*a32*b1*b3 - 6*a13*a21*a22*a31*a33*b1*b2 + 2*a13*a21*a22*a31*b1*b2 - 6*a13*a21*a22*a32*a33*b1**2 + 2*a13*a21*a22*a32*b1**2 + 2*a13*a21*a22*a32*b3**2 - 6*a13*a21*a23*a31*a32*b1*b2 + 6*a13*a21*a23*a32**2*b1**2 - 2*a13*a21*a23*a32*b2*b3 - 2*a13*a21*a31*a32*b1*b3 - 2*a13*a21*a32**2*b2*b3 + 2*a13*a21*a32*a33*b1**2 + 2*a13*a21*a32*a33*b2**2 - 6*a13*a22**2*a31**2*b1*b3 + 6*a13*a22**2*a31*a33*b1**2 - 2*a13*a22**2*a31*b1**2 - 2*a13*a22**2*a31*b3**2 + 6*a13*a22*a23*a31**2*b1*b2 - 6*a13*a22*a23*a31*a32*b1**2 + 2*a13*a22*a23*a31*b2*b3 + 2*a13*a22*a31**2*b1*b3 + 2*a13*a22*a31*a32*b2*b3 - 2*a13*a22*a31*a33*b1**2 - 2*a13*a22*a31*a33*b2**2, a11**2*a22**2*a33*b3**2 - a11**2*a22*a23*a32*b3**2 - a11**2*a22*a23*a33*b2*b3 - a11**2*a22*a32*a33*b2*b3 + a11**2*a22*a33**2*b2**2 + a11**2*a23**2*a32*b2*b3 + a11**2*a23*a32**2*b2*b3 - a11**2*a23*a32*a33*b2**2 - 2*a11*a12*a21*a22*a33*b3**2 + a11*a12*a21*a23*a32*b3**2 + a11*a12*a21*a23*a33*b2*b3 + a11*a12*a21*a32*a33*b2*b3 - a11*a12*a21*a33**2*b2**2 + a11*a12*a22*a23*a31*b3**2 + a11*a12*a22*a23*a33*b1*b3 + a11*a12*a22*a31*a33*b2*b3 - a11*a12*a22*a33**2*b1*b2 - a11*a12*a23**2*a31*b2*b3 - a11*a12*a23**2*a32*b1*b3 - 2*a11*a12*a23*a31*a32*b2*b3 + a11*a12*a23*a31*a33*b2**2 + a11*a12*a23*a32*a33*b1*b2 + a11*a13*a21*a22*a32*b3**2 + a11*a13*a21*a22*a33*b2*b3 - 2*a11*a13*a21*a23*a32*b2*b3 - a11*a13*a21*a32**2*b2*b3 + a11*a13*a21*a32*a33*b2**2 - a11*a13*a22**2*a31*b3**2 - a11*a13*a22**2*a33*b1*b3 + a11*a13*a22*a23*a31*b2*b3 + a11*a13*a22*a23*a32*b1*b3 + a11*a13*a22*a31*a32*b2*b3 - 2*a11*a13*a22*a31*a33*b2**2 + a11*a13*a22*a32*a33*b1*b2 + a11*a13*a23*a31*a32*b2**2 - a11*a13*a23*a32**2*b1*b2 + a11*a21*a22*a32*a33*b1*b3 - a11*a21*a22*a33**2*b1*b2 - a11*a21*a23*a32**2*b1*b3 + a11*a21*a23*a32*a33*b1*b2 - a11*a22**2*a31*a33*b1*b3 + a11*a22**2*a33**2*b1**2 + a11*a22*a23*a31*a32*b1*b3 + a11*a22*a23*a31*a33*b1*b2 - 2*a11*a22*a23*a32*a33*b1**2 - a11*a23**2*a31*a32*b1*b2 + a11*a23**2*a32**2*b1**2 + a12**2*a21**2*a33*b3**2 - a12**2*a21*a23*a31*b3**2 - a12**2*a21*a23*a33*b1*b3 - a12**2*a21*a31*a33*b2*b3 + a12**2*a21*a33**2*b1*b2 + a12**2*a23**2*a31*b1*b3 + a12**2*a23*a31**2*b2*b3 - a12**2*a23*a31*a33*b1*b2 - a12*a13*a21**2*a32*b3**2 - a12*a13*a21**2*a33*b2*b3 + a12*a13*a21*a22*a31*b3**2 + a12*a13*a21*a22*a33*b1*b3 + a12*a13*a21*a23*a31*b2*b3 + a12*a13*a21*a23*a32*b1*b3 + a12*a13*a21*a31*a32*b2*b3 + a12*a13*a21*a31*a33*b2**2 - 2*a12*a13*a21*a32*a33*b1*b2 - 2*a12*a13*a22*a23*a31*b1*b3 - a12*a13*a22*a31**2*b2*b3 + a12*a13*a22*a31*a33*b1*b2 - a12*a13*a23*a31**2*b2**2 + a12*a13*a23*a31*a32*b1*b2 - a12*a21**2*a32*a33*b1*b3 + a12*a21**2*a33**2*b1*b2 + a12*a21*a22*a31*a33*b1*b3 - a12*a21*a22*a33**2*b1**2 + a12*a21*a23*a31*a32*b1*b3 - 2*a12*a21*a23*a31*a33*b1*b2 + a12*a21*a23*a32*a33*b1**2 - a12*a22*a23*a31**2*b1*b3 + a12*a22*a23*a31*a33*b1**2 + a12*a23**2*a31**2*b1*b2 - a12*a23**2*a31*a32*b1**2 + a13**2*a21**2*a32*b2*b3 - a13**2*a21*a22*a31*b2*b3 - a13**2*a21*a22*a32*b1*b3 - a13**2*a21*a31*a32*b2**2 + a13**2*a21*a32**2*b1*b2 + a13**2*a22**2*a31*b1*b3 + a13**2*a22*a31**2*b2**2 - a13**2*a22*a31*a32*b1*b2 + a13*a21**2*a32**2*b1*b3 - a13*a21**2*a32*a33*b1*b2 - 2*a13*a21*a22*a31*a32*b1*b3 + a13*a21*a22*a31*a33*b1*b2 + a13*a21*a22*a32*a33*b1**2 + a13*a21*a23*a31*a32*b1*b2 - a13*a21*a23*a32**2*b1**2 + a13*a22**2*a31**2*b1*b3 - a13*a22**2*a31*a33*b1**2 - a13*a22*a23*a31**2*b1*b2 + a13*a22*a23*a31*a32*b1**2]


#  Ajuste lineal por mínimos cuadrados

$(x_0,y_0),(x_1,y_1),...,(x_{n-1},y_{n-1})$
$$\hat y=\hat A+\hat Bx$$
$$y=A+Bx+\epsilon$$
$$S(x)=\sum_{i=0}^{n-1}x_i$$
$$S(y)=\sum_{i=0}^{n-1}y_i$$
$$SS(x)=\sum_{i=0}^{n-1}x_i^2$$
$$SXY(X,Y)=\sum_{i=0}^{n-1}x_iy_i$$
$$\Delta(x)=n\sum_{i=0}^{n-1}x_i^2-\left(\sum_{i=0}^{n-1}x_i\right)^2$$
Punto de corte:
\begin{equation}
A=\frac{SS(X)*S(Y)-S(X)*SXY(X,Y)}{\Delta(X)}
\end{equation}
Pendiente:
\begin{equation}
B=\frac{n*SXY(X,Y)-S(X)*S(Y)}{\Delta(X)}
\end{equation}
Incertidumbre en Y:(n-2: sesgo)
\begin{equation}
\sigma_y=\sqrt{\frac{\sum (y_{i}-(A+Bx_{i}))^2}{n-2}}
\end{equation}
Incertidumbre en A:
\begin{equation}
\Delta A=\sigma_{y}\frac{\sqrt{\sum x_i^2}}{\Delta(X)}
\end{equation}

```python
import linear_fitting as ajuste
import pandas as pd
import matplotlib.pyplot as plt
import numpy as np
```

```python
#para guardar figuras con latex
from matplotlib import rc
plt.rc('text', usetex=True)
import seaborn as sns
plt.style.use('seaborn-poster')
sns.set_context("poster")
```

```python
#data1 corresponde a las mediciones de los ángulos de incidencia
#data2 corresponde a las mediciones de los ángulos de refraccion
data1=pd.read_csv("incident_angle_030347.txt",delimiter="\t",skiprows=1)
data2=pd.read_csv("refraction_trans_ang_030333.txt",delimiter="\t",skiprows=1)
```

```python
data1
```

      t
      θ

      0
      0.000000
      170.279427

      1
      0.100100
      168.282275

      2
      0.200200
      168.630136

      3
      0.300300
      167.099544

      4
      0.400400
      164.158466

      ...
      ...
      ...

      167
      16.717745
      91.336843

      168
      16.817856
      90.095768

      169
      16.917967
      91.116299

      170
      17.018067
      89.826581

      171
      17.118167
      90.995053

172 rows × 2 columns

```python
data1["θ"]
```

    0      170.279427
    1      168.282275
    2      168.630136
    3      167.099544
    4      164.158466
              ...
    167     91.336843
    168     90.095768
    169     91.116299
    170     89.826581
    171     90.995053
    Name: θ, Length: 172, dtype: float64

```python
plt.plot(data1["t"],data1["θ"],label="Datos de incidencia")
plt.plot(data2["t"],data2["θ"],label="Datos de refracción")
plt.xlabel("t(s)")
plt.ylabel(r"$\displaystyle \theta(grados)$")
plt.legend()

```

```python
pos=0
while(np.abs(data2["θ"][pos])<1e-9):
    print(data2["θ"][pos])
    print(pos)
    pos+=1
```

    0.0
    0
    0.0
    1
    0.0
    2
    0.0
    3
    0.0
    4
    0.0
    5
    0.0
    6
    0.0
    7
    0.0
    8
    0.0
    9
    0.0
    10
    0.0
    11
    0.0
    12
    0.0
    13
    0.0
    14
    0.0
    15
    0.0
    16
    0.0
    17
    0.0
    18
    0.0
    19
    0.0
    20
    0.0
    21
    0.0
    22
    0.0
    23
    0.0
    24
    0.0
    25
    0.0
    26
    0.0
    27
    0.0
    28
    0.0
    29
    0.0
    30
    0.0
    31
    0.0
    32
    0.0
    33
    0.0
    34
    0.0
    35
    0.0
    36
    0.0
    37
    0.0
    38
    0.0
    39
    0.0
    40
    0.0
    41
    0.0
    42
    0.0
    43
    0.0
    44
    0.0
    45
    0.0
    46
    0.0
    47
    0.0
    48
    0.0
    49
    0.0
    50
    0.0
    51
    0.0
    52
    0.0
    53
    0.0
    54
    0.0
    55
    0.0
    56

```python
#Solo tomaremos los angulos y no los tiempos
ang_inc=data1["θ"][57:]-90#-90*np.ones(len(data1["θ"][57:]))
ang_ref=data2["θ"][57:]+90#+90
```

```python
ang_ref
```

    57     80.236963
    58     79.556094
    59     77.255568
    60     75.107764
    61     73.294421
             ...
    167     6.399138
    168     5.783896
    169     4.974365
    170     4.152263
    171     2.794425
    Name: θ, Length: 115, dtype: float64

Modelo físico:
$$n_i\sin \theta_i=n_r\sin\theta_r$$
Transformamos coordenadas:
$$y=\sin\theta_2=\sin\theta_{tr}$$
$$x=\sin\theta_1=\sin\theta_{inc}$$
$$n_ix=n_ry$$
ni: Gelatina\
nr: aire\
$$y=\frac{n_i}{n_r}x$$
Ajuste:
$$\hat{y}=Bx+A$$

Condiciones para poder linealizar una función.\
Ecuaciones con variables separables:
$$ln(y)=e^xy$$
$$\frac{ln(y)}{y}=e^x$$
$$v=\frac{ln(y)}{y}$$
$u=e^x$
$$U=X$$

```python
X=np.sin(ang_inc*np.pi/180)
Y=np.sin(ang_ref*np.pi/180)
```

```python
print(ajuste.A(X,Y))
print(ajuste.B(X,Y))
```

    0.021429047972105878
    1.3398208993672323

```python
ajuste.fit(X,Y)
```

    'y=0.0214+1.3398208993672323x'

```python
plt.scatter(X,Y,label="Datos")
plt.plot(X,ajuste.y_pred(X,X,Y),label=f"{ajuste.fit(X,Y)}")
# plt.xlabel("t(s)")
# plt.ylabel(r"$\displaystyle \theta(grados)$")
plt.legend()
plt.savefig("Ajuste")
```

#  Mínimos Cuadrados
    Dados $(x_0,y_0),(x_1,y_1),...,(x_n,y_n)$, la optimización por mínimos cuadrados busca estimar los parámetros de $b_0$, $b_1$ que cumplen:
\begin{equation}
\hat{y}=\hat{b}_0\vec{x}+\hat{b}_1
\end{equation}
$$\hat{b}=(b_0,b_1)$$
La solucion para b es:
\begin{equation}
\hat{b}=(X^{T}X)^{-1}X^{T}Y
\end{equation}
\begin{equation}
X=\left(
\begin{matrix}
x_0 & 1\\
x_1 & 1\\
.. & ...\\
x_{n-1} &1
\end{matrix}\right)
\end{equation}
\begin{equation}
Y=\left(
\begin{matrix}
y_0\\
y_1\\
..\\
y_{n-1}
\end{matrix}\right)
\end{equation}
$$X^TX=\left(
\begin{matrix}
\sum x_i & n\\
\sum x_i^2 & \sum x_i\\
\end{matrix}\right)$$
Donde  $X=[[x0,1],[x1,1],...,[xn,1]]$ es una matriz de tamaño $n\times 2$, $\hat{b}=[[b_0],[b_1]]$ un array de tamaño $2\times 1$

```python
Xaumentada=np.ones((len(X),2))
Xaumentada[:,0]=X
X_t_X=np.dot(Xaumentada.T,Xaumentada)
```

```python
beta=np.dot(np.linalg.inv(X_t_X),np.dot(Xaumentada.T,Y))
print(beta)
```

    [1.3398209  0.02142905]

```python

```


```python
import sympy as sm
import sympy.physics.mechanics as me
me.init_vprinting()

q1, q2, q3, q4, q5 = me.dynamicsymbols('q1, q2, q3, q4, q5')
l = sm.symbols('l')

N = me.ReferenceFrame('N')
A = me.ReferenceFrame('A')
B = me.ReferenceFrame('B')
C = me.ReferenceFrame('C')

A.orient_axis(N, q3, N.z)
B.orient_axis(A, q4, A.z)
C.orient_axis(A, q5, A.z)

O = me.Point('O')
Ao = me.Point('A_o')
Bo = me.Point('B_o')
Co = me.Point('C_o')

Ao.set_pos(O, q1*N.x + q2*N.y)
Bo.set_pos(Ao, l/2*A.x)
Co.set_pos(Ao, -l/2*A.x)

O.set_vel(N, 0)

Bo.v2pt_theory(Ao, N, A)

Co.v2pt_theory(Ao, N, A)

u1, u2, u3, u4, u5 = me.dynamicsymbols('u1, u2, u3, u4, u5')
```

```python
fk = sm.Matrix([
    u1 - q1.diff(),
    u2 - q2.diff(),
    u3 - l*q3.diff()/2,
    u4 - q4.diff(),
    u5 - q5.diff(),
])
fk
```

```python
q = sm.Matrix([q1, q2, q3, q4, q5])
qd = q.diff()
qd
```

```python
Mk = fk.jacobian(qd)
Mk
```

```python
qd_zero = {qdi: 0 for qdi in qd}
qd_zero
```

```python
gk = fk.xreplace(qd_zero)
gk
```

```python
qd_sol = -Mk.LUsolve(gk)
qd_sol
```

```python
qd_repl = dict(zip(qd, qd_sol))
qd_repl
```

```python
fn = sm.Matrix([
    Bo.vel(N).dot(B.y),
    Co.vel(N).dot(C.y)
])
fn = sm.trigsimp(fn)
fn
```

```python
fn = fn.xreplace(qd_repl)
fn
```

```python
me.find_dynamicsymbols(fn)
```

```python
ur = sm.Matrix([u1, u2])  # dependent
us = sm.Matrix([u3, u4, u5])  # independent
```

```python
Mn = fn.jacobian(ur)
Mn
```

```python
ur_zero = {uri: 0 for uri in ur}
ur_zero
```

```python
gn = fn.xreplace(ur_zero)
gn
```

```python
ur_sol = -Mn.LUsolve(gn)
ur_sol = sm.trigsimp(ur_sol, method='fu')
ur_sol
```

```python
ur_repl = dict(zip(ur, ur_sol))
ur_repl
```

```python
Mk
```

```python
gk = gk.xreplace(ur_repl)
gk
```

```python
m, I = sm.symbols('m, I')
```

```python
N_v_Bo = Bo.vel(N).xreplace(qd_repl).xreplace(ur_repl)
N_v_Bo
```

```python
N_v_Co = Co.vel(N).xreplace(qd_repl).xreplace(ur_repl)
N_v_Co
```

```python
N_w_B = B.ang_vel_in(N).xreplace(qd_repl).xreplace(ur_repl)
N_w_B
```

```python
N_w_C = C.ang_vel_in(N).xreplace(qd_repl).xreplace(ur_repl)
N_w_C
```

```python
v_Bo, v_Co, w_B, w_C = me.partial_velocity((N_v_Bo, N_v_Co, N_w_B, N_w_C),
                                           (u3, u4, u5), N)
```

```python
v_Bo
```

```python
v_Co
```

```python
w_B
```

```python
w_C
```

```python
I_B_Bo = I*me.outer(N.z, N.z)
I_C_Co = I_B_Bo
```

```python
me.find_dynamicsymbols(N_v_Bo.dt(N), reference_frame=N)
```

```python
N_a_Bo = N_v_Bo.dt(N).xreplace(qd_repl).xreplace(ur_repl)
me.find_dynamicsymbols(N_a_Bo, reference_frame=N)
```

```python
N_a_Co = N_v_Co.dt(N).xreplace(qd_repl).xreplace(ur_repl)
me.find_dynamicsymbols(N_a_Co, reference_frame=N)
```

```python
N_alp_B = N_w_B.dt(N).xreplace(qd_repl).xreplace(ur_repl)
me.find_dynamicsymbols(N_alp_B, reference_frame=N)
```

```python
N_alp_C = N_w_C.dt(N).xreplace(qd_repl).xreplace(ur_repl)
me.find_dynamicsymbols(N_alp_C, reference_frame=N)
```

```python
Rs_Bo = -m*N_a_Bo
Rs_Co = -m*N_a_Co

Ts_B = -I_B_Bo.dot(N_alp_B)
Ts_C = -I_C_Co.dot(N_alp_C)
```

```python
F3s = (v_Bo[0].dot(Rs_Bo) + v_Co[0].dot(Rs_Co) +
       w_B[0].dot(Ts_B) + w_C[0].dot(Ts_C))
me.find_dynamicsymbols(F3s)
```

```python
F4s = (v_Bo[1].dot(Rs_Bo) + v_Co[1].dot(Rs_Co) +
       w_B[1].dot(Ts_B) + w_C[1].dot(Ts_C))
me.find_dynamicsymbols(F4s)
```

```python
F5s = (v_Bo[2].dot(Rs_Bo) + v_Co[2].dot(Rs_Co) +
       w_B[2].dot(Ts_B) + w_C[2].dot(Ts_C))
me.find_dynamicsymbols(F5s)
```

```python
Frs = sm.Matrix([F3s, F4s, F5s])
```

```python
usd = us.diff()
usd_zero = {usdi: 0 for usdi in usd}
Md = Frs.jacobian(usd)
gd = Frs.xreplace(usd_zero)
```

```python
me.find_dynamicsymbols(Md)
```

```python
me.find_dynamicsymbols(gd)
```

```python
me.find_dynamicsymbols(Mk)
```

```python
me.find_dynamicsymbols(gk)
```

```python
Md.free_symbols
```

```python
p = sm.Matrix([l, m, I])
```

```python
eval_kd = sm.lambdify((q, us, p), (Mk, gk, Md, gd))
```

```python
import numpy as np

def eval_rhs(t, x, p):
    q = x[:5]
    us = x[5:]

    Mk, gk, Md, gd = eval_kd(q, us, p)

    qd = -np.linalg.solve(Mk, gk.squeeze())
    usd = -np.linalg.solve(Md, gd.squeeze())

    return np.hstack((qd, usd))
```

```python
x0 = np.array([
    0.0,  # q1 [m]
    0.0,  # q2 [m]
    0.0,  # q3 [rad]
    np.deg2rad(5.0),  # q4 [rad]
    -np.deg2rad(3.0),  # q5 [rad]
    0.2,  # u3 [m/s]
    0.05,  # u4 [rad/s]
    0.0,  # u5 [rad/s]
])

p_vals = np.array([
    0.5,  # l [m]
    2.0,  # m [kg]
    0.5*0.1**2,  # I [kg*m^2]
])
```

```python
eval_rhs(0.3, x0, p_vals)
```

```python
from scipy.integrate import solve_ivp
```

```python
t0, tf = 0.0, 5.0
tspan = np.linspace(t0, tf, num=100)
sol = solve_ivp(eval_rhs, (0.0, 5.0), x0, args=(p_vals,), t_eval=tspan)
```

```python
import matplotlib.pyplot as plt
```

```python
%matplotlib notebook
```

```python
fig, ax = plt.subplots()
ax.plot(sol.t, sol.y[:2].T)
```

```python
fig, ax = plt.subplots()
ax.plot(sol.t, sol.y[2:5].T)
```

```python
fig, ax = plt.subplots()
ax.plot(sol.y[0], sol.y[1])
ax.set_aspect('equal')
```

```python

```