GNU Octave 10.1.0
A high-level interpreted language, primarily intended for numerical computations, mostly compatible with Matlab
 
Loading...
Searching...
No Matches
pow2.cc
Go to the documentation of this file.
1////////////////////////////////////////////////////////////////////////
2//
3// Copyright (C) 2022-2025 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 <cmath>
31
32#include "lo-array-errwarn.h"
33
34#include "defun.h"
35#include "error.h"
36#include "errwarn.h"
37
38// FIXME: According to cppreference.com the implementation of `ldexp (f, e)`
39// might be less efficient that the corresponding `f * exp2 (e)`. Consider
40// replacing our implementation with the latter.
41
42template <typename T>
43void
44map_2_xldexp (Array<T>& y, const Array<T>& f, const Array<T>& e)
45{
46 if (f.numel () == e.numel () || e.numel () == 1)
47 y = Array<T> (f.dims ());
48 else if (f.numel () == 1)
49 y = Array<T> (e.dims ());
50 else
51 octave::err_nonconformant ("pow2", f.dims (), e.dims ());
52
53 octave_idx_type f_inc = (f.numel () == 1) ? 0 : 1;
54 octave_idx_type e_inc = (e.numel () == 1) ? 0 : 1;
55
56 for (octave_idx_type i = 0; i < y.numel (); i++)
57 y.xelem (i) = std::ldexp (f.xelem (i * f_inc),
58 static_cast<int> (e.xelem (i * e_inc)));
59}
60
61void
63 const SparseMatrix& e)
64{
65 if (e.numel () == 1)
66 {
67 int ee = static_cast<int> (e.data (0));
68 for (octave_idx_type i = 0; i < y.nnz (); i++)
69 y.data (i) = std::ldexp (f.data (i), ee);
70 }
71 else if (f.numel () == e.numel ())
72 {
73 octave_idx_type col = 1;
74 for (octave_idx_type i = 0; i < y.nnz (); i++)
75 {
76 // Determine current column.
77 while (i >= f.cidx (col))
78 col++;
79 int ee = static_cast<int> (e.xelem (f.ridx (i), col - 1));
80 y.data (i) = std::ldexp (f.data (i), ee);
81 }
82 }
83 else
84 octave::err_nonconformant ("pow2", f.dims (), e.dims ());
85}
86
88
89DEFUN (pow2, args, ,
90 doc: /* -*- texinfo -*-
91@deftypefn {} {@var{y} =} pow2 (@var{x})
92@deftypefnx {} {@var{y} =} pow2 (@var{f}, @var{e})
93With one input argument, compute
94@tex
95$y = 2^x$
96@end tex
97@ifnottex
98y = 2 .^ x
99@end ifnottex
100for each element of @var{x}.
101
102With two input arguments, return
103@tex
104$y = f \cdot 2^e$,
105@end tex
106@ifnottex
107y = f .* (2 .^ e).
108@end ifnottex
109where for complex inputs only the real part of both inputs is regarded
110and from @var{e} only the real integer part. This calling form corresponds
111to C/C++ standard function @code{ldexp()}.
112@seealso{log2, nextpow2, power}
113@end deftypefn */)
114{
115 if (args.length () < 1 || args.length () > 2)
116 print_usage ();
117
118 if (! args(0).isfloat ())
119 err_wrong_type_arg ("pow2", args(0));
120
121 // Call exp2(f) where possible for numerical more accurate results.
122 if (args.length () == 1)
123 {
124 if (args(0).iscomplex ())
125 {
126 // The C++ standard does not define exp2 for complex arguments.
127 // Therefore call `2.^x`.
128 octave_value retval = octave::binary_op (octave_value::op_el_pow,
129 2, args(0));
130
131 // Preserve sparse datatype, but even for sparse input fill-up
132 // is unavoidable `2^0 == 1` thus cast only.
133 if (args(0).issparse ())
134 retval = octave_value (retval.sparse_complex_matrix_value ());
135
136 return ovl (retval);
137 }
138 else if (args(0).is_single_type ())
139 {
140 FloatNDArray x = args(0).float_array_value ();
141 FloatNDArray y (x.dims ());
142 for (octave_idx_type i = 0; i < y.numel (); i++)
143 y.xelem (i) = std::exp2 (x.xelem (i));
144 return ovl (y);
145 }
146 else
147 {
148 NDArray x = args(0).array_value ();
149 NDArray y (x.dims ());
150 for (octave_idx_type i = 0; i < y.numel (); i++)
151 y.xelem (i) = std::exp2 (x.xelem (i));
152
153 // Preserve sparse datatype, but even for sparse input fill-up
154 // is unavoidable `2^0 == 1` thus cast only.
155 if (args(0).issparse ())
156 return ovl (SparseMatrix (y));
157 else
158 return ovl (y);
159 }
160 }
161
162 // For Matlab compatibility, the two argument call `y = pow2 (f, e)`
163 // corresponds to std::ldexp() (see bug #61968). The resulting y is
164 // computed quickly by adding the integer part of e to the floating-point
165 // exponent of f.
166
167 if (! args(1).isfloat ())
168 err_wrong_type_arg ("pow2", args(1));
169
170 if (args(0).iscomplex () || args(1).iscomplex ())
171 warning_with_id ("Octave:pow2:imaginary-ignored",
172 "pow2: imaginary part is ignored");
173
174 // Note: Matlab R2021a errors on `pow2 (sparse (f), single (e))`,
175 // but sparsity in f determines output and can significantly
176 // reduce computation, e.g. `N=1e5; pow2(speye(N),sparse(N,N))`.
177 if (args(0).issparse ())
178 {
179 SparseMatrix f = args(0).sparse_matrix_value ();
180
181 // Special case: return a sparse zero matrix in size of e.
182 if ((f.numel () == 1) && (f.nnz () == 0))
183 return ovl (SparseMatrix (args(1).rows (), args(1).columns ()));
184
185 // Only do sparse computation, if it pays off. For scalar f fill-up
186 // is unavoidable even for sparse e because `f * 2^0 == f`. Use dense
187 // code below in this case.
188 if (f.numel () > 1)
189 {
190 SparseMatrix e = args(1).sparse_matrix_value ();
192 map_2_xldexp_sparse (y, f, e);
193 return ovl (y);
194 }
195 }
196
197 if (args(0).is_single_type () || args(1).is_single_type ())
198 {
199 FloatNDArray f = args(0).float_array_value ();
200 FloatNDArray e = args(1).float_array_value ();
201 FloatNDArray y;
202 map_2_xldexp (y, f, e);
203 return ovl (y);
204 }
205 else
206 {
207 NDArray f = args(0).array_value ();
208 NDArray e = args(1).array_value ();
209 NDArray y;
210 map_2_xldexp (y, f, e);
211
212 // Preserve sparse datatype.
213 // Cases for efficient use of sparsity were treated above already.
214 if (args(0).issparse ())
215 return ovl (SparseMatrix (y));
216 else
217 return ovl (y);
218 }
219}
220
221/*
222## Call `y = pow2 (x)`
223
224%!test
225%! fcns = {@double, @single, @complex};
226%! x = [3, 0, -3];
227%! v = [8, 1, .125];
228%! for i = 1:numel (fcns)
229%! fcn = fcns{i};
230%! assert (pow2 (fcn (x)), fcn (v), sqrt (eps));
231%! endfor
232
233%!test
234%! fcns = {@double, @single, @complex, @sparse};
235%! x = [3, 1, -3];
236%! v = [8, 2, .125];
237%! for i = 1:numel (fcns)
238%! fcn = fcns{i};
239%! assert (pow2 (fcn (x)), fcn (v), sqrt (eps));
240%! endfor
241
242%!test
243%! fcns = {@double, @single, @complex, @sparse};
244%! x = [1, 1+1i, 1i];
245%! for i = 1:numel (fcns)
246%! fcn = fcns{i};
247%! assert (pow2 (fcn (x)), fcn (2) .^ fcn (x), sqrt (eps));
248%! endfor
249
250## Call `y = pow2 (f, e)`
251
252%!test
253%! fcns = {@double, @single, @complex, @sparse};
254%! f = [2 2];
255%! e = [2 2];
256%! z = [8 8];
257%! warning ("off", "Octave:pow2:imaginary-ignored", "local");
258%! for i = 1:numel (fcns)
259%! fcn = fcns{i};
260%! assert (pow2 (fcn (f), fcn (e)), real (fcn (z)));
261%! endfor
262
263## Only integer part is taken into account.
264%!test
265%! f = 2;
266%! e = [2, 2.1, 2.2, 2.4, 2.5, 2.8];
267%! z = 8 .* ones (1, length (e));
268%! assert (pow2 (f, e), z);
269
270## Only real part is taken into account.
271%!test
272%! f = [1+1i, 1];
273%! e = 2;
274%! z = [4, 4];
275%! warning ("off", "Octave:pow2:imaginary-ignored", "local");
276%! assert (pow2 (f, e), z);
277
278%!test
279%! f = 1;
280%! e = [1+1i, 1];
281%! z = [2, 2];
282%! warning ("off", "Octave:pow2:imaginary-ignored", "local");
283%! assert (pow2 (f, e), z);
284
285%!test
286%! f = [1/2, pi/4, -3/4, 1/2, 1-eps()/2, 1/2];
287%! e = [1, 2, 2, -51, 1024, -1021];
288%! z = [1, pi, -3, eps(), realmax(), realmin()];
289%! assert (pow2 (f, e), z);
290
291## Tests for sparsity.
292%!assert (pow2 (sparse (0), ones (3)), sparse (3, 3));
293%!assert (pow2 (sparse (1), ones (3)), 2 .* sparse (ones (3)));
294%!assert (pow2 (sparse (1), speye (3)), sparse (ones (3) + eye (3)));
295%!assert (pow2 (sparse (3, 3), ones (3)), sparse (3, 3));
296%!assert (pow2 (speye (3), ones (3)), 2 .* speye (3));
297%!assert (pow2 (speye (3), 1), 2 .* speye (3));
298
299%!test
300%! f = speye (3);
301%! e = sparse (3, 3);
302%! e(1,1) = 1;
303%! e(1,3) = 1;
304%! z = f;
305%! z(1,1) = 2;
306%! assert (pow2 (f, e), z);
307
308## Large sparse matrix (only few real elements).
309%!test
310%! ## FIXME: `N = 1e5` would be a better test, but `assert` fills-up somehow.
311%! N = 1e3;
312%! assert (pow2 (speye (N), sparse (N,N)), speye (N));
313%! assert (pow2 (sparse (0), speye (N)), sparse(N,N));
314
315%!error <Invalid call> pow2 ()
316%!error <Invalid call> pow2 (1,2,3)
317%!error <wrong type argument> pow2 (int8 (1))
318%!error <wrong type argument> pow2 (2, int8 (1))
319%!warning <imaginary part is ignored> pow2 (i, 2);
320%!warning <imaginary part is ignored> pow2 (2, i);
321%!error <pow2: nonconformant arguments> pow2 ([1,2], [3,4,5])
322%!error <pow2: nonconformant arguments> pow2 (sparse ([1,2]), sparse ([3,4,5]))
323*/
324
325OCTAVE_END_NAMESPACE(octave)
N Dimensional Array with copy-on-write semantics.
Definition Array.h:130
const dim_vector & dims() const
Return a const-reference so that dims ()(i) works efficiently.
Definition Array.h:507
T & xelem(octave_idx_type n)
Size of the specified dimension.
Definition Array.h:525
octave_idx_type numel() const
Number of elements in the array.
Definition Array.h:418
T * data()
Definition Sparse.h:571
octave_idx_type numel() const
Definition Sparse.h:340
octave_idx_type nnz() const
Actual number of nonzero terms.
Definition Sparse.h:336
T & xelem(octave_idx_type n)
Definition Sparse.h:392
dim_vector dims() const
Definition Sparse.h:368
@ op_el_pow
Definition ov.h:107
SparseComplexMatrix sparse_complex_matrix_value(bool frc_str_conv=false) const
Definition ov.h:913
OCTAVE_BEGIN_NAMESPACE(octave) static octave_value daspk_fcn
void print_usage()
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_with_id(const char *id, const char *fmt,...)
Definition error.cc:1093
void err_wrong_type_arg(const char *name, const char *s)
Definition errwarn.cc:166
F77_RET_T const F77_DBLE * x
F77_RET_T const F77_DBLE const F77_DBLE * f
octave_value_list ovl(const OV_Args &... args)
Construct an octave_value_list with less typing.
Definition ovl.h:217
void map_2_xldexp(Array< T > &y, const Array< T > &f, const Array< T > &e)
Definition pow2.cc:44
void map_2_xldexp_sparse(SparseMatrix &y, const SparseMatrix &f, const SparseMatrix &e)
Definition pow2.cc:62