<commit_before>// Copyright (C) 2012 Rhys Ulerich
//
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at http://mozilla.org/MPL/2.0/.

#include <cstdlib>
#include <string>
#include <vector>

#include <Python.h>
#include <numpy/arrayobject.h>

#include "ar.hpp"

/** @file
 * A Python extension module for exposing autoregressive model utilities.
 */

// Compile-time defaults in the code also appearing in arsel docstring
#define DEFAULT_SUBMEAN   true
#define DEFAULT_ABSRHO    false
#define DEFAULT_CRITERION "CIC"
#define DEFAULT_MAXORDER  512
#define STRINGIFY(x) STRINGIFY_HELPER(x)
#define STRINGIFY_HELPER(x) #x

static const char ar_arsel_docstring[] =
"    Usage: M = arsel (data, submean, absrho, criterion, maxorder)\n"
"\n"
"    Automatically fit autoregressive models to input signals.\n"
"\n"
"    Use ar::burg_method and ar::best_model to fit an autoregressive process\n"
"    for signals contained in the rows of matrix data.  Sample means will\n"
"    be subtracted whenever submean is true.  Model orders zero through\n"
"    min(columns(data), maxorder) will be considered.  A dictionary is\n"
"    returned where each key either contains a result indexable by the\n"
"    signal number (i.e. the row indices of input matrix data) or it contains\n"
"    a single scalar applicable to all signals.\n"
"\n"
"    The model order will be selected using the specified criterion.\n"
"    Criteria are specified using the following abbreviations:\n"
"        AIC  - Akaike information criterion\n"
"        AICC - asymptotically-corrected Akaike information criterion\n"
"        BIC  - consistent criterion BIC\n"
"        CIC  - combined information criterion\n"
"        FIC  - finite information criterion\n"
"        FSIC - finite sample information criterion\n"
"        GIC  - generalized information criterion\n"
"        MCC  - minimally consistent criterion\n"
"\n"
"    The number of samples in data (i.e. the number of rows) is returned\n"
"    in key 'N'.  The filter()-ready process parameters are returned\n"
"    in key 'AR', the sample mean in 'mu', and the innovation variance\n"
"    \\sigma^2_\\epsilon in 'sigma2eps'.  The process output variance\n"
"    \\sigma^2_\\x and process gain are returned in keys 'sigma2x' and\n"
"    'gain', respectively.  Autocorrelations for lags zero through the\n"
"    model order, inclusive, are returned in key 'autocor'.  The raw\n"
"    signals are made available for later use in field 'data'.\n"
"\n"
"    Given the observed autocorrelation structure, a decorrelation time\n"
"    'T0' is computed by ar::decorrelation_time and used to estimate\n"
"    the effective signal variance 'eff_var'.  The number of effectively\n"
"    independent samples is returned in 'eff_N'.  These effective values are\n"
"    combined to estimate the sampling error (i.e. the standard deviation\n"
"    of the sample mean) as field 'mu_sigma'.  The absolute value of the\n"
"    autocorrelation function will be used in computing the decorrelation\n"
"    times whenever absrho is true.\n"
"\n"
"    When omitted, submean defaults to " STRINGIFY(DEFAULT_SUBMEAN) ".\n"
"    When omitted, absrho defaults to " STRINGIFY(DEFAULT_ABSRHO) ".\n"
"    When omitted, criterion defaults to " STRINGIFY(DEFAULT_CRITERION) ".\n"
"    When omitted, maxorder defaults to " STRINGIFY(DEFAULT_MAXORDER) ".\n"
;

extern "C" PyObject *ar_arsel(PyObject *self, PyObject *args)
{
    // Prepare argument storage with default values
    PyObject   *data_obj  = NULL;
    int         submean   = DEFAULT_SUBMEAN;
    int         absrho    = DEFAULT_ABSRHO;
    const char *criterion = DEFAULT_CRITERION;
    std::size_t maxorder  = DEFAULT_MAXORDER;

    // Parse input tuple with second and subsequent arguments optional
    {
        unsigned long ul_maxorder = maxorder;
        if (!PyArg_ParseTuple(args, "O|iisk", &data_obj, &submean, &absrho,
                                              &criterion, &ul_maxorder)) {
            return NULL;
        }
        maxorder = ul_maxorder;
    }

    // Lookup the desired model selection criterion
    typedef ar::best_model_function<
            ar::Burg,npy_intp,std::vector<double> > best_model_function;
    const best_model_function::type best_model
            = best_model_function::lookup(std::string(criterion), submean);
    if (!best_model) {
        PyErr_SetString(PyExc_RuntimeError,
            "Unknown model selection criterion provided to arsel.");
        return NULL;
    }

    // Incoming data may be noncontiguous but should otherwise be well-behaved
    // On success, 'data' is returned so Py_DECREF is applied only on failure
    PyObject *data = PyArray_FROMANY(data_obj, NPY_DOUBLE, 1, 2,
            NPY_ALIGNED | NPY_ELEMENTSTRIDES | NPY_NOTSWAPPED);
    if (!data) {
        Py_DECREF(data);
        return NULL;
    }

    // Reshape any 1D ndarray into a 2D ndarray organized as a row vector.
    // Permits the remainder of the routine to uniformly worry about 2D only.
    if (1 == ((PyArrayObject *)(data))->nd) {
        npy_intp dim[2] = { 1, PyArray_DIM(data, 0) };
        PyArray_Dims newshape = { dim, sizeof(dim)/sizeof(dim[0]) };
        PyObject *olddata = data;
        data = PyArray_Newshape((PyArrayObject *)olddata,
                                &newshape, NPY_ANYORDER);
        Py_DECREF(olddata);
        if (!data) {
            Py_DECREF(data);
            PyErr_SetString(PyExc_RuntimeError,
                "Unable to reorganize data into matrix-like row vector.");
            return NULL;
        }
    }

    // How many data points are there?
    npy_intp M = PyArray_DIM(data, 0);
    npy_intp N = PyArray_DIM(data, 1);

    // Prepare per-signal storage locations to return to caller
    // TODO Ensure these invocations all worked as expected
    PyObject *_AR        = PyList_New(M);
    PyObject *_autocor   = PyList_New(M);
    PyObject *_eff_N     = PyArray_ZEROS(1, &M, NPY_DOUBLE, 0);
    PyObject *_eff_var   = PyArray_ZEROS(1, &M, NPY_DOUBLE, 0);
    PyObject *_gain      = PyArray_ZEROS(1, &M, NPY_DOUBLE, 0);
    PyObject *_mu        = PyArray_ZEROS(1, &M, NPY_DOUBLE, 0);
    PyObject *_mu_sigma  = PyArray_ZEROS(1, &M, NPY_DOUBLE, 0);
    PyObject *_sigma2eps = PyArray_ZEROS(1, &M, NPY_DOUBLE, 0);
    PyObject *_sigma2x   = PyArray_ZEROS(1, &M, NPY_DOUBLE, 0);
    PyObject *_T0        = PyArray_ZEROS(1, &M, NPY_DOUBLE, 0);

    // Prepare vectors to capture burg_method() output
    std::vector<double> params, sigma2e, gain, autocor;
    params .reserve(maxorder*(maxorder + 1)/2);
    sigma2e.reserve(maxorder + 1);
    gain   .reserve(maxorder + 1);
    autocor.reserve(maxorder + 1);

    // Prepare repeatedly-used working storage for burg_method()
    std::vector<double> f, b, Ak, ac;

    // Process each signal in turn...
    for (npy_intp i = 0; i < M; ++i)
    {
        // Use burg_method to estimate a hierarchy of AR models from input data
        params .clear();
        sigma2e.clear();
        gain   .clear();
        autocor.clear();
        ar::strided_adaptor<const double*> signal_begin(
                (const double*) PyArray_GETPTR2(data, i, 0),
                PyArray_STRIDES(data)[1] / sizeof(double));
        ar::strided_adaptor<const double*> signal_end  (
                (const double*) PyArray_GETPTR2(data, i, N),
                PyArray_STRIDES(data)[1] / sizeof(double));
        ar::burg_method(signal_begin, signal_end,
                        *(double*)PyArray_GETPTR1(_mu, i),
                        maxorder,
                        std::back_inserter(params),
                        std::back_inserter(sigma2e),
                        std::back_inserter(gain),
                        std::back_inserter(autocor),
                        submean, /* output hierarchy? */ true, f, b, Ak, ac);

        // Keep only best model per chosen criterion via function pointer
        best_model(N, params, sigma2e, gain, autocor);

        // Compute decorrelation time from the estimated autocorrelation model
        ar::predictor<double> p = ar::autocorrelation(
                params.begin(), params.end(), gain[0], autocor.begin());
        *(double*)PyArray_GETPTR1(_T0, i)
            = ar::decorrelation_time(N, p, absrho);

        // Filter()-ready process parameters in field 'AR' with leading one
        PyObject *_ARi = PyList_New(params.size() + 1);
        PyList_SET_ITEM(_AR, i, _ARi);
        PyList_SET_ITEM(_ARi, 0, PyFloat_FromDouble(1));
        for (std::size_t k = 0; k < params.size(); ++k) {
            PyList_SET_ITEM(_ARi, k+1, PyFloat_FromDouble(params[k]));
        }

        // Field 'sigma2eps'
        *(double*)PyArray_GETPTR1(_sigma2eps, i) = sigma2e[0];

        // Field 'gain'
        *(double*)PyArray_GETPTR1(_gain, i) = gain[0];

        // Field 'sigma2x'
        *(double*)PyArray_GETPTR1(_sigma2x, i) = gain[0]*sigma2e[0];

        // Field 'autocor'
        PyObject *_autocori = PyList_New(autocor.size());
        PyList_SET_ITEM(_autocor, i, _autocori);
        for (std::size_t k = 0; k < autocor.size(); ++k) {
            PyList_SET_ITEM(_autocori, k, PyFloat_FromDouble(autocor[k]));
        }

        // Field 'eff_var'
        // Unbiased effective variance expression from [Trenberth1984]
        *(double*)PyArray_GETPTR1(_eff_var, i)
            = (N*gain[0]*sigma2e[0]) / (N - *(double*)PyArray_GETPTR1(_T0, i));

        // Field 'eff_N'
        *(double*)PyArray_GETPTR1(_eff_N, i)
            = N / *(double*)PyArray_GETPTR1(_T0, i);

        // Field 'mu_sigma'
        // Variance of the sample mean using effective quantities
        *(double*)PyArray_GETPTR1(_mu_sigma, i)
            = std::sqrt(   *(double*)PyArray_GETPTR1(_eff_var, i)
                         / *(double*)PyArray_GETPTR1(_eff_N,   i));
    }

    // Allocate the dictionary returned by the method
    PyObject *ret = PyDict_New();
    if (!ret) goto fail;

    // Arguments preserved as outputs
    // TODO Ensure these invocations all worked as expected
    PyDict_SetItemString(ret, "data"     , data);
    PyDict_SetItemString(ret, "submean"  , PyBool_FromLong(submean));
    PyDict_SetItemString(ret, "absrho"   , PyBool_FromLong(absrho));
    PyDict_SetItemString(ret, "criterion", PyString_FromString(criterion));
    PyDict_SetItemString(ret, "maxorder" , PyInt_FromSize_t(maxorder));
    PyDict_SetItemString(ret, "N"        , PyInt_FromLong(N));

    // Computed results
    // TODO Ensure these invocations all worked as expected
    PyDict_SetItemString(ret, "AR"       , _AR       );
    PyDict_SetItemString(ret, "autocor"  , _autocor  );
    PyDict_SetItemString(ret, "eff_N"    , _eff_N    );
    PyDict_SetItemString(ret, "eff_var"  , _eff_var  );
    PyDict_SetItemString(ret, "gain"     , _gain     );
    PyDict_SetItemString(ret, "mu"       , _mu       );
    PyDict_SetItemString(ret, "mu_sigma" , _mu_sigma );
    PyDict_SetItemString(ret, "sigma2eps", _sigma2eps);
    PyDict_SetItemString(ret, "sigma2x"  , _sigma2x  );
    PyDict_SetItemString(ret, "T0"       , _T0       );

    return ret;

fail:
    Py_XDECREF(data);
    Py_XDECREF(_AR);
    Py_XDECREF(_autocor);
    Py_XDECREF(_eff_N);
    Py_XDECREF(_eff_var);
    Py_XDECREF(_gain);
    Py_XDECREF(_mu);
    Py_XDECREF(_mu_sigma);
    Py_XDECREF(_sigma2eps);
    Py_XDECREF(_sigma2x);
    Py_XDECREF(_T0);
    return NULL;
}

// Specification of methods available in the module
static PyMethodDef ar_methods[] = {
    {"arsel", ar_arsel, METH_VARARGS, ar_arsel_docstring},
    {NULL, NULL, 0, NULL}
};

// Module docstring
static const char ar_docstring[] = "Autoregressive process modeling tools";

extern "C" PyMODINIT_FUNC initar(void)
{
    // Initialize the module, including making NumPy available
    if (!Py_InitModule3("ar", ar_methods, ar_docstring)) return;
    import_array();

////// Use collections.namedtuple() to make a type for ar_arsel use
////PyObject *modName = PyString_FromString((char *)"collections");
////PyObject *mod     = PyImport_Import(modName);
////PyObject *func    = PyObject_GetAttrString(mod,(char*)"namedtuple");

////Py_DECREF(modName);
////Py_DECREF(mod);
////Py_DECREF(func);
}
<commit_msg>Use a namedtuple subclass for Python return<commit_after>// Copyright (C) 2012 Rhys Ulerich
//
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at http://mozilla.org/MPL/2.0/.

#include <cstdlib>
#include <string>
#include <vector>

#include <Python.h>
#include <numpy/arrayobject.h>

#include "ar.hpp"

/** @file
 * A Python extension module for exposing autoregressive model utilities.
 */

// Compile-time defaults in the code also appearing in arsel docstring
#define DEFAULT_SUBMEAN   true
#define DEFAULT_ABSRHO    false
#define DEFAULT_CRITERION "CIC"
#define DEFAULT_MAXORDER  512
#define STRINGIFY(x) STRINGIFY_HELPER(x)
#define STRINGIFY_HELPER(x) #x

static const char ar_arsel_docstring[] =
"    Usage: M = arsel (data, submean, absrho, criterion, maxorder)\n"
"\n"
"    Automatically fit autoregressive models to input signals.\n"
"\n"
"    Use ar::burg_method and ar::best_model to fit an autoregressive process\n"
"    for signals contained in the rows of matrix data.  Sample means will\n"
"    be subtracted whenever submean is true.  Model orders zero through\n"
"    min(columns(data), maxorder) will be considered.  A dictionary is\n"
"    returned where each key either contains a result indexable by the\n"
"    signal number (i.e. the row indices of input matrix data) or it contains\n"
"    a single scalar applicable to all signals.\n"
"\n"
"    The model order will be selected using the specified criterion.\n"
"    Criteria are specified using the following abbreviations:\n"
"        AIC  - Akaike information criterion\n"
"        AICC - asymptotically-corrected Akaike information criterion\n"
"        BIC  - consistent criterion BIC\n"
"        CIC  - combined information criterion\n"
"        FIC  - finite information criterion\n"
"        FSIC - finite sample information criterion\n"
"        GIC  - generalized information criterion\n"
"        MCC  - minimally consistent criterion\n"
"\n"
"    The number of samples in data (i.e. the number of rows) is returned\n"
"    in key 'N'.  The filter()-ready process parameters are returned\n"
"    in key 'AR', the sample mean in 'mu', and the innovation variance\n"
"    \\sigma^2_\\epsilon in 'sigma2eps'.  The process output variance\n"
"    \\sigma^2_\\x and process gain are returned in keys 'sigma2x' and\n"
"    'gain', respectively.  Autocorrelations for lags zero through the\n"
"    model order, inclusive, are returned in key 'autocor'.  The raw\n"
"    signals are made available for later use in field 'data'.\n"
"\n"
"    Given the observed autocorrelation structure, a decorrelation time\n"
"    'T0' is computed by ar::decorrelation_time and used to estimate\n"
"    the effective signal variance 'eff_var'.  The number of effectively\n"
"    independent samples is returned in 'eff_N'.  These effective values are\n"
"    combined to estimate the sampling error (i.e. the standard deviation\n"
"    of the sample mean) as field 'mu_sigma'.  The absolute value of the\n"
"    autocorrelation function will be used in computing the decorrelation\n"
"    times whenever absrho is true.\n"
"\n"
"    When omitted, submean defaults to " STRINGIFY(DEFAULT_SUBMEAN) ".\n"
"    When omitted, absrho defaults to " STRINGIFY(DEFAULT_ABSRHO) ".\n"
"    When omitted, criterion defaults to " STRINGIFY(DEFAULT_CRITERION) ".\n"
"    When omitted, maxorder defaults to " STRINGIFY(DEFAULT_MAXORDER) ".\n"
;

// Return type for ar_arsel initialized within initar
static PyTypeObject *ar_ArselType = NULL;

extern "C" PyObject *ar_arsel(PyObject *self, PyObject *args)
{
    // Sanity check that initar could build ar_ArselType
    if (!PyType_Check(ar_ArselType)) {
        PyErr_SetString(PyExc_RuntimeError,
            "PyType_Check(ar_ArselType) failed");
        return NULL;
    }

    // Prepare argument storage with default values
    PyObject   *data_obj  = NULL;
    int         submean   = DEFAULT_SUBMEAN;
    int         absrho    = DEFAULT_ABSRHO;
    const char *criterion = DEFAULT_CRITERION;
    std::size_t maxorder  = DEFAULT_MAXORDER;

    // Parse input tuple with second and subsequent arguments optional
    {
        unsigned long ul_maxorder = maxorder;
        if (!PyArg_ParseTuple(args, "O|iisk", &data_obj, &submean, &absrho,
                                              &criterion, &ul_maxorder)) {
            return NULL;
        }
        maxorder = ul_maxorder;
    }

    // Lookup the desired model selection criterion
    typedef ar::best_model_function<
            ar::Burg,npy_intp,std::vector<double> > best_model_function;
    const best_model_function::type best_model
            = best_model_function::lookup(std::string(criterion), submean);
    if (!best_model) {
        PyErr_SetString(PyExc_RuntimeError,
            "Unknown model selection criterion provided to arsel.");
        return NULL;
    }

    // Incoming data may be noncontiguous but should otherwise be well-behaved
    // On success, 'data' is returned so Py_DECREF is applied only on failure
    PyObject *data = PyArray_FROMANY(data_obj, NPY_DOUBLE, 1, 2,
            NPY_ALIGNED | NPY_ELEMENTSTRIDES | NPY_NOTSWAPPED);
    if (!data) {
        Py_DECREF(data);
        return NULL;
    }

    // Reshape any 1D ndarray into a 2D ndarray organized as a row vector.
    // Permits the remainder of the routine to uniformly worry about 2D only.
    if (1 == ((PyArrayObject *)(data))->nd) {
        npy_intp dim[2] = { 1, PyArray_DIM(data, 0) };
        PyArray_Dims newshape = { dim, sizeof(dim)/sizeof(dim[0]) };
        PyObject *olddata = data;
        data = PyArray_Newshape((PyArrayObject *)olddata,
                                &newshape, NPY_ANYORDER);
        Py_DECREF(olddata);
        if (!data) {
            Py_DECREF(data);
            PyErr_SetString(PyExc_RuntimeError,
                "Unable to reorganize data into matrix-like row vector.");
            return NULL;
        }
    }

    // How many data points are there?
    npy_intp M = PyArray_DIM(data, 0);
    npy_intp N = PyArray_DIM(data, 1);

    // Prepare per-signal storage locations to return to caller
    // TODO Ensure these invocations all worked as expected
    PyObject *_AR        = PyList_New(M);
    PyObject *_autocor   = PyList_New(M);
    PyObject *_eff_N     = PyArray_ZEROS(1, &M, NPY_DOUBLE, 0);
    PyObject *_eff_var   = PyArray_ZEROS(1, &M, NPY_DOUBLE, 0);
    PyObject *_gain      = PyArray_ZEROS(1, &M, NPY_DOUBLE, 0);
    PyObject *_mu        = PyArray_ZEROS(1, &M, NPY_DOUBLE, 0);
    PyObject *_mu_sigma  = PyArray_ZEROS(1, &M, NPY_DOUBLE, 0);
    PyObject *_sigma2eps = PyArray_ZEROS(1, &M, NPY_DOUBLE, 0);
    PyObject *_sigma2x   = PyArray_ZEROS(1, &M, NPY_DOUBLE, 0);
    PyObject *_T0        = PyArray_ZEROS(1, &M, NPY_DOUBLE, 0);

    // Prepare vectors to capture burg_method() output
    std::vector<double> params, sigma2e, gain, autocor;
    params .reserve(maxorder*(maxorder + 1)/2);
    sigma2e.reserve(maxorder + 1);
    gain   .reserve(maxorder + 1);
    autocor.reserve(maxorder + 1);

    // Prepare repeatedly-used working storage for burg_method()
    std::vector<double> f, b, Ak, ac;

    // Process each signal in turn...
    for (npy_intp i = 0; i < M; ++i)
    {
        // Use burg_method to estimate a hierarchy of AR models from input data
        params .clear();
        sigma2e.clear();
        gain   .clear();
        autocor.clear();
        ar::strided_adaptor<const double*> signal_begin(
                (const double*) PyArray_GETPTR2(data, i, 0),
                PyArray_STRIDES(data)[1] / sizeof(double));
        ar::strided_adaptor<const double*> signal_end  (
                (const double*) PyArray_GETPTR2(data, i, N),
                PyArray_STRIDES(data)[1] / sizeof(double));
        ar::burg_method(signal_begin, signal_end,
                        *(double*)PyArray_GETPTR1(_mu, i),
                        maxorder,
                        std::back_inserter(params),
                        std::back_inserter(sigma2e),
                        std::back_inserter(gain),
                        std::back_inserter(autocor),
                        submean, /* output hierarchy? */ true, f, b, Ak, ac);

        // Keep only best model per chosen criterion via function pointer
        best_model(N, params, sigma2e, gain, autocor);

        // Compute decorrelation time from the estimated autocorrelation model
        ar::predictor<double> p = ar::autocorrelation(
                params.begin(), params.end(), gain[0], autocor.begin());
        *(double*)PyArray_GETPTR1(_T0, i)
            = ar::decorrelation_time(N, p, absrho);

        // Filter()-ready process parameters in field 'AR' with leading one
        PyObject *_ARi = PyList_New(params.size() + 1);
        PyList_SET_ITEM(_AR, i, _ARi);
        PyList_SET_ITEM(_ARi, 0, PyFloat_FromDouble(1));
        for (std::size_t k = 0; k < params.size(); ++k) {
            PyList_SET_ITEM(_ARi, k+1, PyFloat_FromDouble(params[k]));
        }

        // Field 'sigma2eps'
        *(double*)PyArray_GETPTR1(_sigma2eps, i) = sigma2e[0];

        // Field 'gain'
        *(double*)PyArray_GETPTR1(_gain, i) = gain[0];

        // Field 'sigma2x'
        *(double*)PyArray_GETPTR1(_sigma2x, i) = gain[0]*sigma2e[0];

        // Field 'autocor'
        PyObject *_autocori = PyList_New(autocor.size());
        PyList_SET_ITEM(_autocor, i, _autocori);
        for (std::size_t k = 0; k < autocor.size(); ++k) {
            PyList_SET_ITEM(_autocori, k, PyFloat_FromDouble(autocor[k]));
        }

        // Field 'eff_var'
        // Unbiased effective variance expression from [Trenberth1984]
        *(double*)PyArray_GETPTR1(_eff_var, i)
            = (N*gain[0]*sigma2e[0]) / (N - *(double*)PyArray_GETPTR1(_T0, i));

        // Field 'eff_N'
        *(double*)PyArray_GETPTR1(_eff_N, i)
            = N / *(double*)PyArray_GETPTR1(_T0, i);

        // Field 'mu_sigma'
        // Variance of the sample mean using effective quantities
        *(double*)PyArray_GETPTR1(_mu_sigma, i)
            = std::sqrt(   *(double*)PyArray_GETPTR1(_eff_var, i)
                         / *(double*)PyArray_GETPTR1(_eff_N,   i));
    }

    // Prepare build and return an ar_ArselType via tuple constructor
    // See initar(...) method for the collections.namedtuple-based definition
    PyObject *ret_args = NULL, *ret = NULL;
    ret_args = PyTuple_Pack(16, PyBool_FromLong(absrho),
                                _AR,
                                _autocor,
                                PyString_FromString(criterion),
                                data,
                                _eff_N,
                                _eff_var,
                                _gain,
                                PyInt_FromSize_t(maxorder),
                                _mu,
                                _mu_sigma,
                                PyInt_FromLong(N),
                                _sigma2eps,
                                _sigma2x,
                                PyBool_FromLong(submean),
                                _T0);
    if (!ret_args) {
        PyErr_SetString(PyExc_RuntimeError,
            "Unable to prepare arguments used to build return value.");
        goto fail;
    }
    ret = PyObject_CallObject((PyObject *)ar_ArselType, ret_args);
    if (!ret) {
        PyErr_SetString(PyExc_RuntimeError,
            "Unable to construct return value.");
        goto fail;
    }

    return ret;

fail:
    Py_XDECREF(ret_args);
    Py_XDECREF(ret);
    Py_XDECREF(data);
    Py_XDECREF(_AR);
    Py_XDECREF(_autocor);
    Py_XDECREF(_eff_N);
    Py_XDECREF(_eff_var);
    Py_XDECREF(_gain);
    Py_XDECREF(_mu);
    Py_XDECREF(_mu_sigma);
    Py_XDECREF(_sigma2eps);
    Py_XDECREF(_sigma2x);
    Py_XDECREF(_T0);
    return NULL;
}

// Specification of methods available in the module
static PyMethodDef ar_methods[] = {
    {"arsel", ar_arsel, METH_VARARGS, ar_arsel_docstring},
    {NULL, NULL, 0, NULL}
};

// Module docstring
static const char ar_docstring[] = "Autoregressive process modeling tools";

extern "C" PyMODINIT_FUNC initar(void)
{
    // Initialize the module, including making NumPy available
    if (!Py_InitModule3("ar", ar_methods, ar_docstring)) return;
    import_array();

    // Use collections.namedtuple() to make type 'Arsel' for ar_arsel use
    // The tuple is ordered alphabetically in case-insensitive manner
    PyObject *modName = PyString_FromString((char *)"collections");
    PyObject *mod     = PyImport_Import(modName);
    PyObject *func    = PyObject_GetAttrString(mod,(char*)"namedtuple");
    PyObject *args    = PyTuple_Pack(2,
                                     PyString_FromString((char *) "Arsel"),
                                     PyString_FromString((char *) " absrho"
                                                                  " AR"
                                                                  " autocor"
                                                                  " criterion"
                                                                  " data"
                                                                  " eff_N"
                                                                  " eff_var"
                                                                  " gain"
                                                                  " maxorder"
                                                                  " mu"
                                                                  " mu_sigma"
                                                                  " N"
                                                                  " sigma2eps"
                                                                  " sigma2x"
                                                                  " submean"
                                                                  " T0"));
    ar_ArselType = (PyTypeObject *) PyObject_CallObject(func, args);
    Py_DECREF(args);
    Py_DECREF(func);
    Py_DECREF(mod);
    Py_DECREF(modName);
}
<|endoftext|>


<commit_before>/*
 *  yosys -- Yosys Open SYnthesis Suite
 *
 *  Copyright (C) 2012  Clifford Wolf <clifford@clifford.at>
 *
 *  Permission to use, copy, modify, and/or distribute this software for any
 *  purpose with or without fee is hereby granted, provided that the above
 *  copyright notice and this permission notice appear in all copies.
 *
 *  THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
 *  WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
 *  MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
 *  ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
 *  WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
 *  ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
 *  OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
 *
 */

#include "kernel/yosys.h"
#include "kernel/sigtools.h"

USING_YOSYS_NAMESPACE
PRIVATE_NAMESPACE_BEGIN

struct EquivStructWorker
{
	Module *module;
	SigMap sigmap;
	SigMap equiv_bits;
	bool mode_fwd;
	bool mode_icells;
	int merge_count;

	struct merge_key_t
	{
		IdString type;
		vector<pair<IdString, Const>> parameters;
		vector<pair<IdString, int>> port_sizes;
		vector<tuple<IdString, int, SigBit>> connections;

		bool operator==(const merge_key_t &other) const {
			return type == other.type && connections == other.connections &&
					parameters == other.parameters && port_sizes == other.port_sizes;
		}

		unsigned int hash() const {
			unsigned int h = mkhash_init;
			h = mkhash(h, mkhash(type));
			h = mkhash(h, mkhash(parameters));
			h = mkhash(h, mkhash(connections));
			return h;
		}
	};

	dict<merge_key_t, pool<IdString>> merge_cache;
	pool<merge_key_t> fwd_merge_cache, bwd_merge_cache;

	void merge_cell_pair(Cell *cell_a, Cell *cell_b)
	{
		SigMap merged_map;
		merge_count++;

		SigSpec inputs_a, inputs_b;
		vector<string> input_names;

		for (auto &port_a : cell_a->connections())
		{
			SigSpec bits_a = sigmap(port_a.second);
			SigSpec bits_b = sigmap(cell_b->getPort(port_a.first));

			log_assert(GetSize(bits_a) == GetSize(bits_b));

			if (!cell_a->output(port_a.first))
				for (int i = 0; i < GetSize(bits_a); i++)
					if (bits_a[i] != bits_b[i]) {
						inputs_a.append(bits_a[i]);
						inputs_b.append(bits_b[i]);
						input_names.push_back(GetSize(bits_a) == 1 ? port_a.first.str() :
								stringf("%s[%d]", log_id(port_a.first), i));
					}
		}

		for (int i = 0; i < GetSize(inputs_a); i++) {
			SigBit bit_a = inputs_a[i], bit_b = inputs_b[i];
			SigBit bit_y = module->addWire(NEW_ID);
			log("        New $equiv for input %s: A: %s, B: %s, Y: %s\n",
					input_names[i].c_str(), log_signal(bit_a), log_signal(bit_b), log_signal(bit_y));
			module->addEquiv(NEW_ID, bit_a, bit_b, bit_y);
			merged_map.add(bit_a, bit_y);
			merged_map.add(bit_b, bit_y);
		}

		std::vector<IdString> outport_names, inport_names;

		for (auto &port_a : cell_a->connections())
			if (cell_a->output(port_a.first))
				outport_names.push_back(port_a.first);
			else
				inport_names.push_back(port_a.first);

		for (auto &pn : inport_names)
			cell_a->setPort(pn, merged_map(sigmap(cell_a->getPort(pn))));

		for (auto &pn : outport_names) {
			SigSpec sig_a = cell_a->getPort(pn);
			SigSpec sig_b = cell_b->getPort(pn);
			module->connect(sig_b, sig_a);
		}

		auto merged_attr = cell_b->get_strpool_attribute("\\equiv_merged");
		merged_attr.insert(log_id(cell_b));
		cell_a->add_strpool_attribute("\\equiv_merged", merged_attr);
		module->remove(cell_b);
	}

	EquivStructWorker(Module *module, bool mode_fwd, bool mode_icells) :
			module(module), sigmap(module), equiv_bits(module),
			mode_fwd(mode_fwd), mode_icells(mode_icells), merge_count(0)
	{
		log("  Starting new iteration.\n");

		pool<SigBit> equiv_inputs;
		pool<IdString> cells;

		for (auto cell : module->selected_cells())
			if (cell->type == "$equiv") {
				SigBit sig_a = sigmap(cell->getPort("\\A").as_bit());
				SigBit sig_b = sigmap(cell->getPort("\\B").as_bit());
				equiv_bits.add(sig_b, sig_a);
				equiv_inputs.insert(sig_a);
				equiv_inputs.insert(sig_b);
				cells.insert(cell->name);
			} else {
				if (mode_icells || module->design->module(cell->type))
					cells.insert(cell->name);
			}

		for (auto cell : module->selected_cells())
			if (cell->type == "$equiv") {
				SigBit sig_a = sigmap(cell->getPort("\\A").as_bit());
				SigBit sig_b = sigmap(cell->getPort("\\B").as_bit());
				SigBit sig_y = sigmap(cell->getPort("\\Y").as_bit());
				if (sig_a == sig_b && equiv_inputs.count(sig_y)) {
					log("    Purging redundant $equiv cell %s.\n", log_id(cell));
					module->remove(cell);
					merge_count++;
				}
			}

		if (merge_count > 0)
			return;

		for (auto cell_name : cells)
		{
			merge_key_t key;
			vector<tuple<IdString, int, SigBit>> fwd_connections;

			Cell *cell = module->cell(cell_name);
			key.type = cell->type;

			for (auto &it : cell->parameters)
				key.parameters.push_back(it);
			std::sort(key.parameters.begin(), key.parameters.end());

			for (auto &it : cell->connections())
				key.port_sizes.push_back(make_pair(it.first, GetSize(it.second)));
			std::sort(key.port_sizes.begin(), key.port_sizes.end());

			for (auto &conn : cell->connections())
			{
				if (cell->input(conn.first)) {
					SigSpec sig = sigmap(conn.second);
					for (int i = 0; i < GetSize(sig); i++)
						fwd_connections.push_back(make_tuple(conn.first, i, sig[i]));
				}

				if (cell->output(conn.first)) {
					SigSpec sig = equiv_bits(conn.second);
					for (int i = 0; i < GetSize(sig); i++) {
						key.connections.clear();
						key.connections.push_back(make_tuple(conn.first, i, sig[i]));

						if (merge_cache.count(key))
							bwd_merge_cache.insert(key);
						merge_cache[key].insert(cell_name);
					}
				}
			}

			std::sort(fwd_connections.begin(), fwd_connections.end());
			key.connections.swap(fwd_connections);

			if (merge_cache.count(key))
				fwd_merge_cache.insert(key);
			merge_cache[key].insert(cell_name);
		}

		for (int phase = 0; phase < 2; phase++)
		{
			auto &queue = phase ? bwd_merge_cache : fwd_merge_cache;

			for (auto &key : queue)
			{
				const char *strategy = nullptr;
				vector<Cell*> gold_cells, gate_cells, other_cells;
				vector<pair<Cell*, Cell*>> cell_pairs;

				for (auto cell_name : merge_cache[key]) {
					Cell *c = module->cell(cell_name);
					if (c != nullptr) {
						string n = cell_name.str();
						if (GetSize(n) > 5 && n.substr(GetSize(n)-5) == "_gold")
							gold_cells.push_back(c);
						else if (GetSize(n) > 5 && n.substr(GetSize(n)-5) == "_gate")
							gate_cells.push_back(c);
						else
							other_cells.push_back(c);
					}
				}

				if (GetSize(gold_cells) > 1 || GetSize(gate_cells) > 1 || GetSize(other_cells) > 1)
				{
					strategy = "deduplicate";
					for (int i = 0; i+1 < GetSize(gold_cells); i += 2)
						cell_pairs.push_back(make_pair(gold_cells[i], gold_cells[i+1]));
					for (int i = 0; i+1 < GetSize(gate_cells); i += 2)
						cell_pairs.push_back(make_pair(gate_cells[i], gate_cells[i+1]));
					for (int i = 0; i+1 < GetSize(other_cells); i += 2)
						cell_pairs.push_back(make_pair(other_cells[i], other_cells[i+1]));
					goto run_strategy;
				}

				if (GetSize(gold_cells) == 1 && GetSize(gate_cells) == 1)
				{
					strategy = "gold-gate-pairs";
					cell_pairs.push_back(make_pair(gold_cells[0], gate_cells[0]));
					goto run_strategy;
				}

				if (GetSize(gold_cells) == 1 && GetSize(other_cells) == 1)
				{
					strategy = "gold-guess";
					cell_pairs.push_back(make_pair(gold_cells[0], other_cells[0]));
					goto run_strategy;
				}

				if (GetSize(other_cells) == 1 && GetSize(gate_cells) == 1)
				{
					strategy = "gate-guess";
					cell_pairs.push_back(make_pair(other_cells[0], gate_cells[0]));
					goto run_strategy;
				}

				log_assert(GetSize(gold_cells) + GetSize(gate_cells) + GetSize(other_cells) < 2);
				continue;

			run_strategy:
				log("    %s merging %d cells (from group of %d) using strategy %s:\n", phase ? "Bwd" : "Fwd",
						2*GetSize(cell_pairs), GetSize(gold_cells) + GetSize(gate_cells) + GetSize(other_cells), strategy);
				for (auto it : cell_pairs) {
					log("      Merging cells %s and %s.\n", log_id(it.first),  log_id(it.second));
					merge_cell_pair(it.first, it.second);
				}
			}

			if (merge_count > 0)
				return;
		}

		log("    Nothing to merge.\n");
	}
};

struct EquivStructPass : public Pass {
	EquivStructPass() : Pass("equiv_struct", "structural equivalence checking") { }
	virtual void help()
	{
		//   |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
		log("\n");
		log("    equiv_struct [options] [selection]\n");
		log("\n");
		log("This command adds additional $equiv cells based on the assumption that the\n");
		log("gold and gate circuit are structurally equivalent. Note that this can introduce\n");
		log("bad $equiv cells in cases where the netlists are not structurally equivalent,\n");
		log("for example when analyzing circuits with cells with commutative inputs. This\n");
		log("command will also de-duplicate gates.\n");
		log("\n");
		log("    -fwd\n");
		log("        by default this command performans forward sweeps until nothing can\n");
		log("        be merged by forwards sweeps, the backward sweeps until forward\n");
		log("        sweeps are effective again. with this option set only forward sweeps\n");
		log("        are performed.\n");
		log("\n");
		log("    -icells\n");
		log("        by default, the internal RTL and gate cell types are ignored. add\n");
		log("        this option to also process those cell types with this command.\n");
		log("\n");
	}
	virtual void execute(std::vector<std::string> args, Design *design)
	{
		bool mode_icells = false;
		bool mode_fwd = false;

		log_header("Executing EQUIV_STRUCT pass.\n");

		size_t argidx;
		for (argidx = 1; argidx < args.size(); argidx++) {
			if (args[argidx] == "-fwd") {
				mode_fwd = true;
				continue;
			}
			if (args[argidx] == "-icells") {
				mode_icells = true;
				continue;
			}
			break;
		}
		extra_args(args, argidx, design);

		for (auto module : design->selected_modules()) {
			int module_merge_count = 0;
			log("Running equiv_struct on module %s:\n", log_id(module));
			while (1) {
				EquivStructWorker worker(module, mode_fwd, mode_icells);
				if (worker.merge_count == 0)
					break;
				module_merge_count += worker.merge_count;
			}
			if (module_merge_count)
				log("  Performed a total of %d merges in module %s.\n", module_merge_count, log_id(module));
		}
	}
} EquivStructPass;

PRIVATE_NAMESPACE_END
<commit_msg>Added "equiv_struct -maxiter <N>"<commit_after>/*
 *  yosys -- Yosys Open SYnthesis Suite
 *
 *  Copyright (C) 2012  Clifford Wolf <clifford@clifford.at>
 *
 *  Permission to use, copy, modify, and/or distribute this software for any
 *  purpose with or without fee is hereby granted, provided that the above
 *  copyright notice and this permission notice appear in all copies.
 *
 *  THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
 *  WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
 *  MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
 *  ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
 *  WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
 *  ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
 *  OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
 *
 */

#include "kernel/yosys.h"
#include "kernel/sigtools.h"

USING_YOSYS_NAMESPACE
PRIVATE_NAMESPACE_BEGIN

struct EquivStructWorker
{
	Module *module;
	SigMap sigmap;
	SigMap equiv_bits;
	bool mode_fwd;
	bool mode_icells;
	int merge_count;

	struct merge_key_t
	{
		IdString type;
		vector<pair<IdString, Const>> parameters;
		vector<pair<IdString, int>> port_sizes;
		vector<tuple<IdString, int, SigBit>> connections;

		bool operator==(const merge_key_t &other) const {
			return type == other.type && connections == other.connections &&
					parameters == other.parameters && port_sizes == other.port_sizes;
		}

		unsigned int hash() const {
			unsigned int h = mkhash_init;
			h = mkhash(h, mkhash(type));
			h = mkhash(h, mkhash(parameters));
			h = mkhash(h, mkhash(connections));
			return h;
		}
	};

	dict<merge_key_t, pool<IdString>> merge_cache;
	pool<merge_key_t> fwd_merge_cache, bwd_merge_cache;

	void merge_cell_pair(Cell *cell_a, Cell *cell_b)
	{
		SigMap merged_map;
		merge_count++;

		SigSpec inputs_a, inputs_b;
		vector<string> input_names;

		for (auto &port_a : cell_a->connections())
		{
			SigSpec bits_a = sigmap(port_a.second);
			SigSpec bits_b = sigmap(cell_b->getPort(port_a.first));

			log_assert(GetSize(bits_a) == GetSize(bits_b));

			if (!cell_a->output(port_a.first))
				for (int i = 0; i < GetSize(bits_a); i++)
					if (bits_a[i] != bits_b[i]) {
						inputs_a.append(bits_a[i]);
						inputs_b.append(bits_b[i]);
						input_names.push_back(GetSize(bits_a) == 1 ? port_a.first.str() :
								stringf("%s[%d]", log_id(port_a.first), i));
					}
		}

		for (int i = 0; i < GetSize(inputs_a); i++) {
			SigBit bit_a = inputs_a[i], bit_b = inputs_b[i];
			SigBit bit_y = module->addWire(NEW_ID);
			log("        New $equiv for input %s: A: %s, B: %s, Y: %s\n",
					input_names[i].c_str(), log_signal(bit_a), log_signal(bit_b), log_signal(bit_y));
			module->addEquiv(NEW_ID, bit_a, bit_b, bit_y);
			merged_map.add(bit_a, bit_y);
			merged_map.add(bit_b, bit_y);
		}

		std::vector<IdString> outport_names, inport_names;

		for (auto &port_a : cell_a->connections())
			if (cell_a->output(port_a.first))
				outport_names.push_back(port_a.first);
			else
				inport_names.push_back(port_a.first);

		for (auto &pn : inport_names)
			cell_a->setPort(pn, merged_map(sigmap(cell_a->getPort(pn))));

		for (auto &pn : outport_names) {
			SigSpec sig_a = cell_a->getPort(pn);
			SigSpec sig_b = cell_b->getPort(pn);
			module->connect(sig_b, sig_a);
		}

		auto merged_attr = cell_b->get_strpool_attribute("\\equiv_merged");
		merged_attr.insert(log_id(cell_b));
		cell_a->add_strpool_attribute("\\equiv_merged", merged_attr);
		module->remove(cell_b);
	}

	EquivStructWorker(Module *module, bool mode_fwd, bool mode_icells, int iter_num) :
			module(module), sigmap(module), equiv_bits(module),
			mode_fwd(mode_fwd), mode_icells(mode_icells), merge_count(0)
	{
		log("  Starting iteration %d.\n", iter_num);

		pool<SigBit> equiv_inputs;
		pool<IdString> cells;

		for (auto cell : module->selected_cells())
			if (cell->type == "$equiv") {
				SigBit sig_a = sigmap(cell->getPort("\\A").as_bit());
				SigBit sig_b = sigmap(cell->getPort("\\B").as_bit());
				equiv_bits.add(sig_b, sig_a);
				equiv_inputs.insert(sig_a);
				equiv_inputs.insert(sig_b);
				cells.insert(cell->name);
			} else {
				if (mode_icells || module->design->module(cell->type))
					cells.insert(cell->name);
			}

		for (auto cell : module->selected_cells())
			if (cell->type == "$equiv") {
				SigBit sig_a = sigmap(cell->getPort("\\A").as_bit());
				SigBit sig_b = sigmap(cell->getPort("\\B").as_bit());
				SigBit sig_y = sigmap(cell->getPort("\\Y").as_bit());
				if (sig_a == sig_b && equiv_inputs.count(sig_y)) {
					log("    Purging redundant $equiv cell %s.\n", log_id(cell));
					module->remove(cell);
					merge_count++;
				}
			}

		if (merge_count > 0)
			return;

		for (auto cell_name : cells)
		{
			merge_key_t key;
			vector<tuple<IdString, int, SigBit>> fwd_connections;

			Cell *cell = module->cell(cell_name);
			key.type = cell->type;

			for (auto &it : cell->parameters)
				key.parameters.push_back(it);
			std::sort(key.parameters.begin(), key.parameters.end());

			for (auto &it : cell->connections())
				key.port_sizes.push_back(make_pair(it.first, GetSize(it.second)));
			std::sort(key.port_sizes.begin(), key.port_sizes.end());

			for (auto &conn : cell->connections())
			{
				if (cell->input(conn.first)) {
					SigSpec sig = sigmap(conn.second);
					for (int i = 0; i < GetSize(sig); i++)
						fwd_connections.push_back(make_tuple(conn.first, i, sig[i]));
				}

				if (cell->output(conn.first)) {
					SigSpec sig = equiv_bits(conn.second);
					for (int i = 0; i < GetSize(sig); i++) {
						key.connections.clear();
						key.connections.push_back(make_tuple(conn.first, i, sig[i]));

						if (merge_cache.count(key))
							bwd_merge_cache.insert(key);
						merge_cache[key].insert(cell_name);
					}
				}
			}

			std::sort(fwd_connections.begin(), fwd_connections.end());
			key.connections.swap(fwd_connections);

			if (merge_cache.count(key))
				fwd_merge_cache.insert(key);
			merge_cache[key].insert(cell_name);
		}

		for (int phase = 0; phase < 2; phase++)
		{
			auto &queue = phase ? bwd_merge_cache : fwd_merge_cache;

			for (auto &key : queue)
			{
				const char *strategy = nullptr;
				vector<Cell*> gold_cells, gate_cells, other_cells;
				vector<pair<Cell*, Cell*>> cell_pairs;

				for (auto cell_name : merge_cache[key]) {
					Cell *c = module->cell(cell_name);
					if (c != nullptr) {
						string n = cell_name.str();
						if (GetSize(n) > 5 && n.substr(GetSize(n)-5) == "_gold")
							gold_cells.push_back(c);
						else if (GetSize(n) > 5 && n.substr(GetSize(n)-5) == "_gate")
							gate_cells.push_back(c);
						else
							other_cells.push_back(c);
					}
				}

				if (GetSize(gold_cells) > 1 || GetSize(gate_cells) > 1 || GetSize(other_cells) > 1)
				{
					strategy = "deduplicate";
					for (int i = 0; i+1 < GetSize(gold_cells); i += 2)
						cell_pairs.push_back(make_pair(gold_cells[i], gold_cells[i+1]));
					for (int i = 0; i+1 < GetSize(gate_cells); i += 2)
						cell_pairs.push_back(make_pair(gate_cells[i], gate_cells[i+1]));
					for (int i = 0; i+1 < GetSize(other_cells); i += 2)
						cell_pairs.push_back(make_pair(other_cells[i], other_cells[i+1]));
					goto run_strategy;
				}

				if (GetSize(gold_cells) == 1 && GetSize(gate_cells) == 1)
				{
					strategy = "gold-gate-pairs";
					cell_pairs.push_back(make_pair(gold_cells[0], gate_cells[0]));
					goto run_strategy;
				}

				if (GetSize(gold_cells) == 1 && GetSize(other_cells) == 1)
				{
					strategy = "gold-guess";
					cell_pairs.push_back(make_pair(gold_cells[0], other_cells[0]));
					goto run_strategy;
				}

				if (GetSize(other_cells) == 1 && GetSize(gate_cells) == 1)
				{
					strategy = "gate-guess";
					cell_pairs.push_back(make_pair(other_cells[0], gate_cells[0]));
					goto run_strategy;
				}

				log_assert(GetSize(gold_cells) + GetSize(gate_cells) + GetSize(other_cells) < 2);
				continue;

			run_strategy:
				log("    %s merging %d cells (from group of %d) using strategy %s:\n", phase ? "Bwd" : "Fwd",
						2*GetSize(cell_pairs), GetSize(gold_cells) + GetSize(gate_cells) + GetSize(other_cells), strategy);
				for (auto it : cell_pairs) {
					log("      Merging cells %s and %s.\n", log_id(it.first),  log_id(it.second));
					merge_cell_pair(it.first, it.second);
				}
			}

			if (merge_count > 0)
				return;
		}

		log("    Nothing to merge.\n");
	}
};

struct EquivStructPass : public Pass {
	EquivStructPass() : Pass("equiv_struct", "structural equivalence checking") { }
	virtual void help()
	{
		//   |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
		log("\n");
		log("    equiv_struct [options] [selection]\n");
		log("\n");
		log("This command adds additional $equiv cells based on the assumption that the\n");
		log("gold and gate circuit are structurally equivalent. Note that this can introduce\n");
		log("bad $equiv cells in cases where the netlists are not structurally equivalent,\n");
		log("for example when analyzing circuits with cells with commutative inputs. This\n");
		log("command will also de-duplicate gates.\n");
		log("\n");
		log("    -fwd\n");
		log("        by default this command performans forward sweeps until nothing can\n");
		log("        be merged by forwards sweeps, the backward sweeps until forward\n");
		log("        sweeps are effective again. with this option set only forward sweeps\n");
		log("        are performed.\n");
		log("\n");
		log("    -icells\n");
		log("        by default, the internal RTL and gate cell types are ignored. add\n");
		log("        this option to also process those cell types with this command.\n");
		log("\n");
		log("    -maxiter <N>\n");
		log("        maximum number of iterations to run before aborting\n");
		log("\n");
	}
	virtual void execute(std::vector<std::string> args, Design *design)
	{
		bool mode_icells = false;
		bool mode_fwd = false;
		int max_iter = -1;

		log_header("Executing EQUIV_STRUCT pass.\n");

		size_t argidx;
		for (argidx = 1; argidx < args.size(); argidx++) {
			if (args[argidx] == "-fwd") {
				mode_fwd = true;
				continue;
			}
			if (args[argidx] == "-icells") {
				mode_icells = true;
				continue;
			}
			if (args[argidx] == "-maxiter" && argidx+1 < args.size()) {
				max_iter = atoi(args[++argidx].c_str());
				continue;
			}
			break;
		}
		extra_args(args, argidx, design);

		for (auto module : design->selected_modules()) {
			int module_merge_count = 0;
			log("Running equiv_struct on module %s:\n", log_id(module));
			for (int iter = 0;; iter++) {
				if (iter == max_iter) {
					log("  Reached iteration limit of %d.\n", iter);
					break;
				}
				EquivStructWorker worker(module, mode_fwd, mode_icells, iter+1);
				if (worker.merge_count == 0)
					break;
				module_merge_count += worker.merge_count;
			}
			if (module_merge_count)
				log("  Performed a total of %d merges in module %s.\n", module_merge_count, log_id(module));
		}
	}
} EquivStructPass;

PRIVATE_NAMESPACE_END
<|endoftext|>


<commit_before>
<commit_msg>Delete Onboard_LED_Flasher.cpp<commit_after><|endoftext|>


<commit_before>#pragma once
#include <arm_neon.h>

// architecture-specific implementations of primitives for ARM NEON

template <typename AccType>
void gemmBinary_neon_tile2x2x2(uint64_t * A, uint64_t * BT, AccType * CT, AccType alpha,
uint64_t rowsA, uint64_t depth_words, uint64_t rowsBT) {
  assert(rowsA % 2 == 0);
  assert(rowsBT % 2 == 0);
  assert(depth_words % 2 == 0);

  for(uint64_t rBT = 0; rBT < rowsBT; rBT+=2) {
    uint64_t * BTptr = &BT[rBT * depth_words];
    for(uint64_t rA = 0; rA < rowsA; rA+=2) {
      uint64_t * Aptr = &A[rA * depth_words];
      uint64_t acc[4] = {0};
      uint8x16_t acc_neon[4];
      // TODO could keep things in 16-bit accumulators for perf.
      uint64x2_t acc2_neon[4];
      // initialize accumulators to zero
      for(int init = 0; init < 4; init++) {
        acc_neon[init] = vcombine_u8(vcreate_u8(0), vcreate_u8(0));
        acc2_neon[init] = vcombine_u64(vcreate_u64(0), vcreate_u64(0));
      }

      for(uint64_t d = 0; d < depth_words; d+=2) {
        uint8x16_t a0, b0, a1, b1;
        a0 = vld1q_u8((uint8_t *) &Aptr[d]);
        a1 = vld1q_u8((uint8_t *) &Aptr[d + depth_words]);
        b0 = vld1q_u8((uint8_t *) &BTptr[d]);
        b1 = vld1q_u8((uint8_t *) &BTptr[d + depth_words]);

        acc_neon[0] = vaddq_u8(acc_neon[0], vcntq_u8(vandq_u8(a0, b0)));
        acc_neon[1] = vaddq_u8(acc_neon[1], vcntq_u8(vandq_u8(a0, b1)));
        acc_neon[2] = vaddq_u8(acc_neon[2], vcntq_u8(vandq_u8(a1, b0)));
        acc_neon[3] = vaddq_u8(acc_neon[3], vcntq_u8(vandq_u8(a1, b1)));

        if(d & 15L == 0L) {
          /* hsum over 8-bit accumulators when end or overflow*/
          for(int init = 0; init < 4; init++) {
            acc2_neon[init] = vaddq_u64(acc2_neon[init], vpaddlq_u32(vpaddlq_u16(vpaddlq_u8(acc_neon[init]))));
            acc_neon[init] = vcombine_u8(vcreate_u8(0), vcreate_u8(0));
          }
        }
      }
      /* move into regular accumulators */
      for(int init = 0; init < 4; init++) {
        uint64_t tmp[2];
        acc2_neon[init] = vaddq_u64(acc2_neon[init], vpaddlq_u32(vpaddlq_u16(vpaddlq_u8(acc_neon[init]))));
        vst1q_u64(tmp, acc2_neon[init]);
        acc[init] = tmp[0] + tmp[1];
      }

      CT[rBT * rowsA + rA] += acc[0] * alpha;
      CT[(rBT + 1) * rowsA + rA] += acc[1] * alpha;
      CT[rBT * rowsA + rA + 1] += acc[2] * alpha;
      CT[(rBT + 1) * rowsA + rA + 1] += acc[3] * alpha;
    }
  }
}

/* Bit-serial GEMM via a series of calls to gemmBinary.
   Note that rhs must be given in transposed form, and the result is also
   produced transposed.
*/
template <typename AccType>
void gemmBitSerial_neon_usingBinary(BitSerialMatrix * lhs, BitSerialMatrix * rhs, AccType * res) {
  // ensure that matrix shapes are compatible
  assert(lhs->ncols == rhs->ncols);
  const uint64_t lhsbits = lhs->nbits;
  const uint64_t rhsbits = rhs->nbits;
  // clear contents of result matrix by setting everything to zero
  memset(res, 0, lhs->nrows*rhs->nrows*sizeof(AccType));
  // call binary GEMM for each bit position
  for(uint64_t lbit = 0; lbit < lhsbits; lbit++) {
    bool neg_lhs = lhs->issigned && (lbit == lhsbits-1);
    for(uint64_t rbit = 0; rbit < rhsbits; rbit++) {
      bool neg_rhs = rhs->issigned && (rbit == rhsbits-1);
      bool neg = neg_rhs ^ neg_lhs;
      AccType alpha = neg ? -(1 << (lbit+rbit)) : (1 << (lbit+rbit));
      if(lhs->nrows % 2 == 0 && rhs->nrows % 2 == 0 && lhs->wordsPerRow() % 2 == 0) {
        gemmBinary_neon_tile2x2x2(lhs->bitplaneptr(lbit), rhs->bitplaneptr(rbit), res, alpha, lhs->nrows, lhs->wordsPerRow(), rhs->nrows);
      }
      else {
        gemmBinary_generic_naive(lhs->bitplaneptr(lbit), rhs->bitplaneptr(rbit), res, alpha, lhs->nrows, lhs->wordsPerRow(), rhs->nrows);
      }
    }
  }
}
<commit_msg>fix precedence<commit_after>#pragma once
#include <arm_neon.h>

// architecture-specific implementations of primitives for ARM NEON

template <typename AccType>
void gemmBinary_neon_tile2x2x2(uint64_t * A, uint64_t * BT, AccType * CT, AccType alpha,
uint64_t rowsA, uint64_t depth_words, uint64_t rowsBT) {
  assert(rowsA % 2 == 0);
  assert(rowsBT % 2 == 0);
  assert(depth_words % 2 == 0);

  for(uint64_t rBT = 0; rBT < rowsBT; rBT+=2) {
    uint64_t * BTptr = &BT[rBT * depth_words];
    for(uint64_t rA = 0; rA < rowsA; rA+=2) {
      uint64_t * Aptr = &A[rA * depth_words];
      uint64_t acc[4] = {0};
      uint8x16_t acc_neon[4];
      // TODO could keep things in 16-bit accumulators for perf.
      uint64x2_t acc2_neon[4];
      // initialize accumulators to zero
      for(int init = 0; init < 4; init++) {
        acc_neon[init] = vcombine_u8(vcreate_u8(0), vcreate_u8(0));
        acc2_neon[init] = vcombine_u64(vcreate_u64(0), vcreate_u64(0));
      }

      for(uint64_t d = 0; d < depth_words; d+=2) {
        uint8x16_t a0, b0, a1, b1;
        a0 = vld1q_u8((uint8_t *) &Aptr[d]);
        a1 = vld1q_u8((uint8_t *) &Aptr[d + depth_words]);
        b0 = vld1q_u8((uint8_t *) &BTptr[d]);
        b1 = vld1q_u8((uint8_t *) &BTptr[d + depth_words]);

        acc_neon[0] = vaddq_u8(acc_neon[0], vcntq_u8(vandq_u8(a0, b0)));
        acc_neon[1] = vaddq_u8(acc_neon[1], vcntq_u8(vandq_u8(a0, b1)));
        acc_neon[2] = vaddq_u8(acc_neon[2], vcntq_u8(vandq_u8(a1, b0)));
        acc_neon[3] = vaddq_u8(acc_neon[3], vcntq_u8(vandq_u8(a1, b1)));

        if((d & 15L) == 0L) {
          /* hsum over 8-bit accumulators when end or overflow*/
          for(int init = 0; init < 4; init++) {
            acc2_neon[init] = vaddq_u64(acc2_neon[init], vpaddlq_u32(vpaddlq_u16(vpaddlq_u8(acc_neon[init]))));
            acc_neon[init] = vcombine_u8(vcreate_u8(0), vcreate_u8(0));
          }
        }
      }
      /* move into regular accumulators */
      for(int init = 0; init < 4; init++) {
        uint64_t tmp[2];
        acc2_neon[init] = vaddq_u64(acc2_neon[init], vpaddlq_u32(vpaddlq_u16(vpaddlq_u8(acc_neon[init]))));
        vst1q_u64(tmp, acc2_neon[init]);
        acc[init] = tmp[0] + tmp[1];
      }

      CT[rBT * rowsA + rA] += acc[0] * alpha;
      CT[(rBT + 1) * rowsA + rA] += acc[1] * alpha;
      CT[rBT * rowsA + rA + 1] += acc[2] * alpha;
      CT[(rBT + 1) * rowsA + rA + 1] += acc[3] * alpha;
    }
  }
}

/* Bit-serial GEMM via a series of calls to gemmBinary.
   Note that rhs must be given in transposed form, and the result is also
   produced transposed.
*/
template <typename AccType>
void gemmBitSerial_neon_usingBinary(BitSerialMatrix * lhs, BitSerialMatrix * rhs, AccType * res) {
  // ensure that matrix shapes are compatible
  assert(lhs->ncols == rhs->ncols);
  const uint64_t lhsbits = lhs->nbits;
  const uint64_t rhsbits = rhs->nbits;
  // clear contents of result matrix by setting everything to zero
  memset(res, 0, lhs->nrows*rhs->nrows*sizeof(AccType));
  // call binary GEMM for each bit position
  for(uint64_t lbit = 0; lbit < lhsbits; lbit++) {
    bool neg_lhs = lhs->issigned && (lbit == lhsbits-1);
    for(uint64_t rbit = 0; rbit < rhsbits; rbit++) {
      bool neg_rhs = rhs->issigned && (rbit == rhsbits-1);
      bool neg = neg_rhs ^ neg_lhs;
      AccType alpha = neg ? -(1 << (lbit+rbit)) : (1 << (lbit+rbit));
      if(lhs->nrows % 2 == 0 && rhs->nrows % 2 == 0 && lhs->wordsPerRow() % 2 == 0) {
        gemmBinary_neon_tile2x2x2(lhs->bitplaneptr(lbit), rhs->bitplaneptr(rbit), res, alpha, lhs->nrows, lhs->wordsPerRow(), rhs->nrows);
      }
      else {
        gemmBinary_generic_naive(lhs->bitplaneptr(lbit), rhs->bitplaneptr(rbit), res, alpha, lhs->nrows, lhs->wordsPerRow(), rhs->nrows);
      }
    }
  }
}
<|endoftext|>


<commit_before>#include <xerus.h>
#include <iostream>
#include <string>
#include <fstream>

using namespace xerus;

const size_t MAX_NUM_PER_SITE = 32;

Tensor create_M() {
	Tensor M = -1*Tensor::identity({MAX_NUM_PER_SITE, MAX_NUM_PER_SITE});
	for (size_t i = 0; i < MAX_NUM_PER_SITE-1; ++i) {
		M[{i+1, i}] = 1.0;
	}
	return M;
}

Tensor create_L() {
	Tensor L({MAX_NUM_PER_SITE, MAX_NUM_PER_SITE});
	L.modify_diagonal_entries([](value_t& _x, const size_t _i) {
		_x = double(_i)/double(_i+5);
	});
	return L;
}

Tensor create_S() {
	Tensor S({MAX_NUM_PER_SITE, MAX_NUM_PER_SITE});

	// Set diagonal
	for (size_t i = 0; i < MAX_NUM_PER_SITE; ++i) {
		S[{i, i}] = -double(i);
	}

	// Set offdiagonal
	for (size_t i = 0; i < MAX_NUM_PER_SITE-1; ++i) {
		S[{i, i+1}] = double(i+1);
	}
	return 0.07*S;
}

TTOperator create_operator(const size_t _degree) {
	const Index i, j, k, l;

	// Create matrices
	const Tensor M = create_M();
	const Tensor S = create_S();
	const Tensor L = create_L();
	const Tensor Sstar = 0.7*M+S;
	const Tensor I = Tensor::identity({MAX_NUM_PER_SITE, MAX_NUM_PER_SITE});

	// Create empty TTOperator
	TTOperator A(2*_degree);

	Tensor comp;

	// Create first component
	comp(i, j, k, l) =
		Sstar(j, k)*Tensor::dirac({1, 3}, 0)(i, l)
		+   L(j, k)*Tensor::dirac({1, 3}, 1)(i, l)
		+   I(j, k)*Tensor::dirac({1, 3}, 2)(i, l);

	A.set_component(0, comp);

	// Create middle components
	comp(i, j, k, l) =
		  I(j, k) * Tensor::dirac({3, 3}, {0, 0})(i, l)
		+ M(j, k) * Tensor::dirac({3, 3}, {1, 0})(i, l)
		+ S(j, k) * Tensor::dirac({3, 3}, {2, 0})(i, l)
		+ L(j, k) * Tensor::dirac({3, 3}, {2, 1})(i, l)
		+ I(j, k) * Tensor::dirac({3, 3}, {2, 2})(i, l);

	for(size_t c = 1; c+1 < _degree; ++c) {
		A.set_component(c, comp);
	}

	// Create last component
	comp(i, j, k, l) =
		  I(j, k)*Tensor::dirac({3, 1}, 0)(i, l)
		+ M(j, k)*Tensor::dirac({3, 1}, 1)(i, l)
		+ S(j, k)*Tensor::dirac({3, 1}, 2)(i, l);

	A.set_component(_degree-1, comp);

	return A;
}

/// @brief calculates the one-norm of a TTTensor, assuming that all entries in it are positive
double one_norm(const TTTensor &_x) {
	Index j;
	return double(_x(j&0) * TTTensor::ones(_x.dimensions)(j&0));
}

std::vector<TTTensor> implicit_euler(const TTOperator& _A, TTTensor _x,
			const double _stepSize, const size_t _n)
{
	const TTOperator op = TTOperator::identity(_A.dimensions)-_stepSize*_A;

	Index j,k;
	auto ourALS = ALS_SPD;
	ourALS.convergenceEpsilon = 0;
	ourALS.numHalfSweeps = 2;

	std::vector<TTTensor> results;
	TTTensor nextX = _x;
	results.push_back(_x);

	for(size_t i = 0; i < _n; ++i) {
		ourALS(op, nextX, _x);

		// Normalize
		double norm = one_norm(nextX);
		nextX /= norm;

		XERUS_LOG(iter, "Done itr " << i
				<< " residual: " << frob_norm(op(j/2,k/2)*nextX(k&0) - _x(j&0))
				<< " norm: " << norm);

		_x = nextX;
		results.push_back(_x);
	}

	return results;
}

double get_mean_concentration(const TTTensor& _res, const size_t _i) {
	const Index k,l;
	TensorNetwork result(_res);
		const Tensor weights({MAX_NUM_PER_SITE}, [](const size_t _k){
		return double(_k);
	});
	const Tensor ones = Tensor::ones({1, MAX_NUM_PER_SITE, 1});

	for (size_t j = 0; j < _res.degree(); ++j) {
		if (j == _i) {
			result(l&0) = result(k, l&1) * weights(k);
		} else {
			result(l&0) = result(k, l&1) * ones(k);
		}
	}
	// at this point the degree of 'result' is 0, so there is only one entry
	return result[{}];
}

void print_mean_concentrations_to_file(const std::vector<TTTensor> &_result) {
	std::fstream out("mean.dat", std::fstream::out);
	for (const auto& res : _result) {
		for (size_t k = 0; k < res.degree(); ++k) {
			out << get_mean_concentration(res, k) << ' ';
		}
		out << std::endl;
	}
}

int main() {
	const size_t numProteins = 10;
	const size_t numSteps = 300;
	const double stepSize = 1.0;
	const size_t rankX = 3;

	auto start = TTTensor::dirac(
			std::vector<size_t>(numProteins, MAX_NUM_PER_SITE),
			0
	);
	start.use_dense_representations();
	start += 1e-14 * TTTensor::random(
			start.dimensions,
			std::vector<size_t>(start.degree()-1, rankX-1)
	);
	const auto A = create_operator(numProteins);

	const auto results = implicit_euler(A, start, stepSize, numSteps);

	print_mean_concentrations_to_file(results);
}

<commit_msg>Fixed typo in Cascade example<commit_after>#include <xerus.h>
#include <iostream>
#include <string>
#include <fstream>

using namespace xerus;

const size_t MAX_NUM_PER_SITE = 32;

Tensor create_M() {
	Tensor M = -1*Tensor::identity({MAX_NUM_PER_SITE, MAX_NUM_PER_SITE});
	for (size_t i = 0; i < MAX_NUM_PER_SITE-1; ++i) {
		M[{i+1, i}] = 1.0;
	}
	return M;
}

Tensor create_L() {
	Tensor L({MAX_NUM_PER_SITE, MAX_NUM_PER_SITE});
	L.modify_diagonal_entries([](value_t& _x, const size_t _i) {
		_x = double(_i)/double(_i+5);
	});
	return L;
}

Tensor create_S() {
	Tensor S({MAX_NUM_PER_SITE, MAX_NUM_PER_SITE});

	// Set diagonal
	for (size_t i = 0; i < MAX_NUM_PER_SITE; ++i) {
		S[{i, i}] = -double(i);
	}

	// Set offdiagonal
	for (size_t i = 0; i < MAX_NUM_PER_SITE-1; ++i) {
		S[{i, i+1}] = double(i+1);
	}
	return 0.07*S;
}

TTOperator create_operator(const size_t _degree) {
	const Index i, j, k, l;

	// Create matrices
	const Tensor M = create_M();
	const Tensor S = create_S();
	const Tensor L = create_L();
	const Tensor Sstar = 0.7*M+S;
	const Tensor I = Tensor::identity({MAX_NUM_PER_SITE, MAX_NUM_PER_SITE});

	// Create empty TTOperator
	TTOperator A(2*_degree);

	Tensor comp;

	// Create first component
	comp(i, j, k, l) =
		Sstar(j, k)*Tensor::dirac({1, 3}, 0)(i, l)
		+   L(j, k)*Tensor::dirac({1, 3}, 1)(i, l)
		+   I(j, k)*Tensor::dirac({1, 3}, 2)(i, l);

	A.set_component(0, comp);

	// Create middle components
	comp(i, j, k, l) =
		  I(j, k) * Tensor::dirac({3, 3}, {0, 0})(i, l)
		+ M(j, k) * Tensor::dirac({3, 3}, {1, 0})(i, l)
		+ S(j, k) * Tensor::dirac({3, 3}, {2, 0})(i, l)
		+ L(j, k) * Tensor::dirac({3, 3}, {2, 1})(i, l)
		+ I(j, k) * Tensor::dirac({3, 3}, {2, 2})(i, l);

	for(size_t c = 1; c+1 < _degree; ++c) {
		A.set_component(c, comp);
	}

	// Create last component
	comp(i, j, k, l) =
		  I(j, k)*Tensor::dirac({3, 1}, 0)(i, l)
		+ M(j, k)*Tensor::dirac({3, 1}, 1)(i, l)
		+ S(j, k)*Tensor::dirac({3, 1}, 2)(i, l);

	A.set_component(_degree-1, comp);

	return A;
}

/// @brief calculates the one-norm of a TTTensor, assuming that all entries in it are positive
double one_norm(const TTTensor &_x) {
	Index j;
	return double(_x(j&0) * TTTensor::ones(_x.dimensions)(j&0));
}

std::vector<TTTensor> implicit_euler(const TTOperator& _A, TTTensor _x,
			const double _stepSize, const size_t _n)
{
	const TTOperator op = TTOperator::identity(_A.dimensions)-_stepSize*_A;

	Index j,k;
	auto ourALS = ALS_SPD;
	ourALS.convergenceEpsilon = 0;
	ourALS.numHalfSweeps = 2;

	std::vector<TTTensor> results;
	TTTensor nextX = _x;
	results.push_back(_x);

	for(size_t i = 0; i < _n; ++i) {
		ourALS(op, nextX, _x);

		// Normalize
		double norm = one_norm(nextX);
		nextX /= norm;

		XERUS_LOG(iter, "Done itr " << i
				<< " residual: " << frob_norm(op(j/2,k/2)*nextX(k&0) - _x(j&0))
				<< " norm: " << norm);

		_x = nextX;
		results.push_back(_x);
	}

	return results;
}

double get_mean_concentration(const TTTensor& _res, const size_t _i) {
	const Index k,l;
	TensorNetwork result(_res);
		const Tensor weights({MAX_NUM_PER_SITE}, [](const size_t _k){
		return double(_k);
	});
	const Tensor ones = Tensor::ones({MAX_NUM_PER_SITE});

	for (size_t j = 0; j < _res.degree(); ++j) {
		if (j == _i) {
			result(l&0) = result(k, l&1) * weights(k);
		} else {
			result(l&0) = result(k, l&1) * ones(k);
		}
	}
	// at this point the degree of 'result' is 0, so there is only one entry
	return result[{}];
}

void print_mean_concentrations_to_file(const std::vector<TTTensor> &_result) {
	std::fstream out("mean.dat", std::fstream::out);
	for (const auto& res : _result) {
		for (size_t k = 0; k < res.degree(); ++k) {
			out << get_mean_concentration(res, k) << ' ';
		}
		out << std::endl;
	}
}

int main() {
	const size_t numProteins = 10;
	const size_t numSteps = 300;
	const double stepSize = 1.0;
	const size_t rankX = 3;

	auto start = TTTensor::dirac(
			std::vector<size_t>(numProteins, MAX_NUM_PER_SITE),
			0
	);
	start.use_dense_representations();
	start += 1e-14 * TTTensor::random(
			start.dimensions,
			std::vector<size_t>(start.degree()-1, rankX-1)
	);
	const auto A = create_operator(numProteins);

	const auto results = implicit_euler(A, start, stepSize, numSteps);

	print_mean_concentrations_to_file(results);
}

<|endoftext|>


<commit_before>/*
  ==============================================================================

    This file was auto-generated by the Introjucer!

    It contains the basic framework code for a JUCE plugin processor.

  ==============================================================================
*/

#include "PluginProcessor.h"
#include "PluginEditor.h"

const char* RP2A03AudioProcessor::paramPulse1Level      = "pulse1Level";
const char* RP2A03AudioProcessor::paramPulse1DutyCycle  = "pulse1Duty";
const char* RP2A03AudioProcessor::paramPulse2Level      = "pulse2Level";
const char* RP2A03AudioProcessor::paramPulse2DutyCycle  = "pulse2Duty";
const char* RP2A03AudioProcessor::paramTriangleLevel    = "triangleLevel";
const char* RP2A03AudioProcessor::paramNoiseLevel       = "noiseLevel";
const char* RP2A03AudioProcessor::paramNoiseShort       = "noisePeriod";

//==============================================================================
RP2A03AudioProcessor::RP2A03AudioProcessor()
{
    addPluginParameter (new slParameter (paramPulse1Level,     "Pulse 1 Level",      "", 0.0f, 1.0f,  0.0f, 1.0f));
    addPluginParameter (new slParameter (paramPulse1DutyCycle, "Pulse 1 Duty Cycle", "", 0.0f, 3.0f,  1.0f, 0.0f));
    addPluginParameter (new slParameter (paramPulse2Level,     "Pulse 2 Level",      "", 0.0f, 1.0f,  0.0f, 0.0f));
    addPluginParameter (new slParameter (paramPulse2DutyCycle, "Pulse 2 Duty Cycle", "", 0.0f, 3.0f,  1.0f, 0.0f));
    addPluginParameter (new slParameter (paramTriangleLevel,   "Triangle Level",     "", 0.0f, 1.0f,  1.0f, 0.0f));
    addPluginParameter (new slParameter (paramNoiseLevel,      "Noise Level",        "", 0.0f, 1.0f,  0.0f, 0.0f));
    addPluginParameter (new slParameter (paramNoiseShort,      "Noise Short",        "", 0.0f, 1.0f,  1.0f, 0.0f));
}

RP2A03AudioProcessor::~RP2A03AudioProcessor()
{
}

//==============================================================================
void RP2A03AudioProcessor::prepareToPlay (double sampleRate, int samplesPerBlock)
{
    apu.sample_rate (sampleRate);
    apu.write_register (0x4015, 0x0F);
}

void RP2A03AudioProcessor::releaseResources()
{
}

void RP2A03AudioProcessor::runUntil (int& done, AudioSampleBuffer& buffer, int pos)
{
    int todo = jmin (pos, buffer.getNumSamples()) - done;

    while (todo > 0)
    {
        if (apu.samples_avail() == 0)
            apu.step();

        Simple_Apu::sample_t out[1024];
        int count = apu.read_samples(out, jmin (todo, int (apu.samples_avail()), 1024));

        float* data = buffer.getWritePointer (0, done);
        for (int i = 0; i < count; i++)
            data[i] = out[i] / 32768.0f;

        done += count;
        todo -= count;
    }

}

void RP2A03AudioProcessor::processBlock (AudioSampleBuffer& buffer, MidiBuffer& midi)
{
    const float p1Level = getParameter (paramPulse1Level)->getUserValue();
    const int p1Duty    = getParameter (paramPulse1DutyCycle)->getUserValue();
    const float p2Level = getParameter (paramPulse2Level)->getUserValue();
    const int p2Duty    = getParameter (paramPulse2DutyCycle)->getUserValue();
    const float tLevel  = getParameter (paramTriangleLevel)->getUserValue();
    const float nLevel  = getParameter (paramNoiseLevel)->getUserValue();
    const bool nShort   = getParameter (paramNoiseShort)->getUserValue();

    int done = 0;
    runUntil (done, buffer, 0);

    int pos = 0;
    MidiMessage msg;
    MidiBuffer::Iterator itr (midi);
    while (itr.getNextEvent (msg, pos))
    {
        runUntil (done, buffer, pos);

        if (msg.isNoteOn())
        {
            noteQueue.add (msg.getNoteNumber());
            velocity = msg.getVelocity();

            printf ("Note on: %d\n", msg.getNoteNumber());
        }
        else if (msg.isNoteOff())
        {
            noteQueue.removeFirstMatchingValue (msg.getNoteNumber());

            printf ("Note off: %d\n", msg.getNoteNumber());
        }

        const int curNote = noteQueue.size() > 0 ? noteQueue.getFirst() : -1;

        if (curNote != lastNote)
        {
            int v = curNote == -1 ? 0 : velocity;

            // Pulse 1
            apu.write_register (0x4000, (p1Duty << 6) | 0x30 | int (p1Level * v / 127.0 * 0xF));

            if (curNote != -1)
            {
                int period = 111860.8 / MidiMessage::getMidiNoteInHertz (curNote) - 1;

                apu.write_register (0x4002, period & 0xFF);
                apu.write_register (0x4003, (period >> 8) & 0x7);
            }

            // Pulse 2
            apu.write_register (0x4004, (p2Duty << 6) | 0x30 | int (p2Level * v / 127.0 * 0xF));

            if (curNote != -1)
            {
                int period = 111860.8 / MidiMessage::getMidiNoteInHertz (curNote) - 1;

                apu.write_register (0x4006, period & 0xFF);
                apu.write_register (0x4007, (period >> 8) & 0x7);
            }

            // Triangle
            apu.write_register (0x4008, curNote == -1 ? 0x00 : 0xFF);
            if (curNote != -1)
            {
                int period = tLevel == 1.0f ? 111860.8 / (MidiMessage::getMidiNoteInHertz (curNote) * 2) - 1 : 0;

                apu.write_register (0x400A, period & 0xFF);
                apu.write_register (0x400B, (period >> 8) & 0x7);
            }

            // Noise
            apu.write_register (0x400C, 0x30 | int (nLevel * v / 127.0 * 0xF));

            if (curNote != -1)
            {
                apu.write_register (0x400E, (nShort ? 0x80 : 0x00) | (curNote % 16));
                apu.write_register (0x400F, 0xFF);
            }

            lastNote = curNote;
        }
    }

    runUntil (done, buffer, buffer.getNumSamples());

    outputLevel.trackBuffer (buffer);
}

//==============================================================================
bool RP2A03AudioProcessor::hasEditor() const
{
    return true;
}

AudioProcessorEditor* RP2A03AudioProcessor::createEditor()
{
    return new RP2A03AudioProcessorEditor (*this);
}

//==============================================================================
// This creates new instances of the plugin..
AudioProcessor* JUCE_CALLTYPE createPluginFilter()
{
    return new RP2A03AudioProcessor();
}
<commit_msg>Handle all notes off<commit_after>/*
  ==============================================================================

    This file was auto-generated by the Introjucer!

    It contains the basic framework code for a JUCE plugin processor.

  ==============================================================================
*/

#include "PluginProcessor.h"
#include "PluginEditor.h"

const char* RP2A03AudioProcessor::paramPulse1Level      = "pulse1Level";
const char* RP2A03AudioProcessor::paramPulse1DutyCycle  = "pulse1Duty";
const char* RP2A03AudioProcessor::paramPulse2Level      = "pulse2Level";
const char* RP2A03AudioProcessor::paramPulse2DutyCycle  = "pulse2Duty";
const char* RP2A03AudioProcessor::paramTriangleLevel    = "triangleLevel";
const char* RP2A03AudioProcessor::paramNoiseLevel       = "noiseLevel";
const char* RP2A03AudioProcessor::paramNoiseShort       = "noisePeriod";

//==============================================================================
RP2A03AudioProcessor::RP2A03AudioProcessor()
{
    addPluginParameter (new slParameter (paramPulse1Level,     "Pulse 1 Level",      "", 0.0f, 1.0f,  0.0f, 1.0f));
    addPluginParameter (new slParameter (paramPulse1DutyCycle, "Pulse 1 Duty Cycle", "", 0.0f, 3.0f,  1.0f, 0.0f));
    addPluginParameter (new slParameter (paramPulse2Level,     "Pulse 2 Level",      "", 0.0f, 1.0f,  0.0f, 0.0f));
    addPluginParameter (new slParameter (paramPulse2DutyCycle, "Pulse 2 Duty Cycle", "", 0.0f, 3.0f,  1.0f, 0.0f));
    addPluginParameter (new slParameter (paramTriangleLevel,   "Triangle Level",     "", 0.0f, 1.0f,  1.0f, 0.0f));
    addPluginParameter (new slParameter (paramNoiseLevel,      "Noise Level",        "", 0.0f, 1.0f,  0.0f, 0.0f));
    addPluginParameter (new slParameter (paramNoiseShort,      "Noise Short",        "", 0.0f, 1.0f,  1.0f, 0.0f));
}

RP2A03AudioProcessor::~RP2A03AudioProcessor()
{
}

//==============================================================================
void RP2A03AudioProcessor::prepareToPlay (double sampleRate, int samplesPerBlock)
{
    apu.sample_rate (sampleRate);
    apu.write_register (0x4015, 0x0F);
}

void RP2A03AudioProcessor::releaseResources()
{
}

void RP2A03AudioProcessor::runUntil (int& done, AudioSampleBuffer& buffer, int pos)
{
    int todo = jmin (pos, buffer.getNumSamples()) - done;

    while (todo > 0)
    {
        if (apu.samples_avail() == 0)
            apu.step();

        Simple_Apu::sample_t out[1024];
        int count = apu.read_samples(out, jmin (todo, int (apu.samples_avail()), 1024));

        float* data = buffer.getWritePointer (0, done);
        for (int i = 0; i < count; i++)
            data[i] = out[i] / 32768.0f;

        done += count;
        todo -= count;
    }

}

void RP2A03AudioProcessor::processBlock (AudioSampleBuffer& buffer, MidiBuffer& midi)
{
    const float p1Level = getParameter (paramPulse1Level)->getUserValue();
    const int p1Duty    = getParameter (paramPulse1DutyCycle)->getUserValue();
    const float p2Level = getParameter (paramPulse2Level)->getUserValue();
    const int p2Duty    = getParameter (paramPulse2DutyCycle)->getUserValue();
    const float tLevel  = getParameter (paramTriangleLevel)->getUserValue();
    const float nLevel  = getParameter (paramNoiseLevel)->getUserValue();
    const bool nShort   = getParameter (paramNoiseShort)->getUserValue();

    int done = 0;
    runUntil (done, buffer, 0);

    int pos = 0;
    MidiMessage msg;
    MidiBuffer::Iterator itr (midi);
    while (itr.getNextEvent (msg, pos))
    {
        runUntil (done, buffer, pos);

        if (msg.isNoteOn())
        {
            noteQueue.add (msg.getNoteNumber());
            velocity = msg.getVelocity();

            printf ("Note on: %d\n", msg.getNoteNumber());
        }
        else if (msg.isNoteOff())
        {
            noteQueue.removeFirstMatchingValue (msg.getNoteNumber());

            printf ("Note off: %d\n", msg.getNoteNumber());
        }
        else if (msg.isAllNotesOff())
        {
            noteQueue.clear();

            printf ("All notes off\n");
        }

        const int curNote = noteQueue.size() > 0 ? noteQueue.getFirst() : -1;

        if (curNote != lastNote)
        {
            int v = curNote == -1 ? 0 : velocity;

            // Pulse 1
            apu.write_register (0x4000, (p1Duty << 6) | 0x30 | int (p1Level * v / 127.0 * 0xF));

            if (curNote != -1)
            {
                int period = 111860.8 / MidiMessage::getMidiNoteInHertz (curNote) - 1;

                apu.write_register (0x4002, period & 0xFF);
                apu.write_register (0x4003, (period >> 8) & 0x7);
            }

            // Pulse 2
            apu.write_register (0x4004, (p2Duty << 6) | 0x30 | int (p2Level * v / 127.0 * 0xF));

            if (curNote != -1)
            {
                int period = 111860.8 / MidiMessage::getMidiNoteInHertz (curNote) - 1;

                apu.write_register (0x4006, period & 0xFF);
                apu.write_register (0x4007, (period >> 8) & 0x7);
            }

            // Triangle
            apu.write_register (0x4008, curNote == -1 ? 0x00 : 0xFF);
            if (curNote != -1)
            {
                int period = tLevel == 1.0f ? 111860.8 / (MidiMessage::getMidiNoteInHertz (curNote) * 2) - 1 : 0;

                apu.write_register (0x400A, period & 0xFF);
                apu.write_register (0x400B, (period >> 8) & 0x7);
            }

            // Noise
            apu.write_register (0x400C, 0x30 | int (nLevel * v / 127.0 * 0xF));

            if (curNote != -1)
            {
                apu.write_register (0x400E, (nShort ? 0x80 : 0x00) | (curNote % 16));
                apu.write_register (0x400F, 0xFF);
            }

            lastNote = curNote;
        }
    }

    runUntil (done, buffer, buffer.getNumSamples());

    outputLevel.trackBuffer (buffer);
}

//==============================================================================
bool RP2A03AudioProcessor::hasEditor() const
{
    return true;
}

AudioProcessorEditor* RP2A03AudioProcessor::createEditor()
{
    return new RP2A03AudioProcessorEditor (*this);
}

//==============================================================================
// This creates new instances of the plugin..
AudioProcessor* JUCE_CALLTYPE createPluginFilter()
{
    return new RP2A03AudioProcessor();
}
<|endoftext|>


<commit_before><commit_msg>fix html5 video not render while playing(ex: v.qq.com)<commit_after><|endoftext|>


<commit_before>#ifndef EVENT_SET_HPP
#define EVENT_SET_HPP

/* This class provides an interface for a specific Event Set
 */

namespace warped {

class EventSet {
public:

    virtual bool insert(const Event* event) = 0;

    virtual bool handleAntiMessage(SimulationObject* reclaimer, const NegativeEvent* cancelEvent);

    virtual const Event* getEvent(SimulationObject*, unsigned int);

    virtual const Event* peekEvent(SimulationObject*, unsigned int);

    virtual void fossilCollect(SimulationObject*, unsigned int);

    virtual void fossilCollect(const Event*);

    virtual void rollback(SimulationObject* object, unsigned int rollbackTime);

protected:
    EventSet(){};
};

} // warped namespace

#endif
<commit_msg>Added the Event set class private variables - unprocessed, processed and removed queues and their locks.<commit_after>#ifndef EVENT_SET_HPP
#define EVENT_SET_HPP

/* This class provides the set of APIs needed to handle events in
   an event set.
 */

#include <mutex>

namespace warped {

class EventSet {
public:
    EventSet() {}

    bool insertEvent (
                const std::unique_ptr<Event> event,
                unsigned int threadId );

    bool handleAntiMessage (
                std::unique_ptr<SimulationObject> object,
                const std::unique_ptr<Event> cancelEvent,
                unsigned int threadId );

    const std::unique_ptr<Event> getEvent (
                std::unique_ptr<SimulationObject> object,
                unsigned int timeStamp,
                unsigned int threadId );

    const std::<Event> peekEvent (
                std::unique_ptr<SimulationObject> object,
                unsigned int timeStamp,
                unsigned int threadId );

    void fossilCollect (
                std::unique_ptr<SimulationObject> object,
                unsigned int timeStamp,
                unsigned int threadId );

    void fossilCollect (
                const std::unique_ptr<Event> event,
                unsigned int threadId );

    void rollback (
                std::unique_ptr<SimulationObject> object,
                unsigned int rollbackTime,
                unsigned int threadId );

private:
    std::mutex unprocessedQueueLock;

    std::mutex processedQueueLock;

    std::mutex removedQueueLock;

    //to be modified

    //Queues to hold the unprocessed events for each simulation object
    std::vector<std::multiset<const std::unique_ptr<Event>, receiveTimeLessThanEventIdLessThan>*> unprocessedQueue;

    //Queues to hold the processed events for each simulation object
    std::vector<std::vector<const std::unique_ptr<Event>>*> processedQueue;

    //Queues to hold the removed events for each simulation object
    std::vector<std::vector<const std::unique_ptr<Event>>*> removedQueue;

};

} // warped namespace

#endif
<|endoftext|>