GNU Octave  3.8.0
A high-level interpreted language, primarily intended for numerical computations, mostly compatible with Matlab
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Properties Friends Macros Pages
sparse-xpow.cc
Go to the documentation of this file.
1 /*
2 
3 Copyright (C) 2004-2013 David Bateman
4 Copyright (C) 1998-2004 Andy Adler
5 
6 This file is part of Octave.
7 
8 Octave is free software; you can redistribute it and/or modify it
9 under the terms of the GNU General Public License as published by the
10 Free Software Foundation; either version 3 of the License, or (at your
11 option) any later version.
12 
13 Octave is distributed in the hope that it will be useful, but WITHOUT
14 ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
15 FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
16 for more details.
17 
18 You should have received a copy of the GNU General Public License
19 along with Octave; see the file COPYING. If not, see
20 <http://www.gnu.org/licenses/>.
21 
22 */
23 
24 #ifdef HAVE_CONFIG_H
25 #include <config.h>
26 #endif
27 
28 #include <cassert>
29 
30 #include <limits>
31 
32 #include "Array-util.h"
33 #include "oct-cmplx.h"
34 #include "quit.h"
35 
36 #include "error.h"
37 #include "oct-obj.h"
38 #include "utils.h"
39 
40 #include "dSparse.h"
41 #include "CSparse.h"
42 #include "ov-re-sparse.h"
43 #include "ov-cx-sparse.h"
44 #include "sparse-xpow.h"
45 
46 static inline int
47 xisint (double x)
48 {
49  return (D_NINT (x) == x
50  && ((x >= 0 && x < std::numeric_limits<int>::max ())
51  || (x <= 0 && x > std::numeric_limits<int>::min ())));
52 }
53 
54 
55 // Safer pow functions. Only two make sense for sparse matrices, the
56 // others should all promote to full matrices.
57 
59 xpow (const SparseMatrix& a, double b)
60 {
61  octave_value retval;
62 
63  octave_idx_type nr = a.rows ();
64  octave_idx_type nc = a.cols ();
65 
66  if (nr == 0 || nc == 0 || nr != nc)
67  error ("for A^b, A must be a square matrix. Use .^ for elementwise power.");
68  else
69  {
70  if (static_cast<int> (b) == b)
71  {
72  int btmp = static_cast<int> (b);
73  if (btmp == 0)
74  {
75  SparseMatrix tmp = SparseMatrix (nr, nr, nr);
76  for (octave_idx_type i = 0; i < nr; i++)
77  {
78  tmp.data (i) = 1.0;
79  tmp.ridx (i) = i;
80  }
81  for (octave_idx_type i = 0; i < nr + 1; i++)
82  tmp.cidx (i) = i;
83 
84  retval = tmp;
85  }
86  else
87  {
88  SparseMatrix atmp;
89  if (btmp < 0)
90  {
91  btmp = -btmp;
92 
93  octave_idx_type info;
94  double rcond = 0.0;
95  MatrixType mattyp (a);
96 
97  atmp = a.inverse (mattyp, info, rcond, 1);
98 
99  if (info == -1)
100  warning ("inverse: matrix singular to machine\
101  precision, rcond = %g", rcond);
102  }
103  else
104  atmp = a;
105 
106  SparseMatrix result (atmp);
107 
108  btmp--;
109 
110  while (btmp > 0)
111  {
112  if (btmp & 1)
113  result = result * atmp;
114 
115  btmp >>= 1;
116 
117  if (btmp > 0)
118  atmp = atmp * atmp;
119  }
120 
121  retval = result;
122  }
123  }
124  else
125  error ("use full(a) ^ full(b)");
126  }
127 
128  return retval;
129 }
130 
132 xpow (const SparseComplexMatrix& a, double b)
133 {
134  octave_value retval;
135 
136  octave_idx_type nr = a.rows ();
137  octave_idx_type nc = a.cols ();
138 
139  if (nr == 0 || nc == 0 || nr != nc)
140  error ("for A^b, A must be a square matrix. Use .^ for elementwise power.");
141  else
142  {
143  if (static_cast<int> (b) == b)
144  {
145  int btmp = static_cast<int> (b);
146  if (btmp == 0)
147  {
148  SparseMatrix tmp = SparseMatrix (nr, nr, nr);
149  for (octave_idx_type i = 0; i < nr; i++)
150  {
151  tmp.data (i) = 1.0;
152  tmp.ridx (i) = i;
153  }
154  for (octave_idx_type i = 0; i < nr + 1; i++)
155  tmp.cidx (i) = i;
156 
157  retval = tmp;
158  }
159  else
160  {
161  SparseComplexMatrix atmp;
162  if (btmp < 0)
163  {
164  btmp = -btmp;
165 
166  octave_idx_type info;
167  double rcond = 0.0;
168  MatrixType mattyp (a);
169 
170  atmp = a.inverse (mattyp, info, rcond, 1);
171 
172  if (info == -1)
173  warning ("inverse: matrix singular to machine\
174  precision, rcond = %g", rcond);
175  }
176  else
177  atmp = a;
178 
179  SparseComplexMatrix result (atmp);
180 
181  btmp--;
182 
183  while (btmp > 0)
184  {
185  if (btmp & 1)
186  result = result * atmp;
187 
188  btmp >>= 1;
189 
190  if (btmp > 0)
191  atmp = atmp * atmp;
192  }
193 
194  retval = result;
195  }
196  }
197  else
198  error ("use full(a) ^ full(b)");
199  }
200 
201  return retval;
202 }
203 
204 // Safer pow functions that work elementwise for matrices.
205 //
206 // op2 \ op1: s m cs cm
207 // +-- +---+---+----+----+
208 // scalar | | * | 3 | * | 9 |
209 // +---+---+----+----+
210 // matrix | 1 | 4 | 7 | 10 |
211 // +---+---+----+----+
212 // complex_scalar | * | 5 | * | 11 |
213 // +---+---+----+----+
214 // complex_matrix | 2 | 6 | 8 | 12 |
215 // +---+---+----+----+
216 //
217 // * -> not needed.
218 
219 // FIXME: these functions need to be fixed so that things
220 // like
221 //
222 // a = -1; b = [ 0, 0.5, 1 ]; r = a .^ b
223 //
224 // and
225 //
226 // a = -1; b = [ 0, 0.5, 1 ]; for i = 1:3, r(i) = a .^ b(i), end
227 //
228 // produce identical results. Also, it would be nice if -1^0.5
229 // produced a pure imaginary result instead of a complex number with a
230 // small real part. But perhaps that's really a problem with the math
231 // library...
232 
233 // Handle special case of scalar-sparse-matrix .^ sparse-matrix.
234 // Forwarding to the scalar elem_xpow function and then converting the
235 // result back to a sparse matrix is a bit wasteful but it does not
236 // seem worth the effort to optimize -- how often does this case come up
237 // in practice?
238 
239 template <class S, class SM>
240 inline octave_value
241 scalar_xpow (const S& a, const SM& b)
242 {
243  octave_value val = elem_xpow (a, b);
244 
245  if (val.is_complex_type ())
247  else
248  return SparseMatrix (val.matrix_value ());
249 }
250 
251 /*
252 %!assert (sparse (2) .^ [3, 4], sparse ([8, 16]));
253 %!assert (sparse (2i) .^ [3, 4], sparse ([-0-8i, 16]));
254 */
255 
256 // -*- 1 -*-
258 elem_xpow (double a, const SparseMatrix& b)
259 {
260  octave_value retval;
261 
262  octave_idx_type nr = b.rows ();
263  octave_idx_type nc = b.cols ();
264 
265  double d1, d2;
266 
267  if (a < 0.0 && ! b.all_integers (d1, d2))
268  {
269  Complex atmp (a);
270  ComplexMatrix result (nr, nc);
271 
272  for (octave_idx_type j = 0; j < nc; j++)
273  {
274  for (octave_idx_type i = 0; i < nr; i++)
275  {
276  octave_quit ();
277  result(i, j) = std::pow (atmp, b(i,j));
278  }
279  }
280 
281  retval = result;
282  }
283  else
284  {
285  Matrix result (nr, nc);
286 
287  for (octave_idx_type j = 0; j < nc; j++)
288  {
289  for (octave_idx_type i = 0; i < nr; i++)
290  {
291  octave_quit ();
292  result(i, j) = std::pow (a, b(i,j));
293  }
294  }
295 
296  retval = result;
297  }
298 
299  return retval;
300 }
301 
302 // -*- 2 -*-
304 elem_xpow (double a, const SparseComplexMatrix& b)
305 {
306  octave_idx_type nr = b.rows ();
307  octave_idx_type nc = b.cols ();
308 
309  Complex atmp (a);
310  ComplexMatrix result (nr, nc);
311 
312  for (octave_idx_type j = 0; j < nc; j++)
313  {
314  for (octave_idx_type i = 0; i < nr; i++)
315  {
316  octave_quit ();
317  result(i, j) = std::pow (atmp, b(i,j));
318  }
319  }
320 
321  return result;
322 }
323 
324 // -*- 3 -*-
326 elem_xpow (const SparseMatrix& a, double b)
327 {
328  // FIXME: What should a .^ 0 give? Matlab gives a
329  // sparse matrix with same structure as a, which is strictly
330  // incorrect. Keep compatibility.
331 
332  octave_value retval;
333 
334  octave_idx_type nz = a.nnz ();
335 
336  if (b <= 0.0)
337  {
338  octave_idx_type nr = a.rows ();
339  octave_idx_type nc = a.cols ();
340 
341  if (static_cast<int> (b) != b && a.any_element_is_negative ())
342  {
343  ComplexMatrix result (nr, nc, Complex (std::pow (0.0, b)));
344 
345  // FIXME: avoid apparent GNU libm bug by
346  // converting A and B to complex instead of just A.
347  Complex btmp (b);
348 
349  for (octave_idx_type j = 0; j < nc; j++)
350  for (octave_idx_type i = a.cidx (j); i < a.cidx (j+1); i++)
351  {
352  octave_quit ();
353 
354  Complex atmp (a.data (i));
355 
356  result(a.ridx (i), j) = std::pow (atmp, btmp);
357  }
358 
359  retval = octave_value (result);
360  }
361  else
362  {
363  Matrix result (nr, nc, (std::pow (0.0, b)));
364 
365  for (octave_idx_type j = 0; j < nc; j++)
366  for (octave_idx_type i = a.cidx (j); i < a.cidx (j+1); i++)
367  {
368  octave_quit ();
369  result(a.ridx (i), j) = std::pow (a.data (i), b);
370  }
371 
372  retval = octave_value (result);
373  }
374  }
375  else if (static_cast<int> (b) != b && a.any_element_is_negative ())
376  {
377  SparseComplexMatrix result (a);
378 
379  for (octave_idx_type i = 0; i < nz; i++)
380  {
381  octave_quit ();
382 
383  // FIXME: avoid apparent GNU libm bug by
384  // converting A and B to complex instead of just A.
385 
386  Complex atmp (a.data (i));
387  Complex btmp (b);
388 
389  result.data (i) = std::pow (atmp, btmp);
390  }
391 
392  result.maybe_compress (true);
393 
394  retval = result;
395  }
396  else
397  {
398  SparseMatrix result (a);
399 
400  for (octave_idx_type i = 0; i < nz; i++)
401  {
402  octave_quit ();
403  result.data (i) = std::pow (a.data (i), b);
404  }
405 
406  result.maybe_compress (true);
407 
408  retval = result;
409  }
410 
411  return retval;
412 }
413 
414 // -*- 4 -*-
416 elem_xpow (const SparseMatrix& a, const SparseMatrix& b)
417 {
418  octave_value retval;
419 
420  octave_idx_type nr = a.rows ();
421  octave_idx_type nc = a.cols ();
422 
423  octave_idx_type b_nr = b.rows ();
424  octave_idx_type b_nc = b.cols ();
425 
426  if (a.numel () == 1 && b.numel () > 1)
427  return scalar_xpow (a(0), b);
428 
429  if (nr != b_nr || nc != b_nc)
430  {
431  gripe_nonconformant ("operator .^", nr, nc, b_nr, b_nc);
432  return octave_value ();
433  }
434 
435  int convert_to_complex = 0;
436  for (octave_idx_type j = 0; j < nc; j++)
437  for (octave_idx_type i = a.cidx (j); i < a.cidx (j+1); i++)
438  {
439  if (a.data(i) < 0.0)
440  {
441  double btmp = b (a.ridx (i), j);
442  if (static_cast<int> (btmp) != btmp)
443  {
444  convert_to_complex = 1;
445  goto done;
446  }
447  }
448  }
449 
450 done:
451 
452  // This is a dumb operator for sparse matrices anyway, and there is
453  // no sensible way to handle the 0.^0 versus the 0.^x cases. Therefore
454  // allocate a full matrix filled for the 0.^0 case and shrink it later
455  // as needed
456 
457  if (convert_to_complex)
458  {
459  SparseComplexMatrix complex_result (nr, nc, Complex (1.0, 0.0));
460 
461  for (octave_idx_type j = 0; j < nc; j++)
462  {
463  for (octave_idx_type i = a.cidx (j); i < a.cidx (j+1); i++)
464  {
465  octave_quit ();
466  complex_result.xelem (a.ridx (i), j) =
467  std::pow (Complex (a.data (i)), Complex (b(a.ridx (i), j)));
468  }
469  }
470  complex_result.maybe_compress (true);
471  retval = complex_result;
472  }
473  else
474  {
475  SparseMatrix result (nr, nc, 1.0);
476 
477  for (octave_idx_type j = 0; j < nc; j++)
478  {
479  for (octave_idx_type i = a.cidx (j); i < a.cidx (j+1); i++)
480  {
481  octave_quit ();
482  result.xelem (a.ridx (i), j) = std::pow (a.data (i),
483  b(a.ridx (i), j));
484  }
485  }
486  result.maybe_compress (true);
487  retval = result;
488  }
489 
490  return retval;
491 }
492 
493 // -*- 5 -*-
495 elem_xpow (const SparseMatrix& a, const Complex& b)
496 {
497  octave_value retval;
498 
499  if (b == 0.0)
500  // Can this case ever happen, due to automatic retyping with maybe_mutate?
501  retval = octave_value (NDArray (a.dims (), 1));
502  else
503  {
504  octave_idx_type nz = a.nnz ();
505  SparseComplexMatrix result (a);
506 
507  for (octave_idx_type i = 0; i < nz; i++)
508  {
509  octave_quit ();
510  result.data (i) = std::pow (Complex (a.data (i)), b);
511  }
512 
513  result.maybe_compress (true);
514 
515  retval = result;
516  }
517 
518  return retval;
519 }
520 
521 // -*- 6 -*-
524 {
525  octave_idx_type nr = a.rows ();
526  octave_idx_type nc = a.cols ();
527 
528  octave_idx_type b_nr = b.rows ();
529  octave_idx_type b_nc = b.cols ();
530 
531  if (a.numel () == 1 && b.numel () > 1)
532  return scalar_xpow (a(0), b);
533 
534  if (nr != b_nr || nc != b_nc)
535  {
536  gripe_nonconformant ("operator .^", nr, nc, b_nr, b_nc);
537  return octave_value ();
538  }
539 
540  SparseComplexMatrix result (nr, nc, Complex (1.0, 0.0));
541  for (octave_idx_type j = 0; j < nc; j++)
542  {
543  for (octave_idx_type i = a.cidx (j); i < a.cidx (j+1); i++)
544  {
545  octave_quit ();
546  result.xelem (a.ridx(i), j) = std::pow (a.data (i), b(a.ridx (i), j));
547  }
548  }
549 
550  result.maybe_compress (true);
551 
552  return result;
553 }
554 
555 // -*- 7 -*-
557 elem_xpow (const Complex& a, const SparseMatrix& b)
558 {
559  octave_idx_type nr = b.rows ();
560  octave_idx_type nc = b.cols ();
561 
562  ComplexMatrix result (nr, nc);
563 
564  for (octave_idx_type j = 0; j < nc; j++)
565  {
566  for (octave_idx_type i = 0; i < nr; i++)
567  {
568  octave_quit ();
569  double btmp = b (i, j);
570  if (xisint (btmp))
571  result (i, j) = std::pow (a, static_cast<int> (btmp));
572  else
573  result (i, j) = std::pow (a, btmp);
574  }
575  }
576 
577  return result;
578 }
579 
580 // -*- 8 -*-
583 {
584  octave_idx_type nr = b.rows ();
585  octave_idx_type nc = b.cols ();
586 
587  ComplexMatrix result (nr, nc);
588  for (octave_idx_type j = 0; j < nc; j++)
589  for (octave_idx_type i = 0; i < nr; i++)
590  {
591  octave_quit ();
592  result (i, j) = std::pow (a, b (i, j));
593  }
594 
595  return result;
596 }
597 
598 // -*- 9 -*-
600 elem_xpow (const SparseComplexMatrix& a, double b)
601 {
602  octave_value retval;
603 
604  if (b <= 0)
605  {
606  octave_idx_type nr = a.rows ();
607  octave_idx_type nc = a.cols ();
608 
609  ComplexMatrix result (nr, nc, Complex (std::pow (0.0, b)));
610 
611  if (xisint (b))
612  {
613  for (octave_idx_type j = 0; j < nc; j++)
614  for (octave_idx_type i = a.cidx (j); i < a.cidx (j+1); i++)
615  {
616  octave_quit ();
617  result (a.ridx (i), j) =
618  std::pow (a.data (i), static_cast<int> (b));
619  }
620  }
621  else
622  {
623  for (octave_idx_type j = 0; j < nc; j++)
624  for (octave_idx_type i = a.cidx (j); i < a.cidx (j+1); i++)
625  {
626  octave_quit ();
627  result (a.ridx (i), j) = std::pow (a.data (i), b);
628  }
629  }
630 
631  retval = result;
632  }
633  else
634  {
635  octave_idx_type nz = a.nnz ();
636 
637  SparseComplexMatrix result (a);
638 
639  if (xisint (b))
640  {
641  for (octave_idx_type i = 0; i < nz; i++)
642  {
643  octave_quit ();
644  result.data (i) = std::pow (a.data (i), static_cast<int> (b));
645  }
646  }
647  else
648  {
649  for (octave_idx_type i = 0; i < nz; i++)
650  {
651  octave_quit ();
652  result.data (i) = std::pow (a.data (i), b);
653  }
654  }
655 
656  result.maybe_compress (true);
657 
658  retval = result;
659  }
660 
661  return retval;
662 }
663 
664 // -*- 10 -*-
667 {
668  octave_idx_type nr = a.rows ();
669  octave_idx_type nc = a.cols ();
670 
671  octave_idx_type b_nr = b.rows ();
672  octave_idx_type b_nc = b.cols ();
673 
674  if (a.numel () == 1 && b.numel () > 1)
675  return scalar_xpow (a(0), b);
676 
677  if (nr != b_nr || nc != b_nc)
678  {
679  gripe_nonconformant ("operator .^", nr, nc, b_nr, b_nc);
680  return octave_value ();
681  }
682 
683  SparseComplexMatrix result (nr, nc, Complex (1.0, 0.0));
684  for (octave_idx_type j = 0; j < nc; j++)
685  {
686  for (octave_idx_type i = a.cidx (j); i < a.cidx (j+1); i++)
687  {
688  octave_quit ();
689  double btmp = b(a.ridx (i), j);
690  Complex tmp;
691 
692  if (xisint (btmp))
693  result.xelem (a.ridx (i), j) = std::pow (a.data (i),
694  static_cast<int> (btmp));
695  else
696  result.xelem (a.ridx (i), j) = std::pow (a.data (i), btmp);
697  }
698  }
699 
700  result.maybe_compress (true);
701 
702  return result;
703 }
704 
705 // -*- 11 -*-
708 {
709  octave_value retval;
710 
711  if (b == 0.0)
712  // Can this case ever happen, due to automatic retyping with maybe_mutate?
713  retval = octave_value (NDArray (a.dims (), 1));
714  else
715  {
716 
717  octave_idx_type nz = a.nnz ();
718 
719  SparseComplexMatrix result (a);
720 
721  for (octave_idx_type i = 0; i < nz; i++)
722  {
723  octave_quit ();
724  result.data (i) = std::pow (a.data (i), b);
725  }
726 
727  result.maybe_compress (true);
728 
729  retval = result;
730  }
731 
732  return retval;
733 }
734 
735 // -*- 12 -*-
738 {
739  octave_idx_type nr = a.rows ();
740  octave_idx_type nc = a.cols ();
741 
742  octave_idx_type b_nr = b.rows ();
743  octave_idx_type b_nc = b.cols ();
744 
745  if (a.numel () == 1 && b.numel () > 1)
746  return scalar_xpow (a(0), b);
747 
748  if (nr != b_nr || nc != b_nc)
749  {
750  gripe_nonconformant ("operator .^", nr, nc, b_nr, b_nc);
751  return octave_value ();
752  }
753 
754  SparseComplexMatrix result (nr, nc, Complex (1.0, 0.0));
755  for (octave_idx_type j = 0; j < nc; j++)
756  {
757  for (octave_idx_type i = a.cidx (j); i < a.cidx (j+1); i++)
758  {
759  octave_quit ();
760  result.xelem (a.ridx (i), j) = std::pow (a.data (i),
761  b(a.ridx (i), j));
762  }
763  }
764  result.maybe_compress (true);
765 
766  return result;
767 }