1 ------------------------------------------------------------------------------
3 -- GNAT RUN-TIME COMPONENTS --
5 -- ADA.NUMERICS.GENERIC_REAL_ARRAYS --
9 -- Copyright (C) 2006-2009, Free Software Foundation, Inc. --
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. --
18 -- As a special exception under Section 7 of GPL version 3, you are granted --
19 -- additional permissions described in the GCC Runtime Library Exception, --
20 -- version 3.1, as published by the Free Software Foundation. --
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/>. --
27 -- GNAT was originally developed by the GNAT team at New York University. --
28 -- Extensive contributions were provided by Ada Core Technologies Inc. --
30 ------------------------------------------------------------------------------
32 with System; use System;
33 with System.Generic_Real_BLAS;
34 with System.Generic_Real_LAPACK;
35 with System.Generic_Array_Operations; use System.Generic_Array_Operations;
37 package body Ada.Numerics.Generic_Real_Arrays is
39 -- Operations involving inner products use BLAS library implementations.
40 -- This allows larger matrices and vectors to be computed efficiently,
41 -- taking into account memory hierarchy issues and vector instructions
42 -- that vary widely between machines.
44 -- Operations that are defined in terms of operations on the type Real,
45 -- such as addition, subtraction and scaling, are computed in the canonical
46 -- way looping over all elements.
48 -- Operations for solving linear systems and computing determinant,
49 -- eigenvalues, eigensystem and inverse, are implemented using the
53 new Generic_Real_BLAS (Real'Base, Real_Vector, Real_Matrix);
56 new Generic_Real_LAPACK (Real'Base, Real_Vector, Real_Matrix);
60 -- Procedure versions of functions returning unconstrained values.
61 -- This allows for inlining the function wrapper.
63 procedure Eigenvalues (A : Real_Matrix; Values : out Real_Vector);
64 procedure Inverse (A : Real_Matrix; R : out Real_Matrix);
65 procedure Solve (A : Real_Matrix; X : Real_Vector; B : out Real_Vector);
66 procedure Solve (A : Real_Matrix; X : Real_Matrix; B : out Real_Matrix);
68 procedure Transpose is new
69 Generic_Array_Operations.Transpose
71 Matrix => Real_Matrix);
73 -- Helper function that raises a Constraint_Error is the argument is
74 -- not a square matrix, and otherwise returns its length.
76 function Length is new Square_Matrix_Length (Real'Base, Real_Matrix);
78 -- Instantiating the following subprograms directly would lead to
79 -- name clashes, so use a local package.
81 package Instantiations is
84 Vector_Elementwise_Operation
85 (X_Scalar => Real'Base,
86 Result_Scalar => Real'Base,
87 X_Vector => Real_Vector,
88 Result_Vector => Real_Vector,
92 Matrix_Elementwise_Operation
93 (X_Scalar => Real'Base,
94 Result_Scalar => Real'Base,
95 X_Matrix => Real_Matrix,
96 Result_Matrix => Real_Matrix,
100 Vector_Vector_Elementwise_Operation
101 (Left_Scalar => Real'Base,
102 Right_Scalar => Real'Base,
103 Result_Scalar => Real'Base,
104 Left_Vector => Real_Vector,
105 Right_Vector => Real_Vector,
106 Result_Vector => Real_Vector,
110 Matrix_Matrix_Elementwise_Operation
111 (Left_Scalar => Real'Base,
112 Right_Scalar => Real'Base,
113 Result_Scalar => Real'Base,
114 Left_Matrix => Real_Matrix,
115 Right_Matrix => Real_Matrix,
116 Result_Matrix => Real_Matrix,
120 Vector_Elementwise_Operation
121 (X_Scalar => Real'Base,
122 Result_Scalar => Real'Base,
123 X_Vector => Real_Vector,
124 Result_Vector => Real_Vector,
128 Matrix_Elementwise_Operation
129 (X_Scalar => Real'Base,
130 Result_Scalar => Real'Base,
131 X_Matrix => Real_Matrix,
132 Result_Matrix => Real_Matrix,
136 Vector_Vector_Elementwise_Operation
137 (Left_Scalar => Real'Base,
138 Right_Scalar => Real'Base,
139 Result_Scalar => Real'Base,
140 Left_Vector => Real_Vector,
141 Right_Vector => Real_Vector,
142 Result_Vector => Real_Vector,
146 Matrix_Matrix_Elementwise_Operation
147 (Left_Scalar => Real'Base,
148 Right_Scalar => Real'Base,
149 Result_Scalar => Real'Base,
150 Left_Matrix => Real_Matrix,
151 Right_Matrix => Real_Matrix,
152 Result_Matrix => Real_Matrix,
156 Scalar_Vector_Elementwise_Operation
157 (Left_Scalar => Real'Base,
158 Right_Scalar => Real'Base,
159 Result_Scalar => Real'Base,
160 Right_Vector => Real_Vector,
161 Result_Vector => Real_Vector,
165 Scalar_Matrix_Elementwise_Operation
166 (Left_Scalar => Real'Base,
167 Right_Scalar => Real'Base,
168 Result_Scalar => Real'Base,
169 Right_Matrix => Real_Matrix,
170 Result_Matrix => Real_Matrix,
174 Vector_Scalar_Elementwise_Operation
175 (Left_Scalar => Real'Base,
176 Right_Scalar => Real'Base,
177 Result_Scalar => Real'Base,
178 Left_Vector => Real_Vector,
179 Result_Vector => Real_Vector,
183 Matrix_Scalar_Elementwise_Operation
184 (Left_Scalar => Real'Base,
185 Right_Scalar => Real'Base,
186 Result_Scalar => Real'Base,
187 Left_Matrix => Real_Matrix,
188 Result_Matrix => Real_Matrix,
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 Matrix => Real_Matrix);
201 Vector_Scalar_Elementwise_Operation
202 (Left_Scalar => Real'Base,
203 Right_Scalar => Real'Base,
204 Result_Scalar => Real'Base,
205 Left_Vector => Real_Vector,
206 Result_Vector => Real_Vector,
210 Matrix_Scalar_Elementwise_Operation
211 (Left_Scalar => Real'Base,
212 Right_Scalar => Real'Base,
213 Result_Scalar => Real'Base,
214 Left_Matrix => Real_Matrix,
215 Result_Matrix => Real_Matrix,
218 function "abs" is new
219 Vector_Elementwise_Operation
220 (X_Scalar => Real'Base,
221 Result_Scalar => Real'Base,
222 X_Vector => Real_Vector,
223 Result_Vector => Real_Vector,
226 function "abs" is new
227 Matrix_Elementwise_Operation
228 (X_Scalar => Real'Base,
229 Result_Scalar => Real'Base,
230 X_Matrix => Real_Matrix,
231 Result_Matrix => Real_Matrix,
234 function Unit_Matrix is new
235 Generic_Array_Operations.Unit_Matrix
236 (Scalar => Real'Base,
237 Matrix => Real_Matrix,
241 function Unit_Vector is new
242 Generic_Array_Operations.Unit_Vector
243 (Scalar => Real'Base,
244 Vector => Real_Vector,
254 function "+" (Right : Real_Vector) return Real_Vector
255 renames Instantiations."+";
257 function "+" (Right : Real_Matrix) return Real_Matrix
258 renames Instantiations."+";
260 function "+" (Left, Right : Real_Vector) return Real_Vector
261 renames Instantiations."+";
263 function "+" (Left, Right : Real_Matrix) return Real_Matrix
264 renames Instantiations."+";
270 function "-" (Right : Real_Vector) return Real_Vector
271 renames Instantiations."-";
273 function "-" (Right : Real_Matrix) return Real_Matrix
274 renames Instantiations."-";
276 function "-" (Left, Right : Real_Vector) return Real_Vector
277 renames Instantiations."-";
279 function "-" (Left, Right : Real_Matrix) return Real_Matrix
280 renames Instantiations."-";
286 -- Scalar multiplication
288 function "*" (Left : Real'Base; Right : Real_Vector) return Real_Vector
289 renames Instantiations."*";
291 function "*" (Left : Real_Vector; Right : Real'Base) return Real_Vector
292 renames Instantiations."*";
294 function "*" (Left : Real'Base; Right : Real_Matrix) return Real_Matrix
295 renames Instantiations."*";
297 function "*" (Left : Real_Matrix; Right : Real'Base) return Real_Matrix
298 renames Instantiations."*";
300 -- Vector multiplication
302 function "*" (Left, Right : Real_Vector) return Real'Base is
304 if Left'Length /= Right'Length then
305 raise Constraint_Error with
306 "vectors are of different length in inner product";
309 return dot (Left'Length, X => Left, Y => Right);
312 function "*" (Left, Right : Real_Vector) return Real_Matrix
313 renames Instantiations."*";
317 Right : Real_Matrix) return Real_Vector
319 R : Real_Vector (Right'Range (2));
322 if Left'Length /= Right'Length (1) then
323 raise Constraint_Error with
324 "incompatible dimensions in vector-matrix multiplication";
327 gemv (Trans => No_Trans'Access,
328 M => Right'Length (2),
329 N => Right'Length (1),
331 Ld_A => Right'Length (2),
340 Right : Real_Vector) return Real_Vector
342 R : Real_Vector (Left'Range (1));
345 if Left'Length (2) /= Right'Length then
346 raise Constraint_Error with
347 "incompatible dimensions in matrix-vector multiplication";
350 gemv (Trans => Trans'Access,
351 M => Left'Length (2),
352 N => Left'Length (1),
354 Ld_A => Left'Length (2),
361 -- Matrix Multiplication
363 function "*" (Left, Right : Real_Matrix) return Real_Matrix is
364 R : Real_Matrix (Left'Range (1), Right'Range (2));
367 if Left'Length (2) /= Right'Length (1) then
368 raise Constraint_Error with
369 "incompatible dimensions in matrix-matrix multiplication";
372 gemm (Trans_A => No_Trans'Access,
373 Trans_B => No_Trans'Access,
374 M => Right'Length (2),
375 N => Left'Length (1),
376 K => Right'Length (1),
378 Ld_A => Right'Length (2),
380 Ld_B => Left'Length (2),
382 Ld_C => R'Length (2));
391 function "/" (Left : Real_Vector; Right : Real'Base) return Real_Vector
392 renames Instantiations."/";
394 function "/" (Left : Real_Matrix; Right : Real'Base) return Real_Matrix
395 renames Instantiations."/";
401 function "abs" (Right : Real_Vector) return Real'Base is
403 return nrm2 (Right'Length, Right);
406 function "abs" (Right : Real_Vector) return Real_Vector
407 renames Instantiations."abs";
409 function "abs" (Right : Real_Matrix) return Real_Matrix
410 renames Instantiations."abs";
416 function Determinant (A : Real_Matrix) return Real'Base is
417 N : constant Integer := Length (A);
418 LU : Real_Matrix (1 .. N, 1 .. N) := A;
419 Piv : Integer_Vector (1 .. N);
420 Info : aliased Integer := -1;
429 Info => Info'Access);
432 raise Constraint_Error with "ill-conditioned matrix";
437 Det := -Det * LU (J, J);
439 Det := Det * LU (J, J);
450 procedure Eigensystem
452 Values : out Real_Vector;
453 Vectors : out Real_Matrix)
455 N : constant Natural := Length (A);
456 Tau : Real_Vector (1 .. N);
457 L_Work : Real_Vector (1 .. 1);
458 Info : aliased Integer;
460 E : Real_Vector (1 .. N);
461 pragma Warnings (Off, E);
464 if Values'Length /= N then
465 raise Constraint_Error with "wrong length for output vector";
472 -- Initialize working matrix and check for symmetric input matrix
474 Transpose (A, Vectors);
477 raise Argument_Error with "matrix not symmetric";
480 -- Compute size of additional working space
482 sytrd (Uplo => Lower'Access,
491 Info => Info'Access);
494 Work : Real_Vector (1 .. Integer'Max (Integer (L_Work (1)), 2 * N));
495 pragma Warnings (Off, Work);
497 Comp_Z : aliased constant Character := 'V';
500 -- Reduce matrix to tridiagonal form
502 sytrd (Uplo => Lower'Access,
505 Ld_A => A'Length (1),
510 L_Work => Work'Length,
511 Info => Info'Access);
517 -- Generate the real orthogonal matrix determined by sytrd
519 orgtr (Uplo => Lower'Access,
525 L_Work => Work'Length,
526 Info => Info'Access);
532 -- Compute all eigenvalues and eigenvectors using QR algorithm
534 steqr (Comp_Z => Comp_Z'Access,
541 Info => Info'Access);
544 raise Constraint_Error with
545 "eigensystem computation failed to converge";
554 procedure Eigenvalues
556 Values : out Real_Vector)
558 N : constant Natural := Length (A);
559 L_Work : Real_Vector (1 .. 1);
560 Info : aliased Integer;
562 B : Real_Matrix (1 .. N, 1 .. N);
563 Tau : Real_Vector (1 .. N);
564 E : Real_Vector (1 .. N);
565 pragma Warnings (Off, B);
566 pragma Warnings (Off, Tau);
567 pragma Warnings (Off, E);
570 if Values'Length /= N then
571 raise Constraint_Error with "wrong length for output vector";
578 -- Initialize working matrix and check for symmetric input matrix
583 raise Argument_Error with "matrix not symmetric";
586 -- Find size of work area
588 sytrd (Uplo => Lower'Access,
597 Info => Info'Access);
600 Work : Real_Vector (1 .. Integer'Min (Integer (L_Work (1)), 4 * N));
601 pragma Warnings (Off, Work);
604 -- Reduce matrix to tridiagonal form
606 sytrd (Uplo => Lower'Access,
609 Ld_A => A'Length (1),
614 L_Work => Work'Length,
615 Info => Info'Access);
618 raise Constraint_Error;
621 -- Compute all eigenvalues using QR algorithm
626 Info => Info'Access);
629 raise Constraint_Error with
630 "eigenvalues computation failed to converge";
635 function Eigenvalues (A : Real_Matrix) return Real_Vector is
636 R : Real_Vector (A'Range (1));
646 procedure Inverse (A : Real_Matrix; R : out Real_Matrix) is
647 N : constant Integer := Length (A);
648 Piv : Integer_Vector (1 .. N);
649 L_Work : Real_Vector (1 .. 1);
650 Info : aliased Integer := -1;
653 -- All computations are done using column-major order, but this works
654 -- out fine, because Transpose (Inverse (Transpose (A))) = Inverse (A).
658 -- Compute LU decomposition
665 Info => Info'Access);
668 raise Constraint_Error with "inverting singular matrix";
671 -- Determine size of work area
679 Info => Info'Access);
682 raise Constraint_Error;
686 Work : Real_Vector (1 .. Integer (L_Work (1)));
687 pragma Warnings (Off, Work);
690 -- Compute inverse from LU decomposition
697 L_Work => Work'Length,
698 Info => Info'Access);
701 raise Constraint_Error with "inverting singular matrix";
704 -- ??? Should iterate with gerfs, based on implementation advice
708 function Inverse (A : Real_Matrix) return Real_Matrix is
709 R : Real_Matrix (A'Range (2), A'Range (1));
719 procedure Solve (A : Real_Matrix; X : Real_Vector; B : out Real_Vector) is
721 if Length (A) /= X'Length then
722 raise Constraint_Error with
723 "incompatible matrix and vector dimensions";
726 -- ??? Should solve directly, is faster and more accurate
728 B := Inverse (A) * X;
731 procedure Solve (A : Real_Matrix; X : Real_Matrix; B : out Real_Matrix) is
733 if Length (A) /= X'Length (1) then
734 raise Constraint_Error with "incompatible matrix dimensions";
737 -- ??? Should solve directly, is faster and more accurate
739 B := Inverse (A) * X;
742 function Solve (A : Real_Matrix; X : Real_Vector) return Real_Vector is
743 B : Real_Vector (A'Range (2));
749 function Solve (A, X : Real_Matrix) return Real_Matrix is
750 B : Real_Matrix (A'Range (2), X'Range (2));
760 function Transpose (X : Real_Matrix) return Real_Matrix is
761 R : Real_Matrix (X'Range (2), X'Range (1));
774 First_1 : Integer := 1;
775 First_2 : Integer := 1) return Real_Matrix
776 renames Instantiations.Unit_Matrix;
785 First : Integer := 1) return Real_Vector
786 renames Instantiations.Unit_Vector;
788 end Ada.Numerics.Generic_Real_Arrays;