-
Notifications
You must be signed in to change notification settings - Fork 1.5k
/
PowerIteration.cs
86 lines (77 loc) · 3.82 KB
/
PowerIteration.cs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
using System;
using System.Linq;
using Utilities.Extensions;
namespace Algorithms.LinearAlgebra.Eigenvalue;
/// <summary>
/// Power iteration method - eigenvalue numeric algorithm, based on recurrent relation:
/// Li+1 = (A * Li) / || A * Li ||, where Li - eigenvector approximation.
/// </summary>
public static class PowerIteration
{
/// <summary>
/// Returns approximation of the dominant eigenvalue and eigenvector of <paramref name="source" /> matrix.
/// </summary>
/// <list type="bullet">
/// <item>
/// <description>The algorithm will not converge if the start vector is orthogonal to the eigenvector.</description>
/// </item>
/// <item>
/// <description>The <paramref name="source" /> matrix must be square-shaped.</description>
/// </item>
/// </list>
/// <param name="source">Source square-shaped matrix.</param>
/// <param name="startVector">Start vector.</param>
/// <param name="error">Accuracy of the result.</param>
/// <returns>Dominant eigenvalue and eigenvector pair.</returns>
/// <exception cref="ArgumentException">The <paramref name="source" /> matrix is not square-shaped.</exception>
/// <exception cref="ArgumentException">The length of the start vector doesn't equal the size of the source matrix.</exception>
public static (double Eigenvalue, double[] Eigenvector) Dominant(
double[,] source,
double[] startVector,
double error = 0.00001)
{
if (source.GetLength(0) != source.GetLength(1))
{
throw new ArgumentException("The source matrix is not square-shaped.");
}
if (source.GetLength(0) != startVector.Length)
{
throw new ArgumentException(
"The length of the start vector doesn't equal the size of the source matrix.");
}
double eigenNorm;
double[] previousEigenVector;
double[] currentEigenVector = startVector;
do
{
previousEigenVector = currentEigenVector;
currentEigenVector = source.Multiply(
previousEigenVector.ToColumnVector())
.ToRowVector();
eigenNorm = currentEigenVector.Magnitude();
currentEigenVector = currentEigenVector.Select(x => x / eigenNorm).ToArray();
}
while (Math.Abs(currentEigenVector.Dot(previousEigenVector)) < 1.0 - error);
var eigenvalue = source.Multiply(currentEigenVector.ToColumnVector()).ToRowVector().Magnitude();
return (eigenvalue, Eigenvector: currentEigenVector);
}
/// <summary>
/// Returns approximation of the dominant eigenvalue and eigenvector of <paramref name="source" /> matrix.
/// Random normalized vector is used as the start vector to decrease chance of orthogonality to the eigenvector.
/// </summary>
/// <list type="bullet">
/// <item>
/// <description>The algorithm will not converge if the start vector is orthogonal to the eigenvector.</description>
/// </item>
/// <item>
/// <description>The <paramref name="source" /> matrix should be square-shaped.</description>
/// </item>
/// </list>
/// <param name="source">Source square-shaped matrix.</param>
/// <param name="error">Accuracy of the result.</param>
/// <returns>Dominant eigenvalue and eigenvector pair.</returns>
/// <exception cref="ArgumentException">The <paramref name="source" /> matrix is not square-shaped.</exception>
/// <exception cref="ArgumentException">The length of the start vector doesn't equal the size of the source matrix.</exception>
public static (double Eigenvalue, double[] Eigenvector) Dominant(double[,] source, double error = 0.00001) =>
Dominant(source, new Random().NextVector(source.GetLength(1)), error);
}