source: branches/decorate/wrapper.py @ 855

Revision 855, 4.8 KB checked in by mmckerns, 5 months ago (diff)

updated copyright to 2016

Line 
1#!/usr/bin/env python
2#
3# Author: Mike McKerns (mmckerns @caltech and @uqfoundation)
4# Copyright (c) 2012-2016 California Institute of Technology.
5# License: 3-clause BSD.  The full license text is available at:
6#  - http://mmckerns.github.io/project/mystic/browser/mystic/LICENSE
7
8#XXX: be mindful if the decorators restrict to functions that expect arrays
9#     compare against the originals for restrictions
10
11#XXX: when_decorated registers methods to 'populate up' when is decorated ?
12
13from mystic.tools import Null
14def monitored(monitor=Null):
15    """bind a monitor to a function object"""
16    def dec(f):
17        def func(x, *args, **kwds):
18            fval =  f(x, *args, **kwds)
19            monitor(x, fval)
20            return fval
21        return func
22    return dec
23
24
25def counted(f):
26    """bind a counter to a function object"""
27    ncalls = [0]
28    def func(x, *args, **kwds):
29        ncalls[0] += 1
30        fval =  f(x, *args, **kwds)
31        return ncalls, fval
32    return func
33
34
35def monitoredcounted(monitor=Null):
36    """bind a counter and a monitor to a function object"""
37    def dec(f):
38        ncalls = [0]
39        def func(x, *args, **kwds):
40            ncalls[0] += 1
41            fval =  f(x, *args, **kwds)
42            monitor(x, fval)
43            return ncalls, fval
44        return func
45    return dec
46
47
48from numpy import asarray, any, inf, vectorize
49def bounded(min=None, max=None):
50    """impose an infinite barrier box constraint on a function
51
52If any of the bounds are violated, the function will evaluate to 'inf'
53
54Example:
55>>> @bounded(1,10)
56... def squared(x):
57...   return x**2
58
59>>> squared(0)
60inf
61>>> squared(10)
62100
63>>> squared(11)
64inf
65    """
66    bounds = True
67    if min is not None and max is not None: #has upper & lower bound
68        min = asarray(min)
69        max = asarray(max)
70    elif min is not None: #has lower bound
71        min = asarray(min)
72        max = asarray([inf for i in min])
73    elif max is not None: #has upper bound
74        max = asarray(max)
75        min = asarray([-inf for i in max])
76    else: #not bounded
77        bounds = False
78    def dec(f):
79        if bounds:
80            def func(x, *args, **kwds):
81                if any((x<min)|(x>max)): #if violate bounds, evaluate as inf
82                    return inf #XXX: better, return x with xi=inf where violate?
83                return f(x, *args, **kwds)
84        else:
85            def func(x, *args, **kwds):
86                return f(x, *args, **kwds)
87        return func
88    return dec
89
90
91def isbounded(func, x, min=None, max=None):
92    """return False if func(x) evaluates outside the bounds, True otherwise.
93
94Inputs:
95    func -- a function of x.
96    x -- a list of parameters.
97
98Additional Inputs:
99    min -- list of lower bounds on parameters.
100    max -- list of upper bounds on parameters.
101"""
102    #from numpy import clip, array   #XXX: numpy.clip doesn't need/use func
103    #return all(clip(x,min,max) == array(x))
104    bound = bounded(min,max)
105    wrapped = bound(func)
106    if wrapped(x) == inf:
107        return False   
108    return True
109
110
111def mixedin(scale=1.0, shift=0.0, mixin=lambda x:x, normalized=False):
112  """build a function from the weighted sum of two functions
113
114The resulting function is: f'(x) = scale * f(x) + shift * mixin(x).
115If normalized, then f' = f' / (scale + shift)"""
116  def dec(f):
117    def func(*args, **kwds):
118      norm = 1.0
119      if normalized: norm = scale + shift
120      return (scale * f(*args, **kwds) + shift * mixin(*args, **kwds)) / norm
121    return func
122  return dec
123
124
125#FIXME: the following should preserve the input type
126def __clip_bound(x, amin, amax):
127  return max(min(x, amax), amin)
128clip_bound = vectorize(__clip_bound)
129
130def __bounce_bound(x, amin, amax):
131  if not x <= amax:
132    x = amax-(amax-amin)*((x-amax)/(x+amax))
133  if not x >= amin:
134    x = amin+(amax-amin)*((amin-x)/(amin+x))
135  return x
136bounce_bound = vectorize(__bounce_bound)
137
138def __target_bound(x, amin, amax, target):
139  if not x <= amax:
140    x = min(target, amax)
141  if not x >= amin:
142    x = max(target, amin)
143  return x
144target_bound = vectorize(__target_bound)
145
146"""
147def _clip_bound(x, amin, amax):
148  return [__clip_bound(xi,li,ui) for xi,li,ui in zip(x,amin,amax)]
149
150def _bounce_bound(x, amin, amax):
151  return [__bounce_bound(xi,li,ui) for xi,li,ui in zip(x,amin,amax)]
152
153def _target_bound(x, amin, amax, target):
154  return [__target_bound(xi,li,ui,target) for xi,li,ui in zip(x,amin,amax)]
155"""
156
157def clip_bounded(min, max):
158    def dec(f):
159        def func(x, *args, **kwds):
160            return f(clip_bound(x, min, max), *args, **kwds)
161        return func
162    return dec
163
164def bounce_bounded(min, max):
165    def dec(f):
166        def func(x, *args, **kwds):
167            return f(bounce_bound(x, min, max), *args, **kwds)
168        return func
169    return dec
170
171def target_bounded(min, max, target):
172    def dec(f):
173        def func(x, *args, **kwds):
174            return f(target_bound(x, min, max, target), *args, **kwds)
175        return func
176    return dec
177
178
179# EOF
Note: See TracBrowser for help on using the repository browser.