signal_tools.h

Back to Signal class

tim/core/

// Description: Algorithms for Signal's

#ifndef TIM_SIGNAL_TOOLS_H
#define TIM_SIGNAL_TOOLS_H

#include "tim/core/signal.h"
#include "tim/core/signal_merge.h"
#include "tim/core/signal_split.h"
#include "tim/core/signal_properties.h"

#include <pastel/math/matrix/matrix.h>
#include <pastel/math/matrix/matrix_inverse.h>
#include <pastel/math/matrix/cholesky_decomposition.h>

#include <pastel/sys/range.h>
#include <pastel/sys/array/array.h>
#include <pastel/sys/string/string_algorithms.h>

#include <iostream>

namespace Tim
{

    //! Prints the signal into an output stream.
    inline TIM std::ostream& operator<<(
        std::ostream& stream, const Signal& signal)
    {
        integer dimension = signal.dimension();
        integer samples = signal.samples();

        if (samples == 0 || dimension == 0)
        {
            std::cout << "[]" << std::endl;
        }
        else
        {
            std::cout << "[";
            for (integer x = 0; x < dimension;++x)
            {
                if (x > 0)
                {
                    std::cout << ";" << std::endl;
                }
                // The beginning time instant is
                // transmitted by padding the first samples
                // of the outputted signal with NaNs.
                for (integer y = 0; y < signal.t();++y)
                {
                    if (y > 0)
                    {
                        std::cout << ", ";
                    }
                    std::cout << "nan";
                }
                // The NaN-padding is followed by the actual
                // signal.
                for (integer y = 0; y < samples;++y)
                {
                    if (y + signal.t() > 0)
                    {
                        std::cout << ", ";
                    }
                    std::cout << realToString(signal.data()(y, x));
                }
            }
            std::cout << "]";
        }

        /*
       stream << "[";
       for (integer i = 0;i < dimension;++i)
       {
           if (i > 0)
           {
               stream << ";" << std::endl;
           }
           for (integer j = 0;j < samples;++j)
           {
               if (j > 0)
               {
                   stream << ", ";
               }
               stream << signal.data()(j, i);
           }
       }
       stream << "]";
       */

        return stream;
    }

    //! Computes the covariance of the signal samples.
    inline TIM void computeCovariance(
        const Signal& signal,
        MatrixView<dreal>& result)
    {
        integer dimension = signal.dimension();
        integer samples = signal.samples();

        ENSURE_OP(result.rows(), ==, signal.dimension());
        ENSURE_OP(result.cols(), ==, signal.dimension());

        VectorD mean = asVector(asMatrix(signal.data()).colwise().sum() / samples);

        asMatrix(result) = 
            (signal.matrix() - asColumnMatrix(mean).replicate(1, samples)) * 
            (signal.matrix() - asColumnMatrix(mean).replicate(1, samples)).transpose();
        asMatrix(result) /= samples;
    }

    //! Transforms the given signal to identity covariance.
    inline TIM void normalizeCovariance(
        Signal& signal,
        const Matrix<dreal>& covariance)
    {
        // Let X be the signal matrix with
        // each sample as a _column_.
        // Then the covariance C of the signal
        // is given by:
        //
        // C = X X^T
        //
        // Problem: find an invertible matrix A
        // by which the signal X transforms
        // into a signal Y = AX 
        // having identity covariance.
        //
        // Solution:
        // 
        // Y Y^T = I
        // =>
        // (AX) (AX)^T = I
        // =>
        // A X X^T A^T = I
        // =>
        // A C A^T = I
        // =>
        // C = A^-1 A^-T
        // =>
        // C^-1 = (A^-1 A^-T)^-1
        // =>
        // C^-1 = A^T A

        // One solution is given by:
        // A^T = Cholesky(C^-1)
        // =>
        // A = Cholesky(C^-1)^T

        Matrix<dreal> invCovariance = inverse(covariance);

        CholeskyDecompositionInplace<dreal> invCholesky(view(invCovariance));

        // The samples are row vectors, so we
        // multiply with the transpose from the right.

        signal.matrix() *= asMatrix(invCholesky.lower());
    }        

}

#include "tim/core/signal_generate.h"

#endif