summaryrefslogtreecommitdiff
path: root/diffeq.py
blob: 1ecc705bb748050af393b043b35c26809b72aa9f (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
import time, types, itertools
from scipy.interpolate import interp1d
from scipy.integrate import odeint
from scipy.optimize import fmin

class DiffEq(object):
    """designed to make numerically solving diff eqs more readable
    
    setattr and getattr have been modified to provide the following effects:
        >>> self._foo=2 #setting an _under var makes it still callable by...
        >>> self.foo
        2
        
        >>> # Now suppose foo is a class method and bar is in _flags.
        >>> self.foo    #finding the object having not found it before
                        #self.foo() is called and stored to self.__foo
                        #self.__foo is returned
        
        >>> self.foo    #finding the object having asked for it before
                        #self.__foo is returned
        
        >>> self.bar=2  #setting bar resets all self.__foos
        
        >>> self.foo    #self.foo() is called again, self.__foo returned
    
    Note that with this behavior you should treat all variable names, be they
    normal, _under, or __dunder, as in the same namespace. Don't even try to
    differentiate variable names by underscores.
    
    The benefit of this system is that every normal float will be optimized,
    while every _under float will be ignored completely, and flagging a var
    with _under will not change the behavior of any functions using it.
    Additionally, if you suddenly realize that foo should be dependent on
    bar and baz, you simply define:
        def foo(self):
            return do_stuff(self.bar, self.baz)
    and bar and baz will be optimized while foo will be ignored -- and no
    function code using foo needs to be rewritten.
    """
    def __init__(self):
        self._time = time.time()
        
        # All floats lacking an initial _underscore will be optimized.
        self.opt_vars = {}
        for key, value in vars(self).items():
            if key[0] == "_": continue
            if type(value) != float: continue
            self.opt_vars[key] = value
    def time(self): return time.time() - self.time
    
    def __getattribute__(self, attr):
        """Provides a few getattr psuedonyms:
        
        allows self.foo to return:
            self._foo  if self.foo DNE
            self.foo() if self.foo is a method
        (be very wary of storing variables under these conditions!)
        """
        sgetattr = super(DiffEq, self).__getattribute__
        try:
            value = sgetattr(attr)
            if type(value) == types.MethodType:
                try: value = sgetattr("__" + attr)
                except AttributeError:
                    value = value()
                    setattr(self, "__" + attr, value)
        except AttributeError as err:
            if attr[0] == "_": raise err
            else: value = sgetattr("_" + attr)
        return value
    
    _flags = []
    def __setattribute__(self, attr):
        """Clears dunder updates when phi or phi1 are changed."""
        sgetattr = super(DiffEq, self).__getattribute__
        ssetattr = super(DiffEq, self).__setattribute__
        if attr in self.flags:
            for key, value in vars(self).items():
                if key[:2] != "__": continue
                if type(sgetattr(key[2:])) != types.MethodType: continue
                vars(self).remove(key)
        ssetattr(attr)
    
    def check_in(self, variables):
        """Usage is as follows.
        
        def method(self, var0=None, var1=None, ...):
            self.check_in(vars())
            return do_stuff_with_vars()
        """
        for key, var in variables.items():
            if var == None:
                variables[key] = getattr(self, key)
    
    def solve_fun(self, func=None, init_vars=None, timespace=None):
        """wrapper for odeint"""
        check_in(vars())
        time0 = current_time()
        time = numpy.linspace(*timespace)
        soln = odeint(func, init_vars, time)
        print "  ::  took %s seconds" % (current_time() - time0)
        return time, soln
    
    #in optimize(): vars().update(self.opt_vars)