GNU Octave  6.2.0
A high-level interpreted language, primarily intended for numerical computations, mostly compatible with Matlab
filter.cc
Go to the documentation of this file.
1 ////////////////////////////////////////////////////////////////////////
2 //
3 // Copyright (C) 1996-2021 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 // Based on Tony Richardson's filter.m.
27 //
28 // Originally translated to C++ by KH (Kurt.Hornik@wu-wien.ac.at)
29 // with help from Fritz Leisch and Andreas Weingessel on Oct 20, 1994.
30 //
31 // Rewritten to use templates to handle both real and complex cases by
32 // jwe, Wed Nov 1 19:15:29 1995.
33 
34 #if defined (HAVE_CONFIG_H)
35 # include "config.h"
36 #endif
37 
38 #include "quit.h"
39 
40 #include "defun.h"
41 #include "error.h"
42 #include "ovl.h"
43 
44 template <typename T>
47  int dim = 0)
48 {
49  MArray<T> y;
50 
51  octave_idx_type a_len = a.numel ();
52  octave_idx_type b_len = b.numel ();
53 
54  octave_idx_type ab_len = (a_len > b_len ? a_len : b_len);
55 
56  // FIXME: The two lines below should be unnecessary because
57  // this template is called with a and b as column vectors
58  // already. However the a.resize line is currently (2011/04/26)
59  // necessary to stop bug #33164.
60  b.resize (dim_vector (ab_len, 1), 0.0);
61  if (a_len > 1)
62  a.resize (dim_vector (ab_len, 1), 0.0);
63 
64  T norm = a (0);
65 
66  if (norm == static_cast<T> (0.0))
67  error ("filter: the first element of A must be nonzero");
68 
69  dim_vector x_dims = x.dims ();
70  if (dim < 0 || dim > x_dims.ndims ())
71  error ("filter: DIM must be a valid dimension");
72 
73  octave_idx_type x_len = x_dims(dim);
74 
75  dim_vector si_dims = si.dims ();
76  octave_idx_type si_len = si_dims(0);
77 
78  if (si_len != ab_len - 1)
79  error ("filter: first dimension of SI must be of length max (length (a), length (b)) - 1");
80 
81  if (si_dims.ndims () != x_dims.ndims ())
82  error ("filter: dimensionality of SI and X must agree");
83 
84  for (octave_idx_type i = 1; i < dim; i++)
85  {
86  if (si_dims(i) != x_dims(i-1))
87  error ("filter: dimensionality of SI and X must agree");
88  }
89  for (octave_idx_type i = dim+1; i < x_dims.ndims (); i++)
90  {
91  if (si_dims(i) != x_dims(i))
92  error ("filter: dimensionality of SI and X must agree");
93  }
94 
95  if (x_len == 0)
96  return x;
97 
98  if (norm != static_cast<T> (1.0))
99  {
100  a /= norm;
101  b /= norm;
102  }
103 
104  if (a_len <= 1 && si_len <= 0)
105  return b(0) * x;
106 
107  y.resize (x_dims, 0.0);
108 
109  octave_idx_type x_stride = 1;
110  for (int i = 0; i < dim; i++)
111  x_stride *= x_dims(i);
112 
113  octave_idx_type x_num = x_dims.numel () / x_len;
114  for (octave_idx_type num = 0; num < x_num; num++)
115  {
116  octave_idx_type x_offset;
117  if (x_stride == 1)
118  x_offset = num * x_len;
119  else
120  {
121  octave_idx_type x_offset2 = 0;
122  x_offset = num;
123  while (x_offset >= x_stride)
124  {
125  x_offset -= x_stride;
126  x_offset2++;
127  }
128  x_offset += x_offset2 * x_stride * x_len;
129  }
130  octave_idx_type si_offset = num * si_len;
131 
132  if (a_len > 1)
133  {
134  T *py = y.fortran_vec ();
135  T *psi = si.fortran_vec ();
136 
137  const T *pa = a.data ();
138  const T *pb = b.data ();
139  const T *px = x.data ();
140 
141  psi += si_offset;
142 
143  for (octave_idx_type i = 0, idx = x_offset;
144  i < x_len;
145  i++, idx += x_stride)
146  {
147  py[idx] = psi[0] + pb[0] * px[idx];
148 
149  if (si_len > 0)
150  {
151  for (octave_idx_type j = 0; j < si_len - 1; j++)
152  {
153  octave_quit ();
154 
155  psi[j] = psi[j+1] - pa[j+1] * py[idx] + pb[j+1] * px[idx];
156  }
157 
158  psi[si_len-1] = pb[si_len] * px[idx] - pa[si_len] * py[idx];
159  }
160  else
161  {
162  octave_quit ();
163 
164  psi[0] = pb[si_len] * px[idx] - pa[si_len] * py[idx];
165  }
166  }
167  }
168  else if (si_len > 0)
169  {
170  T *py = y.fortran_vec ();
171  T *psi = si.fortran_vec ();
172 
173  const T *pb = b.data ();
174  const T *px = x.data ();
175 
176  psi += si_offset;
177 
178  for (octave_idx_type i = 0, idx = x_offset;
179  i < x_len;
180  i++, idx += x_stride)
181  {
182  py[idx] = psi[0] + pb[0] * px[idx];
183 
184  if (si_len > 1)
185  {
186  for (octave_idx_type j = 0; j < si_len - 1; j++)
187  {
188  octave_quit ();
189 
190  psi[j] = psi[j+1] + pb[j+1] * px[idx];
191  }
192 
193  psi[si_len-1] = pb[si_len] * px[idx];
194  }
195  else
196  {
197  octave_quit ();
198 
199  psi[0] = pb[1] * px[idx];
200  }
201  }
202  }
203  }
204 
205  return y;
206 }
207 
208 template <typename T>
209 MArray<T>
210 filter (MArray<T>& b, MArray<T>& a, MArray<T>& x, int dim = -1)
211 {
212  dim_vector x_dims = x.dims ();
213 
214  if (dim < 0)
215  dim = x_dims.first_non_singleton ();
216  else if (dim > x_dims.ndims ())
217  error ("filter: DIM must be a valid dimension");
218 
219  octave_idx_type a_len = a.numel ();
220  octave_idx_type b_len = b.numel ();
221 
222  octave_idx_type si_len = (a_len > b_len ? a_len : b_len) - 1;
223  dim_vector si_dims = x.dims ();
224  for (int i = dim; i > 0; i--)
225  si_dims(i) = si_dims(i-1);
226  si_dims(0) = si_len;
227 
228  MArray<T> si (si_dims, T (0.0));
229 
230  return filter (b, a, x, si, dim);
231 }
232 
233 DEFUN (filter, args, ,
234  doc: /* -*- texinfo -*-
235 @deftypefn {} {@var{y} =} filter (@var{b}, @var{a}, @var{x})
236 @deftypefnx {} {[@var{y}, @var{sf}] =} filter (@var{b}, @var{a}, @var{x}, @var{si})
237 @deftypefnx {} {[@var{y}, @var{sf}] =} filter (@var{b}, @var{a}, @var{x}, [], @var{dim})
238 @deftypefnx {} {[@var{y}, @var{sf}] =} filter (@var{b}, @var{a}, @var{x}, @var{si}, @var{dim})
239 Apply a 1-D digital filter to the data @var{x}.
240 
241 @code{filter} returns the solution to the following linear, time-invariant
242 difference equation:
243 @tex
244 $$
245 \sum_{k=0}^N a_{k+1} y_{n-k} = \sum_{k=0}^M b_{k+1} x_{n-k}, \qquad
246  1 \le n \le P
247 $$
248 @end tex
249 @ifnottex
250 @c Set example in small font to prevent overfull line
251 
252 @smallexample
253 @group
254  N M
255 SUM a(k+1) y(n-k) = SUM b(k+1) x(n-k) for 1<=n<=length(x)
256 k=0 k=0
257 @end group
258 @end smallexample
259 
260 @end ifnottex
261 
262 @noindent
263 where
264 @ifnottex
265 N=length(a)-1 and M=length(b)-1.
266 @end ifnottex
267 @tex
268 $a \in \Re^{N-1}$, $b \in \Re^{M-1}$, and $x \in \Re^P$.
269 @end tex
270 The result is calculated over the first non-singleton dimension of @var{x}
271 or over @var{dim} if supplied.
272 
273 An equivalent form of the equation is:
274 @tex
275 $$
276 y_n = -\sum_{k=1}^N c_{k+1} y_{n-k} + \sum_{k=0}^M d_{k+1} x_{n-k}, \qquad
277  1 \le n \le P
278 $$
279 @end tex
280 @ifnottex
281 @c Set example in small font to prevent overfull line
282 
283 @smallexample
284 @group
285  N M
286 y(n) = - SUM c(k+1) y(n-k) + SUM d(k+1) x(n-k) for 1<=n<=length(x)
287  k=1 k=0
288 @end group
289 @end smallexample
290 
291 @end ifnottex
292 
293 @noindent
294 where
295 @ifnottex
296  c = a/a(1) and d = b/a(1).
297 @end ifnottex
298 @tex
299 $c = a/a_1$ and $d = b/a_1$.
300 @end tex
301 
302 If the fourth argument @var{si} is provided, it is taken as the
303 initial state of the system and the final state is returned as
304 @var{sf}. The state vector is a column vector whose length is
305 equal to the length of the longest coefficient vector minus one.
306 If @var{si} is not supplied, the initial state vector is set to all
307 zeros.
308 
309 In terms of the Z Transform, @var{y} is the result of passing the
310 discrete-time signal @var{x} through a system characterized by the following
311 rational system function:
312 @tex
313 $$
314 H(z) = {\displaystyle\sum_{k=0}^M d_{k+1} z^{-k}
315  \over 1 + \displaystyle\sum_{k+1}^N c_{k+1} z^{-k}}
316 $$
317 @end tex
318 @ifnottex
319 
320 @example
321 @group
322  M
323  SUM d(k+1) z^(-k)
324  k=0
325 H(z) = ---------------------
326  N
327  1 + SUM c(k+1) z^(-k)
328  k=1
329 @end group
330 @end example
331 
332 @end ifnottex
333 @seealso{filter2, fftfilt, freqz}
334 @end deftypefn */)
335 {
336  int nargin = args.length ();
337 
338  if (nargin < 3 || nargin > 5)
339  print_usage ();
340 
341  int dim;
342  dim_vector x_dims = args(2).dims ();
343 
344  if (nargin == 5)
345  {
346  dim = args(4).nint_value () - 1;
347  if (dim < 0 || dim >= x_dims.ndims ())
348  error ("filter: DIM must be a valid dimension");
349  }
350  else
351  dim = x_dims.first_non_singleton ();
352 
354 
355  const char *a_b_errmsg = "filter: A and B must be vectors";
356  const char *x_si_errmsg = "filter: X and SI must be arrays";
357 
358  bool isfloat = (args(0).is_single_type ()
359  || args(1).is_single_type ()
360  || args(2).is_single_type ()
361  || (nargin >= 4 && args(3).is_single_type ()));
362 
363  if (args(0).iscomplex ()
364  || args(1).iscomplex ()
365  || args(2).iscomplex ()
366  || (nargin >= 4 && args(3).iscomplex ()))
367  {
368  if (isfloat)
369  {
370  FloatComplexColumnVector b = args(0).xfloat_complex_vector_value (a_b_errmsg);
371  FloatComplexColumnVector a = args(1).xfloat_complex_vector_value (a_b_errmsg);
372  FloatComplexNDArray x = args(2).xfloat_complex_array_value (x_si_errmsg);
373 
375 
376  if (nargin == 3 || args(3).isempty ())
377  {
378  octave_idx_type a_len = a.numel ();
379  octave_idx_type b_len = b.numel ();
380 
381  octave_idx_type si_len = (a_len > b_len ? a_len : b_len) - 1;
382 
383  dim_vector si_dims = x.dims ();
384  for (int i = dim; i > 0; i--)
385  si_dims(i) = si_dims(i-1);
386  si_dims(0) = si_len;
387 
388  si.resize (si_dims, 0.0);
389  }
390  else
391  {
392  si = args(3).xfloat_complex_array_value (x_si_errmsg);
393 
394  if (si.isvector () && x.isvector ())
395  si = si.reshape (dim_vector (si.numel (), 1));
396  }
397 
398  FloatComplexNDArray y (filter (b, a, x, si, dim));
399 
400  retval = ovl (y, si);
401  }
402  else
403  {
404  ComplexColumnVector b = args(0).xcomplex_vector_value (a_b_errmsg);
405  ComplexColumnVector a = args(1).xcomplex_vector_value (a_b_errmsg);
406 
407  ComplexNDArray x = args(2).xcomplex_array_value (x_si_errmsg);
408 
409  ComplexNDArray si;
410 
411  if (nargin == 3 || args(3).isempty ())
412  {
413  octave_idx_type a_len = a.numel ();
414  octave_idx_type b_len = b.numel ();
415 
416  octave_idx_type si_len = (a_len > b_len ? a_len : b_len) - 1;
417 
418  dim_vector si_dims = x.dims ();
419  for (int i = dim; i > 0; i--)
420  si_dims(i) = si_dims(i-1);
421  si_dims(0) = si_len;
422 
423  si.resize (si_dims, 0.0);
424  }
425  else
426  {
427  si = args(3).xcomplex_array_value (x_si_errmsg);
428 
429  if (si.isvector () && x.isvector ())
430  si = si.reshape (dim_vector (si.numel (), 1));
431  }
432 
433  ComplexNDArray y (filter (b, a, x, si, dim));
434 
435  retval = ovl (y, si);
436  }
437  }
438  else
439  {
440  if (isfloat)
441  {
442  FloatColumnVector b = args(0).xfloat_vector_value (a_b_errmsg);
443  FloatColumnVector a = args(1).xfloat_vector_value (a_b_errmsg);
444 
445  FloatNDArray x = args(2).xfloat_array_value (x_si_errmsg);
446 
447  FloatNDArray si;
448 
449  if (nargin == 3 || args(3).isempty ())
450  {
451  octave_idx_type a_len = a.numel ();
452  octave_idx_type b_len = b.numel ();
453 
454  octave_idx_type si_len = (a_len > b_len ? a_len : b_len) - 1;
455 
456  dim_vector si_dims = x.dims ();
457  for (int i = dim; i > 0; i--)
458  si_dims(i) = si_dims(i-1);
459  si_dims(0) = si_len;
460 
461  si.resize (si_dims, 0.0);
462  }
463  else
464  {
465  si = args(3).xfloat_array_value (x_si_errmsg);
466 
467  if (si.isvector () && x.isvector ())
468  si = si.reshape (dim_vector (si.numel (), 1));
469  }
470 
471  FloatNDArray y (filter (b, a, x, si, dim));
472 
473  retval = ovl (y, si);
474  }
475  else
476  {
477  ColumnVector b = args(0).xvector_value (a_b_errmsg);
478  ColumnVector a = args(1).xvector_value (a_b_errmsg);
479 
480  NDArray x = args(2).xarray_value (x_si_errmsg);
481 
482  NDArray si;
483 
484  if (nargin == 3 || args(3).isempty ())
485  {
486  octave_idx_type a_len = a.numel ();
487  octave_idx_type b_len = b.numel ();
488 
489  octave_idx_type si_len = (a_len > b_len ? a_len : b_len) - 1;
490 
491  dim_vector si_dims = x.dims ();
492  for (int i = dim; i > 0; i--)
493  si_dims(i) = si_dims(i-1);
494  si_dims(0) = si_len;
495 
496  si.resize (si_dims, 0.0);
497  }
498  else
499  {
500  si = args(3).xarray_value (x_si_errmsg);
501 
502  if (si.isvector () && x.isvector ())
503  si = si.reshape (dim_vector (si.numel (), 1));
504  }
505 
506  NDArray y (filter (b, a, x, si, dim));
507 
508  retval = ovl (y, si);
509  }
510  }
511 
512  return retval;
513 }
514 
515 template MArray<double>
517  MArray<double>&, int dim);
518 
519 template MArray<double>
521 
522 template MArray<Complex>
524  MArray<Complex>&, int dim);
525 
526 template MArray<Complex>
528 
529 template MArray<float>
531  MArray<float>&, int dim);
532 
533 template MArray<float>
535 
536 template MArray<FloatComplex>
538  MArray<FloatComplex>&, int dim);
539 
540 template MArray<FloatComplex>
542  int dim);
543 
544 /*
545 %!shared a, b, x, r
546 %!test
547 %! a = [1 1];
548 %! b = [1 1];
549 %! x = zeros (1,10); x(1) = 1;
550 %! assert (filter (b, [1], x ), [1 1 0 0 0 0 0 0 0 0]);
551 %! assert (filter (b, [1], x.'), [1 1 0 0 0 0 0 0 0 0].');
552 %! assert (filter (b.', [1], x ), [1 1 0 0 0 0 0 0 0 0] );
553 %! assert (filter (b.', [1], x.'), [1 1 0 0 0 0 0 0 0 0].');
554 %! assert (filter ([1], a, x ), [+1 -1 +1 -1 +1 -1 +1 -1 +1 -1] );
555 %! assert (filter ([1], a, x.'), [+1 -1 +1 -1 +1 -1 +1 -1 +1 -1].');
556 %! assert (filter ([1], a.', x ), [+1 -1 +1 -1 +1 -1 +1 -1 +1 -1] );
557 %! assert (filter ([1], a.', x.'), [+1 -1 +1 -1 +1 -1 +1 -1 +1 -1].');
558 %! assert (filter (b, a, x ), [1 0 0 0 0 0 0 0 0 0] );
559 %! assert (filter (b.', a, x ), [1 0 0 0 0 0 0 0 0 0] );
560 %! assert (filter (b, a.', x ), [1 0 0 0 0 0 0 0 0 0] );
561 %! assert (filter (b.', a, x ), [1 0 0 0 0 0 0 0 0 0] );
562 %! assert (filter (b, a, x.'), [1 0 0 0 0 0 0 0 0 0].');
563 %! assert (filter (b.', a, x.'), [1 0 0 0 0 0 0 0 0 0].');
564 %! assert (filter (b, a.', x.'), [1 0 0 0 0 0 0 0 0 0].');
565 %! assert (filter (b.', a, x.'), [1 0 0 0 0 0 0 0 0 0].');
566 
567 %!test
568 %! r = sqrt (1/2) * (1+i);
569 %! a = a*r;
570 %! b = b*r;
571 %! assert (filter (b, [1], x ), r*[1 1 0 0 0 0 0 0 0 0] );
572 %! assert (filter (b, [1], r*x ), r*r*[1 1 0 0 0 0 0 0 0 0] );
573 %! assert (filter (b, [1], x.' ), r*[1 1 0 0 0 0 0 0 0 0].' );
574 %! assert (filter (b, a, x ), [1 0 0 0 0 0 0 0 0 0] );
575 %! assert (filter (b, a, r*x ), r*[1 0 0 0 0 0 0 0 0 0] );
576 
577 %!shared a, b, x, y, so
578 %!test
579 %! a = [1,1];
580 %! b = [1,1];
581 %! x = zeros (1,10); x(1) = 1;
582 %! [y, so] = filter (b, [1], x, [-1]);
583 %! assert (y, [0 1 0 0 0 0 0 0 0 0]);
584 %! assert (so, 0);
585 
586 %!test
587 %! x = zeros (10,3); x(1,1) = -1; x(1,2) = 1;
588 %! y0 = zeros (10,3); y0(1:2,1) = -1; y0(1:2,2) = 1;
589 %! y = filter (b, [1], x);
590 %! assert (y, y0);
591 
592 %!test
593 %! a = [1,1];
594 %! b=[1,1];
595 %! x = zeros (4,4,2); x(1,1:4,1) = +1; x(1,1:4,2) = -1;
596 %! y0 = zeros (4,4,2); y0(1:2,1:4,1) = +1; y0(1:2,1:4,2) = -1;
597 %! y = filter (b, [1], x);
598 %! assert (y, y0);
599 
600 %!assert (filter (1, ones (10,1) / 10, []), [])
601 %!assert (filter (1, ones (10,1) / 10, zeros (0,10)), zeros (0,10))
602 %!assert (filter (1, ones (10,1) / 10, single (1:5)), repmat (single (10), 1, 5))
603 
604 ## Test using initial conditions
605 %!assert (filter ([1, 1, 1], [1, 1], [1 2], [1, 1]), [2 2])
606 %!assert (filter ([1, 1, 1], [1, 1], [1 2], [1, 1]'), [2 2])
607 %!assert (filter ([1, 3], [1], [1 2; 3 4; 5 6], [4, 5]), [5 7; 6 10; 14 18])
608 %!error (filter ([1, 3], [1], [1 2; 3 4; 5 6], [4, 5]'))
609 %!assert (filter ([1, 3, 2], [1], [1 2; 3 4; 5 6], [1 0 0; 1 0 0], 2), [2 6; 3 13; 5 21])
610 
611 ## Test of DIM parameter
612 %!test
613 %! x = ones (2, 1, 3, 4);
614 %! x(1,1,:,:) = [1 2 3 4; 5 6 7 8; 9 10 11 12];
615 %! y0 = [1 1 6 2 15 3 2 1 8 2 18 3 3 1 10 2 21 3 4 1 12 2 24 3];
616 %! y0 = reshape (y0, size (x));
617 %! y = filter ([1 1 1], 1, x, [], 3);
618 %! assert (y, y0);
619 */
void resize(const dim_vector &dv, const T &rfv)
Size of the specified dimension.
Definition: Array.cc:1011
octave_idx_type numel(void) const
Number of elements in the array.
Definition: Array.h:377
bool isvector(void) const
Size of the specified dimension.
Definition: Array.h:574
const T * data(void) const
Size of the specified dimension.
Definition: Array.h:581
const dim_vector & dims(void) const
Return a const-reference so that dims ()(i) works efficiently.
Definition: Array.h:453
const T * fortran_vec(void) const
Size of the specified dimension.
Definition: Array.h:583
Template for N-dimensional array classes with like-type math operators.
Definition: MArray.h:63
MArray< T > reshape(const dim_vector &new_dims) const
Definition: MArray.h:93
Vector representing the dimensions (size) of an Array.
Definition: dim-vector.h:95
octave_idx_type numel(int n=0) const
Number of elements that a matrix with this dimensions would have.
Definition: dim-vector.h:401
int first_non_singleton(int def=0) const
Definition: dim-vector.h:510
octave_idx_type ndims(void) const
Number of dimensions.
Definition: dim-vector.h:334
OCTINTERP_API void print_usage(void)
Definition: defun.cc:53
#define DEFUN(name, args_name, nargout_name, doc)
Macro to define a builtin function.
Definition: defun.h:56
void error(const char *fmt,...)
Definition: error.cc:968
MArray< T > filter(MArray< T > &b, MArray< T > &a, MArray< T > &x, MArray< T > &si, int dim=0)
Definition: filter.cc:46
double norm(const ColumnVector &v)
Definition: graphics.cc:5893
F77_RET_T const F77_DBLE * x
double psi(double z)
Definition: lo-specfun.cc:2099
octave_value::octave_value(const Array< char > &chm, char type) return retval
Definition: ov.cc:811
octave_value_list ovl(const OV_Args &... args)
Construct an octave_value_list with less typing.
Definition: ovl.h:211