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