File : a-ngrear.adb


   1 ------------------------------------------------------------------------------
   2 --                                                                          --
   3 --                         GNAT RUN-TIME COMPONENTS                         --
   4 --                                                                          --
   5 --                     ADA.NUMERICS.GENERIC_REAL_ARRAYS                     --
   6 --                                                                          --
   7 --                                 B o d y                                  --
   8 --                                                                          --
   9 --          Copyright (C) 2006-2016, Free Software Foundation, Inc.         --
  10 --                                                                          --
  11 -- GNAT is free software;  you can  redistribute it  and/or modify it under --
  12 -- terms of the  GNU General Public License as published  by the Free Soft- --
  13 -- ware  Foundation;  either version 3,  or (at your option) any later ver- --
  14 -- sion.  GNAT is distributed in the hope that it will be useful, but WITH- --
  15 -- OUT ANY WARRANTY;  without even the  implied warranty of MERCHANTABILITY --
  16 -- or FITNESS FOR A PARTICULAR PURPOSE.                                     --
  17 --                                                                          --
  18 --                                                                          --
  19 --                                                                          --
  20 --                                                                          --
  21 --                                                                          --
  22 -- You should have received a copy of the GNU General Public License and    --
  23 -- a copy of the GCC Runtime Library Exception along with this program;     --
  24 -- see the files COPYING3 and COPYING.RUNTIME respectively.  If not, see    --
  25 -- <http://www.gnu.org/licenses/>.                                          --
  26 --                                                                          --
  27 -- GNAT was originally developed  by the GNAT team at  New York University. --
  28 -- Extensive contributions were provided by Ada Core Technologies Inc.      --
  29 --                                                                          --
  30 ------------------------------------------------------------------------------
  31 
  32 --  This version of Generic_Real_Arrays avoids the use of BLAS and LAPACK. One
  33 --  reason for this is new Ada 2012 requirements that prohibit algorithms such
  34 --  as Strassen's algorithm, which may be used by some BLAS implementations. In
  35 --  addition, some platforms lacked suitable compilers to compile the reference
  36 --  BLAS/LAPACK implementation. Finally, on some platforms there are more
  37 --  floating point types than supported by BLAS/LAPACK.
  38 
  39 with Ada.Containers.Generic_Anonymous_Array_Sort; use Ada.Containers;
  40 
  41 with System; use System;
  42 with System.Generic_Array_Operations; use System.Generic_Array_Operations;
  43 
  44 package body Ada.Numerics.Generic_Real_Arrays is
  45 
  46    package Ops renames System.Generic_Array_Operations;
  47 
  48    function Is_Non_Zero (X : Real'Base) return Boolean is (X /= 0.0);
  49 
  50    procedure Back_Substitute is new Ops.Back_Substitute
  51      (Scalar        => Real'Base,
  52       Matrix        => Real_Matrix,
  53       Is_Non_Zero   => Is_Non_Zero);
  54 
  55    function Diagonal is new Ops.Diagonal
  56      (Scalar       => Real'Base,
  57       Vector       => Real_Vector,
  58       Matrix       => Real_Matrix);
  59 
  60    procedure Forward_Eliminate is new Ops.Forward_Eliminate
  61     (Scalar        => Real'Base,
  62      Real          => Real'Base,
  63      Matrix        => Real_Matrix,
  64      Zero          => 0.0,
  65      One           => 1.0);
  66 
  67    procedure Swap_Column is new Ops.Swap_Column
  68     (Scalar        => Real'Base,
  69      Matrix        => Real_Matrix);
  70 
  71    procedure Transpose is new  Ops.Transpose
  72        (Scalar        => Real'Base,
  73         Matrix        => Real_Matrix);
  74 
  75    function Is_Symmetric (A : Real_Matrix) return Boolean is
  76      (Transpose (A) = A);
  77    --  Return True iff A is symmetric, see RM G.3.1 (90).
  78 
  79    function Is_Tiny (Value, Compared_To : Real) return Boolean is
  80      (abs Compared_To + 100.0 * abs (Value) = abs Compared_To);
  81    --  Return True iff the Value is much smaller in magnitude than the least
  82    --  significant digit of Compared_To.
  83 
  84    procedure Jacobi
  85      (A               : Real_Matrix;
  86       Values          : out Real_Vector;
  87       Vectors         : out Real_Matrix;
  88       Compute_Vectors : Boolean := True);
  89    --  Perform Jacobi's eigensystem algorithm on real symmetric matrix A
  90 
  91    function Length is new Square_Matrix_Length (Real'Base, Real_Matrix);
  92    --  Helper function that raises a Constraint_Error is the argument is
  93    --  not a square matrix, and otherwise returns its length.
  94 
  95    procedure Rotate (X, Y : in out Real; Sin, Tau : Real);
  96    --  Perform a Givens rotation
  97 
  98    procedure Sort_Eigensystem
  99      (Values  : in out Real_Vector;
 100       Vectors : in out Real_Matrix);
 101    --  Sort Values and associated Vectors by decreasing absolute value
 102 
 103    procedure Swap (Left, Right : in out Real);
 104    --  Exchange Left and Right
 105 
 106    function Sqrt is new Ops.Sqrt (Real);
 107    --  Instant a generic square root implementation here, in order to avoid
 108    --  instantiating a complete copy of Generic_Elementary_Functions.
 109    --  Speed of the square root is not a big concern here.
 110 
 111    ------------
 112    -- Rotate --
 113    ------------
 114 
 115    procedure Rotate (X, Y : in out Real; Sin, Tau : Real) is
 116       Old_X : constant Real := X;
 117       Old_Y : constant Real := Y;
 118    begin
 119       X := Old_X - Sin * (Old_Y + Old_X * Tau);
 120       Y := Old_Y + Sin * (Old_X - Old_Y * Tau);
 121    end Rotate;
 122 
 123    ----------
 124    -- Swap --
 125    ----------
 126 
 127    procedure Swap (Left, Right : in out Real) is
 128       Temp : constant Real := Left;
 129    begin
 130       Left := Right;
 131       Right := Temp;
 132    end Swap;
 133 
 134    --  Instantiating the following subprograms directly would lead to
 135    --  name clashes, so use a local package.
 136 
 137    package Instantiations is
 138 
 139       function "+" is new
 140         Vector_Elementwise_Operation
 141           (X_Scalar      => Real'Base,
 142            Result_Scalar => Real'Base,
 143            X_Vector      => Real_Vector,
 144            Result_Vector => Real_Vector,
 145            Operation     => "+");
 146 
 147       function "+" is new
 148         Matrix_Elementwise_Operation
 149           (X_Scalar      => Real'Base,
 150            Result_Scalar => Real'Base,
 151            X_Matrix      => Real_Matrix,
 152            Result_Matrix => Real_Matrix,
 153            Operation     => "+");
 154 
 155       function "+" is new
 156         Vector_Vector_Elementwise_Operation
 157           (Left_Scalar   => Real'Base,
 158            Right_Scalar  => Real'Base,
 159            Result_Scalar => Real'Base,
 160            Left_Vector   => Real_Vector,
 161            Right_Vector  => Real_Vector,
 162            Result_Vector => Real_Vector,
 163            Operation     => "+");
 164 
 165       function "+" is new
 166         Matrix_Matrix_Elementwise_Operation
 167           (Left_Scalar   => Real'Base,
 168            Right_Scalar  => Real'Base,
 169            Result_Scalar => Real'Base,
 170            Left_Matrix   => Real_Matrix,
 171            Right_Matrix  => Real_Matrix,
 172            Result_Matrix => Real_Matrix,
 173            Operation     => "+");
 174 
 175       function "-" is new
 176         Vector_Elementwise_Operation
 177           (X_Scalar      => Real'Base,
 178            Result_Scalar => Real'Base,
 179            X_Vector      => Real_Vector,
 180            Result_Vector => Real_Vector,
 181            Operation     => "-");
 182 
 183       function "-" is new
 184         Matrix_Elementwise_Operation
 185           (X_Scalar      => Real'Base,
 186            Result_Scalar => Real'Base,
 187            X_Matrix      => Real_Matrix,
 188            Result_Matrix => Real_Matrix,
 189            Operation     => "-");
 190 
 191       function "-" is new
 192         Vector_Vector_Elementwise_Operation
 193           (Left_Scalar   => Real'Base,
 194            Right_Scalar  => Real'Base,
 195            Result_Scalar => Real'Base,
 196            Left_Vector   => Real_Vector,
 197            Right_Vector  => Real_Vector,
 198            Result_Vector => Real_Vector,
 199            Operation     => "-");
 200 
 201       function "-" is new
 202         Matrix_Matrix_Elementwise_Operation
 203           (Left_Scalar   => Real'Base,
 204            Right_Scalar  => Real'Base,
 205            Result_Scalar => Real'Base,
 206            Left_Matrix   => Real_Matrix,
 207            Right_Matrix  => Real_Matrix,
 208            Result_Matrix => Real_Matrix,
 209            Operation     => "-");
 210 
 211       function "*" is new
 212         Scalar_Vector_Elementwise_Operation
 213           (Left_Scalar   => Real'Base,
 214            Right_Scalar  => Real'Base,
 215            Result_Scalar => Real'Base,
 216            Right_Vector  => Real_Vector,
 217            Result_Vector => Real_Vector,
 218            Operation     => "*");
 219 
 220       function "*" is new
 221         Scalar_Matrix_Elementwise_Operation
 222           (Left_Scalar   => Real'Base,
 223            Right_Scalar  => Real'Base,
 224            Result_Scalar => Real'Base,
 225            Right_Matrix  => Real_Matrix,
 226            Result_Matrix => Real_Matrix,
 227            Operation     => "*");
 228 
 229       function "*" is new
 230         Vector_Scalar_Elementwise_Operation
 231           (Left_Scalar   => Real'Base,
 232            Right_Scalar  => Real'Base,
 233            Result_Scalar => Real'Base,
 234            Left_Vector   => Real_Vector,
 235            Result_Vector => Real_Vector,
 236            Operation     => "*");
 237 
 238       function "*" is new
 239         Matrix_Scalar_Elementwise_Operation
 240           (Left_Scalar   => Real'Base,
 241            Right_Scalar  => Real'Base,
 242            Result_Scalar => Real'Base,
 243            Left_Matrix   => Real_Matrix,
 244            Result_Matrix => Real_Matrix,
 245            Operation     => "*");
 246 
 247       function "*" is new
 248         Outer_Product
 249           (Left_Scalar   => Real'Base,
 250            Right_Scalar  => Real'Base,
 251            Result_Scalar => Real'Base,
 252            Left_Vector   => Real_Vector,
 253            Right_Vector  => Real_Vector,
 254            Matrix        => Real_Matrix);
 255 
 256       function "*" is new
 257         Inner_Product
 258           (Left_Scalar   => Real'Base,
 259            Right_Scalar  => Real'Base,
 260            Result_Scalar => Real'Base,
 261            Left_Vector   => Real_Vector,
 262            Right_Vector  => Real_Vector,
 263            Zero          => 0.0);
 264 
 265       function "*" is new
 266         Matrix_Vector_Product
 267           (Left_Scalar   => Real'Base,
 268            Right_Scalar  => Real'Base,
 269            Result_Scalar => Real'Base,
 270            Matrix        => Real_Matrix,
 271            Right_Vector  => Real_Vector,
 272            Result_Vector => Real_Vector,
 273            Zero          => 0.0);
 274 
 275       function "*" is new
 276         Vector_Matrix_Product
 277           (Left_Scalar   => Real'Base,
 278            Right_Scalar  => Real'Base,
 279            Result_Scalar => Real'Base,
 280            Left_Vector   => Real_Vector,
 281            Matrix        => Real_Matrix,
 282            Result_Vector => Real_Vector,
 283            Zero          => 0.0);
 284 
 285       function "*" is new
 286         Matrix_Matrix_Product
 287           (Left_Scalar   => Real'Base,
 288            Right_Scalar  => Real'Base,
 289            Result_Scalar => Real'Base,
 290            Left_Matrix   => Real_Matrix,
 291            Right_Matrix  => Real_Matrix,
 292            Result_Matrix => Real_Matrix,
 293            Zero          => 0.0);
 294 
 295       function "/" is new
 296         Vector_Scalar_Elementwise_Operation
 297           (Left_Scalar   => Real'Base,
 298            Right_Scalar  => Real'Base,
 299            Result_Scalar => Real'Base,
 300            Left_Vector   => Real_Vector,
 301            Result_Vector => Real_Vector,
 302            Operation     => "/");
 303 
 304       function "/" is new
 305         Matrix_Scalar_Elementwise_Operation
 306           (Left_Scalar   => Real'Base,
 307            Right_Scalar  => Real'Base,
 308            Result_Scalar => Real'Base,
 309            Left_Matrix   => Real_Matrix,
 310            Result_Matrix => Real_Matrix,
 311            Operation     => "/");
 312 
 313       function "abs" is new
 314         L2_Norm
 315           (X_Scalar      => Real'Base,
 316            Result_Real   => Real'Base,
 317            X_Vector      => Real_Vector,
 318            "abs"         => "+");
 319       --  While the L2_Norm by definition uses the absolute values of the
 320       --  elements of X_Vector, for real values the subsequent squaring
 321       --  makes this unnecessary, so we substitute the "+" identity function
 322       --  instead.
 323 
 324       function "abs" is new
 325         Vector_Elementwise_Operation
 326           (X_Scalar      => Real'Base,
 327            Result_Scalar => Real'Base,
 328            X_Vector      => Real_Vector,
 329            Result_Vector => Real_Vector,
 330            Operation     => "abs");
 331 
 332       function "abs" is new
 333         Matrix_Elementwise_Operation
 334           (X_Scalar      => Real'Base,
 335            Result_Scalar => Real'Base,
 336            X_Matrix      => Real_Matrix,
 337            Result_Matrix => Real_Matrix,
 338            Operation     => "abs");
 339 
 340       function Solve is new
 341         Matrix_Vector_Solution (Real'Base, 0.0, Real_Vector, Real_Matrix);
 342 
 343       function Solve is new
 344         Matrix_Matrix_Solution (Real'Base, 0.0, Real_Matrix);
 345 
 346       function Unit_Matrix is new
 347         Generic_Array_Operations.Unit_Matrix
 348           (Scalar        => Real'Base,
 349            Matrix        => Real_Matrix,
 350            Zero          => 0.0,
 351            One           => 1.0);
 352 
 353       function Unit_Vector is new
 354         Generic_Array_Operations.Unit_Vector
 355           (Scalar        => Real'Base,
 356            Vector        => Real_Vector,
 357            Zero          => 0.0,
 358            One           => 1.0);
 359 
 360    end Instantiations;
 361 
 362    ---------
 363    -- "+" --
 364    ---------
 365 
 366    function "+" (Right : Real_Vector) return Real_Vector
 367      renames Instantiations."+";
 368 
 369    function "+" (Right : Real_Matrix) return Real_Matrix
 370      renames Instantiations."+";
 371 
 372    function "+" (Left, Right : Real_Vector) return Real_Vector
 373      renames Instantiations."+";
 374 
 375    function "+" (Left, Right : Real_Matrix) return Real_Matrix
 376      renames Instantiations."+";
 377 
 378    ---------
 379    -- "-" --
 380    ---------
 381 
 382    function "-" (Right : Real_Vector) return Real_Vector
 383      renames Instantiations."-";
 384 
 385    function "-" (Right : Real_Matrix) return Real_Matrix
 386      renames Instantiations."-";
 387 
 388    function "-" (Left, Right : Real_Vector) return Real_Vector
 389      renames Instantiations."-";
 390 
 391    function "-" (Left, Right : Real_Matrix) return Real_Matrix
 392       renames Instantiations."-";
 393 
 394    ---------
 395    -- "*" --
 396    ---------
 397 
 398    --  Scalar multiplication
 399 
 400    function "*" (Left : Real'Base; Right : Real_Vector) return Real_Vector
 401      renames Instantiations."*";
 402 
 403    function "*" (Left : Real_Vector; Right : Real'Base) return Real_Vector
 404      renames Instantiations."*";
 405 
 406    function "*" (Left : Real'Base; Right : Real_Matrix) return Real_Matrix
 407      renames Instantiations."*";
 408 
 409    function "*" (Left : Real_Matrix; Right : Real'Base) return Real_Matrix
 410      renames Instantiations."*";
 411 
 412    --  Vector multiplication
 413 
 414    function "*" (Left, Right : Real_Vector) return Real'Base
 415      renames Instantiations."*";
 416 
 417    function "*" (Left, Right : Real_Vector) return Real_Matrix
 418      renames Instantiations."*";
 419 
 420    function "*" (Left : Real_Vector; Right : Real_Matrix) return Real_Vector
 421      renames Instantiations."*";
 422 
 423    function "*" (Left : Real_Matrix; Right : Real_Vector) return Real_Vector
 424      renames Instantiations."*";
 425 
 426    --  Matrix Multiplication
 427 
 428    function "*" (Left, Right : Real_Matrix) return Real_Matrix
 429      renames Instantiations."*";
 430 
 431    ---------
 432    -- "/" --
 433    ---------
 434 
 435    function "/" (Left : Real_Vector; Right : Real'Base) return Real_Vector
 436      renames Instantiations."/";
 437 
 438    function "/" (Left : Real_Matrix; Right : Real'Base) return Real_Matrix
 439      renames Instantiations."/";
 440 
 441    -----------
 442    -- "abs" --
 443    -----------
 444 
 445    function "abs" (Right : Real_Vector) return Real'Base
 446      renames Instantiations."abs";
 447 
 448    function "abs" (Right : Real_Vector) return Real_Vector
 449      renames Instantiations."abs";
 450 
 451    function "abs" (Right : Real_Matrix) return Real_Matrix
 452      renames Instantiations."abs";
 453 
 454    -----------------
 455    -- Determinant --
 456    -----------------
 457 
 458    function Determinant (A : Real_Matrix) return Real'Base is
 459       M : Real_Matrix := A;
 460       B : Real_Matrix (A'Range (1), 1 .. 0);
 461       R : Real'Base;
 462    begin
 463       Forward_Eliminate (M, B, R);
 464       return R;
 465    end Determinant;
 466 
 467    -----------------
 468    -- Eigensystem --
 469    -----------------
 470 
 471    procedure Eigensystem
 472      (A       : Real_Matrix;
 473       Values  : out Real_Vector;
 474       Vectors : out Real_Matrix)
 475    is
 476    begin
 477       Jacobi (A, Values, Vectors, Compute_Vectors => True);
 478       Sort_Eigensystem (Values, Vectors);
 479    end Eigensystem;
 480 
 481    -----------------
 482    -- Eigenvalues --
 483    -----------------
 484 
 485    function Eigenvalues (A : Real_Matrix) return Real_Vector is
 486    begin
 487       return Values : Real_Vector (A'Range (1)) do
 488          declare
 489             Vectors : Real_Matrix (1 .. 0, 1 .. 0);
 490          begin
 491             Jacobi (A, Values, Vectors, Compute_Vectors => False);
 492             Sort_Eigensystem (Values, Vectors);
 493          end;
 494       end return;
 495    end Eigenvalues;
 496 
 497    -------------
 498    -- Inverse --
 499    -------------
 500 
 501    function Inverse (A : Real_Matrix) return Real_Matrix is
 502      (Solve (A, Unit_Matrix (Length (A))));
 503 
 504    ------------
 505    -- Jacobi --
 506    ------------
 507 
 508    procedure Jacobi
 509      (A               : Real_Matrix;
 510       Values          : out Real_Vector;
 511       Vectors         : out Real_Matrix;
 512       Compute_Vectors : Boolean := True)
 513    is
 514       --  This subprogram uses Carl Gustav Jacob Jacobi's iterative method
 515       --  for computing eigenvalues and eigenvectors and is based on
 516       --  Rutishauser's implementation.
 517 
 518       --  The given real symmetric matrix is transformed iteratively to
 519       --  diagonal form through a sequence of appropriately chosen elementary
 520       --  orthogonal transformations, called Jacobi rotations here.
 521 
 522       --  The Jacobi method produces a systematic decrease of the sum of the
 523       --  squares of off-diagonal elements. Convergence to zero is quadratic,
 524       --  both for this implementation, as for the classic method that doesn't
 525       --  use row-wise scanning for pivot selection.
 526 
 527       --  The numerical stability and accuracy of Jacobi's method make it the
 528       --  best choice here, even though for large matrices other methods will
 529       --  be significantly more efficient in both time and space.
 530 
 531       --  While the eigensystem computations are absolutely foolproof for all
 532       --  real symmetric matrices, in presence of invalid values, or similar
 533       --  exceptional situations it might not. In such cases the results cannot
 534       --  be trusted and Constraint_Error is raised.
 535 
 536       --  Note: this implementation needs temporary storage for 2 * N + N**2
 537       --        values of type Real.
 538 
 539       Max_Iterations  : constant := 50;
 540       N               : constant Natural := Length (A);
 541 
 542       subtype Square_Matrix is Real_Matrix (1 .. N, 1 .. N);
 543 
 544       --  In order to annihilate the M (Row, Col) element, the
 545       --  rotation parameters Cos and Sin are computed as
 546       --  follows:
 547 
 548       --    Theta = Cot (2.0 * Phi)
 549       --          = (Diag (Col) - Diag (Row)) / (2.0 * M (Row, Col))
 550 
 551       --  Then Tan (Phi) as the smaller root (in modulus) of
 552 
 553       --    T**2 + 2 * T * Theta = 1 (or 0.5 / Theta, if Theta is large)
 554 
 555       function Compute_Tan (Theta : Real) return Real is
 556          (Real'Copy_Sign (1.0 / (abs Theta + Sqrt (1.0 + Theta**2)), Theta));
 557 
 558       function Compute_Tan (P, H : Real) return Real is
 559          (if Is_Tiny (P, Compared_To => H) then P / H
 560           else Compute_Tan (Theta => H / (2.0 * P)));
 561 
 562       function Sum_Strict_Upper (M : Square_Matrix) return Real;
 563       --  Return the sum of all elements in the strict upper triangle of M
 564 
 565       ----------------------
 566       -- Sum_Strict_Upper --
 567       ----------------------
 568 
 569       function Sum_Strict_Upper (M : Square_Matrix) return Real is
 570          Sum : Real := 0.0;
 571 
 572       begin
 573          for Row in 1 .. N - 1 loop
 574             for Col in Row + 1 .. N loop
 575                Sum := Sum + abs M (Row, Col);
 576             end loop;
 577          end loop;
 578 
 579          return Sum;
 580       end Sum_Strict_Upper;
 581 
 582       M         : Square_Matrix := A; --  Work space for solving eigensystem
 583       Threshold : Real;
 584       Sum       : Real;
 585       Diag      : Real_Vector (1 .. N);
 586       Diag_Adj  : Real_Vector (1 .. N);
 587 
 588       --  The vector Diag_Adj indicates the amount of change in each value,
 589       --  while Diag tracks the value itself and Values holds the values as
 590       --  they were at the beginning. As the changes typically will be small
 591       --  compared to the absolute value of Diag, at the end of each iteration
 592       --  Diag is computed as Diag + Diag_Adj thus avoiding accumulating
 593       --  rounding errors. This technique is due to Rutishauser.
 594 
 595    begin
 596       if Compute_Vectors
 597          and then (Vectors'Length (1) /= N or else Vectors'Length (2) /= N)
 598       then
 599          raise Constraint_Error with "incompatible matrix dimensions";
 600 
 601       elsif Values'Length /= N then
 602          raise Constraint_Error with "incompatible vector length";
 603 
 604       elsif not Is_Symmetric (M) then
 605          raise Constraint_Error with "matrix not symmetric";
 606       end if;
 607 
 608       --  Note: Only the locally declared matrix M and vectors (Diag, Diag_Adj)
 609       --        have lower bound equal to 1. The Vectors matrix may have
 610       --        different bounds, so take care indexing elements. Assignment
 611       --        as a whole is fine as sliding is automatic in that case.
 612 
 613       Vectors := (if not Compute_Vectors then (1 .. 0 => (1 .. 0 => 0.0))
 614                   else Unit_Matrix (Vectors'Length (1), Vectors'Length (2)));
 615       Values := Diagonal (M);
 616 
 617       Sweep : for Iteration in 1 .. Max_Iterations loop
 618 
 619          --  The first three iterations, perform rotation for any non-zero
 620          --  element. After this, rotate only for those that are not much
 621          --  smaller than the average off-diagnal element. After the fifth
 622          --  iteration, additionally zero out off-diagonal elements that are
 623          --  very small compared to elements on the diagonal with the same
 624          --  column or row index.
 625 
 626          Sum := Sum_Strict_Upper (M);
 627 
 628          exit Sweep when Sum = 0.0;
 629 
 630          Threshold := (if Iteration < 4 then 0.2 * Sum / Real (N**2) else 0.0);
 631 
 632          --  Iterate over all off-diagonal elements, rotating any that have
 633          --  an absolute value that exceeds the threshold.
 634 
 635          Diag := Values;
 636          Diag_Adj := (others => 0.0); -- Accumulates adjustments to Diag
 637 
 638          for Row in 1 .. N - 1 loop
 639             for Col in Row + 1 .. N loop
 640 
 641                --  If, before the rotation M (Row, Col) is tiny compared to
 642                --  Diag (Row) and Diag (Col), rotation is skipped. This is
 643                --  meaningful, as it produces no larger error than would be
 644                --  produced anyhow if the rotation had been performed.
 645                --  Suppress this optimization in the first four sweeps, so
 646                --  that this procedure can be used for computing eigenvectors
 647                --  of perturbed diagonal matrices.
 648 
 649                if Iteration > 4
 650                   and then Is_Tiny (M (Row, Col), Compared_To => Diag (Row))
 651                   and then Is_Tiny (M (Row, Col), Compared_To => Diag (Col))
 652                then
 653                   M (Row, Col) := 0.0;
 654 
 655                elsif abs M (Row, Col) > Threshold then
 656                   Perform_Rotation : declare
 657                      Tan : constant Real := Compute_Tan (M (Row, Col),
 658                                                Diag (Col) - Diag (Row));
 659                      Cos : constant Real := 1.0 / Sqrt (1.0 + Tan**2);
 660                      Sin : constant Real := Tan * Cos;
 661                      Tau : constant Real := Sin / (1.0 + Cos);
 662                      Adj : constant Real := Tan * M (Row, Col);
 663 
 664                   begin
 665                      Diag_Adj (Row) := Diag_Adj (Row) - Adj;
 666                      Diag_Adj (Col) := Diag_Adj (Col) + Adj;
 667                      Diag (Row) := Diag (Row) - Adj;
 668                      Diag (Col) := Diag (Col) + Adj;
 669 
 670                      M (Row, Col) := 0.0;
 671 
 672                      for J in 1 .. Row - 1 loop        --  1 <= J < Row
 673                         Rotate (M (J, Row), M (J, Col), Sin, Tau);
 674                      end loop;
 675 
 676                      for J in Row + 1 .. Col - 1 loop  --  Row < J < Col
 677                         Rotate (M (Row, J), M (J, Col), Sin, Tau);
 678                      end loop;
 679 
 680                      for J in Col + 1 .. N loop        --  Col < J <= N
 681                         Rotate (M (Row, J), M (Col, J), Sin, Tau);
 682                      end loop;
 683 
 684                      for J in Vectors'Range (1) loop
 685                         Rotate (Vectors (J, Row - 1 + Vectors'First (2)),
 686                                 Vectors (J, Col - 1 + Vectors'First (2)),
 687                                 Sin, Tau);
 688                      end loop;
 689                   end Perform_Rotation;
 690                end if;
 691             end loop;
 692          end loop;
 693 
 694          Values := Values + Diag_Adj;
 695       end loop Sweep;
 696 
 697       --  All normal matrices with valid values should converge perfectly.
 698 
 699       if Sum /= 0.0 then
 700          raise Constraint_Error with "eigensystem solution does not converge";
 701       end if;
 702    end Jacobi;
 703 
 704    -----------
 705    -- Solve --
 706    -----------
 707 
 708    function Solve (A : Real_Matrix; X : Real_Vector) return Real_Vector
 709       renames Instantiations.Solve;
 710 
 711    function Solve (A, X : Real_Matrix) return Real_Matrix
 712       renames Instantiations.Solve;
 713 
 714    ----------------------
 715    -- Sort_Eigensystem --
 716    ----------------------
 717 
 718    procedure Sort_Eigensystem
 719      (Values  : in out Real_Vector;
 720       Vectors : in out Real_Matrix)
 721    is
 722       procedure Swap (Left, Right : Integer);
 723       --  Swap Values (Left) with Values (Right), and also swap the
 724       --  corresponding eigenvectors. Note that lowerbounds may differ.
 725 
 726       function Less (Left, Right : Integer) return Boolean is
 727         (Values (Left) > Values (Right));
 728       --  Sort by decreasing eigenvalue, see RM G.3.1 (76).
 729 
 730       procedure Sort is new Generic_Anonymous_Array_Sort (Integer);
 731       --  Sorts eigenvalues and eigenvectors by decreasing value
 732 
 733       procedure Swap (Left, Right : Integer) is
 734       begin
 735          Swap (Values (Left), Values (Right));
 736          Swap_Column (Vectors, Left - Values'First + Vectors'First (2),
 737                                Right - Values'First + Vectors'First (2));
 738       end Swap;
 739 
 740    begin
 741       Sort (Values'First, Values'Last);
 742    end Sort_Eigensystem;
 743 
 744    ---------------
 745    -- Transpose --
 746    ---------------
 747 
 748    function Transpose (X : Real_Matrix) return Real_Matrix is
 749    begin
 750       return R : Real_Matrix (X'Range (2), X'Range (1)) do
 751          Transpose (X, R);
 752       end return;
 753    end Transpose;
 754 
 755    -----------------
 756    -- Unit_Matrix --
 757    -----------------
 758 
 759    function Unit_Matrix
 760      (Order   : Positive;
 761       First_1 : Integer := 1;
 762       First_2 : Integer := 1) return Real_Matrix
 763      renames Instantiations.Unit_Matrix;
 764 
 765    -----------------
 766    -- Unit_Vector --
 767    -----------------
 768 
 769    function Unit_Vector
 770      (Index : Integer;
 771       Order : Positive;
 772       First : Integer := 1) return Real_Vector
 773      renames Instantiations.Unit_Vector;
 774 
 775 end Ada.Numerics.Generic_Real_Arrays;