file Elements/daFunk.hpp

[No description available]

Namespaces

Name
daFunk
daFunk::detail

Classes

Name
structdaFunk::index_list
structdaFunk::detail::range_builder< MIN, MIN, Is… >
structdaFunk::detail::range_builder
classdaFunk::FunkBase
classdaFunk::livingVector
classdaFunk::FunkBound
classdaFunk::FunkPlain
classdaFunk::FunkConst
classdaFunk::FunkDerived
classdaFunk::FunkFunc
classdaFunk::FunkFuncM
classdaFunk::FunkDelta
classdaFunk::FunkVar
classdaFunk::FunkMath_umin
classdaFunk::FunkInterp
classdaFunk::FunkIfElse
classdaFunk::ThrowError
classdaFunk::Bottle
classdaFunk::FunkIntegrate_gsl1d

Defines

Name
DEF_FUNKTRAIT(C)
MATH_OPERATION(OPERATION)
MATH_OPERATION(OPERATION, SYMBOL)
MATH_OPERATION(OPERATION)

Macros Documentation

define DEF_FUNKTRAIT

#define DEF_FUNKTRAIT(
    C
)
class C {                                           \
    public:                                         \
        static daFunk::FunkPlain* ptr;              \
        static void set(daFunk::FunkPlain* new_ptr) \
        {                                           \
            delete ptr;                             \
            ptr = new_ptr;                          \
        }                                           \
};                                                  \
daFunk::FunkPlain* C::ptr = NULL;

define MATH_OPERATION

#define MATH_OPERATION(
    OPERATION
)
    class FunkMath_##OPERATION: public FunkBase                                                           \
    {                                                                                                     \
        public:                                                                                           \
            FunkMath_##OPERATION(Funk f)                                                                  \
            {                                                                                             \
                functions = vec(f);                                                                       \
                arguments = f->getArgs();                                                                 \
                singularities = f->getSingl();                                                            \
            }                                                                                             \
            double value(const std::vector<double> & data, size_t bindID)                                       \
            {                                                                                             \
                return OPERATION(functions[0]->value(data, bindID));                                      \
            }                                                                                             \
    };                                                                                                    \
    inline Funk OPERATION (Funk f) { return Funk(new FunkMath_##OPERATION(f)); }

define MATH_OPERATION

#define MATH_OPERATION(
    OPERATION,
    SYMBOL
)

define MATH_OPERATION

#define MATH_OPERATION(
    OPERATION
)
    class FunkMath_##OPERATION: public FunkBase                                                           \
    {                                                                                                     \
        public:                                                                                           \
            FunkMath_##OPERATION(Funk f)                                                                  \
            {                                                                                             \
                functions = vec(f);                                                                       \
                arguments = f->getArgs();                                                                 \
                singularities = f->getSingl();                                                            \
            }                                                                                             \
            double value(const std::vector<double> & data, size_t bindID)                                       \
            {                                                                                             \
                return OPERATION(functions[0]->value(data, bindID));                                      \
            }                                                                                             \
    };                                                                                                    \
    inline Funk OPERATION (Funk f) { return Funk(new FunkMath_##OPERATION(f)); }

Source code

/*
 * _______
 * \  ___ `'.                                 _..._        .
 *  ' |--.\  \                _.._          .'     '.    .'|
 *  | |    \  '             .' .._|        .   .-.   . .'  |
 *  | |     |  '    __      | '            |  '   '  |<    |
 *  | |     |  | .:--.'.  __| |__  _    _  |  |   |  | |   | ____
 *  | |     ' .'/ |   \ ||__   __|| '  / | |  |   |  | |   | \ .'
 *  | |___.' /' `" __ | |   | |  .' | .' | |  |   |  | |   |/  .
 * /_______.'/   .'.''| |   | |  /  | /  | |  |   |  | |    /\  \
 * \_______|/   / /   | |_  | | |   `'.  | |  |   |  | |   |  \  \
 *              \ \._,\ '/  | | '   .'|  '/|  |   |  | '    \  \  \
 *               `--'  `"   |_|  `-'  `--' '--'   '--''------'  '---'
 *
 *                         daFunk - dynamisch allokierbare Funktionen
 *
 *  v0.1 Dec 2014
 *  v0.2 Mar 2015 - Completely rewritten internal structure
 *  v0.3 May 2016 - Extensions
 *
 *  Christoph Weniger, created Dec 2014, edited until May 2016
 *  <c.weniger@uva.nl>
 *
 *  with contributions related to thread-safety from
 *  Lars A. Dal, Apr, Jun 2015
 *  <l.a.dal@fys.uio.no>
 *
 *
 *
 * The MIT License (MIT)
 *
 * Copyright (c) 2016 Christoph Weniger
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy of
 * this software and associated documentation files (the "Software"), to deal in
 * the Software without restriction, including without limitation the rights to
 * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
 * of the Software, and to permit persons to whom the Software is furnished to do
 * so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in all
 * copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 * SOFTWARE.
 */

#ifndef __DAFUNK_HPP__
#define __DAFUNK_HPP__

#include <iostream>
#include <vector>
#include <algorithm>
#include <map>
#include <set>
#include <cmath>
#include <stdexcept>

//#define NDEBUG
#include <assert.h>

//#define GAMBIT_DIR

#ifdef GAMBIT_DIR
#include <omp.h>
#include "gambit/Utils/standalone_error_handlers.hpp"
#include "gambit/Utils/util_macros.hpp"
#endif

#include "boost/shared_ptr.hpp"
#include "boost/enable_shared_from_this.hpp"

//#include <memory>
//using std::shared_ptr;
//using std::enable_shared_from_this;

namespace daFunk {class FunkPlain;}

#define DEF_FUNKTRAIT(C)                            \
class C {                                           \
    public:                                         \
        static daFunk::FunkPlain* ptr;              \
        static void set(daFunk::FunkPlain* new_ptr) \
        {                                           \
            delete ptr;                             \
            ptr = new_ptr;                          \
        }                                           \
};                                                  \
daFunk::FunkPlain* C::ptr = NULL;

// Extensions
#include <gsl/gsl_integration.h>
#include <gsl/gsl_errno.h>

namespace daFunk
{
    //
    // Type declarations etc.
    //

    using boost::shared_ptr;
    using boost::enable_shared_from_this;
    using boost::dynamic_pointer_cast;
    using boost::static_pointer_cast;

    class FunkBase;
    class FunkBound;
    class FunkIntegrate_gsl1d;

    typedef shared_ptr<FunkBase> Funk;
    typedef shared_ptr<FunkBound> BoundFunk;
    typedef std::vector<std::string> ArgsType;
    typedef std::map<std::string, std::vector<std::pair<Funk, Funk>>> Singularities;

    //template <typename... Args>
    //using PlainPtr = double(*)(Args&...);
    typedef double(*PlainPtr1)(double&);
    typedef double(*PlainPtr2)(double&,double&);
    typedef double(*PlainPtr3)(double&,double&,double&);
    typedef double(*PlainPtr4)(double&,double&,double&,double&);

    //template <typename... Args>
    //using PlainPtrs = std::pair<double(*)(Args...,void*), void*>;
    typedef std::pair<double(*)(double,void*), void*> PlainPtrs1;
    typedef std::pair<double(*)(double,double,void*), void*> PlainPtrs2;
    typedef std::pair<double(*)(double,double,double,void*), void*> PlainPtrs3;
    typedef std::pair<double(*)(double,double,double,double,void*), void*> PlainPtrs4;


    //
    // Vector initialization from argument list
    //
    // Usage: std::vector<T> v = vec<T>(v1, v2, v3, ...);
    //

    template <typename T>
    inline std::vector<T> vec(std::vector<T> vector)
    {
        return vector;
    }
    template <typename T, typename... Args>
    inline std::vector<T> vec(std::vector<T> vector, T value, Args... args)
    {
        vector.push_back(value);
        return vec<T>(vector, args...);
    }
    template <typename T, typename... Args>
    inline std::vector<T> vec(T value, Args... args)
    {
        std::vector<T> vector;
        vector.push_back(value);
        vector = vec<T>(vector, args...);
        return vector;
    }
    template <typename T>
    inline std::vector<T> vec()
    {
        std::vector<T> vector;
        return vector;
    }

    //
    // Math helper functions
    //

    // Generate linear 1-dim grid
    inline std::vector<double> linspace(double x0, double x1, unsigned int n)
    {
        std::vector<double> ret;
        double dx = 0;
        if (n > 1)
            dx = (x1-x0)/(n-1);
            for (unsigned int i = 0; i<n; i++)
            {
                ret.push_back(x0 + i * dx);
            }
        return ret;
    }

    // Generate logarithmic 1-dim grid
    inline std::vector<double> logspace(double x0, double x1, unsigned int n)
    {
        std::vector<double> ret;
        double dx = 0;
        if (n > 1)
            dx = (x1-x0)/(n-1);
            for (unsigned int i = 0; i<n; i++)
            {
                ret.push_back(pow(10, x0 + i * dx));
            }
        return ret;
    }


    //
    // Helper functions for internal calculations
    //

    inline bool args_match(ArgsType args1, ArgsType args2)
    {
        bool m = true;
        for ( auto it = args1.begin(); it!=args1.end(); it++ )
        {
            if ( std::find(args2.begin(), args2.end(), *it) == args2.end() )
                m = false;
        }
        if ( args1.size() != args2.size() ) m = false;
        return m;
    }

    inline std::string args_string(ArgsType args)
    {
        std::string msg = "";
        for ( auto it = args.begin(); it!=args.end(); it++ )
        {
            msg += *it;
            if ( it != args.end() - 1)
                msg += ", ";
        }
        return msg;
    }

    inline ArgsType joinArgs(ArgsType args1, ArgsType args2)
    {
        args1.insert(args1.end(), args2.begin(), args2.end());
        std::set<std::string> argsset(args1.begin(), args1.end());
        args1.assign(argsset.begin(), argsset.end());
        return args1;
    }

    inline ArgsType eraseArg(ArgsType args, std::string arg)
    {
        auto it = std::find(args.begin(), args.end(), arg);
        if (it!=args.end()) args.erase(it);
        return args;
    }

    inline Singularities joinSingl(Singularities s1, Singularities s2)
    {
        for ( auto it = s2.begin(); it != s2.end(); ++it )
        {
            if ( s1.find(it->first) == s1.end() )
                s1[it->first] = it->second;
            else
            {
                for ( auto it2 = it->second.begin(); it2 != it->second.end(); ++it2 )
                {
                    if ( std::find(s1[it->first].begin(), s1[it->first].end(), *it2) == s1[it->first].end() )
                        s1[it->first].push_back(*it2);
                }
            }
        }
        return s1;
    }


    //
    // Index lists (taken from stackoverflow)
    //

    // The structure that encapsulates index lists
    template <size_t... Is>
    struct index_list
    {
    };

    // Collects internal details for generating index ranges [MIN, MAX)
    namespace detail
    {
        // Declare primary template for index range builder
        template <size_t MIN, size_t N, size_t... Is>
        struct range_builder;

        // Base step
        template <size_t MIN, size_t... Is>
        struct range_builder<MIN, MIN, Is...>
        {
            typedef index_list<Is...> type;
        };

        // Induction step
        template <size_t MIN, size_t N, size_t... Is>
        struct range_builder : public range_builder<MIN, N - 1, N - 1, Is...>
        {
        };
    }


    //
    // **** The central virtual base class ****
    //

    class FunkBase: public enable_shared_from_this<FunkBase>
    {
        public:
            FunkBase() {};
            virtual ~FunkBase() {};

            // Standard setting handles
            template <typename... Args> Funk set(std::string arg, Funk g, Args... args);
            template <typename... Args> Funk set(std::string arg, double x, Args... args);
            template <typename... Args> Funk set(std::string arg, std::string arg1, Args... args);
            template <typename... Args> Funk set();

            // Standard binding handles
            template <typename... Args> shared_ptr<FunkBound> bind(Args... args);

            // Convenience functions
            const std::vector<std::string> & getArgs() { return this->arguments; };
            std::size_t getNArgs() {return this->arguments.size();};
            bool hasArg(std::string);
            bool hasArgs();
            Funk help();
            template <typename... Args> bool assert_args(Args... args);

            // Return value & standard resolve
            virtual double value(const std::vector<double> &, size_t bindID) = 0;

            // datamap maps the required function arguments onto the specific
            // entries in the double-valued data array generated by eval().
            // datalen acts like a pointer on the last entry of that array, and
            // can be increased if more workspace is required.  bindID ensures
            // that resolution for various different binds can happen in
            // parallel with the same Funk objects.
            virtual void resolve(std::map<std::string, size_t> datamap, size_t & datalen, size_t bindID, std::map<std::string,size_t> &argmap);


            // Singularities handling
            Singularities getSingl() { return singularities; }
        private:  // Note: This works currently only for constant functions
            Funk set_singularity(std::string arg, Funk pos, Funk width);
            Funk set_singularity(std::string arg, double pos, Funk width);
            Funk set_singularity(std::string arg, Funk pos, double width);
        public:
            Funk set_singularity(std::string arg, double pos, double width);

            // Print message
            Funk print(std::string arg);


            //
            // "External" functions
            //

            // Integration
            template <typename... Args> shared_ptr<FunkIntegrate_gsl1d> gsl_integration(Args... args);

            // Plain function generators
            PlainPtrs1 plain(std::string);
            PlainPtrs2 plain(std::string, std::string);
            PlainPtrs3 plain(std::string, std::string, std::string);
            PlainPtrs4 plain(std::string, std::string, std::string, std::string);
            template <typename T>
            PlainPtr1 plain(std::string);
            template <typename T>
            PlainPtr2 plain(std::string, std::string);
            template <typename T>
            PlainPtr3 plain(std::string, std::string, std::string);
            template <typename T>
            PlainPtr4 plain(std::string, std::string, std::string, std::string);

        protected:
            std::vector<Funk> functions;  // Dependent functions
            ArgsType arguments;  // Argument names
            std::vector<std::vector<size_t> > indices;  // Indices for data object
            size_t datalen;
            Singularities singularities;
    };

    // A vector class with global knowledge about its health status.
    // (BoundFunk objects are occasionally destructed *after* livingVector has
    // been destructed, causing segfaults if not catched properly.)
    class livingVector: public std::vector<size_t>
    {
        public:
            ~livingVector()
            {
                livingVector::is_dead() = true;
            }
            static bool & is_dead()
            {
                static bool dead = false;
                return dead;
            }
    };

    class FunkBound
    {
        public:
            FunkBound(Funk f, size_t datalen, size_t bindID) : f(f), datalen(datalen), bindID(bindID) {};
            ~FunkBound() {bindID_manager(bindID,false);};
            double value(std::vector<double> & map, size_t bindID) {(void)bindID; (void)map; return 0;};

            template <typename... Args> inline double eval(Args... argss)
            {
                auto data = vec<double>(argss...);
                data.resize(datalen);
                return f->value(data, bindID);
            }

            template <typename... Args> inline std::vector<double> vect(Args... argss)
            {
                std::vector<std::vector<double>> coll;
                return this->vect2(coll, argss...);
            }

        private:
            template <typename... Args>
            friend shared_ptr<FunkBound> FunkBase::bind(Args... argss);
            // Function for generating unique bindIDs.
            // IDs are sequential, starting from 0.
            static void bindID_manager(size_t &bindID, bool bind)
            {
              static size_t n_idx = 0;
              static livingVector free;
              if(bind)
              {
                #pragma omp critical (bindID_allocation)
                {
                    if(free.empty())
                    {
                        // Increment after assignment (n_idx++ instead of ++n_idx)
                        bindID = n_idx++;
                    }
                    else
                    {
                        bindID = free.back();
                        free.pop_back();
                    }
                }
              }
              else
              {
                    if ( not livingVector::is_dead() )
                    {
                        #pragma omp critical (bindID_allocation)
                        free.push_back(bindID);
                    }
              }
            }
            template <typename... Args> inline std::vector<double> vect2(std::vector<std::vector<double>> & coll)
            {
                size_t size = 1;
                std::vector<bool> vec_flag;
                for ( auto it = coll.begin(); it != coll.end(); ++it )
                {
                    if ( it->size() == 1 )
                    {
                        vec_flag.push_back(false);
                        continue;
                    }
                    vec_flag.push_back(true);
                    if ( size == 1 ) size = it->size();
                    if ( size != it->size() )
                    {
                        std::cerr << "daFunk::FunkBase WARNING: Inconsistent vector lengths." << std::endl;
                        return vec<double>();
                    }
                }
                auto r = vec<double>();
                auto data = vec<double>();
                data.resize(datalen);
                for ( size_t i = 0; i != size; ++i )
                {
                    for ( size_t j = 0; j != coll.size(); ++j )
                    {
                        if ( vec_flag[j] )
                            data[j] = coll[j][i];
                        else
                            data[j] = coll[j][0];
                    }
                    r.push_back(f->value(data, bindID));
                }
                return r;
            }

            template <typename... Args> inline std::vector<double> vect2(std::vector<std::vector<double>> & coll, double x, Args... argss)
            {
                coll.push_back(vec<double>(x));
                return this->vect2(coll, argss...);
            }

            template <typename... Args> inline std::vector<double> vect2(std::vector<std::vector<double>> & coll, std::vector<double> v, Args... argss)
            {
                coll.push_back(v);
                return this->vect2(coll, argss...);
            }

            Funk f;  // bound function

            // datalen is the length of the double-valued data array that is
            // needed as workspace for function evaluation, and that is created
            // on the heap for each eval separately to ensure thread-safety.
            size_t datalen;

            // bindID has the purpose of allowing bound functions (instances of
            // FunkBase and daughter classes) to be bound by various binding
            // functions simultaneously.
            size_t bindID;
    };


    //
    // Derived class with (templated) static member functions as plain function
    // prototypes.
    //

    class FunkPlain: public FunkBase
    {
        public:
            FunkPlain(Funk fin, std::string arg1) : f(fin->bind(arg1)) {}
            FunkPlain(Funk fin, std::string arg1, std::string arg2) : f(fin->bind(arg1, arg2)) {}
            FunkPlain(Funk fin, std::string arg1, std::string arg2, std::string arg3) : f(fin->bind(arg1, arg2, arg3)) {}
            FunkPlain(Funk fin, std::string arg1, std::string arg2, std::string arg3, std::string arg4) : f(fin->bind(arg1, arg2, arg3, arg4)) {}

            static double plain1p(double x1, void* ptr)
            {
                FunkPlain * funkPtrPtr = static_cast<FunkPlain*>(ptr);
                return funkPtrPtr->f->eval(x1);
            }
            static double plain2p(double x1, double x2, void* ptr)
            {
                FunkPlain * funkPtrPtr = static_cast<FunkPlain*>(ptr);
                return funkPtrPtr->f->eval(x1, x2);
            }
            static double plain3p(double x1, double x2, double x3, void* ptr)
            {
                FunkPlain * funkPtrPtr = static_cast<FunkPlain*>(ptr);
                return funkPtrPtr->f->eval(x1, x2, x3);
            }
            static double plain4p(double x1, double x2, double x3, double x4, void* ptr)
            {
                FunkPlain * funkPtrPtr = static_cast<FunkPlain*>(ptr);
                return funkPtrPtr->f->eval(x1, x2, x3, x4);
            }

            template <typename T>
            static double plain1(double& x1)
            {
                FunkPlain * funkPtrPtr = static_cast<FunkPlain*>(T::ptr);
                return funkPtrPtr->f->eval(x1);
            }
            template <typename T>
            static double plain2(double& x1, double& x2)
            {
                FunkPlain * funkPtrPtr = static_cast<FunkPlain*>(T::ptr);
                return funkPtrPtr->f->eval(x1, x2);
            }
            template <typename T>
            static double plain3(double& x1, double& x2, double& x3)
            {
                FunkPlain * funkPtrPtr = static_cast<FunkPlain*>(T::ptr);
                return funkPtrPtr->f->eval(x1, x2, x3);
            }
            template <typename T>
            static double plain4(double& x1, double& x2, double& x3, double& x4)
            {
                FunkPlain * funkPtrPtr = static_cast<FunkPlain*>(T::ptr);
                return funkPtrPtr->f->eval(x1, x2, x3, x4);
            }

            double value(const std::vector<double> & args, size_t bindID)
            {
                (void)args;
                (void)bindID;
                assert ( 0 == 1 );  // This function should never be called
                return 0;
            }

        private:
            shared_ptr<FunkBound> f;  // bound function
            std::string arg1, arg2, arg3, arg4;
    };

    //
    // Derived class that implements constant
    //

    class FunkConst: public FunkBase
    {
        public:
            template <typename... Args>
            FunkConst(double c, Args ...argss) : c(c) { arguments = vec<std::string>(argss...); }
            FunkConst(double c) : c(c) { arguments.resize(0); }

            double value(const std::vector<double> & data, size_t bindID)
            {
                (void)data;
                (void)bindID;
                return c;
            }

        private:
            double c;
    };
    template <typename... Args>
    inline Funk one(Args... argss) { return Funk(new FunkConst(1., argss...)); }
    template <typename... Args>
    inline Funk zero(Args... argss) { return Funk(new FunkConst(0., argss...)); }
    template <typename... Args>
    inline Funk cnst(double x, Args... argss) { return Funk(new FunkConst(x, argss...)); }


    //
    // Derived class that implements setting of parameters
    //

    class FunkDerived: public FunkBase
    {
        public:
            FunkDerived(Funk f, std::string arg, Funk g) : my_arg(arg)
            {
                setup(f, arg, g);
            };

            FunkDerived(Funk f, std::string arg, double x) : my_arg(arg)
            {
                setup(f, arg, cnst(x));
            }

            // We need to sneak in an additional parameter
            void resolve(std::map<std::string, size_t> datamap, size_t & datalen, size_t bindID, std::map<std::string,size_t> &argmap)
            {
                functions[1]->resolve(datamap, datalen, bindID, argmap);  // resolve g
                // add new slot for result from of g
                if(my_index.size() <= bindID)
                {
                    my_index.resize(bindID+1);
                    // TODO: Introduce informative error message if bind fails
                    // because of inconsistencies, e.g. along the lines:
                    //
                    //    std::cout << "FATAL ERROR: bind() attempts to resolve dependencies for FunkDerived" << std::endl;
                    //    std::cout << "object inconsistently." << std::endl;
                    //    std::cout << "Encountered while resolving " << my_arg << " and:" << std::endl;
                    //    for (auto it = datamap.begin(); it != datamap.end(); it++)
                    //    {
                    //        std::cout << "  " << it->first << std::endl;
                    //    }
                    //    exit(1);
                }
                if ( argmap.find(my_arg) == argmap.end() )
                {
                    if(datamap.find(my_arg) == datamap.end())
                    {
                        my_index[bindID] = datalen;
                        argmap[my_arg]   = datalen;
                        ++datalen;
                    }
                    else
                    {
                        my_index[bindID] = datamap[my_arg];
                    }
                }
                else
                {
                    my_index[bindID] = argmap[my_arg];
                }
                datamap[my_arg]  = my_index[bindID];  // add or overwrite entry in datamap
                functions[0]->resolve(datamap, datalen, bindID, argmap);  // resolve f
            }

            double value(const std::vector<double> & data, size_t bindID)
            {
                std::vector<double> data2(data);
                data2[my_index[bindID]] = functions[1]->value(data, bindID);
                return functions[0]->value(data2, bindID);
            }

        private:
            std::string my_arg;


            void setup(Funk f, std::string arg, Funk g)
            {
                functions = vec(f, g);
                Singularities tmp_singl = f->getSingl();
                if ( tmp_singl.erase(arg) > 0 )
                    std::cout << "daFunk::FunkBase WARNING: Loosing singularity information while setting " << arg << std::endl;
                singularities = joinSingl(g->getSingl(), tmp_singl);
                arguments = joinArgs(eraseArg(f->getArgs(), arg), g->getArgs());
            };
            std::vector<size_t> my_index;
    };


    //
    // Derived class for the import of plain functions
    //

    template <bool threadsafe, typename... funcargs>
    class FunkFunc : public FunkBase
    {
        public:
            template <typename... Args>
            FunkFunc(double (*f)(funcargs...), Args... argss)
            {
                ptr = f;
                digest_input(argss...);
            }

            double value(const std::vector<double> & data, size_t bindID)
            {
                std::tuple<typename std::remove_reference<funcargs>::type...> my_input;
                double result;
                size_t i = 0;
                #pragma omp critical (FunkFunc_setInput)
                {
                    for ( auto f = functions.begin(); f != functions.end(); ++f, ++i)
                    {
                        *map[i] = (*f)->value(data, bindID);
                    }
                    my_input = input;
                }
                if(threadsafe)
                {
                    result = ppp(typename detail::range_builder<0, sizeof...(funcargs)>::type(), my_input);
                }
                else
                {
                    #pragma omp critical (FunkFunc_externalFunctionCall)
                    {
                        result = ppp(typename detail::range_builder<0, sizeof...(funcargs)>::type(), my_input);
                    }
                }
                return result;
            }

            template <size_t... Args>
            double ppp(index_list<Args...>, std::tuple<typename std::remove_reference<funcargs>::type...> & my_input)
            {
                return (*ptr)(std::get<Args>(my_input)...);
            }

        private:
            std::tuple<typename std::remove_reference<funcargs>::type...> input;
            std::vector<double*> map;
            double (*ptr)(funcargs...);

            // Digest input parameters
            // (forwarding everything except daFunk::Funk types, which is mapped onto
            // funktion parameters)
            template<typename T, typename... Args>
            void digest_input(T x, Args... argss)
            {
                const int i = sizeof...(funcargs) - sizeof...(argss) - 1;
                std::get<i>(input) = x;
                digest_input(argss...);
            }
            template<typename... Args>
            void digest_input(Funk f, Args... argss)
            {
                const int i = sizeof...(funcargs) - sizeof...(argss) - 1;
                map.push_back(&std::get<i>(input));
                arguments = joinArgs(arguments, f->getArgs());
                functions.push_back(f);
                singularities = joinSingl(singularities, f->getSingl());
                digest_input(argss...);
            }
            void digest_input() {};
    };

    template <typename... funcargs, typename... Args>
    Funk func(double (*f)(funcargs...), Args... args) {
        return Funk(new FunkFunc<false, funcargs...>(f, args...));
    }
    // Version that assumes the function to be threadsafe
    template <typename... funcargs, typename... Args>
    Funk func_fromThreadsafe(double (*f)(funcargs...), Args... args) {
        return Funk(new FunkFunc<true, funcargs...>(f, args...));
    }


    template <bool threadsafe, typename O, typename... funcargs>
    class FunkFuncM : public FunkBase
    {
        public:
            template <typename... Args>
            FunkFuncM(O* obj, double (O::* f)(funcargs...), Args... argss) : obj(obj)
            {
                ptr = f;
                digest_input(argss...);
            }

            template <typename... Args>
            FunkFuncM(shared_ptr<O> obj, double (O::* f)(funcargs...), Args... argss) : shared_obj(obj), obj(&*obj)
            {
                ptr = f;
                digest_input(argss...);
            }

            double value(const std::vector<double> & data, size_t bindID)
            {
                std::tuple<typename std::remove_reference<funcargs>::type...> my_input;
                double result;
                size_t i = 0;
                #pragma omp critical(FunkFuncM_value)
                {
                    for ( auto f = functions.begin(); f != functions.end(); ++f, ++i)
                    {
                        *map[i] = (*f)->value(data, bindID);
                    }
                    my_input = input;
                }
                if(threadsafe)
                {
                    result = ppp(typename detail::range_builder<0, sizeof...(funcargs)>::type(), my_input);
                }
                else
                {
                    #pragma omp critical (FunkFuncM_objectFunctionCall)
                    {
                        result = ppp(typename detail::range_builder<0, sizeof...(funcargs)>::type(), my_input);
                    }
                }
                return result;
            }

            template <size_t... Args>
            double ppp(index_list<Args...>, std::tuple<typename std::remove_reference<funcargs>::type...> & my_input)
            {
                return (*obj.*ptr)(std::get<Args>(my_input)...);
            }

        private:
            std::tuple<typename std::remove_reference<funcargs>::type...> input;
            std::vector<double*> map;
            double (O::* ptr)(funcargs...);
            shared_ptr<O> shared_obj;
            O* obj;

            // Digest input parameters
            // (forwarding everything except daFunk::Funk types, which is mapped onto
            // funktion parameters)
            template<typename T, typename... Args>
            void digest_input(T x, Args... argss)
            {
                const int i = sizeof...(funcargs) - sizeof...(argss) - 1;
                std::get<i>(input) = x;
                digest_input(argss...);
            }
            template<typename... Args>
            void digest_input(Funk f, Args... argss)
            {
                const int i = sizeof...(funcargs) - sizeof...(argss) - 1;
                map.push_back(&std::get<i>(input));
                arguments = joinArgs(arguments, f->getArgs());
                functions.push_back(f);
                singularities = joinSingl(singularities, f->getSingl());
                digest_input(argss...);
            }
            void digest_input() {};
    };


    template <typename O, typename... funcargs, typename... Args>
    Funk funcM(O* obj, double (O::* f)(funcargs...), Args... args) {
        return Funk(new FunkFuncM<false, O, funcargs...>(obj, f, args...));
    }
    template <typename O, typename... funcargs, typename... Args>
    Funk funcM(shared_ptr<O> obj, double (O::* f)(funcargs...), Args... args) {
        return Funk(new FunkFuncM<false, O, funcargs...>(obj, f, args...));
    }

    // Versions that assume the object to be threadsafe
    template <typename O, typename... funcargs, typename... Args>
    Funk funcM_fromThreadsafe(O* obj, double (O::* f)(funcargs...), Args... args) {
        return Funk(new FunkFuncM<true, O, funcargs...>(obj, f, args...));
    }
    template <typename O, typename... funcargs, typename... Args>
    Funk funcM_fromThreadsafe(shared_ptr<O> obj, double (O::* f)(funcargs...), Args... args) {
        return Funk(new FunkFuncM<true, O, funcargs...>(obj, f, args...));
    }

    //
    // Derived class that implements delta function
    //

    class FunkDelta: public FunkBase
    {
        public:
            FunkDelta(std::string arg, double pos, double width) : pos(pos), width(width)
            {
                arguments = vec(arg);
                singularities[arg].push_back(std::pair<Funk, Funk>(cnst(pos), cnst(width)));
            }

            double value(const std::vector<double> & data, size_t bindID)
            {
                double x = data[indices[bindID][0]];
                return exp(-pow(x-pos,2)/pow(width,2)/2)/sqrt(2*M_PI)/width;
            }

        private:
            double pos, width;
    };
    inline Funk delta(std::string arg, double pos, double width) { return Funk(new FunkDelta(arg, pos, width)); }

    //
    // Derived class that implements simple linear variable
    //

    class FunkVar: public FunkBase
    {
        public:
            FunkVar(std::string arg)
            {
                arguments = vec(arg);
            }

            double value(const std::vector<double> & data, size_t bindID)
            {
                return data[indices[bindID][0]];
            }
    };
    inline Funk var(std::string arg) { return Funk(new FunkVar(arg)); }


    //
    // Definition of FunkBase member functions
    //

    inline Funk FunkBase::set_singularity(std::string arg, Funk pos, Funk width)
    {
        singularities[arg].push_back(std::pair<Funk, Funk>(pos, width));
        return shared_from_this();
    };
    inline Funk FunkBase::set_singularity(std::string arg, double pos, Funk width)
    { return shared_from_this()->set_singularity(arg, cnst(pos), width); }
    inline Funk FunkBase::set_singularity(std::string arg, double pos, double width)
    { return shared_from_this()->set_singularity(arg, cnst(pos), cnst(width)); }
    inline Funk FunkBase::set_singularity(std::string arg, Funk pos, double width)
    { return shared_from_this()->set_singularity(arg, pos, cnst(width)); }

    inline void FunkBase::resolve(std::map<std::string, size_t> datamap, size_t & datalen, size_t bindID, std::map<std::string,size_t> &argmap)
    {
        // Resolve my dependencies
        auto it1 = arguments.begin();
        std::vector<size_t> tmp_indices;
        tmp_indices.resize(arguments.size());
        auto it2 = tmp_indices.begin();
        for (; it1 != arguments.end() && it2 != tmp_indices.end(); ++it1, ++it2 )
        {
            try
            {
                *it2 = datamap.at(*it1);
            }
            catch (std::out_of_range & e)
            {
                std::string msg = "FunkBase::resolve() encountered internal problem when resolving " + *it1 + ".\n";
                            msg+= " --- Actual arguments of object: " + args_string(arguments);
#ifdef GAMBIT_DIR
                Gambit::utils_error().raise(LOCAL_INFO, msg);
#else
                throw std::invalid_argument(msg);
#endif
            }
        }

        // Set indices
        if(indices.size() <= bindID)
        {
            indices.resize(bindID+1);
            // TODO: Throw error if problems are encountered
            //std::cout << "FATAL ERROR: bind() attempts to resolve dependencies of FunkBase" << std::endl;
            //std::cout << "object inconsistently." << std::endl;
            //std::cout << "Encountered while resolving: " << std::endl;
            //for (auto it = datamap.begin(); it != datamap.end(); it++)
            //{
            //    std::cout << "  " << it->first << std::endl;
            //}
            //exit(1);
        }
        indices[bindID] = tmp_indices;

        // Resolve other dependencies
        for (auto it = functions.begin(); it != functions.end(); ++it)
        {
            (*it)->resolve(datamap, datalen, bindID, argmap);
        }

    }

    template <typename... Args> inline bool FunkBase::assert_args(Args... args)
    {
        std::vector<std::vector<std::string>> list = vec<std::vector<std::string>>(args...);
        std::set<std::string> myargs(arguments.begin(), arguments.end());
        for ( auto it = list.begin(); it != list.end(); ++it )
        {
            std::set<std::string> theirargs(it->begin(), it->end());
            if ( myargs == theirargs )
                return true;
        }
        return false;

    }

    template <typename... Args> inline Funk FunkBase::set(std::string arg, Funk g, Args... args)
    {
        Funk f = shared_from_this();
        if ( std::find(arguments.begin(), arguments.end(), arg) != arguments.end() )
        {
            f = Funk(new FunkDerived(f, arg, g));
        }
        else
        {
            std::cout << "daFunk::FunkBase WARNING: Ignoring \"" << arg << "\" = function." << std::endl;
        }
        return f->set(args...);
    }

    template <typename... Args> inline Funk FunkBase::set(std::string arg, std::string arg1, Args... args)
        { return shared_from_this()->set(arg, var(arg1))->set(args...); }

    template <typename... Args> inline Funk FunkBase::set(std::string arg, double x, Args... args)
        { return shared_from_this()->set(arg, cnst(x))->set(args...); }

    template <> inline Funk FunkBase::set()
        { return shared_from_this(); }

    template <typename... Args> inline shared_ptr<FunkBound> FunkBase::bind(Args... argss)
    {
        size_t bindID;
        FunkBound::bindID_manager(bindID,true);
        std::map<std::string, size_t> datamap;
        size_t i;
        auto bound_arguments = vec<std::string>(argss...);
        datalen = bound_arguments.size();
        i = 0;
        for ( auto it = bound_arguments.begin(); it != bound_arguments.end(); ++it, ++i )
        {
            datamap[*it] = i;
        }
        std::map<std::string,size_t> argmap;
        if ( not args_match(arguments, bound_arguments) )
        {
            std::string msg = "FunkBase::bind() tries to resolve wrong arguments.\n";
                        msg+= " --- Arguments that are supposed to be bound: " + args_string(bound_arguments) + "\n";
                        msg+= " --- Actual arguments of object: " + args_string(arguments);
#ifdef GAMBIT_DIR
            Gambit::utils_error().raise(LOCAL_INFO, msg);
#else
            throw std::invalid_argument(msg);
#endif
        }
        this->resolve(datamap, datalen, bindID, argmap);
        return shared_ptr<FunkBound>(new FunkBound(shared_from_this(), datalen, bindID));
    }

    inline bool FunkBase::hasArg(std::string arg)
    {
        return ( std::find(arguments.begin(), arguments.end(), arg) != arguments.end() );
    }

    inline bool FunkBase::hasArgs()
    {
        return ( this->arguments.size() != 0 );
    }

    template <typename... Args> inline shared_ptr<FunkIntegrate_gsl1d> FunkBase::gsl_integration(Args... args)
    {
        return getIntegrate_gsl1d(shared_from_this(), args...);
    }

    inline Funk FunkBase::help()
    {
        std::cout << "Arguments:";
        for ( auto it = arguments.begin(); it != arguments.end(); it++ )
        {
            std::cout << " \"" << *it << "\"";
        }
        if ( arguments.size() == 0 )
            std::cout << " none";
        std::cout << std::endl;
        for ( auto it = singularities.begin() ; it != singularities.end(); ++it )
        {
            std::cout << "Singularities in " << it->first << ": " << it->second.size() << std::endl;
        }
        return shared_from_this();
    }

    inline PlainPtrs1 FunkBase::plain(std::string arg1)
    {
        void* ptr = new FunkPlain(shared_from_this(), arg1);
        return PlainPtrs1(&FunkPlain::plain1p, ptr);
    }
    inline PlainPtrs2 FunkBase::plain(std::string arg1, std::string arg2)
    {
        void* ptr = new FunkPlain(shared_from_this(), arg1, arg2);
        return PlainPtrs2(&FunkPlain::plain2p, ptr);
    }
    inline PlainPtrs3 FunkBase::plain(std::string arg1, std::string arg2, std::string arg3)
    {
        void* ptr = new FunkPlain(shared_from_this(), arg1, arg2, arg3);
        return PlainPtrs3(&FunkPlain::plain3p, ptr);
    }
    inline PlainPtrs4 FunkBase::plain(std::string arg1, std::string arg2, std::string arg3, std::string arg4)
    {
        void* ptr = new FunkPlain(shared_from_this(), arg1, arg2, arg3, arg4);
        return PlainPtrs4(&FunkPlain::plain4p, ptr);
    }

    template <typename T>
    inline PlainPtr1 FunkBase::plain(std::string arg1)
    {
        T::set(new FunkPlain(shared_from_this(), arg1));
        return &FunkPlain::plain1<T>;
    }
    template <typename T>
    inline PlainPtr2 FunkBase::plain(std::string arg1, std::string arg2)
    {
        T::set(new FunkPlain(shared_from_this(), arg1, arg2));
        return &FunkPlain::plain2<T>;
    }
    template <typename T>
    inline PlainPtr3 FunkBase::plain(std::string arg1, std::string arg2, std::string arg3)
    {
        T::set(new FunkPlain(shared_from_this(), arg1, arg2, arg3));
        return &FunkPlain::plain3<T>;
    }
    template <typename T>
    inline PlainPtr4 FunkBase::plain(std::string arg1, std::string arg2, std::string arg3, std::string arg4)
    {
        T::set(new FunkPlain(shared_from_this(), arg1, arg2, arg3, arg4));
        return &FunkPlain::plain4<T>;
    }


    //
    // Mathematical functions from cmath
    //

    // Unary minus sign
    class FunkMath_umin: public FunkBase
    {
        public:
            FunkMath_umin(Funk f)
            {
                functions = vec(f);
                singularities = f->getSingl();
                arguments = f->getArgs();
            }
            double value(const std::vector<double> & data, size_t bindID)
            {
                return -(functions[0]->value(data, bindID));
            }
    };
    inline Funk operator - (Funk f) { return Funk(new FunkMath_umin(f)); }

    // Unary operations
#define MATH_OPERATION(OPERATION)                                                                         \
    class FunkMath_##OPERATION: public FunkBase                                                           \
    {                                                                                                     \
        public:                                                                                           \
            FunkMath_##OPERATION(Funk f)                                                                  \
            {                                                                                             \
                functions = vec(f);                                                                       \
                arguments = f->getArgs();                                                                 \
                singularities = f->getSingl();                                                            \
            }                                                                                             \
            double value(const std::vector<double> & data, size_t bindID)                                       \
            {                                                                                             \
                return OPERATION(functions[0]->value(data, bindID));                                      \
            }                                                                                             \
    };                                                                                                    \
    inline Funk OPERATION (Funk f) { return Funk(new FunkMath_##OPERATION(f)); }
    MATH_OPERATION(cos)
    MATH_OPERATION(sin)
    MATH_OPERATION(tan)
    MATH_OPERATION(acos)
    MATH_OPERATION(asin)
    MATH_OPERATION(atan)
    MATH_OPERATION(cosh)
    MATH_OPERATION(sinh)
    MATH_OPERATION(tanh)
    MATH_OPERATION(acosh)
    MATH_OPERATION(asinh)
    MATH_OPERATION(atanh)
    MATH_OPERATION(exp)
    MATH_OPERATION(log)
    MATH_OPERATION(log10)
    MATH_OPERATION(sqrt)
    MATH_OPERATION(fabs)
#undef MATH_OPERATION

    // Standard binary operations
#define MATH_OPERATION(OPERATION, SYMBOL)                                                                 \
    class FunkMath_##OPERATION: public FunkBase                                                           \
    {                                                                                                     \
        public:                                                                                           \
            FunkMath_##OPERATION(Funk f1, Funk f2)                                                        \
            {                                                                                             \
                functions = vec(f1, f2); \
                arguments = joinArgs(f1->getArgs(), f2->getArgs()); \
                singularities = joinSingl(f1->getSingl(), f2->getSingl());\
            }                                                                                             \
            FunkMath_##OPERATION(double x, Funk f2)                 \
            {                                                                                             \
                auto f1 = cnst(x); \
                functions = vec(f1, f2); \
                arguments = joinArgs(f1->getArgs(), f2->getArgs()); \
                singularities = joinSingl(f1->getSingl(), f2->getSingl());\
            }                                                                                             \
            FunkMath_##OPERATION(Funk f1, double x)              \
            {                                                                                             \
                auto f2 = cnst(x); \
                functions = vec(f1, f2); \
                arguments = joinArgs(f1->getArgs(), f2->getArgs()); \
                singularities = joinSingl(f1->getSingl(), f2->getSingl());\
            }                                                                                             \
            double value(const std::vector<double> & data, size_t bindID)                                 \
            {                                                                                             \
                return functions[0]->value(data, bindID) SYMBOL functions[1]->value(data, bindID);        \
            }                                                                                             \
    };                                                                                                    \
    inline Funk operator SYMBOL (Funk f1, Funk f2) { return Funk(new FunkMath_##OPERATION(f1, f2)); }     \
    inline Funk operator SYMBOL (double x, Funk f) { return Funk(new FunkMath_##OPERATION(x, f)); }       \
    inline Funk operator SYMBOL (Funk f, double x) { return Funk(new FunkMath_##OPERATION(f, x)); }
    MATH_OPERATION(Sum,+)
    MATH_OPERATION(Mul,*)
    MATH_OPERATION(Div,/)
    MATH_OPERATION(Dif,-)
#undef MATH_OPERATION

    // More binary operations
#define MATH_OPERATION(OPERATION)                                                                         \
    class FunkMath_##OPERATION: public FunkBase                                                           \
    {                                                                                                     \
        public:                                                                                           \
            FunkMath_##OPERATION(Funk f1, Funk f2)                                                        \
            {                                                                                             \
                functions = vec(f1, f2);                                                                  \
                arguments = joinArgs(f1->getArgs(), f2->getArgs());                                       \
                singularities = joinSingl(f1->getSingl(), f2->getSingl());                                \
            }                                                                                             \
            FunkMath_##OPERATION(double x, Funk f2)                                                       \
            {                                                                                             \
                auto f1 = cnst(x);                                                                        \
                functions = vec(f1, f2);                                                                  \
                arguments = joinArgs(f1->getArgs(), f2->getArgs());                                       \
                singularities = joinSingl(f1->getSingl(), f2->getSingl());                                \
            }                                                                                             \
            FunkMath_##OPERATION(Funk f1, double x)                                                       \
            {                                                                                             \
                auto f2 = cnst(x);                                                                        \
                functions = vec(f1, f2);                                                                  \
                arguments = joinArgs(f1->getArgs(), f2->getArgs());                                       \
                singularities = joinSingl(f1->getSingl(), f2->getSingl());                                \
            }                                                                                             \
            double value(const std::vector<double> & data, size_t bindID)                                 \
            {                                                                                             \
                return OPERATION(functions[0]->value(data, bindID), functions[1]->value(data, bindID));   \
            }                                                                                             \
    };                                                                                                    \
    inline Funk OPERATION (Funk f1, Funk f2) { return Funk(new FunkMath_##OPERATION(f1, f2)); }           \
    inline Funk OPERATION (double x, Funk f) { return Funk(new FunkMath_##OPERATION(x, f)); }             \
    inline Funk OPERATION (Funk f, double x) { return Funk(new FunkMath_##OPERATION(f, x)); }
    MATH_OPERATION(pow)
    MATH_OPERATION(fmin)
    MATH_OPERATION(fmax)
#undef MATH_OPERATION

    ////////////////////////////////////////
    // *** End of core implementation ***
    ////////////////////////////////////////



    ////////////////////////////////////////
    //        *** Extensions ***
    ////////////////////////////////////////


    //
    // Derived class: 1dim linear or logarithmic interpolation
    //

    class FunkInterp : public FunkBase
    {
        public:
            FunkInterp(Funk f, std::vector<double> & Xgrid, std::vector<double> & Ygrid, std::string mode = "lin")
            {
                setup(f, Xgrid, Ygrid, mode);
            }
            FunkInterp(std::string arg, std::vector<double> & Xgrid, std::vector<double> & Ygrid, std::string mode = "lin")
            {
                setup(var(arg), Xgrid, Ygrid, mode);
            }
            FunkInterp(double x, std::vector<double> & Xgrid, std::vector<double> & Ygrid, std::string mode = "lin")
            {
                setup(cnst(x), Xgrid, Ygrid, mode);
            }

            double value(const std::vector<double> & data, size_t bindID)
            {
                functions[0]->value(data, bindID);
                return (this->*ptr)(data[indices[bindID][0]]);
            }

        private:
            void setup(Funk f, std::vector<double> & Xgrid, std::vector<double> & Ygrid, std::string mode)
            {
                // TODO: Catch invalid setup
                functions = vec(f);
                singularities = f->getSingl();
                arguments = f->getArgs();
                this->Xgrid = Xgrid;
                this->Ygrid = Ygrid;
                if ( mode == "lin" ) this->ptr = &FunkInterp::linearInterp;
                else if ( mode == "log" ) this->ptr = &FunkInterp::logInterp;
            }

            double logInterp(double x)
            {
                // Linear interpolation in log-log space
                int i = 0; int imax = Xgrid.size() - 1;
                if (x<Xgrid[0] or x>Xgrid[imax]) return 0;
                for (; i < imax; i++) {if (Xgrid[i] > x) break;};
                double x0 = Xgrid[i-1];
                double x1 = Xgrid[i];
                double y0 = Ygrid[i-1];
                double y1 = Ygrid[i];
                return y0 * std::exp(std::log(y1/y0) * std::log(x/x0) / std::log(x1/x0));
            }

            double linearInterp(double x)
            {
                // Linear interpolation in lin-lin space
                int i = 0; int imax = Xgrid.size() - 1;
                if (x<Xgrid[0] or x>Xgrid[imax]) return 0;
                for (; i < imax; i++) {if (Xgrid[i] > x) break;};
                double x0 = Xgrid[i-1];
                double x1 = Xgrid[i];
                double y0 = Ygrid[i-1];
                double y1 = Ygrid[i];
                return y0 + (x-x0)/(x1-x0)*(y1-y0);
            }

            double(FunkInterp::*ptr)(double);
            std::vector<double> Xgrid;
            std::vector<double> Ygrid;
            std::string mode;
    };
    template <typename T> inline shared_ptr<FunkInterp> interp(T f, std::vector<double> x, std::vector<double> y) { return shared_ptr<FunkInterp>(new FunkInterp(f, x, y)); }


    //
    // Basic if..else clause
    //

    class FunkIfElse: public FunkBase
    {
        public:
            FunkIfElse(Funk f, Funk g, Funk h)
            {
                functions = vec(f, g, h);
                singularities = joinSingl(joinSingl(f->getSingl(), g->getSingl()), h->getSingl());
                arguments = joinArgs(joinArgs(f->getArgs(), g->getArgs()), h->getArgs());
            }
            double value(const std::vector<double> & data, size_t bindID)
            {
              if ( functions[0]->value(data,bindID) >= 0. )
                return functions[1]->value(data,bindID);
              else
                return functions[2]->value(data,bindID);
            }
    };
    inline Funk ifelse(Funk f, Funk g, Funk h) { return Funk(new FunkIfElse(f, g, h)); }
    inline Funk ifelse(Funk f, double g, Funk h) { return Funk(new FunkIfElse(f, cnst(g), h)); }
    inline Funk ifelse(Funk f, double g, double h) { return Funk(new FunkIfElse(f, cnst(g), cnst(h))); }
    inline Funk ifelse(Funk f, Funk g, double h) { return Funk(new FunkIfElse(f, g, cnst(h))); }


    //
    // Throw errors when called
    //

    class ThrowError: public FunkBase
    {
        public:
            ThrowError(std::string msg) : msg(msg)
            {
            }
            double value(const std::vector<double> & data, size_t bindID)
            {
              (void)bindID;
              (void)data;
#ifdef GAMBIT_DIR
              if ( omp_get_level() == 0 )  // Outside of OMP blocks
              {
                  Gambit::utils_error().raise(LOCAL_INFO, "daFunk::ThrowError says: " + msg);
              }
              else  // Inside of OMP blocks
              {
                  Gambit::piped_errors.request(LOCAL_INFO, "daFunk::ThrowError says: " + msg);
              }
#else
              throw std::invalid_argument("daFunk::ThrowError says: " + msg);
#endif
              return 0;
            }

        private:
            std::string msg;  // Error message to throw when function is called
    };
    inline Funk throwError(std::string msg) { return Funk(new ThrowError(msg)); }

#ifdef GAMBIT_DIR
    class RaiseInvalidPoint: public FunkBase
    {
        public:
            RaiseInvalidPoint(std::string msg) : msg(msg)
            {
            }
            double value(const std::vector<double> & data, size_t bindID)
            {
              (void)bindID;
              (void)data;
              if ( omp_get_level() == 0 )  // Outside of OMP blocks
              {
                  Gambit::utils_warning().raise(LOCAL_INFO, "daFunk::RaiseInvalidPoint says: " + msg);
                  Gambit::invalid_point().raise("daFunk::RaiseInvalidPoint says: " + msg);
              }
              else  // Inside OMP blocks
              {
                  Gambit::utils_warning().raise(LOCAL_INFO, "daFunk::RaisePipedInvalidPoint says: " + msg);
                  Gambit::piped_invalid_point.request("daFunk::RaisePipedInvalidPoint says: " + msg);
              }
              return 0;
            }

        private:
            std::string msg;  // Error message to throw when function is called
    };
    inline Funk raiseInvalidPoint(std::string msg) { return Funk(new RaiseInvalidPoint(msg)); }
#endif


    //
    // Prints message when called
    //

    class Bottle: public FunkBase
    {
        public:
            Bottle(Funk f, std::string msg) : msg(msg)
            {
                functions = vec(f);
                singularities = f->getSingl();
                arguments = f->getArgs();
            }
            double value(const std::vector<double> & data, size_t bindID)
            {
              std::cout << "daFunk::Message says:\n" << msg << std::endl;
              return functions[0]->value(data, bindID);
            }

        private:
            std::string msg;  // Message in a bottle.  Ha!
    };
    //inline Funk print(std::string msg) { return Funk(new Bottle(msg)); }
    inline Funk FunkBase::print(std::string msg)
    { return Funk(new Bottle(shared_from_this(), msg)); };


    //
    // GSL integration
    //

    class FunkIntegrate_gsl1d: public FunkBase, public gsl_function
    {
        public:
            FunkIntegrate_gsl1d(Funk f0, std::string arg, Funk f1, Funk f2)
            {
                setup(f0, arg, f1, f2);
            }
            FunkIntegrate_gsl1d(Funk f0, std::string arg, double x, Funk f)
            {
                setup(f0, arg, cnst(x), f);
            }
            FunkIntegrate_gsl1d(Funk f0, std::string arg, double x, double y)
            {
                setup(f0, arg, cnst(x), cnst(y));
            }
            FunkIntegrate_gsl1d(Funk f0, std::string arg, Funk f, double x)
            {
                setup(f0, arg, f, cnst(x));
            }
            FunkIntegrate_gsl1d(Funk f0, std::string arg, std::string x, Funk f)
            {
                setup(f0, arg, var(x), f);
            }
            FunkIntegrate_gsl1d(Funk f0, std::string arg, std::string x, std::string y)
            {
                setup(f0, arg, var(x), var(y));
            }
            FunkIntegrate_gsl1d(Funk f0, std::string arg, Funk f, std::string x)
            {
                setup(f0, arg, f, var(x));
            }
            FunkIntegrate_gsl1d(Funk f0, std::string arg, std::string x, double y)
            {
                setup(f0, arg, var(x), cnst(y));
            }
            FunkIntegrate_gsl1d(Funk f0, std::string arg, double y, std::string x)
            {
                setup(f0, arg, cnst(y), var(x));
            }

            void resolve(std::map<std::string, size_t> datamap, size_t & datalen, size_t bindID, std::map<std::string,size_t> &argmap)
            {
                functions[1]->resolve(datamap, datalen, bindID, argmap);  // Resolve boundary 0
                functions[2]->resolve(datamap, datalen, bindID, argmap);  // Resolve boundary 1

                // Set indices
                if(index.size() <= bindID)
                {
                    index.resize(bindID+1);
                    // TODO: Throw error if problems are encountered
                    //std::cout << "FATAL ERROR: bind() attempts to resolve dependencies of FunkIntegrate_gsl1d" << std::endl;
                    //std::cout << "object inconsistently." << std::endl;
                    //std::cout << "Encountered while resolving " << arg << " and:" << std::endl;
                    //for (auto it = datamap.begin(); it != datamap.end(); it++)
                    //{
                    //    std::cout << "  " << it->first << std::endl;
                    //}
                    //exit(1);
                }
                if ( argmap.find(arg) == argmap.end() )
                {
                    if(datamap.find(arg) == datamap.end())
                    {
                        index[bindID] = datalen;
                        argmap[arg]   = datalen;
                        ++datalen;
                    }
                    else
                    {
                        index[bindID] = datamap[arg];
                    }
                }
                else
                {
                    index[bindID] = argmap[arg];
                }
                datamap[arg]  = index[bindID];
                functions[0]->resolve(datamap, datalen, bindID, argmap);
                for ( auto it = my_singularities.begin(); it != my_singularities.end(); ++it )
                {
                    it->first->resolve(datamap, datalen, bindID, argmap);
                    it->second->resolve(datamap, datalen, bindID, argmap);
                }
            }

            ~FunkIntegrate_gsl1d()
            {
                gsl_integration_workspace_free(gsl_workspace);
            }

            shared_ptr<FunkIntegrate_gsl1d> set_epsrel(double epsrel)
            { this->epsrel = epsrel; return static_pointer_cast<FunkIntegrate_gsl1d>(this->FunkIntegrate_gsl1d::shared_from_this()); }
            shared_ptr<FunkIntegrate_gsl1d> set_epsabs(double epsabs)
            { this->epsabs = epsabs; return static_pointer_cast<FunkIntegrate_gsl1d>(this->shared_from_this()); }
            shared_ptr<FunkIntegrate_gsl1d> set_limit(size_t limit)
            { this->limit = limit; return static_pointer_cast<FunkIntegrate_gsl1d>(this->shared_from_this()); }
            shared_ptr<FunkIntegrate_gsl1d> set_singularity_factor(double f)
            { this->singl_factor = f; return static_pointer_cast<FunkIntegrate_gsl1d>(this->shared_from_this()); }
            shared_ptr<FunkIntegrate_gsl1d> set_use_log_fallback(bool flag)
            { this->use_log_fallback = flag; return static_pointer_cast<FunkIntegrate_gsl1d>(this->shared_from_this()); }

            double value(const std::vector<double> & data, size_t bindID)
            {
                double result;
                #pragma omp critical(FunkIntegrate_gsl1d_integration)
                {
                    local_data = data;
                    local_bindID = bindID;
                    double error;
                    function=&FunkIntegrate_gsl1d::invoke;
                    params=this;
                    double x0 = functions[1]->value(data, bindID);
                    double x1 = functions[2]->value(data, bindID);
                    gsl_set_error_handler_off();
                    int status = 0;
                    if ( my_singularities.size() == 0 )
                    {
                        status = gsl_integration_qags(this, x0, x1, epsabs, epsrel, limit, gsl_workspace, &result, &error);
                    }
                    else
                    {
                        double s = 0;
                        std::vector<double> ranges;
                        ranges.push_back(x0);
                        ranges.push_back(x1);
                        for ( auto it = my_singularities.begin(); it != my_singularities.end(); ++it )
                        {
                            double mean = it->first->value(data, bindID);
                            double sigma = it->second->value(data, bindID);
                            double z0 = mean - singl_factor*sigma;
                            double z1 = mean + singl_factor*sigma;
                            if ( z0 == z1 )
                                std::cout << "daFunk::FunkBase WARNING: Singularity width is beyond machine precision." << std::endl;
                            if ( z0 > x0 and z0 < x1 ) ranges.push_back(z0);
                            if ( z1 > x0 and z1 < x1 ) ranges.push_back(z1);
                        }
                        std::sort(ranges.begin(), ranges.end());
                        for ( auto it = ranges.begin(); it != ranges.end()-1; ++it )
                        {
                            status = gsl_integration_qags(this, *it, *(it+1), epsabs, epsrel, limit, gsl_workspace, &result, &error);
                            s += result;
                            if (status) break;
                        }
                        result = s;
                    }
                    if (status and this->use_log_fallback)
                    {
                        // The last resort: A cheap integration on log grid, linear interpolation
                        const double N = 300;
                        std::vector<double> Xgrid = 
                            logspace(std::log10(x0), std::log10(x1), N);
                        double sum = 0, y0, y1, dx;
                        local_data[index[bindID]] = Xgrid[0];
                        y0 = functions[0]->value(local_data, bindID);
                        for (size_t i = 0; i<N-1; i++)
                        {
                            local_data[index[bindID]] = Xgrid[i+1];
                            y1 = functions[0]->value(local_data, bindID);
                            dx = Xgrid[i+1]-Xgrid[i];
                            sum += dx*(y0+y1)/2;
                            y0 = y1;
                        }
                        result = sum;
                    }
                    // TODO: Implement flags to optionally throw an error
                    if (status and not this->use_log_fallback)
                    {
                        std::cerr << "daFunk::FunkIntegrate_gsl1d WARNING: " << gsl_strerror(status) << std::endl;
                        std::cerr << "Attempt to integrate from " << x0 << " to " << x1 << std::endl;
                        std::cerr << "Attempt to integrate from " << x0 << " to " << x1 << std::endl;
                        std::cerr << "Details about the integrand:" << std::endl;
                        functions[0]->help();
//                        std::cout << "Dumping integrand:" << std::endl;
//                        for ( double x = x0; x <= x1; x = (x0>0) ? x*1.01 : x+(x1-x0)/1000)
//                            std::cerr << "  " << x << " " << invoke(x, this) << std::endl;
                        std::cerr << "Returning zero." << std::endl;
                        result = 0.;
                    }
                }
                return result;
            }

        private:
            void setup(Funk f0, std::string arg, Funk f1, Funk f2)
            {
                this->functions = vec(f0, f1, f2);

                singularities = joinSingl(f1->getSingl(), f2->getSingl());
                if ( f0->getSingl().find(arg) != f0->getSingl().end() )
                    my_singularities = f0->getSingl()[arg];
                Singularities tmp_singl = f0->getSingl();
                tmp_singl.erase(arg);
                singularities = joinSingl(singularities, tmp_singl);

                arguments = joinArgs(eraseArg(f0->getArgs(), arg), joinArgs(f1->getArgs(), f2->getArgs()));
                gsl_workspace = gsl_integration_workspace_alloc (100000);

                this->arg = arg;
                limit = 100;
                epsrel = 1e-2;
                epsabs = 1e-2;
                use_log_fallback = false;
                singl_factor = 4;
            }

            // Static member function that invokes integrand
            static double invoke(double x, void *params) {
                FunkIntegrate_gsl1d * ptr = static_cast<FunkIntegrate_gsl1d*>(params);
                ptr->local_data[ptr->index[ptr->local_bindID]] = x;
                return ptr->functions[0]->value(ptr->local_data, ptr->local_bindID);
            }

            // Required for rewiring input parameters
            std::vector<double> local_data;
            size_t local_bindID;
            std::vector<std::pair<Funk, Funk>> my_singularities;

            // Integration range and function pointer
            std::string arg;

            // GSL workspace and parameters
            gsl_integration_workspace * gsl_workspace;
            size_t limit;
            std::vector<size_t> index;
            double epsrel;
            double epsabs;
            bool use_log_fallback;

            double singl_factor;
    };

    // Standard behaviour
    template <typename T1, typename T2>
    inline shared_ptr<FunkIntegrate_gsl1d> getIntegrate_gsl1d(Funk fptr, std::string arg, T1 x1, T2 x2) { return shared_ptr<FunkIntegrate_gsl1d>(new FunkIntegrate_gsl1d(fptr, arg, x1, x2)); }

    //
    // Helper function for producing singularity-augmented x-grids
    //

    inline std::vector<double> augmentSingl(const std::vector<double> & xgrid, Funk f, int N = 100, double sigma = 3)
    {
        std::vector<double> result = xgrid;
        double xmin = result.front();
        double xmax = result.back();

        if ( f->getNArgs() != 1 )
        {
            std::string msg = "augment_with_singularities(): takes only functions with one argument.\n";
                        msg+= "  --- Actual arguments are: " + args_string(f->getArgs());
#ifdef GAMBIT_DIR
            Gambit::utils_error().raise(LOCAL_INFO, msg);
#else
            throw std::invalid_argument(msg);
#endif
        }

        std::string arg = f->getArgs()[0];
        Singularities singlsMap = f->getSingl();

        if ( singlsMap.find(arg) != singlsMap.end() )
        {
            auto singls = singlsMap.at(arg);
            for (auto it = singls.begin(); it != singls.end(); it ++)
            {
                double position= it->first->bind()->eval();
                double width = it->second->bind()->eval();
                std::vector<double> singlgrid = linspace(std::max(position-width*sigma, xmin), std::min(position+width*sigma, xmax), N);
                result.insert(result.end(), singlgrid.begin(), singlgrid.end());
            }
        }
        // Sort the values only once
        std::sort(result.begin(), result.end());

        // Only use unique values, i.e. do not resolve the same singularity twice.
        result.erase(std::unique(result.begin(), result.end()), result.end());

        return result;
    }
}


#endif  // __DAFUNK_HPP__

Updated on 2023-06-26 at 21:36:55 +0000