Section 1.5

Transformations II: Composability, Linearity and Nonlinearity

Lecture Video

html"""
<div style="
position: absolute;
width: calc(100% - 30px);
border: 50vw solid #282936;
border-top: 500px solid #282936;
border-bottom: none;
box-sizing: content-box;
left: calc(-50vw + 15px);
top: -500px;
height: 500px;
pointer-events: none;
"></div>

<div style="
height: 500px;
width: 100%;
background: #282936;
color: #fff;
padding-top: 68px;
">
<span style="
font-family: Vollkorn, serif;
font-weight: 700;
font-feature-settings: 'lnum', 'pnum';
"> <p style="
font-size: 1.5rem;
opacity: .8;
"><em>Section 1.5</em></p>
<p style="text-align: center; font-size: 2rem;">
<em> Transformations II: Composability, Linearity and Nonlinearity </em>
</p>

<p style="
font-size: 1.5rem;
text-align: center;
6.8 ms
PlutoUI.TableOfContents(aside=true)
7.3 ms

Intializing packages

When running this notebook for the first time, this could take up to 15 minutes. Hang in there!

md"""
#### Intializing packages

_When running this notebook for the first time, this could take up to 15 minutes. Hang in there!_
"""
3.6 ms
begin
using PlutoUI
using Colors, ColorVectorSpace, ImageShow, FileIO, ImageIO
using PlutoUI
using LinearAlgebra
using ForwardDiff
using NonlinearSolve
using StaticArrays
end
9.2 s
Enter cell code...
107 μs
# img_original = load(download(corgis_url));
img_original = load(download(longcorgi_url));
# img_original = load(download(theteam_url));
1.9 s
corgis_url
"https://user-images.githubusercontent.com/6933510/108605549-fb28e180-73b4-11eb-8520-7e29db0cc965.png"
corgis_url = "https://user-images.githubusercontent.com/6933510/108605549-fb28e180-73b4-11eb-8520-7e29db0cc965.png"
18.3 μs
longcorgi_url
"https://user-images.githubusercontent.com/6933510/110868198-713faa80-82c8-11eb-8264-d69df4509f49.png"
longcorgi_url = "https://user-images.githubusercontent.com/6933510/110868198-713faa80-82c8-11eb-8264-d69df4509f49.png"
24.3 μs
theteam_url
"https://news.mit.edu/sites/default/files/styles/news_article__image_gallery/public/images/202004/edelman%2520philip%2520sanders.png?itok=ZcYu9NFeg"
theteam_url = "https://news.mit.edu/sites/default/files/styles/news_article__image_gallery/public/images/202004/edelman%2520philip%2520sanders.png?itok=ZcYu9NFeg"
17.8 μs

After you select your image, we suggest moving this line above just above the top of your browser.


md"""
After you select your image, we suggest moving this line above just above the top of your browser.

---------------
"""
293 μs

The fun stuff: playing with transforms

md"""
# The fun stuff: playing with transforms
"""
240 μs

This is a "scrubbable" matrix: click on the number and drag to change!

A =

( 1.0 0.0 )

( 0.0 1.0 )

let

range = -1.5:.1:1.5
md"""
This is a "scrubbable" matrix: click on the number and drag to change!
**A =**
``(``
$(@bind a Scrubbable( range; default=1.0))
$(@bind b Scrubbable( range; default=0.0))
``)``

``(``
$(@bind c Scrubbable(range; default=0.0 ))
$(@bind d Scrubbable(range; default=1.0))
``)``

"""
end
409 ms

Grab a linear or nonlinear transform, or make up your own!

md"""
Grab a [linear](#a0afe3ae-76b9-11eb-2301-cde7260ddd7f) or [nonlinear](#a290d5e2-7a02-11eb-37db-41bf86b1f3b3) transform, or make up your own!
"""
363 μs
T⁻¹
#5 (generic function with 1 method)
# T⁻¹ = id
# T⁻¹ = rotate(α)
T⁻¹ = shear(α)
# T⁻¹ = lin(A) # uses the scrubbable
# T⁻¹ = shear(α) ∘ shear(-α)
# T⁻¹ = nonlin_shear(α)
# T⁻¹ = inverse(nonlin_shear(α))
# T⁻¹ = nonlin_shear(-α)
# T⁻¹ = xy
# T⁻¹ = warp(α)
# T⁻¹ = ((x,y),)-> (x+α*y^2,y+α*x^2) # may be non-invertible

# T⁻¹ = ((x,y),)-> (x,y^2)
# T⁻¹ = flipy ∘ ((x,y),) -> ( (β*x - α*y)/(β - y) , -h*y/ (β - y) )
21.9 μs

zoom = 1.0

md"""
zoom = $(@bind z Scrubbable(.1:.1:3, default=1))
"""
27.0 ms

pan = [0.0, 0.0 ]

md"""
pan = [$(@bind panx Scrubbable(-1:.1:1, default=0)),
$(@bind pany Scrubbable(-1:.1:1, default=0)) ]
"""
6.5 ms

α= 0 β= 5 h= 5

md"""
α= $(@bind α Slider(-30:.1:30, show_value=true, default=0))
β= $(@bind β Slider(-10:.1:10, show_value=true, default = 5))
h= $(@bind h Slider(.1:.1:10, show_value=true, default = 5))
"""
54.3 ms
2
1+1
18.0 μs

pixels = 800

md"""
pixels = $(@bind pixels Slider(1:1000, default=800, show_value=true))
"""
15.9 ms

Show grid lines ngrid = 10

md"""
Show grid lines $(@bind show_grid CheckBox(default=true))
ngrid = $(@bind ngrid Slider(5:5:20, show_value=true, default = 10))
"""
49.8 ms

Circular Frame radius = 1

md"""
Circular Frame $(@bind circular CheckBox(default=true))
radius = $(@bind r Slider(.1:.1:1, show_value=true, default = 1))
"""
2.2 ms
begin
[
begin
x, y = transform_ij_to_xy(i,j, pixels)
X, Y = ( translate(-panx,-pany) )([x,y])
X, Y = ( T⁻¹scale(1/z)∘translate(-panx,-pany) )([x,y])
i, j = transform_xy_to_ij(img,X,Y)
getpixel(img,i,j; circular=circular, r=r)
end
for i = 1:pixels, j = 1:pixels
]
end
1.9 s
Enter cell code...
105 μs
transform_xy_to_ij(img,0.0,0.0)

36.8 μs

Above: The original image is placed in a [-1,1] x [-1 1] box and transformed.

md"""
Above: The original image is placed in a [-1,1] x [-1 1] box and transformed.
"""
303 μs
A = [a b ; c d];
31.0 μs

Pedagogical note: Why the Module 1 application = image processing

Image processing is a great way to learn Julia, some linear algebra, and some nonlinear mathematics. We don't presume the audience will become professional image processors, but we do believe that the principles learned transcend so many applications... and everybody loves playing with their own images!

md"""
## Pedagogical note: Why the Module 1 application = image processing

Image processing is a great way to learn Julia, some linear algebra, and some nonlinear mathematics. We don't presume the audience will become professional image processors, but we do believe that the principles learned transcend so many applications... and everybody loves playing with their own images!
"""
365 μs

Last Lecture Leftovers

md"""
# Last Lecture Leftovers
"""
253 μs

Interesting question about linear transformations

If a transformation takes lines into lines and preserves the origin, does it have to be linear?

Answer = no!

The example of a perspective map takes all lines into lines, but paralleograms generally do not become parallelograms.

md"""
## Interesting question about linear transformations
If a transformation takes lines into lines and preserves the origin, does it have to be linear?

Answer = **no**!

The example of a **perspective map** takes all lines into lines, but paralleograms generally do not become parallelograms.
"""
575 μs
md"""
[A nice interactive demo of perspective maps](https://www.khanacademy.org/humanities/renaissance-reformation/early-renaissance1/beginners-renaissance-florence/a/linear-perspective-interactive) from Khan academy.
"""
316 μs
Resource("https://cdn.kastatic.org/ka-perseus-images/1b351a3653c1a12f713ec24f443a95516f916136.jpg")
4.5 ms

Challenge exercise: Rewrite this using Julia and Pluto!

md"""
**Challenge exercise**: Rewrite this using Julia and Pluto!
"""
288 μs

Julia style (a little advanced): Reminder about defining vector valued functions

md"""
## Julia style (a little advanced): Reminder about defining vector valued functions
"""
231 μs

Many people find it hard to read

f(v) = [ v[1]+v[2] , v[1]-v[2] ] or f = v -> [ v[1]+v[2] , v[1]-v[2] ]

and instead prefer

f((x,y)) = [ x+y , x-y ] or f = ((x,y),) -> [ x+y , x-y ].

All four of these will take a 2-vector to a 2-vector in the same way for the purposes of this lecture, i.e. f( [1,2] ) can be defined by any of the four forms.

The forms with the -> are anonymous functions. (They are still considered anonymous, even though we then name them f.)

md"""
Many people find it hard to read


`f(v) = [ v[1]+v[2] , v[1]-v[2] ] ` or
` f = v -> [ v[1]+v[2] , v[1]-v[2] ] `

and instead prefer

`f((x,y)) = [ x+y , x-y ] ` or
` f = ((x,y),) -> [ x+y , x-y ] `.

All four of these will take a 2-vector to a 2-vector in the same way for the purposes of this lecture, i.e. `f( [1,2] )` can be defined by any of the four forms.

The forms with the `->` are anonymous functions. (They are still
considered anonymous, even though we then name them `f`.)
"""
562 μs

Functions with parameters

The anonymous form comes in handy when one wants a function to depend on a parameter. For example:

f(α) = ((x,y),) -> [x + αy, x - αy]

allows you to apply the f(7) function to the input vector [1, 2] by running f(7)([1, 2]) .

md"""
## Functions with parameters
The anonymous form comes in handy when one wants a function to depend on a **parameter**.
For example:

`f(α) = ((x,y),) -> [x + αy, x - αy]`

allows you to apply the `f(7)` function to the input vector `[1, 2]` by running
`f(7)([1, 2])` .
"""
554 μs

Linear transformations: a collection

md"""
# Linear transformations: a collection
"""
265 μs

Here are a few useful linear transformations:

md"""
Here are a few useful linear transformations:
"""
315 μs
shear (generic function with 1 method)
begin
id((x, y)) = SA[x, y]
scalex(α) = ((x, y),) -> SA[α*x, y]
scaley(α) = ((x, y),) -> SA[x, α*y]
scale(α) = ((x, y),) -> SA[α*x, α*y]
swap((x, y)) = SA[y, x]
flipy((x, y)) = SA[x, -y]
rotate(θ) = ((x, y),) -> SA[cos(θ)*x + sin(θ)*y, -sin(θ)*x + cos(θ)*y]
shear(α) = ((x, y),) -> SA[x + α*y, y]
end
7.2 ms

In fact we can write down the most general linear transformation in one of two ways:

md"""
In fact we can write down the *most general* linear transformation in one of two ways:
"""
335 μs
lin (generic function with 2 methods)
begin
lin(a, b, c, d) = ((x, y),) -> (a*x + b*y, c*x + d*y)
lin(A) = v-> A * [v...] # linear algebra version using matrix multiplication
end
3.2 ms

The second version uses the matrix multiplication notation from linear algebra, which means exactly the same as the first version when

A=[abcd]

md"""
The second version uses the matrix multiplication notation from linear algebra, which means exactly the same as the first version when

$$A = \begin{bmatrix} a & b \\ c & d \end{bmatrix}$$
"""
265 μs

Nonlinear transformations: a collection

md"""
# Nonlinear transformations: a collection
"""
251 μs
rθ (generic function with 1 method)
begin
translate(α,β) = ((x, y),) -> SA[x+α, y+β] # affine, but not linear
nonlin_shear(α) = ((x, y),) -> SA[x, y + α*x^2]
warp(α) = ((x, y),) -> rotate(α*√(x^2+y^2))(SA[x, y])
xy((r, θ)) = SA[ r*cos(θ), r*sin(θ) ]
(x) = SA[norm(x), atan(x[2],x[1]) ]
# exponentialish = ((x,y),) -> [log(x+1.2), log(y+1.2)]
# merc = ((x,y),) -> [ log(x^2+y^2)/2 , atan(y,x) ] # (reim(log(complex(y,x)) ))
end
4.7 ms

Composition

md"""
# Composition
"""
224 μs
true
let
x = rand()
( sincos )(x) ≈ sin(cos(x))
end
41.5 μs

Composing functions in mathematics

Wikipedia (math)

In math we talk about composing two functions to create a new function: the function that takes x to sin(cos(x)) is the composition of the sine function and the cosine function.

We humans tend to blur the distinction between the sine function and the value of sin(x) at some point x. The sine function is a mathematical object by itself. It's a thing that can be evaluated at as many x's as you like.

If you look at the two sides of (sin ∘ cos )(x) ≈ sin(cos(x)) and see that they are exactly the same, it's time to ask yourself what's a key difference? On the left a function is built sin ∘ cos which is then evaluated at x. On the right, that function is never built.

md"""
## Composing functions in mathematics
[Wikipedia (math) ](https://en.wikipedia.org/wiki/Function_composition)

In math we talk about *composing* two functions to create a new function:
the function that takes $x$ to $\sin(\cos(x))$ is the **composition**
of the sine function and the cosine function.

We humans tend to blur the distinction between the sine function
and the value of $\sin(x)$ at some point $x$. The sine function
is a mathematical object by itself. It's a thing that can be evaluated
at as many $x$'s as you like.

If you look at the two sides of
` (sin ∘ cos )(x) ≈ sin(cos(x)) ` and see that they are exactly the same, it's time to ask yourself what's a key difference? On the left a function is built
` sin ∘ cos ` which is then evaluated at `x`. On the right, that function
is never built.
"""


714 μs

Composing functions in computer science

wikipedia (cs)

A key issue is a programming language is whether it's easy to name the composition in that language. In Julia one can create the function sin ∘ cos and one can readily check that (sin ∘ cos)(x) always yields the same value as sin(cos(x)).

md"""
## Composing functions in computer science
[wikipedia (cs)](https://en.wikipedia.org/wiki/Function_composition_(computer_science))

A key issue is a programming language is whether it's easy to name
the composition in that language. In Julia one can create the function
`sin ∘ cos` and one can readily check that ` (sin ∘ cos)(x) ` always yields the same value as `sin(cos(x))`.
"""
477 μs

Composing functions in Julia

Julia's operator follows the mathematical typography convention, as was shown in the sin ∘ cos example above. We can type this symbol as \circ<TAB>.

md"""

## Composing functions in Julia
[Julia's `∘` operator](https://docs.julialang.org/en/v1/manual/functions/#Function-composition-and-piping)
follows the [mathematical typography](https://en.wikipedia.org/wiki/Function_composition#Typography) convention, as was
shown in the `sin ∘ cos` example above. We can type this symbol as `\circ<TAB>`.

"""
443 μs

Composition of software at a higher level

The trend these days is to have higher-order composition of functionalities. A good example would be that an optimization can wrap a highly complicated program which might include all kinds of solvers, and still run successfully. This might require the ability of the outer software to have some awareness of the inner software. It can be quite magical when two very different pieces of software "compose", i.e. work together. Julia's language construction encourages composability. We will discuss this more in a future lecture.

md"""
## Composition of software at a higher level

The trend these days is to have higher-order composition of functionalities.
A good example would be that an optimization can wrap a highly complicated
program which might include all kinds of solvers, and still run successfully.
This might require the ability of the outer software to have some awareness
of the inner software. It can be quite magical when two very different pieces of software "compose", i.e. work together. Julia's language construction encourages composability. We will discuss this more in a future lecture.

"""
349 μs

Find your own examples

Take some of the Linear and Nonlinear Transformations (see the Table of Contents) and find some inverses by placing them in the T= section of "The fun stuff" at the top of this notebook.

md"""
### Find your own examples

Take some of the Linear and Nonlinear Transformations (see the Table of Contents) and find some inverses by placing them in the `T=` section of "The fun stuff" at the top of this notebook.
"""
320 μs

Linear transformations can be written in math using matrix multiplication notation as

(abcd)(xy)

.

md"""
Linear transformations can be written in math using matrix multiplication notation as

$$\begin{pmatrix} a & b \\ c & d \end{pmatrix}
\begin{pmatrix} x \\ y \end{pmatrix}$$.
"""
347 μs

By contrast, here are a few fun functions that cannot be written as matrix times vector. What characterizes the matrix ones from the non-matrix ones?

md"""
By contrast, here are a few fun functions that cannot be written as matrix times
vector. What characterizes the matrix ones from the non-matrix ones?
"""
280 μs

This may be a little fancy, but we see that warp is a rotation, but the rotation depends on the point where it is applied.

md"""
This may be a little fancy, but we see that warp is a rotation, but the
rotation depends on the point where it is applied.
"""
271 μs
warp₂ (generic function with 2 methods)
begin
warp₂(α,x,y) = rotate(α*√(x^2+y^2))
warp₂(α) = ((x, y),) -> warp₂(α,x,y)([x,y])
end
1.7 ms
warp3 (generic function with 1 method)
warp3(α) = ((x, y),) -> rotate(α*√(x^2+y^2))([x,y])
1.4 ms
warp3(1)([1,2])
25.6 μs
warp(1)([5,6])
23.6 μs
warp₂(1.0)([5.0,6.0])
24.4 μs
Enter cell code...
101 μs

Linear transformations: See a matrix, think beyond number arrays

md"""
# Linear transformations: See a matrix, think beyond number arrays
"""
259 μs

Software writers and beginning linear algebra students see a matrix and see a lowly table of numbers. We want you to see a linear transformation – that's what professional mathematicians see.

md"""
Software writers and beginning linear algebra students see a matrix and see a lowly table of numbers. We want you to see a *linear transformation* -- that's what professional mathematicians see.
"""
350 μs

What defines a linear transformation? There are a few equivalent ways of giving a definition.

md"""
What defines a linear transformation? There are a few equivalent ways of giving a definition.
"""
246 μs

Linear transformation definitions:

md"""
**Linear transformation definitions:**
"""
284 μs
  • The intuitive definition:

    The rectangles (gridlines) in the transformed image above always become a lattice of congruent parallelograms.

  • The easy operational (but devoid of intuition) definition:

    A transformation is linear if it is defined by vAv (matrix times vector) for some fixed matrix A.

  • The scaling and adding definition:

    1. If you scale and then transform or if you transform and then scale, the result is always the same:

    T(cv)=cT(v)

    ( v is any vector, and c any number.)

    1. If you add and then transform or vice versa the result is the same:

    T(v1+v2)=T(v1)+T(v2).

    (v1,v2 are any vectors.)

  • The mathematician's definition:

    (A consolidation of the above definition.) T is linear if

    T(c1v1+c2v2)=c1T(v1)+c2T(v2)

    for all numbers c1,c2 and vectors v1,v2. (This can be extended to beyond 2 terms.)

md"""
- The intuitive definition:
> The rectangles (gridlines) in the transformed image [above](#e0b657ce-7a03-11eb-1f9d-f32168cb5394) always become a lattice of congruent parallelograms.

- The easy operational (but devoid of intuition) definition:

> A transformation is linear if it is defined by $v \mapsto A*v$ (matrix times vector) for some fixed matrix $A$.

- The scaling and adding definition:

> 1. If you scale and then transform or if you transform and then scale, the result is always the same:
>
> $T(cv)=c \, T(v)$ ( $v$ is any vector, and $c$ any number.)
>
> 2. If you add and then transform or vice versa the result is the same:
>
> $T(v_1+v_2) = T(v_1) + T(v_2).$ ($v_1,v_2$ are any vectors.)

- The mathematician's definition:

> (A consolidation of the above definition.) $T$ is linear if
>
> $T(c_1 v_1 + c_2 v_2) = c_1 T(v_1) + c_2 T(v_2)$ for all numbers $c_1,c_2$ and vectors $v_1,v_2$. (This can be extended to beyond 2 terms.)

"""
4.5 ms

The matrix

md"""
### The matrix
"""
253 μs
Resource("https://upload.wikimedia.org/wikipedia/en/c/c1/The_Matrix_Poster.jpg")
32.4 μs

No not that matrix!

md"""
No not that matrix!
"""
297 μs

The matrix for a linear transformation T is easy to write down: The first column is T([1,0]) and the second is T([0,1]). That's it!

md"""
The matrix for a linear transformation $T$ is easy to write down: The first
column is $T([1, 0])$ and the second is $T([0,1])$. That's it!
"""
278 μs

Once we have those, the linearity relation

T([x,y])=xT([1,0])+yT([0,1])=x(column 1)+y(column 2)

is exactly the definition of matrix times vector. Try it.

md"""
Once we have those, the linearity relation

$$T([x,y]) = x \, T([1,0]) + y \, T([0,1]) = x \, \mathrm{(column\ 1)} + y \, \mathrm{(column\ 2)}$$

is exactly the definition of matrix times vector. Try it.
"""
317 μs

Matrix multiply: You know how to do it, but why?

md"""
### Matrix multiply: You know how to do it, but why?
"""
230 μs

Did you ever ask yourself why matrix multiply has that somewhat complicated multiplying and adding going on?

md"""
Did you ever ask yourself why matrix multiply has that somewhat complicated multiplying and adding going on?
"""
276 μs
let
A = randn(2,2)
B = randn(2,2)
v = rand(2)
(lin(A) ∘ lin(B))(v), lin(A * B)(v)
end
93.1 μs

Important: The composition of the linear transformation is the linear transformation of the multiplied matrices! There is only one definition of matmul (matrix multiply) that realizes this fact.

To see what it is exactly, remember the first column of lin(A) ∘ lin(B) should be the result of computing the two matrix times vectors y=A[1,0] then z=Ay, and the second column is the same for [0,1].

This is worth writing out if you have never done this.

md"""
**Important:** The composition of the linear transformation is the linear transformation of the multiplied matrices! There is only one definition of
matmul (matrix multiply) that realizes this fact.

To see what it is exactly, remember the first column of `lin(A) ∘ lin(B)` should
be the result of computing the two matrix times vectors $y=A*[1,0]$ then $z=A*y$,
and the second column is the same for $[0,1]$.

This is worth writing out if you have never done this.
"""
467 μs

Let's try doing that with random matrices:

md"""
Let's try doing that with random matrices:
"""
241 μs
begin
P = randn(2, 2)
Q = randn(2, 2)
T₁ = lin(P) ∘ lin(Q)
T₂ = lin(P*Q)
lin(P*Q)((1, 0)), (lin(P)∘lin(Q))((1, 0))
end
92.8 μs
test_img = load(download(corgis_url));
58.3 ms
test_pixels = 300;
18.4 μs

lin(P*Q)

md"""
`lin(P*Q)`
"""
239 μs
begin
[
begin
x, y = transform_ij_to_xy(i,j, test_pixels)
X, Y = T₁([x,y])
i, j = transform_xy_to_ij(test_img,X,Y)
getpixel(test_img,i,j)
end
for i = 1:test_pixels, j = 1:test_pixels
]
end
855 ms

lin(P)∘lin(Q)

md"""
`lin(P)∘lin(Q)`
"""
240 μs
begin
[
begin
x, y = transform_ij_to_xy(i,j, test_pixels)
X, Y = T₂([x,y])
i, j = transform_xy_to_ij(test_img,X,Y)
getpixel(test_img,i,j)
end
for i = 1:test_pixels, j = 1:test_pixels
]
end
202 ms

Coordinate transformations vs object transformations

md"""
# Coordinate transformations vs object transformations
"""
232 μs
img;
18.2 μs
size(img)
18.6 μs
img[50,56]
18.5 μs

If you want to move an object to the right, the first thing you might think of is adding 1 to the x coordinate of every point. The other thing you could do is to subtract one from the first coordinate of the coordinate system. The latter is an example of a coordinate transform.

md"""
If you want to move an object to the right, the first thing you might think of is adding 1 to the $x$ coordinate of every point. The other thing you could do is to subtract one from the first coordinate of the coordinate system. The latter is an example of a coordinate transform.
"""
277 μs

Coordinate transform of an array (i,j) vs points (x,y)

md"""
### Coordinate transform of an array $(i, j)$ vs points $(x, y)$
"""
289 μs

The original image has (1,1) in the upper left corner as an array but is thought of as existing in the entire plane.

md"""
The original image has (1,1) in the upper left corner as an array but is thought
of as existing in the entire plane.
"""
297 μs
Resource("https://raw.githubusercontent.com/mitmath/18S191/Spring21/notebooks/week3/coord_transform.png")
4.3 ms
transform_ij_to_xy (generic function with 1 method)
begin
function transform_xy_to_ij(img::AbstractMatrix, x::Float64, y::Float64)
# convert coordinate system xy to ij
# center image, and use "white" when out of the boundary
rows, cols = size(img)
m = max(cols, rows)
# function to take xy to ij
xy_to_ij = translate(rows/2, cols/2) ∘ swapflipyscale(m/2)
# apply the function and "snap to grid"
i, j = floor.(Int, xy_to_ij((x, y)))
end
function getpixel(img,i::Int,j::Int; circular::Bool=false, r::Real=200)
# grab image color or place default
rows, cols = size(img)
m = max(cols,rows)
if circular
c = (i-rows/2)^2 + (j-cols/2)^2r*m^2/4
else
c = true
end
if 1 < irows && 1 < jcols && c
img[i, j]
else
# white(img[1, 1])
black(img[1,1])
end
end
4.8 ms
transform_ij_to_xy(1,1,400)
20.0 μs
translate(-400,400)([1,1])
23.4 μs

Inverses

md"""
# Inverses
"""
227 μs

If f is a function from 2-vectors to 2-vectors (say), we define the inverse of f, denoted f1, to have the property that it "undoes" the effect of f, i.e.

f(f1(v))=v

and f1(f(v))=v.

This equation might be true for all v or for some v in a region.

md"""
If $f$ is a function from 2-vectors to 2-vectors (say), we define the **inverse** of $f$, denoted
$f^{-1}$, to have the property that it "*undoes*" the effect of $f$, i.e.

$$f(f^{-1}(v))=v$$

and $f^{-1}(f(v))=v$.

This equation might be true for all $v$ or for some $v$ in a region.
"""
538 μs

Example: Scaling up and down

md"""
## Example: Scaling up and down
"""
230 μs
let
v = rand(2)
T = rotate(30)∘rotate(-30 )
T(v), v
end
45.4 μs
let
T = scale(0.5) ∘ scale(2)
v = rand(2)
T(v) .≈ v
end
107 ms

We observe numerically that scale(2) and scale(.5) are mutually inverse transformations, i.e. each is the inverse of the other.

md"""
We observe numerically that `scale(2)` and `scale(.5)` are mutually inverse transformations, i.e. each is the inverse of the other.
"""
268 μs

Inverses: Solving equations

md"""
## Inverses: Solving equations
"""
905 μs

What does an inverse really do?

Let's think about scaling again. Suppose we scale an input vector x by 2 to get an output vector x:

y=2x

Now suppose that you want to go backwards. If you are given y, how do you find x? In this particular case we see that x=12y.

If we have a linear transformation, we can write

y=Ax

with a matrix A.

If we are given y and want to go backwards to find the x from that, we need to solve a system of linear equations.

Usually, but not always, we can solve these equations to find a new matrix B such that

x=By,

i.e. B undoes the effect of A. Then we have

x=(BA)x,

so that BA must be the identity matrix. We call B the matrix inverse of A, and write

B=A1.

For 2×2 matrices we can write down an explicit formula for the matrix inverse, but in general we will need a computer to run an algorithm to find the inverse.

md"""
What does an inverse really do?

Let's think about scaling again.
Suppose we scale an input vector $\mathbf{x}$ by 2 to get an output vector $\mathbf{x}$:

$$\mathbf{y} = 2 \mathbf{x}$$

Now suppose that you want to go backwards. If you are given $\mathbf{y}$, how do you find $\mathbf{x}$? In this particular case we see that $\mathbf{x} = \frac{1}{2} \mathbf{y}$.

If we have a *linear* transformation, we can write

$$\mathbf{y} = A \, \mathbf{x}$$

with a matrix $A$.

If we are given $\mathbf{y}$ and want to go backwards to find the $\mathbf{x}$ from that, we need to *solve a system of linear equations*.


*Usually*, but *not always*, we can solve these equations to find a new matrix $B$ such that

$$\mathbf{x} = B \, \mathbf{y},$$

i.e. $B$ *undoes* the effect of $A$. Then we have


$$\mathbf{x} = (B \, A) * \mathbf{x},$$

so that $B * A$ must be the identity matrix. We call $B$ the *matrix inverse* of $A$, and write

$$B = A^{-1}.$$

For $2 \times 2$ matrices we can write down an explicit formula for the matrix inverse, but in general we will need a computer to run an algorithm to find the inverse.


"""
1.1 ms

Inverting Linear Transformations

md"""
### Inverting Linear Transformations
"""
231 μs
let
v = rand(2)
A = randn(2,2)
(lin(inv(A)) ∘ lin(A))(v), v
end
147 μs
true
let
A = randn(2,2)
B = randn(2,2)
inv(A*B) ≈ inv(B) * inv(A)
end
75.7 μs

A1=(dbca)/(adbc) if  A =(abcd).

md"""
``A^{-1}
=
\begin{pmatrix} d & -b \\ -c & a \end{pmatrix} / (ad-bc) \quad
``
if
``\ A \ =
\begin{pmatrix} a & b \\ c & d \end{pmatrix} .
``
"""
266 μs

Inverting nonlinear transformations

md"""
### Inverting nonlinear transformations
"""
227 μs

What about if we have a nonlinear transformation T – can we invert it? In other words, if y=T(x), can we solve this to find x in terms of y?

In general this is a difficult question! Sometimes we can do so analytically, but usually we cannot.

Nonetheless, there are numerical methods that can sometimes solve these equations, for example the Newton method.

There are several implementations of such methods in Julia, e.g. in the NonlinearSolve.jl package. We have used that to write a function inverse that tries to invert nonlinear transformations of our images.

md"""
What about if we have a *nonlinear* transformation $T$ -- can we invert it? In other words, if $\mathbf{y} = T(\mathbf{x})$, can we solve this to find $\mathbf{x}$ in terms of $\mathbf{y}$?


In general this is a difficult question! Sometimes we can do so analytically, but usually we cannot.

Nonetheless, there are *numerical* methods that can sometimes solve these equations, for example the [Newton method](https://en.wikipedia.org/wiki/Newton%27s_method).

There are several implementations of such methods in Julia, e.g. in the [NonlinearSolve.jl package](https://github.com/JuliaComputing/NonlinearSolve.jl). We have used that to write a function `inverse` that tries to invert nonlinear transformations of our images.
"""
685 μs

The Big Diagram of Transforming Images

md"""
# The Big Diagram of Transforming Images
"""
226 μs
Resource("https://raw.githubusercontent.com/mitmath/18S191/Spring21/notebooks/week3/comm2.png")
33.6 μs

Note that we are defining the map with the inverse of T so we can go pixel by pixel in the result.

md"""
Note that we are defining the map with the inverse of T so we can go pixel by pixel in the result.
"""
245 μs

Collisions

md"""
## Collisions
"""
222 μs
Resource("https://raw.githubusercontent.com/mitmath/18S191/Spring21/notebooks/week3/collide.png")
27.6 μs
inverse (generic function with 3 methods)
begin
function inverse(f, y, u0=@SVector[0.0, 0.0])
prob = NonlinearProblem{false}( (u, p) -> f(u, p) .- y, u0)
solver = solve(prob, NewtonRaphson(), tol = 1e-4)
return solver.u
end
inverse(f) = y -> inverse( (u, p) -> f(SVector(u...)), y )
end
73.4 ms
md"""


Check out
[Linear Map Wikipedia](https://en.wikipedia.org/wiki/Linear_map)

[Transformation Matrix Wikipedia](https://en.wikipedia.org/wiki/Transformation_matrix)
"""
396 μs

Why are we doing this backwards?

If one moves the colors forward rather than backwards you have trouble dealing with the discrete pixels. You may have gaps. You may have multiple colors going to the same pixel.

An interpolation scheme or a newton scheme could work for going forwards, but very likely care would be neeeded for a satisfying general result.

md"""
## Why are we doing this backwards?

If one moves the colors forward rather than backwards you have trouble dealing
with the discrete pixels. You may have gaps. You may have multiple colors going
to the same pixel.

An interpolation scheme or a newton scheme could work for going forwards, but very likely care would be neeeded for a satisfying general result.
"""
378 μs

Appendix

md"""
# Appendix
"""
225 μs
det_A
1.0
det_A = det(A)
23.8 μs
img_sources
img_sources = [
"https://user-images.githubusercontent.com/6933510/108605549-fb28e180-73b4-11eb-8520-7e29db0cc965.png" => "Corgis",
"https://images.squarespace-cdn.com/content/v1/5cb62a904d546e33119fa495/1589302981165-HHQ2A4JI07C43294HVPD/ke17ZwdGBToddI8pDm48kA7bHnZXCqgRu4g0_U7hbNpZw-zPPgdn4jUwVcJE1ZvWQUxwkmyExglNqGp0IvTJZamWLI2zvYWH8K3-s_4yszcp2ryTI0HqTOaaUohrI8PISCdr-3EAHMyS8K84wLA7X0UZoBreocI4zSJRMe1GOxcKMshLAGzx4R3EDFOm1kBS/fluffy+corgi?format=2500w" => "Long Corgi",
"https://previews.123rf.com/images/camptoloma/camptoloma2002/camptoloma200200020/140962183-pembroke-welsh-corgi-portrait-sitting-gray-background.jpg"=>"Portrait Corgi",
"https://www.eaieducation.com/images/products/506618_L.jpg"=>"Graph Paper"
]
32.0 μs
img = if show_grid
with_gridlines(img_original;n=ngrid)
else
img_original
end;
520 ms
black (generic function with 2 methods)
begin
white(c::RGB) = RGB(1,1,1)
white(c::RGBA) = RGBA(1,1,1,0.75)
black(c::RGB) = RGB(0,0,0)
black(c::RGBA) = RGBA(0,0,0,0.75)
end
1.2 ms
with_gridlines (generic function with 1 method)
function with_gridlines(img::Array{<:Any,2}; n = 10)
n = 2n+1
rows, cols = size(img)
result = copy(img)
# stroke = zero(eltype(img))#RGBA(RGB(1,1,1), 0.75)
stroke = RGBA(1, 1, 1, 0.75)
result[ floor.(Int, LinRange(1, rows, n)), : ] .= stroke
# result[ ceil.(Int,LinRange(1, rows, n) ), : ] .= stroke
result[ : , floor.(Int, LinRange(1, cols, n))] .= stroke
# result[ : , ceil.(Int,LinRange(1, cols, n) )] .= stroke
result[ rows ÷2 , :] .= RGBA(0,1,0,1)
# result[ 1+rows ÷2 , :] .= RGBA(0,1,0,1)
result[ : , cols ÷2 ,] .= RGBA(1,0,0,1)
# result[ : , 1 + cols ÷2 ,] .= RGBA(1,0,0,1)
return result
end
2.9 ms
Enter cell code...
93.1 μs