Repository: sympy/sympy
File: sympy/physics/control/lti.py (Python)

Code before:

        >>> from sympy import Matrix
        >>> from sympy.physics.control import StateSpace
        >>> A = Matrix([[-1.5, -2], [1, 0]])
        >>> B = Matrix([0.5, 0])
        >>> C = Matrix([[0, 1]])
        >>> D = Matrix([1])
        >>> ss = StateSpace(A, B, C, D)
        >>> ob_subspace = ss.observable_subspace()
        >>> ob_subspace
        [Matrix([
        [1],
        [0]]), Matrix([
        [0],
        [1]])]

        """
        M = Matrix.hstack(*self.unobservable_subspace())
        if len(M.free_symbols) > 0:
                raise NotImplementedError(
                    "Observable subspace cannot be determined for " \
                    "symbolic matrices."
                )
        return self._calc_orthogonal_complement(M, self._A.shape[0])

    def _get_full_rank_conditions(self, M):
        rows, cols = M.shape

        if rows == 0 or cols == 0:
            return []

        square_size = min(rows, cols)
        determinant_conditions = []

        num_iterations = max(rows, cols) - square_size + 1

        vertical_scan = rows >= cols
        for i in range(num_iterations):
            if vertical_scan:
                submatrix = M[i:i + square_size, :]
            else:
                submatrix = M[:, i:i + square_size]

            determinant_conditions.append(Unequality(submatrix.det(), 0))

        rank_evaluation = fuzzy_or(determinant_conditions)

        if rank_evaluation is False:
            # if every determinants are zero numerically, the system is not
            # full rank
            return False

Review comment (suggestion):
```suggestion
        Returns conditions for the state space model to be observable or True/False if the conditions can be reduced.
```

Code after:
        >>> from sympy.physics.control import StateSpace
        >>> A = Matrix([[-1.5, -2], [1, 0]])
        >>> B = Matrix([0.5, 0])
        >>> C = Matrix([[0, 1]])
        >>> D = Matrix([1])
        >>> ss = StateSpace(A, B, C, D)
        >>> ob_subspace = ss.observable_subspace()
        >>> ob_subspace
        [Matrix([
        [1],
        [0]]), Matrix([
        [0],
        [1]])]

        """
        M = Matrix.hstack(*self.unobservable_subspace())
        if len(M.free_symbols) > 0:
                raise NotImplementedError(
                    "Observable subspace cannot be determined for " \
                    "symbolic matrices."
                )
        return self._calc_orthogonal_complement(M, self._A.shape[0])

    def is_observable(self):
        """
        Returns conditions for the state space model to be observable.

        Examples
        ========

        >>> from sympy import Matrix
        >>> from sympy.physics.control import StateSpace
        >>> A1 = Matrix([[-1.5, -2], [1, 0]])
        >>> B1 = Matrix([0.5, 0])
        >>> C1 = Matrix([[0, 1]])
        >>> D1 = Matrix([1])
        >>> ss1 = StateSpace(A1, B1, C1, D1)
        >>> ss1.is_observable()
        True

        """
        return self.observability_matrix().rank() == self.num_states

    def controllability_matrix(self):
        """
        Returns the controllability matrix of the system:
            [B, A * B, A^2 * B, .. , A^(n-1) * B]; A in R^(n x n), B in R^(n x m)

        Examples
        ========