<commit_before>"""xferfcn.py

Transfer function representation and functions.

This file contains the TransferFunction class and also functions
that operate on transfer functions.  This is the primary representation
for the python-control library.
"""

# Python 3 compatibility (needs to go here)
from __future__ import print_function
from __future__ import division

"""Copyright (c) 2010 by California Institute of Technology
All rights reserved.

Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions
are met:

1. Redistributions of source code must retain the above copyright
   notice, this list of conditions and the following disclaimer.

2. Redistributions in binary form must reproduce the above copyright
   notice, this list of conditions and the following disclaimer in the
   documentation and/or other materials provided with the distribution.

3. Neither the name of the California Institute of Technology nor
   the names of its contributors may be used to endorse or promote
   products derived from this software without specific prior
   written permission.

THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
FOR A PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL CALTECH
OR THE CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
SUCH DAMAGE.

Author: <NAME>
Date: 24 May 09
Revised: <NAME>, Dec 10

$Id$

"""

# External function declarations
import numpy as np
from numpy import angle, array, empty, finfo, ndarray, ones, \
    polyadd, polymul, polyval, roots, sqrt, zeros, squeeze, exp, pi, \
    where, delete, real, poly, nonzero
import scipy as sp
from scipy.signal import lti, tf2zpk, zpk2tf, cont2discrete
from copy import deepcopy
from warnings import warn
from itertools import chain
from re import sub
from .lti import LTI, timebaseEqual, timebase, isdtime
from . import config

__all__ = ['TransferFunction', 'tf', 'ss2tf', 'tfdata']


# Define module default parameter values
_xferfcn_defaults = {
    'xferfcn.default_dt': None}

class TransferFunction(LTI):

    """TransferFunction(num, den[, dt])

    A class for representing transfer functions

    The TransferFunction class is used to represent systems in transfer
    function form.

    The main data members are 'num' and 'den', which are 2-D lists of arrays
    containing MIMO numerator and denominator coefficients.  For example,

    >>> num[2][5] = numpy.array([1., 4., 8.])

    means that the numerator of the transfer function from the 6th input to the
    3rd output is set to s^2 + 4s + 8.

    Discrete-time transfer functions are implemented by using the 'dt'
    instance variable and setting it to something other than 'None'.  If 'dt'
    has a non-zero value, then it must match whenever two transfer functions
    are combined.  If 'dt' is set to True, the system will be treated as a
    discrete time system with unspecified sampling time. The default value of 
    'dt' is None and can be changed by changing the value of 
    ``control.config.defaults['xferfcn.default_dt']``.

    The TransferFunction class defines two constants ``s`` and ``z`` that
    represent the differentiation and delay operators in continuous and
    discrete time.  These can be used to create variables that allow algebraic
    creation of transfer functions.  For example,

    >>> s = TransferFunction.s
    >>> G  = (s + 1)/(s**2 + 2*s + 1)

    """
    def __init__(self, *args):
        """TransferFunction(num, den[, dt])

        Construct a transfer function.

        The default constructor is TransferFunction(num, den), where num and
        den are lists of lists of arrays containing polynomial coefficients.
        To create a discrete time transfer funtion, use TransferFunction(num,
        den, dt) where 'dt' is the sampling time (or True for unspecified
        sampling time).  To call the copy constructor, call
        TransferFunction(sys), where sys is a TransferFunction object
        (continuous or discrete).

        """
        args = deepcopy(args)
        if len(args) == 2:
            # The user provided a numerator and a denominator.
            (num, den) = args
            dt = config.defaults['xferfcn.default_dt']
        elif len(args) == 3:
            # Discrete time transfer function
            (num, den, dt) = args
        elif len(args) == 1:
            # Use the copy constructor.
            if not isinstance(args[0], TransferFunction):
                raise TypeError("The one-argument constructor can only take \
                        in a TransferFunction object.  Received %s."
                                % type(args[0]))
            num = args[0].num
            den = args[0].den
            # TODO: not sure this can ever happen since dt is always present
            try:
                dt = args[0].dt
            except NameError:   # pragma: no coverage
                dt = config.defaults['xferfcn.default_dt']
        else:
            raise ValueError("Needs 1, 2 or 3 arguments; received %i."
                             % len(args))

        num = _clean_part(num)
        den = _clean_part(den)

        inputs = len(num[0])
        outputs = len(num)

        # Make sure numerator and denominator matrices have consistent sizes
        if inputs != len(den[0]):
            raise ValueError(
                "The numerator has %i input(s), but the denominator has "
                "%i input(s)." % (inputs, len(den[0])))
        if outputs != len(den):
            raise ValueError(
                "The numerator has %i output(s), but the denominator has "
                "%i output(s)." % (outputs, len(den)))

        # Additional checks/updates on structure of the transfer function
        for i in range(outputs):
            # Make sure that each row has the same number of columns
            if len(num[i]) != inputs:
                raise ValueError(
                    "Row 0 of the numerator matrix has %i elements, but row "
                    "%i has %i." % (inputs, i, len(num[i])))
            if len(den[i]) != inputs:
                raise ValueError(
                    "Row 0 of the denominator matrix has %i elements, but row "
                    "%i has %i." % (inputs, i, len(den[i])))

            # Check for zeros in numerator or denominator
            # TODO: Right now these checks are only done during construction.
            # It might be worthwhile to think of a way to perform checks if the
            # user modifies the transfer function after construction.
            for j in range(inputs):
                # Check that we don't have any zero denominators.
                zeroden = True
                for k in den[i][j]:
                    if k:
                        zeroden = False
                        break
                if zeroden:
                    raise ValueError(
                        "Input %i, output %i has a zero denominator."
                        % (j + 1, i + 1))

                # If we have zero numerators, set the denominator to 1.
                zeronum = True
                for k in num[i][j]:
                    if k:
                        zeronum = False
                        break
                if zeronum:
                    den[i][j] = ones(1)

        LTI.__init__(self, inputs, outputs, dt)
        self.num = num
        self.den = den

        self._truncatecoeff()

    def __call__(self, x, squeeze=True):
        """Evaluate system's transfer function at complex frequencies.
        
        Returns the complex frequency response `sys(x)` where `x` is `s` for 
        continuous-time systems and `z` for discrete-time systems. 

        To evaluate at a frequency omega in radians per second, enter 
        x = omega*1j, for continuous-time systems, or x = exp(1j*omega*dt) for 
        discrete-time systems. 

        Parameters
        ----------
        x: complex array_like or complex
            Complex frequencies
        squeeze: bool, optional (default=True)
            If True and sys is single input single output (SISO), returns a 
            1D array or scalar depending on the length of x. 
            
        Returns
        -------
        fresp : (self.outputs, self.inputs, len(x)) or len(x) complex ndarray
            The frequency response of the system. Array is len(x) if and only if<commit_msg>Update control/xferfcn.py<commit_after>"""xferfcn.py

Transfer function representation and functions.

This file contains the TransferFunction class and also functions
that operate on transfer functions.  This is the primary representation
for the python-control library.
"""

# Python 3 compatibility (needs to go here)
from __future__ import print_function
from __future__ import division

"""Copyright (c) 2010 by California Institute of Technology
All rights reserved.

Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions
are met:

1. Redistributions of source code must retain the above copyright
   notice, this list of conditions and the following disclaimer.

2. Redistributions in binary form must reproduce the above copyright
   notice, this list of conditions and the following disclaimer in the
   documentation and/or other materials provided with the distribution.

3. Neither the name of the California Institute of Technology nor
   the names of its contributors may be used to endorse or promote
   products derived from this software without specific prior
   written permission.

THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
FOR A PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL CALTECH
OR THE CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
SUCH DAMAGE.

Author: <NAME>
Date: 24 May 09
Revised: <NAME>, Dec 10

$Id$

"""

# External function declarations
import numpy as np
from numpy import angle, array, empty, finfo, ndarray, ones, \
    polyadd, polymul, polyval, roots, sqrt, zeros, squeeze, exp, pi, \
    where, delete, real, poly, nonzero
import scipy as sp
from scipy.signal import lti, tf2zpk, zpk2tf, cont2discrete
from copy import deepcopy
from warnings import warn
from itertools import chain
from re import sub
from .lti import LTI, timebaseEqual, timebase, isdtime
from . import config

__all__ = ['TransferFunction', 'tf', 'ss2tf', 'tfdata']


# Define module default parameter values
_xferfcn_defaults = {
    'xferfcn.default_dt': None}

class TransferFunction(LTI):

    """TransferFunction(num, den[, dt])

    A class for representing transfer functions

    The TransferFunction class is used to represent systems in transfer
    function form.

    The main data members are 'num' and 'den', which are 2-D lists of arrays
    containing MIMO numerator and denominator coefficients.  For example,

    >>> num[2][5] = numpy.array([1., 4., 8.])

    means that the numerator of the transfer function from the 6th input to the
    3rd output is set to s^2 + 4s + 8.

    Discrete-time transfer functions are implemented by using the 'dt'
    instance variable and setting it to something other than 'None'.  If 'dt'
    has a non-zero value, then it must match whenever two transfer functions
    are combined.  If 'dt' is set to True, the system will be treated as a
    discrete time system with unspecified sampling time. The default value of 
    'dt' is None and can be changed by changing the value of 
    ``control.config.defaults['xferfcn.default_dt']``.

    The TransferFunction class defines two constants ``s`` and ``z`` that
    represent the differentiation and delay operators in continuous and
    discrete time.  These can be used to create variables that allow algebraic
    creation of transfer functions.  For example,

    >>> s = TransferFunction.s
    >>> G  = (s + 1)/(s**2 + 2*s + 1)

    """
    def __init__(self, *args):
        """TransferFunction(num, den[, dt])

        Construct a transfer function.

        The default constructor is TransferFunction(num, den), where num and
        den are lists of lists of arrays containing polynomial coefficients.
        To create a discrete time transfer funtion, use TransferFunction(num,
        den, dt) where 'dt' is the sampling time (or True for unspecified
        sampling time).  To call the copy constructor, call
        TransferFunction(sys), where sys is a TransferFunction object
        (continuous or discrete).

        """
        args = deepcopy(args)
        if len(args) == 2:
            # The user provided a numerator and a denominator.
            (num, den) = args
            dt = config.defaults['xferfcn.default_dt']
        elif len(args) == 3:
            # Discrete time transfer function
            (num, den, dt) = args
        elif len(args) == 1:
            # Use the copy constructor.
            if not isinstance(args[0], TransferFunction):
                raise TypeError("The one-argument constructor can only take \
                        in a TransferFunction object.  Received %s."
                                % type(args[0]))
            num = args[0].num
            den = args[0].den
            # TODO: not sure this can ever happen since dt is always present
            try:
                dt = args[0].dt
            except NameError:   # pragma: no coverage
                dt = config.defaults['xferfcn.default_dt']
        else:
            raise ValueError("Needs 1, 2 or 3 arguments; received %i."
                             % len(args))

        num = _clean_part(num)
        den = _clean_part(den)

        inputs = len(num[0])
        outputs = len(num)

        # Make sure numerator and denominator matrices have consistent sizes
        if inputs != len(den[0]):
            raise ValueError(
                "The numerator has %i input(s), but the denominator has "
                "%i input(s)." % (inputs, len(den[0])))
        if outputs != len(den):
            raise ValueError(
                "The numerator has %i output(s), but the denominator has "
                "%i output(s)." % (outputs, len(den)))

        # Additional checks/updates on structure of the transfer function
        for i in range(outputs):
            # Make sure that each row has the same number of columns
            if len(num[i]) != inputs:
                raise ValueError(
                    "Row 0 of the numerator matrix has %i elements, but row "
                    "%i has %i." % (inputs, i, len(num[i])))
            if len(den[i]) != inputs:
                raise ValueError(
                    "Row 0 of the denominator matrix has %i elements, but row "
                    "%i has %i." % (inputs, i, len(den[i])))

            # Check for zeros in numerator or denominator
            # TODO: Right now these checks are only done during construction.
            # It might be worthwhile to think of a way to perform checks if the
            # user modifies the transfer function after construction.
            for j in range(inputs):
                # Check that we don't have any zero denominators.
                zeroden = True
                for k in den[i][j]:
                    if k:
                        zeroden = False
                        break
                if zeroden:
                    raise ValueError(
                        "Input %i, output %i has a zero denominator."
                        % (j + 1, i + 1))

                # If we have zero numerators, set the denominator to 1.
                zeronum = True
                for k in num[i][j]:
                    if k:
                        zeronum = False
                        break
                if zeronum:
                    den[i][j] = ones(1)

        LTI.__init__(self, inputs, outputs, dt)
        self.num = num
        self.den = den

        self._truncatecoeff()

    def __call__(self, x, squeeze=True):
        """Evaluate system's transfer function at complex frequencies.
        
        Returns the complex frequency response `sys(x)` where `x` is `s` for 
        continuous-time systems and `z` for discrete-time systems. 

        To evaluate at a frequency omega in radians per second, enter 
        ``x = omega*1j``, for continuous-time systems, or ``x = exp(1j*omega*dt)``
        for discrete-time systems.

        Parameters
        ----------
        x: complex array_like or complex
            Complex frequencies
        squeeze: bool, optional (default=True)
            If True and sys is single input single output (SISO), returns a 
            1D array or scalar depending on the length of x. 
            
        Returns
        -------
        fresp : (self.outputs, self.inputs, len(x)) or len(x) complex ndarray
            The frequency response of the system. Array is len(x) if and only if
{'fraction_non_alphanumeric': 0.07233615693267945, 'fraction_numerical': 0.006241640659830584, 'mean_word_length': 2.2520840884378397, 'pattern_counts': {'":': 0, '<': 7, '<?xml version=': 0, '>': 25, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 2, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '3172599', 'n_tokens_mistral': 4751, 'n_tokens_neox': 4403, 'n_words': 2094}