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
def from_generator(iterable_type):
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.

def rec_iter(x, iterables):
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.

class pair:
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

pair(first, second)
38	def __init__(self,first,second):
39		self.first=first
40		self.second=second
first
second
def dict_like(a):
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.

def map_iterables(f, a, iterables, split=False):
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

def map_iterables2(f, a, b, 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

def recurse(step, niter=1):
87def recurse(step,niter=1):
88	def operator(rhs):
89		nonlocal step,niter
90		for i in range(niter):
91			rhs=step(rhs)
92		return rhs
93	return operator
def decorator_with_arguments(decorator):
 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.)

def decorate_module_functions( module, decorator, copy_module=True, fct_names=None, ret_decorated=False):
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.
def func_except_alt(func, exception, alt):
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.