| ------------------------------------------------------------------------------ |
| -- -- |
| -- GNAT RUN-TIME COMPONENTS -- |
| -- -- |
| -- ADA.NUMERICS.GENERIC_REAL_ARRAYS -- |
| -- -- |
| -- B o d y -- |
| -- -- |
| -- Copyright (C) 2006-2022, Free Software Foundation, Inc. -- |
| -- -- |
| -- GNAT is free software; you can redistribute it and/or modify it under -- |
| -- terms of the GNU General Public License as published by the Free Soft- -- |
| -- ware Foundation; either version 3, or (at your option) any later ver- -- |
| -- sion. GNAT is distributed in the hope that it will be useful, but WITH- -- |
| -- OUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY -- |
| -- or FITNESS FOR A PARTICULAR PURPOSE. -- |
| -- -- |
| -- As a special exception under Section 7 of GPL version 3, you are granted -- |
| -- additional permissions described in the GCC Runtime Library Exception, -- |
| -- version 3.1, as published by the Free Software Foundation. -- |
| -- -- |
| -- You should have received a copy of the GNU General Public License and -- |
| -- a copy of the GCC Runtime Library Exception along with this program; -- |
| -- see the files COPYING3 and COPYING.RUNTIME respectively. If not, see -- |
| -- <http://www.gnu.org/licenses/>. -- |
| -- -- |
| -- GNAT was originally developed by the GNAT team at New York University. -- |
| -- Extensive contributions were provided by Ada Core Technologies Inc. -- |
| -- -- |
| ------------------------------------------------------------------------------ |
| |
| -- This version of Generic_Real_Arrays avoids the use of BLAS and LAPACK. One |
| -- reason for this is new Ada 2012 requirements that prohibit algorithms such |
| -- as Strassen's algorithm, which may be used by some BLAS implementations. In |
| -- addition, some platforms lacked suitable compilers to compile the reference |
| -- BLAS/LAPACK implementation. Finally, on some platforms there are more |
| -- floating point types than supported by BLAS/LAPACK. |
| |
| -- Preconditions, postconditions, ghost code, loop invariants and assertions |
| -- in this unit are meant for analysis only, not for run-time checking, as it |
| -- would be too costly otherwise. This is enforced by setting the assertion |
| -- policy to Ignore. |
| |
| pragma Assertion_Policy (Pre => Ignore, |
| Post => Ignore, |
| Ghost => Ignore, |
| Loop_Invariant => Ignore, |
| Assert => Ignore); |
| |
| with Ada.Containers.Generic_Anonymous_Array_Sort; use Ada.Containers; |
| |
| with System; use System; |
| with System.Generic_Array_Operations; use System.Generic_Array_Operations; |
| |
| package body Ada.Numerics.Generic_Real_Arrays is |
| |
| package Ops renames System.Generic_Array_Operations; |
| |
| function Is_Non_Zero (X : Real'Base) return Boolean is (X /= 0.0); |
| |
| procedure Back_Substitute is new Ops.Back_Substitute |
| (Scalar => Real'Base, |
| Matrix => Real_Matrix, |
| Is_Non_Zero => Is_Non_Zero); |
| |
| function Diagonal is new Ops.Diagonal |
| (Scalar => Real'Base, |
| Vector => Real_Vector, |
| Matrix => Real_Matrix); |
| |
| procedure Forward_Eliminate is new Ops.Forward_Eliminate |
| (Scalar => Real'Base, |
| Real => Real'Base, |
| Matrix => Real_Matrix, |
| Zero => 0.0, |
| One => 1.0); |
| |
| procedure Swap_Column is new Ops.Swap_Column |
| (Scalar => Real'Base, |
| Matrix => Real_Matrix); |
| |
| procedure Transpose is new Ops.Transpose |
| (Scalar => Real'Base, |
| Matrix => Real_Matrix); |
| |
| function Is_Symmetric (A : Real_Matrix) return Boolean is |
| (Transpose (A) = A); |
| -- Return True iff A is symmetric, see RM G.3.1 (90). |
| |
| function Is_Tiny (Value, Compared_To : Real) return Boolean is |
| (abs Compared_To + 100.0 * abs (Value) = abs Compared_To); |
| -- Return True iff the Value is much smaller in magnitude than the least |
| -- significant digit of Compared_To. |
| |
| procedure Jacobi |
| (A : Real_Matrix; |
| Values : out Real_Vector; |
| Vectors : out Real_Matrix; |
| Compute_Vectors : Boolean := True); |
| -- Perform Jacobi's eigensystem algorithm on real symmetric matrix A |
| |
| function Length is new Square_Matrix_Length (Real'Base, Real_Matrix); |
| -- Helper function that raises a Constraint_Error is the argument is |
| -- not a square matrix, and otherwise returns its length. |
| |
| procedure Rotate (X, Y : in out Real; Sin, Tau : Real); |
| -- Perform a Givens rotation |
| |
| procedure Sort_Eigensystem |
| (Values : in out Real_Vector; |
| Vectors : in out Real_Matrix); |
| -- Sort Values and associated Vectors by decreasing absolute value |
| |
| procedure Swap (Left, Right : in out Real); |
| -- Exchange Left and Right |
| |
| function Sqrt is new Ops.Sqrt (Real); |
| -- Instant a generic square root implementation here, in order to avoid |
| -- instantiating a complete copy of Generic_Elementary_Functions. |
| -- Speed of the square root is not a big concern here. |
| |
| ------------ |
| -- Rotate -- |
| ------------ |
| |
| procedure Rotate (X, Y : in out Real; Sin, Tau : Real) is |
| Old_X : constant Real := X; |
| Old_Y : constant Real := Y; |
| begin |
| X := Old_X - Sin * (Old_Y + Old_X * Tau); |
| Y := Old_Y + Sin * (Old_X - Old_Y * Tau); |
| end Rotate; |
| |
| ---------- |
| -- Swap -- |
| ---------- |
| |
| procedure Swap (Left, Right : in out Real) is |
| Temp : constant Real := Left; |
| begin |
| Left := Right; |
| Right := Temp; |
| end Swap; |
| |
| -- Instantiating the following subprograms directly would lead to |
| -- name clashes, so use a local package. |
| |
| package Instantiations is |
| |
| function "+" is new |
| Vector_Elementwise_Operation |
| (X_Scalar => Real'Base, |
| Result_Scalar => Real'Base, |
| X_Vector => Real_Vector, |
| Result_Vector => Real_Vector, |
| Operation => "+"); |
| |
| function "+" is new |
| Matrix_Elementwise_Operation |
| (X_Scalar => Real'Base, |
| Result_Scalar => Real'Base, |
| X_Matrix => Real_Matrix, |
| Result_Matrix => Real_Matrix, |
| Operation => "+"); |
| |
| function "+" is new |
| Vector_Vector_Elementwise_Operation |
| (Left_Scalar => Real'Base, |
| Right_Scalar => Real'Base, |
| Result_Scalar => Real'Base, |
| Left_Vector => Real_Vector, |
| Right_Vector => Real_Vector, |
| Result_Vector => Real_Vector, |
| Operation => "+"); |
| |
| function "+" is new |
| Matrix_Matrix_Elementwise_Operation |
| (Left_Scalar => Real'Base, |
| Right_Scalar => Real'Base, |
| Result_Scalar => Real'Base, |
| Left_Matrix => Real_Matrix, |
| Right_Matrix => Real_Matrix, |
| Result_Matrix => Real_Matrix, |
| Operation => "+"); |
| |
| function "-" is new |
| Vector_Elementwise_Operation |
| (X_Scalar => Real'Base, |
| Result_Scalar => Real'Base, |
| X_Vector => Real_Vector, |
| Result_Vector => Real_Vector, |
| Operation => "-"); |
| |
| function "-" is new |
| Matrix_Elementwise_Operation |
| (X_Scalar => Real'Base, |
| Result_Scalar => Real'Base, |
| X_Matrix => Real_Matrix, |
| Result_Matrix => Real_Matrix, |
| Operation => "-"); |
| |
| function "-" is new |
| Vector_Vector_Elementwise_Operation |
| (Left_Scalar => Real'Base, |
| Right_Scalar => Real'Base, |
| Result_Scalar => Real'Base, |
| Left_Vector => Real_Vector, |
| Right_Vector => Real_Vector, |
| Result_Vector => Real_Vector, |
| Operation => "-"); |
| |
| function "-" is new |
| Matrix_Matrix_Elementwise_Operation |
| (Left_Scalar => Real'Base, |
| Right_Scalar => Real'Base, |
| Result_Scalar => Real'Base, |
| Left_Matrix => Real_Matrix, |
| Right_Matrix => Real_Matrix, |
| Result_Matrix => Real_Matrix, |
| Operation => "-"); |
| |
| function "*" is new |
| Scalar_Vector_Elementwise_Operation |
| (Left_Scalar => Real'Base, |
| Right_Scalar => Real'Base, |
| Result_Scalar => Real'Base, |
| Right_Vector => Real_Vector, |
| Result_Vector => Real_Vector, |
| Operation => "*"); |
| |
| function "*" is new |
| Scalar_Matrix_Elementwise_Operation |
| (Left_Scalar => Real'Base, |
| Right_Scalar => Real'Base, |
| Result_Scalar => Real'Base, |
| Right_Matrix => Real_Matrix, |
| Result_Matrix => Real_Matrix, |
| Operation => "*"); |
| |
| function "*" is new |
| Vector_Scalar_Elementwise_Operation |
| (Left_Scalar => Real'Base, |
| Right_Scalar => Real'Base, |
| Result_Scalar => Real'Base, |
| Left_Vector => Real_Vector, |
| Result_Vector => Real_Vector, |
| Operation => "*"); |
| |
| function "*" is new |
| Matrix_Scalar_Elementwise_Operation |
| (Left_Scalar => Real'Base, |
| Right_Scalar => Real'Base, |
| Result_Scalar => Real'Base, |
| Left_Matrix => Real_Matrix, |
| Result_Matrix => Real_Matrix, |
| Operation => "*"); |
| |
| function "*" is new |
| Outer_Product |
| (Left_Scalar => Real'Base, |
| Right_Scalar => Real'Base, |
| Result_Scalar => Real'Base, |
| Left_Vector => Real_Vector, |
| Right_Vector => Real_Vector, |
| Matrix => Real_Matrix); |
| |
| function "*" is new |
| Inner_Product |
| (Left_Scalar => Real'Base, |
| Right_Scalar => Real'Base, |
| Result_Scalar => Real'Base, |
| Left_Vector => Real_Vector, |
| Right_Vector => Real_Vector, |
| Zero => 0.0); |
| |
| function "*" is new |
| Matrix_Vector_Product |
| (Left_Scalar => Real'Base, |
| Right_Scalar => Real'Base, |
| Result_Scalar => Real'Base, |
| Matrix => Real_Matrix, |
| Right_Vector => Real_Vector, |
| Result_Vector => Real_Vector, |
| Zero => 0.0); |
| |
| function "*" is new |
| Vector_Matrix_Product |
| (Left_Scalar => Real'Base, |
| Right_Scalar => Real'Base, |
| Result_Scalar => Real'Base, |
| Left_Vector => Real_Vector, |
| Matrix => Real_Matrix, |
| Result_Vector => Real_Vector, |
| Zero => 0.0); |
| |
| function "*" is new |
| Matrix_Matrix_Product |
| (Left_Scalar => Real'Base, |
| Right_Scalar => Real'Base, |
| Result_Scalar => Real'Base, |
| Left_Matrix => Real_Matrix, |
| Right_Matrix => Real_Matrix, |
| Result_Matrix => Real_Matrix, |
| Zero => 0.0); |
| |
| function "/" is new |
| Vector_Scalar_Elementwise_Operation |
| (Left_Scalar => Real'Base, |
| Right_Scalar => Real'Base, |
| Result_Scalar => Real'Base, |
| Left_Vector => Real_Vector, |
| Result_Vector => Real_Vector, |
| Operation => "/"); |
| |
| function "/" is new |
| Matrix_Scalar_Elementwise_Operation |
| (Left_Scalar => Real'Base, |
| Right_Scalar => Real'Base, |
| Result_Scalar => Real'Base, |
| Left_Matrix => Real_Matrix, |
| Result_Matrix => Real_Matrix, |
| Operation => "/"); |
| |
| function "abs" is new |
| L2_Norm |
| (X_Scalar => Real'Base, |
| Result_Real => Real'Base, |
| X_Vector => Real_Vector, |
| "abs" => "+"); |
| -- While the L2_Norm by definition uses the absolute values of the |
| -- elements of X_Vector, for real values the subsequent squaring |
| -- makes this unnecessary, so we substitute the "+" identity function |
| -- instead. |
| |
| function "abs" is new |
| Vector_Elementwise_Operation |
| (X_Scalar => Real'Base, |
| Result_Scalar => Real'Base, |
| X_Vector => Real_Vector, |
| Result_Vector => Real_Vector, |
| Operation => "abs"); |
| |
| function "abs" is new |
| Matrix_Elementwise_Operation |
| (X_Scalar => Real'Base, |
| Result_Scalar => Real'Base, |
| X_Matrix => Real_Matrix, |
| Result_Matrix => Real_Matrix, |
| Operation => "abs"); |
| |
| function Solve is new |
| Matrix_Vector_Solution (Real'Base, 0.0, Real_Vector, Real_Matrix); |
| |
| function Solve is new |
| Matrix_Matrix_Solution (Real'Base, 0.0, Real_Matrix); |
| |
| function Unit_Matrix is new |
| Generic_Array_Operations.Unit_Matrix |
| (Scalar => Real'Base, |
| Matrix => Real_Matrix, |
| Zero => 0.0, |
| One => 1.0); |
| |
| function Unit_Vector is new |
| Generic_Array_Operations.Unit_Vector |
| (Scalar => Real'Base, |
| Vector => Real_Vector, |
| Zero => 0.0, |
| One => 1.0); |
| |
| end Instantiations; |
| |
| --------- |
| -- "+" -- |
| --------- |
| |
| function "+" (Right : Real_Vector) return Real_Vector |
| renames Instantiations."+"; |
| |
| function "+" (Right : Real_Matrix) return Real_Matrix |
| renames Instantiations."+"; |
| |
| function "+" (Left, Right : Real_Vector) return Real_Vector |
| renames Instantiations."+"; |
| |
| function "+" (Left, Right : Real_Matrix) return Real_Matrix |
| renames Instantiations."+"; |
| |
| --------- |
| -- "-" -- |
| --------- |
| |
| function "-" (Right : Real_Vector) return Real_Vector |
| renames Instantiations."-"; |
| |
| function "-" (Right : Real_Matrix) return Real_Matrix |
| renames Instantiations."-"; |
| |
| function "-" (Left, Right : Real_Vector) return Real_Vector |
| renames Instantiations."-"; |
| |
| function "-" (Left, Right : Real_Matrix) return Real_Matrix |
| renames Instantiations."-"; |
| |
| --------- |
| -- "*" -- |
| --------- |
| |
| -- Scalar multiplication |
| |
| function "*" (Left : Real'Base; Right : Real_Vector) return Real_Vector |
| renames Instantiations."*"; |
| |
| function "*" (Left : Real_Vector; Right : Real'Base) return Real_Vector |
| renames Instantiations."*"; |
| |
| function "*" (Left : Real'Base; Right : Real_Matrix) return Real_Matrix |
| renames Instantiations."*"; |
| |
| function "*" (Left : Real_Matrix; Right : Real'Base) return Real_Matrix |
| renames Instantiations."*"; |
| |
| -- Vector multiplication |
| |
| function "*" (Left, Right : Real_Vector) return Real'Base |
| renames Instantiations."*"; |
| |
| function "*" (Left, Right : Real_Vector) return Real_Matrix |
| renames Instantiations."*"; |
| |
| function "*" (Left : Real_Vector; Right : Real_Matrix) return Real_Vector |
| renames Instantiations."*"; |
| |
| function "*" (Left : Real_Matrix; Right : Real_Vector) return Real_Vector |
| renames Instantiations."*"; |
| |
| -- Matrix Multiplication |
| |
| function "*" (Left, Right : Real_Matrix) return Real_Matrix |
| renames Instantiations."*"; |
| |
| --------- |
| -- "/" -- |
| --------- |
| |
| function "/" (Left : Real_Vector; Right : Real'Base) return Real_Vector |
| renames Instantiations."/"; |
| |
| function "/" (Left : Real_Matrix; Right : Real'Base) return Real_Matrix |
| renames Instantiations."/"; |
| |
| ----------- |
| -- "abs" -- |
| ----------- |
| |
| function "abs" (Right : Real_Vector) return Real'Base |
| renames Instantiations."abs"; |
| |
| function "abs" (Right : Real_Vector) return Real_Vector |
| renames Instantiations."abs"; |
| |
| function "abs" (Right : Real_Matrix) return Real_Matrix |
| renames Instantiations."abs"; |
| |
| ----------------- |
| -- Determinant -- |
| ----------------- |
| |
| function Determinant (A : Real_Matrix) return Real'Base is |
| M : Real_Matrix := A; |
| B : Real_Matrix (A'Range (1), 1 .. 0); |
| R : Real'Base; |
| begin |
| Forward_Eliminate (M, B, R); |
| return R; |
| end Determinant; |
| |
| ----------------- |
| -- Eigensystem -- |
| ----------------- |
| |
| procedure Eigensystem |
| (A : Real_Matrix; |
| Values : out Real_Vector; |
| Vectors : out Real_Matrix) |
| is |
| begin |
| Jacobi (A, Values, Vectors, Compute_Vectors => True); |
| Sort_Eigensystem (Values, Vectors); |
| end Eigensystem; |
| |
| ----------------- |
| -- Eigenvalues -- |
| ----------------- |
| |
| function Eigenvalues (A : Real_Matrix) return Real_Vector is |
| begin |
| return Values : Real_Vector (A'Range (1)) do |
| declare |
| Vectors : Real_Matrix (1 .. 0, 1 .. 0); |
| begin |
| Jacobi (A, Values, Vectors, Compute_Vectors => False); |
| Sort_Eigensystem (Values, Vectors); |
| end; |
| end return; |
| end Eigenvalues; |
| |
| ------------- |
| -- Inverse -- |
| ------------- |
| |
| function Inverse (A : Real_Matrix) return Real_Matrix is |
| (Solve (A, Unit_Matrix (Length (A), |
| First_1 => A'First (2), |
| First_2 => A'First (1)))); |
| |
| ------------ |
| -- Jacobi -- |
| ------------ |
| |
| procedure Jacobi |
| (A : Real_Matrix; |
| Values : out Real_Vector; |
| Vectors : out Real_Matrix; |
| Compute_Vectors : Boolean := True) |
| is |
| -- This subprogram uses Carl Gustav Jacob Jacobi's iterative method |
| -- for computing eigenvalues and eigenvectors and is based on |
| -- Rutishauser's implementation. |
| |
| -- The given real symmetric matrix is transformed iteratively to |
| -- diagonal form through a sequence of appropriately chosen elementary |
| -- orthogonal transformations, called Jacobi rotations here. |
| |
| -- The Jacobi method produces a systematic decrease of the sum of the |
| -- squares of off-diagonal elements. Convergence to zero is quadratic, |
| -- both for this implementation, as for the classic method that doesn't |
| -- use row-wise scanning for pivot selection. |
| |
| -- The numerical stability and accuracy of Jacobi's method make it the |
| -- best choice here, even though for large matrices other methods will |
| -- be significantly more efficient in both time and space. |
| |
| -- While the eigensystem computations are absolutely foolproof for all |
| -- real symmetric matrices, in presence of invalid values, or similar |
| -- exceptional situations it might not. In such cases the results cannot |
| -- be trusted and Constraint_Error is raised. |
| |
| -- Note: this implementation needs temporary storage for 2 * N + N**2 |
| -- values of type Real. |
| |
| Max_Iterations : constant := 50; |
| N : constant Natural := Length (A); |
| |
| subtype Square_Matrix is Real_Matrix (1 .. N, 1 .. N); |
| |
| -- In order to annihilate the M (Row, Col) element, the |
| -- rotation parameters Cos and Sin are computed as |
| -- follows: |
| |
| -- Theta = Cot (2.0 * Phi) |
| -- = (Diag (Col) - Diag (Row)) / (2.0 * M (Row, Col)) |
| |
| -- Then Tan (Phi) as the smaller root (in modulus) of |
| |
| -- T**2 + 2 * T * Theta = 1 (or 0.5 / Theta, if Theta is large) |
| |
| function Compute_Tan (Theta : Real) return Real is |
| (Real'Copy_Sign (1.0 / (abs Theta + Sqrt (1.0 + Theta**2)), Theta)); |
| |
| function Compute_Tan (P, H : Real) return Real is |
| (if Is_Tiny (P, Compared_To => H) then P / H |
| else Compute_Tan (Theta => H / (2.0 * P))); |
| pragma Annotate |
| (CodePeer, False_Positive, "divide by zero", "H, P /= 0"); |
| |
| function Sum_Strict_Upper (M : Square_Matrix) return Real; |
| -- Return the sum of all elements in the strict upper triangle of M |
| |
| ---------------------- |
| -- Sum_Strict_Upper -- |
| ---------------------- |
| |
| function Sum_Strict_Upper (M : Square_Matrix) return Real is |
| Sum : Real := 0.0; |
| |
| begin |
| for Row in 1 .. N - 1 loop |
| for Col in Row + 1 .. N loop |
| Sum := Sum + abs M (Row, Col); |
| end loop; |
| end loop; |
| |
| return Sum; |
| end Sum_Strict_Upper; |
| |
| M : Square_Matrix := A; -- Work space for solving eigensystem |
| Threshold : Real; |
| Sum : Real; |
| Diag : Real_Vector (1 .. N); |
| Diag_Adj : Real_Vector (1 .. N); |
| |
| -- The vector Diag_Adj indicates the amount of change in each value, |
| -- while Diag tracks the value itself and Values holds the values as |
| -- they were at the beginning. As the changes typically will be small |
| -- compared to the absolute value of Diag, at the end of each iteration |
| -- Diag is computed as Diag + Diag_Adj thus avoiding accumulating |
| -- rounding errors. This technique is due to Rutishauser. |
| |
| begin |
| if Compute_Vectors |
| and then (Vectors'Length (1) /= N or else Vectors'Length (2) /= N) |
| then |
| raise Constraint_Error with "incompatible matrix dimensions"; |
| |
| elsif Values'Length /= N then |
| raise Constraint_Error with "incompatible vector length"; |
| |
| elsif not Is_Symmetric (M) then |
| raise Constraint_Error with "matrix not symmetric"; |
| end if; |
| |
| -- Note: Only the locally declared matrix M and vectors (Diag, Diag_Adj) |
| -- have lower bound equal to 1. The Vectors matrix may have |
| -- different bounds, so take care indexing elements. Assignment |
| -- as a whole is fine as sliding is automatic in that case. |
| |
| Vectors := (if not Compute_Vectors then [1 .. 0 => [1 .. 0 => 0.0]] |
| else Unit_Matrix (Vectors'Length (1), Vectors'Length (2))); |
| Values := Diagonal (M); |
| |
| Sweep : for Iteration in 1 .. Max_Iterations loop |
| |
| -- The first three iterations, perform rotation for any non-zero |
| -- element. After this, rotate only for those that are not much |
| -- smaller than the average off-diagnal element. After the fifth |
| -- iteration, additionally zero out off-diagonal elements that are |
| -- very small compared to elements on the diagonal with the same |
| -- column or row index. |
| |
| Sum := Sum_Strict_Upper (M); |
| |
| exit Sweep when Sum = 0.0; |
| |
| Threshold := (if Iteration < 4 then 0.2 * Sum / Real (N**2) else 0.0); |
| |
| -- Iterate over all off-diagonal elements, rotating any that have |
| -- an absolute value that exceeds the threshold. |
| |
| Diag := Values; |
| Diag_Adj := [others => 0.0]; -- Accumulates adjustments to Diag |
| |
| for Row in 1 .. N - 1 loop |
| for Col in Row + 1 .. N loop |
| |
| -- If, before the rotation M (Row, Col) is tiny compared to |
| -- Diag (Row) and Diag (Col), rotation is skipped. This is |
| -- meaningful, as it produces no larger error than would be |
| -- produced anyhow if the rotation had been performed. |
| -- Suppress this optimization in the first four sweeps, so |
| -- that this procedure can be used for computing eigenvectors |
| -- of perturbed diagonal matrices. |
| |
| if Iteration > 4 |
| and then Is_Tiny (M (Row, Col), Compared_To => Diag (Row)) |
| and then Is_Tiny (M (Row, Col), Compared_To => Diag (Col)) |
| then |
| M (Row, Col) := 0.0; |
| |
| elsif abs M (Row, Col) > Threshold then |
| Perform_Rotation : declare |
| Tan : constant Real := Compute_Tan (M (Row, Col), |
| Diag (Col) - Diag (Row)); |
| Cos : constant Real := 1.0 / Sqrt (1.0 + Tan**2); |
| Sin : constant Real := Tan * Cos; |
| Tau : constant Real := Sin / (1.0 + Cos); |
| Adj : constant Real := Tan * M (Row, Col); |
| |
| begin |
| Diag_Adj (Row) := Diag_Adj (Row) - Adj; |
| Diag_Adj (Col) := Diag_Adj (Col) + Adj; |
| Diag (Row) := Diag (Row) - Adj; |
| Diag (Col) := Diag (Col) + Adj; |
| |
| M (Row, Col) := 0.0; |
| |
| for J in 1 .. Row - 1 loop -- 1 <= J < Row |
| Rotate (M (J, Row), M (J, Col), Sin, Tau); |
| end loop; |
| |
| for J in Row + 1 .. Col - 1 loop -- Row < J < Col |
| Rotate (M (Row, J), M (J, Col), Sin, Tau); |
| end loop; |
| |
| for J in Col + 1 .. N loop -- Col < J <= N |
| Rotate (M (Row, J), M (Col, J), Sin, Tau); |
| end loop; |
| |
| for J in Vectors'Range (1) loop |
| Rotate (Vectors (J, Row - 1 + Vectors'First (2)), |
| Vectors (J, Col - 1 + Vectors'First (2)), |
| Sin, Tau); |
| end loop; |
| end Perform_Rotation; |
| end if; |
| end loop; |
| end loop; |
| |
| Values := Values + Diag_Adj; |
| end loop Sweep; |
| |
| -- All normal matrices with valid values should converge perfectly. |
| |
| if Sum /= 0.0 then |
| raise Constraint_Error with "eigensystem solution does not converge"; |
| end if; |
| end Jacobi; |
| |
| ----------- |
| -- Solve -- |
| ----------- |
| |
| function Solve (A : Real_Matrix; X : Real_Vector) return Real_Vector |
| renames Instantiations.Solve; |
| |
| function Solve (A, X : Real_Matrix) return Real_Matrix |
| renames Instantiations.Solve; |
| |
| ---------------------- |
| -- Sort_Eigensystem -- |
| ---------------------- |
| |
| procedure Sort_Eigensystem |
| (Values : in out Real_Vector; |
| Vectors : in out Real_Matrix) |
| is |
| procedure Swap (Left, Right : Integer); |
| -- Swap Values (Left) with Values (Right), and also swap the |
| -- corresponding eigenvectors. Note that lowerbounds may differ. |
| |
| function Less (Left, Right : Integer) return Boolean is |
| (Values (Left) > Values (Right)); |
| -- Sort by decreasing eigenvalue, see RM G.3.1 (76). |
| |
| procedure Sort is new Generic_Anonymous_Array_Sort (Integer); |
| -- Sorts eigenvalues and eigenvectors by decreasing value |
| |
| procedure Swap (Left, Right : Integer) is |
| begin |
| Swap (Values (Left), Values (Right)); |
| Swap_Column (Vectors, Left - Values'First + Vectors'First (2), |
| Right - Values'First + Vectors'First (2)); |
| end Swap; |
| |
| begin |
| Sort (Values'First, Values'Last); |
| end Sort_Eigensystem; |
| |
| --------------- |
| -- Transpose -- |
| --------------- |
| |
| function Transpose (X : Real_Matrix) return Real_Matrix is |
| begin |
| return R : Real_Matrix (X'Range (2), X'Range (1)) do |
| Transpose (X, R); |
| end return; |
| end Transpose; |
| |
| ----------------- |
| -- Unit_Matrix -- |
| ----------------- |
| |
| function Unit_Matrix |
| (Order : Positive; |
| First_1 : Integer := 1; |
| First_2 : Integer := 1) return Real_Matrix |
| renames Instantiations.Unit_Matrix; |
| |
| ----------------- |
| -- Unit_Vector -- |
| ----------------- |
| |
| function Unit_Vector |
| (Index : Integer; |
| Order : Positive; |
| First : Integer := 1) return Real_Vector |
| renames Instantiations.Unit_Vector; |
| |
| end Ada.Numerics.Generic_Real_Arrays; |