def filter(
        self,
        series: TimeSeries,
        covariates: Optional[TimeSeries] = None,
        num_samples: int = 1,
    ) -> TimeSeries:
        """
        Sequentially applies the Kalman filter on the provided series of observations.

        Parameters
        ----------
        series : TimeSeries
            The series of outputs (observations) used to infer the underlying outputs according to the specified Kalman
            process. This must be a deterministic series (containing one sample).
        covariates : Optional[TimeSeries]
            An optional series of inputs (control signal), necessary if the Kalman filter was initialized with
            covariates. This must be a deterministic series (containing one sample).
        num_samples : int, default: 1
            The number of samples to generate from the inferred distribution of the output z. If this is set to 1, the
            output is a `TimeSeries` containing a single sample using the mean of the distribution.

        Returns
        -------
        TimeSeries
            A (stochastic) `TimeSeries` of the inferred output z, of the same width as the input series.
        """
        super().filter(series)

        raise_if(
            self.kf is None,
            "The Kalman filter has not been fitted yet. Call `fit()` first "
            "or provide Kalman filter in constructor.",
        )

        raise_if_not(
            series.width == self.dim_y,
            "The provided TimeSeries dimensionality does not match "
            "the output dimensionality of the Kalman filter.",
        )

        raise_if(
            covariates is not None and not self._expect_covariates,
            "Covariates were provided, but the Kalman filter was not fitted with covariates.",
        )

        if self._expect_covariates:
            raise_if(
                covariates is None,
                "The Kalman filter was fitted with covariates, but these were not provided.",
            )

            raise_if_not(
                covariates.is_deterministic,
                "The covariates must be deterministic (observations).",
            )

            covariates = covariates.slice_intersect(series)
            raise_if_not(
                series.has_same_time_as(covariates),
                "The number of timesteps in the series and the covariates must match.",
            )

        kf = deepcopy(self.kf)

        y_values = series.values(copy=False)
        if self._expect_covariates:
            u_values = covariates.values(copy=False)

            # set control signal to 0 if it contains NaNs:
            u_values = np.nan_to_num(u_values, copy=True, nan=0.0)
        else:
            u_values = np.zeros((len(y_values), 0))

        # For each time step, we'll sample "n_samples" from a multivariate Gaussian
        # whose mean vector and covariance matrix come from the Kalman filter.
        sampled_outputs = np.zeros((len(y_values), self.dim_y, num_samples))

        for i in range(len(y_values)):
            y = y_values[i, :].reshape(-1, 1)
            u = u_values[i, :].reshape(-1, 1)

            if np.isnan(y).any():
                y = None

            kf.step(y, u)
            mean_vec = kf.y_filtereds[-1].reshape(
                self.dim_y,
            )

            if num_samples == 1:
                sampled_outputs[i, :, 0] = mean_vec
            else:
                # The measurement covariance matrix is given by the sum of the covariance matrix of the
                # state estimate (transformed by C) and the covariance matrix of the measurement noise.
                cov_matrix = (
                    kf.state_space.c @ kf.p_filtereds[-1] @ kf.state_space.c.T + kf.r
                )
                sampled_outputs[i, :, :] = np.random.multivariate_normal(
                    mean_vec, cov_matrix, size=num_samples
                ).T

        return series.with_values(sampled_outputs)

        Sequentially applies the Kalman filter on the provided series of observations.

        Parameters
        ----------
        series : TimeSeries
            The series of outputs (observations) used to infer the underlying outputs according to the specified Kalman
            process. This must be a deterministic series (containing one sample).
        covariates : Optional[TimeSeries]
            An optional series of inputs (control signal), necessary if the Kalman filter was initialized with
            covariates. This must be a deterministic series (containing one sample).
        num_samples : int, default: 1
            The number of samples to generate from the inferred distribution of the output z. If this is set to 1, the
            output is a `TimeSeries` containing a single sample using the mean of the distribution.

        Returns
        -------
        TimeSeries
            A (stochastic) `TimeSeries` of the inferred output z, of the same width as the input series.
        
darts/models/filtering/kalman_filter.py