Skip to content

Commit ebbdd1b

Browse files
authored
Add LU and PLU decompositions. (#26)
* Add LU and PLU decompositions. * Refactor & add description for LUP decomposition. * Add docs for algorithms & fix some minor issues. * Codestyle & format. * Nullables added
1 parent 8df6dda commit ebbdd1b

13 files changed

+550
-64
lines changed

.gitignore

+2-1
Original file line numberDiff line numberDiff line change
@@ -3,4 +3,5 @@
33
*.vs*
44
*/bin/*
55
*/obj/*
6-
*/packages/*
6+
*/packages/*
7+
GenericTensor.sln.DotSettings.user

GenericTensor.sln.DotSettings.user

-2
This file was deleted.

GenericTensor/Core/Exceptions.cs

+9
Original file line numberDiff line numberDiff line change
@@ -55,4 +55,13 @@ public class InvalidDeterminantException : DataException
5555
internal InvalidDeterminantException(string msg) : base(msg) {}
5656
internal InvalidDeterminantException() : base() {}
5757
}
58+
59+
/// <summary>
60+
/// Thrown when there is no decomposition for provided matrix
61+
/// </summary>
62+
public class ImpossibleDecomposition : DataException
63+
{
64+
internal ImpossibleDecomposition(string msg) : base(msg) {}
65+
internal ImpossibleDecomposition() : base() {}
66+
}
5867
}

GenericTensor/Declaration.cs

+115-24
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
#region copyright
2+
23
/*
34
* MIT License
45
*
@@ -22,6 +23,7 @@
2223
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
2324
* SOFTWARE.
2425
*/
26+
2527
#endregion
2628

2729

@@ -40,6 +42,7 @@ public sealed partial class GenTensor<T, TWrapper>
4042
: ICloneable where TWrapper : struct, IOperations<T>
4143
{
4244
#region Composition
45+
4346
/// <summary>
4447
/// Creates a new axis that is put backward
4548
/// and then sets all elements as children
@@ -67,6 +70,7 @@ public static GenTensor<T, TWrapper> Concat(GenTensor<T, TWrapper> a, GenTensor<
6770
#endregion
6871

6972
#region Constructors
73+
7074
/// <summary>
7175
/// Creates a tensor whose all matrices are identity matrices
7276
/// <para>1 is achieved with <see cref="IOperations{T}.CreateOne"/></para>
@@ -145,36 +149,31 @@ public static GenTensor<T, TWrapper> CreateMatrix(int width, int height)
145149
/// (its only argument is an array of integers which are indices of the tensor)
146150
/// </summary>
147151
[MethodImpl(MethodImplOptions.AggressiveInlining)]
148-
public static GenTensor<T, TWrapper> CreateTensor(TensorShape shape, Func<int[], T> operation, Threading threading = Threading.Single)
149-
=> Constructors<T, TWrapper>.CreateTensor(shape, operation, threading);
152+
public static GenTensor<T, TWrapper> CreateTensor(TensorShape shape, Func<int[], T> operation, Threading threading = Threading.Single) => Constructors<T, TWrapper>.CreateTensor(shape, operation, threading);
150153

151154
/// <summary>
152155
/// Creates a tensor from an array
153156
/// </summary>
154157
[MethodImpl(MethodImplOptions.AggressiveInlining)]
155-
public static GenTensor<T, TWrapper> CreateTensor(T[] data)
156-
=> Constructors<T, TWrapper>.CreateTensor(data);
158+
public static GenTensor<T, TWrapper> CreateTensor(T[] data) => Constructors<T, TWrapper>.CreateTensor(data);
157159

158160
/// <summary>
159161
/// Creates a tensor from a two-dimensional array
160162
/// </summary>
161163
[MethodImpl(MethodImplOptions.AggressiveInlining)]
162-
public static GenTensor<T, TWrapper> CreateTensor(T[,] data)
163-
=> Constructors<T, TWrapper>.CreateTensor(data);
164+
public static GenTensor<T, TWrapper> CreateTensor(T[,] data) => Constructors<T, TWrapper>.CreateTensor(data);
164165

165166
/// <summary>
166167
/// Creates a tensor from a three-dimensional array
167168
/// </summary>
168169
[MethodImpl(MethodImplOptions.AggressiveInlining)]
169-
public static GenTensor<T, TWrapper> CreateTensor(T[,,] data)
170-
=> Constructors<T, TWrapper>.CreateTensor(data);
170+
public static GenTensor<T, TWrapper> CreateTensor(T[,,] data) => Constructors<T, TWrapper>.CreateTensor(data);
171171

172172
/// <summary>
173173
/// Creates a tensor from an n-dimensional array
174174
/// </summary>
175175
[MethodImpl(MethodImplOptions.AggressiveInlining)]
176-
public static GenTensor<T, TWrapper> CreateTensor(Array data)
177-
=> Constructors<T, TWrapper>.CreateTensor(data);
176+
public static GenTensor<T, TWrapper> CreateTensor(Array data) => Constructors<T, TWrapper>.CreateTensor(data);
178177

179178
#endregion
180179

@@ -186,6 +185,42 @@ public static GenTensor<T, TWrapper> CreateTensor(Array data)
186185
/// </summary>
187186
public GenTensor<T, TWrapper> RowEchelonFormSimple()
188187
=> EchelonForm<T, TWrapper>.RowEchelonFormSimple(this);
188+
189+
/// <summary>
190+
/// Decomposes a matrix into a triangular one.
191+
/// Is of the Row Echelon Form (leading elements might be differ from ones).
192+
///
193+
/// In addition, returns a permutation that algorithm performs with rows.
194+
/// Permutation array is size of rows there are in matrix.
195+
///
196+
/// Initial state of that array is:
197+
/// 1 2 3 ... numberOfRows
198+
///
199+
/// For example, algorithm swaps first and third rows then:
200+
/// 3 2 1 ... numberOfRows
201+
///
202+
/// It can be useful performing decompositions
203+
/// </summary>
204+
public (GenTensor<T, TWrapper>, int[]) RowEchelonFormPermuteSimple()
205+
=> EchelonForm<T, TWrapper>.RowEchelonFormPermuteSimple(this);
206+
207+
/// <summary>
208+
/// Decomposes a matrix into a triangular one.
209+
/// Is of the Row Echelon Form (leading elements might be differ from ones).
210+
///
211+
/// In addition, returns a permutation that algorithm performs with rows.
212+
/// Permutation array is size of rows there are in matrix.
213+
///
214+
/// Initial state of that array is:
215+
/// 1 2 3 ... numberOfRows
216+
///
217+
/// For example, algorithm swaps first and third rows then:
218+
/// 3 2 1 ... numberOfRows
219+
///
220+
/// It can be useful performing decompositions
221+
/// </summary>
222+
public (GenTensor<T, TWrapper>, int[]) RowEchelonFormPermuteSafeDivision()
223+
=> EchelonForm<T, TWrapper>.RowEchelonFormPermuteSafeDivision(this);
189224

190225
/// <summary>
191226
/// Decomposes a matrix into a triangular one.
@@ -196,7 +231,6 @@ public GenTensor<T, TWrapper> RowEchelonFormSafeDivision()
196231
=> EchelonForm<T, TWrapper>.RowEchelonFormSafeDivision(this);
197232

198233

199-
200234
/// <summary>
201235
/// Decomposes a matrix into a triangular one.
202236
/// Is of the Row Echelon Form (leading elements are ones).
@@ -215,7 +249,6 @@ public GenTensor<T, TWrapper> RowEchelonFormLeadingOnesSafeDivision()
215249
=> EchelonForm<T, TWrapper>.RowEchelonFormLeadingOnesSafeDivision(this);
216250

217251

218-
219252
/// <summary>
220253
/// Finds the reduced echelon form of a matrix.
221254
/// </summary>
@@ -228,6 +261,16 @@ public GenTensor<T, TWrapper> ReducedRowEchelonFormSimple()
228261
/// </summary>
229262
public GenTensor<T, TWrapper> ReducedRowEchelonFormSafeDivision()
230263
=> EchelonForm<T, TWrapper>.ReducedRowEchelonFormSafeDivision(this);
264+
265+
/// <summary>
266+
/// Finds the reduced echelon form of a matrix.
267+
/// Uses safe division, i. e. perform division only when computing the final result.
268+
///
269+
/// Additionally returns row permutation
270+
/// </summary>
271+
public (GenTensor<T, TWrapper>, int[]) ReducedRowEchelonFormPermuteSafeDivision()
272+
=> EchelonForm<T, TWrapper>.ReducedRowEchelonFormPermuteSafeDivision(this);
273+
231274
#endregion
232275

233276
#region Determinant
@@ -307,6 +350,7 @@ public void InvertMatrix()
307350
/// </summary>
308351
public void TensorMatrixInvert()
309352
=> Inversion<T, TWrapper>.TensorMatrixInvert(this);
353+
310354
#endregion
311355

312356
#region Elementary matrix operations
@@ -398,70 +442,80 @@ public static GenTensor<T, TWrapper> TensorMatrixMultiply(GenTensor<T, TWrapper>
398442
/// T1 + T2
399443
/// </summary>
400444
[MethodImpl(MethodImplOptions.AggressiveInlining)]
401-
public static GenTensor<T, TWrapper> PiecewiseAdd(GenTensor<T, TWrapper> a, GenTensor<T, TWrapper> b, Threading threading = Threading.Single)
445+
public static GenTensor<T, TWrapper> PiecewiseAdd(GenTensor<T, TWrapper> a, GenTensor<T, TWrapper> b,
446+
Threading threading = Threading.Single)
402447
=> PiecewiseArithmetics<T, TWrapper>.PiecewiseAdd(a, b, threading);
403448

404449
/// <summary>
405450
/// T1 - T2
406451
/// </summary>
407452
[MethodImpl(MethodImplOptions.AggressiveInlining)]
408-
public static GenTensor<T, TWrapper> PiecewiseSubtract(GenTensor<T, TWrapper> a, GenTensor<T, TWrapper> b, Threading threading = Threading.Single)
453+
public static GenTensor<T, TWrapper> PiecewiseSubtract(GenTensor<T, TWrapper> a, GenTensor<T, TWrapper> b,
454+
Threading threading = Threading.Single)
409455
=> PiecewiseArithmetics<T, TWrapper>.PiecewiseSubtract(a, b, threading);
410456

411457
/// <summary>
412458
/// T1 * T2
413459
/// </summary>
414460
[MethodImpl(MethodImplOptions.AggressiveInlining)]
415-
public static GenTensor<T, TWrapper> PiecewiseMultiply(GenTensor<T, TWrapper> a, GenTensor<T, TWrapper> b, Threading threading = Threading.Single)
461+
public static GenTensor<T, TWrapper> PiecewiseMultiply(GenTensor<T, TWrapper> a, GenTensor<T, TWrapper> b,
462+
Threading threading = Threading.Single)
416463
=> PiecewiseArithmetics<T, TWrapper>.PiecewiseMultiply(a, b, threading);
417464

418465
/// <summary>
419466
/// T1 / T2
420467
/// </summary>
421468
[MethodImpl(MethodImplOptions.AggressiveInlining)]
422-
public static GenTensor<T, TWrapper> PiecewiseDivide(GenTensor<T, TWrapper> a, GenTensor<T, TWrapper> b, Threading threading = Threading.Single)
469+
public static GenTensor<T, TWrapper> PiecewiseDivide(GenTensor<T, TWrapper> a, GenTensor<T, TWrapper> b,
470+
Threading threading = Threading.Single)
423471
=> PiecewiseArithmetics<T, TWrapper>.PiecewiseDivide(a, b, threading);
424472

425473
/// <summary>
426474
/// T1 + const
427475
/// </summary>
428476
[MethodImpl(MethodImplOptions.AggressiveInlining)]
429-
public static GenTensor<T, TWrapper> PiecewiseAdd(GenTensor<T, TWrapper> a, T b, Threading threading = Threading.Single)
477+
public static GenTensor<T, TWrapper> PiecewiseAdd(GenTensor<T, TWrapper> a, T b,
478+
Threading threading = Threading.Single)
430479
=> PiecewiseArithmetics<T, TWrapper>.PiecewiseAdd(a, b, threading);
431480

432481
/// <summary>
433482
/// T1 - const
434483
/// </summary>
435484
[MethodImpl(MethodImplOptions.AggressiveInlining)]
436-
public static GenTensor<T, TWrapper> PiecewiseSubtract(GenTensor<T, TWrapper> a, T b, Threading threading = Threading.Single)
485+
public static GenTensor<T, TWrapper> PiecewiseSubtract(GenTensor<T, TWrapper> a, T b,
486+
Threading threading = Threading.Single)
437487
=> PiecewiseArithmetics<T, TWrapper>.PiecewiseSubtract(a, b, threading);
438488

439489
/// <summary>
440490
/// const - T1
441491
/// </summary>
442492
[MethodImpl(MethodImplOptions.AggressiveInlining)]
443-
public static GenTensor<T, TWrapper> PiecewiseSubtract(T a, GenTensor<T, TWrapper> b, Threading threading = Threading.Single)
493+
public static GenTensor<T, TWrapper> PiecewiseSubtract(T a, GenTensor<T, TWrapper> b,
494+
Threading threading = Threading.Single)
444495
=> PiecewiseArithmetics<T, TWrapper>.PiecewiseSubtract(a, b, threading);
445496

446497
/// <summary>
447498
/// T1 * const
448499
/// </summary>
449500
[MethodImpl(MethodImplOptions.AggressiveInlining)]
450-
public static GenTensor<T, TWrapper> PiecewiseMultiply(GenTensor<T, TWrapper> a, T b, Threading threading = Threading.Single)
501+
public static GenTensor<T, TWrapper> PiecewiseMultiply(GenTensor<T, TWrapper> a, T b,
502+
Threading threading = Threading.Single)
451503
=> PiecewiseArithmetics<T, TWrapper>.PiecewiseMultiply(a, b, threading);
452504

453505
/// <summary>
454506
/// T1 / const
455507
/// </summary>
456508
[MethodImpl(MethodImplOptions.AggressiveInlining)]
457-
public static GenTensor<T, TWrapper> PiecewiseDivide(GenTensor<T, TWrapper> a, T b, Threading threading = Threading.Single)
509+
public static GenTensor<T, TWrapper> PiecewiseDivide(GenTensor<T, TWrapper> a, T b,
510+
Threading threading = Threading.Single)
458511
=> PiecewiseArithmetics<T, TWrapper>.PiecewiseDivide(a, b, threading);
459512

460513
/// <summary>
461514
/// const / T1
462515
/// </summary>
463516
[MethodImpl(MethodImplOptions.AggressiveInlining)]
464-
public static GenTensor<T, TWrapper> PiecewiseDivide(T a, GenTensor<T, TWrapper> b, Threading threading = Threading.Single)
517+
public static GenTensor<T, TWrapper> PiecewiseDivide(T a, GenTensor<T, TWrapper> b,
518+
Threading threading = Threading.Single)
465519
=> PiecewiseArithmetics<T, TWrapper>.PiecewiseDivide(a, b, threading);
466520

467521
#endregion
@@ -493,6 +547,40 @@ public GenTensor<T, TWrapper> TensorMatrixPower(int power, Threading threading =
493547

494548
#endregion
495549

550+
#region Decompositions
551+
552+
/// <summary>
553+
/// https://www.geeksforgeeks.org/l-u-decomposition-system-linear-equations/
554+
/// </summary>
555+
/// <returns>
556+
/// LU decomposition
557+
/// </returns>
558+
public (GenTensor<T, TWrapper> l, GenTensor<T, TWrapper> u) LuDecomposition()
559+
=> LuDecomposition<T, TWrapper>.Decompose(this);
560+
561+
/// <summary>
562+
/// Find PLU decomposition: matrices P, L, U such that for original matrix A: PA = LU.
563+
///
564+
/// P stands for permutation matrix, permutations are made during the Gauss elimination process
565+
/// L stands for LIBERTY lower triangle matrix
566+
/// U stands for upper triangle matrix
567+
///
568+
/// Algorithm, given matrix A:
569+
/// 1. Form an adjacent matrix (A|E)
570+
/// 2. Find row echelon form of that matrix (U|L_0) and permutation of the rows
571+
/// 3. Form permutation matrix P such that P_ij = \delta_{}
572+
/// 4. Compute L = P * L_0^{-1}
573+
///
574+
/// Results are: P, L, U
575+
/// </summary>
576+
/// <returns>
577+
/// LUP decomposition of given matrix
578+
/// </returns>
579+
public (GenTensor<T, TWrapper> p, GenTensor<T, TWrapper> l, GenTensor<T, TWrapper> u) PluDecomposition()
580+
=> PluDecomposition<T, TWrapper>.Decompose(this);
581+
582+
#endregion
583+
496584
#region ToString & GetHashCode
497585

498586
/// <inheritdoc/>
@@ -517,7 +605,8 @@ public static GenTensor<T, TWrapper> VectorCrossProduct(GenTensor<T, TWrapper> a
517605
/// <summary>
518606
/// Calls VectorCrossProduct for every vector in the tensor
519607
/// </summary>
520-
public static GenTensor<T, TWrapper> TensorVectorCrossProduct(GenTensor<T, TWrapper> a, GenTensor<T, TWrapper> b)
608+
public static GenTensor<T, TWrapper> TensorVectorCrossProduct(GenTensor<T, TWrapper> a,
609+
GenTensor<T, TWrapper> b)
521610
=> VectorProduct<T, TWrapper>.TensorVectorCrossProduct(a, b);
522611

523612
/// <summary>
@@ -562,6 +651,7 @@ public GenTensor<T, TWrapper> Forward()
562651
#endregion
563652

564653
#region Serialization
654+
565655
/*
566656
* Serialization protocol:
567657
*
@@ -595,6 +685,7 @@ public byte[] Serialize()
595685
/// </returns>
596686
public static GenTensor<T, TWrapper> Deserialize(byte[] data)
597687
=> Serializer<T, TWrapper>.Deserialize(data);
688+
598689
#endregion
599690
}
600-
}
691+
}

GenericTensor/Functions/Determinant.cs

+1-1
Original file line numberDiff line numberDiff line change
@@ -87,7 +87,7 @@ internal static T DeterminantGaussianSafeDivision(GenTensor<T, TWrapper> t, int
8787
return t.GetValueNoCheck(0, 0);
8888

8989
var n = diagLength;
90-
var elemMatrix = EchelonForm<T, TWrapper>.InnerGaussianEliminationSafeDivision(t, n, n, out var swapCount);
90+
var elemMatrix = EchelonForm<T, TWrapper>.InnerGaussianEliminationSafeDivision(t, n, n, null, out var swapCount);
9191

9292
var det = default(EchelonForm<T, TWrapper>.WrapperSafeDivisionWrapper<T, TWrapper>).CreateOne();
9393
for (int i = 0; i < n; i++)

0 commit comments

Comments
 (0)