```python
%matplotlib inline
```

**Read Later:**

Document about ``autograd.Function`` is at
https://pytorch.org/docs/stable/autograd.html#function

**Notes:**

needs to set requires_grad == True when define the tensor if you wanna autograd.

assume z = f(x,y)

z.backward() initiates the backward pass and computed all the gradient automatically.

the gradient will be accumulated into .grad attribute, that means x.grad will have dz/dx and y.grad will have dz/dy.

if z is a scalar, then can use z.backward() and no argument needs to be passed.

if z is a vector, then we have to pass a gradient argument into z.backward(), so if we wanna dz/dx and dz/dy
it should be: z.backward(1). the argument can be any number and should have the same size as z

Autograd: Automatic Differentiation
===================================

Central to all neural networks in PyTorch is the ``autograd`` package.
Let’s first briefly visit this, and we will then go to training our
first neural network.

The ``autograd`` package provides automatic differentiation for all operations
on Tensors. It is a define-by-run framework, which means that your backprop is
defined by how your code is run, and that every single iteration can be
different.

Let us see this in more simple terms with some examples.

Tensor
--------

``torch.Tensor`` is the central class of the package. If you set its attribute
``.requires_grad`` as ``True``, it starts to track all operations on it. When
you finish your computation you can call ``.backward()`` and have all the
gradients computed automatically. The gradient for this tensor will be
accumulated into ``.grad`` attribute.

To stop a tensor from tracking history, you can call ``.detach()`` to detach
it from the computation history, and to prevent future computation from being
tracked.

To prevent tracking history (and using memory), you can also wrap the code block
in ``with torch.no_grad():``. This can be particularly helpful when evaluating a
model because the model may have trainable parameters with
``requires_grad=True``, but for which we don't need the gradients.

There’s one more class which is very important for autograd
implementation - a ``Function``.

``Tensor`` and ``Function`` are interconnected and build up an acyclic
graph, that encodes a complete history of computation. Each tensor has
a ``.grad_fn`` attribute that references a ``Function`` that has created
the ``Tensor`` (except for Tensors created by the user - their
``grad_fn is None``).

If you want to compute the derivatives, you can call ``.backward()`` on
a ``Tensor``. If ``Tensor`` is a scalar (i.e. it holds a one element
data), you don’t need to specify any arguments to ``backward()``,
however if it has more elements, you need to specify a ``gradient``
argument that is a tensor of matching shape.

```python
import torch
```

Create a tensor and set ``requires_grad=True`` to track computation with it

```python
x = torch.tensor([[1,3],[2,2]], requires_grad=True, dtype = torch.float32)
print(x)
print(x.size())
```

    tensor([[1., 3.],
            [2., 2.]], requires_grad=True)
    torch.Size([2, 2])

Do a tensor operation:

```python
y = ( x + 2)
z = 3 * (y ** 2)
print(y,z)
```

    tensor([[3., 5.],
            [4., 4.]], grad_fn=<AddBackward0>) tensor([[27., 75.],
            [48., 48.]], grad_fn=<MulBackward0>)

``y`` was created as a result of an operation, so it has a ``grad_fn``.

```python
print(y.grad_fn)
print(zz.grad_fn)
```

    <AddBackward0 object at 0x7fef96385520>
    <MmBackward object at 0x7fef96385820>

Do more operations on ``y``

```python
z = y * y # dot product
out = z.mean()
print(z, out)
```

    tensor([[ 9., 25.],
            [16., 16.]], grad_fn=<MulBackward0>) tensor(16.5000, grad_fn=<MeanBackward0>)

``.requires_grad_( ... )`` changes an existing Tensor's ``requires_grad``
flag in-place. The input flag defaults to ``False`` if not given.

```python
a = torch.tensor([2, 2],dtype = torch.float32)
a = ((a * 3))
print(a)
print(a.requires_grad)
a.requires_grad_(True)
print(a.requires_grad)
b = (a * a).sum()
print(b)
print(b.grad_fn)
```

    tensor([6., 6.])
    False
    True
    tensor(72., grad_fn=<SumBackward0>)
    <SumBackward0 object at 0x7fef94c668b0>

 Get Gradients
---------
Let's backprop now.
Because ``out`` contains a single scalar, ``out.backward()`` is
equivalent to ``out.backward(torch.tensor(1.))``.

Note:
out.backward() only works for scale

out.backward(v) where v is a vector works for non-scale input, it gives Jacobian-vector product. here v is the gradient we have to put into the backwass pass.

```python
# example: y = 2 * x + 3 * (x^2)
x = torch.tensor([3],dtype = torch.float32,requires_grad = False)
x.requires_grad_(True)
y = 2 * x + x * x  * 3
print(x,y)
```

    tensor([3.], requires_grad=True) tensor([33.], grad_fn=<AddBackward0>)

Print gradients d(y)/dx, which should be y =  2 + 6 * x

```python
y.backward() # first run y.backward(), then x.grad has the value
print(x.grad) # only works when x is scalar
```

    tensor([20.])

Mathematically, if you have a vector valued function $\vec{y}=f(\vec{x})$,
then the gradient of $\vec{y}$ with respect to $\vec{x}$
is a Jacobian matrix:

\begin{align}J=\left(\begin{array}{ccc}
   \frac{\partial y_{1}}{\partial x_{1}} & \cdots & \frac{\partial y_{1}}{\partial x_{n}}\\
   \vdots & \ddots & \vdots\\
   \frac{\partial y_{m}}{\partial x_{1}} & \cdots & \frac{\partial y_{m}}{\partial x_{n}}
   \end{array}\right)\end{align}

Generally speaking, ``torch.autograd`` is an engine for computing
vector-Jacobian product. That is, given any vector
$v=\left(\begin{array}{cccc} v_{1} & v_{2} & \cdots & v_{m}\end{array}\right)^{T}$,
compute the product $v^{T}\cdot J$. If $v$ happens to be
the gradient of a scalar function $l=g\left(\vec{y}\right)$,
that is,
$v=\left(\begin{array}{ccc}\frac{\partial l}{\partial y_{1}} & \cdots & \frac{\partial l}{\partial y_{m}}\end{array}\right)^{T}$,
then by the chain rule, the vector-Jacobian product would be the
gradient of $l$ with respect to $\vec{x}$:

\begin{align}J^{T}\cdot v=\left(\begin{array}{ccc}
   \frac{\partial y_{1}}{\partial x_{1}} & \cdots & \frac{\partial y_{m}}{\partial x_{1}}\\
   \vdots & \ddots & \vdots\\
   \frac{\partial y_{1}}{\partial x_{n}} & \cdots & \frac{\partial y_{m}}{\partial x_{n}}
   \end{array}\right)\left(\begin{array}{c}
   \frac{\partial l}{\partial y_{1}}\\
   \vdots\\
   \frac{\partial l}{\partial y_{m}}
   \end{array}\right)=\left(\begin{array}{c}
   \frac{\partial l}{\partial x_{1}}\\
   \vdots\\
   \frac{\partial l}{\partial x_{n}}
   \end{array}\right)\end{align}

(Note that $v^{T}\cdot J$ gives a row vector which can be
treated as a column vector by taking $J^{T}\cdot v$.)

This characteristic of vector-Jacobian product makes it very
convenient to feed external gradients into a model that has
non-scalar output.

Note:

# vector-Jacobian product is matrix dot product!!!

Now let's take a look at an example of vector-Jacobian product:

```python
# example 1:
x = torch.tensor([3,4,6], dtype = torch.float32,requires_grad=True)
y = x * x * 3
v = torch.tensor([1,1,2], dtype=torch.float)
y.backward(v)                                    # here it does the derivative calulation dyi/dxi = 6 * xi * v
print(x.grad)
```

    tensor([18., 24., 72.])

```python
# example 2:
g = torch.tensor([[1,1,1],[2,3,4]],dtype = torch.float32)
g.add_(torch.ones_like(g))
g.requires_grad_(True)         # _ here means replace the variable with new value
print(g, g.requires_grad)
gg = g*g*g
ggg = gg +3*g
v = torch.tensor([[1,1,2],[2,2,2]], dtype=torch.float)
ggg.backward(v)               # here we do the derivative calculation dggg/dg = 3g^2 + 3
print(g.grad)
```

    tensor([[2., 2., 2.],
            [3., 4., 5.]], requires_grad=True) True
    tensor([[ 15.,  15.,  30.],
            [ 60., 102., 156.]])

in the above case if we do gg.backward(v), then the derivative is 3g^2 only

```python
x = torch.randn(3, requires_grad=True)
print(x)
y = x * 2
count = 0
while y.data.norm() < 100:    # L2 norm / Euclidean norm
    y = y * 2
    count += 1

print(y,count)

# Now in this case ``y`` is no longer a scalar. ``torch.autograd``
# could not compute the full Jacobian directly, but if we just
# want the vector-Jacobian product, simply pass the vector to
# ``backward`` as argument:

v = torch.tensor([0.1, 1.0, 0.0001], dtype=torch.float)
y.backward(v)

print(x.grad)
```

    tensor([ 0.7491,  1.0516, -0.0123], requires_grad=True)
    tensor([ 95.8882, 134.6020,  -1.5742], grad_fn=<MulBackward0>) 6
    tensor([1.2800e+01, 1.2800e+02, 1.2800e-02])

# Stop autograd
You can also stop autograd from tracking history on Tensors
with ``.requires_grad=True`` either by wrapping the code block in
``with torch.no_grad():``

```python
x = torch.randn(3, requires_grad=True)
print(x.requires_grad)
print((x ** 2).requires_grad)

with torch.no_grad():
	print((x ** 2).requires_grad)

x = torch.randn(3, requires_grad=True)
x.requires_grad_(False)
print((x**2).requires_grad)
```

    True
    True
    False
    False

Or by using ``.detach()`` to get a new Tensor with the same
content but that does not require gradients:

```python
x.requires_grad_(True)
print(x.requires_grad)
y = x.detach()
print(y.requires_grad)
print(x.eq(y))
```

    True
    False
    tensor([True, True, True])


 the stars module : interactive star marking, aperture photometry, WCS, plus photometric transformations

Start with the usual setup

```python
%matplotlib qt
import matplotlib.pyplot as plt
import numpy as np
import glob
import pdb
from pyvista import imred, tv, stars
```

    pyautogui does not seem to be available, disabling arrow key cursor moves

Instantiate a reducer object, and a TV object

```python
red=imred.Reducer('TM61',dir='/home/tmo/20211020')
t=tv.TV()
```

    INSTRUMENT: TM61   config:
      will use format:  /home/tmo/20211020/* {:d}.fits*
             gain:  [0.8]    rn: [10.]
             scale:  0.44
      Biastype : 0
      Bias box:
        SC    NC    SR    NR
       100  4601  3195    11
      Trim box:
        SC    NC    SR    NR
         0  4800     0  3194
      Norm box:
        SC    NC    SR    NR
      1200   801  2000   801

```python
t.tvclear()
a=red.reduce(851)
```

      Reading file: /home/tmo/20211020/CCD Image 851.fit
      subtracting overscan:  509.1276007192112

```python
t.tv(a)
red.scale
```

    0.44

Interactive marking of stars with stars.mark(). Note that photutils has routines for automatic detection of stars.

```python
seeing=3
tab=stars.mark(t,rad=seeing/red.scale,new=True)
```

    Hit c near desired star(s) to get centroid position
        i to use integer position of cursor
        q or e to quit

```python
print(tab)
```

     id    x       y        MJD      EXPTIME FILTER  AIRMASS
    --- ------- ------- ------------ ------- ------ ---------
      1 3344.90 1448.30 59507.536177    10.0     SR 1.0339353
      2 1996.00 1514.28 59507.536177    10.0     SR 1.0339353

Note that if you provide an input table to stars.mark(), along with auto=True, it will re-centroid around position in table

Aperture photometry with stars.photom (front end for photutils). This will do aperture photometry on image specified in first argument, for objects specified in the second argument ( a Table). Will do multiple aperture radii as specified with rad= kekyword, using sky annulus as specified by skyrad= keyword.  If display= is given, apertures and annuli will be displayed on the image.

Returns the table with columns added for aperture photometry, uncertainties and sky values. Magnitudes are instrumental magnitudes, not corrected for exposure time, i.e. -2.5 log (counts)

```python
stars.photom(a,tab,rad=np.arange(3,10),skyrad=[20,30],display=t)
```

Table length=2

id	x	y	MJD	EXPTIME	FILTER	AIRMASS	aper3.0	aper3.0err	aper4.0	aper4.0err	aper5.0	aper5.0err	aper6.0	aper6.0err	aper7.0	aper7.0err	aper8.0	aper8.0err	aper9.0	aper9.0err	sky	skysig

int32	float32	float32	float64	float32	bytes32	float32	float64	float64	float64	float64	float64	float64	float64	float64	float64	float64	float64	float64	float64	float64	float64	float64

1	3344.90	1448.30	59507.536177	10.0	SR	1.0339353	-14.145	0.002	-14.525	0.002	-14.763	0.002	-14.916	0.002	-15.014	0.002	-15.079	0.002	-15.123	0.002	12024.45	125.17

2	1996.00	1514.28	59507.536177	10.0	SR	1.0339353	-13.172	0.005	-13.555	0.004	-13.769	0.004	-13.883	0.005	-13.941	0.005	-13.974	0.005	-13.997	0.006	12960.36	129.39

```python
print(tab)
```

     id    x       y        MJD      EXPTIME ... aper9.0 aper9.0err   sky    skysig
    --- ------- ------- ------------ ------- ... ------- ---------- -------- ------
      1 3344.90 1448.30 59507.536177    10.0 ... -15.123      0.002 12024.45 125.17
      2 1996.00 1514.28 59507.536177    10.0 ... -13.997      0.006 12960.36 129.39

 World coordinate system (WCS)

With solve=True, Reducer will try to solve for a WCS using a local instllation of astrometry.net

Example for autofinding stars on a TMO image. Here's an example of using a master file with a set of standard stars, using WCS to get pixel positions and selecting only those stars on the current frame.

```python
#reduce an image and add WCS using astrometry.net routines
# note that I haven't even included flat fielding! so results will be poor
red.dir=('/home/tmo/20211116')
a=red.reduce('SA112-S001-SR-R001-SR.fts',solve=True)

# ugriz_smith file has positions and standard mags
tab=Table.read('ugriz_smith.fits')

# populate x and y columns with pixel locations in this image
tab['x'],tab['y']=a.wcs.wcs_world2pix(tab['RA'],tab['DEC'],0)
#table formatting
for col in ['x','y','u','g','r','i','z'] : tab[col].info.format='.2f'
for col in ['RA','DEC'] : tab[col].info.format='.6f'

# select out stars that fall on the image
nrow,ncol=a.data.shape
gd=np.where((tab['x']>0)&(tab['y']>0)&(tab['x']<ncol)&(tab['y']<nrow))[0]
tab=tab[gd]

# display them
t.tvclear()
t.tv(a)
for xx,yy in zip(tab['x'],tab['y']) :
    t.tvcirc(xx,yy,20)

# show the table
tab
```

      Reading file: /home/tmo/20211116/SA112-S001-SR-R001-SR.fts
      subtracting overscan:  512.8631917962498
      plate solving with local astrometry.net....

    /home/local/Anaconda3-2020.02/lib/python3.7/site-packages/numpy/core/fromnumeric.py:746: UserWarning: Warning: 'partition' will ignore the 'mask' of the MaskedArray.
      a.partition(kth, axis=axis, kind=kind, order=order)

    /usr/local/astrometry/bin/solve-field --scale-units arcsecperpix --scale-low 0.396000 --scale-high 0.484000 -X xcentroid -Y ycentroid -w 4800 -e 3000 --overwrite --ra 310.708333 --dec 0.259444 --radius 3 tmpllazxfu9xy.fits

    /home/local/Anaconda3-2020.02/lib/python3.7/site-packages/astropy/table/column.py:1020: RuntimeWarning: invalid value encountered in greater
      result = getattr(super(), op)(other)
    /home/local/Anaconda3-2020.02/lib/python3.7/site-packages/astropy/table/column.py:1020: RuntimeWarning: invalid value encountered in less
      result = getattr(super(), op)(other)

Table length=4

NAME	RA	DEC	u	g	r	i	z	ID	x	y

bytes11	float64	float64	float64	float64	float64	float64	float64	int64	float64	float64

SA 112 223	310.560750	0.149917	12.75	11.61	11.34	11.27	11.27	130	917.49	498.85

SA 112 250	310.609917	0.128444	13.48	12.31	11.96	11.85	11.84	131	1338.67	358.59

SA 112 805	310.694750	0.269000	13.27	12.09	12.17	12.31	12.40	132	1934.67	1579.06

SA 112 822	310.728750	0.250528	14.32	12.03	11.23	10.94	10.79	133	2228.58	1452.23

This has the nice advantage that you can just add the photometry onto this table and facilitate getting the photometric transformations!

Now we use the x,y positions from the WCS, recentroid using mark() with the auto=True option (exit=True means it doesn't then wait for you to mark additional stars), and then do the aperture photometry.

```python
t.tv(a)
phot=stars.mark(t,tab,rad=3/red.scale,auto=True,exit=True)
stars.photom(a,phot,rad=[10],skyrad=[20,30],display=t)

```

    /home/local/Anaconda3-2020.02/lib/python3.7/site-packages/astropy/table/table.py:3064: FutureWarning: elementwise == comparison failed and returning scalar instead; this will raise an error or perform elementwise comparison in the future.
      result = self.as_array() == other

    possible centroiding convergence issues, consider using a larger radius?

Table length=4

NAME	RA	DEC	u	g	r	i	z	ID	x	y	EXPTIME	FILTER	AIRMASS	MJD	aper10	aper10err	sky	skysig

bytes11	float64	float64	float64	float64	float64	float64	float64	int64	float64	float64	float64	str2	float64	float64	float64	float64	float64	float64

SA 112 223	310.560750	0.149917	12.75	11.61	11.34	11.27	11.27	130	920.11	497.64	10.0	SR	1.358	59534.089396	-12.807	0.004	57.34	12.70

SA 112 250	310.609917	0.128444	13.48	12.31	11.96	11.85	11.84	131	1341.14	357.65	10.0	SR	1.358	59534.089396	-12.234	0.006	58.40	12.73

SA 112 805	310.694750	0.269000	13.27	12.09	12.17	12.31	12.40	132	1937.25	1579.73	10.0	SR	1.358	59534.089396	-12.089	0.006	66.75	13.28

SA 112 822	310.728750	0.250528	14.32	12.03	11.23	10.94	10.79	133	2230.57	1452.49	10.0	SR	1.358	59534.089396	-13.006	0.004	67.81	13.05

Note that the photom() routine does not include the exposure time in the instrumental magnitudes.

Photometric transformations

The general transformation equations follow:
$$g = SG + k_g X + t_g(g-r) + z_g$$
$$r = SR + k_r X + t_r(g-r) + z_r$$
$$...$$
where there would be an equation for every filter. For each filter, we need to associate the standard magnitude to which we want to transform (e.g., SG to g, SR to r, etc.), and also associate a standard color to use for the color term, which is typically a color index between the magnitude that we are transforming to, and an adjacent one (e.g., g-r for both g and r above).

Using standard stars, we solve for the extinction coefficient, the transformation coefficient, and the zero point for each filter. Generally, this is set up as a linear least squares problem for each filter:

\begin{equation}
\begin{pmatrix}
X_1 & color_1 & 1 \\
X_2 & color_2 & 1 \\
... & ... & ... \\
\end{pmatrix}
\begin{pmatrix}
k_g\\
t_g\\
z_g\\
\end{pmatrix}
=
\begin{pmatrix}
g-SG_1\\
g-SG_2\\
...\\
\end{pmatrix}
\end{equation}
where there is a row for every observation in the filter.

```python
%matplotlib inline
from tools import plots
filts=['SR']        #observed filter names
mags=['r']          # standard mag to transform to
colors=[['g','r']]  # pair for each filter
aper='aper10'
for filt,mag,color in zip(filts,mags,colors) :
    j=np.where(tab['FILTER'] == filt)[0]
    mjd=tab['MJD'][j]
    airmass=tab['AIRMASS'][j]
    exptime=tab['EXPTIME'][j]
    col=tab[color[0]][j]-tab[color[1]][j]
    zero=np.ones(len(j))
    # include the EXPTIME correction for instrumental mag
    inst=tab[aper]+2.5*np.log10(exptime)
    stan=tab[mag]
    # right hand side is standard mag - instrumental mag
    rhs=stan-inst
    # left hand side matrix is the airmass,color,1 for each observation
    # for my example, I don't have multiple airmass, so just including 2 terms
    #mat=np.array([airmass,color,zero]).T
    mat=np.array([col,zero]).T

    #plot difference between standard and observe a function of things
    #plots (from holtz-tools) contains convenience routines for making and labelling plots
    fig,ax=plots.multi(2,2,figsize=(8,5))
    # plotc plots points at x,y positions, colored by z
    colname=color[0]+'-'+color[1]
    plots.plotc(ax[0,0],airmass,rhs,col,
                xt='airmass',yt=mag+'-'+filt,zt=colname,colorbar=True,size=20)
    plots.plotc(ax[0,1],col,rhs,airmass,
                xt='color',yt=mag+'-'+filt,zt='airmass',colorbar=True,size=20)
    plots.plotc(ax[1,0],mjd,rhs,airmass,
                xt='MJD',yt=mag+'-'+filt,zt='airmass',colorbar=True,size=20)
    fig.tight_layout()

    # do the fit for the coefficients
    fit,resid,rank,s=np.linalg.lstsq(mat,rhs)
    print('fit: ',fit)
    print('resid ',resid)

    # now use the solution to get transformed magnitudes from the obs
    trans=np.matmul(mat,fit)+obs
    plots.plotc(ax[1,1],mjd,stan-trans,airmass,
                xt='MJD',yt='stan-trans',zt='airmass',colorbar=True,size=20)
```

You could now use the transformation fit to transform any instrumental magnitudes to the standard system....

```python

```


```python
%%%HTML

<form action="javascript:code_toggle()"><input type="submit" id="toggleButton" value="Show Code"></form>
```

<form action="javascript:code_toggle()"><input type="submit" id="toggleButton" value="Show Code"></form>

```python
# MECA654, March 2018, Pierre Nagorny
# Polytech Annecy-Chambéry, SYMME lab, Savoie Mont Blanc University

# HINTS: Check the nice tuto about vector calculus !
# http://docs.sympy.org/latest/modules/physics/vector/vectors.html

from IPython.core.interactiveshell import InteractiveShell
from IPython.display import display, Math, YouTubeVideo
import numpy as np
import matplotlib as mpl
from matplotlib import pyplot as plt  # Loading Pyplot
from scipy.interpolate import interp1d
# Setting Matplotlib render backend
%matplotlib notebook
# %matplotlib nbagg
# %pylab inline
from sympy import *  # Loading Sympy for symbolic calculus
from sympy.physics.vector import *  # Everything needed for vector calculs, Reference frame, dynamic
from sympy.physics.vector import init_vprinting  # To print time varying vectors

init_printing()
init_vprinting(pretty_print=True)

# Display full output in Jupyter Notebook
InteractiveShell.ast_node_interactivity = "all"
```

```python
YouTubeVideo('UblRctFXjh4', height=500, width=478)
```

__MECA654, March 2018, Polytech Annecy-Chambéry__
[This interactive notebook is on Github](https://github.com/a1rb4Ck/MECA654)
## Exercise 8 - Friction wheel
### Rotations composition
$\dot{\theta_{01}}$ rotation speed of axis $\vec{i_1}$
$\dot{\theta_{02}}$ rotation speed of axis $\vec{i_2}$

```python
# Define used symbols for SymPy, constant variables
alpha, R_1, R_2, t = symbols('alpha R_1 R_2 t')  # the time is a symbol.
# Define dynamic symbols, variables function of time (t)
theta_1, theta_2, OM = dynamicsymbols('theta_1 theta_2 OM')
theta_1dot = dynamicsymbols('theta_1', 1)  # First order derivative of theta_1
theta_2dot = dynamicsymbols('theta_2', 1)  # First order derivative of theta_2

# Our reference frames for each body:

B0 = ReferenceFrame('B0', indices=['i', 'j', 'k'])
B1 = ReferenceFrame('B1', indices=['i', 'j', 'k'])
B2center = ReferenceFrame('B2center', indices=['i', 'j', 'k'])
B2 = ReferenceFrame('B2', indices=['i', 'j', 'k'])

# We define space transformation relations in our problem:
B1.orient(B0, 'Axis', [theta_1, B0.x])  # Rotation of theta_1 (i_0), same as (k_1)
B2center.orient(B0, 'Axis', [alpha-(pi/2), B0.z])  # Rotation of axis (k_0), same as (k_1)
B2.orient(B2center, 'Axis', [theta_2, B2center.x])  # Rotation of axis (k_0), same as (k_1)

# We can view the Direct Cosinus Matrix between both reference frames!
# B1.dcm(B0)
# B2center.dcm(B0)
# B2.dcm(B0)
# B2.dcm(B1)
```

**1. You should determined:**
   - **$\overrightarrow{V_{01}(M)}$ in the $(0)$ reference frame:  **

```python
# We define the OM vector translation
OM = - R_1 * B1['j']  # O is the center
display(Math(r'\overrightarrow{OM}=%s \ %s' % (latex(OM), latex('in \ the \ B_2 \ base'))))
```

$$\overrightarrow{OM}=-  R_{1}\mathbf{\hat{b1}_{j}} \ in \ the \ B_2 \ base$$

```python
V_01 = OM.dt(B0)  # B0 is the same as B1
# V_01  # V_01 in the B0 frame
display(Math(r'\overrightarrow{V_{01}(M)}=%s \ %s' % (latex(V_01), latex('in \ the \ B_0 \ base'))))
```

$$\overrightarrow{V_{01}(M)}=-  R_{1} \dot{\theta}_{1}\mathbf{\hat{b1}_{k}} \ in \ the \ B_0 \ base$$

   - **$\overrightarrow{V_{02}(M)}$ in the $(0)$ reference frame:  **

```python
OM_2 = - R_2 * B2['j']
display(Math(r'\overrightarrow{OM}=%s \ %s' % (latex(OM_2), latex('in \ the \ B_2 \ base'))))
display(Math(r'\overrightarrow{OM}=%s \ %s' % (latex(OM_2.express(B2center)), latex('in \ the \ B_2center \ base'))))

V_02 = OM_2.dt(B2center)
display(Math(r'\overrightarrow{V_{02}(M)}=%s \ %s' % (latex(V_02), latex('in \ the \ B_2 \ base'))))

# Express the vector that we originally wrote in the B2 frame, in the B0 frame:
V_02 = V_02.express(B0)
display(Math(r'\overrightarrow{V_{02}(M)}=%s \ %s' % (latex(V_02), latex('in \ the \ B_0 \ base'))))
```

$$\overrightarrow{OM}=-  R_{2}\mathbf{\hat{b2}_{j}} \ in \ the \ B_2 \ base$$

$$\overrightarrow{OM}=-  R_{2} \operatorname{cos}\left(\theta_{2}\right)\mathbf{\hat{b2center}_{j}} -  R_{2} \operatorname{sin}\left(\theta_{2}\right)\mathbf{\hat{b2center}_{k}} \ in \ the \ B_2center \ base$$

$$\overrightarrow{V_{02}(M)}=-  R_{2} \dot{\theta}_{2}\mathbf{\hat{b2}_{k}} \ in \ the \ B_2 \ base$$

$$\overrightarrow{V_{02}(M)}=R_{2} \operatorname{sin}\left(\theta_{2}\right) \operatorname{cos}\left(\alpha\right) \dot{\theta}_{2}\mathbf{\hat{b0}_{i}} + R_{2} \operatorname{sin}\left(\alpha\right) \operatorname{sin}\left(\theta_{2}\right) \dot{\theta}_{2}\mathbf{\hat{b0}_{j}} -  R_{2} \operatorname{cos}\left(\theta_{2}\right) \dot{\theta}_{2}\mathbf{\hat{b0}_{k}} \ in \ the \ B_0 \ base$$

- **Assuming a no slipping contact in $M$, we have $\overrightarrow{V_{12}(M)} = 0$
Find the relation between $\overrightarrow{V_{01}(M)}$, $\overrightarrow{V_{02}(M)}$, $\dot{\theta_{01}}$, $\dot{\theta_{02}}$**

```python
# V_01 + V_12 = V_02
# V_12 = V_02 - V_01
# and V_12.k = 0  hence, we have:
display(Math(r'\overrightarrow{V_{01}(M)} + \overrightarrow{V_{12}(M)}=\overrightarrow{V_{02}(M)}'))
display(Math(r'\overrightarrow{V_{12}(M)} = \overrightarrow{V_{02}(M)} - \overrightarrow{V_{01}(M)}'))
display(Math(r'and \ \overrightarrow{V_{12}(M).k}=0 \ %s' % latex('\ hence, \ we \ have:')))
V_12 = V_02.to_matrix(B0) - V_01.to_matrix(B0)
display(Math(r'\overrightarrow{V_{01}(M)} - \overrightarrow{V_{02}(M)}= \ %s %s' % (latex(V_12), latex(Matrix([B0.name + '_i', B0.name + '_j', B0.name + '_k'])))))
display(Math(r'\overrightarrow{V_{12}(M).k} = \ %s \ = 0' % latex(V_12[2])))

# We solve the linear equation which keeps derivatives theta_xdot (asked question):
sdot = solve(V_12[2], theta_1dot)
display(Math(r'\dot{\theta_1} = \ %s' % latex(sdot)))
# We solve the linear equation which keeps derivatives theta_xdot (asked question):

# Bonus: We solve to find R_1 function of everything else:
sR1 = solve(V_12[2], R_1)
display(Math(r'R_1 = \ %s' % latex(sR1)))

# Bonus: We solve the Ordinary Differential Equation (ODE) to find theta_1(t):
solutions = dsolve(V_12[2], theta_1)
solutions

# Bonus: We optimize to find the maximal velocity with R_1 and R_2 parameters
## Find the zero derivative:
# f = solutions[1]  # .subs(t, 1)
# We divide by theta_1 to maximize the ratio between theta_1 and theta_2
# t2 = symbols('t2')
# fprime = diff(f.args[1].subs(theta_2, t2), t2)
# fprime
# solve(Eq(fprime, 0), R_1)

# f = asin(R_2*sin(theta_2)/R_1)
# fprime = f.diff(func, theta_2)

# from scipy import optimize
# def f_theta_1(theta_2_subs): return solutions[1].subs(t, 1).subs(theta_2, theta_2_subs)
# max_x = optimize.fmin(lambda x: -f_theta_1(x), 0)
# max_x

# V_12 = V_02.to_matrix(B0).col_insert(3,V_01.to_matrix(B0))
# C1, C2, C3 = symbols('C1 C2 C3')
# theta_1 = Function('theta_1')
# theta_2 = Function('theta_2')
# diffeq = tuple([Eq(eq) for eq in V_12])
# diffeq
# from sympy.solvers.ode import odesimp
# odesimp(diffeq, theta_1(t), 1, {C1, C2}, hint='1st_homogeneous_coeff_subs_indep_div_dep')

# No Sympy function seems to be able to perform Gaussian Jordan reduction with many symbols and derivatives.
# Thus, we substract line 1 and line 2:
# gausseq = (diffeq[0], Eq(diffeq[0].args[0], diffeq[1].args[0]).simplify(), diffeq[2])
# gausseq
# We could get some hint on the first ODE:
# classify_ode(gausseq[0], theta_1(t))
# ode_order(gausseq[0], theta_1(t))

# from sympy.solvers.ode import infinitesimals
# infinitesimals(diffeq[0:-1], [theta_1(t), theta_2(t)])
```

### Usefulness of friction transmissions : Continuous Variable Transmissions (CVT)

```python
YouTubeVideo('LHoAh2Bo83c', height=500, width=800)
YouTubeVideo('2wpK-LDvtuc', height=500, width=800)
```

```python
%%HTML

<footer id="attribution" style="float:right; color:#999; background:#fff;">
Created with Jupyter, Sympy, Numpy   -   MECA654   -   Polytech Annecy-Chambéry   -   Pierre Nagorny   -   March 2018
</footer>
```

<footer id="attribution" style="float:right; color:#999; background:#fff;">
Created with Jupyter, Sympy, Numpy   -   MECA654   -   Polytech Annecy-Chambéry   -   Pierre Nagorny   -   March 2018
</footer>

```python

```


```python
 !pip install qiskit
```

    Collecting qiskit
      Downloading qiskit-0.30.0.tar.gz (12 kB)
    Collecting qiskit-terra==0.18.2
      Downloading qiskit_terra-0.18.2-cp37-cp37m-manylinux2010_x86_64.whl (6.1 MB)
         |████████████████████████████████| 6.1 MB 5.4 MB/s
    Collecting qiskit-aer==0.9.0
      Downloading qiskit_aer-0.9.0-cp37-cp37m-manylinux_2_12_x86_64.manylinux2010_x86_64.whl (17.9 MB)
         |████████████████████████████████| 17.9 MB 95 kB/s
    Collecting qiskit-ibmq-provider==0.16.0
      Downloading qiskit_ibmq_provider-0.16.0-py3-none-any.whl (235 kB)
         |████████████████████████████████| 235 kB 69.5 MB/s
    Collecting qiskit-ignis==0.6.0
      Downloading qiskit_ignis-0.6.0-py3-none-any.whl (207 kB)
         |████████████████████████████████| 207 kB 71.7 MB/s
    Collecting qiskit-aqua==0.9.5
      Downloading qiskit_aqua-0.9.5-py3-none-any.whl (2.1 MB)
         |████████████████████████████████| 2.1 MB 58.5 MB/s
    Collecting pybind11>=2.6
      Downloading pybind11-2.7.1-py2.py3-none-any.whl (200 kB)
         |████████████████████████████████| 200 kB 55.4 MB/s
    Requirement already satisfied: scipy>=1.0 in /usr/local/lib/python3.7/dist-packages (from qiskit-aer==0.9.0->qiskit) (1.4.1)
    Requirement already satisfied: numpy>=1.16.3 in /usr/local/lib/python3.7/dist-packages (from qiskit-aer==0.9.0->qiskit) (1.19.5)
    Requirement already satisfied: h5py<3.3.0 in /usr/local/lib/python3.7/dist-packages (from qiskit-aqua==0.9.5->qiskit) (3.1.0)
    Requirement already satisfied: fastdtw<=0.3.4 in /usr/local/lib/python3.7/dist-packages (from qiskit-aqua==0.9.5->qiskit) (0.3.4)
    Collecting dlx<=1.0.4
      Downloading dlx-1.0.4.tar.gz (5.5 kB)
    Collecting retworkx>=0.8.0
      Downloading retworkx-0.10.2-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl (1.4 MB)
         |████████████████████████████████| 1.4 MB 42.2 MB/s
    Requirement already satisfied: pandas in /usr/local/lib/python3.7/dist-packages (from qiskit-aqua==0.9.5->qiskit) (1.1.5)
    Requirement already satisfied: scikit-learn>=0.20.0 in /usr/local/lib/python3.7/dist-packages (from qiskit-aqua==0.9.5->qiskit) (0.22.2.post1)
    Collecting yfinance>=0.1.62
      Downloading yfinance-0.1.63.tar.gz (26 kB)
    Requirement already satisfied: setuptools>=40.1.0 in /usr/local/lib/python3.7/dist-packages (from qiskit-aqua==0.9.5->qiskit) (57.4.0)
    Collecting docplex>=2.21.207
      Downloading docplex-2.22.213.tar.gz (634 kB)
         |████████████████████████████████| 634 kB 42.6 MB/s
    Requirement already satisfied: psutil>=5 in /usr/local/lib/python3.7/dist-packages (from qiskit-aqua==0.9.5->qiskit) (5.4.8)
    Requirement already satisfied: sympy>=1.3 in /usr/local/lib/python3.7/dist-packages (from qiskit-aqua==0.9.5->qiskit) (1.7.1)
    Collecting quandl
      Downloading Quandl-3.6.1-py2.py3-none-any.whl (26 kB)
    Requirement already satisfied: urllib3>=1.21.1 in /usr/local/lib/python3.7/dist-packages (from qiskit-ibmq-provider==0.16.0->qiskit) (1.24.3)
    Collecting requests-ntlm>=1.1.0
      Downloading requests_ntlm-1.1.0-py2.py3-none-any.whl (5.7 kB)
    Requirement already satisfied: python-dateutil>=2.8.0 in /usr/local/lib/python3.7/dist-packages (from qiskit-ibmq-provider==0.16.0->qiskit) (2.8.2)
    Requirement already satisfied: requests>=2.19 in /usr/local/lib/python3.7/dist-packages (from qiskit-ibmq-provider==0.16.0->qiskit) (2.23.0)
    Collecting websocket-client>=1.0.1
      Downloading websocket_client-1.2.1-py2.py3-none-any.whl (52 kB)
         |████████████████████████████████| 52 kB 1.2 MB/s
    Collecting symengine>0.7
      Downloading symengine-0.8.1-cp37-cp37m-manylinux2010_x86_64.whl (38.2 MB)
         |████████████████████████████████| 38.2 MB 24 kB/s
    Collecting fastjsonschema>=2.10
      Downloading fastjsonschema-2.15.1-py3-none-any.whl (21 kB)
    Collecting tweedledum<2.0,>=1.1
      Downloading tweedledum-1.1.1-cp37-cp37m-manylinux_2_12_x86_64.manylinux2010_x86_64.whl (943 kB)
         |████████████████████████████████| 943 kB 46.1 MB/s
    Collecting ply>=3.10
      Downloading ply-3.11-py2.py3-none-any.whl (49 kB)
         |████████████████████████████████| 49 kB 5.7 MB/s
    Requirement already satisfied: dill>=0.3 in /usr/local/lib/python3.7/dist-packages (from qiskit-terra==0.18.2->qiskit) (0.3.4)
    Collecting python-constraint>=1.4
      Downloading python-constraint-1.4.0.tar.bz2 (18 kB)
    Requirement already satisfied: jsonschema>=2.6 in /usr/local/lib/python3.7/dist-packages (from qiskit-terra==0.18.2->qiskit) (2.6.0)
    Requirement already satisfied: six in /usr/local/lib/python3.7/dist-packages (from docplex>=2.21.207->qiskit-aqua==0.9.5->qiskit) (1.15.0)
    Requirement already satisfied: cached-property in /usr/local/lib/python3.7/dist-packages (from h5py<3.3.0->qiskit-aqua==0.9.5->qiskit) (1.5.2)
    Requirement already satisfied: idna<3,>=2.5 in /usr/local/lib/python3.7/dist-packages (from requests>=2.19->qiskit-ibmq-provider==0.16.0->qiskit) (2.10)
    Requirement already satisfied: certifi>=2017.4.17 in /usr/local/lib/python3.7/dist-packages (from requests>=2.19->qiskit-ibmq-provider==0.16.0->qiskit) (2021.5.30)
    Requirement already satisfied: chardet<4,>=3.0.2 in /usr/local/lib/python3.7/dist-packages (from requests>=2.19->qiskit-ibmq-provider==0.16.0->qiskit) (3.0.4)
    Collecting ntlm-auth>=1.0.2
      Downloading ntlm_auth-1.5.0-py2.py3-none-any.whl (29 kB)
    Collecting cryptography>=1.3
      Downloading cryptography-3.4.8-cp36-abi3-manylinux_2_24_x86_64.whl (3.0 MB)
         |████████████████████████████████| 3.0 MB 44.8 MB/s
    Requirement already satisfied: cffi>=1.12 in /usr/local/lib/python3.7/dist-packages (from cryptography>=1.3->requests-ntlm>=1.1.0->qiskit-ibmq-provider==0.16.0->qiskit) (1.14.6)
    Requirement already satisfied: pycparser in /usr/local/lib/python3.7/dist-packages (from cffi>=1.12->cryptography>=1.3->requests-ntlm>=1.1.0->qiskit-ibmq-provider==0.16.0->qiskit) (2.20)
    Requirement already satisfied: joblib>=0.11 in /usr/local/lib/python3.7/dist-packages (from scikit-learn>=0.20.0->qiskit-aqua==0.9.5->qiskit) (1.0.1)
    Requirement already satisfied: mpmath>=0.19 in /usr/local/lib/python3.7/dist-packages (from sympy>=1.3->qiskit-aqua==0.9.5->qiskit) (1.2.1)
    Requirement already satisfied: multitasking>=0.0.7 in /usr/local/lib/python3.7/dist-packages (from yfinance>=0.1.62->qiskit-aqua==0.9.5->qiskit) (0.0.9)
    Collecting lxml>=4.5.1
      Downloading lxml-4.6.3-cp37-cp37m-manylinux2014_x86_64.whl (6.3 MB)
         |████████████████████████████████| 6.3 MB 44.3 MB/s
    Requirement already satisfied: pytz>=2017.2 in /usr/local/lib/python3.7/dist-packages (from pandas->qiskit-aqua==0.9.5->qiskit) (2018.9)
    Collecting inflection>=0.3.1
      Downloading inflection-0.5.1-py2.py3-none-any.whl (9.5 kB)
    Requirement already satisfied: more-itertools in /usr/local/lib/python3.7/dist-packages (from quandl->qiskit-aqua==0.9.5->qiskit) (8.9.0)
    Building wheels for collected packages: qiskit, dlx, docplex, python-constraint, yfinance
      Building wheel for qiskit (setup.py) ... done
      Created wheel for qiskit: filename=qiskit-0.30.0-py3-none-any.whl size=11273 sha256=d28d8fb021583de3a5cc17cc1b6f13cdda5cb2ec01801c64de77f26fdfbd8b9f
      Stored in directory: /root/.cache/pip/wheels/30/2b/0f/42f529b566315599b81731f2a58728da92883b00cccb78b6c1
      Building wheel for dlx (setup.py) ... done
      Created wheel for dlx: filename=dlx-1.0.4-py3-none-any.whl size=5719 sha256=70a8e4474d90df32a230eb0c183afb299ba0b5dfabb23e4e8321e6c3c5001d1c
      Stored in directory: /root/.cache/pip/wheels/78/55/c8/dc61e772445a566b7608a476d151e9dcaf4e092b01b0c4bc3c
      Building wheel for docplex (setup.py) ... done
      Created wheel for docplex: filename=docplex-2.22.213-py3-none-any.whl size=696881 sha256=2a40fa7ed410e69cbfb11c48284a50f3cba42fd625c0a59a5880c2eaefb223ce
      Stored in directory: /root/.cache/pip/wheels/90/69/6b/1375c68a5b7ff94c40263b151c86f58bd72200bf0c465b5ba3
      Building wheel for python-constraint (setup.py) ... done
      Created wheel for python-constraint: filename=python_constraint-1.4.0-py2.py3-none-any.whl size=24081 sha256=814b20b761f92c0a9c59002e12ada1d7125a3d9f41dee77150d878b221674895
      Stored in directory: /root/.cache/pip/wheels/07/27/db/1222c80eb1e431f3d2199c12569cb1cac60f562a451fe30479
      Building wheel for yfinance (setup.py) ... done
      Created wheel for yfinance: filename=yfinance-0.1.63-py2.py3-none-any.whl size=23918 sha256=a7546fe19d53352f58ba13e85532597ae05c1c211c387a2f4c31d36a7ddf126a
      Stored in directory: /root/.cache/pip/wheels/fe/87/8b/7ec24486e001d3926537f5f7801f57a74d181be25b11157983
    Successfully built qiskit dlx docplex python-constraint yfinance
    Installing collected packages: tweedledum, symengine, retworkx, python-constraint, ply, fastjsonschema, qiskit-terra, ntlm-auth, lxml, inflection, cryptography, yfinance, websocket-client, requests-ntlm, quandl, qiskit-ignis, pybind11, docplex, dlx, qiskit-ibmq-provider, qiskit-aqua, qiskit-aer, qiskit
      Attempting uninstall: lxml
        Found existing installation: lxml 4.2.6
        Uninstalling lxml-4.2.6:
          Successfully uninstalled lxml-4.2.6
    Successfully installed cryptography-3.4.8 dlx-1.0.4 docplex-2.22.213 fastjsonschema-2.15.1 inflection-0.5.1 lxml-4.6.3 ntlm-auth-1.5.0 ply-3.11 pybind11-2.7.1 python-constraint-1.4.0 qiskit-0.30.0 qiskit-aer-0.9.0 qiskit-aqua-0.9.5 qiskit-ibmq-provider-0.16.0 qiskit-ignis-0.6.0 qiskit-terra-0.18.2 quandl-3.6.1 requests-ntlm-1.1.0 retworkx-0.10.2 symengine-0.8.1 tweedledum-1.1.1 websocket-client-1.2.1 yfinance-0.1.63

```python
!pip install requests-html
```

    Collecting requests-html
      Downloading requests_html-0.10.0-py3-none-any.whl (13 kB)
    Collecting w3lib
      Downloading w3lib-1.22.0-py2.py3-none-any.whl (20 kB)
    Collecting fake-useragent
      Downloading fake-useragent-0.1.11.tar.gz (13 kB)
    Collecting pyquery
      Downloading pyquery-1.4.3-py3-none-any.whl (22 kB)
    Requirement already satisfied: requests in /usr/local/lib/python3.7/dist-packages (from requests-html) (2.23.0)
    Collecting parse
      Downloading parse-1.19.0.tar.gz (30 kB)
    Collecting pyppeteer>=0.0.14
      Downloading pyppeteer-0.2.6-py3-none-any.whl (83 kB)
         |████████████████████████████████| 83 kB 1.9 MB/s
    Requirement already satisfied: bs4 in /usr/local/lib/python3.7/dist-packages (from requests-html) (0.0.1)
    Collecting websockets<10.0,>=9.1
      Downloading websockets-9.1-cp37-cp37m-manylinux2010_x86_64.whl (103 kB)
         |████████████████████████████████| 103 kB 15.9 MB/s
    Collecting urllib3<2.0.0,>=1.25.8
      Downloading urllib3-1.26.7-py2.py3-none-any.whl (138 kB)
         |████████████████████████████████| 138 kB 19.6 MB/s
    Collecting pyee<9.0.0,>=8.1.0
      Downloading pyee-8.2.2-py2.py3-none-any.whl (12 kB)
    Requirement already satisfied: tqdm<5.0.0,>=4.42.1 in /usr/local/lib/python3.7/dist-packages (from pyppeteer>=0.0.14->requests-html) (4.62.2)
    Requirement already satisfied: importlib-metadata>=1.4 in /usr/local/lib/python3.7/dist-packages (from pyppeteer>=0.0.14->requests-html) (4.8.1)
    Requirement already satisfied: appdirs<2.0.0,>=1.4.3 in /usr/local/lib/python3.7/dist-packages (from pyppeteer>=0.0.14->requests-html) (1.4.4)
    Requirement already satisfied: zipp>=0.5 in /usr/local/lib/python3.7/dist-packages (from importlib-metadata>=1.4->pyppeteer>=0.0.14->requests-html) (3.5.0)
    Requirement already satisfied: typing-extensions>=3.6.4 in /usr/local/lib/python3.7/dist-packages (from importlib-metadata>=1.4->pyppeteer>=0.0.14->requests-html) (3.7.4.3)
    Requirement already satisfied: beautifulsoup4 in /usr/local/lib/python3.7/dist-packages (from bs4->requests-html) (4.6.3)
    Collecting cssselect>0.7.9
      Downloading cssselect-1.1.0-py2.py3-none-any.whl (16 kB)
    Requirement already satisfied: lxml>=2.1 in /usr/local/lib/python3.7/dist-packages (from pyquery->requests-html) (4.6.3)
    Collecting urllib3<2.0.0,>=1.25.8
      Downloading urllib3-1.25.11-py2.py3-none-any.whl (127 kB)
         |████████████████████████████████| 127 kB 33.7 MB/s
    Requirement already satisfied: certifi>=2017.4.17 in /usr/local/lib/python3.7/dist-packages (from requests->requests-html) (2021.5.30)
    Requirement already satisfied: chardet<4,>=3.0.2 in /usr/local/lib/python3.7/dist-packages (from requests->requests-html) (3.0.4)
    Requirement already satisfied: idna<3,>=2.5 in /usr/local/lib/python3.7/dist-packages (from requests->requests-html) (2.10)
    Requirement already satisfied: six>=1.4.1 in /usr/local/lib/python3.7/dist-packages (from w3lib->requests-html) (1.15.0)
    Building wheels for collected packages: fake-useragent, parse
      Building wheel for fake-useragent (setup.py) ... done
      Created wheel for fake-useragent: filename=fake_useragent-0.1.11-py3-none-any.whl size=13502 sha256=e3dbdf0218a18892a5b5663533121847ab6f12c8d2a9c37bb1a0e7d659770d97
      Stored in directory: /root/.cache/pip/wheels/ed/f7/62/50ab6c9a0b5567267ab76a9daa9d06315704209b2c5d032031
      Building wheel for parse (setup.py) ... done
      Created wheel for parse: filename=parse-1.19.0-py3-none-any.whl size=24591 sha256=a6d3c6dc2b68a67a781900a9bff68bf79465745506a80206fd4ba47f5819ea9b
      Stored in directory: /root/.cache/pip/wheels/9c/aa/cc/f2228050ccb40f22144b073f15a2c84f11204f29fc0dce028e
    Successfully built fake-useragent parse
    Installing collected packages: websockets, urllib3, pyee, cssselect, w3lib, pyquery, pyppeteer, parse, fake-useragent, requests-html
      Attempting uninstall: urllib3
        Found existing installation: urllib3 1.24.3
        Uninstalling urllib3-1.24.3:
          Successfully uninstalled urllib3-1.24.3
    ERROR: pip's dependency resolver does not currently take into account all the packages that are installed. This behaviour is the source of the following dependency conflicts.
    datascience 0.10.6 requires folium==0.2.1, but you have folium 0.8.3 which is incompatible.
    Successfully installed cssselect-1.1.0 fake-useragent-0.1.11 parse-1.19.0 pyee-8.2.2 pyppeteer-0.2.6 pyquery-1.4.3 requests-html-0.10.0 urllib3-1.25.11 w3lib-1.22.0 websockets-9.1

```python
pip install requests-html
```

    Requirement already satisfied: requests-html in /usr/local/lib/python3.7/dist-packages (0.10.0)
    Requirement already satisfied: fake-useragent in /usr/local/lib/python3.7/dist-packages (from requests-html) (0.1.11)
    Requirement already satisfied: requests in /usr/local/lib/python3.7/dist-packages (from requests-html) (2.23.0)
    Requirement already satisfied: pyquery in /usr/local/lib/python3.7/dist-packages (from requests-html) (1.4.3)
    Requirement already satisfied: pyppeteer>=0.0.14 in /usr/local/lib/python3.7/dist-packages (from requests-html) (0.2.6)
    Requirement already satisfied: parse in /usr/local/lib/python3.7/dist-packages (from requests-html) (1.19.0)
    Requirement already satisfied: bs4 in /usr/local/lib/python3.7/dist-packages (from requests-html) (0.0.1)
    Requirement already satisfied: w3lib in /usr/local/lib/python3.7/dist-packages (from requests-html) (1.22.0)
    Requirement already satisfied: tqdm<5.0.0,>=4.42.1 in /usr/local/lib/python3.7/dist-packages (from pyppeteer>=0.0.14->requests-html) (4.62.2)
    Requirement already satisfied: websockets<10.0,>=9.1 in /usr/local/lib/python3.7/dist-packages (from pyppeteer>=0.0.14->requests-html) (9.1)
    Requirement already satisfied: importlib-metadata>=1.4 in /usr/local/lib/python3.7/dist-packages (from pyppeteer>=0.0.14->requests-html) (4.8.1)
    Requirement already satisfied: appdirs<2.0.0,>=1.4.3 in /usr/local/lib/python3.7/dist-packages (from pyppeteer>=0.0.14->requests-html) (1.4.4)
    Requirement already satisfied: urllib3<2.0.0,>=1.25.8 in /usr/local/lib/python3.7/dist-packages (from pyppeteer>=0.0.14->requests-html) (1.25.11)
    Requirement already satisfied: pyee<9.0.0,>=8.1.0 in /usr/local/lib/python3.7/dist-packages (from pyppeteer>=0.0.14->requests-html) (8.2.2)
    Requirement already satisfied: zipp>=0.5 in /usr/local/lib/python3.7/dist-packages (from importlib-metadata>=1.4->pyppeteer>=0.0.14->requests-html) (3.5.0)
    Requirement already satisfied: typing-extensions>=3.6.4 in /usr/local/lib/python3.7/dist-packages (from importlib-metadata>=1.4->pyppeteer>=0.0.14->requests-html) (3.7.4.3)
    Requirement already satisfied: beautifulsoup4 in /usr/local/lib/python3.7/dist-packages (from bs4->requests-html) (4.6.3)
    Requirement already satisfied: cssselect>0.7.9 in /usr/local/lib/python3.7/dist-packages (from pyquery->requests-html) (1.1.0)
    Requirement already satisfied: lxml>=2.1 in /usr/local/lib/python3.7/dist-packages (from pyquery->requests-html) (4.6.3)
    Requirement already satisfied: idna<3,>=2.5 in /usr/local/lib/python3.7/dist-packages (from requests->requests-html) (2.10)
    Requirement already satisfied: chardet<4,>=3.0.2 in /usr/local/lib/python3.7/dist-packages (from requests->requests-html) (3.0.4)
    Requirement already satisfied: certifi>=2017.4.17 in /usr/local/lib/python3.7/dist-packages (from requests->requests-html) (2021.5.30)
    Requirement already satisfied: six>=1.4.1 in /usr/local/lib/python3.7/dist-packages (from w3lib->requests-html) (1.15.0)

```python
#import packages
from bs4 import BeautifulSoup
import requests

import nest_asyncio
import pandas as pd
import requests
import re
import json
from requests_html import HTMLSession
from requests_html import AsyncHTMLSession
from urllib.parse import urlparse
from urllib.parse import parse_qs
from bs4 import BeautifulSoup
import numpy as np
import csv
from google.colab import drive
#drive.mount("/content/drive")
drive.mount("/content/drive", force_remount=True)

#open file
f = open('drive/MyDrive/Colab Notebooks/Landservice.csv', "w", newline="")

  #create csv writer
writer = csv.writer(f)

  #write a row to the csv file
headers = "Name, Adresse\n"
f.write(headers)

```

    Mounted at /content/drive

    14

```python
#get search page
source = requests.get("https://www.landservice.de/ls/start_bereich?land=D&ap=5&bernr=106&lstore=1&page=1").text

soup = BeautifulSoup(source, 'lxml')
# print(soup.prettify())
base_info=[]
```

```python
#loop farms on search page

urls =  []
soup = BeautifulSoup(source, 'lxml')
for containers in soup.find_all(class_="col-md-8"):
    try:
      #array to store farm infos
      base_farm=[]

      #Get URL
      base_url="https://www.landservice.de/ls/"
      url=containers.find(class_="col-md-12 weiterlesen").a["href"]
      import_url=base_url+url
      #print(import_url)
      base_farm.append(import_url)

      #Get Hofname
      hofname = containers.find('div', class_="col-md-12 hofname").text
      #print(hofname)
      base_farm.append(hofname)

      urls.append(base_farm)
    except:
      print('error')

for url in urls:
  print(url)
```

    error
    ['https://www.landservice.de/ls/betrieb_detail?land=D&ap=5&bernr=106&adr_nr=DE0300001986&store=1', 'Café & Hofladen Gut Kinderhaus']
    ['https://www.landservice.de/ls/betrieb_detail?land=D&ap=5&bernr=106&adr_nr=DE0300003106&store=1', 'Bauer Godde']
    ['https://www.landservice.de/ls/betrieb_detail?land=D&ap=5&bernr=106&adr_nr=DE0300002706&store=1', 'Schäferei Brinkmann']
    ['https://www.landservice.de/ls/betrieb_detail?land=D&ap=5&bernr=106&adr_nr=DE0300001588&store=1', "Wernerhof Füngeling's Hofladen"]
    ['https://www.landservice.de/ls/betrieb_detail?land=D&ap=5&bernr=106&adr_nr=DE0300000638&store=1', 'Party-Service Hauwe']
    ['https://www.landservice.de/ls/betrieb_detail?land=D&ap=5&bernr=106&adr_nr=DE0300000563&store=1', 'Obstplantagen Bloemersheim (Obstgarten J  B OHG)']
    ['https://www.landservice.de/ls/betrieb_detail?land=D&ap=5&bernr=106&adr_nr=DE0300003911&store=1', 'Möllers Erdbeeren']
    ['https://www.landservice.de/ls/betrieb_detail?land=D&ap=5&bernr=106&adr_nr=DE0300000173&store=1', 'Hof Grothues-Potthoff']
    ['https://www.landservice.de/ls/betrieb_detail?land=D&ap=5&bernr=106&adr_nr=DE0300001145&store=1', 'Milchautomat Gut Steuwen']
    ['https://www.landservice.de/ls/betrieb_detail?land=D&ap=5&bernr=106&adr_nr=DE0300002486&store=1', 'Gut Schobbenhaus']

# **Scrape Farm Pages**

URL 1

```python
#load page
try:
  test_url = urls[0][0]
  print(test_url)

#create session
  session = HTMLSession()

#make request
  request = session.get(test_url)

#cook soup
  farm_soup = BeautifulSoup(request.html.html,"html.parser")

#print(farm_soup.prettify())

except:
  print(error)
```

    https://www.landservice.de/ls/betrieb_detail?land=D&ap=5&bernr=106&adr_nr=DE0300001986&store=1

```python
#scraper
try:
  farm_soup = BeautifulSoup(request.html.html,"html.parser")
  hofname = farm_soup.title
#print(hofname.text)

  adresse = farm_soup.find(class_="col-md-7").find(class_="row").text
#print(adresse.prettify())
  print(adresse)

  web = farm_soup.find(target="TOP").text
  print(web)

except:
  print(error)
```

    Café & Hofladen Gut Kinderhaus

    Gut Kinderhaus
    Am Max-Klemens-Kanal 19
    48159 Münster
    Tel.: 0251 9210334 (Laden)
    2. Tel.: 0251 9210330 (Café)
    Fax: 0251 9210332 (Café)
    hfkjErix2rzjsxyjw3ijwww.westfalenfleiss.de/gut-kinderhaus.html

    www.westfalenfleiss.de/gut-kinderhaus.html

URL2

```python
#load page
try:
  test_url = urls[1][0]
  print(test_url)

#create session
  session = HTMLSession()

#make request
  request = session.get(test_url)

#cook soup
  farm_soup = BeautifulSoup(request.html.html,"html.parser")

#print(farm_soup.prettify())

except:
  print(error)

```

    https://www.landservice.de/ls/betrieb_detail?land=D&ap=5&bernr=106&adr_nr=DE0300003106&store=1

```python
#scraper2
farm_soup = BeautifulSoup(request.html.html,"html.parser")
hofname = farm_soup.title
#print(hofname.text)

adresse = farm_soup.find(class_="col-md-7").find(class_="row").text
#print(adresse.prettify())
print(adresse)
```

    Bauer Godde

    Karl Godde
    Marpenstr. 29
    45699 Herten
    Tel.: 02366 506671
    Fax: 02366 506672
    pfwq2ltiijEy2tsqnsj3ijwww.bauer-godde.de

URL3

```python
#load page
try:
  test_url = urls[2][0]
  print(test_url)

#create session
  session = HTMLSession()

#make request
  request = session.get(test_url)

#cook soup
  farm_soup = BeautifulSoup(request.html.html,"html.parser")

#print(farm_soup.prettify())

except:
  print(error)
```

    https://www.landservice.de/ls/betrieb_detail?land=D&ap=5&bernr=106&adr_nr=DE0300002706&store=1

```python
#scraper3
farm_soup = BeautifulSoup(request.html.html,"html.parser")
hofname = farm_soup.title
#print(hofname.text)

adresse = farm_soup.find(class_="col-md-7").find(class_="row").text
#print(adresse.prettify())
print(adresse)
```

    Schäferei Brinkmann

    Ernst Georg Brinkmann
    Bittinger Haarweg 10
    59469 Ense-Bittingen
    Tel.: 02928 621
    Fax: 02928 621
    rfnqExhmfjkjwjn2jwsxy2gwnsprfss3ijwww.schaeferei-ernst-brinkmann.de

URL4

```python
#load page
try:
  test_url = urls[3][0]
  print(test_url)

#create session
  session = HTMLSession()

#make request
  request = session.get(test_url)

#cook soup
  farm_soup = BeautifulSoup(request.html.html,"html.parser")

#print(farm_soup.prettify())

except:
  print(error)
```

    https://www.landservice.de/ls/betrieb_detail?land=D&ap=5&bernr=106&adr_nr=DE0300001588&store=1

```python
#scraper4
farm_soup = BeautifulSoup(request.html.html,"html.parser")
hofname = farm_soup.title
#print(hofname.text)

adresse = farm_soup.find(class_="col-md-7").find(class_="row").text
#print(adresse.prettify())
print(adresse)
```

    Wernerhof Füngeling's Hofladen

    Martin Füngeling
    Im Kreuel 1
    50354 Hürth
    Tel.: 02233 31907
    Fax: 02233 37139
    nsktEkzjsljqnslxmtk3ijwww.fuengelingshof.de

URL5

```python
#load page
try:
  test_url = urls[4][0]
  print(test_url)

#create session
  session = HTMLSession()

#make request
  request = session.get(test_url)

#cook soup
  farm_soup = BeautifulSoup(request.html.html,"html.parser")

#print(farm_soup.prettify())

except:
  print(error)
```

    https://www.landservice.de/ls/betrieb_detail?land=D&ap=5&bernr=106&adr_nr=DE0300000638&store=1

```python
#scraper5
farm_soup = BeautifulSoup(request.html.html,"html.parser")
hofname = farm_soup.title
#print(hofname.text)

adresse = farm_soup.find(class_="col-md-7").find(class_="row").text
#print(adresse.prettify())
print(adresse)
```

    Party-Service Hauwe

    Josef Hauwe e.K.
    Stemmbrückenstr. 40
    45711 Datteln
    Tel.: 02363 73252
    Mobil: 0172 2831466
    Fax: 02363 731253
    Ufwy~2Xjw{nhj2Mfz|jEy2tsqnsj3ijwww.party-service-hauwe.de

URL6

```python
#load page
try:
  test_url = urls[5][0]
  print(test_url)

#create session
  session = HTMLSession()

#make request
  request = session.get(test_url)

#cook soup
  farm_soup = BeautifulSoup(request.html.html,"html.parser")

#print(farm_soup.prettify())

except:
  print(error)
```

    https://www.landservice.de/ls/betrieb_detail?land=D&ap=5&bernr=106&adr_nr=DE0300000563&store=1

```python
#scraper6
farm_soup = BeautifulSoup(request.html.html,"html.parser")
hofname = farm_soup.title
#print(hofname.text)

adresse = farm_soup.find(class_="col-md-7").find(class_="row").text
#print(adresse.prettify())
print(adresse)
```

    Obstplantagen Bloemersheim (Obstgarten J  B OHG)

    Jacqueline Huhndorf
    Bloemersheimer Weg 2
    47506 Neukirchen-Vluyn
    Mobil: 0151 22617691
    o3mzmsitwkEtgxylfwyjs3sjywww.obstplantagen-bloemersheim.de

URL7

```python
#load page
try:
  test_url = urls[6][0]
  print(test_url)

#create session
  session = HTMLSession()

#make request
  request = session.get(test_url)

#cook soup
  farm_soup = BeautifulSoup(request.html.html,"html.parser")

#print(farm_soup.prettify())

except:
  print(error)
```

    https://www.landservice.de/ls/betrieb_detail?land=D&ap=5&bernr=106&adr_nr=DE0300003911&store=1

```python
#scraper7
farm_soup = BeautifulSoup(request.html.html,"html.parser")
hofname = farm_soup.title
#print(hofname.text)

adresse = farm_soup.find(class_="col-md-7").find(class_="row").text
#print(adresse.prettify())
print(adresse)
```

    Möllers Erdbeeren

    Heinrich Möllers
    Wester 2
    48607 Ochtrup
    Tel.: 02553 98764
    Mobil: 0173 8622229
    Fax: 02553 98314
    rtjqqjwx2jwigjjwjsElr}3ij

URL8

```python
#load page
try:
  test_url = urls[7][0]
  print(test_url)

#create session
  session = HTMLSession()

#make request
  request = session.get(test_url)

#cook soup
  farm_soup = BeautifulSoup(request.html.html,"html.parser")

#print(farm_soup.prettify())

except:
  print(error)
```

    https://www.landservice.de/ls/betrieb_detail?land=D&ap=5&bernr=106&adr_nr=DE0300000173&store=1

```python
#scraper8
farm_soup = BeautifulSoup(request.html.html,"html.parser")
hofname = farm_soup.title
#print(hofname.text)

adresse = farm_soup.find(class_="col-md-7").find(class_="row").text
#print(adresse.prettify())
print(adresse)
```

    Hof Grothues-Potthoff

    Familie Grothues-Potthoff
    Hof Grothues-Potthoff 4-6
    48308 Senden
    Tel.: 02597 696410
    2. Tel.: 02597 696420
    Fax: 02597 696415
    nsktEmtk2lwtymzjx2utyymtkk3ijwww.hof-grothues-potthoff.de

URL9

```python
#load page
try:
  test_url = urls[8][0]
  print(test_url)

#create session
  session = HTMLSession()

#make request
  request = session.get(test_url)

#cook soup
  farm_soup = BeautifulSoup(request.html.html,"html.parser")

#print(farm_soup.prettify())

except:
  print(error)
```

    https://www.landservice.de/ls/betrieb_detail?land=D&ap=5&bernr=106&adr_nr=DE0300001145&store=1

```python
#scraper9
farm_soup = BeautifulSoup(request.html.html,"html.parser")
hofname = farm_soup.title
#print(hofname.text)

adresse = farm_soup.find(class_="col-md-7").find(class_="row").text
#print(adresse.prettify())
print(adresse)
```

    Milchautomat Gut Steuwen

    Reiner und Trudi Geurden
    Boomdyk 69
    47839 Krefeld-Hüls
    Tel.: 02151 734780
    Mobil: 0173 7285520
    Fax: 02151 974870
    ywzin2ljzwijsEy2tsqnsj3ijwww.gutsteuwen.de

URL10

```python
#load page
try:
  test_url = urls[9][0]
  print(test_url)

#create session
  session = HTMLSession()

#make request
  request = session.get(test_url)

#cook soup
  farm_soup = BeautifulSoup(request.html.html,"html.parser")

#print(farm_soup.prettify())

except:
  print(error)
```

    https://www.landservice.de/ls/betrieb_detail?land=D&ap=5&bernr=106&adr_nr=DE0300002486&store=1

```python
#scraper10
farm_soup = BeautifulSoup(request.html.html,"html.parser")
hofname = farm_soup.title
#print(hofname.text)

adresse = farm_soup.find(class_="col-md-7").find(class_="row").text
#print(adresse.prettify())
print(adresse)
```

    Gut Schobbenhaus

    Johannes Kircher
    Außenbürgerschaft 20
    40822 Mettmann
    Tel.: 02104 952353
    Mobil: 0177 7429608
    Fax: 02104 952354
    mtkqfijsElzy2xhmtggjsmfzx3ijwww.gut-schobbenhaus.de

Page2

```python
#get search page
source = requests.get("https://www.landservice.de/ls/start_bereich?land=D&ap=5&bernr=106&lstore=1&page=2").text

soup = BeautifulSoup(source, 'lxml')
# print(soup.prettify())
base_info=[]
```

```python
#loop farms on search page

urls =  []
soup = BeautifulSoup(source, 'lxml')
for containers in soup.find_all(class_="col-md-8"):
    try:
      #array to store farm infos
      base_farm=[]

      #Get URL
      base_url="https://www.landservice.de/ls/"
      url=containers.find(class_="col-md-12 weiterlesen").a["href"]
      import_url=base_url+url
      #print(import_url)
      base_farm.append(import_url)

      #Get Hofname
      hofname = containers.find('div', class_="col-md-12 hofname").text
      #print(hofname)
      base_farm.append(hofname)

      urls.append(base_farm)
    except:
      print('error')

for url in urls:
  print(url)
```

    error
    ['https://www.landservice.de/ls/betrieb_detail?land=D&ap=5&bernr=106&adr_nr=DE0300002777&store=1', 'Nienaber Zuckerrübensirup']
    ['https://www.landservice.de/ls/betrieb_detail?land=D&ap=5&bernr=106&adr_nr=DE0300004213&store=1', 'Hof Schering']
    ['https://www.landservice.de/ls/betrieb_detail?land=D&ap=5&bernr=106&adr_nr=DE0300000166&store=1', 'Ferienhof Bals']
    ['https://www.landservice.de/ls/betrieb_detail?land=D&ap=5&bernr=106&adr_nr=DE0300003114&store=1', 'Brunnenhof in der Ried']
    ['https://www.landservice.de/ls/betrieb_detail?land=D&ap=5&bernr=106&adr_nr=DE0300001500&store=1', 'Bauernhof Schuh']
    ['https://www.landservice.de/ls/betrieb_detail?land=D&ap=5&bernr=106&adr_nr=DE0300001105&store=1', 'Buschhof']
    ['https://www.landservice.de/ls/betrieb_detail?land=D&ap=5&bernr=106&adr_nr=DE0300003562&store=1', 'Straußenfarm Emminghausen']
    ['https://www.landservice.de/ls/betrieb_detail?land=D&ap=5&bernr=106&adr_nr=DE0300001870&store=1', 'Bioland Gemüsehof Weßling']
    ['https://www.landservice.de/ls/betrieb_detail?land=D&ap=5&bernr=106&adr_nr=DE0300000359&store=1', 'Hof Lütke Laxen']
    ['https://www.landservice.de/ls/betrieb_detail?land=D&ap=5&bernr=106&adr_nr=DE0300001310&store=1', 'Gahmener Hof GbR']

```python
f.write("Name: "+ hofname + " , " + "Adresse: " + adresse + "\n")

f.close()
```


 Text provided under a Creative Commons Attribution license, CC-BY. All code is made available under the FSF-approved MIT license. (c) Kyle T. Mandli

Note:  This material largely follows the text "Numerical Linear Algebra" by Trefethen and Bau (SIAM, 1997) and is meant as a guide and supplement to the material presented there.

```python
from __future__ import print_function

%matplotlib inline
import numpy
import matplotlib.pyplot as plt
```

# Conditioning and Stability

Once an approximation to a linear system is constructed the next question is how much trust can we put in the approximation? Since the true solution is not known, one of the few tools we have is to ask how well the approximation matches the original equation. In other words, we seek a solution to a system,
$$
    \vec{f}(\vec{x}) = \vec{b}.
$$

We do not have $\vec{x}$ but instead have an approximation, $\hat{x}$, and we hope that
$$
    \vec{f}(\hat{x}) \approx \vec{b}.
$$
In this section the question we explore is to try to determine a bound on the relative error, $\frac{||\vec{x}-\hat{x}||}{||\vec{x}||}$ given the matrix, $A$.

This leads to the notion of conditioning. Conditioning is the behavior of a problem when the solution is a changed a small bit (perturbed), and it is a  mathematical (analytic) property of the original system of equations.  Stability, on the other hand, is concerned with how the algorithm used to obtain an approximation behaves when the approximation is perturbed.

## Conditioning and Condition Numbers

A **well-conditioned** problem is one where a small perturbation to the original problem leads to only small changes in the solution.

Formally we can think of a function $f$ which maps $x$ to $y$

$$
    f(x) = y \quad \text{or} \quad f: X \rightarrow Y.
$$

Let $x \in X$ where we perturb $x$ with $\delta x$ and we ask how the result $y$ changes:

$$
    ||f(x) - f(x + \delta x)|| \leq C ||x - (x+\delta x)||
$$

for some constant $C$ possible dependent on $\delta x$ depending on the type of conditioning we are considering.

### Absolute Condition Number

If we let $\delta x$ be the small perturbation to the input and $\delta f = f(x + \delta x) - f(x)$ be the result the **absolute condition number** $\hat{~\kappa}$ can be defined as

$$
    \hat{\!\kappa} = \sup_{\delta x} \frac{||\delta f||}{||\delta x||}
$$

for most problems (assuming $\delta f$ and $\delta x$ are both infinitesimal).

When $f$ is differentiable we can evaluate the condition number via the Jacobian.  Recall that the derivative of a multi-valued function can be termed in the form of a Jacobian $J(x)$ where
$$
    [J(x)]_{ij} = \frac{\partial f_i}{\partial x_j}(x).
$$

This allows us to write the infinitesimal $\delta f$ as
$$
    \delta f \approx J(x) \delta x
$$
with equality when $||\delta x|| \rightarrow 0$.  Then we can write the condition number as
$$
    \hat{\!\kappa} = ||J(x)||
$$
where the norm is the one induced by the spaces $X$ and $Y$.

### Relative Condition Number

The **relative condition number** is defined similarly and is related to the difference before between the absolute error and relative error as defined previously.  With the same caveats as before it can be defined as
$$
    \kappa = \sup_{\delta x} \left( \frac{\frac{||\delta f||}{||f(x)||}}{\frac{||\delta x||}{||x||}} \right).
$$

Again if $f$ is differentiable we can use the Jacobian $J(x)$ to evaluate the relative condition number as
$$
    \kappa = \frac{||J(x)||}{||f(x)|| ~/ ~||x||}.
$$

#### Examples
Calculate the following relative condition numbers of the following problems.

$\sqrt{x}$ for $x > 0$.

$$
    f(x) = \sqrt{x}, \quad J(x) = f'(x) = \frac{1}{2 \sqrt{x}} \\
    \kappa = \frac{||J(x)||}{||f(x)|| / ||x||} = \frac{1}{2 \sqrt{x}} \frac{x}{\sqrt{x}} = \frac{1}{2}
$$

Calculate the relative condition number for the scalar function $f(x) = x_1 - x_2$ using the vector $\vec{x} = (x_1, x_2)^T \in \mathbb R^2$ using an $\ell_\infty$ norm.

$$
    f(x) = x_1 - x_2, \quad J(x) = \left [ \frac{\partial f}{\partial x_1}, \frac{\partial f}{\partial x_2}\right ] = [1, -1] \\
    \kappa = \frac{||J(x)||_\infty}{||f(x)||_\infty / ||x||_\infty} = \frac{2 \max_{i=1,2} |x_i|}{|x_1 - x_2|}
$$
where
$$
    ||J||_\infty = 2.
$$

The condition number of a function was discussed in general terms above. Now, the more specific case of a linear function, a matrix-vector multiplication, is examined. Here we let $\vec{f}(\vec{x})=Ax$ and determine the condition number by perturbing $x$.

We begin with the definition above,
$$\begin{aligned}
    \kappa &= \sup_{\delta x} \left ( \frac{||A (\vec{x}+\delta x) - A \vec{x}||}{||A\vec{x}||} \frac{||\vec{x}||}{||\delta x||}\right ), \\
    &= \sup_{\delta x} \frac{ ||A \delta x||}{||\delta x||} \frac{||\vec{x}||}{||A\vec{x}||}, \\
    &= ||A|| \frac{||\vec{x}||}{||A \vec{x}||},
\end{aligned}$$
where $\delta x$ is a vector.

If $A$ has an inverse, then we note that
$$
\begin{align}
 \vec{x} &= A^{-1}A \vec{x}, \\
 \Rightarrow ||\vec{x}|| &= || A^{-1}A \vec{x} ||, \\
                         &\leq ||A^{-1}|| || A \vec{x} ||,
\end{align}
$$
which implies that
$$
    \frac{||x||}{||A x||} \leq ||A^{-1}||.
$$
We can now bound the condition number for a matrix by
$$
    \kappa \leq ||A|| ||A^{-1}||.
$$

### Condition Number of a Matrix

The condition number of a matrix is defined by the product
$$
    \kappa(A) = ||A||~||A^{-1}||.
$$
where here we are thinking about the matrix rather than a problem.  If $\kappa$ is small than $A$ is said to be **well-conditioned**.  If $A$ is singular we assign $\kappa(A) = \infty$ as the matrix's condition number.

When we are considering the $\ell_2$ norm then we can write the condition number as

$$
    \kappa(A) = \frac{\sqrt{\rho(A^\ast A)}}{\sqrt{\rho((A^\ast A)^{-1})}} = \frac{\sqrt{\max |\lambda|}}{\sqrt{\min |\lambda|}}.
$$

### Condition Number of a System of Equations

Another way to think about the conditioning of a problem we have looked at before is that the matrix $A$ itself is an input to the problem.  Consider than the system of equations $A\vec{x} = \vec{b}$ where we will perturb both $A$ and $\vec{x}$ resulting in
$$
    (A + \delta A)(\vec{x} + \delta x) = \vec{b}.
$$

Assuming we solve the problem exactly we know that $A\vec{x} = \vec{b}$ and that the infinitesimals multiplied $\delta A \delta x$ are smaller than the other term, and the above expression can be approximation by
$$
\begin{aligned}
    (A + \delta A)(\vec{x} + \delta x) &= \vec{b}, \\
    A\vec{x} + \delta Ax + A \delta x + \delta A \delta \vec{x} &= \vec{b} \\
    \delta A\vec{x} + A \delta x & = 0
\end{aligned}
$$

Solving for $\delta x$ leads to
$$
    \delta x = -A^{-1} \delta A \vec{x}
$$
implying
$$
    ||\delta x|| \leq ||A^{-1}|| ~ ||\delta A|| ~ ||\vec{x}||
$$
and therefore
$$
    \frac{\frac{||\delta x||}{||\vec{x}||}}{\frac{||\delta A||}{||A||}} \leq ||A^{-1}||~||A|| = \kappa(A).
$$

We can also say the following regarding the condition number of a system of equations then

**Theorem:**  Let $\vec{b}$ be fixed and consider the problem of computing $\vec{x}$ in $A\vec{x} = \vec{b}$ where $A$ is square and non-singular.  The condition number of this problem with respect to perturbations in $A$ is the condition number of the matrix $\kappa(A)$.

## Stability

We now return to the consideration of the fact that we are interested not only in the well-conditioning of a mathematical problem but in how we might solve it on a finite precision machine.  In some sense conditioning describes how well we can solve a problem in exact arithmetic and stability how well we can solve the problem in finite arithmetic.

### Accuracy and Stability

As we have defined before we will consider **absolute error** as
$$
    ||F(x) - f(x)||
$$
where $F(x)$ is the approximation to the true solution $f(x)$.  Similarly we can define **relative error** as
$$
    \frac{||F(x) - f(x)||}{||f(x)||}.
$$
In the ideal case we would like the relative error to be $\mathcal{O}(\epsilon_{\text{machine}})$.

#### Forwards Stability

A **forward stable** algorithm for $x \in X$ has

$$
    \frac{||F(x) - f(x)||}{||f(x)||} = \mathcal{O}(\epsilon_{\text{machine}})
$$

In other words
> A forward stable algorithm gives almost the right answer to exactly the right question.

#### Backwards Stability

A stronger notion of stability can also be defined which is satisfied by many approaches in numerical linear algebra.  We say that an algorithm $F$ is **backward stable** if for $x \in X$ we have

$$
    F(x) = f(\hat{\!x})
$$

for some $\hat{\!x}$ with

$$
    \frac{||\hat{\!x} - x||}{||x||} = \mathcal{O}(\epsilon_{\text{machine}}).
$$

In other words
> A backward stable algorithm gives exactly the right answer to nearly the right question.

Combining these ideas along with the idea that we should not expect to be able to accurately compute the solution to a poorly conditioned problem we can form the mixed forward-backward sense of stability as for $x \in X$ if

$$
    \frac{||F(x) - f(\hat{\!x})||}{||f(\hat{\!x})||} = \mathcal{O}(\epsilon_{\text{machine}})
$$

for some $\hat{\!x}$ with

$$
    \frac{||\hat{\!x} - x||}{||x||} = \mathcal{O}(\epsilon_{\text{machine}}).
$$

In other words
> A stable algorithm gives nearly the right answer to nearly the right question.

An important aspect of the above statement is that we can not necessarily guarantee an accurate result.  If the condition number $\kappa(x)$ is small we would expect that a stable algorithm would give us an accurate result (by definition).  This is reflected in the following theorem.

**Theorem:**  Suppose a backward stable algorithm is applied to solve a problem $f: X \rightarrow Y$ with condition number $\kappa$ on a finite precision machine, then the relative errors satisfy
$$
    \frac{||F(x) - f(\hat{\!x})||}{||f(\hat{\!x})||} = \mathcal{O}(\kappa(x) ~ \epsilon_{\text{machine}}).
$$

**Proof:**  By the definition of the condition number of a problem we can write
$$
    \frac{||F(x) - f(\hat{\!x})||}{||f(\hat{\!x})||} \leq (\kappa(x) + \mathcal{O}(\epsilon_{\text{machine}}))\frac{||\hat{\!x} - x||}{||x||}.
$$
Combining this with the definition of backwards stability we can arrive at the statement of the theorem.

**Backward Error Analysis** - Process of using the condition number of the problem and stability of the algorithm to determine the error.

**Forward Error Analysis** - Considers the accrual of error at each step of an algorithm given slightly perturbed input.

### Stability of $A\vec{x} = \vec{b}$ using Householder Triangularization

As an example lets consider the conditioning and algorithm for solving $A\vec{x} = \vec{b}$.  Here we will use a $QR$ factorization approach to solve $A\vec{x} = \vec{b}$ given by a Householder triangularization.  First off lets discuss the $QR$ factorization itself.

**Theorem:** Let the $QR$ factorization $A = QR$ of a matrix $A \in \mathbb C^{m \times n}$ be computed using a Householder triangularization approach on a finite precision machine, then

$$
    \hat{\!Q} \cdot \hat{\!R} = A + \delta A \quad \frac{||\delta A||}{||A||} = \mathcal{O}(\epsilon_{\text{machine}})
$$

for some $\delta A \in \mathbb C^{m \times n}$ where $\hat{\!Q}$ and $\hat{\!R}$ are the finite arithmetic versions of $Q$ and $R$.  Householder triangularization is therefore backward stable.

#### Solving $A\vec{x} = \vec{b}$ with $QR$ Factorization

So Householder triangularization is backwards stable but we also know that this does not guarantee accuracy if the problem itself is ill-conditioned.  Is backward stability enough to guarantee accurate results if we use it for $A\vec{x} = \vec{b}$ for instance?  It turns out that the accuracy of the product of $QR$ is enough to guarantee accuracy of a larger algorithm.

Consider the steps to solving $A \vec{x} = \vec{b}$ using $QR$ factorization:
1. Compute the $QR$ factorization of $A$
1. Multiply the vector $\vec{b}$ by $Q^\ast$ so that $\vec{y} = Q^\ast \vec{b}$.
1. Solve using backward-substitution the triangular system $R \vec{x} = \vec{y}$ or $\vec{x} = R^{-1} \vec{y}$.

We know that step (1) is backward stable, what about step (2), the matrix-vector multiplication?  We can write the estimate of the backwards stability as

$$
    (\hat{\!Q} + \delta Q) \cdot \hat{\!y} = b \quad \text{with} \quad ||\delta Q|| = \mathcal{O}(\epsilon_{\text{machine}})
$$

where we have inverted the matrix $ \hat{\!Q}$ since it is unitary.  Since this is exact we know also that the matrix-vector multiplication is also backwards stable since this is an equivalent statement to multiplying $b$ by a slightly perturbed matrix.

Step (3) is backward substitution (or the computation of $R^{-1}$).  Writing the backwards stability estimate we have

$$
    (\hat{\!R} + \delta R) \cdot \hat{\!x} = \hat{\!y} \quad \text{with} \quad \frac{||\delta R||}{||\hat{\!R}||} = \mathcal{O}(\epsilon_{\text{machine}})
$$

demonstrating that the results $\hat{\!x}$ is the exact solution to a slight perturbation of the original problem.

These results lead to the following two theorems:

**Theorem:**  Using $QR$ factorization to solve $A\vec{x}=\vec{b}$ as described above is backward stable, satisfying
$$
    (A + \Delta A) ~ \hat{\!x} = \vec{b}, \quad \frac{||\Delta A||}{||A||} = \mathcal{O}(\epsilon_{\text{machine}})
$$
for some $\Delta A \in \mathbb C^{m \times n}$.

**Theorem:** The solution $\hat{x}$ computed by the above algorithm satisfies
$$
    \frac{||\hat{\!x} - \vec{x}||}{||\vec{x}||} = \mathcal{O}(\kappa(x) ~ \epsilon_{\text{machine}}).
$$