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