def _sparse_random_matrix(self, n_features: int) -> torch.Tensor:
        """Generate a sparse random matrix for projection.

        Implements the sparse random matrix generation described in [1]_.

        Args:
            n_features (int): Dimensionality of the original source space.

        Returns:
            torch.Tensor: Sparse matrix of shape ``(n_components, n_features)``.
                The matrix is stored in dense format for GPU compatibility.

        References:
            .. [1] P. Li, T. Hastie and K. Church, "Very Sparse Random
               Projections," KDD '06, 2006.
        """
        # Density 'auto'. Factorize density
        density = 1 / np.sqrt(n_features)

        if density == 1:
            # skip index generation if totally dense
            binomial = torch.distributions.Binomial(total_count=1, probs=0.5)
            components = binomial.sample((self.n_components, n_features)) * 2 - 1
            components = 1 / np.sqrt(self.n_components) * components

        else:
            # Sparse matrix is not being generated here as it is stored as dense anyways
            components = torch.zeros((self.n_components, n_features), dtype=torch.float32)
            for i in range(self.n_components):
                # find the indices of the non-zero components for row i
                nnz_idx = torch.distributions.Binomial(total_count=n_features, probs=density).sample()
                # get nnz_idx column indices
                # pylint: disable=not-callable
                c_idx = torch.tensor(
                    sample_without_replacement(
                        n_population=n_features,
                        n_samples=nnz_idx,
                        random_state=self.random_state,
                    ),
                    dtype=torch.int32,
                )
                data = torch.distributions.Binomial(total_count=1, probs=0.5).sample(sample_shape=c_idx.size()) * 2 - 1
                # assign data to only those columns
                components[i, c_idx] = data

            components *= np.sqrt(1 / density) / np.sqrt(self.n_components)

        return components
Generate a sparse random matrix for projection.

        Implements the sparse random matrix generation described in [1]_.

        Args:
            n_features (int): Dimensionality of the original source space.

        Returns:
            torch.Tensor: Sparse matrix of shape ``(n_components, n_features)``.
                The matrix is stored in dense format for GPU compatibility.

        References:
            .. [1] P. Li, T. Hastie and K. Church, "Very Sparse Random
               Projections," KDD '06, 2006.
        
src/anomalib/models/components/dimensionality_reduction/random_projection.py