GNU Octave  9.1.0
A high-level interpreted language, primarily intended for numerical computations, mostly compatible with Matlab
All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Pages
gsvd.cc
Go to the documentation of this file.
1 ////////////////////////////////////////////////////////////////////////
2 //
3 // Copyright (C) 1997-2024 The Octave Project Developers
4 //
5 // See the file COPYRIGHT.md in the top-level directory of this
6 // distribution or <https://octave.org/copyright/>.
7 //
8 // This file is part of Octave.
9 //
10 // Octave is free software: you can redistribute it and/or modify it
11 // under the terms of the GNU General Public License as published by
12 // the Free Software Foundation, either version 3 of the License, or
13 // (at your option) any later version.
14 //
15 // Octave is distributed in the hope that it will be useful, but
16 // WITHOUT ANY WARRANTY; without even the implied warranty of
17 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 // GNU General Public License for more details.
19 //
20 // You should have received a copy of the GNU General Public License
21 // along with Octave; see the file COPYING. If not, see
22 // <https://www.gnu.org/licenses/>.
23 //
24 ////////////////////////////////////////////////////////////////////////
25 
26 #ifdef HAVE_CONFIG_H
27 # include "config.h"
28 #endif
29 
30 #include "dMatrix.h"
31 #include "CMatrix.h"
32 #include "dDiagMatrix.h"
33 #include "gsvd.h"
34 
35 #include "defun.h"
36 #include "defun-int.h"
37 #include "error.h"
38 #include "errwarn.h"
39 #include "utils.h"
40 #include "ovl.h"
41 #include "ov.h"
42 
44 
45 template <typename T>
46 static typename math::gsvd<T>::Type
47 gsvd_type (int nargout, int nargin)
48 {
49  if (nargout == 0 || nargout == 1)
50  return octave::math::gsvd<T>::Type::sigma_only;
51  else if (nargin < 3)
52  return octave::math::gsvd<T>::Type::std;
53  else
54  return octave::math::gsvd<T>::Type::economy;
55 }
56 
57 // Named do_gsvd to avoid conflicts with the gsvd class itself.
58 template <typename T>
59 static octave_value_list
60 do_gsvd (const T& A, const T& B,
61  const octave_idx_type nargout, const octave_idx_type nargin,
62  bool is_single = false)
63 {
64  math::gsvd<T> result (A, B, gsvd_type<T> (nargout, nargin));
65 
66  octave_value_list retval (nargout);
67  if (nargout <= 1)
68  {
69  if (is_single)
70  {
71  FloatMatrix sigA = result.singular_values_A ();
72  FloatMatrix sigB = result.singular_values_B ();
73  for (int i = sigA.rows () - 1; i >= 0; i--)
74  sigA.xelem (i) /= sigB.xelem (i);
75  retval(0) = sigA.sort ();
76  }
77  else
78  {
79  Matrix sigA = result.singular_values_A ();
80  Matrix sigB = result.singular_values_B ();
81  for (int i = sigA.rows () - 1; i >= 0; i--)
82  sigA.xelem (i) /= sigB.xelem (i);
83  retval(0) = sigA.sort ();
84  }
85  }
86  else
87  {
88  switch (nargout)
89  {
90  case 5:
91  retval(4) = result.singular_values_B ();
92  OCTAVE_FALLTHROUGH;
93 
94  case 4:
95  retval(3) = result.singular_values_A ();
96  OCTAVE_FALLTHROUGH;
97 
98  case 3:
99  retval(2) = result.right_singular_matrix ();
100  }
101 
102  retval(1) = result.left_singular_matrix_B ();
103  retval(0) = result.left_singular_matrix_A ();
104  }
105 
106  return retval;
107 }
108 
109 DEFUN (gsvd, args, nargout,
110  doc: /* -*- texinfo -*-
111 @deftypefn {} {@var{S} =} gsvd (@var{A}, @var{B})
112 @deftypefnx {} {[@var{U}, @var{V}, @var{X}, @var{C}, @var{S}] =} gsvd (@var{A}, @var{B})
113 @deftypefnx {} {[@var{U}, @var{V}, @var{X}, @var{C}, @var{S}] =} gsvd (@var{A}, @var{B}, 0)
114 Compute the generalized singular value decomposition of (@var{A}, @var{B}).
115 
116 The generalized singular value decomposition is defined by the following
117 relations:
118 
119 @tex
120 $$ A = U C X^\dagger $$
121 $$ B = V S X^\dagger $$
122 $$ C^\dagger C + S^\dagger S = eye (columns (A)) $$
123 @end tex
124 @ifnottex
125 
126 @example
127 @group
128 A = U*C*X'
129 B = V*S*X'
130 C'*C + S'*S = eye (columns (A))
131 @end group
132 @end example
133 
134 @end ifnottex
135 
136 The function @code{gsvd} normally returns just the vector of generalized
137 singular values
138 @tex
139 $$ \sqrt{{{diag (C^\dagger C)} \over {diag (S^\dagger S)}}} $$
140 @end tex
141 @ifnottex
142 @code{sqrt (diag (C'*C) ./ diag (S'*S))}.
143 @end ifnottex
144 If asked for five return values, it also computes
145 @tex
146 $U$, $V$, $X$, and $C$.
147 @end tex
148 @ifnottex
149 U, V, X, and C.
150 @end ifnottex
151 
152 If the optional third input is present, @code{gsvd} constructs the
153 "economy-sized" decomposition where the number of columns of @var{U}, @var{V}
154 and the number of rows of @var{C}, @var{S} is less than or equal to the number
155 of columns of @var{A}. This option is not yet implemented.
156 
157 Programming Note: the code is a wrapper to the corresponding @sc{lapack} dggsvd
158 and zggsvd routines. If matrices @var{A} and @var{B} are @emph{both} rank
159 deficient then @sc{lapack} will return an incorrect factorization. Programmers
160 should avoid this combination.
161 @seealso{svd}
162 @end deftypefn */)
163 {
164  int nargin = args.length ();
165 
166  if (nargin < 2 || nargin > 3)
167  print_usage ();
168  else if (nargin == 3)
169  {
170  // FIXME: when "economy" is implemented delete this code
171  warning ("gsvd: economy-sized decomposition is not yet implemented, returning full decomposition");
172  nargin = 2;
173  }
174 
175  octave_value_list retval;
176 
177  octave_value argA = args(0);
178  octave_value argB = args(1);
179 
180  if (argA.columns () != argB.columns ())
181  error ("gsvd: A and B must have the same number of columns");
182 
183  if (argA.is_single_type () || argB.is_single_type ())
184  {
185  if (argA.isreal () && argB.isreal ())
186  {
187  FloatMatrix tmpA = argA.xfloat_matrix_value ("gsvd: A must be a real or complex matrix");
188  FloatMatrix tmpB = argB.xfloat_matrix_value ("gsvd: B must be a real or complex matrix");
189 
190  if (tmpA.any_element_is_inf_or_nan ())
191  error ("gsvd: A cannot have Inf or NaN values");
192  if (tmpB.any_element_is_inf_or_nan ())
193  error ("gsvd: B cannot have Inf or NaN values");
194 
195  retval = do_gsvd (tmpA, tmpB, nargout, nargin, true);
196  }
197  else if (argA.iscomplex () || argB.iscomplex ())
198  {
199  FloatComplexMatrix ctmpA =
200  argA.xfloat_complex_matrix_value ("gsvd: A must be a real or complex matrix");
201  FloatComplexMatrix ctmpB =
202  argB.xfloat_complex_matrix_value ("gsvd: B must be a real or complex matrix");
203 
204  if (ctmpA.any_element_is_inf_or_nan ())
205  error ("gsvd: A cannot have Inf or NaN values");
206  if (ctmpB.any_element_is_inf_or_nan ())
207  error ("gsvd: B cannot have Inf or NaN values");
208 
209  retval = do_gsvd (ctmpA, ctmpB, nargout, nargin, true);
210  }
211  else
212  error ("gsvd: A and B must be real or complex matrices");
213  }
214  else
215  {
216  if (argA.isreal () && argB.isreal ())
217  {
218  Matrix tmpA = argA.xmatrix_value ("gsvd: A must be a real or complex matrix");
219  Matrix tmpB = argB.xmatrix_value ("gsvd: B must be a real or complex matrix");
220 
221  if (tmpA.any_element_is_inf_or_nan ())
222  error ("gsvd: A cannot have Inf or NaN values");
223  if (tmpB.any_element_is_inf_or_nan ())
224  error ("gsvd: B cannot have Inf or NaN values");
225 
226  retval = do_gsvd (tmpA, tmpB, nargout, nargin);
227  }
228  else if (argA.iscomplex () || argB.iscomplex ())
229  {
230  ComplexMatrix ctmpA = argA.xcomplex_matrix_value ("gsvd: A must be a real or complex matrix");
231  ComplexMatrix ctmpB = argB.xcomplex_matrix_value ("gsvd: B must be a real or complex matrix");
232 
233  if (ctmpA.any_element_is_inf_or_nan ())
234  error ("gsvd: A cannot have Inf or NaN values");
235  if (ctmpB.any_element_is_inf_or_nan ())
236  error ("gsvd: B cannot have Inf or NaN values");
237 
238  retval = do_gsvd (ctmpA, ctmpB, nargout, nargin);
239  }
240  else
241  error ("gsvd: A and B must be real or complex matrices");
242  }
243 
244  return retval;
245 }
246 
247 /*
248 
249 ## Basic tests of decomposition
250 %!test <60273>
251 %! A = reshape (1:15,5,3);
252 %! B = magic (3);
253 %! [U,V,X,C,S] = gsvd (A,B);
254 %! assert (size (U), [5, 5]);
255 %! assert (size (V), [3, 3]);
256 %! assert (size (X), [3, 3]);
257 %! assert (size (C), [5, 3]);
258 %! assert (C(4:5, :), zeros (2,3));
259 %! assert (size (S), [3, 3]);
260 %! assert (U*C*X', A, 50*eps);
261 %! assert (V*S*X', B, 50*eps);
262 %! S0 = gsvd (A, B);
263 %! assert (size (S0), [3, 1]);
264 %! S1 = sort (svd (A / B));
265 %! assert (S0, S1, 10*eps);
266 
267 %!test <60273>
268 %! A = reshape (1:15,3,5);
269 %! B = magic (5);
270 %! [U,V,X,C,S] = gsvd (A,B);
271 %! assert (size (U), [3, 3]);
272 %! assert (size (V), [5, 5]);
273 %! assert (size (X), [5, 5]);
274 %! assert (size (C), [3, 5]);
275 %! assert (C(:, 4:5), zeros (3,2));
276 %! assert (size (S), [5, 5]);
277 %! assert (U*C*X', A, 120*eps); # less accurate in this orientation
278 %! assert (V*S*X', B, 150*eps); # for some reason.
279 %! S0 = gsvd (A, B);
280 %! assert (size (S0), [5, 1]);
281 %! S0 = S0(3:end);
282 %! S1 = sort (svd (A / B));
283 %! assert (S0, S1, 20*eps);
284 
285 ## a few tests for gsvd.m
286 %!shared A0, B0
287 %! old_state = randn ("state");
288 %! restore_state = onCleanup (@() randn ("state", old_state));
289 %! randn ("state", 40); # initialize generator to make behavior reproducible
290 %! A0 = randn (5, 3);
291 %! B0 = diag ([1 2 4]);
292 
293 ## A (5x3) and B (3x3) are full rank
294 %!test <48807>
295 %! A = A0;
296 %! B = B0;
297 %! [U, V, X, C, S] = gsvd (A, B);
298 %! assert (C'*C + S'*S, eye (3), 5*eps);
299 %! assert (U*C*X', A, 10*eps);
300 %! assert (V*S*X', B, 20*eps);
301 
302 ## A: 5x3 full rank, B: 3x3 rank deficient
303 %!test <48807>
304 %! A = A0;
305 %! B = B0;
306 %! B(2, 2) = 0;
307 %! [U, V, X, C, S] = gsvd (A, B);
308 %! assert (C'*C + S'*S, eye (3), 5*eps);
309 %! assert (U*C*X', A, 10*eps);
310 %! assert (V*S*X', B, 20*eps);
311 
312 ## A: 5x3 rank deficient, B: 3x3 full rank
313 %!test <48807>
314 %! A = A0;
315 %! B = B0;
316 %! A(:, 3) = 2*A(:, 1) - A(:, 2);
317 %! [U, V, X, C, S] = gsvd (A, B);
318 %! assert (C'*C + S'*S, eye (3), 5*eps);
319 %! assert (U*C*X', A, 10*eps);
320 %! assert (V*S*X', B, 20*eps);
321 
322 ## A and B are both rank deficient
323 ## FIXME: LAPACK seems to be completely broken for this case
324 %!#test <48807>
325 %! A = A0;
326 %! B = B0;
327 %! B(:, 3) = 2*B(:, 1) - B(:, 2);
328 %! [U, V, X, C, S] = gsvd (A, B);
329 %! assert (C'*C + S'*S, eye (3), 5*eps);
330 %! assert (U*C*X', A, 10*eps);
331 %! assert (V*S*X', B, 20*eps);
332 
333 ## A (now 3x5) and B (now 5x5) are full rank
334 %!test <48807>
335 %! A = A0.';
336 %! B = diag ([1 2 4 8 16]);
337 %! [U, V, X, C, S] = gsvd (A, B);
338 %! assert (C'*C + S'*S, eye (5), 5*eps);
339 %! assert (U*C*X', A, 15*eps);
340 %! assert (V*S*X', B, 85*eps);
341 
342 ## A: 3x5 full rank, B: 5x5 rank deficient
343 %!test <48807>
344 %! A = A0.';
345 %! B = diag ([1 2 4 8 16]);
346 %! B(2, 2) = 0;
347 %! [U, V, X, C, S] = gsvd (A, B);
348 %! assert (C'*C + S'*S, eye (5), 5*eps);
349 %! assert (U*C*X', A, 15*eps);
350 %! assert (V*S*X', B, 85*eps);
351 
352 ## A: 3x5 rank deficient, B: 5x5 full rank
353 %!test <48807>
354 %! A = A0.';
355 %! B = diag ([1 2 4 8 16]);
356 %! A(3, :) = 2*A(1, :) - A(2, :);
357 %! [U, V, X, C, S] = gsvd (A, B);
358 %! assert (C'*C + S'*S, eye (5), 5*eps);
359 %! assert (U*C*X', A, 15*eps);
360 %! assert (V*S*X', B, 85*eps);
361 
362 ## A and B are both rank deficient
363 ## FIXME: LAPACK seems to be completely broken for this case
364 %!#test <48807>
365 %! A = A0.'; B = B0.';
366 %! A(:, 3) = 2*A(:, 1) - A(:, 2);
367 %! B(:, 3) = 2*B(:, 1) - B(:, 2);
368 %! [U, V, X, C, S] = gsvd (A, B);
369 %! assert (C'*C + S'*S, eye (3), 5*eps);
370 %! assert (U*C*X', A, 10*eps);
371 %! assert (V*S*X', B, 20*eps);
372 
373 ## A: 5x3 complex full rank, B: 3x3 complex full rank
374 %!test <48807>
375 %! old_state = randn ("state");
376 %! restore_state = onCleanup (@() randn ("state", old_state));
377 %! randn ("state", 12345); # initialize generator to make behavior reproducible
378 %! A = A0 + j* randn (5, 3);
379 %! B = diag ([1 2 4]) + j* diag ([4 -2 -1]);
380 %! [U, V, X, C, S] = gsvd (A, B);
381 %! assert (C'*C + S'*S, eye (3), 5*eps);
382 %! assert (U*C*X', A, 10*eps);
383 %! assert (V*S*X', B, 25*eps);
384 
385 ## A: 5x3 complex full rank, B: 3x3 complex rank deficient
386 %!test <48807>
387 %! A = A0 + j* randn (5, 3);
388 %! B = diag ([1 2 4]) + j* diag ([4 -2 -1]);
389 %! B(2, 2) = 0;
390 %! [U, V, X, C, S] = gsvd (A, B);
391 %! assert (C'*C + S'*S, eye (3), 5*eps);
392 %! assert (U*C*X', A, 10*eps);
393 %! assert (V*S*X', B, 25*eps);
394 
395 ## A: 5x3 complex rank deficient, B: 3x3 complex full rank
396 %!test <48807>
397 %! A = A0 + j* randn (5, 3);
398 %! B = diag ([1 2 4]) + j* diag ([4 -2 -1]);
399 %! A(:, 3) = 2*A(:, 1) - A(:, 2);
400 %! [U, V, X, C, S] = gsvd (A, B);
401 %! assert (C'*C + S'*S, eye (3), 5*eps);
402 %! assert (U*C*X', A, 15*eps);
403 %! assert (V*S*X', B, 25*eps);
404 
405 ## A (5x3) and B (3x3) are both complex rank deficient
406 ## FIXME: LAPACK seems to be completely broken for this case
407 %!#test <48807>
408 %! A = A0 + j* randn (5, 3);
409 %! B = diag ([1 2 4]) + j* diag ([4 -2 -1]);
410 %! B(:, 3) = 2*B(:, 1) - B(:, 2);
411 %! [U, V, X, C, S] = gsvd (A, B);
412 %! assert (C'*C + S'*S, eye (3), 5*eps);
413 %! assert (U*C*X', A, 10*eps);
414 %! assert (V*S*X', B, 20*eps);
415 
416 ## A (now 3x5) complex and B (now 5x5) complex are full rank
417 ## now, A is 3x5
418 %!test <48807>
419 %! A = A0.';
420 %! B = diag ([1 2 4 8 16]) + j* diag ([-5 4 -3 2 -1]);
421 %! [U, V, X, C, S] = gsvd (A, B);
422 %! assert (C'*C + S'*S, eye (5), 5*eps);
423 %! assert (U*C*X', A, 25*eps);
424 %! assert (V*S*X', B, 85*eps);
425 
426 ## A: 3x5 complex full rank, B: 5x5 complex rank deficient
427 %!test <48807>
428 %! A = A0.';
429 %! B = diag ([1 0 4 8 16]) + j* diag ([-5 0 -3 2 -1]);
430 %! [U, V, X, C, S] = gsvd (A, B);
431 %! assert (C'*C + S'*S, eye (5), 5*eps);
432 %! assert (U*C*X', A, 10*eps);
433 %! assert (V*S*X', B, 85*eps);
434 
435 ## A: 3x5 complex rank deficient, B: 5x5 complex full rank
436 %!test <48807>
437 %! A = A0.';
438 %! B = diag ([1 2 4 8 16]) + j* diag ([-5 4 -3 2 -1]);
439 %! A(3, :) = 2*A(1, :) - A(2, :);
440 %! [U, V, X, C, S] = gsvd (A, B);
441 %! assert (C'*C + S'*S, eye (5), 5*eps);
442 %! assert (U*C*X', A, 10*eps);
443 %! assert (V*S*X', B, 85*eps);
444 
445 ## A and B are both complex rank deficient
446 ## FIXME: LAPACK seems to be completely broken for this case
447 %!#test <48807>
448 %! A = A0.';
449 %! B = B0.';
450 %! A(:, 3) = 2*A(:, 1) - A(:, 2);
451 %! B(:, 3) = 2*B(:, 1) - B(:, 2);
452 %! [U, V, X, C, S] = gsvd (A, B);
453 %! assert (C'*C + S'*S, eye (5), 5*eps);
454 %! assert (U*C*X', A, 10*eps);
455 %! assert (V*S*X', B, 85*eps);
456 
457 ## Test that single inputs produce single outputs
458 %!test
459 %! A = A0.';
460 %! B = diag ([1 2 4 8 16]) + j* diag ([-5 4 -3 2 -1]);
461 %! s = gsvd (single (eye (5)), B);
462 %! assert (class (s), "single");
463 %! [U, V, X, C, S] = gsvd (single (eye(5)), B);
464 %! assert (class (U), "single");
465 %! assert (class (V), "single");
466 %! assert (class (X), "single");
467 %! assert (class (C), "single");
468 %! assert (class (S), "single");
469 %!
470 %! s = gsvd (A, single (eye (5)));
471 %! assert (class (s), "single");
472 %! [U, V, X, C, S] = gsvd (A, single (eye (5)));
473 %! assert (class (U), "single");
474 %! assert (class (V), "single");
475 %! assert (class (X), "single");
476 %! assert (class (C), "single");
477 %! assert (class (S), "single");
478 
479 ## Test input validation
480 %!error <Invalid call> gsvd ()
481 %!error <Invalid call> gsvd (1)
482 %!error <Invalid call> gsvd (1,2,3,4)
483 %!warning <economy-sized decomposition is not yet implemented> gsvd (1,2,0);
484 %!error <A and B must have the same number of columns> gsvd (1,[1, 2])
485 ## Test input validation for single (real and complex) inputs.
486 %!error <A cannot have Inf or NaN values> gsvd (Inf, single (2))
487 %!error <A cannot have Inf or NaN values> gsvd (NaN, single (2))
488 %!error <B cannot have Inf or NaN values> gsvd (single (1), Inf)
489 %!error <B cannot have Inf or NaN values> gsvd (single (1), NaN)
490 %!error <A must be a real or complex matrix> gsvd ({1}, single (2i))
491 %!error <B must be a real or complex matrix> gsvd (single (i), {2})
492 %!error <A cannot have Inf or NaN values> gsvd (Inf, single (2i))
493 %!error <A cannot have Inf or NaN values> gsvd (NaN, single (2i))
494 %!error <B cannot have Inf or NaN values> gsvd (single (i), Inf)
495 %!error <B cannot have Inf or NaN values> gsvd (single (i), NaN)
496 ## Test input validation for single, but not real or complex, inputs.
497 %!error <A and B must be real or complex matrices> gsvd ({1}, single (2))
498 %!error <A and B must be real or complex matrices> gsvd (single (1), {2})
499 ## Test input validation for double (real and complex) inputs.
500 %!error <A cannot have Inf or NaN values> gsvd (Inf, 2)
501 %!error <A cannot have Inf or NaN values> gsvd (NaN, 2)
502 %!error <B cannot have Inf or NaN values> gsvd (1, Inf)
503 %!error <B cannot have Inf or NaN values> gsvd (1, NaN)
504 %!error <A must be a real or complex matrix> gsvd ({1}, 2i)
505 %!error <B must be a real or complex matrix> gsvd (i, {2})
506 %!error <A cannot have Inf or NaN values> gsvd (Inf, 2i)
507 %!error <A cannot have Inf or NaN values> gsvd (NaN, 2i)
508 %!error <B cannot have Inf or NaN values> gsvd (i, Inf)
509 %!error <B cannot have Inf or NaN values> gsvd (i, NaN)
510 ## Test input validation for double, but not real or complex, inputs.
511 %!error <A and B must be real or complex matrices> gsvd ({1}, double (2))
512 %!error <A and B must be real or complex matrices> gsvd (double (1), {2})
513 ## Test input validation in liboctave/numeric/gsvd.cc
514 %!error <A and B cannot be empty matrices> gsvd (zeros (0,1), 1)
515 %!error <A and B cannot be empty matrices> gsvd (1, zeros (0,1))
516 
517 */
518 
519 OCTAVE_END_NAMESPACE(octave)
octave_idx_type rows() const
Definition: Array.h:459
Array< T, Alloc > sort(int dim=0, sortmode mode=ASCENDING) const
Size of the specified dimension.
Definition: Array-base.cc:1781
T & xelem(octave_idx_type n)
Size of the specified dimension.
Definition: Array.h:524
bool any_element_is_inf_or_nan() const
Definition: CNDArray.cc:271
bool any_element_is_inf_or_nan() const
Definition: fCNDArray.cc:271
bool any_element_is_inf_or_nan() const
Definition: fNDArray.cc:281
Definition: dMatrix.h:42
bool any_element_is_inf_or_nan() const
Definition: dNDArray.cc:324
Definition: gsvd.h:39
bool isreal() const
Definition: ov.h:738
FloatMatrix xfloat_matrix_value(const char *fmt,...) const
ComplexMatrix xcomplex_matrix_value(const char *fmt,...) const
bool is_single_type() const
Definition: ov.h:698
FloatComplexMatrix xfloat_complex_matrix_value(const char *fmt,...) const
bool iscomplex() const
Definition: ov.h:741
Matrix xmatrix_value(const char *fmt,...) const
octave_idx_type columns() const
Definition: ov.h:547
OCTAVE_BEGIN_NAMESPACE(octave) static octave_value daspk_fcn
void print_usage(void)
Definition: defun-int.h:72
#define DEFUN(name, args_name, nargout_name, doc)
Macro to define a builtin function.
Definition: defun.h:56
void warning(const char *fmt,...)
Definition: error.cc:1063
void() error(const char *fmt,...)
Definition: error.cc:988
F77_RET_T const F77_INT F77_CMPLX const F77_INT F77_CMPLX * B
F77_RET_T const F77_INT F77_CMPLX * A