1 ------------------------------------------------------------------------------
3 -- GNAT RUN-TIME COMPONENTS --
5 -- S Y S T E M . G E N E R I C _ A R R A Y _ O P E R A T I O N S --
9 -- Copyright (C) 2006-2012, 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 Ada.Numerics; use Ada.Numerics;
34 package body System.Generic_Array_Operations is
36 -- The local function Check_Unit_Last computes the index of the last
37 -- element returned by Unit_Vector or Unit_Matrix. A separate function is
38 -- needed to allow raising Constraint_Error before declaring the function
39 -- result variable. The result variable needs to be declared first, to
40 -- allow front-end inlining.
42 function Check_Unit_Last
45 First : Integer) return Integer;
46 pragma Inline_Always (Check_Unit_Last);
52 function Diagonal (A : Matrix) return Vector is
53 N : constant Natural := Natural'Min (A'Length (1), A'Length (2));
54 R : Vector (A'First (1) .. A'First (1) + N - 1);
57 for J in 0 .. N - 1 loop
58 R (R'First + J) := A (A'First (1) + J, A'First (2) + J);
64 --------------------------
65 -- Square_Matrix_Length --
66 --------------------------
68 function Square_Matrix_Length (A : Matrix) return Natural is
70 if A'Length (1) /= A'Length (2) then
71 raise Constraint_Error with "matrix is not square";
75 end Square_Matrix_Length;
81 function Check_Unit_Last
84 First : Integer) return Integer
87 -- Order the tests carefully to avoid overflow
90 or else First > Integer'Last - Order + 1
91 or else Index > First + (Order - 1)
93 raise Constraint_Error;
96 return First + (Order - 1);
100 -- Back_Substitute --
101 ---------------------
103 procedure Back_Substitute (M, N : in out Matrix) is
104 pragma Assert (M'First (1) = N'First (1)
106 M'Last (1) = N'Last (1));
113 -- Elementary row operation that subtracts Factor * M (Source, <>) from
123 for J in M'Range (2) loop
124 M (Target, J) := M (Target, J) - Factor * M (Source, J);
128 -- Local declarations
130 Max_Col : Integer := M'Last (2);
132 -- Start of processing for Back_Substitute
135 Do_Rows : for Row in reverse M'Range (1) loop
136 Find_Non_Zero : for Col in reverse M'First (2) .. Max_Col loop
137 if Is_Non_Zero (M (Row, Col)) then
139 -- Found first non-zero element, so subtract a multiple of this
140 -- element from all higher rows, to reduce all other elements
141 -- in this column to zero.
144 -- We can't use a for loop, as we'd need to iterate to
145 -- Row - 1, but that expression will overflow if M'First
146 -- equals Integer'First, which is true for aggregates
147 -- without explicit bounds..
149 J : Integer := M'First (1);
153 Sub_Row (N, J, Row, (M (J, Col) / M (Row, Col)));
154 Sub_Row (M, J, Row, (M (J, Col) / M (Row, Col)));
159 -- Avoid potential overflow in the subtraction below
161 exit Do_Rows when Col = M'First (2);
167 end loop Find_Non_Zero;
171 -----------------------
172 -- Forward_Eliminate --
173 -----------------------
175 procedure Forward_Eliminate
180 pragma Assert (M'First (1) = N'First (1)
182 M'Last (1) = N'Last (1));
184 -- The following are variations of the elementary matrix row operations:
185 -- row switching, row multiplication and row addition. Because in this
186 -- algorithm the addition factor is always a negated value, we chose to
187 -- use row subtraction instead. Similarly, instead of multiplying by
188 -- a reciprocal, we divide.
195 -- Subtrace Factor * M (Source, <>) from M (Target, <>)
198 (M, N : in out Matrix;
201 -- Divide M (Row) and N (Row) by Scale, and update Det
204 (M, N : in out Matrix;
207 -- Exchange M (Row_1) and N (Row_1) with M (Row_2) and N (Row_2),
208 -- negating Det in the process.
221 for J in M'Range (2) loop
222 M (Target, J) := M (Target, J) - Factor * M (Source, J);
231 (M, N : in out Matrix;
238 for J in M'Range (2) loop
239 M (Row, J) := M (Row, J) / Scale;
242 for J in N'Range (2) loop
243 N (Row - M'First (1) + N'First (1), J) :=
244 N (Row - M'First (1) + N'First (1), J) / Scale;
253 (M, N : in out Matrix;
257 procedure Swap (X, Y : in out Scalar);
258 -- Exchange the values of X and Y
260 procedure Swap (X, Y : in out Scalar) is
261 T : constant Scalar := X;
267 -- Start of processing for Switch_Row
270 if Row_1 /= Row_2 then
273 for J in M'Range (2) loop
274 Swap (M (Row_1, J), M (Row_2, J));
277 for J in N'Range (2) loop
278 Swap (N (Row_1 - M'First (1) + N'First (1), J),
279 N (Row_2 - M'First (1) + N'First (1), J));
284 -- Local declarations
286 Row : Integer := M'First (1);
288 -- Start of processing for Forward_Eliminate
293 for J in M'Range (2) loop
295 Max_Row : Integer := Row;
296 Max_Abs : Real'Base := 0.0;
299 -- Find best pivot in column J, starting in row Row
301 for K in Row .. M'Last (1) loop
303 New_Abs : constant Real'Base := abs M (K, J);
305 if Max_Abs < New_Abs then
312 if Max_Abs > 0.0 then
313 Switch_Row (M, N, Row, Max_Row);
315 -- The temporaries below are necessary to force a copy of the
316 -- value and avoid improper aliasing.
319 Scale : constant Scalar := M (Row, J);
321 Divide_Row (M, N, Row, Scale);
324 for U in Row + 1 .. M'Last (1) loop
326 Factor : constant Scalar := M (U, J);
328 Sub_Row (N, U, Row, Factor);
329 Sub_Row (M, U, Row, Factor);
333 exit when Row >= M'Last (1);
338 -- Set zero (note that we do not have literals)
344 end Forward_Eliminate;
350 function Inner_Product
352 Right : Right_Vector) return Result_Scalar
354 R : Result_Scalar := Zero;
357 if Left'Length /= Right'Length then
358 raise Constraint_Error with
359 "vectors are of different length in inner product";
362 for J in Left'Range loop
363 R := R + Left (J) * Right (J - Left'First + Right'First);
373 function L2_Norm (X : X_Vector) return Result_Real'Base is
374 Sum : Result_Real'Base := 0.0;
377 for J in X'Range loop
378 Sum := Sum + Result_Real'Base (abs X (J))**2;
384 ----------------------------------
385 -- Matrix_Elementwise_Operation --
386 ----------------------------------
388 function Matrix_Elementwise_Operation (X : X_Matrix) return Result_Matrix is
389 R : Result_Matrix (X'Range (1), X'Range (2));
392 for J in R'Range (1) loop
393 for K in R'Range (2) loop
394 R (J, K) := Operation (X (J, K));
399 end Matrix_Elementwise_Operation;
401 ----------------------------------
402 -- Vector_Elementwise_Operation --
403 ----------------------------------
405 function Vector_Elementwise_Operation (X : X_Vector) return Result_Vector is
406 R : Result_Vector (X'Range);
409 for J in R'Range loop
410 R (J) := Operation (X (J));
414 end Vector_Elementwise_Operation;
416 -----------------------------------------
417 -- Matrix_Matrix_Elementwise_Operation --
418 -----------------------------------------
420 function Matrix_Matrix_Elementwise_Operation
422 Right : Right_Matrix) return Result_Matrix
424 R : Result_Matrix (Left'Range (1), Left'Range (2));
427 if Left'Length (1) /= Right'Length (1)
429 Left'Length (2) /= Right'Length (2)
431 raise Constraint_Error with
432 "matrices are of different dimension in elementwise operation";
435 for J in R'Range (1) loop
436 for K in R'Range (2) loop
441 (J - R'First (1) + Right'First (1),
442 K - R'First (2) + Right'First (2)));
447 end Matrix_Matrix_Elementwise_Operation;
449 ------------------------------------------------
450 -- Matrix_Matrix_Scalar_Elementwise_Operation --
451 ------------------------------------------------
453 function Matrix_Matrix_Scalar_Elementwise_Operation
456 Z : Z_Scalar) return Result_Matrix
458 R : Result_Matrix (X'Range (1), X'Range (2));
461 if X'Length (1) /= Y'Length (1)
463 X'Length (2) /= Y'Length (2)
465 raise Constraint_Error with
466 "matrices are of different dimension in elementwise operation";
469 for J in R'Range (1) loop
470 for K in R'Range (2) loop
474 Y (J - R'First (1) + Y'First (1),
475 K - R'First (2) + Y'First (2)),
481 end Matrix_Matrix_Scalar_Elementwise_Operation;
483 -----------------------------------------
484 -- Vector_Vector_Elementwise_Operation --
485 -----------------------------------------
487 function Vector_Vector_Elementwise_Operation
489 Right : Right_Vector) return Result_Vector
491 R : Result_Vector (Left'Range);
494 if Left'Length /= Right'Length then
495 raise Constraint_Error with
496 "vectors are of different length in elementwise operation";
499 for J in R'Range loop
500 R (J) := Operation (Left (J), Right (J - R'First + Right'First));
504 end Vector_Vector_Elementwise_Operation;
506 ------------------------------------------------
507 -- Vector_Vector_Scalar_Elementwise_Operation --
508 ------------------------------------------------
510 function Vector_Vector_Scalar_Elementwise_Operation
513 Z : Z_Scalar) return Result_Vector
515 R : Result_Vector (X'Range);
518 if X'Length /= Y'Length then
519 raise Constraint_Error with
520 "vectors are of different length in elementwise operation";
523 for J in R'Range loop
524 R (J) := Operation (X (J), Y (J - X'First + Y'First), Z);
528 end Vector_Vector_Scalar_Elementwise_Operation;
530 -----------------------------------------
531 -- Matrix_Scalar_Elementwise_Operation --
532 -----------------------------------------
534 function Matrix_Scalar_Elementwise_Operation
536 Right : Right_Scalar) return Result_Matrix
538 R : Result_Matrix (Left'Range (1), Left'Range (2));
541 for J in R'Range (1) loop
542 for K in R'Range (2) loop
543 R (J, K) := Operation (Left (J, K), Right);
548 end Matrix_Scalar_Elementwise_Operation;
550 -----------------------------------------
551 -- Vector_Scalar_Elementwise_Operation --
552 -----------------------------------------
554 function Vector_Scalar_Elementwise_Operation
556 Right : Right_Scalar) return Result_Vector
558 R : Result_Vector (Left'Range);
561 for J in R'Range loop
562 R (J) := Operation (Left (J), Right);
566 end Vector_Scalar_Elementwise_Operation;
568 -----------------------------------------
569 -- Scalar_Matrix_Elementwise_Operation --
570 -----------------------------------------
572 function Scalar_Matrix_Elementwise_Operation
574 Right : Right_Matrix) return Result_Matrix
576 R : Result_Matrix (Right'Range (1), Right'Range (2));
579 for J in R'Range (1) loop
580 for K in R'Range (2) loop
581 R (J, K) := Operation (Left, Right (J, K));
586 end Scalar_Matrix_Elementwise_Operation;
588 -----------------------------------------
589 -- Scalar_Vector_Elementwise_Operation --
590 -----------------------------------------
592 function Scalar_Vector_Elementwise_Operation
594 Right : Right_Vector) return Result_Vector
596 R : Result_Vector (Right'Range);
599 for J in R'Range loop
600 R (J) := Operation (Left, Right (J));
604 end Scalar_Vector_Elementwise_Operation;
610 function Sqrt (X : Real'Base) return Real'Base is
611 Root, Next : Real'Base;
614 -- Be defensive: any comparisons with NaN values will yield False.
616 if not (X > 0.0) then
620 raise Argument_Error;
623 elsif X > Real'Base'Last then
625 -- X is infinity, which is its own square root
630 -- Compute an initial estimate based on:
632 -- X = M * R**E and Sqrt (X) = Sqrt (M) * R**(E / 2.0),
634 -- where M is the mantissa, R is the radix and E the exponent.
636 -- By ignoring the mantissa and ignoring the case of an odd
637 -- exponent, we get a final error that is at most R. In other words,
638 -- the result has about a single bit precision.
640 Root := Real'Base (Real'Machine_Radix) ** (Real'Exponent (X) / 2);
642 -- Because of the poor initial estimate, use the Babylonian method of
643 -- computing the square root, as it is stable for all inputs. Every step
644 -- will roughly double the precision of the result. Just a few steps
645 -- suffice in most cases. Eight iterations should give about 2**8 bits
649 Next := (Root + X / Root) / 2.0;
650 exit when Root = Next;
657 ---------------------------
658 -- Matrix_Matrix_Product --
659 ---------------------------
661 function Matrix_Matrix_Product
663 Right : Right_Matrix) return Result_Matrix
665 R : Result_Matrix (Left'Range (1), Right'Range (2));
668 if Left'Length (2) /= Right'Length (1) then
669 raise Constraint_Error with
670 "incompatible dimensions in matrix multiplication";
673 for J in R'Range (1) loop
674 for K in R'Range (2) loop
676 S : Result_Scalar := Zero;
679 for M in Left'Range (2) loop
680 S := S + Left (J, M) *
681 Right (M - Left'First (2) + Right'First (1), K);
690 end Matrix_Matrix_Product;
692 ----------------------------
693 -- Matrix_Vector_Solution --
694 ----------------------------
696 function Matrix_Vector_Solution (A : Matrix; X : Vector) return Vector is
697 N : constant Natural := A'Length (1);
699 MX : Matrix (A'Range (1), 1 .. 1);
700 R : Vector (A'Range (2));
704 if A'Length (2) /= N then
705 raise Constraint_Error with "matrix is not square";
708 if X'Length /= N then
709 raise Constraint_Error with "incompatible vector length";
712 for J in 0 .. MX'Length (1) - 1 loop
713 MX (MX'First (1) + J, 1) := X (X'First + J);
716 Forward_Eliminate (MA, MX, Det);
717 Back_Substitute (MA, MX);
719 for J in 0 .. R'Length - 1 loop
720 R (R'First + J) := MX (MX'First (1) + J, 1);
724 end Matrix_Vector_Solution;
726 ----------------------------
727 -- Matrix_Matrix_Solution --
728 ----------------------------
730 function Matrix_Matrix_Solution (A, X : Matrix) return Matrix is
731 N : constant Natural := A'Length (1);
732 MA : Matrix (A'Range (2), A'Range (2));
733 MB : Matrix (A'Range (2), X'Range (2));
737 if A'Length (2) /= N then
738 raise Constraint_Error with "matrix is not square";
741 if X'Length (1) /= N then
742 raise Constraint_Error with "matrices have unequal number of rows";
745 for J in 0 .. A'Length (1) - 1 loop
746 for K in MA'Range (2) loop
747 MA (MA'First (1) + J, K) := A (A'First (1) + J, K);
750 for K in MB'Range (2) loop
751 MB (MB'First (1) + J, K) := X (X'First (1) + J, K);
755 Forward_Eliminate (MA, MB, Det);
756 Back_Substitute (MA, MB);
759 end Matrix_Matrix_Solution;
761 ---------------------------
762 -- Matrix_Vector_Product --
763 ---------------------------
765 function Matrix_Vector_Product
767 Right : Right_Vector) return Result_Vector
769 R : Result_Vector (Left'Range (1));
772 if Left'Length (2) /= Right'Length then
773 raise Constraint_Error with
774 "incompatible dimensions in matrix-vector multiplication";
777 for J in Left'Range (1) loop
779 S : Result_Scalar := Zero;
782 for K in Left'Range (2) loop
783 S := S + Left (J, K) * Right (K - Left'First (2) + Right'First);
791 end Matrix_Vector_Product;
797 function Outer_Product
799 Right : Right_Vector) return Matrix
801 R : Matrix (Left'Range, Right'Range);
804 for J in R'Range (1) loop
805 for K in R'Range (2) loop
806 R (J, K) := Left (J) * Right (K);
817 procedure Swap_Column (A : in out Matrix; Left, Right : Integer) is
820 for J in A'Range (1) loop
822 A (J, Left) := A (J, Right);
823 A (J, Right) := Temp;
831 procedure Transpose (A : Matrix; R : out Matrix) is
833 for J in R'Range (1) loop
834 for K in R'Range (2) loop
835 R (J, K) := A (K - R'First (2) + A'First (1),
836 J - R'First (1) + A'First (2));
841 -------------------------------
842 -- Update_Matrix_With_Matrix --
843 -------------------------------
845 procedure Update_Matrix_With_Matrix (X : in out X_Matrix; Y : Y_Matrix) is
847 if X'Length (1) /= Y'Length (1)
848 or else X'Length (2) /= Y'Length (2)
850 raise Constraint_Error with
851 "matrices are of different dimension in update operation";
854 for J in X'Range (1) loop
855 for K in X'Range (2) loop
856 Update (X (J, K), Y (J - X'First (1) + Y'First (1),
857 K - X'First (2) + Y'First (2)));
860 end Update_Matrix_With_Matrix;
862 -------------------------------
863 -- Update_Vector_With_Vector --
864 -------------------------------
866 procedure Update_Vector_With_Vector (X : in out X_Vector; Y : Y_Vector) is
868 if X'Length /= Y'Length then
869 raise Constraint_Error with
870 "vectors are of different length in update operation";
873 for J in X'Range loop
874 Update (X (J), Y (J - X'First + Y'First));
876 end Update_Vector_With_Vector;
884 First_1 : Integer := 1;
885 First_2 : Integer := 1) return Matrix
887 R : Matrix (First_1 .. Check_Unit_Last (First_1, Order, First_1),
888 First_2 .. Check_Unit_Last (First_2, Order, First_2));
891 R := (others => (others => Zero));
893 for J in 0 .. Order - 1 loop
894 R (First_1 + J, First_2 + J) := One;
907 First : Integer := 1) return Vector
909 R : Vector (First .. Check_Unit_Last (Index, Order, First));
911 R := (others => Zero);
916 ---------------------------
917 -- Vector_Matrix_Product --
918 ---------------------------
920 function Vector_Matrix_Product
922 Right : Matrix) return Result_Vector
924 R : Result_Vector (Right'Range (2));
927 if Left'Length /= Right'Length (2) then
928 raise Constraint_Error with
929 "incompatible dimensions in vector-matrix multiplication";
932 for J in Right'Range (2) loop
934 S : Result_Scalar := Zero;
937 for K in Right'Range (1) loop
938 S := S + Left (J - Right'First (1) + Left'First) * Right (K, J);
946 end Vector_Matrix_Product;
948 end System.Generic_Array_Operations;