```python
# Erasmus+ ICCT project (2018-1-SI01-KA203-047081)

# Toggle cell visibility

from IPython.display import HTML
tag = HTML('''
Toggle cell visibility <a href="javascript:code_toggle()">here</a>.''')
display(tag)

# Hide the code completely

# from IPython.display import HTML
# tag = HTML('''<style>
# div.input {
#     display:none;
# }
# </style>''')
# display(tag)

```

Toggle cell visibility here.

## Internal stability

The concept of stability captures the behaviour of a system state evolution when this is perturbed from an equilibrium condition: stability describes if the state evolution arising after a perturbation from an equilibrium point diverges or not.

### Definition
Given a time invariant dynamic system described by the state vector $x(t)\in \mathbb{R}^n$, a point of equilibrium $x_e$, an initial state $x_0$ and an initial time $t_0$ if
$$
\forall \, \epsilon \in \mathbb{R}, \, \epsilon > 0 \quad \exists \delta \in \mathbb{R}, \, \delta > 0 : \quad ||x_0-x_e|| < \delta \, \Rightarrow  \, ||x(t)-x_e|| < \epsilon \quad \forall t \ge t_0
$$
that could be read as: if a small enough initial perturbation $\delta$ from the equilibrium point exists so that the state evolution $x(t)$ from the perturbed point does not get too far (more than $\epsilon$) from the equilibrium itself,
then the equilibrium point is stable.

If it also happens that $\lim_{t\to\infty}||x(t)-x_e|| = 0$, that can be read as: the state evolution returns back to the equilibrium point, then the equilibrium is said to be asymptotically stable.

In the case of linear time invariant systems of the type:
\begin{cases}
\dot{x} = Ax +Bu \\
y = Cx + Du,
\end{cases}

it is possible to prove that stability of one equilibrium point implies stability of all equilibrium points, thus we can talk about stability of the system even if, in general, the stability property is related to an equilibrium point. This linear system's peculiar feature is due to the fact that the evolution of this type of systems is strictly related to the eigenvalues of the dynamics matrix $A$, which are rotation-, translation-, initial-condition- and time-invariant.

Recall what is explained in example [Modal Analysis](SS-02-Modal_Analysis.ipynb):

> The closed form solution of the differential equation, from initial time $t_0$, with initial condition $x(t_0)$ is:
$$
x(t) = e^{A(t-t_0)}x(t_0).
$$ The matrix $e^{A(t-t_0)}x(t_0)$ is composed of linear combinations of functions of time $t$, each one of the type: $$e^{\lambda t},$$ where the $\lambda$(s) are the eigenvalues of the matrix $A$; these functions are the modes of the system.

thus:
- a linear dynamic system is stable if and only if all its modes are not divergent,
- a linear dynamic system is asymptotically stable if and only if all its modes are convergent,
- a linear dynamic system is unstable if it has at least one divergent mode.

and, with respect to dynamic matrix eigenvalues, these happen if:
- all the eigenvalues of $A$ belong to the closed left half complex plane (that is they have negative or zero real part) and, in case they have zero real part, their algebraic multiplicity is the same of the geometric multiplicity, or, equivalently, they have scalar blocks in the Jordan form;
- all the eigenvalues belong to the open left half imaginary plane, that is they have strictly negative real part;
- at least one eigenvalue has positive real part, or eigenvalues with zero real part and non-scalar Jordan blocks exist.

This interactive example presents an editable dynamic matrix $A$ and shows the system free response with the corresponding eigenvalues.

### How to use this notebook?
- Try to change the eigenvalues and the initial condition $x_0$ and see how the response changes.

```python
%matplotlib inline
#%matplotlib notebook
import control as control
import numpy
import sympy as sym
from IPython.display import display, Markdown
import ipywidgets as widgets
import matplotlib.pyplot as plt

#print a matrix latex-like
def bmatrix(a):
     """Returns a LaTeX bmatrix - by Damir Arbula (ICCT project)

     :a: numpy array
     :returns: LaTeX bmatrix as a string
     """
     if len(a.shape) > 2:
         raise ValueError('bmatrix can at most display two dimensions')
     lines = str(a).replace('[', '').replace(']', '').splitlines()
     rv = [r'\begin{bmatrix}']
     rv += ['  ' + ' & '.join(l.split()) + r'\\' for l in lines]
     rv +=  [r'\end{bmatrix}']
     return '\n'.join(rv)

# Display formatted matrix:
def vmatrix(a):
    if len(a.shape) > 2:
         raise ValueError('bmatrix can at most display two dimensions')
    lines = str(a).replace('[', '').replace(']', '').splitlines()
    rv = [r'\begin{vmatrix}']
    rv += ['  ' + ' & '.join(l.split()) + r'\\' for l in lines]
    rv +=  [r'\end{vmatrix}']
    return '\n'.join(rv)

#matrixWidget is a matrix looking widget built with a VBox of HBox(es) that returns a numPy array as value !
class matrixWidget(widgets.VBox):
    def updateM(self,change):
        for irow in range(0,self.n):
            for icol in range(0,self.m):
                self.M_[irow,icol] = self.children[irow].children[icol].value
                #print(self.M_[irow,icol])
        self.value = self.M_

    def dummychangecallback(self,change):
        pass

    def __init__(self,n,m):
        self.n = n
        self.m = m
        self.M_ = numpy.matrix(numpy.zeros((self.n,self.m)))
        self.value = self.M_
        widgets.VBox.__init__(self,
                             children = [
                                 widgets.HBox(children =
                                              [widgets.FloatText(value=0.0, layout=widgets.Layout(width='90px')) for i in range(m)]
                                             )
                                 for j in range(n)
                             ])

        #fill in widgets and tell interact to call updateM each time a children changes value
        for irow in range(0,self.n):
            for icol in range(0,self.m):
                self.children[irow].children[icol].value = self.M_[irow,icol]
                self.children[irow].children[icol].observe(self.updateM, names='value')
        #value = Unicode('example@example.com', help="The email value.").tag(sync=True)
        self.observe(self.updateM, names='value', type= 'All')

    def setM(self, newM):
        #disable callbacks, change values, and reenable
        self.unobserve(self.updateM, names='value', type= 'All')
        for irow in range(0,self.n):
            for icol in range(0,self.m):
                self.children[irow].children[icol].unobserve(self.updateM, names='value')
        self.M_ = newM
        self.value = self.M_
        for irow in range(0,self.n):
            for icol in range(0,self.m):
                self.children[irow].children[icol].value = self.M_[irow,icol]
        for irow in range(0,self.n):
            for icol in range(0,self.m):
                self.children[irow].children[icol].observe(self.updateM, names='value')
        self.observe(self.updateM, names='value', type= 'All')

                #self.children[irow].children[icol].observe(self.updateM, names='value')

#overlaod class for state space systems that DO NOT remove "useless" states (what "professor" of automatic control would do this?)
class sss(control.StateSpace):
    def __init__(self,*args):
        #call base class init constructor
        control.StateSpace.__init__(self,*args)
    #disable function below in base class
    def _remove_useless_states(self):
        pass
```

```python
# Preparatory cell

A = numpy.matrix([[0,1],[-2/5,-1/5]])
X0 = numpy.matrix('5; 3')

Aw = matrixWidget(2,2)
Aw.setM(A)
X0w = matrixWidget(2,1)
X0w.setM(X0)
```

```python
# Misc

#create dummy widget
DW = widgets.FloatText(layout=widgets.Layout(width='0px', height='0px'))

#create button widget
START = widgets.Button(
    description='Test',
    disabled=False,
    button_style='', # 'success', 'info', 'warning', 'danger' or ''
    tooltip='Test',
    icon='check'
)

def on_start_button_clicked(b):
    #This is a workaround to have intreactive_output call the callback:
    #   force the value of the dummy widget to change
    if DW.value> 0 :
        DW.value = -1
    else:
        DW.value = 1
    pass
START.on_click(on_start_button_clicked)
```

```python
# Main cell

def main_callback(A, X0, DW):
    sols = numpy.linalg.eig(A)
    sys = sss(A,[[1],[0]],[0,1],0)
    pole = control.pole(sys)
    if numpy.real(pole[0]) != 0:
        p1r = abs(numpy.real(pole[0]))
    else:
        p1r = 1
    if numpy.real(pole[1]) != 0:
        p2r = abs(numpy.real(pole[1]))
    else:
        p2r = 1
    if numpy.imag(pole[0]) != 0:
        p1i = abs(numpy.imag(pole[0]))
    else:
        p1i = 1
    if numpy.imag(pole[1]) != 0:
        p2i = abs(numpy.imag(pole[1]))
    else:
        p2i = 1

    print('A\'s eigenvalues are:',round(sols[0][0],4),'and',round(sols[0][1],4))

    #T = numpy.linspace(0, 60, 1000)
    T, yout, xout = control.initial_response(sys,X0=X0,return_x=True)

    fig = plt.figure("A's eigenvalues", figsize=(16,16))
    ax = fig.add_subplot(311,title='Poles (Real vs Imag)')
    #plt.axis(True)
    # Move left y-axis and bottim x-axis to centre, passing through (0,0)
    # Eliminate upper and right axes
    ax.spines['left'].set_position(('data',0.0))
    ax.spines['bottom'].set_position(('data',0.0))
    ax.spines['right'].set_color('none')
    ax.spines['top'].set_color('none')
    ax.set_xlim(-max([p1r+p1r/3,p2r+p2r/3]),
                max([p1r+p1r/3,p2r+p2r/3]))
    ax.set_ylim(-max([p1i+p1i/3,p2i+p2i/3]),
                max([p1i+p1i/3,p2i+p2i/3]))

    plt.plot([numpy.real(pole[0]),numpy.real(pole[1])],[numpy.imag(pole[0]),numpy.imag(pole[1])],'o')
    plt.grid()

    ax1 = fig.add_subplot(312,title='Free response')
    plt.plot(T,xout[0])
    plt.grid()
    ax1.set_xlabel('time [s]')
    ax1.set_ylabel('$x_1$')
    ax1.axvline(x=0,color='black',linewidth='0.8')
    ax1.axhline(y=0,color='black',linewidth='0.8')
    ax2 = fig.add_subplot(313)
    plt.plot(T,xout[1])
    plt.grid()
    ax2.set_xlabel('time [s]')
    ax2.set_ylabel('$x_2$')
    ax2.axvline(x=0,color='black',linewidth='0.8')
    ax2.axhline(y=0,color='black',linewidth='0.8')

    #plt.show()

alltogether = widgets.HBox([widgets.VBox([widgets.Label('$A$:',border=3),
                                          Aw]),
                                          widgets.Label('   ',border=3),
                            widgets.VBox([widgets.Label('$X_0$:',border=3),
                                          X0w]),
                                          START])
out = widgets.interactive_output(main_callback, {'A':Aw, 'X0':X0w, 'DW':DW})
out.layout.height = '1000px'
display(out, alltogether)
```

    Output(layout=Layout(height='1000px'))

    HBox(children=(VBox(children=(Label(value='$A$:'), matrixWidget(children=(HBox(children=(FloatText(value=0.0, …


```python
%matplotlib inline
```

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

# High-School Maths Exercise
## Getting to Know Jupyter Notebook. Python Libraries and Best Practices. Basic Workflow

### Problem 1. Markdown
Jupyter Notebook is a very light, beautiful and convenient way to organize your research and display your results. Let's play with it for a while.

First, you can double-click each cell and edit its content. If you want to run a cell (that is, execute the code inside it), use Cell > Run Cells in the top menu or press Ctrl + Enter.

Second, each cell has a type. There are two main types: Markdown (which is for any kind of free text, explanations, formulas, results... you get the idea), and code (which is, well... for code :D).

Let me give you a...
#### Quick Introduction to Markdown
##### Text and Paragraphs
There are several things that you can do. As you already saw, you can write paragraph text just by typing it. In order to create a new paragraph, just leave a blank line. See how this works below:
```
This is some text.
This text is on a new line, but it will continue the same paragraph (so you can make your paragraphs more easily readable by just continuing on a new line, or just go on and on like this one line is ever continuing).

This text is displayed in a new paragraph.

And this is yet another paragraph.
```
**Result:**

This is some text.
This text is on a new line, but it will continue the same paragraph (so you can make your paragraphs more easily readable by just continuing on a new line, or just go on and on like this one line is ever continuing).

This text is displayed in a new paragraph.

And this is yet another paragraph.

##### Headings
There are six levels of headings. Level one is the highest (largest and most important), and level 6 is the smallest. You can create headings of several types by prefixing the header line with one to six "#" symbols (this is called a pound sign if you are ancient, or a sharp sign if you're a musician... or a hashtag if you're too young :D). Have a look:
```
# Heading 1
## Heading 2
### Heading 3
#### Heading 4
##### Heading 5
###### Heading 6
```

**Result:**

# Heading 1
## Heading 2
### Heading 3
#### Heading 4
##### Heading 5
###### Heading 6

It is recommended that you have **only one** H1 heading - this should be the header of your notebook (or scientific paper). Below that, you can add your name or just jump to the explanations directly.

##### Emphasis
You can create emphasized (stonger) text by using a **bold** or _italic_ font. You can do this in several ways (using asterisks (\*) or underscores (\_)). In order to "escape" a symbol, prefix it with a backslash (\). You can also strike thorugh your text in order to signify a correction.
```
**bold** __bold__
*italic* _italic_

This is \*\*not \*\* bold.

I ~~didn't make~~ a mistake.
```

**Result:**

**bold** __bold__
*italic* _italic_

This is \*\*not\*\* bold.

I ~~didn't make~~ a mistake.

##### Lists
You can add two types of lists: ordered and unordered. Lists can also be nested inside one another. To do this, press Tab once (it will be converted to 4 spaces).

To create an ordered list, just type the numbers. Don't worry if your numbers are wrong - Jupyter Notebook will create them properly for you. Well, it's better to have them properly numbered anyway...
```
1. This is
2. A list
10. With many
9. Items
    1. Some of which
    2. Can
        3. Be nested
42. You can also
    * Mix
    * list
    * types
```

**Result:**
1. This is
2. A list
10. With many
9. Items
    1. Some of which
    2. Can
        3. Be nested
42. You can also
    * Mix
    * list
    * types

To create an unordered list, type an asterisk, plus or minus at the beginning:
```
* This is
* An
    + Unordered
    - list
```

**Result:**
* This is
* An
    + Unordered
        - list

##### Links
There are many ways to create links but we mostly use one of them: we present links with some explanatory text. See how it works:
```
This is [a link](http://google.com) to Google.
```

**Result:**

This is [a link](http://google.com) to Google.

##### Images
They are very similar to links. Just prefix the image with an exclamation mark. The alt(ernative) text will be displayed if the image is not available. Have a look (hover over the image to see the title text):
```
 Do you know that "taco cat" is a palindrome? Thanks to The Oatmeal :)
```

**Result:**

 Do you know that "taco cat" is a palindrome? Thanks to The Oatmeal :)

If you want to resize images or do some more advanced stuff, just use HTML.

Did I mention these cells support HTML, CSS and JavaScript? Now I did.

##### Tables
These are a pain because they need to be formatted (somewhat) properly. Here's a good [table generator](http://www.tablesgenerator.com/markdown_tables). Just select File > Paste table data... and provide a tab-separated list of values. It will generate a good-looking ASCII-art table for you.
```
| Cell1 | Cell2 | Cell3 |
|-------|-------|-------|
| 1.1   | 1.2   | 1.3   |
| 2.1   | 2.2   | 2.3   |
| 3.1   | 3.2   | 3.3   |
```

**Result:**

| Cell1 | Cell2 | Cell3 |
|-------|-------|-------|
| 1.1   | 1.2   | 1.3   |
| 2.1   | 2.2   | 2.3   |
| 3.1   | 3.2   | 3.3   |

##### Code
Just use triple backtick symbols. If you provide a language, it will be syntax-highlighted. You can also use inline code with single backticks.

```python
def square(x):
    return x ** 2
```
This is `inline` code. No syntax highlighting here.

**Result:**
```python
def square(x):
    return x ** 2
```
This is `inline` code. No syntax highlighting here.

**Now it's your turn to have some Markdown fun.** In the next cell, try out some of the commands. You can just throw in some things, or do something more structured (like a small notebook).

# Simeon I of Bulgaria

## Background and early life
Simeon was born in 864 or 865, as the third son of [Knyaz Boris](https://en.wikipedia.org/wiki/Boris_I_of_Bulgaria) I of [Krum](https://en.wikipedia.org/wiki/Krum)'s dynasty.
As Boris was the ruler who Christianized Bulgaria in 865, Simeon was a Christian all his life.

```python
print(4 + 2)
```

### Problem 2. Formulas and LaTeX
Writing math formulas has always been hard. But scientists don't like difficulties and prefer standards. So, thanks to Donald Knuth (a very popular computer scientist, who also invented a lot of algorithms), we have a nice typesetting system, called LaTeX (pronounced _lah_-tek). We'll be using it mostly for math formulas, but it has a lot of other things to offer.

There are two main ways to write formulas. You could enclose them in single `$` signs like this: `$ ax + b $`, which will create an **inline formula**: $ ax + b $. You can also enclose them in double `$` signs `$$ ax + b $$` to produce $$ ax + b $$.

Most commands start with a backslash and accept parameters either in square brackets `[]` or in curly braces `{}`. For example, to make a fraction, you typically would write `$$ \frac{a}{b} $$`: $$ \frac{a}{b} $$.

[Here's a resource](http://www.stat.pitt.edu/stoffer/freetex/latex%20basics.pdf) where you can look up the basics of the math syntax. You can also search StackOverflow - there are all sorts of solutions there.

You're on your own now. Research and recreate all formulas shown in the next cell. Try to make your cell look exactly the same as mine. It's an image, so don't try to cheat by copy/pasting :D.

Note that you **do not** need to understand the formulas, what's written there or what it means. We'll have fun with these later in the course.

Equation of a line:
$$ y = ax + b $$
Roots of the quadratic equation $ ax^2 + bx + c = 0: $
$$ x_{1,2} = \frac{-b \pm \sqrt{b^2 - 4ac}}{2a} $$
Taylor series expansion:
$$ f(x)|_{x=a} = f(a) + f'(a)(x - a) + \frac{f''(a)}{2!} (x - a)^2 + ... + \frac{f^{(n)}(a)}{n!}(x - a)^n + ... $$
Binomial theorem:
$$ (x + y)^n = \begin{pmatrix}n\\0\end{pmatrix} x^n y^0 + \begin{pmatrix}n\\1\end{pmatrix} x^{n-1} y^1 + ... + \begin{pmatrix}n\\n\end{pmatrix} x^0 y^n = \sum_{k=0}{n} \begin{pmatrix}n\\k\end{pmatrix} x^{n-k} y^k $$
An integral:
$$ \int_{-\infty}^{+\infty} e^{-x^2} dx = \sqrt{\pi} $$
A short matrix:
$$\begin{pmatrix}2 & 1 & 3 \\ 2 & 6 & 8 \\ 6 & 8 & 18 \end{pmatrix}$$
A long matrix:
$$ A = \begin{pmatrix}a_{11} & a_{12} & \cdots & a_{1n} \\ a_{21} & a_{22} & \cdots & a_{2n} \\ \vdots & \vdots & \ddots & \vdots \\ a_{m1} & a_{m2} & \cdots & a_{mn} \end{pmatrix}$$

### Problem 3. Solving with Python
Let's first do some symbolic computation. We need to import `sympy` first.

**Should your imports be in a single cell at the top or should they appear as they are used?** There's not a single valid best practice. Most people seem to prefer imports at the top of the file though. **Note: If you write new code in a cell, you have to re-execute it!**

Let's use `sympy` to give us a quick symbolic solution to our equation. First import `sympy` (you can use the second cell in this notebook):
```python
import sympy
```

Next, create symbols for all variables and parameters. You may prefer to do this in one pass or separately:
```python
x = sympy.symbols('x')
a, b, c = sympy.symbols('a b c')
```

Now solve:
```python
sympy.solve(a * x**2 + b * x + c)
```

Hmmmm... we didn't expect that :(. We got an expression for $a$ because the library tried to solve for the first symbol it saw. This is an equation and we have to solve for $x$. We can provide it as a second paramter:
```python
sympy.solve(a * x**2 + b * x + c, x)
```

Finally, if we use `sympy.init_printing()`, we'll get a LaTeX-formatted result instead of a typed one. This is very useful because it produces better-looking formulas.

```python
sympy.init_printing()
x, a, b, c = sympy.symbols('x a b c')

```

```python

sympy.solve(a * x**2 + b * x + c, x)
```

How about a function that takes $a, b, c$ (assume they are real numbers, you don't need to do additional checks on them) and returns the **real** roots of the quadratic equation?

Remember that in order to calculate the roots, we first need to see whether the expression under the square root sign is non-negative.

If $b^2 - 4ac > 0$, the equation has two real roots: $x_1, x_2$

If $b^2 - 4ac = 0$, the equation has one real root: $x_1 = x_2$

If $b^2 - 4ac < 0$, the equation has zero real roots

Write a function which returns the roots. In the first case, return a list of 2 numbers: `[2, 3]`. In the second case, return a list of only one number: `[2]`. In the third case, return an empty list: `[]`.

```python
def solve_quadratic_equation(a, b, c):
    """
    Returns the real solutions of the quadratic equation ax^2 + bx + c = 0
    """
    discriminant = b**2 - 4 * a * c
    if discriminant > 0:
        x1 = - ( math.sqrt(discriminant) + b) / (2 * a)
        x2 = (math.sqrt(discriminant) - b) / (2 * a)
        return [x1, x2]
    elif discriminant == 0:
        return - b / (2 * a)
    else:
        return []
```

```python
# Testing: Execute this cell. The outputs should match the expected outputs. Feel free to write more tests
print(solve_quadratic_equation(1, -1, -2)) # [-1.0, 2.0]
print(solve_quadratic_equation(1, -8, 16)) # [4.0]
print(solve_quadratic_equation(1, 1, 1)) # []
```

    [-1.0, 2.0]
    4.0
    []

**Bonus:** Last time we saw how to solve a linear equation. Remember that linear equations are just like quadratic equations with $a = 0$. In this case, however, division by 0 will throw an error. Extend your function above to support solving linear equations (in the same way we did it last time).

### Problem 4. Equation of a Line
Let's go back to our linear equations and systems. There are many ways to define what "linear" means, but they all boil down to the same thing.

The equation $ax + b = 0$ is called *linear* because the function $f(x) = ax+b$ is a linear function. We know that there are several ways to know what one particular function means. One of them is to just write the expression for it, as we did above. Another way is to **plot** it. This is one of the most exciting parts of maths and science - when we have to fiddle around with beautiful plots (although not so beautiful in this case).

The function produces a straight line and we can see it.

How do we plot functions in general? Ww know that functions take many (possibly infinitely many) inputs. We can't draw all of them. We could, however, evaluate the function at some points and connect them with tiny straight lines. If the points are too many, we won't notice - the plot will look smooth.

Now, let's take a function, e.g. $y = 2x + 3$ and plot it. For this, we're going to use `numpy` arrays. This is a special type of array which has two characteristics:
* All elements in it must be of the same type
* All operations are **broadcast**: if `x = [1, 2, 3, 10]` and we write `2 * x`, we'll get `[2, 4, 6, 20]`. That is, all operations are performed at all indices. This is very powerful, easy to use and saves us A LOT of looping.

There's one more thing: it's blazingly fast because all computations are done in C, instead of Python.

First let's import `numpy`. Since the name is a bit long, a common convention is to give it an **alias**:
```python
import numpy as np
```

Import that at the top cell and don't forget to re-run it.

Next, let's create a range of values, e.g. $[-3, 5]$. There are two ways to do this. `np.arange(start, stop, step)` will give us evenly spaced numbers with a given step, while `np.linspace(start, stop, num)` will give us `num` samples. You see, one uses a fixed step, the other uses a number of points to return. When plotting functions, we usually use the latter. Let's generate, say, 1000 points (we know a straight line only needs two but we're generalizing the concept of plotting here :)).
```python
x = np.linspace(-3, 5, 1000)
```
Now, let's generate our function variable
```python
y = 2 * x + 3
```

We can print the values if we like but we're more interested in plotting them. To do this, first let's import a plotting library. `matplotlib` is the most commnly used one and we usually give it an alias as well.
```python
import matplotlib.pyplot as plt
```

Now, let's plot the values. To do this, we just call the `plot()` function. Notice that the top-most part of this notebook contains a "magic string": `%matplotlib inline`. This hints Jupyter to display all plots inside the notebook. However, it's a good practice to call `show()` after our plot is ready.
```python
plt.plot(x, y)
plt.show()
```

```python
# y = 2 * x + 3
x = np.linspace(-100, 100, 5000)
y = [2 * current_x + 3 for current_x in x]
```

```python
plt.plot(x, y)
plt.show()
```

It doesn't look too bad bit we can do much better. See how the axes don't look like they should? Let's move them to zeto. This can be done using the "spines" of the plot (i.e. the borders).

All `matplotlib` figures can have many plots (subfigures) inside them. That's why when performing an operation, we have to specify a target figure. There is a default one and we can get it by using `plt.gca()`. We usually call it `ax` for "axis".
Let's save it in a variable (in order to prevent multiple calculations and to make code prettier). Let's now move the bottom and left spines to the origin $(0, 0)$ and hide the top and right one.
```python
ax = plt.gca()
ax.spines["bottom"].set_position("zero")
ax.spines["left"].set_position("zero")
ax.spines["top"].set_visible(False)
ax.spines["right"].set_visible(False)
```

**Note:** All plot manipulations HAVE TO be done before calling `show()`. It's up to you whether they should be before or after the function you're plotting.

This should look better now. We can, of course, do much better (e.g. remove the double 0 at the origin and replace it with a single one), but this is left as an exercise for the reader :).

```python
ax = plt.gca()
ax.spines["bottom"].set_position("zero")
ax.spines["left"].set_position("zero")
ax.spines["top"].set_visible(False)
ax.spines["right"].set_visible(False)
plt.plot(x, y)
plt.show()
```

### * Problem 5. Linearizing Functions
Why is the line equation so useful? The main reason is because it's so easy to work with. Scientists actually try their best to linearize functions, that is, to make linear functions from non-linear ones. There are several ways of doing this. One of them involves derivatives and we'll talk about it later in the course.

A commonly used method for linearizing functions is through algebraic transformations. Try to linearize
$$ y = ae^{bx} $$

Hint: The inverse operation of $e^{x}$ is $\ln(x)$. Start by taking $\ln$ of both sides and see what you can do. Your goal is to transform the function into another, linear function. You can look up more hints on the Internet :).

$lny = lna + bx$

### * Problem 6. Generalizing the Plotting Function
Let's now use the power of Python to generalize the code we created to plot. In Python, you can pass functions as parameters to other functions. We'll utilize this to pass the math function that we're going to plot.

Note: We can also pass *lambda expressions* (anonymous functions) like this:
```python
lambda x: x + 2```
This is a shorter way to write
```python
def some_anonymous_function(x):
    return x + 2
```

We'll also need a range of x values. We may also provide other optional parameters which will help set up our plot. These may include titles, legends, colors, fonts, etc. Let's stick to the basics now.

Write a Python function which takes another function, x range and number of points, and plots the function graph by evaluating it at every point.

**BIG hint:** If you want to use not only `numpy` functions for `f` but any one function, a very useful (and easy) thing to do, is to vectorize the function `f` (e.g. to allow it to be used with `numpy` broadcasting):
```python
f_vectorized = np.vectorize(f)
y = f_vectorized(x)
```

```python
def plot_math_function(f, min_x, max_x, num_points):
    # Write your code here
    pass
```

```python
plot_math_function(lambda x: 2 * x + 3, -3, 5, 1000)
plot_math_function(lambda x: -x + 8, -1, 10, 1000)
plot_math_function(lambda x: x**2 - x - 2, -3, 4, 1000)
plot_math_function(lambda x: np.sin(x), -np.pi, np.pi, 1000)
plot_math_function(lambda x: np.sin(x) / x, -4 * np.pi, 4 * np.pi, 1000)
```

### * Problem 7. Solving Equations Graphically
Now that we have a general plotting function, we can use it for more interesting things. Sometimes we don't need to know what the exact solution is, just to see where it lies. We can do this by plotting the two functions around the "=" sign ans seeing where they intersect. Take, for example, the equation $2x + 3 = 0$. The two functions are $f(x) = 2x + 3$ and $g(x) = 0$. Since they should be equal, the point of their intersection is the solution of the given equation. We don't need to bother marking the point of intersection right now, just showing the functions.

To do this, we'll need to improve our plotting function yet once. This time we'll need to take multiple functions and plot them all on the same graph. Note that we still need to provide the $[x_{min}; x_{max}]$ range and it's going to be the same for all functions.

```python
vectorized_fs = [np.vectorize(f) for f in functions]
ys = [vectorized_f(x) for vectorized_f in vectorized_fs]
```

```python
def plot_math_functions(functions, min_x, max_x, num_points):
    # Write your code here
    pass
```

```python
plot_math_functions([lambda x: 2 * x + 3, lambda x: 0], -3, 5, 1000)
plot_math_functions([lambda x: 3 * x**2 - 2 * x + 5, lambda x: 3 * x + 7], -2, 3, 1000)
```

This is also a way to plot the solutions of systems of equation, like the one we solved last time. Let's actually try it.

```python
plot_math_functions([lambda x: (-4 * x + 7) / 3, lambda x: (-3 * x + 8) / 5, lambda x: (-x - 1) / -2], -1, 4, 1000)
```

### Problem 8. Trigonometric Functions
We already saw the graph of the function $y = \sin(x)$. But, how do we define the trigonometric functions once again? Let's quickly review that.

The two basic trigonometric functions are defined as the ratio of two sides:
$$ \sin(x) = \frac{\text{opposite}}{\text{hypotenuse}} $$
$$ \cos(x) = \frac{\text{adjacent}}{\text{hypotenuse}} $$

And also:
$$ \tan(x) = \frac{\text{opposite}}{\text{adjacent}} = \frac{\sin(x)}{\cos(x)} $$
$$ \cot(x) = \frac{\text{adjacent}}{\text{opposite}} = \frac{\cos(x)}{\sin(x)} $$

This is fine, but using this, "right-triangle" definition, we're able to calculate the trigonometric functions of angles up to $90^\circ$. But we can do better. Let's now imagine a circle centered at the origin of the coordinate system, with radius $r = 1$. This is called a "unit circle".

We can now see exactly the same picture. The $x$-coordinate of the point in the circle corresponds to $\cos(\alpha)$ and the $y$-coordinate - to $\sin(\alpha)$. What did we get? We're now able to define the trigonometric functions for all degrees up to $360^\circ$. After that, the same values repeat: these functions are **periodic**:
$$ \sin(k.360^\circ + \alpha) = \sin(\alpha), k = 0, 1, 2, \dots $$
$$ \cos(k.360^\circ + \alpha) = \cos(\alpha), k = 0, 1, 2, \dots $$

We can, of course, use this picture to derive other identities, such as:
$$ \sin(90^\circ + \alpha) = \cos(\alpha) $$

A very important property of the sine and cosine is that they accept values in the range $(-\infty; \infty)$ and produce values in the range $[-1; 1]$. The two other functions take values in the range $(-\infty; \infty)$ **except when their denominators are zero** and produce values in the same range.

#### Radians
A degree is a geometric object, $1/360$th of a full circle. This is quite inconvenient when we work with angles. There is another, natural and intrinsic measure of angles. It's called the **radian** and can be written as $\text{rad}$ or without any designation, so $\sin(2)$ means "sine of two radians".

It's defined as *the central angle of an arc with length equal to the circle's radius* and $1\text{rad} \approx 57.296^\circ$.

We know that the circle circumference is $C = 2\pi r$, therefore we can fit exactly $2\pi$ arcs with length $r$ in $C$. The angle corresponding to this is $360^\circ$ or $2\pi\ \text{rad}$. Also, $\pi rad = 180^\circ$.

(Some people prefer using $\tau = 2\pi$ to avoid confusion with always multiplying by 2 or 0.5 but we'll use the standard notation here.)

**NOTE:** All trigonometric functions in `math` and `numpy` accept radians as arguments. In order to convert between radians and degrees, you can use the relations $\text{[deg]} = 180/\pi.\text{[rad]}, \text{[rad]} =  \pi/180.\text{[deg]}$. This can be done using `np.deg2rad()` and `np.rad2deg()` respectively.

#### Inverse trigonometric functions
All trigonometric functions have their inverses. If you plug in, say $\pi/4$ in the $\sin(x)$ function, you get $\sqrt{2}/2$. The inverse functions (also called, arc-functions) take arguments in the interval $[-1; 1]$ and return the angle that they correspond to. Take arcsine for example:
$$ \arcsin(y) = x: sin(y) = x $$
$$ \arcsin\left(\frac{\sqrt{2}}{2}\right) = \frac{\pi}{4} $$

Please note that this is NOT entirely correct. From the relations we found:
$$\sin(x) = sin(2k\pi + x), k = 0, 1, 2, \dots $$

it follows that $\arcsin(x)$ has infinitely many values, separated by $2k\pi$ radians each:
$$ \arcsin\left(\frac{\sqrt{2}}{2}\right) = \frac{\pi}{4} + 2k\pi, k = 0, 1, 2, \dots $$

In most cases, however, we're interested in the first value (when $k = 0$). It's called the **principal value**.

Note 1: There are inverse functions for all four basic trigonometric functions: $\arcsin$, $\arccos$, $\arctan$, $\text{arccot}$. These are sometimes written as $\sin^{-1}(x)$, $cos^{-1}(x)$, etc. These definitions are completely equivalent.

Just notice the difference between $\sin^{-1}(x) := \arcsin(x)$ and $\sin(x^{-1}) = \sin(1/x)$.

#### Exercise
Use the plotting function you wrote above to plot the inverse trigonometric functions. Use `numpy` (look up how to use inverse trigonometric functions).

```python
x = np.linspace(0.3, 0.9, 30)
y = np.arcsin(x)
plt.plot(x, y)
plt.show()
```

### ** Problem 9. Perlin Noise
This algorithm has many applications in computer graphics and can serve to demonstrate several things... and help us learn about math, algorithms and Python :).
#### Noise
Noise is just random values. We can generate noise by just calling a random generator. Note that these are actually called *pseudorandom generators*. We'll talk about this later in this course.
We can generate noise in however many dimensions we want. For example, if we want to generate a single dimension, we just pick N random values and call it a day. If we want to generate a 2D noise space, we can take an approach which is similar to what we already did with `np.meshgrid()`.

$$ \text{noise}(x, y) = N, N \in [n_{min}, n_{max}] $$

This function takes two coordinates and returns a single number N between $n_{min}$ and $n_{max}$. (This is what we call a "scalar field").

Random variables are always connected to **distributions**. We'll talk about these a great deal but now let's just say that these define what our noise will look like. In the most basic case, we can have "uniform noise" - that is, each point in our little noise space $[n_{min}, n_{max}]$ will have an equal chance (probability) of being selected.

#### Perlin noise
There are many more distributions but right now we'll want to have a look at a particular one. **Perlin noise** is a kind of noise which looks smooth. It looks cool, especially if it's colored. The output may be tweaked to look like clouds, fire, etc. 3D Perlin noise is most widely used to generate random terrain.

#### Algorithm
... Now you're on your own :). Research how the algorithm is implemented (note that this will require that you understand some other basic concepts like vectors and gradients).

#### Your task
1. Research about the problem. See what articles, papers, Python notebooks, demos, etc. other people have created
2. Create a new notebook and document your findings. Include any assumptions, models, formulas, etc. that you're using
3. Implement the algorithm. Try not to copy others' work, rather try to do it on your own using the model you've created
4. Test and improve the algorithm
5. (Optional) Create a cool demo :), e.g. using Perlin noise to simulate clouds. You can even do an animation (hint: you'll need gradients not only in space but also in time)
6. Communicate the results (e.g. in the Softuni forum)

Hint: [This](http://flafla2.github.io/2014/08/09/perlinnoise.html) is a very good resource. It can show you both how to organize your notebook (which is important) and how to implement the algorithm.


```python
%autosave 0
```

# MCPC rehearsal problem Oct 25 2017 at UCSY

## Problem E: Stacking Plates

### Input format

- 1st Line: 1 integer, Number of Test Case, each Test Case has following data
   + 1 Line: 1 integer, **n**(Number of Stacks)
   + **n** Lines: first integer: **h** (Number of Plates), and **h** integers (Plate size)

### Output format

Case: (test case number): Number of Operations

### Sample Input

```
3
2
3 1 2 4
2 3 5
3
4 1 1 1 1
4 1 1 1 1
4 1 1 1 1
2
15 1 1 1 1 1 2 2 2 2 2 3 3 3 3 3
15 1 1 1 1 1 2 2 2 2 2 3 3 3 3 3
```

### Sample Output

```
Case 1:5
Case 2:2
Case 3:5
```

### Explanation of sample I/O

- 3 test cases
    + Stack of (1,2,4) and (3,5)
    + Stack of 3 (1,1,1,1)
    + Stack of 2 (1,1,1,1,1,2,2,2,2,2,3,3,3,3,3)

- 1st case:
Split between 2 and 4, 3 and 5, Move 4 on 5, 3 on 4, (1,2) on 3  ==> Total 5 operations

- 2nd case:
Move 1st stack (1,1,1,1,1) on 2nd stack, move (1st+2nd stack) on 3rd stack  ==> Total 2 operations

- 3rd case:
Split between 1 and 2 of 1st stack, between 2 and 3 of 2nd stack, move (2,2,2,2,2,3,3,3,3,3) of 1st stack on (3,3,3,3,3) of 2nd, move (1,1,1,1,1,2,2,2,2,2) of 2nd stack on it, move (1,1,1,1,1) on top  ==> Total 5 operations

### Specific vs Abstract,  Find a General Rule from Detail

When solving problems and puzzles that you can not find how to solve, thinking specificlly then abstractly is important. Finding general rules from specific examples give you a path to the answer.

- Think about simple cases
- Find general pattern (idea, rule) from there
- Proove the rule (if possible)
- Extend the rule to more complex cases

### How to calculate number of operation(movement)

If there are N stacks and each contains just 1 piece (Split is not necessary), (N-1) operations are reuired. (N-1) is a minimum number of oprations.

For each Split operation, Join operation is required to create single stack. Total number of operation increases by 2 for each Split operations (S). The order of Split and Join does not affect total number of movement. (Split-Split-Join-Join) = (Split-Join-Split-Join) \begin{equation} Nmber Of Movement = 2S + (N-1) \end{equation}

Same size of pieces in original stacks (Case 2 and Case 3) can be considered to be same as single piece. Case 2: 3 stack of (1), Case 3: 2 stack of (1,2,3)

### Optimized movement

Reverse-Thinking is sometimes very effective. Create Final Stack and check the boundary. If the combination of the boundary exists in original stacks, it can be used (not necessary to split). **Stack ID needs to be checked, as for detail see later**.

$S = (Maximum Number Of Split) - (Number Of Reused Boundary)$

- Case 1: [1,2,3,4,5] is final form. Boundary of [1,2] exist in Original Stack-1, $S=(2+1)-1=2, Movement=2*2+(2-1)=5$
- Case 2: Convert original stacks to [1], $Movement=2*0+(3-1)=2$
- Case 3: Convert original stacks to [1,2,3], Final form is [1,1,2,2,3,3]. Boundary of [1,2] and [2,3] exists. $S=(2+2)-2=2$

### Sample I/O gives hint

Sample Input/Output often gives great hint to solve problems. Same number in original stack cause problem in above idea, but same number can be considered to be 1 digit, so convert input data to eliminate duplicate number.

### Stack ID checking

- Assign stack ID
- Merge every plates and sort by radius (size of plate)
- Manage the list of candidate for boundary reuse (top and bottom)
- Boundary assignment can be done greedy, if there is only 1 combination between top of stack and next, use it

```python
#!/usr/bin/python3
# -*- coding: utf-8 -*-

'''
    2017 MCPC at UCSY
    Problem-E: Stacking Plates
'''
import sys

class TestCase():
    pass

class Plates():
    # Groupt of same radius plates in merged stack
    # id_list: list of stack ID
    def __init__(self, radius, id_list):
        self.radius = radius
        self.id_list = id_list
        self.top = None
        self.bottom = None

    def match_prev(self, prev_bottom):
        self.top = list()
        for stack_id in self.id_list:
            if stack_id in prev_bottom:
                self.top.append(stack_id)
        self.bottom = self.id_list.copy()
        if len(self.top) == 1 and len(self.bottom) != 1:
            self.bottom.remove(self.top[0])

        return

    def __repr__(self):
        return ('Plates {}: {}, top: {} bottom: {}'.format(self.radius, self.id_list, self.top, self.bottom))

def parse_tc(tc):
    '''
        Input: Test Case
        Update:
        Return: None
    '''

    tc.n = int(tc.stdin.readline())
    tc.stacks = list()

    for i in range(tc.n):
        stack = tc.stdin.readline().split()[1:]  # 2d List, 1st=len
        tc.stacks.append(stack)

    return

def reform_stack(org):
    '''
        Input: tc.stacks
        Output: cosolidated stacks (no prefix, no duplicate)
    '''

    stacks = list()
    stack_id = 0

    for stack in org:
        prev_radius = None
        new_stack = list()

        for radius in stack:
            if radius != prev_radius:
                new_stack.append((radius, stack_id))
            prev_radius = radius

        stacks.append(new_stack)
        stack_id += 1

    return stacks

def merge(stacks):
    '''
        stacks: 2D List of tuple(radius, id)
        Return: 1D sorted List
    '''

    merged_stack = list()

    for stack in stacks:
        merged_stack.extend(stack)

    merged_stack.sort()

    return merged_stack

def stack2plates(merged_stack):
    '''
    merged_stack: List of Tuple(radius, id)
    return: List of Plates
    '''

    plates_list = list()
    id_list = list()
    prev_size = None

    for plate in merged_stack:
        radius, plate_id = plate
        if radius != prev_size:
            if id_list:
                plates_list.append(Plates(prev_size, id_list))
            id_list = [plate_id]
        else:
            id_list.append(plate_id)

        prev_size = radius

    if id_list:
        plates_list.append(Plates(radius, id_list))

    return plates_list

def max_reuse(plates_list):

    reuse = 0
    prev_bottom = list()

    for plates in plates_list:
        plates.match_prev(prev_bottom)
        if plates.top: reuse += 1
        prev_bottom = plates.bottom
        #print(plates, file=sys.stderr)

    return reuse

def solve(tc):
    '''
        Input: Test Case
        Return: Numper of movement
    '''

    parse_tc(tc)
    stacks = reform_stack(tc.stacks)
    #print(stacks)
    num_merge = len(stacks) - 1  ## Join Stacks
    for stack in stacks:
        num_merge += (len(stack) - 1) * 2  ## Split and Join

    merged_stack = merge(stacks)
    plates_list = stack2plates(merged_stack)  # list of Plates

    #return (num_merge - check_bound(merged_stack, stack_bound) * 2)
    return (num_merge - max_reuse(plates_list) * 2)

```

```python
### Main routine

infile = open('reh_e.in', 'r')

tc = TestCase()
tc.stdin = infile
tc.t = int(tc.stdin.readline())

for i in range(tc.t):
    print('Case {}:{}'.format(i+1, solve(tc)))
```

    Case 1:5
    Case 2:2
    Case 3:5
    Case 4:8
    Case 5:3

```python

```


+ This notebook is part of lecture 10 *The four fundamental subspaces* in the OCW MIT course 18.06 by Prof Gilbert Strang [1]
+ Created by me, Dr Juan H Klopper
    + Head of Acute Care Surgery
    + Groote Schuur Hospital
    + University Cape Town
    + Email me with your thoughts, comments, suggestions and corrections

Linear Algebra OCW MIT18.06 IPython notebook [2] study notes by Dr Juan H Klopper is licensed under a Creative Commons Attribution-NonCommercial 4.0 International License.

+ [1] OCW MIT 18.06
+ [2] Fernando Pérez, Brian E. Granger, IPython: A System for Interactive Scientific Computing, Computing in Science and Engineering, vol. 9, no. 3, pp. 21-29, May/June 2007, doi:10.1109/MCSE.2007.53. URL: http://ipython.org

```python
from IPython.core.display import HTML, Image
css_file = 'style.css'
HTML(open(css_file, 'r').read())
```

<link href='http://fonts.googleapis.com/css?family=Alegreya+Sans:100,300,400,500,700,800,900,100italic,300italic,400italic,500italic,700italic,800italic,900italic' rel='stylesheet' type='text/css'>
<link href='http://fonts.googleapis.com/css?family=Arvo:400,700,400italic' rel='stylesheet' type='text/css'>
<link href='http://fonts.googleapis.com/css?family=PT+Mono' rel='stylesheet' type='text/css'>
<link href='http://fonts.googleapis.com/css?family=Shadows+Into+Light' rel='stylesheet' type='text/css'>
<link href='http://fonts.googleapis.com/css?family=Philosopher:400,700,400italic,700italic' rel='stylesheet' type='text/css'>

```python
#import numpy as np
from sympy import init_printing, Matrix, symbols
#import matplotlib.pyplot as plt
#import seaborn as sns
#from IPython.display import Image
from warnings import filterwarnings

init_printing(use_latex = 'mathjax') # Pretty Latex printing to the screen
#%matplotlib inline
filterwarnings('ignore')
```

# The four fundamental subspaces
# Introducing the matrix space

## The four fundamental subspaces

* Columnspace, C(A)
* Nullspace, N(A)
* Rowspaces
    * All linear combinations of rows
    * All the linear combinations of the colums of AT, C(AT)
* Nullspace of AT, N(AT) (the left nullspace of A)

## Where are these spaces for a matrix Am×n?

* C(A) is in ℝm
* N(A) is in ℝn
* C(AT) is in ℝn
* N(AT) is in ℝm

## Calculating basis and dimension

### For C(A)

* The bases are the pivot columns
* The dimension is the rank *r*

### For N(A)

* The bases are the special solutions (one for every free variable, *n* - *r*)
* The dimension is *n* - *r*

### For C(AT)

* If A undergoes row reduction to row echelon form (R), then C(R) ≠ C(A), but rowspace(R) = rowspace(A) (or C(RT) = C(AT))
* A basis for the rowspace of A (or R) is the first *r* rows of R
    * So we row reduce A and take the pivot rows and transpose them
* The dimension is also equal to the rank *r*

### For N(AT)

* It is also called the left, because it ends up on the left (as seen below)
* Here we have AT**y** = **0**
    * **y**T(AT)T = **0**T
    * **y**TA = **0**T
    * This is (again) the pivot columns of AT (after row reduction)
* The dimension is *m* - *r*

## Example problems

### Consider this example matrix and calculate the bases and dimension for all four fundamental spaces

```python
A = Matrix([[1, 2, 3, 1], [1, 1, 2, 1], [1, 2, 3, 1]]) # We note that rows 1 and three are identical and that
# columns 3 is the addtion of columns 1 and 2 and column 1 equals column 4
A
```

$$\left[\begin{matrix}1 & 2 & 3 & 1\\1 & 1 & 2 & 1\\1 & 2 & 3 & 1\end{matrix}\right]$$

#### Columnspace

```python
A.rref() # Remember that the columnspace contains the pivot columns as a basis
```

$$\begin{pmatrix}\left[\begin{matrix}1 & 0 & 1 & 1\\0 & 1 & 1 & 0\\0 & 0 & 0 & 0\end{matrix}\right], & \begin{bmatrix}0, & 1\end{bmatrix}\end{pmatrix}$$

* The basis is thus:
$$ \begin{bmatrix} 1 & 0 \\ 0 & 1 \\ 0 & 0 \end{bmatrix} $$
* It is indeed in ℝ3 (rows of A = *m* = 3, i.e. each column vector is in 3-space or has 3 components)

* The rank (no of columns with pivots) are 2, thus dim(A) = 2

#### Nullspace

```python
A.nullspace() # Calculating the nullspace vectors
```

$$\begin{bmatrix}\left[\begin{matrix}-1\\-1\\1\\0\end{matrix}\right], & \left[\begin{matrix}-1\\0\\0\\1\end{matrix}\right]\end{bmatrix}$$

* So, indeed the basis is in ℝ4 (A has *n* = 4 columns)

```python
A.rref() # No pivots for columns 3 and 4
```

$$\begin{pmatrix}\left[\begin{matrix}1 & 0 & 1 & 1\\0 & 1 & 1 & 0\\0 & 0 & 0 & 0\end{matrix}\right], & \begin{bmatrix}0, & 1\end{bmatrix}\end{pmatrix}$$

* The dimension is two (there are 2 column vectors, which is indeed *n* - *r* = 4 - 2 = 2)

#### Rowspace C(AT)

* So we are looking for the pivot columns of AT

```python
A.rref()
```

$$\begin{pmatrix}\left[\begin{matrix}1 & 0 & 1 & 1\\0 & 1 & 1 & 0\\0 & 0 & 0 & 0\end{matrix}\right], & \begin{bmatrix}0, & 1\end{bmatrix}\end{pmatrix}$$

* The pivot rows are rows 1 and 2
* We take them and transpose them
$$ \begin{bmatrix} 1 & 0 \\ 0 & 1 \\ 1 & 1 \\ 1 & 0 \end{bmatrix} $$

* As stated above, it is in ℝ4

* The dimension is *n* - *r* = 4 - 2 = 2

#### Nullspace of AT

```python
A.nullspace()
```

$$\begin{bmatrix}\left[\begin{matrix}-1\\-1\\1\\0\end{matrix}\right], & \left[\begin{matrix}-1\\0\\0\\1\end{matrix}\right]\end{bmatrix}$$

* Which is indeed in ℝ3

* The dimension is 1, since *m* - *r* = 3 - 2 = 1 (remember that the rank is the number of pivot columns)

### Consider this example matrix (in LU form) and calculate the bases and dimension for all four fundamental spaces

```python
L = Matrix([[1, 0, 0], [2, 1, 0], [-1, 0, 1]])
U = Matrix([[5, 0, 3], [0, 1, 1], [0, 0, 0]])
A = L * U
L, U, A
```

$$\begin{pmatrix}\left[\begin{matrix}1 & 0 & 0\\2 & 1 & 0\\-1 & 0 & 1\end{matrix}\right], & \left[\begin{matrix}5 & 0 & 3\\0 & 1 & 1\\0 & 0 & 0\end{matrix}\right], & \left[\begin{matrix}5 & 0 & 3\\10 & 1 & 7\\-5 & 0 & -3\end{matrix}\right]\end{pmatrix}$$

#### Columnspace of A

```python
A.rref()
```

$$\begin{pmatrix}\left[\begin{matrix}1 & 0 & \frac{3}{5}\\0 & 1 & 1\\0 & 0 & 0\end{matrix}\right], & \begin{bmatrix}0, & 1\end{bmatrix}\end{pmatrix}$$

* The basis is thus:
$$ \begin{bmatrix} 1 & 0 \\ 0 & 1 \\ 0 & 0 \end{bmatrix} $$
* Another basis would be the pivot columns of L:
$$ \begin{bmatrix} 1 & 0 \\ 2 & 1 \\ -1 & 0 \end{bmatrix} $$
* It is in ℝ3, since *m* = 3
* It has a rank of 2 (two pivot columns)
* Since the dimension of the columnspace is equal to the rank, dim(A) = 2
    * Note that it is also equal to the number of pivot columns in U

#### Nullspace of A

```python
A.nullspace()
```

$$\begin{bmatrix}\left[\begin{matrix}- \frac{3}{5}\\-1\\1\end{matrix}\right]\end{bmatrix}$$

* The nullspace is in ℝ3, since *n* = 3
* The basis is the special solution(s), which is one column vector for every free variable
    * Since we only have a single free variable, we have a single nullspace column vector
    * This fits in with the fact that it needs to be *n* - *r*
    * It can also be calculated by taking U, setting the free variable to 1 and solving for the other rows by setting each equal to zero
* The dimension of the nullspace is also 1 (*n* - *r*, i.e. a single column)
    * It is also the number of free variables

#### The rowspace

* This is the columnspace of AT
* Don't take the transpose first!
* Row reduce, identify the rows with pivots and transpose them

```python
A.rref()
```

$$\begin{pmatrix}\left[\begin{matrix}1 & 0 & \frac{3}{5}\\0 & 1 & 1\\0 & 0 & 0\end{matrix}\right], & \begin{bmatrix}0, & 1\end{bmatrix}\end{pmatrix}$$

* The basis is can also be written down by identifying the rows with pivots in U and writing them down as columns (getting their transpose)
$$ \begin{bmatrix} 5 & 0 \\ 0 & 1 \\ 3 & 1 \end{bmatrix} $$
* It is in ℝ3, since *n* = 3
* The rank *r* = 2, which is equal to the dimension, i.e. dim(AT) = 2

#### The nullspace of AT

```python
A.transpose().nullspace()
```

$$\begin{bmatrix}\left[\begin{matrix}1\\0\\1\end{matrix}\right]\end{bmatrix}$$

* It is indeed in ℝ3, since *m* = 3
* A good way to do it is to take the inverse of L, such that L-1A = U
    * Now the free variable row in U is row three
    * Take the corresponding row in L-1 and transpose it
* The dimension in *m* - 2 = 3 - 2 = 1

## The matrix space

* A square matrix is also a 'vector' space, because they obey the vector space rules of addition and scalar multiplication
* Subspaces (of same) would include
    * Upper triangular matrices
    * Symmetric matrices

```python

```


```python
from IPython.core.display import display_html
from urllib.request import urlopen

cssurl = 'http://j.mp/1DnuN9M'
display_html(urlopen(cssurl).read(), raw=True)
```

<link href='http://fonts.googleapis.com/css?family=EB+Garamond' rel='stylesheet' type='text/css'>
<link href='http://fonts.googleapis.com/css?family=Lato:100,100italic' rel='stylesheet' type='text/css'>

<style>
	@font-face{
   		font-family: "Computer Modern";
        	src: url('http://mirrors.ctan.org/fonts/cm-unicode/fonts/otf/cmunss.otf');}

        div.cell{
	        max-width:    800px;
	        margin-left:  auto;
	        margin-right: auto;}

	h1 {
        	font-family: 'Lato', sans-serif;}

	h2 {
        	font-family: 'Lato', serif;}

	h3{
		font-family:   'Lato', serif;
        	margin-top:    12px;
		margin-bottom: 3px;}

        h4{
		font-family: 'Lato', serif;}

	h5 {
        	font-family: 'Lato', serif;}

	div.text_cell_render{
        	font-family:  'EB Garamond', 'Computer Modern', 'Helvetica Neue', Arial, Helvetica, Geneva, sans-serif;
	        line-height:  140%;
	        font-size:    14pt;
		max-width:    650px;
		margin-left:  auto;
        	margin-right: auto;}

	.CodeMirror{
        	font-family: 'Consolas';}

	.text_cell_render h1 {
	        font-weight:   100;
	        font-size:     48pt;
		line-height:   100%;
	        color:         #CD2305;
	        margin-bottom: 0.5em;
	        margin-top:    0.5em;
	        display:       block;}

	.text_cell_render h2 {
        	font-weight:   100;
        	font-size:     36pt;}

	.text_cell_render h3 {
        	font-weight:   100;
        	font-size:     21pt;}

	.text_cell_render h4 {
        	font-weight:   100;
        	font-size:     18pt;}

	.text_cell_render h5 {
	        font-weight:   100;
	        font-size:     16pt;
	        color:         #CD2305;
	        font-style:    italic;
	        margin-bottom: 0.5em;
	        margin-top:    0.5em;
	        display:       block;}

	.text_cell_render h6 {
	        font-weight:   100;
	        font-size:     14pt;
	        color:         #CD2305;
	        font-style:    italic;
	        margin-bottom: 0.5em;
	        margin-top:    0.5em;
	        display:       block;}

	.warning{
	    	background-color: #FFFFE0;
	    	border-color: #FFCC99;
	    	border-left: 5px solid #FFCC99;
	    	padding: 0.5em;}

	.error{
	    	background-color: #fcf2f2;
	    	border-color: #dFb5b4;
	    	border-left: 5px solid #dfb5b4;
	    	padding: 0.5em;}

</style>

# Examen Final

## Modelado de Carro Pendulo

### Variables de estado, posiciones y velocidades

Utilizaremos el enfoque de Euler-Lagrange, el cual nos dice que el primer paso para conseguir el modelo matemático es calcular el Lagrangiano $L$ del sistema, definido por:

$$
L = K - U
$$

en donde $K$ es la energía cinética del sistema y $U$ es la energia potencial del sistema.

El estado del sistema estará descrito por una distancia $x$ del centro del carro a un marco de referencia y el angulo $\theta$ del pendulo con respecto a la horizontal.

$$
q = \begin{pmatrix} x \\ \theta \end{pmatrix} = \begin{pmatrix} q_1 \\ q_2 \end{pmatrix} \implies
\dot{q} = \begin{pmatrix} \dot{x} \\ \dot{\theta} \end{pmatrix} = \begin{pmatrix} \dot{q}_1 \\ \dot{q}_2 \end{pmatrix} \implies
\ddot{q} = \begin{pmatrix} \ddot{x} \\ \ddot{\theta} \end{pmatrix} = \begin{pmatrix} \ddot{q}_1 \\ \ddot{q}_2 \end{pmatrix}
$$

### Energía cinética

Para calcular la energía cinética del sistema, obtenemos $K_1$ y $K_2$ asociadas al carro y al pendulo, en donde $K_i = \frac{1}{2} m_i v_i^2$, por lo que tenemos:

$$
K_1 = \frac{1}{2} m_1 v_1^2 = \frac{1}{2} m_1 \dot{x}^2 = \frac{1}{2} m_1 \dot{q}_1^2
$$

$$
K_2 = \frac{1}{2} m_2 v_2^2 = \frac{1}{2} m_2 \left[ \left( \dot{x} + \dot{x}_2 \right)^2 + \dot{y}_2^2 \right]
$$

con $x_2 = l \cos{\theta}$ y $y_2 = l \sin{\theta}$, por lo que sus derivadas son $\dot{x}_2 = -\dot{\theta} l \sin{\theta}$ y $\dot{y}_2 = \dot{\theta} l \cos{\theta}$, por lo que $K_2$ queda:

$$
\begin{align}
K_2 &= \frac{1}{2} m_2 \left[ \left( \dot{x} -\dot{\theta} l \sin{\theta} \right)^2 + \left( \dot{\theta} l \cos{\theta} \right)^2 \right] \\
&= \frac{1}{2} m_2 \left[ \left( \dot{x} -\dot{\theta} l \sin{\theta} \right)^2 + \left( \dot{\theta} l \cos{\theta} \right)^2 \right] \\
&= \frac{1}{2} m_2 \left[ \left( \dot{q}_1 -\dot{q}_2 l \sin{q_2} \right)^2 + \left( \dot{q}_2 l \cos{q_2} \right)^2 \right]
\end{align}
$$

Entonces la energía cinética será:

$$
K = \frac{1}{2} \left[ (m_1 + m_2) \dot{q}_1^2 + m_2 l^2 \dot{q}_2^2 - 2 m_2 l \sin{q_2} \dot{q}_1 \dot{q}_2 \right]
$$

Lo cual puede ser escrito como una forma matricial cuadratica:

$$
K = \frac{1}{2}
\begin{pmatrix}
\dot{q}_1 & \dot{q}_2
\end{pmatrix}
\begin{pmatrix}
m_1 + m_2 & -m_2 l \sin{q_2} \\
-m_2 l \sin{q_2} & m_2 l^2
\end{pmatrix}
\begin{pmatrix}
\dot{q}_1 \\
\dot{q}_2
\end{pmatrix}
$$

En este punto, introducimos una variable intermedia, tan solo para reducir un poco la notación:

$$
\lambda = l \sin{q_2}
$$

y su derivada con respecto al tiempo, la representamos como:

$$
\dot{\lambda} = \lambda' \dot{q}_2 \implies \lambda' = l \cos{q_2}
$$

por lo que la energía cinetica queda como:

$$
K = \frac{1}{2}
\begin{pmatrix}
\dot{q}_1 & \dot{q}_2
\end{pmatrix}
\begin{pmatrix}
m_1 + m_2 & -m_2 \lambda \\
-m_2 \lambda & m_2 l^2
\end{pmatrix}
\begin{pmatrix}
\dot{q}_1 \\
\dot{q}_2
\end{pmatrix}
$$

en donde el termino matricial es la matriz de masa $M(q)$ y $K(q, \dot{q}) = \dot{q}^T M(q) \dot{q}$.

### Energía potencial

Por otro lado, para calcular la energía potencial del sistema, tenemos que:

$$
U_1 = m_1 g h_1 = 0
$$

$$
U_2 = m_2 g h_2 = m_2 g l \sin{q_1} = m_2 g \lambda
$$

por lo que la energía potencial del sistema será:

$$
U = m_2 g \lambda
$$

### Lagrangiano

El Lagrangiano del sistema, esta dado por la expresión:

$$
L = K - U
$$

por lo que solo queda sumar las dos expresiones y obtener las condiciones de optimalidad para la energía del sistema por medio de la ecuación de Euler-Lagrange

### Euler-Lagrange

Cuando aplicamos la primer condición de optimalidad al Lagrangiano $L(t, q(t), \dot{q}(t)) = K(q(t), \dot{q}(t)) - U(q)$, tenemos la ecuación de Euler-Lagrage, la cual nos dice que:

$$
\frac{d}{dt} L_{\dot{q}} - L_q = 0
$$

por lo que debemos encontrar la derivada del Lagrangiano, con respecto a $q$, $\dot{q}$ y derivar esta ultima con respecto al tiempo. Empecemos con la derivada con respecto a $q$:

$$
L_q = K_q - U_q
$$

en donde:

$$
\begin{align}
K_q &= \frac{\partial}{\partial q} \left\{ \frac{1}{2} \left[ (m_1 + m_2) \dot{q}_1^2 + m_2 l^2 \dot{q}_2^2 - 2 m_2 \lambda \dot{q}_1 \dot{q}_2 \right] \right\} \\
&= \frac{1}{2}
\begin{pmatrix}
\frac{\partial}{\partial q_1} \left\{ \left[ (m_1 + m_2) \dot{q}_1^2 + m_2 l^2 \dot{q}_2^2 - 2 m_2 \lambda \dot{q}_1 \dot{q}_2 \right] \right\} \\
\frac{\partial}{\partial q_2} \left\{ \left[ (m_1 + m_2) \dot{q}_1^2 + m_2 l^2 \dot{q}_2^2 - 2 m_2 \lambda \dot{q}_1 \dot{q}_2 \right] \right\}
\end{pmatrix} \\
&= \frac{1}{2}
\begin{pmatrix}
0 \\
- 2 m_2 \lambda' \dot{q}_1 \dot{q}_2
\end{pmatrix} = - m_2 \lambda'
\begin{pmatrix}
0 & 0 \\
\dot{q}_2 & 0
\end{pmatrix}
\begin{pmatrix}
\dot{q}_1 \\
\dot{q}_2
\end{pmatrix}
\end{align}
$$

y la derivada de la energía potencial con respecto a $q$:

$$
U_q = \frac{\partial}{\partial q} \left\{ m_2 g \lambda \right\} =
\begin{pmatrix}
\frac{\partial}{\partial q_1} \left\{ m_2 g \lambda \right\} \\
\frac{\partial}{\partial q_2} \left\{ m_2 g \lambda \right\}
\end{pmatrix} =
\begin{pmatrix}
0 \\
m_2 g \lambda'
\end{pmatrix}
$$

Ahora obtenemos la derivada con respecto a $\dot{q}$:

$$
L_{\dot{q}} = K_{\dot{q}} - U_{\dot{q}}
$$

en donde:

$$
K_{\dot{q}} = \frac{1}{2} \frac{\partial}{\partial \dot{q}} \left\{ \dot{q}^T M(q) \dot{q} \right\} = M(q) \dot{q}
$$

$$
U_{\dot{q}} = 0
$$

Derivando con respeto al tiempo estas ultimas expresiones, obtenemos:

$$
\frac{d}{dt} K_{\dot{q}} = \dot{M}(q, \dot{q}) \dot{q} + M(q) \ddot{q}
$$

en donde

$$
\begin{align}
\dot{M}(q, \dot{q}) &= \frac{d}{dt} M(q) = \frac{d}{dt}
\begin{pmatrix}
m_1 + m_2 & -m_2 \lambda \\
-m_2 \lambda & m_2 l^2
\end{pmatrix} \\
&=
\begin{pmatrix}
0 & -m_2 \lambda' \dot{q}_2 \\
-m_2 \lambda' \dot{q}_2 & 0
\end{pmatrix} = -m_2 \lambda'
\begin{pmatrix}
0 & \dot{q}_2 \\
\dot{q}_2 & 0
\end{pmatrix}
\end{align}
$$

y ya tenemos todos los elementos que integran nuestra ecuación de Euler-Lagrange:

$$
\begin{align}
\frac{d}{dt} L_{\dot{q}} - L_q &= 0 \\
M(q) \ddot{q} + \dot{M}(q, \dot{q}) \dot{q} - K_q + U_q &= 0
\end{align}
$$

Tan solo cabe recalcar que el segundo y tercer termino se pueden reducir a uno solo:

$$
\begin{align}
\dot{M}(q, \dot{q}) \dot{q} - K_q &= -m_2 \lambda'
\begin{pmatrix}
0 & \dot{q}_2 \\
\dot{q}_2 & 0
\end{pmatrix}
\begin{pmatrix}
\dot{q}_1 \\
\dot{q}_2
\end{pmatrix} + m_2 \lambda'
\begin{pmatrix}
0 & 0 \\
\dot{q}_2 & 0
\end{pmatrix}
\begin{pmatrix}
\dot{q}_1 \\
\dot{q}_2
\end{pmatrix} \\
&= -m_2 \lambda'
\begin{pmatrix}
0 & \dot{q}_2 \\
0 & 0
\end{pmatrix}
\begin{pmatrix}
\dot{q}_1 \\
\dot{q}_2
\end{pmatrix} = C(q, \dot{q})\dot{q}
\end{align}
$$

Por lo que finalmente para el sistema libre tenemos:

$$
M(q) \ddot{q} + C(q, \dot{q}) \dot{q} + U_q = 0
$$

con:

$$
M(q) =
\begin{pmatrix}
m_1 + m_2 & -m_2 \lambda \\
-m_2 \lambda & m_2 l^2
\end{pmatrix} \quad
C(q, \dot{q}) = -m_2 \lambda'
\begin{pmatrix}
0 & \dot{q}_2 \\
0 & 0
\end{pmatrix} \quad
U_q =
\begin{pmatrix}
0 \\
m_2 g \lambda'
\end{pmatrix}
$$

### Señales de control

Y para el sistema bajo control tan solo tenemos que agregar la señal de control asociada al primer grado de libertad, por lo que tendremos:

$$
M(q) \ddot{q} + C(q, \dot{q}) \dot{q} + U_q = G u
$$

con $G = \begin{pmatrix} 1 \\ 0 \end{pmatrix}$.

### Simulación

```python
def f(estado, tiempo):
    from numpy import zeros, matrix, sin, cos

    m1 = 0.2
    m2 = 0.4
    g = 9.81
    l = 0.6

    q1, q2, q1p, q2p = estado

    q = matrix([[q1], [q2]])
    qp = matrix([[q1p], [q2p]])

    λ = l*sin(q2)
    λ̇ = l*cos(q2)

    M = matrix([[m1 + m2, -m2*λ], [-m2*λ, m2*l**2]])
    C = -m2*λ̇*matrix([[0, q2p], [0, 0]])
    U = matrix([[0], [m2*g*λ̇]])

    qpp = M.I*(-C*qp - U)

    dydx = zeros(4)

    dydx[0] = q1p
    dydx[1] = q2p
    dydx[2] = qpp[0]
    dydx[3] = qpp[1]

    return dydx
```

```python
from scipy.integrate import odeint
from numpy import linspace, arange, sin, cos
from matplotlib.pyplot import figure, style, plot
from matplotlib import animation
from matplotlib.patches import Rectangle, Circle
```

```python
ts = linspace(0, 4, 100)
estado_inicial = [0, 0.1, 0, 0]
```

```python
estados = odeint(f, estado_inicial, ts)
q1, q2 = estados[:, 0], estados[:, 1]
```

```python
fig = figure(figsize=(8, 6))

ax = fig.add_subplot(111, autoscale_on=False,
                     xlim=(-0.8333, 1.8333), ylim=(-1.25, 0.75))

ax.axes.get_xaxis().set_visible(False)
ax.axes.get_yaxis().set_visible(False)

ax.axes.spines["right"].set_color("none")
ax.axes.spines["left"].set_color("none")
ax.axes.spines["top"].set_color("none")
ax.axes.spines["bottom"].set_color("none")

ax.set_axis_bgcolor('#F2F1EC')

linea, = ax.plot([], [], 'o-', lw=1.5, color='#393F40')
carro = Rectangle((10,10), 0.8, 0.35, lw=1.5, fc='#E5895C')
guia = Rectangle((10, 10), 2.6666, 0.1, lw=1.5, fc='#A4B187')
pendulo = Circle((10, 10), 0.125, lw=1.5, fc='#F3D966')

def init():
    linea.set_data([], [])
    guia.set_xy((-0.8333, -0.05))
    carro.set_xy((-0.4, -0.175))
    pendulo.center = (1, 0)
    ax.add_patch(guia)
    ax.add_patch(carro)
    ax.add_patch(pendulo)
    return linea, carro, pendulo

def animate(i):
    xs = [q1[i], q1[i] + cos(q2[i])]
    ys = [0, sin(q2[i])]

    linea.set_data(xs, ys)
    carro.set_xy((xs[0] - 0.4, ys[0] - 0.175))
    pendulo.center = (xs[1], ys[1])
    return linea, carro, pendulo

ani = animation.FuncAnimation(fig, animate, arange(1, len(q1)), interval=25,
                              blit=True, init_func=init)

ani.save('./imagenes/carropendulolibre.gif', writer='imagemagick');
```

Espero te hayas divertido con esta larga explicación y al final sepas un truco mas.

Si deseas compartir este Notebook de IPython utiliza la siguiente dirección:

http://bit.ly/1M2tenc

o bien el siguiente código QR:

```python
# Codigo para generar codigo :)
from qrcode import make
img = make("http://bit.ly/1M2tenc")
img.save("./codigos/carropendulo.jpg")
```

IPythonNotebooks/Control Optimo/Examen Final/Carro Pendulo.ipynb


```python
# Erasmus+ ICCT project (2018-1-SI01-KA203-047081)

# Toggle cell visibility

from IPython.display import HTML
tag = HTML('''
Promijeni vidljivost <a href="javascript:code_toggle()">ovdje</a>.''')
display(tag)

# Hide the code completely

# from IPython.display import HTML
# tag = HTML('''<style>
# div.input {
#     display:none;
# }
# </style>''')
# display(tag)
```

Promijeni vidljivost ovdje.

```python
# Examples:
# Factored form: 1/(x**2*(x**2 + 1))
# Expanded form: 1/(x**4+x**2)

import sympy as sym
from IPython.display import Latex, display, Markdown, Javascript, clear_output
from ipywidgets import widgets, Layout # Interactivity module
```

## Rastav na parcijalne razlomke

Kada se Laplaceova transformacija koristi za analizu sustava, Laplaceova transformacija izlaznog signala dobiva se u obliku umnoška prijenosne funkcije i Laplaceove transformacije ulaznog signala. Rezultat ovog množenja često je složen za shvatiti. Da bismo izvršili inverznu Laplaceovu transformaciju, prvo se izvodi postupak rastava na parcijalne razlomke. Ovaj primjer demonstrira taj postupak.

---

### Kako koristiti ovaj interaktivni primjer?
Izaberite jednu između ponuđenih opcija: *Unos funkcije* ili *Unos koeficijenata polinoma*.

1. *Unos funkcije*:
 * Primjer: Za unos funkcije $\frac{1}{x^2(x^2 + 1)}$ (faktorizirani oblik) upišite 1/(x\*\*2\*(x\*\*2 + 1)); za unos iste funkcije u proširenom obliku ($\frac{1}{x^4+x^2}$) upišite 1/(x\*\*4+x\*\*2).

2. *Unos koeficijenata polinoma*:
 * Pomoću klizača odaberite red brojnika i nazivnika željene funkcije.
 * Upišite koeficijente za brojnik i nazivnik u odgovarajuća unosna polja te kliknite na *Potvrdi*.

```python
## System selector buttons
style = {'description_width': 'initial'}
typeSelect = widgets.ToggleButtons(
    options=[('Unos funkcije', 0), ('Unos koeficijenata polinoma', 1),],
    description='Odaberi: ',style={'button_width':'230px'})

btnReset=widgets.Button(description="Reset")

# function
textbox=widgets.Text(description=('Unos funkcije:'),style=style)
btnConfirmFunc=widgets.Button(description="Potvrdi") # ex btnConfirm

# poly
btnConfirmPoly=widgets.Button(description="Potvrdi") # ex btn

display(typeSelect)

def on_button_clickedReset(ev):
    display(Javascript("Jupyter.notebook.execute_cells_below()"))

def on_button_clickedFunc(ev):
    eq = sym.sympify(textbox.value)

    if eq==sym.factor(eq):
        display(Markdown('Ulazna funkcija $%s$ je zapisana u faktoriziranom obliku. ' %sym.latex(eq) + 'Njezina proširena verzija je $%s$.' %sym.latex(sym.expand(eq))))

    else:
        display(Markdown('Ulazna funkcija $%s$ je zapisana u proširenom obliku. ' %sym.latex(eq) + 'Njezin faktorizirani oblik je $%s$.' %sym.latex(sym.factor(eq))))

    display(Markdown('Rezultat rastava na parcijalne razlomke: $%s$' %sym.latex(sym.apart(eq)) + '.'))
    display(btnReset)

def transfer_function(num,denom):
    num = np.array(num, dtype=np.float64)
    denom = np.array(denom, dtype=np.float64)
    len_dif = len(denom) - len(num)
    if len_dif<0:
        temp = np.zeros(abs(len_dif))
        denom = np.concatenate((temp, denom))
        transferf = np.vstack((num, denom))
    elif len_dif>0:
        temp = np.zeros(len_dif)
        num = np.concatenate((temp, num))
        transferf = np.vstack((num, denom))
    return transferf

def f(orderNum, orderDenom):
    global text1, text2
    text1=[None]*(int(orderNum)+1)
    text2=[None]*(int(orderDenom)+1)
    display(Markdown('2. Upišite koeficijente za brojnik.'))
    for i in range(orderNum+1):
        text1[i]=widgets.Text(description=(r'a%i'%(orderNum-i)))
        display(text1[i])
    display(Markdown('3. Upišite koeficijente za nazivnik.'))
    for j in range(orderDenom+1):
        text2[j]=widgets.Text(description=(r'b%i'%(orderDenom-j)))
        display(text2[j])
    global orderNum1, orderDenom1
    orderNum1=orderNum
    orderDenom1=orderDenom

def on_button_clickedPoly(btn):
    clear_output()
    global num,denom
    enacbaNum=""
    enacbaDenom=""
    num=[None]*(int(orderNum1)+1)
    denom=[None]*(int(orderDenom1)+1)
    for i in range(int(orderNum1)+1):
        if text1[i].value=='' or text1[i].value=='Upišite koeficijent':
            text1[i].value='Upišite koeficijent'
        else:
            try:
                num[i]=int(text1[i].value)
            except ValueError:
                if text1[i].value!='' or text1[i].value!='Upišite koeficijent':
                    num[i]=sym.var(text1[i].value)

    for i in range (len(num)-1,-1,-1):
        if i==0:
            enacbaNum=enacbaNum+str(num[len(num)-i-1])
        elif i==1:
            enacbaNum=enacbaNum+"+"+str(num[len(num)-i-1])+"*x+"
        elif i==int(len(num)-1):
            enacbaNum=enacbaNum+str(num[0])+"*x**"+str(len(num)-1)
        else:
            enacbaNum=enacbaNum+"+"+str(num[len(num)-i-1])+"*x**"+str(i)

    for j in range(int(orderDenom1)+1):
        if text2[j].value=='' or text2[j].value=='Upišite koeficijent':
            text2[j].value='Upišite koeficijent'
        else:
            try:
                denom[j]=int(text2[j].value)
            except ValueError:
                if text2[j].value!='' or text2[j].value!='Upišite koeficijent':
                    denom[j]=sym.var(text2[j].value)

    for i in range (len(denom)-1,-1,-1):
        if i==0:
            enacbaDenom=enacbaDenom+"+"+str(denom[len(denom)-i-1])
        elif i==1:
            enacbaDenom=enacbaDenom+"+"+str(denom[len(denom)-i-1])+"*x"
        elif i==int(len(denom)-1):
            enacbaDenom=enacbaDenom+str(denom[0])+"*x**"+str(len(denom)-1)
        else:
            enacbaDenom=enacbaDenom+"+"+str(denom[len(denom)-i-1])+"*x**"+str(i)

    funcSym=sym.sympify('('+enacbaNum+')/('+enacbaDenom+')')

    DenomSym=sym.sympify(enacbaDenom)
    NumSym=sym.sympify(enacbaNum)
    DenomSymFact=sym.factor(DenomSym);
    funcFactSym=NumSym/DenomSymFact;

    if DenomSym==sym.expand(enacbaDenom):
        if DenomSym==DenomSymFact:
            display(Markdown('Zadana funkcija je: $%s$. Brojnik se ne može faktorizirati.' %sym.latex(funcSym)))
        else:
            display(Markdown('Zadana funkcija je: $%s$. Brojnik se ne može faktorizirati. Ista funkcija s faktoriziranim nazivnikom može se zapisati kao: $%s$.' %(sym.latex(funcSym), sym.latex(funcFactSym))))

    if sym.apart(funcSym)==funcSym:
        display(Markdown('Nemoguće je napraviti rastav na parcijalne razlomke.'))
    else:
        display(Markdown('Rezultat rastava na parcijalne razlomke je: $%s$' %sym.latex(sym.apart(funcSym)) + '.'))

    btnReset.on_click(on_button_clickedReset)
    display(btnReset)

def partial_frac(index):

    if index==0:
        x = sym.Symbol('x')
        display(widgets.HBox((textbox, btnConfirmFunc)))
        btnConfirmFunc.on_click(on_button_clickedFunc)
        btnReset.on_click(on_button_clickedReset)

    elif index==1:
        display(Markdown('1. Definirajte red brojnika (orderNum) i nazivnika (orderDenom).'))
        widgets.interact(f, orderNum=widgets.IntSlider(min=0,max=10,step=1,value=0),
                 orderDenom=widgets.IntSlider(min=0,max=10,step=1,value=0));
        btnConfirmPoly.on_click(on_button_clickedPoly)
        display(btnConfirmPoly)

input_data=widgets.interactive_output(partial_frac,{'index':typeSelect})
display(input_data)
```

    ToggleButtons(description='Odaberi: ', options=(('Unos funkcije', 0), ('Unos koeficijenata polinoma', 1)), sty…

    Output()


+ This notebook is part of lecture 9 *Independence, basis and dimension* in the OCW MIT course 18.06 by Prof Gilbert Strang [1]
+ Created by me, Dr Juan H Klopper
    + Head of Acute Care Surgery
    + Groote Schuur Hospital
    + University Cape Town
    + Email me with your thoughts, comments, suggestions and corrections

Linear Algebra OCW MIT18.06 IPython notebook [2] study notes by Dr Juan H Klopper is licensed under a Creative Commons Attribution-NonCommercial 4.0 International License.

+ [1] OCW MIT 18.06
+ [2] Fernando Pérez, Brian E. Granger, IPython: A System for Interactive Scientific Computing, Computing in Science and Engineering, vol. 9, no. 3, pp. 21-29, May/June 2007, doi:10.1109/MCSE.2007.53. URL: http://ipython.org

```python
from IPython.core.display import HTML, Image
css_file = 'style.css'
HTML(open(css_file, 'r').read())
```

<link href='http://fonts.googleapis.com/css?family=Alegreya+Sans:100,300,400,500,700,800,900,100italic,300italic,400italic,500italic,700italic,800italic,900italic' rel='stylesheet' type='text/css'>
<link href='http://fonts.googleapis.com/css?family=Arvo:400,700,400italic' rel='stylesheet' type='text/css'>
<link href='http://fonts.googleapis.com/css?family=PT+Mono' rel='stylesheet' type='text/css'>
<link href='http://fonts.googleapis.com/css?family=Shadows+Into+Light' rel='stylesheet' type='text/css'>
<link href='http://fonts.googleapis.com/css?family=Philosopher:400,700,400italic,700italic' rel='stylesheet' type='text/css'>

```python
#import numpy as np
from sympy import init_printing, Matrix, symbols
#import matplotlib.pyplot as plt
#import seaborn as sns
#from IPython.display import Image
from warnings import filterwarnings

init_printing(use_latex = 'mathjax') # Pretty Latex printing to the screen
#%matplotlib inline
filterwarnings('ignore')
```

# Independence
# Spanning
# Basis
# Dimension

## Independence

* Vectors are linearly independent if
    * No combination of these vectors results in the zero vector (except the zero combinations)
$$ { c }_{ 1 }{ x }_{ 1 }+{ c }_{ 2 }{ x }_{ 2 }+\dots +{ c }_{ n }{ x }_{ n }\neq 0,\quad { c }_{ i }\neq 0 $$
    * In 2-space, this means that they should noy be on the same line through the origin
    * In 3-space they should not be on the same line through the origin or on a plane through the origin
    * In higher-dimensional space they should not be on the same line through the origin or a hyperplane through the origin

* If they are independent by the constraints above, only a zero combination of them will results in zero
* If there are vectors in the nullspace (apart from the zero vector), then (there is a linear combination that will give zero and) then the vectors are not linearly independent

```python
A= Matrix([[1, 2, 4], [3, 1, 4]])
A
```

$$\left[\begin{matrix}1 & 2 & 4\\3 & 1 & 4\end{matrix}\right]$$

* Here we will have a rank of 2 (2 pivots) and 3 unknowns and 2 rows
* Thus, *r* = *m* (full row rank)
* We are left with *n* - *r* freen variable, i.e. 3 - 2 = 1 and will have one vector in the nullspace

```python
A.rref()
```

$$\begin{pmatrix}\left[\begin{matrix}1 & 0 & \frac{4}{5}\\0 & 1 & \frac{8}{5}\end{matrix}\right], & \begin{bmatrix}0, & 1\end{bmatrix}\end{pmatrix}$$

```python
A.nullspace()
```

$$\begin{bmatrix}\left[\begin{matrix}- \frac{4}{5}\\- \frac{8}{5}\\1\end{matrix}\right]\end{bmatrix}$$

### Another way to state independence

* Consider the columns of the matrix A as vectors v1, v2, ..., vn

* If *r* = *n* then the nullspace only contains the zero vector and the column vectors are linearly independent

## Spanning

* If we have a set of linearly independent vector that all their linear combinations (including zero) span a subspace ( in this instance a column space)

* We are particularly interested in a set of (column) vectors (in a matrix) that are linearly independent and span a subspace
* This leads us to the next topic, **basis**

## Basis

* A set of vectors (in a space *W*) with the properties
    * They are linearly independent
    * They span the space (linear combinations of them fill the space)

* Up until now we looked at columns in a matrix A
* It is more common in textbooks to look at a space first and ask about basis vectors, spanning vectors, dimension, etc

* So let's look at ℝ3
* The obvious set of basis vectors are
$$ \hat {i}, \quad \hat {j}, \quad \hat {k} $$

* What about
$$ \begin{bmatrix} 1 \\ 1 \\ 2 \end{bmatrix},\quad \begin{bmatrix} 2 \\ 2 \\ 5 \end{bmatrix} $$
* So, are they linearly independent and do they span ℝ3?|

```python
A = Matrix([[1, 2], [1, 2], [2, 5]])
A
```

$$\left[\begin{matrix}1 & 2\\1 & 2\\2 & 5\end{matrix}\right]$$

* Here we will have *r* = 2, *n* = 2 and thus a (*n* - *r* = 0) zero nullspace

```python
A.nullspace()
```

$$\begin{bmatrix}\end{bmatrix}$$

```python
A.rref()
```

$$\begin{pmatrix}\left[\begin{matrix}1 & 0\\0 & 1\\0 & 0\end{matrix}\right], & \begin{bmatrix}0, & 1\end{bmatrix}\end{pmatrix}$$

* For now, our intuition is that they will not span ℝ3
* This intuition is correct, because all their linear combinations will only fill a plane through the origin
* The zero combination does result in the zero vector, though, so they do fill a subspace of ℝ3
* Some textbooks refer to this as *V* = ℝn, with *W* a subspace of *V*

* If we added a column vector that is a linear combination of these, it will also fall in the plane and thus not be linearly independent (there will be a vector in the nullspace other than the zero vector

```python
A = Matrix([[1, 2, 3], [1, 2, 3], [2, 5, 7]])
A.nullspace()
```

$$\begin{bmatrix}\left[\begin{matrix}-1\\-1\\1\end{matrix}\right]\end{bmatrix}$$

```python
A.rref()
```

$$\begin{pmatrix}\left[\begin{matrix}1 & 0 & 1\\0 & 1 & 1\\0 & 0 & 0\end{matrix}\right], & \begin{bmatrix}0, & 1\end{bmatrix}\end{pmatrix}$$

* Indeed, we have a column without a pivot and thus a free variable

* Let's add another, such that we have

```python
A = Matrix([[1, 2, 3], [1, 2, 3], [2, 5, 8]])
A.rref()
```

$$\begin{pmatrix}\left[\begin{matrix}1 & 0 & -1\\0 & 1 & 2\\0 & 0 & 0\end{matrix}\right], & \begin{bmatrix}0, & 1\end{bmatrix}\end{pmatrix}$$

* Again, a column without a pivot and sure enough, we'll find a vector (other than the zero vector) in the nullspace

```python
A.nullspace()
```

$$\begin{bmatrix}\left[\begin{matrix}1\\-2\\1\end{matrix}\right]\end{bmatrix}$$

### The special case of a square matrix

* If we now end up with a square matrix, we need only look at it's determinant, i.e., is it invertible

```python
A.det() # .det() calculates the determinant
```

$$0$$

* Indeed the determinant is zero as expected

## Dimension

* Given a (sub)space, every basis for that (sub)space has the same number of vectors (there are usally more than one basis for every (sub)space)
* This called the dimension of a (sub)space

## Important point to remember

* The (sub)space which a set of (column) vectors (matrix of coefficients, A) span, is the set of possible **b**-values

## More examples

#### Example problem

* Consider the column space

```python
A = Matrix([[1, 2, 3, 1], [1, 1, 2, 1], [1, 2, 3, 1]])
```

```python
A
```

$$\left[\begin{matrix}1 & 2 & 3 & 1\\1 & 1 & 2 & 1\\1 & 2 & 3 & 1\end{matrix}\right]$$

* There are *n* = 4 unknowns, *m* = 3 unknowns
* We note that column 1 = column 4
* We note that with 4 unknowns we are dealing with ℝ4
* In essence, there are at most three independent columns, thus the matrix cannot be a basis for ℝ4
* It is possible for them to span the column space (don't get confused by column space (ℝ4) and matrix here)

```python
A.nullspace()
```

$$\begin{bmatrix}\left[\begin{matrix}-1\\-1\\1\\0\end{matrix}\right], & \left[\begin{matrix}-1\\0\\0\\1\end{matrix}\right]\end{bmatrix}$$

```python
A.rref()
```

$$\begin{pmatrix}\left[\begin{matrix}1 & 0 & 1 & 1\\0 & 1 & 1 & 0\\0 & 0 & 0 & 0\end{matrix}\right], & \begin{bmatrix}0, & 1\end{bmatrix}\end{pmatrix}$$

* As we can see here (columns three and four have free variables, i.e. no pivots)
$$ { x }_{ 1 }+0{ x }_{ 2 }+{ x }_{ 3 }+{ x }_{ 4 }=0\\ 0{ x }_{ 1 }+1{ x }_{ 2 }+{ x }_{ 3 }+{ 0 }_{ 4 }=0\\ { x }_{ 4 }={ c }_{ 2 }\\ { x }_{ 3 }={ c }_{ 1 }\\ \therefore \quad { x }_{ 2 }=-{ c }_{ 1 }\\ \therefore \quad { x }_{ 1 }=-{ c }_{ 1 }-{ c }_{ 2 }\\ \begin{bmatrix} { x }_{ 1 } \\ { x }_{ 2 } \\ { x }_{ 3 } \\ { x }_{ 4 } \end{bmatrix}=\begin{bmatrix} -{ c }_{ 1 }-{ c }_{ 2 } \\ -{ c }_{ 1 } \\ { c }_{ 1 } \\ { c }_{ 2 } \end{bmatrix}=\begin{bmatrix} -{ c }_{ 1 } \\ -{ c }_{ 1 } \\ { c }_{ 1 } \\ 0 \end{bmatrix}+\begin{bmatrix} -{ c }_{ 2 } \\ 0 \\ 0 \\ { c }_{ 2 } \end{bmatrix}={ c }_{ 1 }\begin{bmatrix} -1 \\ -1 \\ 1 \\ 0 \end{bmatrix}+{ c }_{ 2 }\begin{bmatrix} -1 \\ 0 \\ 0 \\ 1 \end{bmatrix} $$

* The rank of the matrix is two (it is the number of pivot columns)
* This matrix space thus have two basis vectors (column vectors 1 and 2) and we say the dimension of this space is two
* Remember, a matrix has a rank, which is the dimension of a column space (the column space representing the space 'produced' by the column vectors)
* We talk about the rank of a matrix, rank(A) and the column space of a matrix, C(A)

* In summary we have two basis above (they span a space)
    * Any two vectors that are not linearly dependent will also span this space, they can't help but to
    * dimC(A)= *r*
    * The nullspace will have *n* - *r* vectors (the dimension of the null space equal the number of free variables)

```python

```


Tài liệu này mang giấy phép Creative Commons Attribution (CC BY). (c) Nguyễn Ngọc Sáng, Zhukovsky 06/2019.

[@SangVn](https://github.com/SangVn) [@VnCFD](https://vncfdgroup.wordpress.com/)

*Thực hành CFD với Python!*

## Phần 3: Hệ phương trình Euler hai chiều, vncfd_2D
`Nếu không đào bới thì còn gì là thú vị!`

Bạn hãy tưởng tượng việc `nghiên cứu CFD` giống như hành trình `đi tìm kho báu`. Đứng trong một khu rừng nhiệt đới rậm rạp, trên bãi biển của một hòn đảo hoang sơ hay đứng trước đại dương rộng lớn, ta tự hỏi không biết nên làm những gì, đi đến đâu... Chúng ta cần những lời chỉ dẫn, cần `một chiếc bản đồ kho báu`. Những bài báo, những quyển sách về lưu chất, phương pháp tính, về CFD chính là tờ bản đồ kho báu đó. Chúng ta đọc, nghiên cứu và đi theo, chúng ta đã tới được `những nơi được đánh dấu` trên bản đồ. Thế nhưng, trong tay ta `không có một tấc sắt`, không có một `chiếc xẻng` để đào bới... `Chương trình CFD` chính là chiếc xẻng để ta khám phá kho báu, đào hay chôn. Mà `nếu không đào bới thì còn gì là thú vị!`. Chúng ta cần rèn một cái xẻng. Nó có thể được rèn bằng Python, bằng C++, bằng FORTRAN, MATLAB... hay một `vật liệu` nào khác. Khi chúng ta đã biết cách rèn xẻng, biết cách sử dụng một dạng vật liệu thì việc chuyển sang rèn bằng một vật liệu khác không quá phức tạp.

Tóm lại là ta cần một **công cụ** để đào sâu nghiên cứu CFD.

Khóa học **Thực hành CFD với Python!** sẽ cho bạn những lời chỉ dẫn, một mảnh của tấm bản đồ và cách rèn xẻng cho hành trình `đi tìm kho báu`.

Chúng ta chỉ mới đi được 2 bước: ở **[phần I](https://github.com/SangVn/CFD_Notebook_P1) và [phần II](https://github.com/SangVn/CFD_Notebook_P2)** ta đã tìm hiểu về phương pháp tính, ứng dụng giải các phương trình, hệ phương trình đặc trưng từ đơn giản tới phức tạp, ta đã dừng lại ở hệ phương trình Euler một chiều. Còn đó hệ phương trình Euler hai chiều, ba chiều; hệ phương trình Navier-Stokes; các mô hình rối Spalart-Allmaras, k-omega, k-epsilon, SST, LES, DNS....

Thực ra 3D so với 2D thì `chỉ là` thêm 1D, Navier-Stokes hay các mô hình rối so với Euler thì chỉ là thêm một biến, hai biến. Tất nhiên trong cái từ `chỉ là` ấy còn nhiều khác biệt, nhưng khi ta đã có nền tảng để phát triển ta có thể làm được nhiều thứ.

Phần 3 `Thực hành CFD với Python!` sẽ xoay quanh việc xây dựng chương trình giải hệ phương trình Euler 2D. Việc chuyển từ 1D sang 2D là một bước đi quan trọng và thú vị. Phần này tập trung giải thích cấu trúc chương trình, cấu trúc dữ liệu, nhiều phần lý thuyết chỉ dừng lại ở việc giới thiệu tài liệu để bạn đọc tự tham khảo. Chương trình sử dụng **phương pháp thể tích hữu hạn; phương pháp tính dòng Godunov, Roe; sơ đồ hiện, bậc một theo thời gian; tái cấu trúc nghiệm bậc một Godunov, lưới có cấu trúc** và được viết bằng ngôn ngữ `python2.7`. Ta cũng sẽ làm quen với phần mềm `ParaView` để biểu diễn và xử lý kết quả. Các bài toán ví dụ bao gồm: **dòng chảy trên âm qua dốc; dòng chảy bao hình trụ, NACA profile và tàu vũ trụ Crew Dragon**.

Để chuẩn bị cho phần 3, các bạn hãy cài đặt và học cách sử dụng **PyCharm** để viết code và **ParaView** để biểu diễn và xử lý dữ liệu CFD.

**Tài liệu tham khảo:**
- Eleuterio F. Toro `Riemann Solvers and Numerical Methods for Fluid Dynamics`
- Randall J. Leveque `Finite-Volume Methods for Hyperbolic Problems`
- H. K. Versteeg, W. Malalasekera `An introduction to Computational Fluid Dynamics. The Finite Volume Method`
- Katake Masatsuka `I do like CFD. Governing Equations and Exact Solutions`
- F. Moukalled, L. Mangani, M. Darwish `The Finite Volume Method in Computational Fluid Dynamics. An Advanced Introduction with OpenFOAM and Matlab`

# Bài 18. Hệ phương trình Euler hai chiều, phương pháp giải

Các kiến thức cơ bản đã được giới thiệu trong phần II, sau đây ta tóm tắt một số điểm cơ bản.

# 1. Hệ phương trình Euler hai chiều (2D)

Đã được giới thiệu trong [bài 16, phần II](https://nbviewer.jupyter.org/github/SangVn/CFD_Notebook_P2/blob/master/Bai_16.ipynb)

Dòng chảy của khí lý tưởng trong hai chiều không gian được mô tả bởi hệ phương trình Euler 2D:
$$\frac{\partial U}{\partial t} + \frac{\partial F}{\partial x} + \frac{\partial G}{\partial y} = 0\qquad(1)$$
với
\begin{align}
U & = \begin{pmatrix} \rho \\ \rho u \\ \rho v \\ \rho e\end{pmatrix}, &
F & = \begin{pmatrix} \rho u \\ \rho u^2 + p \\ \rho u v \\ \rho uh\end{pmatrix} &
G & = \begin{pmatrix} \rho v \\ \rho uv \\ \rho v^2 + p \\ \rho vh\end{pmatrix} &
\end{align}
và $h = e + \frac{p}{\rho}$, $e = \varepsilon + \frac{u^2+v^2}{2}$, $\varepsilon = \frac{p}{(\gamma-1)\rho}$;

trong đó: $\rho$ - khối lượng riêng; $u, v$ - vận tốc theo phương x, y; $p$ - áp suất; $e$ - năng lượng một đơn vị khối lượng chất khí; $\varepsilon$ - nội năng; $h$ - enthalpy.

# 2. Phương pháp thể tích hữu hạn

Đã được giới thiệu trong [bài 11, phần II](https://nbviewer.jupyter.org/github/SangVn/CFD_Notebook_P2/blob/master/Bai_11.ipynb)

Xét `thể tích hữu hạn` ABCD (hình phải) không nằm trên biên, có các `bề mặt` AB, BC, CD, DA; lấy tích phân phương trình (1):

$$\int_{ABCD} \left(\frac{\partial U}{\partial t} + \frac{\partial F}{\partial x} + \frac{\partial G}{\partial y} \right)dxdy = 0$$

áp dụng định lý Green ta thu được phương trình dạng tích phân:
$$\frac{d}{dt}\int U dV + \oint_{ABCD} \vec F. \vec n ds = 0 \qquad (2)$$
với $\vec F = (F, G)$, $\vec n$ - vector pháp tuyến đơn vị, $\vec n ds = (dy, -dx)$.

Phương trình (2) mô tả định luật `bảo toàn` khối lượng, động lượng và năng lượng. Nó gồm hai thành phần tương ứng sự biến đổi theo thời gian và dòng đi qua các bề mặt.

### Sơ đồ sai phân
Sơ đồ sai phân của phương trình (2) với xấp xỉ thời gian bậc một có dạng:
$$\frac{U^{n+1} - U^n}{\Delta t} + \frac{1}{V_{ABCD}} (\vec F.\vec S_{AB} + \vec F.\vec S_{BC} + \vec F.\vec S_{CD} + \vec F.\vec S_{DA}) = 0 \qquad (3)$$
với:

$U^n = \frac{1}{V_{ABCD}}\int U dV$ - giá trị trung bình của U trong thể tích đang xét tại thời điểm $t$, $V_{ABCD}$ - thể tích hữu hạn (trường hợp 2D - diện tích ABCD);

$\vec S = \vec n . S$ - vector pháp tuyến bề mặt S, có độ lớn bằng diện tích bề mặt (2D - độ dài AB, BC, CD, DA).

Ta có công thức xác định $U^{n+1}$ tại thời điểm $t+\Delta t$:

$$U^{n+1} = U^n + \frac{\Delta t}{V_{ABCD}}\sum {\vec F.\vec S_n} \qquad (4)$$
với $\vec S_n = -\vec n . S = -(nx, ny).S$

### Công thức tính dòng qua bề mặt
Công thức tính dòng qua một đơn vị diện tích bề mặt:
\begin{align}
Flux = \vec F.(\vec n) = (F, G).(nx,ny) & = \begin{pmatrix} \rho(un_x+vn_y) \\ \rho u(un_x+vn_y) + pn_x \\ \rho v(un_x+vn_y) + pn_y \\ \rho h(un_x+vn_y)\end{pmatrix}, &
\end{align}

Trong đó $un_x+vn_y = V_n$ - vận tốc của dòng chảy theo phương vuông góc với bề mặt. Ta có:

\begin{align}
Flux & = \begin{pmatrix} \rho V_n \\ \rho uV_n + pn_x \\ \rho vV_n + pn_y \\ \rho hV_n\end{pmatrix}, &
\end{align}

## Các điểm chú ý

So với phương pháp giải hệ phương trình Euler 1D ở phần 2, khi giải hệ trong không gian hai chiều (2D) chúng ta sẽ cần phải quan tâm kỹ tới các vấn đề: đặc tính hình học; lưới; cấu trúc dữ liệu; điều kiện biên; biểu diễn, xử lý kết quả.

Ta sẽ bắt đầu thực hành với việc tạo lưới cho bài toán dòng chảy trên âm ở bài sau.

# [Bài 19. Dựng lưới có cấu trúc ](Bai_19.ipynb)


There is an infinte amount of resources out there, for instance [here](https://www.dataquest.io/blog/jupyter-notebook-tips-tricks-shortcuts/).

# Notebook intro

## navigating the notebook

There are three types of cells:
1. input cells - contain the actual code
2. output cell - display the results of the computation
3. markdown cells - provide documentation and instructions

The Jupyter Notebook has two different keyboard input modes.
1. Edit mode allows you to type code or text into a cell and is indicated by a green cell border.
2. Command mode binds the keyboard to notebook level commands and is indicated by a grey cell border with a blue left margin.

Command mode is activated by hitting `ESC`
You can swtich back to edit mode by hitting `ENTER`

Some useful shortcuts are
- `ESC`+`dd` - delete cell
- `ESC`+`a` - add cell above
- `ESC`+`b` - add cell below
- `ESC`+`l` - toggle line numbers
- `SHIFT`+`ENTER` - execute cell
- `ENTER` - enter edit mode

To get more help, open the shortcut by hitting `ESC` followed by `h`

## imports, packages and magic commands

In almost any case you will use existing packages. A common good practice is to load them at the beginning of the notebook using the `import` command.

```python
import numpy as np # widely used python library for data manipulation, the 'as' allows you to rename the package on import
from scipy import constants  # this is how you just get specific subpackages or functions
import sys # get some information about the current OS
```

```python
sys.version # show the python version
```

    '3.7.3 | packaged by conda-forge | (default, Mar 27 2019, 23:01:00) \n[GCC 7.3.0]'

```python
sys.executable  # show the path to the python executable - very useful to check when things don't work as expected!
```

    '/home/jgieseler/anaconda3/bin/python'

[magic commands](https://ipython.readthedocs.io/en/stable/interactive/magics.html) are special commands that give some extended functionality to the notebook.

```python
# show figures inline in the notebook
%matplotlib inline

# the following command reloads external packages that have been changed externally without the need to restart the kernel
%load_ext autoreload
%autoreload 2
```

This will list all magic commands

```python
%lsmagic
```

    Available line magics:
    %aimport  %alias  %alias_magic  %autoawait  %autocall  %automagic  %autoreload  %autosave  %bookmark  %cat  %cd  %clear  %colors  %conda  %config  %connect_info  %cp  %debug  %dhist  %dirs  %doctest_mode  %ed  %edit  %env  %gui  %hist  %history  %killbgscripts  %ldir  %less  %lf  %lk  %ll  %load  %load_ext  %loadpy  %logoff  %logon  %logstart  %logstate  %logstop  %ls  %lsmagic  %lx  %macro  %magic  %man  %matplotlib  %mkdir  %more  %mv  %notebook  %page  %pastebin  %pdb  %pdef  %pdoc  %pfile  %pinfo  %pinfo2  %pip  %popd  %pprint  %precision  %prun  %psearch  %psource  %pushd  %pwd  %pycat  %pylab  %qtconsole  %quickref  %recall  %rehashx  %reload_ext  %rep  %rerun  %reset  %reset_selective  %rm  %rmdir  %run  %save  %sc  %set_env  %store  %sx  %system  %tb  %time  %timeit  %unalias  %unload_ext  %who  %who_ls  %whos  %xdel  %xmode

    Available cell magics:
    %%!  %%HTML  %%SVG  %%bash  %%capture  %%debug  %%file  %%html  %%javascript  %%js  %%latex  %%markdown  %%perl  %%prun  %%pypy  %%python  %%python2  %%python3  %%ruby  %%script  %%sh  %%svg  %%sx  %%system  %%time  %%timeit  %%writefile

    Automagic is ON, % prefix IS NOT needed for line magics.

```python
a = 'this is a string'
b = 1 # this is an integer

# list variables
%who str
%who int
```

    a
    b

The `!` allows you to execute shell commands directly from the notebook. You can also [exectute different kernels](https://www.dataquest.io/blog/jupyter-notebook-tips-tricks-shortcuts/) in a single notebook!

```python
! ls ../../images/nbextensions.png
```

    ../../images/nbextensions.png

## extensions

You can install notebook extensions by

If everything is installed, you should get the following tab

One extension that I find particularly usefull is the Table of contents, that provides a TOC based on the titles and subtitles in the notebook.

## Latex, Markdown and HTML

You can write beatiful notebooks using LaTex and [Markdown](https://www.markdownguide.org/cheat-sheet/). Just open any of the cells in this notebook to see the underlying markdown code.

Latex is rendered as expected, for example: $\alpha$.

You can also have inline equations:

$$
\alpha = \int_0^\infty sin(x) dx
$$

and numbered equations

\begin{align}
\alpha &= \int_0^\infty \sin(x) dx \\
\beta &= \frac{\partial}{\partial y} \cos(y)
\end{align}

To navigate the notebook you can also create internal links within the notebook using regular HTML code:

- `<a href='#my_label'>some text</a>` - links to `#my_label`
- `<a id=my_label></a>` - defines the target of the link.

For examle, this link brings you back to the beginning of the notebook.
Note that links "don't work" when the cell is still in edit mode!

Note: HTML also allows you to add some color to you notebooks.

# Python intro

There are more the enough resources on the internet. Here, I will just remind some specifically pythonic code snippets that I find useful.

## functions

Are defined as follows. You can also create classes and more complicated objects.

```python
def simple_function(my_number, my_string='there is nothing'):
    """
    This is the doctring. It contains the documentation of the functions. You can access it with `?`

    """

    return my_string + str(my_number)
```

```python
?simple_function
```

```python
# a short version of writing the above is using a lambda function
simple_function = lambda my_number, my_string='there is nothing' : my_string + str(my_number)
```

## dictionaries

Are very usefull for writing huma friendly code:

```python
simple_dict = {'my_number': 1, 'my_string': 'hello'}

# an alternative way to define the same dictionary (this one is usefull when you want to turn a bunch of definitions into a dict)
simple_dict = dict(
    my_number = 1,
    my_string = 'hello'
)

simple_dict
```

    {'my_number': 1, 'my_string': 'hello'}

you can use dicts to pass many arguments in a compact way

```python
# this unpacks the dictionary, the two stars mean that the unpacking is as key:value
# check what happens if you only have one star
simple_function(**simple_dict)
```

    'hello1'

## loops

```python
for k,v in simple_dict.items():
    print('this is the value:',v, ' and this is the key:', k)
```

    this is the value: 1  and this is the key: my_number
    this is the value: hello  and this is the key: my_string

```python
# zip allows you to bundle different data quickly together
for k,v in zip(['a', 'b'], [1,2]):
    print('this is the value:',v, ' and this is the key:', k)
```

    this is the value: 1  and this is the key: a
    this is the value: 2  and this is the key: b

```python
{k:v for k,v in zip(['a', 'b'], [1,2])}  # you can also loops to create dictionaries
```

    {'a': 1, 'b': 2}

## paths

python provides a convinient pathlib library

```python
from pathlib import Path  # working with path objects - usefull for OS independent code
```

```python
image_path = Path('../../images/') # define the path - usefull to define a global path at the beginning of the notebook
```

```python
[f for f in image_path.glob('*')] # glob allows you to search the path
```

    [PosixPath('../../images/MC.jpg'),
     PosixPath('../../images/motivation.svg'),
     PosixPath('../../images/nbextensions.png'),
     PosixPath('../../images/motivation.png'),
     PosixPath('../../images/distro-01-1.png'),
     PosixPath('../../images/MC.png'),
     PosixPath('../../images/PyCharm-Github.png')]

```python
image_path/'motivation.svg' # appending to a path
```

    PosixPath('../../images/motivation.svg')

```python
image_path.name # f
```

    'images'

```python
image_path.exists() # check if file exists
```

    True

```python
image_path.is_dir()  # check if is directory
```

    True

```python
(image_path/'motivation.svg').exists() # check if file exists
```

    True

```python
(image_path/'motivation.svg').is_dir()
```

    False

```python
image_path.absolute()  # get the absolute path
```

    PosixPath('/home/jgieseler/PycharmProjects/edaipynb/edaipynb/notebooks/../../images')

```python
image_path.parent  # get the parent
```

    PosixPath('../..')

## strings

you can easily format string useing the format function

```python
'this is a bare string, that takes a float here: {:0.3f}'.format(0.2)
```

    'this is a bare string, that takes a float here: 0.200'

```python
s = 'thies is ...'
s.replace('thies', 'this')
```

    'this is ...'

```python
s.split(' ') # breaking up string creates a list
```

    ['thies', 'is', '...']

```python
# this is usefull to encode and decode information into filenames

filename = 'run_{:02d}_pressure_{:0.2e}_frequency_{:0.2f}Hz'.format(1, 1.2e-4, 102024)

print(filename)
```

    run_01_pressure_1.20e-04_frequency_102024.00Hz

```python
# now we extract the information
run = int(filename.split('_')[1])
pressure = float(filename.split('_')[3])
frequency = float(filename.split('_')[5].split('Hz')[0])

run, pressure, frequency
```

    (1, 0.00012, 102024.0)

# save notebook as html

```python
from edaipynb import save_notebook_as_html
save_notebook_as_html('../../html')
```

    ../../html/1) Notebook_and_Python_1-0-1.html saved

```python

```