View all functions

CategoryMath: Signal
GPUYes

What does the filter function do in MATLAB / RunMat?

filter evaluates the causal linear difference equation

a(1) * y(n) = b(1) * x(n) + b(2) * x(n-1) + … + b(nb) * x(n-nb+1)
              - a(2) * y(n-1) - … - a(na) * y(n-na+1)

with MATLAB-compatible handling of scalars, row/column vectors, N-D tensors, complex numbers, and user-supplied initial conditions. RunMat normalises the coefficients internally so that a(1) becomes 1, mirroring MATLAB.

How does the filter function behave in MATLAB / RunMat?

  • filter(b, a, X) applies the filter along the first non-singleton dimension of X (column-wise for matrices).
  • The numerator b and denominator a must be non-empty vectors. a(1) must be non-zero; the runtime divides both coefficient vectors by a(1) automatically.
  • Optional initial conditions zi must have the same size as the output zf (the dimension being filtered is replaced by max(numel(b), numel(a)) - 1). Use [] to indicate zero initial state.
  • filter(b, a, X, zi, dim) processes X along dimension dim. Dimensions beyond ndims(X) are treated as length-one axes, exactly as in MATLAB.
  • Complex coefficients or inputs are supported; results follow MATLAB semantics for real/imaginary propagation.
  • The function returns a single output y by default. Request two outputs ([y, zf] = filter(...)) to obtain the final internal states zf.

filter Function GPU Execution Behaviour

When RunMat Accelerate is active, the runtime dispatches to the provider hook iir_filter whenever:

  • The input signal already resides on the GPU (for example, via gpuArray) and all operands are real-valued.
  • The active provider implements iir_filter (both the in-process provider and the WGPU backend do).

Under those conditions the entire evaluation—including final-state propagation—stays on the device. If the hook is absent, or if any operand is complex, RunMat transparently gathers to the host, evaluates the filter with the scalar reference implementation, and returns MATLAB-compatible results. Auto-offload and fusion residency reuse the same hook, so high-level code does not need to differentiate between CPU and GPU execution.

Examples of using the filter function in MATLAB / RunMat

Smoothing a signal with an FIR moving-average filter

b = ones(1, 3) / 3;
a = 1;
x = [1 5 2 0 3];
y = filter(b, a, x)

Expected output:

y =
    0.3333    2.0000    2.6667    2.3333    1.6667

Applying a first-order IIR low-pass filter

alpha = 0.8;
b = 1 - alpha;
a = [1 -alpha];
imp = [1 zeros(1, 4)];
y = filter(b, a, imp)

Expected output:

y =
    0.2000    0.1600    0.1280    0.1024    0.0819

Continuing a filtered stream with explicit initial conditions

b = ones(1, 3) / 3;
a = 1;
x1 = [1 5 2];
[y1, zf] = filter(b, a, x1);   % First chunk

x2 = [0 3];
[y2, zf2] = filter(b, a, x2, zf);  % Resume with stored state

Expected output:

y1 = [0.3333 2.0000 2.6667]
zf  = [2.3333 0.6667]

y2 = [2.3333 1.6667]
zf2 = [1.0000 1.0000]

Filtering along matrix rows (dimension 2)

X = [1  2  3  4;
     0  1  0  1];
b = [1 -1];   % simple differentiator
a = 1;
Y = filter(b, a, X, [], 2)

Expected output:

Y =
     1     1     1     1
     0     1    -1     1

Filtering complex-valued data

b = [1 1i];
a = 1;
t = 0:3;
x = exp(1i * pi/4 * t);
y = filter(b, a, x)

Expected output (RunMat prints a + bi):

y =
   1.0000 + 0.0000i   0.7071 + 1.7071i  -0.7071 + 1.7071i  -1.7071 + 0.7071i

Executing a real filter on the GPU

g = gpuArray([1 5 2 0 3]);
b = ones(1, 3) / 3;
a = 1;
[y_gpu, zf_gpu] = filter(b, a, g);
y = gather(y_gpu)

Expected output matches the CPU example:

y =
    0.3333    2.0000    2.6667    2.3333    1.6667

FAQ

What happens if a(1) is not 1.0?

RunMat (like MATLAB) divides every coefficient in a and b by a(1) internally so that a(1) becomes 1. You only need to ensure a(1) is non-zero.

How large should zi be?

zi must have the same size as zf (the returned final state). Replace the filtered dimension with max(numel(b), numel(a)) - 1; all other dimensions match X. Use [] for a zero state.

Can I filter along a dimension greater than ndims(X)?

Yes. Just like MATLAB, RunMat treats missing higher dimensions as length-one axes. filter(b, a, X, [], 5) is valid even if X is a vector.

Does filter support complex coefficients or signals?

Absolutely. The host implementation handles complex arithmetic exactly. GPU acceleration is currently limited to real-valued filters; complex inputs fall back to the CPU automatically.

Do I need to initialise the GPU path manually?

No. If the signal is a gpuArray value and the active provider supports iir_filter, RunMat keeps the entire computation on the device. Otherwise, the runtime gathers operands to the host, ensuring MATLAB-compatible results without extra user code.

What shape does zf have?

zf matches zi (or the default zero state). The filtered dimension is replaced by max(numel(b), numel(a)) - 1; other dimensions mirror the input.

How are empty signals handled?

When X is empty, y is empty and zf equals the supplied zi (or zeros). No arithmetic is performed, matching MATLAB.

Are logical and integer inputs supported?

Yes. They are promoted to double precision before filtering, exactly as in MATLAB.

How do I resume filtering a long stream chunk-by-chunk?

Call [y, zf] = filter(...) on each chunk and pass zf as the fourth argument for the next chunk. The example above shows the pattern.

Can I accelerate filtering with the planner’s auto-offload?

The planner leverages the same provider hook under the hood. As long as the operands are real and the provider implements iir_filter, auto-offload keeps the filter on the GPU.

See Also

conv, deconv, fft, filtfilt