@@ -337,6 +337,7 @@ In this lecture, we use the following import:
337
337
338
338
```{code-cell} ipython3
339
339
import numpy as np
340
+ import matplotlib.pyplot as plt
340
341
```
341
342
342
343
```{code-cell} ipython3
@@ -412,7 +413,7 @@ print(f"For η_1, η_2 = (0, 1), sqrt_σ = {sqrt_σ:.5f}")
412
413
```
413
414
414
415
```{code-cell} ipython3
415
- # Case 2: η_1, η_2 = (0, 1 )
416
+ # Case 2: η_1, η_2 = (1, 0 )
416
417
ηs = (1, 0)
417
418
sqrt_σ = y(1, ηs) / y(0, ηs) - 1
418
419
@@ -468,33 +469,37 @@ Now we implement the algorithm above.
468
469
First we write a function that iterates $M$
469
470
470
471
```{code-cell} ipython3
471
- def iterate_M(x_0, M, num_steps):
472
- # Eigendecomposition of M
473
- Λ, V_inv = np.linalg.eig(M)
474
- V = np.linalg.inv(V_inv)
472
+ def iterate_M(x_0, M, num_steps, dtype=np.float64):
475
473
476
- print(f"eigenvalues:\n{Λ}")
477
- print(f"eigenvectors:\n{V}" )
478
- print(f"inverse eigenvectors:\n{ V_inv}" )
474
+ # Eigendecomposition of M
475
+ Λ, V = np.linalg.eig(M )
476
+ V_inv = np.linalg.inv(V )
479
477
480
478
# Initialize the array to store results
481
- x = np.zeros((x_0.shape[0], num_steps))
479
+ xs = np.zeros((x_0.shape[0],
480
+ num_steps + 1))
482
481
483
482
# Perform the iterations
483
+ xs[:, 0] = x_0
484
484
for t in range(num_steps):
485
- x [:, t] = V @ np.diag(Λ**t) @ V_inv @ x_0
485
+ xs [:, t + 1 ] = M @ xs[:, t]
486
486
487
- return x , Λ, V, V_inv
487
+ return xs , Λ, V, V_inv
488
488
489
489
# Define the state transition matrix M
490
- M = np.array([[2, -(1 - σ)],
491
- [1, 0]])
490
+ M = np.array([
491
+ [2, -(1 - σ)],
492
+ [1, 0]])
492
493
493
494
# Initial condition vector x_0
494
495
x_0 = np.array([2, 2])
495
496
496
497
# Perform the iteration
497
498
xs, Λ, V, V_inv = iterate_M(x_0, M, num_steps=100)
499
+
500
+ print(f"eigenvalues:\n{Λ}")
501
+ print(f"eigenvectors:\n{V}")
502
+ print(f"inverse eigenvectors:\n{V_inv}")
498
503
```
499
504
500
505
Let's compare the eigenvalues to the roots {eq}`eq:secretweapon` of equation
@@ -507,37 +512,48 @@ print(f"roots: {np.round(roots, 8)}")
507
512
508
513
Hence we confirmed {eq}`eq:eigen_sqrt`.
509
514
510
- **Request to Humphrey**
511
- Please beautify the following description and code.
512
-
513
515
Information about the square root we are after is also contained
514
516
in the two eigenvectors.
515
517
516
- Indeed, each eigenvector is just a two dimensional subspace of ${\bf R}^3$ pinned down
517
- by dynamics of the form
518
+ Indeed, each eigenvector is just a two-dimensional subspace of ${\mathbb R}^3$ pinned down by dynamics of the form
518
519
519
520
$$
520
- y_ {t} = \delta_i y_ {t-1}, \quad i = 1, 2
521
+ y_ {t} = \lambda_i y_ {t-1}, \quad i = 1, 2
521
522
$$ (eq:invariantsub101)
522
523
523
524
that we encountered above in equation {eq}`eq:2diff8` above.
524
525
525
- In equation {eq}`eq:invariantsub101`, the $i$th $\delta_i$ equals the $V_{i, 0}/V_{i,1}$.
526
-
527
- The following code cell verifies this for our example.
526
+ In equation {eq}`eq:invariantsub101`, the $i$th $\lambda_i$ equals the $V_{i, 1}/V_{i,2}$.
528
527
528
+ The following graph verifies this for our example.
529
529
530
530
```{code-cell} ipython3
531
- s1 = V[0,0] / V[0,1]
532
- s2 = V[1,0] / V[1,1]
533
- print ("(s1, s2) = ", s1, s2)
534
- g1 = 1 + np.sqrt(2)
535
- g2 = 1 - np.sqrt(2)
536
- print("(g1, g2) = ", g1, g2)
531
+ # Plotting the eigenvectors
532
+ plt.figure(figsize=(8, 8))
533
+
534
+ plt.quiver(0, 0, V[0, 0], V[0, 1], angles='xy', scale_units='xy',
535
+ scale=1, color='C0', label=fr'$\lambda_1={np.round(Λ[0], 4)}$')
536
+ plt.quiver(0, 0, V[1, 0], V[1, 1], angles='xy', scale_units='xy',
537
+ scale=1, color='C1', label=fr'$\lambda_2={np.round(Λ[1], 4)}$')
538
+
539
+ # Annotating the slopes
540
+ plt.text(V[0, 0]-0.5, V[0, 1]*1.2,
541
+ r'slope=$\frac{V_{1,1}}{V_{1,2}}=$'+f'{np.round(V[0, 0] / V[0, 1], 4)}',
542
+ fontsize=12, color='C0')
543
+ plt.text(V[1, 0]-0.5, V[1, 1]*1.2,
544
+ r'slope=$\frac{V_{2,1}}{V_{2,2}}=$'+f'{np.round(V[1, 0] / V[1, 1], 4)}',
545
+ fontsize=12, color='C1')
546
+
547
+ # Adding labels
548
+ plt.axhline(0, color='grey', linewidth=0.5, alpha=0.4)
549
+ plt.axvline(0, color='grey', linewidth=0.5, alpha=0.4)
550
+ plt.legend()
551
+
552
+ plt.xlim(-1.5, 1.5)
553
+ plt.ylim(-1.5, 1.5)
554
+ plt.show()
537
555
```
538
556
539
- **End of request to Humphrey**
540
-
541
557
## Invariant Subspace Approach
542
558
543
559
The preceding calculation indicates that we can use the eigenvectors $V$ to construct 2-dimensional **invariant subspaces**.
565
581
$$
566
582
567
583
V = \begin{bmatrix} V_ {1,1} & V_ {1,2} \cr
568
- V_ {2,2 } & V_ {2,2} \end{bmatrix}, \quad
584
+ V_ {2,1 } & V_ {2,2} \end{bmatrix}, \quad
569
585
V^{-1} = \begin{bmatrix} V^{1,1} & V^{1,2} \cr
570
- V^{2,2 } & V^{2,2} \end{bmatrix}
586
+ V^{2,1 } & V^{2,2} \end{bmatrix}
571
587
$$
572
588
573
589
Notice that it follows from
@@ -634,8 +650,11 @@ Let's verify {eq}`eq:deactivate1` and {eq}`eq:deactivate2` below
634
650
To deactivate $\lambda_1$ we use {eq}`eq:deactivate1`
635
651
636
652
```{code-cell} ipython3
653
+ Λ, V = np.linalg.eig(M)
654
+
637
655
xd_1 = np.array((x_0[0],
638
- V[1,1] * (1/V[0,1]) * x_0[0]))
656
+ V[1,1]/V[0,1] * x_0[0]),
657
+ dtype=np.float64)
639
658
640
659
# Compute x_{1,0}^*
641
660
np.round(V_inv @ xd_1, 8)
@@ -649,24 +668,57 @@ Now we deactivate $\lambda_2$ using {eq}`eq:deactivate2`
649
668
650
669
```{code-cell} ipython3
651
670
xd_2 = np.array((x_0[0],
652
- V[1,0] * (1/V[0,0]) * x_0[0]))
671
+ V[1,0]/V[0,0] * x_0[0]),
672
+ dtype=np.float64)
653
673
654
674
# Compute x_{2,0}^*
655
675
np.round(V_inv @ xd_2, 8)
656
676
```
657
677
658
678
We find $x_{2,0}^* = 0$.
659
679
660
- +++
680
+ ```{code-cell} ipython3
681
+ # Simulate the difference equation with muted λ1
682
+ num_steps = 10
683
+ xs_λ1, Λ, _, _ = iterate_M(xd_1, M, num_steps)
684
+
685
+ # Simulate the difference equation with muted λ2
686
+ xs_λ2, _, _, _ = iterate_M(xd_2, M, num_steps)
687
+
688
+ # Compute ratios y_t / y_{t-1} with higher precision
689
+ ratios_λ1 = xs_λ1[1, 1:] / xs_λ1[1, :-1] # Adjusted to second component
690
+ ratios_λ2 = xs_λ2[1, 1:] / xs_λ2[1, :-1] # Adjusted to second component
691
+
692
+ # Plot the ratios for y_t with higher precision
693
+ plt.figure(figsize=(14, 6))
694
+
695
+ plt.subplot(1, 2, 1)
696
+ plt.plot(np.round(ratios_λ1, 6), label='Ratios $y_t / y_{t-1}$ after muting $\lambda_1$', color='blue')
697
+ plt.axhline(y=Λ[1], color='red', linestyle='--', label='$\lambda_2$')
698
+ plt.xlabel('Time')
699
+ plt.ylabel('Ratio $y_t / y_{t-1}$')
700
+ plt.title('Ratios after Muting $\lambda_1$')
701
+ plt.legend()
702
+
703
+ plt.subplot(1, 2, 2)
704
+ plt.plot(ratios_λ2, label='Ratios $y_t / y_{t-1}$ after muting $\lambda_2$', color='orange')
705
+ plt.axhline(y=Λ[0], color='green', linestyle='--', label='$\lambda_1$')
706
+ plt.xlabel('Time')
707
+ plt.ylabel('Ratio $y_t / y_{t-1}$')
708
+ plt.title('Ratios after Muting $\lambda_2$')
709
+ plt.legend()
710
+
711
+ plt.tight_layout()
712
+ plt.show()
713
+ ```
661
714
662
715
Here we compare $V_{2,2} V_{1,2}^{-1}$ and $V_{2,1} V_{1,1}^{-1}$ with the roots we computed above
663
716
664
717
```{code-cell} ipython3
665
- np.round((V[1,1]*(1 /V[0,1]) ,
666
- V[1,0]*(1 /V[0,0]) ), 8)
718
+ np.round((V[1,1]/V[0,1],
719
+ V[1,0]/V[0,0]), 8)
667
720
```
668
721
669
-
670
722
## Concluding Remarks
671
723
672
724
This lecture sets the stage for many other applications of the **invariant subspace** methods.
0 commit comments