sparse-xpow.cc

Go to the documentation of this file.
00001 /*
00002 
00003 Copyright (C) 2004-2012 David Bateman
00004 Copyright (C) 1998-2004 Andy Adler
00005 
00006 This file is part of Octave.
00007 
00008 Octave is free software; you can redistribute it and/or modify it
00009 under the terms of the GNU General Public License as published by the
00010 Free Software Foundation; either version 3 of the License, or (at your
00011 option) any later version.
00012 
00013 Octave is distributed in the hope that it will be useful, but WITHOUT
00014 ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
00015 FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
00016 for more details.
00017 
00018 You should have received a copy of the GNU General Public License
00019 along with Octave; see the file COPYING.  If not, see
00020 <http://www.gnu.org/licenses/>.
00021 
00022 */
00023 
00024 #ifdef HAVE_CONFIG_H
00025 #include <config.h>
00026 #endif
00027 
00028 #include <cassert>
00029 #include <climits>
00030 
00031 #include "Array-util.h"
00032 #include "oct-cmplx.h"
00033 #include "quit.h"
00034 
00035 #include "error.h"
00036 #include "oct-obj.h"
00037 #include "utils.h"
00038 
00039 #include "dSparse.h"
00040 #include "CSparse.h"
00041 #include "ov-re-sparse.h"
00042 #include "ov-cx-sparse.h"
00043 #include "sparse-xpow.h"
00044 
00045 static inline int
00046 xisint (double x)
00047 {
00048   return (D_NINT (x) == x
00049           && ((x >= 0 && x < INT_MAX)
00050               || (x <= 0 && x > INT_MIN)));
00051 }
00052 
00053 
00054 // Safer pow functions. Only two make sense for sparse matrices, the
00055 // others should all promote to full matrices.
00056 
00057 octave_value
00058 xpow (const SparseMatrix& a, double b)
00059 {
00060   octave_value retval;
00061 
00062   octave_idx_type nr = a.rows ();
00063   octave_idx_type nc = a.cols ();
00064 
00065   if (nr == 0 || nc == 0 || nr != nc)
00066     error ("for A^b, A must be a square matrix");
00067   else
00068     {
00069       if (static_cast<int> (b) == b)
00070         {
00071           int btmp = static_cast<int> (b);
00072           if (btmp == 0)
00073             {
00074               SparseMatrix tmp = SparseMatrix (nr, nr, nr);
00075               for (octave_idx_type i = 0; i < nr; i++)
00076                 {
00077                   tmp.data (i) = 1.0;
00078                   tmp.ridx (i) = i;
00079                 }
00080               for (octave_idx_type i = 0; i < nr + 1; i++)
00081                 tmp.cidx (i) = i;
00082 
00083               retval = tmp;
00084             }
00085           else
00086             {
00087               SparseMatrix atmp;
00088               if (btmp < 0)
00089                 {
00090                   btmp = -btmp;
00091 
00092                   octave_idx_type info;
00093                   double rcond = 0.0;
00094                   MatrixType mattyp (a);
00095 
00096                   atmp = a.inverse (mattyp, info, rcond, 1);
00097 
00098                   if (info == -1)
00099                     warning ("inverse: matrix singular to machine\
00100  precision, rcond = %g", rcond);
00101                 }
00102               else
00103                 atmp = a;
00104 
00105               SparseMatrix result (atmp);
00106 
00107               btmp--;
00108 
00109               while (btmp > 0)
00110                 {
00111                   if (btmp & 1)
00112                     result = result * atmp;
00113 
00114                   btmp >>= 1;
00115 
00116                   if (btmp > 0)
00117                     atmp = atmp * atmp;
00118                 }
00119 
00120               retval = result;
00121             }
00122         }
00123       else
00124         error ("use full(a) ^ full(b)");
00125     }
00126 
00127   return retval;
00128 }
00129 
00130 octave_value
00131 xpow (const SparseComplexMatrix& a, double b)
00132 {
00133   octave_value retval;
00134 
00135   octave_idx_type nr = a.rows ();
00136   octave_idx_type nc = a.cols ();
00137 
00138   if (nr == 0 || nc == 0 || nr != nc)
00139     error ("for A^b, A must be a square matrix");
00140   else
00141     {
00142       if (static_cast<int> (b) == b)
00143         {
00144           int btmp = static_cast<int> (b);
00145           if (btmp == 0)
00146             {
00147               SparseMatrix tmp = SparseMatrix (nr, nr, nr);
00148               for (octave_idx_type i = 0; i < nr; i++)
00149                 {
00150                   tmp.data (i) = 1.0;
00151                   tmp.ridx (i) = i;
00152                 }
00153               for (octave_idx_type i = 0; i < nr + 1; i++)
00154                 tmp.cidx (i) = i;
00155 
00156               retval = tmp;
00157             }
00158           else
00159             {
00160               SparseComplexMatrix atmp;
00161               if (btmp < 0)
00162                 {
00163                   btmp = -btmp;
00164 
00165                   octave_idx_type info;
00166                   double rcond = 0.0;
00167                   MatrixType mattyp (a);
00168 
00169                   atmp = a.inverse (mattyp, info, rcond, 1);
00170 
00171                   if (info == -1)
00172                     warning ("inverse: matrix singular to machine\
00173  precision, rcond = %g", rcond);
00174                 }
00175               else
00176                 atmp = a;
00177 
00178               SparseComplexMatrix result (atmp);
00179 
00180               btmp--;
00181 
00182               while (btmp > 0)
00183                 {
00184                   if (btmp & 1)
00185                     result = result * atmp;
00186 
00187                   btmp >>= 1;
00188 
00189                   if (btmp > 0)
00190                     atmp = atmp * atmp;
00191                 }
00192 
00193               retval = result;
00194             }
00195         }
00196       else
00197         error ("use full(a) ^ full(b)");
00198     }
00199 
00200   return retval;
00201 }
00202 
00203 // Safer pow functions that work elementwise for matrices.
00204 //
00205 //       op2 \ op1:   s   m   cs   cm
00206 //            +--   +---+---+----+----+
00207 //   scalar   |     | * | 3 |  * |  9 |
00208 //                  +---+---+----+----+
00209 //   matrix         | 1 | 4 |  7 | 10 |
00210 //                  +---+---+----+----+
00211 //   complex_scalar | * | 5 |  * | 11 |
00212 //                  +---+---+----+----+
00213 //   complex_matrix | 2 | 6 |  8 | 12 |
00214 //                  +---+---+----+----+
00215 //
00216 //   * -> not needed.
00217 
00218 // FIXME -- these functions need to be fixed so that things
00219 // like
00220 //
00221 //   a = -1; b = [ 0, 0.5, 1 ]; r = a .^ b
00222 //
00223 // and
00224 //
00225 //   a = -1; b = [ 0, 0.5, 1 ]; for i = 1:3, r(i) = a .^ b(i), end
00226 //
00227 // produce identical results.  Also, it would be nice if -1^0.5
00228 // produced a pure imaginary result instead of a complex number with a
00229 // small real part.  But perhaps that's really a problem with the math
00230 // library...
00231 
00232 // Handle special case of scalar-sparse-matrix .^ sparse-matrix.
00233 // Forwarding to the scalar elem_xpow function and then converting the
00234 // result back to a sparse matrix is a bit wasteful but it does not
00235 // seem worth the effort to optimize -- how often does this case come up
00236 // in practice?
00237 
00238 template <class S, class SM>
00239 inline octave_value
00240 scalar_xpow (const S& a, const SM& b)
00241 {
00242   octave_value val = elem_xpow (a, b);
00243 
00244   if (val.is_complex_type ())
00245     return SparseComplexMatrix (val.complex_matrix_value ());
00246   else
00247     return SparseMatrix (val.matrix_value ());
00248 }
00249 
00250 /*
00251 %!assert (sparse (2) .^ [3, 4], sparse ([8, 16]));
00252 %!assert (sparse (2i) .^ [3, 4], sparse ([-0-8i, 16]));
00253 */
00254 
00255 // -*- 1 -*-
00256 octave_value
00257 elem_xpow (double a, const SparseMatrix& b)
00258 {
00259   octave_value retval;
00260 
00261   octave_idx_type nr = b.rows ();
00262   octave_idx_type nc = b.cols ();
00263 
00264   double d1, d2;
00265 
00266   if (a < 0.0 && ! b.all_integers (d1, d2))
00267     {
00268       Complex atmp (a);
00269       ComplexMatrix result (nr, nc);
00270 
00271       for (octave_idx_type j = 0; j < nc; j++)
00272         {
00273           for (octave_idx_type i = 0; i < nr; i++)
00274             {
00275               octave_quit ();
00276               result (i, j) = std::pow (atmp, b(i,j));
00277             }
00278         }
00279 
00280       retval = result;
00281     }
00282   else
00283     {
00284       Matrix result (nr, nc);
00285 
00286       for (octave_idx_type j = 0; j < nc; j++)
00287         {
00288           for (octave_idx_type i = 0; i < nr; i++)
00289             {
00290               octave_quit ();
00291               result (i, j) = std::pow (a, b(i,j));
00292             }
00293         }
00294 
00295       retval = result;
00296     }
00297 
00298   return retval;
00299 }
00300 
00301 // -*- 2 -*-
00302 octave_value
00303 elem_xpow (double a, const SparseComplexMatrix& b)
00304 {
00305   octave_idx_type nr = b.rows ();
00306   octave_idx_type nc = b.cols ();
00307 
00308   Complex atmp (a);
00309   ComplexMatrix result (nr, nc);
00310 
00311   for (octave_idx_type j = 0; j < nc; j++)
00312     {
00313       for (octave_idx_type i = 0; i < nr; i++)
00314         {
00315           octave_quit ();
00316           result (i, j) = std::pow (atmp, b(i,j));
00317         }
00318     }
00319 
00320   return result;
00321 }
00322 
00323 // -*- 3 -*-
00324 octave_value
00325 elem_xpow (const SparseMatrix& a, double b)
00326 {
00327   // FIXME What should a .^ 0 give?? Matlab gives a
00328   // sparse matrix with same structure as a, which is strictly
00329   // incorrect. Keep compatiability.
00330 
00331   octave_value retval;
00332 
00333   octave_idx_type nz = a.nnz ();
00334 
00335   if (b <= 0.0)
00336     {
00337       octave_idx_type nr = a.rows ();
00338       octave_idx_type nc = a.cols ();
00339 
00340       if (static_cast<int> (b) != b && a.any_element_is_negative ())
00341         {
00342           ComplexMatrix result (nr, nc, Complex (std::pow (0.0, b)));
00343 
00344           // FIXME -- avoid apparent GNU libm bug by
00345           // converting A and B to complex instead of just A.
00346           Complex btmp (b);
00347 
00348           for (octave_idx_type j = 0; j < nc; j++)
00349             for (octave_idx_type i = a.cidx(j); i < a.cidx(j+1); i++)
00350               {
00351                 octave_quit ();
00352 
00353                 Complex atmp (a.data (i));
00354 
00355                 result (a.ridx(i), j) = std::pow (atmp, btmp);
00356               }
00357 
00358           retval = octave_value (result);
00359         }
00360       else
00361         {
00362           Matrix result (nr, nc, (std::pow (0.0, b)));
00363 
00364           for (octave_idx_type j = 0; j < nc; j++)
00365             for (octave_idx_type i = a.cidx(j); i < a.cidx(j+1); i++)
00366               {
00367                 octave_quit ();
00368                 result (a.ridx(i), j) = std::pow (a.data (i), b);
00369               }
00370 
00371           retval = octave_value (result);
00372         }
00373     }
00374   else if (static_cast<int> (b) != b && a.any_element_is_negative ())
00375     {
00376       SparseComplexMatrix result (a);
00377 
00378       for (octave_idx_type i = 0; i < nz; i++)
00379         {
00380           octave_quit ();
00381 
00382           // FIXME -- avoid apparent GNU libm bug by
00383           // converting A and B to complex instead of just A.
00384 
00385           Complex atmp (a.data (i));
00386           Complex btmp (b);
00387 
00388           result.data (i) = std::pow (atmp, btmp);
00389         }
00390 
00391       result.maybe_compress (true);
00392 
00393       retval = result;
00394     }
00395   else
00396     {
00397       SparseMatrix result (a);
00398 
00399       for (octave_idx_type i = 0; i < nz; i++)
00400         {
00401           octave_quit ();
00402           result.data (i) = std::pow (a.data (i), b);
00403         }
00404 
00405       result.maybe_compress (true);
00406 
00407       retval = result;
00408     }
00409 
00410   return retval;
00411 }
00412 
00413 // -*- 4 -*-
00414 octave_value
00415 elem_xpow (const SparseMatrix& a, const SparseMatrix& b)
00416 {
00417   octave_value retval;
00418 
00419   octave_idx_type nr = a.rows ();
00420   octave_idx_type nc = a.cols ();
00421 
00422   octave_idx_type b_nr = b.rows ();
00423   octave_idx_type b_nc = b.cols ();
00424 
00425   if (a.numel () == 1 && b.numel () > 1)
00426     return scalar_xpow (a(0), b);
00427 
00428   if (nr != b_nr || nc != b_nc)
00429     {
00430       gripe_nonconformant ("operator .^", nr, nc, b_nr, b_nc);
00431       return octave_value ();
00432     }
00433 
00434   int convert_to_complex = 0;
00435   for (octave_idx_type j = 0; j < nc; j++)
00436     for (octave_idx_type i = a.cidx(j); i < a.cidx(j+1); i++)
00437       {
00438         if (a.data(i) < 0.0)
00439           {
00440             double btmp = b (a.ridx(i), j);
00441             if (static_cast<int> (btmp) != btmp)
00442               {
00443                 convert_to_complex = 1;
00444                 goto done;
00445               }
00446           }
00447       }
00448 
00449 done:
00450 
00451   // This is a dumb operator for sparse matrices anyway, and there is
00452   // no sensible way to handle the 0.^0 versus the 0.^x cases. Therefore
00453   // allocate a full matrix filled for the 0.^0 case and shrink it later
00454   // as needed
00455 
00456   if (convert_to_complex)
00457     {
00458       SparseComplexMatrix complex_result (nr, nc, Complex(1.0, 0.0));
00459 
00460       for (octave_idx_type j = 0; j < nc; j++)
00461         {
00462           for (octave_idx_type i = a.cidx(j); i < a.cidx(j+1); i++)
00463             {
00464               octave_quit ();
00465               complex_result.xelem(a.ridx(i), j) =
00466                 std::pow (Complex(a.data(i)), Complex(b(a.ridx(i), j)));
00467             }
00468         }
00469       complex_result.maybe_compress (true);
00470       retval = complex_result;
00471     }
00472   else
00473     {
00474       SparseMatrix result (nr, nc, 1.0);
00475 
00476       for (octave_idx_type j = 0; j < nc; j++)
00477         {
00478           for (octave_idx_type i = a.cidx(j); i < a.cidx(j+1); i++)
00479             {
00480               octave_quit ();
00481               result.xelem(a.ridx(i), j) = std::pow (a.data(i),
00482                                                      b (a.ridx(i), j));
00483             }
00484         }
00485       result.maybe_compress (true);
00486       retval = result;
00487     }
00488 
00489   return retval;
00490 }
00491 
00492 // -*- 5 -*-
00493 octave_value
00494 elem_xpow (const SparseMatrix& a, const Complex& b)
00495 {
00496   octave_value retval;
00497 
00498   if (b == 0.0)
00499     // Can this case ever happen, due to automatic retyping with maybe_mutate?
00500     retval = octave_value (NDArray (a.dims (), 1));
00501   else
00502     {
00503       octave_idx_type nz = a.nnz ();
00504       SparseComplexMatrix result (a);
00505 
00506       for (octave_idx_type i = 0; i < nz; i++)
00507         {
00508           octave_quit ();
00509           result.data (i) = std::pow (Complex (a.data (i)), b);
00510         }
00511 
00512       result.maybe_compress (true);
00513 
00514       retval = result;
00515     }
00516 
00517   return retval;
00518 }
00519 
00520 // -*- 6 -*-
00521 octave_value
00522 elem_xpow (const SparseMatrix& a, const SparseComplexMatrix& b)
00523 {
00524   octave_idx_type nr = a.rows ();
00525   octave_idx_type nc = a.cols ();
00526 
00527   octave_idx_type b_nr = b.rows ();
00528   octave_idx_type b_nc = b.cols ();
00529 
00530   if (a.numel () == 1 && b.numel () > 1)
00531     return scalar_xpow (a(0), b);
00532 
00533   if (nr != b_nr || nc != b_nc)
00534     {
00535       gripe_nonconformant ("operator .^", nr, nc, b_nr, b_nc);
00536       return octave_value ();
00537     }
00538 
00539   SparseComplexMatrix result (nr, nc, Complex(1.0, 0.0));
00540   for (octave_idx_type j = 0; j < nc; j++)
00541     {
00542       for (octave_idx_type i = a.cidx(j); i < a.cidx(j+1); i++)
00543         {
00544           octave_quit ();
00545           result.xelem(a.ridx(i), j) = std::pow (a.data(i), b (a.ridx(i), j));
00546         }
00547     }
00548 
00549   result.maybe_compress (true);
00550 
00551   return result;
00552 }
00553 
00554 // -*- 7 -*-
00555 octave_value
00556 elem_xpow (const Complex& a, const SparseMatrix& b)
00557 {
00558   octave_idx_type nr = b.rows ();
00559   octave_idx_type nc = b.cols ();
00560 
00561   ComplexMatrix result (nr, nc);
00562 
00563   for (octave_idx_type j = 0; j < nc; j++)
00564     {
00565       for (octave_idx_type i = 0; i < nr; i++)
00566         {
00567           octave_quit ();
00568           double btmp = b (i, j);
00569           if (xisint (btmp))
00570             result (i, j) = std::pow (a, static_cast<int> (btmp));
00571           else
00572             result (i, j) = std::pow (a, btmp);
00573         }
00574     }
00575 
00576   return result;
00577 }
00578 
00579 // -*- 8 -*-
00580 octave_value
00581 elem_xpow (const Complex& a, const SparseComplexMatrix& b)
00582 {
00583   octave_idx_type nr = b.rows ();
00584   octave_idx_type nc = b.cols ();
00585 
00586   ComplexMatrix result (nr, nc);
00587   for (octave_idx_type j = 0; j < nc; j++)
00588     for (octave_idx_type i = 0; i < nr; i++)
00589       {
00590         octave_quit ();
00591         result (i, j) = std::pow (a, b (i, j));
00592       }
00593 
00594   return result;
00595 }
00596 
00597 // -*- 9 -*-
00598 octave_value
00599 elem_xpow (const SparseComplexMatrix& a, double b)
00600 {
00601   octave_value retval;
00602 
00603   if (b <= 0)
00604     {
00605       octave_idx_type nr = a.rows ();
00606       octave_idx_type nc = a.cols ();
00607 
00608       ComplexMatrix result (nr, nc, Complex (std::pow (0.0, b)));
00609 
00610       if (xisint (b))
00611         {
00612           for (octave_idx_type j = 0; j < nc; j++)
00613             for (octave_idx_type i = a.cidx(j); i < a.cidx(j+1); i++)
00614               {
00615                 octave_quit ();
00616                 result (a.ridx(i), j) =
00617                   std::pow (a.data (i), static_cast<int> (b));
00618               }
00619         }
00620       else
00621         {
00622           for (octave_idx_type j = 0; j < nc; j++)
00623             for (octave_idx_type i = a.cidx(j); i < a.cidx(j+1); i++)
00624               {
00625                 octave_quit ();
00626                 result (a.ridx(i), j) = std::pow (a.data (i), b);
00627               }
00628         }
00629 
00630       retval = result;
00631     }
00632   else
00633     {
00634       octave_idx_type nz = a.nnz ();
00635 
00636       SparseComplexMatrix result (a);
00637 
00638       if (xisint (b))
00639         {
00640           for (octave_idx_type i = 0; i < nz; i++)
00641             {
00642               octave_quit ();
00643               result.data (i) = std::pow (a.data (i), static_cast<int> (b));
00644             }
00645         }
00646       else
00647         {
00648           for (octave_idx_type i = 0; i < nz; i++)
00649             {
00650               octave_quit ();
00651               result.data (i) = std::pow (a.data (i), b);
00652             }
00653         }
00654 
00655       result.maybe_compress (true);
00656 
00657       retval = result;
00658     }
00659 
00660   return retval;
00661 }
00662 
00663 // -*- 10 -*-
00664 octave_value
00665 elem_xpow (const SparseComplexMatrix& a, const SparseMatrix& b)
00666 {
00667   octave_idx_type nr = a.rows ();
00668   octave_idx_type nc = a.cols ();
00669 
00670   octave_idx_type b_nr = b.rows ();
00671   octave_idx_type b_nc = b.cols ();
00672 
00673   if (a.numel () == 1 && b.numel () > 1)
00674     return scalar_xpow (a(0), b);
00675 
00676   if (nr != b_nr || nc != b_nc)
00677     {
00678       gripe_nonconformant ("operator .^", nr, nc, b_nr, b_nc);
00679       return octave_value ();
00680     }
00681 
00682   SparseComplexMatrix result (nr, nc, Complex(1.0, 0.0));
00683   for (octave_idx_type j = 0; j < nc; j++)
00684     {
00685       for (octave_idx_type i = a.cidx(j); i < a.cidx(j+1); i++)
00686         {
00687           octave_quit ();
00688           double btmp = b (a.ridx(i), j);
00689           Complex tmp;
00690 
00691           if (xisint (btmp))
00692             result.xelem(a.ridx(i), j) = std::pow (a.data (i),
00693                                               static_cast<int> (btmp));
00694           else
00695             result.xelem(a.ridx(i), j) = std::pow (a.data (i), btmp);
00696         }
00697     }
00698 
00699   result.maybe_compress (true);
00700 
00701   return result;
00702 }
00703 
00704 // -*- 11 -*-
00705 octave_value
00706 elem_xpow (const SparseComplexMatrix& a, const Complex& b)
00707 {
00708   octave_value retval;
00709 
00710   if (b == 0.0)
00711     // Can this case ever happen, due to automatic retyping with maybe_mutate?
00712     retval = octave_value (NDArray (a.dims (), 1));
00713   else
00714     {
00715 
00716       octave_idx_type nz = a.nnz ();
00717 
00718       SparseComplexMatrix result (a);
00719 
00720       for (octave_idx_type i = 0; i < nz; i++)
00721         {
00722           octave_quit ();
00723           result.data (i) = std::pow (a.data (i), b);
00724         }
00725 
00726       result.maybe_compress (true);
00727 
00728       retval = result;
00729     }
00730 
00731   return retval;
00732 }
00733 
00734 // -*- 12 -*-
00735 octave_value
00736 elem_xpow (const SparseComplexMatrix& a, const SparseComplexMatrix& b)
00737 {
00738   octave_idx_type nr = a.rows ();
00739   octave_idx_type nc = a.cols ();
00740 
00741   octave_idx_type b_nr = b.rows ();
00742   octave_idx_type b_nc = b.cols ();
00743 
00744   if (a.numel () == 1 && b.numel () > 1)
00745     return scalar_xpow (a(0), b);
00746 
00747   if (nr != b_nr || nc != b_nc)
00748     {
00749       gripe_nonconformant ("operator .^", nr, nc, b_nr, b_nc);
00750       return octave_value ();
00751     }
00752 
00753   SparseComplexMatrix result (nr, nc, Complex(1.0, 0.0));
00754   for (octave_idx_type j = 0; j < nc; j++)
00755     {
00756       for (octave_idx_type i = a.cidx(j); i < a.cidx(j+1); i++)
00757         {
00758           octave_quit ();
00759           result.xelem(a.ridx(i), j) = std::pow (a.data (i), b (a.ridx(i), j));
00760         }
00761     }
00762   result.maybe_compress (true);
00763 
00764   return result;
00765 }
 All Classes Files Functions Variables Typedefs Enumerations Enumerator Friends Defines