Exemple #1
0
def gain(gain_matrix, input_signal):
    """
    Create a signal that represents the product of the given gain matrix and the
    value of the given input signal.

    Args:
        gain_matrix: The gain matrix
        input_signal: The input signal to consider

    Returns:
        A signal that represents the product of the gain matrix and the value of
        the input signal.
    """

    # Determine the shape of the output signal
    if np.isscalar(gain_matrix):
        return Signal(
            shape=input_signal.shape,
            value=partial(_scalar_gain_function, gain_matrix, input_signal),
        )
    else:
        output_shape = (gain_matrix @ np.zeros(input_signal.shape)).shape
        return Signal(
            shape=output_shape,
            value=partial(_gain_function, gain_matrix, input_signal),
        )
Exemple #2
0
def test_sum_signal_gain_mismatch():
    """Test the gain mismatch exception of ``sum_signal``."""

    signal_1 = Signal()
    signal_2 = Signal()

    with pytest.raises(ValueError):
        sum_signal(input_signals=(signal_1, signal_2), gains=(1, 1, 1))
Exemple #3
0
def test_sum_signal_shape_mismatch():
    """Test the shape mismatch exception of ``sum_signal``."""

    signal_1 = Signal()
    signal_2 = Signal(shape=2)

    with pytest.raises(ValueError):
        sum_signal(input_signals=(signal_1, signal_2))
Exemple #4
0
def sum_signal(input_signals, gains=None):
    """
    Create a signal that represents the sum of the input signals multiplied by
    the given gains.

    The signals must have the same shape and there must be exactly as many
    entries in the ``gains`` tuple as there are input signals.

    Args:
        input_signals: A tuple of input signals
        gains:  A tuple of gains for the input signals. Optional: Default value
            is all ones.

    Returns:
        A signal that represents the sum of the input signals
    """

    if gains is None:
        gains = np.ones(len(input_signals))

    shape = input_signals[0].shape
    if any(signal.shape != shape for signal in input_signals):
        raise ValueError("The shapes of the input signals do not match")
    if len(input_signals) != len(gains):
        raise ValueError("There must be as many gains as there are "
                         "input signals")

    return Signal(shape=shape,
                  value=partial(_sum_function, input_signals, gains))
Exemple #5
0
    def __init__(self, parent, channel_weights, output_size=1):
        Block.__init__(self, parent)

        self.channel_weights = np.asarray(channel_weights)
        self.output_size = output_size

        self.inputs = [
            Port(shape=self.output_size)
            for _ in range(self.channel_weights.shape[0])
        ]
        self.output = Signal(shape=self.output_size,
                             value=self.output_function)
def test_empty_system():
    """Test whether the linearization algorithm properly flags a system without
    states and inputs"""

    # Create a system and add a signal to it, so we can check whether the
    # algorithm is actually checking states and inputs instead of signals or
    # outputs
    system = System()
    output_signal = Signal()
    config = LinearizationConfiguration(system)
    OutputDescriptor(config, output_signal)

    with pytest.raises(ValueError):
        system_jacobian(config, single_matrix=False)
Exemple #7
0
def saturation(input_signal, lower_limit, upper_limit):
    """
    Limit a signal to lower and upper limits.

    Args:
        input_signal: The input signal
        lower_limit: The lower limit
        upper_limit: The upper limit

    Returns:
        The output signal with the limited value
    """

    return Signal(shape=input_signal.shape,
                  value=partial(_saturation_function, input_signal,
                                lower_limit, upper_limit))
Exemple #8
0
    def __init__(
        self,
        parent,
        system_matrix,
        input_matrix,
        output_matrix,
        feed_through_matrix,
        initial_condition=None,
    ):
        Block.__init__(self, parent)

        # Determine the number of states and the shape of the state
        if np.isscalar(system_matrix):
            self.state_shape = ()
            num_states = 1
        else:
            system_matrix = np.asarray(system_matrix)
            if (system_matrix.ndim == 2 and system_matrix.shape[0] > 0
                    and system_matrix.shape[0] == system_matrix.shape[1]):
                self.state_shape = system_matrix.shape[0]
                num_states = self.state_shape
            else:
                raise InvalidLTIException("The system matrix must be a scalar "
                                          "or a non-empty square matrix")

        # Determine the number of inputs and the shape of the input signal
        if np.isscalar(input_matrix):
            if num_states > 1:
                raise InvalidLTIException("There is more than one state, but "
                                          "the input matrix is neither empty, "
                                          "nor a vector or a matrix")
            num_inputs = 1
            self.input_shape = ()
        else:
            input_matrix = np.asarray(input_matrix)
            if input_matrix.ndim == 1:
                # The input matrix is a column vector => one input
                if num_states != input_matrix.shape[0]:
                    raise InvalidLTIException(
                        "The height of the input matrix "
                        "must match the number of states")
                num_inputs = 1
            elif input_matrix.ndim == 2:
                # The input matrix is a matrix
                if num_states != input_matrix.shape[0]:
                    raise InvalidLTIException("The height of the input matrix "
                                              "does not match the number of "
                                              "states")
                num_inputs = input_matrix.shape[1]
            else:
                raise InvalidLTIException("The input matrix must be empty,"
                                          "a scalar, a vector or a matrix")
            self.input_shape = num_inputs

        # Determine the number of outputs and the shape of the output array
        if np.isscalar(output_matrix):
            if num_states > 1:
                raise InvalidLTIException("There is more than one state, but "
                                          "the output matrix is neither an "
                                          "empty, a vector nor a matrix")
            num_outputs = 1
            self.output_shape = ()
        else:
            output_matrix = np.asarray(output_matrix)
            if output_matrix.ndim == 1:
                # The output matrix is a row vector => one output
                if num_states != output_matrix.shape[0]:
                    raise InvalidLTIException("The width of the output matrix "
                                              "does not match the number of "
                                              "states")
                num_outputs = 1
            elif output_matrix.ndim == 2:
                # The output matrix is a matrix
                if num_states != output_matrix.shape[1]:
                    raise InvalidLTIException("The width of the output matrix "
                                              "does not match the number of "
                                              "states")
                num_outputs = output_matrix.shape[0]
            else:
                raise InvalidLTIException("The output matrix must be empty, a"
                                          "scalar, a vector or a matrix")
            self.output_shape = num_outputs

        if np.isscalar(feed_through_matrix):
            if not (num_inputs == 1 and num_outputs == 1):
                raise InvalidLTIException("A scalar feed-through matrix is "
                                          "only allowed for systems with "
                                          "exactly one input and one output")
        else:
            feed_through_matrix = np.asarray(feed_through_matrix)
            if feed_through_matrix.ndim == 1:
                # A vector feed_through_matrix is interpreted as row vector,
                # so there must be exactly one output.
                if num_outputs == 0:
                    raise InvalidLTIException("The feed-through matrix for a "
                                              "system without outputs must be"
                                              "empty")
                elif num_outputs > 1:
                    raise InvalidLTIException("The feed-through matrix for a "
                                              "system with more than one "
                                              "output must be a matrix")
                if feed_through_matrix.shape[0] != num_inputs:
                    raise InvalidLTIException(
                        "The width of the feed-through "
                        "matrix must match the number of "
                        "inputs")
            elif feed_through_matrix.ndim == 2:
                if feed_through_matrix.shape[0] != num_outputs:
                    raise InvalidLTIException(
                        "The height of the feed-through "
                        "matrix must match the number of "
                        "outputs")
                if feed_through_matrix.shape[1] != num_inputs:
                    raise InvalidLTIException(
                        "The width of the feed-through "
                        "matrix must match the number of "
                        "inputs")
            else:
                raise InvalidLTIException("The feed-through matrix must be "
                                          "empty, a scalar, a vector or a "
                                          "matrix")

        self.system_matrix = system_matrix
        self.input_matrix = input_matrix
        self.output_matrix = output_matrix
        self.feed_through_matrix = feed_through_matrix

        self.input = Port(shape=self.input_shape)
        self.state = State(
            self,
            shape=self.state_shape,
            derivative_function=self.state_derivative,
            initial_condition=initial_condition,
        )
        self.output = Signal(shape=self.output_shape,
                             value=self.output_function)
Exemple #9
0
    def __init__(self, parent, k):
        Block.__init__(self, parent)
        self.k = np.atleast_2d(k)

        self.input = Port(shape=self.k.shape[0])
        self.output = Signal(shape=self.k.shape[1], value=self.output_function)