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-2018, 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
;
33 package body System
.Generic_Array_Operations
is
34 function Check_Unit_Last
37 First
: Integer) return Integer;
38 pragma Inline_Always
(Check_Unit_Last
);
39 -- Compute index of last element returned by Unit_Vector or Unit_Matrix.
40 -- A separate function is needed to allow raising Constraint_Error before
41 -- declaring the function result variable. The result variable needs to be
42 -- declared first, to allow front-end inlining.
48 function Diagonal
(A
: Matrix
) return Vector
is
49 N
: constant Natural := Natural'Min (A
'Length (1), A
'Length (2));
51 return R
: Vector
(A
'First (1) .. A
'First (1) + N
- 1) do
52 for J
in 0 .. N
- 1 loop
53 R
(R
'First + J
) := A
(A
'First (1) + J
, A
'First (2) + J
);
58 --------------------------
59 -- Square_Matrix_Length --
60 --------------------------
62 function Square_Matrix_Length
(A
: Matrix
) return Natural is
64 if A
'Length (1) /= A
'Length (2) then
65 raise Constraint_Error
with "matrix is not square";
69 end Square_Matrix_Length
;
75 function Check_Unit_Last
78 First
: Integer) return Integer
81 -- Order the tests carefully to avoid overflow
84 or else First
> Integer'Last - Order
+ 1
85 or else Index
> First
+ (Order
- 1)
87 raise Constraint_Error
;
90 return First
+ (Order
- 1);
97 procedure Back_Substitute
(M
, N
: in out Matrix
) is
98 pragma Assert
(M
'First (1) = N
'First (1)
100 M
'Last (1) = N
'Last (1));
107 -- Elementary row operation that subtracts Factor * M (Source, <>) from
121 for J
in M
'Range (2) loop
122 M
(Target
, J
) := M
(Target
, J
) - Factor
* M
(Source
, J
);
126 -- Local declarations
128 Max_Col
: Integer := M
'Last (2);
130 -- Start of processing for Back_Substitute
133 Do_Rows
: for Row
in reverse M
'Range (1) loop
134 Find_Non_Zero
: for Col
in reverse M
'First (2) .. Max_Col
loop
135 if Is_Non_Zero
(M
(Row
, Col
)) then
137 -- Found first non-zero element, so subtract a multiple of this
138 -- element from all higher rows, to reduce all other elements
139 -- in this column to zero.
142 -- We can't use a for loop, as we'd need to iterate to
143 -- Row - 1, but that expression will overflow if M'First
144 -- equals Integer'First, which is true for aggregates
145 -- without explicit bounds..
147 J
: Integer := M
'First (1);
151 Sub_Row
(N
, J
, Row
, (M
(J
, Col
) / M
(Row
, Col
)));
152 Sub_Row
(M
, J
, Row
, (M
(J
, Col
) / M
(Row
, Col
)));
157 -- Avoid potential overflow in the subtraction below
159 exit Do_Rows
when Col
= M
'First (2);
165 end loop Find_Non_Zero
;
169 -----------------------
170 -- Forward_Eliminate --
171 -----------------------
173 procedure Forward_Eliminate
178 pragma Assert
(M
'First (1) = N
'First (1)
180 M
'Last (1) = N
'Last (1));
182 -- The following are variations of the elementary matrix row operations:
183 -- row switching, row multiplication and row addition. Because in this
184 -- algorithm the addition factor is always a negated value, we chose to
185 -- use row subtraction instead. Similarly, instead of multiplying by
186 -- a reciprocal, we divide.
193 -- Subtrace Factor * M (Source, <>) from M (Target, <>)
196 (M
, N
: in out Matrix
;
199 -- Divide M (Row) and N (Row) by Scale, and update Det
202 (M
, N
: in out Matrix
;
205 -- Exchange M (Row_1) and N (Row_1) with M (Row_2) and N (Row_2),
206 -- negating Det in the process.
219 for J
in M
'Range (2) loop
220 M
(Target
, J
) := M
(Target
, J
) - Factor
* M
(Source
, J
);
229 (M
, N
: in out Matrix
;
236 for J
in M
'Range (2) loop
237 M
(Row
, J
) := M
(Row
, J
) / Scale
;
240 for J
in N
'Range (2) loop
241 N
(Row
- M
'First (1) + N
'First (1), J
) :=
242 N
(Row
- M
'First (1) + N
'First (1), J
) / Scale
;
251 (M
, N
: in out Matrix
;
255 procedure Swap
(X
, Y
: in out Scalar
);
256 -- Exchange the values of X and Y
262 procedure Swap
(X
, Y
: in out Scalar
) is
263 T
: constant Scalar
:= X
;
269 -- Start of processing for Switch_Row
272 if Row_1
/= Row_2
then
275 for J
in M
'Range (2) loop
276 Swap
(M
(Row_1
, J
), M
(Row_2
, J
));
279 for J
in N
'Range (2) loop
280 Swap
(N
(Row_1
- M
'First (1) + N
'First (1), J
),
281 N
(Row_2
- M
'First (1) + N
'First (1), J
));
286 -- Local declarations
288 Row
: Integer := M
'First (1);
290 -- Start of processing for Forward_Eliminate
295 for J
in M
'Range (2) loop
297 Max_Row
: Integer := Row
;
298 Max_Abs
: Real
'Base := 0.0;
301 -- Find best pivot in column J, starting in row Row
303 for K
in Row
.. M
'Last (1) loop
305 New_Abs
: constant Real
'Base := abs M
(K
, J
);
307 if Max_Abs
< New_Abs
then
314 if Max_Abs
> 0.0 then
315 Switch_Row
(M
, N
, Row
, Max_Row
);
317 -- The temporaries below are necessary to force a copy of the
318 -- value and avoid improper aliasing.
321 Scale
: constant Scalar
:= M
(Row
, J
);
323 Divide_Row
(M
, N
, Row
, Scale
);
326 for U
in Row
+ 1 .. M
'Last (1) loop
328 Factor
: constant Scalar
:= M
(U
, J
);
330 Sub_Row
(N
, U
, Row
, Factor
);
331 Sub_Row
(M
, U
, Row
, Factor
);
335 exit when Row
>= M
'Last (1);
340 -- Set zero (note that we do not have literals)
346 end Forward_Eliminate
;
352 function Inner_Product
354 Right
: Right_Vector
) return Result_Scalar
356 R
: Result_Scalar
:= Zero
;
359 if Left
'Length /= Right
'Length then
360 raise Constraint_Error
with
361 "vectors are of different length in inner product";
364 for J
in Left
'Range loop
365 R
:= R
+ Left
(J
) * Right
(J
- Left
'First + Right
'First);
375 function L2_Norm
(X
: X_Vector
) return Result_Real
'Base is
376 Sum
: Result_Real
'Base := 0.0;
379 for J
in X
'Range loop
380 Sum
:= Sum
+ Result_Real
'Base (abs X
(J
))**2;
386 ----------------------------------
387 -- Matrix_Elementwise_Operation --
388 ----------------------------------
390 function Matrix_Elementwise_Operation
(X
: X_Matrix
) return Result_Matrix
is
392 return R
: Result_Matrix
(X
'Range (1), X
'Range (2)) do
393 for J
in R
'Range (1) loop
394 for K
in R
'Range (2) loop
395 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
407 return R
: Result_Vector
(X
'Range) do
408 for J
in R
'Range loop
409 R
(J
) := Operation
(X
(J
));
412 end Vector_Elementwise_Operation
;
414 -----------------------------------------
415 -- Matrix_Matrix_Elementwise_Operation --
416 -----------------------------------------
418 function Matrix_Matrix_Elementwise_Operation
420 Right
: Right_Matrix
) return Result_Matrix
423 return R
: Result_Matrix
(Left
'Range (1), Left
'Range (2)) do
424 if Left
'Length (1) /= Right
'Length (1)
426 Left
'Length (2) /= Right
'Length (2)
428 raise Constraint_Error
with
429 "matrices are of different dimension in elementwise operation";
432 for J
in R
'Range (1) loop
433 for K
in R
'Range (2) loop
438 (J
- R
'First (1) + Right
'First (1),
439 K
- R
'First (2) + Right
'First (2)));
443 end Matrix_Matrix_Elementwise_Operation
;
445 ------------------------------------------------
446 -- Matrix_Matrix_Scalar_Elementwise_Operation --
447 ------------------------------------------------
449 function Matrix_Matrix_Scalar_Elementwise_Operation
452 Z
: Z_Scalar
) return Result_Matrix
455 return R
: Result_Matrix
(X
'Range (1), X
'Range (2)) do
456 if X
'Length (1) /= Y
'Length (1)
458 X
'Length (2) /= Y
'Length (2)
460 raise Constraint_Error
with
461 "matrices are of different dimension in elementwise operation";
464 for J
in R
'Range (1) loop
465 for K
in R
'Range (2) loop
469 Y
(J
- R
'First (1) + Y
'First (1),
470 K
- R
'First (2) + Y
'First (2)),
475 end Matrix_Matrix_Scalar_Elementwise_Operation
;
477 -----------------------------------------
478 -- Vector_Vector_Elementwise_Operation --
479 -----------------------------------------
481 function Vector_Vector_Elementwise_Operation
483 Right
: Right_Vector
) return Result_Vector
486 return R
: Result_Vector
(Left
'Range) do
487 if Left
'Length /= Right
'Length then
488 raise Constraint_Error
with
489 "vectors are of different length in elementwise operation";
492 for J
in R
'Range loop
493 R
(J
) := Operation
(Left
(J
), Right
(J
- R
'First + Right
'First));
496 end Vector_Vector_Elementwise_Operation
;
498 ------------------------------------------------
499 -- Vector_Vector_Scalar_Elementwise_Operation --
500 ------------------------------------------------
502 function Vector_Vector_Scalar_Elementwise_Operation
505 Z
: Z_Scalar
) return Result_Vector
is
507 return R
: Result_Vector
(X
'Range) do
508 if X
'Length /= Y
'Length then
509 raise Constraint_Error
with
510 "vectors are of different length in elementwise operation";
513 for J
in R
'Range loop
514 R
(J
) := Operation
(X
(J
), Y
(J
- X
'First + Y
'First), Z
);
517 end Vector_Vector_Scalar_Elementwise_Operation
;
519 -----------------------------------------
520 -- Matrix_Scalar_Elementwise_Operation --
521 -----------------------------------------
523 function Matrix_Scalar_Elementwise_Operation
525 Right
: Right_Scalar
) return Result_Matrix
528 return R
: Result_Matrix
(Left
'Range (1), Left
'Range (2)) do
529 for J
in R
'Range (1) loop
530 for K
in R
'Range (2) loop
531 R
(J
, K
) := Operation
(Left
(J
, K
), Right
);
535 end Matrix_Scalar_Elementwise_Operation
;
537 -----------------------------------------
538 -- Vector_Scalar_Elementwise_Operation --
539 -----------------------------------------
541 function Vector_Scalar_Elementwise_Operation
543 Right
: Right_Scalar
) return Result_Vector
546 return R
: Result_Vector
(Left
'Range) do
547 for J
in R
'Range loop
548 R
(J
) := Operation
(Left
(J
), Right
);
551 end Vector_Scalar_Elementwise_Operation
;
553 -----------------------------------------
554 -- Scalar_Matrix_Elementwise_Operation --
555 -----------------------------------------
557 function Scalar_Matrix_Elementwise_Operation
559 Right
: Right_Matrix
) return Result_Matrix
562 return R
: Result_Matrix
(Right
'Range (1), Right
'Range (2)) do
563 for J
in R
'Range (1) loop
564 for K
in R
'Range (2) loop
565 R
(J
, K
) := Operation
(Left
, Right
(J
, K
));
569 end Scalar_Matrix_Elementwise_Operation
;
571 -----------------------------------------
572 -- Scalar_Vector_Elementwise_Operation --
573 -----------------------------------------
575 function Scalar_Vector_Elementwise_Operation
577 Right
: Right_Vector
) return Result_Vector
580 return R
: Result_Vector
(Right
'Range) do
581 for J
in R
'Range loop
582 R
(J
) := Operation
(Left
, Right
(J
));
585 end Scalar_Vector_Elementwise_Operation
;
591 function Sqrt
(X
: Real
'Base) return Real
'Base is
592 Root
, Next
: Real
'Base;
595 -- Be defensive: any comparisons with NaN values will yield False.
597 if not (X
> 0.0) then
601 raise Argument_Error
;
604 elsif X
> Real
'Base'Last then
606 -- X is infinity, which is its own square root
611 -- Compute an initial estimate based on:
613 -- X = M * R**E and Sqrt (X) = Sqrt (M) * R**(E / 2.0),
615 -- where M is the mantissa, R is the radix and E the exponent.
617 -- By ignoring the mantissa and ignoring the case of an odd
618 -- exponent, we get a final error that is at most R. In other words,
619 -- the result has about a single bit precision.
621 Root := Real'Base (Real'Machine_Radix) ** (Real'Exponent (X) / 2);
623 -- Because of the poor initial estimate, use the Babylonian method of
624 -- computing the square root, as it is stable for all inputs. Every step
625 -- will roughly double the precision of the result. Just a few steps
626 -- suffice in most cases. Eight iterations should give about 2**8 bits
630 Next := (Root + X / Root) / 2.0;
631 exit when Root = Next;
638 ---------------------------
639 -- Matrix_Matrix_Product --
640 ---------------------------
642 function Matrix_Matrix_Product
644 Right : Right_Matrix) return Result_Matrix
647 return R : Result_Matrix (Left'Range (1), Right'Range (2)) do
648 if Left'Length (2) /= Right'Length (1) then
649 raise Constraint_Error with
650 "incompatible dimensions in matrix multiplication";
653 for J in R'Range (1) loop
654 for K in R'Range (2) loop
656 S : Result_Scalar := Zero;
659 for M in Left'Range (2) loop
660 S := S + Left (J, M) *
662 (M - Left'First (2) + Right'First (1), K);
670 end Matrix_Matrix_Product;
672 ----------------------------
673 -- Matrix_Vector_Solution --
674 ----------------------------
676 function Matrix_Vector_Solution (A : Matrix; X : Vector) return Vector is
677 N : constant Natural := A'Length (1);
679 MX : Matrix (A'Range (1), 1 .. 1);
680 R : Vector (A'Range (2));
684 if A'Length (2) /= N then
685 raise Constraint_Error with "matrix is not square";
688 if X'Length /= N then
689 raise Constraint_Error with "incompatible vector length";
692 for J in 0 .. MX'Length (1) - 1 loop
693 MX (MX'First (1) + J, 1) := X (X'First + J);
696 Forward_Eliminate (MA, MX, Det);
699 raise Constraint_Error with "matrix is singular";
702 Back_Substitute (MA, MX);
704 for J in 0 .. R'Length - 1 loop
705 R (R'First + J) := MX (MX'First (1) + J, 1);
709 end Matrix_Vector_Solution;
711 ----------------------------
712 -- Matrix_Matrix_Solution --
713 ----------------------------
715 function Matrix_Matrix_Solution (A, X : Matrix) return Matrix is
716 N : constant Natural := A'Length (1);
717 MA : Matrix (A'Range (2), A'Range (2));
718 MB : Matrix (A'Range (2), X'Range (2));
722 if A'Length (2) /= N then
723 raise Constraint_Error with "matrix is not square";
726 if X'Length (1) /= N then
727 raise Constraint_Error with "matrices have unequal number of rows";
730 for J in 0 .. A'Length (1) - 1 loop
731 for K in MA'Range (2) loop
732 MA (MA'First (1) + J, K) := A (A'First (1) + J, K);
735 for K in MB'Range (2) loop
736 MB (MB'First (1) + J, K) := X (X'First (1) + J, K);
740 Forward_Eliminate (MA, MB, Det);
743 raise Constraint_Error with "matrix is singular";
746 Back_Substitute (MA, MB);
749 end Matrix_Matrix_Solution;
751 ---------------------------
752 -- Matrix_Vector_Product --
753 ---------------------------
755 function Matrix_Vector_Product
757 Right : Right_Vector) return Result_Vector
760 return R : Result_Vector (Left'Range (1)) do
761 if Left'Length (2) /= Right'Length then
762 raise Constraint_Error with
763 "incompatible dimensions in matrix-vector multiplication";
766 for J in Left'Range (1) loop
768 S : Result_Scalar := Zero;
771 for K in Left'Range (2) loop
773 * Right (K - Left'First (2) + Right'First);
780 end Matrix_Vector_Product;
786 function Outer_Product
788 Right : Right_Vector) return Matrix
791 return R : Matrix (Left'Range, Right'Range) do
792 for J in R'Range (1) loop
793 for K in R'Range (2) loop
794 R (J, K) := Left (J) * Right (K);
804 procedure Swap_Column (A : in out Matrix; Left, Right : Integer) is
807 for J in A'Range (1) loop
809 A (J, Left) := A (J, Right);
810 A (J, Right) := Temp;
818 procedure Transpose (A : Matrix; R : out Matrix) is
820 for J in R'Range (1) loop
821 for K in R'Range (2) loop
822 R (J, K) := A (K - R'First (2) + A'First (1),
823 J - R'First (1) + A'First (2));
828 -------------------------------
829 -- Update_Matrix_With_Matrix --
830 -------------------------------
832 procedure Update_Matrix_With_Matrix (X : in out X_Matrix; Y : Y_Matrix) is
834 if X'Length (1) /= Y'Length (1)
836 X'Length (2) /= Y'Length (2)
838 raise Constraint_Error with
839 "matrices are of different dimension in update operation";
842 for J in X'Range (1) loop
843 for K in X'Range (2) loop
844 Update (X (J, K), Y (J - X'First (1) + Y'First (1),
845 K - X'First (2) + Y'First (2)));
848 end Update_Matrix_With_Matrix;
850 -------------------------------
851 -- Update_Vector_With_Vector --
852 -------------------------------
854 procedure Update_Vector_With_Vector (X : in out X_Vector; Y : Y_Vector) is
856 if X'Length /= Y'Length then
857 raise Constraint_Error with
858 "vectors are of different length in update operation";
861 for J in X'Range loop
862 Update (X (J), Y (J - X'First + Y'First));
864 end Update_Vector_With_Vector;
872 First_1 : Integer := 1;
873 First_2 : Integer := 1) return Matrix
876 return R : Matrix (First_1 .. Check_Unit_Last (First_1, Order, First_1),
877 First_2 .. Check_Unit_Last (First_2, Order, First_2))
879 R := (others => (others => Zero));
881 for J in 0 .. Order - 1 loop
882 R (First_1 + J, First_2 + J) := One;
894 First : Integer := 1) return Vector
897 return R : Vector (First .. Check_Unit_Last (Index, Order, First)) do
898 R := (others => Zero);
903 ---------------------------
904 -- Vector_Matrix_Product --
905 ---------------------------
907 function Vector_Matrix_Product
909 Right : Matrix) return Result_Vector
912 return R : Result_Vector (Right'Range (2)) do
913 if Left'Length /= Right'Length (1) then
914 raise Constraint_Error with
915 "incompatible dimensions in vector-matrix multiplication";
918 for J in Right'Range (2) loop
920 S : Result_Scalar := Zero;
923 for K in Right'Range (1) loop
924 S := S + Left (K - Right'First (1)
925 + Left'First) * Right (K, J);
932 end Vector_Matrix_Product;
934 end System.Generic_Array_Operations;