```python
import numpy as np
import sympy as sym
from sympy.abc import x,y,z,a,b,c
```

# Toy Problem

```python
expression = a*(2*x*y*z + 3*x*y*b + x*y*b*c + 5*x*y*c + x*z + x*z*b*c + x*z*b + x*z*c + x*y*z + x*y*z*b*c + x*y*z*c)/(x*y*z*b*c)
print(sym.count_ops(expression))
expression
```

    42

    a*(b*c*x*y*z + b*c*x*y + b*c*x*z + 3*b*x*y + b*x*z + c*x*y*z + 5*c*x*y + c*x*z + 3*x*y*z + x*z)/(b*c*x*y*z)

```python
exp_expand = sym.expand(expression)
print(sym.count_ops(exp_expand))
expression = exp_expand
exp_expand
```

    46

    a*b*c*x*y*z + a*b*c*x*y + a*b*c*x*z + 3*a*b*x*y + a*b*x*z + a*c*x*y*z + 5*a*c*x*y + a*c*x*z + 3*a*x*y*z + a*x*z

```python
exp_handsimp = a*x*(y*(c*(b + 5) + 3*b) + z*(c*(b*y + b + y + 1) + b + 3*y + 1))
# exp_handsimp = a*x*(y*(2*z + b*(3 + c) + 5*c) + z + z*b*c + z*b + z*c)
sym.count_ops(exp_handsimp)
```

    18

```python
exp_simp = sym.simplify(expression)
print(sym.count_ops(exp_simp))
exp_simp
```

    28

    a*x*(b*c*y*z + b*c*y + b*c*z + 3*b*y + b*z + c*y*z + 5*c*y + c*z + 3*y*z + z)

```python
exp_col = sym.collect(expression, x)
print(sym.count_ops(exp_col))
exp_col
```

    37

    x*(a*b*c*y*z + a*b*c*y + a*b*c*z + 3*a*b*y + a*b*z + a*c*y*z + 5*a*c*y + a*c*z + 3*a*y*z + a*z)

```python
def collect_by_list(expr_in, sort_list):
    expr = sym.expand(expr_in)
    res = expr
    for s in sort_list:
        res = sym.rcollect(res, s)
    return res

def get_symbol_list(expr_in, return_counts=False):
    expr = sym.expand(expr_in)
    syms = {}
    for a in expr.atoms():
        if type(a) is sym.Symbol:
            syms[a] = expr.count(a)
    symlist = list(sorted(syms, key=syms.__getitem__, reverse=True))
    if return_counts:
        countlist = list(sorted(syms.values(), reverse=True))
        return symlist, countlist
    else:
        return symlist

def collect_auto(expr_in, print_ops=False, syms_to_prioritize=[]):
    slist = get_symbol_list(expr_in)
    sort_list = syms_to_prioritize
    for s in slist:
        if s not in syms_to_prioritize:
            sort_list.append(s)
    res = collect_by_list(expr_in, sort_list)
    if print_ops:
        print(sym.count_ops(res))
    return res

def collect_terms_in_fraction(expr_in, terms_to_prioritize=[]):
    numerator, denominator = sym.fraction(expr_in)
    num_simp = collect_auto(numerator, syms_to_prioritize=terms_to_prioritize)
    den_simp = collect_auto(denominator, syms_to_prioritize=terms_to_prioritize)
    return num_simp/den_simp
```

```python
terms_to_prioritize = [z,b,c,y]
full_simp = collect_terms_in_fraction(expression, terms_to_prioritize=terms_to_prioritize)
full_simp
```

    a*(y*(b*(c + 3) + 5*c) + z*(b*(c*(y + 1) + 1) + c*(y + 1) + 3*y + 1))/(b*c*y*z)

```python
e2 = b*c*y + b*c + b + c*y + c + 3*y + 1
collect_auto(e2)
```

    9

    b + c*(b + y*(b + 1) + 1) + 3*y + 1

```python
slist, clist = get_symbol_list(expression, return_counts=True)
print(slist, clist)
expr_srcol = collect_by_list(expression, slist)
print(sym.count_ops(expr_srcol))
expr_srcol
```

    [a, x, z, c, y, b] [10, 10, 7, 6, 6, 5]
    18

    a*x*(y*(3*b + c*(b + 5)) + z*(b + c*(b + y*(b + 1) + 1) + 3*y + 1))

# Real Problem

```python
dKMgO_KMgO = sym.Symbol('dKMgO_KMgO')
dKFeO_KFeO = sym.Symbol('dKFeO_KFeO')
dKSiO2_KSiO2 = sym.Symbol('dKSiO2_KSiO2')
dKMgSiO3_KMgSiO3 = sym.Symbol('dKMgSiO3_KMgSiO3')
dKFeSiO3_KFeSiO3 = sym.Symbol('dKFeSiO3_KFeSiO3')

dM_Mg = sym.Symbol('dM_Mg')
dM_Fe = sym.Symbol('dM_Fe')
dM_Si = sym.Symbol('dM_Si')
dM_O = sym.Symbol('dM_O')
dM_c = sym.Symbol('dM_c')

dM_MgO = sym.Symbol('dM_MgO')
dM_FeO = sym.Symbol('dM_FeO')
dM_SiO2 = sym.Symbol('dM_SiO2')
dM_MgSiO3 = sym.Symbol('dM_MgSiO3')
dM_FeSiO3 = sym.Symbol('dM_FeSiO3')
dM_m = sym.Symbol('dM_m')

M_Mg = sym.Symbol('M_Mg')
M_Fe = sym.Symbol('M_Fe')
M_Si = sym.Symbol('M_Si')
M_O = sym.Symbol('M_O')
M_c = sym.Symbol('M_c')

M_MgO = sym.Symbol('M_MgO')
M_FeO = sym.Symbol('M_FeO')
M_SiO2 = sym.Symbol('M_SiO2')
M_MgSiO3 = sym.Symbol('M_MgSiO3')
M_FeSiO3 = sym.Symbol('M_FeSiO3')
M_m = sym.Symbol('M_m')

terms_to_collect = [dKMgO_KMgO, dKFeO_KFeO, dKSiO2_KSiO2, dKFeSiO3_KFeSiO3,
                    M_Mg, M_Fe, M_Si, M_O, M_c,
                    M_MgO, M_FeO, M_SiO2, M_MgSiO3, M_FeSiO3, M_m]
```

```python
sol_dM_c = M_c*(-M_Fe*dKFeO_KFeO*(2*M_FeO*M_FeSiO3*M_Mg*M_O*M_SiO2 - 2*M_FeO*M_FeSiO3*M_Mg*M_O*M_m + 2*M_FeO*M_FeSiO3*M_Mg*M_Si*M_SiO2 - 2*M_FeO*M_FeSiO3*M_Mg*M_Si*M_m - 2*M_FeO*M_FeSiO3*M_MgO*M_O*M_SiO2 + 2*M_FeO*M_FeSiO3*M_MgO*M_O*M_m - 2*M_FeO*M_FeSiO3*M_MgO*M_Si*M_SiO2 + 2*M_FeO*M_FeSiO3*M_MgO*M_Si*M_m - 2*M_FeO*M_FeSiO3*M_MgSiO3*M_O*M_SiO2 + 2*M_FeO*M_FeSiO3*M_MgSiO3*M_O*M_m - 2*M_FeO*M_FeSiO3*M_MgSiO3*M_Si*M_SiO2 + 2*M_FeO*M_FeSiO3*M_MgSiO3*M_Si*M_m + 3*M_FeO*M_Mg*M_MgSiO3*M_O*M_Si - 2*M_FeO*M_Mg*M_MgSiO3*M_O*M_m + M_FeO*M_Mg*M_MgSiO3*M_Si*M_SiO2 - 3*M_FeO*M_Mg*M_MgSiO3*M_Si*M_m + M_FeO*M_Mg*M_O*M_Si*M_SiO2 + 2*M_FeO*M_Mg*M_O*M_Si*M_m - 2*M_FeO*M_Mg*M_O*M_SiO2*M_m - 2*M_FeO*M_Mg*M_Si*M_SiO2*M_m - 2*M_FeO*M_MgO*M_MgSiO3*M_O*M_SiO2 + 2*M_FeO*M_MgO*M_MgSiO3*M_O*M_m - 2*M_FeO*M_MgO*M_MgSiO3*M_Si*M_SiO2 + 2*M_FeO*M_MgO*M_MgSiO3*M_Si*M_m - M_FeO*M_MgO*M_O*M_Si*M_SiO2 - 2*M_FeO*M_MgO*M_O*M_Si*M_m + 2*M_FeO*M_MgO*M_O*M_SiO2*M_m + 2*M_FeO*M_MgO*M_Si*M_SiO2*M_m - M_FeO*M_MgSiO3*M_O*M_Si*M_SiO2 - 2*M_FeO*M_MgSiO3*M_O*M_Si*M_m + 2*M_FeO*M_MgSiO3*M_O*M_SiO2*M_m + 2*M_FeO*M_MgSiO3*M_Si*M_SiO2*M_m - 3*M_FeSiO3*M_Mg*M_MgO*M_O*M_Si + 2*M_FeSiO3*M_Mg*M_MgO*M_O*M_SiO2 + M_FeSiO3*M_Mg*M_MgO*M_Si*M_SiO2 + M_FeSiO3*M_Mg*M_MgO*M_Si*M_m - 2*M_FeSiO3*M_Mg*M_O*M_Si*M_SiO2 + 5*M_FeSiO3*M_Mg*M_O*M_Si*M_m - 2*M_FeSiO3*M_Mg*M_O*M_SiO2*M_m - 2*M_FeSiO3*M_Mg*M_Si*M_SiO2*M_m - 2*M_FeSiO3*M_MgO*M_MgSiO3*M_O*M_SiO2 + 2*M_FeSiO3*M_MgO*M_MgSiO3*M_O*M_m - 2*M_FeSiO3*M_MgO*M_MgSiO3*M_Si*M_SiO2 + 2*M_FeSiO3*M_MgO*M_MgSiO3*M_Si*M_m + 2*M_FeSiO3*M_MgO*M_O*M_Si*M_SiO2 - 5*M_FeSiO3*M_MgO*M_O*M_Si*M_m + 2*M_FeSiO3*M_MgO*M_O*M_SiO2*M_m + 2*M_FeSiO3*M_MgO*M_Si*M_SiO2*M_m + 2*M_FeSiO3*M_MgSiO3*M_O*M_Si*M_SiO2 - 5*M_FeSiO3*M_MgSiO3*M_O*M_Si*M_m + 2*M_FeSiO3*M_MgSiO3*M_O*M_SiO2*M_m + 2*M_FeSiO3*M_MgSiO3*M_Si*M_SiO2*M_m) + M_FeSiO3*dKFeSiO3_KFeSiO3*(3*M_Fe*M_FeO*M_Mg*M_O*M_Si - 2*M_Fe*M_FeO*M_Mg*M_O*M_SiO2 - M_Fe*M_FeO*M_Mg*M_Si*M_SiO2 - M_Fe*M_FeO*M_Mg*M_Si*M_m - 3*M_Fe*M_FeO*M_MgO*M_O*M_Si + 2*M_Fe*M_FeO*M_MgO*M_O*M_SiO2 + M_Fe*M_FeO*M_MgO*M_Si*M_SiO2 + M_Fe*M_FeO*M_MgO*M_Si*M_m - 3*M_Fe*M_FeO*M_MgSiO3*M_O*M_Si + 2*M_Fe*M_FeO*M_MgSiO3*M_O*M_SiO2 + M_Fe*M_FeO*M_MgSiO3*M_Si*M_SiO2 + M_Fe*M_FeO*M_MgSiO3*M_Si*M_m + 3*M_Fe*M_Mg*M_MgO*M_O*M_Si - 2*M_Fe*M_Mg*M_MgO*M_O*M_SiO2 - M_Fe*M_Mg*M_MgO*M_Si*M_SiO2 - M_Fe*M_Mg*M_MgO*M_Si*M_m + 2*M_Fe*M_Mg*M_O*M_Si*M_SiO2 - 5*M_Fe*M_Mg*M_O*M_Si*M_m + 2*M_Fe*M_Mg*M_O*M_SiO2*M_m + 2*M_Fe*M_Mg*M_Si*M_SiO2*M_m + 2*M_Fe*M_MgO*M_MgSiO3*M_O*M_SiO2 - 2*M_Fe*M_MgO*M_MgSiO3*M_O*M_m + 2*M_Fe*M_MgO*M_MgSiO3*M_Si*M_SiO2 - 2*M_Fe*M_MgO*M_MgSiO3*M_Si*M_m - 2*M_Fe*M_MgO*M_O*M_Si*M_SiO2 + 5*M_Fe*M_MgO*M_O*M_Si*M_m - 2*M_Fe*M_MgO*M_O*M_SiO2*M_m - 2*M_Fe*M_MgO*M_Si*M_SiO2*M_m - 2*M_Fe*M_MgSiO3*M_O*M_Si*M_SiO2 + 5*M_Fe*M_MgSiO3*M_O*M_Si*M_m - 2*M_Fe*M_MgSiO3*M_O*M_SiO2*M_m - 2*M_Fe*M_MgSiO3*M_Si*M_SiO2*M_m - 3*M_FeO*M_Mg*M_MgO*M_O*M_Si + 2*M_FeO*M_Mg*M_MgO*M_O*M_SiO2 + M_FeO*M_Mg*M_MgO*M_Si*M_SiO2 + M_FeO*M_Mg*M_MgO*M_Si*M_m - 3*M_FeO*M_Mg*M_MgSiO3*M_O*M_Si + 2*M_FeO*M_Mg*M_MgSiO3*M_O*M_m - M_FeO*M_Mg*M_MgSiO3*M_Si*M_SiO2 + 3*M_FeO*M_Mg*M_MgSiO3*M_Si*M_m - 3*M_FeO*M_Mg*M_O*M_Si*M_SiO2 + 3*M_FeO*M_Mg*M_O*M_Si*M_m + 3*M_FeO*M_MgO*M_O*M_Si*M_SiO2 - 3*M_FeO*M_MgO*M_O*M_Si*M_m + 3*M_FeO*M_MgSiO3*M_O*M_Si*M_SiO2 - 3*M_FeO*M_MgSiO3*M_O*M_Si*M_m) - M_Mg*dKMgO_KMgO*(-3*M_Fe*M_FeO*M_MgSiO3*M_O*M_Si + 2*M_Fe*M_FeO*M_MgSiO3*M_O*M_SiO2 + M_Fe*M_FeO*M_MgSiO3*M_Si*M_SiO2 + M_Fe*M_FeO*M_MgSiO3*M_Si*M_m + 3*M_Fe*M_FeSiO3*M_MgO*M_O*M_Si - 2*M_Fe*M_FeSiO3*M_MgO*M_O*M_m + M_Fe*M_FeSiO3*M_MgO*M_Si*M_SiO2 - 3*M_Fe*M_FeSiO3*M_MgO*M_Si*M_m + 2*M_Fe*M_MgO*M_MgSiO3*M_O*M_SiO2 - 2*M_Fe*M_MgO*M_MgSiO3*M_O*M_m + 2*M_Fe*M_MgO*M_MgSiO3*M_Si*M_SiO2 - 2*M_Fe*M_MgO*M_MgSiO3*M_Si*M_m + M_Fe*M_MgO*M_O*M_Si*M_SiO2 + 2*M_Fe*M_MgO*M_O*M_Si*M_m - 2*M_Fe*M_MgO*M_O*M_SiO2*M_m - 2*M_Fe*M_MgO*M_Si*M_SiO2*M_m - 2*M_Fe*M_MgSiO3*M_O*M_Si*M_SiO2 + 5*M_Fe*M_MgSiO3*M_O*M_Si*M_m - 2*M_Fe*M_MgSiO3*M_O*M_SiO2*M_m - 2*M_Fe*M_MgSiO3*M_Si*M_SiO2*M_m - 2*M_FeO*M_FeSiO3*M_MgO*M_O*M_SiO2 + 2*M_FeO*M_FeSiO3*M_MgO*M_O*M_m - 2*M_FeO*M_FeSiO3*M_MgO*M_Si*M_SiO2 + 2*M_FeO*M_FeSiO3*M_MgO*M_Si*M_m - 2*M_FeO*M_FeSiO3*M_MgSiO3*M_O*M_SiO2 + 2*M_FeO*M_FeSiO3*M_MgSiO3*M_O*M_m - 2*M_FeO*M_FeSiO3*M_MgSiO3*M_Si*M_SiO2 + 2*M_FeO*M_FeSiO3*M_MgSiO3*M_Si*M_m - 2*M_FeO*M_MgO*M_MgSiO3*M_O*M_SiO2 + 2*M_FeO*M_MgO*M_MgSiO3*M_O*M_m - 2*M_FeO*M_MgO*M_MgSiO3*M_Si*M_SiO2 + 2*M_FeO*M_MgO*M_MgSiO3*M_Si*M_m - M_FeO*M_MgO*M_O*M_Si*M_SiO2 - 2*M_FeO*M_MgO*M_O*M_Si*M_m + 2*M_FeO*M_MgO*M_O*M_SiO2*M_m + 2*M_FeO*M_MgO*M_Si*M_SiO2*M_m + 2*M_FeO*M_MgSiO3*M_O*M_Si*M_SiO2 - 5*M_FeO*M_MgSiO3*M_O*M_Si*M_m + 2*M_FeO*M_MgSiO3*M_O*M_SiO2*M_m + 2*M_FeO*M_MgSiO3*M_Si*M_SiO2*M_m - 2*M_FeSiO3*M_MgO*M_MgSiO3*M_O*M_SiO2 + 2*M_FeSiO3*M_MgO*M_MgSiO3*M_O*M_m - 2*M_FeSiO3*M_MgO*M_MgSiO3*M_Si*M_SiO2 + 2*M_FeSiO3*M_MgO*M_MgSiO3*M_Si*M_m - M_FeSiO3*M_MgO*M_O*M_Si*M_SiO2 - 2*M_FeSiO3*M_MgO*M_O*M_Si*M_m + 2*M_FeSiO3*M_MgO*M_O*M_SiO2*M_m + 2*M_FeSiO3*M_MgO*M_Si*M_SiO2*M_m + 2*M_FeSiO3*M_MgSiO3*M_O*M_Si*M_SiO2 - 5*M_FeSiO3*M_MgSiO3*M_O*M_Si*M_m + 2*M_FeSiO3*M_MgSiO3*M_O*M_SiO2*M_m + 2*M_FeSiO3*M_MgSiO3*M_Si*M_SiO2*M_m) + M_MgSiO3*dKMgSiO3_KMgSiO3*(3*M_Fe*M_FeO*M_Mg*M_O*M_Si - 2*M_Fe*M_FeO*M_Mg*M_O*M_SiO2 - M_Fe*M_FeO*M_Mg*M_Si*M_SiO2 - M_Fe*M_FeO*M_Mg*M_Si*M_m - 3*M_Fe*M_FeO*M_MgO*M_O*M_Si + 2*M_Fe*M_FeO*M_MgO*M_O*M_SiO2 + M_Fe*M_FeO*M_MgO*M_Si*M_SiO2 + M_Fe*M_FeO*M_MgO*M_Si*M_m - 3*M_Fe*M_FeSiO3*M_MgO*M_O*M_Si + 2*M_Fe*M_FeSiO3*M_MgO*M_O*M_m - M_Fe*M_FeSiO3*M_MgO*M_Si*M_SiO2 + 3*M_Fe*M_FeSiO3*M_MgO*M_Si*M_m + 3*M_Fe*M_Mg*M_MgO*M_O*M_Si - 2*M_Fe*M_Mg*M_MgO*M_O*M_SiO2 - M_Fe*M_Mg*M_MgO*M_Si*M_SiO2 - M_Fe*M_Mg*M_MgO*M_Si*M_m + 2*M_Fe*M_Mg*M_O*M_Si*M_SiO2 - 5*M_Fe*M_Mg*M_O*M_Si*M_m + 2*M_Fe*M_Mg*M_O*M_SiO2*M_m + 2*M_Fe*M_Mg*M_Si*M_SiO2*M_m - 3*M_Fe*M_MgO*M_O*M_Si*M_SiO2 + 3*M_Fe*M_MgO*M_O*M_Si*M_m + 2*M_FeO*M_FeSiO3*M_Mg*M_O*M_SiO2 - 2*M_FeO*M_FeSiO3*M_Mg*M_O*M_m + 2*M_FeO*M_FeSiO3*M_Mg*M_Si*M_SiO2 - 2*M_FeO*M_FeSiO3*M_Mg*M_Si*M_m - 3*M_FeO*M_Mg*M_MgO*M_O*M_Si + 2*M_FeO*M_Mg*M_MgO*M_O*M_SiO2 + M_FeO*M_Mg*M_MgO*M_Si*M_SiO2 + M_FeO*M_Mg*M_MgO*M_Si*M_m - 2*M_FeO*M_Mg*M_O*M_Si*M_SiO2 + 5*M_FeO*M_Mg*M_O*M_Si*M_m - 2*M_FeO*M_Mg*M_O*M_SiO2*M_m - 2*M_FeO*M_Mg*M_Si*M_SiO2*M_m + 3*M_FeO*M_MgO*M_O*M_Si*M_SiO2 - 3*M_FeO*M_MgO*M_O*M_Si*M_m - 3*M_FeSiO3*M_Mg*M_MgO*M_O*M_Si + 2*M_FeSiO3*M_Mg*M_MgO*M_O*M_SiO2 + M_FeSiO3*M_Mg*M_MgO*M_Si*M_SiO2 + M_FeSiO3*M_Mg*M_MgO*M_Si*M_m - 2*M_FeSiO3*M_Mg*M_O*M_Si*M_SiO2 + 5*M_FeSiO3*M_Mg*M_O*M_Si*M_m - 2*M_FeSiO3*M_Mg*M_O*M_SiO2*M_m - 2*M_FeSiO3*M_Mg*M_Si*M_SiO2*M_m + 3*M_FeSiO3*M_MgO*M_O*M_Si*M_SiO2 - 3*M_FeSiO3*M_MgO*M_O*M_Si*M_m) - M_Si*dKSiO2_KSiO2*(-3*M_Fe*M_FeO*M_FeSiO3*M_Mg*M_O - M_Fe*M_FeO*M_FeSiO3*M_Mg*M_SiO2 + M_Fe*M_FeO*M_FeSiO3*M_Mg*M_m + 3*M_Fe*M_FeO*M_FeSiO3*M_MgO*M_O + M_Fe*M_FeO*M_FeSiO3*M_MgO*M_SiO2 - M_Fe*M_FeO*M_FeSiO3*M_MgO*M_m + 3*M_Fe*M_FeO*M_FeSiO3*M_MgSiO3*M_O + M_Fe*M_FeO*M_FeSiO3*M_MgSiO3*M_SiO2 - M_Fe*M_FeO*M_FeSiO3*M_MgSiO3*M_m - 3*M_Fe*M_FeO*M_Mg*M_MgSiO3*M_O - M_Fe*M_FeO*M_Mg*M_MgSiO3*M_SiO2 + M_Fe*M_FeO*M_Mg*M_MgSiO3*M_m - M_Fe*M_FeO*M_Mg*M_O*M_SiO2 + M_Fe*M_FeO*M_Mg*M_SiO2*M_m + 3*M_Fe*M_FeO*M_MgO*M_MgSiO3*M_O + M_Fe*M_FeO*M_MgO*M_MgSiO3*M_SiO2 - M_Fe*M_FeO*M_MgO*M_MgSiO3*M_m + M_Fe*M_FeO*M_MgO*M_O*M_SiO2 - M_Fe*M_FeO*M_MgO*M_SiO2*M_m + M_Fe*M_FeO*M_MgSiO3*M_O*M_SiO2 - M_Fe*M_FeO*M_MgSiO3*M_SiO2*M_m - 3*M_Fe*M_FeSiO3*M_Mg*M_MgO*M_O - M_Fe*M_FeSiO3*M_Mg*M_MgO*M_SiO2 + M_Fe*M_FeSiO3*M_Mg*M_MgO*M_m + 5*M_Fe*M_FeSiO3*M_Mg*M_O*M_m + M_Fe*M_FeSiO3*M_Mg*M_SiO2*M_m + 3*M_Fe*M_FeSiO3*M_MgO*M_MgSiO3*M_O + M_Fe*M_FeSiO3*M_MgO*M_MgSiO3*M_SiO2 - M_Fe*M_FeSiO3*M_MgO*M_MgSiO3*M_m - 5*M_Fe*M_FeSiO3*M_MgO*M_O*M_m - M_Fe*M_FeSiO3*M_MgO*M_SiO2*M_m - 5*M_Fe*M_FeSiO3*M_MgSiO3*M_O*M_m - M_Fe*M_FeSiO3*M_MgSiO3*M_SiO2*M_m - 3*M_Fe*M_Mg*M_MgO*M_MgSiO3*M_O - M_Fe*M_Mg*M_MgO*M_MgSiO3*M_SiO2 + M_Fe*M_Mg*M_MgO*M_MgSiO3*M_m - M_Fe*M_Mg*M_MgO*M_O*M_SiO2 + M_Fe*M_Mg*M_MgO*M_SiO2*M_m + 5*M_Fe*M_Mg*M_MgSiO3*M_O*M_m + M_Fe*M_Mg*M_MgSiO3*M_SiO2*M_m + 3*M_Fe*M_Mg*M_O*M_SiO2*M_m + 3*M_Fe*M_MgO*M_MgSiO3*M_O*M_SiO2 - 3*M_Fe*M_MgO*M_MgSiO3*M_O*M_m - 3*M_Fe*M_MgO*M_O*M_SiO2*M_m - 3*M_Fe*M_MgSiO3*M_O*M_SiO2*M_m + 3*M_FeO*M_FeSiO3*M_Mg*M_MgO*M_O + M_FeO*M_FeSiO3*M_Mg*M_MgO*M_SiO2 - M_FeO*M_FeSiO3*M_Mg*M_MgO*M_m + 3*M_FeO*M_FeSiO3*M_Mg*M_MgSiO3*M_O + M_FeO*M_FeSiO3*M_Mg*M_MgSiO3*M_SiO2 - M_FeO*M_FeSiO3*M_Mg*M_MgSiO3*M_m + 3*M_FeO*M_FeSiO3*M_Mg*M_O*M_SiO2 - 3*M_FeO*M_FeSiO3*M_Mg*M_O*M_m - 3*M_FeO*M_FeSiO3*M_MgO*M_O*M_SiO2 + 3*M_FeO*M_FeSiO3*M_MgO*M_O*M_m - 3*M_FeO*M_FeSiO3*M_MgSiO3*M_O*M_SiO2 + 3*M_FeO*M_FeSiO3*M_MgSiO3*M_O*M_m + 3*M_FeO*M_Mg*M_MgO*M_MgSiO3*M_O + M_FeO*M_Mg*M_MgO*M_MgSiO3*M_SiO2 - M_FeO*M_Mg*M_MgO*M_MgSiO3*M_m + M_FeO*M_Mg*M_MgO*M_O*M_SiO2 - M_FeO*M_Mg*M_MgO*M_SiO2*M_m - 5*M_FeO*M_Mg*M_MgSiO3*M_O*M_m - M_FeO*M_Mg*M_MgSiO3*M_SiO2*M_m - 3*M_FeO*M_Mg*M_O*M_SiO2*M_m - 3*M_FeO*M_MgO*M_MgSiO3*M_O*M_SiO2 + 3*M_FeO*M_MgO*M_MgSiO3*M_O*M_m + 3*M_FeO*M_MgO*M_O*M_SiO2*M_m + 3*M_FeO*M_MgSiO3*M_O*M_SiO2*M_m + 3*M_FeSiO3*M_Mg*M_MgO*M_MgSiO3*M_O + M_FeSiO3*M_Mg*M_MgO*M_MgSiO3*M_SiO2 - M_FeSiO3*M_Mg*M_MgO*M_MgSiO3*M_m + M_FeSiO3*M_Mg*M_MgO*M_O*M_SiO2 - M_FeSiO3*M_Mg*M_MgO*M_SiO2*M_m - 5*M_FeSiO3*M_Mg*M_MgSiO3*M_O*M_m - M_FeSiO3*M_Mg*M_MgSiO3*M_SiO2*M_m - 3*M_FeSiO3*M_Mg*M_O*M_SiO2*M_m - 3*M_FeSiO3*M_MgO*M_MgSiO3*M_O*M_SiO2 + 3*M_FeSiO3*M_MgO*M_MgSiO3*M_O*M_m + 3*M_FeSiO3*M_MgO*M_O*M_SiO2*M_m + 3*M_FeSiO3*M_MgSiO3*M_O*M_SiO2*M_m))/(-9*M_Fe*M_FeO*M_FeSiO3*M_Mg*M_O*M_Si + 4*M_Fe*M_FeO*M_FeSiO3*M_Mg*M_O*M_SiO2 + M_Fe*M_FeO*M_FeSiO3*M_Mg*M_O*M_c - 4*M_Fe*M_FeO*M_FeSiO3*M_Mg*M_O*M_m + M_Fe*M_FeO*M_FeSiO3*M_Mg*M_Si*M_SiO2 + 4*M_Fe*M_FeO*M_FeSiO3*M_Mg*M_Si*M_c - M_Fe*M_FeO*M_FeSiO3*M_Mg*M_Si*M_m - M_Fe*M_FeO*M_FeSiO3*M_Mg*M_SiO2*M_c + M_Fe*M_FeO*M_FeSiO3*M_Mg*M_c*M_m + 9*M_Fe*M_FeO*M_FeSiO3*M_MgO*M_O*M_Si - 4*M_Fe*M_FeO*M_FeSiO3*M_MgO*M_O*M_SiO2 - M_Fe*M_FeO*M_FeSiO3*M_MgO*M_O*M_c + 4*M_Fe*M_FeO*M_FeSiO3*M_MgO*M_O*M_m - M_Fe*M_FeO*M_FeSiO3*M_MgO*M_Si*M_SiO2 - 4*M_Fe*M_FeO*M_FeSiO3*M_MgO*M_Si*M_c + M_Fe*M_FeO*M_FeSiO3*M_MgO*M_Si*M_m + M_Fe*M_FeO*M_FeSiO3*M_MgO*M_SiO2*M_c - M_Fe*M_FeO*M_FeSiO3*M_MgO*M_c*M_m + 9*M_Fe*M_FeO*M_FeSiO3*M_MgSiO3*M_O*M_Si - 4*M_Fe*M_FeO*M_FeSiO3*M_MgSiO3*M_O*M_SiO2 - M_Fe*M_FeO*M_FeSiO3*M_MgSiO3*M_O*M_c + 4*M_Fe*M_FeO*M_FeSiO3*M_MgSiO3*M_O*M_m - M_Fe*M_FeO*M_FeSiO3*M_MgSiO3*M_Si*M_SiO2 - 4*M_Fe*M_FeO*M_FeSiO3*M_MgSiO3*M_Si*M_c + M_Fe*M_FeO*M_FeSiO3*M_MgSiO3*M_Si*M_m + M_Fe*M_FeO*M_FeSiO3*M_MgSiO3*M_SiO2*M_c - M_Fe*M_FeO*M_FeSiO3*M_MgSiO3*M_c*M_m - 9*M_Fe*M_FeO*M_Mg*M_MgSiO3*M_O*M_Si + 4*M_Fe*M_FeO*M_Mg*M_MgSiO3*M_O*M_SiO2 + M_Fe*M_FeO*M_Mg*M_MgSiO3*M_O*M_c - 4*M_Fe*M_FeO*M_Mg*M_MgSiO3*M_O*M_m + M_Fe*M_FeO*M_Mg*M_MgSiO3*M_Si*M_SiO2 + 4*M_Fe*M_FeO*M_Mg*M_MgSiO3*M_Si*M_c - M_Fe*M_FeO*M_Mg*M_MgSiO3*M_Si*M_m - M_Fe*M_FeO*M_Mg*M_MgSiO3*M_SiO2*M_c + M_Fe*M_FeO*M_Mg*M_MgSiO3*M_c*M_m - M_Fe*M_FeO*M_Mg*M_O*M_Si*M_SiO2 - M_Fe*M_FeO*M_Mg*M_O*M_Si*M_c + 4*M_Fe*M_FeO*M_Mg*M_O*M_Si*M_m + M_Fe*M_FeO*M_Mg*M_O*M_SiO2*M_c - 4*M_Fe*M_FeO*M_Mg*M_O*M_SiO2*M_m + M_Fe*M_FeO*M_Mg*M_Si*M_SiO2*M_c - M_Fe*M_FeO*M_Mg*M_Si*M_SiO2*M_m - M_Fe*M_FeO*M_Mg*M_Si*M_c*M_m + M_Fe*M_FeO*M_Mg*M_SiO2*M_c*M_m + 9*M_Fe*M_FeO*M_MgO*M_MgSiO3*M_O*M_Si - 4*M_Fe*M_FeO*M_MgO*M_MgSiO3*M_O*M_SiO2 - M_Fe*M_FeO*M_MgO*M_MgSiO3*M_O*M_c + 4*M_Fe*M_FeO*M_MgO*M_MgSiO3*M_O*M_m - M_Fe*M_FeO*M_MgO*M_MgSiO3*M_Si*M_SiO2 - 4*M_Fe*M_FeO*M_MgO*M_MgSiO3*M_Si*M_c + M_Fe*M_FeO*M_MgO*M_MgSiO3*M_Si*M_m + M_Fe*M_FeO*M_MgO*M_MgSiO3*M_SiO2*M_c - M_Fe*M_FeO*M_MgO*M_MgSiO3*M_c*M_m + M_Fe*M_FeO*M_MgO*M_O*M_Si*M_SiO2 + M_Fe*M_FeO*M_MgO*M_O*M_Si*M_c - 4*M_Fe*M_FeO*M_MgO*M_O*M_Si*M_m - M_Fe*M_FeO*M_MgO*M_O*M_SiO2*M_c + 4*M_Fe*M_FeO*M_MgO*M_O*M_SiO2*M_m - M_Fe*M_FeO*M_MgO*M_Si*M_SiO2*M_c + M_Fe*M_FeO*M_MgO*M_Si*M_SiO2*M_m + M_Fe*M_FeO*M_MgO*M_Si*M_c*M_m - M_Fe*M_FeO*M_MgO*M_SiO2*M_c*M_m + M_Fe*M_FeO*M_MgSiO3*M_O*M_Si*M_SiO2 + M_Fe*M_FeO*M_MgSiO3*M_O*M_Si*M_c - 4*M_Fe*M_FeO*M_MgSiO3*M_O*M_Si*M_m - M_Fe*M_FeO*M_MgSiO3*M_O*M_SiO2*M_c + 4*M_Fe*M_FeO*M_MgSiO3*M_O*M_SiO2*M_m - M_Fe*M_FeO*M_MgSiO3*M_Si*M_SiO2*M_c + M_Fe*M_FeO*M_MgSiO3*M_Si*M_SiO2*M_m + M_Fe*M_FeO*M_MgSiO3*M_Si*M_c*M_m - M_Fe*M_FeO*M_MgSiO3*M_SiO2*M_c*M_m - 9*M_Fe*M_FeSiO3*M_Mg*M_MgO*M_O*M_Si + 4*M_Fe*M_FeSiO3*M_Mg*M_MgO*M_O*M_SiO2 + M_Fe*M_FeSiO3*M_Mg*M_MgO*M_O*M_c - 4*M_Fe*M_FeSiO3*M_Mg*M_MgO*M_O*M_m + M_Fe*M_FeSiO3*M_Mg*M_MgO*M_Si*M_SiO2 + 4*M_Fe*M_FeSiO3*M_Mg*M_MgO*M_Si*M_c - M_Fe*M_FeSiO3*M_Mg*M_MgO*M_Si*M_m - M_Fe*M_FeSiO3*M_Mg*M_MgO*M_SiO2*M_c + M_Fe*M_FeSiO3*M_Mg*M_MgO*M_c*M_m - 4*M_Fe*M_FeSiO3*M_Mg*M_O*M_Si*M_SiO2 - M_Fe*M_FeSiO3*M_Mg*M_O*M_Si*M_c + 25*M_Fe*M_FeSiO3*M_Mg*M_O*M_Si*M_m - 4*M_Fe*M_FeSiO3*M_Mg*M_O*M_SiO2*M_m - M_Fe*M_FeSiO3*M_Mg*M_O*M_c*M_m + M_Fe*M_FeSiO3*M_Mg*M_Si*M_SiO2*M_c - M_Fe*M_FeSiO3*M_Mg*M_Si*M_SiO2*M_m - 9*M_Fe*M_FeSiO3*M_Mg*M_Si*M_c*M_m + M_Fe*M_FeSiO3*M_Mg*M_SiO2*M_c*M_m + 9*M_Fe*M_FeSiO3*M_MgO*M_MgSiO3*M_O*M_Si - 4*M_Fe*M_FeSiO3*M_MgO*M_MgSiO3*M_O*M_SiO2 - M_Fe*M_FeSiO3*M_MgO*M_MgSiO3*M_O*M_c + 4*M_Fe*M_FeSiO3*M_MgO*M_MgSiO3*M_O*M_m - M_Fe*M_FeSiO3*M_MgO*M_MgSiO3*M_Si*M_SiO2 - 4*M_Fe*M_FeSiO3*M_MgO*M_MgSiO3*M_Si*M_c + M_Fe*M_FeSiO3*M_MgO*M_MgSiO3*M_Si*M_m + M_Fe*M_FeSiO3*M_MgO*M_MgSiO3*M_SiO2*M_c - M_Fe*M_FeSiO3*M_MgO*M_MgSiO3*M_c*M_m + 4*M_Fe*M_FeSiO3*M_MgO*M_O*M_Si*M_SiO2 + M_Fe*M_FeSiO3*M_MgO*M_O*M_Si*M_c - 25*M_Fe*M_FeSiO3*M_MgO*M_O*M_Si*M_m + 4*M_Fe*M_FeSiO3*M_MgO*M_O*M_SiO2*M_m + M_Fe*M_FeSiO3*M_MgO*M_O*M_c*M_m - M_Fe*M_FeSiO3*M_MgO*M_Si*M_SiO2*M_c + M_Fe*M_FeSiO3*M_MgO*M_Si*M_SiO2*M_m + 9*M_Fe*M_FeSiO3*M_MgO*M_Si*M_c*M_m - M_Fe*M_FeSiO3*M_MgO*M_SiO2*M_c*M_m + 4*M_Fe*M_FeSiO3*M_MgSiO3*M_O*M_Si*M_SiO2 + M_Fe*M_FeSiO3*M_MgSiO3*M_O*M_Si*M_c - 25*M_Fe*M_FeSiO3*M_MgSiO3*M_O*M_Si*M_m + 4*M_Fe*M_FeSiO3*M_MgSiO3*M_O*M_SiO2*M_m + M_Fe*M_FeSiO3*M_MgSiO3*M_O*M_c*M_m - M_Fe*M_FeSiO3*M_MgSiO3*M_Si*M_SiO2*M_c + M_Fe*M_FeSiO3*M_MgSiO3*M_Si*M_SiO2*M_m + 9*M_Fe*M_FeSiO3*M_MgSiO3*M_Si*M_c*M_m - M_Fe*M_FeSiO3*M_MgSiO3*M_SiO2*M_c*M_m - 9*M_Fe*M_Mg*M_MgO*M_MgSiO3*M_O*M_Si + 4*M_Fe*M_Mg*M_MgO*M_MgSiO3*M_O*M_SiO2 + M_Fe*M_Mg*M_MgO*M_MgSiO3*M_O*M_c - 4*M_Fe*M_Mg*M_MgO*M_MgSiO3*M_O*M_m + M_Fe*M_Mg*M_MgO*M_MgSiO3*M_Si*M_SiO2 + 4*M_Fe*M_Mg*M_MgO*M_MgSiO3*M_Si*M_c - M_Fe*M_Mg*M_MgO*M_MgSiO3*M_Si*M_m - M_Fe*M_Mg*M_MgO*M_MgSiO3*M_SiO2*M_c + M_Fe*M_Mg*M_MgO*M_MgSiO3*M_c*M_m - M_Fe*M_Mg*M_MgO*M_O*M_Si*M_SiO2 - M_Fe*M_Mg*M_MgO*M_O*M_Si*M_c + 4*M_Fe*M_Mg*M_MgO*M_O*M_Si*M_m + M_Fe*M_Mg*M_MgO*M_O*M_SiO2*M_c - 4*M_Fe*M_Mg*M_MgO*M_O*M_SiO2*M_m + M_Fe*M_Mg*M_MgO*M_Si*M_SiO2*M_c - M_Fe*M_Mg*M_MgO*M_Si*M_SiO2*M_m - M_Fe*M_Mg*M_MgO*M_Si*M_c*M_m + M_Fe*M_Mg*M_MgO*M_SiO2*M_c*M_m - 4*M_Fe*M_Mg*M_MgSiO3*M_O*M_Si*M_SiO2 - M_Fe*M_Mg*M_MgSiO3*M_O*M_Si*M_c + 25*M_Fe*M_Mg*M_MgSiO3*M_O*M_Si*M_m - 4*M_Fe*M_Mg*M_MgSiO3*M_O*M_SiO2*M_m - M_Fe*M_Mg*M_MgSiO3*M_O*M_c*M_m + M_Fe*M_Mg*M_MgSiO3*M_Si*M_SiO2*M_c - M_Fe*M_Mg*M_MgSiO3*M_Si*M_SiO2*M_m - 9*M_Fe*M_Mg*M_MgSiO3*M_Si*M_c*M_m + M_Fe*M_Mg*M_MgSiO3*M_SiO2*M_c*M_m - M_Fe*M_Mg*M_O*M_Si*M_SiO2*M_c + 9*M_Fe*M_Mg*M_O*M_Si*M_SiO2*M_m + M_Fe*M_Mg*M_O*M_Si*M_c*M_m - M_Fe*M_Mg*M_O*M_SiO2*M_c*M_m - 4*M_Fe*M_Mg*M_Si*M_SiO2*M_c*M_m + 9*M_Fe*M_MgO*M_MgSiO3*M_O*M_Si*M_SiO2 - 9*M_Fe*M_MgO*M_MgSiO3*M_O*M_Si*M_m - M_Fe*M_MgO*M_MgSiO3*M_O*M_SiO2*M_c + M_Fe*M_MgO*M_MgSiO3*M_O*M_c*M_m - 4*M_Fe*M_MgO*M_MgSiO3*M_Si*M_SiO2*M_c + 4*M_Fe*M_MgO*M_MgSiO3*M_Si*M_c*M_m + M_Fe*M_MgO*M_O*M_Si*M_SiO2*M_c - 9*M_Fe*M_MgO*M_O*M_Si*M_SiO2*M_m - M_Fe*M_MgO*M_O*M_Si*M_c*M_m + M_Fe*M_MgO*M_O*M_SiO2*M_c*M_m + 4*M_Fe*M_MgO*M_Si*M_SiO2*M_c*M_m + M_Fe*M_MgSiO3*M_O*M_Si*M_SiO2*M_c - 9*M_Fe*M_MgSiO3*M_O*M_Si*M_SiO2*M_m - M_Fe*M_MgSiO3*M_O*M_Si*M_c*M_m + M_Fe*M_MgSiO3*M_O*M_SiO2*M_c*M_m + 4*M_Fe*M_MgSiO3*M_Si*M_SiO2*M_c*M_m + 9*M_FeO*M_FeSiO3*M_Mg*M_MgO*M_O*M_Si - 4*M_FeO*M_FeSiO3*M_Mg*M_MgO*M_O*M_SiO2 - M_FeO*M_FeSiO3*M_Mg*M_MgO*M_O*M_c + 4*M_FeO*M_FeSiO3*M_Mg*M_MgO*M_O*M_m - M_FeO*M_FeSiO3*M_Mg*M_MgO*M_Si*M_SiO2 - 4*M_FeO*M_FeSiO3*M_Mg*M_MgO*M_Si*M_c + M_FeO*M_FeSiO3*M_Mg*M_MgO*M_Si*M_m + M_FeO*M_FeSiO3*M_Mg*M_MgO*M_SiO2*M_c - M_FeO*M_FeSiO3*M_Mg*M_MgO*M_c*M_m + 9*M_FeO*M_FeSiO3*M_Mg*M_MgSiO3*M_O*M_Si - 4*M_FeO*M_FeSiO3*M_Mg*M_MgSiO3*M_O*M_SiO2 - M_FeO*M_FeSiO3*M_Mg*M_MgSiO3*M_O*M_c + 4*M_FeO*M_FeSiO3*M_Mg*M_MgSiO3*M_O*M_m - M_FeO*M_FeSiO3*M_Mg*M_MgSiO3*M_Si*M_SiO2 - 4*M_FeO*M_FeSiO3*M_Mg*M_MgSiO3*M_Si*M_c + M_FeO*M_FeSiO3*M_Mg*M_MgSiO3*M_Si*M_m + M_FeO*M_FeSiO3*M_Mg*M_MgSiO3*M_SiO2*M_c - M_FeO*M_FeSiO3*M_Mg*M_MgSiO3*M_c*M_m + 9*M_FeO*M_FeSiO3*M_Mg*M_O*M_Si*M_SiO2 - 9*M_FeO*M_FeSiO3*M_Mg*M_O*M_Si*M_m - M_FeO*M_FeSiO3*M_Mg*M_O*M_SiO2*M_c + M_FeO*M_FeSiO3*M_Mg*M_O*M_c*M_m - 4*M_FeO*M_FeSiO3*M_Mg*M_Si*M_SiO2*M_c + 4*M_FeO*M_FeSiO3*M_Mg*M_Si*M_c*M_m - 9*M_FeO*M_FeSiO3*M_MgO*M_O*M_Si*M_SiO2 + 9*M_FeO*M_FeSiO3*M_MgO*M_O*M_Si*M_m + M_FeO*M_FeSiO3*M_MgO*M_O*M_SiO2*M_c - M_FeO*M_FeSiO3*M_MgO*M_O*M_c*M_m + 4*M_FeO*M_FeSiO3*M_MgO*M_Si*M_SiO2*M_c - 4*M_FeO*M_FeSiO3*M_MgO*M_Si*M_c*M_m - 9*M_FeO*M_FeSiO3*M_MgSiO3*M_O*M_Si*M_SiO2 + 9*M_FeO*M_FeSiO3*M_MgSiO3*M_O*M_Si*M_m + M_FeO*M_FeSiO3*M_MgSiO3*M_O*M_SiO2*M_c - M_FeO*M_FeSiO3*M_MgSiO3*M_O*M_c*M_m + 4*M_FeO*M_FeSiO3*M_MgSiO3*M_Si*M_SiO2*M_c - 4*M_FeO*M_FeSiO3*M_MgSiO3*M_Si*M_c*M_m + 9*M_FeO*M_Mg*M_MgO*M_MgSiO3*M_O*M_Si - 4*M_FeO*M_Mg*M_MgO*M_MgSiO3*M_O*M_SiO2 - M_FeO*M_Mg*M_MgO*M_MgSiO3*M_O*M_c + 4*M_FeO*M_Mg*M_MgO*M_MgSiO3*M_O*M_m - M_FeO*M_Mg*M_MgO*M_MgSiO3*M_Si*M_SiO2 - 4*M_FeO*M_Mg*M_MgO*M_MgSiO3*M_Si*M_c + M_FeO*M_Mg*M_MgO*M_MgSiO3*M_Si*M_m + M_FeO*M_Mg*M_MgO*M_MgSiO3*M_SiO2*M_c - M_FeO*M_Mg*M_MgO*M_MgSiO3*M_c*M_m + M_FeO*M_Mg*M_MgO*M_O*M_Si*M_SiO2 + M_FeO*M_Mg*M_MgO*M_O*M_Si*M_c - 4*M_FeO*M_Mg*M_MgO*M_O*M_Si*M_m - M_FeO*M_Mg*M_MgO*M_O*M_SiO2*M_c + 4*M_FeO*M_Mg*M_MgO*M_O*M_SiO2*M_m - M_FeO*M_Mg*M_MgO*M_Si*M_SiO2*M_c + M_FeO*M_Mg*M_MgO*M_Si*M_SiO2*M_m + M_FeO*M_Mg*M_MgO*M_Si*M_c*M_m - M_FeO*M_Mg*M_MgO*M_SiO2*M_c*M_m + 4*M_FeO*M_Mg*M_MgSiO3*M_O*M_Si*M_SiO2 + M_FeO*M_Mg*M_MgSiO3*M_O*M_Si*M_c - 25*M_FeO*M_Mg*M_MgSiO3*M_O*M_Si*M_m + 4*M_FeO*M_Mg*M_MgSiO3*M_O*M_SiO2*M_m + M_FeO*M_Mg*M_MgSiO3*M_O*M_c*M_m - M_FeO*M_Mg*M_MgSiO3*M_Si*M_SiO2*M_c + M_FeO*M_Mg*M_MgSiO3*M_Si*M_SiO2*M_m + 9*M_FeO*M_Mg*M_MgSiO3*M_Si*M_c*M_m - M_FeO*M_Mg*M_MgSiO3*M_SiO2*M_c*M_m + M_FeO*M_Mg*M_O*M_Si*M_SiO2*M_c - 9*M_FeO*M_Mg*M_O*M_Si*M_SiO2*M_m - M_FeO*M_Mg*M_O*M_Si*M_c*M_m + M_FeO*M_Mg*M_O*M_SiO2*M_c*M_m + 4*M_FeO*M_Mg*M_Si*M_SiO2*M_c*M_m - 9*M_FeO*M_MgO*M_MgSiO3*M_O*M_Si*M_SiO2 + 9*M_FeO*M_MgO*M_MgSiO3*M_O*M_Si*M_m + M_FeO*M_MgO*M_MgSiO3*M_O*M_SiO2*M_c - M_FeO*M_MgO*M_MgSiO3*M_O*M_c*M_m + 4*M_FeO*M_MgO*M_MgSiO3*M_Si*M_SiO2*M_c - 4*M_FeO*M_MgO*M_MgSiO3*M_Si*M_c*M_m - M_FeO*M_MgO*M_O*M_Si*M_SiO2*M_c + 9*M_FeO*M_MgO*M_O*M_Si*M_SiO2*M_m + M_FeO*M_MgO*M_O*M_Si*M_c*M_m - M_FeO*M_MgO*M_O*M_SiO2*M_c*M_m - 4*M_FeO*M_MgO*M_Si*M_SiO2*M_c*M_m - M_FeO*M_MgSiO3*M_O*M_Si*M_SiO2*M_c + 9*M_FeO*M_MgSiO3*M_O*M_Si*M_SiO2*M_m + M_FeO*M_MgSiO3*M_O*M_Si*M_c*M_m - M_FeO*M_MgSiO3*M_O*M_SiO2*M_c*M_m - 4*M_FeO*M_MgSiO3*M_Si*M_SiO2*M_c*M_m + 9*M_FeSiO3*M_Mg*M_MgO*M_MgSiO3*M_O*M_Si - 4*M_FeSiO3*M_Mg*M_MgO*M_MgSiO3*M_O*M_SiO2 - M_FeSiO3*M_Mg*M_MgO*M_MgSiO3*M_O*M_c + 4*M_FeSiO3*M_Mg*M_MgO*M_MgSiO3*M_O*M_m - M_FeSiO3*M_Mg*M_MgO*M_MgSiO3*M_Si*M_SiO2 - 4*M_FeSiO3*M_Mg*M_MgO*M_MgSiO3*M_Si*M_c + M_FeSiO3*M_Mg*M_MgO*M_MgSiO3*M_Si*M_m + M_FeSiO3*M_Mg*M_MgO*M_MgSiO3*M_SiO2*M_c - M_FeSiO3*M_Mg*M_MgO*M_MgSiO3*M_c*M_m + M_FeSiO3*M_Mg*M_MgO*M_O*M_Si*M_SiO2 + M_FeSiO3*M_Mg*M_MgO*M_O*M_Si*M_c - 4*M_FeSiO3*M_Mg*M_MgO*M_O*M_Si*M_m - M_FeSiO3*M_Mg*M_MgO*M_O*M_SiO2*M_c + 4*M_FeSiO3*M_Mg*M_MgO*M_O*M_SiO2*M_m - M_FeSiO3*M_Mg*M_MgO*M_Si*M_SiO2*M_c + M_FeSiO3*M_Mg*M_MgO*M_Si*M_SiO2*M_m + M_FeSiO3*M_Mg*M_MgO*M_Si*M_c*M_m - M_FeSiO3*M_Mg*M_MgO*M_SiO2*M_c*M_m + 4*M_FeSiO3*M_Mg*M_MgSiO3*M_O*M_Si*M_SiO2 + M_FeSiO3*M_Mg*M_MgSiO3*M_O*M_Si*M_c - 25*M_FeSiO3*M_Mg*M_MgSiO3*M_O*M_Si*M_m + 4*M_FeSiO3*M_Mg*M_MgSiO3*M_O*M_SiO2*M_m + M_FeSiO3*M_Mg*M_MgSiO3*M_O*M_c*M_m - M_FeSiO3*M_Mg*M_MgSiO3*M_Si*M_SiO2*M_c + M_FeSiO3*M_Mg*M_MgSiO3*M_Si*M_SiO2*M_m + 9*M_FeSiO3*M_Mg*M_MgSiO3*M_Si*M_c*M_m - M_FeSiO3*M_Mg*M_MgSiO3*M_SiO2*M_c*M_m + M_FeSiO3*M_Mg*M_O*M_Si*M_SiO2*M_c - 9*M_FeSiO3*M_Mg*M_O*M_Si*M_SiO2*M_m - M_FeSiO3*M_Mg*M_O*M_Si*M_c*M_m + M_FeSiO3*M_Mg*M_O*M_SiO2*M_c*M_m + 4*M_FeSiO3*M_Mg*M_Si*M_SiO2*M_c*M_m - 9*M_FeSiO3*M_MgO*M_MgSiO3*M_O*M_Si*M_SiO2 + 9*M_FeSiO3*M_MgO*M_MgSiO3*M_O*M_Si*M_m + M_FeSiO3*M_MgO*M_MgSiO3*M_O*M_SiO2*M_c - M_FeSiO3*M_MgO*M_MgSiO3*M_O*M_c*M_m + 4*M_FeSiO3*M_MgO*M_MgSiO3*M_Si*M_SiO2*M_c - 4*M_FeSiO3*M_MgO*M_MgSiO3*M_Si*M_c*M_m - M_FeSiO3*M_MgO*M_O*M_Si*M_SiO2*M_c + 9*M_FeSiO3*M_MgO*M_O*M_Si*M_SiO2*M_m + M_FeSiO3*M_MgO*M_O*M_Si*M_c*M_m - M_FeSiO3*M_MgO*M_O*M_SiO2*M_c*M_m - 4*M_FeSiO3*M_MgO*M_Si*M_SiO2*M_c*M_m - M_FeSiO3*M_MgSiO3*M_O*M_Si*M_SiO2*M_c + 9*M_FeSiO3*M_MgSiO3*M_O*M_Si*M_SiO2*M_m + M_FeSiO3*M_MgSiO3*M_O*M_Si*M_c*M_m - M_FeSiO3*M_MgSiO3*M_O*M_SiO2*M_c*M_m - 4*M_FeSiO3*M_MgSiO3*M_Si*M_SiO2*M_c*M_m)
sol_dM_c_numerator = M_c*(-M_Fe*dKFeO_KFeO*(2*M_FeO*M_FeSiO3*M_Mg*M_O*M_SiO2 - 2*M_FeO*M_FeSiO3*M_Mg*M_O*M_m + 2*M_FeO*M_FeSiO3*M_Mg*M_Si*M_SiO2 - 2*M_FeO*M_FeSiO3*M_Mg*M_Si*M_m - 2*M_FeO*M_FeSiO3*M_MgO*M_O*M_SiO2 + 2*M_FeO*M_FeSiO3*M_MgO*M_O*M_m - 2*M_FeO*M_FeSiO3*M_MgO*M_Si*M_SiO2 + 2*M_FeO*M_FeSiO3*M_MgO*M_Si*M_m - 2*M_FeO*M_FeSiO3*M_MgSiO3*M_O*M_SiO2 + 2*M_FeO*M_FeSiO3*M_MgSiO3*M_O*M_m - 2*M_FeO*M_FeSiO3*M_MgSiO3*M_Si*M_SiO2 + 2*M_FeO*M_FeSiO3*M_MgSiO3*M_Si*M_m + 3*M_FeO*M_Mg*M_MgSiO3*M_O*M_Si - 2*M_FeO*M_Mg*M_MgSiO3*M_O*M_m + M_FeO*M_Mg*M_MgSiO3*M_Si*M_SiO2 - 3*M_FeO*M_Mg*M_MgSiO3*M_Si*M_m + M_FeO*M_Mg*M_O*M_Si*M_SiO2 + 2*M_FeO*M_Mg*M_O*M_Si*M_m - 2*M_FeO*M_Mg*M_O*M_SiO2*M_m - 2*M_FeO*M_Mg*M_Si*M_SiO2*M_m - 2*M_FeO*M_MgO*M_MgSiO3*M_O*M_SiO2 + 2*M_FeO*M_MgO*M_MgSiO3*M_O*M_m - 2*M_FeO*M_MgO*M_MgSiO3*M_Si*M_SiO2 + 2*M_FeO*M_MgO*M_MgSiO3*M_Si*M_m - M_FeO*M_MgO*M_O*M_Si*M_SiO2 - 2*M_FeO*M_MgO*M_O*M_Si*M_m + 2*M_FeO*M_MgO*M_O*M_SiO2*M_m + 2*M_FeO*M_MgO*M_Si*M_SiO2*M_m - M_FeO*M_MgSiO3*M_O*M_Si*M_SiO2 - 2*M_FeO*M_MgSiO3*M_O*M_Si*M_m + 2*M_FeO*M_MgSiO3*M_O*M_SiO2*M_m + 2*M_FeO*M_MgSiO3*M_Si*M_SiO2*M_m - 3*M_FeSiO3*M_Mg*M_MgO*M_O*M_Si + 2*M_FeSiO3*M_Mg*M_MgO*M_O*M_SiO2 + M_FeSiO3*M_Mg*M_MgO*M_Si*M_SiO2 + M_FeSiO3*M_Mg*M_MgO*M_Si*M_m - 2*M_FeSiO3*M_Mg*M_O*M_Si*M_SiO2 + 5*M_FeSiO3*M_Mg*M_O*M_Si*M_m - 2*M_FeSiO3*M_Mg*M_O*M_SiO2*M_m - 2*M_FeSiO3*M_Mg*M_Si*M_SiO2*M_m - 2*M_FeSiO3*M_MgO*M_MgSiO3*M_O*M_SiO2 + 2*M_FeSiO3*M_MgO*M_MgSiO3*M_O*M_m - 2*M_FeSiO3*M_MgO*M_MgSiO3*M_Si*M_SiO2 + 2*M_FeSiO3*M_MgO*M_MgSiO3*M_Si*M_m + 2*M_FeSiO3*M_MgO*M_O*M_Si*M_SiO2 - 5*M_FeSiO3*M_MgO*M_O*M_Si*M_m + 2*M_FeSiO3*M_MgO*M_O*M_SiO2*M_m + 2*M_FeSiO3*M_MgO*M_Si*M_SiO2*M_m + 2*M_FeSiO3*M_MgSiO3*M_O*M_Si*M_SiO2 - 5*M_FeSiO3*M_MgSiO3*M_O*M_Si*M_m + 2*M_FeSiO3*M_MgSiO3*M_O*M_SiO2*M_m + 2*M_FeSiO3*M_MgSiO3*M_Si*M_SiO2*M_m) + M_FeSiO3*dKFeSiO3_KFeSiO3*(3*M_Fe*M_FeO*M_Mg*M_O*M_Si - 2*M_Fe*M_FeO*M_Mg*M_O*M_SiO2 - M_Fe*M_FeO*M_Mg*M_Si*M_SiO2 - M_Fe*M_FeO*M_Mg*M_Si*M_m - 3*M_Fe*M_FeO*M_MgO*M_O*M_Si + 2*M_Fe*M_FeO*M_MgO*M_O*M_SiO2 + M_Fe*M_FeO*M_MgO*M_Si*M_SiO2 + M_Fe*M_FeO*M_MgO*M_Si*M_m - 3*M_Fe*M_FeO*M_MgSiO3*M_O*M_Si + 2*M_Fe*M_FeO*M_MgSiO3*M_O*M_SiO2 + M_Fe*M_FeO*M_MgSiO3*M_Si*M_SiO2 + M_Fe*M_FeO*M_MgSiO3*M_Si*M_m + 3*M_Fe*M_Mg*M_MgO*M_O*M_Si - 2*M_Fe*M_Mg*M_MgO*M_O*M_SiO2 - M_Fe*M_Mg*M_MgO*M_Si*M_SiO2 - M_Fe*M_Mg*M_MgO*M_Si*M_m + 2*M_Fe*M_Mg*M_O*M_Si*M_SiO2 - 5*M_Fe*M_Mg*M_O*M_Si*M_m + 2*M_Fe*M_Mg*M_O*M_SiO2*M_m + 2*M_Fe*M_Mg*M_Si*M_SiO2*M_m + 2*M_Fe*M_MgO*M_MgSiO3*M_O*M_SiO2 - 2*M_Fe*M_MgO*M_MgSiO3*M_O*M_m + 2*M_Fe*M_MgO*M_MgSiO3*M_Si*M_SiO2 - 2*M_Fe*M_MgO*M_MgSiO3*M_Si*M_m - 2*M_Fe*M_MgO*M_O*M_Si*M_SiO2 + 5*M_Fe*M_MgO*M_O*M_Si*M_m - 2*M_Fe*M_MgO*M_O*M_SiO2*M_m - 2*M_Fe*M_MgO*M_Si*M_SiO2*M_m - 2*M_Fe*M_MgSiO3*M_O*M_Si*M_SiO2 + 5*M_Fe*M_MgSiO3*M_O*M_Si*M_m - 2*M_Fe*M_MgSiO3*M_O*M_SiO2*M_m - 2*M_Fe*M_MgSiO3*M_Si*M_SiO2*M_m - 3*M_FeO*M_Mg*M_MgO*M_O*M_Si + 2*M_FeO*M_Mg*M_MgO*M_O*M_SiO2 + M_FeO*M_Mg*M_MgO*M_Si*M_SiO2 + M_FeO*M_Mg*M_MgO*M_Si*M_m - 3*M_FeO*M_Mg*M_MgSiO3*M_O*M_Si + 2*M_FeO*M_Mg*M_MgSiO3*M_O*M_m - M_FeO*M_Mg*M_MgSiO3*M_Si*M_SiO2 + 3*M_FeO*M_Mg*M_MgSiO3*M_Si*M_m - 3*M_FeO*M_Mg*M_O*M_Si*M_SiO2 + 3*M_FeO*M_Mg*M_O*M_Si*M_m + 3*M_FeO*M_MgO*M_O*M_Si*M_SiO2 - 3*M_FeO*M_MgO*M_O*M_Si*M_m + 3*M_FeO*M_MgSiO3*M_O*M_Si*M_SiO2 - 3*M_FeO*M_MgSiO3*M_O*M_Si*M_m) - M_Mg*dKMgO_KMgO*(-3*M_Fe*M_FeO*M_MgSiO3*M_O*M_Si + 2*M_Fe*M_FeO*M_MgSiO3*M_O*M_SiO2 + M_Fe*M_FeO*M_MgSiO3*M_Si*M_SiO2 + M_Fe*M_FeO*M_MgSiO3*M_Si*M_m + 3*M_Fe*M_FeSiO3*M_MgO*M_O*M_Si - 2*M_Fe*M_FeSiO3*M_MgO*M_O*M_m + M_Fe*M_FeSiO3*M_MgO*M_Si*M_SiO2 - 3*M_Fe*M_FeSiO3*M_MgO*M_Si*M_m + 2*M_Fe*M_MgO*M_MgSiO3*M_O*M_SiO2 - 2*M_Fe*M_MgO*M_MgSiO3*M_O*M_m + 2*M_Fe*M_MgO*M_MgSiO3*M_Si*M_SiO2 - 2*M_Fe*M_MgO*M_MgSiO3*M_Si*M_m + M_Fe*M_MgO*M_O*M_Si*M_SiO2 + 2*M_Fe*M_MgO*M_O*M_Si*M_m - 2*M_Fe*M_MgO*M_O*M_SiO2*M_m - 2*M_Fe*M_MgO*M_Si*M_SiO2*M_m - 2*M_Fe*M_MgSiO3*M_O*M_Si*M_SiO2 + 5*M_Fe*M_MgSiO3*M_O*M_Si*M_m - 2*M_Fe*M_MgSiO3*M_O*M_SiO2*M_m - 2*M_Fe*M_MgSiO3*M_Si*M_SiO2*M_m - 2*M_FeO*M_FeSiO3*M_MgO*M_O*M_SiO2 + 2*M_FeO*M_FeSiO3*M_MgO*M_O*M_m - 2*M_FeO*M_FeSiO3*M_MgO*M_Si*M_SiO2 + 2*M_FeO*M_FeSiO3*M_MgO*M_Si*M_m - 2*M_FeO*M_FeSiO3*M_MgSiO3*M_O*M_SiO2 + 2*M_FeO*M_FeSiO3*M_MgSiO3*M_O*M_m - 2*M_FeO*M_FeSiO3*M_MgSiO3*M_Si*M_SiO2 + 2*M_FeO*M_FeSiO3*M_MgSiO3*M_Si*M_m - 2*M_FeO*M_MgO*M_MgSiO3*M_O*M_SiO2 + 2*M_FeO*M_MgO*M_MgSiO3*M_O*M_m - 2*M_FeO*M_MgO*M_MgSiO3*M_Si*M_SiO2 + 2*M_FeO*M_MgO*M_MgSiO3*M_Si*M_m - M_FeO*M_MgO*M_O*M_Si*M_SiO2 - 2*M_FeO*M_MgO*M_O*M_Si*M_m + 2*M_FeO*M_MgO*M_O*M_SiO2*M_m + 2*M_FeO*M_MgO*M_Si*M_SiO2*M_m + 2*M_FeO*M_MgSiO3*M_O*M_Si*M_SiO2 - 5*M_FeO*M_MgSiO3*M_O*M_Si*M_m + 2*M_FeO*M_MgSiO3*M_O*M_SiO2*M_m + 2*M_FeO*M_MgSiO3*M_Si*M_SiO2*M_m - 2*M_FeSiO3*M_MgO*M_MgSiO3*M_O*M_SiO2 + 2*M_FeSiO3*M_MgO*M_MgSiO3*M_O*M_m - 2*M_FeSiO3*M_MgO*M_MgSiO3*M_Si*M_SiO2 + 2*M_FeSiO3*M_MgO*M_MgSiO3*M_Si*M_m - M_FeSiO3*M_MgO*M_O*M_Si*M_SiO2 - 2*M_FeSiO3*M_MgO*M_O*M_Si*M_m + 2*M_FeSiO3*M_MgO*M_O*M_SiO2*M_m + 2*M_FeSiO3*M_MgO*M_Si*M_SiO2*M_m + 2*M_FeSiO3*M_MgSiO3*M_O*M_Si*M_SiO2 - 5*M_FeSiO3*M_MgSiO3*M_O*M_Si*M_m + 2*M_FeSiO3*M_MgSiO3*M_O*M_SiO2*M_m + 2*M_FeSiO3*M_MgSiO3*M_Si*M_SiO2*M_m) + M_MgSiO3*dKMgSiO3_KMgSiO3*(3*M_Fe*M_FeO*M_Mg*M_O*M_Si - 2*M_Fe*M_FeO*M_Mg*M_O*M_SiO2 - M_Fe*M_FeO*M_Mg*M_Si*M_SiO2 - M_Fe*M_FeO*M_Mg*M_Si*M_m - 3*M_Fe*M_FeO*M_MgO*M_O*M_Si + 2*M_Fe*M_FeO*M_MgO*M_O*M_SiO2 + M_Fe*M_FeO*M_MgO*M_Si*M_SiO2 + M_Fe*M_FeO*M_MgO*M_Si*M_m - 3*M_Fe*M_FeSiO3*M_MgO*M_O*M_Si + 2*M_Fe*M_FeSiO3*M_MgO*M_O*M_m - M_Fe*M_FeSiO3*M_MgO*M_Si*M_SiO2 + 3*M_Fe*M_FeSiO3*M_MgO*M_Si*M_m + 3*M_Fe*M_Mg*M_MgO*M_O*M_Si - 2*M_Fe*M_Mg*M_MgO*M_O*M_SiO2 - M_Fe*M_Mg*M_MgO*M_Si*M_SiO2 - M_Fe*M_Mg*M_MgO*M_Si*M_m + 2*M_Fe*M_Mg*M_O*M_Si*M_SiO2 - 5*M_Fe*M_Mg*M_O*M_Si*M_m + 2*M_Fe*M_Mg*M_O*M_SiO2*M_m + 2*M_Fe*M_Mg*M_Si*M_SiO2*M_m - 3*M_Fe*M_MgO*M_O*M_Si*M_SiO2 + 3*M_Fe*M_MgO*M_O*M_Si*M_m + 2*M_FeO*M_FeSiO3*M_Mg*M_O*M_SiO2 - 2*M_FeO*M_FeSiO3*M_Mg*M_O*M_m + 2*M_FeO*M_FeSiO3*M_Mg*M_Si*M_SiO2 - 2*M_FeO*M_FeSiO3*M_Mg*M_Si*M_m - 3*M_FeO*M_Mg*M_MgO*M_O*M_Si + 2*M_FeO*M_Mg*M_MgO*M_O*M_SiO2 + M_FeO*M_Mg*M_MgO*M_Si*M_SiO2 + M_FeO*M_Mg*M_MgO*M_Si*M_m - 2*M_FeO*M_Mg*M_O*M_Si*M_SiO2 + 5*M_FeO*M_Mg*M_O*M_Si*M_m - 2*M_FeO*M_Mg*M_O*M_SiO2*M_m - 2*M_FeO*M_Mg*M_Si*M_SiO2*M_m + 3*M_FeO*M_MgO*M_O*M_Si*M_SiO2 - 3*M_FeO*M_MgO*M_O*M_Si*M_m - 3*M_FeSiO3*M_Mg*M_MgO*M_O*M_Si + 2*M_FeSiO3*M_Mg*M_MgO*M_O*M_SiO2 + M_FeSiO3*M_Mg*M_MgO*M_Si*M_SiO2 + M_FeSiO3*M_Mg*M_MgO*M_Si*M_m - 2*M_FeSiO3*M_Mg*M_O*M_Si*M_SiO2 + 5*M_FeSiO3*M_Mg*M_O*M_Si*M_m - 2*M_FeSiO3*M_Mg*M_O*M_SiO2*M_m - 2*M_FeSiO3*M_Mg*M_Si*M_SiO2*M_m + 3*M_FeSiO3*M_MgO*M_O*M_Si*M_SiO2 - 3*M_FeSiO3*M_MgO*M_O*M_Si*M_m) - M_Si*dKSiO2_KSiO2*(-3*M_Fe*M_FeO*M_FeSiO3*M_Mg*M_O - M_Fe*M_FeO*M_FeSiO3*M_Mg*M_SiO2 + M_Fe*M_FeO*M_FeSiO3*M_Mg*M_m + 3*M_Fe*M_FeO*M_FeSiO3*M_MgO*M_O + M_Fe*M_FeO*M_FeSiO3*M_MgO*M_SiO2 - M_Fe*M_FeO*M_FeSiO3*M_MgO*M_m + 3*M_Fe*M_FeO*M_FeSiO3*M_MgSiO3*M_O + M_Fe*M_FeO*M_FeSiO3*M_MgSiO3*M_SiO2 - M_Fe*M_FeO*M_FeSiO3*M_MgSiO3*M_m - 3*M_Fe*M_FeO*M_Mg*M_MgSiO3*M_O - M_Fe*M_FeO*M_Mg*M_MgSiO3*M_SiO2 + M_Fe*M_FeO*M_Mg*M_MgSiO3*M_m - M_Fe*M_FeO*M_Mg*M_O*M_SiO2 + M_Fe*M_FeO*M_Mg*M_SiO2*M_m + 3*M_Fe*M_FeO*M_MgO*M_MgSiO3*M_O + M_Fe*M_FeO*M_MgO*M_MgSiO3*M_SiO2 - M_Fe*M_FeO*M_MgO*M_MgSiO3*M_m + M_Fe*M_FeO*M_MgO*M_O*M_SiO2 - M_Fe*M_FeO*M_MgO*M_SiO2*M_m + M_Fe*M_FeO*M_MgSiO3*M_O*M_SiO2 - M_Fe*M_FeO*M_MgSiO3*M_SiO2*M_m - 3*M_Fe*M_FeSiO3*M_Mg*M_MgO*M_O - M_Fe*M_FeSiO3*M_Mg*M_MgO*M_SiO2 + M_Fe*M_FeSiO3*M_Mg*M_MgO*M_m + 5*M_Fe*M_FeSiO3*M_Mg*M_O*M_m + M_Fe*M_FeSiO3*M_Mg*M_SiO2*M_m + 3*M_Fe*M_FeSiO3*M_MgO*M_MgSiO3*M_O + M_Fe*M_FeSiO3*M_MgO*M_MgSiO3*M_SiO2 - M_Fe*M_FeSiO3*M_MgO*M_MgSiO3*M_m - 5*M_Fe*M_FeSiO3*M_MgO*M_O*M_m - M_Fe*M_FeSiO3*M_MgO*M_SiO2*M_m - 5*M_Fe*M_FeSiO3*M_MgSiO3*M_O*M_m - M_Fe*M_FeSiO3*M_MgSiO3*M_SiO2*M_m - 3*M_Fe*M_Mg*M_MgO*M_MgSiO3*M_O - M_Fe*M_Mg*M_MgO*M_MgSiO3*M_SiO2 + M_Fe*M_Mg*M_MgO*M_MgSiO3*M_m - M_Fe*M_Mg*M_MgO*M_O*M_SiO2 + M_Fe*M_Mg*M_MgO*M_SiO2*M_m + 5*M_Fe*M_Mg*M_MgSiO3*M_O*M_m + M_Fe*M_Mg*M_MgSiO3*M_SiO2*M_m + 3*M_Fe*M_Mg*M_O*M_SiO2*M_m + 3*M_Fe*M_MgO*M_MgSiO3*M_O*M_SiO2 - 3*M_Fe*M_MgO*M_MgSiO3*M_O*M_m - 3*M_Fe*M_MgO*M_O*M_SiO2*M_m - 3*M_Fe*M_MgSiO3*M_O*M_SiO2*M_m + 3*M_FeO*M_FeSiO3*M_Mg*M_MgO*M_O + M_FeO*M_FeSiO3*M_Mg*M_MgO*M_SiO2 - M_FeO*M_FeSiO3*M_Mg*M_MgO*M_m + 3*M_FeO*M_FeSiO3*M_Mg*M_MgSiO3*M_O + M_FeO*M_FeSiO3*M_Mg*M_MgSiO3*M_SiO2 - M_FeO*M_FeSiO3*M_Mg*M_MgSiO3*M_m + 3*M_FeO*M_FeSiO3*M_Mg*M_O*M_SiO2 - 3*M_FeO*M_FeSiO3*M_Mg*M_O*M_m - 3*M_FeO*M_FeSiO3*M_MgO*M_O*M_SiO2 + 3*M_FeO*M_FeSiO3*M_MgO*M_O*M_m - 3*M_FeO*M_FeSiO3*M_MgSiO3*M_O*M_SiO2 + 3*M_FeO*M_FeSiO3*M_MgSiO3*M_O*M_m + 3*M_FeO*M_Mg*M_MgO*M_MgSiO3*M_O + M_FeO*M_Mg*M_MgO*M_MgSiO3*M_SiO2 - M_FeO*M_Mg*M_MgO*M_MgSiO3*M_m + M_FeO*M_Mg*M_MgO*M_O*M_SiO2 - M_FeO*M_Mg*M_MgO*M_SiO2*M_m - 5*M_FeO*M_Mg*M_MgSiO3*M_O*M_m - M_FeO*M_Mg*M_MgSiO3*M_SiO2*M_m - 3*M_FeO*M_Mg*M_O*M_SiO2*M_m - 3*M_FeO*M_MgO*M_MgSiO3*M_O*M_SiO2 + 3*M_FeO*M_MgO*M_MgSiO3*M_O*M_m + 3*M_FeO*M_MgO*M_O*M_SiO2*M_m + 3*M_FeO*M_MgSiO3*M_O*M_SiO2*M_m + 3*M_FeSiO3*M_Mg*M_MgO*M_MgSiO3*M_O + M_FeSiO3*M_Mg*M_MgO*M_MgSiO3*M_SiO2 - M_FeSiO3*M_Mg*M_MgO*M_MgSiO3*M_m + M_FeSiO3*M_Mg*M_MgO*M_O*M_SiO2 - M_FeSiO3*M_Mg*M_MgO*M_SiO2*M_m - 5*M_FeSiO3*M_Mg*M_MgSiO3*M_O*M_m - M_FeSiO3*M_Mg*M_MgSiO3*M_SiO2*M_m - 3*M_FeSiO3*M_Mg*M_O*M_SiO2*M_m - 3*M_FeSiO3*M_MgO*M_MgSiO3*M_O*M_SiO2 + 3*M_FeSiO3*M_MgO*M_MgSiO3*M_O*M_m + 3*M_FeSiO3*M_MgO*M_O*M_SiO2*M_m + 3*M_FeSiO3*M_MgSiO3*M_O*M_SiO2*M_m))
```

```python
sol_scol = collect_auto(sol_dM_c_numerator)
```

    752

```python
sol_scol
```

    M_c*(M_O*(M_Fe*(M_FeO*(-2*M_MgO*M_SiO2*M_m*dKFeO_KFeO + M_MgSiO3*(-2*M_MgO*M_m*dKFeO_KFeO + M_SiO2*(M_MgO*(2*dKFeO_KFeO + 2*dKMgSiO3_KMgSiO3) - 2*M_m*dKFeO_KFeO))) + M_FeSiO3*(M_MgO*(-2*M_FeO*M_m*dKFeO_KFeO + M_SiO2*(M_FeO*(2*dKFeO_KFeO + 2*dKFeSiO3_KFeSiO3) + M_m*(-2*dKFeO_KFeO - 2*dKFeSiO3_KFeSiO3))) + M_MgSiO3*(M_SiO2*(M_FeO*(2*dKFeO_KFeO + 2*dKFeSiO3_KFeSiO3) + M_MgO*(2*dKFeO_KFeO + 2*dKFeSiO3_KFeSiO3) + M_m*(-2*dKFeO_KFeO - 2*dKFeSiO3_KFeSiO3)) + M_m*(-2*M_FeO*dKFeO_KFeO + M_MgO*(-2*dKFeO_KFeO - 2*dKFeSiO3_KFeSiO3 + 2*dKMgSiO3_KMgSiO3)))) + M_Mg*(M_FeSiO3*(M_SiO2*(M_FeO*(-2*dKFeO_KFeO - 2*dKFeSiO3_KFeSiO3) + M_MgO*(-2*dKFeO_KFeO - 2*dKFeSiO3_KFeSiO3) + M_m*(2*dKFeO_KFeO + 2*dKFeSiO3_KFeSiO3)) + M_m*(2*M_FeO*dKFeO_KFeO + 2*M_MgO*dKMgO_KMgO)) + M_MgSiO3*(M_SiO2*(M_FeO*(-2*dKMgO_KMgO - 2*dKMgSiO3_KMgSiO3) + M_MgO*(-2*dKMgO_KMgO - 2*dKMgSiO3_KMgSiO3) + M_m*(2*dKMgO_KMgO + 2*dKMgSiO3_KMgSiO3)) + M_m*(2*M_FeO*dKFeO_KFeO + 2*M_MgO*dKMgO_KMgO)) + M_SiO2*M_m*(2*M_FeO*dKFeO_KFeO + 2*M_MgO*dKMgO_KMgO))) + M_Mg*(M_FeO*(-2*M_MgO*M_SiO2*M_m*dKMgO_KMgO + M_MgSiO3*(-2*M_MgO*M_m*dKMgO_KMgO + M_SiO2*(M_MgO*(2*dKMgO_KMgO + 2*dKMgSiO3_KMgSiO3) + M_m*(-2*dKMgO_KMgO - 2*dKMgSiO3_KMgSiO3)))) + M_FeSiO3*(M_MgO*(-2*M_FeO*M_m*dKMgO_KMgO + M_SiO2*(M_FeO*(2*dKFeSiO3_KFeSiO3 + 2*dKMgO_KMgO) - 2*M_m*dKMgO_KMgO)) + M_MgSiO3*(M_SiO2*(M_FeO*(2*dKMgO_KMgO + 2*dKMgSiO3_KMgSiO3) + M_MgO*(2*dKMgO_KMgO + 2*dKMgSiO3_KMgSiO3) + M_m*(-2*dKMgO_KMgO - 2*dKMgSiO3_KMgSiO3)) + M_m*(M_FeO*(2*dKFeSiO3_KFeSiO3 - 2*dKMgO_KMgO - 2*dKMgSiO3_KMgSiO3) - 2*M_MgO*dKMgO_KMgO))))) + M_Si*(M_Fe*(M_FeO*(M_MgO*M_SiO2*M_m*(-2*dKFeO_KFeO + dKSiO2_KSiO2) + M_MgSiO3*(M_MgO*M_m*(-2*dKFeO_KFeO + dKMgSiO3_KMgSiO3 + dKSiO2_KSiO2) + M_SiO2*(M_MgO*(2*dKFeO_KFeO + dKMgSiO3_KMgSiO3 - dKSiO2_KSiO2) + M_m*(-2*dKFeO_KFeO + dKSiO2_KSiO2)))) + M_FeSiO3*(M_MgO*(M_FeO*M_m*(-2*dKFeO_KFeO + dKFeSiO3_KFeSiO3 + dKSiO2_KSiO2) + M_SiO2*(M_FeO*(2*dKFeO_KFeO + dKFeSiO3_KFeSiO3 - dKSiO2_KSiO2) + M_m*(-2*dKFeO_KFeO - 2*dKFeSiO3_KFeSiO3 + dKSiO2_KSiO2))) + M_MgSiO3*(M_SiO2*(M_FeO*(2*dKFeO_KFeO + dKFeSiO3_KFeSiO3 - dKSiO2_KSiO2) + M_MgO*(2*dKFeO_KFeO + 2*dKFeSiO3_KFeSiO3 - dKMgSiO3_KMgSiO3 - dKSiO2_KSiO2) + M_m*(-2*dKFeO_KFeO - 2*dKFeSiO3_KFeSiO3 + dKSiO2_KSiO2)) + M_m*(M_FeO*(-2*dKFeO_KFeO + dKFeSiO3_KFeSiO3 + dKSiO2_KSiO2) + M_MgO*(-2*dKFeO_KFeO - 2*dKFeSiO3_KFeSiO3 + 3*dKMgSiO3_KMgSiO3 + dKSiO2_KSiO2)))) + M_Mg*(M_FeSiO3*(M_SiO2*(M_FeO*(-2*dKFeO_KFeO - dKFeSiO3_KFeSiO3 + dKSiO2_KSiO2) + M_MgO*(-dKFeO_KFeO - dKFeSiO3_KFeSiO3 - dKMgO_KMgO + dKSiO2_KSiO2) + M_m*(2*dKFeO_KFeO + 2*dKFeSiO3_KFeSiO3 - dKSiO2_KSiO2)) + M_m*(M_FeO*(2*dKFeO_KFeO - dKFeSiO3_KFeSiO3 - dKSiO2_KSiO2) + M_MgO*(-dKFeO_KFeO - dKFeSiO3_KFeSiO3 + 3*dKMgO_KMgO - dKSiO2_KSiO2))) + M_MgSiO3*(M_SiO2*(M_FeO*(-dKFeO_KFeO - dKMgO_KMgO - dKMgSiO3_KMgSiO3 + dKSiO2_KSiO2) + M_MgO*(-2*dKMgO_KMgO - dKMgSiO3_KMgSiO3 + dKSiO2_KSiO2) + M_m*(2*dKMgO_KMgO + 2*dKMgSiO3_KMgSiO3 - dKSiO2_KSiO2)) + M_m*(M_FeO*(3*dKFeO_KFeO - dKMgO_KMgO - dKMgSiO3_KMgSiO3 - dKSiO2_KSiO2) + M_MgO*(2*dKMgO_KMgO - dKMgSiO3_KMgSiO3 - dKSiO2_KSiO2))) + M_O*(M_FeSiO3*(M_FeO*(3*dKFeSiO3_KFeSiO3 + 3*dKSiO2_KSiO2) + M_MgO*(3*dKFeO_KFeO + 3*dKFeSiO3_KFeSiO3 - 3*dKMgO_KMgO + 3*dKSiO2_KSiO2) + M_SiO2*(2*dKFeO_KFeO + 2*dKFeSiO3_KFeSiO3) + M_m*(-5*dKFeO_KFeO - 5*dKFeSiO3_KFeSiO3 - 5*dKSiO2_KSiO2)) + M_MgSiO3*(M_FeO*(-3*dKFeO_KFeO + 3*dKMgO_KMgO + 3*dKMgSiO3_KMgSiO3 + 3*dKSiO2_KSiO2) + M_MgO*(3*dKMgSiO3_KMgSiO3 + 3*dKSiO2_KSiO2) + M_SiO2*(2*dKMgO_KMgO + 2*dKMgSiO3_KMgSiO3) + M_m*(-5*dKMgO_KMgO - 5*dKMgSiO3_KMgSiO3 - 5*dKSiO2_KSiO2)) + M_SiO2*(M_FeO*(-dKFeO_KFeO + dKSiO2_KSiO2) + M_MgO*(-dKMgO_KMgO + dKSiO2_KSiO2) - 3*M_m*dKSiO2_KSiO2) + M_m*(-2*M_FeO*dKFeO_KFeO - 2*M_MgO*dKMgO_KMgO)) + M_SiO2*M_m*(M_FeO*(2*dKFeO_KFeO - dKSiO2_KSiO2) + M_MgO*(2*dKMgO_KMgO - dKSiO2_KSiO2))) + M_O*(M_FeSiO3*(M_MgO*(M_FeO*(-3*dKFeSiO3_KFeSiO3 - 3*dKSiO2_KSiO2) + M_SiO2*(-2*dKFeO_KFeO - 2*dKFeSiO3_KFeSiO3) + M_m*(5*dKFeO_KFeO + 5*dKFeSiO3_KFeSiO3 + 5*dKSiO2_KSiO2)) + M_MgSiO3*(M_FeO*(-3*dKFeSiO3_KFeSiO3 - 3*dKSiO2_KSiO2) + M_MgO*(-3*dKMgSiO3_KMgSiO3 - 3*dKSiO2_KSiO2) + M_SiO2*(-2*dKFeO_KFeO - 2*dKFeSiO3_KFeSiO3) + M_m*(5*dKFeO_KFeO + 5*dKFeSiO3_KFeSiO3 + 5*dKSiO2_KSiO2))) + M_MgO*(2*M_FeO*M_m*dKFeO_KFeO + M_SiO2*(M_FeO*(dKFeO_KFeO - dKSiO2_KSiO2) + 3*M_m*dKSiO2_KSiO2)) + M_MgSiO3*(M_FeO*(M_MgO*(-3*dKMgSiO3_KMgSiO3 - 3*dKSiO2_KSiO2) + 2*M_m*dKFeO_KFeO) + M_MgO*M_m*(3*dKMgSiO3_KMgSiO3 + 3*dKSiO2_KSiO2) + M_SiO2*(M_FeO*(dKFeO_KFeO - dKSiO2_KSiO2) + M_MgO*(-3*dKMgSiO3_KMgSiO3 - 3*dKSiO2_KSiO2) + 3*M_m*dKSiO2_KSiO2)))) + M_Mg*(M_FeO*(M_MgO*M_SiO2*M_m*(-2*dKMgO_KMgO + dKSiO2_KSiO2) + M_MgSiO3*(M_MgO*M_m*(-2*dKMgO_KMgO + dKMgSiO3_KMgSiO3 + dKSiO2_KSiO2) + M_SiO2*(M_MgO*(2*dKMgO_KMgO + dKMgSiO3_KMgSiO3 - dKSiO2_KSiO2) + M_m*(-2*dKMgO_KMgO - 2*dKMgSiO3_KMgSiO3 + dKSiO2_KSiO2)))) + M_FeSiO3*(M_MgO*(M_FeO*M_m*(dKFeSiO3_KFeSiO3 - 2*dKMgO_KMgO + dKSiO2_KSiO2) + M_SiO2*(M_FeO*(dKFeSiO3_KFeSiO3 + 2*dKMgO_KMgO - dKSiO2_KSiO2) + M_m*(-2*dKMgO_KMgO + dKSiO2_KSiO2))) + M_MgSiO3*(M_SiO2*(M_FeO*(-dKFeSiO3_KFeSiO3 + 2*dKMgO_KMgO + 2*dKMgSiO3_KMgSiO3 - dKSiO2_KSiO2) + M_MgO*(2*dKMgO_KMgO + dKMgSiO3_KMgSiO3 - dKSiO2_KSiO2) + M_m*(-2*dKMgO_KMgO - 2*dKMgSiO3_KMgSiO3 + dKSiO2_KSiO2)) + M_m*(M_FeO*(3*dKFeSiO3_KFeSiO3 - 2*dKMgO_KMgO - 2*dKMgSiO3_KMgSiO3 + dKSiO2_KSiO2) + M_MgO*(-2*dKMgO_KMgO + dKMgSiO3_KMgSiO3 + dKSiO2_KSiO2)))) + M_O*(M_FeO*(2*M_MgO*M_m*dKMgO_KMgO + M_MgSiO3*(M_MgO*(-3*dKMgSiO3_KMgSiO3 - 3*dKSiO2_KSiO2) + M_SiO2*(-2*dKMgO_KMgO - 2*dKMgSiO3_KMgSiO3) + M_m*(5*dKMgO_KMgO + 5*dKMgSiO3_KMgSiO3 + 5*dKSiO2_KSiO2)) + M_SiO2*(M_MgO*(dKMgO_KMgO - dKSiO2_KSiO2) + 3*M_m*dKSiO2_KSiO2)) + M_FeSiO3*(M_FeO*(M_MgO*(-3*dKFeSiO3_KFeSiO3 - 3*dKSiO2_KSiO2) + M_m*(3*dKFeSiO3_KFeSiO3 + 3*dKSiO2_KSiO2)) + 2*M_MgO*M_m*dKMgO_KMgO + M_MgSiO3*(M_FeO*(-3*dKFeSiO3_KFeSiO3 - 3*dKSiO2_KSiO2) + M_MgO*(-3*dKMgSiO3_KMgSiO3 - 3*dKSiO2_KSiO2) + M_SiO2*(-2*dKMgO_KMgO - 2*dKMgSiO3_KMgSiO3) + M_m*(5*dKMgO_KMgO + 5*dKMgSiO3_KMgSiO3 + 5*dKSiO2_KSiO2)) + M_SiO2*(M_FeO*(-3*dKFeSiO3_KFeSiO3 - 3*dKSiO2_KSiO2) + M_MgO*(dKMgO_KMgO - dKSiO2_KSiO2) + 3*M_m*dKSiO2_KSiO2)))) + M_O*(M_FeO*(-3*M_MgO*M_SiO2*M_m*dKSiO2_KSiO2 + M_MgSiO3*(M_MgO*M_m*(-3*dKMgSiO3_KMgSiO3 - 3*dKSiO2_KSiO2) + M_SiO2*(M_MgO*(3*dKMgSiO3_KMgSiO3 + 3*dKSiO2_KSiO2) - 3*M_m*dKSiO2_KSiO2))) + M_FeSiO3*(M_MgO*(M_FeO*M_m*(-3*dKFeSiO3_KFeSiO3 - 3*dKSiO2_KSiO2) + M_SiO2*(M_FeO*(3*dKFeSiO3_KFeSiO3 + 3*dKSiO2_KSiO2) - 3*M_m*dKSiO2_KSiO2)) + M_MgSiO3*(M_SiO2*(M_FeO*(3*dKFeSiO3_KFeSiO3 + 3*dKSiO2_KSiO2) + M_MgO*(3*dKMgSiO3_KMgSiO3 + 3*dKSiO2_KSiO2) - 3*M_m*dKSiO2_KSiO2) + M_m*(M_FeO*(-3*dKFeSiO3_KFeSiO3 - 3*dKSiO2_KSiO2) + M_MgO*(-3*dKMgSiO3_KMgSiO3 - 3*dKSiO2_KSiO2)))))))

```python

```


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

## _*Basic Quantum Operations*_

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

***
### Contributors
Jay Gambetta, Rudy Raymond and Ali Javadi

 In this notebook we will go into the different operations that are available in QISKit-core. These are:
* Single-qubit quantum gates
* Multi-qubit quantum gates
* Measurements
* Reset
* Conditionals
* State initialization

We will also show you how to use the three different simulators:
* local_unitrary_simulator
* local_qasm_simulator
* local_statevector_simulator

```python
import sys
try:
    sys.path.append("../../") # go to parent dir
    import Qconfig
    qx_config = {
        "APItoken": Qconfig.APItoken,
        "url": Qconfig.config['url']}
except:
    qx_config = {
        "APItoken":"YOUR_TOKEN_HERE",
        "url":"https://quantumexperience.ng.bluemix.net/api"}
```

```python
# Useful additional packages
import matplotlib.pyplot as plt
%matplotlib inline
import numpy as np
from math import pi
```

```python
from qiskit import QuantumCircuit, ClassicalRegister, QuantumRegister, QISKitError
from qiskit import available_backends, execute, register, get_backend
from qiskit.tools.visualization import circuit_drawer
```

## Single Qubit Quantum states

A single qubit quantum state can be written as

$$|\psi\rangle = \alpha|0\rangle + \beta |1\rangle$$

where $\alpha$ and $\beta$ are complex numbers. In a measurement the probability of the bit being in $|0\rangle$ is $|\alpha|^2$ and $|1\rangle$ is $|\beta|^2$. As a vector this is

$$
|\psi\rangle =
\begin{pmatrix}
\alpha \\
\beta
\end{pmatrix}.
$$

Note due to conservation probability $|\alpha|^2+ |\beta|^2 = 1$ and since global phase is undetectable $|\psi\rangle := e^{i\delta} |\psi\rangle$ we only requires two real numbers to describe a single qubit quantum state.

A convenient representation is

$$|\psi\rangle = \cos(\theta/2)|0\rangle + \sin(\theta/2)e^{i\phi}|1\rangle$$

where $0\leq \phi < 2\pi$, and $0\leq \theta \leq \pi$.  From this it is clear that there is a one-to-one correspondence between qubit states ($\mathbb{C}^2$) and the points on the surface of a unit sphere ($\mathbb{R}^3$). This is called the Bloch sphere representation of a qubit state.

Quantum gates/operations are usually represented as matrices. A gate which acts on a qubit is represented by a $2\times 2$ unitary matrix $U$. The action of the quantum gate is found by multiplying the matrix representing the gate with the vector which represents the quantum state.

$$|\psi'\rangle = U|\psi\rangle$$

A general unitary must be able to take the $|0\rangle$ to the above state. That is

$$
U = \begin{pmatrix}
\cos(\theta/2) & a \\
e^{i\phi}\sin(\theta/2) & b
\end{pmatrix}
$$

where $a$ and $b$ are complex numbers constrained such that $U^\dagger U = I$ for all $0\leq\theta\leq\pi$ and $0\leq \phi<2\pi$. This gives 3 constraints and as such $a\rightarrow -e^{i\lambda}\sin(\theta/2)$ and $b\rightarrow e^{i\lambda+i\phi}\cos(\theta/2)$ where $0\leq \lambda<2\pi$ giving

$$
U = \begin{pmatrix}
\cos(\theta/2) & -e^{i\lambda}\sin(\theta/2) \\
e^{i\phi}\sin(\theta/2) & e^{i\lambda+i\phi}\cos(\theta/2)
\end{pmatrix}.
$$

This is the most general form of a single qubit unitary.

## Single-Qubit Gates

The single-qubit gates available are:
* u gates
* Identity gate
* Pauli gates
* Cliffords gates
* $C3$ gates
* Standard rotation gates

We have provided a backend: `local_unitary_simulator` to allow you to calculate the unitary matrices.

```python
q = QuantumRegister(1)
```

### u gates

In QISKit we give you access to the general unitary using the $u3$ gate

$$
u3(\theta, \phi, \lambda) = U(\theta, \phi, \lambda)
$$

```python
qc = QuantumCircuit(q)
qc.u3(pi/2,pi/2,pi/2,q)
circuit_drawer(qc)
```

```python
job = execute(qc, backend='local_unitary_simulator')
np.round(job.result().get_data(qc)['unitary'], 3)
```

    array([[ 0.707+0.j   , -0.   -0.707j],
           [ 0.   +0.707j, -0.707+0.j   ]])

The $u2(\phi, \lambda) =u3(\pi/2, \phi, \lambda)$ has the martix form

$$
u2(\phi, \lambda) =
\frac{1}{\sqrt{2}} \begin{pmatrix}
1 & -e^{i\lambda} \\
e^{i\phi} & e^{i(\phi + \lambda)}
\end{pmatrix}.
$$

This is a useful gate as it allows us to create superpositions

```python
qc = QuantumCircuit(q)
qc.u2(pi/2,pi/2,q)
circuit_drawer(qc)
```

```python
job = execute(qc, backend='local_unitary_simulator')
np.round(job.result().get_data(qc)['unitary'], 3)
```

    array([[ 0.707+0.j   , -0.   -0.707j],
           [ 0.   +0.707j, -0.707+0.j   ]])

The $u1(\lambda)= u3(0, 0, \lambda)$ gate has the matrix form

$$
u1(\lambda) =
\begin{pmatrix}
1 & 0 \\
0 & e^{i \lambda}
\end{pmatrix},
$$

which is a useful as it allows us to apply a quantum phase.

```python
qc = QuantumCircuit(q)
qc.u1(pi/2,q)
circuit_drawer(qc)
```

```python
job = execute(qc, backend='local_unitary_simulator')
np.round(job.result().get_data(qc)['unitary'], 3)
```

    array([[1.+0.j, 0.+0.j],
           [0.+0.j, 0.+1.j]])

The $u0(\delta)= u3(0, 0, 0)$ gate is the identity matrix. It has the matrix form

$$
u0(\delta) =
\begin{pmatrix}
1 & 0 \\
0 & 1
\end{pmatrix}.
$$

The identity gate does nothing (but can add noise in the real device for a period of time equal to fractions of the single qubit gate time)

```python
qc = QuantumCircuit(q)
qc.u0(pi/2,q)
circuit_drawer(qc)
```

```python
job = execute(qc, backend='local_unitary_simulator')
np.round(job.result().get_data(qc)['unitary'], 3)
```

    array([[1.+0.j, 0.+0.j],
           [0.+0.j, 1.+0.j]])

### Identity gate

The identity gate is $Id = u0(1)$.

```python
qc = QuantumCircuit(q)
qc.iden(q)
circuit_drawer(qc)
```

```python
job = execute(qc, backend='local_unitary_simulator')
np.round(job.result().get_data(qc)['unitary'], 3)
```

    array([[1.+0.j, 0.+0.j],
           [0.+0.j, 1.+0.j]])

### Pauli gates

#### $X$: bit-flip gate

The bit-flip gate $X$ is defined as:

$$
X   =
\begin{pmatrix}
0 & 1\\
1 & 0
\end{pmatrix}= u3(\pi,0,\pi)
$$

```python
qc = QuantumCircuit(q)
qc.x(q)
circuit_drawer(qc)
```

```python
job = execute(qc, backend='local_unitary_simulator')
np.round(job.result().get_data(qc)['unitary'], 3)
```

    array([[ 0.+0.j,  1.-0.j],
           [ 1.+0.j, -0.+0.j]])

#### $Y$: bit- and phase-flip gate

The $Y$ gate is defined as:

$$
Y  =
\begin{pmatrix}
0 & -i\\
i & 0
\end{pmatrix}=u3(\pi,\pi/2,\pi/2)
$$

```python
qc = QuantumCircuit(q)
qc.y(q)
circuit_drawer(qc)
```

```python
job = execute(qc, backend='local_unitary_simulator')
np.round(job.result().get_data(qc)['unitary'], 3)
```

    array([[ 0.+0.j, -0.-1.j],
           [ 0.+1.j, -0.+0.j]])

#### $Z$: phase-flip gate

The phase flip gate $Z$ is defined as:

$$
Z =
\begin{pmatrix}
1 & 0\\
0 & -1
\end{pmatrix}=u1(\pi)
$$

```python
qc = QuantumCircuit(q)
qc.z(q)
circuit_drawer(qc)
```

```python
job = execute(qc, backend='local_unitary_simulator')
np.round(job.result().get_data(qc)['unitary'], 3)
```

    array([[ 1.+0.j,  0.+0.j],
           [ 0.+0.j, -1.+0.j]])

### Clifford gates

#### Hadamard gate

$$
H =
\frac{1}{\sqrt{2}}
\begin{pmatrix}
1 & 1\\
1 & -1
\end{pmatrix}= u2(0,\pi)
$$

```python
qc = QuantumCircuit(q)
qc.h(q)
circuit_drawer(qc)
```

```python
job = execute(qc, backend='local_unitary_simulator')
np.round(job.result().get_data(qc)['unitary'], 3)
```

    array([[ 0.707+0.j,  0.707-0.j],
           [ 0.707+0.j, -0.707+0.j]])

#### $S$ (or, $\sqrt{Z}$ phase) gate

$$
S =
\begin{pmatrix}
1 & 0\\
0 & i
\end{pmatrix}= u1(\pi/2)
$$

```python
qc = QuantumCircuit(q)
qc.s(q)
circuit_drawer(qc)
```

```python
job = execute(qc, backend='local_unitary_simulator')
np.round(job.result().get_data(qc)['unitary'], 3)
```

    array([[1.+0.j, 0.+0.j],
           [0.+0.j, 0.+1.j]])

#### $S^{\dagger}$ (or, conjugate of $\sqrt{Z}$ phase) gate

$$
S^{\dagger} =
\begin{pmatrix}
1 & 0\\
0 & -i
\end{pmatrix}= u1(-\pi/2)
$$

```python
qc = QuantumCircuit(q)
qc.sdg(q)
circuit_drawer(qc)
```

```python
job = execute(qc, backend='local_unitary_simulator')
np.round(job.result().get_data(qc)['unitary'], 3)
```

    array([[1.+0.j, 0.+0.j],
           [0.+0.j, 0.-1.j]])

### $C3$ gates
#### $T$ (or, $\sqrt{S}$ phase) gate

$$
T =
\begin{pmatrix}
1 & 0\\
0 & e^{i \pi/4}
\end{pmatrix}= u1(\pi/4)
$$

```python
qc = QuantumCircuit(q)
qc.t(q)
circuit_drawer(qc)
```

```python
job = execute(qc, backend='local_unitary_simulator')
np.round(job.result().get_data(qc)['unitary'], 3)
```

    array([[1.   +0.j   , 0.   +0.j   ],
           [0.   +0.j   , 0.707+0.707j]])

#### $T^{\dagger}$ (or, conjugate of $\sqrt{S}$ phase) gate

$$
T^{\dagger} =
\begin{pmatrix}
1 & 0\\
0 & e^{-i \pi/4}
\end{pmatrix}= u1(-pi/4)
$$

They can be added as below.

```python
qc = QuantumCircuit(q)
qc.tdg(q)
circuit_drawer(qc)
```

```python
job = execute(qc, backend='local_unitary_simulator')
np.round(job.result().get_data(qc)['unitary'], 3)
```

    array([[1.   +0.j   , 0.   +0.j   ],
           [0.   +0.j   , 0.707-0.707j]])

### Standard Rotations

The standard rotation gates are those that define rotations around the Paulis $P=\{X,Y,Z\}$. They are defined as

$$ R_P(\theta) = \exp(-i \theta P/2) = \cos(\theta/2)I -i \sin(\theta/2)P$$

#### Rotation around X-axis

$$
R_x(\theta) =
\begin{pmatrix}
\cos(\theta/2) & -i\sin(\theta/2)\\
-i\sin(\theta/2) & \cos(\theta/2)
\end{pmatrix} = u3(\theta, -\pi/2,\pi/2)
$$

```python
qc = QuantumCircuit(q)
qc.rx(pi/2,q)
circuit_drawer(qc)
```

```python
job = execute(qc, backend='local_unitary_simulator')
np.round(job.result().get_data(qc)['unitary'], 3)
```

    array([[ 0.707+0.j   , -0.   -0.707j],
           [ 0.   -0.707j,  0.707+0.j   ]])

#### Rotation round Y-axis

$$
R_y(\theta) =
\begin{pmatrix}
\cos(\theta/2) & - \sin(\theta/2)\\
\sin(\theta/2) & \cos(\theta/2).
\end{pmatrix} =u3(\theta,0,0)
$$

```python
qc = QuantumCircuit(q)
qc.ry(pi/2,q)
circuit_drawer(qc)
```

```python
job = execute(qc, backend='local_unitary_simulator')
np.round(job.result().get_data(qc)['unitary'], 3)
```

    array([[ 0.707+0.j, -0.707+0.j],
           [ 0.707+0.j,  0.707+0.j]])

#### Rotation around Z-axis

$$
R_z(\phi) =
\begin{pmatrix}
e^{-i \phi/2} & 0 \\
0 & e^{i \phi/2}
\end{pmatrix}\equiv u1(\phi)
$$

Note here we have used an equivalent as is different to u1 by global phase $e^{-i \phi/2}$.

```python
qc = QuantumCircuit(q)
qc.rz(pi/2,q)
circuit_drawer(qc)
```

```python
job = execute(qc, backend='local_unitary_simulator')
np.round(job.result().get_data(qc)['unitary'], 3)
```

    array([[1.+0.j, 0.+0.j],
           [0.+0.j, 0.+1.j]])

Note this is different due only to a global phase

## Multi-Qubit Gates

The space of quantum computer grows exponential with the number of qubits. For $n$ qubits the complex vector space has dimensions $d=2^n$.

Notice that in the QISKit, the order of qubits are represented from the most significant bit (MSB) on the left to the least significant bit (LSB) on the right (big-endian). This is similar to bitstring representation on classical computers, and enables easy conversion from bitstrings to integers after measurements are performed.  That is the first four states of two qubits are $|00\rangle$, $|01\rangle$, $|10\rangle$, and $|11\rangle$.

The joint space is a tensor product space:
\begin{equation}
	X\otimes Y =
	\begin{pmatrix}
		x_{00} \begin{pmatrix}
			y_{00} & y_{01} \\
			y_{10} & y_{11}
		\end{pmatrix} & x_{01} 	\begin{pmatrix}
				y_{00} & y_{01} \\
				y_{10} & y_{11}
			\end{pmatrix} \\
		x_{10} 	\begin{pmatrix}
					y_{00} & y_{01} \\
					y_{10} & y_{11}
				\end{pmatrix} & x_{11} 	\begin{pmatrix}
							y_{00} & y_{01} \\
							y_{10} & y_{11}
						\end{pmatrix}
	\end{pmatrix}
\end{equation} which means for the states we have
\begin{equation}\begin{split}
	|{00}\rangle &= \begin{pmatrix}
		\begin{pmatrix}
			1  \\
			0
		\end{pmatrix} \\
		\begin{pmatrix}
			0  \\
			0
		\end{pmatrix}
	\end{pmatrix} ~  	|{01}\rangle = \begin{pmatrix}
	\begin{pmatrix}
	0 \\
	1
	\end{pmatrix} \\
	\begin{pmatrix}
	0  \\
	0
	\end{pmatrix}
	\end{pmatrix} ~ 	|{10}\rangle = \begin{pmatrix}
	\begin{pmatrix}
	0  \\
	0
	\end{pmatrix} \\
	\begin{pmatrix}
	1 \\
	0
	\end{pmatrix}
	\end{pmatrix} ~ 	|{11}\rangle = \begin{pmatrix}
	\begin{pmatrix}
	0  \\
	0
	\end{pmatrix} \\
	\begin{pmatrix}
	0  \\
	1
	\end{pmatrix}
	\end{pmatrix}\end{split}
\end{equation}

Thus, we write the tensor product of qubits $q_0$, $q_1$, and $q_2$ as $q_2 \otimes q_1 \otimes q_0$. The physics community often writes this in the opposite way, and care must be taken when working with these scenarios, as demonstrated in multi-qubit gates below.

A common two-qubit gate is $C_U$. The $C_U$ when qubit 0 is the control and qubit 1 is the target (eg `cU(q[0],q[1])`) is

\begin{equation}
	C_U = \begin{pmatrix}
	1 & 0 & 0 & 0 \\
	0 & u_{00} & 0 & u_{01} \\
	0 & 0 & 1 & 0 \\
	0 & u_{10} &0 & u_{11}
		\end{pmatrix},
\end{equation}

The $C_U$ when qubit 1 is the control and qubit 0 is the taget (eg `cU(q[1],q[0])`)
\begin{equation}
	C_U = \begin{pmatrix}
	1 & 0 & 0  & 0 \\
	0 & 1 & 0 & 0 \\
	0 & 0 & u_{00} & u_{01} \\
	0 & 0 & u_{10} & u_{11}
		\end{pmatrix}
\end{equation}

The standard multi-qubit gates consist of two-qubit gates and three-qubit gates. The two-qubit gates are:
* controlled Pauli gates
* controlled Hadamard gate
* controlled rotation gates
* controlled phase gate
* controlled u3 gate
* swap gate

The three-qubit gates are:
* Toffoli gate
* Fredkin gate

```python
q = QuantumRegister(2)
```

### Controlled Pauli Gates

#### Controlled-X (or, controlled-NOT) gate
The controlled-not gate flips the `target` qubit when the control qubit is in the state $|1\rangle$. If we take the MSB as the control qubit (e.g. `cx(q[1],q[0])`), then the matrix would look like

$$
C_X =
\begin{pmatrix}
1 & 0 & 0 & 0\\
0 & 1 & 0 & 0\\
0 & 0 & 0 & 1\\
0 & 0 & 1 & 0
\end{pmatrix}.
$$

However, when the LSB is the control qubit, (e.g. `cx(q[0],q[1])`), this gate is equivalent to the following matrix:

$$
C_X =
\begin{pmatrix}
1 & 0 & 0 & 0\\
0 & 0 & 0 & 1\\
0 & 0 & 1 & 0\\
0 & 1 & 0 & 0
\end{pmatrix}.
$$

```python
qc = QuantumCircuit(q)
qc.cx(q[0],q[1])
circuit_drawer(qc)
```

```python
job = execute(qc, backend='local_unitary_simulator')
np.round(job.result().get_data(qc)['unitary'], 3)
```

    array([[1.+0.j, 0.+0.j, 0.+0.j, 0.+0.j],
           [0.+0.j, 0.+0.j, 0.+0.j, 1.+0.j],
           [0.+0.j, 0.+0.j, 1.+0.j, 0.+0.j],
           [0.+0.j, 1.+0.j, 0.+0.j, 0.+0.j]])

#### Controlled $Y$ gate

Apply the $Y$ gate to the target qubit if the control qubit is the MSB

$$
C_Y =
\begin{pmatrix}
1 & 0 & 0 & 0\\
0 & 1 & 0 & 0\\
0 & 0 & 0 & -i\\
0 & 0 & i & 0
\end{pmatrix},
$$

or when the LSB is the control

$$
C_Y =
\begin{pmatrix}
1 & 0 & 0 & 0\\
0 & 0 & 0 & -i\\
0 & 0 & 1 & 0\\
0 & i & 0 & 0
\end{pmatrix}.
$$

```python
qc = QuantumCircuit(q)
qc.cy(q[0],q[1])
circuit_drawer(qc)
```

```python
job = execute(qc, backend='local_unitary_simulator')
np.round(job.result().get_data(qc)['unitary'], 3)
```

    array([[1.+0.j, 0.+0.j, 0.+0.j, 0.+0.j],
           [0.+0.j, 0.+0.j, 0.+0.j, 0.-1.j],
           [0.+0.j, 0.+0.j, 1.+0.j, 0.+0.j],
           [0.+0.j, 0.+1.j, 0.+0.j, 0.+0.j]])

#### Controlled $Z$ (or, controlled Phase) gate

Similarly, the controlled Z gate flips the phase of the target qubit if the control qubit is $1$. The matrix looks the same regardless of whether the MSB or LSB is the control qubit:

$$
C_Z =
\begin{pmatrix}
1 & 0 & 0 & 0\\
0 & 1 & 0 & 0\\
0 & 0 & 1 & 0\\
0 & 0 & 0 & -1
\end{pmatrix}
$$

```python
qc = QuantumCircuit(q)
qc.cz(q[0],q[1])
circuit_drawer(qc)
```

```python
job = execute(qc, backend='local_unitary_simulator')
np.round(job.result().get_data(qc)['unitary'], 3)
```

    array([[ 1.-0.j,  0.+0.j,  0.+0.j,  0.+0.j],
           [ 0.+0.j,  1.-0.j,  0.+0.j, -0.-0.j],
           [ 0.+0.j,  0.+0.j,  1.-0.j,  0.+0.j],
           [ 0.+0.j, -0.+0.j,  0.+0.j, -1.+0.j]])

### Controlled Hadamard gate

Apply $H$ gate to the target qubit if the control qubit is $|1\rangle$. Below is the case where the control is the LSB qubit.

$$
C_H =
\begin{pmatrix}
1 & 0 & 0 & 0\\
0 & \frac{1}{\sqrt{2}} & 0 & \frac{1}{\sqrt{2}}\\
0 & 0 & 1 & 0\\
0 & \frac{1}{\sqrt{2}}  & 0& -\frac{1}{\sqrt{2}}
\end{pmatrix}
$$

```python
qc = QuantumCircuit(q)
qc.ch(q[0],q[1])
circuit_drawer(qc)
```

```python
job = execute(qc, backend='local_unitary_simulator')
np.round(job.result().get_data(qc)['unitary']/(0.707+0.707j), 3)
```

    array([[ 1.   -0.j,  0.   +0.j, -0.   +0.j,  0.   +0.j],
           [ 0.   +0.j,  0.707-0.j,  0.   +0.j,  0.707-0.j],
           [ 0.   -0.j,  0.   +0.j,  1.   -0.j,  0.   +0.j],
           [ 0.   +0.j,  0.707-0.j,  0.   +0.j, -0.707+0.j]])

### Controlled rotation gates

#### Controlled rotation around Z-axis

Perform rotation around Z-axis on the target qubit if the control qubit (here LSB) is $|1\rangle$.

$$
C_{Rz}(\lambda) =
\begin{pmatrix}
1 & 0 & 0 & 0\\
0 & e^{-i\lambda/2} & 0 & 0\\
0 & 0 & 1 & 0\\
0 & 0 & 0 & e^{i\lambda/2}
\end{pmatrix}
$$

```python
qc = QuantumCircuit(q)
qc.crz(pi/2,q[0],q[1])
circuit_drawer(qc)
```

```python
job = execute(qc, backend='local_unitary_simulator')
np.round(job.result().get_data(qc)['unitary'], 3)
```

    array([[1.   +0.j   , 0.   +0.j   , 0.   +0.j   , 0.   +0.j   ],
           [0.   +0.j   , 0.707-0.707j, 0.   +0.j   , 0.   +0.j   ],
           [0.   +0.j   , 0.   +0.j   , 1.   +0.j   , 0.   +0.j   ],
           [0.   +0.j   , 0.   +0.j   , 0.   +0.j   , 0.707+0.707j]])

### Controlled phase rotation

Perform a phase rotation if both qubits are in the $|11\rangle$ state. The matrix looks the same regardless of whether the MSB or LSB is the control qubit.

$$
C_{u1}(\lambda) =
\begin{pmatrix}
1 & 0 & 0 & 0\\
0 & 1 & 0 & 0\\
0 & 0 & 1 & 0\\
0 & 0 & 0 & e^{i\lambda}
\end{pmatrix}
$$

```python
qc = QuantumCircuit(q)
qc.cu1(pi/2,q[0], q[1])
circuit_drawer(qc)
```

I THINK SHOULD BE CALLED $C_\mathrm{PHASE}(\lambda)$

```python
job = execute(qc, backend='local_unitary_simulator')
np.round(job.result().get_data(qc)['unitary'], 3)
```

    array([[1.+0.j, 0.+0.j, 0.+0.j, 0.+0.j],
           [0.+0.j, 1.+0.j, 0.+0.j, 0.+0.j],
           [0.+0.j, 0.+0.j, 1.+0.j, 0.+0.j],
           [0.+0.j, 0.+0.j, 0.+0.j, 0.+1.j]])

### Controlled $u3$ rotation

Perform controlled-$u3$ rotation on the target qubit if the control qubit (here LSB) is $|1\rangle$.

$$
C_{u3}(\theta, \phi, \lambda) \equiv
\begin{pmatrix}
1 & 0 & 0 & 0\\
0 & e^{-i(\phi+\lambda)/2}\cos(\theta/2) & 0 & -e^{-i(\phi-\lambda)/2}\sin(\theta/2)\\
0 & 0 & 1 & 0\\
0 & e^{i(\phi-\lambda)/2}\sin(\theta/2) & 0 & e^{i(\phi+\lambda)/2}\cos(\theta/2)
\end{pmatrix}.
$$

```python
qc = QuantumCircuit(q)
qc.cu3(pi/2, pi/2, pi/2, q[0], q[1])
circuit_drawer(qc)
```

```python
job = execute(qc, backend='local_unitary_simulator')
np.round(job.result().get_data(qc)['unitary'], 3)
```

    array([[ 1.   +0.j   ,  0.   +0.j   ,  0.   +0.j   ,  0.   +0.j   ],
           [ 0.   +0.j   ,  0.   -0.707j,  0.   +0.j   , -0.707+0.j   ],
           [ 0.   +0.j   ,  0.   +0.j   ,  1.   +0.j   ,  0.   +0.j   ],
           [ 0.   +0.j   ,  0.707+0.j   ,  0.   +0.j   ,  0.   +0.707j]])

NOTE I NEED TO FIX THIS AND DECIDE ON CONVENTION - I ACTUALLY THINK WE WANT A FOUR PARAMETER GATE AND JUST CALL IT CU AND TO REMOVE THIS GATE.

### Swap gate

The swap gate exchange the first and second qubits.

$$
{SWAP} =
\begin{pmatrix}
1 & 0 & 0 & 0\\
0 & 0 & 1 & 0\\
0 & 1 & 0 & 0\\
0 & 0 & 0 & 1
\end{pmatrix}.
$$

```python
qc = QuantumCircuit(q)
qc.swap(q[0], q[1])
circuit_drawer(qc)
```

```python
job = execute(qc, backend='local_unitary_simulator')
np.round(job.result().get_data(qc)['unitary'], 3)
```

    array([[1.+0.j, 0.+0.j, 0.+0.j, 0.+0.j],
           [0.+0.j, 0.+0.j, 1.+0.j, 0.+0.j],
           [0.+0.j, 1.+0.j, 0.+0.j, 0.+0.j],
           [0.+0.j, 0.+0.j, 0.+0.j, 1.+0.j]])

### Toffoli gate ($ccx$ gate)

The Toffoli gate flips the third qubit if the first two qubits (LSB) are both $|1\rangle$.

$$
C_{CX} =
\begin{pmatrix}
1 & 0 & 0 & 0 & 0 & 0 & 0 & 0\\
0 & 1 & 0 & 0 & 0 & 0 & 0 & 0\\
0 & 0 & 1 & 0 & 0 & 0 & 0 & 0\\
0 & 0 & 0 & 0 & 0 & 0 & 0 & 1\\
0 & 0 & 0 & 0 & 1 & 0 & 0 & 0\\
0 & 0 & 0 & 0 & 0 & 1 & 0 & 0\\
0 & 0 & 0 & 0 & 0 & 0 & 1 & 0\\
0 & 0 & 0 & 1 & 0 & 0 & 0 & 0
\end{pmatrix}
$$

```python
q = QuantumRegister(3)
```

```python
qc = QuantumCircuit(q)
qc.ccx(q[0], q[1], q[2])
circuit_drawer(qc)
```

```python
job = execute(qc, backend='local_unitary_simulator')
np.round(job.result().get_data(qc)['unitary'], 3)
```

    array([[ 1.-0.j,  0.+0.j,  0.+0.j,  0.+0.j,  0.+0.j,  0.+0.j,  0.+0.j,
             0.+0.j],
           [ 0.+0.j,  1.+0.j,  0.+0.j,  0.+0.j,  0.+0.j,  0.+0.j,  0.+0.j,
             0.+0.j],
           [ 0.+0.j,  0.+0.j,  1.+0.j,  0.+0.j,  0.+0.j,  0.+0.j,  0.+0.j,
             0.+0.j],
           [ 0.+0.j,  0.+0.j,  0.+0.j,  0.+0.j,  0.+0.j,  0.+0.j,  0.+0.j,
             1.-0.j],
           [ 0.+0.j,  0.+0.j,  0.+0.j,  0.+0.j,  1.-0.j,  0.+0.j,  0.+0.j,
             0.+0.j],
           [ 0.+0.j,  0.+0.j,  0.+0.j,  0.+0.j,  0.+0.j,  1.-0.j,  0.+0.j,
             0.+0.j],
           [ 0.+0.j,  0.+0.j,  0.+0.j,  0.+0.j,  0.+0.j,  0.+0.j,  1.-0.j,
             0.+0.j],
           [ 0.+0.j,  0.+0.j,  0.+0.j,  1.-0.j,  0.+0.j,  0.+0.j,  0.+0.j,
            -0.+0.j]])

### Controlled swap gate (Fredkin Gate)

The controlled swap gate exchange the second and third qubits if the first qubit (LSB) is $|1\rangle$.

$$
C_{SWAP} =
\begin{pmatrix}
1 & 0 & 0 & 0 & 0 & 0 & 0 & 0\\
0 & 1 & 0 & 0 & 0 & 0 & 0 & 0\\
0 & 0 & 1 & 0 & 0 & 0 & 0 & 0\\
0 & 0 & 0 & 0 & 0 & 1 & 0 & 0\\
0 & 0 & 0 & 0 & 1 & 0 & 0 & 0\\
0 & 0 & 0 & 1 & 0 & 0 & 0 & 0\\
0 & 0 & 0 & 0 & 0 & 0 & 1 & 0\\
0 & 0 & 0 & 0 & 0 & 0 & 0 & 1
\end{pmatrix}
$$

```python
qc = QuantumCircuit(q)
qc.cswap(q[0], q[1], q[2])
circuit_drawer(qc)
```

```python
job = execute(qc, backend='local_unitary_simulator')
np.round(job.result().get_data(qc)['unitary'], 3)
```

    array([[ 1.-0.j,  0.+0.j,  0.+0.j,  0.+0.j,  0.+0.j,  0.+0.j,  0.+0.j,
             0.+0.j],
           [ 0.+0.j,  1.+0.j,  0.+0.j,  0.+0.j,  0.+0.j,  0.+0.j,  0.+0.j,
             0.+0.j],
           [ 0.+0.j,  0.+0.j,  1.+0.j,  0.+0.j,  0.+0.j,  0.+0.j,  0.+0.j,
             0.+0.j],
           [ 0.+0.j,  0.+0.j,  0.+0.j,  0.+0.j,  0.+0.j,  0.+0.j,  0.+0.j,
             1.-0.j],
           [ 0.+0.j,  0.+0.j,  0.+0.j,  0.+0.j,  1.-0.j,  0.+0.j,  0.+0.j,
             0.+0.j],
           [ 0.+0.j,  0.+0.j,  0.+0.j,  0.+0.j,  0.+0.j,  1.-0.j,  0.+0.j,
             0.+0.j],
           [ 0.+0.j,  0.+0.j,  0.+0.j,  0.+0.j,  0.+0.j,  0.+0.j,  1.-0.j,
             0.+0.j],
           [ 0.+0.j,  0.+0.j,  0.+0.j,  1.-0.j,  0.+0.j,  0.+0.j,  0.+0.j,
            -0.+0.j]])

## Non unitary operations

Now we have gone through all the unitary operations in quantum circuits we also have access to non-unitary operations. These include measurements, reset of qubits, and classical conditional operations.

```python
q = QuantumRegister(1)
c = ClassicalRegister(1)
```

### Measurements

We dont have access to all the information when we make a measurement in a quantum computer. The quantum state is projected onto the standard basis. Below are two examples showing a circuit that is prepared in a basis state and the quantum computer prepared in a superpostion state.

```python
qc = QuantumCircuit(q, c)
qc.measure(q, c)
circuit_drawer(qc)
```

```python
job = execute(qc, backend='local_qasm_simulator', shots=1024)
job.result().get_counts(qc)
```

    {'0': 1024}

 The simulator predicts that 100 percent of the time the classical registor returns 0.

```python
qc = QuantumCircuit(q, c)
qc.h(q)
qc.measure(q, c)
circuit_drawer(qc)
```

```python
job = execute(qc, backend='local_qasm_simulator', shots=1024)
job.result().get_counts(qc)
```

    {'0': 524, '1': 500}

 The simulator predicts that 50 percent of the time the classical registor returns 0 or 1.

### Reset
It is also possible to `reset` qubits to the $|0\rangle$ state in the middle of computation. Note that `reset` is not a Gate operation, since it is irreversible.

```python
qc = QuantumCircuit(q, c)
qc.reset(q[0])
qc.measure(q, c)
circuit_drawer(qc)
```

```python
job = execute(qc, backend='local_qasm_simulator', shots=1024)
job.result().get_counts(qc)
```

    {'0': 1024}

```python
qc = QuantumCircuit(q, c)
qc.h(q)
qc.reset(q[0])
qc.measure(q, c)
circuit_drawer(qc)
```

```python
job = execute(qc, backend='local_qasm_simulator', shots=1024)
job.result().get_counts(qc)
```

    {'0': 1024}

Here we see that for both of these circuits the simulator always predicts that the output is 100 percent in the 0 state.

### Conditional operations
It is also possible to do operations conditioned on the state of the classical registor

```python
qc = QuantumCircuit(q, c)
qc.x(q[0]).c_if(c, 0)
qc.measure(q,c)
circuit_drawer(qc)
```

```python
job = execute(qc, backend='local_qasm_simulator', shots=1024)
job.result().get_counts(qc)
```

    {'1': 1024}

Here the classical bit always takes the value 0 so the qubit state is always flipped.

```python
qc = QuantumCircuit(q, c)
qc.h(q)
qc.measure(q,c)
qc.x(q[0]).c_if(c, 0)
qc.measure(q,c)
circuit_drawer(qc)
```

```python
job = execute(qc, backend='local_qasm_simulator', shots=1024)
job.result().get_counts(qc)
```

    {'1': 1024}

Here the classical bit by the first measurement is random but the conditional operation results in the qubit being deterministically put into $|1\rangle$.

### Arbitrary initialization
What if we want to initialize a qubit register to an arbitrary state? An arbitrary state for $n$ qubits may be specified by a vector of $2^n$ amplitudes, where the sum of amplitude-norms-squared equals 1. For example, the following three-qubit state can be prepared:

$$|\psi\rangle = \frac{i}{4}|000\rangle + \frac{1}{\sqrt{8}}|001\rangle + \frac{1+i}{4}|010\rangle + \frac{1+2i}{\sqrt{8}}|101\rangle + \frac{1}{4}|110\rangle$$

```python
# Initializing a three-qubit quantum state
import math
desired_vector = [
    1 / math.sqrt(16) * complex(0, 1),
    1 / math.sqrt(8) * complex(1, 0),
    1 / math.sqrt(16) * complex(1, 1),
    0,
    0,
    1 / math.sqrt(8) * complex(1, 2),
    1 / math.sqrt(16) * complex(1, 0),
    0]

q = QuantumRegister(3)

qc = QuantumCircuit(q)

qc.initialize(desired_vector, [q[0],q[1],q[2]])
circuit_drawer(qc)
```

```python
job = execute(qc, backend='local_statevector_simulator')
qc_state = job.result().get_statevector(qc)
qc_state
```

    array([ 2.50000000e-01+0.00000000e+00j,  5.55111512e-17-3.53553391e-01j,
            2.50000000e-01-2.50000000e-01j, -2.19980517e-18-3.09536077e-17j,
           -2.62603921e-17+5.08377096e-17j,  7.07106781e-01-3.53553391e-01j,
            8.67361738e-17-2.50000000e-01j,  0.00000000e+00+0.00000000e+00j])

```python
def norm(state1, state2):
    return abs(np.dot(np.array(state1).conj(), state2))**2
```

```python
norm(desired_vector,qc_state)
```

    1.0

#### Further details:

How does the desired state get generated behind the scenes? There are multiple methods for doing this. QISKit uses a [method proposed by Shende et al](https://arxiv.org/abs/quant-ph/0406176). Here, the idea is to assume the quantum register to have started from our desired state, and construct a circuit that takes it to the $|00..0\rangle$ state. The initialization circuit is then the reverse of such circuit.

To take an arbitrary quantum state to the zero state in the computational basis, we perform an iterative procedure that disentangles qubits from the register one-by-one. We know that any arbitrary single-qubit state $|\rho\rangle$ can be taken to the $|0\rangle$ state using a $\phi$-degree rotation about the Z axis followed by a $\theta$-degree rotation about the Y axis:

$$R_y(-\theta)R_z(-\phi)|\rho\rangle = re^{it}|0\rangle$$

Since now we are dealing with $n$ qubits instead of just 1, we must factorize the state vector to separate the Least Significant Bit (LSB):

$$\begin{align*}
& |\psi\rangle = \alpha_{0_0}|00..00\rangle + \alpha_{0_1}|00..01\rangle + \alpha_{1_0}|00..10\rangle + \alpha_{1_1}|00..11\rangle + ... + \alpha_{(2^{n-1}-1)_0}|11..10\rangle + \alpha_{(2^{n-1}-1)_1}|11..11\rangle \\
&= |00..0\rangle (\alpha_{0_0}|0\rangle + \alpha_{0_1}|1\rangle) + |00..1\rangle (\alpha_{1_0}|0\rangle + \alpha_{1_1}|1\rangle) + ... + |11..1\rangle (\alpha_{(2^{n-1}-1)_0}(|0\rangle + \alpha_{(2^{n-1}-1)_1}|1\rangle) \\
&= |00..0\rangle|\rho_0\rangle + |00..1\rangle|\rho_1\rangle + ... + |11..1\rangle|\rho_{2^{n-1}-1}\rangle
\end{align*}$$

Now each of the single-qubit states $|\rho_0\rangle, ..., |\rho_{2^{n-1}-1}\rangle$ can be taken to $|0\rangle$ by finding appropriate $\phi$ and $\theta$ angles per the equation above. Doing this simultaneously on all states amounts to the following unitary, which disentangles the LSB:

$$U = \begin{pmatrix}
R_{y}(-\theta_0)R_{z}(-\phi_0) & & & &\\
& R_{y}(-\theta_1)R_{z}(-\phi_1) & & &\\
& . & & &\\
& & . & &\\
& & & & R_y(-\theta_{2^{n-1}-1})R_z(-\phi_{2^{n-1}-1})
\end{pmatrix} $$

Hence,

$$U|\psi\rangle = \begin{pmatrix} r_0e^{it_0}\\ r_1e^{it_1}\\ . \\ . \\ r_{2^{n-1}-1}e^{it_{2^{n-1}-1}} \end{pmatrix}\otimes|0\rangle$$

U can be implemented as a "quantum multiplexor" gate, since it is a block diagonal matrix. In the quantum multiplexor formalism, a block diagonal matrix of size $2^n \times 2^n$, and consisting of $2^s$ blocks, is equivalent to a multiplexor with $s$ select qubits and $n-s$ data qubits. Depending on the state of the select qubits, the corresponding blocks are applied to the data qubits. A multiplexor of this kind can be implemented after recursive decomposition to primitive gates of cx, rz and ry.

```python

```

```python

```


# CMSIS-DSP Python package example

## Installing and importing the needed packages

Installation below may generate an error in google colab. It is related to conflicting versions of the Jinja2 package. It will not prevent the use of the cmsisdsp package in google colab.

```python
!pip install cmsisdsp
```

    Requirement already satisfied: cmsisdsp in c:\benchresults\pythonwrappertests\testenv\lib\site-packages (1.2.1)
    Requirement already satisfied: numpy>=1.19 in c:\benchresults\pythonwrappertests\testenv\lib\site-packages (from cmsisdsp) (1.22.2)
    Requirement already satisfied: jinja2>=3.0 in c:\benchresults\pythonwrappertests\testenv\lib\site-packages (from cmsisdsp) (3.0.3)
    Requirement already satisfied: networkx>=2.5 in c:\benchresults\pythonwrappertests\testenv\lib\site-packages (from cmsisdsp) (2.6.3)
    Requirement already satisfied: sympy>=1.6 in c:\benchresults\pythonwrappertests\testenv\lib\site-packages (from cmsisdsp) (1.9)
    Requirement already satisfied: MarkupSafe>=2.0 in c:\benchresults\pythonwrappertests\testenv\lib\site-packages (from jinja2>=3.0->cmsisdsp) (2.1.0)
    Requirement already satisfied: mpmath>=0.19 in c:\benchresults\pythonwrappertests\testenv\lib\site-packages (from sympy>=1.6->cmsisdsp) (1.2.1)

```python
import numpy as np
import cmsisdsp as dsp
```

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

## Creating the signal

```python
nb = 512
f=100
signal = np.sin(2 * np.pi * np.arange(nb)*f / nb) + 0.1*np.random.randn(nb)
```

```python
plt.plot(signal)
plt.show()
```

```python
# Array of complex numbers as an array of real numbers
def imToReal1D(a):
    ar=np.zeros(np.array(a.shape) * 2)
    ar[0::2]=a.real
    ar[1::2]=a.imag
    return(ar)

# Array of real numbers as an array of complex numbers
def realToIm1D(ar):
    return(ar[0::2] + 1j * ar[1::2])
```

## Using the F32 CMSIS-DSP FFT

```python
# CMSIS-DSP FFT F32 initialization
cfftf32=dsp.arm_cfft_instance_f32()
status=dsp.arm_cfft_init_f32(cfftf32,nb)
print(status)
```

    0

```python
# Re-evaluate this each time you change the signal
signalR = imToReal1D(signal)
resultR = dsp.arm_cfft_f32(cfftf32,signalR,0,1)
resultI = realToIm1D(resultR)
mag=20 * np.log10(np.abs(resultI))
```

```python
plt.plot(mag[1:nb//2])
plt.show()
```

## Using the Q15 CMSIS-DSP FFT

```python
# Convert the signal to Q15 and viewed as a real array
import cmsisdsp.fixedpoint as f
signalR = imToReal1D(signal)
signalRQ15 = f.toQ15(signalR)
```

```python
# Initialize the Q15 CFFT
cfftq15 = dsp.arm_cfft_instance_q15()
status = dsp.arm_cfft_init_q15(cfftq15,nb)
print(status)
```

    0

```python
# Compute the Q15 CFFT and convert back to float and complex array
resultR = dsp.arm_cfft_q15(cfftq15,signalRQ15,0,1)
resultR = f.Q15toF32(resultR)
resultI = realToIm1D(resultR)*nb
```

```python
mag = 20 * np.log10(np.abs(resultI))
plt.plot(mag[1:nb//2])
plt.show()
```

```python

```


# Nosecone Designs

This notbook provides descriptions, equations and renderings of several nose cone shapes commonly used in hobbly rocketry. The notebook leverages serveral common packages such as matplotlib, numpy for data handling and 2D graphing and some less-common packages viewscad, solidpython and OpenSCAD for 3D rendering.

Our first step is to import these packages.

```python
# nbi:hide_in
from os.path import expanduser
from ipywidgets import interact, interactive, fixed, interact_manual
import ipywidgets as widgets
import matplotlib.pyplot as plt
import numpy as np
import viewscad
from solid import *
```

L and R define the length and radius of the nosecone and S defines the diameter of the shoulder. M is the diameter of the mount.

```python
# nbi:hide_in
length = 60
radius = 24.8/2
S = 10
Ri = 24.1/2

SMOOTH=100
```

This function adds origin coordinates to the arrays passed and plots a graph of the nosecone profile in 2 dimensions. The arrays are combined into an array of points and rotated around the z-axis and outputs an .stl file.

```python
def render(x, y, name):
    zero = np.array([0])
    base = np.array([Ri])
    shoulder = np.array([-S])

    xplt = np.concatenate((shoulder, shoulder, zero, x, shoulder))
    yplt = np.concatenate((zero, base, base, y, zero))

    plt.axes().set_aspect("equal")
    plt.plot(xplt, yplt)

    r = viewscad.Renderer()
    p = rotate_extrude(360, segments=SMOOTH)(polygon(np.vstack((yplt, xplt)).T))

    r.render(p, outfile=expanduser("~") + '/' + name + '.stl')
```

We define a set of values for X from 0 to the nosecone length.

## Elliptical

The elliptical nose cone shape is one-half of an ellipse, with the major axis being the centerline and the minor axis being the base of the nose cone. A rotation of a full ellipse about its major axis is called a prolate spheroid, so an elliptical nose shape would properly be known as a prolate hemispheroid. This shape is popular in subsonic flight (such as model rocketry) due to the blunt nose and tangent base and are generally considered superior for model rocketry altitude optimisation use. This is not a shape normally found in professional rocketry, which almost always flies at much higher velocities where other designs are more suitable.

The profile is defined as $y=R{\sqrt  {1-{x^{2} \over L^{2}}}}$ If $R = L$, this is a hemisphere.

We define our function, apply that to the values of x to generate an array of y values, which are used to generate a plot of the nosecone profile. The points returned combine the x and y arrays with the addition of the origin point to enure a closed shape.

```python
x = np.linspace(0, length, int(length * 5))
f = lambda x: radius * np.sqrt(1-(x**2/length**2))
y = f(x)

render(x, y, 'elliptical')
```

rotate the profile around the z-axis to create a solid object and output an .stl for subsequent use.

## Conical

A very common nose-cone shape is a simple cone. This shape is often chosen for its ease of manufacture, and is also often chosen for its drag and radar cross section characteristics. A lower drag cone would be more streamlined, with the most optimal shape being a Sears-Haack body. The sides of a conical profile are straight lines, so the diameter equation is simply $y={xR \over L}$

Cones are sometimes defined by their half angle, $\phi$

$\phi = \arctan \Bigl({R \over L}\Bigr)$ and $y = x \tan (\phi) $

In practical applications, a conical nose is often blunted by capping it with a segment of a sphere. The tangency point where the sphere meets the cone can be found from

$x_t = \frac{L^2}{R}\sqrt{\frac{r_n^2}{R^2+L^2}}$

$y_t = \frac{x_tR}{L}$

where

$r_n$ is the radius of the spherical nose cap.

$x_o = x_t + \sqrt{r_n^2 - y_t^2}$

$x_a = x_o - r_n$

```python
f = lambda x: x*radius/length
y = f(x)

points = build_2d(x, y[::-1])
```

```python
build_3d(points, 'conical')
```

    VBox(children=(HTML(value=''), Renderer(background='#cccc88', background_opacity=0.0, camera=PerspectiveCamera…

## Parabolic
This nose shape is not the blunt shape that is envisioned when people commonly refer to a "parabolic" nose cone. The parabolic series nose shape is generated by rotating a segment of a parabola around a line parallel to its latus rectum. This construction is similar to that of the tangent ogive, except that a parabola is the defining shape rather than a circle. Just as it does on an ogive, this construction produces a nose shape with a sharp tip. For the blunt shape typically associated with a parabolic nose, see power series below. (The parabolic shape is also often confused with the elliptical shape.)

For $0 \leq K^\prime \leq 1 :  y=R \Biggl({2({x \over L})-K^\prime({x \over L})^{2} \over 2-K^\prime}\Biggr)$

$K^\prime$ can vary anywhere between $0$ and $1$, but the most common values used for nose cone shapes are:

| Parabola Type | $K^\prime$ Value |
| --- | --- |
| Cone | $0$ |
| Half | $\frac {1}{2}$ |
| Three Quarter| $3 \over 4$ |
| Full | $1$ |

For the case of the full parabola $(K^\prime = 1)$ the shape is tangent to the body at its base, and the base is on the axis of the parabola. Values of $K^\prime \lt 1$ result in a slimmer shape, whose appearance is similar to that of the secant ogive. The shape is no longer tangent at the base, and the base is parallel to, but offset from, the axis of the parabola.

```python
K = .75

f = lambda x: radius*(((2*(x/length))-(K*(x/length)**2))/(2-K))
y = f(x)

points = build_2d(x, y[::-1])
```

```python
build_3d(points, 'parabolic')
```

    VBox(children=(HTML(value=''), Renderer(background='#cccc88', background_opacity=0.0, camera=PerspectiveCamera…

## Ogive
### Tangent ogive
Next to a simple cone, the tangent ogive shape is the most familiar in model/hobby rocketry. The profile of this shape is formed by a segment of a circle such that the rocket body is tangent to the curve of the nose cone at its base, and the base is on the radius of the circle. The popularity of this shape is largely due to the ease of constructing its profile, as it is simply a circular section.

The radius of the circle that forms the ogive is called the ''ogive radius'', $\rho$, and it is related to the length and base radius of the nose cone as expressed by the formula

$\rho = {R^2 + L^2\over 2R}$

The radius $y$ at any point $x$, as $x$ varies from $0$ to $L$ is

$y = \sqrt{\rho^2 - (L - x)^2}+R - \rho$

The nose cone length, $L$, must be $\leq \rho$. If they are equal, then the shape is a hemisphere.

### Spherically blunted tangent ogive
A tangent ogive nose is often blunted by capping it with a segment of a [[sphere]].  The tangency point where the sphere meets the tangent ogive can be found from:
\begin{align}
  x_o &= L - \sqrt{\left(\rho - r_n\right)^2 - (\rho - R)^2} \\
  y_t &= \frac{r_n(\rho - R)}{\rho - r_n} \\
  x_t &= x_o - \sqrt{r_n^2 - y_t^2}
\end{align}
where $r_n$ is the radius and $x_o$ is the center of the spherical nose cap.

Finally, the apex point can be found from:
$x_a = x_o - r_n$

```python
rho = ((radius**2+length**2)/2*radius)
print(rho)
f = lambda x: np.sqrt((rho**2 - (x-length)**2)) + (radius - rho)
y = f(x)
print(x, y)
yplt = y[::-1]
plt.axes().set_aspect("auto")
plt.plot(x, yplt)

```

## Haack series

Unlike all of the nose cone shapes above, the Haack Series shapes are not constructed from geometric figures. The shapes are instead mathematically derived for the purpose of minimizing drag; see also Sears–Haack body. While the series is a continuous set of shapes determined by the value of $C$ in the equations below, two values of $C$ have particular significance: when $C = 0$, the notation $LD$ signifies minimum drag for the given length and diameter, and when $C = {1 \over 3}$, $LV$ indicates minimum drag for a given length and volume. The Haack series nose cones are not perfectly tangent to the body at their base except for the case where $C = {2 \over 3}$. However, the discontinuity is usually so slight as to be imperceptible. For $C > {2 \over 3}$, Haack nose cones bulge to a maximum diameter greater than the base diameter. Haack nose tips do not come to a sharp point, but are slightly rounded.

$\theta = \arccos \Bigl(1 - {2X \over L}\Bigr)$

$y = {R \over \sqrt{\pi}} \sqrt{\theta-{\sin({2\theta})\over2}+C \sin^3({\theta})}$

Where:

$C = {1 \over 3}$ for LV-Haack

$C = 0$ for LD-Haack

```python
C = 1/3

f = lambda x: (radius/np.sqrt(np.pi))*np.sqrt((np.arccos(1 - (2*x)/length)) - (np.sin(2 * (np.arccos(1 - (2*x)/length))))/2 + C * np.sin((np.arccos(1 - (2*x)/length)))**3)
y = f(x)

points = build_2d(x, y[::-1])
```

```python
build_3d(points, 'haak')
```

    VBox(children=(HTML(value=''), Renderer(background='#cccc88', background_opacity=0.0, camera=PerspectiveCamera…

```python

```


# 05: 2d Classical Ising Model Monte Carlo

```python
import numpy as np
import matplotlib.pyplot as plt
%matplotlib inline
%config InlineBackend.figure_format = 'retina'
try: plt.style.use('./mc_notebook.mplstyle')
except: pass
from IPython.display import display
```

## Ising Model of Ferromagnatism

Measuring energies in units where $J=k_{\rm B} = 1$ we can write the Ising model Hamiltonian:

\begin{equation}
H = - \sum_{\langle i,j\rangle } \sigma_i \sigma_j
\end{equation}

where $\sigma_i = \pm 1$ and $\langle i,j \rangle$ means that sites $i$ and $j$ are nearest neighbors.  Let's consider a $N = L \times L$ square lattice in $d=2$ with periodic boundary conditions.  This is equivalent to wrapping our lattice onto the surface of a torus.

In two dimensions, our configuration $\mathbf{x}$ is a set of spin values $\sigma_i$ which can be written as a matrix:

```python
def initialize_config(L):
    '''Initialize a random spin configuration'''

    σ = np.ones([L,L],dtype=int)
    σ[np.random.random([L,L])<=0.5] = -1
    return σ

L = 10
σ = initialize_config(L)

# visualize
plt.matshow(σ, cmap='gray', extent=[0,9,0,9]);
plt.xticks([]);
plt.yticks([]);
```

In terms of this matrix, the energy can be written as:

\begin{equation}
E(\sigma) = - \frac{1}{2}\sum_{i=0}^{L-1} \sum_{j=0}^{L-1} \sigma[i,j]\left(\sigma[i+1,j] + \sigma[i-1,j] + \sigma[i,j+1] + \sigma[i,j-1]\right)
\end{equation}

How do we deal with the periodic boundary conditions?

```python
# i + 1
p1 = np.arange(1,L+1)
p1[-1] = 0

# i - 1
m1 = np.arange(-1,L-1)
m1[0] = L-1

display(p1)
display(m1)
```

    array([1, 2, 3, 4, 5, 6, 7, 8, 9, 0])

    array([9, 0, 1, 2, 3, 4, 5, 6, 7, 8])

### Thermodynamic Properties: Energy and Magnetization per Spin

```python
def get_props(σ):
    '''The energy E and magnetization M per spin for a microstate of the 2d Ising model.'''
    E,M = 0,0
    Lx = σ.shape[1]
    Ly = σ.shape[0]

    for i in range(Lx):
        for j in range(Ly):
            M += σ[i,j]
            E -= 0.5*σ[i,j]*(σ[p1[i],j] + σ[m1[i],j] + σ[i,p1[j]] + σ[i,m1[j]])
    return E/(Lx*Ly),M/(Lx*Ly)
```

```python
get_props(σ)
```

    (0.04, 0.04)

## Monte Carlo Update

We need to sample the Boltzmann probability:

\begin{equation}
\pi(\sigma) = \frac{\mathrm{e}^{-\beta E(\sigma)}}{\mathcal{Z}}
\end{equation}

which can be done via Metropolis Monte Carlo by constructing a Markov chain with update acceptance probabilities:

\begin{equation}
p(\sigma \to \sigma') = \text{min}\left[1,\frac{\pi(\sigma')\mathcal{A}(\sigma' \to \sigma)}{\pi(\sigma)\mathcal{A}(\sigma \to \sigma')}\right].
\end{equation}

Here, the easiest update consists of flipping an individual spin and thus the *a priori* update probability is $\mathcal{A} = 1/N$ for all updates so it cancels out of the probability.

**Note: this simple scheme has problems with critical slowing down (see below).**

Can be solved with cluster updates. See, e.g.: [U. Wolff,  *Collective Monte Carlo updating for spin systems*, Phys. Rev. Lett. **62**, 361 (1989).](https://journals.aps.org/prl/abstract/10.1103/PhysRevLett.62.361)

Next, we need to determine the change in energy as:

\begin{equation}
\frac{\pi(\sigma')}{\pi(\sigma)} = \frac{\mathrm{e}^{-E(\sigma')/T}}{\mathrm{e}^{-E(\sigma)/T}} = \mathrm{e}^{-\Delta E/T}
\end{equation}

where $\Delta E = E(\sigma') - E(\sigma)$.

**Note: the partition function has cancelled out of the probability!**

Suppose we flip the spin with index $k,\ell$, then: $\sigma'[k,\ell] = -\sigma[k,\ell]$ and we can write:

\begin{equation}
\Delta E = 2\sigma[k,\ell]\left(\sigma[k+1,\ell] + \sigma[k-1,\ell] + \sigma[k,\ell+1] + \sigma[k,\ell-1]\right).
\end{equation}

and

\begin{equation}
\Delta M = 2 \sigma[k,\ell]
\end{equation}

and thus the acceptance probability for the move will be:

\begin{equation}
p(\sigma \to \sigma') = \min\left\{1, \mathrm{e}^{-\Delta E/T}\right\}.
\end{equation}

Each Monte Carlo update thus consists of:

1. Select a random spin to flip
2. Calculate $\Delta E$
3. Generate a uniform random number $r \in \mathcal{U}_{[0,1)}$
4. Perform the Metropolis test
    - If $r < \mathrm{e}^{-\Delta E/T}$: accept the move
    - Otherwise: do nothing
5. Measure the magnetization

A Monte Carlo **step** consists of $N=L^2$ updates.

```python
def monte_carlo_step(σ,T):
    '''Perform a Monte Carlo step.'''

    # get the current magnetization
    M = np.sum(σ)

    # attempt L^2 spin flips
    for update in range(σ.size):

        # get the random spin
        k = np.random.randint(0,σ.shape[0])
        ℓ = np.random.randint(0,σ.shape[1])

        # calculate the change in energy
        ΔE = 2*σ[k,ℓ]*(σ[p1[k],ℓ] + σ[m1[k],ℓ] + σ[k,p1[ℓ]] + σ[k,m1[ℓ]])

        # perform the Metropolis test
        if np.random.random() <= np.exp(-ΔE/T):
            σ[k,ℓ] *= -1

            # Update the magnetization
            M += 2*σ[k,ℓ]

    return M
```

### Testing it out

```python
# initialize
σ = initialize_config(L)

# visualize
plt.matshow(σ, cmap='gray', extent=[0,9,0,9]);
plt.xticks([]);
plt.yticks([]);
get_props(σ)
```

```python
# update
monte_carlo_step(σ,1.0)

# visualize
plt.matshow(σ, cmap='gray', extent=[0,9,0,9]);
plt.xticks([]);
plt.yticks([]);
get_props(σ)
```

### Monte Carlo Simulation

A simulation consists of performing a large number of Monte Carlo steps at each temperature starting from some random initial configuration.

```python
# temperatures to consider
T = np.arange(0.2,2.5,0.25)

# number of Monte Carlo steps we will perform
num_steps = 2**13

# magnetization for each temperature
M = np.zeros([num_steps,T.size])
```

```python
%%time
# initialize
L = 4
σ = initialize_config(L)

# create PBC lookup tables
p1 = np.arange(1,L+1)
p1[-1] = 0
m1 = np.arange(-1,L-1)
m1[0] - L-1

# Loop over temperatures from high to low
for iT,cT in enumerate(T[::-1]):
    m = T.size - 1 - iT

    # initialize the magnetization
    M[0,m] = np.sum(σ)/L**2

    # Perform the Monte Carlo steps
    for step in range(1,num_steps):
        M[step,m] = monte_carlo_step(σ,cT)/L**2
```

    CPU times: user 18.3 s, sys: 173 ms, total: 18.5 s
    Wall time: 18.6 s

### Investigate the raw data

```python
iT = -2

# compute a running average
run_ave_m = np.zeros(len(M[:,iT])-1)
for n in range(1,len(M[:,iT])-1):
    run_ave_m[n] = np.average(M[:n,iT])

# plot the raw data
plt.plot(M[:,iT],'-', label='T = %3.1f' %T[iT], alpha=0.25)

# plot the running average
plt.plot(run_ave_m, linewidth=2)

plt.legend(loc='upper right', frameon=True, framealpha=0.95)
plt.xlabel('Monte Carlo Step')
plt.ylabel('Magnetization per spin');
```

### Analyzing the results

We want to compute the average value of the magnetization at each temperature along with its standard error, but we need to skip some number of initial measurements as the system takes some *time* to **equilibrate**.

```python
skip = int(0.2*M.shape[0])
m = np.average(M[skip:],axis=0)
Δm = np.std(M[skip:],axis=0)/np.sqrt(num_steps-skip)
```

```python
plt.errorbar(T,np.abs(m),yerr=Δm, linewidth=1, marker='o', markersize=4, elinewidth=1, capsize=0)
plt.axvline(x=2.0/np.log(1.0+np.sqrt(2.0)), linewidth=1, color='gray', linestyle='--')
plt.xlim(0.2,2.4)
plt.ylim(-0.01,1.1)
plt.xlabel(r'Temperature ($k_{\rm B}T/J$)')
plt.ylabel('Magnetization per spin')
plt.title("L = 4");
```

These error bars look **way** too small!  Need to perform a binning analysis!

```python
def get_binned_error(mc_data):
    '''Get the standard error in mc_data and return neighbor averaged data.'''
    N_bins = mc_data.size
    Δ = np.std(mc_data)/np.sqrt(N_bins)

    start_bin = N_bins % 2
    binned_mc_data = 0.5*(mc_data[start_bin::2]+mc_data[start_bin+1::2])

    return Δ,binned_mc_data
```

```python
# number of possible binning levels
num_levels = np.int(np.log2(M[skip:,0].size/4))+1

# compute the error at each bin level for each temperature
Δm = np.zeros([len(T),num_levels])

for iT,cT in enumerate(T):

    binned_mc_data = M[:,iT]
    for n in range(num_levels):
        Δₙ,binned_mc_data = get_binned_error(binned_mc_data)
        Δm[iT,n] = Δₙ
```

```python
for iT,cT in enumerate(T):
    plt.plot(np.abs(Δm[iT]),'-o', linewidth=1, markersize=8, label='$k_{\mathrm{B}}T/J = %4.2f$'%cT)
    plt.xlabel('bin level')
    plt.ylabel('Δm');
    plt.legend(ncol=1, loc=(1,0))
```

### Replot with improved binned error bars

```python
plt.errorbar(T,np.abs(m),yerr=Δm[:,-2], linewidth=1, marker='o', markersize=6, elinewidth=1, capsize=0)
plt.axvline(x=2.0/np.log(1.0+np.sqrt(2.0)), linewidth=1, color='gray', linestyle='--')
plt.xlim(0.2,2.5)
plt.ylim(-0.01,1.1)
plt.xlabel(r'Temperature ($k_{\rm B}T/J$)')
plt.ylabel('Magnetization per spin')
plt.title("L = 4");
```

## Load some data for a *much* longer run and $L=32$ from disk and compare with the exact Onsager solution

\begin{equation}
m=\left[1-\left(\sinh \frac{2J}{k_{\rm B}T}\right)^{-4}\right]^{\frac {1}{8}}
\end{equation}

```python
def magnetization_exact_(T):
    '''We use units where J/k_B = 1.'''
    Tc = 2.0/np.log(1.0+np.sqrt(2.0))
    if T < Tc:
        return (1.0 - np.sinh(2.0/T)**(-4))**(1.0/8)
    else:
        return 0.0
magnetization_exact = np.vectorize(magnetization_exact_)
```

```python
data = np.loadtxt('Ising_estimators_032.dat.gz')
lT = np.linspace(0.01,4,1000)
lL = 32

plt.plot(lT,magnetization_exact(lT),'-', linewidth=1, label='Exact')
plt.errorbar(data[:,0],np.abs(data[:,5])/lL**2,yerr=data[:,6]/lL**2, linewidth=1, marker='o',
             markersize=6, elinewidth=1, label='Monte Carlo', linestyle='None')
plt.xlim(0.2,4)
plt.ylim(0,1.1)
plt.xlabel(r'Temperature ($k_{\rm B}T/J$)')
plt.ylabel('Magnetization per spin')
plt.title("L = 32")

plt.legend();
```

<!--
hlabel = 'm(T = %4.2f)' % T[0]
header = '%23s' % hlabel
for cT in T[1:]:
    hlabel = 'm(T = %4.2f)' % cT
    header += '%24s ' % hlabel

np.savetxt('Ising_magnetization_004.dat.gz',M, fmt='%24.16E', header=header)
M = np.loadtxt('Ising_magnetization_004.dat.gz')
-->

```python

```