Hide keyboard shortcuts

Hot-keys on this page

r m x p   toggle line displays

j k   next/prev highlighted chunk

0   (zero) top of page

1   (one) first highlighted chunk

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

107

108

109

110

111

112

113

114

115

116

117

118

119

120

121

122

123

124

125

126

127

128

129

130

131

132

133

134

135

136

137

138

139

140

141

142

143

144

145

146

147

148

149

150

151

152

153

154

155

156

157

158

159

160

161

162

163

164

165

166

167

168

169

170

171

172

173

174

175

176

177

178

179

180

181

182

183

184

185

186

187

188

189

190

191

192

193

194

195

196

197

198

199

200

201

202

203

204

205

206

207

208

209

210

211

212

213

214

215

216

217

218

# Copyright 2011-2015 Kwant authors. 

# 

# This file is part of Kwant. It is subject to the license terms in the file 

# LICENSE.rst found in the top-level directory of this distribution and at 

# http://kwant-project.org/license. A list of Kwant authors can be found in 

# the file AUTHORS.rst at the top-level directory of this distribution and at 

# http://kwant-project.org/authors. 

 

import sys 

import numpy as np 

import numbers 

import inspect 

import warnings 

import importlib 

import functools 

import collections 

from contextlib import contextmanager 

 

__all__ = ['KwantDeprecationWarning', 'UserCodeError'] 

 

 

class KwantDeprecationWarning(Warning): 

"""Class of warnings about a deprecated feature of Kwant. 

 

DeprecationWarning has been made invisible by default in Python 2.7 in order 

to not confuse non-developer users with warnings that are not relevant to 

them. In the case of Kwant, by far most users are developers, so we feel 

that a KwantDeprecationWarning that is visible by default is useful. 

""" 

pass 

 

 

class UserCodeError(Exception): 

"""Class for errors that occur in user-provided code. 

 

Usually users will define value functions that Kwant calls in order to 

evaluate the Hamiltonian. If one of these function raises an exception 

then it is caught and this error is raised in its place. This makes it 

clear that the error is from the user's code (and not a bug in Kwant) and 

also makes it possible for any libraries that wrap Kwant to detect when a 

user's function causes an error. 

""" 

pass 

 

 

def deprecate_parameter(parameter_name, version=None, help=None, 

stacklevel=3): 

"""Trigger a deprecation warning if the wrapped function is called 

with the provided parameter.""" 

 

message = ("The '{}' parameter has been deprecated since version {} -- {}" 

.format(parameter_name, version, help)) 

 

def warn(): 

warnings.warn(message, KwantDeprecationWarning, 

stacklevel=stacklevel) 

 

def wrapper(f=None): 

 

# Instead of being used as a decorator, can be called with 

# no arguments to just raise the warning. 

if f is None: 

warn() 

return 

 

sig = inspect.signature(f) 

 

@functools.wraps(f) 

def inner(*args, **kwargs): 

# If the named argument is truthy 

if sig.bind(*args, **kwargs).arguments.get(parameter_name): 

warn() 

return f(*args, **kwargs) 

 

return inner 

 

return wrapper 

 

 

# Deprecation for 'args' parameter; defined once to minimize boilerplate, 

# as this parameter is present all over Kwant. 

deprecate_args = deprecate_parameter('args', version=1.4, help= 

"Instead, provide named parameters as a dictionary via 'params'.") 

 

 

def interleave(seq): 

"""Return an iterator that yields pairs of elements from a sequence. 

 

If 'seq' has an odd number of elements then the last element is dropped. 

 

Examples 

-------- 

>>> list(interleave(range(4))) 

[(0, 1), (2, 3)] 

>>> list(interleave(range(5)) 

[(0, 1), (2, 3)] 

""" 

# zip, when given the same iterator twice, turns a sequence into a 

# sequence of pairs. 

iseq = iter(seq) 

return zip(iseq, iseq) 

 

 

def ensure_isinstance(obj, typ, msg=None): 

105 ↛ 107line 105 didn't jump to line 107, because the condition on line 105 was never false if isinstance(obj, typ): 

return 

if msg is None: 

msg = "Expecting an instance of {}.".format(typ.__name__) 

raise TypeError(msg) 

 

def ensure_rng(rng=None): 

"""Turn rng into a random number generator instance 

 

If rng is None, return the RandomState instance used by np.random. 

If rng is an integer, return a new RandomState instance seeded with rng. 

If rng is already a RandomState instance, return it. 

Otherwise raise ValueError. 

""" 

if rng is None: 

return np.random.mtrand._rand 

if isinstance(rng, numbers.Integral): 

return np.random.RandomState(rng) 

123 ↛ 126line 123 didn't jump to line 126, because the condition on line 123 was never false if all(hasattr(rng, attr) for attr in ('random_sample', 'randn', 

'randint', 'choice')): 

return rng 

raise ValueError("Expecting a seed or an object that offers the " 

"numpy.random.RandomState interface.") 

 

 

@contextmanager 

def reraise_warnings(level=3): 

with warnings.catch_warnings(record=True) as caught_warnings: 

yield 

for warning in caught_warnings: 

warnings.warn(warning.message, stacklevel=level) 

 

 

def get_parameters(func): 

"""Return the names of parameters of a function. 

 

It is made sure that the function can be called as func(*args) with 

'args' corresponding to the returned parameter names. 

 

Returns 

------- 

param_names : list 

Names of positional parameters that appear in the signature of 'func'. 

""" 

def error(msg): 

fname = inspect.getsourcefile(func) 

try: 

line = inspect.getsourcelines(func)[1] 

except OSError: 

line = '<unknown line>' 

raise ValueError("{}:\nFile {}, line {}, in {}".format( 

msg, repr(fname), line, func.__name__)) 

 

P = inspect.Parameter 

pars = inspect.signature(func).parameters # an *ordered mapping* 

names = [] 

for k, v in pars.items(): 

162 ↛ 168line 162 didn't jump to line 168, because the condition on line 162 was never false if v.kind in (P.POSITIONAL_ONLY, P.POSITIONAL_OR_KEYWORD): 

163 ↛ 166line 163 didn't jump to line 166, because the condition on line 163 was never false if v.default is P.empty: 

names.append(k) 

else: 

error("Arguments of value functions " 

"must not have default values") 

elif v.kind is P.KEYWORD_ONLY: 

error("Keyword-only arguments are not allowed in value functions") 

elif v.kind in (P.VAR_POSITIONAL, P.VAR_KEYWORD): 

error("Value functions must not take *args or **kwargs") 

return tuple(names) 

 

 

class lazy_import: 

def __init__(self, module, package='kwant', deprecation_warning=False): 

177 ↛ 178line 177 didn't jump to line 178, because the condition on line 177 was never true if module.startswith('.') and not package: 

raise ValueError('Cannot import a relative module without a package.') 

self.__module = module 

self.__package = package 

self.__deprecation_warning = deprecation_warning 

 

def __getattr__(self, name): 

184 ↛ 185line 184 didn't jump to line 185, because the condition on line 184 was never true if self.__deprecation_warning: 

msg = ("Accessing {0} without an explicit import is deprecated. " 

"Instead, explicitly 'import {0}'." 

).format('.'.join((self.__package, self.__module))) 

warnings.warn(msg, KwantDeprecationWarning, stacklevel=2) 

relative_module = '.' + self.__module 

mod = importlib.import_module(relative_module, self.__package) 

# Replace this _LazyModuleProxy with an actual module 

package = sys.modules[self.__package] 

setattr(package, self.__module, mod) 

return getattr(mod, name) 

 

 

def _hashable(obj): 

return isinstance(obj, collections.abc.Hashable) 

 

 

def memoize(f): 

"""Decorator to memoize a function that works even with unhashable args. 

 

This decorator will even work with functions whose args are not hashable. 

The cache key is made up by the hashable arguments and the ids of the 

non-hashable args. It is up to the user to make sure that non-hashable 

args do not change during the lifetime of the decorator. 

 

This decorator will keep reevaluating functions that return None. 

""" 

def lookup(*args): 

key = tuple(arg if _hashable(arg) else id(arg) for arg in args) 

result = cache.get(key) 

if result is None: 

cache[key] = result = f(*args) 

return result 

cache = {} 

return lookup