agd.AutomaticDifferentiation.functional
1# Copyright 2020 Jean-Marie Mirebeau, University Paris-Sud, CNRS, University Paris-Saclay 2# Distributed WITHOUT ANY WARRANTY. Licensed under the Apache License, Version 2.0, see http://www.apache.org/licenses/LICENSE-2.0 3 4import functools 5import types 6 7 8""" 9This file collects functional like methods used throughout the AD library. 10""" 11 12# --------- (Recursive) iteration -------- 13 14def from_generator(iterable_type): 15 """ 16 Returns the method for constructing an object from a generator. 17 """ 18 return getattr(iterable_type,'from_generator',iterable_type) 19 20 21def rec_iter(x,iterables): 22 """ 23 Iterate recursively over x. 24 In the case of dictionnaries, if specified among the iterables, one iterates over values. 25 """ 26 if isinstance(x,iterables): 27 if isinstance(x,dict): x=x.values() 28 for y in x: 29 for z in rec_iter(y,iterables): yield z 30 else: yield x 31 32class pair(object): 33 """ 34 A two element iterable. 35 Introduced as an alternative of tuple, to avoid confusion in map_iterables 36 """ 37 def __init__(self,first,second): 38 self.first=first 39 self.second=second 40 def __iter__(self): 41 yield self.first 42 yield self.second 43 def __str__(self): 44 return "pair("+str(self.first)+","+str(self.second)+")" 45 def __repr__(self): 46 return "pair("+repr(self.first)+","+repr(self.second)+")" 47 48def dict_like(a): 49 """ 50 Wether a, type or instance, has 'items' attribute. 51 Will be regarded as dict-like structure. 52 """ 53 return hasattr(a,'items') 54 55def map_iterables(f,a,iterables,split=False): 56 """ 57 Apply f to variable 'a' exploring recursively certain iterables 58 """ 59 if isinstance(a,iterables): 60 type_a = type(a) 61 if dict_like(a): 62 result = type_a({key:map_iterables(f,val,iterables,split=split) for key,val in a.items()}) 63 if split: return type_a({key:a for key,(a,_) in a.items()}), type_a({key:a for key,(_,a) in a.items()}) 64 else: return result 65 else: 66 ctor_a = from_generator(type_a) 67 result = ctor_a(map_iterables(f,val,iterables,split=split) for val in a) 68 if split: return ctor_a(a for a,_ in result), ctor_a(a for _,a in result) 69 else: return result 70 return f(a) 71 72def map_iterables2(f,a,b,iterables): 73 """ 74 Apply f to variable 'a' and 'b' zipped, exploring recursively certain iterables 75 """ 76 for type_iterable in iterables: 77 if isinstance(a,type_iterable): 78 if dict_like(a): 79 return type_iterable({key:map_iterables2(f,a[key],b[key],iterables) for key in a}) 80 else: 81 return from_generator(type_iterable)(map_iterables2(f,ai,bi,iterables) for ai,bi in zip(a,b)) 82 return f(a,b) 83 84# -------- Decorator related functions -------- 85 86def recurse(step,niter=1): 87 def operator(rhs): 88 nonlocal step,niter 89 for i in range(niter): 90 rhs=step(rhs) 91 return rhs 92 return operator 93 94def decorator_with_arguments(decorator): 95 """ 96 Decorator intended to simplify writing decorators with arguments. 97 (In addition to the decorated function itself.) 98 """ 99 @functools.wraps(decorator) 100 def wrapper(f=None,*args,**kwargs): 101 if f is None: return lambda f: decorator(f,*args,**kwargs) 102 else: return decorator(f,*args,**kwargs) 103 return wrapper 104 105 106def decorate_module_functions(module,decorator, 107 copy_module=True,fct_names=None,ret_decorated=False): 108 """ 109 Decorate the functions of a module. 110 Inputs : 111 - module : whose functions must be decorated 112 - decorator : to be applied 113 - copy_module : create a shallow copy of the module 114 - fct_names (optional) : list of functions to be decorated. 115 If unspecified, all functions, builtin functions, and builtin methods, are decorated. 116 """ 117 if copy_module: #Create a shallow module copy 118 new_module = type(module)(module.__name__, module.__doc__) 119 new_module.__dict__.update(module.__dict__) 120 module = new_module 121 122 decorated = [] 123 124 for key,value in module.__dict__.items(): 125 if fct_names is None: 126 if not isinstance(value,(types.FunctionType,types.BuiltinFunctionType, 127 types.BuiltinMethodType)): 128 continue 129 elif key not in fct_names: 130 continue 131 decorated.append(key) 132 module.__dict__[key] = decorator(value) 133 return (module,decorated) if ret_decorated else module 134 135def func_except_alt(func,exception,alt): 136 """ 137 Returns a callable which evaluates func, but falls back to alt if exception is caught. 138 """ 139 @functools.wraps(func) 140 def wrapper(*args,**kwargs): 141 try: return func(*args,**kwargs) 142 except exception: return alt(*args,**kwargs) 143 return wrapper
15def from_generator(iterable_type): 16 """ 17 Returns the method for constructing an object from a generator. 18 """ 19 return getattr(iterable_type,'from_generator',iterable_type)
Returns the method for constructing an object from a generator.
22def rec_iter(x,iterables): 23 """ 24 Iterate recursively over x. 25 In the case of dictionnaries, if specified among the iterables, one iterates over values. 26 """ 27 if isinstance(x,iterables): 28 if isinstance(x,dict): x=x.values() 29 for y in x: 30 for z in rec_iter(y,iterables): yield z 31 else: yield x
Iterate recursively over x. In the case of dictionnaries, if specified among the iterables, one iterates over values.
33class pair(object): 34 """ 35 A two element iterable. 36 Introduced as an alternative of tuple, to avoid confusion in map_iterables 37 """ 38 def __init__(self,first,second): 39 self.first=first 40 self.second=second 41 def __iter__(self): 42 yield self.first 43 yield self.second 44 def __str__(self): 45 return "pair("+str(self.first)+","+str(self.second)+")" 46 def __repr__(self): 47 return "pair("+repr(self.first)+","+repr(self.second)+")"
A two element iterable. Introduced as an alternative of tuple, to avoid confusion in map_iterables
49def dict_like(a): 50 """ 51 Wether a, type or instance, has 'items' attribute. 52 Will be regarded as dict-like structure. 53 """ 54 return hasattr(a,'items')
Wether a, type or instance, has 'items' attribute. Will be regarded as dict-like structure.
56def map_iterables(f,a,iterables,split=False): 57 """ 58 Apply f to variable 'a' exploring recursively certain iterables 59 """ 60 if isinstance(a,iterables): 61 type_a = type(a) 62 if dict_like(a): 63 result = type_a({key:map_iterables(f,val,iterables,split=split) for key,val in a.items()}) 64 if split: return type_a({key:a for key,(a,_) in a.items()}), type_a({key:a for key,(_,a) in a.items()}) 65 else: return result 66 else: 67 ctor_a = from_generator(type_a) 68 result = ctor_a(map_iterables(f,val,iterables,split=split) for val in a) 69 if split: return ctor_a(a for a,_ in result), ctor_a(a for _,a in result) 70 else: return result 71 return f(a)
Apply f to variable 'a' exploring recursively certain iterables
73def map_iterables2(f,a,b,iterables): 74 """ 75 Apply f to variable 'a' and 'b' zipped, exploring recursively certain iterables 76 """ 77 for type_iterable in iterables: 78 if isinstance(a,type_iterable): 79 if dict_like(a): 80 return type_iterable({key:map_iterables2(f,a[key],b[key],iterables) for key in a}) 81 else: 82 return from_generator(type_iterable)(map_iterables2(f,ai,bi,iterables) for ai,bi in zip(a,b)) 83 return f(a,b)
Apply f to variable 'a' and 'b' zipped, exploring recursively certain iterables
95def decorator_with_arguments(decorator): 96 """ 97 Decorator intended to simplify writing decorators with arguments. 98 (In addition to the decorated function itself.) 99 """ 100 @functools.wraps(decorator) 101 def wrapper(f=None,*args,**kwargs): 102 if f is None: return lambda f: decorator(f,*args,**kwargs) 103 else: return decorator(f,*args,**kwargs) 104 return wrapper
Decorator intended to simplify writing decorators with arguments. (In addition to the decorated function itself.)
107def decorate_module_functions(module,decorator, 108 copy_module=True,fct_names=None,ret_decorated=False): 109 """ 110 Decorate the functions of a module. 111 Inputs : 112 - module : whose functions must be decorated 113 - decorator : to be applied 114 - copy_module : create a shallow copy of the module 115 - fct_names (optional) : list of functions to be decorated. 116 If unspecified, all functions, builtin functions, and builtin methods, are decorated. 117 """ 118 if copy_module: #Create a shallow module copy 119 new_module = type(module)(module.__name__, module.__doc__) 120 new_module.__dict__.update(module.__dict__) 121 module = new_module 122 123 decorated = [] 124 125 for key,value in module.__dict__.items(): 126 if fct_names is None: 127 if not isinstance(value,(types.FunctionType,types.BuiltinFunctionType, 128 types.BuiltinMethodType)): 129 continue 130 elif key not in fct_names: 131 continue 132 decorated.append(key) 133 module.__dict__[key] = decorator(value) 134 return (module,decorated) if ret_decorated else module
Decorate the functions of a module. Inputs :
- module : whose functions must be decorated
- decorator : to be applied
- copy_module : create a shallow copy of the module
- fct_names (optional) : list of functions to be decorated. If unspecified, all functions, builtin functions, and builtin methods, are decorated.
136def func_except_alt(func,exception,alt): 137 """ 138 Returns a callable which evaluates func, but falls back to alt if exception is caught. 139 """ 140 @functools.wraps(func) 141 def wrapper(*args,**kwargs): 142 try: return func(*args,**kwargs) 143 except exception: return alt(*args,**kwargs) 144 return wrapper
Returns a callable which evaluates func, but falls back to alt if exception is caught.