```python
import sympy as sy
import numpy as np
sy.init_printing(use_latex=True)
```

## Problem 1
### (a)
Choosing sampling time and calculating the discrete-time poles. The time constant of the first-order response is $T=120$s. Rise time is $T_r = 2.2T = 264$s. Rule-of-thumb is 4-10 sampling periods per rise time.

```python
T = 120
Tr = 2.2*120
print [Tr/10, Tr/4]
```

    [26.4, 66.0]

```python
h = 40.0
pc = -1.0/120
pd = np.exp(pc*h)
print pd
```

    0.716531310574

### (b)
Backward difference approximation of given continuous-time controller

```python
s,z = sy.symbols('s, z')
h = sy.symbols('h', positive=True)
F = (16*s+1)/(100*s+1)
H = sy.simplify(F.subs(s, (z-1)/(z*h)))
print H
```

    (h*z + 16*z - 16)/(h*z + 100*z - 100)

```python

```

```python
p1,p2,p3,p4 = sy.symbols('p1, p2, p3, p4')

sy.expand((z-0.7+sy.I*0.1)*(z-0.7-sy.I*0.1))

```

## Problem 2
Calculate the pulse transfer function from the disturbance d to the output y

```python
B = 0.6*z + 0.5
A = z*(z**2 - 1.9*z + 0.9)
S, R = sy.symbols('S, R')

H_dy = (B/A) / (1 + (B/A)*(S/R))
sy.simplify(H_dy)
```

```python
H_dys = sy.simplify(H_dy)
s=sy.latex(H_dys)
print s
```

    \frac{R \left(0.6 z + 0.5\right)}{R z \left(z^{2} - 1.9 z + 0.9\right) + S \left(0.6 z + 0.5\right)}

## Problem 3
Find the diagonal form of the system in problem 2, but without the time delay

```python
H = sy.simplify(z*B/A)
sy.apart(H)
```

Set up the state-space model. Make sure it is correct.

```python
Phi = sy.Matrix([[0.9, 0], [0, 1]])
Gamma = sy.Matrix([[1],[1]])
Cm = sy.Matrix([[-10.4, 11.0]])

Htest = Cm*(z*sy.eye(2)-Phi).inv()*Gamma
Htest

```

```python
H
```

### (c) Finding the feedback gain

```python
l1, l2 = sy.symbols('l1, l2')
L = sy.Matrix([[l1, l2]])
sy.factor((z*sy.eye(2) - (Phi - Gamma*L)).det(), z)
```

```python
sy.simplify(sy.expand((z-0.6+sy.I*0.3)*(z-0.6-sy.I*0.3)))
```

# Check with matlab

```python
%load_ext pymatbridge
```

    Starting MATLAB on ZMQ socket ipc:///tmp/pymatbridge-4d29d37c-5632-4ec9-913b-670fa8e16c20
    Send 'exit' command to kill the server
    .....MATLAB started and connected!

    /home/kjartan/anaconda2/lib/python2.7/site-packages/IPython/nbformat.py:13: ShimWarning: The `IPython.nbformat` package has been deprecated. You should import from nbformat instead.
      "You should import from nbformat instead.", ShimWarning)

```python
%%matlab  --size 800,400

%Mean arteriar pressure, automatic anasthesia model
% Plant
G = tf([1], [120 1 0]);

a = 1/100;
b = 1/160;
F0 = zpk([-b], [-a], a/b);

K = 1e-2;
F = K*F0;

Gc = feedback(G*F, 1);
step(Gc, 1000)
```

```python
%%matlab --size 800,400 -o L,y,t
h = 40.0;
Phi = [0.9 0; 0 1];
Gamma = [1;1];
C = [-10.4  11.0];
D = 0;
sys = ss(Phi, Gamma, C, D, h);
L = place(Phi, Gamma, [0.6+i*0.3 0.6-i*0.3]);
sys_cl = ss(Phi-Gamma*L, Gamma, C, D, h);
[y, t] = step(sys_cl);
```

```python
import matplotlib.pyplot as plt
%matplotlib inline
plt.figure()
plt.plot(t, y)
```

```python

```

```python

```


## $\LaTeX$ can be used in JupyterLab notebooks

https://en.wikibooks.org/wiki/LaTeX/Basics

### Mathematical Symbols

* https://www.math.ubc.ca/~pwalls/math-python/jupyter/latex/
* https://en.wikibooks.org/wiki/LaTeX
* http://detexify.kirelabs.org/classify.html
* https://ctan.math.illinois.edu/info/symbols/comprehensive/symbols-letter.pdf

### Examples

$f'(a) = \lim_{x \to a} \frac{f(x) - f(a)}{x-a}$

$$f'(a) = \lim_{x \to a} \frac{f(x) - f(a)}{x-a}$$

$$\begin{matrix} a & b \\ c & d \end{matrix}$$

$$\begin{pmatrix} a & b \\ c & d \end{pmatrix}$$

$$\begin{bmatrix} 1 & 2 & 1 \\ 3 & 0 & 1 \\ 0 & 2 & 4 \end{bmatrix} \tag{5.23} \label{eq:special} $$ A cross-reference to equation $\eqref{eq:special}$ in the same markdown cell.

A cross-reference to equation $\eqref{eq:special}$ in another markdown cell.

A manually-constructed crossref to ([5.23](#mjx-eqn-eq%3Aspecial)) in another markdown cell

$$\left( \frac{p}{q} \right)$$

$$f'(a) = \lim_{x \to a} \frac{f(x) - f(a)}{x - a}$$

$$\lim_{x \to a^-} f(x) = f(a) = \lim_{x \to a^+} f(x)$$

$$e^x = \sum_{k=0}^{\infty} \frac{x^k}{k!}$$

In inline form this is $e^x = \sum_{k=0}^{\infty} \frac{x^k}{k!}$

It can be displayed inline in "[display style](https://www.overleaf.com/learn/latex/display_style_in_math_mode"): $\displaystyle e^x = \sum_{k=0}^{\infty} \frac{x^k}{k!}$

The Jacobian matrix of the function $\mathbf{f}(x_1, x_2, \dots x_n)$ is
 $$
\mathbf{J}
=
\frac{d \mathbf{f}}{d \mathbf{x}}
=
\left[ \frac{\partial \mathbf{f}}{\partial x_1}
\cdots \frac{\partial \mathbf{f}}{\partial x_n} \right]
=
\begin{bmatrix}
\frac{\partial f_1}{\partial x_1} & \cdots &
\frac{\partial f_1}{\partial x_n} \\
\vdots & \ddots & \vdots \\
\frac{\partial f_m}{\partial x_1} & \cdots &
\frac{\partial f_m}{\partial x_n}
\end{bmatrix}
$$

$\delta_{ij}$ is the [Kronecker delta function](https://mathworld.wolfram.com/KroneckerDelta.html):
$$ \delta_{ij} =
    \begin{cases}
            1 &         \text{for } i=j,\\
            0 &         \text{for } i\neq j.
    \end{cases}
$$

The gradient of a scalar function of two independent variables, $ f(x_1, x_2)$, is:

$$ \nabla f = \frac{\partial f}{\partial x_1}\hat{x}_1
+ \frac{\partial f}{\partial x_2}\hat{x}_2 $$

$$ \nabla f(a) = \left(\frac{\partial f}{\partial x_1}(a), \ldots, \frac{\partial f}{\partial x_n}(a)\right) $$

In general, the partial derivative of an [n-ary](http://en.wikipedia.org/wiki/Arity) function $f(x_1, \dots, x_n)$ in the direction $x_i$ at the point $(a_1, \dots, a_n)$ [is defined](https://en.wikipedia.org/w/index.php?title=Partial_derivative) to be:

$$\frac{\partial f}{\partial x_i}(a_1, \ldots, a_n) = \lim_{h \to 0}\frac{f(a_1, \ldots, a_i+h,\ldots,a_n) - f(a_1,\ldots, a_i, \dots,a_n)}{h}.$$

$$\frac{\partial\mathbf{y}}{\partial\mathbf{x}}$$

Various symbols:

$$ \circ \otimes \odot  \mathbf{J}^\mathsf{T} \mathbf{A}^\intercal \sqcap \sqcup \times \nabla $$

Nonsense?:

$$ \frac{\delta(m_{ij}x_j)}{\delta x_j} = m_{ij}\frac{\delta x_j}{\delta x_j} $$

$$ m_{ij}x_j\delta_{ij} = y_i $$

Where $\delta_{ij}$ is the [Kronecker delta function](https://mathworld.wolfram.com/KroneckerDelta.html):

$$ \delta_{ij} =
    \begin{cases}
            1 &         \text{for } i=j,\\
            0 &         \text{for } i\neq j.
    \end{cases}
$$

$$\begin{align}
  &\mathbf{A} \circ \mathbf{B} = \mathbf{B} \circ \mathbf{A}, \\
  &\mathbf{A} \circ (\mathbf{B} \circ \mathbf{C}) = (\mathbf{A} \circ \mathbf{B}) \circ \mathbf{C}, \\
  &\mathbf{A} \circ (\mathbf{B} + \mathbf{C}) = \mathbf{A} \circ \mathbf{B} + \mathbf{A} \circ \mathbf{C}, \\
  &\left(k\mathbf{A}\right) \circ \mathbf{B} = \mathbf{A} \circ \left(k\mathbf{B}\right) = k\left(\mathbf{A} \circ \mathbf{B}\right), \\
  &\mathbf{A} \circ \mathbf{0} = \mathbf{0} \circ \mathbf{A} = \mathbf{0}.
\end{align}$$

The [Hadamard product](https://en.wikipedia.org/wiki/Hadamard_product_(matrices)) is element-wise multiplication of equal-dimensioned matricies:

$${\displaystyle (A\circ B)_{ij}=(A\odot B)_{ij}=(A)_{ij}(B)_{ij}.}$$

$$\begin{align}
&foo, \\
& bar \\
\end{align}
$$

$$a,\\
b$$

$$\mathbf {a} \cdot \mathbf {b} =\left\|\mathbf {a} \right\|\left\|\mathbf {b} \right\|\cos \theta =\left\|\mathbf {b} \right\|\left\|\mathbf {a} \right\|\cos \theta =\mathbf {b} \cdot \mathbf {a}
$$

[Type sizes](https://www.stat.berkeley.edu/~paciorek/computingTips/Type_sizes_changing_type_si.html)
tiny, scriptsize, footnotesize, small, normalsize, large, Large, LARGE, huge, Huge

$$ \Huge 3x+3=\mu $$

`
\; - a thick space
\: - a medium space
\, - a thin space
\! - a negative thin space
`

$$\circ \; \circ \: \circ \, \circ $$

```python
from itertools import chain, combinations

def powerset(iterable):
    "powerset([1,2,3]) --> () (1,) (2,) (3,) (1,2) (1,3) (2,3) (1,2,3)"
    s = list(iterable)
    return chain.from_iterable(combinations(s, r) for r in range(len(s)+1))
```

```python
list(powerset('abc'))
```

___
# Plotting

```python
#%matplotlib widget
%matplotlib inline
%load_ext autoreload
%autoreload 2
```

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

```python
plt.plot(range(10), range(30,20,-1))
```

```python
import numpy as np
np.sign(3)
```

```python
ls
```

```python
pwd
```

```python
#!jupyter nbconvert --to script nnt1.ipynb
```

$ \eta $

* [Working efficiently with jupyter lab](https://florianwilhelm.info/2018/11/working_efficiently_with_jupyter_lab/)
* [nbcelltests](https://github.com/jpmorganchase/nbcelltests)

```python
import dis
```

```python
?dis.dis
```

```python
dis.dis(lambda x,y:x**2+y**2)
```

---

https://github.com/google/jax

https://ipywidgets.readthedocs.io/en/latest/index.html

```python
import ipywidgets as widgets
```

```python
widgets.IntSlider()
```

```python
from IPython.display import display
w = widgets.IntSlider()
display(w)
```

```python
display(w)
```

```python
a = widgets.FloatText()
b = widgets.FloatSlider()
display(a,b)

mylink = widgets.jslink((a, 'value'), (b, 'value'))
```

```python
widgets.HBox([widgets.Label(value="The $m$ in $E=mc^2$:"), widgets.FloatSlider()])
```

```python
widgets.HTML(
    value="Hello <b>World</b>",
    placeholder='Some HTML',
    description='Some HTML',
)

```

```python
out = widgets.Output(layout={'border': '1px solid black'})
out
```

```python
with out:
    for i in range(10):
        print(i, 'Hello world!')
```

```python
from IPython.display import YouTubeVideo
with out:
    display(YouTubeVideo('eWzY2nGfkXk'))
```

```python
debug_view = widgets.Output(layout={'border': '1px solid black'})

@debug_view.capture(clear_output=True)
def bad_callback(event):
    print('This is about to explode')
    return 1.0 / 0.0

button = widgets.Button(
    description='click me to raise an exception',
    layout={'width': '300px'}
)
button.on_click(bad_callback)
button
```

```python
debug_view
```

```python
int_range = widgets.IntSlider()
output2 = widgets.Output()

display(int_range, output2)

def on_value_change(change):
    with output2:
        print(change['new'])

int_range.observe(on_value_change, names='value')
```

```python
widgets.HTMLMath(
    value=r"Some math and <i>HTML</i>: \(x^2\) and $$\frac{x+1}{x-1}$$",
    placeholder='Some HTML',
    description='Some HTML',
)
```

```python
debug_view = widgets.Output(layout={'border': '1px solid black'})

@debug_view.capture(clear_output=True)
def bad_callback(event):
    print('This is about to explode')
    return 1.0 / 0.0

button = widgets.Button(
    description='click me to raise an exception',
    layout={'width': '300px'}
)
button.on_click(bad_callback)
button
```

```python
debug_view
```

```python
import ipywidgets as widgets
import logging

class OutputWidgetHandler(logging.Handler):
    """ Custom logging handler sending logs to an output widget """

    def __init__(self, *args, **kwargs):
        super(OutputWidgetHandler, self).__init__(*args, **kwargs)
        layout = {
            'width': '100%',
            'height': '160px',
            'border': '1px solid black'
        }
        self.out = widgets.Output(layout=layout)

    def emit(self, record):
        """ Overload of logging.Handler method """
        formatted_record = self.format(record)
        new_output = {
            'name': 'stdout',
            'output_type': 'stream',
            'text': formatted_record+'\n'
        }
        self.out.outputs = (new_output, ) + self.out.outputs

    def show_logs(self):
        """ Show the logs """
        display(self.out)

    def clear_logs(self):
        """ Clear the current logs """
        self.out.clear_output()

logger = logging.getLogger(__name__)
handler = OutputWidgetHandler()
handler.setFormatter(logging.Formatter('%(asctime)s  - [%(levelname)s] %(message)s'))
logger.addHandler(handler)
logger.setLevel(logging.INFO)
```

```python
handler.show_logs()
```

```python
handler.clear_logs()
logger.info('Starting program')

try:
    logger.info('About to try something dangerous...')
    1.0/0.0
except Exception as e:
    logger.exception('An error occurred!')

```

---

```python
from IPython.display import display
button = widgets.Button(description="Click Me!")
output = widgets.Output()

display(button, output)

def on_button_clicked(b):
    with output:
        print("Button clicked.")

button.on_click(on_button_clicked)
```

```python
int_range = widgets.IntSlider()
output2 = widgets.Output()

display(int_range, output2)

def on_value_change(change):
    with output2:
        print(change['new'])

int_range.observe(on_value_change, names='value')
```

* https://ipywidgets.readthedocs.io/en/latest/examples/Widget%20Asynchronous.html

```python
import ipywidgets as widgets
```

```python
%gui
```

```python
%gui?
```

```python
%gui asyncio
```

```python
import asyncio
def wait_for_change(widget, value):
    future = asyncio.Future()
    def getvalue(change):
        # make the new value available
        future.set_result(change.new)
        widget.unobserve(getvalue, value)
    widget.observe(getvalue, value)
    return future
```

```python
from ipywidgets import IntSlider, Output
slider = IntSlider()
out = Output()

async def f():
    for i in range(10):
        out.append_stdout('did work ' + str(i) + '\n')
        x = await wait_for_change(slider, 'value')
        out.append_stdout('async function continued with value ' + str(x) + '\n')
asyncio.ensure_future(f())

slider
```

```python
out
```

Or without `async`, using generators:

```python
from functools import wraps
def yield_for_change(widget, attribute):
    """Pause a generator to wait for a widget change event.

    This is a decorator for a generator function which pauses the generator on yield
    until the given widget attribute changes. The new value of the attribute is
    sent to the generator and is the value of the yield.
    """
    def f(iterator):
        print("<f>")
        @wraps(iterator)
        def inner():
            print("<i>")
            i = iterator()
            print("<i=i>")
            def next_i(change):
                print("<n>")
                try:
                    i.send(change.new)
                except StopIteration as e:
                    widget.unobserve(next_i, attribute)
            print("<o>")
            widget.observe(next_i, attribute)
            # start the generator
            next(i)
            print("<x>")
        print("<ri>")
        return inner
    print("<rf>")
    return f
```

```python
from ipywidgets import IntSlider
slider2=IntSlider()

@yield_for_change(slider2, 'value')
def gf():
    for i in range(10):
        print('did work %s'%i)
        x = yield
        print('generator function continued with value %s'%x)

print("running gf")
gf()

slider2
```

```python
𨭎𨭎𨭎𨭎𨭎 = 10
```

```python
inter
```

```python
from ipywidgets import interact, interactive, fixed, interact_manual
import ipywidgets as widgets
```

```python
@interact(x=True, y=1.0)
def g(x, y):
    return (x, y)
```

```python
%load_ext blackcellmagic
```

```python
from ipywidgets import interactive
import matplotlib.pyplot as plt
import numpy as np

def f(m, b):
    plt.figure(2)
    x = np.linspace(-10, 10, num=1000)
    plt.plot(x, m * x + b)
    plt.ylim(-5, 5)
    plt.show()

interactive_plot = interactive(f, m=(-2.0, 2.0), b=(-3, 3, 0.5))
output = interactive_plot.children[-1]
output.layout.height = "350px"
interactive_plot
```

---

```python
from __future__ import braces
```

```python
import this
```

```python
dir(this)
```

```python
print(''.join(this.d.get(v) or v for v in this.s))
#print(this.s)
```

---

* https://ipython.readthedocs.io/en/stable/interactive/magics.html
* https://ipywidgets.readthedocs.io/en/latest/examples/Widget%20Styling.html
* https://colab.research.google.com/notebooks/intro.ipynb
* https://colab.research.google.com/notebooks/io.ipynb
* https://cloud.google.com/ai-platform
* https://cloud.google.com/blog/products/ai-machine-learning/best-practices-that-can-improve-the-life-of-any-developer-using-jupyter-notebooks
* https://github.com/tensorflow/playground/blob/master/index.html

## Siren
* https://github.com/vsitzmann/siren
* https://colab.research.google.com/github/vsitzmann/siren/blob/master/explore_siren.ipynb#scrollTo=NJlfI3ZEnZ6Y
* https://dcato98.github.io/playground/

* https://en.wikipedia.org/wiki/Musical_isomorphism

```python
%config
```

```python
%edit
```

```python
%env
```

```python
%history
```

```python
%lsmagic
```

```python
#%magic -latex
```

```python
%matplotlib
```

```python
%matplotlib --list
```

```python
%psearch a*
```

```python
%quickref
```

```python
%run test-nnbench
```

```python
%%html
<h1>foo</h1>
<blink>wow</blink>
```

```python
a = widgets.IntSlider(description="a")
b = widgets.IntSlider(description="b")
c = widgets.IntSlider(description="c")

def f(a, b, c):
    print("{}*{}*{}={}".format(a, b, c, a * b * c))

out = widgets.interactive_output(f, {"a": a, "b": b, "c": c})

widgets.HBox([widgets.VBox([a, b, c]), out])
```

* https://github.com/ethereum/snake-charmers-tactical-manual

```python
%%script echo skipping
print("The whole cell gets skipped")
```

Can link to other notebooks, e.g. [Trio notebook](./Trio.ipynb)

```python
from bar.baz import Foo

def foo_html(obj):
    return "<marquee>Foo object %s</marquee>" % obj.name

html_formatter = get_ipython().display_formatter.formatters["text/html"]
html_formatter.for_type(Foo, foo_html)

# Or register a type without importing it - this does the same as above:
html_formatter.for_type_by_name("bar.baz", "Foo", foo_html)
```

```python
from collections import deque
d = deque([], 3)
len(d)
```

```python
d.append('a')
d
```

```python
d.append('b')
d
```

```python
d.append('c')
d
```

```python
d.append('d')
d
```

```python
import numpy as np
np.array(d)
```

```python
list(d)[-10:]
```

```python
from display import Image
```

```python
import antigravity
```

```python

```

```python
from IPython.display import Image
```

```python
import IPython.display
```

```python
dir(IPython.display)
```

```python
?IPython.display.Image
```

```python
import numpy as np
Image(np.arange(16*16))
```

```python
import PIL
```

```python
PIL.__version__
```

```python
from PIL import Image as PILImage
```

```python
PIL.Image.fromarray
```

```python
np.arange(16, dtype='uint8')
```

```python
img_1 = PIL.Image.fromarray(np.arange(16*16, dtype='uint8').reshape(16,16))
```

```python
from io import BytesIO
t_png = BytesIO()
img_1.save(t_png, format="png")
```

```python
t_png.getbuffer()
```

```python
IPython.display.Image(t_png.getbuffer())
```

```python
%pip install sidecar
```

```python
from sidecar import Sidecar
from ipywidgets import IntSlider

sc = Sidecar(title='Sidecar Output')
sl = IntSlider(description='Some slider')
with sc:
    display(sl)

```

```python
import numpy as np
```

```python
np.arctanh(-0.99999999)
```

    -9.556913957243776

# Redis
* https://oss.redislabs.com/redisai/
* https://github.com/RedisAI/RedisAI
* https://github.com/RedisAI/redisai-py
* https://github.com/RedisGraph/RedisGraph

# Poetry
* https://python-poetry.org/docs/

```python
t = np.array([1, 2, np.array([3, 4, np.array([5, 6]), 7]), 8])
```

```python
t
```

    array([1, 2, array([3, 4, array([5, 6]), 7], dtype=object), 8],
          dtype=object)

```python
t.shape
```

    (4,)

```python
t.flatten()
```

    array([1, 2, array([3, 4, array([5, 6]), 7], dtype=object), 8],
          dtype=object)

```python
t.ravel()
```

    array([1, 2, array([3, 4, array([5, 6]), 7], dtype=object), 8],
          dtype=object)

<svg>
<ellipse style="fill:#00ff00;stroke:#000000;" cx="50" cy="50" rx="48" ry="48">
</ellipse>
</svg>

<svg>
<ellipse style="fill:#00ff00;stroke:#000000;" cx="50" cy="50" rx="48" ry="48">
</ellipse>
</svg>

```python
class A:
    def __init__(self):
        print("init A")

    def _repr_svg_(self):
        return """
<svg xmlns="http://www.w3.org/2000/svg" height="44pt" viewBox="0.00 0.00 62.00 44.00" width="62pt">
<g class="graph" id="graph0" transform="scale(1 1) rotate(0) translate(4 40)">
<g class="node" id="node1">
<g id="a_node1"><title>A circle</title>
<ellipse cx="27" cy="-18" fill="#444444" rx="27" ry="18" stroke="#000000"/>
<text fill="#ffffff" font-family="Times,serif" font-size="14.00" text-anchor="middle" x="27" y="-14.3">Abc</text>

</g>
</g>
</g>
</svg>
"""

a = A()
a
```

* https://app.diagrams.net/

```python
class A:
    def __init__(self):
        print("init A")

    def _repr_svg_(self):
        return """
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" version="1.1" width="221px" height="181px" viewBox="-0.5 -0.5 221 181" content="<mxfile host="app.diagrams.net" modified="2021-03-10T01:23:08.606Z" agent="5.0 (Macintosh; Intel Mac OS X 11_2_2) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/88.0.4324.192 Safari/537.36" etag="ZZJzNCXEh2RdeHb9SAKH" version="14.4.6"><diagram id="3l-dQbXBslvPWgFDMlL6" name="Page-1">tZXJTsMwEIafJkekLE0LR0hZLghQDyw3K54mlpw6clyS8PRM8WRxUsQiuFT+f9vjmW/s1IuSornWrMxvFQfphT5vvGjtheHpWYy/B6O1RhyvrJFpwa0VDMZGvAGZPrl7waFyFhqlpBGla6Zqt4PUOB7TWtXusq2S7qkly2BmbFIm5+6j4CansmJ/8G9AZHl3cuDTTMG6xWRUOeOqHlnRpRclWiljR0WTgDyw67jYfVefzPaJadiZ72xY3t0kd1XyUpytNlnzEMswyE8oyiuTeyo4xRxtvqbtIGAg5I3ios6FgU3J0sNMjR1HLzeFRBXgkKKBNtB8mmbQF4+XBlQBRre4hDaEHVq6MMGCdD3gDzqm+Qj9KXmMOp71oQcoOCAuP2AUzhhttcp+D4lVpb2pW9EA/xtqC9+l1lMcUVsdgbb6L2jRDNoc2I6fH14oqlSyqhKpywlL1+0TCr8Tz2OxbhzVkrLHAJ896wlK/I4wnYH5qvFz5COk8RGknadBMiNe3TSOcaYT7pXABPuORvHS7ag/aVWl9joF2jV+95NAi8mDCpeTQJbDLNBH2/uyj90ElMPnyy4f/gOiy3c=</diagram></mxfile>" style="background-color: rgb(255, 255, 255);"><defs/><g><ellipse cx="60" cy="40" rx="60" ry="40" fill="#ffffff" stroke="#000000" pointer-events="all"/><g transform="translate(-0.5 -0.5)"><switch><foreignObject style="overflow: visible; text-align: left;" pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility"><div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 118px; height: 1px; padding-top: 40px; margin-left: 1px;"><div style="box-sizing: border-box; font-size: 0; text-align: center; "><div style="display: inline-block; font-size: 12px; font-family: Helvetica; color: #000000; line-height: 1.2; pointer-events: all; white-space: normal; word-wrap: normal; ">cow</div></div></div></foreignObject><text x="60" y="44" fill="#000000" font-family="Helvetica" font-size="12px" text-anchor="middle">cow</text></switch></g><ellipse cx="185" cy="145" rx="35" ry="35" fill="#ffffff" stroke="#000000" pointer-events="all"/><g transform="translate(-0.5 -0.5)"><switch><foreignObject style="overflow: visible; text-align: left;" pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility"><div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 68px; height: 1px; padding-top: 145px; margin-left: 151px;"><div style="box-sizing: border-box; font-size: 0; text-align: center; "><div style="display: inline-block; font-size: 12px; font-family: Helvetica; color: #000000; line-height: 1.2; pointer-events: all; white-space: normal; word-wrap: normal; ">frog</div></div></div></foreignObject><text x="185" y="149" fill="#000000" font-family="Helvetica" font-size="12px" text-anchor="middle">frog</text></switch></g><path d="M 106 60 L 155.99 115.52" fill="none" stroke="#000000" stroke-miterlimit="10" pointer-events="stroke"/><path d="M 159.5 119.42 L 152.22 116.56 L 155.99 115.52 L 157.42 111.88 Z" fill="#000000" stroke="#000000" stroke-miterlimit="10" pointer-events="all"/></g><switch><g requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility"/><a transform="translate(0,-5)" xlink:href="https://www.diagrams.net/doc/faq/svg-export-text-problems" target="_blank"><text text-anchor="middle" font-size="10px" x="50%" y="100%">Viewer does not support full SVG 1.1</text></a></switch></svg>"""

a=A()
a
```

    init A

```python
class SVGLiteral:
    def __init__(self, s):
        self.s = s

    def _repr_svg_(self):
        return self.s
```

```python
svg_str = """<svg>
<ellipse style="fill:#00ff00;stroke:#000000;" cx="50" cy="50" rx="48" ry="48">
</ellipse>
</svg>"""
a = SVGLiteral(svg_str)
a
```

```python
from IPython.display import SVG, display
display(SVG(svg_str))
```

```python
from IPython.display import SVG, display
display(SVG('ml graphdb structure r1.svg'))
```

```python
from IPython.display import SVG, display
display('ml graphdb structure r1.svg')
```

    'ml graphdb structure r1.svg'

* https://github.com/encode/httpx

```python

```


# Conservative SDOF - Multiple Scales

- Introduces multiple time scales (Homogenation)
- Treate damped systems easier then L-P
- Built-in stability

Introduce new independent time variables

$$
\begin{gather*}
    T_n = \epsilon^n t
\end{gather*}
$$

and

$$
\begin{align*}
    \frac{d}{dt} &= \frac{\partial}{\partial T_0}\frac{dT_0}{dt} + \frac{\partial}{\partial T_1}\frac{dT_1}{dt} +  \frac{\partial}{\partial T_2}\frac{dT_2}{dt}  + \cdots\\
    &= \frac{\partial}{\partial T_0} + \epsilon \frac{\partial}{\partial T_1} + \epsilon^2 \frac{\partial}{\partial T_2} + \cdots\\
    &= D_0 + \epsilon D_1 + \epsilon^2 D_2 + \cdots
\end{align*}
$$

$$
\begin{align*}
    \frac{d^2}{dt^2} &= \left( D_0 + \epsilon D_1 + \epsilon^2 D_2 + \cdots \right)^2
\end{align*}
$$

Introducing the Expansion for $x(t)$

$$
\begin{align*}
    x(t) &= x_0(T_0,T_1,T_2,\cdots) + \epsilon x_1(T_0,T_1,T_2,\cdots) + \epsilon^2(T_0,T_1,T_2,\cdots) + \epsilon^3(T_0,T_1,T_2,\cdots) + O(\epsilon^4)
\end{align*}
$$

```python
import sympy as sp
from sympy.simplify.fu import TR0, TR7, TR8, TR11
from math import factorial
```

```python
# Functions for multiple scales

# Function for Time operator
def Dt(f, n, Ts, e=sp.Symbol('epsilon')):
    if n==1:
        return sp.expand(sum([e**i * sp.diff(f, T_i) for i, T_i in enumerate(Ts)]))
    return Dt(Dt(f, 1, Ts, e), n-1, Ts, e)

def collect_epsilon(f, e=sp.Symbol('epsilon')):
    N = sp.degree(f, e)
    f_temp = f
    collected_dict = {}
    for i in range(N, 0, -1):
        collected_term = f_temp.coeff(e**i)
        collected_dict[e**i] = collected_term
        delete_terms = sp.expand(e**i * collected_term)
        f_temp -= delete_terms
    collected_dict[e**0] = f_temp
    return collected_dict
```

```python
N = 3

f = sp.Function('f')
t = sp.Symbol('t', real=True)

# Define the symbolic parameters
epsilon = sp.symbols('epsilon')

T_i = sp.symbols('T_(0:' + str(N) + ')', real=True)
alpha_i = sp.symbols('alpha_(2:' + str(N+1) + ')', real=True)
omega0 = sp.Symbol('omega_0', real=True)

# x0 = sp.Function('x_0')(*T_i)
x1 = sp.Function('x_1')(*T_i)
x2 = sp.Function('x_2')(*T_i)
x3 = sp.Function('x_3')(*T_i)

# Expansion for x(t)
x_e = epsilon*x1 + epsilon**2 * x2 + epsilon**3 * x3
x_e
```

$\displaystyle \epsilon^{3} \operatorname{x_{3}}{\left(T_{0},T_{1},T_{2} \right)} + \epsilon^{2} \operatorname{x_{2}}{\left(T_{0},T_{1},T_{2} \right)} + \epsilon \operatorname{x_{1}}{\left(T_{0},T_{1},T_{2} \right)}$

```python
# Derivatives with time operators
xd = Dt(x_e, 1, T_i, epsilon)
xdd = Dt(x_e, 2, T_i, epsilon)

# EOM
EOM = xdd + sp.expand(omega0**2 * x_e) + sp.expand(sum([alpha_i[i-2] * x_e**i for i in range(2,N+1)]))
EOM
```

$\displaystyle \alpha_{2} \epsilon^{6} \operatorname{x_{3}}^{2}{\left(T_{0},T_{1},T_{2} \right)} + 2 \alpha_{2} \epsilon^{5} \operatorname{x_{2}}{\left(T_{0},T_{1},T_{2} \right)} \operatorname{x_{3}}{\left(T_{0},T_{1},T_{2} \right)} + 2 \alpha_{2} \epsilon^{4} \operatorname{x_{1}}{\left(T_{0},T_{1},T_{2} \right)} \operatorname{x_{3}}{\left(T_{0},T_{1},T_{2} \right)} + \alpha_{2} \epsilon^{4} \operatorname{x_{2}}^{2}{\left(T_{0},T_{1},T_{2} \right)} + 2 \alpha_{2} \epsilon^{3} \operatorname{x_{1}}{\left(T_{0},T_{1},T_{2} \right)} \operatorname{x_{2}}{\left(T_{0},T_{1},T_{2} \right)} + \alpha_{2} \epsilon^{2} \operatorname{x_{1}}^{2}{\left(T_{0},T_{1},T_{2} \right)} + \alpha_{3} \epsilon^{9} \operatorname{x_{3}}^{3}{\left(T_{0},T_{1},T_{2} \right)} + 3 \alpha_{3} \epsilon^{8} \operatorname{x_{2}}{\left(T_{0},T_{1},T_{2} \right)} \operatorname{x_{3}}^{2}{\left(T_{0},T_{1},T_{2} \right)} + 3 \alpha_{3} \epsilon^{7} \operatorname{x_{1}}{\left(T_{0},T_{1},T_{2} \right)} \operatorname{x_{3}}^{2}{\left(T_{0},T_{1},T_{2} \right)} + 3 \alpha_{3} \epsilon^{7} \operatorname{x_{2}}^{2}{\left(T_{0},T_{1},T_{2} \right)} \operatorname{x_{3}}{\left(T_{0},T_{1},T_{2} \right)} + 6 \alpha_{3} \epsilon^{6} \operatorname{x_{1}}{\left(T_{0},T_{1},T_{2} \right)} \operatorname{x_{2}}{\left(T_{0},T_{1},T_{2} \right)} \operatorname{x_{3}}{\left(T_{0},T_{1},T_{2} \right)} + \alpha_{3} \epsilon^{6} \operatorname{x_{2}}^{3}{\left(T_{0},T_{1},T_{2} \right)} + 3 \alpha_{3} \epsilon^{5} \operatorname{x_{1}}^{2}{\left(T_{0},T_{1},T_{2} \right)} \operatorname{x_{3}}{\left(T_{0},T_{1},T_{2} \right)} + 3 \alpha_{3} \epsilon^{5} \operatorname{x_{1}}{\left(T_{0},T_{1},T_{2} \right)} \operatorname{x_{2}}^{2}{\left(T_{0},T_{1},T_{2} \right)} + 3 \alpha_{3} \epsilon^{4} \operatorname{x_{1}}^{2}{\left(T_{0},T_{1},T_{2} \right)} \operatorname{x_{2}}{\left(T_{0},T_{1},T_{2} \right)} + \alpha_{3} \epsilon^{3} \operatorname{x_{1}}^{3}{\left(T_{0},T_{1},T_{2} \right)} + \epsilon^{7} \frac{\partial^{2}}{\partial T_{2}^{2}} \operatorname{x_{3}}{\left(T_{0},T_{1},T_{2} \right)} + \epsilon^{6} \frac{\partial^{2}}{\partial T_{2}^{2}} \operatorname{x_{2}}{\left(T_{0},T_{1},T_{2} \right)} + 2 \epsilon^{6} \frac{\partial^{2}}{\partial T_{2}\partial T_{1}} \operatorname{x_{3}}{\left(T_{0},T_{1},T_{2} \right)} + \epsilon^{5} \frac{\partial^{2}}{\partial T_{2}^{2}} \operatorname{x_{1}}{\left(T_{0},T_{1},T_{2} \right)} + \epsilon^{5} \frac{\partial^{2}}{\partial T_{1}^{2}} \operatorname{x_{3}}{\left(T_{0},T_{1},T_{2} \right)} + 2 \epsilon^{5} \frac{\partial^{2}}{\partial T_{2}\partial T_{1}} \operatorname{x_{2}}{\left(T_{0},T_{1},T_{2} \right)} + 2 \epsilon^{5} \frac{\partial^{2}}{\partial T_{2}\partial T_{0}} \operatorname{x_{3}}{\left(T_{0},T_{1},T_{2} \right)} + \epsilon^{4} \frac{\partial^{2}}{\partial T_{1}^{2}} \operatorname{x_{2}}{\left(T_{0},T_{1},T_{2} \right)} + 2 \epsilon^{4} \frac{\partial^{2}}{\partial T_{2}\partial T_{1}} \operatorname{x_{1}}{\left(T_{0},T_{1},T_{2} \right)} + 2 \epsilon^{4} \frac{\partial^{2}}{\partial T_{2}\partial T_{0}} \operatorname{x_{2}}{\left(T_{0},T_{1},T_{2} \right)} + 2 \epsilon^{4} \frac{\partial^{2}}{\partial T_{1}\partial T_{0}} \operatorname{x_{3}}{\left(T_{0},T_{1},T_{2} \right)} + \epsilon^{3} \omega_{0}^{2} \operatorname{x_{3}}{\left(T_{0},T_{1},T_{2} \right)} + \epsilon^{3} \frac{\partial^{2}}{\partial T_{1}^{2}} \operatorname{x_{1}}{\left(T_{0},T_{1},T_{2} \right)} + \epsilon^{3} \frac{\partial^{2}}{\partial T_{0}^{2}} \operatorname{x_{3}}{\left(T_{0},T_{1},T_{2} \right)} + 2 \epsilon^{3} \frac{\partial^{2}}{\partial T_{2}\partial T_{0}} \operatorname{x_{1}}{\left(T_{0},T_{1},T_{2} \right)} + 2 \epsilon^{3} \frac{\partial^{2}}{\partial T_{1}\partial T_{0}} \operatorname{x_{2}}{\left(T_{0},T_{1},T_{2} \right)} + \epsilon^{2} \omega_{0}^{2} \operatorname{x_{2}}{\left(T_{0},T_{1},T_{2} \right)} + \epsilon^{2} \frac{\partial^{2}}{\partial T_{0}^{2}} \operatorname{x_{2}}{\left(T_{0},T_{1},T_{2} \right)} + 2 \epsilon^{2} \frac{\partial^{2}}{\partial T_{1}\partial T_{0}} \operatorname{x_{1}}{\left(T_{0},T_{1},T_{2} \right)} + \epsilon \omega_{0}^{2} \operatorname{x_{1}}{\left(T_{0},T_{1},T_{2} \right)} + \epsilon \frac{\partial^{2}}{\partial T_{0}^{2}} \operatorname{x_{1}}{\left(T_{0},T_{1},T_{2} \right)}$

```python
# Ordered Equations by epsilon
epsilon_Eq = collect_epsilon(EOM)
epsilon0_Eq = sp.Eq(epsilon_Eq[epsilon**0], 0)
epsilon0_Eq
```

$\displaystyle \text{True}$

```python
epsilon1_Eq = sp.Eq(epsilon_Eq[epsilon**1], 0)
epsilon1_Eq
```

$\displaystyle \omega_{0}^{2} \operatorname{x_{1}}{\left(T_{0},T_{1},T_{2} \right)} + \frac{\partial^{2}}{\partial T_{0}^{2}} \operatorname{x_{1}}{\left(T_{0},T_{1},T_{2} \right)} = 0$

```python
epsilon2_Eq = sp.Eq(epsilon_Eq[epsilon**2], 0)
epsilon2_Eq
```

$\displaystyle \alpha_{2} \operatorname{x_{1}}^{2}{\left(T_{0},T_{1},T_{2} \right)} + \omega_{0}^{2} \operatorname{x_{2}}{\left(T_{0},T_{1},T_{2} \right)} + \frac{\partial^{2}}{\partial T_{0}^{2}} \operatorname{x_{2}}{\left(T_{0},T_{1},T_{2} \right)} + 2 \frac{\partial^{2}}{\partial T_{1}\partial T_{0}} \operatorname{x_{1}}{\left(T_{0},T_{1},T_{2} \right)} = 0$

```python
epsilon3_Eq = sp.Eq(epsilon_Eq[epsilon**3], 0)
epsilon3_Eq
```

$\displaystyle 2 \alpha_{2} \operatorname{x_{1}}{\left(T_{0},T_{1},T_{2} \right)} \operatorname{x_{2}}{\left(T_{0},T_{1},T_{2} \right)} + \alpha_{3} \operatorname{x_{1}}^{3}{\left(T_{0},T_{1},T_{2} \right)} + \omega_{0}^{2} \operatorname{x_{3}}{\left(T_{0},T_{1},T_{2} \right)} + \frac{\partial^{2}}{\partial T_{1}^{2}} \operatorname{x_{1}}{\left(T_{0},T_{1},T_{2} \right)} + \frac{\partial^{2}}{\partial T_{0}^{2}} \operatorname{x_{3}}{\left(T_{0},T_{1},T_{2} \right)} + 2 \frac{\partial^{2}}{\partial T_{2}\partial T_{0}} \operatorname{x_{1}}{\left(T_{0},T_{1},T_{2} \right)} + 2 \frac{\partial^{2}}{\partial T_{1}\partial T_{0}} \operatorname{x_{2}}{\left(T_{0},T_{1},T_{2} \right)} = 0$

```python
# Find the solution for epsilon-1
A = sp.Function('A')(*T_i[1::])
x1_sol = A * sp.exp(sp.I * omega0 * T_i[0]) + sp.conjugate(A) * sp.exp(-sp.I * omega0 * T_i[0])
x1_sol
```

$\displaystyle A{\left(T_{1},T_{2} \right)} e^{i T_{0} \omega_{0}} + e^{- i T_{0} \omega_{0}} \overline{A{\left(T_{1},T_{2} \right)}}$

```python
# Update the epsilon-2 equation
epsilon2_Eq = epsilon2_Eq.subs(x1, x1_sol).doit()
epsilon2_Eq = sp.expand(epsilon2_Eq)
epsilon2_Eq
```

$\displaystyle \alpha_{2} A^{2}{\left(T_{1},T_{2} \right)} e^{2 i T_{0} \omega_{0}} + 2 \alpha_{2} A{\left(T_{1},T_{2} \right)} \overline{A{\left(T_{1},T_{2} \right)}} + \alpha_{2} e^{- 2 i T_{0} \omega_{0}} \overline{A{\left(T_{1},T_{2} \right)}}^{2} + \omega_{0}^{2} \operatorname{x_{2}}{\left(T_{0},T_{1},T_{2} \right)} + 2 i \omega_{0} e^{i T_{0} \omega_{0}} \frac{\partial}{\partial T_{1}} A{\left(T_{1},T_{2} \right)} - 2 i \omega_{0} e^{- i T_{0} \omega_{0}} \overline{\frac{\partial}{\partial T_{1}} A{\left(T_{1},T_{2} \right)}} + \frac{\partial^{2}}{\partial T_{0}^{2}} \operatorname{x_{2}}{\left(T_{0},T_{1},T_{2} \right)} = 0$

The secular terms will be cancelled out by

$$
\begin{gather*}
    D_1 A = 0
\end{gather*}
$$

```python
epsilon2_Eq = epsilon2_Eq.subs(sp.diff(A, T_i[1]), 0)
epsilon2_Eq
```

$\displaystyle \alpha_{2} A^{2}{\left(T_{1},T_{2} \right)} e^{2 i T_{0} \omega_{0}} + 2 \alpha_{2} A{\left(T_{1},T_{2} \right)} \overline{A{\left(T_{1},T_{2} \right)}} + \alpha_{2} e^{- 2 i T_{0} \omega_{0}} \overline{A{\left(T_{1},T_{2} \right)}}^{2} + \omega_{0}^{2} \operatorname{x_{2}}{\left(T_{0},T_{1},T_{2} \right)} + \frac{\partial^{2}}{\partial T_{0}^{2}} \operatorname{x_{2}}{\left(T_{0},T_{1},T_{2} \right)} = 0$

The particular solution of $x_2$ is

$$
\begin{gather*}
    x_2 = \frac{\alpha_2 A^2}{3 \omega_0^2} e^{2i\omega_0 T_0} - \frac{\alpha_2 }{\omega^2_0}A\overline{A} + cc
\end{gather*}
$$

```python
x2_p = alpha_i[0] * A**2 / 3/omega0**2 * sp.exp(2*sp.I*omega0*T_i[0]) - alpha_i[0]/omega0**2 * A * sp.conjugate(A)
x2_p
```

$\displaystyle \frac{\alpha_{2} A^{2}{\left(T_{1},T_{2} \right)} e^{2 i T_{0} \omega_{0}}}{3 \omega_{0}^{2}} - \frac{\alpha_{2} A{\left(T_{1},T_{2} \right)} \overline{A{\left(T_{1},T_{2} \right)}}}{\omega_{0}^{2}}$

```python
epsilon3_Eq = epsilon3_Eq.subs([
    (sp.diff(A, T_i[1]), 0), (x1, x1_sol), (x2, x2_p)
]).doit()
epsilon3_Eq = sp.expand(epsilon3_Eq)
epsilon3_Eq = epsilon3_Eq.subs(sp.diff(A, T_i[1], 2), 0)
epsilon3_Eq
```

$\displaystyle \frac{2 \alpha_{2}^{2} A^{3}{\left(T_{1},T_{2} \right)} e^{3 i T_{0} \omega_{0}}}{3 \omega_{0}^{2}} - \frac{4 \alpha_{2}^{2} A^{2}{\left(T_{1},T_{2} \right)} e^{i T_{0} \omega_{0}} \overline{A{\left(T_{1},T_{2} \right)}}}{3 \omega_{0}^{2}} - \frac{2 \alpha_{2}^{2} A{\left(T_{1},T_{2} \right)} e^{- i T_{0} \omega_{0}} \overline{A{\left(T_{1},T_{2} \right)}}^{2}}{\omega_{0}^{2}} + \frac{8 i \alpha_{2} A{\left(T_{1},T_{2} \right)} e^{2 i T_{0} \omega_{0}} \frac{\partial}{\partial T_{1}} A{\left(T_{1},T_{2} \right)}}{3 \omega_{0}} + \alpha_{3} A^{3}{\left(T_{1},T_{2} \right)} e^{3 i T_{0} \omega_{0}} + 3 \alpha_{3} A^{2}{\left(T_{1},T_{2} \right)} e^{i T_{0} \omega_{0}} \overline{A{\left(T_{1},T_{2} \right)}} + 3 \alpha_{3} A{\left(T_{1},T_{2} \right)} e^{- i T_{0} \omega_{0}} \overline{A{\left(T_{1},T_{2} \right)}}^{2} + \alpha_{3} e^{- 3 i T_{0} \omega_{0}} \overline{A{\left(T_{1},T_{2} \right)}}^{3} + \omega_{0}^{2} \operatorname{x_{3}}{\left(T_{0},T_{1},T_{2} \right)} + 2 i \omega_{0} e^{i T_{0} \omega_{0}} \frac{\partial}{\partial T_{2}} A{\left(T_{1},T_{2} \right)} - 2 i \omega_{0} e^{- i T_{0} \omega_{0}} \overline{\frac{\partial}{\partial T_{2}} A{\left(T_{1},T_{2} \right)}} + \frac{\partial^{2}}{\partial T_{0}^{2}} \operatorname{x_{3}}{\left(T_{0},T_{1},T_{2} \right)} = 0$

The to cancel out the secular term we let

$$
\begin{gather*}
    2i\omega_0 D_2 A + \dfrac{9\alpha_3 \omega_0^2 - 10\alpha_2^2 }{3\omega_0^2}A^2\overline{A} = 0
\end{gather*}
$$

Question: What if the secular terms arising from $i\omega_0$ and $-i \omega_0$ are handled together - do we get a single real equation?

Substitute the polar $A$

$$
\begin{gather*}
    A = \dfrac{1}{2}a e^{i\beta}
\end{gather*}
$$

```python
x3_sec = sp.Eq(2*sp.I*omega0*sp.diff(A, T_i[2]) + (9*alpha_i[1]*omega0**2 - 10*alpha_i[0]**2)/3/omega0**2 * A**2 * sp.conjugate(A), 0)

a = sp.Symbol('a', real=True)
beta = sp.Symbol('beta', real=True)

temp = x3_sec.subs(A, a*sp.exp(sp.I * beta)/2)
temp
```

$\displaystyle \frac{a^{3} \left(- \frac{10 \alpha_{2}^{2}}{3} + 3 \alpha_{3} \omega_{0}^{2}\right) e^{i \beta}}{8 \omega_{0}^{2}} + 2 i \omega_{0} \frac{\partial}{\partial T_{2}} \frac{a e^{i \beta}}{2} = 0$

```python
temp = sp.expand(temp)
temp_im = sp.im(temp.lhs)
temp_im
```

$\displaystyle - \frac{5 a^{3} \alpha_{2}^{2} \sin{\left(\beta \right)}}{12 \omega_{0}^{2}} + \frac{3 a^{3} \alpha_{3} \sin{\left(\beta \right)}}{8} + 2 \omega_{0} \operatorname{re}{\left(\frac{\partial}{\partial T_{2}} \frac{a e^{i \beta}}{2}\right)}$

```python
temp_re = sp.re(temp.lhs)
temp_re
```

$\displaystyle - \frac{5 a^{3} \alpha_{2}^{2} \cos{\left(\beta \right)}}{12 \omega_{0}^{2}} + \frac{3 a^{3} \alpha_{3} \cos{\left(\beta \right)}}{8} - 2 \omega_{0} \operatorname{im}{\left(\frac{\partial}{\partial T_{2}} \frac{a e^{i \beta}}{2}\right)}$

Thus separating into real and imaginary parts we obtain

$$
\begin{align*}
    \omega_0 D_2 a &=0\\
    omega_0 a D_2 \beta + \dfrac{10\alpha_2^2 - 9\alpha_3\omega_0^2}{24\omega_0^2}a^3 &= 0
\end{align*}
$$

$a$ is a constant and

$$
\begin{gather*}
    D_2\beta = - \dfrac{10\alpha_2^2 - 9\alpha_3\omega_0^3a}{24\omega_0^2}a^3
    \beta = \dfrac{9\alpha_3\omega_0^2 - 10\alpha_2^2  }{24\omega_0^3}a^2 T_2 + \beta_0
\end{gather*}
$$

Here $\beta_0$ is a constant. Now using $T_2 = \epsilon^2 t$ we find that

$$
\begin{gather*}
    A = \dfrac{1}{2}a \exp\left[ i\dfrac{9\alpha_3\omega_0^2 - 10\alpha_2^2  }{24\omega_0^3}a^3 \epsilon^2 t + i\beta_0 \right]
\end{gather*}
$$

and substituting in the expressions for $x_1$ and $x_2$ into the equations we have, we obtain the following final results

$$
\begin{gather*}
    x = \epsilon a \cos(\omega t + \beta_0) - \dfrac{\epsilon^2 a^2\alpha_2}{2\omega_0^2}\left[ 1 - \dfrac{1}{3}\cos(2\omega t + 2\beta_0) \right] + O(\epsilon^3)
\end{gather*}
$$

where

$$
\begin{gather*}
    \omega = \omega_0 \left[ 1 + \dfrac{9\alpha_3 \omega_0^2 - 10\alpha_2^2}{24\omega_0^4}\epsilon^2 a^2 \right] + O(\epsilon^3)
\end{gather*}
$$


```python
import loader
from sympy import *
init_printing()
from root.solver import *
```

Draw the phase portrait for the following system of ode
$$
    \vec{\mathbf{x'}} = \begin{bmatrix}
        -\frac{1}{2} & 1 \\
        -1 & -\frac{1}{2} \\
    \end{bmatrix} \vec{\mathbf{x}}
$$

```python
%matplotlib inline
phase_portrait(
    [
        [-1/2, 1],
        [-1, -1/2]
    ],
    -5, 5, -5, 5, 15, "vector field"
)
```

Draw the phase portrait for the following system of ode
$$
    \vec{\mathbf{x'}} = \begin{bmatrix}
        -3 & \sqrt{2} \\
        \sqrt{2} & -2 \\
    \end{bmatrix} \vec{\mathbf{x}}
$$

```python
phase_portrait(
    [
        [-3, 1.414],
        [1.414, -2]
    ],
    -10, 10, -10, 10, 15, "stream"
)
```

```python

```


# Exercício 1.0

## Problema

Em uma loja de CD´s existem apenas quatro tipos de preços que estão associados a cores. Assim os CD´s que ficam na loja não são marcados por preços e sim por cores.

Desenvolva uma aplicação que receba como entrada a cor do CD e mostre o preço. A loja está atualmente com a seguinte tabela de preços.

| Cor | Preço|
| :-: | :--: |
| Verde    | R\$ 10,00 |
| Azul     | R\$ 20,00 |
| Amarelo  | R\$ 30,00 |
| Vermelho | R\$ 40,00 |

## Análise

Em uma loja de CD´s existem apenas quatro tipos de preços que estão associados a cores.

Assim os CD´s que ficam na loja não são marcados por preços e sim por cores.

Desenvolva uma aplicação que receba como entrada a cor do CD e mostre o preço.

A loja está atualmente com a seguinte tabela de preços.

## Implicações / Proposições / Afirmações

* Na loja de CD's existem 4 preços.

* Existem 4 cores associadas a cada preço.

* Desenvolva uma aplicação que receba como entrada a cor do CD.

* mostre como saída o preço da cor recebida.

## Resolução e Resposta

```python
# Resolução e Resposta Aqui
cor_str = input('Digite uma das seguintes cores (verde, azul, amarelo e vermelho): ')
cor = cor_str.lower()

preco = 0

print('')

if (cor != 'verde' and cor != 'azul' and cor != 'amarelo' and cor != 'vermelho'):
    print('Valor da cor não encontrado.')

else:

    if (cor == 'verde'):
        preco = 10.00

    elif (cor == 'azul'):
        preco = 20.00

    elif (cor == 'amarelo'):
        preco = 30.00

    else:
        preco = 40.00

    print(f'A cor escolhida foi {cor} e o seu valor R${preco}')
```

    Digite uma das seguintes cores (verde, azul, amarelo e vermelho): azul

    A cor escolhida foi azul e o seu valor R$20.0

# Exercício 2.0

## Problema

Uma empresa decidiu dar uma gratificação de Natal a seus funcionários, baseada no número de horas extras e no número de horas que o funcionário faltou ao trabalho. O valor do prêmio é obtido pela consulta na tabela a seguir, em que:

\begin{equation}\label{eq:}
H = (horas\_extras) - 2/3 * (horas\_falta)
\end{equation}

| H | Gratificação |
| :-: | :--------: |
| Maior que 2400 | R\$ 500,00 |
| 1801 até 2400  | R\$ 400,00 |
| 1201 até 1800  | R\$ 300,00 |
| 600 até 1200   | R\$ 200,00 |
| Menor que 600  | R\$ 100,00 |

Faça uma aplicação que receba o número de horas extras e o número de horas-falta de um funcionário e imprima o valor de sua gratificação.

## Análise

Uma empresa decidiu dar uma gratificação de Natal a seus funcionários baseada no número de horas extras e no número de horas que o funcionário faltou ao trabalho.

O valor do prêmio é obtido pela consulta na tabela a seguir, em que:

H = (horas\_extras) - 2/3 * (horas\_falta)

Faça uma aplicação que receba o número de horas extras e o número de horas-falta de um funcionário e imprima o valor de sua gratificação.

## Implicações / Proposições / Afirmações

* Uma empresa decidiu dar uma gratificação de Natal a seus funcionários baseada no número de horas extras e no número de horas que o funcionário faltou ao trabalho.

* O valor do prêmio é obtido pela consulta na tabela.

* H = (horas_extras) - 2/3 * (horas_falta)

* Faça uma aplicação que receba:
 - o número de horas extras,
 - o número de horas-falta de um funcionário

* imprima o valor de sua gratificação de acordo com os valores da tabela.

## Resolução e Resposta

```python
# Resolução e Resposta Aqui

horas_extras = float(input("Digite o número de horas extras realizadas de um funcionário: "))
horas_falta = float(input("Digite o número de horas-falta de um funcionário: "))

horas = (horas_extras) - 2 / 3 * (horas_falta)

gratificacao = 0

if (horas < 600.00):
    gratificacao = 100.00

elif (horas >= 600.00 and horas <= 1200.00):
    gratificacao = 200.00

elif (horas >= 1201.00 and horas <= 1800.00):
    gratificacao = 300.00

elif (horas >= 1801.00 and horas <= 2400.00):
    gratificacao = 400.00

else:
    gratificacao = 500.00

print('')
print(f'O total de horas foi de {horas} e a gratificação R${gratificacao}')
```

    Digite o número de horas extras realizadas de um funcionário: 4000
    Digite o número de horas-falta de um funcionário: 2

    O total de horas foi de 3998.6666666666665 e a gratificação R$500.0


# 3 - Perfectly Matched Layer (PML)

# 3.1 - Introduction

In this notebook we describe the *Perfectly Matched Layer* method, originally proposed by Berenger for electromagnetic waves, a scheme also employing an absorbing layer to reduce the wave reflections coming from the computational boundaries. This is one of the most efficient schemes for this purpose. The formulation that we present here, designed for the wave equation in the form of a second order equation (instead of its formulation as a first order system), is the one proposed By Grote and Sim.

We will use all the previous set ups employed in the notebooks Introduction to Acoustic Problem and Damping, detailling only the new features specific for the
PML method.

# 3.2 - Acoustic Problem with PML

We again use an extended spatial domain $\Omega=\left[x_{I}-L_{x},x_{F}+L_{x}\right] \times\left[z_{I},z_{F}+L_{z}\right]$, with an absorption region as depicted in blue in the figure bellow.
In the PML scheme, two auxiliary functions are included, which will provide the adequate damping of the wave reflections. The design of the method is such that it would ideally suppress all the reflections in a continuous setting, but since we employ a finite difference discretization some reflections remain, although strongly attenuated.

The set of equations for the acoustic wave equaation with PML, including the auxiliary functions, is given by:

\begin{eqnarray}
        \frac{\partial^2 u(x,z,t)}{\partial t^2}
        + (\zeta_1(x,z)+\zeta_2(x,z))\frac{\partial u(x,z,t)}{\partial t}
	+ \zeta_1(x,z) \zeta_2(x,z) u(x,z,t)& = & \\
	 c^2(x,z)\Delta u(x,z,t)
	+\frac{\partial \phi_1(x,z,t)}{\partial x}+
	\frac{\partial \phi_2(x,z,t)}{\partial z}+  f(x,z,t) & &
\end{eqnarray}

\begin{equation}
        \frac{\partial \phi_1(x,z,t)}{\partial t} =
        - \zeta_1(x,z)\phi_1(x,z,t)+c^2(x,z)(\zeta_2(x,z)-\zeta_1(x,z))
        \frac{\partial u(x,z,t)}{\partial x}
\end{equation}

\begin{equation}
        \frac{\partial \phi_2(x,z,t)}{\partial t} =
        - \zeta_2(x,z)\phi_2(x,z,t)+c^2(x,z)(\zeta_1(x,z)-\zeta_2(x,z))
        \frac{\partial u(x,z,t)}{\partial y}
\end{equation}

where $u(x,z,t):D\rightarrow \mathbb{R}$ is the wave displacement,  $f(x,z,t)$ is the source term, $c(x,z)$ is the wave speed and $\phi_{1}(x,z)$ and $\phi_{2}(x,z)$ are the auxiliary variables, which will be non zero only in the absorption region. The damping functions $\zeta_1(x,z)$ and $\zeta_2(x,z)$ will be defined as in the Damping notebook. The initial and outer boundary conditions for the displacement $u$ are the same as in the previous notebooks. The auxiliary functions will also be kept equal to zero in all the outer boundary of
$\Omega$. The Ricker source term is defined as before.

# 3.3 - Finite Difference Operators and Discretization of Spatial and Temporal Domains

We employ the same methods as in the previous notebooks.

# 3.4 - *Staggered* e *non-staggered* variables

In the discretization with PML we will use staggering of some variables, which is a convenient way for the centered discretization of the partial derivatives of these variables.

The staggered spatial domain is $\Omega^{x,y}_\Delta=\{(x_{i+1/2},z_{j+1/2}, i=0,\cdots,nx-1,\ j=0,\cdots,nz-1\}, \mbox{ with } x_{i+1/2}=(i+\frac{1}{2})\Delta x \mbox{ and } z_{j+1/2}=(j+\frac{1}{2})\Delta z$. Some variables will also be staggered in time, being defined
at intermediary time instantes $t_{k+1/2}=t_k+\Delta t$.

In the formulation of PML that is described here we will define the variables as follows:

- The variable $u(x,z,t)$ is a *non-staggered* variable;
- The variables $\phi_{1}(x,z,t)$ and $\phi_{2}(x,z,t)$ are *staggered* variables;
- The functions $\zeta_{1}(x,z)$, $\zeta_{2}(x,z)$, $c(x,z)$ and $f(x,z,t)$ will be used as *staggered* or *non-staggered* variables depending on the equation in which they are required.

The direct implication of *staggered* and *non-staggered* variables appearing in the same equation is that:

- When updating $u(x,z,t)$ we employ averages of neighboring values of $\phi_{1}(x,z,t)$ and $\phi_{2}(x,z,t)$ in order to have them on the non-staggered grid

- When updating $\phi_{1}(x,z,t)$ and $\phi_{2}(x,z,t)$ we use averages of neighboring values of $u(x,z,t)$ to define it on the staggered grid.

The calculation of these averages will be explicit when we write the equations in the Devito code.

# 3.5 - Standard Problem

Redeeming the Standard Problem definitions discussed on the notebook Introduction to Acoustic Problem we have that:

- $x_{I}$ =  0.0 Km;
- $x_{F}$ =  1.0 Km = 1000 m;
- $z_{I}$ =  0.0 Km;
- $z_{F}$ =  1.0 Km = 1000 m;

The spatial discretization parameters are given by:
- $\Delta x$ = 0.01 km = 10m;
- $\Delta z$ = 0.01 km = 10m;

Let's consider a $I$ the time domain with the following limitations:

- $t_{I}$ = 0 s = 0 ms;
- $t_{F}$ = 1 s = 1000 ms;

The temporal discretization parameters are given by:

- $\Delta t$ $\approx$ 0.0016 s = 1.6 ms;
- $NT$ = 626.

The source term, velocity model and positioning of receivers will be as in the previous notebooks.

# 3.6 - Damping Functions

We choose the pair of functions $\zeta_{1}(x,z)$ and $\zeta_{2}(x,z)$ as in the  Damping notebook. They will act in the directions $x$ and $z$, respectively.

# 3.7 - Numerical Simulations

For the numerical simulations of this notebook we use several parts the notebook codes Damping. We will highlight only the significant changes and comment on the most relevant operations, since the computational structure of the acoustic equation with PML is very similar to the case of the acoustic equation with Damping.

We import the following Python and Devito packages:

```python
# NBVAL_IGNORE_OUTPUT

import numpy                   as np
import matplotlib.pyplot       as plot
import math                    as mt
import matplotlib.ticker       as mticker
from   mpl_toolkits.axes_grid1 import make_axes_locatable
from   matplotlib              import cm
```

From Devito's library of examples we import the following structures:

```python
# NBVAL_IGNORE_OUTPUT

%matplotlib inline
from   examples.seismic  import TimeAxis
from   examples.seismic  import RickerSource
from   examples.seismic  import Receiver
from   devito            import SubDomain, Grid, NODE, TimeFunction, Function, Eq, solve, Operator
```

The mesh parameters that we choose define the domain $\Omega_{0}$ plus the absorption region. For this, we use the following data:

```python
nptx   =  101
nptz   =  101
x0     =  0.
x1     =  1000.
compx  =  x1-x0
z0     =  0.
z1     =  1000.
compz  =  z1-z0;
hxv    =  (x1-x0)/(nptx-1)
hzv    =  (z1-z0)/(nptz-1)
```

The number of points of the absorption layer in the directions $x$ and $z$ are given, respectively, by:

```python
npmlx  = 20
npmlz  = 20
```

The lengths $L_{x}$ and $L_{z}$ are given, respectively, by:

```python
lx = npmlx*hxv
lz = npmlz*hzv
```

We define the *grid*:

```python
nptx   =  nptx + 2*npmlx
nptz   =  nptz + 1*npmlz
x0     =  x0 - hxv*npmlx
x1     =  x1 + hxv*npmlx
compx  =  x1-x0
z0     =  z0
z1     =  z1 + hzv*npmlz
compz  =  z1-z0
origin  = (x0,z0)
extent  = (compx,compz)
shape   = (nptx,nptz)
spacing = (hxv,hzv)
```

As in the case of the Damping acoustic equation, we can here split the computations in the two subdomains:

- In the blue region:

\begin{eqnarray}
        \frac{\partial^2 u(x,z,t)}{\partial t^2}
        + (\zeta_1(x,z)+\zeta_2(x,z))\frac{\partial u(x,z,t)}{\partial t}
	+ \zeta_1(x,z) \zeta_2(x,z) u(x,z,t)& = & \\
	 c^2(x,z)\Delta u(x,z,t)
	+\frac{\partial \phi_1(x,z,t)}{\partial x}+
	\frac{\partial \phi_2(x,z,t)}{\partial z}+  f(x,z,t) & &
\end{eqnarray}

\begin{equation}
        \frac{\partial \phi_1(x,z,t)}{\partial t} =
        - \zeta_1(x,z)\phi_1(x,z,t)+c^2(x,z)(\zeta_2(x,z)-\zeta_1(x,z))
        \frac{\partial u(x,z,t)}{\partial x}
\end{equation}

\begin{equation}
        \frac{\partial \phi_2(x,z,t)}{\partial t} =
        - \zeta_2(x,z)\phi_2(x,z,t)+c^2(x,z)(\zeta_1(x,z)-\zeta_2(x,z))
        \frac{\partial u(x,z,t)}{\partial y}
\end{equation}

- In the white region:

\begin{equation}
u(x,z,t)_{tt}-c^2(x,z)\Delta(u(x,z,t))=c^2(x,z)f(x,z,t).
\end{equation}

We use the structure of the *subdomains* to represent the white region and the blue region.

First, we define the white region, naming it as *d0

```python
class d0domain(SubDomain):
    name = 'd0'
    def define(self, dimensions):
        x, z = dimensions
        return {x: ('middle', npmlx, npmlx), z: ('middle', 0, npmlz)}
d0_domain = d0domain()
```

The blue region is the union of 3 subdomains:

```python
class d1domain(SubDomain):
    name = 'd1'
    def define(self, dimensions):
        x, z = dimensions
        return {x: ('left',npmlx), z: z}
d1_domain = d1domain()

class d2domain(SubDomain):
    name = 'd2'
    def define(self, dimensions):
        x, z = dimensions
        return {x: ('right',npmlx), z: z}
d2_domain = d2domain()

class d3domain(SubDomain):
    name = 'd3'
    def define(self, dimensions):
        x, z = dimensions
        return {x: ('middle', npmlx, npmlx), z: ('right',npmlz)}
d3_domain = d3domain()
```

The figure below represents the division of domains that we did previously:

The *spatial grid* is then defined:

```python
grid = Grid(origin=origin, extent=extent, shape=shape, subdomains=(d0_domain,d1_domain,d2_domain,d3_domain), dtype=np.float64)
```

The velocity field is needed in both staggered and non-staggered grids. As before, we read the file and interpolate it to the non-staggered grid. From these values we interpolate to the staggered grid.

```python
v0 = np.zeros((nptx,nptz))
v1 = np.zeros((nptx-1,nptz-1))
X0 = np.linspace(x0,x1,nptx)
Z0 = np.linspace(z0,z1,nptz)

x10 = x0+lx
x11 = x1-lx

z10 = z0
z11 = z1 - lz

xm = 0.5*(x10+x11)
zm = 0.5*(z10+z11)

pxm = 0
pzm = 0

for i in range(0,nptx):
    if(X0[i]==xm): pxm = i

for j in range(0,nptz):
    if(Z0[j]==zm): pzm = j

p0 = 0
p1 = pzm
p2 = nptz
v0[0:nptx,p0:p1] = 1.5
v0[0:nptx,p1:p2] = 2.5

p0 = 0
p1 = pzm
p2 = nptz-1
v1[0:nptx-1,p0:p1] = 1.5
v1[0:nptx-1,p1:p2] = 2.5
```

Previously we introduce the local variables *x10,x11,z10,z11,xm,zm,pxm* and *pzm* that help us to create a specific velocity field, where we consider the whole domain (including the absorpion region). Below we include a routine to plot the velocity field.

```python
def graph2dvel(vel):
        plot.figure()
        plot.figure(figsize=(16,8))
        fscale =  1/10**(3)
        scale  = np.amax(vel[npmlx:-npmlx,0:-npmlz])
        extent = [fscale*(x0+lx),fscale*(x1-lx), fscale*(z1-lz), fscale*(z0)]
        fig = plot.imshow(np.transpose(vel[npmlx:-npmlx,0:-npmlz]), vmin=0.,vmax=scale, cmap=cm.seismic, extent=extent)
        plot.gca().xaxis.set_major_formatter(mticker.FormatStrFormatter('%.1f km'))
        plot.gca().yaxis.set_major_formatter(mticker.FormatStrFormatter('%.1f km'))
        plot.title('Velocity Profile')
        plot.grid()
        ax = plot.gca()
        divider = make_axes_locatable(ax)
        cax = divider.append_axes("right", size="5%", pad=0.05)
        cbar = plot.colorbar(fig, cax=cax, format='%.2e')
        cbar.set_label('Velocity [km/s]')
        plot.show()
```

Below we include the plot of velocity field.

```python
# NBVAL_IGNORE_OUTPUT

graph2dvel(v0)
```

We then define the temporal properties:

```python
t0    = 0.
tn    = 1000.
CFL   = 0.4
vmax  = np.amax(v0)
dtmax = np.float64((min(hxv,hzv)*CFL)/(vmax))
ntmax = int((tn-t0)/dtmax)+1
dt0   = np.float64((tn-t0)/ntmax)
```

```python
time_range = TimeAxis(start=t0,stop=tn,num=ntmax+1)
nt         = time_range.num - 1
```

The symbolic values associated with the spatial and temporal grids that are used in the composition of the equations are given by:

```python
(hx,hz) = grid.spacing_map
(x, z)  = grid.dimensions
t       = grid.stepping_dim
dt      = grid.stepping_dim.spacing
```

We set the parameters for the Ricker source:

```python
f0      = 0.01
nsource = 1
xposf   = 0.5*(compx-2*npmlx*hxv)
zposf   = hzv
```

```python
src = RickerSource(name='src',grid=grid,f0=f0,npoint=nsource,time_range=time_range,staggered=NODE,dtype=np.float64)
src.coordinates.data[:, 0] = xposf
src.coordinates.data[:, 1] = zposf
```

Below we include the plot of Ricker source.

```python
# NBVAL_IGNORE_OUTPUT

src.show()
```

For the receivers:

```python
nrec   = nptx
nxpos  = np.linspace(x0,x1,nrec)
nzpos  = hzv
```

```python
rec = Receiver(name='rec',grid=grid,npoint=nrec,time_range=time_range,staggered=NODE,dtype=np.float64)
rec.coordinates.data[:, 0] = nxpos
rec.coordinates.data[:, 1] = nzpos
```

The displacement field *u* is allocated

```python
u = TimeFunction(name="u",grid=grid,time_order=2,space_order=2,staggered=NODE,dtype=np.float64)
```

The auxiliary functions $\phi_{1}(x,z,t)$ and $\phi_{2}(x,z,t)$ will be two fields of second order in time and space, which use points of type *staggered*.

```python
phi1 = TimeFunction(name="phi1",grid=grid,time_order=2,space_order=2,staggered=(x,z),dtype=np.float64)
phi2 = TimeFunction(name="phi2",grid=grid,time_order=2,space_order=2,staggered=(x,z),dtype=np.float64)
```

We set the velocity on the non-staggered grid

```python
vel0 = Function(name="vel0",grid=grid,space_order=2,staggered=NODE,dtype=np.float64)
vel0.data[:,:] = v0[:,:]
```

and on the staggered one. Notice that the field has one less point in each direction.

```python
vel1 = Function(name="vel1", grid=grid,space_order=2,staggered=(x,z),dtype=np.float64)
vel1.data[0:nptx-1,0:nptz-1] = v1
```

Taking into account the dimension of the array *v1* and the dimension of the field *vel1* we will complete the line *nptx-1* with information from the line *nptx-2* and the column *nptz-1* with information from the column *nptz-2*, information from the *v1* array. This copy of information does not alter the properties of the *vel1* velocity field in view of its structure of constant profiles on the part. Copying information is done by the following sequence of commands:

```python
vel1.data[nptx-1,0:nptz-1] = vel1.data[nptx-2,0:nptz-1]
vel1.data[0:nptx,nptz-1]   = vel1.data[0:nptx,nptz-2]
```

We set the source term and receivers

```python
src_term = src.inject(field=u.forward,expr=src*dt**2*vel0**2)
```

```python
rec_term = rec.interpolate(expr=u)
```

The next step is to create the structures that reproduce the functions $\zeta_{1}(x,z)$ and $\zeta_{2}(x,z)$ and then assign these functions to fields in *non-staggered* and *staggered* grids.

We define the region $\Omega_{0}$ by choosing the values of *x0pml* and *x1pml* in the direction $x$ and *z0pml* and *z1pml* in the direction $z$. These points satisfy the following relations with the lengths $L_{x}$ and $L_{z}$:

- x0pml = x0 + $L_{x}$;
- x1pml = x1 - $L_{x}$;
- z0pml = z0;
- z1pml = z1 - $L_{z}$;

In terms of program variables, we have the following definitions:

```python
x0pml  = x0 + npmlx*hxv
x1pml  = x1 - npmlx*hxv
z0pml  = z0
z1pml  = z1 - npmlz*hzv
```

Having set the boundaries of $\Omega$, we create a function *fdamp*, which represents $\zeta_{1}(x,z)$ (when $i=1$) and $\zeta_{2}(x,z)$ (when $i=2$).

```python
def fdamp(x,z,i):

    quibar  = 0.05

    if(i==1):
        a = np.where(x<=x0pml,(np.abs(x-x0pml)/lx),np.where(x>=x1pml,(np.abs(x-x1pml)/lx),0.))
        fdamp = quibar*(a-(1./(2.*np.pi))*np.sin(2.*np.pi*a))
    if(i==2):
        a = np.where(z<=z0pml,(np.abs(z-z0pml)/lz),np.where(z>=z1pml,(np.abs(z-z1pml)/lz),0.))
        fdamp = quibar*(a-(1./(2.*np.pi))*np.sin(2.*np.pi*a))

    return fdamp
```

We created the damping function that represents $\zeta_{1}(x,z)$ and $\zeta_{2}(x,z)$. We now define arrays with the damping function values on grid points (staggered and non-staggered): c

- The arrays *D01* and *D02* are associated with points of type *staggered* and represent the functions $\zeta_{1}(x,z)$ and $\zeta_{2}(x,z)$, respectively.

- The arrays *D11* and *D12* are associated with points of type *non-staggered* and represent the functions $\zeta_{1}(x,z)$ and $\zeta_{2}(x,z)$, respectively.

```python
def generatemdamp():

    X0     = np.linspace(x0,x1,nptx)
    Z0     = np.linspace(z0,z1,nptz)
    X0grid,Z0grid = np.meshgrid(X0,Z0)
    X1   = np.linspace((x0+0.5*hxv),(x1-0.5*hxv),nptx-1)
    Z1   = np.linspace((z0+0.5*hzv),(z1-0.5*hzv),nptz-1)
    X1grid,Z1grid = np.meshgrid(X1,Z1)

    D01 = np.zeros((nptx,nptz))
    D02 = np.zeros((nptx,nptz))
    D11 = np.zeros((nptx,nptz))
    D12 = np.zeros((nptx,nptz))

    D01 = np.transpose(fdamp(X0grid,Z0grid,1))
    D02 = np.transpose(fdamp(X0grid,Z0grid,2))

    D11 = np.transpose(fdamp(X1grid,Z1grid,1))
    D12 = np.transpose(fdamp(X1grid,Z1grid,2))

    return D01, D02, D11, D12
```

```python
D01, D02, D11, D12 = generatemdamp();
```

Below we include a routine to plot the damping fields.

```python
def graph2damp(D):
    plot.figure()
    plot.figure(figsize=(16,8))
    fscale = 1/10**(-3)
    fscale = 10**(-3)
    scale  = np.amax(D)
    extent = [fscale*x0,fscale*x1, fscale*z1, fscale*z0]
    fig = plot.imshow(np.transpose(D), vmin=0.,vmax=scale, cmap=cm.seismic, extent=extent)
    plot.gca().xaxis.set_major_formatter(mticker.FormatStrFormatter('%.1f km'))
    plot.gca().yaxis.set_major_formatter(mticker.FormatStrFormatter('%.1f km'))
    plot.title('Absorbing Layer Function')
    plot.grid()
    ax = plot.gca()
    divider = make_axes_locatable(ax)
    cax = divider.append_axes("right", size="5%", pad=0.05)
    cbar = plot.colorbar(fig, cax=cax, format='%.2e')
    cbar.set_label('Damping')
    plot.show()
```

Below we include the plot of damping field in $x$ direction.

```python
# NBVAL_IGNORE_OUTPUT

graph2damp(D01)
```

Below we include the plot of damping field in $z$ direction.

```python
# NBVAL_IGNORE_OUTPUT

graph2damp(D02)
```

As pointed out previously, the functions $\zeta_{1}(x,z)$ and $\zeta_{2}(x,z)$ define  damping in the directions $x$ and $z$ respectively. They will be identified  with the symbolic names of *dampx* and *dampz*, respectively.

As damping acts on non-staggered and staggered grids, we will identify *dampx0* and *dampz0* as being damping on the non-staggered points grid. Similarly, we will identify *dampx1* and *dampz1* as being the damping on the staggered points grid.

```python
dampx0 = Function(name="dampx0", grid=grid,space_order=2,staggered=NODE ,dtype=np.float64)
dampz0 = Function(name="dampz0", grid=grid,space_order=2,staggered=NODE ,dtype=np.float64)
dampx0.data[:,:] = D01
dampz0.data[:,:] = D02
```

```python
dampx1 = Function(name="dampx1", grid=grid,space_order=2,staggered=(x,z),dtype=np.float64)
dampz1 = Function(name="dampz1", grid=grid,space_order=2,staggered=(x,z),dtype=np.float64)
dampx1.data[0:nptx-1,0:nptz-1] = D11
dampz1.data[0:nptx-1,0:nptz-1] = D12
```

In terms of dimensions, the arrays *D11* and *D12* have dimension $(nptx-1)\times (nptz-1)$. As our grid has $nptx\times nptz$ points, so we complete the line *nptx-1* with information from the line *nptx-2* and the column *nptz-1* with information from the column *nptz-2*, in fields *dampx1* and *dampz1* using the arrays *D11* and *D12*, respectively.

```python
dampx1.data[nptx-1,0:nptz-1]   = dampx1.data[nptx-2,0:nptz-1]
dampx1.data[0:nptx,nptz-1]     = dampx1.data[0:nptx,nptz-2]
dampz1.data[nptx-1,0:nptz-1]   = dampz1.data[nptx-2,0:nptz-1]
dampz1.data[0:nptx,nptz-1]     = dampz1.data[0:nptx,nptz-2]
```

As we saw previously, the acoustic equation with PML has the formulations

 In the white (interior) region:

- eq1 = u.dt2 - vel0 * vel0 * u.laplace;

 And in the blue (absorption) region:

- eq2  = u.dt2 + (dampx0+dampz0) * u.dtc + (dampx0 * dampz0) * u - u.laplace * vel0 * vel0 + $\bar{phi1}$[t,x,z] + $\bar{phi2}$[t,x,z];

- eq3 = phi1.dt + dampx1 * 0.5 * (phi1.forward+phi1) -(dampz1-dampx1) * $\bar{u}$[t,x,z] * vel1 * vel1

- eq4 = phi2.dt + dampz1 * 0.5 * (phi2.forward+phi2) -(dampx1-dampz1) * $\bar{u}$[t,x,z] * vel1 * vel1

In the equation *eq2* the term $\bar{phi1}$[t,x,z] is given by following expression:

- -(0.5/hx) * (phi1[t,x,z-1]+phi1[t,x,z]-phi1[t,x-1,z-1]-phi1[t,x-1,z]);

And the term $\bar{phi2}$[t,x,z] in the equation *eq2* is given by:

-  -(0.5/hz) * (phi2[t,x-1,z]+phi2[t,x,z]-phi2[t,x-1,z-1]-phi2[t,x,z-1]);

In the equation *eq3* the term $\bar{u}$[t,x,z] is given by:

- a1 = u[t+1,x+1,z] + u[t+1,x+1,z+1] - u[t+1,x,z] - u[t+1,x,z+1];
- a2 = u[t,x+1,z]   + u[t,x+1,z+1]   - u[t,x,z]   - u[t,x,z+1];
- $\bar{u}$[t,x,z] = 0.5 * (0.5/hx) * (a1+a2);

In the equation *eq4* the term $\bar{u}$[t,x,z] is given by:

- b1 = u[t+1,x,z+1] + u[t+1,x+1,z+1] - u[t+1,x,z] - u[t+1,x+1,z];
- b2 = u[t,x,z+1]   + u[t,x+1,z+1]   - u[t,x,z]   - u[t,x+1,z];
-  $\bar{u}$[t,x,z] = 0.5 * (0.5/hz) * (b1+b2)

Then, using the operator *Eq(eq)* and the equation in the format associated with Devito we create the *pdes* that represent the acoustic equations with PML without the external force term in the white and blue regions, respectively by:

```python
# White Region
pde01   = Eq(u.dt2-u.laplace*vel0**2)

# Blue Region
pde02a  = u.dt2   + (dampx0+dampz0)*u.dtc + (dampx0*dampz0)*u - u.laplace*vel0*vel0
pde02b  = - (0.5/hx)*(phi1[t,x,z-1]+phi1[t,x,z]-phi1[t,x-1,z-1]-phi1[t,x-1,z])
pde02c  = - (0.5/hz)*(phi2[t,x-1,z]+phi2[t,x,z]-phi2[t,x-1,z-1]-phi2[t,x,z-1])
pde02   = Eq(pde02a + pde02b + pde02c)

pde10 = phi1.dt + dampx1*0.5*(phi1.forward+phi1)
a1    = u[t+1,x+1,z] + u[t+1,x+1,z+1] - u[t+1,x,z] - u[t+1,x,z+1]
a2    = u[t,x+1,z]   + u[t,x+1,z+1]   - u[t,x,z]   - u[t,x,z+1]
pde11 = -(dampz1-dampx1)*0.5*(0.5/hx)*(a1+a2)*vel1**2
pde1  = Eq(pde10+pde11)

pde20 = phi2.dt + dampz1*0.5*(phi2.forward+phi2)
b1    = u[t+1,x,z+1] + u[t+1,x+1,z+1] - u[t+1,x,z] - u[t+1,x+1,z]
b2    = u[t,x,z+1]   + u[t,x+1,z+1]   - u[t,x,z]   - u[t,x+1,z]
pde21 = -(dampx1-dampz1)*0.5*(0.5/hz)*(b1+b2)*vel1**2
pde2  = Eq(pde20+pde21)
```

Now we define the *stencils* for each of the *pdes* that we created previously. The *pde01* is defined on *subdomain* *d0*.

```python
stencil01 =  Eq(u.forward,solve(pde01,u.forward) ,subdomain = grid.subdomains['d0'])
```

The *pdes*: *pde02*, *pde1* and *pde2* are defined on the union of the subdomains *d1*, *d2* and *d3*.

```python
subds = ['d1','d2','d3']
```

```python
stencil02 = [Eq(u.forward,solve(pde02, u.forward),subdomain = grid.subdomains[subds[i]]) for i in range(0,len(subds))]
stencil1 = [Eq(phi1.forward, solve(pde1,phi1.forward),subdomain = grid.subdomains[subds[i]]) for i in range(0,len(subds))]
stencil2 = [Eq(phi2.forward, solve(pde2,phi2.forward),subdomain = grid.subdomains[subds[i]]) for i in range(0,len(subds))]
```

The boundary conditions  are set

```python
bc  = [Eq(u[t+1,0,z],0.),Eq(u[t+1,nptx-1,z],0.),Eq(u[t+1,x,nptz-1],0.),Eq(u[t+1,x,0],u[t+1,x,1])]
```

We then define the operator (*op*) that will join the acoustic equation, source term, boundary conditions and receivers.

- 1. The acoustic wave equation in the *d0* region: *[stencil01];*
- 2. The acoustic wave equation in the *d1*, *d2* and *d3* regions: *[stencil02];*
- 3. Source term: *src_term;*
- 4. Boundary Condition: *bc;*
- 5. Auxiliary function $\phi_{1}(x,z,t)$ in the *d1*, *d2* and *d3* regions: *[stencil1];*
- 6. Auxiliary function $\phi_{2}(x,z,t)$ in the *d1*, *d2* and *d3* regions: *[stencil2];*
- 7. Receivers: *rec_term;*

We then define the following *op*:

```python
# NBVAL_IGNORE_OUTPUT

op = Operator([stencil01,stencil02] + src_term + bc + [stencil1,stencil2] + rec_term,subs=grid.spacing_map)
```

So that there are no residuals in the variables of interest, we reset the fields *u*, *phi1* and *phi2* as follows:

```python
u.data[:]     = 0.
phi1.data[:]  = 0.
phi2.data[:]  = 0.
```

We assign to *op* the number of time steps it must execute and the size of the time step in the local variables *time* and *dt*, respectively. This assignment is done as in Introduction to Acoustic Problem, where we have the following attribution structure:

```python
# NBVAL_IGNORE_OUTPUT

op(time=nt,dt=dt0)
```

    Operator `Kernel` run in 0.06 s

    PerformanceSummary([(PerfKey(name='section0', rank=None),
                         PerfEntry(time=0.024502999999999806, gflopss=0.0, gpointss=0.0, oi=0.0, ops=0, itershapes=[])),
                        (PerfKey(name='section1', rank=None),
                         PerfEntry(time=0.00010099999999999995, gflopss=0.0, gpointss=0.0, oi=0.0, ops=0, itershapes=[])),
                        (PerfKey(name='section2', rank=None),
                         PerfEntry(time=4.399999999999997e-05, gflopss=0.0, gpointss=0.0, oi=0.0, ops=0, itershapes=[])),
                        (PerfKey(name='section3', rank=None),
                         PerfEntry(time=0.025268000000000068, gflopss=0.0, gpointss=0.0, oi=0.0, ops=0, itershapes=[])),
                        (PerfKey(name='section4', rank=None),
                         PerfEntry(time=0.0011940000000000047, gflopss=0.0, gpointss=0.0, oi=0.0, ops=0, itershapes=[]))])

We view the result of the displacement field at the end time using the *graph2d* routine given by:

```python
def graph2d(U):
    plot.figure()
    plot.figure(figsize=(16,8))
    fscale =  1/10**(3)
    scale  = np.amax(U[npmlx:-npmlx,0:-npmlz])/10.
    extent = [fscale*x0pml,fscale*x1pml,fscale*z1pml,fscale*z0pml]
    fig = plot.imshow(np.transpose(U[npmlx:-npmlx,0:-npmlz]),vmin=-scale, vmax=scale, cmap=cm.seismic, extent=extent)
    plot.gca().xaxis.set_major_formatter(mticker.FormatStrFormatter('%.1f km'))
    plot.gca().yaxis.set_major_formatter(mticker.FormatStrFormatter('%.1f km'))
    plot.axis('equal')
    plot.title('Map - Acoustic Problem PML Devito')
    plot.grid()
    ax = plot.gca()
    divider = make_axes_locatable(ax)
    cax = divider.append_axes("right", size="5%", pad=0.05)
    cbar = plot.colorbar(fig, cax=cax, format='%.2e')
    cbar.set_label('Displacement [km]')
    plot.draw()
    plot.show()
```

```python
# NBVAL_IGNORE_OUTPUT

graph2d(u.data[0,:,:])
```

Realize that the solution obtained here has a reduction in the noises when we compared with the results displayed on the notebook Introduction to Acoustic Problem. We plot the result of the Receivers using the *graph2drec* routine.

```python
def graph2drec(rec):
        plot.figure()
        plot.figure(figsize=(16,8))
        fscaled = 1/10**(3)
        fscalet = 1/10**(3)
        scale   = np.amax(rec[:,npmlx:-npmlx])/10.
        extent  = [fscaled*x0pml,fscaled*x1pml, fscalet*tn, fscalet*t0]
        fig = plot.imshow(rec[:,npmlx:-npmlx], vmin=-scale, vmax=scale, cmap=cm.seismic, extent=extent)
        plot.gca().xaxis.set_major_formatter(mticker.FormatStrFormatter('%.1f km'))
        plot.gca().yaxis.set_major_formatter(mticker.FormatStrFormatter('%.1f s'))
        plot.axis('equal')
        plot.title('Receivers Signal Profile with PML - Devito')
        ax = plot.gca()
        divider = make_axes_locatable(ax)
        cax = divider.append_axes("right", size="5%", pad=0.05)
        cbar = plot.colorbar(fig, cax=cax, format='%.2e')
        plot.show()
```

```python
# NBVAL_IGNORE_OUTPUT

graph2drec(rec.data)
```

```python
assert np.isclose(np.linalg.norm(rec.data), 990, rtol=1)
```

# 3.8 - Conclusions

We conclude our small approach to the PML method applied to the Acoustic Problem, including simulations in the Devito environment for this problem. With this study we can see that:

- When we use the PML strategy to reduce reflections at the end of the simulation, we observe a reduction in the amount of noise in our image when compared to the results of the notebook Introduction to Acoustic Problem.

# 3.9 - References

- Berenger, J.-P. (1994). "A perfectly matched layer for the absorption of electromagnetic waves", Journal of Computational Physics, 114(2), 185-200. DOI: 10.1006/jcph.1994.1159. Reference Link.

- Grote, M. J. and Sim, I. (2010). "Efficient PML for the wave equation", arXiv preprint arXiv:1001.0319. Reference Link.


# Deutsch-Jozsa algorithm

We explain the Deutsch-Jozsa algorithm, a generalization of the Deutsch algorithm.
For function $f$, which can take $2^n$ kinds of inputs (00...000 to 11...111), we assume that one of the following conditions holds.

1.  $f(x)$ is same for all inputs,
namely, $f(x)=0$ for all $x$ or $f(x)=1$ for all $x$.

2. $f(x)$ is different in half of the inputs,
namely, $f(x)=0$ for $2^{n-1}$ of inputs $x$, and $f(x)=1$ for others.

This algorithm determines whether Oracle is 1\. or 2\. above.

The circuit is here.

It has $n$ qubits $\lvert 0\rangle$ and 1qubit $\lvert 1\rangle$ as  initial states.

Now let's check the states.

$$
\begin{align}
\lvert \psi_1\rangle &= \biggl(\otimes^n H\lvert 0\rangle \biggr)\otimes H \lvert 1\rangle \\
&= \frac{1}{ \sqrt{2^{n+1}} } \sum^{2^n}_{x\in \{ 0, 1 \}^n} \bigl( \lvert x\rangle \otimes (\lvert 0\rangle - \lvert 1\rangle) \bigr) \\
&= \frac{1}{ \sqrt{2^{n+1}} } \sum^{2^n}_{x\in \{ 0, 1 \}^n} \lvert x\rangle \otimes \lvert 0\rangle - \frac{1}{ \sqrt{2^{n+1}} } \sum^{2^n}_{x\in \{ 0, 1 \}^n} \lvert x\rangle \otimes \lvert 1\rangle \\
\end{align}
$$

For the $n$ th qubit, you can see that swapping $\lvert 0\rangle$ and $\lvert 1\rangle$ changes the overall sign.

Next, consider $\psi_2$.
Because $f(x)$ operates to $n$ th qubit,

$$
f(x) = 0 \to n\text{ th qubit is NOT be flipped.}
$$
$$
f(x) = 1 \to n\text{ th qubit is flipped.}
$$

Dividing the summation by $f(x) = 0 \text{ or } 1$ for each term,

$$
\frac{1}{ \sqrt{2^{n+1}} } \sum^{2^n}_{x\in \{ 0, 1 \}^n} \lvert x\rangle \otimes \lvert 0\rangle \xrightarrow{U_f} \frac{1}{ \sqrt{2^{n+1}} } \sum_{f(x)=0} \lvert x\rangle \otimes \lvert 0\rangle + \frac{1}{ \sqrt{2^{n+1}} } \sum_{f(x)=1} \lvert x\rangle \otimes \lvert 1\rangle
$$

$$
\frac{1}{ \sqrt{2^{n+1}} } \sum^{2^n}_{x\in \{ 0, 1 \}^n} \lvert x\rangle \otimes \lvert 1\rangle \xrightarrow{U_f} \frac{1}{ \sqrt{2^{n+1}} } \sum_{f(x)=0} \lvert x\rangle \otimes \lvert 1\rangle + \frac{1}{ \sqrt{2^{n+1}} } \sum_{f(x)=1} \lvert x\rangle \otimes \lvert 0\rangle
$$

We can see that $n$ th qubit is flipped when $f(x)=1$.

Therefore,

$$
\lvert \psi_2\rangle = \frac{1}{\sqrt{2^n}} \biggl( \sum_{f(x)=0}\lvert x\rangle - \sum_{f(x)=1}\lvert x\rangle \biggr) \otimes \frac{1}{\sqrt{2}} (\lvert 0\rangle - \lvert 1\rangle)
$$

In the case "1.$f(x)$ is same for all inputs", each of terms for $f(x)=0$ or $f(x)=1$ vanish.
So,

$$
\begin{align}
\lvert \psi_2 \rangle &= \pm \frac{1}{ \sqrt{2^{n}} } \sum^{2^n}_{x\in \{ 0, 1 \}^n} \lvert x\rangle \otimes \frac{1}{\sqrt{2}} (\lvert 0\rangle - \lvert 1\rangle) \\
&= \pm \biggl( \otimes^{n} H \lvert 0\rangle \biggr) \otimes H \lvert 1\rangle
\end{align}
$$

As a consequence, $\lvert \psi_3 \rangle$ is

$$
\lvert \psi_3 \rangle = \pm \lvert 00...00 \rangle \otimes H\lvert 1 \rangle
$$

In this case, the measurmet results from the $0$ th to $n$ th qubits will all be "0".

In the case "2.$f(x)$ is different in half of the inputs", because the signs are different for $f(x)$,

$$
\lvert \psi_2 \rangle = \frac{1}{ \sqrt{2^{n}} } \sum^{2^n}_{x\in \{ 0, 1 \}^n} (-1)^{f(x)} \lvert x\rangle \otimes \frac{1}{\sqrt{2}} (\lvert 0\rangle - \lvert 1\rangle)
$$

$$
\begin{align}
\lvert \psi_3 \rangle &= \frac{1}{ \sqrt{2^{n}} } \sum^{2^n}_{x\in \{ 0, 1 \}^n} (-1)^{f(x)} (\otimes^nH) \lvert x\rangle \otimes \frac{1}{\sqrt{2}} (\lvert 0\rangle - \lvert 1\rangle) \\
&= \frac{1}{ 2^{n} } \sum^{2^n}_{x\in \{ 0, 1 \}^n} (-1)^{f(x)} \bigl( \sum^{2^n-1}_{y=0}(-1)^{x\cdot y}\lvert y \rangle \bigr) \otimes \frac{1}{\sqrt{2}} (\lvert 0\rangle - \lvert 1\rangle)
\end{align}
$$

Now, consider the probability $\rm{Pr}(0)$ that all the measurements results from the $0$ th to the $n$ th qubits are "0".
It is squared absolute value of the amplitude of state $\lvert y \rangle\ (y=0)$.

Because $x\cdot y=0$ when $y=0$,

$$
\mathrm{Pr}(0) = \biggl| \frac{1}{2^n} \sum^{2^n}_{x\in \{ 0, 1 \}^n} (-1)^{f(x)} \biggr|^2
$$

If $f(x)$ is different in half of the inputs, all the terms cancel each other out, so $\mathrm{Pr}(0) =0$.
Therefore, the result of measuring up to $n$ th qubits from the $0$ th will never be all "0".

From the above, we can determine the oracle by whether the results of measuring the qubits up to the ($n-1$) th bit are all $0$ or not.

Let's implement this in blueqat.

```python
from blueqat import Circuit
import numpy as np
```

Let's implement oracle for case 1\. and 2\., respectively.

In the case 1\., we invert the $n$ th qubit regardless of the qubits up to the ($n-1$) th.

In the case 2\., we prepare $n$ $CX$ gates such that each qubit up to the ($n-1$) th qubit is a control bit and all target bits are the $n$ th qubit.
In this case, the $n$ th qubit is inverted for half of the possible values of the qubits up to the ($n-1$) th qubit, while remains unchanged for the other half.

```python
def oracle_1(c):
    n = c.n_qubits
    c.x[n-1]

def oracle_2(c):
    n = c.n_qubits
    for i in range(n-1):
        c.cx[i, n-1]
```

The following is the main body of the algorithm.
まず、オラクルが 1\. と2\. のどちらであるかを乱数で決めます。これが求めたい正解となります。
First, set the oracles 1\. or 2\. by random number. This will be the correct answer we want to find.

Next, use the Deutsch-Jozsa algorithm to determine the oracle.

Finally, check if the result of the discrimination and the oracle selected by a random number in advance match.

```python
n = 4

c = Circuit(n + 1)
c.x[n].h[:]

if np.random.rand() > 0.5:
    oracle_1(c)
    oracle = "f(x) = 1 for all x."
else:
    oracle_2(c)
    oracle = "f(x) = 0 for half x and f(x) = 1 for others."

c.h[:]
res = c.m[:].run(shots = 1000)

print("Oracle:", oracle)
print("Results of quantum circuit:", res)

if [arr[:n] for arr in res.keys()] == ['0'*n] and oracle == "f(x) = 1 for all x.":
    print("OK")
elif [arr[:n] for arr in res.keys()] != ['0'*n] and oracle == "f(x) = 0 for half x and f(x) = 1 for others.":
    print("OK")
else:
    print("incorrect")
```

    Oracle: f(x) = 1 for all x.
    Results of quantum circuit: Counter({'00001': 1000})
    OK

Now we determined the oracle using the Deutsch-Jozsa algorithm.

```python

```


```python
from sympy import *
from sympy.stats import *
from matplotlib import pyplot
```

# Distribuição Binomial

Para construir o modelo binomial vamos introduzir uma sequência de ensaios de Bernoulli. Tal sequência é definida por meio das seguintes condições:

* Em cada ensaio considera-se somente a ocorrência ou não-ocorrência de um certo evento que será denominado sucesso (S) e cuja não-ocorrência será denominada falha (F).
* Os ensaios são independentes.
* A probabilidade de sucesso, que denotaremos por p é a mesma para cada ensaio. A probabilidade de falha será denotada por 1-p.

Para um experimento que consiste na realização de n ensaios independentes de Bernoulli, o espaço amostral pode ser considerado como o conjunto de n-uplas, em que cada posição há um sucesso (S) ou uma falha (F).

A probabilidade de um ponto amostral com sucessos nos k primeiros ensaios e falhas nos n−k ensaios seguintes é pk(1−p)n−k.

Note que esta é a probabilidade de qualquer ponto com k sucessos e n−k falhas. O número de pontos do espaço amostral que satisfaz essa condição é igual ao número de maneiras com que podemos escolher k ensaios para a ocorrência de sucesso dentre o total de n ensaios, pois nos n−k restantes deverão ocorrer falhas. Este número é igual ao número de combinações de n elementos tomados k a k, ou seja,

```python
p, n, k, nk = symbols("p n k, (nk)")
f_nk = factorial(n)/(factorial(k)*factorial(n-k))
Lambda(nk, f_nk)
```

$\displaystyle \left( (nk) \mapsto \frac{n!}{k! \left(- k + n\right)!} \right)$

Ou seja, para k=0,1,…,n:

```python
f_X = (nk)*p**k*(1-p)**(n-k)
X = f_X.subs({nk: f_nk})
pdf = DiscreteRV(k, X)
Lambda(symbols("P[X=k]"), f_X)
```

$\displaystyle \left( P[X=k] \mapsto (nk) p^{k} \left(1 - p\right)^{- k + n} \right)$

## Exemplo 1
Suponha que numa linha de produção a probabilidade de se obter uma peça defeituosa (sucesso) é p=0,1. Toma-se uma amostra de 10 peças para serem inspecionadas. Qual a probabilidade de se obter:

```python
exemplo1 = pdf.subs({p:0.1,n:10})
d = density(exemplo1)
pyplot.plot([d(i) for i in range(11)])
```

1. Uma peça defeituosa?

```python
P(Eq(exemplo1,1))
```

$\displaystyle 0.387420489$

2. Nenhuma peça defeituosa?

```python
P(Eq(exemplo1, 0))
```

$\displaystyle 0.3486784401$

3. Duas peças defeituosas?

```python
P(Eq(exemplo1, 2))
```

$\displaystyle 0.1937102445$

4. No mínimo duas peças defeituosas?

```python
N(P(nsimplify(Ge(exemplo1, 2))))
```

$\displaystyle 0.2639010709$

5. No máximo duas peças defeituosas?

```python
N(P(nsimplify(Le(exemplo1, 2)))) # necessario fazer esta transformacao devido ao bug no sympy
```

$\displaystyle 0.9298091736$

## Exemplo 2
Suponha que um aluno pretende fazer um teste de múltipla escolha com 10 questões e cinco alternativas por questão respondendo cada uma das questões de forma aleatória. Qual é probabilidade dele acertar no máximo 3 questões?

```python
exemplo2 = pdf.subs({p:0.2,n:10})
d = density(exemplo2)
pyplot.plot([d(i) for i in range(11)])
```

```python
N(P(nsimplify(Le(exemplo2, 3))))
```

$\displaystyle 0.8791261184$

## Exemplo 3

Uma moeda não viciada é lançada várias vezes. Qual a probabilidade de que obtermos 5 caras antes de obtermos 3 coroas?

```python
exemplo3 = pdf.subs({p:0.5,n:7})
d = density(exemplo3)
pyplot.plot([d(i) for i in range(8)])
```

```python
P(Ge(exemplo3, 5))
```

$\displaystyle 0.2265625$

## Exemplo 4

(Problema da caixa de fosforo de Banach) Suponha que um homem ande sempre com duas caixas de fósforos com n palitos cada uma. Suponha também que cada vez que ele necessite usar um fósforo ele pegue de forma aleatória em qualquer uma das caixas. Como ele é uma pessoa distraída quando ele pega o último palito da caixa de fósforos ele não se lembra de joga-la fora. Qual a probabilidade de que quando ele perceba que uma das caixas está vazia a outra contenha exatamente k fósforos?

```python
X.subs({p:0.5, n:2*n-k,k:n})
```

$\displaystyle 0.5^{- k + 2 n}$


# COURSE: Master math by coding in Python
## SECTION: Trigonometry

#### https://www.udemy.com/course/math-with-python/?couponCode=MXC-DISC4ALL
#### INSTRUCTOR: sincxpress.com

Note about this code: Each video in this section of the course corresponds to a section of code below. Please note that this code roughly matches the code shown in the live recording, but is not exactly the same -- the variable names, order of lines, and parameters may be slightly different.

```python

```

```python
import sympy as sym
import numpy as np
import matplotlib.pyplot as plt
from IPython.display import display,Math
```

# VIDEO: Introduction to random numbers

```python
# use the numpy module
np.random.rand(1)
```

```python
# make a plot of these numbers
nums = np.random.rand(1000)

plt.plot(nums,'s')
plt.title('Uniformly distributed random numbers')
plt.xlabel('Index')
plt.ylabel('Value')
plt.show()

plt.hist(nums)
plt.title('Histogram of said numbers')
plt.xlabel('Value')
plt.ylabel('Bin count')
plt.show()
```

```python
# generating random numbers in a specific range

# parameters
minval = 2
maxval = 17

# random numbers in range [0 1]
randangs = np.random.rand(1000)

# modulate those numbers to parameters
randangs = randangs*(maxval-minval) + minval

plt.plot(randangs,'s')
plt.title('Uniformly distributed random numbers')
plt.xlabel('Index')
plt.ylabel('Value')
plt.show()

plt.hist(randangs)
plt.title('Histogram of said numbers')
plt.xlabel('Value')
plt.ylabel('Bin count')
plt.show()
```

```python
# normally distributed random numbers
nums = np.random.randn(1000)

plt.subplot(211)
plt.plot(nums,'s',markersize=5,alpha=.3)
plt.title('Normally distributed random numbers')
plt.xlabel('Index')
plt.ylabel('Value')

plt.subplot(212)
plt.hist(nums)
plt.title('Histogram of said numbers')
plt.xlabel('Value')
plt.ylabel('Bin count')
plt.show()
```

### Exercise

```python
# generate normally distributed random numbers with a mean of 15 and a standard deviation of 4.3
desired_mean = 15
desired_std = 4.3

nums = desired_mean + desired_std*np.random.randn(1000)

plt.subplot(121)
plt.plot(nums,'s')
plt.xlabel('Index')
plt.ylabel('Value')

plt.subplot(122)
plt.hist(nums)
plt.xlabel('Value')
plt.ylabel('Bin count')
plt.show()

print('This distribution has a mean of %g and a standard deviation of %g' %(np.mean(nums),np.std(nums)))

```

```python
# to be more exact:

# generate normal random numbers
nums = np.random.randn(1000)

# enforce mean of 0
nums = nums - np.mean(nums)

# enforce std of 1
nums = nums/np.std(nums)

nums = nums*desired_std + desired_mean

plt.subplot(121)
plt.plot(nums,'s')
plt.xlabel('Index')
plt.ylabel('Value')

plt.subplot(122)
plt.hist(nums)
plt.xlabel('Value')
plt.ylabel('Bin count')
plt.show()

print('This distribution has a mean of %g and a standard deviation of %g' %(np.mean(nums),np.std(nums)))

```

# VIDEO: Exercise: Plotting random phase angles

```python

angs = np.random.randn(100) # not this one!
angs = np.random.rand(100)*2*np.pi

for i in angs:
    plt.plot([0,np.cos(i)],[0,np.sin(i)])

plt.axis('square')
plt.axis([-1,1,-1,1])
plt.axis('off')
plt.show()
```

# VIDEO: Converting between radians and degrees

```python
# the "manual" method, part 1: implement the formula!

# specify in degrees
deg = 180

# print the conversion (note that the algorithm is implemented in-line!)
print( "%g degrees is %g radians." %(deg,deg*np.pi/180) )
```

```python
# the "manual" method, part 2: implement the formula!

# specify in radians
rad = np.pi

# print the conversion (note that the algorithm is implemented in-line!)
print( "%g radians is %g degrees." %(rad,180*rad/np.pi) )
```

```python
# but the above implementations are not completely satisfactory, because angles are circular (e.g., 2pi = 0)

rad = np.pi*3

deg = (180*rad/np.pi)%360

print( "%g radians is %g degrees." %(rad,deg) )
```

```python
# or use a Python function

deg = 180*4
np.deg2rad(deg)
```

```python
# some silliness with embedding functions inside functions

np.rad2deg( np.deg2rad(180) )
```

### Exercise

```python
# make a function that asks for a number and unit, then converts and makes a plot
def angle_convert_plot():

    # input from user
    angle = eval(input('What angle to convert? ') )
    unit  = input('Which unit did you input (radian or degree)? ')

    # do the conversion
    if unit[0].lower()=='r':
        rad = angle
        deg = np.rad2deg(angle)
    elif unit[0].lower()=='d':
        deg = angle
        rad = np.deg2rad(angle)
    else:
        raise ValueError('Unknown unit!')

    # convert to <360
    deg = deg%360
    rad = rad%(2*np.pi)

    # make the graph
    plt.plot([0,np.cos(rad)],[0,np.sin(rad)],'r',linewidth=3)
    plt.plot([0,1],[0,0],'r',linewidth=3)
    plt.axis('square')
    plt.axis([-1,1,-1,1])
    plt.grid()
    plt.title('Angle of %g$^o$, or %g rad.' %(deg,rad))

    plt.show()

```

```python
angle_convert_plot()
```

# VIDEO: The Pythagorean theorem

```python
a = 8
b = 4

# compute c
c = np.sqrt(a**2 + b**2)

# draw the triangle
plt.plot([0,a],[0,0],'k',linewidth=2)
plt.plot([0,0],[0,b],'k')
plt.plot([0,a],[b,0],'k')

# plot right-angle
plt.plot([.3,.3],[0,.3],'k',linewidth=1)
plt.plot([0,.3],[.3,.3],'k',linewidth=1)

plt.text(a/3,.25,'a='+str(a),fontsize=15)
plt.text(.1,b/3,'b='+str(b),fontsize=15)
plt.text(a/2+.1,b/2,'c='+str(np.round(c,3)),fontsize=15)

axlim = np.max((a,b))+.5
plt.axis('square')
plt.axis([-.5,axlim,-.5,axlim])
plt.axis('off')
plt.show()
```

### Exercise

```python
# compute magnitude and angle of complex number

z = np.complex(3,4)

mag1 = np.sqrt( np.real(z)**2 + np.imag(z)**2 )
mag2 = np.abs(z)
print(mag1,mag2)

```

```python
ang1 = np.arctan2(np.imag(z),np.real(z))
ang2 = np.angle(z)

print(ang1,ang2)
```

# VIDEO: Graphing resolution for sine, cosine, and tangent

```python
# specify the radians ("time")

x = np.linspace(0,6*np.pi,400)

plt.plot(x,np.sin(x),'r',label='$\sin(\\theta)$')
plt.plot(x,np.cos(x),'b',label='$\cos(\\theta)$')
plt.legend()
plt.xlabel('Radians ($\pi$)')
plt.ylabel('Function value')
plt.show()
```

```python
# plot tangent

x = np.linspace(0,4*np.pi,100)

plt.plot(x,np.tan(x),'k',label='$\\tan(\\theta)$')
plt.legend()
plt.xlabel('Rad.')
plt.xticks(np.arange(x[0],x[-1],np.pi/2))
plt.ylabel('Function value')
plt.show()
```

```python
# illustrate that tan = sin/cos

ang = np.random.rand(1)[0]*2*np.pi

tan = np.tan(ang)
sc  = np.sin(ang) / np.cos(ang)

print( sc - tan )
```

```python
# famous equality in trigonometry

ang = np.linspace(0,2*np.pi,10)

np.cos(ang)**2 + np.sin(ang)**2
```

### Exercise

```python

x = np.linspace(-2*np.pi,2*np.pi,200)

y1 = np.sin(x + np.cos(x))
y2 = np.cos(x + np.sin(x))

plt.plot(x,y1,x,y2)
plt.legend(['$\sin(x+\cos(x))$','cos(x+sin(x))'])
plt.show()

```

```python

th = sym.pi*7/6
theta = float(th)

x = np.linspace(0,theta%(2*np.pi),100)

# plot unit circle
plt.plot(np.cos(np.linspace(0,2*np.pi)),np.sin(np.linspace(0,2*np.pi)),color='gray')
plt.plot([-1.1,1.1],[0,0],'--',color=[.8,.8,.8])
plt.plot([0,0],[-1.1,1.1],'--',color=[.8,.8,.8])

# plot arc
plt.plot(np.cos(x),np.sin(x),'k',linewidth=3)

# plot vector to point
plt.plot([0,np.cos(theta)],[0,np.sin(theta)],'k:',linewidth=1)
plt.plot(np.cos(theta),np.sin(theta),'ko')

# plot cosine part
plt.plot([0,0],[0,np.sin(theta)],'r',linewidth=2,label='sin part')
plt.plot([np.cos(theta),0],[np.sin(theta),np.sin(theta)],'r:',linewidth=1)
plt.plot([np.cos(theta),0],[0,0],'g',linewidth=2,label='cos part')
plt.plot([np.cos(theta),np.cos(theta)],[0,np.sin(theta)],'g:',linewidth=1)

plt.title('$\\theta=%s$' %sym.latex(th))
plt.axis([-1.1,1.1,-1.1,1.1])
plt.gca().set_aspect('equal')
plt.legend()
plt.show()
```

# VIDEO: Euler's formula

```python
# Euler's formula: me^ik = cos(k)+isin(k)

# create euler's number
k = np.pi/4
m = 2.3

cis = m*(np.cos(k) + 1j*np.sin(k))
eul = m*np.exp(1j*k)

print(cis)
print(eul)
```

```python
# now extract magnitude and phase
mag = np.abs(eul)
ang = np.angle(eul)

# then plot
plt.polar([0,ang],[0,mag],'b',linewidth=3)
# plt.plot(k,m,'ro')

plt.xlabel('Cos axis'), plt.ylabel('Sin axis')
plt.title('me$^{i\phi}$, m=%g, $\phi=$%g' %(mag,ang))
plt.show()
```

### Exercise

```python
# write a function that will input a cosine/sine pair and output euler's formula with aa graph

def eulerFromCosSin():

    re = eval( input('cosine part: ') )
    im = eval( input('sine part: ') )

    m = np.round( np.sqrt(re**2 + im**2) ,2)
    k = np.round( np.arctan2(im,re) ,2)

    plt.polar([0,k],[0,m],'b',linewidth=3)
    plt.title('me$^{i\phi}$, m=%g, $\phi=$%g' %(m,np.round(k,2)))
    plt.thetagrids([0,45,130,215,250])
    plt.show()

```

```python
eulerFromCosSin()
```

# VIDEO: Exercise: Random exploding Euler

```python
nvects = 200
ms = np.random.rand(nvects)
ps = np.random.rand(nvects)*2*np.pi

for i in range(0,nvects):

    randnum = np.random.rand()

    if randnum<.4:
        color = [1,.2,.7]
    elif randnum>.4 and randnum<.8:
        color = [.7,.2,1]
    else:
        color = [0,1,0]

    plt.polar([0,ps[i]],[0,ms[i]],color=color)

plt.axis('off')
plt.show()

```

# VIDEO: Exercise: make random snakes with trigonometry!

```python
angs = np.linspace(0,8*np.pi,1000)

cs = np.random.rand()
ss = np.random.rand()

x = np.cos(cs*angs)
y = np.sin(ss*angs)

# x = np.append(x,x[0])
# y = np.append(y,y[0])

plt.plot(x,y,'k')
plt.axis('square')
plt.axis([-1.1,1.1,-1.1,1.1])
plt.title('r$_1$ = %s, r$_2$ = %s' %(np.round(cs,2),np.round(ss,2)))
plt.axis('off')
plt.show()
```

# VIDEO: Trigonometry BUG HUNT!

```python
# plot a series of random numbers
s = np.random.randn(100,1)
plt.plot(s)
plt.show()
```

```python
# create and image a matrix of random integers between (and including) 3 and 20

mat = np.random.randint(3,21,(30,20))

plt.imshow(mat)
plt.colorbar()
plt.show()
print(np.max(mat))
```

```python
# create 100 random phase angles [0,2pi] and show unit vectors with those angles

n = 100

randphases = np.random.rand(n)*2*np.pi

for i in range(0,n):
    plt.polar([0,randphases[i]],[0,1],'o-',color=np.random.rand(3),markersize=20,alpha=.3)

plt.show()

```

```python
# create an outwards spiral using phase angles and amplitudes

n = 100
a = np.linspace(0,1,n)
p = np.linspace(0,4*np.pi,n)

plt.polar(p,a,'m',linewidth=3)
plt.axis('off')
plt.show()
```

```python
# convert radians to degrees

n = 10
rad = np.logspace(np.log10(.001),np.log10(np.pi*2),n)

print(np.rad2deg(rad))
```

```python
# famous equality in trigonometry

ang = np.logspace(np.log10(.001),np.log10(2*np.pi),10)
np.cos(ang)**2 + np.sin(ang)**2
```

```python
# create euler's number
p = np.pi/4
m = .5

eulr = m*np.exp(1j*p)

# now extract magnitude and phase
mag = np.abs(eulr)
ang = np.angle(eulr)

# then plot
plt.polar([0,ang],[0,mag],'b',linewidth=3)
plt.show()
```


Markdown macros

$\newcommand{\I}{\mathrm{i}}
\newcommand{\E}{\mathrm{e}}
\newcommand{\D}{\mathop{}\!\mathrm{d}}
\newcommand{\bra}[1]{\langle{#1}|}
\newcommand{\ket}[1]{|{#1}\rangle}
\newcommand{\braket}[1]{\langle{#1}\rangle}
\newcommand{\bm}[1]{\boldsymbol{#1}}$

# Imports

```python
# numpy
import numpy as np
```

```python
# matplotlib
import matplotlib
import matplotlib.pylab as plt
```

```python
%matplotlib inline
from IPython.display import Image
```

```python
# sympy
import sympy as sy
sy.init_printing(use_unicode = True)
```

# Le pendule solide

```python
Image(filename='pendule_g.png', width=300)
```

On étudie le movement pendulare d'une barre de longueur $l$ et de masse $m$ suspendue à une de ses extrémités. On veut déterminer les forces de réaction au point de suspension (axe de rotation).

## Cinématique

Notez que le mouvement se fait dans le plan $XY$ (du repère fixe), il est donc naturel de choisir le repère fixe pour décrire ce système.

La position du centre de masse est
$$\bm R = \frac{l}{2}\big(\sin\theta \hat{\bm X} - \cos\theta \hat{\bm Y} \big)$$
et le moment d'inertie (par rapport au point $O$ de rotation) est
$$I_y = I_z = I_l = \frac{ml^2}{3}$$
La vitesse et l'accelération angulaires sont:
$$\bm \omega = \dot\theta \hat{\bm Z}, \quad \dot{\bm \omega} = \ddot\theta \hat{\bm Z}$$
La vitesse du centre de masse est
$$\bm V = \frac{\D \bm R}{\D t} =  \frac{l\dot\theta}{2}\big(\cos\theta \hat{\bm X} + \sin\theta \hat{\bm Y} \big)$$
et son accelération:
$$\dot{\bm V} = \frac{l}{2}\big[(\ddot{\theta}\cos\theta - \dot\theta^2 \sin\theta) \hat{\bm X} + (\ddot\theta \sin\theta + \dot\theta^2\cos\theta) \hat{\bm Y}\big]$$

## Moment de forces

La force total agissant sur la barre est
$$\bm F = f_x \hat{\bm X} + (f_y - mg)\hat{\bm Y}$$
Le moment correspondant est
$$M_O = \bm R \times \bm F = -\frac{mgl}{2}\sin\theta \hat{\bm Z}$$

La variation du moment cinétique est due au mouvement du centre de masse:
$$\dot{\bm L} = \frac{ml^2}{3}\ddot \theta \hat{\bm Z}$$
est aussi dirrigée selon l'axe fixe $Z$.

## Newton et Euler

L'équation de Newton $\bm F = m \dot{\bm V}$ devient
\begin{align}
f_x &= \frac{ml}{2}\big(\ddot\theta \cos\theta - \dot\theta^2 \sin\theta \big)\\
f_y &= mg + \frac{ml}{2} \big(\ddot\theta \sin\theta + \dot\theta^2 \cos\theta \big)
\end{align}

En plus, on a l'équation d'Euler (variation du moment cinétique):
$$\frac{ml^2}{3}\ddot\theta = -\frac{mgl}{2}\sin\theta \hat{\bm Z}$$
ce qui donne
$$\ddot\theta + \frac{3g}{2l}\sin\theta$$
qui est l'équation de la trajectoire du pendule.

## Application numérique

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

```python
def f(t,y):
    theta, omega = y[0], y[1]
    return np.array([omega, -(3/2)*np.sin(theta)])
```

```python
NT = 100
t_i = 0
t_f = 4*np.pi
t = np.linspace(t_i,t_f,NT)
theta_0 = np.pi/3
omega_0 = 0
y0 = np.array([theta_0,omega_0])
sol = solve_ivp(f, t_span=(t_i,t_f), y0=y0, t_eval=t)
```

```python
theta = sol.y[0]
omega = sol.y[1]
print(np.shape(sol.t))
```

    (100,)

```python
fig, ax = plt.subplots(figsize=(4,3))
ax.plot(t, theta, color = '0.35')
ax.set_xlabel(r"$t$")
ax.set_ylabel(r"$\theta$");
```

```python
domega = -(3/2)*np.sin(theta)
f_x = 0.5*(domega*np.cos(theta) - omega**2*np.sin(theta))
f_y = 0.5*(domega*np.sin(theta) + omega**2*np.cos(theta))
```

```python
fig, ax = plt.subplots(figsize=(4,3))
ax.plot(t, f_x, color = '0.35')
ax.set_xlabel(r"$t$")
ax.set_ylabel(r"$f_x$");
```

```python
fig, ax = plt.subplots(figsize=(4,3))
ax.plot(t, f_y, color = '0.35')
ax.set_xlabel(r"$t$")
ax.set_ylabel(r"$f_y$");
```

```python
# Espace de phase
fig, ax = plt.subplots(figsize=(4,4))
ax.plot(theta, omega, color = '0.35')
ax.set_xlabel(r"$\theta$")
ax.set_ylabel(r"$\omega$");
```

```python

```