```python
# import what we need
import nltk
from nltk.stem.lancaster import LancasterStemmer ## used to stem words
import tensorflow
import random
import json # used to read in json file
stemmer = LancasterStemmer()

import numpy as np
import tensorflow.keras as keras

# printing
from IPython.display import display
import sympy as sp
sp.init_printing(use_latex=True)

# Plotting
import matplotlib.pyplot as plt
%matplotlib inline

# Generating and printing confusion matrix
import pandas as pd
import seaborn as sn
```

```python
# load json file
with open("intents.json") as file:
    data = json.load(file)
```

```python
# need to loop through random training, add each result to seperate list

predicted = []
actual = []

model_acc = []
times_to_loop = 30

for times in range(times_to_loop):

    words = []
    labels = []

    docs_x = []
    docs_y = []

    test_patterns = []

    # loop through dict to grab our values
    for intent in data['intents']:
        rand = random.randint(0, len(intent['patterns'])-1)
        test_pattern = intent['patterns'][rand]
        test_patterns.append(test_pattern)

        for pattern in intent['patterns']:
            if pattern != test_pattern:
                # stemming get the root of word, when traiing model
                # only care abt the main meaning of word
                # need to tokenize/split words
                wrds = nltk.word_tokenize(pattern)
                words.extend(wrds) ## add all wrds to list
                docs_x.append(wrds)
                # for each pattern add another item in this var to track what tag its part of
                # each entry in docs_x (tokenized words/wrds) will correspond to (intent) docs_y so we know how to classify
                # each of the patterns
                docs_y.append(intent['tag'])

                if intent['tag'] not in labels:
                    labels.append(intent['tag']) # grab all diff tags we need

    # stem words and remove duplicate words. also lower all words so dont confuse the network into
    # thinking spelling case has different meaning
    words = [stemmer.stem(w.lower()) for w in words if w!= '?' ] # remove any Question marks, dont want this
    words = sorted(list(set(words)))                             # to have any meaning to our model
    # sort the labels also
    labels = sorted(labels)

    # prepare data before we feed into the model
    # need to convert our text in lists to nums (One-hot-encoding) so our network can
    # categorize the data
    training = [] # input data goes here
    output = []

    out_empty = [0 for _ in range(len(labels))]

    for x, doc in enumerate(docs_x):

        bag = [] # will be our bag of one hot encoded words
        wrds = [stemmer.stem(w) for w in doc] # stem all the words in patterns

        # go through all diff words in list that are stemmed
        # and add either 1 or 0 to bag of words depending on if this word is in the
        # main words list "(words = sorted(list(set(words))))"
        for w in words:
            # if word here place 1 representing that word exists
            if w in wrds:
                bag.append(1)
            else:
                # word isn't here place 0
                bag.append(0)

        # now generate output that has either 0's or 1's representing the tag that is word
        output_row = out_empty[:]
        # will look through labels list and check where tag is in list and set that value to 1
        # in the output_row
        output_row[labels.index(docs_y[x])] = 1

        # now we have both lists that are now One-hot-encoded !!
        training.append(bag)
        output.append(output_row)

    # turn lists into numpy array
    training = np.array(training)
    output = np.array(output)

    # Multi-layer net with ReLU hidden layer
    model = keras.models.Sequential()

    model.add(keras.layers.Dense(8,input_dim=training.shape[1],activation='relu',
     bias_initializer=keras.initializers.Constant(0.1)))

    model.add(keras.layers.Dense(8,activation='relu',
     bias_initializer=keras.initializers.Constant(0.1)))

    model.add(keras.layers.Dense(8,activation='relu',
     bias_initializer=keras.initializers.Constant(0.1)))

    model.add(keras.layers.Dense(8,activation='relu',
     bias_initializer=keras.initializers.Constant(0.1)))

    # Output layer (size 6), softmax activation function
    model.add(keras.layers.Dense(output.shape[1],activation='softmax'))

    # Compile as above (default learning rate and other
    # hyperparameters for the Adam optimizer).
    # default learning rate for adam optimizer is 0.001

    model.compile(loss=keras.losses.CategoricalCrossentropy(),
     optimizer=keras.optimizers.Nadam(learning_rate=0.001),
     metrics=[keras.metrics.CategoricalAccuracy()])

    # Train it!
    history = model.fit(training, output,
     batch_size=1,
     epochs=100,
     verbose=0)

    # print loss and accuracy from training
    score = model.evaluate(training,output,verbose=1)
    print("printing model.fit stats for loop: ",times + 1)
    print("Loss:",score[0])
    print("Accuracy:",score[1])

    def bag_of_words(s, words):
        bag = [0 for _ in range(len(words))]

        s_words = nltk.word_tokenize(s)
        s_words = [stemmer.stem(word.lower()) for word in s_words]

        for se in s_words:
            for i, w in enumerate(words):
                if w == se:
                    bag[i] = 1

        return np.array(bag)

    def run_test(show_successes=True, show_failures=True):
        successes = []
        failures = []

        for i in range(len(test_patterns)):
            results = model.predict(np.array([bag_of_words(test_patterns[i], words)]))[0]

            results_index = np.argmax(results)
            tag = labels[results_index]

            j = 0
            # protect against unknown questions , if model is unsure what user ask they will ask user to rephrase their question
            if results[results_index] > 0.92:   # the value when the model cant recognize is 0.9133338 so i set this value just above it
                for tg in data["intents"]:
                    if tg['tag'] == tag:
                        responses = tg['responses']
                        predicted.append(j)
                    else:
                        j += 1

                answer = random.choice(responses)

            else:
                answer = 'Answer not found'
                predicted.append(-1)

            real_tag = data["intents"][i]
            real_responses = real_tag["responses"]

            actual.append(i)

            if answer in real_responses:
                successes.append([test_patterns[i], answer])
            else:
                failures.append([test_patterns[i], answer])

        print("print model.predict stats for loop: ",times + 1)
        print( "Percentage correct: {:.2%}".format(len(successes)/(len(successes)+len(failures))) )
        acc_score = round((len(successes)/(len(successes)+len(failures))),2)

        model_acc.append(acc_score)

    run_test()

```

    5/5 [==============================] - 0s 751us/step - loss: 6.0853e-04 - categorical_accuracy: 1.0000
    printing model.fit stats for loop:  1
    Loss: 0.0006085267523303628
    Accuracy: 1.0
    print model.predict stats for loop:  1
    Percentage correct: 42.86%
    5/5 [==============================] - 0s 750us/step - loss: 0.0017 - categorical_accuracy: 1.0000
    printing model.fit stats for loop:  2
    Loss: 0.0016597019275650382
    Accuracy: 1.0
    print model.predict stats for loop:  2
    Percentage correct: 57.14%
    5/5 [==============================] - 0s 750us/step - loss: 1.4327e-04 - categorical_accuracy: 1.0000
    printing model.fit stats for loop:  3
    Loss: 0.00014327417011372745
    Accuracy: 1.0
    print model.predict stats for loop:  3
    Percentage correct: 52.38%
    5/5 [==============================] - 0s 750us/step - loss: 1.5939e-04 - categorical_accuracy: 1.0000
    printing model.fit stats for loop:  4
    Loss: 0.00015938848082441837
    Accuracy: 1.0
    print model.predict stats for loop:  4
    Percentage correct: 47.62%
    5/5 [==============================] - 0s 750us/step - loss: 3.5855e-05 - categorical_accuracy: 1.0000
    printing model.fit stats for loop:  5
    Loss: 3.585473677958362e-05
    Accuracy: 1.0
    print model.predict stats for loop:  5
    Percentage correct: 52.38%
    5/5 [==============================] - 0s 750us/step - loss: 2.6011e-04 - categorical_accuracy: 1.0000
    printing model.fit stats for loop:  6
    Loss: 0.00026011199224740267
    Accuracy: 1.0
    print model.predict stats for loop:  6
    Percentage correct: 38.10%
    5/5 [==============================] - 0s 1ms/step - loss: 1.2063e-04 - categorical_accuracy: 1.0000
    printing model.fit stats for loop:  7
    Loss: 0.00012063293979736045
    Accuracy: 1.0
    print model.predict stats for loop:  7
    Percentage correct: 57.14%
    5/5 [==============================] - 0s 750us/step - loss: 3.0557e-05 - categorical_accuracy: 1.0000
    printing model.fit stats for loop:  8
    Loss: 3.055726847378537e-05
    Accuracy: 1.0
    print model.predict stats for loop:  8
    Percentage correct: 42.86%
    5/5 [==============================] - 0s 1ms/step - loss: 1.8334e-04 - categorical_accuracy: 1.0000
    printing model.fit stats for loop:  9
    Loss: 0.00018334118067286909
    Accuracy: 1.0
    print model.predict stats for loop:  9
    Percentage correct: 66.67%
    5/5 [==============================] - 0s 1ms/step - loss: 1.4770e-04 - categorical_accuracy: 1.0000
    printing model.fit stats for loop:  10
    Loss: 0.00014770295820198953
    Accuracy: 1.0
    print model.predict stats for loop:  10
    Percentage correct: 42.86%
    5/5 [==============================] - 0s 750us/step - loss: 3.7802e-05 - categorical_accuracy: 1.0000
    printing model.fit stats for loop:  11
    Loss: 3.780177212320268e-05
    Accuracy: 1.0
    print model.predict stats for loop:  11
    Percentage correct: 47.62%
    5/5 [==============================] - 0s 750us/step - loss: 8.4388e-04 - categorical_accuracy: 1.0000
    printing model.fit stats for loop:  12
    Loss: 0.0008438754011876881
    Accuracy: 1.0
    print model.predict stats for loop:  12
    Percentage correct: 57.14%
    5/5 [==============================] - 0s 749us/step - loss: 4.5845e-04 - categorical_accuracy: 1.0000
    printing model.fit stats for loop:  13
    Loss: 0.0004584469133988023
    Accuracy: 1.0
    print model.predict stats for loop:  13
    Percentage correct: 47.62%
    5/5 [==============================] - 0s 750us/step - loss: 5.5915e-05 - categorical_accuracy: 1.0000
    printing model.fit stats for loop:  14
    Loss: 5.591489389189519e-05
    Accuracy: 1.0
    print model.predict stats for loop:  14
    Percentage correct: 23.81%
    5/5 [==============================] - 0s 1ms/step - loss: 6.9338e-04 - categorical_accuracy: 1.0000
    printing model.fit stats for loop:  15
    Loss: 0.0006933846743777394
    Accuracy: 1.0
    print model.predict stats for loop:  15
    Percentage correct: 57.14%
    5/5 [==============================] - 0s 750us/step - loss: 4.3738e-05 - categorical_accuracy: 1.0000
    printing model.fit stats for loop:  16
    Loss: 4.373756382847205e-05
    Accuracy: 1.0
    print model.predict stats for loop:  16
    Percentage correct: 47.62%
    5/5 [==============================] - 0s 750us/step - loss: 1.6448e-04 - categorical_accuracy: 1.0000
    printing model.fit stats for loop:  17
    Loss: 0.000164476950885728
    Accuracy: 1.0
    print model.predict stats for loop:  17
    Percentage correct: 38.10%
    5/5 [==============================] - 0s 750us/step - loss: 5.6918e-05 - categorical_accuracy: 1.0000
    printing model.fit stats for loop:  18
    Loss: 5.6918361224234104e-05
    Accuracy: 1.0
    print model.predict stats for loop:  18
    Percentage correct: 52.38%
    5/5 [==============================] - 0s 750us/step - loss: 1.3992e-04 - categorical_accuracy: 1.0000
    printing model.fit stats for loop:  19
    Loss: 0.0001399209868395701
    Accuracy: 1.0
    print model.predict stats for loop:  19
    Percentage correct: 42.86%
    5/5 [==============================] - 0s 750us/step - loss: 4.5245e-05 - categorical_accuracy: 1.0000
    printing model.fit stats for loop:  20
    Loss: 4.5245422370499e-05
    Accuracy: 1.0
    print model.predict stats for loop:  20
    Percentage correct: 57.14%
    5/5 [==============================] - 0s 751us/step - loss: 1.5712e-04 - categorical_accuracy: 1.0000
    printing model.fit stats for loop:  21
    Loss: 0.00015711896412540227
    Accuracy: 1.0
    print model.predict stats for loop:  21
    Percentage correct: 61.90%
    5/5 [==============================] - 0s 1000us/step - loss: 1.1248e-04 - categorical_accuracy: 1.0000
    printing model.fit stats for loop:  22
    Loss: 0.00011247777001699433
    Accuracy: 1.0
    print model.predict stats for loop:  22
    Percentage correct: 42.86%
    5/5 [==============================] - 0s 1ms/step - loss: 8.7137e-05 - categorical_accuracy: 1.0000
    printing model.fit stats for loop:  23
    Loss: 8.713659190107137e-05
    Accuracy: 1.0
    print model.predict stats for loop:  23
    Percentage correct: 57.14%
    5/5 [==============================] - 0s 500us/step - loss: 0.0011 - categorical_accuracy: 1.0000
    printing model.fit stats for loop:  24
    Loss: 0.0010951794683933258
    Accuracy: 1.0
    print model.predict stats for loop:  24
    Percentage correct: 47.62%
    5/5 [==============================] - 0s 750us/step - loss: 1.0389e-04 - categorical_accuracy: 1.0000
    printing model.fit stats for loop:  25
    Loss: 0.0001038896371028386
    Accuracy: 1.0
    print model.predict stats for loop:  25
    Percentage correct: 66.67%
    5/5 [==============================] - 0s 750us/step - loss: 7.2560e-05 - categorical_accuracy: 1.0000
    printing model.fit stats for loop:  26
    Loss: 7.256019307533279e-05
    Accuracy: 1.0
    print model.predict stats for loop:  26
    Percentage correct: 52.38%
    5/5 [==============================] - 0s 750us/step - loss: 3.6337e-05 - categorical_accuracy: 1.0000
    printing model.fit stats for loop:  27
    Loss: 3.633672167779878e-05
    Accuracy: 1.0
    print model.predict stats for loop:  27
    Percentage correct: 52.38%
    5/5 [==============================] - 0s 750us/step - loss: 2.1916e-04 - categorical_accuracy: 1.0000
    printing model.fit stats for loop:  28
    Loss: 0.00021916213154327124
    Accuracy: 1.0
    print model.predict stats for loop:  28
    Percentage correct: 52.38%
    5/5 [==============================] - 0s 750us/step - loss: 2.0746e-04 - categorical_accuracy: 1.0000
    printing model.fit stats for loop:  29
    Loss: 0.00020745917572639883
    Accuracy: 1.0
    print model.predict stats for loop:  29
    Percentage correct: 57.14%
    5/5 [==============================] - 0s 1ms/step - loss: 4.7308e-04 - categorical_accuracy: 1.0000
    printing model.fit stats for loop:  30
    Loss: 0.00047308116336353123
    Accuracy: 1.0
    print model.predict stats for loop:  30
    Percentage correct: 38.10%

```python
model_acc
```

```python
y_actual = pd.Series(actual, name="Actual")
y_predicted = pd.Series(predicted, name="Predicted")
df_confusion = pd.crosstab(y_actual, y_predicted)

sn.heatmap(df_confusion, annot=True)
plt.show()
```

```python
# A custom function to calculate
# probability distribution function
def pdf(x):
    mean = np.mean(x)
    std = np.std(x)
    y_out = 1/(std * np.sqrt(2 * np.pi)) * np.exp( - (x - mean)**2 / (2 * std**2))
    return y_out

```

```python
x = np.arange(len(model_acc))
y = pdf(x)
```

```python
# Plotting the bell-shaped curve
plt.style.use('seaborn')
plt.figure(figsize = (6, 6))
plt.plot(x, y, color = 'black',
         linestyle = 'dashed')

plt.scatter( x, y, marker = 'o', s = 25, color = 'red')
plt.show()
```

```python
#  get sum of list of validation scores
total = sum(model_acc)
```

```python
total
```

```python
# calculate average
avg = total/len(model_acc)
```

```python
avg
```

```python
#calculate standard error of the mean
SE = np.std(model_acc, ddof=1) / np.sqrt(np.size(model_acc))
```

```python
# calc Confidence interval for 95% confidence level
avg + (1.96 * SE) # calc upper confidence limit
```

```python
avg - (1.96 * SE) # calc lower confidence limit
```

```python

```


# 非 GIAO 的 RHF 磁化率数值导数计算方式简述

> 创建时间：2020-08-27

在这篇文档中，我们会讨论使用 PySCF 以及其作为 libcint 的接口，计算非 GIAO 的 RHF 数值磁化率的程序。该文档大量参考 PySCF 的代码 [magnetizability/rhf.py](https://github.com/pyscf/pyscf/blob/master/pyscf/prop/magnetizability/rhf.py) 与 [nmr/rhf.py](https://github.com/pyscf/pyscf/blob/master/pyscf/prop/nmr/rhf.py)。一些公式符号参考 Atkins, Friedman [^Atkins-Friedman.Oxford.2010]。

我们的讨论中所使用到的分子体系 `mol` 会是非对称的氨分子，并且取用最小基组。规范原点 (Gauge Origin) 会取在坐标原点上 `coord_orig`。其 RHF 计算放在实例 `mf`，而磁化率计算实例会放在 `mf_mag`。

```python
from pyscf import gto, scf
from pyscf.prop import nmr, magnetizability
import numpy as np

np.set_printoptions(precision=5, linewidth=150, suppress=True)
```

```python
mol = gto.Mole()
mol.atom = """
N  0.  0.  0.
H  0.  1.  0.2
H  0.1 0.3 1.5
H  0.9 0.4 -.2
"""
mol.basis = "STO-3G"
mol.verbose = 0
mol.build()
coord_orig = np.zeros(3)
```

其自洽场能量为

```python
mf = scf.RHF(mol).run()
mf.e_tot
```

    -55.253540514686556

其磁化率张量 $\xi_{ts}$ 为 (其中，$t, s \in \{ x, y, z \}$ 表示三个坐标方向，需要注意这里选择了规范原点为坐标原点，若选取其它坐标则会得到非常不同的结果)

```python
mf_mag = magnetizability.RHF(mf)
mf_mag.gauge_orig = coord_orig
mf_mag.kernel()
```

    array([[-4.94475,  0.21773, -0.08268],
           [ 0.21773, -4.27801,  0.49885],
           [-0.08268,  0.49885, -4.15348]])

## 基础概念

### 分子能量作为外加微扰量的函数

我们指出，磁化率可以看作是分子处在某一恒定外加磁场 $\boldsymbol{\mathscr{B}}$ 下 (作为三维矢量)，所产生的能量变化的表征：

$$
E_\mathrm{tot} (\boldsymbol{\mathscr{B}}) = E_\mathrm{tot}^{(0)} + E_\mathrm{tot}^{(1)} \boldsymbol{\mathscr{B}} + E_\mathrm{tot}^{(2)} \boldsymbol{\mathscr{B}}^2 + \cdots
$$

以一般物理的约定俗成而言，对于外加的磁场微扰 (Atkins and Friedman, eq 13.34)

$$
E_\mathrm{tot}^{(2)} = - \frac{1}{2} \boldsymbol{\mathscr{B}}^\dagger \boldsymbol{\xi} \boldsymbol{\mathscr{B}} = - \frac{1}{2} \sum_{t, s \in \{ x, y, z \}} \mathscr{B}_t \xi_{ts} \mathscr{B}_s
$$

其中，$\boldsymbol{\xi}$ 是二维对称矩阵 (或称张量，如之前代码所展示)。我们使用了 $\boldsymbol{\xi}$ (Atkins and Friedman, eq 13.34, termed as *magnetizability*) 而非 $\boldsymbol{\chi}$ (Atkins and Friedman, eq 13.3c, termed as *magnetic susceptibility*) 来表示磁化率。因此，磁化率本身可以表示为 (矩阵元的形式与向量形式)

$$
\xi_{ts} = - \frac{\partial^2 E_\mathrm{tot} (\boldsymbol{\mathscr{B}})}{\partial \mathscr{B}_t \partial \mathscr{B}_s},
\quad \boldsymbol{\xi} = - \boldsymbol{\nabla}_{\boldsymbol{\mathscr{B}}} \boldsymbol{\nabla}_{\boldsymbol{\mathscr{B}}}^\dagger E_\mathrm{tot} (\boldsymbol{\mathscr{B}})
$$

### 哈密顿算符作为外加微扰量的算符

能量可以通过波函数在哈密顿算符的变分极小值处的期望获得：

$$
E_\mathrm{tot} (\boldsymbol{\mathscr{B}}) = \langle \Psi (\boldsymbol{\mathscr{B}}) | \hat H (\boldsymbol{\mathscr{B}}) | \Psi (\boldsymbol{\mathscr{B}}) \rangle
$$

其中，

$$
\hat H (\boldsymbol{\mathscr{B}}) = \sum_{i} \hat h (\boldsymbol{\mathscr{B}}, \boldsymbol{r}_i) + \hat V_\mathrm{ee} + \hat V_\mathrm{NN}
$$

上述算符是体系的多电子总哈密顿算符；而 $\hat h (\boldsymbol{\mathscr{B}})$ 则是单电子的 Core Hamiltonian 算符；$\hat V_\mathrm{ee}$ 为电子互斥算符，$\hat V_\mathrm{NN}$ 为原子核互斥算符。需要注意，由于我们不使用 GIAO，因此 $\hat V_\mathrm{ee}$ 就是普通的电子互斥算符，不受外场 $\boldsymbol{\mathscr{B}}$ 干扰；但使用 GIAO 的情况下，可能需要额外考虑这部分贡献。

$$
\hat h (\boldsymbol{\mathscr{B}}) = \hat h {}^{(0)} + \hat h {}^{(1)} (\boldsymbol{\mathscr{B}}) + \hat h {}^{(2)} (\boldsymbol{\mathscr{B}})
$$

$\hat h {}^{(0)}$ 是没有外加场的算符 (这与自洽场计算过程所用到的算符相同)。其余的算符则为 (Atkins and Friedman, eq 13.26, eq 13.29)

$$
\begin{align}
\hat h {}^{(1)} (\boldsymbol{\mathscr{B}}) &= \frac{1}{2} \boldsymbol{\mathscr{B}} \cdot \boldsymbol{r} \times \boldsymbol{\hat{p}} \\
\hat h {}^{(2)} (\boldsymbol{\mathscr{B}}) &= \frac{1}{8} \big( \boldsymbol{\mathscr{B}}^2 \boldsymbol{r}^2 - (\boldsymbol{\mathscr{B}} \cdot \boldsymbol{r})^2 \big)
\end{align}
$$

其中，

$$
\boldsymbol{r} = \begin{pmatrix} x \\ y \\ z \end{pmatrix}, \quad
\boldsymbol{\hat p} = \begin{pmatrix}
  \displaystyle - i \frac{\partial}{\partial x} \\
  \displaystyle - i \frac{\partial}{\partial y} \\
  \displaystyle - i \frac{\partial}{\partial z}
\end{pmatrix} = -i \nabla \boldsymbol{r}
$$

其中，

$$
E_\mathrm{tot}^{(2)} = 2 \langle \Psi^{(0)} (\boldsymbol{\mathscr{B}}) | \sum_i \hat h {}^{(1)} (\boldsymbol{\mathscr{B}}, \boldsymbol{r}_i) | \Psi^{(1)} (\boldsymbol{\mathscr{B}}) \rangle + \langle \Psi^{(0)} | \sum_i \hat h {}^{(2)} (\boldsymbol{\mathscr{B}}, \boldsymbol{r}_i) | \Psi^{(0)} \rangle
$$

前一项被称为顺磁项 (Paramagnetic)，后一项称为抗磁项 (Diamagnetic)。$\Psi^{(0)}$ 是指未微扰的体系哈密顿算符 $\hat H {}^{(0)}$ 的本征态，$\Psi^{(1)} (\boldsymbol{\mathscr{B}})$ 则是一阶微扰的波函数；其解析的求取方法是在程序中表示为 U 矩阵，通过 CP-HF 方程求取；我们在这里不会对解析方法作说明，但了解这两项的区分是有帮助的。

## Core Hamiltonian 的程序实现

我们知道，PySCF 中，在自洽场实例中更改 Core Hamiltonian 的类方法函数 (method function) 就可以实现外场微扰下的能量计算。这在 pyxdh 偶极矩的计算 [文档](https://py-xdh.readthedocs.io/zh_CN/latest/numdiff/num_dip.html) 中有所说明。在这里我们也要做类似的工作。

### 顺磁项

在 PySCF 中，顺磁项 $\hat h {}^{(1)} (\boldsymbol{\mathscr{B}}) = \frac{1}{2} \boldsymbol{\mathscr{B}} \cdot \boldsymbol{r} \times \boldsymbol{\hat p}$ 有其对应的积分 `hcore_1` ($h_{t \mu \nu}^{(1)}$，需要注意它不包含作为标量的 $\mathscr{B}_t$)

$$
h_{t \mu \nu}^{(1)} \cdot \mathscr{B}_t = \langle \mu | \hat h {}^{(1)} (\mathscr{B}_t) | \nu \rangle
$$

```python
hcore_1 = - 0.5 * mol.intor("int1e_cg_irxp") * 1j
hcore_1.shape, hcore_1.dtype
```

    ((3, 8, 8), dtype('complex128'))

上述的程序看起来会有些奇怪，因为这里出现了复数。我们需要分段对其作解释。

**积分字符**

我们使用到了积分字符 `int1e_cg_irxp`。关于这段字符，其意义需要通过 [auto_intor.cl](https://github.com/sunqm/libcint/blob/abf6948fa17e5b4ecbd26de05bf4b1d7b2b2fe3c/scripts/auto_intor.cl#L12) 程序了解：

```lisp
  '("int1e_cg_irxp"             (#C(0 1) \| rc cross p))
```

其右侧是积分的具体形式，说明在 [README](https://github.com/sunqm/libcint/blob/master/README) 文件中，意义为

$$
\mathtt{int1e\_cg\_irxp} = i \langle \mu | \boldsymbol{r} \times \boldsymbol{\hat p} | \nu \rangle
$$

其维度是 $(t, \mu, \nu)$，但其第一个维度是通过向量叉乘给出，因此它与 $\boldsymbol{r}$ 或 $\boldsymbol{p}$ 的维度不是直接相关的。如果我们令动量算符 $\boldsymbol{\hat l} = \boldsymbol{r} \times \boldsymbol{\hat p}$，那么可以将上述积分写为

$$
\mathtt{int1e\_cg\_irxp}_{t \mu \nu} = i \langle \mu | \hat l_t | \nu \rangle
$$

**反对称性厄米性**

我们应当留意到 $\mathtt{int1e\_cg\_irxp}_{t \mu \nu}$ 是一个反对称矩阵 (即随 $\mu, \nu$ 交换成相反值)

```python
np.allclose(mol.intor("int1e_cg_irxp"), - mol.intor("int1e_cg_irxp").swapaxes(-1, -2))
```

    True

这是由于 $\nabla$ 算符本身是一个反对称算符。但是需要留意到，动量算符在此基础上乘以了虚数单位 $- i$，因此，该矩阵是厄米的，即其转置后的共轭是其本身。我们所定义的 $h_{t \mu \nu}^{(1)}$ 就具有这样的性质：

```python
np.allclose(hcore_1, hcore_1.swapaxes(-1, -2).conj())
```

    True

因此，我们会说 `hcore_1` $h_{t \mu \nu}^{(1)}$ 是厄米的。

### 抗磁项

抗磁项 $\hat h {}^{(2)} (\boldsymbol{\mathscr{B}}) = \frac{1}{8} \big( \boldsymbol{\mathscr{B}}^2 \boldsymbol{r}^2 - (\boldsymbol{\mathscr{B}} \cdot \boldsymbol{r})^2 \big)$ 需要一些技巧生成。PySCF 中可以生成张量 `int1e_rr`：

$$
\mathtt{int1e\_rr}_{ts \mu \nu} = \langle \mu | ts | \nu \rangle
$$

我们定义 `hcore_2` $h_{ts \mu \nu}^{(2)}$ 为

$$
h_{ts \mu \nu}^{(2)} = \frac{1}{8} \big( \delta_{ts} \langle \mu | x^2 + y^2 + z^2 | \nu \rangle - \langle \mu | ts | \nu \rangle \big)
$$

```python
with mol.with_common_orig(coord_orig):
    int1e_rr = mol.intor("int1e_rr").reshape(3, 3, mol.nao, mol.nao)
hcore_2 = 1/8 * (np.einsum("ts, uv -> tsuv", np.eye(3), int1e_rr.diagonal(0, 0, 1).sum(-1)) - int1e_rr)
```

并且，上述张量具有下述性质：

$$
h_{ts \mu \nu}^{(2)} \cdot \mathscr{B}_t \mathscr{B}_s = \langle \mu | \hat h {}^{(2)} (\mathscr{B}_t, \mathscr{B}_s) | \nu \rangle
$$

### Core Hamiltonian 程序实现

我们最后可以编写外加磁场微扰下的 Core Hamiltonian，以及在此微扰下的分子体系能量。为了加快计算速度，我们会使用为微扰的自洽场密度作为初猜 `dm_guess`。Core Hamiltonian 表达式为

$$
h_{\mu \nu} (\boldsymbol{\mathscr{B}}) = h_{\mu \nu} (\mathscr{B}_x, \mathscr{B}_y, \mathscr{B}_z)
= h_{\mu \nu}^{(0)} + \sum_{t} h_{t \mu \nu}^{(1)} \mathscr{B}_t + \sum_{ts} h_{ts \mu \nu}^{(2)} \mathscr{B}_t \mathscr{B}_s
$$

```python
dm_guess = mf.make_rdm1()

def hcore_mag_field(dev_xyz):
    mf = scf.RHF(mol)
    def hcore(mol_):
        hcore_total  = np.asarray(scf.rhf.get_hcore(mol_), dtype=np.complex128)
        hcore_total += np.einsum("tuv, t -> uv", hcore_1, dev_xyz)
        hcore_total += np.einsum("tsuv, t, s -> uv", hcore_2, dev_xyz, dev_xyz)
        return hcore_total
    mf.get_hcore = hcore
    return mf.kernel(dm=dm_guess)
```

上述函数的参数 `t`, `s` 表示坐标方向分量，`dev_t`, `dev_s` 表示外加微扰大小，单位为 a.u.。

譬如，若在 $x$ 方向的磁场上施加 $\mathscr{B}_x = 1 \, \mathsf{a.u.}$，而 $y$ 方向上施加 $\mathscr{B}_y = 2 \, \mathsf{a.u.}$ (即 $\boldsymbol{\mathscr{B}} = (\mathscr{B}_x, \mathscr{B}_y, \mathscr{B}_z) = (1, 2, 0) \, \mathsf{a.u.}$)，那么下述程序会给出该自洽场能量 $E_\mathrm{tot} (\mathscr{B}_x, \mathscr{B}_y, \mathscr{B}_z)$：

```python
hcore_mag_field(0, 1, 1, 2)
```

    -57.997213868466154

## 数值导数求取磁化率

我们已经有了求取 $E_\mathrm{tot} (\mathscr{B}_x, \mathscr{B}_y, \mathscr{B}_z)$ 的程序了，接下来就可以进行数值导数计算。数值导数的计算公式可以简单地使用三点差分法；对于被求导量 $x :\neq y$，有 (当 $h$ 足够小时)

$$
\frac{\partial^2 f}{\partial x \partial y} \simeq \frac{1}{4 h^2} \big[ f(x + h, y + h) - f(x - h, y + h) - f(x + h, y - h) + f(x - h, y - h) \big]
$$

而对被求导量相同的情形，有

$$
\frac{\partial^2 f}{\partial x^2} \simeq \frac{1}{h^2} \big[ f(x + h) - 2 f(x) + f(x - h) \big]
$$

下面的程序就依照上述两个公式进行二阶数值导数求取。求导的原点取在 $(\mathscr{B}_x, \mathscr{B}_y, \mathscr{B}_z) = (0, 0, 0)$ 即不受外磁场影响的情形的自洽场能量 `eng_origin`，差分大小为 `interval` $h = 10^{-3} \, \mathsf{a.u.}$。需要注意，根据约定俗成，

$$
\xi_{ts} = - \frac{\partial^2 E_\mathrm{tot} (\boldsymbol{\mathscr{B}})}{\partial \mathscr{B}_t \partial \mathscr{B}_s}
$$

因此求取得到的磁化率 `num_polar` $\xi_{ts}$ 需要乘以 -1。

```python
eng_origin = hcore_mag_field((0, 0, 0))
interval = 1e-3
num_polar = np.zeros((3, 3))
for t in range(3):
    for s in range(3):
        if t != s:
            dev_xyzs = np.zeros((4, 3))
            dev_xyzs[0, t] = dev_xyzs[0, s] = dev_xyzs[1, t] = dev_xyzs[2, s] =  interval
            dev_xyzs[3, t] = dev_xyzs[3, s] = dev_xyzs[2, t] = dev_xyzs[1, s] = -interval
            num_polar[t, s] = (
                + hcore_mag_field(dev_xyzs[0])
                - hcore_mag_field(dev_xyzs[1])
                - hcore_mag_field(dev_xyzs[2])
                + hcore_mag_field(dev_xyzs[3])
            ) / (4 * interval**2)
        else:
            dev_xyzs = np.zeros((2, 3))
            dev_xyzs[0, t], dev_xyzs[1, t] = interval, -interval
            num_polar[t, t] = (
                + hcore_mag_field(dev_xyzs[0])
                + hcore_mag_field(dev_xyzs[2])
                - eng_origin * 2
            ) / (interval ** 2)
num_polar *= -1
```

```python
num_polar
```

    array([[-4.94475,  0.21773, -0.08268],
           [ 0.21773, -4.27801,  0.49885],
           [-0.08268,  0.49885, -4.15348]])

我们再与 PySCF 的解析结果作对照：

```python
mf_mag.kernel()
```

    array([[-4.94475,  0.21773, -0.08268],
           [ 0.21773, -4.27801,  0.49885],
           [-0.08268,  0.49885, -4.15348]])

[^Atkins-Friedman.Oxford.2010]: Atkins, P. W.; Friedman, R. S. *Molecular Quantum Mechanics*; Oxford University Press, 2010.


```python
import numpy as np
import sympy as sy
from sympy import I, exp
from sympy.utilities.codegen import codegen
```

```python
z = sy.symbols("z", real=False)
r1,r2,r3, s0,s1, s2,s3 = sy.symbols("r1,r2,r3, s0,s1,s2,s3", real=True)
a, h = sy.symbols("a, h", real=True)
```

```python
hh = 0.2
pcont = -4
pd1 = np.exp(hh*pcont)
#Ac = sy.poly(z**2*(z - pd1)*(z-pd2), z )
Ac = sy.poly((z - pd1)**4, z )
Ao = sy.poly((z-a)**3, z )
Acl = Ac*Ao

# Assume anti-aliasing filter corresponding to a delay of two sample periods
A = sy.poly( (z**2*(z-1)*(z-exp(h))).subs(h,hh), z )
B = sy.poly( ((-1+exp(h)-h)*z + 1-(1-h)*exp(h)).subs(h,hh), z)
print pd1
print A
print B
print Ao
print Ac
```

    0.449328964117
    Poly(1.0*z**4 - 2.22140275816017*z**3 + 1.22140275816017*z**2, z, domain='RR')
    Poly(0.0214027581601699*z + 0.022877793471864, z, domain='RR')
    Poly(z**3 - 3*a*z**2 + 3*a**2*z - a**3, z, domain='ZZ[a]')
    Poly(1.0*z**4 - 1.79731585646889*z**3 + 1.21137910796793*z**2 - 0.36287181315765*z + 0.0407622039783662, z, domain='RR')

```python
R = sy.poly(z**3 + r1*z**2+r2*z+r3, z)
S = sy.poly(s0*z**3 + s1*z**2 + s2*z + s3, z)
print A*R+B*S
print Acl

dioph=(A*R+B*S-Acl).all_coeffs()
```

    Poly(1.0*z**7 + (1.0*r1 - 2.22140275816017)*z**6 + (-2.22140275816017*r1 + 1.0*r2 + 1.22140275816017)*z**5 + (1.22140275816017*r1 - 2.22140275816017*r2 + 1.0*r3 + 0.0214027581601699*s0)*z**4 + (1.22140275816017*r2 - 2.22140275816017*r3 + 0.022877793471864*s0 + 0.0214027581601699*s1)*z**3 + (1.22140275816017*r3 + 0.022877793471864*s1 + 0.0214027581601699*s2)*z**2 + (0.022877793471864*s2 + 0.0214027581601699*s3)*z + 0.022877793471864*s3, z, domain='RR[r1,r2,r3,s0,s1,s2,s3]')
    Poly(1.0*z**7 + (-3.0*a - 1.79731585646889)*z**6 + (3.0*a**2 + 5.39194756940666*a + 1.21137910796793)*z**5 + (-1.0*a**3 - 5.39194756940666*a**2 - 3.6341373239038*a - 0.36287181315765)*z**4 + (1.79731585646889*a**3 + 3.6341373239038*a**2 + 1.08861543947295*a + 0.0407622039783662)*z**3 + (-1.21137910796793*a**3 - 1.08861543947295*a**2 - 0.122286611935099*a)*z**2 + (0.36287181315765*a**3 + 0.122286611935099*a**2)*z - 0.0407622039783662*a**3, z, domain='RR[a]')

```python
sol=sy.solve(dioph, (r1,r2, r3, s0,s1,s2, s3))
print sol
```

    {s3: -1.78173668839598*a**3, s0: -0.869546949487873*a**3 + 56.6553501855205*a**2 - 70.0836348720752*a + 30.6100629635936, r3: -0.981389296931198*a**3 + 0.0596799465734188*a**2 - 1.29614940062047*a + 0.534452974786953, s2: 3.82121721355068e-29*a**2*(4.58706499600301e+29*a + 1.39882392611259e+29), s1: -16.9535027365645*a**3 - 55.7707162945703*a**2 + 63.853790915937*a - 28.5334483115476, r1: -3.0*a + 0.424086901691283, r2: 3.0*a**2 - 1.27226070507385*a + 0.932044162924379}

```python
len(dioph)
```

    7

```python
# Reorganize solution expression for matlab code generation
sol_expr = ('PS4_ship_heading_RST_example_aliasing', [sol[r1], sol[r2], sol[r3], sol[s0], sol[s1], sol[s2], sol[s3], Ac.subs(z, 1)/B.subs(z,1), a])
```

```python
# Export to matlab code
[(m_name, m_code)] = codegen(sol_expr, 'octave')
```

```python
print m_code
with open("/home/kjartan/Dropbox/undervisning/tec/MR2007/matlab/PS4_ship_heading_RST_example_aliasing.m", "w") as text_file:
    text_file.write(m_code)
```

    function [out1, out2, out3, out4, out5, out6, out7, out8, out9] = PS4_ship_heading_RST_example_aliasing(a)
      %PS4_SHIP_HEADING_RST_EXAMPLE_ALIASING  Autogenerated by sympy
      %   Code generated with sympy 0.7.6
      %
      %   See http://www.sympy.org/ for more information.
      %
      %   This file is part of 'project'

      out1 = -3.0*a + 0.424086901691283;
      out2 = 3.0*a.^2 - 1.27226070507385*a + 0.932044162924379;
      out3 = -0.981389296931198*a.^3 + 0.0596799465734188*a.^2 - 1.29614940062047*a + 0.534452974786953;
      out4 = -0.869546949487873*a.^3 + 56.6553501855205*a.^2 - 70.0836348720752*a + 30.6100629635936;
      out5 = -16.9535027365645*a.^3 - 55.7707162945703*a.^2 + 63.853790915937*a - 28.5334483115476;
      out6 = 3.82121721355068e-29*a.^2.*(4.58706499600301e+29*a + 1.39882392611259e+29);
      out7 = -1.78173668839598*a.^3;
      out8 = 2.07661465204603;
      out9 = a;

    end

```python
Ac.subs(z, 1)
```

    0.0919536423197624

```python
(1-0.45)**4
```

    0.09150625000000003

```python
B.subs(z,1)
```

    0.0442805516320339

```python

```


# Quantum Circuit Plotting with SymPy
Rick Muller, rpmuller@gmail.com

Explore what can and cannot be done using existing sympy circuit plotting. The initial aim will be to support everything that is on the [qasm2circ website](http://www.media.mit.edu/quanta/qasm2circ/). When you see a heading listing an example number, it will refer to the example circuit on that page.

Note: The examples on this page will require *at least* the [development (i.e. github) branch of sympy](http://github.com/sympy/sympy), and may require work in my current branch (which changes from week to week). If you'd like to play with newer features, and the examples on this page aren't working for you, email me (rpmuller@gmail.com) and I can send you instructions for how to get these to work.

```python
from sympy import *
from sympy.physics.quantum.circuitplot import CircuitPlot,labeller,Mz,CreateOneQubitGate
from sympy.physics.quantum.gate import *
from sympy.physics.quantum.qasm import Qasm
```

## Rough priorities of tasks
* Stack multiple gates vertically
* Block multiqubit gate
* Latex matrices as gates
* Do circuits resize correctly?

## Qasm tests status
1. Works
2. Works
3. Works
4. Works
5. Can't plot matrices as operators
6. Works
7. Works
8. Works
9. Works
10. Multi-qubit blocks + classical lines
11. Multi-qubit blocks
12. Multi-qubit blocks
13. Multi-qubit blocks (kinda works if you convert defbox->def by hand)
14. Multi-qubit blocks
15. D-shaped measurement
16. D-shaped measurement + slash gate + vertical line gates
17. D-shaped measurement

## X gate test

```python
CircuitPlot(CNOT(1,0),2)
```

```python
CircuitPlot(X(0),1)
```

Sympy now prints a normal X gate when it's a one-qubit gate, but the circ+ when it's a CNOT!

## Z gate test

```python
CircuitPlot(CPHASE(1,0),2)
```

```python
CircuitPlot(Z(0),1)
```

## Making arbitrary gates:

It's pretty easy to overload single-qubit operators, and you can even specify fairly complicated LaTeX:

```python
VGate = CreateOneQubitGate('V')
SqrtX = CreateOneQubitGate('sqrt-X','\sqrt{X}')
```

```python
CircuitPlot(SqrtX(0)*VGate(0),1)
```

You can even make these controlled:

```python
CircuitPlot(CGate(1,SqrtX(0)),2)
```

Unfortunately, there isn't a general way to make a block multi-qubit gate. You need to overload the plot_gate function, which I haven't. And, I guess you also have to make sure that all of the qubits are contiguous.

**Todo** Create multi-qubit gate.

```python
class SqrtSWAP(TwoQubitGate):
    gate_name = 'sqrt-SWAP'
    gate_name_latex = u'\sqrt{SWAP}'
```

```python
# This doesn't work:
# CircuitPlot(SqrtSWAP(0,1),2)
```

## Example 1: EPR Creation

Goal:

```python
CircuitPlot(CNOT(1,0)*H(1),2,labels=labeller(2))
```

A couple of notes:

* The top wire in a figure is the nth qubit in the circuit, not the 0-th or 1st.
* The gates in a circuit are ordered left to right. In other words, the first gate to be applied is the left-most gate in the circuit.

Both of these make perfect sense from a physics point of view, but they may not be what you expect.

## qasm version:

```python
q = Qasm('qubit q0','qubit q1','h q0','cnot q0,q1')
q.plot()
```

Works, but note that I haven't implemented the subscripts in qasm parser yet. It's not hard to rewrite the labelling so that it sticks an underscore between the alphabetical characters and the numbers, but I don't want to limit the way the code works based on assumptions of use cases that I haven't fully thought through. In the meantime, if you stick underscores in the label names, these will work:

```python
q = Qasm('qubit q_0','qubit q_1','h q_0','cnot q_0,q_1')
q.plot()
```

You can also call the commands directly:

```python
q = Qasm()
q.qubit('q_0')
q.qubit('q_1')
q.h('q_0')
q.cnot('q_0','q_1')
q.plot()
```

## Example 2: Teleportation Gate

Goal:

```python
circuit = CGate(2,Z(0))*CGate(1,X(0))*Mz(2)*Mz(1)*H(2)*CNOT(2,1)*CNOT(1,0)*H(1)
cp = CircuitPlot(circuit,3,labels=labeller(3))
```

```python
# qasm version
q = Qasm('qubit q_0','qubit q_1','qubit q_2','h  q_1',
         'cnot q_1,q_2','cnot q_0,q_1','h q_0',
         'measure q_1','measure q_0',
         'c-x q_1,q_2','c-z q_0,q_2')
q.plot()
```

## Example 3: Swap Gate

Goal:

```python
CircuitPlot(CNOT(1,0)*CNOT(0,1)*CNOT(1,0),2,labels=labeller(2))
```

#### qasm version:

```python
q = Qasm('qubit q_0','qubit q_1','cnot q_0,q_1','cnot q_1,q_0','cnot q_0,q_1')
q.plot()
```

## Example 4: QFT on 3 Qubits
Goal:

```python
CircuitPlot(SWAP(0,2)*H(0)* CGate((0,),S(1)) *H(1)*CGate((0,),T(2))*CGate((1,),S(2))*H(2),3,labels=labeller(3,'j'))
```

```python
# qasm version:
q = Qasm("def	c-S,1,'S'",
        "def	c-T,1,'T'",
        "qubit	j0",
        "qubit	j1",
        "qubit	j2",
        "h	j0",
        "c-S	j1,j0",
        "c-T	j2,j0",
        "nop	j1",
        "h	j1",
        "c-S	j2,j1",
        "h	j2",
        "swap	j0,j2")
q.plot()
```

## Example 5
Goal:

```python
# Note: you currently have to escape (double backslash) the \b and \t,
# I guess b/c they're whitespace shortcuts
class Rot1(OneQubitGate):
    gate_name = 'Rot'
    gate_name_latex = r'\begin{array}{ll}\cos\theta&-\sin\theta\end{array}'
```

```python
# This doesn't work:
#CircuitPlot(Rot1(0),1)
```
Can't stick a matrix in a qubit operator:

    ValueError:
    \begin{array}{ll}\cos\theta&-\sin\theta\end{array}
    ^
    Unknown symbol: \begin (at char 0), (line:1, col:1)

Doesn't work any better when I have $ at the beginning/end of the string.
## Example 6
Goal:

```python
U = CreateOneQubitGate('U')
CircuitPlot(Mz(0)*H(1)*CGate((0,1,3),U(2))*X(3)*CGateS((2,3),X(1)),4,labels=labeller(4,'j'))
```

```python
# Qasm version:
q = Qasm("def	c-U,3,'U'",
    "qubit	j0",
    "qubit	j1",
	"qubit	j2",
    "qubit	j3",
    "toffoli	j0,j1,j2",
    "X	j0",
    "c-U	j2,j3,j0,j1",
    "H	j2",
    "measure	j3")
q.plot()
```

## Example 7
Goal:

```python
V = CreateOneQubitGate('V')

CircuitPlot(CGate(1,V(0))*Mz(1)*H(1)*CGate(1,U(0))*H(1),2,labels=labeller(2))
```

```python
# Qasm
q = Qasm("def	c-U,1,'U'","def	c-V,1,'V'",
         "qubit	q0","qubit	q1",
         "H	q0","c-U	q0,q1","H	q0",
         "measure	q0","c-V	q0,q1")
q.plot()
```

## Example 8
Goal:

```python
CircuitPlot(H(2)*H(2)*CGate(0,Z(2))*H(2)*CNOT(1,0)*CNOT(2,1)*CNOT(1,0)*CNOT(2,1)*H(1),3,labels=labeller(3))
```

```python
#Qasm:
q = Qasm("def	c-Z,1,'Z'",
         "qubit	q0,\psi","qubit	q1,0","qubit	q2,0",
         "H	q1","cnot	q0,q1","cnot	q1,q2","cnot	q0,q1","cnot	q1,q2",
         "H	q0","c-Z	q2,q0","H	q0","H	q0")
q.plot()
```

## Example 9
Goal:

```python
SqrtX = CreateOneQubitGate('sqrt-X','\sqrt{X}')
SqrtXdag = CreateOneQubitGate('sqrt-X-dag','\sqrt{X}^\dagger')

CircuitPlot(CGate(2,SqrtX(0))*CNOT(2,1)*CGate(1,SqrtXdag(0))*CNOT(2,1)*CGate(1,SqrtX(0)),3,labels=labeller(3))
```

```python
q = Qasm("def	c-X,1,'\sqrt{X}'","def	c-Xd,1,'{\sqrt{X}}^\dagger'",
         "qubit	q0","qubit	q1","qubit	q2",
         "c-X	q1,q2","cnot	q0,q1","c-Xd	q1,q2",
         "cnot	q0,q1","c-X	q0,q2")
q.plot()
```

## Example 10
Goal:

## Example 11
Goal:

## Example 12
Goal:

## Example 13
Goal:

```python
# Only works if you convert the defboxes to defs and remove qubit s2:
qasm_lines = """\
	def	CU,1,'U'
	def	CU2,1,'U^2'
	def	CU4,1,'U^4'
	def	c-S,1,'S'
	def	c-T,1,'T'

	qubit	j0,0	# QFT qubits
	qubit	j1,0
	qubit	j2,0
	qubit	s0	# U qubits

	h	j0	# equal superposition
	h	j1
	h	j2

	CU4	j0,s0	# controlled-U
	CU2	j1,s0
	CU	j2,s0

	h	j0	# QFT
	c-S	j0,j1
	h	j1
	nop	j0
	c-T	j0,j2
	c-S	j1,j2
	h	j2
	nop	j0
	nop	j0
	nop	j1

	measure	j0	# final measurement
	measure	j1
	measure	j2"""

q = Qasm(*qasm_lines.splitlines())
q.plot()
```

## Example 14
Goal:

## Example 15
Goal:

```python
CircuitPlot(Mz(1)*CPHASE(0,1),2,labels=['+','\psi'])
```

## Example 16
Goal:

```python
CircuitPlot(Mz(0)*CNOT(1,0)*CPHASE(1,2),4,labels=['0','\phi','\psi','\psi'])
```

## Example 17
Goal:

```python
CircuitPlot(Mz(2)*Mz(1)*CPHASE(1,2)*CPHASE(1,0)*CPHASE(3,2),4,labels=['\phi','+','+','\psi'])
```

# Surface code
These are the X and Z checks from a surface code:

```python
CircuitPlot(Mz(4)*H(4)*CNOT(4,0)*CNOT(4,1)*CNOT(4,2)*CNOT(4,3)*H(4),5,labels=['d_4','d_3','d_2','d_1','a_0'])
```

```python
CircuitPlot(Mz(4)*CNOT(0,4)*CNOT(1,4)*CNOT(2,4)*CNOT(3,4),5,labels=['d_4','d_3','d_2','d_1','a_0'])
```

# Steane code

Make $|\bar{0}\rangle$, Fig 12 from [Aliferis, Gottesman, and Preskill](http://arxiv.org/pdf/quant-ph/0504218.pdf).

```python
CircuitPlot(CNOT(6,0)*CNOT(5,4)*CNOT(3,2)*CNOT(5,1)*CNOT(3,0)*CNOT(6,2)*CNOT(6,4)*CNOT(3,0),
    7,labels=["0","0","0","+","0","+","+"])
```

Let's use the power of python to make this a little easier:

```python
import operator
def prod(c): return reduce(operator.mul, c, 1)

steane_pairs = [(6,0),(5,4),(3,2),(5,1),(3,0),(6,2),(6,4),(3,0)]
circuit2 = prod([CNOT(i,j) for i,j in steane_pairs])
labels=["0","0","0","+","0","+","+"]
CircuitPlot(circuit2,7,labels=labels)
```

We can now extend this to a full Steane check:

```python
circuit1 = prod([CNOT(i+7,j+7) for i,j in steane_pairs])
decoder = prod([CNOT(i+7,j+7) for i,j in reversed(steane_pairs)])
transverse = prod([CPHASE(i+7,i) for i in range(7)])
CircuitPlot(decoder*transverse*circuit2*circuit1,14,labels=labels+labels)
```

That's only 7 real lines of code to plot the encoding.

```python

```


```julia
using PyCall
import SymPy: symbols, sympy, Sym
using GAlgebra
using Test
```

```julia
py"""
def vector(ga, components):
    basis = ga.mv()
    return sum([components[i] * e for i, e in enumerate(basis)])
"""
const vector = py"vector"

py"""
def signature(ga):
    basis = ga.mv()
    signs = [e * e for e in basis]
    p, q, r = 0, 0, 0
    for sign in signs:
        p += 1 if sign == 1 else 0
        q += 1 if sign == -1 else 0
        r += 1 if sign == 0 else 0

    return (p, q, r)
"""
const signature = py"signature"
```

    PyObject <function signature at 0x000000002FE6DAE8>

```julia
# Basic
Hyper   = G(1)       # Hyperbolic numbers.
ℂ       = G(0,1)     # Complex numbers.
Dual    = G(0,0,1)   # Dual numbers.
ℍ       = G(0,2)     # Quaternions.

# Clifford
Cl2       = G(2)     # Clifford algebra for 2D vector space.
Cl3       = G(3)     # Clifford algebra for 3D vector space.
Spacetime = G(1,3)   # Clifford algebra for timespace vectors.

# Geometric
PGA2D = G(2,0,1)     # Projective Euclidean 2D plane. (dual)
PGA3D = G(3,0,1)     # Projective Euclidean 3D space. (dual)
CGA2D = G(3,1)       # conformal 2D space.
CGA3D = G(4,1)       # Conformal 3D space.
```

    PyObject <galgebra.ga.Ga object at 0x00000000313D8898>

```julia
function test_geometric_product(V)
    dimV = range(0, stop=V.n)
    I = V.I()

    α = V.mv("α", "scalar")
    β = V.mv("β", "scalar")
    γ = V.mv("γ", "scalar")
    λ = V.mv("λ", "scalar")

    u = V.mv("u", "vector")
    v = V.mv("v", "vector")
    w = V.mv("w", "vector")

    A = V.mv("A", "mv")
    B = V.mv("B", "mv")
    C = V.mv("C", "mv")
    D = V.mv("D", "mv")

    R = V.mv("R", "spinor")

    return A*B #, V.mul_table_dict)
end
```

    test_geometric_product (generic function with 1 method)

```julia
@time test_geometric_product(Hyper)
```

      0.143947 seconds (270.18 k allocations: 13.434 MiB)

\begin{align*}\left ( A B + A^{0} B^{0}\right )  + \left ( A B^{0} + A^{0} B\right ) \boldsymbol{e}_{0}\end{align*}

```julia
@time test_geometric_product(Dual)
```

      0.002141 seconds (157 allocations: 5.859 KiB)

\begin{align*}A B  + \left ( A B^{0} + A^{0} B\right ) \boldsymbol{e}_{0}\end{align*}

```julia
@time test_geometric_product(ℂ)
```

      0.002485 seconds (157 allocations: 5.859 KiB)

\begin{align*}\left ( A B - A^{0} B^{0}\right )  + \left ( A B^{0} + A^{0} B\right ) \boldsymbol{e}_{0}\end{align*}

```julia
@time test_geometric_product(ℍ)
```

      0.047692 seconds (157 allocations: 5.859 KiB)

\begin{align*}\left ( A B - A^{1} B^{1} - A^{12} B^{12} - A^{2} B^{2}\right )  + \left ( A B^{1} + A^{1} B - A^{12} B^{2} + A^{2} B^{12}\right ) \boldsymbol{e}_{1} + \left ( A B^{2} - A^{1} B^{12} + A^{12} B^{1} + A^{2} B\right ) \boldsymbol{e}_{2} + \left ( A B^{12} + A^{1} B^{2} + A^{12} B - A^{2} B^{1}\right ) \boldsymbol{e}_{1}\wedge \boldsymbol{e}_{2}\end{align*}

```julia
@time test_geometric_product(Cl2)
```

      0.012486 seconds (157 allocations: 5.859 KiB)

\begin{align*}\left ( A B + A^{1} B^{1} - A^{12} B^{12} + A^{2} B^{2}\right )  + \left ( A B^{1} + A^{1} B + A^{12} B^{2} - A^{2} B^{12}\right ) \boldsymbol{e}_{1} + \left ( A B^{2} + A^{1} B^{12} - A^{12} B^{1} + A^{2} B\right ) \boldsymbol{e}_{2} + \left ( A B^{12} + A^{1} B^{2} + A^{12} B - A^{2} B^{1}\right ) \boldsymbol{e}_{1}\wedge \boldsymbol{e}_{2}\end{align*}

```julia
@time test_geometric_product(Cl3)
```

      0.138977 seconds (157 allocations: 5.859 KiB)

\begin{align*}\left ( A B + A^{1} B^{1} - A^{12} B^{12} - A^{123} B^{123} - A^{13} B^{13} + A^{2} B^{2} - A^{23} B^{23} + A^{3} B^{3}\right )  + \left ( A B^{1} + A^{1} B + A^{12} B^{2} - A^{123} B^{23} + A^{13} B^{3} - A^{2} B^{12} - A^{23} B^{123} - A^{3} B^{13}\right ) \boldsymbol{e}_{1} + \left ( A B^{2} + A^{1} B^{12} - A^{12} B^{1} + A^{123} B^{13} + A^{13} B^{123} + A^{2} B + A^{23} B^{3} - A^{3} B^{23}\right ) \boldsymbol{e}_{2} + \left ( A B^{3} + A^{1} B^{13} - A^{12} B^{123} - A^{123} B^{12} - A^{13} B^{1} + A^{2} B^{23} - A^{23} B^{2} + A^{3} B\right ) \boldsymbol{e}_{3} + \left ( A B^{12} + A^{1} B^{2} + A^{12} B + A^{123} B^{3} - A^{13} B^{23} - A^{2} B^{1} + A^{23} B^{13} + A^{3} B^{123}\right ) \boldsymbol{e}_{1}\wedge \boldsymbol{e}_{2} + \left ( A B^{13} + A^{1} B^{3} + A^{12} B^{23} - A^{123} B^{2} + A^{13} B - A^{2} B^{123} - A^{23} B^{12} - A^{3} B^{1}\right ) \boldsymbol{e}_{1}\wedge \boldsymbol{e}_{3} + \left ( A B^{23} + A^{1} B^{123} - A^{12} B^{13} + A^{123} B^{1} + A^{13} B^{12} + A^{2} B^{3} + A^{23} B - A^{3} B^{2}\right ) \boldsymbol{e}_{2}\wedge \boldsymbol{e}_{3} + \left ( A B^{123} + A^{1} B^{23} + A^{12} B^{3} + A^{123} B - A^{13} B^{2} - A^{2} B^{13} + A^{23} B^{1} + A^{3} B^{12}\right ) \boldsymbol{e}_{1}\wedge \boldsymbol{e}_{2}\wedge \boldsymbol{e}_{3}\end{align*}

```julia
@time test_geometric_product(Spacetime)
```

      1.388004 seconds (157 allocations: 5.859 KiB)

\begin{align*}\left ( A B + A^{1} B^{1} + A^{12} B^{12} - A^{123} B^{123} - A^{1234} B^{1234} - A^{124} B^{124} + A^{13} B^{13} - A^{134} B^{134} + A^{14} B^{14} - A^{2} B^{2} - A^{23} B^{23} + A^{234} B^{234} - A^{24} B^{24} - A^{3} B^{3} - A^{34} B^{34} - A^{4} B^{4}\right )  + \left ( A B^{1} + A^{1} B - A^{12} B^{2} - A^{123} B^{23} + A^{1234} B^{234} - A^{124} B^{24} - A^{13} B^{3} - A^{134} B^{34} - A^{14} B^{4} + A^{2} B^{12} - A^{23} B^{123} - A^{234} B^{1234} - A^{24} B^{124} + A^{3} B^{13} - A^{34} B^{134} + A^{4} B^{14}\right ) \boldsymbol{e}_{1} + \left ( A B^{2} + A^{1} B^{12} - A^{12} B^{1} - A^{123} B^{13} + A^{1234} B^{134} - A^{124} B^{14} - A^{13} B^{123} - A^{134} B^{1234} - A^{14} B^{124} + A^{2} B - A^{23} B^{3} - A^{234} B^{34} - A^{24} B^{4} + A^{3} B^{23} - A^{34} B^{234} + A^{4} B^{24}\right ) \boldsymbol{e}_{2} + \left ( A B^{3} + A^{1} B^{13} + A^{12} B^{123} + A^{123} B^{12} - A^{1234} B^{124} + A^{124} B^{1234} - A^{13} B^{1} - A^{134} B^{14} - A^{14} B^{134} - A^{2} B^{23} + A^{23} B^{2} + A^{234} B^{24} + A^{24} B^{234} + A^{3} B - A^{34} B^{4} + A^{4} B^{34}\right ) \boldsymbol{e}_{3} + \left ( A B^{4} + A^{1} B^{14} + A^{12} B^{124} - A^{123} B^{1234} + A^{1234} B^{123} + A^{124} B^{12} + A^{13} B^{134} + A^{134} B^{13} - A^{14} B^{1} - A^{2} B^{24} - A^{23} B^{234} - A^{234} B^{23} + A^{24} B^{2} - A^{3} B^{34} + A^{34} B^{3} + A^{4} B\right ) \boldsymbol{e}_{4} + \left ( A B^{12} + A^{1} B^{2} + A^{12} B - A^{123} B^{3} - A^{1234} B^{34} - A^{124} B^{4} + A^{13} B^{23} - A^{134} B^{234} + A^{14} B^{24} - A^{2} B^{1} - A^{23} B^{13} + A^{234} B^{134} - A^{24} B^{14} - A^{3} B^{123} - A^{34} B^{1234} - A^{4} B^{124}\right ) \boldsymbol{e}_{1}\wedge \boldsymbol{e}_{2} + \left ( A B^{13} + A^{1} B^{3} - A^{12} B^{23} + A^{123} B^{2} + A^{1234} B^{24} + A^{124} B^{234} + A^{13} B - A^{134} B^{4} + A^{14} B^{34} + A^{2} B^{123} + A^{23} B^{12} - A^{234} B^{124} + A^{24} B^{1234} - A^{3} B^{1} - A^{34} B^{14} - A^{4} B^{134}\right ) \boldsymbol{e}_{1}\wedge \boldsymbol{e}_{3} + \left ( A B^{14} + A^{1} B^{4} - A^{12} B^{24} - A^{123} B^{234} - A^{1234} B^{23} + A^{124} B^{2} - A^{13} B^{34} + A^{134} B^{3} + A^{14} B + A^{2} B^{124} - A^{23} B^{1234} + A^{234} B^{123} + A^{24} B^{12} + A^{3} B^{134} + A^{34} B^{13} - A^{4} B^{1}\right ) \boldsymbol{e}_{1}\wedge \boldsymbol{e}_{4} + \left ( A B^{23} + A^{1} B^{123} - A^{12} B^{13} + A^{123} B^{1} + A^{1234} B^{14} + A^{124} B^{134} + A^{13} B^{12} - A^{134} B^{124} + A^{14} B^{1234} + A^{2} B^{3} + A^{23} B - A^{234} B^{4} + A^{24} B^{34} - A^{3} B^{2} - A^{34} B^{24} - A^{4} B^{234}\right ) \boldsymbol{e}_{2}\wedge \boldsymbol{e}_{3} + \left ( A B^{24} + A^{1} B^{124} - A^{12} B^{14} - A^{123} B^{134} - A^{1234} B^{13} + A^{124} B^{1} - A^{13} B^{1234} + A^{134} B^{123} + A^{14} B^{12} + A^{2} B^{4} - A^{23} B^{34} + A^{234} B^{3} + A^{24} B + A^{3} B^{234} + A^{34} B^{23} - A^{4} B^{2}\right ) \boldsymbol{e}_{2}\wedge \boldsymbol{e}_{4} + \left ( A B^{34} + A^{1} B^{134} + A^{12} B^{1234} + A^{123} B^{124} + A^{1234} B^{12} - A^{124} B^{123} - A^{13} B^{14} + A^{134} B^{1} + A^{14} B^{13} - A^{2} B^{234} + A^{23} B^{24} - A^{234} B^{2} - A^{24} B^{23} + A^{3} B^{4} + A^{34} B - A^{4} B^{3}\right ) \boldsymbol{e}_{3}\wedge \boldsymbol{e}_{4} + \left ( A B^{123} + A^{1} B^{23} + A^{12} B^{3} + A^{123} B - A^{1234} B^{4} + A^{124} B^{34} - A^{13} B^{2} - A^{134} B^{24} - A^{14} B^{234} - A^{2} B^{13} + A^{23} B^{1} + A^{234} B^{14} + A^{24} B^{134} + A^{3} B^{12} - A^{34} B^{124} + A^{4} B^{1234}\right ) \boldsymbol{e}_{1}\wedge \boldsymbol{e}_{2}\wedge \boldsymbol{e}_{3} + \left ( A B^{124} + A^{1} B^{24} + A^{12} B^{4} - A^{123} B^{34} + A^{1234} B^{3} + A^{124} B + A^{13} B^{234} + A^{134} B^{23} - A^{14} B^{2} - A^{2} B^{14} - A^{23} B^{134} - A^{234} B^{13} + A^{24} B^{1} - A^{3} B^{1234} + A^{34} B^{123} + A^{4} B^{12}\right ) \boldsymbol{e}_{1}\wedge \boldsymbol{e}_{2}\wedge \boldsymbol{e}_{4} + \left ( A B^{134} + A^{1} B^{34} - A^{12} B^{234} + A^{123} B^{24} - A^{1234} B^{2} - A^{124} B^{23} + A^{13} B^{4} + A^{134} B - A^{14} B^{3} + A^{2} B^{1234} + A^{23} B^{124} + A^{234} B^{12} - A^{24} B^{123} - A^{3} B^{14} + A^{34} B^{1} + A^{4} B^{13}\right ) \boldsymbol{e}_{1}\wedge \boldsymbol{e}_{3}\wedge \boldsymbol{e}_{4} + \left ( A B^{234} + A^{1} B^{1234} - A^{12} B^{134} + A^{123} B^{14} - A^{1234} B^{1} - A^{124} B^{13} + A^{13} B^{124} + A^{134} B^{12} - A^{14} B^{123} + A^{2} B^{34} + A^{23} B^{4} + A^{234} B - A^{24} B^{3} - A^{3} B^{24} + A^{34} B^{2} + A^{4} B^{23}\right ) \boldsymbol{e}_{2}\wedge \boldsymbol{e}_{3}\wedge \boldsymbol{e}_{4} + \left ( A B^{1234} + A^{1} B^{234} + A^{12} B^{34} + A^{123} B^{4} + A^{1234} B - A^{124} B^{3} - A^{13} B^{24} + A^{134} B^{2} + A^{14} B^{23} - A^{2} B^{134} + A^{23} B^{14} - A^{234} B^{1} - A^{24} B^{13} + A^{3} B^{124} + A^{34} B^{12} - A^{4} B^{123}\right ) \boldsymbol{e}_{1}\wedge \boldsymbol{e}_{2}\wedge \boldsymbol{e}_{3}\wedge \boldsymbol{e}_{4}\end{align*}

```julia
@time test_geometric_product(PGA2D)
```

      0.112692 seconds (157 allocations: 5.859 KiB)

\begin{align*}\left ( A B + A^{1} B^{1} - A^{12} B^{12} + A^{2} B^{2}\right )  + \left ( A B^{1} + A^{1} B + A^{12} B^{2} - A^{2} B^{12}\right ) \boldsymbol{e}_{1} + \left ( A B^{2} + A^{1} B^{12} - A^{12} B^{1} + A^{2} B\right ) \boldsymbol{e}_{2} + \left ( A B^{3} + A^{1} B^{13} - A^{12} B^{123} - A^{123} B^{12} - A^{13} B^{1} + A^{2} B^{23} - A^{23} B^{2} + A^{3} B\right ) \boldsymbol{e}_{3} + \left ( A B^{12} + A^{1} B^{2} + A^{12} B - A^{2} B^{1}\right ) \boldsymbol{e}_{1}\wedge \boldsymbol{e}_{2} + \left ( A B^{13} + A^{1} B^{3} + A^{12} B^{23} - A^{123} B^{2} + A^{13} B - A^{2} B^{123} - A^{23} B^{12} - A^{3} B^{1}\right ) \boldsymbol{e}_{1}\wedge \boldsymbol{e}_{3} + \left ( A B^{23} + A^{1} B^{123} - A^{12} B^{13} + A^{123} B^{1} + A^{13} B^{12} + A^{2} B^{3} + A^{23} B - A^{3} B^{2}\right ) \boldsymbol{e}_{2}\wedge \boldsymbol{e}_{3} + \left ( A B^{123} + A^{1} B^{23} + A^{12} B^{3} + A^{123} B - A^{13} B^{2} - A^{2} B^{13} + A^{23} B^{1} + A^{3} B^{12}\right ) \boldsymbol{e}_{1}\wedge \boldsymbol{e}_{2}\wedge \boldsymbol{e}_{3}\end{align*}

```julia
@time test_geometric_product(PGA3D)
```

      0.857979 seconds (157 allocations: 5.859 KiB)

\begin{align*}\left ( A B + A^{1} B^{1} - A^{12} B^{12} - A^{123} B^{123} - A^{13} B^{13} + A^{2} B^{2} - A^{23} B^{23} + A^{3} B^{3}\right )  + \left ( A B^{1} + A^{1} B + A^{12} B^{2} - A^{123} B^{23} + A^{13} B^{3} - A^{2} B^{12} - A^{23} B^{123} - A^{3} B^{13}\right ) \boldsymbol{e}_{1} + \left ( A B^{2} + A^{1} B^{12} - A^{12} B^{1} + A^{123} B^{13} + A^{13} B^{123} + A^{2} B + A^{23} B^{3} - A^{3} B^{23}\right ) \boldsymbol{e}_{2} + \left ( A B^{3} + A^{1} B^{13} - A^{12} B^{123} - A^{123} B^{12} - A^{13} B^{1} + A^{2} B^{23} - A^{23} B^{2} + A^{3} B\right ) \boldsymbol{e}_{3} + \left ( A B^{4} + A^{1} B^{14} - A^{12} B^{124} - A^{123} B^{1234} + A^{1234} B^{123} - A^{124} B^{12} - A^{13} B^{134} - A^{134} B^{13} - A^{14} B^{1} + A^{2} B^{24} - A^{23} B^{234} - A^{234} B^{23} - A^{24} B^{2} + A^{3} B^{34} - A^{34} B^{3} + A^{4} B\right ) \boldsymbol{e}_{4} + \left ( A B^{12} + A^{1} B^{2} + A^{12} B + A^{123} B^{3} - A^{13} B^{23} - A^{2} B^{1} + A^{23} B^{13} + A^{3} B^{123}\right ) \boldsymbol{e}_{1}\wedge \boldsymbol{e}_{2} + \left ( A B^{13} + A^{1} B^{3} + A^{12} B^{23} - A^{123} B^{2} + A^{13} B - A^{2} B^{123} - A^{23} B^{12} - A^{3} B^{1}\right ) \boldsymbol{e}_{1}\wedge \boldsymbol{e}_{3} + \left ( A B^{14} + A^{1} B^{4} + A^{12} B^{24} - A^{123} B^{234} - A^{1234} B^{23} - A^{124} B^{2} + A^{13} B^{34} - A^{134} B^{3} + A^{14} B - A^{2} B^{124} - A^{23} B^{1234} + A^{234} B^{123} - A^{24} B^{12} - A^{3} B^{134} - A^{34} B^{13} - A^{4} B^{1}\right ) \boldsymbol{e}_{1}\wedge \boldsymbol{e}_{4} + \left ( A B^{23} + A^{1} B^{123} - A^{12} B^{13} + A^{123} B^{1} + A^{13} B^{12} + A^{2} B^{3} + A^{23} B - A^{3} B^{2}\right ) \boldsymbol{e}_{2}\wedge \boldsymbol{e}_{3} + \left ( A B^{24} + A^{1} B^{124} - A^{12} B^{14} + A^{123} B^{134} + A^{1234} B^{13} + A^{124} B^{1} + A^{13} B^{1234} - A^{134} B^{123} + A^{14} B^{12} + A^{2} B^{4} + A^{23} B^{34} - A^{234} B^{3} + A^{24} B - A^{3} B^{234} - A^{34} B^{23} - A^{4} B^{2}\right ) \boldsymbol{e}_{2}\wedge \boldsymbol{e}_{4} + \left ( A B^{34} + A^{1} B^{134} - A^{12} B^{1234} - A^{123} B^{124} - A^{1234} B^{12} + A^{124} B^{123} - A^{13} B^{14} + A^{134} B^{1} + A^{14} B^{13} + A^{2} B^{234} - A^{23} B^{24} + A^{234} B^{2} + A^{24} B^{23} + A^{3} B^{4} + A^{34} B - A^{4} B^{3}\right ) \boldsymbol{e}_{3}\wedge \boldsymbol{e}_{4} + \left ( A B^{123} + A^{1} B^{23} + A^{12} B^{3} + A^{123} B - A^{13} B^{2} - A^{2} B^{13} + A^{23} B^{1} + A^{3} B^{12}\right ) \boldsymbol{e}_{1}\wedge \boldsymbol{e}_{2}\wedge \boldsymbol{e}_{3} + \left ( A B^{124} + A^{1} B^{24} + A^{12} B^{4} + A^{123} B^{34} - A^{1234} B^{3} + A^{124} B - A^{13} B^{234} - A^{134} B^{23} - A^{14} B^{2} - A^{2} B^{14} + A^{23} B^{134} + A^{234} B^{13} + A^{24} B^{1} + A^{3} B^{1234} - A^{34} B^{123} + A^{4} B^{12}\right ) \boldsymbol{e}_{1}\wedge \boldsymbol{e}_{2}\wedge \boldsymbol{e}_{4} + \left ( A B^{134} + A^{1} B^{34} + A^{12} B^{234} - A^{123} B^{24} + A^{1234} B^{2} + A^{124} B^{23} + A^{13} B^{4} + A^{134} B - A^{14} B^{3} - A^{2} B^{1234} - A^{23} B^{124} - A^{234} B^{12} + A^{24} B^{123} - A^{3} B^{14} + A^{34} B^{1} + A^{4} B^{13}\right ) \boldsymbol{e}_{1}\wedge \boldsymbol{e}_{3}\wedge \boldsymbol{e}_{4} + \left ( A B^{234} + A^{1} B^{1234} - A^{12} B^{134} + A^{123} B^{14} - A^{1234} B^{1} - A^{124} B^{13} + A^{13} B^{124} + A^{134} B^{12} - A^{14} B^{123} + A^{2} B^{34} + A^{23} B^{4} + A^{234} B - A^{24} B^{3} - A^{3} B^{24} + A^{34} B^{2} + A^{4} B^{23}\right ) \boldsymbol{e}_{2}\wedge \boldsymbol{e}_{3}\wedge \boldsymbol{e}_{4} + \left ( A B^{1234} + A^{1} B^{234} + A^{12} B^{34} + A^{123} B^{4} + A^{1234} B - A^{124} B^{3} - A^{13} B^{24} + A^{134} B^{2} + A^{14} B^{23} - A^{2} B^{134} + A^{23} B^{14} - A^{234} B^{1} - A^{24} B^{13} + A^{3} B^{124} + A^{34} B^{12} - A^{4} B^{123}\right ) \boldsymbol{e}_{1}\wedge \boldsymbol{e}_{2}\wedge \boldsymbol{e}_{3}\wedge \boldsymbol{e}_{4}\end{align*}

```julia
@time test_geometric_product(CGA2D)
```

      1.281144 seconds (157 allocations: 5.859 KiB)

\begin{align*}\left ( A B + A^{1} B^{1} - A^{12} B^{12} - A^{123} B^{123} - A^{1234} B^{1234} + A^{124} B^{124} - A^{13} B^{13} + A^{134} B^{134} + A^{14} B^{14} + A^{2} B^{2} - A^{23} B^{23} + A^{234} B^{234} + A^{24} B^{24} + A^{3} B^{3} + A^{34} B^{34} - A^{4} B^{4}\right )  + \left ( A B^{1} + A^{1} B + A^{12} B^{2} - A^{123} B^{23} + A^{1234} B^{234} + A^{124} B^{24} + A^{13} B^{3} + A^{134} B^{34} - A^{14} B^{4} - A^{2} B^{12} - A^{23} B^{123} - A^{234} B^{1234} + A^{24} B^{124} - A^{3} B^{13} + A^{34} B^{134} + A^{4} B^{14}\right ) \boldsymbol{e}_{1} + \left ( A B^{2} + A^{1} B^{12} - A^{12} B^{1} + A^{123} B^{13} - A^{1234} B^{134} - A^{124} B^{14} + A^{13} B^{123} + A^{134} B^{1234} - A^{14} B^{124} + A^{2} B + A^{23} B^{3} + A^{234} B^{34} - A^{24} B^{4} - A^{3} B^{23} + A^{34} B^{234} + A^{4} B^{24}\right ) \boldsymbol{e}_{2} + \left ( A B^{3} + A^{1} B^{13} - A^{12} B^{123} - A^{123} B^{12} + A^{1234} B^{124} - A^{124} B^{1234} - A^{13} B^{1} - A^{134} B^{14} - A^{14} B^{134} + A^{2} B^{23} - A^{23} B^{2} - A^{234} B^{24} - A^{24} B^{234} + A^{3} B - A^{34} B^{4} + A^{4} B^{34}\right ) \boldsymbol{e}_{3} + \left ( A B^{4} + A^{1} B^{14} - A^{12} B^{124} - A^{123} B^{1234} + A^{1234} B^{123} - A^{124} B^{12} - A^{13} B^{134} - A^{134} B^{13} - A^{14} B^{1} + A^{2} B^{24} - A^{23} B^{234} - A^{234} B^{23} - A^{24} B^{2} + A^{3} B^{34} - A^{34} B^{3} + A^{4} B\right ) \boldsymbol{e}_{4} + \left ( A B^{12} + A^{1} B^{2} + A^{12} B + A^{123} B^{3} + A^{1234} B^{34} - A^{124} B^{4} - A^{13} B^{23} + A^{134} B^{234} + A^{14} B^{24} - A^{2} B^{1} + A^{23} B^{13} - A^{234} B^{134} - A^{24} B^{14} + A^{3} B^{123} + A^{34} B^{1234} - A^{4} B^{124}\right ) \boldsymbol{e}_{1}\wedge \boldsymbol{e}_{2} + \left ( A B^{13} + A^{1} B^{3} + A^{12} B^{23} - A^{123} B^{2} - A^{1234} B^{24} - A^{124} B^{234} + A^{13} B - A^{134} B^{4} + A^{14} B^{34} - A^{2} B^{123} - A^{23} B^{12} + A^{234} B^{124} - A^{24} B^{1234} - A^{3} B^{1} - A^{34} B^{14} - A^{4} B^{134}\right ) \boldsymbol{e}_{1}\wedge \boldsymbol{e}_{3} + \left ( A B^{14} + A^{1} B^{4} + A^{12} B^{24} - A^{123} B^{234} - A^{1234} B^{23} - A^{124} B^{2} + A^{13} B^{34} - A^{134} B^{3} + A^{14} B - A^{2} B^{124} - A^{23} B^{1234} + A^{234} B^{123} - A^{24} B^{12} - A^{3} B^{134} - A^{34} B^{13} - A^{4} B^{1}\right ) \boldsymbol{e}_{1}\wedge \boldsymbol{e}_{4} + \left ( A B^{23} + A^{1} B^{123} - A^{12} B^{13} + A^{123} B^{1} + A^{1234} B^{14} + A^{124} B^{134} + A^{13} B^{12} - A^{134} B^{124} + A^{14} B^{1234} + A^{2} B^{3} + A^{23} B - A^{234} B^{4} + A^{24} B^{34} - A^{3} B^{2} - A^{34} B^{24} - A^{4} B^{234}\right ) \boldsymbol{e}_{2}\wedge \boldsymbol{e}_{3} + \left ( A B^{24} + A^{1} B^{124} - A^{12} B^{14} + A^{123} B^{134} + A^{1234} B^{13} + A^{124} B^{1} + A^{13} B^{1234} - A^{134} B^{123} + A^{14} B^{12} + A^{2} B^{4} + A^{23} B^{34} - A^{234} B^{3} + A^{24} B - A^{3} B^{234} - A^{34} B^{23} - A^{4} B^{2}\right ) \boldsymbol{e}_{2}\wedge \boldsymbol{e}_{4} + \left ( A B^{34} + A^{1} B^{134} - A^{12} B^{1234} - A^{123} B^{124} - A^{1234} B^{12} + A^{124} B^{123} - A^{13} B^{14} + A^{134} B^{1} + A^{14} B^{13} + A^{2} B^{234} - A^{23} B^{24} + A^{234} B^{2} + A^{24} B^{23} + A^{3} B^{4} + A^{34} B - A^{4} B^{3}\right ) \boldsymbol{e}_{3}\wedge \boldsymbol{e}_{4} + \left ( A B^{123} + A^{1} B^{23} + A^{12} B^{3} + A^{123} B - A^{1234} B^{4} + A^{124} B^{34} - A^{13} B^{2} - A^{134} B^{24} - A^{14} B^{234} - A^{2} B^{13} + A^{23} B^{1} + A^{234} B^{14} + A^{24} B^{134} + A^{3} B^{12} - A^{34} B^{124} + A^{4} B^{1234}\right ) \boldsymbol{e}_{1}\wedge \boldsymbol{e}_{2}\wedge \boldsymbol{e}_{3} + \left ( A B^{124} + A^{1} B^{24} + A^{12} B^{4} + A^{123} B^{34} - A^{1234} B^{3} + A^{124} B - A^{13} B^{234} - A^{134} B^{23} - A^{14} B^{2} - A^{2} B^{14} + A^{23} B^{134} + A^{234} B^{13} + A^{24} B^{1} + A^{3} B^{1234} - A^{34} B^{123} + A^{4} B^{12}\right ) \boldsymbol{e}_{1}\wedge \boldsymbol{e}_{2}\wedge \boldsymbol{e}_{4} + \left ( A B^{134} + A^{1} B^{34} + A^{12} B^{234} - A^{123} B^{24} + A^{1234} B^{2} + A^{124} B^{23} + A^{13} B^{4} + A^{134} B - A^{14} B^{3} - A^{2} B^{1234} - A^{23} B^{124} - A^{234} B^{12} + A^{24} B^{123} - A^{3} B^{14} + A^{34} B^{1} + A^{4} B^{13}\right ) \boldsymbol{e}_{1}\wedge \boldsymbol{e}_{3}\wedge \boldsymbol{e}_{4} + \left ( A B^{234} + A^{1} B^{1234} - A^{12} B^{134} + A^{123} B^{14} - A^{1234} B^{1} - A^{124} B^{13} + A^{13} B^{124} + A^{134} B^{12} - A^{14} B^{123} + A^{2} B^{34} + A^{23} B^{4} + A^{234} B - A^{24} B^{3} - A^{3} B^{24} + A^{34} B^{2} + A^{4} B^{23}\right ) \boldsymbol{e}_{2}\wedge \boldsymbol{e}_{3}\wedge \boldsymbol{e}_{4} + \left ( A B^{1234} + A^{1} B^{234} + A^{12} B^{34} + A^{123} B^{4} + A^{1234} B - A^{124} B^{3} - A^{13} B^{24} + A^{134} B^{2} + A^{14} B^{23} - A^{2} B^{134} + A^{23} B^{14} - A^{234} B^{1} - A^{24} B^{13} + A^{3} B^{124} + A^{34} B^{12} - A^{4} B^{123}\right ) \boldsymbol{e}_{1}\wedge \boldsymbol{e}_{2}\wedge \boldsymbol{e}_{3}\wedge \boldsymbol{e}_{4}\end{align*}

```julia
@time test_geometric_product(CGA3D)
```

     14.452170 seconds (157 allocations: 5.859 KiB)

\begin{align*}\left ( A B + A^{1} B^{1} - A^{12} B^{12} - A^{123} B^{123} + A^{1234} B^{1234} - A^{12345} B^{12345} - A^{1235} B^{1235} - A^{124} B^{124} - A^{1245} B^{1245} + A^{125} B^{125} - A^{13} B^{13} - A^{134} B^{134} - A^{1345} B^{1345} + A^{135} B^{135} - A^{14} B^{14} + A^{145} B^{145} + A^{15} B^{15} + A^{2} B^{2} - A^{23} B^{23} - A^{234} B^{234} - A^{2345} B^{2345} + A^{235} B^{235} - A^{24} B^{24} + A^{245} B^{245} + A^{25} B^{25} + A^{3} B^{3} - A^{34} B^{34} + A^{345} B^{345} + A^{35} B^{35} + A^{4} B^{4} + A^{45} B^{45} - A^{5} B^{5}\right )  + \left ( A B^{1} + A^{1} B + A^{12} B^{2} - A^{123} B^{23} - A^{1234} B^{234} - A^{12345} B^{2345} + A^{1235} B^{235} - A^{124} B^{24} + A^{1245} B^{245} + A^{125} B^{25} + A^{13} B^{3} - A^{134} B^{34} + A^{1345} B^{345} + A^{135} B^{35} + A^{14} B^{4} + A^{145} B^{45} - A^{15} B^{5} - A^{2} B^{12} - A^{23} B^{123} + A^{234} B^{1234} - A^{2345} B^{12345} - A^{235} B^{1235} - A^{24} B^{124} - A^{245} B^{1245} + A^{25} B^{125} - A^{3} B^{13} - A^{34} B^{134} - A^{345} B^{1345} + A^{35} B^{135} - A^{4} B^{14} + A^{45} B^{145} + A^{5} B^{15}\right ) \boldsymbol{e}_{1} + \left ( A B^{2} + A^{1} B^{12} - A^{12} B^{1} + A^{123} B^{13} + A^{1234} B^{134} + A^{12345} B^{1345} - A^{1235} B^{135} + A^{124} B^{14} - A^{1245} B^{145} - A^{125} B^{15} + A^{13} B^{123} - A^{134} B^{1234} + A^{1345} B^{12345} + A^{135} B^{1235} + A^{14} B^{124} + A^{145} B^{1245} - A^{15} B^{125} + A^{2} B + A^{23} B^{3} - A^{234} B^{34} + A^{2345} B^{345} + A^{235} B^{35} + A^{24} B^{4} + A^{245} B^{45} - A^{25} B^{5} - A^{3} B^{23} - A^{34} B^{234} - A^{345} B^{2345} + A^{35} B^{235} - A^{4} B^{24} + A^{45} B^{245} + A^{5} B^{25}\right ) \boldsymbol{e}_{2} + \left ( A B^{3} + A^{1} B^{13} - A^{12} B^{123} - A^{123} B^{12} - A^{1234} B^{124} - A^{12345} B^{1245} + A^{1235} B^{125} + A^{124} B^{1234} - A^{1245} B^{12345} - A^{125} B^{1235} - A^{13} B^{1} + A^{134} B^{14} - A^{1345} B^{145} - A^{135} B^{15} + A^{14} B^{134} + A^{145} B^{1345} - A^{15} B^{135} + A^{2} B^{23} - A^{23} B^{2} + A^{234} B^{24} - A^{2345} B^{245} - A^{235} B^{25} + A^{24} B^{234} + A^{245} B^{2345} - A^{25} B^{235} + A^{3} B + A^{34} B^{4} + A^{345} B^{45} - A^{35} B^{5} - A^{4} B^{34} + A^{45} B^{345} + A^{5} B^{35}\right ) \boldsymbol{e}_{3} + \left ( A B^{4} + A^{1} B^{14} - A^{12} B^{124} - A^{123} B^{1234} + A^{1234} B^{123} + A^{12345} B^{1235} + A^{1235} B^{12345} - A^{124} B^{12} + A^{1245} B^{125} - A^{125} B^{1245} - A^{13} B^{134} - A^{134} B^{13} + A^{1345} B^{135} - A^{135} B^{1345} - A^{14} B^{1} - A^{145} B^{15} - A^{15} B^{145} + A^{2} B^{24} - A^{23} B^{234} - A^{234} B^{23} + A^{2345} B^{235} - A^{235} B^{2345} - A^{24} B^{2} - A^{245} B^{25} - A^{25} B^{245} + A^{3} B^{34} - A^{34} B^{3} - A^{345} B^{35} - A^{35} B^{345} + A^{4} B - A^{45} B^{5} + A^{5} B^{45}\right ) \boldsymbol{e}_{4} + \left ( A B^{5} + A^{1} B^{15} - A^{12} B^{125} - A^{123} B^{1235} + A^{1234} B^{12345} + A^{12345} B^{1234} + A^{1235} B^{123} - A^{124} B^{1245} + A^{1245} B^{124} - A^{125} B^{12} - A^{13} B^{135} - A^{134} B^{1345} + A^{1345} B^{134} - A^{135} B^{13} - A^{14} B^{145} - A^{145} B^{14} - A^{15} B^{1} + A^{2} B^{25} - A^{23} B^{235} - A^{234} B^{2345} + A^{2345} B^{234} - A^{235} B^{23} - A^{24} B^{245} - A^{245} B^{24} - A^{25} B^{2} + A^{3} B^{35} - A^{34} B^{345} - A^{345} B^{34} - A^{35} B^{3} + A^{4} B^{45} - A^{45} B^{4} + A^{5} B\right ) \boldsymbol{e}_{5} + \left ( A B^{12} + A^{1} B^{2} + A^{12} B + A^{123} B^{3} - A^{1234} B^{34} + A^{12345} B^{345} + A^{1235} B^{35} + A^{124} B^{4} + A^{1245} B^{45} - A^{125} B^{5} - A^{13} B^{23} - A^{134} B^{234} - A^{1345} B^{2345} + A^{135} B^{235} - A^{14} B^{24} + A^{145} B^{245} + A^{15} B^{25} - A^{2} B^{1} + A^{23} B^{13} + A^{234} B^{134} + A^{2345} B^{1345} - A^{235} B^{135} + A^{24} B^{14} - A^{245} B^{145} - A^{25} B^{15} + A^{3} B^{123} - A^{34} B^{1234} + A^{345} B^{12345} + A^{35} B^{1235} + A^{4} B^{124} + A^{45} B^{1245} - A^{5} B^{125}\right ) \boldsymbol{e}_{1}\wedge \boldsymbol{e}_{2} + \left ( A B^{13} + A^{1} B^{3} + A^{12} B^{23} - A^{123} B^{2} + A^{1234} B^{24} - A^{12345} B^{245} - A^{1235} B^{25} + A^{124} B^{234} + A^{1245} B^{2345} - A^{125} B^{235} + A^{13} B + A^{134} B^{4} + A^{1345} B^{45} - A^{135} B^{5} - A^{14} B^{34} + A^{145} B^{345} + A^{15} B^{35} - A^{2} B^{123} - A^{23} B^{12} - A^{234} B^{124} - A^{2345} B^{1245} + A^{235} B^{125} + A^{24} B^{1234} - A^{245} B^{12345} - A^{25} B^{1235} - A^{3} B^{1} + A^{34} B^{14} - A^{345} B^{145} - A^{35} B^{15} + A^{4} B^{134} + A^{45} B^{1345} - A^{5} B^{135}\right ) \boldsymbol{e}_{1}\wedge \boldsymbol{e}_{3} + \left ( A B^{14} + A^{1} B^{4} + A^{12} B^{24} - A^{123} B^{234} - A^{1234} B^{23} + A^{12345} B^{235} - A^{1235} B^{2345} - A^{124} B^{2} - A^{1245} B^{25} - A^{125} B^{245} + A^{13} B^{34} - A^{134} B^{3} - A^{1345} B^{35} - A^{135} B^{345} + A^{14} B - A^{145} B^{5} + A^{15} B^{45} - A^{2} B^{124} - A^{23} B^{1234} + A^{234} B^{123} + A^{2345} B^{1235} + A^{235} B^{12345} - A^{24} B^{12} + A^{245} B^{125} - A^{25} B^{1245} - A^{3} B^{134} - A^{34} B^{13} + A^{345} B^{135} - A^{35} B^{1345} - A^{4} B^{1} - A^{45} B^{15} - A^{5} B^{145}\right ) \boldsymbol{e}_{1}\wedge \boldsymbol{e}_{4} + \left ( A B^{15} + A^{1} B^{5} + A^{12} B^{25} - A^{123} B^{235} - A^{1234} B^{2345} + A^{12345} B^{234} - A^{1235} B^{23} - A^{124} B^{245} - A^{1245} B^{24} - A^{125} B^{2} + A^{13} B^{35} - A^{134} B^{345} - A^{1345} B^{34} - A^{135} B^{3} + A^{14} B^{45} - A^{145} B^{4} + A^{15} B - A^{2} B^{125} - A^{23} B^{1235} + A^{234} B^{12345} + A^{2345} B^{1234} + A^{235} B^{123} - A^{24} B^{1245} + A^{245} B^{124} - A^{25} B^{12} - A^{3} B^{135} - A^{34} B^{1345} + A^{345} B^{134} - A^{35} B^{13} - A^{4} B^{145} - A^{45} B^{14} - A^{5} B^{1}\right ) \boldsymbol{e}_{1}\wedge \boldsymbol{e}_{5} + \left ( A B^{23} + A^{1} B^{123} - A^{12} B^{13} + A^{123} B^{1} - A^{1234} B^{14} + A^{12345} B^{145} + A^{1235} B^{15} - A^{124} B^{134} - A^{1245} B^{1345} + A^{125} B^{135} + A^{13} B^{12} + A^{134} B^{124} + A^{1345} B^{1245} - A^{135} B^{125} - A^{14} B^{1234} + A^{145} B^{12345} + A^{15} B^{1235} + A^{2} B^{3} + A^{23} B + A^{234} B^{4} + A^{2345} B^{45} - A^{235} B^{5} - A^{24} B^{34} + A^{245} B^{345} + A^{25} B^{35} - A^{3} B^{2} + A^{34} B^{24} - A^{345} B^{245} - A^{35} B^{25} + A^{4} B^{234} + A^{45} B^{2345} - A^{5} B^{235}\right ) \boldsymbol{e}_{2}\wedge \boldsymbol{e}_{3} + \left ( A B^{24} + A^{1} B^{124} - A^{12} B^{14} + A^{123} B^{134} + A^{1234} B^{13} - A^{12345} B^{135} + A^{1235} B^{1345} + A^{124} B^{1} + A^{1245} B^{15} + A^{125} B^{145} + A^{13} B^{1234} - A^{134} B^{123} - A^{1345} B^{1235} - A^{135} B^{12345} + A^{14} B^{12} - A^{145} B^{125} + A^{15} B^{1245} + A^{2} B^{4} + A^{23} B^{34} - A^{234} B^{3} - A^{2345} B^{35} - A^{235} B^{345} + A^{24} B - A^{245} B^{5} + A^{25} B^{45} - A^{3} B^{234} - A^{34} B^{23} + A^{345} B^{235} - A^{35} B^{2345} - A^{4} B^{2} - A^{45} B^{25} - A^{5} B^{245}\right ) \boldsymbol{e}_{2}\wedge \boldsymbol{e}_{4} + \left ( A B^{25} + A^{1} B^{125} - A^{12} B^{15} + A^{123} B^{135} + A^{1234} B^{1345} - A^{12345} B^{134} + A^{1235} B^{13} + A^{124} B^{145} + A^{1245} B^{14} + A^{125} B^{1} + A^{13} B^{1235} - A^{134} B^{12345} - A^{1345} B^{1234} - A^{135} B^{123} + A^{14} B^{1245} - A^{145} B^{124} + A^{15} B^{12} + A^{2} B^{5} + A^{23} B^{35} - A^{234} B^{345} - A^{2345} B^{34} - A^{235} B^{3} + A^{24} B^{45} - A^{245} B^{4} + A^{25} B - A^{3} B^{235} - A^{34} B^{2345} + A^{345} B^{234} - A^{35} B^{23} - A^{4} B^{245} - A^{45} B^{24} - A^{5} B^{2}\right ) \boldsymbol{e}_{2}\wedge \boldsymbol{e}_{5} + \left ( A B^{34} + A^{1} B^{134} - A^{12} B^{1234} - A^{123} B^{124} - A^{1234} B^{12} + A^{12345} B^{125} - A^{1235} B^{1245} + A^{124} B^{123} + A^{1245} B^{1235} + A^{125} B^{12345} - A^{13} B^{14} + A^{134} B^{1} + A^{1345} B^{15} + A^{135} B^{145} + A^{14} B^{13} - A^{145} B^{135} + A^{15} B^{1345} + A^{2} B^{234} - A^{23} B^{24} + A^{234} B^{2} + A^{2345} B^{25} + A^{235} B^{245} + A^{24} B^{23} - A^{245} B^{235} + A^{25} B^{2345} + A^{3} B^{4} + A^{34} B - A^{345} B^{5} + A^{35} B^{45} - A^{4} B^{3} - A^{45} B^{35} - A^{5} B^{345}\right ) \boldsymbol{e}_{3}\wedge \boldsymbol{e}_{4} + \left ( A B^{35} + A^{1} B^{135} - A^{12} B^{1235} - A^{123} B^{125} - A^{1234} B^{1245} + A^{12345} B^{124} - A^{1235} B^{12} + A^{124} B^{12345} + A^{1245} B^{1234} + A^{125} B^{123} - A^{13} B^{15} + A^{134} B^{145} + A^{1345} B^{14} + A^{135} B^{1} + A^{14} B^{1345} - A^{145} B^{134} + A^{15} B^{13} + A^{2} B^{235} - A^{23} B^{25} + A^{234} B^{245} + A^{2345} B^{24} + A^{235} B^{2} + A^{24} B^{2345} - A^{245} B^{234} + A^{25} B^{23} + A^{3} B^{5} + A^{34} B^{45} - A^{345} B^{4} + A^{35} B - A^{4} B^{345} - A^{45} B^{34} - A^{5} B^{3}\right ) \boldsymbol{e}_{3}\wedge \boldsymbol{e}_{5} + \left ( A B^{45} + A^{1} B^{145} - A^{12} B^{1245} - A^{123} B^{12345} + A^{1234} B^{1235} - A^{12345} B^{123} - A^{1235} B^{1234} - A^{124} B^{125} - A^{1245} B^{12} + A^{125} B^{124} - A^{13} B^{1345} - A^{134} B^{135} - A^{1345} B^{13} + A^{135} B^{134} - A^{14} B^{15} + A^{145} B^{1} + A^{15} B^{14} + A^{2} B^{245} - A^{23} B^{2345} - A^{234} B^{235} - A^{2345} B^{23} + A^{235} B^{234} - A^{24} B^{25} + A^{245} B^{2} + A^{25} B^{24} + A^{3} B^{345} - A^{34} B^{35} + A^{345} B^{3} + A^{35} B^{34} + A^{4} B^{5} + A^{45} B - A^{5} B^{4}\right ) \boldsymbol{e}_{4}\wedge \boldsymbol{e}_{5} + \left ( A B^{123} + A^{1} B^{23} + A^{12} B^{3} + A^{123} B + A^{1234} B^{4} + A^{12345} B^{45} - A^{1235} B^{5} - A^{124} B^{34} + A^{1245} B^{345} + A^{125} B^{35} - A^{13} B^{2} + A^{134} B^{24} - A^{1345} B^{245} - A^{135} B^{25} + A^{14} B^{234} + A^{145} B^{2345} - A^{15} B^{235} - A^{2} B^{13} + A^{23} B^{1} - A^{234} B^{14} + A^{2345} B^{145} + A^{235} B^{15} - A^{24} B^{134} - A^{245} B^{1345} + A^{25} B^{135} + A^{3} B^{12} + A^{34} B^{124} + A^{345} B^{1245} - A^{35} B^{125} - A^{4} B^{1234} + A^{45} B^{12345} + A^{5} B^{1235}\right ) \boldsymbol{e}_{1}\wedge \boldsymbol{e}_{2}\wedge \boldsymbol{e}_{3} + \left ( A B^{124} + A^{1} B^{24} + A^{12} B^{4} + A^{123} B^{34} - A^{1234} B^{3} - A^{12345} B^{35} - A^{1235} B^{345} + A^{124} B - A^{1245} B^{5} + A^{125} B^{45} - A^{13} B^{234} - A^{134} B^{23} + A^{1345} B^{235} - A^{135} B^{2345} - A^{14} B^{2} - A^{145} B^{25} - A^{15} B^{245} - A^{2} B^{14} + A^{23} B^{134} + A^{234} B^{13} - A^{2345} B^{135} + A^{235} B^{1345} + A^{24} B^{1} + A^{245} B^{15} + A^{25} B^{145} + A^{3} B^{1234} - A^{34} B^{123} - A^{345} B^{1235} - A^{35} B^{12345} + A^{4} B^{12} - A^{45} B^{125} + A^{5} B^{1245}\right ) \boldsymbol{e}_{1}\wedge \boldsymbol{e}_{2}\wedge \boldsymbol{e}_{4} + \left ( A B^{125} + A^{1} B^{25} + A^{12} B^{5} + A^{123} B^{35} - A^{1234} B^{345} - A^{12345} B^{34} - A^{1235} B^{3} + A^{124} B^{45} - A^{1245} B^{4} + A^{125} B - A^{13} B^{235} - A^{134} B^{2345} + A^{1345} B^{234} - A^{135} B^{23} - A^{14} B^{245} - A^{145} B^{24} - A^{15} B^{2} - A^{2} B^{15} + A^{23} B^{135} + A^{234} B^{1345} - A^{2345} B^{134} + A^{235} B^{13} + A^{24} B^{145} + A^{245} B^{14} + A^{25} B^{1} + A^{3} B^{1235} - A^{34} B^{12345} - A^{345} B^{1234} - A^{35} B^{123} + A^{4} B^{1245} - A^{45} B^{124} + A^{5} B^{12}\right ) \boldsymbol{e}_{1}\wedge \boldsymbol{e}_{2}\wedge \boldsymbol{e}_{5} + \left ( A B^{134} + A^{1} B^{34} + A^{12} B^{234} - A^{123} B^{24} + A^{1234} B^{2} + A^{12345} B^{25} + A^{1235} B^{245} + A^{124} B^{23} - A^{1245} B^{235} + A^{125} B^{2345} + A^{13} B^{4} + A^{134} B - A^{1345} B^{5} + A^{135} B^{45} - A^{14} B^{3} - A^{145} B^{35} - A^{15} B^{345} - A^{2} B^{1234} - A^{23} B^{124} - A^{234} B^{12} + A^{2345} B^{125} - A^{235} B^{1245} + A^{24} B^{123} + A^{245} B^{1235} + A^{25} B^{12345} - A^{3} B^{14} + A^{34} B^{1} + A^{345} B^{15} + A^{35} B^{145} + A^{4} B^{13} - A^{45} B^{135} + A^{5} B^{1345}\right ) \boldsymbol{e}_{1}\wedge \boldsymbol{e}_{3}\wedge \boldsymbol{e}_{4} + \left ( A B^{135} + A^{1} B^{35} + A^{12} B^{235} - A^{123} B^{25} + A^{1234} B^{245} + A^{12345} B^{24} + A^{1235} B^{2} + A^{124} B^{2345} - A^{1245} B^{234} + A^{125} B^{23} + A^{13} B^{5} + A^{134} B^{45} - A^{1345} B^{4} + A^{135} B - A^{14} B^{345} - A^{145} B^{34} - A^{15} B^{3} - A^{2} B^{1235} - A^{23} B^{125} - A^{234} B^{1245} + A^{2345} B^{124} - A^{235} B^{12} + A^{24} B^{12345} + A^{245} B^{1234} + A^{25} B^{123} - A^{3} B^{15} + A^{34} B^{145} + A^{345} B^{14} + A^{35} B^{1} + A^{4} B^{1345} - A^{45} B^{134} + A^{5} B^{13}\right ) \boldsymbol{e}_{1}\wedge \boldsymbol{e}_{3}\wedge \boldsymbol{e}_{5} + \left ( A B^{145} + A^{1} B^{45} + A^{12} B^{245} - A^{123} B^{2345} - A^{1234} B^{235} - A^{12345} B^{23} + A^{1235} B^{234} - A^{124} B^{25} + A^{1245} B^{2} + A^{125} B^{24} + A^{13} B^{345} - A^{134} B^{35} + A^{1345} B^{3} + A^{135} B^{34} + A^{14} B^{5} + A^{145} B - A^{15} B^{4} - A^{2} B^{1245} - A^{23} B^{12345} + A^{234} B^{1235} - A^{2345} B^{123} - A^{235} B^{1234} - A^{24} B^{125} - A^{245} B^{12} + A^{25} B^{124} - A^{3} B^{1345} - A^{34} B^{135} - A^{345} B^{13} + A^{35} B^{134} - A^{4} B^{15} + A^{45} B^{1} + A^{5} B^{14}\right ) \boldsymbol{e}_{1}\wedge \boldsymbol{e}_{4}\wedge \boldsymbol{e}_{5} + \left ( A B^{234} + A^{1} B^{1234} - A^{12} B^{134} + A^{123} B^{14} - A^{1234} B^{1} - A^{12345} B^{15} - A^{1235} B^{145} - A^{124} B^{13} + A^{1245} B^{135} - A^{125} B^{1345} + A^{13} B^{124} + A^{134} B^{12} - A^{1345} B^{125} + A^{135} B^{1245} - A^{14} B^{123} - A^{145} B^{1235} - A^{15} B^{12345} + A^{2} B^{34} + A^{23} B^{4} + A^{234} B - A^{2345} B^{5} + A^{235} B^{45} - A^{24} B^{3} - A^{245} B^{35} - A^{25} B^{345} - A^{3} B^{24} + A^{34} B^{2} + A^{345} B^{25} + A^{35} B^{245} + A^{4} B^{23} - A^{45} B^{235} + A^{5} B^{2345}\right ) \boldsymbol{e}_{2}\wedge \boldsymbol{e}_{3}\wedge \boldsymbol{e}_{4} + \left ( A B^{235} + A^{1} B^{1235} - A^{12} B^{135} + A^{123} B^{15} - A^{1234} B^{145} - A^{12345} B^{14} - A^{1235} B^{1} - A^{124} B^{1345} + A^{1245} B^{134} - A^{125} B^{13} + A^{13} B^{125} + A^{134} B^{1245} - A^{1345} B^{124} + A^{135} B^{12} - A^{14} B^{12345} - A^{145} B^{1234} - A^{15} B^{123} + A^{2} B^{35} + A^{23} B^{5} + A^{234} B^{45} - A^{2345} B^{4} + A^{235} B - A^{24} B^{345} - A^{245} B^{34} - A^{25} B^{3} - A^{3} B^{25} + A^{34} B^{245} + A^{345} B^{24} + A^{35} B^{2} + A^{4} B^{2345} - A^{45} B^{234} + A^{5} B^{23}\right ) \boldsymbol{e}_{2}\wedge \boldsymbol{e}_{3}\wedge \boldsymbol{e}_{5} + \left ( A B^{245} + A^{1} B^{1245} - A^{12} B^{145} + A^{123} B^{1345} + A^{1234} B^{135} + A^{12345} B^{13} - A^{1235} B^{134} + A^{124} B^{15} - A^{1245} B^{1} - A^{125} B^{14} + A^{13} B^{12345} - A^{134} B^{1235} + A^{1345} B^{123} + A^{135} B^{1234} + A^{14} B^{125} + A^{145} B^{12} - A^{15} B^{124} + A^{2} B^{45} + A^{23} B^{345} - A^{234} B^{35} + A^{2345} B^{3} + A^{235} B^{34} + A^{24} B^{5} + A^{245} B - A^{25} B^{4} - A^{3} B^{2345} - A^{34} B^{235} - A^{345} B^{23} + A^{35} B^{234} - A^{4} B^{25} + A^{45} B^{2} + A^{5} B^{24}\right ) \boldsymbol{e}_{2}\wedge \boldsymbol{e}_{4}\wedge \boldsymbol{e}_{5} + \left ( A B^{345} + A^{1} B^{1345} - A^{12} B^{12345} - A^{123} B^{1245} - A^{1234} B^{125} - A^{12345} B^{12} + A^{1235} B^{124} + A^{124} B^{1235} - A^{1245} B^{123} - A^{125} B^{1234} - A^{13} B^{145} + A^{134} B^{15} - A^{1345} B^{1} - A^{135} B^{14} + A^{14} B^{135} + A^{145} B^{13} - A^{15} B^{134} + A^{2} B^{2345} - A^{23} B^{245} + A^{234} B^{25} - A^{2345} B^{2} - A^{235} B^{24} + A^{24} B^{235} + A^{245} B^{23} - A^{25} B^{234} + A^{3} B^{45} + A^{34} B^{5} + A^{345} B - A^{35} B^{4} - A^{4} B^{35} + A^{45} B^{3} + A^{5} B^{34}\right ) \boldsymbol{e}_{3}\wedge \boldsymbol{e}_{4}\wedge \boldsymbol{e}_{5} + \left ( A B^{1234} + A^{1} B^{234} + A^{12} B^{34} + A^{123} B^{4} + A^{1234} B - A^{12345} B^{5} + A^{1235} B^{45} - A^{124} B^{3} - A^{1245} B^{35} - A^{125} B^{345} - A^{13} B^{24} + A^{134} B^{2} + A^{1345} B^{25} + A^{135} B^{245} + A^{14} B^{23} - A^{145} B^{235} + A^{15} B^{2345} - A^{2} B^{134} + A^{23} B^{14} - A^{234} B^{1} - A^{2345} B^{15} - A^{235} B^{145} - A^{24} B^{13} + A^{245} B^{135} - A^{25} B^{1345} + A^{3} B^{124} + A^{34} B^{12} - A^{345} B^{125} + A^{35} B^{1245} - A^{4} B^{123} - A^{45} B^{1235} - A^{5} B^{12345}\right ) \boldsymbol{e}_{1}\wedge \boldsymbol{e}_{2}\wedge \boldsymbol{e}_{3}\wedge \boldsymbol{e}_{4} + \left ( A B^{1235} + A^{1} B^{235} + A^{12} B^{35} + A^{123} B^{5} + A^{1234} B^{45} - A^{12345} B^{4} + A^{1235} B - A^{124} B^{345} - A^{1245} B^{34} - A^{125} B^{3} - A^{13} B^{25} + A^{134} B^{245} + A^{1345} B^{24} + A^{135} B^{2} + A^{14} B^{2345} - A^{145} B^{234} + A^{15} B^{23} - A^{2} B^{135} + A^{23} B^{15} - A^{234} B^{145} - A^{2345} B^{14} - A^{235} B^{1} - A^{24} B^{1345} + A^{245} B^{134} - A^{25} B^{13} + A^{3} B^{125} + A^{34} B^{1245} - A^{345} B^{124} + A^{35} B^{12} - A^{4} B^{12345} - A^{45} B^{1234} - A^{5} B^{123}\right ) \boldsymbol{e}_{1}\wedge \boldsymbol{e}_{2}\wedge \boldsymbol{e}_{3}\wedge \boldsymbol{e}_{5} + \left ( A B^{1245} + A^{1} B^{245} + A^{12} B^{45} + A^{123} B^{345} - A^{1234} B^{35} + A^{12345} B^{3} + A^{1235} B^{34} + A^{124} B^{5} + A^{1245} B - A^{125} B^{4} - A^{13} B^{2345} - A^{134} B^{235} - A^{1345} B^{23} + A^{135} B^{234} - A^{14} B^{25} + A^{145} B^{2} + A^{15} B^{24} - A^{2} B^{145} + A^{23} B^{1345} + A^{234} B^{135} + A^{2345} B^{13} - A^{235} B^{134} + A^{24} B^{15} - A^{245} B^{1} - A^{25} B^{14} + A^{3} B^{12345} - A^{34} B^{1235} + A^{345} B^{123} + A^{35} B^{1234} + A^{4} B^{125} + A^{45} B^{12} - A^{5} B^{124}\right ) \boldsymbol{e}_{1}\wedge \boldsymbol{e}_{2}\wedge \boldsymbol{e}_{4}\wedge \boldsymbol{e}_{5} + \left ( A B^{1345} + A^{1} B^{345} + A^{12} B^{2345} - A^{123} B^{245} + A^{1234} B^{25} - A^{12345} B^{2} - A^{1235} B^{24} + A^{124} B^{235} + A^{1245} B^{23} - A^{125} B^{234} + A^{13} B^{45} + A^{134} B^{5} + A^{1345} B - A^{135} B^{4} - A^{14} B^{35} + A^{145} B^{3} + A^{15} B^{34} - A^{2} B^{12345} - A^{23} B^{1245} - A^{234} B^{125} - A^{2345} B^{12} + A^{235} B^{124} + A^{24} B^{1235} - A^{245} B^{123} - A^{25} B^{1234} - A^{3} B^{145} + A^{34} B^{15} - A^{345} B^{1} - A^{35} B^{14} + A^{4} B^{135} + A^{45} B^{13} - A^{5} B^{134}\right ) \boldsymbol{e}_{1}\wedge \boldsymbol{e}_{3}\wedge \boldsymbol{e}_{4}\wedge \boldsymbol{e}_{5} + \left ( A B^{2345} + A^{1} B^{12345} - A^{12} B^{1345} + A^{123} B^{145} - A^{1234} B^{15} + A^{12345} B^{1} + A^{1235} B^{14} - A^{124} B^{135} - A^{1245} B^{13} + A^{125} B^{134} + A^{13} B^{1245} + A^{134} B^{125} + A^{1345} B^{12} - A^{135} B^{124} - A^{14} B^{1235} + A^{145} B^{123} + A^{15} B^{1234} + A^{2} B^{345} + A^{23} B^{45} + A^{234} B^{5} + A^{2345} B - A^{235} B^{4} - A^{24} B^{35} + A^{245} B^{3} + A^{25} B^{34} - A^{3} B^{245} + A^{34} B^{25} - A^{345} B^{2} - A^{35} B^{24} + A^{4} B^{235} + A^{45} B^{23} - A^{5} B^{234}\right ) \boldsymbol{e}_{2}\wedge \boldsymbol{e}_{3}\wedge \boldsymbol{e}_{4}\wedge \boldsymbol{e}_{5} + \left ( A B^{12345} + A^{1} B^{2345} + A^{12} B^{345} + A^{123} B^{45} + A^{1234} B^{5} + A^{12345} B - A^{1235} B^{4} - A^{124} B^{35} + A^{1245} B^{3} + A^{125} B^{34} - A^{13} B^{245} + A^{134} B^{25} - A^{1345} B^{2} - A^{135} B^{24} + A^{14} B^{235} + A^{145} B^{23} - A^{15} B^{234} - A^{2} B^{1345} + A^{23} B^{145} - A^{234} B^{15} + A^{2345} B^{1} + A^{235} B^{14} - A^{24} B^{135} - A^{245} B^{13} + A^{25} B^{134} + A^{3} B^{1245} + A^{34} B^{125} + A^{345} B^{12} - A^{35} B^{124} - A^{4} B^{1235} + A^{45} B^{123} + A^{5} B^{1234}\right ) \boldsymbol{e}_{1}\wedge \boldsymbol{e}_{2}\wedge \boldsymbol{e}_{3}\wedge \boldsymbol{e}_{4}\wedge \boldsymbol{e}_{5}\end{align*}

```julia
function test_all(V)
    dimV = range(0, stop=V.n)
    I = V.I()

    α = V.mv("α", "scalar")
    β = V.mv("β", "scalar")
    γ = V.mv("γ", "scalar")
    λ = V.mv("λ", "scalar")

    u = V.mv("u", "vector")
    v = V.mv("v", "vector")
    w = V.mv("w", "vector")

    A = V.mv("A", "mv")
    B = V.mv("B", "mv")
    C = V.mv("C", "mv")
    D = V.mv("D", "mv")

    R = V.mv("R", "spinor")

    # Precalculte AB and BA
    AB = A * B
    BA = B * A

    # The following tests verified implementation correctness per definition

    @test u ⋅ v == u | v == (u < v) == (u > v) == u ⨼ v == u ⨽ v == u ⊙ v
    @test u ∧ v == u ⊠ v
    @test v ⨼ B == (v < B)
    @test v ⨽ B == (v > B)
    if V ∉ [PGA3D, CGA3D] # too slow
        @test A ⊙ B == A << B == (AB + BA) / 2
        # @test A ×̄ B == A ⊙ B
        @test A ⊠ B == A >> B == (AB - BA) / 2
        @test A ⊛ B == A % B
    end

    @test abs(v) == norm(v) == v.norm()
    if V ∉ [Spacetime, PGA2D, PGA3D, CGA2D, CGA3D]
        @test abs(R) == norm(R) == R.norm()
    end

    @test ~A == A[:~] == rev(A) == A.rev()

    if V ∉ [Dual, PGA2D, PGA3D, CGA2D, CGA3D]
        @test A' == dual(A) == A.dual() == adjoint(A) == A * I # Ga.dual_mode_value is default to "I+"
        @test (v)⁻¹ == v[:⁻¹] == v^-1 == inv(v) == v.inv()
        @test v^-2 == (v^2).inv()
    end

    @test (A)ˣ == A[:*] == involute(A) == (A)₊ - (A)₋ == A[:+] - A[:-] == A.even() - A.odd()
    @test (A)ǂ == A[:ǂ] == conj(A) == involute(A).rev()

    if V ∉ [Spacetime, PGA2D, PGA3D, CGA2D, CGA3D]
        @test R^-2 == (R^2).inv()
        @test (R)⁻¹ == R[:⁻¹] == R^-1 == inv(R) == R.inv()
        @test ((R)⁻¹)ˣ == ((R)ˣ)⁻¹
        @test ((R)⁻¹)ǂ == ((R)ǂ)⁻¹
    end

    if V ∈ [Cl2, Cl3]
        @test (v)⁻¹ == (~v) / norm(v)^2 == v / v^2
        @test (R)⁻¹ == (~R) / norm(R)^2 == R / R^2
    end

    @test v^0 == 1
    @test v^2 == v*v

    @test ((A)ˣ)ˣ == ~(~A) == A[:~][:~] == ((A)ǂ)ǂ == A
    @test ~((A)ˣ) == (~A)ˣ

    if V ∉ [Dual]
        @test proj(u, v) == v.project_in_blade(u)
        @test refl(u, v) == v.reflect_in_blade(u)
    end

    if V ∉ [PGA2D, PGA3D, CGA2D, CGA3D] # too slow
        @test rot(u ∧ v, A) == A.rotate_multivector(u ∧ v)
        @test exp(u ∧ v) == (u ∧ v).exp()
    end

    @test typeof(scalar(A)) == Sym
    @test typeof(A[0]) == Mv
    @test scalar(A) == A.scalar() == A[0].obj
    @test (A)₊ == A[:+] == even(A) == A.even()
    @test (A)₋ == A[:-] == odd(A) == A.odd()

    for r ∈ dimV
        A[r] == A.grade(r) == A.get_grade(r)
    end

    # The following tests verified many identities in Linear Algebra

    @test v + w == w + v
    @test (u + v) + w == u + (v + w)
    @test v + 0 == v
    @test 0 * v == 0
    @test 1 * v == v
    @test α * (β * v) == (α * β) * v
    @test α * (v + w) == α * v + α * w
    @test (α + β) * v == α * v + β * v
    @test v + (-1) * v == 0
    @test -v == -1 * v

    𝑶 = vector(V, fill(0, V.n))
    @test α * 𝑶 == 𝑶
    @test (-α) * v == α * (-v) == -α * v

    # The following tests verified many identities in https://arxiv.org/abs/1205.5935

    @test v * v == (v * v).scalar()
    @test v * B == v ⋅ B + v ∧ B == v ⨼ B + v ∧ B

    @test u ∧ (v + λ * u) == u ∧ v

    @test v == v[1]
    if V.n >= 2
        G2 = V.mv("G2", "grade", 2)
        @test G2 == G2[2]
    end

    for r ∈ dimV
        @test (A + B)[r] == A[r] + B[r]
        @test (λ * A)[r] == (A * λ)[r] == λ * A[r]

        Ar = A[r]

        @test v ⨼ Ar == (v * Ar - (-1)^r * Ar * v) / 2
        @test Ar ⨽ v == (Ar * v - (-1)^r * v * Ar) / 2 == (-1)^(r-1) * (v ⨼ Ar)
        @test v ∧ Ar == (v * Ar + (-1)^r * Ar * v) / 2
        @test Ar ∧ v == (Ar * v + (-1)^r * v * Ar) / 2 == (-1)^r * (v ∧ Ar)

        @test v ⨼ Ar == (v * Ar)[r-1]
        @test v ∧ Ar == (v * Ar)[r+1]
        @test Ar ⨽ v == (Ar * v)[r-1]
        @test Ar ∧ v == (Ar * v)[r+1]
        @test v * Ar == v ⨼ Ar + v ∧ Ar
        @test Ar * v == Ar ⨽ v + Ar ∧ v

        Br = B[r]
        Ar ⨼ Br == Ar ⨽ Br == (Ar * Br).scalar()

        for s ∈ dimV
            @test A[r][s] == (if r == s; A[r] else 0 end)

            Bs = B[s]
            ArBs = Ar * Bs

            @test ArBs == sum([ArBs[abs(r - s) + 2j] for j=0:min(r, s)])                      # A.4.1
            @test Ar ⨼ Bs == (-1)^(r * (s - 1)) * Bs ⨽ Ar                                    # A.4.10
            @test Ar ∧ Bs == (-1)^(r * s) * Bs ∧ Ar                                          # A.4.11

            for j ∈ dimV
                @test ArBs[r + s - 2j] == (-1)^(r * s - j) * (B[s] * A[r])[r + s - 2j]        # A.4.2
            end

            if V ∉ [PGA2D, PGA3D, CGA2D, CGA3D] # too slow
                @test v ⨼ ArBs == (v * ArBs - (-1)^(r+s) * ArBs * v)/2 ==
                    (v ⨼ Ar) * Bs + (-1)^r * Ar * (v ⨼ Bs) ==
                    (v ∧ Ar) * Bs - (-1)^r * Ar * (v ∧ Bs)
                @test v ∧ ArBs == (v * ArBs + (-1)^(r+s) * ArBs * v)/2 ==
                    (v ∧ Ar) * Bs - (-1)^r * Ar * (v ⨼ Bs) ==
                    (v ⨼ Ar) * Bs + (-1)^r * Ar * (v ∧ Bs)
            end

            @test v ⨼ (Ar ∧ Bs) == (v ⨼ Ar) ∧ Bs + (-1)^r * Ar ∧ (v ⨼ Bs)
            @test v ∧ (Ar ⨽ Bs) == (v ∧ Ar) ⨽ Bs - (-1)^r * Ar ⨽ (v ⨼ Bs)
            @test v ∧ (Ar ⨼ Bs) == (v ⨼ Ar) ⨼ Bs + (-1)^r * Ar ⨼ (v ∧ Bs)

            if r > s
                @test Ar ⨼ Bs == Bs ⨽ Ar == 0
            end

            if V ∉ [PGA2D, PGA3D, CGA2D, CGA3D] # too slow
                for t ∈ dimV
                    Ct = C[t]

                    Ar ∧ (Bs ∧ Ct) == (Ar * Bs * Ct)[r + s + t]
                end
            end
        end
    end

    @test A == sum([A[r] for r ∈ dimV])
    @test A[-3] == 0

    @test v ⨼ A == (v * A - (A)ˣ * v)/2                                                      # A.4.13
    @test v ∧ A == (v * A + (A)ˣ * v)/2                                                      # A.4.14
    @test A ⨽ v == - v ⨼ (A)ˣ                                                                # A.4.15
    @test A ∧ v == v ∧ (A)ˣ                                                                  # A.4.16

    if V ∉ [PGA2D, PGA3D, CGA2D, CGA3D] # too slow
        @test v ⨼ (AB) == (v ⨼ A) * B + (A)ˣ * (v ⨼ B) == (v ∧ A) * B - (A)ˣ * (v ∧ B)       # A.4.18-19
        @test v ∧ (AB) == (v ∧ A) * B - (A)ˣ * (v ⨼ B) == (v ⨼ A) * B + (A)ˣ * (v ∧ B)       # A.4.20-21
    end

    @test v ⨼ (A ∧ B) == (v ⨼ A) ∧ B + (A)ˣ ∧ (v ⨼ B)                                       # A.4.22
    @test v ∧ (A ⨽ B) == (v ∧ A) ⨽ B - (A)ˣ ⨽ (v ⨼ B)                                       # A.4.23
    @test v ∧ (A ⨼ B) == (v ⨼ A) ⨼ B + (A)ˣ ⨼ (v ∧ B)                                       # A.4.24

    @test v ⨼ A[:+] == - (A[:+] ⨽ v)
    @test v ⨼ A[:-] == A[:-] ⨽ v
    @test v ∧ A[:+] == A[:+] ∧ v
    @test v ∧ A[:-] == - (A[:-] ∧ v)

    if V ∉ [PGA2D, PGA3D, CGA2D, CGA3D] # too slow
        @test (AB).scalar() == (BA).scalar() == (~A * ~B).scalar() ==
            ((A)ˣ * (B)ˣ).scalar() == ((A)ǂ * (B)ǂ).scalar()                                      # A.4.3-6
    end

    @test A ⨼ B == sum([sum([(A[r] * B[s])[s - r] for r ∈ dimV]) for s ∈ dimV])             # A.4.7
    @test A ⨽ B == sum([sum([(A[r] * B[s])[r - s] for r ∈ dimV]) for s ∈ dimV])             # A.4.8
    @test A ∧ B == sum([sum([(A[r] * B[s])[r + s] for r ∈ dimV]) for s ∈ dimV])             # A.4.9

    @test (A ∧ B) ∧ C == A ∧ (B ∧ C) == A ∧ B ∧ C                                           # A.4.28
    @test A ⨼ (B ⨽ C) == (A ⨼ B) ⨽ C                                                        # A.4.29
    @test A ⨼ (B ⨼ C) == (A ∧ B) ⨼ C                                                        # A.4.30
    @test A ⨽ (B ∧ C) == (A ⨽ B) ⨽ C                                                        # A.4.31
    @test (A ∧ B) ⨼ C == A ⨼ (B ⨼ C)

    @test u ∧ A ∧ v == - v ∧ A ∧ u                                                           # A.4.17

    if V ∉ [PGA2D, PGA3D, CGA2D, CGA3D] # too slow
        @test AB == A ⊠ B + A ⊙ B
        @test A ⊙ B == B ⊙ A
        @test A ⊠ B == - B ⊠ A

        @test A ⊛ B == B ⊛ A

        @test A ⊛ B == ~A ⊛ ~B == A.rev() ⊛ B.rev()
        @test A ⊛ (B * C) == (~B * A) ⊛ C
        @test A ⊛ (B ⨽ C) == (~B ⨽ A) ⊛ C
        @test A ⊛ (B ⨼ C) == (~B ∧ A) ⊛ C
        @test A ⊛ (B ∧ C) == (~B ⨼ A) ⊛ C
    end

    if V ∉ [Spacetime, ℂ, ℍ, Dual, PGA2D, PGA3D, CGA2D, CGA3D]
        @test A ⊛ B == A' ⊛ B' == A.dual() ⊛ B.dual()
    end

    if V ∉ [PGA2D, PGA3D, CGA2D, CGA3D] # too slow
        @test AB ⋅ C ∧ D == ((AB) ⋅ C) ∧ D
    end

    if V ∉ [Dual, PGA2D, PGA3D, CGA2D, CGA3D]
        @test u.dual() == u * V.I()
        @test proj(u, v) == (v ⋅ u) / u == (v ⨼ u) ⨼ u.inv()
        @test proj(w, v) + proj(w, u) == proj(w, u + v)
    end

    if V == Cl3
        @test u × v == -I * (u ∧ v)
        @test_throws PyCall.PyError A × B

        Vr = u ∧ v
        @test proj(Vr, B) == B ⨼ Vr * (Vr)⁻¹ == (B ⨼ Vr) ⨼ (Vr)⁻¹                               # A.4.34
        # TODO this is failing for now
        @test_broken refl(Vr, B) == B ∧ Vr * (Vr)⁻¹ == (B ∧ Vr) ⨽ (Vr)⁻¹                               # A.4.35

        # The following tests verified interoperability with numeric and symbolic numbers
        (ex, ey, ez) = V.mv()

        uu = vector(V, [1, 2, 3])
        vv = vector(V, [4, 5, 6])
        ww = vector(V, [5, 6, 7])

        @test uu + vv == 5 * ex + 7 * ey + 9 * ez
        @test 7 * uu + 2 * ww == 17 * ex + 26 * ey + 35 * ez
        @test 7 * uu - 2 * ww == -3 * ex + 2 * ey + 7 * ez
        @test 3 * uu + 2 * vv + ww == 16 * ex + 22 * ey + 28 * ez
        @test (sympy.sqrt(2) * u + sympy.Rational(2, 3) * v) ⋅ ey ==
            sympy.sqrt(2) * (u ⋅ ey) + sympy.Rational(2, 3) * (v ⋅ ey)
    end
end
```

    test_all (generic function with 1 method)

```julia
using Profile
```

```julia
Profile.init(n = 10^8, delay = 0.01)
```

```julia
@time @profile test_all(PGA3D)
```

     27.347987 seconds (7.76 M allocations: 377.404 MiB, 1.26% gc time)

```julia
using ProfileView
ProfileView.svgwrite("profile_results.svg",combine = true, colorgc=false, pruned=[
        ("PyObject", raw"pyfncall.jl"),
        ("_pycall!", raw"pyfncall.jl")])
```

```julia

```


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

# Diminuindo o erro: a fórmula do valor médio e a fórmula dos trapézios

Vimos que o erro ao considerar somas de Riemann (na verdade, deveríamos chamar de "somas de Cauchy" estas que usam o ponto inicial)
tende a zero porque a oscilação da função diminui conforme o tamanho do intervalo considerado diminui.
Será que é possível (analogamente às diferenças centrais) obter fórmulas que convirjam mais rápido?

Em geral (ou seja, para funções apenas contínuas) isso não é possível,
pois a oscilação $\omega$ é o único mecanismo de controle que possuímos.
Mas, seguindo o princípio geral do curso, "mais derivadas = melhor convergência",
então vamos procurar métodos que nos dêem erros menores se supusermos que a função seja derivável.

Começamos com funções uma vez deriváveis.

## Erro, com derivadas

A primeira coisa a ser feita é estimar o erro da fórmula que já temos, supondo que $f$ seja derivável.
Começamos com as somas de Cauchy, onde $x_k = c_k$:
$$\begin{align}
e _ {n,k} & = h \cdot \left| f(c_k) - \int_0^1 f(c_k + th) \, dt \right|
     = h \cdot \left| \int_0^1 \big[ f(c_k) - f(c_k + th) \big] \, dt \right| \\
   & = h \cdot \left| \int_0^1 f'(\xi_t)(-th) \, dt \right|
     = h^2 \cdot \left| \int_0^1 f'(\xi_t) t \, dt \right| \\
   & \leq h^2 \cdot \int_0^1 \max \bigl| f'(\xi) \bigr| t \, dt
     =    h^2 \cdot \max \bigl| f'(\xi) \bigr| \cdot \int_0^1 t \, dt
     =    h^2 \cdot \max \bigl| f'(\xi) \bigr| \cdot \frac{1}{2}
\end{align}$$
onde usamos a fórmula do valor médio para derivada: $f(y) - f(x) = f'(\xi)(y - x)$ para algum $\xi \in (x,y)$.

Ao somar todos os $e _ {n,k}$, teremos então que o erro $E_n$ será, no máximo,
$$
\def\maxhalf{\frac{\max \bigl| f'(\xi) \bigr|}{2}}
E_n
  \leq \sum _ {k=0}^{n-1} e _ {n,k}
  \leq n \cdot h^2 \maxhalf
  \leq h \cdot (b - a) \maxhalf.
$$
Assim, o erro da "fórmula de Cauchy" decresce linearmente com $h$.

Obs: podemos obter esta estimativa da estimativa anterior e a seguinte relação:
a oscilação de $f$ num intervalo é sempre menor do que o máximo do valor absoluto da derivada $f'$
neste mesmo intervalo, vezes o comprimento do intervalo.

## Como diminuir o erro?

Para reduzir o erro, podemos apostar em duas vertentes.
Ou fazemos os erros $e _ {n,k}$ se compensarem, ou reduzimos os $e _ {n,k}$ diretamente.
A primeira estratégia depende muito da função considerada, então vamos tentar arrumar um outro método.
Em suma, gostaríamos de reduzir o erro
$$\big(\text{Estimativa da integral de $f$ no intervalo $[c_k, d_k]$}\big) - \int_{c_k}^{d_k} f(u) \, du.$$

Inspirados pela fórmula das diferenças centrais, podemos pensar que, se calcularmos $f$ no meio do intervalo,
em vez de no bordo, o erro pode ser menor.
Ou seja, usaremos $f\left(\frac{c_k + d_k}{2}\right)$ em vez de $f(c_k)$ como estimativa de $f$.
Assim, em vez de calcularmos $S_n$, calcularemos
$$M_n = \sum_{k=0}^{n-1} f \left(\frac{c_k + d_k}{2} \right) \cdot h.$$
Esta fórmula é conhecida como **fórmula do ponto médio**.

### Exercício:

- Implemente a fórmula do ponto médio.
- Refaça os gráficos para as funções seno e gaussiana.

```python
def midpoint(f,a,b,n=100):
    """Calcula uma aproximação da integral de $f$ no intervalo $[a,b]$, com $n$ pontos pela fórmula do ponto médio."""
    ### Resposta aqui

```

```python
# Use esta caixa para calcular os erros
def f(x): return np.sin(x)
def g(x): return np.exp(-x**2)

# Sugestão de valores de $n$ para usar
ns = np.array([int(x) for x in np.logspace(1,4)])
### Resposta aqui

```

```python
def glinha(x):
    return -2*x*np.exp(-x**2)
K_g = abs(glinha(np.pi) - glinha(0))
```

```python
plt.loglog(ns, abs(F_m - true_F), '.', label='seno')
plt.loglog(ns, abs(G_m - true_G), '+', label='gaussiana')
plt.loglog(ns, np.pi**2/(24*ns**2)*2, label='estimativa seno')
plt.loglog(ns, np.pi**2/(24*ns**2)*K_g, label='estimativa gaussiana')
plt.xlabel('Número de passos')
plt.ylabel('Erro')
plt.title('Método do ponto médio para a integral')
plt.legend(loc=0)
plt.show()
```

### Exercício: estimativa do erro

- Suponha que $f$ seja **duas** vezes diferenciável, e estime o erro cometido pela fórmula do ponto médio.
  Para aproveitar a simetria ao máximo, introduza $m_k = \frac{c_k + d_k}{2}$
  e faça uma mudança de variáveis para o intervalo $[-1,1]$ (em vez de $[0,1]$, que não é simétrico!).

Dê sua resposta aqui


```python
from sympy import *
```

```python
sm, pr = symbols('sm, pr')
Dall = symbols('Dx, Dy, Dz')
Dx, Dy, Dz = Dall
nsrcx, nsrcy, nsrcz = symbols('nsrcx, nsrcy, nsrcz')
nobsAll = symbols('nobsx, nobsy, nobsz')
nobsx, nobsy, nobsz = nobsAll
CsRT0, CsRT1, CsRT2 = symbols('CsRT0, CsRT1, CsRT2')
srcb = [symbols('srcb' + str(i)) for i in range(3)]
bsrc_surf_curl = [[symbols('bsrc_surf_curl' + str(i) + str(j)) for j in range(3)] for i in range(3)]
```

```python
r2 = Dx ** 2 + Dy ** 2 + Dz ** 2
invr = 1 / sqrt(r2)
invr3 = invr * invr * invr
Q1 = CsRT1 * invr
Q2 = CsRT0 * invr3
Q3 = CsRT2 * invr3
Q3nD = Q3 * (nsrcx * Dx + nsrcy * Dy + nsrcz * Dz)
```

```python
e = [[[int((i - j) * (j - k) * (k - i) / 2) for k in range(3)]
    for j in range(3)] for i in range(3)]
kronecker = np.array([[1,0,0],[0,1,0],[0,0,1]])
```

```python
Ks = [[[0 for i in range(3)] for j in range(3)] for k in range(3)]
for d_obs in range(3):
    for d_src in range(3):
        for b_src in range(3):
            Kval = 0
            for Ij in range(3):
                A = Q1 * e[Ij][d_obs][d_src]
                B = 0;
                for It in range(3):
                    B += e[Ij][It][d_src] * Dall[It]
                Gimk = A + Q2 * Dall[d_obs] * B
                Kval += Gimk * bsrc_surf_curl[b_src][Ij]
            if d_obs == d_src:
                Kval += Q3nD * srcb[b_src]
            Ks[d_obs][b_src][d_src] = Kval
```

```python
import pickle
pickle.dump(Ks, open('../tectosaur/data/sympyT.pkl', 'wb'))
```

```python
Hs = [[[0 for i in range(3)] for j in range(3)] for k in range(3)]
for b_src in range(3):
    for d_src in range(3):
        disp_grad = [[diff(Ks[l][b_src][d_src], Dall[m]) for m in range(3)] for l in range(3)]
        strain = [[(disp_grad[l][m] + disp_grad[m][l]) / 2 for m in range(3)] for l in range(3)]
        strain_trace = sum([strain[d][d] for d in range(3)])
        lame_lambda = (2 * sm * pr) / (1 - 2 * pr)
        stress = [[lame_lambda * strain_trace * kronecker[l,m] + 2 * sm * strain[l][m] for m in range(3)] for l in range(3)]
        trac = [sum([stress[i][d] * nobsAll[d] for d in range(3)]) for i in range(3)]
        for d_obs in range(3):
            Hs[d_obs][b_src][d_src] = -trac[d_obs]
```

```python
pickle.dump(Hs, open('../tectosaur/data/semiregularizedH.pkl', 'wb'))
```