```python
import sympy as sp
from sympy.parsing.sympy_parser import parse_expr
import pandas as pd
```

```python
def get_lines(filename):
    file = open(filename, 'r+')
    lines = file.readlines()
    #
    lines = map(lambda line : line[:-1],lines)
    file.close()
    return lines
```

```python
lines = get_lines('./tests/ejercicio1.txt')
```

```python
syntax = pd.DataFrame(data=lines, columns=['line'])
syntax.head()
```

      line

      0
      Inicio

      1
      Lea n

      2
      Para i=1,n,+1

      3
      Para j=1,n,+1

      4
      Lea A(i,,j)

```python
syntax["length"] = syntax["line"].map(lambda line: len(line))
syntax.head()
```

      line
      length

      0
      Inicio
      6

      1
      Lea n
      5

      2
      Para i=1,n,+1
      13

      3
      Para j=1,n,+1
      13

      4
      Lea A(i,,j)
      11

```python
def get_type(line):
    #If not line.lower().find('x') returns true if x starts at line[0]
    if not line.lower().find('inicio'):
        return "inicio"
    if not line.lower().find('pare'):
        return "pare"
    if not line.lower().find('para'):
        return "para"
    if not line.lower().find('lea'):
        return "lea"
    if not line.lower().find('esc'):
        return "esc"
    if not line.lower().find('fpara'):
        return "fpara"
    if not line.lower().find('sino'):
        return "sino"
    if not line.lower().find('si'):
        return "si"
    if not line.lower().find('fsi'):
        return "fsi"
    if line.lower().find('='):
        return "assignment"
    return "Indefinite so far"

syntax["type"] = syntax["line"].map(get_type)
syntax.head(len(syntax))
```

      line
      length
      type

      0
      Inicio
      6
      inicio

      1
      Lea n
      5
      lea

      2
      Para i=1,n,+1
      13
      para

      3
      Para j=1,n,+1
      13
      para

      4
      Lea A(i,,j)
      11
      lea

      5
      Fpara
      5
      fpara

      6
      Fpara
      5
      fpara

      7
      Para i=1,n,+1
      13
      para

      8
      Para j=1,n,+1
      13
      para

      9
      Lea B(i,,j)
      11
      lea

      10
      Fpara
      5
      fpara

      11
      Fpara
      5
      fpara

      12
      s=0
      3
      assignment

      13
      Para i=1,n,+1
      13
      para

      14
      X=x+A(i,i)
      10
      assignment

      15
      Fpara
      5
      fpara

      16
      x=0
      3
      assignment

      17
      Para i=1,n,+1
      13
      para

      18
      Para j=1,n,+1
      13
      para

      19
      Si (i==j)
      9
      si

      20
      x=x+B(i,j)
      10
      assignment

      21
      Fsi
      3
      fsi

      22
      Fpara
      5
      fpara

      23
      Fpara
      5
      fpara

      24
      Si (x>s)
      8
      si

      25
      Esc “x es mayor”
      16
      esc

      26
      Esc x
      5
      esc

      27
      sino
      4
      sino

      28
      Esc “x no es mayor”
      19
      esc

      29
      Fsi
      3
      fsi

      30
      Pare
      4
      pare

```python
def process_for(line):
        raw_data = line[line.index('=')+1:]
        split_data = raw_data.split(',')
        print(split_data)
        lower_bound = parse_expr(split_data[0])
        upper_bound = parse_expr(split_data[1])
        increment = parse_expr(split_data[2])
        return {
            'lower_b' : lower_bound,
            'upper_b' : upper_bound,
            'inc' : increment
        }
```

```python
test_for_line = 'Para i=100,a+b,-10'
process_for(test_for_line)
```

    ['100', 'a+b', '-10']

    {'lower_b': 100, 'upper_b': a + b, 'inc': -10}

```python
def process_if(line):
    comparisons = [pos for pos, char in enumerate(line) if char == '(']
    return {
        'comparisons' : len(comparisons)
    }
```

```python

def new_get_statement_runtime(syntax):
    lines_dict_list = lines = syntax.to_dict('records')
    order = 0
    for index in range(len(lines_dict_list)):
        line = lines[index]
        # print(line['type'])
        line_type = line['type']
        if line_type in ('inicio','pare','sino'):
            #order does not change
            line['runtime'] = 0
            # line['data'] = 'Control statement'
            line['order'] = order
        elif line_type in ("assignment",'lea','esc'):
            line['runtime'] = 1
            # line['data'] = 'Assignment i/o'
            line['order'] = order
        elif  line_type in ('fsi','fpara'):
            order -= 1
            line['runtime'] = 0
            # line['data'] = 'End of block'
            line['order'] = order

        elif line_type is 'para':
            line['runtime'] = 'Nan'
            line['data'] = process_for(line['line'])
            line['order'] = order
            order += 1
        elif line_type is 'si':
            line['runtime'] = 'Nan' #Number of comparisons + instructions inside
            line['data'] = process_if(line['line'])
            line['order'] = order
            order +=1
        # print(f'Line : {line}')

    return pd.DataFrame.from_dict(lines)

```

```python
new_syntax = new_get_statement_runtime(syntax)
new_syntax.head(len(new_syntax))
```

    ['1', 'n', '+1']
    ['1', 'n', '+1']
    ['1', 'n', '+1']
    ['1', 'n', '+1']
    ['1', 'n', '+1']
    ['1', 'n', '+1']
    ['1', 'n', '+1']

      line
      length
      type
      runtime
      order
      data

      0
      Inicio
      6
      inicio
      0
      0
      NaN

      1
      Lea n
      5
      lea
      1
      0
      NaN

      2
      Para i=1,n,+1
      13
      para
      Nan
      0
      {'lower_b': 1, 'upper_b': n, 'inc': 1}

      3
      Para j=1,n,+1
      13
      para
      Nan
      1
      {'lower_b': 1, 'upper_b': n, 'inc': 1}

      4
      Lea A(i,,j)
      11
      lea
      1
      2
      NaN

      5
      Fpara
      5
      fpara
      0
      1
      NaN

      6
      Fpara
      5
      fpara
      0
      0
      NaN

      7
      Para i=1,n,+1
      13
      para
      Nan
      0
      {'lower_b': 1, 'upper_b': n, 'inc': 1}

      8
      Para j=1,n,+1
      13
      para
      Nan
      1
      {'lower_b': 1, 'upper_b': n, 'inc': 1}

      9
      Lea B(i,,j)
      11
      lea
      1
      2
      NaN

      10
      Fpara
      5
      fpara
      0
      1
      NaN

      11
      Fpara
      5
      fpara
      0
      0
      NaN

      12
      s=0
      3
      assignment
      1
      0
      NaN

      13
      Para i=1,n,+1
      13
      para
      Nan
      0
      {'lower_b': 1, 'upper_b': n, 'inc': 1}

      14
      X=x+A(i,i)
      10
      assignment
      1
      1
      NaN

      15
      Fpara
      5
      fpara
      0
      0
      NaN

      16
      x=0
      3
      assignment
      1
      0
      NaN

      17
      Para i=1,n,+1
      13
      para
      Nan
      0
      {'lower_b': 1, 'upper_b': n, 'inc': 1}

      18
      Para j=1,n,+1
      13
      para
      Nan
      1
      {'lower_b': 1, 'upper_b': n, 'inc': 1}

      19
      Si (i==j)
      9
      si
      Nan
      2
      {'comparisons': 1}

      20
      x=x+B(i,j)
      10
      assignment
      1
      3
      NaN

      21
      Fsi
      3
      fsi
      0
      2
      NaN

      22
      Fpara
      5
      fpara
      0
      1
      NaN

      23
      Fpara
      5
      fpara
      0
      0
      NaN

      24
      Si (x>s)
      8
      si
      Nan
      0
      {'comparisons': 1}

      25
      Esc “x es mayor”
      16
      esc
      1
      1
      NaN

      26
      Esc x
      5
      esc
      1
      1
      NaN

      27
      sino
      4
      sino
      0
      1
      NaN

      28
      Esc “x no es mayor”
      19
      esc
      1
      1
      NaN

      29
      Fsi
      3
      fsi
      0
      0
      NaN

      30
      Pare
      4
      pare
      0
      0
      NaN

```python
new_line = 'Si (i==j) y (j==1)'
process_if(new_line)
```

    {'comparisons': 2}

```python
def get_if_block_runtime(block_lines):
    runtime = 0
    for line in block_lines:
        runtime += line['runtime']
    return runtime
```

```python
def get_if_blocks_runtime(syntax):
    lines_dict_list = lines = syntax.to_dict('records')
    if_indices = [pos for pos, line in enumerate(lines) if line['type'] is 'si']
    else_indices = [pos for pos, line in enumerate(lines) if line['type'] is 'sino']
    end_if_indices = [pos for pos, line in enumerate(lines) if line['type'] is 'fsi']
    # done = False

    #Let's begin by processing the ifs statements
    if_statements = []
    for x, if_index in enumerate(if_indices):
        #Find closing endif
        end_if_index = end_if_indices[x]
        #Is there an else?
        else_index = False
        for line_index in range(if_index,end_if_index):
            if  line_index in else_indices:
                else_index = line_index
                break
        # print((if_index,else_index,end_if_index))
        comparisons = lines[if_index]['data']['comparisons']
        if_runtime = comparisons
        if else_index:
            block_a = lines[if_index+1:else_index]
            block_b = lines[else_index+1: end_if_index]
            bloc_a_runtime = get_if_block_runtime(block_a)
            bloc_b_runtime = get_if_block_runtime(block_b)
            if_runtime += max(bloc_a_runtime,bloc_b_runtime)
        else:
            block = lines[if_index+1:end_if_index]
            bloc_runtime = get_if_block_runtime(block)
            if_runtime += bloc_runtime
        print((if_index,else_index,end_if_index,if_runtime))
        lines[if_index]['runtime'] = if_runtime
    return pd.DataFrame.from_dict(lines)

    # print('para',for_indices)
    # print('fpara',end_for_indices)
    # print('si',if_indices)
    # print('sino',else_indices)
    # print('fsi',end_if_indices)

```

```python
syntax_with_ifs = get_if_blocks_runtime(new_syntax)
syntax_with_ifs.head(len(syntax_with_ifs))
```

    (19, False, 21, 2)
    (24, 27, 29, 3)

      line
      length
      type
      runtime
      order
      data

      0
      Inicio
      6
      inicio
      0
      0
      NaN

      1
      Lea n
      5
      lea
      1
      0
      NaN

      2
      Para i=1,n,+1
      13
      para
      Nan
      0
      {'lower_b': 1, 'upper_b': n, 'inc': 1}

      3
      Para j=1,n,+1
      13
      para
      Nan
      1
      {'lower_b': 1, 'upper_b': n, 'inc': 1}

      4
      Lea A(i,,j)
      11
      lea
      1
      2
      NaN

      5
      Fpara
      5
      fpara
      0
      1
      NaN

      6
      Fpara
      5
      fpara
      0
      0
      NaN

      7
      Para i=1,n,+1
      13
      para
      Nan
      0
      {'lower_b': 1, 'upper_b': n, 'inc': 1}

      8
      Para j=1,n,+1
      13
      para
      Nan
      1
      {'lower_b': 1, 'upper_b': n, 'inc': 1}

      9
      Lea B(i,,j)
      11
      lea
      1
      2
      NaN

      10
      Fpara
      5
      fpara
      0
      1
      NaN

      11
      Fpara
      5
      fpara
      0
      0
      NaN

      12
      s=0
      3
      assignment
      1
      0
      NaN

      13
      Para i=1,n,+1
      13
      para
      Nan
      0
      {'lower_b': 1, 'upper_b': n, 'inc': 1}

      14
      X=x+A(i,i)
      10
      assignment
      1
      1
      NaN

      15
      Fpara
      5
      fpara
      0
      0
      NaN

      16
      x=0
      3
      assignment
      1
      0
      NaN

      17
      Para i=1,n,+1
      13
      para
      Nan
      0
      {'lower_b': 1, 'upper_b': n, 'inc': 1}

      18
      Para j=1,n,+1
      13
      para
      Nan
      1
      {'lower_b': 1, 'upper_b': n, 'inc': 1}

      19
      Si (i==j)
      9
      si
      2
      2
      {'comparisons': 1}

      20
      x=x+B(i,j)
      10
      assignment
      1
      3
      NaN

      21
      Fsi
      3
      fsi
      0
      2
      NaN

      22
      Fpara
      5
      fpara
      0
      1
      NaN

      23
      Fpara
      5
      fpara
      0
      0
      NaN

      24
      Si (x>s)
      8
      si
      3
      0
      {'comparisons': 1}

      25
      Esc “x es mayor”
      16
      esc
      1
      1
      NaN

      26
      Esc x
      5
      esc
      1
      1
      NaN

      27
      sino
      4
      sino
      0
      1
      NaN

      28
      Esc “x no es mayor”
      19
      esc
      1
      1
      NaN

      29
      Fsi
      3
      fsi
      0
      0
      NaN

      30
      Pare
      4
      pare
      0
      0
      NaN

```python
# orders = []
# for line in lines:
#     orders.append(line['order'])
# max_order = max(orders)
# if max_order is 3:
#There exists at least a combination of two nested loops with an if
# for_indices = [pos for pos, line in enumerate(lines) if line['type'] is 'para']
# end_for_indices = [pos for pos, line in enumerate(lines) if line['type'] is 'fpara']
```

```python
def for_runtime_formula(for_data,content_runtime):
    lower_bound = for_data['lower_b']
    upper_bound = for_data['upper_b']
    try:
        lower_bound = int(lower_bound)
    except TypeError:
        lower_bound = lower_bound
    try:
        upper_bound = int(upper_bound)
    except TypeError:
        upper_bound = upper_bound

    increment = parse_expr(str(for_data['inc']))
    ceil = sp.Function('ceil')
    iterations = (ceil((upper_bound-lower_bound+1)/increment)*(content_runtime +2)) + 2
    return iterations
```

```python
int(parse_expr('8'))
```

    8

```python
lower_bound = parse_expr('n-1')
upper_bound = parse_expr('8')
try:
    lb = int(lower_bound)
except TypeError:
    lb = lower_bound
lb + 2

```

$\displaystyle n + 1$

```python
def get_for_blocks_runtime(syntax):
    lines_dict_list = lines = syntax.to_dict('records')
    for_indices = [pos for pos, line in enumerate(lines) if line['type'] is 'para']
    print('for_indices',for_indices)
    endfor_indices = [pos for pos,line in enumerate(lines) if line['type'] is 'fpara']
    print('endfor_indices',endfor_indices)
    #get for blocks and their orders
    block_orders = []
    for x ,for_index in enumerate(for_indices):
        if x < len(for_indices) -1 :
            next_end_for = endfor_indices[x]
            next_for = for_indices[x+1]
            if next_for < next_end_for:
                block_orders.append((for_index,0))
            else:
                block_orders.append((for_index,1))
        else:
            block_orders.append((for_index,1))
    # print(block_orders)

    #get inner for runtime
    for for_index in [bloc_order[0] for bloc_order in block_orders if bloc_order[1] is 1]:
        # print(for_index)
        for end_for in endfor_indices:
            if end_for > for_index:
                break
        for_order = lines[for_index]['order']
        instruction_order = for_order + 1
        inner_instructions = lines[for_index+1:end_for]
        content_runtime = 0 #placeholder
        for line in inner_instructions:
            if(line['order'] is instruction_order):
                content_runtime+= line['runtime']
        for_runtime = for_runtime_formula(lines[for_index]['data'],content_runtime)
        lines[for_index]['runtime'] = for_runtime

    #get outer for runtimes
    for for_index in [bloc_order[0] for bloc_order in block_orders if bloc_order[1] is 0]:
        for x, end_for in enumerate(endfor_indices):
            if lines[end_for]['order'] == lines[for_index]['order'] and end_for >for_index:
                break
        for_order = lines[for_index]['order']
        instruction_order = for_order + 1
        inner_instructions = lines[for_index+1:end_for]
        content_runtime = "" #placeholder
        for line in inner_instructions:
            if(line['order'] is instruction_order):
                content_runtime += '+'+str(line['runtime'])
        for_runtime =for_runtime_formula(lines[for_index]['data'],parse_expr(str(content_runtime)))
        lines[for_index]['runtime'] = for_runtime
    return pd.DataFrame.from_dict(lines)

```

```python
syntax_complete = get_for_blocks_runtime(syntax_with_ifs)
syntax_complete.head(len(syntax_complete))
```

    for_indices [2, 3, 7, 8, 13, 17, 18]
    endfor_indices [5, 6, 10, 11, 15, 22, 23]

      line
      length
      type
      runtime
      order
      data

      0
      Inicio
      6
      inicio
      0
      0
      NaN

      1
      Lea n
      5
      lea
      1
      0
      NaN

      2
      Para i=1,n,+1
      13
      para
      (3*ceil(n) + 4)*ceil(n) + 2
      0
      {'lower_b': 1, 'upper_b': n, 'inc': 1}

      3
      Para j=1,n,+1
      13
      para
      3*ceil(n) + 2
      1
      {'lower_b': 1, 'upper_b': n, 'inc': 1}

      4
      Lea A(i,,j)
      11
      lea
      1
      2
      NaN

      5
      Fpara
      5
      fpara
      0
      1
      NaN

      6
      Fpara
      5
      fpara
      0
      0
      NaN

      7
      Para i=1,n,+1
      13
      para
      (3*ceil(n) + 4)*ceil(n) + 2
      0
      {'lower_b': 1, 'upper_b': n, 'inc': 1}

      8
      Para j=1,n,+1
      13
      para
      3*ceil(n) + 2
      1
      {'lower_b': 1, 'upper_b': n, 'inc': 1}

      9
      Lea B(i,,j)
      11
      lea
      1
      2
      NaN

      10
      Fpara
      5
      fpara
      0
      1
      NaN

      11
      Fpara
      5
      fpara
      0
      0
      NaN

      12
      s=0
      3
      assignment
      1
      0
      NaN

      13
      Para i=1,n,+1
      13
      para
      3*ceil(n) + 2
      0
      {'lower_b': 1, 'upper_b': n, 'inc': 1}

      14
      X=x+A(i,i)
      10
      assignment
      1
      1
      NaN

      15
      Fpara
      5
      fpara
      0
      0
      NaN

      16
      x=0
      3
      assignment
      1
      0
      NaN

      17
      Para i=1,n,+1
      13
      para
      (4*ceil(n) + 4)*ceil(n) + 2
      0
      {'lower_b': 1, 'upper_b': n, 'inc': 1}

      18
      Para j=1,n,+1
      13
      para
      4*ceil(n) + 2
      1
      {'lower_b': 1, 'upper_b': n, 'inc': 1}

      19
      Si (i==j)
      9
      si
      2
      2
      {'comparisons': 1}

      20
      x=x+B(i,j)
      10
      assignment
      1
      3
      NaN

      21
      Fsi
      3
      fsi
      0
      2
      NaN

      22
      Fpara
      5
      fpara
      0
      1
      NaN

      23
      Fpara
      5
      fpara
      0
      0
      NaN

      24
      Si (x>s)
      8
      si
      3
      0
      {'comparisons': 1}

      25
      Esc “x es mayor”
      16
      esc
      1
      1
      NaN

      26
      Esc x
      5
      esc
      1
      1
      NaN

      27
      sino
      4
      sino
      0
      1
      NaN

      28
      Esc “x no es mayor”
      19
      esc
      1
      1
      NaN

      29
      Fsi
      3
      fsi
      0
      0
      NaN

      30
      Pare
      4
      pare
      0
      0
      NaN

```python
test_for_data = {'lower_b': parse_expr('1'), 'upper_b': parse_expr('n'), 'inc': parse_expr(str(1))}
for_runtime_formula(test_for_data,2)
```

$\displaystyle 4 \operatorname{ceil}{\left(n \right)} + 2$

```python
ceil = sp.Function('ceil')
upper_bound = parse_expr('n')
lower_bound = parse_expr('3')
increment = parse_expr('2')
iterations = ceil((upper_bound-lower_bound)/increment)
iterations
```

$\displaystyle \operatorname{ceil}{\left(\frac{n}{2} - \frac{3}{2} \right)}$

```python
def get_runtime(syntax_complete):
    lines = syntax_complete.to_dict('records')
    runtime = parse_expr('0')
    for line in lines:
        if line['order'] is 0:
            runtime += line['runtime']
    return runtime
```

```python
str(sp.simplify(get_runtime(syntax_complete)))
```

    '10*ceil(n)**2 + 15*ceil(n) + 14'


# Announcements

* Chart of the Nuclides
* Let's talk about what submitting with a jupyter notebook would look like
* For future homeworks, you do not need to submit each problem as a separate page. Instead, make sure to tag *each problem* with the page it is associated with. That is, if you solved problems 3, 4, and 5 on page 2 of your homework, each one needs to be tagged as being on page 2.
* Number density problem!
* A quick review on the end of lecture on Monday

# Nuclear Energetics

## Learning Objectives

- Define: exothermic and endothermic reactions
- Differentiate exothermic and endothermic reactions
- Calculate binding energies
- Recognize the noation for various binary reactions
- Recognize the physics in common binary reactions
- Define the relationship between the Q value, mass, and energy in a reaction
- Explain the role of energy conservation in binary reactions
- Explain the role of charge conservation in binary reactions
- Calculate Q values for various reactions

## Endothermic and Exothermic Reactions

In any reaction (nuclear, mechanical, chemical) energy can be either emitted or absorbed.

- When energy is emitted by the reaction, this is called **_exothermic_**.
- When energy is absorbed into the reaction, this is called **_endothermic_**.

If we consider the energy in relation to mass via Einstein's equivalence, then it's very clear that a change in the mass of reactants will result in a change in energy ($\Delta E = \Delta M c^2$).

\begin{align}
\mbox{reactants} &\rightarrow \mbox{products}\\
A + B + \cdots &\rightarrow C + D + \cdots\\
\Delta M = (M_A + M_B + \cdots) &- (M_C + M_D + \cdots)\\
\implies \Delta E &= \left[(M_A + M_B + \cdots) - (M_C + M_D + \cdots)\right]c^2\\
\end{align}

(credit: BBC)

## Exercise: Think-pair-share

Which of the following are exothermic and which are endothermic?

- Boiling Water
- Lighting a match
- Freezing an ice cube
- Melting an ice cube
- Snow formation in the clouds
- Conversion of frost to water vapor on the grass

Harder ones:
- Formation of ion pairs (exothermic)
- Separation of ion pairs (endothermic)

The most important one:
- nuclear fission

# Nuclear Reactions

When we talk about nuclear reactions, we use a similar notation in chemistry with the reactants (our initial state) on the left and the products (after the reaction) on the right.

\begin{align}
\mbox{reactants} &\rightarrow \mbox{products}\\
A + B &\rightarrow C + D\\
\end{align}

There are four fundamental rules that must be followed to satisfy these reactions:
1. *Conservation of nucleons*
2. *Conservation of charge*
3. *Conservation of moomentum*
4. *Conservation of energy* - this includes kinetic and rest mass energy

### Exercise: Think-pair-share

Why do you think rule 1 is conservation of nucleons and not explicitly conservation of neutrons and protons?

# Binding Energy

Last lecture, we discussed how the binding energy tells us how *tightly bound* nucleons are in the nucleus. All nuclei will have a mass slightly less than their constituent parts. This represents the energy needed to keep the nucleons bound in the nucleus. The term we use for this is the binding energy.

## Binding Energy of the nucleus

A nucleus, with Z protons and N=A-Z neutrons will have a formation reaction that looks like:

\begin{align}
Z \mbox{ protons } + (A − Z) \mbox{ neutrons }\rightarrow \mbox{ nucleus}\left(^A_ZX\right) + BE.
\end{align}

It can be helpful to define the term $BE/c^2$:

\begin{align}
\frac{BE}{c^2} &=Zm_p +(A−Z)m_n − m\left(^A_ZX\right)\\
\end{align}

So, this means the binding energy is the difference between the constituent parts of the atom and its measured mass.

We also know that there exists a mass energy equivalence, so we can convert from this mass difference to an energy difference.

Thus, we can arrive at the nuclear binding energy from this:

\begin{align}
BE\left(^A_ZX\right) = \left[ZM(\left(^1_1H\right) + (A-Z)m_n - M\left(^A_ZX\right)\right]c^2
\end{align}

### Exercise: Nuclear binding energy

From your book, example 4.1: What is the binding energy of an alpha particle?

When calculating Q values and Binding Energies, make sure that your proton and electron masses *always* balance. This is why you'll see $M(^1_1H)$ rather than the proton mass in our balancing equations.

There are three ways one might choose:
\begin{align}
BE\left(^4_2 He\right) &= \left[2m_p + 2m_n\right]-m\left(^4_2He\right)\\
BE\left(^4_2 He\right) &= \left[2m_p + 2m_n\right]-M\left(^4_2He\right)\\
BE\left(^4_2 He\right) &= \left[2M\left(^1_1H\right) + 2m_n\right]-M\left(^4_2He\right)\\
\end{align}

**Think pair share**
Two of these are correct and one isn't. Which ones are which?

Let us proceed with the final method in our above equations

\begin{align}
\frac{BE}{c^2} &=Zm_p +(A−Z)m_n − m\left(^A_ZX\right)\\
\implies\frac{BE}{c^2} &=\left[ZM(\left(^1_1H\right) + (A-Z)m_n - M\left(^A_ZX\right)\right]\\
 &=\left[2M(\left(^1_1H\right) + 2m_n - M\left(^4_2He\right)\right]\\
 &=\left[2\left(1.0078250\right) + 2\left(1.0086649\right) - 4.0026032\right]\\
\end{align}

Thus:

\begin{align}
BE\left(^4_2 He\right) &= \frac{BE}{c^2}\times 931.5 (MeV/u) \\
&= 28.30 MeV.
\end{align}

## Binding Energy per nucleon

The BE/A versus A curve immediately suggests two ways to extract energy from the nucleus.

Binding energy curve **(average binding energy per nucleon in MeV against number of nucleons in nucleus)** for a number of relatively common (abundant) isotopes (not chosen systematically; almost anything with an occurence of over .2 was chosen though a few exceptions are in there, such as U235). A few important ones for the purposes of nuclear fusion and nuclear fission are marked, as well as iron-56, which sits at the highest point on this graph and cannot yield energy from fusion or fission.

From your book (Shultis and Faw)

> Most isotopes of an element are radioactive, i.e., their nuclei are unstable. To better understand what makes a nucleus stable, it is instructive to plot the BE per nucleon, BE/A, of all the isotopes of an element. For example, if one calculates the BE/A for all the isotopes of calcium (Z = 20), as is done in Example 4.2 for one isotope, and plots BE/A versus the neutron number N, a plot like that shown in Fig. 4.2 is obtained. The BE/A rapidly rises as N increase from A = 14 and reaches a broad maximum before rapidly decreasing as N increases above 30. It is in the broad maximum where the BE/A is highest that one finds the stable isotopes. The reason for the zig-zag shape in the maximum region is due to the fact that an isotope with an even number of neutrons tends to have a higher BE/A compared

### Fusion

**Two light nuclei $\longrightarrow$ one heavy nucleus**

\begin{align}
^{2}_1H + ^2_1H \longrightarrow ^{4}_2He
\end{align}

### Fission
**One heavy nucleus $\longrightarrow$ two lighter nuclei**

\begin{align}
^{235}_{92}U + ^1_0n \longrightarrow \left(^{236}_{92}U\right)^*  \longrightarrow ^{139}_{56}Ba +  ^{94}_{36}Kr + 3^1_0n
\end{align}

## Nucleon Separation Energy

The energy required to remove a single nucleon from a nucleus is called "separation energy". Consider the addition of a single neutron to form the nucleus of $^A_ZX$, i.e.,

\begin{align}
^{A-1}_ZX + ^1_0n \rightarrow ^{A}_ZX.
\end{align}

$S_n\left(^A_ZX\right)$ is the energy released in this reaction and is equivalent to the energy required to remove (or separate) a single neutron from the nucleus $^A_ZX$.

\begin{align}
S_n\left(^A_ZX\right) &= [m\left(^{A-1}_ZX\right) + m_n − m\left(^{A}_ZX\right)]c^2 \\
 &\simeq [M\left(^{A-1}_ZX\right) + m_n − M\left(^{A}_ZX\right)]c^2.
\end{align}

If we make the substition for nuclear binding energy, where binding energy is:

\begin{align}
BE\left(^A_ZX\right) = \left[ZM(\left(^1_1H\right) + (A-Z)m_n - M\left(^A_ZX\right)\right]c^2
\end{align}

We can see that the separation energy is the difference between the binding energies of the initial and final nucleons:

\begin{align}
S_n\left(^A_ZX\right) &= BE\left(^A_ZX\right) - BE\left(^{A-1}_ZX\right)
\end{align}

### $S_p$

The equation for the separation energy of the proton is very similar to the separation energy of the neutron.

\begin{align}
S_p\left(^{A}_ZX\right) &= [m\left(^{A-1}_{Z-1}X\right) + m_p − m\left(^{A}_ZX\right)]c^2 \\
 &\simeq [M\left(^{A-1}_{Z-1}X\right) + M\left(^1_1H\right) − M\left(^{A}_ZX\right)]c^2\\
\end{align}

## Exercise: Nucleon Separation Energy
(Example 4.3 in Shultis and Faw)

What is the binding energy of the last neutron in $^{16}_8O$? This is the energy released in the reaction:
\begin{align}
^{15}_8O + ^1_0n \rightarrow ^{16}_8O
\end{align}

So, we can use the equation above and plug in the mass values we can find in the book appendix.

\begin{align}
S_n\left(^{A}_ZX\right) &= [m\left(^{A-1}_ZX\right) + m_n − m\left(^{A}_ZX\right)]c^2 \\
 &\simeq [M\left(^{A-1}_ZX\right) + m_n − M\left(^{A}_ZX\right)]c^2\\
\implies S_n &= [m\left(^{15}_8O\right) + m_n − m\left(^{16}_8O\right)]c^2 \\
 &\simeq [M\left(^{15}_8X\right) + m_n − M\left(^{16}_8X\right)]c^2\\
 &=[15.0030654 + 1.00866492 − 15.9949146] u \times 931.5 MeV/u\\
 &= 15.66 MeV.
\end{align}

This is a very high value for a single nucleon.

### Think-Pair-Share

What does this high value mean about the comparative stability of $^{16}_8O$ and $^{15}_8O$?

### Think-Pair-Share

As engineers, why should we care about high neutron separation energies in materials?

## Binary Nuclear Reactions

We will refer to the reaction $x+X\longrightarrow Y+ y$ as :

\begin{align}
(x, y)
\end{align}

which is shorthand for $X(x,y)Y$. The very long form of this reaction is:

\begin{align}
^{A}_{Z}X + ^{A}_{Z}x \longrightarrow ^{A}_{Z}Y + ^{A}_{Z}y
\end{align}

### $(\alpha, p)$

This  was first seen in air by Rutherford. Protons were produced when alpha particles were absorbed by nitrogen in the air to make oxygen. That example follows.

\begin{align}
\alpha + ^{14}_{7}N \longrightarrow ^{17}_{8}O + ^{1}_{1}H
\end{align}

### $(\alpha, n)$

This  was first seen with beryllium by Chadwick. Neutrons were produced when alpha particles were absorbed by beryllium in the air to make carbon. That example follows.

\begin{align}
\alpha + ^{9}_{4}Be \longrightarrow ^{12}_{6}C + ^{1}_{0}n
\end{align}

### $(\gamma, n)$

Neutrons can also be produced when very energetic photons strike a nucleus. An example follows.

\begin{align}
\gamma + ^{2}_{1}H \longrightarrow ^{1}_{1}H + ^{1}_{0}n
\end{align}

### $(p, \gamma)$

This is typically called "radiative capture" of a proton.  An example follows.

\begin{align}
^1_1p + ^{7}_{3}Li \longrightarrow ^{8}_{4}Be + \gamma
\end{align}

Interestingly, in this example, the resulting beryllium nucleus is poorly bound and becomes two alphas.

### $(\gamma, \alpha n)$

Sometimes, more than two products can be produced. An example follows.

\begin{align}
\gamma + ^{17}_{8}O \longrightarrow ^{12}_{6}C + ^{4}_{2}He + ^{1}_{0}n
\end{align}

### $(n, p)$

Fast neutrons are very common in nculear reactor cores and can produce liberate protons.  An example follows.

\begin{align}
^{1}_{0}n + ^{16}_{8}O \longrightarrow ^{16}_{7}N + ^{1}_{1}p
\end{align}

### $(n, \gamma)$

Most free neutrons in a reactor are fated to be radiatively captured.

\begin{align}
^{1}_{0}n + ^{238}_{92}U \longrightarrow ^{239}_{92}U + \gamma
\end{align}

## Q-Value For a Reaction

Energy is conserved in nuclear reactions. Specifically, the _total energy_ is conserved.

The total energy, recall, includes rest mass energy as well as kinetic energy.

\begin{align}
\sum_i \left[E_i + m_ic^2\right] &= \sum_i\left[E'_i + m'_ic^2\right]\\
\mbox{where }&\\
E_i &= \mbox{the kinetic energy of the ith initial particle }\\
E'_i &=\mbox{the kinetic energy of the ith final particle }\\
m_i &= \mbox{rest mass of the ith initial particle}\\
m'_i &= \mbox{rest mass of the ith final particle}\\
\end{align}

Changes in kinetic energy between the initial and final states must be balanced by equivalent changes in total rest mass. **The Q-value is the kinetic energy gained in the reaction.**

\begin{align}
Q &= (\mbox{KE of final particles}) − (\mbox{KE of initial particles})\\
 &= \left(\sum_iE'_i\right) − \left(\sum_iE_i\right) \\
\end{align}

**The Q-value is equivalently the rest mass energy lost in the reaction.**

\begin{align}
Q &= (\mbox{rest mass of initial particles})c^2 − (\mbox{rest mass of final particles})c^2\\
  &= \left(\sum_im_i\right)c^2 − \left(\sum_im'_i\right)c^2 \\
\end{align}

### Think-pair-share: Exothermic or Endothermic

If $Q<0$, is the reaction exothermic or endothermic?

### Q-value in binary reactions

For an $X(x, y)Y$ reaction:

\begin{align}
Q &= \left(E_y + E_Y \right) − \left(E_x + E_X\right) \\
  &= \left( m_x + m_X\right)c^2 − \left( m_y + m_Y\right)c^2 \\
\end{align}

**If proton numbers (and therefore electron masses) are conserved,** we can use the neutral atom masses and simplify this to:

\begin{align}
Q &= \left(E_y + E_Y \right) − \left(E_x + E_X\right) \\
  &= \left( M_x + M_X\right)c^2 − \left( M_y + M_Y\right)c^2 \\
\end{align}

### Q-value in decay reactions
For a radioactive decay reaction:

\begin{align}
X\longrightarrow Y+ y
\end{align}

The corresponding Q-value is:
\begin{align}
Q&=(E_y +E_Y)\\
&=[m_X −(m_y +m_Y )]c^2 \\
\end{align}

Note that radioactive decay, being spontaneous, is **always exothermic**. So, $Q>0$ for all decay reactions.

### Total Charge Conservation

Charge is always conserved in nuclear reactions. Thus, we might get into trouble with reactions that drive a change in electron number. The mass of the electron should be managed carefully in these cases. In an $n, p$ reaction, a proton is ejected from the nucleus by the incident neutron. During this process, an orbital electron is usually also lost from the resulting product atom.  Thus, we must include an extra electron in the equation.

**Does the electron $_{-1}^0e$ belong on the right hand side (products) or the left hand side (reactants) of the following reaction?**

\begin{align}
^1_0n + ^{16}_8O \longrightarrow ^{16}_7N + ^1_1p
\end{align}

### Special case for changing proton number

The above procedure is further complicated in cases when the number of protons and the number of neutrons are not conserved. These reactions involve the nuclear weak force.

**The nuclear _weak force_ alters neutrons into protons and vice versa, usually inside the nucleus.**

The details are provided in an example in your book. Effectively, the neutrinos which moderate this reaction must be considered among the products and reactants. This is why we have neutrinos in $\beta$ decay equations (which we will talk about next week)

## Special case for excited products

Many of the reactions we're  most interested in as nuclear engineers involve an intermediate excited compound nucleus.

Let's consider Example 4.6 in your book regarding the reaction $^{10}B + ^1_0n \longrightarrow \left(^7Li\right)^*$. In short hand, this is an $(n,\alpha)$ reaction. Specifically,  $^{10}B(n,\alpha)^7Li^∗$. The product, $\left(^7Li\right)^*$ is left in an excited state. Specifically it is left 0.48 MeV above its ground state.

The ordinary neutral atomic masses approach would look like this:

\begin{align}
Q &= \left[m_n + M\left(^{10}_5B\right) −  M\left(^{4}_2He\right) - M\left(^{7}_3Li\right)^*\right]c^2 \\
\end{align}

But, the mass tabulated for $M\left(^{7}_3Li\right)$ is the ground state mass. So if we want to use that ground state mass to arrive at the solution, the extra energy contributing to the excited state needs to be accounted for.

\begin{align}
Q &= \left[m_n + M\left(^{10}_5B\right) −  M\left(^{4}_2He\right) - M\left(^{7}_3Li\right)\right]c^2 -0.48MeV\\
\end{align}

## Some final thoughts on density changes

```python

```


##### Copyright 2020 The Cirq Developers

```
#@title Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# https://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
```

# Cirq basics

    View on QuantumAI

    Run in Google Colab

    View source on GitHub

    Download notebook

This tutorial will teach the basics of how to use Cirq.  This tutorial will walk through how to use qubits, gates, and operations to create and simulate your first quantum circuit using Cirq.  It will briefly introduce devices, unitary matrices, decompositions, and optimizers.

This tutorial isn’t a quantum computing 101 tutorial, we assume familiarity of quantum computing at about the level of the textbook “Quantum Computation and Quantum Information” by Nielsen and Chuang.

For more in-depth examples closer to those found in current work, check out our tutorials page.

To begin, please follow the instructions for [installing Cirq](../install.md).

Note: this notebook relies on unreleased Cirq features. If you want to try these features, make sure you install cirq via `pip install cirq --pre`.

```
try:
    import cirq
except ImportError:
    print("installing cirq...")
    !pip install --quiet cirq --pre
    print("installed cirq.")
```

## Qubits

The first part of creating a quantum circuit is to define a set of qubits (also known as a quantum registers) to act on.

Cirq has three main ways of defining qubits:

*   `cirq.NamedQubit`: used to label qubits by an abstract name
*   `cirq.LineQubit`: qubits labelled by number in a linear array
*   `cirq.GridQubit`: qubits labelled by two numbers in a rectangular lattice.

Here are some examples of defining each type of qubit.

```
import cirq
import cirq_google

# Using named qubits can be useful for abstract algorithms
# as well as algorithms not yet mapped onto hardware.
q0 = cirq.NamedQubit('source')
q1 = cirq.NamedQubit('target')

# Line qubits can be created individually
q3 = cirq.LineQubit(3)

# Or created in a range
# This will create LineQubit(0), LineQubit(1), LineQubit(2)
q0, q1, q2 = cirq.LineQubit.range(3)

# Grid Qubits can also be referenced individually
q4_5 = cirq.GridQubit(4,5)

# Or created in bulk in a square
# This will create 16 qubits from (0,0) to (3,3)
qubits = cirq.GridQubit.square(4)
```

There are also pre-packaged sets of qubits called [Devices](../devices.md).  These are qubits along with a set of rules of how they can be used.  A `cirq.Device` can be used to apply adjacency rules and other hardware constraints to a quantum circuit.  For our example, we will use the `cirq_google.Foxtail` device that comes with cirq.  It is a 2x11 grid that mimics early hardware released by Google.

```
print(cirq_google.Foxtail)
```

    (0, 0)───(0, 1)───(0, 2)───(0, 3)───(0, 4)───(0, 5)───(0, 6)───(0, 7)───(0, 8)───(0, 9)───(0, 10)
    │        │        │        │        │        │        │        │        │        │        │
    │        │        │        │        │        │        │        │        │        │        │
    (1, 0)───(1, 1)───(1, 2)───(1, 3)───(1, 4)───(1, 5)───(1, 6)───(1, 7)───(1, 8)───(1, 9)───(1, 10)

## Gates and operations

The next step is to use the qubits to create operations that can be used in our circuit.  Cirq has two concepts that are important to understand here:

*   A `Gate` is an effect that can be applied to a set of qubits.
*   An `Operation` is a gate applied to a set of qubits.

For instance, `cirq.H` is the quantum [Hadamard](https://en.wikipedia.org/wiki/Quantum_logic_gate#Hadamard_(H)_gate) and is a `Gate` object.  `cirq.H(cirq.LineQubit(1))` is an `Operation` object and is the Hadamard gate applied to a specific qubit (line qubit number 1).

Many textbook gates are included within cirq.  `cirq.X`, `cirq.Y`, and `cirq.Z` refer to the single-qubit Pauli gates.  `cirq.CZ`, `cirq.CNOT`, `cirq.SWAP` are a few of the common two-qubit gates.  `cirq.measure` is a macro to apply a `MeasurementGate` to a set of qubits.  You can find more, as well as instructions on how to creats your own custom gates, on the [Gates documentation](../gates.ipynb) page.

Many arithmetic operations can also be applied to gates.  Here are some examples:

```
# Example gates
not_gate = cirq.CNOT
pauli_z = cirq.Z

# Using exponentiation to get square root gates
sqrt_x_gate = cirq.X**0.5

# Some gates can also take parameters
sqrt_sqrt_y = cirq.YPowGate(exponent=0.25)

# Example operations
q0, q1 = cirq.LineQubit.range(2)
z_op = cirq.Z(q0)
not_op = cirq.CNOT(q0, q1)
sqrt_iswap_op = cirq.SQRT_ISWAP(q0, q1)
```

## Circuits and moments

We are now ready to construct a quantum circuit.  A `Circuit` is a collection of `Moment`s. A `Moment` is a collection of `Operation`s that all act during the same abstract time slice.  Each `Operation` must have a disjoint set of qubits from the other `Operation`s in the `Moment`.  A `Moment` can be thought of as a vertical slice of a quantum circuit diagram.

Circuits can be constructed in several different ways.  By default, cirq will attempt to slide your operation into the earliest possible `Moment` when you insert it.

```
circuit = cirq.Circuit()
# You can create a circuit by appending to it
circuit.append(cirq.H(q) for q in cirq.LineQubit.range(3))
# All of the gates are put into the same Moment since none overlap
print(circuit)
```

    0: ───H───

    1: ───H───

    2: ───H───

```
# We can also create a circuit directly as well:
print(cirq.Circuit(cirq.SWAP(q, q+1) for q in cirq.LineQubit.range(3)))
```

    0: ───×───────────
          │
    1: ───×───×───────
              │
    2: ───────×───×───
                  │
    3: ───────────×───

Sometimes, you may not want cirq to automatically shift operations all the way to the left.  To construct a circuit without doing this, you can create the circuit moment-by-moment or use a different `InsertStrategy`, explained more in the [Circuit documentation](../circuits.ipynb).

```
# Creates each gate in a separate moment.
print(cirq.Circuit(cirq.Moment([cirq.H(q)]) for q in cirq.LineQubit.range(3)))
```

    0: ───H───────────

    1: ───────H───────

    2: ───────────H───

### Circuits and devices

One important consideration when using real quantum devices is that there are often hardware constraints on the circuit.  Creating a circuit with a `Device` will allow you to capture some of these requirements.  These `Device` objects will validate the operations you add to the circuit to make sure that no illegal operations are added.

Let's look at an example using the Foxtail device.

```
q0 = cirq.GridQubit(0, 0)
q1 = cirq.GridQubit(0, 1)
q2 = cirq.GridQubit(0, 2)
adjacent_op = cirq.CZ(q0, q1)
nonadjacent_op = cirq.CZ(q0, q2)

# This is an unconstrained circuit with no device
free_circuit = cirq.Circuit()
# Both operations are allowed:
free_circuit.append(adjacent_op)
free_circuit.append(nonadjacent_op)
print('Unconstrained device:')
print(free_circuit)
print()

# This is a circuit on the Foxtail device
# only adjacent operations are allowed.
print('Foxtail device:')
foxtail_circuit = cirq.Circuit(device=cirq_google.Foxtail)
foxtail_circuit.append(adjacent_op)
try:
    # Not allowed, will throw exception
    foxtail_circuit.append(nonadjacent_op)
except ValueError as e:
    print('Not allowed. %s' % e)

```

    Unconstrained device:
    (0, 0): ───@───@───
               │   │
    (0, 1): ───@───┼───
                   │
    (0, 2): ───────@───

    Foxtail device:
    Not allowed. Non-local interaction: cirq.CZ.on(cirq.GridQubit(0, 0), cirq.GridQubit(0, 2)).

## Simulation

The results of the application of a quantum circuit can be calculated by a `Simulator`.  Cirq comes bundled with a simulator that can calculate the results of circuits up to about a limit of 20 qubits.  It can be initialized with `cirq.Simulator()`.

There are two different approaches to using a simulator:

*   `simulate()`:  Since we are classically simulating a circuit, a simulator can directly access and view the resulting wave function.  This is useful for debugging, learning, and understanding how circuits will function.
*   `run()`:  When using actual quantum devices, we can only access the end result of a computation and must sample the results to get a distribution of results.  Running the simulator as a sampler mimics this behavior and only returns bit strings as output.

Let's try to simulate a 2-qubit "Bell State":

```
# Create a circuit to generate a Bell State:
# 1/sqrt(2) * ( |00⟩ + |11⟩ )
bell_circuit = cirq.Circuit()
q0, q1 = cirq.LineQubit.range(2)
bell_circuit.append(cirq.H(q0))
bell_circuit.append(cirq.CNOT(q0,q1))

# Initialize Simulator
s=cirq.Simulator()

print('Simulate the circuit:')
results=s.simulate(bell_circuit)
print(results)
print()

# For sampling, we need to add a measurement at the end
bell_circuit.append(cirq.measure(q0, q1, key='result'))

print('Sample the circuit:')
samples=s.run(bell_circuit, repetitions=1000)
# Print a histogram of results
print(samples.histogram(key='result'))
```

    Simulate the circuit:
    measurements: (no measurements)
    output vector: 0.707|00⟩ + 0.707|11⟩

    Sample the circuit:
    Counter({3: 537, 0: 463})

### Using parameter sweeps

Cirq circuits allow for gates to have symbols as free parameters within the circuit.  This is especially useful for variational algorithms, which vary parameters within the circuit in order to optimize a cost function, but it can be useful in a variety of circumstances.

For parameters, cirq uses the library `sympy` to add `sympy.Symbol` as parameters to gates and operations.

Once the circuit is complete, you can fill in the possible values of each of these parameters with a `Sweep`.  There are several possibilities that can be used as a sweep:

*   `cirq.Points`: A list of manually specified values for one specific symbol as a sequence of floats
*   `cirq.Linspace`: A linear sweep from a starting value to an ending value.
*   `cirq.ListSweep`: A list of manually specified values for several different symbols, specified as a list of dictionaries.
*   `cirq.Zip` and `cirq.Product`: Sweeps can be combined list-wise by zipping them together or through their Cartesian product.

A parameterized circuit and sweep together can be run using the simulator or other sampler by changing `run()` to `run_sweep()` and adding the sweep as a parameter.

Here is an example of sweeping an exponent of a X gate:

```
import matplotlib.pyplot as plt
import sympy

# Perform an X gate with variable exponent
q = cirq.GridQubit(1,1)
circuit = cirq.Circuit(cirq.X(q) ** sympy.Symbol('t'),
                       cirq.measure(q, key='m'))

# Sweep exponent from zero (off) to one (on) and back to two (off)
param_sweep = cirq.Linspace('t', start=0, stop=2, length=200)

# Simulate the sweep
s = cirq.Simulator()
trials = s.run_sweep(circuit, param_sweep, repetitions=1000)

# Plot all the results
x_data = [trial.params['t'] for trial in trials]
y_data = [trial.histogram(key='m')[1] / 1000.0 for trial in trials]
plt.scatter('t','p', data={'t': x_data, 'p': y_data})

```

## Unitary matrices and decompositions

Most quantum operations have a unitary matrix representation.  This matrix can be accessed by applying `cirq.unitary()`.  This can be applied to gates, operations, and circuits that support this protocol and will return the unitary matrix that represents the object.

```
print('Unitary of the X gate')
print(cirq.unitary(cirq.X))

print('Unitary of SWAP operator on two qubits.')
q0, q1 = cirq.LineQubit.range(2)
print(cirq.unitary(cirq.SWAP(q0, q1)))

print('Unitary of a sample circuit')
print(cirq.unitary(cirq.Circuit(cirq.X(q0), cirq.SWAP(q0, q1))))
```

    Unitary of the X gate
    [[0.+0.j 1.+0.j]
     [1.+0.j 0.+0.j]]
    Unitary of SWAP operator on two qubits.
    [[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]]
    Unitary of a sample circuit
    [[0.+0.j 0.+0.j 1.+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 1.+0.j]
     [0.+0.j 1.+0.j 0.+0.j 0.+0.j]]

### Decompositions

Many gates can be decomposed into an equivalent circuit with simpler operations and gates.  This is called decomposition and can be accomplished with the `cirq.decompose` protocol.

For instance, a Hadamard H gate can be decomposed into X and Y gates:

```
print(cirq.decompose(cirq.H(cirq.LineQubit(0))))
```

    [(cirq.Y**0.5).on(cirq.LineQubit(0)), cirq.XPowGate(exponent=1.0, global_shift=-0.25).on(cirq.LineQubit(0))]

Another example is the 3-qubit Toffoli gate, which is equivalent to a controlled-controlled-X gate.  Many devices do not support a three qubit gate, so it is important

```
q0, q1, q2 = cirq.LineQubit.range(3)
print(cirq.Circuit(cirq.decompose(cirq.TOFFOLI(q0, q1, q2))))
```

    0: ───T────────────────@─────────────────────────────────@─────────────────────────────@────────────────────────────@───────────────────────────────────────
                           │                                 │                             │                            │
    1: ───T───────Y^-0.5───@───Y^0.5────@───T^-1────Y^-0.5───@────────Y^0.5───@───Y^-0.5───@──────Y^0.5────@───Y^-0.5───@──────Y^0.5────@───────────────────────
                                        │                                     │                            │                            │
    2: ───Y^0.5───X────────T───Y^-0.5───@───Y^0.5───T────────Y^-0.5───────────@───Y^0.5────T^-1───Y^-0.5───@───Y^0.5────T^-1───Y^-0.5───@───Y^0.5───Y^0.5───X───

The above decomposes the Toffoli into a simpler set of one-qubit gates and CZ gates at the cost of lengthening the circuit considerably.

Some devices will automatically decompose gates that they do not support.  For instance, if we use the `Foxtail` device from above, we can see this in action by adding an unsupported SWAP gate:

```
swap = cirq.SWAP(cirq.GridQubit(0, 0), cirq.GridQubit(0, 1))
print(cirq.Circuit(swap, device=cirq_google.Foxtail))
```

    (0, 0): ───S^-1───Y^-0.5───@───S^-1───Y^0.5───X^0.5───@───S^-1───X^-0.5───@───S^-1───Z───
                               │                          │                   │
    (0, 1): ───Z──────Y^-0.5───@───S^-1───Y^0.5───X^0.5───@───S^-1───X^-0.5───@───S^-1───S───

### Optimizers

The last concept in this tutorial is the optimizer.  An optimizer can take a circuit and modify it.  Usually, this will entail combining or modifying operations to make it more efficient and shorter, though an optimizer can, in theory, do any sort of circuit manipulation.

For example, the `cirq.merge_single_qubit_gates_to_phxz` optimizer will take consecutive single-qubit operations and merge them into a single `PhasedXZ` operation.

```
q=cirq.GridQubit(1, 1)
c=cirq.Circuit(cirq.X(q) ** 0.25, cirq.Y(q) ** 0.25, cirq.Z(q) ** 0.25)
print(c)
c = cirq.merge_single_qubit_gates_to_phxz(c)
print(c)
```

    (1, 1): ───X^0.25───Y^0.25───T───
               ┌                           ┐
    (1, 1): ───│ 0.5  +0.707j -0.   -0.5j  │───────────
               │ 0.354+0.354j  0.146+0.854j│
               └                           ┘

Other optimizers can assist in transforming a circuit into operations that are native operations on specific hardware devices.  You can find more about optimizers and how to create your own elsewhere in the documentation.

## Next steps

After completing this tutorial, you should be able to use gates and operations to construct your own quantum circuits, simulate them, and to use sweeps.  It should give you a brief idea of the commonly used

There is much more to learn and try out for those who are interested:

* Learn about the variety of [Gates](../gates.ipynb) available in cirq and more about the different ways to construct [Circuits](../circuits.ipynb)
* Learn more about [Simulations](../simulation.ipynb) and how it works.
* Learn about [Noise](../noise.ipynb) and how to utilize multi-level systems using [Qudits](../qudits.ipynb)
* Dive into some [Examples](_index.yaml) and some in-depth tutorials of how to use cirq.

Also, join our [cirq-announce mailing list](https://groups.google.com/forum/#!forum/cirq-announce) to hear about changes and releases or go to the [cirq github](https://github.com/quantumlib/Cirq/) to file issues.


```python
%matplotlib inline
from jocument import *
import IPython
import time
import numpy
import pandas
import matplotlib.pyplot as plt
from matplotlib import colors

# Create the references objects
tables = Labels('Table')
figures = Labels('Figure')
citations = Citations()
footnotes = Footnotes()

# Create some footnotes
footnotes.add("most_footnotes",
'''Most footnotes will be defined in the same cell as the footnotes object is created.''')
footnotes.add("links",
'''You can add links in a footnote like
<a href=https://www.youtube.com/watch?v=dQw4w9WgXcQ target=_blank>this</a>.  Important to note that
you don't use single or double quotes in the definition of the href attribute
''')
footnotes.add('math',
'''It's perfectly possible to add math in a footnote too.  For example $e^{i\pi} + 1 = 0$''')

# Create some citations
citations.reference('Darwin C', '<a href=http://graphics8.nytimes.com/packages/images/nytint/docs/charles-darwin-on-the-origin-of-species/original.pdf>On The Origin Of Species</a>', 'London (1859)')
citations.reference('Einstein, A', 'On The Influence of Gravity on the Propagation of Light', 'Annalen der Physik, <strong>35</strong>: 898-908 (1911)')

```

## Jocument Tutorial Notebook

This notebook is a tutorial on how to use the `jocument` classes and functions to produce nice
looking reports and presentations.

#### Prerequisites
-   Make sure you jave `jocument` installed.
-   You should also install the standard set of notebook extensions
-   Make sure that `python markdown` is enabled and (for presentations) that you've installed Rise.
-   Run all the cells in this notebook  before getting started.

### Getting started

You can use this notebook as a guide or create a new notebook and follow the instructions as we go along.

The first thing you need is to create a code cell at the top of your notebook which contains (at a minimum)
the following code.

```python
from jocument import *
```

In general, there are going to be a number of other things you'll need in your notebook and in this
notebook (like so many others) the first cell
should contain

```python
%matplotlib inline
from jocument import *
import IPython
import time
import numpy
import pandas
import matplotlib.pyplot as plt
from matplotlib import colors
```

In the next cell (or the same cell), add the following four lines of code

```python
tables = Labels('Table')
figures = Labels('Figure')
citations = Citations()
footnotes = Footnotes()
```

This creates four global variables in the notebook.

-   `tables` which is a `jocument.Label` object.  This will be used to store the labels for the tables that we create
-   `figures` which is also a `jocument.Label` object.  This will be used to store the labels for the figures we create
-   `citations` which is a `jocument.Citations` object.  This object handles citations (references).
-   `footnotes` which is a `jocument.Footnotes` object.  This object will handle our footnotes.

Below the definition of the footnotes variable, we are going to add our citations and footnotes in this cell
at the top of the notebook.  Note that the citations and footnotes can be added anywhere in the notebook
but defining them in the same cell as the creation of the objects avoids having to run cells in a particular
order.

Let's define some footnotes.  In the same cell as the `footnotes` object is created, add the following
```python
footnotes.add("most_footnotes",
'''Most footnotes will be defined in the same cell as the footnotes object is created.''')
footnotes.add("links",
'''You can add links in a footnote like
<a href=https://www.youtube.com/watch?v=dQw4w9WgXcQ target=_blank>this</a>.  Important to note that
you don't use single or double quotes in the definition of the href attribute
''')
footnotes.add('math',
'''It's perfectly possible to add math in a footnote too.  For example $e^{i\pi} + 1 = 0$''')
```

And while we're at it, let's add some citations too
```python
citations.reference('Darwin C', '<a href=http://graphics8.nytimes.com/packages/images/nytint/docs/charles-darwin-on-the-origin-of-species/original.pdf>On The Origin Of Species</a>', 'London (1859)')
citations.reference('Einstein, A', 'On The Influence of Gravity on the Propagation of Light', 'Annalen der Physik, <strong>35</strong>: 898-908 (1911)')
```

Now how do we do something with this?  Well in a mark down cell, if you write
```markdown
    ### Introduction to footnotes
    It is possible to add a footnote in a markdown cell by using the python
    markdown functionality.  This allows you to insert and run any python within
    double curly braces inside a markdown cell.  For the first note, we will
    use a footnote which is defined in the first
    cell{{print("{"+"{" + "footnotes.ref('most_footnotes')" + "}"+"}")}}.  There
    are other features of footnotes which are generally
    useful{{print("{"+"{" + "footnotes.ref('links')" + "}"+"}")}} and some which
    are more useful in mathematical
    papers{{print("{"+"{" + "footnotes.ref('math')" + "}"+"}")}}

    Note that we are referring to each footnote by its "friendly name".  Sometimes
    you want to refer to footnotes by their number and in this case you refer
    to Footnote {{print("{"+"{" + "footnotes.num('most_footnotes')" + "}"+"}")}}.
```

Having inserted this in a markdown cell, you also need to output the footnotes somewhere so
for the moment insert a code cell after the markdown cell and insert the line

```python
IPython.display.HTML(footnotes.output())
```

Generally, of course, this cell will go right at the end of your document.  This is what you should get:

> ### Introduction to footnotes
> It is possible to add a footnote in a markdown cell by using the python
>  markdown functionality.  This allows you to insert and run any python within
>  double curly braces inside a markdown cell.  For the first note, we will
>  use a footnote which is defined in the first
> cell{{footnotes.ref('most_footnotes')}}.  There
> are other features of footnotes which are generally
> useful{{footnotes.ref('links')}} and some which
> are more useful in mathematical
> papers{{footnotes.ref('math')}}

> Note that we are referring to each footnote by its "friendly name".  Sometimes
> you want to refer to footnotes by their number and in this case you refer
> to Footnote {{footnotes.num('most_footnotes')}}.

```python
IPython.display.HTML(footnotes.output())
```

Most footnotes will be defined in the same cell as the footnotes object is created.↵

You can add links in a footnote like this.  Important to note that you don't use single or double quotes in the definition of the href attribute ↵

It's perfectly possible to add math in a footnote too.  For example $e^{i\pi} + 1 = 0$↵

This is the default styling of footnotes and if you don't like it, there's a way of changing it which
we will be investigating later.

The footnotes are hyperlinks to where the footnotes are output at the bottom of this

$$
\begin{equation}\label{eq:1}

e = \sin{\theta}

\end{equation}
$$

\begin{theorem}\label{theo:1}

The theorem is that $e = \sqrt{-1}$

\end{theorem}

\begin{remark}\label{rem:1}

This is a remark

\end{remark}

## Introduction

Systematic trading (or algorithmic, quant, model driven, computerised...) is often characterised as "complicated" or
"complex".  Indeed, much of the commentary by the press and by investors focuses on this perceived complexity.  In some
ineffable way, the fact that systematic trading is complicated indicates that there is something unsavoury or
underhand about it.  Complicated trading strategies cause market chaos, excess volatility, flash
crashes (both down and up)...there is just something "wrong" about them.

Whilst we would naturally strongly disagree with the characterisation of systematic trading as "wrong", in this
post, we would like to take issue with the conflating of two terms.  In the press and in the industry, the words
"complex" and "complicated" are often used interchangably but they are strictly not the same thing and by
understanding the difference, we may have more insight into the nature of systematic trading and how
it interacts with the financial ecosystem.

## Complicated

How do we define something as complicated?  If you're a dog then an alarm clock is complicated and incomprehensible.  If you've
never seen American Football before, it seems complicated and incomprehensible. The Space Shuttle,
an iPhone, Quantum Mechanics, the ISDA Credit Default swap conventions are all complicated.  However, they're not incomprehensible.
**Somebody** understands them or at least understands the some of the components and understands how the components work
together.  If you repeat inputs into the Shuttle, your iPhone and Credit Default Swaps, they behave the same way every
time.{{footnotes.ref("most_of_the_time")}}

Is being complicated an undesirable property of a process or system?  This mostly depends on what the system
is.  Making something unnecessarily complicated might not be a good idea.  We could open cans of beans using computer controlled
laser beams or enter into full legal multi-party debt obligation documentation when one buys a round of drinks at the pub
but that would be stupidly complicated compared to a can opener or just informally assuming everybody buys a round eventually.

However, excessive simplicity might not be good either.  Managing air traffic around an airport by having a person on top of a tall building with a pair of binoculars is pretty simple compared to the current system of ADS-B{{footnotes.ref('adsb')}}, RADAR and a centralised air traffic control{{footnotes.ref('airtraffic')}} infrastructure.  But does anybody want a non-complicated air traffic control?  Leeches are a simpler solution to illness than MRI scanners.

So in some cases complicated systems are either a good thing or, even more often, they are a result of the requirements for the system.  A example of this is telephony.  When telephony first started, users had a box in their house with a microphone and speaker.  This box
was connected by a length of copper wire to a local exchange which was manned (or more precisely wommaned) by operators.  You pressed
the receiver hook a few times to speak to the operator and told them who you wanted to speak to.  The operator connected your line
directly to somebody else's line using a patch cable with a 1/4 inch jack plug{{footnotes.ref('guitar')}} and there was effectively
a copper wire directly connecting your microphone to the speaker at the other end and a wire connecting your speaker to
their microphone.  Simple.

However, there are a number of problems

-   This isn't very scalable.  Imagine how many jack plug holes one would need for London or Manhattan.
-   As the local systems get connected to each other both within a country and internationally, setting up calls can
    be exceptionally difficult and time consuming.
-   Whilst one often wants to phone a location when one is making a business call ("Hello is that British Airways?"),
    for personal calls, I want to call a person, not a location ("Hello, is that Brian's house?  I'd like to speak to Brian.")

Over approximately 130 years{{footnotes.ref('firstexchange')}} telephony has become much more complicated.  Mechanical (and then electronic) direct dial exchanges and multiple undersea cables have connected together all the fixed line phones in the world.
Digital signal processing techniques allowed multiplexing{{footnotes.ref('multiplex')}} many conversations on a single wire or fiber.

In the early 1980s, mobile telephony took off and mobile telephony is astonishingly complicated.  Identifying how to
contact a single phone (which potentially could be in any location in the world) and setting up the connection,
negotiating protocols, and billing is extremely complicated.

The advances in the telephony have been required handsets to become more and more complicated.  A microphone,
a speaker and a bell has evolved into the iPhone X.  Gigabytes of operating system and application code run by a microprocessor containing nearly 7 billion transistors.  It is debatable whether or not any single person can completely
understand an iPhone given how complicated it is.  Is being complicated bad in this case?  For those of you who sometimes struggle
with new features and interfaces, it may seem like a bad thing but overall the complication has added astonishing functionality
to telephony which is considered desirable and (probably) good.

Although all this is very complicated -- too complicated for anybody to understand -- these types of highly complicated
systems have certain characteristics

-   The complicated system can be subdivided into smaller less complicated parts.
-   This subdivision can be done almost indefinitely until the small component is easily understandable.  For example, a single line
    of computer code or a single transistor.
-   When these simple functional "atoms" are combined, they work together in well defined and predictable ways
-   Feedback between components is limited, predictable and often linear.
-   The reaction of the complicated system to identical inputs are identical and predictable.{{footnotes.ref('randomness')}}

Let's look at a mathematical example.  Here's an equation which describes a curve:

$$
\left(\left(\frac x7\right)^2\sqrt{\frac{||x|-3|}{|x|-3}}+\left(\frac y3\right)^2\sqrt{\frac{\left|y+\frac{3\sqrt{33}}7\right|}{y+\frac{3\sqrt{33}}7}}-1 \right)
$$

$$
\qquad \qquad \left(\left|\frac x2\right|-\left(\frac{3\sqrt{33}-7}{112}\right)x^2-3+\sqrt{1-(||x|-2|-1)^2}-y \right)
$$

$$
\qquad \qquad \left(3\sqrt{\frac{|(|x|-1)(|x|-.75)|}{(1-|x|)(|x|-.75)}}-8|x|-y\right)\left(3|x|+.75\sqrt{\frac{|(|x|-.75)(|x|-.5)|}{(.75-|x|)(|x|-.5)}}-y \right)
$$

$$
\qquad \qquad \left(2.25\sqrt{\frac{(x-.5)(x+.5)}{(.5-x)(.5+x)}}-y \right)
$$

$$
\qquad \qquad \left(\frac{6\sqrt{10}}7+(1.5-.5|x|)\sqrt{\frac{||x|-1|}{|x|-1}} -\frac{6\sqrt{10}}{14}\sqrt{4-(|x|-1)^2}-y\right)=0
$$

There's no doubt that this is a pretty complicated equation.  In fact, I doubt anybody could look at this equation and work
out what this equation describes.  However, the thing it describes isn't very complex.  It's this:

```python
x = np.linspace(0, 2*np.pi, 50)
y = np.sin(x)
y2 = y + 0.1 * np.random.normal(size=x.shape)

fig, ax = plt.subplots()
ax.plot(x, y, 'k--')
ax.plot(x, y2, 'ro')

# set ticks and tick labels
ax.set_xlim((0, 2*np.pi))
ax.set_xticks([0, np.pi, 2*np.pi])
ax.set_xticklabels(['0', '$\pi$', '2$\pi$'])
ax.set_ylim((-1.5, 1.5))
ax.set_yticks([-1, 0, 1])

# Only draw spine between the y-ticks
ax.spines['left'].set_bounds(-1, 1)
# Hide the right and top spines
ax.spines['right'].set_visible(False)
ax.spines['top'].set_visible(False)
# Only show ticks on the left and bottom spines
ax.yaxis.set_ticks_position('left')
ax.xaxis.set_ticks_position('bottom')

plt;
```

{{figures.add('batman', 'The Batman Curve')}}

The result of that complicated equation isn't actually that complicated.

## Complex

Complex systems are different.  Whilst there are formal definitions of complexity{{footnotes.ref('complexity')}} there are
complex systems all around us.  Complex systems are sometimes hard to understand.  The components interact in strange
unpredictable ways, they're difficult or impossible to forecast even though they're easy to describe.

The classic example of a complex system is weather.  Whilst it is easy to describe the state of the weather at any one time,
forecasting the weather is a notoriously difficult problem.  Indeed, even with almost unlimited computing power, any forecast
beyond 10 days is little better than using the historical average of temperature and precipitation.  Butterflies flapping their
tiny hurricane-causing wings is a tired old cliché but does try to encapsulate a sensitivity of the weather to initial
conditions.  Another way of thinking about this sensitivity is as a lack of long term memory.{{footnotes.ref('chaos')}}

So let's compare our complicated equation above to this simple equation

\begin{equation}
z_{n+1} = z^{2}_{n} + c
\end{equation}

How does this equation work?  The numbers $z$ and $c$ are complex{{footnotes.ref('complexnum')}} and they define a
plane.  For each value of $c$ (basically an $x,y$ pair) we start with $z_0=0 + i0$ and the next value $z_1$ is found from the equation.  For the first iteration, it is obviously $c$.  Now we use the equation again to get $z_2 = c^2 + c$.  As we keep going, there are two things that can happen to $z$

-   $z$ can end up going to infinity.
-   $z$ remains bounded.

So we can classify each $c$ as either one which leads to an infinite $z$ or one which leads to a bounded $z$.  This should be pretty simple right?  If $c$ is a normal positive *real* number then this classification is very simple.  If $c$ > 0.25 or $c$ < -2.0
then the equation is unbounded.  If $c$ is between -2 and 0.25 then it's bounded.  Simple...

But, when we make the imaginary part of the number non zero, something very complex indeed happens.{{footnotes.ref('zoom')}}

```python
"""
===================================
Shaded & power normalized rendering
===================================

The Mandelbrot set rendering can be improved by using a normalized recount
associated with a power normalized colormap (gamma=0.3). Rendering can be
further enhanced thanks to shading.

The `maxiter` gives the precision of the computation. `maxiter=200` should
take a few seconds on most modern laptops.
"""
import numpy as np

def mandelbrot_set(xmin, xmax, ymin, ymax, xn, yn, maxiter, horizon=2.0):
    X = np.linspace(xmin, xmax, xn, dtype=np.float32)
    Y = np.linspace(ymin, ymax, yn, dtype=np.float32)
    C = X + Y[:, None]*1j
    N = np.zeros(C.shape, dtype=int)
    Z = np.zeros(C.shape, np.complex64)
    for n in range(maxiter):
        I = np.less(abs(Z), horizon)
        N[I] = n
        Z[I] = Z[I]**2 + C[I]
    N[N == maxiter-1] = 0
    return Z, N

from matplotlib import colors

xmin, xmax, xn = -2.25, +0.75, 1500
ymin, ymax, yn = -1.25, +1.25, 1250
maxiter = 200
horizon = 2.0 ** 40
log_horizon = np.log(np.log(horizon))/np.log(2)
Z, N = mandelbrot_set(xmin, xmax, ymin, ymax, xn, yn, maxiter, horizon)

# Normalized recount as explained in:
# https://linas.org/art-gallery/escape/smooth.html
# https://www.ibm.com/developerworks/community/blogs/jfp/entry/My_Christmas_Gift

# This line will generate warnings for null values but it is faster to
# process them afterwards using the nan_to_num
with np.errstate(invalid='ignore'):
    M = np.nan_to_num(N + 1 -
                      np.log(np.log(abs(Z)))/np.log(2) +
                      log_horizon)

dpi = 72
width = 7
height = 10*yn/xn
fig = plt.figure(figsize=(width, height), dpi=dpi)
ax = fig.add_axes([0.0, 0.0, 1.0, 1.0], frameon=False, aspect=1)

# Shaded rendering
light = colors.LightSource(azdeg=315, altdeg=10)
M = light.shade(M, cmap=plt.cm.gray, vert_exag=20.0,
                norm=colors.PowerNorm(0.3), blend_mode='hsv')
plt.imshow(M, extent=[xmin, xmax, ymin, ymax], interpolation="bicubic")
ax.set_xticks([])
ax.set_yticks([])
plt.show()
```

{{figures.add('hello', 'The hello')}}

{{figures.add('mandelbrot', 'The Mandelbrot Set')}}
This sort of emergent complexity arising from relatively simple rules has been an area of active research for
more than 30 years and not only generates some beautiful pictures but also has provided some deep insight into dynamical
systems.

## What's this got to do with finance?

We've produced some pretty graphs{{footnotes.ref('batman')}} but what, if anything, has this to do with Systematic Trading,
finance or algorithms?

Very often when investors, commentators and practitioners are thinking about or discussing systematic trading
the two concepts of being complicated or being complex are used interchangeably and incorrectly.

#### Some things in finance are complicated
Systematic trading is very complicated.  Or, at least, it **should** be very complicated.    Writing computer code libraries,
combining them into systems and then connecting these systems both to themselves and to market venues thousands of
miles away is a very complicated task.  This is not an endeavour where an Excel spreadsheet and a couple of VBA
macros is going to work very well.  Whilst a single model running on a single stock in a simple brokerage account
*may* be able to be work reliably with a simple system, it's rare that a simple model running on one asset really
is an investable proposition.

As the number of assets, the number of models and the number of funds increases, the system necessarily becomes more
complicated.  However, the individual components don't necessarily become more complicated.  They remain
understandable and predictable.  Of course, there is always the chance that a bug is introduced into the
system or individual components can interact in counter-intuitive ways but careful and rigorous
regression testing before adding a new component, vehicle, model or asset to the system can
deal with these problems before they create issues in a live running system.

There are other examples of complicated things in finance being considered.  Vanilla European options were considered complicated
and scary for many years.  Only the *cognoscenti* could trade them and anybody who understood what "the greeks" were was thought
of as a mega-brain geek.  There was nothing complex about options, they were just a little complicated.  Tranched credit products
are sometimes complicated.

Investors, journalists and even some practitioners find these complicated systems daunting and worrying.  The term
"Black Box" is used in a very pejorative way.  Some worry that complicated systems which they don't understand
are causing market volatility{{footnotes.ref('neverup')}} in some complicated way which they can't really
explain.

It appears that there is something uniquely scary about things in finance where the "complicated" part
involves mathematics, statistics or computer code.  Many private or public equity transactions involve astonishingly
complicated legal documents.  It is likely that no one person understands all of the
documentation {{footnotes.ref('barely')}} but for some reason this causes less consternation and fear than a
few lines of mathematics which a mathematically competent 14 year old could understand.

#### Some things in finance are complex

In contrast to complicated things, complex things are very hard (and often impossible) to forecast.  First and foremost of the complex things
in finance is "the market".  We aren't talking about any specific market like the NYSE or the CME Eurodollar market.  We're talking
about an idealised market which could just as easily refer to high frequency trading in IBM on the NASDAQ to an open outcry
market in 17th century Amsterdam.  What characteristics do these idealised markets have?

-   Multiple participants both on the buy and sell side
-   Participants are generally heterogeneous.  Some may be market makers, some may be hedgers, some may be speculators
-   Each participant has a goal which is generally known only to them
-   Some proportion of the market participants are "uninformed" or "noise traders"
-   A single security is (generally) traded in a single location
-   Arbitrage{{footnotes.ref('arbitrage')}} opportunities between identical securities are "rapidly" arbitraged away.

These rather simple characteristics generate extremely complex behaviour.  To a very good approximation for the
vast majority of participants in the market, it is impossible to forecast the market dynamics over a few seconds,
minutes, hours or days.

To give an indication of why it's unlikely that anybody can forecast a market in the short term, let's assume that
a participant in the market can forecast just the direction of the market move tomorrow.  All this person knows
is whether or not the market will go up or down tomorrow.  She doesn't know how much the move is, she just knows the direction
of the move in the SP500 tomorrow. If the prediction is up, she buys the SP500 on the close of the previous day, holds for one
day and then changes
her position at the end of the following day on the basis of whether or not the market will go up or down on the following day.

If our super trader starts with \\$1 on the 1st of January 2000 then by the end of 2018 our trader has something slightly
over \\${{'{:,.0f}'.format(super_trader_wealth)}} and will have had a Sharpe ratio of {{'{:,.0f}'.format(super_trader_sharpe)}}.
The trader's wealth of {{'{:,.0f} petadollars'.format(super_trader_wealth/1E15)}} is approximately
200 times the total global wealth{{footnotes.ref('totalwealth')}} in the world.  Since very few, if any, people have 18 year track records this good{{footnotes.ref('problems')}}, we can conclude
that even forecasting up or down is really really difficult.

This is a direct result of the complexity of market dynamics.  Complex systems are difficult to forecast due to their
extreme dependence on initial conditions and non-linear relationships between the components.

So is any trading or financial process doomed to failure?  Does complexity ensure that the efficient market hypothesis is
true?  Slightly surprisingly, the answer to this is no.

## Complexity requires complicated processes

For our final example, let's assume that we have a very poor quality forecast of whether or not a security will
rise or fall over a year.  The probability that this forecast is right is 51%.  Making some simple assumptions{{footnotes.ref('assumptions')}} about the nature of the security, our forecast would enable us to make about 0.25%
annually.  At its heart, this poor performance is a
direct result of the near complete randomness of markets which is a direct result of the highly complex nature of
markets in general.  Obviously, the process to actually trade this single forecast isn't very complicated.  The "system"
makes the forecast on the 1st of January (up/down) and on the basis of this, you buy or sell.  Then ignore the market,
take the position off on the 31st of December and see what your profit or loss is.

Even the most ardent advocate of high quality systems and software in the financial world would agree that there
is little need for complicated systems to trade this forecast.

However, for our forecasting technique to be useful, it will have to generate higher returns than half a percent a year.  An
obvious way to do this is to apply the same forecasting technique to other assets.  If we can make forecasts on
5000 uncorrelated stocks (this is a big if) then our return rises to 17% with the same level of risk.

Leaving aside how likely it is that you can make 5000 uncorrelated forecasts, making use of very imperfect forecasting
techniques on many securities theoretically looks like a good idea.  Indeed, this is often referred to as the *Fundamental
Law Of Active Management*.  If you have a certain degree of "skill" then you should apply that skill to as many things
as possible.

The problem of managing one position is a lot easier than the problem of managing 5000.  Firstly, of course, one
has to collect all the data for each of the 5000 securities.  Then one has to make the forecast on these 5000
securities.  Then a decision has to be made about how much risk (or assets) should be assigned to each
of the securities.  This could be as simple as 1/5000 or could be a complicated optimisation procedure with multiple
constraints and which will require a forecast covariance matrix.
Then the resulting portfolio needs to be analysed to understand its risk characteristics and
exposures to various risk factors (which themselves have to be defined and analysed).  Once the 5000 trades have
been done, things get a little less complicated although it is highly likely that there will be thousands
of corporate actions like dividend payments, stock splits, bankruptcies and mergers to handle over the course
of the year.  If the securities are not all denominated in the domestic currency then currency hedges and
rebalances will have to be done.  Finally, on the 31st of December, all the trades and currency hedges
will have to be unwound.

This whole process is complicated.  So complicated that it requires a significant amount of "specialist"
mathematical, statistical and financial quantitative knowledge.  If the investment process is going to be
robust then it also requires a significant amount of immensely complicated software and arrays of
computers and storage to analyse, manage and retain all the information.  This may run to millions
of lines of code.  Writing, testing, managing and deploying these millions of lines of code is also a
complicated process requiring specialist knowledge and experience.  At Cantab we are
very keen to explain what we do and how we do it to our investors ("a glass box not a black box") but
necessarily, these explanations have to elide over some of the critical and complicated details.

So why do we need all these complicated (and hard to explain) techniques and processes?  We need complicated processes
precisely because the markets and the securities which we trade are complex.  They are difficult to forecast and
difficult to understand in detail.  The markets, the securities and the people who trade in these markets interact
in complex non-linear ways.  There is the impact of unexpected news events arriving at random times which
can have unpredictable (literally) effects on the prices of securities.  All this complexity needs to be "factored out"
from the securities to (hopefully) generate a reasonable forecast of how the security will behave over the next
minute or the next year.

It is complicated and there is no simple way to explain it.

## Conclusion

Investors often describe systematic investment processes as "complex" and worry that they can't understand "the complexity".  This
is a category error.  Markets are complex.  Investors suffer from exposure to complexity in both systematic and discretionary
investments.

However, if investors describe systematic investment processes as complicated then they are correct and something which is
complicated should not be something to fear.  Investors should initially ignore how complicated the investment process is
and concentrate on what the output of the process is.  Investors should also accept that at some level they
will never understand a complicated systematic investment process.  Not because they specifically aren't capable of that (many
of our investors are very smart indeed) but because it's unlikely that *any* one person can understand it all.  Whilst
each individual component is fully explicable and systematic, the ability to understand every single part of
a systematic investment process is probably beyond any one person.

Conversely, the press and broadcast media are full of glib simple explanations of
the dynamics of highly complex markets.  Investors would find it instructive to worry more about overly simple
descriptions of unfathomable complexity than about investment processes which are complicated.

#### Footnotes
{{footnotes.output()}}

```python

```

```python

```

```python

```


# ECO 701: Microeconomic Theory, Fall 2017

## Jonathan Conning

**Office Hours: HW1504A
After class, Tue, Wed 11-12pm and by appt.**

jconning@hunter.cuny.edu

**Teaching Assistant:** Chuxin Liu

## Textbook
**Nicholson & Snyder, *Microeconomic Theory: Basic Principles and Extensions*.**   11th edition (10th or 12th acceptable).

### Jupyter notebook materials
  - These lecture slides, problem sets and other handouts prepared as Jupyter notebooks

  - Cells use markdown for text and math and python (or other language) code for interactive simulations and visualizations.

  - Notebooks can be rendered in different formats. Depending on how you access, you may be viewing:
     - Interactive notebook (.ipynb) that can be viewed an modified on jupyter server on own computer or cloud. Interactive widgets work.
     - Static HTML or PDF rendering. Cannot edit and interactive widgets do not work or display.
     - Slideshow from notebook displays only pre-selected cells. May be static or interactive (alt-r)

  - Promising but still evolving technology, so this is an experiment !
    - Please suggest ways to improve content and user experience.

### The Eco 701 notebook library
  - class notebooks stored in a library on Microsoft's Azure Notebooks server, a free cloud server: [notebooks.azure.com/jhconning/libraries/eco701](https://notebooks.azure.com/jhconning/libraries/eco701)
    - Follow link create account/login to view notebooks/slideshows.
    - 'Clone' library to your own account if you wish to interact/modify notebooks or download to own machine if you have local  jupyter notebooks and python installation.

- [Eco 701 repository] on github also has content and will be used for issue tracking (i.e.your post corrections/suggestions welcomed here).

  - You are encouraged but not required to use Jupyter notebooks interactively.
  - Many good intros for learning to use jupyter and python including [on Azure](https://notebooks.azure.com/Microsoft/libraries/samples/html/Introduction%20to%20Python.ipynb) and at the [Quantecon project](https://lectures.quantecon.org/py/)

- ### Blackboard or google classroom
     - for communications and grades

# Course Grading
  - ## 25% Problem sets/participation
  - ## 45% higher of midterm or final
  - ## 30% lower of midterm or final
      - **Final:** TBA
      - **Midterm:** TBA

### Problem sets
 - Usually 4 in the semester. ~ 2 weeks to complete
 - Can work in groups but you must write up your own answers.

There are just 12 lecture sessions (excluding midterm), so problem sets and outside reading are important.

# What are the questions?

## How do individuals and societies allocate resources?
## What are the alternatives?

 - Far more than the study of demand and supply choices in ‘markets’
 - how do agents (individuals, households, firms, groups) choose to associate and contract and the rules of the game they play by.
   - strategies, contracts, organizations, property rights, institutions and policy. Supply and demand decisions.
   - Judging outcomes: Conflict or cooperation?  Efficient, equitable, just?

## Methods and Assumptions
 - Methodological individualism  and instrumental rationality
 - Optimization subject to constraints and marginal analysis
 - Feedback, interactions and equilibrium
 - *Ceteribus paribus* and 'what-if' comparative statics
 - Modern challenges to behavioral and other assumptions
 - Theory and Evidence

## What is neo-classical economics ... and why bother with it?

Paul Krugman ([8/28/12](https://krugman.blogs.nytimes.com/2012/08/28/neo-fights-slightly-wonkish-and-vague/?mcubz=0&_r=0)):

>... maximization-with-equilibrium. We imagine an economy consisting of rational, self-interested players, and suppose that economic outcomes reflect a situation in which each player is doing the best he, she, or it can given the actions of all the other players. If nobody has market power, this comes down to the textbook picture of perfectly competitive markets with all the marginal whatevers equal.

>... Some economists really really believe that life is like this — and they have a significant impact on our discourse.

> But the rest of us are well aware that this is nothing but a metaphor! [we take]... the maximization-and-equilibrium world as a starting point or baseline, which is then modified — but not too much — in the direction of realism.

## Mathematical Preliminaries

### (brief note on Jupyter notebooks)

Jupyter notebooks are made up of cells that are either markdown cells or code cells.  A Jupyter slideshow is typically created by marking many code cells to be 'skipped' in order to not clutter the presentation.

I'd typically hide a code cell like the following, but will reveal it here to illustrate how we are importing additional python libraries to extend the standard python library.  matplotlib and numpy are for plotting and matrix algebra.

```python
import numpy as np
import matplotlib.pyplot as plt
import seaborn as sns
%matplotlib inline
```

## Calculus for Optimization

Cost function: $$C(q) = F + q^2$$

Profit function:
\begin{align}
 \Pi(q) & = P \cdot q - C(q)  \\
        &= P\cdot q - A - q^2
\end{align}

First-order necessary condition: $\frac{d\Pi}{dq} = P - 2q = 0$

Solve for $q$:  $q^* =\frac{P}{2} $

Profit maximizing output level is independent of fixed cost $F$

```python
F = 10
P = 10

def C(q, F=F):
    return F + q**2

def profit(q, P=P, F=F):
    return P*q - C(q, F)

def opt(P, F):
    return P/2, profit(P/2, P, F)

q = np.linspace(0,10,20)
```

```python
qe, pre = opt(P,F)

plt.figure(figsize=(12,5))
plt.plot(q, profit(q))
plt.scatter(qe,pre)
plt.plot([qe,qe],[0,pre],linestyle=':')
plt.gca().spines['bottom'].set_position('zero')
plt.gca().spines['right'].set_visible(False)
plt.gca().spines['top'].set_visible(False)
plt.xlabel(r'$q - output$');
```

### Next Lecture: [Constrained Optimization](01_Constrained_Optimization.ipynb)