Exemple #1
0
def __dmddiff__(X, dt, params, options={}):
    '''
    Fit the timeseries with optimized dynamic mode decomposition model.
    
    Inputs
    ------
    X       : (np.matrix of floats, NxM) hankel matrix of time delay embedded time series to differentiate. 
                                         N = time steps, M = time delay embedding
    dt      : (float) time step

    Parameters
    ----------
    params  : (list)  [delay_embedding, : (int)  amount of delay_embedding 
                       svd_rank]        : (int)  rank trunkcation
    options : (dict) {}

    Outputs
    -------
    x_hat    : estimated (smoothed) x
    dxdt_hat : estimated derivative of x

    '''
    delay_embedding = params[0]
    svd_rank = params[1]

    if len(params) == 2:  # not sliding:
        L = X.shape[0] + delay_embedding
    else:  # sliding
        L = X.shape[0]

    X = X.T

    Ue = np.zeros_like(X)

    # DMD
    dmdc = pydmd.dmdc.DMDc(svd_rank=svd_rank, opt=True)
    dmdc.fit(np.array(X), np.array(Ue[:, 1:]), B=np.eye(X.shape[0]))

    # DMD reconstruction
    x0 = X[:, 0]
    Xr = dmdc.forward_backward_reconstruct(x0, L, 0, overlap=2)

    integral_x_hat = np.real(np.ravel(Xr[0, :]))
    integral_x_hat, x_hat = finite_difference(integral_x_hat, dt)
    x_hat, dxdt_hat = finite_difference(x_hat, dt)

    return x_hat, dxdt_hat
def butterdiff(x, dt, params, options={}):
    """
    Perform butterworth smoothing on x with scipy.signal.filtfilt
    followed by first order finite difference

    :param x: array of time series to differentiate
    :type x: np.array (float)

    :param dt: time step size
    :type dt: float

    :param params: [n, wn], n  = order of the filter; wn = Cutoff frequency.
                    For a discrete timeseries, the value is normalized to the range 0-1,
                    where 1 is the Nyquist frequency.

    :type params: list (int)

    :param options: an empty dictionary or a dictionary with 2 key value pair

                    - 'iterate': whether to run multiple iterations of the smoother. Note: iterate does nothing for median smoother.
                    - 'padmethod': "pad" or "gust", see scipy.signal.filtfilt

    :type options: dict {'iterate': (boolean), 'padmethod': string}

    :return: a tuple consisting of:

            - x_hat: estimated (smoothed) x
            - dxdt_hat: estimated derivative of x


    :rtype: tuple -> (np.array, np.array)
    """
    if 'iterate' in options.keys() and options['iterate'] is True:
        n, wn, iterations = params
    else:
        iterations = 1
        n, wn = params

    b, a = scipy.signal.butter(n, wn)

    x_hat = x
    for _ in range(iterations):
        if len(x) < 9:
            x_hat = scipy.signal.filtfilt(b,
                                          a,
                                          x_hat,
                                          method="pad",
                                          padlen=len(x) - 1)
        else:
            x_hat = scipy.signal.filtfilt(b, a, x_hat, method="pad")

    x_hat, dxdt_hat = finite_difference(x_hat, dt)

    offset = np.mean(x) - np.mean(x_hat)
    x_hat = x_hat + offset

    return x_hat, dxdt_hat
def butterdiff(x, dt, params, options={'iterate': False}):
    '''
    Perform butterworth smoothing on x with scipy.signal.filtfilt
    followed by first order finite difference

    Inputs
    ------
    x       : (np.array of floats, 1xN) time series to differentiate
    dt      : (float) time step

    Parameters
    ----------
    params  : (list) [n, wn], n  = order of the filter; 
                              wn = Cutoff frequency. For a discrete timeseries, 
                                   the value is normalized to the range 0-1, 
                                   where 1 is the Nyquist frequency.
                     or if 'iterate' in options: [n, wn, num_iterations]
    options : (dict) {}
                     or {'iterate'  : True,      to run multiple iterations of the smoother
                         'padmethod': "pad"}     "pad" or "gust", see scipy.signal.filtfilt

    Outputs
    -------
    x_hat    : estimated (smoothed) x
    dxdt_hat : estimated derivative of x
    
    '''
    if 'iterate' in options.keys() and options['iterate'] is True:
        n, wn, iterations = params
    else:
        iterations = 1
        n, wn = params

    b, a = scipy.signal.butter(n, wn)

    x_hat = x
    for i in range(iterations):
        if len(x) < 9:
            x_hat = scipy.signal.filtfilt(b,
                                          a,
                                          x_hat,
                                          method="pad",
                                          padlen=len(x) - 1)
        else:
            x_hat = scipy.signal.filtfilt(b, a, x_hat, method="pad")

    x_hat, dxdt_hat = finite_difference(x_hat, dt)

    offset = np.mean(x) - np.mean(x_hat)
    x_hat = x_hat + offset

    return x_hat, dxdt_hat
def splinediff(x, dt, params, options={}):
    """
    Perform spline smoothing on x with scipy.interpolate.UnivariateSpline
    followed by first order finite difference

    :param x: array of time series to differentiate
    :type x: np.array (float)

    :param dt: time step size
    :type dt: float

    :param params: [k, s], k: Order of the spline. A kth order spline can be differentiated k times.
                    s: Positive smoothing factor used to choose the number of knots.
                    Number of knots will be increased until the smoothing condition is satisfied:
                    sum((w[i] * (y[i]-spl(x[i])))**2, axis=0) <= s

    :type params: list (int)

    :param options: an empty dictionary or a dictionary with 1 key value pair

                    - 'iterate': whether to run multiple iterations of the smoother. Note: iterate does nothing for median smoother.

    :type options: dict {'iterate': (boolean)}

    :return: a tuple consisting of:

            - x_hat: estimated (smoothed) x
            - dxdt_hat: estimated derivative of x


    :rtype: tuple -> (np.array, np.array)
    """
    if 'iterate' in options.keys() and options['iterate'] is True:
        k, s, iterations = params
    else:
        iterations = 1
        k, s = params

    t = np.arange(0, len(x) * dt, dt)

    x_hat = x
    for _ in range(iterations):
        spline = scipy.interpolate.UnivariateSpline(t, x_hat, k=k, s=s)
        x_hat = spline(t)

    x_hat, dxdt_hat = finite_difference(x_hat, dt)

    return x_hat, dxdt_hat
def mediandiff(x, dt, params, options={}):
    """
    Perform median smoothing using scipy.signal.medfilt
    followed by first order finite difference

    :param x: array of time series to differentiate
    :type x: np.array (float)

    :param dt: time step size
    :type dt: float

    :param params: filter window size
    :type params: list (int) or int

    :param options: an empty dictionary or a dictionary with 1 key value pair

                    - 'iterate': whether to run multiple iterations of the smoother. Note: iterate does nothing for median smoother.

    :type options: dict {'iterate': (boolean)}

    :return: a tuple consisting of:

            - x_hat: estimated (smoothed) x
            - dxdt_hat: estimated derivative of x


    :rtype: tuple -> (np.array, np.array)
    """

    if 'iterate' in options.keys() and options['iterate'] is True:
        window_size, iterations = params
    else:
        iterations = 1
        if isinstance(params, list):
            window_size = params[0]
        else:
            window_size = params

    if not window_size % 2:
        window_size += 1

    x_hat = x
    for _ in range(iterations):
        x_hat = __median_smooth__(x_hat, window_size)
    x_hat, dxdt_hat = finite_difference(x_hat, dt)

    return x_hat, dxdt_hat
def friedrichsdiff(x, dt, params, options={}):
    """
    Perform friedrichs smoothing by convolving friedrichs kernel with x
    followed by first order finite difference

    :param x: array of time series to differentiate
    :type x: np.array (float)

    :param dt: time step size
    :type dt: float

    :param params: [filter_window_size] or if 'iterate' in options:
                    [filter_window_size, num_iterations]

    :type params: list (int)

    :param options: an empty dictionary or a dictionary with 1 key value pair

                    - 'iterate': whether to run multiple iterations of the smoother. Note: iterate does nothing for median smoother.

    :type options: dict {'iterate': (boolean)}

    :return: a tuple consisting of:

            - x_hat: estimated (smoothed) x
            - dxdt_hat: estimated derivative of x


    :rtype: tuple -> (np.array, np.array)
    """

    if 'iterate' in options.keys() and options['iterate'] is True:
        window_size, iterations = params
    else:
        iterations = 1
        if isinstance(params, list):
            window_size = params[0]
        else:
            window_size = params

    kernel = __friedrichs_kernel__(window_size)
    x_hat = __convolutional_smoother__(x, kernel, iterations)
    x_hat, dxdt_hat = finite_difference(x_hat, dt)

    return x_hat, dxdt_hat
def mediandiff(x, dt, params, options={'iterate': False}):
    '''
    Perform median smoothing using scipy.signal.medfilt
    followed by first order finite difference

    Inputs
    ------
    x       : (np.array of floats, 1xN) time series to differentiate
    dt      : (float) time step

    Parameters
    ----------
    params  : (list) [filter_window_size] 
                     or if 'iterate' in options: [filter_window_size, num_iterations]
    options : (dict) {}
                     or {'iterate': True} to run multiple iterations of the smoother. 
                                          Note: iterate does nothing for median smoother.

    Outputs
    -------
    x_hat    : estimated (smoothed) x
    dxdt_hat : estimated derivative of x

    '''
    if 'iterate' in options.keys() and options['iterate'] is True:
        window_size, iterations = params
    else:
        iterations = 1
        if type(params) is list:
            window_size = params[0]
        else:
            window_size = params

    if not window_size % 2:
        window_size += 1

    x_hat = x
    for i in range(iterations):
        x_hat = __median_smooth__(x_hat, window_size)
    x_hat, dxdt_hat = finite_difference(x_hat, dt)

    return x_hat, dxdt_hat
def splinediff(x, dt, params, options={'iterate': False}):
    '''
    Perform spline smoothing on x with scipy.interpolate.UnivariateSpline
    followed by first order finite difference

    Inputs
    ------
    x       : (np.array of floats, 1xN) time series to differentiate
    dt      : (float) time step

    Parameters
    ----------
    params  : (list) [k, s], k: Order of the spline. A kth order spline can be differentiated k times.
                             s: Positive smoothing factor used to choose the number of knots. 
                                Number of knots will be increased until the smoothing condition is satisfied: 
                                sum((w[i] * (y[i]-spl(x[i])))**2, axis=0) <= s
                     or if 'iterate' in options: [k, s, num_iterations]
    options : (dict) {}
                     or {'iterate': True} to run multiple iterations of the smoother

    Outputs
    -------
    x_hat    : estimated (smoothed) x
    dxdt_hat : estimated derivative of x
    
    '''
    if 'iterate' in options.keys() and options['iterate'] is True:
        k, s, iterations = params
    else:
        iterations = 1
        k, s = params

    t = np.arange(0, len(x) * dt, dt)

    x_hat = x
    for i in range(iterations):
        spline = scipy.interpolate.UnivariateSpline(t, x_hat, k=k, s=s)
        x_hat = spline(t)

    x_hat, dxdt_hat = finite_difference(x_hat, dt)

    return x_hat, dxdt_hat
def friedrichsdiff(x, dt, params, options={'iterate': False}):
    '''
    Perform friedrichs smoothing by convolving friedrichs kernel with x
    followed by first order finite difference

    Inputs
    ------
    x       : (np.array of floats, 1xN) time series to differentiate
    dt      : (float) time step

    Parameters
    ----------
    params  : (list) [filter_window_size] 
                     or if 'iterate' in options: [filter_window_size, num_iterations]
    options : (dict) {}
                     or {'iterate': True} to run multiple iterations of the smoother

    Outputs
    -------
    x_hat    : estimated (smoothed) x
    dxdt_hat : estimated derivative of x
    
    '''
    if 'iterate' in options.keys() and options['iterate'] is True:
        window_size, iterations = params
    else:
        iterations = 1
        if type(params) is list:
            window_size = params[0]
        else:
            window_size = params

    kernel = __friedrichs_kernel__(window_size)
    x_hat = __convolutional_smoother__(x, kernel, iterations)
    x_hat, dxdt_hat = finite_difference(x_hat, dt)

    return x_hat, dxdt_hat
Exemple #10
0
def lineardiff(x,
               dt,
               params,
               options={
                   'sliding': True,
                   'step_size': 10,
                   'kernel_name': 'friedrichs'
               }):
    '''
    Slide a smoothing derivative function across a timeseries with specified window size.
    
    Inputs
    ------
    x       : (np.array of floats, 1xN) time series to differentiate
    dt      : (float) time step

    Parameters
    ----------
    params  : (list)  [N,            : (int)    order of the dynamics (e.g. 3 means rank 3 A matrix, with states = {x, xdot, xddot})
                       gamma,        : (float) regularization term
                       window_size], : (int)    size of the sliding window (ignored if not sliding)
    options : (dict) {'sliding'      : (bool)   slide the method (True or False)
                      'step_size'    : (int)    step size for sliding (smaller value is more accurate and more time consuming)
                      'kernel_name'} : (string) kernel to use for weighting and smoothing windows ('gaussian' or 'friedrichs')

    Outputs
    -------
    x_hat    : estimated (smoothed) x
    dxdt_hat : estimated derivative of x

    '''

    if 'sliding' in options.keys() and options['sliding'] is True:
        window_size = copy.copy(params[-1])
        params = params[0:-1]

        # forward and backward
        x_hat_forward, dxdt_hat_forward = __slide_function__(
            __lineardiff__, x, dt, params, window_size, options['step_size'],
            options['kernel_name'])
        x_hat_backward, dxdt_hat_backward = __slide_function__(
            __lineardiff__, x[::-1], dt, params, window_size,
            options['step_size'], options['kernel_name'])

        # weights
        w = np.arange(1, len(x_hat_forward) + 1, 1)[::-1]
        w = np.pad(w, [0, len(x) - len(w)], mode='constant')
        wfb = np.vstack((w, w[::-1]))
        norm = np.sum(wfb, axis=0)

        # orient and pad
        x_hat_forward = np.pad(x_hat_forward,
                               [0, len(x) - len(x_hat_forward)],
                               mode='constant')
        x_hat_backward = np.pad(x_hat_backward[::-1],
                                [len(x) - len(x_hat_backward), 0],
                                mode='constant')

        # merge
        x_hat = x_hat_forward * w / norm + x_hat_backward * w[::-1] / norm
        x_hat, dxdt_hat = finite_difference(x_hat, dt)

        return x_hat, dxdt_hat

    else:
        return __lineardiff__(x, dt, params, options={})
Exemple #11
0
def dmddiff(x,
            dt,
            params,
            options={
                'sliding': True,
                'step_size': 10,
                'kernel_name': 'gaussian'
            }):
    '''
    Fit the timeseries with optimized dynamic mode decomposition model.
    The DMD runs on the itnegral of x, to reduce effects of noise.
    
    Inputs
    ------
    x       : (np.array of floats, 1xN) time series to differentiate
    dt      : (float) time step

    Parameters
    ----------
    params  : (list)  [delay_embedding, : (int)  amount of delay_embedding 
                       svd_rank,        : (int)  rank trunkcation
                       window_size],    : (int)  size of the sliding window (ignored if not sliding)
    options : (dict) {'sliding'         : (bool)   slide the method (True or False)
                      'step_size'       : (int)    step size for sliding (smaller value is more accurate and more time consuming)
                      'kernel_name'}    : (string) kernel to use for weighting and smoothing windows ('gaussian' or 'friedrichs')

    Outputs
    -------
    x_hat    : estimated (smoothed) x
    dxdt_hat : estimated derivative of x

    '''

    delay_embedding = params[0]
    svd_rank = params[1]

    if delay_embedding >= len(x - 1):
        delay_embedding = len(x - 1)
    if delay_embedding < svd_rank:
        delay_embedding = svd_rank

    if 'sliding' in options.keys() and options['sliding'] is True:
        window_size = copy.copy(params[2])
        if window_size <= svd_rank + 1:
            window_size = svd_rank + 1

    # forward
    integral_x = utility.integrate_dxdt_hat(x, dt)
    X = utility.hankel_matrix(np.matrix(integral_x), delay_embedding).T
    if 'sliding' in options.keys() and options['sliding'] is True:
        x_hat_forward, dxdt_hat_forward = __slide_function__(
            __dmddiff__, X, dt, params, window_size, options['step_size'],
            options['kernel_name'])
    else:
        x_hat_forward, dxdt_hat_forward = __dmddiff__(X,
                                                      dt,
                                                      params,
                                                      options={})

    # backward
    integral_x = utility.integrate_dxdt_hat(x[::-1], dt)
    X = utility.hankel_matrix(np.matrix(integral_x), delay_embedding).T
    if 'sliding' in options.keys() and options['sliding'] is True:
        x_hat_backward, dxdt_hat_backward = __slide_function__(
            __dmddiff__, X, dt, params, window_size, options['step_size'],
            options['kernel_name'])
    else:
        x_hat_backward, dxdt_hat_backward = __dmddiff__(X,
                                                        dt,
                                                        params,
                                                        options={})

    # weights
    w = np.arange(1, len(x_hat_forward) + 1, 1)[::-1]
    w = np.pad(w, [0, len(x) - len(w)], mode='constant')
    wfb = np.vstack((w, w[::-1]))
    norm = np.sum(wfb, axis=0)

    # orient and pad
    x_hat_forward = np.pad(x_hat_forward, [0, len(x) - len(x_hat_forward)],
                           mode='constant')
    x_hat_backward = np.pad(x_hat_backward[::-1],
                            [len(x) - len(x_hat_backward), 0],
                            mode='constant')

    # merge
    x_hat = x_hat_forward * w / norm + x_hat_backward * w[::-1] / norm
    x_hat, dxdt_hat = finite_difference(x_hat, dt)

    return x_hat, dxdt_hat
Exemple #12
0
def lineardiff(x, dt, params, options=None):
    """
    Slide a smoothing derivative function across a time series with specified window size.

    :param x: array of time series to differentiate
    :type x: np.array (float)

    :param dt: time step size
    :type dt: float

    :param params: a list of 3 elements:

                    - N: order of the polynomial
                    - gamma: regularization term
                    - window_size: size of the sliding window (ignored if not sliding)

    :type params: list (int, float, int)

    :param options: a dictionary consisting of 3 key value pairs:

                    - 'sliding': whether to use sliding approach
                    - 'step_size': step size for sliding
                    - 'kernel_name': kernel to use for weighting and smoothing windows ('gaussian' or 'friedrichs')

    :type options: dict {'sliding': (bool), 'step_size': (int), 'kernel_name': (string)}, optional

    :return: a tuple consisting of:

            - x_hat: estimated (smoothed) x
            - dxdt_hat: estimated derivative of x

    :rtype: tuple -> (np.array, np.array)
    """

    if options is None:
        options = {
            'sliding': True,
            'step_size': 10,
            'kernel_name': 'friedrichs',
            'solver': 'MOSEK'
        }

    if 'sliding' not in options.keys():
        options['sliding'] = True
    if 'step_size' not in options.keys():
        options['step_size'] = 10
    if 'kernel_name' not in options.keys():
        options['kernel_name'] = 'friedrichs'
    if 'solver' not in options.keys():
        options['solver'] = 'MOSEK'

    if 'sliding' in options.keys() and options['sliding'] is True:
        window_size = copy.copy(params[-1])
        params = params[0:-1]

        # forward and backward
        x_hat_forward, _ = __slide_function__(__lineardiff__, x, dt, params,
                                              window_size,
                                              options['step_size'],
                                              options['kernel_name'],
                                              options['solver'])
        x_hat_backward, _ = __slide_function__(__lineardiff__, x[::-1], dt,
                                               params, window_size,
                                               options['step_size'],
                                               options['kernel_name'],
                                               options['solver'])

        # weights
        w = np.arange(1, len(x_hat_forward) + 1, 1)[::-1]
        w = np.pad(w, [0, len(x) - len(w)], mode='constant')
        wfb = np.vstack((w, w[::-1]))
        norm = np.sum(wfb, axis=0)

        # orient and pad
        x_hat_forward = np.pad(x_hat_forward,
                               [0, len(x) - len(x_hat_forward)],
                               mode='constant')
        x_hat_backward = np.pad(x_hat_backward[::-1],
                                [len(x) - len(x_hat_backward), 0],
                                mode='constant')

        # merge
        x_hat = x_hat_forward * w / norm + x_hat_backward * w[::-1] / norm
        x_hat, dxdt_hat = finite_difference(x_hat, dt)

        return x_hat, dxdt_hat

    return __lineardiff__(x, dt, params, options)