Search code examples
pythoncachingscipyscikitsroot-framework

optimize function evaluations caching partial result


Suppose I have a complex mathematical function with many input parameters P = [p1, ..., pn]. Suppose that I can factor the function in blocks, for example:

f(P) = f1(p1, p2) * f2(p2, ... pn)

and maybe

f2(p2, ..., pn) = p2 * f3(p4) + f4(p5, ..., pn)

suppose I have to evaluate f for many value of P, for example I want to find the minimum of f. Suppose I have already computed f(P) and I need to compute f(P') where P' is equal to P except for p1. In this case I don't have to recompute f2, f3, f4, but only f1.

Is there a library which help me to implement this kind of caching system? I know RooFit, but it is oriented to statistical model, made by blocks. I am looking for something more general. scipy / scikits and similar are preferred, but also c++ libraries are ok. Has this technique a name?


Solution

  • If you can write these function to be pure functions (which means that they always return the same value for the same arguments, and have no side effects), you can use memoization, which is a method for saving results of function calls.

    try:
        from functools import lru_cache  # Python 3.2+
    except ImportError:  # Python 2
        # Python 2 and Python 3.0-3.1
        # Requires the third-party functools32 package
        from functools32 import lru_cache
    
    @lru_cache(maxsize=None)
    def f(arg):
        # expensive operations
        return result
    
    x = f('arg that will take 10 seconds')  # takes 10 seconds
    y = f('arg that will take 10 seconds')  # virtually instant
    

    For illustration, or if you don't want to use functools32 on Python < 3.2:

    def memoize(func):
        memo = {}
    
        def wrapper(*args):
            if args not in memo:            
                memo[args] = func(*args)
            return memo[args]
    
        return helper
    
    @memoize
    def f(arg):
        # expensive operations
        return result
    
    x = f('arg that will take 10 seconds')  # takes 10 seconds
    y = f('arg that will take 10 seconds')  # virtually instant