From 5f5900f4cb99017a6c87eece40edc5e814f287aa Mon Sep 17 00:00:00 2001 From: Uwe Date: Mon, 7 Aug 2017 18:05:27 +0200 Subject: [PATCH 01/48] adapted hope to run on python 3.5 and newer Python versions --- hope/_ast.py | 20 +++++++++++++++++++ hope/_tosource.py | 18 +++++++++++------- hope/_transformer.py | 7 +++++-- hope/_wrapper.py | 4 ++-- setup.py | 2 ++ test/.test_optimize.py.swp | Bin 0 -> 20480 bytes test/test_op_div.py | 20 +++++++++---------- test/test_op_pow.py | 8 ++++---- test/test_operators.py | 8 ++++---- test/test_optimize.py | 38 +++++++++++++++++++++++++++---------- test/test_return.py | 18 +++++++++++++++--- test/test_tosource.py | 29 +++++++++++++++++++++------- test/test_ufig.py | 13 +++++++++---- 13 files changed, 132 insertions(+), 53 deletions(-) create mode 100644 test/.test_optimize.py.swp diff --git a/hope/_ast.py b/hope/_ast.py index 5088f08..952dc27 100644 --- a/hope/_ast.py +++ b/hope/_ast.py @@ -7,6 +7,7 @@ from __future__ import print_function, division, absolute_import, unicode_literals +import ast import copy from hope._const import * @@ -390,3 +391,22 @@ def visit(self, node): def generic_visit(self, node): raise Exception("Not Implemented Token: {0}({1!s})".format(type(node).__name__, node)) + + +def call_has_kw_args(node): + if hasattr(node, "kwargs"): + return node.kwargs is not None + return any(kw.arg is None for kw in getattr(node, "keywords", [])) + + +def call_has_starargs(node): + if hasattr(node, "starargs"): + return node.starargs is not None + try: + # must be defined in Python 3 + ast.Starred + except AttributeError: + raise RuntimeError("should not happen") + return any(isinstance(arg, ast.Starred) for arg in node.args) + + diff --git a/hope/_tosource.py b/hope/_tosource.py index f644a4a..3d48cc4 100644 --- a/hope/_tosource.py +++ b/hope/_tosource.py @@ -232,11 +232,11 @@ def paren_or_comma(): paren_or_comma() self.write(keyword.arg + '=') self.visit(keyword.value) - if node.starargs is not None: + if getattr(node, "starargs", None) is not None: paren_or_comma() self.write('*') self.visit(node.starargs) - if node.kwargs is not None: + if getattr(node, "kwargs", None) is not None: paren_or_comma() self.write('**') self.visit(node.kwargs) @@ -436,13 +436,17 @@ def write_comma(): self.visit(arg) for keyword in node.keywords: write_comma() - self.write(keyword.arg + '=') - self.visit(keyword.value) - if node.starargs is not None: + if keyword.arg is not None: + self.write(keyword.arg + '=') + self.visit(keyword.value) + else: + self.write("**") + self.visit(keyword.value) + if getattr(node, "starargs", None) is not None: write_comma() self.write('*') self.visit(node.starargs) - if node.kwargs is not None: + if getattr(node, "kwargs", None) is not None: write_comma() self.write('**') self.visit(node.kwargs) @@ -628,4 +632,4 @@ def visit_arguments(self, node): self.signature(node) def visit_arg(self, node): - self.write(node.arg) \ No newline at end of file + self.write(node.arg) diff --git a/hope/_transformer.py b/hope/_transformer.py index cd5d746..593fddc 100644 --- a/hope/_transformer.py +++ b/hope/_transformer.py @@ -428,7 +428,7 @@ def visit_For(self, node): raise Exception("The variable '{0}' does already exists, since the scopeing is different in c++ and python, this is not supported".format(iter.name)) # TODO: implement this more generic if isinstance(node.iter, ast.Call) and isinstance(node.iter.func, ast.Name) and node.iter.func.id in ["range", "xrange"] \ - and len(node.iter.keywords) == 0 and node.iter.starargs is None and node.iter.kwargs is None: + and not call_has_kw_args(node.iter): if len(node.iter.args) == 1: args = [Number(0), self.visit(node.iter.args[0])] elif len(node.iter.args) == 2: @@ -575,7 +575,10 @@ def visit_Call(self, node): return self.variables[name] elif isinstance(node.func, ast.Attribute): - if not node.starargs is None or not node.kwargs is None: + + if call_has_starargs(node): + raise UnsupportedFeatureException("Only arguments without default values are supported in calls") + if call_has_kw_args(node): raise UnsupportedFeatureException("Only arguments without default values are supported in calls") return Call(self.visit(node.func), diff --git a/hope/_wrapper.py b/hope/_wrapper.py index bdb23dd..dfad428 100644 --- a/hope/_wrapper.py +++ b/hope/_wrapper.py @@ -271,8 +271,8 @@ def _compile(target, localfilename, fkt_name): try: #trying to encode utf-8 to support AstroPy warnings.warn("A warning has been issued during compilation:\n{0}".format(out).encode('utf-8')) - except UnicodeError: - #encoding fails on Linux + except (UnicodeError, TypeError): + #encoding fails on Linux and Python 3 warnings.warn("A warning has been issued during compilation:\n{0}".format(out)) if config.verbose: diff --git a/setup.py b/setup.py index b717c08..b845c38 100644 --- a/setup.py +++ b/setup.py @@ -66,6 +66,8 @@ def run_tests(self): "Programming Language :: Python :: 2.7", "Programming Language :: Python :: 3.3", "Programming Language :: Python :: 3.4", + "Programming Language :: Python :: 3.5", + "Programming Language :: Python :: 3.6", "Topic :: Scientific/Engineering", "Topic :: Scientific/Engineering :: Mathematics", "Topic :: Scientific/Engineering :: Physics", diff --git a/test/.test_optimize.py.swp b/test/.test_optimize.py.swp new file mode 100644 index 0000000000000000000000000000000000000000..532594789f30c1dadaa7a1056496f98a038543f5 GIT binary patch literal 20480 zcmeI2Uu+ab9LEP*5LB=c|2=3jwgm3lyKS$QP@7aSSlTLojIAbc)@8akcf0g%XWiMO z9{zpN2ZM>mgeMcx5cPrRi{OL72P10WL0>SDkBXapLKXe65Wo!z~bwo(eEP+&Is zbpOoG?9BImGyVN$X0YYq-VL!tQ-e&N?-eFjFHRsb~1vWq6i*4KrWV0J9a`(`DB`G}BUbeyxC3Kr5gX&g=WYREIF@BFyV8{+f43Fq4Q{>J#cenNUpeBTtG*@X1?R-d#2S^=$qRzNGD z70?Q31+)TM0j+>mKr5gXxCjcc93i_9y#J&Ee60Uhk zuEss+0nMNe{I!^nFToM;Ja`23f?L5qiwOB1yasLt^TDyJ2ssK40~>UMbzmh}3Pu*9 zJg^VE2VMYq&<9q4h2Yo%Lf!!@z)fHw_!HVG{cO+;lAsR!23@}b?}PoI0PY4Uuo(P; zNtZ9cPEZR*FzNC&_yD{CqJ!dKQgT0_md&l9ge6kAN)nZj9|{so-HT+3Qail0$C8Vs9UvpMeSQL2bpv%lleZYjIV14S>sjIYetFrLx#tER^ow=Z7zxWP`Omtrur0h z0{qTG6s6($B3HJ8s2~})T4pmH1IZ)_t8$vwtiv9hs5$3sHPf=TNyY~8U=q-diKo<0%x$uVcg6ua5`)|mQ8bxWW}Nwmel11 zys({9{|qlsa~lol&8KOQ>H*hSDe!mc9chV_phYfm+xxoH0>+JeC-rO;_M%7KFvhfC^|Jb?fw@>x{;f4StC!D!87&ZR+(nL9F%=-n$;G0J`}-I*9zc~qRP&8NBO203J0YqmIH1^6-m*u z>sTVkO-RChIPs*uzMk}>^|YEja(K6@DsQDxGmY>q(m>ZDX!D(d9ni$uByDZUw9&5q z9=fIM!;4PQURMTAfX{_K^F0!dw<}5&qT>G(h?fr_o>uXHQyg`EhZz4e@D_LzJPEdd zZm=BGf?pBm?*+@j4WJhMf%yIha1iVTFMr}V z<-#}tGr=4l?ld3zocdgri-F@7uy7JkN8$nIkSk$fJVz6Vp_7vfRFlcF?5RKmFOB-# zb7Hc6@@$HV1kcNKcDSB%`jwtmW5O~`yaF^S$LQ+Lq#Q)mbs7shvp#Lz*qCk&D>GFH z9j)S=_2hImTm;EGp3O%qJovpD^SdQa@cO@GmDg=j)zX50Ssp4iK7 JPi(X&@ISeVmq-8r literal 0 HcmV?d00001 diff --git a/test/test_op_div.py b/test/test_op_div.py index 318dc7f..32dd2a2 100644 --- a/test/test_op_div.py +++ b/test/test_op_div.py @@ -16,8 +16,8 @@ def fkt(a, b, c): c[:] = a / b hfkt = hope.jit(fkt) (ao, ah), (bo, bh), (co, ch) = random(dtype, shape), random(dtype, shape), random(dtype, shape) - if np.count_nonzero(bo == 0) > 0: bo[bo == 0] += 1 - if np.count_nonzero(bh == 0) > 0: bh[bh == 0] += 1 + bo[bo == 0] += 1 + bh[bh == 0] += 1 ro, rh = fkt(ao, bo, co), hfkt(ah, bh, ch) if dtype in [np.float32, np.float64, float]: co[co < 1. / (np.finfo(dtype).max * np.finfo(dtype).resolution)] /= np.finfo(dtype).resolution @@ -30,8 +30,8 @@ def fkt(a, b, c): c[:] = a // b hfkt = hope.jit(fkt) (ao, ah), (bo, bh), (co, ch) = random(dtype, shape), random(dtype, shape), random(dtype, shape) - if np.count_nonzero(bo == 0) > 0: bo[bo == 0] += 1 - if np.count_nonzero(bh == 0) > 0: bh[bh == 0] += 1 + bo[bo == 0] += 1 + bh[bh == 0] += 1 ro, rh = fkt(ao, bo, co), hfkt(ah, bh, ch) if dtype in [np.float32, np.float64, float]: co[co < 1. / (np.finfo(dtype).max * np.finfo(dtype).resolution)] /= np.finfo(dtype).resolution @@ -50,8 +50,8 @@ def fkt(a, b, c): ao, ah, bo, bh = ao.astype(np.float64), ah.astype(np.float64), bo.astype(np.float64), bh.astype(np.float64) ao, ah = np.copysign(np.power(np.abs(ao), 1. / 4.), ao).astype(dtypea), np.copysign(np.power(np.abs(ah), 1. / 4.), ah).astype(dtypea) bo, bh = np.copysign(np.power(np.abs(bo), 1. / 4.), bo).astype(dtypeb), np.copysign(np.power(np.abs(bh), 1. / 4.), bh).astype(dtypeb) - if np.count_nonzero(bo == 0) > 0: bo[bo == 0] += 1 - if np.count_nonzero(bh == 0) > 0: bh[bh == 0] += 1 + bo[bo == 0] += 1 + bh[bh == 0] += 1 fkt(ao, bo, co), hfkt(ah, bh, ch) assert check(co, ch) fkt(ao, bo, co), hfkt(ah, bh, ch) @@ -66,8 +66,8 @@ def fkt(a, c): (ao, ah) = random(dtype, shape) (co, ch) = random(np.float64, shape) - if np.count_nonzero(ao == 0) > 0: ao[ao == 0] += 1 - if np.count_nonzero(ah == 0) > 0: ah[ah == 0] += 1 + ao[ao == 0] += 1 + ah[ah == 0] += 1 ro, rh = fkt(ao, co), hfkt(ah, ch) if dtype in [np.float32, np.float64, float]: @@ -81,8 +81,8 @@ def fkt(a, c): c[:] //= a hfkt = hope.jit(fkt) (ao, ah), (co, ch) = random(dtype, shape), random(dtype, shape) - if np.count_nonzero(ao == 0) > 0: ao[ao == 0] += 1 - if np.count_nonzero(ah == 0) > 0: ah[ah == 0] += 1 + ao[ao == 0] += 1 + ah[ah == 0] += 1 ro, rh = fkt(ao, co), hfkt(ah, ch) if dtype in [np.float32, np.float64, float]: co[co < 1. / (np.finfo(dtype).max * np.finfo(dtype).resolution)] /= np.finfo(dtype).resolution diff --git a/test/test_op_pow.py b/test/test_op_pow.py index 434e3df..7d590b5 100644 --- a/test/test_op_pow.py +++ b/test/test_op_pow.py @@ -16,10 +16,10 @@ def fkt(a, c): c[:] **= a hfkt = hope.jit(fkt) (ao, ah), (co, ch) = random(np.uint8, shape), random(dtype, shape) - if np.count_nonzero(ao == 0) > 0: ao[ao == 0] += 1 - if np.count_nonzero(ah == 0) > 0: ah[ah == 0] += 1 - if np.count_nonzero(co == 0) > 0: co[co == 0] += 1 - if np.count_nonzero(ch == 0) > 0: ch[ch == 0] += 1 + ao[ao == 0] += 1 + ah[ah == 0] += 1 + co[co == 0] += 1 + ch[ch == 0] += 1 co, ch = np.copysign(np.sqrt(np.abs(co)), co).astype(dtype), np.copysign(np.sqrt(np.abs(ch)), ch).astype(dtype) ao, ah = np.power(np.abs(ao).astype(np.float64), 1. / co.astype(np.float64)).astype(dtype), np.power(np.abs(ah).astype(np.float64), 1. / ch.astype(np.float64)).astype(dtype) fkt(ao, co), hfkt(ah, ch) diff --git a/test/test_operators.py b/test/test_operators.py index 5e78fd6..5ffac80 100644 --- a/test/test_operators.py +++ b/test/test_operators.py @@ -18,8 +18,8 @@ def fkt(a, b, c): c[:] = a % b hfkt = hope.jit(fkt) (ao, ah), (bo, bh), (co, ch) = random(dtype, shape), random(dtype, shape), random(dtype, shape) - if np.count_nonzero(bo == 0) > 0: bo[bo == 0] += 1 - if np.count_nonzero(bh == 0) > 0: bh[bh == 0] += 1 + bo[bo == 0] += 1 + bh[bh == 0] += 1 fkt(ao, bo, co), hfkt(ah, bh, ch) assert check(co, ch) fkt(ao, bo, co), hfkt(ah, bh, ch) @@ -55,8 +55,8 @@ def fkt(a, c): c[:] %= a hfkt = hope.jit(fkt) (ao, ah), (co, ch) = random(dtype, shape), random(dtype, shape) - if np.count_nonzero(ao == 0) > 0: ao[ao == 0] += 1 - if np.count_nonzero(ah == 0) > 0: ah[ah == 0] += 1 + ao[ao == 0] += 1 + ah[ah == 0] += 1 fkt(ao, co), hfkt(ah, ch) assert check(co, ch) fkt(ao, co), hfkt(ah, ch) diff --git a/test/test_optimize.py b/test/test_optimize.py index 83d25d1..db62cf9 100644 --- a/test/test_optimize.py +++ b/test/test_optimize.py @@ -10,6 +10,10 @@ from test.utilities import random, check, make_test, JENKINS, min_dtypes, dtypes, shapes, setup_module, setup_method, teardown_module + +np_version = tuple(map(int, np.__version__.split("."))) + + @pytest.mark.parametrize("dtype,shape", itertools.product(dtypes, shapes[1:])) def test_opt_pow_array(dtype, shape): def fkt(a, c): @@ -46,8 +50,13 @@ def fkt(a, c): hfkt = hope.jit(fkt) (ao, ah), (co, ch) = random(dtype, shape), random(dtype, shape) ao, ah = np.copysign(np.power(np.abs(ao), 1. / 8.), ao).astype(dtype), np.copysign(np.power(np.abs(ah), 1. / 8.), ah).astype(dtype) - if np.count_nonzero(ao == 0) > 0: ao[ao == 0] += 1 - if np.count_nonzero(ah == 0) > 0: ah[ah == 0] += 1 + ao[ao == 0] += 1 + ah[ah == 0] += 1 + + if np_version >= (1, 12, 0): + # numpy 1.12 introduce incompatible change which disallows negative exponents for integers + ao = ao.astype(np.float) + fkt(ao, co), hfkt(ah, ch) assert check(co, ch) fkt(ao, co), hfkt(ah, ch) @@ -62,8 +71,13 @@ def fkt(a, c): hfkt = hope.jit(fkt) (ao, ah), (co, ch) = random(dtype, []), random(dtype, [1]) ao, ah = np.copysign(np.power(np.abs(ao), 1. / 8.), ao).astype(dtype), np.copysign(np.power(np.abs(ah), 1. / 8.), ah).astype(dtype) - if np.count_nonzero(ao == 0) > 0: ao[ao == 0] += 1 - if np.count_nonzero(ah == 0) > 0: ah[ah == 0] += 1 + if ao == 0: + ao += 1 + if ah == 0: + ah += 1 + if np_version >= (1, 12, 0): + # numpy 1.12 introduce incompatible change which disallows negative exponents for integers + ao = ao.astype(np.float) fkt(ao, co), hfkt(ah, ch) assert check(co, ch) fkt(ao, co), hfkt(ah, ch) @@ -78,8 +92,8 @@ def fkt(a, c): hfkt = hope.jit(fkt) (ao, ah), (co, ch) = random(dtype, shape), random(dtype, shape) ao, ah = np.copysign(np.power(np.abs(ao), 1. / 4.), ao).astype(dtype), np.copysign(np.power(np.abs(ah), 1. / 4.), ah).astype(dtype) - if np.count_nonzero(ao == 0) > 0: ao[ao == 0] += 1 - if np.count_nonzero(ah == 0) > 0: ah[ah == 0] += 1 + ao[ao == 0] += 1 + ah[ah == 0] += 1 fkt(ao, co), hfkt(ah, ch) assert check(co, ch) fkt(ao, co), hfkt(ah, ch) @@ -94,8 +108,10 @@ def fkt(a, c): hfkt = hope.jit(fkt) (ao, ah), (co, ch) = random(dtype, []), random(dtype, [1]) ao, ah = np.copysign(np.power(np.abs(ao), 1. / 4.), ao).astype(dtype), np.copysign(np.power(np.abs(ah), 1. / 4.), ah).astype(dtype) - if np.count_nonzero(ao == 0) > 0: ao[ao == 0] += 1 - if np.count_nonzero(ah == 0) > 0: ah[ah == 0] += 1 + if ao == 0: + ao += 1 + if ah == 0: + ah += 1 fkt(ao, co), hfkt(ah, ch) assert check(co, ch) fkt(ao, co), hfkt(ah, ch) @@ -110,8 +126,10 @@ def fkt(a, c): hfkt = hope.jit(fkt) (ao, ah), (co, ch) = random(dtype, []), random(dtype, [1]) ao, ah = 1. / np.power(np.abs(ao), 1. / 2.).astype(dtype), 1. / np.power(np.abs(ah), 1. / 2.).astype(dtype) - if np.count_nonzero(ao == 0) > 0: ao[ao == 0] += 1 - if np.count_nonzero(ah == 0) > 0: ah[ah == 0] += 1 + if ao == 0: + ao += 1 + if ah == 0: + ah += 1 fkt(ao, co), hfkt(ah, ch) assert check(co, ch) fkt(ao, co), hfkt(ah, ch) diff --git a/test/test_return.py b/test/test_return.py index 1e7277e..69626ee 100644 --- a/test/test_return.py +++ b/test/test_return.py @@ -10,11 +10,16 @@ from test.utilities import random, check, make_test, JENKINS, min_dtypes, dtypes, shapes, setup_module, setup_method, teardown_module + +np_version = tuple(map(int, np.__version__.split("."))) + + @pytest.mark.parametrize("dtype,shape", itertools.product(dtypes, shapes)) @make_test -def test_return_arr(a, b, c): +def test_return_arr(a, b, c): return a + @pytest.mark.parametrize("dtype,shape", itertools.product(dtypes, shapes)) def test_return_arr_expr(dtype, shape): def fkt(a, c): @@ -22,8 +27,15 @@ def fkt(a, c): hfkt = hope.jit(fkt) (ao, ah), (co, ch) = random(dtype, shape), random(dtype, shape) ao, ah = np.copysign(np.power(np.abs(ao), 1. / 8.), ao).astype(dtype), np.copysign(np.power(np.abs(ah), 1. / 8.), ah).astype(dtype) - if np.count_nonzero(ao == 0) > 0: ao[ao == 0] += 1 - if np.count_nonzero(ah == 0) > 0: ah[ah == 0] += 1 + if shape: + ao[ao == 0] += 1 + ah[ah == 0] += 1 + else: + ao = 1 if ao == 0 else ao + ah = 1 if ah == 0 else ah + if np_version >= (1, 12, 0): + # numpy 1.12 introduce incompatible change which disallows negative exponents for integers + ao = ao.astype(np.float) fkt(ao, co), hfkt(ah, ch) assert check(co, ch) fkt(ao, co), hfkt(ah, ch) diff --git a/test/test_tosource.py b/test/test_tosource.py index 93a4906..4df06b8 100644 --- a/test/test_tosource.py +++ b/test/test_tosource.py @@ -14,20 +14,29 @@ from hope import _tosource from hope import _transformer + def dummy(par, *args, **kwargs): for _ in range(1): continue assert True, "msg" + +def dummy2(): + """used to test for differences between ast of python 3.4 and previous versions""" + kws = dict(c=7) + args = (1, 2) + return dummy(1, *args, **kws) + dummy(2, **{'c': 7}) + + @pytest.mark.infrastructure class TestToSource(object): - + def test_tosource_mod(self): self.exec_test_on_mod(hope._tosource) - + def test_wrapper_mod(self): self.exec_test_on_mod(hope._wrapper) - + def test_simple(self): mod_ast = _transformer.get_fkt_ast(dummy) mod_source = _tosource.tosource(mod_ast) @@ -35,12 +44,18 @@ def test_simple(self): # f.write(mod_source) mod_ast2 = ast.parse(mod_source) assert compare_ast(mod_ast, mod_ast2.body[0]) - + + def test_simple_2(self): + mod_ast = _transformer.get_fkt_ast(dummy2) + mod_source = _tosource.tosource(mod_ast) + mod_ast2 = ast.parse(mod_source) + assert compare_ast(mod_ast, mod_ast2.body[0]) + def exec_test_on_mod(self, mod): modpath = mod.__file__ if modpath.endswith('.pyc'): modpath = modpath[:-1] - + with open(modpath, "r") as f: source = f.read() mod_ast = ast.parse(source) @@ -48,7 +63,7 @@ def exec_test_on_mod(self, mod): mod_source = mod_source.replace("'\\n'", "@@@@") mod_source = mod_source.replace("\\n", "\n") mod_source = mod_source.replace("@@@@", "'\\n'") - + # with open("dummy.py", "w") as f: # f.write(mod_source) mod_ast2 = ast.parse(mod_source) @@ -68,4 +83,4 @@ def compare_ast(node1, node2): elif isinstance(node1, list): return all(itertools.starmap(compare_ast, zip(node1, node2))) else: - return node1 == node2 \ No newline at end of file + return node1 == node2 diff --git a/test/test_ufig.py b/test/test_ufig.py index ce00a7a..6f66471 100644 --- a/test/test_ufig.py +++ b/test/test_ufig.py @@ -10,6 +10,9 @@ from test.utilities import random, check, make_test, JENKINS, min_dtypes, dtypes, shapes, setup_module, setup_method, teardown_module + +np_version = tuple(map(int, np.__version__.split("."))) + #hope.config.keeptemp = True def test_ufig_sin_cos(): @@ -92,11 +95,13 @@ def test_ufig_bincount(): def fkt_bincount(buffer, x, y, size, size_x, size_y): for idx in range(size): - if x[idx]>= 0 and x[idx] <= size_x and y[idx] >= 0 and y[idx] <= size_y: - buffer[x[idx], y[idx]] += 1. + if x[idx] >= 0 and x[idx] <= size_x and y[idx] >= 0 and y[idx] <= size_y: + x_bin = np.uint64(x[idx]) + y_bin = np.uint64(y[idx]) + buffer[x_bin, y_bin] += 1. hope.config.optimize = True hbincount = hope.jit(fkt_bincount) - + x = np.random.uniform(-1, 6, size=(1000,)) y = np.random.uniform(-1, 6, size=x.shape) buffer, hbuffer = np.zeros((5, 5)), np.zeros((5, 5)) @@ -156,4 +161,4 @@ def fkt_pdf(density, dims, center, w2D, r50, b, a): if np.all(hdensity == 1): print("asdf") else: - print("else") \ No newline at end of file + print("else") From 844b2e0b2e11abc96069559e10a4060ce0876747 Mon Sep 17 00:00:00 2001 From: Uwe Date: Fri, 11 Aug 2017 11:08:48 +0200 Subject: [PATCH 02/48] first refac attempt for using gitlab-ci instead of jenkins --- .gitlab-ci.yml | 30 ++++++++++++ Makefile | 4 +- requirements.txt | 1 - {test => tests}/__init__.py | 0 {test => tests}/test_Parser.py | 0 {test => tests}/test_blocks.py | 2 +- {test => tests}/test_call.py | 4 +- {test => tests}/test_cast.py | 2 +- {test => tests}/test_comparison.py | 2 +- {test => tests}/test_control_structures.py | 2 +- {test => tests}/test_dump.py | 2 +- {test => tests}/test_functions.py | 2 +- {test => tests}/test_jit.py | 0 {test => tests}/test_object.py | 4 +- {test => tests}/test_op_binary.py | 2 +- {test => tests}/test_op_bool.py | 2 +- {test => tests}/test_op_div.py | 2 +- {test => tests}/test_op_minus.py | 2 +- {test => tests}/test_op_mult.py | 2 +- {test => tests}/test_op_plus.py | 2 +- {test => tests}/test_op_pow.py | 2 +- {test => tests}/test_operators.py | 2 +- {test => tests}/test_optimize.py | 2 +- {test => tests}/test_options.py | 0 {test => tests}/test_pycosmo.py | 2 +- {test => tests}/test_rangecheck.py | 2 +- {test => tests}/test_return.py | 2 +- {test => tests}/test_serialization.py | 2 +- {test => tests}/test_slice.py | 4 +- {test => tests}/test_slice_negative.py | 2 +- {test => tests}/test_tosource.py | 2 +- {test => tests}/test_ufig.py | 4 +- {test => tests}/test_wrapper.py | 0 {test => tests}/utilities.py | 0 tox.ini | 55 ++++++++++------------ 35 files changed, 86 insertions(+), 62 deletions(-) create mode 100644 .gitlab-ci.yml rename {test => tests}/__init__.py (100%) rename {test => tests}/test_Parser.py (100%) rename {test => tests}/test_blocks.py (88%) rename {test => tests}/test_call.py (95%) rename {test => tests}/test_cast.py (96%) rename {test => tests}/test_comparison.py (91%) rename {test => tests}/test_control_structures.py (96%) rename {test => tests}/test_dump.py (99%) rename {test => tests}/test_functions.py (99%) rename {test => tests}/test_jit.py (100%) rename {test => tests}/test_object.py (96%) rename {test => tests}/test_op_binary.py (91%) rename {test => tests}/test_op_bool.py (79%) rename {test => tests}/test_op_div.py (96%) rename {test => tests}/test_op_minus.py (91%) rename {test => tests}/test_op_mult.py (92%) rename {test => tests}/test_op_plus.py (96%) rename {test => tests}/test_op_pow.py (95%) rename {test => tests}/test_operators.py (96%) rename {test => tests}/test_optimize.py (97%) rename {test => tests}/test_options.py (100%) rename {test => tests}/test_pycosmo.py (99%) rename {test => tests}/test_rangecheck.py (83%) rename {test => tests}/test_return.py (95%) rename {test => tests}/test_serialization.py (97%) rename {test => tests}/test_slice.py (96%) rename {test => tests}/test_slice_negative.py (95%) rename {test => tests}/test_tosource.py (98%) rename {test => tests}/test_ufig.py (97%) rename {test => tests}/test_wrapper.py (100%) rename {test => tests}/utilities.py (100%) diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml new file mode 100644 index 0000000..f5c3a03 --- /dev/null +++ b/.gitlab-ci.yml @@ -0,0 +1,30 @@ +tests: + tags: + - python + script: + - pyenv versions + - pyenv local 2.7 3.4 3.5 3.6 + - tox -r -vv -i https://cosmo-pypi.phys.ethz.ch/simple + +style: + tags: + - python + script: + - pyenv local 2.7 3.4 3.5 3.6 + - tox -e style -i https://cosmo-pypi.phys.ethz.ch/simple + allow_failure: true + +docs: + tags: + - python + script: + - pyenv local 2.7 3.4 3.5 3.6 + - tox -e docs -i https://cosmo-pypi.phys.ethz.ch/simple + +publish_docs: + tags: + - python + script: + - pyenv local 2.7 3.4 3.5 3.6 + - tox -e publish_docs -i https://cosmo-pypi.phys.ethz.ch/simple + when: manual diff --git a/Makefile b/Makefile index cba4c2d..c577965 100644 --- a/Makefile +++ b/Makefile @@ -28,7 +28,7 @@ lint: flake8 hope test test: - py.test -v -s + py.test -v -s tests test-all: tox @@ -48,4 +48,4 @@ docs: sdist: clean #pip freeze > requirements.rst python setup.py sdist - ls -l dist \ No newline at end of file + ls -l dist diff --git a/requirements.txt b/requirements.txt index e0dc245..60deee0 100644 --- a/requirements.txt +++ b/requirements.txt @@ -6,4 +6,3 @@ numpy>=1.9.2 pytest>=2.6.3 pytest-cov>=1.8.0 sympy>=0.7.5 -tox==1.8.0 diff --git a/test/__init__.py b/tests/__init__.py similarity index 100% rename from test/__init__.py rename to tests/__init__.py diff --git a/test/test_Parser.py b/tests/test_Parser.py similarity index 100% rename from test/test_Parser.py rename to tests/test_Parser.py diff --git a/test/test_blocks.py b/tests/test_blocks.py similarity index 88% rename from test/test_blocks.py rename to tests/test_blocks.py index a57d280..972acbf 100644 --- a/test/test_blocks.py +++ b/tests/test_blocks.py @@ -9,7 +9,7 @@ import itertools import pytest -from test.utilities import random, check, make_test, JENKINS, min_dtypes, dtypes, shapes, setup_module, setup_method, teardown_module +from .utilities import random, check, make_test, JENKINS, min_dtypes, dtypes, shapes, setup_module, setup_method, teardown_module @pytest.mark.parametrize("dtype", dtypes) def test_blocks_1(dtype): diff --git a/test/test_call.py b/tests/test_call.py similarity index 95% rename from test/test_call.py rename to tests/test_call.py index 5f859a5..ab9a25f 100644 --- a/test/test_call.py +++ b/tests/test_call.py @@ -9,7 +9,7 @@ import itertools import pytest -from test.utilities import random, check, make_test, JENKINS, min_dtypes, dtypes, shapes, setup_module, setup_method, teardown_module +from .utilities import random, check, make_test, JENKINS, min_dtypes, dtypes, shapes, setup_module, setup_method, teardown_module def fkt_call_local_fun_callback(a, b): a[:] = b @@ -117,4 +117,4 @@ def test_recursion(): hope_fkt_recursion_callback = hope.jit(fkt_recursion_callback) assert hope_fkt_recursion_callback(20) == 6765 -# TODO: make tests for function calls with return types and expr / local vars as arguments \ No newline at end of file +# TODO: make tests for function calls with return types and expr / local vars as arguments diff --git a/test/test_cast.py b/tests/test_cast.py similarity index 96% rename from test/test_cast.py rename to tests/test_cast.py index c070b06..cd4955e 100644 --- a/test/test_cast.py +++ b/tests/test_cast.py @@ -8,7 +8,7 @@ import numpy as np import hope, itertools, pytest, sys, sysconfig, os, shutil -from test.utilities import random, check, make_test, JENKINS, min_dtypes, dtypes, shapes, setup_module, setup_method, teardown_module +from .utilities import random, check, make_test, JENKINS, min_dtypes, dtypes, shapes, setup_module, setup_method, teardown_module @pytest.mark.parametrize("dtype", [dtype for dtype in dtypes if dtype != float]) def test_func_bool_(dtype): diff --git a/test/test_comparison.py b/tests/test_comparison.py similarity index 91% rename from test/test_comparison.py rename to tests/test_comparison.py index 458366e..00e92b3 100644 --- a/test/test_comparison.py +++ b/tests/test_comparison.py @@ -8,7 +8,7 @@ import numpy as np import hope, itertools, pytest, sys, sysconfig, os, shutil -from test.utilities import random, check, make_test, JENKINS, min_dtypes, dtypes, shapes, setup_module, setup_method, teardown_module +from .utilities import random, check, make_test, JENKINS, min_dtypes, dtypes, shapes, setup_module, setup_method, teardown_module @pytest.mark.parametrize("dtype,shape", itertools.product(dtypes, shapes[1:])) @make_test diff --git a/test/test_control_structures.py b/tests/test_control_structures.py similarity index 96% rename from test/test_control_structures.py rename to tests/test_control_structures.py index 3fda07a..2c469d8 100644 --- a/test/test_control_structures.py +++ b/tests/test_control_structures.py @@ -8,7 +8,7 @@ import numpy as np import hope, itertools, pytest, sys, sysconfig, os, shutil -from test.utilities import random, check, make_test, JENKINS, min_dtypes, dtypes, shapes, setup_module, setup_method, teardown_module +from .utilities import random, check, make_test, JENKINS, min_dtypes, dtypes, shapes, setup_module, setup_method, teardown_module def test_ifelse_scalar(): def fkt(a, b, c): diff --git a/test/test_dump.py b/tests/test_dump.py similarity index 99% rename from test/test_dump.py rename to tests/test_dump.py index 762322f..5a8d485 100644 --- a/test/test_dump.py +++ b/tests/test_dump.py @@ -55,4 +55,4 @@ def test_dump_complex(self): assert fkt_str is not None - \ No newline at end of file + diff --git a/test/test_functions.py b/tests/test_functions.py similarity index 99% rename from test/test_functions.py rename to tests/test_functions.py index 2865ab7..a834827 100644 --- a/test/test_functions.py +++ b/tests/test_functions.py @@ -10,7 +10,7 @@ import pytest import copy -from test.utilities import random, check, make_test, dtypes, shapes +from .utilities import random, check, make_test, dtypes, shapes import hope from hope.exceptions import UnsupportedFeatureException diff --git a/test/test_jit.py b/tests/test_jit.py similarity index 100% rename from test/test_jit.py rename to tests/test_jit.py diff --git a/test/test_object.py b/tests/test_object.py similarity index 96% rename from test/test_object.py rename to tests/test_object.py index 0bf992e..e33f848 100644 --- a/test/test_object.py +++ b/tests/test_object.py @@ -8,7 +8,7 @@ import hope import numpy as np -from test.utilities import check,setup_module,setup_method,teardown_module +from .utilities import check,setup_module,setup_method,teardown_module class SubCls(object): @@ -148,4 +148,4 @@ def fkt(self): t1 = Test() a = t1.fkt() ah = t1.hfkt() - assert np.all(a==ah) \ No newline at end of file + assert np.all(a==ah) diff --git a/test/test_op_binary.py b/tests/test_op_binary.py similarity index 91% rename from test/test_op_binary.py rename to tests/test_op_binary.py index f88eb88..c247ec9 100644 --- a/test/test_op_binary.py +++ b/tests/test_op_binary.py @@ -8,7 +8,7 @@ import numpy as np import hope, itertools, pytest, sys, sysconfig, os, shutil -from test.utilities import random, check, make_test, JENKINS, min_dtypes, dtypes, shapes, setup_module, setup_method, teardown_module +from .utilities import random, check, make_test, JENKINS, min_dtypes, dtypes, shapes, setup_module, setup_method, teardown_module @pytest.mark.parametrize("dtype,shape", itertools.product([dtype for dtype in dtypes if issubclass(dtype, np.integer) or dtype == int], shapes)) @make_test diff --git a/test/test_op_bool.py b/tests/test_op_bool.py similarity index 79% rename from test/test_op_bool.py rename to tests/test_op_bool.py index a2723f2..e5288f4 100644 --- a/test/test_op_bool.py +++ b/tests/test_op_bool.py @@ -8,7 +8,7 @@ import numpy as np import hope, itertools, pytest, sys, sysconfig, os, shutil -from test.utilities import random, check, make_test, JENKINS, min_dtypes, dtypes, shapes, setup_module, setup_method, teardown_module +from .utilities import random, check, make_test, JENKINS, min_dtypes, dtypes, shapes, setup_module, setup_method, teardown_module @pytest.mark.parametrize("dtype,shape", [[np.int8, []]]) @make_test diff --git a/test/test_op_div.py b/tests/test_op_div.py similarity index 96% rename from test/test_op_div.py rename to tests/test_op_div.py index 318dc7f..095ed70 100644 --- a/test/test_op_div.py +++ b/tests/test_op_div.py @@ -8,7 +8,7 @@ import numpy as np import hope, itertools, pytest, sys, sysconfig, os, shutil -from test.utilities import random, check, make_test, JENKINS, min_dtypes, dtypes, shapes, setup_module, setup_method, teardown_module +from .utilities import random, check, make_test, JENKINS, min_dtypes, dtypes, shapes, setup_module, setup_method, teardown_module @pytest.mark.parametrize("dtype,shape", itertools.product(dtypes, shapes[1:])) def test_binary_div(dtype, shape): diff --git a/test/test_op_minus.py b/tests/test_op_minus.py similarity index 91% rename from test/test_op_minus.py rename to tests/test_op_minus.py index aa11e84..504b0dd 100644 --- a/test/test_op_minus.py +++ b/tests/test_op_minus.py @@ -8,7 +8,7 @@ import numpy as np import hope, itertools, pytest, sys, sysconfig, os, shutil -from test.utilities import random, check, make_test, JENKINS, min_dtypes, dtypes, shapes, setup_module, setup_method, teardown_module +from .utilities import random, check, make_test, JENKINS, min_dtypes, dtypes, shapes, setup_module, setup_method, teardown_module @pytest.mark.parametrize("dtype,shape", itertools.product(dtypes, shapes[1:])) @make_test diff --git a/test/test_op_mult.py b/tests/test_op_mult.py similarity index 92% rename from test/test_op_mult.py rename to tests/test_op_mult.py index 668a6a6..2476ff2 100644 --- a/test/test_op_mult.py +++ b/tests/test_op_mult.py @@ -8,7 +8,7 @@ import numpy as np import hope, itertools, pytest, sys, sysconfig, os, shutil -from test.utilities import random, check, make_test, JENKINS, min_dtypes, dtypes, shapes, setup_module, setup_method, teardown_module +from .utilities import random, check, make_test, JENKINS, min_dtypes, dtypes, shapes, setup_module, setup_method, teardown_module @pytest.mark.parametrize("dtype,shape", itertools.product(dtypes, shapes[1:])) def test_binary_mult(dtype, shape): diff --git a/test/test_op_plus.py b/tests/test_op_plus.py similarity index 96% rename from test/test_op_plus.py rename to tests/test_op_plus.py index 5a0eabb..63dd0d2 100644 --- a/test/test_op_plus.py +++ b/tests/test_op_plus.py @@ -8,7 +8,7 @@ import numpy as np import hope, itertools, pytest, sys, sysconfig, os, shutil -from test.utilities import random, check, make_test, JENKINS, min_dtypes, dtypes, shapes, setup_module, setup_method, teardown_module +from .utilities import random, check, make_test, JENKINS, min_dtypes, dtypes, shapes, setup_module, setup_method, teardown_module @pytest.mark.parametrize("dtype,shape", itertools.product(dtypes, shapes[1:])) @make_test diff --git a/test/test_op_pow.py b/tests/test_op_pow.py similarity index 95% rename from test/test_op_pow.py rename to tests/test_op_pow.py index 434e3df..6bcdef8 100644 --- a/test/test_op_pow.py +++ b/tests/test_op_pow.py @@ -8,7 +8,7 @@ import numpy as np import hope, itertools, pytest, sys, sysconfig, os, shutil -from test.utilities import random, check, make_test, JENKINS, min_dtypes, dtypes, shapes, setup_module, setup_method, teardown_module +from .utilities import random, check, make_test, JENKINS, min_dtypes, dtypes, shapes, setup_module, setup_method, teardown_module @pytest.mark.parametrize("dtype,shape", itertools.product(dtypes, shapes[1:])) def test_augmented_pow(dtype, shape): diff --git a/test/test_operators.py b/tests/test_operators.py similarity index 96% rename from test/test_operators.py rename to tests/test_operators.py index 5e78fd6..c9cd751 100644 --- a/test/test_operators.py +++ b/tests/test_operators.py @@ -8,7 +8,7 @@ import numpy as np import hope, itertools, pytest, sys, sysconfig, os, shutil -from test.utilities import random, check, make_test, JENKINS, min_dtypes, dtypes, shapes, setup_module, setup_method, teardown_module +from .utilities import random, check, make_test, JENKINS, min_dtypes, dtypes, shapes, setup_module, setup_method, teardown_module @pytest.mark.parametrize("dtype,shape", itertools.product([dtype for dtype in dtypes if issubclass(dtype, np.integer) or dtype == int], shapes[1:])) def test_binary_mod(dtype, shape): diff --git a/test/test_optimize.py b/tests/test_optimize.py similarity index 97% rename from test/test_optimize.py rename to tests/test_optimize.py index 83d25d1..16ff8fb 100644 --- a/test/test_optimize.py +++ b/tests/test_optimize.py @@ -8,7 +8,7 @@ import numpy as np import hope, itertools, pytest, sys, sysconfig, os, shutil -from test.utilities import random, check, make_test, JENKINS, min_dtypes, dtypes, shapes, setup_module, setup_method, teardown_module +from .utilities import random, check, make_test, JENKINS, min_dtypes, dtypes, shapes, setup_module, setup_method, teardown_module @pytest.mark.parametrize("dtype,shape", itertools.product(dtypes, shapes[1:])) def test_opt_pow_array(dtype, shape): diff --git a/test/test_options.py b/tests/test_options.py similarity index 100% rename from test/test_options.py rename to tests/test_options.py diff --git a/test/test_pycosmo.py b/tests/test_pycosmo.py similarity index 99% rename from test/test_pycosmo.py rename to tests/test_pycosmo.py index 7cf8928..7830aa9 100644 --- a/test/test_pycosmo.py +++ b/tests/test_pycosmo.py @@ -8,7 +8,7 @@ import numpy as np import hope, sysconfig, sys, os -from test.utilities import random, check, make_test, setup_module, setup_method, teardown_module +from .utilities import random, check, make_test, setup_module, setup_method, teardown_module def test_pycosmo_1(): def fkt_pycosmo(y, lna, k, eta, hubble_a, tdot, omega_gam, omega_neu, omega_dm_0, omega_b_0, ha, rh, r_bph_a, xc_damp): diff --git a/test/test_rangecheck.py b/tests/test_rangecheck.py similarity index 83% rename from test/test_rangecheck.py rename to tests/test_rangecheck.py index 4a226f8..cb95cea 100644 --- a/test/test_rangecheck.py +++ b/tests/test_rangecheck.py @@ -8,7 +8,7 @@ import numpy as np import hope, itertools, pytest, sys, sysconfig, os, shutil -from test.utilities import random, check, make_test, JENKINS, min_dtypes, dtypes, shapes, setup_module, setup_method, teardown_module +from .utilities import random, check, make_test, JENKINS, min_dtypes, dtypes, shapes, setup_module, setup_method, teardown_module def test_opt_pow_array(): def fkt(a, i): diff --git a/test/test_return.py b/tests/test_return.py similarity index 95% rename from test/test_return.py rename to tests/test_return.py index 1e7277e..e6a4d5c 100644 --- a/test/test_return.py +++ b/tests/test_return.py @@ -8,7 +8,7 @@ import numpy as np import hope, itertools, pytest, sys, sysconfig, os, shutil -from test.utilities import random, check, make_test, JENKINS, min_dtypes, dtypes, shapes, setup_module, setup_method, teardown_module +from .utilities import random, check, make_test, JENKINS, min_dtypes, dtypes, shapes, setup_module, setup_method, teardown_module @pytest.mark.parametrize("dtype,shape", itertools.product(dtypes, shapes)) @make_test diff --git a/test/test_serialization.py b/tests/test_serialization.py similarity index 97% rename from test/test_serialization.py rename to tests/test_serialization.py index 403b7b4..5723b50 100644 --- a/test/test_serialization.py +++ b/tests/test_serialization.py @@ -48,4 +48,4 @@ def test_unserialize_inexistent(self): def tear_down(self): shutil.rmtree(config.prefix) - config.prefix = self.prefix \ No newline at end of file + config.prefix = self.prefix diff --git a/test/test_slice.py b/tests/test_slice.py similarity index 96% rename from test/test_slice.py rename to tests/test_slice.py index 43c8f5c..e8bb109 100644 --- a/test/test_slice.py +++ b/tests/test_slice.py @@ -8,7 +8,7 @@ import hope import itertools, pytest -from test.utilities import random, check, make_test, dtypes, shapes, setup_module, setup_method, teardown_module +from .utilities import random, check, make_test, dtypes, shapes, setup_module, setup_method, teardown_module @pytest.mark.parametrize("dtype", dtypes) def test_assignment(dtype): @@ -111,4 +111,4 @@ def fkt(a, c): fkt(ao, co), hfkt(ah, ch) assert check(co, ch) fkt(ao, co), hfkt(ah, ch) - assert check(co, ch) \ No newline at end of file + assert check(co, ch) diff --git a/test/test_slice_negative.py b/tests/test_slice_negative.py similarity index 95% rename from test/test_slice_negative.py rename to tests/test_slice_negative.py index 2d91be7..d6c85a4 100644 --- a/test/test_slice_negative.py +++ b/tests/test_slice_negative.py @@ -11,7 +11,7 @@ import pytest import numpy as np -from test.utilities import setup_module, setup_method, teardown_module +from .utilities import setup_module, setup_method, teardown_module from hope.exceptions import UnsupportedFeatureException def test_negative_idx_1d(): diff --git a/test/test_tosource.py b/tests/test_tosource.py similarity index 98% rename from test/test_tosource.py rename to tests/test_tosource.py index 93a4906..7a939fb 100644 --- a/test/test_tosource.py +++ b/tests/test_tosource.py @@ -68,4 +68,4 @@ def compare_ast(node1, node2): elif isinstance(node1, list): return all(itertools.starmap(compare_ast, zip(node1, node2))) else: - return node1 == node2 \ No newline at end of file + return node1 == node2 diff --git a/test/test_ufig.py b/tests/test_ufig.py similarity index 97% rename from test/test_ufig.py rename to tests/test_ufig.py index ce00a7a..60fa667 100644 --- a/test/test_ufig.py +++ b/tests/test_ufig.py @@ -8,7 +8,7 @@ import numpy as np import hope, itertools, pytest, sys, sysconfig, os, shutil -from test.utilities import random, check, make_test, JENKINS, min_dtypes, dtypes, shapes, setup_module, setup_method, teardown_module +from .utilities import random, check, make_test, JENKINS, min_dtypes, dtypes, shapes, setup_module, setup_method, teardown_module #hope.config.keeptemp = True @@ -156,4 +156,4 @@ def fkt_pdf(density, dims, center, w2D, r50, b, a): if np.all(hdensity == 1): print("asdf") else: - print("else") \ No newline at end of file + print("else") diff --git a/test/test_wrapper.py b/tests/test_wrapper.py similarity index 100% rename from test/test_wrapper.py rename to tests/test_wrapper.py diff --git a/test/utilities.py b/tests/utilities.py similarity index 100% rename from test/utilities.py rename to tests/utilities.py diff --git a/tox.ini b/tox.ini index d6d2c8b..24dc7a6 100644 --- a/tox.ini +++ b/tox.ini @@ -1,36 +1,31 @@ [tox] -envlist = py27, py33, py34, docs-ci +envlist = py27, py34, py35, py36 + +envdir = + py27: {toxworkdir}/py27 + style: {toxworkdir}/py27 + docs: {toxworkdir}/py27 + publish_docs: {toxworkdir}/py27 [testenv] -setenv = - PYTHONPATH = {toxinidir}:{toxinidir}/hope deps = - -r{toxinidir}/requirements.txt + pip>=9.0.0 + setuptools>=34.0.0 + numpy + style: pylint + style: flake8 commands = - py.test --basetemp={envtmpdir} --junitxml=junit-{envname}.xml --cov-report xml --cov hope + pip install -r requirements.txt + pip install -e . # in case we have to compile c extensions + py.test -v --basetemp={envtmpdir} --cov-report term-missing --cov-report html --cov PyCosmo tests -[testenv:style] -deps = - -r{toxinidir}/requirements.txt - flake8 -commands = - python setup.py flake8 - -[testenv:docs] -changedir=docs/ -deps = - -r{toxinidir}/requirements.txt - sphinx -commands = - sphinx-build -b linkcheck ./ _build/ - sphinx-build -b html ./ _build/ - -[testenv:docs-ci] -whitelist_externals = mv -changedir=docs/ -deps = - -r{toxinidir}/requirements.txt - sphinx -commands= - py.test --tb=line -v --junitxml=junit-{envname}.xml check_sphinx.py --cov-report xml --cov hope - mv coverage.xml ../. + docs: sphinx-build -b linkcheck docs/ docs/_build/ # checks external links + docs: sphinx-build -b html docs/ docs/_build/ + docs: rm -fr docs/_build/htmlcov + docs: mv htmlcov docs/_build + + publish_docs: publish_docs docs/_build PyCosmo + publish_docs: create_index_html + + style: pylint --disable R0913,R0914 PyCosmo tests + style: flake8 tests PyCosmo From e6f2150d7f9ce98883bd4dbef4d820fcb26e457a Mon Sep 17 00:00:00 2001 From: Uwe Date: Fri, 11 Aug 2017 11:21:41 +0200 Subject: [PATCH 03/48] fixed pyenv calls in .gitlab-ci.yml --- .gitlab-ci.yml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index f5c3a03..047304d 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -3,14 +3,14 @@ tests: - python script: - pyenv versions - - pyenv local 2.7 3.4 3.5 3.6 + - pyenv local 2.7 3.4.6 3.5.3 3.6.2 - tox -r -vv -i https://cosmo-pypi.phys.ethz.ch/simple style: tags: - python script: - - pyenv local 2.7 3.4 3.5 3.6 + - pyenv local 2.7 3.4.6 3.5.3 3.6.2 - tox -e style -i https://cosmo-pypi.phys.ethz.ch/simple allow_failure: true @@ -18,13 +18,13 @@ docs: tags: - python script: - - pyenv local 2.7 3.4 3.5 3.6 + - pyenv local 2.7 3.4.6 3.5.3 3.6.2 - tox -e docs -i https://cosmo-pypi.phys.ethz.ch/simple publish_docs: tags: - python script: - - pyenv local 2.7 3.4 3.5 3.6 + - pyenv local 2.7 3.4.6 3.5.3 3.6.2 - tox -e publish_docs -i https://cosmo-pypi.phys.ethz.ch/simple when: manual From dd6def494d53844ac8b908b4bbd8594d5fb0525d Mon Sep 17 00:00:00 2001 From: Uwe Date: Fri, 11 Aug 2017 13:24:18 +0200 Subject: [PATCH 04/48] removed py>3.4 from tox.ini --- tox.ini | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tox.ini b/tox.ini index 24dc7a6..f51511d 100644 --- a/tox.ini +++ b/tox.ini @@ -1,5 +1,5 @@ [tox] -envlist = py27, py34, py35, py36 +envlist = py27, py34 envdir = py27: {toxworkdir}/py27 From 5d3c217f35ffc657880cb2bfad057cd10076cbc9 Mon Sep 17 00:00:00 2001 From: Uwe Date: Tue, 15 Aug 2017 17:48:10 +0200 Subject: [PATCH 05/48] fixed copied references to PyCosmo from tox.ini --- tox.ini | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/tox.ini b/tox.ini index f51511d..7025238 100644 --- a/tox.ini +++ b/tox.ini @@ -17,15 +17,15 @@ deps = commands = pip install -r requirements.txt pip install -e . # in case we have to compile c extensions - py.test -v --basetemp={envtmpdir} --cov-report term-missing --cov-report html --cov PyCosmo tests + py.test -v --basetemp={envtmpdir} --cov-report term-missing --cov-report html --cov hope tests docs: sphinx-build -b linkcheck docs/ docs/_build/ # checks external links docs: sphinx-build -b html docs/ docs/_build/ docs: rm -fr docs/_build/htmlcov docs: mv htmlcov docs/_build - publish_docs: publish_docs docs/_build PyCosmo + publish_docs: publish_docs docs/_build hope publish_docs: create_index_html - style: pylint --disable R0913,R0914 PyCosmo tests - style: flake8 tests PyCosmo + style: pylint --disable R0913,R0914 hope tests + style: flake8 tests hope From ad2957d77e5b808f424e64637c3abc902b569a82 Mon Sep 17 00:00:00 2001 From: Uwe Date: Wed, 16 Aug 2017 16:08:10 +0200 Subject: [PATCH 06/48] fixed randomized tests, eg using a seed --- tests/conftest.py | 8 ++++++++ tests/test_op_pow.py | 4 ++-- 2 files changed, 10 insertions(+), 2 deletions(-) create mode 100644 tests/conftest.py diff --git a/tests/conftest.py b/tests/conftest.py new file mode 100644 index 0000000..c0567eb --- /dev/null +++ b/tests/conftest.py @@ -0,0 +1,8 @@ +# encoding: utf-8 +from __future__ import print_function, division, absolute_import + +# we set a seed, because the legacy hope test code uses random data in somep places which caused +# unforeseeable failures when numpy changed its behaviour handling exponentiation of integer +# types. +import random +random.seed(12345) diff --git a/tests/test_op_pow.py b/tests/test_op_pow.py index b0a4aa4..fea71b4 100644 --- a/tests/test_op_pow.py +++ b/tests/test_op_pow.py @@ -12,7 +12,7 @@ @pytest.mark.parametrize("dtype,shape", itertools.product(dtypes, shapes[1:])) def test_augmented_pow(dtype, shape): - def fkt(a, c): + def fkt(a, c): c[:] **= a hfkt = hope.jit(fkt) (ao, ah), (co, ch) = random(np.uint8, shape), random(dtype, shape) @@ -22,7 +22,7 @@ def fkt(a, c): ch[ch == 0] += 1 co, ch = np.copysign(np.sqrt(np.abs(co)), co).astype(dtype), np.copysign(np.sqrt(np.abs(ch)), ch).astype(dtype) ao, ah = np.power(np.abs(ao).astype(np.float64), 1. / co.astype(np.float64)).astype(dtype), np.power(np.abs(ah).astype(np.float64), 1. / ch.astype(np.float64)).astype(dtype) - fkt(ao, co), hfkt(ah, ch) + fkt(np.abs(ao), co), hfkt(np.abs(ah), ch) assert check(co, ch) # TODO: fix for np.ulonglong and uint64, std::power produce different results From 0f9392f6e25577d2723a9df61bf1113fd0df15cc Mon Sep 17 00:00:00 2001 From: Uwe Date: Thu, 17 Aug 2017 12:11:50 +0200 Subject: [PATCH 07/48] converted jupyter notebooks in benchmarks folder to recent nb format --- benchmarks/HPC Python.ipynb | 936 +++---- benchmarks/fibonacci.ipynb | 362 ++- benchmarks/julialang.org.ipynb | 1406 +++++----- benchmarks/native_cpp_gen.ipynb | 4444 +++++++++++++++---------------- benchmarks/numexpr.ipynb | 1192 ++++----- benchmarks/pairwise.ipynb | 721 +++-- benchmarks/simplify.ipynb | 638 +++-- benchmarks/star.ipynb | 926 ++++--- examples/hope_numpy.py | 2 +- 9 files changed, 5071 insertions(+), 5556 deletions(-) diff --git a/benchmarks/HPC Python.ipynb b/benchmarks/HPC Python.ipynb index 37420b8..2f0012e 100644 --- a/benchmarks/HPC Python.ipynb +++ b/benchmarks/HPC Python.ipynb @@ -1,535 +1,447 @@ { - "metadata": { - "name": "", - "signature": "sha256:bc54c0a983314fa977f09e4ffb2a2c91ee6f84fd2477d2ff297f27cc2b72d428" - }, - "nbformat": 3, - "nbformat_minor": 0, - "worksheets": [ + "cells": [ { - "cells": [ - { - "cell_type": "code", - "collapsed": false, - "input": [ - "import hope\n", - "hope.config.optimize = True\n", - "hope.config.keeptemp = True\n", - "import numpy as np\n", - "from matplotlib import pyplot as plt\n", - "%pylab inline" - ], - "language": "python", - "metadata": { - "slideshow": { - "slide_type": "slide" - } - }, - "outputs": [ - { - "output_type": "stream", - "stream": "stdout", - "text": [ - "Populating the interactive namespace from numpy and matplotlib\n" - ] - } - ], - "prompt_number": 1 - }, + "cell_type": "code", + "execution_count": 1, + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "outputs": [ { - "cell_type": "heading", - "level": 1, - "metadata": { - "slideshow": { - "slide_type": "slide" - } - }, - "source": [ - "Python Optimizations" + "name": "stdout", + "output_type": "stream", + "text": [ + "Populating the interactive namespace from numpy and matplotlib\n" ] - }, + } + ], + "source": [ + "import hope\n", + "hope.config.optimize = True\n", + "hope.config.keeptemp = True\n", + "import numpy as np\n", + "from matplotlib import pyplot as plt\n", + "%pylab inline" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "source": [ + "# Python Optimizations" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Avoid memory allocations" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "**zombie apocalypse modeling** from http://wiki.scipy.org/Cookbook/Zombie_Apocalypse_ODEINT" + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "metadata": {}, + "outputs": [], + "source": [ + "from scipy.integrate import odeint\n", + "\n", + "P, d, B, G, A = 0, 0.0001, 0.0095, 0.0001, 0.0001\n", + "\n", + "def f_nat(y, t):\n", + " dy = np.empty(3)\n", + " dy[0] = P - B*y[0]*y[1] - d*y[0]\n", + " dy[1] = B*y[0]*y[1] + G*y[2] - A*y[0]*y[1]\n", + " dy[2] = d*y[0] + A*y[0]*y[1] - G*y[2]\n", + " return dy\n", + "\n", + "y0, t = np.array([500., 0., 5.]), np.linspace(0, 5., 1000)" + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "metadata": {}, + "outputs": [ { - "cell_type": "heading", - "level": 2, + "data": { + "image/png": "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\nCLPk4uLCZ599xkcffQToP83279+fHj160KNHDw4cOABARETENdOgT548mU2bNvHnn38SFBREYGAg\n/v7+JCUlXbP/119/nf379zNjxgxeeOEFli5dyvDhwwkNDSUsLIzz588zcuRI/P39ufvuuw3rTkRG\nRhIREUH//v3x9vZm/fr1/Otf/8LPz48hQ4ZQXl5+w+9y+PBhevfujb+/P6NHjzZMLxEcHExcXByg\nn3bCx8eHsrIyXnvtNdasWUNgYCBr164F9K2GPn360KFDB7744gsAYmJi6NevHyNGjKBr167odDqe\nf/55evXqhb+/P5999hmgn/hv4MCB9OjRAz8/v5uuDXDq1Cm6d+9uyCOslCKEmWjcuPENtzk5OSk5\nOTlKUVGRUlJSoiiKoiQmJio9e/ZUFEVRfvjhB2XkyJGKoihKfn6+4uPjo5SXlyuzZs1SVq5cqSiK\nopSVlSnFxcU37Ds4OFiJi4tTFEVRlixZonh5eSnnz59XFEVRZs2apbz++uuKoijKnj17lICAAEVR\nFGXu3LlKv379lPLycuX3339XGjZsqGzfvl1RFEUZNWqUsmHDhhuO061bN+XHH39UFEVRXnvtNeWZ\nZ5654fi5ubmKt7e3oiiK8tVXXylPPvmk4flz585V/P39lZKSEuXs2bNKq1atlIyMDGXv3r1Ko0aN\nlJSUFEVRFOXTTz9V3nzzTUVRFKWkpETp2bOnkpycrJSXlysFBQWG4/j6+iqKoijJyclK165dlePH\njyuBgYHKkSNHbvGvI6yBtDFFnVBaWsqsWbP4/fffsbW1JTExEYD+/fszc+ZMzp49y7p16xg7diy2\ntrb06dOHf//736SlpTF69Gh8fX2rPEZYWBhOTk6Afo6k9evXAxASEsK5c+e4ePEiGo2GIUOGYGtr\na/hUPmjQIAC6detGSkrKNfu8cOECFy5coF+/foC+RTNu3Lhb5lAU5ZpzJxqNhpEjR1K/fn3q169P\nSEgIsbGxODk50atXL9q0aQPAjh07OHr0KOvWrQOgoKCApKQkvLy8mDNnDvv27cPGxoaMjAxycnIA\nyMnJYeTIkXz33Xd06tSpytdIWDbpMhJm69SpU9ja2uLi4sIHH3yAh4cHR44c4dChQ5SWlhoeN23a\nNJYvX85XX33FjBkzAP0EgJs3b6Zhw4bcd9997N2795bH0mg0NGrU6JrblEpOaNerVw/Qrz1gb29v\nuN3GxuamXUaV7dPOzg6dTgfo10OuiasLv1yf+aOPPiI+Pp74+HhOnjzJwIEDWbFiBWfPnuW3334j\nPj4eV1dXw/GcnJxo06ZNpfPjC+siBUGYpdzcXB5//HGefPJJQP9p193dHYBly5ZRUVFheOz06dP5\n8MMP0Wg0hk+5ycnJ+Pj48OSTTzJixIgq156+/s2/X79+rFy5EtD31bu4uODo6FjjUU9NmzbF2dmZ\nn376CYDly5cTHBwM6Bc+v7pewdVP9QBNmjQxTFd8NdvGjRu5fPky586dIyYmhrvuuuuGLIMGDWLR\nokWGopSYmEhRUREFBQW4urpia2vL3r17OX36tOE59erVY/369SxbtoxVq1bV6HcTlke6jITZKC4u\nJjAwkLKyMuzs7Jg2bRrPPvssADNnzmTMmDEsW7aMwYMHX7MqmqurK1qtllGjRhluW7t2LcuXL8fe\n3h4PDw9eeeWVWx5bo9FcM5onMjKSGTNm4O/vT6NGjVi6dOlNH3f9CKCbjQhaunQpjz/+OEVFRbRr\n184wXfm//vUvxo8fz2effcbQoUMNzw0JCSEqKorAwEDmzJmDRqPBz8+PkJAQzp49y2uvvYa7uzt/\n/fXXNcd7+OGHSUlJoXv37iiKgqurKxs2bGDy5MkMGzYMPz8/evbsec208RqNBgcHB7Zs2UJYWBiO\njo7cf//9t3ythOWS6a9FnVdUVISfnx/x8fE4OjqqHUeIOku6jESdtmvXLrRaLU899ZQUAyHukLQQ\nhBBCANJCEEIIcYUUBCGEEIAUBCGEEFdIQRBCCAFIQRBCCHGFFAQhhBAA/H8Alc/dnlS5igAAAABJ\nRU5ErkJggg==\n", + "text/plain": [ + "" + ] + }, "metadata": {}, - "source": [ - "Avoid memory allocations" - ] - }, + "output_type": "display_data" + } + ], + "source": [ + "soln = odeint(f_nat, y0, t)\n", + "plt.figure()\n", + "plt.plot(t, soln[:, 0], label='Living')\n", + "plt.plot(t, soln[:, 1], label='Zombies')\n", + "plt.xlabel('Days from outbreak')\n", + "plt.ylabel('Population')\n", + "plt.legend(loc=0)\n", + "plt.show()" + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "metadata": {}, + "outputs": [ { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "**zombie apocalypse modeling** from http://wiki.scipy.org/Cookbook/Zombie_Apocalypse_ODEINT" + "name": "stdout", + "output_type": "stream", + "text": [ + "native python\n", + "100 loops, best of 3: 4.46 ms per loop\n", + "hope\n", + "1000 loops, best of 3: 515 µs per loop\n", + "hope without allocation\n", + "1000 loops, best of 3: 470 µs per loop\n" ] - }, - { - "cell_type": "code", - "collapsed": false, - "input": [ - "from scipy.integrate import odeint\n", - "\n", - "P, d, B, G, A = 0, 0.0001, 0.0095, 0.0001, 0.0001\n", - "\n", - "def f_nat(y, t):\n", - " dy = np.empty(3)\n", - " dy[0] = P - B*y[0]*y[1] - d*y[0]\n", - " dy[1] = B*y[0]*y[1] + G*y[2] - A*y[0]*y[1]\n", - " dy[2] = d*y[0] + A*y[0]*y[1] - G*y[2]\n", - " return dy\n", - "\n", - "y0, t = np.array([500., 0., 5.]), np.linspace(0, 5., 1000)" - ], - "language": "python", - "metadata": {}, - "outputs": [], - "prompt_number": 2 - }, - { - "cell_type": "code", - "collapsed": false, - "input": [ - "soln = odeint(f_nat, y0, t)\n", - "plt.figure()\n", - "plt.plot(t, soln[:, 0], label='Living')\n", - "plt.plot(t, soln[:, 1], label='Zombies')\n", - "plt.xlabel('Days from outbreak')\n", - "plt.ylabel('Population')\n", - "plt.legend(loc=0)\n", - "plt.show()" - ], - "language": "python", - "metadata": {}, - "outputs": [ - { - "metadata": {}, - "output_type": "display_data", - "png": "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\nCLPk4uLCZ599xkcffQToP83279+fHj160KNHDw4cOABARETENdOgT548mU2bNvHnn38SFBREYGAg\n/v7+JCUlXbP/119/nf379zNjxgxeeOEFli5dyvDhwwkNDSUsLIzz588zcuRI/P39ufvuuw3rTkRG\nRhIREUH//v3x9vZm/fr1/Otf/8LPz48hQ4ZQXl5+w+9y+PBhevfujb+/P6NHjzZMLxEcHExcXByg\nn3bCx8eHsrIyXnvtNdasWUNgYCBr164F9K2GPn360KFDB7744gsAYmJi6NevHyNGjKBr167odDqe\nf/55evXqhb+/P5999hmgn/hv4MCB9OjRAz8/v5uuDXDq1Cm6d+9uyCOslCKEmWjcuPENtzk5OSk5\nOTlKUVGRUlJSoiiKoiQmJio9e/ZUFEVRfvjhB2XkyJGKoihKfn6+4uPjo5SXlyuzZs1SVq5cqSiK\nopSVlSnFxcU37Ds4OFiJi4tTFEVRlixZonh5eSnnz59XFEVRZs2apbz++uuKoijKnj17lICAAEVR\nFGXu3LlKv379lPLycuX3339XGjZsqGzfvl1RFEUZNWqUsmHDhhuO061bN+XHH39UFEVRXnvtNeWZ\nZ5654fi5ubmKt7e3oiiK8tVXXylPPvmk4flz585V/P39lZKSEuXs2bNKq1atlIyMDGXv3r1Ko0aN\nlJSUFEVRFOXTTz9V3nzzTUVRFKWkpETp2bOnkpycrJSXlysFBQWG4/j6+iqKoijJyclK165dlePH\njyuBgYHKkSNHbvGvI6yBtDFFnVBaWsqsWbP4/fffsbW1JTExEYD+/fszc+ZMzp49y7p16xg7diy2\ntrb06dOHf//736SlpTF69Gh8fX2rPEZYWBhOTk6Afo6k9evXAxASEsK5c+e4ePEiGo2GIUOGYGtr\na/hUPmjQIAC6detGSkrKNfu8cOECFy5coF+/foC+RTNu3Lhb5lAU5ZpzJxqNhpEjR1K/fn3q169P\nSEgIsbGxODk50atXL9q0aQPAjh07OHr0KOvWrQOgoKCApKQkvLy8mDNnDvv27cPGxoaMjAxycnIA\nyMnJYeTIkXz33Xd06tSpytdIWDbpMhJm69SpU9ja2uLi4sIHH3yAh4cHR44c4dChQ5SWlhoeN23a\nNJYvX85XX33FjBkzAP0EgJs3b6Zhw4bcd9997N2795bH0mg0NGrU6JrblEpOaNerVw/Qrz1gb29v\nuN3GxuamXUaV7dPOzg6dTgfo10OuiasLv1yf+aOPPiI+Pp74+HhOnjzJwIEDWbFiBWfPnuW3334j\nPj4eV1dXw/GcnJxo06ZNpfPjC+siBUGYpdzcXB5//HGefPJJQP9p193dHYBly5ZRUVFheOz06dP5\n8MMP0Wg0hk+5ycnJ+Pj48OSTTzJixIgq156+/s2/X79+rFy5EtD31bu4uODo6FjjUU9NmzbF2dmZ\nn376CYDly5cTHBwM6Bc+v7pewdVP9QBNmjQxTFd8NdvGjRu5fPky586dIyYmhrvuuuuGLIMGDWLR\nokWGopSYmEhRUREFBQW4urpia2vL3r17OX36tOE59erVY/369SxbtoxVq1bV6HcTlke6jITZKC4u\nJjAwkLKyMuzs7Jg2bRrPPvssADNnzmTMmDEsW7aMwYMHX7MqmqurK1qtllGjRhluW7t2LcuXL8fe\n3h4PDw9eeeWVWx5bo9FcM5onMjKSGTNm4O/vT6NGjVi6dOlNH3f9CKCbjQhaunQpjz/+OEVFRbRr\n184wXfm//vUvxo8fz2effcbQoUMNzw0JCSEqKorAwEDmzJmDRqPBz8+PkJAQzp49y2uvvYa7uzt/\n/fXXNcd7+OGHSUlJoXv37iiKgqurKxs2bGDy5MkMGzYMPz8/evbsec208RqNBgcHB7Zs2UJYWBiO\njo7cf//9t3ythOWS6a9FnVdUVISfnx/x8fE4OjqqHUeIOku6jESdtmvXLrRaLU899ZQUAyHukLQQ\nhBBCANJCEEIIcYUUBCGEEIAUBCGEEFdIQRBCCAFIQRBCCHGFFAQhhBAA/H8Alc/dnlS5igAAAABJ\nRU5ErkJggg==\n", - "text": [ - "" - ] - } - ], - "prompt_number": 3 - }, - { - "cell_type": "code", - "collapsed": false, - "input": [ - "@hope.jit\n", - "def f_hope(y, t, P, d, B, G, A):\n", - " dy = np.empty(3)\n", - " dy[0] = P - B*y[0]*y[1] - d*y[0]\n", - " dy[1] = B*y[0]*y[1] + G*y[2] - A*y[0]*y[1]\n", - " dy[2] = d*y[0] + A*y[0]*y[1] - G*y[2]\n", - " return dy\n", - "\n", - "@hope.jit\n", - "def f_opt(y, t, dy, P, d, B, G, A):\n", - " dy[0] = P - B*y[0]*y[1] - d*y[0]\n", - " dy[1] = B*y[0]*y[1] + G*y[2] - A*y[0]*y[1]\n", - " dy[2] = d*y[0] + A*y[0]*y[1] - G*y[2]\n", - " return dy\n", - "\n", - "dy = np.empty(3)\n", - "print \"native python\"\n", - "%timeit odeint(f_nat, y0, t)\n", - "print \"hope\"\n", - "%timeit odeint(f_hope, y0, t, args=(P, d, B, G, A))\n", - "print \"hope without allocation\"\n", - "%timeit odeint(f_opt, y0, t, args=(dy, P, d, B, G, A))" - ], - "language": "python", - "metadata": {}, - "outputs": [ - { - "output_type": "stream", - "stream": "stdout", - "text": [ - "native python\n", - "100 loops, best of 3: 4.46 ms per loop" - ] - }, - { - "output_type": "stream", - "stream": "stdout", - "text": [ - "\n", - "hope\n", - "1000 loops, best of 3: 515 \u00b5s per loop" - ] - }, - { - "output_type": "stream", - "stream": "stdout", - "text": [ - "\n", - "hope without allocation\n", - "1000 loops, best of 3: 470 \u00b5s per loop" - ] - }, - { - "output_type": "stream", - "stream": "stdout", - "text": [ - "\n" - ] - } - ], - "prompt_number": 4 - }, + } + ], + "source": [ + "@hope.jit\n", + "def f_hope(y, t, P, d, B, G, A):\n", + " dy = np.empty(3)\n", + " dy[0] = P - B*y[0]*y[1] - d*y[0]\n", + " dy[1] = B*y[0]*y[1] + G*y[2] - A*y[0]*y[1]\n", + " dy[2] = d*y[0] + A*y[0]*y[1] - G*y[2]\n", + " return dy\n", + "\n", + "@hope.jit\n", + "def f_opt(y, t, dy, P, d, B, G, A):\n", + " dy[0] = P - B*y[0]*y[1] - d*y[0]\n", + " dy[1] = B*y[0]*y[1] + G*y[2] - A*y[0]*y[1]\n", + " dy[2] = d*y[0] + A*y[0]*y[1] - G*y[2]\n", + " return dy\n", + "\n", + "dy = np.empty(3)\n", + "print \"native python\"\n", + "%timeit odeint(f_nat, y0, t)\n", + "print \"hope\"\n", + "%timeit odeint(f_hope, y0, t, args=(P, d, B, G, A))\n", + "print \"hope without allocation\"\n", + "%timeit odeint(f_opt, y0, t, args=(dy, P, d, B, G, A))" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "source": [ + "## Approximate expensive functions" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### tanh" + ] + }, + { + "cell_type": "code", + "execution_count": 5, + "metadata": {}, + "outputs": [ { - "cell_type": "heading", - "level": 2, - "metadata": { - "slideshow": { - "slide_type": "slide" - } + "data": { + "image/png": "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\n", + "text/plain": [ + "" + ] }, - "source": [ - "Approximate expensive functions" - ] - }, - { - "cell_type": "heading", - "level": 3, "metadata": {}, - "source": [ - "tanh" - ] - }, - { - "cell_type": "code", - "collapsed": false, - "input": [ - "def tanhpoly(x):\n", - " a = np.fabs(x)\n", - " b = 1.26175667589988239 + a * (-0.54699348440059470 + a * 2.66559097474027817)\n", - " return (b * x) / (b * a + 1)\n", - "\n", - "x = np.linspace(-10, 10, 10000)\n", - "\n", - "plt.subplot(2, 1,1)\n", - "plt.plot(x, tanhpoly(x), label=\"approx\")\n", - "plt.plot(x, np.tanh(x), label=\"tanh\")\n", - "plt.ylabel('Tanh(x)')\n", - "plt.legend()\n", - "\n", - "plt.subplot(2, 1,2)\n", - "plt.semilogy()\n", - "plt.plot(x, np.fabs(tanhpoly(x)- np.tanh(x)))\n", - "plt.ylabel('Absolute Error')\n", - "plt.show()" - ], - "language": "python", - "metadata": {}, - "outputs": [ - { - "metadata": {}, - "output_type": "display_data", - "png": "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\n", - "text": [ - "" - ] - } - ], - "prompt_number": 5 - }, - { - "cell_type": "code", - "collapsed": false, - "input": [ - "@hope.jit\n", - "def tanh_hope(x, y):\n", - " y[:] = np.tanh(x)\n", - "\n", - "@hope.jit\n", - "def tanhpoly_hope(x, y):\n", - " a = np.fabs(x)\n", - " b = 1.26175667589988239 + a * (-0.54699348440059470 + a * 2.66559097474027817)\n", - " y[:] = (b * x) / (b * a + 1)\n", - "\n", - "y = np.empty_like(x)\n", - "\n", - "print \"numpy tanh\"\n", - "%timeit tanh_hope(x, y)\n", - "print \"polynomial approximation\"\n", - "%timeit tanhpoly_hope(x, y)" - ], - "language": "python", - "metadata": {}, - "outputs": [ - { - "output_type": "stream", - "stream": "stdout", - "text": [ - "numpy tanh\n" - ] - }, - { - "output_type": "stream", - "stream": "stdout", - "text": [ - "1 loops, best of 3: 759 \u00b5s per loop\n", - "polynomial approximation\n" - ] - }, - { - "output_type": "stream", - "stream": "stdout", - "text": [ - "1 loops, best of 3: 23.8 \u00b5s per loop\n" - ] - } - ], - "prompt_number": 6 - }, + "output_type": "display_data" + } + ], + "source": [ + "def tanhpoly(x):\n", + " a = np.fabs(x)\n", + " b = 1.26175667589988239 + a * (-0.54699348440059470 + a * 2.66559097474027817)\n", + " return (b * x) / (b * a + 1)\n", + "\n", + "x = np.linspace(-10, 10, 10000)\n", + "\n", + "plt.subplot(2, 1,1)\n", + "plt.plot(x, tanhpoly(x), label=\"approx\")\n", + "plt.plot(x, np.tanh(x), label=\"tanh\")\n", + "plt.ylabel('Tanh(x)')\n", + "plt.legend()\n", + "\n", + "plt.subplot(2, 1,2)\n", + "plt.semilogy()\n", + "plt.plot(x, np.fabs(tanhpoly(x)- np.tanh(x)))\n", + "plt.ylabel('Absolute Error')\n", + "plt.show()" + ] + }, + { + "cell_type": "code", + "execution_count": 6, + "metadata": {}, + "outputs": [ { - "cell_type": "heading", - "level": 3, - "metadata": {}, - "source": [ - "exp" + "name": "stdout", + "output_type": "stream", + "text": [ + "numpy tanh\n", + "1 loops, best of 3: 759 µs per loop\n", + "polynomial approximation\n", + "1 loops, best of 3: 23.8 µs per loop\n" ] - }, - { - "cell_type": "code", - "collapsed": false, - "input": [ - "@hope.jit\n", - "def expapprox(x, y):\n", - " y[:] = hope.exp(x)\n", - " return y\n", - "\n", - "x = np.linspace(-16, 0, 10000)\n", - "y = np.empty_like(x)\n", - "\n", - "plt.subplot(3, 1, 1)\n", - "plt.plot(x, expapprox(x, y), label=\"approx\")\n", - "plt.plot(x, np.exp(x), label=\"exp\")\n", - "plt.ylabel('Exp(x)')\n", - "plt.legend()\n", - "\n", - "plt.subplot(3, 1, 2)\n", - "plt.semilogy()\n", - "plt.plot(x, np.fabs(expapprox(x, y)- np.exp(x)) + np.finfo(np.float64).resolution)\n", - "plt.ylabel('Absolute Error')\n", - "\n", - "plt.subplot(3, 1, 3)\n", - "plt.semilogy()\n", - "plt.plot(x, np.fabs(expapprox(x, y)- np.exp(x)) / np.exp(x) + np.finfo(np.float64).resolution)\n", - "plt.ylabel('Relative Error')\n", - "plt.show()" - ], - "language": "python", - "metadata": {}, - "outputs": [ - { - "metadata": {}, - "output_type": "display_data", - "png": "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\nYQgEAoGAi/8HnIqwBONccX4AAAAASUVORK5CYII=\n", - "text": [ - "" - ] - } - ], - "prompt_number": 7 - }, + } + ], + "source": [ + "@hope.jit\n", + "def tanh_hope(x, y):\n", + " y[:] = np.tanh(x)\n", + "\n", + "@hope.jit\n", + "def tanhpoly_hope(x, y):\n", + " a = np.fabs(x)\n", + " b = 1.26175667589988239 + a * (-0.54699348440059470 + a * 2.66559097474027817)\n", + " y[:] = (b * x) / (b * a + 1)\n", + "\n", + "y = np.empty_like(x)\n", + "\n", + "print \"numpy tanh\"\n", + "%timeit tanh_hope(x, y)\n", + "print \"polynomial approximation\"\n", + "%timeit tanhpoly_hope(x, y)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### exp" + ] + }, + { + "cell_type": "code", + "execution_count": 7, + "metadata": {}, + "outputs": [ { - "cell_type": "code", - "collapsed": false, - "input": [ - "@hope.jit\n", - "def exp_hope(x, y):\n", - " y[:] = np.exp(x)\n", - "\n", - "@hope.jit\n", - "def exppow_hope(x, y):\n", - " y[:] = hope.exp(x)\n", - " \n", - "y = np.empty_like(x)\n", - "\n", - "print \"numpy exp\"\n", - "%timeit exp_hope(x, y)\n", - "print \"polynomial exp\"\n", - "%timeit exppow_hope(x, y)" - ], - "language": "python", + "data": { + "image/png": "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\nYQgEAoGAi/8HnIqwBONccX4AAAAASUVORK5CYII=\n", + "text/plain": [ + "" + ] + }, "metadata": {}, - "outputs": [ - { - "output_type": "stream", - "stream": "stdout", - "text": [ - "numpy exp\n" - ] - }, - { - "output_type": "stream", - "stream": "stdout", - "text": [ - "1 loops, best of 3: 68.9 \u00b5s per loop\n", - "polynomial exp\n" - ] - }, - { - "output_type": "stream", - "stream": "stdout", - "text": [ - "1 loops, best of 3: 21 \u00b5s per loop\n" - ] - } - ], - "prompt_number": 8 - }, + "output_type": "display_data" + } + ], + "source": [ + "@hope.jit\n", + "def expapprox(x, y):\n", + " y[:] = hope.exp(x)\n", + " return y\n", + "\n", + "x = np.linspace(-16, 0, 10000)\n", + "y = np.empty_like(x)\n", + "\n", + "plt.subplot(3, 1, 1)\n", + "plt.plot(x, expapprox(x, y), label=\"approx\")\n", + "plt.plot(x, np.exp(x), label=\"exp\")\n", + "plt.ylabel('Exp(x)')\n", + "plt.legend()\n", + "\n", + "plt.subplot(3, 1, 2)\n", + "plt.semilogy()\n", + "plt.plot(x, np.fabs(expapprox(x, y)- np.exp(x)) + np.finfo(np.float64).resolution)\n", + "plt.ylabel('Absolute Error')\n", + "\n", + "plt.subplot(3, 1, 3)\n", + "plt.semilogy()\n", + "plt.plot(x, np.fabs(expapprox(x, y)- np.exp(x)) / np.exp(x) + np.finfo(np.float64).resolution)\n", + "plt.ylabel('Relative Error')\n", + "plt.show()" + ] + }, + { + "cell_type": "code", + "execution_count": 8, + "metadata": {}, + "outputs": [ { - "cell_type": "heading", - "level": 2, - "metadata": { - "slideshow": { - "slide_type": "slide" - } - }, - "source": [ - "Avoid floating point powers" + "name": "stdout", + "output_type": "stream", + "text": [ + "numpy exp\n", + "1 loops, best of 3: 68.9 µs per loop\n", + "polynomial exp\n", + "1 loops, best of 3: 21 µs per loop\n" ] - }, - { - "cell_type": "code", - "collapsed": false, - "input": [ - "def native_pow(x, y):\n", - " y[:] = 16.11 - 3./(3. * x**2) + 0.5 *(0.4651 * x**-3 + 43.44) * x**-3 + 4. * x**-4\n", - " \n", - "@hope.jit\n", - "def float_pow(x, y):\n", - " y[:] = 16.11 - 3./(3. * x**2.) + 0.5 *(0.4651 * x**-3. + 43.44) * x**-3. + 4. * x**-4.\n", - "\n", - "@hope.jit\n", - "def int_pow(x, y):\n", - " y[:] = 16.11 - 3./(3. * x**2) + 0.5 *(0.4651 * x**-3 + 43.44) * x**-3 + 4. * x**-4\n", - "\n", - "x = np.linspace(-10, 10, 10000)\n", - "y = np.empty_like(x)\n", - "\n", - "print \"native python\"\n", - "%timeit native_pow(x, y)\n", - "print \"hope float power\"\n", - "%timeit float_pow(x, y)\n", - "print \"hope integer power\"\n", - "%timeit int_pow(x, y)" - ], - "language": "python", - "metadata": {}, - "outputs": [ - { - "output_type": "stream", - "stream": "stdout", - "text": [ - "native python\n", - "1000 loops, best of 3: 1.14 ms per loop" - ] - }, - { - "output_type": "stream", - "stream": "stdout", - "text": [ - "\n", - "hope float power\n", - "Integer exponent as flaot: x.d[:x@0]**(-2.0)" - ] - }, - { - "output_type": "stream", - "stream": "stdout", - "text": [ - "\n", - "Integer exponent as flaot: x.d[:x@0]**(-4.0)\n", - "Integer exponent as flaot: x.d[:x@0]**(-6.0)\n", - "Integer exponent as flaot: x.d[:x@0]**(-3.0)\n" - ] - }, - { - "output_type": "stream", - "stream": "stdout", - "text": [ - "1 loops, best of 3: 1.43 ms per loop\n", - "hope integer power\n" - ] - }, - { - "output_type": "stream", - "stream": "stdout", - "text": [ - "1 loops, best of 3: 81.1 \u00b5s per loop\n" - ] - } - ], - "prompt_number": 9 - }, + } + ], + "source": [ + "@hope.jit\n", + "def exp_hope(x, y):\n", + " y[:] = np.exp(x)\n", + "\n", + "@hope.jit\n", + "def exppow_hope(x, y):\n", + " y[:] = hope.exp(x)\n", + " \n", + "y = np.empty_like(x)\n", + "\n", + "print \"numpy exp\"\n", + "%timeit exp_hope(x, y)\n", + "print \"polynomial exp\"\n", + "%timeit exppow_hope(x, y)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "source": [ + "## Avoid floating point powers" + ] + }, + { + "cell_type": "code", + "execution_count": 9, + "metadata": {}, + "outputs": [ { - "cell_type": "heading", - "level": 2, - "metadata": {}, - "source": [ - "Improve interpolation" + "name": "stdout", + "output_type": "stream", + "text": [ + "native python\n", + "1000 loops, best of 3: 1.14 ms per loop\n", + "hope float power\n", + "Integer exponent as flaot: x.d[:x@0]**(-2.0)\n", + "Integer exponent as flaot: x.d[:x@0]**(-4.0)\n", + "Integer exponent as flaot: x.d[:x@0]**(-6.0)\n", + "Integer exponent as flaot: x.d[:x@0]**(-3.0)\n", + "1 loops, best of 3: 1.43 ms per loop\n", + "hope integer power\n", + "1 loops, best of 3: 81.1 µs per loop\n" ] - }, + } + ], + "source": [ + "def native_pow(x, y):\n", + " y[:] = 16.11 - 3./(3. * x**2) + 0.5 *(0.4651 * x**-3 + 43.44) * x**-3 + 4. * x**-4\n", + " \n", + "@hope.jit\n", + "def float_pow(x, y):\n", + " y[:] = 16.11 - 3./(3. * x**2.) + 0.5 *(0.4651 * x**-3. + 43.44) * x**-3. + 4. * x**-4.\n", + "\n", + "@hope.jit\n", + "def int_pow(x, y):\n", + " y[:] = 16.11 - 3./(3. * x**2) + 0.5 *(0.4651 * x**-3 + 43.44) * x**-3 + 4. * x**-4\n", + "\n", + "x = np.linspace(-10, 10, 10000)\n", + "y = np.empty_like(x)\n", + "\n", + "print \"native python\"\n", + "%timeit native_pow(x, y)\n", + "print \"hope float power\"\n", + "%timeit float_pow(x, y)\n", + "print \"hope integer power\"\n", + "%timeit int_pow(x, y)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Improve interpolation" + ] + }, + { + "cell_type": "code", + "execution_count": 10, + "metadata": {}, + "outputs": [ { - "cell_type": "code", - "collapsed": false, - "input": [ - "X = np.empty(1100)\n", - "X[:1024] = np.linspace(-1., 1., 1024)\n", - "X[1024:] = X[1023] + X[1:77] - X[0]\n", - "SN = np.sin(X)\n", - "CN = np.cos(X)\n", - "\n", - "@hope.jit\n", - "def approx(x, sn, cn, X, SN, CN):\n", - " for i in range(100):\n", - " sn[i] = np.interp(x[i], X, SN)\n", - " cn[i] = np.interp(x[i], X, CN)\n", - "\n", - "@hope.jit\n", - "def approx_opt(x, sn, cn, X, SN, CN):\n", - " for i in range(100):\n", - " f = (x[i] - X[0]) / (X[1024] - X[0]) * 1024.\n", - " g = np.floor(f)\n", - " j = np.int_(g)\n", - " a = f - g\n", - " sn[i] = (1 - a) * SN[j] + a * SN[j + 1]\n", - " cn[i] = (1 - a) * CN[j] + a * CN[j + 1]\n", - "\n", - "x = 2. * random_sample(100) - 1\n", - "sn = np.empty_like(x)\n", - "cn = np.empty_like(x)\n", - "\n", - "%timeit approx(x, sn, cn, X, SN, CN)\n", - "%timeit approx_opt(x, sn, cn, X, SN, CN)" - ], - "language": "python", - "metadata": {}, - "outputs": [ - { - "output_type": "stream", - "stream": "stdout", - "text": [ - "1 loops, best of 3: 10 \u00b5s per loop\n" - ] - }, - { - "output_type": "stream", - "stream": "stdout", - "text": [ - "1 loops, best of 3: 954 ns per loop\n" - ] - } - ], - "prompt_number": 10 + "name": "stdout", + "output_type": "stream", + "text": [ + "1 loops, best of 3: 10 µs per loop\n", + "1 loops, best of 3: 954 ns per loop\n" + ] } ], - "metadata": {} + "source": [ + "X = np.empty(1100)\n", + "X[:1024] = np.linspace(-1., 1., 1024)\n", + "X[1024:] = X[1023] + X[1:77] - X[0]\n", + "SN = np.sin(X)\n", + "CN = np.cos(X)\n", + "\n", + "@hope.jit\n", + "def approx(x, sn, cn, X, SN, CN):\n", + " for i in range(100):\n", + " sn[i] = np.interp(x[i], X, SN)\n", + " cn[i] = np.interp(x[i], X, CN)\n", + "\n", + "@hope.jit\n", + "def approx_opt(x, sn, cn, X, SN, CN):\n", + " for i in range(100):\n", + " f = (x[i] - X[0]) / (X[1024] - X[0]) * 1024.\n", + " g = np.floor(f)\n", + " j = np.int_(g)\n", + " a = f - g\n", + " sn[i] = (1 - a) * SN[j] + a * SN[j + 1]\n", + " cn[i] = (1 - a) * CN[j] + a * CN[j + 1]\n", + "\n", + "x = 2. * random_sample(100) - 1\n", + "sn = np.empty_like(x)\n", + "cn = np.empty_like(x)\n", + "\n", + "%timeit approx(x, sn, cn, X, SN, CN)\n", + "%timeit approx_opt(x, sn, cn, X, SN, CN)" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.5.1" } - ] -} \ No newline at end of file + }, + "nbformat": 4, + "nbformat_minor": 1 +} diff --git a/benchmarks/fibonacci.ipynb b/benchmarks/fibonacci.ipynb index 9a09e74..1b066cf 100644 --- a/benchmarks/fibonacci.ipynb +++ b/benchmarks/fibonacci.ipynb @@ -1,208 +1,182 @@ { - "metadata": { - "name": "", - "signature": "sha256:c2e8018cf3530fa66b1f12e3044c71d39d6f7c1954acb15ff475eefbef500867" - }, - "nbformat": 3, - "nbformat_minor": 0, - "worksheets": [ + "cells": [ + { + "cell_type": "code", + "execution_count": 1, + "metadata": {}, + "outputs": [], + "source": [ + "from util import perf_comp_data" + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "metadata": {}, + "outputs": [], + "source": [ + "def fib(n):\n", + " if n < 2:\n", + " return n\n", + " return fib(n - 1) + fib(n - 2)\n", + "\n", + "assert fib(20) == 6765" + ] + }, { - "cells": [ + "cell_type": "code", + "execution_count": 3, + "metadata": {}, + "outputs": [], + "source": [ + "from hope import jit\n", + "\n", + "hope_fib = jit(fib)\n", + "\n", + "assert hope_fib(20) == 6765" + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "metadata": {}, + "outputs": [], + "source": [ + "from numba import jit\n", + "\n", + "numba_fib = jit(fib)\n", + "assert numba_fib(20) == 6765" + ] + }, + { + "cell_type": "code", + "execution_count": 5, + "metadata": {}, + "outputs": [ { - "cell_type": "code", - "collapsed": false, - "input": [ - "from util import perf_comp_data" - ], - "language": "python", - "metadata": {}, - "outputs": [], - "prompt_number": 1 - }, + "name": "stdout", + "output_type": "stream", + "text": [ + "python\n", + "100 loops, best of 3: 2.59 ms per loop\n", + "hope 1\n", + "10000 loops, best of 3: 42 µs per loop\n", + "numba\n", + "100 loops, best of 3: 2.67 ms per loop\n" + ] + } + ], + "source": [ + "print \"python\"\n", + "%timeit fib(20)\n", + "print \"hope 1\"\n", + "%timeit hope_fib(20)\n", + "print \"numba\"\n", + "%timeit numba_fib(20)" + ] + }, + { + "cell_type": "code", + "execution_count": 6, + "metadata": {}, + "outputs": [ { - "cell_type": "code", - "collapsed": false, - "input": [ + "name": "stdout", + "output_type": "stream", + "text": [ + "function: hope_fib , av. time sec: 0.00005205, relative: 1.0\n", + "function: fib , av. time sec: 0.00290926, relative: 55.9\n", + "function: numba_fib , av. time sec: 0.00306726, relative: 58.9\n" + ] + } + ], + "source": [ + "n=20\n", + "perf_comp_data([\"fib\", \"hope_fib\", \"numba_fib\"], 3*[\"n\"])" + ] + }, + { + "cell_type": "code", + "execution_count": 7, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "fib (int64,)\n", + "--------------------------------------------------------------------------------\n", + "# File: \n", + "# --- LINE 1 --- \n", + "\n", "def fib(n):\n", + "\n", + " # --- LINE 2 --- \n", + " # label 0\n", + " # n.1 = n :: pyobject\n", + " # $const0.2 = const(, 2) :: pyobject\n", + " # $0.3 = n.1 < $const0.2 :: pyobject\n", + " # branch $0.3, 12, 16\n", + "\n", " if n < 2:\n", + "\n", + " # --- LINE 3 --- \n", + " # label 12\n", + " # return n.1\n", + "\n", " return n\n", - " return fib(n - 1) + fib(n - 2)\n", "\n", - "assert fib(20) == 6765" - ], - "language": "python", - "metadata": {}, - "outputs": [], - "prompt_number": 2 - }, - { - "cell_type": "code", - "collapsed": false, - "input": [ - "from hope import jit\n", + " # --- LINE 4 --- \n", + " # label 16\n", + " # $16.1 = global(fib: ) :: pyobject\n", + " # $const16.3 = const(, 1) :: pyobject\n", + " # $16.4 = n.1 - $const16.3 :: pyobject\n", + " # $16.5 = call $16.1($16.4, ) :: pyobject\n", + " # $16.6 = global(fib: ) :: pyobject\n", + " # $const16.8 = const(, 2) :: pyobject\n", + " # $16.9 = n.1 - $const16.8 :: pyobject\n", + " # $16.10 = call $16.6($16.9, ) :: pyobject\n", + " # $16.11 = $16.5 + $16.10 :: pyobject\n", + " # return $16.11\n", "\n", - "hope_fib = jit(fib)\n", + " return fib(n - 1) + fib(n - 2)\n", "\n", - "assert hope_fib(20) == 6765" - ], - "language": "python", - "metadata": {}, - "outputs": [], - "prompt_number": 3 - }, - { - "cell_type": "code", - "collapsed": false, - "input": [ - "from numba import jit\n", "\n", - "numba_fib = jit(fib)\n", - "assert numba_fib(20) == 6765" - ], - "language": "python", - "metadata": {}, - "outputs": [], - "prompt_number": 4 - }, - { - "cell_type": "code", - "collapsed": false, - "input": [ - "print \"python\"\n", - "%timeit fib(20)\n", - "print \"hope 1\"\n", - "%timeit hope_fib(20)\n", - "print \"numba\"\n", - "%timeit numba_fib(20)" - ], - "language": "python", - "metadata": {}, - "outputs": [ - { - "output_type": "stream", - "stream": "stdout", - "text": [ - "python\n", - "100 loops, best of 3: 2.59 ms per loop" - ] - }, - { - "output_type": "stream", - "stream": "stdout", - "text": [ - "\n", - "hope 1\n", - "10000 loops, best of 3: 42 \u00b5s per loop" - ] - }, - { - "output_type": "stream", - "stream": "stdout", - "text": [ - "\n", - "numba\n", - "100 loops, best of 3: 2.67 ms per loop" - ] - }, - { - "output_type": "stream", - "stream": "stdout", - "text": [ - "\n" - ] - } - ], - "prompt_number": 5 - }, - { - "cell_type": "code", - "collapsed": false, - "input": [ - "n=20\n", - "perf_comp_data([\"fib\", \"hope_fib\", \"numba_fib\"], 3*[\"n\"])" - ], - "language": "python", - "metadata": {}, - "outputs": [ - { - "output_type": "stream", - "stream": "stdout", - "text": [ - "function: hope_fib , av. time sec: 0.00005205, relative: 1.0\n", - "function: fib , av. time sec: 0.00290926, relative: 55.9\n", - "function: numba_fib , av. time sec: 0.00306726, relative: 58.9\n" - ] - } - ], - "prompt_number": 6 - }, - { - "cell_type": "code", - "collapsed": false, - "input": [ - "numba_fib.inspect_types()" - ], - "language": "python", - "metadata": {}, - "outputs": [ - { - "output_type": "stream", - "stream": "stdout", - "text": [ - "fib (int64,)\n", - "--------------------------------------------------------------------------------\n", - "# File: \n", - "# --- LINE 1 --- \n", - "\n", - "def fib(n):\n", - "\n", - " # --- LINE 2 --- \n", - " # label 0\n", - " # n.1 = n :: pyobject\n", - " # $const0.2 = const(, 2) :: pyobject\n", - " # $0.3 = n.1 < $const0.2 :: pyobject\n", - " # branch $0.3, 12, 16\n", - "\n", - " if n < 2:\n", - "\n", - " # --- LINE 3 --- \n", - " # label 12\n", - " # return n.1\n", - "\n", - " return n\n", - "\n", - " # --- LINE 4 --- \n", - " # label 16\n", - " # $16.1 = global(fib: ) :: pyobject\n", - " # $const16.3 = const(, 1) :: pyobject\n", - " # $16.4 = n.1 - $const16.3 :: pyobject\n", - " # $16.5 = call $16.1($16.4, ) :: pyobject\n", - " # $16.6 = global(fib: ) :: pyobject\n", - " # $const16.8 = const(, 2) :: pyobject\n", - " # $16.9 = n.1 - $const16.8 :: pyobject\n", - " # $16.10 = call $16.6($16.9, ) :: pyobject\n", - " # $16.11 = $16.5 + $16.10 :: pyobject\n", - " # return $16.11\n", - "\n", - " return fib(n - 1) + fib(n - 2)\n", - "\n", - "\n", - "================================================================================\n" - ] - } - ], - "prompt_number": 7 - }, - { - "cell_type": "code", - "collapsed": false, - "input": [], - "language": "python", - "metadata": {}, - "outputs": [] + "================================================================================\n" + ] } ], - "metadata": {} + "source": [ + "numba_fib.inspect_types()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [] } - ] -} \ No newline at end of file + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.5.1" + } + }, + "nbformat": 4, + "nbformat_minor": 1 +} diff --git a/benchmarks/julialang.org.ipynb b/benchmarks/julialang.org.ipynb index 7459d05..9a75334 100644 --- a/benchmarks/julialang.org.ipynb +++ b/benchmarks/julialang.org.ipynb @@ -1,813 +1,653 @@ { - "metadata": { - "name": "", - "signature": "sha256:2dc0f66f43617ca29e3f9e2e223da552ebd0f852686cd4d0a4f32aa41ec4f354" - }, - "nbformat": 3, - "nbformat_minor": 0, - "worksheets": [ + "cells": [ { - "cells": [ - { - "cell_type": "code", - "collapsed": false, - "input": [ - "import hope\n", - "hope.config.optimize = True\n", - "hope.config.verbose = True\n", - "hope.config.keeptemp = True\n", - "import numba\n", - "import numpy as np\n", - "from util import perf_comp_data\n", - "from native_util import load\n", - "%load_ext cythonmagic\n", - "%load_ext version_information\n", - "%version_information numpy, Cython, numba, hope" - ], - "language": "python", - "metadata": {}, - "outputs": [ - { - "html": [ - "
SoftwareVersion
Python2.7.8 64bit [GCC 4.2.1 Compatible Apple LLVM 5.1 (clang-503.0.40)]
IPython1.1.0
OSDarwin 13.4.0 x86_64 i386 64bit
numpy1.8.1
Cython0.20.2
numbatag: 0.13.3
hope0.3.0
Thu Dec 04 11:18:15 2014 CET
" - ], - "json": [ - "{\"Software versions\": [{\"version\": \"2.7.8 64bit [GCC 4.2.1 Compatible Apple LLVM 5.1 (clang-503.0.40)]\", \"module\": \"Python\"}, {\"version\": \"1.1.0\", \"module\": \"IPython\"}, {\"version\": \"Darwin 13.4.0 x86_64 i386 64bit\", \"module\": \"OS\"}, {\"version\": \"1.8.1\", \"module\": \"numpy\"}, {\"version\": \"0.20.2\", \"module\": \"Cython\"}, {\"version\": \"tag: 0.13.3\", \"module\": \"numba\"}, {\"version\": \"0.3.0\", \"module\": \"hope\"}]}" - ], - "latex": [ - "\\begin{tabular}{|l|l|}\\hline\n", - "{\\bf Software} & {\\bf Version} \\\\ \\hline\\hline\n", - "Python & 2.7.8 64bit [GCC 4.2.1 Compatible Apple LLVM 5.1 (clang-503.0.40)] \\\\ \\hline\n", - "IPython & 1.1.0 \\\\ \\hline\n", - "OS & Darwin 13.4.0 x86\\_64 i386 64bit \\\\ \\hline\n", - "numpy & 1.8.1 \\\\ \\hline\n", - "Cython & 0.20.2 \\\\ \\hline\n", - "numba & tag: 0.13.3 \\\\ \\hline\n", - "hope & 0.3.0 \\\\ \\hline\n", - "\\hline \\multicolumn{2}{|l|}{Thu Dec 04 11:18:15 2014 CET} \\\\ \\hline\n", - "\\end{tabular}\n" - ], - "metadata": {}, - "output_type": "pyout", - "prompt_number": 1, - "text": [ - "Software versions\n", - "Python 2.7.8 64bit [GCC 4.2.1 Compatible Apple LLVM 5.1 (clang-503.0.40)]\n", - "IPython 1.1.0\n", - "OS Darwin 13.4.0 x86_64 i386 64bit\n", - "numpy 1.8.1\n", - "Cython 0.20.2\n", - "numba tag: 0.13.3\n", - "hope 0.3.0\n", - "Thu Dec 04 11:18:15 2014 CET" - ] - } - ], - "prompt_number": 1 - }, + "cell_type": "code", + "execution_count": 1, + "metadata": {}, + "outputs": [ { - "cell_type": "heading", - "level": 1, - "metadata": {}, - "source": [ - "fibonacci" - ] - }, - { - "cell_type": "code", - "collapsed": false, - "input": [ - "def fib(n):\n", - " if n<2:\n", - " return n\n", - " return fib(n-1)+fib(n-2)\n", - "hope_fib = hope.jit(fib)\n", - "numba_fib = numba.jit(fib, nopython=False)\n", - "\n", - "native_fib_mod = load(\"fib\")\n", - "native_fib = native_fib_mod.run \n", - "\n", - "n=20\n", - "assert fib(20) == 6765\n", - "assert hope_fib(20) == 6765\n", - "assert numba_fib(20) == 6765\n", - "assert native_fib(20) == 6765" - ], - "language": "python", - "metadata": {}, - "outputs": [ - { - "output_type": "stream", - "stream": "stdout", - "text": [ - "running build_ext\n", - "building 'fib' extension\n", - "C compiler: /usr/bin/clang -fno-strict-aliasing -fno-common -dynamic -pipe -Os -fwrapv -DNDEBUG -g -fwrapv -O3 -Wall -Wstrict-prototypes\n", - "\n", - "compile options: '-I/Users/jakeret/workspace/virtualenvs/hope_benchmarks/lib/python2.7/site-packages/numpy/core/include -I/opt/local/Library/Frameworks/Python.framework/Versions/2.7/include/python2.7 -c'\n", - "extra options: '-Wall -Wno-unused-variable -march=native -stdlib=libc++ -std=c++11'\n", - "clang: ././src/fib.cpp\n", - "/usr/bin/clang++ -bundle -undefined dynamic_lookup -L/opt/local/lib -Wl,-headerpad_max_install_names -L/opt/local/lib/db48 ./src/fib.o -o ./fib.so\n", - "\n", - "fib(int64 n)\n", - "\tif (n.J < 2.J) {\n", - "\t\treturn n.J\n", - "\t}\n", - "\treturn (fib((n.J - 1.J)) + fib((n.J - 2.J)))\n", - "\n", - "Compiling following functions:" - ] - }, - { - "output_type": "stream", - "stream": "stdout", - "text": [ - "\n", - "fib(int64 n)\n" - ] - }, - { - "output_type": "stream", - "stream": "stdout", - "text": [ - "running build_ext\n", - "building 'fib_181039868ed022600dc3388e38604df736dff187257d5d6ae4c4d3ea_0' extension\n", - "C compiler: /usr/bin/clang -fno-strict-aliasing -fno-common -dynamic -pipe -Os -fwrapv -DNDEBUG -g -fwrapv -O3 -Wall -Wstrict-prototypes\n", - "\n", - "compile options: '-I/Users/jakeret/workspace/virtualenvs/hope_benchmarks/lib/python2.7/site-packages/numpy/core/include -I/opt/local/Library/Frameworks/Python.framework/Versions/2.7/include/python2.7 -c'\n", - "extra options: '-Wall -Wno-unused-variable -march=native -stdlib=libc++ -std=c++11'\n", - "clang: /var/folders/k_/ttz9cd4d3kj2kbh3lvtn6tkm0000gn/T/hopeDXfz9_/fib_181039868ed022600dc3388e38604df736dff187257d5d6ae4c4d3ea_0.cpp\n", - "/usr/bin/clang++ -bundle -undefined dynamic_lookup -L/opt/local/lib -Wl,-headerpad_max_install_names -L/opt/local/lib/db48 /var/folders/k_/ttz9cd4d3kj2kbh3lvtn6tkm0000gn/T/hopeDXfz9_/fib_181039868ed022600dc3388e38604df736dff187257d5d6ae4c4d3ea_0.o -o /var/folders/k_/ttz9cd4d3kj2kbh3lvtn6tkm0000gn/T/hopeDXfz9_/fib_181039868ed022600dc3388e38604df736dff187257d5d6ae4c4d3ea_0.so\n", - "\n" - ] - } - ], - "prompt_number": 2 - }, - { - "cell_type": "code", - "collapsed": false, - "input": [ - "%%cython\n", - "\n", - "cimport cython\n", - "\n", - "@cython.boundscheck(False)\n", - "@cython.wraparound(False)\n", - "cpdef int cython_fib(int n):\n", - " if n<2:\n", - " return n\n", - " return cython_fib(n-1)+cython_fib(n-2)\n", - "\n", - "assert cython_fib(20) == 6765\n" - ], - "language": "python", - "metadata": {}, - "outputs": [ - { - "output_type": "stream", - "stream": "stdout", - "text": [ - "building '_cython_magic_d4ac66e2963d3320a31dcc65e2809bf2' extension\n" + "data": { + "application/json": { + "Software versions": [ + { + "module": "Python", + "version": "2.7.8 64bit [GCC 4.2.1 Compatible Apple LLVM 5.1 (clang-503.0.40)]" + }, + { + "module": "IPython", + "version": "1.1.0" + }, + { + "module": "OS", + "version": "Darwin 13.4.0 x86_64 i386 64bit" + }, + { + "module": "numpy", + "version": "1.8.1" + }, + { + "module": "Cython", + "version": "0.20.2" + }, + { + "module": "numba", + "version": "tag: 0.13.3" + }, + { + "module": "hope", + "version": "0.3.0" + } ] }, - { - "output_type": "stream", - "stream": "stdout", - "text": [ - "C compiler: /usr/bin/clang -fno-strict-aliasing -fno-common -dynamic -pipe -Os -fwrapv -DNDEBUG -g -fwrapv -O3 -Wall -Wstrict-prototypes\n", - "\n" - ] - }, - { - "output_type": "stream", - "stream": "stdout", - "text": [ - "compile options: '-I/opt/local/Library/Frameworks/Python.framework/Versions/2.7/include/python2.7 -c'\n" - ] - }, - { - "output_type": "stream", - "stream": "stdout", - "text": [ - "clang: /Users/jakeret/.ipython/cython/_cython_magic_d4ac66e2963d3320a31dcc65e2809bf2.c\n" - ] - }, - { - "output_type": "stream", - "stream": "stdout", - "text": [ - "/usr/bin/clang -bundle -undefined dynamic_lookup -L/opt/local/lib -Wl,-headerpad_max_install_names -L/opt/local/lib/db48 /Users/jakeret/.ipython/cython/Users/jakeret/.ipython/cython/_cython_magic_d4ac66e2963d3320a31dcc65e2809bf2.o -o /Users/jakeret/.ipython/cython/_cython_magic_d4ac66e2963d3320a31dcc65e2809bf2.so\n" - ] - } - ], - "prompt_number": 3 - }, - { - "cell_type": "code", - "collapsed": false, - "input": [ - "%timeit fib(20)\n", - "%timeit hope_fib(20)\n", - "%timeit numba_fib(20)\n", - "%timeit cython_fib(20)\n", - "%timeit native_fib(20)" - ], - "language": "python", - "metadata": {}, - "outputs": [ - { - "output_type": "stream", - "stream": "stdout", - "text": [ - "100 loops, best of 3: 2.59 ms per loop\n", - "10000 loops, best of 3: 40.6 \u00b5s per loop" - ] - }, - { - "output_type": "stream", - "stream": "stdout", - "text": [ - "\n", - "100 loops, best of 3: 2.55 ms per loop" - ] - }, - { - "output_type": "stream", - "stream": "stdout", - "text": [ - "\n", - "10000 loops, best of 3: 42.5 \u00b5s per loop" - ] - }, - { - "output_type": "stream", - "stream": "stdout", - "text": [ - "\n", - "10000 loops, best of 3: 41.4 \u00b5s per loop" - ] - }, - { - "output_type": "stream", - "stream": "stdout", - "text": [ - "\n" - ] - } - ], - "prompt_number": 4 - }, - { - "cell_type": "code", - "collapsed": false, - "input": [ - "perf_comp_data([\"fib\", \"hope_fib\", \"numba_fib\", \"cython_fib\", \"native_fib\"],\n", - " 5*[\"n\"])" - ], - "language": "python", - "metadata": {}, - "outputs": [ - { - "output_type": "stream", - "stream": "stdout", - "text": [ - "function: native_fib , av. time sec: 0.00003982, min. time sec: 0.00003886, relative: 1.0\n", - "function: hope_fib , av. time sec: 0.00004315, min. time sec: 0.00003815, relative: 1.1\n", - "function: cython_fib , av. time sec: 0.00004911, min. time sec: 0.00004101, relative: 1.2\n", - "function: numba_fib , av. time sec: 0.00260401, min. time sec: 0.00255513, relative: 65.4\n", - "function: fib , av. time sec: 0.00291204, min. time sec: 0.00247192, relative: 73.1\n" - ] - } - ], - "prompt_number": 7 - }, - { - "cell_type": "heading", - "level": 1, + "text/html": [ + "
SoftwareVersion
Python2.7.8 64bit [GCC 4.2.1 Compatible Apple LLVM 5.1 (clang-503.0.40)]
IPython1.1.0
OSDarwin 13.4.0 x86_64 i386 64bit
numpy1.8.1
Cython0.20.2
numbatag: 0.13.3
hope0.3.0
Thu Dec 04 11:18:15 2014 CET
" + ], + "text/latex": [ + "\\begin{tabular}{|l|l|}\\hline\n", + "{\\bf Software} & {\\bf Version} \\\\ \\hline\\hline\n", + "Python & 2.7.8 64bit [GCC 4.2.1 Compatible Apple LLVM 5.1 (clang-503.0.40)] \\\\ \\hline\n", + "IPython & 1.1.0 \\\\ \\hline\n", + "OS & Darwin 13.4.0 x86\\_64 i386 64bit \\\\ \\hline\n", + "numpy & 1.8.1 \\\\ \\hline\n", + "Cython & 0.20.2 \\\\ \\hline\n", + "numba & tag: 0.13.3 \\\\ \\hline\n", + "hope & 0.3.0 \\\\ \\hline\n", + "\\hline \\multicolumn{2}{|l|}{Thu Dec 04 11:18:15 2014 CET} \\\\ \\hline\n", + "\\end{tabular}\n" + ], + "text/plain": [ + "Software versions\n", + "Python 2.7.8 64bit [GCC 4.2.1 Compatible Apple LLVM 5.1 (clang-503.0.40)]\n", + "IPython 1.1.0\n", + "OS Darwin 13.4.0 x86_64 i386 64bit\n", + "numpy 1.8.1\n", + "Cython 0.20.2\n", + "numba tag: 0.13.3\n", + "hope 0.3.0\n", + "Thu Dec 04 11:18:15 2014 CET" + ] + }, + "execution_count": 1, "metadata": {}, - "source": [ - "quicksort" - ] - }, + "output_type": "execute_result" + } + ], + "source": [ + "import hope\n", + "hope.config.optimize = True\n", + "hope.config.verbose = True\n", + "hope.config.keeptemp = True\n", + "import numba\n", + "import numpy as np\n", + "from util import perf_comp_data\n", + "from native_util import load\n", + "%load_ext cythonmagic\n", + "%load_ext version_information\n", + "%version_information numpy, Cython, numba, hope" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# fibonacci" + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "metadata": {}, + "outputs": [ { - "cell_type": "code", - "collapsed": false, - "input": [ - "def qsort_kernel(a, lo, hi):\n", - " i = lo\n", - " j = hi\n", - " if False: return a\n", - " while i < hi:\n", - " pivot = a[(lo+hi) // 2]\n", - " while i <= j:\n", - " while a[i] < pivot:\n", - " i += 1\n", - " while a[j] > pivot:\n", - " j -= 1\n", - " if i <= j:\n", - " tmp = a[i]\n", - " a[i] = a[j]\n", - " a[j] = tmp\n", - " i += 1\n", - " j -= 1\n", - " if lo < j:\n", - " qsort_kernel(a, lo, j)\n", - " lo = i\n", - " j = hi\n", - " return a\n", + "name": "stdout", + "output_type": "stream", + "text": [ + "running build_ext\n", + "building 'fib' extension\n", + "C compiler: /usr/bin/clang -fno-strict-aliasing -fno-common -dynamic -pipe -Os -fwrapv -DNDEBUG -g -fwrapv -O3 -Wall -Wstrict-prototypes\n", "\n", - "hope_qsort_kernel = hope.jit(qsort_kernel)\n", - "numba_qsort_kernel = numba.jit(qsort_kernel)\n", + "compile options: '-I/Users/jakeret/workspace/virtualenvs/hope_benchmarks/lib/python2.7/site-packages/numpy/core/include -I/opt/local/Library/Frameworks/Python.framework/Versions/2.7/include/python2.7 -c'\n", + "extra options: '-Wall -Wno-unused-variable -march=native -stdlib=libc++ -std=c++11'\n", + "clang: ././src/fib.cpp\n", + "/usr/bin/clang++ -bundle -undefined dynamic_lookup -L/opt/local/lib -Wl,-headerpad_max_install_names -L/opt/local/lib/db48 ./src/fib.o -o ./fib.so\n", "\n", - "native_qsort_kernel_mod = load(\"qsort_kernel\")\n", - "native_qsort_kernel = native_qsort_kernel_mod.run\n" - ], - "language": "python", - "metadata": {}, - "outputs": [ - { - "output_type": "stream", - "stream": "stdout", - "text": [ - "running build_ext\n", - "building 'qsort_kernel' extension\n", - "C compiler: /usr/bin/clang -fno-strict-aliasing -fno-common -dynamic -pipe -Os -fwrapv -DNDEBUG -g -fwrapv -O3 -Wall -Wstrict-prototypes\n", - "\n", - "compile options: '-I/Users/jakeret/Library/Python/2.7/lib/python/site-packages/numpy/core/include -I/opt/local/Library/Frameworks/Python.framework/Versions/2.7/include/python2.7 -c'\n", - "extra options: '-Wall -Wno-unused-variable -march=native -stdlib=libc++ -std=c++11'\n", - "clang: ././src/qsort_kernel.cpp\n", - "/usr/bin/clang++ -bundle -undefined dynamic_lookup -L/opt/local/lib -Wl,-headerpad_max_install_names -L/opt/local/lib/db46 ./src/qsort_kernel.o -o ./qsort_kernel.so\n", - "\n" - ] - } - ], - "prompt_number": 8 - }, - { - "cell_type": "code", - "collapsed": false, - "input": [ - "def numpy_qsort_kernel(a, lo, hi):\n", - " np.sort(a)" - ], - "language": "python", - "metadata": {}, - "outputs": [], - "prompt_number": 16 - }, - { - "cell_type": "code", - "collapsed": false, - "input": [ - "%%cython\n", + "fib(int64 n)\n", + "\tif (n.J < 2.J) {\n", + "\t\treturn n.J\n", + "\t}\n", + "\treturn (fib((n.J - 1.J)) + fib((n.J - 2.J)))\n", "\n", - "cimport cython\n", - "import numpy as np\n", - "cimport numpy as np\n", + "Compiling following functions:\n", + "fib(int64 n)\n", + "running build_ext\n", + "building 'fib_181039868ed022600dc3388e38604df736dff187257d5d6ae4c4d3ea_0' extension\n", + "C compiler: /usr/bin/clang -fno-strict-aliasing -fno-common -dynamic -pipe -Os -fwrapv -DNDEBUG -g -fwrapv -O3 -Wall -Wstrict-prototypes\n", "\n", - "@cython.boundscheck(False)\n", - "@cython.wraparound(False)\n", - "def cython_qsort_kernel(np.ndarray[np.double_t, ndim=1] a, int lo, int hi):\n", - " cdef int i = lo\n", - " cdef int j = hi\n", - " cdef double pivot = 0\n", - " cdef double tmp = 0.0\n", - " if False: return a\n", - " while i < hi:\n", - " pivot = a[(lo+hi) // 2]\n", - " while i <= j:\n", - " while a[i] < pivot:\n", - " i += 1\n", - " while a[j] > pivot:\n", - " j -= 1\n", - " if i <= j:\n", - " tmp = a[i]\n", - " a[i] = a[j]\n", - " a[j] = tmp\n", - " i += 1\n", - " j -= 1\n", - " if lo < j:\n", - " cython_qsort_kernel(a, lo, j)\n", - " lo = i\n", - " j = hi\n", - " return a\n", + "compile options: '-I/Users/jakeret/workspace/virtualenvs/hope_benchmarks/lib/python2.7/site-packages/numpy/core/include -I/opt/local/Library/Frameworks/Python.framework/Versions/2.7/include/python2.7 -c'\n", + "extra options: '-Wall -Wno-unused-variable -march=native -stdlib=libc++ -std=c++11'\n", + "clang: /var/folders/k_/ttz9cd4d3kj2kbh3lvtn6tkm0000gn/T/hopeDXfz9_/fib_181039868ed022600dc3388e38604df736dff187257d5d6ae4c4d3ea_0.cpp\n", + "/usr/bin/clang++ -bundle -undefined dynamic_lookup -L/opt/local/lib -Wl,-headerpad_max_install_names -L/opt/local/lib/db48 /var/folders/k_/ttz9cd4d3kj2kbh3lvtn6tkm0000gn/T/hopeDXfz9_/fib_181039868ed022600dc3388e38604df736dff187257d5d6ae4c4d3ea_0.o -o /var/folders/k_/ttz9cd4d3kj2kbh3lvtn6tkm0000gn/T/hopeDXfz9_/fib_181039868ed022600dc3388e38604df736dff187257d5d6ae4c4d3ea_0.so\n", "\n" - ], - "language": "python", - "metadata": {}, - "outputs": [], - "prompt_number": 17 - }, - { - "cell_type": "code", - "collapsed": false, - "input": [ - "lst = np.random.random(5000)\n" - ], - "language": "python", - "metadata": {}, - "outputs": [], - "prompt_number": 18 - }, - { - "cell_type": "code", - "collapsed": false, - "input": [ - "psorted = qsort_kernel(lst.copy(), 0, len(lst)-1)\n", - "hsorted = hope_qsort_kernel(lst.copy(), 0, len(lst)-1)\n", - "#nsorted = numba_qsort_kernel(lst.copy(), 0, len(lst)-1)\n", - "csorted = cython_qsort_kernel(lst.copy(), 0, len(lst)-1)\n", - "nasorted = native_qsort_kernel(lst.copy(), 0, len(lst)-1)\n" - ], - "language": "python", - "metadata": {}, - "outputs": [], - "prompt_number": 19 - }, + ] + } + ], + "source": [ + "def fib(n):\n", + " if n<2:\n", + " return n\n", + " return fib(n-1)+fib(n-2)\n", + "hope_fib = hope.jit(fib)\n", + "numba_fib = numba.jit(fib, nopython=False)\n", + "\n", + "native_fib_mod = load(\"fib\")\n", + "native_fib = native_fib_mod.run \n", + "\n", + "n=20\n", + "assert fib(20) == 6765\n", + "assert hope_fib(20) == 6765\n", + "assert numba_fib(20) == 6765\n", + "assert native_fib(20) == 6765" + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "metadata": {}, + "outputs": [ { - "cell_type": "code", - "collapsed": false, - "input": [ - "assert np.all(psorted[:-1] <= psorted[1:])\n", - "#assert np.all(hope_qsort_kernel[:-1] <= hope_qsort_kernel[1:])\n", - "#assert np.all(numba_qsort_kernel[:-1] <= numba_qsort_kernel[1:])\n", - "#assert np.all(cython_qsort_kernel[:-1] <= cython_qsort_kernel[1:])\n", + "name": "stdout", + "output_type": "stream", + "text": [ + "building '_cython_magic_d4ac66e2963d3320a31dcc65e2809bf2' extension\n", + "C compiler: /usr/bin/clang -fno-strict-aliasing -fno-common -dynamic -pipe -Os -fwrapv -DNDEBUG -g -fwrapv -O3 -Wall -Wstrict-prototypes\n", "\n", - "%timeit qsort_kernel(lst.copy(), 0, len(lst)-1)\n", - "%timeit hope_qsort_kernel(lst.copy(), 0, len(lst)-1)\n", - "#%timeit numba_qsort_kernel(lst.copy(), 0, len(lst)-1)\n", - "%timeit cython_qsort_kernel(lst.copy(), 0, len(lst)-1)\n", - "%timeit native_qsort_kernel(lst.copy(), 0, len(lst)-1)\n", - "%timeit np.sort(lst.copy())" - ], - "language": "python", - "metadata": {}, - "outputs": [ - { - "output_type": "stream", - "stream": "stdout", - "text": [ - "10 loops, best of 3: 22.4 ms per loop\n", - "1000 loops, best of 3: 334 \u00b5s per loop" - ] - }, - { - "output_type": "stream", - "stream": "stdout", - "text": [ - "\n", - "1000 loops, best of 3: 1.4 ms per loop" - ] - }, - { - "output_type": "stream", - "stream": "stdout", - "text": [ - "\n", - "1000 loops, best of 3: 303 \u00b5s per loop" - ] - }, - { - "output_type": "stream", - "stream": "stdout", - "text": [ - "\n", - "1000 loops, best of 3: 248 \u00b5s per loop" - ] - }, - { - "output_type": "stream", - "stream": "stdout", - "text": [ - "\n" - ] - } - ], - "prompt_number": 20 - }, + "compile options: '-I/opt/local/Library/Frameworks/Python.framework/Versions/2.7/include/python2.7 -c'\n", + "clang: /Users/jakeret/.ipython/cython/_cython_magic_d4ac66e2963d3320a31dcc65e2809bf2.c\n", + "/usr/bin/clang -bundle -undefined dynamic_lookup -L/opt/local/lib -Wl,-headerpad_max_install_names -L/opt/local/lib/db48 /Users/jakeret/.ipython/cython/Users/jakeret/.ipython/cython/_cython_magic_d4ac66e2963d3320a31dcc65e2809bf2.o -o /Users/jakeret/.ipython/cython/_cython_magic_d4ac66e2963d3320a31dcc65e2809bf2.so\n" + ] + } + ], + "source": [ + "%%cython\n", + "\n", + "cimport cython\n", + "\n", + "@cython.boundscheck(False)\n", + "@cython.wraparound(False)\n", + "cpdef int cython_fib(int n):\n", + " if n<2:\n", + " return n\n", + " return cython_fib(n-1)+cython_fib(n-2)\n", + "\n", + "assert cython_fib(20) == 6765\n" + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "metadata": {}, + "outputs": [ { - "cell_type": "code", - "collapsed": false, - "input": [ - "a = lst.copy()\n", - "\n", - "lo = 0\n", - "hi = len(lst)-1\n", - "\n", - "perf_comp_data([\"hope_qsort_kernel\", \n", - " \"qsort_kernel\", \n", - " #\"numpy_qsort_kernel\", \n", - " \"cython_qsort_kernel\", \n", - " \"native_qsort_kernel\"],\n", - " 5*[\"a, lo, hi\"], rep=100, extra_setup=\"from __main__ import lst;a = lst.copy()\")" - ], - "language": "python", - "metadata": {}, - "outputs": [ - { - "output_type": "stream", - "stream": "stdout", - "text": [ - "function: native_qsort_kernel , av. time sec: 0.00029302, min. time sec: 0.00029182, relative: 1.0\n", - "function: hope_qsort_kernel , av. time sec: 0.00033617, min. time sec: 0.00033379, relative: 1.1\n", - "function: cython_qsort_kernel , av. time sec: 0.00143504, min. time sec: 0.00140619, relative: 4.9\n", - "function: qsort_kernel , av. time sec: 0.02197444, min. time sec: 0.02172208, relative: 75.0\n" - ] - } - ], - "prompt_number": 21 - }, + "name": "stdout", + "output_type": "stream", + "text": [ + "100 loops, best of 3: 2.59 ms per loop\n", + "10000 loops, best of 3: 40.6 µs per loop\n", + "100 loops, best of 3: 2.55 ms per loop\n", + "10000 loops, best of 3: 42.5 µs per loop\n", + "10000 loops, best of 3: 41.4 µs per loop\n" + ] + } + ], + "source": [ + "%timeit fib(20)\n", + "%timeit hope_fib(20)\n", + "%timeit numba_fib(20)\n", + "%timeit cython_fib(20)\n", + "%timeit native_fib(20)" + ] + }, + { + "cell_type": "code", + "execution_count": 7, + "metadata": {}, + "outputs": [ { - "cell_type": "heading", - "level": 1, - "metadata": {}, - "source": [ - "pi sum" + "name": "stdout", + "output_type": "stream", + "text": [ + "function: native_fib , av. time sec: 0.00003982, min. time sec: 0.00003886, relative: 1.0\n", + "function: hope_fib , av. time sec: 0.00004315, min. time sec: 0.00003815, relative: 1.1\n", + "function: cython_fib , av. time sec: 0.00004911, min. time sec: 0.00004101, relative: 1.2\n", + "function: numba_fib , av. time sec: 0.00260401, min. time sec: 0.00255513, relative: 65.4\n", + "function: fib , av. time sec: 0.00291204, min. time sec: 0.00247192, relative: 73.1\n" ] - }, + } + ], + "source": [ + "perf_comp_data([\"fib\", \"hope_fib\", \"numba_fib\", \"cython_fib\", \"native_fib\"],\n", + " 5*[\"n\"])" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# quicksort" + ] + }, + { + "cell_type": "code", + "execution_count": 8, + "metadata": {}, + "outputs": [ { - "cell_type": "code", - "collapsed": false, - "input": [ - "def pisum():\n", - " for j in range(1, 501):\n", - " sum = 0.0\n", - " f = 0.0\n", - " for k in range(1, 10001):\n", - " sum += 1.0/(k*k)\n", - " return sum\n", - "def pisum_opt():\n", - " for j in range(1, 501):\n", - " sum = 0.0\n", - " f = 0.0\n", - " for k in range(1, 10001):\n", - " f += 1.\n", - " sum += 1.0/(f*f)\n", - " return sum\n", - "\n", - "hope_pisum = hope.jit(pisum)\n", - "hope_pisum_opt = hope.jit(pisum_opt)\n", - "\n", - "numba_pisum = numba.jit(pisum, nopython=True)\n", - "numba_pisum_opt = numba.jit(pisum_opt, nopython=True)\n", - "\n", - "native_pisum_mod = load(\"pisum\")\n", - "native_pisum = native_pisum_mod.run\n", - "\n", - "native_pisum_opt_mod = load(\"pisum_opt\")\n", - "native_pisum_opt = native_pisum_opt_mod.run\n", + "name": "stdout", + "output_type": "stream", + "text": [ + "running build_ext\n", + "building 'qsort_kernel' extension\n", + "C compiler: /usr/bin/clang -fno-strict-aliasing -fno-common -dynamic -pipe -Os -fwrapv -DNDEBUG -g -fwrapv -O3 -Wall -Wstrict-prototypes\n", "\n", - "\n", - "assert abs(pisum()-1.644834071848065) < 1e-6\n", - "assert abs(hope_pisum()-1.644834071848065) < 1e-6\n", - "assert abs(hope_pisum_opt()-1.644834071848065) < 1e-6\n", - "assert abs(numba_pisum()-1.644834071848065) < 1e-6\n", - "assert abs(native_pisum()-1.644834071848065) < 1e-6\n", - "assert abs(native_pisum_opt()-1.644834071848065) < 1e-6\n" - ], - "language": "python", - "metadata": {}, - "outputs": [ - { - "output_type": "stream", - "stream": "stdout", - "text": [ - "running build_ext\n", - "building 'pisum' extension\n", - "C compiler: /usr/bin/clang -fno-strict-aliasing -fno-common -dynamic -pipe -Os -fwrapv -DNDEBUG -g -fwrapv -O3 -Wall -Wstrict-prototypes\n", - "\n", - "compile options: '-I/Users/jakeret/Library/Python/2.7/lib/python/site-packages/numpy/core/include -I/opt/local/Library/Frameworks/Python.framework/Versions/2.7/include/python2.7 -c'\n", - "extra options: '-Wall -Wno-unused-variable -march=native -stdlib=libc++ -std=c++11'\n", - "clang: ././src/pisum.cpp\n", - "/usr/bin/clang++ -bundle -undefined dynamic_lookup -L/opt/local/lib -Wl,-headerpad_max_install_names -L/opt/local/lib/db46 ./src/pisum.o -o ./pisum.so\n", - "\n" - ] - }, - { - "output_type": "stream", - "stream": "stdout", - "text": [ - "running build_ext\n", - "building 'pisum_opt' extension\n", - "C compiler: /usr/bin/clang -fno-strict-aliasing -fno-common -dynamic -pipe -Os -fwrapv -DNDEBUG -g -fwrapv -O3 -Wall -Wstrict-prototypes\n", - "\n", - "compile options: '-I/Users/jakeret/Library/Python/2.7/lib/python/site-packages/numpy/core/include -I/opt/local/Library/Frameworks/Python.framework/Versions/2.7/include/python2.7 -c'\n", - "extra options: '-Wall -Wno-unused-variable -march=native -stdlib=libc++ -std=c++11'\n", - "clang: ././src/pisum_opt.cpp\n", - "/usr/bin/clang++ -bundle -undefined dynamic_lookup -L/opt/local/lib -Wl,-headerpad_max_install_names -L/opt/local/lib/db46 ./src/pisum_opt.o -o ./pisum_opt.so\n", - "\n" - ] - } - ], - "prompt_number": 23 - }, + "compile options: '-I/Users/jakeret/Library/Python/2.7/lib/python/site-packages/numpy/core/include -I/opt/local/Library/Frameworks/Python.framework/Versions/2.7/include/python2.7 -c'\n", + "extra options: '-Wall -Wno-unused-variable -march=native -stdlib=libc++ -std=c++11'\n", + "clang: ././src/qsort_kernel.cpp\n", + "/usr/bin/clang++ -bundle -undefined dynamic_lookup -L/opt/local/lib -Wl,-headerpad_max_install_names -L/opt/local/lib/db46 ./src/qsort_kernel.o -o ./qsort_kernel.so\n", + "\n" + ] + } + ], + "source": [ + "def qsort_kernel(a, lo, hi):\n", + " i = lo\n", + " j = hi\n", + " if False: return a\n", + " while i < hi:\n", + " pivot = a[(lo+hi) // 2]\n", + " while i <= j:\n", + " while a[i] < pivot:\n", + " i += 1\n", + " while a[j] > pivot:\n", + " j -= 1\n", + " if i <= j:\n", + " tmp = a[i]\n", + " a[i] = a[j]\n", + " a[j] = tmp\n", + " i += 1\n", + " j -= 1\n", + " if lo < j:\n", + " qsort_kernel(a, lo, j)\n", + " lo = i\n", + " j = hi\n", + " return a\n", + "\n", + "hope_qsort_kernel = hope.jit(qsort_kernel)\n", + "numba_qsort_kernel = numba.jit(qsort_kernel)\n", + "\n", + "native_qsort_kernel_mod = load(\"qsort_kernel\")\n", + "native_qsort_kernel = native_qsort_kernel_mod.run\n" + ] + }, + { + "cell_type": "code", + "execution_count": 16, + "metadata": {}, + "outputs": [], + "source": [ + "def numpy_qsort_kernel(a, lo, hi):\n", + " np.sort(a)" + ] + }, + { + "cell_type": "code", + "execution_count": 17, + "metadata": {}, + "outputs": [], + "source": [ + "%%cython\n", + "\n", + "cimport cython\n", + "import numpy as np\n", + "cimport numpy as np\n", + "\n", + "@cython.boundscheck(False)\n", + "@cython.wraparound(False)\n", + "def cython_qsort_kernel(np.ndarray[np.double_t, ndim=1] a, int lo, int hi):\n", + " cdef int i = lo\n", + " cdef int j = hi\n", + " cdef double pivot = 0\n", + " cdef double tmp = 0.0\n", + " if False: return a\n", + " while i < hi:\n", + " pivot = a[(lo+hi) // 2]\n", + " while i <= j:\n", + " while a[i] < pivot:\n", + " i += 1\n", + " while a[j] > pivot:\n", + " j -= 1\n", + " if i <= j:\n", + " tmp = a[i]\n", + " a[i] = a[j]\n", + " a[j] = tmp\n", + " i += 1\n", + " j -= 1\n", + " if lo < j:\n", + " cython_qsort_kernel(a, lo, j)\n", + " lo = i\n", + " j = hi\n", + " return a\n", + "\n" + ] + }, + { + "cell_type": "code", + "execution_count": 18, + "metadata": {}, + "outputs": [], + "source": [ + "lst = np.random.random(5000)\n" + ] + }, + { + "cell_type": "code", + "execution_count": 19, + "metadata": {}, + "outputs": [], + "source": [ + "psorted = qsort_kernel(lst.copy(), 0, len(lst)-1)\n", + "hsorted = hope_qsort_kernel(lst.copy(), 0, len(lst)-1)\n", + "#nsorted = numba_qsort_kernel(lst.copy(), 0, len(lst)-1)\n", + "csorted = cython_qsort_kernel(lst.copy(), 0, len(lst)-1)\n", + "nasorted = native_qsort_kernel(lst.copy(), 0, len(lst)-1)\n" + ] + }, + { + "cell_type": "code", + "execution_count": 20, + "metadata": {}, + "outputs": [ { - "cell_type": "code", - "collapsed": false, - "input": [ - "%load_ext cythonmagic\n" - ], - "language": "python", - "metadata": {}, - "outputs": [ - { - "output_type": "stream", - "stream": "stdout", - "text": [ - "The cythonmagic extension is already loaded. To reload it, use:\n", - " %reload_ext cythonmagic\n" - ] - } - ], - "prompt_number": 24 - }, + "name": "stdout", + "output_type": "stream", + "text": [ + "10 loops, best of 3: 22.4 ms per loop\n", + "1000 loops, best of 3: 334 µs per loop\n", + "1000 loops, best of 3: 1.4 ms per loop\n", + "1000 loops, best of 3: 303 µs per loop\n", + "1000 loops, best of 3: 248 µs per loop\n" + ] + } + ], + "source": [ + "assert np.all(psorted[:-1] <= psorted[1:])\n", + "#assert np.all(hope_qsort_kernel[:-1] <= hope_qsort_kernel[1:])\n", + "#assert np.all(numba_qsort_kernel[:-1] <= numba_qsort_kernel[1:])\n", + "#assert np.all(cython_qsort_kernel[:-1] <= cython_qsort_kernel[1:])\n", + "\n", + "%timeit qsort_kernel(lst.copy(), 0, len(lst)-1)\n", + "%timeit hope_qsort_kernel(lst.copy(), 0, len(lst)-1)\n", + "#%timeit numba_qsort_kernel(lst.copy(), 0, len(lst)-1)\n", + "%timeit cython_qsort_kernel(lst.copy(), 0, len(lst)-1)\n", + "%timeit native_qsort_kernel(lst.copy(), 0, len(lst)-1)\n", + "%timeit np.sort(lst.copy())" + ] + }, + { + "cell_type": "code", + "execution_count": 21, + "metadata": {}, + "outputs": [ { - "cell_type": "code", - "collapsed": false, - "input": [ - "%%cython\n", - "\n", - "cimport cython\n", - "\n", - "@cython.boundscheck(False)\n", - "@cython.wraparound(False)\n", - "@cython.locals(f=float)\n", - "def cython_pisum():\n", - " cdef double sum = 0.0\n", - " for j in range(1, 501):\n", - " sum = 0.0\n", - " f = 0.0\n", - " for k in range(1, 10001):\n", - " sum += 1.0/(k*k)\n", - " return sum\n", + "name": "stdout", + "output_type": "stream", + "text": [ + "function: native_qsort_kernel , av. time sec: 0.00029302, min. time sec: 0.00029182, relative: 1.0\n", + "function: hope_qsort_kernel , av. time sec: 0.00033617, min. time sec: 0.00033379, relative: 1.1\n", + "function: cython_qsort_kernel , av. time sec: 0.00143504, min. time sec: 0.00140619, relative: 4.9\n", + "function: qsort_kernel , av. time sec: 0.02197444, min. time sec: 0.02172208, relative: 75.0\n" + ] + } + ], + "source": [ + "a = lst.copy()\n", + "\n", + "lo = 0\n", + "hi = len(lst)-1\n", + "\n", + "perf_comp_data([\"hope_qsort_kernel\", \n", + " \"qsort_kernel\", \n", + " #\"numpy_qsort_kernel\", \n", + " \"cython_qsort_kernel\", \n", + " \"native_qsort_kernel\"],\n", + " 5*[\"a, lo, hi\"], rep=100, extra_setup=\"from __main__ import lst;a = lst.copy()\")" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# pi sum" + ] + }, + { + "cell_type": "code", + "execution_count": 23, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "running build_ext\n", + "building 'pisum' extension\n", + "C compiler: /usr/bin/clang -fno-strict-aliasing -fno-common -dynamic -pipe -Os -fwrapv -DNDEBUG -g -fwrapv -O3 -Wall -Wstrict-prototypes\n", "\n", - "@cython.boundscheck(False)\n", - "@cython.wraparound(False)\n", - "@cython.locals(f=float)\n", - "def cython_pisum_opt():\n", - " cdef double sum = 0.0\n", - " for j in range(1, 501):\n", - " sum = 0.0\n", - " f = 0.0\n", - " for k in range(1, 10001):\n", - " f += 1.\n", - " sum += 1.0/(f*f)\n", - " return sum\n", + "compile options: '-I/Users/jakeret/Library/Python/2.7/lib/python/site-packages/numpy/core/include -I/opt/local/Library/Frameworks/Python.framework/Versions/2.7/include/python2.7 -c'\n", + "extra options: '-Wall -Wno-unused-variable -march=native -stdlib=libc++ -std=c++11'\n", + "clang: ././src/pisum.cpp\n", + "/usr/bin/clang++ -bundle -undefined dynamic_lookup -L/opt/local/lib -Wl,-headerpad_max_install_names -L/opt/local/lib/db46 ./src/pisum.o -o ./pisum.so\n", "\n", + "running build_ext\n", + "building 'pisum_opt' extension\n", + "C compiler: /usr/bin/clang -fno-strict-aliasing -fno-common -dynamic -pipe -Os -fwrapv -DNDEBUG -g -fwrapv -O3 -Wall -Wstrict-prototypes\n", "\n", - "assert abs(cython_pisum()-1.644834071848065) < 1e-6\n", - "assert abs(cython_pisum_opt()-1.644834071848065) < 1e-6" - ], - "language": "python", - "metadata": {}, - "outputs": [], - "prompt_number": 25 - }, + "compile options: '-I/Users/jakeret/Library/Python/2.7/lib/python/site-packages/numpy/core/include -I/opt/local/Library/Frameworks/Python.framework/Versions/2.7/include/python2.7 -c'\n", + "extra options: '-Wall -Wno-unused-variable -march=native -stdlib=libc++ -std=c++11'\n", + "clang: ././src/pisum_opt.cpp\n", + "/usr/bin/clang++ -bundle -undefined dynamic_lookup -L/opt/local/lib -Wl,-headerpad_max_install_names -L/opt/local/lib/db46 ./src/pisum_opt.o -o ./pisum_opt.so\n", + "\n" + ] + } + ], + "source": [ + "def pisum():\n", + " for j in range(1, 501):\n", + " sum = 0.0\n", + " f = 0.0\n", + " for k in range(1, 10001):\n", + " sum += 1.0/(k*k)\n", + " return sum\n", + "def pisum_opt():\n", + " for j in range(1, 501):\n", + " sum = 0.0\n", + " f = 0.0\n", + " for k in range(1, 10001):\n", + " f += 1.\n", + " sum += 1.0/(f*f)\n", + " return sum\n", + "\n", + "hope_pisum = hope.jit(pisum)\n", + "hope_pisum_opt = hope.jit(pisum_opt)\n", + "\n", + "numba_pisum = numba.jit(pisum, nopython=True)\n", + "numba_pisum_opt = numba.jit(pisum_opt, nopython=True)\n", + "\n", + "native_pisum_mod = load(\"pisum\")\n", + "native_pisum = native_pisum_mod.run\n", + "\n", + "native_pisum_opt_mod = load(\"pisum_opt\")\n", + "native_pisum_opt = native_pisum_opt_mod.run\n", + "\n", + "\n", + "assert abs(pisum()-1.644834071848065) < 1e-6\n", + "assert abs(hope_pisum()-1.644834071848065) < 1e-6\n", + "assert abs(hope_pisum_opt()-1.644834071848065) < 1e-6\n", + "assert abs(numba_pisum()-1.644834071848065) < 1e-6\n", + "assert abs(native_pisum()-1.644834071848065) < 1e-6\n", + "assert abs(native_pisum_opt()-1.644834071848065) < 1e-6\n" + ] + }, + { + "cell_type": "code", + "execution_count": 24, + "metadata": {}, + "outputs": [ { - "cell_type": "code", - "collapsed": false, - "input": [ - "%timeit pisum()\n", - "%timeit pisum_opt()\n", - "%timeit hope_pisum()\n", - "%timeit hope_pisum_opt()\n", - "%timeit numba_pisum()\n", - "%timeit numba_pisum_opt()\n", - "%timeit cython_pisum()\n", - "%timeit cython_pisum_opt()\n", - "%timeit native_pisum()\n", - "%timeit native_pisum_opt()" - ], - "language": "python", - "metadata": {}, - "outputs": [ - { - "output_type": "stream", - "stream": "stdout", - "text": [ - "1 loops, best of 3: 493 ms per loop\n", - "1 loops, best of 3: 641 ms per loop" - ] - }, - { - "output_type": "stream", - "stream": "stdout", - "text": [ - "\n", - "10 loops, best of 3: 21.4 ms per loop" - ] - }, - { - "output_type": "stream", - "stream": "stdout", - "text": [ - "\n", - "10 loops, best of 3: 21.6 ms per loop" - ] - }, - { - "output_type": "stream", - "stream": "stdout", - "text": [ - "\n", - "10 loops, best of 3: 40.8 ms per loop" - ] - }, - { - "output_type": "stream", - "stream": "stdout", - "text": [ - "\n", - "10 loops, best of 3: 21.8 ms per loop" - ] - }, - { - "output_type": "stream", - "stream": "stdout", - "text": [ - "\n", - "1 loops, best of 3: 284 ms per loop" - ] - }, - { - "output_type": "stream", - "stream": "stdout", - "text": [ - "\n", - "10 loops, best of 3: 21.1 ms per loop" - ] - }, - { - "output_type": "stream", - "stream": "stdout", - "text": [ - "\n", - "10 loops, best of 3: 21.1 ms per loop" - ] - }, - { - "output_type": "stream", - "stream": "stdout", - "text": [ - "\n", - "10 loops, best of 3: 21.3 ms per loop" - ] - }, - { - "output_type": "stream", - "stream": "stdout", - "text": [ - "\n" - ] - } - ], - "prompt_number": 26 - }, + "name": "stdout", + "output_type": "stream", + "text": [ + "The cythonmagic extension is already loaded. To reload it, use:\n", + " %reload_ext cythonmagic\n" + ] + } + ], + "source": [ + "%load_ext cythonmagic\n" + ] + }, + { + "cell_type": "code", + "execution_count": 25, + "metadata": {}, + "outputs": [], + "source": [ + "%%cython\n", + "\n", + "cimport cython\n", + "\n", + "@cython.boundscheck(False)\n", + "@cython.wraparound(False)\n", + "@cython.locals(f=float)\n", + "def cython_pisum():\n", + " cdef double sum = 0.0\n", + " for j in range(1, 501):\n", + " sum = 0.0\n", + " f = 0.0\n", + " for k in range(1, 10001):\n", + " sum += 1.0/(k*k)\n", + " return sum\n", + "\n", + "@cython.boundscheck(False)\n", + "@cython.wraparound(False)\n", + "@cython.locals(f=float)\n", + "def cython_pisum_opt():\n", + " cdef double sum = 0.0\n", + " for j in range(1, 501):\n", + " sum = 0.0\n", + " f = 0.0\n", + " for k in range(1, 10001):\n", + " f += 1.\n", + " sum += 1.0/(f*f)\n", + " return sum\n", + "\n", + "\n", + "assert abs(cython_pisum()-1.644834071848065) < 1e-6\n", + "assert abs(cython_pisum_opt()-1.644834071848065) < 1e-6" + ] + }, + { + "cell_type": "code", + "execution_count": 26, + "metadata": {}, + "outputs": [ { - "cell_type": "code", - "collapsed": false, - "input": [ - "perf_comp_data([\"pisum\", \"pisum_opt\", \n", - " \"hope_pisum\", \"hope_pisum_opt\", \n", - " \"numba_pisum\", \"numba_pisum_opt\", \n", - " #\"cython_pisum\", \n", - " \"cython_pisum_opt\",\n", - " \"native_pisum\", \"native_pisum_opt\",], \n", - " None, rep=100)" - ], - "language": "python", - "metadata": {}, - "outputs": [ - { - "output_type": "stream", - "stream": "stdout", - "text": [ - "function: cython_pisum_opt , av. time sec: 0.02122343, min. time sec: 0.02094698, relative: 1.0\n", - "function: native_pisum_opt , av. time sec: 0.02158451, min. time sec: 0.02108502, relative: 1.0\n", - "function: native_pisum , av. time sec: 0.02167845, min. time sec: 0.02090883, relative: 1.0\n", - "function: numba_pisum_opt , av. time sec: 0.02173293, min. time sec: 0.02095199, relative: 1.0\n", - "function: hope_pisum_opt , av. time sec: 0.02241504, min. time sec: 0.02135611, relative: 1.1\n", - "function: hope_pisum , av. time sec: 0.02270293, min. time sec: 0.02136111, relative: 1.1\n", - "function: numba_pisum , av. time sec: 0.04188490, min. time sec: 0.04017806, relative: 2.0\n", - "function: pisum , av. time sec: 0.49351048, min. time sec: 0.48015594, relative: 23.3\n", - "function: pisum_opt , av. time sec: 0.64186037, min. time sec: 0.61331892, relative: 30.2\n" - ] - } - ], - "prompt_number": 27 - }, + "name": "stdout", + "output_type": "stream", + "text": [ + "1 loops, best of 3: 493 ms per loop\n", + "1 loops, best of 3: 641 ms per loop\n", + "10 loops, best of 3: 21.4 ms per loop\n", + "10 loops, best of 3: 21.6 ms per loop\n", + "10 loops, best of 3: 40.8 ms per loop\n", + "10 loops, best of 3: 21.8 ms per loop\n", + "1 loops, best of 3: 284 ms per loop\n", + "10 loops, best of 3: 21.1 ms per loop\n", + "10 loops, best of 3: 21.1 ms per loop\n", + "10 loops, best of 3: 21.3 ms per loop\n" + ] + } + ], + "source": [ + "%timeit pisum()\n", + "%timeit pisum_opt()\n", + "%timeit hope_pisum()\n", + "%timeit hope_pisum_opt()\n", + "%timeit numba_pisum()\n", + "%timeit numba_pisum_opt()\n", + "%timeit cython_pisum()\n", + "%timeit cython_pisum_opt()\n", + "%timeit native_pisum()\n", + "%timeit native_pisum_opt()" + ] + }, + { + "cell_type": "code", + "execution_count": 27, + "metadata": {}, + "outputs": [ { - "cell_type": "code", - "collapsed": false, - "input": [], - "language": "python", - "metadata": {}, - "outputs": [], - "prompt_number": 17 + "name": "stdout", + "output_type": "stream", + "text": [ + "function: cython_pisum_opt , av. time sec: 0.02122343, min. time sec: 0.02094698, relative: 1.0\n", + "function: native_pisum_opt , av. time sec: 0.02158451, min. time sec: 0.02108502, relative: 1.0\n", + "function: native_pisum , av. time sec: 0.02167845, min. time sec: 0.02090883, relative: 1.0\n", + "function: numba_pisum_opt , av. time sec: 0.02173293, min. time sec: 0.02095199, relative: 1.0\n", + "function: hope_pisum_opt , av. time sec: 0.02241504, min. time sec: 0.02135611, relative: 1.1\n", + "function: hope_pisum , av. time sec: 0.02270293, min. time sec: 0.02136111, relative: 1.1\n", + "function: numba_pisum , av. time sec: 0.04188490, min. time sec: 0.04017806, relative: 2.0\n", + "function: pisum , av. time sec: 0.49351048, min. time sec: 0.48015594, relative: 23.3\n", + "function: pisum_opt , av. time sec: 0.64186037, min. time sec: 0.61331892, relative: 30.2\n" + ] } ], - "metadata": {} + "source": [ + "perf_comp_data([\"pisum\", \"pisum_opt\", \n", + " \"hope_pisum\", \"hope_pisum_opt\", \n", + " \"numba_pisum\", \"numba_pisum_opt\", \n", + " #\"cython_pisum\", \n", + " \"cython_pisum_opt\",\n", + " \"native_pisum\", \"native_pisum_opt\",], \n", + " None, rep=100)" + ] + }, + { + "cell_type": "code", + "execution_count": 17, + "metadata": {}, + "outputs": [], + "source": [] } - ] -} \ No newline at end of file + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.5.1" + } + }, + "nbformat": 4, + "nbformat_minor": 1 +} diff --git a/benchmarks/native_cpp_gen.ipynb b/benchmarks/native_cpp_gen.ipynb index 20878e4..ee93e30 100644 --- a/benchmarks/native_cpp_gen.ipynb +++ b/benchmarks/native_cpp_gen.ipynb @@ -1,2267 +1,2239 @@ { - "metadata": { - "name": "" - }, - "nbformat": 3, - "nbformat_minor": 0, - "worksheets": [ + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# IPython magic extension version_information" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Use the '%version_information' IPython magic extension in a notebook to display information about which versions of dependency package that was used to run the notebook.\n", + "Installation" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Installation" + ] + }, { - "cells": [ + "cell_type": "code", + "execution_count": 3, + "metadata": {}, + "outputs": [ { - "cell_type": "heading", - "level": 1, - "metadata": {}, - "source": [ - "IPython magic extension version_information\n" + "name": "stdout", + "output_type": "stream", + "text": [ + "Installed version_information.py. To use it, type:\n", + " %load_ext version_information\n" ] - }, + } + ], + "source": [ + "%install_ext http://raw.github.com/jrjohansson/version_information/master/version_information.py" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Native CPP codes" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Fibonacci CPP" + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "metadata": {}, + "outputs": [ { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Use the '%version_information' IPython magic extension in a notebook to display information about which versions of dependency package that was used to run the notebook.\n", - "Installation" + "name": "stdout", + "output_type": "stream", + "text": [ + "Overwriting src/fib.cpp\n" ] - }, + } + ], + "source": [ + "%%file src/fib.cpp\n", + "#define PY_ARRAY_UNIQUE_SYMBOL fib_ARRAY_API\n", + "#define NPY_NO_DEPRECATED_API NPY_1_7_API_VERSION\n", + "#include \n", + "#include \n", + "#include \n", + "#include \n", + "#include \n", + "#include \n", + "#include \n", + "#include \n", + "#include \n", + "#include \n", + "#include \n", + "struct PyObj {\n", + "\ttypedef PyObject * ptr_t;\n", + "\ttypedef PyArrayObject * arrptr_t;\n", + "\tPyObj(): dec(false), ptr(NULL) {}\n", + "\tPyObj(ptr_t p): dec(false), ptr(p) {}\n", + "\t~PyObj() { if(dec) Py_DECREF(ptr); }\n", + "\tPyObj & operator=(ptr_t p) { if(dec) Py_DECREF(ptr); ptr = p; dec = false; return *this; }\n", + "\tPyObj & incref(ptr_t p) { if(dec) Py_DECREF(ptr); ptr = p; dec = (p != NULL); return *this; }\n", + "\toperator bool() const { return ptr; }\n", + "\toperator ptr_t() const { return ptr; }\n", + "\toperator arrptr_t() const { return (arrptr_t)ptr; }\n", + "\tbool dec;\n", + "\tptr_t ptr;\n", + "};\n", + "\n", + "\n", + "inline npy_int64 fib_J(npy_int64 cn);\n", + "\n", + "inline npy_int64 fib_J(npy_int64 cn) {\n", + "\tif (cn < 2) {\n", + "\t\treturn cn;\n", + "\t}\n", + "\treturn fib_J(cn - 1) + fib_J(cn - 2);\n", + "}\n", + "\n", + "\n", + "\n", + "void sighandler(int sig);\n", + "#include \n", + "extern \"C\" {\n", + "\tPyObject * create_signature;\n", + "\tstruct sigaction slot;\n", + "\tPyObject * set_create_signature(PyObject * self, PyObject * args) {\n", + "\t\tif (!PyArg_ParseTuple(args, \"O\", &create_signature)) {\n", + "\t\t\tPyErr_SetString(PyExc_ValueError, \"Invalid Argument to set_create_signature!\");\n", + "\t\t\treturn NULL;\n", + "\t\t}\n", + "\t\tPy_INCREF(create_signature);\n", + "\t\tmemset(&slot, 0, sizeof(slot));\n", + "\t\tslot.sa_handler = &sighandler;\n", + "\t\tsigaction(SIGSEGV, &slot, NULL);\n", + "\t\tsigaction(SIGBUS, &slot, NULL);\n", + "\t\tPy_INCREF(Py_None);\n", + "\t\treturn Py_None;\n", + "\t}\n", + "\tPyObject * run(PyObject * self, PyObject * args) {\n", + "\t\t{\n", + "\t\t\tPyObject * pn; npy_int64 cn;\n", + "\t\t\tif (\n", + "\t\t\t\tPyTuple_CheckExact(args) and PyTuple_GET_SIZE(args) == 1\n", + "\t\t\t\tand (pn = PyTuple_GET_ITEM(args, 0)) and PyInt_CheckExact(pn)\n", + "\t\t\t) {\n", + "\t\t\t\tcn = PyInt_AS_LONG(pn);\n", + "\t\t\t\ttry {\n", + "\t\t\t\t\treturn Py_BuildValue(\"l\", fib_J(\n", + "\t\t\t\t\t\t cn\n", + "\t\t\t\t\t));\n", + "\t\t\t\t} catch (...) {\n", + "\t\t\t\t\treturn NULL;\n", + "\t\t\t\t}\n", + "\t\t\t} else\n", + "\t\t\t\tPyErr_Clear();\n", + "\t\t}\n", + "\t\tPyObject * signatures = Py_BuildValue(\"(sO)\", \"(lp0\\n(lp1\\nccopy_reg\\n_reconstructor\\np2\\n(chope._ast\\nVariable\\np3\\nc__builtin__\\nobject\\np4\\nNtp5\\nRp6\\n(dp7\\nS'name'\\np8\\nS'n'\\np9\\nsS'dtype'\\np10\\nc__builtin__\\nint\\np11\\nsS'dims'\\np12\\nI0\\nsbaa.\", args);\n", + "\t\tif (!signatures) {\n", + "\t\t\tPyErr_SetString(PyExc_ValueError, \"Error building signature string for fib\");\n", + "\t\t\treturn NULL;\n", + "\t\t}\n", + "\t\treturn PyObject_Call(create_signature, signatures, NULL);\n", + "\t}\n", + "\tPyMethodDef fibMethods[] = {\n", + "\t\t{ \"set_create_signature\", (PyCFunction)set_create_signature, METH_VARARGS },\n", + "\t\t{ \"run\", (PyCFunction)run, METH_VARARGS },\n", + "\t\t{ NULL, NULL }\n", + "\t};\n", + "\tPyMODINIT_FUNC initfib(void) {\n", + "\t\timport_array();\n", + "\t\tPyImport_ImportModule(\"numpy\");\n", + "\t\t(void)Py_InitModule(\"fib\", fibMethods);\n", + "\t}\n", + "}\n", + "#include \n", + "#include \n", + "#include \n", + "#include \n", + "#include \n", + "void sighandler(int sig) {\n", + "\tstd::ostringstream buffer;\n", + "\tbuffer << \"Abort by \" << (sig == SIGSEGV ? \"segfault\" : \"bus error\") << std::endl;\n", + "\tvoid * stack[64];\n", + "\tstd::size_t depth = backtrace(stack, 64);\n", + "\tif (!depth)\n", + "\t\tbuffer << \" \" << std::endl;\n", + "\telse {\n", + "\t\tchar ** symbols = backtrace_symbols(stack, depth);\n", + "\t\tfor (std::size_t i = 1; i < depth; ++i) {\n", + "\t\t\tstd::string symbol = symbols[i];\n", + "\t\t\t\tif (symbol.find_first_of(' ', 59) != std::string::npos) {\n", + "\t\t\t\t\tstd::string name = symbol.substr(59, symbol.find_first_of(' ', 59) - 59);\n", + "\t\t\t\t\tint status;\n", + "\t\t\t\t\tchar * demangled = abi::__cxa_demangle(name.c_str(), NULL, NULL, &status);\n", + "\t\t\t\t\tif (!status) {\n", + "\t\t\t\t\t\tbuffer << \" \" \n", + "\t\t\t\t\t\t\t<< symbol.substr(0, 59) \n", + "\t\t\t\t\t\t\t<< demangled\n", + "\t\t\t\t\t\t\t<< symbol.substr(59 + name.size())\n", + "\t\t\t\t\t\t\t<< std::endl;\n", + "\t\t\t\t\t\tfree(demangled);\n", + "\t\t\t\t\t} else\n", + "\t\t\t\t\t\tbuffer << \" \" << symbol << std::endl;\n", + "\t\t\t\t} else\n", + "\t\t\t\t\tbuffer << \" \" << symbol << std::endl;\n", + "\t\t\t}\n", + "\t\t\tfree(symbols);\n", + "\t\t}\n", + "\t\tstd::cerr << buffer.str();\n", + "\t\tstd::exit(EXIT_FAILURE);\n", + "\t}\n", + "\n" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Quicksort CPP" + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "metadata": {}, + "outputs": [ { - "cell_type": "heading", - "level": 2, - "metadata": {}, - "source": [ - "Installation" + "name": "stdout", + "output_type": "stream", + "text": [ + "Overwriting src/qsort_kernel.cpp\n" ] - }, - { - "cell_type": "code", - "collapsed": false, - "input": [ - "%install_ext http://raw.github.com/jrjohansson/version_information/master/version_information.py" - ], - "language": "python", - "metadata": {}, - "outputs": [ - { - "output_type": "stream", - "stream": "stdout", - "text": [ - "Installed version_information.py. To use it, type:\n", - " %load_ext version_information\n" - ] - } - ], - "prompt_number": 3 - }, + } + ], + "source": [ + "%%file src/qsort_kernel.cpp\n", + "#define PY_ARRAY_UNIQUE_SYMBOL qsort_kernel_ARRAY_API\n", + "#define NPY_NO_DEPRECATED_API NPY_1_7_API_VERSION\n", + "#include \n", + "#include \n", + "#include \n", + "#include \n", + "#include \n", + "#include \n", + "#include \n", + "#include \n", + "#include \n", + "#include \n", + "#include \n", + "struct PyObj {\n", + "\ttypedef PyObject * ptr_t;\n", + "\ttypedef PyArrayObject * arrptr_t;\n", + "\tPyObj(): dec(false), ptr(NULL) {}\n", + "\tPyObj(ptr_t p): dec(false), ptr(p) {}\n", + "\t~PyObj() { if(dec) Py_DECREF(ptr); }\n", + "\tPyObj & operator=(ptr_t p) { if(dec) Py_DECREF(ptr); ptr = p; dec = false; return *this; }\n", + "\tPyObj & incref(ptr_t p) { if(dec) Py_DECREF(ptr); ptr = p; dec = (p != NULL); return *this; }\n", + "\toperator bool() const { return ptr; }\n", + "\toperator ptr_t() const { return ptr; }\n", + "\toperator arrptr_t() const { return (arrptr_t)ptr; }\n", + "\tbool dec;\n", + "\tptr_t ptr;\n", + "};\n", + "inline std::tuple qsort_kernel_d1JJ(PyObject * pa, \n", + "\t \t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tnpy_intp const * __restrict__ sa, \n", + "\t \t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tnpy_double * __restrict__ ca, \n", + "\t \t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tnpy_int64 clo, \n", + "\t \t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tnpy_int64 chi);\n", + "\t \t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\n", + "inline std::tuple qsort_kernel_d1JJ(PyObject * pa, \n", + "\t \t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tnpy_intp const * __restrict__ sa, \n", + "\t \t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tnpy_double * __restrict__ ca, \n", + "\t \t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tnpy_int64 clo, \n", + "\t \t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tnpy_int64 chi){\n", + "\tnpy_int64 ci = clo;\n", + "\tnpy_int64 cj = chi;\n", + "\tnpy_double cpivot;\n", + "\n", + "\twhile (ci < chi) {\n", + "\t\tcpivot = ca[(int)((clo + chi) / 2)];\n", + "\t\t\n", + "\t\twhile (ci <= cj) {\n", + "\t\t\twhile (ca[ci] < cpivot) {\n", + "\t\t\t\tci += 1;\n", + "\t\t\t}\n", + "\t\t\t\n", + "\t\t\twhile (ca[cj] > cpivot) {\n", + "\t\t\t\tcj -= 1;\n", + "\t\t\t}\n", + "\t\t\t\n", + "\t\t\tif (ci <= cj) {\n", + "\t\t\t\tauto ctmp = ca[ci];\n", + "\t\t\t\tca[ci] = ca[cj];\n", + "\t\t\t\tca[cj] = ctmp;\n", + "\t\t\t\tci += 1;\n", + "\t\t\t\tcj -= 1;\n", + "\t\t\t}\n", + "\t\t}\n", + "\t\t\n", + "\t\tif (clo < cj) {\n", + "\t\t\tqsort_kernel_d1JJ(pa, sa, ca, clo, cj);\n", + "\t\t}\n", + "\t\t\n", + "\t\tclo = ci;\n", + "\t\tcj = chi;\n", + "\t}\n", + "\t\n", + "\treturn std::make_tuple((PyObject *)pa, sa, ca);\n", + "}\n", + "\n", + "void sighandler(int sig);\n", + "#include \n", + "extern \"C\" {\n", + "\tPyObject * create_signature;\n", + "\tstruct sigaction slot;\n", + "\tPyObject * set_create_signature(PyObject * self, PyObject * args) {\n", + "\t\tif (!PyArg_ParseTuple(args, \"O\", &create_signature)) {\n", + "\t\t\tPyErr_SetString(PyExc_ValueError, \"Invalid Argument to set_create_signature!\");\n", + "\t\t\treturn NULL;\n", + "\t\t}\n", + "\t\tPy_INCREF(create_signature);\n", + "\t\tmemset(&slot, 0, sizeof(slot));\n", + "\t\tslot.sa_handler = &sighandler;\n", + "\t\tsigaction(SIGSEGV, &slot, NULL);\n", + "\t\tsigaction(SIGBUS, &slot, NULL);\n", + "\t\tPy_INCREF(Py_None);\n", + "\t\treturn Py_None;\n", + "\t}\n", + "\tPyObject * run(PyObject * self, PyObject * args) {\n", + "\t\t{\n", + "\t\t\tPyObj pa;\n", + "\t\t\tPyObject * plo; npy_int64 clo;\n", + "\t\t\tPyObject * phi; npy_int64 chi;\n", + "\t\t\tif (\n", + "\t\t\t\tPyTuple_CheckExact(args) and PyTuple_GET_SIZE(args) == 3\n", + "\t\t\t\tand (pa = PyTuple_GET_ITEM(args, 0)) and PyArray_CheckExact(pa)\n", + "\t\t\t\tand PyArray_TYPE((PyArrayObject *)pa) == NPY_FLOAT64 and PyArray_NDIM((PyArrayObject *)pa) == 1\n", + "\t\t\t\tand (plo = PyTuple_GET_ITEM(args, 1)) and PyInt_CheckExact(plo)\n", + "\t\t\t\tand (phi = PyTuple_GET_ITEM(args, 2)) and PyInt_CheckExact(phi)\n", + "\t\t\t) {\n", + "\t\t\t\tif (!(pa.incref((PyObject *)PyArray_GETCONTIGUOUS((PyArrayObject *)pa)))) {\n", + "\t\t\t\t\tPyErr_SetString(PyExc_ValueError, \"Invalid Argument type on a!\");\n", + "\t\t\t\t\treturn NULL;\n", + "\t\t\t\t}\n", + "\t\t\t\tclo = PyInt_AS_LONG(plo);\n", + "\t\t\t\tchi = PyInt_AS_LONG(phi);\n", + "\t\t\t\ttry {\n", + "\t\t\t\t\tPyObject * res = std::get<0>(qsort_kernel_d1JJ(\n", + "\t\t\t\t\t\t pa, PyArray_SHAPE((PyArrayObject *)pa), (npy_double *)PyArray_DATA((PyArrayObject *)pa)\n", + "\t\t\t\t\t\t, clo\n", + "\t\t\t\t\t\t, chi\n", + "\t\t\t\t\t));\n", + "\n", + "\t\t\t\t\tPy_INCREF(res);\n", + "\t\t\t\t\treturn res;\n", + "\t\t\t\t} catch (...) {\n", + "\t\t\t\t\treturn NULL;\n", + "\t\t\t\t}\n", + "\t\t\t} else\n", + "\t\t\t\tPyErr_Clear();\n", + "\t\t}\n", + "\t\tPyObject * signatures = Py_BuildValue(\"(sO)\", \"(lp0\\n(lp1\\nccopy_reg\\n_reconstructor\\np2\\n(chope._ast\\nVariable\\np3\\nc__builtin__\\nobject\\np4\\nNtp5\\nRp6\\n(dp7\\nS'name'\\np8\\nS'a'\\np9\\nsS'dtype'\\np10\\nS'float64'\\np11\\nsS'dims'\\np12\\nI1\\nsbag2\\n(g3\\ng4\\nNtp13\\nRp14\\n(dp15\\ng8\\nS'lo'\\np16\\nsg10\\nc__builtin__\\nint\\np17\\nsg12\\nI0\\nsbag2\\n(g3\\ng4\\nNtp18\\nRp19\\n(dp20\\ng8\\nS'hi'\\np21\\nsg10\\ng17\\nsg12\\nI0\\nsbaa.\", args);\n", + "\t\tif (!signatures) {\n", + "\t\t\tPyErr_SetString(PyExc_ValueError, \"Error building signature string for qsort_kernel\");\n", + "\t\t\treturn NULL;\n", + "\t\t}\n", + "\t\treturn PyObject_Call(create_signature, signatures, NULL);\n", + "\t}\n", + "\tPyMethodDef qsort_kernelMethods[] = {\n", + "\t\t{ \"set_create_signature\", (PyCFunction)set_create_signature, METH_VARARGS },\n", + "\t\t{ \"run\", (PyCFunction)run, METH_VARARGS },\n", + "\t\t{ NULL, NULL }\n", + "\t};\n", + "\tPyMODINIT_FUNC initqsort_kernel(void) {\n", + "\t\timport_array();\n", + "\t\tPyImport_ImportModule(\"numpy\");\n", + "\t\t(void)Py_InitModule(\"qsort_kernel\", qsort_kernelMethods);\n", + "\t}\n", + "}\n", + "#include \n", + "#include \n", + "#include \n", + "#include \n", + "#include \n", + "void sighandler(int sig) {\n", + "\tstd::ostringstream buffer;\n", + "\tbuffer << \"Abort by \" << (sig == SIGSEGV ? \"segfault\" : \"bus error\") << std::endl;\n", + "\tvoid * stack[64];\n", + "\tstd::size_t depth = backtrace(stack, 64);\n", + "\tif (!depth)\n", + "\t\tbuffer << \" \" << std::endl;\n", + "\telse {\n", + "\t\tchar ** symbols = backtrace_symbols(stack, depth);\n", + "\t\tfor (std::size_t i = 1; i < depth; ++i) {\n", + "\t\t\tstd::string symbol = symbols[i];\n", + "\t\t\t\tif (symbol.find_first_of(' ', 59) != std::string::npos) {\n", + "\t\t\t\t\tstd::string name = symbol.substr(59, symbol.find_first_of(' ', 59) - 59);\n", + "\t\t\t\t\tint status;\n", + "\t\t\t\t\tchar * demangled = abi::__cxa_demangle(name.c_str(), NULL, NULL, &status);\n", + "\t\t\t\t\tif (!status) {\n", + "\t\t\t\t\t\tbuffer << \" \" \n", + "\t\t\t\t\t\t\t<< symbol.substr(0, 59) \n", + "\t\t\t\t\t\t\t<< demangled\n", + "\t\t\t\t\t\t\t<< symbol.substr(59 + name.size())\n", + "\t\t\t\t\t\t\t<< std::endl;\n", + "\t\t\t\t\t\tfree(demangled);\n", + "\t\t\t\t\t} else\n", + "\t\t\t\t\t\tbuffer << \" \" << symbol << std::endl;\n", + "\t\t\t\t} else\n", + "\t\t\t\t\tbuffer << \" \" << symbol << std::endl;\n", + "\t\t\t}\n", + "\t\t\tfree(symbols);\n", + "\t\t}\n", + "\t\tstd::cerr << buffer.str();\n", + "\t\tstd::exit(EXIT_FAILURE);\n", + "\t}\n" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Pi sum CPP" + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "metadata": {}, + "outputs": [ { - "cell_type": "heading", - "level": 1, - "metadata": {}, - "source": [ - "Native CPP codes" + "name": "stdout", + "output_type": "stream", + "text": [ + "Overwriting src/pisum.cpp\n" ] - }, + } + ], + "source": [ + "%%file src/pisum.cpp\n", + "#define PY_ARRAY_UNIQUE_SYMBOL pisum_ARRAY_API\n", + "#define NPY_NO_DEPRECATED_API NPY_1_7_API_VERSION\n", + "#include \n", + "#include \n", + "#include \n", + "#include \n", + "#include \n", + "#include \n", + "#include \n", + "#include \n", + "#include \n", + "#include \n", + "#include \n", + "struct PyObj {\n", + "\ttypedef PyObject * ptr_t;\n", + "\ttypedef PyArrayObject * arrptr_t;\n", + "\tPyObj(): dec(false), ptr(NULL) {}\n", + "\tPyObj(ptr_t p): dec(false), ptr(p) {}\n", + "\t~PyObj() { if(dec) Py_DECREF(ptr); }\n", + "\tPyObj & operator=(ptr_t p) { if(dec) Py_DECREF(ptr); ptr = p; dec = false; return *this; }\n", + "\tPyObj & incref(ptr_t p) { if(dec) Py_DECREF(ptr); ptr = p; dec = (p != NULL); return *this; }\n", + "\toperator bool() const { return ptr; }\n", + "\toperator ptr_t() const { return ptr; }\n", + "\toperator arrptr_t() const { return (arrptr_t)ptr; }\n", + "\tbool dec;\n", + "\tptr_t ptr;\n", + "};\n", + "\n", + "inline npy_double pisum_();\n", + "\n", + "inline npy_double pisum_() {\n", + "\tnpy_double csum = npy_double();\n", + "\tnpy_intp ck = 1;\n", + "\tfor (npy_intp cj = 1; cj < 501; ++cj) {\n", + "\t\tcsum = 0.0;\n", + "\t\tauto cf = 0.0;\n", + "\t\tfor (ck = 1; ck < 10001; ++ck) {\n", + "\t\t\tauto c__sp0 = (ck * ck);\n", + "\t\t\tcsum += (1.0 / c__sp0);\n", + "\t\t}\n", + "\t}\n", + "\t\n", + "\treturn csum;\n", + "}\n", + "\n", + "void sighandler(int sig);\n", + "#include \n", + "extern \"C\" {\n", + "\tPyObject * create_signature;\n", + "\tstruct sigaction slot;\n", + "\tPyObject * set_create_signature(PyObject * self, PyObject * args) {\n", + "\t\tif (!PyArg_ParseTuple(args, \"O\", &create_signature)) {\n", + "\t\t\tPyErr_SetString(PyExc_ValueError, \"Invalid Argument to set_create_signature!\");\n", + "\t\t\treturn NULL;\n", + "\t\t}\n", + "\t\tPy_INCREF(create_signature);\n", + "\t\tmemset(&slot, 0, sizeof(slot));\n", + "\t\tslot.sa_handler = &sighandler;\n", + "\t\tsigaction(SIGSEGV, &slot, NULL);\n", + "\t\tsigaction(SIGBUS, &slot, NULL);\n", + "\t\tPy_INCREF(Py_None);\n", + "\t\treturn Py_None;\n", + "\t}\n", + "\tPyObject * run(PyObject * self, PyObject * args) {\n", + "\t\t{\t\t\t\ttry {\n", + "\t\t\t\t\treturn Py_BuildValue(\"d\", pisum_());\n", + "\t\t\t\t} catch (...) {\n", + "\t\t\t\t\treturn NULL;\n", + "\t\t\t\t}\n", + "\t\t}\n", + "\t\tPyObject * signatures = Py_BuildValue(\"(sO)\", \"(lp0\\n(lp1\\na.\", args);\n", + "\t\tif (!signatures) {\n", + "\t\t\tPyErr_SetString(PyExc_ValueError, \"Error building signature string for pisum\");\n", + "\t\t\treturn NULL;\n", + "\t\t}\n", + "\t\treturn PyObject_Call(create_signature, signatures, NULL);\n", + "\t}\n", + "\tPyMethodDef pisumMethods[] = {\n", + "\t\t{ \"set_create_signature\", (PyCFunction)set_create_signature, METH_VARARGS },\n", + "\t\t{ \"run\", (PyCFunction)run, METH_VARARGS },\n", + "\t\t{ NULL, NULL }\n", + "\t};\n", + "\tPyMODINIT_FUNC initpisum(void) {\n", + "\t\timport_array();\n", + "\t\tPyImport_ImportModule(\"numpy\");\n", + "\t\t(void)Py_InitModule(\"pisum\", pisumMethods);\n", + "\t}\n", + "}\n", + "#include \n", + "#include \n", + "#include \n", + "#include \n", + "#include \n", + "void sighandler(int sig) {\n", + "\tstd::ostringstream buffer;\n", + "\tbuffer << \"Abort by \" << (sig == SIGSEGV ? \"segfault\" : \"bus error\") << std::endl;\n", + "\tvoid * stack[64];\n", + "\tstd::size_t depth = backtrace(stack, 64);\n", + "\tif (!depth)\n", + "\t\tbuffer << \" \" << std::endl;\n", + "\telse {\n", + "\t\tchar ** symbols = backtrace_symbols(stack, depth);\n", + "\t\tfor (std::size_t i = 1; i < depth; ++i) {\n", + "\t\t\tstd::string symbol = symbols[i];\n", + "\t\t\t\tif (symbol.find_first_of(' ', 59) != std::string::npos) {\n", + "\t\t\t\t\tstd::string name = symbol.substr(59, symbol.find_first_of(' ', 59) - 59);\n", + "\t\t\t\t\tint status;\n", + "\t\t\t\t\tchar * demangled = abi::__cxa_demangle(name.c_str(), NULL, NULL, &status);\n", + "\t\t\t\t\tif (!status) {\n", + "\t\t\t\t\t\tbuffer << \" \" \n", + "\t\t\t\t\t\t\t<< symbol.substr(0, 59) \n", + "\t\t\t\t\t\t\t<< demangled\n", + "\t\t\t\t\t\t\t<< symbol.substr(59 + name.size())\n", + "\t\t\t\t\t\t\t<< std::endl;\n", + "\t\t\t\t\t\tfree(demangled);\n", + "\t\t\t\t\t} else\n", + "\t\t\t\t\t\tbuffer << \" \" << symbol << std::endl;\n", + "\t\t\t\t} else\n", + "\t\t\t\t\tbuffer << \" \" << symbol << std::endl;\n", + "\t\t\t}\n", + "\t\t\tfree(symbols);\n", + "\t\t}\n", + "\t\tstd::cerr << buffer.str();\n", + "\t\tstd::exit(EXIT_FAILURE);\n", + "\t}\n" + ] + }, + { + "cell_type": "code", + "execution_count": 5, + "metadata": {}, + "outputs": [ { - "cell_type": "heading", - "level": 2, - "metadata": {}, - "source": [ - "Fibonacci CPP" + "name": "stdout", + "output_type": "stream", + "text": [ + "Overwriting src/pisum_opt.cpp\n" ] - }, - { - "cell_type": "code", - "collapsed": false, - "input": [ - "%%file src/fib.cpp\n", - "#define PY_ARRAY_UNIQUE_SYMBOL fib_ARRAY_API\n", - "#define NPY_NO_DEPRECATED_API NPY_1_7_API_VERSION\n", - "#include \n", - "#include \n", - "#include \n", - "#include \n", - "#include \n", - "#include \n", - "#include \n", - "#include \n", - "#include \n", - "#include \n", - "#include \n", - "struct PyObj {\n", - "\ttypedef PyObject * ptr_t;\n", - "\ttypedef PyArrayObject * arrptr_t;\n", - "\tPyObj(): dec(false), ptr(NULL) {}\n", - "\tPyObj(ptr_t p): dec(false), ptr(p) {}\n", - "\t~PyObj() { if(dec) Py_DECREF(ptr); }\n", - "\tPyObj & operator=(ptr_t p) { if(dec) Py_DECREF(ptr); ptr = p; dec = false; return *this; }\n", - "\tPyObj & incref(ptr_t p) { if(dec) Py_DECREF(ptr); ptr = p; dec = (p != NULL); return *this; }\n", - "\toperator bool() const { return ptr; }\n", - "\toperator ptr_t() const { return ptr; }\n", - "\toperator arrptr_t() const { return (arrptr_t)ptr; }\n", - "\tbool dec;\n", - "\tptr_t ptr;\n", - "};\n", - "\n", - "\n", - "inline npy_int64 fib_J(npy_int64 cn);\n", - "\n", - "inline npy_int64 fib_J(npy_int64 cn) {\n", - "\tif (cn < 2) {\n", - "\t\treturn cn;\n", - "\t}\n", - "\treturn fib_J(cn - 1) + fib_J(cn - 2);\n", - "}\n", - "\n", - "\n", - "\n", - "void sighandler(int sig);\n", - "#include \n", - "extern \"C\" {\n", - "\tPyObject * create_signature;\n", - "\tstruct sigaction slot;\n", - "\tPyObject * set_create_signature(PyObject * self, PyObject * args) {\n", - "\t\tif (!PyArg_ParseTuple(args, \"O\", &create_signature)) {\n", - "\t\t\tPyErr_SetString(PyExc_ValueError, \"Invalid Argument to set_create_signature!\");\n", - "\t\t\treturn NULL;\n", - "\t\t}\n", - "\t\tPy_INCREF(create_signature);\n", - "\t\tmemset(&slot, 0, sizeof(slot));\n", - "\t\tslot.sa_handler = &sighandler;\n", - "\t\tsigaction(SIGSEGV, &slot, NULL);\n", - "\t\tsigaction(SIGBUS, &slot, NULL);\n", - "\t\tPy_INCREF(Py_None);\n", - "\t\treturn Py_None;\n", - "\t}\n", - "\tPyObject * run(PyObject * self, PyObject * args) {\n", - "\t\t{\n", - "\t\t\tPyObject * pn; npy_int64 cn;\n", - "\t\t\tif (\n", - "\t\t\t\tPyTuple_CheckExact(args) and PyTuple_GET_SIZE(args) == 1\n", - "\t\t\t\tand (pn = PyTuple_GET_ITEM(args, 0)) and PyInt_CheckExact(pn)\n", - "\t\t\t) {\n", - "\t\t\t\tcn = PyInt_AS_LONG(pn);\n", - "\t\t\t\ttry {\n", - "\t\t\t\t\treturn Py_BuildValue(\"l\", fib_J(\n", - "\t\t\t\t\t\t cn\n", - "\t\t\t\t\t));\n", - "\t\t\t\t} catch (...) {\n", - "\t\t\t\t\treturn NULL;\n", - "\t\t\t\t}\n", - "\t\t\t} else\n", - "\t\t\t\tPyErr_Clear();\n", - "\t\t}\n", - "\t\tPyObject * signatures = Py_BuildValue(\"(sO)\", \"(lp0\\n(lp1\\nccopy_reg\\n_reconstructor\\np2\\n(chope._ast\\nVariable\\np3\\nc__builtin__\\nobject\\np4\\nNtp5\\nRp6\\n(dp7\\nS'name'\\np8\\nS'n'\\np9\\nsS'dtype'\\np10\\nc__builtin__\\nint\\np11\\nsS'dims'\\np12\\nI0\\nsbaa.\", args);\n", - "\t\tif (!signatures) {\n", - "\t\t\tPyErr_SetString(PyExc_ValueError, \"Error building signature string for fib\");\n", - "\t\t\treturn NULL;\n", - "\t\t}\n", - "\t\treturn PyObject_Call(create_signature, signatures, NULL);\n", - "\t}\n", - "\tPyMethodDef fibMethods[] = {\n", - "\t\t{ \"set_create_signature\", (PyCFunction)set_create_signature, METH_VARARGS },\n", - "\t\t{ \"run\", (PyCFunction)run, METH_VARARGS },\n", - "\t\t{ NULL, NULL }\n", - "\t};\n", - "\tPyMODINIT_FUNC initfib(void) {\n", - "\t\timport_array();\n", - "\t\tPyImport_ImportModule(\"numpy\");\n", - "\t\t(void)Py_InitModule(\"fib\", fibMethods);\n", - "\t}\n", - "}\n", - "#include \n", - "#include \n", - "#include \n", - "#include \n", - "#include \n", - "void sighandler(int sig) {\n", - "\tstd::ostringstream buffer;\n", - "\tbuffer << \"Abort by \" << (sig == SIGSEGV ? \"segfault\" : \"bus error\") << std::endl;\n", - "\tvoid * stack[64];\n", - "\tstd::size_t depth = backtrace(stack, 64);\n", - "\tif (!depth)\n", - "\t\tbuffer << \" \" << std::endl;\n", - "\telse {\n", - "\t\tchar ** symbols = backtrace_symbols(stack, depth);\n", - "\t\tfor (std::size_t i = 1; i < depth; ++i) {\n", - "\t\t\tstd::string symbol = symbols[i];\n", - "\t\t\t\tif (symbol.find_first_of(' ', 59) != std::string::npos) {\n", - "\t\t\t\t\tstd::string name = symbol.substr(59, symbol.find_first_of(' ', 59) - 59);\n", - "\t\t\t\t\tint status;\n", - "\t\t\t\t\tchar * demangled = abi::__cxa_demangle(name.c_str(), NULL, NULL, &status);\n", - "\t\t\t\t\tif (!status) {\n", - "\t\t\t\t\t\tbuffer << \" \" \n", - "\t\t\t\t\t\t\t<< symbol.substr(0, 59) \n", - "\t\t\t\t\t\t\t<< demangled\n", - "\t\t\t\t\t\t\t<< symbol.substr(59 + name.size())\n", - "\t\t\t\t\t\t\t<< std::endl;\n", - "\t\t\t\t\t\tfree(demangled);\n", - "\t\t\t\t\t} else\n", - "\t\t\t\t\t\tbuffer << \" \" << symbol << std::endl;\n", - "\t\t\t\t} else\n", - "\t\t\t\t\tbuffer << \" \" << symbol << std::endl;\n", - "\t\t\t}\n", - "\t\t\tfree(symbols);\n", - "\t\t}\n", - "\t\tstd::cerr << buffer.str();\n", - "\t\tstd::exit(EXIT_FAILURE);\n", - "\t}\n", - "\n" - ], - "language": "python", - "metadata": {}, - "outputs": [ - { - "output_type": "stream", - "stream": "stdout", - "text": [ - "Overwriting src/fib.cpp\n" - ] - } - ], - "prompt_number": 2 - }, + } + ], + "source": [ + "%%file src/pisum_opt.cpp\n", + "#define PY_ARRAY_UNIQUE_SYMBOL pisum_opt_ARRAY_API\n", + "#define NPY_NO_DEPRECATED_API NPY_1_7_API_VERSION\n", + "#include \n", + "#include \n", + "#include \n", + "#include \n", + "#include \n", + "#include \n", + "#include \n", + "#include \n", + "#include \n", + "#include \n", + "#include \n", + "struct PyObj {\n", + "\ttypedef PyObject * ptr_t;\n", + "\ttypedef PyArrayObject * arrptr_t;\n", + "\tPyObj(): dec(false), ptr(NULL) {}\n", + "\tPyObj(ptr_t p): dec(false), ptr(p) {}\n", + "\t~PyObj() { if(dec) Py_DECREF(ptr); }\n", + "\tPyObj & operator=(ptr_t p) { if(dec) Py_DECREF(ptr); ptr = p; dec = false; return *this; }\n", + "\tPyObj & incref(ptr_t p) { if(dec) Py_DECREF(ptr); ptr = p; dec = (p != NULL); return *this; }\n", + "\toperator bool() const { return ptr; }\n", + "\toperator ptr_t() const { return ptr; }\n", + "\toperator arrptr_t() const { return (arrptr_t)ptr; }\n", + "\tbool dec;\n", + "\tptr_t ptr;\n", + "};\n", + "\n", + "inline npy_double pisum_opt_();\n", + "\n", + "inline npy_double pisum_opt_() {\n", + "\tnpy_double csum = npy_double();\n", + "\tnpy_intp ck = 1;\n", + "\tnpy_double cf = 0.0;\n", + "\t\n", + "\tfor (npy_intp cj = 1; cj < 501; ++cj) {\n", + "\t\tcsum = 0.0;\n", + "\t\tcf = 0.0;\n", + "\t\tfor (ck = 1; ck < 10001; ++ck) {\n", + "\t\t\tcf += 1.0;\n", + "\t\t\tauto c__sp0 = (cf * cf);\n", + "\t\t\tcsum += (1.0 / c__sp0);\n", + "\t\t}\n", + "\t}\n", + "\treturn csum;\n", + "}\n", + "\n", + "void sighandler(int sig);\n", + "#include \n", + "extern \"C\" {\n", + "\tPyObject * create_signature;\n", + "\tstruct sigaction slot;\n", + "\tPyObject * set_create_signature(PyObject * self, PyObject * args) {\n", + "\t\tif (!PyArg_ParseTuple(args, \"O\", &create_signature)) {\n", + "\t\t\tPyErr_SetString(PyExc_ValueError, \"Invalid Argument to set_create_signature!\");\n", + "\t\t\treturn NULL;\n", + "\t\t}\n", + "\t\tPy_INCREF(create_signature);\n", + "\t\tmemset(&slot, 0, sizeof(slot));\n", + "\t\tslot.sa_handler = &sighandler;\n", + "\t\tsigaction(SIGSEGV, &slot, NULL);\n", + "\t\tsigaction(SIGBUS, &slot, NULL);\n", + "\t\tPy_INCREF(Py_None);\n", + "\t\treturn Py_None;\n", + "\t}\n", + "\tPyObject * run(PyObject * self, PyObject * args) {\n", + "\t\t{\t\t\t\ttry {\n", + "\t\t\t\t\treturn Py_BuildValue(\"d\", pisum_opt_());\n", + "\t\t\t\t} catch (...) {\n", + "\t\t\t\t\treturn NULL;\n", + "\t\t\t\t}\n", + "\t\t}\n", + "\t\tPyObject * signatures = Py_BuildValue(\"(sO)\", \"(lp0\\n(lp1\\na.\", args);\n", + "\t\tif (!signatures) {\n", + "\t\t\tPyErr_SetString(PyExc_ValueError, \"Error building signature string for pisum_opt\");\n", + "\t\t\treturn NULL;\n", + "\t\t}\n", + "\t\treturn PyObject_Call(create_signature, signatures, NULL);\n", + "\t}\n", + "\tPyMethodDef pisum_optMethods[] = {\n", + "\t\t{ \"set_create_signature\", (PyCFunction)set_create_signature, METH_VARARGS },\n", + "\t\t{ \"run\", (PyCFunction)run, METH_VARARGS },\n", + "\t\t{ NULL, NULL }\n", + "\t};\n", + "\tPyMODINIT_FUNC initpisum_opt(void) {\n", + "\t\timport_array();\n", + "\t\tPyImport_ImportModule(\"numpy\");\n", + "\t\t(void)Py_InitModule(\"pisum_opt\", pisum_optMethods);\n", + "\t}\n", + "}\n", + "#include \n", + "#include \n", + "#include \n", + "#include \n", + "#include \n", + "void sighandler(int sig) {\n", + "\tstd::ostringstream buffer;\n", + "\tbuffer << \"Abort by \" << (sig == SIGSEGV ? \"segfault\" : \"bus error\") << std::endl;\n", + "\tvoid * stack[64];\n", + "\tstd::size_t depth = backtrace(stack, 64);\n", + "\tif (!depth)\n", + "\t\tbuffer << \" \" << std::endl;\n", + "\telse {\n", + "\t\tchar ** symbols = backtrace_symbols(stack, depth);\n", + "\t\tfor (std::size_t i = 1; i < depth; ++i) {\n", + "\t\t\tstd::string symbol = symbols[i];\n", + "\t\t\t\tif (symbol.find_first_of(' ', 59) != std::string::npos) {\n", + "\t\t\t\t\tstd::string name = symbol.substr(59, symbol.find_first_of(' ', 59) - 59);\n", + "\t\t\t\t\tint status;\n", + "\t\t\t\t\tchar * demangled = abi::__cxa_demangle(name.c_str(), NULL, NULL, &status);\n", + "\t\t\t\t\tif (!status) {\n", + "\t\t\t\t\t\tbuffer << \" \" \n", + "\t\t\t\t\t\t\t<< symbol.substr(0, 59) \n", + "\t\t\t\t\t\t\t<< demangled\n", + "\t\t\t\t\t\t\t<< symbol.substr(59 + name.size())\n", + "\t\t\t\t\t\t\t<< std::endl;\n", + "\t\t\t\t\t\tfree(demangled);\n", + "\t\t\t\t\t} else\n", + "\t\t\t\t\t\tbuffer << \" \" << symbol << std::endl;\n", + "\t\t\t\t} else\n", + "\t\t\t\t\tbuffer << \" \" << symbol << std::endl;\n", + "\t\t\t}\n", + "\t\t\tfree(symbols);\n", + "\t\t}\n", + "\t\tstd::cerr << buffer.str();\n", + "\t\tstd::exit(EXIT_FAILURE);\n", + "\t}\n" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# 10th order poly log approx CPP" + ] + }, + { + "cell_type": "code", + "execution_count": 8, + "metadata": {}, + "outputs": [ { - "cell_type": "heading", - "level": 2, - "metadata": {}, - "source": [ - "Quicksort CPP" + "name": "stdout", + "output_type": "stream", + "text": [ + "Overwriting src/ln.cpp\n" ] - }, - { - "cell_type": "code", - "collapsed": false, - "input": [ - "%%file src/qsort_kernel.cpp\n", - "#define PY_ARRAY_UNIQUE_SYMBOL qsort_kernel_ARRAY_API\n", - "#define NPY_NO_DEPRECATED_API NPY_1_7_API_VERSION\n", - "#include \n", - "#include \n", - "#include \n", - "#include \n", - "#include \n", - "#include \n", - "#include \n", - "#include \n", - "#include \n", - "#include \n", - "#include \n", - "struct PyObj {\n", - "\ttypedef PyObject * ptr_t;\n", - "\ttypedef PyArrayObject * arrptr_t;\n", - "\tPyObj(): dec(false), ptr(NULL) {}\n", - "\tPyObj(ptr_t p): dec(false), ptr(p) {}\n", - "\t~PyObj() { if(dec) Py_DECREF(ptr); }\n", - "\tPyObj & operator=(ptr_t p) { if(dec) Py_DECREF(ptr); ptr = p; dec = false; return *this; }\n", - "\tPyObj & incref(ptr_t p) { if(dec) Py_DECREF(ptr); ptr = p; dec = (p != NULL); return *this; }\n", - "\toperator bool() const { return ptr; }\n", - "\toperator ptr_t() const { return ptr; }\n", - "\toperator arrptr_t() const { return (arrptr_t)ptr; }\n", - "\tbool dec;\n", - "\tptr_t ptr;\n", - "};\n", - "inline std::tuple qsort_kernel_d1JJ(PyObject * pa, \n", - "\t \t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tnpy_intp const * __restrict__ sa, \n", - "\t \t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tnpy_double * __restrict__ ca, \n", - "\t \t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tnpy_int64 clo, \n", - "\t \t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tnpy_int64 chi);\n", - "\t \t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\n", - "inline std::tuple qsort_kernel_d1JJ(PyObject * pa, \n", - "\t \t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tnpy_intp const * __restrict__ sa, \n", - "\t \t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tnpy_double * __restrict__ ca, \n", - "\t \t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tnpy_int64 clo, \n", - "\t \t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tnpy_int64 chi){\n", - "\tnpy_int64 ci = clo;\n", - "\tnpy_int64 cj = chi;\n", - "\tnpy_double cpivot;\n", - "\n", - "\twhile (ci < chi) {\n", - "\t\tcpivot = ca[(int)((clo + chi) / 2)];\n", - "\t\t\n", - "\t\twhile (ci <= cj) {\n", - "\t\t\twhile (ca[ci] < cpivot) {\n", - "\t\t\t\tci += 1;\n", - "\t\t\t}\n", - "\t\t\t\n", - "\t\t\twhile (ca[cj] > cpivot) {\n", - "\t\t\t\tcj -= 1;\n", - "\t\t\t}\n", - "\t\t\t\n", - "\t\t\tif (ci <= cj) {\n", - "\t\t\t\tauto ctmp = ca[ci];\n", - "\t\t\t\tca[ci] = ca[cj];\n", - "\t\t\t\tca[cj] = ctmp;\n", - "\t\t\t\tci += 1;\n", - "\t\t\t\tcj -= 1;\n", - "\t\t\t}\n", - "\t\t}\n", - "\t\t\n", - "\t\tif (clo < cj) {\n", - "\t\t\tqsort_kernel_d1JJ(pa, sa, ca, clo, cj);\n", - "\t\t}\n", - "\t\t\n", - "\t\tclo = ci;\n", - "\t\tcj = chi;\n", - "\t}\n", - "\t\n", - "\treturn std::make_tuple((PyObject *)pa, sa, ca);\n", - "}\n", - "\n", - "void sighandler(int sig);\n", - "#include \n", - "extern \"C\" {\n", - "\tPyObject * create_signature;\n", - "\tstruct sigaction slot;\n", - "\tPyObject * set_create_signature(PyObject * self, PyObject * args) {\n", - "\t\tif (!PyArg_ParseTuple(args, \"O\", &create_signature)) {\n", - "\t\t\tPyErr_SetString(PyExc_ValueError, \"Invalid Argument to set_create_signature!\");\n", - "\t\t\treturn NULL;\n", - "\t\t}\n", - "\t\tPy_INCREF(create_signature);\n", - "\t\tmemset(&slot, 0, sizeof(slot));\n", - "\t\tslot.sa_handler = &sighandler;\n", - "\t\tsigaction(SIGSEGV, &slot, NULL);\n", - "\t\tsigaction(SIGBUS, &slot, NULL);\n", - "\t\tPy_INCREF(Py_None);\n", - "\t\treturn Py_None;\n", - "\t}\n", - "\tPyObject * run(PyObject * self, PyObject * args) {\n", - "\t\t{\n", - "\t\t\tPyObj pa;\n", - "\t\t\tPyObject * plo; npy_int64 clo;\n", - "\t\t\tPyObject * phi; npy_int64 chi;\n", - "\t\t\tif (\n", - "\t\t\t\tPyTuple_CheckExact(args) and PyTuple_GET_SIZE(args) == 3\n", - "\t\t\t\tand (pa = PyTuple_GET_ITEM(args, 0)) and PyArray_CheckExact(pa)\n", - "\t\t\t\tand PyArray_TYPE((PyArrayObject *)pa) == NPY_FLOAT64 and PyArray_NDIM((PyArrayObject *)pa) == 1\n", - "\t\t\t\tand (plo = PyTuple_GET_ITEM(args, 1)) and PyInt_CheckExact(plo)\n", - "\t\t\t\tand (phi = PyTuple_GET_ITEM(args, 2)) and PyInt_CheckExact(phi)\n", - "\t\t\t) {\n", - "\t\t\t\tif (!(pa.incref((PyObject *)PyArray_GETCONTIGUOUS((PyArrayObject *)pa)))) {\n", - "\t\t\t\t\tPyErr_SetString(PyExc_ValueError, \"Invalid Argument type on a!\");\n", - "\t\t\t\t\treturn NULL;\n", - "\t\t\t\t}\n", - "\t\t\t\tclo = PyInt_AS_LONG(plo);\n", - "\t\t\t\tchi = PyInt_AS_LONG(phi);\n", - "\t\t\t\ttry {\n", - "\t\t\t\t\tPyObject * res = std::get<0>(qsort_kernel_d1JJ(\n", - "\t\t\t\t\t\t pa, PyArray_SHAPE((PyArrayObject *)pa), (npy_double *)PyArray_DATA((PyArrayObject *)pa)\n", - "\t\t\t\t\t\t, clo\n", - "\t\t\t\t\t\t, chi\n", - "\t\t\t\t\t));\n", - "\n", - "\t\t\t\t\tPy_INCREF(res);\n", - "\t\t\t\t\treturn res;\n", - "\t\t\t\t} catch (...) {\n", - "\t\t\t\t\treturn NULL;\n", - "\t\t\t\t}\n", - "\t\t\t} else\n", - "\t\t\t\tPyErr_Clear();\n", - "\t\t}\n", - "\t\tPyObject * signatures = Py_BuildValue(\"(sO)\", \"(lp0\\n(lp1\\nccopy_reg\\n_reconstructor\\np2\\n(chope._ast\\nVariable\\np3\\nc__builtin__\\nobject\\np4\\nNtp5\\nRp6\\n(dp7\\nS'name'\\np8\\nS'a'\\np9\\nsS'dtype'\\np10\\nS'float64'\\np11\\nsS'dims'\\np12\\nI1\\nsbag2\\n(g3\\ng4\\nNtp13\\nRp14\\n(dp15\\ng8\\nS'lo'\\np16\\nsg10\\nc__builtin__\\nint\\np17\\nsg12\\nI0\\nsbag2\\n(g3\\ng4\\nNtp18\\nRp19\\n(dp20\\ng8\\nS'hi'\\np21\\nsg10\\ng17\\nsg12\\nI0\\nsbaa.\", args);\n", - "\t\tif (!signatures) {\n", - "\t\t\tPyErr_SetString(PyExc_ValueError, \"Error building signature string for qsort_kernel\");\n", - "\t\t\treturn NULL;\n", - "\t\t}\n", - "\t\treturn PyObject_Call(create_signature, signatures, NULL);\n", - "\t}\n", - "\tPyMethodDef qsort_kernelMethods[] = {\n", - "\t\t{ \"set_create_signature\", (PyCFunction)set_create_signature, METH_VARARGS },\n", - "\t\t{ \"run\", (PyCFunction)run, METH_VARARGS },\n", - "\t\t{ NULL, NULL }\n", - "\t};\n", - "\tPyMODINIT_FUNC initqsort_kernel(void) {\n", - "\t\timport_array();\n", - "\t\tPyImport_ImportModule(\"numpy\");\n", - "\t\t(void)Py_InitModule(\"qsort_kernel\", qsort_kernelMethods);\n", - "\t}\n", - "}\n", - "#include \n", - "#include \n", - "#include \n", - "#include \n", - "#include \n", - "void sighandler(int sig) {\n", - "\tstd::ostringstream buffer;\n", - "\tbuffer << \"Abort by \" << (sig == SIGSEGV ? \"segfault\" : \"bus error\") << std::endl;\n", - "\tvoid * stack[64];\n", - "\tstd::size_t depth = backtrace(stack, 64);\n", - "\tif (!depth)\n", - "\t\tbuffer << \" \" << std::endl;\n", - "\telse {\n", - "\t\tchar ** symbols = backtrace_symbols(stack, depth);\n", - "\t\tfor (std::size_t i = 1; i < depth; ++i) {\n", - "\t\t\tstd::string symbol = symbols[i];\n", - "\t\t\t\tif (symbol.find_first_of(' ', 59) != std::string::npos) {\n", - "\t\t\t\t\tstd::string name = symbol.substr(59, symbol.find_first_of(' ', 59) - 59);\n", - "\t\t\t\t\tint status;\n", - "\t\t\t\t\tchar * demangled = abi::__cxa_demangle(name.c_str(), NULL, NULL, &status);\n", - "\t\t\t\t\tif (!status) {\n", - "\t\t\t\t\t\tbuffer << \" \" \n", - "\t\t\t\t\t\t\t<< symbol.substr(0, 59) \n", - "\t\t\t\t\t\t\t<< demangled\n", - "\t\t\t\t\t\t\t<< symbol.substr(59 + name.size())\n", - "\t\t\t\t\t\t\t<< std::endl;\n", - "\t\t\t\t\t\tfree(demangled);\n", - "\t\t\t\t\t} else\n", - "\t\t\t\t\t\tbuffer << \" \" << symbol << std::endl;\n", - "\t\t\t\t} else\n", - "\t\t\t\t\tbuffer << \" \" << symbol << std::endl;\n", - "\t\t\t}\n", - "\t\t\tfree(symbols);\n", - "\t\t}\n", - "\t\tstd::cerr << buffer.str();\n", - "\t\tstd::exit(EXIT_FAILURE);\n", - "\t}\n" - ], - "language": "python", - "metadata": {}, - "outputs": [ - { - "output_type": "stream", - "stream": "stdout", - "text": [ - "Overwriting src/qsort_kernel.cpp\n" - ] - } - ], - "prompt_number": 3 - }, + } + ], + "source": [ + "%%file src/ln.cpp\n", + "#define PY_ARRAY_UNIQUE_SYMBOL ln_hope_ARRAY_API\n", + "#define NPY_NO_DEPRECATED_API NPY_1_7_API_VERSION\n", + "#include \n", + "#include \n", + "#include \n", + "#include \n", + "#include \n", + "#include \n", + "#include \n", + "#include \n", + "#include \n", + "#include \n", + "#include \n", + "struct PyObj {\n", + "\ttypedef PyObject * ptr_t;\n", + "\ttypedef PyArrayObject * arrptr_t;\n", + "\tPyObj(): dec(false), ptr(NULL) {}\n", + "\tPyObj(ptr_t p): dec(false), ptr(p) {}\n", + "\t~PyObj() { if(dec) Py_DECREF(ptr); }\n", + "\tPyObj & operator=(ptr_t p) { if(dec) Py_DECREF(ptr); ptr = p; dec = false; return *this; }\n", + "\tPyObj & incref(ptr_t p) { if(dec) Py_DECREF(ptr); ptr = p; dec = (p != NULL); return *this; }\n", + "\toperator bool() const { return ptr; }\n", + "\toperator ptr_t() const { return ptr; }\n", + "\toperator arrptr_t() const { return (arrptr_t)ptr; }\n", + "\tbool dec;\n", + "\tptr_t ptr;\n", + "};\n", + "\n", + "inline void ln_hope_d1d1(PyObject * pX, npy_intp const * __restrict__ sX, npy_double * __restrict__ cX, \n", + "\t\t\t\t\t\t\t\t\t\t\t\t\tPyObject * pY, npy_intp const * __restrict__ sY, npy_double * __restrict__ cY);\n", + "\n", + "inline void ln_hope_d1d1(PyObject * pX, npy_intp const * __restrict__ sX, npy_double * __restrict__ cX, \n", + "\t\t\t\t\t\t\t\t\t\t\t\t\tPyObject * pY, npy_intp const * __restrict__ sY, npy_double * __restrict__ cY){\n", + "\t\n", + "\tfor (npy_intp i0 = 0; i0 < sY[0] - 0; ++i0) {\n", + "\t\tcY[(int)(i0)] = (cX[i0] - 1) - (std::pow((cX[i0] - 1), 2) / 2) + (std::pow((cX[i0] - 1), 3) / 3) - (std::pow((cX[i0] - 1), 4) / 4) + (std::pow((cX[i0] - 1), 5) / 5) - (std::pow((cX[i0] - 1), 6) / 6) + (std::pow((cX[i0] - 1), 7) / 7) - (std::pow((cX[i0] - 1), 8) / 8) + (std::pow((cX[i0] - 1), 9) / 9);\n", + "\t}\n", + "}\n", + "\n", + "void sighandler(int sig);\n", + "#include \n", + "extern \"C\" {\n", + "\tPyObject * create_signature;\n", + "\tstruct sigaction slot;\n", + "\tPyObject * set_create_signature(PyObject * self, PyObject * args) {\n", + "\t\tif (!PyArg_ParseTuple(args, \"O\", &create_signature)) {\n", + "\t\t\tPyErr_SetString(PyExc_ValueError, \"Invalid Argument to set_create_signature!\");\n", + "\t\t\treturn NULL;\n", + "\t\t}\n", + "\t\tPy_INCREF(create_signature);\n", + "\t\tmemset(&slot, 0, sizeof(slot));\n", + "\t\tslot.sa_handler = &sighandler;\n", + "\t\tsigaction(SIGSEGV, &slot, NULL);\n", + "\t\tsigaction(SIGBUS, &slot, NULL);\n", + "\t\tPy_INCREF(Py_None);\n", + "\t\treturn Py_None;\n", + "\t}\n", + "\tPyObject * run(PyObject * self, PyObject * args) {\n", + "\t\t{\n", + "\t\t\tPyObj pX;\n", + "\t\t\tPyObj pY;\n", + "\t\t\tif (\n", + "\t\t\t\tPyTuple_CheckExact(args) and PyTuple_GET_SIZE(args) == 2\n", + "\t\t\t\tand (pX = PyTuple_GET_ITEM(args, 0)) and PyArray_CheckExact(pX)\n", + "\t\t\t\tand PyArray_TYPE((PyArrayObject *)pX) == NPY_FLOAT64 and PyArray_NDIM((PyArrayObject *)pX) == 1\n", + "\t\t\t\tand (pY = PyTuple_GET_ITEM(args, 1)) and PyArray_CheckExact(pY)\n", + "\t\t\t\tand PyArray_TYPE((PyArrayObject *)pY) == NPY_FLOAT64 and PyArray_NDIM((PyArrayObject *)pY) == 1\n", + "\t\t\t) {\n", + "\t\t\t\tif (!(pX.incref((PyObject *)PyArray_GETCONTIGUOUS((PyArrayObject *)pX)))) {\n", + "\t\t\t\t\tPyErr_SetString(PyExc_ValueError, \"Invalid Argument type on X!\");\n", + "\t\t\t\t\treturn NULL;\n", + "\t\t\t\t}\n", + "\t\t\t\tif (!(pY.incref((PyObject *)PyArray_GETCONTIGUOUS((PyArrayObject *)pY)))) {\n", + "\t\t\t\t\tPyErr_SetString(PyExc_ValueError, \"Invalid Argument type on Y!\");\n", + "\t\t\t\t\treturn NULL;\n", + "\t\t\t\t}\n", + "\t\t\t\ttry {\n", + "\t\t\t\t\tln_hope_d1d1(\n", + "\t\t\t\t\t\t pX, PyArray_SHAPE((PyArrayObject *)pX), (npy_double *)PyArray_DATA((PyArrayObject *)pX)\n", + "\t\t\t\t\t\t, pY, PyArray_SHAPE((PyArrayObject *)pY), (npy_double *)PyArray_DATA((PyArrayObject *)pY)\n", + "\t\t\t\t\t);\n", + "\t\t\t\t\tPy_INCREF(Py_None);\n", + "\t\t\t\t\treturn Py_None;\n", + "\t\t\t\t} catch (...) {\n", + "\t\t\t\t\treturn NULL;\n", + "\t\t\t\t}\n", + "\t\t\t} else\n", + "\t\t\t\tPyErr_Clear();\n", + "\t\t}\n", + "\t\tPyObject * signatures = Py_BuildValue(\"(sO)\", \"(lp0\\n(lp1\\nccopy_reg\\n_reconstructor\\np2\\n(chope._ast\\nVariable\\np3\\nc__builtin__\\nobject\\np4\\nNtp5\\nRp6\\n(dp7\\nS'name'\\np8\\nS'X'\\np9\\nsS'dtype'\\np10\\nS'float64'\\np11\\nsS'dims'\\np12\\nI1\\nsbag2\\n(g3\\ng4\\nNtp13\\nRp14\\n(dp15\\ng8\\nS'Y'\\np16\\nsg10\\ng11\\nsg12\\nI1\\nsbaa.\", args);\n", + "\t\tif (!signatures) {\n", + "\t\t\tPyErr_SetString(PyExc_ValueError, \"Error building signature string for ln_hope\");\n", + "\t\t\treturn NULL;\n", + "\t\t}\n", + "\t\treturn PyObject_Call(create_signature, signatures, NULL);\n", + "\t}\n", + "\tPyMethodDef ln_hopeMethods[] = {\n", + "\t\t{ \"set_create_signature\", (PyCFunction)set_create_signature, METH_VARARGS },\n", + "\t\t{ \"run\", (PyCFunction)run, METH_VARARGS },\n", + "\t\t{ NULL, NULL }\n", + "\t};\n", + "\tPyMODINIT_FUNC initln(void) {\n", + "\t\timport_array();\n", + "\t\tPyImport_ImportModule(\"numpy\");\n", + "\t\t(void)Py_InitModule(\"ln\", ln_hopeMethods);\n", + "\t}\n", + "}\n", + "#include \n", + "#include \n", + "#include \n", + "#include \n", + "#include \n", + "void sighandler(int sig) {\n", + "\tstd::ostringstream buffer;\n", + "\tbuffer << \"Abort by \" << (sig == SIGSEGV ? \"segfault\" : \"bus error\") << std::endl;\n", + "\tvoid * stack[64];\n", + "\tstd::size_t depth = backtrace(stack, 64);\n", + "\tif (!depth)\n", + "\t\tbuffer << \" \" << std::endl;\n", + "\telse {\n", + "\t\tchar ** symbols = backtrace_symbols(stack, depth);\n", + "\t\tfor (std::size_t i = 1; i < depth; ++i) {\n", + "\t\t\tstd::string symbol = symbols[i];\n", + "\t\t\t\tif (symbol.find_first_of(' ', 59) != std::string::npos) {\n", + "\t\t\t\t\tstd::string name = symbol.substr(59, symbol.find_first_of(' ', 59) - 59);\n", + "\t\t\t\t\tint status;\n", + "\t\t\t\t\tchar * demangled = abi::__cxa_demangle(name.c_str(), NULL, NULL, &status);\n", + "\t\t\t\t\tif (!status) {\n", + "\t\t\t\t\t\tbuffer << \" \" \n", + "\t\t\t\t\t\t\t<< symbol.substr(0, 59) \n", + "\t\t\t\t\t\t\t<< demangled\n", + "\t\t\t\t\t\t\t<< symbol.substr(59 + name.size())\n", + "\t\t\t\t\t\t\t<< std::endl;\n", + "\t\t\t\t\t\tfree(demangled);\n", + "\t\t\t\t\t} else\n", + "\t\t\t\t\t\tbuffer << \" \" << symbol << std::endl;\n", + "\t\t\t\t} else\n", + "\t\t\t\t\tbuffer << \" \" << symbol << std::endl;\n", + "\t\t\t}\n", + "\t\t\tfree(symbols);\n", + "\t\t}\n", + "\t\tstd::cerr << buffer.str();\n", + "\t\tstd::exit(EXIT_FAILURE);\n", + "\t}\n" + ] + }, + { + "cell_type": "code", + "execution_count": 9, + "metadata": {}, + "outputs": [ { - "cell_type": "heading", - "level": 2, - "metadata": {}, - "source": [ - "Pi sum CPP" + "name": "stdout", + "output_type": "stream", + "text": [ + "Overwriting src/ln_exp.cpp\n" ] - }, - { - "cell_type": "code", - "collapsed": false, - "input": [ - "%%file src/pisum.cpp\n", - "#define PY_ARRAY_UNIQUE_SYMBOL pisum_ARRAY_API\n", - "#define NPY_NO_DEPRECATED_API NPY_1_7_API_VERSION\n", - "#include \n", - "#include \n", - "#include \n", - "#include \n", - "#include \n", - "#include \n", - "#include \n", - "#include \n", - "#include \n", - "#include \n", - "#include \n", - "struct PyObj {\n", - "\ttypedef PyObject * ptr_t;\n", - "\ttypedef PyArrayObject * arrptr_t;\n", - "\tPyObj(): dec(false), ptr(NULL) {}\n", - "\tPyObj(ptr_t p): dec(false), ptr(p) {}\n", - "\t~PyObj() { if(dec) Py_DECREF(ptr); }\n", - "\tPyObj & operator=(ptr_t p) { if(dec) Py_DECREF(ptr); ptr = p; dec = false; return *this; }\n", - "\tPyObj & incref(ptr_t p) { if(dec) Py_DECREF(ptr); ptr = p; dec = (p != NULL); return *this; }\n", - "\toperator bool() const { return ptr; }\n", - "\toperator ptr_t() const { return ptr; }\n", - "\toperator arrptr_t() const { return (arrptr_t)ptr; }\n", - "\tbool dec;\n", - "\tptr_t ptr;\n", - "};\n", - "\n", - "inline npy_double pisum_();\n", - "\n", - "inline npy_double pisum_() {\n", - "\tnpy_double csum = npy_double();\n", - "\tnpy_intp ck = 1;\n", - "\tfor (npy_intp cj = 1; cj < 501; ++cj) {\n", - "\t\tcsum = 0.0;\n", - "\t\tauto cf = 0.0;\n", - "\t\tfor (ck = 1; ck < 10001; ++ck) {\n", - "\t\t\tauto c__sp0 = (ck * ck);\n", - "\t\t\tcsum += (1.0 / c__sp0);\n", - "\t\t}\n", - "\t}\n", - "\t\n", - "\treturn csum;\n", - "}\n", - "\n", - "void sighandler(int sig);\n", - "#include \n", - "extern \"C\" {\n", - "\tPyObject * create_signature;\n", - "\tstruct sigaction slot;\n", - "\tPyObject * set_create_signature(PyObject * self, PyObject * args) {\n", - "\t\tif (!PyArg_ParseTuple(args, \"O\", &create_signature)) {\n", - "\t\t\tPyErr_SetString(PyExc_ValueError, \"Invalid Argument to set_create_signature!\");\n", - "\t\t\treturn NULL;\n", - "\t\t}\n", - "\t\tPy_INCREF(create_signature);\n", - "\t\tmemset(&slot, 0, sizeof(slot));\n", - "\t\tslot.sa_handler = &sighandler;\n", - "\t\tsigaction(SIGSEGV, &slot, NULL);\n", - "\t\tsigaction(SIGBUS, &slot, NULL);\n", - "\t\tPy_INCREF(Py_None);\n", - "\t\treturn Py_None;\n", - "\t}\n", - "\tPyObject * run(PyObject * self, PyObject * args) {\n", - "\t\t{\t\t\t\ttry {\n", - "\t\t\t\t\treturn Py_BuildValue(\"d\", pisum_());\n", - "\t\t\t\t} catch (...) {\n", - "\t\t\t\t\treturn NULL;\n", - "\t\t\t\t}\n", - "\t\t}\n", - "\t\tPyObject * signatures = Py_BuildValue(\"(sO)\", \"(lp0\\n(lp1\\na.\", args);\n", - "\t\tif (!signatures) {\n", - "\t\t\tPyErr_SetString(PyExc_ValueError, \"Error building signature string for pisum\");\n", - "\t\t\treturn NULL;\n", - "\t\t}\n", - "\t\treturn PyObject_Call(create_signature, signatures, NULL);\n", - "\t}\n", - "\tPyMethodDef pisumMethods[] = {\n", - "\t\t{ \"set_create_signature\", (PyCFunction)set_create_signature, METH_VARARGS },\n", - "\t\t{ \"run\", (PyCFunction)run, METH_VARARGS },\n", - "\t\t{ NULL, NULL }\n", - "\t};\n", - "\tPyMODINIT_FUNC initpisum(void) {\n", - "\t\timport_array();\n", - "\t\tPyImport_ImportModule(\"numpy\");\n", - "\t\t(void)Py_InitModule(\"pisum\", pisumMethods);\n", - "\t}\n", - "}\n", - "#include \n", - "#include \n", - "#include \n", - "#include \n", - "#include \n", - "void sighandler(int sig) {\n", - "\tstd::ostringstream buffer;\n", - "\tbuffer << \"Abort by \" << (sig == SIGSEGV ? \"segfault\" : \"bus error\") << std::endl;\n", - "\tvoid * stack[64];\n", - "\tstd::size_t depth = backtrace(stack, 64);\n", - "\tif (!depth)\n", - "\t\tbuffer << \" \" << std::endl;\n", - "\telse {\n", - "\t\tchar ** symbols = backtrace_symbols(stack, depth);\n", - "\t\tfor (std::size_t i = 1; i < depth; ++i) {\n", - "\t\t\tstd::string symbol = symbols[i];\n", - "\t\t\t\tif (symbol.find_first_of(' ', 59) != std::string::npos) {\n", - "\t\t\t\t\tstd::string name = symbol.substr(59, symbol.find_first_of(' ', 59) - 59);\n", - "\t\t\t\t\tint status;\n", - "\t\t\t\t\tchar * demangled = abi::__cxa_demangle(name.c_str(), NULL, NULL, &status);\n", - "\t\t\t\t\tif (!status) {\n", - "\t\t\t\t\t\tbuffer << \" \" \n", - "\t\t\t\t\t\t\t<< symbol.substr(0, 59) \n", - "\t\t\t\t\t\t\t<< demangled\n", - "\t\t\t\t\t\t\t<< symbol.substr(59 + name.size())\n", - "\t\t\t\t\t\t\t<< std::endl;\n", - "\t\t\t\t\t\tfree(demangled);\n", - "\t\t\t\t\t} else\n", - "\t\t\t\t\t\tbuffer << \" \" << symbol << std::endl;\n", - "\t\t\t\t} else\n", - "\t\t\t\t\tbuffer << \" \" << symbol << std::endl;\n", - "\t\t\t}\n", - "\t\t\tfree(symbols);\n", - "\t\t}\n", - "\t\tstd::cerr << buffer.str();\n", - "\t\tstd::exit(EXIT_FAILURE);\n", - "\t}\n" - ], - "language": "python", - "metadata": {}, - "outputs": [ - { - "output_type": "stream", - "stream": "stdout", - "text": [ - "Overwriting src/pisum.cpp\n" - ] - } - ], - "prompt_number": 4 - }, - { - "cell_type": "code", - "collapsed": false, - "input": [ - "%%file src/pisum_opt.cpp\n", - "#define PY_ARRAY_UNIQUE_SYMBOL pisum_opt_ARRAY_API\n", - "#define NPY_NO_DEPRECATED_API NPY_1_7_API_VERSION\n", - "#include \n", - "#include \n", - "#include \n", - "#include \n", - "#include \n", - "#include \n", - "#include \n", - "#include \n", - "#include \n", - "#include \n", - "#include \n", - "struct PyObj {\n", - "\ttypedef PyObject * ptr_t;\n", - "\ttypedef PyArrayObject * arrptr_t;\n", - "\tPyObj(): dec(false), ptr(NULL) {}\n", - "\tPyObj(ptr_t p): dec(false), ptr(p) {}\n", - "\t~PyObj() { if(dec) Py_DECREF(ptr); }\n", - "\tPyObj & operator=(ptr_t p) { if(dec) Py_DECREF(ptr); ptr = p; dec = false; return *this; }\n", - "\tPyObj & incref(ptr_t p) { if(dec) Py_DECREF(ptr); ptr = p; dec = (p != NULL); return *this; }\n", - "\toperator bool() const { return ptr; }\n", - "\toperator ptr_t() const { return ptr; }\n", - "\toperator arrptr_t() const { return (arrptr_t)ptr; }\n", - "\tbool dec;\n", - "\tptr_t ptr;\n", - "};\n", - "\n", - "inline npy_double pisum_opt_();\n", - "\n", - "inline npy_double pisum_opt_() {\n", - "\tnpy_double csum = npy_double();\n", - "\tnpy_intp ck = 1;\n", - "\tnpy_double cf = 0.0;\n", - "\t\n", - "\tfor (npy_intp cj = 1; cj < 501; ++cj) {\n", - "\t\tcsum = 0.0;\n", - "\t\tcf = 0.0;\n", - "\t\tfor (ck = 1; ck < 10001; ++ck) {\n", - "\t\t\tcf += 1.0;\n", - "\t\t\tauto c__sp0 = (cf * cf);\n", - "\t\t\tcsum += (1.0 / c__sp0);\n", - "\t\t}\n", - "\t}\n", - "\treturn csum;\n", - "}\n", - "\n", - "void sighandler(int sig);\n", - "#include \n", - "extern \"C\" {\n", - "\tPyObject * create_signature;\n", - "\tstruct sigaction slot;\n", - "\tPyObject * set_create_signature(PyObject * self, PyObject * args) {\n", - "\t\tif (!PyArg_ParseTuple(args, \"O\", &create_signature)) {\n", - "\t\t\tPyErr_SetString(PyExc_ValueError, \"Invalid Argument to set_create_signature!\");\n", - "\t\t\treturn NULL;\n", - "\t\t}\n", - "\t\tPy_INCREF(create_signature);\n", - "\t\tmemset(&slot, 0, sizeof(slot));\n", - "\t\tslot.sa_handler = &sighandler;\n", - "\t\tsigaction(SIGSEGV, &slot, NULL);\n", - "\t\tsigaction(SIGBUS, &slot, NULL);\n", - "\t\tPy_INCREF(Py_None);\n", - "\t\treturn Py_None;\n", - "\t}\n", - "\tPyObject * run(PyObject * self, PyObject * args) {\n", - "\t\t{\t\t\t\ttry {\n", - "\t\t\t\t\treturn Py_BuildValue(\"d\", pisum_opt_());\n", - "\t\t\t\t} catch (...) {\n", - "\t\t\t\t\treturn NULL;\n", - "\t\t\t\t}\n", - "\t\t}\n", - "\t\tPyObject * signatures = Py_BuildValue(\"(sO)\", \"(lp0\\n(lp1\\na.\", args);\n", - "\t\tif (!signatures) {\n", - "\t\t\tPyErr_SetString(PyExc_ValueError, \"Error building signature string for pisum_opt\");\n", - "\t\t\treturn NULL;\n", - "\t\t}\n", - "\t\treturn PyObject_Call(create_signature, signatures, NULL);\n", - "\t}\n", - "\tPyMethodDef pisum_optMethods[] = {\n", - "\t\t{ \"set_create_signature\", (PyCFunction)set_create_signature, METH_VARARGS },\n", - "\t\t{ \"run\", (PyCFunction)run, METH_VARARGS },\n", - "\t\t{ NULL, NULL }\n", - "\t};\n", - "\tPyMODINIT_FUNC initpisum_opt(void) {\n", - "\t\timport_array();\n", - "\t\tPyImport_ImportModule(\"numpy\");\n", - "\t\t(void)Py_InitModule(\"pisum_opt\", pisum_optMethods);\n", - "\t}\n", - "}\n", - "#include \n", - "#include \n", - "#include \n", - "#include \n", - "#include \n", - "void sighandler(int sig) {\n", - "\tstd::ostringstream buffer;\n", - "\tbuffer << \"Abort by \" << (sig == SIGSEGV ? \"segfault\" : \"bus error\") << std::endl;\n", - "\tvoid * stack[64];\n", - "\tstd::size_t depth = backtrace(stack, 64);\n", - "\tif (!depth)\n", - "\t\tbuffer << \" \" << std::endl;\n", - "\telse {\n", - "\t\tchar ** symbols = backtrace_symbols(stack, depth);\n", - "\t\tfor (std::size_t i = 1; i < depth; ++i) {\n", - "\t\t\tstd::string symbol = symbols[i];\n", - "\t\t\t\tif (symbol.find_first_of(' ', 59) != std::string::npos) {\n", - "\t\t\t\t\tstd::string name = symbol.substr(59, symbol.find_first_of(' ', 59) - 59);\n", - "\t\t\t\t\tint status;\n", - "\t\t\t\t\tchar * demangled = abi::__cxa_demangle(name.c_str(), NULL, NULL, &status);\n", - "\t\t\t\t\tif (!status) {\n", - "\t\t\t\t\t\tbuffer << \" \" \n", - "\t\t\t\t\t\t\t<< symbol.substr(0, 59) \n", - "\t\t\t\t\t\t\t<< demangled\n", - "\t\t\t\t\t\t\t<< symbol.substr(59 + name.size())\n", - "\t\t\t\t\t\t\t<< std::endl;\n", - "\t\t\t\t\t\tfree(demangled);\n", - "\t\t\t\t\t} else\n", - "\t\t\t\t\t\tbuffer << \" \" << symbol << std::endl;\n", - "\t\t\t\t} else\n", - "\t\t\t\t\tbuffer << \" \" << symbol << std::endl;\n", - "\t\t\t}\n", - "\t\t\tfree(symbols);\n", - "\t\t}\n", - "\t\tstd::cerr << buffer.str();\n", - "\t\tstd::exit(EXIT_FAILURE);\n", - "\t}\n" - ], - "language": "python", - "metadata": {}, - "outputs": [ - { - "output_type": "stream", - "stream": "stdout", - "text": [ - "Overwriting src/pisum_opt.cpp\n" - ] - } - ], - "prompt_number": 5 - }, + } + ], + "source": [ + "%%file src/ln_exp.cpp\n", + "#define PY_ARRAY_UNIQUE_SYMBOL ln_hope_exp_ARRAY_API\n", + "#define NPY_NO_DEPRECATED_API NPY_1_7_API_VERSION\n", + "#include \n", + "#include \n", + "#include \n", + "#include \n", + "#include \n", + "#include \n", + "#include \n", + "#include \n", + "#include \n", + "#include \n", + "#include \n", + "struct PyObj {\n", + "\ttypedef PyObject * ptr_t;\n", + "\ttypedef PyArrayObject * arrptr_t;\n", + "\tPyObj(): dec(false), ptr(NULL) {}\n", + "\tPyObj(ptr_t p): dec(false), ptr(p) {}\n", + "\t~PyObj() { if(dec) Py_DECREF(ptr); }\n", + "\tPyObj & operator=(ptr_t p) { if(dec) Py_DECREF(ptr); ptr = p; dec = false; return *this; }\n", + "\tPyObj & incref(ptr_t p) { if(dec) Py_DECREF(ptr); ptr = p; dec = (p != NULL); return *this; }\n", + "\toperator bool() const { return ptr; }\n", + "\toperator ptr_t() const { return ptr; }\n", + "\toperator arrptr_t() const { return (arrptr_t)ptr; }\n", + "\tbool dec;\n", + "\tptr_t ptr;\n", + "};\n", + "\n", + "inline void ln_hope_exp_d1d1(PyObject * pX, npy_intp const * __restrict__ sX, npy_double * __restrict__ cX, \n", + "\t\t\t\t\t\t\t\t\t\t\t\t\tPyObject * pY, npy_intp const * __restrict__ sY, npy_double * __restrict__ cY);\n", + "\n", + "inline void ln_hope_exp_d1d1(PyObject * pX, npy_intp const * __restrict__ sX, npy_double * __restrict__ cX, \n", + "\t\t\t\t\t\t\t\t\t\t\t\t\tPyObject * pY, npy_intp const * __restrict__ sY, npy_double * __restrict__ cY){\n", + "\n", + "\tfor (npy_intp i0 = 0; i0 < sX[0] - 0; ++i0) {\n", + "\t\tauto cx = (cX[i0] - 1);\n", + "\t\tauto cx2 = (cx * cx);\n", + "\t\tauto cx4 = (cx2 * cx2);\n", + "\t\tauto cx6 = (cx4 * cx2);\n", + "\t\tauto cx8 = (cx4 * cx4);\n", + "\t\tcY[i0] = cx - (cx2 / 2) + (cx * cx2 / 3) - (cx4 / 4) + (cx * cx4 / 5) - (cx6 / 6) + (cx6 * cx / 7) - (cx8 / 8) + (cx8 * cx / 9);\n", + "\t}\n", + "}\n", + "\n", + "void sighandler(int sig);\n", + "#include \n", + "extern \"C\" {\n", + "\tPyObject * create_signature;\n", + "\tstruct sigaction slot;\n", + "\tPyObject * set_create_signature(PyObject * self, PyObject * args) {\n", + "\t\tif (!PyArg_ParseTuple(args, \"O\", &create_signature)) {\n", + "\t\t\tPyErr_SetString(PyExc_ValueError, \"Invalid Argument to set_create_signature!\");\n", + "\t\t\treturn NULL;\n", + "\t\t}\n", + "\t\tPy_INCREF(create_signature);\n", + "\t\tmemset(&slot, 0, sizeof(slot));\n", + "\t\tslot.sa_handler = &sighandler;\n", + "\t\tsigaction(SIGSEGV, &slot, NULL);\n", + "\t\tsigaction(SIGBUS, &slot, NULL);\n", + "\t\tPy_INCREF(Py_None);\n", + "\t\treturn Py_None;\n", + "\t}\n", + "\tPyObject * run(PyObject * self, PyObject * args) {\n", + "\t\t{\n", + "\t\t\tPyObj pX;\n", + "\t\t\tPyObj pY;\n", + "\t\t\tif (\n", + "\t\t\t\tPyTuple_CheckExact(args) and PyTuple_GET_SIZE(args) == 2\n", + "\t\t\t\tand (pX = PyTuple_GET_ITEM(args, 0)) and PyArray_CheckExact(pX)\n", + "\t\t\t\tand PyArray_TYPE((PyArrayObject *)pX) == NPY_FLOAT64 and PyArray_NDIM((PyArrayObject *)pX) == 1\n", + "\t\t\t\tand (pY = PyTuple_GET_ITEM(args, 1)) and PyArray_CheckExact(pY)\n", + "\t\t\t\tand PyArray_TYPE((PyArrayObject *)pY) == NPY_FLOAT64 and PyArray_NDIM((PyArrayObject *)pY) == 1\n", + "\t\t\t) {\n", + "\t\t\t\tif (!(pX.incref((PyObject *)PyArray_GETCONTIGUOUS((PyArrayObject *)pX)))) {\n", + "\t\t\t\t\tPyErr_SetString(PyExc_ValueError, \"Invalid Argument type on X!\");\n", + "\t\t\t\t\treturn NULL;\n", + "\t\t\t\t}\n", + "\t\t\t\tif (!(pY.incref((PyObject *)PyArray_GETCONTIGUOUS((PyArrayObject *)pY)))) {\n", + "\t\t\t\t\tPyErr_SetString(PyExc_ValueError, \"Invalid Argument type on Y!\");\n", + "\t\t\t\t\treturn NULL;\n", + "\t\t\t\t}\n", + "\t\t\t\ttry {\n", + "\t\t\t\t\tln_hope_exp_d1d1(\n", + "\t\t\t\t\t\t pX, PyArray_SHAPE((PyArrayObject *)pX), (npy_double *)PyArray_DATA((PyArrayObject *)pX)\n", + "\t\t\t\t\t\t, pY, PyArray_SHAPE((PyArrayObject *)pY), (npy_double *)PyArray_DATA((PyArrayObject *)pY)\n", + "\t\t\t\t\t);\n", + "\t\t\t\t\tPy_INCREF(Py_None);\n", + "\t\t\t\t\treturn Py_None;\n", + "\t\t\t\t} catch (...) {\n", + "\t\t\t\t\treturn NULL;\n", + "\t\t\t\t}\n", + "\t\t\t} else\n", + "\t\t\t\tPyErr_Clear();\n", + "\t\t}\n", + "\t\tPyObject * signatures = Py_BuildValue(\"(sO)\", \"(lp0\\n(lp1\\nccopy_reg\\n_reconstructor\\np2\\n(chope._ast\\nVariable\\np3\\nc__builtin__\\nobject\\np4\\nNtp5\\nRp6\\n(dp7\\nS'name'\\np8\\nS'X'\\np9\\nsS'dtype'\\np10\\nS'float64'\\np11\\nsS'dims'\\np12\\nI1\\nsbag2\\n(g3\\ng4\\nNtp13\\nRp14\\n(dp15\\ng8\\nS'Y'\\np16\\nsg10\\ng11\\nsg12\\nI1\\nsbaa.\", args);\n", + "\t\tif (!signatures) {\n", + "\t\t\tPyErr_SetString(PyExc_ValueError, \"Error building signature string for ln_hope_exp\");\n", + "\t\t\treturn NULL;\n", + "\t\t}\n", + "\t\treturn PyObject_Call(create_signature, signatures, NULL);\n", + "\t}\n", + "\tPyMethodDef ln_hope_expMethods[] = {\n", + "\t\t{ \"set_create_signature\", (PyCFunction)set_create_signature, METH_VARARGS },\n", + "\t\t{ \"run\", (PyCFunction)run, METH_VARARGS },\n", + "\t\t{ NULL, NULL }\n", + "\t};\n", + "\tPyMODINIT_FUNC initln_exp(void) {\n", + "\t\timport_array();\n", + "\t\tPyImport_ImportModule(\"numpy\");\n", + "\t\t(void)Py_InitModule(\"ln_exp\", ln_hope_expMethods);\n", + "\t}\n", + "}\n", + "#include \n", + "#include \n", + "#include \n", + "#include \n", + "#include \n", + "void sighandler(int sig) {\n", + "\tstd::ostringstream buffer;\n", + "\tbuffer << \"Abort by \" << (sig == SIGSEGV ? \"segfault\" : \"bus error\") << std::endl;\n", + "\tvoid * stack[64];\n", + "\tstd::size_t depth = backtrace(stack, 64);\n", + "\tif (!depth)\n", + "\t\tbuffer << \" \" << std::endl;\n", + "\telse {\n", + "\t\tchar ** symbols = backtrace_symbols(stack, depth);\n", + "\t\tfor (std::size_t i = 1; i < depth; ++i) {\n", + "\t\t\tstd::string symbol = symbols[i];\n", + "\t\t\t\tif (symbol.find_first_of(' ', 59) != std::string::npos) {\n", + "\t\t\t\t\tstd::string name = symbol.substr(59, symbol.find_first_of(' ', 59) - 59);\n", + "\t\t\t\t\tint status;\n", + "\t\t\t\t\tchar * demangled = abi::__cxa_demangle(name.c_str(), NULL, NULL, &status);\n", + "\t\t\t\t\tif (!status) {\n", + "\t\t\t\t\t\tbuffer << \" \" \n", + "\t\t\t\t\t\t\t<< symbol.substr(0, 59) \n", + "\t\t\t\t\t\t\t<< demangled\n", + "\t\t\t\t\t\t\t<< symbol.substr(59 + name.size())\n", + "\t\t\t\t\t\t\t<< std::endl;\n", + "\t\t\t\t\t\tfree(demangled);\n", + "\t\t\t\t\t} else\n", + "\t\t\t\t\t\tbuffer << \" \" << symbol << std::endl;\n", + "\t\t\t\t} else\n", + "\t\t\t\t\tbuffer << \" \" << symbol << std::endl;\n", + "\t\t\t}\n", + "\t\t\tfree(symbols);\n", + "\t\t}\n", + "\t\tstd::cerr << buffer.str();\n", + "\t\tstd::exit(EXIT_FAILURE);\n", + "\t}\n" + ] + }, + { + "cell_type": "code", + "execution_count": 10, + "metadata": {}, + "outputs": [ { - "cell_type": "heading", - "level": 1, - "metadata": {}, - "source": [ - "10th order poly log approx CPP" + "name": "stdout", + "output_type": "stream", + "text": [ + "Overwriting src/ln_opt.cpp\n" ] - }, - { - "cell_type": "code", - "collapsed": false, - "input": [ - "%%file src/ln.cpp\n", - "#define PY_ARRAY_UNIQUE_SYMBOL ln_hope_ARRAY_API\n", - "#define NPY_NO_DEPRECATED_API NPY_1_7_API_VERSION\n", - "#include \n", - "#include \n", - "#include \n", - "#include \n", - "#include \n", - "#include \n", - "#include \n", - "#include \n", - "#include \n", - "#include \n", - "#include \n", - "struct PyObj {\n", - "\ttypedef PyObject * ptr_t;\n", - "\ttypedef PyArrayObject * arrptr_t;\n", - "\tPyObj(): dec(false), ptr(NULL) {}\n", - "\tPyObj(ptr_t p): dec(false), ptr(p) {}\n", - "\t~PyObj() { if(dec) Py_DECREF(ptr); }\n", - "\tPyObj & operator=(ptr_t p) { if(dec) Py_DECREF(ptr); ptr = p; dec = false; return *this; }\n", - "\tPyObj & incref(ptr_t p) { if(dec) Py_DECREF(ptr); ptr = p; dec = (p != NULL); return *this; }\n", - "\toperator bool() const { return ptr; }\n", - "\toperator ptr_t() const { return ptr; }\n", - "\toperator arrptr_t() const { return (arrptr_t)ptr; }\n", - "\tbool dec;\n", - "\tptr_t ptr;\n", - "};\n", - "\n", - "inline void ln_hope_d1d1(PyObject * pX, npy_intp const * __restrict__ sX, npy_double * __restrict__ cX, \n", - "\t\t\t\t\t\t\t\t\t\t\t\t\tPyObject * pY, npy_intp const * __restrict__ sY, npy_double * __restrict__ cY);\n", - "\n", - "inline void ln_hope_d1d1(PyObject * pX, npy_intp const * __restrict__ sX, npy_double * __restrict__ cX, \n", - "\t\t\t\t\t\t\t\t\t\t\t\t\tPyObject * pY, npy_intp const * __restrict__ sY, npy_double * __restrict__ cY){\n", - "\t\n", - "\tfor (npy_intp i0 = 0; i0 < sY[0] - 0; ++i0) {\n", - "\t\tcY[(int)(i0)] = (cX[i0] - 1) - (std::pow((cX[i0] - 1), 2) / 2) + (std::pow((cX[i0] - 1), 3) / 3) - (std::pow((cX[i0] - 1), 4) / 4) + (std::pow((cX[i0] - 1), 5) / 5) - (std::pow((cX[i0] - 1), 6) / 6) + (std::pow((cX[i0] - 1), 7) / 7) - (std::pow((cX[i0] - 1), 8) / 8) + (std::pow((cX[i0] - 1), 9) / 9);\n", - "\t}\n", - "}\n", - "\n", - "void sighandler(int sig);\n", - "#include \n", - "extern \"C\" {\n", - "\tPyObject * create_signature;\n", - "\tstruct sigaction slot;\n", - "\tPyObject * set_create_signature(PyObject * self, PyObject * args) {\n", - "\t\tif (!PyArg_ParseTuple(args, \"O\", &create_signature)) {\n", - "\t\t\tPyErr_SetString(PyExc_ValueError, \"Invalid Argument to set_create_signature!\");\n", - "\t\t\treturn NULL;\n", - "\t\t}\n", - "\t\tPy_INCREF(create_signature);\n", - "\t\tmemset(&slot, 0, sizeof(slot));\n", - "\t\tslot.sa_handler = &sighandler;\n", - "\t\tsigaction(SIGSEGV, &slot, NULL);\n", - "\t\tsigaction(SIGBUS, &slot, NULL);\n", - "\t\tPy_INCREF(Py_None);\n", - "\t\treturn Py_None;\n", - "\t}\n", - "\tPyObject * run(PyObject * self, PyObject * args) {\n", - "\t\t{\n", - "\t\t\tPyObj pX;\n", - "\t\t\tPyObj pY;\n", - "\t\t\tif (\n", - "\t\t\t\tPyTuple_CheckExact(args) and PyTuple_GET_SIZE(args) == 2\n", - "\t\t\t\tand (pX = PyTuple_GET_ITEM(args, 0)) and PyArray_CheckExact(pX)\n", - "\t\t\t\tand PyArray_TYPE((PyArrayObject *)pX) == NPY_FLOAT64 and PyArray_NDIM((PyArrayObject *)pX) == 1\n", - "\t\t\t\tand (pY = PyTuple_GET_ITEM(args, 1)) and PyArray_CheckExact(pY)\n", - "\t\t\t\tand PyArray_TYPE((PyArrayObject *)pY) == NPY_FLOAT64 and PyArray_NDIM((PyArrayObject *)pY) == 1\n", - "\t\t\t) {\n", - "\t\t\t\tif (!(pX.incref((PyObject *)PyArray_GETCONTIGUOUS((PyArrayObject *)pX)))) {\n", - "\t\t\t\t\tPyErr_SetString(PyExc_ValueError, \"Invalid Argument type on X!\");\n", - "\t\t\t\t\treturn NULL;\n", - "\t\t\t\t}\n", - "\t\t\t\tif (!(pY.incref((PyObject *)PyArray_GETCONTIGUOUS((PyArrayObject *)pY)))) {\n", - "\t\t\t\t\tPyErr_SetString(PyExc_ValueError, \"Invalid Argument type on Y!\");\n", - "\t\t\t\t\treturn NULL;\n", - "\t\t\t\t}\n", - "\t\t\t\ttry {\n", - "\t\t\t\t\tln_hope_d1d1(\n", - "\t\t\t\t\t\t pX, PyArray_SHAPE((PyArrayObject *)pX), (npy_double *)PyArray_DATA((PyArrayObject *)pX)\n", - "\t\t\t\t\t\t, pY, PyArray_SHAPE((PyArrayObject *)pY), (npy_double *)PyArray_DATA((PyArrayObject *)pY)\n", - "\t\t\t\t\t);\n", - "\t\t\t\t\tPy_INCREF(Py_None);\n", - "\t\t\t\t\treturn Py_None;\n", - "\t\t\t\t} catch (...) {\n", - "\t\t\t\t\treturn NULL;\n", - "\t\t\t\t}\n", - "\t\t\t} else\n", - "\t\t\t\tPyErr_Clear();\n", - "\t\t}\n", - "\t\tPyObject * signatures = Py_BuildValue(\"(sO)\", \"(lp0\\n(lp1\\nccopy_reg\\n_reconstructor\\np2\\n(chope._ast\\nVariable\\np3\\nc__builtin__\\nobject\\np4\\nNtp5\\nRp6\\n(dp7\\nS'name'\\np8\\nS'X'\\np9\\nsS'dtype'\\np10\\nS'float64'\\np11\\nsS'dims'\\np12\\nI1\\nsbag2\\n(g3\\ng4\\nNtp13\\nRp14\\n(dp15\\ng8\\nS'Y'\\np16\\nsg10\\ng11\\nsg12\\nI1\\nsbaa.\", args);\n", - "\t\tif (!signatures) {\n", - "\t\t\tPyErr_SetString(PyExc_ValueError, \"Error building signature string for ln_hope\");\n", - "\t\t\treturn NULL;\n", - "\t\t}\n", - "\t\treturn PyObject_Call(create_signature, signatures, NULL);\n", - "\t}\n", - "\tPyMethodDef ln_hopeMethods[] = {\n", - "\t\t{ \"set_create_signature\", (PyCFunction)set_create_signature, METH_VARARGS },\n", - "\t\t{ \"run\", (PyCFunction)run, METH_VARARGS },\n", - "\t\t{ NULL, NULL }\n", - "\t};\n", - "\tPyMODINIT_FUNC initln(void) {\n", - "\t\timport_array();\n", - "\t\tPyImport_ImportModule(\"numpy\");\n", - "\t\t(void)Py_InitModule(\"ln\", ln_hopeMethods);\n", - "\t}\n", - "}\n", - "#include \n", - "#include \n", - "#include \n", - "#include \n", - "#include \n", - "void sighandler(int sig) {\n", - "\tstd::ostringstream buffer;\n", - "\tbuffer << \"Abort by \" << (sig == SIGSEGV ? \"segfault\" : \"bus error\") << std::endl;\n", - "\tvoid * stack[64];\n", - "\tstd::size_t depth = backtrace(stack, 64);\n", - "\tif (!depth)\n", - "\t\tbuffer << \" \" << std::endl;\n", - "\telse {\n", - "\t\tchar ** symbols = backtrace_symbols(stack, depth);\n", - "\t\tfor (std::size_t i = 1; i < depth; ++i) {\n", - "\t\t\tstd::string symbol = symbols[i];\n", - "\t\t\t\tif (symbol.find_first_of(' ', 59) != std::string::npos) {\n", - "\t\t\t\t\tstd::string name = symbol.substr(59, symbol.find_first_of(' ', 59) - 59);\n", - "\t\t\t\t\tint status;\n", - "\t\t\t\t\tchar * demangled = abi::__cxa_demangle(name.c_str(), NULL, NULL, &status);\n", - "\t\t\t\t\tif (!status) {\n", - "\t\t\t\t\t\tbuffer << \" \" \n", - "\t\t\t\t\t\t\t<< symbol.substr(0, 59) \n", - "\t\t\t\t\t\t\t<< demangled\n", - "\t\t\t\t\t\t\t<< symbol.substr(59 + name.size())\n", - "\t\t\t\t\t\t\t<< std::endl;\n", - "\t\t\t\t\t\tfree(demangled);\n", - "\t\t\t\t\t} else\n", - "\t\t\t\t\t\tbuffer << \" \" << symbol << std::endl;\n", - "\t\t\t\t} else\n", - "\t\t\t\t\tbuffer << \" \" << symbol << std::endl;\n", - "\t\t\t}\n", - "\t\t\tfree(symbols);\n", - "\t\t}\n", - "\t\tstd::cerr << buffer.str();\n", - "\t\tstd::exit(EXIT_FAILURE);\n", - "\t}\n" - ], - "language": "python", - "metadata": {}, - "outputs": [ - { - "output_type": "stream", - "stream": "stdout", - "text": [ - "Overwriting src/ln.cpp\n" - ] - } - ], - "prompt_number": 8 - }, - { - "cell_type": "code", - "collapsed": false, - "input": [ - "%%file src/ln_exp.cpp\n", - "#define PY_ARRAY_UNIQUE_SYMBOL ln_hope_exp_ARRAY_API\n", - "#define NPY_NO_DEPRECATED_API NPY_1_7_API_VERSION\n", - "#include \n", - "#include \n", - "#include \n", - "#include \n", - "#include \n", - "#include \n", - "#include \n", - "#include \n", - "#include \n", - "#include \n", - "#include \n", - "struct PyObj {\n", - "\ttypedef PyObject * ptr_t;\n", - "\ttypedef PyArrayObject * arrptr_t;\n", - "\tPyObj(): dec(false), ptr(NULL) {}\n", - "\tPyObj(ptr_t p): dec(false), ptr(p) {}\n", - "\t~PyObj() { if(dec) Py_DECREF(ptr); }\n", - "\tPyObj & operator=(ptr_t p) { if(dec) Py_DECREF(ptr); ptr = p; dec = false; return *this; }\n", - "\tPyObj & incref(ptr_t p) { if(dec) Py_DECREF(ptr); ptr = p; dec = (p != NULL); return *this; }\n", - "\toperator bool() const { return ptr; }\n", - "\toperator ptr_t() const { return ptr; }\n", - "\toperator arrptr_t() const { return (arrptr_t)ptr; }\n", - "\tbool dec;\n", - "\tptr_t ptr;\n", - "};\n", - "\n", - "inline void ln_hope_exp_d1d1(PyObject * pX, npy_intp const * __restrict__ sX, npy_double * __restrict__ cX, \n", - "\t\t\t\t\t\t\t\t\t\t\t\t\tPyObject * pY, npy_intp const * __restrict__ sY, npy_double * __restrict__ cY);\n", - "\n", - "inline void ln_hope_exp_d1d1(PyObject * pX, npy_intp const * __restrict__ sX, npy_double * __restrict__ cX, \n", - "\t\t\t\t\t\t\t\t\t\t\t\t\tPyObject * pY, npy_intp const * __restrict__ sY, npy_double * __restrict__ cY){\n", - "\n", - "\tfor (npy_intp i0 = 0; i0 < sX[0] - 0; ++i0) {\n", - "\t\tauto cx = (cX[i0] - 1);\n", - "\t\tauto cx2 = (cx * cx);\n", - "\t\tauto cx4 = (cx2 * cx2);\n", - "\t\tauto cx6 = (cx4 * cx2);\n", - "\t\tauto cx8 = (cx4 * cx4);\n", - "\t\tcY[i0] = cx - (cx2 / 2) + (cx * cx2 / 3) - (cx4 / 4) + (cx * cx4 / 5) - (cx6 / 6) + (cx6 * cx / 7) - (cx8 / 8) + (cx8 * cx / 9);\n", - "\t}\n", - "}\n", - "\n", - "void sighandler(int sig);\n", - "#include \n", - "extern \"C\" {\n", - "\tPyObject * create_signature;\n", - "\tstruct sigaction slot;\n", - "\tPyObject * set_create_signature(PyObject * self, PyObject * args) {\n", - "\t\tif (!PyArg_ParseTuple(args, \"O\", &create_signature)) {\n", - "\t\t\tPyErr_SetString(PyExc_ValueError, \"Invalid Argument to set_create_signature!\");\n", - "\t\t\treturn NULL;\n", - "\t\t}\n", - "\t\tPy_INCREF(create_signature);\n", - "\t\tmemset(&slot, 0, sizeof(slot));\n", - "\t\tslot.sa_handler = &sighandler;\n", - "\t\tsigaction(SIGSEGV, &slot, NULL);\n", - "\t\tsigaction(SIGBUS, &slot, NULL);\n", - "\t\tPy_INCREF(Py_None);\n", - "\t\treturn Py_None;\n", - "\t}\n", - "\tPyObject * run(PyObject * self, PyObject * args) {\n", - "\t\t{\n", - "\t\t\tPyObj pX;\n", - "\t\t\tPyObj pY;\n", - "\t\t\tif (\n", - "\t\t\t\tPyTuple_CheckExact(args) and PyTuple_GET_SIZE(args) == 2\n", - "\t\t\t\tand (pX = PyTuple_GET_ITEM(args, 0)) and PyArray_CheckExact(pX)\n", - "\t\t\t\tand PyArray_TYPE((PyArrayObject *)pX) == NPY_FLOAT64 and PyArray_NDIM((PyArrayObject *)pX) == 1\n", - "\t\t\t\tand (pY = PyTuple_GET_ITEM(args, 1)) and PyArray_CheckExact(pY)\n", - "\t\t\t\tand PyArray_TYPE((PyArrayObject *)pY) == NPY_FLOAT64 and PyArray_NDIM((PyArrayObject *)pY) == 1\n", - "\t\t\t) {\n", - "\t\t\t\tif (!(pX.incref((PyObject *)PyArray_GETCONTIGUOUS((PyArrayObject *)pX)))) {\n", - "\t\t\t\t\tPyErr_SetString(PyExc_ValueError, \"Invalid Argument type on X!\");\n", - "\t\t\t\t\treturn NULL;\n", - "\t\t\t\t}\n", - "\t\t\t\tif (!(pY.incref((PyObject *)PyArray_GETCONTIGUOUS((PyArrayObject *)pY)))) {\n", - "\t\t\t\t\tPyErr_SetString(PyExc_ValueError, \"Invalid Argument type on Y!\");\n", - "\t\t\t\t\treturn NULL;\n", - "\t\t\t\t}\n", - "\t\t\t\ttry {\n", - "\t\t\t\t\tln_hope_exp_d1d1(\n", - "\t\t\t\t\t\t pX, PyArray_SHAPE((PyArrayObject *)pX), (npy_double *)PyArray_DATA((PyArrayObject *)pX)\n", - "\t\t\t\t\t\t, pY, PyArray_SHAPE((PyArrayObject *)pY), (npy_double *)PyArray_DATA((PyArrayObject *)pY)\n", - "\t\t\t\t\t);\n", - "\t\t\t\t\tPy_INCREF(Py_None);\n", - "\t\t\t\t\treturn Py_None;\n", - "\t\t\t\t} catch (...) {\n", - "\t\t\t\t\treturn NULL;\n", - "\t\t\t\t}\n", - "\t\t\t} else\n", - "\t\t\t\tPyErr_Clear();\n", - "\t\t}\n", - "\t\tPyObject * signatures = Py_BuildValue(\"(sO)\", \"(lp0\\n(lp1\\nccopy_reg\\n_reconstructor\\np2\\n(chope._ast\\nVariable\\np3\\nc__builtin__\\nobject\\np4\\nNtp5\\nRp6\\n(dp7\\nS'name'\\np8\\nS'X'\\np9\\nsS'dtype'\\np10\\nS'float64'\\np11\\nsS'dims'\\np12\\nI1\\nsbag2\\n(g3\\ng4\\nNtp13\\nRp14\\n(dp15\\ng8\\nS'Y'\\np16\\nsg10\\ng11\\nsg12\\nI1\\nsbaa.\", args);\n", - "\t\tif (!signatures) {\n", - "\t\t\tPyErr_SetString(PyExc_ValueError, \"Error building signature string for ln_hope_exp\");\n", - "\t\t\treturn NULL;\n", - "\t\t}\n", - "\t\treturn PyObject_Call(create_signature, signatures, NULL);\n", - "\t}\n", - "\tPyMethodDef ln_hope_expMethods[] = {\n", - "\t\t{ \"set_create_signature\", (PyCFunction)set_create_signature, METH_VARARGS },\n", - "\t\t{ \"run\", (PyCFunction)run, METH_VARARGS },\n", - "\t\t{ NULL, NULL }\n", - "\t};\n", - "\tPyMODINIT_FUNC initln_exp(void) {\n", - "\t\timport_array();\n", - "\t\tPyImport_ImportModule(\"numpy\");\n", - "\t\t(void)Py_InitModule(\"ln_exp\", ln_hope_expMethods);\n", - "\t}\n", - "}\n", - "#include \n", - "#include \n", - "#include \n", - "#include \n", - "#include \n", - "void sighandler(int sig) {\n", - "\tstd::ostringstream buffer;\n", - "\tbuffer << \"Abort by \" << (sig == SIGSEGV ? \"segfault\" : \"bus error\") << std::endl;\n", - "\tvoid * stack[64];\n", - "\tstd::size_t depth = backtrace(stack, 64);\n", - "\tif (!depth)\n", - "\t\tbuffer << \" \" << std::endl;\n", - "\telse {\n", - "\t\tchar ** symbols = backtrace_symbols(stack, depth);\n", - "\t\tfor (std::size_t i = 1; i < depth; ++i) {\n", - "\t\t\tstd::string symbol = symbols[i];\n", - "\t\t\t\tif (symbol.find_first_of(' ', 59) != std::string::npos) {\n", - "\t\t\t\t\tstd::string name = symbol.substr(59, symbol.find_first_of(' ', 59) - 59);\n", - "\t\t\t\t\tint status;\n", - "\t\t\t\t\tchar * demangled = abi::__cxa_demangle(name.c_str(), NULL, NULL, &status);\n", - "\t\t\t\t\tif (!status) {\n", - "\t\t\t\t\t\tbuffer << \" \" \n", - "\t\t\t\t\t\t\t<< symbol.substr(0, 59) \n", - "\t\t\t\t\t\t\t<< demangled\n", - "\t\t\t\t\t\t\t<< symbol.substr(59 + name.size())\n", - "\t\t\t\t\t\t\t<< std::endl;\n", - "\t\t\t\t\t\tfree(demangled);\n", - "\t\t\t\t\t} else\n", - "\t\t\t\t\t\tbuffer << \" \" << symbol << std::endl;\n", - "\t\t\t\t} else\n", - "\t\t\t\t\tbuffer << \" \" << symbol << std::endl;\n", - "\t\t\t}\n", - "\t\t\tfree(symbols);\n", - "\t\t}\n", - "\t\tstd::cerr << buffer.str();\n", - "\t\tstd::exit(EXIT_FAILURE);\n", - "\t}\n" - ], - "language": "python", - "metadata": {}, - "outputs": [ - { - "output_type": "stream", - "stream": "stdout", - "text": [ - "Overwriting src/ln_exp.cpp\n" - ] - } - ], - "prompt_number": 9 - }, - { - "cell_type": "code", - "collapsed": false, - "input": [ - "%%file src/ln_opt.cpp\n", - "#define PY_ARRAY_UNIQUE_SYMBOL ln_hope_opt_ARRAY_API\n", - "#define NPY_NO_DEPRECATED_API NPY_1_7_API_VERSION\n", - "#include \n", - "#include \n", - "#include \n", - "#include \n", - "#include \n", - "#include \n", - "#include \n", - "#include \n", - "#include \n", - "#include \n", - "#include \n", - "struct PyObj {\n", - "\ttypedef PyObject * ptr_t;\n", - "\ttypedef PyArrayObject * arrptr_t;\n", - "\tPyObj(): dec(false), ptr(NULL) {}\n", - "\tPyObj(ptr_t p): dec(false), ptr(p) {}\n", - "\t~PyObj() { if(dec) Py_DECREF(ptr); }\n", - "\tPyObj & operator=(ptr_t p) { if(dec) Py_DECREF(ptr); ptr = p; dec = false; return *this; }\n", - "\tPyObj & incref(ptr_t p) { if(dec) Py_DECREF(ptr); ptr = p; dec = (p != NULL); return *this; }\n", - "\toperator bool() const { return ptr; }\n", - "\toperator ptr_t() const { return ptr; }\n", - "\toperator arrptr_t() const { return (arrptr_t)ptr; }\n", - "\tbool dec;\n", - "\tptr_t ptr;\n", - "};\n", - "\n", - "inline void ln_hope_opt_d1d1(PyObject * pX, npy_intp const * __restrict__ sX, npy_double * __restrict__ cX, \n", - "\t\t\t\t\t\t\t\t\t\t\t\t\tPyObject * pY, npy_intp const * __restrict__ sY, npy_double * __restrict__ cY);\n", - "\n", - "inline void ln_hope_opt_d1d1(PyObject * pX, npy_intp const * __restrict__ sX, npy_double * __restrict__ cX, \n", - "\t\t\t\t\t\t\t\t\t\t\t\t\tPyObject * pY, npy_intp const * __restrict__ sY, npy_double * __restrict__ cY){\n", - "\t\t\t\t\t\t\t\t\t\t\t\t\t\t\n", - "\tfor (npy_intp i0 = 0; i0 < sY[0] - 0; ++i0) {\n", - "\t\tauto c__sp0 = (cX[i0] * cX[i0]);\n", - "\t\tauto c__sp1 = (c__sp0 * c__sp0);\n", - "\t\tauto c__sp2 = (c__sp1 * c__sp1);\n", - "\t\tauto c__sp3 = (c__sp2 * cX[i0]);\n", - "\t\tauto c__sp4 = (c__sp0 * cX[i0]);\n", - "\t\tauto c__sp5 = (c__sp4 * c__sp4);\n", - "\t\tauto c__sp6 = (c__sp5 * cX[i0]);\n", - "\t\tauto c__sp7 = (c__sp1 * cX[i0]);\n", - "\t\tcY[(int)(i0)] = (-7129.0 / 2520.0) + (28 * c__sp4) + (-(18 * c__sp0)) + (-(9 * c__sp2 / 8)) + (-(14 * c__sp5)) + (-(63 * c__sp1 / 2)) + (126 * c__sp7 / 5) + (9 * cX[i0]) + (c__sp3 / 9) + (36 * c__sp6 / 7);\n", - "\t}\n", - "}\n", - "\n", - "void sighandler(int sig);\n", - "#include \n", - "extern \"C\" {\n", - "\tPyObject * create_signature;\n", - "\tstruct sigaction slot;\n", - "\tPyObject * set_create_signature(PyObject * self, PyObject * args) {\n", - "\t\tif (!PyArg_ParseTuple(args, \"O\", &create_signature)) {\n", - "\t\t\tPyErr_SetString(PyExc_ValueError, \"Invalid Argument to set_create_signature!\");\n", - "\t\t\treturn NULL;\n", - "\t\t}\n", - "\t\tPy_INCREF(create_signature);\n", - "\t\tmemset(&slot, 0, sizeof(slot));\n", - "\t\tslot.sa_handler = &sighandler;\n", - "\t\tsigaction(SIGSEGV, &slot, NULL);\n", - "\t\tsigaction(SIGBUS, &slot, NULL);\n", - "\t\tPy_INCREF(Py_None);\n", - "\t\treturn Py_None;\n", - "\t}\n", - "\tPyObject * run(PyObject * self, PyObject * args) {\n", - "\t\t{\n", - "\t\t\tPyObj pX;\n", - "\t\t\tPyObj pY;\n", - "\t\t\tif (\n", - "\t\t\t\tPyTuple_CheckExact(args) and PyTuple_GET_SIZE(args) == 2\n", - "\t\t\t\tand (pX = PyTuple_GET_ITEM(args, 0)) and PyArray_CheckExact(pX)\n", - "\t\t\t\tand PyArray_TYPE((PyArrayObject *)pX) == NPY_FLOAT64 and PyArray_NDIM((PyArrayObject *)pX) == 1\n", - "\t\t\t\tand (pY = PyTuple_GET_ITEM(args, 1)) and PyArray_CheckExact(pY)\n", - "\t\t\t\tand PyArray_TYPE((PyArrayObject *)pY) == NPY_FLOAT64 and PyArray_NDIM((PyArrayObject *)pY) == 1\n", - "\t\t\t) {\n", - "\t\t\t\tif (!(pX.incref((PyObject *)PyArray_GETCONTIGUOUS((PyArrayObject *)pX)))) {\n", - "\t\t\t\t\tPyErr_SetString(PyExc_ValueError, \"Invalid Argument type on X!\");\n", - "\t\t\t\t\treturn NULL;\n", - "\t\t\t\t}\n", - "\t\t\t\tif (!(pY.incref((PyObject *)PyArray_GETCONTIGUOUS((PyArrayObject *)pY)))) {\n", - "\t\t\t\t\tPyErr_SetString(PyExc_ValueError, \"Invalid Argument type on Y!\");\n", - "\t\t\t\t\treturn NULL;\n", - "\t\t\t\t}\n", - "\t\t\t\ttry {\n", - "\t\t\t\t\tln_hope_opt_d1d1(\n", - "\t\t\t\t\t\t pX, PyArray_SHAPE((PyArrayObject *)pX), (npy_double *)PyArray_DATA((PyArrayObject *)pX)\n", - "\t\t\t\t\t\t, pY, PyArray_SHAPE((PyArrayObject *)pY), (npy_double *)PyArray_DATA((PyArrayObject *)pY)\n", - "\t\t\t\t\t);\n", - "\t\t\t\t\tPy_INCREF(Py_None);\n", - "\t\t\t\t\treturn Py_None;\n", - "\t\t\t\t} catch (...) {\n", - "\t\t\t\t\treturn NULL;\n", - "\t\t\t\t}\n", - "\t\t\t} else\n", - "\t\t\t\tPyErr_Clear();\n", - "\t\t}\n", - "\t\tPyObject * signatures = Py_BuildValue(\"(sO)\", \"(lp0\\n(lp1\\nccopy_reg\\n_reconstructor\\np2\\n(chope._ast\\nVariable\\np3\\nc__builtin__\\nobject\\np4\\nNtp5\\nRp6\\n(dp7\\nS'name'\\np8\\nS'X'\\np9\\nsS'dtype'\\np10\\nS'float64'\\np11\\nsS'dims'\\np12\\nI1\\nsbag2\\n(g3\\ng4\\nNtp13\\nRp14\\n(dp15\\ng8\\nS'Y'\\np16\\nsg10\\ng11\\nsg12\\nI1\\nsbaa.\", args);\n", - "\t\tif (!signatures) {\n", - "\t\t\tPyErr_SetString(PyExc_ValueError, \"Error building signature string for ln_hope_opt\");\n", - "\t\t\treturn NULL;\n", - "\t\t}\n", - "\t\treturn PyObject_Call(create_signature, signatures, NULL);\n", - "\t}\n", - "\tPyMethodDef ln_hope_optMethods[] = {\n", - "\t\t{ \"set_create_signature\", (PyCFunction)set_create_signature, METH_VARARGS },\n", - "\t\t{ \"run\", (PyCFunction)run, METH_VARARGS },\n", - "\t\t{ NULL, NULL }\n", - "\t};\n", - "\tPyMODINIT_FUNC initln_opt(void) {\n", - "\t\timport_array();\n", - "\t\tPyImport_ImportModule(\"numpy\");\n", - "\t\t(void)Py_InitModule(\"ln_opt\", ln_hope_optMethods);\n", - "\t}\n", - "}\n", - "#include \n", - "#include \n", - "#include \n", - "#include \n", - "#include \n", - "void sighandler(int sig) {\n", - "\tstd::ostringstream buffer;\n", - "\tbuffer << \"Abort by \" << (sig == SIGSEGV ? \"segfault\" : \"bus error\") << std::endl;\n", - "\tvoid * stack[64];\n", - "\tstd::size_t depth = backtrace(stack, 64);\n", - "\tif (!depth)\n", - "\t\tbuffer << \" \" << std::endl;\n", - "\telse {\n", - "\t\tchar ** symbols = backtrace_symbols(stack, depth);\n", - "\t\tfor (std::size_t i = 1; i < depth; ++i) {\n", - "\t\t\tstd::string symbol = symbols[i];\n", - "\t\t\t\tif (symbol.find_first_of(' ', 59) != std::string::npos) {\n", - "\t\t\t\t\tstd::string name = symbol.substr(59, symbol.find_first_of(' ', 59) - 59);\n", - "\t\t\t\t\tint status;\n", - "\t\t\t\t\tchar * demangled = abi::__cxa_demangle(name.c_str(), NULL, NULL, &status);\n", - "\t\t\t\t\tif (!status) {\n", - "\t\t\t\t\t\tbuffer << \" \" \n", - "\t\t\t\t\t\t\t<< symbol.substr(0, 59) \n", - "\t\t\t\t\t\t\t<< demangled\n", - "\t\t\t\t\t\t\t<< symbol.substr(59 + name.size())\n", - "\t\t\t\t\t\t\t<< std::endl;\n", - "\t\t\t\t\t\tfree(demangled);\n", - "\t\t\t\t\t} else\n", - "\t\t\t\t\t\tbuffer << \" \" << symbol << std::endl;\n", - "\t\t\t\t} else\n", - "\t\t\t\t\tbuffer << \" \" << symbol << std::endl;\n", - "\t\t\t}\n", - "\t\t\tfree(symbols);\n", - "\t\t}\n", - "\t\tstd::cerr << buffer.str();\n", - "\t\tstd::exit(EXIT_FAILURE);\n", - "\t}\n" - ], - "language": "python", - "metadata": {}, - "outputs": [ - { - "output_type": "stream", - "stream": "stdout", - "text": [ - "Overwriting src/ln_opt.cpp\n" - ] - } - ], - "prompt_number": 10 - }, + } + ], + "source": [ + "%%file src/ln_opt.cpp\n", + "#define PY_ARRAY_UNIQUE_SYMBOL ln_hope_opt_ARRAY_API\n", + "#define NPY_NO_DEPRECATED_API NPY_1_7_API_VERSION\n", + "#include \n", + "#include \n", + "#include \n", + "#include \n", + "#include \n", + "#include \n", + "#include \n", + "#include \n", + "#include \n", + "#include \n", + "#include \n", + "struct PyObj {\n", + "\ttypedef PyObject * ptr_t;\n", + "\ttypedef PyArrayObject * arrptr_t;\n", + "\tPyObj(): dec(false), ptr(NULL) {}\n", + "\tPyObj(ptr_t p): dec(false), ptr(p) {}\n", + "\t~PyObj() { if(dec) Py_DECREF(ptr); }\n", + "\tPyObj & operator=(ptr_t p) { if(dec) Py_DECREF(ptr); ptr = p; dec = false; return *this; }\n", + "\tPyObj & incref(ptr_t p) { if(dec) Py_DECREF(ptr); ptr = p; dec = (p != NULL); return *this; }\n", + "\toperator bool() const { return ptr; }\n", + "\toperator ptr_t() const { return ptr; }\n", + "\toperator arrptr_t() const { return (arrptr_t)ptr; }\n", + "\tbool dec;\n", + "\tptr_t ptr;\n", + "};\n", + "\n", + "inline void ln_hope_opt_d1d1(PyObject * pX, npy_intp const * __restrict__ sX, npy_double * __restrict__ cX, \n", + "\t\t\t\t\t\t\t\t\t\t\t\t\tPyObject * pY, npy_intp const * __restrict__ sY, npy_double * __restrict__ cY);\n", + "\n", + "inline void ln_hope_opt_d1d1(PyObject * pX, npy_intp const * __restrict__ sX, npy_double * __restrict__ cX, \n", + "\t\t\t\t\t\t\t\t\t\t\t\t\tPyObject * pY, npy_intp const * __restrict__ sY, npy_double * __restrict__ cY){\n", + "\t\t\t\t\t\t\t\t\t\t\t\t\t\t\n", + "\tfor (npy_intp i0 = 0; i0 < sY[0] - 0; ++i0) {\n", + "\t\tauto c__sp0 = (cX[i0] * cX[i0]);\n", + "\t\tauto c__sp1 = (c__sp0 * c__sp0);\n", + "\t\tauto c__sp2 = (c__sp1 * c__sp1);\n", + "\t\tauto c__sp3 = (c__sp2 * cX[i0]);\n", + "\t\tauto c__sp4 = (c__sp0 * cX[i0]);\n", + "\t\tauto c__sp5 = (c__sp4 * c__sp4);\n", + "\t\tauto c__sp6 = (c__sp5 * cX[i0]);\n", + "\t\tauto c__sp7 = (c__sp1 * cX[i0]);\n", + "\t\tcY[(int)(i0)] = (-7129.0 / 2520.0) + (28 * c__sp4) + (-(18 * c__sp0)) + (-(9 * c__sp2 / 8)) + (-(14 * c__sp5)) + (-(63 * c__sp1 / 2)) + (126 * c__sp7 / 5) + (9 * cX[i0]) + (c__sp3 / 9) + (36 * c__sp6 / 7);\n", + "\t}\n", + "}\n", + "\n", + "void sighandler(int sig);\n", + "#include \n", + "extern \"C\" {\n", + "\tPyObject * create_signature;\n", + "\tstruct sigaction slot;\n", + "\tPyObject * set_create_signature(PyObject * self, PyObject * args) {\n", + "\t\tif (!PyArg_ParseTuple(args, \"O\", &create_signature)) {\n", + "\t\t\tPyErr_SetString(PyExc_ValueError, \"Invalid Argument to set_create_signature!\");\n", + "\t\t\treturn NULL;\n", + "\t\t}\n", + "\t\tPy_INCREF(create_signature);\n", + "\t\tmemset(&slot, 0, sizeof(slot));\n", + "\t\tslot.sa_handler = &sighandler;\n", + "\t\tsigaction(SIGSEGV, &slot, NULL);\n", + "\t\tsigaction(SIGBUS, &slot, NULL);\n", + "\t\tPy_INCREF(Py_None);\n", + "\t\treturn Py_None;\n", + "\t}\n", + "\tPyObject * run(PyObject * self, PyObject * args) {\n", + "\t\t{\n", + "\t\t\tPyObj pX;\n", + "\t\t\tPyObj pY;\n", + "\t\t\tif (\n", + "\t\t\t\tPyTuple_CheckExact(args) and PyTuple_GET_SIZE(args) == 2\n", + "\t\t\t\tand (pX = PyTuple_GET_ITEM(args, 0)) and PyArray_CheckExact(pX)\n", + "\t\t\t\tand PyArray_TYPE((PyArrayObject *)pX) == NPY_FLOAT64 and PyArray_NDIM((PyArrayObject *)pX) == 1\n", + "\t\t\t\tand (pY = PyTuple_GET_ITEM(args, 1)) and PyArray_CheckExact(pY)\n", + "\t\t\t\tand PyArray_TYPE((PyArrayObject *)pY) == NPY_FLOAT64 and PyArray_NDIM((PyArrayObject *)pY) == 1\n", + "\t\t\t) {\n", + "\t\t\t\tif (!(pX.incref((PyObject *)PyArray_GETCONTIGUOUS((PyArrayObject *)pX)))) {\n", + "\t\t\t\t\tPyErr_SetString(PyExc_ValueError, \"Invalid Argument type on X!\");\n", + "\t\t\t\t\treturn NULL;\n", + "\t\t\t\t}\n", + "\t\t\t\tif (!(pY.incref((PyObject *)PyArray_GETCONTIGUOUS((PyArrayObject *)pY)))) {\n", + "\t\t\t\t\tPyErr_SetString(PyExc_ValueError, \"Invalid Argument type on Y!\");\n", + "\t\t\t\t\treturn NULL;\n", + "\t\t\t\t}\n", + "\t\t\t\ttry {\n", + "\t\t\t\t\tln_hope_opt_d1d1(\n", + "\t\t\t\t\t\t pX, PyArray_SHAPE((PyArrayObject *)pX), (npy_double *)PyArray_DATA((PyArrayObject *)pX)\n", + "\t\t\t\t\t\t, pY, PyArray_SHAPE((PyArrayObject *)pY), (npy_double *)PyArray_DATA((PyArrayObject *)pY)\n", + "\t\t\t\t\t);\n", + "\t\t\t\t\tPy_INCREF(Py_None);\n", + "\t\t\t\t\treturn Py_None;\n", + "\t\t\t\t} catch (...) {\n", + "\t\t\t\t\treturn NULL;\n", + "\t\t\t\t}\n", + "\t\t\t} else\n", + "\t\t\t\tPyErr_Clear();\n", + "\t\t}\n", + "\t\tPyObject * signatures = Py_BuildValue(\"(sO)\", \"(lp0\\n(lp1\\nccopy_reg\\n_reconstructor\\np2\\n(chope._ast\\nVariable\\np3\\nc__builtin__\\nobject\\np4\\nNtp5\\nRp6\\n(dp7\\nS'name'\\np8\\nS'X'\\np9\\nsS'dtype'\\np10\\nS'float64'\\np11\\nsS'dims'\\np12\\nI1\\nsbag2\\n(g3\\ng4\\nNtp13\\nRp14\\n(dp15\\ng8\\nS'Y'\\np16\\nsg10\\ng11\\nsg12\\nI1\\nsbaa.\", args);\n", + "\t\tif (!signatures) {\n", + "\t\t\tPyErr_SetString(PyExc_ValueError, \"Error building signature string for ln_hope_opt\");\n", + "\t\t\treturn NULL;\n", + "\t\t}\n", + "\t\treturn PyObject_Call(create_signature, signatures, NULL);\n", + "\t}\n", + "\tPyMethodDef ln_hope_optMethods[] = {\n", + "\t\t{ \"set_create_signature\", (PyCFunction)set_create_signature, METH_VARARGS },\n", + "\t\t{ \"run\", (PyCFunction)run, METH_VARARGS },\n", + "\t\t{ NULL, NULL }\n", + "\t};\n", + "\tPyMODINIT_FUNC initln_opt(void) {\n", + "\t\timport_array();\n", + "\t\tPyImport_ImportModule(\"numpy\");\n", + "\t\t(void)Py_InitModule(\"ln_opt\", ln_hope_optMethods);\n", + "\t}\n", + "}\n", + "#include \n", + "#include \n", + "#include \n", + "#include \n", + "#include \n", + "void sighandler(int sig) {\n", + "\tstd::ostringstream buffer;\n", + "\tbuffer << \"Abort by \" << (sig == SIGSEGV ? \"segfault\" : \"bus error\") << std::endl;\n", + "\tvoid * stack[64];\n", + "\tstd::size_t depth = backtrace(stack, 64);\n", + "\tif (!depth)\n", + "\t\tbuffer << \" \" << std::endl;\n", + "\telse {\n", + "\t\tchar ** symbols = backtrace_symbols(stack, depth);\n", + "\t\tfor (std::size_t i = 1; i < depth; ++i) {\n", + "\t\t\tstd::string symbol = symbols[i];\n", + "\t\t\t\tif (symbol.find_first_of(' ', 59) != std::string::npos) {\n", + "\t\t\t\t\tstd::string name = symbol.substr(59, symbol.find_first_of(' ', 59) - 59);\n", + "\t\t\t\t\tint status;\n", + "\t\t\t\t\tchar * demangled = abi::__cxa_demangle(name.c_str(), NULL, NULL, &status);\n", + "\t\t\t\t\tif (!status) {\n", + "\t\t\t\t\t\tbuffer << \" \" \n", + "\t\t\t\t\t\t\t<< symbol.substr(0, 59) \n", + "\t\t\t\t\t\t\t<< demangled\n", + "\t\t\t\t\t\t\t<< symbol.substr(59 + name.size())\n", + "\t\t\t\t\t\t\t<< std::endl;\n", + "\t\t\t\t\t\tfree(demangled);\n", + "\t\t\t\t\t} else\n", + "\t\t\t\t\t\tbuffer << \" \" << symbol << std::endl;\n", + "\t\t\t\t} else\n", + "\t\t\t\t\tbuffer << \" \" << symbol << std::endl;\n", + "\t\t\t}\n", + "\t\t\tfree(symbols);\n", + "\t\t}\n", + "\t\tstd::cerr << buffer.str();\n", + "\t\tstd::exit(EXIT_FAILURE);\n", + "\t}\n" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Simplify CPP" + ] + }, + { + "cell_type": "code", + "execution_count": 15, + "metadata": {}, + "outputs": [ { - "cell_type": "heading", - "level": 2, - "metadata": {}, - "source": [ - "Simplify CPP" + "name": "stdout", + "output_type": "stream", + "text": [ + "Overwriting src/poly.cpp\n" ] - }, - { - "cell_type": "code", - "collapsed": false, - "input": [ - "%%file src/poly.cpp\n", - "#define PY_ARRAY_UNIQUE_SYMBOL poly_ARRAY_API\n", - "#define NPY_NO_DEPRECATED_API NPY_1_7_API_VERSION\n", - "#include \n", - "#include \n", - "#include \n", - "#include \n", - "#include \n", - "#include \n", - "#include \n", - "#include \n", - "#include \n", - "#include \n", - "#include \n", - "struct PyObj {\n", - "\ttypedef PyObject * ptr_t;\n", - "\ttypedef PyArrayObject * arrptr_t;\n", - "\tPyObj(): dec(false), ptr(NULL) {}\n", - "\tPyObj(ptr_t p): dec(false), ptr(p) {}\n", - "\t~PyObj() { if(dec) Py_DECREF(ptr); }\n", - "\tPyObj & operator=(ptr_t p) { if(dec) Py_DECREF(ptr); ptr = p; dec = false; return *this; }\n", - "\tPyObj & incref(ptr_t p) { if(dec) Py_DECREF(ptr); ptr = p; dec = (p != NULL); return *this; }\n", - "\toperator bool() const { return ptr; }\n", - "\toperator ptr_t() const { return ptr; }\n", - "\toperator arrptr_t() const { return (arrptr_t)ptr; }\n", - "\tbool dec;\n", - "\tptr_t ptr;\n", - "};\n", - "inline void poly_d1d1(PyObject * pres, npy_intp const * __restrict__ sres, npy_double * __restrict__ cres, \n", - "\t\t\t\t\t\t\t\t\t\t\tPyObject * parg, npy_intp const * __restrict__ sarg, npy_double * __restrict__ carg);\n", - "\n", - "inline void poly_d1d1(PyObject * pres, npy_intp const * __restrict__ sres, npy_double * __restrict__ cres, \n", - "\t\t\t\t\t\t\t\t\t\t\tPyObject * parg, npy_intp const * __restrict__ sarg, npy_double * __restrict__ carg){\n", - "\t\n", - "\tnpy_double arg_i;\n", - "\tdouble sin_arg_i;\n", - "\tfor (npy_intp i0 = 0; i0 < sres[0] - 0; ++i0) {\n", - "\t\targ_i = carg[i0];\n", - "\t\tcres[(int)(i0)] = std::pow(std::sin(arg_i), 2) + (std::pow(arg_i, 3) + std::pow(arg_i, 2) - arg_i - 1) / (std::pow(arg_i, 2) + 2 * arg_i + 1) + std::pow(std::cos(arg_i), 2);\n", - "\t}\n", - "}\n", - "void sighandler(int sig);\n", - "#include \n", - "extern \"C\" {\n", - "\tPyObject * create_signature;\n", - "\tstruct sigaction slot;\n", - "\tPyObject * set_create_signature(PyObject * self, PyObject * args) {\n", - "\t\tif (!PyArg_ParseTuple(args, \"O\", &create_signature)) {\n", - "\t\t\tPyErr_SetString(PyExc_ValueError, \"Invalid Argument to set_create_signature!\");\n", - "\t\t\treturn NULL;\n", - "\t\t}\n", - "\t\tPy_INCREF(create_signature);\n", - "\t\tmemset(&slot, 0, sizeof(slot));\n", - "\t\tslot.sa_handler = &sighandler;\n", - "\t\tsigaction(SIGSEGV, &slot, NULL);\n", - "\t\tsigaction(SIGBUS, &slot, NULL);\n", - "\t\tPy_INCREF(Py_None);\n", - "\t\treturn Py_None;\n", - "\t}\n", - "\tPyObject * run(PyObject * self, PyObject * args) {\n", - "\t\t{\n", - "\t\t\tPyObj pres;\n", - "\t\t\tPyObj parg;\n", - "\t\t\tif (\n", - "\t\t\t\tPyTuple_CheckExact(args) and PyTuple_GET_SIZE(args) == 2\n", - "\t\t\t\tand (pres = PyTuple_GET_ITEM(args, 0)) and PyArray_CheckExact(pres)\n", - "\t\t\t\tand PyArray_TYPE((PyArrayObject *)pres) == NPY_FLOAT64 and PyArray_NDIM((PyArrayObject *)pres) == 1\n", - "\t\t\t\tand (parg = PyTuple_GET_ITEM(args, 1)) and PyArray_CheckExact(parg)\n", - "\t\t\t\tand PyArray_TYPE((PyArrayObject *)parg) == NPY_FLOAT64 and PyArray_NDIM((PyArrayObject *)parg) == 1\n", - "\t\t\t) {\n", - "\t\t\t\tif (!(pres.incref((PyObject *)PyArray_GETCONTIGUOUS((PyArrayObject *)pres)))) {\n", - "\t\t\t\t\tPyErr_SetString(PyExc_ValueError, \"Invalid Argument type on res!\");\n", - "\t\t\t\t\treturn NULL;\n", - "\t\t\t\t}\n", - "\t\t\t\tif (!(parg.incref((PyObject *)PyArray_GETCONTIGUOUS((PyArrayObject *)parg)))) {\n", - "\t\t\t\t\tPyErr_SetString(PyExc_ValueError, \"Invalid Argument type on arg!\");\n", - "\t\t\t\t\treturn NULL;\n", - "\t\t\t\t}\n", - "\t\t\t\ttry {\n", - "\t\t\t\t\tpoly_d1d1(\n", - "\t\t\t\t\t\t pres, PyArray_SHAPE((PyArrayObject *)pres), (npy_double *)PyArray_DATA((PyArrayObject *)pres)\n", - "\t\t\t\t\t\t, parg, PyArray_SHAPE((PyArrayObject *)parg), (npy_double *)PyArray_DATA((PyArrayObject *)parg)\n", - "\t\t\t\t\t);\n", - "\t\t\t\t\tPy_INCREF(Py_None);\n", - "\t\t\t\t\treturn Py_None;\n", - "\t\t\t\t} catch (...) {\n", - "\t\t\t\t\treturn NULL;\n", - "\t\t\t\t}\n", - "\t\t\t} else\n", - "\t\t\t\tPyErr_Clear();\n", - "\t\t}\n", - "\t\tPyObject * signatures = Py_BuildValue(\"(sO)\", \"(lp0\\n(lp1\\nccopy_reg\\n_reconstructor\\np2\\n(chope._ast\\nVariable\\np3\\nc__builtin__\\nobject\\np4\\nNtp5\\nRp6\\n(dp7\\nS'name'\\np8\\nS'res'\\np9\\nsS'dtype'\\np10\\nS'float64'\\np11\\nsS'dims'\\np12\\nI1\\nsbag2\\n(g3\\ng4\\nNtp13\\nRp14\\n(dp15\\ng8\\nS'arg'\\np16\\nsg10\\ng11\\nsg12\\nI1\\nsbaa.\", args);\n", - "\t\tif (!signatures) {\n", - "\t\t\tPyErr_SetString(PyExc_ValueError, \"Error building signature string for poly\");\n", - "\t\t\treturn NULL;\n", - "\t\t}\n", - "\t\treturn PyObject_Call(create_signature, signatures, NULL);\n", - "\t}\n", - "\tPyMethodDef polyMethods[] = {\n", - "\t\t{ \"set_create_signature\", (PyCFunction)set_create_signature, METH_VARARGS },\n", - "\t\t{ \"run\", (PyCFunction)run, METH_VARARGS },\n", - "\t\t{ NULL, NULL }\n", - "\t};\n", - "\tPyMODINIT_FUNC initpoly(void) {\n", - "\t\timport_array();\n", - "\t\tPyImport_ImportModule(\"numpy\");\n", - "\t\t(void)Py_InitModule(\"poly\", polyMethods);\n", - "\t}\n", - "}\n", - "#include \n", - "#include \n", - "#include \n", - "#include \n", - "#include \n", - "void sighandler(int sig) {\n", - "\tstd::ostringstream buffer;\n", - "\tbuffer << \"Abort by \" << (sig == SIGSEGV ? \"segfault\" : \"bus error\") << std::endl;\n", - "\tvoid * stack[64];\n", - "\tstd::size_t depth = backtrace(stack, 64);\n", - "\tif (!depth)\n", - "\t\tbuffer << \" \" << std::endl;\n", - "\telse {\n", - "\t\tchar ** symbols = backtrace_symbols(stack, depth);\n", - "\t\tfor (std::size_t i = 1; i < depth; ++i) {\n", - "\t\t\tstd::string symbol = symbols[i];\n", - "\t\t\t\tif (symbol.find_first_of(' ', 59) != std::string::npos) {\n", - "\t\t\t\t\tstd::string name = symbol.substr(59, symbol.find_first_of(' ', 59) - 59);\n", - "\t\t\t\t\tint status;\n", - "\t\t\t\t\tchar * demangled = abi::__cxa_demangle(name.c_str(), NULL, NULL, &status);\n", - "\t\t\t\t\tif (!status) {\n", - "\t\t\t\t\t\tbuffer << \" \" \n", - "\t\t\t\t\t\t\t<< symbol.substr(0, 59) \n", - "\t\t\t\t\t\t\t<< demangled\n", - "\t\t\t\t\t\t\t<< symbol.substr(59 + name.size())\n", - "\t\t\t\t\t\t\t<< std::endl;\n", - "\t\t\t\t\t\tfree(demangled);\n", - "\t\t\t\t\t} else\n", - "\t\t\t\t\t\tbuffer << \" \" << symbol << std::endl;\n", - "\t\t\t\t} else\n", - "\t\t\t\t\tbuffer << \" \" << symbol << std::endl;\n", - "\t\t\t}\n", - "\t\t\tfree(symbols);\n", - "\t\t}\n", - "\t\tstd::cerr << buffer.str();\n", - "\t\tstd::exit(EXIT_FAILURE);\n", - "\t}" - ], - "language": "python", - "metadata": {}, - "outputs": [ - { - "output_type": "stream", - "stream": "stdout", - "text": [ - "Overwriting src/poly.cpp\n" - ] - } - ], - "prompt_number": 15 - }, - { - "cell_type": "code", - "collapsed": false, - "input": [ - "%%file src/poly_opt.cpp\n", - "#define PY_ARRAY_UNIQUE_SYMBOL poly_ARRAY_API\n", - "#define NPY_NO_DEPRECATED_API NPY_1_7_API_VERSION\n", - "#include \n", - "#include \n", - "#include \n", - "#include \n", - "#include \n", - "#include \n", - "#include \n", - "#include \n", - "#include \n", - "#include \n", - "#include \n", - "struct PyObj {\n", - "\ttypedef PyObject * ptr_t;\n", - "\ttypedef PyArrayObject * arrptr_t;\n", - "\tPyObj(): dec(false), ptr(NULL) {}\n", - "\tPyObj(ptr_t p): dec(false), ptr(p) {}\n", - "\t~PyObj() { if(dec) Py_DECREF(ptr); }\n", - "\tPyObj & operator=(ptr_t p) { if(dec) Py_DECREF(ptr); ptr = p; dec = false; return *this; }\n", - "\tPyObj & incref(ptr_t p) { if(dec) Py_DECREF(ptr); ptr = p; dec = (p != NULL); return *this; }\n", - "\toperator bool() const { return ptr; }\n", - "\toperator ptr_t() const { return ptr; }\n", - "\toperator arrptr_t() const { return (arrptr_t)ptr; }\n", - "\tbool dec;\n", - "\tptr_t ptr;\n", - "};\n", - "\n", - "inline void poly_d1d1(PyObject * pres, npy_intp const * __restrict__ sres, npy_double * __restrict__ cres, \n", - "\t\t\t\t\t\t\t\t\t\t\tPyObject * parg, npy_intp const * __restrict__ sarg, npy_double * __restrict__ carg);\n", - "\n", - "inline void poly_d1d1(PyObject * pres, npy_intp const * __restrict__ sres, npy_double * __restrict__ cres, \n", - "\t\t\t\t\t\t\t\t\t\t\tPyObject * parg, npy_intp const * __restrict__ sarg, npy_double * __restrict__ carg){\n", - "\tfor (npy_intp i0 = 0; i0 < sres[0] - 0; ++i0) {\n", - "\t\tcres[(int)(i0)] = carg[i0];\n", - "\t}\n", - "}\n", - "\n", - "void sighandler(int sig);\n", - "#include \n", - "extern \"C\" {\n", - "\tPyObject * create_signature;\n", - "\tstruct sigaction slot;\n", - "\tPyObject * set_create_signature(PyObject * self, PyObject * args) {\n", - "\t\tif (!PyArg_ParseTuple(args, \"O\", &create_signature)) {\n", - "\t\t\tPyErr_SetString(PyExc_ValueError, \"Invalid Argument to set_create_signature!\");\n", - "\t\t\treturn NULL;\n", - "\t\t}\n", - "\t\tPy_INCREF(create_signature);\n", - "\t\tmemset(&slot, 0, sizeof(slot));\n", - "\t\tslot.sa_handler = &sighandler;\n", - "\t\tsigaction(SIGSEGV, &slot, NULL);\n", - "\t\tsigaction(SIGBUS, &slot, NULL);\n", - "\t\tPy_INCREF(Py_None);\n", - "\t\treturn Py_None;\n", - "\t}\n", - "\tPyObject * run(PyObject * self, PyObject * args) {\n", - "\t\t{\n", - "\t\t\tPyObj pres;\n", - "\t\t\tPyObj parg;\n", - "\t\t\tif (\n", - "\t\t\t\tPyTuple_CheckExact(args) and PyTuple_GET_SIZE(args) == 2\n", - "\t\t\t\tand (pres = PyTuple_GET_ITEM(args, 0)) and PyArray_CheckExact(pres)\n", - "\t\t\t\tand PyArray_TYPE((PyArrayObject *)pres) == NPY_FLOAT64 and PyArray_NDIM((PyArrayObject *)pres) == 1\n", - "\t\t\t\tand (parg = PyTuple_GET_ITEM(args, 1)) and PyArray_CheckExact(parg)\n", - "\t\t\t\tand PyArray_TYPE((PyArrayObject *)parg) == NPY_FLOAT64 and PyArray_NDIM((PyArrayObject *)parg) == 1\n", - "\t\t\t) {\n", - "\t\t\t\tif (!(pres.incref((PyObject *)PyArray_GETCONTIGUOUS((PyArrayObject *)pres)))) {\n", - "\t\t\t\t\tPyErr_SetString(PyExc_ValueError, \"Invalid Argument type on res!\");\n", - "\t\t\t\t\treturn NULL;\n", - "\t\t\t\t}\n", - "\t\t\t\tif (!(parg.incref((PyObject *)PyArray_GETCONTIGUOUS((PyArrayObject *)parg)))) {\n", - "\t\t\t\t\tPyErr_SetString(PyExc_ValueError, \"Invalid Argument type on arg!\");\n", - "\t\t\t\t\treturn NULL;\n", - "\t\t\t\t}\n", - "\t\t\t\ttry {\n", - "\t\t\t\t\tpoly_d1d1(\n", - "\t\t\t\t\t\t pres, PyArray_SHAPE((PyArrayObject *)pres), (npy_double *)PyArray_DATA((PyArrayObject *)pres)\n", - "\t\t\t\t\t\t, parg, PyArray_SHAPE((PyArrayObject *)parg), (npy_double *)PyArray_DATA((PyArrayObject *)parg)\n", - "\t\t\t\t\t);\n", - "\t\t\t\t\tPy_INCREF(Py_None);\n", - "\t\t\t\t\treturn Py_None;\n", - "\t\t\t\t} catch (...) {\n", - "\t\t\t\t\treturn NULL;\n", - "\t\t\t\t}\n", - "\t\t\t} else\n", - "\t\t\t\tPyErr_Clear();\n", - "\t\t}\n", - "\t\tPyObject * signatures = Py_BuildValue(\"(sO)\", \"(lp0\\n(lp1\\nccopy_reg\\n_reconstructor\\np2\\n(chope._ast\\nVariable\\np3\\nc__builtin__\\nobject\\np4\\nNtp5\\nRp6\\n(dp7\\nS'name'\\np8\\nS'res'\\np9\\nsS'dtype'\\np10\\nS'float64'\\np11\\nsS'dims'\\np12\\nI1\\nsbag2\\n(g3\\ng4\\nNtp13\\nRp14\\n(dp15\\ng8\\nS'arg'\\np16\\nsg10\\ng11\\nsg12\\nI1\\nsbaa.\", args);\n", - "\t\tif (!signatures) {\n", - "\t\t\tPyErr_SetString(PyExc_ValueError, \"Error building signature string for poly\");\n", - "\t\t\treturn NULL;\n", - "\t\t}\n", - "\t\treturn PyObject_Call(create_signature, signatures, NULL);\n", - "\t}\n", - "\tPyMethodDef polyMethods[] = {\n", - "\t\t{ \"set_create_signature\", (PyCFunction)set_create_signature, METH_VARARGS },\n", - "\t\t{ \"run\", (PyCFunction)run, METH_VARARGS },\n", - "\t\t{ NULL, NULL }\n", - "\t};\n", - "\tPyMODINIT_FUNC initpoly(void) {\n", - "\t\timport_array();\n", - "\t\tPyImport_ImportModule(\"numpy\");\n", - "\t\t(void)Py_InitModule(\"poly\", polyMethods);\n", - "\t}\n", - "}\n", - "#include \n", - "#include \n", - "#include \n", - "#include \n", - "#include \n", - "void sighandler(int sig) {\n", - "\tstd::ostringstream buffer;\n", - "\tbuffer << \"Abort by \" << (sig == SIGSEGV ? \"segfault\" : \"bus error\") << std::endl;\n", - "\tvoid * stack[64];\n", - "\tstd::size_t depth = backtrace(stack, 64);\n", - "\tif (!depth)\n", - "\t\tbuffer << \" \" << std::endl;\n", - "\telse {\n", - "\t\tchar ** symbols = backtrace_symbols(stack, depth);\n", - "\t\tfor (std::size_t i = 1; i < depth; ++i) {\n", - "\t\t\tstd::string symbol = symbols[i];\n", - "\t\t\t\tif (symbol.find_first_of(' ', 59) != std::string::npos) {\n", - "\t\t\t\t\tstd::string name = symbol.substr(59, symbol.find_first_of(' ', 59) - 59);\n", - "\t\t\t\t\tint status;\n", - "\t\t\t\t\tchar * demangled = abi::__cxa_demangle(name.c_str(), NULL, NULL, &status);\n", - "\t\t\t\t\tif (!status) {\n", - "\t\t\t\t\t\tbuffer << \" \" \n", - "\t\t\t\t\t\t\t<< symbol.substr(0, 59) \n", - "\t\t\t\t\t\t\t<< demangled\n", - "\t\t\t\t\t\t\t<< symbol.substr(59 + name.size())\n", - "\t\t\t\t\t\t\t<< std::endl;\n", - "\t\t\t\t\t\tfree(demangled);\n", - "\t\t\t\t\t} else\n", - "\t\t\t\t\t\tbuffer << \" \" << symbol << std::endl;\n", - "\t\t\t\t} else\n", - "\t\t\t\t\tbuffer << \" \" << symbol << std::endl;\n", - "\t\t\t}\n", - "\t\t\tfree(symbols);\n", - "\t\t}\n", - "\t\tstd::cerr << buffer.str();\n", - "\t\tstd::exit(EXIT_FAILURE);\n", - "\t}\n" - ], - "language": "python", - "metadata": {}, - "outputs": [ - { - "output_type": "stream", - "stream": "stdout", - "text": [ - "Overwriting src/poly_opt.cpp\n" - ] - } - ], - "prompt_number": 7 - }, + } + ], + "source": [ + "%%file src/poly.cpp\n", + "#define PY_ARRAY_UNIQUE_SYMBOL poly_ARRAY_API\n", + "#define NPY_NO_DEPRECATED_API NPY_1_7_API_VERSION\n", + "#include \n", + "#include \n", + "#include \n", + "#include \n", + "#include \n", + "#include \n", + "#include \n", + "#include \n", + "#include \n", + "#include \n", + "#include \n", + "struct PyObj {\n", + "\ttypedef PyObject * ptr_t;\n", + "\ttypedef PyArrayObject * arrptr_t;\n", + "\tPyObj(): dec(false), ptr(NULL) {}\n", + "\tPyObj(ptr_t p): dec(false), ptr(p) {}\n", + "\t~PyObj() { if(dec) Py_DECREF(ptr); }\n", + "\tPyObj & operator=(ptr_t p) { if(dec) Py_DECREF(ptr); ptr = p; dec = false; return *this; }\n", + "\tPyObj & incref(ptr_t p) { if(dec) Py_DECREF(ptr); ptr = p; dec = (p != NULL); return *this; }\n", + "\toperator bool() const { return ptr; }\n", + "\toperator ptr_t() const { return ptr; }\n", + "\toperator arrptr_t() const { return (arrptr_t)ptr; }\n", + "\tbool dec;\n", + "\tptr_t ptr;\n", + "};\n", + "inline void poly_d1d1(PyObject * pres, npy_intp const * __restrict__ sres, npy_double * __restrict__ cres, \n", + "\t\t\t\t\t\t\t\t\t\t\tPyObject * parg, npy_intp const * __restrict__ sarg, npy_double * __restrict__ carg);\n", + "\n", + "inline void poly_d1d1(PyObject * pres, npy_intp const * __restrict__ sres, npy_double * __restrict__ cres, \n", + "\t\t\t\t\t\t\t\t\t\t\tPyObject * parg, npy_intp const * __restrict__ sarg, npy_double * __restrict__ carg){\n", + "\t\n", + "\tnpy_double arg_i;\n", + "\tdouble sin_arg_i;\n", + "\tfor (npy_intp i0 = 0; i0 < sres[0] - 0; ++i0) {\n", + "\t\targ_i = carg[i0];\n", + "\t\tcres[(int)(i0)] = std::pow(std::sin(arg_i), 2) + (std::pow(arg_i, 3) + std::pow(arg_i, 2) - arg_i - 1) / (std::pow(arg_i, 2) + 2 * arg_i + 1) + std::pow(std::cos(arg_i), 2);\n", + "\t}\n", + "}\n", + "void sighandler(int sig);\n", + "#include \n", + "extern \"C\" {\n", + "\tPyObject * create_signature;\n", + "\tstruct sigaction slot;\n", + "\tPyObject * set_create_signature(PyObject * self, PyObject * args) {\n", + "\t\tif (!PyArg_ParseTuple(args, \"O\", &create_signature)) {\n", + "\t\t\tPyErr_SetString(PyExc_ValueError, \"Invalid Argument to set_create_signature!\");\n", + "\t\t\treturn NULL;\n", + "\t\t}\n", + "\t\tPy_INCREF(create_signature);\n", + "\t\tmemset(&slot, 0, sizeof(slot));\n", + "\t\tslot.sa_handler = &sighandler;\n", + "\t\tsigaction(SIGSEGV, &slot, NULL);\n", + "\t\tsigaction(SIGBUS, &slot, NULL);\n", + "\t\tPy_INCREF(Py_None);\n", + "\t\treturn Py_None;\n", + "\t}\n", + "\tPyObject * run(PyObject * self, PyObject * args) {\n", + "\t\t{\n", + "\t\t\tPyObj pres;\n", + "\t\t\tPyObj parg;\n", + "\t\t\tif (\n", + "\t\t\t\tPyTuple_CheckExact(args) and PyTuple_GET_SIZE(args) == 2\n", + "\t\t\t\tand (pres = PyTuple_GET_ITEM(args, 0)) and PyArray_CheckExact(pres)\n", + "\t\t\t\tand PyArray_TYPE((PyArrayObject *)pres) == NPY_FLOAT64 and PyArray_NDIM((PyArrayObject *)pres) == 1\n", + "\t\t\t\tand (parg = PyTuple_GET_ITEM(args, 1)) and PyArray_CheckExact(parg)\n", + "\t\t\t\tand PyArray_TYPE((PyArrayObject *)parg) == NPY_FLOAT64 and PyArray_NDIM((PyArrayObject *)parg) == 1\n", + "\t\t\t) {\n", + "\t\t\t\tif (!(pres.incref((PyObject *)PyArray_GETCONTIGUOUS((PyArrayObject *)pres)))) {\n", + "\t\t\t\t\tPyErr_SetString(PyExc_ValueError, \"Invalid Argument type on res!\");\n", + "\t\t\t\t\treturn NULL;\n", + "\t\t\t\t}\n", + "\t\t\t\tif (!(parg.incref((PyObject *)PyArray_GETCONTIGUOUS((PyArrayObject *)parg)))) {\n", + "\t\t\t\t\tPyErr_SetString(PyExc_ValueError, \"Invalid Argument type on arg!\");\n", + "\t\t\t\t\treturn NULL;\n", + "\t\t\t\t}\n", + "\t\t\t\ttry {\n", + "\t\t\t\t\tpoly_d1d1(\n", + "\t\t\t\t\t\t pres, PyArray_SHAPE((PyArrayObject *)pres), (npy_double *)PyArray_DATA((PyArrayObject *)pres)\n", + "\t\t\t\t\t\t, parg, PyArray_SHAPE((PyArrayObject *)parg), (npy_double *)PyArray_DATA((PyArrayObject *)parg)\n", + "\t\t\t\t\t);\n", + "\t\t\t\t\tPy_INCREF(Py_None);\n", + "\t\t\t\t\treturn Py_None;\n", + "\t\t\t\t} catch (...) {\n", + "\t\t\t\t\treturn NULL;\n", + "\t\t\t\t}\n", + "\t\t\t} else\n", + "\t\t\t\tPyErr_Clear();\n", + "\t\t}\n", + "\t\tPyObject * signatures = Py_BuildValue(\"(sO)\", \"(lp0\\n(lp1\\nccopy_reg\\n_reconstructor\\np2\\n(chope._ast\\nVariable\\np3\\nc__builtin__\\nobject\\np4\\nNtp5\\nRp6\\n(dp7\\nS'name'\\np8\\nS'res'\\np9\\nsS'dtype'\\np10\\nS'float64'\\np11\\nsS'dims'\\np12\\nI1\\nsbag2\\n(g3\\ng4\\nNtp13\\nRp14\\n(dp15\\ng8\\nS'arg'\\np16\\nsg10\\ng11\\nsg12\\nI1\\nsbaa.\", args);\n", + "\t\tif (!signatures) {\n", + "\t\t\tPyErr_SetString(PyExc_ValueError, \"Error building signature string for poly\");\n", + "\t\t\treturn NULL;\n", + "\t\t}\n", + "\t\treturn PyObject_Call(create_signature, signatures, NULL);\n", + "\t}\n", + "\tPyMethodDef polyMethods[] = {\n", + "\t\t{ \"set_create_signature\", (PyCFunction)set_create_signature, METH_VARARGS },\n", + "\t\t{ \"run\", (PyCFunction)run, METH_VARARGS },\n", + "\t\t{ NULL, NULL }\n", + "\t};\n", + "\tPyMODINIT_FUNC initpoly(void) {\n", + "\t\timport_array();\n", + "\t\tPyImport_ImportModule(\"numpy\");\n", + "\t\t(void)Py_InitModule(\"poly\", polyMethods);\n", + "\t}\n", + "}\n", + "#include \n", + "#include \n", + "#include \n", + "#include \n", + "#include \n", + "void sighandler(int sig) {\n", + "\tstd::ostringstream buffer;\n", + "\tbuffer << \"Abort by \" << (sig == SIGSEGV ? \"segfault\" : \"bus error\") << std::endl;\n", + "\tvoid * stack[64];\n", + "\tstd::size_t depth = backtrace(stack, 64);\n", + "\tif (!depth)\n", + "\t\tbuffer << \" \" << std::endl;\n", + "\telse {\n", + "\t\tchar ** symbols = backtrace_symbols(stack, depth);\n", + "\t\tfor (std::size_t i = 1; i < depth; ++i) {\n", + "\t\t\tstd::string symbol = symbols[i];\n", + "\t\t\t\tif (symbol.find_first_of(' ', 59) != std::string::npos) {\n", + "\t\t\t\t\tstd::string name = symbol.substr(59, symbol.find_first_of(' ', 59) - 59);\n", + "\t\t\t\t\tint status;\n", + "\t\t\t\t\tchar * demangled = abi::__cxa_demangle(name.c_str(), NULL, NULL, &status);\n", + "\t\t\t\t\tif (!status) {\n", + "\t\t\t\t\t\tbuffer << \" \" \n", + "\t\t\t\t\t\t\t<< symbol.substr(0, 59) \n", + "\t\t\t\t\t\t\t<< demangled\n", + "\t\t\t\t\t\t\t<< symbol.substr(59 + name.size())\n", + "\t\t\t\t\t\t\t<< std::endl;\n", + "\t\t\t\t\t\tfree(demangled);\n", + "\t\t\t\t\t} else\n", + "\t\t\t\t\t\tbuffer << \" \" << symbol << std::endl;\n", + "\t\t\t\t} else\n", + "\t\t\t\t\tbuffer << \" \" << symbol << std::endl;\n", + "\t\t\t}\n", + "\t\t\tfree(symbols);\n", + "\t\t}\n", + "\t\tstd::cerr << buffer.str();\n", + "\t\tstd::exit(EXIT_FAILURE);\n", + "\t}" + ] + }, + { + "cell_type": "code", + "execution_count": 7, + "metadata": {}, + "outputs": [ { - "cell_type": "heading", - "level": 2, - "metadata": {}, - "source": [ - "Pairwise distance" + "name": "stdout", + "output_type": "stream", + "text": [ + "Overwriting src/poly_opt.cpp\n" ] - }, - { - "cell_type": "code", - "collapsed": false, - "input": [ - "%%file src/pairwise.cpp\n", - "#define PY_ARRAY_UNIQUE_SYMBOL pairwise_ARRAY_API\n", - "#define NPY_NO_DEPRECATED_API NPY_1_7_API_VERSION\n", - "#include \n", - "#include \n", - "#include \n", - "#include \n", - "#include \n", - "#include \n", - "#include \n", - "#include \n", - "#include \n", - "#include \n", - "#include \n", - "struct PyObj {\n", - "\ttypedef PyObject * ptr_t;\n", - "\ttypedef PyArrayObject * arrptr_t;\n", - "\tPyObj(): dec(false), ptr(NULL) {}\n", - "\tPyObj(ptr_t p): dec(false), ptr(p) {}\n", - "\t~PyObj() { if(dec) Py_DECREF(ptr); }\n", - "\tPyObj & operator=(ptr_t p) { if(dec) Py_DECREF(ptr); ptr = p; dec = false; return *this; }\n", - "\tPyObj & incref(ptr_t p) { if(dec) Py_DECREF(ptr); ptr = p; dec = (p != NULL); return *this; }\n", - "\toperator bool() const { return ptr; }\n", - "\toperator ptr_t() const { return ptr; }\n", - "\toperator arrptr_t() const { return (arrptr_t)ptr; }\n", - "\tbool dec;\n", - "\tptr_t ptr;\n", - "};\n", - "\n", - "inline void pairwise_d2d2JJ(PyObject * pX, \n", - "\t\t\t\t\t\t\t\t\t\t\t\t\t\tnpy_intp const * __restrict__ sX, \n", - "\t\t\t\t\t\t\t\t\t\t\t\t\t\tnpy_double * __restrict__ cX, \n", - "\t\t\t\t\t\t\t\t\t\t\t\t\t\tPyObject * pD, \n", - "\t\t\t\t\t\t\t\t\t\t\t\t\t\tnpy_intp const * __restrict__ sD, \n", - "\t\t\t\t\t\t\t\t\t\t\t\t\t\tnpy_double * __restrict__ cD, \n", - "\t\t\t\t\t\t\t\t\t\t\t\t\t\tnpy_int64 const cM, \n", - "\t\t\t\t\t\t\t\t\t\t\t\t\t\tnpy_int64 const cN);\n", - "\t\t\t\t\t\t\t\t\t\t\t\t\t\t\n", - "inline void pairwise_d2d2JJ(PyObject * pX, \n", - "\t\t\t\t\t\t\t\t\t\t\t\t\t\tnpy_intp const * __restrict__ sX, \n", - "\t\t\t\t\t\t\t\t\t\t\t\t\t\tnpy_double * __restrict__ cX, \n", - "\t\t\t\t\t\t\t\t\t\t\t\t\t\tPyObject * pD, \n", - "\t\t\t\t\t\t\t\t\t\t\t\t\t\tnpy_intp const * __restrict__ sD, \n", - "\t\t\t\t\t\t\t\t\t\t\t\t\t\tnpy_double * __restrict__ cD, \n", - "\t\t\t\t\t\t\t\t\t\t\t\t\t\tnpy_int64 const cM, \n", - "\t\t\t\t\t\t\t\t\t\t\t\t\t\tnpy_int64 const cN){\n", - "\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\n", - "\tnpy_double cd = 0.0;\n", - "\tnpy_double ctmp = 0.0;\n", - "\t\n", - "\tnpy_intp cj;\n", - "\tnpy_intp ck;\n", - "\tnpy_intp x_i_idx;\n", - "\tnpy_intp x_j_idx;\n", - "\tnpy_intp d_i_idx;\n", - "\tnpy_intp const xp = sX[1];\n", - "\tnpy_intp const dp = sD[1];\n", - "\t\n", - "\tfor (npy_intp ci = 0; ci < cM; ++ci) {\n", - "\t\tx_i_idx = ci*xp;\n", - "\t\td_i_idx = ci*dp;\n", - "\t\tfor (cj = 0; cj < cM; ++cj) {\n", - "\t\t\tcd = 0.0;\n", - "\t\t\tx_j_idx = cj*xp;\n", - "\t\t\tfor (ck = 0; ck < cN; ++ck) {\n", - "\t\t\t\tctmp = (cX[(x_i_idx + ck)] - cX[(x_j_idx + ck)]);\n", - "\t\t\t\tcd += (ctmp * ctmp);\n", - "\t\t\t}\n", - "\t\t\tcD[(d_i_idx + cj)] = std::sqrt(cd);\n", - "\t\t}\n", - "\t}\n", - "}\n", - "\n", - "void sighandler(int sig);\n", - "#include \n", - "extern \"C\" {\n", - "\tPyObject * create_signature;\n", - "\tstruct sigaction slot;\n", - "\tPyObject * set_create_signature(PyObject * self, PyObject * args) {\n", - "\t\tif (!PyArg_ParseTuple(args, \"O\", &create_signature)) {\n", - "\t\t\tPyErr_SetString(PyExc_ValueError, \"Invalid Argument to set_create_signature!\");\n", - "\t\t\treturn NULL;\n", - "\t\t}\n", - "\t\tPy_INCREF(create_signature);\n", - "\t\tmemset(&slot, 0, sizeof(slot));\n", - "\t\tslot.sa_handler = &sighandler;\n", - "\t\tsigaction(SIGSEGV, &slot, NULL);\n", - "\t\tsigaction(SIGBUS, &slot, NULL);\n", - "\t\tPy_INCREF(Py_None);\n", - "\t\treturn Py_None;\n", - "\t}\n", - "\tPyObject * run(PyObject * self, PyObject * args) {\n", - "\t\t{\n", - "\t\t\tPyObj pX;\n", - "\t\t\tPyObj pD;\n", - "\t\t\tPyObject * pM; npy_int64 cM;\n", - "\t\t\tPyObject * pN; npy_int64 cN;\n", - "\t\t\tif (\n", - "\t\t\t\tPyTuple_CheckExact(args) and PyTuple_GET_SIZE(args) == 4\n", - "\t\t\t\tand (pX = PyTuple_GET_ITEM(args, 0)) and PyArray_CheckExact(pX)\n", - "\t\t\t\tand PyArray_TYPE((PyArrayObject *)pX) == NPY_FLOAT64 and PyArray_NDIM((PyArrayObject *)pX) == 2\n", - "\t\t\t\tand (pD = PyTuple_GET_ITEM(args, 1)) and PyArray_CheckExact(pD)\n", - "\t\t\t\tand PyArray_TYPE((PyArrayObject *)pD) == NPY_FLOAT64 and PyArray_NDIM((PyArrayObject *)pD) == 2\n", - "\t\t\t\tand (pM = PyTuple_GET_ITEM(args, 2)) and PyInt_CheckExact(pM)\n", - "\t\t\t\tand (pN = PyTuple_GET_ITEM(args, 3)) and PyInt_CheckExact(pN)\n", - "\t\t\t) {\n", - "\t\t\t\tif (!(pX.incref((PyObject *)PyArray_GETCONTIGUOUS((PyArrayObject *)pX)))) {\n", - "\t\t\t\t\tPyErr_SetString(PyExc_ValueError, \"Invalid Argument type on X!\");\n", - "\t\t\t\t\treturn NULL;\n", - "\t\t\t\t}\n", - "\t\t\t\tif (!(pD.incref((PyObject *)PyArray_GETCONTIGUOUS((PyArrayObject *)pD)))) {\n", - "\t\t\t\t\tPyErr_SetString(PyExc_ValueError, \"Invalid Argument type on D!\");\n", - "\t\t\t\t\treturn NULL;\n", - "\t\t\t\t}\n", - "\t\t\t\tcM = PyInt_AS_LONG(pM);\n", - "\t\t\t\tcN = PyInt_AS_LONG(pN);\n", - "\t\t\t\ttry {\n", - "\t\t\t\t\tpairwise_d2d2JJ(\n", - "\t\t\t\t\t\t pX, PyArray_SHAPE((PyArrayObject *)pX), (npy_double *)PyArray_DATA((PyArrayObject *)pX)\n", - "\t\t\t\t\t\t, pD, PyArray_SHAPE((PyArrayObject *)pD), (npy_double *)PyArray_DATA((PyArrayObject *)pD)\n", - "\t\t\t\t\t\t, cM\n", - "\t\t\t\t\t\t, cN\n", - "\t\t\t\t\t);\n", - "\t\t\t\t\tPy_INCREF(Py_None);\n", - "\t\t\t\t\treturn Py_None;\n", - "\t\t\t\t} catch (...) {\n", - "\t\t\t\t\treturn NULL;\n", - "\t\t\t\t}\n", - "\t\t\t} else\n", - "\t\t\t\tPyErr_Clear();\n", - "\t\t}\n", - "\t\tPyObject * signatures = Py_BuildValue(\"(sO)\", \"(lp0\\n(lp1\\nccopy_reg\\n_reconstructor\\np2\\n(chope._ast\\nVariable\\np3\\nc__builtin__\\nobject\\np4\\nNtp5\\nRp6\\n(dp7\\nS'name'\\np8\\nS'X'\\np9\\nsS'dtype'\\np10\\nS'float64'\\np11\\nsS'dims'\\np12\\nI2\\nsbag2\\n(g3\\ng4\\nNtp13\\nRp14\\n(dp15\\ng8\\nS'D'\\np16\\nsg10\\ng11\\nsg12\\nI2\\nsbag2\\n(g3\\ng4\\nNtp17\\nRp18\\n(dp19\\ng8\\nS'M'\\np20\\nsg10\\nc__builtin__\\nint\\np21\\nsg12\\nI0\\nsbag2\\n(g3\\ng4\\nNtp22\\nRp23\\n(dp24\\ng8\\nS'N'\\np25\\nsg10\\ng21\\nsg12\\nI0\\nsbaa.\", args);\n", - "\t\tif (!signatures) {\n", - "\t\t\tPyErr_SetString(PyExc_ValueError, \"Error building signature string for pairwise\");\n", - "\t\t\treturn NULL;\n", - "\t\t}\n", - "\t\treturn PyObject_Call(create_signature, signatures, NULL);\n", - "\t}\n", - "\tPyMethodDef pairwiseMethods[] = {\n", - "\t\t{ \"set_create_signature\", (PyCFunction)set_create_signature, METH_VARARGS },\n", - "\t\t{ \"run\", (PyCFunction)run, METH_VARARGS },\n", - "\t\t{ NULL, NULL }\n", - "\t};\n", - "\tPyMODINIT_FUNC initpairwise(void) {\n", - "\t\timport_array();\n", - "\t\tPyImport_ImportModule(\"numpy\");\n", - "\t\t(void)Py_InitModule(\"pairwise\", pairwiseMethods);\n", - "\t}\n", - "}\n", - "#include \n", - "#include \n", - "#include \n", - "#include \n", - "#include \n", - "void sighandler(int sig) {\n", - "\tstd::ostringstream buffer;\n", - "\tbuffer << \"Abort by \" << (sig == SIGSEGV ? \"segfault\" : \"bus error\") << std::endl;\n", - "\tvoid * stack[64];\n", - "\tstd::size_t depth = backtrace(stack, 64);\n", - "\tif (!depth)\n", - "\t\tbuffer << \" \" << std::endl;\n", - "\telse {\n", - "\t\tchar ** symbols = backtrace_symbols(stack, depth);\n", - "\t\tfor (std::size_t i = 1; i < depth; ++i) {\n", - "\t\t\tstd::string symbol = symbols[i];\n", - "\t\t\t\tif (symbol.find_first_of(' ', 59) != std::string::npos) {\n", - "\t\t\t\t\tstd::string name = symbol.substr(59, symbol.find_first_of(' ', 59) - 59);\n", - "\t\t\t\t\tint status;\n", - "\t\t\t\t\tchar * demangled = abi::__cxa_demangle(name.c_str(), NULL, NULL, &status);\n", - "\t\t\t\t\tif (!status) {\n", - "\t\t\t\t\t\tbuffer << \" \" \n", - "\t\t\t\t\t\t\t<< symbol.substr(0, 59) \n", - "\t\t\t\t\t\t\t<< demangled\n", - "\t\t\t\t\t\t\t<< symbol.substr(59 + name.size())\n", - "\t\t\t\t\t\t\t<< std::endl;\n", - "\t\t\t\t\t\tfree(demangled);\n", - "\t\t\t\t\t} else\n", - "\t\t\t\t\t\tbuffer << \" \" << symbol << std::endl;\n", - "\t\t\t\t} else\n", - "\t\t\t\t\tbuffer << \" \" << symbol << std::endl;\n", - "\t\t\t}\n", - "\t\t\tfree(symbols);\n", - "\t\t}\n", - "\t\tstd::cerr << buffer.str();\n", - "\t\tstd::exit(EXIT_FAILURE);\n", - "\t}\n" - ], - "language": "python", - "metadata": {}, - "outputs": [ - { - "output_type": "stream", - "stream": "stdout", - "text": [ - "Overwriting src/pairwise.cpp\n" - ] - } - ], - "prompt_number": 12 - }, + } + ], + "source": [ + "%%file src/poly_opt.cpp\n", + "#define PY_ARRAY_UNIQUE_SYMBOL poly_ARRAY_API\n", + "#define NPY_NO_DEPRECATED_API NPY_1_7_API_VERSION\n", + "#include \n", + "#include \n", + "#include \n", + "#include \n", + "#include \n", + "#include \n", + "#include \n", + "#include \n", + "#include \n", + "#include \n", + "#include \n", + "struct PyObj {\n", + "\ttypedef PyObject * ptr_t;\n", + "\ttypedef PyArrayObject * arrptr_t;\n", + "\tPyObj(): dec(false), ptr(NULL) {}\n", + "\tPyObj(ptr_t p): dec(false), ptr(p) {}\n", + "\t~PyObj() { if(dec) Py_DECREF(ptr); }\n", + "\tPyObj & operator=(ptr_t p) { if(dec) Py_DECREF(ptr); ptr = p; dec = false; return *this; }\n", + "\tPyObj & incref(ptr_t p) { if(dec) Py_DECREF(ptr); ptr = p; dec = (p != NULL); return *this; }\n", + "\toperator bool() const { return ptr; }\n", + "\toperator ptr_t() const { return ptr; }\n", + "\toperator arrptr_t() const { return (arrptr_t)ptr; }\n", + "\tbool dec;\n", + "\tptr_t ptr;\n", + "};\n", + "\n", + "inline void poly_d1d1(PyObject * pres, npy_intp const * __restrict__ sres, npy_double * __restrict__ cres, \n", + "\t\t\t\t\t\t\t\t\t\t\tPyObject * parg, npy_intp const * __restrict__ sarg, npy_double * __restrict__ carg);\n", + "\n", + "inline void poly_d1d1(PyObject * pres, npy_intp const * __restrict__ sres, npy_double * __restrict__ cres, \n", + "\t\t\t\t\t\t\t\t\t\t\tPyObject * parg, npy_intp const * __restrict__ sarg, npy_double * __restrict__ carg){\n", + "\tfor (npy_intp i0 = 0; i0 < sres[0] - 0; ++i0) {\n", + "\t\tcres[(int)(i0)] = carg[i0];\n", + "\t}\n", + "}\n", + "\n", + "void sighandler(int sig);\n", + "#include \n", + "extern \"C\" {\n", + "\tPyObject * create_signature;\n", + "\tstruct sigaction slot;\n", + "\tPyObject * set_create_signature(PyObject * self, PyObject * args) {\n", + "\t\tif (!PyArg_ParseTuple(args, \"O\", &create_signature)) {\n", + "\t\t\tPyErr_SetString(PyExc_ValueError, \"Invalid Argument to set_create_signature!\");\n", + "\t\t\treturn NULL;\n", + "\t\t}\n", + "\t\tPy_INCREF(create_signature);\n", + "\t\tmemset(&slot, 0, sizeof(slot));\n", + "\t\tslot.sa_handler = &sighandler;\n", + "\t\tsigaction(SIGSEGV, &slot, NULL);\n", + "\t\tsigaction(SIGBUS, &slot, NULL);\n", + "\t\tPy_INCREF(Py_None);\n", + "\t\treturn Py_None;\n", + "\t}\n", + "\tPyObject * run(PyObject * self, PyObject * args) {\n", + "\t\t{\n", + "\t\t\tPyObj pres;\n", + "\t\t\tPyObj parg;\n", + "\t\t\tif (\n", + "\t\t\t\tPyTuple_CheckExact(args) and PyTuple_GET_SIZE(args) == 2\n", + "\t\t\t\tand (pres = PyTuple_GET_ITEM(args, 0)) and PyArray_CheckExact(pres)\n", + "\t\t\t\tand PyArray_TYPE((PyArrayObject *)pres) == NPY_FLOAT64 and PyArray_NDIM((PyArrayObject *)pres) == 1\n", + "\t\t\t\tand (parg = PyTuple_GET_ITEM(args, 1)) and PyArray_CheckExact(parg)\n", + "\t\t\t\tand PyArray_TYPE((PyArrayObject *)parg) == NPY_FLOAT64 and PyArray_NDIM((PyArrayObject *)parg) == 1\n", + "\t\t\t) {\n", + "\t\t\t\tif (!(pres.incref((PyObject *)PyArray_GETCONTIGUOUS((PyArrayObject *)pres)))) {\n", + "\t\t\t\t\tPyErr_SetString(PyExc_ValueError, \"Invalid Argument type on res!\");\n", + "\t\t\t\t\treturn NULL;\n", + "\t\t\t\t}\n", + "\t\t\t\tif (!(parg.incref((PyObject *)PyArray_GETCONTIGUOUS((PyArrayObject *)parg)))) {\n", + "\t\t\t\t\tPyErr_SetString(PyExc_ValueError, \"Invalid Argument type on arg!\");\n", + "\t\t\t\t\treturn NULL;\n", + "\t\t\t\t}\n", + "\t\t\t\ttry {\n", + "\t\t\t\t\tpoly_d1d1(\n", + "\t\t\t\t\t\t pres, PyArray_SHAPE((PyArrayObject *)pres), (npy_double *)PyArray_DATA((PyArrayObject *)pres)\n", + "\t\t\t\t\t\t, parg, PyArray_SHAPE((PyArrayObject *)parg), (npy_double *)PyArray_DATA((PyArrayObject *)parg)\n", + "\t\t\t\t\t);\n", + "\t\t\t\t\tPy_INCREF(Py_None);\n", + "\t\t\t\t\treturn Py_None;\n", + "\t\t\t\t} catch (...) {\n", + "\t\t\t\t\treturn NULL;\n", + "\t\t\t\t}\n", + "\t\t\t} else\n", + "\t\t\t\tPyErr_Clear();\n", + "\t\t}\n", + "\t\tPyObject * signatures = Py_BuildValue(\"(sO)\", \"(lp0\\n(lp1\\nccopy_reg\\n_reconstructor\\np2\\n(chope._ast\\nVariable\\np3\\nc__builtin__\\nobject\\np4\\nNtp5\\nRp6\\n(dp7\\nS'name'\\np8\\nS'res'\\np9\\nsS'dtype'\\np10\\nS'float64'\\np11\\nsS'dims'\\np12\\nI1\\nsbag2\\n(g3\\ng4\\nNtp13\\nRp14\\n(dp15\\ng8\\nS'arg'\\np16\\nsg10\\ng11\\nsg12\\nI1\\nsbaa.\", args);\n", + "\t\tif (!signatures) {\n", + "\t\t\tPyErr_SetString(PyExc_ValueError, \"Error building signature string for poly\");\n", + "\t\t\treturn NULL;\n", + "\t\t}\n", + "\t\treturn PyObject_Call(create_signature, signatures, NULL);\n", + "\t}\n", + "\tPyMethodDef polyMethods[] = {\n", + "\t\t{ \"set_create_signature\", (PyCFunction)set_create_signature, METH_VARARGS },\n", + "\t\t{ \"run\", (PyCFunction)run, METH_VARARGS },\n", + "\t\t{ NULL, NULL }\n", + "\t};\n", + "\tPyMODINIT_FUNC initpoly(void) {\n", + "\t\timport_array();\n", + "\t\tPyImport_ImportModule(\"numpy\");\n", + "\t\t(void)Py_InitModule(\"poly\", polyMethods);\n", + "\t}\n", + "}\n", + "#include \n", + "#include \n", + "#include \n", + "#include \n", + "#include \n", + "void sighandler(int sig) {\n", + "\tstd::ostringstream buffer;\n", + "\tbuffer << \"Abort by \" << (sig == SIGSEGV ? \"segfault\" : \"bus error\") << std::endl;\n", + "\tvoid * stack[64];\n", + "\tstd::size_t depth = backtrace(stack, 64);\n", + "\tif (!depth)\n", + "\t\tbuffer << \" \" << std::endl;\n", + "\telse {\n", + "\t\tchar ** symbols = backtrace_symbols(stack, depth);\n", + "\t\tfor (std::size_t i = 1; i < depth; ++i) {\n", + "\t\t\tstd::string symbol = symbols[i];\n", + "\t\t\t\tif (symbol.find_first_of(' ', 59) != std::string::npos) {\n", + "\t\t\t\t\tstd::string name = symbol.substr(59, symbol.find_first_of(' ', 59) - 59);\n", + "\t\t\t\t\tint status;\n", + "\t\t\t\t\tchar * demangled = abi::__cxa_demangle(name.c_str(), NULL, NULL, &status);\n", + "\t\t\t\t\tif (!status) {\n", + "\t\t\t\t\t\tbuffer << \" \" \n", + "\t\t\t\t\t\t\t<< symbol.substr(0, 59) \n", + "\t\t\t\t\t\t\t<< demangled\n", + "\t\t\t\t\t\t\t<< symbol.substr(59 + name.size())\n", + "\t\t\t\t\t\t\t<< std::endl;\n", + "\t\t\t\t\t\tfree(demangled);\n", + "\t\t\t\t\t} else\n", + "\t\t\t\t\t\tbuffer << \" \" << symbol << std::endl;\n", + "\t\t\t\t} else\n", + "\t\t\t\t\tbuffer << \" \" << symbol << std::endl;\n", + "\t\t\t}\n", + "\t\t\tfree(symbols);\n", + "\t\t}\n", + "\t\tstd::cerr << buffer.str();\n", + "\t\tstd::exit(EXIT_FAILURE);\n", + "\t}\n" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Pairwise distance" + ] + }, + { + "cell_type": "code", + "execution_count": 12, + "metadata": {}, + "outputs": [ { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Star point spread function CPP" + "name": "stdout", + "output_type": "stream", + "text": [ + "Overwriting src/pairwise.cpp\n" ] - }, - { - "cell_type": "code", - "collapsed": false, - "input": [ - "%%file src/pdf.cpp\n", - "#define PY_ARRAY_UNIQUE_SYMBOL pdf_ARRAY_API\n", - "#define NPY_NO_DEPRECATED_API NPY_1_7_API_VERSION\n", - "#include \n", - "#include \n", - "#include \n", - "#include \n", - "#include \n", - "#include \n", - "#include \n", - "#include \n", - "#include \n", - "#include \n", - "#include \n", - "struct PyObj {\n", - "\ttypedef PyObject * ptr_t;\n", - "\ttypedef PyArrayObject * arrptr_t;\n", - "\tPyObj(): dec(false), ptr(NULL) {}\n", - "\tPyObj(ptr_t p): dec(false), ptr(p) {}\n", - "\t~PyObj() { if(dec) Py_DECREF(ptr); }\n", - "\tPyObj & operator=(ptr_t p) { if(dec) Py_DECREF(ptr); ptr = p; dec = false; return *this; }\n", - "\tPyObj & incref(ptr_t p) { if(dec) Py_DECREF(ptr); ptr = p; dec = (p != NULL); return *this; }\n", - "\toperator bool() const { return ptr; }\n", - "\toperator ptr_t() const { return ptr; }\n", - "\toperator arrptr_t() const { return (arrptr_t)ptr; }\n", - "\tbool dec;\n", - "\tptr_t ptr;\n", - "};\n", - "\n", - "inline std::tuple pdf_f2l1d1f2JDd(\n", - " \tPyObject * pdensity\n", - " , npy_intp const * __restrict__ sdensity\n", - " , npy_float * __restrict__ cdensity\n", - "\t, PyObject * pdims\n", - "\t, npy_intp const * __restrict__ sdims\n", - "\t, npy_int64 * __restrict__ cdims\n", - "\t, PyObject * pcenter\n", - "\t, npy_intp const * __restrict__ scenter\n", - "\t, npy_double * __restrict__ ccenter\n", - "\t, PyObject * pw2D\n", - "\t, npy_intp const * __restrict__ sw2D\n", - "\t, npy_float * __restrict__ cw2D\n", - "\t, npy_int64 cr50\n", - "\t, npy_double cb\n", - "\t, npy_double ca);\n", - "\n", - "inline std::tuple pdf_f2l1d1f2JDd(\n", - " \tPyObject * pdensity\n", - " , npy_intp const * __restrict__ sdensity\n", - " , npy_float * __restrict__ cdensity\n", - "\t, PyObject * pdims\n", - "\t, npy_intp const * __restrict__ sdims\n", - "\t, npy_int64 * __restrict__ cdims\n", - "\t, PyObject * pcenter\n", - "\t, npy_intp const * __restrict__ scenter\n", - "\t, npy_double * __restrict__ ccenter\n", - "\t, PyObject * pw2D\n", - "\t, npy_intp const * __restrict__ sw2D\n", - "\t, npy_float * __restrict__ cw2D\n", - "\t, npy_int64 const cr50\n", - "\t, npy_double const cb\n", - "\t, npy_double const ca) {\n", - "\t\n", - "\tnpy_double cdr;\n", - "\tnpy_double c__sum0;\n", - "\tconst npy_double x_center = ccenter[0];\n", - "\tconst npy_double y_center = ccenter[1];\n", - "\tconst npy_intp len_0_sw2D = sw2D[0];\n", - "\tconst npy_intp len_1_sw2D = sw2D[1];\n", - "\tnpy_intp dc[] = {len_0_sw2D, len_1_sw2D};\n", - "\tPyObject * pc = PyArray_EMPTY(2, dc, NPY_FLOAT64, 0);\n", - "\tnpy_intp * sc = PyArray_SHAPE((PyArrayObject *)pc);\n", - "\tnpy_double * cc = (npy_double *)PyArray_DATA((PyArrayObject *)pc);\n", - "\t\n", - "\tnpy_intp cy = 0;\n", - "\tnpy_intp i0 = 0;\n", - "\tnpy_intp i1 = 0;\n", - "\tnpy_intp i2 = 0;\n", - "\tnpy_intp i3 = 0;\n", - "\t\n", - "\tnpy_intp sw2D_i0_idx;\n", - "\tnpy_intp sw2D_i2_idx;\n", - "\tnpy_intp density_x_idx;\n", - "\t\n", - "\tauto c__sp0 = ca * ca;\n", - "\tauto c__sp1 = cr50 * cr50;\n", - "\tauto c__sp2 = 1.0 / (c__sp0 * c__sp1);\n", - "\tauto c__sp4 = 0.3183098861846737 * c__sp2 * (-1 + cb);\n", - "\n", - "\tfor (npy_intp cx = 0; cx < cdims[(int)(0)]; ++cx) {\n", - "\t\t\n", - "\t\tdensity_x_idx = cx*sdensity[1];\n", - "\t\tfor (cy = 0; cy < cdims[(int)(1)]; ++cy) {\n", - "\t\t\t\n", - "\t\t\tcdr = std::sqrt(std::pow(cx - x_center, 2) + std::pow(cy - y_center, 2));\n", - "\t\t\tauto c__sp3 = cdr * cdr;\n", - "\t\t\tauto c__sp5 = c__sp4 * std::pow((1 + c__sp2 * c__sp3), -cb);\n", - "\t\t\t\n", - "\t\t\tfor (i0 = 0; i0 < len_0_sw2D - 0; ++i0) {\n", - "\t\t\t\t\n", - "\t\t\t\tsw2D_i0_idx = (i0)*len_1_sw2D;\n", - "\t\t\t\tfor (i1 = 0; i1 < len_1_sw2D - 0; ++i1) {\n", - "\t\t\t\t\tcc[sw2D_i0_idx + i1] = c__sp5 * cw2D[sw2D_i0_idx + i1];\n", - "\t\t\t\t}\n", - "\t\t\t}\n", - "\t\t\t\n", - "\t\t\tc__sum0 = 0;\n", - "\t\t\tfor (i2 = 0; i2 < len_0_sw2D - 0; ++i2) {\n", - "\t\t\t\t\n", - "\t\t\t\tsw2D_i2_idx = (i2)*len_1_sw2D;\n", - "\t\t\t\tfor (i3 = 0; i3 < len_1_sw2D - 0; ++i3) {\n", - "\t\t\t\t\tc__sum0 += cc[sw2D_i2_idx + i3];\n", - "\t\t\t\t}\n", - "\t\t\t}\n", - "\t\t\t\n", - "\t\t\tcdensity[(density_x_idx + cy)] = c__sum0;\n", - "\t\t}\n", - "\t}\n", - "\treturn std::make_tuple((PyObject *)pdensity, sdensity, cdensity);\n", - "}\n", - "\n", - "void sighandler(int sig);\n", - "#include \n", - "extern \"C\" {\n", - "\tPyObject * create_signature;\n", - "\tstruct sigaction slot;\n", - "\tPyObject * set_create_signature(PyObject * self, PyObject * args) {\n", - "\t\tif (!PyArg_ParseTuple(args, \"O\", &create_signature)) {\n", - "\t\t\tPyErr_SetString(PyExc_ValueError, \"Invalid Argument to set_create_signature!\");\n", - "\t\t\treturn NULL;\n", - "\t\t}\n", - "\t\tPy_INCREF(create_signature);\n", - "\t\tmemset(&slot, 0, sizeof(slot));\n", - "\t\tslot.sa_handler = &sighandler;\n", - "\t\tsigaction(SIGSEGV, &slot, NULL);\n", - "\t\tsigaction(SIGBUS, &slot, NULL);\n", - "\t\tPy_INCREF(Py_None);\n", - "\t\treturn Py_None;\n", - "\t}\n", - "\tPyObject * run(PyObject * self, PyObject * args) {\n", - "\t\t{\n", - "\t\t\tPyObj pdensity;\n", - "\t\t\tPyObj pdims;\n", - "\t\t\tPyObj pcenter;\n", - "\t\t\tPyObj pw2D;\n", - "\t\t\tPyObject * pr50; npy_int64 cr50;\n", - "\t\t\tPyObject * pb; npy_double cb;\n", - "\t\t\tPyObject * pa; npy_double ca;\n", - "\t\t\tif (\n", - "\t\t\t\tPyTuple_CheckExact(args) and PyTuple_GET_SIZE(args) == 7\n", - "\t\t\t\tand (pdensity = PyTuple_GET_ITEM(args, 0)) and PyArray_CheckExact(pdensity)\n", - "\t\t\t\tand PyArray_TYPE((PyArrayObject *)pdensity) == NPY_FLOAT32 and PyArray_NDIM((PyArrayObject *)pdensity) == 2\n", - "\t\t\t\tand (pdims = PyTuple_GET_ITEM(args, 1)) and PyArray_CheckExact(pdims)\n", - "\t\t\t\tand PyArray_TYPE((PyArrayObject *)pdims) == NPY_INT64 and PyArray_NDIM((PyArrayObject *)pdims) == 1\n", - "\t\t\t\tand (pcenter = PyTuple_GET_ITEM(args, 2)) and PyArray_CheckExact(pcenter)\n", - "\t\t\t\tand PyArray_TYPE((PyArrayObject *)pcenter) == NPY_FLOAT64 and PyArray_NDIM((PyArrayObject *)pcenter) == 1\n", - "\t\t\t\tand (pw2D = PyTuple_GET_ITEM(args, 3)) and PyArray_CheckExact(pw2D)\n", - "\t\t\t\tand PyArray_TYPE((PyArrayObject *)pw2D) == NPY_FLOAT32 and PyArray_NDIM((PyArrayObject *)pw2D) == 2\n", - "\t\t\t\tand (pr50 = PyTuple_GET_ITEM(args, 4)) and PyInt_CheckExact(pr50)\n", - "\t\t\t\tand (pb = PyTuple_GET_ITEM(args, 5)) and PyFloat_CheckExact(pb)\n", - "\t\t\t\tand (pa = PyTuple_GET_ITEM(args, 6)) and PyArray_IsScalar(pa, Double)\n", - "\t\t\t) {\n", - "\t\t\t\tif (!(pdensity.incref((PyObject *)PyArray_GETCONTIGUOUS((PyArrayObject *)pdensity)))) {\n", - "\t\t\t\t\tPyErr_SetString(PyExc_ValueError, \"Invalid Argument type on density!\");\n", - "\t\t\t\t\treturn NULL;\n", - "\t\t\t\t}\n", - "\t\t\t\tif (!(pdims.incref((PyObject *)PyArray_GETCONTIGUOUS((PyArrayObject *)pdims)))) {\n", - "\t\t\t\t\tPyErr_SetString(PyExc_ValueError, \"Invalid Argument type on dims!\");\n", - "\t\t\t\t\treturn NULL;\n", - "\t\t\t\t}\n", - "\t\t\t\tif (!(pcenter.incref((PyObject *)PyArray_GETCONTIGUOUS((PyArrayObject *)pcenter)))) {\n", - "\t\t\t\t\tPyErr_SetString(PyExc_ValueError, \"Invalid Argument type on center!\");\n", - "\t\t\t\t\treturn NULL;\n", - "\t\t\t\t}\n", - "\t\t\t\tif (!(pw2D.incref((PyObject *)PyArray_GETCONTIGUOUS((PyArrayObject *)pw2D)))) {\n", - "\t\t\t\t\tPyErr_SetString(PyExc_ValueError, \"Invalid Argument type on w2D!\");\n", - "\t\t\t\t\treturn NULL;\n", - "\t\t\t\t}\n", - "\t\t\t\tcr50 = PyInt_AS_LONG(pr50);\n", - "\t\t\t\tcb = PyFloat_AS_DOUBLE(pb);\n", - "\t\t\t\tca = PyArrayScalar_VAL(pa, Double);\n", - "\t\t\t\ttry {\n", - "\t\t\t\t\tPyObject * res = std::get<0>(pdf_f2l1d1f2JDd(\n", - "\t\t\t\t\t\t pdensity, PyArray_SHAPE((PyArrayObject *)pdensity), (npy_float *)PyArray_DATA((PyArrayObject *)pdensity)\n", - "\t\t\t\t\t\t, pdims, PyArray_SHAPE((PyArrayObject *)pdims), (npy_int64 *)PyArray_DATA((PyArrayObject *)pdims)\n", - "\t\t\t\t\t\t, pcenter, PyArray_SHAPE((PyArrayObject *)pcenter), (npy_double *)PyArray_DATA((PyArrayObject *)pcenter)\n", - "\t\t\t\t\t\t, pw2D, PyArray_SHAPE((PyArrayObject *)pw2D), (npy_float *)PyArray_DATA((PyArrayObject *)pw2D)\n", - "\t\t\t\t\t\t, cr50\n", - "\t\t\t\t\t\t, cb\n", - "\t\t\t\t\t\t, ca\n", - "\t\t\t\t\t));\n", - "\n", - "\t\t\t\t\tPy_INCREF(res);\n", - "\t\t\t\t\treturn res;\n", - "\t\t\t\t} catch (...) {\n", - "\t\t\t\t\treturn NULL;\n", - "\t\t\t\t}\n", - "\t\t\t} else\n", - "\t\t\t\tPyErr_Clear();\n", - "\t\t}\n", - "\t\tPyObject * signatures = Py_BuildValue(\"(sO)\", \"(lp0\\n(lp1\\nccopy_reg\\n_reconstructor\\np2\\n(chope._ast\\nVariable\\np3\\nc__builtin__\\nobject\\np4\\nNtp5\\nRp6\\n(dp7\\nS'name'\\np8\\nS'density'\\np9\\nsS'dtype'\\np10\\nS'float32'\\np11\\nsS'dims'\\np12\\nI2\\nsbag2\\n(g3\\ng4\\nNtp13\\nRp14\\n(dp15\\ng8\\ng12\\nsg10\\nS'int64'\\np16\\nsg12\\nI1\\nsbag2\\n(g3\\ng4\\nNtp17\\nRp18\\n(dp19\\ng8\\nS'center'\\np20\\nsg10\\nS'float64'\\np21\\nsg12\\nI1\\nsbag2\\n(g3\\ng4\\nNtp22\\nRp23\\n(dp24\\ng8\\nS'w2D'\\np25\\nsg10\\ng11\\nsg12\\nI2\\nsbag2\\n(g3\\ng4\\nNtp26\\nRp27\\n(dp28\\ng8\\nS'r50'\\np29\\nsg10\\nc__builtin__\\nint\\np30\\nsg12\\nI0\\nsbag2\\n(g3\\ng4\\nNtp31\\nRp32\\n(dp33\\ng8\\nS'b'\\np34\\nsg10\\nc__builtin__\\nfloat\\np35\\nsg12\\nI0\\nsbag2\\n(g3\\ng4\\nNtp36\\nRp37\\n(dp38\\ng8\\nS'a'\\np39\\nsg10\\ng21\\nsg12\\nI0\\nsbaa.\", args);\n", - "\t\tif (!signatures) {\n", - "\t\t\tPyErr_SetString(PyExc_ValueError, \"Error building signature string for pdf\");\n", - "\t\t\treturn NULL;\n", - "\t\t}\n", - "\t\treturn PyObject_Call(create_signature, signatures, NULL);\n", - "\t}\n", - "\tPyMethodDef pdfMethods[] = {\n", - "\t\t{ \"set_create_signature\", (PyCFunction)set_create_signature, METH_VARARGS },\n", - "\t\t{ \"run\", (PyCFunction)run, METH_VARARGS },\n", - "\t\t{ NULL, NULL }\n", - "\t};\n", - "\tPyMODINIT_FUNC initpdf(void) {\n", - "\t\timport_array();\n", - "\t\tPyImport_ImportModule(\"numpy\");\n", - "\t\t(void)Py_InitModule(\"pdf\", pdfMethods);\n", - "\t}\n", - "}\n", - "#include \n", - "#include \n", - "#include \n", - "#include \n", - "#include \n", - "void sighandler(int sig) {\n", - "\tstd::ostringstream buffer;\n", - "\tbuffer << \"Abort by \" << (sig == SIGSEGV ? \"segfault\" : \"bus error\") << std::endl;\n", - "\tvoid * stack[64];\n", - "\tstd::size_t depth = backtrace(stack, 64);\n", - "\tif (!depth)\n", - "\t\tbuffer << \" \" << std::endl;\n", - "\telse {\n", - "\t\tchar ** symbols = backtrace_symbols(stack, depth);\n", - "\t\tfor (std::size_t i = 1; i < depth; ++i) {\n", - "\t\t\tstd::string symbol = symbols[i];\n", - "\t\t\t\tif (symbol.find_first_of(' ', 59) != std::string::npos) {\n", - "\t\t\t\t\tstd::string name = symbol.substr(59, symbol.find_first_of(' ', 59) - 59);\n", - "\t\t\t\t\tint status;\n", - "\t\t\t\t\tchar * demangled = abi::__cxa_demangle(name.c_str(), NULL, NULL, &status);\n", - "\t\t\t\t\tif (!status) {\n", - "\t\t\t\t\t\tbuffer << \" \" \n", - "\t\t\t\t\t\t\t<< symbol.substr(0, 59) \n", - "\t\t\t\t\t\t\t<< demangled\n", - "\t\t\t\t\t\t\t<< symbol.substr(59 + name.size())\n", - "\t\t\t\t\t\t\t<< std::endl;\n", - "\t\t\t\t\t\tfree(demangled);\n", - "\t\t\t\t\t} else\n", - "\t\t\t\t\t\tbuffer << \" \" << symbol << std::endl;\n", - "\t\t\t\t} else\n", - "\t\t\t\t\tbuffer << \" \" << symbol << std::endl;\n", - "\t\t\t}\n", - "\t\t\tfree(symbols);\n", - "\t\t}\n", - "\t\tstd::cerr << buffer.str();\n", - "\t\tstd::exit(EXIT_FAILURE);\n", - "\t}\n" - ], - "language": "python", - "metadata": {}, - "outputs": [ - { - "output_type": "stream", - "stream": "stdout", - "text": [ - "Overwriting src/pdf.cpp\n" - ] - } - ], - "prompt_number": 14 - }, + } + ], + "source": [ + "%%file src/pairwise.cpp\n", + "#define PY_ARRAY_UNIQUE_SYMBOL pairwise_ARRAY_API\n", + "#define NPY_NO_DEPRECATED_API NPY_1_7_API_VERSION\n", + "#include \n", + "#include \n", + "#include \n", + "#include \n", + "#include \n", + "#include \n", + "#include \n", + "#include \n", + "#include \n", + "#include \n", + "#include \n", + "struct PyObj {\n", + "\ttypedef PyObject * ptr_t;\n", + "\ttypedef PyArrayObject * arrptr_t;\n", + "\tPyObj(): dec(false), ptr(NULL) {}\n", + "\tPyObj(ptr_t p): dec(false), ptr(p) {}\n", + "\t~PyObj() { if(dec) Py_DECREF(ptr); }\n", + "\tPyObj & operator=(ptr_t p) { if(dec) Py_DECREF(ptr); ptr = p; dec = false; return *this; }\n", + "\tPyObj & incref(ptr_t p) { if(dec) Py_DECREF(ptr); ptr = p; dec = (p != NULL); return *this; }\n", + "\toperator bool() const { return ptr; }\n", + "\toperator ptr_t() const { return ptr; }\n", + "\toperator arrptr_t() const { return (arrptr_t)ptr; }\n", + "\tbool dec;\n", + "\tptr_t ptr;\n", + "};\n", + "\n", + "inline void pairwise_d2d2JJ(PyObject * pX, \n", + "\t\t\t\t\t\t\t\t\t\t\t\t\t\tnpy_intp const * __restrict__ sX, \n", + "\t\t\t\t\t\t\t\t\t\t\t\t\t\tnpy_double * __restrict__ cX, \n", + "\t\t\t\t\t\t\t\t\t\t\t\t\t\tPyObject * pD, \n", + "\t\t\t\t\t\t\t\t\t\t\t\t\t\tnpy_intp const * __restrict__ sD, \n", + "\t\t\t\t\t\t\t\t\t\t\t\t\t\tnpy_double * __restrict__ cD, \n", + "\t\t\t\t\t\t\t\t\t\t\t\t\t\tnpy_int64 const cM, \n", + "\t\t\t\t\t\t\t\t\t\t\t\t\t\tnpy_int64 const cN);\n", + "\t\t\t\t\t\t\t\t\t\t\t\t\t\t\n", + "inline void pairwise_d2d2JJ(PyObject * pX, \n", + "\t\t\t\t\t\t\t\t\t\t\t\t\t\tnpy_intp const * __restrict__ sX, \n", + "\t\t\t\t\t\t\t\t\t\t\t\t\t\tnpy_double * __restrict__ cX, \n", + "\t\t\t\t\t\t\t\t\t\t\t\t\t\tPyObject * pD, \n", + "\t\t\t\t\t\t\t\t\t\t\t\t\t\tnpy_intp const * __restrict__ sD, \n", + "\t\t\t\t\t\t\t\t\t\t\t\t\t\tnpy_double * __restrict__ cD, \n", + "\t\t\t\t\t\t\t\t\t\t\t\t\t\tnpy_int64 const cM, \n", + "\t\t\t\t\t\t\t\t\t\t\t\t\t\tnpy_int64 const cN){\n", + "\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\n", + "\tnpy_double cd = 0.0;\n", + "\tnpy_double ctmp = 0.0;\n", + "\t\n", + "\tnpy_intp cj;\n", + "\tnpy_intp ck;\n", + "\tnpy_intp x_i_idx;\n", + "\tnpy_intp x_j_idx;\n", + "\tnpy_intp d_i_idx;\n", + "\tnpy_intp const xp = sX[1];\n", + "\tnpy_intp const dp = sD[1];\n", + "\t\n", + "\tfor (npy_intp ci = 0; ci < cM; ++ci) {\n", + "\t\tx_i_idx = ci*xp;\n", + "\t\td_i_idx = ci*dp;\n", + "\t\tfor (cj = 0; cj < cM; ++cj) {\n", + "\t\t\tcd = 0.0;\n", + "\t\t\tx_j_idx = cj*xp;\n", + "\t\t\tfor (ck = 0; ck < cN; ++ck) {\n", + "\t\t\t\tctmp = (cX[(x_i_idx + ck)] - cX[(x_j_idx + ck)]);\n", + "\t\t\t\tcd += (ctmp * ctmp);\n", + "\t\t\t}\n", + "\t\t\tcD[(d_i_idx + cj)] = std::sqrt(cd);\n", + "\t\t}\n", + "\t}\n", + "}\n", + "\n", + "void sighandler(int sig);\n", + "#include \n", + "extern \"C\" {\n", + "\tPyObject * create_signature;\n", + "\tstruct sigaction slot;\n", + "\tPyObject * set_create_signature(PyObject * self, PyObject * args) {\n", + "\t\tif (!PyArg_ParseTuple(args, \"O\", &create_signature)) {\n", + "\t\t\tPyErr_SetString(PyExc_ValueError, \"Invalid Argument to set_create_signature!\");\n", + "\t\t\treturn NULL;\n", + "\t\t}\n", + "\t\tPy_INCREF(create_signature);\n", + "\t\tmemset(&slot, 0, sizeof(slot));\n", + "\t\tslot.sa_handler = &sighandler;\n", + "\t\tsigaction(SIGSEGV, &slot, NULL);\n", + "\t\tsigaction(SIGBUS, &slot, NULL);\n", + "\t\tPy_INCREF(Py_None);\n", + "\t\treturn Py_None;\n", + "\t}\n", + "\tPyObject * run(PyObject * self, PyObject * args) {\n", + "\t\t{\n", + "\t\t\tPyObj pX;\n", + "\t\t\tPyObj pD;\n", + "\t\t\tPyObject * pM; npy_int64 cM;\n", + "\t\t\tPyObject * pN; npy_int64 cN;\n", + "\t\t\tif (\n", + "\t\t\t\tPyTuple_CheckExact(args) and PyTuple_GET_SIZE(args) == 4\n", + "\t\t\t\tand (pX = PyTuple_GET_ITEM(args, 0)) and PyArray_CheckExact(pX)\n", + "\t\t\t\tand PyArray_TYPE((PyArrayObject *)pX) == NPY_FLOAT64 and PyArray_NDIM((PyArrayObject *)pX) == 2\n", + "\t\t\t\tand (pD = PyTuple_GET_ITEM(args, 1)) and PyArray_CheckExact(pD)\n", + "\t\t\t\tand PyArray_TYPE((PyArrayObject *)pD) == NPY_FLOAT64 and PyArray_NDIM((PyArrayObject *)pD) == 2\n", + "\t\t\t\tand (pM = PyTuple_GET_ITEM(args, 2)) and PyInt_CheckExact(pM)\n", + "\t\t\t\tand (pN = PyTuple_GET_ITEM(args, 3)) and PyInt_CheckExact(pN)\n", + "\t\t\t) {\n", + "\t\t\t\tif (!(pX.incref((PyObject *)PyArray_GETCONTIGUOUS((PyArrayObject *)pX)))) {\n", + "\t\t\t\t\tPyErr_SetString(PyExc_ValueError, \"Invalid Argument type on X!\");\n", + "\t\t\t\t\treturn NULL;\n", + "\t\t\t\t}\n", + "\t\t\t\tif (!(pD.incref((PyObject *)PyArray_GETCONTIGUOUS((PyArrayObject *)pD)))) {\n", + "\t\t\t\t\tPyErr_SetString(PyExc_ValueError, \"Invalid Argument type on D!\");\n", + "\t\t\t\t\treturn NULL;\n", + "\t\t\t\t}\n", + "\t\t\t\tcM = PyInt_AS_LONG(pM);\n", + "\t\t\t\tcN = PyInt_AS_LONG(pN);\n", + "\t\t\t\ttry {\n", + "\t\t\t\t\tpairwise_d2d2JJ(\n", + "\t\t\t\t\t\t pX, PyArray_SHAPE((PyArrayObject *)pX), (npy_double *)PyArray_DATA((PyArrayObject *)pX)\n", + "\t\t\t\t\t\t, pD, PyArray_SHAPE((PyArrayObject *)pD), (npy_double *)PyArray_DATA((PyArrayObject *)pD)\n", + "\t\t\t\t\t\t, cM\n", + "\t\t\t\t\t\t, cN\n", + "\t\t\t\t\t);\n", + "\t\t\t\t\tPy_INCREF(Py_None);\n", + "\t\t\t\t\treturn Py_None;\n", + "\t\t\t\t} catch (...) {\n", + "\t\t\t\t\treturn NULL;\n", + "\t\t\t\t}\n", + "\t\t\t} else\n", + "\t\t\t\tPyErr_Clear();\n", + "\t\t}\n", + "\t\tPyObject * signatures = Py_BuildValue(\"(sO)\", \"(lp0\\n(lp1\\nccopy_reg\\n_reconstructor\\np2\\n(chope._ast\\nVariable\\np3\\nc__builtin__\\nobject\\np4\\nNtp5\\nRp6\\n(dp7\\nS'name'\\np8\\nS'X'\\np9\\nsS'dtype'\\np10\\nS'float64'\\np11\\nsS'dims'\\np12\\nI2\\nsbag2\\n(g3\\ng4\\nNtp13\\nRp14\\n(dp15\\ng8\\nS'D'\\np16\\nsg10\\ng11\\nsg12\\nI2\\nsbag2\\n(g3\\ng4\\nNtp17\\nRp18\\n(dp19\\ng8\\nS'M'\\np20\\nsg10\\nc__builtin__\\nint\\np21\\nsg12\\nI0\\nsbag2\\n(g3\\ng4\\nNtp22\\nRp23\\n(dp24\\ng8\\nS'N'\\np25\\nsg10\\ng21\\nsg12\\nI0\\nsbaa.\", args);\n", + "\t\tif (!signatures) {\n", + "\t\t\tPyErr_SetString(PyExc_ValueError, \"Error building signature string for pairwise\");\n", + "\t\t\treturn NULL;\n", + "\t\t}\n", + "\t\treturn PyObject_Call(create_signature, signatures, NULL);\n", + "\t}\n", + "\tPyMethodDef pairwiseMethods[] = {\n", + "\t\t{ \"set_create_signature\", (PyCFunction)set_create_signature, METH_VARARGS },\n", + "\t\t{ \"run\", (PyCFunction)run, METH_VARARGS },\n", + "\t\t{ NULL, NULL }\n", + "\t};\n", + "\tPyMODINIT_FUNC initpairwise(void) {\n", + "\t\timport_array();\n", + "\t\tPyImport_ImportModule(\"numpy\");\n", + "\t\t(void)Py_InitModule(\"pairwise\", pairwiseMethods);\n", + "\t}\n", + "}\n", + "#include \n", + "#include \n", + "#include \n", + "#include \n", + "#include \n", + "void sighandler(int sig) {\n", + "\tstd::ostringstream buffer;\n", + "\tbuffer << \"Abort by \" << (sig == SIGSEGV ? \"segfault\" : \"bus error\") << std::endl;\n", + "\tvoid * stack[64];\n", + "\tstd::size_t depth = backtrace(stack, 64);\n", + "\tif (!depth)\n", + "\t\tbuffer << \" \" << std::endl;\n", + "\telse {\n", + "\t\tchar ** symbols = backtrace_symbols(stack, depth);\n", + "\t\tfor (std::size_t i = 1; i < depth; ++i) {\n", + "\t\t\tstd::string symbol = symbols[i];\n", + "\t\t\t\tif (symbol.find_first_of(' ', 59) != std::string::npos) {\n", + "\t\t\t\t\tstd::string name = symbol.substr(59, symbol.find_first_of(' ', 59) - 59);\n", + "\t\t\t\t\tint status;\n", + "\t\t\t\t\tchar * demangled = abi::__cxa_demangle(name.c_str(), NULL, NULL, &status);\n", + "\t\t\t\t\tif (!status) {\n", + "\t\t\t\t\t\tbuffer << \" \" \n", + "\t\t\t\t\t\t\t<< symbol.substr(0, 59) \n", + "\t\t\t\t\t\t\t<< demangled\n", + "\t\t\t\t\t\t\t<< symbol.substr(59 + name.size())\n", + "\t\t\t\t\t\t\t<< std::endl;\n", + "\t\t\t\t\t\tfree(demangled);\n", + "\t\t\t\t\t} else\n", + "\t\t\t\t\t\tbuffer << \" \" << symbol << std::endl;\n", + "\t\t\t\t} else\n", + "\t\t\t\t\tbuffer << \" \" << symbol << std::endl;\n", + "\t\t\t}\n", + "\t\t\tfree(symbols);\n", + "\t\t}\n", + "\t\tstd::cerr << buffer.str();\n", + "\t\tstd::exit(EXIT_FAILURE);\n", + "\t}\n" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Star point spread function CPP" + ] + }, + { + "cell_type": "code", + "execution_count": 14, + "metadata": {}, + "outputs": [ { - "cell_type": "heading", - "level": 1, - "metadata": {}, - "source": [ - "Python helper module" + "name": "stdout", + "output_type": "stream", + "text": [ + "Overwriting src/pdf.cpp\n" ] - }, - { - "cell_type": "code", - "collapsed": false, - "input": [ - "%%file native_util.py\n", - "import os\n", - "import sys\n", - "\n", - "from numpy.distutils.misc_util import get_numpy_include_dirs\n", - "import setuptools\n", - "from os import listdir\n", - "# import tempfile\n", - "from hope import config\n", - "\n", - "def load(name):\n", - " compile(name, \"./src\")\n", - " module = __import__(name, globals(), locals(), [], -1)\n", - " return module\n", - "\n", - "\n", - "\n", - "def compile(name, src_folder, target_folder = \"./\"):\n", - " localfilename =os.path.join(src_folder, name)\n", - " \n", - " outfile, stdout, stderr, argv = None, None, None, sys.argv\n", - " try:\n", - " sys.stdout.flush(), sys.stderr.flush()\n", - " outpath = os.path.join(target_folder, \"{0}.out\".format(localfilename))\n", - " if os.path.exists(outpath):\n", - " os.remove(outpath)\n", - " \n", - " so_path = os.path.join(target_folder, \"{0}.so\".format(name))\n", - " if os.path.exists(so_path):\n", - " os.remove(so_path)\n", - " \n", - " outfile = open(outpath, 'w')\n", - " \n", - " if isinstance(sys.stdout, file) and isinstance(sys.stdout, file):\n", - " stdout, stderr = os.dup(sys.stdout.fileno()), os.dup(sys.stderr.fileno())\n", - " os.dup2(outfile.fileno(), sys.stdout.fileno())\n", - " os.dup2(outfile.fileno(), sys.stderr.fileno())\n", - " else:\n", - " stdout, stderr = sys.stdout, sys.stderr\n", - " sys.stdout, sys.stderr = outfile, outfile\n", - " try:\n", - " sources = \"./{0}.cpp\".format(localfilename)\n", - " sys.argv = [\"\", \"build_ext\",\n", - " \"-b\", target_folder, #--build-lib (-b) directory for compiled extension modules\n", - " \"-t\", \".\" #--build-temp - a rel path will result in a dir structure of -b at the cur position \n", - " ]\n", - " \n", - " from types import StringType\n", - " localfilename = StringType(localfilename)\n", - " sources = StringType(sources)\n", - " \n", - " setuptools.setup( \\\n", - " name = name\\\n", - " , ext_modules = [setuptools.Extension( \\\n", - " StringType(name) \\\n", - " , sources = [sources] \\\n", - " , extra_compile_args = config.cxxflags \\\n", - " )] \\\n", - " , include_dirs = get_numpy_include_dirs() \\\n", - " )\n", - " except SystemExit as e:\n", - " print(sys.stderr.write(str(e)))\n", - " sys.stdout.flush(), sys.stderr.flush()\n", - " finally:\n", - " if isinstance(stdout, int):\n", - " os.dup2(stdout, sys.stdout.fileno()), os.close(stdout)\n", - " elif not stdout is None:\n", - " sys.stdout = stdout\n", - " if isinstance(stderr, int):\n", - " os.dup2(stderr, sys.stderr.fileno()), os.close(stderr)\n", - " elif not stderr is None:\n", - " sys.stderr = stderr\n", - " if isinstance(outfile, file):\n", - " outfile.close()\n", - " sys.argv = argv\n", - " \n", - " with open(outpath) as outfile:\n", - " out = outfile.read()\n", - " \n", - " if not os.path.isfile(os.path.join(target_folder, \"{0}.so\".format(name))) or out.find(\"error:\") > -1:\n", - " print(out)\n", - " raise Exception(\"Error compiling function {0} (compiled to {1})\".format(localfilename, target_folder))\n", - " \n", - " if out.find(\"warning:\") > -1:\n", - " import warnings\n", - " warnings.warn(\"A warning has been issued during compilation:\\n%s\"%out)\n", - " \n", - " print(out)\n", - "\n", - "\n", - "def compile_all():\n", - " src_folder = \"./src\"\n", - " func_names = (src_file.split(\".cpp\")[0] for src_file in listdir(src_folder) if src_file.endswith(\".cpp\"))\n", - " for func_name in func_names:\n", - " compile(func_name, src_folder)\n", - " \n", - " \n", - "if __name__ == '__main__':\n", - " compile_all()" - ], - "language": "python", - "metadata": {}, - "outputs": [ - { - "output_type": "stream", - "stream": "stdout", - "text": [ - "Overwriting native_util.py\n" - ] - } - ], - "prompt_number": 16 - }, + } + ], + "source": [ + "%%file src/pdf.cpp\n", + "#define PY_ARRAY_UNIQUE_SYMBOL pdf_ARRAY_API\n", + "#define NPY_NO_DEPRECATED_API NPY_1_7_API_VERSION\n", + "#include \n", + "#include \n", + "#include \n", + "#include \n", + "#include \n", + "#include \n", + "#include \n", + "#include \n", + "#include \n", + "#include \n", + "#include \n", + "struct PyObj {\n", + "\ttypedef PyObject * ptr_t;\n", + "\ttypedef PyArrayObject * arrptr_t;\n", + "\tPyObj(): dec(false), ptr(NULL) {}\n", + "\tPyObj(ptr_t p): dec(false), ptr(p) {}\n", + "\t~PyObj() { if(dec) Py_DECREF(ptr); }\n", + "\tPyObj & operator=(ptr_t p) { if(dec) Py_DECREF(ptr); ptr = p; dec = false; return *this; }\n", + "\tPyObj & incref(ptr_t p) { if(dec) Py_DECREF(ptr); ptr = p; dec = (p != NULL); return *this; }\n", + "\toperator bool() const { return ptr; }\n", + "\toperator ptr_t() const { return ptr; }\n", + "\toperator arrptr_t() const { return (arrptr_t)ptr; }\n", + "\tbool dec;\n", + "\tptr_t ptr;\n", + "};\n", + "\n", + "inline std::tuple pdf_f2l1d1f2JDd(\n", + " \tPyObject * pdensity\n", + " , npy_intp const * __restrict__ sdensity\n", + " , npy_float * __restrict__ cdensity\n", + "\t, PyObject * pdims\n", + "\t, npy_intp const * __restrict__ sdims\n", + "\t, npy_int64 * __restrict__ cdims\n", + "\t, PyObject * pcenter\n", + "\t, npy_intp const * __restrict__ scenter\n", + "\t, npy_double * __restrict__ ccenter\n", + "\t, PyObject * pw2D\n", + "\t, npy_intp const * __restrict__ sw2D\n", + "\t, npy_float * __restrict__ cw2D\n", + "\t, npy_int64 cr50\n", + "\t, npy_double cb\n", + "\t, npy_double ca);\n", + "\n", + "inline std::tuple pdf_f2l1d1f2JDd(\n", + " \tPyObject * pdensity\n", + " , npy_intp const * __restrict__ sdensity\n", + " , npy_float * __restrict__ cdensity\n", + "\t, PyObject * pdims\n", + "\t, npy_intp const * __restrict__ sdims\n", + "\t, npy_int64 * __restrict__ cdims\n", + "\t, PyObject * pcenter\n", + "\t, npy_intp const * __restrict__ scenter\n", + "\t, npy_double * __restrict__ ccenter\n", + "\t, PyObject * pw2D\n", + "\t, npy_intp const * __restrict__ sw2D\n", + "\t, npy_float * __restrict__ cw2D\n", + "\t, npy_int64 const cr50\n", + "\t, npy_double const cb\n", + "\t, npy_double const ca) {\n", + "\t\n", + "\tnpy_double cdr;\n", + "\tnpy_double c__sum0;\n", + "\tconst npy_double x_center = ccenter[0];\n", + "\tconst npy_double y_center = ccenter[1];\n", + "\tconst npy_intp len_0_sw2D = sw2D[0];\n", + "\tconst npy_intp len_1_sw2D = sw2D[1];\n", + "\tnpy_intp dc[] = {len_0_sw2D, len_1_sw2D};\n", + "\tPyObject * pc = PyArray_EMPTY(2, dc, NPY_FLOAT64, 0);\n", + "\tnpy_intp * sc = PyArray_SHAPE((PyArrayObject *)pc);\n", + "\tnpy_double * cc = (npy_double *)PyArray_DATA((PyArrayObject *)pc);\n", + "\t\n", + "\tnpy_intp cy = 0;\n", + "\tnpy_intp i0 = 0;\n", + "\tnpy_intp i1 = 0;\n", + "\tnpy_intp i2 = 0;\n", + "\tnpy_intp i3 = 0;\n", + "\t\n", + "\tnpy_intp sw2D_i0_idx;\n", + "\tnpy_intp sw2D_i2_idx;\n", + "\tnpy_intp density_x_idx;\n", + "\t\n", + "\tauto c__sp0 = ca * ca;\n", + "\tauto c__sp1 = cr50 * cr50;\n", + "\tauto c__sp2 = 1.0 / (c__sp0 * c__sp1);\n", + "\tauto c__sp4 = 0.3183098861846737 * c__sp2 * (-1 + cb);\n", + "\n", + "\tfor (npy_intp cx = 0; cx < cdims[(int)(0)]; ++cx) {\n", + "\t\t\n", + "\t\tdensity_x_idx = cx*sdensity[1];\n", + "\t\tfor (cy = 0; cy < cdims[(int)(1)]; ++cy) {\n", + "\t\t\t\n", + "\t\t\tcdr = std::sqrt(std::pow(cx - x_center, 2) + std::pow(cy - y_center, 2));\n", + "\t\t\tauto c__sp3 = cdr * cdr;\n", + "\t\t\tauto c__sp5 = c__sp4 * std::pow((1 + c__sp2 * c__sp3), -cb);\n", + "\t\t\t\n", + "\t\t\tfor (i0 = 0; i0 < len_0_sw2D - 0; ++i0) {\n", + "\t\t\t\t\n", + "\t\t\t\tsw2D_i0_idx = (i0)*len_1_sw2D;\n", + "\t\t\t\tfor (i1 = 0; i1 < len_1_sw2D - 0; ++i1) {\n", + "\t\t\t\t\tcc[sw2D_i0_idx + i1] = c__sp5 * cw2D[sw2D_i0_idx + i1];\n", + "\t\t\t\t}\n", + "\t\t\t}\n", + "\t\t\t\n", + "\t\t\tc__sum0 = 0;\n", + "\t\t\tfor (i2 = 0; i2 < len_0_sw2D - 0; ++i2) {\n", + "\t\t\t\t\n", + "\t\t\t\tsw2D_i2_idx = (i2)*len_1_sw2D;\n", + "\t\t\t\tfor (i3 = 0; i3 < len_1_sw2D - 0; ++i3) {\n", + "\t\t\t\t\tc__sum0 += cc[sw2D_i2_idx + i3];\n", + "\t\t\t\t}\n", + "\t\t\t}\n", + "\t\t\t\n", + "\t\t\tcdensity[(density_x_idx + cy)] = c__sum0;\n", + "\t\t}\n", + "\t}\n", + "\treturn std::make_tuple((PyObject *)pdensity, sdensity, cdensity);\n", + "}\n", + "\n", + "void sighandler(int sig);\n", + "#include \n", + "extern \"C\" {\n", + "\tPyObject * create_signature;\n", + "\tstruct sigaction slot;\n", + "\tPyObject * set_create_signature(PyObject * self, PyObject * args) {\n", + "\t\tif (!PyArg_ParseTuple(args, \"O\", &create_signature)) {\n", + "\t\t\tPyErr_SetString(PyExc_ValueError, \"Invalid Argument to set_create_signature!\");\n", + "\t\t\treturn NULL;\n", + "\t\t}\n", + "\t\tPy_INCREF(create_signature);\n", + "\t\tmemset(&slot, 0, sizeof(slot));\n", + "\t\tslot.sa_handler = &sighandler;\n", + "\t\tsigaction(SIGSEGV, &slot, NULL);\n", + "\t\tsigaction(SIGBUS, &slot, NULL);\n", + "\t\tPy_INCREF(Py_None);\n", + "\t\treturn Py_None;\n", + "\t}\n", + "\tPyObject * run(PyObject * self, PyObject * args) {\n", + "\t\t{\n", + "\t\t\tPyObj pdensity;\n", + "\t\t\tPyObj pdims;\n", + "\t\t\tPyObj pcenter;\n", + "\t\t\tPyObj pw2D;\n", + "\t\t\tPyObject * pr50; npy_int64 cr50;\n", + "\t\t\tPyObject * pb; npy_double cb;\n", + "\t\t\tPyObject * pa; npy_double ca;\n", + "\t\t\tif (\n", + "\t\t\t\tPyTuple_CheckExact(args) and PyTuple_GET_SIZE(args) == 7\n", + "\t\t\t\tand (pdensity = PyTuple_GET_ITEM(args, 0)) and PyArray_CheckExact(pdensity)\n", + "\t\t\t\tand PyArray_TYPE((PyArrayObject *)pdensity) == NPY_FLOAT32 and PyArray_NDIM((PyArrayObject *)pdensity) == 2\n", + "\t\t\t\tand (pdims = PyTuple_GET_ITEM(args, 1)) and PyArray_CheckExact(pdims)\n", + "\t\t\t\tand PyArray_TYPE((PyArrayObject *)pdims) == NPY_INT64 and PyArray_NDIM((PyArrayObject *)pdims) == 1\n", + "\t\t\t\tand (pcenter = PyTuple_GET_ITEM(args, 2)) and PyArray_CheckExact(pcenter)\n", + "\t\t\t\tand PyArray_TYPE((PyArrayObject *)pcenter) == NPY_FLOAT64 and PyArray_NDIM((PyArrayObject *)pcenter) == 1\n", + "\t\t\t\tand (pw2D = PyTuple_GET_ITEM(args, 3)) and PyArray_CheckExact(pw2D)\n", + "\t\t\t\tand PyArray_TYPE((PyArrayObject *)pw2D) == NPY_FLOAT32 and PyArray_NDIM((PyArrayObject *)pw2D) == 2\n", + "\t\t\t\tand (pr50 = PyTuple_GET_ITEM(args, 4)) and PyInt_CheckExact(pr50)\n", + "\t\t\t\tand (pb = PyTuple_GET_ITEM(args, 5)) and PyFloat_CheckExact(pb)\n", + "\t\t\t\tand (pa = PyTuple_GET_ITEM(args, 6)) and PyArray_IsScalar(pa, Double)\n", + "\t\t\t) {\n", + "\t\t\t\tif (!(pdensity.incref((PyObject *)PyArray_GETCONTIGUOUS((PyArrayObject *)pdensity)))) {\n", + "\t\t\t\t\tPyErr_SetString(PyExc_ValueError, \"Invalid Argument type on density!\");\n", + "\t\t\t\t\treturn NULL;\n", + "\t\t\t\t}\n", + "\t\t\t\tif (!(pdims.incref((PyObject *)PyArray_GETCONTIGUOUS((PyArrayObject *)pdims)))) {\n", + "\t\t\t\t\tPyErr_SetString(PyExc_ValueError, \"Invalid Argument type on dims!\");\n", + "\t\t\t\t\treturn NULL;\n", + "\t\t\t\t}\n", + "\t\t\t\tif (!(pcenter.incref((PyObject *)PyArray_GETCONTIGUOUS((PyArrayObject *)pcenter)))) {\n", + "\t\t\t\t\tPyErr_SetString(PyExc_ValueError, \"Invalid Argument type on center!\");\n", + "\t\t\t\t\treturn NULL;\n", + "\t\t\t\t}\n", + "\t\t\t\tif (!(pw2D.incref((PyObject *)PyArray_GETCONTIGUOUS((PyArrayObject *)pw2D)))) {\n", + "\t\t\t\t\tPyErr_SetString(PyExc_ValueError, \"Invalid Argument type on w2D!\");\n", + "\t\t\t\t\treturn NULL;\n", + "\t\t\t\t}\n", + "\t\t\t\tcr50 = PyInt_AS_LONG(pr50);\n", + "\t\t\t\tcb = PyFloat_AS_DOUBLE(pb);\n", + "\t\t\t\tca = PyArrayScalar_VAL(pa, Double);\n", + "\t\t\t\ttry {\n", + "\t\t\t\t\tPyObject * res = std::get<0>(pdf_f2l1d1f2JDd(\n", + "\t\t\t\t\t\t pdensity, PyArray_SHAPE((PyArrayObject *)pdensity), (npy_float *)PyArray_DATA((PyArrayObject *)pdensity)\n", + "\t\t\t\t\t\t, pdims, PyArray_SHAPE((PyArrayObject *)pdims), (npy_int64 *)PyArray_DATA((PyArrayObject *)pdims)\n", + "\t\t\t\t\t\t, pcenter, PyArray_SHAPE((PyArrayObject *)pcenter), (npy_double *)PyArray_DATA((PyArrayObject *)pcenter)\n", + "\t\t\t\t\t\t, pw2D, PyArray_SHAPE((PyArrayObject *)pw2D), (npy_float *)PyArray_DATA((PyArrayObject *)pw2D)\n", + "\t\t\t\t\t\t, cr50\n", + "\t\t\t\t\t\t, cb\n", + "\t\t\t\t\t\t, ca\n", + "\t\t\t\t\t));\n", + "\n", + "\t\t\t\t\tPy_INCREF(res);\n", + "\t\t\t\t\treturn res;\n", + "\t\t\t\t} catch (...) {\n", + "\t\t\t\t\treturn NULL;\n", + "\t\t\t\t}\n", + "\t\t\t} else\n", + "\t\t\t\tPyErr_Clear();\n", + "\t\t}\n", + "\t\tPyObject * signatures = Py_BuildValue(\"(sO)\", \"(lp0\\n(lp1\\nccopy_reg\\n_reconstructor\\np2\\n(chope._ast\\nVariable\\np3\\nc__builtin__\\nobject\\np4\\nNtp5\\nRp6\\n(dp7\\nS'name'\\np8\\nS'density'\\np9\\nsS'dtype'\\np10\\nS'float32'\\np11\\nsS'dims'\\np12\\nI2\\nsbag2\\n(g3\\ng4\\nNtp13\\nRp14\\n(dp15\\ng8\\ng12\\nsg10\\nS'int64'\\np16\\nsg12\\nI1\\nsbag2\\n(g3\\ng4\\nNtp17\\nRp18\\n(dp19\\ng8\\nS'center'\\np20\\nsg10\\nS'float64'\\np21\\nsg12\\nI1\\nsbag2\\n(g3\\ng4\\nNtp22\\nRp23\\n(dp24\\ng8\\nS'w2D'\\np25\\nsg10\\ng11\\nsg12\\nI2\\nsbag2\\n(g3\\ng4\\nNtp26\\nRp27\\n(dp28\\ng8\\nS'r50'\\np29\\nsg10\\nc__builtin__\\nint\\np30\\nsg12\\nI0\\nsbag2\\n(g3\\ng4\\nNtp31\\nRp32\\n(dp33\\ng8\\nS'b'\\np34\\nsg10\\nc__builtin__\\nfloat\\np35\\nsg12\\nI0\\nsbag2\\n(g3\\ng4\\nNtp36\\nRp37\\n(dp38\\ng8\\nS'a'\\np39\\nsg10\\ng21\\nsg12\\nI0\\nsbaa.\", args);\n", + "\t\tif (!signatures) {\n", + "\t\t\tPyErr_SetString(PyExc_ValueError, \"Error building signature string for pdf\");\n", + "\t\t\treturn NULL;\n", + "\t\t}\n", + "\t\treturn PyObject_Call(create_signature, signatures, NULL);\n", + "\t}\n", + "\tPyMethodDef pdfMethods[] = {\n", + "\t\t{ \"set_create_signature\", (PyCFunction)set_create_signature, METH_VARARGS },\n", + "\t\t{ \"run\", (PyCFunction)run, METH_VARARGS },\n", + "\t\t{ NULL, NULL }\n", + "\t};\n", + "\tPyMODINIT_FUNC initpdf(void) {\n", + "\t\timport_array();\n", + "\t\tPyImport_ImportModule(\"numpy\");\n", + "\t\t(void)Py_InitModule(\"pdf\", pdfMethods);\n", + "\t}\n", + "}\n", + "#include \n", + "#include \n", + "#include \n", + "#include \n", + "#include \n", + "void sighandler(int sig) {\n", + "\tstd::ostringstream buffer;\n", + "\tbuffer << \"Abort by \" << (sig == SIGSEGV ? \"segfault\" : \"bus error\") << std::endl;\n", + "\tvoid * stack[64];\n", + "\tstd::size_t depth = backtrace(stack, 64);\n", + "\tif (!depth)\n", + "\t\tbuffer << \" \" << std::endl;\n", + "\telse {\n", + "\t\tchar ** symbols = backtrace_symbols(stack, depth);\n", + "\t\tfor (std::size_t i = 1; i < depth; ++i) {\n", + "\t\t\tstd::string symbol = symbols[i];\n", + "\t\t\t\tif (symbol.find_first_of(' ', 59) != std::string::npos) {\n", + "\t\t\t\t\tstd::string name = symbol.substr(59, symbol.find_first_of(' ', 59) - 59);\n", + "\t\t\t\t\tint status;\n", + "\t\t\t\t\tchar * demangled = abi::__cxa_demangle(name.c_str(), NULL, NULL, &status);\n", + "\t\t\t\t\tif (!status) {\n", + "\t\t\t\t\t\tbuffer << \" \" \n", + "\t\t\t\t\t\t\t<< symbol.substr(0, 59) \n", + "\t\t\t\t\t\t\t<< demangled\n", + "\t\t\t\t\t\t\t<< symbol.substr(59 + name.size())\n", + "\t\t\t\t\t\t\t<< std::endl;\n", + "\t\t\t\t\t\tfree(demangled);\n", + "\t\t\t\t\t} else\n", + "\t\t\t\t\t\tbuffer << \" \" << symbol << std::endl;\n", + "\t\t\t\t} else\n", + "\t\t\t\t\tbuffer << \" \" << symbol << std::endl;\n", + "\t\t\t}\n", + "\t\t\tfree(symbols);\n", + "\t\t}\n", + "\t\tstd::cerr << buffer.str();\n", + "\t\tstd::exit(EXIT_FAILURE);\n", + "\t}\n" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Python helper module" + ] + }, + { + "cell_type": "code", + "execution_count": 16, + "metadata": {}, + "outputs": [ { - "cell_type": "code", - "collapsed": false, - "input": [ - "%%file util.py\n", - "# Copyright (C) 2014 ETH Zurich, Institute for Astronomy\n", - "\n", - "'''\n", - "Created on Aug 4, 2014\n", - "\n", - "author: jakeret\n", - "'''\n", - "from __future__ import print_function, division, absolute_import, unicode_literals\n", - "import math\n", - "\n", - "def perf_comp_data(func_list, data_list, rep=5, number=1, extra_setup=None):\n", - " ''' Function to compare the performance of different functions.\n", - " \n", - " Parameters\n", - " ==========\n", - " func_list : list\n", - " list with function names as strings\n", - " data_list : list\n", - " list with data set names as strings\n", - " rep : int\n", - " number of repetitions of the whole comparison\n", - " number : int\n", - " number of executions for every function\n", - " '''\n", - " from timeit import repeat\n", - " res_list = {}\n", - " for name in enumerate(func_list):\n", - " if data_list is None:\n", - " stmt = \"%s()\"%(name[1])\n", - " setup = \"from __main__ import %s\"%(name[1]) \n", - " else:\n", - " stmt = \"%s(%s)\"%(name[1], data_list[name[0]])\n", - " setup = \"from __main__ import %s, %s\"%(name[1], data_list[name[0]])\n", - " if extra_setup is not None:\n", - " stmt = extra_setup + \"; \" + stmt\n", - " \n", - " results = repeat(stmt=stmt, setup=setup, repeat=rep, number=number)\n", - " \n", - " res_list[name[1]] = (median(results), min(results))\n", - " \n", - "# res_sort = sorted(res_list.iteritems(), key=lambda (k, v): (v, k))\n", - " res_sort = sorted(iter(res_list.items()), key=lambda k_v: (k_v[1], k_v[0]))\n", - " for func, (av_time, min_time) in res_sort:\n", - " rel = av_time / res_sort[0][1][0]\n", - " print('function: {0!s:20}, av. time sec: {1:>12.8f}, min. time sec: {2:>12.8f}, relative: {3:>9.1f}'.format(func, av_time, min_time, rel))\n", - "\n", - "def median(x):\n", - " s_x = sorted(x)\n", - " return (s_x[int(math.floor((len(s_x)-1)/2))] + s_x[int(math.ceil((len(s_x)-1)/2))])/2" - ], - "language": "python", - "metadata": {}, - "outputs": [ - { - "output_type": "stream", - "stream": "stdout", - "text": [ - "Overwriting util.py\n" - ] - } - ], - "prompt_number": 1 - }, + "name": "stdout", + "output_type": "stream", + "text": [ + "Overwriting native_util.py\n" + ] + } + ], + "source": [ + "%%file native_util.py\n", + "import os\n", + "import sys\n", + "\n", + "from numpy.distutils.misc_util import get_numpy_include_dirs\n", + "import setuptools\n", + "from os import listdir\n", + "# import tempfile\n", + "from hope import config\n", + "\n", + "def load(name):\n", + " compile(name, \"./src\")\n", + " module = __import__(name, globals(), locals(), [], -1)\n", + " return module\n", + "\n", + "\n", + "\n", + "def compile(name, src_folder, target_folder = \"./\"):\n", + " localfilename =os.path.join(src_folder, name)\n", + " \n", + " outfile, stdout, stderr, argv = None, None, None, sys.argv\n", + " try:\n", + " sys.stdout.flush(), sys.stderr.flush()\n", + " outpath = os.path.join(target_folder, \"{0}.out\".format(localfilename))\n", + " if os.path.exists(outpath):\n", + " os.remove(outpath)\n", + " \n", + " so_path = os.path.join(target_folder, \"{0}.so\".format(name))\n", + " if os.path.exists(so_path):\n", + " os.remove(so_path)\n", + " \n", + " outfile = open(outpath, 'w')\n", + " \n", + " if isinstance(sys.stdout, file) and isinstance(sys.stdout, file):\n", + " stdout, stderr = os.dup(sys.stdout.fileno()), os.dup(sys.stderr.fileno())\n", + " os.dup2(outfile.fileno(), sys.stdout.fileno())\n", + " os.dup2(outfile.fileno(), sys.stderr.fileno())\n", + " else:\n", + " stdout, stderr = sys.stdout, sys.stderr\n", + " sys.stdout, sys.stderr = outfile, outfile\n", + " try:\n", + " sources = \"./{0}.cpp\".format(localfilename)\n", + " sys.argv = [\"\", \"build_ext\",\n", + " \"-b\", target_folder, #--build-lib (-b) directory for compiled extension modules\n", + " \"-t\", \".\" #--build-temp - a rel path will result in a dir structure of -b at the cur position \n", + " ]\n", + " \n", + " from types import StringType\n", + " localfilename = StringType(localfilename)\n", + " sources = StringType(sources)\n", + " \n", + " setuptools.setup( \\\n", + " name = name\\\n", + " , ext_modules = [setuptools.Extension( \\\n", + " StringType(name) \\\n", + " , sources = [sources] \\\n", + " , extra_compile_args = config.cxxflags \\\n", + " )] \\\n", + " , include_dirs = get_numpy_include_dirs() \\\n", + " )\n", + " except SystemExit as e:\n", + " print(sys.stderr.write(str(e)))\n", + " sys.stdout.flush(), sys.stderr.flush()\n", + " finally:\n", + " if isinstance(stdout, int):\n", + " os.dup2(stdout, sys.stdout.fileno()), os.close(stdout)\n", + " elif not stdout is None:\n", + " sys.stdout = stdout\n", + " if isinstance(stderr, int):\n", + " os.dup2(stderr, sys.stderr.fileno()), os.close(stderr)\n", + " elif not stderr is None:\n", + " sys.stderr = stderr\n", + " if isinstance(outfile, file):\n", + " outfile.close()\n", + " sys.argv = argv\n", + " \n", + " with open(outpath) as outfile:\n", + " out = outfile.read()\n", + " \n", + " if not os.path.isfile(os.path.join(target_folder, \"{0}.so\".format(name))) or out.find(\"error:\") > -1:\n", + " print(out)\n", + " raise Exception(\"Error compiling function {0} (compiled to {1})\".format(localfilename, target_folder))\n", + " \n", + " if out.find(\"warning:\") > -1:\n", + " import warnings\n", + " warnings.warn(\"A warning has been issued during compilation:\\n%s\"%out)\n", + " \n", + " print(out)\n", + "\n", + "\n", + "def compile_all():\n", + " src_folder = \"./src\"\n", + " func_names = (src_file.split(\".cpp\")[0] for src_file in listdir(src_folder) if src_file.endswith(\".cpp\"))\n", + " for func_name in func_names:\n", + " compile(func_name, src_folder)\n", + " \n", + " \n", + "if __name__ == '__main__':\n", + " compile_all()" + ] + }, + { + "cell_type": "code", + "execution_count": 1, + "metadata": {}, + "outputs": [ { - "cell_type": "code", - "collapsed": false, - "input": [], - "language": "python", - "metadata": {}, - "outputs": [] + "name": "stdout", + "output_type": "stream", + "text": [ + "Overwriting util.py\n" + ] } ], - "metadata": {} + "source": [ + "%%file util.py\n", + "# Copyright (C) 2014 ETH Zurich, Institute for Astronomy\n", + "\n", + "'''\n", + "Created on Aug 4, 2014\n", + "\n", + "author: jakeret\n", + "'''\n", + "from __future__ import print_function, division, absolute_import, unicode_literals\n", + "import math\n", + "\n", + "def perf_comp_data(func_list, data_list, rep=5, number=1, extra_setup=None):\n", + " ''' Function to compare the performance of different functions.\n", + " \n", + " Parameters\n", + " ==========\n", + " func_list : list\n", + " list with function names as strings\n", + " data_list : list\n", + " list with data set names as strings\n", + " rep : int\n", + " number of repetitions of the whole comparison\n", + " number : int\n", + " number of executions for every function\n", + " '''\n", + " from timeit import repeat\n", + " res_list = {}\n", + " for name in enumerate(func_list):\n", + " if data_list is None:\n", + " stmt = \"%s()\"%(name[1])\n", + " setup = \"from __main__ import %s\"%(name[1]) \n", + " else:\n", + " stmt = \"%s(%s)\"%(name[1], data_list[name[0]])\n", + " setup = \"from __main__ import %s, %s\"%(name[1], data_list[name[0]])\n", + " if extra_setup is not None:\n", + " stmt = extra_setup + \"; \" + stmt\n", + " \n", + " results = repeat(stmt=stmt, setup=setup, repeat=rep, number=number)\n", + " \n", + " res_list[name[1]] = (median(results), min(results))\n", + " \n", + "# res_sort = sorted(res_list.iteritems(), key=lambda (k, v): (v, k))\n", + " res_sort = sorted(iter(res_list.items()), key=lambda k_v: (k_v[1], k_v[0]))\n", + " for func, (av_time, min_time) in res_sort:\n", + " rel = av_time / res_sort[0][1][0]\n", + " print('function: {0!s:20}, av. time sec: {1:>12.8f}, min. time sec: {2:>12.8f}, relative: {3:>9.1f}'.format(func, av_time, min_time, rel))\n", + "\n", + "def median(x):\n", + " s_x = sorted(x)\n", + " return (s_x[int(math.floor((len(s_x)-1)/2))] + s_x[int(math.ceil((len(s_x)-1)/2))])/2" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.5.1" } - ] -} \ No newline at end of file + }, + "nbformat": 4, + "nbformat_minor": 1 +} diff --git a/benchmarks/numexpr.ipynb b/benchmarks/numexpr.ipynb index 48c37c2..8fb2504 100644 --- a/benchmarks/numexpr.ipynb +++ b/benchmarks/numexpr.ipynb @@ -1,665 +1,565 @@ { - "metadata": { - "name": "", - "signature": "sha256:f51733b3c4c4439e321dc29d5ba36f149a1a77c8e879a9549b540aed6dbc3edf" - }, - "nbformat": 3, - "nbformat_minor": 0, - "worksheets": [ + "cells": [ { - "cells": [ - { - "cell_type": "code", - "collapsed": false, - "input": [ - "import hope\n", - "hope.config.optimize = True\n", - "hope.config.verbose = True\n", - "hope.config.keeptemp = True\n", - "import numba\n", - "import numpy as np\n", - "import numexpr as ne\n", - "\n", - "from util import perf_comp_data\n", - "from native_util import load\n", - "%load_ext cythonmagic\n", - "%load_ext version_information\n", - "%version_information numpy, Cython, numba, hope, numexpr" - ], - "language": "python", - "metadata": {}, - "outputs": [ - { - "html": [ - "
SoftwareVersion
Python2.7.8 (default, Jul 13 2014, 17:11:32) [GCC 4.2.1 Compatible Apple LLVM 5.1 (clang-503.0.40)]
IPython1.1.0
OSposix [darwin]
numpy1.8.1
Cython0.20.2
numba0.13.3
hope0.3.0
numexpr2.4
Thu Sep 04 15:13:34 2014 CEST
" - ], - "json": [ - "{\"Software versions\": [{\"version\": \"2.7.8 (default, Jul 13 2014, 17:11:32) [GCC 4.2.1 Compatible Apple LLVM 5.1 (clang-503.0.40)]\", \"module\": \"Python\"}, {\"version\": \"1.1.0\", \"module\": \"IPython\"}, {\"version\": \"posix [darwin]\", \"module\": \"OS\"}, {\"version\": \"1.8.1\", \"module\": \"numpy\"}, {\"version\": \"0.20.2\", \"module\": \"Cython\"}, {\"version\": \"0.13.3\", \"module\": \"numba\"}, {\"version\": \"0.3.0\", \"module\": \"hope\"}, {\"version\": \"2.4\", \"module\": \"numexpr\"}]}" - ], - "latex": [ - "\\begin{tabular}{|l|l|}\\hline\n", - "{\\bf Software} & {\\bf Version} \\\\ \\hline\\hline\n", - "Python & 2.7.8 (default, Jul 13 2014, 17:11:32) [GCC 4.2.1 Compatible Apple LLVM 5.1 (clang-503.0.40)] \\\\ \\hline\n", - "IPython & 1.1.0 \\\\ \\hline\n", - "OS & posix [darwin] \\\\ \\hline\n", - "numpy & 1.8.1 \\\\ \\hline\n", - "Cython & 0.20.2 \\\\ \\hline\n", - "numba & 0.13.3 \\\\ \\hline\n", - "hope & 0.3.0 \\\\ \\hline\n", - "numexpr & 2.4 \\\\ \\hline\n", - "\\hline \\multicolumn{2}{|l|}{Thu Sep 04 15:13:34 2014 CEST} \\\\ \\hline\n", - "\\end{tabular}\n" - ], - "metadata": {}, - "output_type": "pyout", - "prompt_number": 1, - "text": [ - "Software versions\n", - "Python 2.7.8 (default, Jul 13 2014, 17:11:32) [GCC 4.2.1 Compatible Apple LLVM 5.1 (clang-503.0.40)]\n", - "IPython 1.1.0\n", - "OS posix [darwin]\n", - "numpy 1.8.1\n", - "Cython 0.20.2\n", - "numba 0.13.3\n", - "hope 0.3.0\n", - "numexpr 2.4\n", - "Thu Sep 04 15:13:34 2014 CEST" - ] - } - ], - "prompt_number": 1 - }, - { - "cell_type": "code", - "collapsed": false, - "input": [ - "# Python version\n", - "\n", - "def ln_python(X, Y):\n", - " Y[:] = (X-1) - (X-1)**2 / 2 + (X-1)**3 / 3 - (X-1)**4 / 4 + (X-1)**5 / 5 - (X-1)**6 / 6 + (X-1)**7 / 7 - (X-1)**8 / 8 + (X-1)**9 / 9\n" - ], - "language": "python", - "metadata": { - "slideshow": { - "slide_type": "slide" - } - }, - "outputs": [], - "prompt_number": 2 - }, - { - "cell_type": "code", - "collapsed": false, - "input": [ - "# Python version\n", - "\n", - "def ln_python_exp(X, Y):\n", - " x = (X - 1)\n", - " x2 = x*x\n", - " x4 = x2*x2\n", - " x6 = x4*x2\n", - " x8 = x4*x4\n", - " Y[:] = x - x2 / 2 + x * x2 / 3 - x4 / 4 + x * x4 / 5 - x6 / 6 + x6 * x / 7 - x8 / 8 + x8 * x / 9" - ], - "language": "python", - "metadata": { - "slideshow": { - "slide_type": "slide" - } - }, - "outputs": [], - "prompt_number": 3 - }, + "cell_type": "code", + "execution_count": 1, + "metadata": {}, + "outputs": [ { - "cell_type": "code", - "collapsed": false, - "input": [ - "# NumExpr version\n", - "def ln_numexpr(X, Y):\n", - " Y[:] = ne.evaluate(\"(X-1) - (X-1)**2 / 2 + (X-1)**3 / 3 - (X-1)**4 / 4 + (X-1)**5 / 5 - (X-1)**6 / 6 + (X-1)**7 / 7 - (X-1)**8 / 8 + (X-1)**9 / 9\")\n" - ], - "language": "python", - "metadata": { - "slideshow": { - "slide_type": "slide" - } - }, - "outputs": [], - "prompt_number": 4 - }, - { - "cell_type": "code", - "collapsed": false, - "input": [ - "# Hope version\n", - "@hope.jit\n", - "def ln_hope(X, Y):\n", - " Y[:] = (X-1) - (X-1)**2 / 2 + (X-1)**3 / 3 - (X-1)**4 / 4 + (X-1)**5 / 5 - (X-1)**6 / 6 + (X-1)**7 / 7 - (X-1)**8 / 8 + (X-1)**9 / 9\n" - ], - "language": "python", - "metadata": { - "slideshow": { - "slide_type": "slide" - } - }, - "outputs": [ - { - "output_type": "stream", - "stream": "stderr", - "text": [ - "/Users/jakeret/workspace/hope/hope/jit.py:128: UserWarning: Recompiling... Reason: State is inconsistent with config. Inconsistent state key: [optimize].\n", - " warnings.warn(\"Recompiling... Reason: {0}\".format(le))\n" + "data": { + "application/json": { + "Software versions": [ + { + "module": "Python", + "version": "2.7.8 (default, Jul 13 2014, 17:11:32) [GCC 4.2.1 Compatible Apple LLVM 5.1 (clang-503.0.40)]" + }, + { + "module": "IPython", + "version": "1.1.0" + }, + { + "module": "OS", + "version": "posix [darwin]" + }, + { + "module": "numpy", + "version": "1.8.1" + }, + { + "module": "Cython", + "version": "0.20.2" + }, + { + "module": "numba", + "version": "0.13.3" + }, + { + "module": "hope", + "version": "0.3.0" + }, + { + "module": "numexpr", + "version": "2.4" + } ] - } - ], - "prompt_number": 5 - }, - { - "cell_type": "code", - "collapsed": false, - "input": [ - "# Hope version\n", - "\n", - "import hope\n", - "hope.config.optimize = False\n", - "@hope.jit\n", - "def ln_hope_exp(X, Y):\n", - " x = (X - 1)\n", - " x2 = x*x\n", - " x4 = x2*x2\n", - " x6 = x4*x2\n", - " x8 = x4*x4\n", - " Y[:] = x - x2 / 2 + x * x2 / 3 - x4 / 4 + x * x4 / 5 - x6 / 6 + x6 * x / 7 - x8 / 8 + x8 * x / 9" - ], - "language": "python", - "metadata": { - "slideshow": { - "slide_type": "slide" - } - }, - "outputs": [], - "prompt_number": 6 - }, - { - "cell_type": "code", - "collapsed": false, - "input": [ - "# numba version\n", - "\n", - "@numba.jit\n", - "def ln_numba(X, Y):\n", - " Y[:] = (X-1) - (X-1)**2 / 2 + (X-1)**3 / 3 - (X-1)**4 / 4 + (X-1)**5 / 5 - (X-1)**6 / 6 + (X-1)**7 / 7 - (X-1)**8 / 8 + (X-1)**9 / 9\n" - ], - "language": "python", - "metadata": { - "slideshow": { - "slide_type": "slide" - } - }, - "outputs": [], - "prompt_number": 7 - }, - { - "cell_type": "code", - "collapsed": false, - "input": [ - "# numba version\n", - "\n", - "import numba\n", - "\n", - "@numba.jit\n", - "def ln_numba_exp(X, Y):\n", - " x = (X - 1)\n", - " x2 = x*x\n", - " x4 = x2*x2\n", - " x6 = x4*x2\n", - " x8 = x4*x4\n", - " Y[:] = x - x2 / 2 + x * x2 / 3 - x4 / 4 + x * x4 / 5 - x6 / 6 + x6 * x / 7 - x8 / 8 + x8 * x / 9" - ], - "language": "python", - "metadata": { - "slideshow": { - "slide_type": "slide" - } + }, + "text/html": [ + "
SoftwareVersion
Python2.7.8 (default, Jul 13 2014, 17:11:32) [GCC 4.2.1 Compatible Apple LLVM 5.1 (clang-503.0.40)]
IPython1.1.0
OSposix [darwin]
numpy1.8.1
Cython0.20.2
numba0.13.3
hope0.3.0
numexpr2.4
Thu Sep 04 15:13:34 2014 CEST
" + ], + "text/latex": [ + "\\begin{tabular}{|l|l|}\\hline\n", + "{\\bf Software} & {\\bf Version} \\\\ \\hline\\hline\n", + "Python & 2.7.8 (default, Jul 13 2014, 17:11:32) [GCC 4.2.1 Compatible Apple LLVM 5.1 (clang-503.0.40)] \\\\ \\hline\n", + "IPython & 1.1.0 \\\\ \\hline\n", + "OS & posix [darwin] \\\\ \\hline\n", + "numpy & 1.8.1 \\\\ \\hline\n", + "Cython & 0.20.2 \\\\ \\hline\n", + "numba & 0.13.3 \\\\ \\hline\n", + "hope & 0.3.0 \\\\ \\hline\n", + "numexpr & 2.4 \\\\ \\hline\n", + "\\hline \\multicolumn{2}{|l|}{Thu Sep 04 15:13:34 2014 CEST} \\\\ \\hline\n", + "\\end{tabular}\n" + ], + "text/plain": [ + "Software versions\n", + "Python 2.7.8 (default, Jul 13 2014, 17:11:32) [GCC 4.2.1 Compatible Apple LLVM 5.1 (clang-503.0.40)]\n", + "IPython 1.1.0\n", + "OS posix [darwin]\n", + "numpy 1.8.1\n", + "Cython 0.20.2\n", + "numba 0.13.3\n", + "hope 0.3.0\n", + "numexpr 2.4\n", + "Thu Sep 04 15:13:34 2014 CEST" + ] }, - "outputs": [], - "prompt_number": 8 - }, - { - "cell_type": "code", - "collapsed": false, - "input": [ - "%load_ext cythonmagic" - ], - "language": "python", + "execution_count": 1, "metadata": {}, - "outputs": [ - { - "output_type": "stream", - "stream": "stdout", - "text": [ - "The cythonmagic extension is already loaded. To reload it, use:\n", - " %reload_ext cythonmagic\n" - ] - } - ], - "prompt_number": 9 - }, + "output_type": "execute_result" + } + ], + "source": [ + "import hope\n", + "hope.config.optimize = True\n", + "hope.config.verbose = True\n", + "hope.config.keeptemp = True\n", + "import numba\n", + "import numpy as np\n", + "import numexpr as ne\n", + "\n", + "from util import perf_comp_data\n", + "from native_util import load\n", + "%load_ext cythonmagic\n", + "%load_ext version_information\n", + "%version_information numpy, Cython, numba, hope, numexpr" + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "outputs": [], + "source": [ + "# Python version\n", + "\n", + "def ln_python(X, Y):\n", + " Y[:] = (X-1) - (X-1)**2 / 2 + (X-1)**3 / 3 - (X-1)**4 / 4 + (X-1)**5 / 5 - (X-1)**6 / 6 + (X-1)**7 / 7 - (X-1)**8 / 8 + (X-1)**9 / 9\n" + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "outputs": [], + "source": [ + "# Python version\n", + "\n", + "def ln_python_exp(X, Y):\n", + " x = (X - 1)\n", + " x2 = x*x\n", + " x4 = x2*x2\n", + " x6 = x4*x2\n", + " x8 = x4*x4\n", + " Y[:] = x - x2 / 2 + x * x2 / 3 - x4 / 4 + x * x4 / 5 - x6 / 6 + x6 * x / 7 - x8 / 8 + x8 * x / 9" + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "outputs": [], + "source": [ + "# NumExpr version\n", + "def ln_numexpr(X, Y):\n", + " Y[:] = ne.evaluate(\"(X-1) - (X-1)**2 / 2 + (X-1)**3 / 3 - (X-1)**4 / 4 + (X-1)**5 / 5 - (X-1)**6 / 6 + (X-1)**7 / 7 - (X-1)**8 / 8 + (X-1)**9 / 9\")\n" + ] + }, + { + "cell_type": "code", + "execution_count": 5, + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "outputs": [ { - "cell_type": "code", - "collapsed": false, - "input": [ - "%%cython\n", - "\n", - "cimport cython\n", - "\n", - "cimport cython\n", - "import numpy as np\n", - "cimport numpy as np\n", - "\n", - "@cython.boundscheck(False)\n", - "@cython.wraparound(False)\n", - "def ln_cython(np.ndarray[np.double_t, ndim=1] X, np.ndarray[np.double_t, ndim=1] Y):\n", - " Y[:] = (X-1) - (X-1)**2 / 2 + (X-1)**3 / 3 - (X-1)**4 / 4 + (X-1)**5 / 5 - (X-1)**6 / 6 + (X-1)**7 / 7 - (X-1)**8 / 8 + (X-1)**9 / 9\n", - "\n", - " \n", - "@cython.boundscheck(False)\n", - "@cython.wraparound(False)\n", - "def ln_cython_exp(np.ndarray[np.float_t, ndim=1] X, np.ndarray[np.float_t, ndim=1] Y):\n", - " cdef np.ndarray[np.double_t, ndim=1] x = (X - 1)\n", - " cdef np.ndarray[np.double_t, ndim=1] x2 = x*x\n", - " cdef np.ndarray[np.double_t, ndim=1] x4 = x2*x2\n", - " cdef np.ndarray[np.double_t, ndim=1] x6 = x4*x2\n", - " cdef np.ndarray[np.double_t, ndim=1] x8 = x4*x4\n", - " Y[:] = x - x2 / 2 + x * x2 / 3 - x4 / 4 + x * x4 / 5 - x6 / 6 + x6 * x / 7 - x8 / 8 + x8 * x / 9" - ], - "language": "python", - "metadata": {}, - "outputs": [], - "prompt_number": 10 - }, + "name": "stderr", + "output_type": "stream", + "text": [ + "/Users/jakeret/workspace/hope/hope/jit.py:128: UserWarning: Recompiling... Reason: State is inconsistent with config. Inconsistent state key: [optimize].\n", + " warnings.warn(\"Recompiling... Reason: {0}\".format(le))\n" + ] + } + ], + "source": [ + "# Hope version\n", + "@hope.jit\n", + "def ln_hope(X, Y):\n", + " Y[:] = (X-1) - (X-1)**2 / 2 + (X-1)**3 / 3 - (X-1)**4 / 4 + (X-1)**5 / 5 - (X-1)**6 / 6 + (X-1)**7 / 7 - (X-1)**8 / 8 + (X-1)**9 / 9\n" + ] + }, + { + "cell_type": "code", + "execution_count": 6, + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "outputs": [], + "source": [ + "# Hope version\n", + "\n", + "import hope\n", + "hope.config.optimize = False\n", + "@hope.jit\n", + "def ln_hope_exp(X, Y):\n", + " x = (X - 1)\n", + " x2 = x*x\n", + " x4 = x2*x2\n", + " x6 = x4*x2\n", + " x8 = x4*x4\n", + " Y[:] = x - x2 / 2 + x * x2 / 3 - x4 / 4 + x * x4 / 5 - x6 / 6 + x6 * x / 7 - x8 / 8 + x8 * x / 9" + ] + }, + { + "cell_type": "code", + "execution_count": 7, + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "outputs": [], + "source": [ + "# numba version\n", + "\n", + "@numba.jit\n", + "def ln_numba(X, Y):\n", + " Y[:] = (X-1) - (X-1)**2 / 2 + (X-1)**3 / 3 - (X-1)**4 / 4 + (X-1)**5 / 5 - (X-1)**6 / 6 + (X-1)**7 / 7 - (X-1)**8 / 8 + (X-1)**9 / 9\n" + ] + }, + { + "cell_type": "code", + "execution_count": 8, + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "outputs": [], + "source": [ + "# numba version\n", + "\n", + "import numba\n", + "\n", + "@numba.jit\n", + "def ln_numba_exp(X, Y):\n", + " x = (X - 1)\n", + " x2 = x*x\n", + " x4 = x2*x2\n", + " x6 = x4*x2\n", + " x8 = x4*x4\n", + " Y[:] = x - x2 / 2 + x * x2 / 3 - x4 / 4 + x * x4 / 5 - x6 / 6 + x6 * x / 7 - x8 / 8 + x8 * x / 9" + ] + }, + { + "cell_type": "code", + "execution_count": 9, + "metadata": {}, + "outputs": [ { - "cell_type": "code", - "collapsed": false, - "input": [ - "from native_util import load\n", - "native_ln_mod = load(\"ln\")\n", - "ln_native = native_ln_mod.run\n", - "\n", - "native_ln_opt_mod = load(\"ln_opt\")\n", - "ln_native_opt = native_ln_opt_mod.run\n", - "\n", - "native_ln_exp_mod = load(\"ln_exp\")\n", - "ln_native_exp = native_ln_exp_mod.run\n" - ], - "language": "python", - "metadata": {}, - "outputs": [ - { - "output_type": "stream", - "stream": "stdout", - "text": [ - "running build_ext\n", - "building 'ln' extension\n", - "C compiler: /usr/bin/clang -fno-strict-aliasing -fno-common -dynamic -pipe -Os -fwrapv -DNDEBUG -g -fwrapv -O3 -Wall -Wstrict-prototypes\n", - "\n", - "compile options: '-I/Users/jakeret/Library/Python/2.7/lib/python/site-packages/numpy/core/include -I/opt/local/Library/Frameworks/Python.framework/Versions/2.7/include/python2.7 -c'\n", - "extra options: '-Wall -Wno-unused-variable -march=native -stdlib=libc++ -std=c++11'\n", - "clang: ././src/ln.cpp\n", - "/usr/bin/clang++ -bundle -undefined dynamic_lookup -L/opt/local/lib -Wl,-headerpad_max_install_names -L/opt/local/lib/db46 ./src/ln.o -o ./ln.so\n", - "\n" - ] - }, - { - "output_type": "stream", - "stream": "stdout", - "text": [ - "running build_ext\n", - "building 'ln_opt' extension\n", - "C compiler: /usr/bin/clang -fno-strict-aliasing -fno-common -dynamic -pipe -Os -fwrapv -DNDEBUG -g -fwrapv -O3 -Wall -Wstrict-prototypes\n", - "\n", - "compile options: '-I/Users/jakeret/Library/Python/2.7/lib/python/site-packages/numpy/core/include -I/opt/local/Library/Frameworks/Python.framework/Versions/2.7/include/python2.7 -c'\n", - "extra options: '-Wall -Wno-unused-variable -march=native -stdlib=libc++ -std=c++11'\n", - "clang: ././src/ln_opt.cpp\n", - "/usr/bin/clang++ -bundle -undefined dynamic_lookup -L/opt/local/lib -Wl,-headerpad_max_install_names -L/opt/local/lib/db46 ./src/ln_opt.o -o ./ln_opt.so\n", - "\n" - ] - }, - { - "output_type": "stream", - "stream": "stdout", - "text": [ - "running build_ext\n", - "building 'ln_exp' extension\n", - "C compiler: /usr/bin/clang -fno-strict-aliasing -fno-common -dynamic -pipe -Os -fwrapv -DNDEBUG -g -fwrapv -O3 -Wall -Wstrict-prototypes\n", - "\n", - "compile options: '-I/Users/jakeret/Library/Python/2.7/lib/python/site-packages/numpy/core/include -I/opt/local/Library/Frameworks/Python.framework/Versions/2.7/include/python2.7 -c'\n", - "extra options: '-Wall -Wno-unused-variable -march=native -stdlib=libc++ -std=c++11'\n", - "clang: ././src/ln_exp.cpp\n", - "/usr/bin/clang++ -bundle -undefined dynamic_lookup -L/opt/local/lib -Wl,-headerpad_max_install_names -L/opt/local/lib/db46 ./src/ln_exp.o -o ./ln_exp.so\n", - "\n" - ] - } - ], - "prompt_number": 11 - }, + "name": "stdout", + "output_type": "stream", + "text": [ + "The cythonmagic extension is already loaded. To reload it, use:\n", + " %reload_ext cythonmagic\n" + ] + } + ], + "source": [ + "%load_ext cythonmagic" + ] + }, + { + "cell_type": "code", + "execution_count": 10, + "metadata": {}, + "outputs": [], + "source": [ + "%%cython\n", + "\n", + "cimport cython\n", + "\n", + "cimport cython\n", + "import numpy as np\n", + "cimport numpy as np\n", + "\n", + "@cython.boundscheck(False)\n", + "@cython.wraparound(False)\n", + "def ln_cython(np.ndarray[np.double_t, ndim=1] X, np.ndarray[np.double_t, ndim=1] Y):\n", + " Y[:] = (X-1) - (X-1)**2 / 2 + (X-1)**3 / 3 - (X-1)**4 / 4 + (X-1)**5 / 5 - (X-1)**6 / 6 + (X-1)**7 / 7 - (X-1)**8 / 8 + (X-1)**9 / 9\n", + "\n", + " \n", + "@cython.boundscheck(False)\n", + "@cython.wraparound(False)\n", + "def ln_cython_exp(np.ndarray[np.float_t, ndim=1] X, np.ndarray[np.float_t, ndim=1] Y):\n", + " cdef np.ndarray[np.double_t, ndim=1] x = (X - 1)\n", + " cdef np.ndarray[np.double_t, ndim=1] x2 = x*x\n", + " cdef np.ndarray[np.double_t, ndim=1] x4 = x2*x2\n", + " cdef np.ndarray[np.double_t, ndim=1] x6 = x4*x2\n", + " cdef np.ndarray[np.double_t, ndim=1] x8 = x4*x4\n", + " Y[:] = x - x2 / 2 + x * x2 / 3 - x4 / 4 + x * x4 / 5 - x6 / 6 + x6 * x / 7 - x8 / 8 + x8 * x / 9" + ] + }, + { + "cell_type": "code", + "execution_count": 11, + "metadata": {}, + "outputs": [ { - "cell_type": "code", - "collapsed": false, - "input": [ - "# Hope version - optimized\n", - "\n", - "import hope\n", - "hope.config.optimize = True\n", - "\n", - "@hope.jit\n", - "def ln_hope_opt(X, Y):\n", - " Y[:] = (X-1) - (X-1)**2 / 2 + (X-1)**3 / 3 - (X-1)**4 / 4 + (X-1)**5 / 5 - (X-1)**6 / 6 + (X-1)**7 / 7 - (X-1)**8 / 8 + (X-1)**9 / 9\n", - "\n", + "name": "stdout", + "output_type": "stream", + "text": [ + "running build_ext\n", + "building 'ln' extension\n", + "C compiler: /usr/bin/clang -fno-strict-aliasing -fno-common -dynamic -pipe -Os -fwrapv -DNDEBUG -g -fwrapv -O3 -Wall -Wstrict-prototypes\n", "\n", - "hope.config.optimize = False" - ], - "language": "python", - "metadata": { - "slideshow": { - "slide_type": "notes" - } - }, - "outputs": [], - "prompt_number": 13 - }, - { - "cell_type": "code", - "collapsed": false, - "input": [ - "import numpy as np\n", + "compile options: '-I/Users/jakeret/Library/Python/2.7/lib/python/site-packages/numpy/core/include -I/opt/local/Library/Frameworks/Python.framework/Versions/2.7/include/python2.7 -c'\n", + "extra options: '-Wall -Wno-unused-variable -march=native -stdlib=libc++ -std=c++11'\n", + "clang: ././src/ln.cpp\n", + "/usr/bin/clang++ -bundle -undefined dynamic_lookup -L/opt/local/lib -Wl,-headerpad_max_install_names -L/opt/local/lib/db46 ./src/ln.o -o ./ln.so\n", "\n", - "X = np.random.random(10000).astype(np.float64)\n", - "Y = np.ones_like(X)" - ], - "language": "python", - "metadata": { - "slideshow": { - "slide_type": "slide" - } - }, - "outputs": [], - "prompt_number": 15 - }, - { - "cell_type": "code", - "collapsed": false, - "input": [ - "Y1 = np.ones_like(X)\n", - "Y2 = np.ones_like(X)\n", - "Y3 = np.ones_like(X)\n", - "Y4 = np.ones_like(X)\n", - "Y5 = np.ones_like(X)\n", - "Y6 = np.ones_like(X)\n", - "Y7 = np.ones_like(X)\n", - "Y8 = np.ones_like(X)\n", - "Y9 = np.ones_like(X)\n", - "Y10 = np.ones_like(X)\n", - "Y11 = np.ones_like(X)\n", - "Y12 = np.ones_like(X)\n", - "Y13 = np.ones_like(X)\n", + "running build_ext\n", + "building 'ln_opt' extension\n", + "C compiler: /usr/bin/clang -fno-strict-aliasing -fno-common -dynamic -pipe -Os -fwrapv -DNDEBUG -g -fwrapv -O3 -Wall -Wstrict-prototypes\n", "\n", - "ln_python(X, Y1)\n", - "ln_python_exp(X, Y2)\n", - "ln_cython(X, Y3)\n", - "ln_cython_exp(X, Y4)\n", - "ln_numexpr(X, Y5)\n", - "ln_hope(X, Y6)\n", - "ln_hope_exp(X, Y7)\n", - "ln_numba(X, Y8)\n", - "ln_numba_exp(X, Y9)\n", - "ln_native(X, Y10)\n", - "ln_native_opt(X, Y11)\n", - "ln_native_exp(X, Y12)\n", - "ln_hope_opt(X, Y13)\n", + "compile options: '-I/Users/jakeret/Library/Python/2.7/lib/python/site-packages/numpy/core/include -I/opt/local/Library/Frameworks/Python.framework/Versions/2.7/include/python2.7 -c'\n", + "extra options: '-Wall -Wno-unused-variable -march=native -stdlib=libc++ -std=c++11'\n", + "clang: ././src/ln_opt.cpp\n", + "/usr/bin/clang++ -bundle -undefined dynamic_lookup -L/opt/local/lib -Wl,-headerpad_max_install_names -L/opt/local/lib/db46 ./src/ln_opt.o -o ./ln_opt.so\n", "\n", - "assert np.allclose(Y1,Y2, 1E-10)\n", - "assert np.allclose(Y1,Y3, 1E-10)\n", - "assert np.allclose(Y1,Y4, 1E-10)\n", - "assert np.allclose(Y1,Y5, 1E-10)\n", - "assert np.allclose(Y1,Y6, 1E-10)\n", - "assert np.allclose(Y1,Y7, 1E-10)\n", - "assert np.allclose(Y1,Y8, 1E-10)\n", - "assert np.allclose(Y1,Y9, 1E-10)\n", - "assert np.allclose(Y1,Y10, 1E-10)\n", - "assert np.allclose(Y1,Y11, 1E-10)\n", - "assert np.allclose(Y1,Y12, 1E-10)\n", - "assert np.allclose(Y1,Y13, 1E-10)\n" - ], - "language": "python", - "metadata": {}, - "outputs": [], - "prompt_number": 17 - }, - { - "cell_type": "code", - "collapsed": false, - "input": [ - "import numexpr as ne\n", + "running build_ext\n", + "building 'ln_exp' extension\n", + "C compiler: /usr/bin/clang -fno-strict-aliasing -fno-common -dynamic -pipe -Os -fwrapv -DNDEBUG -g -fwrapv -O3 -Wall -Wstrict-prototypes\n", "\n", - "print \"python\"\n", - "%timeit ln_python(X, Y)\n", - "%timeit ln_python_exp(X, Y)\n", - "print \"numexpr (1)\"\n", - "ne.set_num_threads(1)\n", - "%timeit ln_numexpr(X, Y)\n", - "print \"numexpr ({0})\".format(ne.detect_number_of_cores())\n", - "ne.set_num_threads(ne.detect_number_of_cores())\n", - "%timeit ln_numexpr(X, Y)\n", - "print \"hope\"\n", - "%timeit ln_hope(X, Y)\n", - "%timeit ln_hope_exp(X, Y)\n", - "%timeit ln_hope_opt(X, Y)\n", - "print \"cython\"\n", - "%timeit ln_cython(X, Y)\n", - "%timeit ln_cython_exp(X, Y)\n", - "print \"numba\"\n", - "%timeit ln_numba(X, Y)\n", - "%timeit ln_numba_exp(X, Y)\n", - "print \"native\"\n", - "%timeit ln_native(X, Y)\n", - "%timeit ln_native_opt(X, Y)\n", - "%timeit ln_native_exp(X, Y)\n" - ], - "language": "python", - "metadata": { - "slideshow": { - "slide_type": "slide" - } - }, - "outputs": [ - { - "output_type": "stream", - "stream": "stdout", - "text": [ - "python\n", - "100 loops, best of 3: 2.4 ms per loop" - ] - }, - { - "output_type": "stream", - "stream": "stdout", - "text": [ - "\n", - "1000 loops, best of 3: 299 \u00b5s per loop" - ] - }, - { - "output_type": "stream", - "stream": "stdout", - "text": [ - "\n", - "numexpr (1)\n", - "1000 loops, best of 3: 400 \u00b5s per loop" - ] - }, - { - "output_type": "stream", - "stream": "stdout", - "text": [ - "\n", - "numexpr (8)\n", - "1000 loops, best of 3: 200 \u00b5s per loop" - ] - }, - { - "output_type": "stream", - "stream": "stdout", - "text": [ - "\n", - "hope\n", - "10000 loops, best of 3: 128 \u00b5s per loop" - ] - }, - { - "output_type": "stream", - "stream": "stdout", - "text": [ - "\n", - "1000 loops, best of 3: 212 \u00b5s per loop" - ] - }, - { - "output_type": "stream", - "stream": "stdout", - "text": [ - "\n", - "10000 loops, best of 3: 128 \u00b5s per loop" - ] - }, - { - "output_type": "stream", - "stream": "stdout", - "text": [ - "\n", - "cython\n", - "100 loops, best of 3: 2.4 ms per loop" - ] - }, - { - "output_type": "stream", - "stream": "stdout", - "text": [ - "\n", - "1000 loops, best of 3: 305 \u00b5s per loop" - ] - }, - { - "output_type": "stream", - "stream": "stdout", - "text": [ - "\n", - "numba\n", - "100 loops, best of 3: 2.41 ms per loop" - ] - }, - { - "output_type": "stream", - "stream": "stdout", - "text": [ - "\n", - "1000 loops, best of 3: 305 \u00b5s per loop" - ] - }, - { - "output_type": "stream", - "stream": "stdout", - "text": [ - "\n", - "native\n", - "100 loops, best of 3: 2.12 ms per loop" - ] - }, - { - "output_type": "stream", - "stream": "stdout", - "text": [ - "\n", - "10000 loops, best of 3: 128 \u00b5s per loop" - ] - }, - { - "output_type": "stream", - "stream": "stdout", - "text": [ - "\n", - "10000 loops, best of 3: 106 \u00b5s per loop" - ] - }, - { - "output_type": "stream", - "stream": "stdout", - "text": [ - "\n" - ] - } - ], - "prompt_number": 18 - }, - { - "cell_type": "code", - "collapsed": false, - "input": [ - "from util import perf_comp_data" - ], - "language": "python", - "metadata": {}, - "outputs": [], - "prompt_number": 19 - }, + "compile options: '-I/Users/jakeret/Library/Python/2.7/lib/python/site-packages/numpy/core/include -I/opt/local/Library/Frameworks/Python.framework/Versions/2.7/include/python2.7 -c'\n", + "extra options: '-Wall -Wno-unused-variable -march=native -stdlib=libc++ -std=c++11'\n", + "clang: ././src/ln_exp.cpp\n", + "/usr/bin/clang++ -bundle -undefined dynamic_lookup -L/opt/local/lib -Wl,-headerpad_max_install_names -L/opt/local/lib/db46 ./src/ln_exp.o -o ./ln_exp.so\n", + "\n" + ] + } + ], + "source": [ + "from native_util import load\n", + "native_ln_mod = load(\"ln\")\n", + "ln_native = native_ln_mod.run\n", + "\n", + "native_ln_opt_mod = load(\"ln_opt\")\n", + "ln_native_opt = native_ln_opt_mod.run\n", + "\n", + "native_ln_exp_mod = load(\"ln_exp\")\n", + "ln_native_exp = native_ln_exp_mod.run\n" + ] + }, + { + "cell_type": "code", + "execution_count": 13, + "metadata": { + "slideshow": { + "slide_type": "notes" + } + }, + "outputs": [], + "source": [ + "# Hope version - optimized\n", + "\n", + "import hope\n", + "hope.config.optimize = True\n", + "\n", + "@hope.jit\n", + "def ln_hope_opt(X, Y):\n", + " Y[:] = (X-1) - (X-1)**2 / 2 + (X-1)**3 / 3 - (X-1)**4 / 4 + (X-1)**5 / 5 - (X-1)**6 / 6 + (X-1)**7 / 7 - (X-1)**8 / 8 + (X-1)**9 / 9\n", + "\n", + "\n", + "hope.config.optimize = False" + ] + }, + { + "cell_type": "code", + "execution_count": 15, + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "outputs": [], + "source": [ + "import numpy as np\n", + "\n", + "X = np.random.random(10000).astype(np.float64)\n", + "Y = np.ones_like(X)" + ] + }, + { + "cell_type": "code", + "execution_count": 17, + "metadata": {}, + "outputs": [], + "source": [ + "Y1 = np.ones_like(X)\n", + "Y2 = np.ones_like(X)\n", + "Y3 = np.ones_like(X)\n", + "Y4 = np.ones_like(X)\n", + "Y5 = np.ones_like(X)\n", + "Y6 = np.ones_like(X)\n", + "Y7 = np.ones_like(X)\n", + "Y8 = np.ones_like(X)\n", + "Y9 = np.ones_like(X)\n", + "Y10 = np.ones_like(X)\n", + "Y11 = np.ones_like(X)\n", + "Y12 = np.ones_like(X)\n", + "Y13 = np.ones_like(X)\n", + "\n", + "ln_python(X, Y1)\n", + "ln_python_exp(X, Y2)\n", + "ln_cython(X, Y3)\n", + "ln_cython_exp(X, Y4)\n", + "ln_numexpr(X, Y5)\n", + "ln_hope(X, Y6)\n", + "ln_hope_exp(X, Y7)\n", + "ln_numba(X, Y8)\n", + "ln_numba_exp(X, Y9)\n", + "ln_native(X, Y10)\n", + "ln_native_opt(X, Y11)\n", + "ln_native_exp(X, Y12)\n", + "ln_hope_opt(X, Y13)\n", + "\n", + "assert np.allclose(Y1,Y2, 1E-10)\n", + "assert np.allclose(Y1,Y3, 1E-10)\n", + "assert np.allclose(Y1,Y4, 1E-10)\n", + "assert np.allclose(Y1,Y5, 1E-10)\n", + "assert np.allclose(Y1,Y6, 1E-10)\n", + "assert np.allclose(Y1,Y7, 1E-10)\n", + "assert np.allclose(Y1,Y8, 1E-10)\n", + "assert np.allclose(Y1,Y9, 1E-10)\n", + "assert np.allclose(Y1,Y10, 1E-10)\n", + "assert np.allclose(Y1,Y11, 1E-10)\n", + "assert np.allclose(Y1,Y12, 1E-10)\n", + "assert np.allclose(Y1,Y13, 1E-10)\n" + ] + }, + { + "cell_type": "code", + "execution_count": 18, + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "outputs": [ { - "cell_type": "code", - "collapsed": false, - "input": [ - "func_list = [\"ln_python\", \"ln_python_exp\", \n", - " \"ln_numexpr\", \n", - " \"ln_hope\", \"ln_hope_exp\", \"ln_hope_opt\", \n", - " \"ln_cython\", \"ln_cython_exp\", \n", - " \"ln_numba\", \"ln_numba_exp\", \n", - " \"ln_native\", \"ln_native_opt\", \"ln_native_exp\", ]\n", - "perf_comp_data(func_list,\n", - " len(func_list)*[\"X, Y\"], rep=100)" - ], - "language": "python", - "metadata": {}, - "outputs": [ - { - "output_type": "stream", - "stream": "stdout", - "text": [ - "function: ln_native_exp , av. time sec: 0.00010705, min. time sec: 0.00010586, relative: 1.0\n", - "function: ln_hope_opt , av. time sec: 0.00012398, min. time sec: 0.00012398, relative: 1.2\n", - "function: ln_hope , av. time sec: 0.00012422, min. time sec: 0.00012398, relative: 1.2\n", - "function: ln_native_opt , av. time sec: 0.00012803, min. time sec: 0.00012708, relative: 1.2\n", - "function: ln_hope_exp , av. time sec: 0.00020707, min. time sec: 0.00020695, relative: 1.9\n", - "function: ln_numexpr , av. time sec: 0.00021243, min. time sec: 0.00018501, relative: 2.0\n", - "function: ln_python_exp , av. time sec: 0.00029302, min. time sec: 0.00029206, relative: 2.7\n", - "function: ln_numba_exp , av. time sec: 0.00030351, min. time sec: 0.00029993, relative: 2.8\n", - "function: ln_cython_exp , av. time sec: 0.00030696, min. time sec: 0.00029683, relative: 2.9\n", - "function: ln_native , av. time sec: 0.00215054, min. time sec: 0.00208783, relative: 20.1\n", - "function: ln_cython , av. time sec: 0.00241804, min. time sec: 0.00235510, relative: 22.6\n", - "function: ln_numba , av. time sec: 0.00241804, min. time sec: 0.00237322, relative: 22.6\n", - "function: ln_python , av. time sec: 0.00243747, min. time sec: 0.00235200, relative: 22.8\n" - ] - } - ], - "prompt_number": 20 - }, + "name": "stdout", + "output_type": "stream", + "text": [ + "python\n", + "100 loops, best of 3: 2.4 ms per loop\n", + "1000 loops, best of 3: 299 µs per loop\n", + "numexpr (1)\n", + "1000 loops, best of 3: 400 µs per loop\n", + "numexpr (8)\n", + "1000 loops, best of 3: 200 µs per loop\n", + "hope\n", + "10000 loops, best of 3: 128 µs per loop\n", + "1000 loops, best of 3: 212 µs per loop\n", + "10000 loops, best of 3: 128 µs per loop\n", + "cython\n", + "100 loops, best of 3: 2.4 ms per loop\n", + "1000 loops, best of 3: 305 µs per loop\n", + "numba\n", + "100 loops, best of 3: 2.41 ms per loop\n", + "1000 loops, best of 3: 305 µs per loop\n", + "native\n", + "100 loops, best of 3: 2.12 ms per loop\n", + "10000 loops, best of 3: 128 µs per loop\n", + "10000 loops, best of 3: 106 µs per loop\n" + ] + } + ], + "source": [ + "import numexpr as ne\n", + "\n", + "print \"python\"\n", + "%timeit ln_python(X, Y)\n", + "%timeit ln_python_exp(X, Y)\n", + "print \"numexpr (1)\"\n", + "ne.set_num_threads(1)\n", + "%timeit ln_numexpr(X, Y)\n", + "print \"numexpr ({0})\".format(ne.detect_number_of_cores())\n", + "ne.set_num_threads(ne.detect_number_of_cores())\n", + "%timeit ln_numexpr(X, Y)\n", + "print \"hope\"\n", + "%timeit ln_hope(X, Y)\n", + "%timeit ln_hope_exp(X, Y)\n", + "%timeit ln_hope_opt(X, Y)\n", + "print \"cython\"\n", + "%timeit ln_cython(X, Y)\n", + "%timeit ln_cython_exp(X, Y)\n", + "print \"numba\"\n", + "%timeit ln_numba(X, Y)\n", + "%timeit ln_numba_exp(X, Y)\n", + "print \"native\"\n", + "%timeit ln_native(X, Y)\n", + "%timeit ln_native_opt(X, Y)\n", + "%timeit ln_native_exp(X, Y)\n" + ] + }, + { + "cell_type": "code", + "execution_count": 19, + "metadata": {}, + "outputs": [], + "source": [ + "from util import perf_comp_data" + ] + }, + { + "cell_type": "code", + "execution_count": 20, + "metadata": {}, + "outputs": [ { - "cell_type": "code", - "collapsed": false, - "input": [], - "language": "python", - "metadata": {}, - "outputs": [] + "name": "stdout", + "output_type": "stream", + "text": [ + "function: ln_native_exp , av. time sec: 0.00010705, min. time sec: 0.00010586, relative: 1.0\n", + "function: ln_hope_opt , av. time sec: 0.00012398, min. time sec: 0.00012398, relative: 1.2\n", + "function: ln_hope , av. time sec: 0.00012422, min. time sec: 0.00012398, relative: 1.2\n", + "function: ln_native_opt , av. time sec: 0.00012803, min. time sec: 0.00012708, relative: 1.2\n", + "function: ln_hope_exp , av. time sec: 0.00020707, min. time sec: 0.00020695, relative: 1.9\n", + "function: ln_numexpr , av. time sec: 0.00021243, min. time sec: 0.00018501, relative: 2.0\n", + "function: ln_python_exp , av. time sec: 0.00029302, min. time sec: 0.00029206, relative: 2.7\n", + "function: ln_numba_exp , av. time sec: 0.00030351, min. time sec: 0.00029993, relative: 2.8\n", + "function: ln_cython_exp , av. time sec: 0.00030696, min. time sec: 0.00029683, relative: 2.9\n", + "function: ln_native , av. time sec: 0.00215054, min. time sec: 0.00208783, relative: 20.1\n", + "function: ln_cython , av. time sec: 0.00241804, min. time sec: 0.00235510, relative: 22.6\n", + "function: ln_numba , av. time sec: 0.00241804, min. time sec: 0.00237322, relative: 22.6\n", + "function: ln_python , av. time sec: 0.00243747, min. time sec: 0.00235200, relative: 22.8\n" + ] } ], - "metadata": {} + "source": [ + "func_list = [\"ln_python\", \"ln_python_exp\", \n", + " \"ln_numexpr\", \n", + " \"ln_hope\", \"ln_hope_exp\", \"ln_hope_opt\", \n", + " \"ln_cython\", \"ln_cython_exp\", \n", + " \"ln_numba\", \"ln_numba_exp\", \n", + " \"ln_native\", \"ln_native_opt\", \"ln_native_exp\", ]\n", + "perf_comp_data(func_list,\n", + " len(func_list)*[\"X, Y\"], rep=100)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [] } - ] -} \ No newline at end of file + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.5.1" + } + }, + "nbformat": 4, + "nbformat_minor": 1 +} diff --git a/benchmarks/pairwise.ipynb b/benchmarks/pairwise.ipynb index 98acb33..af69c4b 100644 --- a/benchmarks/pairwise.ipynb +++ b/benchmarks/pairwise.ipynb @@ -1,385 +1,360 @@ { - "metadata": { - "name": "", - "signature": "sha256:1fb785098fbe4c8872abc9c10232ddb25cc7c2e7aa992b02730c9fd63ff5b18f" - }, - "nbformat": 3, - "nbformat_minor": 0, - "worksheets": [ + "cells": [ { - "cells": [ + "cell_type": "code", + "execution_count": 1, + "metadata": {}, + "outputs": [ { - "cell_type": "code", - "collapsed": false, - "input": [ - "import hope\n", - "hope.config.optimize = True\n", - "hope.config.verbose = True\n", - "hope.config.keeptemp = True\n", - "import numba\n", - "import numpy as np\n", - "from util import perf_comp_data\n", - "from native_util import load\n", - "%load_ext cythonmagic\n", - "%load_ext version_information\n", - "%version_information numpy, Cython, numba, hope" - ], - "language": "python", - "metadata": {}, - "outputs": [ - { - "html": [ - "
SoftwareVersion
Python2.7.8 (default, Jul 13 2014, 17:11:32) [GCC 4.2.1 Compatible Apple LLVM 5.1 (clang-503.0.40)]
IPython1.1.0
OSposix [darwin]
numpy1.8.1
Cython0.20.2
numba0.13.3
hope0.3.0
Thu Sep 04 15:18:18 2014 CEST
" - ], - "json": [ - "{\"Software versions\": [{\"version\": \"2.7.8 (default, Jul 13 2014, 17:11:32) [GCC 4.2.1 Compatible Apple LLVM 5.1 (clang-503.0.40)]\", \"module\": \"Python\"}, {\"version\": \"1.1.0\", \"module\": \"IPython\"}, {\"version\": \"posix [darwin]\", \"module\": \"OS\"}, {\"version\": \"1.8.1\", \"module\": \"numpy\"}, {\"version\": \"0.20.2\", \"module\": \"Cython\"}, {\"version\": \"0.13.3\", \"module\": \"numba\"}, {\"version\": \"0.3.0\", \"module\": \"hope\"}]}" - ], - "latex": [ - "\\begin{tabular}{|l|l|}\\hline\n", - "{\\bf Software} & {\\bf Version} \\\\ \\hline\\hline\n", - "Python & 2.7.8 (default, Jul 13 2014, 17:11:32) [GCC 4.2.1 Compatible Apple LLVM 5.1 (clang-503.0.40)] \\\\ \\hline\n", - "IPython & 1.1.0 \\\\ \\hline\n", - "OS & posix [darwin] \\\\ \\hline\n", - "numpy & 1.8.1 \\\\ \\hline\n", - "Cython & 0.20.2 \\\\ \\hline\n", - "numba & 0.13.3 \\\\ \\hline\n", - "hope & 0.3.0 \\\\ \\hline\n", - "\\hline \\multicolumn{2}{|l|}{Thu Sep 04 15:18:18 2014 CEST} \\\\ \\hline\n", - "\\end{tabular}\n" - ], - "metadata": {}, - "output_type": "pyout", - "prompt_number": 1, - "text": [ - "Software versions\n", - "Python 2.7.8 (default, Jul 13 2014, 17:11:32) [GCC 4.2.1 Compatible Apple LLVM 5.1 (clang-503.0.40)]\n", - "IPython 1.1.0\n", - "OS posix [darwin]\n", - "numpy 1.8.1\n", - "Cython 0.20.2\n", - "numba 0.13.3\n", - "hope 0.3.0\n", - "Thu Sep 04 15:18:18 2014 CEST" + "data": { + "application/json": { + "Software versions": [ + { + "module": "Python", + "version": "2.7.8 (default, Jul 13 2014, 17:11:32) [GCC 4.2.1 Compatible Apple LLVM 5.1 (clang-503.0.40)]" + }, + { + "module": "IPython", + "version": "1.1.0" + }, + { + "module": "OS", + "version": "posix [darwin]" + }, + { + "module": "numpy", + "version": "1.8.1" + }, + { + "module": "Cython", + "version": "0.20.2" + }, + { + "module": "numba", + "version": "0.13.3" + }, + { + "module": "hope", + "version": "0.3.0" + } ] - } - ], - "prompt_number": 1 - }, - { - "cell_type": "code", - "collapsed": false, - "input": [ - "# Pure python version\n", - "\n", - "def pairwise_python(X, D):\n", - " M = X.shape[0]\n", - " N = X.shape[1]\n", - " for i in range(M):\n", - " for j in range(M):\n", - " d = 0.0\n", - " for k in range(N):\n", - " tmp = X[i, k] - X[j, k]\n", - " d += tmp * tmp\n", - " D[i, j] = np.sqrt(d)" - ], - "language": "python", - "metadata": {}, - "outputs": [], - "prompt_number": 2 - }, - { - "cell_type": "code", - "collapsed": false, - "input": [ - "# Numpy python version\n", - "\n", - "def pairwise_numpy(X, D):\n", - " M = X.shape[0]\n", - " for i in range(M):\n", - " D[i, :] = np.sqrt(np.sum((X[i, :] - X[:]) ** 2, axis=1))" - ], - "language": "python", - "metadata": {}, - "outputs": [], - "prompt_number": 3 - }, - { - "cell_type": "code", - "collapsed": false, - "input": [ - "# numba version\n", - "@numba.jit(nopython=True)\n", - "def pairwise_numba(X, D):\n", - " M = X.shape[0]\n", - " N = X.shape[1]\n", - " for i in range(M):\n", - " for j in range(M):\n", - " d = 0.0\n", - " for k in range(N):\n", - " tmp = X[i, k] - X[j, k]\n", - " d += tmp * tmp\n", - " D[i, j] = np.sqrt(d)" - ], - "language": "python", - "metadata": {}, - "outputs": [], - "prompt_number": 4 - }, - { - "cell_type": "code", - "collapsed": false, - "input": [ - "%%cython\n", - "\n", - "cimport cython\n", - "from libc.math cimport sqrt\n", - "\n", - "@cython.boundscheck(False)\n", - "@cython.wraparound(False)\n", - "def pairwise_cython(double[:, ::1] X, double[:, ::1] D):\n", - " cdef int M = X.shape[0]\n", - " cdef int N = X.shape[1]\n", - " cdef double tmp, d\n", - " for i in range(M):\n", - " for j in range(M):\n", - " d = 0.0\n", - " for k in range(N):\n", - " tmp = X[i, k] - X[j, k]\n", - " d += tmp * tmp\n", - " D[i, j] = sqrt(d)" - ], - "language": "python", - "metadata": {}, - "outputs": [], - "prompt_number": 5 - }, - { - "cell_type": "code", - "collapsed": false, - "input": [ - "# hope version\n", - "@hope.jit\n", - "def pairwise_hope(X, D, M, N):\n", - " for i in range(M):\n", - " for j in range(M):\n", - " d = 0.0\n", - " for k in range(N):\n", - " tmp = X[i, k] - X[j, k]\n", - " d += tmp * tmp\n", - " D[i, j] = np.sqrt(d)\n" - ], - "language": "python", - "metadata": {}, - "outputs": [], - "prompt_number": 6 - }, - { - "cell_type": "code", - "collapsed": false, - "input": [ - "from native_util import load\n", - "\n", - "native_pairwise_mod = load(\"pairwise\")\n", - "pairwise_native = native_pairwise_mod.run" - ], - "language": "python", - "metadata": {}, - "outputs": [ - { - "output_type": "stream", - "stream": "stdout", - "text": [ - "running build_ext\n", - "building 'pairwise' extension\n", - "C compiler: /usr/bin/clang -fno-strict-aliasing -fno-common -dynamic -pipe -Os -fwrapv -DNDEBUG -g -fwrapv -O3 -Wall -Wstrict-prototypes\n", - "\n", - "compile options: '-I/Users/jakeret/Library/Python/2.7/lib/python/site-packages/numpy/core/include -I/opt/local/Library/Frameworks/Python.framework/Versions/2.7/include/python2.7 -c'\n", - "extra options: '-Wall -Wno-unused-variable -march=native -stdlib=libc++ -std=c++11'\n", - "clang: ././src/pairwise.cpp\n", - "/usr/bin/clang++ -bundle -undefined dynamic_lookup -L/opt/local/lib -Wl,-headerpad_max_install_names -L/opt/local/lib/db46 ./src/pairwise.o -o ./pairwise.so\n", - "\n" - ] - } - ], - "prompt_number": 7 - }, - { - "cell_type": "code", - "collapsed": false, - "input": [ - "X = np.random.random((1000, 3))\n", - "D = np.empty((1000, 1000))" - ], - "language": "python", + }, + "text/html": [ + "
SoftwareVersion
Python2.7.8 (default, Jul 13 2014, 17:11:32) [GCC 4.2.1 Compatible Apple LLVM 5.1 (clang-503.0.40)]
IPython1.1.0
OSposix [darwin]
numpy1.8.1
Cython0.20.2
numba0.13.3
hope0.3.0
Thu Sep 04 15:18:18 2014 CEST
" + ], + "text/latex": [ + "\\begin{tabular}{|l|l|}\\hline\n", + "{\\bf Software} & {\\bf Version} \\\\ \\hline\\hline\n", + "Python & 2.7.8 (default, Jul 13 2014, 17:11:32) [GCC 4.2.1 Compatible Apple LLVM 5.1 (clang-503.0.40)] \\\\ \\hline\n", + "IPython & 1.1.0 \\\\ \\hline\n", + "OS & posix [darwin] \\\\ \\hline\n", + "numpy & 1.8.1 \\\\ \\hline\n", + "Cython & 0.20.2 \\\\ \\hline\n", + "numba & 0.13.3 \\\\ \\hline\n", + "hope & 0.3.0 \\\\ \\hline\n", + "\\hline \\multicolumn{2}{|l|}{Thu Sep 04 15:18:18 2014 CEST} \\\\ \\hline\n", + "\\end{tabular}\n" + ], + "text/plain": [ + "Software versions\n", + "Python 2.7.8 (default, Jul 13 2014, 17:11:32) [GCC 4.2.1 Compatible Apple LLVM 5.1 (clang-503.0.40)]\n", + "IPython 1.1.0\n", + "OS posix [darwin]\n", + "numpy 1.8.1\n", + "Cython 0.20.2\n", + "numba 0.13.3\n", + "hope 0.3.0\n", + "Thu Sep 04 15:18:18 2014 CEST" + ] + }, + "execution_count": 1, "metadata": {}, - "outputs": [], - "prompt_number": 8 - }, + "output_type": "execute_result" + } + ], + "source": [ + "import hope\n", + "hope.config.optimize = True\n", + "hope.config.verbose = True\n", + "hope.config.keeptemp = True\n", + "import numba\n", + "import numpy as np\n", + "from util import perf_comp_data\n", + "from native_util import load\n", + "%load_ext cythonmagic\n", + "%load_ext version_information\n", + "%version_information numpy, Cython, numba, hope" + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "metadata": {}, + "outputs": [], + "source": [ + "# Pure python version\n", + "\n", + "def pairwise_python(X, D):\n", + " M = X.shape[0]\n", + " N = X.shape[1]\n", + " for i in range(M):\n", + " for j in range(M):\n", + " d = 0.0\n", + " for k in range(N):\n", + " tmp = X[i, k] - X[j, k]\n", + " d += tmp * tmp\n", + " D[i, j] = np.sqrt(d)" + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "metadata": {}, + "outputs": [], + "source": [ + "# Numpy python version\n", + "\n", + "def pairwise_numpy(X, D):\n", + " M = X.shape[0]\n", + " for i in range(M):\n", + " D[i, :] = np.sqrt(np.sum((X[i, :] - X[:]) ** 2, axis=1))" + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "metadata": {}, + "outputs": [], + "source": [ + "# numba version\n", + "@numba.jit(nopython=True)\n", + "def pairwise_numba(X, D):\n", + " M = X.shape[0]\n", + " N = X.shape[1]\n", + " for i in range(M):\n", + " for j in range(M):\n", + " d = 0.0\n", + " for k in range(N):\n", + " tmp = X[i, k] - X[j, k]\n", + " d += tmp * tmp\n", + " D[i, j] = np.sqrt(d)" + ] + }, + { + "cell_type": "code", + "execution_count": 5, + "metadata": {}, + "outputs": [], + "source": [ + "%%cython\n", + "\n", + "cimport cython\n", + "from libc.math cimport sqrt\n", + "\n", + "@cython.boundscheck(False)\n", + "@cython.wraparound(False)\n", + "def pairwise_cython(double[:, ::1] X, double[:, ::1] D):\n", + " cdef int M = X.shape[0]\n", + " cdef int N = X.shape[1]\n", + " cdef double tmp, d\n", + " for i in range(M):\n", + " for j in range(M):\n", + " d = 0.0\n", + " for k in range(N):\n", + " tmp = X[i, k] - X[j, k]\n", + " d += tmp * tmp\n", + " D[i, j] = sqrt(d)" + ] + }, + { + "cell_type": "code", + "execution_count": 6, + "metadata": {}, + "outputs": [], + "source": [ + "# hope version\n", + "@hope.jit\n", + "def pairwise_hope(X, D, M, N):\n", + " for i in range(M):\n", + " for j in range(M):\n", + " d = 0.0\n", + " for k in range(N):\n", + " tmp = X[i, k] - X[j, k]\n", + " d += tmp * tmp\n", + " D[i, j] = np.sqrt(d)\n" + ] + }, + { + "cell_type": "code", + "execution_count": 7, + "metadata": {}, + "outputs": [ { - "cell_type": "code", - "collapsed": false, - "input": [ - "D1 = np.empty((1000, 1000))\n", - "D2 = np.empty((1000, 1000))\n", - "D3 = np.empty((1000, 1000))\n", - "D4 = np.empty((1000, 1000))\n", - "D5 = np.empty((1000, 1000))\n", - "D6 = np.empty((1000, 1000))\n", + "name": "stdout", + "output_type": "stream", + "text": [ + "running build_ext\n", + "building 'pairwise' extension\n", + "C compiler: /usr/bin/clang -fno-strict-aliasing -fno-common -dynamic -pipe -Os -fwrapv -DNDEBUG -g -fwrapv -O3 -Wall -Wstrict-prototypes\n", "\n", - "pairwise_python(X, D1)\n", - "pairwise_numpy(X, D2)\n", - "pairwise_numba(X, D3)\n", - "pairwise_cython(X, D4)\n", - "pairwise_hope(X, D5, X.shape[0], X.shape[1])\n", - "pairwise_native(X, D6, X.shape[0], X.shape[1])\n", - "\n", - "assert np.allclose(D1, D2)\n", - "assert np.allclose(D1, D3)\n", - "assert np.allclose(D1, D4)\n", - "assert np.allclose(D1, D5)\n", - "assert np.allclose(D1, D6)\n" - ], - "language": "python", - "metadata": {}, - "outputs": [], - "prompt_number": 10 - }, - { - "cell_type": "code", - "collapsed": false, - "input": [ - "print \"naive python\"\n", - "%timeit pairwise_python(X, D)\n", - "print \"numpy\"\n", - "%timeit pairwise_numpy(X, D)\n", - "print \"numba\"\n", - "%timeit pairwise_numba(X, D)\n", - "print \"cython\"\n", - "%timeit pairwise_cython(X, D)\n", - "print \"hope\"\n", - "%timeit pairwise_hope(X, D, X.shape[0], X.shape[1])\n", - "print \"native\"\n", - "%timeit pairwise_native(X, D, X.shape[0], X.shape[1])" - ], - "language": "python", - "metadata": {}, - "outputs": [ - { - "output_type": "stream", - "stream": "stdout", - "text": [ - "naive python\n", - "1 loops, best of 3: 5.75 s per loop" - ] - }, - { - "output_type": "stream", - "stream": "stdout", - "text": [ - "\n", - "numpy\n", - "10 loops, best of 3: 36.6 ms per loop" - ] - }, - { - "output_type": "stream", - "stream": "stdout", - "text": [ - "\n", - "numba\n", - "100 loops, best of 3: 6.88 ms per loop" - ] - }, - { - "output_type": "stream", - "stream": "stdout", - "text": [ - "\n", - "cython\n", - "100 loops, best of 3: 4.22 ms per loop" - ] - }, - { - "output_type": "stream", - "stream": "stdout", - "text": [ - "\n", - "hope\n", - "100 loops, best of 3: 6.36 ms per loop" - ] - }, - { - "output_type": "stream", - "stream": "stdout", - "text": [ - "\n", - "native\n", - "100 loops, best of 3: 4.23 ms per loop" - ] - }, - { - "output_type": "stream", - "stream": "stdout", - "text": [ - "\n" - ] - } - ], - "prompt_number": 11 - }, - { - "cell_type": "code", - "collapsed": false, - "input": [ - "from util import perf_comp_data" - ], - "language": "python", - "metadata": {}, - "outputs": [] - }, + "compile options: '-I/Users/jakeret/Library/Python/2.7/lib/python/site-packages/numpy/core/include -I/opt/local/Library/Frameworks/Python.framework/Versions/2.7/include/python2.7 -c'\n", + "extra options: '-Wall -Wno-unused-variable -march=native -stdlib=libc++ -std=c++11'\n", + "clang: ././src/pairwise.cpp\n", + "/usr/bin/clang++ -bundle -undefined dynamic_lookup -L/opt/local/lib -Wl,-headerpad_max_install_names -L/opt/local/lib/db46 ./src/pairwise.o -o ./pairwise.so\n", + "\n" + ] + } + ], + "source": [ + "from native_util import load\n", + "\n", + "native_pairwise_mod = load(\"pairwise\")\n", + "pairwise_native = native_pairwise_mod.run" + ] + }, + { + "cell_type": "code", + "execution_count": 8, + "metadata": {}, + "outputs": [], + "source": [ + "X = np.random.random((1000, 3))\n", + "D = np.empty((1000, 1000))" + ] + }, + { + "cell_type": "code", + "execution_count": 10, + "metadata": {}, + "outputs": [], + "source": [ + "D1 = np.empty((1000, 1000))\n", + "D2 = np.empty((1000, 1000))\n", + "D3 = np.empty((1000, 1000))\n", + "D4 = np.empty((1000, 1000))\n", + "D5 = np.empty((1000, 1000))\n", + "D6 = np.empty((1000, 1000))\n", + "\n", + "pairwise_python(X, D1)\n", + "pairwise_numpy(X, D2)\n", + "pairwise_numba(X, D3)\n", + "pairwise_cython(X, D4)\n", + "pairwise_hope(X, D5, X.shape[0], X.shape[1])\n", + "pairwise_native(X, D6, X.shape[0], X.shape[1])\n", + "\n", + "assert np.allclose(D1, D2)\n", + "assert np.allclose(D1, D3)\n", + "assert np.allclose(D1, D4)\n", + "assert np.allclose(D1, D5)\n", + "assert np.allclose(D1, D6)\n" + ] + }, + { + "cell_type": "code", + "execution_count": 11, + "metadata": {}, + "outputs": [ { - "cell_type": "code", - "collapsed": false, - "input": [ - "M, N = X.shape\n", - "data_list = 4*[\"X, D\"]+ 2*[\"X, D, M, N\"]\n", - "#print data_list\n", - "perf_comp_data([\"pairwise_python\", \n", - " \"pairwise_numpy\", \n", - " \"pairwise_numba\", \n", - " \"pairwise_cython\", \n", - " \"pairwise_hope\",\n", - " \"pairwise_native\"],\n", - " data_list, rep=100)" - ], - "language": "python", - "metadata": {}, - "outputs": [ - { - "output_type": "stream", - "stream": "stdout", - "text": [ - "function: pairwise_native , av. time sec: 0.00420749, min. time sec: 0.00414991, relative: 1.0\n", - "function: pairwise_cython , av. time sec: 0.00421405, min. time sec: 0.00415277, relative: 1.0\n", - "function: pairwise_hope , av. time sec: 0.00635457, min. time sec: 0.00626707, relative: 1.5\n", - "function: pairwise_numba , av. time sec: 0.00690150, min. time sec: 0.00681400, relative: 1.6\n", - "function: pairwise_numpy , av. time sec: 0.03677249, min. time sec: 0.03618908, relative: 8.7\n", - "function: pairwise_python , av. time sec: 5.90122139, min. time sec: 5.63599896, relative: 1402.6\n" - ] - } - ], - "prompt_number": 13 - }, + "name": "stdout", + "output_type": "stream", + "text": [ + "naive python\n", + "1 loops, best of 3: 5.75 s per loop\n", + "numpy\n", + "10 loops, best of 3: 36.6 ms per loop\n", + "numba\n", + "100 loops, best of 3: 6.88 ms per loop\n", + "cython\n", + "100 loops, best of 3: 4.22 ms per loop\n", + "hope\n", + "100 loops, best of 3: 6.36 ms per loop\n", + "native\n", + "100 loops, best of 3: 4.23 ms per loop\n" + ] + } + ], + "source": [ + "print \"naive python\"\n", + "%timeit pairwise_python(X, D)\n", + "print \"numpy\"\n", + "%timeit pairwise_numpy(X, D)\n", + "print \"numba\"\n", + "%timeit pairwise_numba(X, D)\n", + "print \"cython\"\n", + "%timeit pairwise_cython(X, D)\n", + "print \"hope\"\n", + "%timeit pairwise_hope(X, D, X.shape[0], X.shape[1])\n", + "print \"native\"\n", + "%timeit pairwise_native(X, D, X.shape[0], X.shape[1])" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "from util import perf_comp_data" + ] + }, + { + "cell_type": "code", + "execution_count": 13, + "metadata": {}, + "outputs": [ { - "cell_type": "code", - "collapsed": false, - "input": [], - "language": "python", - "metadata": {}, - "outputs": [] + "name": "stdout", + "output_type": "stream", + "text": [ + "function: pairwise_native , av. time sec: 0.00420749, min. time sec: 0.00414991, relative: 1.0\n", + "function: pairwise_cython , av. time sec: 0.00421405, min. time sec: 0.00415277, relative: 1.0\n", + "function: pairwise_hope , av. time sec: 0.00635457, min. time sec: 0.00626707, relative: 1.5\n", + "function: pairwise_numba , av. time sec: 0.00690150, min. time sec: 0.00681400, relative: 1.6\n", + "function: pairwise_numpy , av. time sec: 0.03677249, min. time sec: 0.03618908, relative: 8.7\n", + "function: pairwise_python , av. time sec: 5.90122139, min. time sec: 5.63599896, relative: 1402.6\n" + ] } ], - "metadata": {} + "source": [ + "M, N = X.shape\n", + "data_list = 4*[\"X, D\"]+ 2*[\"X, D, M, N\"]\n", + "#print data_list\n", + "perf_comp_data([\"pairwise_python\", \n", + " \"pairwise_numpy\", \n", + " \"pairwise_numba\", \n", + " \"pairwise_cython\", \n", + " \"pairwise_hope\",\n", + " \"pairwise_native\"],\n", + " data_list, rep=100)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.5.1" } - ] -} \ No newline at end of file + }, + "nbformat": 4, + "nbformat_minor": 1 +} diff --git a/benchmarks/simplify.ipynb b/benchmarks/simplify.ipynb index 5da6ab1..5312bce 100644 --- a/benchmarks/simplify.ipynb +++ b/benchmarks/simplify.ipynb @@ -1,346 +1,318 @@ { - "metadata": { - "name": "", - "signature": "sha256:162f3d58c482919f419e58ffcac472a163f0e58c1278956fd7cefd2e91118997" - }, - "nbformat": 3, - "nbformat_minor": 0, - "worksheets": [ + "cells": [ { - "cells": [ - { - "cell_type": "code", - "collapsed": false, - "input": [ - "import hope\n", - "hope.config.optimize = True\n", - "hope.config.verbose = True\n", - "hope.config.keeptemp = True\n", - "import numba\n", - "import numpy as np\n", - "import numexpr as ne\n", - "\n", - "from util import perf_comp_data\n", - "from native_util import load\n", - "%load_ext cythonmagic\n", - "%load_ext version_information\n", - "%version_information numpy, Cython, numba, hope, numexpr" - ], - "language": "python", - "metadata": {}, - "outputs": [ - { - "html": [ - "
SoftwareVersion
Python2.7.8 (default, Jul 13 2014, 17:11:32) [GCC 4.2.1 Compatible Apple LLVM 5.1 (clang-503.0.40)]
IPython1.1.0
OSposix [darwin]
numpy1.8.1
Cython0.20.2
numba0.13.3
hope0.3.0
numexpr2.4
Thu Sep 04 15:17:12 2014 CEST
" - ], - "json": [ - "{\"Software versions\": [{\"version\": \"2.7.8 (default, Jul 13 2014, 17:11:32) [GCC 4.2.1 Compatible Apple LLVM 5.1 (clang-503.0.40)]\", \"module\": \"Python\"}, {\"version\": \"1.1.0\", \"module\": \"IPython\"}, {\"version\": \"posix [darwin]\", \"module\": \"OS\"}, {\"version\": \"1.8.1\", \"module\": \"numpy\"}, {\"version\": \"0.20.2\", \"module\": \"Cython\"}, {\"version\": \"0.13.3\", \"module\": \"numba\"}, {\"version\": \"0.3.0\", \"module\": \"hope\"}, {\"version\": \"2.4\", \"module\": \"numexpr\"}]}" - ], - "latex": [ - "\\begin{tabular}{|l|l|}\\hline\n", - "{\\bf Software} & {\\bf Version} \\\\ \\hline\\hline\n", - "Python & 2.7.8 (default, Jul 13 2014, 17:11:32) [GCC 4.2.1 Compatible Apple LLVM 5.1 (clang-503.0.40)] \\\\ \\hline\n", - "IPython & 1.1.0 \\\\ \\hline\n", - "OS & posix [darwin] \\\\ \\hline\n", - "numpy & 1.8.1 \\\\ \\hline\n", - "Cython & 0.20.2 \\\\ \\hline\n", - "numba & 0.13.3 \\\\ \\hline\n", - "hope & 0.3.0 \\\\ \\hline\n", - "numexpr & 2.4 \\\\ \\hline\n", - "\\hline \\multicolumn{2}{|l|}{Thu Sep 04 15:17:12 2014 CEST} \\\\ \\hline\n", - "\\end{tabular}\n" - ], - "metadata": {}, - "output_type": "pyout", - "prompt_number": 1, - "text": [ - "Software versions\n", - "Python 2.7.8 (default, Jul 13 2014, 17:11:32) [GCC 4.2.1 Compatible Apple LLVM 5.1 (clang-503.0.40)]\n", - "IPython 1.1.0\n", - "OS posix [darwin]\n", - "numpy 1.8.1\n", - "Cython 0.20.2\n", - "numba 0.13.3\n", - "hope 0.3.0\n", - "numexpr 2.4\n", - "Thu Sep 04 15:17:12 2014 CEST" - ] - } - ], - "prompt_number": 1 - }, + "cell_type": "code", + "execution_count": 1, + "metadata": {}, + "outputs": [ { - "cell_type": "code", - "collapsed": false, - "input": [ - "def poly(res, arg):\n", - " res[:] = np.sin(arg)**2 + (arg**3 + arg**2 - arg - 1)/(arg**2 + 2*arg + 1) + np.cos(arg)**2\n", - "hope_poly = hope.jit(poly)\n", - "numba_poly = numba.jit(poly, nopython=False)\n", - "\n", - "native_poly_mod = load(\"poly\")\n", - "native_poly = native_poly_mod.run\n" - ], - "language": "python", - "metadata": {}, - "outputs": [ - { - "output_type": "stream", - "stream": "stdout", - "text": [ - "running build_ext\n", - "building 'poly' extension\n", - "C compiler: /usr/bin/clang -fno-strict-aliasing -fno-common -dynamic -pipe -Os -fwrapv -DNDEBUG -g -fwrapv -O3 -Wall -Wstrict-prototypes\n", - "\n", - "compile options: '-I/Users/jakeret/Library/Python/2.7/lib/python/site-packages/numpy/core/include -I/opt/local/Library/Frameworks/Python.framework/Versions/2.7/include/python2.7 -c'\n", - "extra options: '-Wall -Wno-unused-variable -march=native -stdlib=libc++ -std=c++11'\n", - "clang: ././src/poly.cpp\n", - "/usr/bin/clang++ -bundle -undefined dynamic_lookup -L/opt/local/lib -Wl,-headerpad_max_install_names -L/opt/local/lib/db46 ./src/poly.o -o ./poly.so\n", - "\n" + "data": { + "application/json": { + "Software versions": [ + { + "module": "Python", + "version": "2.7.8 (default, Jul 13 2014, 17:11:32) [GCC 4.2.1 Compatible Apple LLVM 5.1 (clang-503.0.40)]" + }, + { + "module": "IPython", + "version": "1.1.0" + }, + { + "module": "OS", + "version": "posix [darwin]" + }, + { + "module": "numpy", + "version": "1.8.1" + }, + { + "module": "Cython", + "version": "0.20.2" + }, + { + "module": "numba", + "version": "0.13.3" + }, + { + "module": "hope", + "version": "0.3.0" + }, + { + "module": "numexpr", + "version": "2.4" + } ] - } - ], - "prompt_number": 2 - }, - { - "cell_type": "code", - "collapsed": false, - "input": [ - "%%cython\n", - "\n", - "cimport cython\n", - "from libc.math cimport sin\n", - "from libc.math cimport cos\n", - "from libc.math cimport pow\n", - "\n", - "@cython.boundscheck(False)\n", - "@cython.wraparound(False)\n", - "def cython_poly(double[:] res, double[:] arg):\n", - " for i in range(len(arg)):\n", - " i_arg = arg[i]\n", - " res[i] = pow(sin(i_arg),2) + (pow(i_arg,3) + pow(i_arg,2) - i_arg - 1)/(pow(i_arg,2) + 2*i_arg + 1) + pow(cos(i_arg),2)\n", - "\n" - ], - "language": "python", - "metadata": {}, - "outputs": [], - "prompt_number": 3 - }, - { - "cell_type": "code", - "collapsed": false, - "input": [ - "%%cython\n", - "cimport cython\n", - "import numpy as np\n", - "cimport numpy as np\n", - "\n", - "@cython.boundscheck(False)\n", - "@cython.wraparound(False)\n", - "def cython_numpy_poly(np.ndarray[np.double_t, ndim=1] res, np.ndarray[np.double_t, ndim=1] arg):\n", - " res[:] = np.sin(arg)**2 + (arg**3 + arg**2 - arg - 1)/(arg**2 + 2*arg + 1) + np.cos(arg)**2\n" - ], - "language": "python", - "metadata": {}, - "outputs": [], - "prompt_number": 4 - }, - { - "cell_type": "code", - "collapsed": false, - "input": [ - "# NumExpr version\n", - "\n", - "import numexpr as ne\n", - "\n", - "def numexpr_poly(res, arg):\n", - " res[:] = ne.evaluate(\"sin(arg)**2 + (arg**3 + arg**2 - arg - 1)/(arg**2 + 2*arg + 1) + cos(arg)**2\")\n" - ], - "language": "python", + }, + "text/html": [ + "
SoftwareVersion
Python2.7.8 (default, Jul 13 2014, 17:11:32) [GCC 4.2.1 Compatible Apple LLVM 5.1 (clang-503.0.40)]
IPython1.1.0
OSposix [darwin]
numpy1.8.1
Cython0.20.2
numba0.13.3
hope0.3.0
numexpr2.4
Thu Sep 04 15:17:12 2014 CEST
" + ], + "text/latex": [ + "\\begin{tabular}{|l|l|}\\hline\n", + "{\\bf Software} & {\\bf Version} \\\\ \\hline\\hline\n", + "Python & 2.7.8 (default, Jul 13 2014, 17:11:32) [GCC 4.2.1 Compatible Apple LLVM 5.1 (clang-503.0.40)] \\\\ \\hline\n", + "IPython & 1.1.0 \\\\ \\hline\n", + "OS & posix [darwin] \\\\ \\hline\n", + "numpy & 1.8.1 \\\\ \\hline\n", + "Cython & 0.20.2 \\\\ \\hline\n", + "numba & 0.13.3 \\\\ \\hline\n", + "hope & 0.3.0 \\\\ \\hline\n", + "numexpr & 2.4 \\\\ \\hline\n", + "\\hline \\multicolumn{2}{|l|}{Thu Sep 04 15:17:12 2014 CEST} \\\\ \\hline\n", + "\\end{tabular}\n" + ], + "text/plain": [ + "Software versions\n", + "Python 2.7.8 (default, Jul 13 2014, 17:11:32) [GCC 4.2.1 Compatible Apple LLVM 5.1 (clang-503.0.40)]\n", + "IPython 1.1.0\n", + "OS posix [darwin]\n", + "numpy 1.8.1\n", + "Cython 0.20.2\n", + "numba 0.13.3\n", + "hope 0.3.0\n", + "numexpr 2.4\n", + "Thu Sep 04 15:17:12 2014 CEST" + ] + }, + "execution_count": 1, "metadata": {}, - "outputs": [], - "prompt_number": 5 - }, + "output_type": "execute_result" + } + ], + "source": [ + "import hope\n", + "hope.config.optimize = True\n", + "hope.config.verbose = True\n", + "hope.config.keeptemp = True\n", + "import numba\n", + "import numpy as np\n", + "import numexpr as ne\n", + "\n", + "from util import perf_comp_data\n", + "from native_util import load\n", + "%load_ext cythonmagic\n", + "%load_ext version_information\n", + "%version_information numpy, Cython, numba, hope, numexpr" + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "metadata": {}, + "outputs": [ { - "cell_type": "code", - "collapsed": false, - "input": [ - "arg = np.random.random(50000)\n", - "res = np.empty_like(arg)\n", - "\n", - "res1 = np.empty_like(arg)\n", - "res2 = np.empty_like(arg)\n", - "res3 = np.empty_like(arg)\n", - "res4 = np.empty_like(arg)\n", - "res5 = np.empty_like(arg)\n", + "name": "stdout", + "output_type": "stream", + "text": [ + "running build_ext\n", + "building 'poly' extension\n", + "C compiler: /usr/bin/clang -fno-strict-aliasing -fno-common -dynamic -pipe -Os -fwrapv -DNDEBUG -g -fwrapv -O3 -Wall -Wstrict-prototypes\n", "\n", - "poly(res1, arg)\n", - "hope_poly(res2, arg)\n", - "numba_poly(res3, arg)\n", - "native_poly(res4, arg)\n", - "numexpr_poly(res5, arg)\n", - "\n", - "assert np.allclose(res1, res2)\n", - "assert np.allclose(res1, res3)\n", - "assert np.allclose(res1, res4)\n", - "assert np.allclose(res1, res5)\n" - ], - "language": "python", - "metadata": {}, - "outputs": [], - "prompt_number": 7 - }, - { - "cell_type": "code", - "collapsed": false, - "input": [ - "print \"python\"\n", - "%timeit poly(res, arg)\n", - "print \"hope\"\n", - "%timeit hope_poly(res, arg)\n", - "print \"numba\"\n", - "%timeit numba_poly(res, arg)\n", - "print \"cython\"\n", - "%timeit cython_poly(res, arg)\n", - "print \"cython numpy\"\n", - "%timeit cython_numpy_poly(res, arg)\n", - "print \"native\"\n", - "%timeit native_poly(res, arg)\n", - "print \"numexpr (1)\"\n", - "ne.set_num_threads(1)\n", - "%timeit numexpr_poly(res, arg)\n", - "print \"numexpr ({0})\".format(ne.detect_number_of_cores())\n", - "ne.set_num_threads(ne.detect_number_of_cores())\n", - "%timeit numexpr_poly(res, arg)\n", + "compile options: '-I/Users/jakeret/Library/Python/2.7/lib/python/site-packages/numpy/core/include -I/opt/local/Library/Frameworks/Python.framework/Versions/2.7/include/python2.7 -c'\n", + "extra options: '-Wall -Wno-unused-variable -march=native -stdlib=libc++ -std=c++11'\n", + "clang: ././src/poly.cpp\n", + "/usr/bin/clang++ -bundle -undefined dynamic_lookup -L/opt/local/lib -Wl,-headerpad_max_install_names -L/opt/local/lib/db46 ./src/poly.o -o ./poly.so\n", "\n" - ], - "language": "python", - "metadata": {}, - "outputs": [ - { - "output_type": "stream", - "stream": "stdout", - "text": [ - "python\n", - "100 loops, best of 3: 2.57 ms per loop" - ] - }, - { - "output_type": "stream", - "stream": "stdout", - "text": [ - "\n", - "hope\n", - "10000 loops, best of 3: 29 \u00b5s per loop" - ] - }, - { - "output_type": "stream", - "stream": "stdout", - "text": [ - "\n", - "numba\n", - "100 loops, best of 3: 2.66 ms per loop" - ] - }, - { - "output_type": "stream", - "stream": "stdout", - "text": [ - "\n", - "cython\n", - "100 loops, best of 3: 9.97 ms per loop" - ] - }, - { - "output_type": "stream", - "stream": "stdout", - "text": [ - "\n", - "cython numpy\n", - "100 loops, best of 3: 2.56 ms per loop" - ] - }, - { - "output_type": "stream", - "stream": "stdout", - "text": [ - "\n", - "native\n", - "100 loops, best of 3: 2.06 ms per loop" - ] - }, - { - "output_type": "stream", - "stream": "stdout", - "text": [ - "\n", - "numexpr (1)\n", - "1000 loops, best of 3: 1.33 ms per loop" - ] - }, - { - "output_type": "stream", - "stream": "stdout", - "text": [ - "\n", - "numexpr (8)\n", - "1000 loops, best of 3: 550 \u00b5s per loop" - ] - }, - { - "output_type": "stream", - "stream": "stdout", - "text": [ - "\n" - ] - } - ], - "prompt_number": 8 - }, + ] + } + ], + "source": [ + "def poly(res, arg):\n", + " res[:] = np.sin(arg)**2 + (arg**3 + arg**2 - arg - 1)/(arg**2 + 2*arg + 1) + np.cos(arg)**2\n", + "hope_poly = hope.jit(poly)\n", + "numba_poly = numba.jit(poly, nopython=False)\n", + "\n", + "native_poly_mod = load(\"poly\")\n", + "native_poly = native_poly_mod.run\n" + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "metadata": {}, + "outputs": [], + "source": [ + "%%cython\n", + "\n", + "cimport cython\n", + "from libc.math cimport sin\n", + "from libc.math cimport cos\n", + "from libc.math cimport pow\n", + "\n", + "@cython.boundscheck(False)\n", + "@cython.wraparound(False)\n", + "def cython_poly(double[:] res, double[:] arg):\n", + " for i in range(len(arg)):\n", + " i_arg = arg[i]\n", + " res[i] = pow(sin(i_arg),2) + (pow(i_arg,3) + pow(i_arg,2) - i_arg - 1)/(pow(i_arg,2) + 2*i_arg + 1) + pow(cos(i_arg),2)\n", + "\n" + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "metadata": {}, + "outputs": [], + "source": [ + "%%cython\n", + "cimport cython\n", + "import numpy as np\n", + "cimport numpy as np\n", + "\n", + "@cython.boundscheck(False)\n", + "@cython.wraparound(False)\n", + "def cython_numpy_poly(np.ndarray[np.double_t, ndim=1] res, np.ndarray[np.double_t, ndim=1] arg):\n", + " res[:] = np.sin(arg)**2 + (arg**3 + arg**2 - arg - 1)/(arg**2 + 2*arg + 1) + np.cos(arg)**2\n" + ] + }, + { + "cell_type": "code", + "execution_count": 5, + "metadata": {}, + "outputs": [], + "source": [ + "# NumExpr version\n", + "\n", + "import numexpr as ne\n", + "\n", + "def numexpr_poly(res, arg):\n", + " res[:] = ne.evaluate(\"sin(arg)**2 + (arg**3 + arg**2 - arg - 1)/(arg**2 + 2*arg + 1) + cos(arg)**2\")\n" + ] + }, + { + "cell_type": "code", + "execution_count": 7, + "metadata": {}, + "outputs": [], + "source": [ + "arg = np.random.random(50000)\n", + "res = np.empty_like(arg)\n", + "\n", + "res1 = np.empty_like(arg)\n", + "res2 = np.empty_like(arg)\n", + "res3 = np.empty_like(arg)\n", + "res4 = np.empty_like(arg)\n", + "res5 = np.empty_like(arg)\n", + "\n", + "poly(res1, arg)\n", + "hope_poly(res2, arg)\n", + "numba_poly(res3, arg)\n", + "native_poly(res4, arg)\n", + "numexpr_poly(res5, arg)\n", + "\n", + "assert np.allclose(res1, res2)\n", + "assert np.allclose(res1, res3)\n", + "assert np.allclose(res1, res4)\n", + "assert np.allclose(res1, res5)\n" + ] + }, + { + "cell_type": "code", + "execution_count": 8, + "metadata": {}, + "outputs": [ { - "cell_type": "code", - "collapsed": false, - "input": [ - "ne.set_num_threads(8)\n", - "perf_comp_data([\"poly\", \n", - " \"hope_poly\", \n", - " \"numba_poly\", \n", - " \"cython_poly\", \n", - " \"cython_numpy_poly\", \n", - " \"native_poly\",\n", - " \"numexpr_poly\"], \n", - " 7*[\"res, arg\"], rep=100)" - ], - "language": "python", - "metadata": {}, - "outputs": [ - { - "output_type": "stream", - "stream": "stdout", - "text": [ - "function: hope_poly , av. time sec: 0.00002789, min. time sec: 0.00002694, relative: 1.0\n", - "function: numexpr_poly , av. time sec: 0.00054300, min. time sec: 0.00048995, relative: 19.5\n", - "function: native_poly , av. time sec: 0.00204802, min. time sec: 0.00201893, relative: 73.4\n", - "function: cython_numpy_poly , av. time sec: 0.00257111, min. time sec: 0.00251794, relative: 92.2\n", - "function: poly , av. time sec: 0.00260139, min. time sec: 0.00251603, relative: 93.3\n", - "function: numba_poly , av. time sec: 0.00272107, min. time sec: 0.00263000, relative: 97.5\n", - "function: cython_poly , av. time sec: 0.00962663, min. time sec: 0.00949407, relative: 345.1\n" - ] - } - ], - "prompt_number": 9 - }, + "name": "stdout", + "output_type": "stream", + "text": [ + "python\n", + "100 loops, best of 3: 2.57 ms per loop\n", + "hope\n", + "10000 loops, best of 3: 29 µs per loop\n", + "numba\n", + "100 loops, best of 3: 2.66 ms per loop\n", + "cython\n", + "100 loops, best of 3: 9.97 ms per loop\n", + "cython numpy\n", + "100 loops, best of 3: 2.56 ms per loop\n", + "native\n", + "100 loops, best of 3: 2.06 ms per loop\n", + "numexpr (1)\n", + "1000 loops, best of 3: 1.33 ms per loop\n", + "numexpr (8)\n", + "1000 loops, best of 3: 550 µs per loop\n" + ] + } + ], + "source": [ + "print \"python\"\n", + "%timeit poly(res, arg)\n", + "print \"hope\"\n", + "%timeit hope_poly(res, arg)\n", + "print \"numba\"\n", + "%timeit numba_poly(res, arg)\n", + "print \"cython\"\n", + "%timeit cython_poly(res, arg)\n", + "print \"cython numpy\"\n", + "%timeit cython_numpy_poly(res, arg)\n", + "print \"native\"\n", + "%timeit native_poly(res, arg)\n", + "print \"numexpr (1)\"\n", + "ne.set_num_threads(1)\n", + "%timeit numexpr_poly(res, arg)\n", + "print \"numexpr ({0})\".format(ne.detect_number_of_cores())\n", + "ne.set_num_threads(ne.detect_number_of_cores())\n", + "%timeit numexpr_poly(res, arg)\n", + "\n" + ] + }, + { + "cell_type": "code", + "execution_count": 9, + "metadata": {}, + "outputs": [ { - "cell_type": "code", - "collapsed": false, - "input": [], - "language": "python", - "metadata": {}, - "outputs": [] + "name": "stdout", + "output_type": "stream", + "text": [ + "function: hope_poly , av. time sec: 0.00002789, min. time sec: 0.00002694, relative: 1.0\n", + "function: numexpr_poly , av. time sec: 0.00054300, min. time sec: 0.00048995, relative: 19.5\n", + "function: native_poly , av. time sec: 0.00204802, min. time sec: 0.00201893, relative: 73.4\n", + "function: cython_numpy_poly , av. time sec: 0.00257111, min. time sec: 0.00251794, relative: 92.2\n", + "function: poly , av. time sec: 0.00260139, min. time sec: 0.00251603, relative: 93.3\n", + "function: numba_poly , av. time sec: 0.00272107, min. time sec: 0.00263000, relative: 97.5\n", + "function: cython_poly , av. time sec: 0.00962663, min. time sec: 0.00949407, relative: 345.1\n" + ] } ], - "metadata": {} + "source": [ + "ne.set_num_threads(8)\n", + "perf_comp_data([\"poly\", \n", + " \"hope_poly\", \n", + " \"numba_poly\", \n", + " \"cython_poly\", \n", + " \"cython_numpy_poly\", \n", + " \"native_poly\",\n", + " \"numexpr_poly\"], \n", + " 7*[\"res, arg\"], rep=100)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [] } - ] -} \ No newline at end of file + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.5.1" + } + }, + "nbformat": 4, + "nbformat_minor": 1 +} diff --git a/benchmarks/star.ipynb b/benchmarks/star.ipynb index 09cf1f5..efa2733 100644 --- a/benchmarks/star.ipynb +++ b/benchmarks/star.ipynb @@ -1,494 +1,464 @@ { - "metadata": { - "name": "", - "signature": "sha256:420bd7e5c9a28bef62dee51f5a5a1770f5cec1ee324c4204a83a2dc17c7c78f0" - }, - "nbformat": 3, - "nbformat_minor": 0, - "worksheets": [ + "cells": [ { - "cells": [ + "cell_type": "code", + "execution_count": 1, + "metadata": {}, + "outputs": [ { - "cell_type": "code", - "collapsed": false, - "input": [ - "import hope\n", - "hope.config.optimize = True\n", - "hope.config.verbose = True\n", - "hope.config.keeptemp = True\n", - "import numba\n", - "import numpy as np\n", - "from util import perf_comp_data\n", - "from native_util import load\n", - "%load_ext cythonmagic\n", - "%load_ext version_information\n", - "%version_information numpy, Cython, numba, hope" - ], - "language": "python", - "metadata": {}, - "outputs": [ - { - "html": [ - "
SoftwareVersion
Python2.7.8 64bit [GCC 4.2.1 Compatible Apple LLVM 5.1 (clang-503.0.40)]
IPython1.1.0
OSDarwin 13.4.0 x86_64 i386 64bit
numpy1.8.1
Cython0.20.2
numbatag: 0.13.3
hope0.3.0
Tue Nov 25 14:36:16 2014 CET
" - ], - "json": [ - "{\"Software versions\": [{\"version\": \"2.7.8 64bit [GCC 4.2.1 Compatible Apple LLVM 5.1 (clang-503.0.40)]\", \"module\": \"Python\"}, {\"version\": \"1.1.0\", \"module\": \"IPython\"}, {\"version\": \"Darwin 13.4.0 x86_64 i386 64bit\", \"module\": \"OS\"}, {\"version\": \"1.8.1\", \"module\": \"numpy\"}, {\"version\": \"0.20.2\", \"module\": \"Cython\"}, {\"version\": \"tag: 0.13.3\", \"module\": \"numba\"}, {\"version\": \"0.3.0\", \"module\": \"hope\"}]}" - ], - "latex": [ - "\\begin{tabular}{|l|l|}\\hline\n", - "{\\bf Software} & {\\bf Version} \\\\ \\hline\\hline\n", - "Python & 2.7.8 64bit [GCC 4.2.1 Compatible Apple LLVM 5.1 (clang-503.0.40)] \\\\ \\hline\n", - "IPython & 1.1.0 \\\\ \\hline\n", - "OS & Darwin 13.4.0 x86\\_64 i386 64bit \\\\ \\hline\n", - "numpy & 1.8.1 \\\\ \\hline\n", - "Cython & 0.20.2 \\\\ \\hline\n", - "numba & tag: 0.13.3 \\\\ \\hline\n", - "hope & 0.3.0 \\\\ \\hline\n", - "\\hline \\multicolumn{2}{|l|}{Tue Nov 25 14:36:16 2014 CET} \\\\ \\hline\n", - "\\end{tabular}\n" - ], - "metadata": {}, - "output_type": "pyout", - "prompt_number": 1, - "text": [ - "Software versions\n", - "Python 2.7.8 64bit [GCC 4.2.1 Compatible Apple LLVM 5.1 (clang-503.0.40)]\n", - "IPython 1.1.0\n", - "OS Darwin 13.4.0 x86_64 i386 64bit\n", - "numpy 1.8.1\n", - "Cython 0.20.2\n", - "numba tag: 0.13.3\n", - "hope 0.3.0\n", - "Tue Nov 25 14:36:16 2014 CET" + "data": { + "application/json": { + "Software versions": [ + { + "module": "Python", + "version": "2.7.8 64bit [GCC 4.2.1 Compatible Apple LLVM 5.1 (clang-503.0.40)]" + }, + { + "module": "IPython", + "version": "1.1.0" + }, + { + "module": "OS", + "version": "Darwin 13.4.0 x86_64 i386 64bit" + }, + { + "module": "numpy", + "version": "1.8.1" + }, + { + "module": "Cython", + "version": "0.20.2" + }, + { + "module": "numba", + "version": "tag: 0.13.3" + }, + { + "module": "hope", + "version": "0.3.0" + } ] - } - ], - "prompt_number": 1 - }, - { - "cell_type": "code", - "collapsed": false, - "input": [ - "b = 3.5\n", - "a = 1. / np.sqrt(2. ** (1. / (b - 1.)) - 1.)\n", - "\n", - "r50 = 2\n", - "\n", - "center = np.array([10.141, 10.414])\n", - "dims = np.array([20, 20])\n", - "# coefficients generated by http://keisan.casio.com/has10/SpecExec.cgi?id=system/2006/1280624821, 7th order\n", - "x1D = np.array([ \\\n", - " 0.5 - 0.9491079123427585245262 / 2 \\\n", - " , 0.5 - 0.7415311855993944398639 / 2 \\\n", - " , 0.5 - 0.4058451513773971669066 / 2 \\\n", - " , 0.5 \\\n", - " , 0.5 + 0.4058451513773971669066 / 2 \\\n", - " , 0.5 + 0.7415311855993944398639 / 2 \\\n", - " , 0.5 + 0.9491079123427585245262 / 2 \\\n", - "], dtype=np.float32)\n", - "w1D = np.array([ \\\n", - " 0.1294849661688696932706 / 2 \\\n", - " , 0.2797053914892766679015 / 2 \\\n", - " , 0.38183005050511894495 / 2 \\\n", - " , 0.4179591836734693877551 / 2 \\\n", - " , 0.38183005050511894495 / 2 \\\n", - " , 0.2797053914892766679015 / 2 \\\n", - " , 0.1294849661688696932706 / 2 \\\n", - "], dtype=np.float32)\n", - "w2D = np.outer(w1D, w1D)\n", - "\n", - "print dims.dtype" - ], - "language": "python", + }, + "text/html": [ + "
SoftwareVersion
Python2.7.8 64bit [GCC 4.2.1 Compatible Apple LLVM 5.1 (clang-503.0.40)]
IPython1.1.0
OSDarwin 13.4.0 x86_64 i386 64bit
numpy1.8.1
Cython0.20.2
numbatag: 0.13.3
hope0.3.0
Tue Nov 25 14:36:16 2014 CET
" + ], + "text/latex": [ + "\\begin{tabular}{|l|l|}\\hline\n", + "{\\bf Software} & {\\bf Version} \\\\ \\hline\\hline\n", + "Python & 2.7.8 64bit [GCC 4.2.1 Compatible Apple LLVM 5.1 (clang-503.0.40)] \\\\ \\hline\n", + "IPython & 1.1.0 \\\\ \\hline\n", + "OS & Darwin 13.4.0 x86\\_64 i386 64bit \\\\ \\hline\n", + "numpy & 1.8.1 \\\\ \\hline\n", + "Cython & 0.20.2 \\\\ \\hline\n", + "numba & tag: 0.13.3 \\\\ \\hline\n", + "hope & 0.3.0 \\\\ \\hline\n", + "\\hline \\multicolumn{2}{|l|}{Tue Nov 25 14:36:16 2014 CET} \\\\ \\hline\n", + "\\end{tabular}\n" + ], + "text/plain": [ + "Software versions\n", + "Python 2.7.8 64bit [GCC 4.2.1 Compatible Apple LLVM 5.1 (clang-503.0.40)]\n", + "IPython 1.1.0\n", + "OS Darwin 13.4.0 x86_64 i386 64bit\n", + "numpy 1.8.1\n", + "Cython 0.20.2\n", + "numba tag: 0.13.3\n", + "hope 0.3.0\n", + "Tue Nov 25 14:36:16 2014 CET" + ] + }, + "execution_count": 1, "metadata": {}, - "outputs": [ - { - "output_type": "stream", - "stream": "stdout", - "text": [ - "int64\n" - ] - } - ], - "prompt_number": 2 - }, + "output_type": "execute_result" + } + ], + "source": [ + "import hope\n", + "hope.config.optimize = True\n", + "hope.config.verbose = True\n", + "hope.config.keeptemp = True\n", + "import numba\n", + "import numpy as np\n", + "from util import perf_comp_data\n", + "from native_util import load\n", + "%load_ext cythonmagic\n", + "%load_ext version_information\n", + "%version_information numpy, Cython, numba, hope" + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "metadata": {}, + "outputs": [ { - "cell_type": "code", - "collapsed": false, - "input": [ - "def pdf(density, dims, center, w2D, r50, b, a):\n", - " for x in range(dims[0]):\n", - " for y in range(dims[1]):\n", - " dr = np.sqrt((x - center[0]) ** 2 + (y - center[1]) ** 2)\n", - " density[x, y] = np.sum(w2D * 2 * (b - 1) / (2 * np.pi * (r50 * a)**2) * (1 + (dr / (r50 * a))**2)**(-b))\n", - " return density" - ], - "language": "python", - "metadata": {}, - "outputs": [], - "prompt_number": 3 - }, + "name": "stdout", + "output_type": "stream", + "text": [ + "int64\n" + ] + } + ], + "source": [ + "b = 3.5\n", + "a = 1. / np.sqrt(2. ** (1. / (b - 1.)) - 1.)\n", + "\n", + "r50 = 2\n", + "\n", + "center = np.array([10.141, 10.414])\n", + "dims = np.array([20, 20])\n", + "# coefficients generated by http://keisan.casio.com/has10/SpecExec.cgi?id=system/2006/1280624821, 7th order\n", + "x1D = np.array([ \\\n", + " 0.5 - 0.9491079123427585245262 / 2 \\\n", + " , 0.5 - 0.7415311855993944398639 / 2 \\\n", + " , 0.5 - 0.4058451513773971669066 / 2 \\\n", + " , 0.5 \\\n", + " , 0.5 + 0.4058451513773971669066 / 2 \\\n", + " , 0.5 + 0.7415311855993944398639 / 2 \\\n", + " , 0.5 + 0.9491079123427585245262 / 2 \\\n", + "], dtype=np.float32)\n", + "w1D = np.array([ \\\n", + " 0.1294849661688696932706 / 2 \\\n", + " , 0.2797053914892766679015 / 2 \\\n", + " , 0.38183005050511894495 / 2 \\\n", + " , 0.4179591836734693877551 / 2 \\\n", + " , 0.38183005050511894495 / 2 \\\n", + " , 0.2797053914892766679015 / 2 \\\n", + " , 0.1294849661688696932706 / 2 \\\n", + "], dtype=np.float32)\n", + "w2D = np.outer(w1D, w1D)\n", + "\n", + "print dims.dtype" + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "metadata": {}, + "outputs": [], + "source": [ + "def pdf(density, dims, center, w2D, r50, b, a):\n", + " for x in range(dims[0]):\n", + " for y in range(dims[1]):\n", + " dr = np.sqrt((x - center[0]) ** 2 + (y - center[1]) ** 2)\n", + " density[x, y] = np.sum(w2D * 2 * (b - 1) / (2 * np.pi * (r50 * a)**2) * (1 + (dr / (r50 * a))**2)**(-b))\n", + " return density" + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "metadata": {}, + "outputs": [], + "source": [ + "%%cython\n", + "#Naive Cython implementation\n", + "cimport cython\n", + "import numpy as np\n", + "cimport numpy as np\n", + "\n", + "@cython.boundscheck(False)\n", + "@cython.wraparound(False)\n", + "def cython_pdf(np.ndarray[np.float32_t, ndim=2] density, \n", + " np.ndarray[np.long_t, ndim=1] dims, \n", + " np.ndarray[np.float_t, ndim=1] center, \n", + " np.ndarray[np.float32_t, ndim=2] w2D, \n", + " float r50, \n", + " float b, \n", + " float a):\n", + " \n", + " cdef double dr = 0.0\n", + "\n", + " for x in range(dims[0]):\n", + " for y in range(dims[1]):\n", + " dr = np.sqrt((x - center[0]) ** 2 + (y - center[1]) ** 2)\n", + " density[x, y] = np.sum(w2D * 2 * (b - 1) / (2 * np.pi * (r50 * a)**2) * (1 + (dr / (r50 * a))**2)**(-b))\n", + " return density\n", + "\n", + "\n" + ] + }, + { + "cell_type": "code", + "execution_count": 5, + "metadata": {}, + "outputs": [], + "source": [ + "%%cython\n", + "#Modestly tunned Cython implementation\n", + "cimport cython\n", + "import numpy as np\n", + "cimport numpy as np\n", + "\n", + "@cython.boundscheck(False)\n", + "@cython.wraparound(False)\n", + "def cython_pdf_opt(np.ndarray[np.float32_t, ndim=2] density, \n", + " np.ndarray[np.int64_t, ndim=1] dims, \n", + " np.ndarray[np.float64_t, ndim=1] center, \n", + " np.ndarray[np.float32_t, ndim=2] w2D, \n", + " float r50, \n", + " float b, \n", + " np.float32_t a):\n", + " \n", + " cdef np.float32_t pi = np.pi\n", + " cdef np.float32_t dr = 0.0\n", + " cdef int x, y\n", + " \n", + " for x in range(dims[0]):\n", + " for y in range(dims[1]):\n", + " dr = ((x - center[0]) ** 2 + (y - center[1]) ** 2)**(0.5)\n", + " density[x, y] = np.sum(2. * (b - 1.) / (2. * pi * (r50 * a)**2) * (1. + (dr / (r50 * a))**2)**(-b) * w2D)\n", + " return density" + ] + }, + { + "cell_type": "code", + "execution_count": 6, + "metadata": {}, + "outputs": [], + "source": [ + "%%cython\n", + "#Aggresively tunned Cython implementation\n", + "cimport cython\n", + "import numpy as np\n", + "cimport numpy as np\n", + "\n", + "@cython.boundscheck(False)\n", + "@cython.wraparound(False)\n", + "@cython.cdivision(True)\n", + "def cython_pdf_unrolled(np.ndarray[np.float32_t, ndim=2] density, \n", + " np.ndarray[np.long_t, ndim=1] dims, \n", + " np.ndarray[np.float_t, ndim=1] center, \n", + " np.ndarray[np.float32_t, ndim=2] w2D, \n", + " float r50, \n", + " float b, \n", + " float a):\n", + "\n", + " cdef float pi = np.pi\n", + " cdef int x,y,k,m\n", + " cdef float s,dr,d\n", + " cdef Py_ssize_t fac_x = 7\n", + " cdef Py_ssize_t fac_y = 7\n", + " cdef np.ndarray[np.float32_t, ndim=2] fac = np.zeros([fac_x, fac_y], dtype=np.float32)\n", + " cdef float w2D_fac = 2 * (b - 1) / (2 * pi * (r50 * a)**2)\n", + "\n", + " for k in range(fac_x):\n", + " for m in range(fac_y):\n", + " fac[k, m] = w2D[k, m] * w2D_fac\n", + "\n", + " for x in range(dims[0]):\n", + " for y in range(dims[1]):\n", + " dr = ((x - center[0]) ** 2 + (y - center[1]) ** 2)**(1./2)\n", + " d = (1 + (dr / (r50 * a))**2)**(-b)\n", + " \n", + " s = 0\n", + " for k in range(fac_x):\n", + " for m in range(fac_y):\n", + " s += fac[k, m] * d\n", + " density[x, y] = s\n", + " return density" + ] + }, + { + "cell_type": "code", + "execution_count": 7, + "metadata": {}, + "outputs": [ { - "cell_type": "code", - "collapsed": false, - "input": [ - "%%cython\n", - "#Naive Cython implementation\n", - "cimport cython\n", - "import numpy as np\n", - "cimport numpy as np\n", - "\n", - "@cython.boundscheck(False)\n", - "@cython.wraparound(False)\n", - "def cython_pdf(np.ndarray[np.float32_t, ndim=2] density, \n", - " np.ndarray[np.long_t, ndim=1] dims, \n", - " np.ndarray[np.float_t, ndim=1] center, \n", - " np.ndarray[np.float32_t, ndim=2] w2D, \n", - " float r50, \n", - " float b, \n", - " float a):\n", - " \n", - " cdef double dr = 0.0\n", - "\n", - " for x in range(dims[0]):\n", - " for y in range(dims[1]):\n", - " dr = np.sqrt((x - center[0]) ** 2 + (y - center[1]) ** 2)\n", - " density[x, y] = np.sum(w2D * 2 * (b - 1) / (2 * np.pi * (r50 * a)**2) * (1 + (dr / (r50 * a))**2)**(-b))\n", - " return density\n", + "name": "stdout", + "output_type": "stream", + "text": [ + "running build_ext\n", + "building 'pdf' extension\n", + "C compiler: /usr/bin/clang -fno-strict-aliasing -fno-common -dynamic -pipe -Os -fwrapv -DNDEBUG -g -fwrapv -O3 -Wall -Wstrict-prototypes\n", "\n", + "compile options: '-I/Users/jakeret/workspace/virtualenvs/hope_benchmarks/lib/python2.7/site-packages/numpy/core/include -I/opt/local/Library/Frameworks/Python.framework/Versions/2.7/include/python2.7 -c'\n", + "extra options: '-Wall -Wno-unused-variable -march=native -stdlib=libc++ -std=c++11'\n", + "clang: ././src/pdf.cpp\n", + "/usr/bin/clang++ -bundle -undefined dynamic_lookup -L/opt/local/lib -Wl,-headerpad_max_install_names -L/opt/local/lib/db48 ./src/pdf.o -o ./pdf.so\n", "\n" - ], - "language": "python", - "metadata": {}, - "outputs": [], - "prompt_number": 4 - }, - { - "cell_type": "code", - "collapsed": false, - "input": [ - "%%cython\n", - "#Modestly tunned Cython implementation\n", - "cimport cython\n", - "import numpy as np\n", - "cimport numpy as np\n", - "\n", - "@cython.boundscheck(False)\n", - "@cython.wraparound(False)\n", - "def cython_pdf_opt(np.ndarray[np.float32_t, ndim=2] density, \n", - " np.ndarray[np.int64_t, ndim=1] dims, \n", - " np.ndarray[np.float64_t, ndim=1] center, \n", - " np.ndarray[np.float32_t, ndim=2] w2D, \n", - " float r50, \n", - " float b, \n", - " np.float32_t a):\n", - " \n", - " cdef np.float32_t pi = np.pi\n", - " cdef np.float32_t dr = 0.0\n", - " cdef int x, y\n", - " \n", - " for x in range(dims[0]):\n", - " for y in range(dims[1]):\n", - " dr = ((x - center[0]) ** 2 + (y - center[1]) ** 2)**(0.5)\n", - " density[x, y] = np.sum(2. * (b - 1.) / (2. * pi * (r50 * a)**2) * (1. + (dr / (r50 * a))**2)**(-b) * w2D)\n", - " return density" - ], - "language": "python", - "metadata": {}, - "outputs": [], - "prompt_number": 5 - }, - { - "cell_type": "code", - "collapsed": false, - "input": [ - "%%cython\n", - "#Aggresively tunned Cython implementation\n", - "cimport cython\n", - "import numpy as np\n", - "cimport numpy as np\n", - "\n", - "@cython.boundscheck(False)\n", - "@cython.wraparound(False)\n", - "@cython.cdivision(True)\n", - "def cython_pdf_unrolled(np.ndarray[np.float32_t, ndim=2] density, \n", - " np.ndarray[np.long_t, ndim=1] dims, \n", - " np.ndarray[np.float_t, ndim=1] center, \n", - " np.ndarray[np.float32_t, ndim=2] w2D, \n", - " float r50, \n", - " float b, \n", - " float a):\n", - "\n", - " cdef float pi = np.pi\n", - " cdef int x,y,k,m\n", - " cdef float s,dr,d\n", - " cdef Py_ssize_t fac_x = 7\n", - " cdef Py_ssize_t fac_y = 7\n", - " cdef np.ndarray[np.float32_t, ndim=2] fac = np.zeros([fac_x, fac_y], dtype=np.float32)\n", - " cdef float w2D_fac = 2 * (b - 1) / (2 * pi * (r50 * a)**2)\n", - "\n", - " for k in range(fac_x):\n", - " for m in range(fac_y):\n", - " fac[k, m] = w2D[k, m] * w2D_fac\n", - "\n", - " for x in range(dims[0]):\n", - " for y in range(dims[1]):\n", - " dr = ((x - center[0]) ** 2 + (y - center[1]) ** 2)**(1./2)\n", - " d = (1 + (dr / (r50 * a))**2)**(-b)\n", - " \n", - " s = 0\n", - " for k in range(fac_x):\n", - " for m in range(fac_y):\n", - " s += fac[k, m] * d\n", - " density[x, y] = s\n", - " return density" - ], - "language": "python", - "metadata": {}, - "outputs": [], - "prompt_number": 6 - }, - { - "cell_type": "code", - "collapsed": false, - "input": [ - "hope.config.keeptemp= True\n", - "hope.config.optimize=True\n", - "hope_pdf = hope.jit(pdf) \n", - "numba_pdf = numba.jit(pdf, nopython=False)\n", - "\n", - "native_pdf_mod = load(\"pdf\")\n", - "native_pdf = native_pdf_mod.run" - ], - "language": "python", - "metadata": {}, - "outputs": [ - { - "output_type": "stream", - "stream": "stdout", - "text": [ - "running build_ext\n", - "building 'pdf' extension\n", - "C compiler: /usr/bin/clang -fno-strict-aliasing -fno-common -dynamic -pipe -Os -fwrapv -DNDEBUG -g -fwrapv -O3 -Wall -Wstrict-prototypes\n", - "\n", - "compile options: '-I/Users/jakeret/workspace/virtualenvs/hope_benchmarks/lib/python2.7/site-packages/numpy/core/include -I/opt/local/Library/Frameworks/Python.framework/Versions/2.7/include/python2.7 -c'\n", - "extra options: '-Wall -Wno-unused-variable -march=native -stdlib=libc++ -std=c++11'\n", - "clang: ././src/pdf.cpp\n", - "/usr/bin/clang++ -bundle -undefined dynamic_lookup -L/opt/local/lib -Wl,-headerpad_max_install_names -L/opt/local/lib/db48 ./src/pdf.o -o ./pdf.so\n", - "\n" - ] - } - ], - "prompt_number": 7 - }, - { - "cell_type": "code", - "collapsed": false, - "input": [ - "density = np.zeros((dims[0], dims[1]), dtype=np.float32)\n", - "pdf(density, dims, center, w2D, r50, b, a)\n", - "\n", - "hdensity = np.zeros((dims[0], dims[1]), dtype=np.float32)\n", - "hope_pdf(hdensity, dims, center, w2D, r50, b, a)\n", - "\n", - "ndensity = np.zeros((dims[0], dims[1]), dtype=np.float32)\n", - "numba_pdf(ndensity, dims, center, w2D, r50, b, a)\n", - "\n", - "cdensity = np.zeros((dims[0], dims[1]), dtype=np.float32)\n", - "cython_pdf(cdensity, dims, center, w2D, r50, b, a)\n", - "\n", - "c2density = np.zeros((dims[0], dims[1]), dtype=np.float32)\n", - "cython_pdf_opt(c2density, dims, center, w2D, r50, b, a)\n", - "\n", - "c3density = np.zeros((dims[0], dims[1]), dtype=np.float32)\n", - "cython_pdf_unrolled(c3density, dims, center, w2D, r50, b, a)\n", - "\n", - "nadensity = np.zeros((dims[0], dims[1]), dtype=np.float32)\n", - "native_pdf(nadensity, dims, center, w2D, r50, b, a)\n", - "\n", - "assert np.allclose(density, hdensity)\n", - "assert np.allclose(density, ndensity)\n", - "assert np.allclose(density, cdensity)\n", - "assert np.allclose(density, c2density)\n", - "assert np.allclose(density, c3density)\n", - "assert np.allclose(density, nadensity)\n" - ], - "language": "python", - "metadata": {}, - "outputs": [], - "prompt_number": 8 - }, - { - "cell_type": "code", - "collapsed": false, - "input": [ - "print \"Python\"\n", - "%timeit pdf(density, dims, center, w2D, r50, b, a)\n", - "print \"hope\"\n", - "%timeit hope_pdf(density, dims, center, w2D, r50, b, a)\n", - "print \"Numba\"\n", - "%timeit numba_pdf(density, dims, center, w2D, r50, b, a)\n", - "print \"Cython\"\n", - "%timeit cython_pdf(density, dims, center, w2D, r50, b, a)\n", - "print \"Cython opt\"\n", - "%timeit cython_pdf_opt(density, dims, center, w2D, r50, b, a)\n", - "print \"Cython unrolled\"\n", - "%timeit cython_pdf_unrolled(density, dims, center, w2D, r50, b, a)\n", - "print \"Native\"\n", - "%timeit native_pdf(density, dims, center, w2D, r50, b, a)\n" - ], - "language": "python", - "metadata": {}, - "outputs": [ - { - "output_type": "stream", - "stream": "stdout", - "text": [ - "Python\n", - "100 loops, best of 3: 10.8 ms per loop" - ] - }, - { - "output_type": "stream", - "stream": "stdout", - "text": [ - "\n", - "hope\n", - "10000 loops, best of 3: 106 \u00b5s per loop" - ] - }, - { - "output_type": "stream", - "stream": "stdout", - "text": [ - "\n", - "Numba\n", - "100 loops, best of 3: 11 ms per loop" - ] - }, - { - "output_type": "stream", - "stream": "stdout", - "text": [ - "\n", - "Cython\n", - "100 loops, best of 3: 7.15 ms per loop" - ] - }, - { - "output_type": "stream", - "stream": "stdout", - "text": [ - "\n", - "Cython opt\n", - "100 loops, best of 3: 2.56 ms per loop" - ] - }, - { - "output_type": "stream", - "stream": "stdout", - "text": [ - "\n", - "Cython unrolled\n", - "10000 loops, best of 3: 35.9 \u00b5s per loop" - ] - }, - { - "output_type": "stream", - "stream": "stdout", - "text": [ - "\n", - "Native\n", - "10000 loops, best of 3: 54.7 \u00b5s per loop" - ] - }, - { - "output_type": "stream", - "stream": "stdout", - "text": [ - "\n" - ] - } - ], - "prompt_number": 9 - }, + ] + } + ], + "source": [ + "hope.config.keeptemp= True\n", + "hope.config.optimize=True\n", + "hope_pdf = hope.jit(pdf) \n", + "numba_pdf = numba.jit(pdf, nopython=False)\n", + "\n", + "native_pdf_mod = load(\"pdf\")\n", + "native_pdf = native_pdf_mod.run" + ] + }, + { + "cell_type": "code", + "execution_count": 8, + "metadata": {}, + "outputs": [], + "source": [ + "density = np.zeros((dims[0], dims[1]), dtype=np.float32)\n", + "pdf(density, dims, center, w2D, r50, b, a)\n", + "\n", + "hdensity = np.zeros((dims[0], dims[1]), dtype=np.float32)\n", + "hope_pdf(hdensity, dims, center, w2D, r50, b, a)\n", + "\n", + "ndensity = np.zeros((dims[0], dims[1]), dtype=np.float32)\n", + "numba_pdf(ndensity, dims, center, w2D, r50, b, a)\n", + "\n", + "cdensity = np.zeros((dims[0], dims[1]), dtype=np.float32)\n", + "cython_pdf(cdensity, dims, center, w2D, r50, b, a)\n", + "\n", + "c2density = np.zeros((dims[0], dims[1]), dtype=np.float32)\n", + "cython_pdf_opt(c2density, dims, center, w2D, r50, b, a)\n", + "\n", + "c3density = np.zeros((dims[0], dims[1]), dtype=np.float32)\n", + "cython_pdf_unrolled(c3density, dims, center, w2D, r50, b, a)\n", + "\n", + "nadensity = np.zeros((dims[0], dims[1]), dtype=np.float32)\n", + "native_pdf(nadensity, dims, center, w2D, r50, b, a)\n", + "\n", + "assert np.allclose(density, hdensity)\n", + "assert np.allclose(density, ndensity)\n", + "assert np.allclose(density, cdensity)\n", + "assert np.allclose(density, c2density)\n", + "assert np.allclose(density, c3density)\n", + "assert np.allclose(density, nadensity)\n" + ] + }, + { + "cell_type": "code", + "execution_count": 9, + "metadata": {}, + "outputs": [ { - "cell_type": "code", - "collapsed": false, - "input": [ - "funcs = [\"pdf\", \n", - " \"numba_pdf\", \n", - " \"hope_pdf\", \n", - " \"cython_pdf\", \n", - " \"cython_pdf_opt\", \n", - " #\"cython_pdf_unrolled\",\n", - " \"native_pdf\", \n", - " ]\n", - "perf_comp_data(funcs, \n", - " len(funcs)*[\"density, dims, center, w2D, r50, b, a\"], rep=100)" - ], - "language": "python", - "metadata": {}, - "outputs": [ - { - "output_type": "stream", - "stream": "stdout", - "text": [ - "function: native_pdf , av. time sec: 0.00005007, min. time sec: 0.00004888, relative: 1.0\n", - "function: hope_pdf , av. time sec: 0.00010014, min. time sec: 0.00009894, relative: 2.0\n", - "function: cython_pdf_opt , av. time sec: 0.00257397, min. time sec: 0.00239992, relative: 51.4\n", - "function: cython_pdf , av. time sec: 0.00727844, min. time sec: 0.00702214, relative: 145.4\n", - "function: pdf , av. time sec: 0.01095247, min. time sec: 0.01038003, relative: 218.8\n", - "function: numba_pdf , av. time sec: 0.01107156, min. time sec: 0.01068902, relative: 221.1\n" - ] - } - ], - "prompt_number": 10 - }, + "name": "stdout", + "output_type": "stream", + "text": [ + "Python\n", + "100 loops, best of 3: 10.8 ms per loop\n", + "hope\n", + "10000 loops, best of 3: 106 µs per loop\n", + "Numba\n", + "100 loops, best of 3: 11 ms per loop\n", + "Cython\n", + "100 loops, best of 3: 7.15 ms per loop\n", + "Cython opt\n", + "100 loops, best of 3: 2.56 ms per loop\n", + "Cython unrolled\n", + "10000 loops, best of 3: 35.9 µs per loop\n", + "Native\n", + "10000 loops, best of 3: 54.7 µs per loop\n" + ] + } + ], + "source": [ + "print \"Python\"\n", + "%timeit pdf(density, dims, center, w2D, r50, b, a)\n", + "print \"hope\"\n", + "%timeit hope_pdf(density, dims, center, w2D, r50, b, a)\n", + "print \"Numba\"\n", + "%timeit numba_pdf(density, dims, center, w2D, r50, b, a)\n", + "print \"Cython\"\n", + "%timeit cython_pdf(density, dims, center, w2D, r50, b, a)\n", + "print \"Cython opt\"\n", + "%timeit cython_pdf_opt(density, dims, center, w2D, r50, b, a)\n", + "print \"Cython unrolled\"\n", + "%timeit cython_pdf_unrolled(density, dims, center, w2D, r50, b, a)\n", + "print \"Native\"\n", + "%timeit native_pdf(density, dims, center, w2D, r50, b, a)\n" + ] + }, + { + "cell_type": "code", + "execution_count": 10, + "metadata": {}, + "outputs": [ { - "cell_type": "code", - "collapsed": false, - "input": [ - "from matplotlib import pyplot as plt\n", - "from mpl_toolkits.mplot3d import Axes3D\n", - "from matplotlib import cm\n", - "from matplotlib.ticker import LinearLocator, FormatStrFormatter\n", - "\n", - "density = np.zeros((dims[0], dims[1]), dtype=np.float32)\n", - "pdf(density, dims, center, w2D, r50, b, a)\n", - "\n", - "fig = plt.figure()\n", - "ax = fig.gca(projection='3d')\n", - "gx, gy = np.mgrid[0:dims[0], 0:dims[1]]\n", - "surf = ax.plot_surface(gx, gy, density, rstride=1, cstride=1, cmap=cm.coolwarm, linewidth=0, antialiased=False)\n", - "fig.colorbar(surf, shrink=0.5, aspect=5)\n", - "plt.show()" - ], - "language": "python", - "metadata": {}, - "outputs": [ - { - "metadata": {}, - "output_type": "display_data", - "png": "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\n", - "text": [ - "" - ] - } - ], - "prompt_number": 11 - }, + "name": "stdout", + "output_type": "stream", + "text": [ + "function: native_pdf , av. time sec: 0.00005007, min. time sec: 0.00004888, relative: 1.0\n", + "function: hope_pdf , av. time sec: 0.00010014, min. time sec: 0.00009894, relative: 2.0\n", + "function: cython_pdf_opt , av. time sec: 0.00257397, min. time sec: 0.00239992, relative: 51.4\n", + "function: cython_pdf , av. time sec: 0.00727844, min. time sec: 0.00702214, relative: 145.4\n", + "function: pdf , av. time sec: 0.01095247, min. time sec: 0.01038003, relative: 218.8\n", + "function: numba_pdf , av. time sec: 0.01107156, min. time sec: 0.01068902, relative: 221.1\n" + ] + } + ], + "source": [ + "funcs = [\"pdf\", \n", + " \"numba_pdf\", \n", + " \"hope_pdf\", \n", + " \"cython_pdf\", \n", + " \"cython_pdf_opt\", \n", + " #\"cython_pdf_unrolled\",\n", + " \"native_pdf\", \n", + " ]\n", + "perf_comp_data(funcs, \n", + " len(funcs)*[\"density, dims, center, w2D, r50, b, a\"], rep=100)" + ] + }, + { + "cell_type": "code", + "execution_count": 11, + "metadata": {}, + "outputs": [ { - "cell_type": "code", - "collapsed": false, - "input": [], - "language": "python", + "data": { + "image/png": "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\n", + "text/plain": [ + "" + ] + }, "metadata": {}, - "outputs": [], - "prompt_number": 9 + "output_type": "display_data" } ], - "metadata": {} + "source": [ + "from matplotlib import pyplot as plt\n", + "from mpl_toolkits.mplot3d import Axes3D\n", + "from matplotlib import cm\n", + "from matplotlib.ticker import LinearLocator, FormatStrFormatter\n", + "\n", + "density = np.zeros((dims[0], dims[1]), dtype=np.float32)\n", + "pdf(density, dims, center, w2D, r50, b, a)\n", + "\n", + "fig = plt.figure()\n", + "ax = fig.gca(projection='3d')\n", + "gx, gy = np.mgrid[0:dims[0], 0:dims[1]]\n", + "surf = ax.plot_surface(gx, gy, density, rstride=1, cstride=1, cmap=cm.coolwarm, linewidth=0, antialiased=False)\n", + "fig.colorbar(surf, shrink=0.5, aspect=5)\n", + "plt.show()" + ] + }, + { + "cell_type": "code", + "execution_count": 9, + "metadata": {}, + "outputs": [], + "source": [] } - ] -} \ No newline at end of file + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.5.1" + } + }, + "nbformat": 4, + "nbformat_minor": 1 +} diff --git a/examples/hope_numpy.py b/examples/hope_numpy.py index 1cce654..3e6b2c1 100644 --- a/examples/hope_numpy.py +++ b/examples/hope_numpy.py @@ -69,4 +69,4 @@ def example(): assert np.all(r == [1.25, 1.5, 1.75]) if __name__ == '__main__': - example() \ No newline at end of file + example() From a1d20dac845af51accdb30f420d0dbf1da396632 Mon Sep 17 00:00:00 2001 From: Uwe Date: Tue, 29 Aug 2017 15:23:43 +0200 Subject: [PATCH 08/48] added missing method for python 3 ast in _transformer.py --- hope/_transformer.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/hope/_transformer.py b/hope/_transformer.py index 593fddc..7efb962 100644 --- a/hope/_transformer.py +++ b/hope/_transformer.py @@ -179,6 +179,8 @@ def visit_Num(self, node): return True def visit_Name(self, node): return True + def visit_NameConstant(self, node): + return True def visit_Slice(self, node): return node.lower if node.lower is None else self.visit(node.lower) and node.upper if node.upper is None else self.visit(node.upper) def visit_ExtSlice(self, node): From 9f9bf55b313fdab2052517ea77c93a72291c99cc Mon Sep 17 00:00:00 2001 From: Uwe Date: Tue, 29 Aug 2017 15:24:09 +0200 Subject: [PATCH 09/48] modified c++ options to surpress warnings about unreachable code --- hope/._transformer.py.swp | Bin 0 -> 53248 bytes hope/.options.py.swo | Bin 0 -> 16384 bytes hope/.options.py.swp | Bin 0 -> 20480 bytes hope/options.py | 10 ++++++---- tests/test_control_structures.py | 26 ++++++++++++++++++++++---- 5 files changed, 28 insertions(+), 8 deletions(-) create mode 100644 hope/._transformer.py.swp create mode 100644 hope/.options.py.swo create mode 100644 hope/.options.py.swp diff --git a/hope/._transformer.py.swp b/hope/._transformer.py.swp new file mode 100644 index 0000000000000000000000000000000000000000..a6178a876dda428d3c25011f859aa24931770ed2 GIT binary patch literal 53248 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 U`iI5IpRPVV&qkJtpDAnXKZjiY`~Uy| literal 0 HcmV?d00001 diff --git a/hope/.options.py.swp b/hope/.options.py.swp new file mode 100644 index 0000000000000000000000000000000000000000..ea672ecc6188ec393a97aae359dd14a5445370e5 GIT binary patch literal 20480 zcmeHN&5s;M6|Xn}hlJn=Zjgw#y|O&R&a`*ecCc0+3u|}TELnRkuh$=0tl8e~nwesI zx`(dn-5FbPq`(~saRf*F14u~RI3PtJ!40AaIB-A+B>n&{oWifFKjv#~HW4u(RY|{@ z>8e-nz54a5S5-Z|H-BsWDqVEv8OqCyz5m<~?|%Q!Uw-z&ml#VAx%4|-p_FcXeAyFm zSeFC2=hr*Eem#i&{rb83!i9@2SDpK=-^sf^94b6}J&C`?eI?I!;vUaF;+_(5B;DTN z@uqBxWxz7ANcYAe{pVq_Y`A~fH#0|0PDcZz$xGv;K!e1tPgw< z_{U3(eGj+*oC6ktSAge%lfYj-3thl>fm^^V@FMU6@b?!P`z`Qm;BDX`&;@3IzrMiO zpMZCPN5GGO`+x`B1x^7cf!}-vx`FQi?*Ny9uK@pip0W3UN5Dg%37iE^03V!W>}S9` zz-?d-m;rwIX~up5`~Wxrwt-iHSAbuAim?#b2U>s&RDloiaO4Bv=Ku!+U=jEb4@Le4 z{1jLNUIl)S2Pkbo^Zf_SIej8!N^Rl^$!R;1X|ES2iU-SawYKdJ{o0s2qE*VzG-yjD z!dJI4JC(KN<>vCnm1}FwTdOy=);F(JmuQxPI8v02z>A0%9uqVPC8_QE)Jx*N-~sue z7qv@sBc?7*SlZgYetq-C&DE8{($-GtbL4gTz3Nh_reI;xTbVahOUa^!8lY(pPWfC) zyaB`g{bro1UaH2Kp*cE3^&su`a#d$)Bs)9|8#j}bSKWa7alk88H<93}R~cGrC2^Mm zAyukGDBZ$E(bczie5|E+dE?MC(*lylMouE4X?EOwK9CiV67Uu^vk~P+heOiD)rQ*i zy+;pYq44E2;bZo2E?FWm-q_uO(w+HxnXAL8UNkke zRYZz|0Ir83O8d^V&n*!J6)oXZ=`z(9h=-WSV=9bWlY=!=b!;g44a9s>$v{MHQgJbt zsekdc=4GO z>5TRU^!n!YRfkR!%tNe_N13L4m<`I{*m3;1Ip-b*FjA%ORK|dIX{15dA5+xmn3Byy zBvZ~q-0;FnYBXqL{o3}O<{PVbkL#$(l*e`T3Br$Yw(Au>BI|{o z($k!Z6lM5ln!@o!HzJS)#St=Y89asbbTA&(%`ZbT_hK|IP2MxH*zcP>p>yG^2^eIb zZ>J*Rl2m6nHTCq$6=rKGf@NWWTHHgbf-H}`y^sg_79ijuq)ov4xdE(4W7jS+0q<9w zddrhC_Jya!0k0!7cARP!6dy?mw5YM(j^h|sR)o}^nZcAota})_RLT%}A z9@)j92xT)02b3av!drai*}g@CI3>OC&|$|@=E{QyL5v(ZW5%I7r_!X%Vb2=v?nYj; zyKAV~-4#(w|3*)}Ny5_K^AIlBlIWj22?rS`u1kkr5<%HgzA~Roki9p+qs#? zldVTNUEX5Tb-HF`(ORObn9zFbmT%hF^!5SS?15Ruri5#3qBlgH^X^yOd52AH2eYh`xqgRl+h{~E7Pa8uwm9> zHq2DIa>YvyMdUD+F)}uO#N%x*3>_VUx1+e8MhLT@jwMRyz&G_$1r3t|Mx+w%`5i4$ z*W0`zWRX!ZF@0xA$N5Lp+m(`^cNd>vV|+7S_&e!ICgljMTE}yv>m`1t5t*b^j5ic} z4M4Awnwb#>CWGqVxREVCpNJ#n3XOaG0OvZrt=8|PyX7$hJB4gh{iM?qa+T@YY_?LJ z-%S1%EfLT%*h*V}7Ih6h5QscPiy$v8|EZ)W;y$bLe{K@*!Po_Ha`a+a3k-XIa} zj-rZRrN#M$bF_N%b$TY}fAbe%1h0n318z%pPNunbrRECZGS%YbFT zGGH073_Ov6qaU0-z1r!gaFc&=R?YeUC-@KleL$c8i}(8vaQ4@Azti=$SOzQumI2Fv rWxz6E8L$jk1}p=X0n318;D3&Rr+2c%tkM- literal 0 HcmV?d00001 diff --git a/hope/options.py b/hope/options.py index 0c8758c..df039b3 100644 --- a/hope/options.py +++ b/hope/options.py @@ -6,10 +6,12 @@ from hope.exceptions import UnsupportedCompilerException CXX_FLAGS = { - "clang": ["-Wall", "-Wno-unused-variable", "-march=native", "-stdlib=libc++", "-std=c++11"], - "icc": ["-Wall", "-Wno-unused-variable", "-march=native", "-stdlib=libc++", "-std=c++11"], - "gcc-mac": ["-Wall", "-Wno-unused-variable", "-std=c++11", "-msse4.2"], - "gcc-linux": ["-Wall", "-Wno-unused-variable", "-std=c++11"] + "clang": ["-Wall", "-Wno-unused-variable", "-march=native", "-stdlib=libc++", "-std=c++11", + "-Wno-unreachable-code"], + "icc": ["-Wall", "-Wno-unused-variable", "-march=native", "-stdlib=libc++", "-std=c++11", + "-Wno-unreachable-code"], + "gcc-mac": ["-Wall", "-Wno-unused-variable", "-std=c++11", "-msse4.2", "-Wno-unreachable-code"], + "gcc-linux": ["-Wall", "-Wno-unused-variable", "-std=c++11", "-Wno-unreachable-code"] } DARWIN_KEY = "Darwin" diff --git a/tests/test_control_structures.py b/tests/test_control_structures.py index 2c469d8..8f9bd78 100644 --- a/tests/test_control_structures.py +++ b/tests/test_control_structures.py @@ -82,18 +82,18 @@ def fkt(a, b, size_x): n = 2 for dx in range(2 * n + 1): b[n + 1:(size_x - n - 1)] += a[dx + 1:(size_x + dx - 2 * n - 1)] - + hfkt = hope.jit(fkt) size_x = 10 ao, ah = random(dtype, [size_x]) ao = (ao / (2 * size_x + 1)).astype(dtype) ah = (ah / (2 * size_x + 1)).astype(dtype) - + bo, bh = np.zeros((size_x), dtype), np.zeros((size_x), dtype) - + fkt(ao, bo, size_x) hfkt(ah, bh, size_x) - + assert check(bo, bh) @@ -109,4 +109,22 @@ def fkt(): ro, rh = fkt(), hfkt() assert check(ro, rh) + +def test_if_const(): + def fkt(): + if True: + a = 1 + else: + a = 2 + if False: + b = 7 + else: + b = 2 + return a + b + + hfkt = hope.jit(fkt) + ro, rh = fkt(), hfkt() + assert check(ro, rh) + + # TODO: make test for for/wile with array and for/else wihle/else From 3f4b3fd7cd3d1ed23ce1bfd69fc98be11d6ae362 Mon Sep 17 00:00:00 2001 From: Uwe Date: Tue, 29 Aug 2017 17:50:21 +0200 Subject: [PATCH 10/48] improved .gitignore --- .gitignore | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/.gitignore b/.gitignore index 01dfddb..0df7ce3 100644 --- a/.gitignore +++ b/.gitignore @@ -51,3 +51,10 @@ htmlcov/ /.cache/ /.ipynb_checkpoints/ /.settings/ + +**/.*.sw? +venv* +sandbox + +**/.DS_Store +.python-version From b106a14764f4ea1ce2328d88d2af543df8f94fe1 Mon Sep 17 00:00:00 2001 From: Uwe Date: Tue, 29 Aug 2017 17:54:06 +0200 Subject: [PATCH 11/48] fixed test_control_structures.py for python 3 --- tests/test_control_structures.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/tests/test_control_structures.py b/tests/test_control_structures.py index 8f9bd78..b26686b 100644 --- a/tests/test_control_structures.py +++ b/tests/test_control_structures.py @@ -44,9 +44,10 @@ def fkt(a, b, c): ro, rh = fkt(ao, bo, co), hfkt(ah, bh, ch) assert check(co, ch) -@pytest.mark.skipif("sys.version_info[0] > 2") @pytest.mark.parametrize("dtype", dtypes) def test_for_xrange_1(dtype): + if sys.version_info.major >= 3: + xrange = range def fkt(a, b, c): for i in xrange(10): c[:, i] = a[i, :] From 80a65af0ca7d3401e446efa08d18011f322dfd71 Mon Sep 17 00:00:00 2001 From: Uwe Date: Tue, 29 Aug 2017 17:57:24 +0200 Subject: [PATCH 12/48] backup of benchmarks folder for python 3 transition --- benchmarks/HPC Python.ipynb | 933 ++-- benchmarks/compile | 1 + benchmarks/fibonacci.ipynb | 388 +- ...c3388e38604df736dff187257d5d6ae4c4d3ea.pck | Bin 0 -> 486 bytes ...388e38604df736dff187257d5d6ae4c4d3ea_0.cpp | 157 + ...c3388e38604df736dff187257d5d6ae4c4d3ea_0.o | Bin 0 -> 334924 bytes ...388e38604df736dff187257d5d6ae4c4d3ea_0.out | 8 + ...371d6a5362b67ac5531087a5dd1885eaba960b.pck | Bin 0 -> 490 bytes ...1d6a5362b67ac5531087a5dd1885eaba960b_0.cpp | 150 + ...371d6a5362b67ac5531087a5dd1885eaba960b_0.o | Bin 0 -> 334120 bytes ...1d6a5362b67ac5531087a5dd1885eaba960b_0.out | 8 + ...a460735cef49c69a69c967957f695741c0feae.pck | Bin 0 -> 498 bytes ...60735cef49c69a69c967957f695741c0feae_0.cpp | 152 + ...a460735cef49c69a69c967957f695741c0feae_0.o | Bin 0 -> 334220 bytes ...60735cef49c69a69c967957f695741c0feae_0.out | 8 + ...37089ed771c5f43e2e28f5e25a2c4f0ec12559.pck | Bin 0 -> 504 bytes ...089ed771c5f43e2e28f5e25a2c4f0ec12559_0.cpp | 205 + ...37089ed771c5f43e2e28f5e25a2c4f0ec12559_0.o | Bin 0 -> 383220 bytes ...089ed771c5f43e2e28f5e25a2c4f0ec12559_0.out | 8 + benchmarks/julialang.org.ipynb | 1394 +++-- benchmarks/native_cpp_gen.ipynb | 4593 +++++++++-------- benchmarks/numexpr.ipynb | 1215 ++--- benchmarks/pairwise.ipynb | 721 ++- benchmarks/simplify.ipynb | 638 ++- benchmarks/star.ipynb | 926 ++-- 25 files changed, 5948 insertions(+), 5557 deletions(-) create mode 100644 benchmarks/compile create mode 100644 benchmarks/hope/fib_181039868ed022600dc3388e38604df736dff187257d5d6ae4c4d3ea.pck create mode 100644 benchmarks/hope/fib_181039868ed022600dc3388e38604df736dff187257d5d6ae4c4d3ea_0.cpp create mode 100644 benchmarks/hope/fib_181039868ed022600dc3388e38604df736dff187257d5d6ae4c4d3ea_0.o create mode 100644 benchmarks/hope/fib_181039868ed022600dc3388e38604df736dff187257d5d6ae4c4d3ea_0.out create mode 100644 benchmarks/hope/pisum_49f15c96f2c4d5ce51371d6a5362b67ac5531087a5dd1885eaba960b.pck create mode 100644 benchmarks/hope/pisum_49f15c96f2c4d5ce51371d6a5362b67ac5531087a5dd1885eaba960b_0.cpp create mode 100644 benchmarks/hope/pisum_49f15c96f2c4d5ce51371d6a5362b67ac5531087a5dd1885eaba960b_0.o create mode 100644 benchmarks/hope/pisum_49f15c96f2c4d5ce51371d6a5362b67ac5531087a5dd1885eaba960b_0.out create mode 100644 benchmarks/hope/pisum_opt_d21ddf605a07e3a4d8a460735cef49c69a69c967957f695741c0feae.pck create mode 100644 benchmarks/hope/pisum_opt_d21ddf605a07e3a4d8a460735cef49c69a69c967957f695741c0feae_0.cpp create mode 100644 benchmarks/hope/pisum_opt_d21ddf605a07e3a4d8a460735cef49c69a69c967957f695741c0feae_0.o create mode 100644 benchmarks/hope/pisum_opt_d21ddf605a07e3a4d8a460735cef49c69a69c967957f695741c0feae_0.out create mode 100644 benchmarks/hope/qsort_kernel_674d9af4cba0defec637089ed771c5f43e2e28f5e25a2c4f0ec12559.pck create mode 100644 benchmarks/hope/qsort_kernel_674d9af4cba0defec637089ed771c5f43e2e28f5e25a2c4f0ec12559_0.cpp create mode 100644 benchmarks/hope/qsort_kernel_674d9af4cba0defec637089ed771c5f43e2e28f5e25a2c4f0ec12559_0.o create mode 100644 benchmarks/hope/qsort_kernel_674d9af4cba0defec637089ed771c5f43e2e28f5e25a2c4f0ec12559_0.out diff --git a/benchmarks/HPC Python.ipynb b/benchmarks/HPC Python.ipynb index 37420b8..d751889 100644 --- a/benchmarks/HPC Python.ipynb +++ b/benchmarks/HPC Python.ipynb @@ -1,535 +1,442 @@ { - "metadata": { - "name": "", - "signature": "sha256:bc54c0a983314fa977f09e4ffb2a2c91ee6f84fd2477d2ff297f27cc2b72d428" - }, - "nbformat": 3, - "nbformat_minor": 0, - "worksheets": [ + "cells": [ { - "cells": [ - { - "cell_type": "code", - "collapsed": false, - "input": [ - "import hope\n", - "hope.config.optimize = True\n", - "hope.config.keeptemp = True\n", - "import numpy as np\n", - "from matplotlib import pyplot as plt\n", - "%pylab inline" - ], - "language": "python", - "metadata": { - "slideshow": { - "slide_type": "slide" - } - }, - "outputs": [ - { - "output_type": "stream", - "stream": "stdout", - "text": [ - "Populating the interactive namespace from numpy and matplotlib\n" - ] - } - ], - "prompt_number": 1 - }, + "cell_type": "code", + "execution_count": 1, + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "outputs": [ { - "cell_type": "heading", - "level": 1, - "metadata": { - "slideshow": { - "slide_type": "slide" - } - }, - "source": [ - "Python Optimizations" + "name": "stdout", + "output_type": "stream", + "text": [ + "Populating the interactive namespace from numpy and matplotlib\n" ] - }, + } + ], + "source": [ + "import hope\n", + "hope.config.optimize = True\n", + "hope.config.keeptemp = True\n", + "import numpy as np\n", + "from matplotlib import pyplot as plt\n", + "import warnings\n", + "# warnings.filterwarnings(\"ignore\", category=UserWarning) \n", + "\n", + "\n", + "%pylab inline" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "source": [ + "# Python Optimizations" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Avoid memory allocations" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "**zombie apocalypse modeling** from http://wiki.scipy.org/Cookbook/Zombie_Apocalypse_ODEINT" + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "metadata": { + "collapsed": true + }, + "outputs": [], + "source": [ + "from scipy.integrate import odeint\n", + "\n", + "P, d, B, G, A = 0, 0.0001, 0.0095, 0.0001, 0.0001\n", + "\n", + "def f_nat(y, t):\n", + " dy = np.empty(3)\n", + " dy[0] = P - B*y[0]*y[1] - d*y[0]\n", + " dy[1] = B*y[0]*y[1] + G*y[2] - A*y[0]*y[1]\n", + " dy[2] = d*y[0] + A*y[0]*y[1] - G*y[2]\n", + " return dy\n", + "\n", + "y0, t = np.array([500., .001, 0.]), np.linspace(0, 5., 1000)" + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "metadata": {}, + "outputs": [ { - "cell_type": "heading", - "level": 2, + "data": { + "image/png": "iVBORw0KGgoAAAANSUhEUgAAAYgAAAEKCAYAAAAIO8L1AAAABHNCSVQICAgIfAhkiAAAAAlwSFlz\nAAALEgAACxIB0t1+/AAAIABJREFUeJzt3Xl8FdXZwPHfc29WICGQhQTCEpKw74QAAoJoFakVV6hb\n1brVvq34ttraxba29X27qLW1b4u2WvetUiuuQCUoIFvYd0jClrAnQIDsuef9YyYY4EJuknvvJLnP\n9/OZz8yc2Z4bSJ47Z86cI8YYlFJKqbO5nA5AKaVUy6QJQimllFeaIJRSSnmlCUIppZRXmiCUUkp5\npQlCKaWUV5oglFJKeaUJQimllFeaIJRSSnkV5nQAzZGQkGB69erldBhKKdWqrFq16ogxJrGh/Vp1\ngujVqxe5ublOh6GUUq2KiOz2ZT+tYlJKKeWVJgillFJeaYJQSinllSYIpZRSXmmCUEop5VVAE4SI\n7BKRDSKyVkRy7bLOIjJfRHbY8052uYjIn0QkT0TWi8iIQMamlFLqwoJxB3GJMWaYMSbLXn8E+NQY\nkwl8aq8DXAlk2tO9wF+DEJtSSqnzcOI9iGnAJHv5JWAh8EO7/GVjjYG6TETiRCTFGLPf3wGs3FXC\nou2HrRURa8YZq8jpkvplZ63Ll/t4O/bs4y60z5fbzxfPudc85/wNfJbzfY76+9SfiQhhLsHtsuZh\nbtc563XLbpcQXm89IsxFdLib6Ag3kWGuc35WSqmWL9AJwgDzRMQAzxpjngO61PujfwDoYi93A/bW\nO7bQLjsjQYjIvVh3GPTo0aNJQa3efZRncvLQ4biDpy5ZRIe7iQp3ER3hpn1EGHHtwomLjrDm7ex5\ndDiJMZGkxEWTFBNJuFsflSnlhEAniPHGmCIRSQLmi8jW+huNMcZOHj6zk8xzAFlZWU36E3/fxHTu\nm5ju7dz2vF7Z2dtOr9dtP/cYb9vPLLvwOTnPuetfoqF4OOda54/3nHjMl8fUGkNNrYcaj6HWY+y5\nh+raC69XVnsor66lvLqWiqra08vlVR4q7OWTFTXsOlLGsfJjHC2rpqrGw9lcAokxkSR3jKZ7p2gy\nkjqQntiBjKQOpCW0Jyrcfc4xSp3BGKithpoKa+6psSZTay+fZ15/O8b+xTD2L0z99abMOXf9zKDP\n/QxnS82ChEy//IjOJ6AJwhhTZM8Pici7QDZwsK7qSERSgEP27kVA93qHp9plQXO6esdrbYhWkQSS\nMYaKag/Hyqs4eqqawycr2X+snH3HKzhwvJz9xytYX3icDzfsP/274nYJ/VNiGNY9juHdOzGyZyd6\nxrfT6qy2pLYGTh2Gkwfh5CE4dQgqjttTKVSWfrledRJqKqG63JrX1M0rwJz75aPV++pTrTdBiEh7\nwGWMOWEvXw78EpgD3A78xp6/Zx8yB/iOiLwJjAaOB+L5g2qZRMSqgoqIJqVj9Hn3q6iuZeeRU+Qd\nOsnWA6Ws3XuMf6/Zx6vL9gDQvXM0E/skMqlPEuMzE/QOozUoPwoHN0NJARzdac1LdkJpEZw6wjnf\nputExEBUR4iKhchYaBcP4dEQFvXlFB515ro7AlxucIXZk/vLudQvd325LC5A7G+O3uY0sN2XuRfn\nlJ+13i6+CT/sxhHj9fbGDycW6Q28a6+GAa8bYx4XkXjgbaAHsBuYbowpEetr35+BKUAZcKcx5oI9\n8WVlZRntrE95PIb8wydZtrOEz7Yd5ov8I5RV1RITGcbUwSlcM7wbo9M643LpnYXjqiugcCXsWQr7\n18GB9XBsz5fbXWEQ1wM6pUFcd+iQDB2SoEMXe0qEqDiIjLH+qKsmEZFV9VqWnn+/QCWIYNAEobyp\nrKlleUEJ763dxycb93Oqqpbeie25a3wa1w1PJTpC/7AE1eHtsPV9yFtgJYfaSkAgPh2SB0PyEGse\nnwEdu4O7VXcy3SpoglAKKK+q5eON+3lhyU42FpXSuX0E909M57axPbX6KZCOF8Ha12DDO3Bkm1WW\nPATSLoZe46HHWIiOczbGEKYJQql6jDGs2FnCn3PyWLTjCF07RvHDK/tx9dCu+lDbX4yBgoWw7C+Q\n9x/rwXCvCdD/aug3FTqmOh2hsmmCUOo8vsg7wv9+vJUNRceZ2CeRX18ziO6d2zkdVutlDGz7GBY9\nAUWrrGcFI74Bw2+FTr2cjk55oQlCqQuo9RheXrqL38+1qj/+59rBXDO8m7NBtUb718Mnj8DuJVYy\nGPcgDLsZwiKdjkxdgK8JQp8GqZDkdgl3jkvj8oHJ/Peba3nwrbUs31nMz782UJ9N+KKqDD59DJY/\nC+06W23yR9yuD5jbGP3XVCGtW1w0r98zmifnb+evC/PZeuAEz98+is7tI5wOreXauxLevQ9K8iH7\nXrjkJ/rAuY3STm5UyAtzu/jhlH7MunUEm/eVct1flrC7+JTTYbU8xsCKv8E/pkBtFdz+Pkz9vSaH\nNkwThFK2KYNSeP2e0Rwvr2bGs8s0SdRXUwlzvgMfPQQZl8H9S6wmq6pN0wShVD0je3bm9XvGUFlT\ny03PLWNvSZnTITmv6hS8Ph3WvAoXPwxff8Pq4kK1eZoglDpL/5RYXr17NKeqarn1+eWUnKpyOiTn\nlB+Dl6+BnZ/DNX+FyT+1+ilSIUH/pZXyYmDXjvzjzlEcOF7BvS/nUlFd63RIwVd5El69HvatgRtf\nspqvqpCiCUKp8xjRoxNPTR9G7u6j/HD2elrzO0ONVlMFb98G+1bDjS/CgKudjkg5QBOEUhfw1SEp\nPHR5H95bu49Xl+9p+IC2wBiY813IXwBXPwP9r3I6IuUQTRBKNeDbkzKY1DeRX72/mY1Fx50OJ/CW\n/h+sf9N6v2H4rU5HoxykCUKpBrhcwlPTh9G5fQTfeX01ZVU1TocUOPk5MP9Rq4O9ix92OhrlME0Q\nSvmgc/sInv76MHYVl53uv6nNOXEQZt8NCX2tFkvay23I0wShlI/G9I7nG2N78uIXu8jdVeJ0OP5l\njPUiXNVJ66F0ZAenI1ItgCYIpRrhh1P60bVjND94Z33bavqa+zzsmAdf+RUk9XM6GtVCaIJQqhHa\nR4bxv9cNpuDIKZ5fvNPpcPzj6G6Y9yikXwrZ9zgdjWpBNEEo1UgX90nkioFd+L+cPA4cr3A6nOYx\nBj56GBC4+k/63EGdQROEUk3wk6kDqPEYfvvJVqdDaZ4t78OOuXDJj3VIUHUOTRBKNUGP+HbcMyGN\nd9cUsWbPUafDaZrKk/DxD6HLYBj9LaejUS2QJgilmujbkzKIbx/Bk/O2Ox1K0yz9M5zYB199UkeC\nU15pglCqidpHhnH/pHQW5x1haX6x0+E0zomDsORP1gtxPUY7HY1qoTRBKNUMt47pSVJMJE/N39a6\nOvNb+L9QWwmX/cLpSFQLpglCqWaICnfz3ckZrNx1lEU7jjgdjm+O5MHqlyHrLohPdzoa1YJpglCq\nmWaM6kFybBR/XZjvdCi+WfQEuCO0ryXVIE0QSjVTRJiLu8ansbSgmHV7jzkdzoWVFMD6tyHrm9Ah\n0eloVAunCUIpP/h6dndiosJ49vMWfhex6ClwhcG4B5yORLUCmiCU8oOYqHBuG9OTjzceYNeRU06H\n492xvbDuDRh5O8QkOx2NagUCniBExC0ia0TkA3s9TUSWi0ieiLwlIhF2eaS9nmdv7xXo2JTypzvG\n9SLc5eKFJS20j6bls6z5uJnOxqFajWDcQcwEttRb/y3wB2NMBnAUuMsuvws4apf/wd5PqVYjKSaK\nq4ak8K/VRZysbGGDClWesFouDbhGu9RQPgtoghCRVOCrwN/tdQEmA+/Yu7wEXGMvT7PXsbdfau+v\nVKtx29ienKys4d01RU6Hcqa1b0BlKYy53+lIVCsS6DuIp4EfAB57PR44Zoyp+3pVCHSzl7sBewHs\n7cft/ZVqNYZ1j2NQt1heXbq75bw45/FY1UupoyA1y+loVCsSsAQhIlcBh4wxq/x83ntFJFdEcg8f\nPuzPUyvVbCLCbWN6su3gCVbuaiGd+OXNh5J87ZBPNVog7yDGAVeLyC7gTayqpT8CcSJS1zNYKlB3\nL14EdAewt3cEzungxhjznDEmyxiTlZio7bhVy3P10G7ERoXxyrLdTodiWfZXiOkKA6Y5HYlqZQKW\nIIwxPzLGpBpjegFfBxYYY24BcoAb7N1uB96zl+fY69jbF5gWc4+ulO+iI9xcO7wbczcd4Hh5tbPB\nHN0FBTkw8g5whzsbi2p1nHgP4ofA90QkD+sZw/N2+fNAvF3+PeARB2JTyi9uGNmdqhoPH6zf52wg\na14DBIbf4mwcqlUKSifwxpiFwEJ7uQDI9rJPBXBjMOJRKtAGdYulb5cY3llVyC2jezoThKcW1rwK\nGZdp01bVJPomtVIBICLcMDKVNXuOkXfopDNB5H1qDQg04hvOXF+1epoglAqQacO74nYJs1cXOhPA\n6pegfSL0meLM9VWrpwlCqQBJioliUp9E/rW6kFpPkNtbnDwE2z+BoTdBWERwr63aDE0QSgXQ9SNT\nOVhaybKCIA9JunE2eGpg+K3Bva5qUzRBKBVAk/sl0T7CHfzWTBv+CclDILFvcK+r2hRNEEoFUFS4\nm8sGdOHjjQeorvU0fIA/FOdD0SoYrI0CVfNoglAqwK4a0pVjZdUsyQvSmNUb3gEEBl0fnOupNksT\nhFIBdnGfBGKiwnh/3f7AX8wYq3qp13jo2K3h/ZW6AE0QSgVYZJibKwYmM2/zASpragN7sf1roXgH\nDL6h4X2VaoAmCKWC4KohKZyoqOHz7QGuZtrwDrjCtWM+5ReaIJQKgnEZCcS1C+ejDQGsZjIGNr8H\nGZdCdKfAXUeFDE0QSgVBuNvFpf26sGDrocC1Ztq/Fo7vhf5XB+b8KuRoglAqSC4f2IXj5dWs3FkS\nmAtsngPihr5XBub8KuRoglAqSC7OTCQq3MW8zQf9f3JjYMscq/VSu87+P78KSZoglAqS6Ag3EzIT\nmbfpgP/Hqz68DYrzYIBWLyn/0QShVBBdPqAL+45XsGlfqX9PvGUOINDvKv+eV4U0TRBKBdGl/bvg\nEvxfzbRlDnTPhphk/55XhTRNEEoFUef2EYzq1Zl5mw7476QlO+HABm29pPxOE4RSQfaVAV3YeuAE\ne4rL/HPCrR9a8/5avaT8SxOEUkF2+QCrGmjBVj9VM23/BJIGQKde/jmfUjZNEEoFWY/4dvRObE/O\ntsPNP1n5MdizFPpc0fxzKXUWTRBKOeCSvkksLSimvKqZnfflL7BGjtNxp1UAaIJQygGX9E2iqsbD\n0oJmdt63fa7V71LqKP8EplQ9miCUcsCotE60i3CTs7UZ1UyeWtgxDzIvB5fbf8EpZdMEoZQDIsPc\njMtIIGfboaa/VV2YC+UlVoJQKgA0QSjlkEv6JlF4tJz8wyebdoIdc63O+TIu9W9gStk0QSjlkEl9\nEwGaXs20fS70GKtjP6iA0QShlEO6xkXTLzmGhdsPNf7gY3vh4EZt3qoCShOEUg6a1DeJFTtLOFlZ\n07gDd8y15pogVABpglDKQZP6JlJda1iS18jmrvk50LEHJPQJTGBKoQlCKUeN7NmJ9hFuFu1oxHOI\n2hrY+TmkTwKRgMWmlE8JQkTGich8EdkuIgUislNECho4JkpEVojIOhHZJCKP2eVpIrJcRPJE5C0R\nibDLI+31PHt7r+Z+OKVaunC3i7Hp8Sze0Yg7iH2robIUel8SuMCUwvc7iOeBp4DxwCggy55fSCUw\n2RgzFBgGTBGRMcBvgT8YYzKAo8Bd9v53AUft8j/Y+ynV5o3PSGBXcRl7S3zs3TU/BxDoPSmAUSnl\ne4I4boz52BhzyBhTXDdd6ABjqWvgHW5PBpgMvGOXvwRcYy9Ps9ext18qovfPqu0bn2k1d13k611E\n/gJIGapjT6uA8zVB5IjI70VkrIiMqJsaOkhE3CKyFjgEzAfygWPGmLomG4VAN3u5G7AXwN5+HIj3\ncs57RSRXRHIPH/ZDb5hKOSw9sT1dO0axOM+H/88VpVC4EtK1ekkFXpiP+42251n1yuruBs7LGFML\nDBOROOBdoF+jIzz3nM8BzwFkZWX5eeR3pYJPRBifmcDcTQep9RjcrgvcOO9aDKYW0i/4q6eUX/iU\nIIwxzfq6Yow5JiI5wFggTkTC7LuEVKDI3q0I6A4UikgY0BG4YDWWUm3F+MxE3s4tZEPRcYZ1jzv/\njgU5EN4Ouo8+/z5K+YmvrZg6ishTdVU7IvKkiHRs4JhE+84BEYkGvgJsAXKAG+zdbgfes5fn2OvY\n2xeYJvdiplTrMi7dqk1dtL2Baqb8HOh5EYRFBiEqFep8rWJ6AdgITLfXbwP+AVx3gWNSgJdExI2V\niN42xnwgIpuBN0Xk18AarBZS2PNXRCQPKAG+3qhPolQrFt8hkkHdYlmUd4TvXprpfafjhVC8A0be\nEdTYWoLq6moKCwupqKhwOpRWJSoqitTUVMLDw5t0vK8JIt0Yc3299cfsh8/nZYxZDwz3Ul4AZHsp\nrwBu9DEepdqc8RmJPL+4gJOVNXSI9PKrmZ9jzUPwAXVhYSExMTH06tULbdzoG2MMxcXFFBYWkpaW\n1qRz+NqKqVxExtetiMg4oLxJV1RKeTUhM4HqWsPygvM8estfAB26QNKA4AbWAlRUVBAfH6/JoRFE\nhPj4+Gbddfl6B3E/VnVRR0CwqoDuaPJVlVLnGNmzE1HhLhbtOMKl/bucudHjgZ2fQcZXQrZ7DU0O\njdfcn5lPdxDGmLX2G9FDgMHGmOHGmHXNurJS6gxR4W6y0+K998t0YD2UFYdk9VJL0aFDh3PKZs2a\nxcsvv3zB4+6++242b94cqLAC6oJ3ECJyqzHmVRH53lnlABhjngpgbEqFnAkZCTz+0Rb2Hy8npWP0\nlxsK7OcPvSc5EZY6j29961sN7vP3v/89CJEERkN3EO3teYyX6dx0qpRqlvGZCYCXbjfyc6xnDzHJ\nDkSlzucXv/gFTzzxBFu3biU7+8u2N7t27WLw4MEATJo0idzcXMC6C/nJT37C0KFDGTNmDAcPHgQg\nPz+fMWPGMHjwYH760596vVtxwgXvIIwxz9qL/zHGLKm/zX5QrZTyo37JMSR0iGRJ3hGmZ3W3CqvK\nYM8yGHW3s8G1EI+9v4nN+0r9es4BXWP5+dcGNvn4fv36UVVVxc6dO0lLS+Ott95ixowZ5+x36tQp\nxowZw+OPP84PfvAD/va3v/HTn/6UmTNnMnPmTG666SZmzZrVnI/iV762YnrGxzKlVDOICOMz4lmS\ndwSPx35PdM8XUFupzx9auOnTp/PWW28BnDdBREREcNVVVwEwcuRIdu3aBcDSpUu58Uarlf/NN98c\nnIB90NAziLHARUDiWc8hYgF3IANTKlSNy0jg32v3se3gCfqnxFrVS+4I6w1q1axv+oE0Y8YMbrzx\nRq677jpEhMzMc194DA8PP/0M1+12U1PTyKFmg6yhO4gIrGcNYZz5/KGUL7vLUEr5Ud1ziNODCBUs\ntPpeimh//oOU49LT03G73fzqV7/yevdwIWPGjGH27NkAvPnmm4EIr0kaegbxGfCZiLxojNkdpJiU\nCmkpHaNJT2zP4rwj3DOiAxzcCJf+zOmwQl5ZWRmpqamn17/3ve+ds8+MGTN4+OGH2blzZ6PO/fTT\nT3Prrbfy+OOPM2XKFDp2vGBXd0Hj64tyZSLye2AgEFVXaIzRPoeVCoDxGQm8nVtI9Y4CwkGHF20B\nPB5Pg/s89NBDPPTQQ2eULVy48PTyyZMnTy/fcMMN3HCDVRHTrVs3li1bhojw5ptvsm3bNv8E3Uy+\nPqR+DdgKpAGPAbuAlQGKSamQNz4zkfLqWo5umAfRnawR5FSbtWrVKoYNG8aQIUP4y1/+wpNPPul0\nSIDvdxDxxpjnRWRmvWonTRBKBcjo3p1xuyB67+eQORFc2iakLZswYQLr1rW8zil8TRDV9ny/iHwV\n2AfogLhKBUhsVDhfSyklpviwNm9VjvE1Qfza7qjv+1jvP8QC/x2wqJRSTIvdBsVQ2nUCsU4Ho0KS\nr0OOfmAvHgf064xSQTC8ai0FnmS2F7djSorT0ahQ1NCLcs8A5x320xjzgN8jUkpBTRUdD63gYxnH\nprwjTBmkGUIFX0OtmHKBVReYlFKBULgCqT7FkaRxLMk7zwBCKmjeffddhg0bdsbkcrn4+OOPm3Xe\nO+64g3feeeec8tzcXB54wPnv3w29KPdSsAJRStWTnwPiJm7gZHZ+spfCo2WkdmrndFQh69prr+Xa\na689vf7cc8/x2muvccUVVwTkellZWWRlZQXk3I3h03sQIpIjIgvOngIdnFIhK38BdBvJ6P7WWMJL\n8o40cIAKlu3bt/PLX/6SV155BRHh4YcfZtCgQQwePPh0Z30LFy5k4sSJTJs2jd69e/PII4/w2muv\nkZ2dzeDBg8nPzz99vv/85z9kZWXRp08fPvjgg9PH13Xqd+rUKb75zW+SnZ3N8OHDee+99wDYtGkT\n2dnZp9+f2LFjh98/q6+tmOq/GhgFXA+07F6mlGqtykpg3xqY+EMykzqQFBPJoh1HmDGqh9ORtQwf\nPwIHNvj3nMmD4crfNLhbdXU1N998M08++SQ9evRg9uzZrF27lnXr1nHkyBFGjRrFxRdfDMC6devY\nsmULnTt3pnfv3tx9992sWLGCP/7xjzzzzDM8/fTTgDV2xIoVK8jPz+eSSy4hLy/vjGs+/vjjTJ48\nmRdeeIFjx46RnZ3NZZddxqxZs5g5cya33HILVVVV1NbW+vdngu+tmM5+3rBERFb4PRqlFOz8HDCQ\nfond/XcCC7cfxuMxuFw6LrOTHn30UQYOHHi6M77Fixdz00034Xa76dKlCxMnTmTlypXExsYyatQo\nUlKsxgXp6elcfvnlAAwePJicnJzT55w+fToul4vMzEx69+7N1q1bz7jmvHnzmDNnDk888QQAFRUV\n7Nmzh7Fjx/L4449TWFjIdddd57X32ObyKUGISP2X4lzASKBl9CalVFtTkAMRMdBtJGB1//2vNUVs\n3l/KoG76a+fLN/1AWLhwIbNnz2b16tU+7R8ZGXl62eVynV53uVxndPNd1/33+daNMcyePZu+ffue\nUd6/f39Gjx7Nhx9+yNSpU3n22WeZPNm/3eP52hfTKr5s0bQU64W5u/waiVLKkp8DaRPAHQ582f23\nPodwztGjR7nzzjt5+eWXiYmJOV0+YcIE3nrrLWprazl8+DCff/75GUOP+uKf//wnHo+H/Px8CgoK\nzkkEV1xxBc888wzGWG8crFmzBoCCggJ69+7NAw88wLRp01i/fn0zP+W5fK1iSvP7lZVS5yopgGO7\nYex3Thd1iY0iM6kDi/OOcN/EdAeDC12zZs3i0KFD3H///WeU/+hHP2LIkCEMHToUEeF3v/sdycnJ\n51QTXUiPHj3Izs6mtLSUWbNmERUVdcb2Rx99lAcffJAhQ4bg8XhIS0vjgw8+4O233+aVV14hPDyc\n5ORkfvzjH/vls9YndVnpgjuJRAHfBsZjvTi3CJhljKnwe0SNkJWVZeoGA1eqTVj5d/jw+/CdXEj4\nsk75sfc38fryPaz7+eVEhYdex31btmyhf//+TofRKnn72YnIKmNMg+1ofa1iehlrLIhngD/by680\nMk6lVEPyc6BjD4jPOKN4fEYClTUeVu8+6lBgKhT52sx1kDFmQL31HBHZHIiAlApZtdVWC6aB18JZ\nDypH944nzCUsyjvCRRkJDgWoQo2vdxCrRWRM3YqIjMZ6aK2U8peiVVBZChmXnrOpQ2QYw3vE6YNq\nFVS+JoiRwBcisktEdmG1ZBolIhtExP+PzpUKRfkLQFyQdrHXzeMyEthQdJyjp6qCHFjL4MvzUnWm\n5v7MfE0QU7CGG51oT2l22VXA17wdICLd7S46NovIJhGZaZd3FpH5IrLDnneyy0VE/iQieSKyXkRG\nNOuTKdXa2N1rEN3J6+YJmQkYA0sLQq/zvqioKIqLizVJNIIxhuLi4nNaRTWGr81cd4vIUGCCXbTI\nGNPQ+Hg1wPeNMatFJAZYJSLzgTuAT40xvxGRR4BHgB8CVwKZ9jQa+Ks9V6rtKz9qVTFd/PB5dxmS\nGkeHyDAW5x1h6uDQ6v47NTWVwsJCDh8+7HQorUpUVBSpqalNPt7XN6lnAvcA/7KLXhWR54wxz5zv\nGGPMfmC/vXxCRLYA3YBpwCR7t5eAhVgJYhrwsrG+IiwTkTgRSbHPo1TbtvNzMB5IP/+bsOFuF2N6\nx7N4R+g9hwgPDyctTV/HCjZfq5juAkYbY35mjPkZMAYrYfhERHoBw4HlQJd6f/QPAF3s5W7A3nqH\nFdplSrV9+QsgMvZ09xrnMz4jnj0lZewpLgtSYCqU+ZogBKjfVWCtXdbwgSIdgNnAg8aY0vrb7LuF\nRlUqisi9IpIrIrl6u6naBGMgb4H1cNruXuN8xmcmArBYWzOpIPA1QfwDWC4ivxCRXwDLgOcbOkhE\nwrGSw2vGmLrqqYMikmJvTwEO2eVFQPd6h6faZWcwxjxnjMkyxmQlJib6GL5SLVhJARzfA+kND/ee\nntie5Ngobe6qgsKnBGGMeQq4EyixpzuNMU9f6BixuiR8HthiH19nDnC7vXw78F698m/YrZnGAMf1\n+YMKCfn22Fvp577/cDYRYVxGAkvyj1Dr0RY9KrAu+JDa7oPpW0AGsAH4izHG14GCxgG3ARtEZK1d\n9mPgN8DbInIXsBuYbm/7CJgK5AFlWAlJqbYvfwF0SoPOvj2EnZCZwOzVhWzeV8rgVO3+WwVOQ62Y\nXgKqsTrnuxLoDzzoy4mNMYs5/3OKc74q2c8j/suXcyvVZtR1rzFkhs+HXJQRD1jPITRBqEBqqIpp\ngDHmVmPMs8ANgPdXPJVSTbNnGVSdvGDz1rMlxUTRLzmGxXnaSEMFVkMJorpuoRFVS0opX+2YB65w\n6D2xUYeNy0hg5a6jlFf5fxxipeo0lCCGikipPZ0AhtQti0hpA8cqpRqyYx70GgeRMQ3vW8/EPolU\n1XhYWqCtmVTgXDBBGGPcxphYe4oxxoTVW44NVpBKtUlHd8PhrZB5RaMPHd27M+0i3CzYeqjhnZVq\nIl/fg1CAclmVAAAVCElEQVRK+duOedY88/JGHxoZ5mZcRgI5Ww9rB3YqYDRBKOWUHfOgc29IyGh4\nXy8m90ui6Fg52w+e9HNgSlk0QSjlhKoyq3lrE6qX6lzSNwlAq5lUwGiCUMoJuxZBTQVkfqXJp0ju\nGMWAlFhyNEGoANEEoZQTdsyD8PbQa3yzTjO5XxKr9hzleFl1wzsr1UiaIJQKNmNg+zzoPQnCIpt1\nqkv6JVHrMXy2Q1+aU/6nCUKpYDu81eq9tRnVS3WGdY+jc/sIrWZSAaEJQqlg2/qBNe8zpdmncruE\nSX0SWbjtkPbuqvxOE4RSwbblA0gdBbH+GVd6cv8kjpZVs2r3Ub+cT6k6miCUCqZje2H/Wuh3ld9O\nOalvEhFhLuZuOuC3cyoFmiCUCq6tH1pzPyaIDpFhjM9I4JONB/StauVXmiCUCqatH0Bivya/PX0+\nUwYmU3SsnE37tA9N5T+aIJQKlrIS2L3Er3cPdS7tn4RL0Gom5VeaIJQKlm0fg/FAf/8niPgOkWSn\nddYEofxKE4RSwbL1A4hNhZRhATn9FQOT2X7wJAWHtfM+5R+aIJQKhsoTkL8A+n0V5HxDtTfPFQOT\nAZi76WBAzq9CjyYIpYJh60dW53yDrgvYJbrGRTMktSOfbNwfsGuo0KIJQqlg2Djbql5KzQ7oZaYO\nTmFd4XF2F58K6HVUaNAEoVSglZVA/qfW3YMrsL9yXxvaFYD31+0L6HVUaNAEoVSgbXkfPDUw6PqA\nX6pbXDSjenXivbX79KU51WyaIJQKtI2zoXM6pAwNyuWuHtaNHYdOsvXAiaBcT7VdmiCUCqQTB63R\n4wZdH7DWS2ebOigZt0uYo9VMqpk0QSgVSJv+Zb0cF4TqpTrxHSIZn5HAHK1mUs2kCUKpQFrzGnQd\nDkn9gnrZacO6UnSsXLsAV82iCUKpQNm/Dg5ugGG3BP3SVwxMpl2Em3/mFgb92qrt0AShVKCseQ3c\nkTD4hqBfun1kGFcNSeGD9fs4VVkT9OurtkEThFKBUFMJG962utaI7uRICDNGdedUVS0frtc3q1XT\nBCxBiMgLInJIRDbWK+ssIvNFZIc972SXi4j8SUTyRGS9iIwIVFxKBcW2j6D8KAwPfvVSnRE9OtE7\nsT1v5e51LAbVugXyDuJF4OxR2R8BPjXGZAKf2usAVwKZ9nQv8NcAxqVU4K15DWK7Qe9LHAtBRJiR\n1Z1Vu4+Sd0jfiVCNF7AEYYz5HCg5q3ga8JK9/BJwTb3yl41lGRAnIv4Z0V2pYCvZCXn/sR5Ou9yO\nhnLdiFTcLuFtfVitmiDYzyC6GGPqKkQPAF3s5W5A/fvgQrvsHCJyr4jkikju4cOHAxepUk2V+zyI\nC7LudDoSEmMi+Ur/Lrydu5eK6lqnw1GtjGMPqY31Bk+j3+IxxjxnjMkyxmQlJiYGIDKlmqGqDFa/\nYo0aF9vV6WgAuP2iXhwrq+bfa4qcDkW1MsFOEAfrqo7s+SG7vAjoXm+/VLtMqdZl42yoOAbZ9zod\nyWljenemX3IML36xS9+sVo0S7AQxB7jdXr4deK9e+Tfs1kxjgOP1qqKUah2MgRXPQWJ/6DnO6WhO\nExHuHNeLrQdOsLSg2OlwVCsSyGaubwBLgb4iUigidwG/Ab4iIjuAy+x1gI+AAiAP+Bvw7UDFpVTA\n7PwcDqyH0fcFrWM+X00b1o1O7cJ5cckup0NRrUhYoE5sjLnpPJsu9bKvAf4rULEoFRSL/wDtk2Do\n+f7rOycq3M1N2T3462f57C4+Rc/49k6HpFoBfZNaKX/YtxYKcmDM/RAe5XQ0Xt1xUS/C3S5mfZbv\ndCiqldAEoZQ/LPkjRMbCqLucjuS8kmKjmJHVnXdWFbLvWLnT4ahWQBOEUs11ZAds/rf13kNUR6ej\nuaD7JvbGGHju8wKnQ1GtgCYIpZor538gLBrGftfpSBqU2qkd143oxhsr9nDoRIXT4agWThOEUs2x\nf701atyY+6FD63hx89uTMqjxGP68IM/pUFQLpwlCqebIedyqVrqo5d891OmV0J6bsrvz+vI97Dxy\nyulwVAumCUKpptq9FLZ/Ahc9ANFxTkfTKDMv7UNEmIsn5m5zOhTVgmmCUKopPLXw8cNWl95j7nc6\nmkZLjInkngm9+XDDftbs0XGrlXeaIJRqitUvwYENcPmvIaJ1vnR2z8W9SYqJ5GfvbaLWo300qXNp\nglCqscpK4NNfQc/xMPBap6Npsg6RYTx61QA2FB3n1WW7nQ5HtUCaIJRqrLk/gYrjcOVvW1yfS411\n1ZAUJmQm8MTcbRwq1Wav6kyaIJRqjO1zYd3rMP6/IXmQ09E0m4jwy2mDqKzx8Oh7G7U7cHUGTRBK\n+ar8GLw/E5IGwMQfOB2N36QltOf7l/dh7qaDvLNKhyZVX9IEoZQvjIEP/htOHoJp/wdhkU5H5Fd3\nT+hNdlpnHnt/M3tLypwOR7UQmiCU8sXKv1tvTE/+KXQb4XQ0fud2CU9NHwrAA2+uobJGx69WmiCU\nati+NTD3x5B5OYx70OloAia1Uzt+d8MQ1uw5xmPvb3Y6HNUCaIJQ6kJK98EbN1sDAV37LLja9q/M\n1MEp3D8pndeX7+HNFXucDkc5rG3/b1eqOSpPwGvTrfnNb0G7zk5HFBQPXd6Xi/sk8tN/byRn2yGn\nw1EO0gShlDfV5fDWrXBoM0x/sU00afWV2yX8383D6ZcSw7dfXc1q7YojZGmCUOps1RXw5i1Q8Blc\n/QxkXOZ0REEXExXOP+7IJik2kjteWKH9NYUoTRBK1Vd5At74OuQvsJLD8FucjsgxiTGRvHrXaOLa\nRXDr35ezvKDY6ZBUkGmCUKpO6T74x5Ww83PrXYcRtzkdkeO6d27H2/eNJbljFN94YQVz1u1zOiQV\nRJoglALYswz+dimU7IRb3g7pO4ezJXeM4u37xjI0NY4H3ljDbz/Zqr2/hghNECq0eTyw6Cn4x1Tr\n7ehvfhKSzxwaEt8hklfvHs1N2T3468J8bnt+OUXHyp0OSwWYJggVug5thRenwqePwYCr4b7PIXmw\n01G1WBFhLv7n2kH89vrBrNt7jCl/+Jx/5u7VDv7aME0QKvRUHIf/PAazxsPhrTDtL3DDPyAq1unI\nWjwRYcaoHnw882L6pcTw8DvruWHWUjYUHnc6NBUA0pqzf1ZWlsnNzXU6DNVaVJ6E3Odh8R+g/CgM\n+Tpc8Ti0T3A6slbJ4zG8s6qQ383dSvGpKqYOTuE7l2TQP0UTbUsnIquMMVkN7RcWjGCUctSxPbDi\nOVj1MlQeh/RL4dKfQddhTkfWqrlcwvRR3blycDKzPsvnpS928+H6/VzWP4lbx/RkQmYiblfrHlAp\n1OkdhGqbKo7D5jmw/i3Ytdga+a3/1TD2v6B7ttPRtUnHy6p58YtdvLR0FyWnqugWF831I1OZOjiZ\nvl1ikFY++l5b4usdhCYI1TZ4PFa3GHn/gfxPYfdS8FRDpzQYMh2G3wpxPZyOMiRU1tQyf/NB3lyx\nlyX5RzAGesa347L+XbgoPZ5RaZ2JjQp3OsyQ1ioThIhMAf4IuIG/G2N+c6H9NUGEKE+tVW10eBsU\nrYKiXGteYT8o7TII0ifDgGussRv0m6tjDp+oZP7mg3y8cT/LC0qoqvXgEhjUrSNDUjsysGtHBnaN\npU+XGKLC3U6HGzJaXYIQETewHfgKUAisBG4yxpy3Y3pNEG2UMVBWAqVF1tvNpUXWVLITjuyA4h1Q\nU2HtKy5rCNDULEjNhvRLILars/Erryqqa1m95yjL8otZvrOEzftKOVFZA1gdBHaLi6ZnfDt6xrej\nV3x7UjtFkxgTSVJMFIkxkZpA/Kg1PqTOBvKMMQUAIvImMA3QkUtaAmPAeMBTA7XV1txTa8/rT3ZZ\nbaXVI2p1mT0/e7kcqk5arYm8TZ6aM68vbuiYCol9ofdESOhjTcmDIbKDMz8T1ShR4W4uSk/gonSr\n1ZjHY9h7tIxN+0rZsr+UXcVl7C4+xZy1+yitqDnn+JjIMBJjIomNDicmKozY6HBio8KIjbLWO0SG\nERXuJjLcRVTYmfPIMDdR4S4i3G7cbsEtgtslhLnk3HWX6PMSW0tKEN2AvfXWC4HRAbnS6lfgi2fs\nFfsO6ow7KT+UnbFYv8wEqKwpcdPwfvX/+PtbRAeI7gzRcdaU1B+iO0FUHMQkW3cCsd2sqUMSuPQb\nZFvicgk949vTM749UwennLHtWFkVRcfKOXyikkMnKjlcN52spLS8mtKKGoqOlVNaXsOJimoqazz+\njU0gzOXC7RJcYr3/IQACgr1ef5m6msz65SCcuR915efZVsdrejqr8MHL+nD10MDeLbekBOETEbkX\nuBegR48mPnRsF2/9MfrypHULfi6rV+61DB/387HMb3HXK3OHgav+5K63HH7Wer3t7giIaAdh0RAe\nDeHt7Lm9HBapzwbUecW1iyCuXYTP+1fW1HKqspaK6loqazznzCura6mo8VBV48HjMdR4DLXGUFvr\nsZZPr5sz1+1lY8BgTn9/MsZg4IzyunXq1r1sM1gr5qxz1Oet0t/bo4BO7QL/oL8lJYgioHu99VS7\n7AzGmOeA58B6BtGkK/Wbak1KqTYhMsxNZJjeYfpbS+pqYyWQKSJpIhIBfB2Y43BMSikVslrMHYQx\npkZEvgPMxWrm+oIxZpPDYSmlVMhqMQkCwBjzEfCR03EopZRqWVVMSimlWhBNEEoppbzSBKGUUsor\nTRBKKaW80gShlFLKqxbTWV9TiMhhYHcTD08AjvgxnNZAP3No0M8cGprzmXsaYxIb2qlVJ4jmEJFc\nX3ozbEv0M4cG/cyhIRifWauYlFJKeaUJQimllFehnCCeczoAB+hnDg36mUNDwD9zyD6DUEopdWGh\nfAehlFLqAkIyQYjIFBHZJiJ5IvKI0/EEmoi8ICKHRGSj07EEi4h0F5EcEdksIptEZKbTMQWaiESJ\nyAoRWWd/5secjikYRMQtImtE5AOnYwkGEdklIhtEZK2I5Ab0WqFWxSQibmA78BWsYU1XAjcZY9rs\n2NcicjFwEnjZGDPI6XiCQURSgBRjzGoRiQFWAde08X9nAdobY06KSDiwGJhpjFnmcGgBJSLfA7KA\nWGPMVU7HE2gisgvIMsYE/L2PULyDyAbyjDEFxpgq4E1gmsMxBZQx5nOgxOk4gskYs98Ys9pePgFs\nwRr3vM0ylpP2arg9telvgCKSCnwV+LvTsbRFoZggugF7660X0sb/cIQ6EekFDAeWOxtJ4NnVLWuB\nQ8B8Y0xb/8xPAz8APE4HEkQGmCciq0Tk3kBeKBQThAohItIBmA08aIwpdTqeQDPG1BpjhmGN6Z4t\nIm22SlFErgIOGWNWOR1LkI03xowArgT+y65CDohQTBBFQPd666l2mWpj7Hr42cBrxph/OR1PMBlj\njgE5wBSnYwmgccDVdp38m8BkEXnV2ZACzxhTZM8PAe9iVZsHRCgmiJVApoikiUgE8HVgjsMxKT+z\nH9g+D2wxxjzldDzBICKJIhJnL0djNcTY6mxUgWOM+ZExJtUY0wvr93iBMeZWh8MKKBFpbze6QETa\nA5cDAWudGHIJwhhTA3wHmIv14PJtY8wmZ6MKLBF5A1gK9BWRQhG5y+mYgmAccBvWt8q19jTV6aAC\nLAXIEZH1WF+E5htjQqLpZwjpAiwWkXXACuBDY8wngbpYyDVzVUop5ZuQu4NQSinlG00QSimlvNIE\noZRSyitNEEoppbzSBKGUUsorTRCqRRKRWrtp6ia7d9Lvi0jA/r+KyI0iskVEcgJ1jaYQkTgR+Xa9\n9UmB6rVURF4UkRsCcW7VOmmCUC1VuTFmmDFmINYLX1cCPw/g9e4C7jHGXFK/UETCAnhNX8QB325w\nr7PYvRYr1SyaIFSLZ3cpcC/wHbH0EpFFIrLani4CEJGXReSauuNE5DURmSYiA+1xEtaKyHoRyax/\nfhH5GTAeeF5Efi8id4jIHBFZAHxqX/P3IrLR7od/hn3cJBH5TETeE5ECEfmNiNxiX2uDiKSf/VlE\npLOI/NuOY5mIDLHLfyEiD9Xbb6PdyeBvgHQ79t/bm2NF5EOxxjSZVXdnJSInReRJ+yWqsSIy0o5v\nlYjMtbtAR0TuEZGV9p3ZbBFp5yXOX9l3FJpoQpkxRiedWtwEnPRSdgzrTdJ2QJRdlgnk2ssTgX/b\nyx2BnUAY8Axwi10eAUR7OfdCrD72Ae7A6uW3s71+PTAfcNvX34P11vIkO6YUIBKrT6/H7GNmAk97\nuc4zwM/t5cnAWnv5F8BD9fbbCPSyp431yicBFUBvO575wA32NgNMt5fDgS+ARHt9BvCCvRxf73y/\nBr5rL78I3AD8HpiF/SKtTqE7OX37rFRThAN/FpFhQC3QB8AY85mI/EVEErH+qM82xtSIyFLgJ/bY\nAf8yxuzw4RrzjTF1Y2iMB94wxtQCB0XkM2AUUAqsNMbsBxCRfGCefcwG4BLONd6ODWPMAhGJF5HY\nRn7+FcaYAvuab9jnfAfrZzHb3qcvMAiYb3VLhRvYb28bJCK/xqq+6oDV7UydR4HlxpiAdiOtWgdN\nEKpVEJHeWH8AD2E9izgIDMWqJq2ot+vLwK1YnbfdCWCMeV1ElmMNLPORiNxnjFnQwCVP+RhaZb1l\nT711D437/arhzCrfqAvse3b/OHXrFXYSAxBgkzFmrJfjX8QaXW+diNyBdVdSZyUwUkQ610uQKkTp\nMwjV4tl3BLOAPxtjDFb10X5jjAerQ7769eQvAg8CGHt4UTu5FBhj/gS8BwxpZAiLgBliDcaTCFyM\n1VFaUywCbrHjmgQcMdY4FbuAEXb5CCDN3v8EEHPWObLF6o3YhVV1tNjLdbYBiSIy1j5nuIgMtLfF\nAPvF6g79lrOO+wTruceHYvcaqkKX3kGoliparJHRwrG+Xb8C1HXb/Rdgtoh8A+sP2ulv+8aYgyKy\nBfh3vXNNB24TkWrgAPA/jYzlXWAssA7r2/oPjDEHRKRf4z8WvwBeEKvH1TLgdrt8NvANEdmENfLd\ndvvzFIvIEhHZCHwMfIj1Lf/PQAbWmA/vnn0RY0yV3WT1TyLSEet3/WlgE3Y1EnDYnsecdew/7eQw\nR0SmGmPKm/A5VRugvbmqNsVukbMBGGGMOe50PEq1ZlrFpNoMEbkMa4yPZzQ5KNV8egehlFLKK72D\nUEop5ZUmCKWUUl5pglBKKeWVJgillFJeaYJQSinllSYIpZRSXv0/t9Vfp8KNp6oAAAAASUVORK5C\nYII=\n", + "text/plain": [ + "" + ] + }, "metadata": {}, - "source": [ - "Avoid memory allocations" - ] - }, + "output_type": "display_data" + } + ], + "source": [ + "soln = odeint(f_nat, y0, t)\n", + "plt.figure()\n", + "plt.plot(t, soln[:, 0], label='Living')\n", + "plt.plot(t, soln[:, 1], label='Zombies')\n", + "plt.xlabel('Days from outbreak')\n", + "plt.ylabel('Population')\n", + "plt.legend(loc=0)\n", + "plt.show()" + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "metadata": {}, + "outputs": [ { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "**zombie apocalypse modeling** from http://wiki.scipy.org/Cookbook/Zombie_Apocalypse_ODEINT" + "name": "stdout", + "output_type": "stream", + "text": [ + "native python\n", + "1.53 ms ± 29.1 µs per loop (mean ± std. dev. of 7 runs, 1000 loops each)\n", + "hope\n", + "266 µs ± 24.3 µs per loop (mean ± std. dev. of 7 runs, 1 loop each)\n", + "hope without allocation\n", + "237 µs ± 19.2 µs per loop (mean ± std. dev. of 7 runs, 1 loop each)\n" ] - }, - { - "cell_type": "code", - "collapsed": false, - "input": [ - "from scipy.integrate import odeint\n", - "\n", - "P, d, B, G, A = 0, 0.0001, 0.0095, 0.0001, 0.0001\n", - "\n", - "def f_nat(y, t):\n", - " dy = np.empty(3)\n", - " dy[0] = P - B*y[0]*y[1] - d*y[0]\n", - " dy[1] = B*y[0]*y[1] + G*y[2] - A*y[0]*y[1]\n", - " dy[2] = d*y[0] + A*y[0]*y[1] - G*y[2]\n", - " return dy\n", - "\n", - "y0, t = np.array([500., 0., 5.]), np.linspace(0, 5., 1000)" - ], - "language": "python", - "metadata": {}, - "outputs": [], - "prompt_number": 2 - }, - { - "cell_type": "code", - "collapsed": false, - "input": [ - "soln = odeint(f_nat, y0, t)\n", - "plt.figure()\n", - "plt.plot(t, soln[:, 0], label='Living')\n", - "plt.plot(t, soln[:, 1], label='Zombies')\n", - "plt.xlabel('Days from outbreak')\n", - "plt.ylabel('Population')\n", - "plt.legend(loc=0)\n", - "plt.show()" - ], - "language": "python", - "metadata": {}, - "outputs": [ - { - "metadata": {}, - "output_type": "display_data", - "png": "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\nCLPk4uLCZ599xkcffQToP83279+fHj160KNHDw4cOABARETENdOgT548mU2bNvHnn38SFBREYGAg\n/v7+JCUlXbP/119/nf379zNjxgxeeOEFli5dyvDhwwkNDSUsLIzz588zcuRI/P39ufvuuw3rTkRG\nRhIREUH//v3x9vZm/fr1/Otf/8LPz48hQ4ZQXl5+w+9y+PBhevfujb+/P6NHjzZMLxEcHExcXByg\nn3bCx8eHsrIyXnvtNdasWUNgYCBr164F9K2GPn360KFDB7744gsAYmJi6NevHyNGjKBr167odDqe\nf/55evXqhb+/P5999hmgn/hv4MCB9OjRAz8/v5uuDXDq1Cm6d+9uyCOslCKEmWjcuPENtzk5OSk5\nOTlKUVGRUlJSoiiKoiQmJio9e/ZUFEVRfvjhB2XkyJGKoihKfn6+4uPjo5SXlyuzZs1SVq5cqSiK\nopSVlSnFxcU37Ds4OFiJi4tTFEVRlixZonh5eSnnz59XFEVRZs2apbz++uuKoijKnj17lICAAEVR\nFGXu3LlKv379lPLycuX3339XGjZsqGzfvl1RFEUZNWqUsmHDhhuO061bN+XHH39UFEVRXnvtNeWZ\nZ5654fi5ubmKt7e3oiiK8tVXXylPPvmk4flz585V/P39lZKSEuXs2bNKq1atlIyMDGXv3r1Ko0aN\nlJSUFEVRFOXTTz9V3nzzTUVRFKWkpETp2bOnkpycrJSXlysFBQWG4/j6+iqKoijJyclK165dlePH\njyuBgYHKkSNHbvGvI6yBtDFFnVBaWsqsWbP4/fffsbW1JTExEYD+/fszc+ZMzp49y7p16xg7diy2\ntrb06dOHf//736SlpTF69Gh8fX2rPEZYWBhOTk6Afo6k9evXAxASEsK5c+e4ePEiGo2GIUOGYGtr\na/hUPmjQIAC6detGSkrKNfu8cOECFy5coF+/foC+RTNu3Lhb5lAU5ZpzJxqNhpEjR1K/fn3q169P\nSEgIsbGxODk50atXL9q0aQPAjh07OHr0KOvWrQOgoKCApKQkvLy8mDNnDvv27cPGxoaMjAxycnIA\nyMnJYeTIkXz33Xd06tSpytdIWDbpMhJm69SpU9ja2uLi4sIHH3yAh4cHR44c4dChQ5SWlhoeN23a\nNJYvX85XX33FjBkzAP0EgJs3b6Zhw4bcd9997N2795bH0mg0NGrU6JrblEpOaNerVw/Qrz1gb29v\nuN3GxuamXUaV7dPOzg6dTgfo10OuiasLv1yf+aOPPiI+Pp74+HhOnjzJwIEDWbFiBWfPnuW3334j\nPj4eV1dXw/GcnJxo06ZNpfPjC+siBUGYpdzcXB5//HGefPJJQP9p193dHYBly5ZRUVFheOz06dP5\n8MMP0Wg0hk+5ycnJ+Pj48OSTTzJixIgq156+/s2/X79+rFy5EtD31bu4uODo6FjjUU9NmzbF2dmZ\nn376CYDly5cTHBwM6Bc+v7pewdVP9QBNmjQxTFd8NdvGjRu5fPky586dIyYmhrvuuuuGLIMGDWLR\nokWGopSYmEhRUREFBQW4urpia2vL3r17OX36tOE59erVY/369SxbtoxVq1bV6HcTlke6jITZKC4u\nJjAwkLKyMuzs7Jg2bRrPPvssADNnzmTMmDEsW7aMwYMHX7MqmqurK1qtllGjRhluW7t2LcuXL8fe\n3h4PDw9eeeWVWx5bo9FcM5onMjKSGTNm4O/vT6NGjVi6dOlNH3f9CKCbjQhaunQpjz/+OEVFRbRr\n184wXfm//vUvxo8fz2effcbQoUMNzw0JCSEqKorAwEDmzJmDRqPBz8+PkJAQzp49y2uvvYa7uzt/\n/fXXNcd7+OGHSUlJoXv37iiKgqurKxs2bGDy5MkMGzYMPz8/evbsec208RqNBgcHB7Zs2UJYWBiO\njo7cf//9t3ythOWS6a9FnVdUVISfnx/x8fE4OjqqHUeIOku6jESdtmvXLrRaLU899ZQUAyHukLQQ\nhBBCANJCEEIIcYUUBCGEEIAUBCGEEFdIQRBCCAFIQRBCCHGFFAQhhBAA/H8Alc/dnlS5igAAAABJ\nRU5ErkJggg==\n", - "text": [ - "" - ] - } - ], - "prompt_number": 3 - }, - { - "cell_type": "code", - "collapsed": false, - "input": [ - "@hope.jit\n", - "def f_hope(y, t, P, d, B, G, A):\n", - " dy = np.empty(3)\n", - " dy[0] = P - B*y[0]*y[1] - d*y[0]\n", - " dy[1] = B*y[0]*y[1] + G*y[2] - A*y[0]*y[1]\n", - " dy[2] = d*y[0] + A*y[0]*y[1] - G*y[2]\n", - " return dy\n", - "\n", - "@hope.jit\n", - "def f_opt(y, t, dy, P, d, B, G, A):\n", - " dy[0] = P - B*y[0]*y[1] - d*y[0]\n", - " dy[1] = B*y[0]*y[1] + G*y[2] - A*y[0]*y[1]\n", - " dy[2] = d*y[0] + A*y[0]*y[1] - G*y[2]\n", - " return dy\n", - "\n", - "dy = np.empty(3)\n", - "print \"native python\"\n", - "%timeit odeint(f_nat, y0, t)\n", - "print \"hope\"\n", - "%timeit odeint(f_hope, y0, t, args=(P, d, B, G, A))\n", - "print \"hope without allocation\"\n", - "%timeit odeint(f_opt, y0, t, args=(dy, P, d, B, G, A))" - ], - "language": "python", - "metadata": {}, - "outputs": [ - { - "output_type": "stream", - "stream": "stdout", - "text": [ - "native python\n", - "100 loops, best of 3: 4.46 ms per loop" - ] - }, - { - "output_type": "stream", - "stream": "stdout", - "text": [ - "\n", - "hope\n", - "1000 loops, best of 3: 515 \u00b5s per loop" - ] - }, - { - "output_type": "stream", - "stream": "stdout", - "text": [ - "\n", - "hope without allocation\n", - "1000 loops, best of 3: 470 \u00b5s per loop" - ] - }, - { - "output_type": "stream", - "stream": "stdout", - "text": [ - "\n" - ] - } - ], - "prompt_number": 4 - }, + } + ], + "source": [ + "@hope.jit\n", + "def f_hope(y, t, P, d, B, G, A):\n", + " dy = np.empty(3)\n", + " dy[0] = P - B*y[0]*y[1] - d*y[0]\n", + " dy[1] = B*y[0]*y[1] + G*y[2] - A*y[0]*y[1]\n", + " dy[2] = d*y[0] + A*y[0]*y[1] - G*y[2]\n", + " return dy\n", + "\n", + "@hope.jit\n", + "def f_opt(y, t, dy, P, d, B, G, A):\n", + " dy[0] = P - B*y[0]*y[1] - d*y[0]\n", + " dy[1] = B*y[0]*y[1] + G*y[2] - A*y[0]*y[1]\n", + " dy[2] = d*y[0] + A*y[0]*y[1] - G*y[2]\n", + " return dy\n", + "\n", + "dy = np.empty(3)\n", + "print(\"native python\")\n", + "%timeit odeint(f_nat, y0, t)\n", + "print(\"hope\")\n", + "%timeit odeint(f_hope, y0, t, args=(P, d, B, G, A))\n", + "print(\"hope without allocation\")\n", + "%timeit odeint(f_opt, y0, t, args=(dy, P, d, B, G, A))" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "source": [ + "## Approximate expensive functions" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### tanh" + ] + }, + { + "cell_type": "code", + "execution_count": 5, + "metadata": {}, + "outputs": [ { - "cell_type": "heading", - "level": 2, - "metadata": { - "slideshow": { - "slide_type": "slide" - } + "data": { + "image/png": "iVBORw0KGgoAAAANSUhEUgAAAY4AAAD8CAYAAABgmUMCAAAABHNCSVQICAgIfAhkiAAAAAlwSFlz\nAAALEgAACxIB0t1+/AAAIABJREFUeJzt3Xl8VPW5+PHPM5ONLASyEUIICfsqIAEXUFEUlypYtVZb\nvSq2Vqu3dr0/297ui7bX1tZWq1bRat13VCyKbKIii4LsIawhQBISCNm3+f7+OGfCJEySSTJbkuf9\neuWVyTlnznlycnKe+a5HjDEopZRSvnKEOgCllFI9iyYOpZRSnaKJQymlVKdo4lBKKdUpmjiUUkp1\niiYOpZRSnaKJQymlVKdo4lBKKdUpmjiUUkp1SkSoAwiElJQUk52dHeowlFKqR9mwYcNRY0xqR9v1\niMQhIguBy4FiY8zEjrbPzs5m/fr1gQ9MKaV6ERHZ78t2PaWq6ingklAHoZRSqockDmPMKqAs1HEo\npZTqIVVVSqkAaKyDymKoKobKEqg+iqmroLGmgvrqEzTUnMBVV4Wpr8HV1ICxv2hqAJf1XVyNiGkC\n4wJjANPqdavvGMS0+g72upZOnbjbeHl16uqO5vuWDrfoPs8jdOt4XXhr6eVPMCZ3TteP6YNekzhE\n5DbgNoCsrKwQR6NUGGmohSNfUL9/LZUHt+A6mk+/E3uJqz96yqYCRALGOKmnH9XEUGciaSCCRpw0\n4qQBJ40mwvpONE04cSGICEYEcIBYP2Mvb/5ZrHUGAYe1HpGTRxf3K+uFe5X13Voq7kDtb57r8FgH\nQGQsseMvwxGf6nEc3wmdf0+oOaP6s3379na3iYmJITMzk8jIyC4do9ckDmPMY8BjALm5ufqQEdWn\nNRbncWTtazh3/YfU8s1E0EgU4DL92WfS2esaz2FHOvX9UnHFpUJcGhKXQmTsAGLi+xMfG0tCTARx\nURHERDqJiXQQE+kkPtJBdISzeVmk0/pyOsLzBrt3714SEhJITk62E5kyxlBaWsrBgwfJycnp0j56\nTeJQqq9raqhjxwf/ot+mpxles5lMYKtrGMujr6Aq9XRk6HQGD8lmeGocFw/sR0J0RK+/mdbW1pKd\nnd3rf8/OEBGSk5MpKSnp8j56ROIQkeeB2UCKiBwEfmGMeSK0USkVHmrr6lnzxsOM3vEQE0wx+xjM\norQ7iJ/2FSaNn8jXEqJDHWJIadI4VXfPSY9IHMaY60Mdg1LhxhjDB6tWMmjFj5ht8tgdOYp1M37L\nlPOvITvCGerwVC/WIxKHUqqlsqp63nny91xb8jfqHLHsmvVnRs1ZwAj9dK2CQBOHUj1M/pHjbHn8\nW9zY+B8Kks8i45anGZWQFuqwVBA1NjYSERG623ePGAColLJsKTjKrkeu58rG/1A06VsMvesdnJo0\nwt6VV17JtGnTmDBhAo899hgA8fHxfO9732PChAnMmTOnubF69uzZ3H333UyZMoWJEyeydu1aAH75\ny19y4403MnPmTG688UZqa2u55ZZbmDRpElOnTmX58uUAPPDAAyxYsACAzZs3M3HiRKqrq/36+2iJ\nQ6keIr/oBIULb+RSPub4zJ8x6KIfhjqkHuVXb21l26ETft3n+Iz+/OKKCR1ut3DhQpKSkqipqWH6\n9OlcffXVVFVVkZubywMPPMCvf/1rfvWrX/H3v/8dgOrqajZu3MiqVatYsGABW7ZsAWDbtm2sXr2a\nfv368ac//QkRYfPmzezYsYO5c+eSl5fH3XffzezZs3n99df53e9+x6OPPkpsbKxff28tcSjVA5TX\nNLD28bu52HxM2dn/ywBNGj3Kgw8+yOTJkznzzDMpKChg165dOBwOvvrVrwJwww03sHr16ubtr7/e\n6g907rnncuLECY4fPw7AvHnz6NevHwCrV6/mhhtuAGDs2LEMGzaMvLw8HA4HTz31FDfeeCPnnXce\nM2fO9PvvoyUOpcKcMYZXnvg/bm14jeIxXydNk0aX+FIyCIQVK1awdOlSPvnkE2JjY5k9eza1tbWn\nbOfZRbZ1d1n3z3FxcT4dc9euXcTHx3Po0KFuRN42LXEoFebeWf4h15X8hUMDc0m79sEuTZ2hQqe8\nvJyBAwcSGxvLjh07WLNmDQAul4tXXnkFgOeee45Zs2Y1v+fFF18ErFJFYmIiiYmJp+z3nHPO4dln\nnwUgLy+PAwcOMGbMGMrLy/nOd77DqlWrKC0tbT6GP2mJQ6kwdqTsBDkr78bliGLwzU+DU/9le5pL\nLrmERx55hHHjxjFmzBjOPPNMwCo9rF27lt/+9rekpaU1Jwuw5pKaOnUqDQ0NLFy40Ot+v/3tb3PH\nHXcwadIkIiIieOqpp4iOjuaOO+7gzjvvZPTo0TzxxBOcf/75nHvuuaSl+a8ThZhTp6Ds8XJzc40+\nyEn1Bm8++H3mlz1B8WVPkDbjmlCH0+Ns376dcePGhToMr+Lj46msrDxl+ezZs7n//vvJzc0N6PG9\nnRsR2WCM6fDAWlWlVJj6YvMmLi59ml0pczRpqLCi5V6lwpAxhvq3fkCTOMm8/q+hDkcFgLfSBliN\n6eFOSxxKhaH1y98gt34deWPvpF/y0FCHo1QLmjiUCjPG5aL/x7+nSFKY9GXteqvCjyYOpcLMjhXP\nM6Yxj70TvkNEtH9H/CrlD5o4lAonLhfxH/+RvQxh6rzbQx2NUl5p4lAqjOxd8zpDG/exd/ztREf1\n7Qcw9QbHjx/n4Ycf7vL7Z8+eTTgOLdDEoVQYca3+K4dNMtO/9I1Qh6L8oLuJI1xp4lAqTJTv+oQR\n1ZvYmPk1EuK0baM3uOeee9i9ezdTpkzhe9/7HnPmzOH0009n0qRJvPnmmwDs27ePcePG8c1vfpMJ\nEyYwd+5campqmvfx8ssvM2PGDEaPHs2HH34Yql+lBR3HoVSYOPre/WBiGXnJnaEOpXd69x44stm/\n+0yfBJfe1+bq++67jy1btrBx40YaGxuprq6mf//+HD16lDPPPJN58+YB1qSEzz//PP/85z+59tpr\nefXVV5tnvm1sbGTt2rUsXryYX/3qVyxdutS/v0MXaOJQKgw0HS9kWMky3om/mvlDB4c6HBUAxhh+\n8pOfsGrVKhwOB4WFhRQVFQGQk5PDlClTAJg2bRr79u1rft9VV13ldXkoaeJQKgwcWPoIObhImKlt\nGwHTTskgGJ599llKSkrYsGEDkZGRZGdnN0+vHh19siOE0+lsUVXlXud0OmlsbAxu0G3QNg6lQq2p\nkcTtz/OJTGbWjBmhjkb5UUJCAhUVFYA1vXpaWhqRkZEsX76c/fv3hzi6rvNbiUNEooDLgHOADKAG\n2AK8Y4zZ6a/jKNXblHz+NqlNJZSM+SFREfpZrjdJTk5m5syZTJw4kenTp7Njxw4mTZpEbm4uY8eO\nDXV4XeaXadVF5GfAVcAqYANQDMQAo4HzAQF+aIzZ0u2D+UCnVVc9ye6/XErcse2Yu79gcFL/UIfT\nq4TztOqh1p1p1f1V4vjCGPObNtb9UUQGAzpTm1Kt1JUVkHP8E95N+jpf0qShegi/lIuNMW8CiMgp\nQ11FJMkYc9gYs9Yfx1KqN8n74GkcGAbNujnUoSjlM39XqK4VkenuH0RkPvCJn4+hVK/Rb+fr7HCM\n5PSpgX3aW1/WG59y2l3dPSf+7o57E7BQRJZgNZAPAS7y8zGU6hX27dzEyMZdfDLy+zgcEupweqWY\nmBhKS0tJTk5GRM8xWEmjtLSUmJiYLu/Dr4nDGLNRRH4BPA9UAGcbYw748xhK9Rb7Vj5NlhHGzLkp\n1KH0WpmZmRw8eJCSkpJQhxJWYmJiyMzM7PL7/Zo4RORRYDwwBRgDvCsiDxhjHvXncZTq6eoaGhl2\naDH5sZMZPTg71OH0WpGRkeTk5IQ6jF7H320cu4BzjTH5xph3gLOAs/18DKV6vDUfryCHQzDpmlCH\nolSn+buq6v5WPx/DavdQSnmoXPc8DUQw8tyvhToUpTrNLyUOEXlDRC4VkVMSkYgME5Gfi8gCfxxL\nqZ6u8FgVUyuWUTDwLBzxyaEOR6lO81eJ407gB8BDIlIElGCNHM8BCoCHjDGv+ulYSvVoG1e/y5ek\njJLp14U6FKW6xC+JwxhTCHwf+L6IjAQGY81VtdMYU+GPYyjVW0Ruf41aoknN/XKoQ1GqS/w+rbox\nJh/I9/d+leoNSssrya1ayb7U8xgbFRfqcJTqEr/2qhKR+SKyXUTKReSEiFSIyAk/7PcSEdkpIvki\nco8/YlUqFLaufpMkqaTf6V8NdShKdZm/u+P+CbjWGJNojOlvjEkwxnRr5jYRcQIPAZdijRG5XkTG\n+yFWpYIuaturnCCerBlXhDoUpbrM34mjyBjj54f6MgPIN8bsMcbUAy8A8/18DKUCrqryBJMqV7Mr\n5QIk4pT5QJXqMfzSxiEi8+yX60TkWeANoM693hizqBu7H4LVM8vtIHBGN/anVEjkrXqZqVKn1VSq\nx/NX4/hXPF67gHkePxugO4nDJyJyG3AbQFZWVqAPp1SnObe9SjFJjJ5+cahDUapb/NUd90Z/7KcN\nhbR8CFSmvax1DI8Bj4H1BMAAxqNUp9VXlDGucg2fJF/NuZGRoQ5HqW7x9ySHKcACINtz38aY27qx\n23XAKBHJwUoY1wE6T4PqUfasep6xNBGj1VSqF/D3OI43gTXAaqDJHzs0xjSKyF3AEsAJLDTGbPXH\nvpUKFufWV9hPOlNmnB/qUJTqNn8njjhjzA/8vE+MMYuBxf7er1LBUHfsICOqP2dp6k0Mi3SGOhyl\nus3f3XHfFZG5ft6nUj3a/pX/xoEhcYbOTaV6B38njtuB/4hIpYiUicgxESnz8zGU6lGit7/KdoYz\ndar2Ile9g78TRwoQCSQCqfbPqX4+hlI9Rtn+rQyry6Nw6OVERfj7302p0PD3g5yaRCQRGIE1rbrb\nx/48jlI9xZ5lTzLACCP1ueKqF/F3d9xbsaZXHwJsBqZj9bKa7c/jKNUTGJeLwQfeYkv0ZE7LHhnq\ncJTyG3+Xnb8L5AL7jDHnANOAUj8fQ6keYfPaZQwxR2gYr88VV72LvxNHrTGmBkBEouzxFmP8fAyl\neoRja56ljkgmzLkh1KEo5Vf+muQwwhjTCBwWkQHAW8ASu0fVQX8cQ6me5FhFNROOfUD+wFlMSBgY\n6nCU8it/tXGsBU43xrgnN/yZiMzB6l31jp+OoVSPsX7pi1wk5dSeqaUN1fv4K3FI6wXGmA/8tG+l\nehRjDPFbn6PMkUTm9CtDHY5SfuevxJEqIt9va6Ux5s9+Oo5SYW/zjh3MaFhH3sgFJDn9PauPUqHn\nr6vaCcTjpeShVF9TsPwJThPDsAu/FepQlAoIfyWOw8aYX/tpX0r1WGWVtUwsWsTe+KnkDNYOhap3\n8ld3XC1pKAV89P5rDJMiYs7QkeKq9/JX4pjjp/0o1WM1uQxJm5+g3JHI4LOuD3U4SgWMXxKHMUZn\nwFV93qfr13FW0waKR38dImM6foNSPZRO16mUn1SueogmcZJ9yX+HOhSlAkoTh1J+sL/wMGdVLCE/\n9UIiB2SEOhylAkoTh1J+sOutP5MgNQya6/cnJysVdjRxKNVNpWWlTDv8HNsTziZp1IxQh6NUwGni\nUKqbtr35ZwZKJQkX/yTUoSgVFJo4lOqGqvIyJuz/F1v6TSdz4jmhDkepoNDEoVQ37Hj55wwwlUTO\n/UWoQ1EqaDRxKNVF5YV5TCp4nk8S5jJmqpY2VN+hiUOprjCGope/RyNOBn35d6GORqmg0sShVBcc\n/uhZRh9fzbL0BYwcMSrU4SgVVJo4lOokU1lM3LKfsJmRnPX1n4c6HKWCThOHUp3hauLwkzcS3VRN\nwTl/JLl/bKgjUiroNHEo1QnFb/+ajNI1PJd8F5decEGow1EqJDRxKOWjE2ufJe2zv/COzGbeLfcg\noo+hUX2TJg6lfFCz7T/ELv5vPjXjGXLjIyQn6LTpqu/SxKFUByo2vEzES19nh2so1V9+hinDB4c6\nJKVCShOHUm1xuTjyzr3EvfVNNpkRlF79CudPGRnqqJQKuYhQB6BUOKotO8iRf99GdtlHLHWcTdLX\nH+e8EUNCHZZSYUETh1Ie6mqr2L7oL4za9jcGm0ZeSP0Oc2/+X5Lio0MdmlJhQxOH6vOMMezYs49D\nK5/gtAP/ZgrHWBcxDXPpH7luWm6ow1Mq7IR14hCRrwC/BMYBM4wx60MbkeoN6hqb2FNSxa78fCq3\nvceQog84q2kD46SJbTFTOHz2g+TOuhxxaBOgUt6EdeIAtgBXAY+GOhDVMzQ2uThR20h5TQNlVXUc\nLq/lSFklVUcLcJXtJaZ0G4Oq85goe5jnKASg3JnE7uE3MOTcWxifMzXEv4FS4S+sE4cxZjsQtIFW\n1dWVNNTVY04e3zOaFstOfrfWGTy2dRmPd3hsi7GWemzqfX+tj+ne1uW5uPmY1ntbHdNlaLnxyR2f\njKvlft2/S4v4cLV8u/E4TstgcRlDk8tgjKHJGIzLRZMxuFzgMi5cLmhyGVzG4HK5cAEul/s9Lppc\nNK9rMtDQ6KK+yUV9QyOuxlpcDXXWV2MdNNZh7K+m+jpoqCKq/gT9XJUkShWJVDFAKpgqpaRThlNO\nxloZk0JV0jiODr+J5NMuIXHQJBK1dKGUz8I6cQTb1se+wfTj74Y6DNVVDqiPjKU+sj+NUf0xMQNx\nDJhEQ3I2juQsZGAWDJpIfHwa8aGOVakeLOSJQ0SWAuleVv3UGPNmJ/ZzG3AbQFZWVpdiiZlyDWsL\nx7XesecxWh/V41vLdSe3FS/fpMXm4h5OI3h5j7Ra1dZxTl3X3ntFWn7CbrmfFu9qfl/r/bRaioi1\nX4eAQwQRweGwvws4xIHY6xwC4nDgAI9t5OR6h4NIhxDhdOBwCDijISIanFGtvkdDRBRExkJMIlHO\nSKJQSgWSmFbVDeFIRFYAP/S1cTw3N9esX6/t6Eop1RkissEY02FXQq3YVUop1SlhnThE5MsichA4\nC3hHRJaEOiallOrrekRVVWeJSAmwv4tvTwGO+jEcf9G4Okfj6hyNq3N6a1zDjDGpHW3UKxNHd4jI\nel/q+IJN4+ocjatzNK7O6etxhXVVlVJKqfCjiUMppVSnaOI41WOhDqANGlfnaFydo3F1Tp+OS9s4\nlFJKdYqWOJRSSnWKJg6llFKd0icTh4h8RUS2iohLRHJbrfuxiOSLyE4RubiN9+eIyKf2di+KiN+n\nR7L3u9H+2iciG9vYbp+IbLa3C/g8KyLySxEp9Ijtsja2u8Q+h/kick8Q4vo/EdkhIl+IyOsiMqCN\n7YJyvjr6/UUk2v4b59vXUnagYvE45lARWS4i2+zr/24v28wWkXKPv+/PAx2Xfdx2/y5iedA+X1+I\nyOlBiGmMx3nYKCInROS7rbYJ2vkSkYUiUiwiWzyWJYnI+yKyy/4+sI333mRvs0tEbup2MMaYPveF\n9WCoMcAKINdj+XhgExAN5AC7AaeX978EXGe/fgS4I8Dx/gn4eRvr9gEpQTx3v8SaN6y9bZz2uRsO\nRNnndHyA45oLRNiv/wD8IVTny5ffH/g28Ij9+jrgxSD87QYDp9uvE4A8L3HNBt4O1vXk698FuAx4\nF2tWzTOBT4McnxM4gjVALiTnCzgXOB3Y4rHsj8A99ut7vF33QBKwx/4+0H49sDux9MkShzFmuzFm\np5dV84EXjDF1xpi9QD4ww3MDsaaRvQB4xV70L+DKQMVqH+9a4PlAHSMAZgD5xpg9xph64AWscxsw\nxpj3jDGN9o9rgMxAHq8Dvvz+87GuHbCupTkS4AfPGGMOG2M+s19XANuBIYE8ph/NB542ljXAABEZ\nHMTjzwF2G2O6OiNFtxljVgFlrRZ7Xkdt3YsuBt43xpQZY44B7wOXdCeWPpk42jEEKPD4+SCn/mMl\nA8c9blLetvGnc4AiY8yuNtYb4D0R2WBPLR8Md9nVBQvbKBr7ch4DaQHWp1NvgnG+fPn9m7exr6Vy\nrGsrKOyqsanAp15WnyUim0TkXRGZEKSQOvq7hPqauo62P7yF4ny5DTLGHLZfHwEGednG7+cu5M/j\nCBTx03M+AsnHGK+n/dLGLGNMoYikAe+LyA77k0lA4gL+AfwG6x/9N1jVaAu6czx/xOU+XyLyU6AR\neLaN3fj9fPU0IhIPvAp81xhzotXqz7CqYyrt9qs3gFFBCCts/y52G+Y84MdeVofqfJ3CGGNEJCjj\nK3pt4jDGXNiFtxUCQz1+zrSXeSrFKiZH2J8UvW3jlxhFJALrmevT2tlHof29WERex6om6dY/nK/n\nTkT+CbztZZUv59HvcYnIzcDlwBxjV+562Yffz5cXvvz+7m0O2n/nRKxrK6BEJBIraTxrjHmt9XrP\nRGKMWSwiD4tIijEmoBP6+fB3Ccg15aNLgc+MMUWtV4TqfHkoEpHBxpjDdtVdsZdtCrHaYtwysdp3\nu0yrqlpaBFxn93jJwfrksNZzA/uGtBy4xl50ExCoEsyFwA5jzEFvK0UkTkQS3K+xGoi3eNvWX1rV\nK3+5jeOtA0aJ1fssCquYvyjAcV0C/A8wzxhT3cY2wTpfvvz+i7CuHbCupWVtJTt/sdtQngC2G2P+\n3MY26e62FhGZgXWPCGhC8/Hvsgj4L7t31ZlAuUcVTaC1WeoPxflqxfM6autetASYKyID7arlufay\nrgtGb4Bw+8K64R0E6oAiYInHup9i9YjZCVzqsXwxkGG/Ho6VUPKBl4HoAMX5FHB7q2UZwGKPODbZ\nX1uxqmwCfe6eATYDX9gX7eDWcdk/X4bVa2d3kOLKx6rH3Wh/PdI6rmCeL2+/P/BrrMQGEGNfO/n2\ntTQ8COdoFlYV4xce5+ky4Hb3dQbcZZ+bTVidDM4OQlxe/y6t4hLgIft8bsajN2SAY4vDSgSJHstC\ncr6wktdhoMG+f92K1S72AbALWAok2dvmAo97vHeBfa3lA7d0NxadckQppVSnaFWVUkqpTtHEoZRS\nqlM0cSillOqUXtkdNyUlxWRnZ4c6DKWU6lE2bNhw1PjwzPEekThE5ErgS0B/4AljzHvtbZ+dnc36\n9QGf708ppXoVEfFpSpWAV1V5m9HRXu7z7KnGmDeMMd/E6gb31UDGq5RSqn3BKHE8BfwdeNq9QESc\nWH2yL8Lqj7xORBZhzUB5b6v3LzDGuEdD/q/9PqVO4XIZiivqKDxeTU29C4dAemIMQ5NiiXR2/zNS\nTX0TXxw8ztj0/iTGRnrdxhjDruJKhiXHEh3hbF5eUFZNTKST1IRo8ooqyBzYj9iok/9+JRV1NLpc\nDE7s1+bx84srqG1wMSGjP/6YD7G8poEDpdWUVdcT6RT6x0SSnRJHfHSPqIhQIRTwK8QYs0pOfdZA\n8+yhACLyAjDfGHMv1pQRLdgjM+8D3jX27J5KGWPYUniCZTuKWb+/jM/2H6OqvumU7eKinJw3JpWb\nz85hRk5Sl46180gF/7XwU4pO1NE/JoJnbj2DyUNPfeTHn9/P42/L8jl3dCpPL7AmVq6sa+Syv35I\nemIMD3x1Cpf/bTXzJmfw4PVTAThQWs0lf11Fk8vwxp0zGTe4/yn7/dN7O/nbsnwALp2YzoPXT+1S\nMqypb+LlDQW8vP4gWw6V420YV0ZiDDNykjhzeDIXjEsjLSGm08dRvVuoPlp4m63xjHa2/2+s6TcS\nRWSkMeaR1hvYM2reBpCVleXHUFW4ySuq4OX1Bby75QgHj9UgAmPT+3PV6ZmMTk8gc2A/4qMjaGwy\nHDpew2cHjvGfLUdYvPkIl05M5/dfnsTAON+fvVVd38g3nl6HMfDX66bwx//s5LsvbuT9751LhMfN\nu7ymgUdX7QFgVV4JWwrLmTgkkQ/zSqioa6SiuJI3PremV1q06RB/+eoUHA5h4Ud7qbYT3t+X5/PQ\n11o+o+jTPaX8bVk+V00dwrDkOB5Ymsf97+3kx5eO69R5W7OnlB+9somCshomDUnku3NGMyY9gZT4\nKBpdhuPV9ew9Ws2WQ+Wszi/ljY2HEIEZ2UlcPjmD+VMy6B/jvaSl+pYeUSY1xjwIPNjBNo+JyGHg\niqioqDYnBVQ9U11jE//ZcoRn1xxg7b4yIp3CrJEpfGfOKC4aN6jdRHD1tEz+90vjWfjRXv6yNI+8\nogqe++aZDOrv2yfpR1bspqCshhdvO5MzhicTHeHk9n9vYMnWIr502smpu97fVkR9o4snb57OLU+t\nY9WuEiYOSWTHkYrmbZZuPzlP3oGyarJT4liVV8LsMakMGdCP1z4rpLahiZjIk9VcD6/YTUp8NL+/\nahIxkU4Kj1fzxId7+WruUIanxvv0O7y5sZAfvLSJrKRYnvvmGZw9IqXd7Y0x7CyqYPHmIyzefJif\nvbGF37+znXmTM/jaGVleS1uq7wjVOI5AzZ76ljHmtsTExO7uSoWJ6vpGHlu1m5n3LePuFzZSVFHL\nTy4by6c/uZAnb5nBtblDfSo99Itycuf5I3n2G2dypLyWmxaupcZLtVZrVXWN/OuT/Vw8YRBnDLce\nl3HR+EEMTozhzY0tL9l1e8tI7BfJeaNTGZEax4Z9xwDYVXwycewrPTn/4v6yao5V1bPnaBVnDU/m\n/DFp1DQ0sbmwvHmbY1X1rM4/yrW5mc3J5EcXj8XpEB5fvbfD+AE+zj/K91/axLRhA3njrpkdJg0A\nEWFsen++f9Foln7/PBbdNZP5UzJYtOkQ8x/6iKv/8THvbyvC5dIpi/qiUCWOgMyeKiJXiMhj5eXl\nHW+swpo7YZzzh+X8fvEOxg3uzzO3zmD5D2Zz27kjSOpEVZOnGTlJPHzDNHYWVfDrt7d2uP1bmw5R\nXtPAbecOb17mdAgXjE1jdf5R6hpPJp/PDhxjatYAHA7rpru7pBKAwmM1jPdotxgzKAGAA6VVzduM\nHpTQ/Cl+U8Hx5m1X5BXT5DJcMvHkY0hSE6KZNzmDNz4v5ERtQ7vxl1c38J0XPicnJY7Hb8rtclXT\naZkDuO/q01j70zn88orxFJ2o5ZtPr+eiB1by0rqCFudB9X7B6I77PPAJMEZEDorIrcZ6jsVdWFP7\nbgdeMsZ0/F/cAS1x9HytE8b4jP68esdZPHPrGZwzKhWHo/u9ic4bnco3zxnO82sL+OzAsXa3fWfz\nYbKTYznYkjeFAAAgAElEQVQ9q+WDDmeNTKG6vonth63SRG1DE/kllZyWad38h6fGUXCshvpGFyUV\ndYwadLJKaUx6AhEO4ciJ2ubEMTw1jtSEaDISY1qUOD7bf5z46AgmZrS8pq+bMZTq+iaWbff2+IWT\n/rBkB8eqG/jrdVNI8EP7REJMJDfPzGHFD2fz4PVTiY5w8j+vfsF5f1zBM5/s0wTSRwSjV9X1bSxf\njDVVud+IyBXAFSNHjvTnblUQVNc38u81+3l05R5Kq+o5Z1QK371wFNOGda0XVEe+M2cUr39eyJ/f\ny+Pf3/DeL6Osqp6Pd5dyx3kjTun+eppdOthcWM6UoQM4UFaNMTAiNQ6AYclxNLkMBceqKamsI2NA\nP+KinFTVN5EUF8XAuChKK+txGYhwCJkDYwEYkRbPvqNVzcfZdPA4p2UmnpIwpw4dSFpCNEu2HuHK\nqd6fAlpQVs2L6wq44YwsJmT498NUhNPBvMkZXHHaYD7cdZQHP9jFz97cysMrdvPt80dybW5mi+7I\nqnfpVXNVaYmj52mvhBGopAEQHx3Bgpk5rM4/ypZC71WbH+UfpclluHD8qY9xzkiMITkuii0Hrfe6\nb/bZyVbiGNQ/GoD84koamgyp8dH0s8dt9ItykhwXxdHKekoq6kiJj8ZpJ4bs5LjmdpCGJhfbD59g\nUuap17PDIVw0fhAr80poaHJ5jf+J1XtxCNw+e4TP56WzRIRzR6fy8u1n8e9bzyBjQD9+9sYWzv+/\nFTz76X7qG73Hpnq2XpU4tI2j5whVwvD0tTOyiIl08OK6Aq/r1+wptauJTh1XISKMSItnz1Grqmlf\nqZ04UqzEkZpgJY5th6wni6YkRBMdYf27RUc4SImPpqyqjqOVdaQknGyvGZYcS3lNA8er6yk8VkND\nk2FEGz2nzh5hVZd5S3x1jU289tlBLps0uN1Bhf4iIswalcIrt5/FM7fOID0xhp++voXz71/Bc58e\n0ATSy/SqxKEljvAXDgnDLbFfJHPGDWLx5sM0evnUvmZPKTNyklqM1fA0LCmW/Xbp4OCxGhL7RZLY\nz2pHSI23Eoe7DWNAv0jctV3REU6S4qIoraqntLKe5Ljo5n0OTbKqrArKathfVt18HG+m51jtLuv2\nlZ2ybsXOEk7UNvLlNqqxAkVEOGdUKq/ecTZPL5hBWv9ofvL6Zi740wpeWlfQZulI9SztJg4RcYjI\ntcEKpru0xBG+wilheLritAxKq+pZ2+rmW17dwO6SKnKzB7bxTqt0UFxRR3V9IyUVdc2lDICBsVE4\nHcJeuworPiYCh505oiMcJMREUFHbaJU44k++L83eR0llLQfsUswwu/qrtbSEGHJS4li799QG/iVb\njjAwNpKZIzvuehsI7iqs1+44m6dumU5yXBT/8+oXzPnTSl7ZcNBrolY9R7uJwxjjAv4nSLF0m5Y4\nwk+4Jgy3mSOTcTqEj/KPtli+7bBVxdReo7K7dFB4rMZOACernBwOoX9MBIeO1wCQEB3RXOKIiXQS\nHx1BZV0jpZX1Ld7nTj4lFXUUHKshOsLRnEy8mTgkke12rG7GGFbnH2XmyBS/zNHVHSLC7DFpvHHn\nTJ64KZf+/SL44cubuOiBVbz++UGadBxIj+TLVbVURH4oIkNFJMn9FfDIVI8W7gnDLSEmksmZiXyU\nX9piuftmPG5wQpvvdVdHlVTWcbSyvkXJAaxSxrFqa5xFXHRE87xQ0REO4qIjqG90Ud/kIiHmZOdG\n9z5KKuo4apdi2uuCPDY9gcLjNS3Gc+wuqaS4oi5kpQ1vRIQ54wbx1l2zeOzGacREOvnei5uY+8BK\n3txYqAmkh/GlO657GvM7PZYZYLiXbUNKu+OGXlWd1a32sVXB6VbrD2cOT+bRVXtaTPWx/fAJkuOi\nmpODNyl2SaC0sp6jFXWnJo7oSMAqccTHRDTfHKMjrcTh5jlLbkykk/4xEVbiqKonuZ3jw8nElnek\ngtxs6xy7q67Oske6hxMRYe6EdC4cN4glW4/wl6W7uPuFjfx9WT7fvXA0l05M98tYHRVYHSYOY0xO\nMALxB2PMW8Bbubm53wx1LH1NZV0jT3+yj8c/3EtZD0kYbqdlJtLkMuw4UsEUe3xGXnElY9IT2p2+\n3J0oCo/XUFHX2KKNA6zqKbe4qAgaXVa9fnSEk/jok2McWk9jnhwfbTec15HewXxaY9KtHl87PBLH\ntsPlJMREMCzZe6N6OHA4hEsnDebiCeks3nKYvyzdxZ3PfcbY9AS+e+Eo5o7XBBLOOkwcIhIJ3AGc\nay9aATxqjGl/rgPVJ5yobeDpj/fx+Oq9HK9uYPaYVP77glFMG9Z2o3K4cbdjbD1U3pw4CsqquXhC\nentvY0C/SJwOIb/Y7jnV6hkd8XYVVGyUE6dDmquqIp3SssQR3XKgnLvhvLSyngleugJ7Gtw/hqgI\nBwVlJ+fA2nboBOMH++eZHYHmcAiXn5bBpRMH8/YXh/jr0l3c/u/PGD+4P9+9cBQXjR/UI36PvsaX\nqqp/AJHAw/bPN9rLvhGooFT4K69p4MmP9rJw9V5O1DYyZ2wa35kzqkfOmpo5sB/9YyKax1xU1jVS\nVlXP0KT2xz84HEJSXBQH7C65rUsO7p9jo6zE4O5V5RAhLqplacRTQkwEJ2obKK2qIymu/aoqh0PI\nHNCPgmNWDC675HRt7tB23xdunA5h/pQhfGnSYBZtOsSDH+zitmc2WNO/XziKC8amaQIJI74kjunG\nmMkePy8TkU2BCqg7tI0j8I5V1fPkR3t58qN9VNQ1Mnf8IL4zZxQTh/TcnmwiQk5qfPOYDPen96w2\nxk94SoiO4MiJWuDUxBET6R7w57SPYy13iOCMOHkTdCeWk/uMZG9JFQ1NhqS4jueXykyK5YAd86Hy\nGqrrmxg9qO1G/XAW4XRw1emZzJucweufF/Lgsl3c+q/1TMjozx2zR3DpxMHNo+xV6PiSOJpEZIQx\nZjeAiAwHwnImM23jCJzC4zU8/uEeXlhbQE1DE5dNSueu80cxvoOqlJ5iWFIsnxdYjcruxDF0YMeJ\nIz4mgh32RIetE4c7YUQ6rRud+3bncNCim2xc9KkljqKKOq/rvMlK6tc8o+7BYzX2svBt3/BFhNPB\nV3KHcuXUIbz+WSGPrNzNXc99TnbyTr513giuOn2IzoUVQr4kjh8By0VkD9a1Pwy4JaBRqbCx48gJ\nHl25h0WbDiHAvCkZ3H7eiB77ibYtw5JjeWfzYRqaXM1jL4YM7HiqjvjoCOrtwWytb/JR9hQj7u/i\nUVUV5ZE4+rUuccRENvfAal2N5c2QAdY0JVV1jSeTXgfVbD1FpNPBtdOHcvW0TN7beoSHV+zmx69t\n5oH387h1Vg5fOyPLL7P+qs5p96oUEQdWf8JRwBh78U5jTF2gA1OhY4zh071lPLpyN8t3lhAb5eTm\ns7NZMCuHIQN6xw2ptaFJsTS5jD2Yrx6HQFJsx8/88CxltC5xuBOGu3ThWVXlXge0SCJAi3Edraux\nvHEPICytrKfAfpRuMOanCian3QvrkonpfJRfyj9W5nPvuzt4aHk+/3VWNjednX1KrzYVOO0mDmOM\nS0QeMsZMBb4IUkwqRBqbXLy3rYjHVu1hY8FxkuOi+OHc0dxw5jAG+HAT7cky7Btt0Yna5kZpX7qD\ntkgcMa0Sh50Q3HNduRvHna2qqiKcLY8T59HLypeqqpSEkwMRDx6rbu5p1Ru5J1OcNSqFTQXHeWTl\nbh5akc9jq/Ywf0oGt8zM6TXVp+HMl6qqD0TkauA1Y0xYD+/UxvGuOV5dz/NrC3jmk30cKq8lKymW\n31w5ka9My2zx7OvezD1D7clR4L4lSs9kcUobR2TLm7c7D4lI80y5wCnTgnjW3ftU4ohzD0Sso/hE\nHWk+Pku9p5s8dAD/uGEau0sqeeqjfbyy4SAvbzjIWcOTuXVWDheMTdOxIAHiS+L4FvB9oFFEarHa\nOYwxJuzSujaOd87OIxU89fE+Xv/8ILUNLs4ekcyv5k/kgrFpfa7ninuE+NGKOkor60j2MXG4R32L\n0CIZgEcVlP15q0Ubh2ficLROHG03nHvjTnpHK+spraonI7FvJA63Eanx/ObKifxg7mheWFfAvz7e\nxzeeXk92ciy3zMzhmmmZPp1H5buO2jgEmGCMORCkeFSANbkMy3cU8+THe/kov5ToCAdfnjqEm2dm\nMzY97D4LBI17Nlt3iWOKj+NRPNsxWo8zaJ1I3JytGsdbV1V5llR8KXG4n79eWllHWVWd1+eH9AUD\nYqO4/bwR3DorhyVbj/DE6r38YtFW7n9vJ1+ZNpSvn5nV5rNNVOd01MZhROQdYFKQ4lEBUnyilhfX\nFfDCugIKj9eQ3j+GH108hutnZDXfePoyh0Osp/JV1FNWVe/zOWkrOcDJpNK6fleEFiWOUxKHR1WV\nL72qrClMIiirtmP3sbTUW0U6HVx+WgaXn5bBZweO8eRH+3hmzT4WfrSXs4Ync8OZw7ho/KBe2w4U\nDL6U3z4TkenGmHUBj0b5lctl+DD/KM99up+l24tpchlmjkzmJ5eNY+6EQSGfcjvcWA9XqqOyrpH+\nMb5VbTQnDi+tf+4bk8uuqnI3EXZUVRXjUeLw9eaWEBPBkfJaGpoMyfpBoNnpWQM5PWsgRyvH89L6\nAp779AB3PvcZKfHRfHV6JtfPyGp+3rvynS//HWcAXxeR/UAVJ9s4TgtoZKrLSirqeGl9AS+sO0BB\nWQ1JcVF8Y1YO183IIifF+0OBlD3w7oTV07x1D6m2nCxVnJo5nHZCcH93zxzucFjVVW6tG3A9Sxyt\nSyNtiY+OaB753tE0JX1RSnw03549km+dO4JVu0p4ds1+/rFiNw+v2M35Y9K4bvpQzh+bph+mfOTL\nf8fFAY+iHSIyDrgbSAE+MMb8I5TxhKuGJhcrdpbw6oaDLN1eRKPLcObwJH508VgunjBIR9n6ICEm\nkn2l1tMjfW1MbT0Gw5OzuTHc+tmdXJwi7fb2iW6nNNKW+JgI9tlPG/S1tNQXOR3C+WPSOH9MGoXH\na3hh7QFeWFfAsh3FpMRHceWUIXwldyhj0nvXAFd/a/MKE5ELjDHLjDH7RSTHGLPXY91VwP6Odi4i\nC4HLgWJjzESP5ZcAfwWcwOPGmPva2ocxZjtwuz0Y8WmsCRaVbduhE7z62UHe+LyQ0iqrG+nNZ2dz\n/RnaENhZ8dHWczDcr33RXOLwUlXVOqe4t+losj7PJO9rd9L46JYPjVIdGzKgHz+YO4bvzBnFyp0l\nvLyhgKfsmZ4nZyZyTe5Q5p2WQWKsjkxvrb0r7H7gdPv1qx6vAf4XeM2H/T8F/B3rhg+AiDiBh4CL\ngIPAOhFZhJVE7m31/gXGmGIRmYc1tfszPhyz1ztaWcebGw/x6oaDbDt8gkincOG4QVx9eibnjUnV\n4nYXeY7Y9qVRGtpuAIeTA/7cVVTuxNFRLuhKo21nR5urkyKdDi4cP4gLxw+itLKONzYe4uX1Bfzs\njS385u1tXDwhnWumZTJzRHLzYM6+rr3/DmnjtbefvTLGrBKR7FaLZwD5xpg9ACLyAjDfGHMvVunE\n234WAYvsHl7P+XLs3qamvoml24t4c+MhVuwsptFlOC0zkV/Pn8AVp2UwUBtEu81zziNf2zjcpQNv\nY2Obx8K0WtfRGJmujKHxLCFpiaPrkuOjuXVWDgtmZrP10AleXl/Am5sO8damQ6TER3H5aRnMm5LB\n1KED+vQ07+1dYaaN195+7owhQIHHzwexGuC9EpHZwFVANLC4ne1uA24DyMrK6kZ44aO+0cWqvBIW\nbTrE0u1FVNc3kZZgXdhXT8vsdRMNhlpCO6PA29JuicNOAO51nr2q2hPRhcTR8lG0WuLoLhFh4pBE\nJg5J5CdfGseKnSW8ubGQ59Ye4KmP95GVFMu8yRlcOTWDkWl97/+wvf+O4XYVkni8xv45aI+TNcas\nwHrqYEfbPSYih4EroqKipgU6rkBpchnW7Cll0cZDvLvlMCdqGxkYG8mVU4dwxWkZzMhJ6nOjuoOl\nsyO2ASIdJ0eDt+Zsrqqyu+Payzv6oNqVv29nx34o30VHOLl4QjoXT0inoraBJVuLeHNjIQ+vyOfv\ny/MZP7g/86dkcPnkjF47CWhr7V1h8z1e399qXeufO6MQ8Hw8Waa9rNt66pQjTS7D+n1lvLvlCG9/\ncZijlXXERVkX6xWTM5g1KkXbLYIg2mNerphI3863u1Th9JY43CWOU9o4/F9V5Zn0Wj+KVvlPQkwk\n10zL5JppmRRX1PLOF4d5c+Mh7n13B/e+u4PJQwdw6cR0Lp2YzrDk3tv1vc3EYYxZGaBjrgNGiUgO\nVsK4DviaP3bckyY5rG908fHuoyzZeoT3thZRWlVPVISDOWPTuGJyBheMTeszEwyGi5h2pjpvi/sm\n7y0XtG4c91VXqqo8pynxNXbVPWkJMdwyM4dbZuawv7SKxZuP8O6Ww9z37g7ue3cHEzL6W0lk0uBe\n18MxoGVaEXkemA2kiMhB4BfGmCdE5C5gCVZPqoXGmK2BjCNc1NQ3sTKvhCVbj7B0exEVtY3ERTm5\nYNwgLpmQzuwxqdqwGUKeJY5IH3s2tVd6cA/ea91wHuiqqr7caBsqw5LjuGP2CO6YPYKCsmqWbD3C\n4s2Huf+9PO5/L48xgxK4ZGI6l05KZ8yghB7/NwroXcoYc30byxfTTkN3N44XdlVVx6rqWZFXzJIt\nRazIK6a2wcWA2EgumZDOJRPTmTkyRUsWYaI7JQ5v4zjcSaX1uo4eTtDdqioVWkOTYvnGOcP5xjnD\nOVxew5ItR1i85QgPLtvFXz/YxdCkflw4bhAXjhvEjJykHlkN7XPiEJFYY0x1IIPprnCoqjLGkF9c\nydLtxSzbUcSG/cdwGUhLiOba3KFcMiGdGTlJ2h88DLUocfiaONzJweuUIy3X+fohUxNH7zE4sR83\nz8zh5pk5FFfUsnRbMR9sL+K5Tw/w5Ef7SIiJ4LzRqVw0fhCzR6f1mMGGHSYOETkbeByIB7JEZDLw\nLWPMtwMdXGeFqsRR19jEp3vKWLajmA92FFFQZj2zekJGf+46fyTnj01jcuYAfahMmPMscfh683bP\nCOKtHaP1MA73Hjtq8ojwcZoRT9Faag17aQkxfO2MLL52RhbV9Y18lF/K0m1FfLCjiLe/OIzTIUzP\nHthcGskO43nlfClxPIA1X9UiAGPMJhE5N6BR9QBHymtZlVfCBzuK+HDXUarrm4iJdDBrZAp3nDeS\n88em9rrnPvd2Xbn5RjRPYHhqOnBXVblLGiLe2zxOeV8XCg9a4uhZYqMiuGj8IC4aPwiXy7Dp4HGW\nbi/ig+3F/Pad7fz2ne1kJ8dy3uhUzhuTypnDk5sfGhYOfIrEGFPQqjGnKTDhdE8gq6pqG5pYu7eM\nVXklfLjrKDuLKgDISIzhqtOHMGfsIM4akaztFT1YV3oztXrIn0862rYrJQ7tSdVzORzC1KyBTM0a\nyI8uHktBWTXLdhSzKq+El9Yf5F+f7CfK6WBGTlJzIhmVFh/SBnZfEkeBXV1lRCQSa6ba7YENq2v8\nWVVljGFnUQUf5h1l1a4S1u4to67RRVSEgxnZSVx1+hDOHZ3K2PSe30NCWXydwtxTe72qmksavs3Q\n47HPToehg0J7kaFJsdx0djY3nZ1NbUMT6/cdY2VeMSvzSvjd4u38bvF2BifGWElkdCpnj0whsV9w\n20Z8SRy3Y81kOwRr3MV7QNi1b/hDaWUdq/OPsirvKB/uKqHYnil1VFo8Xz9jGOeOTuGMnGT66ZQO\nvVLXShztJI5WCcPXzxdd+SCiiaN3iol0MmtUCrNGpfDTL8Gh4zWsyithZV4J73xxmBfWFeAQmDx0\nALNGpjBrZApTswYG/OmGviSOMcaYr3suEJGZwEeBCanrultV9fNFW3nni8MMiI1k1sgUzh2VyqxR\nKWT0kWkE+jpnF6qIOhoFDp5tHNb3zlRr+UoTR9+QMaAf183I4roZWTQ0ufj8wHFW7yrhw/yjPLQ8\nn78ty+dfC2Zw3ujUgMbhS+L4Gy2nVG9rWch1t6rqjvNG8M1zhjNpSKL+I/ZB/i5xeOui297y7uhK\n7Kpni7TbPWbkJPH9uWMor2lgzZ5SZmQnBfzY7T3I6SzgbCBVRL7vsao/1ojvXmfikMRQh6BCqCsf\nFnypVXJXPXW2raMztKu3SuwXycUT0oNyrPZKHFFYYzciAM95g08A1wQyKKVCwd+N48GkJQ4VTB1N\ncrhSRJ4yxnT4mNhwEA4jx1XP1ZVusO3drlu3ZQSyjSNcEpjqG3xp43hKRE651I0xFwQgnm4Jx7mq\nVM/RpXYtX6qq7O9jBiWwv7Q6IGN9ulJaUqqrfEkcP/R4HQNcDTQGJhylQqcr1T2dabd44KtT2FRw\nnPTEmE4fpyNaVaWCqcPEYYzZ0GrRRyKyNkDxKBUyXSlxtPeW5jmq7G3ioiM4e2RKFyLzJQ5NHCp4\nfJnk0LNvlwOYBmj3I9XrdK1XVcfdcYNxT+9K+4xSXeVLVdUGrAk9BauKai9wayCD6iptHFfd0ZVP\n7eHyOV/zhgomX6qqcoIRiD9o47jqjq4kgfZyja/PGPcHHbCqgqm9AYBXtfdGY8xr/g9HqdDpyv29\nvaoq91TrwbilB3JwoVKttVfiuKKddQbQxKF6la5MLthuiaMb+/VnHEr5W3sDAG8JZiBK9US+3K8D\nMN7vFJo3VDB12KQmIoki8mcRWW9//UlEtFeVUrRfmgjqzVwzhwoiX/piLAQqgGvtrxPAk4EMqjUR\nibOT1uXBPK5SHWl3ypGgRaFtHCq4fEkcI4wxvzDG7LG/fgUM92XnIrJQRIpFZEur5ZeIyE4RyReR\ne3zY1f8DXvLlmEqFQnudmoLSOK55QwWRL+M4akRkljFmNTQ/xKnGx/0/BfwdeNq9QEScwEPARcBB\nYJ2ILMKaqv3eVu9fAEwGtmFNd6JUWGp/IGAQjh+EYyjl5kviuAP4l92uIUAZcLMvOzfGrBKR7FaL\nZwD5xpg9ACLyAjDfGHMvcEpVlIjMBuKA8VhJbLExxuVlu9uA2wCysrJ8CU+pbjv5XPHQ0ufeq2Dy\nZQDgRmCyiPS3fz7RzWMOAQo8fj4InNHO8X8KICI3A0e9JQ17u8eAxwByc3ODWb2sVLuD/IJxS9fx\nfyqYfOlVdbedNCqAP4vIZyIyN/ChtWSMecoY83Z724jIFSLyWHl5ebDCUiosaOO4CiZfGscX2KWM\nuUAycCNwXzeOWQgM9fg5016mVI/TfMMO9X071MdXfYovicN9SV4GPG2M2Ur3LtN1wCgRyRGRKOA6\nYFE39tfMGPOWMea2xEQdZqL6Fm3iUMHkS+LYICLvYSWOJSKSAHhtZ2hNRJ4HPgHGiMhBEbnVGNMI\n3AUsAbYDL9nJqNu0qkr1VZo3VDD50qvqVmAKsMcYUy0iyYBP05EYY65vY/liYLHPUfpIZ8dVwRYu\nn/S1V5UKJl96VbnsLrU32M8eX22MeT3QgXWFPo9D9VWaNlQw+dKr6mHgdmAzsAX4log8FOjAukLb\nOFRfpQUOFUy+VFVdAIwzxnq4gIj8C2skt1IqTGh3XBVMvjSO5wOeQ7GHArsCE073aOO46qu0xKGC\nqc3EISJv2XNIJQDbRWSFiCzH6gmVEKwAO0OrqlQ4MUGcv0AThwqm9qqq7m9nnU7poZSPgnFT16oq\nFUztPQFwpbflIjILuB5YFaigukp7ValwFIySh5Y4VDD50saBiEwVkf8TkX3Ab7Cqq8KOVlWpcBLM\nm7nmDRVMbZY4RGQ0VsnieuAo8CIgxpjzgxSbUj2Gtxt3MNs4lAqm9to4dgAfApcbY/IBROR7QYlK\nqV4kKG0cWlelgqi9qqqrgMPAchH5p4jMIcxLxNodV4UjLXmo3qbNxGGMecMYcx0wFlgOfBdIE5F/\nhOJ5HL7QNg7VV4X1JzrV63TYOG6MqTLGPGeMuQLr2RmfA/8v4JEp1QNEOq1/oW+ck9PmNl2tRTpn\nVErX3qhUgPky5UgzY8wxrMezPhaYcJTqWZwOYd99X/L7fju7T23iUMHkU3dcpZRSyq1TJY5wpwMA\nVTjJTo4F4LrpQ72uz0iMISEm0i/H0l5VKph6VeLQBzmpcJIcH91uldPHP54TxGiU8h+tqlJKKdUp\nmjiUUkp1iiYOpZRSnaKJQymlVKeEfeIQkdki8qGIPCIis0Mdj+r95k/JCHUIXXbH7BGhDkH1AQHt\nVSUiC4HLgWJjzESP5ZcAfwWcwOPGmPva2Y0BKoEY4GAAw1UqIIP5gqUnx656lkB3x30K+DvwtHuB\niDiBh4CLsBLBOvsRtU7g3lbvXwB8aIxZKSKDgD8DXw9wzEoppdoR0MRhjFklItmtFs8A8o0xewBE\n5AVgvjHmXqzSSVuOAdFtrRSR24DbALKysroRtVJKqfaEoo1jCFDg8fNBe5lXInKViDwKPINVevHK\nGPOYMSbXGJObmprqt2CVUkq1FPYjx40xrwGv+bKte8oR4ISI7OriIVOwnngYbjSuztG4Okfj6pze\nGtcwXzYKReIoBDwn78m0l3Wbe8oR7CqrrhCR9caYXH/E408aV+doXJ2jcXVOX48rFFVV64BRIpIj\nIlHAdcCiEMShlFKqCwKaOETkeeATYIyIHBSRW40xjcBdwBJgO/CSMWZrIONQSinlP4HuVXV9G8sX\nA4sDeexuCNeHVGlcnaNxdY7G1Tl9Oi4xxgTjOEoppXqJsJ9yRCmlVHjpk4lDRL4iIltFxCUiua3W\n/VhE8kVkp4hc3Mb7c0TkU3u7F+1Gfn/H+KKIbLS/9onIxja22ycim+3t1vs7Di/H+6WIFHrEdlkb\n211in8N8EbknCHH9n4jsEJEvROR1ERnQxnZBOV8d/f4iEm3/jfPtayk7ULF4HHOoiCwXkW329X+3\nl21mi0i5x9/354GOyz5uu38XsTxon68vROT0IMQ0xuM8bBSREyLy3VbbBO18ichCESkWkS0ey5JE\n5Nn/AQUAAASuSURBVH0R2WV/H9jGe2+yt9klIjd1OxhjTJ/7AsYBY4AVQK7H8vHAJqwR6jnAbsDp\n5f0vAdfZrx8B7ghwvH8Cft7Gun1AShDP3S+BH3awjdM+d8OBKPucjg9wXHOBCPv1H4A/hOp8+fL7\nA98GHrFfXwe8GIS/3WDgdPt1ApDnJa7ZwNvBup58/bsAlwHvAgKcCXwa5PicwBFgWKjOF3AucDqw\nxWPZH4F77Nf3eLvugSRgj/19oP16YHdi6ZMlDmPMdmPMTi+r5gMvGGPqjDF7gXysKVKaiYgAFwCv\n2Iv+BVwZqFjt410LPB+oYwRA87Qyxph64AWscxswxpj3jNVjD2AN1vigUPHl95+Pde2AdS3Nsf/W\nAWOMOWyM+cx+XYHVq7HNWRvCzHzgaWNZAwwQkcFBPP4cYLcxZn8Qj9mCMWYVUNZqsed11Na96GLg\nfWNMmTHmGPA+cEl3YumTiaMdvkyHkgwc97hJtTtlih+cAxQZY9oaCW+A90Rkgz1fVzDcZVcXLGyj\naNypaWUCYAHWp1NvgnG+fPn9m7exr6VyrGsrKOyqsanAp15WnyUim0TkXRGZEKSQOvq7hPqauo62\nP7yF4ny5DTLGHLZfHwEGednG7+cu7Kcc6SoRWQqke1n1U2PMm8GOxxsfY7ye9ksbs4wxhSKSBrwv\nIjvsTyYBiQv4B/AbrH/032BVoy3ozvH8EZf7fInIT4FG4Nk2duP389XTiEg88CrwXWPMiVarP8Oq\njqm026/eAEYFIayw/bvYbZjzgB97WR2q83UKY4wRkaB0k+21icMYc2EX3ubLdCilWMXkCPuTYpen\nTOkoRhGJAK4CprWzj0L7e7GIvI5VTdKtfzhfz52I/BN428uqgEwr48P5uhlrhuU5xq7c9bIPv58v\nL3z5/d3bHLT/zolY11ZAiUgkVtJ41ljzwLXgmUiMMYtF5GERSTHGBHReJh/+LgGbqsgHlwKfGWOK\nWq8I1fnyUCQig40xh+2qu2Iv2xRitcW4ZWK173aZVlW1tAi4zu7xkoP1yWGt5wb2DWk5cI296CYg\nUCWYC4EdxhivD7ASkTgRSXC/xmog3uJtW39pVa/85TaOF/RpZcR6ONj/APOMMdVtbBOs8+XL778I\n69oB61pa1lay8xe7DeUJYLsx5s9tbJPubmsRkRlY94iAJjQf/y6LgP+ye1edCZR7VNEEWpul/lCc\nr1Y8r6O27kVLgLkiMtCuWp5rL+u6YPQGCLcvrBveQaAOKAKWeKz7KVaPmJ3ApR7LFwMZ9uvhWAkl\nH3gZiA5QnE8Bt7dalgEs9ohjk/21FavKJtDn7hlgM/CFfdEObh2X/fNlWL12dgcprnysetyN9tcj\nreMK5vny9vsDv8ZKbGA90fJlO+61wPAgnKNZWFWMX3icp8uA293XGdZ0QFvtc7QGODsIcXn9u7SK\nS7AeALfbvv5yAx2Xfdw4rESQ6LEsJOcLK3kdBhrs+9etWO1iHwC7gKVAkr1tLtbTVd3vXWBfa/nA\nLd2NRUeOK6WU6hStqlJKKdUpmjiUUkp1iiYOpZRSnaKJQymlVKdo4lBKKdUpmjiUUkp1iiYOpZRS\nnaKJQymlVKf8f+bk4i1JIZLKAAAAAElFTkSuQmCC\n", + "text/plain": [ + "" + ] }, - "source": [ - "Approximate expensive functions" - ] - }, - { - "cell_type": "heading", - "level": 3, - "metadata": {}, - "source": [ - "tanh" - ] - }, - { - "cell_type": "code", - "collapsed": false, - "input": [ - "def tanhpoly(x):\n", - " a = np.fabs(x)\n", - " b = 1.26175667589988239 + a * (-0.54699348440059470 + a * 2.66559097474027817)\n", - " return (b * x) / (b * a + 1)\n", - "\n", - "x = np.linspace(-10, 10, 10000)\n", - "\n", - "plt.subplot(2, 1,1)\n", - "plt.plot(x, tanhpoly(x), label=\"approx\")\n", - "plt.plot(x, np.tanh(x), label=\"tanh\")\n", - "plt.ylabel('Tanh(x)')\n", - "plt.legend()\n", - "\n", - "plt.subplot(2, 1,2)\n", - "plt.semilogy()\n", - "plt.plot(x, np.fabs(tanhpoly(x)- np.tanh(x)))\n", - "plt.ylabel('Absolute Error')\n", - "plt.show()" - ], - "language": "python", "metadata": {}, - "outputs": [ - { - "metadata": {}, - "output_type": "display_data", - "png": "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\n", - "text": [ - "" - ] - } - ], - "prompt_number": 5 - }, - { - "cell_type": "code", - "collapsed": false, - "input": [ - "@hope.jit\n", - "def tanh_hope(x, y):\n", - " y[:] = np.tanh(x)\n", - "\n", - "@hope.jit\n", - "def tanhpoly_hope(x, y):\n", - " a = np.fabs(x)\n", - " b = 1.26175667589988239 + a * (-0.54699348440059470 + a * 2.66559097474027817)\n", - " y[:] = (b * x) / (b * a + 1)\n", - "\n", - "y = np.empty_like(x)\n", - "\n", - "print \"numpy tanh\"\n", - "%timeit tanh_hope(x, y)\n", - "print \"polynomial approximation\"\n", - "%timeit tanhpoly_hope(x, y)" - ], - "language": "python", - "metadata": {}, - "outputs": [ - { - "output_type": "stream", - "stream": "stdout", - "text": [ - "numpy tanh\n" - ] - }, - { - "output_type": "stream", - "stream": "stdout", - "text": [ - "1 loops, best of 3: 759 \u00b5s per loop\n", - "polynomial approximation\n" - ] - }, - { - "output_type": "stream", - "stream": "stdout", - "text": [ - "1 loops, best of 3: 23.8 \u00b5s per loop\n" - ] - } - ], - "prompt_number": 6 - }, + "output_type": "display_data" + } + ], + "source": [ + "def tanhpoly(x):\n", + " a = np.fabs(x)\n", + " b = 1.26175667589988239 + a * (-0.54699348440059470 + a * 2.66559097474027817)\n", + " return (b * x) / (b * a + 1)\n", + "\n", + "x = np.linspace(-10, 10, 10000)\n", + "\n", + "plt.subplot(2, 1,1)\n", + "plt.plot(x, tanhpoly(x), label=\"approx\")\n", + "plt.plot(x, np.tanh(x), label=\"tanh\")\n", + "plt.ylabel('Tanh(x)')\n", + "plt.legend()\n", + "\n", + "plt.subplot(2, 1,2)\n", + "plt.semilogy()\n", + "plt.plot(x, np.fabs(tanhpoly(x)- np.tanh(x)))\n", + "plt.ylabel('Absolute Error')\n", + "plt.show()" + ] + }, + { + "cell_type": "code", + "execution_count": 6, + "metadata": {}, + "outputs": [ { - "cell_type": "heading", - "level": 3, - "metadata": {}, - "source": [ - "exp" + "ename": "SyntaxError", + "evalue": "Missing parentheses in call to 'print' (, line 13)", + "output_type": "error", + "traceback": [ + "\u001b[0;36m File \u001b[0;32m\"\"\u001b[0;36m, line \u001b[0;32m13\u001b[0m\n\u001b[0;31m print \"numpy tanh\"\u001b[0m\n\u001b[0m ^\u001b[0m\n\u001b[0;31mSyntaxError\u001b[0m\u001b[0;31m:\u001b[0m Missing parentheses in call to 'print'\n" ] - }, - { - "cell_type": "code", - "collapsed": false, - "input": [ - "@hope.jit\n", - "def expapprox(x, y):\n", - " y[:] = hope.exp(x)\n", - " return y\n", - "\n", - "x = np.linspace(-16, 0, 10000)\n", - "y = np.empty_like(x)\n", - "\n", - "plt.subplot(3, 1, 1)\n", - "plt.plot(x, expapprox(x, y), label=\"approx\")\n", - "plt.plot(x, np.exp(x), label=\"exp\")\n", - "plt.ylabel('Exp(x)')\n", - "plt.legend()\n", - "\n", - "plt.subplot(3, 1, 2)\n", - "plt.semilogy()\n", - "plt.plot(x, np.fabs(expapprox(x, y)- np.exp(x)) + np.finfo(np.float64).resolution)\n", - "plt.ylabel('Absolute Error')\n", - "\n", - "plt.subplot(3, 1, 3)\n", - "plt.semilogy()\n", - "plt.plot(x, np.fabs(expapprox(x, y)- np.exp(x)) / np.exp(x) + np.finfo(np.float64).resolution)\n", - "plt.ylabel('Relative Error')\n", - "plt.show()" - ], - "language": "python", - "metadata": {}, - "outputs": [ - { - "metadata": {}, - "output_type": "display_data", - "png": "iVBORw0KGgoAAAANSUhEUgAAAYwAAAEACAYAAACgS0HpAAAABHNCSVQICAgIfAhkiAAAAAlwSFlz\nAAALEgAACxIB0t1+/AAAIABJREFUeJzsnXdYFMcbx7+HYFQs2AsoCiiICCjYYiN2Tey9C1FjTVBj\n1BgDdmM0+tMkpthijTEWEkWsoNFYo8auqGDvgAooHNz7+2Pdq7u3u8fBHWY+z7PP3u7Ou/Pu3u68\nOzPvvKMiIgKDwWAwGBI42FoBBoPBYOQPmMFgMBgMhiyYwWAwGAyGLJjBYDAYDIYsmMFgMBgMhiyY\nwWAwGAyGLOzGYISFhaF8+fKoXbu24PH169cjICAA/v7+aNy4Mc6dO5fHGjIYDMZ/G7sxGKGhoYiJ\niRE97uHhgUOHDuHcuXOYNm0ahg8fnofaMRgMBsNuDEbTpk1RsmRJ0eONGjVCiRIlAAANGjTA3bt3\n80o1BoPBYMCODIYSVqxYgQ4dOthaDQaDwfhP4WhrBZQSGxuLlStX4siRI7ZWhcFgMP5T5CuDce7c\nOQwbNgwxMTGizVdeXl64ceNGHmvGYDAY+RtPT09cv37dbJp80yR1+/ZtdOvWDevWrYOXl5douhs3\nboCI7H6JiIiwuQ5vi575QUemJ9PT3hc5H9p2U8Po27cvDh48iKdPn6Jy5cqYPn061Go1AOCjjz7C\njBkzkJycjJEjRwIAnJyccOLECVuqzGAwGP8p7MZgbNy40ezx5cuXY/ny5XmkDYPBYDCMyTdNUm8b\nISEhtlZBFvlBz/ygI8D0tDZMz7xHRURv1QRKKpUKb9klMRgMRq4jp+y0mxqGVGgQAPj4449RvXp1\nBAQE4MyZM3moHYPBsBWlSpWCSqVii5WWUqVKWfxf2I3BkAoNEh0djevXryM+Ph4//fSTtvObwWC8\n3SQnJ9vcg+htWpKTky3+L+zGYEiFBvnjjz8wePBgAFxokJSUFDx69Civ1GMwGIz/PHZjMKS4d+8e\nKleurN12c3Nj8aQYDAbDCqS/VstKl28MBgCTDhmVSmUjTRgMBuPtodyUxrLS2c04DClcXV1x584d\n7fbdu3fh6uoqmDYyMlL7OyQk5K1ya2MwGAxrEBcXh7i4OJxLuI/0+GuyZOzKrTYxMREdO3bE+fPn\nTY5FR0fj22+/RXR0NI4dO4bw8HAcO3bMJB1zq2Uw3i7YO21djO9nrUkjUcHZFQcipkneZ7sxGPqh\nQcqXL28SGgQAxowZg5iYGDg7O2PVqlWoW7euyXnYw8VgvF2wdxrIysqCo6N1GoT07+e9py9ReaE7\njoeeQ33vytL3md4y3sJLYjD+09j7Oz137lzy9PSkYsWKka+vL23bto2IiFatWkXvvvsujRkzhkqU\nKEE+Pj60f/9+rVzz5s1p8uTJVL9+fSpevDh17tyZkpKSiIgoISGBVCoVrVixgqpUqULNmzcnjUZD\nM2fOJHd3dypXrhwNGjSInj9/TkREHTp0oAkTJmjP3bt3bwoLCxPUV/9+9pj/LbmO626yXwz7/ics\nwN4fLgaDoQx7f6c3b95MDx48ICKiTZs2kbOzMz148IBWrVpFjo6OtHjxYsrKyqJNmzZRiRIlKDk5\nmYg4g+Hq6koXL16ktLQ06t69Ow0YMICIdAZj8ODBlJ6eTq9evaIVK1aQl5cXJSQkUGpqKnXr1o0G\nDhxIREQPHz6kcuXK0YEDB2jdunXk6elJqampgvry9zMrS0MFx/nQ/7bHGew3h33/ExZg7w8Xg8FQ\nhqyCDDlfrEVgYCBFRUXRqlWrqFKlSgbH6tevT2vXriUiopCQEJoyZYr22KVLl6hgwYKk0Wi0BiMh\nIUF7vEWLFrRs2TLt9tWrV8nJyYmys7OJiGjLli3k5uZGZcqUoSNHjojqx9/POZv2UKFxtSk7W2Ow\n3xx241YbExMDHx8fVK9eHV999ZXJ8adPn6Jdu3YIDAyEn58fVq9enfdKMhgMu8QaJsNS1qxZgzp1\n6qBkyZIoWbIkLly4gKdPn0KlUpl4crq7u+PBgwfabf2xZVWqVIFarcbTp08Fjz948ADu7u4G6bOy\nsrQDmD/44ANkZ2fDx8cH7777rqTe/zu6FD2rjIWDg/zhCVY3GGlpabhy5QquXr2KtLQ0WTLZ2dna\nDu1Lly5h48aNuHz5skGab7/9FnXq1MHZs2cRFxeHCRMmICsry9rqMxgMhmxu3bqF4cOH47vvvkNS\nUhKSk5Ph5+cHgBs3du/ePZP0lSpV0m7fvn3b4LeTkxPKlCmj3ac/1qxSpUpITEw0SO/o6Ijy5csD\nAKZOnQpfX188ePAAv/76q1m995+5jsfv/I2FQ/opul6rGIyXL1/im2++Qf369VG7dm2EhoZi8ODB\n8PPzQ3BwMBYtWoTU1FRR+RMnTsDLywtVq1aFk5MT+vTpg6ioKIM0FStWxIsXLwAAL168QOnSpa3m\nNcBgMBiWkJaWBpVKhTJlykCj0WDVqlW4cOGC9vjjx4+xZMkSqNVqbN68GVeuXEGHDh0AcAZl3bp1\nuHz5MtLT0/Hll1+iZ8+eogOS+/bti0WLFiExMRGpqan4/PPP0adPHzg4OODgwYNYvXo11q5di9Wr\nV2Ps2LG4f/++qN5jNn6Nxu+MRFkXZ0XXa5USt0uXLujTpw/+/PNPrbXjefjwIf744w907twZ+/fv\nF5QXCvtx/PhxgzTDhg1DixYtUKlSJbx8+RK//fabNVRnMBgMi/H19cWECRPQqFEjODg4YNCgQWjS\npAkArnbQoEEDxMfHo2zZsqhQoQK2bNmijZmnUqkwcOBADBkyBFeuXEFISAh+/PFH7bmNDUdYWBju\n37+PZs2a4fXr12jXrh2WLl2KFy9eYMiQIfjuu+9QsWJFVKxYER9++CHCwsJEA7peLbAZWz+8qvh6\n7WIcxpYtWxATE4Off/4ZALBu3TocP34cS5cu1aaZNWsWnj59isWLF+PGjRto3bo1/v33XxQrVszg\nXCqVChEREdptNtKbwcjf5NdxGKtXr8aKFSvw119/CR5/7733MHDgQISFheWpXiqVCgGTP8bitl0R\nFxen3T99+nTJ+2zVNp3ly5dj6NCh2u2srCzMmjXLIFSHEMZhP+7cuQM3NzeDNH///TemTp0KAPD0\n9ES1atVw9epVBAcHm5xPKj8Gg8GwB2xlCJcNnIBGvlUMPqanT58uKWfVTu/9+/ejQ4cOuH//Pi5c\nuIBGjRrh5cuXknLBwcGIj49HYmIiMjMzsWnTJnTq1MkgjY+PD/bt2wcAePToEa5evQoPDw9rqs9g\nMBhWg5+wSCqNLWjkW8UiOas3Sf36668YM2YMnJ2dsX79em17nhS7du1CeHg4srOz8eGHH2LKlCna\n9ryPPvoIT58+RWhoKG7fvg2NRoMpU6agXz/THv78Wn1lMBjCsHfauojdTzn32aoG49q1axgyZAj8\n/Pxw+fJl1KpVCwsXLoSzs7Ke+JzAHi4G4+2CvdPWJScGw6pNUp06dcKMGTPw008/4eDBg6hevTrq\n1atnzSwYDAaDYSOsWsN4/vw5SpQoYbDv6tWr8Pb2tlYWkrCvEQbj7YK909bF5jUM3jXL2FgA0BqL\n2NhYs+eQCg3C51OnTh34+fkxV1kGg8HIY6xSw/j0009x6NAhtGrVCsHBwahYsSI0Gg0ePnyIU6dO\nYd++fXjvvfcwf/58Qfns7Gx4e3tj3759cHV1Rb169bBx40bUrFlTmyYlJQWNGzfG7t274ebmhqdP\nnxoModdeEPsaYTDeKtg7bV1yUsOwyjiMBQsW4OXLl4iKisLevXtx69YtAFygrSZNmmDq1KkoWrSo\nqLx+aBAA2tAg+gZjw4YN6N69u3Z8hpCxYDAYDEbuYbWBe8WKFcOAAQMwYMAAxbJyQoPEx8dDrVbj\nvffew8uXL/HJJ59g4MCBOdabwWAwGPKw6kjvp0+fYvr06Th8+DBUKhWaNm2KL7/8EqVLlzYrJ2fw\nilqtxunTp7F//36kp6ejUaNGaNiwIapXr26SVn+kNwsNwmAwGKbExcUZhAaRg1UNRp8+fdC8eXNs\n3boVRIQNGzagd+/e2hHaYsgJDVK5cmWUKVMGhQsXRuHChdGsWTP8+++/kgaDwWAwcpP79+9j7Nix\n+Ouvv1C0aFGMGzcO/fv3R0BAAJYtW4YPPvgAqampCAwMRGRkJAYMGIAhQ4agUKFCuHnzJo4dO4a6\ndetizZo1qFLFshHYlmD8MS0nNIhVp6erVauWyT4/Pz9JObVaTR4eHpSQkEAZGRkUEBBAly5dMkhz\n+fJlatmyJWVlZVFaWhr5+fnRxYsXTc5l5UtiMBg2xp7f6ezsbKpbty7NnDmT1Go13bx5kzw8PGj3\n7t20Z88eqlChAj1+/JiGDh1KPXv21MoNHjyYihUrRn/99RdlZGTQJ598Qk2aNMkTncXup5z7bNUa\nRps2bbBx40b07t0bALB582a0adNGUs7R0RHffvst2rZtqw0NUrNmTYPQID4+PmjXrh38/f3h4OCA\nYcOGwdfX15rqMxiMfIpqes5jMlGEck+skydP4unTp/jiiy8AANWqVcPQoUPx66+/YuXKlejZsyda\ntGiBlJQUnDt3zkD2gw8+0IZOmj17NkqUKIF79+6ZzNJnT1h14F7RokWRnp4OBwdueIdGo9GGBVGp\nVNoJkHIT5oLHYLxd2PM7/dtvv6F///4GXqDZ2dlo1qwZduzYgfPnzyMgIABTp07FzJkztWlCQ0NR\ntmxZg6EG5cqVw86dO3M9OobN3Wp5zM2qx2AwGG8bVapUQbVq1XDt2jWTY9nZ2Rg+fDgGDRqE7777\nDkOGDIGnpycALqy5fr9tamoqkpKSDKZvtUesGktqxYoVBttZWVnyOlIYDAYjH1K/fn0UK1YM8+fP\nx6tXr5CdnY0LFy7g5MmTmDNnDgoUKIBVq1Zh4sSJGDRoEDQajVY2OjoaR44cQWZmJqZNm4ZGjRrZ\ndXMUYGWDsW/fPpP5MOQ2Q8kJDQJwbYaOjo7YunWrtdRmMBgMi3BwcMCOHTtw9uxZeHh4oGzZshg+\nfDhiY2OxePFirFmzBiqVCpMmTYJKpdKWbSqVCv369cP06dNRunRpnDlzBuvWrbPx1UhjF/NhyAkN\nwqdr3bo1ihQpgtDQUHTv3t3kXPbc3slgMJTzNr7ToaGhcHNzM+jXyCtsHnyQ59q1a1iyZAm6deuG\nKlWqYN26dUhLS5OU0w8N4uTkpA0NYszSpUvRo0cPlC1b1ppqMxgMRp6SXw2gXcyHIRQa5N69eyZp\noqKiMHLkSAC2m9qQwWAwcoqc6VvtEat6SR0/flwb4tzBwQETJkxAx44dJeXk3Ljw8HDMmzdPW20y\nZ6FZaBAGg2HPrFq1ytYqWBQaxCp9GPPnz8dnn30GgBus17NnT+2xzz//HHPmzDErf+zYMURGRiIm\nJgYAMHfuXDg4OGDSpEnaNB4eHloj8fTpUxQpUgQ///wzOnXqZHhBb2F7J4PxX4a909bF5nN616lT\nB2fOnDH5LbQtRFZWFry9vbF//35UqlQJ9evXF+z05gkNDUXHjh3RrVs3k2Ps4WIw3i7YO21d7Gbg\nnqXICQ3CYDAYDNtiFzUMa8K+RhiMt4tSpUohOTnZ1mq8NZQsWRJJSUkm+/OsSapAgQIoUqQIAODV\nq1coXLiw9tirV6+QlZWV0yxkwwwGg8F4Wzh57Q5afN8bLg6uOP3lLyjrUiTX8sozg2FPMIPBYDDe\nBqat+wNzzg9Dy6Lh2DV1Ego4WHUUhAl5PnAvp0iFB1m/fj0CAgLg7++Pxo0bm4QLZjAYjPzOw6RU\n+E8ei7lnP8a3Tbdhz7QpuW4s5GIXnd4AF/ZjzJgxBuFBOnXqZOAp5eHhgUOHDqFEiRKIiYnB8OHD\ncezYMRtqzWAwGNZj1q+7EHlqJKoiBFfHn4ZnpVK2VskAuzEY+uFBAGjDg+gbjEaNGml/N2jQAHfv\n3s1rNRkMBsPqHL6QiH4rJ+OBwwnMrP8zpvRqbWuVBLEbgyEUHuT48eOi6VesWIEOHTrkhWoMBoOR\nK9x6lIJeS+fgZNYKNC8xFqc+XoFyJZ1trZYodmMwlMRViY2NxcqVK3HkyBHB4yw0CIPBsGfi7z7D\n0OVL8Nfr71E9uzNODT+PutXzdvIkS0KD2I3BcHV1NZiB6s6dO3BzczNJd+7cOQwbNgwxMTEoWbKk\n4Ln0DQaDwWDYC3H/3sSETd/iDK1Gjazu2N3nb7QOqm4TXYw/puVMdmc3BiM4OBjx8fFITExEpUqV\nsGnTJmzcuNEgze3bt9GtWzesW7cOXl5eNtKUwWAw5JP+Wo2Zm3bg57M/IOmd06hbYDCODz6Het6m\nH8T2jt0YDDnhQWbMmIHk5GRtiHMnJyecOHHClmozGAyGCeqsbCyL/gs/HfkNl7AFRTOqo2+NEZg3\nMAolixWytXoWwwbuMRgMhhW4++QFvt15AH9c3I2rqigUVJdDs1K9MblTL7wX4Glr9STJN8EHGQwG\nI7/xMCkV62NPYueFIzidsg/Pi/yDUukN0aRCOyxsEYv29bxtraLVYTUMBoPBkCDpxSvsPHkJsZfO\n49T9U7ie8TdeFb6KYumB8HFuhA41W2L0+81zNdZTbpOvQoNIhQUBgI8//hjVq1dHQEBAnkbAzQ2U\nurPZivygZ37QEWB6Whtr66nREC4mPsZPu45ixPfrEBI5HVXG98Y742ui9PxSGL4jDHG39sG9eDUs\navkdUqYk4cWiIzgxawEi+7cXNRb55X7KwS6apOSEBYmOjsb169cRHx+P48ePY+TIkfk6LEhcXFy+\nGB+SH/TMDzoCTE9ro0TPrGwNrt55igu37uPKvfu4+eQB7qTcx6P0+0jKfIAUVSJeF74BVXZBFMnw\nRGkHT1Qu6on3q3dCK78v0DbYG0ULF8x1Pe0duzAYcsKC/PHHHxg8eDAALixISkoKHj16hPLly9tC\nZQaDkQdkazR49vwVnjxPw9MXqXj6Ig3JqWlISkvF/n+v4MkPG/DydRqev36JpFfJeJ6RjBfqJKRq\nkvAaSchwSEJ2wSRQwedQZZTEO5mVUJQqwcWxIsoVrgS/cn6oWro1/Cq7o2ktT1St4GLrS7Zr7MJg\nyAkLIpTm7t27ggZj4dYDgvlozLTPmWu7MydnqeyhC9cx69cYs+e1VF+pdkgl5z3471Vkr/2Tk4Pl\n9yG37i8R4ciZS0hbuUX5ec1cT27oe/yfC3j240bBY7LOa6G+Us9DNmmQlZ0NDWmQpcnGv3+fxMWv\nv4WGNMjWZCP7zVpDGmRTtsFvjd4xDXFpNW/2a5D9Zl8W1JpMZFEmsigDWchENmUiG5nIVmUgW5UJ\nDTKhUWWCHDJADpncUiADcHwNZBWCKssZDtnOcMwuCkdyhhM5Q/3gLm7Hq1DIwRlFHIvCpVBJ1Cjt\njXLFSqFCiVJwLVUKlcuURNXypVClnAsKFbSL4i5fYxd3UG5YEOMHX0jO09MTn3ZvaRW9cpvYLett\nrYIs/tr+q61VkOToH5ttrYIsTuwwNWz2yMW90bZWQY9XILxCNp4iG0CG3pG04/G2UkoRckZR2xpP\nT2nXX7swGHLCghinuXv3LlxdXU3Odf369dxTlMFgMP7D2IWXlH5YkMzMTGzatAmdOnUySNOpUyes\nWbMGAHDs2DG4uLiw/gsGg8HIQ+yihiEnLEiHDh0QHR0NLy8vODs7Y9WqVTbWmsFgMP5bvHUD9xgM\nBoORO9hFk1RO2bx5M2rVqoUCBQrg9OnTBsfOnTuHRo0awc/PD/7+/sjIyBA5S+5jTk+Ai8ZbtGhR\nLFy40Aba6dDX859//tHu37t3L4KDg+Hv74/g4GDExsbaUEvz93Pu3LmoXr06fHx8sGfPHhtpaMqJ\nEydQv3591KlTB/Xq1cPJkydtrZIoS5cuRc2aNeHn54dJkybZWh2zLFy4EA4ODkhKSrK1KoJMnDgR\nNWvWREBAALp164bnz5/bWiUtcgZNa6G3gMuXL9PVq1cpJCSE/vnnH+1+tVpN/v7+dO7cOSIiSkpK\nouzsbFupKaonT/fu3alXr160YMECG2inQ0zPM2fO0IMHD4iI6MKFC+Tq6morFYlIXM+LFy9SQEAA\nZWZmUkJCAnl6etr0f9enefPmFBMTQ0RE0dHRFBISYmONhDlw4AC1atWKMjMziYjo8ePHNtZInNu3\nb1Pbtm2patWq9OzZM1urI8iePXu0z+CkSZNo0qRJNtaIIysrizw9PSkhIYEyMzMpICCALl26JJr+\nrahh+Pj4oEaNGib79+zZA39/f9SuXRsAULJkSTg42O6SxfQEgO3bt8PDwwO+vr55rJUpYnoGBgai\nQoUKAABfX1+8evUKarU6r9XTIqZnVFQU+vbtCycnJ1StWhVeXl52Ewa/YsWK2q/LlJQUQU8/e2DZ\nsmWYMmUKnJycAABly5a1sUbijB8/HvPnz7e1GmZp3bq1tuxp0KAB7t69a2ONOPQHTTs5OWkHTYuR\n7wxGWloa6tWrh507d0qmjY+Ph0qlQrt27RAUFISvv/46DzRUTmpqKubPn5+vZgrcsmULgoKCtAWK\nPXH//n0Dt2w3Nzfcu3fPhhrpmDdvHiZMmIAqVapg4sSJmDt3rq1VEiQ+Ph6HDh1Cw4YNERISglOn\nTtlaJUGioqLg5uYGf39/W6sim5UrV6JDhw62VgOA8IBoc++KXXhJyaF169Z4+PAhHj16hAIFCmD0\n6NGYPHky5syZg44dOwrKqNVqHD58GKdOnULhwoXRsmVLBAUFoUWLFrmupzHm9IyMjMS4ceNQpEiR\nPIu0a4mePBcvXsTkyZOxd+/e3FJPS0701EfJnPE5RUzn2bNnY8mSJViyZAm6du2KzZs3IywsLE/u\noxDm9MzKykJycjKOHTuGkydPolevXrh586YNtDSv59y5cw36qPLq/RFCzrM6e/ZsFCxYEP369ctr\n9QRR+l7YxGCEhYVh586dKFeuHM6fP6/dHxMTg/DwcGRnZ2Po0KEGHW179+7F3r17kZSUhNevX6NM\nmTJ4//33zeZTuXJlNGvWDKVKlQIAdOjQAadPn85Vg2HJy3/ixAls2bIFn332GVJSUuDg4IDChQtj\n1KhRuaAhh6WF1N27d9GtWzesXbsW1apVs7JWpliip9xBnrmFOZ0HDBiAffv2AQB69OiBoUOH5pVa\nJpjTc9myZejWrRsAoF69enBwcMCzZ89QunTpvFJPi5ieFy5cQEJCAgICAgBw/3NQUBBOnDiBcuXK\n5aWKAKSf1dWrVyM6Ohr79+/PI42kkTNo2oA8613R49ChQ3T69Gny8/PT7hPrfFmzZg2Fh4fTvXv3\naOrUqRQeHk5t2rShzp07k0ajMThvSEgInTp1SrudnJxMdevWpfT0dFKr1dSqVSuKjo7Os+sUw1hP\nfSIjI2nhwoV5rJEwQvfT39+ftm3bZkOtTDHWk+/0zsjIoJs3b5KHh4fJs2Ir6tSpQ3FxcUREtG/f\nPgoODraxRsL88MMP9OWXXxIR0dWrV6ly5co21kgae+703rVrF/n6+tKTJ09srYoBarWaPDw8KCEh\ngTIyMiQ7vc0aDLVaTTVq1LC6kkRECQkJBgbj77//prZt22q3586dS3PnzhWUXb16Ne3cuVO7vXXr\nVnJzc6NChQpR+fLlqV27dtpj69ato1q1apGfn5/NPRPM6cljDwZDTM+ZM2eSs7MzBQYGahdbvgDm\n7ufs2bPJ09OTvL29tV5J9sDJkyepfv36FBAQQA0bNqTTp0/bWiVBMjMzacCAAeTn50d169al2NhY\nW6skSbVq1ezWYHh5eVGVKlW0783IkSNtrZKW6OhoqlGjBnl6etKcOXPMppWsYXTq1IkSExOtphyP\nscHYvHkzDR06VLu9du1aGjNmjOLzenp6EgC2sIUtbGGLgsXT01OyfJX0kkpKSkKtWrXQokULdOzY\nER07djSJ82QN5HS+xMXFoWnTphg5ciQOHjwomObGjRuIiIhAbGwsiDOIdrlERETYXIe3Rc/8oCPT\nk+lpr0tsbCwiIiJw48YNyTJYstN75syZBgU6EeWKx4mczhcHBwcUK1YMGRkZ5jtmGAwGg2F1JA1G\nSEgIHj58iJMnT0KlUqF+/fo59kAICwvDH3/8gdTUVO2+4OBg/Pvvv/D09IRKpUJ6erqJN0HTpk3R\nrFkzPH78GOPHj8e6detypAeDwWAw5CPZJPXbb7+hQYMG2Lx5M3777TfUr18fmzfnbLKa27dvQ6VS\nITMzE5UrV8aqVasMajD667Vr12LcuHG4f/++No2Li4vZmFCRkZF2P4euvevHkx/0zA86AkxPa8P0\nNOXnnwGVCpA7kPzuXeC990IwfXqkrPSS0Wr9/f2xb98+ba3iyZMnaNmyJc6dOydPIxESExPRsWNH\n7TiMo0ePYvr06YiJ4aYtnTdvHgBg8uTJWplt27Zh9+7dSElJwahRo9CsWTPTC1KpEBERgZCQkHzz\nQDEYDEZOyc4GHI3ajNLTgcKFTdO+fAkUL85vxb1ZpkPCHEg3SRGRQRyZ0qVLS57UEuTM6921a1d0\n7dpV1vni4uIQFxfHDAeDwch38N3EarWpETAmNRUoVkz4WJEiwOvXwDvv6PYlJwPcWOa4N4t8JA1G\nu3bt0LZtW/Tr1w9EhE2bNqF9+/aKMpGDnI50IsIXX3yBly9fIjg4GIMGDRJNywwFg8GwNWo1oCTc\nWno6MHasbtvJCahSBbh1yzQtEVCtmvAxfQoVAjQazghlZfHGAgBC3ixxkGs4zBoMIsLYsWNx8uRJ\nHDlyBAA3+53cr3wlyPGS2r59O+7du4cyZcowLykGg2HXLFwIfPop0LEjsHmz4Ve+MRoNUKCA8LHb\nt4GCBYHMTMP9SgJvOzhwBiansUIlaxgdOnTAhQsX0L1795zlpIelXlLXrl1D48aNMWzYMPTs2TNX\nY0IxGAwGwDXhFCok3BcgxMOHQPv2wNmz3Paff3Lyv/8OCBWj338PjB5t/pxqNVC0KNf8BOiarJTw\n6pVyGWPM2iiVSqUN5mVNLPWScnNzg4uLC6e4GfOaH7ykGAyG/UPENeEUKQK4ugLmJuwkAipXBipW\n1BkLfXqveC7zAAAgAElEQVT0ANauNdwXHi5tLHjS0oBHj4D79+Xrr4+Hh9iREACRss4h6SXl7e2N\n69evw93dHc7OzpyQSmUTL6lXr15h7NixKFKkCGrWrImRI0eaXhDzkmIwGEZcugRs2ADMmiUvfXo6\n0L8/sH276bGUFKBECcN9164B3t7yzn3nDuDmBly9Cvj4yJPJXeJgFS8pIsLPP/+MKlWqWE01MeR4\nSRUuXBjLly+XdT7mJcVgMHhq1eLWs2cDLVoA5iKMf/45YG5eKxcX7iu/YkVu+/lz+cYC4GohRPZg\nLOJgdS+pUaNG4cKFC5bpowA5XlKHDx/G+vXrkZWVhUuXLmk74oVghoLBePto3hxo1Qr44gvpdvzX\nr4E+fQDjGUcPHOBkec8hnqws+Z3ClSpxhT7AGRClyAjbZDFubnIH7oUACEHbtnHw9Y3DokXSEjbp\nwxBCjpdUkyZNsGzZMnzwwQcYMmRIruvEYDDsh23bgEOHgC+/5Lx+zNUSoqO5Tmoz01ObeBkp9SDa\nvBmwtBjy8rJMTg49e8pLt3Sp7rduEJ95JB2zjh07hkaNGsHDwwO1a9dG7dq1czx/blhYGIKDgxEf\nH6/dp+8l5eXlhaVLl4pGxd2wYYPdTHHIYDCUERnJfdmPGcONTjYHEdCrF5f+zQSAWlq1Eu4wVqkA\nick4tfDFiNz0+vTqBfzyi3K53OaTT7j1yZPm0ymY4ViLpMHYvXs3bty4gQMHDuDPP//ULjnBUi8p\nXrZEiRLaDngGg2EbzHkMiXHwIDB9Ovf7u++4Uczp6cJp79zhagHmQtd9/71hwVe/vjJ9Nm7kmq6i\no5XJ5RZ8XNePPtLtU+pC6+7OreWO02jeXMHJSYT9+/drf9+8edPg2JYtW8TEZGPpjHsRERF09OhR\n0fOauSQGg2FFuO9/ou+/l0779de69EKL8czJv/xiPr3xotEQ3bmjTIZfhgyxTC43lqAgbp2QwK0/\n+4woMFA8ffXqpvuIiEaPJkpLM59XYqIuPfd/SpedoikCAwMFfwttW0JuzbgHgCIiIrRLfphaksGw\nFa9fEz16pEzm6FHxQtuYly/lF5ZqNSdz757ygrZoUdsX9vxSvLjud5s23HrJEnmy16/rCvPnz7l7\nEhAgnj4rS/d7715DA0BkPq+NG2MJ0JWVcgyGpJdUXiHHS+ru3bv4+OOPUbJkSdSoUQOTJk0STcu8\npBgMadzcgKdPud/nzgG1a4unPXwYaNpU/LiDg2Ggu/R08aB4Qjg5cUWZq6t8GR69oBFWp2FD4Ngx\n+em9vXX9BzExwO7d3Ehtc3TvzuXj6cltq1TmO6IHDOBGlOuHE2nVirt/QrRowXmH8QwZAjRqFAIg\nBCEh3BAEOSiIRpK7yPGSOn/+PLp3744VK1bgzJkzea0ig2G3REcDp0/LT3/9Olco8cYCAPz9gYkT\nTdNqNEBQkHljwVOokO63Jd2MaWnKZXIb3psoMNB8uhUruDXfkf7tt9w9btdOV5CXKSMs+/vvXNwp\nHv3+B16W75sAuBHje/dyv83NZzdqFLfWHydy5AjwzTfmr0UM0RrGzZs30alTJxAREhIS0FGvZykh\nIcGy3N5gaSypd999F506dcLKlSsxcODAHOnAYLwtJCYaevkYh7PW5+FD3YAzIRYs4Bb9L1WxoHhi\npKYCq1crk+EpWtQyudxg1CiuUz04mNtevBgw12gRFgZ8+CHnuTV6tGFYcrEvfx8fwPjbt1Qp/Yiy\nOtk9e5QNEAR0Y0QWLeJceSdMAN59l9v34oWycwFmahhRUVEYP348JkyYgO3bt2PChAnaJcqcc7MM\nLPWSWrVqFWbNmoX9+/dj586dOdKBwbA39uzhvkjl1hTOnOHSV6tmuL9QIWDTJtP0S5aYNxb68GNi\nP/9cXnp9ihUzDNGdG+zYIS+dzMAQWvTHKA8dqvv94oU8b6JNmzhD4eRk6N3EF/rGhqNwYcNaGQA8\ne8bFrjKWzUmk2XfeAcaN4yZO4pGaZ0MQZd1d1sMSL6l///2XunfvTiNGjKCJEycKnteGl8RgWEx8\nvGGHpLOzcCcyEVFmprwO1DNndDLLlinvvCWyfQey8dKuHbc+f96ya6hRQ/fb09N8eiKiK1cM7z1A\nVL68eF5inD7NpSld2lCmbl3pZ6NWLS4t7zllnNeJE0T79gnLLlxoXrfz5/WvLR91esuJJeXv74/f\nf/9d8lyRkZHa36zzm5HX8F+Wt25xk9+Y4/Rprn/AmLQ0rh37yRPDdu8rV4CaNeXpUacOV7y8egUI\nxOmU5KuvlMvkNnzbvkajTG7fPq5T2NmZu65Jk7iYTmIhOvjR0sZNQOvXc/8HEdc3IZc6dbg+os6d\ngZkzdfvN9T/w8DUMMb+gevXEZT/+WHzkNx9vT0aRqsVuOr3leEldunQJvXv3xqhRo7BlyxazaUNC\nQliYc0aOkBqFbMzt20D16rptd3euzVis/VqlEjYW+pQtq5NPTZVvLHj+9z+gbl1lMjx6gaKtyrJl\n8tLxhemuXbp9/L3Q+7Y0YfFi032OjsDPP3Nt+RMncnGjtm4VPwcfrNCYfv2ANm2Atm0N9xtvC/Hv\nv8CMGbprKFJEuOnQGCmDYQ5HR/F7ZUkZKdtgpIsNx7QScrykYmJiMHbsWHz//fdYs2ZNrurD+G8z\naBD3sqlU0obj3j0unbs7532kzzffmI64zcxU9vKHhnJrJS6qPOHhXK3EHuA74keM4NZSExLxtQh9\n7yC+8CxZkutnSUkxlStbVvh8Q4dy/RAqFdeRX7KkuLusnP+Hr/kRce6zSvnnH3kxnPj7ZYnBsDaS\nBuPvv/+Gr68vvN/Uzc6ePYtRvK+WhVgaS2rgwIH49ddf8dlnn+HZs2c50oHx32D7dmUTzjx/zr2Y\n+hPdODqKuyGOHs2NZZBi/Hjdb3NTdQphj/GKLMG4+WXaNG6ioaVLhWszUk0+777LzUvBhxrh6dNH\nvk4lS3JrfceBbt24piMp9IP3KeX8efnhzceOBf74w/K8rImkwQgPD0dMTAzKvDGngYGBOHjwYI4y\ntdRLqmzZsvj2228xd+5crT4Mhjm6duUGgg0aJN40BHDTcKpU4qGqJ0wAVq403FeyJOd2KYdFi7jY\nSHqVaEUoNTJKkVvI8l5WvGsmAJjzsufTR0SYTkbk4gIEBHBBCI2bTebN45qQAO4r3NWVm/xI6D80\nNjYODpxrMM+oUVwfghD8+dau1bm3btnC6SWFWE1GDn5+8tOqVFy8LL6G0b+/5fnmGKle8Xr16hGR\nYTgQf39/yd50KSzxkkpMTKThw4dT//796ciRI4LnlXFJjHzGsWNEvr5cbBw5mPMiEgqDcfGifC+d\nq1c5mYMHbe8xZI2lVStuffmyvPSzZnHr5csNPXZu3yZ69sw0vZub7j4/fqxLn5DAhbXguXVL+D7z\n4UJ4+FAb5v5vIqLt26U9l4g4LyiA6K+/pNMKkZRkmZwl3L3L6dq/f+6cX07ZKeklVaVKFe1ERZmZ\nmViyZAlqKu15k4EcLyl3d3f8+OOPkudiXlJvFw0bcmtnZ65pZ8EC4fZcjYbzSrp3T/xc5ctzUVYL\nFuS2L18W7+AUwtubK5YURfjMZUqVApKSuIFjxrUgIdat40JLAFwn/b590oPzLlzgvoonTeJqFHwn\nLz+GQKxjVey8Vasabht7k/H/r5yxAkLPgtwxBvy8FEq9rnj4Jq28xFp9GbyXlBIkm6SWLVuG7777\nDvfu3YOrqyvOnDmD7777zlIdRTH2knry5AkOHDiAnm98wtLS0jB48GAMHz4cGzZskDwf85KyP1Qq\nbtSs3JfT09P05RDqRAa4jukCBcwbCx6+eSczE/D1laeLPvoxeeyBS5d0v+W01Op3tOoPBtu1C4iN\nFZapVo3zEHJ05AbD8f02UoWXktno9PUyHszGY65ZUX90ebt2wF9/SefJGzRz57UXlI64lyJXvKSu\nXbuGDRs24PHjx3jy5AnWr1+PK7ngdmHsJfX69WsMGjRIu71161b06tULP/30E/6wlx4ghiyys3Uu\njP/8wz3427aJpx84kCuIbt4UT2M8ilbpqNXp0y13N23Z0jI5ucjtF+nVi1vrF67mCj5jr6Rq1Tg3\nz2rVOAPQrp146IsiRbgxCMZY03OHdyyIizPvNmuMoyP3POmHFSlQAGjSRP458oPBKF/e1hrIMBhj\nxoyRtU+MsLAwlC9fHrWNwmAePHgQ165dQ/Xq1fHVV19pvaYSExORmZmJTZs2GXhJ6TdZFbC2qWXI\nZskSUy8icwwYwL3Q3bsb7u/WTdjzSKXimkzkMGUKt5Y7UYw+kZHAxYvK5eQiZ35kAKhQgVu3aQOU\nLs395r/K+WNC1K4tXMiZqzEZ32+VivMyunlT2sVVLvw93beP00WJcf3wQ25tbrCjWMHepUvOCn25\nIVNsiUrFeWaZCdKd64i+akePHsXChQvx5MkTfPPNN1i4cCEWLlyIyMhIaBQ0+IWGhiLGyEm5T58+\n+PDN0/H69Wt89913iI+PR9euXVG3bl14e3ujd+/eBn0lbm5u2hqIkvwZwly8qGxg2uvX3IAofvrH\nQYO4BzgxUTj9b79xx4W+SnkmTOA8UngaNZKvD8B50mRlKZPJbfgggOHh3FrKmPFjLOrWNf1aFyoA\n+YJdpRI+HhXFDSAUwji93NoBr6MQxufQN1gXLwILF+q2y5QBZLQmm60tBgRYv2mGSHlQP1sxZowy\nDytrI/o4Z2Zm4uXLl8jOzsbLly+RmpqK1NRUFC9eXFZ4Dp6mTZuipFHP0CeffIJWrVohIyMDd+7c\nwahRoxAVFYUFCxYgKSkJCQkJ+OijjzBixAicOXMGX331Fbp164YtW7Zg1KhRonN987C+C/NkZ3MP\nnaOjdBA3Ii6UQuHCXPAyY6pVM3UVXbUK6N1bni49enCB3e7dUzbnAI+5r3Bb0KqV4baUwdAPcFei\nBLc2N5pZv1bFp9OvHZQsKd6co28wvvySW4S4coUbIc7Dz9GQU1QqoG9f82kuXjTfHDV/PhfqRIj8\n0Kxkj/B9GXIQteXNmzdH8+bNERoaCnf9oZZWQI5HVKlSpfDDDz8Y7FspxwUE/x0vqcePuVhEcmPa\nZGdzhf/Uqbp9fNR6jcb0a/HZM3mdqFWqcAPeihfnBiSFhcnTh8fV1fIJcHJz/Gb9+ty1KYm1Y1z5\nlTIYHh6633/9xXlwHT7MbTdvDpw6ZZh+2jSuoHd355pwSpTQeXzJpWhR08Fu+nh76zq/z58HatQQ\nT2vt0cdSTggODuL3NDDQ/j4g7BlLvKQkuwqHDBlisk+lUuFADlxF5MSNSkhIwOzZs/H8+XNs3rzZ\nZFuKt9lQ8Oh3gu3bZ769+OBB87H8HRwMjUZamjxjwVOiBPeF5+8vX4YnN2dLA7jZz8wFaDNm6VJu\ndK2Spo/Klbl7Z+yi6+DA1baMYwalp5t6AvHt6Py3Ez8S2N+fmw2P5+FDrtB3dgbat5enn/7kR3Je\nC/45kGr+EHqV589X3rxoDapXBx48yPt88yt8GanEcEh2F3799dfaZebMmQgMDESQVMQ0CeTEjapW\nrRqW6wWzN95+m1ixwnQSFXMsXmz6orZqxU27aAxvBOQUEvyXG5FlE9k8eaJcJi/gmyqkRuZevcqt\nW7fm1voup0KVW40G+PprrpA6e5b7D9u2NWwaKV4c+PVXU9nChcW/zrt3B/7+m6tBqNU6Q8K7z5Yv\nr2w2u6pVOc8jJUHsWrSw3Bts4kTD+RwYbw+SNYxgfrqpNzRp0gT1FHyuWTq73n+Fe/cM27Hj43WD\niYz55x/d7F9C/PIL572k35mttIPw+XP50USNkROq2VKWLOFCNUuxYYNuiswGDbivdb6gbN3afKdr\njRrc17uQ+2KvXsDOnYad9CqV4bSaQkRHS+tsjKOj7gvd0ZF7Hnbv1nlRKaFQIa6fSaknGT+gzxwu\nLvnDu4hhPSQfo6SkJO3y9OlTxMTE4IWCuf0sjRuV3xg9mitA5MYKWrOGS28cuK56dcPmB57Wrc0b\nCx6NhvuaBOQFUDPGxUXXsWoPKC1wed97lUr3Jc0bDA8P6XtobCx4WeMZ1OQiFJjw6FHD7TVrzBtD\nXgdzkU0LFuT6XIy5fx/4809pPS0hPl43Mx/jP4JU7BB3d3eqWrUqVa1alby8vKhVq1b0l8LAK5bE\njXr27Bl99NFH5OXlRfPmzTPZFkPGJVkdoRg6YrOlJSXJi9lz9KhOZtgw5TGC1GrbxymyxrJzJ7f+\n8UfdvvnzTdOVKcOtb9/m1j16EPXrp7uXANEXXxCdOmUoFxtrGIOIByBq1oyoWzfdsR49uN/FismL\nU8THQ+LPBxD5+Ch6tIiIaORIefnpAxCpVIb7li5Vfh7Gfwc5Zadkk1SimKN9DrDUS8p4W4yceEnx\nX5E3bhh6sAhx7Jh4556DAxern3eVBDiPE7mdwo0acbWFlBRd1E4lmPOCyWsiI7lFn6dPxTvVu3fX\nNf3wX9ehocBHH3H31TgOkRBly3KD5777znyzXEgIF+qiVCnD/Y6O3OA4oU5UFxfDuZHF0Pcuiojg\n/hOxkBe5gT3Mn8CwX6zqJbVlyxaz3kzdunVTlJE+crykAFNPqaioKOzcuRMvXrzAhx9+iNZ876QR\nsbFx6NKlC8aNC5et04sXhoW7pyc3SE1o9q6sLHkTsru46ALdJSUp9yDatEnab12MWbMsk5PDxIlc\nZ++QIYbxe8yl5w3G+PHciGOhoG0uLpyBFOpD4O+3gwM3dsMcd+9yBuCdd+SFBedHGOuTmsoZDaHp\nLcuUUT5mJDLSciNu6fgCZjAY5uA/phcvXoztxrHnRRA1GH/++WeuGQw5XlKAzjOKD0DYuXNndO7c\nGSkpKfj0009FDUa1anEYP54rnPjxAWI8eSLeWfu//3Ffuvr9Emq1Mr/3sDAu1IUlHZaWGovc5tNP\nOYOxapV5gxEUxHXU8x4zFSroDIXQo8V31vv7A5Uqce3vQoWlkKy+YXB1FddJbqBlIUPDD2Br3ZrT\nTynTp1s2StcSg1G8uGlthg1sYwgRHh6O8PBweR/yedA0Rj169KACBQpo+zHUajV5eHjQ6tWrqUaN\nGlSwYEEaP368WXl9JkyYQGfOnBFMC4CACAJite3Gt2+bptNoiAYOlNeOXqOG/vmVL2fP2r4vgF/c\n3bl1377y0vPt3hUrcnH4AW4egy++MH8/tm0jOn+eqHp1XbpKlYgyMohu3ODuP0Dk56eTqVePaPNm\nLn21aty+P/7QtbsXKUJUujT3u1QpndyOHUSDB4v/10TceR8/5n6PGCHcbyFEly66dGo10YsX0jLW\nZuxY5X0P9+4R3b9vuG/JEtaHwTAlNjaWIiIiZPVhSHpJpaSkYNy4cQgKCkJQUBAmTJiA58+fy7Ze\nffv2RWxsLBwcHHDlyhWsWrUKjo6O+N///odhw4YhIyMD06ZNw/79+3H58mWznlJEhEmTJqF9+/YI\nDAyUrUOVKsDs2Yb7HBzkB9C7do3rT7hxQ3aWBihQ1WLkVvj4voFx47hR4vqD5oTGXtSqxUUCPXxY\nF+OnQAFg5kzz+XTpwn1NX7vGbQ8YwDX9FCzI9Q3xHzP6tbt33jHf3HTlCjfeAeBGePNfzEJxmIzp\n0UM3DkPJXBb6X+WOjpbNq51TZs0yHfEtRaVKzOWVkQtIWZSuXbvSl19+STdu3KDr169TREQEde3a\nVbEVy6mn1Ny5c2nJkiUUFBREI0aMoB9++EEwHwCiX73x8VwafjYuJUvHjravHfAL77kDEH35Jbd+\n8UI8fUgI0bvvcr9fvyZavNj4nnHLzJmmsgcO6NLxNTIhWeNFDhMm6GoQANGKFbpjrVsTFSpEFBVl\n/nwA0YMHRN9/b76GYczly0Tjxkmn69z57fkq/9//3p5rYVgfGeZA2kvqxo0b2MpPZgDOAylAzoS3\nEljqKTV27FgZZ4/U+x3yZuHGOBBxX79KyS1fdoCb4L1TJ86rhv8iN0fdurr5JQYPBq5f1335Hj5s\nOg+ASqU7/s47uoizctCP7SOn72bqVPnn1593ecQIwxhU27dzExzJ+bJWqYCRI7m5m+Xi4yMcXt0Y\n/RoGg/E2kSsz7hUuXBh/6U1ddfjwYRSxwrh/qQ6WhIQEDB06VNvhfeXKFYwcORK9evXCihUrJM4e\nB8AFnOEIMTiSmwW/UvhmJH7+g5EjzaffvVv3m++U9fAwDCEu9Nc4OMibfMW4cCQylFuwgGvG0sd4\nIFyXLtIhOIxp2NC0Sa1IEe6+tGypC9khBv8ozZpl/WYYS5wVGIz8AB+l1sXFxXqxpH744QeMHj0a\n7u7ucHd3x5gxY2SPhzCHlKeUcewoHx8fLFu2DL/++it265ecgoQAEO44kIiMbjGWxNPnPWacnLjC\nOVzCC1g/jPX06cLhqYVCQDg4cOE+pKYv1Y+0KjTrm4sLUKeO4b7lyw1nz5MzGt2Yo0d18ZuMUanM\nR0tt3VrneTV1qvKZ96RYuhS4dcu652Qw7InAwEDZY9UkX6/AwECcO3cOz58/h0qlQnFzPqoChIWF\nYefOnXBxcUFBvTaNZ8+e4cCBA6hatSqGDRuGzZs3Y+PGjWbP9eeff+L777/HsGHDJHKNVKSjPu7u\nXAFRoQIXV0gu1apxA8okbZkADRooS1+rlnizmr7BKFiQa9ZRqbgvdiUVQ6naDk9AALfs3y8deyg3\n2LMnd8/v7Kws0J89w5rXGELw4zGmyxgoJFnDWLx4MV68eIHixYtj3LhxqFu3rowvfB2hoaGoU6cO\nbt68iWvXrqFy5cpYsWIFPvnkE/z0008oWLAgZsyYgffeew+nTp0yG0uqY8eO2LVrF3755ReJXCPB\nNUsp58cfubXcSf34mpxKxcUE4r14xBCK6Cp3gBX/wpvrg9E/l9JpN3NSoLRoAcyZY7k8I/fhw6Uz\nGPrExcXJnkBJslu8du3aREQUExNDXbp0ofPnz1NgYKCi3ndreEjFxcXRxx9/TMOHD6dFixaJ5gWA\nFiyw3AMpJoZbL14sL/2rV9y6QwedDjt2mPceMvZw0uf5c6KUFFO5ESOI4uLMe7kMG2You2IFt27T\nxvz/w6efNk2ZlxODwXh7kGEOpL2k6M1n586dOzFw4ED4WWFCWUs9pJrLdKA/dChSbysExh3fxjRv\nzk0wpN8MM3Ys16fQogUgNFcUP8mOUJv5++9zI8L/+IOLi8SjH69IbL4JvsVv/Xqgf39u3uuFC7mm\nMqkawE8/cRPz8PTvLxz2Qgz+/KymwGC8/eSKl1RQUBDatGmD6OhotG3bFi9evICD0uD6RsidcU/f\nSwoA0tLSUK9ePezcudOs7MOHcQBc4OMTCXPGIimJ6yhdt44rLL//ngtG98svujmTd+82jDEFcMdG\nj+aMgqMj8MUXXMGuj6OjqedPhw4SF61Hv35c/rzrqUolr8lI3/WVb1aT2zQ1YgR3D+wpvDmDwcgd\ncsVLasWKFZg3bx5OnToFZ2dnqNVq2XNri2HJjHsAMH/+fPTu3Vvy/O3bhwAIxJo1ukadrl2F086a\nZThnwTvvAIMG6bYdHQ2n9/znHy7mkUqlq13MnMnVRISoVo1bb90qf2S5EHL7ORwduet1c+OMx8WL\nwrPFCeHqKr+zm8FgvB1Y1UvKwcEBCQkJWLt2LVQqFZo2bYouCka+WWvGvb1798LX1xevX7+WzDMy\nMhLTpxsWsj/8YOj+qQT989Stq0x29WquyUsqVLocHZR0SvP2WH/gHYPBYBhjVS+pUaNG4ccff4S/\nvz/8/Pzw448/YvTo0bKVsdaMewcPHsSxY8ewYcMG/Pzzz1oZIbge/ziDgl4/ZpHSsRg5CRPNt97p\nD44/f56rqSjVIbfcIjUa3XzRDAbjv4VVvaS8vb0pOztbu52dnU3e3t6Ket+tMeMez+rVq2nnzp2i\nefGXBBD984/xMW65f59bJyXJ079NG8u9h9LTiSIjhY8tXy7vnADR118TJScThYYq14HBYDCkkGEO\npL2kvLy8cPv2bVR9M83Z7du34eXlZbE1Ayz3kgKAwYMHS54/MjISxYtzExC9eGE6456cMBn68COJ\nLQkrUrgwN9uaEGFhwhP0GNOgARciw8VFfn8Eg8FgmMOqXlIdO3ZEx44d8fLlS9SsWRPNmzdHSEgI\nfH198VLO/JRmsMRLKi4uDk2bNsXIkSNx8OBBs7JxcXGYPt0FX30VKdiZo9TJ66uvOJkPPlAmJ4VK\nZX5yJ55jx0xDcjAYDEZOsMRLSrSGMeGNn6hKpTLpL5A7xaoYSrykeIPh4OCAYsWKISMjQ3B2Pn1C\nQkJE58tYs0a5vu7uutngrEVcXJyiucZtRX7QMz/oCDA9rQ3T0zoEBgYiJSVF8kMcgPJW+UOHDtHI\nkSNlpw8NDaXSpUvTO++8o92nVqupQoUK5OHhQZ6enlSxYkW6dOmSoDw/255GoyEiokePHlH//v1F\n8zN3Sfy8Efzv169lX4bViYiIsF3mCsgPeuYHHYmYntaG6Wld5JgDWY0zp0+fxsSJE+Hu7o5p06ah\nptyJkWE9LylexsXFBRkZGWbzjIyMFK1i8c1RRMLzNjMYDMZ/CSVeUqJNUlevXsXGjRuxadMmlC1b\nFj179gQRKe4k2bdvHxITE9GxY0ecP38eAHD06FEEBAQgJiYGADBv3jxERUVh8uTJGDhwIAAgKSkJ\nn3/+Oc6ePYt58+bB29sbu3fvRkpKiuQkSuYuvkABReozGAzGW42ScRiidRCVSkUdO3akW7duafdV\nrVrVoqqOsVvt5s2baejQodrttWvX0pgxYyw6tzGenp4EgC1sYQtb2KJg8fT0lCxfRWsYW7duxcaN\nG9GsWTO0a9dOW8OwBjntNDfH9evXc+3cDAaD8V9GtA+jS5cu2LRpEy5cuICmTZti0aJFePLkCUaO\nHIk9OZy1Ro6XFIPBYDDsC8lO76JFi6J///7YsWMH7ty5gzp16mDevHk5yjQ4OBjx8fFITExEZmYm\nNsc1iHcAACAASURBVG3ahE65NXcqg8FgMKyCiqzVziRC3759cfDgQTx79gzlypXDjBkzEBoail27\ndiE8PBzZ2dn48MMPMYXF1GYwGAz7xio9zTbmt99+I19fX3JwcKB/jAJI/fvvv9SwYUOqVasW1a5d\nm17bcPCFOT2JiG7dukXOzs60YMECG2inQ1/PU6dOaffv2bOHgoKCqHbt2hQUFEQHDhywoZbm7+ec\nOXPIy8uLvL29affu3TbS0JTjx49TvXr1KDAwkIKDg+nEiRO2VkmUJUuWkI+PD9WqVYs+++wzW6tj\nlgULFpBKpaJnz57ZWhVBPv30U/Lx8SF/f3/q2rUrpaSk2FolLbt27SJvb2+TuH1CvBUG4/Lly3T1\n6lUKCQkxKDjUajX5+/vTuXPniIgoKSnJIJBiXiOmJ0/37t2pV69eNjcYYnqeOXOGHjx4QEREFy5c\nIFdXV1upSETiel68eJECAgIoMzOTEhISyNPT06b/uz7NmzenmJgYIiKKjo6mkJAQG2skzIEDB6hV\nq1aUmZlJRESPHz+2sUbi3L59m9q2bUtVq1a1W4OxZ88e7TM4adIkmjRpko014sjKyiJPT09KSEig\nzMxMCggIEB1ETSRz4J694+Pjgxo1apjs37NnD/z9/VG7dm0AQMmSJXM8W2BOENMTALZv3w4PDw/4\n2sEEFmJ6BgYGokKFCgAAX19fvHr1Cmq1Oq/V0yKmZ1RUFPr27QsnJydUrVoVXl5eOHHihA00NKVi\nxYp4/vw5ACAlJQWurq421kiYZcuWYcqUKXBycgIAlC1b1sYaiTN+/HjMnz/f1mqYpXXr1tqyp0GD\nBrh7966NNeI4ceIEvLy8ULVqVTg5OaFPnz6IiooSTZ/vDIbcaVoBID4+HiqVCu3atUNQUBC+/vrr\nPNBQOampqZg/f778mPR2wJYtWxAUFKQtUOyJ+/fvG3jdubm54d69ezbUSMe8efMwYcIEVKlSBRMn\nTsTcuXNtrZIg8fHxOHToEBo2bIiQkBCcOnXK1ioJEhUVBTc3N/j7+9taFdmsXLkSHZTM15yLCEUO\nN/euSIY3txdat26Nhw8f4tGjRyhQoABGjx6NyZMnY86cOejYsaOgjFqtxuHDh3Hq1CkULlwYLVu2\nRFBQEFqIzadqRT2NMadnZGQkxo0bhyJFilhtrIsUlujJc/HiRUyePBl79+7NLfW05ERPfXJz7I8x\nYjrPnj0bS5YswZIlS9C1a1ds3rwZYWFheXIfhTCnZ1ZWFpKTk3Hs2DGcPHkSvXr1ws2bN22gpXk9\n586da+Dmn1fvjxByntXZs2ejYMGC6NevX16rJ4jS98ImBiMsLAw7d+5EuXLltOFCACAmJkbrOTV0\n6FBMmjRJe2zv3r3Yu3cvkpKS8Pr1a5QpUwbvv/++2XwqV66MZs2aoVSpUgCADh064PTp07lqMCx5\n+U+cOIEtW7bgs88+Q0pKChwcHFC4cGGMGjUqFzTksLSQunv3Lrp164a1a9eiGj9heS5iiZ7G43zu\n3r2bp00/5nQeMGAA9u3bBwDo0aMHhg4dmldqmWBOz2XLlqFbt24AgHr16sHBwQHPnj1D6dKl80o9\nLWJ6XrhwAQkJCQh4M53l3bt3ERQUhBMnTqCc/hSbeYTUs7p69WpER0dLTkedlygeE5dnvSt6HDp0\niE6fPm0QLkSs82XNmjUUHh5O9+7do6lTp1J4eDi1adOGOnfurI1gyxMSEmLg1ZOcnEx169al9PR0\nUqvV1KpVK4qOjs6z6xTDWE99IiMjaeHChXmskTBC99Pf35+2bdtmQ61MMdaT7/TOyMigmzdvkoeH\nh8mzYivq1KlDcXFxRES0b98+Cg4OtrFGwvzwww/05ZdfEhHR1atXqXLlyjbWSBp77vTetWsX+fr6\n0pMnT2ytigFqtZo8PDwoISGBMjIyJDu9beYlZcm0rTzG07Ru3bqV3NzcqFChQlS+fHlq166d9ti6\ndeuoVq1a5OfnZ3PPBHN68tiDwRDTc+bMmeTs7EyBgYHaxZYvgLn7OXv2bPL09CRvb2+tV5I9cPLk\nSapfvz4FBARQw4YN6fTp07ZWSZDMzEwaMGAA+fn5Ud26dSk2NtbWKklSrVo1uzUYXl5eVKVKFe17\no2SKiNwmOjqaatSoQZ6enjRnzhyzac0ajKysLJowYYJVlePJrYCELPggW9jCFrYoX+QEHzTrJVWg\nQAEcPnw4TzqSrNUpeePGDTRv3hyLFi0CcQbRLpeIiAib6/C26JkfdGR6Mj3tdVm0aBGaN2+OGzdu\nSJavkp3egYGB6Ny5M3r27IkiRYoA4Ap3vkPMWlgzIKG5KVoZDAaDoUPJFK2SBuP169coVaoUDhw4\nYLDf2gZDPyBhpUqVsGnTJmzcuNGic8XFxcHFxcWu59FlMBgMe+Ds2bPyJ8YjG9CnTx+qWLEiFSxY\nkNzc3GjlypVEpKzzRQwApNEQvYlooJijR4kscag5fZpIxPFJEP1OxMePiSyJXHH/PtHhw8rliIhm\nzSJKS5NOZ9zZmZ1NdPmyZXmeOEGUkaFcLjmZ6OJF8ePmOmT37bPs3mo0RJaG+xELVybVcfz6NdHL\nl5blaSlZWUSpqYb75HZwm3GmyTUSEjidieTrSUT06BHRv/9almdmpmXPUHY2UWKiMj15oqLkvZ/G\nnDlD9MMPyuWIuLJTMo1Ugtu3b1OXLl2oTJkyVKZMGerWrRvduXPHMo3yAAD03nsRBMRScrIl8kRe\nXpbJAUTp6ZbJOjkpl6tUiZO9e1eZXHa2Tl+lhIdzcn//rVzW0jx5OUuMIy+r9CNg6FBObscOy/NU\nWsgEB3Nyljh1WXqdkyZxcsePW5ZniRLK8/T352SfPrUsT0veFW9vTvb5c8vytOS5XbaMk7t2Le/y\n5K/z7Fn5MrGxsRQREWEdg9GyZUtauXIlZWZmUmZmJq1atYpatWolXxsrEhsbS02aNKERI0ZofdmN\nAUAAZzAAIiVen8nJuj+qVClluvFyAFdjkEtmpk6uVy/L81Ty5Z6aqpNbt87yPJXCyyn1GtbPk/+6\nVCqr1OFOP0+ltVVerkYNy/NU8tyq1To5BwfL81RSgD95YvmzoC+nNwO0JC9eWCfPxET5cvrvZ4MG\nluf54oVlsjNnWp7nq1fyZKxqMPz9/WXtywsOHjxI7du3p9DQULp+/bpgGs5gKHuwNBqiDRvIRE4k\nCwNu3jSVk5NnejrRN9+Yyq1eLS177JhleWZkEIWEmMrJqTHv329ZnkS6rx795cgRabnLly3LU6Mh\natfOVO7MGWnZ+/ctv86JE03l5Hw8vHpleZ7bt5vKyWl60Wgsz/PgQVM5OcNJ9Gu2SvM8dMhUbssW\nabmkJMvyzM4mWrzYVG7GDGlZofsDSNc4NRpdzVZ/+eIL6TxPnBDOU85sDh068OmtYDDee+89WrNm\nDWVlZZFaraa1a9dSixYtpLUwQ2hoKJUrV85gHAaRdFx2frTuo0ePqH///oLnNq5hAOabbFJShG+0\nnAdrwABxud9/F5fT/2pRmqeQkeGXxYvF5bKyzOepVovLbtwoLmdu/FFGhvk8zdUWfv9dXO7jj8Xl\nXr60LE+NhujHH8XlunYVz1PMyPCLWO1PoyFq315cbvx48TyvXrX8GRIypvxy8KC43PPn5vM0VyD2\n7i0uZ64JLi3N8uucNk1cztwMAq9fm8/TXBPcjh3icgMGiMuJGXA5eR49Ki5Xvbq4nK5MiCWuzJS4\noUTSBiMxMZE++OADbR9Gp06d6JaSeqQAloYG4cnIyKAePXoIXxBMaxhiD7PYl53+ItZW//770rJC\nf7LYF4/+8ttvpnIaDVGxYpbl+c8/0nJiHWVlykjLCvH339Jy06cLy0rJieUpVtvTX7p1E5Z1dZWW\nFTI2T59aru+YMdJyQh2fUgWauYLi00+lZYU+HqQKNEC8b0Go5iXn3kp95ADcuYUYNsyyd+XePWm5\niAjhPAcOlJYVKoeePZOWE/t4cHe37Nm7cEEorUhiPcymUKvV1K9fP8mTWIIloUG2bt1KH330EfXu\n3ZsOinwKiRmM4cMN06Wny3vRhe5haKg8OeM2TyV5GrebN2kiT279ekM5OS+d2HV+9ZU8uWXLhP4H\ny/I0V7PQX4Qm0bM0z1Wr5Ml17Gi9PPfulSf3wQeGcnIKbn4x7uAVanaVo68cAyX23M6aZVmeYs1X\nQouxo0lYmGV5Krm3xrVGuc/QlCmWX6exgfv1V3lyS5eSCcJpBR5UYzmpBI0bN86VaU1zKzSIrkmK\nX2IFHw65fxJAtGaNTu7BA2Wy+i+QErlvvtHJKXlhja/Tw0O+3JtYc0Qk3ZxkLs8CBeTL6Rf8Sl5Y\n4zwbNpQvt2mTTk6JQc3JM7RihU4uMTFv8tR/buXUbK2Rp35LspIPJMDw61uJnI+PTk5JAWx8nZ07\ny5dr3Njye6TP4MHy5fT7T3JynUOG8PtjybCsNEoogGSKAQMGUHBwMM2YMYMWLFhACxYssEpwPGOD\n8fvvv1vRYDQnYJHJTTt6lEujtGDSv49K5W7f5uSMPTxyM09+mmg5TW5iebZsqUzu/HlOTm4TjVCe\npUopk+NnZZVqW8+Ne6vU0OQkT74PztgzSUmevAu23IUfY5GX18l7EuVlnvpu05bm2bixZc9QTvL8\n6CNlclu3misTFhFXZuplIILkjHteXl54//33odFokJqaitTUVLx8+VLRSEI5WDM0CBACwDQ0yOzZ\n3LprVwtPawG9enHr4sUtkydSLsPPzdKnj2V5ZmUBSkP2v5kFF2FhyvN7+ZK7zqQkZXLBwdzaxUV5\nntnZQHq6crn69bl1eLhy2eRk5TIAMG0at54xwzJ5IuD+fWUy/AR2LVtalqcl1KrFrS2dpdiSd+WD\nD7i1Jf8nn9+RI8rk+GeIL4+UkJLCrX/8UZkcH5hDeILHQHBlpgzMWRO1Wk19+/aVtDqWYFzDUBqX\nXQxAvIbBX61Sqw5wzUJyOsSsmefu3UTz5+dtnunpRHFxeZvnjh1Eu3blbZ6ffcbVivIyz507Lasl\n5CTPa9e4r8u8zPPVK6Jz5/I2z5gYom3b8jbP5cu5waR5meeff+bGcyu/hiGZIjf6MHI7NIhx34X+\norTtj1/MuehJLVKugWLLuHGW52lJ01BOF+7+v/155rd7K9dhwnix9F3Jb/+n0r4Wfilc2PI89QfP\nKlmKFyeqX9/a9zaW5PZhSAYfrFatGpo0aYJOnToZRKsdP368vCqMAGJBBdu3b4/27dtbfF4dkaJH\njh617IwzZ1omBwATJ1omt2iR5XmuX2+5rKXozbabZ0RG5n2eRnE48wS12nLZw4ctk1Pa7GEN/vkn\n7/OMirJM7tUry/O09DpfvABOnLBMdvNmsSMhb5bpkudQvfkqFyXyzRtpPF9FRESE5MmtDRHhiy++\nwMuXLxEcHIxBgwaZpOH0jIDuJvw3CQ4GTp2ytRYMBkOIoUOB5cttrQVP3JtlOiTMgbTBEEKtVsPJ\nycki1XLCtm3bEBUVhTJlyqBDhw5o0aKFSRrOYCi+JAaDwfiPo5I0GKJeUk2aNNH+HjhwoMGxBg0a\n5EitsLAwlC9fHrV515o3xMTEwMfHB9WrV8dXX31lInft2jU0btwYCxYswLJly8zkEAnOYjIYDMZ/\nCwdJ31dj4mCuGd/g3GIH0tLStL8vXLhgcMyCSokBoaGhiImJMdiXnZ2NMWPGICYmBpcuXcLGjRtx\n+fJlrF27FuPGjcP9+/fh5uYGlzc+lA5m70ok/svNUQwG479L2bJKJUKQY4ORmzRt2hQlS5Y02Hfi\nxAl4eXmhatWqcHJyQp8+ffD/9s49rKoq/ePfA4oi+FO8C2gQijcGULzn5ZSa5CQWmI2OOFpaSmap\neWmaenBKLY1hRJ9onFIn7ykk6eOYihI65hUdVEyR8EZlPhqm5oji+v2xXOfss8++rL3P4RzI9Xme\n8+xz9tlr73ff1rvWu973XTk5OUhOTkZ6ejqCg4ORmJiIr776ClOmTNGcTW/ZMtdlbNHC9X3wYtbv\n/GHiww/Nl/XGILUr8pqlXTvzZRs0cJ8cevj7my/bujVdFha6RxYemjTx3LHcgdmYLx5UFcb169eR\nnZ2NrKws23fpb3dTVlaGVq1a2X6HhoairKzMYRt/f3988sknyMjIwKRJk1T3tWlTKqjGTIVR09SW\nLXRptBP1+OPGtpeyaBFdtm1rfh+uVBZGMd7lNU/9+nTp5+f6PozSrRswdKixMizWdMoUc8cEAAVf\nDk1mz6bLxx4zf8w2bcyXNcqwYebLRkXRpSefwfbtjW1vJpCU0by5+bKMZ57h3TIP9noylauE6mXv\n168fNm/ejC1btti+s9/9+/fnlYgbuReWK3z7bR6AhjBjmqr1wNHYqMLw8zPeSmOtpTp16JL3Erz3\nnv07i1T9+9/5yjZqRJeuVGivvUaXXbrwbf/oo+aPtX69+bJyjL6MFgtQt66xMuzaMJ8QvXvasqX9\nOzvWkCF0ydvL/eMf+eWTwxo6nnCh9fWlS4XhySrDTAS3nDfeMLa9tO5gim3yZL6y9+/TJe8U21ro\nNzysAFLRrVtD9OjBd0BVhbFixQosX74cy5cvd/jOPu7GvalBXKdZM77tWPfPYgE6dzZ2jLFjHX93\n62asPACwsJXBg/m237qVLllFVksnEkepO/7mm3TJ67Y7b57zOjMpRHhQMgWxF5i9jHqwNApmWrHy\n66mnMJhss2cDAQHGyrK0M+w1sVj47NfZ2fbvHTrQpfzYajwIxXKAXS892PmwhpIeTGEq9S55G3R/\n+xvfdlqwHtFf/sK3fUiI/fvu3XTJ+36ya8OWsbHG6xXWK9ZTUlJTOG/DyCtjGEp07doVxcXFOHfu\nHCoqKrB+/XokJCSY2ldSkhVKuaSMwGsWiI+nSx8f/h6CErdvAytWaG+TkmL/zm6wVGGp0a+fc7nU\nVJrbSC8gTMnMwV5W3vOVHp9VqEuX8pU1CqtEAaBnT8f/9CqZWbPo8s9/pksz91N+DD2l89xz9u/s\neEZ7t9JyPGWlSo1tz1r/erChQ6mTosyJ0gHp+ct7eHq5zphs0jEos9eGF6njptwRU+bUqYrUBNqv\nH3DsGP/x4+Loksndp4/xc+7b13EfamzaRJeNGsVqjglL8YrCGDlyJHr37o0zZ86gVatWWL58OWrV\nqoUlS5Zg8ODB6NixI55//nl0YM0fg7AWyapV2maTJ5+kS9bSZ78BgNdzWNrZWrkSOHCAW0zbDfXz\noxW5Xmt/5EjndaNGAWfP2n8rtfp9fOw9EdZqa9jQ2db6/PPOZZmJJSLCfp2MjidIzS6vvkqXWg+z\n9DyVlNPChcaOz/Yh8RRXRN7r8USoEWuYSM+PyatVUbRrp/y/iw6Mqvzzn3TJxjqk14bXZi6XLSoK\nkAxbOsF6hMyMqrUvPZjZLlbSjlQyYEgH02NiHP/Tc0755Re6ZC38V16x74dX3gUL7N/Lyuizbvae\n6ikMM+OCXlEYa9euxffff487d+7g4sWLGDduHACaGuT06dM4e/Ys3mR2DxPQ6HQrIiNpCD4hyuYe\nlqEkMZFu89VX9v9Yq1jPjixtmYWE2DNRqiGtpNmDoFdGCx8fWpkzlB4Si8V+rGbN1B9AtRbU8OH2\nVjdAz4En8+pLLzn+ZmM1ajDHOZkDHQDgxRft35XkZ6Yzdv7DhztvzwZM1YiMpC086fVkZR95xHl7\nJbOlXDZek5TRSsFdg768x+3Viy7lqViYx9OFC7SCk8Pen+Rk5SzRR44Ae/bYHT94ZWOZbZVQGksc\nP97+nSkNI95ahOg/P6xnMWMGMG4cMHq0Y3lA3WTo7097x0x2iwUIDtY3FWmZyXh7Vy1bWm0ZPfTQ\nfexu3bqFd999FxMmTAAAFBcXYwtzJfIwe/fuxaRJkzBhwgQ8puESQk8+z2GdUsXPKlKpJ0yHDvYK\na9cuQC1lFmtVKT3UWh5TShWtG8f7FZHu38yxNmygrW/puep5grz9tj39OKNxY+3ja1UQ7OWuVUt5\nO7PmHDmHDyuf29dfO6/79lu6XLyYKo/mzZ1fcL2KXUlJaymRwED7fpWUk9bAvnywOTzcsdXMY9IK\nCgISEgB5koVWrWgFJ8fHBzhzhpp35NmE2JhLnz7ajQklmXx86ID2zp3O/2kNqlss9muu14Dp2hX4\n17+0t1E7xrJlziZRLa5coRYK6T54kI+DMl55hd+764cf8tynMMaNGwc/Pz/s27cPABAcHIy33nqL\nTxI306dPH2RmZuLpp5/GWLUrBXsPQ3rRIyOdt1O6Ka1b2+dlePxxdQ8J1tpQ2oeZh4wH6YuTkwNs\n3uy8jV4Pg3f/Zv6X8s471Kwn7/b36EG7wkpyKg1KKx1TydyqtB2rEKT/ySsYPasnK9ukid17iREU\nBOzfD0yYQBMvFhbSlmxBgX0bi0W5x+TvT80NUgWTkUGVDztm797qcikpDEKoye/ll5XLKPnnT55M\n5dcbiJaahXJyqLLhwWKh7uJKg+pSZal076dPd14nPef0dP75OphTSnw8HQjft0/fq7F2beWxTK1W\nf4MGyuNBej3JgAB1E5FSGbattDcszU21ZImyg4IU9g6uWuXGHkZJSQlmzZoFvwcSBvC6U2hgNjUI\nY82aNRjFZglSQKmH8f77ztvxVIAWCzBokPP6t98GLl5UbqW0aqW8b6WKwwhsn/Xq0fEW5lIrl1eO\nj4/+wyPdvzuoVYtOwiNxfANAu9137iiXYZVG06Z2V1wlmXhNeOvX00pcq2dkpO2jdA179KDPQLNm\n9OPn5+jVYrEoTwz166/OjZGRIx09W6SVAYP1dNRaoBMnAh9/rH0eUtksFuqAoNfabtnS3POhVkbe\ns5dv9+mn9utjtFesVqEXFFCPvSZNqIlNK0vtO++o/9e4sfp/5eXGzIVt2wI3bzqvl+5D6Rq2a+f8\nLCclAWlp/Mdm+z1xwo09jDp16uC2JI9vSUkJ6ug9XTqYTQ0CABcuXECDBg00FZdSD0Oq9Xld3Bjy\nG1ZSQisG1npu2FC5+/ndd46/pfIw04KcMWP0BxHZYJoSSg+rxUJbH0VF2vuVs2mT3eMCcF2hSCtc\ndi2kpg32IrrSgbVYgIED6cvTvDltxWoFRPL6Veg5JKjB630kRd4alQ4MMycOaa+RN7K3KgbE9Spz\n3qpCHtr12GP2a87GiqRjAlLksTq8cSlaFb/aefXubXcgMYKaZ2FAgHLvS8+MnJ/v6OwC0HrIzKwT\nVit/D0P3NUhNTUV8fDwuXbqEUaNG4T//+Q9W6Pl/6tC3b1+cO3fOYZ00NQgAW2qQ2bNnOyQ/XLZs\nGV4w4cQvvehGBwylZe/fd76BagPAvN12KcycpfUiank3SMvVrQv873/0e6NGyt4mUuQVijwi15UK\np7hYf5AxOBiQPRaGIQTYscNxnVZqB1YBqw1GsnM220b697+Nl5ErjKAg556anx/w4FWp8jEwV+CN\nspcPYsvHpC5epNchKcm5rLRRAxhT7qtXKysYtWvKpmN97z1j6YOMODfk5iqPB2VmAizBhbR30awZ\n8NNP2vt013QHupf2ySefRJcuXbB//34AwKJFi9DUeHYrXZRSgxxQ8FHl0YTUpzgMS5eGYeRIq5OP\nsSsvmLfKmpl+xOjx9B7mqVP1U5C8+qrzgCjAl3pCy/Zblezbxx+4ZhQl7yo92PjYs88Cp07ZvY+Y\nws/OpuaqDh34g8mU8ISicZdLqFYMr1KDwNeXztuuh5lgWcB41gAmI48Xnfz9YaZaqdu/UeTmq3Hj\ngG++yQOQh7Fjzzk14NXQbWsPHToU27dvx+OPP46nn366SpQF4N7UIFRBjMXEiamKASkueOyaZsgQ\nY+kX5BGqdevyj7kwXMm/JI0GZnTp4lxByV+4Ro34/fL9/Y1FsTZurPyidu/ummtyr150vEWO0sAy\nYKxlaeax7tyZHrdPHxqZzyoMNnPjs89SeWvXNj8utngxfxT0AwdJQ0jdWM2gdt14FVBWluNvtXgP\ntf25W5mqBdPxHMeVRhNzh5eP6wQEAL16WQGkYuzYse4L3Js+fTr27NmDjh07Yvjw4di4cSP+x+wc\nbsSdqUHy8vIQF3dM0TMK4A/KY7hyw5h9efx45e60GlOn0gEsFgzEK4dUn7vy0Cv5zCshl8lIrOWN\nG45BeEqB/dL9nzwJHD1qX89a3l98wZ/KRctuLeXIEWDNGuX/jDwPSi66S5bwlwfsres//clYOS0m\nT1a+3gCd0lWaYkXLm0j+jDG7Ogu+NPIMqo3r6cET66KXz4xFPTOMJGPct48+m3okJxtPZgm4R3lJ\ng2cPHHDMR3fs2DHkcSav0lUYVqsVmZmZKCkpwcsvv4zPP/8czXjfTgO4MzWI1WrFhx/GqpoZPJnp\nkrXUzdz0adPsNmDelojUZmzUjGVGMT73HB1kZihFi6vh62s/rz/8QX/u8+bNHRUis/PyXtvwcGr3\nnzhRf9suXRxfMim8xyNEuRKUOy107qzsicdg3lLuem71zIqPPWa+8mayEkLHfYxkepXGhLiS8dUM\nQUH2mIY7d5SzKshhbr+9evFNUfDZZ7Rnt2ePfZ2ResEVxZGRYX/uund3bATExro5Ncjt27eRlZWF\njz/+GIcOHcKfXGzqVHVqED2MXnhXbhRLoeDJ+TUYGhng3cbMmc6DzGaQX5+1a5XjTMzy66/Ubgu4\nXvEGBxtLASNFnm4CoO6eSuM+clwdA2MNArWetxo8silx9aqxmCTmy1JS4rk5KNg18fGxp/lRixOS\nY2b8IyDAMUXNqlX6ZZhJUk0mHk+8unX5s0troTvoPWLECBw4cADx8fGYPHky+vXrB18zvoIS1rKc\nHDKeeuopPGXGZ02G3sC4J71K2LGMRH2q7cdo8J3RHoMrpjde+dSQxziwMQktX3npsfWQemgZkVMt\nX5MrYyZmMavoHn2UpvT46CPjZV25p/Ievl6ai5dfpr0/s9kA1qyhudXM4AnnCjlffGF8rg0ljdJH\nlgAAD9NJREFU9u6lyUv1GDdO2dRstVLHoDlz5ujuQ1dhvPjii1i7dq3LSsIdXLp0CVOmTEFQUBAi\nIyMxi6UXlZGammq7CEqYVRj8E5NoM2gQvy3dKN5ysbxxw/xL99//qtuMeYex+vbVz/bLMCsnq/DM\n9BbPnaPmMFcy4JptdVdlZWjkfOrX18+O7ApS1+f4eGDdOv0yTKm5MyiRF1fmiTGzH7WsA3l5ea6P\nYeTm5gIAbt68iZycHIcZ97KVXGg8wPHjx5GUlIRPP/0UR9nopwJMYSihl1JZCVaZffGF8bJKL/n2\n7fbEh+6mXj0a1AN4tocREGDe7h0drR6JHhen7x5psRibX+OvfzU2OM/IyKD3zsykTmbca92F2QSH\nVYHZAEi9fX7zjeO6OnX4xtNcmXLH1el6eBseakF/PGj16pi53EjgnqrCyH9Q67CZ9qQz7m120bhs\nNjVI7969sXTpUgwYMADxbCIKBVJTU1U1pjzHPQ/SzKdG6dBBOfTfKEZMPvJAJt6pX9n+ma2/uqBn\nijH6IjVtap/W1AhBQbR3aHbKV7Pw3PdVq5RjMvLz7dMOG8GbCs6MSapnT88rxD59gIoKc2UJ4ffs\nY5hRGG+8YfcslBMcDJw+TXsYvAoDRIeSkhKudUbIz88nBQUFJCoqyrbu3r17JCIigpSWlpKKigoS\nExNDioqKyGeffUZef/11UlZWRtLT00l+fj4hhJDhw4cr7lvrlABCfv7Z/v3aNT55798npKiIb1sj\nvPMOlUMPgJAFCwjJzeXbnhBCDh+myxs3CLl7V3//ACGLFvHvv7oAEHL5sv37L7/wlbt/X/+6EELI\nsGHuuya1ahEyYoTxchMnGpcBIMRicVy3ZImx/dy5o3+MwEDl9UeO8B9HSl4evTdqDByofg4bN5q7\nVwA9bnVlyxYq4/nz9ne1KuBQB0R3GG24QvP6Oek0YSbo27cvgmTGNGlqkNq1a9tSgyQnJyM9PR3B\nwcF44oknsGjRIkyaNAnhZvJumMRiMWfC0GPIEO1U6HKMtKDYzF2BgfxmgFdeUZ7ToLojnYedt/Vv\nsVSNeUSLa9eoa2VNgSfwU6nVe+2aeY+c/v21W9KDBqm7BbvSw6gO5jo1tFyuPY3qK3Pq1CkUFRWh\nvLwc2dnZIITAYrHgl19+qZLAPZ7UINHR0di4caPuvqxWK8LCwhAWFuY0+M0exrlz9dMbVzU9ejhO\nP6mFJwazfX2Vc9hUZ06f1s+RVV3wtCnLWw4QrmZl1mLmTPp5mPDzowpNnk/MVdhg97lz/KlBVBXG\nmTNnsHnzZly/ft1hzKJ+/fr4J5uv0Y24OzWImpcUO4x0BrmaQq9eVNEJ7BiNKaiJuKv1641stTWF\n6tzDkKOWtdcorI5kiuNrpVnCZKgqjGHDhmHYsGHYt28femvN5OIm3J0apGHDhtzRizWB//s/6klU\nExVdTaYmVSSeZupUz0dka2H2Xr34onuC2moqRlKD6FpxO3fujCVLlqCoqAi3b9+29QSWLVvmkpBy\npKlBgoODsX79etUAPz2sVitipbO913DOnHGfz7YS+fn2OcwF1Q93ZXx1N7zJC6s70pnqqjPumoZY\nTmxsLMrLy7l6GLqD3snJybh8+TK2bdsGq9WKixcvItCsw/0DvJkapCZ2odu2NTcRDy99+4qWtBru\niMQVeIa4uN/2/aoOdZflgTuVKrGxsTh27Biio6NRWFiIu3fvok+fPopzVVQHLBYL1E7JYqHZXz09\n+CiouVRW0mR0PFPcVhWXL9N8U0ay5lgsNDBLGiewZAmdr0Q0DmomZWU0WHD0aGDlSvfvX6vuZOia\npNhc3g0aNMDx48fRokULXLlyxT0SGqSoqAhz5sxB48aNMWDAACSp5AvXSg1SHbS0oObg6+tdZQHQ\nLL1GU6wVFXk2K7Og5mIkNYhupMbSpUvJ1atXSV5eHgkLCyNNmjQhmZmZrsaImCItLY3s2bOHEEJI\nQkKC4jZapwTQQDaB4GEkI6PmBWYK7JSV0fs3enTV7J9DHegH7k2YMAGNGjVC//79UVpaiitXrmAi\nz4QCGphNDZKcnIx169Zh5syZuHr1qur+tVKDiB6GQCCoiVRVj9FIahDVMYy0tDTnjR/YuCwWC6ZN\nm2ZawD179iAwMBBjxozB8ePHAQCVlZVo164ddu7ciZCQEHTr1g1r167F4cOHUVBQgBkzZiD4QVRZ\nZWUlkpKSsEk+TRb0xzBu3qy6+ZsFgurM4sXAlCliDKOmQghVGtVyDOPGjRtuDaaT0rdvX6fIQmlq\nEAC21CCzZ89GcnIyAOD8+fOYN28ebt26hZkPW7inQCB4qKkO1hFVhcGdvdBN8KQGeeSRR/CPf/xD\nd188qUEEAoGgpjFlimvZs6W4NTUI4/Tp00hJScGPP/6IkydPorCwEF9++SX+opRL2QU8nRpEIHjY\nEKaoms+iRe7bl5nUIFyD3vPmzbO51/7ud78zHYGthbtTgxw7dsxpfWQkXwZOgUAgeFgwkhpEV2H8\n+uuv6NGjh+23xWJBbTZVkxuRpgapqKjA+vXrkZCQYGpfaqlBTp+u2ohpgUAgqGnExsZy593TVRhN\nmzbF2bNnbb83btyIli1bmhYO8G5qEIFAIBCYQzc1SElJCV566SV88803aNiwIcLDw7F69WqbN1N1\ng8c1TCB4GMnIAF57TYxlCJRxS2qQiIgI5Obm4ubNmyCEIDAwEJ9//nmVK4zS0lLMnTsX169fx4YN\nG3Dr1i2kpKSgTp06sFqtGDVqlGpZrdQgAoFAILBjJDWIqknq5s2bSEtLQ0pKCj766CPUq1cPO3fu\nRKdOnbB69Wp3yapKeHg4PpHkHc7OzsaIESOwdOlSfPnll5plmcKoznDnbvEyNUHOmiAj4H05eXsW\n3paTFyGne7BardxhFKoKg0Vhx8TEIDc3Fz179kR6ejrWrFmjW2FLMZsGRI40TsNXZ+RaKzVIdaG6\ny8eoCXLWBBkBIae7EXK6ByOpQVRNUmfPnkVhYSEAYPz48WjZsiXOnz8Pf39/Q8KMGzcOr776KsaM\nGWNbV1lZicmTJzukAUlISFBMA8IIDQ3FxYsXER0djfv372se09NBhwKBQFBTYeb7OXPm6G6r2sOQ\ntuJ9fX0REhJiWFkANA1IkGxWeGkakNq1a9vSgCQnJyM9PR3BwcG4du0aJk6ciKNHj+KDDz5AYmIi\nsrKykJKSYtrdViB4mBk4EOjf39tSCGo0qmlsfXxIYGCg7ePr62v7Xr9+fUNpc0tLS0lUVJTt94YN\nG8j48eNtv1euXEkmT55saJ9qREREEADiIz7iIz7iY+ATERGhW7+qmqQqKyvV/nKZqkpqCMAhZkQg\nEAgE7sMrc3K5Mw2IQCAQCDyDVxSGO9OACAQCgcAzVLnC8EQakA0bNqBTp07w9fVFQUGBw3+FhYXo\n1asXoqKiEB0djTt37rh6SqbRkhMALly4gMDAQMXJqzyJVM4jR47Y1u/YsQNdu3ZFdHQ0unbtit27\nd3tRSu3rOX/+fLRt2xbt27fH9u3bvSShMwcPHkT37t3RuXNndOvWDYcOHfK2SKosXrwYHTp0QFRU\nFGbNmuVtcTRJS0uDj48Prl275m1RFJkxYwY6dOiAmJgYJCYm4vr1694WyYahEAe3jDR7mVOnTpHT\np08Tq9VKjhw5Ylt/9+5dEh0dTQoLCwkhhFy7do1UVlZ6S0xVORlJSUlkxIgR5MMPP/SCdHbU5Dx6\n9Cj54YcfCCGEnDhxgoSEhHhLREKIupwnT54kMTExpKKigpSWlpKIiAiv3ncp/fv3J9u2bSOEELJ1\n61ZitVq9LJEyu3btIgMHDiQVFRWEEEJ++uknL0ukzoULF8jgwYNJWFgYuXr1qrfFUWT79u22Z3DW\nrFlk1qxZXpaIcu/ePRIREUFKS0tJRUUFiYmJIUVFRarbe8Uk5W7at2+PyMhIp/Xbt29HdHS0LWgw\nKCgIPlU1MS4HanICwKZNm/Doo4+iY8eOHpbKGTU5Y2Nj0aJFCwBAx44dcfv2bdy9e9fT4tlQkzMn\nJwcjR45E7dq1ERYWhjZt2uDgwYNekNCZli1b2lqX5eXlCAkJ8bJEymRmZuLNN9+0ZaZu2rSplyVS\nZ9q0aViwYIG3xdBk0KBBtrqnR48euHTpkpcloqiFOKjxm1AYahQXF8NisSA+Ph5xcXFYuHCht0VS\n5ObNm1iwYEGNCjjMyspCXFxclaS6d5Xvv//ewYkiNDQUZWVlXpTIzvvvv4/p06ejdevWmDFjBubP\nn+9tkRQpLi5Gfn4+evbsCavVisOHD3tbJEVycnIQGhqK6Ohob4vCzbJlyzBkyBBviwFAeaZTrXdF\nN/lgdWHQoEH48ccfndbPmzcPQ4cOVSxz9+5d7N27F4cPH4a/vz8GDBiAuLg4PPHEE9VKztTUVEyd\nOhX16tXzWKZdM3IyTp48idmzZ2PHjh1VJZ4NV+SUUpWu3HLUZJ47dy4yMjKQkZGBZ599Fhs2bMAL\nL7zgkeuohJac9+7dw88//4z9+/fj0KFDGDFiBL777jsvSKkt5/z58x3GqDz1/ijB86zOnTsXfn5+\nmslTPYnR96LGKAwzL1WrVq3Qr18/NGrUCAAwZMgQFBQUVKnCMCPnwYMHkZWVhZkzZ6K8vBw+Pj7w\n9/dHSkpKFUhIMVtJXbp0CYmJiVi5ciXCw8PdLJUzZuSUu21funTJo6YfLZlHjx6NnTt3AgCGDx+O\n8ePHe0osJ7TkzMzMRGJiIgCgW7du8PHxwdWrV9G4cWNPiWdDTc4TJ06gtLQUMTExAOh9jouLw8GD\nB9GsWTNPighA/1ldsWIFtm7ditzcXA9JpI/REIffnElK2sIYPHgwjh8/jtu3b+PevXv4+uuv0alT\nJy9KZ0cqZ35+PkpLS1FaWorXX38db731VpUqCyNI5SwvL8fvf/97fPDBB+jVq5cXpXJGKmdCQgLW\nrVuHiooKlJaWori4GN27d/eidHbatGljmzt5165dqmNa3uaZZ57Brl27AABnzpxBRUWFV5SFFlFR\nUbh8+bLt3QkNDUVBQYFXlIUe27Ztw8KFC5GTk4O6det6WxwbhkMcPDIUX8VkZ2eT0NBQUrduXdK8\neXMSHx9v+2/VqlWkU6dOJCoqyuueCVpyMlJTU0laWpoXpLOjJue7775LAgICSGxsrO1z5cqVaicn\nIYTMnTuXREREkHbt2tm8kqoDhw4dIt27dycxMTGkZ8+epKCgwNsiKVJRUUFGjx5NoqKiSJcuXcju\n3bu9LZIu4eHh1dZLqk2bNqR169a292bSpEneFsnG1q1bSWRkJImIiCDz5s3T3FZ3xj2BQCAQCIDf\noElKIBAIBFWDUBgCgUAg4EIoDIFAIBBwIRSGQCAQCLgQCkMgEAgEXAiFIRAIBAIuhMIQCAQCARdC\nYQgEAoGAi/8HnIqwBONccX4AAAAASUVORK5CYII=\n", - "text": [ - "" - ] - } - ], - "prompt_number": 7 - }, - { - "cell_type": "code", - "collapsed": false, - "input": [ - "@hope.jit\n", - "def exp_hope(x, y):\n", - " y[:] = np.exp(x)\n", - "\n", - "@hope.jit\n", - "def exppow_hope(x, y):\n", - " y[:] = hope.exp(x)\n", - " \n", - "y = np.empty_like(x)\n", - "\n", - "print \"numpy exp\"\n", - "%timeit exp_hope(x, y)\n", - "print \"polynomial exp\"\n", - "%timeit exppow_hope(x, y)" - ], - "language": "python", - "metadata": {}, - "outputs": [ - { - "output_type": "stream", - "stream": "stdout", - "text": [ - "numpy exp\n" - ] - }, - { - "output_type": "stream", - "stream": "stdout", - "text": [ - "1 loops, best of 3: 68.9 \u00b5s per loop\n", - "polynomial exp\n" - ] - }, - { - "output_type": "stream", - "stream": "stdout", - "text": [ - "1 loops, best of 3: 21 \u00b5s per loop\n" - ] - } - ], - "prompt_number": 8 - }, + } + ], + "source": [ + "@hope.jit\n", + "def tanh_hope(x, y):\n", + " y[:] = np.tanh(x)\n", + "\n", + "@hope.jit\n", + "def tanhpoly_hope(x, y):\n", + " a = np.fabs(x)\n", + " b = 1.26175667589988239 + a * (-0.54699348440059470 + a * 2.66559097474027817)\n", + " y[:] = (b * x) / (b * a + 1)\n", + "\n", + "y = np.empty_like(x)\n", + "\n", + "print \"numpy tanh\"\n", + "%timeit tanh_hope(x, y)\n", + "print \"polynomial approximation\"\n", + "%timeit tanhpoly_hope(x, y)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### exp" + ] + }, + { + "cell_type": "code", + "execution_count": 7, + "metadata": {}, + "outputs": [ { - "cell_type": "heading", - "level": 2, - "metadata": { - "slideshow": { - "slide_type": "slide" - } + "data": { + "image/png": "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\n", + "text/plain": [ + "" + ] }, - "source": [ - "Avoid floating point powers" - ] - }, - { - "cell_type": "code", - "collapsed": false, - "input": [ - "def native_pow(x, y):\n", - " y[:] = 16.11 - 3./(3. * x**2) + 0.5 *(0.4651 * x**-3 + 43.44) * x**-3 + 4. * x**-4\n", - " \n", - "@hope.jit\n", - "def float_pow(x, y):\n", - " y[:] = 16.11 - 3./(3. * x**2.) + 0.5 *(0.4651 * x**-3. + 43.44) * x**-3. + 4. * x**-4.\n", - "\n", - "@hope.jit\n", - "def int_pow(x, y):\n", - " y[:] = 16.11 - 3./(3. * x**2) + 0.5 *(0.4651 * x**-3 + 43.44) * x**-3 + 4. * x**-4\n", - "\n", - "x = np.linspace(-10, 10, 10000)\n", - "y = np.empty_like(x)\n", - "\n", - "print \"native python\"\n", - "%timeit native_pow(x, y)\n", - "print \"hope float power\"\n", - "%timeit float_pow(x, y)\n", - "print \"hope integer power\"\n", - "%timeit int_pow(x, y)" - ], - "language": "python", "metadata": {}, - "outputs": [ - { - "output_type": "stream", - "stream": "stdout", - "text": [ - "native python\n", - "1000 loops, best of 3: 1.14 ms per loop" - ] - }, - { - "output_type": "stream", - "stream": "stdout", - "text": [ - "\n", - "hope float power\n", - "Integer exponent as flaot: x.d[:x@0]**(-2.0)" - ] - }, - { - "output_type": "stream", - "stream": "stdout", - "text": [ - "\n", - "Integer exponent as flaot: x.d[:x@0]**(-4.0)\n", - "Integer exponent as flaot: x.d[:x@0]**(-6.0)\n", - "Integer exponent as flaot: x.d[:x@0]**(-3.0)\n" - ] - }, - { - "output_type": "stream", - "stream": "stdout", - "text": [ - "1 loops, best of 3: 1.43 ms per loop\n", - "hope integer power\n" - ] - }, - { - "output_type": "stream", - "stream": "stdout", - "text": [ - "1 loops, best of 3: 81.1 \u00b5s per loop\n" - ] - } - ], - "prompt_number": 9 - }, + "output_type": "display_data" + } + ], + "source": [ + "@hope.jit\n", + "def expapprox(x, y):\n", + " y[:] = hope.exp(x)\n", + " return y\n", + "\n", + "x = np.linspace(-16, 0, 10000)\n", + "y = np.empty_like(x)\n", + "\n", + "plt.subplot(3, 1, 1)\n", + "plt.plot(x, expapprox(x, y), label=\"approx\")\n", + "plt.plot(x, np.exp(x), label=\"exp\")\n", + "plt.ylabel('Exp(x)')\n", + "plt.legend()\n", + "\n", + "plt.subplot(3, 1, 2)\n", + "plt.semilogy()\n", + "plt.plot(x, np.fabs(expapprox(x, y)- np.exp(x)) + np.finfo(np.float64).resolution)\n", + "plt.ylabel('Absolute Error')\n", + "\n", + "plt.subplot(3, 1, 3)\n", + "plt.semilogy()\n", + "plt.plot(x, np.fabs(expapprox(x, y)- np.exp(x)) / np.exp(x) + np.finfo(np.float64).resolution)\n", + "plt.ylabel('Relative Error')\n", + "plt.show()" + ] + }, + { + "cell_type": "code", + "execution_count": 8, + "metadata": {}, + "outputs": [ { - "cell_type": "heading", - "level": 2, - "metadata": {}, - "source": [ - "Improve interpolation" + "ename": "SyntaxError", + "evalue": "Missing parentheses in call to 'print' (, line 11)", + "output_type": "error", + "traceback": [ + "\u001b[0;36m File \u001b[0;32m\"\"\u001b[0;36m, line \u001b[0;32m11\u001b[0m\n\u001b[0;31m print \"numpy exp\"\u001b[0m\n\u001b[0m ^\u001b[0m\n\u001b[0;31mSyntaxError\u001b[0m\u001b[0;31m:\u001b[0m Missing parentheses in call to 'print'\n" ] - }, + } + ], + "source": [ + "@hope.jit\n", + "def exp_hope(x, y):\n", + " y[:] = np.exp(x)\n", + "\n", + "@hope.jit\n", + "def exppow_hope(x, y):\n", + " y[:] = hope.exp(x)\n", + " \n", + "y = np.empty_like(x)\n", + "\n", + "print \"numpy exp\"\n", + "%timeit exp_hope(x, y)\n", + "print \"polynomial exp\"\n", + "%timeit exppow_hope(x, y)" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "source": [ + "## Avoid floating point powers" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "def native_pow(x, y):\n", + " y[:] = 16.11 - 3./(3. * x**2) + 0.5 *(0.4651 * x**-3 + 43.44) * x**-3 + 4. * x**-4\n", + " \n", + "@hope.jit\n", + "def float_pow(x, y):\n", + " y[:] = 16.11 - 3./(3. * x**2.) + 0.5 *(0.4651 * x**-3. + 43.44) * x**-3. + 4. * x**-4.\n", + "\n", + "@hope.jit\n", + "def int_pow(x, y):\n", + " y[:] = 16.11 - 3./(3. * x**2) + 0.5 *(0.4651 * x**-3 + 43.44) * x**-3 + 4. * x**-4\n", + "\n", + "x = np.linspace(-10, 10, 10000)\n", + "y = np.empty_like(x)\n", + "\n", + "print \"native python\"\n", + "%timeit native_pow(x, y)\n", + "print \"hope float power\"\n", + "%timeit float_pow(x, y)\n", + "print \"hope integer power\"\n", + "%timeit int_pow(x, y)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Improve interpolation" + ] + }, + { + "cell_type": "code", + "execution_count": 9, + "metadata": {}, + "outputs": [ { - "cell_type": "code", - "collapsed": false, - "input": [ - "X = np.empty(1100)\n", - "X[:1024] = np.linspace(-1., 1., 1024)\n", - "X[1024:] = X[1023] + X[1:77] - X[0]\n", - "SN = np.sin(X)\n", - "CN = np.cos(X)\n", - "\n", - "@hope.jit\n", - "def approx(x, sn, cn, X, SN, CN):\n", - " for i in range(100):\n", - " sn[i] = np.interp(x[i], X, SN)\n", - " cn[i] = np.interp(x[i], X, CN)\n", - "\n", - "@hope.jit\n", - "def approx_opt(x, sn, cn, X, SN, CN):\n", - " for i in range(100):\n", - " f = (x[i] - X[0]) / (X[1024] - X[0]) * 1024.\n", - " g = np.floor(f)\n", - " j = np.int_(g)\n", - " a = f - g\n", - " sn[i] = (1 - a) * SN[j] + a * SN[j + 1]\n", - " cn[i] = (1 - a) * CN[j] + a * CN[j + 1]\n", - "\n", - "x = 2. * random_sample(100) - 1\n", - "sn = np.empty_like(x)\n", - "cn = np.empty_like(x)\n", - "\n", - "%timeit approx(x, sn, cn, X, SN, CN)\n", - "%timeit approx_opt(x, sn, cn, X, SN, CN)" - ], - "language": "python", - "metadata": {}, - "outputs": [ - { - "output_type": "stream", - "stream": "stdout", - "text": [ - "1 loops, best of 3: 10 \u00b5s per loop\n" - ] - }, - { - "output_type": "stream", - "stream": "stdout", - "text": [ - "1 loops, best of 3: 954 ns per loop\n" - ] - } - ], - "prompt_number": 10 + "name": "stdout", + "output_type": "stream", + "text": [ + "9.79 µs ± 1.09 µs per loop (mean ± std. dev. of 7 runs, 1 loop each)\n", + "The slowest run took 6.76 times longer than the fastest. This could mean that an intermediate result is being cached.\n", + "1.91 µs ± 1.99 µs per loop (mean ± std. dev. of 7 runs, 1 loop each)\n" + ] } ], - "metadata": {} + "source": [ + "X = np.empty(1100)\n", + "X[:1024] = np.linspace(-1., 1., 1024)\n", + "X[1024:] = X[1023] + X[1:77] - X[0]\n", + "SN = np.sin(X)\n", + "CN = np.cos(X)\n", + "\n", + "@hope.jit\n", + "def approx(x, sn, cn, X, SN, CN):\n", + " for i in range(100):\n", + " sn[i] = np.interp(x[i], X, SN)\n", + " cn[i] = np.interp(x[i], X, CN)\n", + "\n", + "@hope.jit\n", + "def approx_opt(x, sn, cn, X, SN, CN):\n", + " for i in range(100):\n", + " f = (x[i] - X[0]) / (X[1024] - X[0]) * 1024.\n", + " g = np.floor(f)\n", + " j = np.int_(g)\n", + " a = f - g\n", + " sn[i] = (1 - a) * SN[j] + a * SN[j + 1]\n", + " cn[i] = (1 - a) * CN[j] + a * CN[j + 1]\n", + "\n", + "x = 2. * random_sample(100) - 1\n", + "sn = np.empty_like(x)\n", + "cn = np.empty_like(x)\n", + "\n", + "%timeit approx(x, sn, cn, X, SN, CN)\n", + "%timeit approx_opt(x, sn, cn, X, SN, CN)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": true + }, + "outputs": [], + "source": [] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.5.1" } - ] -} \ No newline at end of file + }, + "nbformat": 4, + "nbformat_minor": 1 +} diff --git a/benchmarks/compile b/benchmarks/compile new file mode 100644 index 0000000..22e8c02 --- /dev/null +++ b/benchmarks/compile @@ -0,0 +1 @@ +clang++ -bundle -undefined dynamic_lookup fib.o build/temp.macosx-10.11-x86_64-2.7/recfast4py/cosmology.Recfast.o build/temp.macosx-10.11-x86_64-2.7/recfast4py/evalode.Recfast.o build/temp.macosx-10.11-x86_64-2.7/recfast4py/recombination.Recfast.o build/temp.macosx-10.11-x86_64-2.7/recfast4py/ODE_solver.Recfast.o build/temp.macosx-10.11-x86_64-2.7/recfast4py/DM_annihilation.Recfast.o build/temp.macosx-10.11-x86_64-2.7/recfast4py/Rec_corrs_CT.Recfast.o -o build/lib.macosx-10.11-x86_64-2.7/recfast4py/_recfast.so diff --git a/benchmarks/fibonacci.ipynb b/benchmarks/fibonacci.ipynb index 9a09e74..52e3542 100644 --- a/benchmarks/fibonacci.ipynb +++ b/benchmarks/fibonacci.ipynb @@ -1,208 +1,208 @@ { - "metadata": { - "name": "", - "signature": "sha256:c2e8018cf3530fa66b1f12e3044c71d39d6f7c1954acb15ff475eefbef500867" - }, - "nbformat": 3, - "nbformat_minor": 0, - "worksheets": [ + "cells": [ + { + "cell_type": "code", + "execution_count": 1, + "metadata": { + "collapsed": true + }, + "outputs": [], + "source": [ + "from util import perf_comp_data" + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "metadata": { + "collapsed": true + }, + "outputs": [], + "source": [ + "def fib(n):\n", + " if n < 2:\n", + " return n\n", + " return fib(n - 1) + fib(n - 2)\n", + "\n", + "assert fib(20) == 6765" + ] + }, { - "cells": [ + "cell_type": "code", + "execution_count": 3, + "metadata": { + "collapsed": true + }, + "outputs": [], + "source": [ + "from hope import jit\n", + "\n", + "hope_fib = jit(fib)\n", + "\n", + "assert hope_fib(20) == 6765" + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "metadata": { + "collapsed": true + }, + "outputs": [], + "source": [ + "from numba import jit\n", + "\n", + "numba_fib = jit(fib)\n", + "assert numba_fib(20) == 6765" + ] + }, + { + "cell_type": "code", + "execution_count": 5, + "metadata": {}, + "outputs": [ { - "cell_type": "code", - "collapsed": false, - "input": [ - "from util import perf_comp_data" - ], - "language": "python", - "metadata": {}, - "outputs": [], - "prompt_number": 1 - }, + "name": "stdout", + "output_type": "stream", + "text": [ + "python\n", + "3.13 ms ± 139 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)\n", + "hope 1\n", + "39.4 µs ± 617 ns per loop (mean ± std. dev. of 7 runs, 10000 loops each)\n", + "numba\n", + "3.07 ms ± 40.8 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)\n" + ] + } + ], + "source": [ + "print(\"python\")\n", + "%timeit fib(20)\n", + "print(\"hope 1\")\n", + "%timeit hope_fib(20)\n", + "print(\"numba\")\n", + "%timeit numba_fib(20)" + ] + }, + { + "cell_type": "code", + "execution_count": 6, + "metadata": {}, + "outputs": [ { - "cell_type": "code", - "collapsed": false, - "input": [ + "name": "stdout", + "output_type": "stream", + "text": [ + "function: hope_fib , av. time sec: 0.00005121, min. time sec: 0.00004093, relative: 1.0\n", + "function: numba_fib , av. time sec: 0.00359036, min. time sec: 0.00320426, relative: 70.1\n", + "function: fib , av. time sec: 0.00361408, min. time sec: 0.00353485, relative: 70.6\n" + ] + } + ], + "source": [ + "n=20\n", + "perf_comp_data([\"fib\", \"hope_fib\", \"numba_fib\"], 3*[\"n\"])" + ] + }, + { + "cell_type": "code", + "execution_count": 7, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "fib (int64,)\n", + "--------------------------------------------------------------------------------\n", + "# File: \n", + "# --- LINE 1 --- \n", + "# label 0\n", + "# del $const0.2\n", + "\n", "def fib(n):\n", + "\n", + " # --- LINE 2 --- \n", + " # n = arg(0, name=n) :: pyobject\n", + " # $const0.2 = const(int, 2) :: pyobject\n", + " # $0.3 = n < $const0.2 :: pyobject\n", + " # branch $0.3, 12, 16\n", + " # label 12\n", + " # del $0.3\n", + " # del n\n", + "\n", " if n < 2:\n", + "\n", + " # --- LINE 3 --- \n", + " # $12.2 = cast(value=n) :: pyobject\n", + " # return $12.2\n", + " # label 16\n", + " # del $0.3\n", + " # del $const16.3\n", + " # del $16.4\n", + " # del $16.1\n", + " # del n\n", + " # del $const16.8\n", + " # del $16.9\n", + " # del $16.6\n", + " # del $16.5\n", + " # del $16.10\n", + " # del $16.11\n", + "\n", " return n\n", - " return fib(n - 1) + fib(n - 2)\n", "\n", - "assert fib(20) == 6765" - ], - "language": "python", - "metadata": {}, - "outputs": [], - "prompt_number": 2 - }, - { - "cell_type": "code", - "collapsed": false, - "input": [ - "from hope import jit\n", + " # --- LINE 4 --- \n", + " # $16.1 = global(fib: ) :: pyobject\n", + " # $const16.3 = const(int, 1) :: pyobject\n", + " # $16.4 = n - $const16.3 :: pyobject\n", + " # $16.5 = call $16.1($16.4, kws=[], args=[Var($16.4, (4))], func=$16.1, vararg=None) :: pyobject\n", + " # $16.6 = global(fib: ) :: pyobject\n", + " # $const16.8 = const(int, 2) :: pyobject\n", + " # $16.9 = n - $const16.8 :: pyobject\n", + " # $16.10 = call $16.6($16.9, kws=[], args=[Var($16.9, (4))], func=$16.6, vararg=None) :: pyobject\n", + " # $16.11 = $16.5 + $16.10 :: pyobject\n", + " # $16.12 = cast(value=$16.11) :: pyobject\n", + " # return $16.12\n", "\n", - "hope_fib = jit(fib)\n", + " return fib(n - 1) + fib(n - 2)\n", "\n", - "assert hope_fib(20) == 6765" - ], - "language": "python", - "metadata": {}, - "outputs": [], - "prompt_number": 3 - }, - { - "cell_type": "code", - "collapsed": false, - "input": [ - "from numba import jit\n", "\n", - "numba_fib = jit(fib)\n", - "assert numba_fib(20) == 6765" - ], - "language": "python", - "metadata": {}, - "outputs": [], - "prompt_number": 4 - }, - { - "cell_type": "code", - "collapsed": false, - "input": [ - "print \"python\"\n", - "%timeit fib(20)\n", - "print \"hope 1\"\n", - "%timeit hope_fib(20)\n", - "print \"numba\"\n", - "%timeit numba_fib(20)" - ], - "language": "python", - "metadata": {}, - "outputs": [ - { - "output_type": "stream", - "stream": "stdout", - "text": [ - "python\n", - "100 loops, best of 3: 2.59 ms per loop" - ] - }, - { - "output_type": "stream", - "stream": "stdout", - "text": [ - "\n", - "hope 1\n", - "10000 loops, best of 3: 42 \u00b5s per loop" - ] - }, - { - "output_type": "stream", - "stream": "stdout", - "text": [ - "\n", - "numba\n", - "100 loops, best of 3: 2.67 ms per loop" - ] - }, - { - "output_type": "stream", - "stream": "stdout", - "text": [ - "\n" - ] - } - ], - "prompt_number": 5 - }, - { - "cell_type": "code", - "collapsed": false, - "input": [ - "n=20\n", - "perf_comp_data([\"fib\", \"hope_fib\", \"numba_fib\"], 3*[\"n\"])" - ], - "language": "python", - "metadata": {}, - "outputs": [ - { - "output_type": "stream", - "stream": "stdout", - "text": [ - "function: hope_fib , av. time sec: 0.00005205, relative: 1.0\n", - "function: fib , av. time sec: 0.00290926, relative: 55.9\n", - "function: numba_fib , av. time sec: 0.00306726, relative: 58.9\n" - ] - } - ], - "prompt_number": 6 - }, - { - "cell_type": "code", - "collapsed": false, - "input": [ - "numba_fib.inspect_types()" - ], - "language": "python", - "metadata": {}, - "outputs": [ - { - "output_type": "stream", - "stream": "stdout", - "text": [ - "fib (int64,)\n", - "--------------------------------------------------------------------------------\n", - "# File: \n", - "# --- LINE 1 --- \n", - "\n", - "def fib(n):\n", - "\n", - " # --- LINE 2 --- \n", - " # label 0\n", - " # n.1 = n :: pyobject\n", - " # $const0.2 = const(, 2) :: pyobject\n", - " # $0.3 = n.1 < $const0.2 :: pyobject\n", - " # branch $0.3, 12, 16\n", - "\n", - " if n < 2:\n", - "\n", - " # --- LINE 3 --- \n", - " # label 12\n", - " # return n.1\n", - "\n", - " return n\n", - "\n", - " # --- LINE 4 --- \n", - " # label 16\n", - " # $16.1 = global(fib: ) :: pyobject\n", - " # $const16.3 = const(, 1) :: pyobject\n", - " # $16.4 = n.1 - $const16.3 :: pyobject\n", - " # $16.5 = call $16.1($16.4, ) :: pyobject\n", - " # $16.6 = global(fib: ) :: pyobject\n", - " # $const16.8 = const(, 2) :: pyobject\n", - " # $16.9 = n.1 - $const16.8 :: pyobject\n", - " # $16.10 = call $16.6($16.9, ) :: pyobject\n", - " # $16.11 = $16.5 + $16.10 :: pyobject\n", - " # return $16.11\n", - "\n", - " return fib(n - 1) + fib(n - 2)\n", - "\n", - "\n", - "================================================================================\n" - ] - } - ], - "prompt_number": 7 - }, - { - "cell_type": "code", - "collapsed": false, - "input": [], - "language": "python", - "metadata": {}, - "outputs": [] + "================================================================================\n" + ] } ], - "metadata": {} + "source": [ + "numba_fib.inspect_types()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": true + }, + "outputs": [], + "source": [] } - ] -} \ No newline at end of file + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.5.1" + } + }, + "nbformat": 4, + "nbformat_minor": 1 +} diff --git a/benchmarks/hope/fib_181039868ed022600dc3388e38604df736dff187257d5d6ae4c4d3ea.pck b/benchmarks/hope/fib_181039868ed022600dc3388e38604df736dff187257d5d6ae4c4d3ea.pck new file mode 100644 index 0000000000000000000000000000000000000000..33f61c4c2f2958dfac7122bd5efb7e486bfcb07a GIT binary patch literal 486 zcmYjN*=~YB6t#6N)vnt02TTkEM3Fw&-*8?UGR$0LA`Dy>W15)sQUAIFrpv=5bMA7^ zx#z37$Htg+8;!;mvRot*&q!M#({Kz6&xxk~8I@~bA+QZLkEEw6Nm__ld;+!(q@!?} zK_GV6CM^ZRBn9l$EPhf-j@{EzAR>@72JDe{H5Lfpvs}xx-(1H9Zce9;X>w-Ut_!y7 zm@^(MY=;Md$h4b(I7g7g*18VZl?c0okfIcltuHY#(8%(eeL4KHis zzq9=h9E@v^`ie#oUXh`q(^oRlOIhZ?^)1Z=^&`M*GN@WH%~-gUv=Cds8*)|E@`6Xg zU#iC@6TGd{s)|bmEer3+=#Ml5%EF3JX9)+qho98_BnKQG>g-}FB#uZ^t*LzQp*CGz o6+xck<0;4uKw3bY;?pU8rVap(&wG5SL-CXpuawla_aZ(TL1t6 literal 0 HcmV?d00001 diff --git a/benchmarks/hope/fib_181039868ed022600dc3388e38604df736dff187257d5d6ae4c4d3ea_0.cpp b/benchmarks/hope/fib_181039868ed022600dc3388e38604df736dff187257d5d6ae4c4d3ea_0.cpp new file mode 100644 index 0000000..124b93e --- /dev/null +++ b/benchmarks/hope/fib_181039868ed022600dc3388e38604df736dff187257d5d6ae4c4d3ea_0.cpp @@ -0,0 +1,157 @@ + +#define PY_ARRAY_UNIQUE_SYMBOL fkt_ARRAY_API +#define NPY_NO_DEPRECATED_API NPY_1_7_API_VERSION +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + + +struct PyObj { + typedef PyObject * ptr_t; + typedef PyArrayObject * arrptr_t; + PyObj(): dec(false), ptr(NULL) {} + PyObj(ptr_t p): dec(false), ptr(p) {} + ~PyObj() { if(dec) Py_DECREF(ptr); } + PyObj & operator=(ptr_t p) { if(dec) Py_DECREF(ptr); ptr = p; dec = false; return *this; } + PyObj & incref(ptr_t p) { if(dec) Py_DECREF(ptr); ptr = p; dec = (p != NULL); return *this; } + operator bool() const { return ptr; } + operator ptr_t() const { return ptr; } + operator arrptr_t() const { return (arrptr_t)ptr; } + bool dec; + ptr_t ptr; +}; + +inline npy_int64 fib_J( + npy_int64 cn +); +inline npy_int64 fib_J( + npy_int64 cn +) { + if ((npy_bool)(cn < (npy_int64)2)) { + return cn; + } + return (npy_int64)((npy_int64)fib_J((npy_int64)((npy_int64)cn - (npy_int64)(npy_int64)1)) + (npy_int64)fib_J((npy_int64)((npy_int64)cn - (npy_int64)(npy_int64)2))); + PyErr_SetString(PyExc_ValueError, "No return type passed!"); + throw std::exception(); +} + +#include +#include +#include +#include +#include +#include + +void sighandler(int sig); + +void sighandler(int sig) { + std::ostringstream buffer; + buffer << "Abort by " << (sig == SIGSEGV ? "segfault" : "bus error") << std::endl; + void * stack[64]; + std::size_t depth = backtrace(stack, 64); + if (!depth) + buffer << " " << std::endl; + else { + char ** symbols = backtrace_symbols(stack, depth); + for (std::size_t i = 1; i < depth; ++i) { + std::string symbol = symbols[i]; + if (symbol.find_first_of(' ', 59) != std::string::npos) { + std::string name = symbol.substr(59, symbol.find_first_of(' ', 59) - 59); + int status; + char * demangled = abi::__cxa_demangle(name.c_str(), NULL, NULL, &status); + if (!status) { + buffer << " " + << symbol.substr(0, 59) + << demangled + << symbol.substr(59 + name.size()) + << std::endl; + free(demangled); + } else + buffer << " " << symbol << std::endl; + } else + buffer << " " << symbol << std::endl; + } + free(symbols); + } + std::cerr << buffer.str(); + std::exit(EXIT_FAILURE); + } + + +extern "C" { + + PyObject * create_signature; + + struct sigaction slot; + + PyObject * set_create_signature(PyObject * self, PyObject * args) { + if (!PyArg_ParseTuple(args, "O", &create_signature)) { + PyErr_SetString(PyExc_ValueError, "Invalid Argument to set_create_signature!"); + return NULL; + } + Py_INCREF(create_signature); + memset(&slot, 0, sizeof(slot)); + slot.sa_handler = &sighandler; + sigaction(SIGSEGV, &slot, NULL); + sigaction(SIGBUS, &slot, NULL); + Py_INCREF(Py_None); + return Py_None; + } + + PyObject * run(PyObject * self, PyObject * args) { + { + PyObject * pn; npy_int64 cn; + if ( + PyTuple_CheckExact(args) and PyTuple_GET_SIZE(args) == 1 + and (pn = PyTuple_GET_ITEM(args, 0)) and PyLong_CheckExact(pn) + ) { + cn = PyLong_AS_LONG(pn); + try { + return Py_BuildValue("l", fib_J( + cn + )); + } catch (...) { + return NULL; + } + } else + PyErr_Clear(); + } + PyObject * signatures = Py_BuildValue("(sO)", "gANdcQBdcQFjaG9wZS5fYXN0ClZhcmlhYmxlCnECKYFxA31xBChYBQAAAGR0eXBlcQVjYnVpbHRp\nbnMKaW50CnEGWAQAAABkaW1zcQdLAFgEAAAAbmFtZXEIWAEAAABucQl1YmFhLg==\n", args); + if (!signatures) { + PyErr_SetString(PyExc_ValueError, "Error building signature string for fib"); + return NULL; + } + return PyObject_Call(create_signature, signatures, NULL); + } + + PyMethodDef fibMethods[] = { + { "set_create_signature", set_create_signature, METH_VARARGS, "signal handler" }, + { "run", run, METH_VARARGS, "module function" }, + { NULL, NULL, 0, NULL } + }; + + + static struct PyModuleDef fibmodule = { + PyModuleDef_HEAD_INIT, + "fib", + NULL, + -1, + fibMethods + }; + + + PyMODINIT_FUNC PyInit_fib_181039868ed022600dc3388e38604df736dff187257d5d6ae4c4d3ea_0(void) { + import_array(); + PyImport_ImportModule("numpy"); + return PyModule_Create(&fibmodule); + } + +} diff --git a/benchmarks/hope/fib_181039868ed022600dc3388e38604df736dff187257d5d6ae4c4d3ea_0.o b/benchmarks/hope/fib_181039868ed022600dc3388e38604df736dff187257d5d6ae4c4d3ea_0.o new file mode 100644 index 0000000000000000000000000000000000000000..2be39f820119359e4ae490d669a4403f1823da6b GIT binary patch literal 334924 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

OT24|LOV6boZj4)IlAvf87*LcT~|z$ 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

j=ZfAI5%Hs^LO?j2!btxY) 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 literal 0 HcmV?d00001 diff --git a/benchmarks/hope/pisum_49f15c96f2c4d5ce51371d6a5362b67ac5531087a5dd1885eaba960b_0.cpp b/benchmarks/hope/pisum_49f15c96f2c4d5ce51371d6a5362b67ac5531087a5dd1885eaba960b_0.cpp new file mode 100644 index 0000000..d9cac5c --- /dev/null +++ b/benchmarks/hope/pisum_49f15c96f2c4d5ce51371d6a5362b67ac5531087a5dd1885eaba960b_0.cpp @@ -0,0 +1,150 @@ + +#define PY_ARRAY_UNIQUE_SYMBOL fkt_ARRAY_API +#define NPY_NO_DEPRECATED_API NPY_1_7_API_VERSION +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + + +struct PyObj { + typedef PyObject * ptr_t; + typedef PyArrayObject * arrptr_t; + PyObj(): dec(false), ptr(NULL) {} + PyObj(ptr_t p): dec(false), ptr(p) {} + ~PyObj() { if(dec) Py_DECREF(ptr); } + PyObj & operator=(ptr_t p) { if(dec) Py_DECREF(ptr); ptr = p; dec = false; return *this; } + PyObj & incref(ptr_t p) { if(dec) Py_DECREF(ptr); ptr = p; dec = (p != NULL); return *this; } + operator bool() const { return ptr; } + operator ptr_t() const { return ptr; } + operator arrptr_t() const { return (arrptr_t)ptr; } + bool dec; + ptr_t ptr; +}; + +inline npy_double pisum_( + ); +inline npy_double pisum_( + ) { + npy_double csum = npy_double(); + for (npy_intp cj = (npy_int64)1; cj < (npy_int64)501; ++cj) { + csum = (npy_double)0.0; + auto cf = (npy_double)0.0; + for (npy_intp ck = (npy_int64)1; ck < (npy_int64)10001; ++ck) { + auto c__sp0 = (npy_int64)((npy_int64)ck * (npy_int64)ck); + csum += (npy_double)((npy_double)(npy_double)1.0 / (npy_double)c__sp0); + } + } + return csum; + PyErr_SetString(PyExc_ValueError, "No return type passed!"); + throw std::exception(); +} + +#include +#include +#include +#include +#include +#include + +void sighandler(int sig); + +void sighandler(int sig) { + std::ostringstream buffer; + buffer << "Abort by " << (sig == SIGSEGV ? "segfault" : "bus error") << std::endl; + void * stack[64]; + std::size_t depth = backtrace(stack, 64); + if (!depth) + buffer << " " << std::endl; + else { + char ** symbols = backtrace_symbols(stack, depth); + for (std::size_t i = 1; i < depth; ++i) { + std::string symbol = symbols[i]; + if (symbol.find_first_of(' ', 59) != std::string::npos) { + std::string name = symbol.substr(59, symbol.find_first_of(' ', 59) - 59); + int status; + char * demangled = abi::__cxa_demangle(name.c_str(), NULL, NULL, &status); + if (!status) { + buffer << " " + << symbol.substr(0, 59) + << demangled + << symbol.substr(59 + name.size()) + << std::endl; + free(demangled); + } else + buffer << " " << symbol << std::endl; + } else + buffer << " " << symbol << std::endl; + } + free(symbols); + } + std::cerr << buffer.str(); + std::exit(EXIT_FAILURE); + } + + +extern "C" { + + PyObject * create_signature; + + struct sigaction slot; + + PyObject * set_create_signature(PyObject * self, PyObject * args) { + if (!PyArg_ParseTuple(args, "O", &create_signature)) { + PyErr_SetString(PyExc_ValueError, "Invalid Argument to set_create_signature!"); + return NULL; + } + Py_INCREF(create_signature); + memset(&slot, 0, sizeof(slot)); + slot.sa_handler = &sighandler; + sigaction(SIGSEGV, &slot, NULL); + sigaction(SIGBUS, &slot, NULL); + Py_INCREF(Py_None); + return Py_None; + } + + PyObject * run(PyObject * self, PyObject * args) { + { try { + return Py_BuildValue("d", pisum_()); + } catch (...) { + return NULL; + } + } + PyObject * signatures = Py_BuildValue("(sO)", "gANdcQBdcQFhLg==\n", args); + if (!signatures) { + PyErr_SetString(PyExc_ValueError, "Error building signature string for pisum"); + return NULL; + } + return PyObject_Call(create_signature, signatures, NULL); + } + + PyMethodDef pisumMethods[] = { + { "set_create_signature", set_create_signature, METH_VARARGS, "signal handler" }, + { "run", run, METH_VARARGS, "module function" }, + { NULL, NULL, 0, NULL } + }; + + + static struct PyModuleDef pisummodule = { + PyModuleDef_HEAD_INIT, + "pisum", + NULL, + -1, + pisumMethods + }; + + + PyMODINIT_FUNC PyInit_pisum_49f15c96f2c4d5ce51371d6a5362b67ac5531087a5dd1885eaba960b_0(void) { + import_array(); + PyImport_ImportModule("numpy"); + return PyModule_Create(&pisummodule); + } + +} diff --git a/benchmarks/hope/pisum_49f15c96f2c4d5ce51371d6a5362b67ac5531087a5dd1885eaba960b_0.o b/benchmarks/hope/pisum_49f15c96f2c4d5ce51371d6a5362b67ac5531087a5dd1885eaba960b_0.o new file mode 100644 index 0000000000000000000000000000000000000000..7166b554cde74eb1727f4777b90afe855f765aa9 GIT binary patch literal 334120 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 x^G|nl{0E{_CFDAH#%Z`dBXF{{SZ?Qa1nq literal 0 HcmV?d00001 diff --git a/benchmarks/hope/pisum_49f15c96f2c4d5ce51371d6a5362b67ac5531087a5dd1885eaba960b_0.out b/benchmarks/hope/pisum_49f15c96f2c4d5ce51371d6a5362b67ac5531087a5dd1885eaba960b_0.out new file mode 100644 index 0000000..b3b8cc0 --- /dev/null +++ b/benchmarks/hope/pisum_49f15c96f2c4d5ce51371d6a5362b67ac5531087a5dd1885eaba960b_0.out @@ -0,0 +1,8 @@ +running build_ext +building 'pisum_49f15c96f2c4d5ce51371d6a5362b67ac5531087a5dd1885eaba960b_0' extension +C compiler: /usr/bin/clang -fno-strict-aliasing -Wsign-compare -Wunreachable-code -fno-common -dynamic -DNDEBUG -g -fwrapv -O3 -Wall -arch i386 -arch x86_64 -g + +compile options: '-I/Users/uweschmitt/Projects/hope/venv3/lib/python3.5/site-packages/numpy/core/include -I/Users/uweschmitt/Projects/hope/venv3/include -I/Library/Frameworks/Python.framework/Versions/3.5/include/python3.5m -c' +extra options: '-Wall -Wno-unused-variable -march=native -stdlib=libc++ -std=c++11 -Wno-unreachable-code' +clang: /var/folders/k8/zfp7dvcs1m326gz1brql1tv80000gn/T/hope1zybbqy0/pisum_49f15c96f2c4d5ce51371d6a5362b67ac5531087a5dd1885eaba960b_0.cpp +/usr/bin/clang++ -bundle -undefined dynamic_lookup -arch i386 -arch x86_64 -g /var/folders/k8/zfp7dvcs1m326gz1brql1tv80000gn/T/hope1zybbqy0/pisum_49f15c96f2c4d5ce51371d6a5362b67ac5531087a5dd1885eaba960b_0.o -o /var/folders/k8/zfp7dvcs1m326gz1brql1tv80000gn/T/hope1zybbqy0/pisum_49f15c96f2c4d5ce51371d6a5362b67ac5531087a5dd1885eaba960b_0.cpython-35m-darwin.so diff --git a/benchmarks/hope/pisum_opt_d21ddf605a07e3a4d8a460735cef49c69a69c967957f695741c0feae.pck b/benchmarks/hope/pisum_opt_d21ddf605a07e3a4d8a460735cef49c69a69c967957f695741c0feae.pck new file mode 100644 index 0000000000000000000000000000000000000000..af50eca1d4caf3ad09df2626e1f4d70d04ee79ba GIT binary patch literal 498 zcmYjO$!@|h5T#{Lp(}L#0VEWZ6iAu_xBdgZRFSbKV3BR&1ffbuJ;Gnt_JQ_b%d@;U z?~Q-z2ds@quU4zAp$yr6AH{Sn@q%r#941*_iY%4Z|C)uDq@(&H5kpE7z&iO8q zgbEPycbD_XbLQ0XVM=HGgU&q1pUxRXvw(R4^#T@nelYhVPhB&YIT26@9c&n8Jvs&{ zv3VkGMZJSUDjC=!O%*v4Z{lLxyjNM3a_pRlr3wX2*T6Qww%9!bSD6H~j>chF0OfQQ zgR+TNCo}8pxc7zqks-C0G>-9_3>2rmOf$PmD+%1*(L!i6-jKd7CA46hMM_Jt1H2{I zIwwmWi`7CMHXh@h(WR3M6}m3oli?q!Qo}Zy&}JD2d;lY83y_zPUmxv_asg7m< literal 0 HcmV?d00001 diff --git a/benchmarks/hope/pisum_opt_d21ddf605a07e3a4d8a460735cef49c69a69c967957f695741c0feae_0.cpp b/benchmarks/hope/pisum_opt_d21ddf605a07e3a4d8a460735cef49c69a69c967957f695741c0feae_0.cpp new file mode 100644 index 0000000..3e6984f --- /dev/null +++ b/benchmarks/hope/pisum_opt_d21ddf605a07e3a4d8a460735cef49c69a69c967957f695741c0feae_0.cpp @@ -0,0 +1,152 @@ + +#define PY_ARRAY_UNIQUE_SYMBOL fkt_ARRAY_API +#define NPY_NO_DEPRECATED_API NPY_1_7_API_VERSION +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + + +struct PyObj { + typedef PyObject * ptr_t; + typedef PyArrayObject * arrptr_t; + PyObj(): dec(false), ptr(NULL) {} + PyObj(ptr_t p): dec(false), ptr(p) {} + ~PyObj() { if(dec) Py_DECREF(ptr); } + PyObj & operator=(ptr_t p) { if(dec) Py_DECREF(ptr); ptr = p; dec = false; return *this; } + PyObj & incref(ptr_t p) { if(dec) Py_DECREF(ptr); ptr = p; dec = (p != NULL); return *this; } + operator bool() const { return ptr; } + operator ptr_t() const { return ptr; } + operator arrptr_t() const { return (arrptr_t)ptr; } + bool dec; + ptr_t ptr; +}; + +inline npy_double pisum_opt_( + ); +inline npy_double pisum_opt_( + ) { + npy_double csum = npy_double(); + for (npy_intp cj = (npy_int64)1; cj < (npy_int64)501; ++cj) { + csum = (npy_double)0.0; + npy_double cf = npy_double(); + cf = (npy_double)0.0; + for (npy_intp ck = (npy_int64)1; ck < (npy_int64)10001; ++ck) { + cf += (npy_double)1.0; + auto c__sp0 = (npy_double)((npy_double)cf * (npy_double)cf); + csum += (npy_double)((npy_double)(npy_double)1.0 / (npy_double)c__sp0); + } + } + return csum; + PyErr_SetString(PyExc_ValueError, "No return type passed!"); + throw std::exception(); +} + +#include +#include +#include +#include +#include +#include + +void sighandler(int sig); + +void sighandler(int sig) { + std::ostringstream buffer; + buffer << "Abort by " << (sig == SIGSEGV ? "segfault" : "bus error") << std::endl; + void * stack[64]; + std::size_t depth = backtrace(stack, 64); + if (!depth) + buffer << " " << std::endl; + else { + char ** symbols = backtrace_symbols(stack, depth); + for (std::size_t i = 1; i < depth; ++i) { + std::string symbol = symbols[i]; + if (symbol.find_first_of(' ', 59) != std::string::npos) { + std::string name = symbol.substr(59, symbol.find_first_of(' ', 59) - 59); + int status; + char * demangled = abi::__cxa_demangle(name.c_str(), NULL, NULL, &status); + if (!status) { + buffer << " " + << symbol.substr(0, 59) + << demangled + << symbol.substr(59 + name.size()) + << std::endl; + free(demangled); + } else + buffer << " " << symbol << std::endl; + } else + buffer << " " << symbol << std::endl; + } + free(symbols); + } + std::cerr << buffer.str(); + std::exit(EXIT_FAILURE); + } + + +extern "C" { + + PyObject * create_signature; + + struct sigaction slot; + + PyObject * set_create_signature(PyObject * self, PyObject * args) { + if (!PyArg_ParseTuple(args, "O", &create_signature)) { + PyErr_SetString(PyExc_ValueError, "Invalid Argument to set_create_signature!"); + return NULL; + } + Py_INCREF(create_signature); + memset(&slot, 0, sizeof(slot)); + slot.sa_handler = &sighandler; + sigaction(SIGSEGV, &slot, NULL); + sigaction(SIGBUS, &slot, NULL); + Py_INCREF(Py_None); + return Py_None; + } + + PyObject * run(PyObject * self, PyObject * args) { + { try { + return Py_BuildValue("d", pisum_opt_()); + } catch (...) { + return NULL; + } + } + PyObject * signatures = Py_BuildValue("(sO)", "gANdcQBdcQFhLg==\n", args); + if (!signatures) { + PyErr_SetString(PyExc_ValueError, "Error building signature string for pisum_opt"); + return NULL; + } + return PyObject_Call(create_signature, signatures, NULL); + } + + PyMethodDef pisum_optMethods[] = { + { "set_create_signature", set_create_signature, METH_VARARGS, "signal handler" }, + { "run", run, METH_VARARGS, "module function" }, + { NULL, NULL, 0, NULL } + }; + + + static struct PyModuleDef pisum_optmodule = { + PyModuleDef_HEAD_INIT, + "pisum_opt", + NULL, + -1, + pisum_optMethods + }; + + + PyMODINIT_FUNC PyInit_pisum_opt_d21ddf605a07e3a4d8a460735cef49c69a69c967957f695741c0feae_0(void) { + import_array(); + PyImport_ImportModule("numpy"); + return PyModule_Create(&pisum_optmodule); + } + +} diff --git a/benchmarks/hope/pisum_opt_d21ddf605a07e3a4d8a460735cef49c69a69c967957f695741c0feae_0.o b/benchmarks/hope/pisum_opt_d21ddf605a07e3a4d8a460735cef49c69a69c967957f695741c0feae_0.o new file mode 100644 index 0000000000000000000000000000000000000000..7c72f9ba7246a4b3ca2e791d4dfd7c0949202cb9 GIT binary patch literal 334220 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࡚YisVl(NGD)?I-bM$y-B6^%96%vj=-slKmqz!=S1YI^N zoHOj&Sw+|Z8X*~-RF5P;Bf+Q~*U2cZQSoH0{PbZp$bYn563;n>GYYFcqhPTyHZnhn zEYEZ3Fm?je9CZwayi``DDki(2hR%Ay@EL{Y6rz%yT~dj>ezHNPW)8G35a;$u5R51Il)0j#WvMt>hj%cia=} zh4Z6qmC5`)cK*z*Dp|TzLbW4>U5u%tV5{lFn6Sp=)2sBgj+71}4O5pXH|(oAQrOxl zH9fWPO4XY@ZH3OVjRk)?TWhD(jANiCeT;{E7n2bZNdCbqukq5h-S5P15eVD^Y4~vOLJVS9!e0X(eFe zb#4W1JR>2;C((RHo9c;$O8&DXLR@o-Oerz%#srdRv^pEzVwD{@+`~Xgh9!%=p(e#l z!F*T&8#4Kl75O(NWkP++xS=G{WQ0{5#~^xbTL~gcg-^v`)PAnSH(BluB_meDK{Q$` z#F%M6wg+LEYTBd&w|TNt@P*>!e4 zylDRLa!)gqzlt`~9r$NRGWcySKpqF<)j$~HXI&l)eO(qtscR5(uxg_th+MxxG1D0wO=z5p!m#}L>Aj_Ene-3&P!o4b_(w_Qxm&4(~ zbA!O**??|LLh!sV*jhFZr+eJwq7yN8vrRF&SnA&*EC#96t)f1Y+=e8ufN?<-mah9n zTTt_GP-!NsGg9hyOO~#VFF81EYby0+2b$G|ogpTJ#dNZ-SP+xYJMW}Ij)w46tyW6L zUjvSk?s83548N{<82tvKIvcfO7Z(!z%Y%h+Ri`t+-w*H;)YBL+a{dVDEMi=-oc!EI z4~E-w0OsYthAWrbu3^6X+~ve``BTt@%Ln-7w+*3Scm8GWLWI{670Rhl?m2+&Bh;Ex zk*5l7Li9MG@_tZoC!#!~k~_NK9z-W1n$tP-ECl&^xjzPG*ae6`oxjrMS&7J9IH8cg z$_1I8D8Qc+ycl4iXB$At@DhL}o^B^t=HbbfKaqJYs3Sf10eqXF7vLz*eK5)_Ojh&P z0W9}C1<*&(2XLI{PXMna*aUEb=VYY&8^LCPlRT3FzCmyUz)H^wIN{|u0h1pDSmQYr zl)GWl{L27N_gn$61U{7C25^SwHh^c5ISg=?=c@okay!7eo^J!>%rk!z!1Fwh0h~iH z0&t;+#{-rS>;Ty4`2r3VT#3e*zXjlO&vyZeaV`hA%JU=0Q)T zRuQ}r;131~T%K0|e@OOQ0AJ_wybsuo zE-(L9z~~yvptERp8{ivVo>Ksqk^V)%x41l00hbfL9q{cg&pg1h34aOjS6rSIfY%fL zGT^&ho)*AjhdTg&%jF3J7Ja?~_#T&Mi-GS1{9TvlGY0-D;P1OU*BSV0fPd`r+-Bgr z06*yRd;_p}-PZv>;_}=J_%qD^TYz`EJWm?>cK|=(QU(7GXY#@~^P`1@`R#6cJR4a4 zCN`ptfTZ-1oC>$feIC5WynxHlD?li4741d*TTJ{ZSMKYG7niUss>sE2H^k3z;QRf@n+~y9^CWq+E?Q={^|{dzpf3&?MdC z0VT+}9WBy53D6yccA!DJs{uVs=vuT#cMG5o30;Tg=xzlhIV8{;-J1X%hlWz{IkZ0a z)qo_wZZtmkc0glEyAf^AeG8x(LSM+KnB)F3Adzwl8lwBFCfyg&@Z5I;`Y+~pJKCMf zU4`h-7f>#@Yv?}_70pVIP=#Bhu-bV4+p+T`6$M{Qz~% zaFwawFz^MS; zs^A51x`507PVP&HCN)t-B#kF&fh+eN#EWv#qn%B}iLSyM^Z(a<0pcg3?d8A0idkcz zH|fkl1HDCO?l92Xbmq?*=pfzqbAY7xekZ4*%>8vhqVBtN=KnCzdvxZ94D>#o`AI<1 z>wG|G{;i>XNN0W-kZAUQbY`|_fj*)$ziFUD99iBskb*s?d)UZ@)dg^dX9z&iCI{dw z4^Q&ZZ}M{i&h?yVsCfX-^S~W-+5&(JJ#76V3XY7Cg(vw0bOT)Oxx|1&0j~0d0iH!u z4gHI2}Lq|~pmuBRgKf!g}g#1x1&t?Fp zae8nvN})X08LdVG%=6p?@LW>I0CaoqHDEcwLeCEXN}Y}cSmJpMU=x{70a)hwgP~Rc z9O>By@LE#G0UYJYL8}mRoeHqrQwC5nIt}1B^#9=8h7|e#mOBm6QjR)S5f=ge3Q7N- zyOgA42S6rO&<)`SmRPME`{t!}r2KfBPb2lNXrywOPm>T+J;D_WD z+yMMf$?{t6Er>eH4^W0TRq$g(e@>>oxeui>F-KMOTOeO2{Q_6+AIM-=s??sgq6`FO zm_z0-bV+;?gdz=b136 z?lQ0uqTI*kVlzb@doALw+zpg{C!%FTJ??uE(zHVhDf@eX{uAV($58e|fPP1)h_atB zoW-oJrvb5`LrYLys$e(RehAc1ckUiU9fcKZ6#NxP9{U|SH23eMCmWrZMVpL0*#Ckl zf3bGrA%OFj=s3H){6<&NBuq|cFuv54TZOpJiBtBHYsK!1fLug|3thQOQyJ_UP(@Kt z){tw4D|ZXx#ky99J_y{9y!_`8+zzyBm_I*v6;_eMUI3=hGa4%ZM1BR5hh3iX{1lDt z)DnQ~^71@?1<0~_*yXv^@#N0)9OLr5j>tPq#*svMJ^+aO49dfi1kn#g2c}fHZs3c> zKT1wORBM(y8qp!cAbytT2ktVH%@0{?CmZNTthG}Oga(L42Ixo32o12%%|p%qMF`)q!0y;(6|o2wkP9g85Wp9w4^8;`3;uMFv_x8!ZDw zrz<|6Ho64RN~XJjHfjd6n$SXaL|uTmx-DMBDb(iyae7s}m{X|R0CCAzyaZDyRZxgT zV}L52G=xD}@oDxVt9Sx6sswfsC8IZRySe-p9nc#Ty3YYbFE2)KP@-~|g6%}Yximzs z58#jxSlq?a-E?u@8*lkR7@!!_zQq-0-zUg_UY-i`3$ zR@7UCa*h5AfDnMv|8?O{>U1AarTa&}Whk$MkTd#S1O5YG-sq1EcmSY#G&^b$^G|>! zqjO*?0p9>vK6;1&-vl^ow8wyN0o>*qU25RB3FnL+W#EH^^G1(1@H>Rvqo)}7UBV@! zry2M?!sVl98u)#}vqsM|@CSsEVWUBO2*meYqd6Ffy8j3G{Lw82{0QJRIb%4?Y@&^F z0J~g8bo?#Al`7YmZHAUhQqGub4V*_fZw!YbNt;htjrk(r&6G8iu$FZPaHT_BW4>i* z!$`^*bB}>Ng!9JSXW&A@?lC_!@G*o-#ynu)BEsdKM*u&DiZ5M{J}S@iG{AD)pfBY% zlH2o|0lAG-=y?<1e;*4fw~=Imal!rswcUr;6y{U1Y7J&awkb6@TJP8AG zZ)(yhw*x$b+DB0~&-VcBB5x;%m7WI;FSn*@JRHG9(pG@eJ--FWy~a}RRL$_bY^YZN zoaK4lfS(39w}P8PBT>PnC%9-kT5{Y|t%#_K(Lf97#3boCl1j*_D(XRB#-t}@rmr&e zQJLsk$aoHHH&DpQ%-S{1nZa$qKS0_T7b{ZemTo%@sXR&2Ny(`yUIzUGCLNokTS;B$ z(uH(NlH^P(_u{})I^I=fa@F)#QFvAH;l)awa0%$ACh1!AspUY=AZenDT@@9sm;-n% zai_b!7`6(b#gRo-G=ugT($8>xB?JAm?W9dc`zR54?Af%D)c6}fdY0s=%*og;7bn>D zY;6L&z|uMg?d!-vP5cwm{18J_=``2ZS)X=NAy>Q$^5{!JKFgKUrZp~M1oyjvT}a9d z7u${q)QM|e?ZzSJ2T^d4*>l(j*yhj?nQbI zqUc2hj_h=+njK zi-^iG^>pkS++fGAw9$dq)1-=O;1`p2p^F`kBr_&8q~*Pmjc*6->r8fulU9tXSL1&O z{BKCR)Jc=N9sd&W_nZXU>J%Csd;GhgwUmRlrZ;WGTFja7D_!TLQDaqp4)A%UC9bja z5m93=z|}Q&Is6N|PX$*%G?x~o|8wj&jsf9}1!!7%Wiuuuqw8}3OR24pRiOuEv5=L0OC^jQO505ES-!hj0_p5wj&;2)?$ zE9M4gehH9kGSw?v0sIoK&i z?-`!QE=%VlRqe>9go#TW3@g(mh#1XKha+EQ7bh81&E=Z*4?rW(+m~&!N@>~P642Ed zV?c42UFSXp;WV^_vg*7lsdb~MwWCj$9fTPa~iht-@yMAMc+zefojC7X+H=04U!_=%n%e^iBbSnum_d? zwPK8ByU+R#g2EABD>?i48ZocuQB1&BAU@*Md=zWg8r=3Q95E_?#s^3#Q-Cu zz6a2)if)*$pQ#-2XkJk#;*&70jM$Yo>=N)5ju?_Z<0di>6}4OlMILwFM|eb!Ch$E) zmO{}(UU-SRpnL-*S=QN^C`5x z@gV&ouVNuWVcVAfZNpZ^@~EQM!FDUzewkPC7(x!tBf8S1Rpgy&k#?j@sJH;6Tksok zeSXf^R2|-e@HC!588Pd(^5-NsH zzrV3M%5zL%sQ=jX#H zNo$Cfs^|o`Sv$Mrop}{MHoW)eR~cUMR*QEucx%Y})4Ymz!8`Lz#Q!s++=|(t3H`y0 zvMP3f{!6BRIKAcQmiv1{|4BMsx6CnC5LzDwBru~Kf4OFKRIZs;(VZPBKps1)ubwJH>8)-YFJhL1*8epNcBpuRA&M$ zq`&k^)dA977|ln#ny$V!`EEo1dpccr)?QmhRkIOJ;sXc1IC! zFq{Q_a;l1#4AYQ|Opd~yT4!bKE=V2CN;qm+PP*MN4a>;n5cfZZb5ur7ZNN(~^ZF)! zBg*^KPF37%m`=$gNmUeJ-YG)HWulArs-gu<58yZAv_5mvGrfw(4CjQyb2=-nq~4M~ zvF|jFn)4d=(+|(CD*nT8p3yg_!|3lD_A}Gjr4Hqyz0!Bq|HImQz(-NM|Kq!N$q@pC zqa+l8B!m`f5TrwbbO{h3K#m$9bOFs${^QM6U&)C}fe(0kZ|GurzjIy8(7*vA8I~I%;f>@<%Fj|V=8sW&2Mqe6V~00& ziGey5@-Q}H?trUZA6y$3+aSw<5fcx17OA$6Q1by_Czb4~P$DqZkANq#?49go_YH2i zZHo(NF-ox;`F4bLzL}vdJ3rRah8P+H%-qEsdC0wb2N>kzc{`HNwLqNVwN-83+H%ZeEsdC}jjcS#*^T@PHXBA?Lc%B7jr4Mo%g*=JN04Q?MqAGQ_m=+WjN3ZR9D5qp z*odjq%*uVPozKc$S7*9hsn~^puaMf{Du+Q5 zjb+_eiTzwNn_ayAC!;P;;w!YkBNRyZagMRpJr|bVh+Pc$8&aqB4$_JK0NUaiFc%(4 z^w)I0V>CxPZ_3Fdu!qWB2F{Yiajr(#C~ev8xrTH)9=;HEnE87;MRuG5fc7` zJx!WsezK*BKMLj{)7;Iff)VR#gWHFY@Q1p}s#nDK1jt*S;XyfkRdU;oz4#;AC^$PK zBi>B1oVfgW>8@wlcJVF2uY-gavGZAdIxu9!?|^-dwn+2Df15^%ZNwKxqM=L}WlLzp zPXc_ERE!_R(a4Cut*LT0%3U2(6(hcHJ1c25JI|?%5q|@G+xBo>%a7-@!maoo;DoPZ z=av8W!BE!O>k2&c(199Nkyr;la_{5Oxy( z+ZDYJVYgBK_7>#UQLa=IxMb`);@056s9F#%w-RGdZNojC?JzHt<3VRRq$I@)fbA)a z*ugM-jmGzN-+r2FeFiRvPgNtM&fUE562390beLPM@d=;86%vk8e+^+>O7{l56%szx zA8sTArBUZKFu5bz<*fVsQ*t-H{KzP*x=)og6s2B8|G{l$fqJTC6$*1N!6l#(_9xQt zj(kL?oXV(ofYFO$R1B9*wP}=+Q#tSB=Ug1Q#V}?Day~)Bl{xPtCF)>WB?wD}kJm_b z&KdKwHd~dmB=Q%tH>99pvtV|-1kAeSjOiS07|kLPUX!zhHdFNp+Xu7Oq`KscIitse>|>gB&sn-^Nj0~QSnpr>cX)gy+(dsGghEMI8j65Xr*%HI>V}ZI>|K25 zkX*npXcw_11kv*OS(m*bQuZZM_67p&6UZzjn48UOgfW>k^0p9Vv_HUz&8u$sOCxU& zQO*zgITuH6F^p-1oTZU}2w|i8l^|>-3L=fXGbAP+tte(chOkk^Y=;yyY#o#|@^c|E zZ^KL)`KJ&zs=v4WFq1~!6%x}Gtte(chpH1hKyrGJ1KFJK~m@h|*Ku*jy7 z8*#0W!>Dr;O>IVOc9s7VfrtYd6$Z|mg%P`4F%5LOBd)oD=~hTOu6pCiFJh-FtT@u~hGfKUSIlJS?F_$|m7f&F zT@qIAMz{_)N?(M*uQZY=gjH5I%O_Sw%s(J`Yd7L$7n5a`+9=J>TPBja=T}cjYsk=c z4oSoKY?5CCjo9Wg>d2QyBW5@n3so8Zq~LR~Zqo}#X2iDO*ewW1vCjtci5D_-S7&+o zD9i{eiVJ0Fq}{>3=SmSfg2OU_OExbA#~vwYc{u;52RCm&7-4&1{s+t=_5{ZVM_5hp z4u?#Y8FN-S#x@5n4R9}*MW9Iol*fpj3F1pg#KYie{zObjAuIphE~^I4tSU$W)Cnc# zBW>94v4(PHiJ4OvUw?r_Om|sjfJdA&R7qlf)%2_^nh7nsT@kCG^IcX!15#hq^x`Z! zE47Rx+t5^D$zYf^mEx=Z#sQDTI}-CE(vWL&bB@8G@jtMN+XUqLn}d>5y!K! zS7Uz|yPTwY8QnQ_gqX(%`EpO-qM_%3TIS`@;$q$~$Oz3DPUi)KpH?l!Y%l@g^)DMj|jMUj%Ps&-88}yuuuA7pBD-@a}0+z3;o@M3p$%4eW8cQKXwt& zWCkUfZVVh%~b?$`~f}g9ODNsN}-xN!G;fa#2$ctpW(3%mXwx! z&MZGh>}4>&ksY5M%P&L8HRB|QwUW#|jJe2&MEfZ>g_j^IheXtpiHMs&4X@_&j+Ni6j>9#g1fO+N&~ zg=68%bu!-+GwjdS*pX@AG8|zgl5mr)55glc@V zIW$oYE140~5AZ@#{le%%sFo}%_CR?LR)G~gYc{Wr0B6rd-@|fL!�*(YM65huD@E z67 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�OVnFuvRnDVH$2))@QNGVWMK%H2#^ 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 literal 0 HcmV?d00001 diff --git a/benchmarks/hope/pisum_opt_d21ddf605a07e3a4d8a460735cef49c69a69c967957f695741c0feae_0.out b/benchmarks/hope/pisum_opt_d21ddf605a07e3a4d8a460735cef49c69a69c967957f695741c0feae_0.out new file mode 100644 index 0000000..46219fd --- /dev/null +++ b/benchmarks/hope/pisum_opt_d21ddf605a07e3a4d8a460735cef49c69a69c967957f695741c0feae_0.out @@ -0,0 +1,8 @@ +running build_ext +building 'pisum_opt_d21ddf605a07e3a4d8a460735cef49c69a69c967957f695741c0feae_0' extension +C compiler: /usr/bin/clang -fno-strict-aliasing -Wsign-compare -Wunreachable-code -fno-common -dynamic -DNDEBUG -g -fwrapv -O3 -Wall -arch i386 -arch x86_64 -g + +compile options: '-I/Users/uweschmitt/Projects/hope/venv3/lib/python3.5/site-packages/numpy/core/include -I/Users/uweschmitt/Projects/hope/venv3/include -I/Library/Frameworks/Python.framework/Versions/3.5/include/python3.5m -c' +extra options: '-Wall -Wno-unused-variable -march=native -stdlib=libc++ -std=c++11 -Wno-unreachable-code' +clang: /var/folders/k8/zfp7dvcs1m326gz1brql1tv80000gn/T/hopehpwfl_f0/pisum_opt_d21ddf605a07e3a4d8a460735cef49c69a69c967957f695741c0feae_0.cpp +/usr/bin/clang++ -bundle -undefined dynamic_lookup -arch i386 -arch x86_64 -g /var/folders/k8/zfp7dvcs1m326gz1brql1tv80000gn/T/hopehpwfl_f0/pisum_opt_d21ddf605a07e3a4d8a460735cef49c69a69c967957f695741c0feae_0.o -o /var/folders/k8/zfp7dvcs1m326gz1brql1tv80000gn/T/hopehpwfl_f0/pisum_opt_d21ddf605a07e3a4d8a460735cef49c69a69c967957f695741c0feae_0.cpython-35m-darwin.so diff --git a/benchmarks/hope/qsort_kernel_674d9af4cba0defec637089ed771c5f43e2e28f5e25a2c4f0ec12559.pck b/benchmarks/hope/qsort_kernel_674d9af4cba0defec637089ed771c5f43e2e28f5e25a2c4f0ec12559.pck new file mode 100644 index 0000000000000000000000000000000000000000..5041f546985a277a4a0f285b4c2e6efb79606125 GIT binary patch literal 504 zcmYjO$!@|h5T#`;g|5)`2ar&rBoG1z&iw>@sUpXokch;|!~vm7NIk+|*YSaJur$k? zH}8#q>j$ijNUv6_t)cYUejg@uE%Ay)Sq^5FmvJU!_p;Z;Ytm8pFit?w6tGUdREjby zN`C`|fW)79Q@)_#lm*n{5CWS`JZruH?s+zI!|4PZaOR;4j!PXj4J}}{A@)F%A` zD5pUJ$|>HS^sck%-Z%C~n$%d)B*8l}P@Kj}WX4u(C2(U$i@2J`d(y93DJ@vE6ts+Y zfDh!Zs>zZkaj;a6jmP+?bydZsN^KjT$nc+3sbNt?Xt0a}K7$st0mw^8a~vE=LxEEo u3mj_wmsiFYjjsMEjeh`NkEEs0c>!U($2UD%-4Wk)oheJe@#BD>Tk{XWw~<`{ literal 0 HcmV?d00001 diff --git a/benchmarks/hope/qsort_kernel_674d9af4cba0defec637089ed771c5f43e2e28f5e25a2c4f0ec12559_0.cpp b/benchmarks/hope/qsort_kernel_674d9af4cba0defec637089ed771c5f43e2e28f5e25a2c4f0ec12559_0.cpp new file mode 100644 index 0000000..f7656e3 --- /dev/null +++ b/benchmarks/hope/qsort_kernel_674d9af4cba0defec637089ed771c5f43e2e28f5e25a2c4f0ec12559_0.cpp @@ -0,0 +1,205 @@ + +#define PY_ARRAY_UNIQUE_SYMBOL fkt_ARRAY_API +#define NPY_NO_DEPRECATED_API NPY_1_7_API_VERSION +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + + +struct PyObj { + typedef PyObject * ptr_t; + typedef PyArrayObject * arrptr_t; + PyObj(): dec(false), ptr(NULL) {} + PyObj(ptr_t p): dec(false), ptr(p) {} + ~PyObj() { if(dec) Py_DECREF(ptr); } + PyObj & operator=(ptr_t p) { if(dec) Py_DECREF(ptr); ptr = p; dec = false; return *this; } + PyObj & incref(ptr_t p) { if(dec) Py_DECREF(ptr); ptr = p; dec = (p != NULL); return *this; } + operator bool() const { return ptr; } + operator ptr_t() const { return ptr; } + operator arrptr_t() const { return (arrptr_t)ptr; } + bool dec; + ptr_t ptr; +}; + +inline std::tuple qsort_kernel_d1JJ( + PyObject * pa, npy_intp const * __restrict__ sa, npy_double * __restrict__ ca + , npy_int64 clo + , npy_int64 chi +); +inline std::tuple qsort_kernel_d1JJ( + PyObject * pa, npy_intp const * __restrict__ sa, npy_double * __restrict__ ca + , npy_int64 clo + , npy_int64 chi +) { + npy_int64 ci = npy_int64(); + ci = clo; + npy_int64 cj = npy_int64(); + cj = chi; + if (false) { + return std::make_tuple((PyObject *)pa, sa, ca); + } + while ((npy_bool)(ci < chi)) { + npy_double cpivot = npy_double(); + cpivot = ca[(int)((npy_int64)((npy_int64)((npy_int64)clo + (npy_int64)chi) / (npy_int64)2))]; + while ((npy_bool)(ci <= cj)) { + while ((npy_bool)(ca[(int)(ci)] < cpivot)) { + ci += (npy_int64)1; + } + while ((npy_bool)(ca[(int)(cj)] > cpivot)) { + cj -= (npy_int64)1; + } + if ((npy_bool)(ci <= cj)) { + npy_double ctmp = npy_double(); + ctmp = ca[(int)(ci)]; + ca[(int)(ci)] = ca[(int)(cj)]; + ca[(int)(cj)] = ctmp; + ci += (npy_int64)1; + cj -= (npy_int64)1; + } + } + if ((npy_bool)(clo < cj)) { + qsort_kernel_d1JJ(pa, sa, ca, clo, cj); + } + clo = ci; + cj = chi; + } + return std::make_tuple((PyObject *)pa, sa, ca); + PyErr_SetString(PyExc_ValueError, "No return type passed!"); + throw std::exception(); +} + +#include +#include +#include +#include +#include +#include + +void sighandler(int sig); + +void sighandler(int sig) { + std::ostringstream buffer; + buffer << "Abort by " << (sig == SIGSEGV ? "segfault" : "bus error") << std::endl; + void * stack[64]; + std::size_t depth = backtrace(stack, 64); + if (!depth) + buffer << " " << std::endl; + else { + char ** symbols = backtrace_symbols(stack, depth); + for (std::size_t i = 1; i < depth; ++i) { + std::string symbol = symbols[i]; + if (symbol.find_first_of(' ', 59) != std::string::npos) { + std::string name = symbol.substr(59, symbol.find_first_of(' ', 59) - 59); + int status; + char * demangled = abi::__cxa_demangle(name.c_str(), NULL, NULL, &status); + if (!status) { + buffer << " " + << symbol.substr(0, 59) + << demangled + << symbol.substr(59 + name.size()) + << std::endl; + free(demangled); + } else + buffer << " " << symbol << std::endl; + } else + buffer << " " << symbol << std::endl; + } + free(symbols); + } + std::cerr << buffer.str(); + std::exit(EXIT_FAILURE); + } + + +extern "C" { + + PyObject * create_signature; + + struct sigaction slot; + + PyObject * set_create_signature(PyObject * self, PyObject * args) { + if (!PyArg_ParseTuple(args, "O", &create_signature)) { + PyErr_SetString(PyExc_ValueError, "Invalid Argument to set_create_signature!"); + return NULL; + } + Py_INCREF(create_signature); + memset(&slot, 0, sizeof(slot)); + slot.sa_handler = &sighandler; + sigaction(SIGSEGV, &slot, NULL); + sigaction(SIGBUS, &slot, NULL); + Py_INCREF(Py_None); + return Py_None; + } + + PyObject * run(PyObject * self, PyObject * args) { + { + PyObj pa; + PyObject * plo; npy_int64 clo; + PyObject * phi; npy_int64 chi; + if ( + PyTuple_CheckExact(args) and PyTuple_GET_SIZE(args) == 3 + and (pa = PyTuple_GET_ITEM(args, 0)) and PyArray_CheckExact(pa) + and PyArray_TYPE((PyArrayObject *)pa) == NPY_FLOAT64 and PyArray_NDIM((PyArrayObject *)pa) == 1 + and (plo = PyTuple_GET_ITEM(args, 1)) and PyLong_CheckExact(plo) + and (phi = PyTuple_GET_ITEM(args, 2)) and PyLong_CheckExact(phi) + ) { + if (!(pa.incref((PyObject *)PyArray_GETCONTIGUOUS((PyArrayObject *)pa)))) { + PyErr_SetString(PyExc_ValueError, "Invalid Argument type on a!"); + return NULL; + } + clo = PyLong_AS_LONG(plo); + chi = PyLong_AS_LONG(phi); + try { + PyObject * res = std::get<0>(qsort_kernel_d1JJ( + pa, PyArray_SHAPE((PyArrayObject *)pa), (npy_double *)PyArray_DATA((PyArrayObject *)pa) + , clo + , chi + )); + + Py_INCREF(res); + return res; + } catch (...) { + return NULL; + } + } else + PyErr_Clear(); + } + PyObject * signatures = Py_BuildValue("(sO)", "gANdcQBdcQEoY2hvcGUuX2FzdApWYXJpYWJsZQpxAimBcQN9cQQoWAQAAABuYW1lcQVYAQAAAGFx\nBlgFAAAAZHR5cGVxB1gHAAAAZmxvYXQ2NHEIWAQAAABkaW1zcQlLAXViaAIpgXEKfXELKGgFWAIA\nAABsb3EMaAdjYnVpbHRpbnMKaW50CnENaAlLAHViaAIpgXEOfXEPKGgFWAIAAABoaXEQaAdoDWgJ\nSwB1YmVhLg==\n", args); + if (!signatures) { + PyErr_SetString(PyExc_ValueError, "Error building signature string for qsort_kernel"); + return NULL; + } + return PyObject_Call(create_signature, signatures, NULL); + } + + PyMethodDef qsort_kernelMethods[] = { + { "set_create_signature", set_create_signature, METH_VARARGS, "signal handler" }, + { "run", run, METH_VARARGS, "module function" }, + { NULL, NULL, 0, NULL } + }; + + + static struct PyModuleDef qsort_kernelmodule = { + PyModuleDef_HEAD_INIT, + "qsort_kernel", + NULL, + -1, + qsort_kernelMethods + }; + + + PyMODINIT_FUNC PyInit_qsort_kernel_674d9af4cba0defec637089ed771c5f43e2e28f5e25a2c4f0ec12559_0(void) { + import_array(); + PyImport_ImportModule("numpy"); + return PyModule_Create(&qsort_kernelmodule); + } + +} diff --git a/benchmarks/hope/qsort_kernel_674d9af4cba0defec637089ed771c5f43e2e28f5e25a2c4f0ec12559_0.o b/benchmarks/hope/qsort_kernel_674d9af4cba0defec637089ed771c5f43e2e28f5e25a2c4f0ec12559_0.o new file mode 100644 index 0000000000000000000000000000000000000000..b60c6f64e3362ceb4048834f834d478461c514ae GIT binary patch literal 383220 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 A%K!iX literal 0 HcmV?d00001 diff --git a/benchmarks/hope/qsort_kernel_674d9af4cba0defec637089ed771c5f43e2e28f5e25a2c4f0ec12559_0.out b/benchmarks/hope/qsort_kernel_674d9af4cba0defec637089ed771c5f43e2e28f5e25a2c4f0ec12559_0.out new file mode 100644 index 0000000..168f8ec --- /dev/null +++ b/benchmarks/hope/qsort_kernel_674d9af4cba0defec637089ed771c5f43e2e28f5e25a2c4f0ec12559_0.out @@ -0,0 +1,8 @@ +running build_ext +building 'qsort_kernel_674d9af4cba0defec637089ed771c5f43e2e28f5e25a2c4f0ec12559_0' extension +C compiler: /usr/bin/clang -fno-strict-aliasing -Wsign-compare -Wunreachable-code -fno-common -dynamic -DNDEBUG -g -fwrapv -O3 -Wall -arch i386 -arch x86_64 -g + +compile options: '-I/Users/uweschmitt/Projects/hope/venv3/lib/python3.5/site-packages/numpy/core/include -I/Users/uweschmitt/Projects/hope/venv3/include -I/Library/Frameworks/Python.framework/Versions/3.5/include/python3.5m -c' +extra options: '-Wall -Wno-unused-variable -march=native -stdlib=libc++ -std=c++11 -Wno-unreachable-code' +clang: /var/folders/k8/zfp7dvcs1m326gz1brql1tv80000gn/T/hope6uqz3bwn/qsort_kernel_674d9af4cba0defec637089ed771c5f43e2e28f5e25a2c4f0ec12559_0.cpp +/usr/bin/clang++ -bundle -undefined dynamic_lookup -arch i386 -arch x86_64 -g /var/folders/k8/zfp7dvcs1m326gz1brql1tv80000gn/T/hope6uqz3bwn/qsort_kernel_674d9af4cba0defec637089ed771c5f43e2e28f5e25a2c4f0ec12559_0.o -o /var/folders/k8/zfp7dvcs1m326gz1brql1tv80000gn/T/hope6uqz3bwn/qsort_kernel_674d9af4cba0defec637089ed771c5f43e2e28f5e25a2c4f0ec12559_0.cpython-35m-darwin.so diff --git a/benchmarks/julialang.org.ipynb b/benchmarks/julialang.org.ipynb index 7459d05..04fef7c 100644 --- a/benchmarks/julialang.org.ipynb +++ b/benchmarks/julialang.org.ipynb @@ -1,813 +1,639 @@ { - "metadata": { - "name": "", - "signature": "sha256:2dc0f66f43617ca29e3f9e2e223da552ebd0f852686cd4d0a4f32aa41ec4f354" - }, - "nbformat": 3, - "nbformat_minor": 0, - "worksheets": [ + "cells": [ { - "cells": [ + "cell_type": "code", + "execution_count": 1, + "metadata": {}, + "outputs": [ { - "cell_type": "code", - "collapsed": false, - "input": [ - "import hope\n", - "hope.config.optimize = True\n", - "hope.config.verbose = True\n", - "hope.config.keeptemp = True\n", - "import numba\n", - "import numpy as np\n", - "from util import perf_comp_data\n", - "from native_util import load\n", - "%load_ext cythonmagic\n", - "%load_ext version_information\n", - "%version_information numpy, Cython, numba, hope" - ], - "language": "python", - "metadata": {}, - "outputs": [ - { - "html": [ - "
SoftwareVersion
Python2.7.8 64bit [GCC 4.2.1 Compatible Apple LLVM 5.1 (clang-503.0.40)]
IPython1.1.0
OSDarwin 13.4.0 x86_64 i386 64bit
numpy1.8.1
Cython0.20.2
numbatag: 0.13.3
hope0.3.0
Thu Dec 04 11:18:15 2014 CET
" - ], - "json": [ - "{\"Software versions\": [{\"version\": \"2.7.8 64bit [GCC 4.2.1 Compatible Apple LLVM 5.1 (clang-503.0.40)]\", \"module\": \"Python\"}, {\"version\": \"1.1.0\", \"module\": \"IPython\"}, {\"version\": \"Darwin 13.4.0 x86_64 i386 64bit\", \"module\": \"OS\"}, {\"version\": \"1.8.1\", \"module\": \"numpy\"}, {\"version\": \"0.20.2\", \"module\": \"Cython\"}, {\"version\": \"tag: 0.13.3\", \"module\": \"numba\"}, {\"version\": \"0.3.0\", \"module\": \"hope\"}]}" - ], - "latex": [ - "\\begin{tabular}{|l|l|}\\hline\n", - "{\\bf Software} & {\\bf Version} \\\\ \\hline\\hline\n", - "Python & 2.7.8 64bit [GCC 4.2.1 Compatible Apple LLVM 5.1 (clang-503.0.40)] \\\\ \\hline\n", - "IPython & 1.1.0 \\\\ \\hline\n", - "OS & Darwin 13.4.0 x86\\_64 i386 64bit \\\\ \\hline\n", - "numpy & 1.8.1 \\\\ \\hline\n", - "Cython & 0.20.2 \\\\ \\hline\n", - "numba & tag: 0.13.3 \\\\ \\hline\n", - "hope & 0.3.0 \\\\ \\hline\n", - "\\hline \\multicolumn{2}{|l|}{Thu Dec 04 11:18:15 2014 CET} \\\\ \\hline\n", - "\\end{tabular}\n" - ], - "metadata": {}, - "output_type": "pyout", - "prompt_number": 1, - "text": [ - "Software versions\n", - "Python 2.7.8 64bit [GCC 4.2.1 Compatible Apple LLVM 5.1 (clang-503.0.40)]\n", - "IPython 1.1.0\n", - "OS Darwin 13.4.0 x86_64 i386 64bit\n", - "numpy 1.8.1\n", - "Cython 0.20.2\n", - "numba tag: 0.13.3\n", - "hope 0.3.0\n", - "Thu Dec 04 11:18:15 2014 CET" - ] - } - ], - "prompt_number": 1 - }, - { - "cell_type": "heading", - "level": 1, - "metadata": {}, - "source": [ - "fibonacci" - ] - }, - { - "cell_type": "code", - "collapsed": false, - "input": [ - "def fib(n):\n", - " if n<2:\n", - " return n\n", - " return fib(n-1)+fib(n-2)\n", - "hope_fib = hope.jit(fib)\n", - "numba_fib = numba.jit(fib, nopython=False)\n", - "\n", - "native_fib_mod = load(\"fib\")\n", - "native_fib = native_fib_mod.run \n", - "\n", - "n=20\n", - "assert fib(20) == 6765\n", - "assert hope_fib(20) == 6765\n", - "assert numba_fib(20) == 6765\n", - "assert native_fib(20) == 6765" - ], - "language": "python", - "metadata": {}, - "outputs": [ - { - "output_type": "stream", - "stream": "stdout", - "text": [ - "running build_ext\n", - "building 'fib' extension\n", - "C compiler: /usr/bin/clang -fno-strict-aliasing -fno-common -dynamic -pipe -Os -fwrapv -DNDEBUG -g -fwrapv -O3 -Wall -Wstrict-prototypes\n", - "\n", - "compile options: '-I/Users/jakeret/workspace/virtualenvs/hope_benchmarks/lib/python2.7/site-packages/numpy/core/include -I/opt/local/Library/Frameworks/Python.framework/Versions/2.7/include/python2.7 -c'\n", - "extra options: '-Wall -Wno-unused-variable -march=native -stdlib=libc++ -std=c++11'\n", - "clang: ././src/fib.cpp\n", - "/usr/bin/clang++ -bundle -undefined dynamic_lookup -L/opt/local/lib -Wl,-headerpad_max_install_names -L/opt/local/lib/db48 ./src/fib.o -o ./fib.so\n", - "\n", - "fib(int64 n)\n", - "\tif (n.J < 2.J) {\n", - "\t\treturn n.J\n", - "\t}\n", - "\treturn (fib((n.J - 1.J)) + fib((n.J - 2.J)))\n", - "\n", - "Compiling following functions:" - ] - }, - { - "output_type": "stream", - "stream": "stdout", - "text": [ - "\n", - "fib(int64 n)\n" + "data": { + "application/json": { + "Software versions": [ + { + "module": "Python", + "version": "3.5.1 64bit [GCC 4.2.1 (Apple Inc. build 5666) (dot 3)]" + }, + { + "module": "IPython", + "version": "6.1.0" + }, + { + "module": "OS", + "version": "Darwin 15.6.0 x86_64 i386 64bit" + }, + { + "module": "numpy", + "version": "1.13.1" + }, + { + "module": "Cython", + "version": "0.26" + }, + { + "module": "numba", + "version": "0.34.0" + }, + { + "module": "hope", + "version": "0.6.1" + } ] }, - { - "output_type": "stream", - "stream": "stdout", - "text": [ - "running build_ext\n", - "building 'fib_181039868ed022600dc3388e38604df736dff187257d5d6ae4c4d3ea_0' extension\n", - "C compiler: /usr/bin/clang -fno-strict-aliasing -fno-common -dynamic -pipe -Os -fwrapv -DNDEBUG -g -fwrapv -O3 -Wall -Wstrict-prototypes\n", - "\n", - "compile options: '-I/Users/jakeret/workspace/virtualenvs/hope_benchmarks/lib/python2.7/site-packages/numpy/core/include -I/opt/local/Library/Frameworks/Python.framework/Versions/2.7/include/python2.7 -c'\n", - "extra options: '-Wall -Wno-unused-variable -march=native -stdlib=libc++ -std=c++11'\n", - "clang: /var/folders/k_/ttz9cd4d3kj2kbh3lvtn6tkm0000gn/T/hopeDXfz9_/fib_181039868ed022600dc3388e38604df736dff187257d5d6ae4c4d3ea_0.cpp\n", - "/usr/bin/clang++ -bundle -undefined dynamic_lookup -L/opt/local/lib -Wl,-headerpad_max_install_names -L/opt/local/lib/db48 /var/folders/k_/ttz9cd4d3kj2kbh3lvtn6tkm0000gn/T/hopeDXfz9_/fib_181039868ed022600dc3388e38604df736dff187257d5d6ae4c4d3ea_0.o -o /var/folders/k_/ttz9cd4d3kj2kbh3lvtn6tkm0000gn/T/hopeDXfz9_/fib_181039868ed022600dc3388e38604df736dff187257d5d6ae4c4d3ea_0.so\n", - "\n" - ] - } - ], - "prompt_number": 2 - }, - { - "cell_type": "code", - "collapsed": false, - "input": [ - "%%cython\n", - "\n", - "cimport cython\n", - "\n", - "@cython.boundscheck(False)\n", - "@cython.wraparound(False)\n", - "cpdef int cython_fib(int n):\n", - " if n<2:\n", - " return n\n", - " return cython_fib(n-1)+cython_fib(n-2)\n", - "\n", - "assert cython_fib(20) == 6765\n" - ], - "language": "python", - "metadata": {}, - "outputs": [ - { - "output_type": "stream", - "stream": "stdout", - "text": [ - "building '_cython_magic_d4ac66e2963d3320a31dcc65e2809bf2' extension\n" - ] - }, - { - "output_type": "stream", - "stream": "stdout", - "text": [ - "C compiler: /usr/bin/clang -fno-strict-aliasing -fno-common -dynamic -pipe -Os -fwrapv -DNDEBUG -g -fwrapv -O3 -Wall -Wstrict-prototypes\n", - "\n" - ] - }, - { - "output_type": "stream", - "stream": "stdout", - "text": [ - "compile options: '-I/opt/local/Library/Frameworks/Python.framework/Versions/2.7/include/python2.7 -c'\n" - ] - }, - { - "output_type": "stream", - "stream": "stdout", - "text": [ - "clang: /Users/jakeret/.ipython/cython/_cython_magic_d4ac66e2963d3320a31dcc65e2809bf2.c\n" - ] - }, - { - "output_type": "stream", - "stream": "stdout", - "text": [ - "/usr/bin/clang -bundle -undefined dynamic_lookup -L/opt/local/lib -Wl,-headerpad_max_install_names -L/opt/local/lib/db48 /Users/jakeret/.ipython/cython/Users/jakeret/.ipython/cython/_cython_magic_d4ac66e2963d3320a31dcc65e2809bf2.o -o /Users/jakeret/.ipython/cython/_cython_magic_d4ac66e2963d3320a31dcc65e2809bf2.so\n" - ] - } - ], - "prompt_number": 3 - }, - { - "cell_type": "code", - "collapsed": false, - "input": [ - "%timeit fib(20)\n", - "%timeit hope_fib(20)\n", - "%timeit numba_fib(20)\n", - "%timeit cython_fib(20)\n", - "%timeit native_fib(20)" - ], - "language": "python", - "metadata": {}, - "outputs": [ - { - "output_type": "stream", - "stream": "stdout", - "text": [ - "100 loops, best of 3: 2.59 ms per loop\n", - "10000 loops, best of 3: 40.6 \u00b5s per loop" - ] - }, - { - "output_type": "stream", - "stream": "stdout", - "text": [ - "\n", - "100 loops, best of 3: 2.55 ms per loop" - ] - }, - { - "output_type": "stream", - "stream": "stdout", - "text": [ - "\n", - "10000 loops, best of 3: 42.5 \u00b5s per loop" - ] - }, - { - "output_type": "stream", - "stream": "stdout", - "text": [ - "\n", - "10000 loops, best of 3: 41.4 \u00b5s per loop" - ] - }, - { - "output_type": "stream", - "stream": "stdout", - "text": [ - "\n" - ] - } - ], - "prompt_number": 4 - }, - { - "cell_type": "code", - "collapsed": false, - "input": [ - "perf_comp_data([\"fib\", \"hope_fib\", \"numba_fib\", \"cython_fib\", \"native_fib\"],\n", - " 5*[\"n\"])" - ], - "language": "python", + "text/html": [ + "
SoftwareVersion
Python3.5.1 64bit [GCC 4.2.1 (Apple Inc. build 5666) (dot 3)]
IPython6.1.0
OSDarwin 15.6.0 x86_64 i386 64bit
numpy1.13.1
Cython0.26
numba0.34.0
hope0.6.1
Tue Aug 29 17:15:40 2017 CEST
" + ], + "text/latex": [ + "\\begin{tabular}{|l|l|}\\hline\n", + "{\\bf Software} & {\\bf Version} \\\\ \\hline\\hline\n", + "Python & 3.5.1 64bit [GCC 4.2.1 (Apple Inc. build 5666) (dot 3)] \\\\ \\hline\n", + "IPython & 6.1.0 \\\\ \\hline\n", + "OS & Darwin 15.6.0 x86\\_64 i386 64bit \\\\ \\hline\n", + "numpy & 1.13.1 \\\\ \\hline\n", + "Cython & 0.26 \\\\ \\hline\n", + "numba & 0.34.0 \\\\ \\hline\n", + "hope & 0.6.1 \\\\ \\hline\n", + "\\hline \\multicolumn{2}{|l|}{Tue Aug 29 17:15:40 2017 CEST} \\\\ \\hline\n", + "\\end{tabular}\n" + ], + "text/plain": [ + "Software versions\n", + "Python 3.5.1 64bit [GCC 4.2.1 (Apple Inc. build 5666) (dot 3)]\n", + "IPython 6.1.0\n", + "OS Darwin 15.6.0 x86_64 i386 64bit\n", + "numpy 1.13.1\n", + "Cython 0.26\n", + "numba 0.34.0\n", + "hope 0.6.1\n", + "Tue Aug 29 17:15:40 2017 CEST" + ] + }, + "execution_count": 1, "metadata": {}, - "outputs": [ - { - "output_type": "stream", - "stream": "stdout", - "text": [ - "function: native_fib , av. time sec: 0.00003982, min. time sec: 0.00003886, relative: 1.0\n", - "function: hope_fib , av. time sec: 0.00004315, min. time sec: 0.00003815, relative: 1.1\n", - "function: cython_fib , av. time sec: 0.00004911, min. time sec: 0.00004101, relative: 1.2\n", - "function: numba_fib , av. time sec: 0.00260401, min. time sec: 0.00255513, relative: 65.4\n", - "function: fib , av. time sec: 0.00291204, min. time sec: 0.00247192, relative: 73.1\n" - ] - } - ], - "prompt_number": 7 - }, + "output_type": "execute_result" + } + ], + "source": [ + "import hope\n", + "hope.config.optimize = True\n", + "hope.config.verbose = True\n", + "hope.config.keeptemp = True\n", + "hope.config.prefix = \"hope\"\n", + "import numba\n", + "import numpy as np\n", + "from util import perf_comp_data\n", + "from native_util import load\n", + "%load_ext Cython\n", + "%load_ext version_information\n", + "%version_information numpy, Cython, numba, hope" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# fibonacci" + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "metadata": {}, + "outputs": [ { - "cell_type": "heading", - "level": 1, - "metadata": {}, - "source": [ - "quicksort" + "name": "stdout", + "output_type": "stream", + "text": [ + "running build_ext\n", + "\n" ] - }, + } + ], + "source": [ + "def fib(n):\n", + " if n<2:\n", + " return n\n", + " return fib(n-1)+fib(n-2)\n", + "hope_fib = hope.jit(fib)\n", + "numba_fib = numba.jit(fib, nopython=False)\n", + "\n", + "native_fib_mod = load(\"fib\")\n", + "native_fib = native_fib_mod.run \n", + "\n", + "n=20\n", + "assert fib(20) == 6765\n", + "assert hope_fib(20) == 6765\n", + "assert numba_fib(20) == 6765\n", + "assert native_fib(20) == 6765" + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "metadata": {}, + "outputs": [], + "source": [ + "%%cython\n", + "\n", + "cimport cython\n", + "\n", + "@cython.boundscheck(False)\n", + "@cython.wraparound(False)\n", + "cpdef int cython_fib(int n):\n", + " if n<2:\n", + " return n\n", + " return cython_fib(n-1)+cython_fib(n-2)\n", + "\n", + "assert cython_fib(20) == 6765\n" + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "metadata": {}, + "outputs": [ { - "cell_type": "code", - "collapsed": false, - "input": [ - "def qsort_kernel(a, lo, hi):\n", - " i = lo\n", - " j = hi\n", - " if False: return a\n", - " while i < hi:\n", - " pivot = a[(lo+hi) // 2]\n", - " while i <= j:\n", - " while a[i] < pivot:\n", - " i += 1\n", - " while a[j] > pivot:\n", - " j -= 1\n", - " if i <= j:\n", - " tmp = a[i]\n", - " a[i] = a[j]\n", - " a[j] = tmp\n", - " i += 1\n", - " j -= 1\n", - " if lo < j:\n", - " qsort_kernel(a, lo, j)\n", - " lo = i\n", - " j = hi\n", - " return a\n", - "\n", - "hope_qsort_kernel = hope.jit(qsort_kernel)\n", - "numba_qsort_kernel = numba.jit(qsort_kernel)\n", - "\n", - "native_qsort_kernel_mod = load(\"qsort_kernel\")\n", - "native_qsort_kernel = native_qsort_kernel_mod.run\n" - ], - "language": "python", - "metadata": {}, - "outputs": [ - { - "output_type": "stream", - "stream": "stdout", - "text": [ - "running build_ext\n", - "building 'qsort_kernel' extension\n", - "C compiler: /usr/bin/clang -fno-strict-aliasing -fno-common -dynamic -pipe -Os -fwrapv -DNDEBUG -g -fwrapv -O3 -Wall -Wstrict-prototypes\n", - "\n", - "compile options: '-I/Users/jakeret/Library/Python/2.7/lib/python/site-packages/numpy/core/include -I/opt/local/Library/Frameworks/Python.framework/Versions/2.7/include/python2.7 -c'\n", - "extra options: '-Wall -Wno-unused-variable -march=native -stdlib=libc++ -std=c++11'\n", - "clang: ././src/qsort_kernel.cpp\n", - "/usr/bin/clang++ -bundle -undefined dynamic_lookup -L/opt/local/lib -Wl,-headerpad_max_install_names -L/opt/local/lib/db46 ./src/qsort_kernel.o -o ./qsort_kernel.so\n", - "\n" - ] - } - ], - "prompt_number": 8 - }, + "name": "stdout", + "output_type": "stream", + "text": [ + "3.11 ms ± 32.5 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)\n", + "39.5 µs ± 707 ns per loop (mean ± std. dev. of 7 runs, 10000 loops each)\n", + "3.07 ms ± 30.5 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)\n", + "39.5 µs ± 571 ns per loop (mean ± std. dev. of 7 runs, 10000 loops each)\n", + "39.6 µs ± 522 ns per loop (mean ± std. dev. of 7 runs, 10000 loops each)\n" + ] + } + ], + "source": [ + "%timeit fib(20)\n", + "%timeit hope_fib(20)\n", + "%timeit numba_fib(20)\n", + "%timeit cython_fib(20)\n", + "%timeit native_fib(20)" + ] + }, + { + "cell_type": "code", + "execution_count": 5, + "metadata": {}, + "outputs": [ { - "cell_type": "code", - "collapsed": false, - "input": [ - "def numpy_qsort_kernel(a, lo, hi):\n", - " np.sort(a)" - ], - "language": "python", - "metadata": {}, - "outputs": [], - "prompt_number": 16 - }, + "name": "stdout", + "output_type": "stream", + "text": [ + "function: native_fib , av. time sec: 0.00003945, min. time sec: 0.00003941, relative: 1.0\n", + "function: cython_fib , av. time sec: 0.00003969, min. time sec: 0.00003967, relative: 1.0\n", + "function: hope_fib , av. time sec: 0.00004080, min. time sec: 0.00004075, relative: 1.0\n", + "function: numba_fib , av. time sec: 0.00341285, min. time sec: 0.00320110, relative: 86.5\n", + "function: fib , av. time sec: 0.00344686, min. time sec: 0.00311820, relative: 87.4\n" + ] + } + ], + "source": [ + "perf_comp_data([\"fib\", \"hope_fib\", \"numba_fib\", \"cython_fib\", \"native_fib\"],\n", + " 5*[\"n\"])" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# quicksort" + ] + }, + { + "cell_type": "code", + "execution_count": 6, + "metadata": {}, + "outputs": [ { - "cell_type": "code", - "collapsed": false, - "input": [ - "%%cython\n", + "name": "stdout", + "output_type": "stream", + "text": [ + "running build_ext\n", + "building 'qsort_kernel' extension\n", + "C compiler: /usr/bin/clang -fno-strict-aliasing -Wsign-compare -Wunreachable-code -fno-common -dynamic -DNDEBUG -g -fwrapv -O3 -Wall -Wstrict-prototypes -arch i386 -arch x86_64 -g\n", "\n", - "cimport cython\n", - "import numpy as np\n", - "cimport numpy as np\n", - "\n", - "@cython.boundscheck(False)\n", - "@cython.wraparound(False)\n", - "def cython_qsort_kernel(np.ndarray[np.double_t, ndim=1] a, int lo, int hi):\n", - " cdef int i = lo\n", - " cdef int j = hi\n", - " cdef double pivot = 0\n", - " cdef double tmp = 0.0\n", - " if False: return a\n", - " while i < hi:\n", - " pivot = a[(lo+hi) // 2]\n", - " while i <= j:\n", - " while a[i] < pivot:\n", - " i += 1\n", - " while a[j] > pivot:\n", - " j -= 1\n", - " if i <= j:\n", - " tmp = a[i]\n", - " a[i] = a[j]\n", - " a[j] = tmp\n", - " i += 1\n", - " j -= 1\n", - " if lo < j:\n", - " cython_qsort_kernel(a, lo, j)\n", - " lo = i\n", - " j = hi\n", - " return a\n", + "compile options: '-I/Users/uweschmitt/Projects/hope/venv3/lib/python3.5/site-packages/numpy/core/include -I/Users/uweschmitt/Projects/hope/venv3/include -I/Library/Frameworks/Python.framework/Versions/3.5/include/python3.5m -c'\n", + "extra options: '-Wall -Wno-unused-variable -march=native -stdlib=libc++ -std=c++11 -Wno-unreachable-code'\n", "\n" - ], - "language": "python", - "metadata": {}, - "outputs": [], - "prompt_number": 17 - }, - { - "cell_type": "code", - "collapsed": false, - "input": [ - "lst = np.random.random(5000)\n" - ], - "language": "python", - "metadata": {}, - "outputs": [], - "prompt_number": 18 - }, - { - "cell_type": "code", - "collapsed": false, - "input": [ - "psorted = qsort_kernel(lst.copy(), 0, len(lst)-1)\n", - "hsorted = hope_qsort_kernel(lst.copy(), 0, len(lst)-1)\n", - "#nsorted = numba_qsort_kernel(lst.copy(), 0, len(lst)-1)\n", - "csorted = cython_qsort_kernel(lst.copy(), 0, len(lst)-1)\n", - "nasorted = native_qsort_kernel(lst.copy(), 0, len(lst)-1)\n" - ], - "language": "python", - "metadata": {}, - "outputs": [], - "prompt_number": 19 - }, - { - "cell_type": "code", - "collapsed": false, - "input": [ - "assert np.all(psorted[:-1] <= psorted[1:])\n", - "#assert np.all(hope_qsort_kernel[:-1] <= hope_qsort_kernel[1:])\n", - "#assert np.all(numba_qsort_kernel[:-1] <= numba_qsort_kernel[1:])\n", - "#assert np.all(cython_qsort_kernel[:-1] <= cython_qsort_kernel[1:])\n", - "\n", - "%timeit qsort_kernel(lst.copy(), 0, len(lst)-1)\n", - "%timeit hope_qsort_kernel(lst.copy(), 0, len(lst)-1)\n", - "#%timeit numba_qsort_kernel(lst.copy(), 0, len(lst)-1)\n", - "%timeit cython_qsort_kernel(lst.copy(), 0, len(lst)-1)\n", - "%timeit native_qsort_kernel(lst.copy(), 0, len(lst)-1)\n", - "%timeit np.sort(lst.copy())" - ], - "language": "python", - "metadata": {}, - "outputs": [ - { - "output_type": "stream", - "stream": "stdout", - "text": [ - "10 loops, best of 3: 22.4 ms per loop\n", - "1000 loops, best of 3: 334 \u00b5s per loop" - ] - }, - { - "output_type": "stream", - "stream": "stdout", - "text": [ - "\n", - "1000 loops, best of 3: 1.4 ms per loop" - ] - }, - { - "output_type": "stream", - "stream": "stdout", - "text": [ - "\n", - "1000 loops, best of 3: 303 \u00b5s per loop" - ] - }, - { - "output_type": "stream", - "stream": "stdout", - "text": [ - "\n", - "1000 loops, best of 3: 248 \u00b5s per loop" - ] - }, - { - "output_type": "stream", - "stream": "stdout", - "text": [ - "\n" - ] - } - ], - "prompt_number": 20 - }, + ] + } + ], + "source": [ + "def qsort_kernel(a, lo, hi):\n", + " i = lo\n", + " j = hi\n", + " if False: return a\n", + " while i < hi:\n", + " pivot = a[(lo+hi) // 2]\n", + " while i <= j:\n", + " while a[i] < pivot:\n", + " i += 1\n", + " while a[j] > pivot:\n", + " j -= 1\n", + " if i <= j:\n", + " tmp = a[i]\n", + " a[i] = a[j]\n", + " a[j] = tmp\n", + " i += 1\n", + " j -= 1\n", + " if lo < j:\n", + " qsort_kernel(a, lo, j)\n", + " lo = i\n", + " j = hi\n", + " return a\n", + "\n", + "hope_qsort_kernel = hope.jit(qsort_kernel)\n", + "numba_qsort_kernel = numba.jit(qsort_kernel)\n", + "\n", + "native_qsort_kernel_mod = load(\"qsort_kernel\")\n", + "native_qsort_kernel = native_qsort_kernel_mod.run\n" + ] + }, + { + "cell_type": "code", + "execution_count": 7, + "metadata": { + "collapsed": true + }, + "outputs": [], + "source": [ + "def numpy_qsort_kernel(a, lo, hi):\n", + " np.sort(a)" + ] + }, + { + "cell_type": "code", + "execution_count": 8, + "metadata": {}, + "outputs": [ { - "cell_type": "code", - "collapsed": false, - "input": [ - "a = lst.copy()\n", - "\n", - "lo = 0\n", - "hi = len(lst)-1\n", + "name": "stdout", + "output_type": "stream", + "text": [ + "building '_cython_magic_c4e853c051d00b69b625b262d253da82' extension\n", + "C compiler: /usr/bin/clang -fno-strict-aliasing -Wsign-compare -Wunreachable-code -fno-common -dynamic -DNDEBUG -g -fwrapv -O3 -Wall -Wstrict-prototypes -arch i386 -arch x86_64 -g\n", "\n", - "perf_comp_data([\"hope_qsort_kernel\", \n", - " \"qsort_kernel\", \n", - " #\"numpy_qsort_kernel\", \n", - " \"cython_qsort_kernel\", \n", - " \"native_qsort_kernel\"],\n", - " 5*[\"a, lo, hi\"], rep=100, extra_setup=\"from __main__ import lst;a = lst.copy()\")" - ], - "language": "python", - "metadata": {}, - "outputs": [ - { - "output_type": "stream", - "stream": "stdout", - "text": [ - "function: native_qsort_kernel , av. time sec: 0.00029302, min. time sec: 0.00029182, relative: 1.0\n", - "function: hope_qsort_kernel , av. time sec: 0.00033617, min. time sec: 0.00033379, relative: 1.1\n", - "function: cython_qsort_kernel , av. time sec: 0.00143504, min. time sec: 0.00140619, relative: 4.9\n", - "function: qsort_kernel , av. time sec: 0.02197444, min. time sec: 0.02172208, relative: 75.0\n" - ] - } - ], - "prompt_number": 21 - }, - { - "cell_type": "heading", - "level": 1, - "metadata": {}, - "source": [ - "pi sum" + "compile options: '-I/Users/uweschmitt/Projects/hope/venv3/lib/python3.5/site-packages/numpy/core/include -I/Users/uweschmitt/Projects/hope/venv3/include -I/Library/Frameworks/Python.framework/Versions/3.5/include/python3.5m -c'\n", + "clang: /Users/uweschmitt/.ipython/cython/_cython_magic_c4e853c051d00b69b625b262d253da82.c\n", + "In file included from /Users/uweschmitt/.ipython/cython/_cython_magic_c4e853c051d00b69b625b262d253da82.c:495:\n", + "In file included from /Users/uweschmitt/Projects/hope/venv3/lib/python3.5/site-packages/numpy/core/include/numpy/arrayobject.h:4:\n", + "In file included from /Users/uweschmitt/Projects/hope/venv3/lib/python3.5/site-packages/numpy/core/include/numpy/ndarrayobject.h:18:\n", + "In file included from /Users/uweschmitt/Projects/hope/venv3/lib/python3.5/site-packages/numpy/core/include/numpy/ndarraytypes.h:1809:\n", + "/Users/uweschmitt/Projects/hope/venv3/lib/python3.5/site-packages/numpy/core/include/numpy/npy_1_7_deprecated_api.h:15:2: warning: \"Using deprecated NumPy API, disable it by \" \"#defining NPY_NO_DEPRECATED_API NPY_1_7_API_VERSION\" [-W#warnings]\n", + "#warning \"Using deprecated NumPy API, disable it by \" \\\n", + " ^\n", + "1 warning generated.\n", + "In file included from /Users/uweschmitt/.ipython/cython/_cython_magic_c4e853c051d00b69b625b262d253da82.c:495:\n", + "In file included from /Users/uweschmitt/Projects/hope/venv3/lib/python3.5/site-packages/numpy/core/include/numpy/arrayobject.h:4:\n", + "In file included from /Users/uweschmitt/Projects/hope/venv3/lib/python3.5/site-packages/numpy/core/include/numpy/ndarrayobject.h:18:\n", + "In file included from /Users/uweschmitt/Projects/hope/venv3/lib/python3.5/site-packages/numpy/core/include/numpy/ndarraytypes.h:1809:\n", + "/Users/uweschmitt/Projects/hope/venv3/lib/python3.5/site-packages/numpy/core/include/numpy/npy_1_7_deprecated_api.h:15:2: warning: \"Using deprecated NumPy API, disable it by \" \"#defining NPY_NO_DEPRECATED_API NPY_1_7_API_VERSION\" [-W#warnings]\n", + "#warning \"Using deprecated NumPy API, disable it by \" \\\n", + " ^\n", + "1 warning generated.\n", + "/usr/bin/clang -bundle -undefined dynamic_lookup -arch i386 -arch x86_64 -g /Users/uweschmitt/.ipython/cython/Users/uweschmitt/.ipython/cython/_cython_magic_c4e853c051d00b69b625b262d253da82.o -o /Users/uweschmitt/.ipython/cython/_cython_magic_c4e853c051d00b69b625b262d253da82.cpython-35m-darwin.so\n" ] - }, + } + ], + "source": [ + "%%cython\n", + "\n", + "cimport cython\n", + "import numpy as np\n", + "cimport numpy as np\n", + "\n", + "@cython.boundscheck(False)\n", + "@cython.wraparound(False)\n", + "cdef _cython_qsort_kernel(np.double_t * a, int lo, int hi):\n", + " cdef int i = lo\n", + " cdef int j = hi\n", + " cdef double pivot = 0\n", + " cdef double tmp = 0.0\n", + " if False: return a\n", + " while i < hi:\n", + " pivot = a[(lo+hi) // 2]\n", + " while i <= j:\n", + " while a[i] < pivot:\n", + " i += 1\n", + " while a[j] > pivot:\n", + " j -= 1\n", + " if i <= j:\n", + " tmp = a[i]\n", + " a[i] = a[j]\n", + " a[j] = tmp\n", + " i += 1\n", + " j -= 1\n", + " if lo < j:\n", + " _cython_qsort_kernel(a, lo, j)\n", + " lo = i\n", + " j = hi\n", + "\n", + "def cython_qsort_kernel(np.ndarray[np.double_t, ndim=1] a, int lo, int hi):\n", + " _cython_qsort_kernel( a.data, lo, hi)\n", + " return a" + ] + }, + { + "cell_type": "code", + "execution_count": 9, + "metadata": { + "collapsed": true + }, + "outputs": [], + "source": [ + "lst = np.random.random(5000)\n" + ] + }, + { + "cell_type": "code", + "execution_count": 10, + "metadata": { + "scrolled": false + }, + "outputs": [], + "source": [ + "psorted = qsort_kernel(lst.copy(), 0, len(lst)-1)\n", + "hsorted = hope_qsort_kernel(lst.copy(), 0, len(lst)-1)\n", + "#nsorted = numba_qsort_kernel(lst.copy(), 0, len(lst)-1)\n", + "csorted = cython_qsort_kernel(lst.copy(), 0, len(lst)-1)\n", + "nasorted = native_qsort_kernel(lst.copy(), 0, len(lst)-1)\n" + ] + }, + { + "cell_type": "code", + "execution_count": 11, + "metadata": {}, + "outputs": [ { - "cell_type": "code", - "collapsed": false, - "input": [ - "def pisum():\n", - " for j in range(1, 501):\n", - " sum = 0.0\n", - " f = 0.0\n", - " for k in range(1, 10001):\n", - " sum += 1.0/(k*k)\n", - " return sum\n", - "def pisum_opt():\n", - " for j in range(1, 501):\n", - " sum = 0.0\n", - " f = 0.0\n", - " for k in range(1, 10001):\n", - " f += 1.\n", - " sum += 1.0/(f*f)\n", - " return sum\n", - "\n", - "hope_pisum = hope.jit(pisum)\n", - "hope_pisum_opt = hope.jit(pisum_opt)\n", - "\n", - "numba_pisum = numba.jit(pisum, nopython=True)\n", - "numba_pisum_opt = numba.jit(pisum_opt, nopython=True)\n", - "\n", - "native_pisum_mod = load(\"pisum\")\n", - "native_pisum = native_pisum_mod.run\n", - "\n", - "native_pisum_opt_mod = load(\"pisum_opt\")\n", - "native_pisum_opt = native_pisum_opt_mod.run\n", - "\n", - "\n", - "assert abs(pisum()-1.644834071848065) < 1e-6\n", - "assert abs(hope_pisum()-1.644834071848065) < 1e-6\n", - "assert abs(hope_pisum_opt()-1.644834071848065) < 1e-6\n", - "assert abs(numba_pisum()-1.644834071848065) < 1e-6\n", - "assert abs(native_pisum()-1.644834071848065) < 1e-6\n", - "assert abs(native_pisum_opt()-1.644834071848065) < 1e-6\n" - ], - "language": "python", - "metadata": {}, - "outputs": [ - { - "output_type": "stream", - "stream": "stdout", - "text": [ - "running build_ext\n", - "building 'pisum' extension\n", - "C compiler: /usr/bin/clang -fno-strict-aliasing -fno-common -dynamic -pipe -Os -fwrapv -DNDEBUG -g -fwrapv -O3 -Wall -Wstrict-prototypes\n", - "\n", - "compile options: '-I/Users/jakeret/Library/Python/2.7/lib/python/site-packages/numpy/core/include -I/opt/local/Library/Frameworks/Python.framework/Versions/2.7/include/python2.7 -c'\n", - "extra options: '-Wall -Wno-unused-variable -march=native -stdlib=libc++ -std=c++11'\n", - "clang: ././src/pisum.cpp\n", - "/usr/bin/clang++ -bundle -undefined dynamic_lookup -L/opt/local/lib -Wl,-headerpad_max_install_names -L/opt/local/lib/db46 ./src/pisum.o -o ./pisum.so\n", - "\n" - ] - }, - { - "output_type": "stream", - "stream": "stdout", - "text": [ - "running build_ext\n", - "building 'pisum_opt' extension\n", - "C compiler: /usr/bin/clang -fno-strict-aliasing -fno-common -dynamic -pipe -Os -fwrapv -DNDEBUG -g -fwrapv -O3 -Wall -Wstrict-prototypes\n", - "\n", - "compile options: '-I/Users/jakeret/Library/Python/2.7/lib/python/site-packages/numpy/core/include -I/opt/local/Library/Frameworks/Python.framework/Versions/2.7/include/python2.7 -c'\n", - "extra options: '-Wall -Wno-unused-variable -march=native -stdlib=libc++ -std=c++11'\n", - "clang: ././src/pisum_opt.cpp\n", - "/usr/bin/clang++ -bundle -undefined dynamic_lookup -L/opt/local/lib -Wl,-headerpad_max_install_names -L/opt/local/lib/db46 ./src/pisum_opt.o -o ./pisum_opt.so\n", - "\n" - ] - } - ], - "prompt_number": 23 - }, + "name": "stdout", + "output_type": "stream", + "text": [ + "22.9 ms ± 785 µs per loop (mean ± std. dev. of 7 runs, 10 loops each)\n", + "349 µs ± 3.85 µs per loop (mean ± std. dev. of 7 runs, 1000 loops each)\n", + "332 µs ± 8.69 µs per loop (mean ± std. dev. of 7 runs, 1000 loops each)\n", + "313 µs ± 8.87 µs per loop (mean ± std. dev. of 7 runs, 1000 loops each)\n", + "242 µs ± 15.2 µs per loop (mean ± std. dev. of 7 runs, 1000 loops each)\n" + ] + } + ], + "source": [ + "assert np.all(psorted[:-1] <= psorted[1:])\n", + "#assert np.all(hope_qsort_kernel[:-1] <= hope_qsort_kernel[1:])\n", + "#assert np.all(numba_qsort_kernel[:-1] <= numba_qsort_kernel[1:])\n", + "#assert np.all(cython_qsort_kernel[:-1] <= cython_qsort_kernel[1:])\n", + "\n", + "%timeit qsort_kernel(lst.copy(), 0, len(lst)-1)\n", + "%timeit hope_qsort_kernel(lst.copy(), 0, len(lst)-1)\n", + "#%timeit numba_qsort_kernel(lst.copy(), 0, len(lst)-1)\n", + "%timeit cython_qsort_kernel(lst.copy(), 0, len(lst)-1)\n", + "%timeit native_qsort_kernel(lst.copy(), 0, len(lst)-1)\n", + "%timeit np.sort(lst.copy())" + ] + }, + { + "cell_type": "code", + "execution_count": 12, + "metadata": {}, + "outputs": [ { - "cell_type": "code", - "collapsed": false, - "input": [ - "%load_ext cythonmagic\n" - ], - "language": "python", - "metadata": {}, - "outputs": [ - { - "output_type": "stream", - "stream": "stdout", - "text": [ - "The cythonmagic extension is already loaded. To reload it, use:\n", - " %reload_ext cythonmagic\n" - ] - } - ], - "prompt_number": 24 - }, + "name": "stdout", + "output_type": "stream", + "text": [ + "function: native_qsort_kernel , av. time sec: 0.00029402, min. time sec: 0.00029235, relative: 1.0\n", + "function: cython_qsort_kernel , av. time sec: 0.00031540, min. time sec: 0.00031303, relative: 1.1\n", + "function: hope_qsort_kernel , av. time sec: 0.00035815, min. time sec: 0.00033481, relative: 1.2\n", + "function: qsort_kernel , av. time sec: 0.02226692, min. time sec: 0.02194059, relative: 75.7\n" + ] + } + ], + "source": [ + "a = lst.copy()\n", + "\n", + "lo = 0\n", + "hi = len(lst)-1\n", + "\n", + "perf_comp_data([\"hope_qsort_kernel\", \n", + " \"qsort_kernel\", \n", + " #\"numpy_qsort_kernel\", \n", + " \"cython_qsort_kernel\", \n", + " \"native_qsort_kernel\"],\n", + " 5*[\"a, lo, hi\"], rep=100, extra_setup=\"from __main__ import lst;a = lst.copy()\")" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# pi sum" + ] + }, + { + "cell_type": "code", + "execution_count": 13, + "metadata": {}, + "outputs": [ { - "cell_type": "code", - "collapsed": false, - "input": [ - "%%cython\n", - "\n", - "cimport cython\n", - "\n", - "@cython.boundscheck(False)\n", - "@cython.wraparound(False)\n", - "@cython.locals(f=float)\n", - "def cython_pisum():\n", - " cdef double sum = 0.0\n", - " for j in range(1, 501):\n", - " sum = 0.0\n", - " f = 0.0\n", - " for k in range(1, 10001):\n", - " sum += 1.0/(k*k)\n", - " return sum\n", + "name": "stdout", + "output_type": "stream", + "text": [ + "running build_ext\n", + "building 'pisum' extension\n", + "C compiler: /usr/bin/clang -fno-strict-aliasing -Wsign-compare -Wunreachable-code -fno-common -dynamic -DNDEBUG -g -fwrapv -O3 -Wall -Wstrict-prototypes -arch i386 -arch x86_64 -g\n", "\n", - "@cython.boundscheck(False)\n", - "@cython.wraparound(False)\n", - "@cython.locals(f=float)\n", - "def cython_pisum_opt():\n", - " cdef double sum = 0.0\n", - " for j in range(1, 501):\n", - " sum = 0.0\n", - " f = 0.0\n", - " for k in range(1, 10001):\n", - " f += 1.\n", - " sum += 1.0/(f*f)\n", - " return sum\n", + "compile options: '-I/Users/uweschmitt/Projects/hope/venv3/lib/python3.5/site-packages/numpy/core/include -I/Users/uweschmitt/Projects/hope/venv3/include -I/Library/Frameworks/Python.framework/Versions/3.5/include/python3.5m -c'\n", + "extra options: '-Wall -Wno-unused-variable -march=native -stdlib=libc++ -std=c++11 -Wno-unreachable-code'\n", "\n", + "running build_ext\n", + "building 'pisum_opt' extension\n", + "C compiler: /usr/bin/clang -fno-strict-aliasing -Wsign-compare -Wunreachable-code -fno-common -dynamic -DNDEBUG -g -fwrapv -O3 -Wall -Wstrict-prototypes -arch i386 -arch x86_64 -g\n", "\n", - "assert abs(cython_pisum()-1.644834071848065) < 1e-6\n", - "assert abs(cython_pisum_opt()-1.644834071848065) < 1e-6" - ], - "language": "python", - "metadata": {}, - "outputs": [], - "prompt_number": 25 + "compile options: '-I/Users/uweschmitt/Projects/hope/venv3/lib/python3.5/site-packages/numpy/core/include -I/Users/uweschmitt/Projects/hope/venv3/include -I/Library/Frameworks/Python.framework/Versions/3.5/include/python3.5m -c'\n", + "extra options: '-Wall -Wno-unused-variable -march=native -stdlib=libc++ -std=c++11 -Wno-unreachable-code'\n", + "\n" + ] }, { - "cell_type": "code", - "collapsed": false, - "input": [ - "%timeit pisum()\n", - "%timeit pisum_opt()\n", - "%timeit hope_pisum()\n", - "%timeit hope_pisum_opt()\n", - "%timeit numba_pisum()\n", - "%timeit numba_pisum_opt()\n", - "%timeit cython_pisum()\n", - "%timeit cython_pisum_opt()\n", - "%timeit native_pisum()\n", - "%timeit native_pisum_opt()" - ], - "language": "python", - "metadata": {}, - "outputs": [ - { - "output_type": "stream", - "stream": "stdout", - "text": [ - "1 loops, best of 3: 493 ms per loop\n", - "1 loops, best of 3: 641 ms per loop" - ] - }, - { - "output_type": "stream", - "stream": "stdout", - "text": [ - "\n", - "10 loops, best of 3: 21.4 ms per loop" - ] - }, - { - "output_type": "stream", - "stream": "stdout", - "text": [ - "\n", - "10 loops, best of 3: 21.6 ms per loop" - ] - }, - { - "output_type": "stream", - "stream": "stdout", - "text": [ - "\n", - "10 loops, best of 3: 40.8 ms per loop" - ] - }, - { - "output_type": "stream", - "stream": "stdout", - "text": [ - "\n", - "10 loops, best of 3: 21.8 ms per loop" - ] - }, - { - "output_type": "stream", - "stream": "stdout", - "text": [ - "\n", - "1 loops, best of 3: 284 ms per loop" - ] - }, - { - "output_type": "stream", - "stream": "stdout", - "text": [ - "\n", - "10 loops, best of 3: 21.1 ms per loop" - ] - }, - { - "output_type": "stream", - "stream": "stdout", - "text": [ - "\n", - "10 loops, best of 3: 21.1 ms per loop" - ] - }, - { - "output_type": "stream", - "stream": "stdout", - "text": [ - "\n", - "10 loops, best of 3: 21.3 ms per loop" - ] - }, - { - "output_type": "stream", - "stream": "stdout", - "text": [ - "\n" - ] - } - ], - "prompt_number": 26 - }, + "ename": "AssertionError", + "evalue": "", + "output_type": "error", + "traceback": [ + "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m", + "\u001b[0;31mAssertionError\u001b[0m Traceback (most recent call last)", + "\u001b[0;32m\u001b[0m in \u001b[0;36m\u001b[0;34m()\u001b[0m\n\u001b[1;32m 33\u001b[0m \u001b[0;32massert\u001b[0m \u001b[0mabs\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mhope_pisum_opt\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m-\u001b[0m\u001b[0;36m1.644834071848065\u001b[0m\u001b[0;34m)\u001b[0m \u001b[0;34m<\u001b[0m \u001b[0;36m1e-6\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 34\u001b[0m \u001b[0;32massert\u001b[0m \u001b[0mabs\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mnumba_pisum\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m-\u001b[0m\u001b[0;36m1.644834071848065\u001b[0m\u001b[0;34m)\u001b[0m \u001b[0;34m<\u001b[0m \u001b[0;36m1e-6\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m---> 35\u001b[0;31m \u001b[0;32massert\u001b[0m \u001b[0mabs\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mnative_pisum\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m-\u001b[0m\u001b[0;36m1.644834071848065\u001b[0m\u001b[0;34m)\u001b[0m \u001b[0;34m<\u001b[0m \u001b[0;36m1e-6\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 36\u001b[0m \u001b[0;32massert\u001b[0m \u001b[0mabs\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mnative_pisum_opt\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m-\u001b[0m\u001b[0;36m1.644834071848065\u001b[0m\u001b[0;34m)\u001b[0m \u001b[0;34m<\u001b[0m \u001b[0;36m1e-6\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n", + "\u001b[0;31mAssertionError\u001b[0m: " + ] + } + ], + "source": [ + "def pisum():\n", + " for j in range(1, 501):\n", + " sum = 0.0\n", + " f = 0.0\n", + " for k in range(1, 10001):\n", + " sum += 1.0/(k*k)\n", + " return sum\n", + "\n", + "def pisum_opt():\n", + " for j in range(1, 501):\n", + " sum = 0.0\n", + " f = 0.0\n", + " for k in range(1, 10001):\n", + " f += 1.\n", + " sum += 1.0/(f*f)\n", + " return sum\n", + "\n", + "hope_pisum = hope.jit(pisum)\n", + "hope_pisum_opt = hope.jit(pisum_opt)\n", + "\n", + "numba_pisum = numba.jit(pisum, nopython=True)\n", + "numba_pisum_opt = numba.jit(pisum_opt, nopython=True)\n", + "\n", + "native_pisum_mod = load(\"pisum\")\n", + "native_pisum = native_pisum_mod.run\n", + "\n", + "native_pisum_opt_mod = load(\"pisum_opt\")\n", + "native_pisum_opt = native_pisum_opt_mod.run\n", + "\n", + "\n", + "assert abs(pisum()-1.644834071848065) < 1e-6\n", + "assert abs(hope_pisum()-1.644834071848065) < 1e-6\n", + "assert abs(hope_pisum_opt()-1.644834071848065) < 1e-6\n", + "assert abs(numba_pisum()-1.644834071848065) < 1e-6\n", + "assert abs(native_pisum()-1.644834071848065) < 1e-6\n", + "assert abs(native_pisum_opt()-1.644834071848065) < 1e-6" + ] + }, + { + "cell_type": "code", + "execution_count": 15, + "metadata": {}, + "outputs": [], + "source": [ + "%%cython\n", + "\n", + "cimport cython\n", + "\n", + "@cython.boundscheck(False)\n", + "@cython.wraparound(False)\n", + "@cython.locals(f=float)\n", + "@cython.cdivision(True)\n", + "def cython_pisum():\n", + " cdef double sum = 0.0\n", + " cdef int j, k\n", + " for j in range(1, 501):\n", + " sum = 0.0\n", + " for k in range(1, 10001):\n", + " sum += 1.0/(k*k)\n", + " return sum\n", + "\n", + "@cython.boundscheck(False)\n", + "@cython.wraparound(False)\n", + "@cython.locals(f=float)\n", + "@cython.cdivision(True)\n", + "def cython_pisum_opt():\n", + " cdef double sum = 0.0\n", + " cdef int j, k\n", + " for j in range(1, 501):\n", + " sum = 0.0\n", + " f = 0.0\n", + " for k in range(1, 10001):\n", + " f += 1.\n", + " sum += 1.0/(f*f)\n", + " return sum\n", + "\n", + "\n", + "assert abs(cython_pisum()-1.644834071848065) < 1e-6\n", + "assert abs(cython_pisum_opt()-1.644834071848065) < 1e-6" + ] + }, + { + "cell_type": "code", + "execution_count": 16, + "metadata": {}, + "outputs": [ { - "cell_type": "code", - "collapsed": false, - "input": [ - "perf_comp_data([\"pisum\", \"pisum_opt\", \n", - " \"hope_pisum\", \"hope_pisum_opt\", \n", - " \"numba_pisum\", \"numba_pisum_opt\", \n", - " #\"cython_pisum\", \n", - " \"cython_pisum_opt\",\n", - " \"native_pisum\", \"native_pisum_opt\",], \n", - " None, rep=100)" - ], - "language": "python", - "metadata": {}, - "outputs": [ - { - "output_type": "stream", - "stream": "stdout", - "text": [ - "function: cython_pisum_opt , av. time sec: 0.02122343, min. time sec: 0.02094698, relative: 1.0\n", - "function: native_pisum_opt , av. time sec: 0.02158451, min. time sec: 0.02108502, relative: 1.0\n", - "function: native_pisum , av. time sec: 0.02167845, min. time sec: 0.02090883, relative: 1.0\n", - "function: numba_pisum_opt , av. time sec: 0.02173293, min. time sec: 0.02095199, relative: 1.0\n", - "function: hope_pisum_opt , av. time sec: 0.02241504, min. time sec: 0.02135611, relative: 1.1\n", - "function: hope_pisum , av. time sec: 0.02270293, min. time sec: 0.02136111, relative: 1.1\n", - "function: numba_pisum , av. time sec: 0.04188490, min. time sec: 0.04017806, relative: 2.0\n", - "function: pisum , av. time sec: 0.49351048, min. time sec: 0.48015594, relative: 23.3\n", - "function: pisum_opt , av. time sec: 0.64186037, min. time sec: 0.61331892, relative: 30.2\n" - ] - } - ], - "prompt_number": 27 - }, + "name": "stdout", + "output_type": "stream", + "text": [ + "740 ms ± 38.7 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)\n", + "615 ms ± 8.21 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)\n", + "39 ms ± 660 µs per loop (mean ± std. dev. of 7 runs, 10 loops each)\n", + "20.9 ms ± 401 µs per loop (mean ± std. dev. of 7 runs, 10 loops each)\n", + "21 ms ± 381 µs per loop (mean ± std. dev. of 7 runs, 10 loops each)\n", + "20.9 ms ± 547 µs per loop (mean ± std. dev. of 7 runs, 10 loops each)\n", + "20.8 ms ± 360 µs per loop (mean ± std. dev. of 7 runs, 10 loops each)\n", + "20.8 ms ± 484 µs per loop (mean ± std. dev. of 7 runs, 10 loops each)\n", + "32.8 ms ± 1.88 ms per loop (mean ± std. dev. of 7 runs, 10 loops each)\n", + "23.3 ms ± 1.31 ms per loop (mean ± std. dev. of 7 runs, 10 loops each)\n" + ] + } + ], + "source": [ + "%timeit pisum()\n", + "%timeit pisum_opt()\n", + "%timeit hope_pisum()\n", + "%timeit hope_pisum_opt()\n", + "%timeit numba_pisum()\n", + "%timeit numba_pisum_opt()\n", + "%timeit cython_pisum()\n", + "%timeit cython_pisum_opt()\n", + "%timeit native_pisum()\n", + "%timeit native_pisum_opt()" + ] + }, + { + "cell_type": "code", + "execution_count": 17, + "metadata": {}, + "outputs": [ { - "cell_type": "code", - "collapsed": false, - "input": [], - "language": "python", - "metadata": {}, - "outputs": [], - "prompt_number": 17 + "name": "stdout", + "output_type": "stream", + "text": [ + "function: cython_pisum_opt , av. time sec: 0.02031763, min. time sec: 0.02003632, relative: 1.0\n", + "function: cython_pisum , av. time sec: 0.02036748, min. time sec: 0.02007828, relative: 1.0\n", + "function: hope_pisum_opt , av. time sec: 0.02039336, min. time sec: 0.02009269, relative: 1.0\n", + "function: numba_pisum , av. time sec: 0.02047833, min. time sec: 0.02018560, relative: 1.0\n", + "function: numba_pisum_opt , av. time sec: 0.02050058, min. time sec: 0.02010900, relative: 1.0\n", + "function: native_pisum_opt , av. time sec: 0.02051785, min. time sec: 0.02011683, relative: 1.0\n", + "function: native_pisum , av. time sec: 0.02956523, min. time sec: 0.02909494, relative: 1.5\n", + "function: hope_pisum , av. time sec: 0.03828400, min. time sec: 0.03774484, relative: 1.9\n", + "function: pisum_opt , av. time sec: 0.62492522, min. time sec: 0.60418793, relative: 30.8\n", + "function: pisum , av. time sec: 0.78460755, min. time sec: 0.68841730, relative: 38.6\n" + ] } ], - "metadata": {} + "source": [ + "perf_comp_data([\"pisum\", \"pisum_opt\", \n", + " \"hope_pisum\", \"hope_pisum_opt\", \n", + " \"numba_pisum\", \"numba_pisum_opt\", \n", + " \"cython_pisum\", \n", + " \"cython_pisum_opt\",\n", + " \"native_pisum\", \"native_pisum_opt\",], \n", + " None, rep=100)" + ] } - ] -} \ No newline at end of file + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.5.1" + } + }, + "nbformat": 4, + "nbformat_minor": 1 +} diff --git a/benchmarks/native_cpp_gen.ipynb b/benchmarks/native_cpp_gen.ipynb index 20878e4..2c761c1 100644 --- a/benchmarks/native_cpp_gen.ipynb +++ b/benchmarks/native_cpp_gen.ipynb @@ -1,2267 +1,2390 @@ { - "metadata": { - "name": "" - }, - "nbformat": 3, - "nbformat_minor": 0, - "worksheets": [ + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# IPython magic extension version_information" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Use the '%version_information' IPython magic extension in a notebook to display information about which versions of dependency package that was used to run the notebook.\n", + "Installation.\n", + "\n", + "Run\n", + "\n", + " pip install git+https://github.com/jrjohansson/version_information\n", + " \n", + " \n", + "to install this extension" + ] + }, { - "cells": [ + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Installation" + ] + }, + { + "cell_type": "code", + "execution_count": 1, + "metadata": { + "collapsed": true + }, + "outputs": [], + "source": [ + "%load_ext version_information" + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "metadata": {}, + "outputs": [ { - "cell_type": "heading", - "level": 1, + "data": { + "application/json": { + "Software versions": [ + { + "module": "Python", + "version": "3.5.1 64bit [GCC 4.2.1 (Apple Inc. build 5666) (dot 3)]" + }, + { + "module": "IPython", + "version": "6.1.0" + }, + { + "module": "OS", + "version": "Darwin 15.6.0 x86_64 i386 64bit" + } + ] + }, + "text/html": [ + "
SoftwareVersion
Python3.5.1 64bit [GCC 4.2.1 (Apple Inc. build 5666) (dot 3)]
IPython6.1.0
OSDarwin 15.6.0 x86_64 i386 64bit
Tue Aug 29 17:03:58 2017 CEST
" + ], + "text/latex": [ + "\\begin{tabular}{|l|l|}\\hline\n", + "{\\bf Software} & {\\bf Version} \\\\ \\hline\\hline\n", + "Python & 3.5.1 64bit [GCC 4.2.1 (Apple Inc. build 5666) (dot 3)] \\\\ \\hline\n", + "IPython & 6.1.0 \\\\ \\hline\n", + "OS & Darwin 15.6.0 x86\\_64 i386 64bit \\\\ \\hline\n", + "\\hline \\multicolumn{2}{|l|}{Tue Aug 29 17:03:58 2017 CEST} \\\\ \\hline\n", + "\\end{tabular}\n" + ], + "text/plain": [ + "Software versions\n", + "Python 3.5.1 64bit [GCC 4.2.1 (Apple Inc. build 5666) (dot 3)]\n", + "IPython 6.1.0\n", + "OS Darwin 15.6.0 x86_64 i386 64bit\n", + "Tue Aug 29 17:03:58 2017 CEST" + ] + }, + "execution_count": 2, "metadata": {}, - "source": [ - "IPython magic extension version_information\n" - ] - }, + "output_type": "execute_result" + } + ], + "source": [ + "%version_information" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Native CPP codes" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Fibonacci CPP" + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "metadata": {}, + "outputs": [ { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Use the '%version_information' IPython magic extension in a notebook to display information about which versions of dependency package that was used to run the notebook.\n", - "Installation" + "name": "stdout", + "output_type": "stream", + "text": [ + "mkdir: src: File exists\r\n" ] - }, + } + ], + "source": [ + "!mkdir src" + ] + }, + { + "cell_type": "code", + "execution_count": 17, + "metadata": {}, + "outputs": [ { - "cell_type": "heading", - "level": 2, - "metadata": {}, - "source": [ - "Installation" + "name": "stdout", + "output_type": "stream", + "text": [ + "Overwriting src/fib.cpp\n" ] - }, - { - "cell_type": "code", - "collapsed": false, - "input": [ - "%install_ext http://raw.github.com/jrjohansson/version_information/master/version_information.py" - ], - "language": "python", - "metadata": {}, - "outputs": [ - { - "output_type": "stream", - "stream": "stdout", - "text": [ - "Installed version_information.py. To use it, type:\n", - " %load_ext version_information\n" - ] - } - ], - "prompt_number": 3 - }, + } + ], + "source": [ + "%%file src/fib.cpp\n", + "#define PY_ARRAY_UNIQUE_SYMBOL fkt_ARRAY_API\n", + "#define NPY_NO_DEPRECATED_API NPY_1_7_API_VERSION\n", + "#include \n", + "#include \n", + "#include \n", + "#include \n", + "#include \n", + "#include \n", + "#include \n", + "#include \n", + "#include \n", + "#include \n", + "#include \n", + "\n", + "\n", + "struct PyObj {\n", + " typedef PyObject * ptr_t;\n", + " typedef PyArrayObject * arrptr_t;\n", + " PyObj(): dec(false), ptr(NULL) {}\n", + " PyObj(ptr_t p): dec(false), ptr(p) {}\n", + " ~PyObj() { if(dec) Py_DECREF(ptr); }\n", + " PyObj & operator=(ptr_t p) { if(dec) Py_DECREF(ptr); ptr = p; dec = false; return *this; }\n", + " PyObj & incref(ptr_t p) { if(dec) Py_DECREF(ptr); ptr = p; dec = (p != NULL); return *this; }\n", + " operator bool() const { return ptr; }\n", + " operator ptr_t() const { return ptr; }\n", + " operator arrptr_t() const { return (arrptr_t)ptr; }\n", + " bool dec;\n", + " ptr_t ptr;\n", + "};\n", + "\n", + "inline npy_int64 fib_J(\n", + " npy_int64 cn\n", + ");\n", + "inline npy_int64 fib_J(\n", + " npy_int64 cn\n", + ") {\n", + " if (cn < 2) {\n", + " return cn;\n", + " }\n", + " return fib_J(cn - 1) + fib_J(cn - 2); \n", + "}\n", + "\n", + "#include \n", + "#include \n", + "#include \n", + "#include \n", + "#include \n", + "#include \n", + "\n", + "void sighandler(int sig);\n", + "\n", + "void sighandler(int sig) {\n", + " std::ostringstream buffer;\n", + " buffer << \"Abort by \" << (sig == SIGSEGV ? \"segfault\" : \"bus error\") << std::endl;\n", + " void * stack[64];\n", + " std::size_t depth = backtrace(stack, 64);\n", + " if (!depth)\n", + " buffer << \" \" << std::endl;\n", + " else {\n", + " char ** symbols = backtrace_symbols(stack, depth);\n", + " for (std::size_t i = 1; i < depth; ++i) {\n", + " std::string symbol = symbols[i];\n", + " if (symbol.find_first_of(' ', 59) != std::string::npos) {\n", + " std::string name = symbol.substr(59, symbol.find_first_of(' ', 59) - 59);\n", + " int status;\n", + " char * demangled = abi::__cxa_demangle(name.c_str(), NULL, NULL, &status);\n", + " if (!status) {\n", + " buffer << \" \"\n", + " << symbol.substr(0, 59)\n", + " << demangled\n", + " << symbol.substr(59 + name.size())\n", + " << std::endl;\n", + " free(demangled);\n", + " } else\n", + " buffer << \" \" << symbol << std::endl;\n", + " } else\n", + " buffer << \" \" << symbol << std::endl;\n", + " }\n", + " free(symbols);\n", + " }\n", + " std::cerr << buffer.str();\n", + " std::exit(EXIT_FAILURE);\n", + " }\n", + "\n", + "\n", + "extern \"C\" {\n", + "\n", + " PyObject * create_signature;\n", + "\n", + " struct sigaction slot;\n", + "\n", + " PyObject * set_create_signature(PyObject * self, PyObject * args) {\n", + " if (!PyArg_ParseTuple(args, \"O\", &create_signature)) {\n", + " PyErr_SetString(PyExc_ValueError, \"Invalid Argument to set_create_signature!\");\n", + " return NULL;\n", + " }\n", + " Py_INCREF(create_signature);\n", + " memset(&slot, 0, sizeof(slot));\n", + " slot.sa_handler = &sighandler;\n", + " sigaction(SIGSEGV, &slot, NULL);\n", + " sigaction(SIGBUS, &slot, NULL);\n", + " Py_INCREF(Py_None);\n", + " return Py_None;\n", + " }\n", + "\n", + " PyObject * run(PyObject * self, PyObject * args) {\n", + " {\n", + " PyObject * pn; npy_int64 cn;\n", + " if (\n", + " PyTuple_CheckExact(args) and PyTuple_GET_SIZE(args) == 1\n", + " and (pn = PyTuple_GET_ITEM(args, 0)) and PyLong_CheckExact(pn)\n", + " ) {\n", + " cn = PyLong_AS_LONG(pn);\n", + " try {\n", + " return Py_BuildValue(\"l\", fib_J(\n", + " cn\n", + " ));\n", + " } catch (...) {\n", + " return NULL;\n", + " }\n", + " } else\n", + " PyErr_Clear();\n", + " }\n", + " PyObject * signatures = Py_BuildValue(\"(sO)\", \"gANdcQBdcQFjaG9wZS5fYXN0ClZhcmlhYmxlCnECKYFxA31xBChYBAAAAGRpbXNxBUsAWAUAAABk\\ndHlwZXEGY2J1aWx0aW5zCmludApxB1gEAAAAbmFtZXEIWAEAAABucQl1YmFhLg==\\n\", args);\n", + " if (!signatures) {\n", + " PyErr_SetString(PyExc_ValueError, \"Error building signature string for fib\");\n", + " return NULL;\n", + " }\n", + " return PyObject_Call(create_signature, signatures, NULL);\n", + " }\n", + "\n", + " PyMethodDef fibMethods[] = {\n", + " { \"set_create_signature\", set_create_signature, METH_VARARGS, \"signal handler\" },\n", + " { \"run\", run, METH_VARARGS, \"module function\" },\n", + " { NULL, NULL, 0, NULL }\n", + " };\n", + "\n", + "\n", + " static struct PyModuleDef fibmodule = {\n", + " PyModuleDef_HEAD_INIT,\n", + " \"fib\",\n", + " NULL,\n", + " -1,\n", + " fibMethods\n", + " };\n", + "\n", + "\n", + "\n", + " PyMODINIT_FUNC PyInit_fib(void) {\n", + " import_array();\n", + " PyImport_ImportModule(\"numpy\");\n", + " return PyModule_Create(&fibmodule);\n", + " }\n", + "}" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Quicksort CPP" + ] + }, + { + "cell_type": "code", + "execution_count": 5, + "metadata": {}, + "outputs": [ { - "cell_type": "heading", - "level": 1, - "metadata": {}, - "source": [ - "Native CPP codes" + "name": "stdout", + "output_type": "stream", + "text": [ + "Overwriting src/qsort_kernel.cpp\n" ] - }, + } + ], + "source": [ + "%%file src/qsort_kernel.cpp\n", + "#define PY_ARRAY_UNIQUE_SYMBOL qsort_kernel_ARRAY_API\n", + "#define NPY_NO_DEPRECATED_API NPY_1_7_API_VERSION\n", + "#include \n", + "#include \n", + "#include \n", + "#include \n", + "#include \n", + "#include \n", + "#include \n", + "#include \n", + "#include \n", + "#include \n", + "#include \n", + "struct PyObj {\n", + " typedef PyObject * ptr_t;\n", + " typedef PyArrayObject * arrptr_t;\n", + " PyObj(): dec(false), ptr(NULL) {}\n", + " PyObj(ptr_t p): dec(false), ptr(p) {}\n", + " ~PyObj() { if(dec) Py_DECREF(ptr); }\n", + " PyObj & operator=(ptr_t p) { if(dec) Py_DECREF(ptr); ptr = p; dec = false; return *this; }\n", + " PyObj & incref(ptr_t p) { if(dec) Py_DECREF(ptr); ptr = p; dec = (p != NULL); return *this; }\n", + " operator bool() const { return ptr; }\n", + " operator ptr_t() const { return ptr; }\n", + " operator arrptr_t() const { return (arrptr_t)ptr; }\n", + " bool dec;\n", + " ptr_t ptr;\n", + "};\n", + "inline std::tuple qsort_kernel_d1JJ(PyObject * pa,\n", + " npy_intp const * __restrict__ sa,\n", + " npy_double * __restrict__ ca,\n", + " npy_int64 clo,\n", + " npy_int64 chi);\n", + "\n", + "inline std::tuple qsort_kernel_d1JJ(PyObject * pa,\n", + " npy_intp const * __restrict__ sa,\n", + " npy_double * __restrict__ ca,\n", + " npy_int64 clo,\n", + " npy_int64 chi){\n", + " npy_int64 ci = clo;\n", + " npy_int64 cj = chi;\n", + " npy_double cpivot;\n", + "\n", + " while (ci < chi) {\n", + " cpivot = ca[(int)((clo + chi) / 2)];\n", + "\n", + " while (ci <= cj) {\n", + " while (ca[ci] < cpivot) {\n", + " ci += 1;\n", + " }\n", + "\n", + " while (ca[cj] > cpivot) {\n", + " cj -= 1;\n", + " }\n", + "\n", + " if (ci <= cj) {\n", + " auto ctmp = ca[ci];\n", + " ca[ci] = ca[cj];\n", + " ca[cj] = ctmp;\n", + " ci += 1;\n", + " cj -= 1;\n", + " }\n", + " }\n", + "\n", + " if (clo < cj) {\n", + " qsort_kernel_d1JJ(pa, sa, ca, clo, cj);\n", + " }\n", + "\n", + " clo = ci;\n", + " cj = chi;\n", + " }\n", + "\n", + " return std::make_tuple((PyObject *)pa, sa, ca);\n", + "}\n", + "\n", + "void sighandler(int sig);\n", + "#include \n", + "extern \"C\" {\n", + " PyObject * create_signature;\n", + " struct sigaction slot;\n", + " PyObject * set_create_signature(PyObject * self, PyObject * args) {\n", + " if (!PyArg_ParseTuple(args, \"O\", &create_signature)) {\n", + " PyErr_SetString(PyExc_ValueError, \"Invalid Argument to set_create_signature!\");\n", + " return NULL;\n", + " }\n", + " Py_INCREF(create_signature);\n", + " memset(&slot, 0, sizeof(slot));\n", + " slot.sa_handler = &sighandler;\n", + " sigaction(SIGSEGV, &slot, NULL);\n", + " sigaction(SIGBUS, &slot, NULL);\n", + " Py_INCREF(Py_None);\n", + " return Py_None;\n", + " }\n", + " PyObject * run(PyObject * self, PyObject * args) {\n", + " {\n", + " PyObj pa;\n", + " PyObject * plo; npy_int64 clo;\n", + " PyObject * phi; npy_int64 chi;\n", + " if (\n", + " PyTuple_CheckExact(args) and PyTuple_GET_SIZE(args) == 3\n", + " and (pa = PyTuple_GET_ITEM(args, 0)) and PyArray_CheckExact(pa)\n", + " and PyArray_TYPE((PyArrayObject *)pa) == NPY_FLOAT64 and PyArray_NDIM((PyArrayObject *)pa) == 1\n", + " and (plo = PyTuple_GET_ITEM(args, 1)) and PyLong_CheckExact(plo)\n", + " and (phi = PyTuple_GET_ITEM(args, 2)) and PyLong_CheckExact(phi)\n", + " ) {\n", + " if (!(pa.incref((PyObject *)PyArray_GETCONTIGUOUS((PyArrayObject *)pa)))) {\n", + " PyErr_SetString(PyExc_ValueError, \"Invalid Argument type on a!\");\n", + " return NULL;\n", + " }\n", + " clo = PyLong_AS_LONG(plo);\n", + " chi = PyLong_AS_LONG(phi);\n", + " try {\n", + " PyObject * res = std::get<0>(qsort_kernel_d1JJ(\n", + " pa, PyArray_SHAPE((PyArrayObject *)pa), (npy_double *)PyArray_DATA((PyArrayObject *)pa)\n", + " , clo\n", + " , chi\n", + " ));\n", + "\n", + " Py_INCREF(res);\n", + " return res;\n", + " } catch (...) {\n", + " return NULL;\n", + " }\n", + " } else\n", + " PyErr_Clear();\n", + " }\n", + " PyObject * signatures = Py_BuildValue(\"(sO)\", \"(lp0\\n(lp1\\nccopy_reg\\n_reconstructor\\np2\\n(chope._ast\\nVariable\\np3\\nc__builtin__\\nobject\\np4\\nNtp5\\nRp6\\n(dp7\\nS'name'\\np8\\nS'a'\\np9\\nsS'dtype'\\np10\\nS'float64'\\np11\\nsS'dims'\\np12\\nI1\\nsbag2\\n(g3\\ng4\\nNtp13\\nRp14\\n(dp15\\ng8\\nS'lo'\\np16\\nsg10\\nc__builtin__\\nint\\np17\\nsg12\\nI0\\nsbag2\\n(g3\\ng4\\nNtp18\\nRp19\\n(dp20\\ng8\\nS'hi'\\np21\\nsg10\\ng17\\nsg12\\nI0\\nsbaa.\", args);\n", + " if (!signatures) {\n", + " PyErr_SetString(PyExc_ValueError, \"Error building signature string for qsort_kernel\");\n", + " return NULL;\n", + " }\n", + " return PyObject_Call(create_signature, signatures, NULL);\n", + " }\n", + " PyMethodDef qsort_kernelMethods[] = {\n", + " { \"set_create_signature\", set_create_signature, METH_VARARGS, \"signal handler\" },\n", + " { \"run\", run, METH_VARARGS, \"module function\" },\n", + " { NULL, NULL, 0, NULL }\n", + " };\n", + "}\n", + "\n", + "static struct PyModuleDef qsort_kernel_module = {\n", + " PyModuleDef_HEAD_INIT,\n", + " \"qsort_kernel\",\n", + " NULL,\n", + " -1,\n", + " qsort_kernelMethods\n", + "};\n", + "\n", + "PyMODINIT_FUNC PyInit_qsort_kernel(void) {\n", + " import_array();\n", + " PyImport_ImportModule(\"numpy\");\n", + " return PyModule_Create(&qsort_kernel_module);\n", + "}\n", + "\n", + "#include \n", + "#include \n", + "#include \n", + "#include \n", + "#include \n", + "void sighandler(int sig) {\n", + " std::ostringstream buffer;\n", + " buffer << \"Abort by \" << (sig == SIGSEGV ? \"segfault\" : \"bus error\") << std::endl;\n", + " void * stack[64];\n", + " std::size_t depth = backtrace(stack, 64);\n", + " if (!depth)\n", + " buffer << \" \" << std::endl;\n", + " else {\n", + " char ** symbols = backtrace_symbols(stack, depth);\n", + " for (std::size_t i = 1; i < depth; ++i) {\n", + " std::string symbol = symbols[i];\n", + " if (symbol.find_first_of(' ', 59) != std::string::npos) {\n", + " std::string name = symbol.substr(59, symbol.find_first_of(' ', 59) - 59);\n", + " int status;\n", + " char * demangled = abi::__cxa_demangle(name.c_str(), NULL, NULL, &status);\n", + " if (!status) {\n", + " buffer << \" \"\n", + " << symbol.substr(0, 59)\n", + " << demangled\n", + " << symbol.substr(59 + name.size())\n", + " << std::endl;\n", + " free(demangled);\n", + " } else\n", + " buffer << \" \" << symbol << std::endl;\n", + " } else\n", + " buffer << \" \" << symbol << std::endl;\n", + " }\n", + " free(symbols);\n", + " }\n", + " std::cerr << buffer.str();\n", + " std::exit(EXIT_FAILURE);\n", + " }\n" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Pi sum CPP" + ] + }, + { + "cell_type": "code", + "execution_count": 6, + "metadata": {}, + "outputs": [ { - "cell_type": "heading", - "level": 2, - "metadata": {}, - "source": [ - "Fibonacci CPP" + "name": "stdout", + "output_type": "stream", + "text": [ + "Overwriting src/pisum.cpp\n" ] - }, - { - "cell_type": "code", - "collapsed": false, - "input": [ - "%%file src/fib.cpp\n", - "#define PY_ARRAY_UNIQUE_SYMBOL fib_ARRAY_API\n", - "#define NPY_NO_DEPRECATED_API NPY_1_7_API_VERSION\n", - "#include \n", - "#include \n", - "#include \n", - "#include \n", - "#include \n", - "#include \n", - "#include \n", - "#include \n", - "#include \n", - "#include \n", - "#include \n", - "struct PyObj {\n", - "\ttypedef PyObject * ptr_t;\n", - "\ttypedef PyArrayObject * arrptr_t;\n", - "\tPyObj(): dec(false), ptr(NULL) {}\n", - "\tPyObj(ptr_t p): dec(false), ptr(p) {}\n", - "\t~PyObj() { if(dec) Py_DECREF(ptr); }\n", - "\tPyObj & operator=(ptr_t p) { if(dec) Py_DECREF(ptr); ptr = p; dec = false; return *this; }\n", - "\tPyObj & incref(ptr_t p) { if(dec) Py_DECREF(ptr); ptr = p; dec = (p != NULL); return *this; }\n", - "\toperator bool() const { return ptr; }\n", - "\toperator ptr_t() const { return ptr; }\n", - "\toperator arrptr_t() const { return (arrptr_t)ptr; }\n", - "\tbool dec;\n", - "\tptr_t ptr;\n", - "};\n", - "\n", - "\n", - "inline npy_int64 fib_J(npy_int64 cn);\n", - "\n", - "inline npy_int64 fib_J(npy_int64 cn) {\n", - "\tif (cn < 2) {\n", - "\t\treturn cn;\n", - "\t}\n", - "\treturn fib_J(cn - 1) + fib_J(cn - 2);\n", - "}\n", - "\n", - "\n", - "\n", - "void sighandler(int sig);\n", - "#include \n", - "extern \"C\" {\n", - "\tPyObject * create_signature;\n", - "\tstruct sigaction slot;\n", - "\tPyObject * set_create_signature(PyObject * self, PyObject * args) {\n", - "\t\tif (!PyArg_ParseTuple(args, \"O\", &create_signature)) {\n", - "\t\t\tPyErr_SetString(PyExc_ValueError, \"Invalid Argument to set_create_signature!\");\n", - "\t\t\treturn NULL;\n", - "\t\t}\n", - "\t\tPy_INCREF(create_signature);\n", - "\t\tmemset(&slot, 0, sizeof(slot));\n", - "\t\tslot.sa_handler = &sighandler;\n", - "\t\tsigaction(SIGSEGV, &slot, NULL);\n", - "\t\tsigaction(SIGBUS, &slot, NULL);\n", - "\t\tPy_INCREF(Py_None);\n", - "\t\treturn Py_None;\n", - "\t}\n", - "\tPyObject * run(PyObject * self, PyObject * args) {\n", - "\t\t{\n", - "\t\t\tPyObject * pn; npy_int64 cn;\n", - "\t\t\tif (\n", - "\t\t\t\tPyTuple_CheckExact(args) and PyTuple_GET_SIZE(args) == 1\n", - "\t\t\t\tand (pn = PyTuple_GET_ITEM(args, 0)) and PyInt_CheckExact(pn)\n", - "\t\t\t) {\n", - "\t\t\t\tcn = PyInt_AS_LONG(pn);\n", - "\t\t\t\ttry {\n", - "\t\t\t\t\treturn Py_BuildValue(\"l\", fib_J(\n", - "\t\t\t\t\t\t cn\n", - "\t\t\t\t\t));\n", - "\t\t\t\t} catch (...) {\n", - "\t\t\t\t\treturn NULL;\n", - "\t\t\t\t}\n", - "\t\t\t} else\n", - "\t\t\t\tPyErr_Clear();\n", - "\t\t}\n", - "\t\tPyObject * signatures = Py_BuildValue(\"(sO)\", \"(lp0\\n(lp1\\nccopy_reg\\n_reconstructor\\np2\\n(chope._ast\\nVariable\\np3\\nc__builtin__\\nobject\\np4\\nNtp5\\nRp6\\n(dp7\\nS'name'\\np8\\nS'n'\\np9\\nsS'dtype'\\np10\\nc__builtin__\\nint\\np11\\nsS'dims'\\np12\\nI0\\nsbaa.\", args);\n", - "\t\tif (!signatures) {\n", - "\t\t\tPyErr_SetString(PyExc_ValueError, \"Error building signature string for fib\");\n", - "\t\t\treturn NULL;\n", - "\t\t}\n", - "\t\treturn PyObject_Call(create_signature, signatures, NULL);\n", - "\t}\n", - "\tPyMethodDef fibMethods[] = {\n", - "\t\t{ \"set_create_signature\", (PyCFunction)set_create_signature, METH_VARARGS },\n", - "\t\t{ \"run\", (PyCFunction)run, METH_VARARGS },\n", - "\t\t{ NULL, NULL }\n", - "\t};\n", - "\tPyMODINIT_FUNC initfib(void) {\n", - "\t\timport_array();\n", - "\t\tPyImport_ImportModule(\"numpy\");\n", - "\t\t(void)Py_InitModule(\"fib\", fibMethods);\n", - "\t}\n", - "}\n", - "#include \n", - "#include \n", - "#include \n", - "#include \n", - "#include \n", - "void sighandler(int sig) {\n", - "\tstd::ostringstream buffer;\n", - "\tbuffer << \"Abort by \" << (sig == SIGSEGV ? \"segfault\" : \"bus error\") << std::endl;\n", - "\tvoid * stack[64];\n", - "\tstd::size_t depth = backtrace(stack, 64);\n", - "\tif (!depth)\n", - "\t\tbuffer << \" \" << std::endl;\n", - "\telse {\n", - "\t\tchar ** symbols = backtrace_symbols(stack, depth);\n", - "\t\tfor (std::size_t i = 1; i < depth; ++i) {\n", - "\t\t\tstd::string symbol = symbols[i];\n", - "\t\t\t\tif (symbol.find_first_of(' ', 59) != std::string::npos) {\n", - "\t\t\t\t\tstd::string name = symbol.substr(59, symbol.find_first_of(' ', 59) - 59);\n", - "\t\t\t\t\tint status;\n", - "\t\t\t\t\tchar * demangled = abi::__cxa_demangle(name.c_str(), NULL, NULL, &status);\n", - "\t\t\t\t\tif (!status) {\n", - "\t\t\t\t\t\tbuffer << \" \" \n", - "\t\t\t\t\t\t\t<< symbol.substr(0, 59) \n", - "\t\t\t\t\t\t\t<< demangled\n", - "\t\t\t\t\t\t\t<< symbol.substr(59 + name.size())\n", - "\t\t\t\t\t\t\t<< std::endl;\n", - "\t\t\t\t\t\tfree(demangled);\n", - "\t\t\t\t\t} else\n", - "\t\t\t\t\t\tbuffer << \" \" << symbol << std::endl;\n", - "\t\t\t\t} else\n", - "\t\t\t\t\tbuffer << \" \" << symbol << std::endl;\n", - "\t\t\t}\n", - "\t\t\tfree(symbols);\n", - "\t\t}\n", - "\t\tstd::cerr << buffer.str();\n", - "\t\tstd::exit(EXIT_FAILURE);\n", - "\t}\n", - "\n" - ], - "language": "python", - "metadata": {}, - "outputs": [ - { - "output_type": "stream", - "stream": "stdout", - "text": [ - "Overwriting src/fib.cpp\n" - ] - } - ], - "prompt_number": 2 - }, + } + ], + "source": [ + "%%file src/pisum.cpp\n", + "#define PY_ARRAY_UNIQUE_SYMBOL pisum_ARRAY_API\n", + "#define NPY_NO_DEPRECATED_API NPY_1_7_API_VERSION\n", + "#include \n", + "#include \n", + "#include \n", + "#include \n", + "#include \n", + "#include \n", + "#include \n", + "#include \n", + "#include \n", + "#include \n", + "#include \n", + "struct PyObj {\n", + " typedef PyObject * ptr_t;\n", + " typedef PyArrayObject * arrptr_t;\n", + " PyObj(): dec(false), ptr(NULL) {}\n", + " PyObj(ptr_t p): dec(false), ptr(p) {}\n", + " ~PyObj() { if(dec) Py_DECREF(ptr); }\n", + " PyObj & operator=(ptr_t p) { if(dec) Py_DECREF(ptr); ptr = p; dec = false; return *this; }\n", + " PyObj & incref(ptr_t p) { if(dec) Py_DECREF(ptr); ptr = p; dec = (p != NULL); return *this; }\n", + " operator bool() const { return ptr; }\n", + " operator ptr_t() const { return ptr; }\n", + " operator arrptr_t() const { return (arrptr_t)ptr; }\n", + " bool dec;\n", + " ptr_t ptr;\n", + "};\n", + "\n", + "inline npy_double pisum_();\n", + "\n", + "inline npy_double pisum_() {\n", + " double csum = 0;\n", + " int ck = 1;\n", + " for (int cj = 1; cj < 501; ++cj) {\n", + " csum = 0.0;\n", + " for (ck = 1; ck < 10001; ++ck) {\n", + " csum += (1.0 / (double)(ck * ck));\n", + " }\n", + " }\n", + "\n", + " return npy_double(csum);\n", + "}\n", + "\n", + "void sighandler(int sig);\n", + "#include \n", + "extern \"C\" {\n", + " PyObject * create_signature;\n", + " struct sigaction slot;\n", + " PyObject * set_create_signature(PyObject * self, PyObject * args) {\n", + " if (!PyArg_ParseTuple(args, \"O\", &create_signature)) {\n", + " PyErr_SetString(PyExc_ValueError, \"Invalid Argument to set_create_signature!\");\n", + " return NULL;\n", + " }\n", + " Py_INCREF(create_signature);\n", + " memset(&slot, 0, sizeof(slot));\n", + " slot.sa_handler = &sighandler;\n", + " sigaction(SIGSEGV, &slot, NULL);\n", + " sigaction(SIGBUS, &slot, NULL);\n", + " Py_INCREF(Py_None);\n", + " return Py_None;\n", + " }\n", + " PyObject * run(PyObject * self, PyObject * args) {\n", + " { try {\n", + " return Py_BuildValue(\"d\", pisum_());\n", + " } catch (...) {\n", + " return NULL;\n", + " }\n", + " }\n", + " PyObject * signatures = Py_BuildValue(\"(sO)\", \"(lp0\\n(lp1\\na.\", args);\n", + " if (!signatures) {\n", + " PyErr_SetString(PyExc_ValueError, \"Error building signature string for pisum\");\n", + " return NULL;\n", + " }\n", + " return PyObject_Call(create_signature, signatures, NULL);\n", + " }\n", + "\n", + "PyMethodDef pisumMethods[] = {\n", + "{ \"set_create_signature\", set_create_signature, METH_VARARGS, \"signal handler\" },\n", + "{ \"run\", run, METH_VARARGS, \"module function\" },\n", + "{ NULL, NULL, 0, NULL }\n", + "};\n", + "\n", + "static struct PyModuleDef pisum_module = {\n", + " PyModuleDef_HEAD_INIT,\n", + " \"pisum\",\n", + " NULL,\n", + " -1,\n", + " pisumMethods\n", + "};\n", + "\n", + "PyMODINIT_FUNC PyInit_pisum(void) {\n", + " import_array();\n", + " PyImport_ImportModule(\"numpy\");\n", + " return PyModule_Create(&pisum_module);\n", + "}\n", + "}\n", + "\n", + "#include \n", + "#include \n", + "#include \n", + "#include \n", + "#include \n", + "void sighandler(int sig) {\n", + " std::ostringstream buffer;\n", + " buffer << \"Abort by \" << (sig == SIGSEGV ? \"segfault\" : \"bus error\") << std::endl;\n", + " void * stack[64];\n", + " std::size_t depth = backtrace(stack, 64);\n", + " if (!depth)\n", + " buffer << \" \" << std::endl;\n", + " else {\n", + " char ** symbols = backtrace_symbols(stack, depth);\n", + " for (std::size_t i = 1; i < depth; ++i) {\n", + " std::string symbol = symbols[i];\n", + " if (symbol.find_first_of(' ', 59) != std::string::npos) {\n", + " std::string name = symbol.substr(59, symbol.find_first_of(' ', 59) - 59);\n", + " int status;\n", + " char * demangled = abi::__cxa_demangle(name.c_str(), NULL, NULL, &status);\n", + " if (!status) {\n", + " buffer << \" \"\n", + " << symbol.substr(0, 59)\n", + " << demangled\n", + " << symbol.substr(59 + name.size())\n", + " << std::endl;\n", + " free(demangled);\n", + " } else\n", + " buffer << \" \" << symbol << std::endl;\n", + " } else\n", + " buffer << \" \" << symbol << std::endl;\n", + " }\n", + " free(symbols);\n", + " }\n", + " std::cerr << buffer.str();\n", + " std::exit(EXIT_FAILURE);\n", + " }\n" + ] + }, + { + "cell_type": "code", + "execution_count": 7, + "metadata": {}, + "outputs": [ { - "cell_type": "heading", - "level": 2, - "metadata": {}, - "source": [ - "Quicksort CPP" + "name": "stdout", + "output_type": "stream", + "text": [ + "Overwriting src/pisum_opt.cpp\n" ] - }, - { - "cell_type": "code", - "collapsed": false, - "input": [ - "%%file src/qsort_kernel.cpp\n", - "#define PY_ARRAY_UNIQUE_SYMBOL qsort_kernel_ARRAY_API\n", - "#define NPY_NO_DEPRECATED_API NPY_1_7_API_VERSION\n", - "#include \n", - "#include \n", - "#include \n", - "#include \n", - "#include \n", - "#include \n", - "#include \n", - "#include \n", - "#include \n", - "#include \n", - "#include \n", - "struct PyObj {\n", - "\ttypedef PyObject * ptr_t;\n", - "\ttypedef PyArrayObject * arrptr_t;\n", - "\tPyObj(): dec(false), ptr(NULL) {}\n", - "\tPyObj(ptr_t p): dec(false), ptr(p) {}\n", - "\t~PyObj() { if(dec) Py_DECREF(ptr); }\n", - "\tPyObj & operator=(ptr_t p) { if(dec) Py_DECREF(ptr); ptr = p; dec = false; return *this; }\n", - "\tPyObj & incref(ptr_t p) { if(dec) Py_DECREF(ptr); ptr = p; dec = (p != NULL); return *this; }\n", - "\toperator bool() const { return ptr; }\n", - "\toperator ptr_t() const { return ptr; }\n", - "\toperator arrptr_t() const { return (arrptr_t)ptr; }\n", - "\tbool dec;\n", - "\tptr_t ptr;\n", - "};\n", - "inline std::tuple qsort_kernel_d1JJ(PyObject * pa, \n", - "\t \t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tnpy_intp const * __restrict__ sa, \n", - "\t \t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tnpy_double * __restrict__ ca, \n", - "\t \t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tnpy_int64 clo, \n", - "\t \t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tnpy_int64 chi);\n", - "\t \t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\n", - "inline std::tuple qsort_kernel_d1JJ(PyObject * pa, \n", - "\t \t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tnpy_intp const * __restrict__ sa, \n", - "\t \t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tnpy_double * __restrict__ ca, \n", - "\t \t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tnpy_int64 clo, \n", - "\t \t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tnpy_int64 chi){\n", - "\tnpy_int64 ci = clo;\n", - "\tnpy_int64 cj = chi;\n", - "\tnpy_double cpivot;\n", - "\n", - "\twhile (ci < chi) {\n", - "\t\tcpivot = ca[(int)((clo + chi) / 2)];\n", - "\t\t\n", - "\t\twhile (ci <= cj) {\n", - "\t\t\twhile (ca[ci] < cpivot) {\n", - "\t\t\t\tci += 1;\n", - "\t\t\t}\n", - "\t\t\t\n", - "\t\t\twhile (ca[cj] > cpivot) {\n", - "\t\t\t\tcj -= 1;\n", - "\t\t\t}\n", - "\t\t\t\n", - "\t\t\tif (ci <= cj) {\n", - "\t\t\t\tauto ctmp = ca[ci];\n", - "\t\t\t\tca[ci] = ca[cj];\n", - "\t\t\t\tca[cj] = ctmp;\n", - "\t\t\t\tci += 1;\n", - "\t\t\t\tcj -= 1;\n", - "\t\t\t}\n", - "\t\t}\n", - "\t\t\n", - "\t\tif (clo < cj) {\n", - "\t\t\tqsort_kernel_d1JJ(pa, sa, ca, clo, cj);\n", - "\t\t}\n", - "\t\t\n", - "\t\tclo = ci;\n", - "\t\tcj = chi;\n", - "\t}\n", - "\t\n", - "\treturn std::make_tuple((PyObject *)pa, sa, ca);\n", - "}\n", - "\n", - "void sighandler(int sig);\n", - "#include \n", - "extern \"C\" {\n", - "\tPyObject * create_signature;\n", - "\tstruct sigaction slot;\n", - "\tPyObject * set_create_signature(PyObject * self, PyObject * args) {\n", - "\t\tif (!PyArg_ParseTuple(args, \"O\", &create_signature)) {\n", - "\t\t\tPyErr_SetString(PyExc_ValueError, \"Invalid Argument to set_create_signature!\");\n", - "\t\t\treturn NULL;\n", - "\t\t}\n", - "\t\tPy_INCREF(create_signature);\n", - "\t\tmemset(&slot, 0, sizeof(slot));\n", - "\t\tslot.sa_handler = &sighandler;\n", - "\t\tsigaction(SIGSEGV, &slot, NULL);\n", - "\t\tsigaction(SIGBUS, &slot, NULL);\n", - "\t\tPy_INCREF(Py_None);\n", - "\t\treturn Py_None;\n", - "\t}\n", - "\tPyObject * run(PyObject * self, PyObject * args) {\n", - "\t\t{\n", - "\t\t\tPyObj pa;\n", - "\t\t\tPyObject * plo; npy_int64 clo;\n", - "\t\t\tPyObject * phi; npy_int64 chi;\n", - "\t\t\tif (\n", - "\t\t\t\tPyTuple_CheckExact(args) and PyTuple_GET_SIZE(args) == 3\n", - "\t\t\t\tand (pa = PyTuple_GET_ITEM(args, 0)) and PyArray_CheckExact(pa)\n", - "\t\t\t\tand PyArray_TYPE((PyArrayObject *)pa) == NPY_FLOAT64 and PyArray_NDIM((PyArrayObject *)pa) == 1\n", - "\t\t\t\tand (plo = PyTuple_GET_ITEM(args, 1)) and PyInt_CheckExact(plo)\n", - "\t\t\t\tand (phi = PyTuple_GET_ITEM(args, 2)) and PyInt_CheckExact(phi)\n", - "\t\t\t) {\n", - "\t\t\t\tif (!(pa.incref((PyObject *)PyArray_GETCONTIGUOUS((PyArrayObject *)pa)))) {\n", - "\t\t\t\t\tPyErr_SetString(PyExc_ValueError, \"Invalid Argument type on a!\");\n", - "\t\t\t\t\treturn NULL;\n", - "\t\t\t\t}\n", - "\t\t\t\tclo = PyInt_AS_LONG(plo);\n", - "\t\t\t\tchi = PyInt_AS_LONG(phi);\n", - "\t\t\t\ttry {\n", - "\t\t\t\t\tPyObject * res = std::get<0>(qsort_kernel_d1JJ(\n", - "\t\t\t\t\t\t pa, PyArray_SHAPE((PyArrayObject *)pa), (npy_double *)PyArray_DATA((PyArrayObject *)pa)\n", - "\t\t\t\t\t\t, clo\n", - "\t\t\t\t\t\t, chi\n", - "\t\t\t\t\t));\n", - "\n", - "\t\t\t\t\tPy_INCREF(res);\n", - "\t\t\t\t\treturn res;\n", - "\t\t\t\t} catch (...) {\n", - "\t\t\t\t\treturn NULL;\n", - "\t\t\t\t}\n", - "\t\t\t} else\n", - "\t\t\t\tPyErr_Clear();\n", - "\t\t}\n", - "\t\tPyObject * signatures = Py_BuildValue(\"(sO)\", \"(lp0\\n(lp1\\nccopy_reg\\n_reconstructor\\np2\\n(chope._ast\\nVariable\\np3\\nc__builtin__\\nobject\\np4\\nNtp5\\nRp6\\n(dp7\\nS'name'\\np8\\nS'a'\\np9\\nsS'dtype'\\np10\\nS'float64'\\np11\\nsS'dims'\\np12\\nI1\\nsbag2\\n(g3\\ng4\\nNtp13\\nRp14\\n(dp15\\ng8\\nS'lo'\\np16\\nsg10\\nc__builtin__\\nint\\np17\\nsg12\\nI0\\nsbag2\\n(g3\\ng4\\nNtp18\\nRp19\\n(dp20\\ng8\\nS'hi'\\np21\\nsg10\\ng17\\nsg12\\nI0\\nsbaa.\", args);\n", - "\t\tif (!signatures) {\n", - "\t\t\tPyErr_SetString(PyExc_ValueError, \"Error building signature string for qsort_kernel\");\n", - "\t\t\treturn NULL;\n", - "\t\t}\n", - "\t\treturn PyObject_Call(create_signature, signatures, NULL);\n", - "\t}\n", - "\tPyMethodDef qsort_kernelMethods[] = {\n", - "\t\t{ \"set_create_signature\", (PyCFunction)set_create_signature, METH_VARARGS },\n", - "\t\t{ \"run\", (PyCFunction)run, METH_VARARGS },\n", - "\t\t{ NULL, NULL }\n", - "\t};\n", - "\tPyMODINIT_FUNC initqsort_kernel(void) {\n", - "\t\timport_array();\n", - "\t\tPyImport_ImportModule(\"numpy\");\n", - "\t\t(void)Py_InitModule(\"qsort_kernel\", qsort_kernelMethods);\n", - "\t}\n", - "}\n", - "#include \n", - "#include \n", - "#include \n", - "#include \n", - "#include \n", - "void sighandler(int sig) {\n", - "\tstd::ostringstream buffer;\n", - "\tbuffer << \"Abort by \" << (sig == SIGSEGV ? \"segfault\" : \"bus error\") << std::endl;\n", - "\tvoid * stack[64];\n", - "\tstd::size_t depth = backtrace(stack, 64);\n", - "\tif (!depth)\n", - "\t\tbuffer << \" \" << std::endl;\n", - "\telse {\n", - "\t\tchar ** symbols = backtrace_symbols(stack, depth);\n", - "\t\tfor (std::size_t i = 1; i < depth; ++i) {\n", - "\t\t\tstd::string symbol = symbols[i];\n", - "\t\t\t\tif (symbol.find_first_of(' ', 59) != std::string::npos) {\n", - "\t\t\t\t\tstd::string name = symbol.substr(59, symbol.find_first_of(' ', 59) - 59);\n", - "\t\t\t\t\tint status;\n", - "\t\t\t\t\tchar * demangled = abi::__cxa_demangle(name.c_str(), NULL, NULL, &status);\n", - "\t\t\t\t\tif (!status) {\n", - "\t\t\t\t\t\tbuffer << \" \" \n", - "\t\t\t\t\t\t\t<< symbol.substr(0, 59) \n", - "\t\t\t\t\t\t\t<< demangled\n", - "\t\t\t\t\t\t\t<< symbol.substr(59 + name.size())\n", - "\t\t\t\t\t\t\t<< std::endl;\n", - "\t\t\t\t\t\tfree(demangled);\n", - "\t\t\t\t\t} else\n", - "\t\t\t\t\t\tbuffer << \" \" << symbol << std::endl;\n", - "\t\t\t\t} else\n", - "\t\t\t\t\tbuffer << \" \" << symbol << std::endl;\n", - "\t\t\t}\n", - "\t\t\tfree(symbols);\n", - "\t\t}\n", - "\t\tstd::cerr << buffer.str();\n", - "\t\tstd::exit(EXIT_FAILURE);\n", - "\t}\n" - ], - "language": "python", - "metadata": {}, - "outputs": [ - { - "output_type": "stream", - "stream": "stdout", - "text": [ - "Overwriting src/qsort_kernel.cpp\n" - ] - } - ], - "prompt_number": 3 - }, + } + ], + "source": [ + "%%file src/pisum_opt.cpp\n", + "#define PY_ARRAY_UNIQUE_SYMBOL pisum_opt_ARRAY_API\n", + "#define NPY_NO_DEPRECATED_API NPY_1_7_API_VERSION\n", + "#include \n", + "#include \n", + "#include \n", + "#include \n", + "#include \n", + "#include \n", + "#include \n", + "#include \n", + "#include \n", + "#include \n", + "#include \n", + "struct PyObj {\n", + " typedef PyObject * ptr_t;\n", + " typedef PyArrayObject * arrptr_t;\n", + " PyObj(): dec(false), ptr(NULL) {}\n", + " PyObj(ptr_t p): dec(false), ptr(p) {}\n", + " ~PyObj() { if(dec) Py_DECREF(ptr); }\n", + " PyObj & operator=(ptr_t p) { if(dec) Py_DECREF(ptr); ptr = p; dec = false; return *this; }\n", + " PyObj & incref(ptr_t p) { if(dec) Py_DECREF(ptr); ptr = p; dec = (p != NULL); return *this; }\n", + " operator bool() const { return ptr; }\n", + " operator ptr_t() const { return ptr; }\n", + " operator arrptr_t() const { return (arrptr_t)ptr; }\n", + " bool dec;\n", + " ptr_t ptr;\n", + "};\n", + "\n", + "inline npy_double pisum_opt_();\n", + "\n", + "inline npy_double pisum_opt_() {\n", + " npy_double csum = npy_double();\n", + " npy_intp ck = 1;\n", + " npy_double cf = 0.0;\n", + "\n", + " for (npy_intp cj = 1; cj < 501; ++cj) {\n", + " csum = 0.0;\n", + " cf = 0.0;\n", + " for (ck = 1; ck < 10001; ++ck) {\n", + " cf += 1.0;\n", + " auto c__sp0 = (cf * cf);\n", + " csum += (1.0 / c__sp0);\n", + " }\n", + " }\n", + " return csum;\n", + "}\n", + "\n", + "void sighandler(int sig);\n", + "#include \n", + "extern \"C\" {\n", + " PyObject * create_signature;\n", + " struct sigaction slot;\n", + " PyObject * set_create_signature(PyObject * self, PyObject * args) {\n", + " if (!PyArg_ParseTuple(args, \"O\", &create_signature)) {\n", + " PyErr_SetString(PyExc_ValueError, \"Invalid Argument to set_create_signature!\");\n", + " return NULL;\n", + " }\n", + " Py_INCREF(create_signature);\n", + " memset(&slot, 0, sizeof(slot));\n", + " slot.sa_handler = &sighandler;\n", + " sigaction(SIGSEGV, &slot, NULL);\n", + " sigaction(SIGBUS, &slot, NULL);\n", + " Py_INCREF(Py_None);\n", + " return Py_None;\n", + " }\n", + " PyObject * run(PyObject * self, PyObject * args) {\n", + " { try {\n", + " return Py_BuildValue(\"d\", pisum_opt_());\n", + " } catch (...) {\n", + " return NULL;\n", + " }\n", + " }\n", + " PyObject * signatures = Py_BuildValue(\"(sO)\", \"(lp0\\n(lp1\\na.\", args);\n", + " if (!signatures) {\n", + " PyErr_SetString(PyExc_ValueError, \"Error building signature string for pisum_opt\");\n", + " return NULL;\n", + " }\n", + " return PyObject_Call(create_signature, signatures, NULL);\n", + " }\n", + "\n", + " PyMethodDef pisum_optMethods[] = {\n", + " { \"set_create_signature\", set_create_signature, METH_VARARGS, \"signal handler\" },\n", + " { \"run\", run, METH_VARARGS, \"module function\" },\n", + " { NULL, NULL, 0, NULL }\n", + " };\n", + "\n", + " static struct PyModuleDef pisum_opt_module = {\n", + " PyModuleDef_HEAD_INIT,\n", + " \"pisum_opt\",\n", + " NULL,\n", + " -1,\n", + " pisum_optMethods\n", + " };\n", + "\n", + " PyMODINIT_FUNC PyInit_pisum_opt(void) {\n", + " import_array();\n", + " PyImport_ImportModule(\"numpy\");\n", + " return PyModule_Create(&pisum_opt_module);\n", + " }\n", + "}\n", + "\n", + "#include \n", + "#include \n", + "#include \n", + "#include \n", + "#include \n", + "void sighandler(int sig) {\n", + " std::ostringstream buffer;\n", + " buffer << \"Abort by \" << (sig == SIGSEGV ? \"segfault\" : \"bus error\") << std::endl;\n", + " void * stack[64];\n", + " std::size_t depth = backtrace(stack, 64);\n", + " if (!depth)\n", + " buffer << \" \" << std::endl;\n", + " else {\n", + " char ** symbols = backtrace_symbols(stack, depth);\n", + " for (std::size_t i = 1; i < depth; ++i) {\n", + " std::string symbol = symbols[i];\n", + " if (symbol.find_first_of(' ', 59) != std::string::npos) {\n", + " std::string name = symbol.substr(59, symbol.find_first_of(' ', 59) - 59);\n", + " int status;\n", + " char * demangled = abi::__cxa_demangle(name.c_str(), NULL, NULL, &status);\n", + " if (!status) {\n", + " buffer << \" \"\n", + " << symbol.substr(0, 59)\n", + " << demangled\n", + " << symbol.substr(59 + name.size())\n", + " << std::endl;\n", + " free(demangled);\n", + " } else\n", + " buffer << \" \" << symbol << std::endl;\n", + " } else\n", + " buffer << \" \" << symbol << std::endl;\n", + " }\n", + " free(symbols);\n", + " }\n", + " std::cerr << buffer.str();\n", + " std::exit(EXIT_FAILURE);\n", + " }\n" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# 10th order poly log approx CPP" + ] + }, + { + "cell_type": "code", + "execution_count": 8, + "metadata": {}, + "outputs": [ { - "cell_type": "heading", - "level": 2, - "metadata": {}, - "source": [ - "Pi sum CPP" + "name": "stdout", + "output_type": "stream", + "text": [ + "Overwriting src/ln.cpp\n" ] - }, - { - "cell_type": "code", - "collapsed": false, - "input": [ - "%%file src/pisum.cpp\n", - "#define PY_ARRAY_UNIQUE_SYMBOL pisum_ARRAY_API\n", - "#define NPY_NO_DEPRECATED_API NPY_1_7_API_VERSION\n", - "#include \n", - "#include \n", - "#include \n", - "#include \n", - "#include \n", - "#include \n", - "#include \n", - "#include \n", - "#include \n", - "#include \n", - "#include \n", - "struct PyObj {\n", - "\ttypedef PyObject * ptr_t;\n", - "\ttypedef PyArrayObject * arrptr_t;\n", - "\tPyObj(): dec(false), ptr(NULL) {}\n", - "\tPyObj(ptr_t p): dec(false), ptr(p) {}\n", - "\t~PyObj() { if(dec) Py_DECREF(ptr); }\n", - "\tPyObj & operator=(ptr_t p) { if(dec) Py_DECREF(ptr); ptr = p; dec = false; return *this; }\n", - "\tPyObj & incref(ptr_t p) { if(dec) Py_DECREF(ptr); ptr = p; dec = (p != NULL); return *this; }\n", - "\toperator bool() const { return ptr; }\n", - "\toperator ptr_t() const { return ptr; }\n", - "\toperator arrptr_t() const { return (arrptr_t)ptr; }\n", - "\tbool dec;\n", - "\tptr_t ptr;\n", - "};\n", - "\n", - "inline npy_double pisum_();\n", - "\n", - "inline npy_double pisum_() {\n", - "\tnpy_double csum = npy_double();\n", - "\tnpy_intp ck = 1;\n", - "\tfor (npy_intp cj = 1; cj < 501; ++cj) {\n", - "\t\tcsum = 0.0;\n", - "\t\tauto cf = 0.0;\n", - "\t\tfor (ck = 1; ck < 10001; ++ck) {\n", - "\t\t\tauto c__sp0 = (ck * ck);\n", - "\t\t\tcsum += (1.0 / c__sp0);\n", - "\t\t}\n", - "\t}\n", - "\t\n", - "\treturn csum;\n", - "}\n", - "\n", - "void sighandler(int sig);\n", - "#include \n", - "extern \"C\" {\n", - "\tPyObject * create_signature;\n", - "\tstruct sigaction slot;\n", - "\tPyObject * set_create_signature(PyObject * self, PyObject * args) {\n", - "\t\tif (!PyArg_ParseTuple(args, \"O\", &create_signature)) {\n", - "\t\t\tPyErr_SetString(PyExc_ValueError, \"Invalid Argument to set_create_signature!\");\n", - "\t\t\treturn NULL;\n", - "\t\t}\n", - "\t\tPy_INCREF(create_signature);\n", - "\t\tmemset(&slot, 0, sizeof(slot));\n", - "\t\tslot.sa_handler = &sighandler;\n", - "\t\tsigaction(SIGSEGV, &slot, NULL);\n", - "\t\tsigaction(SIGBUS, &slot, NULL);\n", - "\t\tPy_INCREF(Py_None);\n", - "\t\treturn Py_None;\n", - "\t}\n", - "\tPyObject * run(PyObject * self, PyObject * args) {\n", - "\t\t{\t\t\t\ttry {\n", - "\t\t\t\t\treturn Py_BuildValue(\"d\", pisum_());\n", - "\t\t\t\t} catch (...) {\n", - "\t\t\t\t\treturn NULL;\n", - "\t\t\t\t}\n", - "\t\t}\n", - "\t\tPyObject * signatures = Py_BuildValue(\"(sO)\", \"(lp0\\n(lp1\\na.\", args);\n", - "\t\tif (!signatures) {\n", - "\t\t\tPyErr_SetString(PyExc_ValueError, \"Error building signature string for pisum\");\n", - "\t\t\treturn NULL;\n", - "\t\t}\n", - "\t\treturn PyObject_Call(create_signature, signatures, NULL);\n", - "\t}\n", - "\tPyMethodDef pisumMethods[] = {\n", - "\t\t{ \"set_create_signature\", (PyCFunction)set_create_signature, METH_VARARGS },\n", - "\t\t{ \"run\", (PyCFunction)run, METH_VARARGS },\n", - "\t\t{ NULL, NULL }\n", - "\t};\n", - "\tPyMODINIT_FUNC initpisum(void) {\n", - "\t\timport_array();\n", - "\t\tPyImport_ImportModule(\"numpy\");\n", - "\t\t(void)Py_InitModule(\"pisum\", pisumMethods);\n", - "\t}\n", - "}\n", - "#include \n", - "#include \n", - "#include \n", - "#include \n", - "#include \n", - "void sighandler(int sig) {\n", - "\tstd::ostringstream buffer;\n", - "\tbuffer << \"Abort by \" << (sig == SIGSEGV ? \"segfault\" : \"bus error\") << std::endl;\n", - "\tvoid * stack[64];\n", - "\tstd::size_t depth = backtrace(stack, 64);\n", - "\tif (!depth)\n", - "\t\tbuffer << \" \" << std::endl;\n", - "\telse {\n", - "\t\tchar ** symbols = backtrace_symbols(stack, depth);\n", - "\t\tfor (std::size_t i = 1; i < depth; ++i) {\n", - "\t\t\tstd::string symbol = symbols[i];\n", - "\t\t\t\tif (symbol.find_first_of(' ', 59) != std::string::npos) {\n", - "\t\t\t\t\tstd::string name = symbol.substr(59, symbol.find_first_of(' ', 59) - 59);\n", - "\t\t\t\t\tint status;\n", - "\t\t\t\t\tchar * demangled = abi::__cxa_demangle(name.c_str(), NULL, NULL, &status);\n", - "\t\t\t\t\tif (!status) {\n", - "\t\t\t\t\t\tbuffer << \" \" \n", - "\t\t\t\t\t\t\t<< symbol.substr(0, 59) \n", - "\t\t\t\t\t\t\t<< demangled\n", - "\t\t\t\t\t\t\t<< symbol.substr(59 + name.size())\n", - "\t\t\t\t\t\t\t<< std::endl;\n", - "\t\t\t\t\t\tfree(demangled);\n", - "\t\t\t\t\t} else\n", - "\t\t\t\t\t\tbuffer << \" \" << symbol << std::endl;\n", - "\t\t\t\t} else\n", - "\t\t\t\t\tbuffer << \" \" << symbol << std::endl;\n", - "\t\t\t}\n", - "\t\t\tfree(symbols);\n", - "\t\t}\n", - "\t\tstd::cerr << buffer.str();\n", - "\t\tstd::exit(EXIT_FAILURE);\n", - "\t}\n" - ], - "language": "python", - "metadata": {}, - "outputs": [ - { - "output_type": "stream", - "stream": "stdout", - "text": [ - "Overwriting src/pisum.cpp\n" - ] - } - ], - "prompt_number": 4 - }, - { - "cell_type": "code", - "collapsed": false, - "input": [ - "%%file src/pisum_opt.cpp\n", - "#define PY_ARRAY_UNIQUE_SYMBOL pisum_opt_ARRAY_API\n", - "#define NPY_NO_DEPRECATED_API NPY_1_7_API_VERSION\n", - "#include \n", - "#include \n", - "#include \n", - "#include \n", - "#include \n", - "#include \n", - "#include \n", - "#include \n", - "#include \n", - "#include \n", - "#include \n", - "struct PyObj {\n", - "\ttypedef PyObject * ptr_t;\n", - "\ttypedef PyArrayObject * arrptr_t;\n", - "\tPyObj(): dec(false), ptr(NULL) {}\n", - "\tPyObj(ptr_t p): dec(false), ptr(p) {}\n", - "\t~PyObj() { if(dec) Py_DECREF(ptr); }\n", - "\tPyObj & operator=(ptr_t p) { if(dec) Py_DECREF(ptr); ptr = p; dec = false; return *this; }\n", - "\tPyObj & incref(ptr_t p) { if(dec) Py_DECREF(ptr); ptr = p; dec = (p != NULL); return *this; }\n", - "\toperator bool() const { return ptr; }\n", - "\toperator ptr_t() const { return ptr; }\n", - "\toperator arrptr_t() const { return (arrptr_t)ptr; }\n", - "\tbool dec;\n", - "\tptr_t ptr;\n", - "};\n", - "\n", - "inline npy_double pisum_opt_();\n", - "\n", - "inline npy_double pisum_opt_() {\n", - "\tnpy_double csum = npy_double();\n", - "\tnpy_intp ck = 1;\n", - "\tnpy_double cf = 0.0;\n", - "\t\n", - "\tfor (npy_intp cj = 1; cj < 501; ++cj) {\n", - "\t\tcsum = 0.0;\n", - "\t\tcf = 0.0;\n", - "\t\tfor (ck = 1; ck < 10001; ++ck) {\n", - "\t\t\tcf += 1.0;\n", - "\t\t\tauto c__sp0 = (cf * cf);\n", - "\t\t\tcsum += (1.0 / c__sp0);\n", - "\t\t}\n", - "\t}\n", - "\treturn csum;\n", - "}\n", - "\n", - "void sighandler(int sig);\n", - "#include \n", - "extern \"C\" {\n", - "\tPyObject * create_signature;\n", - "\tstruct sigaction slot;\n", - "\tPyObject * set_create_signature(PyObject * self, PyObject * args) {\n", - "\t\tif (!PyArg_ParseTuple(args, \"O\", &create_signature)) {\n", - "\t\t\tPyErr_SetString(PyExc_ValueError, \"Invalid Argument to set_create_signature!\");\n", - "\t\t\treturn NULL;\n", - "\t\t}\n", - "\t\tPy_INCREF(create_signature);\n", - "\t\tmemset(&slot, 0, sizeof(slot));\n", - "\t\tslot.sa_handler = &sighandler;\n", - "\t\tsigaction(SIGSEGV, &slot, NULL);\n", - "\t\tsigaction(SIGBUS, &slot, NULL);\n", - "\t\tPy_INCREF(Py_None);\n", - "\t\treturn Py_None;\n", - "\t}\n", - "\tPyObject * run(PyObject * self, PyObject * args) {\n", - "\t\t{\t\t\t\ttry {\n", - "\t\t\t\t\treturn Py_BuildValue(\"d\", pisum_opt_());\n", - "\t\t\t\t} catch (...) {\n", - "\t\t\t\t\treturn NULL;\n", - "\t\t\t\t}\n", - "\t\t}\n", - "\t\tPyObject * signatures = Py_BuildValue(\"(sO)\", \"(lp0\\n(lp1\\na.\", args);\n", - "\t\tif (!signatures) {\n", - "\t\t\tPyErr_SetString(PyExc_ValueError, \"Error building signature string for pisum_opt\");\n", - "\t\t\treturn NULL;\n", - "\t\t}\n", - "\t\treturn PyObject_Call(create_signature, signatures, NULL);\n", - "\t}\n", - "\tPyMethodDef pisum_optMethods[] = {\n", - "\t\t{ \"set_create_signature\", (PyCFunction)set_create_signature, METH_VARARGS },\n", - "\t\t{ \"run\", (PyCFunction)run, METH_VARARGS },\n", - "\t\t{ NULL, NULL }\n", - "\t};\n", - "\tPyMODINIT_FUNC initpisum_opt(void) {\n", - "\t\timport_array();\n", - "\t\tPyImport_ImportModule(\"numpy\");\n", - "\t\t(void)Py_InitModule(\"pisum_opt\", pisum_optMethods);\n", - "\t}\n", - "}\n", - "#include \n", - "#include \n", - "#include \n", - "#include \n", - "#include \n", - "void sighandler(int sig) {\n", - "\tstd::ostringstream buffer;\n", - "\tbuffer << \"Abort by \" << (sig == SIGSEGV ? \"segfault\" : \"bus error\") << std::endl;\n", - "\tvoid * stack[64];\n", - "\tstd::size_t depth = backtrace(stack, 64);\n", - "\tif (!depth)\n", - "\t\tbuffer << \" \" << std::endl;\n", - "\telse {\n", - "\t\tchar ** symbols = backtrace_symbols(stack, depth);\n", - "\t\tfor (std::size_t i = 1; i < depth; ++i) {\n", - "\t\t\tstd::string symbol = symbols[i];\n", - "\t\t\t\tif (symbol.find_first_of(' ', 59) != std::string::npos) {\n", - "\t\t\t\t\tstd::string name = symbol.substr(59, symbol.find_first_of(' ', 59) - 59);\n", - "\t\t\t\t\tint status;\n", - "\t\t\t\t\tchar * demangled = abi::__cxa_demangle(name.c_str(), NULL, NULL, &status);\n", - "\t\t\t\t\tif (!status) {\n", - "\t\t\t\t\t\tbuffer << \" \" \n", - "\t\t\t\t\t\t\t<< symbol.substr(0, 59) \n", - "\t\t\t\t\t\t\t<< demangled\n", - "\t\t\t\t\t\t\t<< symbol.substr(59 + name.size())\n", - "\t\t\t\t\t\t\t<< std::endl;\n", - "\t\t\t\t\t\tfree(demangled);\n", - "\t\t\t\t\t} else\n", - "\t\t\t\t\t\tbuffer << \" \" << symbol << std::endl;\n", - "\t\t\t\t} else\n", - "\t\t\t\t\tbuffer << \" \" << symbol << std::endl;\n", - "\t\t\t}\n", - "\t\t\tfree(symbols);\n", - "\t\t}\n", - "\t\tstd::cerr << buffer.str();\n", - "\t\tstd::exit(EXIT_FAILURE);\n", - "\t}\n" - ], - "language": "python", - "metadata": {}, - "outputs": [ - { - "output_type": "stream", - "stream": "stdout", - "text": [ - "Overwriting src/pisum_opt.cpp\n" - ] - } - ], - "prompt_number": 5 - }, + } + ], + "source": [ + "%%file src/ln.cpp\n", + "#define PY_ARRAY_UNIQUE_SYMBOL ln_hope_ARRAY_API\n", + "#define NPY_NO_DEPRECATED_API NPY_1_7_API_VERSION\n", + "#include \n", + "#include \n", + "#include \n", + "#include \n", + "#include \n", + "#include \n", + "#include \n", + "#include \n", + "#include \n", + "#include \n", + "#include \n", + "struct PyObj {\n", + "\ttypedef PyObject * ptr_t;\n", + "\ttypedef PyArrayObject * arrptr_t;\n", + "\tPyObj(): dec(false), ptr(NULL) {}\n", + "\tPyObj(ptr_t p): dec(false), ptr(p) {}\n", + "\t~PyObj() { if(dec) Py_DECREF(ptr); }\n", + "\tPyObj & operator=(ptr_t p) { if(dec) Py_DECREF(ptr); ptr = p; dec = false; return *this; }\n", + "\tPyObj & incref(ptr_t p) { if(dec) Py_DECREF(ptr); ptr = p; dec = (p != NULL); return *this; }\n", + "\toperator bool() const { return ptr; }\n", + "\toperator ptr_t() const { return ptr; }\n", + "\toperator arrptr_t() const { return (arrptr_t)ptr; }\n", + "\tbool dec;\n", + "\tptr_t ptr;\n", + "};\n", + "\n", + "inline void ln_hope_d1d1(PyObject * pX, npy_intp const * __restrict__ sX, npy_double * __restrict__ cX, \n", + "\t\t\t\t\t\t\t\t\t\t\t\t\tPyObject * pY, npy_intp const * __restrict__ sY, npy_double * __restrict__ cY);\n", + "\n", + "inline void ln_hope_d1d1(PyObject * pX, npy_intp const * __restrict__ sX, npy_double * __restrict__ cX, \n", + "\t\t\t\t\t\t\t\t\t\t\t\t\tPyObject * pY, npy_intp const * __restrict__ sY, npy_double * __restrict__ cY){\n", + "\t\n", + "\tfor (npy_intp i0 = 0; i0 < sY[0] - 0; ++i0) {\n", + "\t\tcY[(int)(i0)] = (cX[i0] - 1) - (std::pow((cX[i0] - 1), 2) / 2) + (std::pow((cX[i0] - 1), 3) / 3) - (std::pow((cX[i0] - 1), 4) / 4) + (std::pow((cX[i0] - 1), 5) / 5) - (std::pow((cX[i0] - 1), 6) / 6) + (std::pow((cX[i0] - 1), 7) / 7) - (std::pow((cX[i0] - 1), 8) / 8) + (std::pow((cX[i0] - 1), 9) / 9);\n", + "\t}\n", + "}\n", + "\n", + "void sighandler(int sig);\n", + "#include \n", + "extern \"C\" {\n", + "\tPyObject * create_signature;\n", + "\tstruct sigaction slot;\n", + "\tPyObject * set_create_signature(PyObject * self, PyObject * args) {\n", + "\t\tif (!PyArg_ParseTuple(args, \"O\", &create_signature)) {\n", + "\t\t\tPyErr_SetString(PyExc_ValueError, \"Invalid Argument to set_create_signature!\");\n", + "\t\t\treturn NULL;\n", + "\t\t}\n", + "\t\tPy_INCREF(create_signature);\n", + "\t\tmemset(&slot, 0, sizeof(slot));\n", + "\t\tslot.sa_handler = &sighandler;\n", + "\t\tsigaction(SIGSEGV, &slot, NULL);\n", + "\t\tsigaction(SIGBUS, &slot, NULL);\n", + "\t\tPy_INCREF(Py_None);\n", + "\t\treturn Py_None;\n", + "\t}\n", + "\tPyObject * run(PyObject * self, PyObject * args) {\n", + "\t\t{\n", + "\t\t\tPyObj pX;\n", + "\t\t\tPyObj pY;\n", + "\t\t\tif (\n", + "\t\t\t\tPyTuple_CheckExact(args) and PyTuple_GET_SIZE(args) == 2\n", + "\t\t\t\tand (pX = PyTuple_GET_ITEM(args, 0)) and PyArray_CheckExact(pX)\n", + "\t\t\t\tand PyArray_TYPE((PyArrayObject *)pX) == NPY_FLOAT64 and PyArray_NDIM((PyArrayObject *)pX) == 1\n", + "\t\t\t\tand (pY = PyTuple_GET_ITEM(args, 1)) and PyArray_CheckExact(pY)\n", + "\t\t\t\tand PyArray_TYPE((PyArrayObject *)pY) == NPY_FLOAT64 and PyArray_NDIM((PyArrayObject *)pY) == 1\n", + "\t\t\t) {\n", + "\t\t\t\tif (!(pX.incref((PyObject *)PyArray_GETCONTIGUOUS((PyArrayObject *)pX)))) {\n", + "\t\t\t\t\tPyErr_SetString(PyExc_ValueError, \"Invalid Argument type on X!\");\n", + "\t\t\t\t\treturn NULL;\n", + "\t\t\t\t}\n", + "\t\t\t\tif (!(pY.incref((PyObject *)PyArray_GETCONTIGUOUS((PyArrayObject *)pY)))) {\n", + "\t\t\t\t\tPyErr_SetString(PyExc_ValueError, \"Invalid Argument type on Y!\");\n", + "\t\t\t\t\treturn NULL;\n", + "\t\t\t\t}\n", + "\t\t\t\ttry {\n", + "\t\t\t\t\tln_hope_d1d1(\n", + "\t\t\t\t\t\t pX, PyArray_SHAPE((PyArrayObject *)pX), (npy_double *)PyArray_DATA((PyArrayObject *)pX)\n", + "\t\t\t\t\t\t, pY, PyArray_SHAPE((PyArrayObject *)pY), (npy_double *)PyArray_DATA((PyArrayObject *)pY)\n", + "\t\t\t\t\t);\n", + "\t\t\t\t\tPy_INCREF(Py_None);\n", + "\t\t\t\t\treturn Py_None;\n", + "\t\t\t\t} catch (...) {\n", + "\t\t\t\t\treturn NULL;\n", + "\t\t\t\t}\n", + "\t\t\t} else\n", + "\t\t\t\tPyErr_Clear();\n", + "\t\t}\n", + "\t\tPyObject * signatures = Py_BuildValue(\"(sO)\", \"(lp0\\n(lp1\\nccopy_reg\\n_reconstructor\\np2\\n(chope._ast\\nVariable\\np3\\nc__builtin__\\nobject\\np4\\nNtp5\\nRp6\\n(dp7\\nS'name'\\np8\\nS'X'\\np9\\nsS'dtype'\\np10\\nS'float64'\\np11\\nsS'dims'\\np12\\nI1\\nsbag2\\n(g3\\ng4\\nNtp13\\nRp14\\n(dp15\\ng8\\nS'Y'\\np16\\nsg10\\ng11\\nsg12\\nI1\\nsbaa.\", args);\n", + "\t\tif (!signatures) {\n", + "\t\t\tPyErr_SetString(PyExc_ValueError, \"Error building signature string for ln_hope\");\n", + "\t\t\treturn NULL;\n", + "\t\t}\n", + "\t\treturn PyObject_Call(create_signature, signatures, NULL);\n", + "\t}\n", + "\tPyMethodDef ln_hopeMethods[] = {\n", + "\t\t{ \"set_create_signature\", (PyCFunction)set_create_signature, METH_VARARGS },\n", + "\t\t{ \"run\", (PyCFunction)run, METH_VARARGS },\n", + "\t\t{ NULL, NULL }\n", + "\t};\n", + "\tPyMODINIT_FUNC initln(void) {\n", + "\t\timport_array();\n", + "\t\tPyImport_ImportModule(\"numpy\");\n", + "\t\t(void)Py_InitModule(\"ln\", ln_hopeMethods);\n", + "\t}\n", + "}\n", + "#include \n", + "#include \n", + "#include \n", + "#include \n", + "#include \n", + "void sighandler(int sig) {\n", + "\tstd::ostringstream buffer;\n", + "\tbuffer << \"Abort by \" << (sig == SIGSEGV ? \"segfault\" : \"bus error\") << std::endl;\n", + "\tvoid * stack[64];\n", + "\tstd::size_t depth = backtrace(stack, 64);\n", + "\tif (!depth)\n", + "\t\tbuffer << \" \" << std::endl;\n", + "\telse {\n", + "\t\tchar ** symbols = backtrace_symbols(stack, depth);\n", + "\t\tfor (std::size_t i = 1; i < depth; ++i) {\n", + "\t\t\tstd::string symbol = symbols[i];\n", + "\t\t\t\tif (symbol.find_first_of(' ', 59) != std::string::npos) {\n", + "\t\t\t\t\tstd::string name = symbol.substr(59, symbol.find_first_of(' ', 59) - 59);\n", + "\t\t\t\t\tint status;\n", + "\t\t\t\t\tchar * demangled = abi::__cxa_demangle(name.c_str(), NULL, NULL, &status);\n", + "\t\t\t\t\tif (!status) {\n", + "\t\t\t\t\t\tbuffer << \" \" \n", + "\t\t\t\t\t\t\t<< symbol.substr(0, 59) \n", + "\t\t\t\t\t\t\t<< demangled\n", + "\t\t\t\t\t\t\t<< symbol.substr(59 + name.size())\n", + "\t\t\t\t\t\t\t<< std::endl;\n", + "\t\t\t\t\t\tfree(demangled);\n", + "\t\t\t\t\t} else\n", + "\t\t\t\t\t\tbuffer << \" \" << symbol << std::endl;\n", + "\t\t\t\t} else\n", + "\t\t\t\t\tbuffer << \" \" << symbol << std::endl;\n", + "\t\t\t}\n", + "\t\t\tfree(symbols);\n", + "\t\t}\n", + "\t\tstd::cerr << buffer.str();\n", + "\t\tstd::exit(EXIT_FAILURE);\n", + "\t}\n" + ] + }, + { + "cell_type": "code", + "execution_count": 9, + "metadata": {}, + "outputs": [ { - "cell_type": "heading", - "level": 1, - "metadata": {}, - "source": [ - "10th order poly log approx CPP" + "name": "stdout", + "output_type": "stream", + "text": [ + "Overwriting src/ln_exp.cpp\n" ] - }, - { - "cell_type": "code", - "collapsed": false, - "input": [ - "%%file src/ln.cpp\n", - "#define PY_ARRAY_UNIQUE_SYMBOL ln_hope_ARRAY_API\n", - "#define NPY_NO_DEPRECATED_API NPY_1_7_API_VERSION\n", - "#include \n", - "#include \n", - "#include \n", - "#include \n", - "#include \n", - "#include \n", - "#include \n", - "#include \n", - "#include \n", - "#include \n", - "#include \n", - "struct PyObj {\n", - "\ttypedef PyObject * ptr_t;\n", - "\ttypedef PyArrayObject * arrptr_t;\n", - "\tPyObj(): dec(false), ptr(NULL) {}\n", - "\tPyObj(ptr_t p): dec(false), ptr(p) {}\n", - "\t~PyObj() { if(dec) Py_DECREF(ptr); }\n", - "\tPyObj & operator=(ptr_t p) { if(dec) Py_DECREF(ptr); ptr = p; dec = false; return *this; }\n", - "\tPyObj & incref(ptr_t p) { if(dec) Py_DECREF(ptr); ptr = p; dec = (p != NULL); return *this; }\n", - "\toperator bool() const { return ptr; }\n", - "\toperator ptr_t() const { return ptr; }\n", - "\toperator arrptr_t() const { return (arrptr_t)ptr; }\n", - "\tbool dec;\n", - "\tptr_t ptr;\n", - "};\n", - "\n", - "inline void ln_hope_d1d1(PyObject * pX, npy_intp const * __restrict__ sX, npy_double * __restrict__ cX, \n", - "\t\t\t\t\t\t\t\t\t\t\t\t\tPyObject * pY, npy_intp const * __restrict__ sY, npy_double * __restrict__ cY);\n", - "\n", - "inline void ln_hope_d1d1(PyObject * pX, npy_intp const * __restrict__ sX, npy_double * __restrict__ cX, \n", - "\t\t\t\t\t\t\t\t\t\t\t\t\tPyObject * pY, npy_intp const * __restrict__ sY, npy_double * __restrict__ cY){\n", - "\t\n", - "\tfor (npy_intp i0 = 0; i0 < sY[0] - 0; ++i0) {\n", - "\t\tcY[(int)(i0)] = (cX[i0] - 1) - (std::pow((cX[i0] - 1), 2) / 2) + (std::pow((cX[i0] - 1), 3) / 3) - (std::pow((cX[i0] - 1), 4) / 4) + (std::pow((cX[i0] - 1), 5) / 5) - (std::pow((cX[i0] - 1), 6) / 6) + (std::pow((cX[i0] - 1), 7) / 7) - (std::pow((cX[i0] - 1), 8) / 8) + (std::pow((cX[i0] - 1), 9) / 9);\n", - "\t}\n", - "}\n", - "\n", - "void sighandler(int sig);\n", - "#include \n", - "extern \"C\" {\n", - "\tPyObject * create_signature;\n", - "\tstruct sigaction slot;\n", - "\tPyObject * set_create_signature(PyObject * self, PyObject * args) {\n", - "\t\tif (!PyArg_ParseTuple(args, \"O\", &create_signature)) {\n", - "\t\t\tPyErr_SetString(PyExc_ValueError, \"Invalid Argument to set_create_signature!\");\n", - "\t\t\treturn NULL;\n", - "\t\t}\n", - "\t\tPy_INCREF(create_signature);\n", - "\t\tmemset(&slot, 0, sizeof(slot));\n", - "\t\tslot.sa_handler = &sighandler;\n", - "\t\tsigaction(SIGSEGV, &slot, NULL);\n", - "\t\tsigaction(SIGBUS, &slot, NULL);\n", - "\t\tPy_INCREF(Py_None);\n", - "\t\treturn Py_None;\n", - "\t}\n", - "\tPyObject * run(PyObject * self, PyObject * args) {\n", - "\t\t{\n", - "\t\t\tPyObj pX;\n", - "\t\t\tPyObj pY;\n", - "\t\t\tif (\n", - "\t\t\t\tPyTuple_CheckExact(args) and PyTuple_GET_SIZE(args) == 2\n", - "\t\t\t\tand (pX = PyTuple_GET_ITEM(args, 0)) and PyArray_CheckExact(pX)\n", - "\t\t\t\tand PyArray_TYPE((PyArrayObject *)pX) == NPY_FLOAT64 and PyArray_NDIM((PyArrayObject *)pX) == 1\n", - "\t\t\t\tand (pY = PyTuple_GET_ITEM(args, 1)) and PyArray_CheckExact(pY)\n", - "\t\t\t\tand PyArray_TYPE((PyArrayObject *)pY) == NPY_FLOAT64 and PyArray_NDIM((PyArrayObject *)pY) == 1\n", - "\t\t\t) {\n", - "\t\t\t\tif (!(pX.incref((PyObject *)PyArray_GETCONTIGUOUS((PyArrayObject *)pX)))) {\n", - "\t\t\t\t\tPyErr_SetString(PyExc_ValueError, \"Invalid Argument type on X!\");\n", - "\t\t\t\t\treturn NULL;\n", - "\t\t\t\t}\n", - "\t\t\t\tif (!(pY.incref((PyObject *)PyArray_GETCONTIGUOUS((PyArrayObject *)pY)))) {\n", - "\t\t\t\t\tPyErr_SetString(PyExc_ValueError, \"Invalid Argument type on Y!\");\n", - "\t\t\t\t\treturn NULL;\n", - "\t\t\t\t}\n", - "\t\t\t\ttry {\n", - "\t\t\t\t\tln_hope_d1d1(\n", - "\t\t\t\t\t\t pX, PyArray_SHAPE((PyArrayObject *)pX), (npy_double *)PyArray_DATA((PyArrayObject *)pX)\n", - "\t\t\t\t\t\t, pY, PyArray_SHAPE((PyArrayObject *)pY), (npy_double *)PyArray_DATA((PyArrayObject *)pY)\n", - "\t\t\t\t\t);\n", - "\t\t\t\t\tPy_INCREF(Py_None);\n", - "\t\t\t\t\treturn Py_None;\n", - "\t\t\t\t} catch (...) {\n", - "\t\t\t\t\treturn NULL;\n", - "\t\t\t\t}\n", - "\t\t\t} else\n", - "\t\t\t\tPyErr_Clear();\n", - "\t\t}\n", - "\t\tPyObject * signatures = Py_BuildValue(\"(sO)\", \"(lp0\\n(lp1\\nccopy_reg\\n_reconstructor\\np2\\n(chope._ast\\nVariable\\np3\\nc__builtin__\\nobject\\np4\\nNtp5\\nRp6\\n(dp7\\nS'name'\\np8\\nS'X'\\np9\\nsS'dtype'\\np10\\nS'float64'\\np11\\nsS'dims'\\np12\\nI1\\nsbag2\\n(g3\\ng4\\nNtp13\\nRp14\\n(dp15\\ng8\\nS'Y'\\np16\\nsg10\\ng11\\nsg12\\nI1\\nsbaa.\", args);\n", - "\t\tif (!signatures) {\n", - "\t\t\tPyErr_SetString(PyExc_ValueError, \"Error building signature string for ln_hope\");\n", - "\t\t\treturn NULL;\n", - "\t\t}\n", - "\t\treturn PyObject_Call(create_signature, signatures, NULL);\n", - "\t}\n", - "\tPyMethodDef ln_hopeMethods[] = {\n", - "\t\t{ \"set_create_signature\", (PyCFunction)set_create_signature, METH_VARARGS },\n", - "\t\t{ \"run\", (PyCFunction)run, METH_VARARGS },\n", - "\t\t{ NULL, NULL }\n", - "\t};\n", - "\tPyMODINIT_FUNC initln(void) {\n", - "\t\timport_array();\n", - "\t\tPyImport_ImportModule(\"numpy\");\n", - "\t\t(void)Py_InitModule(\"ln\", ln_hopeMethods);\n", - "\t}\n", - "}\n", - "#include \n", - "#include \n", - "#include \n", - "#include \n", - "#include \n", - "void sighandler(int sig) {\n", - "\tstd::ostringstream buffer;\n", - "\tbuffer << \"Abort by \" << (sig == SIGSEGV ? \"segfault\" : \"bus error\") << std::endl;\n", - "\tvoid * stack[64];\n", - "\tstd::size_t depth = backtrace(stack, 64);\n", - "\tif (!depth)\n", - "\t\tbuffer << \" \" << std::endl;\n", - "\telse {\n", - "\t\tchar ** symbols = backtrace_symbols(stack, depth);\n", - "\t\tfor (std::size_t i = 1; i < depth; ++i) {\n", - "\t\t\tstd::string symbol = symbols[i];\n", - "\t\t\t\tif (symbol.find_first_of(' ', 59) != std::string::npos) {\n", - "\t\t\t\t\tstd::string name = symbol.substr(59, symbol.find_first_of(' ', 59) - 59);\n", - "\t\t\t\t\tint status;\n", - "\t\t\t\t\tchar * demangled = abi::__cxa_demangle(name.c_str(), NULL, NULL, &status);\n", - "\t\t\t\t\tif (!status) {\n", - "\t\t\t\t\t\tbuffer << \" \" \n", - "\t\t\t\t\t\t\t<< symbol.substr(0, 59) \n", - "\t\t\t\t\t\t\t<< demangled\n", - "\t\t\t\t\t\t\t<< symbol.substr(59 + name.size())\n", - "\t\t\t\t\t\t\t<< std::endl;\n", - "\t\t\t\t\t\tfree(demangled);\n", - "\t\t\t\t\t} else\n", - "\t\t\t\t\t\tbuffer << \" \" << symbol << std::endl;\n", - "\t\t\t\t} else\n", - "\t\t\t\t\tbuffer << \" \" << symbol << std::endl;\n", - "\t\t\t}\n", - "\t\t\tfree(symbols);\n", - "\t\t}\n", - "\t\tstd::cerr << buffer.str();\n", - "\t\tstd::exit(EXIT_FAILURE);\n", - "\t}\n" - ], - "language": "python", - "metadata": {}, - "outputs": [ - { - "output_type": "stream", - "stream": "stdout", - "text": [ - "Overwriting src/ln.cpp\n" - ] - } - ], - "prompt_number": 8 - }, - { - "cell_type": "code", - "collapsed": false, - "input": [ - "%%file src/ln_exp.cpp\n", - "#define PY_ARRAY_UNIQUE_SYMBOL ln_hope_exp_ARRAY_API\n", - "#define NPY_NO_DEPRECATED_API NPY_1_7_API_VERSION\n", - "#include \n", - "#include \n", - "#include \n", - "#include \n", - "#include \n", - "#include \n", - "#include \n", - "#include \n", - "#include \n", - "#include \n", - "#include \n", - "struct PyObj {\n", - "\ttypedef PyObject * ptr_t;\n", - "\ttypedef PyArrayObject * arrptr_t;\n", - "\tPyObj(): dec(false), ptr(NULL) {}\n", - "\tPyObj(ptr_t p): dec(false), ptr(p) {}\n", - "\t~PyObj() { if(dec) Py_DECREF(ptr); }\n", - "\tPyObj & operator=(ptr_t p) { if(dec) Py_DECREF(ptr); ptr = p; dec = false; return *this; }\n", - "\tPyObj & incref(ptr_t p) { if(dec) Py_DECREF(ptr); ptr = p; dec = (p != NULL); return *this; }\n", - "\toperator bool() const { return ptr; }\n", - "\toperator ptr_t() const { return ptr; }\n", - "\toperator arrptr_t() const { return (arrptr_t)ptr; }\n", - "\tbool dec;\n", - "\tptr_t ptr;\n", - "};\n", - "\n", - "inline void ln_hope_exp_d1d1(PyObject * pX, npy_intp const * __restrict__ sX, npy_double * __restrict__ cX, \n", - "\t\t\t\t\t\t\t\t\t\t\t\t\tPyObject * pY, npy_intp const * __restrict__ sY, npy_double * __restrict__ cY);\n", - "\n", - "inline void ln_hope_exp_d1d1(PyObject * pX, npy_intp const * __restrict__ sX, npy_double * __restrict__ cX, \n", - "\t\t\t\t\t\t\t\t\t\t\t\t\tPyObject * pY, npy_intp const * __restrict__ sY, npy_double * __restrict__ cY){\n", - "\n", - "\tfor (npy_intp i0 = 0; i0 < sX[0] - 0; ++i0) {\n", - "\t\tauto cx = (cX[i0] - 1);\n", - "\t\tauto cx2 = (cx * cx);\n", - "\t\tauto cx4 = (cx2 * cx2);\n", - "\t\tauto cx6 = (cx4 * cx2);\n", - "\t\tauto cx8 = (cx4 * cx4);\n", - "\t\tcY[i0] = cx - (cx2 / 2) + (cx * cx2 / 3) - (cx4 / 4) + (cx * cx4 / 5) - (cx6 / 6) + (cx6 * cx / 7) - (cx8 / 8) + (cx8 * cx / 9);\n", - "\t}\n", - "}\n", - "\n", - "void sighandler(int sig);\n", - "#include \n", - "extern \"C\" {\n", - "\tPyObject * create_signature;\n", - "\tstruct sigaction slot;\n", - "\tPyObject * set_create_signature(PyObject * self, PyObject * args) {\n", - "\t\tif (!PyArg_ParseTuple(args, \"O\", &create_signature)) {\n", - "\t\t\tPyErr_SetString(PyExc_ValueError, \"Invalid Argument to set_create_signature!\");\n", - "\t\t\treturn NULL;\n", - "\t\t}\n", - "\t\tPy_INCREF(create_signature);\n", - "\t\tmemset(&slot, 0, sizeof(slot));\n", - "\t\tslot.sa_handler = &sighandler;\n", - "\t\tsigaction(SIGSEGV, &slot, NULL);\n", - "\t\tsigaction(SIGBUS, &slot, NULL);\n", - "\t\tPy_INCREF(Py_None);\n", - "\t\treturn Py_None;\n", - "\t}\n", - "\tPyObject * run(PyObject * self, PyObject * args) {\n", - "\t\t{\n", - "\t\t\tPyObj pX;\n", - "\t\t\tPyObj pY;\n", - "\t\t\tif (\n", - "\t\t\t\tPyTuple_CheckExact(args) and PyTuple_GET_SIZE(args) == 2\n", - "\t\t\t\tand (pX = PyTuple_GET_ITEM(args, 0)) and PyArray_CheckExact(pX)\n", - "\t\t\t\tand PyArray_TYPE((PyArrayObject *)pX) == NPY_FLOAT64 and PyArray_NDIM((PyArrayObject *)pX) == 1\n", - "\t\t\t\tand (pY = PyTuple_GET_ITEM(args, 1)) and PyArray_CheckExact(pY)\n", - "\t\t\t\tand PyArray_TYPE((PyArrayObject *)pY) == NPY_FLOAT64 and PyArray_NDIM((PyArrayObject *)pY) == 1\n", - "\t\t\t) {\n", - "\t\t\t\tif (!(pX.incref((PyObject *)PyArray_GETCONTIGUOUS((PyArrayObject *)pX)))) {\n", - "\t\t\t\t\tPyErr_SetString(PyExc_ValueError, \"Invalid Argument type on X!\");\n", - "\t\t\t\t\treturn NULL;\n", - "\t\t\t\t}\n", - "\t\t\t\tif (!(pY.incref((PyObject *)PyArray_GETCONTIGUOUS((PyArrayObject *)pY)))) {\n", - "\t\t\t\t\tPyErr_SetString(PyExc_ValueError, \"Invalid Argument type on Y!\");\n", - "\t\t\t\t\treturn NULL;\n", - "\t\t\t\t}\n", - "\t\t\t\ttry {\n", - "\t\t\t\t\tln_hope_exp_d1d1(\n", - "\t\t\t\t\t\t pX, PyArray_SHAPE((PyArrayObject *)pX), (npy_double *)PyArray_DATA((PyArrayObject *)pX)\n", - "\t\t\t\t\t\t, pY, PyArray_SHAPE((PyArrayObject *)pY), (npy_double *)PyArray_DATA((PyArrayObject *)pY)\n", - "\t\t\t\t\t);\n", - "\t\t\t\t\tPy_INCREF(Py_None);\n", - "\t\t\t\t\treturn Py_None;\n", - "\t\t\t\t} catch (...) {\n", - "\t\t\t\t\treturn NULL;\n", - "\t\t\t\t}\n", - "\t\t\t} else\n", - "\t\t\t\tPyErr_Clear();\n", - "\t\t}\n", - "\t\tPyObject * signatures = Py_BuildValue(\"(sO)\", \"(lp0\\n(lp1\\nccopy_reg\\n_reconstructor\\np2\\n(chope._ast\\nVariable\\np3\\nc__builtin__\\nobject\\np4\\nNtp5\\nRp6\\n(dp7\\nS'name'\\np8\\nS'X'\\np9\\nsS'dtype'\\np10\\nS'float64'\\np11\\nsS'dims'\\np12\\nI1\\nsbag2\\n(g3\\ng4\\nNtp13\\nRp14\\n(dp15\\ng8\\nS'Y'\\np16\\nsg10\\ng11\\nsg12\\nI1\\nsbaa.\", args);\n", - "\t\tif (!signatures) {\n", - "\t\t\tPyErr_SetString(PyExc_ValueError, \"Error building signature string for ln_hope_exp\");\n", - "\t\t\treturn NULL;\n", - "\t\t}\n", - "\t\treturn PyObject_Call(create_signature, signatures, NULL);\n", - "\t}\n", - "\tPyMethodDef ln_hope_expMethods[] = {\n", - "\t\t{ \"set_create_signature\", (PyCFunction)set_create_signature, METH_VARARGS },\n", - "\t\t{ \"run\", (PyCFunction)run, METH_VARARGS },\n", - "\t\t{ NULL, NULL }\n", - "\t};\n", - "\tPyMODINIT_FUNC initln_exp(void) {\n", - "\t\timport_array();\n", - "\t\tPyImport_ImportModule(\"numpy\");\n", - "\t\t(void)Py_InitModule(\"ln_exp\", ln_hope_expMethods);\n", - "\t}\n", - "}\n", - "#include \n", - "#include \n", - "#include \n", - "#include \n", - "#include \n", - "void sighandler(int sig) {\n", - "\tstd::ostringstream buffer;\n", - "\tbuffer << \"Abort by \" << (sig == SIGSEGV ? \"segfault\" : \"bus error\") << std::endl;\n", - "\tvoid * stack[64];\n", - "\tstd::size_t depth = backtrace(stack, 64);\n", - "\tif (!depth)\n", - "\t\tbuffer << \" \" << std::endl;\n", - "\telse {\n", - "\t\tchar ** symbols = backtrace_symbols(stack, depth);\n", - "\t\tfor (std::size_t i = 1; i < depth; ++i) {\n", - "\t\t\tstd::string symbol = symbols[i];\n", - "\t\t\t\tif (symbol.find_first_of(' ', 59) != std::string::npos) {\n", - "\t\t\t\t\tstd::string name = symbol.substr(59, symbol.find_first_of(' ', 59) - 59);\n", - "\t\t\t\t\tint status;\n", - "\t\t\t\t\tchar * demangled = abi::__cxa_demangle(name.c_str(), NULL, NULL, &status);\n", - "\t\t\t\t\tif (!status) {\n", - "\t\t\t\t\t\tbuffer << \" \" \n", - "\t\t\t\t\t\t\t<< symbol.substr(0, 59) \n", - "\t\t\t\t\t\t\t<< demangled\n", - "\t\t\t\t\t\t\t<< symbol.substr(59 + name.size())\n", - "\t\t\t\t\t\t\t<< std::endl;\n", - "\t\t\t\t\t\tfree(demangled);\n", - "\t\t\t\t\t} else\n", - "\t\t\t\t\t\tbuffer << \" \" << symbol << std::endl;\n", - "\t\t\t\t} else\n", - "\t\t\t\t\tbuffer << \" \" << symbol << std::endl;\n", - "\t\t\t}\n", - "\t\t\tfree(symbols);\n", - "\t\t}\n", - "\t\tstd::cerr << buffer.str();\n", - "\t\tstd::exit(EXIT_FAILURE);\n", - "\t}\n" - ], - "language": "python", - "metadata": {}, - "outputs": [ - { - "output_type": "stream", - "stream": "stdout", - "text": [ - "Overwriting src/ln_exp.cpp\n" - ] - } - ], - "prompt_number": 9 - }, - { - "cell_type": "code", - "collapsed": false, - "input": [ - "%%file src/ln_opt.cpp\n", - "#define PY_ARRAY_UNIQUE_SYMBOL ln_hope_opt_ARRAY_API\n", - "#define NPY_NO_DEPRECATED_API NPY_1_7_API_VERSION\n", - "#include \n", - "#include \n", - "#include \n", - "#include \n", - "#include \n", - "#include \n", - "#include \n", - "#include \n", - "#include \n", - "#include \n", - "#include \n", - "struct PyObj {\n", - "\ttypedef PyObject * ptr_t;\n", - "\ttypedef PyArrayObject * arrptr_t;\n", - "\tPyObj(): dec(false), ptr(NULL) {}\n", - "\tPyObj(ptr_t p): dec(false), ptr(p) {}\n", - "\t~PyObj() { if(dec) Py_DECREF(ptr); }\n", - "\tPyObj & operator=(ptr_t p) { if(dec) Py_DECREF(ptr); ptr = p; dec = false; return *this; }\n", - "\tPyObj & incref(ptr_t p) { if(dec) Py_DECREF(ptr); ptr = p; dec = (p != NULL); return *this; }\n", - "\toperator bool() const { return ptr; }\n", - "\toperator ptr_t() const { return ptr; }\n", - "\toperator arrptr_t() const { return (arrptr_t)ptr; }\n", - "\tbool dec;\n", - "\tptr_t ptr;\n", - "};\n", - "\n", - "inline void ln_hope_opt_d1d1(PyObject * pX, npy_intp const * __restrict__ sX, npy_double * __restrict__ cX, \n", - "\t\t\t\t\t\t\t\t\t\t\t\t\tPyObject * pY, npy_intp const * __restrict__ sY, npy_double * __restrict__ cY);\n", - "\n", - "inline void ln_hope_opt_d1d1(PyObject * pX, npy_intp const * __restrict__ sX, npy_double * __restrict__ cX, \n", - "\t\t\t\t\t\t\t\t\t\t\t\t\tPyObject * pY, npy_intp const * __restrict__ sY, npy_double * __restrict__ cY){\n", - "\t\t\t\t\t\t\t\t\t\t\t\t\t\t\n", - "\tfor (npy_intp i0 = 0; i0 < sY[0] - 0; ++i0) {\n", - "\t\tauto c__sp0 = (cX[i0] * cX[i0]);\n", - "\t\tauto c__sp1 = (c__sp0 * c__sp0);\n", - "\t\tauto c__sp2 = (c__sp1 * c__sp1);\n", - "\t\tauto c__sp3 = (c__sp2 * cX[i0]);\n", - "\t\tauto c__sp4 = (c__sp0 * cX[i0]);\n", - "\t\tauto c__sp5 = (c__sp4 * c__sp4);\n", - "\t\tauto c__sp6 = (c__sp5 * cX[i0]);\n", - "\t\tauto c__sp7 = (c__sp1 * cX[i0]);\n", - "\t\tcY[(int)(i0)] = (-7129.0 / 2520.0) + (28 * c__sp4) + (-(18 * c__sp0)) + (-(9 * c__sp2 / 8)) + (-(14 * c__sp5)) + (-(63 * c__sp1 / 2)) + (126 * c__sp7 / 5) + (9 * cX[i0]) + (c__sp3 / 9) + (36 * c__sp6 / 7);\n", - "\t}\n", - "}\n", - "\n", - "void sighandler(int sig);\n", - "#include \n", - "extern \"C\" {\n", - "\tPyObject * create_signature;\n", - "\tstruct sigaction slot;\n", - "\tPyObject * set_create_signature(PyObject * self, PyObject * args) {\n", - "\t\tif (!PyArg_ParseTuple(args, \"O\", &create_signature)) {\n", - "\t\t\tPyErr_SetString(PyExc_ValueError, \"Invalid Argument to set_create_signature!\");\n", - "\t\t\treturn NULL;\n", - "\t\t}\n", - "\t\tPy_INCREF(create_signature);\n", - "\t\tmemset(&slot, 0, sizeof(slot));\n", - "\t\tslot.sa_handler = &sighandler;\n", - "\t\tsigaction(SIGSEGV, &slot, NULL);\n", - "\t\tsigaction(SIGBUS, &slot, NULL);\n", - "\t\tPy_INCREF(Py_None);\n", - "\t\treturn Py_None;\n", - "\t}\n", - "\tPyObject * run(PyObject * self, PyObject * args) {\n", - "\t\t{\n", - "\t\t\tPyObj pX;\n", - "\t\t\tPyObj pY;\n", - "\t\t\tif (\n", - "\t\t\t\tPyTuple_CheckExact(args) and PyTuple_GET_SIZE(args) == 2\n", - "\t\t\t\tand (pX = PyTuple_GET_ITEM(args, 0)) and PyArray_CheckExact(pX)\n", - "\t\t\t\tand PyArray_TYPE((PyArrayObject *)pX) == NPY_FLOAT64 and PyArray_NDIM((PyArrayObject *)pX) == 1\n", - "\t\t\t\tand (pY = PyTuple_GET_ITEM(args, 1)) and PyArray_CheckExact(pY)\n", - "\t\t\t\tand PyArray_TYPE((PyArrayObject *)pY) == NPY_FLOAT64 and PyArray_NDIM((PyArrayObject *)pY) == 1\n", - "\t\t\t) {\n", - "\t\t\t\tif (!(pX.incref((PyObject *)PyArray_GETCONTIGUOUS((PyArrayObject *)pX)))) {\n", - "\t\t\t\t\tPyErr_SetString(PyExc_ValueError, \"Invalid Argument type on X!\");\n", - "\t\t\t\t\treturn NULL;\n", - "\t\t\t\t}\n", - "\t\t\t\tif (!(pY.incref((PyObject *)PyArray_GETCONTIGUOUS((PyArrayObject *)pY)))) {\n", - "\t\t\t\t\tPyErr_SetString(PyExc_ValueError, \"Invalid Argument type on Y!\");\n", - "\t\t\t\t\treturn NULL;\n", - "\t\t\t\t}\n", - "\t\t\t\ttry {\n", - "\t\t\t\t\tln_hope_opt_d1d1(\n", - "\t\t\t\t\t\t pX, PyArray_SHAPE((PyArrayObject *)pX), (npy_double *)PyArray_DATA((PyArrayObject *)pX)\n", - "\t\t\t\t\t\t, pY, PyArray_SHAPE((PyArrayObject *)pY), (npy_double *)PyArray_DATA((PyArrayObject *)pY)\n", - "\t\t\t\t\t);\n", - "\t\t\t\t\tPy_INCREF(Py_None);\n", - "\t\t\t\t\treturn Py_None;\n", - "\t\t\t\t} catch (...) {\n", - "\t\t\t\t\treturn NULL;\n", - "\t\t\t\t}\n", - "\t\t\t} else\n", - "\t\t\t\tPyErr_Clear();\n", - "\t\t}\n", - "\t\tPyObject * signatures = Py_BuildValue(\"(sO)\", \"(lp0\\n(lp1\\nccopy_reg\\n_reconstructor\\np2\\n(chope._ast\\nVariable\\np3\\nc__builtin__\\nobject\\np4\\nNtp5\\nRp6\\n(dp7\\nS'name'\\np8\\nS'X'\\np9\\nsS'dtype'\\np10\\nS'float64'\\np11\\nsS'dims'\\np12\\nI1\\nsbag2\\n(g3\\ng4\\nNtp13\\nRp14\\n(dp15\\ng8\\nS'Y'\\np16\\nsg10\\ng11\\nsg12\\nI1\\nsbaa.\", args);\n", - "\t\tif (!signatures) {\n", - "\t\t\tPyErr_SetString(PyExc_ValueError, \"Error building signature string for ln_hope_opt\");\n", - "\t\t\treturn NULL;\n", - "\t\t}\n", - "\t\treturn PyObject_Call(create_signature, signatures, NULL);\n", - "\t}\n", - "\tPyMethodDef ln_hope_optMethods[] = {\n", - "\t\t{ \"set_create_signature\", (PyCFunction)set_create_signature, METH_VARARGS },\n", - "\t\t{ \"run\", (PyCFunction)run, METH_VARARGS },\n", - "\t\t{ NULL, NULL }\n", - "\t};\n", - "\tPyMODINIT_FUNC initln_opt(void) {\n", - "\t\timport_array();\n", - "\t\tPyImport_ImportModule(\"numpy\");\n", - "\t\t(void)Py_InitModule(\"ln_opt\", ln_hope_optMethods);\n", - "\t}\n", - "}\n", - "#include \n", - "#include \n", - "#include \n", - "#include \n", - "#include \n", - "void sighandler(int sig) {\n", - "\tstd::ostringstream buffer;\n", - "\tbuffer << \"Abort by \" << (sig == SIGSEGV ? \"segfault\" : \"bus error\") << std::endl;\n", - "\tvoid * stack[64];\n", - "\tstd::size_t depth = backtrace(stack, 64);\n", - "\tif (!depth)\n", - "\t\tbuffer << \" \" << std::endl;\n", - "\telse {\n", - "\t\tchar ** symbols = backtrace_symbols(stack, depth);\n", - "\t\tfor (std::size_t i = 1; i < depth; ++i) {\n", - "\t\t\tstd::string symbol = symbols[i];\n", - "\t\t\t\tif (symbol.find_first_of(' ', 59) != std::string::npos) {\n", - "\t\t\t\t\tstd::string name = symbol.substr(59, symbol.find_first_of(' ', 59) - 59);\n", - "\t\t\t\t\tint status;\n", - "\t\t\t\t\tchar * demangled = abi::__cxa_demangle(name.c_str(), NULL, NULL, &status);\n", - "\t\t\t\t\tif (!status) {\n", - "\t\t\t\t\t\tbuffer << \" \" \n", - "\t\t\t\t\t\t\t<< symbol.substr(0, 59) \n", - "\t\t\t\t\t\t\t<< demangled\n", - "\t\t\t\t\t\t\t<< symbol.substr(59 + name.size())\n", - "\t\t\t\t\t\t\t<< std::endl;\n", - "\t\t\t\t\t\tfree(demangled);\n", - "\t\t\t\t\t} else\n", - "\t\t\t\t\t\tbuffer << \" \" << symbol << std::endl;\n", - "\t\t\t\t} else\n", - "\t\t\t\t\tbuffer << \" \" << symbol << std::endl;\n", - "\t\t\t}\n", - "\t\t\tfree(symbols);\n", - "\t\t}\n", - "\t\tstd::cerr << buffer.str();\n", - "\t\tstd::exit(EXIT_FAILURE);\n", - "\t}\n" - ], - "language": "python", - "metadata": {}, - "outputs": [ - { - "output_type": "stream", - "stream": "stdout", - "text": [ - "Overwriting src/ln_opt.cpp\n" - ] - } - ], - "prompt_number": 10 - }, + } + ], + "source": [ + "%%file src/ln_exp.cpp\n", + "#define PY_ARRAY_UNIQUE_SYMBOL ln_hope_exp_ARRAY_API\n", + "#define NPY_NO_DEPRECATED_API NPY_1_7_API_VERSION\n", + "#include \n", + "#include \n", + "#include \n", + "#include \n", + "#include \n", + "#include \n", + "#include \n", + "#include \n", + "#include \n", + "#include \n", + "#include \n", + "struct PyObj {\n", + "\ttypedef PyObject * ptr_t;\n", + "\ttypedef PyArrayObject * arrptr_t;\n", + "\tPyObj(): dec(false), ptr(NULL) {}\n", + "\tPyObj(ptr_t p): dec(false), ptr(p) {}\n", + "\t~PyObj() { if(dec) Py_DECREF(ptr); }\n", + "\tPyObj & operator=(ptr_t p) { if(dec) Py_DECREF(ptr); ptr = p; dec = false; return *this; }\n", + "\tPyObj & incref(ptr_t p) { if(dec) Py_DECREF(ptr); ptr = p; dec = (p != NULL); return *this; }\n", + "\toperator bool() const { return ptr; }\n", + "\toperator ptr_t() const { return ptr; }\n", + "\toperator arrptr_t() const { return (arrptr_t)ptr; }\n", + "\tbool dec;\n", + "\tptr_t ptr;\n", + "};\n", + "\n", + "inline void ln_hope_exp_d1d1(PyObject * pX, npy_intp const * __restrict__ sX, npy_double * __restrict__ cX, \n", + "\t\t\t\t\t\t\t\t\t\t\t\t\tPyObject * pY, npy_intp const * __restrict__ sY, npy_double * __restrict__ cY);\n", + "\n", + "inline void ln_hope_exp_d1d1(PyObject * pX, npy_intp const * __restrict__ sX, npy_double * __restrict__ cX, \n", + "\t\t\t\t\t\t\t\t\t\t\t\t\tPyObject * pY, npy_intp const * __restrict__ sY, npy_double * __restrict__ cY){\n", + "\n", + "\tfor (npy_intp i0 = 0; i0 < sX[0] - 0; ++i0) {\n", + "\t\tauto cx = (cX[i0] - 1);\n", + "\t\tauto cx2 = (cx * cx);\n", + "\t\tauto cx4 = (cx2 * cx2);\n", + "\t\tauto cx6 = (cx4 * cx2);\n", + "\t\tauto cx8 = (cx4 * cx4);\n", + "\t\tcY[i0] = cx - (cx2 / 2) + (cx * cx2 / 3) - (cx4 / 4) + (cx * cx4 / 5) - (cx6 / 6) + (cx6 * cx / 7) - (cx8 / 8) + (cx8 * cx / 9);\n", + "\t}\n", + "}\n", + "\n", + "void sighandler(int sig);\n", + "#include \n", + "extern \"C\" {\n", + "\tPyObject * create_signature;\n", + "\tstruct sigaction slot;\n", + "\tPyObject * set_create_signature(PyObject * self, PyObject * args) {\n", + "\t\tif (!PyArg_ParseTuple(args, \"O\", &create_signature)) {\n", + "\t\t\tPyErr_SetString(PyExc_ValueError, \"Invalid Argument to set_create_signature!\");\n", + "\t\t\treturn NULL;\n", + "\t\t}\n", + "\t\tPy_INCREF(create_signature);\n", + "\t\tmemset(&slot, 0, sizeof(slot));\n", + "\t\tslot.sa_handler = &sighandler;\n", + "\t\tsigaction(SIGSEGV, &slot, NULL);\n", + "\t\tsigaction(SIGBUS, &slot, NULL);\n", + "\t\tPy_INCREF(Py_None);\n", + "\t\treturn Py_None;\n", + "\t}\n", + "\tPyObject * run(PyObject * self, PyObject * args) {\n", + "\t\t{\n", + "\t\t\tPyObj pX;\n", + "\t\t\tPyObj pY;\n", + "\t\t\tif (\n", + "\t\t\t\tPyTuple_CheckExact(args) and PyTuple_GET_SIZE(args) == 2\n", + "\t\t\t\tand (pX = PyTuple_GET_ITEM(args, 0)) and PyArray_CheckExact(pX)\n", + "\t\t\t\tand PyArray_TYPE((PyArrayObject *)pX) == NPY_FLOAT64 and PyArray_NDIM((PyArrayObject *)pX) == 1\n", + "\t\t\t\tand (pY = PyTuple_GET_ITEM(args, 1)) and PyArray_CheckExact(pY)\n", + "\t\t\t\tand PyArray_TYPE((PyArrayObject *)pY) == NPY_FLOAT64 and PyArray_NDIM((PyArrayObject *)pY) == 1\n", + "\t\t\t) {\n", + "\t\t\t\tif (!(pX.incref((PyObject *)PyArray_GETCONTIGUOUS((PyArrayObject *)pX)))) {\n", + "\t\t\t\t\tPyErr_SetString(PyExc_ValueError, \"Invalid Argument type on X!\");\n", + "\t\t\t\t\treturn NULL;\n", + "\t\t\t\t}\n", + "\t\t\t\tif (!(pY.incref((PyObject *)PyArray_GETCONTIGUOUS((PyArrayObject *)pY)))) {\n", + "\t\t\t\t\tPyErr_SetString(PyExc_ValueError, \"Invalid Argument type on Y!\");\n", + "\t\t\t\t\treturn NULL;\n", + "\t\t\t\t}\n", + "\t\t\t\ttry {\n", + "\t\t\t\t\tln_hope_exp_d1d1(\n", + "\t\t\t\t\t\t pX, PyArray_SHAPE((PyArrayObject *)pX), (npy_double *)PyArray_DATA((PyArrayObject *)pX)\n", + "\t\t\t\t\t\t, pY, PyArray_SHAPE((PyArrayObject *)pY), (npy_double *)PyArray_DATA((PyArrayObject *)pY)\n", + "\t\t\t\t\t);\n", + "\t\t\t\t\tPy_INCREF(Py_None);\n", + "\t\t\t\t\treturn Py_None;\n", + "\t\t\t\t} catch (...) {\n", + "\t\t\t\t\treturn NULL;\n", + "\t\t\t\t}\n", + "\t\t\t} else\n", + "\t\t\t\tPyErr_Clear();\n", + "\t\t}\n", + "\t\tPyObject * signatures = Py_BuildValue(\"(sO)\", \"(lp0\\n(lp1\\nccopy_reg\\n_reconstructor\\np2\\n(chope._ast\\nVariable\\np3\\nc__builtin__\\nobject\\np4\\nNtp5\\nRp6\\n(dp7\\nS'name'\\np8\\nS'X'\\np9\\nsS'dtype'\\np10\\nS'float64'\\np11\\nsS'dims'\\np12\\nI1\\nsbag2\\n(g3\\ng4\\nNtp13\\nRp14\\n(dp15\\ng8\\nS'Y'\\np16\\nsg10\\ng11\\nsg12\\nI1\\nsbaa.\", args);\n", + "\t\tif (!signatures) {\n", + "\t\t\tPyErr_SetString(PyExc_ValueError, \"Error building signature string for ln_hope_exp\");\n", + "\t\t\treturn NULL;\n", + "\t\t}\n", + "\t\treturn PyObject_Call(create_signature, signatures, NULL);\n", + "\t}\n", + "\tPyMethodDef ln_hope_expMethods[] = {\n", + "\t\t{ \"set_create_signature\", (PyCFunction)set_create_signature, METH_VARARGS },\n", + "\t\t{ \"run\", (PyCFunction)run, METH_VARARGS },\n", + "\t\t{ NULL, NULL }\n", + "\t};\n", + "\tPyMODINIT_FUNC initln_exp(void) {\n", + "\t\timport_array();\n", + "\t\tPyImport_ImportModule(\"numpy\");\n", + "\t\t(void)Py_InitModule(\"ln_exp\", ln_hope_expMethods);\n", + "\t}\n", + "}\n", + "#include \n", + "#include \n", + "#include \n", + "#include \n", + "#include \n", + "void sighandler(int sig) {\n", + "\tstd::ostringstream buffer;\n", + "\tbuffer << \"Abort by \" << (sig == SIGSEGV ? \"segfault\" : \"bus error\") << std::endl;\n", + "\tvoid * stack[64];\n", + "\tstd::size_t depth = backtrace(stack, 64);\n", + "\tif (!depth)\n", + "\t\tbuffer << \" \" << std::endl;\n", + "\telse {\n", + "\t\tchar ** symbols = backtrace_symbols(stack, depth);\n", + "\t\tfor (std::size_t i = 1; i < depth; ++i) {\n", + "\t\t\tstd::string symbol = symbols[i];\n", + "\t\t\t\tif (symbol.find_first_of(' ', 59) != std::string::npos) {\n", + "\t\t\t\t\tstd::string name = symbol.substr(59, symbol.find_first_of(' ', 59) - 59);\n", + "\t\t\t\t\tint status;\n", + "\t\t\t\t\tchar * demangled = abi::__cxa_demangle(name.c_str(), NULL, NULL, &status);\n", + "\t\t\t\t\tif (!status) {\n", + "\t\t\t\t\t\tbuffer << \" \" \n", + "\t\t\t\t\t\t\t<< symbol.substr(0, 59) \n", + "\t\t\t\t\t\t\t<< demangled\n", + "\t\t\t\t\t\t\t<< symbol.substr(59 + name.size())\n", + "\t\t\t\t\t\t\t<< std::endl;\n", + "\t\t\t\t\t\tfree(demangled);\n", + "\t\t\t\t\t} else\n", + "\t\t\t\t\t\tbuffer << \" \" << symbol << std::endl;\n", + "\t\t\t\t} else\n", + "\t\t\t\t\tbuffer << \" \" << symbol << std::endl;\n", + "\t\t\t}\n", + "\t\t\tfree(symbols);\n", + "\t\t}\n", + "\t\tstd::cerr << buffer.str();\n", + "\t\tstd::exit(EXIT_FAILURE);\n", + "\t}\n" + ] + }, + { + "cell_type": "code", + "execution_count": 10, + "metadata": {}, + "outputs": [ { - "cell_type": "heading", - "level": 2, - "metadata": {}, - "source": [ - "Simplify CPP" + "name": "stdout", + "output_type": "stream", + "text": [ + "Overwriting src/ln_opt.cpp\n" ] - }, - { - "cell_type": "code", - "collapsed": false, - "input": [ - "%%file src/poly.cpp\n", - "#define PY_ARRAY_UNIQUE_SYMBOL poly_ARRAY_API\n", - "#define NPY_NO_DEPRECATED_API NPY_1_7_API_VERSION\n", - "#include \n", - "#include \n", - "#include \n", - "#include \n", - "#include \n", - "#include \n", - "#include \n", - "#include \n", - "#include \n", - "#include \n", - "#include \n", - "struct PyObj {\n", - "\ttypedef PyObject * ptr_t;\n", - "\ttypedef PyArrayObject * arrptr_t;\n", - "\tPyObj(): dec(false), ptr(NULL) {}\n", - "\tPyObj(ptr_t p): dec(false), ptr(p) {}\n", - "\t~PyObj() { if(dec) Py_DECREF(ptr); }\n", - "\tPyObj & operator=(ptr_t p) { if(dec) Py_DECREF(ptr); ptr = p; dec = false; return *this; }\n", - "\tPyObj & incref(ptr_t p) { if(dec) Py_DECREF(ptr); ptr = p; dec = (p != NULL); return *this; }\n", - "\toperator bool() const { return ptr; }\n", - "\toperator ptr_t() const { return ptr; }\n", - "\toperator arrptr_t() const { return (arrptr_t)ptr; }\n", - "\tbool dec;\n", - "\tptr_t ptr;\n", - "};\n", - "inline void poly_d1d1(PyObject * pres, npy_intp const * __restrict__ sres, npy_double * __restrict__ cres, \n", - "\t\t\t\t\t\t\t\t\t\t\tPyObject * parg, npy_intp const * __restrict__ sarg, npy_double * __restrict__ carg);\n", - "\n", - "inline void poly_d1d1(PyObject * pres, npy_intp const * __restrict__ sres, npy_double * __restrict__ cres, \n", - "\t\t\t\t\t\t\t\t\t\t\tPyObject * parg, npy_intp const * __restrict__ sarg, npy_double * __restrict__ carg){\n", - "\t\n", - "\tnpy_double arg_i;\n", - "\tdouble sin_arg_i;\n", - "\tfor (npy_intp i0 = 0; i0 < sres[0] - 0; ++i0) {\n", - "\t\targ_i = carg[i0];\n", - "\t\tcres[(int)(i0)] = std::pow(std::sin(arg_i), 2) + (std::pow(arg_i, 3) + std::pow(arg_i, 2) - arg_i - 1) / (std::pow(arg_i, 2) + 2 * arg_i + 1) + std::pow(std::cos(arg_i), 2);\n", - "\t}\n", - "}\n", - "void sighandler(int sig);\n", - "#include \n", - "extern \"C\" {\n", - "\tPyObject * create_signature;\n", - "\tstruct sigaction slot;\n", - "\tPyObject * set_create_signature(PyObject * self, PyObject * args) {\n", - "\t\tif (!PyArg_ParseTuple(args, \"O\", &create_signature)) {\n", - "\t\t\tPyErr_SetString(PyExc_ValueError, \"Invalid Argument to set_create_signature!\");\n", - "\t\t\treturn NULL;\n", - "\t\t}\n", - "\t\tPy_INCREF(create_signature);\n", - "\t\tmemset(&slot, 0, sizeof(slot));\n", - "\t\tslot.sa_handler = &sighandler;\n", - "\t\tsigaction(SIGSEGV, &slot, NULL);\n", - "\t\tsigaction(SIGBUS, &slot, NULL);\n", - "\t\tPy_INCREF(Py_None);\n", - "\t\treturn Py_None;\n", - "\t}\n", - "\tPyObject * run(PyObject * self, PyObject * args) {\n", - "\t\t{\n", - "\t\t\tPyObj pres;\n", - "\t\t\tPyObj parg;\n", - "\t\t\tif (\n", - "\t\t\t\tPyTuple_CheckExact(args) and PyTuple_GET_SIZE(args) == 2\n", - "\t\t\t\tand (pres = PyTuple_GET_ITEM(args, 0)) and PyArray_CheckExact(pres)\n", - "\t\t\t\tand PyArray_TYPE((PyArrayObject *)pres) == NPY_FLOAT64 and PyArray_NDIM((PyArrayObject *)pres) == 1\n", - "\t\t\t\tand (parg = PyTuple_GET_ITEM(args, 1)) and PyArray_CheckExact(parg)\n", - "\t\t\t\tand PyArray_TYPE((PyArrayObject *)parg) == NPY_FLOAT64 and PyArray_NDIM((PyArrayObject *)parg) == 1\n", - "\t\t\t) {\n", - "\t\t\t\tif (!(pres.incref((PyObject *)PyArray_GETCONTIGUOUS((PyArrayObject *)pres)))) {\n", - "\t\t\t\t\tPyErr_SetString(PyExc_ValueError, \"Invalid Argument type on res!\");\n", - "\t\t\t\t\treturn NULL;\n", - "\t\t\t\t}\n", - "\t\t\t\tif (!(parg.incref((PyObject *)PyArray_GETCONTIGUOUS((PyArrayObject *)parg)))) {\n", - "\t\t\t\t\tPyErr_SetString(PyExc_ValueError, \"Invalid Argument type on arg!\");\n", - "\t\t\t\t\treturn NULL;\n", - "\t\t\t\t}\n", - "\t\t\t\ttry {\n", - "\t\t\t\t\tpoly_d1d1(\n", - "\t\t\t\t\t\t pres, PyArray_SHAPE((PyArrayObject *)pres), (npy_double *)PyArray_DATA((PyArrayObject *)pres)\n", - "\t\t\t\t\t\t, parg, PyArray_SHAPE((PyArrayObject *)parg), (npy_double *)PyArray_DATA((PyArrayObject *)parg)\n", - "\t\t\t\t\t);\n", - "\t\t\t\t\tPy_INCREF(Py_None);\n", - "\t\t\t\t\treturn Py_None;\n", - "\t\t\t\t} catch (...) {\n", - "\t\t\t\t\treturn NULL;\n", - "\t\t\t\t}\n", - "\t\t\t} else\n", - "\t\t\t\tPyErr_Clear();\n", - "\t\t}\n", - "\t\tPyObject * signatures = Py_BuildValue(\"(sO)\", \"(lp0\\n(lp1\\nccopy_reg\\n_reconstructor\\np2\\n(chope._ast\\nVariable\\np3\\nc__builtin__\\nobject\\np4\\nNtp5\\nRp6\\n(dp7\\nS'name'\\np8\\nS'res'\\np9\\nsS'dtype'\\np10\\nS'float64'\\np11\\nsS'dims'\\np12\\nI1\\nsbag2\\n(g3\\ng4\\nNtp13\\nRp14\\n(dp15\\ng8\\nS'arg'\\np16\\nsg10\\ng11\\nsg12\\nI1\\nsbaa.\", args);\n", - "\t\tif (!signatures) {\n", - "\t\t\tPyErr_SetString(PyExc_ValueError, \"Error building signature string for poly\");\n", - "\t\t\treturn NULL;\n", - "\t\t}\n", - "\t\treturn PyObject_Call(create_signature, signatures, NULL);\n", - "\t}\n", - "\tPyMethodDef polyMethods[] = {\n", - "\t\t{ \"set_create_signature\", (PyCFunction)set_create_signature, METH_VARARGS },\n", - "\t\t{ \"run\", (PyCFunction)run, METH_VARARGS },\n", - "\t\t{ NULL, NULL }\n", - "\t};\n", - "\tPyMODINIT_FUNC initpoly(void) {\n", - "\t\timport_array();\n", - "\t\tPyImport_ImportModule(\"numpy\");\n", - "\t\t(void)Py_InitModule(\"poly\", polyMethods);\n", - "\t}\n", - "}\n", - "#include \n", - "#include \n", - "#include \n", - "#include \n", - "#include \n", - "void sighandler(int sig) {\n", - "\tstd::ostringstream buffer;\n", - "\tbuffer << \"Abort by \" << (sig == SIGSEGV ? \"segfault\" : \"bus error\") << std::endl;\n", - "\tvoid * stack[64];\n", - "\tstd::size_t depth = backtrace(stack, 64);\n", - "\tif (!depth)\n", - "\t\tbuffer << \" \" << std::endl;\n", - "\telse {\n", - "\t\tchar ** symbols = backtrace_symbols(stack, depth);\n", - "\t\tfor (std::size_t i = 1; i < depth; ++i) {\n", - "\t\t\tstd::string symbol = symbols[i];\n", - "\t\t\t\tif (symbol.find_first_of(' ', 59) != std::string::npos) {\n", - "\t\t\t\t\tstd::string name = symbol.substr(59, symbol.find_first_of(' ', 59) - 59);\n", - "\t\t\t\t\tint status;\n", - "\t\t\t\t\tchar * demangled = abi::__cxa_demangle(name.c_str(), NULL, NULL, &status);\n", - "\t\t\t\t\tif (!status) {\n", - "\t\t\t\t\t\tbuffer << \" \" \n", - "\t\t\t\t\t\t\t<< symbol.substr(0, 59) \n", - "\t\t\t\t\t\t\t<< demangled\n", - "\t\t\t\t\t\t\t<< symbol.substr(59 + name.size())\n", - "\t\t\t\t\t\t\t<< std::endl;\n", - "\t\t\t\t\t\tfree(demangled);\n", - "\t\t\t\t\t} else\n", - "\t\t\t\t\t\tbuffer << \" \" << symbol << std::endl;\n", - "\t\t\t\t} else\n", - "\t\t\t\t\tbuffer << \" \" << symbol << std::endl;\n", - "\t\t\t}\n", - "\t\t\tfree(symbols);\n", - "\t\t}\n", - "\t\tstd::cerr << buffer.str();\n", - "\t\tstd::exit(EXIT_FAILURE);\n", - "\t}" - ], - "language": "python", - "metadata": {}, - "outputs": [ - { - "output_type": "stream", - "stream": "stdout", - "text": [ - "Overwriting src/poly.cpp\n" - ] - } - ], - "prompt_number": 15 - }, - { - "cell_type": "code", - "collapsed": false, - "input": [ - "%%file src/poly_opt.cpp\n", - "#define PY_ARRAY_UNIQUE_SYMBOL poly_ARRAY_API\n", - "#define NPY_NO_DEPRECATED_API NPY_1_7_API_VERSION\n", - "#include \n", - "#include \n", - "#include \n", - "#include \n", - "#include \n", - "#include \n", - "#include \n", - "#include \n", - "#include \n", - "#include \n", - "#include \n", - "struct PyObj {\n", - "\ttypedef PyObject * ptr_t;\n", - "\ttypedef PyArrayObject * arrptr_t;\n", - "\tPyObj(): dec(false), ptr(NULL) {}\n", - "\tPyObj(ptr_t p): dec(false), ptr(p) {}\n", - "\t~PyObj() { if(dec) Py_DECREF(ptr); }\n", - "\tPyObj & operator=(ptr_t p) { if(dec) Py_DECREF(ptr); ptr = p; dec = false; return *this; }\n", - "\tPyObj & incref(ptr_t p) { if(dec) Py_DECREF(ptr); ptr = p; dec = (p != NULL); return *this; }\n", - "\toperator bool() const { return ptr; }\n", - "\toperator ptr_t() const { return ptr; }\n", - "\toperator arrptr_t() const { return (arrptr_t)ptr; }\n", - "\tbool dec;\n", - "\tptr_t ptr;\n", - "};\n", - "\n", - "inline void poly_d1d1(PyObject * pres, npy_intp const * __restrict__ sres, npy_double * __restrict__ cres, \n", - "\t\t\t\t\t\t\t\t\t\t\tPyObject * parg, npy_intp const * __restrict__ sarg, npy_double * __restrict__ carg);\n", - "\n", - "inline void poly_d1d1(PyObject * pres, npy_intp const * __restrict__ sres, npy_double * __restrict__ cres, \n", - "\t\t\t\t\t\t\t\t\t\t\tPyObject * parg, npy_intp const * __restrict__ sarg, npy_double * __restrict__ carg){\n", - "\tfor (npy_intp i0 = 0; i0 < sres[0] - 0; ++i0) {\n", - "\t\tcres[(int)(i0)] = carg[i0];\n", - "\t}\n", - "}\n", - "\n", - "void sighandler(int sig);\n", - "#include \n", - "extern \"C\" {\n", - "\tPyObject * create_signature;\n", - "\tstruct sigaction slot;\n", - "\tPyObject * set_create_signature(PyObject * self, PyObject * args) {\n", - "\t\tif (!PyArg_ParseTuple(args, \"O\", &create_signature)) {\n", - "\t\t\tPyErr_SetString(PyExc_ValueError, \"Invalid Argument to set_create_signature!\");\n", - "\t\t\treturn NULL;\n", - "\t\t}\n", - "\t\tPy_INCREF(create_signature);\n", - "\t\tmemset(&slot, 0, sizeof(slot));\n", - "\t\tslot.sa_handler = &sighandler;\n", - "\t\tsigaction(SIGSEGV, &slot, NULL);\n", - "\t\tsigaction(SIGBUS, &slot, NULL);\n", - "\t\tPy_INCREF(Py_None);\n", - "\t\treturn Py_None;\n", - "\t}\n", - "\tPyObject * run(PyObject * self, PyObject * args) {\n", - "\t\t{\n", - "\t\t\tPyObj pres;\n", - "\t\t\tPyObj parg;\n", - "\t\t\tif (\n", - "\t\t\t\tPyTuple_CheckExact(args) and PyTuple_GET_SIZE(args) == 2\n", - "\t\t\t\tand (pres = PyTuple_GET_ITEM(args, 0)) and PyArray_CheckExact(pres)\n", - "\t\t\t\tand PyArray_TYPE((PyArrayObject *)pres) == NPY_FLOAT64 and PyArray_NDIM((PyArrayObject *)pres) == 1\n", - "\t\t\t\tand (parg = PyTuple_GET_ITEM(args, 1)) and PyArray_CheckExact(parg)\n", - "\t\t\t\tand PyArray_TYPE((PyArrayObject *)parg) == NPY_FLOAT64 and PyArray_NDIM((PyArrayObject *)parg) == 1\n", - "\t\t\t) {\n", - "\t\t\t\tif (!(pres.incref((PyObject *)PyArray_GETCONTIGUOUS((PyArrayObject *)pres)))) {\n", - "\t\t\t\t\tPyErr_SetString(PyExc_ValueError, \"Invalid Argument type on res!\");\n", - "\t\t\t\t\treturn NULL;\n", - "\t\t\t\t}\n", - "\t\t\t\tif (!(parg.incref((PyObject *)PyArray_GETCONTIGUOUS((PyArrayObject *)parg)))) {\n", - "\t\t\t\t\tPyErr_SetString(PyExc_ValueError, \"Invalid Argument type on arg!\");\n", - "\t\t\t\t\treturn NULL;\n", - "\t\t\t\t}\n", - "\t\t\t\ttry {\n", - "\t\t\t\t\tpoly_d1d1(\n", - "\t\t\t\t\t\t pres, PyArray_SHAPE((PyArrayObject *)pres), (npy_double *)PyArray_DATA((PyArrayObject *)pres)\n", - "\t\t\t\t\t\t, parg, PyArray_SHAPE((PyArrayObject *)parg), (npy_double *)PyArray_DATA((PyArrayObject *)parg)\n", - "\t\t\t\t\t);\n", - "\t\t\t\t\tPy_INCREF(Py_None);\n", - "\t\t\t\t\treturn Py_None;\n", - "\t\t\t\t} catch (...) {\n", - "\t\t\t\t\treturn NULL;\n", - "\t\t\t\t}\n", - "\t\t\t} else\n", - "\t\t\t\tPyErr_Clear();\n", - "\t\t}\n", - "\t\tPyObject * signatures = Py_BuildValue(\"(sO)\", \"(lp0\\n(lp1\\nccopy_reg\\n_reconstructor\\np2\\n(chope._ast\\nVariable\\np3\\nc__builtin__\\nobject\\np4\\nNtp5\\nRp6\\n(dp7\\nS'name'\\np8\\nS'res'\\np9\\nsS'dtype'\\np10\\nS'float64'\\np11\\nsS'dims'\\np12\\nI1\\nsbag2\\n(g3\\ng4\\nNtp13\\nRp14\\n(dp15\\ng8\\nS'arg'\\np16\\nsg10\\ng11\\nsg12\\nI1\\nsbaa.\", args);\n", - "\t\tif (!signatures) {\n", - "\t\t\tPyErr_SetString(PyExc_ValueError, \"Error building signature string for poly\");\n", - "\t\t\treturn NULL;\n", - "\t\t}\n", - "\t\treturn PyObject_Call(create_signature, signatures, NULL);\n", - "\t}\n", - "\tPyMethodDef polyMethods[] = {\n", - "\t\t{ \"set_create_signature\", (PyCFunction)set_create_signature, METH_VARARGS },\n", - "\t\t{ \"run\", (PyCFunction)run, METH_VARARGS },\n", - "\t\t{ NULL, NULL }\n", - "\t};\n", - "\tPyMODINIT_FUNC initpoly(void) {\n", - "\t\timport_array();\n", - "\t\tPyImport_ImportModule(\"numpy\");\n", - "\t\t(void)Py_InitModule(\"poly\", polyMethods);\n", - "\t}\n", - "}\n", - "#include \n", - "#include \n", - "#include \n", - "#include \n", - "#include \n", - "void sighandler(int sig) {\n", - "\tstd::ostringstream buffer;\n", - "\tbuffer << \"Abort by \" << (sig == SIGSEGV ? \"segfault\" : \"bus error\") << std::endl;\n", - "\tvoid * stack[64];\n", - "\tstd::size_t depth = backtrace(stack, 64);\n", - "\tif (!depth)\n", - "\t\tbuffer << \" \" << std::endl;\n", - "\telse {\n", - "\t\tchar ** symbols = backtrace_symbols(stack, depth);\n", - "\t\tfor (std::size_t i = 1; i < depth; ++i) {\n", - "\t\t\tstd::string symbol = symbols[i];\n", - "\t\t\t\tif (symbol.find_first_of(' ', 59) != std::string::npos) {\n", - "\t\t\t\t\tstd::string name = symbol.substr(59, symbol.find_first_of(' ', 59) - 59);\n", - "\t\t\t\t\tint status;\n", - "\t\t\t\t\tchar * demangled = abi::__cxa_demangle(name.c_str(), NULL, NULL, &status);\n", - "\t\t\t\t\tif (!status) {\n", - "\t\t\t\t\t\tbuffer << \" \" \n", - "\t\t\t\t\t\t\t<< symbol.substr(0, 59) \n", - "\t\t\t\t\t\t\t<< demangled\n", - "\t\t\t\t\t\t\t<< symbol.substr(59 + name.size())\n", - "\t\t\t\t\t\t\t<< std::endl;\n", - "\t\t\t\t\t\tfree(demangled);\n", - "\t\t\t\t\t} else\n", - "\t\t\t\t\t\tbuffer << \" \" << symbol << std::endl;\n", - "\t\t\t\t} else\n", - "\t\t\t\t\tbuffer << \" \" << symbol << std::endl;\n", - "\t\t\t}\n", - "\t\t\tfree(symbols);\n", - "\t\t}\n", - "\t\tstd::cerr << buffer.str();\n", - "\t\tstd::exit(EXIT_FAILURE);\n", - "\t}\n" - ], - "language": "python", - "metadata": {}, - "outputs": [ - { - "output_type": "stream", - "stream": "stdout", - "text": [ - "Overwriting src/poly_opt.cpp\n" - ] - } - ], - "prompt_number": 7 - }, + } + ], + "source": [ + "%%file src/ln_opt.cpp\n", + "#define PY_ARRAY_UNIQUE_SYMBOL ln_hope_opt_ARRAY_API\n", + "#define NPY_NO_DEPRECATED_API NPY_1_7_API_VERSION\n", + "#include \n", + "#include \n", + "#include \n", + "#include \n", + "#include \n", + "#include \n", + "#include \n", + "#include \n", + "#include \n", + "#include \n", + "#include \n", + "struct PyObj {\n", + "\ttypedef PyObject * ptr_t;\n", + "\ttypedef PyArrayObject * arrptr_t;\n", + "\tPyObj(): dec(false), ptr(NULL) {}\n", + "\tPyObj(ptr_t p): dec(false), ptr(p) {}\n", + "\t~PyObj() { if(dec) Py_DECREF(ptr); }\n", + "\tPyObj & operator=(ptr_t p) { if(dec) Py_DECREF(ptr); ptr = p; dec = false; return *this; }\n", + "\tPyObj & incref(ptr_t p) { if(dec) Py_DECREF(ptr); ptr = p; dec = (p != NULL); return *this; }\n", + "\toperator bool() const { return ptr; }\n", + "\toperator ptr_t() const { return ptr; }\n", + "\toperator arrptr_t() const { return (arrptr_t)ptr; }\n", + "\tbool dec;\n", + "\tptr_t ptr;\n", + "};\n", + "\n", + "inline void ln_hope_opt_d1d1(PyObject * pX, npy_intp const * __restrict__ sX, npy_double * __restrict__ cX, \n", + "\t\t\t\t\t\t\t\t\t\t\t\t\tPyObject * pY, npy_intp const * __restrict__ sY, npy_double * __restrict__ cY);\n", + "\n", + "inline void ln_hope_opt_d1d1(PyObject * pX, npy_intp const * __restrict__ sX, npy_double * __restrict__ cX, \n", + "\t\t\t\t\t\t\t\t\t\t\t\t\tPyObject * pY, npy_intp const * __restrict__ sY, npy_double * __restrict__ cY){\n", + "\t\t\t\t\t\t\t\t\t\t\t\t\t\t\n", + "\tfor (npy_intp i0 = 0; i0 < sY[0] - 0; ++i0) {\n", + "\t\tauto c__sp0 = (cX[i0] * cX[i0]);\n", + "\t\tauto c__sp1 = (c__sp0 * c__sp0);\n", + "\t\tauto c__sp2 = (c__sp1 * c__sp1);\n", + "\t\tauto c__sp3 = (c__sp2 * cX[i0]);\n", + "\t\tauto c__sp4 = (c__sp0 * cX[i0]);\n", + "\t\tauto c__sp5 = (c__sp4 * c__sp4);\n", + "\t\tauto c__sp6 = (c__sp5 * cX[i0]);\n", + "\t\tauto c__sp7 = (c__sp1 * cX[i0]);\n", + "\t\tcY[(int)(i0)] = (-7129.0 / 2520.0) + (28 * c__sp4) + (-(18 * c__sp0)) + (-(9 * c__sp2 / 8)) + (-(14 * c__sp5)) + (-(63 * c__sp1 / 2)) + (126 * c__sp7 / 5) + (9 * cX[i0]) + (c__sp3 / 9) + (36 * c__sp6 / 7);\n", + "\t}\n", + "}\n", + "\n", + "void sighandler(int sig);\n", + "#include \n", + "extern \"C\" {\n", + "\tPyObject * create_signature;\n", + "\tstruct sigaction slot;\n", + "\tPyObject * set_create_signature(PyObject * self, PyObject * args) {\n", + "\t\tif (!PyArg_ParseTuple(args, \"O\", &create_signature)) {\n", + "\t\t\tPyErr_SetString(PyExc_ValueError, \"Invalid Argument to set_create_signature!\");\n", + "\t\t\treturn NULL;\n", + "\t\t}\n", + "\t\tPy_INCREF(create_signature);\n", + "\t\tmemset(&slot, 0, sizeof(slot));\n", + "\t\tslot.sa_handler = &sighandler;\n", + "\t\tsigaction(SIGSEGV, &slot, NULL);\n", + "\t\tsigaction(SIGBUS, &slot, NULL);\n", + "\t\tPy_INCREF(Py_None);\n", + "\t\treturn Py_None;\n", + "\t}\n", + "\tPyObject * run(PyObject * self, PyObject * args) {\n", + "\t\t{\n", + "\t\t\tPyObj pX;\n", + "\t\t\tPyObj pY;\n", + "\t\t\tif (\n", + "\t\t\t\tPyTuple_CheckExact(args) and PyTuple_GET_SIZE(args) == 2\n", + "\t\t\t\tand (pX = PyTuple_GET_ITEM(args, 0)) and PyArray_CheckExact(pX)\n", + "\t\t\t\tand PyArray_TYPE((PyArrayObject *)pX) == NPY_FLOAT64 and PyArray_NDIM((PyArrayObject *)pX) == 1\n", + "\t\t\t\tand (pY = PyTuple_GET_ITEM(args, 1)) and PyArray_CheckExact(pY)\n", + "\t\t\t\tand PyArray_TYPE((PyArrayObject *)pY) == NPY_FLOAT64 and PyArray_NDIM((PyArrayObject *)pY) == 1\n", + "\t\t\t) {\n", + "\t\t\t\tif (!(pX.incref((PyObject *)PyArray_GETCONTIGUOUS((PyArrayObject *)pX)))) {\n", + "\t\t\t\t\tPyErr_SetString(PyExc_ValueError, \"Invalid Argument type on X!\");\n", + "\t\t\t\t\treturn NULL;\n", + "\t\t\t\t}\n", + "\t\t\t\tif (!(pY.incref((PyObject *)PyArray_GETCONTIGUOUS((PyArrayObject *)pY)))) {\n", + "\t\t\t\t\tPyErr_SetString(PyExc_ValueError, \"Invalid Argument type on Y!\");\n", + "\t\t\t\t\treturn NULL;\n", + "\t\t\t\t}\n", + "\t\t\t\ttry {\n", + "\t\t\t\t\tln_hope_opt_d1d1(\n", + "\t\t\t\t\t\t pX, PyArray_SHAPE((PyArrayObject *)pX), (npy_double *)PyArray_DATA((PyArrayObject *)pX)\n", + "\t\t\t\t\t\t, pY, PyArray_SHAPE((PyArrayObject *)pY), (npy_double *)PyArray_DATA((PyArrayObject *)pY)\n", + "\t\t\t\t\t);\n", + "\t\t\t\t\tPy_INCREF(Py_None);\n", + "\t\t\t\t\treturn Py_None;\n", + "\t\t\t\t} catch (...) {\n", + "\t\t\t\t\treturn NULL;\n", + "\t\t\t\t}\n", + "\t\t\t} else\n", + "\t\t\t\tPyErr_Clear();\n", + "\t\t}\n", + "\t\tPyObject * signatures = Py_BuildValue(\"(sO)\", \"(lp0\\n(lp1\\nccopy_reg\\n_reconstructor\\np2\\n(chope._ast\\nVariable\\np3\\nc__builtin__\\nobject\\np4\\nNtp5\\nRp6\\n(dp7\\nS'name'\\np8\\nS'X'\\np9\\nsS'dtype'\\np10\\nS'float64'\\np11\\nsS'dims'\\np12\\nI1\\nsbag2\\n(g3\\ng4\\nNtp13\\nRp14\\n(dp15\\ng8\\nS'Y'\\np16\\nsg10\\ng11\\nsg12\\nI1\\nsbaa.\", args);\n", + "\t\tif (!signatures) {\n", + "\t\t\tPyErr_SetString(PyExc_ValueError, \"Error building signature string for ln_hope_opt\");\n", + "\t\t\treturn NULL;\n", + "\t\t}\n", + "\t\treturn PyObject_Call(create_signature, signatures, NULL);\n", + "\t}\n", + "\tPyMethodDef ln_hope_optMethods[] = {\n", + "\t\t{ \"set_create_signature\", (PyCFunction)set_create_signature, METH_VARARGS },\n", + "\t\t{ \"run\", (PyCFunction)run, METH_VARARGS },\n", + "\t\t{ NULL, NULL }\n", + "\t};\n", + "\tPyMODINIT_FUNC initln_opt(void) {\n", + "\t\timport_array();\n", + "\t\tPyImport_ImportModule(\"numpy\");\n", + "\t\t(void)Py_InitModule(\"ln_opt\", ln_hope_optMethods);\n", + "\t}\n", + "}\n", + "#include \n", + "#include \n", + "#include \n", + "#include \n", + "#include \n", + "void sighandler(int sig) {\n", + "\tstd::ostringstream buffer;\n", + "\tbuffer << \"Abort by \" << (sig == SIGSEGV ? \"segfault\" : \"bus error\") << std::endl;\n", + "\tvoid * stack[64];\n", + "\tstd::size_t depth = backtrace(stack, 64);\n", + "\tif (!depth)\n", + "\t\tbuffer << \" \" << std::endl;\n", + "\telse {\n", + "\t\tchar ** symbols = backtrace_symbols(stack, depth);\n", + "\t\tfor (std::size_t i = 1; i < depth; ++i) {\n", + "\t\t\tstd::string symbol = symbols[i];\n", + "\t\t\t\tif (symbol.find_first_of(' ', 59) != std::string::npos) {\n", + "\t\t\t\t\tstd::string name = symbol.substr(59, symbol.find_first_of(' ', 59) - 59);\n", + "\t\t\t\t\tint status;\n", + "\t\t\t\t\tchar * demangled = abi::__cxa_demangle(name.c_str(), NULL, NULL, &status);\n", + "\t\t\t\t\tif (!status) {\n", + "\t\t\t\t\t\tbuffer << \" \" \n", + "\t\t\t\t\t\t\t<< symbol.substr(0, 59) \n", + "\t\t\t\t\t\t\t<< demangled\n", + "\t\t\t\t\t\t\t<< symbol.substr(59 + name.size())\n", + "\t\t\t\t\t\t\t<< std::endl;\n", + "\t\t\t\t\t\tfree(demangled);\n", + "\t\t\t\t\t} else\n", + "\t\t\t\t\t\tbuffer << \" \" << symbol << std::endl;\n", + "\t\t\t\t} else\n", + "\t\t\t\t\tbuffer << \" \" << symbol << std::endl;\n", + "\t\t\t}\n", + "\t\t\tfree(symbols);\n", + "\t\t}\n", + "\t\tstd::cerr << buffer.str();\n", + "\t\tstd::exit(EXIT_FAILURE);\n", + "\t}\n" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Simplify CPP" + ] + }, + { + "cell_type": "code", + "execution_count": 11, + "metadata": {}, + "outputs": [ { - "cell_type": "heading", - "level": 2, - "metadata": {}, - "source": [ - "Pairwise distance" + "name": "stdout", + "output_type": "stream", + "text": [ + "Overwriting src/poly.cpp\n" ] - }, - { - "cell_type": "code", - "collapsed": false, - "input": [ - "%%file src/pairwise.cpp\n", - "#define PY_ARRAY_UNIQUE_SYMBOL pairwise_ARRAY_API\n", - "#define NPY_NO_DEPRECATED_API NPY_1_7_API_VERSION\n", - "#include \n", - "#include \n", - "#include \n", - "#include \n", - "#include \n", - "#include \n", - "#include \n", - "#include \n", - "#include \n", - "#include \n", - "#include \n", - "struct PyObj {\n", - "\ttypedef PyObject * ptr_t;\n", - "\ttypedef PyArrayObject * arrptr_t;\n", - "\tPyObj(): dec(false), ptr(NULL) {}\n", - "\tPyObj(ptr_t p): dec(false), ptr(p) {}\n", - "\t~PyObj() { if(dec) Py_DECREF(ptr); }\n", - "\tPyObj & operator=(ptr_t p) { if(dec) Py_DECREF(ptr); ptr = p; dec = false; return *this; }\n", - "\tPyObj & incref(ptr_t p) { if(dec) Py_DECREF(ptr); ptr = p; dec = (p != NULL); return *this; }\n", - "\toperator bool() const { return ptr; }\n", - "\toperator ptr_t() const { return ptr; }\n", - "\toperator arrptr_t() const { return (arrptr_t)ptr; }\n", - "\tbool dec;\n", - "\tptr_t ptr;\n", - "};\n", - "\n", - "inline void pairwise_d2d2JJ(PyObject * pX, \n", - "\t\t\t\t\t\t\t\t\t\t\t\t\t\tnpy_intp const * __restrict__ sX, \n", - "\t\t\t\t\t\t\t\t\t\t\t\t\t\tnpy_double * __restrict__ cX, \n", - "\t\t\t\t\t\t\t\t\t\t\t\t\t\tPyObject * pD, \n", - "\t\t\t\t\t\t\t\t\t\t\t\t\t\tnpy_intp const * __restrict__ sD, \n", - "\t\t\t\t\t\t\t\t\t\t\t\t\t\tnpy_double * __restrict__ cD, \n", - "\t\t\t\t\t\t\t\t\t\t\t\t\t\tnpy_int64 const cM, \n", - "\t\t\t\t\t\t\t\t\t\t\t\t\t\tnpy_int64 const cN);\n", - "\t\t\t\t\t\t\t\t\t\t\t\t\t\t\n", - "inline void pairwise_d2d2JJ(PyObject * pX, \n", - "\t\t\t\t\t\t\t\t\t\t\t\t\t\tnpy_intp const * __restrict__ sX, \n", - "\t\t\t\t\t\t\t\t\t\t\t\t\t\tnpy_double * __restrict__ cX, \n", - "\t\t\t\t\t\t\t\t\t\t\t\t\t\tPyObject * pD, \n", - "\t\t\t\t\t\t\t\t\t\t\t\t\t\tnpy_intp const * __restrict__ sD, \n", - "\t\t\t\t\t\t\t\t\t\t\t\t\t\tnpy_double * __restrict__ cD, \n", - "\t\t\t\t\t\t\t\t\t\t\t\t\t\tnpy_int64 const cM, \n", - "\t\t\t\t\t\t\t\t\t\t\t\t\t\tnpy_int64 const cN){\n", - "\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\n", - "\tnpy_double cd = 0.0;\n", - "\tnpy_double ctmp = 0.0;\n", - "\t\n", - "\tnpy_intp cj;\n", - "\tnpy_intp ck;\n", - "\tnpy_intp x_i_idx;\n", - "\tnpy_intp x_j_idx;\n", - "\tnpy_intp d_i_idx;\n", - "\tnpy_intp const xp = sX[1];\n", - "\tnpy_intp const dp = sD[1];\n", - "\t\n", - "\tfor (npy_intp ci = 0; ci < cM; ++ci) {\n", - "\t\tx_i_idx = ci*xp;\n", - "\t\td_i_idx = ci*dp;\n", - "\t\tfor (cj = 0; cj < cM; ++cj) {\n", - "\t\t\tcd = 0.0;\n", - "\t\t\tx_j_idx = cj*xp;\n", - "\t\t\tfor (ck = 0; ck < cN; ++ck) {\n", - "\t\t\t\tctmp = (cX[(x_i_idx + ck)] - cX[(x_j_idx + ck)]);\n", - "\t\t\t\tcd += (ctmp * ctmp);\n", - "\t\t\t}\n", - "\t\t\tcD[(d_i_idx + cj)] = std::sqrt(cd);\n", - "\t\t}\n", - "\t}\n", - "}\n", - "\n", - "void sighandler(int sig);\n", - "#include \n", - "extern \"C\" {\n", - "\tPyObject * create_signature;\n", - "\tstruct sigaction slot;\n", - "\tPyObject * set_create_signature(PyObject * self, PyObject * args) {\n", - "\t\tif (!PyArg_ParseTuple(args, \"O\", &create_signature)) {\n", - "\t\t\tPyErr_SetString(PyExc_ValueError, \"Invalid Argument to set_create_signature!\");\n", - "\t\t\treturn NULL;\n", - "\t\t}\n", - "\t\tPy_INCREF(create_signature);\n", - "\t\tmemset(&slot, 0, sizeof(slot));\n", - "\t\tslot.sa_handler = &sighandler;\n", - "\t\tsigaction(SIGSEGV, &slot, NULL);\n", - "\t\tsigaction(SIGBUS, &slot, NULL);\n", - "\t\tPy_INCREF(Py_None);\n", - "\t\treturn Py_None;\n", - "\t}\n", - "\tPyObject * run(PyObject * self, PyObject * args) {\n", - "\t\t{\n", - "\t\t\tPyObj pX;\n", - "\t\t\tPyObj pD;\n", - "\t\t\tPyObject * pM; npy_int64 cM;\n", - "\t\t\tPyObject * pN; npy_int64 cN;\n", - "\t\t\tif (\n", - "\t\t\t\tPyTuple_CheckExact(args) and PyTuple_GET_SIZE(args) == 4\n", - "\t\t\t\tand (pX = PyTuple_GET_ITEM(args, 0)) and PyArray_CheckExact(pX)\n", - "\t\t\t\tand PyArray_TYPE((PyArrayObject *)pX) == NPY_FLOAT64 and PyArray_NDIM((PyArrayObject *)pX) == 2\n", - "\t\t\t\tand (pD = PyTuple_GET_ITEM(args, 1)) and PyArray_CheckExact(pD)\n", - "\t\t\t\tand PyArray_TYPE((PyArrayObject *)pD) == NPY_FLOAT64 and PyArray_NDIM((PyArrayObject *)pD) == 2\n", - "\t\t\t\tand (pM = PyTuple_GET_ITEM(args, 2)) and PyInt_CheckExact(pM)\n", - "\t\t\t\tand (pN = PyTuple_GET_ITEM(args, 3)) and PyInt_CheckExact(pN)\n", - "\t\t\t) {\n", - "\t\t\t\tif (!(pX.incref((PyObject *)PyArray_GETCONTIGUOUS((PyArrayObject *)pX)))) {\n", - "\t\t\t\t\tPyErr_SetString(PyExc_ValueError, \"Invalid Argument type on X!\");\n", - "\t\t\t\t\treturn NULL;\n", - "\t\t\t\t}\n", - "\t\t\t\tif (!(pD.incref((PyObject *)PyArray_GETCONTIGUOUS((PyArrayObject *)pD)))) {\n", - "\t\t\t\t\tPyErr_SetString(PyExc_ValueError, \"Invalid Argument type on D!\");\n", - "\t\t\t\t\treturn NULL;\n", - "\t\t\t\t}\n", - "\t\t\t\tcM = PyInt_AS_LONG(pM);\n", - "\t\t\t\tcN = PyInt_AS_LONG(pN);\n", - "\t\t\t\ttry {\n", - "\t\t\t\t\tpairwise_d2d2JJ(\n", - "\t\t\t\t\t\t pX, PyArray_SHAPE((PyArrayObject *)pX), (npy_double *)PyArray_DATA((PyArrayObject *)pX)\n", - "\t\t\t\t\t\t, pD, PyArray_SHAPE((PyArrayObject *)pD), (npy_double *)PyArray_DATA((PyArrayObject *)pD)\n", - "\t\t\t\t\t\t, cM\n", - "\t\t\t\t\t\t, cN\n", - "\t\t\t\t\t);\n", - "\t\t\t\t\tPy_INCREF(Py_None);\n", - "\t\t\t\t\treturn Py_None;\n", - "\t\t\t\t} catch (...) {\n", - "\t\t\t\t\treturn NULL;\n", - "\t\t\t\t}\n", - "\t\t\t} else\n", - "\t\t\t\tPyErr_Clear();\n", - "\t\t}\n", - "\t\tPyObject * signatures = Py_BuildValue(\"(sO)\", \"(lp0\\n(lp1\\nccopy_reg\\n_reconstructor\\np2\\n(chope._ast\\nVariable\\np3\\nc__builtin__\\nobject\\np4\\nNtp5\\nRp6\\n(dp7\\nS'name'\\np8\\nS'X'\\np9\\nsS'dtype'\\np10\\nS'float64'\\np11\\nsS'dims'\\np12\\nI2\\nsbag2\\n(g3\\ng4\\nNtp13\\nRp14\\n(dp15\\ng8\\nS'D'\\np16\\nsg10\\ng11\\nsg12\\nI2\\nsbag2\\n(g3\\ng4\\nNtp17\\nRp18\\n(dp19\\ng8\\nS'M'\\np20\\nsg10\\nc__builtin__\\nint\\np21\\nsg12\\nI0\\nsbag2\\n(g3\\ng4\\nNtp22\\nRp23\\n(dp24\\ng8\\nS'N'\\np25\\nsg10\\ng21\\nsg12\\nI0\\nsbaa.\", args);\n", - "\t\tif (!signatures) {\n", - "\t\t\tPyErr_SetString(PyExc_ValueError, \"Error building signature string for pairwise\");\n", - "\t\t\treturn NULL;\n", - "\t\t}\n", - "\t\treturn PyObject_Call(create_signature, signatures, NULL);\n", - "\t}\n", - "\tPyMethodDef pairwiseMethods[] = {\n", - "\t\t{ \"set_create_signature\", (PyCFunction)set_create_signature, METH_VARARGS },\n", - "\t\t{ \"run\", (PyCFunction)run, METH_VARARGS },\n", - "\t\t{ NULL, NULL }\n", - "\t};\n", - "\tPyMODINIT_FUNC initpairwise(void) {\n", - "\t\timport_array();\n", - "\t\tPyImport_ImportModule(\"numpy\");\n", - "\t\t(void)Py_InitModule(\"pairwise\", pairwiseMethods);\n", - "\t}\n", - "}\n", - "#include \n", - "#include \n", - "#include \n", - "#include \n", - "#include \n", - "void sighandler(int sig) {\n", - "\tstd::ostringstream buffer;\n", - "\tbuffer << \"Abort by \" << (sig == SIGSEGV ? \"segfault\" : \"bus error\") << std::endl;\n", - "\tvoid * stack[64];\n", - "\tstd::size_t depth = backtrace(stack, 64);\n", - "\tif (!depth)\n", - "\t\tbuffer << \" \" << std::endl;\n", - "\telse {\n", - "\t\tchar ** symbols = backtrace_symbols(stack, depth);\n", - "\t\tfor (std::size_t i = 1; i < depth; ++i) {\n", - "\t\t\tstd::string symbol = symbols[i];\n", - "\t\t\t\tif (symbol.find_first_of(' ', 59) != std::string::npos) {\n", - "\t\t\t\t\tstd::string name = symbol.substr(59, symbol.find_first_of(' ', 59) - 59);\n", - "\t\t\t\t\tint status;\n", - "\t\t\t\t\tchar * demangled = abi::__cxa_demangle(name.c_str(), NULL, NULL, &status);\n", - "\t\t\t\t\tif (!status) {\n", - "\t\t\t\t\t\tbuffer << \" \" \n", - "\t\t\t\t\t\t\t<< symbol.substr(0, 59) \n", - "\t\t\t\t\t\t\t<< demangled\n", - "\t\t\t\t\t\t\t<< symbol.substr(59 + name.size())\n", - "\t\t\t\t\t\t\t<< std::endl;\n", - "\t\t\t\t\t\tfree(demangled);\n", - "\t\t\t\t\t} else\n", - "\t\t\t\t\t\tbuffer << \" \" << symbol << std::endl;\n", - "\t\t\t\t} else\n", - "\t\t\t\t\tbuffer << \" \" << symbol << std::endl;\n", - "\t\t\t}\n", - "\t\t\tfree(symbols);\n", - "\t\t}\n", - "\t\tstd::cerr << buffer.str();\n", - "\t\tstd::exit(EXIT_FAILURE);\n", - "\t}\n" - ], - "language": "python", - "metadata": {}, - "outputs": [ - { - "output_type": "stream", - "stream": "stdout", - "text": [ - "Overwriting src/pairwise.cpp\n" - ] - } - ], - "prompt_number": 12 - }, + } + ], + "source": [ + "%%file src/poly.cpp\n", + "#define PY_ARRAY_UNIQUE_SYMBOL poly_ARRAY_API\n", + "#define NPY_NO_DEPRECATED_API NPY_1_7_API_VERSION\n", + "#include \n", + "#include \n", + "#include \n", + "#include \n", + "#include \n", + "#include \n", + "#include \n", + "#include \n", + "#include \n", + "#include \n", + "#include \n", + "struct PyObj {\n", + "\ttypedef PyObject * ptr_t;\n", + "\ttypedef PyArrayObject * arrptr_t;\n", + "\tPyObj(): dec(false), ptr(NULL) {}\n", + "\tPyObj(ptr_t p): dec(false), ptr(p) {}\n", + "\t~PyObj() { if(dec) Py_DECREF(ptr); }\n", + "\tPyObj & operator=(ptr_t p) { if(dec) Py_DECREF(ptr); ptr = p; dec = false; return *this; }\n", + "\tPyObj & incref(ptr_t p) { if(dec) Py_DECREF(ptr); ptr = p; dec = (p != NULL); return *this; }\n", + "\toperator bool() const { return ptr; }\n", + "\toperator ptr_t() const { return ptr; }\n", + "\toperator arrptr_t() const { return (arrptr_t)ptr; }\n", + "\tbool dec;\n", + "\tptr_t ptr;\n", + "};\n", + "inline void poly_d1d1(PyObject * pres, npy_intp const * __restrict__ sres, npy_double * __restrict__ cres, \n", + "\t\t\t\t\t\t\t\t\t\t\tPyObject * parg, npy_intp const * __restrict__ sarg, npy_double * __restrict__ carg);\n", + "\n", + "inline void poly_d1d1(PyObject * pres, npy_intp const * __restrict__ sres, npy_double * __restrict__ cres, \n", + "\t\t\t\t\t\t\t\t\t\t\tPyObject * parg, npy_intp const * __restrict__ sarg, npy_double * __restrict__ carg){\n", + "\t\n", + "\tnpy_double arg_i;\n", + "\tdouble sin_arg_i;\n", + "\tfor (npy_intp i0 = 0; i0 < sres[0] - 0; ++i0) {\n", + "\t\targ_i = carg[i0];\n", + "\t\tcres[(int)(i0)] = std::pow(std::sin(arg_i), 2) + (std::pow(arg_i, 3) + std::pow(arg_i, 2) - arg_i - 1) / (std::pow(arg_i, 2) + 2 * arg_i + 1) + std::pow(std::cos(arg_i), 2);\n", + "\t}\n", + "}\n", + "void sighandler(int sig);\n", + "#include \n", + "extern \"C\" {\n", + "\tPyObject * create_signature;\n", + "\tstruct sigaction slot;\n", + "\tPyObject * set_create_signature(PyObject * self, PyObject * args) {\n", + "\t\tif (!PyArg_ParseTuple(args, \"O\", &create_signature)) {\n", + "\t\t\tPyErr_SetString(PyExc_ValueError, \"Invalid Argument to set_create_signature!\");\n", + "\t\t\treturn NULL;\n", + "\t\t}\n", + "\t\tPy_INCREF(create_signature);\n", + "\t\tmemset(&slot, 0, sizeof(slot));\n", + "\t\tslot.sa_handler = &sighandler;\n", + "\t\tsigaction(SIGSEGV, &slot, NULL);\n", + "\t\tsigaction(SIGBUS, &slot, NULL);\n", + "\t\tPy_INCREF(Py_None);\n", + "\t\treturn Py_None;\n", + "\t}\n", + "\tPyObject * run(PyObject * self, PyObject * args) {\n", + "\t\t{\n", + "\t\t\tPyObj pres;\n", + "\t\t\tPyObj parg;\n", + "\t\t\tif (\n", + "\t\t\t\tPyTuple_CheckExact(args) and PyTuple_GET_SIZE(args) == 2\n", + "\t\t\t\tand (pres = PyTuple_GET_ITEM(args, 0)) and PyArray_CheckExact(pres)\n", + "\t\t\t\tand PyArray_TYPE((PyArrayObject *)pres) == NPY_FLOAT64 and PyArray_NDIM((PyArrayObject *)pres) == 1\n", + "\t\t\t\tand (parg = PyTuple_GET_ITEM(args, 1)) and PyArray_CheckExact(parg)\n", + "\t\t\t\tand PyArray_TYPE((PyArrayObject *)parg) == NPY_FLOAT64 and PyArray_NDIM((PyArrayObject *)parg) == 1\n", + "\t\t\t) {\n", + "\t\t\t\tif (!(pres.incref((PyObject *)PyArray_GETCONTIGUOUS((PyArrayObject *)pres)))) {\n", + "\t\t\t\t\tPyErr_SetString(PyExc_ValueError, \"Invalid Argument type on res!\");\n", + "\t\t\t\t\treturn NULL;\n", + "\t\t\t\t}\n", + "\t\t\t\tif (!(parg.incref((PyObject *)PyArray_GETCONTIGUOUS((PyArrayObject *)parg)))) {\n", + "\t\t\t\t\tPyErr_SetString(PyExc_ValueError, \"Invalid Argument type on arg!\");\n", + "\t\t\t\t\treturn NULL;\n", + "\t\t\t\t}\n", + "\t\t\t\ttry {\n", + "\t\t\t\t\tpoly_d1d1(\n", + "\t\t\t\t\t\t pres, PyArray_SHAPE((PyArrayObject *)pres), (npy_double *)PyArray_DATA((PyArrayObject *)pres)\n", + "\t\t\t\t\t\t, parg, PyArray_SHAPE((PyArrayObject *)parg), (npy_double *)PyArray_DATA((PyArrayObject *)parg)\n", + "\t\t\t\t\t);\n", + "\t\t\t\t\tPy_INCREF(Py_None);\n", + "\t\t\t\t\treturn Py_None;\n", + "\t\t\t\t} catch (...) {\n", + "\t\t\t\t\treturn NULL;\n", + "\t\t\t\t}\n", + "\t\t\t} else\n", + "\t\t\t\tPyErr_Clear();\n", + "\t\t}\n", + "\t\tPyObject * signatures = Py_BuildValue(\"(sO)\", \"(lp0\\n(lp1\\nccopy_reg\\n_reconstructor\\np2\\n(chope._ast\\nVariable\\np3\\nc__builtin__\\nobject\\np4\\nNtp5\\nRp6\\n(dp7\\nS'name'\\np8\\nS'res'\\np9\\nsS'dtype'\\np10\\nS'float64'\\np11\\nsS'dims'\\np12\\nI1\\nsbag2\\n(g3\\ng4\\nNtp13\\nRp14\\n(dp15\\ng8\\nS'arg'\\np16\\nsg10\\ng11\\nsg12\\nI1\\nsbaa.\", args);\n", + "\t\tif (!signatures) {\n", + "\t\t\tPyErr_SetString(PyExc_ValueError, \"Error building signature string for poly\");\n", + "\t\t\treturn NULL;\n", + "\t\t}\n", + "\t\treturn PyObject_Call(create_signature, signatures, NULL);\n", + "\t}\n", + "\tPyMethodDef polyMethods[] = {\n", + "\t\t{ \"set_create_signature\", (PyCFunction)set_create_signature, METH_VARARGS },\n", + "\t\t{ \"run\", (PyCFunction)run, METH_VARARGS },\n", + "\t\t{ NULL, NULL }\n", + "\t};\n", + "\tPyMODINIT_FUNC initpoly(void) {\n", + "\t\timport_array();\n", + "\t\tPyImport_ImportModule(\"numpy\");\n", + "\t\t(void)Py_InitModule(\"poly\", polyMethods);\n", + "\t}\n", + "}\n", + "#include \n", + "#include \n", + "#include \n", + "#include \n", + "#include \n", + "void sighandler(int sig) {\n", + "\tstd::ostringstream buffer;\n", + "\tbuffer << \"Abort by \" << (sig == SIGSEGV ? \"segfault\" : \"bus error\") << std::endl;\n", + "\tvoid * stack[64];\n", + "\tstd::size_t depth = backtrace(stack, 64);\n", + "\tif (!depth)\n", + "\t\tbuffer << \" \" << std::endl;\n", + "\telse {\n", + "\t\tchar ** symbols = backtrace_symbols(stack, depth);\n", + "\t\tfor (std::size_t i = 1; i < depth; ++i) {\n", + "\t\t\tstd::string symbol = symbols[i];\n", + "\t\t\t\tif (symbol.find_first_of(' ', 59) != std::string::npos) {\n", + "\t\t\t\t\tstd::string name = symbol.substr(59, symbol.find_first_of(' ', 59) - 59);\n", + "\t\t\t\t\tint status;\n", + "\t\t\t\t\tchar * demangled = abi::__cxa_demangle(name.c_str(), NULL, NULL, &status);\n", + "\t\t\t\t\tif (!status) {\n", + "\t\t\t\t\t\tbuffer << \" \" \n", + "\t\t\t\t\t\t\t<< symbol.substr(0, 59) \n", + "\t\t\t\t\t\t\t<< demangled\n", + "\t\t\t\t\t\t\t<< symbol.substr(59 + name.size())\n", + "\t\t\t\t\t\t\t<< std::endl;\n", + "\t\t\t\t\t\tfree(demangled);\n", + "\t\t\t\t\t} else\n", + "\t\t\t\t\t\tbuffer << \" \" << symbol << std::endl;\n", + "\t\t\t\t} else\n", + "\t\t\t\t\tbuffer << \" \" << symbol << std::endl;\n", + "\t\t\t}\n", + "\t\t\tfree(symbols);\n", + "\t\t}\n", + "\t\tstd::cerr << buffer.str();\n", + "\t\tstd::exit(EXIT_FAILURE);\n", + "\t}" + ] + }, + { + "cell_type": "code", + "execution_count": 12, + "metadata": {}, + "outputs": [ { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Star point spread function CPP" + "name": "stdout", + "output_type": "stream", + "text": [ + "Overwriting src/poly_opt.cpp\n" ] - }, - { - "cell_type": "code", - "collapsed": false, - "input": [ - "%%file src/pdf.cpp\n", - "#define PY_ARRAY_UNIQUE_SYMBOL pdf_ARRAY_API\n", - "#define NPY_NO_DEPRECATED_API NPY_1_7_API_VERSION\n", - "#include \n", - "#include \n", - "#include \n", - "#include \n", - "#include \n", - "#include \n", - "#include \n", - "#include \n", - "#include \n", - "#include \n", - "#include \n", - "struct PyObj {\n", - "\ttypedef PyObject * ptr_t;\n", - "\ttypedef PyArrayObject * arrptr_t;\n", - "\tPyObj(): dec(false), ptr(NULL) {}\n", - "\tPyObj(ptr_t p): dec(false), ptr(p) {}\n", - "\t~PyObj() { if(dec) Py_DECREF(ptr); }\n", - "\tPyObj & operator=(ptr_t p) { if(dec) Py_DECREF(ptr); ptr = p; dec = false; return *this; }\n", - "\tPyObj & incref(ptr_t p) { if(dec) Py_DECREF(ptr); ptr = p; dec = (p != NULL); return *this; }\n", - "\toperator bool() const { return ptr; }\n", - "\toperator ptr_t() const { return ptr; }\n", - "\toperator arrptr_t() const { return (arrptr_t)ptr; }\n", - "\tbool dec;\n", - "\tptr_t ptr;\n", - "};\n", - "\n", - "inline std::tuple pdf_f2l1d1f2JDd(\n", - " \tPyObject * pdensity\n", - " , npy_intp const * __restrict__ sdensity\n", - " , npy_float * __restrict__ cdensity\n", - "\t, PyObject * pdims\n", - "\t, npy_intp const * __restrict__ sdims\n", - "\t, npy_int64 * __restrict__ cdims\n", - "\t, PyObject * pcenter\n", - "\t, npy_intp const * __restrict__ scenter\n", - "\t, npy_double * __restrict__ ccenter\n", - "\t, PyObject * pw2D\n", - "\t, npy_intp const * __restrict__ sw2D\n", - "\t, npy_float * __restrict__ cw2D\n", - "\t, npy_int64 cr50\n", - "\t, npy_double cb\n", - "\t, npy_double ca);\n", - "\n", - "inline std::tuple pdf_f2l1d1f2JDd(\n", - " \tPyObject * pdensity\n", - " , npy_intp const * __restrict__ sdensity\n", - " , npy_float * __restrict__ cdensity\n", - "\t, PyObject * pdims\n", - "\t, npy_intp const * __restrict__ sdims\n", - "\t, npy_int64 * __restrict__ cdims\n", - "\t, PyObject * pcenter\n", - "\t, npy_intp const * __restrict__ scenter\n", - "\t, npy_double * __restrict__ ccenter\n", - "\t, PyObject * pw2D\n", - "\t, npy_intp const * __restrict__ sw2D\n", - "\t, npy_float * __restrict__ cw2D\n", - "\t, npy_int64 const cr50\n", - "\t, npy_double const cb\n", - "\t, npy_double const ca) {\n", - "\t\n", - "\tnpy_double cdr;\n", - "\tnpy_double c__sum0;\n", - "\tconst npy_double x_center = ccenter[0];\n", - "\tconst npy_double y_center = ccenter[1];\n", - "\tconst npy_intp len_0_sw2D = sw2D[0];\n", - "\tconst npy_intp len_1_sw2D = sw2D[1];\n", - "\tnpy_intp dc[] = {len_0_sw2D, len_1_sw2D};\n", - "\tPyObject * pc = PyArray_EMPTY(2, dc, NPY_FLOAT64, 0);\n", - "\tnpy_intp * sc = PyArray_SHAPE((PyArrayObject *)pc);\n", - "\tnpy_double * cc = (npy_double *)PyArray_DATA((PyArrayObject *)pc);\n", - "\t\n", - "\tnpy_intp cy = 0;\n", - "\tnpy_intp i0 = 0;\n", - "\tnpy_intp i1 = 0;\n", - "\tnpy_intp i2 = 0;\n", - "\tnpy_intp i3 = 0;\n", - "\t\n", - "\tnpy_intp sw2D_i0_idx;\n", - "\tnpy_intp sw2D_i2_idx;\n", - "\tnpy_intp density_x_idx;\n", - "\t\n", - "\tauto c__sp0 = ca * ca;\n", - "\tauto c__sp1 = cr50 * cr50;\n", - "\tauto c__sp2 = 1.0 / (c__sp0 * c__sp1);\n", - "\tauto c__sp4 = 0.3183098861846737 * c__sp2 * (-1 + cb);\n", - "\n", - "\tfor (npy_intp cx = 0; cx < cdims[(int)(0)]; ++cx) {\n", - "\t\t\n", - "\t\tdensity_x_idx = cx*sdensity[1];\n", - "\t\tfor (cy = 0; cy < cdims[(int)(1)]; ++cy) {\n", - "\t\t\t\n", - "\t\t\tcdr = std::sqrt(std::pow(cx - x_center, 2) + std::pow(cy - y_center, 2));\n", - "\t\t\tauto c__sp3 = cdr * cdr;\n", - "\t\t\tauto c__sp5 = c__sp4 * std::pow((1 + c__sp2 * c__sp3), -cb);\n", - "\t\t\t\n", - "\t\t\tfor (i0 = 0; i0 < len_0_sw2D - 0; ++i0) {\n", - "\t\t\t\t\n", - "\t\t\t\tsw2D_i0_idx = (i0)*len_1_sw2D;\n", - "\t\t\t\tfor (i1 = 0; i1 < len_1_sw2D - 0; ++i1) {\n", - "\t\t\t\t\tcc[sw2D_i0_idx + i1] = c__sp5 * cw2D[sw2D_i0_idx + i1];\n", - "\t\t\t\t}\n", - "\t\t\t}\n", - "\t\t\t\n", - "\t\t\tc__sum0 = 0;\n", - "\t\t\tfor (i2 = 0; i2 < len_0_sw2D - 0; ++i2) {\n", - "\t\t\t\t\n", - "\t\t\t\tsw2D_i2_idx = (i2)*len_1_sw2D;\n", - "\t\t\t\tfor (i3 = 0; i3 < len_1_sw2D - 0; ++i3) {\n", - "\t\t\t\t\tc__sum0 += cc[sw2D_i2_idx + i3];\n", - "\t\t\t\t}\n", - "\t\t\t}\n", - "\t\t\t\n", - "\t\t\tcdensity[(density_x_idx + cy)] = c__sum0;\n", - "\t\t}\n", - "\t}\n", - "\treturn std::make_tuple((PyObject *)pdensity, sdensity, cdensity);\n", - "}\n", - "\n", - "void sighandler(int sig);\n", - "#include \n", - "extern \"C\" {\n", - "\tPyObject * create_signature;\n", - "\tstruct sigaction slot;\n", - "\tPyObject * set_create_signature(PyObject * self, PyObject * args) {\n", - "\t\tif (!PyArg_ParseTuple(args, \"O\", &create_signature)) {\n", - "\t\t\tPyErr_SetString(PyExc_ValueError, \"Invalid Argument to set_create_signature!\");\n", - "\t\t\treturn NULL;\n", - "\t\t}\n", - "\t\tPy_INCREF(create_signature);\n", - "\t\tmemset(&slot, 0, sizeof(slot));\n", - "\t\tslot.sa_handler = &sighandler;\n", - "\t\tsigaction(SIGSEGV, &slot, NULL);\n", - "\t\tsigaction(SIGBUS, &slot, NULL);\n", - "\t\tPy_INCREF(Py_None);\n", - "\t\treturn Py_None;\n", - "\t}\n", - "\tPyObject * run(PyObject * self, PyObject * args) {\n", - "\t\t{\n", - "\t\t\tPyObj pdensity;\n", - "\t\t\tPyObj pdims;\n", - "\t\t\tPyObj pcenter;\n", - "\t\t\tPyObj pw2D;\n", - "\t\t\tPyObject * pr50; npy_int64 cr50;\n", - "\t\t\tPyObject * pb; npy_double cb;\n", - "\t\t\tPyObject * pa; npy_double ca;\n", - "\t\t\tif (\n", - "\t\t\t\tPyTuple_CheckExact(args) and PyTuple_GET_SIZE(args) == 7\n", - "\t\t\t\tand (pdensity = PyTuple_GET_ITEM(args, 0)) and PyArray_CheckExact(pdensity)\n", - "\t\t\t\tand PyArray_TYPE((PyArrayObject *)pdensity) == NPY_FLOAT32 and PyArray_NDIM((PyArrayObject *)pdensity) == 2\n", - "\t\t\t\tand (pdims = PyTuple_GET_ITEM(args, 1)) and PyArray_CheckExact(pdims)\n", - "\t\t\t\tand PyArray_TYPE((PyArrayObject *)pdims) == NPY_INT64 and PyArray_NDIM((PyArrayObject *)pdims) == 1\n", - "\t\t\t\tand (pcenter = PyTuple_GET_ITEM(args, 2)) and PyArray_CheckExact(pcenter)\n", - "\t\t\t\tand PyArray_TYPE((PyArrayObject *)pcenter) == NPY_FLOAT64 and PyArray_NDIM((PyArrayObject *)pcenter) == 1\n", - "\t\t\t\tand (pw2D = PyTuple_GET_ITEM(args, 3)) and PyArray_CheckExact(pw2D)\n", - "\t\t\t\tand PyArray_TYPE((PyArrayObject *)pw2D) == NPY_FLOAT32 and PyArray_NDIM((PyArrayObject *)pw2D) == 2\n", - "\t\t\t\tand (pr50 = PyTuple_GET_ITEM(args, 4)) and PyInt_CheckExact(pr50)\n", - "\t\t\t\tand (pb = PyTuple_GET_ITEM(args, 5)) and PyFloat_CheckExact(pb)\n", - "\t\t\t\tand (pa = PyTuple_GET_ITEM(args, 6)) and PyArray_IsScalar(pa, Double)\n", - "\t\t\t) {\n", - "\t\t\t\tif (!(pdensity.incref((PyObject *)PyArray_GETCONTIGUOUS((PyArrayObject *)pdensity)))) {\n", - "\t\t\t\t\tPyErr_SetString(PyExc_ValueError, \"Invalid Argument type on density!\");\n", - "\t\t\t\t\treturn NULL;\n", - "\t\t\t\t}\n", - "\t\t\t\tif (!(pdims.incref((PyObject *)PyArray_GETCONTIGUOUS((PyArrayObject *)pdims)))) {\n", - "\t\t\t\t\tPyErr_SetString(PyExc_ValueError, \"Invalid Argument type on dims!\");\n", - "\t\t\t\t\treturn NULL;\n", - "\t\t\t\t}\n", - "\t\t\t\tif (!(pcenter.incref((PyObject *)PyArray_GETCONTIGUOUS((PyArrayObject *)pcenter)))) {\n", - "\t\t\t\t\tPyErr_SetString(PyExc_ValueError, \"Invalid Argument type on center!\");\n", - "\t\t\t\t\treturn NULL;\n", - "\t\t\t\t}\n", - "\t\t\t\tif (!(pw2D.incref((PyObject *)PyArray_GETCONTIGUOUS((PyArrayObject *)pw2D)))) {\n", - "\t\t\t\t\tPyErr_SetString(PyExc_ValueError, \"Invalid Argument type on w2D!\");\n", - "\t\t\t\t\treturn NULL;\n", - "\t\t\t\t}\n", - "\t\t\t\tcr50 = PyInt_AS_LONG(pr50);\n", - "\t\t\t\tcb = PyFloat_AS_DOUBLE(pb);\n", - "\t\t\t\tca = PyArrayScalar_VAL(pa, Double);\n", - "\t\t\t\ttry {\n", - "\t\t\t\t\tPyObject * res = std::get<0>(pdf_f2l1d1f2JDd(\n", - "\t\t\t\t\t\t pdensity, PyArray_SHAPE((PyArrayObject *)pdensity), (npy_float *)PyArray_DATA((PyArrayObject *)pdensity)\n", - "\t\t\t\t\t\t, pdims, PyArray_SHAPE((PyArrayObject *)pdims), (npy_int64 *)PyArray_DATA((PyArrayObject *)pdims)\n", - "\t\t\t\t\t\t, pcenter, PyArray_SHAPE((PyArrayObject *)pcenter), (npy_double *)PyArray_DATA((PyArrayObject *)pcenter)\n", - "\t\t\t\t\t\t, pw2D, PyArray_SHAPE((PyArrayObject *)pw2D), (npy_float *)PyArray_DATA((PyArrayObject *)pw2D)\n", - "\t\t\t\t\t\t, cr50\n", - "\t\t\t\t\t\t, cb\n", - "\t\t\t\t\t\t, ca\n", - "\t\t\t\t\t));\n", - "\n", - "\t\t\t\t\tPy_INCREF(res);\n", - "\t\t\t\t\treturn res;\n", - "\t\t\t\t} catch (...) {\n", - "\t\t\t\t\treturn NULL;\n", - "\t\t\t\t}\n", - "\t\t\t} else\n", - "\t\t\t\tPyErr_Clear();\n", - "\t\t}\n", - "\t\tPyObject * signatures = Py_BuildValue(\"(sO)\", \"(lp0\\n(lp1\\nccopy_reg\\n_reconstructor\\np2\\n(chope._ast\\nVariable\\np3\\nc__builtin__\\nobject\\np4\\nNtp5\\nRp6\\n(dp7\\nS'name'\\np8\\nS'density'\\np9\\nsS'dtype'\\np10\\nS'float32'\\np11\\nsS'dims'\\np12\\nI2\\nsbag2\\n(g3\\ng4\\nNtp13\\nRp14\\n(dp15\\ng8\\ng12\\nsg10\\nS'int64'\\np16\\nsg12\\nI1\\nsbag2\\n(g3\\ng4\\nNtp17\\nRp18\\n(dp19\\ng8\\nS'center'\\np20\\nsg10\\nS'float64'\\np21\\nsg12\\nI1\\nsbag2\\n(g3\\ng4\\nNtp22\\nRp23\\n(dp24\\ng8\\nS'w2D'\\np25\\nsg10\\ng11\\nsg12\\nI2\\nsbag2\\n(g3\\ng4\\nNtp26\\nRp27\\n(dp28\\ng8\\nS'r50'\\np29\\nsg10\\nc__builtin__\\nint\\np30\\nsg12\\nI0\\nsbag2\\n(g3\\ng4\\nNtp31\\nRp32\\n(dp33\\ng8\\nS'b'\\np34\\nsg10\\nc__builtin__\\nfloat\\np35\\nsg12\\nI0\\nsbag2\\n(g3\\ng4\\nNtp36\\nRp37\\n(dp38\\ng8\\nS'a'\\np39\\nsg10\\ng21\\nsg12\\nI0\\nsbaa.\", args);\n", - "\t\tif (!signatures) {\n", - "\t\t\tPyErr_SetString(PyExc_ValueError, \"Error building signature string for pdf\");\n", - "\t\t\treturn NULL;\n", - "\t\t}\n", - "\t\treturn PyObject_Call(create_signature, signatures, NULL);\n", - "\t}\n", - "\tPyMethodDef pdfMethods[] = {\n", - "\t\t{ \"set_create_signature\", (PyCFunction)set_create_signature, METH_VARARGS },\n", - "\t\t{ \"run\", (PyCFunction)run, METH_VARARGS },\n", - "\t\t{ NULL, NULL }\n", - "\t};\n", - "\tPyMODINIT_FUNC initpdf(void) {\n", - "\t\timport_array();\n", - "\t\tPyImport_ImportModule(\"numpy\");\n", - "\t\t(void)Py_InitModule(\"pdf\", pdfMethods);\n", - "\t}\n", - "}\n", - "#include \n", - "#include \n", - "#include \n", - "#include \n", - "#include \n", - "void sighandler(int sig) {\n", - "\tstd::ostringstream buffer;\n", - "\tbuffer << \"Abort by \" << (sig == SIGSEGV ? \"segfault\" : \"bus error\") << std::endl;\n", - "\tvoid * stack[64];\n", - "\tstd::size_t depth = backtrace(stack, 64);\n", - "\tif (!depth)\n", - "\t\tbuffer << \" \" << std::endl;\n", - "\telse {\n", - "\t\tchar ** symbols = backtrace_symbols(stack, depth);\n", - "\t\tfor (std::size_t i = 1; i < depth; ++i) {\n", - "\t\t\tstd::string symbol = symbols[i];\n", - "\t\t\t\tif (symbol.find_first_of(' ', 59) != std::string::npos) {\n", - "\t\t\t\t\tstd::string name = symbol.substr(59, symbol.find_first_of(' ', 59) - 59);\n", - "\t\t\t\t\tint status;\n", - "\t\t\t\t\tchar * demangled = abi::__cxa_demangle(name.c_str(), NULL, NULL, &status);\n", - "\t\t\t\t\tif (!status) {\n", - "\t\t\t\t\t\tbuffer << \" \" \n", - "\t\t\t\t\t\t\t<< symbol.substr(0, 59) \n", - "\t\t\t\t\t\t\t<< demangled\n", - "\t\t\t\t\t\t\t<< symbol.substr(59 + name.size())\n", - "\t\t\t\t\t\t\t<< std::endl;\n", - "\t\t\t\t\t\tfree(demangled);\n", - "\t\t\t\t\t} else\n", - "\t\t\t\t\t\tbuffer << \" \" << symbol << std::endl;\n", - "\t\t\t\t} else\n", - "\t\t\t\t\tbuffer << \" \" << symbol << std::endl;\n", - "\t\t\t}\n", - "\t\t\tfree(symbols);\n", - "\t\t}\n", - "\t\tstd::cerr << buffer.str();\n", - "\t\tstd::exit(EXIT_FAILURE);\n", - "\t}\n" - ], - "language": "python", - "metadata": {}, - "outputs": [ - { - "output_type": "stream", - "stream": "stdout", - "text": [ - "Overwriting src/pdf.cpp\n" - ] - } - ], - "prompt_number": 14 - }, + } + ], + "source": [ + "%%file src/poly_opt.cpp\n", + "#define PY_ARRAY_UNIQUE_SYMBOL poly_ARRAY_API\n", + "#define NPY_NO_DEPRECATED_API NPY_1_7_API_VERSION\n", + "#include \n", + "#include \n", + "#include \n", + "#include \n", + "#include \n", + "#include \n", + "#include \n", + "#include \n", + "#include \n", + "#include \n", + "#include \n", + "struct PyObj {\n", + "\ttypedef PyObject * ptr_t;\n", + "\ttypedef PyArrayObject * arrptr_t;\n", + "\tPyObj(): dec(false), ptr(NULL) {}\n", + "\tPyObj(ptr_t p): dec(false), ptr(p) {}\n", + "\t~PyObj() { if(dec) Py_DECREF(ptr); }\n", + "\tPyObj & operator=(ptr_t p) { if(dec) Py_DECREF(ptr); ptr = p; dec = false; return *this; }\n", + "\tPyObj & incref(ptr_t p) { if(dec) Py_DECREF(ptr); ptr = p; dec = (p != NULL); return *this; }\n", + "\toperator bool() const { return ptr; }\n", + "\toperator ptr_t() const { return ptr; }\n", + "\toperator arrptr_t() const { return (arrptr_t)ptr; }\n", + "\tbool dec;\n", + "\tptr_t ptr;\n", + "};\n", + "\n", + "inline void poly_d1d1(PyObject * pres, npy_intp const * __restrict__ sres, npy_double * __restrict__ cres, \n", + "\t\t\t\t\t\t\t\t\t\t\tPyObject * parg, npy_intp const * __restrict__ sarg, npy_double * __restrict__ carg);\n", + "\n", + "inline void poly_d1d1(PyObject * pres, npy_intp const * __restrict__ sres, npy_double * __restrict__ cres, \n", + "\t\t\t\t\t\t\t\t\t\t\tPyObject * parg, npy_intp const * __restrict__ sarg, npy_double * __restrict__ carg){\n", + "\tfor (npy_intp i0 = 0; i0 < sres[0] - 0; ++i0) {\n", + "\t\tcres[(int)(i0)] = carg[i0];\n", + "\t}\n", + "}\n", + "\n", + "void sighandler(int sig);\n", + "#include \n", + "extern \"C\" {\n", + "\tPyObject * create_signature;\n", + "\tstruct sigaction slot;\n", + "\tPyObject * set_create_signature(PyObject * self, PyObject * args) {\n", + "\t\tif (!PyArg_ParseTuple(args, \"O\", &create_signature)) {\n", + "\t\t\tPyErr_SetString(PyExc_ValueError, \"Invalid Argument to set_create_signature!\");\n", + "\t\t\treturn NULL;\n", + "\t\t}\n", + "\t\tPy_INCREF(create_signature);\n", + "\t\tmemset(&slot, 0, sizeof(slot));\n", + "\t\tslot.sa_handler = &sighandler;\n", + "\t\tsigaction(SIGSEGV, &slot, NULL);\n", + "\t\tsigaction(SIGBUS, &slot, NULL);\n", + "\t\tPy_INCREF(Py_None);\n", + "\t\treturn Py_None;\n", + "\t}\n", + "\tPyObject * run(PyObject * self, PyObject * args) {\n", + "\t\t{\n", + "\t\t\tPyObj pres;\n", + "\t\t\tPyObj parg;\n", + "\t\t\tif (\n", + "\t\t\t\tPyTuple_CheckExact(args) and PyTuple_GET_SIZE(args) == 2\n", + "\t\t\t\tand (pres = PyTuple_GET_ITEM(args, 0)) and PyArray_CheckExact(pres)\n", + "\t\t\t\tand PyArray_TYPE((PyArrayObject *)pres) == NPY_FLOAT64 and PyArray_NDIM((PyArrayObject *)pres) == 1\n", + "\t\t\t\tand (parg = PyTuple_GET_ITEM(args, 1)) and PyArray_CheckExact(parg)\n", + "\t\t\t\tand PyArray_TYPE((PyArrayObject *)parg) == NPY_FLOAT64 and PyArray_NDIM((PyArrayObject *)parg) == 1\n", + "\t\t\t) {\n", + "\t\t\t\tif (!(pres.incref((PyObject *)PyArray_GETCONTIGUOUS((PyArrayObject *)pres)))) {\n", + "\t\t\t\t\tPyErr_SetString(PyExc_ValueError, \"Invalid Argument type on res!\");\n", + "\t\t\t\t\treturn NULL;\n", + "\t\t\t\t}\n", + "\t\t\t\tif (!(parg.incref((PyObject *)PyArray_GETCONTIGUOUS((PyArrayObject *)parg)))) {\n", + "\t\t\t\t\tPyErr_SetString(PyExc_ValueError, \"Invalid Argument type on arg!\");\n", + "\t\t\t\t\treturn NULL;\n", + "\t\t\t\t}\n", + "\t\t\t\ttry {\n", + "\t\t\t\t\tpoly_d1d1(\n", + "\t\t\t\t\t\t pres, PyArray_SHAPE((PyArrayObject *)pres), (npy_double *)PyArray_DATA((PyArrayObject *)pres)\n", + "\t\t\t\t\t\t, parg, PyArray_SHAPE((PyArrayObject *)parg), (npy_double *)PyArray_DATA((PyArrayObject *)parg)\n", + "\t\t\t\t\t);\n", + "\t\t\t\t\tPy_INCREF(Py_None);\n", + "\t\t\t\t\treturn Py_None;\n", + "\t\t\t\t} catch (...) {\n", + "\t\t\t\t\treturn NULL;\n", + "\t\t\t\t}\n", + "\t\t\t} else\n", + "\t\t\t\tPyErr_Clear();\n", + "\t\t}\n", + "\t\tPyObject * signatures = Py_BuildValue(\"(sO)\", \"(lp0\\n(lp1\\nccopy_reg\\n_reconstructor\\np2\\n(chope._ast\\nVariable\\np3\\nc__builtin__\\nobject\\np4\\nNtp5\\nRp6\\n(dp7\\nS'name'\\np8\\nS'res'\\np9\\nsS'dtype'\\np10\\nS'float64'\\np11\\nsS'dims'\\np12\\nI1\\nsbag2\\n(g3\\ng4\\nNtp13\\nRp14\\n(dp15\\ng8\\nS'arg'\\np16\\nsg10\\ng11\\nsg12\\nI1\\nsbaa.\", args);\n", + "\t\tif (!signatures) {\n", + "\t\t\tPyErr_SetString(PyExc_ValueError, \"Error building signature string for poly\");\n", + "\t\t\treturn NULL;\n", + "\t\t}\n", + "\t\treturn PyObject_Call(create_signature, signatures, NULL);\n", + "\t}\n", + "\tPyMethodDef polyMethods[] = {\n", + "\t\t{ \"set_create_signature\", (PyCFunction)set_create_signature, METH_VARARGS },\n", + "\t\t{ \"run\", (PyCFunction)run, METH_VARARGS },\n", + "\t\t{ NULL, NULL }\n", + "\t};\n", + "\tPyMODINIT_FUNC initpoly(void) {\n", + "\t\timport_array();\n", + "\t\tPyImport_ImportModule(\"numpy\");\n", + "\t\t(void)Py_InitModule(\"poly\", polyMethods);\n", + "\t}\n", + "}\n", + "#include \n", + "#include \n", + "#include \n", + "#include \n", + "#include \n", + "void sighandler(int sig) {\n", + "\tstd::ostringstream buffer;\n", + "\tbuffer << \"Abort by \" << (sig == SIGSEGV ? \"segfault\" : \"bus error\") << std::endl;\n", + "\tvoid * stack[64];\n", + "\tstd::size_t depth = backtrace(stack, 64);\n", + "\tif (!depth)\n", + "\t\tbuffer << \" \" << std::endl;\n", + "\telse {\n", + "\t\tchar ** symbols = backtrace_symbols(stack, depth);\n", + "\t\tfor (std::size_t i = 1; i < depth; ++i) {\n", + "\t\t\tstd::string symbol = symbols[i];\n", + "\t\t\t\tif (symbol.find_first_of(' ', 59) != std::string::npos) {\n", + "\t\t\t\t\tstd::string name = symbol.substr(59, symbol.find_first_of(' ', 59) - 59);\n", + "\t\t\t\t\tint status;\n", + "\t\t\t\t\tchar * demangled = abi::__cxa_demangle(name.c_str(), NULL, NULL, &status);\n", + "\t\t\t\t\tif (!status) {\n", + "\t\t\t\t\t\tbuffer << \" \" \n", + "\t\t\t\t\t\t\t<< symbol.substr(0, 59) \n", + "\t\t\t\t\t\t\t<< demangled\n", + "\t\t\t\t\t\t\t<< symbol.substr(59 + name.size())\n", + "\t\t\t\t\t\t\t<< std::endl;\n", + "\t\t\t\t\t\tfree(demangled);\n", + "\t\t\t\t\t} else\n", + "\t\t\t\t\t\tbuffer << \" \" << symbol << std::endl;\n", + "\t\t\t\t} else\n", + "\t\t\t\t\tbuffer << \" \" << symbol << std::endl;\n", + "\t\t\t}\n", + "\t\t\tfree(symbols);\n", + "\t\t}\n", + "\t\tstd::cerr << buffer.str();\n", + "\t\tstd::exit(EXIT_FAILURE);\n", + "\t}\n" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Pairwise distance" + ] + }, + { + "cell_type": "code", + "execution_count": 13, + "metadata": {}, + "outputs": [ { - "cell_type": "heading", - "level": 1, - "metadata": {}, - "source": [ - "Python helper module" + "name": "stdout", + "output_type": "stream", + "text": [ + "Overwriting src/pairwise.cpp\n" ] - }, + } + ], + "source": [ + "%%file src/pairwise.cpp\n", + "#define PY_ARRAY_UNIQUE_SYMBOL pairwise_ARRAY_API\n", + "#define NPY_NO_DEPRECATED_API NPY_1_7_API_VERSION\n", + "#include \n", + "#include \n", + "#include \n", + "#include \n", + "#include \n", + "#include \n", + "#include \n", + "#include \n", + "#include \n", + "#include \n", + "#include \n", + "struct PyObj {\n", + "\ttypedef PyObject * ptr_t;\n", + "\ttypedef PyArrayObject * arrptr_t;\n", + "\tPyObj(): dec(false), ptr(NULL) {}\n", + "\tPyObj(ptr_t p): dec(false), ptr(p) {}\n", + "\t~PyObj() { if(dec) Py_DECREF(ptr); }\n", + "\tPyObj & operator=(ptr_t p) { if(dec) Py_DECREF(ptr); ptr = p; dec = false; return *this; }\n", + "\tPyObj & incref(ptr_t p) { if(dec) Py_DECREF(ptr); ptr = p; dec = (p != NULL); return *this; }\n", + "\toperator bool() const { return ptr; }\n", + "\toperator ptr_t() const { return ptr; }\n", + "\toperator arrptr_t() const { return (arrptr_t)ptr; }\n", + "\tbool dec;\n", + "\tptr_t ptr;\n", + "};\n", + "\n", + "inline void pairwise_d2d2JJ(PyObject * pX, \n", + "\t\t\t\t\t\t\t\t\t\t\t\t\t\tnpy_intp const * __restrict__ sX, \n", + "\t\t\t\t\t\t\t\t\t\t\t\t\t\tnpy_double * __restrict__ cX, \n", + "\t\t\t\t\t\t\t\t\t\t\t\t\t\tPyObject * pD, \n", + "\t\t\t\t\t\t\t\t\t\t\t\t\t\tnpy_intp const * __restrict__ sD, \n", + "\t\t\t\t\t\t\t\t\t\t\t\t\t\tnpy_double * __restrict__ cD, \n", + "\t\t\t\t\t\t\t\t\t\t\t\t\t\tnpy_int64 const cM, \n", + "\t\t\t\t\t\t\t\t\t\t\t\t\t\tnpy_int64 const cN);\n", + "\t\t\t\t\t\t\t\t\t\t\t\t\t\t\n", + "inline void pairwise_d2d2JJ(PyObject * pX, \n", + "\t\t\t\t\t\t\t\t\t\t\t\t\t\tnpy_intp const * __restrict__ sX, \n", + "\t\t\t\t\t\t\t\t\t\t\t\t\t\tnpy_double * __restrict__ cX, \n", + "\t\t\t\t\t\t\t\t\t\t\t\t\t\tPyObject * pD, \n", + "\t\t\t\t\t\t\t\t\t\t\t\t\t\tnpy_intp const * __restrict__ sD, \n", + "\t\t\t\t\t\t\t\t\t\t\t\t\t\tnpy_double * __restrict__ cD, \n", + "\t\t\t\t\t\t\t\t\t\t\t\t\t\tnpy_int64 const cM, \n", + "\t\t\t\t\t\t\t\t\t\t\t\t\t\tnpy_int64 const cN){\n", + "\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\n", + "\tnpy_double cd = 0.0;\n", + "\tnpy_double ctmp = 0.0;\n", + "\t\n", + "\tnpy_intp cj;\n", + "\tnpy_intp ck;\n", + "\tnpy_intp x_i_idx;\n", + "\tnpy_intp x_j_idx;\n", + "\tnpy_intp d_i_idx;\n", + "\tnpy_intp const xp = sX[1];\n", + "\tnpy_intp const dp = sD[1];\n", + "\t\n", + "\tfor (npy_intp ci = 0; ci < cM; ++ci) {\n", + "\t\tx_i_idx = ci*xp;\n", + "\t\td_i_idx = ci*dp;\n", + "\t\tfor (cj = 0; cj < cM; ++cj) {\n", + "\t\t\tcd = 0.0;\n", + "\t\t\tx_j_idx = cj*xp;\n", + "\t\t\tfor (ck = 0; ck < cN; ++ck) {\n", + "\t\t\t\tctmp = (cX[(x_i_idx + ck)] - cX[(x_j_idx + ck)]);\n", + "\t\t\t\tcd += (ctmp * ctmp);\n", + "\t\t\t}\n", + "\t\t\tcD[(d_i_idx + cj)] = std::sqrt(cd);\n", + "\t\t}\n", + "\t}\n", + "}\n", + "\n", + "void sighandler(int sig);\n", + "#include \n", + "extern \"C\" {\n", + "\tPyObject * create_signature;\n", + "\tstruct sigaction slot;\n", + "\tPyObject * set_create_signature(PyObject * self, PyObject * args) {\n", + "\t\tif (!PyArg_ParseTuple(args, \"O\", &create_signature)) {\n", + "\t\t\tPyErr_SetString(PyExc_ValueError, \"Invalid Argument to set_create_signature!\");\n", + "\t\t\treturn NULL;\n", + "\t\t}\n", + "\t\tPy_INCREF(create_signature);\n", + "\t\tmemset(&slot, 0, sizeof(slot));\n", + "\t\tslot.sa_handler = &sighandler;\n", + "\t\tsigaction(SIGSEGV, &slot, NULL);\n", + "\t\tsigaction(SIGBUS, &slot, NULL);\n", + "\t\tPy_INCREF(Py_None);\n", + "\t\treturn Py_None;\n", + "\t}\n", + "\tPyObject * run(PyObject * self, PyObject * args) {\n", + "\t\t{\n", + "\t\t\tPyObj pX;\n", + "\t\t\tPyObj pD;\n", + "\t\t\tPyObject * pM; npy_int64 cM;\n", + "\t\t\tPyObject * pN; npy_int64 cN;\n", + "\t\t\tif (\n", + "\t\t\t\tPyTuple_CheckExact(args) and PyTuple_GET_SIZE(args) == 4\n", + "\t\t\t\tand (pX = PyTuple_GET_ITEM(args, 0)) and PyArray_CheckExact(pX)\n", + "\t\t\t\tand PyArray_TYPE((PyArrayObject *)pX) == NPY_FLOAT64 and PyArray_NDIM((PyArrayObject *)pX) == 2\n", + "\t\t\t\tand (pD = PyTuple_GET_ITEM(args, 1)) and PyArray_CheckExact(pD)\n", + "\t\t\t\tand PyArray_TYPE((PyArrayObject *)pD) == NPY_FLOAT64 and PyArray_NDIM((PyArrayObject *)pD) == 2\n", + "\t\t\t\tand (pM = PyTuple_GET_ITEM(args, 2)) and PyInt_CheckExact(pM)\n", + "\t\t\t\tand (pN = PyTuple_GET_ITEM(args, 3)) and PyInt_CheckExact(pN)\n", + "\t\t\t) {\n", + "\t\t\t\tif (!(pX.incref((PyObject *)PyArray_GETCONTIGUOUS((PyArrayObject *)pX)))) {\n", + "\t\t\t\t\tPyErr_SetString(PyExc_ValueError, \"Invalid Argument type on X!\");\n", + "\t\t\t\t\treturn NULL;\n", + "\t\t\t\t}\n", + "\t\t\t\tif (!(pD.incref((PyObject *)PyArray_GETCONTIGUOUS((PyArrayObject *)pD)))) {\n", + "\t\t\t\t\tPyErr_SetString(PyExc_ValueError, \"Invalid Argument type on D!\");\n", + "\t\t\t\t\treturn NULL;\n", + "\t\t\t\t}\n", + "\t\t\t\tcM = PyInt_AS_LONG(pM);\n", + "\t\t\t\tcN = PyInt_AS_LONG(pN);\n", + "\t\t\t\ttry {\n", + "\t\t\t\t\tpairwise_d2d2JJ(\n", + "\t\t\t\t\t\t pX, PyArray_SHAPE((PyArrayObject *)pX), (npy_double *)PyArray_DATA((PyArrayObject *)pX)\n", + "\t\t\t\t\t\t, pD, PyArray_SHAPE((PyArrayObject *)pD), (npy_double *)PyArray_DATA((PyArrayObject *)pD)\n", + "\t\t\t\t\t\t, cM\n", + "\t\t\t\t\t\t, cN\n", + "\t\t\t\t\t);\n", + "\t\t\t\t\tPy_INCREF(Py_None);\n", + "\t\t\t\t\treturn Py_None;\n", + "\t\t\t\t} catch (...) {\n", + "\t\t\t\t\treturn NULL;\n", + "\t\t\t\t}\n", + "\t\t\t} else\n", + "\t\t\t\tPyErr_Clear();\n", + "\t\t}\n", + "\t\tPyObject * signatures = Py_BuildValue(\"(sO)\", \"(lp0\\n(lp1\\nccopy_reg\\n_reconstructor\\np2\\n(chope._ast\\nVariable\\np3\\nc__builtin__\\nobject\\np4\\nNtp5\\nRp6\\n(dp7\\nS'name'\\np8\\nS'X'\\np9\\nsS'dtype'\\np10\\nS'float64'\\np11\\nsS'dims'\\np12\\nI2\\nsbag2\\n(g3\\ng4\\nNtp13\\nRp14\\n(dp15\\ng8\\nS'D'\\np16\\nsg10\\ng11\\nsg12\\nI2\\nsbag2\\n(g3\\ng4\\nNtp17\\nRp18\\n(dp19\\ng8\\nS'M'\\np20\\nsg10\\nc__builtin__\\nint\\np21\\nsg12\\nI0\\nsbag2\\n(g3\\ng4\\nNtp22\\nRp23\\n(dp24\\ng8\\nS'N'\\np25\\nsg10\\ng21\\nsg12\\nI0\\nsbaa.\", args);\n", + "\t\tif (!signatures) {\n", + "\t\t\tPyErr_SetString(PyExc_ValueError, \"Error building signature string for pairwise\");\n", + "\t\t\treturn NULL;\n", + "\t\t}\n", + "\t\treturn PyObject_Call(create_signature, signatures, NULL);\n", + "\t}\n", + "\tPyMethodDef pairwiseMethods[] = {\n", + "\t\t{ \"set_create_signature\", (PyCFunction)set_create_signature, METH_VARARGS },\n", + "\t\t{ \"run\", (PyCFunction)run, METH_VARARGS },\n", + "\t\t{ NULL, NULL }\n", + "\t};\n", + "\tPyMODINIT_FUNC initpairwise(void) {\n", + "\t\timport_array();\n", + "\t\tPyImport_ImportModule(\"numpy\");\n", + "\t\t(void)Py_InitModule(\"pairwise\", pairwiseMethods);\n", + "\t}\n", + "}\n", + "#include \n", + "#include \n", + "#include \n", + "#include \n", + "#include \n", + "void sighandler(int sig) {\n", + "\tstd::ostringstream buffer;\n", + "\tbuffer << \"Abort by \" << (sig == SIGSEGV ? \"segfault\" : \"bus error\") << std::endl;\n", + "\tvoid * stack[64];\n", + "\tstd::size_t depth = backtrace(stack, 64);\n", + "\tif (!depth)\n", + "\t\tbuffer << \" \" << std::endl;\n", + "\telse {\n", + "\t\tchar ** symbols = backtrace_symbols(stack, depth);\n", + "\t\tfor (std::size_t i = 1; i < depth; ++i) {\n", + "\t\t\tstd::string symbol = symbols[i];\n", + "\t\t\t\tif (symbol.find_first_of(' ', 59) != std::string::npos) {\n", + "\t\t\t\t\tstd::string name = symbol.substr(59, symbol.find_first_of(' ', 59) - 59);\n", + "\t\t\t\t\tint status;\n", + "\t\t\t\t\tchar * demangled = abi::__cxa_demangle(name.c_str(), NULL, NULL, &status);\n", + "\t\t\t\t\tif (!status) {\n", + "\t\t\t\t\t\tbuffer << \" \" \n", + "\t\t\t\t\t\t\t<< symbol.substr(0, 59) \n", + "\t\t\t\t\t\t\t<< demangled\n", + "\t\t\t\t\t\t\t<< symbol.substr(59 + name.size())\n", + "\t\t\t\t\t\t\t<< std::endl;\n", + "\t\t\t\t\t\tfree(demangled);\n", + "\t\t\t\t\t} else\n", + "\t\t\t\t\t\tbuffer << \" \" << symbol << std::endl;\n", + "\t\t\t\t} else\n", + "\t\t\t\t\tbuffer << \" \" << symbol << std::endl;\n", + "\t\t\t}\n", + "\t\t\tfree(symbols);\n", + "\t\t}\n", + "\t\tstd::cerr << buffer.str();\n", + "\t\tstd::exit(EXIT_FAILURE);\n", + "\t}\n" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Star point spread function CPP" + ] + }, + { + "cell_type": "code", + "execution_count": 14, + "metadata": {}, + "outputs": [ { - "cell_type": "code", - "collapsed": false, - "input": [ - "%%file native_util.py\n", - "import os\n", - "import sys\n", - "\n", - "from numpy.distutils.misc_util import get_numpy_include_dirs\n", - "import setuptools\n", - "from os import listdir\n", - "# import tempfile\n", - "from hope import config\n", - "\n", - "def load(name):\n", - " compile(name, \"./src\")\n", - " module = __import__(name, globals(), locals(), [], -1)\n", - " return module\n", - "\n", - "\n", - "\n", - "def compile(name, src_folder, target_folder = \"./\"):\n", - " localfilename =os.path.join(src_folder, name)\n", - " \n", - " outfile, stdout, stderr, argv = None, None, None, sys.argv\n", - " try:\n", - " sys.stdout.flush(), sys.stderr.flush()\n", - " outpath = os.path.join(target_folder, \"{0}.out\".format(localfilename))\n", - " if os.path.exists(outpath):\n", - " os.remove(outpath)\n", - " \n", - " so_path = os.path.join(target_folder, \"{0}.so\".format(name))\n", - " if os.path.exists(so_path):\n", - " os.remove(so_path)\n", - " \n", - " outfile = open(outpath, 'w')\n", - " \n", - " if isinstance(sys.stdout, file) and isinstance(sys.stdout, file):\n", - " stdout, stderr = os.dup(sys.stdout.fileno()), os.dup(sys.stderr.fileno())\n", - " os.dup2(outfile.fileno(), sys.stdout.fileno())\n", - " os.dup2(outfile.fileno(), sys.stderr.fileno())\n", - " else:\n", - " stdout, stderr = sys.stdout, sys.stderr\n", - " sys.stdout, sys.stderr = outfile, outfile\n", - " try:\n", - " sources = \"./{0}.cpp\".format(localfilename)\n", - " sys.argv = [\"\", \"build_ext\",\n", - " \"-b\", target_folder, #--build-lib (-b) directory for compiled extension modules\n", - " \"-t\", \".\" #--build-temp - a rel path will result in a dir structure of -b at the cur position \n", - " ]\n", - " \n", - " from types import StringType\n", - " localfilename = StringType(localfilename)\n", - " sources = StringType(sources)\n", - " \n", - " setuptools.setup( \\\n", - " name = name\\\n", - " , ext_modules = [setuptools.Extension( \\\n", - " StringType(name) \\\n", - " , sources = [sources] \\\n", - " , extra_compile_args = config.cxxflags \\\n", - " )] \\\n", - " , include_dirs = get_numpy_include_dirs() \\\n", - " )\n", - " except SystemExit as e:\n", - " print(sys.stderr.write(str(e)))\n", - " sys.stdout.flush(), sys.stderr.flush()\n", - " finally:\n", - " if isinstance(stdout, int):\n", - " os.dup2(stdout, sys.stdout.fileno()), os.close(stdout)\n", - " elif not stdout is None:\n", - " sys.stdout = stdout\n", - " if isinstance(stderr, int):\n", - " os.dup2(stderr, sys.stderr.fileno()), os.close(stderr)\n", - " elif not stderr is None:\n", - " sys.stderr = stderr\n", - " if isinstance(outfile, file):\n", - " outfile.close()\n", - " sys.argv = argv\n", - " \n", - " with open(outpath) as outfile:\n", - " out = outfile.read()\n", - " \n", - " if not os.path.isfile(os.path.join(target_folder, \"{0}.so\".format(name))) or out.find(\"error:\") > -1:\n", - " print(out)\n", - " raise Exception(\"Error compiling function {0} (compiled to {1})\".format(localfilename, target_folder))\n", - " \n", - " if out.find(\"warning:\") > -1:\n", - " import warnings\n", - " warnings.warn(\"A warning has been issued during compilation:\\n%s\"%out)\n", - " \n", - " print(out)\n", - "\n", - "\n", - "def compile_all():\n", - " src_folder = \"./src\"\n", - " func_names = (src_file.split(\".cpp\")[0] for src_file in listdir(src_folder) if src_file.endswith(\".cpp\"))\n", - " for func_name in func_names:\n", - " compile(func_name, src_folder)\n", - " \n", - " \n", - "if __name__ == '__main__':\n", - " compile_all()" - ], - "language": "python", - "metadata": {}, - "outputs": [ - { - "output_type": "stream", - "stream": "stdout", - "text": [ - "Overwriting native_util.py\n" - ] - } - ], - "prompt_number": 16 - }, + "name": "stdout", + "output_type": "stream", + "text": [ + "Overwriting src/pdf.cpp\n" + ] + } + ], + "source": [ + "%%file src/pdf.cpp\n", + "#define PY_ARRAY_UNIQUE_SYMBOL pdf_ARRAY_API\n", + "#define NPY_NO_DEPRECATED_API NPY_1_7_API_VERSION\n", + "#include \n", + "#include \n", + "#include \n", + "#include \n", + "#include \n", + "#include \n", + "#include \n", + "#include \n", + "#include \n", + "#include \n", + "#include \n", + "struct PyObj {\n", + "\ttypedef PyObject * ptr_t;\n", + "\ttypedef PyArrayObject * arrptr_t;\n", + "\tPyObj(): dec(false), ptr(NULL) {}\n", + "\tPyObj(ptr_t p): dec(false), ptr(p) {}\n", + "\t~PyObj() { if(dec) Py_DECREF(ptr); }\n", + "\tPyObj & operator=(ptr_t p) { if(dec) Py_DECREF(ptr); ptr = p; dec = false; return *this; }\n", + "\tPyObj & incref(ptr_t p) { if(dec) Py_DECREF(ptr); ptr = p; dec = (p != NULL); return *this; }\n", + "\toperator bool() const { return ptr; }\n", + "\toperator ptr_t() const { return ptr; }\n", + "\toperator arrptr_t() const { return (arrptr_t)ptr; }\n", + "\tbool dec;\n", + "\tptr_t ptr;\n", + "};\n", + "\n", + "inline std::tuple pdf_f2l1d1f2JDd(\n", + " \tPyObject * pdensity\n", + " , npy_intp const * __restrict__ sdensity\n", + " , npy_float * __restrict__ cdensity\n", + "\t, PyObject * pdims\n", + "\t, npy_intp const * __restrict__ sdims\n", + "\t, npy_int64 * __restrict__ cdims\n", + "\t, PyObject * pcenter\n", + "\t, npy_intp const * __restrict__ scenter\n", + "\t, npy_double * __restrict__ ccenter\n", + "\t, PyObject * pw2D\n", + "\t, npy_intp const * __restrict__ sw2D\n", + "\t, npy_float * __restrict__ cw2D\n", + "\t, npy_int64 cr50\n", + "\t, npy_double cb\n", + "\t, npy_double ca);\n", + "\n", + "inline std::tuple pdf_f2l1d1f2JDd(\n", + " \tPyObject * pdensity\n", + " , npy_intp const * __restrict__ sdensity\n", + " , npy_float * __restrict__ cdensity\n", + "\t, PyObject * pdims\n", + "\t, npy_intp const * __restrict__ sdims\n", + "\t, npy_int64 * __restrict__ cdims\n", + "\t, PyObject * pcenter\n", + "\t, npy_intp const * __restrict__ scenter\n", + "\t, npy_double * __restrict__ ccenter\n", + "\t, PyObject * pw2D\n", + "\t, npy_intp const * __restrict__ sw2D\n", + "\t, npy_float * __restrict__ cw2D\n", + "\t, npy_int64 const cr50\n", + "\t, npy_double const cb\n", + "\t, npy_double const ca) {\n", + "\t\n", + "\tnpy_double cdr;\n", + "\tnpy_double c__sum0;\n", + "\tconst npy_double x_center = ccenter[0];\n", + "\tconst npy_double y_center = ccenter[1];\n", + "\tconst npy_intp len_0_sw2D = sw2D[0];\n", + "\tconst npy_intp len_1_sw2D = sw2D[1];\n", + "\tnpy_intp dc[] = {len_0_sw2D, len_1_sw2D};\n", + "\tPyObject * pc = PyArray_EMPTY(2, dc, NPY_FLOAT64, 0);\n", + "\tnpy_intp * sc = PyArray_SHAPE((PyArrayObject *)pc);\n", + "\tnpy_double * cc = (npy_double *)PyArray_DATA((PyArrayObject *)pc);\n", + "\t\n", + "\tnpy_intp cy = 0;\n", + "\tnpy_intp i0 = 0;\n", + "\tnpy_intp i1 = 0;\n", + "\tnpy_intp i2 = 0;\n", + "\tnpy_intp i3 = 0;\n", + "\t\n", + "\tnpy_intp sw2D_i0_idx;\n", + "\tnpy_intp sw2D_i2_idx;\n", + "\tnpy_intp density_x_idx;\n", + "\t\n", + "\tauto c__sp0 = ca * ca;\n", + "\tauto c__sp1 = cr50 * cr50;\n", + "\tauto c__sp2 = 1.0 / (c__sp0 * c__sp1);\n", + "\tauto c__sp4 = 0.3183098861846737 * c__sp2 * (-1 + cb);\n", + "\n", + "\tfor (npy_intp cx = 0; cx < cdims[(int)(0)]; ++cx) {\n", + "\t\t\n", + "\t\tdensity_x_idx = cx*sdensity[1];\n", + "\t\tfor (cy = 0; cy < cdims[(int)(1)]; ++cy) {\n", + "\t\t\t\n", + "\t\t\tcdr = std::sqrt(std::pow(cx - x_center, 2) + std::pow(cy - y_center, 2));\n", + "\t\t\tauto c__sp3 = cdr * cdr;\n", + "\t\t\tauto c__sp5 = c__sp4 * std::pow((1 + c__sp2 * c__sp3), -cb);\n", + "\t\t\t\n", + "\t\t\tfor (i0 = 0; i0 < len_0_sw2D - 0; ++i0) {\n", + "\t\t\t\t\n", + "\t\t\t\tsw2D_i0_idx = (i0)*len_1_sw2D;\n", + "\t\t\t\tfor (i1 = 0; i1 < len_1_sw2D - 0; ++i1) {\n", + "\t\t\t\t\tcc[sw2D_i0_idx + i1] = c__sp5 * cw2D[sw2D_i0_idx + i1];\n", + "\t\t\t\t}\n", + "\t\t\t}\n", + "\t\t\t\n", + "\t\t\tc__sum0 = 0;\n", + "\t\t\tfor (i2 = 0; i2 < len_0_sw2D - 0; ++i2) {\n", + "\t\t\t\t\n", + "\t\t\t\tsw2D_i2_idx = (i2)*len_1_sw2D;\n", + "\t\t\t\tfor (i3 = 0; i3 < len_1_sw2D - 0; ++i3) {\n", + "\t\t\t\t\tc__sum0 += cc[sw2D_i2_idx + i3];\n", + "\t\t\t\t}\n", + "\t\t\t}\n", + "\t\t\t\n", + "\t\t\tcdensity[(density_x_idx + cy)] = c__sum0;\n", + "\t\t}\n", + "\t}\n", + "\treturn std::make_tuple((PyObject *)pdensity, sdensity, cdensity);\n", + "}\n", + "\n", + "void sighandler(int sig);\n", + "#include \n", + "extern \"C\" {\n", + "\tPyObject * create_signature;\n", + "\tstruct sigaction slot;\n", + "\tPyObject * set_create_signature(PyObject * self, PyObject * args) {\n", + "\t\tif (!PyArg_ParseTuple(args, \"O\", &create_signature)) {\n", + "\t\t\tPyErr_SetString(PyExc_ValueError, \"Invalid Argument to set_create_signature!\");\n", + "\t\t\treturn NULL;\n", + "\t\t}\n", + "\t\tPy_INCREF(create_signature);\n", + "\t\tmemset(&slot, 0, sizeof(slot));\n", + "\t\tslot.sa_handler = &sighandler;\n", + "\t\tsigaction(SIGSEGV, &slot, NULL);\n", + "\t\tsigaction(SIGBUS, &slot, NULL);\n", + "\t\tPy_INCREF(Py_None);\n", + "\t\treturn Py_None;\n", + "\t}\n", + "\tPyObject * run(PyObject * self, PyObject * args) {\n", + "\t\t{\n", + "\t\t\tPyObj pdensity;\n", + "\t\t\tPyObj pdims;\n", + "\t\t\tPyObj pcenter;\n", + "\t\t\tPyObj pw2D;\n", + "\t\t\tPyObject * pr50; npy_int64 cr50;\n", + "\t\t\tPyObject * pb; npy_double cb;\n", + "\t\t\tPyObject * pa; npy_double ca;\n", + "\t\t\tif (\n", + "\t\t\t\tPyTuple_CheckExact(args) and PyTuple_GET_SIZE(args) == 7\n", + "\t\t\t\tand (pdensity = PyTuple_GET_ITEM(args, 0)) and PyArray_CheckExact(pdensity)\n", + "\t\t\t\tand PyArray_TYPE((PyArrayObject *)pdensity) == NPY_FLOAT32 and PyArray_NDIM((PyArrayObject *)pdensity) == 2\n", + "\t\t\t\tand (pdims = PyTuple_GET_ITEM(args, 1)) and PyArray_CheckExact(pdims)\n", + "\t\t\t\tand PyArray_TYPE((PyArrayObject *)pdims) == NPY_INT64 and PyArray_NDIM((PyArrayObject *)pdims) == 1\n", + "\t\t\t\tand (pcenter = PyTuple_GET_ITEM(args, 2)) and PyArray_CheckExact(pcenter)\n", + "\t\t\t\tand PyArray_TYPE((PyArrayObject *)pcenter) == NPY_FLOAT64 and PyArray_NDIM((PyArrayObject *)pcenter) == 1\n", + "\t\t\t\tand (pw2D = PyTuple_GET_ITEM(args, 3)) and PyArray_CheckExact(pw2D)\n", + "\t\t\t\tand PyArray_TYPE((PyArrayObject *)pw2D) == NPY_FLOAT32 and PyArray_NDIM((PyArrayObject *)pw2D) == 2\n", + "\t\t\t\tand (pr50 = PyTuple_GET_ITEM(args, 4)) and PyInt_CheckExact(pr50)\n", + "\t\t\t\tand (pb = PyTuple_GET_ITEM(args, 5)) and PyFloat_CheckExact(pb)\n", + "\t\t\t\tand (pa = PyTuple_GET_ITEM(args, 6)) and PyArray_IsScalar(pa, Double)\n", + "\t\t\t) {\n", + "\t\t\t\tif (!(pdensity.incref((PyObject *)PyArray_GETCONTIGUOUS((PyArrayObject *)pdensity)))) {\n", + "\t\t\t\t\tPyErr_SetString(PyExc_ValueError, \"Invalid Argument type on density!\");\n", + "\t\t\t\t\treturn NULL;\n", + "\t\t\t\t}\n", + "\t\t\t\tif (!(pdims.incref((PyObject *)PyArray_GETCONTIGUOUS((PyArrayObject *)pdims)))) {\n", + "\t\t\t\t\tPyErr_SetString(PyExc_ValueError, \"Invalid Argument type on dims!\");\n", + "\t\t\t\t\treturn NULL;\n", + "\t\t\t\t}\n", + "\t\t\t\tif (!(pcenter.incref((PyObject *)PyArray_GETCONTIGUOUS((PyArrayObject *)pcenter)))) {\n", + "\t\t\t\t\tPyErr_SetString(PyExc_ValueError, \"Invalid Argument type on center!\");\n", + "\t\t\t\t\treturn NULL;\n", + "\t\t\t\t}\n", + "\t\t\t\tif (!(pw2D.incref((PyObject *)PyArray_GETCONTIGUOUS((PyArrayObject *)pw2D)))) {\n", + "\t\t\t\t\tPyErr_SetString(PyExc_ValueError, \"Invalid Argument type on w2D!\");\n", + "\t\t\t\t\treturn NULL;\n", + "\t\t\t\t}\n", + "\t\t\t\tcr50 = PyInt_AS_LONG(pr50);\n", + "\t\t\t\tcb = PyFloat_AS_DOUBLE(pb);\n", + "\t\t\t\tca = PyArrayScalar_VAL(pa, Double);\n", + "\t\t\t\ttry {\n", + "\t\t\t\t\tPyObject * res = std::get<0>(pdf_f2l1d1f2JDd(\n", + "\t\t\t\t\t\t pdensity, PyArray_SHAPE((PyArrayObject *)pdensity), (npy_float *)PyArray_DATA((PyArrayObject *)pdensity)\n", + "\t\t\t\t\t\t, pdims, PyArray_SHAPE((PyArrayObject *)pdims), (npy_int64 *)PyArray_DATA((PyArrayObject *)pdims)\n", + "\t\t\t\t\t\t, pcenter, PyArray_SHAPE((PyArrayObject *)pcenter), (npy_double *)PyArray_DATA((PyArrayObject *)pcenter)\n", + "\t\t\t\t\t\t, pw2D, PyArray_SHAPE((PyArrayObject *)pw2D), (npy_float *)PyArray_DATA((PyArrayObject *)pw2D)\n", + "\t\t\t\t\t\t, cr50\n", + "\t\t\t\t\t\t, cb\n", + "\t\t\t\t\t\t, ca\n", + "\t\t\t\t\t));\n", + "\n", + "\t\t\t\t\tPy_INCREF(res);\n", + "\t\t\t\t\treturn res;\n", + "\t\t\t\t} catch (...) {\n", + "\t\t\t\t\treturn NULL;\n", + "\t\t\t\t}\n", + "\t\t\t} else\n", + "\t\t\t\tPyErr_Clear();\n", + "\t\t}\n", + "\t\tPyObject * signatures = Py_BuildValue(\"(sO)\", \"(lp0\\n(lp1\\nccopy_reg\\n_reconstructor\\np2\\n(chope._ast\\nVariable\\np3\\nc__builtin__\\nobject\\np4\\nNtp5\\nRp6\\n(dp7\\nS'name'\\np8\\nS'density'\\np9\\nsS'dtype'\\np10\\nS'float32'\\np11\\nsS'dims'\\np12\\nI2\\nsbag2\\n(g3\\ng4\\nNtp13\\nRp14\\n(dp15\\ng8\\ng12\\nsg10\\nS'int64'\\np16\\nsg12\\nI1\\nsbag2\\n(g3\\ng4\\nNtp17\\nRp18\\n(dp19\\ng8\\nS'center'\\np20\\nsg10\\nS'float64'\\np21\\nsg12\\nI1\\nsbag2\\n(g3\\ng4\\nNtp22\\nRp23\\n(dp24\\ng8\\nS'w2D'\\np25\\nsg10\\ng11\\nsg12\\nI2\\nsbag2\\n(g3\\ng4\\nNtp26\\nRp27\\n(dp28\\ng8\\nS'r50'\\np29\\nsg10\\nc__builtin__\\nint\\np30\\nsg12\\nI0\\nsbag2\\n(g3\\ng4\\nNtp31\\nRp32\\n(dp33\\ng8\\nS'b'\\np34\\nsg10\\nc__builtin__\\nfloat\\np35\\nsg12\\nI0\\nsbag2\\n(g3\\ng4\\nNtp36\\nRp37\\n(dp38\\ng8\\nS'a'\\np39\\nsg10\\ng21\\nsg12\\nI0\\nsbaa.\", args);\n", + "\t\tif (!signatures) {\n", + "\t\t\tPyErr_SetString(PyExc_ValueError, \"Error building signature string for pdf\");\n", + "\t\t\treturn NULL;\n", + "\t\t}\n", + "\t\treturn PyObject_Call(create_signature, signatures, NULL);\n", + "\t}\n", + "\tPyMethodDef pdfMethods[] = {\n", + "\t\t{ \"set_create_signature\", (PyCFunction)set_create_signature, METH_VARARGS },\n", + "\t\t{ \"run\", (PyCFunction)run, METH_VARARGS },\n", + "\t\t{ NULL, NULL }\n", + "\t};\n", + "\tPyMODINIT_FUNC initpdf(void) {\n", + "\t\timport_array();\n", + "\t\tPyImport_ImportModule(\"numpy\");\n", + "\t\t(void)Py_InitModule(\"pdf\", pdfMethods);\n", + "\t}\n", + "}\n", + "#include \n", + "#include \n", + "#include \n", + "#include \n", + "#include \n", + "void sighandler(int sig) {\n", + "\tstd::ostringstream buffer;\n", + "\tbuffer << \"Abort by \" << (sig == SIGSEGV ? \"segfault\" : \"bus error\") << std::endl;\n", + "\tvoid * stack[64];\n", + "\tstd::size_t depth = backtrace(stack, 64);\n", + "\tif (!depth)\n", + "\t\tbuffer << \" \" << std::endl;\n", + "\telse {\n", + "\t\tchar ** symbols = backtrace_symbols(stack, depth);\n", + "\t\tfor (std::size_t i = 1; i < depth; ++i) {\n", + "\t\t\tstd::string symbol = symbols[i];\n", + "\t\t\t\tif (symbol.find_first_of(' ', 59) != std::string::npos) {\n", + "\t\t\t\t\tstd::string name = symbol.substr(59, symbol.find_first_of(' ', 59) - 59);\n", + "\t\t\t\t\tint status;\n", + "\t\t\t\t\tchar * demangled = abi::__cxa_demangle(name.c_str(), NULL, NULL, &status);\n", + "\t\t\t\t\tif (!status) {\n", + "\t\t\t\t\t\tbuffer << \" \" \n", + "\t\t\t\t\t\t\t<< symbol.substr(0, 59) \n", + "\t\t\t\t\t\t\t<< demangled\n", + "\t\t\t\t\t\t\t<< symbol.substr(59 + name.size())\n", + "\t\t\t\t\t\t\t<< std::endl;\n", + "\t\t\t\t\t\tfree(demangled);\n", + "\t\t\t\t\t} else\n", + "\t\t\t\t\t\tbuffer << \" \" << symbol << std::endl;\n", + "\t\t\t\t} else\n", + "\t\t\t\t\tbuffer << \" \" << symbol << std::endl;\n", + "\t\t\t}\n", + "\t\t\tfree(symbols);\n", + "\t\t}\n", + "\t\tstd::cerr << buffer.str();\n", + "\t\tstd::exit(EXIT_FAILURE);\n", + "\t}\n" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Python helper module" + ] + }, + { + "cell_type": "code", + "execution_count": 20, + "metadata": {}, + "outputs": [ { - "cell_type": "code", - "collapsed": false, - "input": [ - "%%file util.py\n", - "# Copyright (C) 2014 ETH Zurich, Institute for Astronomy\n", - "\n", - "'''\n", - "Created on Aug 4, 2014\n", - "\n", - "author: jakeret\n", - "'''\n", - "from __future__ import print_function, division, absolute_import, unicode_literals\n", - "import math\n", - "\n", - "def perf_comp_data(func_list, data_list, rep=5, number=1, extra_setup=None):\n", - " ''' Function to compare the performance of different functions.\n", - " \n", - " Parameters\n", - " ==========\n", - " func_list : list\n", - " list with function names as strings\n", - " data_list : list\n", - " list with data set names as strings\n", - " rep : int\n", - " number of repetitions of the whole comparison\n", - " number : int\n", - " number of executions for every function\n", - " '''\n", - " from timeit import repeat\n", - " res_list = {}\n", - " for name in enumerate(func_list):\n", - " if data_list is None:\n", - " stmt = \"%s()\"%(name[1])\n", - " setup = \"from __main__ import %s\"%(name[1]) \n", - " else:\n", - " stmt = \"%s(%s)\"%(name[1], data_list[name[0]])\n", - " setup = \"from __main__ import %s, %s\"%(name[1], data_list[name[0]])\n", - " if extra_setup is not None:\n", - " stmt = extra_setup + \"; \" + stmt\n", - " \n", - " results = repeat(stmt=stmt, setup=setup, repeat=rep, number=number)\n", - " \n", - " res_list[name[1]] = (median(results), min(results))\n", - " \n", - "# res_sort = sorted(res_list.iteritems(), key=lambda (k, v): (v, k))\n", - " res_sort = sorted(iter(res_list.items()), key=lambda k_v: (k_v[1], k_v[0]))\n", - " for func, (av_time, min_time) in res_sort:\n", - " rel = av_time / res_sort[0][1][0]\n", - " print('function: {0!s:20}, av. time sec: {1:>12.8f}, min. time sec: {2:>12.8f}, relative: {3:>9.1f}'.format(func, av_time, min_time, rel))\n", - "\n", - "def median(x):\n", - " s_x = sorted(x)\n", - " return (s_x[int(math.floor((len(s_x)-1)/2))] + s_x[int(math.ceil((len(s_x)-1)/2))])/2" - ], - "language": "python", - "metadata": {}, - "outputs": [ - { - "output_type": "stream", - "stream": "stdout", - "text": [ - "Overwriting util.py\n" - ] - } - ], - "prompt_number": 1 - }, + "name": "stdout", + "output_type": "stream", + "text": [ + "Overwriting native_util.py\n" + ] + } + ], + "source": [ + "%%file native_util.py\n", + "import importlib\n", + "import os\n", + "import glob\n", + "import sys\n", + "\n", + "from numpy.distutils.misc_util import get_numpy_include_dirs\n", + "import setuptools\n", + "from os import listdir\n", + "# import tempfile\n", + "from hope import config\n", + "\n", + "try:\n", + " # python 3\n", + " import io\n", + " file = io.IOBase\n", + "except ImportError:\n", + " pass\n", + "\n", + "\n", + "def load(name):\n", + " compile(name, \"./src\")\n", + " module = importlib.import_module(name)\n", + " # module = __import__(name, globals(), locals(), [], -1)\n", + " return module\n", + "\n", + "\n", + "\n", + "def compile(name, src_folder, target_folder = \"./\"):\n", + " localfilename =os.path.join(src_folder, name)\n", + " \n", + " outfile, stdout, stderr, argv = None, None, None, sys.argv\n", + " try:\n", + " sys.stdout.flush(), sys.stderr.flush()\n", + " outpath = os.path.join(target_folder, \"{0}.out\".format(localfilename))\n", + " if os.path.exists(outpath):\n", + " os.remove(outpath)\n", + " \n", + " so_path = os.path.join(target_folder, \"{0}.so\".format(name))\n", + " if os.path.exists(so_path):\n", + " os.remove(so_path)\n", + " \n", + " outfile = open(outpath, 'w')\n", + " \n", + " try:\n", + " sys.stdout.fileno()\n", + " sys.stderr.fileno()\n", + " have_fileno = True\n", + " except OSError:\n", + " have_fileno = False\n", + " \n", + " if have_fileno and isinstance(sys.stdout, file) and isinstance(sys.stdout, file):\n", + " stdout, stderr = os.dup(sys.stdout.fileno()), os.dup(sys.stderr.fileno())\n", + " os.dup2(outfile.fileno(), sys.stdout.fileno())\n", + " os.dup2(outfile.fileno(), sys.stderr.fileno())\n", + " else:\n", + " stdout, stderr = sys.stdout, sys.stderr\n", + " sys.stdout, sys.stderr = outfile, outfile\n", + " try:\n", + " sources = \"./{0}.cpp\".format(localfilename)\n", + " sys.argv = [\"\", \"build_ext\",\n", + " \"-b\", target_folder, #--build-lib (-b) directory for compiled extension modules\n", + " \"-t\", \".\" #--build-temp - a rel path will result in a dir structure of -b at the cur position \n", + " ]\n", + " \n", + " localfilename = str(localfilename)\n", + " sources = str(sources)\n", + " \n", + " setuptools.setup( \\\n", + " name = name\\\n", + " , ext_modules = [setuptools.Extension( \\\n", + " name \\\n", + " , sources = [sources] \\\n", + " , extra_compile_args = config.cxxflags \\\n", + " )] \\\n", + " , include_dirs = get_numpy_include_dirs() \\\n", + " )\n", + " except SystemExit as e:\n", + " print(sys.stderr.write(str(e)))\n", + " sys.stdout.flush(), sys.stderr.flush()\n", + " finally:\n", + " if isinstance(stdout, int):\n", + " os.dup2(stdout, sys.stdout.fileno()), os.close(stdout)\n", + " elif not stdout is None:\n", + " sys.stdout = stdout\n", + " if isinstance(stderr, int):\n", + " os.dup2(stderr, sys.stderr.fileno()), os.close(stderr)\n", + " elif not stderr is None:\n", + " sys.stderr = stderr\n", + " if isinstance(outfile, file):\n", + " outfile.close()\n", + " sys.argv = argv\n", + " \n", + " with open(outpath) as outfile:\n", + " out = outfile.read()\n", + " \n", + " modules = glob.glob(os.path.join(target_folder, \"{}*.so\".format(name)))\n", + " \n", + " if not modules or out.find(\"error:\") > -1:\n", + " print(out)\n", + " raise Exception(\"Error compiling function {0} (compiled to {1})\".format(localfilename, target_folder))\n", + " \n", + " if out.find(\"warning:\") > -1:\n", + " import warnings\n", + " warnings.warn(\"A warning has been issued during compilation:\\n%s\"%out)\n", + " \n", + " print(out)\n", + "\n", + "\n", + "def compile_all():\n", + " src_folder = \"./src\"\n", + " func_names = (src_file.split(\".cpp\")[0] for src_file in listdir(src_folder) if src_file.endswith(\".cpp\"))\n", + " for func_name in func_names:\n", + " compile(func_name, src_folder)\n", + " \n", + " \n", + "if __name__ == '__main__':\n", + " compile_all()" + ] + }, + { + "cell_type": "code", + "execution_count": 16, + "metadata": {}, + "outputs": [ { - "cell_type": "code", - "collapsed": false, - "input": [], - "language": "python", - "metadata": {}, - "outputs": [] + "name": "stdout", + "output_type": "stream", + "text": [ + "Overwriting util.py\n" + ] } ], - "metadata": {} + "source": [ + "%%file util.py\n", + "# Copyright (C) 2014 ETH Zurich, Institute for Astronomy\n", + "\n", + "'''\n", + "Created on Aug 4, 2014\n", + "\n", + "author: jakeret\n", + "'''\n", + "from __future__ import print_function, division, absolute_import, unicode_literals\n", + "import math\n", + "\n", + "def perf_comp_data(func_list, data_list, rep=5, number=1, extra_setup=None):\n", + " ''' Function to compare the performance of different functions.\n", + " \n", + " Parameters\n", + " ==========\n", + " func_list : list\n", + " list with function names as strings\n", + " data_list : list\n", + " list with data set names as strings\n", + " rep : int\n", + " number of repetitions of the whole comparison\n", + " number : int\n", + " number of executions for every function\n", + " '''\n", + " from timeit import repeat\n", + " res_list = {}\n", + " for name in enumerate(func_list):\n", + " if data_list is None:\n", + " stmt = \"%s()\"%(name[1])\n", + " setup = \"from __main__ import %s\"%(name[1]) \n", + " else:\n", + " stmt = \"%s(%s)\"%(name[1], data_list[name[0]])\n", + " setup = \"from __main__ import %s, %s\"%(name[1], data_list[name[0]])\n", + " if extra_setup is not None:\n", + " stmt = extra_setup + \"; \" + stmt\n", + " \n", + " results = repeat(stmt=stmt, setup=setup, repeat=rep, number=number)\n", + " \n", + " res_list[name[1]] = (median(results), min(results))\n", + " \n", + "# res_sort = sorted(res_list.iteritems(), key=lambda (k, v): (v, k))\n", + " res_sort = sorted(iter(res_list.items()), key=lambda k_v: (k_v[1], k_v[0]))\n", + " for func, (av_time, min_time) in res_sort:\n", + " rel = av_time / res_sort[0][1][0]\n", + " print('function: {0!s:20}, av. time sec: {1:>12.8f}, min. time sec: {2:>12.8f}, relative: {3:>9.1f}'.format(func, av_time, min_time, rel))\n", + "\n", + "def median(x):\n", + " s_x = sorted(x)\n", + " return (s_x[int(math.floor((len(s_x)-1)/2))] + s_x[int(math.ceil((len(s_x)-1)/2))])/2" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": true + }, + "outputs": [], + "source": [] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": true + }, + "outputs": [], + "source": [] } - ] -} \ No newline at end of file + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.5.1" + } + }, + "nbformat": 4, + "nbformat_minor": 1 +} diff --git a/benchmarks/numexpr.ipynb b/benchmarks/numexpr.ipynb index 48c37c2..912c17b 100644 --- a/benchmarks/numexpr.ipynb +++ b/benchmarks/numexpr.ipynb @@ -1,665 +1,586 @@ { - "metadata": { - "name": "", - "signature": "sha256:f51733b3c4c4439e321dc29d5ba36f149a1a77c8e879a9549b540aed6dbc3edf" - }, - "nbformat": 3, - "nbformat_minor": 0, - "worksheets": [ + "cells": [ { - "cells": [ - { - "cell_type": "code", - "collapsed": false, - "input": [ - "import hope\n", - "hope.config.optimize = True\n", - "hope.config.verbose = True\n", - "hope.config.keeptemp = True\n", - "import numba\n", - "import numpy as np\n", - "import numexpr as ne\n", - "\n", - "from util import perf_comp_data\n", - "from native_util import load\n", - "%load_ext cythonmagic\n", - "%load_ext version_information\n", - "%version_information numpy, Cython, numba, hope, numexpr" - ], - "language": "python", - "metadata": {}, - "outputs": [ - { - "html": [ - "
SoftwareVersion
Python2.7.8 (default, Jul 13 2014, 17:11:32) [GCC 4.2.1 Compatible Apple LLVM 5.1 (clang-503.0.40)]
IPython1.1.0
OSposix [darwin]
numpy1.8.1
Cython0.20.2
numba0.13.3
hope0.3.0
numexpr2.4
Thu Sep 04 15:13:34 2014 CEST
" - ], - "json": [ - "{\"Software versions\": [{\"version\": \"2.7.8 (default, Jul 13 2014, 17:11:32) [GCC 4.2.1 Compatible Apple LLVM 5.1 (clang-503.0.40)]\", \"module\": \"Python\"}, {\"version\": \"1.1.0\", \"module\": \"IPython\"}, {\"version\": \"posix [darwin]\", \"module\": \"OS\"}, {\"version\": \"1.8.1\", \"module\": \"numpy\"}, {\"version\": \"0.20.2\", \"module\": \"Cython\"}, {\"version\": \"0.13.3\", \"module\": \"numba\"}, {\"version\": \"0.3.0\", \"module\": \"hope\"}, {\"version\": \"2.4\", \"module\": \"numexpr\"}]}" - ], - "latex": [ - "\\begin{tabular}{|l|l|}\\hline\n", - "{\\bf Software} & {\\bf Version} \\\\ \\hline\\hline\n", - "Python & 2.7.8 (default, Jul 13 2014, 17:11:32) [GCC 4.2.1 Compatible Apple LLVM 5.1 (clang-503.0.40)] \\\\ \\hline\n", - "IPython & 1.1.0 \\\\ \\hline\n", - "OS & posix [darwin] \\\\ \\hline\n", - "numpy & 1.8.1 \\\\ \\hline\n", - "Cython & 0.20.2 \\\\ \\hline\n", - "numba & 0.13.3 \\\\ \\hline\n", - "hope & 0.3.0 \\\\ \\hline\n", - "numexpr & 2.4 \\\\ \\hline\n", - "\\hline \\multicolumn{2}{|l|}{Thu Sep 04 15:13:34 2014 CEST} \\\\ \\hline\n", - "\\end{tabular}\n" - ], - "metadata": {}, - "output_type": "pyout", - "prompt_number": 1, - "text": [ - "Software versions\n", - "Python 2.7.8 (default, Jul 13 2014, 17:11:32) [GCC 4.2.1 Compatible Apple LLVM 5.1 (clang-503.0.40)]\n", - "IPython 1.1.0\n", - "OS posix [darwin]\n", - "numpy 1.8.1\n", - "Cython 0.20.2\n", - "numba 0.13.3\n", - "hope 0.3.0\n", - "numexpr 2.4\n", - "Thu Sep 04 15:13:34 2014 CEST" - ] - } - ], - "prompt_number": 1 - }, - { - "cell_type": "code", - "collapsed": false, - "input": [ - "# Python version\n", - "\n", - "def ln_python(X, Y):\n", - " Y[:] = (X-1) - (X-1)**2 / 2 + (X-1)**3 / 3 - (X-1)**4 / 4 + (X-1)**5 / 5 - (X-1)**6 / 6 + (X-1)**7 / 7 - (X-1)**8 / 8 + (X-1)**9 / 9\n" - ], - "language": "python", - "metadata": { - "slideshow": { - "slide_type": "slide" - } - }, - "outputs": [], - "prompt_number": 2 - }, - { - "cell_type": "code", - "collapsed": false, - "input": [ - "# Python version\n", - "\n", - "def ln_python_exp(X, Y):\n", - " x = (X - 1)\n", - " x2 = x*x\n", - " x4 = x2*x2\n", - " x6 = x4*x2\n", - " x8 = x4*x4\n", - " Y[:] = x - x2 / 2 + x * x2 / 3 - x4 / 4 + x * x4 / 5 - x6 / 6 + x6 * x / 7 - x8 / 8 + x8 * x / 9" - ], - "language": "python", - "metadata": { - "slideshow": { - "slide_type": "slide" - } - }, - "outputs": [], - "prompt_number": 3 - }, - { - "cell_type": "code", - "collapsed": false, - "input": [ - "# NumExpr version\n", - "def ln_numexpr(X, Y):\n", - " Y[:] = ne.evaluate(\"(X-1) - (X-1)**2 / 2 + (X-1)**3 / 3 - (X-1)**4 / 4 + (X-1)**5 / 5 - (X-1)**6 / 6 + (X-1)**7 / 7 - (X-1)**8 / 8 + (X-1)**9 / 9\")\n" - ], - "language": "python", - "metadata": { - "slideshow": { - "slide_type": "slide" - } - }, - "outputs": [], - "prompt_number": 4 - }, - { - "cell_type": "code", - "collapsed": false, - "input": [ - "# Hope version\n", - "@hope.jit\n", - "def ln_hope(X, Y):\n", - " Y[:] = (X-1) - (X-1)**2 / 2 + (X-1)**3 / 3 - (X-1)**4 / 4 + (X-1)**5 / 5 - (X-1)**6 / 6 + (X-1)**7 / 7 - (X-1)**8 / 8 + (X-1)**9 / 9\n" - ], - "language": "python", - "metadata": { - "slideshow": { - "slide_type": "slide" - } - }, - "outputs": [ - { - "output_type": "stream", - "stream": "stderr", - "text": [ - "/Users/jakeret/workspace/hope/hope/jit.py:128: UserWarning: Recompiling... Reason: State is inconsistent with config. Inconsistent state key: [optimize].\n", - " warnings.warn(\"Recompiling... Reason: {0}\".format(le))\n" - ] - } - ], - "prompt_number": 5 - }, - { - "cell_type": "code", - "collapsed": false, - "input": [ - "# Hope version\n", - "\n", - "import hope\n", - "hope.config.optimize = False\n", - "@hope.jit\n", - "def ln_hope_exp(X, Y):\n", - " x = (X - 1)\n", - " x2 = x*x\n", - " x4 = x2*x2\n", - " x6 = x4*x2\n", - " x8 = x4*x4\n", - " Y[:] = x - x2 / 2 + x * x2 / 3 - x4 / 4 + x * x4 / 5 - x6 / 6 + x6 * x / 7 - x8 / 8 + x8 * x / 9" - ], - "language": "python", - "metadata": { - "slideshow": { - "slide_type": "slide" - } - }, - "outputs": [], - "prompt_number": 6 - }, - { - "cell_type": "code", - "collapsed": false, - "input": [ - "# numba version\n", - "\n", - "@numba.jit\n", - "def ln_numba(X, Y):\n", - " Y[:] = (X-1) - (X-1)**2 / 2 + (X-1)**3 / 3 - (X-1)**4 / 4 + (X-1)**5 / 5 - (X-1)**6 / 6 + (X-1)**7 / 7 - (X-1)**8 / 8 + (X-1)**9 / 9\n" - ], - "language": "python", - "metadata": { - "slideshow": { - "slide_type": "slide" - } - }, - "outputs": [], - "prompt_number": 7 - }, - { - "cell_type": "code", - "collapsed": false, - "input": [ - "# numba version\n", - "\n", - "import numba\n", - "\n", - "@numba.jit\n", - "def ln_numba_exp(X, Y):\n", - " x = (X - 1)\n", - " x2 = x*x\n", - " x4 = x2*x2\n", - " x6 = x4*x2\n", - " x8 = x4*x4\n", - " Y[:] = x - x2 / 2 + x * x2 / 3 - x4 / 4 + x * x4 / 5 - x6 / 6 + x6 * x / 7 - x8 / 8 + x8 * x / 9" - ], - "language": "python", - "metadata": { - "slideshow": { - "slide_type": "slide" - } - }, - "outputs": [], - "prompt_number": 8 - }, + "cell_type": "code", + "execution_count": 3, + "metadata": {}, + "outputs": [ { - "cell_type": "code", - "collapsed": false, - "input": [ - "%load_ext cythonmagic" - ], - "language": "python", - "metadata": {}, - "outputs": [ - { - "output_type": "stream", - "stream": "stdout", - "text": [ - "The cythonmagic extension is already loaded. To reload it, use:\n", - " %reload_ext cythonmagic\n" - ] - } - ], - "prompt_number": 9 - }, - { - "cell_type": "code", - "collapsed": false, - "input": [ - "%%cython\n", - "\n", - "cimport cython\n", - "\n", - "cimport cython\n", - "import numpy as np\n", - "cimport numpy as np\n", - "\n", - "@cython.boundscheck(False)\n", - "@cython.wraparound(False)\n", - "def ln_cython(np.ndarray[np.double_t, ndim=1] X, np.ndarray[np.double_t, ndim=1] Y):\n", - " Y[:] = (X-1) - (X-1)**2 / 2 + (X-1)**3 / 3 - (X-1)**4 / 4 + (X-1)**5 / 5 - (X-1)**6 / 6 + (X-1)**7 / 7 - (X-1)**8 / 8 + (X-1)**9 / 9\n", - "\n", - " \n", - "@cython.boundscheck(False)\n", - "@cython.wraparound(False)\n", - "def ln_cython_exp(np.ndarray[np.float_t, ndim=1] X, np.ndarray[np.float_t, ndim=1] Y):\n", - " cdef np.ndarray[np.double_t, ndim=1] x = (X - 1)\n", - " cdef np.ndarray[np.double_t, ndim=1] x2 = x*x\n", - " cdef np.ndarray[np.double_t, ndim=1] x4 = x2*x2\n", - " cdef np.ndarray[np.double_t, ndim=1] x6 = x4*x2\n", - " cdef np.ndarray[np.double_t, ndim=1] x8 = x4*x4\n", - " Y[:] = x - x2 / 2 + x * x2 / 3 - x4 / 4 + x * x4 / 5 - x6 / 6 + x6 * x / 7 - x8 / 8 + x8 * x / 9" - ], - "language": "python", - "metadata": {}, - "outputs": [], - "prompt_number": 10 + "name": "stdout", + "output_type": "stream", + "text": [ + "The version_information extension is already loaded. To reload it, use:\n", + " %reload_ext version_information\n" + ] }, { - "cell_type": "code", - "collapsed": false, - "input": [ - "from native_util import load\n", - "native_ln_mod = load(\"ln\")\n", - "ln_native = native_ln_mod.run\n", - "\n", - "native_ln_opt_mod = load(\"ln_opt\")\n", - "ln_native_opt = native_ln_opt_mod.run\n", - "\n", - "native_ln_exp_mod = load(\"ln_exp\")\n", - "ln_native_exp = native_ln_exp_mod.run\n" - ], - "language": "python", - "metadata": {}, - "outputs": [ - { - "output_type": "stream", - "stream": "stdout", - "text": [ - "running build_ext\n", - "building 'ln' extension\n", - "C compiler: /usr/bin/clang -fno-strict-aliasing -fno-common -dynamic -pipe -Os -fwrapv -DNDEBUG -g -fwrapv -O3 -Wall -Wstrict-prototypes\n", - "\n", - "compile options: '-I/Users/jakeret/Library/Python/2.7/lib/python/site-packages/numpy/core/include -I/opt/local/Library/Frameworks/Python.framework/Versions/2.7/include/python2.7 -c'\n", - "extra options: '-Wall -Wno-unused-variable -march=native -stdlib=libc++ -std=c++11'\n", - "clang: ././src/ln.cpp\n", - "/usr/bin/clang++ -bundle -undefined dynamic_lookup -L/opt/local/lib -Wl,-headerpad_max_install_names -L/opt/local/lib/db46 ./src/ln.o -o ./ln.so\n", - "\n" - ] - }, - { - "output_type": "stream", - "stream": "stdout", - "text": [ - "running build_ext\n", - "building 'ln_opt' extension\n", - "C compiler: /usr/bin/clang -fno-strict-aliasing -fno-common -dynamic -pipe -Os -fwrapv -DNDEBUG -g -fwrapv -O3 -Wall -Wstrict-prototypes\n", - "\n", - "compile options: '-I/Users/jakeret/Library/Python/2.7/lib/python/site-packages/numpy/core/include -I/opt/local/Library/Frameworks/Python.framework/Versions/2.7/include/python2.7 -c'\n", - "extra options: '-Wall -Wno-unused-variable -march=native -stdlib=libc++ -std=c++11'\n", - "clang: ././src/ln_opt.cpp\n", - "/usr/bin/clang++ -bundle -undefined dynamic_lookup -L/opt/local/lib -Wl,-headerpad_max_install_names -L/opt/local/lib/db46 ./src/ln_opt.o -o ./ln_opt.so\n", - "\n" + "data": { + "application/json": { + "Software versions": [ + { + "module": "Python", + "version": "3.5.1 64bit [GCC 4.2.1 (Apple Inc. build 5666) (dot 3)]" + }, + { + "module": "IPython", + "version": "6.1.0" + }, + { + "module": "OS", + "version": "Darwin 15.6.0 x86_64 i386 64bit" + }, + { + "module": "numpy", + "version": "1.13.1" + }, + { + "module": "Cython", + "version": "0.26" + }, + { + "module": "numba", + "version": "0.34.0" + }, + { + "module": "hope", + "version": "0.6.1" + }, + { + "module": "numexpr", + "version": "2.6.2" + } ] }, - { - "output_type": "stream", - "stream": "stdout", - "text": [ - "running build_ext\n", - "building 'ln_exp' extension\n", - "C compiler: /usr/bin/clang -fno-strict-aliasing -fno-common -dynamic -pipe -Os -fwrapv -DNDEBUG -g -fwrapv -O3 -Wall -Wstrict-prototypes\n", - "\n", - "compile options: '-I/Users/jakeret/Library/Python/2.7/lib/python/site-packages/numpy/core/include -I/opt/local/Library/Frameworks/Python.framework/Versions/2.7/include/python2.7 -c'\n", - "extra options: '-Wall -Wno-unused-variable -march=native -stdlib=libc++ -std=c++11'\n", - "clang: ././src/ln_exp.cpp\n", - "/usr/bin/clang++ -bundle -undefined dynamic_lookup -L/opt/local/lib -Wl,-headerpad_max_install_names -L/opt/local/lib/db46 ./src/ln_exp.o -o ./ln_exp.so\n", - "\n" - ] - } - ], - "prompt_number": 11 - }, - { - "cell_type": "code", - "collapsed": false, - "input": [ - "# Hope version - optimized\n", - "\n", - "import hope\n", - "hope.config.optimize = True\n", - "\n", - "@hope.jit\n", - "def ln_hope_opt(X, Y):\n", - " Y[:] = (X-1) - (X-1)**2 / 2 + (X-1)**3 / 3 - (X-1)**4 / 4 + (X-1)**5 / 5 - (X-1)**6 / 6 + (X-1)**7 / 7 - (X-1)**8 / 8 + (X-1)**9 / 9\n", - "\n", - "\n", - "hope.config.optimize = False" - ], - "language": "python", - "metadata": { - "slideshow": { - "slide_type": "notes" - } + "text/html": [ + "
SoftwareVersion
Python3.5.1 64bit [GCC 4.2.1 (Apple Inc. build 5666) (dot 3)]
IPython6.1.0
OSDarwin 15.6.0 x86_64 i386 64bit
numpy1.13.1
Cython0.26
numba0.34.0
hope0.6.1
numexpr2.6.2
Tue Aug 29 17:25:19 2017 CEST
" + ], + "text/latex": [ + "\\begin{tabular}{|l|l|}\\hline\n", + "{\\bf Software} & {\\bf Version} \\\\ \\hline\\hline\n", + "Python & 3.5.1 64bit [GCC 4.2.1 (Apple Inc. build 5666) (dot 3)] \\\\ \\hline\n", + "IPython & 6.1.0 \\\\ \\hline\n", + "OS & Darwin 15.6.0 x86\\_64 i386 64bit \\\\ \\hline\n", + "numpy & 1.13.1 \\\\ \\hline\n", + "Cython & 0.26 \\\\ \\hline\n", + "numba & 0.34.0 \\\\ \\hline\n", + "hope & 0.6.1 \\\\ \\hline\n", + "numexpr & 2.6.2 \\\\ \\hline\n", + "\\hline \\multicolumn{2}{|l|}{Tue Aug 29 17:25:19 2017 CEST} \\\\ \\hline\n", + "\\end{tabular}\n" + ], + "text/plain": [ + "Software versions\n", + "Python 3.5.1 64bit [GCC 4.2.1 (Apple Inc. build 5666) (dot 3)]\n", + "IPython 6.1.0\n", + "OS Darwin 15.6.0 x86_64 i386 64bit\n", + "numpy 1.13.1\n", + "Cython 0.26\n", + "numba 0.34.0\n", + "hope 0.6.1\n", + "numexpr 2.6.2\n", + "Tue Aug 29 17:25:19 2017 CEST" + ] }, - "outputs": [], - "prompt_number": 13 - }, - { - "cell_type": "code", - "collapsed": false, - "input": [ - "import numpy as np\n", - "\n", - "X = np.random.random(10000).astype(np.float64)\n", - "Y = np.ones_like(X)" - ], - "language": "python", - "metadata": { - "slideshow": { - "slide_type": "slide" - } - }, - "outputs": [], - "prompt_number": 15 - }, - { - "cell_type": "code", - "collapsed": false, - "input": [ - "Y1 = np.ones_like(X)\n", - "Y2 = np.ones_like(X)\n", - "Y3 = np.ones_like(X)\n", - "Y4 = np.ones_like(X)\n", - "Y5 = np.ones_like(X)\n", - "Y6 = np.ones_like(X)\n", - "Y7 = np.ones_like(X)\n", - "Y8 = np.ones_like(X)\n", - "Y9 = np.ones_like(X)\n", - "Y10 = np.ones_like(X)\n", - "Y11 = np.ones_like(X)\n", - "Y12 = np.ones_like(X)\n", - "Y13 = np.ones_like(X)\n", - "\n", - "ln_python(X, Y1)\n", - "ln_python_exp(X, Y2)\n", - "ln_cython(X, Y3)\n", - "ln_cython_exp(X, Y4)\n", - "ln_numexpr(X, Y5)\n", - "ln_hope(X, Y6)\n", - "ln_hope_exp(X, Y7)\n", - "ln_numba(X, Y8)\n", - "ln_numba_exp(X, Y9)\n", - "ln_native(X, Y10)\n", - "ln_native_opt(X, Y11)\n", - "ln_native_exp(X, Y12)\n", - "ln_hope_opt(X, Y13)\n", - "\n", - "assert np.allclose(Y1,Y2, 1E-10)\n", - "assert np.allclose(Y1,Y3, 1E-10)\n", - "assert np.allclose(Y1,Y4, 1E-10)\n", - "assert np.allclose(Y1,Y5, 1E-10)\n", - "assert np.allclose(Y1,Y6, 1E-10)\n", - "assert np.allclose(Y1,Y7, 1E-10)\n", - "assert np.allclose(Y1,Y8, 1E-10)\n", - "assert np.allclose(Y1,Y9, 1E-10)\n", - "assert np.allclose(Y1,Y10, 1E-10)\n", - "assert np.allclose(Y1,Y11, 1E-10)\n", - "assert np.allclose(Y1,Y12, 1E-10)\n", - "assert np.allclose(Y1,Y13, 1E-10)\n" - ], - "language": "python", + "execution_count": 3, "metadata": {}, - "outputs": [], - "prompt_number": 17 - }, + "output_type": "execute_result" + } + ], + "source": [ + "import hope\n", + "hope.config.optimize = True\n", + "hope.config.verbose = True\n", + "hope.config.keeptemp = True\n", + "import numba\n", + "import numpy as np\n", + "import numexpr as ne\n", + "\n", + "from util import perf_comp_data\n", + "from native_util import load\n", + "%load_ext Cython\n", + "%load_ext version_information\n", + "%version_information numpy, Cython, numba, hope, numexpr" + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "metadata": { + "collapsed": true, + "slideshow": { + "slide_type": "slide" + } + }, + "outputs": [], + "source": [ + "# Python version\n", + "\n", + "def ln_python(X, Y):\n", + " Y[:] = (X-1) - (X-1)**2 / 2 + (X-1)**3 / 3 - (X-1)**4 / 4 + (X-1)**5 / 5 - (X-1)**6 / 6 + (X-1)**7 / 7 - (X-1)**8 / 8 + (X-1)**9 / 9\n" + ] + }, + { + "cell_type": "code", + "execution_count": 5, + "metadata": { + "collapsed": true, + "slideshow": { + "slide_type": "slide" + } + }, + "outputs": [], + "source": [ + "# Python version\n", + "\n", + "def ln_python_exp(X, Y):\n", + " x = (X - 1)\n", + " x2 = x*x\n", + " x4 = x2*x2\n", + " x6 = x4*x2\n", + " x8 = x4*x4\n", + " Y[:] = x - x2 / 2 + x * x2 / 3 - x4 / 4 + x * x4 / 5 - x6 / 6 + x6 * x / 7 - x8 / 8 + x8 * x / 9" + ] + }, + { + "cell_type": "code", + "execution_count": 6, + "metadata": { + "collapsed": true, + "slideshow": { + "slide_type": "slide" + } + }, + "outputs": [], + "source": [ + "# NumExpr version\n", + "def ln_numexpr(X, Y):\n", + " Y[:] = ne.evaluate(\"(X-1) - (X-1)**2 / 2 + (X-1)**3 / 3 - (X-1)**4 / 4 + (X-1)**5 / 5 - (X-1)**6 / 6 + (X-1)**7 / 7 - (X-1)**8 / 8 + (X-1)**9 / 9\")\n" + ] + }, + { + "cell_type": "code", + "execution_count": 7, + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "outputs": [], + "source": [ + "# Hope version\n", + "@hope.jit\n", + "def ln_hope(X, Y):\n", + " Y[:] = (X-1) - (X-1)**2 / 2 + (X-1)**3 / 3 - (X-1)**4 / 4 + (X-1)**5 / 5 - (X-1)**6 / 6 + (X-1)**7 / 7 - (X-1)**8 / 8 + (X-1)**9 / 9\n" + ] + }, + { + "cell_type": "code", + "execution_count": 8, + "metadata": { + "collapsed": true, + "slideshow": { + "slide_type": "slide" + } + }, + "outputs": [], + "source": [ + "# Hope version\n", + "\n", + "import hope\n", + "hope.config.optimize = False\n", + "@hope.jit\n", + "def ln_hope_exp(X, Y):\n", + " x = (X - 1)\n", + " x2 = x*x\n", + " x4 = x2*x2\n", + " x6 = x4*x2\n", + " x8 = x4*x4\n", + " Y[:] = x - x2 / 2 + x * x2 / 3 - x4 / 4 + x * x4 / 5 - x6 / 6 + x6 * x / 7 - x8 / 8 + x8 * x / 9" + ] + }, + { + "cell_type": "code", + "execution_count": 9, + "metadata": { + "collapsed": true, + "slideshow": { + "slide_type": "slide" + } + }, + "outputs": [], + "source": [ + "# numba version\n", + "\n", + "@numba.jit\n", + "def ln_numba(X, Y):\n", + " Y[:] = (X-1) - (X-1)**2 / 2 + (X-1)**3 / 3 - (X-1)**4 / 4 + (X-1)**5 / 5 - (X-1)**6 / 6 + (X-1)**7 / 7 - (X-1)**8 / 8 + (X-1)**9 / 9\n" + ] + }, + { + "cell_type": "code", + "execution_count": 10, + "metadata": { + "collapsed": true, + "slideshow": { + "slide_type": "slide" + } + }, + "outputs": [], + "source": [ + "# numba version\n", + "\n", + "import numba\n", + "\n", + "@numba.jit\n", + "def ln_numba_exp(X, Y):\n", + " x = (X - 1)\n", + " x2 = x*x\n", + " x4 = x2*x2\n", + " x6 = x4*x2\n", + " x8 = x4*x4\n", + " Y[:] = x - x2 / 2 + x * x2 / 3 - x4 / 4 + x * x4 / 5 - x6 / 6 + x6 * x / 7 - x8 / 8 + x8 * x / 9" + ] + }, + { + "cell_type": "code", + "execution_count": 12, + "metadata": {}, + "outputs": [ { - "cell_type": "code", - "collapsed": false, - "input": [ - "import numexpr as ne\n", + "name": "stdout", + "output_type": "stream", + "text": [ + "In file included from /Users/uweschmitt/.ipython/cython/_cython_magic_c0a8dbdf5333e81ffeb4f99decde5aac.c:495:\n", + "In file included from /Users/uweschmitt/Projects/hope/venv3/lib/python3.5/site-packages/numpy/core/include/numpy/arrayobject.h:4:\n", + "In file included from /Users/uweschmitt/Projects/hope/venv3/lib/python3.5/site-packages/numpy/core/include/numpy/ndarrayobject.h:18:\n", + "In file included from /Users/uweschmitt/Projects/hope/venv3/lib/python3.5/site-packages/numpy/core/include/numpy/ndarraytypes.h:1809:\n", + "/Users/uweschmitt/Projects/hope/venv3/lib/python3.5/site-packages/numpy/core/include/numpy/npy_1_7_deprecated_api.h:15:2: warning: \"Using deprecated NumPy API, disable it by \" \"#defining NPY_NO_DEPRECATED_API NPY_1_7_API_VERSION\" [-W#warnings]\n", + "#warning \"Using deprecated NumPy API, disable it by \" \\\n", + " ^\n", + "1 warning generated.\n", + "In file included from /Users/uweschmitt/.ipython/cython/_cython_magic_c0a8dbdf5333e81ffeb4f99decde5aac.c:495:\n", + "In file included from /Users/uweschmitt/Projects/hope/venv3/lib/python3.5/site-packages/numpy/core/include/numpy/arrayobject.h:4:\n", + "In file included from /Users/uweschmitt/Projects/hope/venv3/lib/python3.5/site-packages/numpy/core/include/numpy/ndarrayobject.h:18:\n", + "In file included from /Users/uweschmitt/Projects/hope/venv3/lib/python3.5/site-packages/numpy/core/include/numpy/ndarraytypes.h:1809:\n", + "/Users/uweschmitt/Projects/hope/venv3/lib/python3.5/site-packages/numpy/core/include/numpy/npy_1_7_deprecated_api.h:15:2: warning: \"Using deprecated NumPy API, disable it by \" \"#defining NPY_NO_DEPRECATED_API NPY_1_7_API_VERSION\" [-W#warnings]\n", + "#warning \"Using deprecated NumPy API, disable it by \" \\\n", + " ^\n", + "1 warning generated.\n" + ] + } + ], + "source": [ + "%%cython\n", + "\n", + "cimport cython\n", + "\n", + "cimport cython\n", + "import numpy as np\n", + "cimport numpy as np\n", + "\n", + "@cython.boundscheck(False)\n", + "@cython.wraparound(False)\n", + "def ln_cython(np.ndarray[np.double_t, ndim=1] X, np.ndarray[np.double_t, ndim=1] Y):\n", + " Y[:] = (X-1) - (X-1)**2 / 2 + (X-1)**3 / 3 - (X-1)**4 / 4 + (X-1)**5 / 5 - (X-1)**6 / 6 + (X-1)**7 / 7 - (X-1)**8 / 8 + (X-1)**9 / 9\n", + "\n", + " \n", + "@cython.boundscheck(False)\n", + "@cython.wraparound(False)\n", + "def ln_cython_exp(np.ndarray[np.float_t, ndim=1] X, np.ndarray[np.float_t, ndim=1] Y):\n", + " cdef np.ndarray[np.double_t, ndim=1] x = (X - 1)\n", + " cdef np.ndarray[np.double_t, ndim=1] x2 = x*x\n", + " cdef np.ndarray[np.double_t, ndim=1] x4 = x2*x2\n", + " cdef np.ndarray[np.double_t, ndim=1] x6 = x4*x2\n", + " cdef np.ndarray[np.double_t, ndim=1] x8 = x4*x4\n", + " Y[:] = x - x2 / 2 + x * x2 / 3 - x4 / 4 + x * x4 / 5 - x6 / 6 + x6 * x / 7 - x8 / 8 + x8 * x / 9" + ] + }, + { + "cell_type": "code", + "execution_count": 13, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "running build_ext\n", + "building 'ln' extension\n", + "C compiler: /usr/bin/clang -fno-strict-aliasing -Wsign-compare -Wunreachable-code -fno-common -dynamic -DNDEBUG -g -fwrapv -O3 -Wall -Wstrict-prototypes -arch i386 -arch x86_64 -g\n", "\n", - "print \"python\"\n", - "%timeit ln_python(X, Y)\n", - "%timeit ln_python_exp(X, Y)\n", - "print \"numexpr (1)\"\n", - "ne.set_num_threads(1)\n", - "%timeit ln_numexpr(X, Y)\n", - "print \"numexpr ({0})\".format(ne.detect_number_of_cores())\n", - "ne.set_num_threads(ne.detect_number_of_cores())\n", - "%timeit ln_numexpr(X, Y)\n", - "print \"hope\"\n", - "%timeit ln_hope(X, Y)\n", - "%timeit ln_hope_exp(X, Y)\n", - "%timeit ln_hope_opt(X, Y)\n", - "print \"cython\"\n", - "%timeit ln_cython(X, Y)\n", - "%timeit ln_cython_exp(X, Y)\n", - "print \"numba\"\n", - "%timeit ln_numba(X, Y)\n", - "%timeit ln_numba_exp(X, Y)\n", - "print \"native\"\n", - "%timeit ln_native(X, Y)\n", - "%timeit ln_native_opt(X, Y)\n", - "%timeit ln_native_exp(X, Y)\n" - ], - "language": "python", - "metadata": { - "slideshow": { - "slide_type": "slide" - } - }, - "outputs": [ - { - "output_type": "stream", - "stream": "stdout", - "text": [ - "python\n", - "100 loops, best of 3: 2.4 ms per loop" - ] - }, - { - "output_type": "stream", - "stream": "stdout", - "text": [ - "\n", - "1000 loops, best of 3: 299 \u00b5s per loop" - ] - }, - { - "output_type": "stream", - "stream": "stdout", - "text": [ - "\n", - "numexpr (1)\n", - "1000 loops, best of 3: 400 \u00b5s per loop" - ] - }, - { - "output_type": "stream", - "stream": "stdout", - "text": [ - "\n", - "numexpr (8)\n", - "1000 loops, best of 3: 200 \u00b5s per loop" - ] - }, - { - "output_type": "stream", - "stream": "stdout", - "text": [ - "\n", - "hope\n", - "10000 loops, best of 3: 128 \u00b5s per loop" - ] - }, - { - "output_type": "stream", - "stream": "stdout", - "text": [ - "\n", - "1000 loops, best of 3: 212 \u00b5s per loop" - ] - }, - { - "output_type": "stream", - "stream": "stdout", - "text": [ - "\n", - "10000 loops, best of 3: 128 \u00b5s per loop" - ] - }, - { - "output_type": "stream", - "stream": "stdout", - "text": [ - "\n", - "cython\n", - "100 loops, best of 3: 2.4 ms per loop" - ] - }, - { - "output_type": "stream", - "stream": "stdout", - "text": [ - "\n", - "1000 loops, best of 3: 305 \u00b5s per loop" - ] - }, - { - "output_type": "stream", - "stream": "stdout", - "text": [ - "\n", - "numba\n", - "100 loops, best of 3: 2.41 ms per loop" - ] - }, - { - "output_type": "stream", - "stream": "stdout", - "text": [ - "\n", - "1000 loops, best of 3: 305 \u00b5s per loop" - ] - }, - { - "output_type": "stream", - "stream": "stdout", - "text": [ - "\n", - "native\n", - "100 loops, best of 3: 2.12 ms per loop" - ] - }, - { - "output_type": "stream", - "stream": "stdout", - "text": [ - "\n", - "10000 loops, best of 3: 128 \u00b5s per loop" - ] - }, - { - "output_type": "stream", - "stream": "stdout", - "text": [ - "\n", - "10000 loops, best of 3: 106 \u00b5s per loop" - ] - }, - { - "output_type": "stream", - "stream": "stdout", - "text": [ - "\n" - ] - } - ], - "prompt_number": 18 + "compile options: '-I/Users/uweschmitt/Projects/hope/venv3/lib/python3.5/site-packages/numpy/core/include -I/Users/uweschmitt/Projects/hope/venv3/include -I/Library/Frameworks/Python.framework/Versions/3.5/include/python3.5m -c'\n", + "extra options: '-Wall -Wno-unused-variable -march=native -stdlib=libc++ -std=c++11 -Wno-unreachable-code'\n", + "clang: ././src/ln.cpp\n", + "././src/ln.cpp:105:9: error: use of undeclared identifier 'Py_InitModule'\n", + " (void)Py_InitModule(\"ln\", ln_hopeMethods);\n", + " ^\n", + "1 error generated.\n", + "././src/ln.cpp:105:9: error: use of undeclared identifier 'Py_InitModule'\n", + " (void)Py_InitModule(\"ln\", ln_hopeMethods);\n", + " ^\n", + "1 error generated.\n", + "error: Command \"/usr/bin/clang -fno-strict-aliasing -Wsign-compare -Wunreachable-code -fno-common -dynamic -DNDEBUG -g -fwrapv -O3 -Wall -Wstrict-prototypes -arch i386 -arch x86_64 -g -I/Users/uweschmitt/Projects/hope/venv3/lib/python3.5/site-packages/numpy/core/include -I/Users/uweschmitt/Projects/hope/venv3/include -I/Library/Frameworks/Python.framework/Versions/3.5/include/python3.5m -c ././src/ln.cpp -o ./src/ln.o -Wall -Wno-unused-variable -march=native -stdlib=libc++ -std=c++11 -Wno-unreachable-code\" failed with exit status 1537\n", + "\n" + ] }, { - "cell_type": "code", - "collapsed": false, - "input": [ - "from util import perf_comp_data" - ], - "language": "python", - "metadata": {}, - "outputs": [], - "prompt_number": 19 - }, + "ename": "Exception", + "evalue": "Error compiling function ./src/ln (compiled to ./)", + "output_type": "error", + "traceback": [ + "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m", + "\u001b[0;31mException\u001b[0m Traceback (most recent call last)", + "\u001b[0;32m\u001b[0m in \u001b[0;36m\u001b[0;34m()\u001b[0m\n\u001b[1;32m 1\u001b[0m \u001b[0;32mfrom\u001b[0m \u001b[0mnative_util\u001b[0m \u001b[0;32mimport\u001b[0m \u001b[0mload\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m----> 2\u001b[0;31m \u001b[0mnative_ln_mod\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mload\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m\"ln\"\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 3\u001b[0m \u001b[0mln_native\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mnative_ln_mod\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mrun\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 4\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 5\u001b[0m \u001b[0mnative_ln_opt_mod\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mload\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m\"ln_opt\"\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n", + "\u001b[0;32m~/Projects/hope/benchmarks/native_util.py\u001b[0m in \u001b[0;36mload\u001b[0;34m(name)\u001b[0m\n\u001b[1;32m 19\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 20\u001b[0m \u001b[0;32mdef\u001b[0m \u001b[0mload\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mname\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m---> 21\u001b[0;31m \u001b[0mcompile\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mname\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0;34m\"./src\"\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 22\u001b[0m \u001b[0mmodule\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mimportlib\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mimport_module\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mname\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 23\u001b[0m \u001b[0;31m# module = __import__(name, globals(), locals(), [], -1)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n", + "\u001b[0;32m~/Projects/hope/benchmarks/native_util.py\u001b[0m in \u001b[0;36mcompile\u001b[0;34m(name, src_folder, target_folder)\u001b[0m\n\u001b[1;32m 98\u001b[0m \u001b[0;32mif\u001b[0m \u001b[0;32mnot\u001b[0m \u001b[0mmodules\u001b[0m \u001b[0;32mor\u001b[0m \u001b[0mout\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mfind\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m\"error:\"\u001b[0m\u001b[0;34m)\u001b[0m \u001b[0;34m>\u001b[0m \u001b[0;34m-\u001b[0m\u001b[0;36m1\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 99\u001b[0m \u001b[0mprint\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mout\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m--> 100\u001b[0;31m \u001b[0;32mraise\u001b[0m \u001b[0mException\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m\"Error compiling function {0} (compiled to {1})\"\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mformat\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mlocalfilename\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mtarget_folder\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 101\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 102\u001b[0m \u001b[0;32mif\u001b[0m \u001b[0mout\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mfind\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m\"warning:\"\u001b[0m\u001b[0;34m)\u001b[0m \u001b[0;34m>\u001b[0m \u001b[0;34m-\u001b[0m\u001b[0;36m1\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n", + "\u001b[0;31mException\u001b[0m: Error compiling function ./src/ln (compiled to ./)" + ] + } + ], + "source": [ + "from native_util import load\n", + "native_ln_mod = load(\"ln\")\n", + "ln_native = native_ln_mod.run\n", + "\n", + "native_ln_opt_mod = load(\"ln_opt\")\n", + "ln_native_opt = native_ln_opt_mod.run\n", + "\n", + "native_ln_exp_mod = load(\"ln_exp\")\n", + "ln_native_exp = native_ln_exp_mod.run\n" + ] + }, + { + "cell_type": "code", + "execution_count": 13, + "metadata": { + "collapsed": true, + "slideshow": { + "slide_type": "notes" + } + }, + "outputs": [], + "source": [ + "# Hope version - optimized\n", + "\n", + "import hope\n", + "hope.config.optimize = True\n", + "\n", + "@hope.jit\n", + "def ln_hope_opt(X, Y):\n", + " Y[:] = (X-1) - (X-1)**2 / 2 + (X-1)**3 / 3 - (X-1)**4 / 4 + (X-1)**5 / 5 - (X-1)**6 / 6 + (X-1)**7 / 7 - (X-1)**8 / 8 + (X-1)**9 / 9\n", + "\n", + "\n", + "hope.config.optimize = False" + ] + }, + { + "cell_type": "code", + "execution_count": 15, + "metadata": { + "collapsed": true, + "slideshow": { + "slide_type": "slide" + } + }, + "outputs": [], + "source": [ + "import numpy as np\n", + "\n", + "X = np.random.random(10000).astype(np.float64)\n", + "Y = np.ones_like(X)" + ] + }, + { + "cell_type": "code", + "execution_count": 17, + "metadata": { + "collapsed": true + }, + "outputs": [], + "source": [ + "Y1 = np.ones_like(X)\n", + "Y2 = np.ones_like(X)\n", + "Y3 = np.ones_like(X)\n", + "Y4 = np.ones_like(X)\n", + "Y5 = np.ones_like(X)\n", + "Y6 = np.ones_like(X)\n", + "Y7 = np.ones_like(X)\n", + "Y8 = np.ones_like(X)\n", + "Y9 = np.ones_like(X)\n", + "Y10 = np.ones_like(X)\n", + "Y11 = np.ones_like(X)\n", + "Y12 = np.ones_like(X)\n", + "Y13 = np.ones_like(X)\n", + "\n", + "ln_python(X, Y1)\n", + "ln_python_exp(X, Y2)\n", + "ln_cython(X, Y3)\n", + "ln_cython_exp(X, Y4)\n", + "ln_numexpr(X, Y5)\n", + "ln_hope(X, Y6)\n", + "ln_hope_exp(X, Y7)\n", + "ln_numba(X, Y8)\n", + "ln_numba_exp(X, Y9)\n", + "ln_native(X, Y10)\n", + "ln_native_opt(X, Y11)\n", + "ln_native_exp(X, Y12)\n", + "ln_hope_opt(X, Y13)\n", + "\n", + "assert np.allclose(Y1,Y2, 1E-10)\n", + "assert np.allclose(Y1,Y3, 1E-10)\n", + "assert np.allclose(Y1,Y4, 1E-10)\n", + "assert np.allclose(Y1,Y5, 1E-10)\n", + "assert np.allclose(Y1,Y6, 1E-10)\n", + "assert np.allclose(Y1,Y7, 1E-10)\n", + "assert np.allclose(Y1,Y8, 1E-10)\n", + "assert np.allclose(Y1,Y9, 1E-10)\n", + "assert np.allclose(Y1,Y10, 1E-10)\n", + "assert np.allclose(Y1,Y11, 1E-10)\n", + "assert np.allclose(Y1,Y12, 1E-10)\n", + "assert np.allclose(Y1,Y13, 1E-10)\n" + ] + }, + { + "cell_type": "code", + "execution_count": 18, + "metadata": { + "slideshow": { + "slide_type": "slide" + } + }, + "outputs": [ { - "cell_type": "code", - "collapsed": false, - "input": [ - "func_list = [\"ln_python\", \"ln_python_exp\", \n", - " \"ln_numexpr\", \n", - " \"ln_hope\", \"ln_hope_exp\", \"ln_hope_opt\", \n", - " \"ln_cython\", \"ln_cython_exp\", \n", - " \"ln_numba\", \"ln_numba_exp\", \n", - " \"ln_native\", \"ln_native_opt\", \"ln_native_exp\", ]\n", - "perf_comp_data(func_list,\n", - " len(func_list)*[\"X, Y\"], rep=100)" - ], - "language": "python", - "metadata": {}, - "outputs": [ - { - "output_type": "stream", - "stream": "stdout", - "text": [ - "function: ln_native_exp , av. time sec: 0.00010705, min. time sec: 0.00010586, relative: 1.0\n", - "function: ln_hope_opt , av. time sec: 0.00012398, min. time sec: 0.00012398, relative: 1.2\n", - "function: ln_hope , av. time sec: 0.00012422, min. time sec: 0.00012398, relative: 1.2\n", - "function: ln_native_opt , av. time sec: 0.00012803, min. time sec: 0.00012708, relative: 1.2\n", - "function: ln_hope_exp , av. time sec: 0.00020707, min. time sec: 0.00020695, relative: 1.9\n", - "function: ln_numexpr , av. time sec: 0.00021243, min. time sec: 0.00018501, relative: 2.0\n", - "function: ln_python_exp , av. time sec: 0.00029302, min. time sec: 0.00029206, relative: 2.7\n", - "function: ln_numba_exp , av. time sec: 0.00030351, min. time sec: 0.00029993, relative: 2.8\n", - "function: ln_cython_exp , av. time sec: 0.00030696, min. time sec: 0.00029683, relative: 2.9\n", - "function: ln_native , av. time sec: 0.00215054, min. time sec: 0.00208783, relative: 20.1\n", - "function: ln_cython , av. time sec: 0.00241804, min. time sec: 0.00235510, relative: 22.6\n", - "function: ln_numba , av. time sec: 0.00241804, min. time sec: 0.00237322, relative: 22.6\n", - "function: ln_python , av. time sec: 0.00243747, min. time sec: 0.00235200, relative: 22.8\n" - ] - } - ], - "prompt_number": 20 - }, + "name": "stdout", + "output_type": "stream", + "text": [ + "python\n", + "100 loops, best of 3: 2.4 ms per loop\n", + "1000 loops, best of 3: 299 µs per loop\n", + "numexpr (1)\n", + "1000 loops, best of 3: 400 µs per loop\n", + "numexpr (8)\n", + "1000 loops, best of 3: 200 µs per loop\n", + "hope\n", + "10000 loops, best of 3: 128 µs per loop\n", + "1000 loops, best of 3: 212 µs per loop\n", + "10000 loops, best of 3: 128 µs per loop\n", + "cython\n", + "100 loops, best of 3: 2.4 ms per loop\n", + "1000 loops, best of 3: 305 µs per loop\n", + "numba\n", + "100 loops, best of 3: 2.41 ms per loop\n", + "1000 loops, best of 3: 305 µs per loop\n", + "native\n", + "100 loops, best of 3: 2.12 ms per loop\n", + "10000 loops, best of 3: 128 µs per loop\n", + "10000 loops, best of 3: 106 µs per loop\n" + ] + } + ], + "source": [ + "import numexpr as ne\n", + "\n", + "print \"python\"\n", + "%timeit ln_python(X, Y)\n", + "%timeit ln_python_exp(X, Y)\n", + "print \"numexpr (1)\"\n", + "ne.set_num_threads(1)\n", + "%timeit ln_numexpr(X, Y)\n", + "print \"numexpr ({0})\".format(ne.detect_number_of_cores())\n", + "ne.set_num_threads(ne.detect_number_of_cores())\n", + "%timeit ln_numexpr(X, Y)\n", + "print \"hope\"\n", + "%timeit ln_hope(X, Y)\n", + "%timeit ln_hope_exp(X, Y)\n", + "%timeit ln_hope_opt(X, Y)\n", + "print \"cython\"\n", + "%timeit ln_cython(X, Y)\n", + "%timeit ln_cython_exp(X, Y)\n", + "print \"numba\"\n", + "%timeit ln_numba(X, Y)\n", + "%timeit ln_numba_exp(X, Y)\n", + "print \"native\"\n", + "%timeit ln_native(X, Y)\n", + "%timeit ln_native_opt(X, Y)\n", + "%timeit ln_native_exp(X, Y)\n" + ] + }, + { + "cell_type": "code", + "execution_count": 19, + "metadata": { + "collapsed": true + }, + "outputs": [], + "source": [ + "from util import perf_comp_data" + ] + }, + { + "cell_type": "code", + "execution_count": 20, + "metadata": {}, + "outputs": [ { - "cell_type": "code", - "collapsed": false, - "input": [], - "language": "python", - "metadata": {}, - "outputs": [] + "name": "stdout", + "output_type": "stream", + "text": [ + "function: ln_native_exp , av. time sec: 0.00010705, min. time sec: 0.00010586, relative: 1.0\n", + "function: ln_hope_opt , av. time sec: 0.00012398, min. time sec: 0.00012398, relative: 1.2\n", + "function: ln_hope , av. time sec: 0.00012422, min. time sec: 0.00012398, relative: 1.2\n", + "function: ln_native_opt , av. time sec: 0.00012803, min. time sec: 0.00012708, relative: 1.2\n", + "function: ln_hope_exp , av. time sec: 0.00020707, min. time sec: 0.00020695, relative: 1.9\n", + "function: ln_numexpr , av. time sec: 0.00021243, min. time sec: 0.00018501, relative: 2.0\n", + "function: ln_python_exp , av. time sec: 0.00029302, min. time sec: 0.00029206, relative: 2.7\n", + "function: ln_numba_exp , av. time sec: 0.00030351, min. time sec: 0.00029993, relative: 2.8\n", + "function: ln_cython_exp , av. time sec: 0.00030696, min. time sec: 0.00029683, relative: 2.9\n", + "function: ln_native , av. time sec: 0.00215054, min. time sec: 0.00208783, relative: 20.1\n", + "function: ln_cython , av. time sec: 0.00241804, min. time sec: 0.00235510, relative: 22.6\n", + "function: ln_numba , av. time sec: 0.00241804, min. time sec: 0.00237322, relative: 22.6\n", + "function: ln_python , av. time sec: 0.00243747, min. time sec: 0.00235200, relative: 22.8\n" + ] } ], - "metadata": {} + "source": [ + "func_list = [\"ln_python\", \"ln_python_exp\", \n", + " \"ln_numexpr\", \n", + " \"ln_hope\", \"ln_hope_exp\", \"ln_hope_opt\", \n", + " \"ln_cython\", \"ln_cython_exp\", \n", + " \"ln_numba\", \"ln_numba_exp\", \n", + " \"ln_native\", \"ln_native_opt\", \"ln_native_exp\", ]\n", + "perf_comp_data(func_list,\n", + " len(func_list)*[\"X, Y\"], rep=100)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "collapsed": true + }, + "outputs": [], + "source": [] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.5.1" } - ] -} \ No newline at end of file + }, + "nbformat": 4, + "nbformat_minor": 1 +} diff --git a/benchmarks/pairwise.ipynb b/benchmarks/pairwise.ipynb index 98acb33..af69c4b 100644 --- a/benchmarks/pairwise.ipynb +++ b/benchmarks/pairwise.ipynb @@ -1,385 +1,360 @@ { - "metadata": { - "name": "", - "signature": "sha256:1fb785098fbe4c8872abc9c10232ddb25cc7c2e7aa992b02730c9fd63ff5b18f" - }, - "nbformat": 3, - "nbformat_minor": 0, - "worksheets": [ + "cells": [ { - "cells": [ + "cell_type": "code", + "execution_count": 1, + "metadata": {}, + "outputs": [ { - "cell_type": "code", - "collapsed": false, - "input": [ - "import hope\n", - "hope.config.optimize = True\n", - "hope.config.verbose = True\n", - "hope.config.keeptemp = True\n", - "import numba\n", - "import numpy as np\n", - "from util import perf_comp_data\n", - "from native_util import load\n", - "%load_ext cythonmagic\n", - "%load_ext version_information\n", - "%version_information numpy, Cython, numba, hope" - ], - "language": "python", - "metadata": {}, - "outputs": [ - { - "html": [ - "
SoftwareVersion
Python2.7.8 (default, Jul 13 2014, 17:11:32) [GCC 4.2.1 Compatible Apple LLVM 5.1 (clang-503.0.40)]
IPython1.1.0
OSposix [darwin]
numpy1.8.1
Cython0.20.2
numba0.13.3
hope0.3.0
Thu Sep 04 15:18:18 2014 CEST
" - ], - "json": [ - "{\"Software versions\": [{\"version\": \"2.7.8 (default, Jul 13 2014, 17:11:32) [GCC 4.2.1 Compatible Apple LLVM 5.1 (clang-503.0.40)]\", \"module\": \"Python\"}, {\"version\": \"1.1.0\", \"module\": \"IPython\"}, {\"version\": \"posix [darwin]\", \"module\": \"OS\"}, {\"version\": \"1.8.1\", \"module\": \"numpy\"}, {\"version\": \"0.20.2\", \"module\": \"Cython\"}, {\"version\": \"0.13.3\", \"module\": \"numba\"}, {\"version\": \"0.3.0\", \"module\": \"hope\"}]}" - ], - "latex": [ - "\\begin{tabular}{|l|l|}\\hline\n", - "{\\bf Software} & {\\bf Version} \\\\ \\hline\\hline\n", - "Python & 2.7.8 (default, Jul 13 2014, 17:11:32) [GCC 4.2.1 Compatible Apple LLVM 5.1 (clang-503.0.40)] \\\\ \\hline\n", - "IPython & 1.1.0 \\\\ \\hline\n", - "OS & posix [darwin] \\\\ \\hline\n", - "numpy & 1.8.1 \\\\ \\hline\n", - "Cython & 0.20.2 \\\\ \\hline\n", - "numba & 0.13.3 \\\\ \\hline\n", - "hope & 0.3.0 \\\\ \\hline\n", - "\\hline \\multicolumn{2}{|l|}{Thu Sep 04 15:18:18 2014 CEST} \\\\ \\hline\n", - "\\end{tabular}\n" - ], - "metadata": {}, - "output_type": "pyout", - "prompt_number": 1, - "text": [ - "Software versions\n", - "Python 2.7.8 (default, Jul 13 2014, 17:11:32) [GCC 4.2.1 Compatible Apple LLVM 5.1 (clang-503.0.40)]\n", - "IPython 1.1.0\n", - "OS posix [darwin]\n", - "numpy 1.8.1\n", - "Cython 0.20.2\n", - "numba 0.13.3\n", - "hope 0.3.0\n", - "Thu Sep 04 15:18:18 2014 CEST" + "data": { + "application/json": { + "Software versions": [ + { + "module": "Python", + "version": "2.7.8 (default, Jul 13 2014, 17:11:32) [GCC 4.2.1 Compatible Apple LLVM 5.1 (clang-503.0.40)]" + }, + { + "module": "IPython", + "version": "1.1.0" + }, + { + "module": "OS", + "version": "posix [darwin]" + }, + { + "module": "numpy", + "version": "1.8.1" + }, + { + "module": "Cython", + "version": "0.20.2" + }, + { + "module": "numba", + "version": "0.13.3" + }, + { + "module": "hope", + "version": "0.3.0" + } ] - } - ], - "prompt_number": 1 - }, - { - "cell_type": "code", - "collapsed": false, - "input": [ - "# Pure python version\n", - "\n", - "def pairwise_python(X, D):\n", - " M = X.shape[0]\n", - " N = X.shape[1]\n", - " for i in range(M):\n", - " for j in range(M):\n", - " d = 0.0\n", - " for k in range(N):\n", - " tmp = X[i, k] - X[j, k]\n", - " d += tmp * tmp\n", - " D[i, j] = np.sqrt(d)" - ], - "language": "python", - "metadata": {}, - "outputs": [], - "prompt_number": 2 - }, - { - "cell_type": "code", - "collapsed": false, - "input": [ - "# Numpy python version\n", - "\n", - "def pairwise_numpy(X, D):\n", - " M = X.shape[0]\n", - " for i in range(M):\n", - " D[i, :] = np.sqrt(np.sum((X[i, :] - X[:]) ** 2, axis=1))" - ], - "language": "python", - "metadata": {}, - "outputs": [], - "prompt_number": 3 - }, - { - "cell_type": "code", - "collapsed": false, - "input": [ - "# numba version\n", - "@numba.jit(nopython=True)\n", - "def pairwise_numba(X, D):\n", - " M = X.shape[0]\n", - " N = X.shape[1]\n", - " for i in range(M):\n", - " for j in range(M):\n", - " d = 0.0\n", - " for k in range(N):\n", - " tmp = X[i, k] - X[j, k]\n", - " d += tmp * tmp\n", - " D[i, j] = np.sqrt(d)" - ], - "language": "python", - "metadata": {}, - "outputs": [], - "prompt_number": 4 - }, - { - "cell_type": "code", - "collapsed": false, - "input": [ - "%%cython\n", - "\n", - "cimport cython\n", - "from libc.math cimport sqrt\n", - "\n", - "@cython.boundscheck(False)\n", - "@cython.wraparound(False)\n", - "def pairwise_cython(double[:, ::1] X, double[:, ::1] D):\n", - " cdef int M = X.shape[0]\n", - " cdef int N = X.shape[1]\n", - " cdef double tmp, d\n", - " for i in range(M):\n", - " for j in range(M):\n", - " d = 0.0\n", - " for k in range(N):\n", - " tmp = X[i, k] - X[j, k]\n", - " d += tmp * tmp\n", - " D[i, j] = sqrt(d)" - ], - "language": "python", - "metadata": {}, - "outputs": [], - "prompt_number": 5 - }, - { - "cell_type": "code", - "collapsed": false, - "input": [ - "# hope version\n", - "@hope.jit\n", - "def pairwise_hope(X, D, M, N):\n", - " for i in range(M):\n", - " for j in range(M):\n", - " d = 0.0\n", - " for k in range(N):\n", - " tmp = X[i, k] - X[j, k]\n", - " d += tmp * tmp\n", - " D[i, j] = np.sqrt(d)\n" - ], - "language": "python", - "metadata": {}, - "outputs": [], - "prompt_number": 6 - }, - { - "cell_type": "code", - "collapsed": false, - "input": [ - "from native_util import load\n", - "\n", - "native_pairwise_mod = load(\"pairwise\")\n", - "pairwise_native = native_pairwise_mod.run" - ], - "language": "python", - "metadata": {}, - "outputs": [ - { - "output_type": "stream", - "stream": "stdout", - "text": [ - "running build_ext\n", - "building 'pairwise' extension\n", - "C compiler: /usr/bin/clang -fno-strict-aliasing -fno-common -dynamic -pipe -Os -fwrapv -DNDEBUG -g -fwrapv -O3 -Wall -Wstrict-prototypes\n", - "\n", - "compile options: '-I/Users/jakeret/Library/Python/2.7/lib/python/site-packages/numpy/core/include -I/opt/local/Library/Frameworks/Python.framework/Versions/2.7/include/python2.7 -c'\n", - "extra options: '-Wall -Wno-unused-variable -march=native -stdlib=libc++ -std=c++11'\n", - "clang: ././src/pairwise.cpp\n", - "/usr/bin/clang++ -bundle -undefined dynamic_lookup -L/opt/local/lib -Wl,-headerpad_max_install_names -L/opt/local/lib/db46 ./src/pairwise.o -o ./pairwise.so\n", - "\n" - ] - } - ], - "prompt_number": 7 - }, - { - "cell_type": "code", - "collapsed": false, - "input": [ - "X = np.random.random((1000, 3))\n", - "D = np.empty((1000, 1000))" - ], - "language": "python", + }, + "text/html": [ + "
SoftwareVersion
Python2.7.8 (default, Jul 13 2014, 17:11:32) [GCC 4.2.1 Compatible Apple LLVM 5.1 (clang-503.0.40)]
IPython1.1.0
OSposix [darwin]
numpy1.8.1
Cython0.20.2
numba0.13.3
hope0.3.0
Thu Sep 04 15:18:18 2014 CEST
" + ], + "text/latex": [ + "\\begin{tabular}{|l|l|}\\hline\n", + "{\\bf Software} & {\\bf Version} \\\\ \\hline\\hline\n", + "Python & 2.7.8 (default, Jul 13 2014, 17:11:32) [GCC 4.2.1 Compatible Apple LLVM 5.1 (clang-503.0.40)] \\\\ \\hline\n", + "IPython & 1.1.0 \\\\ \\hline\n", + "OS & posix [darwin] \\\\ \\hline\n", + "numpy & 1.8.1 \\\\ \\hline\n", + "Cython & 0.20.2 \\\\ \\hline\n", + "numba & 0.13.3 \\\\ \\hline\n", + "hope & 0.3.0 \\\\ \\hline\n", + "\\hline \\multicolumn{2}{|l|}{Thu Sep 04 15:18:18 2014 CEST} \\\\ \\hline\n", + "\\end{tabular}\n" + ], + "text/plain": [ + "Software versions\n", + "Python 2.7.8 (default, Jul 13 2014, 17:11:32) [GCC 4.2.1 Compatible Apple LLVM 5.1 (clang-503.0.40)]\n", + "IPython 1.1.0\n", + "OS posix [darwin]\n", + "numpy 1.8.1\n", + "Cython 0.20.2\n", + "numba 0.13.3\n", + "hope 0.3.0\n", + "Thu Sep 04 15:18:18 2014 CEST" + ] + }, + "execution_count": 1, "metadata": {}, - "outputs": [], - "prompt_number": 8 - }, + "output_type": "execute_result" + } + ], + "source": [ + "import hope\n", + "hope.config.optimize = True\n", + "hope.config.verbose = True\n", + "hope.config.keeptemp = True\n", + "import numba\n", + "import numpy as np\n", + "from util import perf_comp_data\n", + "from native_util import load\n", + "%load_ext cythonmagic\n", + "%load_ext version_information\n", + "%version_information numpy, Cython, numba, hope" + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "metadata": {}, + "outputs": [], + "source": [ + "# Pure python version\n", + "\n", + "def pairwise_python(X, D):\n", + " M = X.shape[0]\n", + " N = X.shape[1]\n", + " for i in range(M):\n", + " for j in range(M):\n", + " d = 0.0\n", + " for k in range(N):\n", + " tmp = X[i, k] - X[j, k]\n", + " d += tmp * tmp\n", + " D[i, j] = np.sqrt(d)" + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "metadata": {}, + "outputs": [], + "source": [ + "# Numpy python version\n", + "\n", + "def pairwise_numpy(X, D):\n", + " M = X.shape[0]\n", + " for i in range(M):\n", + " D[i, :] = np.sqrt(np.sum((X[i, :] - X[:]) ** 2, axis=1))" + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "metadata": {}, + "outputs": [], + "source": [ + "# numba version\n", + "@numba.jit(nopython=True)\n", + "def pairwise_numba(X, D):\n", + " M = X.shape[0]\n", + " N = X.shape[1]\n", + " for i in range(M):\n", + " for j in range(M):\n", + " d = 0.0\n", + " for k in range(N):\n", + " tmp = X[i, k] - X[j, k]\n", + " d += tmp * tmp\n", + " D[i, j] = np.sqrt(d)" + ] + }, + { + "cell_type": "code", + "execution_count": 5, + "metadata": {}, + "outputs": [], + "source": [ + "%%cython\n", + "\n", + "cimport cython\n", + "from libc.math cimport sqrt\n", + "\n", + "@cython.boundscheck(False)\n", + "@cython.wraparound(False)\n", + "def pairwise_cython(double[:, ::1] X, double[:, ::1] D):\n", + " cdef int M = X.shape[0]\n", + " cdef int N = X.shape[1]\n", + " cdef double tmp, d\n", + " for i in range(M):\n", + " for j in range(M):\n", + " d = 0.0\n", + " for k in range(N):\n", + " tmp = X[i, k] - X[j, k]\n", + " d += tmp * tmp\n", + " D[i, j] = sqrt(d)" + ] + }, + { + "cell_type": "code", + "execution_count": 6, + "metadata": {}, + "outputs": [], + "source": [ + "# hope version\n", + "@hope.jit\n", + "def pairwise_hope(X, D, M, N):\n", + " for i in range(M):\n", + " for j in range(M):\n", + " d = 0.0\n", + " for k in range(N):\n", + " tmp = X[i, k] - X[j, k]\n", + " d += tmp * tmp\n", + " D[i, j] = np.sqrt(d)\n" + ] + }, + { + "cell_type": "code", + "execution_count": 7, + "metadata": {}, + "outputs": [ { - "cell_type": "code", - "collapsed": false, - "input": [ - "D1 = np.empty((1000, 1000))\n", - "D2 = np.empty((1000, 1000))\n", - "D3 = np.empty((1000, 1000))\n", - "D4 = np.empty((1000, 1000))\n", - "D5 = np.empty((1000, 1000))\n", - "D6 = np.empty((1000, 1000))\n", + "name": "stdout", + "output_type": "stream", + "text": [ + "running build_ext\n", + "building 'pairwise' extension\n", + "C compiler: /usr/bin/clang -fno-strict-aliasing -fno-common -dynamic -pipe -Os -fwrapv -DNDEBUG -g -fwrapv -O3 -Wall -Wstrict-prototypes\n", "\n", - "pairwise_python(X, D1)\n", - "pairwise_numpy(X, D2)\n", - "pairwise_numba(X, D3)\n", - "pairwise_cython(X, D4)\n", - "pairwise_hope(X, D5, X.shape[0], X.shape[1])\n", - "pairwise_native(X, D6, X.shape[0], X.shape[1])\n", - "\n", - "assert np.allclose(D1, D2)\n", - "assert np.allclose(D1, D3)\n", - "assert np.allclose(D1, D4)\n", - "assert np.allclose(D1, D5)\n", - "assert np.allclose(D1, D6)\n" - ], - "language": "python", - "metadata": {}, - "outputs": [], - "prompt_number": 10 - }, - { - "cell_type": "code", - "collapsed": false, - "input": [ - "print \"naive python\"\n", - "%timeit pairwise_python(X, D)\n", - "print \"numpy\"\n", - "%timeit pairwise_numpy(X, D)\n", - "print \"numba\"\n", - "%timeit pairwise_numba(X, D)\n", - "print \"cython\"\n", - "%timeit pairwise_cython(X, D)\n", - "print \"hope\"\n", - "%timeit pairwise_hope(X, D, X.shape[0], X.shape[1])\n", - "print \"native\"\n", - "%timeit pairwise_native(X, D, X.shape[0], X.shape[1])" - ], - "language": "python", - "metadata": {}, - "outputs": [ - { - "output_type": "stream", - "stream": "stdout", - "text": [ - "naive python\n", - "1 loops, best of 3: 5.75 s per loop" - ] - }, - { - "output_type": "stream", - "stream": "stdout", - "text": [ - "\n", - "numpy\n", - "10 loops, best of 3: 36.6 ms per loop" - ] - }, - { - "output_type": "stream", - "stream": "stdout", - "text": [ - "\n", - "numba\n", - "100 loops, best of 3: 6.88 ms per loop" - ] - }, - { - "output_type": "stream", - "stream": "stdout", - "text": [ - "\n", - "cython\n", - "100 loops, best of 3: 4.22 ms per loop" - ] - }, - { - "output_type": "stream", - "stream": "stdout", - "text": [ - "\n", - "hope\n", - "100 loops, best of 3: 6.36 ms per loop" - ] - }, - { - "output_type": "stream", - "stream": "stdout", - "text": [ - "\n", - "native\n", - "100 loops, best of 3: 4.23 ms per loop" - ] - }, - { - "output_type": "stream", - "stream": "stdout", - "text": [ - "\n" - ] - } - ], - "prompt_number": 11 - }, - { - "cell_type": "code", - "collapsed": false, - "input": [ - "from util import perf_comp_data" - ], - "language": "python", - "metadata": {}, - "outputs": [] - }, + "compile options: '-I/Users/jakeret/Library/Python/2.7/lib/python/site-packages/numpy/core/include -I/opt/local/Library/Frameworks/Python.framework/Versions/2.7/include/python2.7 -c'\n", + "extra options: '-Wall -Wno-unused-variable -march=native -stdlib=libc++ -std=c++11'\n", + "clang: ././src/pairwise.cpp\n", + "/usr/bin/clang++ -bundle -undefined dynamic_lookup -L/opt/local/lib -Wl,-headerpad_max_install_names -L/opt/local/lib/db46 ./src/pairwise.o -o ./pairwise.so\n", + "\n" + ] + } + ], + "source": [ + "from native_util import load\n", + "\n", + "native_pairwise_mod = load(\"pairwise\")\n", + "pairwise_native = native_pairwise_mod.run" + ] + }, + { + "cell_type": "code", + "execution_count": 8, + "metadata": {}, + "outputs": [], + "source": [ + "X = np.random.random((1000, 3))\n", + "D = np.empty((1000, 1000))" + ] + }, + { + "cell_type": "code", + "execution_count": 10, + "metadata": {}, + "outputs": [], + "source": [ + "D1 = np.empty((1000, 1000))\n", + "D2 = np.empty((1000, 1000))\n", + "D3 = np.empty((1000, 1000))\n", + "D4 = np.empty((1000, 1000))\n", + "D5 = np.empty((1000, 1000))\n", + "D6 = np.empty((1000, 1000))\n", + "\n", + "pairwise_python(X, D1)\n", + "pairwise_numpy(X, D2)\n", + "pairwise_numba(X, D3)\n", + "pairwise_cython(X, D4)\n", + "pairwise_hope(X, D5, X.shape[0], X.shape[1])\n", + "pairwise_native(X, D6, X.shape[0], X.shape[1])\n", + "\n", + "assert np.allclose(D1, D2)\n", + "assert np.allclose(D1, D3)\n", + "assert np.allclose(D1, D4)\n", + "assert np.allclose(D1, D5)\n", + "assert np.allclose(D1, D6)\n" + ] + }, + { + "cell_type": "code", + "execution_count": 11, + "metadata": {}, + "outputs": [ { - "cell_type": "code", - "collapsed": false, - "input": [ - "M, N = X.shape\n", - "data_list = 4*[\"X, D\"]+ 2*[\"X, D, M, N\"]\n", - "#print data_list\n", - "perf_comp_data([\"pairwise_python\", \n", - " \"pairwise_numpy\", \n", - " \"pairwise_numba\", \n", - " \"pairwise_cython\", \n", - " \"pairwise_hope\",\n", - " \"pairwise_native\"],\n", - " data_list, rep=100)" - ], - "language": "python", - "metadata": {}, - "outputs": [ - { - "output_type": "stream", - "stream": "stdout", - "text": [ - "function: pairwise_native , av. time sec: 0.00420749, min. time sec: 0.00414991, relative: 1.0\n", - "function: pairwise_cython , av. time sec: 0.00421405, min. time sec: 0.00415277, relative: 1.0\n", - "function: pairwise_hope , av. time sec: 0.00635457, min. time sec: 0.00626707, relative: 1.5\n", - "function: pairwise_numba , av. time sec: 0.00690150, min. time sec: 0.00681400, relative: 1.6\n", - "function: pairwise_numpy , av. time sec: 0.03677249, min. time sec: 0.03618908, relative: 8.7\n", - "function: pairwise_python , av. time sec: 5.90122139, min. time sec: 5.63599896, relative: 1402.6\n" - ] - } - ], - "prompt_number": 13 - }, + "name": "stdout", + "output_type": "stream", + "text": [ + "naive python\n", + "1 loops, best of 3: 5.75 s per loop\n", + "numpy\n", + "10 loops, best of 3: 36.6 ms per loop\n", + "numba\n", + "100 loops, best of 3: 6.88 ms per loop\n", + "cython\n", + "100 loops, best of 3: 4.22 ms per loop\n", + "hope\n", + "100 loops, best of 3: 6.36 ms per loop\n", + "native\n", + "100 loops, best of 3: 4.23 ms per loop\n" + ] + } + ], + "source": [ + "print \"naive python\"\n", + "%timeit pairwise_python(X, D)\n", + "print \"numpy\"\n", + "%timeit pairwise_numpy(X, D)\n", + "print \"numba\"\n", + "%timeit pairwise_numba(X, D)\n", + "print \"cython\"\n", + "%timeit pairwise_cython(X, D)\n", + "print \"hope\"\n", + "%timeit pairwise_hope(X, D, X.shape[0], X.shape[1])\n", + "print \"native\"\n", + "%timeit pairwise_native(X, D, X.shape[0], X.shape[1])" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "from util import perf_comp_data" + ] + }, + { + "cell_type": "code", + "execution_count": 13, + "metadata": {}, + "outputs": [ { - "cell_type": "code", - "collapsed": false, - "input": [], - "language": "python", - "metadata": {}, - "outputs": [] + "name": "stdout", + "output_type": "stream", + "text": [ + "function: pairwise_native , av. time sec: 0.00420749, min. time sec: 0.00414991, relative: 1.0\n", + "function: pairwise_cython , av. time sec: 0.00421405, min. time sec: 0.00415277, relative: 1.0\n", + "function: pairwise_hope , av. time sec: 0.00635457, min. time sec: 0.00626707, relative: 1.5\n", + "function: pairwise_numba , av. time sec: 0.00690150, min. time sec: 0.00681400, relative: 1.6\n", + "function: pairwise_numpy , av. time sec: 0.03677249, min. time sec: 0.03618908, relative: 8.7\n", + "function: pairwise_python , av. time sec: 5.90122139, min. time sec: 5.63599896, relative: 1402.6\n" + ] } ], - "metadata": {} + "source": [ + "M, N = X.shape\n", + "data_list = 4*[\"X, D\"]+ 2*[\"X, D, M, N\"]\n", + "#print data_list\n", + "perf_comp_data([\"pairwise_python\", \n", + " \"pairwise_numpy\", \n", + " \"pairwise_numba\", \n", + " \"pairwise_cython\", \n", + " \"pairwise_hope\",\n", + " \"pairwise_native\"],\n", + " data_list, rep=100)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.5.1" } - ] -} \ No newline at end of file + }, + "nbformat": 4, + "nbformat_minor": 1 +} diff --git a/benchmarks/simplify.ipynb b/benchmarks/simplify.ipynb index 5da6ab1..5312bce 100644 --- a/benchmarks/simplify.ipynb +++ b/benchmarks/simplify.ipynb @@ -1,346 +1,318 @@ { - "metadata": { - "name": "", - "signature": "sha256:162f3d58c482919f419e58ffcac472a163f0e58c1278956fd7cefd2e91118997" - }, - "nbformat": 3, - "nbformat_minor": 0, - "worksheets": [ + "cells": [ { - "cells": [ - { - "cell_type": "code", - "collapsed": false, - "input": [ - "import hope\n", - "hope.config.optimize = True\n", - "hope.config.verbose = True\n", - "hope.config.keeptemp = True\n", - "import numba\n", - "import numpy as np\n", - "import numexpr as ne\n", - "\n", - "from util import perf_comp_data\n", - "from native_util import load\n", - "%load_ext cythonmagic\n", - "%load_ext version_information\n", - "%version_information numpy, Cython, numba, hope, numexpr" - ], - "language": "python", - "metadata": {}, - "outputs": [ - { - "html": [ - "
SoftwareVersion
Python2.7.8 (default, Jul 13 2014, 17:11:32) [GCC 4.2.1 Compatible Apple LLVM 5.1 (clang-503.0.40)]
IPython1.1.0
OSposix [darwin]
numpy1.8.1
Cython0.20.2
numba0.13.3
hope0.3.0
numexpr2.4
Thu Sep 04 15:17:12 2014 CEST
" - ], - "json": [ - "{\"Software versions\": [{\"version\": \"2.7.8 (default, Jul 13 2014, 17:11:32) [GCC 4.2.1 Compatible Apple LLVM 5.1 (clang-503.0.40)]\", \"module\": \"Python\"}, {\"version\": \"1.1.0\", \"module\": \"IPython\"}, {\"version\": \"posix [darwin]\", \"module\": \"OS\"}, {\"version\": \"1.8.1\", \"module\": \"numpy\"}, {\"version\": \"0.20.2\", \"module\": \"Cython\"}, {\"version\": \"0.13.3\", \"module\": \"numba\"}, {\"version\": \"0.3.0\", \"module\": \"hope\"}, {\"version\": \"2.4\", \"module\": \"numexpr\"}]}" - ], - "latex": [ - "\\begin{tabular}{|l|l|}\\hline\n", - "{\\bf Software} & {\\bf Version} \\\\ \\hline\\hline\n", - "Python & 2.7.8 (default, Jul 13 2014, 17:11:32) [GCC 4.2.1 Compatible Apple LLVM 5.1 (clang-503.0.40)] \\\\ \\hline\n", - "IPython & 1.1.0 \\\\ \\hline\n", - "OS & posix [darwin] \\\\ \\hline\n", - "numpy & 1.8.1 \\\\ \\hline\n", - "Cython & 0.20.2 \\\\ \\hline\n", - "numba & 0.13.3 \\\\ \\hline\n", - "hope & 0.3.0 \\\\ \\hline\n", - "numexpr & 2.4 \\\\ \\hline\n", - "\\hline \\multicolumn{2}{|l|}{Thu Sep 04 15:17:12 2014 CEST} \\\\ \\hline\n", - "\\end{tabular}\n" - ], - "metadata": {}, - "output_type": "pyout", - "prompt_number": 1, - "text": [ - "Software versions\n", - "Python 2.7.8 (default, Jul 13 2014, 17:11:32) [GCC 4.2.1 Compatible Apple LLVM 5.1 (clang-503.0.40)]\n", - "IPython 1.1.0\n", - "OS posix [darwin]\n", - "numpy 1.8.1\n", - "Cython 0.20.2\n", - "numba 0.13.3\n", - "hope 0.3.0\n", - "numexpr 2.4\n", - "Thu Sep 04 15:17:12 2014 CEST" - ] - } - ], - "prompt_number": 1 - }, + "cell_type": "code", + "execution_count": 1, + "metadata": {}, + "outputs": [ { - "cell_type": "code", - "collapsed": false, - "input": [ - "def poly(res, arg):\n", - " res[:] = np.sin(arg)**2 + (arg**3 + arg**2 - arg - 1)/(arg**2 + 2*arg + 1) + np.cos(arg)**2\n", - "hope_poly = hope.jit(poly)\n", - "numba_poly = numba.jit(poly, nopython=False)\n", - "\n", - "native_poly_mod = load(\"poly\")\n", - "native_poly = native_poly_mod.run\n" - ], - "language": "python", - "metadata": {}, - "outputs": [ - { - "output_type": "stream", - "stream": "stdout", - "text": [ - "running build_ext\n", - "building 'poly' extension\n", - "C compiler: /usr/bin/clang -fno-strict-aliasing -fno-common -dynamic -pipe -Os -fwrapv -DNDEBUG -g -fwrapv -O3 -Wall -Wstrict-prototypes\n", - "\n", - "compile options: '-I/Users/jakeret/Library/Python/2.7/lib/python/site-packages/numpy/core/include -I/opt/local/Library/Frameworks/Python.framework/Versions/2.7/include/python2.7 -c'\n", - "extra options: '-Wall -Wno-unused-variable -march=native -stdlib=libc++ -std=c++11'\n", - "clang: ././src/poly.cpp\n", - "/usr/bin/clang++ -bundle -undefined dynamic_lookup -L/opt/local/lib -Wl,-headerpad_max_install_names -L/opt/local/lib/db46 ./src/poly.o -o ./poly.so\n", - "\n" + "data": { + "application/json": { + "Software versions": [ + { + "module": "Python", + "version": "2.7.8 (default, Jul 13 2014, 17:11:32) [GCC 4.2.1 Compatible Apple LLVM 5.1 (clang-503.0.40)]" + }, + { + "module": "IPython", + "version": "1.1.0" + }, + { + "module": "OS", + "version": "posix [darwin]" + }, + { + "module": "numpy", + "version": "1.8.1" + }, + { + "module": "Cython", + "version": "0.20.2" + }, + { + "module": "numba", + "version": "0.13.3" + }, + { + "module": "hope", + "version": "0.3.0" + }, + { + "module": "numexpr", + "version": "2.4" + } ] - } - ], - "prompt_number": 2 - }, - { - "cell_type": "code", - "collapsed": false, - "input": [ - "%%cython\n", - "\n", - "cimport cython\n", - "from libc.math cimport sin\n", - "from libc.math cimport cos\n", - "from libc.math cimport pow\n", - "\n", - "@cython.boundscheck(False)\n", - "@cython.wraparound(False)\n", - "def cython_poly(double[:] res, double[:] arg):\n", - " for i in range(len(arg)):\n", - " i_arg = arg[i]\n", - " res[i] = pow(sin(i_arg),2) + (pow(i_arg,3) + pow(i_arg,2) - i_arg - 1)/(pow(i_arg,2) + 2*i_arg + 1) + pow(cos(i_arg),2)\n", - "\n" - ], - "language": "python", - "metadata": {}, - "outputs": [], - "prompt_number": 3 - }, - { - "cell_type": "code", - "collapsed": false, - "input": [ - "%%cython\n", - "cimport cython\n", - "import numpy as np\n", - "cimport numpy as np\n", - "\n", - "@cython.boundscheck(False)\n", - "@cython.wraparound(False)\n", - "def cython_numpy_poly(np.ndarray[np.double_t, ndim=1] res, np.ndarray[np.double_t, ndim=1] arg):\n", - " res[:] = np.sin(arg)**2 + (arg**3 + arg**2 - arg - 1)/(arg**2 + 2*arg + 1) + np.cos(arg)**2\n" - ], - "language": "python", - "metadata": {}, - "outputs": [], - "prompt_number": 4 - }, - { - "cell_type": "code", - "collapsed": false, - "input": [ - "# NumExpr version\n", - "\n", - "import numexpr as ne\n", - "\n", - "def numexpr_poly(res, arg):\n", - " res[:] = ne.evaluate(\"sin(arg)**2 + (arg**3 + arg**2 - arg - 1)/(arg**2 + 2*arg + 1) + cos(arg)**2\")\n" - ], - "language": "python", + }, + "text/html": [ + "
SoftwareVersion
Python2.7.8 (default, Jul 13 2014, 17:11:32) [GCC 4.2.1 Compatible Apple LLVM 5.1 (clang-503.0.40)]
IPython1.1.0
OSposix [darwin]
numpy1.8.1
Cython0.20.2
numba0.13.3
hope0.3.0
numexpr2.4
Thu Sep 04 15:17:12 2014 CEST
" + ], + "text/latex": [ + "\\begin{tabular}{|l|l|}\\hline\n", + "{\\bf Software} & {\\bf Version} \\\\ \\hline\\hline\n", + "Python & 2.7.8 (default, Jul 13 2014, 17:11:32) [GCC 4.2.1 Compatible Apple LLVM 5.1 (clang-503.0.40)] \\\\ \\hline\n", + "IPython & 1.1.0 \\\\ \\hline\n", + "OS & posix [darwin] \\\\ \\hline\n", + "numpy & 1.8.1 \\\\ \\hline\n", + "Cython & 0.20.2 \\\\ \\hline\n", + "numba & 0.13.3 \\\\ \\hline\n", + "hope & 0.3.0 \\\\ \\hline\n", + "numexpr & 2.4 \\\\ \\hline\n", + "\\hline \\multicolumn{2}{|l|}{Thu Sep 04 15:17:12 2014 CEST} \\\\ \\hline\n", + "\\end{tabular}\n" + ], + "text/plain": [ + "Software versions\n", + "Python 2.7.8 (default, Jul 13 2014, 17:11:32) [GCC 4.2.1 Compatible Apple LLVM 5.1 (clang-503.0.40)]\n", + "IPython 1.1.0\n", + "OS posix [darwin]\n", + "numpy 1.8.1\n", + "Cython 0.20.2\n", + "numba 0.13.3\n", + "hope 0.3.0\n", + "numexpr 2.4\n", + "Thu Sep 04 15:17:12 2014 CEST" + ] + }, + "execution_count": 1, "metadata": {}, - "outputs": [], - "prompt_number": 5 - }, + "output_type": "execute_result" + } + ], + "source": [ + "import hope\n", + "hope.config.optimize = True\n", + "hope.config.verbose = True\n", + "hope.config.keeptemp = True\n", + "import numba\n", + "import numpy as np\n", + "import numexpr as ne\n", + "\n", + "from util import perf_comp_data\n", + "from native_util import load\n", + "%load_ext cythonmagic\n", + "%load_ext version_information\n", + "%version_information numpy, Cython, numba, hope, numexpr" + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "metadata": {}, + "outputs": [ { - "cell_type": "code", - "collapsed": false, - "input": [ - "arg = np.random.random(50000)\n", - "res = np.empty_like(arg)\n", - "\n", - "res1 = np.empty_like(arg)\n", - "res2 = np.empty_like(arg)\n", - "res3 = np.empty_like(arg)\n", - "res4 = np.empty_like(arg)\n", - "res5 = np.empty_like(arg)\n", + "name": "stdout", + "output_type": "stream", + "text": [ + "running build_ext\n", + "building 'poly' extension\n", + "C compiler: /usr/bin/clang -fno-strict-aliasing -fno-common -dynamic -pipe -Os -fwrapv -DNDEBUG -g -fwrapv -O3 -Wall -Wstrict-prototypes\n", "\n", - "poly(res1, arg)\n", - "hope_poly(res2, arg)\n", - "numba_poly(res3, arg)\n", - "native_poly(res4, arg)\n", - "numexpr_poly(res5, arg)\n", - "\n", - "assert np.allclose(res1, res2)\n", - "assert np.allclose(res1, res3)\n", - "assert np.allclose(res1, res4)\n", - "assert np.allclose(res1, res5)\n" - ], - "language": "python", - "metadata": {}, - "outputs": [], - "prompt_number": 7 - }, - { - "cell_type": "code", - "collapsed": false, - "input": [ - "print \"python\"\n", - "%timeit poly(res, arg)\n", - "print \"hope\"\n", - "%timeit hope_poly(res, arg)\n", - "print \"numba\"\n", - "%timeit numba_poly(res, arg)\n", - "print \"cython\"\n", - "%timeit cython_poly(res, arg)\n", - "print \"cython numpy\"\n", - "%timeit cython_numpy_poly(res, arg)\n", - "print \"native\"\n", - "%timeit native_poly(res, arg)\n", - "print \"numexpr (1)\"\n", - "ne.set_num_threads(1)\n", - "%timeit numexpr_poly(res, arg)\n", - "print \"numexpr ({0})\".format(ne.detect_number_of_cores())\n", - "ne.set_num_threads(ne.detect_number_of_cores())\n", - "%timeit numexpr_poly(res, arg)\n", + "compile options: '-I/Users/jakeret/Library/Python/2.7/lib/python/site-packages/numpy/core/include -I/opt/local/Library/Frameworks/Python.framework/Versions/2.7/include/python2.7 -c'\n", + "extra options: '-Wall -Wno-unused-variable -march=native -stdlib=libc++ -std=c++11'\n", + "clang: ././src/poly.cpp\n", + "/usr/bin/clang++ -bundle -undefined dynamic_lookup -L/opt/local/lib -Wl,-headerpad_max_install_names -L/opt/local/lib/db46 ./src/poly.o -o ./poly.so\n", "\n" - ], - "language": "python", - "metadata": {}, - "outputs": [ - { - "output_type": "stream", - "stream": "stdout", - "text": [ - "python\n", - "100 loops, best of 3: 2.57 ms per loop" - ] - }, - { - "output_type": "stream", - "stream": "stdout", - "text": [ - "\n", - "hope\n", - "10000 loops, best of 3: 29 \u00b5s per loop" - ] - }, - { - "output_type": "stream", - "stream": "stdout", - "text": [ - "\n", - "numba\n", - "100 loops, best of 3: 2.66 ms per loop" - ] - }, - { - "output_type": "stream", - "stream": "stdout", - "text": [ - "\n", - "cython\n", - "100 loops, best of 3: 9.97 ms per loop" - ] - }, - { - "output_type": "stream", - "stream": "stdout", - "text": [ - "\n", - "cython numpy\n", - "100 loops, best of 3: 2.56 ms per loop" - ] - }, - { - "output_type": "stream", - "stream": "stdout", - "text": [ - "\n", - "native\n", - "100 loops, best of 3: 2.06 ms per loop" - ] - }, - { - "output_type": "stream", - "stream": "stdout", - "text": [ - "\n", - "numexpr (1)\n", - "1000 loops, best of 3: 1.33 ms per loop" - ] - }, - { - "output_type": "stream", - "stream": "stdout", - "text": [ - "\n", - "numexpr (8)\n", - "1000 loops, best of 3: 550 \u00b5s per loop" - ] - }, - { - "output_type": "stream", - "stream": "stdout", - "text": [ - "\n" - ] - } - ], - "prompt_number": 8 - }, + ] + } + ], + "source": [ + "def poly(res, arg):\n", + " res[:] = np.sin(arg)**2 + (arg**3 + arg**2 - arg - 1)/(arg**2 + 2*arg + 1) + np.cos(arg)**2\n", + "hope_poly = hope.jit(poly)\n", + "numba_poly = numba.jit(poly, nopython=False)\n", + "\n", + "native_poly_mod = load(\"poly\")\n", + "native_poly = native_poly_mod.run\n" + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "metadata": {}, + "outputs": [], + "source": [ + "%%cython\n", + "\n", + "cimport cython\n", + "from libc.math cimport sin\n", + "from libc.math cimport cos\n", + "from libc.math cimport pow\n", + "\n", + "@cython.boundscheck(False)\n", + "@cython.wraparound(False)\n", + "def cython_poly(double[:] res, double[:] arg):\n", + " for i in range(len(arg)):\n", + " i_arg = arg[i]\n", + " res[i] = pow(sin(i_arg),2) + (pow(i_arg,3) + pow(i_arg,2) - i_arg - 1)/(pow(i_arg,2) + 2*i_arg + 1) + pow(cos(i_arg),2)\n", + "\n" + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "metadata": {}, + "outputs": [], + "source": [ + "%%cython\n", + "cimport cython\n", + "import numpy as np\n", + "cimport numpy as np\n", + "\n", + "@cython.boundscheck(False)\n", + "@cython.wraparound(False)\n", + "def cython_numpy_poly(np.ndarray[np.double_t, ndim=1] res, np.ndarray[np.double_t, ndim=1] arg):\n", + " res[:] = np.sin(arg)**2 + (arg**3 + arg**2 - arg - 1)/(arg**2 + 2*arg + 1) + np.cos(arg)**2\n" + ] + }, + { + "cell_type": "code", + "execution_count": 5, + "metadata": {}, + "outputs": [], + "source": [ + "# NumExpr version\n", + "\n", + "import numexpr as ne\n", + "\n", + "def numexpr_poly(res, arg):\n", + " res[:] = ne.evaluate(\"sin(arg)**2 + (arg**3 + arg**2 - arg - 1)/(arg**2 + 2*arg + 1) + cos(arg)**2\")\n" + ] + }, + { + "cell_type": "code", + "execution_count": 7, + "metadata": {}, + "outputs": [], + "source": [ + "arg = np.random.random(50000)\n", + "res = np.empty_like(arg)\n", + "\n", + "res1 = np.empty_like(arg)\n", + "res2 = np.empty_like(arg)\n", + "res3 = np.empty_like(arg)\n", + "res4 = np.empty_like(arg)\n", + "res5 = np.empty_like(arg)\n", + "\n", + "poly(res1, arg)\n", + "hope_poly(res2, arg)\n", + "numba_poly(res3, arg)\n", + "native_poly(res4, arg)\n", + "numexpr_poly(res5, arg)\n", + "\n", + "assert np.allclose(res1, res2)\n", + "assert np.allclose(res1, res3)\n", + "assert np.allclose(res1, res4)\n", + "assert np.allclose(res1, res5)\n" + ] + }, + { + "cell_type": "code", + "execution_count": 8, + "metadata": {}, + "outputs": [ { - "cell_type": "code", - "collapsed": false, - "input": [ - "ne.set_num_threads(8)\n", - "perf_comp_data([\"poly\", \n", - " \"hope_poly\", \n", - " \"numba_poly\", \n", - " \"cython_poly\", \n", - " \"cython_numpy_poly\", \n", - " \"native_poly\",\n", - " \"numexpr_poly\"], \n", - " 7*[\"res, arg\"], rep=100)" - ], - "language": "python", - "metadata": {}, - "outputs": [ - { - "output_type": "stream", - "stream": "stdout", - "text": [ - "function: hope_poly , av. time sec: 0.00002789, min. time sec: 0.00002694, relative: 1.0\n", - "function: numexpr_poly , av. time sec: 0.00054300, min. time sec: 0.00048995, relative: 19.5\n", - "function: native_poly , av. time sec: 0.00204802, min. time sec: 0.00201893, relative: 73.4\n", - "function: cython_numpy_poly , av. time sec: 0.00257111, min. time sec: 0.00251794, relative: 92.2\n", - "function: poly , av. time sec: 0.00260139, min. time sec: 0.00251603, relative: 93.3\n", - "function: numba_poly , av. time sec: 0.00272107, min. time sec: 0.00263000, relative: 97.5\n", - "function: cython_poly , av. time sec: 0.00962663, min. time sec: 0.00949407, relative: 345.1\n" - ] - } - ], - "prompt_number": 9 - }, + "name": "stdout", + "output_type": "stream", + "text": [ + "python\n", + "100 loops, best of 3: 2.57 ms per loop\n", + "hope\n", + "10000 loops, best of 3: 29 µs per loop\n", + "numba\n", + "100 loops, best of 3: 2.66 ms per loop\n", + "cython\n", + "100 loops, best of 3: 9.97 ms per loop\n", + "cython numpy\n", + "100 loops, best of 3: 2.56 ms per loop\n", + "native\n", + "100 loops, best of 3: 2.06 ms per loop\n", + "numexpr (1)\n", + "1000 loops, best of 3: 1.33 ms per loop\n", + "numexpr (8)\n", + "1000 loops, best of 3: 550 µs per loop\n" + ] + } + ], + "source": [ + "print \"python\"\n", + "%timeit poly(res, arg)\n", + "print \"hope\"\n", + "%timeit hope_poly(res, arg)\n", + "print \"numba\"\n", + "%timeit numba_poly(res, arg)\n", + "print \"cython\"\n", + "%timeit cython_poly(res, arg)\n", + "print \"cython numpy\"\n", + "%timeit cython_numpy_poly(res, arg)\n", + "print \"native\"\n", + "%timeit native_poly(res, arg)\n", + "print \"numexpr (1)\"\n", + "ne.set_num_threads(1)\n", + "%timeit numexpr_poly(res, arg)\n", + "print \"numexpr ({0})\".format(ne.detect_number_of_cores())\n", + "ne.set_num_threads(ne.detect_number_of_cores())\n", + "%timeit numexpr_poly(res, arg)\n", + "\n" + ] + }, + { + "cell_type": "code", + "execution_count": 9, + "metadata": {}, + "outputs": [ { - "cell_type": "code", - "collapsed": false, - "input": [], - "language": "python", - "metadata": {}, - "outputs": [] + "name": "stdout", + "output_type": "stream", + "text": [ + "function: hope_poly , av. time sec: 0.00002789, min. time sec: 0.00002694, relative: 1.0\n", + "function: numexpr_poly , av. time sec: 0.00054300, min. time sec: 0.00048995, relative: 19.5\n", + "function: native_poly , av. time sec: 0.00204802, min. time sec: 0.00201893, relative: 73.4\n", + "function: cython_numpy_poly , av. time sec: 0.00257111, min. time sec: 0.00251794, relative: 92.2\n", + "function: poly , av. time sec: 0.00260139, min. time sec: 0.00251603, relative: 93.3\n", + "function: numba_poly , av. time sec: 0.00272107, min. time sec: 0.00263000, relative: 97.5\n", + "function: cython_poly , av. time sec: 0.00962663, min. time sec: 0.00949407, relative: 345.1\n" + ] } ], - "metadata": {} + "source": [ + "ne.set_num_threads(8)\n", + "perf_comp_data([\"poly\", \n", + " \"hope_poly\", \n", + " \"numba_poly\", \n", + " \"cython_poly\", \n", + " \"cython_numpy_poly\", \n", + " \"native_poly\",\n", + " \"numexpr_poly\"], \n", + " 7*[\"res, arg\"], rep=100)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [] } - ] -} \ No newline at end of file + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.5.1" + } + }, + "nbformat": 4, + "nbformat_minor": 1 +} diff --git a/benchmarks/star.ipynb b/benchmarks/star.ipynb index 09cf1f5..efa2733 100644 --- a/benchmarks/star.ipynb +++ b/benchmarks/star.ipynb @@ -1,494 +1,464 @@ { - "metadata": { - "name": "", - "signature": "sha256:420bd7e5c9a28bef62dee51f5a5a1770f5cec1ee324c4204a83a2dc17c7c78f0" - }, - "nbformat": 3, - "nbformat_minor": 0, - "worksheets": [ + "cells": [ { - "cells": [ + "cell_type": "code", + "execution_count": 1, + "metadata": {}, + "outputs": [ { - "cell_type": "code", - "collapsed": false, - "input": [ - "import hope\n", - "hope.config.optimize = True\n", - "hope.config.verbose = True\n", - "hope.config.keeptemp = True\n", - "import numba\n", - "import numpy as np\n", - "from util import perf_comp_data\n", - "from native_util import load\n", - "%load_ext cythonmagic\n", - "%load_ext version_information\n", - "%version_information numpy, Cython, numba, hope" - ], - "language": "python", - "metadata": {}, - "outputs": [ - { - "html": [ - "
SoftwareVersion
Python2.7.8 64bit [GCC 4.2.1 Compatible Apple LLVM 5.1 (clang-503.0.40)]
IPython1.1.0
OSDarwin 13.4.0 x86_64 i386 64bit
numpy1.8.1
Cython0.20.2
numbatag: 0.13.3
hope0.3.0
Tue Nov 25 14:36:16 2014 CET
" - ], - "json": [ - "{\"Software versions\": [{\"version\": \"2.7.8 64bit [GCC 4.2.1 Compatible Apple LLVM 5.1 (clang-503.0.40)]\", \"module\": \"Python\"}, {\"version\": \"1.1.0\", \"module\": \"IPython\"}, {\"version\": \"Darwin 13.4.0 x86_64 i386 64bit\", \"module\": \"OS\"}, {\"version\": \"1.8.1\", \"module\": \"numpy\"}, {\"version\": \"0.20.2\", \"module\": \"Cython\"}, {\"version\": \"tag: 0.13.3\", \"module\": \"numba\"}, {\"version\": \"0.3.0\", \"module\": \"hope\"}]}" - ], - "latex": [ - "\\begin{tabular}{|l|l|}\\hline\n", - "{\\bf Software} & {\\bf Version} \\\\ \\hline\\hline\n", - "Python & 2.7.8 64bit [GCC 4.2.1 Compatible Apple LLVM 5.1 (clang-503.0.40)] \\\\ \\hline\n", - "IPython & 1.1.0 \\\\ \\hline\n", - "OS & Darwin 13.4.0 x86\\_64 i386 64bit \\\\ \\hline\n", - "numpy & 1.8.1 \\\\ \\hline\n", - "Cython & 0.20.2 \\\\ \\hline\n", - "numba & tag: 0.13.3 \\\\ \\hline\n", - "hope & 0.3.0 \\\\ \\hline\n", - "\\hline \\multicolumn{2}{|l|}{Tue Nov 25 14:36:16 2014 CET} \\\\ \\hline\n", - "\\end{tabular}\n" - ], - "metadata": {}, - "output_type": "pyout", - "prompt_number": 1, - "text": [ - "Software versions\n", - "Python 2.7.8 64bit [GCC 4.2.1 Compatible Apple LLVM 5.1 (clang-503.0.40)]\n", - "IPython 1.1.0\n", - "OS Darwin 13.4.0 x86_64 i386 64bit\n", - "numpy 1.8.1\n", - "Cython 0.20.2\n", - "numba tag: 0.13.3\n", - "hope 0.3.0\n", - "Tue Nov 25 14:36:16 2014 CET" + "data": { + "application/json": { + "Software versions": [ + { + "module": "Python", + "version": "2.7.8 64bit [GCC 4.2.1 Compatible Apple LLVM 5.1 (clang-503.0.40)]" + }, + { + "module": "IPython", + "version": "1.1.0" + }, + { + "module": "OS", + "version": "Darwin 13.4.0 x86_64 i386 64bit" + }, + { + "module": "numpy", + "version": "1.8.1" + }, + { + "module": "Cython", + "version": "0.20.2" + }, + { + "module": "numba", + "version": "tag: 0.13.3" + }, + { + "module": "hope", + "version": "0.3.0" + } ] - } - ], - "prompt_number": 1 - }, - { - "cell_type": "code", - "collapsed": false, - "input": [ - "b = 3.5\n", - "a = 1. / np.sqrt(2. ** (1. / (b - 1.)) - 1.)\n", - "\n", - "r50 = 2\n", - "\n", - "center = np.array([10.141, 10.414])\n", - "dims = np.array([20, 20])\n", - "# coefficients generated by http://keisan.casio.com/has10/SpecExec.cgi?id=system/2006/1280624821, 7th order\n", - "x1D = np.array([ \\\n", - " 0.5 - 0.9491079123427585245262 / 2 \\\n", - " , 0.5 - 0.7415311855993944398639 / 2 \\\n", - " , 0.5 - 0.4058451513773971669066 / 2 \\\n", - " , 0.5 \\\n", - " , 0.5 + 0.4058451513773971669066 / 2 \\\n", - " , 0.5 + 0.7415311855993944398639 / 2 \\\n", - " , 0.5 + 0.9491079123427585245262 / 2 \\\n", - "], dtype=np.float32)\n", - "w1D = np.array([ \\\n", - " 0.1294849661688696932706 / 2 \\\n", - " , 0.2797053914892766679015 / 2 \\\n", - " , 0.38183005050511894495 / 2 \\\n", - " , 0.4179591836734693877551 / 2 \\\n", - " , 0.38183005050511894495 / 2 \\\n", - " , 0.2797053914892766679015 / 2 \\\n", - " , 0.1294849661688696932706 / 2 \\\n", - "], dtype=np.float32)\n", - "w2D = np.outer(w1D, w1D)\n", - "\n", - "print dims.dtype" - ], - "language": "python", + }, + "text/html": [ + "
SoftwareVersion
Python2.7.8 64bit [GCC 4.2.1 Compatible Apple LLVM 5.1 (clang-503.0.40)]
IPython1.1.0
OSDarwin 13.4.0 x86_64 i386 64bit
numpy1.8.1
Cython0.20.2
numbatag: 0.13.3
hope0.3.0
Tue Nov 25 14:36:16 2014 CET
" + ], + "text/latex": [ + "\\begin{tabular}{|l|l|}\\hline\n", + "{\\bf Software} & {\\bf Version} \\\\ \\hline\\hline\n", + "Python & 2.7.8 64bit [GCC 4.2.1 Compatible Apple LLVM 5.1 (clang-503.0.40)] \\\\ \\hline\n", + "IPython & 1.1.0 \\\\ \\hline\n", + "OS & Darwin 13.4.0 x86\\_64 i386 64bit \\\\ \\hline\n", + "numpy & 1.8.1 \\\\ \\hline\n", + "Cython & 0.20.2 \\\\ \\hline\n", + "numba & tag: 0.13.3 \\\\ \\hline\n", + "hope & 0.3.0 \\\\ \\hline\n", + "\\hline \\multicolumn{2}{|l|}{Tue Nov 25 14:36:16 2014 CET} \\\\ \\hline\n", + "\\end{tabular}\n" + ], + "text/plain": [ + "Software versions\n", + "Python 2.7.8 64bit [GCC 4.2.1 Compatible Apple LLVM 5.1 (clang-503.0.40)]\n", + "IPython 1.1.0\n", + "OS Darwin 13.4.0 x86_64 i386 64bit\n", + "numpy 1.8.1\n", + "Cython 0.20.2\n", + "numba tag: 0.13.3\n", + "hope 0.3.0\n", + "Tue Nov 25 14:36:16 2014 CET" + ] + }, + "execution_count": 1, "metadata": {}, - "outputs": [ - { - "output_type": "stream", - "stream": "stdout", - "text": [ - "int64\n" - ] - } - ], - "prompt_number": 2 - }, + "output_type": "execute_result" + } + ], + "source": [ + "import hope\n", + "hope.config.optimize = True\n", + "hope.config.verbose = True\n", + "hope.config.keeptemp = True\n", + "import numba\n", + "import numpy as np\n", + "from util import perf_comp_data\n", + "from native_util import load\n", + "%load_ext cythonmagic\n", + "%load_ext version_information\n", + "%version_information numpy, Cython, numba, hope" + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "metadata": {}, + "outputs": [ { - "cell_type": "code", - "collapsed": false, - "input": [ - "def pdf(density, dims, center, w2D, r50, b, a):\n", - " for x in range(dims[0]):\n", - " for y in range(dims[1]):\n", - " dr = np.sqrt((x - center[0]) ** 2 + (y - center[1]) ** 2)\n", - " density[x, y] = np.sum(w2D * 2 * (b - 1) / (2 * np.pi * (r50 * a)**2) * (1 + (dr / (r50 * a))**2)**(-b))\n", - " return density" - ], - "language": "python", - "metadata": {}, - "outputs": [], - "prompt_number": 3 - }, + "name": "stdout", + "output_type": "stream", + "text": [ + "int64\n" + ] + } + ], + "source": [ + "b = 3.5\n", + "a = 1. / np.sqrt(2. ** (1. / (b - 1.)) - 1.)\n", + "\n", + "r50 = 2\n", + "\n", + "center = np.array([10.141, 10.414])\n", + "dims = np.array([20, 20])\n", + "# coefficients generated by http://keisan.casio.com/has10/SpecExec.cgi?id=system/2006/1280624821, 7th order\n", + "x1D = np.array([ \\\n", + " 0.5 - 0.9491079123427585245262 / 2 \\\n", + " , 0.5 - 0.7415311855993944398639 / 2 \\\n", + " , 0.5 - 0.4058451513773971669066 / 2 \\\n", + " , 0.5 \\\n", + " , 0.5 + 0.4058451513773971669066 / 2 \\\n", + " , 0.5 + 0.7415311855993944398639 / 2 \\\n", + " , 0.5 + 0.9491079123427585245262 / 2 \\\n", + "], dtype=np.float32)\n", + "w1D = np.array([ \\\n", + " 0.1294849661688696932706 / 2 \\\n", + " , 0.2797053914892766679015 / 2 \\\n", + " , 0.38183005050511894495 / 2 \\\n", + " , 0.4179591836734693877551 / 2 \\\n", + " , 0.38183005050511894495 / 2 \\\n", + " , 0.2797053914892766679015 / 2 \\\n", + " , 0.1294849661688696932706 / 2 \\\n", + "], dtype=np.float32)\n", + "w2D = np.outer(w1D, w1D)\n", + "\n", + "print dims.dtype" + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "metadata": {}, + "outputs": [], + "source": [ + "def pdf(density, dims, center, w2D, r50, b, a):\n", + " for x in range(dims[0]):\n", + " for y in range(dims[1]):\n", + " dr = np.sqrt((x - center[0]) ** 2 + (y - center[1]) ** 2)\n", + " density[x, y] = np.sum(w2D * 2 * (b - 1) / (2 * np.pi * (r50 * a)**2) * (1 + (dr / (r50 * a))**2)**(-b))\n", + " return density" + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "metadata": {}, + "outputs": [], + "source": [ + "%%cython\n", + "#Naive Cython implementation\n", + "cimport cython\n", + "import numpy as np\n", + "cimport numpy as np\n", + "\n", + "@cython.boundscheck(False)\n", + "@cython.wraparound(False)\n", + "def cython_pdf(np.ndarray[np.float32_t, ndim=2] density, \n", + " np.ndarray[np.long_t, ndim=1] dims, \n", + " np.ndarray[np.float_t, ndim=1] center, \n", + " np.ndarray[np.float32_t, ndim=2] w2D, \n", + " float r50, \n", + " float b, \n", + " float a):\n", + " \n", + " cdef double dr = 0.0\n", + "\n", + " for x in range(dims[0]):\n", + " for y in range(dims[1]):\n", + " dr = np.sqrt((x - center[0]) ** 2 + (y - center[1]) ** 2)\n", + " density[x, y] = np.sum(w2D * 2 * (b - 1) / (2 * np.pi * (r50 * a)**2) * (1 + (dr / (r50 * a))**2)**(-b))\n", + " return density\n", + "\n", + "\n" + ] + }, + { + "cell_type": "code", + "execution_count": 5, + "metadata": {}, + "outputs": [], + "source": [ + "%%cython\n", + "#Modestly tunned Cython implementation\n", + "cimport cython\n", + "import numpy as np\n", + "cimport numpy as np\n", + "\n", + "@cython.boundscheck(False)\n", + "@cython.wraparound(False)\n", + "def cython_pdf_opt(np.ndarray[np.float32_t, ndim=2] density, \n", + " np.ndarray[np.int64_t, ndim=1] dims, \n", + " np.ndarray[np.float64_t, ndim=1] center, \n", + " np.ndarray[np.float32_t, ndim=2] w2D, \n", + " float r50, \n", + " float b, \n", + " np.float32_t a):\n", + " \n", + " cdef np.float32_t pi = np.pi\n", + " cdef np.float32_t dr = 0.0\n", + " cdef int x, y\n", + " \n", + " for x in range(dims[0]):\n", + " for y in range(dims[1]):\n", + " dr = ((x - center[0]) ** 2 + (y - center[1]) ** 2)**(0.5)\n", + " density[x, y] = np.sum(2. * (b - 1.) / (2. * pi * (r50 * a)**2) * (1. + (dr / (r50 * a))**2)**(-b) * w2D)\n", + " return density" + ] + }, + { + "cell_type": "code", + "execution_count": 6, + "metadata": {}, + "outputs": [], + "source": [ + "%%cython\n", + "#Aggresively tunned Cython implementation\n", + "cimport cython\n", + "import numpy as np\n", + "cimport numpy as np\n", + "\n", + "@cython.boundscheck(False)\n", + "@cython.wraparound(False)\n", + "@cython.cdivision(True)\n", + "def cython_pdf_unrolled(np.ndarray[np.float32_t, ndim=2] density, \n", + " np.ndarray[np.long_t, ndim=1] dims, \n", + " np.ndarray[np.float_t, ndim=1] center, \n", + " np.ndarray[np.float32_t, ndim=2] w2D, \n", + " float r50, \n", + " float b, \n", + " float a):\n", + "\n", + " cdef float pi = np.pi\n", + " cdef int x,y,k,m\n", + " cdef float s,dr,d\n", + " cdef Py_ssize_t fac_x = 7\n", + " cdef Py_ssize_t fac_y = 7\n", + " cdef np.ndarray[np.float32_t, ndim=2] fac = np.zeros([fac_x, fac_y], dtype=np.float32)\n", + " cdef float w2D_fac = 2 * (b - 1) / (2 * pi * (r50 * a)**2)\n", + "\n", + " for k in range(fac_x):\n", + " for m in range(fac_y):\n", + " fac[k, m] = w2D[k, m] * w2D_fac\n", + "\n", + " for x in range(dims[0]):\n", + " for y in range(dims[1]):\n", + " dr = ((x - center[0]) ** 2 + (y - center[1]) ** 2)**(1./2)\n", + " d = (1 + (dr / (r50 * a))**2)**(-b)\n", + " \n", + " s = 0\n", + " for k in range(fac_x):\n", + " for m in range(fac_y):\n", + " s += fac[k, m] * d\n", + " density[x, y] = s\n", + " return density" + ] + }, + { + "cell_type": "code", + "execution_count": 7, + "metadata": {}, + "outputs": [ { - "cell_type": "code", - "collapsed": false, - "input": [ - "%%cython\n", - "#Naive Cython implementation\n", - "cimport cython\n", - "import numpy as np\n", - "cimport numpy as np\n", - "\n", - "@cython.boundscheck(False)\n", - "@cython.wraparound(False)\n", - "def cython_pdf(np.ndarray[np.float32_t, ndim=2] density, \n", - " np.ndarray[np.long_t, ndim=1] dims, \n", - " np.ndarray[np.float_t, ndim=1] center, \n", - " np.ndarray[np.float32_t, ndim=2] w2D, \n", - " float r50, \n", - " float b, \n", - " float a):\n", - " \n", - " cdef double dr = 0.0\n", - "\n", - " for x in range(dims[0]):\n", - " for y in range(dims[1]):\n", - " dr = np.sqrt((x - center[0]) ** 2 + (y - center[1]) ** 2)\n", - " density[x, y] = np.sum(w2D * 2 * (b - 1) / (2 * np.pi * (r50 * a)**2) * (1 + (dr / (r50 * a))**2)**(-b))\n", - " return density\n", + "name": "stdout", + "output_type": "stream", + "text": [ + "running build_ext\n", + "building 'pdf' extension\n", + "C compiler: /usr/bin/clang -fno-strict-aliasing -fno-common -dynamic -pipe -Os -fwrapv -DNDEBUG -g -fwrapv -O3 -Wall -Wstrict-prototypes\n", "\n", + "compile options: '-I/Users/jakeret/workspace/virtualenvs/hope_benchmarks/lib/python2.7/site-packages/numpy/core/include -I/opt/local/Library/Frameworks/Python.framework/Versions/2.7/include/python2.7 -c'\n", + "extra options: '-Wall -Wno-unused-variable -march=native -stdlib=libc++ -std=c++11'\n", + "clang: ././src/pdf.cpp\n", + "/usr/bin/clang++ -bundle -undefined dynamic_lookup -L/opt/local/lib -Wl,-headerpad_max_install_names -L/opt/local/lib/db48 ./src/pdf.o -o ./pdf.so\n", "\n" - ], - "language": "python", - "metadata": {}, - "outputs": [], - "prompt_number": 4 - }, - { - "cell_type": "code", - "collapsed": false, - "input": [ - "%%cython\n", - "#Modestly tunned Cython implementation\n", - "cimport cython\n", - "import numpy as np\n", - "cimport numpy as np\n", - "\n", - "@cython.boundscheck(False)\n", - "@cython.wraparound(False)\n", - "def cython_pdf_opt(np.ndarray[np.float32_t, ndim=2] density, \n", - " np.ndarray[np.int64_t, ndim=1] dims, \n", - " np.ndarray[np.float64_t, ndim=1] center, \n", - " np.ndarray[np.float32_t, ndim=2] w2D, \n", - " float r50, \n", - " float b, \n", - " np.float32_t a):\n", - " \n", - " cdef np.float32_t pi = np.pi\n", - " cdef np.float32_t dr = 0.0\n", - " cdef int x, y\n", - " \n", - " for x in range(dims[0]):\n", - " for y in range(dims[1]):\n", - " dr = ((x - center[0]) ** 2 + (y - center[1]) ** 2)**(0.5)\n", - " density[x, y] = np.sum(2. * (b - 1.) / (2. * pi * (r50 * a)**2) * (1. + (dr / (r50 * a))**2)**(-b) * w2D)\n", - " return density" - ], - "language": "python", - "metadata": {}, - "outputs": [], - "prompt_number": 5 - }, - { - "cell_type": "code", - "collapsed": false, - "input": [ - "%%cython\n", - "#Aggresively tunned Cython implementation\n", - "cimport cython\n", - "import numpy as np\n", - "cimport numpy as np\n", - "\n", - "@cython.boundscheck(False)\n", - "@cython.wraparound(False)\n", - "@cython.cdivision(True)\n", - "def cython_pdf_unrolled(np.ndarray[np.float32_t, ndim=2] density, \n", - " np.ndarray[np.long_t, ndim=1] dims, \n", - " np.ndarray[np.float_t, ndim=1] center, \n", - " np.ndarray[np.float32_t, ndim=2] w2D, \n", - " float r50, \n", - " float b, \n", - " float a):\n", - "\n", - " cdef float pi = np.pi\n", - " cdef int x,y,k,m\n", - " cdef float s,dr,d\n", - " cdef Py_ssize_t fac_x = 7\n", - " cdef Py_ssize_t fac_y = 7\n", - " cdef np.ndarray[np.float32_t, ndim=2] fac = np.zeros([fac_x, fac_y], dtype=np.float32)\n", - " cdef float w2D_fac = 2 * (b - 1) / (2 * pi * (r50 * a)**2)\n", - "\n", - " for k in range(fac_x):\n", - " for m in range(fac_y):\n", - " fac[k, m] = w2D[k, m] * w2D_fac\n", - "\n", - " for x in range(dims[0]):\n", - " for y in range(dims[1]):\n", - " dr = ((x - center[0]) ** 2 + (y - center[1]) ** 2)**(1./2)\n", - " d = (1 + (dr / (r50 * a))**2)**(-b)\n", - " \n", - " s = 0\n", - " for k in range(fac_x):\n", - " for m in range(fac_y):\n", - " s += fac[k, m] * d\n", - " density[x, y] = s\n", - " return density" - ], - "language": "python", - "metadata": {}, - "outputs": [], - "prompt_number": 6 - }, - { - "cell_type": "code", - "collapsed": false, - "input": [ - "hope.config.keeptemp= True\n", - "hope.config.optimize=True\n", - "hope_pdf = hope.jit(pdf) \n", - "numba_pdf = numba.jit(pdf, nopython=False)\n", - "\n", - "native_pdf_mod = load(\"pdf\")\n", - "native_pdf = native_pdf_mod.run" - ], - "language": "python", - "metadata": {}, - "outputs": [ - { - "output_type": "stream", - "stream": "stdout", - "text": [ - "running build_ext\n", - "building 'pdf' extension\n", - "C compiler: /usr/bin/clang -fno-strict-aliasing -fno-common -dynamic -pipe -Os -fwrapv -DNDEBUG -g -fwrapv -O3 -Wall -Wstrict-prototypes\n", - "\n", - "compile options: '-I/Users/jakeret/workspace/virtualenvs/hope_benchmarks/lib/python2.7/site-packages/numpy/core/include -I/opt/local/Library/Frameworks/Python.framework/Versions/2.7/include/python2.7 -c'\n", - "extra options: '-Wall -Wno-unused-variable -march=native -stdlib=libc++ -std=c++11'\n", - "clang: ././src/pdf.cpp\n", - "/usr/bin/clang++ -bundle -undefined dynamic_lookup -L/opt/local/lib -Wl,-headerpad_max_install_names -L/opt/local/lib/db48 ./src/pdf.o -o ./pdf.so\n", - "\n" - ] - } - ], - "prompt_number": 7 - }, - { - "cell_type": "code", - "collapsed": false, - "input": [ - "density = np.zeros((dims[0], dims[1]), dtype=np.float32)\n", - "pdf(density, dims, center, w2D, r50, b, a)\n", - "\n", - "hdensity = np.zeros((dims[0], dims[1]), dtype=np.float32)\n", - "hope_pdf(hdensity, dims, center, w2D, r50, b, a)\n", - "\n", - "ndensity = np.zeros((dims[0], dims[1]), dtype=np.float32)\n", - "numba_pdf(ndensity, dims, center, w2D, r50, b, a)\n", - "\n", - "cdensity = np.zeros((dims[0], dims[1]), dtype=np.float32)\n", - "cython_pdf(cdensity, dims, center, w2D, r50, b, a)\n", - "\n", - "c2density = np.zeros((dims[0], dims[1]), dtype=np.float32)\n", - "cython_pdf_opt(c2density, dims, center, w2D, r50, b, a)\n", - "\n", - "c3density = np.zeros((dims[0], dims[1]), dtype=np.float32)\n", - "cython_pdf_unrolled(c3density, dims, center, w2D, r50, b, a)\n", - "\n", - "nadensity = np.zeros((dims[0], dims[1]), dtype=np.float32)\n", - "native_pdf(nadensity, dims, center, w2D, r50, b, a)\n", - "\n", - "assert np.allclose(density, hdensity)\n", - "assert np.allclose(density, ndensity)\n", - "assert np.allclose(density, cdensity)\n", - "assert np.allclose(density, c2density)\n", - "assert np.allclose(density, c3density)\n", - "assert np.allclose(density, nadensity)\n" - ], - "language": "python", - "metadata": {}, - "outputs": [], - "prompt_number": 8 - }, - { - "cell_type": "code", - "collapsed": false, - "input": [ - "print \"Python\"\n", - "%timeit pdf(density, dims, center, w2D, r50, b, a)\n", - "print \"hope\"\n", - "%timeit hope_pdf(density, dims, center, w2D, r50, b, a)\n", - "print \"Numba\"\n", - "%timeit numba_pdf(density, dims, center, w2D, r50, b, a)\n", - "print \"Cython\"\n", - "%timeit cython_pdf(density, dims, center, w2D, r50, b, a)\n", - "print \"Cython opt\"\n", - "%timeit cython_pdf_opt(density, dims, center, w2D, r50, b, a)\n", - "print \"Cython unrolled\"\n", - "%timeit cython_pdf_unrolled(density, dims, center, w2D, r50, b, a)\n", - "print \"Native\"\n", - "%timeit native_pdf(density, dims, center, w2D, r50, b, a)\n" - ], - "language": "python", - "metadata": {}, - "outputs": [ - { - "output_type": "stream", - "stream": "stdout", - "text": [ - "Python\n", - "100 loops, best of 3: 10.8 ms per loop" - ] - }, - { - "output_type": "stream", - "stream": "stdout", - "text": [ - "\n", - "hope\n", - "10000 loops, best of 3: 106 \u00b5s per loop" - ] - }, - { - "output_type": "stream", - "stream": "stdout", - "text": [ - "\n", - "Numba\n", - "100 loops, best of 3: 11 ms per loop" - ] - }, - { - "output_type": "stream", - "stream": "stdout", - "text": [ - "\n", - "Cython\n", - "100 loops, best of 3: 7.15 ms per loop" - ] - }, - { - "output_type": "stream", - "stream": "stdout", - "text": [ - "\n", - "Cython opt\n", - "100 loops, best of 3: 2.56 ms per loop" - ] - }, - { - "output_type": "stream", - "stream": "stdout", - "text": [ - "\n", - "Cython unrolled\n", - "10000 loops, best of 3: 35.9 \u00b5s per loop" - ] - }, - { - "output_type": "stream", - "stream": "stdout", - "text": [ - "\n", - "Native\n", - "10000 loops, best of 3: 54.7 \u00b5s per loop" - ] - }, - { - "output_type": "stream", - "stream": "stdout", - "text": [ - "\n" - ] - } - ], - "prompt_number": 9 - }, + ] + } + ], + "source": [ + "hope.config.keeptemp= True\n", + "hope.config.optimize=True\n", + "hope_pdf = hope.jit(pdf) \n", + "numba_pdf = numba.jit(pdf, nopython=False)\n", + "\n", + "native_pdf_mod = load(\"pdf\")\n", + "native_pdf = native_pdf_mod.run" + ] + }, + { + "cell_type": "code", + "execution_count": 8, + "metadata": {}, + "outputs": [], + "source": [ + "density = np.zeros((dims[0], dims[1]), dtype=np.float32)\n", + "pdf(density, dims, center, w2D, r50, b, a)\n", + "\n", + "hdensity = np.zeros((dims[0], dims[1]), dtype=np.float32)\n", + "hope_pdf(hdensity, dims, center, w2D, r50, b, a)\n", + "\n", + "ndensity = np.zeros((dims[0], dims[1]), dtype=np.float32)\n", + "numba_pdf(ndensity, dims, center, w2D, r50, b, a)\n", + "\n", + "cdensity = np.zeros((dims[0], dims[1]), dtype=np.float32)\n", + "cython_pdf(cdensity, dims, center, w2D, r50, b, a)\n", + "\n", + "c2density = np.zeros((dims[0], dims[1]), dtype=np.float32)\n", + "cython_pdf_opt(c2density, dims, center, w2D, r50, b, a)\n", + "\n", + "c3density = np.zeros((dims[0], dims[1]), dtype=np.float32)\n", + "cython_pdf_unrolled(c3density, dims, center, w2D, r50, b, a)\n", + "\n", + "nadensity = np.zeros((dims[0], dims[1]), dtype=np.float32)\n", + "native_pdf(nadensity, dims, center, w2D, r50, b, a)\n", + "\n", + "assert np.allclose(density, hdensity)\n", + "assert np.allclose(density, ndensity)\n", + "assert np.allclose(density, cdensity)\n", + "assert np.allclose(density, c2density)\n", + "assert np.allclose(density, c3density)\n", + "assert np.allclose(density, nadensity)\n" + ] + }, + { + "cell_type": "code", + "execution_count": 9, + "metadata": {}, + "outputs": [ { - "cell_type": "code", - "collapsed": false, - "input": [ - "funcs = [\"pdf\", \n", - " \"numba_pdf\", \n", - " \"hope_pdf\", \n", - " \"cython_pdf\", \n", - " \"cython_pdf_opt\", \n", - " #\"cython_pdf_unrolled\",\n", - " \"native_pdf\", \n", - " ]\n", - "perf_comp_data(funcs, \n", - " len(funcs)*[\"density, dims, center, w2D, r50, b, a\"], rep=100)" - ], - "language": "python", - "metadata": {}, - "outputs": [ - { - "output_type": "stream", - "stream": "stdout", - "text": [ - "function: native_pdf , av. time sec: 0.00005007, min. time sec: 0.00004888, relative: 1.0\n", - "function: hope_pdf , av. time sec: 0.00010014, min. time sec: 0.00009894, relative: 2.0\n", - "function: cython_pdf_opt , av. time sec: 0.00257397, min. time sec: 0.00239992, relative: 51.4\n", - "function: cython_pdf , av. time sec: 0.00727844, min. time sec: 0.00702214, relative: 145.4\n", - "function: pdf , av. time sec: 0.01095247, min. time sec: 0.01038003, relative: 218.8\n", - "function: numba_pdf , av. time sec: 0.01107156, min. time sec: 0.01068902, relative: 221.1\n" - ] - } - ], - "prompt_number": 10 - }, + "name": "stdout", + "output_type": "stream", + "text": [ + "Python\n", + "100 loops, best of 3: 10.8 ms per loop\n", + "hope\n", + "10000 loops, best of 3: 106 µs per loop\n", + "Numba\n", + "100 loops, best of 3: 11 ms per loop\n", + "Cython\n", + "100 loops, best of 3: 7.15 ms per loop\n", + "Cython opt\n", + "100 loops, best of 3: 2.56 ms per loop\n", + "Cython unrolled\n", + "10000 loops, best of 3: 35.9 µs per loop\n", + "Native\n", + "10000 loops, best of 3: 54.7 µs per loop\n" + ] + } + ], + "source": [ + "print \"Python\"\n", + "%timeit pdf(density, dims, center, w2D, r50, b, a)\n", + "print \"hope\"\n", + "%timeit hope_pdf(density, dims, center, w2D, r50, b, a)\n", + "print \"Numba\"\n", + "%timeit numba_pdf(density, dims, center, w2D, r50, b, a)\n", + "print \"Cython\"\n", + "%timeit cython_pdf(density, dims, center, w2D, r50, b, a)\n", + "print \"Cython opt\"\n", + "%timeit cython_pdf_opt(density, dims, center, w2D, r50, b, a)\n", + "print \"Cython unrolled\"\n", + "%timeit cython_pdf_unrolled(density, dims, center, w2D, r50, b, a)\n", + "print \"Native\"\n", + "%timeit native_pdf(density, dims, center, w2D, r50, b, a)\n" + ] + }, + { + "cell_type": "code", + "execution_count": 10, + "metadata": {}, + "outputs": [ { - "cell_type": "code", - "collapsed": false, - "input": [ - "from matplotlib import pyplot as plt\n", - "from mpl_toolkits.mplot3d import Axes3D\n", - "from matplotlib import cm\n", - "from matplotlib.ticker import LinearLocator, FormatStrFormatter\n", - "\n", - "density = np.zeros((dims[0], dims[1]), dtype=np.float32)\n", - "pdf(density, dims, center, w2D, r50, b, a)\n", - "\n", - "fig = plt.figure()\n", - "ax = fig.gca(projection='3d')\n", - "gx, gy = np.mgrid[0:dims[0], 0:dims[1]]\n", - "surf = ax.plot_surface(gx, gy, density, rstride=1, cstride=1, cmap=cm.coolwarm, linewidth=0, antialiased=False)\n", - "fig.colorbar(surf, shrink=0.5, aspect=5)\n", - "plt.show()" - ], - "language": "python", - "metadata": {}, - "outputs": [ - { - "metadata": {}, - "output_type": "display_data", - "png": "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\n", - "text": [ - "" - ] - } - ], - "prompt_number": 11 - }, + "name": "stdout", + "output_type": "stream", + "text": [ + "function: native_pdf , av. time sec: 0.00005007, min. time sec: 0.00004888, relative: 1.0\n", + "function: hope_pdf , av. time sec: 0.00010014, min. time sec: 0.00009894, relative: 2.0\n", + "function: cython_pdf_opt , av. time sec: 0.00257397, min. time sec: 0.00239992, relative: 51.4\n", + "function: cython_pdf , av. time sec: 0.00727844, min. time sec: 0.00702214, relative: 145.4\n", + "function: pdf , av. time sec: 0.01095247, min. time sec: 0.01038003, relative: 218.8\n", + "function: numba_pdf , av. time sec: 0.01107156, min. time sec: 0.01068902, relative: 221.1\n" + ] + } + ], + "source": [ + "funcs = [\"pdf\", \n", + " \"numba_pdf\", \n", + " \"hope_pdf\", \n", + " \"cython_pdf\", \n", + " \"cython_pdf_opt\", \n", + " #\"cython_pdf_unrolled\",\n", + " \"native_pdf\", \n", + " ]\n", + "perf_comp_data(funcs, \n", + " len(funcs)*[\"density, dims, center, w2D, r50, b, a\"], rep=100)" + ] + }, + { + "cell_type": "code", + "execution_count": 11, + "metadata": {}, + "outputs": [ { - "cell_type": "code", - "collapsed": false, - "input": [], - "language": "python", + "data": { + "image/png": "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\n", + "text/plain": [ + "" + ] + }, "metadata": {}, - "outputs": [], - "prompt_number": 9 + "output_type": "display_data" } ], - "metadata": {} + "source": [ + "from matplotlib import pyplot as plt\n", + "from mpl_toolkits.mplot3d import Axes3D\n", + "from matplotlib import cm\n", + "from matplotlib.ticker import LinearLocator, FormatStrFormatter\n", + "\n", + "density = np.zeros((dims[0], dims[1]), dtype=np.float32)\n", + "pdf(density, dims, center, w2D, r50, b, a)\n", + "\n", + "fig = plt.figure()\n", + "ax = fig.gca(projection='3d')\n", + "gx, gy = np.mgrid[0:dims[0], 0:dims[1]]\n", + "surf = ax.plot_surface(gx, gy, density, rstride=1, cstride=1, cmap=cm.coolwarm, linewidth=0, antialiased=False)\n", + "fig.colorbar(surf, shrink=0.5, aspect=5)\n", + "plt.show()" + ] + }, + { + "cell_type": "code", + "execution_count": 9, + "metadata": {}, + "outputs": [], + "source": [] } - ] -} \ No newline at end of file + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.5.1" + } + }, + "nbformat": 4, + "nbformat_minor": 1 +} From 98bd1a0c541b2a44cc7faf252bc2516a949938b1 Mon Sep 17 00:00:00 2001 From: Uwe Date: Mon, 4 Sep 2017 17:42:06 +0200 Subject: [PATCH 13/48] integrated benchmarks to docs --- benchmarks/.gitignore | 2 + benchmarks/HPC Python.html | 13316 ++++++++++++++ benchmarks/HPC Python.ipynb | 42 +- benchmarks/fibonacci.html | 12038 +++++++++++++ ...c3388e38604df736dff187257d5d6ae4c4d3ea.pck | Bin 486 -> 0 bytes ...388e38604df736dff187257d5d6ae4c4d3ea_0.cpp | 157 - ...c3388e38604df736dff187257d5d6ae4c4d3ea_0.o | Bin 334924 -> 0 bytes ...388e38604df736dff187257d5d6ae4c4d3ea_0.out | 8 - ...371d6a5362b67ac5531087a5dd1885eaba960b.pck | Bin 490 -> 0 bytes ...1d6a5362b67ac5531087a5dd1885eaba960b_0.cpp | 150 - ...371d6a5362b67ac5531087a5dd1885eaba960b_0.o | Bin 334120 -> 0 bytes ...1d6a5362b67ac5531087a5dd1885eaba960b_0.out | 8 - ...a460735cef49c69a69c967957f695741c0feae.pck | Bin 498 -> 0 bytes ...60735cef49c69a69c967957f695741c0feae_0.cpp | 152 - ...a460735cef49c69a69c967957f695741c0feae_0.o | Bin 334220 -> 0 bytes ...60735cef49c69a69c967957f695741c0feae_0.out | 8 - ...37089ed771c5f43e2e28f5e25a2c4f0ec12559.pck | Bin 504 -> 0 bytes ...089ed771c5f43e2e28f5e25a2c4f0ec12559_0.cpp | 205 - ...37089ed771c5f43e2e28f5e25a2c4f0ec12559_0.o | Bin 383220 -> 0 bytes ...089ed771c5f43e2e28f5e25a2c4f0ec12559_0.out | 8 - benchmarks/julialang.org.html | 12617 ++++++++++++++ benchmarks/native_cpp_gen.html | 14392 ++++++++++++++++ benchmarks/native_cpp_gen.ipynb | 1850 +- benchmarks/native_cpp_gen.nbconvert.html | 14392 ++++++++++++++++ benchmarks/native_cpp_gen.nbconvert.ipynb | 2434 +++ benchmarks/numexpr.html | 12383 +++++++++++++ benchmarks/numexpr.ipynb | 272 +- benchmarks/pairwise.html | 12201 +++++++++++++ benchmarks/pairwise.ipynb | 186 +- benchmarks/simplify.html | 12656 ++++++++++++++ benchmarks/simplify.ipynb | 716 +- benchmarks/star.html | 12756 ++++++++++++++ benchmarks/star.ipynb | 235 +- docs/Makefile | 7 +- docs/index.rst | 7 +- hope/._transformer.py.swp | Bin 53248 -> 0 bytes hope/.options.py.swp | Bin 20480 -> 0 bytes render_benchmarks_to_html.sh | 23 + tox.ini | 3 +- 39 files changed, 121249 insertions(+), 1975 deletions(-) create mode 100644 benchmarks/HPC Python.html create mode 100644 benchmarks/fibonacci.html delete mode 100644 benchmarks/hope/fib_181039868ed022600dc3388e38604df736dff187257d5d6ae4c4d3ea.pck delete mode 100644 benchmarks/hope/fib_181039868ed022600dc3388e38604df736dff187257d5d6ae4c4d3ea_0.cpp delete mode 100644 benchmarks/hope/fib_181039868ed022600dc3388e38604df736dff187257d5d6ae4c4d3ea_0.o delete mode 100644 benchmarks/hope/fib_181039868ed022600dc3388e38604df736dff187257d5d6ae4c4d3ea_0.out delete mode 100644 benchmarks/hope/pisum_49f15c96f2c4d5ce51371d6a5362b67ac5531087a5dd1885eaba960b.pck delete mode 100644 benchmarks/hope/pisum_49f15c96f2c4d5ce51371d6a5362b67ac5531087a5dd1885eaba960b_0.cpp delete mode 100644 benchmarks/hope/pisum_49f15c96f2c4d5ce51371d6a5362b67ac5531087a5dd1885eaba960b_0.o delete mode 100644 benchmarks/hope/pisum_49f15c96f2c4d5ce51371d6a5362b67ac5531087a5dd1885eaba960b_0.out delete mode 100644 benchmarks/hope/pisum_opt_d21ddf605a07e3a4d8a460735cef49c69a69c967957f695741c0feae.pck delete mode 100644 benchmarks/hope/pisum_opt_d21ddf605a07e3a4d8a460735cef49c69a69c967957f695741c0feae_0.cpp delete mode 100644 benchmarks/hope/pisum_opt_d21ddf605a07e3a4d8a460735cef49c69a69c967957f695741c0feae_0.o delete mode 100644 benchmarks/hope/pisum_opt_d21ddf605a07e3a4d8a460735cef49c69a69c967957f695741c0feae_0.out delete mode 100644 benchmarks/hope/qsort_kernel_674d9af4cba0defec637089ed771c5f43e2e28f5e25a2c4f0ec12559.pck delete mode 100644 benchmarks/hope/qsort_kernel_674d9af4cba0defec637089ed771c5f43e2e28f5e25a2c4f0ec12559_0.cpp delete mode 100644 benchmarks/hope/qsort_kernel_674d9af4cba0defec637089ed771c5f43e2e28f5e25a2c4f0ec12559_0.o delete mode 100644 benchmarks/hope/qsort_kernel_674d9af4cba0defec637089ed771c5f43e2e28f5e25a2c4f0ec12559_0.out create mode 100644 benchmarks/julialang.org.html create mode 100644 benchmarks/native_cpp_gen.html create mode 100644 benchmarks/native_cpp_gen.nbconvert.html create mode 100644 benchmarks/native_cpp_gen.nbconvert.ipynb create mode 100644 benchmarks/numexpr.html create mode 100644 benchmarks/pairwise.html create mode 100644 benchmarks/simplify.html create mode 100644 benchmarks/star.html delete mode 100644 hope/._transformer.py.swp delete mode 100644 hope/.options.py.swp create mode 100755 render_benchmarks_to_html.sh diff --git a/benchmarks/.gitignore b/benchmarks/.gitignore index 88901c5..c408179 100644 --- a/benchmarks/.gitignore +++ b/benchmarks/.gitignore @@ -5,3 +5,5 @@ /star.py /util.py /.ipynb_checkpoints/ +hope/ + diff --git a/benchmarks/HPC Python.html b/benchmarks/HPC Python.html new file mode 100644 index 0000000..5e7baad --- /dev/null +++ b/benchmarks/HPC Python.html @@ -0,0 +1,13316 @@ + + + +HPC Python + + + + + + + + + + + + + + + + + + + + +

+
+ +
+
+
In [1]:
+
+
+
import hope
+hope.config.optimize = True
+hope.config.keeptemp = True
+import numpy as np
+from matplotlib import pyplot as plt
+import warnings
+# warnings.filterwarnings("ignore", category=UserWarning) 
+
+
+%pylab inline
+
+ +
+
+
+ +
+
+ + +
+ +
+ + +
+
Populating the interactive namespace from numpy and matplotlib
+
+
+
+ +
+
+ +
+
+
+
+
+
+

Avoid memory allocations

+
+
+
+
+
+
+
+

zombie apocalypse modeling from http://wiki.scipy.org/Cookbook/Zombie_Apocalypse_ODEINT

+ +
+
+
+
+
+
In [2]:
+
+
+
from scipy.integrate import odeint
+
+P, d, B, G, A = 0, 0.0001, 0.0095, 0.0001, 0.0001
+
+def f_nat(y, t):
+    dy = np.empty(3)
+    dy[0] = P - B*y[0]*y[1] - d*y[0]
+    dy[1] = B*y[0]*y[1] + G*y[2] - A*y[0]*y[1]
+    dy[2] = d*y[0] + A*y[0]*y[1] - G*y[2]
+    return dy
+
+y0, t = np.array([500., .001, 0.]), np.linspace(0, 5., 1000)
+
+ +
+
+
+ +
+
+
+
In [3]:
+
+
+
soln = odeint(f_nat, y0, t)
+plt.figure()
+plt.plot(t, soln[:, 0], label='Living')
+plt.plot(t, soln[:, 1], label='Zombies')
+plt.xlabel('Days from outbreak')
+plt.ylabel('Population')
+plt.legend(loc=0)
+plt.show()
+
+ +
+
+
+ +
+
+ + +
+ +
+ + + + +
+ +
+ +
+ +
+
+ +
+
+
+
In [4]:
+
+
+
@hope.jit
+def f_hope(y, t, P, d, B, G, A):
+    dy = np.empty(3)
+    dy[0] = P - B*y[0]*y[1] - d*y[0]
+    dy[1] = B*y[0]*y[1] + G*y[2] - A*y[0]*y[1]
+    dy[2] = d*y[0] + A*y[0]*y[1] - G*y[2]
+    return dy
+
+@hope.jit
+def f_opt(y, t, dy, P, d, B, G, A):
+    dy[0] = P - B*y[0]*y[1] - d*y[0]
+    dy[1] = B*y[0]*y[1] + G*y[2] - A*y[0]*y[1]
+    dy[2] = d*y[0] + A*y[0]*y[1] - G*y[2]
+    return dy
+
+dy = np.empty(3)
+print("native python")
+%timeit odeint(f_nat, y0, t)
+print("hope")
+%timeit odeint(f_hope, y0, t, args=(P, d, B, G, A))
+print("hope without allocation")
+%timeit odeint(f_opt, y0, t, args=(dy, P, d, B, G, A))
+
+ +
+
+
+ +
+
+ + +
+ +
+ + +
+
native python
+1.62 ms ± 43.1 µs per loop (mean ± std. dev. of 7 runs, 1000 loops each)
+hope
+353 µs ± 41.9 µs per loop (mean ± std. dev. of 7 runs, 1 loop each)
+hope without allocation
+238 µs ± 15.4 µs per loop (mean ± std. dev. of 7 runs, 1 loop each)
+
+
+
+ +
+
+ +
+
+
+
+
+

Approximate expensive functions

+
+
+
+
+
+
+
+

tanh

+
+
+
+
+
+
In [5]:
+
+
+
def tanhpoly(x):
+    a = np.fabs(x)
+    b = 1.26175667589988239 + a * (-0.54699348440059470 + a * 2.66559097474027817)
+    return (b * x) / (b * a + 1)
+
+x = np.linspace(-10, 10, 10000)
+
+plt.subplot(2, 1,1)
+plt.plot(x, tanhpoly(x), label="approx")
+plt.plot(x, np.tanh(x), label="tanh")
+plt.ylabel('Tanh(x)')
+plt.legend()
+
+plt.subplot(2, 1,2)
+plt.semilogy()
+plt.plot(x, np.fabs(tanhpoly(x)- np.tanh(x)))
+plt.ylabel('Absolute Error')
+plt.show()
+
+ +
+
+
+ +
+
+ + +
+ +
+ + + + +
+ +
+ +
+ +
+
+ +
+
+
+
In [6]:
+
+
+
@hope.jit
+def tanh_hope(x, y):
+    y[:] = np.tanh(x)
+
+@hope.jit
+def tanhpoly_hope(x, y):
+    a = np.fabs(x)
+    b = 1.26175667589988239 + a * (-0.54699348440059470 + a * 2.66559097474027817)
+    y[:] = (b * x) / (b * a + 1)
+
+y = np.empty_like(x)
+
+print("numpy tanh")
+%timeit tanh_hope(x, y)
+print("polynomial approximation")
+%timeit tanhpoly_hope(x, y)
+
+ +
+
+
+ +
+
+ + +
+ +
+ + +
+
numpy tanh
+145 µs ± 6.65 µs per loop (mean ± std. dev. of 7 runs, 1 loop each)
+polynomial approximation
+25.8 µs ± 6.41 µs per loop (mean ± std. dev. of 7 runs, 1 loop each)
+
+
+
+ +
+
+ +
+
+
+
+
+

exp

+
+
+
+
+
+
In [7]:
+
+
+
@hope.jit
+def expapprox(x, y):
+    y[:] = hope.exp(x)
+    return y
+
+x = np.linspace(-16, 0, 10000)
+y = np.empty_like(x)
+
+plt.subplot(3, 1, 1)
+plt.plot(x, expapprox(x, y), label="approx")
+plt.plot(x, np.exp(x), label="exp")
+plt.ylabel('Exp(x)')
+plt.legend()
+
+plt.subplot(3, 1, 2)
+plt.semilogy()
+plt.plot(x, np.fabs(expapprox(x, y)- np.exp(x)) + np.finfo(np.float64).resolution)
+plt.ylabel('Absolute Error')
+
+plt.subplot(3, 1, 3)
+plt.semilogy()
+plt.plot(x, np.fabs(expapprox(x, y)- np.exp(x)) / np.exp(x) + np.finfo(np.float64).resolution)
+plt.ylabel('Relative Error')
+plt.show()
+
+ +
+
+
+ +
+
+ + +
+ +
+ + + + +
+ +
+ +
+ +
+
+ +
+
+
+
In [8]:
+
+
+
@hope.jit
+def exp_hope(x, y):
+    y[:] = np.exp(x)
+
+@hope.jit
+def exppow_hope(x, y):
+    y[:] = hope.exp(x)
+    
+y = np.empty_like(x)
+
+print("numpy exp")
+%timeit exp_hope(x, y)
+print("polynomial exp")
+%timeit exppow_hope(x, y)
+
+ +
+
+
+ +
+
+ + +
+ +
+ + +
+
numpy exp
+65.6 µs ± 492 ns per loop (mean ± std. dev. of 7 runs, 1 loop each)
+polynomial exp
+19.5 µs ± 701 ns per loop (mean ± std. dev. of 7 runs, 1 loop each)
+
+
+
+ +
+
+ +
+
+
+
+
+

Avoid floating point powers

+
+
+
+
+
+
In [9]:
+
+
+
def native_pow(x, y):
+    y[:] = 16.11 - 3./(3. * x**2) + 0.5 *(0.4651 * x**-3 + 43.44) * x**-3 + 4. * x**-4
+                                              
+@hope.jit
+def float_pow(x, y):
+    y[:] = 16.11 - 3./(3. * x**2.) + 0.5 *(0.4651 * x**-3. + 43.44) * x**-3. + 4. * x**-4.
+
+@hope.jit
+def int_pow(x, y):
+    y[:] = 16.11 - 3./(3. * x**2) + 0.5 *(0.4651 * x**-3 + 43.44) * x**-3 + 4. * x**-4
+
+x = np.linspace(-10, 10, 10000)
+y = np.empty_like(x)
+
+print("native python")
+%timeit native_pow(x, y)
+print("hope float power")
+%timeit float_pow(x, y)
+print("hope integer power")
+%timeit int_pow(x, y)
+
+ +
+
+
+ +
+
+ + +
+ +
+ + +
+
native python
+984 µs ± 26.3 µs per loop (mean ± std. dev. of 7 runs, 1000 loops each)
+hope float power
+Integer exponent as flaot: x.d[:x@0]**(-4.0)
+Integer exponent as flaot: x.d[:x@0]**(-3.0)
+Integer exponent as flaot: x.d[:x@0]**(-6.0)
+Integer exponent as flaot: x.d[:x@0]**(-2.0)
+1.13 ms ± 111 µs per loop (mean ± std. dev. of 7 runs, 1 loop each)
+hope integer power
+92.6 µs ± 3.71 µs per loop (mean ± std. dev. of 7 runs, 1 loop each)
+
+
+
+ +
+
+ +
+
+
+
+
+

Improve interpolation

+
+
+
+
+
+
In [10]:
+
+
+
X = np.empty(1100)
+X[:1024] = np.linspace(-1., 1., 1024)
+X[1024:] = X[1023] + X[1:77] - X[0]
+SN = np.sin(X)
+CN = np.cos(X)
+
+@hope.jit
+def approx(x, sn, cn, X, SN, CN):
+    for i in range(100):
+        sn[i] = np.interp(x[i], X, SN)
+        cn[i] = np.interp(x[i], X, CN)
+
+@hope.jit
+def approx_opt(x, sn, cn, X, SN, CN):
+    for i in range(100):
+        f = (x[i] - X[0]) / (X[1024] - X[0]) * 1024.
+        g = np.floor(f)
+        j = np.int_(g)
+        a = f - g
+        sn[i] = (1 - a) * SN[j] + a * SN[j + 1]
+        cn[i] = (1 - a) * CN[j] + a * CN[j + 1]
+
+x = 2. * random_sample(100) - 1
+sn = np.empty_like(x)
+cn = np.empty_like(x)
+
+%timeit approx(x, sn, cn, X, SN, CN)
+%timeit approx_opt(x, sn, cn, X, SN, CN)
+
+ +
+
+
+ +
+
+ + +
+ +
+ + +
+
9.44 µs ± 1.01 µs per loop (mean ± std. dev. of 7 runs, 1 loop each)
+1.35 µs ± 595 ns per loop (mean ± std. dev. of 7 runs, 1 loop each)
+
+
+
+ +
+
+ +
+
+
+ + + + + + diff --git a/benchmarks/HPC Python.ipynb b/benchmarks/HPC Python.ipynb index d751889..a7bb615 100644 --- a/benchmarks/HPC Python.ipynb +++ b/benchmarks/HPC Python.ipynb @@ -204,15 +204,18 @@ }, { "cell_type": "code", - "execution_count": 6, + "execution_count": 10, "metadata": {}, "outputs": [ { - "ename": "SyntaxError", - "evalue": "Missing parentheses in call to 'print' (, line 13)", - "output_type": "error", - "traceback": [ - "\u001b[0;36m File \u001b[0;32m\"\"\u001b[0;36m, line \u001b[0;32m13\u001b[0m\n\u001b[0;31m print \"numpy tanh\"\u001b[0m\n\u001b[0m ^\u001b[0m\n\u001b[0;31mSyntaxError\u001b[0m\u001b[0;31m:\u001b[0m Missing parentheses in call to 'print'\n" + "name": "stdout", + "output_type": "stream", + "text": [ + "numpy tanh\n", + "1.32 µs ± 541 ns per loop (mean ± std. dev. of 7 runs, 1 loop each)\n", + "polynomial approximation\n", + "The slowest run took 6.28 times longer than the fastest. This could mean that an intermediate result is being cached.\n", + "1.12 µs ± 1.12 µs per loop (mean ± std. dev. of 7 runs, 1 loop each)\n" ] } ], @@ -229,9 +232,9 @@ "\n", "y = np.empty_like(x)\n", "\n", - "print \"numpy tanh\"\n", + "print(\"numpy tanh\")\n", "%timeit tanh_hope(x, y)\n", - "print \"polynomial approximation\"\n", + "print(\"polynomial approximation\")\n", "%timeit tanhpoly_hope(x, y)" ] }, @@ -310,9 +313,9 @@ " \n", "y = np.empty_like(x)\n", "\n", - "print \"numpy exp\"\n", + "print(\"numpy exp\")\n", "%timeit exp_hope(x, y)\n", - "print \"polynomial exp\"\n", + "print(\"polynomial exp\")\n", "%timeit exppow_hope(x, y)" ] }, @@ -330,7 +333,9 @@ { "cell_type": "code", "execution_count": null, - "metadata": {}, + "metadata": { + "collapsed": true + }, "outputs": [], "source": [ "def native_pow(x, y):\n", @@ -347,11 +352,11 @@ "x = np.linspace(-10, 10, 10000)\n", "y = np.empty_like(x)\n", "\n", - "print \"native python\"\n", + "print(\"native python\")\n", "%timeit native_pow(x, y)\n", - "print \"hope float power\"\n", + "print(\"hope float power\")\n", "%timeit float_pow(x, y)\n", - "print \"hope integer power\"\n", + "print(\"hope integer power\")\n", "%timeit int_pow(x, y)" ] }, @@ -407,15 +412,6 @@ "%timeit approx(x, sn, cn, X, SN, CN)\n", "%timeit approx_opt(x, sn, cn, X, SN, CN)" ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "collapsed": true - }, - "outputs": [], - "source": [] } ], "metadata": { diff --git a/benchmarks/fibonacci.html b/benchmarks/fibonacci.html new file mode 100644 index 0000000..f11ceef --- /dev/null +++ b/benchmarks/fibonacci.html @@ -0,0 +1,12038 @@ + + + +fibonacci + + + + + + + + + + + + + + + + + + + + +
+
+ +
+
+
In [1]:
+
+
+
from util import perf_comp_data
+
+ +
+
+
+ +
+
+
+
In [2]:
+
+
+
def fib(n):
+    if n < 2:
+        return n
+    return fib(n - 1) + fib(n - 2)
+
+assert fib(20) == 6765
+
+ +
+
+
+ +
+
+
+
In [3]:
+
+
+
from hope import jit
+
+hope_fib = jit(fib)
+
+assert hope_fib(20) == 6765
+
+ +
+
+
+ +
+
+
+
In [4]:
+
+
+
from numba import jit
+
+numba_fib = jit(fib)
+assert numba_fib(20) == 6765
+
+ +
+
+
+ +
+
+
+
In [5]:
+
+
+
print("python")
+%timeit fib(20)
+print("hope 1")
+%timeit hope_fib(20)
+print("numba")
+%timeit numba_fib(20)
+
+ +
+
+
+ +
+
+ + +
+ +
+ + +
+
python
+3.55 ms ± 109 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)
+hope 1
+44.5 µs ± 884 ns per loop (mean ± std. dev. of 7 runs, 10000 loops each)
+numba
+3.49 ms ± 58.6 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)
+
+
+
+ +
+
+ +
+
+
+
In [6]:
+
+
+
n=20
+perf_comp_data(["fib", "hope_fib", "numba_fib"], 3*["n"])
+
+ +
+
+
+ +
+
+ + +
+ +
+ + +
+
function: hope_fib            , av. time sec:   0.00003980, min. time sec:   0.00003974, relative:       1.0
+function: numba_fib           , av. time sec:   0.00370073, min. time sec:   0.00327747, relative:      93.0
+function: fib                 , av. time sec:   0.00424831, min. time sec:   0.00329386, relative:     106.7
+
+
+
+ +
+
+ +
+
+
+
In [7]:
+
+
+
numba_fib.inspect_types()
+
+ +
+
+
+ +
+
+ + +
+ +
+ + +
+
fib (int64,)
+--------------------------------------------------------------------------------
+# File: <ipython-input-2-24febe657cfb>
+# --- LINE 1 --- 
+# label 0
+#   del $const0.2
+
+def fib(n):
+
+    # --- LINE 2 --- 
+    #   n = arg(0, name=n)  :: pyobject
+    #   $const0.2 = const(int, 2)  :: pyobject
+    #   $0.3 = n < $const0.2  :: pyobject
+    #   branch $0.3, 12, 16
+    # label 12
+    #   del $0.3
+    #   del n
+
+    if n < 2:
+
+        # --- LINE 3 --- 
+        #   $12.2 = cast(value=n)  :: pyobject
+        #   return $12.2
+        # label 16
+        #   del $0.3
+        #   del $const16.3
+        #   del $16.4
+        #   del $16.1
+        #   del n
+        #   del $const16.8
+        #   del $16.9
+        #   del $16.6
+        #   del $16.5
+        #   del $16.10
+        #   del $16.11
+
+        return n
+
+    # --- LINE 4 --- 
+    #   $16.1 = global(fib: <function fib at 0x10554e598>)  :: pyobject
+    #   $const16.3 = const(int, 1)  :: pyobject
+    #   $16.4 = n - $const16.3  :: pyobject
+    #   $16.5 = call $16.1($16.4, kws=[], args=[Var($16.4, <ipython-input-2-24febe657cfb> (4))], func=$16.1, vararg=None)  :: pyobject
+    #   $16.6 = global(fib: <function fib at 0x10554e598>)  :: pyobject
+    #   $const16.8 = const(int, 2)  :: pyobject
+    #   $16.9 = n - $const16.8  :: pyobject
+    #   $16.10 = call $16.6($16.9, kws=[], args=[Var($16.9, <ipython-input-2-24febe657cfb> (4))], func=$16.6, vararg=None)  :: pyobject
+    #   $16.11 = $16.5 + $16.10  :: pyobject
+    #   $16.12 = cast(value=$16.11)  :: pyobject
+    #   return $16.12
+
+    return fib(n - 1) + fib(n - 2)
+
+
+================================================================================
+
+
+
+ +
+
+ +
+
+
+
In [8]:
+
+
+
 
+
+ +
+
+
+ +
+
+
+ + + + + + diff --git a/benchmarks/hope/fib_181039868ed022600dc3388e38604df736dff187257d5d6ae4c4d3ea.pck b/benchmarks/hope/fib_181039868ed022600dc3388e38604df736dff187257d5d6ae4c4d3ea.pck deleted file mode 100644 index 33f61c4c2f2958dfac7122bd5efb7e486bfcb07a..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 486 zcmYjN*=~YB6t#6N)vnt02TTkEM3Fw&-*8?UGR$0LA`Dy>W15)sQUAIFrpv=5bMA7^ zx#z37$Htg+8;!;mvRot*&q!M#({Kz6&xxk~8I@~bA+QZLkEEw6Nm__ld;+!(q@!?} zK_GV6CM^ZRBn9l$EPhf-j@{EzAR>@72JDe{H5Lfpvs}xx-(1H9Zce9;X>w-Ut_!y7 zm@^(MY=;Md$h4b(I7g7g*18VZl?c0okfIcltuHY#(8%(eeL4KHis zzq9=h9E@v^`ie#oUXh`q(^oRlOIhZ?^)1Z=^&`M*GN@WH%~-gUv=Cds8*)|E@`6Xg zU#iC@6TGd{s)|bmEer3+=#Ml5%EF3JX9)+qho98_BnKQG>g-}FB#uZ^t*LzQp*CGz o6+xck<0;4uKw3bY;?pU8rVap(&wG5SL-CXpuawla_aZ(TL1t6 diff --git a/benchmarks/hope/fib_181039868ed022600dc3388e38604df736dff187257d5d6ae4c4d3ea_0.cpp b/benchmarks/hope/fib_181039868ed022600dc3388e38604df736dff187257d5d6ae4c4d3ea_0.cpp deleted file mode 100644 index 124b93e..0000000 --- a/benchmarks/hope/fib_181039868ed022600dc3388e38604df736dff187257d5d6ae4c4d3ea_0.cpp +++ /dev/null @@ -1,157 +0,0 @@ - -#define PY_ARRAY_UNIQUE_SYMBOL fkt_ARRAY_API -#define NPY_NO_DEPRECATED_API NPY_1_7_API_VERSION -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - - -struct PyObj { - typedef PyObject * ptr_t; - typedef PyArrayObject * arrptr_t; - PyObj(): dec(false), ptr(NULL) {} - PyObj(ptr_t p): dec(false), ptr(p) {} - ~PyObj() { if(dec) Py_DECREF(ptr); } - PyObj & operator=(ptr_t p) { if(dec) Py_DECREF(ptr); ptr = p; dec = false; return *this; } - PyObj & incref(ptr_t p) { if(dec) Py_DECREF(ptr); ptr = p; dec = (p != NULL); return *this; } - operator bool() const { return ptr; } - operator ptr_t() const { return ptr; } - operator arrptr_t() const { return (arrptr_t)ptr; } - bool dec; - ptr_t ptr; -}; - -inline npy_int64 fib_J( - npy_int64 cn -); -inline npy_int64 fib_J( - npy_int64 cn -) { - if ((npy_bool)(cn < (npy_int64)2)) { - return cn; - } - return (npy_int64)((npy_int64)fib_J((npy_int64)((npy_int64)cn - (npy_int64)(npy_int64)1)) + (npy_int64)fib_J((npy_int64)((npy_int64)cn - (npy_int64)(npy_int64)2))); - PyErr_SetString(PyExc_ValueError, "No return type passed!"); - throw std::exception(); -} - -#include -#include -#include -#include -#include -#include - -void sighandler(int sig); - -void sighandler(int sig) { - std::ostringstream buffer; - buffer << "Abort by " << (sig == SIGSEGV ? "segfault" : "bus error") << std::endl; - void * stack[64]; - std::size_t depth = backtrace(stack, 64); - if (!depth) - buffer << " " << std::endl; - else { - char ** symbols = backtrace_symbols(stack, depth); - for (std::size_t i = 1; i < depth; ++i) { - std::string symbol = symbols[i]; - if (symbol.find_first_of(' ', 59) != std::string::npos) { - std::string name = symbol.substr(59, symbol.find_first_of(' ', 59) - 59); - int status; - char * demangled = abi::__cxa_demangle(name.c_str(), NULL, NULL, &status); - if (!status) { - buffer << " " - << symbol.substr(0, 59) - << demangled - << symbol.substr(59 + name.size()) - << std::endl; - free(demangled); - } else - buffer << " " << symbol << std::endl; - } else - buffer << " " << symbol << std::endl; - } - free(symbols); - } - std::cerr << buffer.str(); - std::exit(EXIT_FAILURE); - } - - -extern "C" { - - PyObject * create_signature; - - struct sigaction slot; - - PyObject * set_create_signature(PyObject * self, PyObject * args) { - if (!PyArg_ParseTuple(args, "O", &create_signature)) { - PyErr_SetString(PyExc_ValueError, "Invalid Argument to set_create_signature!"); - return NULL; - } - Py_INCREF(create_signature); - memset(&slot, 0, sizeof(slot)); - slot.sa_handler = &sighandler; - sigaction(SIGSEGV, &slot, NULL); - sigaction(SIGBUS, &slot, NULL); - Py_INCREF(Py_None); - return Py_None; - } - - PyObject * run(PyObject * self, PyObject * args) { - { - PyObject * pn; npy_int64 cn; - if ( - PyTuple_CheckExact(args) and PyTuple_GET_SIZE(args) == 1 - and (pn = PyTuple_GET_ITEM(args, 0)) and PyLong_CheckExact(pn) - ) { - cn = PyLong_AS_LONG(pn); - try { - return Py_BuildValue("l", fib_J( - cn - )); - } catch (...) { - return NULL; - } - } else - PyErr_Clear(); - } - PyObject * signatures = Py_BuildValue("(sO)", "gANdcQBdcQFjaG9wZS5fYXN0ClZhcmlhYmxlCnECKYFxA31xBChYBQAAAGR0eXBlcQVjYnVpbHRp\nbnMKaW50CnEGWAQAAABkaW1zcQdLAFgEAAAAbmFtZXEIWAEAAABucQl1YmFhLg==\n", args); - if (!signatures) { - PyErr_SetString(PyExc_ValueError, "Error building signature string for fib"); - return NULL; - } - return PyObject_Call(create_signature, signatures, NULL); - } - - PyMethodDef fibMethods[] = { - { "set_create_signature", set_create_signature, METH_VARARGS, "signal handler" }, - { "run", run, METH_VARARGS, "module function" }, - { NULL, NULL, 0, NULL } - }; - - - static struct PyModuleDef fibmodule = { - PyModuleDef_HEAD_INIT, - "fib", - NULL, - -1, - fibMethods - }; - - - PyMODINIT_FUNC PyInit_fib_181039868ed022600dc3388e38604df736dff187257d5d6ae4c4d3ea_0(void) { - import_array(); - PyImport_ImportModule("numpy"); - return PyModule_Create(&fibmodule); - } - -} diff --git a/benchmarks/hope/fib_181039868ed022600dc3388e38604df736dff187257d5d6ae4c4d3ea_0.o b/benchmarks/hope/fib_181039868ed022600dc3388e38604df736dff187257d5d6ae4c4d3ea_0.o deleted file mode 100644 index 2be39f820119359e4ae490d669a4403f1823da6b..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 334924 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

OT24|LOV6boZj4)IlAvf87*LcT~|z$ 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

j=ZfAI5%Hs^LO?j2!btxY) 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 diff --git a/benchmarks/hope/pisum_49f15c96f2c4d5ce51371d6a5362b67ac5531087a5dd1885eaba960b_0.cpp b/benchmarks/hope/pisum_49f15c96f2c4d5ce51371d6a5362b67ac5531087a5dd1885eaba960b_0.cpp deleted file mode 100644 index d9cac5c..0000000 --- a/benchmarks/hope/pisum_49f15c96f2c4d5ce51371d6a5362b67ac5531087a5dd1885eaba960b_0.cpp +++ /dev/null @@ -1,150 +0,0 @@ - -#define PY_ARRAY_UNIQUE_SYMBOL fkt_ARRAY_API -#define NPY_NO_DEPRECATED_API NPY_1_7_API_VERSION -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - - -struct PyObj { - typedef PyObject * ptr_t; - typedef PyArrayObject * arrptr_t; - PyObj(): dec(false), ptr(NULL) {} - PyObj(ptr_t p): dec(false), ptr(p) {} - ~PyObj() { if(dec) Py_DECREF(ptr); } - PyObj & operator=(ptr_t p) { if(dec) Py_DECREF(ptr); ptr = p; dec = false; return *this; } - PyObj & incref(ptr_t p) { if(dec) Py_DECREF(ptr); ptr = p; dec = (p != NULL); return *this; } - operator bool() const { return ptr; } - operator ptr_t() const { return ptr; } - operator arrptr_t() const { return (arrptr_t)ptr; } - bool dec; - ptr_t ptr; -}; - -inline npy_double pisum_( - ); -inline npy_double pisum_( - ) { - npy_double csum = npy_double(); - for (npy_intp cj = (npy_int64)1; cj < (npy_int64)501; ++cj) { - csum = (npy_double)0.0; - auto cf = (npy_double)0.0; - for (npy_intp ck = (npy_int64)1; ck < (npy_int64)10001; ++ck) { - auto c__sp0 = (npy_int64)((npy_int64)ck * (npy_int64)ck); - csum += (npy_double)((npy_double)(npy_double)1.0 / (npy_double)c__sp0); - } - } - return csum; - PyErr_SetString(PyExc_ValueError, "No return type passed!"); - throw std::exception(); -} - -#include -#include -#include -#include -#include -#include - -void sighandler(int sig); - -void sighandler(int sig) { - std::ostringstream buffer; - buffer << "Abort by " << (sig == SIGSEGV ? "segfault" : "bus error") << std::endl; - void * stack[64]; - std::size_t depth = backtrace(stack, 64); - if (!depth) - buffer << " " << std::endl; - else { - char ** symbols = backtrace_symbols(stack, depth); - for (std::size_t i = 1; i < depth; ++i) { - std::string symbol = symbols[i]; - if (symbol.find_first_of(' ', 59) != std::string::npos) { - std::string name = symbol.substr(59, symbol.find_first_of(' ', 59) - 59); - int status; - char * demangled = abi::__cxa_demangle(name.c_str(), NULL, NULL, &status); - if (!status) { - buffer << " " - << symbol.substr(0, 59) - << demangled - << symbol.substr(59 + name.size()) - << std::endl; - free(demangled); - } else - buffer << " " << symbol << std::endl; - } else - buffer << " " << symbol << std::endl; - } - free(symbols); - } - std::cerr << buffer.str(); - std::exit(EXIT_FAILURE); - } - - -extern "C" { - - PyObject * create_signature; - - struct sigaction slot; - - PyObject * set_create_signature(PyObject * self, PyObject * args) { - if (!PyArg_ParseTuple(args, "O", &create_signature)) { - PyErr_SetString(PyExc_ValueError, "Invalid Argument to set_create_signature!"); - return NULL; - } - Py_INCREF(create_signature); - memset(&slot, 0, sizeof(slot)); - slot.sa_handler = &sighandler; - sigaction(SIGSEGV, &slot, NULL); - sigaction(SIGBUS, &slot, NULL); - Py_INCREF(Py_None); - return Py_None; - } - - PyObject * run(PyObject * self, PyObject * args) { - { try { - return Py_BuildValue("d", pisum_()); - } catch (...) { - return NULL; - } - } - PyObject * signatures = Py_BuildValue("(sO)", "gANdcQBdcQFhLg==\n", args); - if (!signatures) { - PyErr_SetString(PyExc_ValueError, "Error building signature string for pisum"); - return NULL; - } - return PyObject_Call(create_signature, signatures, NULL); - } - - PyMethodDef pisumMethods[] = { - { "set_create_signature", set_create_signature, METH_VARARGS, "signal handler" }, - { "run", run, METH_VARARGS, "module function" }, - { NULL, NULL, 0, NULL } - }; - - - static struct PyModuleDef pisummodule = { - PyModuleDef_HEAD_INIT, - "pisum", - NULL, - -1, - pisumMethods - }; - - - PyMODINIT_FUNC PyInit_pisum_49f15c96f2c4d5ce51371d6a5362b67ac5531087a5dd1885eaba960b_0(void) { - import_array(); - PyImport_ImportModule("numpy"); - return PyModule_Create(&pisummodule); - } - -} diff --git a/benchmarks/hope/pisum_49f15c96f2c4d5ce51371d6a5362b67ac5531087a5dd1885eaba960b_0.o b/benchmarks/hope/pisum_49f15c96f2c4d5ce51371d6a5362b67ac5531087a5dd1885eaba960b_0.o deleted file mode 100644 index 7166b554cde74eb1727f4777b90afe855f765aa9..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 334120 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 x^G|nl{0E{_CFDAH#%Z`dBXF{{SZ?Qa1nq diff --git a/benchmarks/hope/pisum_49f15c96f2c4d5ce51371d6a5362b67ac5531087a5dd1885eaba960b_0.out b/benchmarks/hope/pisum_49f15c96f2c4d5ce51371d6a5362b67ac5531087a5dd1885eaba960b_0.out deleted file mode 100644 index b3b8cc0..0000000 --- a/benchmarks/hope/pisum_49f15c96f2c4d5ce51371d6a5362b67ac5531087a5dd1885eaba960b_0.out +++ /dev/null @@ -1,8 +0,0 @@ -running build_ext -building 'pisum_49f15c96f2c4d5ce51371d6a5362b67ac5531087a5dd1885eaba960b_0' extension -C compiler: /usr/bin/clang -fno-strict-aliasing -Wsign-compare -Wunreachable-code -fno-common -dynamic -DNDEBUG -g -fwrapv -O3 -Wall -arch i386 -arch x86_64 -g - -compile options: '-I/Users/uweschmitt/Projects/hope/venv3/lib/python3.5/site-packages/numpy/core/include -I/Users/uweschmitt/Projects/hope/venv3/include -I/Library/Frameworks/Python.framework/Versions/3.5/include/python3.5m -c' -extra options: '-Wall -Wno-unused-variable -march=native -stdlib=libc++ -std=c++11 -Wno-unreachable-code' -clang: /var/folders/k8/zfp7dvcs1m326gz1brql1tv80000gn/T/hope1zybbqy0/pisum_49f15c96f2c4d5ce51371d6a5362b67ac5531087a5dd1885eaba960b_0.cpp -/usr/bin/clang++ -bundle -undefined dynamic_lookup -arch i386 -arch x86_64 -g /var/folders/k8/zfp7dvcs1m326gz1brql1tv80000gn/T/hope1zybbqy0/pisum_49f15c96f2c4d5ce51371d6a5362b67ac5531087a5dd1885eaba960b_0.o -o /var/folders/k8/zfp7dvcs1m326gz1brql1tv80000gn/T/hope1zybbqy0/pisum_49f15c96f2c4d5ce51371d6a5362b67ac5531087a5dd1885eaba960b_0.cpython-35m-darwin.so diff --git a/benchmarks/hope/pisum_opt_d21ddf605a07e3a4d8a460735cef49c69a69c967957f695741c0feae.pck b/benchmarks/hope/pisum_opt_d21ddf605a07e3a4d8a460735cef49c69a69c967957f695741c0feae.pck deleted file mode 100644 index af50eca1d4caf3ad09df2626e1f4d70d04ee79ba..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 498 zcmYjO$!@|h5T#{Lp(}L#0VEWZ6iAu_xBdgZRFSbKV3BR&1ffbuJ;Gnt_JQ_b%d@;U z?~Q-z2ds@quU4zAp$yr6AH{Sn@q%r#941*_iY%4Z|C)uDq@(&H5kpE7z&iO8q zgbEPycbD_XbLQ0XVM=HGgU&q1pUxRXvw(R4^#T@nelYhVPhB&YIT26@9c&n8Jvs&{ zv3VkGMZJSUDjC=!O%*v4Z{lLxyjNM3a_pRlr3wX2*T6Qww%9!bSD6H~j>chF0OfQQ zgR+TNCo}8pxc7zqks-C0G>-9_3>2rmOf$PmD+%1*(L!i6-jKd7CA46hMM_Jt1H2{I zIwwmWi`7CMHXh@h(WR3M6}m3oli?q!Qo}Zy&}JD2d;lY83y_zPUmxv_asg7m< diff --git a/benchmarks/hope/pisum_opt_d21ddf605a07e3a4d8a460735cef49c69a69c967957f695741c0feae_0.cpp b/benchmarks/hope/pisum_opt_d21ddf605a07e3a4d8a460735cef49c69a69c967957f695741c0feae_0.cpp deleted file mode 100644 index 3e6984f..0000000 --- a/benchmarks/hope/pisum_opt_d21ddf605a07e3a4d8a460735cef49c69a69c967957f695741c0feae_0.cpp +++ /dev/null @@ -1,152 +0,0 @@ - -#define PY_ARRAY_UNIQUE_SYMBOL fkt_ARRAY_API -#define NPY_NO_DEPRECATED_API NPY_1_7_API_VERSION -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - - -struct PyObj { - typedef PyObject * ptr_t; - typedef PyArrayObject * arrptr_t; - PyObj(): dec(false), ptr(NULL) {} - PyObj(ptr_t p): dec(false), ptr(p) {} - ~PyObj() { if(dec) Py_DECREF(ptr); } - PyObj & operator=(ptr_t p) { if(dec) Py_DECREF(ptr); ptr = p; dec = false; return *this; } - PyObj & incref(ptr_t p) { if(dec) Py_DECREF(ptr); ptr = p; dec = (p != NULL); return *this; } - operator bool() const { return ptr; } - operator ptr_t() const { return ptr; } - operator arrptr_t() const { return (arrptr_t)ptr; } - bool dec; - ptr_t ptr; -}; - -inline npy_double pisum_opt_( - ); -inline npy_double pisum_opt_( - ) { - npy_double csum = npy_double(); - for (npy_intp cj = (npy_int64)1; cj < (npy_int64)501; ++cj) { - csum = (npy_double)0.0; - npy_double cf = npy_double(); - cf = (npy_double)0.0; - for (npy_intp ck = (npy_int64)1; ck < (npy_int64)10001; ++ck) { - cf += (npy_double)1.0; - auto c__sp0 = (npy_double)((npy_double)cf * (npy_double)cf); - csum += (npy_double)((npy_double)(npy_double)1.0 / (npy_double)c__sp0); - } - } - return csum; - PyErr_SetString(PyExc_ValueError, "No return type passed!"); - throw std::exception(); -} - -#include -#include -#include -#include -#include -#include - -void sighandler(int sig); - -void sighandler(int sig) { - std::ostringstream buffer; - buffer << "Abort by " << (sig == SIGSEGV ? "segfault" : "bus error") << std::endl; - void * stack[64]; - std::size_t depth = backtrace(stack, 64); - if (!depth) - buffer << " " << std::endl; - else { - char ** symbols = backtrace_symbols(stack, depth); - for (std::size_t i = 1; i < depth; ++i) { - std::string symbol = symbols[i]; - if (symbol.find_first_of(' ', 59) != std::string::npos) { - std::string name = symbol.substr(59, symbol.find_first_of(' ', 59) - 59); - int status; - char * demangled = abi::__cxa_demangle(name.c_str(), NULL, NULL, &status); - if (!status) { - buffer << " " - << symbol.substr(0, 59) - << demangled - << symbol.substr(59 + name.size()) - << std::endl; - free(demangled); - } else - buffer << " " << symbol << std::endl; - } else - buffer << " " << symbol << std::endl; - } - free(symbols); - } - std::cerr << buffer.str(); - std::exit(EXIT_FAILURE); - } - - -extern "C" { - - PyObject * create_signature; - - struct sigaction slot; - - PyObject * set_create_signature(PyObject * self, PyObject * args) { - if (!PyArg_ParseTuple(args, "O", &create_signature)) { - PyErr_SetString(PyExc_ValueError, "Invalid Argument to set_create_signature!"); - return NULL; - } - Py_INCREF(create_signature); - memset(&slot, 0, sizeof(slot)); - slot.sa_handler = &sighandler; - sigaction(SIGSEGV, &slot, NULL); - sigaction(SIGBUS, &slot, NULL); - Py_INCREF(Py_None); - return Py_None; - } - - PyObject * run(PyObject * self, PyObject * args) { - { try { - return Py_BuildValue("d", pisum_opt_()); - } catch (...) { - return NULL; - } - } - PyObject * signatures = Py_BuildValue("(sO)", "gANdcQBdcQFhLg==\n", args); - if (!signatures) { - PyErr_SetString(PyExc_ValueError, "Error building signature string for pisum_opt"); - return NULL; - } - return PyObject_Call(create_signature, signatures, NULL); - } - - PyMethodDef pisum_optMethods[] = { - { "set_create_signature", set_create_signature, METH_VARARGS, "signal handler" }, - { "run", run, METH_VARARGS, "module function" }, - { NULL, NULL, 0, NULL } - }; - - - static struct PyModuleDef pisum_optmodule = { - PyModuleDef_HEAD_INIT, - "pisum_opt", - NULL, - -1, - pisum_optMethods - }; - - - PyMODINIT_FUNC PyInit_pisum_opt_d21ddf605a07e3a4d8a460735cef49c69a69c967957f695741c0feae_0(void) { - import_array(); - PyImport_ImportModule("numpy"); - return PyModule_Create(&pisum_optmodule); - } - -} diff --git a/benchmarks/hope/pisum_opt_d21ddf605a07e3a4d8a460735cef49c69a69c967957f695741c0feae_0.o b/benchmarks/hope/pisum_opt_d21ddf605a07e3a4d8a460735cef49c69a69c967957f695741c0feae_0.o deleted file mode 100644 index 7c72f9ba7246a4b3ca2e791d4dfd7c0949202cb9..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 334220 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࡚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�*(YM65huD@E z67 z(7p{0PC)PvwbHrN-QjHDDj3rZMf@2+{0UmIVQ->u%!Q}upB?4J#XYgKJeVU`z_W18 z0!IJsZ~>4ysT_naU2vkBSw%nePfJrHrVltS3ygL+bLq5h28@^^pq7sV<#hfTZ@E=e zY3C%13X}*`Bw(XO z?dwiAHaao?NY!Rh`~4_dS`H8S!M(*{B2tLM!#0PCo(}pnB}|xI*Yp`1y_FB%*+Gtr<9G9T! zH*GYh=tf+Irq9`EIojiPY5Kg4=CNwTUDNbiHu`rdxqFmVv9=k4L80H%^gA~CxX|v=R^IR0Xiiy-xTczZ&qhmsjav@-0wn4K8~sxwq?TC>Pw!Fd z;@*W-=okbq+bmC^7;agG9_I$%oHic@+8A-IfzBjzrEO#D7?#7vaP6Eb5LmXF`hU^a%52T>O5lhhZc|)^Sv0L`L*)f!rB}CC0NbqBqZV8_~@e zs`AZ7G%<|x0!}pYHFC869Tc7CYoVtN@cuiJVpuJciW3lPu*?UWeZ`r#_ zReA*|S@xc&#_)%9=2pwz&x4a?@2wu;VjhB%W$&#Y$?eIW*0T3Dk5Fl#WZ8StBh*Sz zvh2O>Bh-0Nvh2P6BgJ?2vZ&-oirAnYxO^#so268}Gh4Ik&{AJY2p%J3EZS( zAMlmWT{?D|uQTq~u@Ct=<1rok2rp$-KZRK2bloXgs8V6b%{1cP2UD~+*jc{aCXBU~ zJq8rt=ZT!{qYJ2n_@8-WBNNW^^+=VF)v$*6-QXY6W_~Q54aq)NHq{Z8L0HWgedBuM z6yKm6IW=xE#^zg)$c47#jH;Azzk;vQ7dDHnOF>iDtR~P5GRu7LM_F~A2=o}4rw!EwQ_DAI< zD{^}R_qH#Aq7$!y?u!E?a)%#9OCzrE0IWlWMDAvtg|<432k25V&)Jz(KtCY!yp2h` z3bepLFnjzmk||tb5x^c&d;KVBXWx$z*Bu6{X|&(wIc_7+t7Hz?nd?A{3<7h|#w11p z^^j2$Y1NfgX?g%&N9u5Pr7_xU2FKws@)!<689A~JLUpJm-GoW0!7zDQ4k(A4mnjZE zGpU!%osufRZX#_@$dTc2%Yza>TiXGaGy}$0ncx*G0dHgaJI^|#lD>dlQ4HFVr{s`w zxOwN+&lWQbu-$0!y7V=Nn@_C*Ec$`nMvFHr3*I01v+z42l6XhtJKCO+j_q*sfrX!~ zk@PFrN|=c$gfg*+r{Ze0$d4EbCxNIt9DG zY5TV2GLOwxTxK)qyBuxMYtzt+_SY#6H9PzRGglP^qHqoj|A?3Jxt>C_L=vJOjUxY5w&@dCipk&& z`DRo->FI zlV$YjkaZw#AJ7~Sqd)VbXlcZ?#N7zFBk?)wBJ?e~FcMd5<_o_>EQ*oz4=5fI z(cg4w3tQ`u;GG$jZPuA8`t_t%<5k~}{?;;VlBLj|~8^Ws1A3cQR);u3^KQnEO zRA1W7%u0>v{9xS9F(Yt?ehvcG9r{V_fZ+ydQB2Z|Fh28=$3VV2^h9{~(a3R}sRQoH z&qZ3fE6)$z$z6E{th@4|IWl2i@?}SC2?U;olzc^BN|cI4Zl=2$e&xrI2LXeka9fFY zJBK1*)NxJ5Exnk@&T55{tqZf4fXqRfm^n`L_AvJP*8s)Lbe4agdY-yVwNRWd#jg>@ zJ4D}*n`PN7@VB8eBjzr6cGQ?f&Q(l`t(5Yirq~sypr=A&mfLtEW*Fe-NUeB;I-;qS zHcGuq9rH8zDpQekl~Z4tmRis3v$>8oLK~orX3R@wLIX6$5=7)tU*mNQ?cmP~_%Yf} z!mk-Kz--4q5#s+3nH>gf#4d$I-EyeQ`moUUhEW4KN8NR_e<}=L`d#5;+h0a}AmiUT zxaX4x?~*XjHyu)Q;)<3pT8R^bk~c;p?Df-31Fes_|t|4$9Tu^H}|N zBd7z|#RUuDPC`ho9f)$*7c7iu?rwY=^B&R^Oy^`Laxd7UG#<|AWJd$Ow_Fh4qvuxY zW%d*|oyn{8>Q&X4V@PueQZP~XvTOsZA+(HluxNg{OG#tAnr~wFbXAwm zh*=4vBXrU-dnYF~-}c`)xd9_yMHlR3ccO-X`!Mw?DC=ZhwAa%{1MKd6?I@PAq{xWr3G<1Nf?fyrcvkL4nQ4G`k{acs${t~SioT!X zIN1S1f%F%n`w39jNl#Vvv}Vj=l2uBh`*+%Inrbv_w(Mt@wFkQAoq@}8NWtmashqg{ zjAeIrucz&1Xs0qu_OtVI+6%^0=yaxbDidZuW7#v^=W4rIswo|AnKt{`+4fNP{YddX zJAgHfj`jvKRJ3p27U0z0wy3J&4^!)J8D z_${bTKnwOWBfDxgU9s-SUK-(h?D&jNWPc3|Qznkcbd3xbBq}mP10w`*M2^$INC843 zCu<->fP~1I8t@8$d=_YAoI*k(muh6YAfb_~HNcBmbMiCgS<8Za_=53c54$L48_t=~ zd;rSjQStzk0sR0pN1V{VsC5T&HtQkWFGgrnQZZ}Q@__D$gz>8nb+_U0T5sZMtI&hZ zx=(eHfz=4j6l9QDx0y!P2r|&D>#>lwF9_1#Om$bLw^Vl(p43v^)vlwwJ>Byi--%w} z_)YXq$3xM(90l>nrl*z1~vSXs?4;?E-|KvCz`e(;k(SJKW z75$Inj_BOZU_6fL>CWTiGoPOBV$Ld}i#r>OuI%)PuHsA=UEeuPbOYxq(XE_&MYndo zDSCkO3(*6e_eGC!hI{FEth0*f+0GWC=Qz8IUhNzydX00o=v~hBqIWw_h(6@}RPS3}zV56o`i8T$=%1XuMgQy^Bl;ib0?`khTSSKiy(l_w&_&TDg1!(P z8T5zfNEl0?4{)JOFGpiI$k2CWu-F6fx(FM>W6{bkTE zqW=|?Yb^cU3o0!-UvRwW{J|d4vB4unmk*vTx<>GN(KUk)if$78rs$@@*F?7q{zY{A zVAnYM?GgNh=+xji(Sw7Vh#nH$S@hW8p`ynHPZvEinAb)<-Lr!CiC!A~y69!WS43|M zzAbutuwy*!_68Rcy)U?;=$C>Uihen`i|9+iUeO-}FA{wtc(>@!gI^c@XYgmD{|bH} zIzwFeuFpgdcij~|(#7kZp6(1+ zG0|RERnendO+}A(brC(*HB9t)*IdyPTpLACbR8Bw$;BsEJ>8kE>!K&S{t`XKRcJEt zsjjM`r@2~+p6==+dWLJ9=$WoXqG!3biJt8`E_#mZ9no`LUx}XQ`b+eDR~}rD^mH$9 zl@z_uRa5jLSEA^}E?xrlbU*8wEP9D+jp(JWfV$oYV%xCTbGO4wn^0X=SA%}Dr(nh zQM)gSdhTOU&wnXu?`={0{u1?q6BD^q_x>TnfNN9u@rv5BZ-T|~Xq zSJcbHp?W>p%GukU;n>=fiF>NENUsTE(0i7sK1)RPTQ92r0Z{|ah#GWN)R5bv((_`b zmFgZ^QPi*|qDJ%i$s0l%srlh(j78W&$ld)8HW?fN} zJBXUnN7U3&qNZ`$l)nbCEXV!>nc}P^^`=VO?EUI8klbmy$midYDjZY>8YYdl?@^{n%`+pb&pZS z9hV{8_}QW+tP?fypr}b7iOReqYI5jYrkhe;)YK-Trl*RUv02p2Q=(?y5H-g+j|t|M z6g97gsQJmF77P)!aIUCD8$>NWChFNsqL%zFYH6|g^s=musO2f5R*V<5a*?Q2n?$YN zFKW#jqSjs!wc$Ha8(jiv$WJ~u^;zAwt^L}ObGV-Dj~ImfGhHlf(F1Si%IH7Q9{ z=1@_S7l@j=U(~d>L`}aXYNl%m)6FU>YIZGAbJ~iUJ5bcTX`<$@6t!T#sD~g`(ctBI?2^Q5U}w^|osT)4fwl z)VqnIE@gh`RQfs9JYL)mBG%osgAuR#)w#dL<-QKTcGG7NX+& zifWiC>glDT8XXYT_>8EgSE17Gp?LM+JZged-C8q!kFk^GrkcW&<%Ze>Coe>ev|&ha zgo#3ebRu5K1}vphsg;4z=OuY`|@j91&_&cfwW>E7eb<* zdc0%bU1F7kvQ*`(5hO2?rG2s7xC45B;H2{Uj}=wf-eos^A7T; zfv7)POu zl>BQ|=KLS=MqdSc4!~J)U?~sajHM!hj|q6zgUCVRTZ=eI;%Ap<4v1fX87U1w6Fp)v zUN9|ykz-5I75`72`Q$B*tON7&LMR{)G$@5f#L~ zjf0SnR(KF5_kx)P8-rhj&Bu@?9c*TO&*M04(t$578G~By#NLJ!8ta;IO1$ZNGRZ5@ z>j1Yt4ZFc7VDl-YNe5OInew<`rj|FpK4rMLqBjSU6d`NXQ5ew!u4^> zJA_}~V?7Et-~zvQ!4-a*w}y~Nm1P`x~S$|*0Q zf1VqE6Hv{`Z{9i=!#J;--#~S9yim_=(z#5~$>gr105kI7xpfH_SFw&q(9zY2hB2HM zK;;oj{^V(nPUw7#KU@-&&c~E_B*drmdHAhN=idw!s*WX1$8dPyFGIP9oCVO}c^FFP zo=9gO@PG5m$!GXO)A{yMdbzszX+(QQ^YFofvmK*+JI2|SA->C!&KDli2QkW!lOZQV z1^BA7d@DYeQGhQqTi=a$@}1H2PwV4nGgC3-e4Zg8q;M=|I-^d0;?a4YEJNx1$wOyO z4r}ROlHf;1(tl)#*f9P$?=#8*IQh{OXDIe~9){9|rExk<(#3>7>Ga}cLpu4K#mQF= z(i<|Fg!toRRM>QBYd%|X^23_x1H_IYMrFVE@ZfEP5>DEr&n7L_OBka6bnex37#r!_ zwZnsoU5Xyj9nt7H_%)+f;n!j04k_L)0>vtqsXqCY~;8C zs$z*2A)i{MD1i_48zEon7(Ub=6!M*p;Y0l)A;0JtT;~q?myW@;JH+UrYE}Xt>Ms}) zqGL&1JLlH1w)`uA!aCLt%Me1M5n~lgphZfBR6vZQO9`|H{*&Z-0`|&1R3;DCeLCfb z4|bhg+vSu{;o)E<#MKqfvJyooU7|G@rExm#(rK4YFX?nTO#CsAVf;y#pZ-iC`Hny+ zA7^21{us`wjC0YVTUIVQmoP5JK7Wjye`jFibL4D13MwS$BB-FCoad=Rat4itay!o; zNuHk?!C9Xq{P6jE`c-0F$}mi#{BhnUCLs=|=?@s?>lec$${$JV3?BhPuvgk4R0`3N z+{MnyBskGZuf-6XG^__#4EiGe;9IZp&dhc_|VVE2b)el)5ycI+R0g|^9-XBdY2(SN^y!Y zLoO~aeQhS5M@6}~Hvb(2OILKUcpouXDdPrJBa;Y*emAv|@ioDxO{O3ytNJs(C)mtp z`7^<$Hp|}$wzOH^BlwKXlK&)8x=nzkRRkBA)&b^LIr5PCC?JiIk)Pm)0p^$t5^Qg? zEJm=M%`%$cAe&_wf&*=q6$y^ES;i3@WwU&W;0rd(It2IGEE5QRVzX>Y@T$$S6+wr? ze{Wgs)sBqm@ZV)td!>-s7~tK#gGX0JcQP9SvapywWYz|z;TUUV3?j28FpXkHkeL>c z#>f~&aB6_Lkujd&ESu#Nf-`NFvk9&YuynuZMLC>of{X>ER|KSSe9A!8}&mjhF) z4!)AiOMwYhFJDKdQ$QLcV-vxS0p>==c7j7~md_DPw^{Bd_^QqF2*FoumM;_RVzYde zU}u}<8w5w%EYA}hVY7UfU=N$+hXlLZEUyvlZL|E6U@x2HcLe*{EO{fbbYGk09|RBC zEbkLMXtOkD0}RWqZ-mgNYJw^>#qIL>BS zjbNtDvNpjKpC9D!4OOr;XbarV5l6f(p4XhFjBXcyMA*`a0CbL!l!-i!(k>C~|sE)?z1m6kpZe+|O z_;!F}9P0#sH+d4yI*+X(_?r(@KDQA3mk(3}!gB=w@`1|o5Wz*Z+>aAnXv_T+!Fe{z zGX&?_EH4mTYO{Qg;1ZkVM+Cn$cVV>4s?jxq-}pdP&aVjmj6VqG^X*&(-Y0n7R?gr#06(*p(@ikK=aacxyR;~oaGzrq!Ri5}$rK4pBX9p@ z#E~f)m_{-6$hZR2sH!$46C9XE)weB~&_~l)r@0i;IUh}E(R^-E`b%3I^db0#tqled z{N83coZxph%P|C>53p4Kk&rQ&%yR+e7Bh#;&cHP4oV%FJj=(gESw-ejKpG=sBf<9r z%#95G3sdQTY?eD|`M1q-AHl{ZcL#f<+AW6(Hn)K<5lpm!rwAqmfR>Lp$$T8Z7#VL9 zOtyiS2oAP^R|t-=fu9lFZv(#~_^A#2o}km=UoER#eDYL!Q-HbEHU1>CE|5_T{|}kz z0gRF1#1pB~vuz+BXq8?S0J@h=;^c-!$#9cC5lE{h4=0lnz!(`t2@bP?Q3Oxfz%m58 z+Q13~Gi+d0f~hvJ7Qr+d7*DXj4QxvAunkNkm^ZuD?nvehn|T+4=WJk4f)i|DKZ28O zU^>Cb>~hN>Q{hp@+Vx{e$2>|K8JT3tX2;ARb1a|)mbdw2w)swOYBw$=_-=r?k+GKG zAHEZjvfN7Wo(Y~ZT|Z~D#;Dx%UusT0{K=6jm z=XHXGe3tZSHNbadp2&{*olK7G7y}PrOXqr&u@1oy(qGw15JvC^8yHS-PXK6*tR=|o z3S`t_T!zdC0gRDRnc%}f(3Ln9Z(D*WQzB^j7P9iaTnKZ%x18Ji)%MlFc|6L`i*T2 zM!C3#;~<04F0SJ^#b8Mn*K)keV2q3FIj%EUO8nktu$=h)7lX0l*EJu(^5VAugB8SY z6oVDTZzTpRiQl>mRu;c47_8z-nudEH2v#oP+Jylu6+uT;xk)<$!78d9CLkCWCDojP zVAUwsNmOV%g2g+yt|Rz7gPmOu5j@CXs`n+TX*|pGgd;uA`zo>N+)G&)pSc&G!K|~e z^TcNB38AE|`7W@}iEr`u=?_)3CYFA`zGwtLP5#A>mpN~6R zl9soO)Ux&3wMp=gjMRUac^fdk%HPoNHX*i2W7I0~=vE{qBeAzVLsRvhXfuP2n2g^s zwL6np$PDCYD%S(?YVtO+RjocWSj-luk=i+fflVZSK)X~sd^)kYx+MGxgj;@m6>`5N zS-*>4^*_Hp8>9AoCSI?rDpgQFlE%A)^c0<+6xO?f7@t#0*{BWgT4KxfE~GZRn~5#p zIwW0Dt8&{(ESY5ybfAVC?{g$pEU^fBaVuB*NvzhzV6|0adykS>tBDi1hvxS*$9Wp@ zoF`!t9OOMi%R}1o2z`?_ycbEVVPmnFHW%Ix$sW|KBrA@qbCtcbB!YS9#(N!h)tl>` zAl<>Y6AbS+WLrFry-l_aeX|6ThuX8hk+2?p`4Lv#ACPUCO)}>~5bYl!tYo>!Hq#B^ z>k1Z|pKL45N=vJ<6d{rP2w^3QCfoWE*6_xX=n~L=e!H(K*`~T(q%8DdIju!DQP)F~ zku|*WB$BjKzd|-6(J>%}Dt#LgodO8usuPKnY+Q9G;R#5gTyg18_0F20wg%q8B%aZg zkuH!`nVKWP&ZI?Kom3TI5wUju1u(oTfVpmwXNM>$bzV>GhXAY`Up02|O+jU(-p1^G1l-NCUY+vCgWu?+kJke<}A0BGUE;Mc>;Mh*;pgv zBB?MR<=bPRJ|Gq0qolOLa~^59=HmAnOrDB#El2Qc1RcHP!+pOa7@Fb8$gv1!J09mC zBQNRQKH8VHTl{AfBE8E;2Ug0miKb2AK%1;0ddrjk(Z9tEZyd3o^qv;4()wy&)*$=4 zPLV~_tOp{IOzTb&r#ND!WmB#BI$1 zFd0X)f3%rzFq31`>XTDwa@)T|?x!#XFq~Y&=;aDH*Zlxi#(E-#cM;hK0qk%|I|{#f z#KB6M{H;$kQnGSfPv?@a;i@v!-bR35>#@qTm+|lX{Tkkb#J&%}${i;rFR!V?x8^AT zmkHisPITz&D@M&vK>SGZuSZCgZ^mzAbNJeuX^o8g1VaKqmD@PSVla74%Ueb$qXlJE z26JYIF~;R1Xz0`#MHn>$Z8Ay_4AnOJP%K4sX8>yT8SSMni2Vy;leH)1NBj)Q|%HL38zXsZkCibsD zER&e;P?5A5vxwR2xQJjbUv-q_N`m=p;6{R3gMhN+dww-a*(~`6VU6;Epo<6ZF1V-} zDUbJGK+rMF)gS1q2-a5Uc?4^fb4>zz4Z%8*a*6N_(Td(bq3S-Z>&dm8hSzu&So1(E zC$M@?24eYX=Q};Cv`-N09B5aHSZW|viCEu2jEhF=4i3cPiH!)vniCrnh_xj)DG=*S z%y-SA%9lpWH{l@W=F(cjJA}j@hV?;ecr%F2r4cdZU?Q=_f!Iu7^~U;RZZ6w3yo*VU zXV~Y|;Ci{@F2?$c)sC zOgClVwGw_xdX%PpF5Ns2VjfA>*PeliEt8*VlA#^QT-9>@Czn7Hlgu6FwS{dF7BnD_# z%D5`A;TrREUz3Da6Vz6|@gx@O>sqPcmw*f>OYUt-_62=So5gxtkvO2QY5j-}B$n#4 zlaH{jak`Q{{JS-$x3g(vm+NarJS9wDq`OUe3LRkKuBfC(~DwwMrG8TJ~>=e!V<@OSZ z>HdV_Jxy%BKPIjW?>Q3BK3XH$-QIV}u6$hiJ|?^B(X`CQ@ZKQ1`f=6ntX z<{hxkrLdT)i%d(YxN3r7%h?;f_|3z`;NILc?x}mSIu!B~Tcit5&hQo{maeg*;^k#} z;Y$GCD6)femNKPb#C=`t#H1`u25S>3mCepug(l;*iOgrEYU0Jq<@*hWYE~RHTcPWw zGNMCoL)y*NcH(8I&5rwgR7=_ov}*26W^A9XpL7Z}8g?bI&Y!@@+neAB-D)hP;T=qD zoWG$pta(ZJZXeL~Sj;$4q=_Kn)iTN%2s*s30xNNI5y1p?3vWHqF*Wu=orIJa#6|rW z2feQ|Hbf6IT-@*G5B{qf#AhUjGtMEiCvm6j>xp?mWN2cX5R7@hW{QDqDyEPw;{B1> zV2$k*<30GzBPs5Z9ZKI!A*}ab#D)c6HJ!__Y!^~uM8FPVM9q99v+J%;<%#y$+jYX_B0%r0ggz&9l`9zPCYs4lNGN%K}(GYBFZF>iu+nn?P z@H(i!+4p0FqP7MNM7R6q1+@UDn=gUxTOB|yESwil5eR_hI@;-G6xvMa@`3ckOF3qO+FSy* zO*AF#qAJ}u_z$$60W|= z58ZH(m}k~PW9>%l6%xOBIHl&XnNr*j51*66GYSNc=aeszOeY9mki*jp1P6-wrbZW- zMldX`ZK1iRpJ9B6*nLQ7{5**lM8&H>LgPJe06qs3&jAE3BaqS_scBFg5;e)`*#M%m zc${nYgWiOAXnf}{V0#R~sHaWO$I!0+FynWJ-Xj$^inhbw5~Ic5>=mL`1U(_t5Y*=9fd{<9NcQ|`A8V+8yM$^RtSjBg7p zl6u}^Q9I5v`N6N24#VgT32pA#1ydfoU7}Xww`<+)_%%Az!iY87Yzk+)Ltxf|ZswTL z%={VlpE+hZhUNaYayc08rqg8$&u7`WoM*lW&$D2$TUnie6$gN>b>GIXX9Liy%zK_$ z4*iuzuhOukyj-NC_^(oVr^pv?>*~Z;i5Nc$!K*}__yQ5bCsZ6R)sDY|xEu8fO`dDk z=?@qM2u=1J0NfwZ&I6IIFtDgvL5s~~L~0=tzAf13%-5Te6w{e+Gz}xs*0tDV){7bW z*#&>9ORzyhlch_XVj6UbH;{#|OI#rs&?S1JP@^E|5(Bb6Lq%k+LOr+PAh0{ADow$z zI0y-CrJ9%F>x{Sdv_Vp7UW3iswXW5+tT-(SW6lDriLmlj{tio~EAK{@zRH(F1dLQZ zfrPGn3L{pV-+|jdAgVFc!NqKzWL9lFTagtHE;*)%M^SK0Ee4^Esd*;%0qvVIO;RWB z1xD*bP#D?O49BZD`U*Q$3&e&)LX$k_ty+7IA|Mris5jHE1xCaA1(o7Y82VaZn5w7R z{fk*Qz5915Mq1z$3Ecu$7_nMl4qPsR_;&~Py3n@L9YT;5yF(}%$=4mC2nKYAbMWvP zB(OUW`9-0gYH$$P9nkl%@*bn;BU9ZWqE*avme+82Q=i#hu=;l#qM;q7Ld_9mg$lsjSD{pb0TrqWghNSSg@|-ksHYwr1XgIC*%f;> zCmOE_8?c#Y89FDYA)aOkj6xvgDL;eo4{~@WgW!C|(;0y_wBW0ZbWs)(-dMfLsN?!o zM!&e#H)&mkDL%~M83I?TZ{qm2#*z3C{C^N6w9!11=KzM^l6VTw13Jov*@XJBVN!T5 zNQccnNMPek2C@Xg^TBzTXAJ;EcUa74U;-Oxt~nX|Fa*0LwDSPYKX5Rx3#`OQ^f4r~ zvu7L<`MS0oZTjF&Kr&y~=F$$`L57jgU3)eoR@dGNm%AalYrlpZH-QiBI)Jm5x#l|j z9s^Kh`~XgN=9>5L`5CeD+QGIL?MrsR^bVJAbIc)(mSS`&Vj~zU{pNNRo6A^P z%`IRoR&$FPtH8@p*<6>9hEvO8*IZ4eVJ?SY(3+)Y6w3ICl#}zy(2@3X3XoM!iOw>cbJk+m%Pi3MVe`IZaTQs##Qu}M-xUGncmznkvW>o< z59$043Ve6+(*#l)N-v-QC&3r~z z$y0#iE7kI8YA0V*qV(`svt;R9y#cI}Jmv?jnodA<@Z>ln`W63P$`}%jgt` z=Pq2u0y8?zgJp7l1R&9k95O4P49C&U9iC>e;x)g_&o%+XNz79npzmV?O_3@Qf)ki0 z8bsE|2HF6nUou0$$j1g!HY2yLKzmqBXE5B2*d*su%(=D0vjey<=POpuO9A?F&V+{< zmY2yOvT~jcbb-ZO1e29B(Zv>X9E_E7%9kj?4d5=ePEALb%=-XRUi3rJdptLgf*q?% z=6m=>izT1|pF)y4OxD#QZW;}2A+QxHy5anBmKlVe8e(-LS0GJlw!7GO%CKAu_H#F%D|lE!@%$1ncS zkblsT{6GKBE&JECE{H&JK@*p5Czlz-{Hrz3uLH+pB$CGIvd9G zm>|?4y@KI|jOTK6N<-3T5niU^9a3=A_dgJd1xh4Y@L!BaI6UQZ;=OBN^1&7ow;|!Xf;}xjsFCa{XrjMc zOn)%kaY*zt6g}ldThZGrP~3Z-4JcL+noiwyxx=xvu)~uIl+)nOyzY0SI=DvhWCG;P zSR*AL5>$q#st__I!EjGoBXT(;K(#zOkcn?ZPM{Z@3(f%ajmRb8pbVX0#Qz#Z)`(2B zyu~zx`>YX}nO3(LCm1~&QtKS7;MSZP){*{I|zb{UKUj)1UTI)TV)moY#mTFh23 zS?w|z=roI23`VyL&*QU!FM~vv#kDOs8I!Pg(JCP!fMXpVS8jYk9`O{8r<-8A6%v_h zdal9#Ct!-%1M~$l-A&IgngO}SBZc<;tTYus$&r#b%>PIk#=?DMdiI0%6>hdw zxYq$@70wG!W0(;NHy4cGk+O+iJ~TZI0Q-8xF?vCdXbaHSBL>1jI-Q_Lq=Crl5k!Yu z%z7|cJ%X8zx0umjbdTVXk_mh^gt?bQ&9V-Vg+RGaqOX{q2S{ZdAiN>?9*cXy^z888 zBe$$dAJ=jD43|Y0wAXL>j?aNElVwQr@6=0Vx z3Ooi9ebMxMgkth!%2`rU6Li6)N37G%1_Urm;+HX|o0D*Eso5UDK~0}0Ef3r9ClpIz zq&}T7-F#o0e)f3NDW?3f^h0eCdQ|edFa)i8)X}0DVov~)Nr*g!rh9jLV)@A1>$5h0%mfljkre@Du3Z;I9KFjou6n5VwN^j4}-<5l`_lx z#atN<6Mp4PJX!hnH3zo@%nx@7tj@+fKqEI*#R1w^u0P~owv35gLe3-S@JJ-!2g@XZ z-+4b#&iiU+m>7H(2Qia`ZL*T^D`@{klFYx%gDfdO`z7Y8&h6nQ-(My_@7B?P>wNbl zY)o8|!?77(eky?^vvd+wD0wnnIry`57Gz0|XSCVA$D2(zm%z$d1I{jMD|ybFoO282 zc>6<&fkHnDLu^BJSl!cB^0-%7_0IVsC|HfsR#A8$C3Sh!YT}s?$5srR)!-}%X7b6t z>qzL=RPA99-Z7Z#800f%IxH3JXjSk`NH6VY)&B=SnHxjSvMkrMj~8$Y&|a28IkL*4 z!X3L|A``wjghO{np7p}Fg^WNLca{1wwnrHD)ag~=z3TA*r|fUx$MqBD)tqj&53s<@ z_*a(dX|4ogwcYCZ_?rwQ z*EG|EuqmH6j5r%y9R|wyZ15r)O)(p=Sl?@Rs*6cYuen%LSmdNd?w1~|+K0MG$65Kx z*;riA75Tj`OCc8kqa8h23b_Cn?T~+!Ut=Z|!J7D+S{Qc|k0X!6gOvZP%wuziClSW)0rNZo+iM7?& z41pVVo#p162&o=(12y#Z7`oj?*XS{uVZt6G9YquDD2G9?qkv-n2r7!^gs^{vpcYyE zgJ=OVR{vnQ7~|+4Pr%aGKYWDlAG9h(OZ1P~D4?%@XoCI23&Phwd<6POFA)CygPzLL z8~Vo+*t60de8hnM!KT^p zBJRAQDcKXEPnjvB{o>)PbI7s}&4;hg;Tey8t(JXg)PLE>2J=471^5$q)08ucEZs6T zm^F7iif%M>JY@}}@f_5QjppDQ7SPxN`n1FF$jLy&R;T<6+9q>ZeH`{?B!1OAWP#j7 z0rUiJRHS5{ZtP33NTt|p-bSgCnp;`QjLO(-7Tbxb2U=gsFEbN>)c{7; zv_Lt}z~krDEkMrs0GjCHtA%45&F|V<7A>s$48++%9IIO;8_f>(CU{Bv&`mI+d_a<6 z{HWs=b9C-JCH?V^Yj$Cmj?FFiWsq?#v<1(i5%+={aCWrO98#Mn#Obc;IQ=MUy33+& z>6*2-iurmS*jUY8TP07@C7078{8iQ^*R@}&ORj2P0BauA#|{$=`J$eDdQ!elDc`C~ zzt$NiW`3R0z=0|Yg(l$6#8k+ykbF6C6BSY)k_;IP$%M><+=B4A@I}bikRY6LLLqv~ zu>8^PA4m{vazn0L_W!FL7c8xYybWKUK=k%FyasHiW@CQ?*e{R+PP|$U(Hqyvl!TOn zG`Hb}5U&8K1gQe40nr=N`SThlrv3S+{a<;Vr%uPRvFw%52HZD#dmPSHG0U^D=aQIj zAon4ELmon0!P=WzGULx{#vfxEz5P3!Pp@?z+MWwiCP9`!_?&VDWIaT0c3$K5H2)3G zRVq3UZO_Fp1<=PwqYc@o_4YXYDC{3V?Dkv*ruQ@V3l}KWhhM#av^|fR&3^glH0*<1 zZ=yGi-voXi@;BrmL~s8se-~-Es6}t|N6hYz`P=hy`IX1cYkw>=>-se6#IZqdkHaHj z+b0`)9*-^k+DrTMZ?_lx{9k~5`hT!L;b(sw_8imn{>aY1zdddB7|4|+dSiG4WIyB} z_n;|K{QvXN1o1eYD zlg<0qygRM8$KlMY7lb_TSnG{p-jUu3;l--n_*}*xXFYf)T5r6=Tot145c}ihfmgA3 zjq|oI@3r!dD(|1_%^&9tUA_6+@?NXn9)~|b+4K!hm!JLPaBZ6xw!9Upx5wemLbzap zT!DNJ(OYhWld{1t!RIkQ{3nF}1<~6yOn2XfgyS0E35ec!vHvGz11={@K7nhpV(L=z zzvVylOaBvm{0?~neu`p3-w~1l;iakGz6Jj{yJfK30NLbc&nrmY1^FAY3~6JnGHQSS ziRaa#fBLpazXEc^=BEjK)4xA|8EtVL!Zz{W_}{mBbsbl}aUA2KrnT4yry#tuqqpYm z8*+iV(Hrt6L=SnyOhXOuI7J$IQ7xtb22U@HW5^VHxHdS%@o?ZSMKKNBz7-o>AsD40 zWg+Dt6(N-%n93VfAx}YSLh3;3L2!j6=^8*cvcgTEn?jmHT0&Yu+CW$aT-_KQAe|s7 zkgkv(kW@%7NE(E8{h<3p210m2P7F8D4bJ^XLPkMGL&icTKqf*mAyXhTATuHJAoC%M zAxj|3AoQ~wdNpJXWF2HZWD8^)WGCb~2<`S*dcUO)Lmz`2hrDcIv^#|CL(t53>R zdGUa5->sPN@Y#Dm%>Q+D#kc=lT`}yz>WZ~zteVj5U+X6f{BCiX*K)NSd1vYPjPQ5r z4*qKR_haT1fBW|;yH~e5RzC9Pj+SRmuGzCc?+-PPxZ90PuUz}*&-K5pd8)eO z`G#A*89Z&!$-T1%ot!zN&6>e-~7H(t|ql==6~f*p=!M|D%9RKrb6wRKff3J?b9b( zel>IEiv1-{)qQ7S(5_%~z%e-zlb?L|?2BdYd^&K}glhvAM@PHkpDa0Vboq&uSN>DA zPT@k0>+F6pU$b+S))%|AKBoNCS>G2}venUd>Y_`@-M+igs78a*Z{@zV^8;_++CLmf zJYIKzF>q72r{8#c)X}buemnM!x$OCqtuK%Aob9k^(An?2(e7ueb}IkfsG8|ji+y(Q z(vIPIyEQ#oYr(J2e|g$*-?{ROh~_nhBz;hH#$RI!bm;tPtDXD5efY50tzAQ&`1`}{ z%kvDLd~N8dXUfm}wC&AhOM5g_^*VDxAr!_TYWXxLUVTHHPJmC3x>5huGa$Sg@Ip|{MXXE!)uCdtDZcLYz_eRvd z^3RCcu6L)zm&-pgs8Vd?Z~0gJmH(a7qig3qJGyq#Tcc~|=(x6dyJO$X&uqB6_?A(N za_#8+d9JT9DE$WEb#_5WDM)ol24olH1mp^YPZ1wL3SwNW1xbXYK{6ooA)6qFA?F~! zL4q*IMM2^qiIA?4!H{K;6OfCLYmgry4C!j*cQ^JYM%LzY2!_w*d( z3giwX2zL+*LXsi1LvBI1KqWWE3eIsW zLXse9kO`3akZq8|kjs!;5YCz0IB%AM)PiunHXpJM!t?1h2>+_SF;0HTkTggJWIp5^ zU$aKgu$TrAf$QuyeQhxy9t@0>H93&Z%2FZX-hb)8af*gZf zgnSO+i8l%o2WbpRhGamFBY&RH_ax&7TT7f289%j0qDIScD&FTM73W*Jz2J8Uj4GZ4`Th4qs zqx_d4eNl%=sKa8ErL9#5{uva@~Ctv`OeDkf;;4gA=UwU8mx0`YXFL z1F#$$ZCGrO!lPCxpY4H_*s3tT^2>aB1FLLd?U9byw|nu1g|*K{Mxm{NF)eMGp7uY3 ze+Vg$GS;!msoq(|J4f4*0NZvRThO;r7Z=(g589!O)fVSdRoO>a?btL;+1^3BxX^!U zLYhIiwX*6t(TZ~&Hv3V5-9j6mu2FNxHE6qsklmOZ72Jh+4&+lvUCWNIz+xyj_)xeo z%6J5JmB7CR`3sV4*;VSM{4ciJ_5GYGJ`>{_x9=g$KLNsRc)E&jM!Rw2AJCCMh5gHo z?@q8?ub~|tSZ&p@wd(7e(5An^egyjL&`t1#eOqA*`Y_6v4{b3IQXGDsLO*GSep1rv zCnYe6WFEg-c|6@p<#7mkd|>5~XzQ!*PgXW3(9bVgeVxk>=;v!|zr1giH4^(P-oh%P zY_w@+rK#B*OIr{>jf^v4) zgw?l@PAF#&l=DfH^JA1R4YnrA_6LL;ueY4gspxBmH(@NdjuEcIpzq68+Z>sw;`c3n z;$Ri;^&E__U0IE>19}YdnFBcw`Iceyz15I2kS`(kAq&w5H$e75xP3o9(AVn3|2%$V zqOR;~tI-dRTm684SH&{lw~n8)<(17w)cd^EMyE&O^O>+2kNPe|JFQ1O)6qVeR=rw3 zraidgg!$z~e$QC>4IZxY)5i~AJfIDaZ64R$_}&6B9_#uz=Qku+&e7I>r$l?z=q(DIZh8@3Hn<-u}ww2>KegGVsNGdZB%}e3hF6bt1;GU61Shdra1!WnF{% z??%1GZNZ{QtNvk|lwa3C7-8dDB9^6`6<-ZapAFHb*H6#_Zy^s>HhSDzXYJ2XFDRZX zY?y{)DaTQ6T$@9TW2+ulU2PQhI@jn6F z1-=%+*DlM~Ui7hCu>D{97=0HWpz`ydgWRy{{X95stDv3G343vFfviE7t%A5y%o?ApqmA8N2A z92;L-<4kMxU$#>;`g_-t2w;1%n@X$am9y%r`Dzb#u{<skD?HEJ%xxgY0sod0od$2lP9 zcbo@uj>owm=XsnDa<0d@Bj<&B&gVg$d3;<#os&>s*FMx8vIhHi$PrbRljz&r$NqD3 zd2tokpH};yLjLxC(4xO8vpy&Bg;&;5r}+uzp8s@g@-JKI*MD9%@G*Vy8tmx5EQH(G zqH6!xuSd=!J?->Z$-jr@@$tBG;C&p2ECY{2eN2tEj+yD$_xr3q*#iB6c2m$_Hd|x( zRm;x*I_Y6+W#@lBNCTD!Y5%YD;&p?RPyaaq?_sc>dfdD*O|^Ft#;R^wG%UuQf!RKM62c{Jzk`kuk${Az#l`cYpy)~+AeE+1fD(Wm`ewcwo2A<7-7X<8KHPR|2&_m;`;tZJscnZ&AfsC%gp}Y{$iu2{&)uErcXVuNn+ z`WDmZYt;ov!?N?bT&Eiczr=X0!fO<6wJ`4T7~pjcuW5Ka!|NDczi{IRtH|@3hu1s2 z*5NgdzP9-X`Svk_ZYSN`oMSm;nML z5+#8II?%z$7!mArlFlHR>;b|Y2vZxPQNfE7prWD|ub{|91PwAPGC83l;uRGH6eq-q zeE(hbukKF7=ebw!`>t=T@2i!QU!8sS*=MX%)is>6h?M<0?B4@-AKVhSrEoaBRlNs5 zX+Inq>hm_~e$uTa-qYYSaDRt;7EUgi3DkgnJoI4*OT(w!%rgZQu^Lop8J0 z_Q1Uk_XgZvxczW%!5xM>0{1bT#5)e2fIAI$2JRcUZ{dd5U%%(z_=c8mJ!iOaNOu27 z^VFgzyXJ0qmGe!bMb&lYtGI1a(M5JTb91@-9LL>I?$VN*}(SyYCG`jhZ~-K&$)C*VeNwMdrnPH za9?-tv}<}CeEH4y9&oSvYRLOnJr|lXVpw{L<<=X1s>*o%?32&k-~5JN;g@_pW#7GB zKfQD9)VK4VTs^(Ty31~ucGCcB(fnVZyRXCC)0N5AMKNFgxP8l-e(j&xcg34;g)Y4L z@)q}fd*F(pKVNm#_>_)azZo0iy8mcH&*$yA2^)^xGwjnt=WBbukoWFYXGe|iU3rV) z*F*Jb=RWC>^W4C5{bxNq(lWt@eHP+~Gaf+m1YyG;v$u zGtXU>vv$)XuIF|?^vNBO4;MZ%_09W|pPw6_Rg;o*bIoU;kF3f6ZsYDJr{02joZo)< zOsCVWvQ{i?`0mHSLmaIR?_IKNV)w%@WNx^<^XXPAj*X0-cw$s^$o*rY6Q{NB{q85T zrdh5yeYf$wu%*{lO{zcf zQqkliTOLd7@%_i%o=-eKZ^o#z6{p?LG^}#pUiivKS&Ld2UweP?&l8{RasIvWt1jJg zq{WsQeShgOJ^9fCS7nYXdGmbjm9yUM)@;l28GXNM`{Lq9rrz<cgc+ z;@O(2P#(O^Ghn{WPn!YJK7<>KgbKc9F5?8gT#Y4X?uX`(F z-!yX_rpC6C(jA@!^x-F@{zE7#Cru?3|uPQXt+YSX>jta@F}=WaBspL zgZmCnKAXK9t{2=uxRG#9xJtMhxaDx`;I_lP2lpl1Z*Xl}V%&$5=ZE9qZikx(_cyp_ z;I_cYGsI8f&cTJ_w?hZGShzuOW8jM5ro-I@_bA+YxLt4`z@34Uo6N1@u7OK}lV^z2 z;qHQa6mC7-F1Qci&cMk@tu@>=a7l3T9MKLZlitjSTL$-cxL4o~!<~Y=02dvNYh$?n zaKqpx!pWR5)o}8G;?r=O;r7G*3r=PjG2^=Q3OM;JBo%HnTp`>vIGJ$0x8c5k`x&lf8-u|D*9UG0+%0e=aI@hafLjCi zD%>Hs&*0?wV+)+nQ{jr?uqc`i8)?smADaCLBx!)<`u1NRNw<+!lu1ve0GB%Bkj z60QbrIovupc{ceT+?Q~_!O8Qo4elAZEpP|mK7|{Ai!Qll`!P(ruEMStO(~+RM08IO+q<|V5z+T9zt+1r z=tZ;)9iLju?$JatdpD|Hwvt(zXu?RpRvc}e9PbzHLdDC)-%ec3A!5Y<6?-*^nJ00y zNMbU@v0Qay8$*#570bl7m^H2UaQsrV7{!u^y)bl8B%^?cBGLOo#gf5bS*(dJul8%n za(kk61{YI5e4^DVAi5qAM6_j$zd#$|7ts%> z37Y>3SHU9Mg8_`9EAur`$W8u;4 z%w9ntd$K5?n3Or$XfkuQCb}We1$wc*<-Iy;y~Or@Fwow6S%{|tT2-vcZh@MVZ!siA zS%k3*h)q6M5YhGMX*9o2?>m7^9AiJ+kA8@5B%4e(ArUuS?=NFt9Ht_w3-l>jzDY!~ z;1bQt@`WP0H&E>7nBpzB`fW{QiiJp#5#?5bjAU=(-TsI#@{pH>NSNZo%yDR-haEtPCE}hN z{I*`_fjKyEV9I5Ks)a1m)(B)-L|>vpD3TMsh(5il`A8=#@Zy$vT`O+B?^;CF^mD=)HjGC`JDYh<;?BUKr@p^0lo*ye-fVcd?9e z%4a4@Src-nn4;-XC@xYk$j8sn8mS%8Q?!g9(K^Shk$wv@dyORBCeTNR zAg>}iaEreu$anXHMPzr)`-j65{oK;D_>`S)pBvP=O*!(m8l zt%qYVjEM9wFR2!sLL->D0+Ro-@IILN<^7|kP%n# z0Fa5CDH=Xb)9Qbtzb1MhmPC96PEQdv9Rlw!392ZkpFhO=kftn?T2i?$J49-rL&&nI zVp0~Aq?U&rRVJ*aNY-u;Em9%=py?21S}aipjtI*y2bzQQj`W;c_!?U;!> z+G<)Ufx|zxt0tOd@oPQEwi|k@zwL%l6dDl8;RkKgO4g;r?8`aAvn z@jId}rNWG;Kvapm_s2MQPu48UYCc_{flXG{lB^d68c`+#5YcR8omz|7xmM2gry}bK zi`L%*qE!@a2#89M7twMAGI97eO(e^EFkfjn042u}=$#OakMgGyibFu+9|EcTJdf6Y zLYXa|Fjdp)k>#&3na5R)7&t=Y<2p4j5&no_Rhp>zD1XF>NJg~2z|qPSb674k2Qy9_VGsp~nu=Uko6|W9ek(i;viTXoW?pPI2HuOX- zAd3c>nhqh4rmI+|_1Rl*_UmoL$wA7~7pDWHEn8Pb6MY`YgPlcOi6B^$O=dBdh(DP8 zrOsskOAQ=4<+C-deQAF4vJj0#l;ve;>lb)Mg!<4$Cuv@;!l|{CW5E*^zi1;moJ0&M z^Jk(Z>-it3XGVO49j{31rO-FzGhZ^*Ftx1g2D1D%MRp#&?sC5Hpy=a(XdKLnty8EGYUOb3l$F3K3g`9P zVq8?PkLRL)Nk6QOQ?>5IIRVOpwTq8$QA$Sr2leF3cZj-c3NoV$d@>6R^+sSkl8pQv zMo2|V7gir-LF+Q1cn={G^1@{l)1@3`${_T=(rLI+k>nUnZAdG zRWda#dA7lb&3N>@KGH8r&eycA(ncb&^%8zjh}q8CAiA)hKbM!b(cYG5kA8xTXK9$N z;aLsc2dH5mY6b3z#G&~L5(H$<0pM2+l9o)CD@BP4RO}|CEus_zL0Pstr)oL+aiA)) z1@d|wawN*JNSuYd41N(Ugk$`|_L}H4@`FvChC@bttx6MJJIXIA zgaOf#AA7VVH#L$F7V|Q}1VsaR;+J`eLnT+ZHwhb}wIPr%SwBoft^ekaC~Ja> zNR|qvtuxGoyr@7?Gn_a@>+ur5Xuv8>bQy*&Y7NQNL?Jliu|TgOmPC}P)LHlWD2Rw= z2kL$SE9Yp6KjN$^P3y+Mu`qjqChCh6*&I(S)7ZF&Qqu=pftiHdG##cA-H^W7MJSC&(KZX@G;xs6!Io}x#zrt`1 z(nQK4(~~n~L1!mqT@4|AnS8mkNlQ(QD__GLgX{`4LU3I9I&w}EdpW2847ZK+jzQ*Y zB9lANFFU%=$9RXY&Lws>E@udvYObFxw3Gz)TkUXr|_&MdNL(#j%H6S z+hUYWzDRzfiQY=IB2=-3vg;YtuU7Jd;>sO;`7$LkNS1&)Nx?zmMXwCbGp^0l%{VQ~ zHqv06VQ%)&Ef*RZqM{;9p^FgTf>7zu$D3bhkai1wukD3~)K=zsA%j|mW-Kr*46T+M z7V2i4rdlxSamBVcbNdSo(!K^g-ViOGgeAirZhfIa+S(tUz94iVMlK0o3Ad=t1$i;o z5W1~hAZ_?1d71 zg?l94l@}V4gTn^}>TL)-=8aqVou^Lb&v~(EEbgG$;>;5w`iN3k}i-o7%vk zquMCxc%eaU|G}-IERbcP9V#J{{ur9~d%L3JfzEczg$C)Lp|Ad4N9t!Zbk=sd&>;P_ zY1)tD_z5xmCgZE|msTzDUp+2i%@{kz?R2?gy;Gd-yrR-#k0&e)LMUG-*RMks!7C5{mo|wu!j~=2{Om1;mY)p*5AbHnb8#_7v z|6oGWgn1>7vclN@F@0j<^n@GN6x+jKx0fQ%#SWLtG1cyqpR87?&4`q zy9YiDJQY(b%3ZRLvw3;0bRTWs?a41L$1ZxFKXMq(T~2hR{9wg=bU#_Pp}4 zg5pB?%&fTFEkNn=odvLqVcs5G9d>(3d7h)hsVTXN%hXt<&eC$%RFO%qw|iWUVvlO4 zxU3j;U4r^_*-MJu9wt#RC13VRy&h+!?AVLTJbilWIV`J~iX4@emX{#|XdGk)O{~?m zr?}Lq?YTWJr=v92TObC?HJ|Ld1G|or!g5!!r>IoqII>+1Pq|A?&#o2FUh1f{BYTBq zYT77lsl!vG*_S*e>RA#`e7|5{zXZR}>(|5u2x{K1s(V>9-OH%(N+&9l4kf(^dlep6 zBdfuF1WRs3TQ?Ns_joI`M)8)Rn)Cftga)Gd$quyz;6ty@lXhaj6j=7Bs*YNEKpq;h z%Cpq4!J*B!WxvU6F?GuiwU~^` zT{8+oqfI7do?`ZxB3?GN^oox1mBC|brR|n{vgz`WF0Iu>pAXv&?Pz6JnE5!=Z5~y34RlRC zm9b*M7K7mXX2q!LOJ+*Njr_yH4$});z``OU-_1gX|F5K>Jk(lB8?CHyhx9W3zBt(z zDGqWrP}w~LC6d;@V4SOG)ne)y<}qCsrr3tD0-BrRl{H?icK4um)pTMmlFnbRHZ;`b z%4?PHnkSl~%>zv>51V=^xfT6sq<8jRA*= z96ll4jfi})<42AwlM;OV$Pqke)^Wt!iK8s^a#JVe>DdL!v`t}BFWthFrKY*6^whM1 zMPWFWnwtz^%I)S&rdG|(txe5!LEZUei;d!x3qisacrS6f^uk_bFjRO^KsTAzBL_Fw8ZDC@qkVNtv8I z&ty>A9}hK}E^V$XZ=Q_N=s8o15hlfaLs$%aVS7yGETGQRHY^sL8)oh#0d2z+w>et` z*nM7Qdt`o%DKhL^lM<$c7chEQ0s3*4sdZS4=}J{`lc`PEJ*L=sre+&WZ50m=eX*)I zWe91heC*w1GMRrse%dInV$A6Vy(*Jgi4wh+O>OQGZP5%G0w z4+=AT=1Ne(MI)&}%P$^F!jx&p5qLaojhM*B?z>@Y%rWQ{2zo_95ASG7ZmwLA!@U!_ zf3=c~_J|A{j#2OGWC(7H*@B`^F||a6Uwtc%sLPbS7*9Ll%<#`Ca)b>~9t~HmC|ljx zkYBAlpvVZ^Sy>q_{&vb|lj}^EU2BTSF-6^@8rWu*Q%Xl=3k^IJo(%PAqJ5=uYO=KB zFjIIovY~{dXGSSg3rsENq0PF6-3tTh%KX{8O(RfQh+qs;7T`>RoK}8oihmi!X{o$D zE9M3Sg)3`}1*+i4EE&Yol`eI9G?c#9c-$1x+&mm1gOu+voXZHZ*ocrCoMt zXH$mqkWtI^=d+TP!erAWudDix&y>-wvvM5z(S>4M`PGb*Z)asUj@TYy*(P-2{WFtI z0|r8`w{oJ=TxaSWYw8}R4!>c_y6_koYYWuV=rH7D!Z1W`K1^vN=Zmdsc3LYZE0axK zl@@hssh*e}bAvKPa??%u4DZXdMLv6=FC?2f;N;%fG&D>yi`IHDQu@rb%Kmcn*`B7B zv1kj4$D|FyHxw``^;HMMKfle%3G;X|Jpg$yWV))eIYzeMSt9O2ym-7^$9|?J=2}e};>WWK)Zk8Ud|@>mIcctHgVU4MSxs95$$0}o>&`SBFS+*&B zoT)>KDLfXXk5KBS3`0OKX{e8wwtqLS6t*zLlQ zekuC((=CTRt7ndc+`ym#N;+G*k01TsVro ztCbfc<|3J?$#TUNrl1$LQ9f7RMc>-#My)H)Mugmgu0d1T0aLpr!vAOr=S!IKsHUVk z4kD#!y$O>Yb2age_V`TZRO@@L_H(h~4L z(ZpU|gyFHQ7V1w!HN8d5hX;DR4dV0G__?9OACz|+I;IU3O|76ja69&fKQ72_G~)F}JQL*42gUmdeL&jv zo92d94+qH)L7ofwn_v!D5B32!La#6Nu7wPbzE*9#N^5`dhr>z!ehW9OYC%1Dz4Zt9 zQt<4@l8cfbaL zFM+AxGvFxjE)Z`sw44eifesM&@LLWw8&-Y9dKG&EAeVIGK}q*&P||G$z2J1skW0Eh zH`CHRO&$a#z1Qe}fqa5|guI)a4vPK|GM@YbH#!R-{}7aNy#-2s@!sYtYf$;?AV*eO zR)U4#80rrtlPJHI{wv8yQ0l1}DD`w6x1%HWJy;AWk0H7M~`f)ei@P~y2ji8mgU zc!NOEk03t^!OL>+KLJWUsz5x0YH24^!Rz6_7LigDEY`jd|OaEq(UzFlGjlsUo8>-R#3lc2LJ0p{vVO1=)H%@sOp2{ z2O*dC-UFgbx2!^Vc2GK%klzG3-o#inAxMrg%NMOJEs$pg$@4HC*&&y4MOXv&2gi^f zA`$8DZ-J8TtE8J82I5tOmi6dl9YI-gPxRML`+36n* zj)gxCM7_6^<3_^wgHo^Cng!~0AD;Vef?Vo#CO8<(0<*#4proUKcpljDPW0mx@ODu0 zKDE(i$FPkb3iFKp5giH`59#FdWOd{yi-uPtlsh)+W9ub z`vH`3;tV(x{1EM|j}u=-VMPweC7m}xNoNDX_4btT74YlrDE?CDiCqUMc5ea2?$98+ zWzduUCU%oRvD-b!u7tM|>`6HKzQ5e!N80k7Clq}~p7Dgr^H3r50o191pNECzANL@WY_gY&=)unI)>BW8feV?-s0ZW~bnqHZEez{|lRQ1X)xP5^Vj zi6Hh>y(};te&j)BijF|qsyqSg0Dl~KDcFnbPI(vbO2{oBhLH&9%F9a;$cs!&8W91u z2F;XrL}nD!r_4|;Y(vVIaN;+T@^zf}&ylj!miRvFCptlA32|#Ov>_IqMt{~LSo|2B4zzB@!vqovSH%yNnS&CBxPwR36CJnq>+?G zpCnvnc^7_3eoD$pS|UF{$_i8B-$KfarQ&~{ly#=Wzk-zIro=BZtO{k}E8$d9*1Hma z0hvpVB5xwCqDPK>D{5tX)vNPG5j3muuC@HhTN__buQ}_*ehWwnACEz6d zEm9Vb6MsFqnS7C4M?OP7Nv$gX53vORej8A;05#S%{;<%JvZ%a_N(?@3w3Tl^=ti_9QpZ9b99SMS1jvL|^B*^!hb2}Li0G?V9X(iHjk z%IpH7e-+uDY)wXzG8=*Dg_6JFT3Gx)k>8SEk@7W~gnvrP zjOpS(Odcd<^-GbPKJ{v@;6+FiJhOwZ^$#`N96nD9&!iyBDs!ylzfD&C4J;n z(o5!(@-DFC<0f)ADYF}jJdu=@p~T;nyo|hrY(X|7&*S1(^uH%RBR?Szk^9J3$(Ko) zSwrHlC1th^@jpV|MczrutQ-%i`@&WQ*Qf4NRbY$jl;at*1mXY%5x`gMEx02(?EYe2G zDhi^XLMD-Y$!o~YWNR{#lpjYDUp^QRo*_??ACR)JyoB!}w~^~fS?NK-A0;0l?;`Ic z<;w=qn?lM*W8%*z$B;LZgUB1nSn^u31KExYCr#uz{6G@>a^GI~DfuyZfZR*U{dv)Q ziF}59lKdNaA1NO_}cgHYdZ# zAMj&V;^Dr#%44Lg=pk~Mu}8Rvlv#Ylzk!sQeZ>DbDYN~EzmA+k&Lqpo+sRwWaio<@ zBNNCt(n5A1WxfVUPi7zz%6)aA%t|EuoRpc0#Qzp4vloefH@TgBkz7YUMlL7sBJU(+ zRwS`An=B_Mk@;i}nMK;jbTXBUC$A%~Av=?okgdpOq|DkR`S=d^=Y(IAC&=UE+vEXq z54nTH{eLz7I`SFvN%DU3UK02BRsDtJ6jJUNN!= zy(`GJWF*;w3?=1espy}_jSk`W_>JZLS31nBY6L|^Qij+?_CH^nu_vBgf1S!)~ zh~7u!`{Y6LO>!ss8YwG)O1yuPvO1{v*N~5p50LkgcaaN8`2tendB_SOfmrlD0VTbU$T!J7);YuC^{^lu_JkWZ11lS{}tat=9@EFp`?9C89Vij-BUCH)jKnY@ndL3Spu zB4t%=iPxG8C8cpi{tceD2+xp5$q&f4$o=GQay$7VxsH5{Tu$Ca-bv0OXOi-rmZV=n z7Ls}7cycV6NoJ7poKfQ6KqioJWOuR)Dc|skep|8?*_;d^FF;B34&X5xJM# zP1ci}$u;C^av3S#&fza~9$7`+LCSL}{Ds~|jwd-&P$-^Hso}C}JN`m@lh=~nNclbx zf1!9jr81NhkI28la|+=Z@^kWE863=4Pa^PpAN?fk0#9^)yP}%xbZCr~ae|tsq|3sc4Pm&*z?~~7vPm&AC zYBHb9A+2N@8B1PEUPfL*nn)vg8V?9@sD_>(kC2DS*T`4Mr^v_2yU07qQRGeJU~(YY zhm0k^*sP`hDfu4x7P*PsK;B6%APdPnGL1|jyOUkWNU{Za?qx0gZ^_TdPsqLGZgMlZ zk$j9?PR=5ykrT*lvK`rmG?Ss^`Au4Wz9%0iSCIFScaxLILUJ+=7->%zIi4I#rjW_x z{`H#v8{~`RI`RQ>DLI>*PC7{kX(2n1hhEU^?<4<7zCbP~A0(^E+2jDSKiP~_$TQDt z_D_;;k^9NV$ra>6vYIRy?;GI9y&Bj=M-NiTT|Iffic4kr7PeaLR))uf3ulE-mDDCIdu){~pbXUHeX zuOHR)Pm%AF?~wPAcazCvBH5e_BhRnY?0rw3Adi#plkbpQ$d||`$VbV0$XfDxvLD%o zj3Prx19@zPmY*Z!>*P){p1h8{lDwR3NrscZE!XV*M4ll}l1Ir8$QQ|VE-2eKV` z^Cazf%Oty!oyhaIYvJFMN65qEYve2B)8rFm9l4mCK~|C_WDz-m%qHW>>&Qs51-Y+S z%irtddU7pkB8}udV>JC*aymJMyoDS?4kQPV(PT^V!f4Ij&*WF+m*hL-L2?JVm3)z0 zN7_g$DKq#t!}u_aT+&LNy*G4(L1RDibaO2qC&=UEF;Z4kH^Rjxaw-M#y_C!I z2+@-j7DSHs{#C9g*OF_<)#NI2B`K@9OZ+-=F}aAGM^=$D$V#$=EFzme$CyBQHfbZR zWEz=5CXSE+!X|GssHPLspRaWYgy{*_7KzE15>7kO^cQ*^BH>b|Ecf8#0QFAkE~j7<8mx zohQF1&yvTYB=94+( z1TvenkybK|Od%7CcajG5iR5nA}V5CbyII zCfb$uUqsF$tH`F$l}actA}5g9gZogE+S`;m86HPAoIx_asru6+DQF5R~r2(WC9sS_9DBJT}TVrhKwR3NHh6ss8&Dc zNqH_N^(D{6gr~_9_E07BS5+kQHP;nL|z>v&qq9 z7HK1`WEz=5CXg_bwLg>JlV{1}v&qq97MVt-kjZ2s*^9(?c4~RMkQTB7*^Z1L&15Jkk?|M$J$^?D z&yuIf6XX%{F!?rlfZR^jlbgwnByKOO_E(du$d%+0vW{F#E+XfVRpbn^k}M&M$b2$~ zoIqxi_<617CyPuYQ^;g8kxU@t$X;Z3vI}V;JCN5>WFc{Ru-&sPrcs z5~0$cU?a4Ma_LVZ&!JrUkLah6(m%v6{X_Jme@J}k4+qaT_?DM) znd|u)?#tZG@|}Uil{uT2VPEFSJ%-=fvi=X5@TII=JEMayqB*_ZjL z_i|t6rM@6>A+JEeGhs{iWghBhXh*(Tk?UTO%Y4^|P&=wV_HSv4@0}r+Ii-)8G`YM( zpGut^M7=+aUNO&Bz1FxEJvw%~gi%&RF>pab(|&{rMsfuQJqQf3?W5UxEFJ zs!qLOH{#x?#;rHBfn46X7n>^~m+Mza>lkEb&=F)`=KPlTUnQR_k%voB&ymoR`4%Fi zey}fdg1jPSL|)@yr?2GMXfViq7n!R<_T?Ue{Js!r|W-!im)`Y#pSyt#NvO8U_a@_#4(npKU$&xQ#vh=NKI2@`$%^~Z`BUQi?`J84=248dhY+5 zwt5F2M1M9zfo}1i9oBlmnpW1o*F15JTKwN^9!6@K;$N!LCK>r}+WONqtLDjFQtmO> zv!Wx#TGt>nQO$8%a_<==8am-@cJ_q_<2au=mRbJ1X3m zjNd-g>B%ZbUY)S$@|MYz`-Kj5l5^~OTo}>i)LAyARG=Rcf2tezvtyolInfT!(~Rk- zsXt{cp`f@dKb@AelT7afyS>8evCFv@MZg)_?YzV5EX#9V{76erA1UYNCf$FueRP~X zJ$+Pvd)Dw#3HC$l;^<+W&IB435_fm6pO@LL++^ z&nq1H>QAC_uLqUz2g8C4I`K(zDW;7cc6~XN41K8Wrwz5E67BetG1)%|rZ-9R-)azE zqe|9CVX!6c78b>zs@TkNS>89_CYkmq!{(nTgPTF(4&{>^sKVvW^P`b*_eJIv_+o~cp)JyZT_`RfJr*Hxf~|Fb@(Rk^=# zf6a`)V1M4s(7}UoIiBal;#VF=ZizEK(eOJ_|KE!eekbZ9r#kJVXCJJdQG26H*b6Hw z?O3YJEvo|+d#2ha$Nes5oO?{NZ3mi5~lWCB5-~MPjFAwOk$@_k?y+yx75r^p&)eSc@os}MX0K0v3diLmm zU*D5wHL{64$;382Jm{n%1S=p6~ED0vb*8m(o!S z+f9|MZdw_S3=Z+vK_Ir2SL_A1N8|EFSu|~VE-ss;yJoKgUP$Q*Kbv0-lYdfwWp4cX zLknyE#{IdjlcsvF2E)o8joaY&_VS&%-a@%Mq{cvb$EA)O!q`~dytB4DWVOcLJ^exY zPNOPBc~yJ3hpdJ-wO91I7R?-^vGS0{wDb^zyvu~`qV^oQxi05qDSzYqRh-x2Ka1yY zvQ_z?ENOoHzo5@HysS!!Yri>BgEUKv(-g}LA5Z``L^{2B5<5L@9X&9C{?{};7~bvo4s zXH-qBY#%8n$}NT7~aD5uBn)o!NPZE2#UZ$-3M z#aNZFC*AnhSs^lCv}{f6Ig4=X1g)zDHXW}{T&bzA)Q$zi`L3G1)3Za=o{i(TNQd&6gZ9{Ij7~lP~R`s+AZg?a=5K_ zOC4aXF^MT_t+k!iSG!0g)%7V>-+Wcacj1H@x+}d(xGQ~;tS8?%&f#AmX%zLCDxrl2 z%>t9Du8)H41Db-QzvI_&^*erzo__%QyXK$8f2pY*=OI1cBfZYLD}5#W$e*{)=4;q- zeu`$Uc4i%S`X0sWJSdZ7eUl7Ws=WG_7S`I9Z>m9k_X{G^zKLAAHFCp2YxV19t8bSU z-M7tJqgZPyPFRgct+g}PYSNm%TAF^p*;b1Vm2I1U9{7$oErJcIjiqunj>qs zRQ=k}@JfoF9pAS3A4blrLscWE668C|2=mXPE~;67M5$Zk7`4^D zm;-HF?GskOTBB+YtLbop6*ON$t-Iz|08mHnv<%pMKU#f9t-jaPR9>+lXwv}vgIr6u z`g}v=JbB;mB4M@G9aR3p<~x_^JF7-HH(#<~wAO6NmNaYUX9yzG&c~mNKV+?~O@mie z%w@*TR7s;2YgB{&`< zJK_LqlG8o7UjA?8^njkz1OIzYHQkHlbPB4opdco)<2+R9@A7-Oo?okPTl}Gyq`gt0 z-BEu*&4^kT$)W>Yq|y_ma%*dQ2ylQ%rPoWP$BDq-skhmDUugOFeQfi6u6HXW6S+a{ z6_6n6YNJHWlBm_M4Y2Oos*V-4TcRLooCA?C2NqGUt&J80=b)Vkn1iOxUawbX{r_H> zn(jp^6ICPGpeePQ)P23IJm&mf9yuA+JB+pg&&nYhId_s|vSy1M!dhI8sTg4)vT^i8 zm5Wy`aP5mfVHfrEqFLhV^=(iM-5`ysXGo%``|{=!rM_*a`k~hKp}TgA96%@UmWK9A zPtMo8r!pY(_j^V;HA)j|U97R4Q^NvAPIc7uwAu^kU9`qflUC$I&$#4C+pn3go(XDd z)l)?678&EMHA}V8Upv=m=NYSSshkVcSo&efrnOe@G$R2S-Sq_I%%trHdMH$+!cHyF z^1W6KKRHcKR;aYs<=;QFpgP@*=G{vMTu^O#IjZ>_~YtiM)om9c%Fnw|Oe zo-z8#xPd3lXr9ln6m4s*=JTm|RW2|vSo7rbs+`XP-hiP~?ezVcMAVN=JFg~(w5zw9 zZ=JX;vSSsZ8a7JL-0@3P%TdsX>}c5W)42MVqVVHw$H{Sr#D6OL;Eq$be6ZuY+dka! zOZK+E>bF*J{l)tXu1}2C8)OX|?_E+u*4i6nLi)8vHIEZ<0@cp#%<`!-1^RfYUJs2O zg$N7uB1sd>|Iky8V#;jzfe~k?To+W=qm2}Ab8F3DTrzyzkQJ9X|D0!HM$Hhjb%0u> zo=J_WDqpvoQ4=PyGkjr@^W`;ciGq_aESwwzZ=*}Y)C?FsZRgi}6B=2U z`TkG!M)Syo^=dTiOGkU<2V5K7ntI#Blj3U>N7e+0uUD0|7K|}=2lf1ltUq@&@#iB0e1!b4}4r<6qc)Gx|QYvIN{-GafAq@{bR)wl zOYlz$scxD=N6;Gbk9OhR73 zj$}~{`2;&QICZ-Y!P3t1<&2>({z)0tZ6G`-rojdp)9_FI0VhG&$JX#r4@L54cuD@r zCOdM-v=@c#aJR!X+Vt@Uzrol`)n8pLv|n6fDh{v)4f zAe)=$^SfO>LBy)D(Q9i|0!YI(Kvn-*EkyZS>@iaq^* zC^k>^fnw{K@Yjg%<0dIL8t?z_m7nc|t6!WXgX*E_`8-e;Y9lLX;}kV6N~WD))nQJn zKeXNdq<^t^jncE#+!kl68Ihft^uD{9&39XNrtkeUUw!qlc9HYsUVQaWMsIladH5E? zmmYrzXN{EVpF%usBxHK(xq7p=W2Wx|+qGl6pXzpQNQ%+h-dZ~(1s&9w>3cn+$7`pe zZ8c-tAw23-Gf1$Lm0>(onWE}8x7Ln;?pEWeP;1SIESqn)wa0E-(t#PB(tKO1KQU*{ zKjcY4lFh8uv)UOvEs$Q6CpL5bhu)sNGcYsRqGHIzo#L~V)Y%?nr2(j?=R7})(nla*4&hBP1@>iX7vrtw)(bil+2wQnq&5K zu-1&j$#IIsnzT=XZ-SwH)}(LUO5}!+iLfPGqxKSewwiI-8A-2c_A-30*=mMHi6PZ) zM$!RyO!b8Z&$UP)%G)X}a>KBOl*kSH(vtRgFU2n}{O*9dZyf4joF!#ZZ{Jy56G5zt z;_H#H`g>PDKDGNp)u+3s`fw}bm-P7KsYoDl-lZ7HY&Fx{*=i=m*{+?AJPc0ENZR3v zw$={Mw$@C~w$_fyIr#|cD0Q=1df4Iu9?x(!dlbcTK)M2TP5Q=+9y_#TX4YGK?6TH)cSmjrTaC^NJ)`yD ziBlLe23xH)Zd@Rb%CQ>HT77P$IXE#h?@WgA2b*tFoYjac<>}cOwO*su=RIIel6wXR ztdWC$vepc=`jXCtm_1jY8;mZEZaUoGO^2iZ?zj4eN7;PSF-A_eNSUvK`MC5&UPF51 zhR@S{$Iu(P-%o&5wYFBqjM}lrQ>Zr?*oRxK#)HT$>Uk8JeH3y$+_E~# zGZPuTG4JpAO^llq>_>g2 z8bw4uIt^~*Swtu|@)o84(;pc5O8P%jee=4AHEYexjhV&^sdGMC3twvWZx);H=Zwg- zche$Y*^S@zsDMo0G0i3}W7}HgCSuA7Yi+Vp<54Wt`%GFGilObO!$)w3$?)ww6)pcR z#Xo-@u%kuDkUK5BS68Fhl*$wOiB`3z{+Oj@?E+ixVok@B!2Mqw-xI`>dfk&QIighuWi0>ZKzwD z&$%|!w>{mrzac9p-S-pfz*gg2YeQ{e*o&{XC4Cb){~oEo`5(@@#fAYuzmZ#y8@UG% zV8D;!gHlSpb85)RNNT9Z9HeOVeb8_~s~W9F;_EY#j@mFVc>84b_#$(D z!z_zGbDyJiY`$&CR0b+q%gImP78$!z)F(tX--*UYBz_~xtr5L#v>FUtuju1pyP6?6 ztE0RfR-3&Jtsrvx85$+!u=>uQ9D++VTg@zt|3k8osx9ffoMLBX+k6;cZtLDON``M= zMvtQz)fdjXhFX1@yD>1LguiJGIu0?SY&8Xzrm-@8pJnv;D5LtfvypRmAmI$3c2EA~ zAvG5?Q+?qwn3@tjyf(C7>LO_~6uI7b%92@I5S3AV{H%T-E5nx#|6ZKCorX-G*PQA5 z(pEF^fVBpOlHQ1%Cv#Y2CjEjwo)J0pyA0pij2?$G=A5rWVz@ZA^_Nw0?(*)=nDbK=l8c;snVN}x z)*kO#Yd1$76JPC;)ZS8R7Nk~C6Nv>ky_ zgI40-uzLI(c?|#BLw_hl(KkU9Jr1Jij@CK5+1s1hEqVG!4cz=$@g(&(e>ztih!}*&LiRl$cIw-?}k2>7N?0E1yzk!N(uPjxxg^E8;aWPl=Oyz0~aIJ3XikRD6DML4ngH zO|F?KbGqFYhsS~zl+V5l{*O2UJ#V0T4?W<=2%~PJDbM?Z@WLkYfQ80;y1mBI-}|oX z*jR?6RNWd&|94--|E{8H)X~1qKZ>SaTp>_r3Kez!x6@NEyqL!S8}Yy8@4xdJd!XdH zy+8lTcdER1WU<-CX8I5D{xN-G;w;_qX-8S%b^YVuiRlyH!w@^!;fgINFTr>8?$}9* zvC|4F`sYv1bH|tV>D{kzT70hSj*@uKo2^` z9g|m4VTc{$mJE2OINf&?1Z7Q z4%bcEcnm$UY4B92@1Ysw8%*)2-#hCecn!8x-_LidlNcECop_HBU&Z2F2lWc|vsv}O zoNx@1g&`R>`If+~vBKcR^b3V$81vLL?D%eV3cm3vaTK}@m;}Iub3*~feSBwDAhGaC zp?q?N>}nabm*f_xdBFRS<&%mb&MPUGP`t^HGmm_zX^<}@Q9J%xDK98cy~s_!1njA` z|2=DL=lSbl}@Q z1F6;l#0XWzq%L;2?GBlm042+thGV`Ae0>dJaal!)e0?qD z3-SgRPFD#oq%LbzSY4*xe7hfd+na}`&34DhI_ z`vqF6dIn*%$l)$B$Un7+75M-d%1qtm+QwPkN0x9SeF67AAowo zFoG*Sy|Qpj?CcIx}{qx}>EHQqk&oBqL#YnHiwgL5^4O(s)qUDG#k+fY-e6#Mx+uUl_8u`>bpsdG4t&h4?w3~RbvMQ7m{d~i_5=o3 zb^Jr62z4#lSBFD=M-M3F%nRrb>Ry_&z)*@WB;++*P=ZnSgNNupiy+gAh8-59)G0%$ zTe_6GCnk_*&!?evILwFoLllQw(srSN)D#{^^2ZH@->H zv*(tbQdhYk&yiQ8<{KSG{hStk2V%W#WIx}j9t!*ohHvu<17~=43{3TapS8HUP-ob{ z*pF0ZsnZu|9)GBKi}AHRUf{=fz1S^NckmNi+sP}(Xo`@E;(Yv4 z$`3yG>t1A~Pj3W3%L^^IU@hJ24N}7|c8?c_O0k?-(5A(9{5bbYI)&H)@E@a-n3TN= zj0Moim9v2QWwimtDRsCfNd&ZBaB_YLRJ2~_zXa8XEx$nf#VMUo{Z3!)$67ZE64?jI z6;dg(gU%q3^Fk>`?TV>@*6G!5U8-G0m8u6odA=K=(kN;t*X`R!$wVkxVM_ytGt9}1 z7KowE18|Y41>7Dfd%QuPnag0GjB4o;@Y*MPa@DHRu1e*?z)@0BC_vLTPV=U{yl{voXF1 zEFbDoGSCS*Ijce=6nK#y-5$++i;H!Z=%xF{riwCjfDQG<)CJ>}bMC0&8(Q~s&1kJD z$IV+1Z7S!)DJWleMXYAYKyE027i|bsLRrN+_=ATdx|KnqqM`|TD`uA9_t#aJY6FZ? z7~STATEgkD7BUIu(XVapY~nhpMvAds#l;cjW){{)|ufb>DUBoqbbVaY5*k+qbN`U9NBRVUevn^xcsr;!Hfm-%?7+N zf@xeuRRbpCWev5pRD1by0&JDlV~#@ebL>1~C>Ff!gg26!OXw{pn(%PVgB~v6x<;N% zV8(@(WZo;6M{Bxa*z7DHGX@>Om@$N_d-Iq#&d%g9imt(vtW>H)c5Udt=*7W2n)QNb z`u$*Uvx~gsuH#D4!iyU)Q=*r@P)_>hJUdzNdIv3;97VGmEL)l@%2C^En({2y*mD}A z-^kgR4Ijw2wKb*KrEr>5j2++qQ)UfI%^F8_OWuYktAT~vN+;~f#E~>0!>9T4%h0ED zQ0U!bY?9DED%KxXeQ6`|Ow7#q%}O0tVqqF&8w*n~O4*CcW(sGn)7H|wfO<~aY_T%5 zuA!zJHFrTHSrbK9W`^2FK|{pNNp+a(Y3*&mUO3b_jt7L#gl2bB#=m7R*2hAcUnXR1=PH3?Hl8Zs=EmOolU z@kFY=qIe2M@&&xr7K=})M#*YORY&KN%7$yIoyf+d4&6_qABM*|wawIZ7GiR0`nm}_ zNzE!YcdN2uW@d*=6cm=_XJ(kR25M~J ztgRMXzQvgAAse)%ft&1T?j_5RHcR0(rMq3S9kjsF%uuE10-uqXq*YWkVWq;(EvauQ zDCO&4^J&JadaekWJQ~Z)s&0GwytPJGxmMC_m6A^V2bt=`Jd>4(;Ffs)m{39du4RQzF0onsvEddF)WBx3?9w zow6;@vQtN2Ll-m9$cGC#i`BD+;@b86utSe_G-0)OijJF~bFpcN?FaOQ^D%vph>9QH z_dDWcRjkP`D-#uKYz6ETsl_#PWdg?stlH)f8L9Tl=$b@ajaG+FSni#*86#I-=}Zx5 zlMuH%x2;`X$w}9HQoo5+IPGMZrRFdtyAC%jVq!t!SUlhXK-;3!`Dsz!l4w&@yBD1{ zhSavw9*Ls{#aNu3TBfw{?zX57o~9 z)85H$ZY^oV1(Tz&<%JMcRB{j+m}A9mm0RY>2xezA8>`7!MewMMUADk!6RAkQ`JuTX# z;MINVQ2QkMplX1sueMGxMm4HuI4CW-O}d@Q05&T4A-8!MasCq_Wa!|`r-UN!NJF;(s$7{9Ij(#c?IR5Q15Y9gkJL`jQ69hR+|wTG~1MLI%x*=*0{& zE~biDvmTDr)1GB3V$Ce8i`AyAk`@?68|$%EVyP!aC5^La?-AcJ&`wH^d2$GqoiM9| zmY8RC(D5tH>T+?+gt~C)JBh?vo?$xc?9W9GZU&-E$Hq^LOCn5c&E#@R4+iD@%*>5ERC-I*EauGs=PUT#xx%lj03Uy zNU4kNqF+^c(P;bmj)R&cU_HezAi*25DJwiyyPbMtZ_PH1ozb4d)pcTaV{a6uY0;|` zO1)Et=L08$=Swk9v)h$Qr@6mPkNp;Mk?)WLp#H}BW-n+&dxbpEZ+@~ zjM&YOl2|7>yA931ipibf_FSt{NAWsW?9Os}WKx~v^XQ6WcX_1VPVl&SQr4X3$24A= z>hu~f)|r}9VJ8fkrb?7|%%x%~tHVo9ovkkR=|N@|y}pV|e{kbg3%wF*M`O$jys zMHfzr)z6tYkxW^k@(a(2Nr^opNQ104HFBYbQDV*peZ-u6Mdtv`vQVc1W2uLk**5>+ zxPz&jGXp~G-a!hcFDM0F$o3Rs$^y=^n?yqkbQ$uTU z=>}~4(LWltBq3HK={5tU3Rk<8(`(supP5RBXJ66yu_jBy06)B;Q(L-b*ag|s@8|W`80U4FZRfM?g!xU?SQAFi!|zk+cM@^P>Od*lBgE|S z4yB(-@8-suW4PLuV_l{AJ%LEZua{ceUjB7+*><{}QJ=E7JIBk~HQ4aSeTucX-b%eu zU@fttF=KUf7S=Jenp3*XMDYq^GaY2y4;Sxj@f8EOP+H$2A~vb(Zp_M-D+whpn|4Y=m>7sbF^yE2MCyEvB5E~>o*B~)MG5K6Nha8xq6RkaPJCACUWB9_~f z&`7>wt)>i?s8ECm_1ZYE&_abnm##{Mg>#UtoJx!uE%|hxhg|~i!-EVWZUAh;Ei`4g zq8lh*%;h9%8C++Li|_1D#%ax9i*LGeQ#jqSmsc6K7_{`mjR-WMQJQ#y6HeMsbS(IB z)r&o0)e}8cezF9j=hPuelYLnu zcrn32n;lBwXk0m2EQ@tM6>(Ngc|Ki9DQN=t*x@=CT#ALWwYc$ygH+ITd6}*7Mw?8t z*ic2vi|M855p*XWF5$q9vLIQGR0beHJ;&qLA=eTXQ!31~C1&yD5}8XGONc0$BY!$G zXVPr)<|SUa(1WH7l`>$NYl*DqofU}4KopUj8hVu)FMuZ5S^>w-s&Ezui zY`zL_`q2#^NSKc!7EZ-|8)%MvVH(~r%_y!xVi9st61IvbGNBy0Ah@`-2A9O)LY|t2 z{8HhdkU~|BP5VZw+vMO66^fFqI$s~{^0QG`Wpj0sSEq3>S~}TuHy)J>Ks3I3wLOZ3 z*>t-eViJfNv$TX1<2M>NGsC(g4^kFF!m_fOz6g_zme~=Q&(N*{(!zCrJQt_6H!e(V zLMOtPharsv%0x+e1a&LWtZowAzv}%7MbVutS77V-?b2vI%4sUc#euM#VH6EYpwQOB zEvKRRQ_VK3cQdX4Fe^8l5zdUF9XrW^EoPRt~6VLESj$!$Eot8-4_lH$pQ3yT-dnK@_fqB+I+ zbEXtboFg=OR>6V=xXoOnlM1F$=V{)}fEi3xJ$<-CtXYdIG;!Sl*y~F8WnjJ;wW<+M z-1xHaN;8BT5t?uZWot3+9>!lq3H24Y3l$e7Fa|?$-~?UzD4|2ML`xn1qfX&M3f$$1 znRU`;b_nj}V59bG3&p^bDwpnNa5fvyU!uadV_ zs$D1RCa?}(>u0IN)t3iWhL;d!4D`J1lEy83HGC~0ZjP_z>Z3Zs(Wah)GMYR)ud}eO z_i1kgJ_WC5gK1P#+Z)r?7n_~a0qu+In-3Vp-e-5fJl%zNAWB59en8uFZ{LwbYwd$Y>$+m#Lw0(7wXV|j-nK98F;C?gQvWH$n!3qUS=?Y1rD_FtM zdP^TIhi|mXD-IgP3zWUh}9hf-w?<@2Z)vleYc<$u`zPL>rrKIX{}Zx&oW*k@od(ROuSKv?wm(e33up z#f=@UzGgAQ<#hW{v^1@PR^`;O7BZ4*31-(QKNYeZ(%vR93Ui5)b#f}JgB{nB+e`~F zGDHRrMnfsS@5LH8&~Xhp%~)Kq0)ht_zn)t+X{ztIhSAN9Yg=e3Rfe}O%y7r`jBH+u zOL}EqWT@jhawvUm$3b}m@!^e`=l$-xJh!AIKnndZf_;kCyixXHE(HeP*yMqd{t8m{rsy2axh@MlS;xp{H+j6R?L0~1$u;cOR2JtFX>!R#p{HIk0uFUPQ z6LK0cBcQEDh6kY_yHR?Pz(GXMsZjA5cH9fFUwV;Y$K#V18>JV?g?!eaW4(3MYtGGa zv%R$$S4NqW5E<-vUU;6dzuAYHogAd*{SSDg`=Cakim}Y7>#dD%B3^flNs)~o}9I`{3aa)J05>D+H%=y-p8t;b}hpl*8`te zgM|!tT*)ZCoq(QB2lZWFSWc6|_AXdQU{MXP;MX$afNLm@gy#{(nIq)dhc*(hKq)B0 zoucvyV0+UyH?GXqeZbW9Ru-inxtK96kUh=;wb{3>!p6)4#>D$0wY;kd&;jSkXtri~ zz=YcB4cqJETztz7#~A41SsCg`1)+~oRMcaV#;_y#lzWz)Q%ZW@&^{wNpxh-hh)OvFwXrj6m*ZC|wTIinh?olU!~ z1o7iRItb*!@9++mY4BQ12-6ySrHglVSuDm@M zHTZI*xd?8TBat-s+Z}N};?U8@&gX_L3T4wYLwT*x$P#y5w z37KSNyr=KH&=rFmjh*(v>0m7mTVq+>z(K1smq%Eh#IDZ1*oj@+0oU`Ll*IlQ_SUM& zeEe$jl$y)X9MEJRe5;5>TlAI@i$?o;Pwy?pqEEFptJGpvw*S_q-Y77?Bx1i;*H(lP zpIO2+L*d_9q9;5!dD8yRE7?=BKC)y|an~@uf!wUW`J`VTv0p)qV;&@dZ$2UM{4zeM zn8_QR`YF+nBSbyEVT74aZZ?P1jG`IG9q`#eobFpqU3x>aZAI0-@f~|{q(bg%V{3Jj zR%vckbV!f-0ufq>d@gDpVr|!BTYdP{c6~D*#A5DYs-c4%!}!}&k-y{U3!qM6e9Z%= zH~5>2_(~Wa^zql}3LD@`d?2`ZzWy2pyhoqA*AE^k$j{8wf{$=*olrFAL$%RSWTSTv zG)CGs8ZtW&vz&H?bhQ|2tJxjU(MZ!Gts~v`1hONBKnaf`W@pj<+~?AET?L&ilL35! ztWkamlWWO18J6GKZN}xm__!{8%C`}{Zi^^Gb*+`(mBr+`6@@52Vq1=H6t`fWT8|Ib zu9rw@ZAtxVu<=z*fsb!B;`2MGKbU0UV?-4=s)8^+sA;?8A7{`qbNG{-n8{?-H0K{% zCTi^4c416BSHeQMkk?$w3nqVLb6H6}US!QbR!ZUPxr|eRIb}yN53ity3xg0%dSpuj ze^eJ0NQL$*#UBZ3LxSDMdZZ(x*oM@kRGWIteW)n06;C;eYrHe_wmq;Y3N%-0D5JLw zBmI}d<%rtEkR;^1(=3e45$Z%Ym3}QXpfFr)l3!tsq8cBxYrrcy+Tj)w&5khPHvOff zwC`DEo9+)2Zd2tbDAButIvCABOKyK9G8!tZjY$)UrqCv8mC$~^Gh{9lWIM-Evx<6d zgEf`>omttZj}yA5HAi!tW#^9?6i}y)&%c&8)D@SMm0{2{du2Z2S6YK3hxq;va$Kb-B_2i?3IGfEo27q$3MWCb%yZH1|iO-MW;2-@Uh*5=qWH5`v zS(T!!0>^6U2SLQ+X!xn7P<15^%r?>=HYQ3+aUKRgyhnjgNFd#YCLma%@ehJcB+b8- zq86WyZj`@38mj1z1DP2V%*?37N5RVFPbJRYw9;QCzGhNSe;lZ$5Nv2((}XX3;|E6& zkqP{9pqfHpQ&y|xugozJ8Ike~M2eM_LJR#+MjJQK#+MmDK9Lpt@ejVj)>K)>dC#KE z!%O(B%ap&!XL(H>|4{Br_=f`P`G-*Cfqp7Q^@c`tko;RIYLM+x`3px>mDJUhC;+sU z#|;S>#Y-6FL?$Tx25-V|C<=fq{^4LL1xpc>8t9i08DB-OhuiR1De7_5vvdOuQD%sU z>sv}Hsn(hyNo;AL+Q?KHtQ2TC_+BNRs#zf>;y5c&0DzKyX@@jJS%D#>wzL-IDZmPbNeTa~r(a5nD@CoW4zP!_4#=g*kP1*5O17iO`L`6mr(F&*zE5?*C#A()#DxK_y! zWCjIB(EB44U=ctpBks znvTQj%t$6?W(!C5RWw+IBNxaQeMYjW6^Fb`3PqDV1n9$`(j_u9qy(Q`-%!`EwmFsF zGULhQ5u&_i9i(a%P!QW@)O|P9&=1u_e4$<7b&sYNbixWq6pf3h3e+ktN|&gJ#Bi+% zQ5i5`iDv#q1)vWWquJ1;mcv*nF)S&?@pz`W`!M%qP z;PholQ(3iG)6B6jfYj0`g0ITs7dhkX0i|FaCO!4|MSaENLV0`~u%)4{6x3!SHv>li zn5e6$D{I_9fWIt>+z<*-uOL2RFHt}emS3Y+pkAZO6YDFR@QF%DRX0&24VoL#H?=f1 zmNu;>sOgFf3Q=1cR#Q|Q5#fTUz{66en*qvJWm#=QbA_mso5YY^hqW3Gjml245={=> zUvs^}n>Cd5by{wn5?t4;7-o!hI!YxU$)jI7yo!TqqL_e0nyHw0o4gE9exxJ{7Vphe z%Drz5z@n2QcnzUqHT_DA_NdW~pkkNH4{aceqLMyFiH|i>uJK7<0>zbBw$%@uGI9Q* z0=yrwctOz|!6a~+Du_o_i=mu`K`B*yykF3Wr{WBOv$538aAtIl*;C3tls^*S46ake znr69{BG_16f~ApMVbwItMHyad;6+$Xvs`V}G|P!?jdUU8~Ry z|M+aD)yNf&s*1*g6T~)-NdglGiyb^kslg*-Ui@)DTHLBlZq+uoYNK1V)vem>R?ax= zYt@FgYRg-->8;9k2+3uH0%%oI4FXb@+8Jkr+8$+y8y~$cXv>&+s2O>+!j;kzREJho zp*3Nh$yx_1WuWlz&7y#UH5Q+|iQ3yEGk*#-;?zk#-dm15 z3bp_br*H@Z3wCo4FfY%x;~MdL8GV)xTgy}=_Gf3=xVNOXjs4B2DK98qkU?pWET->Z z*0yU$xVRXLGSp#FJXO9)uQzx2<#2w1neuLK_3*dW7vO?%5Y4x3%=en@xuj9U+=ejs zjNrD6#L-!^I@YckyyI#Dd9a( zOsdF%)_ZdDq=3!LgY9q~CLXjJG7szOYOKI&v9VITG&93OYv>ewF+S2rb1+(cm$a;H zqGLXIB8a(ZU2#KWOEKOSFvFq@R8e-Uei+v%3i+X&Tz_Ii1w(fky2-{2fejW?G@`hs zu904Q#!1hT4e0mnsKA2>Okc{1OB!Jc#ymXYE3be(c=m+3P~nCG%uHrvfR-nxueTQ=Z+kmGqN%lKiQdO8I=_!6~!3> zwk%keQCy#uja!OZ=s8DB6M!kWm(A%9@a6(!WAp1Y^MDxv3$I-x`3WR)B84-TAB(p z%52aF@~mhbd2|T3MV*-x=|k$ehEnI}c#a%>c+SU+bbl%_b3q#?+p*nG<7-J7T^rI| zv1V-r9taK*eEHUb^-+wu-gl%hVp)-IySD%HeM^q-2^b_^J`(=3>S9C=Y2Nqx+EPzA&F z{B{#Ec?nw!IuTt-Gk?8#87KCur;|xXym2ysp3u=_KWxb2G7&s|;?JvKqM6V#8djl2N*2A4sw1&y2!}=rW?!l~F+G39WWE95{P5q|h9GYy-w(>kGCabWKA&=ac zX%a5apz+mAWUi-v1Ctxm+IOWRl?Vg=714S4qMWPvqq{)L%R!M zVkon@*kGXciO-Dlg_xMI%LZA1Y^(Z&ZFxR#VB2!o&(huI1nIx|bPUnv1NIr35*8R( zsEG24#uhxn-%wYIM^!ZfPds1&pG&jWijv!mg$0&1cqW9W%k@<_c7;U`4kX~dBe3+E zhd1(YaS`2sLX%l6VJM8_5lo`P|2Q>{AKEX)gqNm`4a94p%2>lV9?>ESY2sKErB3L7 z$tvI$3wL@n5;7#!EsAz}w4{auRp2KD4N=^^iwqA`5F{Vkxc;CwVP>I$U9+IUP90k#9rHcZxit zL|#%bN)>%ov2?D`E$E=m6i7O#^B1M__)aPEtL#Qet68y*Yg=TKVx4P>6zkw#i=>0E zStMuH_bApKrbt9<(1np`D-5Taaf(-M_WvPR@+JU5lhpQSGrNqBbY5ZcQ72)D3D)ub19v?62eKKPk z6^z!GEmrcn{u^S2(;Igykp~ns#6qXY3o3xJU=LTO=L4$Ras2 zwnDK=6^!OW&1*K*M3l~sL!{zM6odpf`%F?qCfgzMsEYlJsb!%Ker1v( z6nBn@2+Djm3(w(go zxy6E=9*t;~Zn1KwMbxmoBnS!_Q3yNU81Yv{bogq*kXEA zv3@W`BGx$1Dwf-9A`xqxPn3v;QMxJ3(lCn17fMSPGogsiHCeHGo0U9r3#zkZ2B1~^<`$0=2qCcFiV%s}uwbN$#r}Kn%tK@Drjb5mOe=|u% zUudHer43jZELXwfERtoP3~AkaO+^t6OLKpFjSaNP$H)j48z*iSW~T(c=f^a`3a-4uyvO2vw`#uSODn7bA0 z0aIk54*t_56`8>#>3B4DLj}*XNSCSL9*d-d*I6VTe9$62tb*@Yr0@b3Lfd(-Y19H8 zG>sF*X3$hrtnI9@NNwr9%hWwj2Tg@yr5Q98t<<_FS|q3L7nM#kcI2~{ZYp+yg3-41 zG_SvzQp8BTR)5qQlVy3NwfnG8ZY}M+ZhEW~` zXhj=MMGKrZYLQ;1$UGffXp)xeV6`H-#k$*0)QB)`irA9Z+7nGx^L22ZNh;RCT1)$V zCu-zvORg>37n>rBb+FtdE!V+QOwwWGI$XAw}4*uID73rYm^@!D|&h#=< z`;wg=jg*+g;+-CioM;jusgWmb#E9ufx?)aNFiPP^ibPm?l!~2gYKfQuj*3XA$XN`X zBQj1UWYyJ`y6CM67SSt5iRCm;yU0=;u>zPDk#9T{bvq*)q9c`ntCs z6hw~7Rq#@abh8S+W|5pVRV&c3qwKJpsA6kvm>K+4kp`wneIOmBg7YlW5*0kpB4r#Z zBRtsKjOesTCwHA`^gNjyqXC&8_%dM&t(c$Xc?nmT;h!9`m?H=mz$Cj85YR{0E4v#XMPQ)mAyV9UFq77c6q+c;( z6}_&4LsY|ql&;gq^=Y#W{_oNKsg;$lR4~=binHjn-ByPeQIXpra(9Ghmq&Vbx%RAO z;qpiem+w$c-eWpDqPy40yswXQV>&XgTB-fZl z5;1eo$ypY)N8~P5Uu+gRTTK+j?TFP)Dehxd$Os?)W+I8o2CG*$hkmOEji}?1u_9L7 zQAFNRQ-b#ujB+|po1v#4m`v*6e#|+zSP!YJKW7$0#De8F6M0hEVAVCVLwe6NFQOPC zV?`806p_nRL$Ny6h!Ikop<5s}ZFO)z<{VtC|El~2%)zO!9Tpl1Mkv0g*Bu}99Txxo ziQN*6g*egX$`b>U2lySpgBhN6c~QFv|405N^ZV7lWW8xW#PPs1G_ z2uQ?#2TgTEQWMINf+N1xR;K!p1<*%Z!91 zgY4?j6N>Y?3DMJFJTVR2kw_+qQVayfA(*VRLPIYEDQQqsOmjg$w3oIWNN*E?*kLly z6vx%|v&{_hk^0k^15XO`oNQECedpV z`+ICQ3>X6Q`{1P8z;IC^Lc}By9X4!$tInM+{dv znH8Ab)r@dBygO8L6jg|Hn{G38A%-~w$(|!!IzK>$o7vEAK;DjWslv-p(F11cLS#nJ z%8<7#EB%0(KBYFo%3U}k+ZMyjMou62YE)N>n5)x?$o)uLOdFFNGb&nYLX5V>Fs&0} zOnBynI9jJ^8kA=`3l(szv`&a|hrmp#W7Igt_mYhR0uxkxujw!H%|t78%@AVJQBdJV zwVmuSqfg0T8r8&n3Q}O|Q78)`riGhIoat6xy|zph*eX3tnqfwSvEP$|aqYG&k)XlZj*=tFmjcgj16(D!avsjkhk~QCA1#Gru1t{4f8?&Hi z1bYGXINa2c>xIM7jrK4mP|u*7BsWMQj`0eYPt$~06yW}KadsGW$>AciCuvw5SORrR z4>#=SGE$KVla>>Gp-&2}@QOZ%5OpO2-6>FP`CfJ>Laeeffy_r_0z0v68XQ~d7u30> zKu;M5y%a1TO;ngvF@^&~s|?f0*-V9kDzDUqbcCq3WY7#vnHnVnnh?i&StSKmTjGeW zJXdQGwGJWbXdI#%S|3C}h=xHJkQyg(c#W#sFs4a05(JxbnAxJ+idj8`SgYHgjka#M za*2)Bs%d6BT~E(5Hqd~(QSq$0CB$)~O}8Ge-AXo}Fx)uWiz+g$XKJ7v#&05b$j=0s` zVb)bHpwp3(tE;Ral-3!9%eu_`?S@$&;pa0MQcFLM5=WS5Po`J>_NinTm$^VcLjpU zAbD>0-H2csg6_V)yAV7AL04TX zmjF!(YzE3HE(MwzILnR>30w^HM-pBMZbsl%pgnLZ&|3_2RNz+Rv>0<(?<$}Z0#5_o zOlS$v>4Cohy^hdQpoM|MNevGEd&_|?4h#pHKxj44Re^=*`EJ6waBmIJ>cAkx`yUL! z-eZ9_295(d0aeFa4|IKC7tmu#xWRA_*lYx@pj1d#Bk*lT;9B5SB)$gt4kPd|@MglB zfL~+;{s;Jpgs%m@+X$SCxHqF&de;NL!3g{wIJ;;g@Y{^QoxtxR{wCn}8G+Y8G+HjM-qMx z@K22Zx-zki@EyRvG6Fk+?kJRzs(hx3VaRW7X!b~ z6<7%TLc%Wr{-`TZ37q|LDe%3nKm%~L=Q7|gx&rGpemU?rT!9lceg*J-uD~{pUkUtE zS74XMcLV>*6}SpG*TOx(zjp<01b!=}e+}@>?!Z%;e;x2`ZsDUh%ZgD~z0KWHyp4Wp z0cGT$HPotB1LNE`xdsG<`wPe(@(q6CjswGI#QRXeuc63e4R-+H?m^0ni0_N=eMFy( zR1i)Wg&*bx$5V(<5%)Mrc-&Kf_~zp0X;OM|muCSk@@@3L0@#GQ?mLc5`4AXI^Bs={ z?*Bw%C%6W5^ZyMPhArXS1T+2LY3xMTfFAz;0^35o&1k~@L^L$ka*}Jn5dY!8roj^5 zsc5nO5U`s_atj))e;6=&)5^CM?bV+J>`lT>Lv!_40y_*1!?z8s)n5mUQ`nBi>R$sa zhj?eAar#dJ#_64fw&~vjYy$CixCXTN&j(gS*txC&t^P}Zv6l1EZ2ed0co$MD^wNw zij`oHdJ2`m7;t*5hm_Hia+5dBDx_rakNcyOb$;w0qi}*7^ zq!&KD(+u~yBtSM=0g>rH`!_}%|CzwJL;sFyFunB2_I^(__-2j$KsES&jr~Y9 z_$grA@%%(JnBF7hc>kdq{2nm2?7vilsgM}^*)?F5pDF@lzo0$`sB2+Nz#)wR1CD0Y z0J=WV1t@F9f{_NBc;HlXyl(1jj6iSA$8G`*Hqy zF5#omVm@5#z23bPL92erI_z5u;swON!M!n(uY~ALv*$_ZM)#E@WVQyhf8g;C;-?%b zdxsdwh2B0!U>%S}G=n%Cr6>a1v{ik9`UB?yZ6aZ2qYwYmxK5x;@|APiuk4rf;J_OgYz8;ed_*WxDXFw?sq_p z{}eyjFw8rgI*g%4ARh=jD;-vfz+9b&VL<(XC%xv(P0cixQ?RV>P|<{W{7FB z!1e-`Oe8eJaL*&D<@ixKC3~rV^E?nwmE(32n`*?9-ak=zy5T+*;rJc+`H%>sjpU#= z*9d$KylWKMDSVHB^$W>A?tT(Mn_+JPRekik2>L$5&moBIeY8t2 zfI{z`{{Dc|V3_wV>d6KHquhA!rk-plFf!VE5A|fjHFhucWFvu5QF!mCo@|1~9&im9 z=+6g6-MRNc>eprgJCn?Pi25~pXPB`+P`|cRV-HimRsn1prS}L`8aE~Fy9_Z;1l{|D zdk=zgRuZI)iMUHZ@8b780{SB)@;CRB2-@=xl*c>a`w+qBNa!>7XW>HApi0DhG12^j z_%jW6SA=c#kc*e{Do2JyOe?*!3?3zhQFylD&O?kY^b&8Xe~7;jkUC!f5#-Xvz)k?$ zKa^Zr3yf6x(|K^vl1IX&vddTnlD~)hEX3$>8p4MABGP?1fr!(Bp}I6lqdx%rCt6F{6z z0`m;_$>9PlNo(MdGeqd=3D0O@Oz+Ztn08*X@B4lBk%mw_k?~_*ob!? zC~6kzqozeI%YW2i|AFv5V0Aq`&b?d7UXGriOE>&fcc1D{0UV2$7Q0U)W2OS5cG!J7 zd0@823aA?@0!IBy_Zi#`5k~Q5Qa4l%>>|Qu(e&yRV6+PEKAWajX9J_DSNAzIz1jtg zR)pQ>x(2KgzUhe652WtHx)2!BeXzL(>pp}m$^i9r(u|%V=%>d=Yy*0R6#sZ&RN1?u zXGj(9vmw@pa5p)_eL2uBwGat*ALXa&PCoBGnv|XZD*3$o7*rl{`0XUL#W?(Kf?JKg zFC*st2yaXHq~GI!$JL|!1_&edeIRE8N%)6>e_XbogOu?1)PHHt7ht$jzt`xOKs~8H zYxFCi{#5F;Snx zKu>k0tpUE1eB=Ud81d8q)`ONHjI_;~=O&gb?KF*h2=}C&qj4|cBJD!p7m=cov;1jCQSNQ{b zfv&()+XUKm3I_hBQQCD%348;zUr%sp*C{pdvqo0~9T@0__D9t#p%&>2294UF<>r!T_UgoU@t{$K(q;gN}$gZ z+6Z)dV7o@w04*Fy17I~--;BHkZUOcI$+m*IIB=hqT@Q3s;Ex*J0JJ*r8qi5({6?US zf%i1`IH2nTUug7rn&TUx{{T;khxDNaIgea9=u;$Rh)`dUnbq53eNC(&Ns7>D@Qd)5 za9DKyV$JUpjW3l9Zidh~q~vf)+8AU{U>E535U-y>CCPkc+oPJ5YO!Qgk`$r$!2gM& zrdfOy)j;RZto|0u9+jTUO~zkB$f(z;O1`gwRR4Dg9%JLQL&6}7FI5jo19>d5jxeaN zB7;LyfEN>OsBwRz%7j!$A|g})-e%&b8xKd}AF-8q!_h#3tdF`j=_E0D2UyP$djzGV zeK$T4ognKTN_}8Lu8sEuQV{9yBF>!{s}e>UPt>zX%I(Y!{Q!2qWnhmn1|r84JxBo& zIttX;#K|+L@vvf<^}!244h{orEK#*BBqatnf_FAW8yCR~T?qOe#G7Cyrt9CmApaZ3 zK@uiLuwDY|?-XlN1nZw52XJI5VR8hkJII;D%8xKVPqU^*m^gR^co$H#={BAid@o2} z5xKxl9>zYOO(d5tr=B>_H5`8lg*LLBFNH>cK9_j&4C-+>ns%vXEr^IVcnx@0QM9FY z-b@s|7aA9VtA!Sr$Nv)yjG)B8vfyHHACWFE2> zgH5Ze#8p4^QS7Udq zHQJtR_aZDY@R!&C^Xxfa084^2n-_gIj1yuiWP*w7b0|!6xlAG?o`6U$V^9-W9i-t@ z)EP80T?*_1@Ds6{?jKXFv9k;#gCZoJ1I5Om-IRjJZUW`j zxuBkFBc1~ym)UtHF?cPdcr+{6>7Zu)4)T8_)fbSnz}4!-=;uNHhFDFvQi9;fLgWkx zpXYEqH|>rST94;l2au9f;?Z3UQM95^a16 zV4w_)TB4;AEwHFJo>vi?#wZ>)i|&1a_9ql7s#wpro&@fiIcs6Q|l z=&i*40ccRfzcfmSTY3QhgD3uegojc1F;B`e$fYE8@s4|egyYyO1CaI6zI;-E?I?qs zo9yVucCh}-&?1uJfn$$;ULNLVy?{U9N%2BeN>VRxAb`LPBy=c8O1q-J5Z@Ah1kG-L z(f^ELSA&t1&Qsn&KSIC|6PPrdNrt2ae*s6tFCHmGSt8^g^vHNQws^rfDrZU0b<(8* z$sA*6_Ov2Ra?mQ!9VOz>5e6Kv6t%11eQ7JO7{xX#iW%srUhh}JgwUf^d37xI^tDSt#Pv}$dk0kp}@IIPqw@<_mMUR*f0_%BC zXf_~=ZT5bm#d0Y>B0d9RyGZQMp3ozJG(snx;?x%M4pOYGP7R@1U|oQ}q%*uz(!y=P zP2S=%V!h}Iy{pB}@~+lm%7#3M4H*ovmpq|$fPbLy4yU%z|i zfR%>cE$O-l){vn>wBRr4dM9h}2_XNGSU1_KrL65-K?eU>3;fP0AO`nJ$95I|l5URV z)qws0@ou&8#NfRk(R@AWb~`zg3{o+X@9>%yv8 z*>boy4ZWrL4?6j>Wu^{S@gH*XWy>tr{D+-<*)ksnKX4S%f85EJ>GuGS`A1|u>kX`rS z??9iF3Ep39JQ11+(oQ13>nJ7>x>&Q`bCj?M{S~YuvJh)uM6BT;Gwc0`SnI&rgCRZX zL#O%D(n9^C zX%e9>n0&I5fzkMEy$Dr7=wAFK4Q?|fHLG|;OAgt;q`lA*Ml0<{v>nG*a;voX(EW>x z&@Eary=_UG(|^+9M>@s14Eaaa%F;2);-lJ@v{}4Yi)Zd%T!fB1S}7jUwxrGCbG7)` zw#8*#>5BEht2De%^ih1NavJug zmR#SaqzGkAQE6^;3fT-h9YP=AFX;rQkj=1vX`xL{A*W%p^Od^IZAyyJwOZ(8r;yFC zpCHtqTICkr>a?1<8VP1hRs7SOe6t@>l9y`9ZTpwBj~QR+IHx<~P>x0D=xHkTGn{-Y zbt!p9*7E5LIU{3YE_eXQDZ;qEv6T#!E|)?>PWbfoVr~BsMgc$ykK6l2c(3~$E zaVFjij+l!$Ut3ua>0LlMhN!>jEX!Iku@pqw_e}D1F|!nOw7rS#H#r!>If(QW#;b#e|`~EkFxP;S`n1|Rp2pyjFT_*zYPAc z1&BYv!50%JfXuAv4wf?QKFy!&xJK$P`-9-PdZ@{ex z2gR@pu0m?Z@Je+O9U|KbTy+kF$}{7AF3IPX6?`06JxkL>`?uGLo+J42}=VlSoM?(K!j%pxzCXm%(KqUyZ+{lkMbu z?kGjxG*(0z=rNKP z#Yv&EFrgnQiwixi#V(DzLyIYG zy-+jWAhDHkgR}a}xpng8cH_T7vSQLe7*-3TxGFUwAoAuUkXNtpkEPm0Pag2jsUW%J ztNcKR^`zjT>gvh;e!=IgetzRm?!={`S z1z9jO1=`rjpZm$FHVfkKhav3b>;0h#@FI)--A_(svAxpC4+F51Z}Nx!3^8`{7k+Z8 zUE9kLV<+F>53PU~S?o(cIn}Q1e;~$A{+)mD-yuc|nB;%jjeiFcQJkDlCx%=i?-RJ% zNZ#WcMtABb-%cZgF(@5zaJIz205uSQ$#?qB*Xd>uE4Uk|h^Mwm4|$TW^Tm$=oi+=T zZ}5f2>3Dbho(qrn2FIhbZ#4KNU+;?_k9f2pnS7%!v>EVP3g2s`$EF0QKq|Ne>u@pn zIS711LOg{~wg$;QmJ*?#!KAI-q@)p-rW)(so8-BV=q^yM^Bl#L{`c(+9*-v}vu@@2y9z*uh^Dq&=93wqD z=|->lTq*fFZ~SARb27Jj)9*=C8lH*N5ENL#i})`g{vE`UfA5`$TPLIo{!Cq_OpH>N zDW(^I%?|j{OD2%WD%c~^PXprv{3ZY5eag-V#r0C@m-$o<+*TIY06Cz9{znT|wJFFW zOX!Rg9C5^7@~J*$gPT=IOG5wB?9(hZMO5sfLzSUt`jnwzv7guM9TwY)&Hc!=a8-Qm zI8|JD<0@qz7uO%%W$U$T<3#G3H@qA*OYsMx4k-i(mKM`Z6={$-dqfLJu96CLgbb*d$ zPBG*2=HeV%Ie0Cu__9K1sX(|H9q&sRgNG}erg*TJGLql0HE0^YA{(C(PlFaWzPH?>fR3`vS_F%e7sU5lhJZe&Jfjf^HS*-8 zc(1|l8cI^h$IwC7K}#BuR&XVLMfyn~(?jdzMe)@{4rd>6HN{$nll**i&_&6~=wjxb zV9`;SB zcoFzCwMzTN9+yN#=mzlUaA2Ct-K^uTrvM)Ff%V2lu-xu36O>xX8a#EAVtM$Kwn~l6 zmR)i5Ro3jy7ekG#Du(fw@u(~82-IWp+B5Ls?$iaIIk)BGRDKU%Fr|;LzhU(IN0U`; zGYzDXdNpOy81xVrMz9vYBDXuyuhGg$on(s)j&TFzT`Iz36d|iGB2clW-tsi5aj@qu z1e<)8dYg?!l1ky7HuAHq@Eoo1E{8(N{v+6)7ASl~v9~EQUjb=aLVAFsgvfj|_Cmrlf=tD$Sx3wzmryOiAY!+E_HT!7?n@<2|oub))bg*fT zF0$^`?7a^5UEqqW&o%oQ2b&tJ$m+LFnf50Kn_h(!Sw))ttb^T+Ex$supL4K#GW!k9 ze%`_E&Fr47D(ycz*lElz((D%;Y?`x(tkX67MF*SfYt|cJ$FE0qc*ViaoPyXqjiK>9 z^|GuUkeW@BuR0`$z>FZDMb9b%e>=&%7A_;Q&IR=u;=FM_6r<9rYA1YOkCh`->Xlh9 zK%g%=nbbGKqX#LD$QlS1v)^*C{|a_+!l7jF&(KC=T`N0ct%$ZJnT%+!bc zzXzl`n))I7O#dCgD2ddM&|@A=GO3@C#m^z9M(`za`{?^X<#zk&+e8iLW^$a*K{e^5 zPh@;Yy8dpFMd2@?(DW?zD>Z3CjD4gL;staB{-DSN^MSLlygY_%mO)le)E`T=}?L8RWLk2kCG z_Q7@$8np&<3;ZRlanVY^N+wA0gG3XdwGg{FNeZH*2JfNZC-G{NKT!R1TRl>+yJq%UsHA0LtC4 zoHLp|d%u&+Zn@vdS+AgXPOn7tvmMb1l`*q&!EYj&9cndb=$vf?^B0s={ka`0KaJA(S-*)J?>>(8)O!)JKVPQFxL;n@^qw-%|~l zOT%LaPe7@Ntaz|y5c{%lw#b?f>Y2p3GMu9#o(I+o#NO>l`(m>^GF~G-O+{VGNs@An zMG0OFX_5621Zl|XckK;S+GYgFp4+cRHjsYT*;phgvX+6h3xEA?q&zcQRhY{_eVsTr zIXQ2GntUuczjJWr_5t-6;{4v05KrRfE(iHCV%=U(m-8KoI4zxxuu|9MjSbnmQ`6=a}~(%5$mpK)`D~jqN_bACe#4W=W)*cj>e6jy9lke*rCWJOUfxQ!(7w348=W5XoYfqNH8JfkG&eOa4CkjSFwVTjPn zP5quzX3=Z3b}_556uB{OO~7Bj=atGs9I~u3N~iCeHW#%P-QJ?;Dg4ORch~2E6UW|7-O4 zyTH4j_#Z@#KZ&e(0(`+YOPvTy4}GvN)!fz%*!xWTLk@*$^6GyYgmaPUwC7v{Ch$SB z=Rrx^>wgm<3lrb8=kemNIasDW=6??i+M-DNvunVr%uh)AW$A&DG*6a8hNQn@r{{qy znHe1Eb54zGz5ZHph{txS=#GrGj zID?nsmwyC53#d)S)Q2`a8xatBd7H7@hP(Q5mM>6Cv#&t;2yq6S?nZ5or&|9#NCURI zhjlp#aJ$^6`Wq|%62Bsz_J|H6-f2qaEV~RT6QLpCQ$-zcj{9E}75@fF9de3Y3jSrJ z;{pd?gsua5AF*~tus+wU3mq)^PIV}BGA{1L-++tUdTl!B2TDH5N?N3}AfLv72aHLl zz!`gi@YBAjFH`6p`uPYyV!>VbH3rle3wI|1#lL@1(czzQOv05|)LbD%>ZdNbz>l9> zgs0lDAoXk4F?GFhU&$_$drT7606hr*%tbZem^Hu?Z(y7@Nl>XbED7)oatWnJQAx0Z|Q^hd0R!$5h1W- zcNBWUeJPwj?Zk{t37E-zLR`b&(dKe<+V$AYm` zMBygPb}btTJ>9@t=}pEj{zW!%xzXQ9C6<`z%AuD|sngF{yHvH0*T6cY1qIa$o6*6* zJ3y&wC8hvPDW!(|?F{lqq(iH;1E{kBdxdcw^?Ft1T%n?GaK_o}2WduTl{F8x@2&F@EE~!4! zDaoo$aS>VsspCm9_dt?cAaylKj(3r1w$$0u(C;)q->E4_TPH%#L+Eo-GBdi8Z!~}2 zev~AgDtMKixX7tQb}^Znzk-{a%~EQ8^6hT22|FT?q)mauG8aX)Nm8EFra+Sxs&T3_ z{m7DhYDt7Hg!rBKOI+)s61Q}UA@_s)7hT6!Vou+2vHaV*VR^>ymiiA(qN_k+k2}`9-T5gM0%Cm5roK5LP z5Wbg`Zq-V8LT(f0l3DermOD+l(iP-sxlPVdLaRPTj4oTDd7CUDH50MLuu5uG0t8w3 zbgiD}={DhLC1&;wBJX%KNh7hqNIMMyH%c$Dj>-@B^~BTVQ)hviSY@Q0r@8f#8%(=M zQ|Ntp+!vH~g(fsg5m(wZnzDu|scAQ9LK72QX}4)YGZXx2_h>>36DFiRqzP-8fOMYF zl#P<&PkTmFu4PKsv==mi7R$!w8N(=-iS&2}{fD2bC}uOG;W~cCN++=P_CU`=0sV}% zOC_`aDUUYrXj#I%e~Ogji8bI-c>>{lAn|m�OVnFuvRnDVH$2))@QNGVWMK%H2#^ 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 diff --git a/benchmarks/hope/pisum_opt_d21ddf605a07e3a4d8a460735cef49c69a69c967957f695741c0feae_0.out b/benchmarks/hope/pisum_opt_d21ddf605a07e3a4d8a460735cef49c69a69c967957f695741c0feae_0.out deleted file mode 100644 index 46219fd..0000000 --- a/benchmarks/hope/pisum_opt_d21ddf605a07e3a4d8a460735cef49c69a69c967957f695741c0feae_0.out +++ /dev/null @@ -1,8 +0,0 @@ -running build_ext -building 'pisum_opt_d21ddf605a07e3a4d8a460735cef49c69a69c967957f695741c0feae_0' extension -C compiler: /usr/bin/clang -fno-strict-aliasing -Wsign-compare -Wunreachable-code -fno-common -dynamic -DNDEBUG -g -fwrapv -O3 -Wall -arch i386 -arch x86_64 -g - -compile options: '-I/Users/uweschmitt/Projects/hope/venv3/lib/python3.5/site-packages/numpy/core/include -I/Users/uweschmitt/Projects/hope/venv3/include -I/Library/Frameworks/Python.framework/Versions/3.5/include/python3.5m -c' -extra options: '-Wall -Wno-unused-variable -march=native -stdlib=libc++ -std=c++11 -Wno-unreachable-code' -clang: /var/folders/k8/zfp7dvcs1m326gz1brql1tv80000gn/T/hopehpwfl_f0/pisum_opt_d21ddf605a07e3a4d8a460735cef49c69a69c967957f695741c0feae_0.cpp -/usr/bin/clang++ -bundle -undefined dynamic_lookup -arch i386 -arch x86_64 -g /var/folders/k8/zfp7dvcs1m326gz1brql1tv80000gn/T/hopehpwfl_f0/pisum_opt_d21ddf605a07e3a4d8a460735cef49c69a69c967957f695741c0feae_0.o -o /var/folders/k8/zfp7dvcs1m326gz1brql1tv80000gn/T/hopehpwfl_f0/pisum_opt_d21ddf605a07e3a4d8a460735cef49c69a69c967957f695741c0feae_0.cpython-35m-darwin.so diff --git a/benchmarks/hope/qsort_kernel_674d9af4cba0defec637089ed771c5f43e2e28f5e25a2c4f0ec12559.pck b/benchmarks/hope/qsort_kernel_674d9af4cba0defec637089ed771c5f43e2e28f5e25a2c4f0ec12559.pck deleted file mode 100644 index 5041f546985a277a4a0f285b4c2e6efb79606125..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 504 zcmYjO$!@|h5T#`;g|5)`2ar&rBoG1z&iw>@sUpXokch;|!~vm7NIk+|*YSaJur$k? zH}8#q>j$ijNUv6_t)cYUejg@uE%Ay)Sq^5FmvJU!_p;Z;Ytm8pFit?w6tGUdREjby zN`C`|fW)79Q@)_#lm*n{5CWS`JZruH?s+zI!|4PZaOR;4j!PXj4J}}{A@)F%A` zD5pUJ$|>HS^sck%-Z%C~n$%d)B*8l}P@Kj}WX4u(C2(U$i@2J`d(y93DJ@vE6ts+Y zfDh!Zs>zZkaj;a6jmP+?bydZsN^KjT$nc+3sbNt?Xt0a}K7$st0mw^8a~vE=LxEEo u3mj_wmsiFYjjsMEjeh`NkEEs0c>!U($2UD%-4Wk)oheJe@#BD>Tk{XWw~<`{ diff --git a/benchmarks/hope/qsort_kernel_674d9af4cba0defec637089ed771c5f43e2e28f5e25a2c4f0ec12559_0.cpp b/benchmarks/hope/qsort_kernel_674d9af4cba0defec637089ed771c5f43e2e28f5e25a2c4f0ec12559_0.cpp deleted file mode 100644 index f7656e3..0000000 --- a/benchmarks/hope/qsort_kernel_674d9af4cba0defec637089ed771c5f43e2e28f5e25a2c4f0ec12559_0.cpp +++ /dev/null @@ -1,205 +0,0 @@ - -#define PY_ARRAY_UNIQUE_SYMBOL fkt_ARRAY_API -#define NPY_NO_DEPRECATED_API NPY_1_7_API_VERSION -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - - -struct PyObj { - typedef PyObject * ptr_t; - typedef PyArrayObject * arrptr_t; - PyObj(): dec(false), ptr(NULL) {} - PyObj(ptr_t p): dec(false), ptr(p) {} - ~PyObj() { if(dec) Py_DECREF(ptr); } - PyObj & operator=(ptr_t p) { if(dec) Py_DECREF(ptr); ptr = p; dec = false; return *this; } - PyObj & incref(ptr_t p) { if(dec) Py_DECREF(ptr); ptr = p; dec = (p != NULL); return *this; } - operator bool() const { return ptr; } - operator ptr_t() const { return ptr; } - operator arrptr_t() const { return (arrptr_t)ptr; } - bool dec; - ptr_t ptr; -}; - -inline std::tuple qsort_kernel_d1JJ( - PyObject * pa, npy_intp const * __restrict__ sa, npy_double * __restrict__ ca - , npy_int64 clo - , npy_int64 chi -); -inline std::tuple qsort_kernel_d1JJ( - PyObject * pa, npy_intp const * __restrict__ sa, npy_double * __restrict__ ca - , npy_int64 clo - , npy_int64 chi -) { - npy_int64 ci = npy_int64(); - ci = clo; - npy_int64 cj = npy_int64(); - cj = chi; - if (false) { - return std::make_tuple((PyObject *)pa, sa, ca); - } - while ((npy_bool)(ci < chi)) { - npy_double cpivot = npy_double(); - cpivot = ca[(int)((npy_int64)((npy_int64)((npy_int64)clo + (npy_int64)chi) / (npy_int64)2))]; - while ((npy_bool)(ci <= cj)) { - while ((npy_bool)(ca[(int)(ci)] < cpivot)) { - ci += (npy_int64)1; - } - while ((npy_bool)(ca[(int)(cj)] > cpivot)) { - cj -= (npy_int64)1; - } - if ((npy_bool)(ci <= cj)) { - npy_double ctmp = npy_double(); - ctmp = ca[(int)(ci)]; - ca[(int)(ci)] = ca[(int)(cj)]; - ca[(int)(cj)] = ctmp; - ci += (npy_int64)1; - cj -= (npy_int64)1; - } - } - if ((npy_bool)(clo < cj)) { - qsort_kernel_d1JJ(pa, sa, ca, clo, cj); - } - clo = ci; - cj = chi; - } - return std::make_tuple((PyObject *)pa, sa, ca); - PyErr_SetString(PyExc_ValueError, "No return type passed!"); - throw std::exception(); -} - -#include -#include -#include -#include -#include -#include - -void sighandler(int sig); - -void sighandler(int sig) { - std::ostringstream buffer; - buffer << "Abort by " << (sig == SIGSEGV ? "segfault" : "bus error") << std::endl; - void * stack[64]; - std::size_t depth = backtrace(stack, 64); - if (!depth) - buffer << " " << std::endl; - else { - char ** symbols = backtrace_symbols(stack, depth); - for (std::size_t i = 1; i < depth; ++i) { - std::string symbol = symbols[i]; - if (symbol.find_first_of(' ', 59) != std::string::npos) { - std::string name = symbol.substr(59, symbol.find_first_of(' ', 59) - 59); - int status; - char * demangled = abi::__cxa_demangle(name.c_str(), NULL, NULL, &status); - if (!status) { - buffer << " " - << symbol.substr(0, 59) - << demangled - << symbol.substr(59 + name.size()) - << std::endl; - free(demangled); - } else - buffer << " " << symbol << std::endl; - } else - buffer << " " << symbol << std::endl; - } - free(symbols); - } - std::cerr << buffer.str(); - std::exit(EXIT_FAILURE); - } - - -extern "C" { - - PyObject * create_signature; - - struct sigaction slot; - - PyObject * set_create_signature(PyObject * self, PyObject * args) { - if (!PyArg_ParseTuple(args, "O", &create_signature)) { - PyErr_SetString(PyExc_ValueError, "Invalid Argument to set_create_signature!"); - return NULL; - } - Py_INCREF(create_signature); - memset(&slot, 0, sizeof(slot)); - slot.sa_handler = &sighandler; - sigaction(SIGSEGV, &slot, NULL); - sigaction(SIGBUS, &slot, NULL); - Py_INCREF(Py_None); - return Py_None; - } - - PyObject * run(PyObject * self, PyObject * args) { - { - PyObj pa; - PyObject * plo; npy_int64 clo; - PyObject * phi; npy_int64 chi; - if ( - PyTuple_CheckExact(args) and PyTuple_GET_SIZE(args) == 3 - and (pa = PyTuple_GET_ITEM(args, 0)) and PyArray_CheckExact(pa) - and PyArray_TYPE((PyArrayObject *)pa) == NPY_FLOAT64 and PyArray_NDIM((PyArrayObject *)pa) == 1 - and (plo = PyTuple_GET_ITEM(args, 1)) and PyLong_CheckExact(plo) - and (phi = PyTuple_GET_ITEM(args, 2)) and PyLong_CheckExact(phi) - ) { - if (!(pa.incref((PyObject *)PyArray_GETCONTIGUOUS((PyArrayObject *)pa)))) { - PyErr_SetString(PyExc_ValueError, "Invalid Argument type on a!"); - return NULL; - } - clo = PyLong_AS_LONG(plo); - chi = PyLong_AS_LONG(phi); - try { - PyObject * res = std::get<0>(qsort_kernel_d1JJ( - pa, PyArray_SHAPE((PyArrayObject *)pa), (npy_double *)PyArray_DATA((PyArrayObject *)pa) - , clo - , chi - )); - - Py_INCREF(res); - return res; - } catch (...) { - return NULL; - } - } else - PyErr_Clear(); - } - PyObject * signatures = Py_BuildValue("(sO)", "gANdcQBdcQEoY2hvcGUuX2FzdApWYXJpYWJsZQpxAimBcQN9cQQoWAQAAABuYW1lcQVYAQAAAGFx\nBlgFAAAAZHR5cGVxB1gHAAAAZmxvYXQ2NHEIWAQAAABkaW1zcQlLAXViaAIpgXEKfXELKGgFWAIA\nAABsb3EMaAdjYnVpbHRpbnMKaW50CnENaAlLAHViaAIpgXEOfXEPKGgFWAIAAABoaXEQaAdoDWgJ\nSwB1YmVhLg==\n", args); - if (!signatures) { - PyErr_SetString(PyExc_ValueError, "Error building signature string for qsort_kernel"); - return NULL; - } - return PyObject_Call(create_signature, signatures, NULL); - } - - PyMethodDef qsort_kernelMethods[] = { - { "set_create_signature", set_create_signature, METH_VARARGS, "signal handler" }, - { "run", run, METH_VARARGS, "module function" }, - { NULL, NULL, 0, NULL } - }; - - - static struct PyModuleDef qsort_kernelmodule = { - PyModuleDef_HEAD_INIT, - "qsort_kernel", - NULL, - -1, - qsort_kernelMethods - }; - - - PyMODINIT_FUNC PyInit_qsort_kernel_674d9af4cba0defec637089ed771c5f43e2e28f5e25a2c4f0ec12559_0(void) { - import_array(); - PyImport_ImportModule("numpy"); - return PyModule_Create(&qsort_kernelmodule); - } - -} diff --git a/benchmarks/hope/qsort_kernel_674d9af4cba0defec637089ed771c5f43e2e28f5e25a2c4f0ec12559_0.o b/benchmarks/hope/qsort_kernel_674d9af4cba0defec637089ed771c5f43e2e28f5e25a2c4f0ec12559_0.o deleted file mode 100644 index b60c6f64e3362ceb4048834f834d478461c514ae..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 383220 zcmeFa3w&HvwLg9)uTDFmNuWT*3IQs#QXrEiZ36{K+i9CNP17W83Q~s2WReV>WTx{- z8iZO%3yfnFQ4mEGuX@GD^@;dGZVN&Yt^&eU6z>%gHMLbMYI!OBeZOm;edf$$(k7YU z{ro?F($3j?t-bczYp=cb^X$FPqpyGWe#0;d@SBETp<$E*+IJKDr4EfNcn-Li|3(~s zZ~nchfj2eqrUu^Bz?&L)Qv+{m;7tv@sev~&@TLad)WDk>cvA!aAJf2(U;X3j=EUE? zeBUU-@1_zwDh+|l>Cf@^RbPSe8HC*dm+|>K$)xjoD%SugZEIZpUWqxnU{7&^r; z{`Rus@YPs>(E%OfZxjz-M@KZ!&%9*H@f23T=FKpSffMYdnVUzjuPe-uY#y(jn>(^891Vfrd!fWXn;uTIJ;%O-JJq2?zV)fhZM=Q28`Z(BFV0;A) z+0Csg*}F_0h;PZkvPYAN%8W{ZKL*Uk?;vO+a2MQDU|fEJs_V^w`A1#wcRGHk)pE=>+E0XVd$GP#?f z$!q2oC1=eYxgY+0o^LbWKF@ss5Mz^$@q>SYFdgp^f-3gXxg)#C<>l1-TR=Uu*O?sN zBh*avp_kkDJoIwK?#todw>yL<6|>Lt1C`7kDoI`PH^(7idhHWT?|Z-x9V|-iCZp8OmB>$W_cLa~ zB={HzQqEMv9F=|%%lF8WH$BL(9SAGC|3R`#*32CVGYz=iaEHISL_v_b=n~4jf$+ZF zN6L2HfKr!ygZ4sVcRz;&hmI5^zyCBmW!HbW$S}U=KqMvB@XnX1kKMmSV3+)zS9aPi z1e1VKQ#H5m+r0psh}yS%p5?1DeU$1+5JFOCG7gP?lQhJBkJvk3;Il^bdm&js=1RCD zcLG23GDL2JG`R?_A;F%9z_T2@ONICD+2pNCi`+)qL3CKKj@0f-1P%|s1<$e-dxwW- zk+u;r6+Or$b-E*`QL2ZBzE2vXY}Z|oAtg$X^0ONS^ks}3`UiO4JItKj^ytp#9jR|J z`@?t6$Fpq3U!UB+>nCNNVXBE1K~a(D$afJd(=a?E#Qz)cVi?*Oh86z3z~{0DGk7b7 zNSnmv(ke_D8-8U@ei5$~lP_>;b4=Mao)AVF)HgX0NzE^^8+Pm}Dj?}H? zQkv7HG@nZT^~sT4KP>a?E@gb0H7h&nz(#W60&IdOjf`;3@XG_sRWt zJ75oU5PxXzh1p`g%wkb(2Wmqp+M6RIA0UG}pFjwhkiZno5W!v{e#KL!SpQ&rEfy5k zDv8_MAJ20vmJ5N;Di*DZ(+luAyuOKy~@<+JCA{u%BN+46gBToWK?WXh$ zdr-p6nmY1LLROpbIZ{fSF!Z3~!aG%)bPLKuwKj~tOQJ7jv&>Tb{s`GZ`yt?w9q5M` z(a-R$`{6C~+%;p6I9Ts>FK@8B19-pKvCmZQjsip#fqyzx{?Io1XP!oIOJw;L^x_KP=If8~&#|^^A0DcFYmdHM`9HXynL72^Amy<7ab0OUQ zU#B^k1)JK_Dw?v%hgyaHF@%`A_r#;A z8yJ4X*muKPxcu99!z#Dz{RYpE)gwTV? ztQyyj9GR9X8QOCsSyHxp1q?a0vM6=QY*_t~C-&E0`YiDer>0SiX^H75ePE9?&qk5A zB8dx!YL^xm@pq+$>fyquQm5K<^a(_^a`01V3Na{t^X&bP5E{D3jPyOExc~Elsxwj3 z!Ds(%)UJ#xrC3JRQJSGoY(!E@F_1{JOr?m2QEfU>aS=xOj6GvLuFwK1cDHcQu-odiApxQPiiTvsiD-;`b0XaxWg4nbFn!;|O83 znjve3!Okau|K^2{VD`bZ~}{G1kVwaD&>&)U5wQJ0qEMMONXczEb}vkcEiq~W12 z)8?fGF8Vv-jl2ce{a4d`tTKA5ty;YL7X)8`VB^Rg&tJdODEw8yB50@-zh6wlgR6UN zW&W1{_cL)C`2vCe#*fqYM;W{tJy12=#~5-wfCe3MKAN?Y08}vKMgV8PeH~L6<39oX z8{7i~e2{fM+%rgaD*$9lh3o;a4S>XP!w^Vz5y=7oNG2gS184#u+)9eE4g%nElHCd5 z9JsRyJOY3WLInN+fD9z$8i?^$0Fs~Y0l=D$aUaQk1%L{X5b|epA%E{g-s^S9P5@^C zSUw$(F9RTJiSrPEApr7so|VGs3+)Q!)Q)gXT6N(kos0H|aM;ZCvR0aTJ~z6pGV zz=!Z4gFE3mZpULD0QuWvhU{Yqe)v0b$G`1F*`9JG{O`nX1A{&Z&tClGPt#pnm|%Jo z;CK=>!gC^iGYREImj*WG2jg@#%A@a8#0ENCBu7g^X zMGpqSkG~^#%r9gD#{2o;A3ew6_fGs)-1}nUa@M) zu(v36!>zy#?Qteo6%DVN%l5AD&uB@|gTMACRE!%wD8VxDLGv~|uw>*nz@rTqc^2-_ z4@#x_mYrtk^cqte=cW>sscYw?w#=^kYuShP)cvgN&b{%1n;zM8Yg zZF#zE_vewL*RNG9G&6yuAY>$t}+$(V1-gWoq4X zm{M8&ck)p3(bN{w&w6TT?~$RO9s&Im10!{>mfiATo#%zJPws=vukCs^eqw6N3&|%j zUOdenUnH%V_3}fnRg^xFy7rf;Ezi_FUUu6aWcd>hASPrj+ue@Vqi)?3R6^qEvO7(D zq7Hr995jTpQ=X^Mq|Qoto`8c+Zx)-{l!wiq6GJ38Mds-GPEMxK^(89nUV>_%3gY0n?61gq$s2G@sR+NGxZTq>*GHUs5HL?H+7>gEmJCSh_ZZ=;n2rN zOu7%i)p{vAZ6|9i^f7eMDV29~YMnE+^O=Pz~Ddmi{qC~%_eyQ zVwultk9X-I46}TP3@SSx?(}yYq8?wjs?lr zhMu2Yw(ChqmRdJ^=oLp|`p~OD>;WRV<&gW?@4~||#`2+83gRRv+x;0}QtRd%-s?=9 zn0)P=D`p)yU51wB!%dY#dmRT{sVhp)xxxvzX|2X*<3?wg194!0LgBPwFDZBF_)Vl+_o`vkmr{eRG z^w6)JGLAmzS~n7J9=i4gBVK}Ww-GNDxys{9cOOYCdFUTlSN%uDC0Fus2_Kbs82fha zLIzNSkL*Jap?oq8c-NMPULHVY!Z?&HQyUM)kp-vY$z)S$@``ekwKLT;i>TRSuup!Q z?2|W>^?}c&$Db*b-tOChL^V!Tq`rSMy?1^XPc#P{9IH_=XG<~?lYZhzOF3GHsPh?VD&g^|MnO7Sug}QmkK)eBj&6&*AziXZ)npw$jvKdEL(tvY|Zrv%1$~g=P1a zaF$bc+9g(dj)o+gM{3ZCxWcLKrC4KXODT$O=(Qv9Gr_n#@s6^4uQ-D81HSCuXD{4x zFfo7VVYEx7Nzb7Js=RC$YXnGA zt7auV&!tu^NO}$quc~B^pay~nHb%-vjvV=nf?h9XLKU4Q7KydKqSOK2kIEg9V~e zHUeiu@{u#}IPlca!O|3$3W2R4wiFM0|EAl6QraVC>pLo`2{!W2{|+@`^*g~U(pA8ssj47WQDI8&SErJCl_{ud-SIg^`8lTGD!m&Ze8_nvn6K}Vwe z$pgbHPIDwzE)cyRn3cL>ZffN`$1}jBR#v7}j|8nbY!C^T-8&6>o;I|PYA#8woSWJ) zFZqgNAL?1MX;yN_oWm=t3gg9&p97I@QCCQND)yx2>sO$av-PfJ;JowN5u8Yf!52OkxY-vGiMM3${BgIlu=pvSY#vDJKOL;sP z38*Q4^1%Z$@jFpX%FEMq#iUC(**U%Xf<#Ffg^|Cp)A|G8k>BER;NIlJBfo@)*r(~j zi2e(1F8e`G;&kuXeYkAbuc+FMvoR!rJny1LZ;llhOQwv?AOy*mHF-Or-CnTh62 z$_1Q{Y^bVxrfhc-v34KX(f(e~oP8cn0Pw;~y19LxLxhx0oLt=*-ZDbDmqC)lo)O2e zXMdT;b0{@4)lel132O3svumJM<(QoY2S+z|59pI>lH|UT#Yp+U=2X;~y1G1Pq*vzuW$2YdW!K+_6d7gRq2z-j4#Y~WJG5Vr!fojF z8OePEWy3{_l8KRUh!d065-wE8)FJVgFf(KJ?0r-E9i!c4e@hZglY1xfGA-s2gABp$wzk_|$#utWL zc4F;p?;oaNBH})L_<_QB`_R6f^MAGX&uqAceqZ>~uiS^T#-9)Uqr7M#9*qkz_$Y5% zh;c~yE>^{t9-o8Vjg?B@AGk!hAGuPwpWG>K z#gGcGNU89O52^5qJ5+f2jdz*x_n7gYGvgadZ4B zu2imfCo(mp0+G04_8}Em@h$|GueuA5^?UGWe-4k+K))QQinTyhv;tM}*6SWLOOB=Z zfC0%=`C(#E_ZajZpX2zkV{cj??>!0S_pp@TONkd9&tRDPFbeJwa|EnL==B}pXuPsx zu+oSHy1RUdP~7N9#3}>PXgF$ARxSUw%0e*&p-;X zj)k6ezWUCqF74YC=~&$o>F8Ux*4JKJwY<;McpnRN=skRK6fcrb|G|I48`T$8i@7>aA~Xuz4%{!f{BM=*aH4| z7D>~_#zvTy?Ca73bBmV9hYvAeOdepy3hKlXhg6O~ol-@YBnf|d0k1&s}NRZ8wE zfg{D(tePjxY9)4x~NT3&diTzIY`{FdoDnvy7s@?o7jI#>yyKV&gXWKZ{=_=4XUI4}bO_ z@!5ajw<`=n*FyV8v*zc(DG*w;@FSBO7jOt{>B?$26H;n6u4vYYif-z^m^Uy zx(;6~==X+IwZb#t>uvBiwIayh=y5}3`_xqyfcZEZpu>SM6 zE;;||uE^rfet*o}yRdpu_tox>=(dnM-oK;@f8BlOx1HY;jszAZwq3ojW1#PRyXklr zEw1UT^L5quJA750fv$jm(Za=5OX>ohix<27wOus}1J!}*C0(_F>RMm5zox4y;CENo z*4BBe&htki#`zm#lp!$?i1~YZVbA9`N5ibpF-i0MjzAyqzUbDN(X};hYbUb10i$_v zz1b3&Ekd}%8x3^%u_?TH(2Gvy>VP+HXqUUBqu8ox1%$$A6VYB6h{nC7hCI`(P$~g1 z+T@Fx$$*YLjd(=1;~Fe%0#bZBBp46$YJX=y3g74qpllO<*Z|2RXpG~AJlUcegjO~* zdaw^Qrrd~;=wmaCN-b%4qlS0D@OFiK-7zE9!?xAig@(=Wc62coYzAJ6s|56hI=Vy# zY*Gzx4Fc^Cg&7J-G8uP|u($UvyBYiGq|-3G3rg1+a|%%Z;dqyGZDA_@<8`xbeuq}bc zh>bw2>D8tZ+vW`g`nuyi1{|?0U~qy04ZQ&$>Fo4Ibavo_QN;YwU?gq;kkl=NN>vL8rait`kHLpz5v?x$Xgv_*N83jiO`omShjq0N z>-uQc_b^AJYyhkP=n2gThBT0OnRw7q_+vn=|>$nuFQAb^Qij-1|rm+E3k8C0~i zM{EhheXPoz!CnI+W0(M%Rv)?n_QPng4d_8NS}``!tkEH+9Swv6Xll)LG9ZxwSym0T zG@e#N&f1BfP82L0G$|Z$qAxK$616{wwm~!TBN@?LH0bY1=u z#nX+ew0}?vT}?&5G>6)w1A5K26!sv#6@f0J7l*2NA^|Rgq5I`4^|&86l{I+GjPwTB zl*VXM;v);>4-%j_fn61dw+77{k#uEnbYC z-e{PP-{_(r% zxfZ&2?e``K`B2By)Jy;?R8fk>$AU6hMwJSBF%j<*k3Wpo6o^Q$)4 z*B9H$2q-;!aux<8%IfT+P~CFbacW{kQF(AyvH5S^*vDMtvtP^8CaboIuSMmht2qbs-_oqZ2-5ei_K5JsJe2D`Dv zC*}`gG8pU%`Z;DCkR<4lNwT0Y1TgOPz4%rVF&)ks7lfvBG<@^xO7h{d~tfe?m7 zKq#M#C^WLOgVhT2AS}-qfp8Z=t^&~Q4u>Te9gTshL>~$VJVB?1SE!9fIDzVo*{L6D z&{^`*E}{WHTEcLjmu)d7SJGlbH=N;$i3|ig<2@vec1q&(sxzgC6;lDo4Lz8A(dh!S zKtJ>${Za>3qZDWaqR5+9f{b8q2SysoPlJF^CgfFyBIyVhiUjfav<;#ySSJ->jO}7% zUgSR98}u(lb+{NDUN^xfs=o z8GwnnIT)A(5;z4@* z4i00m!ht##D$yq!vcQ59nlT?wdUE6=mIppkz`YR}Rbc3anuMhT4l(+=mvVBq0IPGI z7hHgb;DQTms|ONxu{}^aE3PA`pi0`1of{7%LawD8{vyoMd$l_!T|;As+vBOO!eQ#3 zXn4RJ|FOFWN1@VO=!r8$4flpIWa0r>8&f;lkckg!G8M&P4(2bhKqss{7+soHjXkG% z>W#|Au=rTI4Fx-}T#+r37i*jUS7I%wtiG7GrIwSF8Khxb>13=qEoRRQoqAgQXw$`O z=yg)8mT<)o6OpvNj1hS)hG=p^T*p;V1_xEgG8il|V=`jWf+xsqL8WR`tTa@~acF9m;@J6KP>KFdGw{~adJXRibmVQa zRN@a6bvR9d*R|3X45!CkFg%I!KIBr6u!uy zDnl-sLTe`PanVjtf~;BwlR`~&FNVzqx}%t}h;n^>@rDlRFFhQvnhn<|nx*W@EK|2S z;IX;+`#aDsVRRg2zg{Ulp-d)p{X#!Hp#q&qZmRe)LX1(RHdc0YwK!IOz)$68Vj{aS z0khw_3}pA^0@Lip;8}jEgCyzTb@;$j<$yP(y-%;@u%K7cthLe zrHAh9I9|0kidnC}7GW&}Z!ypowqW$205g7O+n*J$I-_Zxw2wgS#33B^f*%?4>} z%OY89u9@NWxxaNGI#9iaHfD@PYDt$7xLC;5-7#rvuwK(i%3ijcn1pjh!z#5bQEGa_ z{Q-gS?^;av_hY=f2J zJrt%Icv0pZ2xDOY?$~2^bfIp}sD0@0+N!lr>!W(f(c(6+B30XGB;$Q!TPx#8Piw8W z@v3T%r&dOn$cDHgQhGd%cP_9Lj62OCx^0MMPkPiqaAK24b222g*qo^Ut257%kZv}e z*L1o6i-cJe3qXWU6ovALQCO;{IR9GZq~)P?{$FdSCN|)Ucv@PQV8shQ8Y&fp3YIlt zPnVl}@u;lP;2fMN;Od{|(BNt+Xved!urdk=7fVEt}c&ajDtfY{}c0puF*j zqM44x)wC5R)e-2%CXG?yW*u}d;=;W>BDU4ka30FK3+HHbwZa>O;vS81A+}I~4o}1D zi5NRFL{Im%lj;O~tsGz0daZ%~MYd`)S&r+;Fn_@~^vR#gRPbnx=Pc@crL#9be@f{jcsxi2ba3j?P8uH>ArN564?t)$(4_$YOwgqc7{&gdDP7E)|Fou zpYr4{m|x|RPTtHG`z`A-eR;F2#m+)0266Ldr+cHk%fXzE$|6u+&5?Z-JYT7nVBOp2 z55@;!w%SXTq7;r#@AaLi+sZ^{_v%V;s=;chxlFD0WdUykmDu^znq+v>&SxGNZk z7Bk>{+1JX>1D1{KK*+bWYvuR`*ojNw@-oEWb|_M>cfM;&zL^zv`N~x7i$f?btmkLM zicg+QB8^#N!*lW;9D?N(jOxH8?@=9iyg{S7Mc8G+CR~lH6_9UvX7KE_zZaR@2*eao z)&TRg5-r*kl&fZyv4ogc2H&6}u8SnFvy>ZmV2GAK-$Gsz33I<8m|`o6>CKmCZC4bp zvcOPWa?7jDwAkfoG&N>6W$#nILTP4s%A}XDRlS`eW>Ya0&Jb)WD!+OYGBuUo;+3zZ z>Re7m%XsaK3V9 z^Uc3D+c-`|ea>Vzh1t!&QdpwpHYt?)lyuL>P6qFn@*ZZ7E0rlz|Ck#4SCq<8nyb79 zoHgba1XYLfVzr28$>LGAeae@w=JhKj^|B)`*=l7QHdjjIh+5twV(ym#H~E}XzRcVU zamdMQZMOv5447KXEAMLeid1Wex{yU(K;*3(>}9u#*gR{- zGmfLNQ<0LD6`fK>c1&dcN<*GOeFsm4=?YO&BjVaQg&K(sMfjE4u~d%Nn9` z>$02~B4fFLB+qWr&U32d%YW&Tw#h36ItH=u$3NuOMC29!4yCZk2&p>T&UWD+VMtu~Wrv0Swl^g|NUH;RO=r8c5s(7{w(te%wV3Q10c*Mrs<|+l&kF>~F?7XRyV$RJkJ)pO#l< z8MYXl`r$?d4rokHGQi119VI#@{J81G9yJXfB^pvSuQRkq%|tP}ULDHHXI%mpX^F-l zt>rg%m^#%1J+u5&3FOXc7AXh&$|7VkAzqstCc`>hJ?T}&+QK5P4t9EYHKoECxX%u^ zx!?v}*e5Q$5ibjxcbC};ug(gziVY^x$@6IHZaiGVftzK4N-NS7014{xnA8q6mq-UH zhHhJ8=`b}#mQu#}J4fcI&+N=uI2*lnz1Je}LEQ#R>u^gmsyqoXFNk883frMo{V)IJew}I*?_od;4X_YsK#75EO;Fc~P zZ7o*5+_k(Y*gFu!9dWopDj4>37+DxHG|kwGw%EK(4*D>CIg&N^*XO$XYzb!BVovJj zH1Xz2r-oPKv0UKf3OA3o=a8_5m+K)Wot!Z{d}NGgu26SX`ieYIr2!HvW3_aVL7OYH z3o)MIsshr&ZGSQr=iD0?r$*6;$dzJ9eFG}iFrO7EuTOTBHEHhWsdw`~3TM&-G*%_E+T z%e1d`_4<}Jg-d%InoN}TG_G%2rHCvaJr=Mr16tX*zFsj{v3}z++>K6gNMQq)r-_!^ z(OG2cm#y(ESG-!=S{k$zO%2P}uTXq9t#4Q%39P7Z^RzXrQ&@UdcpBU4C1`bh<4W-~ zrIpgOo_@hDUtQlKo{bnct?<}Mz4+*aclr8t&Gl^!Iw$o_m#znyin2WIT?tLL)HkIO zYdxN31));#5*Bq^Dn|A=BQ1-AYFSn8P^FHRH#RhjAnh&n%F|Nc&}s_VywSU{X>HT` z_9m~VX+=YQlad|i#)h^wQ_IT%E^Al?bM3S4(ZFc4yN{n_F}5Xe;VG`BKyWX%_Mr1z zVrXBZ5%0x+)LvZufqPSN#V4GeFszASr*BYPTxtt&P;NKw^$wWmLDLP!aHS^+x_fzn zFMU_@CPsAvJFIR_#1q$o=6UsnjpewQ;kc&~0)5=Ela~7#<3YLq5qI^6q@tQ#j`;xz z@X38~NctkB@kWsI2726mI@^Tt#A&sANH!7zi5B*pwDNPY>us>no@-xe$VnZvQybn^ z$rtshZpfUd!C1u%mGdhKaOl7%!-Ru)CeEQYhH0!YpgYO@B+H@uny*6OR7kA=ntL-4`0$wA?2CjMedaM^;@D3}){o&#KzS;fty&&4 zp|M)S@#=UHzMh92B)sBR`6gl@{OU)b4}&yuCz4OKIy^O{q*rL|BcfyKO+{Ld>xw5L zwz=Fpp-A>+;G<^PwqC%+H`Ez(tf(P#EkJ`-tr%6)m`v8i$Q}=djhWdUU$o3IqdBq4 zuG(WIh_}>sU5zirP|VZ3vIP+_%<@>1)TK+)1vL>q5Y283(gd_F0^Y#HK@ zx#lJB5$*G*akVA2ifQikQmj75I=tx>3Vb-2hhpdlhp*NG&RPZxb0lhuF zTPDq!(^J}jlqk8ExY@*8$;!9CrLW7lrgyTOboZBe41TiQ7D5v+2Rx@Blfq0s ztCoFrE;>2(o%YUYvJz*^vC_@Jq}f^1BP_(^&(1#C$)DRX=krsP#L*}A=~)wO-W&&} z6E?>(90OXxldl!|aLZjY^5JNo@9DM0eE6BQZEBP^j@s5dxE>!r=DHky_*mr*g+ThX z;zO&{yx5BmB;z|wG=7D}ja#@47q7#tAcY{sT18@g?Z<*GVzeguHBP0;y7C)sXQ9vYakCYvT*q9Nj3}i$yvMKSYXZ1Pl=E1f^Bot7*@ljZQZ_EU^Q9|vi z=);$$%rS_0#>M)!js2!)Tca@Q!(X`gD)Y-#SQAgmp2@n?FiZxS9dqc8%c+*ap6V9l)p@NgC7$ z#*qYW_(921LS))7@4t^U&`qij0do< z5TAY~+KaDf8iA19-k;uS5=(TT$gyX{3rBqe8sAA#5@QE4-jiAhBH5MrjBE@S5@YX( zPInV*DjeO21%yF{avvmiOZQsgs{-)`z!HYV){L-LTNrm?af;n%#as?dD=KUgQQLq#=|IF!NJ#sAb%)G8&wv!5{TtM zs4M`$s9d&0*$J8z&Z3F?wgxgdv;iTZdLz|3?hs>PVJ8zlhVGNQef<0&wDLAZ2I~{s zSK#6wBkh&1d&_s9ZMc>A4z$V`Ucm?K)XRyGAoGH0e9PGv>ydg$4_a(~NgQo4zmlQl zRSlW0!ErWpORa_$Erc{4^0puCg=*mVl>t^(j&LOcKLi=dxOv}1qv`SoVoR$QRJ!@e z(FN%yd~D8|CvKhYVoaMb=?}=6b6jjsS1FOWp|Q6r-ODs~;BZFzRicf~)&w6bz8(W) zEb<&H5^r2W55(?moMP%24Vdn%#?L0~QYbF7434P;H|B$(tn6E}!ffHw#N*0qnq12~ zb}mQL+VfSNwx;w4GshIO+SA-=%TmVYA+xh&(drzHkNVgdvKVVpGe5NcTt-E_e5we0 z@-r$#!y1}uW3L-Oe7wu!S!0cNP;||m+HmoeBv?_l5#`5rJ-@v)wOqBH$v88kR?Ww^ zM*BLq8={4D>a_wljLN-cxX7yq5sPZX$99N6#b~T{iib})fAHg*r@F;T<2VR1OC!%# zE6wxh=}1eKRgdj)$5ErBnN=F^=W`UH*$BEl7PVI7c~i${mqySTLBRGyWCbaag-nXc2E1z^u3<_8$F%M`~pfJt*cdWFT< zQN}IVY;#DH{;s^VDU(bYTf9}1gl`ygb3EJ4H0PSeH(2W#DNME2N7sxAXgo*cQJT#`lV!IV`OCE12^7mHV@0>} z1{X|53V9Yx3^h24)dp{PD@R1(n7y?h*INwi-XaS%eL?5FL$gfxTod7AejOZRBDIs= zI$%DojsE&zxNU-mt^1f4TWmN{T_E3$AN{=8$U%%R?c%&ocbHmQfypT5<9PMg9L8D@ zR&u;8`b)LYUJP$&^j39x*}xm-76HOqI*rTThFzC~Hur)uXN$J2Deeix?BqToy+9_* zljfjd0z4vok*0Z^W81Sn5kbI2-aF`-=LFJU#0_2np88ZC!%tO;m zm59$`!*=jW4y%Cy9BhIg&A9F3n>ZWlt`$M>-m5wo^IYXOg7!^b8G+b^jBORo@`m2j zfEB(ND?nIm1P4WBm@o&@*|bX}3|B*D5(ANodT~4~nTPS1zN1pf$GgX%5cLTkrN|tz zAvHxcCLe3pQjXYWn5ihS_Uz2tc1-19phFc0KX3mUt-o3+M_7`$q)^qtDh&5R^QHxs zeu$MP12%R@k>_IVuIdcq>Kv``G;mDfG4*A#)VddzW2!%ccubZ{z(nsI)81SHrsLc{pD zfem&P7bt~$y*@t{`(jp$EX$W2K^z6fP#kXreCdl#B&aJK9q>gvGwE?yEURP58evR) zWSkx~UOZ|SC=cbW7Jqe@;Sb=AM`?N?m90h<~ z3G#q9N@{s_8KF)B@&pj>=1)BCDtg^jT?S&RM;DHp4e-~6PbK&9C!QYqAVX{$R`mJ6 zsas?MKk@X?hhS#4M?F=J0jP+~GXO>A?*JD*m{Ghrf%Ys9@`7{ z+3QwMFsqbcmA8v?Z67AItfty0NSwbW1`a^-S8^yZOey%*q$7m#RW1sa9usDnDxD|? zU=*FbD&CNkD4r-v^{iG#i)T0{kDd^VCnP3{Cyf&hObk!jWk}f~oN%{1+*Kj5zz_{D zL{pOo_|VisG%k4nAr_`&AVhmYnlL4%tV{{1V;{K~jtBzz#Ac8>tc8;J!@cv7`=6a~sPlrBwr&C*m}RdAtnafuh@1XuW&Y|KJI z?xj$WbyFlM1_;_0UxLQP+#<k(UK% zE-FZ3SYa0DbUd`#@~b)Z1vt z#}-;?MEU0V@ZNK(=lCa2V)x@4)Q^B3v*XIrd#oF=iH4= z4V$a0suprr6krqIm4!#uVsNO_bWI0^OLW3=VFT!FjhH-9?zor-eTz7l+==V!;A>ON zueM|OEf24UUc8nH^x?f!Y!El+^xD!F*uYt1U)0}YY?HUH>^nm8kSFphnTEO3+lxUP zUSxK_RIxdprUj)8;3au)2e2`sV}RiSgcqK@{>UI5-Xg~w5rorcx_*2!RY15T?u`hM za{4q#rGjdESF~5CdZI#QIx)OeM`)xYx|J?+BEcpa7-Hk$t&ECy^im7~91V!t6oL6M zF{EVvy8NLqCOlngZzLl1W5S8E(yF2CLbXE^80$0Pn06JuU(@ybyz;))+VGG@bR>eI zIJQ7&OuxyqKW6fR_>?rkPleCHg^MLVn$u?0N zwc0jHhCFT6#qE=PDv0|!(GFpoMyL7=F!W<|1z_!6N>iBgQDNRLOkDfsuBdNmZ@`Tp zo7-BO3_&3z8jvun8kh(+Z?pz{FdMZCmvcmLq(of6B0d`|$){F^=-bpXvrYo$-RL)w z!I+v_(&Y>^7*i9@U`)+XaU+P#NrN#p`3%O?I6tVg3wkwM4aU?YHyBg1+F;C_zd{jl zSh`h|CJf6*eZ=v5TZn~1Aa|x@hAB)Cn?YnQEFLBCfEIZ`%RHck9?((`Xt4(*X@>}+{Si>PYZWG)Qn*Mo0~=YH9@`ZLOeUNMAJ zW+~{>Kr83RN5s>%s}U5?j@Ph+rzIB<9b=c|2zU5w&CHpCF&@M$5H(#raVHM}xieCH z>@KrZqA`%nzkR!}BiQdY@Z`K#FR$g;OqzRfZ}cAO3a>v-jmGSUVJ5q&7)jX=olKeyGmQHj@w4l2QlDssNcK|`ljGsG^9aRctHh{d zT^?M)IQpz*8+L!=loQrCtR>T>OKl6(YDgk?lv*nrT#09q*f*!w;0oqYZj15cOu5b5 zR>iazd$Cf|85-9Dy4Q;-AZ*z1u2h$TYWtOYF6Ayp=G|Hy!-kNSwQcxTB9P|Ajvl!N zQ%LQ(Os~*#Wka*A#x<9rV23{v!6ll#5p0Ht;Re!(FBs)UEm8BN3Jlv=a7#9ig*9G< zZ7W$efZJrVQk#mD{#B`D*kbm116p9VVFH(RD1$A6`e=*UI!#K+XqJQvZqBE*+O8Kt z4e^gplA~HKKf*~inbM&$!KT}==huED@z~~Ve=#?DG0}FS)7s6Swo`XLo8MPtwm^BY zV&SJRqEbh3Y+_Ef!OaqJ(rhRmX7N3k_=T{H_U1^@M3uK;4GJ_Wioii-k4N^0s^Zmp7?T6D zc2o{FV;XZq$+1i@y#W>yM>DKXwyTIqkKR*p6!T#W*tWG1RrtEKRm;kacWWTphj*!+ z?lo&zJr|F{W~y^L&kVa4a%6yIBc71(4Pt0#2MxTg#Vp3}^+nLyaOZdfK0o5kV7zE? zO=q32tH$5qtLnt-d;g+^i>sE@1v(cmcKd6)Y8D2n1Jz5qY6I1^zG{C>S5?68uCA@E z^Hv#1zpcN@+XthaigEpl%&t7Ij-g6Ms8cGPVyQDa={_NYO|CoRFd9M0;!Beldg|(s zlg=%4-Mpuuh)0LaK`h4= zi#TTH8!#D8WPU&?_kkpNPlu_Kj3`GXriRC;`1o&Q%iVerl?^OL$;H)rni$5QKWpnt z?y(eyn>i;OsJ$8IO((Fi0wcPkvBZP~T{mUNi%-s5e12YR9t&(s1h6gU*h8?4p+By? z&dq0x{N@xbJ;`Za3mw~UBFG#yj&nQ;rRM)FAgGZ{59Ab~2Qs9b_Dc}PW%;fX0n?I=PDJ9>u=}vkckPkuENL60y_Vq$lL86t!79LK?6l+-deE80yhei;waouq(<_qqJD(ZLMP^ zBDb`(-q_W^@wnJjeA^L)toElsV4O@^g#KGTx2BOx-HYtzEaY6hjR)jYLFU|_{g}CZ zFMHH9u2ofY@HV-)8Dk>0@%Y4A)Hk5NLD+!v)t+Y5C7u?^3Ny#}?0#*zti#7{xOx`u z(s5fb4lpcib}V(89UYadjhc{Jr%2Dnab5npqt3=j03&xV(i7|DtJkhM8t9o&ERn2~E2eVpu!UGSSVBv)=dS106!nS~)2eTsan;nU0 zAMIFKFJi2StZ!Y;p_=A=>|+v@XQlT~rKj6+(nB}zi_N;zU`=RbaxY`8Me6M$gfZKr z29&xf5Z+A6#cx>M3Q-#7hy$q@Ai`DAb+Sbuk z4@G#C_M(1NdoeZZa`x#Z4@Jt_YWm2oWd`O8&9GTpp0u5s4QC5`1+dII z=0E;wovY!dKu1;7=!P|Sf;Q%6zTLKi+EbEmv}luR&TOJtrX!v6QS4)M|2Nv0ZG6ov zS5I}8C)z-zCOTuPof%~56sDU&cJvxJTEpoYIQrd~)mhp` znQDOAf(G9{$W(*UHS%^ut!U+_hvr)X&w7r?7EYuV%CX_2*TQs{dNCG&aTzr3>50U# z+k3FL0~;QL1~$fFpe1Lp(l7e(Hp--kF;+YgLp0p2-Hl^LybJ}{05-8lLSZA)gOz3d z@+yW+VKF1(!-EQKf82hjW7F0W()zb~5&`3P}Zp~(Tfni*PpTpQRF%%<;@zKw=i5alK+hih3Sc*M*B6@s^t9v0 z0J@LpfRYl!*nl5&^u)Vgf#-~ACU7AERRLY+P^;d*}#giZl z5L%?;LaD(^=LF@|Dg6&Z|15-7wl4z3r&K}S_ilPvn0Axy070Lyv9k3zz@G`qVsoO? zFiykIRv6s?cM56~o$o6zokx{dOZ8rWx8P^5$b>&5WL6SCDL0IFRAi|9cR-yZC`+~+ zsLC0J@nQT>R&>6jygK!(tk9O%;4N{Hu!T+~eg#J2O1ft#vZ#u$t>{g40v@*%baZP$-U; zW#mA80#yHz$?G|QzZaAhu?|}G;0O6<)BWl3^h4d|pDnyHSNIOZxeGss5ztZY1Nexb zx-{y0;7ZQ6Oq7)xMjd{(ij@O(HYlzYs!gAU^O*8#whXO0Ch7~v!PxP$C4T)0hVcvh zY>DSU-3E$hg{oDjm;?2JS%z^Zehy=Urn(>CbAqz;oC9?T6thlb#FkUw@CWZha2^q_ z#g2qwL2Zh{8BpG9;M}gfbne8DMo9NAg{E`AiKcr%q0cj;^`Dhag`^#bg8 z<4_d&j!gDbiDFhyk~~_?N)5mZ1ZCxM55T`15l;0NmG4!wqbk&Kbr;b1eH@z?Q~RV z`?uizp~C98p90|*C4~)i?!ynl^w9mWLeseok%*>yyD+d!{(lhs8=-8b^PHmW)coHJ zego9PA}y0*u*`QZz{><>l?+jf6l&Gz&;>wwCDa<2w*c%Al*P6eU z#Jfh=SvBe=fT=eQ^&znSOeT9;t3p|N(bc8UX#Nmp0_t!4NaHPN4DmyqTm$Ee%KIO1 z9#me<@_2wXf@*&!92KX1!lCvE3luv$imYw@6cf#X$`SGZhp1&%F<3Sq1!_oCXxT>& z6e&I?R94OS3c&jXWfkcY0Dq^b=oqI+qj@>qlkr0-(fJP(P4_#3ZlP22R+HK++}FnD zkWG3aqA$nKK5!K=D2(D62kx6W}A6s7A=K2|rtxGd|QMh}0<&E#(sB71V}5 z!1=RyEp;(;m!K@A`T)iSWr;{s7F&WnLY2X*Pf)FYgfndpGhp#xh+=C+7#}Kvi0@Nu zDZFB9@vw#J8hJISvhu`iei6Tn=H~8P$a{f&7QX@`ruiBFD%|hQbkBh1YVo5C?}WSF z^lO2BXv%1_s~nnn1yosO&y4d*+``xu-Tl~<=gB2w5=_&7jPI*it7Z^MWQ zKX})`=~3P=oX;q49ag*^IZX&OM@IjF;z&nvYA2`TB~CLP#k73|yc-3(flRk4@6~Xw zRbDy|oi24XWKSSZ`NkXFo}|*D)owAHS*Uxg>~3OqPmwHaqH`vG9MRF8uh4YNob~8r z$UyP5Mw>R&_#+>K@>@veKK#%`(xI#nu9NNz5w4vM3!G@WErPaMnjf37&}w}dA%Bq2 zO$Xsr!ybS=43oGC#b#^PrZ&#&D#>CbcaBO@>yYUv8@F_r1NEepgLdZNhr+I%&hz*o z+abCtW9`hCO0YehVEbLj-(rM91eeITIo=yq@c?m&~(0G)(*Pg7ICbGc*2=frPofjMkP~dr^Adg;W09-ayd>l zPAUaeoEciqLhXmPUlzr0pi=;aLgENrCEf-W!guk5*62Ja-WEDPQCK>^#gFmms$$we z=P-U~Na-juXib|zE4A`f{7}%^1SyRY&?*WWYJ6$_O{JNG<)q>-0J{r>rBy2?jj92k zWx`9^Z`C$fIZcOJ;|v?>4=}AiDdW{yBP;d;ur@kb?2aD#I3oOyimTa88uc^qvDHmg z09Llrp;oqRs4u_(R9j$07&$7tqldD(zg22}a}Lgq3dh@Lo={vkv%KW3Gk^V_(hA2$ zEDIOZ7=<$%PIzbO1+EK=3%KH0W;C2&7)52!OtCG{#lVu25Lh@9v{-?hc7mgDrpu)O z=W)V^z!DHnp9}C}mkVSwEHW;4TV%x~D?P!X860<(q&w3}0n}wDI?$FCOF+em4$5`> z31GYw>xCyc1fF#|LKh>M6PcpRbuqjrnOwo)|Mu7`v+<3`MS-fKk zZ>>WVpb=qCf#Yq+t`_laO86x+YLNb^R!&^+EL~b!?V1CPon|46)5yvqn+&JVgP3!$ zl6giZDLVV}91ue#-EhLv(s!1kkg6f+Tq=M`zTHM)3qR{TBX`8J&ogt<=vp_!itsk% zah?t_u^3+1cw4DsRV<5;@|;tk^8;Xkl?|;1sNC1uV%h?!m_@l#GmOQy7?yN~u_QC|SPa)` zS^`~QDGT;=p_0xpmL3N&X=AJe7oDss0SGQO;}=`{qHf-qPF)8W#wF*2MIp?#zQ~Hc ztV(dS#O3tTv*LV|1>O&2ipr3c>AV)(GPTNP^bl#a6%oQxuMIY7vb48`;wxN@wqI&> ztuB>D#BWT;x0}T}8`_el+Qs}grGwIfs-X4hAeT*;Y8R7jwgonvFe-S%jn_LMFZvFu z5-*6(N0Zk^BxV;`1wk=3AxL!DP7mHmvMG|b0cyJRJhZRJJ1${UqU*5KV66BR8wDjA zhj&B8h{m#KnwPtz72d+eNaL<}o8dlz%r?6&1!wQu9Cq+Za&sf9kNAxxim0R5m~$Ml zet630=}ecKYKpK(oz@2uAEl43lavrBBfJiTG_Kj;(<9zudV_VuLZquM5D#%%Gk8jl z`3f$SMx)kR=w37Lz!^p#axx>G?Ql(onj*!}g1mm!dWoiaLVQ!Xu{2UAgk=QwVJz81 zxs+`GStzc7W#Zp%wZ;&0(5yS~UTxMwc;8(sl-Fn*u*?zjOB0uFits+*JX3Ery7y?D z)e!7-;B|&^odZjq#`O*}IBm{Y;BwSi`rqZy1;AYFcAV$9uo#B5>V!&HCzNu7gL+h* z!i^HBLvAAEvXLf%(1GuDxL~&_6N5z)_-v%Z09oob)cb+TG;6f-=#~uQ0}d3IDm^L8 zn+Yo`7x;sKrECSd#bFsM^!p)!ObZA6VaP?uFm6RGWxlsLY_$M19}!s09nkG*2m^U(^X~nX&a@bc`TCH&=(f3LUNxq1yRxdonqK(&gVd%ozUID zG3MuCYIMJ#Xz2Z-^4juh7+(TTC1t$tdCq3|SHxD%C^Ftb2Y>hAC#aXvYZd$o&;`Ue z;swQpANQfpe<#4A;y>caIlp7ZHN}N@5zjeTNe3RU<5%1_Z5p^Zi?4E)!M%~FP{Emo zb0wg!5b7;Bv&bm91>Uy+RrIxzJK>!RZ{evWcf-2?-hzSY7r`kmDx_31n&5w=xZUBp z44%8N_oR5U13X>*0RM~Nr2tD^LjWbg%K(A z+f*C`SmTm-5S0V@RuB}>oxodz+ZK^QX2jw z;Cmddk81cXz~6SbJ_}f^?o)v8bGW_)_&rSjGl2IvT#xARe+T>nhf(rH7*hvqvp7~- zS{!lG;#t9px3LoS0g~KD3s4A!PlEQipW-on2LehQGxx*)IYz$7QTQkL%gUJ-W2OT; zs=i13az|ka{1tD-L&D0>ppT$2{UK5m6`l>Cq#BPOlIcgM{TR;XlB=CRh1&|-E_pXa z`8^;;E4c>s+4%;c^}7q_Ywb zTbYtwsFKckfKsH~jT-4ZAJ83yZa{@})&lx2p&L;jok2kVAaoO|qcZ|XQb?gTI^%#& zLPaTgA8Ma-7a&P*7?sa?6QI)>b~Eap^CN(22z{{N%;nBI0ST86qar##rQ>}B70>wv zKu2^j(<0(949b0v(5!vXq@kcg);LZoEv|C7UIXwLki~8^7KUpk z1Qf6uV3F%&4K4)eG)jI7$|B&3|Gn^8c+-*?GZ{3GK@E<=m*AgSh!%~z9*a+Ll-^wY zM`siKr=ac?zsQ2QQbT{GF^4qtHyZPH4gHk3e`?5pK9;zO zpl6Bb0JzjO1E5G#0Pqr51wh(OaUsAJuD9vXB7m!1XX&^l02^Jk8k`2O+0~#yC%`t> z77b1ZxY-p2C`yM4p{Pk5@7T!@6h410i$QRNW*Ud{64h2 z>`FF5rNy5sdMSn9e5pp@z90Lxu_ z0d|o141lv-++r%BX9Aq<`V+t#89Eo>99J=Fg(P$qz)IK2041Tb0nSDH56U6LDE>m> zVt6aq>zG+w2l$f=`eI=VgVGHEiHwq7Ab-U0|0s+y+)_cgjR|*RzDlBz!jEJUNsm?d zB_L;h8;`{htoS^(5ASfeRs#@aodYQiSBuWW0)Ry>FTewcQhY8zv=4{jk1+aI3c1_U zCWs}nF>?+EP&bfhjic~%_$wCRVdj+begI)b-;kfQvBdQ~+G@1BD-k*CyEK0h&$nm*Fk> z4q;O6nRW`C1;yWRz8$U<>pg6_<^y7GioeO0>s&xL61tZyms>;MV#{>_AQnaOci3_* z*U)#_a;*l$*1Pz7Y`NA0x}4(P$ChiOhQ801Ym0{NXUo+CNM!j>v|L8vjV%5UVqOn? z@q>li;8jDAa%Rk!ekbtLib_5J`~xKUL*a+vwdWt8bZnQvM39Leu(o%-d3@m(#5kV&ryYeoy`gp`kNgy7hrJuPWoFMg>Lv&N*uCF^)+U$ z1G1R}8ytl#nFLk|7&BuCxsp^%9fjNBFH@yD{d>SoD=K~(&h0=uX6z^~{0wH1Gky+C zsq0kC0N{BXK`u6VhU)=TwzJ9svdJrQ{T3ke=3o4%U#AxhE4A<)b z@v_5ku_uA|A0h)|8ih{a%fvp)-vY0atngHLryU3Om$<&>JXt66b(R{ptxKZcV5!a5 z5Go)l8K7@4AymLpXFH&$384a(J9lVGRKQuzn*gyKT;FCTyBW~mNr~z>$9WH+yBUV+ zSn0eE&?(F_sw3Ls`#~n}Q%)`UA-oqOblU0?3dE+kU_r^RfV~3V<4!I41H7FiSX;tr zMvMgK&i*KzBG)IJcfpm6+{J8t0gwpvDXQoM&K+goH_a$G!Zxt6_^LE6!43IfkEMb*mjdGs2q-9u zfPkG(QS7~8!SZ22MdAOx=g#cTPQv&9?eol>d(QiwbL-rhor*4K#X|58EVsQq)wjLkx;5i_)k`;sw`1`SUWH>W`mfRY-myj%myW5T0`R~FNN8_ zr#ZWVR-U?(4dEOJwOB4FF{SFoaEP<5OVwx42ViAemui3^q*Zths&Bqt;WXs~deS<0 zoMCi z8k5Y4TFC!}>PwC@(W_8XlS`ZGuTZm-%b4nKPz&_ril+WMbw_elQ~!gyEBOvnzeYVK zxuL25N!^#++|>V~o|@d&)c>ZQo!r&buTw{c-1$gp|AFCq7gd8c{LkZVQ-Ia2})Jss0NjYiiC8_&T&X{@}^;GXm&`+Y{#`}k<@-$Kj?@g#*-UgJXk$m1*Y;2;Af|}qh3zfqwel*mw-hio&ftuv43iV!A ziszhC3fd`@B)6<=4M!2S%o3LJFx#ieX4@eTBg3f6s0Xu$@s_9Ak(&W%L^tEyKD z!Cav_phmQsWg$zeG!xiHx-~9@1$V;!9I@<6fMA*ejecN4m&Q1M%tP|HF^tTY1HcDl8;U|OX!FkPp)jn$kfZQg+SG-RTbVEW5^u$^ZgfPBv_Q}%@ zp+Co58~^of2Qc&K)p}slawXhdy&vQw02A&(L`@FA2Evrky!uEJk-n%t(NtT3=%_x; zRM{Ib)#sQhd%;(Ik*T(Ynp%CiskVdas=nS-+e2*@K9qs|$&=9H!Kk*>DnHp@j(ph;x)-l{b za|%$1;kX0P3BrSlgwF%|itq??233A{`~af|=id|VGs|;l9MonsjbrxG=0f&4@=BCe`?xpNYVYa=dx0@uI)+xjd?~PY*FGRu z=!L5?J|5gyK&{R@K(Eqwd?iNH^}5%g1$x6W!KkIt2AROMYSnarGPPy8UV{z^4>k2 zXI!y9@Jjg0xV?!;+)Gk9aZ1^nrfCmT&)~uBP5eq_9)QhXO%8U`_W;wi#$u`=CSBQl zj|0)-66@lrR?N`;6E5X>1$<`r7SA>=77- z{s9(H3r>NF8=ODn{uKGh79@SC`Xu<0i4V5p()__v>4yJsJ1#99>|x>~3gK*!;6@W4 zWyhsH!C!&bWBKykvr?_))8f8ugez4zA>e=A73>8mv;y}dChaTMM~nLjv>l{fa0Oq1 z<=1b>v!8?NAYCT*EJ!=xEy_#3oZRX}^w z6`TY464NW}(Som=&}w^xpuZ~qZ(#ASFI2o?FdrbasStDo<{N|_DiqL@fg^n^{_TaJ z9x#_;D?RMyh?6m)DiVAMP#^47{ErlZE(5w96Ptf`A*fa+E>vUjKWc|6Erj_Zp~o!4 zQjK=mVk*6F686~%TBWpVxPJ_b|M9}uWZ2IUJ7~eQN^il$Lv{ZXR&y`_(@2^RyTdzm zqsrr9ev;5pw;PQTX$>i*7WW;7Ssth4Q?B4Cll!Fm4wI|4CN?C-BDmS)KJN;;Zr8N7 zSp3I|j2rA=;>U}O6-*RL&4SsFnC+xV|GON`v{GTm^v#h~!CVtY6t1a_o z;E6Sn{~0^3@~;R?@E3~0BNLhy9Bnes+L;|`q4!{Yzgf@#5fVcJUbC$?_Vg1b!UQ(F&f!5;zD zz7t-b74oVNvp}C0@|pu^KlbMSuk6KFO@6_|FWYf7S(mS6_Gr|J^uy!rgmQ5B)9sx?jzi*qDw(VD7`>Z!Z6& zEKJFmE3MLD5GG<^`&)X1pmv4ajJ4oD!0u@Ttdoar5DB0K+rhk=P?ti`2AIDm)ZHVG zWx_4p*iZin^CFhLr@idq$qlD%@dsK+z3ilLM;H}cX)^oVlBoqhGD&?4kt&AwT$S+u zcoG)>06WQ2u<0f#$4;^r?7YbwcuOX>*K8xtL4`<>g7pLG2p0cPJIPY8(*U|*TcX}4r*!A32NJZBUlMGCeCq@S?(^X(){!ETzQ zS$2}WV7=}!!WG<-sRa+2q`8Gik%D$FC-mwD{}Cz`{&g3t;( zl(7lsuLy1R{Hf+d`=x$nBO7vo;}{zu-@Gs z$VV0@p7_!-I>G!1p-(+Dge#_H95<0KJj-yP9%+cUtJsZ?y$wERTp1lZz#ohMYrFr` zCi1P#KO?>)?s&uE|H1Av-b8+~`7}8L#M=ozS0h!>GH!!qAgwpeDyvb^WDJZv<>_}7 z3s=c!+xD8AHc9THNLrJUosA$K3ogU;Jln2GPvCp7_{-Vx$e4~!X-y7;U%Cr?D*Ru4 zq}W=M3^>iCW2((jYqA*TuL%V$5N9K;$zLW^)du-$<4{FwGO?@STFZ{F5c~(={ktJ_ zJqymm3ZKG90TaA|9ar%`241&2{IhMi)}$TG0=2e5M$uj{@lJMJW!hf#uzz;bXL^?{xZc{ z4U-{z{s`;K@si+>L9kZ`2G zG?QhnG!CYNG|#n~o6AJ0HMjtzL}J(C4fo-16;6+5&Q8TY`;gp2k^UejKdrHt2CCaC zl+Cvae*v|)Yw&4U5&WLT6=`r3>6nm`@VDt6l6n0 zt8R^H$kD}%4x!Sx&lJ4GoiS|WWY*>J7%BT8DSKlv9T1bAE;b*VSBs;YH1e((MLHBk zV)LpK{?f>g#HiqhEx{#_Pl&-z$XOcshZr`hr37)qQ4ne5M`MER(27F462nFn+7u~h z+-L}C2LF~JdNMWOv1!$!46n-7{a^1hf#*Fc+y_DlS) zweX9!jwhx5l>figg~DV@(qBh z#c|u@^_|4sp15ye=gGpvJ)U4&)9*>o;%L8h;>Ww*xbsVV)D!n3{CGk#ajz%16!HnC zk45s6qWG?YCR5wnh#SXile~hJxXYt8IE#dyIN#Aipc{as;EQnI<{Sjl6L+~Y4j>`LKJVrg zFJ$O*q0xQ36sEBq`mI&b*04J?zq{oOEw4H8K)DB2rn0o;5#W{9tYjwN2&I^ zo5Uv?O>luzrpk;ts~j`B1C|E(+sz`7630Q1u?oOXSQ2l#=UIVZLK!3f!5*Uq&PWxc z0BVO4{K6y-za>%jEWx#9HSISniT8VqGB_oKDoOCB2``AibTr@r<&1(Z^%w<>@_x^R zS47~5x6C7l(4ujfjg9Vl%&o;%90LS-hNbjH?))DxdB zg0JTOI1V}SI)ULFJ4@(IT;bfadDGA_SPgnrw7Afly0ln7dQwkJaLK-_JCv}Su(4|} zi&O^e-ojqx%ovXZU!t41)A~9h0H>9Ll{zSsVeLM?V=CT%-jXs%#J}=|fi{(S=W&XW~H*}Uou4Qf_|BTJB zo+HDlj|Um1^ojs+DT{f$MFgc`np==${0ReTaXit}6yu;wqF$^$q&>ElF$z{A_G?D$ zD*uPrym(z6@%-Z)P39vZXLYCYE&|^jaQD^03J*%5nn!^8Cpa>mgZ!B3=?<2Z%u;uQ z$(5Gz84#@k(3wS{P9XB1ahAillgyXRTx2EpIb_@gid=^(aL~Z;M70dQUrtdnl&?UMW>_3eVR4LF#wOv;7A*c{j(Uw$ zStzwQ)~XJHiH;1umG3^LA9S#7SYRP#Oo4G5ft6uz)p+}0yhLDixbf(r+**DPgg1`) z*P3qOL(I5q_`7muD6q+nxa7__&>oKTBuu;?!9g!G#34rxGB0xOcWK;dXskP!=EIoVT29m_?aTSLlVcwzK zdM1=TXraVDc#6U0s7hAiy}ZRWy;wf-@|D0^%UB9CFIp$|j7y|B8m@AHUVU-zzT7qF zV0Ow~YjYA1?}AC}ALmfF!C277lHEYLqt?ujrO_(=3zsUr;5I}+BU-RFOoM11Dw`(C zVYq3*Nic6BG(3(W1ggNYVpFQq8+U<~Kd-mWMuJDr<=?>VsG74Xf%2~ix2AA2#qv5W z&U7*PYO@7`ZNOv;l)s=iRxJ>PYpp)X#`8-dyd*E}D@QFV$UyggGXt%Mj!(PlK>Icx zISa`@)Sb@h2MXB0H8H1a3b+;p909D*xEC=tnqioy{p`q;5Pu8Pi0~YUT5uLZvw&&; zI?li#by7J9Ug3aWI% zgj{lawow`uTg}8p|H*25?*W7{?l09hBc^i1;|FZD#se{)1KnNi#@vG&XK(ZPN+fyG zbiLxB$&pc8a|)Ee!>pp7?qfN9r%)EH_WiW)GJ`Xmg0&jru?KvI;oygxmbDrVY_!0E z^3d&Y`&R(7%>suk5Sd26BNlTC%~`HoBj8b6Kn33ocsiEA^9F2|KSR9lH{nw@xW2#_ zP56urZX;NBAatO=jD!E#@5)}JQ)q7CLD zx|UUas!_C;Y%n)fEh}WgFWX>lt6J6?6Mn@8|1Kqe$%J3E!Jz_rm!4+i{hAFPE^r4E ze%%I-6?mZuU$DWs0>5O!|FOY5WYMy2nD84mSjKBshv|4u6HDMN8~jsqc*{`?ckh8M zSqs2AL+0ByW*&;+le^HfUIMORz6o54=4e?SSQ`_0uS*MK8zu+LeJ1%4LMxQh#HR~y_Xk?o_1DWO{X)z~Eko{i}S$uS!E z3Ug*mZ)nUS@HJ-4`s4&IBTo9PCEU>GVXf88fmOEKZ+#umRK}!@iqKRTdTGJ2z~#Y^u@3XLW}{trGqVaW`cu%10vb@=!yf9dvnoU<4P!Pk%rr1)cZ(!btX$|NcdN%b<+|nDKNmFOVbT)yDd?c`!slCNV zK0~JVHXmmkVQQE7IOBLzdj}6?)t&||^?q|uvQX)-a2#n_i-24tx**(bf*AL*e+Y=r z^Q0~e!~ao^WrpG)9ZTwx@Q757tcEeg4+MUUoTVAKHY9znIJKe5B&?1Y!}EITobaTa zIyb8|=H~5KQkUD@sVdK`eZVi1vx0l}NETYw&#+b>0c1`1`6#2#cfmTI$l52OiluqX z`7pI0YbpqbNm^$kWjzh+uSC|{k-uQQ>pmbGqOEvvV_V(37uV`8G06kB5 zOEj!yy$Gv563Dh_#BeMIs0HEew!D7~_eYJk#CagWpyZVN=Fmh}c8?u& zGLl2M_HV)*KN`^E7D(DTywb7~LFhu#AzS3Ep0KVY@`N2(2kV+!~Ge=}2RGV7oZJ>O_5GQ1Va`^Z+#bKdIz2w-b z^OxY=H5S}wWy^5*{KYo|R|i)kLZ#x%r%Lwi&@?KM0|EO(K~NrjhUIs$m00Kn^b7&yGMOgkOZ9RN>I zq+P&+z%~0pOS`CAUEn3gpfu{a^7eYlO0@JqMA z?Vke72{G+s3q+=t<;*kANPNb+2)qk`uC?!CBA;8FSQM@EQ-HWhqT{dVko(s`-g2t&O?OR@)N7gd6SZKe% zT-3fh?QD48op!FX8{NzaC+&M9j7&I|Ff^++FT4rKsd--FZmS=ZGDy4W5pSF%Yd;4_ zBF@m?hlFv4epXl5aDsHzTvRxY*SzF1Q1}czEj;_E728PfsI&5m;VWn5`Jy{HE6;>+ zRzCJJ`ROMd88?tfL-_QQ=3j~Q9Z+@OKCES>$%TNasW`2~vz;@M&>9qb0GC%QU)8-? zNThYcYexRyJP`L7lJnqM`8U1su*Hxu+&N%$y#Yn{HNo09g{wVSX_ZD}&2d)wq|gl3 zC1PE8TDEHvl3hD;HJ`@JI)_1CE9C6=d*T_sL&nt+&G5Z`3N)N@l=3an8}8~ZTVKQd zJt$obkD?cFNLsb_A_P*;=N&_2T~ijdrHIA%EOS)mccl)>e4gfvrts$mRQW^wRr;ff z7O#vACj6xh7VjoM0hW4yZwbseTK2Q3RS2mf>tFRR)VDHs*Lt}6-HeP?&8sa&&0Uob z%S$uZdX@RAqq_THmEt}XFiKmQ(pL>@t;VN7D<7Alh*`@NOKDp4bPH426Rh^T7$JBC z5u`$?;fLW3Kjx{bh-0Ojk>Tl6aL}qP1>0-~4j|PIPr$0$p_j@ly+5UI5Y|!cAm-?HurQ3P+0}*nNSs-TZljfOhnp2&t^?5?NI7Sq#X#U zc2F6R6ERvRkg!nW`R;XNaGvlpaqL#+mNABMdu_T&|xMb?cm5}O)Tv&+R_e$R6D2)Z##nw z@xCgCsIt3zb2+IgYhBh3OlJoHhNrXtVhc2;vj=1V}c2PsYk=&-Q;D!G6vKn zr#wmg|4ec{)}Y5KuVcsSUG7J^h%W205O(|C;ohY%-n#;;A{)~vJ11CQPnyBcYTTNv4;Mq^L)m-IhLUUZB^!8WsWzfmGHxYAmJ6M=e?V#+n7$LX{@$K!PaOS54Pn(sktaDS4%Brk_ zS4^mi&TUelR!l_Nq0sD6{#>LT7*Dl>%Fx)Q_Msf}n$`hoaaUG8LsjsEw<4LBQtX^6R99Bm{%J{ zxQaEi?EXe(W+BG3x3}>llhf7u)Ud*z}C9PWTmC+?rM?6x2fd-8wJ3<

wivADAUG`+y}Yvx+6F&vg}R$->pRU@6%O8_f}^z;z4lDruIcjr5c! zZ9uqb8+^GMn^@HXXRSdwoV7LGyM7Jc>W^_)b&-?D)cgav4W_COI5Q7WE>RbJw{Ted zc3`c=eUgm}NyHWyoMl!HgK5G40OwX)^+D%uy5g6vVyLs##n)o0#Zq;x4cCGlU|v9I zT_I?d39Yw5>a|3{*MOH;2iFZw^CD0BK4xFUB`Gbo0?MdYeMZk-gvJ;LMedqwF`cP- zTrI^4*JrTStKO&g=0`zmSAf#vRt~&ZGE``(ekA=}b z89v;@OZ__s|3vlu;1x;pW|n>{isNo-zS|=3v*SVwyd7}+MLbu4jQCB{z%P!RZ=v(6 zsHMpH)sge8MX$Ql8G09)y#rfLwYPAE4WtrS01PN)(c_Oh5M@9Mm>8!tWBu+J*dKZb z1ql4@7`X8;o`gX<{<33G>Jw0M4o|{Kb};#Q)c8*vAzYQwUWZM~c?mGBy$mD~$Xl=t zO8o?DGPWbK`N7n{b;n@$Q!wOAu~_3fI6`;BZys!e-OI7YukgQFbwf*O^rd=kd;xF_WSpl3;*K()Zp zn6Cgz!1ty2OMjUo#m8XsRx0(@m*7hH?8B0fV*Y8>(=Q0K4$9!;mA}Z{SqbGG!2_m4 zCC7W@F|=}^>XX$MNNK?fU|wbD+wDvV9j0r+zrjjb4`!YJAG0c0Ey--+V9_kOOG$$r zP5d5vq|0XM@aiy-mNQ7ZqJyk6@h<-($Z?Q(K`NoAJ&2kD-Zt_6vRPP4%B@7bz=&ni zg4e+0^{<4%k?UWoMOi1~`j>x$LAuW#F5Hg7l)LkB1`zanu_WX=*yEAhwaWcqE+8}| z3{_c9eU5o)(h-Av)p*f{t_5_A@LW|-eB4MFb1pbwZq<=nG{EIl)Hu*qAXn$qEuGtd^E zt%rs#g2!WwyioO!anjA=5$UO+M@{k~GrXLVwvdbJvGClWHAunMpeMA^Q!(QpWMMs#)BmhU+PJndn%B`cfL27QPY+Z^TS#Y8u8XiX)|!X_+9F)RYdUVWJou zDcwxNBr(LK^fnEX#gLtnV;XYBfP99TmKn+tlQP=0JR+9Zl!>N+*M;yBO`o!9BCq-a?}-h%+X%tImbYe=N(f-zU^2d@{;2bkv}<}7x}Z}ZIS;v zz7u)FaYJMYXKDPk9N{bJtSU0e*+^tXXIGImoWn)dbmoa{o8}CPobGHSa-p-Q$VJZ4A~!nch}`5{CvuN-pUAz=b0Uv8zY=-W zc|+uXoMj(m+&7%Ji~PjdOysA|o+5v8ju!c|lLzf1d^eoyMBa4n6B*|^C9;(3lE`G& z_aakVH$~p=N?1X^8m2MTXq=ZfH^Q_2mo;O8~_k1IAg6F!(Ngf|| zu_JtwJ(Wb}dU!-I!Z*d!N#s<|5Rub8(?!notQMK)*&{OF^PI?8o{J)9doGJC@aUMM zNBHJ=Qbo@7G!Qw@(_Q5Kp0Oh5d*+FJz_UT*0?*?j7kbW!T;zFI-l_hgHF(9=WY3ePx^D?N)uuJSxAa<%7-$TgmKMXvQ+5xLIe!E`*rx89Q~ za)akC$l<ZtCyLmXFJk*b5j$3kczCObU3*15a#Y0b=SA#! zMa14qA|Crp#J(RyJpPA>1Ns(bbg+boLuEu9P7`tDb`eMGiFmTPh+_li(cySU(Vpoo z;=~Y$`zm*Ej`U4-G#f+0K(!YcG)@SEXNVX&U&QbgB1Y^Iao=$fqh1p+<}(pv{}nN= z{6h>jzLtoIT|`VCCt}Jn5!3dGnEteg883^N`GttQt0M9}TN!0miip`6A_`iFnA2Uv z+@T`oO&4+hN)hufh*mlu7>-jzU`)%riwdbuwXN%ipX0c zB7cX7S?5H|{!B!{brExt9;W-;dLr)cE@J*#5f2;{vG6Ssi>`@SoVbe)OEN_)Z7pKi zKoQGliFk08h!y)qtUNDb)psIR`yQeDni?Y3wimH(oQU=FL~K|iV&g6mn~sav{F;cZ zAB))bw}|aYyBTc99U^wN6Y+3g5s!`+vA00PzO^Fu?-z0Cyoe`07IFBhh$F=wWw6=- z5q0hoQLl}N2K`0cJwZg{`69A6ifHz@h!&?sw0uWI>mNk4<%1=|eeL}sI%bM!(nLgd z4-rjAiD*7Y#65haD2Cf!5pA9m(e6zV9ljRP=`RsoO711ITR=pQyF~PCBcgYI5q&3! z=)YJ*&Q=iv4~rOlUc}H(MNIuwgfWPw7oW}0GgLWes&ST=fHO72efc#-%xWuQ_DB&0 z3q{P`Ct}`t5%+&3;sM7#`YlKlv9Pv?MIA&e9wuVRY!ORWi&(Z_#PV|@R(vU9Ixyn*KsgEdrrba#?yjcSN!+)6}~AtIXQ zi)gk|MDv3pTD%~l<+~80rlE(P!4k-GmM_464_wf`(onq+ps$>$Q}(`ck}l85KeyVb zpWsjny%C6UnNIw79QCA&@>T?G^k}emU`aUs!eNyQ|I!YHWei_tn{Zt2<{h;Oja&i^ z7P#xyfx|a(s2mhim9LIV-t#c3=2JM~h9&*pSl&LtO7WHn{^!bmr7O;)1MACI2N;CD zD3I(uN;Yrqdb z2mDPp??fAS75Xp4FDjj18~L01yQ1g3@D^_NLUD&-0Vnh+_;0|b<$Q$oWLWPf@_{?l z3CMS_g_a@2Z%oW!-sJW>R2*3~L+hSl=r7!SP%9_EqflDTokvyX zFS7t&xZvmmz@jPy*#?rcnjW#A3TVCrPe6N5u@g^8UPNAcQIC*6Zm!uiVexbhaPltqZ8cZ|I zYk_9E9@5&x$ADhJQf9h`M3l_DoG3h)+k8RZO@NI~3y(p-Gp{R}ypF=l0cVTiF$3@@ zq^2wn;&l^V?m2uX2n!6?Rlcl>l{Z>=70=@dCM=b|b&URu@xs^WioSngjMTcL+~6?r zI2?b+f=qbp2?}FgD>Mr|K{D~bh#F!+Ci9{*u{JGz)jeNPnH+-S6)ebv_jMo>^Rl0r z$rQ;X<|WMXSdhu4=u8}z{<}pcu9xwtCM?Kg!sn8Sd1}whq=1=Bnh4HHEUkNRh@vc< z3fZd00OAWRw26;^p<#Zi{I??oZbqZ-pw)7lLZm2?N9BL@#mvLQCp25ayW9q?+KDt= zPAc0l<{~7AY}rP*+!DjLsdM0$ZOjL@J_J^_88(UZH}a5~I%O~0MGb-EBL)i7vaW#4 z7ay1HK?a=k^h@;8vJznB`=!hFR@+7dX$)(KNPin5wI2cmlS~bSn_L&GkG;53#@nt- zp2S>KlFv$&P8^PE&dq>I3X`jn{5*p56|VS*NAA-^CG`SR&n5wQovAXl@x`~ZmGncB9uk3ej zB+67SS0x$D$^BsNVFG+HL5yQ@MhYoJ&fsP`Juv6ek#%kQwqorGJc@O(z#*(- z0?%MwJn$mcv4Klidjp?hT_W&3)+GafVI3E6(_KlY0!NXY9XO4(7I+P7 zJ@7Hs4ubCo;zBf+!CDWbVoh_!i-AlewLnv>^+0DamxxL38Z!||ZVJiWxYbWJweHyJ zH4u6VflZg(%Or@_TL`}7{zCX2Gga~clOS3SF4Af<3LNcYW+LdnvrrIx2@aoKgGPb_ zUxGtBaHEhRTc8zN0jPRPo0!#*EK>?TGN;9CGBtc;&K0x6)bNqHn3%n$hR`Kqo-j3p z_Qf1GHGE_)A?B2+b>?$lFPT~we#Y(%Q|s!$XYAgG#ww znBvb;_?&a#Qu3$fa30zjs|>7i*V4w@@TZE(m2)p`lH#r-_c&F)I$)}(T#2vqO#)Pl zr6Uy+8y~}u;&CR6O=4$e!umIs*ox>(Pge~$c2T>nemVtj0ZxBo4u+_v4F0geM=z~@ zZPF6MR;_MB7l6T#C05;RDJae@G z2cW(|pu$-rH|62^3hEUCr8Va}khxqrKZUiVq`pHc?su$yhdt?nBi4y@Owwx(ulYIo zq}OpD1AMkSF^~9y<>V8HRD!tE0AGm#zxG^c6)@7Z(rh`|Y;?_e56qs@SS5iI3^;or z8Fv(fOz7@Ix^ox|>Z747rf&*V%~NBRrqzX-^s@5-I*diZnnJHQM$bKo@FlmvI+L5H zb2m~Y`42Mp5L4V7VsA&PYSIQv`8Trs-qnrN_L8(&?$Th{xG5(+#Je=St)aG@2Xz}% zpO+u96?GTX1TSy#XT_3^K`rZD2z4sSPeV=e@)L6s>IBqO?>F-e@^etrHD^08J@;bO z4Q>@b^6+eNKO|k7Jfo2ufn=p*4>#-yNILFt=D`}b5Ub_TlOAxyvcmO}+FGjA4xu$K z|8#dIrz_ML?-3-~EJ@v=mhkc-rl|KqE$KaOqJ5#pNudSp54Du{3{a^<4pg7_RZ|@Z zHNkt)R0l&X>-`Ao-20KyeNdCUzd)7N83#4hd(BiQLrwSo3-ut$)1g-Njxt+kCe-TQ z$xx>*0y+n3miHZ~H>u8tS{q*phGXp4h!B&s&>`I*?Y;;oUd)M3l6)6Kb!pC{Ajh4; z>LwhL(jAG`j9DBh}xaPV?5Ci2dn8sDDGv^L7QAO;vO7@iK27^ikA3(D8lpRnQkwF9v;u?mYlq zTA~#6ExPv@^yi5uLf@`?--rGN^)k?R>E0iqPr=NRR2KRk-FprCCF13w@7KM4bc4=r zr~v(t?yV2~cIrvckLunb(52>S(2wg{X+D1{X$y@a@QoWjS*e=bK~^KIPLj3AU`Z^=m$QkH zZ$DB;VD%2EOU!(2rH#0%3|7%=D^@?#Ypsz%npqw7#?Rxx(P&^e&yarss~B`{(t3l> zDN=R7w}5<#PowiH`8TjiC%-_CkHdgaR4G6lKRHzoSjA4dGX+>&HLRMFys1!aRgv!@ zs|!{G$$F$nmTHsIQ^Aq}gg*s&bYt*2blakrUSk0_5_|-!O#~mZ#*`)wx1#1eOX>=g z`V6U$>P`-M=LfWYgOxTP*1d&WUm9Ljz=JU#cN`8b?-(bo(GV`kHc%;!%*L) zS|6&*`#RKLsWyZfgZd*K_Xbv50VLH%={eJC&W~vS7OUg5-=)XXtgzB7s`P_Hi{sbr zKLn-XG~MZ244)@4uU@6kNr!U~h`D^)%*j=57VwfxO%;{rd2(CO#*~w&vm4btbmPNm zPOfr?)5g?PQF&G%cP4F2If**Q7Qn{G*qmJDZY0XoR8e_4B=>RJm~s+z@}a!k)3h-) zRaCB=u>`n^`v&L+EER`3;(m8)+D@kH>bZQP#K~2zz6AbEO%+v2=BG_cMl_d?ojAG5 ztwDgPsiI1un$aeOBI@L$HM#fF#?(|%xstPNxjaARoD8%)-(ckAsRSqQ#m!wv4@q(5 zr0Se904tv7&fUZnQ_e%=F;%k8P^i2&)ydn^O7cvHlNSM;Z&8)h$4v2VIH!=A^5mkK zL!0psOsZ!%lB1E#tnL|$MY^KwLMoKM@7vnQq*_g{{ zY-wZidVOX)8*>AVZEei0GMUXkl#ym^oNE?$k8e~qjF)z?K#m0P##)CHIdo&)fF+ZX40~_-z8sE1uc}GL0 z!(r_$qrHA1qC2czX0+EIM7Bjm_i@V9^8O*RH7W}Oac%*!IogN&PA#uEkxkJ)3h@z{ z7v-bnCD1rGimv6Aqj7_+2B85b0G2f+emW}xdjmbsT?MosRi>ljqL^28^#uoh(;mpDzjj7-&;k3fAmUoRvpm1C* zk2fJ@RxZlN@eq(oMft=KITqChMhW7GJQdXtM$!F59yXt@Hs}>-+!;2ituc+p3sKRv zybKy&j|z-!oyOmF?(ieKuzEE97B;Gw*);wYHmV7sHI09Ujf%#$Lka}JF^g^j943u*i@Y*fq@ zG~O2OVP5vo^48E8AMRXb+(_dmwsJm9zRuZ`KB#uhregCkOPOII3O+l;+vY;Q9Tps{n5(TKsj5i{S7 zLbSY5G9AJIDA#f#+oR~l&{#`kOEjVy zehZQNqYy1`2aOAD#yvD{h%)*fT91vu$)e;PApC4JteX4?kzP@VmUo=S@iya08c*4b zXKCzbGrmmYWSj9nG=^-(cW4}9Gk#3t2%GUM8jsqH-_uyCsMh|C$XOfxFB)I68E?{< zXEVCD!B}83mY^}EsN8NNQmrszbbT4Z!NRbXSCL4SqL3hw<549rqGb}<72dh2ZoGrW z|3uNXy!tf$5#A9gW>Xsfwi(;fxWeY%g~p{eV^12_*o^&Yyd2&?sEmft__NJ8n#R)M z3akCY1R6iJ#mu9zOqj`-MguG$QlThhHIZUPAzO)f3nRuRxSQ~owi4{8@dumn2#x!r zjK<9RERj9Yh}w+L5_u~M(ehrS@n*Eq(*u8?-U3i9x%dYV9^J#&Ab*2o^<;UG7Vm_< zJ=w!^Y=0nGBgNAj4RZs@nkgQh6DYPFPnD&3LToN1??~~qMa*(Y-dWMp56Q|*CVP07 zd~GIEJiJrB8I!3V-Ywsm$$*Fd;|DOA=HVapF-!(M{3o8rWV*y%!emv6yPnAmiMxx* zOo{shlhq{dNhYgH+*g^rUE;paWDSXXnaP@RAH(lR)>1XTiDd0msZh)goB&SsOrM1- zH%KP;@N7b|29td}Cy>00$xwbHih10hbJrqcEWa6T_p?7DwpO&wHxG&wkN1NoFEkJR z#8$T}Y=ddvX^rXQnNh7S?;M{=@L?V%n86j8KbOF6j$IqDn7}S;*5aY=@5oo0hWF&9O;9b`cuGh;SzMnd z6qET{D0Lb(|5*|@SczKx3$$%FZIsG+_NxR6;GF*^Q*+Hu;1Pu8Y=(7#UjIEp%bA00 zGgUu^UPoTOYSimX5>`BE9Fwb#`5w0Gv_E0SRGsM93EN_`D3Md};Trj#mb|%3Q&$SY z9IbvVogXsm8fgXPlkX!u$IMR(o1Z}2O0zd9wR}Dxki6FHLrN__g|=mUuEVUTQMn+2 zRs7FPfC1E`l3$g;IzIbBfKg+O>8V?Y73Fyjp$($6}J-G&Fr1=NS0)n{C?ooZQTR`8HG|^2cf5y z&!-~lwz(BLifAXsrmy6odUhfK0c+%GYU|lE)nQeM5vDl^p5gSp}r*0#{?*iN%wmKZOzKa5QtQ! z?tg*09tNX}>8c7)lD4ka0%-XOuz9{E&H=%zSz3Mt+J1<#RppyiJ$x~uBB>|n^U}yH z5k6#+i(yjCNNMsj$@$G1O)4=**YfWq^mkEEUVTE3gd| ziwk4nUL)3pSYjBHA`4*>0L?QC>pocOrFa%W48r0VBp)jx9y{5QHy_fo`&I$+mJ!|? zhQoRLBz_(rH}dQW!_oCJINQi68_kInF@F!?E7n%i@(InMl<4oK2c_!i zy3YW3h2S41DETO_HwfG`0kP#>qRka9S|n872SofuAo-sYxn>SK8LZNivXZcMFN5+Y zX*W!oY@jw;{!LPTw3f)1@d$d&$p^#oi|qn>JqncxpB62@B+-AO(4pcx66=x@z)#A* z=H5n1R<#0c@&!QEWcAZ%;1>#vjinm(@2qjP{4CnOkFr&*OPjo9L~X-$8^h3*#$T8d z1BS;6rU?Ke3I16KRQcvjBw8%o-t^V-rqdV`WmLJ%)e8uf(v5I=3#lf^z3$AKBSxF6 zKS--)`sb~usz;OZw$K=Ba>Co=PMRN$G8@BrFA--H!VU2d&F-+7t%X0xGa;M zw(#a6zIkqpt@`#lmZZ@eu8v~*X^giSE7BO52o#eqWvQQTWAb$`^)sW5p0*gul@X#r zirfZ!JCcs^o)D~!ki1)2TOrx7s%HwUeUNOFBL5Qx(^@@07ou@9vz~lNPRpM~TkB}s z0@#||8EsoZUU)yPd^gh8Cz`jDwotTfA8o^;ZAWPv9c_D#wu#ZU^R!Kiw*7~;S<$xl zXbb;wQRVxBw(yY$Z9YD;sOA4aU_aC5hN|UXrEM`uw5b5sX&HE zk0UUX>2OfbjlH_Zp_Dg`nBm0(}M@h2C#*|(5Q@5yoU*d2b+|S zEG>U8f&0u*ihPi^38u{w{z(G4CO~QA`y7E4<{wcT1QG7VNNz&M{F-ZuJPV2W?uu|VgLG1n|InbIt(2}U146F4VeU6N1Q<}W6BpgEG&rm%vx z2hGA0)AHBRHrBKqmMF&SlK&*paV9DOn&SatRX3Fp1Li+Z-eQv{QAXK#?DJVKlQ+_+dHzLe2h94(pitA{ z2L!fQ0nWak(>l>?HkMM$|DLuPR;Dqp{YoHwGJ&xX!gC#kx(=k7dgzFc8#!`4@rQ6r z7?RD^P0AH$omQtBL_;hoqxj4;rh@#Y)W(<-4WFI%afLsM0QwS~K%KKj4((3q?M0;k zOg4dO0#M5zM30edEPBWw$sbAEXw&wHkRQXkqdt{}4eb-7 zx`c{42A0g`=?{U^0-xQ9ef$Pu?twO6FYeqAd8!HW2tI+&80goaHbhGA`wvq1+9DtK z!b4hM%-7544vfWOO#ycav-e|=>ksH%fHVMAAJYS=z{C7%h_*oA7V5wBfo)c2kRK0|Uo2oiDal1C2xvP@+S|9LEz7iB*=W>|Pfz9Eo5yM{DE14Wn-$?w4^-7C5<$S)IFJyqhYeh!&$t^t zefgxezreu{x}pWL^;2xHxnZ(2*qDCUX$79oKn211$dScX+8yScL`v^F5FC7biubi^ z@-R#;Z+cs#pB@C1uZL7c`youmivbmxyrudJ@PG9#aBqgW-Xl=egKA6ldi(JRcWoF_ zU{$fIDVz_ZE!DT0coQ2wU%yz)^*El*Plj{9Feq)Ls^7wWp4BI@tx25!KZpzTr%KkU zj%bgCNf(e(ZQvj2;e%{_S0ED7!;sdTe9~=!UZ&*r4A4Hac?q+*F#Gbo41SeH+ucMNPRd^rndKFu9%?wJWQpZ<Q;uVa7j8#@k_rY_OJ1PduwN^D?RTP5%O~!_%w4 zDT{hP&PXfuCQSF9G(*!?>9=76d7UBHHO>aB^oOvX1}*K^5W{dLS*^Fg=*I@Foh0xZzs{g>)6J=3vHKSl z3rzvQ4L;`wxS0#=gAzgzZ4weW1K~|VMJ$0?&d^o>BP8M~Jr{C2^w>TF3&1^&WT2TIdKdB$=<;IO zH)xD?4T^mNWO=b{YeX7`g)f%Hc(J@#mev^t!aU{0va!aCWwn0qp^aDSz1S@wUMuRi z2Zj~;aI72ADDQddH%*K;;9iHuL3|Q-z0~tt7IngE{h=fHqLu@fps>Ua4($Pz8}p#l zC$S#f@HEz1zj~OjJ=BWELfZm1~>cx>(EwMuQBh{dK+vBBt1{!FnQC1sroy4!k82=?TVTAZr|SL{tee^WdlVuqaEgH00`E~VT3``E zK8VFS960J?N6T=CK~@|Nv1p|5a7d*wYB;=v2p?mK9uBnpS6M@~5FmOuVC=DH$0Jzm zgpg*|i34{bGcfvY57JU{ZX+*I`Pmr7pDD%I4E|I;svXL*Ptgv>_|AeFJ3%Vc8c9~D zH0a?9g=mbb&>gUFDv7QTEq# zJOOiPFQj4&?S~O^FBWrX|9~8~1CO0Lo(C?g^cS!`4nw(_<9WohO0R^Yk~hQn!b8kud@I~T|4##rWcf`>*0T~P*I-@^Q+aJMD~swF0b zRUCXyp99*%D4jZPJctwy`1nxsL;4#C^gEWM-`(nuZ(qkD(cT6NkK15fk`FyUr1MW> zCb6g8__v0974vELSAc=n1=f%Fot~ESBKV#1Of`=7M*^$PlHC5hS(4UB>5KK1I{(PV zCbBxn^PK|zIapxQ`$f~W&3Y=D;Abyfd#t73a(gp~VXK~jwv%=ceT9Al`aHJFK|1uq zr4#G}TfsP!#+5X#&{sj9O??&hmHH~CSG+}t1q?WPxejAq9 zwd?ew(0d`>m-@Oj`cRa0JC+rCUF5m^6sW=v z1BpjN2-X1!#ydysz-m3&swb||O>CRq7H$o!Zg0YcO&5D$ow$YdL_B51htHPk$IzyW zdZ2YbJ%tn7wDF}i-PG9m12$c~bLYd;NUP`Brt*QTrFwT9e$MRUe7Gd%Br zO;>NPIig<|S3YI7R6l2Wb^1SfE!CGCMmBKO=UW}r$BU z7SG#|*5tXp?Rpj_hIi2JdXIPDeiln?CDsA|hGa-q?h5Xh-LJr2IqM6&=m}f>kcUN` zGgql)WxQ757$ur2L$)f_6?%URjEBh1RC+%2?bNGNU!#YR{W@g7LVpOzULtosfVln2 zLS3OB!uk-6chku6x)ZfN35VCP#J0^DZc!_*bA(=i1G_7yPBf6CS@mQ3fb*wRoEH$M z@{{_2kAXA&vZ0qbqteU=I5WrG^AFmGf4cR@jajUJMQVrKT>If41B4g|NDt*Qa_y>ym#lEyne+P0I^lkb- zNNto#rzAxtTb7x08E-@Ts-cI`=|L#h%)h@0xN>Fa*dfDNO zU^jB^Ozg(PJNtTcOK(@Dx54CPU5875&b{(>|yidiMCX^QBT{Y z|AKZJhn<$*V=ib}j-y?$k?A@PJGmW5qjJ*ovHWS@@++=e^}#@&#H^L}t+O2U_t2Jd z^7lKQ{Ox~&P(uf~116wq4nL01u%vZ&TT0z#2|w zbBFxr=Jc4>+7a4<40(1>{(ZdxBMwnRi(m}D*YRF>^}~Y0)6f(ErNCD2b?1^lhx{g7!bu>-R35yj&C{!CB9A1NFU&0zq*BY#Q z;L?_E{TvdeBhu{Gv;htYVCo34N!L5mAHPL0n{}TB|AMh*m-;w^4`ehwO|Fs>;xD!RO&Jw$H ztqiV*V9|S=1@@CZOp3V#fy2$TReu61FB7P*ElaUWz^4?Jv|scA>)}}e>166xRW+Ed zO#L5K>=8&$R(j}Xgp+KJB?@5Av*-?T_ty%lMZK8K-R4Ew15U8Gk{ zgZY!N**u+}&RO*LLgpc4mw3%AkGftV*9Z2w<*LFW{q35d)C^NpzyrLBI9-g$eZa9>yQD$ z^j7L#=y2VE^e(0MTUZQ1JS8;k0)v#6Ami#nzeGJj^xfE0ha7tjdKt$91<2%C1i63! zZ!$<}N6uT&e@8k7wM$EMgl3~q>?3(oE`fis`1iO&TLGwf?R&tqy5U0PIUwvmIO%8| zMoKZFeFTVo82Z(V_xK^9&akGUXgTR{XpBfr znGukwK-k@I;7P4sj8X>kGcpO!&*K>d^K*G{!}If<2vCPXFbmfQ5SgE8ZD1hlfJEkJ zX4=9)dI2%#=Y}XiSJ-=DK^=JHGNc=9p|&N^yewS~=VHSBNh#+Dl>-uj4dos=8JBZe z?w}(yALa+3hc+Uy3WPs?zG`ocUHdJgB-L*O34k{Z%OcL4Ys zHibM3>+?hg=%JP-0uWOz4mV!KqW7rz2L86|$+9Vq2PZt$%2^v;*-iU=MFIa7^aTF} zEdP(X_W+NgY}>zgGnr)*5(r2qKnT4hl+c6p-g~G?NP$2aDG(sEP!+^br56oF2?7Gr z6%Z*>rK3?nKtNFuQIPtb*WBmqu6gizp7;NL|L-`yIdbN=_jO-)zo+fY&Lq5^Up+kA z|D08}A;#DJiBhw3*08cT9dS&?nbKXDazQXUCu0KgNQES(En?JUG}fW9Fi(N}FIdC+ zBg$BZWKEHXNsZsf8FLab=8TXvWi#x%ASrz`s_R@qK7m!n#Q%yltUF@3g2F%F=hi~ zN_T+0C**(98deR}tO%PNGj(f?DY)^RxqI4)PdH|hhvdw*p2zMbV=xRS9Pu|K-niV3 zcZ#lom&JID&TMPNo%DV5Z+zddHp9ALEpgr~t|C66xMkRF;A*^E`?d%+aKsvSBpAp3 z2MQsgyCXs+n_<1_mL(~MJ0i>!o^Iu_VYg0kL@dK6>wqz+WC2gN8wI7_^61|;tR>qZ zVxz-P-6~@Ebyj{etiM^`!Evnhti#DHKH3`F5fOJC5o!l5YpmbupXF@EI6YY{&$q{I z2?Ok-cVO?cE zBK0YHiS=>EUhZG&Q#6mWKI-!zc{f7gHFnv&50X7&;VgR+KI0pyy%NecVq^wS4Mg^F z$lf(q<9?vs>b8|PtV`t3H1qE+IF$ZfDrP*eT^jGB?xK0B63wy}t%X=+due9$ZhhFU zIx_kTpRC5sE=6|Q9*o1+Dqzb77&7De*t4wt@Y!m-exk?`-5pa%IUmvA7N<9?uS>g9Ajn?Maq_uGI5SLE5T#V*jG;6h6dLZFdE+@>!{Lxq{Vw^RMcB>^7G0qxByVa9< zon`Iji+yle`1Ib7d_K0i8++sR*igvcxY75BF-~kw!0$BV|EW43lh0qOaQIG4lw-0P z{}lVV!uZ+-;jnx~al(cV;c@tPN4Dq3>|v#fV0$OA4GZIlNj_x5vJxLc+u)yMcnd0g z)E-*KC0r!ZWAJ0Fb!I%m$k>WOu;b9N@Ns)sTwxTCaM)3N2*8I`)2iM}N+o9$s=Ax0)!*in4wYq&(>zeN8iE?ewjRgjBJwy@D?##nJ# zY@Go#kTKY150%f`FqJ-r3U9ZEMZtfLxM4BQg{Gm~XF~patj1}~5`11R;eA$P4h=&_ z_#7s?DPs;DF61(YW=b)bL;kOE4o$%anM20Zk$kp5=F~C6=G1WsbLwp1)H#G~Wa_N4 z`l2{BW8^TyF=Hg#?UFTS%%5X5mYJwF@xr z_Oz_G&VH47gEdcGb9>uVwriFR*4TQcr4=nvJ+O)NLW_#Lr_IYio!_@EXoBN%YZ?4k zhbV6u-@d?9G8iUf(>liZx=U?Z)n`Pva-?`$rTD~~UjSJfw ztOFbgj5?{Zin)|k%qVN4H4}O4k$j%I=JV9ZXMjA+nh}PHnhHCEw_UQ(B=Fr?&fDB)d$wr|2H!A z#RaFK{ZP}X$i1apol^g;#%DK6_nEM(7RZ%Q^(vM=vtjI$DfM|(1J!&T^gWt3vf?7^ zzP>nw0Q(LUzGF=#CxWVpi>=P4FSa_HzSzp9|MdThTHeNGv@`v2L?|tAxn?{>dV<%C zB?@arOI%vIJOHldi+XsGc&Ll3(e~6|&`TSvje48w)t{IoRy_Z7keDZ-G}YTi8J5)c zgT*`(IV-b~AIf|0814^jXIhQ(Az07BS^%3oa2&*`+|~D=8?3$mWjnRUk&n5ZN~-$zRHNmEC)w7~Uf%wH zix}jI6{qkRFXJ264c5C2Frnn2x5yJcUpo4EgEg+99KKGq1>s0hMNPFa>W-s{(^%Qh z8o`O1M-P?0o1^~5c@uIBaMa)Ihxv~Bt9}^$M>JlO(G=wy4-9T*(dwcA?~YS&0R8?X z{F7I;7;ke)^}^t$R+E1n@XvVUt?@uy<59cn5xT1+)(fMZx(@%0=h>!KLIK8WYDUYA z0v7B?jC+RDZ8%$peuoO=!P8-&CQvUZ7Mcjng6=?ai`Yr%CghF-PEW{OQkb9ky@1@| z;{|<7|Np6<+`GI4I)%Kxg3RTg?DgQA@e2R%VEYZ)s#z@8AajvhX8obcP`h0Aq6k-m zYCwTdJ;+=nool>l7n_-%Yuf*5yjiB1PUtwbNu8ywEzl;}Hkivl*~`E; z>J|R7K6Z5JFEc;ae%tJl{|WfV{|EnlF8;gWFWWS8JYJQ*tH1b~+d#R=)Li8AN=Po& ze*|rZ_Cn^8JKnVCPV?W`vP~}dU)XEo9ApV3mzv~` zKy%rN??U%M<*Gaaw++CgN8s~LRTSk`4I72p<%Ek{txYOF8=0)F}VyT7rV^mpX^dr5+w0* z0nA+Fvs~bkv*+V3cDc;!YL|A%^<{IB3s&KBJ`BkPCvy>de<&1^cyp1fNOI{$zTT0` zG3MfGm+M33;_54xZp`JM?A768UO%bl;{Q)})7QMRV&?x(_Sf)T&v57tbRRMoUwjUG zh5dWf;h2m41wMO}G*e;B@cBEaf-jCuAajv#0P~c>-Dgnk()eXeuP%Uzy9F@cAddEcks0ZE^9JubJelQg5Wof*P`pW`6%EUcORt zO&^Z*>!8nb<#-GW8@5 z&9tBi7AZ=?IBBtzL(8thDXg4Hj_JN;3pF#&| z6Z>cQd>A?coq$e5=OBqY4_<&SLtjHV(Bq0)m0~$7(qpHL*q=8bGbSxhr(Us>auSYJ z_n&p3L)BTm-yU(~%&=MCRk+vf{)CM)^In>;qsz{$nY;y2^!yOPp80ar#{*klT)WVF z_}$Ek_vepG2}&=x_cy)hi7N?he2Nuq)u`Fam;D-L)tq#!#tPpb=6d^`J~+Bzk6pKB z@7`NXf9v;+-)Fpiu2<%)_I&10DKj`kF^v>(I{D2vL z=jGfdKkfVQJ)ijcpI2Y7aetRV)2?i{w*URyN7Fa_QuuV^*c&5%Ok4eCQ2uutj&3-* zRKBA%i=4MD&EAqfU$M2*{btlz_3o&*^M0zkTW(CBzGzU3!>6viNNRX;)px76o_t<( zXHv;Vfx`#>{Q7_q6)WEVdieH5A@O%p%lhoxQTfS-={E}9E-)y6;QLEDSDAONSB)xV z9KD9vWZBLiTI}l>WLbYM&(TrVjei_y`I%es_R;whY7QCoPP%88pb7U^ z)Xp4LbI{^X^ZTKb zuJ4%eMyXSOtY6}rzwfHB5`)_8Z}7?eqII&;sx|D9e7Ms)*Ne0}a%hxwY26^Vb^Wj9 zt$y%_(R*?(oQ>`mcV%m*-HrS8NE@&Ud7pZ;r23(}9h2{lsvqC9w*Rck1(V%t`F``{ z+`5tZ3od$Gu-$^beHWkn>7$qtZ(AZ#sx_?e%O^S0e?6-&&)OBZDWG2F#^&$d8hCK^ zdutMwRKJ?=@u6amn^m9v`;zLmGn1QksMWWs?Z%JGLgy|_?`9p6f8W$<6V`5d&nHix zW$o5gzmxYw)bjLAGe+#Ub^oeR*oY<7r%&s;^zyKN^+DTd4}Pk@!)I&1U+1rRUgU7Z z`ILJhQ`gt|@?Ox?1K2>?J@9Qm6byBO!k|e|CX@wjhYmwmp!<+Jj#rC8)uCW09m<5V zpzY8x=n8ZPl8vtqR2~`##X?h|#n2Y$7<2`?1KF`5_k(IeouDu%5t;;LLRX+W&@;#f z8+1RYCe#WFgXDIdsnB9*1GEpyf#f*Z3&+XwRd6Jf2F-?+L)p+iNNzF7fgVCZ*g>>| z212pWY$yxb4jqGXAd3(3gMy$=&?G1m%7V5-SD=TGJ9ad_P<3b+G!xna9e{EmpMuB- ziiA?2snB958`=lSuPAOp?$}vXh8jSfpn*^(v>Mt19e~b4*C4t3-d+e}1XYHbLA{_9 zXezWA%7%_XSD-u4Gsp{bssYpvk{g>RK?|S_&@Sj0bPak4xfj9LWKb|P5Q>E+K?|TP zXgegg&pw3Qaa>vqstg4~-JnQF?m3+bEr7D1UC=S;8f3?DYcWWU@q-~bHlGSDfHpw; zptF!1A3TJDu@mnE4TR*zs0C0KB-bntLw6v#&}hdoT`{OKGz?0EW`<{3h{g9uV9rsr`wJY;oIpD0 zqcgaebN(+=P`1d`=kRb|_W^w*ibU{__+QI86&hujGz<>m>K zRQLr#XLT%?Gz#kl+P59;`xxzuK>OyR+z!VuU!cg-*sh@;=X{Bq>(Doancuc%s*J(N zcPk`IuQ+8}iZqAeFUw}+E# zCL!Dm_@SDIJ1{Nc#9Cp<@+J~gxv~Kiq)aQKk>;k!QG?1CeO^9Q6NQNS4GdYCUg?upjzoCA17< zvI1?9ZC)D6E{U;N2-U?{bjDbOVk{bQEDj;Pw7JU1*!FQA%Nwl4_6R3vkL6?si`?%i zef@}YV)OU()&G~VEv$$7R7bol>vF9lXV6bl*5?@Ow>j3o zBM-4HMEOTq{;p1HpB9OBd<#uLTNa@$8_<>*wq*_KB>TO|VYn`Zdi{)gJwv&tP_G|Z zFaHeHj=V@KOH>C{&)nOw{N*tJ5ub)UQc?e>&}if}5qb4MpMHh8HWc&Q9p(Q8$#N6E z(nsdDJ?{wB4%hWme)ldu!#GcV#JVAMP6)#>4(j|h^7s+u>_s^jSmzf}YMp$7cKcwP z-yG@=$?{V^m0t(0moNr$`XvSWZhd2|2?ShariEU&qmfa?E~yr5#Jy2!=WCS13lqa5&0g4meEgs zbuN8Tm+MIBQmUN=M^2pSV))(hDr_D1>#g6q(Ytl zVIAqxS@nbKaow2zFxPjpFU{@rhYM65dBU9iD|cDT_`A;M0;q#KY=2_^6^!*%JAv`y zzF`RS{PQ#%u*d@pr2f6xo(9<}KR2u={V}&>$)C$-q957|`vcTp{5rr-d}UuP+jZGr zo7;AIMuFsS9)oxMH+9=uP}N(GSKC2y%sLE`r6BUpJTa&*^1E*-Zy2}t z?MJF`%`h0T)GevTHTUtGJhf2rGPe)nYaOoAp5pdgo+luAoBLgPo}Fxqx0zgYxLz)uG6#8H)4~1G5h4jWK|z?emQA8hmFyCmrkv;9j3 zsC@T_;c-fkEK}GAuIISDUs1k1Nzt{h<>&h{Msge=#{jalZ1?}pIfgvHRmw8YjpTVN z5`K0_O2M^HY!l=-<==g$P!wY#^>f|d$}?glA6frpk!QI|*ql$RcB`<=X<1^ij{m1) zxH$BKMKgl)UFZ!x@^VoZSlmFc@^OvzKANxF6mTXaBbHA1+zv>4$ zCNq!kKTcOZ<~gk#D@k9u9^=kMzOoJar*j&4_^h;3wgs}tGo2)C?hn^6Ue;Gxy0c9A z&P0}@G0NZERu;y3Bz1_uHf$xg1LLso6r1e#&Fyo;K`NcOjk(~V!qYL=W$E*wI+m1k zH#sMl^EWv!mvcBdHw(jE;ha5uiKQgxN>0;ZmE zUM5pdiYW>ehe|@eP${T1J9aQ`a&U4e@OZ-9Eya7Lc^gEkmNlQ#BSTTMw&RFA<4%B^n|=1A4nciD|T}!%5ZV81mw%GJUX{D z%5SI9qbd z?Os3iDHKrP)(<^qg?)5-<-*4?cbkrD+-~aIdEUCY_QN?ZYEIw%;A@}cw^nvsRjKW| z)7sQ)6*dk_Oqktc_j{$ow(MEke$|t12mgE?pJoYsKK7T-zYJ_NJm0~@Z8K&ZdUKio zhVqfW?>ctzet)0Wvo9>4@$JmA%bu;wbX(NBfKSuCMb2-wyj~)_+r7X=yAIqr8~od~ z;d#I6?>A|6tLGUfD-8Mg#FzbFdbbIwpFd}P!$B7p-taB)ebJ_S7MvftXH?QRN%wvz zUDJJ7<06uY{!(MbhSD8OP##qTt-PEqsmg6_O+fKDe>3Ki#Ufq|8kBT%I+jYaZ zyjw>{{Q2Xs?<+(P zu1^?yWn1TucQ2oFI_6HwZ|MQ4kJpXqXPMExc;2#4&*@*54xCnP(uf0XpOh}$zgd|I z9vnpF2FICjvrs(&6nSo3D5p}i-iEl6+H zu=}iQ%N~E4XaCCdo$Cr7uUF!O(;Ei2@NFDWyk6{!va|e3-h2Db{r0Q;1~mDrsb;)~~*=Q0FB#ru978_wlOa4_;<@WHjH>r9tpd zZkoq8D*vE6JqtYiF12dSQ=i=k21{&2BGcDM6KJMW9?^H#Gn@4Wj>x0!jbev?)$u-W@mtBmhkjD-+QIAHR?T=>RR3-0{VD|m zPhDP;+%WRl>|aNpp6BB=YJ0^hU+(R@ed?pV1vBz?cs--!xltjduN-gGDWFX?pNxBH z!E?7QNbg^&aQO%Ez6}O+nm=pzuu(awZWVk!3LO&k-NmlMr-oEYKVL6$d(BBR^VZ#R zyH(JpXR%!y_ubuKY>C=g%eDlqUVq}R8-EOQTXub9{@7EG-)bIu>2&1v(=DocT$^5~ zW#RRw<_CXNbi+4QOYLf~<;RY-cRf3Ipw-5)ty1PKJ9PQ@Pve5N^!xd}i<`f#T4{LY z4FLmvTW0;z=IXl5i#(gXf1$*taTlL{KWAO|_GKf}Mr8k)UU+{_-kgB%9{twGElEq? zJ$uZBh;?&s9Gi7DGOE$qE|a2`UGJqoNuTJ`z0lF%-v?LkUpuXzC4ZX@)9ho0t;=~d zaP-l=Kj*ZY8SvxN%AN98xbfze9oH)Un&O2YDMCx3P0#`8JoE$f7v$xQACN$`A-T^q z1R4QNfaXG3kUae03+Nk2$IiJ7R2ymyg+L>q3D8_93)%^N0eu5Kf;@4|TnefQwS@XW zF;F`6HuNsE4>|*V5B&kzeX!q$0-)wlFK8$<7J3U>4Q+#tKv$uA&`YQw4jd{#4WN$D zU}z*X6?zBy0NM|og>FE9LLLRNe}Mv_7Eo_!7&H!=1FeC!Lr0saY7K=zBcKV;Tqq0L34H;51Ig3Ww8HpK0IC87 zLtUXEP%1PNS_XXx9fU4Gx1lGHcMnB3QijswqIDsj#zJpF ztD$Ys5$Gy(50ZC<$kQAuKnOM~krqWtv zk{9-2hRafTiZV6EVK_~4^OF@(L$R@|22zPXG>54K3K!E?=n2+KUd}Eid7(PV$wG_Z%G;K^vYcy+_Dv#YM6iDFue^@ygu{>n6QtYf^^R8Xe1eva(ZS1Z#` z&JJm^R+)M`*W3qem(QDygiBbjrOtY_MXXdpe#*>B*w-jiq{ zrQ#)TFQwN6gvE3TmEn|6_Ee@HvHsC3xr8#^YwT#yiwI?MYwxI6y?)A6zp2CP0w-g* zb2295FhWX{-~KXt`Q?|G>N{&MKb#Vi93UcrEU&Y_9y#-RJzRNx+ssjk!`NU+!nznC zcJz3b*vwh`iJV{QZ#XKEj)amxFK3t*)px{HMztiyiZMiuy__42kfo}yNzslLuSelh z{x_&Co7ZBTO8*G`0#m1Mj+pfom9tw}hw0ZT$~4cpxx6=4nW`Wmn`B}AznbYtC_l56 zf`4#!&uMI}#3XO~V#3bs*W=E?bw`wVMeB}6w4mubnAnJ%CCaO?v#DXrlA$q&y_MOhlZs}jl^ z1UZ3kaQ!%g^@DTg&{XAh*x6tAnEgxa#JKTT&PvQgC1^U0N=dzHyE{xb<||WgY$;fY znVbh%&dw=`bxaZ#*y3nVjkU@oFBoJSGsBcg-Yv)y1J)?hSm*Yn7dlw-`VlJ_OMG*> zGG+C0m`dbPrrnsX%uC*bC<*0~J4=)|9E(Zbyhzh|&WT!>E;LQSz>8NUMA78K>_0^I zG@V$jylObBdX}>=6k|ZI1oWol^&zTEliZdrrpwMva?M4`R4>ZmwSpze<1aY07ZEE7 z;`Z?>hZLacKfTJ*W&L!4spmR-YF->{Zc5W8rSD#YiGiOyR zGW++PGrChT<+Z%ABm1lLYT&HINY1ZVl+2!JK3OH4jwqV$p}A6G$8io*274kLQS|x& z^Ig0KIxCUCvocv;cT}R%+sgFTK!-`bW|f3!wVBs(nkr$EGy8t*uQ=yX>sKtXH%jDi zbYa5n$cw$Yb+yXAsI%EM*z6r3y#m>*Pn`{#f><}?heK}Mn=MJ3KJDJDO7H3#C_ewKvnb;n)?Y3C;%^X5g?C!0OY|BfIQ{_II18I z{Xj?_`v3$fge%CCKVV#^a9ZK1g1F~jmr~>&nt>%;(h82|s|FK$?(q*~B%Lk=M!RJ7Ft>j!f?3_!3ned{sT}_a! zB+QR#hSfxtJgb#yrqiqcT4j<~pR-;sIdi?cHC4(#_ZnZL9 zbGA#qx037!V+feneiS37W;RE{@(7D*9>lyJ;Io(lFcDd=^=y|s=$&4Z(Kq6ij{4Cw z7278WE>T7C3alW|5phjX{pD*YGEK6=C<_yj}1M#RriUd5ejWcf+TbO~fn9x^Ahb5%Zt zPL=Albk6Tbnab40+4|m^GL>-dY#Ys1rU(5Ujc}*e3FkmH=k6-Av7@Tx*=-Nd@2ve7 zoIn+w#|2MxmHiAv(X0CuWtxehpsDT}Wt!$(@7{e=nSKE2)tn`kaSl{6dPJHX=v+IK zmnze|Xh(nDo~%q?^l+H+pm0ej58mO}MVC;f3C_Hl4pr;!z9vQ`R<$w^)WLwfc7iqA z$Bkbq#;81U#d0D_<@TtH!>jmeWm@T+0-M>&tC&`-#tyskl5f_TeVZ?oDS52J^d~o@ zNzS}#U{Iw=g>V$hgu^nFDI8r+(`^i@c=d8llmRGNOn+dkSo;PA)oldwm`^0^QQyGm z1_p8(8JK|HWTx_vdCB1utT!~tkGRFu+*zAW^Ofl|x{;0jdYUr5bZ$xyq2t_NaGHE4tAwpF0a(@kWmLB9)u!pgdXAWFCDgq*`GXD9 zFD(sx+SY))n*cdPbu+Lj+<-LdD(e>FoHgf*svAt?%WkAeY;549f~0B7^8IcR93T29!d*`2N+2DZ78ggx}yMY8JlD((-UV`G(&FE!?ns8 z2?wjG41;Aar|3jXQTFaTt5tgIaz`n1(1lXLbPOU3_FJb+FWz^Unldjx1ngm@UGiiIcui5=1B0bLv&?p8 zS!R*SvLt$25@FFcDAUu;j#9MA%Jc`w4t+LSnZhPH`Y3arGUXZU$Sa!NUVMsm z)HgsjqBm9&G1bB$3o8+cDvIel=lb(gm@>&5=INCfu1s;Q9i}17eu6XmdcJDf$ZJPX z;u<&uTRRvS;M{QB%22t@nCfVy{Qv9Y6RFe`h(S=n#63lG4ogjJS% zlT==ZCp#KmjfcxmN;>L)d$sav=^W4^oK?R&XVoc89qFDS&XLT)N+G7>XeH|<_q2#< z1;nndyhfQ4oY@z(E7PyegN7MAM4j$jr_6h^BtKc;Y%CJaV^Q~-(e!@~dDuskMQc8y zotyDP>;pdPAr`Qiw`XShWVG@ z#U?*c@8*jO#+YWVX<;%J>)39cdRVOK_B_j4y?FW3$Hz-|pNaH-h?NcI;T_Lk%2>J| z{p|V6=K1VX-CB6Nx1X-haGxSylpBk6tdWD&Oe=Yd+8ufRGEO|qbZt~Ri3^5yee(RJ zjK3qk^>p_cnksuW%)>r?{!-?G`?kZ*vR(3+1fO0gL*|Af5107WFh4x-{H4s3SH&NI z`S8)_FDHYqioXVP(6Q&nO?|J5mzlBv@iLck$72YMo7CKoKYwY=E3*$^m$+c0ErvLm zUyigbrfbqSA&3h`oXj&voJ+gXU|;^l^OrK`UKPI>@h1^4ee8&Dq54+x--Y;lh?lwO zh-dzOF!`dtI-Pj_G7Nt18(qeoKA2LLXNa45^7%_+4z$eYBl-Fu-2&KTp1vyI6vVeW z_57vGS@&`Ony(*{Erw6#>E|zH4!d9eS05=S4L<2-p1+hiox7ZksF0f_Kz5JTr}@0` z-&l8%n>Da^a#T`sVCv|ooj!B8Ckr*B^B7A66a$tOFT;iC(h=io5z?k@m*wn}A#|(*y4-5!!G$f*0wZKue{x23J zLl_Yo9zQg&en6donr6X&?eR2P83EiK-n{yFql_sM>YI zYexh{*Nlp&Rl9E8hM_eBA`%njH#EsHL*v6^Q4^FNnivylRy;J-OP4u!!i>LBp;KVA5&5j|K9fS z*r5qYF)731%xWa2B$@RvoDF-Z<0Cc^N#Q98Ny@CcDKsuTEfkF$8gHaTm&Jvr3{!QI zmNhUllt8U|uAzECj!>$@6GufDKZmOsWCxi+CPh$YW4z2vnU0dsr43S~R8OMKlrG`a zcnnIUqx&$N%w|-$F6CvZEw~jk$nyQm_iDOk6*Q z#tlhM2~TmXa-*@4Nn%+Mm}?`Pg<+u0Ktx(v_z+orQF5@+{@1a4usG*teAH+)MA517 z5wdv;j}09Xo@~rpdBBm5bsgtSmn~Lzf8FY-ZP)F7dZkGBvbt59lOF9}K-aaYnmtAL z+N*n~ijVe-B}LDt9U13V-ilD!m3kigq$%!&^%B}tua)r7wHL|u4#>evdsDYZyL;;8 zw45<+6|H(1+e-K~wQ1|zZyND0teMDaw$<*3f~sg2#;3R!(kp8@<4_47ZIxy}W>jFB z)gJ6#R?BwFxP{z`YhR{i=pOb|z2r2YtdY|S8(chff9>WN`vdnPdNb{WPo@;6olmwO zL!B;qW}reh^V+lFR!93F3T-NE3xdybhJoD`-L^b ztg4@0)s~_cv1y$;ume2xTH4W6)w>^}ca3raUQy2fzS}TR#kFtJF4+g^1?)|A?<;zB zE#Z=0&K9hD+q8mvFh{+#uk=fLC2jviD?Wc_i8d%Q>Q@k~w4dpo(EqMUUG?g1cBV{Ix~t z_LZ0_gEO##m5W9_7rM*GpTrZ2&XWFnXdFmFM{Zl=kr~Nf_pFLam zuyvVYtOL5oUVAE5!%|w>NQ z+A~WBU7zdjt5?&mdxjwUM%t=W_zl&4;N}sm2O{3q1R<>C2&Eu2L@S=Hm+X%%%_hBA z7u^$%g>3fTx;uQ_Z1!eoMAS-DOjbi%s&2Eb)XUoJEA+gzS559`6t&zlMElDkn}~AS z&neM*QJY(hf^cl17t?;75MT=kcB@`60Cl%%Tdnp?cdK65Q@e}u^Vm1*dAsPEy$O8u zJhtPyy&I5;90I`%o4u?=6t!u|_7E}P<3|Qdpf|G7_6NFV(>$V?991u3>!!bE3(!j$ zj(gCQxq9GKJ~XR1*7)py8RE-r;zq0Her?EH%Yf^KH|4mFEm%Y z#nYyJpPHc;y6kS%Hh5z87@4F-+i^1BUL2cw`7T0xVzKIFJ?(xrdqJBvHrTeuW)HGG z2$oSm!#|v8576B_?LG8h`)a+5EnD}xq!+XGL;da1vYGUd4TN1A6QY-oLf>OuvDyF7 z?fsm=TDskK6OFTPMk*~eThD`F7m?8gX<*1a+A2=(JD3~-dpT{1Ci@kCZMBDli)pt;W$Hz$>0V*F&s-ye&+M|J zETipb2Fozf{I#*-T}u0DluV}%x<`ogk_Xl}A8kyu?mZP#r-E%BGH9($n|KTZWHbZ4 zHy!&&)HLmZUTZI!qo5YPm9d1%?zXv2^*K{m~;wep!}GPM3J>rLIu)7}v=EwqQ& zNy&C`wiOc&>%DZ(;|Y6I@0=T-VPv=2&Fq{~O?8hXiPVmbkCw8$wcT#$1+9Gt*j|he zHcWfn0+4wLRHFmJR_)3JtYI@$o`=TE2FFAD)XdY!ZP)k^Rr6zR8M^nV8EA78tYUhd z4T$#EKAspX(RH*lmK4LV83wVG)Xw6*Eo&X!@*YYo;)%v(8yPN2Kh_fQRkf>9J=AT3 zQMVEj##$4g2iUY?A@1e%_S!P5s`U>Og0-Q+dciLY|M$krHXH{Q@Gme_GS{BjaY*5> z9kXQURcs+T)}=G!gZ0Ku;a6L`lV;D<{R8#NHe;uP?TJT#>_MW9gM$vJ$-oXs?Abvp zBzwVwMs;4(?xqFn6|}sWMyu9N3}~W_mfBR(ZdpPw9Rg62hFRi^LzeP-TbooCqqQ`@ z%$aK1nFP$)s=9ZeQNN$c~(FFa!Wg_NQC)xPI z7u&3D7&95Gr4yyUe6)`ybdU&byIY76K510gs-3oCqpu}qOvh@wZlYTyE4KN!u$2hb z^V;_69_iW`SqTbg&peEMYA_leB$chNO&=w*!e3kLnQ4UYq()<=2T03pT9_J753GDf z@;^K}Xy2iCt7>ad0ZBe*Q~=ziQvNpNE^AVKZR#kMhhd!V8I1122$}jF z($vwO+Ho|Zchw7AHgkJ1O64}y;{l4zr@ft)0?*I6eUNtffu%2{uOK zSQ0krg)y(+_wKOC@R**s=XJwn`YT-KVo%plTb!PP!^3F3u1%{FfEpGtg6?f`q=Ca> z$*-KY!z%;Dj0u+GNSlT^SV;RpyM&o_BpJP~ZSr#KXLV#1;>_xpB>1;kVYiK}QjDym zJHA9o1+>k{IP$?~oH*G~j1302`c}+*><0@#IM^~BWtd^V3Hwbwzk_mYxd&mKgFyM+ zY8S*An6}3&K(B?BLl4%Q+qBPOTc ze|qEDr4{4^)UxIUu5J8+a8q16F~c`-9S2u+z0bp*?c#R|_S&$24*Ruo;;z!~NBF*c zfzR-cT=}i`w!FJM+?l^TKv(L4hv{bZcS)C)%MTCS{ad}{A-pw^t{3dPUCQr-aATMH z%e%L$!>>90>qy^||Tw_jaAi72c66yct)& zDx)0yKr$coU}Nwg!wW!sujSnrtOIr?8-Y@TUS;n+k?xZmVB`HfnC0pZ4Dm8Bxncj z261DYq6V<(31b+fqbP$w&dLNW>U8KXH;zSv*j`yW zfbzh6>9_n0<9lX#fQ}yYY(2c8xODX z?hm#>xF$FltOiQCCBR`|VTK3fQU2AzmazL{zD0ua0x?;CeL!h9E@wIVc@M6k{)l-c z@z+6#|59_t&qDkr*d?E}pll~rfU=#K1wV5;@dEc*OoLtW84pT6(JuK&JYJmV7)J@0 zf}iAD0F-<^K*{%!n={`D@cRw(Sn|CKO1@VRZ_X2mKa8+BKP0>mev;o?pyW5sCBI)V z5UI>Bj`{U*$xq^&y5uL}aLhN!4`XNKhjql!ZuyB7=8RFlhM?51vdcV{cyytoeiH5m zKWvKJTY)+#*SUr8ldE0sa52J|wgxe5h8-}%2f#4g%d-!}wDH;nqK;l$K&%2@*GK_y;ELZAo@_=iD}s7p#qpv?uld!DK7~Zdmpj~*^!io5=eXl zQXW(w;Toj8lw86k$RcEZQr`V8@y{?gLU}QP@Gg0alyzS0Uy&zBc^$ggcaidv0|~Ds z<%L}mo=3`S*d;uX97m>*@~Ul#mzRGD2a$b9d5N~zTaxkuE(tdvgUAYGS+XFRkIX~L zk4+@KytqMlpS(`Wi^Ro#nw0m4OL#B2lav=hh&_v3K`tTZl5dgI$qaHFIhq_sMv{Zb zeq?vDGuei0K{h7qlaXdZm%L7XLtY}!ktfKbq`Xu{^4U(VCD)Mi$T{Q;atfJFrjaAbIC3!ApX^D> zgL9(JxkR{1Nqz~yq%FCG~|39&_5k4S)B!3{kBfloklV?bIL64+A zOl~LTUR1HaPp&0bl1s@q$?4=oavUiy1d@Cb$#8NI*_o7=gNk1Z@^!KvDGxD{_-bS& z(vS2dy-5%933keo?oaYId4tR$uaIAor^qAZA#xkJncP5Tkt@h0M zvOU>|tVdQN%aMghAJT)=$v<)6YP65ML4HS`B~Oz3$=&1!sbn-c zgzQOnC7Y4*vKFal4YCSZf-FMXNH_8UPBtXnFXV0V2KhC4nLJ8gUO3z zCD)Kk$c5w_@=bCaIhq_sMw0S_7Adbg*@AqX3?!?OzGN}dgVf1Caq=bkJs@w8-;rlY zc{!c9u%%1mymCg)5(eCI5LHl7XwQAP;vm-ft2eJ5|8T; z25XW2WN9)#nU|Co5lXtpd5Jtnenx&uZXvVD7331~4RRWpN{%GM$w6cn64x1w zddc@&Qos6SWwJb3iY!igk~UI)uq^52#pA*omtJ2 zZ<15UiKKk*DCNhJ@@qN?%lD7Meq?V_p5-I< z_N06tDPj5ft}u|SMwTc2$l_#S(nh+GzvFvI$>zQ>es4tbJ1Mjjydk+|Ms_-`fO zC0CMflemsz_@$F+goIZ!Gp-$lK%%GKaiE9w$F1KPEpS-y>I%ndEG8JUNCOPDYb`$)02@ zP`)2+M#}Xg3D+P?lY9`8dtQd~kn)QO@qdOdUxh!Dcge5FOXN{d`r|WF9#baaE#w+< z1^E{F2057=PsWikq&yT&@*6~U0i|9Y$i^gp0PJ3w;qqi*vHxT!AQzGI$SLGRGL1|j<+_{X6GMiRgUCK) z53(cKmTW)9Ey9k+w)X0bKD*irs9hCZfL!Kg!lY7XI$<5?OGK*YI&L!U> zCy-;w5#&&^FWHmqK(-<4lXb}Qq#s$FEKKGjJ;|r&3~ApZ@(1#J@+(rF4Jdx6$m8T5 z@?&y6`5w7|%p@n1a(zhhizSDVVdOxvCyDDuhJQ0Mn5;ooA^pkHWKr@pk`F?0mq{Sy z%XKB;&*Zn{SLAu}40(*i^(G_T4st8`F1eDNL%vB)CdZTV8%ZfYksL}!ko`$qpECS9 zlkG{l&MWDgkU?ZkvNBnol;6gRe^D|Y=}EegFW@A8Ka+RK>*P1&N%9zZfZRuBlN(6+ ztt$T97m!oQN#sZ}jvP$(Cp(gD$$F%`_yK?J)yPU@F;ZSQfj@V5QUVhG4cCc;Ka)R@ z-;-aHm&s$~VR8q#m3)_6NzNhPB*&7Y$Y^p1*^}%_HY0<{8e|pHpTspWqa8TyG>F4n zgILWC0tS2MsO?*KvMJe^3?!?OWyq4`J#+|G9rruraq@F=9r-T#COMrPNyd>OWG}KU z*^&$(tCFS35~Mq6A@6>r>UWDgOP(Ybl5@#v!sN57s=UYKN%9!^ zKDm~hPG*q9$!M}W*_muW29cG>a%4WzlYDVS)#nL$le|VAAor0g$)#j}vJcsTY(q98 z>yfvxV#s{BPF^IxBsY=olXK1~|2N4bGM)?}dy%)lRQ@-}Q{-`SE4hh$hn!E2C&!Qt z$RP5^vnu}|$TQ>#@?-KNavAv!*^}%_mLW@$I%y?;I-|vNXQDivT zo9s@uCYzIW$y%g0=|NsNuIhJ|oI}1z_9y$09mqChBeEV@oGeUgg@nq-`m3{!(hHOEWAd8S1`SNp> z?tAiEayPkye4kuPE+H3^@#Ju_K3Rt>O_m^a(n{XLfr7EWBu|h>$@j=rLTbHY@pIl4MA>Sl3$O&W;8Bg{hdyv6o1F|$(f-FepBi+aspQ?Jrlf%iO zpD23-*#*?lpB>1$WG&K{EJi-vulyg8OFkl@SjqhPoIFTolN-o(WGnLiD7AjwBM*}Y$al#3 zhsZ2)HTfnvog7GpknPFVWJ9tp=|}pKKBO0EA)ke+dc8x=CzE{CeXkwJ1^F<@`0HX( ztVhZVZ)CeF?_Ch$_bUedNMBIqM=`Px?eg*q@%N(LPP)@BH?>OqGf$QNF?kOZ`yKKo znfv% z<9C+KeO+oF?YqeBNk^|N=_nE$V4)h97YZ#L&#oaH?kEd`@3djFxh|% zB5RW6L8*@)=}Q(Py+Fy&PP&s8@}aKc@00h)J7f-dg}gwXB@dGa$bIB4GMn5$W|6DO z1^*9w?*bUtQQeK-l`LC{f+gcT3?yXJ7_cEET3NEC#0i$AwRi26EnBiJ2Pf;*Y9(#F z(r)%4$$2lzz`ncQ3x$<(-zvag^;!cY(f(AC^#v!;qfJ=K(H~ircmQNi2uKH z=FHr?56O~NN&Edj*48~|X3m^BbLKsF=3IdX1l}WXzrfoBjtEQ(ObF}|xKrQ`fn5Rv z0(}Bo1+EfUFR)hN6#|zCTrAKn@O*(a0?!jzDexssa!B8&1=4#)a?^W8g1-=WOyExh z9u@dQflmwkroh7jzbf#cz%L4XSm5UbJ}8jxEmFD<2>iIf`viVK-~oa62;495Zh?CR z-X?HFU`$}Yz#f4+1$GK-7Z?!e6Sz`fqriHBwE`ClbPK##;39$N39J-2N1!F}B{U8$ z4}rfBcue3=1RfRm9f9&~qdDIc_hErw6!@^f&k1}`;AaFrAn@Y??-Q8+p7b8^-!Jes zfg=Lb0uusz1nv~LLtvM{PJ!(L0|I>lTLrEXxKd!Fz$*kU5x7{OTj2QuYXqJruu|Yl zWuk9^zY%y+;7)(D&< z&=N@Yc}dRG0_8K7b54r;CjyTO{Gq_71?IoU{i^sM6!YLH z^WG-@BLdR`69Rh#?i9E~V3)v7f$aiY1+EgfQedOND+DePxLBZD;Kc$L2|Q0=rNB7? zErGwmIu+=glLCJs@R-0K3Vd4NcLW|0_*H=i1!B95`C;3J;b#OsAn?-y?-%$1fd>TM zBXGaKy9MqQc$>fxfiZ#o0(%7R6u3iRm%vVe?E+f`t`fLXV57i#fwcm!5V%C(Vu5af z7Yke@@O*(a0?!jzDR7QJ0M-}PGjug`?g2!TV4LQ+LNSNx8TnH^BY&!A4E4-4@5_LAUXifH}FrB&3*hn^_F#ImOl4;t<2+=BH&8=U4_3|{CV(4Wkcn3 zHU75YuM2+{;_nUkqjI9}%4F z(!Wb^vPWM9A<4cs+3=_JG2!$hP+fkI?fqW{2jOJ9ej|7hP8XZsj{+y0?C{gsO~Mlp zmi8wICwuz$3QiZ1@miPt$sT_PWF#88OZ7Z7#`M6y`5N3EhW{#LnCw9?Psm7f6~f70 zAf2BioNVrYp5&Q#&~m>S_rj5{>Un#u`+)Z@P`D5HX~HY_vaydh2>u}a>HY@AtpxtJ zOn=bo1inxFkG=^X6R!Xb(xUGaoG&uaw+%KxUnJ*IG>V&mS0jvUi_phS2q$|fKb;F4 zf46~83~MFQEuHP{C3@hrzwj}l0X^A0c@F;Y=AR$*8qYfXgAV_%BCe*PvyK|S&_REx zgZ>2vO@qV#H4gmSj=1w2_-zjSN#tLrHHxy(_*)z_A93LCaNx9d)AWDt@TW7Y+CK(F z<25K>jlT)?UE^a8dOAO^{d*kva}NAA)GJN%Ip|yC%N+h>e@Od(7Kp}Ip-eR{b2Kia zJ*XSn|5)#*H>O|Wpm_t@g_eQtAZh%Qj&z$HI{9OVoJ|fscRR}N$BsOn zcGQPc4u85or_-A6D5Iwwb?L7ic%_5pW=C4IucGa7BShAbZh}?uf#@L2U=NW;PpqBchK}^IV9?VDqwH+Fx}MCbdy9uH10`8`?+*2J zN7KR3U=LY!Pet)zfM61K>67tcual{#0p_ry;h-^^?hkM12zoqZep?K<`@>$Zw;|IT zn>Z>AhQr=^@u* z`vct`ueYu?7-UTZW0AqWbbm0COvW_>5oMW*E#~8k=giyC8_T5nXQa=jRDXPUAT+o; zoGZ8~ODb}SKMr)agh|5#>fJJh5 zt0yOW%aw(A?b|t28yxi11*M^Rrk43!meY%rpQ4lxRX5aD)fkJHRAF<{xzN(jCa*eY zBbuo|cD0yZv(_2QSJu>ur#7d=r&v4^DL=)MRGKHqZ1S41W?80GES|Q4?Yq6nWUw9e z4vxr36i@gCH^c`c9qD8yY)IC`llY*8g?=@>3*UGMM}n!{1Ksgh$_hHiw>JLz2|U4g zBpmb)(6>{N1U{A_URy)4OeDvrIgNVXjn9DO_-@V&(pNo>_rwZVy7ZEXCi~)Nb#2E^=_uIIWVEg)x z`XEGY-@TDPS`%!+IP6rzOsmg|R3x%1-rMUPLHcIIV&9%<(tDF54HMcBrZUP2oAG$h zDJ9}5FOskC1>*N$yf^)C8oxexk&u*OfNLhh)YzL0+cEt|>vCKyN4-^Af*ccqA0;j`T$bgP7%o z`?XhuW}nKVeRm*^X;^cLfbMiUB58CnNZL_H(YUEMu>FIpGtpQN_ZiHWKJo;vu~^W| zmeJBNK%POy1DZVUNhFa<^U4o_r=FzO2Szs3G@NQn;(GVT~#E zEJ%cUczG4iq@jcf`Vi`&JWVvMQ(a9^e>lpoyXobso9<`~;y?B0rpNT=r8%Vnk@+r( z-mwfK_wGsun>TN6-X3gj_d7IFAC4rGUh?SNy1|15k@}RGqjmg-|*{p48!PGt+N#L7p zq3&43(_|HRH5Pg`6nHJ8p-v5Y!4@8G>rf?veIp}5d>t}H-&c#KcL#@R3u4yt*opab z&SIg2Zy)lu;qM>6*TC;J@OusXUIV|^!0$EidkwrQ8u<1LzkHcq@!_>2zvshiUh95% zSZ@5`hvNbkopGM%SNUGNOvUe@&^$*5LhvIe{n0rL`r$2AWF)O{z7 z`Vl{p<$({CS${P^Q#qppH|u9vkijg@X-#H-*6n?=*3<%=e13;ds@U(VQ*i%j1rL0(%%U^5_!Z?B>h4ZPhPb}u z$hZA+rML|^GeE!Bk)fH~Ur~OvfkI^Y^8NYSRs1V|u9);SD0ll8%dCG^Xi9zdU}S1nqzhh6adTfqWNu6#3J1APm3nHs(Yp zuN;qHQ1Qdb1}>2N{Qi;*7vU$>(E99_oahvk%*mp+hadw58ZKdst=wSSM|xA&Pc9@o%* z`&CRGU)o!AkU8e(_x@WHzcn~tN57qSDffQx6Tt+iaMBBROJ6l!h1j5kS}u} zkUjI*>0!lg{PqKaG~D$@3Vwt{|3@75vebw3PwbK`V6zVw! zujWUnV+@RcekZ5{_5oIwe3L`&(c38DI7*oXVHn z2>Qos@PNNccl7j`>bvROcagAh-(y1jvq1J#TlOSJIkk^OaQVg_>7q2p_xTA_x1RQ# z@;v1mA8&<|3H{lxjy^&ztNL!TWJXp4bpiRt8!p0$Tc)di=z}Ev{wMeT2wXo;S%7jv z1gcwq>wEI&cYn=S?fqasc}Q{jvrqWP=23A4TrZ;p4y()n@u?zH?y9yoxD)pm*?9YVpvw`e$ zx?1_Nk5)g#wE{kf`hbg~wo}y6uUzAM;t=;0=A0hDz5aY z=waa%klqt2Cnslk{PWKPg`PfNNpZElEoO8r6{;49qA>nt>Ijr?_R$l|p>^GLk3T{U z==gi7ppEbGead;l4?k1yCCZ5o<*BMzE}s+YY$hizRdh;wHeFGTL6bhnp#>+I6h5|( zM}o0&9wNpcp+4R>_CeMAtFcCnGrsHxX)NGax>-um`bHnGL;}>i>jY?IQn+11K|%^U z@h&CzgB*VR78U8!-?2!?pGE3QAVqxqTkz=RpK0GTes{EE96N@?{=V@GAe-em=%c>0$#SDOZ-kmuZ55&RPbaFVlEtJ8ecM7HNE}5&DTCbxsaxkAAiC zoi{vMed%6Awaokivy0JRxP94Q`^H)-eb;benV@g<)|&E6S70o)sBPb=^hW>K253tO zbd9eja~(VyM|WR*UZ%czbk8DJ=Iw#8TitE@&ZHOn$2Niwj*{c!br%K3!b{rr{Ukl# zKNeW5z0-ZFK*q0K$jWJ006@z5zVe4^F7b_Vi=r$(4$0l`Y~Im)V{`C^!dd*9 z$>I-u*{7Yfk2arLv&xmJ^NsDL>vf(}7(&k+@n;`LmNJ*xOgujzO<|$V$)EU#6CwYZ zxu!0obxIeCJlFWgwz{chqEqx|pY~@DDUru+IP5!9?jM_r8Q2+rc5d~)1nRGUYz?%w zs+Q9|ek0dtd-7tp06Sjhcut%X-`B$@m)F(~-}tq0onNYg`+ks)L-?CXkEX|coaRN4 z!@O;!1a&RT28DqWYiQ67yl7=K&vM}`L2dtPv?8o^EHc7v^}=x{;1P2C435hI&ya&`7!w(X)WQ*h zgX7RwV~Om1O6L(FS*wS*6&-w>?(2o#m zey=Bjuj20}{1yCke}*-!)OudUR;d+iOCm6yG6N1TJTW+p-|FLbVpc*jqGOr?@`P@$ z2sZ7lSI@W2c*pDM(O)#SWxwUie#4jD@~|(PdC-@={eiZolhxxdLsI`2Nn8@&|7F5x zF5H&=Ra^EkB6;sa@LE4Ur`CrK6u*0DI zgzwPL+(LiImwnlnJ?T4in&{6j_m4N9SvS7&I?x{(KHApw&v%^fJM^;qu@@Jd{|!9O z`!$ll_!P(~Bg{E##aKW-h6x$HYm=Y z7Z^{^OZY|)t@339K*o2>LlG;A(L+ce0QdN9^Hd@0(saxv^^lAL=E;mbA?Q8eNI$2P-p|wq?H`c-z(`C*Hzyx7Yi|*Q`RH;A+c$ z-M{oJCl&<8wk|?=&53#N0n+YwJvFk5Y0veIZvgEf*NHj4u?_90_`aow15HP6yR0>P zX!J*wZTp@|uR@aZe3-FX=_;gGlfJ5L-w!fxm7T1$JF%U$b`?F`c+B%X@$S8ll-j>9 zd-?|KU_FZX)xNPcH38QVUv|x+Kz4(h`BeGCU-6Z%Spv`63AhEaA0}=mK39F|DxQCL zv^QtL1wG(gEUgY8%=5J~?P&Hii>0}GUi@*fvaNhTXoVZ&C0@F#49S>AzCt)en^suY&eU`~qX!y8KOFQT+VbuLQ={)(|7+ z>u)-ex^nd8GwHV>g__L#*6N4Womo}=(6?HfzLvQFI|W!LgFL$pdf4V(wZATV@`gvr zTYGs9B4O^obo*5MnWMj0(wxQC+lyY$PnwZH_1zbsyTuY>QDAIWZQyOULWY(`f74^> z1-|k1UB0nfyL{svJCFYnbkzJs&T`Ann(Di0Fa#yEj*e=q1q$&Vq$-Z?{IKT~SJ*FM zk_DRE51*RnN>};DR`^Drdl@U!GoGhZg+?FeU;3Cz20i1_C(t<_u6}6l1E{Q^bNQY; zb^?7yi_bTf!h)`2r_XiLmrWtfmd3X5Fa56n4rF)L`dnDq-P+|J&$uw29Pu^L9@i0H z_3Gz+WAE@~n@*KgrY}F$g5?OR>3UO5*Q5S^+m~Hm6Ug3*KJr#K<@t?ZU+dkUvAop} zJ=dB&irTQm(eLuE=c@kZ>W66aGB@zdQN3W`%$+!-`=@qx^CPD`xXq)7TxGhrCtA9o zF>9=4=L41LI;1lH)FD@9zVFH7{_((kmw$Y#>jdd|rWmY^q5;Y9O_dm$*@B zu<`6BPuh6i@BM-atp1Ys3(eWP7ZW$s&4=4uFE`)$(+A;d9{n9A2`~7oTmQAS`b$`5 zzKjgFWsfSJSaw{bivO5zd}aAqy4*ebtqK)}Y$$}g4`+qJiuc3 zAM-83l6&bPU-icCwPm04FFl2&>7Et-F)t|oGm!nS0CXG3MjmX-e$|`(_L=sb-t6&uUOXH@fQ3z*Cn=Y@bWeE?y6`dr z>3TCqC^eMuPNay92i_Ji8u1+TH$4+T$BWH2kcsA)Xj}j<3Na zJTSHg{r{RSq#9^CO+)OSu0R$Y%#BO(qxiGm@-KbHKl<{?%Csxp9C_UMoHR^R;Jhkf76XofS(Z}~L zedf@A%tN0y`dqp1x6gQ<(*5^iFV)Ohh(}-{W+pYA3o%Kl*|BgZW+`)O@CekA(8MzgTmWT;m^DNRY<^u)E8Rl>p!$ zu2S&*I~DxsUWNPxLZT)2OMZ}d?Y z%_sd$&u0EJ;5vEypIC>7j(-vm%eB4Pe^rI$IkW22<0!x8@$};S5_n%UJzB^xVmfzQXOJDGff38uL-xsfO zmY9 zywP{(r}zptu5+fM-Lc*7a6FmJB+}P`%Ty z+%D*GA1Vty}!$fP~yagJ3i2dbYOLO(}t$-rcLo}&6}E=n^$MHZ}Y^$o3?Ie z=h`(R=dOwR(tw<>>d~y+TOLPZiCP3m&m(9+dQ{~H^l3N6Z4!Yw9K&wOIbd1uXBUvm$}(~uI@Lmc7mXo~&K^-i}ktdd8vVM`!@ zs-2tF&^8$C70PaVd$axcdhsbCJ1lO^OtA^K=UgI$n&`H z@>~z}a{SBnJ-aJ$b`qvIApA9)C>l>r5vbuRA=GfT)8mP&r18HIoK3#jx$|e|T=Sdx z{kUd22k8z3wzip;zT(PdSJt|h;2!K?-`iKz!g1v?&r<8Ep-}Rw-gpcKj8a$aYP{-} z-o%QYp>WDGu&i!*-z}c*f4p! zM8djiONwO33`bJo{sA=ntJ;(Co6y!%oaR;CFdWf85K8V!S-F>h3+~Bl`w*7cq_X1O zuocxC#vyDN0>Nl}OC*@KG^A6;N!*n;0WsX@ca!xHM5A+%7C)sIh96AGM8IfkD5=a) z5FPQf(g{9#YLUGQaV63~B zWx&DA_^v3t!?8Gp;^rg9AhLpCk@*Q|$7m(q+sjT!v%DS{YsaW4IGjYrtYD^_Y^}hG ziWasDevmFhB2bb_k%KHz48+~BT}l%0$GA-ShjEG!EG1Y1SqJb&D0>Uk-l0@5L}sB- z63qm)nU>H%Ff<&3)tAg*D7l-d$%`%uScDAH6$G$NMiNPj9u9^W3cEqpzIy{Bn&n`3 zFx1myb;FXGaD-1P(@jQW(iU)@6(nN22~Wa80t_#J4IDkuAq;a zIyw|l1gWUTL*1!(4Ax`FzdIg}p(@;*i6)tUbP!`Vry5K3M|&Ad8VuG!DAEXO6o7hC zJ%R&95hBT<2#3Q~87$wxGdh@vk>wl8pTp_QoF;K*Qv2m(R(qh2|G5@aw|1q2X(1^5O7P*N9_ltqf>{)l?Q z_#idqp6GyuB?Os>O7({@4A8OyJ+=iss7@=-SNKx}fIw8-^hYT1q7jOW-ykxNWPm+Ubh`vGQ%Dj^ z8c4%QAUXpogg7w<#DpNOLo2wyk3zJStHx!1>KKEOVGDoS$#!q(p*^B0TpQEkr3e{F z#tDQ&;eM7I6^6}^pzgq1mkr@P5pD{y8iOr^K6`|hiUG4)*d@U1f~}ij^w-9~$S{z? zgBcoiLug}c8%jA$C^@4rWrRCBu!910kP+-yC?p(5Z;Fsaw8z4qbL`hnh_b8>0ieo& z3V%*2?aVmHumO_Jps7S@WI>rmgV^iHP&$1;0PsZbL@WtQpf3PTH;n>piN->9212P_ z6al5@OwRa#MAbUyEL8VgPCvB*rK-r*jkq?e%H;40uBLg?0E9x7AV8zT0Q&C4ZU9yF zxpEJvxzqqRgLqF0p;RLU=cUy)Xr|QoEvw;S{Q4l)6+alYl*pFsUfib5kaj?u|xbm=eK5VLYSI zo@A<(#78BFSdn-yIcd9uU|&4W!5C;PEF}g}IN%97HN8S>v|xb<{U1$%gF&4ovR0Hx zM#AU`ofGtT?Q;gNFd;%D~ z(+0gvP(5{|Fh)mU;^1O2mt(wBaTP+b^CfFCV??mA? zVk$&89BJmmH4l8afCmyhtH9I?Ey)=7T}g|(E3l2(bM@8e2(G@G++w_#!>)4%@?b@_ zh*glA>X4HgUBZK0D{1xExn(Q?48is*U9^ zXcT#2rLTSckd~Gv2-I`Zu8WMHfmMtI$9?HX%>?Dnd2 zLoVp0vBCbKKts#7s(sFI@z zr<9_jNf;s}$Q5o{2Wbw$W~#ncuoWYDC-1qHq7$l7sv1(&(Yd6u;hO67WMSNIoutuU zfj2#|6m^~Ln57zBC$XiJlGxm>s*35U&!zCUhrOO!;}@saX1hqr9&G!Now1dij8hOp zmy;HCQ!uH21#bZ&<<(Sb@UVh@49cDTSj{FQm`wGoGF4W|9wY=hrRGgj%j3TRf+SZ1oa8G$F}79Q$G$Asx|k_L7+4~4v%&LUY3k7Ax|bSe$fv2CDW*hvc_r)e zl=3*OQlDxonmA=go>ixgzJ`pv(8vdq=s|hu5R8rRTMs?j(S+66Ddv5CV>xyYu@8a1 za5JV4$|3P{`~JLmb&;FB;jk6C*-^mxBr(|U4O=*tVb``ik`=OPM#iXNQ+5EIFsxLN zePyHEyco;XcQq&i?H1w_8xzwiJLqIno%&6z!f8umBF6Otuw#vh1(9R%fHwgXlTzoW zNqJXdLQ<_>blMnFCrWz-P9FrZIETe+!Du^EI+%$fdFs+}w&%`ti0X7soR-G}I?Wv> zksYqdB1LEiTZ!b2Mp5p@IdQCIl%*lsb+CdhsD_=WrFapAwFcgl1&8C<833Gk5|1I& zLrdxq2E5KXjcI+QvGDmmJv1WfY#I0CX>aTX@sU&!qwexD1`>P}j>`z049u$Q_xKbF^lQ^%g za%_31cK%Omr#>|tO29IABeuMNQAH&Mp@HSC*zI!jtSAVajb>vv8M{bcjihAO{|aTw zQQD@Wy;8Z#N$Zu(mZRDX(q6N(+v3+TQGVr-L^qvEtK}l9l=>h;LAbO*&vM$icSeMb z^?F)}QriWXjIM!sgOK55l*@3~40L!}L2ts^lOwu4*5+AapV}Z`4E^hg|Fw>0GgFQm z`O5)%{F>S51i@(PQ+oDhgGWcYXi=9cov8x!GL%+bG+RhwFNfUDb-wiXGt;C=MfKdp z)1plZUfsLj>YPN+s0K)Vb##g)vXP!)MqHLBbvsiCHY)gSxA}@hfrn#mQ=~X(uiNey zOB6|ur8AVsS!hbGQZ!YM-B;>o2$Y;h&8%cyg>~^Mq;$c;DwhOGX0{@1YL{Rovuwc8 zLM{e|C9~6`QOV^X%TeA0DychiZiAO9RU+op%6jl(AAddX}a7aRPK#5R2npe3lKf7N@&Z_VT?ll#TkfT#5Z=17CEZnGjug zA>m4@h|v)!Qcru{c|)n0Wp<^?R2ND^DB4&rtr9CfB{E3_XzvjpfznEPTgj6{u$+Zi z9k`S{s{@Z$XjZoz$4saTSGrS;VsX}RH zCCa3?uvNRAC1$g+6wVQBHX6Tr6EZuE-|#B6rs`bIM&nmPy+Vy2n?9I)VVqVKMY&4O zeGb!EC}hoVR!Z5yY}PV53*{_g@lKaxsqROf@&0$VBrVSVl@|XMs=N(+#?*@n#z?6; z5_ZK^^ola?XmXzKn9(Ew>nXm81Z}9MEbda}&gWZtYj$v)jrN?&ZWgm!dZRF+6|Yh# z^;sF7&z=n4FO@vao>D5arv3>v_G*;MB;8d>6V7@$f}q+^NvxLBWwLaV`aXq~s(JlN zi9VBD>byEkchfLUXIL`3dO9xyZv1VeQf2cj!!4(vIZ6q(IquShrSmFkwahK9q(Zij z!ptVGS<0h2&{$Ib8j?|;#a!Vxk=2DehZn-wxF}2-4%|yN@wTtRIKg> zsDRSNFOt|SDSHCWuZYOAq4wo=r7og%uGq7s^unZON$15Gm#*?czO$flb5hpK^Gg~p z4LiNYD|I%M&u|uoOv4i8U2>@yW&^n7G~4V-pB{MX==v%Q0Ko`Xny!R8oh4_!3EWb+ zW;@uYI4hjkmhwgz2km(F%RCt?Sx_g-(uDIY$BB5%&-qlmx?YA-%JV8&UP^^#{f<_X^_;0BNu9pPs!Q9B@!+XItOUVLQVsgQV%`#4*lS` zgJI5@fk)}uK?+6}l!7i~X9^`{0cY7w5zAdOy5v~sTXHENC6g7MQciZnvvj3Rl#)8# zCCTt{K6QcaELZ=`rRkCsjjoWguNrKA{&RdwSHMJRmDsZ_Q(4Fte%8vZkbh|cqmR5L zl8kesA*AFg?4cVPIMohItv%e>jjL4F5S5ND(_svnz_4RBdCnHE(zhj2KeO-o!Zhh!|73S0?-*>dsVmO zWbz~P@xqW-Fge=Np5*##iSs(o#QB^!VZNzaYQpGw_&$}slZZoBXG_r;p=6CuD}4{$ z%`G*@P_-k+x=Qgqfx>`SF13lZ{L1EX;&f-DKGkt|j?3CnZ1}^lVhnb!qx<$bYucPJ)=%3-Tbh*q$zwP+ZU z2v$Hsy*AD(q^WSo`l@2sz5&^4=*6g!_L4mhCk5`q!yBG31(<{tnlP+(!^wuZ@;x;T zEL_6^p7YH(tr;BgjVkx#`rGpID#H=`t--B5qEkK5lk!s~P&}u3q%_%AHG&rtj5_2HhE1?N z8C1o(nTohB+T$hLl*-S7K|7dpfl(}+t%b!KMya65c$uT{noOW6HdK+GAYGbXM#k}A zgaf8!;bkjQ1VDm%j>oM-ttIS0#n9tO%nnmaWG-baA&O*<^qHSI$eD!Uq!OkqxHdhG}guio^=&iX?0oPhmn0WGOf}9EFiM803k@z1>!x4>6Q#Y~))Z*5sfM z!HOg+=j)3ZKWoG)o2!#DoyKUfbn3}A9+e9~v2eNCUPQurvaW}i1d7J&4iRHKi-me> zZ5w%@vK>UMDyz{&4sEf_F2j6=b`_8o%>D6PoYvm3oSH-@!p6gp##w2KB)yEf6>t_y zf|IkJjHn3jdbI)@;@hRgbkvaSf#E<%&aemvA!v8Ba9e3;{#3I~^-jSAfT`S2Ms8pc z?$}9Yvx749iK_^qvM53ArC6X`*De~^M7mYEL?(;_SY%gEG)Y%utuSnQZ)o2h?A+e& z?O<&6#*G1mZ140kvSqc#I(!>9cPc*{^=}X_jRZDsSgVK(Mu>52$#Ja-Y;0BxS~qT4 z4ddt}4pBIqEjCe$(>srBVqm9k+Y;q2SuYu?Pr7R;Mky-v~~KF<+s+1S?J-09ajY2L7XBgj;g z78_jyO>S=9V0*0hdfSx?je;z{D|4xs*~5}OZ$GMSRW_nZI%)~{+gXrpo0}D~x!K<# zLbh)SZrQMY!^UkJg5C|S{^kuzc6bN;ot>iP7C2Y?*U~^|>}X)N**8cZud!~D3|dWw zk3UQcoqW?Hwkf8mM&KI@dggu^^(s@1=@>W7Kyp_33dhm2Msbd>(-2P&x6_DHFan=d zHa#*=ClNJRPa{UopzIic&MI9_#GC9oD41#s826@DCwVob>Ys^;?f|yvU=b0$hQNrx zZZh{t{sJQmZHo$MIb?YldXUX``+F!VXo4Evn8?ls8vhErS!;Hj-DQ`bm@4n5VA+wZ|1tMtr4SlJpH!E{J>3~bGjA7_cy zpk~6DVX$>*qt9YV2yKURX;h33_XkJLl58Rd5-sf8Y~>eYH{f^BzTLT3QItCBq_%<` zoG;R+GO{_{>T8M_Dkn=(#nQA3TFPm9Eo24N60C$!euCKwnQD`0#5^N3q?R2Toj#Z4 zDOxm>APSiYhi-hHjX5wfeGUyNEQFZ>!5PLMQmt=uFPAe8o41N z7b6*x*wbWC-v`_DsM-W(5WOKH@fl8EZ4G_YAh2t~aQgAd+It_Wbw_;=|7k@C^TyLw zLPG*G0@@a4I0FXt38h5>Gl%goU?LsZZqbLnP-iOyFM}Afd-g{@>DcOvpj1;6ZM8u z)$wwCfDcDb$Vjllrc*)a!y%DDOwt%mC!cD6d3H)k@9{ckL}%5TinI|1kEbKHY#2`~ zl5-<@(hNKH3#84+Tv5AZFF-p@t0-t`luW@+7p9H5*_~Rn+&QBR)%i_(q6G1t+o`Lm zr5Hf5!3Y>mk!mm1h8F9f7A-*vl|0 z!XSg^bOmYP@A0bDw|ts+diulX!7{0SOoA9rJNa}Z7Ne8u45uBNr}z`@dGVV_=lW?% zeMy62T7170yU(fapuIzZ_diqG?RI{pg^x<)eSZrxK;BISKiLHg_7BirH+}7{AU%hk z>7)x<+co(Ky|=#7Bb1qcuzmy_$~%H z8athX(@Z7KS!3DVz>L|MB{BKJ-d;7C*RKvu$y|o!fF^k6 zt)die#al)x9G&Yuy|-8jKWS}tsg=xZ^48YAdn3M|O#5>9!x6YW$^+>)Fos^KeMJ!8 zSjLx}s7RGBY<9t(E?$S3Amu?6>lKOlUXXDcV)Q2ZHI7?YyWg4sfSSAiSYN5_$4B}f=G6#`c zGUn?zPFvBAs$THvFqqED{dO3=3BzfssedxenUekCFyjmR+4#VBN`J>pUx{*>)yB~! zqQG~|khnL@hi^T+A*_pG$r0E?Qy_nwTx{>447$j;q59f;m)6)1tm##C|wZ zgO6qz`sQRGd-ufE7qE4d)_9uzQ{j;MOuY{1fCq=bM&$qv+d5zk+#P9F27LmR?^Nlq z4a&{&sF3-4Wjb&Tc7zm1AawK+X~u9s#~Hp2-bfkVN-CUZGT|L?urIQfat0u8CX7ml zKMSsyb4}0*D8}niIhYMHNYplAI*!6+KVZEG3df>UM^%km1CJJXs4T#NSveo7auT#F zKSPra?TX}Z=!6G{>Z4d~bjFMd3n!WIJ@pV<2MW_Sr4_m%vAa1l(hAc-mW}d>D*gbu z!*30~MXoZ267oF8FNm^&B8)ljm%a$Yv8pAR6AL zD5j3#$8yJqKZmd@p}5>KII9voSP#bXvTs&}`NG@8Q_8DNu4SG$mxZ*>eAU^t$^PW# zgksit+j|^Y%K5-#ewGZa&e7DU&z>Q}Sd&VAsP-4DROHVeJHn~{oCe`vM^!a2;K3h0 z|K;_rGxHr3U3-tNxcJTzYEgbADvj+%`g+ssa@Be!#<>|aEgxSi9qbvgSPORQ4ea(8 zd=atuVopGnoh?BXk z7_a1ul$!K=9_m8E@=(1^8+eJ6RVrRU?I_M(Z#5RG}HWuPY35j%s$r zalg3;X<>^dGTo+W;*VpFSQ22GP6i%|bDq4xV!Fsvm3+2Eq_IzlbDuK9lyRzCbxHW} zF`cNVuF~c_8+n^Ng%V{iDC*)$pGGE&GbmbW>O?$ERe8G=>siMv)vilL2iE;wfiu zKcyEJIIu-5)QSaz_a4pC9=WE&M?NWzIgvU%Z*HYe>1@BgP3~CJ;j})M!iEj4O|RgO z%@;nevx*So^Srp?(-$W#wPO8=B{<&pwa2mjgUy@ZX8n0vXfK7AGWv8nq13@!aw>q_ zS~~5QfAYGi2yO8_WLh9PPNR6H5wnMm4(UBH-jS3^!!&p#=-rv-@ixaX_2~!#Cj6>F zFE*!<{&G6g6``x53bDeZYOW#IDIE7Yu_>3rm%btvZ-XSTEuXf+sH!n5gOBYb@%D8X z&rmGUAHwcmUo?#unS9#M|&SZHt;f@x>24x3p1r@D>gmfDznf0*-Fn@x@MD zDD^b3Ab8bP7fpF@3R_X_ran3v{k6@Eh@fW`*pkVbeAEn45vLQ7^H6b5!P^n042@^$eZO_lVe_I9+wZO2)O zC)Ag))VgPu6RJOlctVzqV4{zUX|$MtHr-?;(gYUv$u;9B#?V1(mr%j<%pOb4n`G6_ zG4MtPxeZ2pSCaiR)t_Hy>0Z{n#(-f8{z9prI_xB@F2x6ep)mIMQl>}d9n0=0?hj)s zj+X!-+ZqZ7^~RIKp=3`kJ?@9)4J>&xjOove?U~>$SoWT=b_0P{iYCc=Qw2 z--|2!3HrnFn@~62X5hgmaqv-Fq#IAdfrAMA0GcFfe)d|i9&+##j(8vaG2*Eu)KlAQ zA*Oot;)dBU{q^Ge%7gUBNIzkaA$2o0@#%q!w#Wqj80jYr!Iag0^;9{ALq()K!=cE+ z-QYqG$|zozKzjy3K9Lpt@gq!FnDbsonTM9}9Pp?o@`*Q5{Gi;2_(8}BKgfwZ(4*Jt z-V_QPLel`6`HlB}Cj*rb;IYz?sBguZlOuC5k7CQa$TbrNuLz;zxgs ziYLZZ6i=$00H|VkQoW3+x(E>Otsy29{s>P^s@dF-Q!;}o< zqWXk1QI#lVRh5uBZlZI=VL>1t*BPXa+Cq|q_%T9HN{TB*OjQTS!&wL9Qe;R4s01b1 zQRMvW#*O>lWVU`A*NasVOA$f6A)Oah3Pz`Wx%GTDL=5l1xj0vhsB53Ta8uq7e9>fAjL&!pUB z+={2YOEIY(0#!_aK%4-hJ~|$y2UWK?9fZenD~+C3fO4V*k*|Q5grW$^7m*ld3`B`z zXth%OL@C2|4@MFOSjsUBb<=xMtfodV+2%*kODNtuMF#QWDYYBMbp~MSh5_ynhmzrb z>t=qNN&`fUAN)k7IA^HSgbrY4h8L9GsE;`Gu8bf6rsD(M@J*3-3T_~9;YH>^II)`m z*_We?kP~1~U>ILhWhWpJ4Z(?GP4|xcaO*!Co{ebZ@Caai=s?{zIB> zNC<~g5@2Es=_tJ@h_FRZx`K`Cl~#~|a-^u3_^@Xf=a&^F3Ko`ndesR|4G={4pI}TQ zvWuR|MJFxMP2k`}kNUJPvS{^2P`!FpX0arr%m;h1t{hz4+Prz2A67oPIy*L4?1hkI zgu*8XiBX$qbXTcBL8VLN^^)r+^DK(H@FAy z2r*s=q$47^Vag|pQyIKMWkg9ltVJHyG7oE^hqcthTI^xYIOHDIf)8uShqdU#B0GrG zT1Nm{m0|-#(Na6(j8NO73~}Ql17!~3&DDJ5)y8|3B&ZI-sl&aFFga~1} zYz3j(i#~vXs<`ArArHev>l~^P@FE|n$bj56^eE7)06IMOjR_El4v)PvsLnvB!vNrp zgPiCv*r_1(6y!@i1$!$u_ZZ|)odF;>&Zvpz=G|nthM1|NJ-AO~FK*r3aau#7Ndc@VYA8cm`5K~I3ga{2c9qp0 zB$v})yEz=KAJH&4^(wK01G_jM0zxS)z>qEVgasUOAVw(07!6G-o<@w6DKg87sSr42j@GJ!hIeidmSuk#-iQfL;`j?2NF0sk%E!aL@1i1 zlU!Uya^;y~PI1l!hPdh8Sl}j{Ysss>Fj$+H+H9osYNe9Xj`E6Yit3@>Onnzdde#{! z8C@!K3G(gu;Gn+zOfNmmk$JnGdP+GI-kRG3mBfk^Xj^+y_gx%f&a}hL6LH3DNIa~Y z`?2_nVdErty{Fc8M#+{#5XabQ4o0i%P&$*O%W9rFeJ+$t0Jg6$Jd3W+S+WsDI(nWH ztF^haxyS)_6N$np!ZMvqhp!pwOp*)}Ac~y6SL&g&=@B}GAH?+D5lETF1`hg7-q)lB zdav_5!%#fhn5v6Ei#U~ulFxAcZT9d~hbt&?u1p5;Bh z-ljI4nl0Q+4%NuHJ7jvFQ z(|OH5fykPu4MGWZPyi_cw}HK0K7^@?SL}`nDHH7NI^}V%`u&1t}p($8vA^pyw+TdVaJwA7krgyBR4Gg)% zVMzl+EzeMARXW8|XLQD6LJpf^hS@Np9r`Ghw1vrs4k3{)9x=pOl|?f z9(vT_LA-E0LN0jNESF(>5;t;arYhHSa5;zW=g1_Mri;sI&P?x|c$S#^oulHnet5Qy zr4!G}3KCP(V>EpHcd(`NaS4?T>>;szXrffwQ9B92cmpZ3$lmWA% z$yj1Rf}xva$E!wKw}irEe>@erITOJ_l(WyQol}3bp$Yg*kl&)B*^8X^_0X~Ny8q1P zg>~N2KiLB{5)*!tP1*|eu-$ESo@aVm!v1|;{E4(f>z|@UtyM&8P^yWI^q-2^VWT4V zoU_`hP&47;$su;$`iyELvA3JR45#;~g1LNNy>%7_XB1Z&orvy+Jg?kxixYdr)A7(c zlI{(l$@=z+u-)YDCK{~pe`KdE}>gA8V)$8r#%B2cWeXSZArLW97bUL$! z=_%m98LJFFMqw8hzFgQN$Ksfr8bw3i84N#mWIPSDFz-x zs}ms*7}tN6WBlf?qG`{Sp5@N!%;dCbn^s@5lr0?^$K=3V{;X+AyQ-~f@;0-$IddZF z5qKpNN&0b#+1rk`ME5iD!eow5!>=QkP534a?LftQb==LE2Ux~8KbE>O zj)98zM#ZJ}DeToatlGCtx*8_}RC3R9y|8ZatzQ#pUfWSDR@AJ=H5ElQYw`)>nP^O5 zw<%BU04|8C)sN%$(`LF;fd}r4kX01Du4+wCUqD~+GLh)h8kyuE)niqA5u-J+v7?2i zYMQfiAz%C?w)@TYi6wiv&37ww_r5~fj^8Y3cyZ5jt>x;?A%v0s$d6JNf$*}Ck9yfc zIEvgbQtW3Zk%EV^GMDo@burFLHvOcyX)TuB#KNHo%oYQIB(71q#dp+ z;9&d&hve)^Qmo=kA=*UBaTH!@+bTIK%(-PryE)J$I(hFp`UfZJb~AZUd-Gtgqbrr5 zp@Ewpmg}M#8Xr>rb*%}0E8!qjE3ge6YY-Kow0fat*jfB;+Dx{k?ChXO|0lC^_N^K! z+Ou!gP=>fRV^@W2;OR{@C;wti@a(+pdwk5=-iFle#q>IkzO}IFp$I4GFPaPbi`m(i zb1pCG8Y1tl+V^5jHIS7gZvh77=9s?=b=Hw5T~DRs+)SeaSmxcOFW!w7Yu1joX;er8 zO{%EEj%f*}+$`TsPZUo@o#65KXr9 zU{bgEIx6qIn^}=#HSKt5owL-=4Kg}K)y*NByag6&*lvN7yz+F+GSviiGdg_dBvVaF z*YVpCHODW~4&^O@cO%WnmQAM>@X>^q;bZ2_dqv}8AdG} zS;J(DZ$8;C`&4L(Vx==FM1cY8K3wdm3BhKmJxQJ~8T zR?k!TVuA3|)Ebr+%WtQp8EU8*PeSr-q5ZzVGApfP8#r4uHKIDLLYLKP(hy_{B(E1P ze<09hwdZru2{am#4vpSyduepH?Um1{MS^?+%dAX3iKWA~8#V><{6K;#%T#Qa)sfF; zc1T{xOQ_7+lb?_^6&fvQn+RNt&teIiEt+>Z($e_P9DW*qW4X!_$)xeOIsA?a{-VQA zlLXl$1?}#3A^DKuvMHZSK1s30a8ROu!>}nxNeAhAhqBbDu6FR! z=p|AEi-}ZV;&?C0jzX|$%+v{0De`5X*AQRQLQx8 zE{$rX6{&EaFVK3>D-v}ZA%lgQ3TjPh^%UvDzEQw?M81z9U-J5wO}xNv#cD5#+IYm&b+Bn3l-4$?KJNa47o0_Y0ivI@j)mtG)l zdV@}l{z;9hHRaaOgLmXtARTX)iML6kj(87G7_ZO7+pN(8#;YK&IyIZ5Ya)Sq443U1 zy+txoB>N$uPMKI;8a*XmWmb))@im6iCXM=y*9}_))e4}P`9GbYmd#}uF?GTaQ=@Md z2E<9@c7p91)ey|4BEy#?XPVY!h4RJJD%&Fhpbk86hj!@v6`9n&S!!H|>3Ryvatb8Z zO6-psE;k+#^tZNGzRWt7za)~6Rw=%p6j*Mx7cSu+6t%o0u*h&!yFl@|Rv<_;`U&Gz zpq$wud8Z`s$0nhI?Sv_iJSeeWGF%EKajRadGU*qHOlq{>cok%F3M2`Mz1wivqS5<} zR{`VMA$d>|__e@tYja@@uokMEmkD%P*Iy{;BHPO?=pWc#O9j;s5fvxy?w+Zzhnpg#6*Plz!!-iy2zLJkf(9aCX7L9gapolR6(C9g`5ect12zsUM zH4*Qh2=8*M^WHhyp;?w&H{Lr(J2Zdx)G5?U3hbGmh8FfWCJnI5C$c0bIvLCfHTnya z$%3xzItgkuBwIAPS-i@uA)m%I$0B|9G&yav6U@)aB%FMrCX(s*(O3ijuc|~>#i*%B zo%uB)Awhp(ND4MvZk3?FHzZr~xqMiHG%V6h=^zb@kbFY8e8D7C(78S?L0=bGZ1ZEb zX#Pdg_^uJBU}$<)f`0se2gz{}^Yp~x>VD8BDd-P>C$XKKv~2m?^s_S*p2Afn*8^{hlPZl(csLFVV}N6jbMe+2ON7a>_Jnrw(C=Oe(y^hY z{H%RZNDc`sHpJ?%r%Xx(BlVjkgC3L7E%_-mNYK>+DWzV)b#W9+{XwDmxp&j?zbQ=9LmzM-0N9Vs@WqZ9qc-Ks8=+g?Wn-F1nf%9ltdw^cK=wrJFr;07(hr)(;X+Cts%Z#vXSaDAar*B1(P{gBA_ z5hLgI6Uk`{^{S|4Hj=x}wZOhV!nW!nJz{%Jq!YWtE2zjiYm<;f3N*W; zP_sLbl_+_WQFcLhuanzv%6f}N?Xta5OR&l0ev3x!;=WM}Wf%ALg~ffn z7V3H<)b;tA)I!-3Or`@nxgCYcO-Bc>a&mShn6OkPA-T1np4^~I$u5!`bdlKgx+{6Z<=abpu<;<BW)|zSlD~g4`D~wpgsugyH_cyIQ9i$B z?i=R+=#KLixVGSEZCSllKHtCa9p_zLbxlPXPR5j1TmFTX#Ya3V9D!97IQK#Xmd^(* zPQT1s=qjIIRizx2uVFsqXawOoi{ZSkstRQ1GFb)jx2!5dRzYOvEp%xHuX!`4JKv-L z>go%yF;G*%0Shj0kzB7`2*xYVv#f0vU z;s3gewCr%a-Z((^29;-uQ+DpVSK)N+#TT#umoy=Y%EcSYT$ex%TJ%d*rW((!hg@!x z!>V_jxAMHYs*7=&>N4Y5VSE0;9ES{-FM;?Mzs9l_=aO31o33<043)Hh;mY&gaUKe< z4wAl^6hleA#X;c+Uvi~YJmS(TB`1NZwsTE{OQ7Ai>Jag@thYIMo764qiabJ+=j~-W zKX6=WvZ2+0yj@i$g;y)lt4!*aCjm1*4tR_bcBb{Ykott^fx<;pI1X^j7g$j7BlFqWOdkw^-I!2A- z9T%y_0fJQ$zryH?bkl67t^+J<^;KX|j%wR7$3$2y+fKj?FA&NbOLRZ8%0vDt1~HigD#{1*lo1gIcrI~!`lc~ zS4HaBWCz)DtDwzxP?bZrx++Sx!x89TSP!&*Rr;dW-V*$RT%TOk*8&K<&_JG zu^mCI+Up6itann6#a#uow&O~4!>@I%qMk)JX>QP#bwhgxd01AO#xW|kObr*?(3?>f!>bt|F+DHD*)8n^=q=LXfZp1` zl()(FfsEa*%i2^s%i1G;b`NA(cU&o|a?tH{X;0Gu-07m%@OQarYj#uz+x6D6?shd9 zo$qsLrBjaH<+{>!O$BPu+J)|_9%$v=E>f2o4EA%N4#|?M%0c>v@X&!{E*k5o*hsba zxT;WH$0gjd-UG}L@x5?TxjVcLz$>>lqp!!{WLfWnKvJ2x1mBO8%Fb0ah|;>3+^Vv3 z_y=63!b0~SWS7)(xIYBR$Yog{Mm$x8?{hg?0%$&>^TPZ-%AvXee9U%%^5c+=8o?hK z>3bHE`xC~k<&?~P5&_7y))X>GwfKI{0!93kiKwX{gT(z~64;UPKQXjD3qef^l`hb- zJ}pX9<@l!#N~3qn`ZI_3H5F@-{GU@aB%z``ATEkRn*IxRlbC;rnE6Tm75s?DXHWqN zepb;C`qv6|#aY>+!0mRvMQ;Qv*uWDC4k@O@W)={IEd<9_AHRqaZ|;<__ZVGX5$C=gkAhnu?n$s{!vNuUOgT zR^=MF{)t=zWtUf4b3Xy}b?}<=w{!mj=whJdZ=6f=TmiIf_?&A2Dk{ql!spx#zz?`8 zwz;aV2lfbV8dh|)~`)9dJ6ARDB%I7l`;qgw|I59h|Sl zvBioYoQ+j~hnxm6$*$N5=c+2Y(YcqLAvk?iN8$W4a(2VnUgah>xFS{217}y22hIiL z?1yt_6JGT~lluVNCtXz^g8N=_ zzYp%yuBv~5o9o5<;oe(T^#i!6j#b{zs>UVHAlKX>j-&a<3 z5oBliJ_PsuWmRv2yOqK}4EFQ?RkBe3?`&*0u)Zq21TxZS9$6{+(URU|5@1%ydI zH&d$`go|^ZEW5nMD*q4AzUJ3>oU;c3b6xZ2qJsZ9MZVTmUIo0mhVo*~ez-Re-Fu?WDB@n$Pvr zEtdZc;;FKrY+L1D&T-d#2ToNsTSMeEjB(M;eHotDfcMB%@8B>SXy}A!Pm+@c0Fh*1IlTR8i}y zdJ&GF!?VJJ?$WB74*}U(2WMr~McTOx&Khg(e}i%k{3`ye`~{%4B-VTiT0%j7SNVnL z)aL&I9@mk_>s=SQT@}Bq+yKWqG{1`fqLSIEUB9OK9Mi7fPz@f@uK%VwyHC4LVbrO7 zFI?QA|CVa-r{Q9GPg4zkP`jR|8vI4=dVy*%wR7%xUZfiQv<`cTYVg0q#ghGwYVa?# z>*cb`cUGR#t~035Rln0N3kpe8&4Hphq6^NEs&nCFX|STD!Da#6{~u%D0Uky5y*;y& z-E6WXY#<2%1EjbKB-ALqg(AH->5u?Yq(h`;MMOGC??{!RNKph83nF62hFuXWcCiZ< zzV|&dcXxIYf8T$fXXo5=-uIl_@61kHjvTka#YXp4RyYp2xwFYq?jGpa+0;{dH1r+1 zkB{U_crSG9Yz8Ym2Ko`*HwC)1MJ#meZ0=OJ5Be$Hw@&G0pkrsVPw8>cU&GEy>G9C9 zvw2qOWuaqd^QO`hpkrtAnbONazpVRyRyxinac2{Y;V9)zgpQp}I&^7|Wa!x03e5)0 zf9UZiu^EM&-PQEu?(S6GHxtUuJc6iMu9 zE>4G<=6f3IYLYXcR`Y$N&`hXxd_O=vLG*g44SjA*8ELL8sQJDMP$i>msO>dRA((f< z!+oDI3dscSh^#6WL*Gv5eq$b?@QOf^=Gg}0E5si#b`cLXf!`M7G?1$#ePuisNs^tI z@ga=yUt!Y#1#{Qpf{~;9+C!1X%0($PUr$ws8c@AH9^zfc!=;)~v0!8&%{9w?*vLiF zR*+DUHJtyz4>^)L>P8Q;24Q2>l;yn}SkzgSF-BRSR41lT*+!eW1-#0xsugY+6k^?jiIqC+>df4Ypt}$T$$H< z+e71W?mo$NtsAsuEbil6*ZL^!39f5=Yl2`;a$Oq@tsV1wii5^r)}F2KoCmx68Dl4s z=23~C71Lt2!5;1PJO%q{Qob?HAZf2ZRN3EYp3jkdm87qYuOmtA5K4=8<3RH#;+=FO z7HQi!l#~=q&2$&tXbO+$c2Ls1)x5nRxp(wdXOj+uRt(si!zP^w zjiJ1`vU5<(nrKpU$ruikx16yY9_5xGts5H|dk2!$Vtn3%kZjtRGK_r`+9kj-ag6;q zv}hb3#>6xBbBbA(-NlFMS0myuE!68cMGO_>P7)0i9e6xW%_FI#V~d!p@(kFizEn@ z8!dh}5I$n>ZmJvmkuGcYY7E!E=qUGjh?im3V}miqpVqIJ5AM*G#{5e z&Bw)JfFCNCJ+JQzsO%CSm%TDtMY6rVINkRX61jAX7q@?*HlSLTs%F$dx~w(3nugN; zE=ZcCGkPK!9f$CveMh|&R5lN?=hBq+kSjCFTU}|GgkEo5XqVvX!z3)@SW54JO+(Fl6{zx)x3{G>q-n$vJUzm;pIwA70*9NHU}Er z-osYtf~2d4r#!erkc_M1setiYjH7jj`@cQoY?tqlm+retx2TcmJlTB2f zzb@ryFk17BM3@%=$D0w~L^8UA`v-^xK=jxXF-9Hu@Kb&@EY?w^hf|9)%Uk<87iRr& zK(SB87%MIAUf80Ouo+#ptv4N#&r+9d$HH`j#(7w_JzJo!(mHS%;>&(NeWzhI5xNH|5UTuKJL$9)3d3eRA#rcb{Y&TctFwN5*PSs#4TO*pH zTG{N-Z?Lk}SfWO-E@ot`41OO5I>%7~7ym7lGn0 z+tAD5&NeUGh(Q;?$~G_C7=uTv`~*pN>6M?RT%cEZ1D?+!y(r;Q)pL-Y=Ai#FH9hTn zC_|tme6QmpeftMY3E!n@sIDMC0C1&6DfLIFQE9PCy$scx#zkAmKSA}U#Vhn@sA*~C zmHG?R{Im*6{T1q5J*~3Rf1~b7tETkdsYj*NRr(*)y=hI9{wH;RT5F|Wp`Mo3QR!Ey z=cn~l`d`$M;UH!B8-^o#+GwT!1HEI~6s7(Pb-pXT82WIwkqf%6$8!SAhb=+V)4AUg z%pl}SU!(LW>QU)il6X};&7Uq#0!jKW&w^__=0825Q5@T`;H_q$Sg)+xnz4Qkc$KzY_F z&Bwi;sJBAB-j~AphvAhl4r(}VSF)?xJIZYY1&P&@#JWt7i_x)}{HzKy0MjM7ZVqra;|BbrRI}z70y947L09d!UYH>8GM@zQ>?F zMDBEe{d{K>cLvm9zBiRR6Y6N+Cs13l@Ux&!@_nn&*-&Trt}1m7kMZ?@7rlEuz6lj| zHXfTY>k(w7Yk?dERSH9=3Hiy?0yhC4O4sX3$44kUwKUuel6^Z!TNtD=v(~fh8SH}n zG_k5Wds1+#Y~1q+lop0eS2DH0x4@&Z$|t0UamzIhW`Q!oklnQ!@NRSs=o3_~CjK@m zuLWXoD@GQ+30Yy>3|*}z%pC|-*SS`)z=7`2Z>23qUmUb5VTL0`3rq&KjCiiTq7=OP zYGSo8LHrVrOPkq9TJ}~z?-IU&IjOd5k%&c5V&`xTOsH?e)*uHh_Z#>;g?&jv1APtq zGvq38z&#Kgd=nb$znZY_XF>}!fwe!8raEVf1U9RlT>x`x4M5FkRb?Sd%bo*lGu@h( zzyiBrf09^!C^I$wo`Kng+l3NZmViD2^e4Sqm4N<%xxyeotxG`H!Q70{O(lxoUO{b3 z6ft`Yu$^>kZ^N|gXJGo9<_>mq4QcZ(G`UniRmB}=jqy$BZZn(5OMw=!4zTcg&@%gBt7jHqeBs=bXFNiGg2guBi*kps^yoGFU}||2 zo7Nfx`5eH+n-Ecx<1oH4D6w^|u?msCs5M2Y?Lc(ZnxRzohPT!oN@XwjYb{o4N2qDF zRw%U-)Tmk;l-e0;3-1=FFS7{aad1%kUTAt97B8_IU_I+~K)PmHq~}bFUzUNoLhM1J z#gwyynrZK8w_ijYD@-pNong!~ig$6zGV_tHw&mlev)@Lz*%?8?n1t4SQTF~}f|l0_ z7S8m8ncf0zB=AI>P4_mQrnKd{UWX2ecO$T# zHHkTBHwIxj*$viJHscl;rO#G}jQ%3#*hB)Xaai+Cfd3{#uVZ5YRpGTp@4VO$0EGwlvy&!z-aiRMSO`L$YiYs2NldVyeXXEy_0wiQOdr5k|yQk}9};6_A)gQbln}zb^y7DyUcI z(d{+$(Q6zAkd!Njyjd@TplkVJswF1fwE15JqQ&=XfICGJ!9N*g`qi`f`QIbQwAXjq z%>X9Y*baUcc53Cc+nw6j!LPzjZJlQ~?9m-&!({9BcgRz)2#0F}C=`eBDB@J`y=3G(zCVdikLE*!l zxHNyjf4${D%85%$2l^^}v=f&G32avQtxjC(6ZjQ)BbKk&eJahG=H^(7&uOk{O#^^l zi3;?G6xv+(B}MbFK3aSo&~}k_E-LU6B=^uscRHg560$64fips&AE2H1CM|KdNslZ8 z{tipqPw2I%z&DDv+}QD`wqaWKd6;;-o^;f14rIV|E6oqOLwk0s%9CL}PUx7s9U3Lv8d6Fv-iKi}hwJjG zsKC>T`>=be;>u{XxPEZ?M!lq`q5|%kI6;GN(uq>z2D&Qzky2v?)&T#2{*O6ljyZF` zRQO3JZqCdawJiU~owzwOCn@|%CvMKn7lBu(jr>nLaWnrazyyD$G(5bZX@T*IdB(|P zNzJAUJgP{~mPXP7QFW~B&N}h%@*C)?NbfpHQm5<(fM3Bk=>yy7FqiIZqb^RX;+yo5 z4buWQ!?c~|3yyBm0{1HDV@D5bfgb?X%Y)a&5?+mA7U+`_Uh@DwfL(gh=g#7rP5!FF zFFA2@vA*sG%l{iEPJersj8)8UolIMo99N|8N+W3j+&O0!_j@O9>yl22bj?XJhuM9= z|HL=R@Hlo*62z?dHARa0Pm;ZM-RfB}-KElPRotqWp8sTOfv*%Px>S;_vNzPXGWL2R z_h$Y}*lU>sEwEaVVoN32!u_n6siiW_1{{SWuOs*-rTu5=w7_vi$|x<87ATK{PYH5; zX}FYL3rq#+8GMtn|1&4^NH6e`VpjX#OnawQZe&HT{@*^fo*Sp=Iscok1s+q(-2Z0U z8a=wPRgRiYy7ZxJ%uzW(F>C)f(^lg1ieC4B)3rdOCRPz{_;04I#M>0T*?-f`v6776 z1AM+csilYhwtZa^_FVSfC;;;yzDcd+=ah{p8FQm$KLo;54D6(~9wC^!LQZ2X@DH%I z&46|Dunociv_NN=*AnVc0@?)gw}g6o##f2-x&reOmi=aD*+Yw)7HHetBK32ULLDKs z)B>v&bHINxwZKP;G|)-nKGceog&)b&@J$-xB-si!OOb{;NzQ^jr zl57PV4AL=tlSVp8wt}5jq+6UMXTefhTH!|jClmX6MH=fQ*$Q?Fq~um8*mx(&R)u`wt_{s#Z@BC%Dd!L zXLy=hdxE|SU+Ba`>xjkNrq!Z?s5K}Xan;qLMxq6?iQH8AaswXQczYm>%2Ev(xY5st#dB2{N`?h`Da2;s}535Mn^mag>TYJC7f=7`7uK0oK7#on$ii#>rSVZ znJ^a-ddmsr&WHIOLhm}`eFxyf<_$Gbp^qH)X9~?d{p8-;)8+?8a zS3%1y4@)7f*Ho3wQPFY&j6Cm|6crt+lHW1+TAo%UcWET8Wqfxlh{uM@aOJeqTJ{5e z0Nvf&M*tq z-T_%fJEQP!PTb78X&+qk!#Al{7}r`p3`=5PSbK-9=9p;3JGMO-)#5|~IR3{^b>OJ0O99_KfFswXofB-LfmzRxPmB|OZ zQpNej(#T>uBqlveY<@Pc7Ed>6Hz4CGYdZmxUisNG2cf1eQS_W%@)oKPieo5iI{*_#LigzYD@OlH?G=DyyI6 z6P6Z;#aQ8~-Q<%Vx=Dqk)LM3ZnEMgBYOAOCYO=JQN8ET`pyXw&B)QCxrdODVxM*MvKKP+XR*Az9;U?)!k%6l>3}u95l+^+t_9o1OmuoAaG?Kx(Z%nHMdeTH!%LPctN$Bak zmB8N%?*4w*Xz{G(F`!A4UAa#}USN8rOZMwv@?x`WwA@dC=+^~Ix9cr$N1{Q>pB8Q$;w4RU#Sia} zhb=}SJrxV@M{v+9jBv@3gRINk$IFl%;nHf~6aZZNsB9n2V!Bf{wkdx6n9`HqwM}U5 zze-L%J)S!)nSAdXS_j_B((h)JIJtLx)lo>O`;oQkk_X4T%zI&MXuf1u zAota(3|Sg2`(Lj$?H0$R}m^Ex`XNTsa4jVKmOS!kSR_zY2MwpscS8$_lR@FAOdp#*FP z4+(h85zvHh2mCm`=}%d(D*r0+eon!s9PmzolWw!TPdnhF0uNH~GY*(L1TAl+g3mZ$ zJ_N4iol)?!4)|#a9zEU4`>X?&JAm_=DEK)C%wu#dZ=!;qcfiMhYI(aA{DK4KWYzNC zQSgfnm=98GdA=D|(Oz=E&r8YsD)?mw{Hnk!75s_=ep}#|6nxGBeAHu@Jv-Hc%-}Jc3h@f}iE$|N& z;9-ii#;$r)WJUi3mPCY0Sm=_6wZDS?CDy8hMbKC3@jNtWkP5xEtH)(v44=?J9XX&x z72%A9HN6lo0l+t*V+d2wK$zwB_fD?R?eC^n_9{Sf`#W9`#_DAQn+Grn-AX`10m<#}-Ah2L0aX};;`b;4odG1bzxOIp{C^eHyF?N5 z8;n4-eqvYkl37w+pA4YCOD*f%F_`t%0%ZU-Cp_52K9MG|+P9U0hK3=twaC;0%Ye%b z@r5pCV|P8Jpb=rnbd>@H;-^?QzTe^+qg+jVf`o1dd=|b5V_Z?Vj$jKNI%H4CcLi=& zv|Gb8GYq|Oy$85FATq(FZf~~wn>RIUfy=*hMt=6F(uCTPkSjkUo;Hp}LggTn_^7$#YRN8A;IlM~& z)kNlLujAHU`ANvNG%e^|qkuQ~Y~U89y~&5IcPs5YpKH8dX>akl#)p*lHXg^Se-c{S zd^IRpsH{BXhI`sy29h)q=)zF931Z#TUIh@J>q%P_f*Y8Q1AuI%QGG#yzDYmwgr z{3z}N$Buv&65ip+yHBV; z>MSSDLs6I2{ibD4*y0}wwwAXIMDDWE?j6D24mp@`w|4+@f~4)WL1b!q>2bXrtxz2VpHq zM;wv!Hp6bU;Hxctt+8oMeX$HW&fzYwiN~87M3XIER+9Q~R zvT}qcp*huheF{p|380*m3FY$hhlGZUs}j(DWIQ8d&E@BXs|ZFR(CuWLu^2o> zZe!SHM6bQzeNOJPGO%5KUbC=qwO&5}tu_gavlfF-$3`$}0Ubxi3l@XV%i0)znzdfH zfpUzrm#t#)F zbJ4JG^Vhtu4pg3GNqR?~IIYnVdeVEy0EbB}IdS|%aZ3{HV~KsB(;#?T;#!~><~@Wy z(izwWW#6YD9@B;*lBEUS048?^U(~ysGc+>CUx8P^qMG!H&U%EyS|v&c)|&XIrTcdU zHktTmrTVvGDP{xLO3r@y2Rv1}uY+-0N z+q~oylC$%?%H8F_At{5jn;!PYd9u6Nqscf)e-H`lB>giiG0u@*y%QCV=VdRs5)?W~ zPYX{#YSACkJL1IrtMHW*^L!zmoS0|AIx(LjXQ{3~>B?=1z8i_}^-rpw5?L|x5zAj5 zTiIxGEnr3(juUypa}E+((~b*J1#p*z;H_y!WSqW~R)7)IaMyqk*D9G!C+^R}H z9I+j-E|6%JZ|)h;w3L}rzNLDzuY1evYxYwgN^i3(=mi{-=1gCVK<0CLxh$GyTFjXu z7JnTU!!*Ambui71Y0jMwe@>vR5A+uFM-^>exgRL_GY2f*E$ga!e`5>GI9e+mwb~81 z)cWV_M18ZkyVgV1?`vgj*1YB`sJS=mL3w)yTQ7_6KWg+NtWwtJJPH&gnzvsLS4 zEClZnDTXVhFA7aL#xLRPH zg0giE4}qRwLX(>V}dY?sxTFre3tJ;Bc$ZUt_VKv*~UuJ<) z7#ziZFUO#LJcv269c;{qb};Sb7$NX0;ycHKi8DVfkb(JOwL=AF94Y~Bh*nUx&P76? zEGEM3;A+L1*xDiSp>R78GTXt-u$8n!^b*V|J$vuXH_2&D+3K=&VLAH(U}!n3j~Qq! zXXlYDbvb*51u~bj8A$r?#YW2>up)9fGg)sCD|I^8xaDbNRacg?-i1h;>)lj1^w~i# zbGT=jaX7I3#QcJ3DH*6~N|tSrtBqf_T;v)IEV0NPgJ0w#Cl>#s%*MXRndWC`&h5H9 zw8%YAe+(0wSMELqf2K#m;@$EZU>O7EB4>J%`2Sht`ZYa`R$0%E*L&AMx|A;KT?cmO z-l6}EF!pbVO{tA(m7NQ$f9oJ@9^>W=H_oQ5xdctCW=(kYcobH1)(o(kV<@G61S@5q zHDZpOH8y6%tTF8q7$I;8@tw2A#F?KK;8d|@4aWf+D9g+$&{{#+ItQdc^Oy+Fni8u= zA@-GO6=ghg)|eSm$LcW&LbXG9_1FTd*$xYlVkwqU`d48a(GFscYzG@Nq8&^-BWnQ; z5@$P@IP=p2e_(611E+#i*32r9fk7uww$3>z&|xOR?NDO%IBjbO#xvW&%+Q)@i!ih0 znlX1o#N(Mz*Njv42t3l&N;P9V2aUiZamD#n1*_=sY)b`4|4umjRH;jZ7Tx4-qI(Bu z(Oo2Usz3ABQU+7wWo#Y?Ns*XPMXGs1$S>LUp&aw%c~sG}&uKL)u0)iq4K|tz=P5JT z441VTkMxtq%+y{cTtQqjuep_Qwmq}E;4GP`hCpWJej%-9%~d475z10;6)M?GE6eP+ zw3>I5W-FO_#3M2H+Di5{X)Kvq_rq<=lEtrPX7-X*9b%PC^#M!Pg|wOzg={Zbs2b(r z#kOV1&N^tWa0T*hv}$W2D-iA}&nSzArfphrEv8ttxkkNxL%C{ey6+WOu3;QjTWs){ znm-`lf~nd9qsHfyOU+BZQ5fpFKL%SZo|jwyB(~7vEVpwQObfWznX|Uq-9|iJId9Dv z=Gp2Nz-QuHZJh(x0(Zjv1flgMpyw5|!2y}CCknXMW4*_>+D1cN=E*A0>`S>QrKR*h z9rS9C>8;M8G46#TH_o*^%+#aYoW@2QxC&dn+8BM1`t$NVDKO)s_r2u=UKWUN`d2RV zT3P&NG&YYS(|>ji{=JH(Exj*<4!)-$w`IuXB)tCWvouZP zJ1^3IbS2z`C)2OIt7*64$`U_wEt!7VRnYRiu)b2+e{mHgm13{9%qZ-ehO-2~3g%<8 z)N)XcAPgH_iap^G7qT6;4UnR)Q+fU78h)I16gvH?YxpG`6`evl;gV}a+BGNzH&4Ur zXtC9wswZ4<70yISrU26ll8eFe8p{fdU?@(@z`q z5}@x1&ok?3o!k~mxRlzcKNaszv(c;*+csXf2S!!dgzIYfCeBwu6uZR6mOx~lrF!V;kNM{Jv8bmcwA)UMP?6KC-rO| z;hq}xjUq2r;pLRKja*ufh1LeGX&=lmJ+XtHJ`9P<=!L}b>_5EvGM?{oy#?09QF{8V z3Y}o0{`ASp!dFk>)tl+lm0^-e!OJj}WwKb(((hD;DPnM?FII-BV(_Nltqjw|ke|Lz z8H&V!e6}jfEYspm-=!?~iX}FEzcTQ;lRmd&4c0P|cS>RX;E}A3mG_4<(*2YHRp7R-kPdiV>mVr--Wwc)NbtWQ?tY(7aJishhbqi=UyK`$hnA2Ky>t&J0 z!;2-xvCtTQEtYVMzczQiWBfPtMjMRvFLMnQx!g5Y$*i`8DqA{IAf*ARO4Qem5s+l z)-rfNKh|H{_*`T&$NIY%nIgLy%|wndZWcM#m@IOJu~g(t<6e=AjHg5{ zHqMLOWPB}hv+=LU{YH5lIFI!oFlvf?$mlHcm~o59SB<$MUo+N;ykOiX@?+yEkv|yc zMgC}fE%IOEUy;|0@;G=N>yMABCbDc)Gm%xIdWx(XHA-a7s5v5QMXeIqG-|KNW>F_a zc8YpUWap?aMD~xmDsn(nB94^D`e#Sg5;-TTwa7(L{X{N~8ZUBP)I5>vqc(`#6?IVL z?x?3l9*%ljnI+Zmhr96A&5nG!}Wgr<=&Ro-rct@GKHJ&$CP9ou0=<&iA}6@-EM}BJn>8SPn5B zJV_!Kd2&Q9_OuYW#M4{kQqLHX%RE7m%ROsEuJG&^dAH{Ykt;p#h+O6QN#tseZ!6=k z@#KhH>uDi!ou{|R^_~eLH+U9_-00Z`ImW-~K@pqJh}gn69FFmCeO0u3-WRd`3lTdm zi`e;>h~1uTWbR22ac`=Kz1br6=ZQGbOvHWdMcm&@!~;V_92z6y@H7!eZWr<35)lur z6LD;Zh==bNapDDBn;PSP$JuTwo%MiC_buq^Ir@1bTC!xUHixj>igyElx82P=3 zQGbdUUFIG#$E1n4^+pln+l!bmQpCi0A|`DSG38Ma)7}wr+ixOfl;6%MGt))Psw-l4 zM-g)dh$x;QBDhe*?W;x1eL%z=Pl%XzPQ;y`ikN>@#9a+`F!aK{A{LJou`DR!?$shz z-7jM884(*k60z}j5u4I>(tS%K5nFE-ag*6DZEqK?-8K>JPm1VpUPQ;AMRfA*qI>7+ zBD%B_(RG-JZb1>iuP+*-tvLJ>=E6R~Wqh~*E6Sn;BWyDx}X`KO3g<@Pel>Utv93=*++ zmWXv5MXWz5V#BK9UB;QTyn=B}K$N)kSP?D`LlJ5j%@T?Ajn=_dXH(9v5-o zWfAv(BI4kmB94^X&zMK6i+HfPh=+QKxN*FQ`ge$ExK2dl10tF}A)@)~BJ#fw(dvqb zHgN|Svu%cm_6@1>lfrzfRiDN6Y!SirB5pq}V(xhn^ZpQVXSsuPpI=ABg03PK7K&IjSH$8? zB9=TTV(CjFmVGQ@`JWWc%`d|bK^w3x>m#+kBd0}qKLOY6Y>5P5g)`IrTd3jA{sUo z(a4+d+W7Tw_<&B{DCNXo#}UucnBFD)wJO^SHyr|>z_$}vgb9tp_}=uAn`c>deeiYZ=DAYKGI#{^MK6N!GX9_lCs-FkUvAUC zfWF$Mx6Z(ghtywjubBh>`_Q+UI=fnn z25^wYtUh(~8L@&*fEriT)+Gii{wa0Hk|WWEUITqX`9 z%Vl(V9>YroZ({{d>g+DqUy2>?j~KS7bUQn)U@|aOzyej!&)kLi$bJs6f&rxv@P4ww zR1ijzumJ;2Md+tW^P{KmPZW0-Fs-0DXbKr*W1I)012A&f@+$vnwsKsTWwrdD$WL1S zd$fnNyy>*kBHyo^zC(v9r};>TQ*$K5so)$kI%088DxaU-h5Nwx0N4oaOKeL!6+Qm3 zlQkCv?XKc22wMvzgW= zp2O(=65qCa+93w!_@tji;~J?=ne-&)2foQ^QJha$0eD*%SAYpaL9@)-)MMbB$G2S{ z2W`?>^iR7!d~bp_W#3bHrOzpxdl_NyvLCLuGm`TzgbC}hbc4dH8f zoVEB?nCT%AB~#Z*g$MJcuO&}#zA$+V0v?6bfcb#;o$#Wc#vP{k!oUo`Q&&$_yq|#FOCuSt_e?Es7Y9*l5NB#*3r^lHq*!v|1*2Djoyt>)uc==~{8yl+Bs z$fh*IwNG!aO6DKvq%`OAXdePgX~mPHKO=YDyj~#lqK-iFPYouf<$ce9nQ-Vs2AuS) zKj@|9CBe$~XQ%Wx=SCQ50W0?mDT5t|)P5uoyhWos!cFUs%||{w55{}S}WaeNRg zj<@8>e|Uda27h#SgTX&)G3J3>*53(9KX+>8mC+(T)n?G$vmeH}arl3*DjwdQv=B+x zAcL!<@dThaVR944|9~)}(J00Qq>4TuDyc7+;vYO1KQhHP{KuT}BaUvce+@UP9>-J> z`>}|NhW$+?MgaJ2MSNRE(by)~)FYp2951aIZK(2X4n|j|`0j-wKA>MTm?~2SQwCGz z`D!ltm$M35`Q4SRw4oV%$5Bz`=Gd&E7gNSIrUa0}v6w|v4gN2Iagr!gMcl+On8AH* z(Rl)VDQ3|Hrf8eajqyEI7Qo<3K8?SrGF2of$wj0T35A;?Mlsls2Cp<4d_=zJh9KM| z#f?E#&?0H;2C3v>>=q3a9#d51WqITJTTkYGY@;)eV(ZO3iET{g^Vr5_ zzKyLf^CGroGQY((F7qn3@tN+S7<`!t*!nZmu}#RVfo-|W2H2L*Y>jPVW@l`ZGW%nj zoH+{H3Yn9zP05^%ZN0G^b#zXQpFIbLBTObH&_B%ss?h##^PQcPf&c3RQY>*3VE{Z~Td%R>oiDW+AZY zs`OI?(fVUqAYrf&_(*b6Hvpd9lUQ-qGna`hkTm% zBc%oz%A%ry$X@ikoH`1H1MUw3SHdwtPAwDC!W>7sID zB+P?NQrrO*rBQ914~nSVh_BIz0M+PGb5X9?1PnFvR)g47cIJ3k3&Dx4jPCPbvT2iH zi7nTJ^jO{{oz?J8?r}^*Qpn(^m)2xDX~`k0)+h)A@2iWg(2Rb{-=sHM3-mBuQc6RQ z1L5P&u@$*s)3=r6-RLZU3v{c*Ty4NlFqTL2#9mj!%FXofbcR}+KzYq*4xb`!jCQcb zNva1@@q@8x2vX`fR|=ot@u$A*@(s}O>raF66;3bgy$q~IX_*z}?;@#WR${~54F1+T zpb1)>(!_Q*+FJPm2y5Ek6 zenCim!T5m=ZzDY)-^%0lBBK||9W@_M>2P`)@kqt-M{rRJQ=ASdlTg3zsT*zO-()Ti z&CL0dx&^A=w+J*&<I*FL6HxIF!4HAyc@mrTaI5^GhbO$ZBN?^Xa~{e2kqlIk!;E7{ zy6PIA!y11Xo7bVIE^wuM3^^>dqpeDv5L)y70Q)0oqST&H@sEj6*(|BOp{DqF9ahx7 zP%HZWQs_Xam88&u4u*Q2&y6rrhoMkYeep^yfSTq@QtEK1*ZZnM{pK!YGzMxl-wjZu zbtXcs<7=!`F3Syl`A{pMNm6G*&G#LGpO9xmZSOk)_1h&t?||CfR}sXiIG|3w3u-?c z{{T*@h6w&t{GBX_d0jA94Av zLcf76oDBVx%a@8KZ9}~R^ygi^I?y*_l}Sy3{+i3z7YX5c}8$Ctw$xQ-b0$!~>CC-OH~d|^rG zL4Yhijm9nHPr+ss`E&Jzc_au`3J}kK>Y5F#ayQ*rL-KZP=99d+L~YF?KT6iK*z6(e z-cng+o0R_+EbEVzjmvan@H2GVs+ad-=qWG`>G+D+d_-`YJ*G5qs1@CdU^9Z;`xRFhp^RiuHJ5g-})$8+P z85~8m2~J|`yg!oqP?D;K(mRJXHli8U7+}IG>({+XXwU! z`cy`f*MlMTNtZDJ#3DYNXK+)r2Y4J)(?#W(qoPM>W6B_EJV*5y-S{k@!A;Qzv@tbZ zRGw!j`jIxK459`P1&a98pTSL0+%lj{O&66XSc=lcMlw<3M;;v1qm8NQqH<$&Bfw2O zi}3+!UAdyGh!5Qu+!T$W4^z`cmC_W`CZ!=-#3yqMZi?0uU~0OkQjq&-W6B_E@adzX zCuw79x~Sa9xw;~rL#XmL!TjJraXKL)A}tqkDlh?>zsBWnN!2V2EUKV zn#B)MwAN|Cai^bBD}IE=RysXsq|}O^q_LfY`3#M19n2SK?Brmcqp_odd7j2@5lpKH z?-S`7LAT2B36ZxWe6->(X?!b!t`&brV=o8uGL1bQ%-?9d)xo?<<5&k%+X~}!2Q!Ms z+Z@ao8V@^|@iZQCFcWEf-@&Xz<9iNfI*l%uy|=9P$|9n>>|JKHR}CWDBcl7cOlrk> zMDB^m!a^Dm*%Il){iRmS+f;HkNBWpZTOxNx_-Mr)X`B~9*NVH*xX{7uL*oJma}bRi zBA9+oKCQTb$odGnzuih82AiOG6ycMRj^^0DmB?d}j^+TLMC9fOAFX&AjeR5NTJcO8 zCpwrx8YeiI^J#p_!CXw^lMd$HG!Aqy*U&h?!Q4pWR0or{2jxt0Fn7~9)WJMJ;}8e) z2#v!X%;Pi`I+#z;ILg6%hQ?bQ%ok~V$id{TN;wZYm~YV-U)r#~KqRiTVf_`6XB^SL zqw%yO`cE`I=V1O$<5>suZyIMinEEytXE~VBG~VuD#?csbFcWF4Qd-?A5~*BT-Kr9) zUJ|ip^!0>uN`|zSf@*{_O2S%kO(N+f<7&k>5Xmadry-F*X+F(~oQP-xs|0O`9FJ%S ztLUAG>{bt;Tl5|@?g|;r+1QW9a}m+C;-NIY5)l}4oyOmEp5zPf!Y0u8YshHEyp6^y zA)~n<1Zn&;WHgxzX}sH!`*IprIC5W0<5CB66OBt8%Roc zbcDt)LPoQkCusa3WHf8^B#qyPj3)Cejpaf;%!?;l@ryJjggVzWzDnZ-M>*f4@gqk$ zKc_J{6qC7IUHSu&q)=cM!5RTqh*XU9k#|KFN8JOYQlyWG_=$KTeaxy>BI1tpG3y&3 z5?j*8+QZc#98=QKf_ax=&ZmwxXhh>Djy7mP<2MfGO*DS(V0NbQ{s^X@U91)NCURc{ z-9iQv*%#?!?rujA*&FF&BIAg>5#gg1Po?qo2)b5$x(tl}I+(M`{KvuM4VF1=ban?v zq}eTtXl(B=uAs5A!?>2l?h!^S25;HSc_#wVig(i3+hN>G<2Z-$AdNE|#-lVIaTrg~ z_<_UtB#nm4UM;I!XNc^Gpj$)ZB_dlR5wqdnATmD!(Td-pagoD#fyRvyM!$O#hJwMO z6n{?m@krQg@~?^Xi$JvEA84H9F#by8DTnbYje{Jc%6$Qx^O zMmvm^XguaHrqNiowARica>hZgP2=+pV?7$@IE+nboa->QqOodexwR*fTN1Ilz6;^Z zlCW0Xi%529NPi-aM3lgaHjK!g(9X^5#!)oB8bQ~JC(`(PXh&o+r_*@VVVq0jN{2gd zTh3YLFfONYt;4v6#!I37gPGB08h>;cchOirRAF=fu#d)%9WfuIu|kN+m{tSu&gq=% zN<&^C5?vZ{o`|m`Vokvh34i7&0q@Dq`PO0ln#O|>Mr&pLnaKV~#GJ;wt2^h-2t+G( z?SS!Gq|w7;;TQn9Rbu=Q9^J!tMW-TJql&z1?0O_?R`Kw>S`8#?RrNfLhPe^R+EqO~ zC(sPZI#oT-!`c?fx>Y^A!M_`ld6hk9kQ~Hh6%TLpAIoG_509W{Fq!6gAISwwrh9nP z|5_$9JiPILH@od38>%(h0AM@8 z-8@WC!A&r@m%v_*T?cT8z#edSaOk2anXG9Qj^!jp(q_~tD0)NAk}i5u-iEqIBx9m+;& zI?vUABr+G?!QYsgr+Q)Uc94wwvAsiY@;9Ls%t9tjj=l?TD3uq~S`~|ip~_00cP7c~ znRwWeX+NrBn!Q+=wk4`8X~iguY(iD?XY63!+1Q$w2SOaJNr2AVRAs}>VEP1e3E!de zlfnjT)3!==ryPR?Z=`LV>O@K{*qFBEd^SZ@)T&%_0;@l@00uBum0)WE>%X@EM)8}W zIuO{Tiot4|%^mDUV2c70(X6(6ri_ZvWBP*PjtUMT^C88Q)BJ?B;79_S*;p*5BSdfl z(FYYOZbi(67R{K2WE{f;r-Rp^z3K!R5TQ=cfHZZd(cTDgjtcwufjt)exbfV4?RD?V?c0%`D4<1bUYMEVsvqb}fNw!P5i= zMzo);3NI3EtJ+1%!WdT2*NJvk^$<6rTJT*0-BnOqAukf>8{uJ={wo4EM*wE1%LMwB z66y~E{UbcgQ2!Dbpa4p1)#bB54LYjI$PfrurcoK7$z=3Uu4VzM($>>n04AcpBjYAvhG?FCmKO5Z)hxBa3cv7Lbz?$qAPtcsJqi>d|m3n|-#0 z=x@q{((KGeI|1w`_`3onAJgj)folp7Tk%7*MTLqM4pqzt$~;M>Ai<}I{Gmpcj7RA# z*+|$%&w}y_Y5yu(Xwi~6XpOz|q+FL0(?WtKWc{}CCj-?oBS7|S%xNm(7?ZHu*#%iWO%-|$JK&v+P;af<&>dKUWQ^$ z(?;cC$fof!b7H{ISkW5Q1W=#gpCv#u-{R&(qeJaYU#<8i8od!lGq*eS&Vl-B%ShW%+Cl@&jQ1#Q zzeMt$rtM0k?FHIG(?xuXU#HDk$Mh6Z8eZ>U@~t!t zb0UqN^#^d*H$pV3DmTntLDDtJa}d^;dvRBsX)TXr<7%GMux26Iw5o@%`KV26POv3J zvsS8}e3nlOcBHL+q^%ch&GRB{gUAc*icQ~Ywyo9R$cPsjT;?NiuWRQ2>h@;fA5Jv?pZ;zealb_@EjT;-2a zRpE5@^FWY$%tS(?ONCMiO;wN_T*~sRTm!^!Q+OzQUIlu?_vTg)sI-IV9o;wZ?NRSl|&D# zU$J2{xPib?^()o}Y$vcr?WID1^_yld(PR9z!p3o;hls9Izha#z_k{V!)vs75`V`T* z5zS7D7CcK}VF|!Gcs@sTP6<>CzD3}!5`g9Q5z)|$m9m(*eNN!^h;B52?+7d?0a)>V zCAvZ#T`*U(acI$ke-ph!p|;%k;CTLgJD>%9v@NsSB$O5`M_^US8cBBtD-zxCU*$_D zy0N6MSw23op1BTyNb;@hY(&^BbuQ$;4zJgkzMK603&@d<3>CeKA}Rlbr;5DXQ66N-g*RZL{o5Yh61|AauTfu@S=4?kN7h3P?-y@uagz zx{5r5VSNY5R_3|63$)H?_yfdm_*T7@k9K3S2mhirUM)0y#M{q})+lN}ZVtz{>SXF% z7w)IskltQg7Qi$GoDqOpusl7+va#qPgCuwzZR3>fCn5iiZJc-4AOPaGHNAG(UzPQQjYWw1-q zC&Yk*f8pZY>zX|BQp8)^mgu((2bvCw*<+1DWW2%9B%8e7>VU`RmQ7wC@-*WDD!D|j z&L-kx(ROT!z9|Q0K2B{nuh6p(VqDe-v>^oTHly*ew-x$U;QMa`ej0qYOc++cx&PT@Wt+YfBGRmVR z%%70`S_yN-445xD%!~9+#>4plwBssz8Klr{e6Htiz2fZhhwx|*Ct*g2Ayd{i357RA z@j6EG2o^mCaA8jb85II$8VRUl?<3%R2e?v4bIIpuv?=wzy+Ce{Z)`tZUnTgvFHqw; zy;?U`&7JuM^?|3qHubNje@J~07pOJ*BE@V9D44h4&y?W*VN~nvFyX5(S45O%W?u#Ppz1*bnLBlG9rK z8OSu~u>*z&;L{z+^j3P|(~upZ%Nuv+(ij^xA~y8K-G;F6)83MNpKh!RPfx?eeB-V{ z2H6<3z~IU>BDMy${0zyD*R)N#_96-}j8~k7ycWl(4^Thmy1h`{T8GJ*bnQ#ttuZL2%g^k=6ym^(`4YqOfCWq1WoSAcs@Td_-Xu1ec(ZO@i1K zRzZE2F#mP>A&4$ks9RLXTjY%xPIFQ>kGu$@guRDn7@I`d2U=m!R>RnCN-qrCk@XfC zh?iNuiDqY{p;WOG3tOtrV8eEY5^C6$P}s22O{Yvmx=BOpNH?(<`BZ*Hi=zEGJSRbq zohZZe6p|dCBj6hvo_NAM0wOOF0BW{$t0V4)o6(ioBBWmp_J*1;mkb+#$m zH8RI_dKonM$JR>gRf|Aj;RPglI8@jJiE2m`e1@U#BqqHXUHBLXJi;oRhQu`_-1mH zpfk`J{4BI&Y*FDsy5ZmmQ-n)}LLLhc9SL5K<9h1E`k?3y>JAw<8dCIki&Kh1}s14Em?7PVLk{vU|gq9#rA*&JP{`B z1)s-z>-4^*5sC`2SJrs6D4%UQ>%>nx6o4PNox;y!5DJH(A;X3F#v(Sch$zf@5W_I58b_tqV6Vtsv{+W@12Oxn zgmfIwe=-lmDNmy(sfu#yDlR>1bSoPQ{SYwVtOUZ zzEK~A8ZNO4dAB8F*KO4Agnfsl?@{*k>-DuLFh37la*cimqBm#6oy>im{s24`Yoo=A z-Bp+Z+9^O_yaJ(+9bv|AsK!My$Vo+0sysecoI~sM??EVGPhPJ(Z9DWm$S?*`LgBu* z!l=H8J+x86m9Qt{X*oVPxI!O_&N|lz-B$Jtjz?+ZD~$S@eVhfGZoYx(1HQ7#d=_y! zAFNxU{|(RQdV|n`p0x451})D5dYbPtdPvWOVm>vvLLZN_OyYI>|H*5Gegl3#J=Y7- z9=1D}|NCW@@|AF2>s)4Dd0rrSx;2gPXgX<4N#js| z1MMHQNUJOv+p%CnDdw#8uEGn*hrL~JOX-AyI;Gi9=)-=;7*+Y)W#yCgus$pfO|A6h zHhVkRm9EMKJ`0UCbPt;U1itzM)@r;|f0pVIH#=ad{t}g=mhw84!$RZPqTe zq?#kOUT+pF0kmG0+@UuD)&&^A*_Ls02wV+tunB%*8UF}@4+9)e5Cc;`VzpbX1Tmn9 z`Ol*uIEbk@8>fx;=>LEnI;p!(=kEDghfl@X=0St9r&_US=dr>iiysrRZ@gv`)$C5YG&|Zo-y%-1q~5pPi};y_9xQajC)_9< z9K?!6g-t*(8|7Yok_GP##aV|q6^oK^D9p2hd-Ne-v(|FP&>SQ)E%HW7BIb#ROiLlG z4P}ZWu#27lA9Lpc9!0hO|J|L;vI#Bpju3iF=rvTSp(7>K^gt*{NFkI^3{`3|NMkh;ivc_Do4de`b!=PEw@|HPWJHB67Y zrIb&Tix~HM7|Fw@V=kh=IHS0Wi$M`ho|!kmlLhyWePMoVNRO%Si^*p zN1?5-UZ+nzr(}9YSml0>1?k!IXRxc2rZry>D#t?fbLoB#9lKntuzmPt? zZOFdAOqfH??A3gpVqIJp!Rnis>9+RxwhX25W+~b`OJh5U(>3b|HU2v}$G2uv`jL$0Eu3?7Xu?Fuz{^oWaGyJyI81W6b-PjDjV-5WYRoEh3Vx%-a7<7;lg_DjUvJ zeax<Z7ji{a|hx(%jD`&9!y3qJCZ>?^8 z`!~^=TT;e2O_ak4)?HRV*>z8FMyQ?s031+@Y%zBF3C>mQ>APEJw1oLKH*@LGB8%l| zgBaAGr+wExkrz3FM^Jhtz#y9s^?XJzTc*L0S{7Lo&X z@4)R)Gd#B~bRyQJe~ssLn+9EIkZYA5dJgOApivfPW=yYi7KPEkG8W*ZAz*rsqKOZ3 z=>!uQ7om+&+%95kd`To{uReKgp%?Pt%y%mSfE+S4@_%NoVY0=9Xy3e$L5sn~?X^Nhd#oD>(1zcuW!`jPE!?VDjb{Y}b zl-o~yZal-(!}rfjT!)E+rW96hBJeq5mEE5)<~K^w&sal8;d@Qzn3r<|8POjh&^6{S z)88#pEDpm$jd^2AKVZ}^q|Z5P=zW+NQ(7jhY4F7KfhnB{Wl1+-&9oL4TYUJz#F(%f zQ3cn8m9qz*3)ax$2z3@-4(?L4W(af@J{!H|G6{=o@S1MI#)`M|*3b?Jb`HHI;tfMD z5rNL3=Rqe9(;0@|92mHUo*9!VorM(zat%GRAo~jgXLC9Bta=7vN(i&PjeZR_XU7Dt+*{^C(RT05rI{^d8S@B@8MaB0b&{=WW zm&r2S>^@ZdDGXG_<)nErV^*fK$b;<6YRQcHm~lHvpJUe0zEUm3$?5CNCK%}+UzrDg zZ>-_;Rh|kq)jI4`eEQf60mYs1{t|E8FNF6@drpB1&Ipxkg7voTzNGlf8DXaIvW1|ttnw;h zb(;7TYu4zZ+wirYmrWkUZqek4=@YHNO%dVowP~mBn=|ZME4vBSAFXmAw95NHlruB? zNb9DSh>+KSs0elDBKw6ROFGHrs&W0fL|#RL`>MJ-4kQ0AZy|}i#zQ4ZccRqFI8jn3 z?X7gVeac9baXS;qTSESwL=&vftUpMJ<>eJBfvSExc*^$7D#e*(&5ZG%?YXgmY-VJ5 zg~*P`)0*wFnM#!{bu|X3uYPj4QJxtYn(d%r?SEl7)jADkO{&9Rh|}yN>Cb5UPW+Z{ zqoWt%Fvc=s>v~m$PjH%j<}wTO{0(t1i#N?S{0p;j*3TWUV0g6({M~?|@&2332ua^; z?RKF)oprg1#CR`j3{RWy|*6n^xdGcg%l}D{l zvdW8WaxHbp;bW3jj@`MIIW}NJK4J}%&2>hcjN-$7$l)k*NMBJIhq-pBd+#P%FIR-g zcYij)bw)KQoAFu(slT_n74U^hG{xGr5@J=@E7Q^K8VIX?)y%6i)~v>Dd%3sSa}LK- zuEDW2(QC%51*TZ%W8G@JHYoRDy(A2j@nQY67)`W}l7fwM`cs3>sO}ZJR2L>K#bmXc ziBY}tW~p9C3mki$$(LqjYWPb*j7zE{+dIQi5aW_6$rgxpxmuTLilskhs4UjbKG*Q= zv$aKgIRQ9YQk?)?x{ny+;;<*$SC$acXP>$fHn*c(;bGa(@N$9yo6zHB@m&PAL+4e% zz`{z{7Z|Hc5SuH_5jwj(9;u1#&@$z4J|rvUVD=0vAxrSjbec%#xxR3O40%m>?r_iG z4zCYKhTW0tb;ZnuS;isreeq(pFoj2i$iQ%kr=c#tLlr!Sy=@WEFQtk-1hnLifVtJmM_D zH}T($2{$cPIYRQFtIh5HX81ak2Pq39F8DVj{ELA#gC(RpV$Bo@X3F)B(EO-sG~z^3T92^j`M)B9jEmyaExB`RaDj=0d-$Zk4T zEFp{V-!vI%nr?7}2E#%IeP~^*Y%xp^;J;}y+BDth2>k&TGWbLDU>aN$(|Ok0Km!~F zbA93nk@b!o)^Y%nCD#^5Xm^z6p_pS)pZc(C^O8P0t;Tg_Ic`@m!*^SaF}(yC`0TcZ zRxiad-2;Q$Ii{~m_88O45o?U;)}_^$Hiok4t3G|2L)kst9Llc==TI(^R}JOea4$o7 ziM3Z5a|BmLr#MHjnN<@rON`*WFqIK(3|w~u8Mt*|AOjaBGG0Go+O?(+jMw8ZagA3q zW{YXT@oH}GHp4MqA0wr6y#B?&9Is}ILuN{h*O};Q=XiBDkntJ|1Lt`Civh-KYZ#;- zucr4jGZ&24MQA=_yk^CUYrL8!r%Y3f*RSBt7_XmV#Wh|{lXIpi#%muqGsbI443~7{ zHC8(E6NfZ)nalXkB$-H`{nil6Yr=EQ&0yRk>~5cHVTRBg7<=j-VfWbd_Xw}CKHFol z6kUcd^VA_UCr;n2mNnL@dtVd0*4m<)dHlP2Ukuk;KgRitN_vwaYdWBVy? z0xI~4`es<&T=R);+@`bE`kB*(Q6`mF-gJ58HS+q{S`$7(#m|fMeqI=UqU8+;jIexc zjfdG>F}&SoI>7j!d1zl0n@>ppiKWS9|t;V{|(y=1KRSo2}3-uPTu2m7(wI=?(wt}j6 zUP#pjmYi+1q9KF}dI&$T?j#R^s=9NmuBy+mx~e|M%Bs&q%lzLj)(kDiG_efY}h|ofS|GkuVyyg|hJVI`(v8Qc8xLq#4_?15vF*{_i4XD5cnW&ru}G}R zyT_8`b|gz~NS0d)mVvmEl*PP%^%A$AlNs$Q-gp`lR*P`kpKMObjnlGBA>HpTz0C03 z8I+xQf3Mnd{l^~LSj_Ud`}<#~FJV>r427a^yN|PT ze`CL#@w>@=~)vEx@X`Z+LU(as>_v3rW|91VR)9=zZ_;+s~ z_v4b8ZVS`9&ja`L=J|K4H;ga${&~}Q8~TRvwwl)kn@Kaji2qT3v)+)Oc^&CKfBw_@ zwcvHvNOC1<{{GXwz0F@)PG$XzYcvV2UcE!Y3QOYgE&Yk!x|f6Lzdd8d0l z-2KnzI5B_Kd70by-Y|aR9LmEjvWchK-b%Xr&*`N?KFz3jk^F&lw|Bpv+x_nC|J2@B zZim2P-oI_$t!>_mtzupO|Ec}RTq>=3>z#RHop~Fb%d=X0lf8K#pu7E>{F=At{ipWk zJ(Z@v`K-@2vKBuOYG{5ip1f_)0;Q-x%)T6&71cMupP~Ib+a8_V2SjzbAX*Gj>@*Nwwwrzm#^@{!RP7w5vpa?*5}0KIjek)ySvvP9R5;6G&Id z)S7vV#|(b!8BjolUn1ugQrqSi`Ty2}B!FNp-+=pPn9dMRvPz|USR2!-T)rIOq4WPH7#*pOG z0&ESnh2;C7j!OlIJZL_&6#58S2CaZrLaU)Q&{}8%v>Dn0eF|-Zc0l55H@Fwt z2OVITgnx$hL(pO9OXvi229mh5;5q0bbQ!t=Ju9h|$$MpCa{tl&_GI);jT)SxRmc9L zuEeC3_nEXmpzNeh)A}7Q@u=P75o;%8yf9*Gn{CVe@~z&Mty#+W2RUYZWQ#mtd!ZFY z`eP%n4y#_T#L*VN%xE9uNE#T0*AG?6Gc|2v;r(^;k9{_H)qn!OPwtlV{QaR@+x(d4 zScjQGjl*9Q`gVKyFJ~vWJDODMiw6}l7Jb|x;n&onMZI55-&-K5>~P-?{-{*D%qPdDZ7=)Q zw>gfq@y>cUsn(f?&zGlEop)?`!0gye_g0?WbLDJ{JyTPkhU289^I4Z5e!h(0FXD)K$KvJy^ z+wV-?u`91W<)^hbQ>LBim^!IN=j-1sO#b87g->5(>z1|K$_W{(4`_F`=l$8qtygZm z`|4G}d#|QHf4KYOkG!L6d|rOm+C6Q0j=i+S+Ty1(o5!vCKG%sl{cjC?FnGzts#!m* zl~gOKP?j$$=00nmw{BzBEP0oYD>A;y;tvN+%eYVXu-qCqZg$T`hmIdx{LPY0$9~VV zEv`UqzrH=btJl3>$&!yR_uVo(F#3LCQSWVAOF#cO`Bu(vv-QmCw|Z{tGBeI}tWf5y zrz={1zNbme8ri=(Jb&BWM!Q>9wX8go;fq1mwLkBF`!id<7Li$FDuxdFAla);)e(;u zR!$vMvFDtKJ1d*k$a8O3_voM(Pq*K(&KeNp9lNqffh;fQmalyB@V3kCznvBM&duJV zZfqU#UZLYZubf*jYnR2r`Fl3qQ*-a*JXMwrE?28V{GnDK+{oSh@WDaWdDW`gR&=|b zvHXF%NjtBcI~Cb=z@<&CcGT(GVQ}~SeS=;shX3Qg&Mkj1W6SvagK9+Auk16aboO|U zN(HYzKeJ+B*6g#NWp6&KOP4vv9&V26H_Z~3P_9;q@AqCA_rocD!Lsds>wT-Iu5I|? zogN34e6%!XZuzfbK0TQCS%dPEf0|n!ue7coP^n8<`>hA_gQmZq+|C-Bb@!NZBbIOc z$U8&l`OR09znAgLhy}^($M@S~Z~t}9;C^$4V6-CJ0!?6vZ5-C5IY?30Hzwt8>s`oqkn zzvn&_b~fQr;Fy(FPCu$TWi%=E}$RBD41w*mWC@2lO z1U-T*nPCeRfvQ67pkOEgngGp#)!97xG3YiV&o-(G zHHYL05&fab&@yNXlm=aaUPAJego;peC>fdz$q%aKcyPCR0NVAW5q(Fpjpr=NPhdB23?0NxW+CH$u)L!C>fdzEr8ZRyP;#y73eACkrV9> zm4_NY!I1oBW(+h3+5#Pd&O!Ge`Eg-!jH#+nJE%W23YrBigLXs5pzF|MNN!>%4aqS( z5b6(&f>NO+&=%-A^ca%skDO3x$RBD41w*mWC@2+L25o^3L02FT9F%iHrJK&v3R&G8EK z2zm)+DuA&Lm4_NY9U-|ZSZ>YT2+29DoDV*Q~EcsNE69X(4X;b8^#d7+B z3QJAKZ82CK1-}}?JWvK%M?+R)uZ;2IopQm7V;I zNb(I2R@*CD8@mfjY!b?b`b45W7g3*ldysZNZeB*)bw7^2JBMcopxt&{#5ocC1<>D? z1}cwmr2Pbv<%0n#tPRqnG1JllsJ>KlZUt#^qPGXRl)nL!)oO0LzksDttP|-k1x&LDw)3WRctWrG0$R);V@#9&v2#NKk%k+YB>H(kQek zR;8>mhNQgaI2zFKul*^{pp!Nzc|fI?hi1ukyqvgVv1L@-3%%9$^yX^*wn@UdKE^~F z^vgNuD8^MJ%9ROY>t~GVBN$t?(3gLpZSSHz3!)u9KzpXLUz-nCeh;9XWXaJ_l`SXc za3#)#yE0A@=X)LPz;PtM0+aGh8H9gWFvN`Qa>f?+H|SGI^EvV@bVCI`K>4IiLf}{C zdNqWb^Zf&scRDn*BHGH8%2aYw6QM`!TPe zw&>4G&|&uJ_g>0JEZcS-$}aY;(RNwUE-#^AiGoz23*AWV?8rxRr&nRa(3^j z>bNi%PX~i!k)O^)mv>W}uAbB|%)0ByV(GA{Ojb^faSH&Dh- zDBDPsO_mjIb4DKCBzb*;zWErNMD0UX}T2F!~4Sb>!0t z<^2LG$}%^^u|ekNQq+4N^a{#zLQ>-xl|}h`mZ-L8B2O7J^)WYwKrPUJvNRu|>^d`D z2=dK@aLKnc+Dr1D$NbB(3`1FtM=VPxmS-gLl6lw!_P5bCRS~}ey0;gBSc+g=WB{*k z!?`2$E^J!B=M!ii>`u{6o^vQ|v5x!Fw+g9xJ|2YUGQj4-HPrcrc*JKQW&U}8L?JI3 zLubFi@#m)D#uA1(o*AqEzx#-$bybmc_bB{-H_p&UBP zA-{-~bZcpIWv1Ge{fsOj{;!`qS24z=?ca3%_5BBGJV~EQe@b6UKT02({U<-Ymb{aL z#SzAFTUF-t=SojnsP^uJ@&7UUZyEfwK|St5Cov92WBey!{8zn*{q`m0XX8laSI*gF zktbkEK8M)9IX_X`Pq}?(xY{<)73IgsVjqV7mqi}MD%)j0Gd3+92dFl2KL?U0`Wf}% z{#YLLD%+1a$4U>Ye_aZuj4e?GUz|xsSd*OZf;7cD1McI$6eD`tjQlWso^M z9{c4|CvhEc3UiXj1uy#C$bFJLB2&tLh3zEoU?z%$M8;!2`N|lalI6ME@87! zc5_}&9;otL7>o!k)eES8bU#;9rAdFSvcX67os4@qZ5l}|-pIjrH5@ zPxOvg{_-PUmL4dlltGRQ<}slR;v~14dfSdnG%Fcnq> z>6_EPycI-zrG9FdSmg1?vfYYv(!t};sbCfV=_nO%o+~A`R6d7acU}KIi&Zw}Ii);{ zQGAX-Kglw_uNoij=TlbXEq(jWejHmMxh9tDVYwET>tMP5m8C!Zo9E6oU(_g*XGdOK>2N%)WvL%dEJ#?xu%YVWEsOYNq@cD`8D~=TUo@H z+4l0FL+LX)50G;JSsFM0?_6)lTcIQ`^ExSUv)b;?eNfK+fB$*HJ{)7DeCdz312!o? zvOkbT-Vi1EnBz+x_#oRd#%0O5_V3T(a&d0TXCAU7qm9Lf*}jo(px0&#L>M zoj>Osp!}KF#SO8~ll;x&T!yTw9ps$MJjebmS=pG^x9{LMDs7ei{PrB$_+MS$$P29` zAK5p^B9AGTZS(jjuho)mnZL5kVxIE(i7Z}yRlK?H+`}?F#(rVf4P{ddb5Y7A`(SfE z*6yj&nfsh`o@)D3jCEQ5z`i6CSZKexKA#8fgXDTnuG8hbUasrpdR@-%<@(ONZeP(# z)%i{5^zybSsgvvrWx0m&B;}KFXQ*7m+lv_`dAuvKiSE-kFdw)4-RL z?{^;mZV$wvxs2}%CxZ*1JsDMA2eV-VOUaySJF2$YKEU{+jBmbF#ea*rB1@B1YFpl0 zCyQBD@m&*@fIDN6vPpe3)W8E@5J(v%Lh31fCFPd7*&zocb<6?fg7QN7AatRnF!UBw z6!L+JLB*lcP(8Y6?lZ7GNu=9n>Cr2kHWK zgXFLq0!2W5peRV10f!~yd)asphh58HXb3bMLibzVg~mhgL6e}VP%1PXngP8J&4K1a z(r$|&Da>DYOh)0j-9xYjA!iyA|uQOTg*1TsByCLHnSC&>`q^=rHsJbObsMorF$9 z;{ObI7CH~fuH+&l;a`JSp>Lp@&~4~GByrz^KR{2QpBeTG_$%}bdJesW{(!7j%sEKX z*-1|@6W23?S)i;?b|@Dlwz!3MApx z!5UmIw7urW+;xYxT|2aKLibiPCnfJ2+^WQ_i5pj(b1W?TN!W$nQ^uA4B6M%e4+~zc zZ@Itrk(V207OXimf8{ZOrIN4AN?nli?OZFpGY#6(Zu7-UAz3aR8@@5^THL-FQBN*= z$L9}e_o$AxdC|siJ3c(W;ab_{ZAL{j47zY4>R!T+DV5qy(#Bjbk)z#!M^}8m`Sr)n zwz!?M&i0;vqoY2$#WTmuNs~rh4jnh8;;0E3J?dKPZ9Hr4Yf4&lhhf>8FMWAtf8&k2AH)W4KC!6z1m6cc`V6{Kd2m4=~ZW|hi`f3aqOcp(=tpcQsB|FudWU*7g1&YmdJwp zy{nhE<*v8x+=2{wclrL*t9-ZXeKUSN_tw~VHV1`PZ4=O>!N*f>COkZOxWL&BmU>xd z-&nUUb@|v;!!mAK_I=Z@-hUQ#zjIFCY$bnwz9=c|mj_qdP009N(MMSV>ZKHT-gAAK z!oeGNHhW&Ua31}~-i~+t^7Xk{BJ%d~kEbS9&0YV%oJ$RtZyu9;y0G8af!|n8>`AWn z!uE67oLR;#?49MroeeX4-Uu0a`OM)%p%kUpQ2Aak)|b_TO&RyHo!^ zicVU(q_*Y!<8Iz%hWRh8uxk3wZiR9c&wn#d{SUu-f8!^+qw4t0-o8Klr|oGw7EC?y zN5yeF#$TII^t0Y;Hk99;v-Q2lEf!aN*S;^|TGMl<{c87}ek*O#r`M+kd|mRvm{QGF zoT&CH_SeFX-|1E_cGJ>~f#F{l>0bA@>7UF>wqCTqKV?zN#q)0r%zEq0vnlgmE=s+0 zq;{)zi^spJbReTG*PbgGt9_qmt8deC-qC|Cez$(^W7}3fSN-wh4^C`br^}8EaT^~^ zPP)TD&_KA)8_0XPm_g3`F`nXJXzfpr{ zC4UtjQTuF*6ML4d{PMS+ljl_aedza}uNczR;u}75X#HVznvajqo^q;i>n>H-zwCeS zl>d*LM$Jnu5&G$uBNimLEqlND;=AYbudC?Mr}VP<8>@P^`*eQFqrv_cZj2aO{ezP; zx@;Nq>(w**&<8$2$zgfF&$preFE!#T%oyqz(r3k-f@e0(X)){TJ@0?{(-*tGyK-gR z*aBPT4;(zI#nH#fzKPF{JRDZ_LCeb9cV$n>_@Z>HtPN_lpLBcnA`TaiLVn zGN*UNuX(j>lC|fV`O9Ligx@&PsNLBwT2Ed$WZ>?I&QprMIHM=pN_cO*Uh;>Zj=poL z;_VA33C+$g8XVuZ_ug$QvLEq0vTJhL_00DWPG| zAm}|vzGvS69fZz9_n;S07M#=+g{ngG{ONAc0B9666Iuc7fQ~`mKu;jKcOx%U4yp&W zhx$Pypy`l&-!Aut%YD#SA^E;t%Z>hm%0T{5TPPGtgyeqO`H(!S@c?uV`WBMM(q_(M zu@r%7L#-kC-aQ@~2hD}nLHnRH&?D#-lszxrWeL@XIzSQ7U}zGw2-*Z4f-XW2px+^H zoRk%VszWWH9#9PQ9`q6PDRcz70{sBlZ~~VLDh<_v+CcK$Gx z`VA^t0BsF5gStTjpi$6FXa%$bItG0MJ%RLs>O1*zU_Gck6b{MvE)$`J&_?JpNUkCO zfU*_Bc?DDh3V?b-vCwE}HnbYr4V{E;LO(-t?`lQpZKyL81tmk%pbw$XpbOA_=p~f( zEu6DL)u853cPJWq7n%jFgbqOGpl=~-5zHm16jU2(4FyB-&^Tx=v<}(_oq_H^&!CJ& zF_)l9P-CbIGzD4$eF7bZzJeY>cAP}ZyKc)v^`Q<>1T+|$1TBI#L5HA=&;#gqNWMoY z233bzKs}%sCHp|>-a5!la2dK~`EdNsgqPSVGt8{l$D z4QG0Jr>Gdq14hu+veaVwfv)t6Qa$048Bk>gtF=3BhDrn9Kby0|zua~HxgoVx=>r7KYJ6*lHQ}*bv{s=P@N{7twy;CaxjN)?I1*p-0=(jqA7(a- z+R)|x1d1qD^2%?P>kxB%?8>nXeeH)O>v)wd(JoniB8ix9mndH&UFB-}t_tco+L>^- zj|y5k%$Z{jw!}JDOVl5%tkU3srI%y06g3Hb$D-op(MHfr1hWalm#Uzfu0o_>E=fW; z0iso17WMH0XV4PPb-87bbv(>5(abd_{8-e3uAlX%e#l`SG{xz@8mIr5NzS0U z$Vtl85)%xaB#WFs6aagmhfvfR3I`N^Q0QC7h$~djKz{}89b>CK20qfsbBO?XG7%up zAp$b>G$44EL6cnLcQ!jgo^nNB-MAlmi3VZ9ndtGqp(`;IH`s-SN=I7?rjJ@geuH7hg%C&7|q3fwa&p2JbDOIV0P zOO@4HSL57aQRRJ(+*?&GrL6iDbtd#)qJs8dm=s4gSSom`pvJgtqWgy7DyUftr?1i& z0OD5Ow+5@`0E-vP?kt4vWKa#eN`D0VN zCFm#JY4u?V74&TpXV4$85UVhVIqqV;HlSXt>Q@YsJFZ#OsqBIhu1T_mrSFX@(^q{a zl-sx&G#cfR9J{#cxQ*lUM~nk@L3x;n)oNF{7BgX-D`EMr%Bn^|XTrlXR8TAI{^%>7 zV@mFmV(F_cQC4@*oh;WUAu4EN6KBG2S)nnm3dwt9#O;8>M#9h4{s;kuMc!h=^nOfV-j)6lE3p=hK`Z}4%H=t=H?39O8S`wAa(MuL zSeJ53RnVudDfjkR71Rb(k1pq;Q>83rP&`yimNoNLP~Ry|t7+3!&}LV=Sn2+Km;2dF z*wK}+(`=P+e}uDhV%WwdTmxk@&VQv49;_c$W3dC1pc>ts35UL`g08vR=nMA!iy&uF z<$_caX2n2Z->1w}LD^g*EIGS!+pL(8@RB;pA(`d&TPkLAH6tdEE9T-#<)*_XXJ${1 zsG#?UIfKS6Q9+em?Q(!Yy<9=lIKt&;9_)#aXw}?h)tbBRYjvD;IfA-L^%{FP6CNL@ zg6?;8Hq{Mwbj?XltA!lL@*Gr-kjEII;;X!CCvh5WD?x#-y`C3$B{f}EvuO1a&Ck-0 z8KV-;e9sw_H&O*PYwIl6yXX;dKLgX(?3~`}eB+P0M#7to4Gak|Ag}mi2B$QYbQ+4o zp!}@TeKaDy`LRmnFzIL|J0!`o0X|7VqsdZ%!+BCqXUU$zM65>W&gSuMt%4fWbGq;R zp$f{k)M<5<70>LNQ=6A4t9M*I*$$P7wE-Nb5P zHfJIB@(9oZEyrr_f{9pVM+KR1F#G7TtB-1MuRWx`GvN*HisUsFOxT}R5hI*I%cd&# zy>Oghjw_ktb94(6*4(YE>?jw5}&}uAa$O>1t9U80>UU=8pxreY< z6kkuVr!H=|vOHrSedFq*;!Bj($pudL6Y1-u%U2do2Y*Zl7NR#wE$%nCs;$FHTI`8x zv004`<7m}lWnI%@D+l_wu7NHe2Z}dee`has;*K>M-OIKZXIEAYTzj5@qf}66inC;) za3sFuu|_QHxjgDl0IjeQv>E#(#AHEcG?=wSrK#&0_5k6?B|Migy*M z8$0nk?1fqI<7})MUQS;%*rf9IQ@U@@?pcW=0*7io2F*i{(O2Pds$B7toC*D~3zr&4 z!xyc>IYx%M#)y23DY13c(c#1D&h#C+sPrEeaHcQM2Km}GE)J)t*xv6L39AP>)0gIW z{{fk@;O~Ygmqi;oU4DfkN-2}j7#xsKnaf01v;VgQ3R;Sx zO<$|If2)E02!jf;j)z<=DPLwwAr?XG`-#X&g5;^(Ot=Me0;9_!Z{>tlB{iKqnj2fg zTNqfQMo))Or=wS#y;o5Flb&D++Il#%n}g{hCFtv#(Dl4j&=d3t%Q+T9 zO$Jfz21d|y*XUhUQW?vq&omBUUygO{q5VrJtNlfsR=qj*cVT?8>Nl4wtNLr5R`PI9 zDc&eF3Ekg9NhL@_HkjbDXt`97-19-Jt8gn;8Bu$>-_IRT2G?o9f>dSI3j<2(hsA>p z^1zkjZ_AX`?A1>9v4|D-1zcn75lbK7Dt$W^b)YN9UQ1QNiwm8;`YusH1zkZ0$Eu*m zu94V#lnSbs;;hi&!m8BaYHA;;?sQobCyP3Yw*jqy2ANgEz>b;*3akC#XRckdjWcG8 zt2RE9RCecFBeUl;6?DcmV^*`_o5Ceqx%5nB72nI*qW>6zLq^UkvaNx zb0)0Gk@zjTnpWd^4zeoQ85GP`{=n7Bjks$nIL>KxmcAmqok4@y@TFW0-O}H&l!XtFfyOxU5QZaLKc-X=UC-p(`#9(L$8b9$awiE!7wr4`gfgUyMz(>*Y@+yscai6{H1>K^K4oAD*Pp0-Vi9u zV8o>FUkgUGEbTZ}Fg<0#-uur>Fyrqc_;u|oKgpJ*qnrQI^uIB$iuZMkU&Z+A*ptgL z8T*vi^?yMXZLtn**Q$fXI@mkId?};1ccu)ERQSX}-8d?FzxTTg=U3*VWgLF*tAK`C zyvNzz&TMZtI>Ti96n(14G)xrNErr&hhF7cUuQ=i)&m|wI{iiE=F^|-*j|$F0@!V&oy;p^G4oAlcL;WAwB!7e`%6@RGnx2%1iMr7RQY6{ z8H;I`&R34aod2`yy{FBAedzMOQ=I_>zGWHfA zUFrg5#(8P@m-Ldq_#MS~nU}_5`eXk|{{9$&%@-;^GCyT;Wc}CqOS%N4i(RDB$-I@t z(dA#KD-WBFNY`hvN+GZySSLX5n&1OetlwMBm4$ML=URs*FP%MFLp>mpP1+>zSaEVqY@%2#D;|R z3+WvZ?-!joAa;mfSWH}mUsQBh|HSYJ6=d;CjE{2$MOCR;{m&usL*o5{5{AS^#9RDY zMTN$N#0~Ll8W%DkA}J=WU%X#CBRk(nGsLem${7_C9q%VaFmrMiYQUeI|7pVyIa&M$ zg~a(q#`F)D+V-pIH#9P~M);tx_(}t+RIc88Xr<7&f&D8b460cXOYdmEjz;aPCk`B1 zB{V78;@2=XwtrMuNPKk21WEod&T*6jEU(V)+Zzi z1vTs%M?{7s_D}E~9F}0l7!4B|747Hi>#RsvxpIDkD*X>Lk}eGE9}?Z$uZC|G-->3& ze{L!Ehb1Uz0LmN{5*HUTBq$^{%D0c4C97DhV@d?Y$43o~2ui?~4B#QLF>$i)fqP0q zW%#J|_=NDN7_4G^Ium0Q4v4@|3Qunsj^P)VK44%x90v7^h>MQsA5^_Y)$m#&kyXP& zLn?+xL`H;FuTrC8%~}!RHEL7}s}@t3F42X#vB4IM{gA(FGq7n>0QPELo z?*3@fxS;+~@d?Z#GAUfv1|%j#43?FksOW?$mCX`HhZ`Ma%pnv3Jro@~1Z5mBASPOE zqj>0dtfK=Gq6S2$wfKa%h>!uHiIL(mMs3MzylXWfG4{`O59!}KCN3(W&j7O+u?cZz zISgYXJjl6p=rT;}b#>oRcgGyOlWR6^7B(&y^Xv z*4zjiJUAp&CMo@4h^o+7#)$MIwdkOI!tzGA^n3wL*k8*YVq*0cw%;|MZ0LR zdidy8FKvtN2-Hi(c@)*H+SKF(k8HZGjnN!C^-QDn%%jCd``(hEXVDH1vz4=At7wXz z!Lfa`M=m|THYU>rSm@dx@o8|7Nt>uU5e2pZthNqy~9sW{ldq9Hj zZAZ-m?FoTWdAl|iZDUk^JxcY{2(%mWK{ja4|Nngc+`5r}${;~i#;(oJk%A`hMH8SN z+Pe64t_;`xi;AIq^|iEQlo|#8!z#^blsCIx){ZjUwN{aA2`{~p_C=y)9;27nVp8;C_5eMzUCW+^VOvW3K*uoGKC>hkg??tg z?J5k4XY{2}KDWMvYfo*@V25v4!^<)RVl5!(V1GuCx6|1MKUtIF6fp^y6)vDsTW2o>h|e+R`hUjyEbNIfbOfkv;^wnE?irx_A>Q5+TdB^ij#|D{y+AkYP!jdLJ#;Tb<+M9$=^n_^X8#(k5%B~i zlEH89s@v^5^`iD4^^6TvHVeE`wBIZN$f}t3T|$bU$9@4e?ex6b4P5X4*&W!G4Gpm0Mi%yU0Vzm@T7Nteer#U0V)=ECSxfc2-|PO<^fvYYJ<}pI zr>$5{-C^^|p?kDI9|q_i*lYRPTk8(Tk@P|KN}co^?YiokmWXO--l@9BbkwOo25U(@ ztG3pfW>1j(tak0k5dj#e7R}dj0fh>%JF+`=qF90UCw50kJKD`+claYF&F)Bb71!aL zA`z*tO(Id+P{%q%rlNCUm!ei3*)cf~w^zf#qKNMArTvi{fFMt8v=)HcWJUYe(TW8i zpqKw1v|cVf^A){V6efCZ?U*AKrunsPNs-vDqb>E6qD|;mJ)jR{SyB!j#XYPh_l&}v%;wIYI zk+;$GXf#Y|yEe|c4h6$;t@m{~hF!oMYAJhgRJDM4oK$_Oc06Ro&273w<0nx<#+ z+J%Tl+S3vKNRSCL9zz4iAVfbK;V_EtrJWhBnqq^^Y>Gmyb-V*9X42N zEdo*BKMecK+Af#v{+%UZ@>)yUNo8U?$y6#OAL;U*v)3{hW)3U#$Z?U3ED@x zY2HXFi?_B}oh{_ow%BA&cx%VluX}O2G7b}S#YoId-RlU>fdcf5_R+fML~V$4c{c4O zGRFCZRJW>hehqEhAc^tO=6J0$w(ljTV08P#G4@r#s%t$l!;IuVdj@FVph?SWOKmbC zYG_jj`NKR=T-f1V`4}@u`7i>8OFUNQ>#{D5e|kHE!Bn{Uo9`k`{n6wiqFNCg@w)4JE!}ghuHW%!f-)Y{g)G`M)V&EB@3#!= zkRZVrkrwS_(rw)n=LA_CjgU`%ZM&E36Rp~pi96xOUwb@Uwliz*VV0F^rDv|K=jf`J zYiYzi9qx}ZR?^<}PBGd(Ep8`#cxtONM>=vC7UN;iK2m<|L;YCu`O0 zS{a;sYq^b0kES?Bz`3c6x?j6+ zT>1ZduKW#G33R1vr!%EueM5W-TIh5pTScq=iJbj!h{r01IodcIgp;JxIJSzRt+Wx2 z!Z>d0Ss_K7S>qHdD~^TPl(dzWQ4yh7tSj1eKx+Qs(?Q$h!0Y7$*sj8mx%;vmn0P ztRTO}!PRA^PZj$FreQ%PSJl>94!wZXDrG~x@8nZS}ruY>pyuFxO&C)>Z0_dqGvRj?G&o#OTp zQ2eg~yCQrV*c==WHUoz-ycdX1H!5@j1Cf6l5OZra{Skf;6hEJWQm##4 z7vv*1Mx$#g3;+j!?Z75rbFvo0D{;F7i4$m}J+gt#U~dN{{|ET^RodeYDDCkzDD80w zlzeA^jlu5RZVkp@yAij&!N%BrijQ=~{s$0M$#o2r_CEwlemg+PZy_l8$;~H{-xN^t zO9CZ*W3oOdetbajlOGg6SwZphlTG>g9uz;fK=E@N6hHIG_d)R!2lhsI3sA~i8T1Ep zlX4e!18o0dRrOm0_QUo#urHVhN;w9AQjQQ%%F!5<`qTlX992OnM`2LP;Q%FnV_yjV ziqFX;{4OZr-+)c9eF4POt}qqEcT9E1gPpM59V~=x*jTV$p%j=K+o{-}Mqv9o_D{mq zU|leYl>L>A*C(L(ms_WWv0y`RKL$||a62e=8^J!{3UVokyD=)v03*TiU;sD*lzMmO zc5AQ)wkv`7D!syUhl;-nO1qo`rCpAL(k?T=7GOcJ5%_)vdHproq_O1e^@q{|FSx+P#S-1#^GMAO;DPFNZ& zj_@Mf&P$?OEF};Q71n$Ack-jnI9OkSu9|C@EP)L3qA(D!FwQX0m*V5%mbbRgTOQ}Ke!(Z0k?yp z;6@N_m1PwucBx=Xa01v09L4ZHAlfHOFqj(*B-=5(IanCs4Zyd+nqXeA2#8xkvS3{v zdz8hS;e&9H_CS+GlVN0cvH@9>tU#6^y+|ARD-M$4?+5Z6d5k z{EFO5eoD?KXOe@-Sh5q@nygHgCEp_RkS~2z{!hvK|G8^eheqTYA?=E?nJWHmL`^l~3dh$bZ9yyXsBKwo!WLL61*_5nDRw2ug z-lT*4y}T;#&*TI0CV7!ONgg71lbguZJoUow>JLbfFvlm28SvJ9Dpw39E&s`C9r-XpJ*=gH&b zW^xU=h@4GMB1e-+WDFTj_8=RQwMbvGB$<)a$Y*6#c^{MK$>Zc^at*nd{D7QHjvhb%(oC4Vo?@ku@)Z;}_uljI?CH@TFYOHL)nk;BM%vJV+db|PDomC3T? zTVx(G6X`+zh7$ofe*8$@C9jcZ$uG(Mi-LAls8I$a-XTvH+Qb zd{&bEPu?M~l4r=H_v7aTas^+rO6^>4l*MtA96^0JuAv~Bkzz$$%Eupay|JWIggx1jwkz* z;baT4Az7XDC5w>-$X7T~mGb>cJ|u6EY2`a(zQyB#)7Y$Q|S+as|1F98C@*W63^bPqGskKsF+4kbY!wvLKm_^d$el#ekIi z7xFTBmfTKmB;O~eka1*RvMt$+EKe3EJxMEh7Z)p%|26V6Qhq}t;Y-LlP>_&DV zvCr^-v$+hH11oi6G@wxw74etU>yb#YuT3nAl|_JxO_On1tUZzaaOKACmHT zGqD>&fNhL^6epBzuvK$l7FKGB^1G7qpV! zPvj-?6uFgLPtG7Gk}+g&vK85wl-Iq9{~}~AGBf!jF774%J}IwYmhB7VPI5E3l3YxV zCWn#o3OY&OhipYQCTo$E$Xuj6R!`E|NO{zrY+oZUkn+lJ3IB{-Ny;m%C0t&SEtFSQ z3uDPXWKXgaDX%mayGmq9vM?!+36%I3`0!Bp1Nkj^g*->@AUBcA$%W($av~W+_9lCf z9myJ`ADNx>B7et+AyTfNNqP04Y+onOl3$V=$yMY+@_lk5nL;L!@@Nh5+mUQVRwB!g z^7uxvvy<{jN7?>~yg;5H50E>^MdWO97%9&O6F-5ZJWf)!tC1DR?4%d@Lw05NEqRQT z$6!hN)#Org965rNM_h_scT#@$B-`?HKVb#31eu@APG%rqWmEPy$*;+MCI%owik$cFkTyW}asb(Z3?S>1HOTyAb}|F`DwFbill+=INq#}D1EpW&5xK(m$al#|vKRR_ zS%)k_<|Tj6sQmm)%J+(0fP7m(A*_s9|CAhI7R-&2cU`CeKmPd*SmJWU=Z_mUrx^T||l0y&&aB>R$~q&%oe@^4EvCjH4uWErwB znVZZ=YUDFdoscaV!n`MzAjhmrAQA2OJ10!n%1`*UGYG9M}5lS}w7 z*yi@M$OZAZ|A%ap$6He^S0z7yB%v ze6KFsFVLyNpU6w(DRL{ho|Ny|#eO0=l1w7|li_3wvLRWLEKJ%+dE|}w{hqu_eodYs z50iVz&Ey(#5jmTb?^nfd3OR_3Cgpopi4P!s$&#deuPX62@~%y7Un38aJITeQe7`E` zlF0Cd_OeH6fF=TJD3)z;e zMphuRlV0SHH&p)j$&bkucI{6+sfQ%qpl5dk`$fD%9bISh+vOD<>S%dT= zbCH?JpU$fE56KneBC;LXoUA~WATyF0`QVK5cauCr9wo<;!^r_;1lfsfP39%N$xGOQ z;*e)QMaG{}+x^J)WDBwuS&1x4<|92xEBWO~(d7gPZFFW=;_&U4^uf?nIf6rR^ z{Q{rF@8B2lbNCVbAZ9Ru@4!F9!IM`08~7Cd5I=^0k43x(kKjRk?g!K#fBbzb{sDd& z&)_Mn;9ufloW?ifO?W+Cg};*%u=b;0;Lq_V_#}P!z5)LTuNJS6dR~dY{H_&0i#_b(19(4PA!;nejFFIeRtt=9VpUV#t^<7Uy9Fv%gXOLd1sPi@!R@U7@WFS>B>b!+*bJ1C#>~< zijU*h@C<$$Ka78k@53zKh402S!^*#pXFTTbHOB78@9Z{TLpXNWWqB+5dEV$lFX}K; zdc1RsSBUy$V|C!Q(l>Bj)burUKBw%G-odu0`L?i$4Xk4gt60D|#xROu3}FEM=tDQU zaPWuJ2iI{8yV${1T){TBu#PpXqVxG@g7i4XFp6RHqYu64K{vW^@G9zq>$rwp?BFV{ zU>jRl#~N1A`TR6NdKAML!T@^FgKl);;14K2_OXXu>|h&P*u(}_v49y&U=+g`!T@^F zgKl)8^SNsOdaL~U9zpHrtLS{b+9JJ3bB_ zE_QGgSFninJbwv4>skU>jT5#0FMT-#e-G&0qqf z=zM+~Al-`|bfXIgJU`vQKK8JS9c*I@o7liA7BGVe)S*bU9$^e&0KMozH@a|L&IjiH z$3FIO748uqb=tGI$~Y+)1ixwE!Y9cx&{0>&|hQ4C`U1L#E$y3vJ$YpDsk;3}?Q8(Y}K2G+5L8BAatV;IFShA@DB^r06$IJk!T;5zE>4z+#z*uyS%a256U z+PZujTUf^$>hHF7xdLV|fl&-&2m|OxA9~S)Zgk<`YU+pUxQ2b~VHZ2NiYwU07B07)CLSe)ORiJ?MOHzWy?+erwpr9(GZG4{GioY+(}{SjQSxv4C-mVHCp{ z!T|cwhi-J?z|HjT5#0FNefcl(W^V8?-Y81m5!T|cwhi-J?U>D^@ z=X3Wi=^boi3!6B69$zIcU^;si!9j3Er5AARUW4-R;~zJcqwhJEZ|7dzO-7B;bgb*y0(3mC^3Mlp z8(Y}K2G+5LRV-i}V;IFS`q778^q?DEsK4Vj+Z+4X!&O|tHny;d4Xk1TGpN7!*6qYG zhEWWoAARUW54zEX1IbkL-M~Kfu!|jB#T9H~1M67B3??v+F$`e<{pdqCx^N(W_mO{& zZQwetVHZ2NiYwU07S^$bRV-iz6Bx%BMlp;b44@x<=tU2@Q84ROll>wejWO*9(#6KK zA7tfs;R(`p|7*JLe@zdQ?n4(2q>*&F4N;d{6E)uswy=f?45JTSxF+Q@*TdVQ(WW5mPp zB=mcdyXD-|c&#=63(U82mT@YG^*%x;{T{UD*CxJ3Haso`)$1LdCPj^h)1S)@8o#PD z8n@y%GDG9xI0kpheU!%f_amM3{S_Lo$aZu(OT4BPlu5tGsPlXB_YIx?fjB{YM$&Y? z{{2IzH%U8atoQ#q{f9J;#(KZ6)01+aaI?fK+ORUM$$3EM>;1G&`h6~q*QoDrrL8p9 zzt`!c-%lQQOa8C3;6fx^%vd{!Ob2BDYW-rg?>!Qa%Q_nmU1`q$E2)>p z>%`yE_L4dV=noOCuk25~9@A-2*O#~>={l8ld0BtJ`sZZ6ZTGwWSeCQLeBahuS^m%U z(J20;)o(}f`yKJC9Qh9%^MCA!wQt+Ef6g)g-yQk!ecjRa?{m!mk?dc4ejk!{vB$SK zmVek0zs(Up=qS(U9rJa34txF)3GA`Q`a8s^ye~NB^L^3L{2Q$M$WgrDh|f9V`>gxu z(ee*owR!uKj`?45tgr9uv6nY2{mmY~LHesbey{W;d%WtH|A2H``}~_6@%J40FH6bo z%X6PHs*k?^#y)?y9B=ILV-nco|8Z>Z>yG##$M*L+_D|YTpFNK9-ROuPag^tl+KMglP3 zpFXAU@Q+-0-lQz4Lkk>DE*#C4jbwg7hY~1dWi$$-D1#vsm+y_(GYwA58`-ldWAvu} zxzvGqBN)^-z%y#VTq+WYOjj0j7hhH0QZFz0zwoM(-App4Z-tka^qYCjRPs_BTYmek z3fjtYX&FvR>8TZ2O#}^XDyw3W8Dka9z3XT&5}61XhS?HEE}cJC&KT)pae0^lMVYkL zX3T;M*DNx~@N{rzE4M6i?n3+*G|i)*uDL)I%o%Mt)+-;DZ+2TIDxq zd1)&-x5)m{?k2s}`Y>L2Po4j98rEW1`pcQug#AOb-2%PXEiUb2J*=3sv9`8*0-N^u z7IzOTIk&i@CUq3#a^|(|mNm;%$}N{&M(kvySTthN?<7c9v+{pQGY%~0)AQwGB{f>) z9m_=-Ma3n@)zonrW+at1N+*|&F6T-v!#;-29p;cbk|-HhQ^wqq4%j1`SS}8e4kvS! z^rn=3W$E`vW$c+vsRt{09ZF`DHk&MzWS|`5o#}FHS&Eysa+K1~=F&+Cij};MjdLt% zjy59yEEnYnucy-dy-UjQBeJb-y~mKDTlB=cKV@Y$JZ)_&x2eT!ej!3m)=5W>Kt`cb zHuT&pRgg1wDgC}mI-g2k_>&ij9Mp5QwwZ2$a}VHP#G*S?30h2ZVC z$Mzc5h(odcx!6K3r#t6Pb3i3yR!-OU)_Sp*S%djpq%?b~B{^s1BeB%B)|RUlxrW&8 z+SQV@oNRml#WsFvhn~Hqbo%)6;$oyKEj&6Wo2d)gV&ooM^Ni+Br?eMtnz!vM8gom9 zlhS&TY9+)@zH3>o*7lW@;jw8xU6vz}>_A%|m1BS&G+sTrSAW(K z)))?IHa9$)@3D^N^C3h2)5HGguoH1?b4xpf%VI71{G=3l@p#$Tcktl8yNrFYIa`ZN zrP9S>L=z4jJ`j}qCTq~I6G6R)DrJpSE?JV{#&nReY<_XsTyof7w|Lr^&dLCBX=y^K z6xZceHk@8bo2iu2#-dzxl=Z9^Feb7K_N`5uBb~|Zr6YaYheGq7(kxWaaekklFr>*N zR;$boYrB)nx=287JE|@%;O9y9M=Ou;CPnte2 zX;M#h)=AHpHP5#b(j|;z)v6&QmzH!Sz-;-XaUyWungR3NDVN!s?kRRKE~m@&|I5rU A%K!iX diff --git a/benchmarks/hope/qsort_kernel_674d9af4cba0defec637089ed771c5f43e2e28f5e25a2c4f0ec12559_0.out b/benchmarks/hope/qsort_kernel_674d9af4cba0defec637089ed771c5f43e2e28f5e25a2c4f0ec12559_0.out deleted file mode 100644 index 168f8ec..0000000 --- a/benchmarks/hope/qsort_kernel_674d9af4cba0defec637089ed771c5f43e2e28f5e25a2c4f0ec12559_0.out +++ /dev/null @@ -1,8 +0,0 @@ -running build_ext -building 'qsort_kernel_674d9af4cba0defec637089ed771c5f43e2e28f5e25a2c4f0ec12559_0' extension -C compiler: /usr/bin/clang -fno-strict-aliasing -Wsign-compare -Wunreachable-code -fno-common -dynamic -DNDEBUG -g -fwrapv -O3 -Wall -arch i386 -arch x86_64 -g - -compile options: '-I/Users/uweschmitt/Projects/hope/venv3/lib/python3.5/site-packages/numpy/core/include -I/Users/uweschmitt/Projects/hope/venv3/include -I/Library/Frameworks/Python.framework/Versions/3.5/include/python3.5m -c' -extra options: '-Wall -Wno-unused-variable -march=native -stdlib=libc++ -std=c++11 -Wno-unreachable-code' -clang: /var/folders/k8/zfp7dvcs1m326gz1brql1tv80000gn/T/hope6uqz3bwn/qsort_kernel_674d9af4cba0defec637089ed771c5f43e2e28f5e25a2c4f0ec12559_0.cpp -/usr/bin/clang++ -bundle -undefined dynamic_lookup -arch i386 -arch x86_64 -g /var/folders/k8/zfp7dvcs1m326gz1brql1tv80000gn/T/hope6uqz3bwn/qsort_kernel_674d9af4cba0defec637089ed771c5f43e2e28f5e25a2c4f0ec12559_0.o -o /var/folders/k8/zfp7dvcs1m326gz1brql1tv80000gn/T/hope6uqz3bwn/qsort_kernel_674d9af4cba0defec637089ed771c5f43e2e28f5e25a2c4f0ec12559_0.cpython-35m-darwin.so diff --git a/benchmarks/julialang.org.html b/benchmarks/julialang.org.html new file mode 100644 index 0000000..19ee7e5 --- /dev/null +++ b/benchmarks/julialang.org.html @@ -0,0 +1,12617 @@ + + + +julialang.org + + + + + + + + + + + + + + + + + + + + +

+
+ +
+
+
In [1]:
+
+
+
import hope
+hope.config.optimize = True
+hope.config.verbose = True
+hope.config.keeptemp = True
+hope.config.prefix = "hope"
+import numba
+import numpy as np
+from util import perf_comp_data
+from native_util import load
+%load_ext Cython
+%load_ext version_information
+%version_information numpy, Cython, numba, hope
+
+ +
+
+
+ +
+
+ + +
+ +
Out[1]:
+ + + +
+
SoftwareVersion
Python3.5.1 64bit [GCC 4.2.1 (Apple Inc. build 5666) (dot 3)]
IPython6.1.0
OSDarwin 15.6.0 x86_64 i386 64bit
numpy1.13.1
Cython0.26
numba0.34.0
hope0.6.1
Mon Sep 04 16:51:44 2017 CEST
+
+ +
+ +
+
+ +
+
+
+
+
In [2]:
+
+
+
def fib(n):
+    if n<2:
+        return n
+    return fib(n-1)+fib(n-2)
+hope_fib = hope.jit(fib)
+numba_fib = numba.jit(fib, nopython=False)
+
+native_fib_mod = load("fib")
+native_fib = native_fib_mod.run 
+
+n=20
+assert fib(20) == 6765
+assert hope_fib(20) == 6765
+assert numba_fib(20) == 6765
+assert native_fib(20) == 6765
+
+ +
+
+
+ +
+
+ + +
+ +
+ + +
+
running build_ext
+building 'fib' extension
+C compiler: /usr/bin/clang -fno-strict-aliasing -Wsign-compare -Wunreachable-code -fno-common -dynamic -DNDEBUG -g -fwrapv -O3 -Wall -Wstrict-prototypes -arch i386 -arch x86_64 -g
+
+compile options: '-I/Users/uweschmitt/Projects/hope/venv3/lib/python3.5/site-packages/numpy/core/include -I/Users/uweschmitt/Projects/hope/venv3/include -I/Library/Frameworks/Python.framework/Versions/3.5/include/python3.5m -c'
+extra options: '-Wall -Wno-unused-variable -march=native -stdlib=libc++ -std=c++11 -Wno-unreachable-code'
+clang: ././src/fib.cpp
+/usr/bin/clang++ -bundle -undefined dynamic_lookup -arch i386 -arch x86_64 -g ./src/fib.o -o ./fib.cpython-35m-darwin.so
+
+fib(int64 n)
+	if (n.J < 2.J) {
+		return n.J
+	}
+	return (fib((n.J - 1.J)) + fib((n.J - 2.J)))
+
+Compiling following functions:
+fib(int64 n)
+running build_ext
+building 'fib_181039868ed022600dc3388e38604df736dff187257d5d6ae4c4d3ea_0' extension
+C compiler: /usr/bin/clang -fno-strict-aliasing -Wsign-compare -Wunreachable-code -fno-common -dynamic -DNDEBUG -g -fwrapv -O3 -Wall -arch i386 -arch x86_64 -g
+
+compile options: '-I/Users/uweschmitt/Projects/hope/venv3/lib/python3.5/site-packages/numpy/core/include -I/Users/uweschmitt/Projects/hope/venv3/include -I/Library/Frameworks/Python.framework/Versions/3.5/include/python3.5m -c'
+extra options: '-Wall -Wno-unused-variable -march=native -stdlib=libc++ -std=c++11 -Wno-unreachable-code'
+clang: /var/folders/k8/zfp7dvcs1m326gz1brql1tv80000gn/T/hopeimwu5wdk/fib_181039868ed022600dc3388e38604df736dff187257d5d6ae4c4d3ea_0.cpp
+/usr/bin/clang++ -bundle -undefined dynamic_lookup -arch i386 -arch x86_64 -g /var/folders/k8/zfp7dvcs1m326gz1brql1tv80000gn/T/hopeimwu5wdk/fib_181039868ed022600dc3388e38604df736dff187257d5d6ae4c4d3ea_0.o -o /var/folders/k8/zfp7dvcs1m326gz1brql1tv80000gn/T/hopeimwu5wdk/fib_181039868ed022600dc3388e38604df736dff187257d5d6ae4c4d3ea_0.cpython-35m-darwin.so
+
+
+
+
+ +
+
+ +
+
+
+
In [3]:
+
+
+
%%cython
+
+cimport cython
+
+@cython.boundscheck(False)
+@cython.wraparound(False)
+cpdef int cython_fib(int n):
+    if n<2:
+        return n
+    return cython_fib(n-1)+cython_fib(n-2)
+
+assert cython_fib(20) == 6765
+
+ +
+
+
+ +
+
+
+
In [4]:
+
+
+
%timeit fib(20)
+%timeit hope_fib(20)
+%timeit numba_fib(20)
+%timeit cython_fib(20)
+%timeit native_fib(20)
+
+ +
+
+
+ +
+
+ + +
+ +
+ + +
+
3.4 ms ± 269 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)
+44.4 µs ± 2.57 µs per loop (mean ± std. dev. of 7 runs, 10000 loops each)
+3.57 ms ± 123 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)
+46.2 µs ± 1.17 µs per loop (mean ± std. dev. of 7 runs, 10000 loops each)
+44.5 µs ± 1.15 µs per loop (mean ± std. dev. of 7 runs, 10000 loops each)
+
+
+
+ +
+
+ +
+
+
+
In [5]:
+
+
+
perf_comp_data(["fib", "hope_fib", "numba_fib", "cython_fib", "native_fib"],
+               5*["n"])
+
+ +
+
+
+ +
+
+ + +
+ +
+ + +
+
function: hope_fib            , av. time sec:   0.00003974, min. time sec:   0.00003969, relative:       1.0
+function: native_fib          , av. time sec:   0.00004625, min. time sec:   0.00004621, relative:       1.2
+function: cython_fib          , av. time sec:   0.00004649, min. time sec:   0.00004646, relative:       1.2
+function: numba_fib           , av. time sec:   0.00333900, min. time sec:   0.00315241, relative:      84.0
+function: fib                 , av. time sec:   0.00345827, min. time sec:   0.00327053, relative:      87.0
+
+
+
+ +
+
+ +
+
+
+
+
+

quicksort

+
+
+
+
+
+
In [6]:
+
+
+
def qsort_kernel(a, lo, hi):
+    i = lo
+    j = hi
+    if False: return a
+    while i < hi:
+        pivot = a[(lo+hi) // 2]
+        while i <= j:
+            while a[i] < pivot:
+                i += 1
+            while a[j] > pivot:
+                j -= 1
+            if i <= j:
+                tmp = a[i]
+                a[i] = a[j]
+                a[j] = tmp
+                i += 1
+                j -= 1
+        if lo < j:
+            qsort_kernel(a, lo, j)
+        lo = i
+        j = hi
+    return a
+
+hope_qsort_kernel = hope.jit(qsort_kernel)
+numba_qsort_kernel = numba.jit(qsort_kernel)
+
+native_qsort_kernel_mod = load("qsort_kernel")
+native_qsort_kernel = native_qsort_kernel_mod.run
+
+ +
+
+
+ +
+
+ + +
+ +
+ + +
+
running build_ext
+building 'qsort_kernel' extension
+C compiler: /usr/bin/clang -fno-strict-aliasing -Wsign-compare -Wunreachable-code -fno-common -dynamic -DNDEBUG -g -fwrapv -O3 -Wall -arch i386 -arch x86_64 -g
+
+compile options: '-I/Users/uweschmitt/Projects/hope/venv3/lib/python3.5/site-packages/numpy/core/include -I/Users/uweschmitt/Projects/hope/venv3/include -I/Library/Frameworks/Python.framework/Versions/3.5/include/python3.5m -c'
+extra options: '-Wall -Wno-unused-variable -march=native -stdlib=libc++ -std=c++11 -Wno-unreachable-code'
+clang: ././src/qsort_kernel.cpp
+/usr/bin/clang++ -bundle -undefined dynamic_lookup -arch i386 -arch x86_64 -g ./src/qsort_kernel.o -o ./qsort_kernel.cpython-35m-darwin.so
+
+
+
+
+ +
+
+ +
+
+
+
In [7]:
+
+
+
def numpy_qsort_kernel(a, lo, hi):
+    np.sort(a)
+
+ +
+
+
+ +
+
+
+
In [8]:
+
+
+
%%cython
+
+cimport cython
+import numpy as np
+cimport numpy as np
+
+@cython.boundscheck(False)
+@cython.wraparound(False)
+cdef _cython_qsort_kernel(np.double_t * a, int lo, int hi):
+    cdef int i = lo
+    cdef int j = hi
+    cdef double pivot = 0
+    cdef double tmp = 0.0
+    if False: return a
+    while i < hi:
+        pivot = a[(lo+hi) // 2]
+        while i <= j:
+            while a[i] < pivot:
+                i += 1
+            while a[j] > pivot:
+                j -= 1
+            if i <= j:
+                tmp = a[i]
+                a[i] = a[j]
+                a[j] = tmp
+                i += 1
+                j -= 1
+        if lo < j:
+            _cython_qsort_kernel(a, lo, j)
+        lo = i
+        j = hi
+
+def cython_qsort_kernel(np.ndarray[np.double_t, ndim=1] a, int lo, int hi):
+    _cython_qsort_kernel(<np.double_t*> a.data, lo, hi)
+    return a
+
+ +
+
+
+ +
+
+
+
In [9]:
+
+
+
lst = np.random.random(5000)
+
+ +
+
+
+ +
+
+
+
In [10]:
+
+
+
psorted = qsort_kernel(lst.copy(), 0, len(lst)-1)
+hsorted = hope_qsort_kernel(lst.copy(), 0, len(lst)-1)
+#nsorted = numba_qsort_kernel(lst.copy(), 0, len(lst)-1)
+csorted = cython_qsort_kernel(lst.copy(), 0, len(lst)-1)
+nasorted = native_qsort_kernel(lst.copy(), 0, len(lst)-1)
+
+ +
+
+
+ +
+
+ + +
+ +
+ + +
+
qsort_kernel(float64^1 a, int64 lo, int64 hi)
+	new i.J
+	i.J = lo.J
+	new j.J
+	j.J = hi.J
+	if False.P {
+		return a.d[:a@0]
+	}
+	while (i.J < hi.J) {
+		new pivot.d
+		pivot.d = a.d[((lo.J + hi.J) // 2.J)]
+		while (i.J <= j.J) {
+			while (a.d[i.J] < pivot.d) {
+				i.J += 1.J
+			}
+			while (a.d[j.J] > pivot.d) {
+				j.J -= 1.J
+			}
+			if (i.J <= j.J) {
+				new tmp.d
+				tmp.d = a.d[i.J]
+				a.d[i.J] = a.d[j.J]
+				a.d[j.J] = tmp.d
+				i.J += 1.J
+				j.J -= 1.J
+			}
+		}
+		if (lo.J < j.J) {
+			qsort_kernel(a.d[:a@0], lo.J, j.J)
+		}
+		lo.J = i.J
+		j.J = hi.J
+	}
+	return a.d[:a@0]
+
+Compiling following functions:
+qsort_kernel(float64^1 a, int64 lo, int64 hi)
+running build_ext
+building 'qsort_kernel_674d9af4cba0defec637089ed771c5f43e2e28f5e25a2c4f0ec12559_0' extension
+C compiler: /usr/bin/clang -fno-strict-aliasing -Wsign-compare -Wunreachable-code -fno-common -dynamic -DNDEBUG -g -fwrapv -O3 -Wall -arch i386 -arch x86_64 -g
+
+compile options: '-I/Users/uweschmitt/Projects/hope/venv3/lib/python3.5/site-packages/numpy/core/include -I/Users/uweschmitt/Projects/hope/venv3/include -I/Library/Frameworks/Python.framework/Versions/3.5/include/python3.5m -c'
+extra options: '-Wall -Wno-unused-variable -march=native -stdlib=libc++ -std=c++11 -Wno-unreachable-code'
+clang: /var/folders/k8/zfp7dvcs1m326gz1brql1tv80000gn/T/hope9omwep_j/qsort_kernel_674d9af4cba0defec637089ed771c5f43e2e28f5e25a2c4f0ec12559_0.cpp
+/usr/bin/clang++ -bundle -undefined dynamic_lookup -arch i386 -arch x86_64 -g /var/folders/k8/zfp7dvcs1m326gz1brql1tv80000gn/T/hope9omwep_j/qsort_kernel_674d9af4cba0defec637089ed771c5f43e2e28f5e25a2c4f0ec12559_0.o -o /var/folders/k8/zfp7dvcs1m326gz1brql1tv80000gn/T/hope9omwep_j/qsort_kernel_674d9af4cba0defec637089ed771c5f43e2e28f5e25a2c4f0ec12559_0.cpython-35m-darwin.so
+
+
+
+
+ +
+
+ +
+
+
+
In [11]:
+
+
+
assert np.all(psorted[:-1] <= psorted[1:])
+#assert np.all(hope_qsort_kernel[:-1] <= hope_qsort_kernel[1:])
+#assert np.all(numba_qsort_kernel[:-1] <= numba_qsort_kernel[1:])
+#assert np.all(cython_qsort_kernel[:-1] <= cython_qsort_kernel[1:])
+
+%timeit qsort_kernel(lst.copy(), 0, len(lst)-1)
+%timeit hope_qsort_kernel(lst.copy(), 0, len(lst)-1)
+#%timeit numba_qsort_kernel(lst.copy(), 0, len(lst)-1)
+%timeit cython_qsort_kernel(lst.copy(), 0, len(lst)-1)
+%timeit native_qsort_kernel(lst.copy(), 0, len(lst)-1)
+%timeit np.sort(lst.copy())
+
+ +
+
+
+ +
+
+ + +
+ +
+ + +
+
26.6 ms ± 380 µs per loop (mean ± std. dev. of 7 runs, 10 loops each)
+379 µs ± 7.76 µs per loop (mean ± std. dev. of 7 runs, 1000 loops each)
+342 µs ± 4.95 µs per loop (mean ± std. dev. of 7 runs, 1000 loops each)
+325 µs ± 7.95 µs per loop (mean ± std. dev. of 7 runs, 1000 loops each)
+272 µs ± 4.69 µs per loop (mean ± std. dev. of 7 runs, 1000 loops each)
+
+
+
+ +
+
+ +
+
+
+
In [12]:
+
+
+
a = lst.copy()
+
+lo = 0
+hi = len(lst)-1
+
+perf_comp_data(["hope_qsort_kernel", 
+                "qsort_kernel", 
+                #"numpy_qsort_kernel", 
+                "cython_qsort_kernel", 
+                "native_qsort_kernel"],
+               5*["a, lo, hi"], rep=100, extra_setup="from __main__ import lst;a = lst.copy()")
+
+ +
+
+
+ +
+
+ + +
+ +
+ + +
+
function: native_qsort_kernel , av. time sec:   0.00030064, min. time sec:   0.00029132, relative:       1.0
+function: cython_qsort_kernel , av. time sec:   0.00031280, min. time sec:   0.00031010, relative:       1.0
+function: hope_qsort_kernel   , av. time sec:   0.00034323, min. time sec:   0.00033363, relative:       1.1
+function: qsort_kernel        , av. time sec:   0.02440728, min. time sec:   0.02243393, relative:      81.2
+
+
+
+ +
+
+ +
+
+
+
+
+

pi sum

+
+
+
+
+
+
In [13]:
+
+
+
def pisum():
+    for j in range(1, 501):
+        sum = 0.0
+        f = 0.0
+        for k in range(1, 10001):
+            sum += 1.0/(k*k)
+    return sum
+
+def pisum_opt():
+    for j in range(1, 501):
+        sum = 0.0
+        f = 0.0
+        for k in range(1, 10001):
+            f += 1.
+            sum += 1.0/(f*f)
+    return sum
+
+hope_pisum = hope.jit(pisum)
+hope_pisum_opt = hope.jit(pisum_opt)
+
+numba_pisum = numba.jit(pisum, nopython=True)
+numba_pisum_opt = numba.jit(pisum_opt, nopython=True)
+
+native_pisum_mod = load("pisum")
+native_pisum = native_pisum_mod.run
+
+native_pisum_opt_mod = load("pisum_opt")
+native_pisum_opt = native_pisum_opt_mod.run
+
+
+assert abs(pisum()-1.644834071848065) < 1e-6
+assert abs(hope_pisum()-1.644834071848065) < 1e-6
+assert abs(hope_pisum_opt()-1.644834071848065) < 1e-6
+assert abs(numba_pisum()-1.644834071848065) < 1e-6
+assert abs(native_pisum()-1.644834071848065) < 1e-6
+assert abs(native_pisum_opt()-1.644834071848065) < 1e-6
+
+ +
+
+
+ +
+
+ + +
+ +
+ + +
+
running build_ext
+building 'pisum' extension
+C compiler: /usr/bin/clang -fno-strict-aliasing -Wsign-compare -Wunreachable-code -fno-common -dynamic -DNDEBUG -g -fwrapv -O3 -Wall -arch i386 -arch x86_64 -g
+
+compile options: '-I/Users/uweschmitt/Projects/hope/venv3/lib/python3.5/site-packages/numpy/core/include -I/Users/uweschmitt/Projects/hope/venv3/include -I/Library/Frameworks/Python.framework/Versions/3.5/include/python3.5m -c'
+extra options: '-Wall -Wno-unused-variable -march=native -stdlib=libc++ -std=c++11 -Wno-unreachable-code'
+clang: ././src/pisum.cpp
+/usr/bin/clang++ -bundle -undefined dynamic_lookup -arch i386 -arch x86_64 -g ./src/pisum.o -o ./pisum.cpython-35m-darwin.so
+
+running build_ext
+building 'pisum_opt' extension
+C compiler: /usr/bin/clang -fno-strict-aliasing -Wsign-compare -Wunreachable-code -fno-common -dynamic -DNDEBUG -g -fwrapv -O3 -Wall -arch i386 -arch x86_64 -g
+
+compile options: '-I/Users/uweschmitt/Projects/hope/venv3/lib/python3.5/site-packages/numpy/core/include -I/Users/uweschmitt/Projects/hope/venv3/include -I/Library/Frameworks/Python.framework/Versions/3.5/include/python3.5m -c'
+extra options: '-Wall -Wno-unused-variable -march=native -stdlib=libc++ -std=c++11 -Wno-unreachable-code'
+clang: ././src/pisum_opt.cpp
+/usr/bin/clang++ -bundle -undefined dynamic_lookup -arch i386 -arch x86_64 -g ./src/pisum_opt.o -o ./pisum_opt.cpython-35m-darwin.so
+
+pisum()
+	new sum.D
+	for j.l in (1.J:501.J) {
+		sum.D = 0.0.D
+		f.D = 0.0.D
+		for k.l in (1.J:10001.J) {
+			sum.D += (1.0.D / (k.l * k.l))
+		}
+	}
+	return sum.D
+
+Compiling following functions:
+pisum()
+running build_ext
+building 'pisum_49f15c96f2c4d5ce51371d6a5362b67ac5531087a5dd1885eaba960b_0' extension
+C compiler: /usr/bin/clang -fno-strict-aliasing -Wsign-compare -Wunreachable-code -fno-common -dynamic -DNDEBUG -g -fwrapv -O3 -Wall -arch i386 -arch x86_64 -g
+
+compile options: '-I/Users/uweschmitt/Projects/hope/venv3/lib/python3.5/site-packages/numpy/core/include -I/Users/uweschmitt/Projects/hope/venv3/include -I/Library/Frameworks/Python.framework/Versions/3.5/include/python3.5m -c'
+extra options: '-Wall -Wno-unused-variable -march=native -stdlib=libc++ -std=c++11 -Wno-unreachable-code'
+clang: /var/folders/k8/zfp7dvcs1m326gz1brql1tv80000gn/T/hope7mwr_v7o/pisum_49f15c96f2c4d5ce51371d6a5362b67ac5531087a5dd1885eaba960b_0.cpp
+/usr/bin/clang++ -bundle -undefined dynamic_lookup -arch i386 -arch x86_64 -g /var/folders/k8/zfp7dvcs1m326gz1brql1tv80000gn/T/hope7mwr_v7o/pisum_49f15c96f2c4d5ce51371d6a5362b67ac5531087a5dd1885eaba960b_0.o -o /var/folders/k8/zfp7dvcs1m326gz1brql1tv80000gn/T/hope7mwr_v7o/pisum_49f15c96f2c4d5ce51371d6a5362b67ac5531087a5dd1885eaba960b_0.cpython-35m-darwin.so
+
+pisum_opt()
+	new sum.D
+	for j.l in (1.J:501.J) {
+		sum.D = 0.0.D
+		new f.D
+		f.D = 0.0.D
+		for k.l in (1.J:10001.J) {
+			f.D += 1.0.D
+			sum.D += (1.0.D / (f.D * f.D))
+		}
+	}
+	return sum.D
+
+Compiling following functions:
+pisum_opt()
+running build_ext
+building 'pisum_opt_d21ddf605a07e3a4d8a460735cef49c69a69c967957f695741c0feae_0' extension
+C compiler: /usr/bin/clang -fno-strict-aliasing -Wsign-compare -Wunreachable-code -fno-common -dynamic -DNDEBUG -g -fwrapv -O3 -Wall -arch i386 -arch x86_64 -g
+
+compile options: '-I/Users/uweschmitt/Projects/hope/venv3/lib/python3.5/site-packages/numpy/core/include -I/Users/uweschmitt/Projects/hope/venv3/include -I/Library/Frameworks/Python.framework/Versions/3.5/include/python3.5m -c'
+extra options: '-Wall -Wno-unused-variable -march=native -stdlib=libc++ -std=c++11 -Wno-unreachable-code'
+clang: /var/folders/k8/zfp7dvcs1m326gz1brql1tv80000gn/T/hopetzu9xf6n/pisum_opt_d21ddf605a07e3a4d8a460735cef49c69a69c967957f695741c0feae_0.cpp
+/usr/bin/clang++ -bundle -undefined dynamic_lookup -arch i386 -arch x86_64 -g /var/folders/k8/zfp7dvcs1m326gz1brql1tv80000gn/T/hopetzu9xf6n/pisum_opt_d21ddf605a07e3a4d8a460735cef49c69a69c967957f695741c0feae_0.o -o /var/folders/k8/zfp7dvcs1m326gz1brql1tv80000gn/T/hopetzu9xf6n/pisum_opt_d21ddf605a07e3a4d8a460735cef49c69a69c967957f695741c0feae_0.cpython-35m-darwin.so
+
+
+
+
+ +
+
+ +
+
+
+
In [14]:
+
+
+
%%cython
+
+cimport cython
+
+@cython.boundscheck(False)
+@cython.wraparound(False)
+@cython.locals(f=float)
+@cython.cdivision(True)
+def cython_pisum():
+    cdef double sum = 0.0
+    cdef int j, k
+    for j in range(1, 501):
+        sum = 0.0
+        for k in range(1, 10001):
+            sum += 1.0/(k*k)
+    return sum
+
+@cython.boundscheck(False)
+@cython.wraparound(False)
+@cython.locals(f=float)
+@cython.cdivision(True)
+def cython_pisum_opt():
+    cdef double sum = 0.0
+    cdef int j, k
+    for j in range(1, 501):
+        sum = 0.0
+        f = 0.0
+        for k in range(1, 10001):
+            f += 1.
+            sum += 1.0/(f*f)
+    return sum
+
+
+assert abs(cython_pisum()-1.644834071848065) < 1e-6
+assert abs(cython_pisum_opt()-1.644834071848065) < 1e-6
+
+ +
+
+
+ +
+
+
+
In [15]:
+
+
+
%timeit pisum()
+%timeit pisum_opt()
+%timeit hope_pisum()
+%timeit hope_pisum_opt()
+%timeit numba_pisum()
+%timeit numba_pisum_opt()
+%timeit cython_pisum()
+%timeit cython_pisum_opt()
+%timeit native_pisum()
+%timeit native_pisum_opt()
+
+ +
+
+
+ +
+
+ + +
+ +
+ + +
+
747 ms ± 12.2 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)
+729 ms ± 25.6 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)
+43.3 ms ± 2.83 ms per loop (mean ± std. dev. of 7 runs, 10 loops each)
+21.1 ms ± 359 µs per loop (mean ± std. dev. of 7 runs, 10 loops each)
+21.8 ms ± 812 µs per loop (mean ± std. dev. of 7 runs, 10 loops each)
+23 ms ± 947 µs per loop (mean ± std. dev. of 7 runs, 10 loops each)
+22.4 ms ± 148 µs per loop (mean ± std. dev. of 7 runs, 10 loops each)
+22.3 ms ± 562 µs per loop (mean ± std. dev. of 7 runs, 10 loops each)
+39.8 ms ± 476 µs per loop (mean ± std. dev. of 7 runs, 10 loops each)
+21.6 ms ± 503 µs per loop (mean ± std. dev. of 7 runs, 10 loops each)
+
+
+
+ +
+
+ +
+
+
+
In [16]:
+
+
+
perf_comp_data(["pisum", "pisum_opt", 
+                "hope_pisum", "hope_pisum_opt", 
+                "numba_pisum", "numba_pisum_opt", 
+                "cython_pisum", 
+                "cython_pisum_opt",
+                "native_pisum", "native_pisum_opt",], 
+                None, rep=100)
+
+ +
+
+
+ +
+
+ + +
+ +
+ + +
+
function: cython_pisum        , av. time sec:   0.02062879, min. time sec:   0.02034618, relative:       1.0
+function: native_pisum_opt    , av. time sec:   0.02066824, min. time sec:   0.02035908, relative:       1.0
+function: numba_pisum_opt     , av. time sec:   0.02070580, min. time sec:   0.02033324, relative:       1.0
+function: numba_pisum         , av. time sec:   0.02073894, min. time sec:   0.02045512, relative:       1.0
+function: hope_pisum_opt      , av. time sec:   0.02078112, min. time sec:   0.02039398, relative:       1.0
+function: cython_pisum_opt    , av. time sec:   0.02091439, min. time sec:   0.02033318, relative:       1.0
+function: native_pisum        , av. time sec:   0.03938080, min. time sec:   0.03867311, relative:       1.9
+function: hope_pisum          , av. time sec:   0.03939245, min. time sec:   0.03843478, relative:       1.9
+function: pisum_opt           , av. time sec:   0.63299016, min. time sec:   0.60915709, relative:      30.7
+function: pisum               , av. time sec:   0.73068575, min. time sec:   0.69684398, relative:      35.4
+
+
+
+ +
+
+ +
+
+
+ + + + + + diff --git a/benchmarks/native_cpp_gen.html b/benchmarks/native_cpp_gen.html new file mode 100644 index 0000000..06a4d75 --- /dev/null +++ b/benchmarks/native_cpp_gen.html @@ -0,0 +1,14392 @@ + + + +native_cpp_gen + + + + + + + + + + + + + + + + + + + + +
+
+ +
+
+
+
+

IPython magic extension version_information

+
+
+
+
+
+
+
+

Use the '%version_information' IPython magic extension in a notebook to display information about which versions of dependency package that was used to run the notebook. +Installation.

+

Run

+ +
pip install git+https://github.com/jrjohansson/version_information
+
+
+
+

to install this extension

+ +
+
+
+
+
+
+
+

Installation

+
+
+
+
+
+
In [1]:
+
+
+
%load_ext version_information
+
+ +
+
+
+ +
+
+
+
In [2]:
+
+
+
%version_information
+
+ +
+
+
+ +
+
+ + +
+ +
Out[2]:
+ + + +
+
SoftwareVersion
Python3.5.1 64bit [GCC 4.2.1 (Apple Inc. build 5666) (dot 3)]
IPython6.1.0
OSDarwin 15.6.0 x86_64 i386 64bit
Mon Sep 04 17:18:43 2017 CEST
+
+ +
+ +
+
+ +
+
+
+
+
+

Native CPP codes

+
+
+
+
+
+
+
+

Fibonacci CPP

+
+
+
+
+
+
In [3]:
+
+
+
!mkdir -p src
+
+ +
+
+
+ +
+
+
+
In [4]:
+
+
+
%%file src/fib.cpp
+#define PY_ARRAY_UNIQUE_SYMBOL fkt_ARRAY_API
+#define NPY_NO_DEPRECATED_API NPY_1_7_API_VERSION
+#include <Python.h>
+#include <numpy/arrayobject.h>
+#include <numpy/arrayscalars.h>
+#include <cmath>
+#include <tuple>
+#include <numeric>
+#include <cstdint>
+#include <cstdlib>
+#include <exception>
+#include <functional>
+#include <type_traits>
+
+
+struct PyObj {
+    typedef PyObject * ptr_t;
+    typedef PyArrayObject * arrptr_t;
+    PyObj(): dec(false), ptr(NULL) {}
+    PyObj(ptr_t p): dec(false), ptr(p) {}
+    ~PyObj() { if(dec) Py_DECREF(ptr); }
+    PyObj & operator=(ptr_t p) { if(dec) Py_DECREF(ptr); ptr = p; dec = false; return *this; }
+    PyObj & incref(ptr_t p) { if(dec) Py_DECREF(ptr); ptr = p; dec = (p != NULL); return *this; }
+    operator bool() const { return ptr; }
+    operator ptr_t() const { return ptr; }
+    operator arrptr_t() const { return (arrptr_t)ptr; }
+    bool dec;
+    ptr_t ptr;
+};
+
+inline npy_int64 fib_J(
+      npy_int64 cn
+);
+inline npy_int64 fib_J(
+      npy_int64 cn
+) {
+    if (cn < 2) {
+        return cn;
+    }
+    return fib_J(cn - 1) + fib_J(cn - 2);    
+}
+
+#include <string>
+#include <sstream>
+#include <iostream>
+#include <cxxabi.h>
+#include <execinfo.h>
+#include <signal.h>
+
+void sighandler(int sig);
+
+void sighandler(int sig) {
+    std::ostringstream buffer;
+    buffer << "Abort by " << (sig == SIGSEGV ? "segfault" : "bus error") << std::endl;
+    void * stack[64];
+    std::size_t depth = backtrace(stack, 64);
+    if (!depth)
+        buffer << "  <empty stacktrace, possibly corrupt>" << std::endl;
+    else {
+        char ** symbols = backtrace_symbols(stack, depth);
+        for (std::size_t i = 1; i < depth; ++i) {
+            std::string symbol = symbols[i];
+                if (symbol.find_first_of(' ', 59) != std::string::npos) {
+                    std::string name = symbol.substr(59, symbol.find_first_of(' ', 59) - 59);
+                    int status;
+                    char * demangled = abi::__cxa_demangle(name.c_str(), NULL, NULL, &status);
+                    if (!status) {
+                        buffer << "    "
+                            << symbol.substr(0, 59)
+                            << demangled
+                            << symbol.substr(59 + name.size())
+                            << std::endl;
+                        free(demangled);
+                    } else
+                        buffer << "    " << symbol << std::endl;
+                } else
+                    buffer << "    " << symbol << std::endl;
+            }
+            free(symbols);
+        }
+        std::cerr << buffer.str();
+        std::exit(EXIT_FAILURE);
+    }
+
+
+extern "C" {
+
+    PyObject * create_signature;
+
+    struct sigaction slot;
+
+    PyObject * set_create_signature(PyObject * self, PyObject * args) {
+        if (!PyArg_ParseTuple(args, "O", &create_signature)) {
+            PyErr_SetString(PyExc_ValueError, "Invalid Argument to set_create_signature!");
+            return NULL;
+        }
+        Py_INCREF(create_signature);
+        memset(&slot, 0, sizeof(slot));
+        slot.sa_handler = &sighandler;
+        sigaction(SIGSEGV, &slot, NULL);
+        sigaction(SIGBUS, &slot, NULL);
+        Py_INCREF(Py_None);
+        return Py_None;
+    }
+
+    PyObject * run(PyObject * self, PyObject * args) {
+        {
+            PyObject * pn; npy_int64 cn;
+            if (
+                PyTuple_CheckExact(args) and PyTuple_GET_SIZE(args) == 1
+                and (pn = PyTuple_GET_ITEM(args, 0)) and PyLong_CheckExact(pn)
+            ) {
+                cn = PyLong_AS_LONG(pn);
+                try {
+                    return Py_BuildValue("l", fib_J(
+                          cn
+                    ));
+                } catch (...) {
+                    return NULL;
+                }
+            } else
+                PyErr_Clear();
+        }
+        PyObject * signatures = Py_BuildValue("(sO)", "gANdcQBdcQFjaG9wZS5fYXN0ClZhcmlhYmxlCnECKYFxA31xBChYBAAAAGRpbXNxBUsAWAUAAABk\ndHlwZXEGY2J1aWx0aW5zCmludApxB1gEAAAAbmFtZXEIWAEAAABucQl1YmFhLg==\n", args);
+        if (!signatures) {
+            PyErr_SetString(PyExc_ValueError, "Error building signature string for fib");
+            return NULL;
+        }
+        return PyObject_Call(create_signature, signatures, NULL);
+    }
+
+    PyMethodDef fibMethods[] = {
+        { "set_create_signature", set_create_signature, METH_VARARGS, "signal handler" },
+        { "run", run, METH_VARARGS, "module function" },
+        { NULL, NULL, 0, NULL }
+    };
+
+
+    static struct PyModuleDef fibmodule = {
+        PyModuleDef_HEAD_INIT,
+        "fib",
+        NULL,
+        -1,
+        fibMethods
+    };
+
+
+
+   PyMODINIT_FUNC PyInit_fib(void) {
+        import_array();
+        PyImport_ImportModule("numpy");
+        return PyModule_Create(&fibmodule);
+   }
+}
+
+ +
+
+
+ +
+
+ + +
+ +
+ + +
+
Overwriting src/fib.cpp
+
+
+
+ +
+
+ +
+
+
+
+
+

Quicksort CPP

+
+
+
+
+
+
In [5]:
+
+
+
%%file src/qsort_kernel.cpp
+#define PY_ARRAY_UNIQUE_SYMBOL qsort_kernel_ARRAY_API
+#define NPY_NO_DEPRECATED_API NPY_1_7_API_VERSION
+#include <Python.h>
+#include <numpy/arrayobject.h>
+#include <numpy/arrayscalars.h>
+#include <cmath>
+#include <tuple>
+#include <numeric>
+#include <cstdint>
+#include <cstdlib>
+#include <exception>
+#include <functional>
+#include <type_traits>
+struct PyObj {
+ typedef PyObject * ptr_t;
+ typedef PyArrayObject * arrptr_t;
+ PyObj(): dec(false), ptr(NULL) {}
+ PyObj(ptr_t p): dec(false), ptr(p) {}
+ ~PyObj() { if(dec) Py_DECREF(ptr); }
+ PyObj & operator=(ptr_t p) { if(dec) Py_DECREF(ptr); ptr = p; dec = false; return *this; }
+ PyObj & incref(ptr_t p) { if(dec) Py_DECREF(ptr); ptr = p; dec = (p != NULL); return *this; }
+ operator bool() const { return ptr; }
+ operator ptr_t() const { return ptr; }
+ operator arrptr_t() const { return (arrptr_t)ptr; }
+ bool dec;
+ ptr_t ptr;
+};
+inline std::tuple<PyObject *, npy_intp const *, npy_double *> qsort_kernel_d1JJ(PyObject * pa,
+    npy_intp const * __restrict__ sa,
+                                         npy_double * __restrict__ ca,
+                                         npy_int64 clo,
+                                         npy_int64 chi);
+
+inline std::tuple<PyObject *, npy_intp const *, npy_double *> qsort_kernel_d1JJ(PyObject * pa,
+                                         npy_intp const * __restrict__ sa,
+                                         npy_double * __restrict__ ca,
+                                         npy_int64 clo,
+                                         npy_int64 chi){
+ npy_int64 ci = clo;
+ npy_int64 cj = chi;
+ npy_double cpivot;
+
+ while (ci < chi) {
+  cpivot = ca[(int)((clo + chi) / 2)];
+
+  while (ci <= cj) {
+   while (ca[ci] < cpivot) {
+    ci += 1;
+   }
+
+   while (ca[cj] > cpivot) {
+    cj -= 1;
+   }
+
+   if (ci <= cj) {
+    auto ctmp = ca[ci];
+    ca[ci] = ca[cj];
+    ca[cj] = ctmp;
+    ci += 1;
+    cj -= 1;
+   }
+  }
+
+  if (clo < cj) {
+   qsort_kernel_d1JJ(pa, sa, ca, clo, cj);
+  }
+
+  clo = ci;
+  cj = chi;
+ }
+
+ return std::make_tuple((PyObject *)pa, sa, ca);
+}
+
+void sighandler(int sig);
+#include <signal.h>
+extern "C" {
+ PyObject * create_signature;
+ struct sigaction slot;
+ PyObject * set_create_signature(PyObject * self, PyObject * args) {
+  if (!PyArg_ParseTuple(args, "O", &create_signature)) {
+   PyErr_SetString(PyExc_ValueError, "Invalid Argument to set_create_signature!");
+   return NULL;
+  }
+  Py_INCREF(create_signature);
+  memset(&slot, 0, sizeof(slot));
+  slot.sa_handler = &sighandler;
+  sigaction(SIGSEGV, &slot, NULL);
+  sigaction(SIGBUS, &slot, NULL);
+  Py_INCREF(Py_None);
+  return Py_None;
+ }
+ PyObject * run(PyObject * self, PyObject * args) {
+  {
+   PyObj pa;
+   PyObject * plo; npy_int64 clo;
+   PyObject * phi; npy_int64 chi;
+   if (
+    PyTuple_CheckExact(args) and PyTuple_GET_SIZE(args) == 3
+    and (pa = PyTuple_GET_ITEM(args, 0)) and PyArray_CheckExact(pa)
+    and PyArray_TYPE((PyArrayObject *)pa) == NPY_FLOAT64 and PyArray_NDIM((PyArrayObject *)pa) == 1
+    and (plo = PyTuple_GET_ITEM(args, 1)) and PyLong_CheckExact(plo)
+    and (phi = PyTuple_GET_ITEM(args, 2)) and PyLong_CheckExact(phi)
+   ) {
+    if (!(pa.incref((PyObject *)PyArray_GETCONTIGUOUS((PyArrayObject *)pa)))) {
+     PyErr_SetString(PyExc_ValueError, "Invalid Argument type on a!");
+     return NULL;
+    }
+    clo = PyLong_AS_LONG(plo);
+    chi = PyLong_AS_LONG(phi);
+    try {
+     PyObject * res = std::get<0>(qsort_kernel_d1JJ(
+        pa, PyArray_SHAPE((PyArrayObject *)pa), (npy_double *)PyArray_DATA((PyArrayObject *)pa)
+      , clo
+      , chi
+     ));
+
+     Py_INCREF(res);
+     return res;
+    } catch (...) {
+     return NULL;
+    }
+   } else
+    PyErr_Clear();
+  }
+  PyObject * signatures = Py_BuildValue("(sO)", "(lp0\n(lp1\nccopy_reg\n_reconstructor\np2\n(chope._ast\nVariable\np3\nc__builtin__\nobject\np4\nNtp5\nRp6\n(dp7\nS'name'\np8\nS'a'\np9\nsS'dtype'\np10\nS'float64'\np11\nsS'dims'\np12\nI1\nsbag2\n(g3\ng4\nNtp13\nRp14\n(dp15\ng8\nS'lo'\np16\nsg10\nc__builtin__\nint\np17\nsg12\nI0\nsbag2\n(g3\ng4\nNtp18\nRp19\n(dp20\ng8\nS'hi'\np21\nsg10\ng17\nsg12\nI0\nsbaa.", args);
+  if (!signatures) {
+   PyErr_SetString(PyExc_ValueError, "Error building signature string for qsort_kernel");
+   return NULL;
+  }
+  return PyObject_Call(create_signature, signatures, NULL);
+ }
+ PyMethodDef qsort_kernelMethods[] = {
+  { "set_create_signature", set_create_signature, METH_VARARGS, "signal handler" },
+  { "run", run, METH_VARARGS, "module function" },
+  { NULL, NULL, 0, NULL }
+ };
+}
+
+static struct PyModuleDef qsort_kernel_module = {
+    PyModuleDef_HEAD_INIT,
+    "qsort_kernel",
+    NULL,
+    -1,
+    qsort_kernelMethods
+};
+
+PyMODINIT_FUNC PyInit_qsort_kernel(void) {
+    import_array();
+    PyImport_ImportModule("numpy");
+    return PyModule_Create(&qsort_kernel_module);
+}
+
+#include <string>
+#include <sstream>
+#include <iostream>
+#include <cxxabi.h>
+#include <execinfo.h>
+void sighandler(int sig) {
+ std::ostringstream buffer;
+ buffer << "Abort by " << (sig == SIGSEGV ? "segfault" : "bus error") << std::endl;
+ void * stack[64];
+ std::size_t depth = backtrace(stack, 64);
+ if (!depth)
+  buffer << "  <empty stacktrace, possibly corrupt>" << std::endl;
+ else {
+  char ** symbols = backtrace_symbols(stack, depth);
+  for (std::size_t i = 1; i < depth; ++i) {
+   std::string symbol = symbols[i];
+    if (symbol.find_first_of(' ', 59) != std::string::npos) {
+     std::string name = symbol.substr(59, symbol.find_first_of(' ', 59) - 59);
+     int status;
+     char * demangled = abi::__cxa_demangle(name.c_str(), NULL, NULL, &status);
+     if (!status) {
+      buffer << "    "
+       << symbol.substr(0, 59)
+       << demangled
+       << symbol.substr(59 + name.size())
+       << std::endl;
+      free(demangled);
+     } else
+      buffer << "    " << symbol << std::endl;
+    } else
+     buffer << "    " << symbol << std::endl;
+   }
+   free(symbols);
+  }
+  std::cerr << buffer.str();
+  std::exit(EXIT_FAILURE);
+ }
+
+ +
+
+
+ +
+
+ + +
+ +
+ + +
+
Overwriting src/qsort_kernel.cpp
+
+
+
+ +
+
+ +
+
+
+
+
+

Pi sum CPP

+
+
+
+
+
+
In [6]:
+
+
+
%%file src/pisum.cpp
+#define PY_ARRAY_UNIQUE_SYMBOL pisum_ARRAY_API
+#define NPY_NO_DEPRECATED_API NPY_1_7_API_VERSION
+#include <Python.h>
+#include <numpy/arrayobject.h>
+#include <numpy/arrayscalars.h>
+#include <cmath>
+#include <tuple>
+#include <numeric>
+#include <cstdint>
+#include <cstdlib>
+#include <exception>
+#include <functional>
+#include <type_traits>
+struct PyObj {
+    typedef PyObject * ptr_t;
+    typedef PyArrayObject * arrptr_t;
+    PyObj(): dec(false), ptr(NULL) {}
+    PyObj(ptr_t p): dec(false), ptr(p) {}
+    ~PyObj() { if(dec) Py_DECREF(ptr); }
+    PyObj & operator=(ptr_t p) { if(dec) Py_DECREF(ptr); ptr = p; dec = false; return *this; }
+    PyObj & incref(ptr_t p) { if(dec) Py_DECREF(ptr); ptr = p; dec = (p != NULL); return *this; }
+    operator bool() const { return ptr; }
+    operator ptr_t() const { return ptr; }
+    operator arrptr_t() const { return (arrptr_t)ptr; }
+    bool dec;
+    ptr_t ptr;
+};
+
+inline npy_double pisum_();
+
+inline npy_double pisum_() {
+    double csum = 0;
+    int ck = 1;
+    for (int cj = 1; cj < 501; ++cj) {
+        csum = 0.0;
+        for (ck = 1; ck < 10001; ++ck) {
+            csum += (1.0 / (double)(ck * ck));
+        }
+    }
+
+    return npy_double(csum);
+}
+
+void sighandler(int sig);
+#include <signal.h>
+extern "C" {
+    PyObject * create_signature;
+    struct sigaction slot;
+    PyObject * set_create_signature(PyObject * self, PyObject * args) {
+        if (!PyArg_ParseTuple(args, "O", &create_signature)) {
+            PyErr_SetString(PyExc_ValueError, "Invalid Argument to set_create_signature!");
+            return NULL;
+        }
+        Py_INCREF(create_signature);
+        memset(&slot, 0, sizeof(slot));
+        slot.sa_handler = &sighandler;
+        sigaction(SIGSEGV, &slot, NULL);
+        sigaction(SIGBUS, &slot, NULL);
+        Py_INCREF(Py_None);
+        return Py_None;
+    }
+    PyObject * run(PyObject * self, PyObject * args) {
+        {                try {
+                    return Py_BuildValue("d", pisum_());
+                } catch (...) {
+                    return NULL;
+                }
+        }
+        PyObject * signatures = Py_BuildValue("(sO)", "(lp0\n(lp1\na.", args);
+        if (!signatures) {
+            PyErr_SetString(PyExc_ValueError, "Error building signature string for pisum");
+            return NULL;
+        }
+        return PyObject_Call(create_signature, signatures, NULL);
+    }
+
+PyMethodDef pisumMethods[] = {
+{ "set_create_signature", set_create_signature, METH_VARARGS, "signal handler" },
+{ "run", run, METH_VARARGS, "module function" },
+{ NULL, NULL, 0, NULL }
+};
+
+static struct PyModuleDef pisum_module = {
+    PyModuleDef_HEAD_INIT,
+    "pisum",
+    NULL,
+    -1,
+    pisumMethods
+};
+
+PyMODINIT_FUNC PyInit_pisum(void) {
+    import_array();
+    PyImport_ImportModule("numpy");
+    return PyModule_Create(&pisum_module);
+}
+}
+
+#include <string>
+#include <sstream>
+#include <iostream>
+#include <cxxabi.h>
+#include <execinfo.h>
+void sighandler(int sig) {
+    std::ostringstream buffer;
+    buffer << "Abort by " << (sig == SIGSEGV ? "segfault" : "bus error") << std::endl;
+    void * stack[64];
+    std::size_t depth = backtrace(stack, 64);
+    if (!depth)
+        buffer << "  <empty stacktrace, possibly corrupt>" << std::endl;
+    else {
+        char ** symbols = backtrace_symbols(stack, depth);
+        for (std::size_t i = 1; i < depth; ++i) {
+            std::string symbol = symbols[i];
+                if (symbol.find_first_of(' ', 59) != std::string::npos) {
+                    std::string name = symbol.substr(59, symbol.find_first_of(' ', 59) - 59);
+                    int status;
+                    char * demangled = abi::__cxa_demangle(name.c_str(), NULL, NULL, &status);
+                    if (!status) {
+                        buffer << "    "
+                            << symbol.substr(0, 59)
+                            << demangled
+                            << symbol.substr(59 + name.size())
+                            << std::endl;
+                        free(demangled);
+                    } else
+                        buffer << "    " << symbol << std::endl;
+                } else
+                    buffer << "    " << symbol << std::endl;
+            }
+            free(symbols);
+        }
+        std::cerr << buffer.str();
+        std::exit(EXIT_FAILURE);
+    }
+
+ +
+
+
+ +
+
+ + +
+ +
+ + +
+
Overwriting src/pisum.cpp
+
+
+
+ +
+
+ +
+
+
+
In [7]:
+
+
+
%%file src/pisum_opt.cpp
+#define PY_ARRAY_UNIQUE_SYMBOL pisum_opt_ARRAY_API
+#define NPY_NO_DEPRECATED_API NPY_1_7_API_VERSION
+#include <Python.h>
+#include <numpy/arrayobject.h>
+#include <numpy/arrayscalars.h>
+#include <cmath>
+#include <tuple>
+#include <numeric>
+#include <cstdint>
+#include <cstdlib>
+#include <exception>
+#include <functional>
+#include <type_traits>
+struct PyObj {
+    typedef PyObject * ptr_t;
+    typedef PyArrayObject * arrptr_t;
+    PyObj(): dec(false), ptr(NULL) {}
+    PyObj(ptr_t p): dec(false), ptr(p) {}
+    ~PyObj() { if(dec) Py_DECREF(ptr); }
+    PyObj & operator=(ptr_t p) { if(dec) Py_DECREF(ptr); ptr = p; dec = false; return *this; }
+    PyObj & incref(ptr_t p) { if(dec) Py_DECREF(ptr); ptr = p; dec = (p != NULL); return *this; }
+    operator bool() const { return ptr; }
+    operator ptr_t() const { return ptr; }
+    operator arrptr_t() const { return (arrptr_t)ptr; }
+    bool dec;
+    ptr_t ptr;
+};
+
+inline npy_double pisum_opt_();
+
+inline npy_double pisum_opt_() {
+    npy_double csum = npy_double();
+    npy_intp ck = 1;
+    npy_double cf = 0.0;
+
+    for (npy_intp cj = 1; cj < 501; ++cj) {
+        csum = 0.0;
+        cf = 0.0;
+        for (ck = 1; ck < 10001; ++ck) {
+            cf += 1.0;
+            auto c__sp0 = (cf * cf);
+            csum += (1.0 / c__sp0);
+        }
+    }
+    return csum;
+}
+
+void sighandler(int sig);
+#include <signal.h>
+extern "C" {
+    PyObject * create_signature;
+    struct sigaction slot;
+    PyObject * set_create_signature(PyObject * self, PyObject * args) {
+        if (!PyArg_ParseTuple(args, "O", &create_signature)) {
+            PyErr_SetString(PyExc_ValueError, "Invalid Argument to set_create_signature!");
+            return NULL;
+        }
+        Py_INCREF(create_signature);
+        memset(&slot, 0, sizeof(slot));
+        slot.sa_handler = &sighandler;
+        sigaction(SIGSEGV, &slot, NULL);
+        sigaction(SIGBUS, &slot, NULL);
+        Py_INCREF(Py_None);
+        return Py_None;
+    }
+    PyObject * run(PyObject * self, PyObject * args) {
+        {                try {
+                    return Py_BuildValue("d", pisum_opt_());
+                } catch (...) {
+                    return NULL;
+                }
+        }
+        PyObject * signatures = Py_BuildValue("(sO)", "(lp0\n(lp1\na.", args);
+        if (!signatures) {
+            PyErr_SetString(PyExc_ValueError, "Error building signature string for pisum_opt");
+            return NULL;
+        }
+        return PyObject_Call(create_signature, signatures, NULL);
+    }
+
+    PyMethodDef pisum_optMethods[] = {
+    { "set_create_signature", set_create_signature, METH_VARARGS, "signal handler" },
+    { "run", run, METH_VARARGS, "module function" },
+    { NULL, NULL, 0, NULL }
+    };
+
+    static struct PyModuleDef pisum_opt_module = {
+        PyModuleDef_HEAD_INIT,
+        "pisum_opt",
+        NULL,
+        -1,
+        pisum_optMethods
+    };
+
+    PyMODINIT_FUNC PyInit_pisum_opt(void) {
+        import_array();
+        PyImport_ImportModule("numpy");
+        return PyModule_Create(&pisum_opt_module);
+    }
+}
+
+#include <string>
+#include <sstream>
+#include <iostream>
+#include <cxxabi.h>
+#include <execinfo.h>
+void sighandler(int sig) {
+    std::ostringstream buffer;
+    buffer << "Abort by " << (sig == SIGSEGV ? "segfault" : "bus error") << std::endl;
+    void * stack[64];
+    std::size_t depth = backtrace(stack, 64);
+    if (!depth)
+        buffer << "  <empty stacktrace, possibly corrupt>" << std::endl;
+    else {
+        char ** symbols = backtrace_symbols(stack, depth);
+        for (std::size_t i = 1; i < depth; ++i) {
+            std::string symbol = symbols[i];
+                if (symbol.find_first_of(' ', 59) != std::string::npos) {
+                    std::string name = symbol.substr(59, symbol.find_first_of(' ', 59) - 59);
+                    int status;
+                    char * demangled = abi::__cxa_demangle(name.c_str(), NULL, NULL, &status);
+                    if (!status) {
+                        buffer << "    "
+                            << symbol.substr(0, 59)
+                            << demangled
+                            << symbol.substr(59 + name.size())
+                            << std::endl;
+                        free(demangled);
+                    } else
+                        buffer << "    " << symbol << std::endl;
+                } else
+                    buffer << "    " << symbol << std::endl;
+            }
+            free(symbols);
+        }
+        std::cerr << buffer.str();
+        std::exit(EXIT_FAILURE);
+    }
+
+ +
+
+
+ +
+
+ + +
+ +
+ + +
+
Overwriting src/pisum_opt.cpp
+
+
+
+ +
+
+ +
+
+
+
+
+

10th order poly log approx CPP

+
+
+
+
+
+
In [8]:
+
+
+
%%file src/ln.cpp
+#define PY_ARRAY_UNIQUE_SYMBOL ln_hope_ARRAY_API
+#define NPY_NO_DEPRECATED_API NPY_1_7_API_VERSION
+#include <Python.h>
+#include <numpy/arrayobject.h>
+#include <numpy/arrayscalars.h>
+#include <cmath>
+#include <tuple>
+#include <numeric>
+#include <cstdint>
+#include <cstdlib>
+#include <exception>
+#include <functional>
+#include <type_traits>
+struct PyObj {
+    typedef PyObject * ptr_t;
+    typedef PyArrayObject * arrptr_t;
+    PyObj(): dec(false), ptr(NULL) {}
+    PyObj(ptr_t p): dec(false), ptr(p) {}
+    ~PyObj() { if(dec) Py_DECREF(ptr); }
+    PyObj & operator=(ptr_t p) { if(dec) Py_DECREF(ptr); ptr = p; dec = false; return *this; }
+    PyObj & incref(ptr_t p) { if(dec) Py_DECREF(ptr); ptr = p; dec = (p != NULL); return *this; }
+    operator bool() const { return ptr; }
+    operator ptr_t() const { return ptr; }
+    operator arrptr_t() const { return (arrptr_t)ptr; }
+    bool dec;
+    ptr_t ptr;
+};
+
+inline void ln_hope_d1d1(PyObject * pX, npy_intp const * __restrict__ sX, npy_double * __restrict__ cX,
+                                                    PyObject * pY, npy_intp const * __restrict__ sY, npy_double * __restrict__ cY);
+
+inline void ln_hope_d1d1(PyObject * pX, npy_intp const * __restrict__ sX, npy_double * __restrict__ cX,
+                                                    PyObject * pY, npy_intp const * __restrict__ sY, npy_double * __restrict__ cY){
+
+    for (npy_intp i0 = 0; i0 < sY[0] - 0; ++i0) {
+        cY[(int)(i0)] = (cX[i0] - 1) - (std::pow((cX[i0] - 1), 2) / 2) + (std::pow((cX[i0] - 1), 3) / 3) - (std::pow((cX[i0] - 1), 4) / 4) + (std::pow((cX[i0] - 1), 5) / 5) - (std::pow((cX[i0] - 1), 6) / 6) + (std::pow((cX[i0] - 1), 7) / 7) - (std::pow((cX[i0] - 1), 8) / 8) + (std::pow((cX[i0] - 1), 9) / 9);
+    }
+}
+
+void sighandler(int sig);
+#include <signal.h>
+extern "C" {
+    PyObject * create_signature;
+    struct sigaction slot;
+    PyObject * set_create_signature(PyObject * self, PyObject * args) {
+        if (!PyArg_ParseTuple(args, "O", &create_signature)) {
+            PyErr_SetString(PyExc_ValueError, "Invalid Argument to set_create_signature!");
+            return NULL;
+        }
+        Py_INCREF(create_signature);
+        memset(&slot, 0, sizeof(slot));
+        slot.sa_handler = &sighandler;
+        sigaction(SIGSEGV, &slot, NULL);
+        sigaction(SIGBUS, &slot, NULL);
+        Py_INCREF(Py_None);
+        return Py_None;
+    }
+    PyObject * run(PyObject * self, PyObject * args) {
+        {
+            PyObj pX;
+            PyObj pY;
+            if (
+                PyTuple_CheckExact(args) and PyTuple_GET_SIZE(args) == 2
+                and (pX = PyTuple_GET_ITEM(args, 0)) and PyArray_CheckExact(pX)
+                and PyArray_TYPE((PyArrayObject *)pX) == NPY_FLOAT64 and PyArray_NDIM((PyArrayObject *)pX) == 1
+                and (pY = PyTuple_GET_ITEM(args, 1)) and PyArray_CheckExact(pY)
+                and PyArray_TYPE((PyArrayObject *)pY) == NPY_FLOAT64 and PyArray_NDIM((PyArrayObject *)pY) == 1
+            ) {
+                if (!(pX.incref((PyObject *)PyArray_GETCONTIGUOUS((PyArrayObject *)pX)))) {
+                    PyErr_SetString(PyExc_ValueError, "Invalid Argument type on X!");
+                    return NULL;
+                }
+                if (!(pY.incref((PyObject *)PyArray_GETCONTIGUOUS((PyArrayObject *)pY)))) {
+                    PyErr_SetString(PyExc_ValueError, "Invalid Argument type on Y!");
+                    return NULL;
+                }
+                try {
+                    ln_hope_d1d1(
+                          pX, PyArray_SHAPE((PyArrayObject *)pX), (npy_double *)PyArray_DATA((PyArrayObject *)pX)
+                        , pY, PyArray_SHAPE((PyArrayObject *)pY), (npy_double *)PyArray_DATA((PyArrayObject *)pY)
+                    );
+                    Py_INCREF(Py_None);
+                    return Py_None;
+                } catch (...) {
+                    return NULL;
+                }
+            } else
+                PyErr_Clear();
+        }
+        PyObject * signatures = Py_BuildValue("(sO)", "(lp0\n(lp1\nccopy_reg\n_reconstructor\np2\n(chope._ast\nVariable\np3\nc__builtin__\nobject\np4\nNtp5\nRp6\n(dp7\nS'name'\np8\nS'X'\np9\nsS'dtype'\np10\nS'float64'\np11\nsS'dims'\np12\nI1\nsbag2\n(g3\ng4\nNtp13\nRp14\n(dp15\ng8\nS'Y'\np16\nsg10\ng11\nsg12\nI1\nsbaa.", args);
+        if (!signatures) {
+            PyErr_SetString(PyExc_ValueError, "Error building signature string for ln_hope");
+            return NULL;
+        }
+        return PyObject_Call(create_signature, signatures, NULL);
+    }
+
+    PyMethodDef lnMethods[] = {
+        { "set_create_signature", set_create_signature, METH_VARARGS, "signal handler" },
+        { "run", run, METH_VARARGS, "module function" },
+        { NULL, NULL, 0, NULL }
+    };
+
+
+    static struct PyModuleDef lnmodule = {
+        PyModuleDef_HEAD_INIT,
+        "ln",
+        NULL,
+        -1,
+        lnMethods
+    };
+
+
+
+   PyMODINIT_FUNC PyInit_ln(void) {
+        import_array();
+        PyImport_ImportModule("numpy");
+        return PyModule_Create(&lnmodule);
+   }
+}
+#include <string>
+#include <sstream>
+#include <iostream>
+#include <cxxabi.h>
+#include <execinfo.h>
+void sighandler(int sig) {
+    std::ostringstream buffer;
+    buffer << "Abort by " << (sig == SIGSEGV ? "segfault" : "bus error") << std::endl;
+    void * stack[64];
+    std::size_t depth = backtrace(stack, 64);
+    if (!depth)
+        buffer << "  <empty stacktrace, possibly corrupt>" << std::endl;
+    else {
+        char ** symbols = backtrace_symbols(stack, depth);
+        for (std::size_t i = 1; i < depth; ++i) {
+            std::string symbol = symbols[i];
+                if (symbol.find_first_of(' ', 59) != std::string::npos) {
+                    std::string name = symbol.substr(59, symbol.find_first_of(' ', 59) - 59);
+                    int status;
+                    char * demangled = abi::__cxa_demangle(name.c_str(), NULL, NULL, &status);
+                    if (!status) {
+                        buffer << "    "
+                            << symbol.substr(0, 59)
+                            << demangled
+                            << symbol.substr(59 + name.size())
+                            << std::endl;
+                        free(demangled);
+                    } else
+                        buffer << "    " << symbol << std::endl;
+                } else
+                    buffer << "    " << symbol << std::endl;
+            }
+            free(symbols);
+        }
+        std::cerr << buffer.str();
+        std::exit(EXIT_FAILURE);
+    }
+
+ +
+
+
+ +
+
+ + +
+ +
+ + +
+
Overwriting src/ln.cpp
+
+
+
+ +
+
+ +
+
+
+
In [9]:
+
+
+
%%file src/ln_exp.cpp
+#define PY_ARRAY_UNIQUE_SYMBOL ln_hope_exp_ARRAY_API
+#define NPY_NO_DEPRECATED_API NPY_1_7_API_VERSION
+#include <Python.h>
+#include <numpy/arrayobject.h>
+#include <numpy/arrayscalars.h>
+#include <cmath>
+#include <tuple>
+#include <numeric>
+#include <cstdint>
+#include <cstdlib>
+#include <exception>
+#include <functional>
+#include <type_traits>
+struct PyObj {
+    typedef PyObject * ptr_t;
+    typedef PyArrayObject * arrptr_t;
+    PyObj(): dec(false), ptr(NULL) {}
+    PyObj(ptr_t p): dec(false), ptr(p) {}
+    ~PyObj() { if(dec) Py_DECREF(ptr); }
+    PyObj & operator=(ptr_t p) { if(dec) Py_DECREF(ptr); ptr = p; dec = false; return *this; }
+    PyObj & incref(ptr_t p) { if(dec) Py_DECREF(ptr); ptr = p; dec = (p != NULL); return *this; }
+    operator bool() const { return ptr; }
+    operator ptr_t() const { return ptr; }
+    operator arrptr_t() const { return (arrptr_t)ptr; }
+    bool dec;
+    ptr_t ptr;
+};
+
+inline void ln_hope_exp_d1d1(PyObject * pX, npy_intp const * __restrict__ sX, npy_double * __restrict__ cX,
+                                                    PyObject * pY, npy_intp const * __restrict__ sY, npy_double * __restrict__ cY);
+
+inline void ln_hope_exp_d1d1(PyObject * pX, npy_intp const * __restrict__ sX, npy_double * __restrict__ cX,
+                                                    PyObject * pY, npy_intp const * __restrict__ sY, npy_double * __restrict__ cY){
+
+    for (npy_intp i0 = 0; i0 < sX[0] - 0; ++i0) {
+        auto cx = (cX[i0] - 1);
+        auto cx2 = (cx * cx);
+        auto cx4 = (cx2 * cx2);
+        auto cx6 = (cx4 * cx2);
+        auto cx8 = (cx4 * cx4);
+        cY[i0] = cx - (cx2 / 2) + (cx * cx2 / 3) - (cx4 / 4) + (cx * cx4 / 5) - (cx6 / 6) + (cx6 * cx / 7) - (cx8 / 8) + (cx8 * cx / 9);
+    }
+}
+
+void sighandler(int sig);
+#include <signal.h>
+extern "C" {
+    PyObject * create_signature;
+    struct sigaction slot;
+    PyObject * set_create_signature(PyObject * self, PyObject * args) {
+        if (!PyArg_ParseTuple(args, "O", &create_signature)) {
+            PyErr_SetString(PyExc_ValueError, "Invalid Argument to set_create_signature!");
+            return NULL;
+        }
+        Py_INCREF(create_signature);
+        memset(&slot, 0, sizeof(slot));
+        slot.sa_handler = &sighandler;
+        sigaction(SIGSEGV, &slot, NULL);
+        sigaction(SIGBUS, &slot, NULL);
+        Py_INCREF(Py_None);
+        return Py_None;
+    }
+    PyObject * run(PyObject * self, PyObject * args) {
+        {
+            PyObj pX;
+            PyObj pY;
+            if (
+                PyTuple_CheckExact(args) and PyTuple_GET_SIZE(args) == 2
+                and (pX = PyTuple_GET_ITEM(args, 0)) and PyArray_CheckExact(pX)
+                and PyArray_TYPE((PyArrayObject *)pX) == NPY_FLOAT64 and PyArray_NDIM((PyArrayObject *)pX) == 1
+                and (pY = PyTuple_GET_ITEM(args, 1)) and PyArray_CheckExact(pY)
+                and PyArray_TYPE((PyArrayObject *)pY) == NPY_FLOAT64 and PyArray_NDIM((PyArrayObject *)pY) == 1
+            ) {
+                if (!(pX.incref((PyObject *)PyArray_GETCONTIGUOUS((PyArrayObject *)pX)))) {
+                    PyErr_SetString(PyExc_ValueError, "Invalid Argument type on X!");
+                    return NULL;
+                }
+                if (!(pY.incref((PyObject *)PyArray_GETCONTIGUOUS((PyArrayObject *)pY)))) {
+                    PyErr_SetString(PyExc_ValueError, "Invalid Argument type on Y!");
+                    return NULL;
+                }
+                try {
+                    ln_hope_exp_d1d1(
+                          pX, PyArray_SHAPE((PyArrayObject *)pX), (npy_double *)PyArray_DATA((PyArrayObject *)pX)
+                        , pY, PyArray_SHAPE((PyArrayObject *)pY), (npy_double *)PyArray_DATA((PyArrayObject *)pY)
+                    );
+                    Py_INCREF(Py_None);
+                    return Py_None;
+                } catch (...) {
+                    return NULL;
+                }
+            } else
+                PyErr_Clear();
+        }
+        PyObject * signatures = Py_BuildValue("(sO)", "(lp0\n(lp1\nccopy_reg\n_reconstructor\np2\n(chope._ast\nVariable\np3\nc__builtin__\nobject\np4\nNtp5\nRp6\n(dp7\nS'name'\np8\nS'X'\np9\nsS'dtype'\np10\nS'float64'\np11\nsS'dims'\np12\nI1\nsbag2\n(g3\ng4\nNtp13\nRp14\n(dp15\ng8\nS'Y'\np16\nsg10\ng11\nsg12\nI1\nsbaa.", args);
+        if (!signatures) {
+            PyErr_SetString(PyExc_ValueError, "Error building signature string for ln_hope_exp");
+            return NULL;
+        }
+        return PyObject_Call(create_signature, signatures, NULL);
+    }
+    PyMethodDef ln_expMethods[] = {
+        { "set_create_signature", set_create_signature, METH_VARARGS, "signal handler" },
+        { "run", run, METH_VARARGS, "module function" },
+        { NULL, NULL, 0, NULL }
+    };
+
+
+    static struct PyModuleDef ln_expmodule = {
+        PyModuleDef_HEAD_INIT,
+        "ln_exp",
+        NULL,
+        -1,
+        ln_expMethods
+    };
+
+
+
+   PyMODINIT_FUNC PyInit_ln_exp(void) {
+        import_array();
+        PyImport_ImportModule("numpy");
+        return PyModule_Create(&ln_expmodule);
+   }
+}
+#include <string>
+#include <sstream>
+#include <iostream>
+#include <cxxabi.h>
+#include <execinfo.h>
+void sighandler(int sig) {
+    std::ostringstream buffer;
+    buffer << "Abort by " << (sig == SIGSEGV ? "segfault" : "bus error") << std::endl;
+    void * stack[64];
+    std::size_t depth = backtrace(stack, 64);
+    if (!depth)
+        buffer << "  <empty stacktrace, possibly corrupt>" << std::endl;
+    else {
+        char ** symbols = backtrace_symbols(stack, depth);
+        for (std::size_t i = 1; i < depth; ++i) {
+            std::string symbol = symbols[i];
+                if (symbol.find_first_of(' ', 59) != std::string::npos) {
+                    std::string name = symbol.substr(59, symbol.find_first_of(' ', 59) - 59);
+                    int status;
+                    char * demangled = abi::__cxa_demangle(name.c_str(), NULL, NULL, &status);
+                    if (!status) {
+                        buffer << "    "
+                            << symbol.substr(0, 59)
+                            << demangled
+                            << symbol.substr(59 + name.size())
+                            << std::endl;
+                        free(demangled);
+                    } else
+                        buffer << "    " << symbol << std::endl;
+                } else
+                    buffer << "    " << symbol << std::endl;
+            }
+            free(symbols);
+        }
+        std::cerr << buffer.str();
+        std::exit(EXIT_FAILURE);
+    }
+
+ +
+
+
+ +
+
+ + +
+ +
+ + +
+
Overwriting src/ln_exp.cpp
+
+
+
+ +
+
+ +
+
+
+
In [10]:
+
+
+
%%file src/ln_opt.cpp
+#define PY_ARRAY_UNIQUE_SYMBOL ln_hope_opt_ARRAY_API
+#define NPY_NO_DEPRECATED_API NPY_1_7_API_VERSION
+#include <Python.h>
+#include <numpy/arrayobject.h>
+#include <numpy/arrayscalars.h>
+#include <cmath>
+#include <tuple>
+#include <numeric>
+#include <cstdint>
+#include <cstdlib>
+#include <exception>
+#include <functional>
+#include <type_traits>
+struct PyObj {
+    typedef PyObject * ptr_t;
+    typedef PyArrayObject * arrptr_t;
+    PyObj(): dec(false), ptr(NULL) {}
+    PyObj(ptr_t p): dec(false), ptr(p) {}
+    ~PyObj() { if(dec) Py_DECREF(ptr); }
+    PyObj & operator=(ptr_t p) { if(dec) Py_DECREF(ptr); ptr = p; dec = false; return *this; }
+    PyObj & incref(ptr_t p) { if(dec) Py_DECREF(ptr); ptr = p; dec = (p != NULL); return *this; }
+    operator bool() const { return ptr; }
+    operator ptr_t() const { return ptr; }
+    operator arrptr_t() const { return (arrptr_t)ptr; }
+    bool dec;
+    ptr_t ptr;
+};
+
+inline void ln_hope_opt_d1d1(PyObject * pX, npy_intp const * __restrict__ sX, npy_double * __restrict__ cX,
+                                                    PyObject * pY, npy_intp const * __restrict__ sY, npy_double * __restrict__ cY);
+
+inline void ln_hope_opt_d1d1(PyObject * pX, npy_intp const * __restrict__ sX, npy_double * __restrict__ cX,
+                                                    PyObject * pY, npy_intp const * __restrict__ sY, npy_double * __restrict__ cY){
+
+    for (npy_intp i0 = 0; i0 < sY[0] - 0; ++i0) {
+        auto c__sp0 = (cX[i0] * cX[i0]);
+        auto c__sp1 = (c__sp0 * c__sp0);
+        auto c__sp2 = (c__sp1 * c__sp1);
+        auto c__sp3 = (c__sp2 * cX[i0]);
+        auto c__sp4 = (c__sp0 * cX[i0]);
+        auto c__sp5 = (c__sp4 * c__sp4);
+        auto c__sp6 = (c__sp5 * cX[i0]);
+        auto c__sp7 = (c__sp1 * cX[i0]);
+        cY[(int)(i0)] = (-7129.0 / 2520.0) + (28 * c__sp4) + (-(18 * c__sp0)) + (-(9 * c__sp2 / 8)) + (-(14 * c__sp5)) + (-(63 * c__sp1 / 2)) + (126 * c__sp7 / 5) + (9 * cX[i0]) + (c__sp3 / 9) + (36 * c__sp6 / 7);
+    }
+}
+
+void sighandler(int sig);
+#include <signal.h>
+extern "C" {
+    PyObject * create_signature;
+    struct sigaction slot;
+    PyObject * set_create_signature(PyObject * self, PyObject * args) {
+        if (!PyArg_ParseTuple(args, "O", &create_signature)) {
+            PyErr_SetString(PyExc_ValueError, "Invalid Argument to set_create_signature!");
+            return NULL;
+        }
+        Py_INCREF(create_signature);
+        memset(&slot, 0, sizeof(slot));
+        slot.sa_handler = &sighandler;
+        sigaction(SIGSEGV, &slot, NULL);
+        sigaction(SIGBUS, &slot, NULL);
+        Py_INCREF(Py_None);
+        return Py_None;
+    }
+    PyObject * run(PyObject * self, PyObject * args) {
+        {
+            PyObj pX;
+            PyObj pY;
+            if (
+                PyTuple_CheckExact(args) and PyTuple_GET_SIZE(args) == 2
+                and (pX = PyTuple_GET_ITEM(args, 0)) and PyArray_CheckExact(pX)
+                and PyArray_TYPE((PyArrayObject *)pX) == NPY_FLOAT64 and PyArray_NDIM((PyArrayObject *)pX) == 1
+                and (pY = PyTuple_GET_ITEM(args, 1)) and PyArray_CheckExact(pY)
+                and PyArray_TYPE((PyArrayObject *)pY) == NPY_FLOAT64 and PyArray_NDIM((PyArrayObject *)pY) == 1
+            ) {
+                if (!(pX.incref((PyObject *)PyArray_GETCONTIGUOUS((PyArrayObject *)pX)))) {
+                    PyErr_SetString(PyExc_ValueError, "Invalid Argument type on X!");
+                    return NULL;
+                }
+                if (!(pY.incref((PyObject *)PyArray_GETCONTIGUOUS((PyArrayObject *)pY)))) {
+                    PyErr_SetString(PyExc_ValueError, "Invalid Argument type on Y!");
+                    return NULL;
+                }
+                try {
+                    ln_hope_opt_d1d1(
+                          pX, PyArray_SHAPE((PyArrayObject *)pX), (npy_double *)PyArray_DATA((PyArrayObject *)pX)
+                        , pY, PyArray_SHAPE((PyArrayObject *)pY), (npy_double *)PyArray_DATA((PyArrayObject *)pY)
+                    );
+                    Py_INCREF(Py_None);
+                    return Py_None;
+                } catch (...) {
+                    return NULL;
+                }
+            } else
+                PyErr_Clear();
+        }
+        PyObject * signatures = Py_BuildValue("(sO)", "(lp0\n(lp1\nccopy_reg\n_reconstructor\np2\n(chope._ast\nVariable\np3\nc__builtin__\nobject\np4\nNtp5\nRp6\n(dp7\nS'name'\np8\nS'X'\np9\nsS'dtype'\np10\nS'float64'\np11\nsS'dims'\np12\nI1\nsbag2\n(g3\ng4\nNtp13\nRp14\n(dp15\ng8\nS'Y'\np16\nsg10\ng11\nsg12\nI1\nsbaa.", args);
+        if (!signatures) {
+            PyErr_SetString(PyExc_ValueError, "Error building signature string for ln_hope_opt");
+            return NULL;
+        }
+        return PyObject_Call(create_signature, signatures, NULL);
+    }
+
+    PyMethodDef ln_optMethods[] = {
+        { "set_create_signature", set_create_signature, METH_VARARGS, "signal handler" },
+        { "run", run, METH_VARARGS, "module function" },
+        { NULL, NULL, 0, NULL }
+    };
+
+
+    static struct PyModuleDef ln_optmodule = {
+        PyModuleDef_HEAD_INIT,
+        "ln",
+        NULL,
+        -1,
+        ln_optMethods
+    };
+
+
+
+   PyMODINIT_FUNC PyInit_ln_opt(void) {
+        import_array();
+        PyImport_ImportModule("numpy");
+        return PyModule_Create(&ln_optmodule);
+   }
+}
+#include <string>
+#include <sstream>
+#include <iostream>
+#include <cxxabi.h>
+#include <execinfo.h>
+void sighandler(int sig) {
+    std::ostringstream buffer;
+    buffer << "Abort by " << (sig == SIGSEGV ? "segfault" : "bus error") << std::endl;
+    void * stack[64];
+    std::size_t depth = backtrace(stack, 64);
+    if (!depth)
+        buffer << "  <empty stacktrace, possibly corrupt>" << std::endl;
+    else {
+        char ** symbols = backtrace_symbols(stack, depth);
+        for (std::size_t i = 1; i < depth; ++i) {
+            std::string symbol = symbols[i];
+                if (symbol.find_first_of(' ', 59) != std::string::npos) {
+                    std::string name = symbol.substr(59, symbol.find_first_of(' ', 59) - 59);
+                    int status;
+                    char * demangled = abi::__cxa_demangle(name.c_str(), NULL, NULL, &status);
+                    if (!status) {
+                        buffer << "    "
+                            << symbol.substr(0, 59)
+                            << demangled
+                            << symbol.substr(59 + name.size())
+                            << std::endl;
+                        free(demangled);
+                    } else
+                        buffer << "    " << symbol << std::endl;
+                } else
+                    buffer << "    " << symbol << std::endl;
+            }
+            free(symbols);
+        }
+        std::cerr << buffer.str();
+        std::exit(EXIT_FAILURE);
+    }
+
+ +
+
+
+ +
+
+ + +
+ +
+ + +
+
Overwriting src/ln_opt.cpp
+
+
+
+ +
+
+ +
+
+
+
+
+

Simplify CPP

+
+
+
+
+
+
In [11]:
+
+
+
%%file src/poly.cpp
+#define PY_ARRAY_UNIQUE_SYMBOL poly_ARRAY_API
+#define NPY_NO_DEPRECATED_API NPY_1_7_API_VERSION
+#include <Python.h>
+#include <numpy/arrayobject.h>
+#include <numpy/arrayscalars.h>
+#include <cmath>
+#include <tuple>
+#include <numeric>
+#include <cstdint>
+#include <cstdlib>
+#include <exception>
+#include <functional>
+#include <type_traits>
+struct PyObj {
+    typedef PyObject * ptr_t;
+    typedef PyArrayObject * arrptr_t;
+    PyObj(): dec(false), ptr(NULL) {}
+    PyObj(ptr_t p): dec(false), ptr(p) {}
+    ~PyObj() { if(dec) Py_DECREF(ptr); }
+    PyObj & operator=(ptr_t p) { if(dec) Py_DECREF(ptr); ptr = p; dec = false; return *this; }
+    PyObj & incref(ptr_t p) { if(dec) Py_DECREF(ptr); ptr = p; dec = (p != NULL); return *this; }
+    operator bool() const { return ptr; }
+    operator ptr_t() const { return ptr; }
+    operator arrptr_t() const { return (arrptr_t)ptr; }
+    bool dec;
+    ptr_t ptr;
+};
+inline void poly_d1d1(PyObject * pres, npy_intp const * __restrict__ sres, npy_double * __restrict__ cres,
+                                            PyObject * parg, npy_intp const * __restrict__ sarg, npy_double * __restrict__ carg);
+
+inline void poly_d1d1(PyObject * pres, npy_intp const * __restrict__ sres, npy_double * __restrict__ cres,
+                                            PyObject * parg, npy_intp const * __restrict__ sarg, npy_double * __restrict__ carg){
+
+    npy_double arg_i;
+    double sin_arg_i;
+    for (npy_intp i0 = 0; i0 < sres[0] - 0; ++i0) {
+        arg_i = carg[i0];
+        cres[(int)(i0)] = std::pow(std::sin(arg_i), 2) + (std::pow(arg_i, 3) + std::pow(arg_i, 2) - arg_i - 1) / (std::pow(arg_i, 2) + 2 * arg_i + 1) + std::pow(std::cos(arg_i), 2);
+    }
+}
+void sighandler(int sig);
+#include <signal.h>
+extern "C" {
+    PyObject * create_signature;
+    struct sigaction slot;
+    PyObject * set_create_signature(PyObject * self, PyObject * args) {
+        if (!PyArg_ParseTuple(args, "O", &create_signature)) {
+            PyErr_SetString(PyExc_ValueError, "Invalid Argument to set_create_signature!");
+            return NULL;
+        }
+        Py_INCREF(create_signature);
+        memset(&slot, 0, sizeof(slot));
+        slot.sa_handler = &sighandler;
+        sigaction(SIGSEGV, &slot, NULL);
+        sigaction(SIGBUS, &slot, NULL);
+        Py_INCREF(Py_None);
+        return Py_None;
+    }
+    PyObject * run(PyObject * self, PyObject * args) {
+        {
+            PyObj pres;
+            PyObj parg;
+            if (
+                PyTuple_CheckExact(args) and PyTuple_GET_SIZE(args) == 2
+                and (pres = PyTuple_GET_ITEM(args, 0)) and PyArray_CheckExact(pres)
+                and PyArray_TYPE((PyArrayObject *)pres) == NPY_FLOAT64 and PyArray_NDIM((PyArrayObject *)pres) == 1
+                and (parg = PyTuple_GET_ITEM(args, 1)) and PyArray_CheckExact(parg)
+                and PyArray_TYPE((PyArrayObject *)parg) == NPY_FLOAT64 and PyArray_NDIM((PyArrayObject *)parg) == 1
+            ) {
+                if (!(pres.incref((PyObject *)PyArray_GETCONTIGUOUS((PyArrayObject *)pres)))) {
+                    PyErr_SetString(PyExc_ValueError, "Invalid Argument type on res!");
+                    return NULL;
+                }
+                if (!(parg.incref((PyObject *)PyArray_GETCONTIGUOUS((PyArrayObject *)parg)))) {
+                    PyErr_SetString(PyExc_ValueError, "Invalid Argument type on arg!");
+                    return NULL;
+                }
+                try {
+                    poly_d1d1(
+                          pres, PyArray_SHAPE((PyArrayObject *)pres), (npy_double *)PyArray_DATA((PyArrayObject *)pres)
+                        , parg, PyArray_SHAPE((PyArrayObject *)parg), (npy_double *)PyArray_DATA((PyArrayObject *)parg)
+                    );
+                    Py_INCREF(Py_None);
+                    return Py_None;
+                } catch (...) {
+                    return NULL;
+                }
+            } else
+                PyErr_Clear();
+        }
+        PyObject * signatures = Py_BuildValue("(sO)", "(lp0\n(lp1\nccopy_reg\n_reconstructor\np2\n(chope._ast\nVariable\np3\nc__builtin__\nobject\np4\nNtp5\nRp6\n(dp7\nS'name'\np8\nS'res'\np9\nsS'dtype'\np10\nS'float64'\np11\nsS'dims'\np12\nI1\nsbag2\n(g3\ng4\nNtp13\nRp14\n(dp15\ng8\nS'arg'\np16\nsg10\ng11\nsg12\nI1\nsbaa.", args);
+        if (!signatures) {
+            PyErr_SetString(PyExc_ValueError, "Error building signature string for poly");
+            return NULL;
+        }
+        return PyObject_Call(create_signature, signatures, NULL);
+    }
+
+    PyMethodDef polyMethods[] = {
+        { "set_create_signature", set_create_signature, METH_VARARGS, "signal handler" },
+        { "run", run, METH_VARARGS, "module function" },
+        { NULL, NULL, 0, NULL }
+    };
+
+    static struct PyModuleDef polymodule = {
+        PyModuleDef_HEAD_INIT,
+        "poly",
+        NULL,
+        -1,
+        polyMethods
+    };
+
+   PyMODINIT_FUNC PyInit_poly(void) {
+        import_array();
+        PyImport_ImportModule("numpy");
+        return PyModule_Create(&polymodule);
+   }
+}
+#include <string>
+#include <sstream>
+#include <iostream>
+#include <cxxabi.h>
+#include <execinfo.h>
+void sighandler(int sig) {
+    std::ostringstream buffer;
+    buffer << "Abort by " << (sig == SIGSEGV ? "segfault" : "bus error") << std::endl;
+    void * stack[64];
+    std::size_t depth = backtrace(stack, 64);
+    if (!depth)
+        buffer << "  <empty stacktrace, possibly corrupt>" << std::endl;
+    else {
+        char ** symbols = backtrace_symbols(stack, depth);
+        for (std::size_t i = 1; i < depth; ++i) {
+            std::string symbol = symbols[i];
+                if (symbol.find_first_of(' ', 59) != std::string::npos) {
+                    std::string name = symbol.substr(59, symbol.find_first_of(' ', 59) - 59);
+                    int status;
+                    char * demangled = abi::__cxa_demangle(name.c_str(), NULL, NULL, &status);
+                    if (!status) {
+                        buffer << "    "
+                            << symbol.substr(0, 59)
+                            << demangled
+                            << symbol.substr(59 + name.size())
+                            << std::endl;
+                        free(demangled);
+                    } else
+                        buffer << "    " << symbol << std::endl;
+                } else
+                    buffer << "    " << symbol << std::endl;
+            }
+            free(symbols);
+        }
+        std::cerr << buffer.str();
+        std::exit(EXIT_FAILURE);
+    }
+
+ +
+
+
+ +
+
+ + +
+ +
+ + +
+
Overwriting src/poly.cpp
+
+
+
+ +
+
+ +
+
+
+
In [12]:
+
+
+
%%file src/poly_opt.cpp
+#define PY_ARRAY_UNIQUE_SYMBOL poly_ARRAY_API
+#define NPY_NO_DEPRECATED_API NPY_1_7_API_VERSION
+#include <Python.h>
+#include <numpy/arrayobject.h>
+#include <numpy/arrayscalars.h>
+#include <cmath>
+#include <tuple>
+#include <numeric>
+#include <cstdint>
+#include <cstdlib>
+#include <exception>
+#include <functional>
+#include <type_traits>
+struct PyObj {
+	typedef PyObject * ptr_t;
+	typedef PyArrayObject * arrptr_t;
+	PyObj(): dec(false), ptr(NULL) {}
+	PyObj(ptr_t p): dec(false), ptr(p) {}
+	~PyObj() { if(dec) Py_DECREF(ptr); }
+	PyObj & operator=(ptr_t p) { if(dec) Py_DECREF(ptr); ptr = p; dec = false; return *this; }
+	PyObj & incref(ptr_t p) { if(dec) Py_DECREF(ptr); ptr = p; dec = (p != NULL); return *this; }
+	operator bool() const { return ptr; }
+	operator ptr_t() const { return ptr; }
+	operator arrptr_t() const { return (arrptr_t)ptr; }
+	bool dec;
+	ptr_t ptr;
+};
+
+inline void poly_d1d1(PyObject * pres, npy_intp const * __restrict__ sres, npy_double * __restrict__ cres, 
+											PyObject * parg, npy_intp const * __restrict__ sarg, npy_double * __restrict__ carg);
+
+inline void poly_d1d1(PyObject * pres, npy_intp const * __restrict__ sres, npy_double * __restrict__ cres, 
+											PyObject * parg, npy_intp const * __restrict__ sarg, npy_double * __restrict__ carg){
+	for (npy_intp i0 = 0; i0 < sres[0] - 0; ++i0) {
+		cres[(int)(i0)] = carg[i0];
+	}
+}
+
+void sighandler(int sig);
+#include <signal.h>
+extern "C" {
+	PyObject * create_signature;
+	struct sigaction slot;
+	PyObject * set_create_signature(PyObject * self, PyObject * args) {
+		if (!PyArg_ParseTuple(args, "O", &create_signature)) {
+			PyErr_SetString(PyExc_ValueError, "Invalid Argument to set_create_signature!");
+			return NULL;
+		}
+		Py_INCREF(create_signature);
+		memset(&slot, 0, sizeof(slot));
+		slot.sa_handler = &sighandler;
+		sigaction(SIGSEGV, &slot, NULL);
+		sigaction(SIGBUS, &slot, NULL);
+		Py_INCREF(Py_None);
+		return Py_None;
+	}
+	PyObject * run(PyObject * self, PyObject * args) {
+		{
+			PyObj pres;
+			PyObj parg;
+			if (
+				PyTuple_CheckExact(args) and PyTuple_GET_SIZE(args) == 2
+				and (pres = PyTuple_GET_ITEM(args, 0)) and PyArray_CheckExact(pres)
+				and PyArray_TYPE((PyArrayObject *)pres) == NPY_FLOAT64 and PyArray_NDIM((PyArrayObject *)pres) == 1
+				and (parg = PyTuple_GET_ITEM(args, 1)) and PyArray_CheckExact(parg)
+				and PyArray_TYPE((PyArrayObject *)parg) == NPY_FLOAT64 and PyArray_NDIM((PyArrayObject *)parg) == 1
+			) {
+				if (!(pres.incref((PyObject *)PyArray_GETCONTIGUOUS((PyArrayObject *)pres)))) {
+					PyErr_SetString(PyExc_ValueError, "Invalid Argument type on res!");
+					return NULL;
+				}
+				if (!(parg.incref((PyObject *)PyArray_GETCONTIGUOUS((PyArrayObject *)parg)))) {
+					PyErr_SetString(PyExc_ValueError, "Invalid Argument type on arg!");
+					return NULL;
+				}
+				try {
+					poly_d1d1(
+						  pres, PyArray_SHAPE((PyArrayObject *)pres), (npy_double *)PyArray_DATA((PyArrayObject *)pres)
+						, parg, PyArray_SHAPE((PyArrayObject *)parg), (npy_double *)PyArray_DATA((PyArrayObject *)parg)
+					);
+					Py_INCREF(Py_None);
+					return Py_None;
+				} catch (...) {
+					return NULL;
+				}
+			} else
+				PyErr_Clear();
+		}
+		PyObject * signatures = Py_BuildValue("(sO)", "(lp0\n(lp1\nccopy_reg\n_reconstructor\np2\n(chope._ast\nVariable\np3\nc__builtin__\nobject\np4\nNtp5\nRp6\n(dp7\nS'name'\np8\nS'res'\np9\nsS'dtype'\np10\nS'float64'\np11\nsS'dims'\np12\nI1\nsbag2\n(g3\ng4\nNtp13\nRp14\n(dp15\ng8\nS'arg'\np16\nsg10\ng11\nsg12\nI1\nsbaa.", args);
+		if (!signatures) {
+			PyErr_SetString(PyExc_ValueError, "Error building signature string for poly");
+			return NULL;
+		}
+		return PyObject_Call(create_signature, signatures, NULL);
+	}
+	PyMethodDef polyMethods[] = {
+		{ "set_create_signature", (PyCFunction)set_create_signature, METH_VARARGS },
+		{ "run", (PyCFunction)run, METH_VARARGS },
+		{ NULL, NULL }
+	};
+	PyMODINIT_FUNC initpoly(void) {
+		import_array();
+		PyImport_ImportModule("numpy");
+		(void)Py_InitModule("poly", polyMethods);
+	}
+}
+#include <string>
+#include <sstream>
+#include <iostream>
+#include <cxxabi.h>
+#include <execinfo.h>
+void sighandler(int sig) {
+	std::ostringstream buffer;
+	buffer << "Abort by " << (sig == SIGSEGV ? "segfault" : "bus error") << std::endl;
+	void * stack[64];
+	std::size_t depth = backtrace(stack, 64);
+	if (!depth)
+		buffer << "  <empty stacktrace, possibly corrupt>" << std::endl;
+	else {
+		char ** symbols = backtrace_symbols(stack, depth);
+		for (std::size_t i = 1; i < depth; ++i) {
+			std::string symbol = symbols[i];
+				if (symbol.find_first_of(' ', 59) != std::string::npos) {
+					std::string name = symbol.substr(59, symbol.find_first_of(' ', 59) - 59);
+					int status;
+					char * demangled = abi::__cxa_demangle(name.c_str(), NULL, NULL, &status);
+					if (!status) {
+						buffer << "    " 
+							<< symbol.substr(0, 59) 
+							<< demangled
+							<< symbol.substr(59 + name.size())
+							<< std::endl;
+						free(demangled);
+					} else
+						buffer << "    " << symbol << std::endl;
+				} else
+					buffer << "    " << symbol << std::endl;
+			}
+			free(symbols);
+		}
+		std::cerr << buffer.str();
+		std::exit(EXIT_FAILURE);
+	}
+
+ +
+
+
+ +
+
+ + +
+ +
+ + +
+
Overwriting src/poly_opt.cpp
+
+
+
+ +
+
+ +
+
+
+
+
+

Pairwise distance

+
+
+
+
+
+
In [13]:
+
+
+
%%file src/pairwise.cpp
+#define PY_ARRAY_UNIQUE_SYMBOL pairwise_ARRAY_API
+#define NPY_NO_DEPRECATED_API NPY_1_7_API_VERSION
+#include <Python.h>
+#include <numpy/arrayobject.h>
+#include <numpy/arrayscalars.h>
+#include <cmath>
+#include <tuple>
+#include <numeric>
+#include <cstdint>
+#include <cstdlib>
+#include <exception>
+#include <functional>
+#include <type_traits>
+struct PyObj {
+    typedef PyObject * ptr_t;
+    typedef PyArrayObject * arrptr_t;
+    PyObj(): dec(false), ptr(NULL) {}
+    PyObj(ptr_t p): dec(false), ptr(p) {}
+    ~PyObj() { if(dec) Py_DECREF(ptr); }
+    PyObj & operator=(ptr_t p) { if(dec) Py_DECREF(ptr); ptr = p; dec = false; return *this; }
+    PyObj & incref(ptr_t p) { if(dec) Py_DECREF(ptr); ptr = p; dec = (p != NULL); return *this; }
+    operator bool() const { return ptr; }
+    operator ptr_t() const { return ptr; }
+    operator arrptr_t() const { return (arrptr_t)ptr; }
+    bool dec;
+    ptr_t ptr;
+};
+
+inline void pairwise_d2d2JJ(PyObject * pX,
+                                                        npy_intp const * __restrict__ sX,
+                                                        npy_double * __restrict__ cX,
+                                                        PyObject * pD,
+                                                        npy_intp const * __restrict__ sD,
+                                                        npy_double * __restrict__ cD,
+                                                        npy_int64 const cM,
+                                                        npy_int64 const cN);
+
+inline void pairwise_d2d2JJ(PyObject * pX,
+                                                        npy_intp const * __restrict__ sX,
+                                                        npy_double * __restrict__ cX,
+                                                        PyObject * pD,
+                                                        npy_intp const * __restrict__ sD,
+                                                        npy_double * __restrict__ cD,
+                                                        npy_int64 const cM,
+                                                        npy_int64 const cN){
+
+    npy_double cd = 0.0;
+    npy_double ctmp = 0.0;
+
+    npy_intp cj;
+    npy_intp ck;
+    npy_intp x_i_idx;
+    npy_intp x_j_idx;
+    npy_intp d_i_idx;
+    npy_intp const xp = sX[1];
+    npy_intp const dp = sD[1];
+
+    for (npy_intp ci = 0; ci < cM; ++ci) {
+        x_i_idx = ci*xp;
+        d_i_idx = ci*dp;
+        for (cj = 0; cj < cM; ++cj) {
+            cd = 0.0;
+            x_j_idx = cj*xp;
+            for (ck = 0; ck < cN; ++ck) {
+                ctmp = (cX[(x_i_idx + ck)] - cX[(x_j_idx + ck)]);
+                cd += (ctmp * ctmp);
+            }
+            cD[(d_i_idx + cj)] = std::sqrt(cd);
+        }
+    }
+}
+
+void sighandler(int sig);
+#include <signal.h>
+extern "C" {
+    PyObject * create_signature;
+    struct sigaction slot;
+    PyObject * set_create_signature(PyObject * self, PyObject * args) {
+        if (!PyArg_ParseTuple(args, "O", &create_signature)) {
+            PyErr_SetString(PyExc_ValueError, "Invalid Argument to set_create_signature!");
+            return NULL;
+        }
+        Py_INCREF(create_signature);
+        memset(&slot, 0, sizeof(slot));
+        slot.sa_handler = &sighandler;
+        sigaction(SIGSEGV, &slot, NULL);
+        sigaction(SIGBUS, &slot, NULL);
+        Py_INCREF(Py_None);
+        return Py_None;
+    }
+    PyObject * run(PyObject * self, PyObject * args) {
+        {
+            PyObj pX;
+            PyObj pD;
+            PyObject * pM; npy_int64 cM;
+            PyObject * pN; npy_int64 cN;
+            if (
+                PyTuple_CheckExact(args) and PyTuple_GET_SIZE(args) == 4
+                and (pX = PyTuple_GET_ITEM(args, 0)) and PyArray_CheckExact(pX)
+                and PyArray_TYPE((PyArrayObject *)pX) == NPY_FLOAT64 and PyArray_NDIM((PyArrayObject *)pX) == 2
+                and (pD = PyTuple_GET_ITEM(args, 1)) and PyArray_CheckExact(pD)
+                and PyArray_TYPE((PyArrayObject *)pD) == NPY_FLOAT64 and PyArray_NDIM((PyArrayObject *)pD) == 2
+                and (pM = PyTuple_GET_ITEM(args, 2)) and PyLong_CheckExact(pM)
+                and (pN = PyTuple_GET_ITEM(args, 3)) and PyLong_CheckExact(pN)
+            ) {
+                if (!(pX.incref((PyObject *)PyArray_GETCONTIGUOUS((PyArrayObject *)pX)))) {
+                    PyErr_SetString(PyExc_ValueError, "Invalid Argument type on X!");
+                    return NULL;
+                }
+                if (!(pD.incref((PyObject *)PyArray_GETCONTIGUOUS((PyArrayObject *)pD)))) {
+                    PyErr_SetString(PyExc_ValueError, "Invalid Argument type on D!");
+                    return NULL;
+                }
+                cM = PyLong_AS_LONG(pM);
+                cN = PyLong_AS_LONG(pN);
+                try {
+                    pairwise_d2d2JJ(
+                          pX, PyArray_SHAPE((PyArrayObject *)pX), (npy_double *)PyArray_DATA((PyArrayObject *)pX)
+                        , pD, PyArray_SHAPE((PyArrayObject *)pD), (npy_double *)PyArray_DATA((PyArrayObject *)pD)
+                        , cM
+                        , cN
+                    );
+                    Py_INCREF(Py_None);
+                    return Py_None;
+                } catch (...) {
+                    return NULL;
+                }
+            } else
+                PyErr_Clear();
+        }
+        PyObject * signatures = Py_BuildValue("(sO)", "(lp0\n(lp1\nccopy_reg\n_reconstructor\np2\n(chope._ast\nVariable\np3\nc__builtin__\nobject\np4\nNtp5\nRp6\n(dp7\nS'name'\np8\nS'X'\np9\nsS'dtype'\np10\nS'float64'\np11\nsS'dims'\np12\nI2\nsbag2\n(g3\ng4\nNtp13\nRp14\n(dp15\ng8\nS'D'\np16\nsg10\ng11\nsg12\nI2\nsbag2\n(g3\ng4\nNtp17\nRp18\n(dp19\ng8\nS'M'\np20\nsg10\nc__builtin__\nint\np21\nsg12\nI0\nsbag2\n(g3\ng4\nNtp22\nRp23\n(dp24\ng8\nS'N'\np25\nsg10\ng21\nsg12\nI0\nsbaa.", args);
+        if (!signatures) {
+            PyErr_SetString(PyExc_ValueError, "Error building signature string for pairwise");
+            return NULL;
+        }
+        return PyObject_Call(create_signature, signatures, NULL);
+    }
+
+    PyMethodDef pairwiseMethods[] = {
+        { "set_create_signature", set_create_signature, METH_VARARGS, "signal handler" },
+        { "run", run, METH_VARARGS, "module function" },
+        { NULL, NULL, 0, NULL }
+    };
+
+
+    static struct PyModuleDef pairwisemodule = {
+        PyModuleDef_HEAD_INIT,
+        "pairwise",
+        NULL,
+        -1,
+        pairwiseMethods
+    };
+
+
+   PyMODINIT_FUNC PyInit_pairwise(void) {
+        import_array();
+        PyImport_ImportModule("numpy");
+        return PyModule_Create(&pairwisemodule);
+   }
+}
+#include <string>
+#include <sstream>
+#include <iostream>
+#include <cxxabi.h>
+#include <execinfo.h>
+void sighandler(int sig) {
+    std::ostringstream buffer;
+    buffer << "Abort by " << (sig == SIGSEGV ? "segfault" : "bus error") << std::endl;
+    void * stack[64];
+    std::size_t depth = backtrace(stack, 64);
+    if (!depth)
+        buffer << "  <empty stacktrace, possibly corrupt>" << std::endl;
+    else {
+        char ** symbols = backtrace_symbols(stack, depth);
+        for (std::size_t i = 1; i < depth; ++i) {
+            std::string symbol = symbols[i];
+                if (symbol.find_first_of(' ', 59) != std::string::npos) {
+                    std::string name = symbol.substr(59, symbol.find_first_of(' ', 59) - 59);
+                    int status;
+                    char * demangled = abi::__cxa_demangle(name.c_str(), NULL, NULL, &status);
+                    if (!status) {
+                        buffer << "    "
+                            << symbol.substr(0, 59)
+                            << demangled
+                            << symbol.substr(59 + name.size())
+                            << std::endl;
+                        free(demangled);
+                    } else
+                        buffer << "    " << symbol << std::endl;
+                } else
+                    buffer << "    " << symbol << std::endl;
+            }
+            free(symbols);
+        }
+        std::cerr << buffer.str();
+        std::exit(EXIT_FAILURE);
+    }
+
+ +
+
+
+ +
+
+ + +
+ +
+ + +
+
Overwriting src/pairwise.cpp
+
+
+
+ +
+
+ +
+
+
+
+
+

Star point spread function CPP

+ +
+
+
+
+
+
In [14]:
+
+
+
%%file src/pdf.cpp
+#define PY_ARRAY_UNIQUE_SYMBOL pdf_ARRAY_API
+#define NPY_NO_DEPRECATED_API NPY_1_7_API_VERSION
+#include <Python.h>
+#include <numpy/arrayobject.h>
+#include <numpy/arrayscalars.h>
+#include <cmath>
+#include <tuple>
+#include <numeric>
+#include <cstdint>
+#include <cstdlib>
+#include <exception>
+#include <functional>
+#include <type_traits>
+struct PyObj {
+    typedef PyObject * ptr_t;
+    typedef PyArrayObject * arrptr_t;
+    PyObj(): dec(false), ptr(NULL) {}
+    PyObj(ptr_t p): dec(false), ptr(p) {}
+    ~PyObj() { if(dec) Py_DECREF(ptr); }
+    PyObj & operator=(ptr_t p) { if(dec) Py_DECREF(ptr); ptr = p; dec = false; return *this; }
+    PyObj & incref(ptr_t p) { if(dec) Py_DECREF(ptr); ptr = p; dec = (p != NULL); return *this; }
+    operator bool() const { return ptr; }
+    operator ptr_t() const { return ptr; }
+    operator arrptr_t() const { return (arrptr_t)ptr; }
+    bool dec;
+    ptr_t ptr;
+};
+
+inline std::tuple<PyObject *, npy_intp const *, npy_float *> pdf_f2l1d1f2JDd(
+      PyObject * pdensity
+  , npy_intp const * __restrict__ sdensity
+  , npy_float * __restrict__ cdensity
+    , PyObject * pdims
+    , npy_intp const * __restrict__ sdims
+    , npy_int64 * __restrict__ cdims
+    , PyObject * pcenter
+    , npy_intp const * __restrict__ scenter
+    , npy_double * __restrict__ ccenter
+    , PyObject * pw2D
+    , npy_intp const * __restrict__ sw2D
+    , npy_float * __restrict__ cw2D
+    , npy_int64 cr50
+    , npy_double cb
+    , npy_double ca);
+
+inline std::tuple<PyObject *, npy_intp const *, npy_float *> pdf_f2l1d1f2JDd(
+      PyObject * pdensity
+  , npy_intp const * __restrict__ sdensity
+  , npy_float * __restrict__ cdensity
+    , PyObject * pdims
+    , npy_intp const * __restrict__ sdims
+    , npy_int64 * __restrict__ cdims
+    , PyObject * pcenter
+    , npy_intp const * __restrict__ scenter
+    , npy_double * __restrict__ ccenter
+    , PyObject * pw2D
+    , npy_intp const * __restrict__ sw2D
+    , npy_float * __restrict__ cw2D
+    , npy_int64 const cr50
+    , npy_double const cb
+    , npy_double const ca) {
+
+    npy_double cdr;
+    npy_double c__sum0;
+    const npy_double x_center = ccenter[0];
+    const npy_double y_center = ccenter[1];
+    const npy_intp len_0_sw2D = sw2D[0];
+    const npy_intp len_1_sw2D = sw2D[1];
+    npy_intp dc[] = {len_0_sw2D, len_1_sw2D};
+    PyObject * pc = PyArray_EMPTY(2, dc, NPY_FLOAT64, 0);
+    npy_intp * sc = PyArray_SHAPE((PyArrayObject *)pc);
+    npy_double * cc = (npy_double *)PyArray_DATA((PyArrayObject *)pc);
+
+    npy_intp cy = 0;
+    npy_intp i0 = 0;
+    npy_intp i1 = 0;
+    npy_intp i2 = 0;
+    npy_intp i3 = 0;
+
+    npy_intp sw2D_i0_idx;
+    npy_intp sw2D_i2_idx;
+    npy_intp density_x_idx;
+
+    auto c__sp0 = ca * ca;
+    auto c__sp1 = cr50 * cr50;
+    auto c__sp2 = 1.0 / (c__sp0 * c__sp1);
+    auto c__sp4 = 0.3183098861846737 * c__sp2 * (-1 + cb);
+
+    for (npy_intp cx = 0; cx < cdims[(int)(0)]; ++cx) {
+
+        density_x_idx = cx*sdensity[1];
+        for (cy = 0; cy < cdims[(int)(1)]; ++cy) {
+
+            cdr = std::sqrt(std::pow(cx - x_center, 2) + std::pow(cy - y_center, 2));
+            auto c__sp3 = cdr * cdr;
+            auto c__sp5 = c__sp4 * std::pow((1 + c__sp2 * c__sp3), -cb);
+
+            for (i0 = 0; i0 < len_0_sw2D - 0; ++i0) {
+
+                sw2D_i0_idx = (i0)*len_1_sw2D;
+                for (i1 = 0; i1 < len_1_sw2D - 0; ++i1) {
+                    cc[sw2D_i0_idx + i1] = c__sp5 * cw2D[sw2D_i0_idx + i1];
+                }
+            }
+
+            c__sum0 = 0;
+            for (i2 = 0; i2 < len_0_sw2D - 0; ++i2) {
+
+                sw2D_i2_idx = (i2)*len_1_sw2D;
+                for (i3 = 0; i3 < len_1_sw2D - 0; ++i3) {
+                    c__sum0 += cc[sw2D_i2_idx + i3];
+                }
+            }
+
+            cdensity[(density_x_idx + cy)] = c__sum0;
+        }
+    }
+    return std::make_tuple((PyObject *)pdensity, sdensity, cdensity);
+}
+
+void sighandler(int sig);
+#include <signal.h>
+extern "C" {
+    PyObject * create_signature;
+    struct sigaction slot;
+    PyObject * set_create_signature(PyObject * self, PyObject * args) {
+        if (!PyArg_ParseTuple(args, "O", &create_signature)) {
+            PyErr_SetString(PyExc_ValueError, "Invalid Argument to set_create_signature!");
+            return NULL;
+        }
+        Py_INCREF(create_signature);
+        memset(&slot, 0, sizeof(slot));
+        slot.sa_handler = &sighandler;
+        sigaction(SIGSEGV, &slot, NULL);
+        sigaction(SIGBUS, &slot, NULL);
+        Py_INCREF(Py_None);
+        return Py_None;
+    }
+    PyObject * run(PyObject * self, PyObject * args) {
+        {
+            PyObj pdensity;
+            PyObj pdims;
+            PyObj pcenter;
+            PyObj pw2D;
+            PyObject * pr50; npy_int64 cr50;
+            PyObject * pb; npy_double cb;
+            PyObject * pa; npy_double ca;
+            if (
+                PyTuple_CheckExact(args) and PyTuple_GET_SIZE(args) == 7
+                and (pdensity = PyTuple_GET_ITEM(args, 0)) and PyArray_CheckExact(pdensity)
+                and PyArray_TYPE((PyArrayObject *)pdensity) == NPY_FLOAT32 and PyArray_NDIM((PyArrayObject *)pdensity) == 2
+                and (pdims = PyTuple_GET_ITEM(args, 1)) and PyArray_CheckExact(pdims)
+                and PyArray_TYPE((PyArrayObject *)pdims) == NPY_INT64 and PyArray_NDIM((PyArrayObject *)pdims) == 1
+                and (pcenter = PyTuple_GET_ITEM(args, 2)) and PyArray_CheckExact(pcenter)
+                and PyArray_TYPE((PyArrayObject *)pcenter) == NPY_FLOAT64 and PyArray_NDIM((PyArrayObject *)pcenter) == 1
+                and (pw2D = PyTuple_GET_ITEM(args, 3)) and PyArray_CheckExact(pw2D)
+                and PyArray_TYPE((PyArrayObject *)pw2D) == NPY_FLOAT32 and PyArray_NDIM((PyArrayObject *)pw2D) == 2
+                and (pr50 = PyTuple_GET_ITEM(args, 4)) and PyLong_CheckExact(pr50)
+                and (pb = PyTuple_GET_ITEM(args, 5)) and PyFloat_CheckExact(pb)
+                and (pa = PyTuple_GET_ITEM(args, 6)) and PyArray_IsScalar(pa, Double)
+            ) {
+                if (!(pdensity.incref((PyObject *)PyArray_GETCONTIGUOUS((PyArrayObject *)pdensity)))) {
+                    PyErr_SetString(PyExc_ValueError, "Invalid Argument type on density!");
+                    return NULL;
+                }
+                if (!(pdims.incref((PyObject *)PyArray_GETCONTIGUOUS((PyArrayObject *)pdims)))) {
+                    PyErr_SetString(PyExc_ValueError, "Invalid Argument type on dims!");
+                    return NULL;
+                }
+                if (!(pcenter.incref((PyObject *)PyArray_GETCONTIGUOUS((PyArrayObject *)pcenter)))) {
+                    PyErr_SetString(PyExc_ValueError, "Invalid Argument type on center!");
+                    return NULL;
+                }
+                if (!(pw2D.incref((PyObject *)PyArray_GETCONTIGUOUS((PyArrayObject *)pw2D)))) {
+                    PyErr_SetString(PyExc_ValueError, "Invalid Argument type on w2D!");
+                    return NULL;
+                }
+                cr50 = PyLong_AS_LONG(pr50);
+                cb = PyFloat_AS_DOUBLE(pb);
+                ca = PyArrayScalar_VAL(pa, Double);
+                try {
+                    PyObject * res = std::get<0>(pdf_f2l1d1f2JDd(
+                          pdensity, PyArray_SHAPE((PyArrayObject *)pdensity), (npy_float *)PyArray_DATA((PyArrayObject *)pdensity)
+                        , pdims, PyArray_SHAPE((PyArrayObject *)pdims), (npy_int64 *)PyArray_DATA((PyArrayObject *)pdims)
+                        , pcenter, PyArray_SHAPE((PyArrayObject *)pcenter), (npy_double *)PyArray_DATA((PyArrayObject *)pcenter)
+                        , pw2D, PyArray_SHAPE((PyArrayObject *)pw2D), (npy_float *)PyArray_DATA((PyArrayObject *)pw2D)
+                        , cr50
+                        , cb
+                        , ca
+                    ));
+
+                    Py_INCREF(res);
+                    return res;
+                } catch (...) {
+                    return NULL;
+                }
+            } else
+                PyErr_Clear();
+        }
+        PyObject * signatures = Py_BuildValue("(sO)", "(lp0\n(lp1\nccopy_reg\n_reconstructor\np2\n(chope._ast\nVariable\np3\nc__builtin__\nobject\np4\nNtp5\nRp6\n(dp7\nS'name'\np8\nS'density'\np9\nsS'dtype'\np10\nS'float32'\np11\nsS'dims'\np12\nI2\nsbag2\n(g3\ng4\nNtp13\nRp14\n(dp15\ng8\ng12\nsg10\nS'int64'\np16\nsg12\nI1\nsbag2\n(g3\ng4\nNtp17\nRp18\n(dp19\ng8\nS'center'\np20\nsg10\nS'float64'\np21\nsg12\nI1\nsbag2\n(g3\ng4\nNtp22\nRp23\n(dp24\ng8\nS'w2D'\np25\nsg10\ng11\nsg12\nI2\nsbag2\n(g3\ng4\nNtp26\nRp27\n(dp28\ng8\nS'r50'\np29\nsg10\nc__builtin__\nint\np30\nsg12\nI0\nsbag2\n(g3\ng4\nNtp31\nRp32\n(dp33\ng8\nS'b'\np34\nsg10\nc__builtin__\nfloat\np35\nsg12\nI0\nsbag2\n(g3\ng4\nNtp36\nRp37\n(dp38\ng8\nS'a'\np39\nsg10\ng21\nsg12\nI0\nsbaa.", args);
+        if (!signatures) {
+            PyErr_SetString(PyExc_ValueError, "Error building signature string for pdf");
+            return NULL;
+        }
+        return PyObject_Call(create_signature, signatures, NULL);
+    }
+
+    PyMethodDef pdfMethods[] = {
+        { "set_create_signature", set_create_signature, METH_VARARGS, "signal handler" },
+        { "run", run, METH_VARARGS, "module function" },
+        { NULL, NULL, 0, NULL }
+    };
+
+    static struct PyModuleDef pdfmodule = {
+        PyModuleDef_HEAD_INIT,
+        "pdf",
+        NULL,
+        -1,
+        pdfMethods
+    };
+
+   PyMODINIT_FUNC PyInit_pdf(void) {
+        import_array();
+        PyImport_ImportModule("numpy");
+        return PyModule_Create(&pdfmodule);
+   }
+}
+#include <string>
+#include <sstream>
+#include <iostream>
+#include <cxxabi.h>
+#include <execinfo.h>
+void sighandler(int sig) {
+    std::ostringstream buffer;
+    buffer << "Abort by " << (sig == SIGSEGV ? "segfault" : "bus error") << std::endl;
+    void * stack[64];
+    std::size_t depth = backtrace(stack, 64);
+    if (!depth)
+        buffer << "  <empty stacktrace, possibly corrupt>" << std::endl;
+    else {
+        char ** symbols = backtrace_symbols(stack, depth);
+        for (std::size_t i = 1; i < depth; ++i) {
+            std::string symbol = symbols[i];
+                if (symbol.find_first_of(' ', 59) != std::string::npos) {
+                    std::string name = symbol.substr(59, symbol.find_first_of(' ', 59) - 59);
+                    int status;
+                    char * demangled = abi::__cxa_demangle(name.c_str(), NULL, NULL, &status);
+                    if (!status) {
+                        buffer << "    "
+                            << symbol.substr(0, 59)
+                            << demangled
+                            << symbol.substr(59 + name.size())
+                            << std::endl;
+                        free(demangled);
+                    } else
+                        buffer << "    " << symbol << std::endl;
+                } else
+                    buffer << "    " << symbol << std::endl;
+            }
+            free(symbols);
+        }
+        std::cerr << buffer.str();
+        std::exit(EXIT_FAILURE);
+    }
+
+ +
+
+
+ +
+
+ + +
+ +
+ + +
+
Overwriting src/pdf.cpp
+
+
+
+ +
+
+ +
+
+
+
+
+

Python helper module

+
+
+
+
+
+
In [15]:
+
+
+
%%file native_util.py
+import importlib
+import os
+import glob
+import sys
+
+from numpy.distutils.misc_util import get_numpy_include_dirs
+import setuptools
+from os import listdir
+# import tempfile
+from hope import config
+
+try:
+    # python 3
+    import io
+    file = io.IOBase
+except ImportError:
+    pass
+
+
+def load(name):
+    compile(name, "./src")
+    module = importlib.import_module(name)
+    # module = __import__(name, globals(), locals(), [], -1)
+    return module
+
+
+
+def compile(name, src_folder, target_folder = "./"):
+    localfilename =os.path.join(src_folder, name)
+    
+    outfile, stdout, stderr, argv = None, None, None, sys.argv
+    try:
+        sys.stdout.flush(), sys.stderr.flush()
+        outpath = os.path.join(target_folder, "{0}.out".format(localfilename))
+        if os.path.exists(outpath):
+            os.remove(outpath)
+            
+        so_path = os.path.join(target_folder, "{0}.so".format(name))
+        if os.path.exists(so_path):
+            os.remove(so_path)
+            
+        outfile = open(outpath, 'w')
+        
+        try:
+            sys.stdout.fileno()
+            sys.stderr.fileno()
+            have_fileno = True
+        except OSError:
+            have_fileno = False
+        
+        if have_fileno and isinstance(sys.stdout, file) and  isinstance(sys.stdout, file):
+            stdout, stderr = os.dup(sys.stdout.fileno()), os.dup(sys.stderr.fileno())
+            os.dup2(outfile.fileno(), sys.stdout.fileno())
+            os.dup2(outfile.fileno(), sys.stderr.fileno())
+        else:
+            stdout, stderr = sys.stdout, sys.stderr
+            sys.stdout, sys.stderr = outfile, outfile
+        try:
+            sources = "./{0}.cpp".format(localfilename)
+            sys.argv = ["", "build_ext",
+                        "-b", target_folder,  #--build-lib (-b)     directory for compiled extension modules
+                        "-t", "." #--build-temp - a rel path will result in a dir structure of -b at the cur position 
+                        ]
+    
+            localfilename = str(localfilename)
+            sources = str(sources)
+    
+            setuptools.setup( \
+                  name = name\
+                , ext_modules = [setuptools.Extension( \
+                      name \
+                    , sources = [sources] \
+                    , extra_compile_args = config.cxxflags \
+                  )] \
+                , include_dirs = get_numpy_include_dirs() \
+            )
+        except SystemExit as e:
+            print(sys.stderr.write(str(e)))
+        sys.stdout.flush(), sys.stderr.flush()
+    finally:
+        if isinstance(stdout, int):
+            os.dup2(stdout, sys.stdout.fileno()), os.close(stdout)
+        elif not stdout is None:
+            sys.stdout = stdout
+        if isinstance(stderr, int):
+            os.dup2(stderr, sys.stderr.fileno()), os.close(stderr)
+        elif not stderr is None:
+            sys.stderr = stderr
+        if isinstance(outfile, file):
+            outfile.close()
+        sys.argv = argv
+    
+    with open(outpath) as outfile:
+        out = outfile.read()
+    
+    modules = glob.glob(os.path.join(target_folder, "{}*.so".format(name)))
+        
+    if not modules or out.find("error:") > -1:
+        print(out)
+        raise Exception("Error compiling function {0} (compiled to {1})".format(localfilename, target_folder))
+    
+    if out.find("warning:") > -1:
+        import warnings
+        warnings.warn("A warning has been issued during compilation:\n%s"%out)
+    
+    print(out)
+
+
+def compile_all():
+    src_folder = "./src"
+    func_names = (src_file.split(".cpp")[0] for src_file in listdir(src_folder) if src_file.endswith(".cpp"))
+    for func_name in func_names:
+        compile(func_name, src_folder)
+    
+    
+if __name__ == '__main__':
+    compile_all()
+
+ +
+
+
+ +
+
+ + +
+ +
+ + +
+
Overwriting native_util.py
+
+
+
+ +
+
+ +
+
+
+
In [16]:
+
+
+
%%file util.py
+# Copyright (C) 2014 ETH Zurich, Institute for Astronomy
+
+'''
+Created on Aug 4, 2014
+
+author: jakeret
+'''
+from __future__ import print_function, division, absolute_import, unicode_literals
+import math
+
+def perf_comp_data(func_list, data_list, rep=5, number=1, extra_setup=None):
+    ''' Function to compare the performance of different functions.
+    
+    Parameters
+    ==========
+    func_list : list
+        list with function names as strings
+    data_list : list
+        list with data set names as strings
+    rep : int
+        number of repetitions of the whole comparison
+    number : int
+        number of executions for every function
+    '''
+    from timeit import repeat
+    res_list = {}
+    for name in enumerate(func_list):
+        if data_list is None:
+            stmt = "%s()"%(name[1])
+            setup = "from __main__ import %s"%(name[1]) 
+        else:
+            stmt = "%s(%s)"%(name[1], data_list[name[0]])
+            setup = "from __main__ import %s, %s"%(name[1], data_list[name[0]])
+        if extra_setup is not None:
+            stmt = extra_setup + "; " + stmt
+              
+        results = repeat(stmt=stmt, setup=setup, repeat=rep, number=number)
+        
+        res_list[name[1]] = (median(results), min(results))
+        
+#     res_sort = sorted(res_list.iteritems(), key=lambda (k, v): (v, k))
+    res_sort = sorted(iter(res_list.items()), key=lambda k_v: (k_v[1], k_v[0]))
+    for func, (av_time, min_time) in res_sort:
+        rel = av_time / res_sort[0][1][0]
+        print('function: {0!s:20}, av. time sec: {1:>12.8f}, min. time sec: {2:>12.8f}, relative: {3:>9.1f}'.format(func, av_time, min_time, rel))
+
+def median(x):
+    s_x = sorted(x)
+    return (s_x[int(math.floor((len(s_x)-1)/2))] + s_x[int(math.ceil((len(s_x)-1)/2))])/2
+
+ +
+
+
+ +
+
+ + +
+ +
+ + +
+
Overwriting util.py
+
+
+
+ +
+
+ +
+
+
+ + + + + + diff --git a/benchmarks/native_cpp_gen.ipynb b/benchmarks/native_cpp_gen.ipynb index 2c761c1..a66e17e 100644 --- a/benchmarks/native_cpp_gen.ipynb +++ b/benchmarks/native_cpp_gen.ipynb @@ -64,7 +64,7 @@ ] }, "text/html": [ - "
SoftwareVersion
Python3.5.1 64bit [GCC 4.2.1 (Apple Inc. build 5666) (dot 3)]
IPython6.1.0
OSDarwin 15.6.0 x86_64 i386 64bit
Tue Aug 29 17:03:58 2017 CEST
" + "
SoftwareVersion
Python3.5.1 64bit [GCC 4.2.1 (Apple Inc. build 5666) (dot 3)]
IPython6.1.0
OSDarwin 15.6.0 x86_64 i386 64bit
Mon Sep 04 16:11:03 2017 CEST
" ], "text/latex": [ "\\begin{tabular}{|l|l|}\\hline\n", @@ -72,7 +72,7 @@ "Python & 3.5.1 64bit [GCC 4.2.1 (Apple Inc. build 5666) (dot 3)] \\\\ \\hline\n", "IPython & 6.1.0 \\\\ \\hline\n", "OS & Darwin 15.6.0 x86\\_64 i386 64bit \\\\ \\hline\n", - "\\hline \\multicolumn{2}{|l|}{Tue Aug 29 17:03:58 2017 CEST} \\\\ \\hline\n", + "\\hline \\multicolumn{2}{|l|}{Mon Sep 04 16:11:03 2017 CEST} \\\\ \\hline\n", "\\end{tabular}\n" ], "text/plain": [ @@ -80,7 +80,7 @@ "Python 3.5.1 64bit [GCC 4.2.1 (Apple Inc. build 5666) (dot 3)]\n", "IPython 6.1.0\n", "OS Darwin 15.6.0 x86_64 i386 64bit\n", - "Tue Aug 29 17:03:58 2017 CEST" + "Mon Sep 04 16:11:03 2017 CEST" ] }, "execution_count": 2, @@ -110,29 +110,21 @@ "cell_type": "code", "execution_count": 3, "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "mkdir: src: File exists\r\n" - ] - } - ], + "outputs": [], "source": [ - "!mkdir src" + "!mkdir -p src" ] }, { "cell_type": "code", - "execution_count": 17, + "execution_count": 4, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ - "Overwriting src/fib.cpp\n" + "Writing src/fib.cpp\n" ] } ], @@ -310,7 +302,7 @@ "name": "stdout", "output_type": "stream", "text": [ - "Overwriting src/qsort_kernel.cpp\n" + "Writing src/qsort_kernel.cpp\n" ] } ], @@ -524,7 +516,7 @@ "name": "stdout", "output_type": "stream", "text": [ - "Overwriting src/pisum.cpp\n" + "Writing src/pisum.cpp\n" ] } ], @@ -675,7 +667,7 @@ "name": "stdout", "output_type": "stream", "text": [ - "Overwriting src/pisum_opt.cpp\n" + "Writing src/pisum_opt.cpp\n" ] } ], @@ -837,7 +829,7 @@ "name": "stdout", "output_type": "stream", "text": [ - "Overwriting src/ln.cpp\n" + "Writing src/ln.cpp\n" ] } ], @@ -857,98 +849,111 @@ "#include \n", "#include \n", "struct PyObj {\n", - "\ttypedef PyObject * ptr_t;\n", - "\ttypedef PyArrayObject * arrptr_t;\n", - "\tPyObj(): dec(false), ptr(NULL) {}\n", - "\tPyObj(ptr_t p): dec(false), ptr(p) {}\n", - "\t~PyObj() { if(dec) Py_DECREF(ptr); }\n", - "\tPyObj & operator=(ptr_t p) { if(dec) Py_DECREF(ptr); ptr = p; dec = false; return *this; }\n", - "\tPyObj & incref(ptr_t p) { if(dec) Py_DECREF(ptr); ptr = p; dec = (p != NULL); return *this; }\n", - "\toperator bool() const { return ptr; }\n", - "\toperator ptr_t() const { return ptr; }\n", - "\toperator arrptr_t() const { return (arrptr_t)ptr; }\n", - "\tbool dec;\n", - "\tptr_t ptr;\n", + " typedef PyObject * ptr_t;\n", + " typedef PyArrayObject * arrptr_t;\n", + " PyObj(): dec(false), ptr(NULL) {}\n", + " PyObj(ptr_t p): dec(false), ptr(p) {}\n", + " ~PyObj() { if(dec) Py_DECREF(ptr); }\n", + " PyObj & operator=(ptr_t p) { if(dec) Py_DECREF(ptr); ptr = p; dec = false; return *this; }\n", + " PyObj & incref(ptr_t p) { if(dec) Py_DECREF(ptr); ptr = p; dec = (p != NULL); return *this; }\n", + " operator bool() const { return ptr; }\n", + " operator ptr_t() const { return ptr; }\n", + " operator arrptr_t() const { return (arrptr_t)ptr; }\n", + " bool dec;\n", + " ptr_t ptr;\n", "};\n", "\n", - "inline void ln_hope_d1d1(PyObject * pX, npy_intp const * __restrict__ sX, npy_double * __restrict__ cX, \n", - "\t\t\t\t\t\t\t\t\t\t\t\t\tPyObject * pY, npy_intp const * __restrict__ sY, npy_double * __restrict__ cY);\n", + "inline void ln_hope_d1d1(PyObject * pX, npy_intp const * __restrict__ sX, npy_double * __restrict__ cX,\n", + " PyObject * pY, npy_intp const * __restrict__ sY, npy_double * __restrict__ cY);\n", "\n", - "inline void ln_hope_d1d1(PyObject * pX, npy_intp const * __restrict__ sX, npy_double * __restrict__ cX, \n", - "\t\t\t\t\t\t\t\t\t\t\t\t\tPyObject * pY, npy_intp const * __restrict__ sY, npy_double * __restrict__ cY){\n", - "\t\n", - "\tfor (npy_intp i0 = 0; i0 < sY[0] - 0; ++i0) {\n", - "\t\tcY[(int)(i0)] = (cX[i0] - 1) - (std::pow((cX[i0] - 1), 2) / 2) + (std::pow((cX[i0] - 1), 3) / 3) - (std::pow((cX[i0] - 1), 4) / 4) + (std::pow((cX[i0] - 1), 5) / 5) - (std::pow((cX[i0] - 1), 6) / 6) + (std::pow((cX[i0] - 1), 7) / 7) - (std::pow((cX[i0] - 1), 8) / 8) + (std::pow((cX[i0] - 1), 9) / 9);\n", - "\t}\n", + "inline void ln_hope_d1d1(PyObject * pX, npy_intp const * __restrict__ sX, npy_double * __restrict__ cX,\n", + " PyObject * pY, npy_intp const * __restrict__ sY, npy_double * __restrict__ cY){\n", + "\n", + " for (npy_intp i0 = 0; i0 < sY[0] - 0; ++i0) {\n", + " cY[(int)(i0)] = (cX[i0] - 1) - (std::pow((cX[i0] - 1), 2) / 2) + (std::pow((cX[i0] - 1), 3) / 3) - (std::pow((cX[i0] - 1), 4) / 4) + (std::pow((cX[i0] - 1), 5) / 5) - (std::pow((cX[i0] - 1), 6) / 6) + (std::pow((cX[i0] - 1), 7) / 7) - (std::pow((cX[i0] - 1), 8) / 8) + (std::pow((cX[i0] - 1), 9) / 9);\n", + " }\n", "}\n", "\n", "void sighandler(int sig);\n", "#include \n", "extern \"C\" {\n", - "\tPyObject * create_signature;\n", - "\tstruct sigaction slot;\n", - "\tPyObject * set_create_signature(PyObject * self, PyObject * args) {\n", - "\t\tif (!PyArg_ParseTuple(args, \"O\", &create_signature)) {\n", - "\t\t\tPyErr_SetString(PyExc_ValueError, \"Invalid Argument to set_create_signature!\");\n", - "\t\t\treturn NULL;\n", - "\t\t}\n", - "\t\tPy_INCREF(create_signature);\n", - "\t\tmemset(&slot, 0, sizeof(slot));\n", - "\t\tslot.sa_handler = &sighandler;\n", - "\t\tsigaction(SIGSEGV, &slot, NULL);\n", - "\t\tsigaction(SIGBUS, &slot, NULL);\n", - "\t\tPy_INCREF(Py_None);\n", - "\t\treturn Py_None;\n", - "\t}\n", - "\tPyObject * run(PyObject * self, PyObject * args) {\n", - "\t\t{\n", - "\t\t\tPyObj pX;\n", - "\t\t\tPyObj pY;\n", - "\t\t\tif (\n", - "\t\t\t\tPyTuple_CheckExact(args) and PyTuple_GET_SIZE(args) == 2\n", - "\t\t\t\tand (pX = PyTuple_GET_ITEM(args, 0)) and PyArray_CheckExact(pX)\n", - "\t\t\t\tand PyArray_TYPE((PyArrayObject *)pX) == NPY_FLOAT64 and PyArray_NDIM((PyArrayObject *)pX) == 1\n", - "\t\t\t\tand (pY = PyTuple_GET_ITEM(args, 1)) and PyArray_CheckExact(pY)\n", - "\t\t\t\tand PyArray_TYPE((PyArrayObject *)pY) == NPY_FLOAT64 and PyArray_NDIM((PyArrayObject *)pY) == 1\n", - "\t\t\t) {\n", - "\t\t\t\tif (!(pX.incref((PyObject *)PyArray_GETCONTIGUOUS((PyArrayObject *)pX)))) {\n", - "\t\t\t\t\tPyErr_SetString(PyExc_ValueError, \"Invalid Argument type on X!\");\n", - "\t\t\t\t\treturn NULL;\n", - "\t\t\t\t}\n", - "\t\t\t\tif (!(pY.incref((PyObject *)PyArray_GETCONTIGUOUS((PyArrayObject *)pY)))) {\n", - "\t\t\t\t\tPyErr_SetString(PyExc_ValueError, \"Invalid Argument type on Y!\");\n", - "\t\t\t\t\treturn NULL;\n", - "\t\t\t\t}\n", - "\t\t\t\ttry {\n", - "\t\t\t\t\tln_hope_d1d1(\n", - "\t\t\t\t\t\t pX, PyArray_SHAPE((PyArrayObject *)pX), (npy_double *)PyArray_DATA((PyArrayObject *)pX)\n", - "\t\t\t\t\t\t, pY, PyArray_SHAPE((PyArrayObject *)pY), (npy_double *)PyArray_DATA((PyArrayObject *)pY)\n", - "\t\t\t\t\t);\n", - "\t\t\t\t\tPy_INCREF(Py_None);\n", - "\t\t\t\t\treturn Py_None;\n", - "\t\t\t\t} catch (...) {\n", - "\t\t\t\t\treturn NULL;\n", - "\t\t\t\t}\n", - "\t\t\t} else\n", - "\t\t\t\tPyErr_Clear();\n", - "\t\t}\n", - "\t\tPyObject * signatures = Py_BuildValue(\"(sO)\", \"(lp0\\n(lp1\\nccopy_reg\\n_reconstructor\\np2\\n(chope._ast\\nVariable\\np3\\nc__builtin__\\nobject\\np4\\nNtp5\\nRp6\\n(dp7\\nS'name'\\np8\\nS'X'\\np9\\nsS'dtype'\\np10\\nS'float64'\\np11\\nsS'dims'\\np12\\nI1\\nsbag2\\n(g3\\ng4\\nNtp13\\nRp14\\n(dp15\\ng8\\nS'Y'\\np16\\nsg10\\ng11\\nsg12\\nI1\\nsbaa.\", args);\n", - "\t\tif (!signatures) {\n", - "\t\t\tPyErr_SetString(PyExc_ValueError, \"Error building signature string for ln_hope\");\n", - "\t\t\treturn NULL;\n", - "\t\t}\n", - "\t\treturn PyObject_Call(create_signature, signatures, NULL);\n", - "\t}\n", - "\tPyMethodDef ln_hopeMethods[] = {\n", - "\t\t{ \"set_create_signature\", (PyCFunction)set_create_signature, METH_VARARGS },\n", - "\t\t{ \"run\", (PyCFunction)run, METH_VARARGS },\n", - "\t\t{ NULL, NULL }\n", - "\t};\n", - "\tPyMODINIT_FUNC initln(void) {\n", - "\t\timport_array();\n", - "\t\tPyImport_ImportModule(\"numpy\");\n", - "\t\t(void)Py_InitModule(\"ln\", ln_hopeMethods);\n", - "\t}\n", + " PyObject * create_signature;\n", + " struct sigaction slot;\n", + " PyObject * set_create_signature(PyObject * self, PyObject * args) {\n", + " if (!PyArg_ParseTuple(args, \"O\", &create_signature)) {\n", + " PyErr_SetString(PyExc_ValueError, \"Invalid Argument to set_create_signature!\");\n", + " return NULL;\n", + " }\n", + " Py_INCREF(create_signature);\n", + " memset(&slot, 0, sizeof(slot));\n", + " slot.sa_handler = &sighandler;\n", + " sigaction(SIGSEGV, &slot, NULL);\n", + " sigaction(SIGBUS, &slot, NULL);\n", + " Py_INCREF(Py_None);\n", + " return Py_None;\n", + " }\n", + " PyObject * run(PyObject * self, PyObject * args) {\n", + " {\n", + " PyObj pX;\n", + " PyObj pY;\n", + " if (\n", + " PyTuple_CheckExact(args) and PyTuple_GET_SIZE(args) == 2\n", + " and (pX = PyTuple_GET_ITEM(args, 0)) and PyArray_CheckExact(pX)\n", + " and PyArray_TYPE((PyArrayObject *)pX) == NPY_FLOAT64 and PyArray_NDIM((PyArrayObject *)pX) == 1\n", + " and (pY = PyTuple_GET_ITEM(args, 1)) and PyArray_CheckExact(pY)\n", + " and PyArray_TYPE((PyArrayObject *)pY) == NPY_FLOAT64 and PyArray_NDIM((PyArrayObject *)pY) == 1\n", + " ) {\n", + " if (!(pX.incref((PyObject *)PyArray_GETCONTIGUOUS((PyArrayObject *)pX)))) {\n", + " PyErr_SetString(PyExc_ValueError, \"Invalid Argument type on X!\");\n", + " return NULL;\n", + " }\n", + " if (!(pY.incref((PyObject *)PyArray_GETCONTIGUOUS((PyArrayObject *)pY)))) {\n", + " PyErr_SetString(PyExc_ValueError, \"Invalid Argument type on Y!\");\n", + " return NULL;\n", + " }\n", + " try {\n", + " ln_hope_d1d1(\n", + " pX, PyArray_SHAPE((PyArrayObject *)pX), (npy_double *)PyArray_DATA((PyArrayObject *)pX)\n", + " , pY, PyArray_SHAPE((PyArrayObject *)pY), (npy_double *)PyArray_DATA((PyArrayObject *)pY)\n", + " );\n", + " Py_INCREF(Py_None);\n", + " return Py_None;\n", + " } catch (...) {\n", + " return NULL;\n", + " }\n", + " } else\n", + " PyErr_Clear();\n", + " }\n", + " PyObject * signatures = Py_BuildValue(\"(sO)\", \"(lp0\\n(lp1\\nccopy_reg\\n_reconstructor\\np2\\n(chope._ast\\nVariable\\np3\\nc__builtin__\\nobject\\np4\\nNtp5\\nRp6\\n(dp7\\nS'name'\\np8\\nS'X'\\np9\\nsS'dtype'\\np10\\nS'float64'\\np11\\nsS'dims'\\np12\\nI1\\nsbag2\\n(g3\\ng4\\nNtp13\\nRp14\\n(dp15\\ng8\\nS'Y'\\np16\\nsg10\\ng11\\nsg12\\nI1\\nsbaa.\", args);\n", + " if (!signatures) {\n", + " PyErr_SetString(PyExc_ValueError, \"Error building signature string for ln_hope\");\n", + " return NULL;\n", + " }\n", + " return PyObject_Call(create_signature, signatures, NULL);\n", + " }\n", + "\n", + " PyMethodDef lnMethods[] = {\n", + " { \"set_create_signature\", set_create_signature, METH_VARARGS, \"signal handler\" },\n", + " { \"run\", run, METH_VARARGS, \"module function\" },\n", + " { NULL, NULL, 0, NULL }\n", + " };\n", + "\n", + "\n", + " static struct PyModuleDef lnmodule = {\n", + " PyModuleDef_HEAD_INIT,\n", + " \"ln\",\n", + " NULL,\n", + " -1,\n", + " lnMethods\n", + " };\n", + "\n", + "\n", + "\n", + " PyMODINIT_FUNC PyInit_ln(void) {\n", + " import_array();\n", + " PyImport_ImportModule(\"numpy\");\n", + " return PyModule_Create(&lnmodule);\n", + " }\n", "}\n", "#include \n", "#include \n", @@ -956,37 +961,37 @@ "#include \n", "#include \n", "void sighandler(int sig) {\n", - "\tstd::ostringstream buffer;\n", - "\tbuffer << \"Abort by \" << (sig == SIGSEGV ? \"segfault\" : \"bus error\") << std::endl;\n", - "\tvoid * stack[64];\n", - "\tstd::size_t depth = backtrace(stack, 64);\n", - "\tif (!depth)\n", - "\t\tbuffer << \" \" << std::endl;\n", - "\telse {\n", - "\t\tchar ** symbols = backtrace_symbols(stack, depth);\n", - "\t\tfor (std::size_t i = 1; i < depth; ++i) {\n", - "\t\t\tstd::string symbol = symbols[i];\n", - "\t\t\t\tif (symbol.find_first_of(' ', 59) != std::string::npos) {\n", - "\t\t\t\t\tstd::string name = symbol.substr(59, symbol.find_first_of(' ', 59) - 59);\n", - "\t\t\t\t\tint status;\n", - "\t\t\t\t\tchar * demangled = abi::__cxa_demangle(name.c_str(), NULL, NULL, &status);\n", - "\t\t\t\t\tif (!status) {\n", - "\t\t\t\t\t\tbuffer << \" \" \n", - "\t\t\t\t\t\t\t<< symbol.substr(0, 59) \n", - "\t\t\t\t\t\t\t<< demangled\n", - "\t\t\t\t\t\t\t<< symbol.substr(59 + name.size())\n", - "\t\t\t\t\t\t\t<< std::endl;\n", - "\t\t\t\t\t\tfree(demangled);\n", - "\t\t\t\t\t} else\n", - "\t\t\t\t\t\tbuffer << \" \" << symbol << std::endl;\n", - "\t\t\t\t} else\n", - "\t\t\t\t\tbuffer << \" \" << symbol << std::endl;\n", - "\t\t\t}\n", - "\t\t\tfree(symbols);\n", - "\t\t}\n", - "\t\tstd::cerr << buffer.str();\n", - "\t\tstd::exit(EXIT_FAILURE);\n", - "\t}\n" + " std::ostringstream buffer;\n", + " buffer << \"Abort by \" << (sig == SIGSEGV ? \"segfault\" : \"bus error\") << std::endl;\n", + " void * stack[64];\n", + " std::size_t depth = backtrace(stack, 64);\n", + " if (!depth)\n", + " buffer << \" \" << std::endl;\n", + " else {\n", + " char ** symbols = backtrace_symbols(stack, depth);\n", + " for (std::size_t i = 1; i < depth; ++i) {\n", + " std::string symbol = symbols[i];\n", + " if (symbol.find_first_of(' ', 59) != std::string::npos) {\n", + " std::string name = symbol.substr(59, symbol.find_first_of(' ', 59) - 59);\n", + " int status;\n", + " char * demangled = abi::__cxa_demangle(name.c_str(), NULL, NULL, &status);\n", + " if (!status) {\n", + " buffer << \" \"\n", + " << symbol.substr(0, 59)\n", + " << demangled\n", + " << symbol.substr(59 + name.size())\n", + " << std::endl;\n", + " free(demangled);\n", + " } else\n", + " buffer << \" \" << symbol << std::endl;\n", + " } else\n", + " buffer << \" \" << symbol << std::endl;\n", + " }\n", + " free(symbols);\n", + " }\n", + " std::cerr << buffer.str();\n", + " std::exit(EXIT_FAILURE);\n", + " }\n" ] }, { @@ -998,7 +1003,7 @@ "name": "stdout", "output_type": "stream", "text": [ - "Overwriting src/ln_exp.cpp\n" + "Writing src/ln_exp.cpp\n" ] } ], @@ -1018,103 +1023,115 @@ "#include \n", "#include \n", "struct PyObj {\n", - "\ttypedef PyObject * ptr_t;\n", - "\ttypedef PyArrayObject * arrptr_t;\n", - "\tPyObj(): dec(false), ptr(NULL) {}\n", - "\tPyObj(ptr_t p): dec(false), ptr(p) {}\n", - "\t~PyObj() { if(dec) Py_DECREF(ptr); }\n", - "\tPyObj & operator=(ptr_t p) { if(dec) Py_DECREF(ptr); ptr = p; dec = false; return *this; }\n", - "\tPyObj & incref(ptr_t p) { if(dec) Py_DECREF(ptr); ptr = p; dec = (p != NULL); return *this; }\n", - "\toperator bool() const { return ptr; }\n", - "\toperator ptr_t() const { return ptr; }\n", - "\toperator arrptr_t() const { return (arrptr_t)ptr; }\n", - "\tbool dec;\n", - "\tptr_t ptr;\n", + " typedef PyObject * ptr_t;\n", + " typedef PyArrayObject * arrptr_t;\n", + " PyObj(): dec(false), ptr(NULL) {}\n", + " PyObj(ptr_t p): dec(false), ptr(p) {}\n", + " ~PyObj() { if(dec) Py_DECREF(ptr); }\n", + " PyObj & operator=(ptr_t p) { if(dec) Py_DECREF(ptr); ptr = p; dec = false; return *this; }\n", + " PyObj & incref(ptr_t p) { if(dec) Py_DECREF(ptr); ptr = p; dec = (p != NULL); return *this; }\n", + " operator bool() const { return ptr; }\n", + " operator ptr_t() const { return ptr; }\n", + " operator arrptr_t() const { return (arrptr_t)ptr; }\n", + " bool dec;\n", + " ptr_t ptr;\n", "};\n", "\n", - "inline void ln_hope_exp_d1d1(PyObject * pX, npy_intp const * __restrict__ sX, npy_double * __restrict__ cX, \n", - "\t\t\t\t\t\t\t\t\t\t\t\t\tPyObject * pY, npy_intp const * __restrict__ sY, npy_double * __restrict__ cY);\n", + "inline void ln_hope_exp_d1d1(PyObject * pX, npy_intp const * __restrict__ sX, npy_double * __restrict__ cX,\n", + " PyObject * pY, npy_intp const * __restrict__ sY, npy_double * __restrict__ cY);\n", "\n", - "inline void ln_hope_exp_d1d1(PyObject * pX, npy_intp const * __restrict__ sX, npy_double * __restrict__ cX, \n", - "\t\t\t\t\t\t\t\t\t\t\t\t\tPyObject * pY, npy_intp const * __restrict__ sY, npy_double * __restrict__ cY){\n", + "inline void ln_hope_exp_d1d1(PyObject * pX, npy_intp const * __restrict__ sX, npy_double * __restrict__ cX,\n", + " PyObject * pY, npy_intp const * __restrict__ sY, npy_double * __restrict__ cY){\n", "\n", - "\tfor (npy_intp i0 = 0; i0 < sX[0] - 0; ++i0) {\n", - "\t\tauto cx = (cX[i0] - 1);\n", - "\t\tauto cx2 = (cx * cx);\n", - "\t\tauto cx4 = (cx2 * cx2);\n", - "\t\tauto cx6 = (cx4 * cx2);\n", - "\t\tauto cx8 = (cx4 * cx4);\n", - "\t\tcY[i0] = cx - (cx2 / 2) + (cx * cx2 / 3) - (cx4 / 4) + (cx * cx4 / 5) - (cx6 / 6) + (cx6 * cx / 7) - (cx8 / 8) + (cx8 * cx / 9);\n", - "\t}\n", + " for (npy_intp i0 = 0; i0 < sX[0] - 0; ++i0) {\n", + " auto cx = (cX[i0] - 1);\n", + " auto cx2 = (cx * cx);\n", + " auto cx4 = (cx2 * cx2);\n", + " auto cx6 = (cx4 * cx2);\n", + " auto cx8 = (cx4 * cx4);\n", + " cY[i0] = cx - (cx2 / 2) + (cx * cx2 / 3) - (cx4 / 4) + (cx * cx4 / 5) - (cx6 / 6) + (cx6 * cx / 7) - (cx8 / 8) + (cx8 * cx / 9);\n", + " }\n", "}\n", "\n", "void sighandler(int sig);\n", "#include \n", "extern \"C\" {\n", - "\tPyObject * create_signature;\n", - "\tstruct sigaction slot;\n", - "\tPyObject * set_create_signature(PyObject * self, PyObject * args) {\n", - "\t\tif (!PyArg_ParseTuple(args, \"O\", &create_signature)) {\n", - "\t\t\tPyErr_SetString(PyExc_ValueError, \"Invalid Argument to set_create_signature!\");\n", - "\t\t\treturn NULL;\n", - "\t\t}\n", - "\t\tPy_INCREF(create_signature);\n", - "\t\tmemset(&slot, 0, sizeof(slot));\n", - "\t\tslot.sa_handler = &sighandler;\n", - "\t\tsigaction(SIGSEGV, &slot, NULL);\n", - "\t\tsigaction(SIGBUS, &slot, NULL);\n", - "\t\tPy_INCREF(Py_None);\n", - "\t\treturn Py_None;\n", - "\t}\n", - "\tPyObject * run(PyObject * self, PyObject * args) {\n", - "\t\t{\n", - "\t\t\tPyObj pX;\n", - "\t\t\tPyObj pY;\n", - "\t\t\tif (\n", - "\t\t\t\tPyTuple_CheckExact(args) and PyTuple_GET_SIZE(args) == 2\n", - "\t\t\t\tand (pX = PyTuple_GET_ITEM(args, 0)) and PyArray_CheckExact(pX)\n", - "\t\t\t\tand PyArray_TYPE((PyArrayObject *)pX) == NPY_FLOAT64 and PyArray_NDIM((PyArrayObject *)pX) == 1\n", - "\t\t\t\tand (pY = PyTuple_GET_ITEM(args, 1)) and PyArray_CheckExact(pY)\n", - "\t\t\t\tand PyArray_TYPE((PyArrayObject *)pY) == NPY_FLOAT64 and PyArray_NDIM((PyArrayObject *)pY) == 1\n", - "\t\t\t) {\n", - "\t\t\t\tif (!(pX.incref((PyObject *)PyArray_GETCONTIGUOUS((PyArrayObject *)pX)))) {\n", - "\t\t\t\t\tPyErr_SetString(PyExc_ValueError, \"Invalid Argument type on X!\");\n", - "\t\t\t\t\treturn NULL;\n", - "\t\t\t\t}\n", - "\t\t\t\tif (!(pY.incref((PyObject *)PyArray_GETCONTIGUOUS((PyArrayObject *)pY)))) {\n", - "\t\t\t\t\tPyErr_SetString(PyExc_ValueError, \"Invalid Argument type on Y!\");\n", - "\t\t\t\t\treturn NULL;\n", - "\t\t\t\t}\n", - "\t\t\t\ttry {\n", - "\t\t\t\t\tln_hope_exp_d1d1(\n", - "\t\t\t\t\t\t pX, PyArray_SHAPE((PyArrayObject *)pX), (npy_double *)PyArray_DATA((PyArrayObject *)pX)\n", - "\t\t\t\t\t\t, pY, PyArray_SHAPE((PyArrayObject *)pY), (npy_double *)PyArray_DATA((PyArrayObject *)pY)\n", - "\t\t\t\t\t);\n", - "\t\t\t\t\tPy_INCREF(Py_None);\n", - "\t\t\t\t\treturn Py_None;\n", - "\t\t\t\t} catch (...) {\n", - "\t\t\t\t\treturn NULL;\n", - "\t\t\t\t}\n", - "\t\t\t} else\n", - "\t\t\t\tPyErr_Clear();\n", - "\t\t}\n", - "\t\tPyObject * signatures = Py_BuildValue(\"(sO)\", \"(lp0\\n(lp1\\nccopy_reg\\n_reconstructor\\np2\\n(chope._ast\\nVariable\\np3\\nc__builtin__\\nobject\\np4\\nNtp5\\nRp6\\n(dp7\\nS'name'\\np8\\nS'X'\\np9\\nsS'dtype'\\np10\\nS'float64'\\np11\\nsS'dims'\\np12\\nI1\\nsbag2\\n(g3\\ng4\\nNtp13\\nRp14\\n(dp15\\ng8\\nS'Y'\\np16\\nsg10\\ng11\\nsg12\\nI1\\nsbaa.\", args);\n", - "\t\tif (!signatures) {\n", - "\t\t\tPyErr_SetString(PyExc_ValueError, \"Error building signature string for ln_hope_exp\");\n", - "\t\t\treturn NULL;\n", - "\t\t}\n", - "\t\treturn PyObject_Call(create_signature, signatures, NULL);\n", - "\t}\n", - "\tPyMethodDef ln_hope_expMethods[] = {\n", - "\t\t{ \"set_create_signature\", (PyCFunction)set_create_signature, METH_VARARGS },\n", - "\t\t{ \"run\", (PyCFunction)run, METH_VARARGS },\n", - "\t\t{ NULL, NULL }\n", - "\t};\n", - "\tPyMODINIT_FUNC initln_exp(void) {\n", - "\t\timport_array();\n", - "\t\tPyImport_ImportModule(\"numpy\");\n", - "\t\t(void)Py_InitModule(\"ln_exp\", ln_hope_expMethods);\n", - "\t}\n", + " PyObject * create_signature;\n", + " struct sigaction slot;\n", + " PyObject * set_create_signature(PyObject * self, PyObject * args) {\n", + " if (!PyArg_ParseTuple(args, \"O\", &create_signature)) {\n", + " PyErr_SetString(PyExc_ValueError, \"Invalid Argument to set_create_signature!\");\n", + " return NULL;\n", + " }\n", + " Py_INCREF(create_signature);\n", + " memset(&slot, 0, sizeof(slot));\n", + " slot.sa_handler = &sighandler;\n", + " sigaction(SIGSEGV, &slot, NULL);\n", + " sigaction(SIGBUS, &slot, NULL);\n", + " Py_INCREF(Py_None);\n", + " return Py_None;\n", + " }\n", + " PyObject * run(PyObject * self, PyObject * args) {\n", + " {\n", + " PyObj pX;\n", + " PyObj pY;\n", + " if (\n", + " PyTuple_CheckExact(args) and PyTuple_GET_SIZE(args) == 2\n", + " and (pX = PyTuple_GET_ITEM(args, 0)) and PyArray_CheckExact(pX)\n", + " and PyArray_TYPE((PyArrayObject *)pX) == NPY_FLOAT64 and PyArray_NDIM((PyArrayObject *)pX) == 1\n", + " and (pY = PyTuple_GET_ITEM(args, 1)) and PyArray_CheckExact(pY)\n", + " and PyArray_TYPE((PyArrayObject *)pY) == NPY_FLOAT64 and PyArray_NDIM((PyArrayObject *)pY) == 1\n", + " ) {\n", + " if (!(pX.incref((PyObject *)PyArray_GETCONTIGUOUS((PyArrayObject *)pX)))) {\n", + " PyErr_SetString(PyExc_ValueError, \"Invalid Argument type on X!\");\n", + " return NULL;\n", + " }\n", + " if (!(pY.incref((PyObject *)PyArray_GETCONTIGUOUS((PyArrayObject *)pY)))) {\n", + " PyErr_SetString(PyExc_ValueError, \"Invalid Argument type on Y!\");\n", + " return NULL;\n", + " }\n", + " try {\n", + " ln_hope_exp_d1d1(\n", + " pX, PyArray_SHAPE((PyArrayObject *)pX), (npy_double *)PyArray_DATA((PyArrayObject *)pX)\n", + " , pY, PyArray_SHAPE((PyArrayObject *)pY), (npy_double *)PyArray_DATA((PyArrayObject *)pY)\n", + " );\n", + " Py_INCREF(Py_None);\n", + " return Py_None;\n", + " } catch (...) {\n", + " return NULL;\n", + " }\n", + " } else\n", + " PyErr_Clear();\n", + " }\n", + " PyObject * signatures = Py_BuildValue(\"(sO)\", \"(lp0\\n(lp1\\nccopy_reg\\n_reconstructor\\np2\\n(chope._ast\\nVariable\\np3\\nc__builtin__\\nobject\\np4\\nNtp5\\nRp6\\n(dp7\\nS'name'\\np8\\nS'X'\\np9\\nsS'dtype'\\np10\\nS'float64'\\np11\\nsS'dims'\\np12\\nI1\\nsbag2\\n(g3\\ng4\\nNtp13\\nRp14\\n(dp15\\ng8\\nS'Y'\\np16\\nsg10\\ng11\\nsg12\\nI1\\nsbaa.\", args);\n", + " if (!signatures) {\n", + " PyErr_SetString(PyExc_ValueError, \"Error building signature string for ln_hope_exp\");\n", + " return NULL;\n", + " }\n", + " return PyObject_Call(create_signature, signatures, NULL);\n", + " }\n", + " PyMethodDef ln_expMethods[] = {\n", + " { \"set_create_signature\", set_create_signature, METH_VARARGS, \"signal handler\" },\n", + " { \"run\", run, METH_VARARGS, \"module function\" },\n", + " { NULL, NULL, 0, NULL }\n", + " };\n", + "\n", + "\n", + " static struct PyModuleDef ln_expmodule = {\n", + " PyModuleDef_HEAD_INIT,\n", + " \"ln_exp\",\n", + " NULL,\n", + " -1,\n", + " ln_expMethods\n", + " };\n", + "\n", + "\n", + "\n", + " PyMODINIT_FUNC PyInit_ln_exp(void) {\n", + " import_array();\n", + " PyImport_ImportModule(\"numpy\");\n", + " return PyModule_Create(&ln_expmodule);\n", + " }\n", "}\n", "#include \n", "#include \n", @@ -1122,37 +1139,37 @@ "#include \n", "#include \n", "void sighandler(int sig) {\n", - "\tstd::ostringstream buffer;\n", - "\tbuffer << \"Abort by \" << (sig == SIGSEGV ? \"segfault\" : \"bus error\") << std::endl;\n", - "\tvoid * stack[64];\n", - "\tstd::size_t depth = backtrace(stack, 64);\n", - "\tif (!depth)\n", - "\t\tbuffer << \" \" << std::endl;\n", - "\telse {\n", - "\t\tchar ** symbols = backtrace_symbols(stack, depth);\n", - "\t\tfor (std::size_t i = 1; i < depth; ++i) {\n", - "\t\t\tstd::string symbol = symbols[i];\n", - "\t\t\t\tif (symbol.find_first_of(' ', 59) != std::string::npos) {\n", - "\t\t\t\t\tstd::string name = symbol.substr(59, symbol.find_first_of(' ', 59) - 59);\n", - "\t\t\t\t\tint status;\n", - "\t\t\t\t\tchar * demangled = abi::__cxa_demangle(name.c_str(), NULL, NULL, &status);\n", - "\t\t\t\t\tif (!status) {\n", - "\t\t\t\t\t\tbuffer << \" \" \n", - "\t\t\t\t\t\t\t<< symbol.substr(0, 59) \n", - "\t\t\t\t\t\t\t<< demangled\n", - "\t\t\t\t\t\t\t<< symbol.substr(59 + name.size())\n", - "\t\t\t\t\t\t\t<< std::endl;\n", - "\t\t\t\t\t\tfree(demangled);\n", - "\t\t\t\t\t} else\n", - "\t\t\t\t\t\tbuffer << \" \" << symbol << std::endl;\n", - "\t\t\t\t} else\n", - "\t\t\t\t\tbuffer << \" \" << symbol << std::endl;\n", - "\t\t\t}\n", - "\t\t\tfree(symbols);\n", - "\t\t}\n", - "\t\tstd::cerr << buffer.str();\n", - "\t\tstd::exit(EXIT_FAILURE);\n", - "\t}\n" + " std::ostringstream buffer;\n", + " buffer << \"Abort by \" << (sig == SIGSEGV ? \"segfault\" : \"bus error\") << std::endl;\n", + " void * stack[64];\n", + " std::size_t depth = backtrace(stack, 64);\n", + " if (!depth)\n", + " buffer << \" \" << std::endl;\n", + " else {\n", + " char ** symbols = backtrace_symbols(stack, depth);\n", + " for (std::size_t i = 1; i < depth; ++i) {\n", + " std::string symbol = symbols[i];\n", + " if (symbol.find_first_of(' ', 59) != std::string::npos) {\n", + " std::string name = symbol.substr(59, symbol.find_first_of(' ', 59) - 59);\n", + " int status;\n", + " char * demangled = abi::__cxa_demangle(name.c_str(), NULL, NULL, &status);\n", + " if (!status) {\n", + " buffer << \" \"\n", + " << symbol.substr(0, 59)\n", + " << demangled\n", + " << symbol.substr(59 + name.size())\n", + " << std::endl;\n", + " free(demangled);\n", + " } else\n", + " buffer << \" \" << symbol << std::endl;\n", + " } else\n", + " buffer << \" \" << symbol << std::endl;\n", + " }\n", + " free(symbols);\n", + " }\n", + " std::cerr << buffer.str();\n", + " std::exit(EXIT_FAILURE);\n", + " }\n" ] }, { @@ -1164,7 +1181,7 @@ "name": "stdout", "output_type": "stream", "text": [ - "Overwriting src/ln_opt.cpp\n" + "Writing src/ln_opt.cpp\n" ] } ], @@ -1184,106 +1201,119 @@ "#include \n", "#include \n", "struct PyObj {\n", - "\ttypedef PyObject * ptr_t;\n", - "\ttypedef PyArrayObject * arrptr_t;\n", - "\tPyObj(): dec(false), ptr(NULL) {}\n", - "\tPyObj(ptr_t p): dec(false), ptr(p) {}\n", - "\t~PyObj() { if(dec) Py_DECREF(ptr); }\n", - "\tPyObj & operator=(ptr_t p) { if(dec) Py_DECREF(ptr); ptr = p; dec = false; return *this; }\n", - "\tPyObj & incref(ptr_t p) { if(dec) Py_DECREF(ptr); ptr = p; dec = (p != NULL); return *this; }\n", - "\toperator bool() const { return ptr; }\n", - "\toperator ptr_t() const { return ptr; }\n", - "\toperator arrptr_t() const { return (arrptr_t)ptr; }\n", - "\tbool dec;\n", - "\tptr_t ptr;\n", + " typedef PyObject * ptr_t;\n", + " typedef PyArrayObject * arrptr_t;\n", + " PyObj(): dec(false), ptr(NULL) {}\n", + " PyObj(ptr_t p): dec(false), ptr(p) {}\n", + " ~PyObj() { if(dec) Py_DECREF(ptr); }\n", + " PyObj & operator=(ptr_t p) { if(dec) Py_DECREF(ptr); ptr = p; dec = false; return *this; }\n", + " PyObj & incref(ptr_t p) { if(dec) Py_DECREF(ptr); ptr = p; dec = (p != NULL); return *this; }\n", + " operator bool() const { return ptr; }\n", + " operator ptr_t() const { return ptr; }\n", + " operator arrptr_t() const { return (arrptr_t)ptr; }\n", + " bool dec;\n", + " ptr_t ptr;\n", "};\n", "\n", - "inline void ln_hope_opt_d1d1(PyObject * pX, npy_intp const * __restrict__ sX, npy_double * __restrict__ cX, \n", - "\t\t\t\t\t\t\t\t\t\t\t\t\tPyObject * pY, npy_intp const * __restrict__ sY, npy_double * __restrict__ cY);\n", - "\n", - "inline void ln_hope_opt_d1d1(PyObject * pX, npy_intp const * __restrict__ sX, npy_double * __restrict__ cX, \n", - "\t\t\t\t\t\t\t\t\t\t\t\t\tPyObject * pY, npy_intp const * __restrict__ sY, npy_double * __restrict__ cY){\n", - "\t\t\t\t\t\t\t\t\t\t\t\t\t\t\n", - "\tfor (npy_intp i0 = 0; i0 < sY[0] - 0; ++i0) {\n", - "\t\tauto c__sp0 = (cX[i0] * cX[i0]);\n", - "\t\tauto c__sp1 = (c__sp0 * c__sp0);\n", - "\t\tauto c__sp2 = (c__sp1 * c__sp1);\n", - "\t\tauto c__sp3 = (c__sp2 * cX[i0]);\n", - "\t\tauto c__sp4 = (c__sp0 * cX[i0]);\n", - "\t\tauto c__sp5 = (c__sp4 * c__sp4);\n", - "\t\tauto c__sp6 = (c__sp5 * cX[i0]);\n", - "\t\tauto c__sp7 = (c__sp1 * cX[i0]);\n", - "\t\tcY[(int)(i0)] = (-7129.0 / 2520.0) + (28 * c__sp4) + (-(18 * c__sp0)) + (-(9 * c__sp2 / 8)) + (-(14 * c__sp5)) + (-(63 * c__sp1 / 2)) + (126 * c__sp7 / 5) + (9 * cX[i0]) + (c__sp3 / 9) + (36 * c__sp6 / 7);\n", - "\t}\n", + "inline void ln_hope_opt_d1d1(PyObject * pX, npy_intp const * __restrict__ sX, npy_double * __restrict__ cX,\n", + " PyObject * pY, npy_intp const * __restrict__ sY, npy_double * __restrict__ cY);\n", + "\n", + "inline void ln_hope_opt_d1d1(PyObject * pX, npy_intp const * __restrict__ sX, npy_double * __restrict__ cX,\n", + " PyObject * pY, npy_intp const * __restrict__ sY, npy_double * __restrict__ cY){\n", + "\n", + " for (npy_intp i0 = 0; i0 < sY[0] - 0; ++i0) {\n", + " auto c__sp0 = (cX[i0] * cX[i0]);\n", + " auto c__sp1 = (c__sp0 * c__sp0);\n", + " auto c__sp2 = (c__sp1 * c__sp1);\n", + " auto c__sp3 = (c__sp2 * cX[i0]);\n", + " auto c__sp4 = (c__sp0 * cX[i0]);\n", + " auto c__sp5 = (c__sp4 * c__sp4);\n", + " auto c__sp6 = (c__sp5 * cX[i0]);\n", + " auto c__sp7 = (c__sp1 * cX[i0]);\n", + " cY[(int)(i0)] = (-7129.0 / 2520.0) + (28 * c__sp4) + (-(18 * c__sp0)) + (-(9 * c__sp2 / 8)) + (-(14 * c__sp5)) + (-(63 * c__sp1 / 2)) + (126 * c__sp7 / 5) + (9 * cX[i0]) + (c__sp3 / 9) + (36 * c__sp6 / 7);\n", + " }\n", "}\n", "\n", "void sighandler(int sig);\n", "#include \n", "extern \"C\" {\n", - "\tPyObject * create_signature;\n", - "\tstruct sigaction slot;\n", - "\tPyObject * set_create_signature(PyObject * self, PyObject * args) {\n", - "\t\tif (!PyArg_ParseTuple(args, \"O\", &create_signature)) {\n", - "\t\t\tPyErr_SetString(PyExc_ValueError, \"Invalid Argument to set_create_signature!\");\n", - "\t\t\treturn NULL;\n", - "\t\t}\n", - "\t\tPy_INCREF(create_signature);\n", - "\t\tmemset(&slot, 0, sizeof(slot));\n", - "\t\tslot.sa_handler = &sighandler;\n", - "\t\tsigaction(SIGSEGV, &slot, NULL);\n", - "\t\tsigaction(SIGBUS, &slot, NULL);\n", - "\t\tPy_INCREF(Py_None);\n", - "\t\treturn Py_None;\n", - "\t}\n", - "\tPyObject * run(PyObject * self, PyObject * args) {\n", - "\t\t{\n", - "\t\t\tPyObj pX;\n", - "\t\t\tPyObj pY;\n", - "\t\t\tif (\n", - "\t\t\t\tPyTuple_CheckExact(args) and PyTuple_GET_SIZE(args) == 2\n", - "\t\t\t\tand (pX = PyTuple_GET_ITEM(args, 0)) and PyArray_CheckExact(pX)\n", - "\t\t\t\tand PyArray_TYPE((PyArrayObject *)pX) == NPY_FLOAT64 and PyArray_NDIM((PyArrayObject *)pX) == 1\n", - "\t\t\t\tand (pY = PyTuple_GET_ITEM(args, 1)) and PyArray_CheckExact(pY)\n", - "\t\t\t\tand PyArray_TYPE((PyArrayObject *)pY) == NPY_FLOAT64 and PyArray_NDIM((PyArrayObject *)pY) == 1\n", - "\t\t\t) {\n", - "\t\t\t\tif (!(pX.incref((PyObject *)PyArray_GETCONTIGUOUS((PyArrayObject *)pX)))) {\n", - "\t\t\t\t\tPyErr_SetString(PyExc_ValueError, \"Invalid Argument type on X!\");\n", - "\t\t\t\t\treturn NULL;\n", - "\t\t\t\t}\n", - "\t\t\t\tif (!(pY.incref((PyObject *)PyArray_GETCONTIGUOUS((PyArrayObject *)pY)))) {\n", - "\t\t\t\t\tPyErr_SetString(PyExc_ValueError, \"Invalid Argument type on Y!\");\n", - "\t\t\t\t\treturn NULL;\n", - "\t\t\t\t}\n", - "\t\t\t\ttry {\n", - "\t\t\t\t\tln_hope_opt_d1d1(\n", - "\t\t\t\t\t\t pX, PyArray_SHAPE((PyArrayObject *)pX), (npy_double *)PyArray_DATA((PyArrayObject *)pX)\n", - "\t\t\t\t\t\t, pY, PyArray_SHAPE((PyArrayObject *)pY), (npy_double *)PyArray_DATA((PyArrayObject *)pY)\n", - "\t\t\t\t\t);\n", - "\t\t\t\t\tPy_INCREF(Py_None);\n", - "\t\t\t\t\treturn Py_None;\n", - "\t\t\t\t} catch (...) {\n", - "\t\t\t\t\treturn NULL;\n", - "\t\t\t\t}\n", - "\t\t\t} else\n", - "\t\t\t\tPyErr_Clear();\n", - "\t\t}\n", - "\t\tPyObject * signatures = Py_BuildValue(\"(sO)\", \"(lp0\\n(lp1\\nccopy_reg\\n_reconstructor\\np2\\n(chope._ast\\nVariable\\np3\\nc__builtin__\\nobject\\np4\\nNtp5\\nRp6\\n(dp7\\nS'name'\\np8\\nS'X'\\np9\\nsS'dtype'\\np10\\nS'float64'\\np11\\nsS'dims'\\np12\\nI1\\nsbag2\\n(g3\\ng4\\nNtp13\\nRp14\\n(dp15\\ng8\\nS'Y'\\np16\\nsg10\\ng11\\nsg12\\nI1\\nsbaa.\", args);\n", - "\t\tif (!signatures) {\n", - "\t\t\tPyErr_SetString(PyExc_ValueError, \"Error building signature string for ln_hope_opt\");\n", - "\t\t\treturn NULL;\n", - "\t\t}\n", - "\t\treturn PyObject_Call(create_signature, signatures, NULL);\n", - "\t}\n", - "\tPyMethodDef ln_hope_optMethods[] = {\n", - "\t\t{ \"set_create_signature\", (PyCFunction)set_create_signature, METH_VARARGS },\n", - "\t\t{ \"run\", (PyCFunction)run, METH_VARARGS },\n", - "\t\t{ NULL, NULL }\n", - "\t};\n", - "\tPyMODINIT_FUNC initln_opt(void) {\n", - "\t\timport_array();\n", - "\t\tPyImport_ImportModule(\"numpy\");\n", - "\t\t(void)Py_InitModule(\"ln_opt\", ln_hope_optMethods);\n", - "\t}\n", + " PyObject * create_signature;\n", + " struct sigaction slot;\n", + " PyObject * set_create_signature(PyObject * self, PyObject * args) {\n", + " if (!PyArg_ParseTuple(args, \"O\", &create_signature)) {\n", + " PyErr_SetString(PyExc_ValueError, \"Invalid Argument to set_create_signature!\");\n", + " return NULL;\n", + " }\n", + " Py_INCREF(create_signature);\n", + " memset(&slot, 0, sizeof(slot));\n", + " slot.sa_handler = &sighandler;\n", + " sigaction(SIGSEGV, &slot, NULL);\n", + " sigaction(SIGBUS, &slot, NULL);\n", + " Py_INCREF(Py_None);\n", + " return Py_None;\n", + " }\n", + " PyObject * run(PyObject * self, PyObject * args) {\n", + " {\n", + " PyObj pX;\n", + " PyObj pY;\n", + " if (\n", + " PyTuple_CheckExact(args) and PyTuple_GET_SIZE(args) == 2\n", + " and (pX = PyTuple_GET_ITEM(args, 0)) and PyArray_CheckExact(pX)\n", + " and PyArray_TYPE((PyArrayObject *)pX) == NPY_FLOAT64 and PyArray_NDIM((PyArrayObject *)pX) == 1\n", + " and (pY = PyTuple_GET_ITEM(args, 1)) and PyArray_CheckExact(pY)\n", + " and PyArray_TYPE((PyArrayObject *)pY) == NPY_FLOAT64 and PyArray_NDIM((PyArrayObject *)pY) == 1\n", + " ) {\n", + " if (!(pX.incref((PyObject *)PyArray_GETCONTIGUOUS((PyArrayObject *)pX)))) {\n", + " PyErr_SetString(PyExc_ValueError, \"Invalid Argument type on X!\");\n", + " return NULL;\n", + " }\n", + " if (!(pY.incref((PyObject *)PyArray_GETCONTIGUOUS((PyArrayObject *)pY)))) {\n", + " PyErr_SetString(PyExc_ValueError, \"Invalid Argument type on Y!\");\n", + " return NULL;\n", + " }\n", + " try {\n", + " ln_hope_opt_d1d1(\n", + " pX, PyArray_SHAPE((PyArrayObject *)pX), (npy_double *)PyArray_DATA((PyArrayObject *)pX)\n", + " , pY, PyArray_SHAPE((PyArrayObject *)pY), (npy_double *)PyArray_DATA((PyArrayObject *)pY)\n", + " );\n", + " Py_INCREF(Py_None);\n", + " return Py_None;\n", + " } catch (...) {\n", + " return NULL;\n", + " }\n", + " } else\n", + " PyErr_Clear();\n", + " }\n", + " PyObject * signatures = Py_BuildValue(\"(sO)\", \"(lp0\\n(lp1\\nccopy_reg\\n_reconstructor\\np2\\n(chope._ast\\nVariable\\np3\\nc__builtin__\\nobject\\np4\\nNtp5\\nRp6\\n(dp7\\nS'name'\\np8\\nS'X'\\np9\\nsS'dtype'\\np10\\nS'float64'\\np11\\nsS'dims'\\np12\\nI1\\nsbag2\\n(g3\\ng4\\nNtp13\\nRp14\\n(dp15\\ng8\\nS'Y'\\np16\\nsg10\\ng11\\nsg12\\nI1\\nsbaa.\", args);\n", + " if (!signatures) {\n", + " PyErr_SetString(PyExc_ValueError, \"Error building signature string for ln_hope_opt\");\n", + " return NULL;\n", + " }\n", + " return PyObject_Call(create_signature, signatures, NULL);\n", + " }\n", + "\n", + " PyMethodDef ln_optMethods[] = {\n", + " { \"set_create_signature\", set_create_signature, METH_VARARGS, \"signal handler\" },\n", + " { \"run\", run, METH_VARARGS, \"module function\" },\n", + " { NULL, NULL, 0, NULL }\n", + " };\n", + "\n", + "\n", + " static struct PyModuleDef ln_optmodule = {\n", + " PyModuleDef_HEAD_INIT,\n", + " \"ln\",\n", + " NULL,\n", + " -1,\n", + " ln_optMethods\n", + " };\n", + "\n", + "\n", + "\n", + " PyMODINIT_FUNC PyInit_ln_opt(void) {\n", + " import_array();\n", + " PyImport_ImportModule(\"numpy\");\n", + " return PyModule_Create(&ln_optmodule);\n", + " }\n", "}\n", "#include \n", "#include \n", @@ -1291,37 +1321,37 @@ "#include \n", "#include \n", "void sighandler(int sig) {\n", - "\tstd::ostringstream buffer;\n", - "\tbuffer << \"Abort by \" << (sig == SIGSEGV ? \"segfault\" : \"bus error\") << std::endl;\n", - "\tvoid * stack[64];\n", - "\tstd::size_t depth = backtrace(stack, 64);\n", - "\tif (!depth)\n", - "\t\tbuffer << \" \" << std::endl;\n", - "\telse {\n", - "\t\tchar ** symbols = backtrace_symbols(stack, depth);\n", - "\t\tfor (std::size_t i = 1; i < depth; ++i) {\n", - "\t\t\tstd::string symbol = symbols[i];\n", - "\t\t\t\tif (symbol.find_first_of(' ', 59) != std::string::npos) {\n", - "\t\t\t\t\tstd::string name = symbol.substr(59, symbol.find_first_of(' ', 59) - 59);\n", - "\t\t\t\t\tint status;\n", - "\t\t\t\t\tchar * demangled = abi::__cxa_demangle(name.c_str(), NULL, NULL, &status);\n", - "\t\t\t\t\tif (!status) {\n", - "\t\t\t\t\t\tbuffer << \" \" \n", - "\t\t\t\t\t\t\t<< symbol.substr(0, 59) \n", - "\t\t\t\t\t\t\t<< demangled\n", - "\t\t\t\t\t\t\t<< symbol.substr(59 + name.size())\n", - "\t\t\t\t\t\t\t<< std::endl;\n", - "\t\t\t\t\t\tfree(demangled);\n", - "\t\t\t\t\t} else\n", - "\t\t\t\t\t\tbuffer << \" \" << symbol << std::endl;\n", - "\t\t\t\t} else\n", - "\t\t\t\t\tbuffer << \" \" << symbol << std::endl;\n", - "\t\t\t}\n", - "\t\t\tfree(symbols);\n", - "\t\t}\n", - "\t\tstd::cerr << buffer.str();\n", - "\t\tstd::exit(EXIT_FAILURE);\n", - "\t}\n" + " std::ostringstream buffer;\n", + " buffer << \"Abort by \" << (sig == SIGSEGV ? \"segfault\" : \"bus error\") << std::endl;\n", + " void * stack[64];\n", + " std::size_t depth = backtrace(stack, 64);\n", + " if (!depth)\n", + " buffer << \" \" << std::endl;\n", + " else {\n", + " char ** symbols = backtrace_symbols(stack, depth);\n", + " for (std::size_t i = 1; i < depth; ++i) {\n", + " std::string symbol = symbols[i];\n", + " if (symbol.find_first_of(' ', 59) != std::string::npos) {\n", + " std::string name = symbol.substr(59, symbol.find_first_of(' ', 59) - 59);\n", + " int status;\n", + " char * demangled = abi::__cxa_demangle(name.c_str(), NULL, NULL, &status);\n", + " if (!status) {\n", + " buffer << \" \"\n", + " << symbol.substr(0, 59)\n", + " << demangled\n", + " << symbol.substr(59 + name.size())\n", + " << std::endl;\n", + " free(demangled);\n", + " } else\n", + " buffer << \" \" << symbol << std::endl;\n", + " } else\n", + " buffer << \" \" << symbol << std::endl;\n", + " }\n", + " free(symbols);\n", + " }\n", + " std::cerr << buffer.str();\n", + " std::exit(EXIT_FAILURE);\n", + " }\n" ] }, { @@ -1340,7 +1370,7 @@ "name": "stdout", "output_type": "stream", "text": [ - "Overwriting src/poly.cpp\n" + "Writing src/poly.cpp\n" ] } ], @@ -1360,99 +1390,109 @@ "#include \n", "#include \n", "struct PyObj {\n", - "\ttypedef PyObject * ptr_t;\n", - "\ttypedef PyArrayObject * arrptr_t;\n", - "\tPyObj(): dec(false), ptr(NULL) {}\n", - "\tPyObj(ptr_t p): dec(false), ptr(p) {}\n", - "\t~PyObj() { if(dec) Py_DECREF(ptr); }\n", - "\tPyObj & operator=(ptr_t p) { if(dec) Py_DECREF(ptr); ptr = p; dec = false; return *this; }\n", - "\tPyObj & incref(ptr_t p) { if(dec) Py_DECREF(ptr); ptr = p; dec = (p != NULL); return *this; }\n", - "\toperator bool() const { return ptr; }\n", - "\toperator ptr_t() const { return ptr; }\n", - "\toperator arrptr_t() const { return (arrptr_t)ptr; }\n", - "\tbool dec;\n", - "\tptr_t ptr;\n", + " typedef PyObject * ptr_t;\n", + " typedef PyArrayObject * arrptr_t;\n", + " PyObj(): dec(false), ptr(NULL) {}\n", + " PyObj(ptr_t p): dec(false), ptr(p) {}\n", + " ~PyObj() { if(dec) Py_DECREF(ptr); }\n", + " PyObj & operator=(ptr_t p) { if(dec) Py_DECREF(ptr); ptr = p; dec = false; return *this; }\n", + " PyObj & incref(ptr_t p) { if(dec) Py_DECREF(ptr); ptr = p; dec = (p != NULL); return *this; }\n", + " operator bool() const { return ptr; }\n", + " operator ptr_t() const { return ptr; }\n", + " operator arrptr_t() const { return (arrptr_t)ptr; }\n", + " bool dec;\n", + " ptr_t ptr;\n", "};\n", - "inline void poly_d1d1(PyObject * pres, npy_intp const * __restrict__ sres, npy_double * __restrict__ cres, \n", - "\t\t\t\t\t\t\t\t\t\t\tPyObject * parg, npy_intp const * __restrict__ sarg, npy_double * __restrict__ carg);\n", + "inline void poly_d1d1(PyObject * pres, npy_intp const * __restrict__ sres, npy_double * __restrict__ cres,\n", + " PyObject * parg, npy_intp const * __restrict__ sarg, npy_double * __restrict__ carg);\n", "\n", - "inline void poly_d1d1(PyObject * pres, npy_intp const * __restrict__ sres, npy_double * __restrict__ cres, \n", - "\t\t\t\t\t\t\t\t\t\t\tPyObject * parg, npy_intp const * __restrict__ sarg, npy_double * __restrict__ carg){\n", - "\t\n", - "\tnpy_double arg_i;\n", - "\tdouble sin_arg_i;\n", - "\tfor (npy_intp i0 = 0; i0 < sres[0] - 0; ++i0) {\n", - "\t\targ_i = carg[i0];\n", - "\t\tcres[(int)(i0)] = std::pow(std::sin(arg_i), 2) + (std::pow(arg_i, 3) + std::pow(arg_i, 2) - arg_i - 1) / (std::pow(arg_i, 2) + 2 * arg_i + 1) + std::pow(std::cos(arg_i), 2);\n", - "\t}\n", + "inline void poly_d1d1(PyObject * pres, npy_intp const * __restrict__ sres, npy_double * __restrict__ cres,\n", + " PyObject * parg, npy_intp const * __restrict__ sarg, npy_double * __restrict__ carg){\n", + "\n", + " npy_double arg_i;\n", + " double sin_arg_i;\n", + " for (npy_intp i0 = 0; i0 < sres[0] - 0; ++i0) {\n", + " arg_i = carg[i0];\n", + " cres[(int)(i0)] = std::pow(std::sin(arg_i), 2) + (std::pow(arg_i, 3) + std::pow(arg_i, 2) - arg_i - 1) / (std::pow(arg_i, 2) + 2 * arg_i + 1) + std::pow(std::cos(arg_i), 2);\n", + " }\n", "}\n", "void sighandler(int sig);\n", "#include \n", "extern \"C\" {\n", - "\tPyObject * create_signature;\n", - "\tstruct sigaction slot;\n", - "\tPyObject * set_create_signature(PyObject * self, PyObject * args) {\n", - "\t\tif (!PyArg_ParseTuple(args, \"O\", &create_signature)) {\n", - "\t\t\tPyErr_SetString(PyExc_ValueError, \"Invalid Argument to set_create_signature!\");\n", - "\t\t\treturn NULL;\n", - "\t\t}\n", - "\t\tPy_INCREF(create_signature);\n", - "\t\tmemset(&slot, 0, sizeof(slot));\n", - "\t\tslot.sa_handler = &sighandler;\n", - "\t\tsigaction(SIGSEGV, &slot, NULL);\n", - "\t\tsigaction(SIGBUS, &slot, NULL);\n", - "\t\tPy_INCREF(Py_None);\n", - "\t\treturn Py_None;\n", - "\t}\n", - "\tPyObject * run(PyObject * self, PyObject * args) {\n", - "\t\t{\n", - "\t\t\tPyObj pres;\n", - "\t\t\tPyObj parg;\n", - "\t\t\tif (\n", - "\t\t\t\tPyTuple_CheckExact(args) and PyTuple_GET_SIZE(args) == 2\n", - "\t\t\t\tand (pres = PyTuple_GET_ITEM(args, 0)) and PyArray_CheckExact(pres)\n", - "\t\t\t\tand PyArray_TYPE((PyArrayObject *)pres) == NPY_FLOAT64 and PyArray_NDIM((PyArrayObject *)pres) == 1\n", - "\t\t\t\tand (parg = PyTuple_GET_ITEM(args, 1)) and PyArray_CheckExact(parg)\n", - "\t\t\t\tand PyArray_TYPE((PyArrayObject *)parg) == NPY_FLOAT64 and PyArray_NDIM((PyArrayObject *)parg) == 1\n", - "\t\t\t) {\n", - "\t\t\t\tif (!(pres.incref((PyObject *)PyArray_GETCONTIGUOUS((PyArrayObject *)pres)))) {\n", - "\t\t\t\t\tPyErr_SetString(PyExc_ValueError, \"Invalid Argument type on res!\");\n", - "\t\t\t\t\treturn NULL;\n", - "\t\t\t\t}\n", - "\t\t\t\tif (!(parg.incref((PyObject *)PyArray_GETCONTIGUOUS((PyArrayObject *)parg)))) {\n", - "\t\t\t\t\tPyErr_SetString(PyExc_ValueError, \"Invalid Argument type on arg!\");\n", - "\t\t\t\t\treturn NULL;\n", - "\t\t\t\t}\n", - "\t\t\t\ttry {\n", - "\t\t\t\t\tpoly_d1d1(\n", - "\t\t\t\t\t\t pres, PyArray_SHAPE((PyArrayObject *)pres), (npy_double *)PyArray_DATA((PyArrayObject *)pres)\n", - "\t\t\t\t\t\t, parg, PyArray_SHAPE((PyArrayObject *)parg), (npy_double *)PyArray_DATA((PyArrayObject *)parg)\n", - "\t\t\t\t\t);\n", - "\t\t\t\t\tPy_INCREF(Py_None);\n", - "\t\t\t\t\treturn Py_None;\n", - "\t\t\t\t} catch (...) {\n", - "\t\t\t\t\treturn NULL;\n", - "\t\t\t\t}\n", - "\t\t\t} else\n", - "\t\t\t\tPyErr_Clear();\n", - "\t\t}\n", - "\t\tPyObject * signatures = Py_BuildValue(\"(sO)\", \"(lp0\\n(lp1\\nccopy_reg\\n_reconstructor\\np2\\n(chope._ast\\nVariable\\np3\\nc__builtin__\\nobject\\np4\\nNtp5\\nRp6\\n(dp7\\nS'name'\\np8\\nS'res'\\np9\\nsS'dtype'\\np10\\nS'float64'\\np11\\nsS'dims'\\np12\\nI1\\nsbag2\\n(g3\\ng4\\nNtp13\\nRp14\\n(dp15\\ng8\\nS'arg'\\np16\\nsg10\\ng11\\nsg12\\nI1\\nsbaa.\", args);\n", - "\t\tif (!signatures) {\n", - "\t\t\tPyErr_SetString(PyExc_ValueError, \"Error building signature string for poly\");\n", - "\t\t\treturn NULL;\n", - "\t\t}\n", - "\t\treturn PyObject_Call(create_signature, signatures, NULL);\n", - "\t}\n", - "\tPyMethodDef polyMethods[] = {\n", - "\t\t{ \"set_create_signature\", (PyCFunction)set_create_signature, METH_VARARGS },\n", - "\t\t{ \"run\", (PyCFunction)run, METH_VARARGS },\n", - "\t\t{ NULL, NULL }\n", - "\t};\n", - "\tPyMODINIT_FUNC initpoly(void) {\n", - "\t\timport_array();\n", - "\t\tPyImport_ImportModule(\"numpy\");\n", - "\t\t(void)Py_InitModule(\"poly\", polyMethods);\n", - "\t}\n", + " PyObject * create_signature;\n", + " struct sigaction slot;\n", + " PyObject * set_create_signature(PyObject * self, PyObject * args) {\n", + " if (!PyArg_ParseTuple(args, \"O\", &create_signature)) {\n", + " PyErr_SetString(PyExc_ValueError, \"Invalid Argument to set_create_signature!\");\n", + " return NULL;\n", + " }\n", + " Py_INCREF(create_signature);\n", + " memset(&slot, 0, sizeof(slot));\n", + " slot.sa_handler = &sighandler;\n", + " sigaction(SIGSEGV, &slot, NULL);\n", + " sigaction(SIGBUS, &slot, NULL);\n", + " Py_INCREF(Py_None);\n", + " return Py_None;\n", + " }\n", + " PyObject * run(PyObject * self, PyObject * args) {\n", + " {\n", + " PyObj pres;\n", + " PyObj parg;\n", + " if (\n", + " PyTuple_CheckExact(args) and PyTuple_GET_SIZE(args) == 2\n", + " and (pres = PyTuple_GET_ITEM(args, 0)) and PyArray_CheckExact(pres)\n", + " and PyArray_TYPE((PyArrayObject *)pres) == NPY_FLOAT64 and PyArray_NDIM((PyArrayObject *)pres) == 1\n", + " and (parg = PyTuple_GET_ITEM(args, 1)) and PyArray_CheckExact(parg)\n", + " and PyArray_TYPE((PyArrayObject *)parg) == NPY_FLOAT64 and PyArray_NDIM((PyArrayObject *)parg) == 1\n", + " ) {\n", + " if (!(pres.incref((PyObject *)PyArray_GETCONTIGUOUS((PyArrayObject *)pres)))) {\n", + " PyErr_SetString(PyExc_ValueError, \"Invalid Argument type on res!\");\n", + " return NULL;\n", + " }\n", + " if (!(parg.incref((PyObject *)PyArray_GETCONTIGUOUS((PyArrayObject *)parg)))) {\n", + " PyErr_SetString(PyExc_ValueError, \"Invalid Argument type on arg!\");\n", + " return NULL;\n", + " }\n", + " try {\n", + " poly_d1d1(\n", + " pres, PyArray_SHAPE((PyArrayObject *)pres), (npy_double *)PyArray_DATA((PyArrayObject *)pres)\n", + " , parg, PyArray_SHAPE((PyArrayObject *)parg), (npy_double *)PyArray_DATA((PyArrayObject *)parg)\n", + " );\n", + " Py_INCREF(Py_None);\n", + " return Py_None;\n", + " } catch (...) {\n", + " return NULL;\n", + " }\n", + " } else\n", + " PyErr_Clear();\n", + " }\n", + " PyObject * signatures = Py_BuildValue(\"(sO)\", \"(lp0\\n(lp1\\nccopy_reg\\n_reconstructor\\np2\\n(chope._ast\\nVariable\\np3\\nc__builtin__\\nobject\\np4\\nNtp5\\nRp6\\n(dp7\\nS'name'\\np8\\nS'res'\\np9\\nsS'dtype'\\np10\\nS'float64'\\np11\\nsS'dims'\\np12\\nI1\\nsbag2\\n(g3\\ng4\\nNtp13\\nRp14\\n(dp15\\ng8\\nS'arg'\\np16\\nsg10\\ng11\\nsg12\\nI1\\nsbaa.\", args);\n", + " if (!signatures) {\n", + " PyErr_SetString(PyExc_ValueError, \"Error building signature string for poly\");\n", + " return NULL;\n", + " }\n", + " return PyObject_Call(create_signature, signatures, NULL);\n", + " }\n", + "\n", + " PyMethodDef polyMethods[] = {\n", + " { \"set_create_signature\", set_create_signature, METH_VARARGS, \"signal handler\" },\n", + " { \"run\", run, METH_VARARGS, \"module function\" },\n", + " { NULL, NULL, 0, NULL }\n", + " };\n", + "\n", + " static struct PyModuleDef polymodule = {\n", + " PyModuleDef_HEAD_INIT,\n", + " \"poly\",\n", + " NULL,\n", + " -1,\n", + " polyMethods\n", + " };\n", + "\n", + " PyMODINIT_FUNC PyInit_poly(void) {\n", + " import_array();\n", + " PyImport_ImportModule(\"numpy\");\n", + " return PyModule_Create(&polymodule);\n", + " }\n", "}\n", "#include \n", "#include \n", @@ -1460,37 +1500,37 @@ "#include \n", "#include \n", "void sighandler(int sig) {\n", - "\tstd::ostringstream buffer;\n", - "\tbuffer << \"Abort by \" << (sig == SIGSEGV ? \"segfault\" : \"bus error\") << std::endl;\n", - "\tvoid * stack[64];\n", - "\tstd::size_t depth = backtrace(stack, 64);\n", - "\tif (!depth)\n", - "\t\tbuffer << \" \" << std::endl;\n", - "\telse {\n", - "\t\tchar ** symbols = backtrace_symbols(stack, depth);\n", - "\t\tfor (std::size_t i = 1; i < depth; ++i) {\n", - "\t\t\tstd::string symbol = symbols[i];\n", - "\t\t\t\tif (symbol.find_first_of(' ', 59) != std::string::npos) {\n", - "\t\t\t\t\tstd::string name = symbol.substr(59, symbol.find_first_of(' ', 59) - 59);\n", - "\t\t\t\t\tint status;\n", - "\t\t\t\t\tchar * demangled = abi::__cxa_demangle(name.c_str(), NULL, NULL, &status);\n", - "\t\t\t\t\tif (!status) {\n", - "\t\t\t\t\t\tbuffer << \" \" \n", - "\t\t\t\t\t\t\t<< symbol.substr(0, 59) \n", - "\t\t\t\t\t\t\t<< demangled\n", - "\t\t\t\t\t\t\t<< symbol.substr(59 + name.size())\n", - "\t\t\t\t\t\t\t<< std::endl;\n", - "\t\t\t\t\t\tfree(demangled);\n", - "\t\t\t\t\t} else\n", - "\t\t\t\t\t\tbuffer << \" \" << symbol << std::endl;\n", - "\t\t\t\t} else\n", - "\t\t\t\t\tbuffer << \" \" << symbol << std::endl;\n", - "\t\t\t}\n", - "\t\t\tfree(symbols);\n", - "\t\t}\n", - "\t\tstd::cerr << buffer.str();\n", - "\t\tstd::exit(EXIT_FAILURE);\n", - "\t}" + " std::ostringstream buffer;\n", + " buffer << \"Abort by \" << (sig == SIGSEGV ? \"segfault\" : \"bus error\") << std::endl;\n", + " void * stack[64];\n", + " std::size_t depth = backtrace(stack, 64);\n", + " if (!depth)\n", + " buffer << \" \" << std::endl;\n", + " else {\n", + " char ** symbols = backtrace_symbols(stack, depth);\n", + " for (std::size_t i = 1; i < depth; ++i) {\n", + " std::string symbol = symbols[i];\n", + " if (symbol.find_first_of(' ', 59) != std::string::npos) {\n", + " std::string name = symbol.substr(59, symbol.find_first_of(' ', 59) - 59);\n", + " int status;\n", + " char * demangled = abi::__cxa_demangle(name.c_str(), NULL, NULL, &status);\n", + " if (!status) {\n", + " buffer << \" \"\n", + " << symbol.substr(0, 59)\n", + " << demangled\n", + " << symbol.substr(59 + name.size())\n", + " << std::endl;\n", + " free(demangled);\n", + " } else\n", + " buffer << \" \" << symbol << std::endl;\n", + " } else\n", + " buffer << \" \" << symbol << std::endl;\n", + " }\n", + " free(symbols);\n", + " }\n", + " std::cerr << buffer.str();\n", + " std::exit(EXIT_FAILURE);\n", + " }\n" ] }, { @@ -1502,7 +1542,7 @@ "name": "stdout", "output_type": "stream", "text": [ - "Overwriting src/poly_opt.cpp\n" + "Writing src/poly_opt.cpp\n" ] } ], @@ -1669,7 +1709,7 @@ "name": "stdout", "output_type": "stream", "text": [ - "Overwriting src/pairwise.cpp\n" + "Writing src/pairwise.cpp\n" ] } ], @@ -1689,177 +1729,189 @@ "#include \n", "#include \n", "struct PyObj {\n", - "\ttypedef PyObject * ptr_t;\n", - "\ttypedef PyArrayObject * arrptr_t;\n", - "\tPyObj(): dec(false), ptr(NULL) {}\n", - "\tPyObj(ptr_t p): dec(false), ptr(p) {}\n", - "\t~PyObj() { if(dec) Py_DECREF(ptr); }\n", - "\tPyObj & operator=(ptr_t p) { if(dec) Py_DECREF(ptr); ptr = p; dec = false; return *this; }\n", - "\tPyObj & incref(ptr_t p) { if(dec) Py_DECREF(ptr); ptr = p; dec = (p != NULL); return *this; }\n", - "\toperator bool() const { return ptr; }\n", - "\toperator ptr_t() const { return ptr; }\n", - "\toperator arrptr_t() const { return (arrptr_t)ptr; }\n", - "\tbool dec;\n", - "\tptr_t ptr;\n", + " typedef PyObject * ptr_t;\n", + " typedef PyArrayObject * arrptr_t;\n", + " PyObj(): dec(false), ptr(NULL) {}\n", + " PyObj(ptr_t p): dec(false), ptr(p) {}\n", + " ~PyObj() { if(dec) Py_DECREF(ptr); }\n", + " PyObj & operator=(ptr_t p) { if(dec) Py_DECREF(ptr); ptr = p; dec = false; return *this; }\n", + " PyObj & incref(ptr_t p) { if(dec) Py_DECREF(ptr); ptr = p; dec = (p != NULL); return *this; }\n", + " operator bool() const { return ptr; }\n", + " operator ptr_t() const { return ptr; }\n", + " operator arrptr_t() const { return (arrptr_t)ptr; }\n", + " bool dec;\n", + " ptr_t ptr;\n", "};\n", "\n", - "inline void pairwise_d2d2JJ(PyObject * pX, \n", - "\t\t\t\t\t\t\t\t\t\t\t\t\t\tnpy_intp const * __restrict__ sX, \n", - "\t\t\t\t\t\t\t\t\t\t\t\t\t\tnpy_double * __restrict__ cX, \n", - "\t\t\t\t\t\t\t\t\t\t\t\t\t\tPyObject * pD, \n", - "\t\t\t\t\t\t\t\t\t\t\t\t\t\tnpy_intp const * __restrict__ sD, \n", - "\t\t\t\t\t\t\t\t\t\t\t\t\t\tnpy_double * __restrict__ cD, \n", - "\t\t\t\t\t\t\t\t\t\t\t\t\t\tnpy_int64 const cM, \n", - "\t\t\t\t\t\t\t\t\t\t\t\t\t\tnpy_int64 const cN);\n", - "\t\t\t\t\t\t\t\t\t\t\t\t\t\t\n", - "inline void pairwise_d2d2JJ(PyObject * pX, \n", - "\t\t\t\t\t\t\t\t\t\t\t\t\t\tnpy_intp const * __restrict__ sX, \n", - "\t\t\t\t\t\t\t\t\t\t\t\t\t\tnpy_double * __restrict__ cX, \n", - "\t\t\t\t\t\t\t\t\t\t\t\t\t\tPyObject * pD, \n", - "\t\t\t\t\t\t\t\t\t\t\t\t\t\tnpy_intp const * __restrict__ sD, \n", - "\t\t\t\t\t\t\t\t\t\t\t\t\t\tnpy_double * __restrict__ cD, \n", - "\t\t\t\t\t\t\t\t\t\t\t\t\t\tnpy_int64 const cM, \n", - "\t\t\t\t\t\t\t\t\t\t\t\t\t\tnpy_int64 const cN){\n", - "\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\n", - "\tnpy_double cd = 0.0;\n", - "\tnpy_double ctmp = 0.0;\n", - "\t\n", - "\tnpy_intp cj;\n", - "\tnpy_intp ck;\n", - "\tnpy_intp x_i_idx;\n", - "\tnpy_intp x_j_idx;\n", - "\tnpy_intp d_i_idx;\n", - "\tnpy_intp const xp = sX[1];\n", - "\tnpy_intp const dp = sD[1];\n", - "\t\n", - "\tfor (npy_intp ci = 0; ci < cM; ++ci) {\n", - "\t\tx_i_idx = ci*xp;\n", - "\t\td_i_idx = ci*dp;\n", - "\t\tfor (cj = 0; cj < cM; ++cj) {\n", - "\t\t\tcd = 0.0;\n", - "\t\t\tx_j_idx = cj*xp;\n", - "\t\t\tfor (ck = 0; ck < cN; ++ck) {\n", - "\t\t\t\tctmp = (cX[(x_i_idx + ck)] - cX[(x_j_idx + ck)]);\n", - "\t\t\t\tcd += (ctmp * ctmp);\n", - "\t\t\t}\n", - "\t\t\tcD[(d_i_idx + cj)] = std::sqrt(cd);\n", - "\t\t}\n", - "\t}\n", + "inline void pairwise_d2d2JJ(PyObject * pX,\n", + " npy_intp const * __restrict__ sX,\n", + " npy_double * __restrict__ cX,\n", + " PyObject * pD,\n", + " npy_intp const * __restrict__ sD,\n", + " npy_double * __restrict__ cD,\n", + " npy_int64 const cM,\n", + " npy_int64 const cN);\n", + "\n", + "inline void pairwise_d2d2JJ(PyObject * pX,\n", + " npy_intp const * __restrict__ sX,\n", + " npy_double * __restrict__ cX,\n", + " PyObject * pD,\n", + " npy_intp const * __restrict__ sD,\n", + " npy_double * __restrict__ cD,\n", + " npy_int64 const cM,\n", + " npy_int64 const cN){\n", + "\n", + " npy_double cd = 0.0;\n", + " npy_double ctmp = 0.0;\n", + "\n", + " npy_intp cj;\n", + " npy_intp ck;\n", + " npy_intp x_i_idx;\n", + " npy_intp x_j_idx;\n", + " npy_intp d_i_idx;\n", + " npy_intp const xp = sX[1];\n", + " npy_intp const dp = sD[1];\n", + "\n", + " for (npy_intp ci = 0; ci < cM; ++ci) {\n", + " x_i_idx = ci*xp;\n", + " d_i_idx = ci*dp;\n", + " for (cj = 0; cj < cM; ++cj) {\n", + " cd = 0.0;\n", + " x_j_idx = cj*xp;\n", + " for (ck = 0; ck < cN; ++ck) {\n", + " ctmp = (cX[(x_i_idx + ck)] - cX[(x_j_idx + ck)]);\n", + " cd += (ctmp * ctmp);\n", + " }\n", + " cD[(d_i_idx + cj)] = std::sqrt(cd);\n", + " }\n", + " }\n", "}\n", "\n", "void sighandler(int sig);\n", "#include \n", "extern \"C\" {\n", - "\tPyObject * create_signature;\n", - "\tstruct sigaction slot;\n", - "\tPyObject * set_create_signature(PyObject * self, PyObject * args) {\n", - "\t\tif (!PyArg_ParseTuple(args, \"O\", &create_signature)) {\n", - "\t\t\tPyErr_SetString(PyExc_ValueError, \"Invalid Argument to set_create_signature!\");\n", - "\t\t\treturn NULL;\n", - "\t\t}\n", - "\t\tPy_INCREF(create_signature);\n", - "\t\tmemset(&slot, 0, sizeof(slot));\n", - "\t\tslot.sa_handler = &sighandler;\n", - "\t\tsigaction(SIGSEGV, &slot, NULL);\n", - "\t\tsigaction(SIGBUS, &slot, NULL);\n", - "\t\tPy_INCREF(Py_None);\n", - "\t\treturn Py_None;\n", - "\t}\n", - "\tPyObject * run(PyObject * self, PyObject * args) {\n", - "\t\t{\n", - "\t\t\tPyObj pX;\n", - "\t\t\tPyObj pD;\n", - "\t\t\tPyObject * pM; npy_int64 cM;\n", - "\t\t\tPyObject * pN; npy_int64 cN;\n", - "\t\t\tif (\n", - "\t\t\t\tPyTuple_CheckExact(args) and PyTuple_GET_SIZE(args) == 4\n", - "\t\t\t\tand (pX = PyTuple_GET_ITEM(args, 0)) and PyArray_CheckExact(pX)\n", - "\t\t\t\tand PyArray_TYPE((PyArrayObject *)pX) == NPY_FLOAT64 and PyArray_NDIM((PyArrayObject *)pX) == 2\n", - "\t\t\t\tand (pD = PyTuple_GET_ITEM(args, 1)) and PyArray_CheckExact(pD)\n", - "\t\t\t\tand PyArray_TYPE((PyArrayObject *)pD) == NPY_FLOAT64 and PyArray_NDIM((PyArrayObject *)pD) == 2\n", - "\t\t\t\tand (pM = PyTuple_GET_ITEM(args, 2)) and PyInt_CheckExact(pM)\n", - "\t\t\t\tand (pN = PyTuple_GET_ITEM(args, 3)) and PyInt_CheckExact(pN)\n", - "\t\t\t) {\n", - "\t\t\t\tif (!(pX.incref((PyObject *)PyArray_GETCONTIGUOUS((PyArrayObject *)pX)))) {\n", - "\t\t\t\t\tPyErr_SetString(PyExc_ValueError, \"Invalid Argument type on X!\");\n", - "\t\t\t\t\treturn NULL;\n", - "\t\t\t\t}\n", - "\t\t\t\tif (!(pD.incref((PyObject *)PyArray_GETCONTIGUOUS((PyArrayObject *)pD)))) {\n", - "\t\t\t\t\tPyErr_SetString(PyExc_ValueError, \"Invalid Argument type on D!\");\n", - "\t\t\t\t\treturn NULL;\n", - "\t\t\t\t}\n", - "\t\t\t\tcM = PyInt_AS_LONG(pM);\n", - "\t\t\t\tcN = PyInt_AS_LONG(pN);\n", - "\t\t\t\ttry {\n", - "\t\t\t\t\tpairwise_d2d2JJ(\n", - "\t\t\t\t\t\t pX, PyArray_SHAPE((PyArrayObject *)pX), (npy_double *)PyArray_DATA((PyArrayObject *)pX)\n", - "\t\t\t\t\t\t, pD, PyArray_SHAPE((PyArrayObject *)pD), (npy_double *)PyArray_DATA((PyArrayObject *)pD)\n", - "\t\t\t\t\t\t, cM\n", - "\t\t\t\t\t\t, cN\n", - "\t\t\t\t\t);\n", - "\t\t\t\t\tPy_INCREF(Py_None);\n", - "\t\t\t\t\treturn Py_None;\n", - "\t\t\t\t} catch (...) {\n", - "\t\t\t\t\treturn NULL;\n", - "\t\t\t\t}\n", - "\t\t\t} else\n", - "\t\t\t\tPyErr_Clear();\n", - "\t\t}\n", - "\t\tPyObject * signatures = Py_BuildValue(\"(sO)\", \"(lp0\\n(lp1\\nccopy_reg\\n_reconstructor\\np2\\n(chope._ast\\nVariable\\np3\\nc__builtin__\\nobject\\np4\\nNtp5\\nRp6\\n(dp7\\nS'name'\\np8\\nS'X'\\np9\\nsS'dtype'\\np10\\nS'float64'\\np11\\nsS'dims'\\np12\\nI2\\nsbag2\\n(g3\\ng4\\nNtp13\\nRp14\\n(dp15\\ng8\\nS'D'\\np16\\nsg10\\ng11\\nsg12\\nI2\\nsbag2\\n(g3\\ng4\\nNtp17\\nRp18\\n(dp19\\ng8\\nS'M'\\np20\\nsg10\\nc__builtin__\\nint\\np21\\nsg12\\nI0\\nsbag2\\n(g3\\ng4\\nNtp22\\nRp23\\n(dp24\\ng8\\nS'N'\\np25\\nsg10\\ng21\\nsg12\\nI0\\nsbaa.\", args);\n", - "\t\tif (!signatures) {\n", - "\t\t\tPyErr_SetString(PyExc_ValueError, \"Error building signature string for pairwise\");\n", - "\t\t\treturn NULL;\n", - "\t\t}\n", - "\t\treturn PyObject_Call(create_signature, signatures, NULL);\n", - "\t}\n", - "\tPyMethodDef pairwiseMethods[] = {\n", - "\t\t{ \"set_create_signature\", (PyCFunction)set_create_signature, METH_VARARGS },\n", - "\t\t{ \"run\", (PyCFunction)run, METH_VARARGS },\n", - "\t\t{ NULL, NULL }\n", - "\t};\n", - "\tPyMODINIT_FUNC initpairwise(void) {\n", - "\t\timport_array();\n", - "\t\tPyImport_ImportModule(\"numpy\");\n", - "\t\t(void)Py_InitModule(\"pairwise\", pairwiseMethods);\n", - "\t}\n", - "}\n", - "#include \n", - "#include \n", - "#include \n", - "#include \n", - "#include \n", - "void sighandler(int sig) {\n", - "\tstd::ostringstream buffer;\n", - "\tbuffer << \"Abort by \" << (sig == SIGSEGV ? \"segfault\" : \"bus error\") << std::endl;\n", - "\tvoid * stack[64];\n", - "\tstd::size_t depth = backtrace(stack, 64);\n", - "\tif (!depth)\n", - "\t\tbuffer << \" \" << std::endl;\n", - "\telse {\n", - "\t\tchar ** symbols = backtrace_symbols(stack, depth);\n", - "\t\tfor (std::size_t i = 1; i < depth; ++i) {\n", - "\t\t\tstd::string symbol = symbols[i];\n", - "\t\t\t\tif (symbol.find_first_of(' ', 59) != std::string::npos) {\n", - "\t\t\t\t\tstd::string name = symbol.substr(59, symbol.find_first_of(' ', 59) - 59);\n", - "\t\t\t\t\tint status;\n", - "\t\t\t\t\tchar * demangled = abi::__cxa_demangle(name.c_str(), NULL, NULL, &status);\n", - "\t\t\t\t\tif (!status) {\n", - "\t\t\t\t\t\tbuffer << \" \" \n", - "\t\t\t\t\t\t\t<< symbol.substr(0, 59) \n", - "\t\t\t\t\t\t\t<< demangled\n", - "\t\t\t\t\t\t\t<< symbol.substr(59 + name.size())\n", - "\t\t\t\t\t\t\t<< std::endl;\n", - "\t\t\t\t\t\tfree(demangled);\n", - "\t\t\t\t\t} else\n", - "\t\t\t\t\t\tbuffer << \" \" << symbol << std::endl;\n", - "\t\t\t\t} else\n", - "\t\t\t\t\tbuffer << \" \" << symbol << std::endl;\n", - "\t\t\t}\n", - "\t\t\tfree(symbols);\n", - "\t\t}\n", - "\t\tstd::cerr << buffer.str();\n", - "\t\tstd::exit(EXIT_FAILURE);\n", - "\t}\n" + " PyObject * create_signature;\n", + " struct sigaction slot;\n", + " PyObject * set_create_signature(PyObject * self, PyObject * args) {\n", + " if (!PyArg_ParseTuple(args, \"O\", &create_signature)) {\n", + " PyErr_SetString(PyExc_ValueError, \"Invalid Argument to set_create_signature!\");\n", + " return NULL;\n", + " }\n", + " Py_INCREF(create_signature);\n", + " memset(&slot, 0, sizeof(slot));\n", + " slot.sa_handler = &sighandler;\n", + " sigaction(SIGSEGV, &slot, NULL);\n", + " sigaction(SIGBUS, &slot, NULL);\n", + " Py_INCREF(Py_None);\n", + " return Py_None;\n", + " }\n", + " PyObject * run(PyObject * self, PyObject * args) {\n", + " {\n", + " PyObj pX;\n", + " PyObj pD;\n", + " PyObject * pM; npy_int64 cM;\n", + " PyObject * pN; npy_int64 cN;\n", + " if (\n", + " PyTuple_CheckExact(args) and PyTuple_GET_SIZE(args) == 4\n", + " and (pX = PyTuple_GET_ITEM(args, 0)) and PyArray_CheckExact(pX)\n", + " and PyArray_TYPE((PyArrayObject *)pX) == NPY_FLOAT64 and PyArray_NDIM((PyArrayObject *)pX) == 2\n", + " and (pD = PyTuple_GET_ITEM(args, 1)) and PyArray_CheckExact(pD)\n", + " and PyArray_TYPE((PyArrayObject *)pD) == NPY_FLOAT64 and PyArray_NDIM((PyArrayObject *)pD) == 2\n", + " and (pM = PyTuple_GET_ITEM(args, 2)) and PyLong_CheckExact(pM)\n", + " and (pN = PyTuple_GET_ITEM(args, 3)) and PyLong_CheckExact(pN)\n", + " ) {\n", + " if (!(pX.incref((PyObject *)PyArray_GETCONTIGUOUS((PyArrayObject *)pX)))) {\n", + " PyErr_SetString(PyExc_ValueError, \"Invalid Argument type on X!\");\n", + " return NULL;\n", + " }\n", + " if (!(pD.incref((PyObject *)PyArray_GETCONTIGUOUS((PyArrayObject *)pD)))) {\n", + " PyErr_SetString(PyExc_ValueError, \"Invalid Argument type on D!\");\n", + " return NULL;\n", + " }\n", + " cM = PyLong_AS_LONG(pM);\n", + " cN = PyLong_AS_LONG(pN);\n", + " try {\n", + " pairwise_d2d2JJ(\n", + " pX, PyArray_SHAPE((PyArrayObject *)pX), (npy_double *)PyArray_DATA((PyArrayObject *)pX)\n", + " , pD, PyArray_SHAPE((PyArrayObject *)pD), (npy_double *)PyArray_DATA((PyArrayObject *)pD)\n", + " , cM\n", + " , cN\n", + " );\n", + " Py_INCREF(Py_None);\n", + " return Py_None;\n", + " } catch (...) {\n", + " return NULL;\n", + " }\n", + " } else\n", + " PyErr_Clear();\n", + " }\n", + " PyObject * signatures = Py_BuildValue(\"(sO)\", \"(lp0\\n(lp1\\nccopy_reg\\n_reconstructor\\np2\\n(chope._ast\\nVariable\\np3\\nc__builtin__\\nobject\\np4\\nNtp5\\nRp6\\n(dp7\\nS'name'\\np8\\nS'X'\\np9\\nsS'dtype'\\np10\\nS'float64'\\np11\\nsS'dims'\\np12\\nI2\\nsbag2\\n(g3\\ng4\\nNtp13\\nRp14\\n(dp15\\ng8\\nS'D'\\np16\\nsg10\\ng11\\nsg12\\nI2\\nsbag2\\n(g3\\ng4\\nNtp17\\nRp18\\n(dp19\\ng8\\nS'M'\\np20\\nsg10\\nc__builtin__\\nint\\np21\\nsg12\\nI0\\nsbag2\\n(g3\\ng4\\nNtp22\\nRp23\\n(dp24\\ng8\\nS'N'\\np25\\nsg10\\ng21\\nsg12\\nI0\\nsbaa.\", args);\n", + " if (!signatures) {\n", + " PyErr_SetString(PyExc_ValueError, \"Error building signature string for pairwise\");\n", + " return NULL;\n", + " }\n", + " return PyObject_Call(create_signature, signatures, NULL);\n", + " }\n", + "\n", + " PyMethodDef pairwiseMethods[] = {\n", + " { \"set_create_signature\", set_create_signature, METH_VARARGS, \"signal handler\" },\n", + " { \"run\", run, METH_VARARGS, \"module function\" },\n", + " { NULL, NULL, 0, NULL }\n", + " };\n", + "\n", + "\n", + " static struct PyModuleDef pairwisemodule = {\n", + " PyModuleDef_HEAD_INIT,\n", + " \"pairwise\",\n", + " NULL,\n", + " -1,\n", + " pairwiseMethods\n", + " };\n", + "\n", + "\n", + " PyMODINIT_FUNC PyInit_pairwise(void) {\n", + " import_array();\n", + " PyImport_ImportModule(\"numpy\");\n", + " return PyModule_Create(&pairwisemodule);\n", + " }\n", + "}\n", + "#include \n", + "#include \n", + "#include \n", + "#include \n", + "#include \n", + "void sighandler(int sig) {\n", + " std::ostringstream buffer;\n", + " buffer << \"Abort by \" << (sig == SIGSEGV ? \"segfault\" : \"bus error\") << std::endl;\n", + " void * stack[64];\n", + " std::size_t depth = backtrace(stack, 64);\n", + " if (!depth)\n", + " buffer << \" \" << std::endl;\n", + " else {\n", + " char ** symbols = backtrace_symbols(stack, depth);\n", + " for (std::size_t i = 1; i < depth; ++i) {\n", + " std::string symbol = symbols[i];\n", + " if (symbol.find_first_of(' ', 59) != std::string::npos) {\n", + " std::string name = symbol.substr(59, symbol.find_first_of(' ', 59) - 59);\n", + " int status;\n", + " char * demangled = abi::__cxa_demangle(name.c_str(), NULL, NULL, &status);\n", + " if (!status) {\n", + " buffer << \" \"\n", + " << symbol.substr(0, 59)\n", + " << demangled\n", + " << symbol.substr(59 + name.size())\n", + " << std::endl;\n", + " free(demangled);\n", + " } else\n", + " buffer << \" \" << symbol << std::endl;\n", + " } else\n", + " buffer << \" \" << symbol << std::endl;\n", + " }\n", + " free(symbols);\n", + " }\n", + " std::cerr << buffer.str();\n", + " std::exit(EXIT_FAILURE);\n", + " }\n" ] }, { @@ -1871,7 +1923,7 @@ }, { "cell_type": "code", - "execution_count": 14, + "execution_count": 17, "metadata": {}, "outputs": [ { @@ -1898,208 +1950,218 @@ "#include \n", "#include \n", "struct PyObj {\n", - "\ttypedef PyObject * ptr_t;\n", - "\ttypedef PyArrayObject * arrptr_t;\n", - "\tPyObj(): dec(false), ptr(NULL) {}\n", - "\tPyObj(ptr_t p): dec(false), ptr(p) {}\n", - "\t~PyObj() { if(dec) Py_DECREF(ptr); }\n", - "\tPyObj & operator=(ptr_t p) { if(dec) Py_DECREF(ptr); ptr = p; dec = false; return *this; }\n", - "\tPyObj & incref(ptr_t p) { if(dec) Py_DECREF(ptr); ptr = p; dec = (p != NULL); return *this; }\n", - "\toperator bool() const { return ptr; }\n", - "\toperator ptr_t() const { return ptr; }\n", - "\toperator arrptr_t() const { return (arrptr_t)ptr; }\n", - "\tbool dec;\n", - "\tptr_t ptr;\n", + " typedef PyObject * ptr_t;\n", + " typedef PyArrayObject * arrptr_t;\n", + " PyObj(): dec(false), ptr(NULL) {}\n", + " PyObj(ptr_t p): dec(false), ptr(p) {}\n", + " ~PyObj() { if(dec) Py_DECREF(ptr); }\n", + " PyObj & operator=(ptr_t p) { if(dec) Py_DECREF(ptr); ptr = p; dec = false; return *this; }\n", + " PyObj & incref(ptr_t p) { if(dec) Py_DECREF(ptr); ptr = p; dec = (p != NULL); return *this; }\n", + " operator bool() const { return ptr; }\n", + " operator ptr_t() const { return ptr; }\n", + " operator arrptr_t() const { return (arrptr_t)ptr; }\n", + " bool dec;\n", + " ptr_t ptr;\n", "};\n", "\n", "inline std::tuple pdf_f2l1d1f2JDd(\n", - " \tPyObject * pdensity\n", + " PyObject * pdensity\n", " , npy_intp const * __restrict__ sdensity\n", " , npy_float * __restrict__ cdensity\n", - "\t, PyObject * pdims\n", - "\t, npy_intp const * __restrict__ sdims\n", - "\t, npy_int64 * __restrict__ cdims\n", - "\t, PyObject * pcenter\n", - "\t, npy_intp const * __restrict__ scenter\n", - "\t, npy_double * __restrict__ ccenter\n", - "\t, PyObject * pw2D\n", - "\t, npy_intp const * __restrict__ sw2D\n", - "\t, npy_float * __restrict__ cw2D\n", - "\t, npy_int64 cr50\n", - "\t, npy_double cb\n", - "\t, npy_double ca);\n", + " , PyObject * pdims\n", + " , npy_intp const * __restrict__ sdims\n", + " , npy_int64 * __restrict__ cdims\n", + " , PyObject * pcenter\n", + " , npy_intp const * __restrict__ scenter\n", + " , npy_double * __restrict__ ccenter\n", + " , PyObject * pw2D\n", + " , npy_intp const * __restrict__ sw2D\n", + " , npy_float * __restrict__ cw2D\n", + " , npy_int64 cr50\n", + " , npy_double cb\n", + " , npy_double ca);\n", "\n", "inline std::tuple pdf_f2l1d1f2JDd(\n", - " \tPyObject * pdensity\n", + " PyObject * pdensity\n", " , npy_intp const * __restrict__ sdensity\n", " , npy_float * __restrict__ cdensity\n", - "\t, PyObject * pdims\n", - "\t, npy_intp const * __restrict__ sdims\n", - "\t, npy_int64 * __restrict__ cdims\n", - "\t, PyObject * pcenter\n", - "\t, npy_intp const * __restrict__ scenter\n", - "\t, npy_double * __restrict__ ccenter\n", - "\t, PyObject * pw2D\n", - "\t, npy_intp const * __restrict__ sw2D\n", - "\t, npy_float * __restrict__ cw2D\n", - "\t, npy_int64 const cr50\n", - "\t, npy_double const cb\n", - "\t, npy_double const ca) {\n", - "\t\n", - "\tnpy_double cdr;\n", - "\tnpy_double c__sum0;\n", - "\tconst npy_double x_center = ccenter[0];\n", - "\tconst npy_double y_center = ccenter[1];\n", - "\tconst npy_intp len_0_sw2D = sw2D[0];\n", - "\tconst npy_intp len_1_sw2D = sw2D[1];\n", - "\tnpy_intp dc[] = {len_0_sw2D, len_1_sw2D};\n", - "\tPyObject * pc = PyArray_EMPTY(2, dc, NPY_FLOAT64, 0);\n", - "\tnpy_intp * sc = PyArray_SHAPE((PyArrayObject *)pc);\n", - "\tnpy_double * cc = (npy_double *)PyArray_DATA((PyArrayObject *)pc);\n", - "\t\n", - "\tnpy_intp cy = 0;\n", - "\tnpy_intp i0 = 0;\n", - "\tnpy_intp i1 = 0;\n", - "\tnpy_intp i2 = 0;\n", - "\tnpy_intp i3 = 0;\n", - "\t\n", - "\tnpy_intp sw2D_i0_idx;\n", - "\tnpy_intp sw2D_i2_idx;\n", - "\tnpy_intp density_x_idx;\n", - "\t\n", - "\tauto c__sp0 = ca * ca;\n", - "\tauto c__sp1 = cr50 * cr50;\n", - "\tauto c__sp2 = 1.0 / (c__sp0 * c__sp1);\n", - "\tauto c__sp4 = 0.3183098861846737 * c__sp2 * (-1 + cb);\n", - "\n", - "\tfor (npy_intp cx = 0; cx < cdims[(int)(0)]; ++cx) {\n", - "\t\t\n", - "\t\tdensity_x_idx = cx*sdensity[1];\n", - "\t\tfor (cy = 0; cy < cdims[(int)(1)]; ++cy) {\n", - "\t\t\t\n", - "\t\t\tcdr = std::sqrt(std::pow(cx - x_center, 2) + std::pow(cy - y_center, 2));\n", - "\t\t\tauto c__sp3 = cdr * cdr;\n", - "\t\t\tauto c__sp5 = c__sp4 * std::pow((1 + c__sp2 * c__sp3), -cb);\n", - "\t\t\t\n", - "\t\t\tfor (i0 = 0; i0 < len_0_sw2D - 0; ++i0) {\n", - "\t\t\t\t\n", - "\t\t\t\tsw2D_i0_idx = (i0)*len_1_sw2D;\n", - "\t\t\t\tfor (i1 = 0; i1 < len_1_sw2D - 0; ++i1) {\n", - "\t\t\t\t\tcc[sw2D_i0_idx + i1] = c__sp5 * cw2D[sw2D_i0_idx + i1];\n", - "\t\t\t\t}\n", - "\t\t\t}\n", - "\t\t\t\n", - "\t\t\tc__sum0 = 0;\n", - "\t\t\tfor (i2 = 0; i2 < len_0_sw2D - 0; ++i2) {\n", - "\t\t\t\t\n", - "\t\t\t\tsw2D_i2_idx = (i2)*len_1_sw2D;\n", - "\t\t\t\tfor (i3 = 0; i3 < len_1_sw2D - 0; ++i3) {\n", - "\t\t\t\t\tc__sum0 += cc[sw2D_i2_idx + i3];\n", - "\t\t\t\t}\n", - "\t\t\t}\n", - "\t\t\t\n", - "\t\t\tcdensity[(density_x_idx + cy)] = c__sum0;\n", - "\t\t}\n", - "\t}\n", - "\treturn std::make_tuple((PyObject *)pdensity, sdensity, cdensity);\n", + " , PyObject * pdims\n", + " , npy_intp const * __restrict__ sdims\n", + " , npy_int64 * __restrict__ cdims\n", + " , PyObject * pcenter\n", + " , npy_intp const * __restrict__ scenter\n", + " , npy_double * __restrict__ ccenter\n", + " , PyObject * pw2D\n", + " , npy_intp const * __restrict__ sw2D\n", + " , npy_float * __restrict__ cw2D\n", + " , npy_int64 const cr50\n", + " , npy_double const cb\n", + " , npy_double const ca) {\n", + "\n", + " npy_double cdr;\n", + " npy_double c__sum0;\n", + " const npy_double x_center = ccenter[0];\n", + " const npy_double y_center = ccenter[1];\n", + " const npy_intp len_0_sw2D = sw2D[0];\n", + " const npy_intp len_1_sw2D = sw2D[1];\n", + " npy_intp dc[] = {len_0_sw2D, len_1_sw2D};\n", + " PyObject * pc = PyArray_EMPTY(2, dc, NPY_FLOAT64, 0);\n", + " npy_intp * sc = PyArray_SHAPE((PyArrayObject *)pc);\n", + " npy_double * cc = (npy_double *)PyArray_DATA((PyArrayObject *)pc);\n", + "\n", + " npy_intp cy = 0;\n", + " npy_intp i0 = 0;\n", + " npy_intp i1 = 0;\n", + " npy_intp i2 = 0;\n", + " npy_intp i3 = 0;\n", + "\n", + " npy_intp sw2D_i0_idx;\n", + " npy_intp sw2D_i2_idx;\n", + " npy_intp density_x_idx;\n", + "\n", + " auto c__sp0 = ca * ca;\n", + " auto c__sp1 = cr50 * cr50;\n", + " auto c__sp2 = 1.0 / (c__sp0 * c__sp1);\n", + " auto c__sp4 = 0.3183098861846737 * c__sp2 * (-1 + cb);\n", + "\n", + " for (npy_intp cx = 0; cx < cdims[(int)(0)]; ++cx) {\n", + "\n", + " density_x_idx = cx*sdensity[1];\n", + " for (cy = 0; cy < cdims[(int)(1)]; ++cy) {\n", + "\n", + " cdr = std::sqrt(std::pow(cx - x_center, 2) + std::pow(cy - y_center, 2));\n", + " auto c__sp3 = cdr * cdr;\n", + " auto c__sp5 = c__sp4 * std::pow((1 + c__sp2 * c__sp3), -cb);\n", + "\n", + " for (i0 = 0; i0 < len_0_sw2D - 0; ++i0) {\n", + "\n", + " sw2D_i0_idx = (i0)*len_1_sw2D;\n", + " for (i1 = 0; i1 < len_1_sw2D - 0; ++i1) {\n", + " cc[sw2D_i0_idx + i1] = c__sp5 * cw2D[sw2D_i0_idx + i1];\n", + " }\n", + " }\n", + "\n", + " c__sum0 = 0;\n", + " for (i2 = 0; i2 < len_0_sw2D - 0; ++i2) {\n", + "\n", + " sw2D_i2_idx = (i2)*len_1_sw2D;\n", + " for (i3 = 0; i3 < len_1_sw2D - 0; ++i3) {\n", + " c__sum0 += cc[sw2D_i2_idx + i3];\n", + " }\n", + " }\n", + "\n", + " cdensity[(density_x_idx + cy)] = c__sum0;\n", + " }\n", + " }\n", + " return std::make_tuple((PyObject *)pdensity, sdensity, cdensity);\n", "}\n", "\n", "void sighandler(int sig);\n", "#include \n", "extern \"C\" {\n", - "\tPyObject * create_signature;\n", - "\tstruct sigaction slot;\n", - "\tPyObject * set_create_signature(PyObject * self, PyObject * args) {\n", - "\t\tif (!PyArg_ParseTuple(args, \"O\", &create_signature)) {\n", - "\t\t\tPyErr_SetString(PyExc_ValueError, \"Invalid Argument to set_create_signature!\");\n", - "\t\t\treturn NULL;\n", - "\t\t}\n", - "\t\tPy_INCREF(create_signature);\n", - "\t\tmemset(&slot, 0, sizeof(slot));\n", - "\t\tslot.sa_handler = &sighandler;\n", - "\t\tsigaction(SIGSEGV, &slot, NULL);\n", - "\t\tsigaction(SIGBUS, &slot, NULL);\n", - "\t\tPy_INCREF(Py_None);\n", - "\t\treturn Py_None;\n", - "\t}\n", - "\tPyObject * run(PyObject * self, PyObject * args) {\n", - "\t\t{\n", - "\t\t\tPyObj pdensity;\n", - "\t\t\tPyObj pdims;\n", - "\t\t\tPyObj pcenter;\n", - "\t\t\tPyObj pw2D;\n", - "\t\t\tPyObject * pr50; npy_int64 cr50;\n", - "\t\t\tPyObject * pb; npy_double cb;\n", - "\t\t\tPyObject * pa; npy_double ca;\n", - "\t\t\tif (\n", - "\t\t\t\tPyTuple_CheckExact(args) and PyTuple_GET_SIZE(args) == 7\n", - "\t\t\t\tand (pdensity = PyTuple_GET_ITEM(args, 0)) and PyArray_CheckExact(pdensity)\n", - "\t\t\t\tand PyArray_TYPE((PyArrayObject *)pdensity) == NPY_FLOAT32 and PyArray_NDIM((PyArrayObject *)pdensity) == 2\n", - "\t\t\t\tand (pdims = PyTuple_GET_ITEM(args, 1)) and PyArray_CheckExact(pdims)\n", - "\t\t\t\tand PyArray_TYPE((PyArrayObject *)pdims) == NPY_INT64 and PyArray_NDIM((PyArrayObject *)pdims) == 1\n", - "\t\t\t\tand (pcenter = PyTuple_GET_ITEM(args, 2)) and PyArray_CheckExact(pcenter)\n", - "\t\t\t\tand PyArray_TYPE((PyArrayObject *)pcenter) == NPY_FLOAT64 and PyArray_NDIM((PyArrayObject *)pcenter) == 1\n", - "\t\t\t\tand (pw2D = PyTuple_GET_ITEM(args, 3)) and PyArray_CheckExact(pw2D)\n", - "\t\t\t\tand PyArray_TYPE((PyArrayObject *)pw2D) == NPY_FLOAT32 and PyArray_NDIM((PyArrayObject *)pw2D) == 2\n", - "\t\t\t\tand (pr50 = PyTuple_GET_ITEM(args, 4)) and PyInt_CheckExact(pr50)\n", - "\t\t\t\tand (pb = PyTuple_GET_ITEM(args, 5)) and PyFloat_CheckExact(pb)\n", - "\t\t\t\tand (pa = PyTuple_GET_ITEM(args, 6)) and PyArray_IsScalar(pa, Double)\n", - "\t\t\t) {\n", - "\t\t\t\tif (!(pdensity.incref((PyObject *)PyArray_GETCONTIGUOUS((PyArrayObject *)pdensity)))) {\n", - "\t\t\t\t\tPyErr_SetString(PyExc_ValueError, \"Invalid Argument type on density!\");\n", - "\t\t\t\t\treturn NULL;\n", - "\t\t\t\t}\n", - "\t\t\t\tif (!(pdims.incref((PyObject *)PyArray_GETCONTIGUOUS((PyArrayObject *)pdims)))) {\n", - "\t\t\t\t\tPyErr_SetString(PyExc_ValueError, \"Invalid Argument type on dims!\");\n", - "\t\t\t\t\treturn NULL;\n", - "\t\t\t\t}\n", - "\t\t\t\tif (!(pcenter.incref((PyObject *)PyArray_GETCONTIGUOUS((PyArrayObject *)pcenter)))) {\n", - "\t\t\t\t\tPyErr_SetString(PyExc_ValueError, \"Invalid Argument type on center!\");\n", - "\t\t\t\t\treturn NULL;\n", - "\t\t\t\t}\n", - "\t\t\t\tif (!(pw2D.incref((PyObject *)PyArray_GETCONTIGUOUS((PyArrayObject *)pw2D)))) {\n", - "\t\t\t\t\tPyErr_SetString(PyExc_ValueError, \"Invalid Argument type on w2D!\");\n", - "\t\t\t\t\treturn NULL;\n", - "\t\t\t\t}\n", - "\t\t\t\tcr50 = PyInt_AS_LONG(pr50);\n", - "\t\t\t\tcb = PyFloat_AS_DOUBLE(pb);\n", - "\t\t\t\tca = PyArrayScalar_VAL(pa, Double);\n", - "\t\t\t\ttry {\n", - "\t\t\t\t\tPyObject * res = std::get<0>(pdf_f2l1d1f2JDd(\n", - "\t\t\t\t\t\t pdensity, PyArray_SHAPE((PyArrayObject *)pdensity), (npy_float *)PyArray_DATA((PyArrayObject *)pdensity)\n", - "\t\t\t\t\t\t, pdims, PyArray_SHAPE((PyArrayObject *)pdims), (npy_int64 *)PyArray_DATA((PyArrayObject *)pdims)\n", - "\t\t\t\t\t\t, pcenter, PyArray_SHAPE((PyArrayObject *)pcenter), (npy_double *)PyArray_DATA((PyArrayObject *)pcenter)\n", - "\t\t\t\t\t\t, pw2D, PyArray_SHAPE((PyArrayObject *)pw2D), (npy_float *)PyArray_DATA((PyArrayObject *)pw2D)\n", - "\t\t\t\t\t\t, cr50\n", - "\t\t\t\t\t\t, cb\n", - "\t\t\t\t\t\t, ca\n", - "\t\t\t\t\t));\n", - "\n", - "\t\t\t\t\tPy_INCREF(res);\n", - "\t\t\t\t\treturn res;\n", - "\t\t\t\t} catch (...) {\n", - "\t\t\t\t\treturn NULL;\n", - "\t\t\t\t}\n", - "\t\t\t} else\n", - "\t\t\t\tPyErr_Clear();\n", - "\t\t}\n", - "\t\tPyObject * signatures = Py_BuildValue(\"(sO)\", \"(lp0\\n(lp1\\nccopy_reg\\n_reconstructor\\np2\\n(chope._ast\\nVariable\\np3\\nc__builtin__\\nobject\\np4\\nNtp5\\nRp6\\n(dp7\\nS'name'\\np8\\nS'density'\\np9\\nsS'dtype'\\np10\\nS'float32'\\np11\\nsS'dims'\\np12\\nI2\\nsbag2\\n(g3\\ng4\\nNtp13\\nRp14\\n(dp15\\ng8\\ng12\\nsg10\\nS'int64'\\np16\\nsg12\\nI1\\nsbag2\\n(g3\\ng4\\nNtp17\\nRp18\\n(dp19\\ng8\\nS'center'\\np20\\nsg10\\nS'float64'\\np21\\nsg12\\nI1\\nsbag2\\n(g3\\ng4\\nNtp22\\nRp23\\n(dp24\\ng8\\nS'w2D'\\np25\\nsg10\\ng11\\nsg12\\nI2\\nsbag2\\n(g3\\ng4\\nNtp26\\nRp27\\n(dp28\\ng8\\nS'r50'\\np29\\nsg10\\nc__builtin__\\nint\\np30\\nsg12\\nI0\\nsbag2\\n(g3\\ng4\\nNtp31\\nRp32\\n(dp33\\ng8\\nS'b'\\np34\\nsg10\\nc__builtin__\\nfloat\\np35\\nsg12\\nI0\\nsbag2\\n(g3\\ng4\\nNtp36\\nRp37\\n(dp38\\ng8\\nS'a'\\np39\\nsg10\\ng21\\nsg12\\nI0\\nsbaa.\", args);\n", - "\t\tif (!signatures) {\n", - "\t\t\tPyErr_SetString(PyExc_ValueError, \"Error building signature string for pdf\");\n", - "\t\t\treturn NULL;\n", - "\t\t}\n", - "\t\treturn PyObject_Call(create_signature, signatures, NULL);\n", - "\t}\n", - "\tPyMethodDef pdfMethods[] = {\n", - "\t\t{ \"set_create_signature\", (PyCFunction)set_create_signature, METH_VARARGS },\n", - "\t\t{ \"run\", (PyCFunction)run, METH_VARARGS },\n", - "\t\t{ NULL, NULL }\n", - "\t};\n", - "\tPyMODINIT_FUNC initpdf(void) {\n", - "\t\timport_array();\n", - "\t\tPyImport_ImportModule(\"numpy\");\n", - "\t\t(void)Py_InitModule(\"pdf\", pdfMethods);\n", - "\t}\n", + " PyObject * create_signature;\n", + " struct sigaction slot;\n", + " PyObject * set_create_signature(PyObject * self, PyObject * args) {\n", + " if (!PyArg_ParseTuple(args, \"O\", &create_signature)) {\n", + " PyErr_SetString(PyExc_ValueError, \"Invalid Argument to set_create_signature!\");\n", + " return NULL;\n", + " }\n", + " Py_INCREF(create_signature);\n", + " memset(&slot, 0, sizeof(slot));\n", + " slot.sa_handler = &sighandler;\n", + " sigaction(SIGSEGV, &slot, NULL);\n", + " sigaction(SIGBUS, &slot, NULL);\n", + " Py_INCREF(Py_None);\n", + " return Py_None;\n", + " }\n", + " PyObject * run(PyObject * self, PyObject * args) {\n", + " {\n", + " PyObj pdensity;\n", + " PyObj pdims;\n", + " PyObj pcenter;\n", + " PyObj pw2D;\n", + " PyObject * pr50; npy_int64 cr50;\n", + " PyObject * pb; npy_double cb;\n", + " PyObject * pa; npy_double ca;\n", + " if (\n", + " PyTuple_CheckExact(args) and PyTuple_GET_SIZE(args) == 7\n", + " and (pdensity = PyTuple_GET_ITEM(args, 0)) and PyArray_CheckExact(pdensity)\n", + " and PyArray_TYPE((PyArrayObject *)pdensity) == NPY_FLOAT32 and PyArray_NDIM((PyArrayObject *)pdensity) == 2\n", + " and (pdims = PyTuple_GET_ITEM(args, 1)) and PyArray_CheckExact(pdims)\n", + " and PyArray_TYPE((PyArrayObject *)pdims) == NPY_INT64 and PyArray_NDIM((PyArrayObject *)pdims) == 1\n", + " and (pcenter = PyTuple_GET_ITEM(args, 2)) and PyArray_CheckExact(pcenter)\n", + " and PyArray_TYPE((PyArrayObject *)pcenter) == NPY_FLOAT64 and PyArray_NDIM((PyArrayObject *)pcenter) == 1\n", + " and (pw2D = PyTuple_GET_ITEM(args, 3)) and PyArray_CheckExact(pw2D)\n", + " and PyArray_TYPE((PyArrayObject *)pw2D) == NPY_FLOAT32 and PyArray_NDIM((PyArrayObject *)pw2D) == 2\n", + " and (pr50 = PyTuple_GET_ITEM(args, 4)) and PyLong_CheckExact(pr50)\n", + " and (pb = PyTuple_GET_ITEM(args, 5)) and PyFloat_CheckExact(pb)\n", + " and (pa = PyTuple_GET_ITEM(args, 6)) and PyArray_IsScalar(pa, Double)\n", + " ) {\n", + " if (!(pdensity.incref((PyObject *)PyArray_GETCONTIGUOUS((PyArrayObject *)pdensity)))) {\n", + " PyErr_SetString(PyExc_ValueError, \"Invalid Argument type on density!\");\n", + " return NULL;\n", + " }\n", + " if (!(pdims.incref((PyObject *)PyArray_GETCONTIGUOUS((PyArrayObject *)pdims)))) {\n", + " PyErr_SetString(PyExc_ValueError, \"Invalid Argument type on dims!\");\n", + " return NULL;\n", + " }\n", + " if (!(pcenter.incref((PyObject *)PyArray_GETCONTIGUOUS((PyArrayObject *)pcenter)))) {\n", + " PyErr_SetString(PyExc_ValueError, \"Invalid Argument type on center!\");\n", + " return NULL;\n", + " }\n", + " if (!(pw2D.incref((PyObject *)PyArray_GETCONTIGUOUS((PyArrayObject *)pw2D)))) {\n", + " PyErr_SetString(PyExc_ValueError, \"Invalid Argument type on w2D!\");\n", + " return NULL;\n", + " }\n", + " cr50 = PyLong_AS_LONG(pr50);\n", + " cb = PyFloat_AS_DOUBLE(pb);\n", + " ca = PyArrayScalar_VAL(pa, Double);\n", + " try {\n", + " PyObject * res = std::get<0>(pdf_f2l1d1f2JDd(\n", + " pdensity, PyArray_SHAPE((PyArrayObject *)pdensity), (npy_float *)PyArray_DATA((PyArrayObject *)pdensity)\n", + " , pdims, PyArray_SHAPE((PyArrayObject *)pdims), (npy_int64 *)PyArray_DATA((PyArrayObject *)pdims)\n", + " , pcenter, PyArray_SHAPE((PyArrayObject *)pcenter), (npy_double *)PyArray_DATA((PyArrayObject *)pcenter)\n", + " , pw2D, PyArray_SHAPE((PyArrayObject *)pw2D), (npy_float *)PyArray_DATA((PyArrayObject *)pw2D)\n", + " , cr50\n", + " , cb\n", + " , ca\n", + " ));\n", + "\n", + " Py_INCREF(res);\n", + " return res;\n", + " } catch (...) {\n", + " return NULL;\n", + " }\n", + " } else\n", + " PyErr_Clear();\n", + " }\n", + " PyObject * signatures = Py_BuildValue(\"(sO)\", \"(lp0\\n(lp1\\nccopy_reg\\n_reconstructor\\np2\\n(chope._ast\\nVariable\\np3\\nc__builtin__\\nobject\\np4\\nNtp5\\nRp6\\n(dp7\\nS'name'\\np8\\nS'density'\\np9\\nsS'dtype'\\np10\\nS'float32'\\np11\\nsS'dims'\\np12\\nI2\\nsbag2\\n(g3\\ng4\\nNtp13\\nRp14\\n(dp15\\ng8\\ng12\\nsg10\\nS'int64'\\np16\\nsg12\\nI1\\nsbag2\\n(g3\\ng4\\nNtp17\\nRp18\\n(dp19\\ng8\\nS'center'\\np20\\nsg10\\nS'float64'\\np21\\nsg12\\nI1\\nsbag2\\n(g3\\ng4\\nNtp22\\nRp23\\n(dp24\\ng8\\nS'w2D'\\np25\\nsg10\\ng11\\nsg12\\nI2\\nsbag2\\n(g3\\ng4\\nNtp26\\nRp27\\n(dp28\\ng8\\nS'r50'\\np29\\nsg10\\nc__builtin__\\nint\\np30\\nsg12\\nI0\\nsbag2\\n(g3\\ng4\\nNtp31\\nRp32\\n(dp33\\ng8\\nS'b'\\np34\\nsg10\\nc__builtin__\\nfloat\\np35\\nsg12\\nI0\\nsbag2\\n(g3\\ng4\\nNtp36\\nRp37\\n(dp38\\ng8\\nS'a'\\np39\\nsg10\\ng21\\nsg12\\nI0\\nsbaa.\", args);\n", + " if (!signatures) {\n", + " PyErr_SetString(PyExc_ValueError, \"Error building signature string for pdf\");\n", + " return NULL;\n", + " }\n", + " return PyObject_Call(create_signature, signatures, NULL);\n", + " }\n", + "\n", + " PyMethodDef pdfMethods[] = {\n", + " { \"set_create_signature\", set_create_signature, METH_VARARGS, \"signal handler\" },\n", + " { \"run\", run, METH_VARARGS, \"module function\" },\n", + " { NULL, NULL, 0, NULL }\n", + " };\n", + "\n", + " static struct PyModuleDef pdfmodule = {\n", + " PyModuleDef_HEAD_INIT,\n", + " \"pdf\",\n", + " NULL,\n", + " -1,\n", + " pdfMethods\n", + " };\n", + "\n", + " PyMODINIT_FUNC PyInit_pdf(void) {\n", + " import_array();\n", + " PyImport_ImportModule(\"numpy\");\n", + " return PyModule_Create(&pdfmodule);\n", + " }\n", "}\n", "#include \n", "#include \n", @@ -2107,37 +2169,37 @@ "#include \n", "#include \n", "void sighandler(int sig) {\n", - "\tstd::ostringstream buffer;\n", - "\tbuffer << \"Abort by \" << (sig == SIGSEGV ? \"segfault\" : \"bus error\") << std::endl;\n", - "\tvoid * stack[64];\n", - "\tstd::size_t depth = backtrace(stack, 64);\n", - "\tif (!depth)\n", - "\t\tbuffer << \" \" << std::endl;\n", - "\telse {\n", - "\t\tchar ** symbols = backtrace_symbols(stack, depth);\n", - "\t\tfor (std::size_t i = 1; i < depth; ++i) {\n", - "\t\t\tstd::string symbol = symbols[i];\n", - "\t\t\t\tif (symbol.find_first_of(' ', 59) != std::string::npos) {\n", - "\t\t\t\t\tstd::string name = symbol.substr(59, symbol.find_first_of(' ', 59) - 59);\n", - "\t\t\t\t\tint status;\n", - "\t\t\t\t\tchar * demangled = abi::__cxa_demangle(name.c_str(), NULL, NULL, &status);\n", - "\t\t\t\t\tif (!status) {\n", - "\t\t\t\t\t\tbuffer << \" \" \n", - "\t\t\t\t\t\t\t<< symbol.substr(0, 59) \n", - "\t\t\t\t\t\t\t<< demangled\n", - "\t\t\t\t\t\t\t<< symbol.substr(59 + name.size())\n", - "\t\t\t\t\t\t\t<< std::endl;\n", - "\t\t\t\t\t\tfree(demangled);\n", - "\t\t\t\t\t} else\n", - "\t\t\t\t\t\tbuffer << \" \" << symbol << std::endl;\n", - "\t\t\t\t} else\n", - "\t\t\t\t\tbuffer << \" \" << symbol << std::endl;\n", - "\t\t\t}\n", - "\t\t\tfree(symbols);\n", - "\t\t}\n", - "\t\tstd::cerr << buffer.str();\n", - "\t\tstd::exit(EXIT_FAILURE);\n", - "\t}\n" + " std::ostringstream buffer;\n", + " buffer << \"Abort by \" << (sig == SIGSEGV ? \"segfault\" : \"bus error\") << std::endl;\n", + " void * stack[64];\n", + " std::size_t depth = backtrace(stack, 64);\n", + " if (!depth)\n", + " buffer << \" \" << std::endl;\n", + " else {\n", + " char ** symbols = backtrace_symbols(stack, depth);\n", + " for (std::size_t i = 1; i < depth; ++i) {\n", + " std::string symbol = symbols[i];\n", + " if (symbol.find_first_of(' ', 59) != std::string::npos) {\n", + " std::string name = symbol.substr(59, symbol.find_first_of(' ', 59) - 59);\n", + " int status;\n", + " char * demangled = abi::__cxa_demangle(name.c_str(), NULL, NULL, &status);\n", + " if (!status) {\n", + " buffer << \" \"\n", + " << symbol.substr(0, 59)\n", + " << demangled\n", + " << symbol.substr(59 + name.size())\n", + " << std::endl;\n", + " free(demangled);\n", + " } else\n", + " buffer << \" \" << symbol << std::endl;\n", + " } else\n", + " buffer << \" \" << symbol << std::endl;\n", + " }\n", + " free(symbols);\n", + " }\n", + " std::cerr << buffer.str();\n", + " std::exit(EXIT_FAILURE);\n", + " }" ] }, { @@ -2149,7 +2211,7 @@ }, { "cell_type": "code", - "execution_count": 20, + "execution_count": 15, "metadata": {}, "outputs": [ { @@ -2346,24 +2408,6 @@ " s_x = sorted(x)\n", " return (s_x[int(math.floor((len(s_x)-1)/2))] + s_x[int(math.ceil((len(s_x)-1)/2))])/2" ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "collapsed": true - }, - "outputs": [], - "source": [] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "collapsed": true - }, - "outputs": [], - "source": [] } ], "metadata": { diff --git a/benchmarks/native_cpp_gen.nbconvert.html b/benchmarks/native_cpp_gen.nbconvert.html new file mode 100644 index 0000000..2344978 --- /dev/null +++ b/benchmarks/native_cpp_gen.nbconvert.html @@ -0,0 +1,14392 @@ + + + +native_cpp_gen.nbconvert + + + + + + + + + + + + + + + + + + + + +
+
+ +
+
+
+
+

IPython magic extension version_information

+
+
+
+
+
+
+
+

Use the '%version_information' IPython magic extension in a notebook to display information about which versions of dependency package that was used to run the notebook. +Installation.

+

Run

+ +
pip install git+https://github.com/jrjohansson/version_information
+
+
+
+

to install this extension

+ +
+
+
+
+
+
+
+

Installation

+
+
+
+
+
+
In [1]:
+
+
+
%load_ext version_information
+
+ +
+
+
+ +
+
+
+
In [2]:
+
+
+
%version_information
+
+ +
+
+
+ +
+
+ + +
+ +
Out[2]:
+ + + +
+
SoftwareVersion
Python3.5.1 64bit [GCC 4.2.1 (Apple Inc. build 5666) (dot 3)]
IPython6.1.0
OSDarwin 15.6.0 x86_64 i386 64bit
Mon Sep 04 16:15:36 2017 CEST
+
+ +
+ +
+
+ +
+
+
+
+
+

Native CPP codes

+
+
+
+
+
+
+
+

Fibonacci CPP

+
+
+
+
+
+
In [3]:
+
+
+
!mkdir -p src
+
+ +
+
+
+ +
+
+
+
In [4]:
+
+
+
%%file src/fib.cpp
+#define PY_ARRAY_UNIQUE_SYMBOL fkt_ARRAY_API
+#define NPY_NO_DEPRECATED_API NPY_1_7_API_VERSION
+#include <Python.h>
+#include <numpy/arrayobject.h>
+#include <numpy/arrayscalars.h>
+#include <cmath>
+#include <tuple>
+#include <numeric>
+#include <cstdint>
+#include <cstdlib>
+#include <exception>
+#include <functional>
+#include <type_traits>
+
+
+struct PyObj {
+    typedef PyObject * ptr_t;
+    typedef PyArrayObject * arrptr_t;
+    PyObj(): dec(false), ptr(NULL) {}
+    PyObj(ptr_t p): dec(false), ptr(p) {}
+    ~PyObj() { if(dec) Py_DECREF(ptr); }
+    PyObj & operator=(ptr_t p) { if(dec) Py_DECREF(ptr); ptr = p; dec = false; return *this; }
+    PyObj & incref(ptr_t p) { if(dec) Py_DECREF(ptr); ptr = p; dec = (p != NULL); return *this; }
+    operator bool() const { return ptr; }
+    operator ptr_t() const { return ptr; }
+    operator arrptr_t() const { return (arrptr_t)ptr; }
+    bool dec;
+    ptr_t ptr;
+};
+
+inline npy_int64 fib_J(
+      npy_int64 cn
+);
+inline npy_int64 fib_J(
+      npy_int64 cn
+) {
+    if (cn < 2) {
+        return cn;
+    }
+    return fib_J(cn - 1) + fib_J(cn - 2);    
+}
+
+#include <string>
+#include <sstream>
+#include <iostream>
+#include <cxxabi.h>
+#include <execinfo.h>
+#include <signal.h>
+
+void sighandler(int sig);
+
+void sighandler(int sig) {
+    std::ostringstream buffer;
+    buffer << "Abort by " << (sig == SIGSEGV ? "segfault" : "bus error") << std::endl;
+    void * stack[64];
+    std::size_t depth = backtrace(stack, 64);
+    if (!depth)
+        buffer << "  <empty stacktrace, possibly corrupt>" << std::endl;
+    else {
+        char ** symbols = backtrace_symbols(stack, depth);
+        for (std::size_t i = 1; i < depth; ++i) {
+            std::string symbol = symbols[i];
+                if (symbol.find_first_of(' ', 59) != std::string::npos) {
+                    std::string name = symbol.substr(59, symbol.find_first_of(' ', 59) - 59);
+                    int status;
+                    char * demangled = abi::__cxa_demangle(name.c_str(), NULL, NULL, &status);
+                    if (!status) {
+                        buffer << "    "
+                            << symbol.substr(0, 59)
+                            << demangled
+                            << symbol.substr(59 + name.size())
+                            << std::endl;
+                        free(demangled);
+                    } else
+                        buffer << "    " << symbol << std::endl;
+                } else
+                    buffer << "    " << symbol << std::endl;
+            }
+            free(symbols);
+        }
+        std::cerr << buffer.str();
+        std::exit(EXIT_FAILURE);
+    }
+
+
+extern "C" {
+
+    PyObject * create_signature;
+
+    struct sigaction slot;
+
+    PyObject * set_create_signature(PyObject * self, PyObject * args) {
+        if (!PyArg_ParseTuple(args, "O", &create_signature)) {
+            PyErr_SetString(PyExc_ValueError, "Invalid Argument to set_create_signature!");
+            return NULL;
+        }
+        Py_INCREF(create_signature);
+        memset(&slot, 0, sizeof(slot));
+        slot.sa_handler = &sighandler;
+        sigaction(SIGSEGV, &slot, NULL);
+        sigaction(SIGBUS, &slot, NULL);
+        Py_INCREF(Py_None);
+        return Py_None;
+    }
+
+    PyObject * run(PyObject * self, PyObject * args) {
+        {
+            PyObject * pn; npy_int64 cn;
+            if (
+                PyTuple_CheckExact(args) and PyTuple_GET_SIZE(args) == 1
+                and (pn = PyTuple_GET_ITEM(args, 0)) and PyLong_CheckExact(pn)
+            ) {
+                cn = PyLong_AS_LONG(pn);
+                try {
+                    return Py_BuildValue("l", fib_J(
+                          cn
+                    ));
+                } catch (...) {
+                    return NULL;
+                }
+            } else
+                PyErr_Clear();
+        }
+        PyObject * signatures = Py_BuildValue("(sO)", "gANdcQBdcQFjaG9wZS5fYXN0ClZhcmlhYmxlCnECKYFxA31xBChYBAAAAGRpbXNxBUsAWAUAAABk\ndHlwZXEGY2J1aWx0aW5zCmludApxB1gEAAAAbmFtZXEIWAEAAABucQl1YmFhLg==\n", args);
+        if (!signatures) {
+            PyErr_SetString(PyExc_ValueError, "Error building signature string for fib");
+            return NULL;
+        }
+        return PyObject_Call(create_signature, signatures, NULL);
+    }
+
+    PyMethodDef fibMethods[] = {
+        { "set_create_signature", set_create_signature, METH_VARARGS, "signal handler" },
+        { "run", run, METH_VARARGS, "module function" },
+        { NULL, NULL, 0, NULL }
+    };
+
+
+    static struct PyModuleDef fibmodule = {
+        PyModuleDef_HEAD_INIT,
+        "fib",
+        NULL,
+        -1,
+        fibMethods
+    };
+
+
+
+   PyMODINIT_FUNC PyInit_fib(void) {
+        import_array();
+        PyImport_ImportModule("numpy");
+        return PyModule_Create(&fibmodule);
+   }
+}
+
+ +
+
+
+ +
+
+ + +
+ +
+ + +
+
Overwriting src/fib.cpp
+
+
+
+ +
+
+ +
+
+
+
+
+

Quicksort CPP

+
+
+
+
+
+
In [5]:
+
+
+
%%file src/qsort_kernel.cpp
+#define PY_ARRAY_UNIQUE_SYMBOL qsort_kernel_ARRAY_API
+#define NPY_NO_DEPRECATED_API NPY_1_7_API_VERSION
+#include <Python.h>
+#include <numpy/arrayobject.h>
+#include <numpy/arrayscalars.h>
+#include <cmath>
+#include <tuple>
+#include <numeric>
+#include <cstdint>
+#include <cstdlib>
+#include <exception>
+#include <functional>
+#include <type_traits>
+struct PyObj {
+ typedef PyObject * ptr_t;
+ typedef PyArrayObject * arrptr_t;
+ PyObj(): dec(false), ptr(NULL) {}
+ PyObj(ptr_t p): dec(false), ptr(p) {}
+ ~PyObj() { if(dec) Py_DECREF(ptr); }
+ PyObj & operator=(ptr_t p) { if(dec) Py_DECREF(ptr); ptr = p; dec = false; return *this; }
+ PyObj & incref(ptr_t p) { if(dec) Py_DECREF(ptr); ptr = p; dec = (p != NULL); return *this; }
+ operator bool() const { return ptr; }
+ operator ptr_t() const { return ptr; }
+ operator arrptr_t() const { return (arrptr_t)ptr; }
+ bool dec;
+ ptr_t ptr;
+};
+inline std::tuple<PyObject *, npy_intp const *, npy_double *> qsort_kernel_d1JJ(PyObject * pa,
+    npy_intp const * __restrict__ sa,
+                                         npy_double * __restrict__ ca,
+                                         npy_int64 clo,
+                                         npy_int64 chi);
+
+inline std::tuple<PyObject *, npy_intp const *, npy_double *> qsort_kernel_d1JJ(PyObject * pa,
+                                         npy_intp const * __restrict__ sa,
+                                         npy_double * __restrict__ ca,
+                                         npy_int64 clo,
+                                         npy_int64 chi){
+ npy_int64 ci = clo;
+ npy_int64 cj = chi;
+ npy_double cpivot;
+
+ while (ci < chi) {
+  cpivot = ca[(int)((clo + chi) / 2)];
+
+  while (ci <= cj) {
+   while (ca[ci] < cpivot) {
+    ci += 1;
+   }
+
+   while (ca[cj] > cpivot) {
+    cj -= 1;
+   }
+
+   if (ci <= cj) {
+    auto ctmp = ca[ci];
+    ca[ci] = ca[cj];
+    ca[cj] = ctmp;
+    ci += 1;
+    cj -= 1;
+   }
+  }
+
+  if (clo < cj) {
+   qsort_kernel_d1JJ(pa, sa, ca, clo, cj);
+  }
+
+  clo = ci;
+  cj = chi;
+ }
+
+ return std::make_tuple((PyObject *)pa, sa, ca);
+}
+
+void sighandler(int sig);
+#include <signal.h>
+extern "C" {
+ PyObject * create_signature;
+ struct sigaction slot;
+ PyObject * set_create_signature(PyObject * self, PyObject * args) {
+  if (!PyArg_ParseTuple(args, "O", &create_signature)) {
+   PyErr_SetString(PyExc_ValueError, "Invalid Argument to set_create_signature!");
+   return NULL;
+  }
+  Py_INCREF(create_signature);
+  memset(&slot, 0, sizeof(slot));
+  slot.sa_handler = &sighandler;
+  sigaction(SIGSEGV, &slot, NULL);
+  sigaction(SIGBUS, &slot, NULL);
+  Py_INCREF(Py_None);
+  return Py_None;
+ }
+ PyObject * run(PyObject * self, PyObject * args) {
+  {
+   PyObj pa;
+   PyObject * plo; npy_int64 clo;
+   PyObject * phi; npy_int64 chi;
+   if (
+    PyTuple_CheckExact(args) and PyTuple_GET_SIZE(args) == 3
+    and (pa = PyTuple_GET_ITEM(args, 0)) and PyArray_CheckExact(pa)
+    and PyArray_TYPE((PyArrayObject *)pa) == NPY_FLOAT64 and PyArray_NDIM((PyArrayObject *)pa) == 1
+    and (plo = PyTuple_GET_ITEM(args, 1)) and PyLong_CheckExact(plo)
+    and (phi = PyTuple_GET_ITEM(args, 2)) and PyLong_CheckExact(phi)
+   ) {
+    if (!(pa.incref((PyObject *)PyArray_GETCONTIGUOUS((PyArrayObject *)pa)))) {
+     PyErr_SetString(PyExc_ValueError, "Invalid Argument type on a!");
+     return NULL;
+    }
+    clo = PyLong_AS_LONG(plo);
+    chi = PyLong_AS_LONG(phi);
+    try {
+     PyObject * res = std::get<0>(qsort_kernel_d1JJ(
+        pa, PyArray_SHAPE((PyArrayObject *)pa), (npy_double *)PyArray_DATA((PyArrayObject *)pa)
+      , clo
+      , chi
+     ));
+
+     Py_INCREF(res);
+     return res;
+    } catch (...) {
+     return NULL;
+    }
+   } else
+    PyErr_Clear();
+  }
+  PyObject * signatures = Py_BuildValue("(sO)", "(lp0\n(lp1\nccopy_reg\n_reconstructor\np2\n(chope._ast\nVariable\np3\nc__builtin__\nobject\np4\nNtp5\nRp6\n(dp7\nS'name'\np8\nS'a'\np9\nsS'dtype'\np10\nS'float64'\np11\nsS'dims'\np12\nI1\nsbag2\n(g3\ng4\nNtp13\nRp14\n(dp15\ng8\nS'lo'\np16\nsg10\nc__builtin__\nint\np17\nsg12\nI0\nsbag2\n(g3\ng4\nNtp18\nRp19\n(dp20\ng8\nS'hi'\np21\nsg10\ng17\nsg12\nI0\nsbaa.", args);
+  if (!signatures) {
+   PyErr_SetString(PyExc_ValueError, "Error building signature string for qsort_kernel");
+   return NULL;
+  }
+  return PyObject_Call(create_signature, signatures, NULL);
+ }
+ PyMethodDef qsort_kernelMethods[] = {
+  { "set_create_signature", set_create_signature, METH_VARARGS, "signal handler" },
+  { "run", run, METH_VARARGS, "module function" },
+  { NULL, NULL, 0, NULL }
+ };
+}
+
+static struct PyModuleDef qsort_kernel_module = {
+    PyModuleDef_HEAD_INIT,
+    "qsort_kernel",
+    NULL,
+    -1,
+    qsort_kernelMethods
+};
+
+PyMODINIT_FUNC PyInit_qsort_kernel(void) {
+    import_array();
+    PyImport_ImportModule("numpy");
+    return PyModule_Create(&qsort_kernel_module);
+}
+
+#include <string>
+#include <sstream>
+#include <iostream>
+#include <cxxabi.h>
+#include <execinfo.h>
+void sighandler(int sig) {
+ std::ostringstream buffer;
+ buffer << "Abort by " << (sig == SIGSEGV ? "segfault" : "bus error") << std::endl;
+ void * stack[64];
+ std::size_t depth = backtrace(stack, 64);
+ if (!depth)
+  buffer << "  <empty stacktrace, possibly corrupt>" << std::endl;
+ else {
+  char ** symbols = backtrace_symbols(stack, depth);
+  for (std::size_t i = 1; i < depth; ++i) {
+   std::string symbol = symbols[i];
+    if (symbol.find_first_of(' ', 59) != std::string::npos) {
+     std::string name = symbol.substr(59, symbol.find_first_of(' ', 59) - 59);
+     int status;
+     char * demangled = abi::__cxa_demangle(name.c_str(), NULL, NULL, &status);
+     if (!status) {
+      buffer << "    "
+       << symbol.substr(0, 59)
+       << demangled
+       << symbol.substr(59 + name.size())
+       << std::endl;
+      free(demangled);
+     } else
+      buffer << "    " << symbol << std::endl;
+    } else
+     buffer << "    " << symbol << std::endl;
+   }
+   free(symbols);
+  }
+  std::cerr << buffer.str();
+  std::exit(EXIT_FAILURE);
+ }
+
+ +
+
+
+ +
+
+ + +
+ +
+ + +
+
Overwriting src/qsort_kernel.cpp
+
+
+
+ +
+
+ +
+
+
+
+
+

Pi sum CPP

+
+
+
+
+
+
In [6]:
+
+
+
%%file src/pisum.cpp
+#define PY_ARRAY_UNIQUE_SYMBOL pisum_ARRAY_API
+#define NPY_NO_DEPRECATED_API NPY_1_7_API_VERSION
+#include <Python.h>
+#include <numpy/arrayobject.h>
+#include <numpy/arrayscalars.h>
+#include <cmath>
+#include <tuple>
+#include <numeric>
+#include <cstdint>
+#include <cstdlib>
+#include <exception>
+#include <functional>
+#include <type_traits>
+struct PyObj {
+    typedef PyObject * ptr_t;
+    typedef PyArrayObject * arrptr_t;
+    PyObj(): dec(false), ptr(NULL) {}
+    PyObj(ptr_t p): dec(false), ptr(p) {}
+    ~PyObj() { if(dec) Py_DECREF(ptr); }
+    PyObj & operator=(ptr_t p) { if(dec) Py_DECREF(ptr); ptr = p; dec = false; return *this; }
+    PyObj & incref(ptr_t p) { if(dec) Py_DECREF(ptr); ptr = p; dec = (p != NULL); return *this; }
+    operator bool() const { return ptr; }
+    operator ptr_t() const { return ptr; }
+    operator arrptr_t() const { return (arrptr_t)ptr; }
+    bool dec;
+    ptr_t ptr;
+};
+
+inline npy_double pisum_();
+
+inline npy_double pisum_() {
+    double csum = 0;
+    int ck = 1;
+    for (int cj = 1; cj < 501; ++cj) {
+        csum = 0.0;
+        for (ck = 1; ck < 10001; ++ck) {
+            csum += (1.0 / (double)(ck * ck));
+        }
+    }
+
+    return npy_double(csum);
+}
+
+void sighandler(int sig);
+#include <signal.h>
+extern "C" {
+    PyObject * create_signature;
+    struct sigaction slot;
+    PyObject * set_create_signature(PyObject * self, PyObject * args) {
+        if (!PyArg_ParseTuple(args, "O", &create_signature)) {
+            PyErr_SetString(PyExc_ValueError, "Invalid Argument to set_create_signature!");
+            return NULL;
+        }
+        Py_INCREF(create_signature);
+        memset(&slot, 0, sizeof(slot));
+        slot.sa_handler = &sighandler;
+        sigaction(SIGSEGV, &slot, NULL);
+        sigaction(SIGBUS, &slot, NULL);
+        Py_INCREF(Py_None);
+        return Py_None;
+    }
+    PyObject * run(PyObject * self, PyObject * args) {
+        {                try {
+                    return Py_BuildValue("d", pisum_());
+                } catch (...) {
+                    return NULL;
+                }
+        }
+        PyObject * signatures = Py_BuildValue("(sO)", "(lp0\n(lp1\na.", args);
+        if (!signatures) {
+            PyErr_SetString(PyExc_ValueError, "Error building signature string for pisum");
+            return NULL;
+        }
+        return PyObject_Call(create_signature, signatures, NULL);
+    }
+
+PyMethodDef pisumMethods[] = {
+{ "set_create_signature", set_create_signature, METH_VARARGS, "signal handler" },
+{ "run", run, METH_VARARGS, "module function" },
+{ NULL, NULL, 0, NULL }
+};
+
+static struct PyModuleDef pisum_module = {
+    PyModuleDef_HEAD_INIT,
+    "pisum",
+    NULL,
+    -1,
+    pisumMethods
+};
+
+PyMODINIT_FUNC PyInit_pisum(void) {
+    import_array();
+    PyImport_ImportModule("numpy");
+    return PyModule_Create(&pisum_module);
+}
+}
+
+#include <string>
+#include <sstream>
+#include <iostream>
+#include <cxxabi.h>
+#include <execinfo.h>
+void sighandler(int sig) {
+    std::ostringstream buffer;
+    buffer << "Abort by " << (sig == SIGSEGV ? "segfault" : "bus error") << std::endl;
+    void * stack[64];
+    std::size_t depth = backtrace(stack, 64);
+    if (!depth)
+        buffer << "  <empty stacktrace, possibly corrupt>" << std::endl;
+    else {
+        char ** symbols = backtrace_symbols(stack, depth);
+        for (std::size_t i = 1; i < depth; ++i) {
+            std::string symbol = symbols[i];
+                if (symbol.find_first_of(' ', 59) != std::string::npos) {
+                    std::string name = symbol.substr(59, symbol.find_first_of(' ', 59) - 59);
+                    int status;
+                    char * demangled = abi::__cxa_demangle(name.c_str(), NULL, NULL, &status);
+                    if (!status) {
+                        buffer << "    "
+                            << symbol.substr(0, 59)
+                            << demangled
+                            << symbol.substr(59 + name.size())
+                            << std::endl;
+                        free(demangled);
+                    } else
+                        buffer << "    " << symbol << std::endl;
+                } else
+                    buffer << "    " << symbol << std::endl;
+            }
+            free(symbols);
+        }
+        std::cerr << buffer.str();
+        std::exit(EXIT_FAILURE);
+    }
+
+ +
+
+
+ +
+
+ + +
+ +
+ + +
+
Overwriting src/pisum.cpp
+
+
+
+ +
+
+ +
+
+
+
In [7]:
+
+
+
%%file src/pisum_opt.cpp
+#define PY_ARRAY_UNIQUE_SYMBOL pisum_opt_ARRAY_API
+#define NPY_NO_DEPRECATED_API NPY_1_7_API_VERSION
+#include <Python.h>
+#include <numpy/arrayobject.h>
+#include <numpy/arrayscalars.h>
+#include <cmath>
+#include <tuple>
+#include <numeric>
+#include <cstdint>
+#include <cstdlib>
+#include <exception>
+#include <functional>
+#include <type_traits>
+struct PyObj {
+    typedef PyObject * ptr_t;
+    typedef PyArrayObject * arrptr_t;
+    PyObj(): dec(false), ptr(NULL) {}
+    PyObj(ptr_t p): dec(false), ptr(p) {}
+    ~PyObj() { if(dec) Py_DECREF(ptr); }
+    PyObj & operator=(ptr_t p) { if(dec) Py_DECREF(ptr); ptr = p; dec = false; return *this; }
+    PyObj & incref(ptr_t p) { if(dec) Py_DECREF(ptr); ptr = p; dec = (p != NULL); return *this; }
+    operator bool() const { return ptr; }
+    operator ptr_t() const { return ptr; }
+    operator arrptr_t() const { return (arrptr_t)ptr; }
+    bool dec;
+    ptr_t ptr;
+};
+
+inline npy_double pisum_opt_();
+
+inline npy_double pisum_opt_() {
+    npy_double csum = npy_double();
+    npy_intp ck = 1;
+    npy_double cf = 0.0;
+
+    for (npy_intp cj = 1; cj < 501; ++cj) {
+        csum = 0.0;
+        cf = 0.0;
+        for (ck = 1; ck < 10001; ++ck) {
+            cf += 1.0;
+            auto c__sp0 = (cf * cf);
+            csum += (1.0 / c__sp0);
+        }
+    }
+    return csum;
+}
+
+void sighandler(int sig);
+#include <signal.h>
+extern "C" {
+    PyObject * create_signature;
+    struct sigaction slot;
+    PyObject * set_create_signature(PyObject * self, PyObject * args) {
+        if (!PyArg_ParseTuple(args, "O", &create_signature)) {
+            PyErr_SetString(PyExc_ValueError, "Invalid Argument to set_create_signature!");
+            return NULL;
+        }
+        Py_INCREF(create_signature);
+        memset(&slot, 0, sizeof(slot));
+        slot.sa_handler = &sighandler;
+        sigaction(SIGSEGV, &slot, NULL);
+        sigaction(SIGBUS, &slot, NULL);
+        Py_INCREF(Py_None);
+        return Py_None;
+    }
+    PyObject * run(PyObject * self, PyObject * args) {
+        {                try {
+                    return Py_BuildValue("d", pisum_opt_());
+                } catch (...) {
+                    return NULL;
+                }
+        }
+        PyObject * signatures = Py_BuildValue("(sO)", "(lp0\n(lp1\na.", args);
+        if (!signatures) {
+            PyErr_SetString(PyExc_ValueError, "Error building signature string for pisum_opt");
+            return NULL;
+        }
+        return PyObject_Call(create_signature, signatures, NULL);
+    }
+
+    PyMethodDef pisum_optMethods[] = {
+    { "set_create_signature", set_create_signature, METH_VARARGS, "signal handler" },
+    { "run", run, METH_VARARGS, "module function" },
+    { NULL, NULL, 0, NULL }
+    };
+
+    static struct PyModuleDef pisum_opt_module = {
+        PyModuleDef_HEAD_INIT,
+        "pisum_opt",
+        NULL,
+        -1,
+        pisum_optMethods
+    };
+
+    PyMODINIT_FUNC PyInit_pisum_opt(void) {
+        import_array();
+        PyImport_ImportModule("numpy");
+        return PyModule_Create(&pisum_opt_module);
+    }
+}
+
+#include <string>
+#include <sstream>
+#include <iostream>
+#include <cxxabi.h>
+#include <execinfo.h>
+void sighandler(int sig) {
+    std::ostringstream buffer;
+    buffer << "Abort by " << (sig == SIGSEGV ? "segfault" : "bus error") << std::endl;
+    void * stack[64];
+    std::size_t depth = backtrace(stack, 64);
+    if (!depth)
+        buffer << "  <empty stacktrace, possibly corrupt>" << std::endl;
+    else {
+        char ** symbols = backtrace_symbols(stack, depth);
+        for (std::size_t i = 1; i < depth; ++i) {
+            std::string symbol = symbols[i];
+                if (symbol.find_first_of(' ', 59) != std::string::npos) {
+                    std::string name = symbol.substr(59, symbol.find_first_of(' ', 59) - 59);
+                    int status;
+                    char * demangled = abi::__cxa_demangle(name.c_str(), NULL, NULL, &status);
+                    if (!status) {
+                        buffer << "    "
+                            << symbol.substr(0, 59)
+                            << demangled
+                            << symbol.substr(59 + name.size())
+                            << std::endl;
+                        free(demangled);
+                    } else
+                        buffer << "    " << symbol << std::endl;
+                } else
+                    buffer << "    " << symbol << std::endl;
+            }
+            free(symbols);
+        }
+        std::cerr << buffer.str();
+        std::exit(EXIT_FAILURE);
+    }
+
+ +
+
+
+ +
+
+ + +
+ +
+ + +
+
Overwriting src/pisum_opt.cpp
+
+
+
+ +
+
+ +
+
+
+
+
+

10th order poly log approx CPP

+
+
+
+
+
+
In [8]:
+
+
+
%%file src/ln.cpp
+#define PY_ARRAY_UNIQUE_SYMBOL ln_hope_ARRAY_API
+#define NPY_NO_DEPRECATED_API NPY_1_7_API_VERSION
+#include <Python.h>
+#include <numpy/arrayobject.h>
+#include <numpy/arrayscalars.h>
+#include <cmath>
+#include <tuple>
+#include <numeric>
+#include <cstdint>
+#include <cstdlib>
+#include <exception>
+#include <functional>
+#include <type_traits>
+struct PyObj {
+    typedef PyObject * ptr_t;
+    typedef PyArrayObject * arrptr_t;
+    PyObj(): dec(false), ptr(NULL) {}
+    PyObj(ptr_t p): dec(false), ptr(p) {}
+    ~PyObj() { if(dec) Py_DECREF(ptr); }
+    PyObj & operator=(ptr_t p) { if(dec) Py_DECREF(ptr); ptr = p; dec = false; return *this; }
+    PyObj & incref(ptr_t p) { if(dec) Py_DECREF(ptr); ptr = p; dec = (p != NULL); return *this; }
+    operator bool() const { return ptr; }
+    operator ptr_t() const { return ptr; }
+    operator arrptr_t() const { return (arrptr_t)ptr; }
+    bool dec;
+    ptr_t ptr;
+};
+
+inline void ln_hope_d1d1(PyObject * pX, npy_intp const * __restrict__ sX, npy_double * __restrict__ cX,
+                                                    PyObject * pY, npy_intp const * __restrict__ sY, npy_double * __restrict__ cY);
+
+inline void ln_hope_d1d1(PyObject * pX, npy_intp const * __restrict__ sX, npy_double * __restrict__ cX,
+                                                    PyObject * pY, npy_intp const * __restrict__ sY, npy_double * __restrict__ cY){
+
+    for (npy_intp i0 = 0; i0 < sY[0] - 0; ++i0) {
+        cY[(int)(i0)] = (cX[i0] - 1) - (std::pow((cX[i0] - 1), 2) / 2) + (std::pow((cX[i0] - 1), 3) / 3) - (std::pow((cX[i0] - 1), 4) / 4) + (std::pow((cX[i0] - 1), 5) / 5) - (std::pow((cX[i0] - 1), 6) / 6) + (std::pow((cX[i0] - 1), 7) / 7) - (std::pow((cX[i0] - 1), 8) / 8) + (std::pow((cX[i0] - 1), 9) / 9);
+    }
+}
+
+void sighandler(int sig);
+#include <signal.h>
+extern "C" {
+    PyObject * create_signature;
+    struct sigaction slot;
+    PyObject * set_create_signature(PyObject * self, PyObject * args) {
+        if (!PyArg_ParseTuple(args, "O", &create_signature)) {
+            PyErr_SetString(PyExc_ValueError, "Invalid Argument to set_create_signature!");
+            return NULL;
+        }
+        Py_INCREF(create_signature);
+        memset(&slot, 0, sizeof(slot));
+        slot.sa_handler = &sighandler;
+        sigaction(SIGSEGV, &slot, NULL);
+        sigaction(SIGBUS, &slot, NULL);
+        Py_INCREF(Py_None);
+        return Py_None;
+    }
+    PyObject * run(PyObject * self, PyObject * args) {
+        {
+            PyObj pX;
+            PyObj pY;
+            if (
+                PyTuple_CheckExact(args) and PyTuple_GET_SIZE(args) == 2
+                and (pX = PyTuple_GET_ITEM(args, 0)) and PyArray_CheckExact(pX)
+                and PyArray_TYPE((PyArrayObject *)pX) == NPY_FLOAT64 and PyArray_NDIM((PyArrayObject *)pX) == 1
+                and (pY = PyTuple_GET_ITEM(args, 1)) and PyArray_CheckExact(pY)
+                and PyArray_TYPE((PyArrayObject *)pY) == NPY_FLOAT64 and PyArray_NDIM((PyArrayObject *)pY) == 1
+            ) {
+                if (!(pX.incref((PyObject *)PyArray_GETCONTIGUOUS((PyArrayObject *)pX)))) {
+                    PyErr_SetString(PyExc_ValueError, "Invalid Argument type on X!");
+                    return NULL;
+                }
+                if (!(pY.incref((PyObject *)PyArray_GETCONTIGUOUS((PyArrayObject *)pY)))) {
+                    PyErr_SetString(PyExc_ValueError, "Invalid Argument type on Y!");
+                    return NULL;
+                }
+                try {
+                    ln_hope_d1d1(
+                          pX, PyArray_SHAPE((PyArrayObject *)pX), (npy_double *)PyArray_DATA((PyArrayObject *)pX)
+                        , pY, PyArray_SHAPE((PyArrayObject *)pY), (npy_double *)PyArray_DATA((PyArrayObject *)pY)
+                    );
+                    Py_INCREF(Py_None);
+                    return Py_None;
+                } catch (...) {
+                    return NULL;
+                }
+            } else
+                PyErr_Clear();
+        }
+        PyObject * signatures = Py_BuildValue("(sO)", "(lp0\n(lp1\nccopy_reg\n_reconstructor\np2\n(chope._ast\nVariable\np3\nc__builtin__\nobject\np4\nNtp5\nRp6\n(dp7\nS'name'\np8\nS'X'\np9\nsS'dtype'\np10\nS'float64'\np11\nsS'dims'\np12\nI1\nsbag2\n(g3\ng4\nNtp13\nRp14\n(dp15\ng8\nS'Y'\np16\nsg10\ng11\nsg12\nI1\nsbaa.", args);
+        if (!signatures) {
+            PyErr_SetString(PyExc_ValueError, "Error building signature string for ln_hope");
+            return NULL;
+        }
+        return PyObject_Call(create_signature, signatures, NULL);
+    }
+
+    PyMethodDef lnMethods[] = {
+        { "set_create_signature", set_create_signature, METH_VARARGS, "signal handler" },
+        { "run", run, METH_VARARGS, "module function" },
+        { NULL, NULL, 0, NULL }
+    };
+
+
+    static struct PyModuleDef lnmodule = {
+        PyModuleDef_HEAD_INIT,
+        "ln",
+        NULL,
+        -1,
+        lnMethods
+    };
+
+
+
+   PyMODINIT_FUNC PyInit_ln(void) {
+        import_array();
+        PyImport_ImportModule("numpy");
+        return PyModule_Create(&lnmodule);
+   }
+}
+#include <string>
+#include <sstream>
+#include <iostream>
+#include <cxxabi.h>
+#include <execinfo.h>
+void sighandler(int sig) {
+    std::ostringstream buffer;
+    buffer << "Abort by " << (sig == SIGSEGV ? "segfault" : "bus error") << std::endl;
+    void * stack[64];
+    std::size_t depth = backtrace(stack, 64);
+    if (!depth)
+        buffer << "  <empty stacktrace, possibly corrupt>" << std::endl;
+    else {
+        char ** symbols = backtrace_symbols(stack, depth);
+        for (std::size_t i = 1; i < depth; ++i) {
+            std::string symbol = symbols[i];
+                if (symbol.find_first_of(' ', 59) != std::string::npos) {
+                    std::string name = symbol.substr(59, symbol.find_first_of(' ', 59) - 59);
+                    int status;
+                    char * demangled = abi::__cxa_demangle(name.c_str(), NULL, NULL, &status);
+                    if (!status) {
+                        buffer << "    "
+                            << symbol.substr(0, 59)
+                            << demangled
+                            << symbol.substr(59 + name.size())
+                            << std::endl;
+                        free(demangled);
+                    } else
+                        buffer << "    " << symbol << std::endl;
+                } else
+                    buffer << "    " << symbol << std::endl;
+            }
+            free(symbols);
+        }
+        std::cerr << buffer.str();
+        std::exit(EXIT_FAILURE);
+    }
+
+ +
+
+
+ +
+
+ + +
+ +
+ + +
+
Overwriting src/ln.cpp
+
+
+
+ +
+
+ +
+
+
+
In [9]:
+
+
+
%%file src/ln_exp.cpp
+#define PY_ARRAY_UNIQUE_SYMBOL ln_hope_exp_ARRAY_API
+#define NPY_NO_DEPRECATED_API NPY_1_7_API_VERSION
+#include <Python.h>
+#include <numpy/arrayobject.h>
+#include <numpy/arrayscalars.h>
+#include <cmath>
+#include <tuple>
+#include <numeric>
+#include <cstdint>
+#include <cstdlib>
+#include <exception>
+#include <functional>
+#include <type_traits>
+struct PyObj {
+    typedef PyObject * ptr_t;
+    typedef PyArrayObject * arrptr_t;
+    PyObj(): dec(false), ptr(NULL) {}
+    PyObj(ptr_t p): dec(false), ptr(p) {}
+    ~PyObj() { if(dec) Py_DECREF(ptr); }
+    PyObj & operator=(ptr_t p) { if(dec) Py_DECREF(ptr); ptr = p; dec = false; return *this; }
+    PyObj & incref(ptr_t p) { if(dec) Py_DECREF(ptr); ptr = p; dec = (p != NULL); return *this; }
+    operator bool() const { return ptr; }
+    operator ptr_t() const { return ptr; }
+    operator arrptr_t() const { return (arrptr_t)ptr; }
+    bool dec;
+    ptr_t ptr;
+};
+
+inline void ln_hope_exp_d1d1(PyObject * pX, npy_intp const * __restrict__ sX, npy_double * __restrict__ cX,
+                                                    PyObject * pY, npy_intp const * __restrict__ sY, npy_double * __restrict__ cY);
+
+inline void ln_hope_exp_d1d1(PyObject * pX, npy_intp const * __restrict__ sX, npy_double * __restrict__ cX,
+                                                    PyObject * pY, npy_intp const * __restrict__ sY, npy_double * __restrict__ cY){
+
+    for (npy_intp i0 = 0; i0 < sX[0] - 0; ++i0) {
+        auto cx = (cX[i0] - 1);
+        auto cx2 = (cx * cx);
+        auto cx4 = (cx2 * cx2);
+        auto cx6 = (cx4 * cx2);
+        auto cx8 = (cx4 * cx4);
+        cY[i0] = cx - (cx2 / 2) + (cx * cx2 / 3) - (cx4 / 4) + (cx * cx4 / 5) - (cx6 / 6) + (cx6 * cx / 7) - (cx8 / 8) + (cx8 * cx / 9);
+    }
+}
+
+void sighandler(int sig);
+#include <signal.h>
+extern "C" {
+    PyObject * create_signature;
+    struct sigaction slot;
+    PyObject * set_create_signature(PyObject * self, PyObject * args) {
+        if (!PyArg_ParseTuple(args, "O", &create_signature)) {
+            PyErr_SetString(PyExc_ValueError, "Invalid Argument to set_create_signature!");
+            return NULL;
+        }
+        Py_INCREF(create_signature);
+        memset(&slot, 0, sizeof(slot));
+        slot.sa_handler = &sighandler;
+        sigaction(SIGSEGV, &slot, NULL);
+        sigaction(SIGBUS, &slot, NULL);
+        Py_INCREF(Py_None);
+        return Py_None;
+    }
+    PyObject * run(PyObject * self, PyObject * args) {
+        {
+            PyObj pX;
+            PyObj pY;
+            if (
+                PyTuple_CheckExact(args) and PyTuple_GET_SIZE(args) == 2
+                and (pX = PyTuple_GET_ITEM(args, 0)) and PyArray_CheckExact(pX)
+                and PyArray_TYPE((PyArrayObject *)pX) == NPY_FLOAT64 and PyArray_NDIM((PyArrayObject *)pX) == 1
+                and (pY = PyTuple_GET_ITEM(args, 1)) and PyArray_CheckExact(pY)
+                and PyArray_TYPE((PyArrayObject *)pY) == NPY_FLOAT64 and PyArray_NDIM((PyArrayObject *)pY) == 1
+            ) {
+                if (!(pX.incref((PyObject *)PyArray_GETCONTIGUOUS((PyArrayObject *)pX)))) {
+                    PyErr_SetString(PyExc_ValueError, "Invalid Argument type on X!");
+                    return NULL;
+                }
+                if (!(pY.incref((PyObject *)PyArray_GETCONTIGUOUS((PyArrayObject *)pY)))) {
+                    PyErr_SetString(PyExc_ValueError, "Invalid Argument type on Y!");
+                    return NULL;
+                }
+                try {
+                    ln_hope_exp_d1d1(
+                          pX, PyArray_SHAPE((PyArrayObject *)pX), (npy_double *)PyArray_DATA((PyArrayObject *)pX)
+                        , pY, PyArray_SHAPE((PyArrayObject *)pY), (npy_double *)PyArray_DATA((PyArrayObject *)pY)
+                    );
+                    Py_INCREF(Py_None);
+                    return Py_None;
+                } catch (...) {
+                    return NULL;
+                }
+            } else
+                PyErr_Clear();
+        }
+        PyObject * signatures = Py_BuildValue("(sO)", "(lp0\n(lp1\nccopy_reg\n_reconstructor\np2\n(chope._ast\nVariable\np3\nc__builtin__\nobject\np4\nNtp5\nRp6\n(dp7\nS'name'\np8\nS'X'\np9\nsS'dtype'\np10\nS'float64'\np11\nsS'dims'\np12\nI1\nsbag2\n(g3\ng4\nNtp13\nRp14\n(dp15\ng8\nS'Y'\np16\nsg10\ng11\nsg12\nI1\nsbaa.", args);
+        if (!signatures) {
+            PyErr_SetString(PyExc_ValueError, "Error building signature string for ln_hope_exp");
+            return NULL;
+        }
+        return PyObject_Call(create_signature, signatures, NULL);
+    }
+    PyMethodDef ln_expMethods[] = {
+        { "set_create_signature", set_create_signature, METH_VARARGS, "signal handler" },
+        { "run", run, METH_VARARGS, "module function" },
+        { NULL, NULL, 0, NULL }
+    };
+
+
+    static struct PyModuleDef ln_expmodule = {
+        PyModuleDef_HEAD_INIT,
+        "ln_exp",
+        NULL,
+        -1,
+        ln_expMethods
+    };
+
+
+
+   PyMODINIT_FUNC PyInit_ln_exp(void) {
+        import_array();
+        PyImport_ImportModule("numpy");
+        return PyModule_Create(&ln_expmodule);
+   }
+}
+#include <string>
+#include <sstream>
+#include <iostream>
+#include <cxxabi.h>
+#include <execinfo.h>
+void sighandler(int sig) {
+    std::ostringstream buffer;
+    buffer << "Abort by " << (sig == SIGSEGV ? "segfault" : "bus error") << std::endl;
+    void * stack[64];
+    std::size_t depth = backtrace(stack, 64);
+    if (!depth)
+        buffer << "  <empty stacktrace, possibly corrupt>" << std::endl;
+    else {
+        char ** symbols = backtrace_symbols(stack, depth);
+        for (std::size_t i = 1; i < depth; ++i) {
+            std::string symbol = symbols[i];
+                if (symbol.find_first_of(' ', 59) != std::string::npos) {
+                    std::string name = symbol.substr(59, symbol.find_first_of(' ', 59) - 59);
+                    int status;
+                    char * demangled = abi::__cxa_demangle(name.c_str(), NULL, NULL, &status);
+                    if (!status) {
+                        buffer << "    "
+                            << symbol.substr(0, 59)
+                            << demangled
+                            << symbol.substr(59 + name.size())
+                            << std::endl;
+                        free(demangled);
+                    } else
+                        buffer << "    " << symbol << std::endl;
+                } else
+                    buffer << "    " << symbol << std::endl;
+            }
+            free(symbols);
+        }
+        std::cerr << buffer.str();
+        std::exit(EXIT_FAILURE);
+    }
+
+ +
+
+
+ +
+
+ + +
+ +
+ + +
+
Overwriting src/ln_exp.cpp
+
+
+
+ +
+
+ +
+
+
+
In [10]:
+
+
+
%%file src/ln_opt.cpp
+#define PY_ARRAY_UNIQUE_SYMBOL ln_hope_opt_ARRAY_API
+#define NPY_NO_DEPRECATED_API NPY_1_7_API_VERSION
+#include <Python.h>
+#include <numpy/arrayobject.h>
+#include <numpy/arrayscalars.h>
+#include <cmath>
+#include <tuple>
+#include <numeric>
+#include <cstdint>
+#include <cstdlib>
+#include <exception>
+#include <functional>
+#include <type_traits>
+struct PyObj {
+    typedef PyObject * ptr_t;
+    typedef PyArrayObject * arrptr_t;
+    PyObj(): dec(false), ptr(NULL) {}
+    PyObj(ptr_t p): dec(false), ptr(p) {}
+    ~PyObj() { if(dec) Py_DECREF(ptr); }
+    PyObj & operator=(ptr_t p) { if(dec) Py_DECREF(ptr); ptr = p; dec = false; return *this; }
+    PyObj & incref(ptr_t p) { if(dec) Py_DECREF(ptr); ptr = p; dec = (p != NULL); return *this; }
+    operator bool() const { return ptr; }
+    operator ptr_t() const { return ptr; }
+    operator arrptr_t() const { return (arrptr_t)ptr; }
+    bool dec;
+    ptr_t ptr;
+};
+
+inline void ln_hope_opt_d1d1(PyObject * pX, npy_intp const * __restrict__ sX, npy_double * __restrict__ cX,
+                                                    PyObject * pY, npy_intp const * __restrict__ sY, npy_double * __restrict__ cY);
+
+inline void ln_hope_opt_d1d1(PyObject * pX, npy_intp const * __restrict__ sX, npy_double * __restrict__ cX,
+                                                    PyObject * pY, npy_intp const * __restrict__ sY, npy_double * __restrict__ cY){
+
+    for (npy_intp i0 = 0; i0 < sY[0] - 0; ++i0) {
+        auto c__sp0 = (cX[i0] * cX[i0]);
+        auto c__sp1 = (c__sp0 * c__sp0);
+        auto c__sp2 = (c__sp1 * c__sp1);
+        auto c__sp3 = (c__sp2 * cX[i0]);
+        auto c__sp4 = (c__sp0 * cX[i0]);
+        auto c__sp5 = (c__sp4 * c__sp4);
+        auto c__sp6 = (c__sp5 * cX[i0]);
+        auto c__sp7 = (c__sp1 * cX[i0]);
+        cY[(int)(i0)] = (-7129.0 / 2520.0) + (28 * c__sp4) + (-(18 * c__sp0)) + (-(9 * c__sp2 / 8)) + (-(14 * c__sp5)) + (-(63 * c__sp1 / 2)) + (126 * c__sp7 / 5) + (9 * cX[i0]) + (c__sp3 / 9) + (36 * c__sp6 / 7);
+    }
+}
+
+void sighandler(int sig);
+#include <signal.h>
+extern "C" {
+    PyObject * create_signature;
+    struct sigaction slot;
+    PyObject * set_create_signature(PyObject * self, PyObject * args) {
+        if (!PyArg_ParseTuple(args, "O", &create_signature)) {
+            PyErr_SetString(PyExc_ValueError, "Invalid Argument to set_create_signature!");
+            return NULL;
+        }
+        Py_INCREF(create_signature);
+        memset(&slot, 0, sizeof(slot));
+        slot.sa_handler = &sighandler;
+        sigaction(SIGSEGV, &slot, NULL);
+        sigaction(SIGBUS, &slot, NULL);
+        Py_INCREF(Py_None);
+        return Py_None;
+    }
+    PyObject * run(PyObject * self, PyObject * args) {
+        {
+            PyObj pX;
+            PyObj pY;
+            if (
+                PyTuple_CheckExact(args) and PyTuple_GET_SIZE(args) == 2
+                and (pX = PyTuple_GET_ITEM(args, 0)) and PyArray_CheckExact(pX)
+                and PyArray_TYPE((PyArrayObject *)pX) == NPY_FLOAT64 and PyArray_NDIM((PyArrayObject *)pX) == 1
+                and (pY = PyTuple_GET_ITEM(args, 1)) and PyArray_CheckExact(pY)
+                and PyArray_TYPE((PyArrayObject *)pY) == NPY_FLOAT64 and PyArray_NDIM((PyArrayObject *)pY) == 1
+            ) {
+                if (!(pX.incref((PyObject *)PyArray_GETCONTIGUOUS((PyArrayObject *)pX)))) {
+                    PyErr_SetString(PyExc_ValueError, "Invalid Argument type on X!");
+                    return NULL;
+                }
+                if (!(pY.incref((PyObject *)PyArray_GETCONTIGUOUS((PyArrayObject *)pY)))) {
+                    PyErr_SetString(PyExc_ValueError, "Invalid Argument type on Y!");
+                    return NULL;
+                }
+                try {
+                    ln_hope_opt_d1d1(
+                          pX, PyArray_SHAPE((PyArrayObject *)pX), (npy_double *)PyArray_DATA((PyArrayObject *)pX)
+                        , pY, PyArray_SHAPE((PyArrayObject *)pY), (npy_double *)PyArray_DATA((PyArrayObject *)pY)
+                    );
+                    Py_INCREF(Py_None);
+                    return Py_None;
+                } catch (...) {
+                    return NULL;
+                }
+            } else
+                PyErr_Clear();
+        }
+        PyObject * signatures = Py_BuildValue("(sO)", "(lp0\n(lp1\nccopy_reg\n_reconstructor\np2\n(chope._ast\nVariable\np3\nc__builtin__\nobject\np4\nNtp5\nRp6\n(dp7\nS'name'\np8\nS'X'\np9\nsS'dtype'\np10\nS'float64'\np11\nsS'dims'\np12\nI1\nsbag2\n(g3\ng4\nNtp13\nRp14\n(dp15\ng8\nS'Y'\np16\nsg10\ng11\nsg12\nI1\nsbaa.", args);
+        if (!signatures) {
+            PyErr_SetString(PyExc_ValueError, "Error building signature string for ln_hope_opt");
+            return NULL;
+        }
+        return PyObject_Call(create_signature, signatures, NULL);
+    }
+
+    PyMethodDef ln_optMethods[] = {
+        { "set_create_signature", set_create_signature, METH_VARARGS, "signal handler" },
+        { "run", run, METH_VARARGS, "module function" },
+        { NULL, NULL, 0, NULL }
+    };
+
+
+    static struct PyModuleDef ln_optmodule = {
+        PyModuleDef_HEAD_INIT,
+        "ln",
+        NULL,
+        -1,
+        ln_optMethods
+    };
+
+
+
+   PyMODINIT_FUNC PyInit_ln_opt(void) {
+        import_array();
+        PyImport_ImportModule("numpy");
+        return PyModule_Create(&ln_optmodule);
+   }
+}
+#include <string>
+#include <sstream>
+#include <iostream>
+#include <cxxabi.h>
+#include <execinfo.h>
+void sighandler(int sig) {
+    std::ostringstream buffer;
+    buffer << "Abort by " << (sig == SIGSEGV ? "segfault" : "bus error") << std::endl;
+    void * stack[64];
+    std::size_t depth = backtrace(stack, 64);
+    if (!depth)
+        buffer << "  <empty stacktrace, possibly corrupt>" << std::endl;
+    else {
+        char ** symbols = backtrace_symbols(stack, depth);
+        for (std::size_t i = 1; i < depth; ++i) {
+            std::string symbol = symbols[i];
+                if (symbol.find_first_of(' ', 59) != std::string::npos) {
+                    std::string name = symbol.substr(59, symbol.find_first_of(' ', 59) - 59);
+                    int status;
+                    char * demangled = abi::__cxa_demangle(name.c_str(), NULL, NULL, &status);
+                    if (!status) {
+                        buffer << "    "
+                            << symbol.substr(0, 59)
+                            << demangled
+                            << symbol.substr(59 + name.size())
+                            << std::endl;
+                        free(demangled);
+                    } else
+                        buffer << "    " << symbol << std::endl;
+                } else
+                    buffer << "    " << symbol << std::endl;
+            }
+            free(symbols);
+        }
+        std::cerr << buffer.str();
+        std::exit(EXIT_FAILURE);
+    }
+
+ +
+
+
+ +
+
+ + +
+ +
+ + +
+
Overwriting src/ln_opt.cpp
+
+
+
+ +
+
+ +
+
+
+
+
+

Simplify CPP

+
+
+
+
+
+
In [11]:
+
+
+
%%file src/poly.cpp
+#define PY_ARRAY_UNIQUE_SYMBOL poly_ARRAY_API
+#define NPY_NO_DEPRECATED_API NPY_1_7_API_VERSION
+#include <Python.h>
+#include <numpy/arrayobject.h>
+#include <numpy/arrayscalars.h>
+#include <cmath>
+#include <tuple>
+#include <numeric>
+#include <cstdint>
+#include <cstdlib>
+#include <exception>
+#include <functional>
+#include <type_traits>
+struct PyObj {
+    typedef PyObject * ptr_t;
+    typedef PyArrayObject * arrptr_t;
+    PyObj(): dec(false), ptr(NULL) {}
+    PyObj(ptr_t p): dec(false), ptr(p) {}
+    ~PyObj() { if(dec) Py_DECREF(ptr); }
+    PyObj & operator=(ptr_t p) { if(dec) Py_DECREF(ptr); ptr = p; dec = false; return *this; }
+    PyObj & incref(ptr_t p) { if(dec) Py_DECREF(ptr); ptr = p; dec = (p != NULL); return *this; }
+    operator bool() const { return ptr; }
+    operator ptr_t() const { return ptr; }
+    operator arrptr_t() const { return (arrptr_t)ptr; }
+    bool dec;
+    ptr_t ptr;
+};
+inline void poly_d1d1(PyObject * pres, npy_intp const * __restrict__ sres, npy_double * __restrict__ cres,
+                                            PyObject * parg, npy_intp const * __restrict__ sarg, npy_double * __restrict__ carg);
+
+inline void poly_d1d1(PyObject * pres, npy_intp const * __restrict__ sres, npy_double * __restrict__ cres,
+                                            PyObject * parg, npy_intp const * __restrict__ sarg, npy_double * __restrict__ carg){
+
+    npy_double arg_i;
+    double sin_arg_i;
+    for (npy_intp i0 = 0; i0 < sres[0] - 0; ++i0) {
+        arg_i = carg[i0];
+        cres[(int)(i0)] = std::pow(std::sin(arg_i), 2) + (std::pow(arg_i, 3) + std::pow(arg_i, 2) - arg_i - 1) / (std::pow(arg_i, 2) + 2 * arg_i + 1) + std::pow(std::cos(arg_i), 2);
+    }
+}
+void sighandler(int sig);
+#include <signal.h>
+extern "C" {
+    PyObject * create_signature;
+    struct sigaction slot;
+    PyObject * set_create_signature(PyObject * self, PyObject * args) {
+        if (!PyArg_ParseTuple(args, "O", &create_signature)) {
+            PyErr_SetString(PyExc_ValueError, "Invalid Argument to set_create_signature!");
+            return NULL;
+        }
+        Py_INCREF(create_signature);
+        memset(&slot, 0, sizeof(slot));
+        slot.sa_handler = &sighandler;
+        sigaction(SIGSEGV, &slot, NULL);
+        sigaction(SIGBUS, &slot, NULL);
+        Py_INCREF(Py_None);
+        return Py_None;
+    }
+    PyObject * run(PyObject * self, PyObject * args) {
+        {
+            PyObj pres;
+            PyObj parg;
+            if (
+                PyTuple_CheckExact(args) and PyTuple_GET_SIZE(args) == 2
+                and (pres = PyTuple_GET_ITEM(args, 0)) and PyArray_CheckExact(pres)
+                and PyArray_TYPE((PyArrayObject *)pres) == NPY_FLOAT64 and PyArray_NDIM((PyArrayObject *)pres) == 1
+                and (parg = PyTuple_GET_ITEM(args, 1)) and PyArray_CheckExact(parg)
+                and PyArray_TYPE((PyArrayObject *)parg) == NPY_FLOAT64 and PyArray_NDIM((PyArrayObject *)parg) == 1
+            ) {
+                if (!(pres.incref((PyObject *)PyArray_GETCONTIGUOUS((PyArrayObject *)pres)))) {
+                    PyErr_SetString(PyExc_ValueError, "Invalid Argument type on res!");
+                    return NULL;
+                }
+                if (!(parg.incref((PyObject *)PyArray_GETCONTIGUOUS((PyArrayObject *)parg)))) {
+                    PyErr_SetString(PyExc_ValueError, "Invalid Argument type on arg!");
+                    return NULL;
+                }
+                try {
+                    poly_d1d1(
+                          pres, PyArray_SHAPE((PyArrayObject *)pres), (npy_double *)PyArray_DATA((PyArrayObject *)pres)
+                        , parg, PyArray_SHAPE((PyArrayObject *)parg), (npy_double *)PyArray_DATA((PyArrayObject *)parg)
+                    );
+                    Py_INCREF(Py_None);
+                    return Py_None;
+                } catch (...) {
+                    return NULL;
+                }
+            } else
+                PyErr_Clear();
+        }
+        PyObject * signatures = Py_BuildValue("(sO)", "(lp0\n(lp1\nccopy_reg\n_reconstructor\np2\n(chope._ast\nVariable\np3\nc__builtin__\nobject\np4\nNtp5\nRp6\n(dp7\nS'name'\np8\nS'res'\np9\nsS'dtype'\np10\nS'float64'\np11\nsS'dims'\np12\nI1\nsbag2\n(g3\ng4\nNtp13\nRp14\n(dp15\ng8\nS'arg'\np16\nsg10\ng11\nsg12\nI1\nsbaa.", args);
+        if (!signatures) {
+            PyErr_SetString(PyExc_ValueError, "Error building signature string for poly");
+            return NULL;
+        }
+        return PyObject_Call(create_signature, signatures, NULL);
+    }
+
+    PyMethodDef polyMethods[] = {
+        { "set_create_signature", set_create_signature, METH_VARARGS, "signal handler" },
+        { "run", run, METH_VARARGS, "module function" },
+        { NULL, NULL, 0, NULL }
+    };
+
+    static struct PyModuleDef polymodule = {
+        PyModuleDef_HEAD_INIT,
+        "poly",
+        NULL,
+        -1,
+        polyMethods
+    };
+
+   PyMODINIT_FUNC PyInit_poly(void) {
+        import_array();
+        PyImport_ImportModule("numpy");
+        return PyModule_Create(&polymodule);
+   }
+}
+#include <string>
+#include <sstream>
+#include <iostream>
+#include <cxxabi.h>
+#include <execinfo.h>
+void sighandler(int sig) {
+    std::ostringstream buffer;
+    buffer << "Abort by " << (sig == SIGSEGV ? "segfault" : "bus error") << std::endl;
+    void * stack[64];
+    std::size_t depth = backtrace(stack, 64);
+    if (!depth)
+        buffer << "  <empty stacktrace, possibly corrupt>" << std::endl;
+    else {
+        char ** symbols = backtrace_symbols(stack, depth);
+        for (std::size_t i = 1; i < depth; ++i) {
+            std::string symbol = symbols[i];
+                if (symbol.find_first_of(' ', 59) != std::string::npos) {
+                    std::string name = symbol.substr(59, symbol.find_first_of(' ', 59) - 59);
+                    int status;
+                    char * demangled = abi::__cxa_demangle(name.c_str(), NULL, NULL, &status);
+                    if (!status) {
+                        buffer << "    "
+                            << symbol.substr(0, 59)
+                            << demangled
+                            << symbol.substr(59 + name.size())
+                            << std::endl;
+                        free(demangled);
+                    } else
+                        buffer << "    " << symbol << std::endl;
+                } else
+                    buffer << "    " << symbol << std::endl;
+            }
+            free(symbols);
+        }
+        std::cerr << buffer.str();
+        std::exit(EXIT_FAILURE);
+    }
+
+ +
+
+
+ +
+
+ + +
+ +
+ + +
+
Overwriting src/poly.cpp
+
+
+
+ +
+
+ +
+
+
+
In [12]:
+
+
+
%%file src/poly_opt.cpp
+#define PY_ARRAY_UNIQUE_SYMBOL poly_ARRAY_API
+#define NPY_NO_DEPRECATED_API NPY_1_7_API_VERSION
+#include <Python.h>
+#include <numpy/arrayobject.h>
+#include <numpy/arrayscalars.h>
+#include <cmath>
+#include <tuple>
+#include <numeric>
+#include <cstdint>
+#include <cstdlib>
+#include <exception>
+#include <functional>
+#include <type_traits>
+struct PyObj {
+	typedef PyObject * ptr_t;
+	typedef PyArrayObject * arrptr_t;
+	PyObj(): dec(false), ptr(NULL) {}
+	PyObj(ptr_t p): dec(false), ptr(p) {}
+	~PyObj() { if(dec) Py_DECREF(ptr); }
+	PyObj & operator=(ptr_t p) { if(dec) Py_DECREF(ptr); ptr = p; dec = false; return *this; }
+	PyObj & incref(ptr_t p) { if(dec) Py_DECREF(ptr); ptr = p; dec = (p != NULL); return *this; }
+	operator bool() const { return ptr; }
+	operator ptr_t() const { return ptr; }
+	operator arrptr_t() const { return (arrptr_t)ptr; }
+	bool dec;
+	ptr_t ptr;
+};
+
+inline void poly_d1d1(PyObject * pres, npy_intp const * __restrict__ sres, npy_double * __restrict__ cres, 
+											PyObject * parg, npy_intp const * __restrict__ sarg, npy_double * __restrict__ carg);
+
+inline void poly_d1d1(PyObject * pres, npy_intp const * __restrict__ sres, npy_double * __restrict__ cres, 
+											PyObject * parg, npy_intp const * __restrict__ sarg, npy_double * __restrict__ carg){
+	for (npy_intp i0 = 0; i0 < sres[0] - 0; ++i0) {
+		cres[(int)(i0)] = carg[i0];
+	}
+}
+
+void sighandler(int sig);
+#include <signal.h>
+extern "C" {
+	PyObject * create_signature;
+	struct sigaction slot;
+	PyObject * set_create_signature(PyObject * self, PyObject * args) {
+		if (!PyArg_ParseTuple(args, "O", &create_signature)) {
+			PyErr_SetString(PyExc_ValueError, "Invalid Argument to set_create_signature!");
+			return NULL;
+		}
+		Py_INCREF(create_signature);
+		memset(&slot, 0, sizeof(slot));
+		slot.sa_handler = &sighandler;
+		sigaction(SIGSEGV, &slot, NULL);
+		sigaction(SIGBUS, &slot, NULL);
+		Py_INCREF(Py_None);
+		return Py_None;
+	}
+	PyObject * run(PyObject * self, PyObject * args) {
+		{
+			PyObj pres;
+			PyObj parg;
+			if (
+				PyTuple_CheckExact(args) and PyTuple_GET_SIZE(args) == 2
+				and (pres = PyTuple_GET_ITEM(args, 0)) and PyArray_CheckExact(pres)
+				and PyArray_TYPE((PyArrayObject *)pres) == NPY_FLOAT64 and PyArray_NDIM((PyArrayObject *)pres) == 1
+				and (parg = PyTuple_GET_ITEM(args, 1)) and PyArray_CheckExact(parg)
+				and PyArray_TYPE((PyArrayObject *)parg) == NPY_FLOAT64 and PyArray_NDIM((PyArrayObject *)parg) == 1
+			) {
+				if (!(pres.incref((PyObject *)PyArray_GETCONTIGUOUS((PyArrayObject *)pres)))) {
+					PyErr_SetString(PyExc_ValueError, "Invalid Argument type on res!");
+					return NULL;
+				}
+				if (!(parg.incref((PyObject *)PyArray_GETCONTIGUOUS((PyArrayObject *)parg)))) {
+					PyErr_SetString(PyExc_ValueError, "Invalid Argument type on arg!");
+					return NULL;
+				}
+				try {
+					poly_d1d1(
+						  pres, PyArray_SHAPE((PyArrayObject *)pres), (npy_double *)PyArray_DATA((PyArrayObject *)pres)
+						, parg, PyArray_SHAPE((PyArrayObject *)parg), (npy_double *)PyArray_DATA((PyArrayObject *)parg)
+					);
+					Py_INCREF(Py_None);
+					return Py_None;
+				} catch (...) {
+					return NULL;
+				}
+			} else
+				PyErr_Clear();
+		}
+		PyObject * signatures = Py_BuildValue("(sO)", "(lp0\n(lp1\nccopy_reg\n_reconstructor\np2\n(chope._ast\nVariable\np3\nc__builtin__\nobject\np4\nNtp5\nRp6\n(dp7\nS'name'\np8\nS'res'\np9\nsS'dtype'\np10\nS'float64'\np11\nsS'dims'\np12\nI1\nsbag2\n(g3\ng4\nNtp13\nRp14\n(dp15\ng8\nS'arg'\np16\nsg10\ng11\nsg12\nI1\nsbaa.", args);
+		if (!signatures) {
+			PyErr_SetString(PyExc_ValueError, "Error building signature string for poly");
+			return NULL;
+		}
+		return PyObject_Call(create_signature, signatures, NULL);
+	}
+	PyMethodDef polyMethods[] = {
+		{ "set_create_signature", (PyCFunction)set_create_signature, METH_VARARGS },
+		{ "run", (PyCFunction)run, METH_VARARGS },
+		{ NULL, NULL }
+	};
+	PyMODINIT_FUNC initpoly(void) {
+		import_array();
+		PyImport_ImportModule("numpy");
+		(void)Py_InitModule("poly", polyMethods);
+	}
+}
+#include <string>
+#include <sstream>
+#include <iostream>
+#include <cxxabi.h>
+#include <execinfo.h>
+void sighandler(int sig) {
+	std::ostringstream buffer;
+	buffer << "Abort by " << (sig == SIGSEGV ? "segfault" : "bus error") << std::endl;
+	void * stack[64];
+	std::size_t depth = backtrace(stack, 64);
+	if (!depth)
+		buffer << "  <empty stacktrace, possibly corrupt>" << std::endl;
+	else {
+		char ** symbols = backtrace_symbols(stack, depth);
+		for (std::size_t i = 1; i < depth; ++i) {
+			std::string symbol = symbols[i];
+				if (symbol.find_first_of(' ', 59) != std::string::npos) {
+					std::string name = symbol.substr(59, symbol.find_first_of(' ', 59) - 59);
+					int status;
+					char * demangled = abi::__cxa_demangle(name.c_str(), NULL, NULL, &status);
+					if (!status) {
+						buffer << "    " 
+							<< symbol.substr(0, 59) 
+							<< demangled
+							<< symbol.substr(59 + name.size())
+							<< std::endl;
+						free(demangled);
+					} else
+						buffer << "    " << symbol << std::endl;
+				} else
+					buffer << "    " << symbol << std::endl;
+			}
+			free(symbols);
+		}
+		std::cerr << buffer.str();
+		std::exit(EXIT_FAILURE);
+	}
+
+ +
+
+
+ +
+
+ + +
+ +
+ + +
+
Overwriting src/poly_opt.cpp
+
+
+
+ +
+
+ +
+
+
+
+
+

Pairwise distance

+
+
+
+
+
+
In [13]:
+
+
+
%%file src/pairwise.cpp
+#define PY_ARRAY_UNIQUE_SYMBOL pairwise_ARRAY_API
+#define NPY_NO_DEPRECATED_API NPY_1_7_API_VERSION
+#include <Python.h>
+#include <numpy/arrayobject.h>
+#include <numpy/arrayscalars.h>
+#include <cmath>
+#include <tuple>
+#include <numeric>
+#include <cstdint>
+#include <cstdlib>
+#include <exception>
+#include <functional>
+#include <type_traits>
+struct PyObj {
+    typedef PyObject * ptr_t;
+    typedef PyArrayObject * arrptr_t;
+    PyObj(): dec(false), ptr(NULL) {}
+    PyObj(ptr_t p): dec(false), ptr(p) {}
+    ~PyObj() { if(dec) Py_DECREF(ptr); }
+    PyObj & operator=(ptr_t p) { if(dec) Py_DECREF(ptr); ptr = p; dec = false; return *this; }
+    PyObj & incref(ptr_t p) { if(dec) Py_DECREF(ptr); ptr = p; dec = (p != NULL); return *this; }
+    operator bool() const { return ptr; }
+    operator ptr_t() const { return ptr; }
+    operator arrptr_t() const { return (arrptr_t)ptr; }
+    bool dec;
+    ptr_t ptr;
+};
+
+inline void pairwise_d2d2JJ(PyObject * pX,
+                                                        npy_intp const * __restrict__ sX,
+                                                        npy_double * __restrict__ cX,
+                                                        PyObject * pD,
+                                                        npy_intp const * __restrict__ sD,
+                                                        npy_double * __restrict__ cD,
+                                                        npy_int64 const cM,
+                                                        npy_int64 const cN);
+
+inline void pairwise_d2d2JJ(PyObject * pX,
+                                                        npy_intp const * __restrict__ sX,
+                                                        npy_double * __restrict__ cX,
+                                                        PyObject * pD,
+                                                        npy_intp const * __restrict__ sD,
+                                                        npy_double * __restrict__ cD,
+                                                        npy_int64 const cM,
+                                                        npy_int64 const cN){
+
+    npy_double cd = 0.0;
+    npy_double ctmp = 0.0;
+
+    npy_intp cj;
+    npy_intp ck;
+    npy_intp x_i_idx;
+    npy_intp x_j_idx;
+    npy_intp d_i_idx;
+    npy_intp const xp = sX[1];
+    npy_intp const dp = sD[1];
+
+    for (npy_intp ci = 0; ci < cM; ++ci) {
+        x_i_idx = ci*xp;
+        d_i_idx = ci*dp;
+        for (cj = 0; cj < cM; ++cj) {
+            cd = 0.0;
+            x_j_idx = cj*xp;
+            for (ck = 0; ck < cN; ++ck) {
+                ctmp = (cX[(x_i_idx + ck)] - cX[(x_j_idx + ck)]);
+                cd += (ctmp * ctmp);
+            }
+            cD[(d_i_idx + cj)] = std::sqrt(cd);
+        }
+    }
+}
+
+void sighandler(int sig);
+#include <signal.h>
+extern "C" {
+    PyObject * create_signature;
+    struct sigaction slot;
+    PyObject * set_create_signature(PyObject * self, PyObject * args) {
+        if (!PyArg_ParseTuple(args, "O", &create_signature)) {
+            PyErr_SetString(PyExc_ValueError, "Invalid Argument to set_create_signature!");
+            return NULL;
+        }
+        Py_INCREF(create_signature);
+        memset(&slot, 0, sizeof(slot));
+        slot.sa_handler = &sighandler;
+        sigaction(SIGSEGV, &slot, NULL);
+        sigaction(SIGBUS, &slot, NULL);
+        Py_INCREF(Py_None);
+        return Py_None;
+    }
+    PyObject * run(PyObject * self, PyObject * args) {
+        {
+            PyObj pX;
+            PyObj pD;
+            PyObject * pM; npy_int64 cM;
+            PyObject * pN; npy_int64 cN;
+            if (
+                PyTuple_CheckExact(args) and PyTuple_GET_SIZE(args) == 4
+                and (pX = PyTuple_GET_ITEM(args, 0)) and PyArray_CheckExact(pX)
+                and PyArray_TYPE((PyArrayObject *)pX) == NPY_FLOAT64 and PyArray_NDIM((PyArrayObject *)pX) == 2
+                and (pD = PyTuple_GET_ITEM(args, 1)) and PyArray_CheckExact(pD)
+                and PyArray_TYPE((PyArrayObject *)pD) == NPY_FLOAT64 and PyArray_NDIM((PyArrayObject *)pD) == 2
+                and (pM = PyTuple_GET_ITEM(args, 2)) and PyLong_CheckExact(pM)
+                and (pN = PyTuple_GET_ITEM(args, 3)) and PyLong_CheckExact(pN)
+            ) {
+                if (!(pX.incref((PyObject *)PyArray_GETCONTIGUOUS((PyArrayObject *)pX)))) {
+                    PyErr_SetString(PyExc_ValueError, "Invalid Argument type on X!");
+                    return NULL;
+                }
+                if (!(pD.incref((PyObject *)PyArray_GETCONTIGUOUS((PyArrayObject *)pD)))) {
+                    PyErr_SetString(PyExc_ValueError, "Invalid Argument type on D!");
+                    return NULL;
+                }
+                cM = PyLong_AS_LONG(pM);
+                cN = PyLong_AS_LONG(pN);
+                try {
+                    pairwise_d2d2JJ(
+                          pX, PyArray_SHAPE((PyArrayObject *)pX), (npy_double *)PyArray_DATA((PyArrayObject *)pX)
+                        , pD, PyArray_SHAPE((PyArrayObject *)pD), (npy_double *)PyArray_DATA((PyArrayObject *)pD)
+                        , cM
+                        , cN
+                    );
+                    Py_INCREF(Py_None);
+                    return Py_None;
+                } catch (...) {
+                    return NULL;
+                }
+            } else
+                PyErr_Clear();
+        }
+        PyObject * signatures = Py_BuildValue("(sO)", "(lp0\n(lp1\nccopy_reg\n_reconstructor\np2\n(chope._ast\nVariable\np3\nc__builtin__\nobject\np4\nNtp5\nRp6\n(dp7\nS'name'\np8\nS'X'\np9\nsS'dtype'\np10\nS'float64'\np11\nsS'dims'\np12\nI2\nsbag2\n(g3\ng4\nNtp13\nRp14\n(dp15\ng8\nS'D'\np16\nsg10\ng11\nsg12\nI2\nsbag2\n(g3\ng4\nNtp17\nRp18\n(dp19\ng8\nS'M'\np20\nsg10\nc__builtin__\nint\np21\nsg12\nI0\nsbag2\n(g3\ng4\nNtp22\nRp23\n(dp24\ng8\nS'N'\np25\nsg10\ng21\nsg12\nI0\nsbaa.", args);
+        if (!signatures) {
+            PyErr_SetString(PyExc_ValueError, "Error building signature string for pairwise");
+            return NULL;
+        }
+        return PyObject_Call(create_signature, signatures, NULL);
+    }
+
+    PyMethodDef pairwiseMethods[] = {
+        { "set_create_signature", set_create_signature, METH_VARARGS, "signal handler" },
+        { "run", run, METH_VARARGS, "module function" },
+        { NULL, NULL, 0, NULL }
+    };
+
+
+    static struct PyModuleDef pairwisemodule = {
+        PyModuleDef_HEAD_INIT,
+        "pairwise",
+        NULL,
+        -1,
+        pairwiseMethods
+    };
+
+
+   PyMODINIT_FUNC PyInit_pairwise(void) {
+        import_array();
+        PyImport_ImportModule("numpy");
+        return PyModule_Create(&pairwisemodule);
+   }
+}
+#include <string>
+#include <sstream>
+#include <iostream>
+#include <cxxabi.h>
+#include <execinfo.h>
+void sighandler(int sig) {
+    std::ostringstream buffer;
+    buffer << "Abort by " << (sig == SIGSEGV ? "segfault" : "bus error") << std::endl;
+    void * stack[64];
+    std::size_t depth = backtrace(stack, 64);
+    if (!depth)
+        buffer << "  <empty stacktrace, possibly corrupt>" << std::endl;
+    else {
+        char ** symbols = backtrace_symbols(stack, depth);
+        for (std::size_t i = 1; i < depth; ++i) {
+            std::string symbol = symbols[i];
+                if (symbol.find_first_of(' ', 59) != std::string::npos) {
+                    std::string name = symbol.substr(59, symbol.find_first_of(' ', 59) - 59);
+                    int status;
+                    char * demangled = abi::__cxa_demangle(name.c_str(), NULL, NULL, &status);
+                    if (!status) {
+                        buffer << "    "
+                            << symbol.substr(0, 59)
+                            << demangled
+                            << symbol.substr(59 + name.size())
+                            << std::endl;
+                        free(demangled);
+                    } else
+                        buffer << "    " << symbol << std::endl;
+                } else
+                    buffer << "    " << symbol << std::endl;
+            }
+            free(symbols);
+        }
+        std::cerr << buffer.str();
+        std::exit(EXIT_FAILURE);
+    }
+
+ +
+
+
+ +
+
+ + +
+ +
+ + +
+
Overwriting src/pairwise.cpp
+
+
+
+ +
+
+ +
+
+
+
+
+

Star point spread function CPP

+ +
+
+
+
+
+
In [14]:
+
+
+
%%file src/pdf.cpp
+#define PY_ARRAY_UNIQUE_SYMBOL pdf_ARRAY_API
+#define NPY_NO_DEPRECATED_API NPY_1_7_API_VERSION
+#include <Python.h>
+#include <numpy/arrayobject.h>
+#include <numpy/arrayscalars.h>
+#include <cmath>
+#include <tuple>
+#include <numeric>
+#include <cstdint>
+#include <cstdlib>
+#include <exception>
+#include <functional>
+#include <type_traits>
+struct PyObj {
+    typedef PyObject * ptr_t;
+    typedef PyArrayObject * arrptr_t;
+    PyObj(): dec(false), ptr(NULL) {}
+    PyObj(ptr_t p): dec(false), ptr(p) {}
+    ~PyObj() { if(dec) Py_DECREF(ptr); }
+    PyObj & operator=(ptr_t p) { if(dec) Py_DECREF(ptr); ptr = p; dec = false; return *this; }
+    PyObj & incref(ptr_t p) { if(dec) Py_DECREF(ptr); ptr = p; dec = (p != NULL); return *this; }
+    operator bool() const { return ptr; }
+    operator ptr_t() const { return ptr; }
+    operator arrptr_t() const { return (arrptr_t)ptr; }
+    bool dec;
+    ptr_t ptr;
+};
+
+inline std::tuple<PyObject *, npy_intp const *, npy_float *> pdf_f2l1d1f2JDd(
+      PyObject * pdensity
+  , npy_intp const * __restrict__ sdensity
+  , npy_float * __restrict__ cdensity
+    , PyObject * pdims
+    , npy_intp const * __restrict__ sdims
+    , npy_int64 * __restrict__ cdims
+    , PyObject * pcenter
+    , npy_intp const * __restrict__ scenter
+    , npy_double * __restrict__ ccenter
+    , PyObject * pw2D
+    , npy_intp const * __restrict__ sw2D
+    , npy_float * __restrict__ cw2D
+    , npy_int64 cr50
+    , npy_double cb
+    , npy_double ca);
+
+inline std::tuple<PyObject *, npy_intp const *, npy_float *> pdf_f2l1d1f2JDd(
+      PyObject * pdensity
+  , npy_intp const * __restrict__ sdensity
+  , npy_float * __restrict__ cdensity
+    , PyObject * pdims
+    , npy_intp const * __restrict__ sdims
+    , npy_int64 * __restrict__ cdims
+    , PyObject * pcenter
+    , npy_intp const * __restrict__ scenter
+    , npy_double * __restrict__ ccenter
+    , PyObject * pw2D
+    , npy_intp const * __restrict__ sw2D
+    , npy_float * __restrict__ cw2D
+    , npy_int64 const cr50
+    , npy_double const cb
+    , npy_double const ca) {
+
+    npy_double cdr;
+    npy_double c__sum0;
+    const npy_double x_center = ccenter[0];
+    const npy_double y_center = ccenter[1];
+    const npy_intp len_0_sw2D = sw2D[0];
+    const npy_intp len_1_sw2D = sw2D[1];
+    npy_intp dc[] = {len_0_sw2D, len_1_sw2D};
+    PyObject * pc = PyArray_EMPTY(2, dc, NPY_FLOAT64, 0);
+    npy_intp * sc = PyArray_SHAPE((PyArrayObject *)pc);
+    npy_double * cc = (npy_double *)PyArray_DATA((PyArrayObject *)pc);
+
+    npy_intp cy = 0;
+    npy_intp i0 = 0;
+    npy_intp i1 = 0;
+    npy_intp i2 = 0;
+    npy_intp i3 = 0;
+
+    npy_intp sw2D_i0_idx;
+    npy_intp sw2D_i2_idx;
+    npy_intp density_x_idx;
+
+    auto c__sp0 = ca * ca;
+    auto c__sp1 = cr50 * cr50;
+    auto c__sp2 = 1.0 / (c__sp0 * c__sp1);
+    auto c__sp4 = 0.3183098861846737 * c__sp2 * (-1 + cb);
+
+    for (npy_intp cx = 0; cx < cdims[(int)(0)]; ++cx) {
+
+        density_x_idx = cx*sdensity[1];
+        for (cy = 0; cy < cdims[(int)(1)]; ++cy) {
+
+            cdr = std::sqrt(std::pow(cx - x_center, 2) + std::pow(cy - y_center, 2));
+            auto c__sp3 = cdr * cdr;
+            auto c__sp5 = c__sp4 * std::pow((1 + c__sp2 * c__sp3), -cb);
+
+            for (i0 = 0; i0 < len_0_sw2D - 0; ++i0) {
+
+                sw2D_i0_idx = (i0)*len_1_sw2D;
+                for (i1 = 0; i1 < len_1_sw2D - 0; ++i1) {
+                    cc[sw2D_i0_idx + i1] = c__sp5 * cw2D[sw2D_i0_idx + i1];
+                }
+            }
+
+            c__sum0 = 0;
+            for (i2 = 0; i2 < len_0_sw2D - 0; ++i2) {
+
+                sw2D_i2_idx = (i2)*len_1_sw2D;
+                for (i3 = 0; i3 < len_1_sw2D - 0; ++i3) {
+                    c__sum0 += cc[sw2D_i2_idx + i3];
+                }
+            }
+
+            cdensity[(density_x_idx + cy)] = c__sum0;
+        }
+    }
+    return std::make_tuple((PyObject *)pdensity, sdensity, cdensity);
+}
+
+void sighandler(int sig);
+#include <signal.h>
+extern "C" {
+    PyObject * create_signature;
+    struct sigaction slot;
+    PyObject * set_create_signature(PyObject * self, PyObject * args) {
+        if (!PyArg_ParseTuple(args, "O", &create_signature)) {
+            PyErr_SetString(PyExc_ValueError, "Invalid Argument to set_create_signature!");
+            return NULL;
+        }
+        Py_INCREF(create_signature);
+        memset(&slot, 0, sizeof(slot));
+        slot.sa_handler = &sighandler;
+        sigaction(SIGSEGV, &slot, NULL);
+        sigaction(SIGBUS, &slot, NULL);
+        Py_INCREF(Py_None);
+        return Py_None;
+    }
+    PyObject * run(PyObject * self, PyObject * args) {
+        {
+            PyObj pdensity;
+            PyObj pdims;
+            PyObj pcenter;
+            PyObj pw2D;
+            PyObject * pr50; npy_int64 cr50;
+            PyObject * pb; npy_double cb;
+            PyObject * pa; npy_double ca;
+            if (
+                PyTuple_CheckExact(args) and PyTuple_GET_SIZE(args) == 7
+                and (pdensity = PyTuple_GET_ITEM(args, 0)) and PyArray_CheckExact(pdensity)
+                and PyArray_TYPE((PyArrayObject *)pdensity) == NPY_FLOAT32 and PyArray_NDIM((PyArrayObject *)pdensity) == 2
+                and (pdims = PyTuple_GET_ITEM(args, 1)) and PyArray_CheckExact(pdims)
+                and PyArray_TYPE((PyArrayObject *)pdims) == NPY_INT64 and PyArray_NDIM((PyArrayObject *)pdims) == 1
+                and (pcenter = PyTuple_GET_ITEM(args, 2)) and PyArray_CheckExact(pcenter)
+                and PyArray_TYPE((PyArrayObject *)pcenter) == NPY_FLOAT64 and PyArray_NDIM((PyArrayObject *)pcenter) == 1
+                and (pw2D = PyTuple_GET_ITEM(args, 3)) and PyArray_CheckExact(pw2D)
+                and PyArray_TYPE((PyArrayObject *)pw2D) == NPY_FLOAT32 and PyArray_NDIM((PyArrayObject *)pw2D) == 2
+                and (pr50 = PyTuple_GET_ITEM(args, 4)) and PyLong_CheckExact(pr50)
+                and (pb = PyTuple_GET_ITEM(args, 5)) and PyFloat_CheckExact(pb)
+                and (pa = PyTuple_GET_ITEM(args, 6)) and PyArray_IsScalar(pa, Double)
+            ) {
+                if (!(pdensity.incref((PyObject *)PyArray_GETCONTIGUOUS((PyArrayObject *)pdensity)))) {
+                    PyErr_SetString(PyExc_ValueError, "Invalid Argument type on density!");
+                    return NULL;
+                }
+                if (!(pdims.incref((PyObject *)PyArray_GETCONTIGUOUS((PyArrayObject *)pdims)))) {
+                    PyErr_SetString(PyExc_ValueError, "Invalid Argument type on dims!");
+                    return NULL;
+                }
+                if (!(pcenter.incref((PyObject *)PyArray_GETCONTIGUOUS((PyArrayObject *)pcenter)))) {
+                    PyErr_SetString(PyExc_ValueError, "Invalid Argument type on center!");
+                    return NULL;
+                }
+                if (!(pw2D.incref((PyObject *)PyArray_GETCONTIGUOUS((PyArrayObject *)pw2D)))) {
+                    PyErr_SetString(PyExc_ValueError, "Invalid Argument type on w2D!");
+                    return NULL;
+                }
+                cr50 = PyLong_AS_LONG(pr50);
+                cb = PyFloat_AS_DOUBLE(pb);
+                ca = PyArrayScalar_VAL(pa, Double);
+                try {
+                    PyObject * res = std::get<0>(pdf_f2l1d1f2JDd(
+                          pdensity, PyArray_SHAPE((PyArrayObject *)pdensity), (npy_float *)PyArray_DATA((PyArrayObject *)pdensity)
+                        , pdims, PyArray_SHAPE((PyArrayObject *)pdims), (npy_int64 *)PyArray_DATA((PyArrayObject *)pdims)
+                        , pcenter, PyArray_SHAPE((PyArrayObject *)pcenter), (npy_double *)PyArray_DATA((PyArrayObject *)pcenter)
+                        , pw2D, PyArray_SHAPE((PyArrayObject *)pw2D), (npy_float *)PyArray_DATA((PyArrayObject *)pw2D)
+                        , cr50
+                        , cb
+                        , ca
+                    ));
+
+                    Py_INCREF(res);
+                    return res;
+                } catch (...) {
+                    return NULL;
+                }
+            } else
+                PyErr_Clear();
+        }
+        PyObject * signatures = Py_BuildValue("(sO)", "(lp0\n(lp1\nccopy_reg\n_reconstructor\np2\n(chope._ast\nVariable\np3\nc__builtin__\nobject\np4\nNtp5\nRp6\n(dp7\nS'name'\np8\nS'density'\np9\nsS'dtype'\np10\nS'float32'\np11\nsS'dims'\np12\nI2\nsbag2\n(g3\ng4\nNtp13\nRp14\n(dp15\ng8\ng12\nsg10\nS'int64'\np16\nsg12\nI1\nsbag2\n(g3\ng4\nNtp17\nRp18\n(dp19\ng8\nS'center'\np20\nsg10\nS'float64'\np21\nsg12\nI1\nsbag2\n(g3\ng4\nNtp22\nRp23\n(dp24\ng8\nS'w2D'\np25\nsg10\ng11\nsg12\nI2\nsbag2\n(g3\ng4\nNtp26\nRp27\n(dp28\ng8\nS'r50'\np29\nsg10\nc__builtin__\nint\np30\nsg12\nI0\nsbag2\n(g3\ng4\nNtp31\nRp32\n(dp33\ng8\nS'b'\np34\nsg10\nc__builtin__\nfloat\np35\nsg12\nI0\nsbag2\n(g3\ng4\nNtp36\nRp37\n(dp38\ng8\nS'a'\np39\nsg10\ng21\nsg12\nI0\nsbaa.", args);
+        if (!signatures) {
+            PyErr_SetString(PyExc_ValueError, "Error building signature string for pdf");
+            return NULL;
+        }
+        return PyObject_Call(create_signature, signatures, NULL);
+    }
+
+    PyMethodDef pdfMethods[] = {
+        { "set_create_signature", set_create_signature, METH_VARARGS, "signal handler" },
+        { "run", run, METH_VARARGS, "module function" },
+        { NULL, NULL, 0, NULL }
+    };
+
+    static struct PyModuleDef pdfmodule = {
+        PyModuleDef_HEAD_INIT,
+        "pdf",
+        NULL,
+        -1,
+        pdfMethods
+    };
+
+   PyMODINIT_FUNC PyInit_pdf(void) {
+        import_array();
+        PyImport_ImportModule("numpy");
+        return PyModule_Create(&pdfmodule);
+   }
+}
+#include <string>
+#include <sstream>
+#include <iostream>
+#include <cxxabi.h>
+#include <execinfo.h>
+void sighandler(int sig) {
+    std::ostringstream buffer;
+    buffer << "Abort by " << (sig == SIGSEGV ? "segfault" : "bus error") << std::endl;
+    void * stack[64];
+    std::size_t depth = backtrace(stack, 64);
+    if (!depth)
+        buffer << "  <empty stacktrace, possibly corrupt>" << std::endl;
+    else {
+        char ** symbols = backtrace_symbols(stack, depth);
+        for (std::size_t i = 1; i < depth; ++i) {
+            std::string symbol = symbols[i];
+                if (symbol.find_first_of(' ', 59) != std::string::npos) {
+                    std::string name = symbol.substr(59, symbol.find_first_of(' ', 59) - 59);
+                    int status;
+                    char * demangled = abi::__cxa_demangle(name.c_str(), NULL, NULL, &status);
+                    if (!status) {
+                        buffer << "    "
+                            << symbol.substr(0, 59)
+                            << demangled
+                            << symbol.substr(59 + name.size())
+                            << std::endl;
+                        free(demangled);
+                    } else
+                        buffer << "    " << symbol << std::endl;
+                } else
+                    buffer << "    " << symbol << std::endl;
+            }
+            free(symbols);
+        }
+        std::cerr << buffer.str();
+        std::exit(EXIT_FAILURE);
+    }
+
+ +
+
+
+ +
+
+ + +
+ +
+ + +
+
Overwriting src/pdf.cpp
+
+
+
+ +
+
+ +
+
+
+
+
+

Python helper module

+
+
+
+
+
+
In [15]:
+
+
+
%%file native_util.py
+import importlib
+import os
+import glob
+import sys
+
+from numpy.distutils.misc_util import get_numpy_include_dirs
+import setuptools
+from os import listdir
+# import tempfile
+from hope import config
+
+try:
+    # python 3
+    import io
+    file = io.IOBase
+except ImportError:
+    pass
+
+
+def load(name):
+    compile(name, "./src")
+    module = importlib.import_module(name)
+    # module = __import__(name, globals(), locals(), [], -1)
+    return module
+
+
+
+def compile(name, src_folder, target_folder = "./"):
+    localfilename =os.path.join(src_folder, name)
+    
+    outfile, stdout, stderr, argv = None, None, None, sys.argv
+    try:
+        sys.stdout.flush(), sys.stderr.flush()
+        outpath = os.path.join(target_folder, "{0}.out".format(localfilename))
+        if os.path.exists(outpath):
+            os.remove(outpath)
+            
+        so_path = os.path.join(target_folder, "{0}.so".format(name))
+        if os.path.exists(so_path):
+            os.remove(so_path)
+            
+        outfile = open(outpath, 'w')
+        
+        try:
+            sys.stdout.fileno()
+            sys.stderr.fileno()
+            have_fileno = True
+        except OSError:
+            have_fileno = False
+        
+        if have_fileno and isinstance(sys.stdout, file) and  isinstance(sys.stdout, file):
+            stdout, stderr = os.dup(sys.stdout.fileno()), os.dup(sys.stderr.fileno())
+            os.dup2(outfile.fileno(), sys.stdout.fileno())
+            os.dup2(outfile.fileno(), sys.stderr.fileno())
+        else:
+            stdout, stderr = sys.stdout, sys.stderr
+            sys.stdout, sys.stderr = outfile, outfile
+        try:
+            sources = "./{0}.cpp".format(localfilename)
+            sys.argv = ["", "build_ext",
+                        "-b", target_folder,  #--build-lib (-b)     directory for compiled extension modules
+                        "-t", "." #--build-temp - a rel path will result in a dir structure of -b at the cur position 
+                        ]
+    
+            localfilename = str(localfilename)
+            sources = str(sources)
+    
+            setuptools.setup( \
+                  name = name\
+                , ext_modules = [setuptools.Extension( \
+                      name \
+                    , sources = [sources] \
+                    , extra_compile_args = config.cxxflags \
+                  )] \
+                , include_dirs = get_numpy_include_dirs() \
+            )
+        except SystemExit as e:
+            print(sys.stderr.write(str(e)))
+        sys.stdout.flush(), sys.stderr.flush()
+    finally:
+        if isinstance(stdout, int):
+            os.dup2(stdout, sys.stdout.fileno()), os.close(stdout)
+        elif not stdout is None:
+            sys.stdout = stdout
+        if isinstance(stderr, int):
+            os.dup2(stderr, sys.stderr.fileno()), os.close(stderr)
+        elif not stderr is None:
+            sys.stderr = stderr
+        if isinstance(outfile, file):
+            outfile.close()
+        sys.argv = argv
+    
+    with open(outpath) as outfile:
+        out = outfile.read()
+    
+    modules = glob.glob(os.path.join(target_folder, "{}*.so".format(name)))
+        
+    if not modules or out.find("error:") > -1:
+        print(out)
+        raise Exception("Error compiling function {0} (compiled to {1})".format(localfilename, target_folder))
+    
+    if out.find("warning:") > -1:
+        import warnings
+        warnings.warn("A warning has been issued during compilation:\n%s"%out)
+    
+    print(out)
+
+
+def compile_all():
+    src_folder = "./src"
+    func_names = (src_file.split(".cpp")[0] for src_file in listdir(src_folder) if src_file.endswith(".cpp"))
+    for func_name in func_names:
+        compile(func_name, src_folder)
+    
+    
+if __name__ == '__main__':
+    compile_all()
+
+ +
+
+
+ +
+
+ + +
+ +
+ + +
+
Overwriting native_util.py
+
+
+
+ +
+
+ +
+
+
+
In [16]:
+
+
+
%%file util.py
+# Copyright (C) 2014 ETH Zurich, Institute for Astronomy
+
+'''
+Created on Aug 4, 2014
+
+author: jakeret
+'''
+from __future__ import print_function, division, absolute_import, unicode_literals
+import math
+
+def perf_comp_data(func_list, data_list, rep=5, number=1, extra_setup=None):
+    ''' Function to compare the performance of different functions.
+    
+    Parameters
+    ==========
+    func_list : list
+        list with function names as strings
+    data_list : list
+        list with data set names as strings
+    rep : int
+        number of repetitions of the whole comparison
+    number : int
+        number of executions for every function
+    '''
+    from timeit import repeat
+    res_list = {}
+    for name in enumerate(func_list):
+        if data_list is None:
+            stmt = "%s()"%(name[1])
+            setup = "from __main__ import %s"%(name[1]) 
+        else:
+            stmt = "%s(%s)"%(name[1], data_list[name[0]])
+            setup = "from __main__ import %s, %s"%(name[1], data_list[name[0]])
+        if extra_setup is not None:
+            stmt = extra_setup + "; " + stmt
+              
+        results = repeat(stmt=stmt, setup=setup, repeat=rep, number=number)
+        
+        res_list[name[1]] = (median(results), min(results))
+        
+#     res_sort = sorted(res_list.iteritems(), key=lambda (k, v): (v, k))
+    res_sort = sorted(iter(res_list.items()), key=lambda k_v: (k_v[1], k_v[0]))
+    for func, (av_time, min_time) in res_sort:
+        rel = av_time / res_sort[0][1][0]
+        print('function: {0!s:20}, av. time sec: {1:>12.8f}, min. time sec: {2:>12.8f}, relative: {3:>9.1f}'.format(func, av_time, min_time, rel))
+
+def median(x):
+    s_x = sorted(x)
+    return (s_x[int(math.floor((len(s_x)-1)/2))] + s_x[int(math.ceil((len(s_x)-1)/2))])/2
+
+ +
+
+
+ +
+
+ + +
+ +
+ + +
+
Overwriting util.py
+
+
+
+ +
+
+ +
+
+
+ + + + + + diff --git a/benchmarks/native_cpp_gen.nbconvert.ipynb b/benchmarks/native_cpp_gen.nbconvert.ipynb new file mode 100644 index 0000000..716e3c3 --- /dev/null +++ b/benchmarks/native_cpp_gen.nbconvert.ipynb @@ -0,0 +1,2434 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# IPython magic extension version_information" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Use the '%version_information' IPython magic extension in a notebook to display information about which versions of dependency package that was used to run the notebook.\n", + "Installation.\n", + "\n", + "Run\n", + "\n", + " pip install git+https://github.com/jrjohansson/version_information\n", + " \n", + " \n", + "to install this extension" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Installation" + ] + }, + { + "cell_type": "code", + "execution_count": 1, + "metadata": { + "collapsed": true + }, + "outputs": [], + "source": [ + "%load_ext version_information" + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "metadata": {}, + "outputs": [ + { + "data": { + "application/json": { + "Software versions": [ + { + "module": "Python", + "version": "3.5.1 64bit [GCC 4.2.1 (Apple Inc. build 5666) (dot 3)]" + }, + { + "module": "IPython", + "version": "6.1.0" + }, + { + "module": "OS", + "version": "Darwin 15.6.0 x86_64 i386 64bit" + } + ] + }, + "text/html": [ + "
SoftwareVersion
Python3.5.1 64bit [GCC 4.2.1 (Apple Inc. build 5666) (dot 3)]
IPython6.1.0
OSDarwin 15.6.0 x86_64 i386 64bit
Mon Sep 04 16:13:15 2017 CEST
" + ], + "text/latex": [ + "\\begin{tabular}{|l|l|}\\hline\n", + "{\\bf Software} & {\\bf Version} \\\\ \\hline\\hline\n", + "Python & 3.5.1 64bit [GCC 4.2.1 (Apple Inc. build 5666) (dot 3)] \\\\ \\hline\n", + "IPython & 6.1.0 \\\\ \\hline\n", + "OS & Darwin 15.6.0 x86\\_64 i386 64bit \\\\ \\hline\n", + "\\hline \\multicolumn{2}{|l|}{Mon Sep 04 16:13:15 2017 CEST} \\\\ \\hline\n", + "\\end{tabular}\n" + ], + "text/plain": [ + "Software versions\n", + "Python 3.5.1 64bit [GCC 4.2.1 (Apple Inc. build 5666) (dot 3)]\n", + "IPython 6.1.0\n", + "OS Darwin 15.6.0 x86_64 i386 64bit\n", + "Mon Sep 04 16:13:15 2017 CEST" + ] + }, + "execution_count": 2, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "%version_information" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Native CPP codes" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Fibonacci CPP" + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "metadata": {}, + "outputs": [], + "source": [ + "!mkdir -p src" + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Overwriting src/fib.cpp\n" + ] + } + ], + "source": [ + "%%file src/fib.cpp\n", + "#define PY_ARRAY_UNIQUE_SYMBOL fkt_ARRAY_API\n", + "#define NPY_NO_DEPRECATED_API NPY_1_7_API_VERSION\n", + "#include \n", + "#include \n", + "#include \n", + "#include \n", + "#include \n", + "#include \n", + "#include \n", + "#include \n", + "#include \n", + "#include \n", + "#include \n", + "\n", + "\n", + "struct PyObj {\n", + " typedef PyObject * ptr_t;\n", + " typedef PyArrayObject * arrptr_t;\n", + " PyObj(): dec(false), ptr(NULL) {}\n", + " PyObj(ptr_t p): dec(false), ptr(p) {}\n", + " ~PyObj() { if(dec) Py_DECREF(ptr); }\n", + " PyObj & operator=(ptr_t p) { if(dec) Py_DECREF(ptr); ptr = p; dec = false; return *this; }\n", + " PyObj & incref(ptr_t p) { if(dec) Py_DECREF(ptr); ptr = p; dec = (p != NULL); return *this; }\n", + " operator bool() const { return ptr; }\n", + " operator ptr_t() const { return ptr; }\n", + " operator arrptr_t() const { return (arrptr_t)ptr; }\n", + " bool dec;\n", + " ptr_t ptr;\n", + "};\n", + "\n", + "inline npy_int64 fib_J(\n", + " npy_int64 cn\n", + ");\n", + "inline npy_int64 fib_J(\n", + " npy_int64 cn\n", + ") {\n", + " if (cn < 2) {\n", + " return cn;\n", + " }\n", + " return fib_J(cn - 1) + fib_J(cn - 2); \n", + "}\n", + "\n", + "#include \n", + "#include \n", + "#include \n", + "#include \n", + "#include \n", + "#include \n", + "\n", + "void sighandler(int sig);\n", + "\n", + "void sighandler(int sig) {\n", + " std::ostringstream buffer;\n", + " buffer << \"Abort by \" << (sig == SIGSEGV ? \"segfault\" : \"bus error\") << std::endl;\n", + " void * stack[64];\n", + " std::size_t depth = backtrace(stack, 64);\n", + " if (!depth)\n", + " buffer << \" \" << std::endl;\n", + " else {\n", + " char ** symbols = backtrace_symbols(stack, depth);\n", + " for (std::size_t i = 1; i < depth; ++i) {\n", + " std::string symbol = symbols[i];\n", + " if (symbol.find_first_of(' ', 59) != std::string::npos) {\n", + " std::string name = symbol.substr(59, symbol.find_first_of(' ', 59) - 59);\n", + " int status;\n", + " char * demangled = abi::__cxa_demangle(name.c_str(), NULL, NULL, &status);\n", + " if (!status) {\n", + " buffer << \" \"\n", + " << symbol.substr(0, 59)\n", + " << demangled\n", + " << symbol.substr(59 + name.size())\n", + " << std::endl;\n", + " free(demangled);\n", + " } else\n", + " buffer << \" \" << symbol << std::endl;\n", + " } else\n", + " buffer << \" \" << symbol << std::endl;\n", + " }\n", + " free(symbols);\n", + " }\n", + " std::cerr << buffer.str();\n", + " std::exit(EXIT_FAILURE);\n", + " }\n", + "\n", + "\n", + "extern \"C\" {\n", + "\n", + " PyObject * create_signature;\n", + "\n", + " struct sigaction slot;\n", + "\n", + " PyObject * set_create_signature(PyObject * self, PyObject * args) {\n", + " if (!PyArg_ParseTuple(args, \"O\", &create_signature)) {\n", + " PyErr_SetString(PyExc_ValueError, \"Invalid Argument to set_create_signature!\");\n", + " return NULL;\n", + " }\n", + " Py_INCREF(create_signature);\n", + " memset(&slot, 0, sizeof(slot));\n", + " slot.sa_handler = &sighandler;\n", + " sigaction(SIGSEGV, &slot, NULL);\n", + " sigaction(SIGBUS, &slot, NULL);\n", + " Py_INCREF(Py_None);\n", + " return Py_None;\n", + " }\n", + "\n", + " PyObject * run(PyObject * self, PyObject * args) {\n", + " {\n", + " PyObject * pn; npy_int64 cn;\n", + " if (\n", + " PyTuple_CheckExact(args) and PyTuple_GET_SIZE(args) == 1\n", + " and (pn = PyTuple_GET_ITEM(args, 0)) and PyLong_CheckExact(pn)\n", + " ) {\n", + " cn = PyLong_AS_LONG(pn);\n", + " try {\n", + " return Py_BuildValue(\"l\", fib_J(\n", + " cn\n", + " ));\n", + " } catch (...) {\n", + " return NULL;\n", + " }\n", + " } else\n", + " PyErr_Clear();\n", + " }\n", + " PyObject * signatures = Py_BuildValue(\"(sO)\", \"gANdcQBdcQFjaG9wZS5fYXN0ClZhcmlhYmxlCnECKYFxA31xBChYBAAAAGRpbXNxBUsAWAUAAABk\\ndHlwZXEGY2J1aWx0aW5zCmludApxB1gEAAAAbmFtZXEIWAEAAABucQl1YmFhLg==\\n\", args);\n", + " if (!signatures) {\n", + " PyErr_SetString(PyExc_ValueError, \"Error building signature string for fib\");\n", + " return NULL;\n", + " }\n", + " return PyObject_Call(create_signature, signatures, NULL);\n", + " }\n", + "\n", + " PyMethodDef fibMethods[] = {\n", + " { \"set_create_signature\", set_create_signature, METH_VARARGS, \"signal handler\" },\n", + " { \"run\", run, METH_VARARGS, \"module function\" },\n", + " { NULL, NULL, 0, NULL }\n", + " };\n", + "\n", + "\n", + " static struct PyModuleDef fibmodule = {\n", + " PyModuleDef_HEAD_INIT,\n", + " \"fib\",\n", + " NULL,\n", + " -1,\n", + " fibMethods\n", + " };\n", + "\n", + "\n", + "\n", + " PyMODINIT_FUNC PyInit_fib(void) {\n", + " import_array();\n", + " PyImport_ImportModule(\"numpy\");\n", + " return PyModule_Create(&fibmodule);\n", + " }\n", + "}" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Quicksort CPP" + ] + }, + { + "cell_type": "code", + "execution_count": 5, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Overwriting src/qsort_kernel.cpp\n" + ] + } + ], + "source": [ + "%%file src/qsort_kernel.cpp\n", + "#define PY_ARRAY_UNIQUE_SYMBOL qsort_kernel_ARRAY_API\n", + "#define NPY_NO_DEPRECATED_API NPY_1_7_API_VERSION\n", + "#include \n", + "#include \n", + "#include \n", + "#include \n", + "#include \n", + "#include \n", + "#include \n", + "#include \n", + "#include \n", + "#include \n", + "#include \n", + "struct PyObj {\n", + " typedef PyObject * ptr_t;\n", + " typedef PyArrayObject * arrptr_t;\n", + " PyObj(): dec(false), ptr(NULL) {}\n", + " PyObj(ptr_t p): dec(false), ptr(p) {}\n", + " ~PyObj() { if(dec) Py_DECREF(ptr); }\n", + " PyObj & operator=(ptr_t p) { if(dec) Py_DECREF(ptr); ptr = p; dec = false; return *this; }\n", + " PyObj & incref(ptr_t p) { if(dec) Py_DECREF(ptr); ptr = p; dec = (p != NULL); return *this; }\n", + " operator bool() const { return ptr; }\n", + " operator ptr_t() const { return ptr; }\n", + " operator arrptr_t() const { return (arrptr_t)ptr; }\n", + " bool dec;\n", + " ptr_t ptr;\n", + "};\n", + "inline std::tuple qsort_kernel_d1JJ(PyObject * pa,\n", + " npy_intp const * __restrict__ sa,\n", + " npy_double * __restrict__ ca,\n", + " npy_int64 clo,\n", + " npy_int64 chi);\n", + "\n", + "inline std::tuple qsort_kernel_d1JJ(PyObject * pa,\n", + " npy_intp const * __restrict__ sa,\n", + " npy_double * __restrict__ ca,\n", + " npy_int64 clo,\n", + " npy_int64 chi){\n", + " npy_int64 ci = clo;\n", + " npy_int64 cj = chi;\n", + " npy_double cpivot;\n", + "\n", + " while (ci < chi) {\n", + " cpivot = ca[(int)((clo + chi) / 2)];\n", + "\n", + " while (ci <= cj) {\n", + " while (ca[ci] < cpivot) {\n", + " ci += 1;\n", + " }\n", + "\n", + " while (ca[cj] > cpivot) {\n", + " cj -= 1;\n", + " }\n", + "\n", + " if (ci <= cj) {\n", + " auto ctmp = ca[ci];\n", + " ca[ci] = ca[cj];\n", + " ca[cj] = ctmp;\n", + " ci += 1;\n", + " cj -= 1;\n", + " }\n", + " }\n", + "\n", + " if (clo < cj) {\n", + " qsort_kernel_d1JJ(pa, sa, ca, clo, cj);\n", + " }\n", + "\n", + " clo = ci;\n", + " cj = chi;\n", + " }\n", + "\n", + " return std::make_tuple((PyObject *)pa, sa, ca);\n", + "}\n", + "\n", + "void sighandler(int sig);\n", + "#include \n", + "extern \"C\" {\n", + " PyObject * create_signature;\n", + " struct sigaction slot;\n", + " PyObject * set_create_signature(PyObject * self, PyObject * args) {\n", + " if (!PyArg_ParseTuple(args, \"O\", &create_signature)) {\n", + " PyErr_SetString(PyExc_ValueError, \"Invalid Argument to set_create_signature!\");\n", + " return NULL;\n", + " }\n", + " Py_INCREF(create_signature);\n", + " memset(&slot, 0, sizeof(slot));\n", + " slot.sa_handler = &sighandler;\n", + " sigaction(SIGSEGV, &slot, NULL);\n", + " sigaction(SIGBUS, &slot, NULL);\n", + " Py_INCREF(Py_None);\n", + " return Py_None;\n", + " }\n", + " PyObject * run(PyObject * self, PyObject * args) {\n", + " {\n", + " PyObj pa;\n", + " PyObject * plo; npy_int64 clo;\n", + " PyObject * phi; npy_int64 chi;\n", + " if (\n", + " PyTuple_CheckExact(args) and PyTuple_GET_SIZE(args) == 3\n", + " and (pa = PyTuple_GET_ITEM(args, 0)) and PyArray_CheckExact(pa)\n", + " and PyArray_TYPE((PyArrayObject *)pa) == NPY_FLOAT64 and PyArray_NDIM((PyArrayObject *)pa) == 1\n", + " and (plo = PyTuple_GET_ITEM(args, 1)) and PyLong_CheckExact(plo)\n", + " and (phi = PyTuple_GET_ITEM(args, 2)) and PyLong_CheckExact(phi)\n", + " ) {\n", + " if (!(pa.incref((PyObject *)PyArray_GETCONTIGUOUS((PyArrayObject *)pa)))) {\n", + " PyErr_SetString(PyExc_ValueError, \"Invalid Argument type on a!\");\n", + " return NULL;\n", + " }\n", + " clo = PyLong_AS_LONG(plo);\n", + " chi = PyLong_AS_LONG(phi);\n", + " try {\n", + " PyObject * res = std::get<0>(qsort_kernel_d1JJ(\n", + " pa, PyArray_SHAPE((PyArrayObject *)pa), (npy_double *)PyArray_DATA((PyArrayObject *)pa)\n", + " , clo\n", + " , chi\n", + " ));\n", + "\n", + " Py_INCREF(res);\n", + " return res;\n", + " } catch (...) {\n", + " return NULL;\n", + " }\n", + " } else\n", + " PyErr_Clear();\n", + " }\n", + " PyObject * signatures = Py_BuildValue(\"(sO)\", \"(lp0\\n(lp1\\nccopy_reg\\n_reconstructor\\np2\\n(chope._ast\\nVariable\\np3\\nc__builtin__\\nobject\\np4\\nNtp5\\nRp6\\n(dp7\\nS'name'\\np8\\nS'a'\\np9\\nsS'dtype'\\np10\\nS'float64'\\np11\\nsS'dims'\\np12\\nI1\\nsbag2\\n(g3\\ng4\\nNtp13\\nRp14\\n(dp15\\ng8\\nS'lo'\\np16\\nsg10\\nc__builtin__\\nint\\np17\\nsg12\\nI0\\nsbag2\\n(g3\\ng4\\nNtp18\\nRp19\\n(dp20\\ng8\\nS'hi'\\np21\\nsg10\\ng17\\nsg12\\nI0\\nsbaa.\", args);\n", + " if (!signatures) {\n", + " PyErr_SetString(PyExc_ValueError, \"Error building signature string for qsort_kernel\");\n", + " return NULL;\n", + " }\n", + " return PyObject_Call(create_signature, signatures, NULL);\n", + " }\n", + " PyMethodDef qsort_kernelMethods[] = {\n", + " { \"set_create_signature\", set_create_signature, METH_VARARGS, \"signal handler\" },\n", + " { \"run\", run, METH_VARARGS, \"module function\" },\n", + " { NULL, NULL, 0, NULL }\n", + " };\n", + "}\n", + "\n", + "static struct PyModuleDef qsort_kernel_module = {\n", + " PyModuleDef_HEAD_INIT,\n", + " \"qsort_kernel\",\n", + " NULL,\n", + " -1,\n", + " qsort_kernelMethods\n", + "};\n", + "\n", + "PyMODINIT_FUNC PyInit_qsort_kernel(void) {\n", + " import_array();\n", + " PyImport_ImportModule(\"numpy\");\n", + " return PyModule_Create(&qsort_kernel_module);\n", + "}\n", + "\n", + "#include \n", + "#include \n", + "#include \n", + "#include \n", + "#include \n", + "void sighandler(int sig) {\n", + " std::ostringstream buffer;\n", + " buffer << \"Abort by \" << (sig == SIGSEGV ? \"segfault\" : \"bus error\") << std::endl;\n", + " void * stack[64];\n", + " std::size_t depth = backtrace(stack, 64);\n", + " if (!depth)\n", + " buffer << \" \" << std::endl;\n", + " else {\n", + " char ** symbols = backtrace_symbols(stack, depth);\n", + " for (std::size_t i = 1; i < depth; ++i) {\n", + " std::string symbol = symbols[i];\n", + " if (symbol.find_first_of(' ', 59) != std::string::npos) {\n", + " std::string name = symbol.substr(59, symbol.find_first_of(' ', 59) - 59);\n", + " int status;\n", + " char * demangled = abi::__cxa_demangle(name.c_str(), NULL, NULL, &status);\n", + " if (!status) {\n", + " buffer << \" \"\n", + " << symbol.substr(0, 59)\n", + " << demangled\n", + " << symbol.substr(59 + name.size())\n", + " << std::endl;\n", + " free(demangled);\n", + " } else\n", + " buffer << \" \" << symbol << std::endl;\n", + " } else\n", + " buffer << \" \" << symbol << std::endl;\n", + " }\n", + " free(symbols);\n", + " }\n", + " std::cerr << buffer.str();\n", + " std::exit(EXIT_FAILURE);\n", + " }\n" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Pi sum CPP" + ] + }, + { + "cell_type": "code", + "execution_count": 6, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Overwriting src/pisum.cpp\n" + ] + } + ], + "source": [ + "%%file src/pisum.cpp\n", + "#define PY_ARRAY_UNIQUE_SYMBOL pisum_ARRAY_API\n", + "#define NPY_NO_DEPRECATED_API NPY_1_7_API_VERSION\n", + "#include \n", + "#include \n", + "#include \n", + "#include \n", + "#include \n", + "#include \n", + "#include \n", + "#include \n", + "#include \n", + "#include \n", + "#include \n", + "struct PyObj {\n", + " typedef PyObject * ptr_t;\n", + " typedef PyArrayObject * arrptr_t;\n", + " PyObj(): dec(false), ptr(NULL) {}\n", + " PyObj(ptr_t p): dec(false), ptr(p) {}\n", + " ~PyObj() { if(dec) Py_DECREF(ptr); }\n", + " PyObj & operator=(ptr_t p) { if(dec) Py_DECREF(ptr); ptr = p; dec = false; return *this; }\n", + " PyObj & incref(ptr_t p) { if(dec) Py_DECREF(ptr); ptr = p; dec = (p != NULL); return *this; }\n", + " operator bool() const { return ptr; }\n", + " operator ptr_t() const { return ptr; }\n", + " operator arrptr_t() const { return (arrptr_t)ptr; }\n", + " bool dec;\n", + " ptr_t ptr;\n", + "};\n", + "\n", + "inline npy_double pisum_();\n", + "\n", + "inline npy_double pisum_() {\n", + " double csum = 0;\n", + " int ck = 1;\n", + " for (int cj = 1; cj < 501; ++cj) {\n", + " csum = 0.0;\n", + " for (ck = 1; ck < 10001; ++ck) {\n", + " csum += (1.0 / (double)(ck * ck));\n", + " }\n", + " }\n", + "\n", + " return npy_double(csum);\n", + "}\n", + "\n", + "void sighandler(int sig);\n", + "#include \n", + "extern \"C\" {\n", + " PyObject * create_signature;\n", + " struct sigaction slot;\n", + " PyObject * set_create_signature(PyObject * self, PyObject * args) {\n", + " if (!PyArg_ParseTuple(args, \"O\", &create_signature)) {\n", + " PyErr_SetString(PyExc_ValueError, \"Invalid Argument to set_create_signature!\");\n", + " return NULL;\n", + " }\n", + " Py_INCREF(create_signature);\n", + " memset(&slot, 0, sizeof(slot));\n", + " slot.sa_handler = &sighandler;\n", + " sigaction(SIGSEGV, &slot, NULL);\n", + " sigaction(SIGBUS, &slot, NULL);\n", + " Py_INCREF(Py_None);\n", + " return Py_None;\n", + " }\n", + " PyObject * run(PyObject * self, PyObject * args) {\n", + " { try {\n", + " return Py_BuildValue(\"d\", pisum_());\n", + " } catch (...) {\n", + " return NULL;\n", + " }\n", + " }\n", + " PyObject * signatures = Py_BuildValue(\"(sO)\", \"(lp0\\n(lp1\\na.\", args);\n", + " if (!signatures) {\n", + " PyErr_SetString(PyExc_ValueError, \"Error building signature string for pisum\");\n", + " return NULL;\n", + " }\n", + " return PyObject_Call(create_signature, signatures, NULL);\n", + " }\n", + "\n", + "PyMethodDef pisumMethods[] = {\n", + "{ \"set_create_signature\", set_create_signature, METH_VARARGS, \"signal handler\" },\n", + "{ \"run\", run, METH_VARARGS, \"module function\" },\n", + "{ NULL, NULL, 0, NULL }\n", + "};\n", + "\n", + "static struct PyModuleDef pisum_module = {\n", + " PyModuleDef_HEAD_INIT,\n", + " \"pisum\",\n", + " NULL,\n", + " -1,\n", + " pisumMethods\n", + "};\n", + "\n", + "PyMODINIT_FUNC PyInit_pisum(void) {\n", + " import_array();\n", + " PyImport_ImportModule(\"numpy\");\n", + " return PyModule_Create(&pisum_module);\n", + "}\n", + "}\n", + "\n", + "#include \n", + "#include \n", + "#include \n", + "#include \n", + "#include \n", + "void sighandler(int sig) {\n", + " std::ostringstream buffer;\n", + " buffer << \"Abort by \" << (sig == SIGSEGV ? \"segfault\" : \"bus error\") << std::endl;\n", + " void * stack[64];\n", + " std::size_t depth = backtrace(stack, 64);\n", + " if (!depth)\n", + " buffer << \" \" << std::endl;\n", + " else {\n", + " char ** symbols = backtrace_symbols(stack, depth);\n", + " for (std::size_t i = 1; i < depth; ++i) {\n", + " std::string symbol = symbols[i];\n", + " if (symbol.find_first_of(' ', 59) != std::string::npos) {\n", + " std::string name = symbol.substr(59, symbol.find_first_of(' ', 59) - 59);\n", + " int status;\n", + " char * demangled = abi::__cxa_demangle(name.c_str(), NULL, NULL, &status);\n", + " if (!status) {\n", + " buffer << \" \"\n", + " << symbol.substr(0, 59)\n", + " << demangled\n", + " << symbol.substr(59 + name.size())\n", + " << std::endl;\n", + " free(demangled);\n", + " } else\n", + " buffer << \" \" << symbol << std::endl;\n", + " } else\n", + " buffer << \" \" << symbol << std::endl;\n", + " }\n", + " free(symbols);\n", + " }\n", + " std::cerr << buffer.str();\n", + " std::exit(EXIT_FAILURE);\n", + " }\n" + ] + }, + { + "cell_type": "code", + "execution_count": 7, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Overwriting src/pisum_opt.cpp\n" + ] + } + ], + "source": [ + "%%file src/pisum_opt.cpp\n", + "#define PY_ARRAY_UNIQUE_SYMBOL pisum_opt_ARRAY_API\n", + "#define NPY_NO_DEPRECATED_API NPY_1_7_API_VERSION\n", + "#include \n", + "#include \n", + "#include \n", + "#include \n", + "#include \n", + "#include \n", + "#include \n", + "#include \n", + "#include \n", + "#include \n", + "#include \n", + "struct PyObj {\n", + " typedef PyObject * ptr_t;\n", + " typedef PyArrayObject * arrptr_t;\n", + " PyObj(): dec(false), ptr(NULL) {}\n", + " PyObj(ptr_t p): dec(false), ptr(p) {}\n", + " ~PyObj() { if(dec) Py_DECREF(ptr); }\n", + " PyObj & operator=(ptr_t p) { if(dec) Py_DECREF(ptr); ptr = p; dec = false; return *this; }\n", + " PyObj & incref(ptr_t p) { if(dec) Py_DECREF(ptr); ptr = p; dec = (p != NULL); return *this; }\n", + " operator bool() const { return ptr; }\n", + " operator ptr_t() const { return ptr; }\n", + " operator arrptr_t() const { return (arrptr_t)ptr; }\n", + " bool dec;\n", + " ptr_t ptr;\n", + "};\n", + "\n", + "inline npy_double pisum_opt_();\n", + "\n", + "inline npy_double pisum_opt_() {\n", + " npy_double csum = npy_double();\n", + " npy_intp ck = 1;\n", + " npy_double cf = 0.0;\n", + "\n", + " for (npy_intp cj = 1; cj < 501; ++cj) {\n", + " csum = 0.0;\n", + " cf = 0.0;\n", + " for (ck = 1; ck < 10001; ++ck) {\n", + " cf += 1.0;\n", + " auto c__sp0 = (cf * cf);\n", + " csum += (1.0 / c__sp0);\n", + " }\n", + " }\n", + " return csum;\n", + "}\n", + "\n", + "void sighandler(int sig);\n", + "#include \n", + "extern \"C\" {\n", + " PyObject * create_signature;\n", + " struct sigaction slot;\n", + " PyObject * set_create_signature(PyObject * self, PyObject * args) {\n", + " if (!PyArg_ParseTuple(args, \"O\", &create_signature)) {\n", + " PyErr_SetString(PyExc_ValueError, \"Invalid Argument to set_create_signature!\");\n", + " return NULL;\n", + " }\n", + " Py_INCREF(create_signature);\n", + " memset(&slot, 0, sizeof(slot));\n", + " slot.sa_handler = &sighandler;\n", + " sigaction(SIGSEGV, &slot, NULL);\n", + " sigaction(SIGBUS, &slot, NULL);\n", + " Py_INCREF(Py_None);\n", + " return Py_None;\n", + " }\n", + " PyObject * run(PyObject * self, PyObject * args) {\n", + " { try {\n", + " return Py_BuildValue(\"d\", pisum_opt_());\n", + " } catch (...) {\n", + " return NULL;\n", + " }\n", + " }\n", + " PyObject * signatures = Py_BuildValue(\"(sO)\", \"(lp0\\n(lp1\\na.\", args);\n", + " if (!signatures) {\n", + " PyErr_SetString(PyExc_ValueError, \"Error building signature string for pisum_opt\");\n", + " return NULL;\n", + " }\n", + " return PyObject_Call(create_signature, signatures, NULL);\n", + " }\n", + "\n", + " PyMethodDef pisum_optMethods[] = {\n", + " { \"set_create_signature\", set_create_signature, METH_VARARGS, \"signal handler\" },\n", + " { \"run\", run, METH_VARARGS, \"module function\" },\n", + " { NULL, NULL, 0, NULL }\n", + " };\n", + "\n", + " static struct PyModuleDef pisum_opt_module = {\n", + " PyModuleDef_HEAD_INIT,\n", + " \"pisum_opt\",\n", + " NULL,\n", + " -1,\n", + " pisum_optMethods\n", + " };\n", + "\n", + " PyMODINIT_FUNC PyInit_pisum_opt(void) {\n", + " import_array();\n", + " PyImport_ImportModule(\"numpy\");\n", + " return PyModule_Create(&pisum_opt_module);\n", + " }\n", + "}\n", + "\n", + "#include \n", + "#include \n", + "#include \n", + "#include \n", + "#include \n", + "void sighandler(int sig) {\n", + " std::ostringstream buffer;\n", + " buffer << \"Abort by \" << (sig == SIGSEGV ? \"segfault\" : \"bus error\") << std::endl;\n", + " void * stack[64];\n", + " std::size_t depth = backtrace(stack, 64);\n", + " if (!depth)\n", + " buffer << \" \" << std::endl;\n", + " else {\n", + " char ** symbols = backtrace_symbols(stack, depth);\n", + " for (std::size_t i = 1; i < depth; ++i) {\n", + " std::string symbol = symbols[i];\n", + " if (symbol.find_first_of(' ', 59) != std::string::npos) {\n", + " std::string name = symbol.substr(59, symbol.find_first_of(' ', 59) - 59);\n", + " int status;\n", + " char * demangled = abi::__cxa_demangle(name.c_str(), NULL, NULL, &status);\n", + " if (!status) {\n", + " buffer << \" \"\n", + " << symbol.substr(0, 59)\n", + " << demangled\n", + " << symbol.substr(59 + name.size())\n", + " << std::endl;\n", + " free(demangled);\n", + " } else\n", + " buffer << \" \" << symbol << std::endl;\n", + " } else\n", + " buffer << \" \" << symbol << std::endl;\n", + " }\n", + " free(symbols);\n", + " }\n", + " std::cerr << buffer.str();\n", + " std::exit(EXIT_FAILURE);\n", + " }\n" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# 10th order poly log approx CPP" + ] + }, + { + "cell_type": "code", + "execution_count": 8, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Overwriting src/ln.cpp\n" + ] + } + ], + "source": [ + "%%file src/ln.cpp\n", + "#define PY_ARRAY_UNIQUE_SYMBOL ln_hope_ARRAY_API\n", + "#define NPY_NO_DEPRECATED_API NPY_1_7_API_VERSION\n", + "#include \n", + "#include \n", + "#include \n", + "#include \n", + "#include \n", + "#include \n", + "#include \n", + "#include \n", + "#include \n", + "#include \n", + "#include \n", + "struct PyObj {\n", + " typedef PyObject * ptr_t;\n", + " typedef PyArrayObject * arrptr_t;\n", + " PyObj(): dec(false), ptr(NULL) {}\n", + " PyObj(ptr_t p): dec(false), ptr(p) {}\n", + " ~PyObj() { if(dec) Py_DECREF(ptr); }\n", + " PyObj & operator=(ptr_t p) { if(dec) Py_DECREF(ptr); ptr = p; dec = false; return *this; }\n", + " PyObj & incref(ptr_t p) { if(dec) Py_DECREF(ptr); ptr = p; dec = (p != NULL); return *this; }\n", + " operator bool() const { return ptr; }\n", + " operator ptr_t() const { return ptr; }\n", + " operator arrptr_t() const { return (arrptr_t)ptr; }\n", + " bool dec;\n", + " ptr_t ptr;\n", + "};\n", + "\n", + "inline void ln_hope_d1d1(PyObject * pX, npy_intp const * __restrict__ sX, npy_double * __restrict__ cX,\n", + " PyObject * pY, npy_intp const * __restrict__ sY, npy_double * __restrict__ cY);\n", + "\n", + "inline void ln_hope_d1d1(PyObject * pX, npy_intp const * __restrict__ sX, npy_double * __restrict__ cX,\n", + " PyObject * pY, npy_intp const * __restrict__ sY, npy_double * __restrict__ cY){\n", + "\n", + " for (npy_intp i0 = 0; i0 < sY[0] - 0; ++i0) {\n", + " cY[(int)(i0)] = (cX[i0] - 1) - (std::pow((cX[i0] - 1), 2) / 2) + (std::pow((cX[i0] - 1), 3) / 3) - (std::pow((cX[i0] - 1), 4) / 4) + (std::pow((cX[i0] - 1), 5) / 5) - (std::pow((cX[i0] - 1), 6) / 6) + (std::pow((cX[i0] - 1), 7) / 7) - (std::pow((cX[i0] - 1), 8) / 8) + (std::pow((cX[i0] - 1), 9) / 9);\n", + " }\n", + "}\n", + "\n", + "void sighandler(int sig);\n", + "#include \n", + "extern \"C\" {\n", + " PyObject * create_signature;\n", + " struct sigaction slot;\n", + " PyObject * set_create_signature(PyObject * self, PyObject * args) {\n", + " if (!PyArg_ParseTuple(args, \"O\", &create_signature)) {\n", + " PyErr_SetString(PyExc_ValueError, \"Invalid Argument to set_create_signature!\");\n", + " return NULL;\n", + " }\n", + " Py_INCREF(create_signature);\n", + " memset(&slot, 0, sizeof(slot));\n", + " slot.sa_handler = &sighandler;\n", + " sigaction(SIGSEGV, &slot, NULL);\n", + " sigaction(SIGBUS, &slot, NULL);\n", + " Py_INCREF(Py_None);\n", + " return Py_None;\n", + " }\n", + " PyObject * run(PyObject * self, PyObject * args) {\n", + " {\n", + " PyObj pX;\n", + " PyObj pY;\n", + " if (\n", + " PyTuple_CheckExact(args) and PyTuple_GET_SIZE(args) == 2\n", + " and (pX = PyTuple_GET_ITEM(args, 0)) and PyArray_CheckExact(pX)\n", + " and PyArray_TYPE((PyArrayObject *)pX) == NPY_FLOAT64 and PyArray_NDIM((PyArrayObject *)pX) == 1\n", + " and (pY = PyTuple_GET_ITEM(args, 1)) and PyArray_CheckExact(pY)\n", + " and PyArray_TYPE((PyArrayObject *)pY) == NPY_FLOAT64 and PyArray_NDIM((PyArrayObject *)pY) == 1\n", + " ) {\n", + " if (!(pX.incref((PyObject *)PyArray_GETCONTIGUOUS((PyArrayObject *)pX)))) {\n", + " PyErr_SetString(PyExc_ValueError, \"Invalid Argument type on X!\");\n", + " return NULL;\n", + " }\n", + " if (!(pY.incref((PyObject *)PyArray_GETCONTIGUOUS((PyArrayObject *)pY)))) {\n", + " PyErr_SetString(PyExc_ValueError, \"Invalid Argument type on Y!\");\n", + " return NULL;\n", + " }\n", + " try {\n", + " ln_hope_d1d1(\n", + " pX, PyArray_SHAPE((PyArrayObject *)pX), (npy_double *)PyArray_DATA((PyArrayObject *)pX)\n", + " , pY, PyArray_SHAPE((PyArrayObject *)pY), (npy_double *)PyArray_DATA((PyArrayObject *)pY)\n", + " );\n", + " Py_INCREF(Py_None);\n", + " return Py_None;\n", + " } catch (...) {\n", + " return NULL;\n", + " }\n", + " } else\n", + " PyErr_Clear();\n", + " }\n", + " PyObject * signatures = Py_BuildValue(\"(sO)\", \"(lp0\\n(lp1\\nccopy_reg\\n_reconstructor\\np2\\n(chope._ast\\nVariable\\np3\\nc__builtin__\\nobject\\np4\\nNtp5\\nRp6\\n(dp7\\nS'name'\\np8\\nS'X'\\np9\\nsS'dtype'\\np10\\nS'float64'\\np11\\nsS'dims'\\np12\\nI1\\nsbag2\\n(g3\\ng4\\nNtp13\\nRp14\\n(dp15\\ng8\\nS'Y'\\np16\\nsg10\\ng11\\nsg12\\nI1\\nsbaa.\", args);\n", + " if (!signatures) {\n", + " PyErr_SetString(PyExc_ValueError, \"Error building signature string for ln_hope\");\n", + " return NULL;\n", + " }\n", + " return PyObject_Call(create_signature, signatures, NULL);\n", + " }\n", + "\n", + " PyMethodDef lnMethods[] = {\n", + " { \"set_create_signature\", set_create_signature, METH_VARARGS, \"signal handler\" },\n", + " { \"run\", run, METH_VARARGS, \"module function\" },\n", + " { NULL, NULL, 0, NULL }\n", + " };\n", + "\n", + "\n", + " static struct PyModuleDef lnmodule = {\n", + " PyModuleDef_HEAD_INIT,\n", + " \"ln\",\n", + " NULL,\n", + " -1,\n", + " lnMethods\n", + " };\n", + "\n", + "\n", + "\n", + " PyMODINIT_FUNC PyInit_ln(void) {\n", + " import_array();\n", + " PyImport_ImportModule(\"numpy\");\n", + " return PyModule_Create(&lnmodule);\n", + " }\n", + "}\n", + "#include \n", + "#include \n", + "#include \n", + "#include \n", + "#include \n", + "void sighandler(int sig) {\n", + " std::ostringstream buffer;\n", + " buffer << \"Abort by \" << (sig == SIGSEGV ? \"segfault\" : \"bus error\") << std::endl;\n", + " void * stack[64];\n", + " std::size_t depth = backtrace(stack, 64);\n", + " if (!depth)\n", + " buffer << \" \" << std::endl;\n", + " else {\n", + " char ** symbols = backtrace_symbols(stack, depth);\n", + " for (std::size_t i = 1; i < depth; ++i) {\n", + " std::string symbol = symbols[i];\n", + " if (symbol.find_first_of(' ', 59) != std::string::npos) {\n", + " std::string name = symbol.substr(59, symbol.find_first_of(' ', 59) - 59);\n", + " int status;\n", + " char * demangled = abi::__cxa_demangle(name.c_str(), NULL, NULL, &status);\n", + " if (!status) {\n", + " buffer << \" \"\n", + " << symbol.substr(0, 59)\n", + " << demangled\n", + " << symbol.substr(59 + name.size())\n", + " << std::endl;\n", + " free(demangled);\n", + " } else\n", + " buffer << \" \" << symbol << std::endl;\n", + " } else\n", + " buffer << \" \" << symbol << std::endl;\n", + " }\n", + " free(symbols);\n", + " }\n", + " std::cerr << buffer.str();\n", + " std::exit(EXIT_FAILURE);\n", + " }\n" + ] + }, + { + "cell_type": "code", + "execution_count": 9, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Overwriting src/ln_exp.cpp\n" + ] + } + ], + "source": [ + "%%file src/ln_exp.cpp\n", + "#define PY_ARRAY_UNIQUE_SYMBOL ln_hope_exp_ARRAY_API\n", + "#define NPY_NO_DEPRECATED_API NPY_1_7_API_VERSION\n", + "#include \n", + "#include \n", + "#include \n", + "#include \n", + "#include \n", + "#include \n", + "#include \n", + "#include \n", + "#include \n", + "#include \n", + "#include \n", + "struct PyObj {\n", + " typedef PyObject * ptr_t;\n", + " typedef PyArrayObject * arrptr_t;\n", + " PyObj(): dec(false), ptr(NULL) {}\n", + " PyObj(ptr_t p): dec(false), ptr(p) {}\n", + " ~PyObj() { if(dec) Py_DECREF(ptr); }\n", + " PyObj & operator=(ptr_t p) { if(dec) Py_DECREF(ptr); ptr = p; dec = false; return *this; }\n", + " PyObj & incref(ptr_t p) { if(dec) Py_DECREF(ptr); ptr = p; dec = (p != NULL); return *this; }\n", + " operator bool() const { return ptr; }\n", + " operator ptr_t() const { return ptr; }\n", + " operator arrptr_t() const { return (arrptr_t)ptr; }\n", + " bool dec;\n", + " ptr_t ptr;\n", + "};\n", + "\n", + "inline void ln_hope_exp_d1d1(PyObject * pX, npy_intp const * __restrict__ sX, npy_double * __restrict__ cX,\n", + " PyObject * pY, npy_intp const * __restrict__ sY, npy_double * __restrict__ cY);\n", + "\n", + "inline void ln_hope_exp_d1d1(PyObject * pX, npy_intp const * __restrict__ sX, npy_double * __restrict__ cX,\n", + " PyObject * pY, npy_intp const * __restrict__ sY, npy_double * __restrict__ cY){\n", + "\n", + " for (npy_intp i0 = 0; i0 < sX[0] - 0; ++i0) {\n", + " auto cx = (cX[i0] - 1);\n", + " auto cx2 = (cx * cx);\n", + " auto cx4 = (cx2 * cx2);\n", + " auto cx6 = (cx4 * cx2);\n", + " auto cx8 = (cx4 * cx4);\n", + " cY[i0] = cx - (cx2 / 2) + (cx * cx2 / 3) - (cx4 / 4) + (cx * cx4 / 5) - (cx6 / 6) + (cx6 * cx / 7) - (cx8 / 8) + (cx8 * cx / 9);\n", + " }\n", + "}\n", + "\n", + "void sighandler(int sig);\n", + "#include \n", + "extern \"C\" {\n", + " PyObject * create_signature;\n", + " struct sigaction slot;\n", + " PyObject * set_create_signature(PyObject * self, PyObject * args) {\n", + " if (!PyArg_ParseTuple(args, \"O\", &create_signature)) {\n", + " PyErr_SetString(PyExc_ValueError, \"Invalid Argument to set_create_signature!\");\n", + " return NULL;\n", + " }\n", + " Py_INCREF(create_signature);\n", + " memset(&slot, 0, sizeof(slot));\n", + " slot.sa_handler = &sighandler;\n", + " sigaction(SIGSEGV, &slot, NULL);\n", + " sigaction(SIGBUS, &slot, NULL);\n", + " Py_INCREF(Py_None);\n", + " return Py_None;\n", + " }\n", + " PyObject * run(PyObject * self, PyObject * args) {\n", + " {\n", + " PyObj pX;\n", + " PyObj pY;\n", + " if (\n", + " PyTuple_CheckExact(args) and PyTuple_GET_SIZE(args) == 2\n", + " and (pX = PyTuple_GET_ITEM(args, 0)) and PyArray_CheckExact(pX)\n", + " and PyArray_TYPE((PyArrayObject *)pX) == NPY_FLOAT64 and PyArray_NDIM((PyArrayObject *)pX) == 1\n", + " and (pY = PyTuple_GET_ITEM(args, 1)) and PyArray_CheckExact(pY)\n", + " and PyArray_TYPE((PyArrayObject *)pY) == NPY_FLOAT64 and PyArray_NDIM((PyArrayObject *)pY) == 1\n", + " ) {\n", + " if (!(pX.incref((PyObject *)PyArray_GETCONTIGUOUS((PyArrayObject *)pX)))) {\n", + " PyErr_SetString(PyExc_ValueError, \"Invalid Argument type on X!\");\n", + " return NULL;\n", + " }\n", + " if (!(pY.incref((PyObject *)PyArray_GETCONTIGUOUS((PyArrayObject *)pY)))) {\n", + " PyErr_SetString(PyExc_ValueError, \"Invalid Argument type on Y!\");\n", + " return NULL;\n", + " }\n", + " try {\n", + " ln_hope_exp_d1d1(\n", + " pX, PyArray_SHAPE((PyArrayObject *)pX), (npy_double *)PyArray_DATA((PyArrayObject *)pX)\n", + " , pY, PyArray_SHAPE((PyArrayObject *)pY), (npy_double *)PyArray_DATA((PyArrayObject *)pY)\n", + " );\n", + " Py_INCREF(Py_None);\n", + " return Py_None;\n", + " } catch (...) {\n", + " return NULL;\n", + " }\n", + " } else\n", + " PyErr_Clear();\n", + " }\n", + " PyObject * signatures = Py_BuildValue(\"(sO)\", \"(lp0\\n(lp1\\nccopy_reg\\n_reconstructor\\np2\\n(chope._ast\\nVariable\\np3\\nc__builtin__\\nobject\\np4\\nNtp5\\nRp6\\n(dp7\\nS'name'\\np8\\nS'X'\\np9\\nsS'dtype'\\np10\\nS'float64'\\np11\\nsS'dims'\\np12\\nI1\\nsbag2\\n(g3\\ng4\\nNtp13\\nRp14\\n(dp15\\ng8\\nS'Y'\\np16\\nsg10\\ng11\\nsg12\\nI1\\nsbaa.\", args);\n", + " if (!signatures) {\n", + " PyErr_SetString(PyExc_ValueError, \"Error building signature string for ln_hope_exp\");\n", + " return NULL;\n", + " }\n", + " return PyObject_Call(create_signature, signatures, NULL);\n", + " }\n", + " PyMethodDef ln_expMethods[] = {\n", + " { \"set_create_signature\", set_create_signature, METH_VARARGS, \"signal handler\" },\n", + " { \"run\", run, METH_VARARGS, \"module function\" },\n", + " { NULL, NULL, 0, NULL }\n", + " };\n", + "\n", + "\n", + " static struct PyModuleDef ln_expmodule = {\n", + " PyModuleDef_HEAD_INIT,\n", + " \"ln_exp\",\n", + " NULL,\n", + " -1,\n", + " ln_expMethods\n", + " };\n", + "\n", + "\n", + "\n", + " PyMODINIT_FUNC PyInit_ln_exp(void) {\n", + " import_array();\n", + " PyImport_ImportModule(\"numpy\");\n", + " return PyModule_Create(&ln_expmodule);\n", + " }\n", + "}\n", + "#include \n", + "#include \n", + "#include \n", + "#include \n", + "#include \n", + "void sighandler(int sig) {\n", + " std::ostringstream buffer;\n", + " buffer << \"Abort by \" << (sig == SIGSEGV ? \"segfault\" : \"bus error\") << std::endl;\n", + " void * stack[64];\n", + " std::size_t depth = backtrace(stack, 64);\n", + " if (!depth)\n", + " buffer << \" \" << std::endl;\n", + " else {\n", + " char ** symbols = backtrace_symbols(stack, depth);\n", + " for (std::size_t i = 1; i < depth; ++i) {\n", + " std::string symbol = symbols[i];\n", + " if (symbol.find_first_of(' ', 59) != std::string::npos) {\n", + " std::string name = symbol.substr(59, symbol.find_first_of(' ', 59) - 59);\n", + " int status;\n", + " char * demangled = abi::__cxa_demangle(name.c_str(), NULL, NULL, &status);\n", + " if (!status) {\n", + " buffer << \" \"\n", + " << symbol.substr(0, 59)\n", + " << demangled\n", + " << symbol.substr(59 + name.size())\n", + " << std::endl;\n", + " free(demangled);\n", + " } else\n", + " buffer << \" \" << symbol << std::endl;\n", + " } else\n", + " buffer << \" \" << symbol << std::endl;\n", + " }\n", + " free(symbols);\n", + " }\n", + " std::cerr << buffer.str();\n", + " std::exit(EXIT_FAILURE);\n", + " }\n" + ] + }, + { + "cell_type": "code", + "execution_count": 10, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Overwriting src/ln_opt.cpp\n" + ] + } + ], + "source": [ + "%%file src/ln_opt.cpp\n", + "#define PY_ARRAY_UNIQUE_SYMBOL ln_hope_opt_ARRAY_API\n", + "#define NPY_NO_DEPRECATED_API NPY_1_7_API_VERSION\n", + "#include \n", + "#include \n", + "#include \n", + "#include \n", + "#include \n", + "#include \n", + "#include \n", + "#include \n", + "#include \n", + "#include \n", + "#include \n", + "struct PyObj {\n", + " typedef PyObject * ptr_t;\n", + " typedef PyArrayObject * arrptr_t;\n", + " PyObj(): dec(false), ptr(NULL) {}\n", + " PyObj(ptr_t p): dec(false), ptr(p) {}\n", + " ~PyObj() { if(dec) Py_DECREF(ptr); }\n", + " PyObj & operator=(ptr_t p) { if(dec) Py_DECREF(ptr); ptr = p; dec = false; return *this; }\n", + " PyObj & incref(ptr_t p) { if(dec) Py_DECREF(ptr); ptr = p; dec = (p != NULL); return *this; }\n", + " operator bool() const { return ptr; }\n", + " operator ptr_t() const { return ptr; }\n", + " operator arrptr_t() const { return (arrptr_t)ptr; }\n", + " bool dec;\n", + " ptr_t ptr;\n", + "};\n", + "\n", + "inline void ln_hope_opt_d1d1(PyObject * pX, npy_intp const * __restrict__ sX, npy_double * __restrict__ cX,\n", + " PyObject * pY, npy_intp const * __restrict__ sY, npy_double * __restrict__ cY);\n", + "\n", + "inline void ln_hope_opt_d1d1(PyObject * pX, npy_intp const * __restrict__ sX, npy_double * __restrict__ cX,\n", + " PyObject * pY, npy_intp const * __restrict__ sY, npy_double * __restrict__ cY){\n", + "\n", + " for (npy_intp i0 = 0; i0 < sY[0] - 0; ++i0) {\n", + " auto c__sp0 = (cX[i0] * cX[i0]);\n", + " auto c__sp1 = (c__sp0 * c__sp0);\n", + " auto c__sp2 = (c__sp1 * c__sp1);\n", + " auto c__sp3 = (c__sp2 * cX[i0]);\n", + " auto c__sp4 = (c__sp0 * cX[i0]);\n", + " auto c__sp5 = (c__sp4 * c__sp4);\n", + " auto c__sp6 = (c__sp5 * cX[i0]);\n", + " auto c__sp7 = (c__sp1 * cX[i0]);\n", + " cY[(int)(i0)] = (-7129.0 / 2520.0) + (28 * c__sp4) + (-(18 * c__sp0)) + (-(9 * c__sp2 / 8)) + (-(14 * c__sp5)) + (-(63 * c__sp1 / 2)) + (126 * c__sp7 / 5) + (9 * cX[i0]) + (c__sp3 / 9) + (36 * c__sp6 / 7);\n", + " }\n", + "}\n", + "\n", + "void sighandler(int sig);\n", + "#include \n", + "extern \"C\" {\n", + " PyObject * create_signature;\n", + " struct sigaction slot;\n", + " PyObject * set_create_signature(PyObject * self, PyObject * args) {\n", + " if (!PyArg_ParseTuple(args, \"O\", &create_signature)) {\n", + " PyErr_SetString(PyExc_ValueError, \"Invalid Argument to set_create_signature!\");\n", + " return NULL;\n", + " }\n", + " Py_INCREF(create_signature);\n", + " memset(&slot, 0, sizeof(slot));\n", + " slot.sa_handler = &sighandler;\n", + " sigaction(SIGSEGV, &slot, NULL);\n", + " sigaction(SIGBUS, &slot, NULL);\n", + " Py_INCREF(Py_None);\n", + " return Py_None;\n", + " }\n", + " PyObject * run(PyObject * self, PyObject * args) {\n", + " {\n", + " PyObj pX;\n", + " PyObj pY;\n", + " if (\n", + " PyTuple_CheckExact(args) and PyTuple_GET_SIZE(args) == 2\n", + " and (pX = PyTuple_GET_ITEM(args, 0)) and PyArray_CheckExact(pX)\n", + " and PyArray_TYPE((PyArrayObject *)pX) == NPY_FLOAT64 and PyArray_NDIM((PyArrayObject *)pX) == 1\n", + " and (pY = PyTuple_GET_ITEM(args, 1)) and PyArray_CheckExact(pY)\n", + " and PyArray_TYPE((PyArrayObject *)pY) == NPY_FLOAT64 and PyArray_NDIM((PyArrayObject *)pY) == 1\n", + " ) {\n", + " if (!(pX.incref((PyObject *)PyArray_GETCONTIGUOUS((PyArrayObject *)pX)))) {\n", + " PyErr_SetString(PyExc_ValueError, \"Invalid Argument type on X!\");\n", + " return NULL;\n", + " }\n", + " if (!(pY.incref((PyObject *)PyArray_GETCONTIGUOUS((PyArrayObject *)pY)))) {\n", + " PyErr_SetString(PyExc_ValueError, \"Invalid Argument type on Y!\");\n", + " return NULL;\n", + " }\n", + " try {\n", + " ln_hope_opt_d1d1(\n", + " pX, PyArray_SHAPE((PyArrayObject *)pX), (npy_double *)PyArray_DATA((PyArrayObject *)pX)\n", + " , pY, PyArray_SHAPE((PyArrayObject *)pY), (npy_double *)PyArray_DATA((PyArrayObject *)pY)\n", + " );\n", + " Py_INCREF(Py_None);\n", + " return Py_None;\n", + " } catch (...) {\n", + " return NULL;\n", + " }\n", + " } else\n", + " PyErr_Clear();\n", + " }\n", + " PyObject * signatures = Py_BuildValue(\"(sO)\", \"(lp0\\n(lp1\\nccopy_reg\\n_reconstructor\\np2\\n(chope._ast\\nVariable\\np3\\nc__builtin__\\nobject\\np4\\nNtp5\\nRp6\\n(dp7\\nS'name'\\np8\\nS'X'\\np9\\nsS'dtype'\\np10\\nS'float64'\\np11\\nsS'dims'\\np12\\nI1\\nsbag2\\n(g3\\ng4\\nNtp13\\nRp14\\n(dp15\\ng8\\nS'Y'\\np16\\nsg10\\ng11\\nsg12\\nI1\\nsbaa.\", args);\n", + " if (!signatures) {\n", + " PyErr_SetString(PyExc_ValueError, \"Error building signature string for ln_hope_opt\");\n", + " return NULL;\n", + " }\n", + " return PyObject_Call(create_signature, signatures, NULL);\n", + " }\n", + "\n", + " PyMethodDef ln_optMethods[] = {\n", + " { \"set_create_signature\", set_create_signature, METH_VARARGS, \"signal handler\" },\n", + " { \"run\", run, METH_VARARGS, \"module function\" },\n", + " { NULL, NULL, 0, NULL }\n", + " };\n", + "\n", + "\n", + " static struct PyModuleDef ln_optmodule = {\n", + " PyModuleDef_HEAD_INIT,\n", + " \"ln\",\n", + " NULL,\n", + " -1,\n", + " ln_optMethods\n", + " };\n", + "\n", + "\n", + "\n", + " PyMODINIT_FUNC PyInit_ln_opt(void) {\n", + " import_array();\n", + " PyImport_ImportModule(\"numpy\");\n", + " return PyModule_Create(&ln_optmodule);\n", + " }\n", + "}\n", + "#include \n", + "#include \n", + "#include \n", + "#include \n", + "#include \n", + "void sighandler(int sig) {\n", + " std::ostringstream buffer;\n", + " buffer << \"Abort by \" << (sig == SIGSEGV ? \"segfault\" : \"bus error\") << std::endl;\n", + " void * stack[64];\n", + " std::size_t depth = backtrace(stack, 64);\n", + " if (!depth)\n", + " buffer << \" \" << std::endl;\n", + " else {\n", + " char ** symbols = backtrace_symbols(stack, depth);\n", + " for (std::size_t i = 1; i < depth; ++i) {\n", + " std::string symbol = symbols[i];\n", + " if (symbol.find_first_of(' ', 59) != std::string::npos) {\n", + " std::string name = symbol.substr(59, symbol.find_first_of(' ', 59) - 59);\n", + " int status;\n", + " char * demangled = abi::__cxa_demangle(name.c_str(), NULL, NULL, &status);\n", + " if (!status) {\n", + " buffer << \" \"\n", + " << symbol.substr(0, 59)\n", + " << demangled\n", + " << symbol.substr(59 + name.size())\n", + " << std::endl;\n", + " free(demangled);\n", + " } else\n", + " buffer << \" \" << symbol << std::endl;\n", + " } else\n", + " buffer << \" \" << symbol << std::endl;\n", + " }\n", + " free(symbols);\n", + " }\n", + " std::cerr << buffer.str();\n", + " std::exit(EXIT_FAILURE);\n", + " }\n" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Simplify CPP" + ] + }, + { + "cell_type": "code", + "execution_count": 11, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Overwriting src/poly.cpp\n" + ] + } + ], + "source": [ + "%%file src/poly.cpp\n", + "#define PY_ARRAY_UNIQUE_SYMBOL poly_ARRAY_API\n", + "#define NPY_NO_DEPRECATED_API NPY_1_7_API_VERSION\n", + "#include \n", + "#include \n", + "#include \n", + "#include \n", + "#include \n", + "#include \n", + "#include \n", + "#include \n", + "#include \n", + "#include \n", + "#include \n", + "struct PyObj {\n", + " typedef PyObject * ptr_t;\n", + " typedef PyArrayObject * arrptr_t;\n", + " PyObj(): dec(false), ptr(NULL) {}\n", + " PyObj(ptr_t p): dec(false), ptr(p) {}\n", + " ~PyObj() { if(dec) Py_DECREF(ptr); }\n", + " PyObj & operator=(ptr_t p) { if(dec) Py_DECREF(ptr); ptr = p; dec = false; return *this; }\n", + " PyObj & incref(ptr_t p) { if(dec) Py_DECREF(ptr); ptr = p; dec = (p != NULL); return *this; }\n", + " operator bool() const { return ptr; }\n", + " operator ptr_t() const { return ptr; }\n", + " operator arrptr_t() const { return (arrptr_t)ptr; }\n", + " bool dec;\n", + " ptr_t ptr;\n", + "};\n", + "inline void poly_d1d1(PyObject * pres, npy_intp const * __restrict__ sres, npy_double * __restrict__ cres,\n", + " PyObject * parg, npy_intp const * __restrict__ sarg, npy_double * __restrict__ carg);\n", + "\n", + "inline void poly_d1d1(PyObject * pres, npy_intp const * __restrict__ sres, npy_double * __restrict__ cres,\n", + " PyObject * parg, npy_intp const * __restrict__ sarg, npy_double * __restrict__ carg){\n", + "\n", + " npy_double arg_i;\n", + " double sin_arg_i;\n", + " for (npy_intp i0 = 0; i0 < sres[0] - 0; ++i0) {\n", + " arg_i = carg[i0];\n", + " cres[(int)(i0)] = std::pow(std::sin(arg_i), 2) + (std::pow(arg_i, 3) + std::pow(arg_i, 2) - arg_i - 1) / (std::pow(arg_i, 2) + 2 * arg_i + 1) + std::pow(std::cos(arg_i), 2);\n", + " }\n", + "}\n", + "void sighandler(int sig);\n", + "#include \n", + "extern \"C\" {\n", + " PyObject * create_signature;\n", + " struct sigaction slot;\n", + " PyObject * set_create_signature(PyObject * self, PyObject * args) {\n", + " if (!PyArg_ParseTuple(args, \"O\", &create_signature)) {\n", + " PyErr_SetString(PyExc_ValueError, \"Invalid Argument to set_create_signature!\");\n", + " return NULL;\n", + " }\n", + " Py_INCREF(create_signature);\n", + " memset(&slot, 0, sizeof(slot));\n", + " slot.sa_handler = &sighandler;\n", + " sigaction(SIGSEGV, &slot, NULL);\n", + " sigaction(SIGBUS, &slot, NULL);\n", + " Py_INCREF(Py_None);\n", + " return Py_None;\n", + " }\n", + " PyObject * run(PyObject * self, PyObject * args) {\n", + " {\n", + " PyObj pres;\n", + " PyObj parg;\n", + " if (\n", + " PyTuple_CheckExact(args) and PyTuple_GET_SIZE(args) == 2\n", + " and (pres = PyTuple_GET_ITEM(args, 0)) and PyArray_CheckExact(pres)\n", + " and PyArray_TYPE((PyArrayObject *)pres) == NPY_FLOAT64 and PyArray_NDIM((PyArrayObject *)pres) == 1\n", + " and (parg = PyTuple_GET_ITEM(args, 1)) and PyArray_CheckExact(parg)\n", + " and PyArray_TYPE((PyArrayObject *)parg) == NPY_FLOAT64 and PyArray_NDIM((PyArrayObject *)parg) == 1\n", + " ) {\n", + " if (!(pres.incref((PyObject *)PyArray_GETCONTIGUOUS((PyArrayObject *)pres)))) {\n", + " PyErr_SetString(PyExc_ValueError, \"Invalid Argument type on res!\");\n", + " return NULL;\n", + " }\n", + " if (!(parg.incref((PyObject *)PyArray_GETCONTIGUOUS((PyArrayObject *)parg)))) {\n", + " PyErr_SetString(PyExc_ValueError, \"Invalid Argument type on arg!\");\n", + " return NULL;\n", + " }\n", + " try {\n", + " poly_d1d1(\n", + " pres, PyArray_SHAPE((PyArrayObject *)pres), (npy_double *)PyArray_DATA((PyArrayObject *)pres)\n", + " , parg, PyArray_SHAPE((PyArrayObject *)parg), (npy_double *)PyArray_DATA((PyArrayObject *)parg)\n", + " );\n", + " Py_INCREF(Py_None);\n", + " return Py_None;\n", + " } catch (...) {\n", + " return NULL;\n", + " }\n", + " } else\n", + " PyErr_Clear();\n", + " }\n", + " PyObject * signatures = Py_BuildValue(\"(sO)\", \"(lp0\\n(lp1\\nccopy_reg\\n_reconstructor\\np2\\n(chope._ast\\nVariable\\np3\\nc__builtin__\\nobject\\np4\\nNtp5\\nRp6\\n(dp7\\nS'name'\\np8\\nS'res'\\np9\\nsS'dtype'\\np10\\nS'float64'\\np11\\nsS'dims'\\np12\\nI1\\nsbag2\\n(g3\\ng4\\nNtp13\\nRp14\\n(dp15\\ng8\\nS'arg'\\np16\\nsg10\\ng11\\nsg12\\nI1\\nsbaa.\", args);\n", + " if (!signatures) {\n", + " PyErr_SetString(PyExc_ValueError, \"Error building signature string for poly\");\n", + " return NULL;\n", + " }\n", + " return PyObject_Call(create_signature, signatures, NULL);\n", + " }\n", + "\n", + " PyMethodDef polyMethods[] = {\n", + " { \"set_create_signature\", set_create_signature, METH_VARARGS, \"signal handler\" },\n", + " { \"run\", run, METH_VARARGS, \"module function\" },\n", + " { NULL, NULL, 0, NULL }\n", + " };\n", + "\n", + " static struct PyModuleDef polymodule = {\n", + " PyModuleDef_HEAD_INIT,\n", + " \"poly\",\n", + " NULL,\n", + " -1,\n", + " polyMethods\n", + " };\n", + "\n", + " PyMODINIT_FUNC PyInit_poly(void) {\n", + " import_array();\n", + " PyImport_ImportModule(\"numpy\");\n", + " return PyModule_Create(&polymodule);\n", + " }\n", + "}\n", + "#include \n", + "#include \n", + "#include \n", + "#include \n", + "#include \n", + "void sighandler(int sig) {\n", + " std::ostringstream buffer;\n", + " buffer << \"Abort by \" << (sig == SIGSEGV ? \"segfault\" : \"bus error\") << std::endl;\n", + " void * stack[64];\n", + " std::size_t depth = backtrace(stack, 64);\n", + " if (!depth)\n", + " buffer << \" \" << std::endl;\n", + " else {\n", + " char ** symbols = backtrace_symbols(stack, depth);\n", + " for (std::size_t i = 1; i < depth; ++i) {\n", + " std::string symbol = symbols[i];\n", + " if (symbol.find_first_of(' ', 59) != std::string::npos) {\n", + " std::string name = symbol.substr(59, symbol.find_first_of(' ', 59) - 59);\n", + " int status;\n", + " char * demangled = abi::__cxa_demangle(name.c_str(), NULL, NULL, &status);\n", + " if (!status) {\n", + " buffer << \" \"\n", + " << symbol.substr(0, 59)\n", + " << demangled\n", + " << symbol.substr(59 + name.size())\n", + " << std::endl;\n", + " free(demangled);\n", + " } else\n", + " buffer << \" \" << symbol << std::endl;\n", + " } else\n", + " buffer << \" \" << symbol << std::endl;\n", + " }\n", + " free(symbols);\n", + " }\n", + " std::cerr << buffer.str();\n", + " std::exit(EXIT_FAILURE);\n", + " }\n" + ] + }, + { + "cell_type": "code", + "execution_count": 12, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Overwriting src/poly_opt.cpp\n" + ] + } + ], + "source": [ + "%%file src/poly_opt.cpp\n", + "#define PY_ARRAY_UNIQUE_SYMBOL poly_ARRAY_API\n", + "#define NPY_NO_DEPRECATED_API NPY_1_7_API_VERSION\n", + "#include \n", + "#include \n", + "#include \n", + "#include \n", + "#include \n", + "#include \n", + "#include \n", + "#include \n", + "#include \n", + "#include \n", + "#include \n", + "struct PyObj {\n", + "\ttypedef PyObject * ptr_t;\n", + "\ttypedef PyArrayObject * arrptr_t;\n", + "\tPyObj(): dec(false), ptr(NULL) {}\n", + "\tPyObj(ptr_t p): dec(false), ptr(p) {}\n", + "\t~PyObj() { if(dec) Py_DECREF(ptr); }\n", + "\tPyObj & operator=(ptr_t p) { if(dec) Py_DECREF(ptr); ptr = p; dec = false; return *this; }\n", + "\tPyObj & incref(ptr_t p) { if(dec) Py_DECREF(ptr); ptr = p; dec = (p != NULL); return *this; }\n", + "\toperator bool() const { return ptr; }\n", + "\toperator ptr_t() const { return ptr; }\n", + "\toperator arrptr_t() const { return (arrptr_t)ptr; }\n", + "\tbool dec;\n", + "\tptr_t ptr;\n", + "};\n", + "\n", + "inline void poly_d1d1(PyObject * pres, npy_intp const * __restrict__ sres, npy_double * __restrict__ cres, \n", + "\t\t\t\t\t\t\t\t\t\t\tPyObject * parg, npy_intp const * __restrict__ sarg, npy_double * __restrict__ carg);\n", + "\n", + "inline void poly_d1d1(PyObject * pres, npy_intp const * __restrict__ sres, npy_double * __restrict__ cres, \n", + "\t\t\t\t\t\t\t\t\t\t\tPyObject * parg, npy_intp const * __restrict__ sarg, npy_double * __restrict__ carg){\n", + "\tfor (npy_intp i0 = 0; i0 < sres[0] - 0; ++i0) {\n", + "\t\tcres[(int)(i0)] = carg[i0];\n", + "\t}\n", + "}\n", + "\n", + "void sighandler(int sig);\n", + "#include \n", + "extern \"C\" {\n", + "\tPyObject * create_signature;\n", + "\tstruct sigaction slot;\n", + "\tPyObject * set_create_signature(PyObject * self, PyObject * args) {\n", + "\t\tif (!PyArg_ParseTuple(args, \"O\", &create_signature)) {\n", + "\t\t\tPyErr_SetString(PyExc_ValueError, \"Invalid Argument to set_create_signature!\");\n", + "\t\t\treturn NULL;\n", + "\t\t}\n", + "\t\tPy_INCREF(create_signature);\n", + "\t\tmemset(&slot, 0, sizeof(slot));\n", + "\t\tslot.sa_handler = &sighandler;\n", + "\t\tsigaction(SIGSEGV, &slot, NULL);\n", + "\t\tsigaction(SIGBUS, &slot, NULL);\n", + "\t\tPy_INCREF(Py_None);\n", + "\t\treturn Py_None;\n", + "\t}\n", + "\tPyObject * run(PyObject * self, PyObject * args) {\n", + "\t\t{\n", + "\t\t\tPyObj pres;\n", + "\t\t\tPyObj parg;\n", + "\t\t\tif (\n", + "\t\t\t\tPyTuple_CheckExact(args) and PyTuple_GET_SIZE(args) == 2\n", + "\t\t\t\tand (pres = PyTuple_GET_ITEM(args, 0)) and PyArray_CheckExact(pres)\n", + "\t\t\t\tand PyArray_TYPE((PyArrayObject *)pres) == NPY_FLOAT64 and PyArray_NDIM((PyArrayObject *)pres) == 1\n", + "\t\t\t\tand (parg = PyTuple_GET_ITEM(args, 1)) and PyArray_CheckExact(parg)\n", + "\t\t\t\tand PyArray_TYPE((PyArrayObject *)parg) == NPY_FLOAT64 and PyArray_NDIM((PyArrayObject *)parg) == 1\n", + "\t\t\t) {\n", + "\t\t\t\tif (!(pres.incref((PyObject *)PyArray_GETCONTIGUOUS((PyArrayObject *)pres)))) {\n", + "\t\t\t\t\tPyErr_SetString(PyExc_ValueError, \"Invalid Argument type on res!\");\n", + "\t\t\t\t\treturn NULL;\n", + "\t\t\t\t}\n", + "\t\t\t\tif (!(parg.incref((PyObject *)PyArray_GETCONTIGUOUS((PyArrayObject *)parg)))) {\n", + "\t\t\t\t\tPyErr_SetString(PyExc_ValueError, \"Invalid Argument type on arg!\");\n", + "\t\t\t\t\treturn NULL;\n", + "\t\t\t\t}\n", + "\t\t\t\ttry {\n", + "\t\t\t\t\tpoly_d1d1(\n", + "\t\t\t\t\t\t pres, PyArray_SHAPE((PyArrayObject *)pres), (npy_double *)PyArray_DATA((PyArrayObject *)pres)\n", + "\t\t\t\t\t\t, parg, PyArray_SHAPE((PyArrayObject *)parg), (npy_double *)PyArray_DATA((PyArrayObject *)parg)\n", + "\t\t\t\t\t);\n", + "\t\t\t\t\tPy_INCREF(Py_None);\n", + "\t\t\t\t\treturn Py_None;\n", + "\t\t\t\t} catch (...) {\n", + "\t\t\t\t\treturn NULL;\n", + "\t\t\t\t}\n", + "\t\t\t} else\n", + "\t\t\t\tPyErr_Clear();\n", + "\t\t}\n", + "\t\tPyObject * signatures = Py_BuildValue(\"(sO)\", \"(lp0\\n(lp1\\nccopy_reg\\n_reconstructor\\np2\\n(chope._ast\\nVariable\\np3\\nc__builtin__\\nobject\\np4\\nNtp5\\nRp6\\n(dp7\\nS'name'\\np8\\nS'res'\\np9\\nsS'dtype'\\np10\\nS'float64'\\np11\\nsS'dims'\\np12\\nI1\\nsbag2\\n(g3\\ng4\\nNtp13\\nRp14\\n(dp15\\ng8\\nS'arg'\\np16\\nsg10\\ng11\\nsg12\\nI1\\nsbaa.\", args);\n", + "\t\tif (!signatures) {\n", + "\t\t\tPyErr_SetString(PyExc_ValueError, \"Error building signature string for poly\");\n", + "\t\t\treturn NULL;\n", + "\t\t}\n", + "\t\treturn PyObject_Call(create_signature, signatures, NULL);\n", + "\t}\n", + "\tPyMethodDef polyMethods[] = {\n", + "\t\t{ \"set_create_signature\", (PyCFunction)set_create_signature, METH_VARARGS },\n", + "\t\t{ \"run\", (PyCFunction)run, METH_VARARGS },\n", + "\t\t{ NULL, NULL }\n", + "\t};\n", + "\tPyMODINIT_FUNC initpoly(void) {\n", + "\t\timport_array();\n", + "\t\tPyImport_ImportModule(\"numpy\");\n", + "\t\t(void)Py_InitModule(\"poly\", polyMethods);\n", + "\t}\n", + "}\n", + "#include \n", + "#include \n", + "#include \n", + "#include \n", + "#include \n", + "void sighandler(int sig) {\n", + "\tstd::ostringstream buffer;\n", + "\tbuffer << \"Abort by \" << (sig == SIGSEGV ? \"segfault\" : \"bus error\") << std::endl;\n", + "\tvoid * stack[64];\n", + "\tstd::size_t depth = backtrace(stack, 64);\n", + "\tif (!depth)\n", + "\t\tbuffer << \" \" << std::endl;\n", + "\telse {\n", + "\t\tchar ** symbols = backtrace_symbols(stack, depth);\n", + "\t\tfor (std::size_t i = 1; i < depth; ++i) {\n", + "\t\t\tstd::string symbol = symbols[i];\n", + "\t\t\t\tif (symbol.find_first_of(' ', 59) != std::string::npos) {\n", + "\t\t\t\t\tstd::string name = symbol.substr(59, symbol.find_first_of(' ', 59) - 59);\n", + "\t\t\t\t\tint status;\n", + "\t\t\t\t\tchar * demangled = abi::__cxa_demangle(name.c_str(), NULL, NULL, &status);\n", + "\t\t\t\t\tif (!status) {\n", + "\t\t\t\t\t\tbuffer << \" \" \n", + "\t\t\t\t\t\t\t<< symbol.substr(0, 59) \n", + "\t\t\t\t\t\t\t<< demangled\n", + "\t\t\t\t\t\t\t<< symbol.substr(59 + name.size())\n", + "\t\t\t\t\t\t\t<< std::endl;\n", + "\t\t\t\t\t\tfree(demangled);\n", + "\t\t\t\t\t} else\n", + "\t\t\t\t\t\tbuffer << \" \" << symbol << std::endl;\n", + "\t\t\t\t} else\n", + "\t\t\t\t\tbuffer << \" \" << symbol << std::endl;\n", + "\t\t\t}\n", + "\t\t\tfree(symbols);\n", + "\t\t}\n", + "\t\tstd::cerr << buffer.str();\n", + "\t\tstd::exit(EXIT_FAILURE);\n", + "\t}\n" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Pairwise distance" + ] + }, + { + "cell_type": "code", + "execution_count": 13, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Overwriting src/pairwise.cpp\n" + ] + } + ], + "source": [ + "%%file src/pairwise.cpp\n", + "#define PY_ARRAY_UNIQUE_SYMBOL pairwise_ARRAY_API\n", + "#define NPY_NO_DEPRECATED_API NPY_1_7_API_VERSION\n", + "#include \n", + "#include \n", + "#include \n", + "#include \n", + "#include \n", + "#include \n", + "#include \n", + "#include \n", + "#include \n", + "#include \n", + "#include \n", + "struct PyObj {\n", + " typedef PyObject * ptr_t;\n", + " typedef PyArrayObject * arrptr_t;\n", + " PyObj(): dec(false), ptr(NULL) {}\n", + " PyObj(ptr_t p): dec(false), ptr(p) {}\n", + " ~PyObj() { if(dec) Py_DECREF(ptr); }\n", + " PyObj & operator=(ptr_t p) { if(dec) Py_DECREF(ptr); ptr = p; dec = false; return *this; }\n", + " PyObj & incref(ptr_t p) { if(dec) Py_DECREF(ptr); ptr = p; dec = (p != NULL); return *this; }\n", + " operator bool() const { return ptr; }\n", + " operator ptr_t() const { return ptr; }\n", + " operator arrptr_t() const { return (arrptr_t)ptr; }\n", + " bool dec;\n", + " ptr_t ptr;\n", + "};\n", + "\n", + "inline void pairwise_d2d2JJ(PyObject * pX,\n", + " npy_intp const * __restrict__ sX,\n", + " npy_double * __restrict__ cX,\n", + " PyObject * pD,\n", + " npy_intp const * __restrict__ sD,\n", + " npy_double * __restrict__ cD,\n", + " npy_int64 const cM,\n", + " npy_int64 const cN);\n", + "\n", + "inline void pairwise_d2d2JJ(PyObject * pX,\n", + " npy_intp const * __restrict__ sX,\n", + " npy_double * __restrict__ cX,\n", + " PyObject * pD,\n", + " npy_intp const * __restrict__ sD,\n", + " npy_double * __restrict__ cD,\n", + " npy_int64 const cM,\n", + " npy_int64 const cN){\n", + "\n", + " npy_double cd = 0.0;\n", + " npy_double ctmp = 0.0;\n", + "\n", + " npy_intp cj;\n", + " npy_intp ck;\n", + " npy_intp x_i_idx;\n", + " npy_intp x_j_idx;\n", + " npy_intp d_i_idx;\n", + " npy_intp const xp = sX[1];\n", + " npy_intp const dp = sD[1];\n", + "\n", + " for (npy_intp ci = 0; ci < cM; ++ci) {\n", + " x_i_idx = ci*xp;\n", + " d_i_idx = ci*dp;\n", + " for (cj = 0; cj < cM; ++cj) {\n", + " cd = 0.0;\n", + " x_j_idx = cj*xp;\n", + " for (ck = 0; ck < cN; ++ck) {\n", + " ctmp = (cX[(x_i_idx + ck)] - cX[(x_j_idx + ck)]);\n", + " cd += (ctmp * ctmp);\n", + " }\n", + " cD[(d_i_idx + cj)] = std::sqrt(cd);\n", + " }\n", + " }\n", + "}\n", + "\n", + "void sighandler(int sig);\n", + "#include \n", + "extern \"C\" {\n", + " PyObject * create_signature;\n", + " struct sigaction slot;\n", + " PyObject * set_create_signature(PyObject * self, PyObject * args) {\n", + " if (!PyArg_ParseTuple(args, \"O\", &create_signature)) {\n", + " PyErr_SetString(PyExc_ValueError, \"Invalid Argument to set_create_signature!\");\n", + " return NULL;\n", + " }\n", + " Py_INCREF(create_signature);\n", + " memset(&slot, 0, sizeof(slot));\n", + " slot.sa_handler = &sighandler;\n", + " sigaction(SIGSEGV, &slot, NULL);\n", + " sigaction(SIGBUS, &slot, NULL);\n", + " Py_INCREF(Py_None);\n", + " return Py_None;\n", + " }\n", + " PyObject * run(PyObject * self, PyObject * args) {\n", + " {\n", + " PyObj pX;\n", + " PyObj pD;\n", + " PyObject * pM; npy_int64 cM;\n", + " PyObject * pN; npy_int64 cN;\n", + " if (\n", + " PyTuple_CheckExact(args) and PyTuple_GET_SIZE(args) == 4\n", + " and (pX = PyTuple_GET_ITEM(args, 0)) and PyArray_CheckExact(pX)\n", + " and PyArray_TYPE((PyArrayObject *)pX) == NPY_FLOAT64 and PyArray_NDIM((PyArrayObject *)pX) == 2\n", + " and (pD = PyTuple_GET_ITEM(args, 1)) and PyArray_CheckExact(pD)\n", + " and PyArray_TYPE((PyArrayObject *)pD) == NPY_FLOAT64 and PyArray_NDIM((PyArrayObject *)pD) == 2\n", + " and (pM = PyTuple_GET_ITEM(args, 2)) and PyLong_CheckExact(pM)\n", + " and (pN = PyTuple_GET_ITEM(args, 3)) and PyLong_CheckExact(pN)\n", + " ) {\n", + " if (!(pX.incref((PyObject *)PyArray_GETCONTIGUOUS((PyArrayObject *)pX)))) {\n", + " PyErr_SetString(PyExc_ValueError, \"Invalid Argument type on X!\");\n", + " return NULL;\n", + " }\n", + " if (!(pD.incref((PyObject *)PyArray_GETCONTIGUOUS((PyArrayObject *)pD)))) {\n", + " PyErr_SetString(PyExc_ValueError, \"Invalid Argument type on D!\");\n", + " return NULL;\n", + " }\n", + " cM = PyLong_AS_LONG(pM);\n", + " cN = PyLong_AS_LONG(pN);\n", + " try {\n", + " pairwise_d2d2JJ(\n", + " pX, PyArray_SHAPE((PyArrayObject *)pX), (npy_double *)PyArray_DATA((PyArrayObject *)pX)\n", + " , pD, PyArray_SHAPE((PyArrayObject *)pD), (npy_double *)PyArray_DATA((PyArrayObject *)pD)\n", + " , cM\n", + " , cN\n", + " );\n", + " Py_INCREF(Py_None);\n", + " return Py_None;\n", + " } catch (...) {\n", + " return NULL;\n", + " }\n", + " } else\n", + " PyErr_Clear();\n", + " }\n", + " PyObject * signatures = Py_BuildValue(\"(sO)\", \"(lp0\\n(lp1\\nccopy_reg\\n_reconstructor\\np2\\n(chope._ast\\nVariable\\np3\\nc__builtin__\\nobject\\np4\\nNtp5\\nRp6\\n(dp7\\nS'name'\\np8\\nS'X'\\np9\\nsS'dtype'\\np10\\nS'float64'\\np11\\nsS'dims'\\np12\\nI2\\nsbag2\\n(g3\\ng4\\nNtp13\\nRp14\\n(dp15\\ng8\\nS'D'\\np16\\nsg10\\ng11\\nsg12\\nI2\\nsbag2\\n(g3\\ng4\\nNtp17\\nRp18\\n(dp19\\ng8\\nS'M'\\np20\\nsg10\\nc__builtin__\\nint\\np21\\nsg12\\nI0\\nsbag2\\n(g3\\ng4\\nNtp22\\nRp23\\n(dp24\\ng8\\nS'N'\\np25\\nsg10\\ng21\\nsg12\\nI0\\nsbaa.\", args);\n", + " if (!signatures) {\n", + " PyErr_SetString(PyExc_ValueError, \"Error building signature string for pairwise\");\n", + " return NULL;\n", + " }\n", + " return PyObject_Call(create_signature, signatures, NULL);\n", + " }\n", + "\n", + " PyMethodDef pairwiseMethods[] = {\n", + " { \"set_create_signature\", set_create_signature, METH_VARARGS, \"signal handler\" },\n", + " { \"run\", run, METH_VARARGS, \"module function\" },\n", + " { NULL, NULL, 0, NULL }\n", + " };\n", + "\n", + "\n", + " static struct PyModuleDef pairwisemodule = {\n", + " PyModuleDef_HEAD_INIT,\n", + " \"pairwise\",\n", + " NULL,\n", + " -1,\n", + " pairwiseMethods\n", + " };\n", + "\n", + "\n", + " PyMODINIT_FUNC PyInit_pairwise(void) {\n", + " import_array();\n", + " PyImport_ImportModule(\"numpy\");\n", + " return PyModule_Create(&pairwisemodule);\n", + " }\n", + "}\n", + "#include \n", + "#include \n", + "#include \n", + "#include \n", + "#include \n", + "void sighandler(int sig) {\n", + " std::ostringstream buffer;\n", + " buffer << \"Abort by \" << (sig == SIGSEGV ? \"segfault\" : \"bus error\") << std::endl;\n", + " void * stack[64];\n", + " std::size_t depth = backtrace(stack, 64);\n", + " if (!depth)\n", + " buffer << \" \" << std::endl;\n", + " else {\n", + " char ** symbols = backtrace_symbols(stack, depth);\n", + " for (std::size_t i = 1; i < depth; ++i) {\n", + " std::string symbol = symbols[i];\n", + " if (symbol.find_first_of(' ', 59) != std::string::npos) {\n", + " std::string name = symbol.substr(59, symbol.find_first_of(' ', 59) - 59);\n", + " int status;\n", + " char * demangled = abi::__cxa_demangle(name.c_str(), NULL, NULL, &status);\n", + " if (!status) {\n", + " buffer << \" \"\n", + " << symbol.substr(0, 59)\n", + " << demangled\n", + " << symbol.substr(59 + name.size())\n", + " << std::endl;\n", + " free(demangled);\n", + " } else\n", + " buffer << \" \" << symbol << std::endl;\n", + " } else\n", + " buffer << \" \" << symbol << std::endl;\n", + " }\n", + " free(symbols);\n", + " }\n", + " std::cerr << buffer.str();\n", + " std::exit(EXIT_FAILURE);\n", + " }\n" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Star point spread function CPP" + ] + }, + { + "cell_type": "code", + "execution_count": 14, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Overwriting src/pdf.cpp\n" + ] + } + ], + "source": [ + "%%file src/pdf.cpp\n", + "#define PY_ARRAY_UNIQUE_SYMBOL pdf_ARRAY_API\n", + "#define NPY_NO_DEPRECATED_API NPY_1_7_API_VERSION\n", + "#include \n", + "#include \n", + "#include \n", + "#include \n", + "#include \n", + "#include \n", + "#include \n", + "#include \n", + "#include \n", + "#include \n", + "#include \n", + "struct PyObj {\n", + " typedef PyObject * ptr_t;\n", + " typedef PyArrayObject * arrptr_t;\n", + " PyObj(): dec(false), ptr(NULL) {}\n", + " PyObj(ptr_t p): dec(false), ptr(p) {}\n", + " ~PyObj() { if(dec) Py_DECREF(ptr); }\n", + " PyObj & operator=(ptr_t p) { if(dec) Py_DECREF(ptr); ptr = p; dec = false; return *this; }\n", + " PyObj & incref(ptr_t p) { if(dec) Py_DECREF(ptr); ptr = p; dec = (p != NULL); return *this; }\n", + " operator bool() const { return ptr; }\n", + " operator ptr_t() const { return ptr; }\n", + " operator arrptr_t() const { return (arrptr_t)ptr; }\n", + " bool dec;\n", + " ptr_t ptr;\n", + "};\n", + "\n", + "inline std::tuple pdf_f2l1d1f2JDd(\n", + " PyObject * pdensity\n", + " , npy_intp const * __restrict__ sdensity\n", + " , npy_float * __restrict__ cdensity\n", + " , PyObject * pdims\n", + " , npy_intp const * __restrict__ sdims\n", + " , npy_int64 * __restrict__ cdims\n", + " , PyObject * pcenter\n", + " , npy_intp const * __restrict__ scenter\n", + " , npy_double * __restrict__ ccenter\n", + " , PyObject * pw2D\n", + " , npy_intp const * __restrict__ sw2D\n", + " , npy_float * __restrict__ cw2D\n", + " , npy_int64 cr50\n", + " , npy_double cb\n", + " , npy_double ca);\n", + "\n", + "inline std::tuple pdf_f2l1d1f2JDd(\n", + " PyObject * pdensity\n", + " , npy_intp const * __restrict__ sdensity\n", + " , npy_float * __restrict__ cdensity\n", + " , PyObject * pdims\n", + " , npy_intp const * __restrict__ sdims\n", + " , npy_int64 * __restrict__ cdims\n", + " , PyObject * pcenter\n", + " , npy_intp const * __restrict__ scenter\n", + " , npy_double * __restrict__ ccenter\n", + " , PyObject * pw2D\n", + " , npy_intp const * __restrict__ sw2D\n", + " , npy_float * __restrict__ cw2D\n", + " , npy_int64 const cr50\n", + " , npy_double const cb\n", + " , npy_double const ca) {\n", + "\n", + " npy_double cdr;\n", + " npy_double c__sum0;\n", + " const npy_double x_center = ccenter[0];\n", + " const npy_double y_center = ccenter[1];\n", + " const npy_intp len_0_sw2D = sw2D[0];\n", + " const npy_intp len_1_sw2D = sw2D[1];\n", + " npy_intp dc[] = {len_0_sw2D, len_1_sw2D};\n", + " PyObject * pc = PyArray_EMPTY(2, dc, NPY_FLOAT64, 0);\n", + " npy_intp * sc = PyArray_SHAPE((PyArrayObject *)pc);\n", + " npy_double * cc = (npy_double *)PyArray_DATA((PyArrayObject *)pc);\n", + "\n", + " npy_intp cy = 0;\n", + " npy_intp i0 = 0;\n", + " npy_intp i1 = 0;\n", + " npy_intp i2 = 0;\n", + " npy_intp i3 = 0;\n", + "\n", + " npy_intp sw2D_i0_idx;\n", + " npy_intp sw2D_i2_idx;\n", + " npy_intp density_x_idx;\n", + "\n", + " auto c__sp0 = ca * ca;\n", + " auto c__sp1 = cr50 * cr50;\n", + " auto c__sp2 = 1.0 / (c__sp0 * c__sp1);\n", + " auto c__sp4 = 0.3183098861846737 * c__sp2 * (-1 + cb);\n", + "\n", + " for (npy_intp cx = 0; cx < cdims[(int)(0)]; ++cx) {\n", + "\n", + " density_x_idx = cx*sdensity[1];\n", + " for (cy = 0; cy < cdims[(int)(1)]; ++cy) {\n", + "\n", + " cdr = std::sqrt(std::pow(cx - x_center, 2) + std::pow(cy - y_center, 2));\n", + " auto c__sp3 = cdr * cdr;\n", + " auto c__sp5 = c__sp4 * std::pow((1 + c__sp2 * c__sp3), -cb);\n", + "\n", + " for (i0 = 0; i0 < len_0_sw2D - 0; ++i0) {\n", + "\n", + " sw2D_i0_idx = (i0)*len_1_sw2D;\n", + " for (i1 = 0; i1 < len_1_sw2D - 0; ++i1) {\n", + " cc[sw2D_i0_idx + i1] = c__sp5 * cw2D[sw2D_i0_idx + i1];\n", + " }\n", + " }\n", + "\n", + " c__sum0 = 0;\n", + " for (i2 = 0; i2 < len_0_sw2D - 0; ++i2) {\n", + "\n", + " sw2D_i2_idx = (i2)*len_1_sw2D;\n", + " for (i3 = 0; i3 < len_1_sw2D - 0; ++i3) {\n", + " c__sum0 += cc[sw2D_i2_idx + i3];\n", + " }\n", + " }\n", + "\n", + " cdensity[(density_x_idx + cy)] = c__sum0;\n", + " }\n", + " }\n", + " return std::make_tuple((PyObject *)pdensity, sdensity, cdensity);\n", + "}\n", + "\n", + "void sighandler(int sig);\n", + "#include \n", + "extern \"C\" {\n", + " PyObject * create_signature;\n", + " struct sigaction slot;\n", + " PyObject * set_create_signature(PyObject * self, PyObject * args) {\n", + " if (!PyArg_ParseTuple(args, \"O\", &create_signature)) {\n", + " PyErr_SetString(PyExc_ValueError, \"Invalid Argument to set_create_signature!\");\n", + " return NULL;\n", + " }\n", + " Py_INCREF(create_signature);\n", + " memset(&slot, 0, sizeof(slot));\n", + " slot.sa_handler = &sighandler;\n", + " sigaction(SIGSEGV, &slot, NULL);\n", + " sigaction(SIGBUS, &slot, NULL);\n", + " Py_INCREF(Py_None);\n", + " return Py_None;\n", + " }\n", + " PyObject * run(PyObject * self, PyObject * args) {\n", + " {\n", + " PyObj pdensity;\n", + " PyObj pdims;\n", + " PyObj pcenter;\n", + " PyObj pw2D;\n", + " PyObject * pr50; npy_int64 cr50;\n", + " PyObject * pb; npy_double cb;\n", + " PyObject * pa; npy_double ca;\n", + " if (\n", + " PyTuple_CheckExact(args) and PyTuple_GET_SIZE(args) == 7\n", + " and (pdensity = PyTuple_GET_ITEM(args, 0)) and PyArray_CheckExact(pdensity)\n", + " and PyArray_TYPE((PyArrayObject *)pdensity) == NPY_FLOAT32 and PyArray_NDIM((PyArrayObject *)pdensity) == 2\n", + " and (pdims = PyTuple_GET_ITEM(args, 1)) and PyArray_CheckExact(pdims)\n", + " and PyArray_TYPE((PyArrayObject *)pdims) == NPY_INT64 and PyArray_NDIM((PyArrayObject *)pdims) == 1\n", + " and (pcenter = PyTuple_GET_ITEM(args, 2)) and PyArray_CheckExact(pcenter)\n", + " and PyArray_TYPE((PyArrayObject *)pcenter) == NPY_FLOAT64 and PyArray_NDIM((PyArrayObject *)pcenter) == 1\n", + " and (pw2D = PyTuple_GET_ITEM(args, 3)) and PyArray_CheckExact(pw2D)\n", + " and PyArray_TYPE((PyArrayObject *)pw2D) == NPY_FLOAT32 and PyArray_NDIM((PyArrayObject *)pw2D) == 2\n", + " and (pr50 = PyTuple_GET_ITEM(args, 4)) and PyLong_CheckExact(pr50)\n", + " and (pb = PyTuple_GET_ITEM(args, 5)) and PyFloat_CheckExact(pb)\n", + " and (pa = PyTuple_GET_ITEM(args, 6)) and PyArray_IsScalar(pa, Double)\n", + " ) {\n", + " if (!(pdensity.incref((PyObject *)PyArray_GETCONTIGUOUS((PyArrayObject *)pdensity)))) {\n", + " PyErr_SetString(PyExc_ValueError, \"Invalid Argument type on density!\");\n", + " return NULL;\n", + " }\n", + " if (!(pdims.incref((PyObject *)PyArray_GETCONTIGUOUS((PyArrayObject *)pdims)))) {\n", + " PyErr_SetString(PyExc_ValueError, \"Invalid Argument type on dims!\");\n", + " return NULL;\n", + " }\n", + " if (!(pcenter.incref((PyObject *)PyArray_GETCONTIGUOUS((PyArrayObject *)pcenter)))) {\n", + " PyErr_SetString(PyExc_ValueError, \"Invalid Argument type on center!\");\n", + " return NULL;\n", + " }\n", + " if (!(pw2D.incref((PyObject *)PyArray_GETCONTIGUOUS((PyArrayObject *)pw2D)))) {\n", + " PyErr_SetString(PyExc_ValueError, \"Invalid Argument type on w2D!\");\n", + " return NULL;\n", + " }\n", + " cr50 = PyLong_AS_LONG(pr50);\n", + " cb = PyFloat_AS_DOUBLE(pb);\n", + " ca = PyArrayScalar_VAL(pa, Double);\n", + " try {\n", + " PyObject * res = std::get<0>(pdf_f2l1d1f2JDd(\n", + " pdensity, PyArray_SHAPE((PyArrayObject *)pdensity), (npy_float *)PyArray_DATA((PyArrayObject *)pdensity)\n", + " , pdims, PyArray_SHAPE((PyArrayObject *)pdims), (npy_int64 *)PyArray_DATA((PyArrayObject *)pdims)\n", + " , pcenter, PyArray_SHAPE((PyArrayObject *)pcenter), (npy_double *)PyArray_DATA((PyArrayObject *)pcenter)\n", + " , pw2D, PyArray_SHAPE((PyArrayObject *)pw2D), (npy_float *)PyArray_DATA((PyArrayObject *)pw2D)\n", + " , cr50\n", + " , cb\n", + " , ca\n", + " ));\n", + "\n", + " Py_INCREF(res);\n", + " return res;\n", + " } catch (...) {\n", + " return NULL;\n", + " }\n", + " } else\n", + " PyErr_Clear();\n", + " }\n", + " PyObject * signatures = Py_BuildValue(\"(sO)\", \"(lp0\\n(lp1\\nccopy_reg\\n_reconstructor\\np2\\n(chope._ast\\nVariable\\np3\\nc__builtin__\\nobject\\np4\\nNtp5\\nRp6\\n(dp7\\nS'name'\\np8\\nS'density'\\np9\\nsS'dtype'\\np10\\nS'float32'\\np11\\nsS'dims'\\np12\\nI2\\nsbag2\\n(g3\\ng4\\nNtp13\\nRp14\\n(dp15\\ng8\\ng12\\nsg10\\nS'int64'\\np16\\nsg12\\nI1\\nsbag2\\n(g3\\ng4\\nNtp17\\nRp18\\n(dp19\\ng8\\nS'center'\\np20\\nsg10\\nS'float64'\\np21\\nsg12\\nI1\\nsbag2\\n(g3\\ng4\\nNtp22\\nRp23\\n(dp24\\ng8\\nS'w2D'\\np25\\nsg10\\ng11\\nsg12\\nI2\\nsbag2\\n(g3\\ng4\\nNtp26\\nRp27\\n(dp28\\ng8\\nS'r50'\\np29\\nsg10\\nc__builtin__\\nint\\np30\\nsg12\\nI0\\nsbag2\\n(g3\\ng4\\nNtp31\\nRp32\\n(dp33\\ng8\\nS'b'\\np34\\nsg10\\nc__builtin__\\nfloat\\np35\\nsg12\\nI0\\nsbag2\\n(g3\\ng4\\nNtp36\\nRp37\\n(dp38\\ng8\\nS'a'\\np39\\nsg10\\ng21\\nsg12\\nI0\\nsbaa.\", args);\n", + " if (!signatures) {\n", + " PyErr_SetString(PyExc_ValueError, \"Error building signature string for pdf\");\n", + " return NULL;\n", + " }\n", + " return PyObject_Call(create_signature, signatures, NULL);\n", + " }\n", + "\n", + " PyMethodDef pdfMethods[] = {\n", + " { \"set_create_signature\", set_create_signature, METH_VARARGS, \"signal handler\" },\n", + " { \"run\", run, METH_VARARGS, \"module function\" },\n", + " { NULL, NULL, 0, NULL }\n", + " };\n", + "\n", + " static struct PyModuleDef pdfmodule = {\n", + " PyModuleDef_HEAD_INIT,\n", + " \"pdf\",\n", + " NULL,\n", + " -1,\n", + " pdfMethods\n", + " };\n", + "\n", + " PyMODINIT_FUNC PyInit_pdf(void) {\n", + " import_array();\n", + " PyImport_ImportModule(\"numpy\");\n", + " return PyModule_Create(&pdfmodule);\n", + " }\n", + "}\n", + "#include \n", + "#include \n", + "#include \n", + "#include \n", + "#include \n", + "void sighandler(int sig) {\n", + " std::ostringstream buffer;\n", + " buffer << \"Abort by \" << (sig == SIGSEGV ? \"segfault\" : \"bus error\") << std::endl;\n", + " void * stack[64];\n", + " std::size_t depth = backtrace(stack, 64);\n", + " if (!depth)\n", + " buffer << \" \" << std::endl;\n", + " else {\n", + " char ** symbols = backtrace_symbols(stack, depth);\n", + " for (std::size_t i = 1; i < depth; ++i) {\n", + " std::string symbol = symbols[i];\n", + " if (symbol.find_first_of(' ', 59) != std::string::npos) {\n", + " std::string name = symbol.substr(59, symbol.find_first_of(' ', 59) - 59);\n", + " int status;\n", + " char * demangled = abi::__cxa_demangle(name.c_str(), NULL, NULL, &status);\n", + " if (!status) {\n", + " buffer << \" \"\n", + " << symbol.substr(0, 59)\n", + " << demangled\n", + " << symbol.substr(59 + name.size())\n", + " << std::endl;\n", + " free(demangled);\n", + " } else\n", + " buffer << \" \" << symbol << std::endl;\n", + " } else\n", + " buffer << \" \" << symbol << std::endl;\n", + " }\n", + " free(symbols);\n", + " }\n", + " std::cerr << buffer.str();\n", + " std::exit(EXIT_FAILURE);\n", + " }" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Python helper module" + ] + }, + { + "cell_type": "code", + "execution_count": 15, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Overwriting native_util.py\n" + ] + } + ], + "source": [ + "%%file native_util.py\n", + "import importlib\n", + "import os\n", + "import glob\n", + "import sys\n", + "\n", + "from numpy.distutils.misc_util import get_numpy_include_dirs\n", + "import setuptools\n", + "from os import listdir\n", + "# import tempfile\n", + "from hope import config\n", + "\n", + "try:\n", + " # python 3\n", + " import io\n", + " file = io.IOBase\n", + "except ImportError:\n", + " pass\n", + "\n", + "\n", + "def load(name):\n", + " compile(name, \"./src\")\n", + " module = importlib.import_module(name)\n", + " # module = __import__(name, globals(), locals(), [], -1)\n", + " return module\n", + "\n", + "\n", + "\n", + "def compile(name, src_folder, target_folder = \"./\"):\n", + " localfilename =os.path.join(src_folder, name)\n", + " \n", + " outfile, stdout, stderr, argv = None, None, None, sys.argv\n", + " try:\n", + " sys.stdout.flush(), sys.stderr.flush()\n", + " outpath = os.path.join(target_folder, \"{0}.out\".format(localfilename))\n", + " if os.path.exists(outpath):\n", + " os.remove(outpath)\n", + " \n", + " so_path = os.path.join(target_folder, \"{0}.so\".format(name))\n", + " if os.path.exists(so_path):\n", + " os.remove(so_path)\n", + " \n", + " outfile = open(outpath, 'w')\n", + " \n", + " try:\n", + " sys.stdout.fileno()\n", + " sys.stderr.fileno()\n", + " have_fileno = True\n", + " except OSError:\n", + " have_fileno = False\n", + " \n", + " if have_fileno and isinstance(sys.stdout, file) and isinstance(sys.stdout, file):\n", + " stdout, stderr = os.dup(sys.stdout.fileno()), os.dup(sys.stderr.fileno())\n", + " os.dup2(outfile.fileno(), sys.stdout.fileno())\n", + " os.dup2(outfile.fileno(), sys.stderr.fileno())\n", + " else:\n", + " stdout, stderr = sys.stdout, sys.stderr\n", + " sys.stdout, sys.stderr = outfile, outfile\n", + " try:\n", + " sources = \"./{0}.cpp\".format(localfilename)\n", + " sys.argv = [\"\", \"build_ext\",\n", + " \"-b\", target_folder, #--build-lib (-b) directory for compiled extension modules\n", + " \"-t\", \".\" #--build-temp - a rel path will result in a dir structure of -b at the cur position \n", + " ]\n", + " \n", + " localfilename = str(localfilename)\n", + " sources = str(sources)\n", + " \n", + " setuptools.setup( \\\n", + " name = name\\\n", + " , ext_modules = [setuptools.Extension( \\\n", + " name \\\n", + " , sources = [sources] \\\n", + " , extra_compile_args = config.cxxflags \\\n", + " )] \\\n", + " , include_dirs = get_numpy_include_dirs() \\\n", + " )\n", + " except SystemExit as e:\n", + " print(sys.stderr.write(str(e)))\n", + " sys.stdout.flush(), sys.stderr.flush()\n", + " finally:\n", + " if isinstance(stdout, int):\n", + " os.dup2(stdout, sys.stdout.fileno()), os.close(stdout)\n", + " elif not stdout is None:\n", + " sys.stdout = stdout\n", + " if isinstance(stderr, int):\n", + " os.dup2(stderr, sys.stderr.fileno()), os.close(stderr)\n", + " elif not stderr is None:\n", + " sys.stderr = stderr\n", + " if isinstance(outfile, file):\n", + " outfile.close()\n", + " sys.argv = argv\n", + " \n", + " with open(outpath) as outfile:\n", + " out = outfile.read()\n", + " \n", + " modules = glob.glob(os.path.join(target_folder, \"{}*.so\".format(name)))\n", + " \n", + " if not modules or out.find(\"error:\") > -1:\n", + " print(out)\n", + " raise Exception(\"Error compiling function {0} (compiled to {1})\".format(localfilename, target_folder))\n", + " \n", + " if out.find(\"warning:\") > -1:\n", + " import warnings\n", + " warnings.warn(\"A warning has been issued during compilation:\\n%s\"%out)\n", + " \n", + " print(out)\n", + "\n", + "\n", + "def compile_all():\n", + " src_folder = \"./src\"\n", + " func_names = (src_file.split(\".cpp\")[0] for src_file in listdir(src_folder) if src_file.endswith(\".cpp\"))\n", + " for func_name in func_names:\n", + " compile(func_name, src_folder)\n", + " \n", + " \n", + "if __name__ == '__main__':\n", + " compile_all()" + ] + }, + { + "cell_type": "code", + "execution_count": 16, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Overwriting util.py\n" + ] + } + ], + "source": [ + "%%file util.py\n", + "# Copyright (C) 2014 ETH Zurich, Institute for Astronomy\n", + "\n", + "'''\n", + "Created on Aug 4, 2014\n", + "\n", + "author: jakeret\n", + "'''\n", + "from __future__ import print_function, division, absolute_import, unicode_literals\n", + "import math\n", + "\n", + "def perf_comp_data(func_list, data_list, rep=5, number=1, extra_setup=None):\n", + " ''' Function to compare the performance of different functions.\n", + " \n", + " Parameters\n", + " ==========\n", + " func_list : list\n", + " list with function names as strings\n", + " data_list : list\n", + " list with data set names as strings\n", + " rep : int\n", + " number of repetitions of the whole comparison\n", + " number : int\n", + " number of executions for every function\n", + " '''\n", + " from timeit import repeat\n", + " res_list = {}\n", + " for name in enumerate(func_list):\n", + " if data_list is None:\n", + " stmt = \"%s()\"%(name[1])\n", + " setup = \"from __main__ import %s\"%(name[1]) \n", + " else:\n", + " stmt = \"%s(%s)\"%(name[1], data_list[name[0]])\n", + " setup = \"from __main__ import %s, %s\"%(name[1], data_list[name[0]])\n", + " if extra_setup is not None:\n", + " stmt = extra_setup + \"; \" + stmt\n", + " \n", + " results = repeat(stmt=stmt, setup=setup, repeat=rep, number=number)\n", + " \n", + " res_list[name[1]] = (median(results), min(results))\n", + " \n", + "# res_sort = sorted(res_list.iteritems(), key=lambda (k, v): (v, k))\n", + " res_sort = sorted(iter(res_list.items()), key=lambda k_v: (k_v[1], k_v[0]))\n", + " for func, (av_time, min_time) in res_sort:\n", + " rel = av_time / res_sort[0][1][0]\n", + " print('function: {0!s:20}, av. time sec: {1:>12.8f}, min. time sec: {2:>12.8f}, relative: {3:>9.1f}'.format(func, av_time, min_time, rel))\n", + "\n", + "def median(x):\n", + " s_x = sorted(x)\n", + " return (s_x[int(math.floor((len(s_x)-1)/2))] + s_x[int(math.ceil((len(s_x)-1)/2))])/2" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.5.1" + } + }, + "nbformat": 4, + "nbformat_minor": 1 +} diff --git a/benchmarks/numexpr.html b/benchmarks/numexpr.html new file mode 100644 index 0000000..1a33351 --- /dev/null +++ b/benchmarks/numexpr.html @@ -0,0 +1,12383 @@ + + + +numexpr + + + + + + + + + + + + + + + + + + + + +
+
+ +
+
+
In [1]:
+
+
+
import hope
+hope.config.optimize = True
+hope.config.verbose = True
+hope.config.keeptemp = True
+import numba
+import numpy as np
+import numexpr as ne
+
+from util import perf_comp_data
+from native_util import load
+%load_ext Cython
+%load_ext version_information
+%version_information numpy, Cython, numba, hope, numexpr
+
+ +
+
+
+ +
+
+ + +
+ +
Out[1]:
+ + + +
+
SoftwareVersion
Python3.5.1 64bit [GCC 4.2.1 (Apple Inc. build 5666) (dot 3)]
IPython6.1.0
OSDarwin 15.6.0 x86_64 i386 64bit
numpy1.13.1
Cython0.26
numba0.34.0
hope0.6.1
numexpr2.6.2
Mon Sep 04 16:42:26 2017 CEST
+
+ +
+ +
+
+ +
+
+
+
In [2]:
+
+
+
# Python version
+
+def ln_python(X, Y):
+    Y[:] = (X-1) - (X-1)**2 / 2 + (X-1)**3 / 3 - (X-1)**4 / 4 + (X-1)**5 / 5 - (X-1)**6 / 6 + (X-1)**7 / 7 - (X-1)**8 / 8 + (X-1)**9 / 9
+
+ +
+
+
+ +
+
+
+
In [3]:
+
+
+
# Python version
+
+def ln_python_exp(X, Y):
+    x = (X - 1)
+    x2 = x*x
+    x4 = x2*x2
+    x6 = x4*x2
+    x8 = x4*x4
+    Y[:] = x - x2 / 2 + x * x2 / 3 - x4 / 4 + x * x4 / 5 - x6 / 6 + x6 * x / 7 - x8 / 8 + x8 * x / 9
+
+ +
+
+
+ +
+
+
+
In [4]:
+
+
+
# NumExpr version
+def ln_numexpr(X, Y):
+    Y[:] = ne.evaluate("(X-1) - (X-1)**2 / 2 + (X-1)**3 / 3 - (X-1)**4 / 4 + (X-1)**5 / 5 - (X-1)**6 / 6 + (X-1)**7 / 7 - (X-1)**8 / 8 + (X-1)**9 / 9")
+
+ +
+
+
+ +
+
+
+
In [5]:
+
+
+
# Hope version
+@hope.jit
+def ln_hope(X, Y):
+    Y[:] = (X-1) - (X-1)**2 / 2 + (X-1)**3 / 3 - (X-1)**4 / 4 + (X-1)**5 / 5 - (X-1)**6 / 6 + (X-1)**7 / 7 - (X-1)**8 / 8 + (X-1)**9 / 9
+
+ +
+
+
+ +
+
+
+
In [6]:
+
+
+
# Hope version
+
+import hope
+hope.config.optimize = False
+@hope.jit
+def ln_hope_exp(X, Y):
+    x = (X - 1)
+    x2 = x*x
+    x4 = x2*x2
+    x6 = x4*x2
+    x8 = x4*x4
+    Y[:] = x - x2 / 2 + x * x2 / 3 - x4 / 4 + x * x4 / 5 - x6 / 6 + x6 * x / 7 - x8 / 8 + x8 * x / 9
+
+ +
+
+
+ +
+
+
+
In [7]:
+
+
+
# numba version
+
+@numba.jit
+def ln_numba(X, Y):
+    Y[:] = (X-1) - (X-1)**2 / 2 + (X-1)**3 / 3 - (X-1)**4 / 4 + (X-1)**5 / 5 - (X-1)**6 / 6 + (X-1)**7 / 7 - (X-1)**8 / 8 + (X-1)**9 / 9
+
+ +
+
+
+ +
+
+
+
In [8]:
+
+
+
# numba version
+
+import numba
+
+@numba.jit
+def ln_numba_exp(X, Y):
+    x = (X - 1)
+    x2 = x*x
+    x4 = x2*x2
+    x6 = x4*x2
+    x8 = x4*x4
+    Y[:] = x - x2 / 2 + x * x2 / 3 - x4 / 4 + x * x4 / 5 - x6 / 6 + x6 * x / 7 - x8 / 8 + x8 * x / 9
+
+ +
+
+
+ +
+
+
+
In [9]:
+
+
+
%%cython
+
+cimport cython
+
+cimport cython
+import numpy as np
+cimport numpy as np
+
+@cython.boundscheck(False)
+@cython.wraparound(False)
+def ln_cython(np.ndarray[np.double_t, ndim=1] X, np.ndarray[np.double_t, ndim=1] Y):
+    cdef size_t i, n
+    cdef double x
+    n = len(X)
+    cdef double *xx = <double *>X.data
+    cdef double *yy = <double *>Y.data
+    for i in range(n):
+        x = xx[i] 
+        yy[i] = (x-1) - (x-1)**2 / 2 + (x-1)**3 / 3 - (x-1)**4 / 4 + (x-1)**5 / 5 - (x-1)**6 / 6 + (x-1)**7 / 7 - (x-1)**8 / 8 + (x-1)**9 / 9
+
+    
+@cython.boundscheck(False)
+@cython.wraparound(False)
+def ln_cython_exp(np.ndarray[np.float_t, ndim=1] X, np.ndarray[np.float_t, ndim=1] Y):
+    cdef size_t i, n
+    cdef double x, x2, x4, x6, x8
+    cdef double *xx = <double *>X.data
+    cdef double *yy = <double *>Y.data
+    n = len(X)
+    for i in range(n):
+        x = xx[i] - 1
+        x2 = x * x
+        x4 = x2 * x2
+        x6 = x2 * x4
+        x8 = x4 * x4
+        yy[i] = x - x2 / 2 + x * x2 / 3 - x4 / 4 + x * x4 / 5 - x6 / 6 + x6 * x / 7 - x8 / 8 + x8 * x / 9
+
+ +
+
+
+ +
+
+
+
In [10]:
+
+
+
from native_util import load
+native_ln_mod = load("ln")
+ln_native = native_ln_mod.run
+
+native_ln_opt_mod = load("ln_opt")
+ln_native_opt = native_ln_opt_mod.run
+
+native_ln_exp_mod = load("ln_exp")
+ln_native_exp = native_ln_exp_mod.run
+
+ +
+
+
+ +
+
+ + +
+ +
+ + +
+
running build_ext
+building 'ln' extension
+C compiler: /usr/bin/clang -fno-strict-aliasing -Wsign-compare -Wunreachable-code -fno-common -dynamic -DNDEBUG -g -fwrapv -O3 -Wall -Wstrict-prototypes -arch i386 -arch x86_64 -g
+
+compile options: '-I/Users/uweschmitt/Projects/hope/venv3/lib/python3.5/site-packages/numpy/core/include -I/Users/uweschmitt/Projects/hope/venv3/include -I/Library/Frameworks/Python.framework/Versions/3.5/include/python3.5m -c'
+extra options: '-Wall -Wno-unused-variable -march=native -stdlib=libc++ -std=c++11 -Wno-unreachable-code'
+clang: ././src/ln.cpp
+/usr/bin/clang++ -bundle -undefined dynamic_lookup -arch i386 -arch x86_64 -g ./src/ln.o -o ./ln.cpython-35m-darwin.so
+
+running build_ext
+building 'ln_opt' extension
+C compiler: /usr/bin/clang -fno-strict-aliasing -Wsign-compare -Wunreachable-code -fno-common -dynamic -DNDEBUG -g -fwrapv -O3 -Wall -Wstrict-prototypes -arch i386 -arch x86_64 -g
+
+compile options: '-I/Users/uweschmitt/Projects/hope/venv3/lib/python3.5/site-packages/numpy/core/include -I/Users/uweschmitt/Projects/hope/venv3/include -I/Library/Frameworks/Python.framework/Versions/3.5/include/python3.5m -c'
+extra options: '-Wall -Wno-unused-variable -march=native -stdlib=libc++ -std=c++11 -Wno-unreachable-code'
+clang: ././src/ln_opt.cpp
+/usr/bin/clang++ -bundle -undefined dynamic_lookup -arch i386 -arch x86_64 -g ./src/ln_opt.o -o ./ln_opt.cpython-35m-darwin.so
+
+running build_ext
+building 'ln_exp' extension
+C compiler: /usr/bin/clang -fno-strict-aliasing -Wsign-compare -Wunreachable-code -fno-common -dynamic -DNDEBUG -g -fwrapv -O3 -Wall -Wstrict-prototypes -arch i386 -arch x86_64 -g
+
+compile options: '-I/Users/uweschmitt/Projects/hope/venv3/lib/python3.5/site-packages/numpy/core/include -I/Users/uweschmitt/Projects/hope/venv3/include -I/Library/Frameworks/Python.framework/Versions/3.5/include/python3.5m -c'
+extra options: '-Wall -Wno-unused-variable -march=native -stdlib=libc++ -std=c++11 -Wno-unreachable-code'
+clang: ././src/ln_exp.cpp
+/usr/bin/clang++ -bundle -undefined dynamic_lookup -arch i386 -arch x86_64 -g ./src/ln_exp.o -o ./ln_exp.cpython-35m-darwin.so
+
+
+
+
+ +
+
+ +
+
+
+
In [11]:
+
+
+
# Hope version - optimized
+
+import hope
+hope.config.optimize = True
+
+@hope.jit
+def ln_hope_opt(X, Y):
+    Y[:] = (X-1) - (X-1)**2 / 2 + (X-1)**3 / 3 - (X-1)**4 / 4 + (X-1)**5 / 5 - (X-1)**6 / 6 + (X-1)**7 / 7 - (X-1)**8 / 8 + (X-1)**9 / 9
+
+
+hope.config.optimize = False
+
+ +
+
+
+ +
+
+
+
In [12]:
+
+
+
import numpy as np
+
+X = np.random.random(10000).astype(np.float64)
+Y = np.ones_like(X)
+
+ +
+
+
+ +
+
+
+
In [13]:
+
+
+
Y1 = np.ones_like(X)
+Y2 = np.ones_like(X)
+Y3 = np.ones_like(X)
+Y4 = np.ones_like(X)
+Y5 = np.ones_like(X)
+Y6 = np.ones_like(X)
+Y7 = np.ones_like(X)
+Y8 = np.ones_like(X)
+Y9 = np.ones_like(X)
+Y10 = np.ones_like(X)
+Y11 = np.ones_like(X)
+Y12 = np.ones_like(X)
+Y13 = np.ones_like(X)
+
+ln_python(X, Y1)
+ln_python_exp(X, Y2)
+ln_cython(X, Y3)
+ln_cython_exp(X, Y4)
+ln_numexpr(X, Y5)
+ln_hope(X, Y6)
+ln_hope_exp(X, Y7)
+ln_numba(X, Y8)
+ln_numba_exp(X, Y9)
+ln_native(X, Y10)
+ln_native_opt(X, Y11)
+ln_native_exp(X, Y12)
+ln_hope_opt(X, Y13)
+
+assert np.allclose(Y1,Y2, 1E-10)
+assert np.allclose(Y1,Y3, 1E-10)
+assert np.allclose(Y1,Y4, 1E-10)
+assert np.allclose(Y1,Y5, 1E-10)
+assert np.allclose(Y1,Y6, 1E-10)
+assert np.allclose(Y1,Y7, 1E-10)
+assert np.allclose(Y1,Y8, 1E-10)
+assert np.allclose(Y1,Y9, 1E-10)
+assert np.allclose(Y1,Y10, 1E-10)
+assert np.allclose(Y1,Y11, 1E-10)
+assert np.allclose(Y1,Y12, 1E-10)
+assert np.allclose(Y1,Y13, 1E-10)
+
+ +
+
+
+ +
+
+ + +
+ +
+ + +
+
Compiling following functions:
+ln_hope(float64^1 X, float64^1 Y)
+running build_ext
+building 'ln_hope_f15670420910980a71b044ad212142e5c0bffbce3030fa3603b54df8_0' extension
+C compiler: /usr/bin/clang -fno-strict-aliasing -Wsign-compare -Wunreachable-code -fno-common -dynamic -DNDEBUG -g -fwrapv -O3 -Wall -arch i386 -arch x86_64 -g
+
+compile options: '-I/Users/uweschmitt/Projects/hope/venv3/lib/python3.5/site-packages/numpy/core/include -I/Users/uweschmitt/Projects/hope/venv3/include -I/Library/Frameworks/Python.framework/Versions/3.5/include/python3.5m -c'
+extra options: '-Wall -Wno-unused-variable -march=native -stdlib=libc++ -std=c++11 -Wno-unreachable-code'
+clang: /var/folders/k8/zfp7dvcs1m326gz1brql1tv80000gn/T/hopeh55im_15/ln_hope_f15670420910980a71b044ad212142e5c0bffbce3030fa3603b54df8_0.cpp
+/usr/bin/clang++ -bundle -undefined dynamic_lookup -arch i386 -arch x86_64 -g /var/folders/k8/zfp7dvcs1m326gz1brql1tv80000gn/T/hopeh55im_15/ln_hope_f15670420910980a71b044ad212142e5c0bffbce3030fa3603b54df8_0.o -o /var/folders/k8/zfp7dvcs1m326gz1brql1tv80000gn/T/hopeh55im_15/ln_hope_f15670420910980a71b044ad212142e5c0bffbce3030fa3603b54df8_0.cpython-35m-darwin.so
+
+Compiling following functions:
+ln_hope_exp(float64^1 X, float64^1 Y)
+running build_ext
+building 'ln_hope_exp_b65cbfbe0daf1548da7f56b10a2288d21522ef64767c336874bf9997_0' extension
+C compiler: /usr/bin/clang -fno-strict-aliasing -Wsign-compare -Wunreachable-code -fno-common -dynamic -DNDEBUG -g -fwrapv -O3 -Wall -arch i386 -arch x86_64 -g
+
+compile options: '-I/Users/uweschmitt/Projects/hope/venv3/lib/python3.5/site-packages/numpy/core/include -I/Users/uweschmitt/Projects/hope/venv3/include -I/Library/Frameworks/Python.framework/Versions/3.5/include/python3.5m -c'
+extra options: '-Wall -Wno-unused-variable -march=native -stdlib=libc++ -std=c++11 -Wno-unreachable-code'
+clang: /var/folders/k8/zfp7dvcs1m326gz1brql1tv80000gn/T/hopehdsxcvb2/ln_hope_exp_b65cbfbe0daf1548da7f56b10a2288d21522ef64767c336874bf9997_0.cpp
+/usr/bin/clang++ -bundle -undefined dynamic_lookup -arch i386 -arch x86_64 -g /var/folders/k8/zfp7dvcs1m326gz1brql1tv80000gn/T/hopehdsxcvb2/ln_hope_exp_b65cbfbe0daf1548da7f56b10a2288d21522ef64767c336874bf9997_0.o -o /var/folders/k8/zfp7dvcs1m326gz1brql1tv80000gn/T/hopehdsxcvb2/ln_hope_exp_b65cbfbe0daf1548da7f56b10a2288d21522ef64767c336874bf9997_0.cpython-35m-darwin.so
+
+Compiling following functions:
+ln_hope_opt(float64^1 X, float64^1 Y)
+running build_ext
+building 'ln_hope_opt_e61495c068774ff7cc57eca53040c8172f643a2ae5e21fb9ddd84b2c_0' extension
+C compiler: /usr/bin/clang -fno-strict-aliasing -Wsign-compare -Wunreachable-code -fno-common -dynamic -DNDEBUG -g -fwrapv -O3 -Wall -arch i386 -arch x86_64 -g
+
+compile options: '-I/Users/uweschmitt/Projects/hope/venv3/lib/python3.5/site-packages/numpy/core/include -I/Users/uweschmitt/Projects/hope/venv3/include -I/Library/Frameworks/Python.framework/Versions/3.5/include/python3.5m -c'
+extra options: '-Wall -Wno-unused-variable -march=native -stdlib=libc++ -std=c++11 -Wno-unreachable-code'
+clang: /var/folders/k8/zfp7dvcs1m326gz1brql1tv80000gn/T/hopeqyup473d/ln_hope_opt_e61495c068774ff7cc57eca53040c8172f643a2ae5e21fb9ddd84b2c_0.cpp
+/usr/bin/clang++ -bundle -undefined dynamic_lookup -arch i386 -arch x86_64 -g /var/folders/k8/zfp7dvcs1m326gz1brql1tv80000gn/T/hopeqyup473d/ln_hope_opt_e61495c068774ff7cc57eca53040c8172f643a2ae5e21fb9ddd84b2c_0.o -o /var/folders/k8/zfp7dvcs1m326gz1brql1tv80000gn/T/hopeqyup473d/ln_hope_opt_e61495c068774ff7cc57eca53040c8172f643a2ae5e21fb9ddd84b2c_0.cpython-35m-darwin.so
+
+
+
+
+ +
+
+ +
+
+
+
In [14]:
+
+
+
import numexpr as ne
+
+print("python")
+%timeit ln_python(X, Y)
+%timeit ln_python_exp(X, Y)
+print("numexpr (1)")
+ne.set_num_threads(1)
+%timeit ln_numexpr(X, Y)
+print("numexpr ({0})".format(ne.detect_number_of_cores()))
+ne.set_num_threads(ne.detect_number_of_cores())
+%timeit ln_numexpr(X, Y)
+print("hope")
+%timeit ln_hope(X, Y)
+%timeit ln_hope_exp(X, Y)
+%timeit ln_hope_opt(X, Y)
+print("cython")
+%timeit ln_cython(X, Y)
+%timeit ln_cython_exp(X, Y)
+print("numba")
+%timeit ln_numba(X, Y)
+%timeit ln_numba_exp(X, Y)
+print("native")
+%timeit ln_native(X, Y)
+%timeit ln_native_opt(X, Y)
+%timeit ln_native_exp(X, Y)
+
+ +
+
+
+ +
+
+ + +
+ +
+ + +
+
python
+2.66 ms ± 42.8 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)
+324 µs ± 10.8 µs per loop (mean ± std. dev. of 7 runs, 1000 loops each)
+numexpr (1)
+457 µs ± 13.9 µs per loop (mean ± std. dev. of 7 runs, 1000 loops each)
+numexpr (8)
+207 µs ± 6.11 µs per loop (mean ± std. dev. of 7 runs, 1000 loops each)
+hope
+2.42 ms ± 86.2 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)
+122 µs ± 1.48 µs per loop (mean ± std. dev. of 7 runs, 10000 loops each)
+2.4 ms ± 67.7 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)
+cython
+2.34 ms ± 48.7 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)
+121 µs ± 2.79 µs per loop (mean ± std. dev. of 7 runs, 10000 loops each)
+numba
+128 µs ± 1.36 µs per loop (mean ± std. dev. of 7 runs, 10000 loops each)
+271 µs ± 4.42 µs per loop (mean ± std. dev. of 7 runs, 1000 loops each)
+native
+2.33 ms ± 43 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)
+74 µs ± 600 ns per loop (mean ± std. dev. of 7 runs, 10000 loops each)
+122 µs ± 995 ns per loop (mean ± std. dev. of 7 runs, 10000 loops each)
+
+
+
+ +
+
+ +
+
+
+
In [15]:
+
+
+
from util import perf_comp_data
+
+ +
+
+
+ +
+
+
+
In [16]:
+
+
+
func_list = ["ln_python", "ln_python_exp", 
+             "ln_numexpr", 
+             "ln_hope", "ln_hope_exp", "ln_hope_opt", 
+             "ln_cython", "ln_cython_exp", 
+             "ln_numba", "ln_numba_exp", 
+             "ln_native", "ln_native_opt", "ln_native_exp", ]
+perf_comp_data(func_list,
+               len(func_list)*["X, Y"], rep=100)
+
+ +
+
+
+ +
+
+ + +
+ +
+ + +
+
function: ln_native_opt       , av. time sec:   0.00006836, min. time sec:   0.00006820, relative:       1.0
+function: ln_cython_exp       , av. time sec:   0.00010528, min. time sec:   0.00010499, relative:       1.5
+function: ln_native_exp       , av. time sec:   0.00011371, min. time sec:   0.00011366, relative:       1.7
+function: ln_hope_exp         , av. time sec:   0.00011394, min. time sec:   0.00011386, relative:       1.7
+function: ln_numba            , av. time sec:   0.00011907, min. time sec:   0.00011892, relative:       1.7
+function: ln_numexpr          , av. time sec:   0.00024022, min. time sec:   0.00017358, relative:       3.5
+function: ln_numba_exp        , av. time sec:   0.00025819, min. time sec:   0.00025539, relative:       3.8
+function: ln_python_exp       , av. time sec:   0.00027077, min. time sec:   0.00026273, relative:       4.0
+function: ln_cython           , av. time sec:   0.00215541, min. time sec:   0.00191540, relative:      31.5
+function: ln_native           , av. time sec:   0.00223247, min. time sec:   0.00220383, relative:      32.7
+function: ln_hope             , av. time sec:   0.00226024, min. time sec:   0.00220324, relative:      33.1
+function: ln_hope_opt         , av. time sec:   0.00227007, min. time sec:   0.00220347, relative:      33.2
+function: ln_python           , av. time sec:   0.00232935, min. time sec:   0.00224894, relative:      34.1
+
+
+
+ +
+
+ +
+
+
+
In [17]:
+
+
+
 
+
+ +
+
+
+ +
+
+
+ + + + + + diff --git a/benchmarks/numexpr.ipynb b/benchmarks/numexpr.ipynb index 912c17b..da1fa20 100644 --- a/benchmarks/numexpr.ipynb +++ b/benchmarks/numexpr.ipynb @@ -2,17 +2,9 @@ "cells": [ { "cell_type": "code", - "execution_count": 3, + "execution_count": 1, "metadata": {}, "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "The version_information extension is already loaded. To reload it, use:\n", - " %reload_ext version_information\n" - ] - }, { "data": { "application/json": { @@ -52,7 +44,7 @@ ] }, "text/html": [ - "
SoftwareVersion
Python3.5.1 64bit [GCC 4.2.1 (Apple Inc. build 5666) (dot 3)]
IPython6.1.0
OSDarwin 15.6.0 x86_64 i386 64bit
numpy1.13.1
Cython0.26
numba0.34.0
hope0.6.1
numexpr2.6.2
Tue Aug 29 17:25:19 2017 CEST
" + "
SoftwareVersion
Python3.5.1 64bit [GCC 4.2.1 (Apple Inc. build 5666) (dot 3)]
IPython6.1.0
OSDarwin 15.6.0 x86_64 i386 64bit
numpy1.13.1
Cython0.26
numba0.34.0
hope0.6.1
numexpr2.6.2
Mon Sep 04 15:24:42 2017 CEST
" ], "text/latex": [ "\\begin{tabular}{|l|l|}\\hline\n", @@ -65,7 +57,7 @@ "numba & 0.34.0 \\\\ \\hline\n", "hope & 0.6.1 \\\\ \\hline\n", "numexpr & 2.6.2 \\\\ \\hline\n", - "\\hline \\multicolumn{2}{|l|}{Tue Aug 29 17:25:19 2017 CEST} \\\\ \\hline\n", + "\\hline \\multicolumn{2}{|l|}{Mon Sep 04 15:24:42 2017 CEST} \\\\ \\hline\n", "\\end{tabular}\n" ], "text/plain": [ @@ -78,10 +70,10 @@ "numba 0.34.0\n", "hope 0.6.1\n", "numexpr 2.6.2\n", - "Tue Aug 29 17:25:19 2017 CEST" + "Mon Sep 04 15:24:42 2017 CEST" ] }, - "execution_count": 3, + "execution_count": 1, "metadata": {}, "output_type": "execute_result" } @@ -104,7 +96,7 @@ }, { "cell_type": "code", - "execution_count": 4, + "execution_count": 2, "metadata": { "collapsed": true, "slideshow": { @@ -121,7 +113,7 @@ }, { "cell_type": "code", - "execution_count": 5, + "execution_count": 3, "metadata": { "collapsed": true, "slideshow": { @@ -143,7 +135,7 @@ }, { "cell_type": "code", - "execution_count": 6, + "execution_count": 4, "metadata": { "collapsed": true, "slideshow": { @@ -159,13 +151,22 @@ }, { "cell_type": "code", - "execution_count": 7, + "execution_count": 5, "metadata": { "slideshow": { "slide_type": "slide" } }, - "outputs": [], + "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + "/Users/uweschmitt/Projects/hope/hope/jit.py:112: UserWarning: Recompiling... Reason: State is inconsistent with config. Inconsistent state key: [optimize].\n", + " warnings.warn(\"Recompiling... Reason: {0}\".format(le))\n" + ] + } + ], "source": [ "# Hope version\n", "@hope.jit\n", @@ -175,7 +176,7 @@ }, { "cell_type": "code", - "execution_count": 8, + "execution_count": 6, "metadata": { "collapsed": true, "slideshow": { @@ -200,7 +201,7 @@ }, { "cell_type": "code", - "execution_count": 9, + "execution_count": 7, "metadata": { "collapsed": true, "slideshow": { @@ -218,7 +219,7 @@ }, { "cell_type": "code", - "execution_count": 10, + "execution_count": 8, "metadata": { "collapsed": true, "slideshow": { @@ -243,32 +244,11 @@ }, { "cell_type": "code", - "execution_count": 12, - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "In file included from /Users/uweschmitt/.ipython/cython/_cython_magic_c0a8dbdf5333e81ffeb4f99decde5aac.c:495:\n", - "In file included from /Users/uweschmitt/Projects/hope/venv3/lib/python3.5/site-packages/numpy/core/include/numpy/arrayobject.h:4:\n", - "In file included from /Users/uweschmitt/Projects/hope/venv3/lib/python3.5/site-packages/numpy/core/include/numpy/ndarrayobject.h:18:\n", - "In file included from /Users/uweschmitt/Projects/hope/venv3/lib/python3.5/site-packages/numpy/core/include/numpy/ndarraytypes.h:1809:\n", - "/Users/uweschmitt/Projects/hope/venv3/lib/python3.5/site-packages/numpy/core/include/numpy/npy_1_7_deprecated_api.h:15:2: warning: \"Using deprecated NumPy API, disable it by \" \"#defining NPY_NO_DEPRECATED_API NPY_1_7_API_VERSION\" [-W#warnings]\n", - "#warning \"Using deprecated NumPy API, disable it by \" \\\n", - " ^\n", - "1 warning generated.\n", - "In file included from /Users/uweschmitt/.ipython/cython/_cython_magic_c0a8dbdf5333e81ffeb4f99decde5aac.c:495:\n", - "In file included from /Users/uweschmitt/Projects/hope/venv3/lib/python3.5/site-packages/numpy/core/include/numpy/arrayobject.h:4:\n", - "In file included from /Users/uweschmitt/Projects/hope/venv3/lib/python3.5/site-packages/numpy/core/include/numpy/ndarrayobject.h:18:\n", - "In file included from /Users/uweschmitt/Projects/hope/venv3/lib/python3.5/site-packages/numpy/core/include/numpy/ndarraytypes.h:1809:\n", - "/Users/uweschmitt/Projects/hope/venv3/lib/python3.5/site-packages/numpy/core/include/numpy/npy_1_7_deprecated_api.h:15:2: warning: \"Using deprecated NumPy API, disable it by \" \"#defining NPY_NO_DEPRECATED_API NPY_1_7_API_VERSION\" [-W#warnings]\n", - "#warning \"Using deprecated NumPy API, disable it by \" \\\n", - " ^\n", - "1 warning generated.\n" - ] - } - ], + "execution_count": 9, + "metadata": { + "collapsed": true + }, + "outputs": [], "source": [ "%%cython\n", "\n", @@ -281,23 +261,37 @@ "@cython.boundscheck(False)\n", "@cython.wraparound(False)\n", "def ln_cython(np.ndarray[np.double_t, ndim=1] X, np.ndarray[np.double_t, ndim=1] Y):\n", - " Y[:] = (X-1) - (X-1)**2 / 2 + (X-1)**3 / 3 - (X-1)**4 / 4 + (X-1)**5 / 5 - (X-1)**6 / 6 + (X-1)**7 / 7 - (X-1)**8 / 8 + (X-1)**9 / 9\n", + " cdef size_t i, n\n", + " cdef double x\n", + " n = len(X)\n", + " cdef double *xx = X.data\n", + " cdef double *yy = Y.data\n", + " for i in range(n):\n", + " x = xx[i] \n", + " yy[i] = (x-1) - (x-1)**2 / 2 + (x-1)**3 / 3 - (x-1)**4 / 4 + (x-1)**5 / 5 - (x-1)**6 / 6 + (x-1)**7 / 7 - (x-1)**8 / 8 + (x-1)**9 / 9\n", "\n", " \n", "@cython.boundscheck(False)\n", "@cython.wraparound(False)\n", "def ln_cython_exp(np.ndarray[np.float_t, ndim=1] X, np.ndarray[np.float_t, ndim=1] Y):\n", - " cdef np.ndarray[np.double_t, ndim=1] x = (X - 1)\n", - " cdef np.ndarray[np.double_t, ndim=1] x2 = x*x\n", - " cdef np.ndarray[np.double_t, ndim=1] x4 = x2*x2\n", - " cdef np.ndarray[np.double_t, ndim=1] x6 = x4*x2\n", - " cdef np.ndarray[np.double_t, ndim=1] x8 = x4*x4\n", - " Y[:] = x - x2 / 2 + x * x2 / 3 - x4 / 4 + x * x4 / 5 - x6 / 6 + x6 * x / 7 - x8 / 8 + x8 * x / 9" + " cdef size_t i, n\n", + " cdef double x, x2, x4, x6, x8\n", + " cdef double *xx = X.data\n", + " cdef double *yy = Y.data\n", + " n = len(X)\n", + " for i in range(n):\n", + " x = xx[i] - 1\n", + " x2 = x * x\n", + " x4 = x2 * x2\n", + " x6 = x2 * x4\n", + " x8 = x4 * x4\n", + " yy[i] = x - x2 / 2 + x * x2 / 3 - x4 / 4 + x * x4 / 5 - x6 / 6 + x6 * x / 7 - x8 / 8 + x8 * x / 9 \n", + " " ] }, { "cell_type": "code", - "execution_count": 13, + "execution_count": 10, "metadata": {}, "outputs": [ { @@ -311,30 +305,27 @@ "compile options: '-I/Users/uweschmitt/Projects/hope/venv3/lib/python3.5/site-packages/numpy/core/include -I/Users/uweschmitt/Projects/hope/venv3/include -I/Library/Frameworks/Python.framework/Versions/3.5/include/python3.5m -c'\n", "extra options: '-Wall -Wno-unused-variable -march=native -stdlib=libc++ -std=c++11 -Wno-unreachable-code'\n", "clang: ././src/ln.cpp\n", - "././src/ln.cpp:105:9: error: use of undeclared identifier 'Py_InitModule'\n", - " (void)Py_InitModule(\"ln\", ln_hopeMethods);\n", - " ^\n", - "1 error generated.\n", - "././src/ln.cpp:105:9: error: use of undeclared identifier 'Py_InitModule'\n", - " (void)Py_InitModule(\"ln\", ln_hopeMethods);\n", - " ^\n", - "1 error generated.\n", - "error: Command \"/usr/bin/clang -fno-strict-aliasing -Wsign-compare -Wunreachable-code -fno-common -dynamic -DNDEBUG -g -fwrapv -O3 -Wall -Wstrict-prototypes -arch i386 -arch x86_64 -g -I/Users/uweschmitt/Projects/hope/venv3/lib/python3.5/site-packages/numpy/core/include -I/Users/uweschmitt/Projects/hope/venv3/include -I/Library/Frameworks/Python.framework/Versions/3.5/include/python3.5m -c ././src/ln.cpp -o ./src/ln.o -Wall -Wno-unused-variable -march=native -stdlib=libc++ -std=c++11 -Wno-unreachable-code\" failed with exit status 1537\n", + "/usr/bin/clang++ -bundle -undefined dynamic_lookup -arch i386 -arch x86_64 -g ./src/ln.o -o ./ln.cpython-35m-darwin.so\n", + "\n", + "running build_ext\n", + "building 'ln_opt' extension\n", + "C compiler: /usr/bin/clang -fno-strict-aliasing -Wsign-compare -Wunreachable-code -fno-common -dynamic -DNDEBUG -g -fwrapv -O3 -Wall -Wstrict-prototypes -arch i386 -arch x86_64 -g\n", + "\n", + "compile options: '-I/Users/uweschmitt/Projects/hope/venv3/lib/python3.5/site-packages/numpy/core/include -I/Users/uweschmitt/Projects/hope/venv3/include -I/Library/Frameworks/Python.framework/Versions/3.5/include/python3.5m -c'\n", + "extra options: '-Wall -Wno-unused-variable -march=native -stdlib=libc++ -std=c++11 -Wno-unreachable-code'\n", + "clang: ././src/ln_opt.cpp\n", + "/usr/bin/clang++ -bundle -undefined dynamic_lookup -arch i386 -arch x86_64 -g ./src/ln_opt.o -o ./ln_opt.cpython-35m-darwin.so\n", + "\n", + "running build_ext\n", + "building 'ln_exp' extension\n", + "C compiler: /usr/bin/clang -fno-strict-aliasing -Wsign-compare -Wunreachable-code -fno-common -dynamic -DNDEBUG -g -fwrapv -O3 -Wall -Wstrict-prototypes -arch i386 -arch x86_64 -g\n", + "\n", + "compile options: '-I/Users/uweschmitt/Projects/hope/venv3/lib/python3.5/site-packages/numpy/core/include -I/Users/uweschmitt/Projects/hope/venv3/include -I/Library/Frameworks/Python.framework/Versions/3.5/include/python3.5m -c'\n", + "extra options: '-Wall -Wno-unused-variable -march=native -stdlib=libc++ -std=c++11 -Wno-unreachable-code'\n", + "clang: ././src/ln_exp.cpp\n", + "/usr/bin/clang++ -bundle -undefined dynamic_lookup -arch i386 -arch x86_64 -g ./src/ln_exp.o -o ./ln_exp.cpython-35m-darwin.so\n", "\n" ] - }, - { - "ename": "Exception", - "evalue": "Error compiling function ./src/ln (compiled to ./)", - "output_type": "error", - "traceback": [ - "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m", - "\u001b[0;31mException\u001b[0m Traceback (most recent call last)", - "\u001b[0;32m\u001b[0m in \u001b[0;36m\u001b[0;34m()\u001b[0m\n\u001b[1;32m 1\u001b[0m \u001b[0;32mfrom\u001b[0m \u001b[0mnative_util\u001b[0m \u001b[0;32mimport\u001b[0m \u001b[0mload\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m----> 2\u001b[0;31m \u001b[0mnative_ln_mod\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mload\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m\"ln\"\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 3\u001b[0m \u001b[0mln_native\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mnative_ln_mod\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mrun\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 4\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 5\u001b[0m \u001b[0mnative_ln_opt_mod\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mload\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m\"ln_opt\"\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n", - "\u001b[0;32m~/Projects/hope/benchmarks/native_util.py\u001b[0m in \u001b[0;36mload\u001b[0;34m(name)\u001b[0m\n\u001b[1;32m 19\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 20\u001b[0m \u001b[0;32mdef\u001b[0m \u001b[0mload\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mname\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m---> 21\u001b[0;31m \u001b[0mcompile\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mname\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0;34m\"./src\"\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 22\u001b[0m \u001b[0mmodule\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mimportlib\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mimport_module\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mname\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 23\u001b[0m \u001b[0;31m# module = __import__(name, globals(), locals(), [], -1)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n", - "\u001b[0;32m~/Projects/hope/benchmarks/native_util.py\u001b[0m in \u001b[0;36mcompile\u001b[0;34m(name, src_folder, target_folder)\u001b[0m\n\u001b[1;32m 98\u001b[0m \u001b[0;32mif\u001b[0m \u001b[0;32mnot\u001b[0m \u001b[0mmodules\u001b[0m \u001b[0;32mor\u001b[0m \u001b[0mout\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mfind\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m\"error:\"\u001b[0m\u001b[0;34m)\u001b[0m \u001b[0;34m>\u001b[0m \u001b[0;34m-\u001b[0m\u001b[0;36m1\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 99\u001b[0m \u001b[0mprint\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mout\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m--> 100\u001b[0;31m \u001b[0;32mraise\u001b[0m \u001b[0mException\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m\"Error compiling function {0} (compiled to {1})\"\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mformat\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mlocalfilename\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mtarget_folder\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m 101\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m 102\u001b[0m \u001b[0;32mif\u001b[0m \u001b[0mout\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mfind\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m\"warning:\"\u001b[0m\u001b[0;34m)\u001b[0m \u001b[0;34m>\u001b[0m \u001b[0;34m-\u001b[0m\u001b[0;36m1\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n", - "\u001b[0;31mException\u001b[0m: Error compiling function ./src/ln (compiled to ./)" - ] } ], "source": [ @@ -351,14 +342,22 @@ }, { "cell_type": "code", - "execution_count": 13, + "execution_count": 11, "metadata": { - "collapsed": true, "slideshow": { "slide_type": "notes" } }, - "outputs": [], + "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + "/Users/uweschmitt/Projects/hope/hope/jit.py:112: UserWarning: Recompiling... Reason: State is inconsistent with config. Inconsistent state key: [optimize].\n", + " warnings.warn(\"Recompiling... Reason: {0}\".format(le))\n" + ] + } + ], "source": [ "# Hope version - optimized\n", "\n", @@ -375,7 +374,7 @@ }, { "cell_type": "code", - "execution_count": 15, + "execution_count": 12, "metadata": { "collapsed": true, "slideshow": { @@ -392,11 +391,38 @@ }, { "cell_type": "code", - "execution_count": 17, - "metadata": { - "collapsed": true - }, - "outputs": [], + "execution_count": 13, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Compiling following functions:\n", + "ln_hope(float64^1 X, float64^1 Y)\n", + "running build_ext\n", + "building 'ln_hope_f15670420910980a71b044ad212142e5c0bffbce3030fa3603b54df8_0' extension\n", + "C compiler: /usr/bin/clang -fno-strict-aliasing -Wsign-compare -Wunreachable-code -fno-common -dynamic -DNDEBUG -g -fwrapv -O3 -Wall -arch i386 -arch x86_64 -g\n", + "\n", + "compile options: '-I/Users/uweschmitt/Projects/hope/venv3/lib/python3.5/site-packages/numpy/core/include -I/Users/uweschmitt/Projects/hope/venv3/include -I/Library/Frameworks/Python.framework/Versions/3.5/include/python3.5m -c'\n", + "extra options: '-Wall -Wno-unused-variable -march=native -stdlib=libc++ -std=c++11 -Wno-unreachable-code'\n", + "clang: /var/folders/k8/zfp7dvcs1m326gz1brql1tv80000gn/T/hope8ltuei58/ln_hope_f15670420910980a71b044ad212142e5c0bffbce3030fa3603b54df8_0.cpp\n", + "/usr/bin/clang++ -bundle -undefined dynamic_lookup -arch i386 -arch x86_64 -g /var/folders/k8/zfp7dvcs1m326gz1brql1tv80000gn/T/hope8ltuei58/ln_hope_f15670420910980a71b044ad212142e5c0bffbce3030fa3603b54df8_0.o -o /var/folders/k8/zfp7dvcs1m326gz1brql1tv80000gn/T/hope8ltuei58/ln_hope_f15670420910980a71b044ad212142e5c0bffbce3030fa3603b54df8_0.cpython-35m-darwin.so\n", + "\n", + "Compiling following functions:\n", + "ln_hope_opt(float64^1 X, float64^1 Y)\n", + "running build_ext\n", + "building 'ln_hope_opt_e61495c068774ff7cc57eca53040c8172f643a2ae5e21fb9ddd84b2c_0' extension\n", + "C compiler: /usr/bin/clang -fno-strict-aliasing -Wsign-compare -Wunreachable-code -fno-common -dynamic -DNDEBUG -g -fwrapv -O3 -Wall -arch i386 -arch x86_64 -g\n", + "\n", + "compile options: '-I/Users/uweschmitt/Projects/hope/venv3/lib/python3.5/site-packages/numpy/core/include -I/Users/uweschmitt/Projects/hope/venv3/include -I/Library/Frameworks/Python.framework/Versions/3.5/include/python3.5m -c'\n", + "extra options: '-Wall -Wno-unused-variable -march=native -stdlib=libc++ -std=c++11 -Wno-unreachable-code'\n", + "clang: /var/folders/k8/zfp7dvcs1m326gz1brql1tv80000gn/T/hopehhalgg0h/ln_hope_opt_e61495c068774ff7cc57eca53040c8172f643a2ae5e21fb9ddd84b2c_0.cpp\n", + "/usr/bin/clang++ -bundle -undefined dynamic_lookup -arch i386 -arch x86_64 -g /var/folders/k8/zfp7dvcs1m326gz1brql1tv80000gn/T/hopehhalgg0h/ln_hope_opt_e61495c068774ff7cc57eca53040c8172f643a2ae5e21fb9ddd84b2c_0.o -o /var/folders/k8/zfp7dvcs1m326gz1brql1tv80000gn/T/hopehhalgg0h/ln_hope_opt_e61495c068774ff7cc57eca53040c8172f643a2ae5e21fb9ddd84b2c_0.cpython-35m-darwin.so\n", + "\n" + ] + } + ], "source": [ "Y1 = np.ones_like(X)\n", "Y2 = np.ones_like(X)\n", @@ -442,7 +468,7 @@ }, { "cell_type": "code", - "execution_count": 18, + "execution_count": 14, "metadata": { "slideshow": { "slide_type": "slide" @@ -454,52 +480,52 @@ "output_type": "stream", "text": [ "python\n", - "100 loops, best of 3: 2.4 ms per loop\n", - "1000 loops, best of 3: 299 µs per loop\n", + "2.46 ms ± 25.3 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)\n", + "304 µs ± 12 µs per loop (mean ± std. dev. of 7 runs, 1000 loops each)\n", "numexpr (1)\n", - "1000 loops, best of 3: 400 µs per loop\n", + "442 µs ± 7.12 µs per loop (mean ± std. dev. of 7 runs, 1000 loops each)\n", "numexpr (8)\n", - "1000 loops, best of 3: 200 µs per loop\n", + "211 µs ± 7.66 µs per loop (mean ± std. dev. of 7 runs, 1000 loops each)\n", "hope\n", - "10000 loops, best of 3: 128 µs per loop\n", - "1000 loops, best of 3: 212 µs per loop\n", - "10000 loops, best of 3: 128 µs per loop\n", + "2.37 ms ± 59.9 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)\n", + "124 µs ± 2.12 µs per loop (mean ± std. dev. of 7 runs, 10000 loops each)\n", + "2.45 ms ± 24.6 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)\n", "cython\n", - "100 loops, best of 3: 2.4 ms per loop\n", - "1000 loops, best of 3: 305 µs per loop\n", + "2.24 ms ± 87.3 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)\n", + "120 µs ± 3.78 µs per loop (mean ± std. dev. of 7 runs, 10000 loops each)\n", "numba\n", - "100 loops, best of 3: 2.41 ms per loop\n", - "1000 loops, best of 3: 305 µs per loop\n", + "130 µs ± 1.64 µs per loop (mean ± std. dev. of 7 runs, 10000 loops each)\n", + "282 µs ± 3.52 µs per loop (mean ± std. dev. of 7 runs, 1000 loops each)\n", "native\n", - "100 loops, best of 3: 2.12 ms per loop\n", - "10000 loops, best of 3: 128 µs per loop\n", - "10000 loops, best of 3: 106 µs per loop\n" + "2.4 ms ± 73.5 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)\n", + "74 µs ± 1.19 µs per loop (mean ± std. dev. of 7 runs, 10000 loops each)\n", + "122 µs ± 1.52 µs per loop (mean ± std. dev. of 7 runs, 10000 loops each)\n" ] } ], "source": [ "import numexpr as ne\n", "\n", - "print \"python\"\n", + "print(\"python\")\n", "%timeit ln_python(X, Y)\n", "%timeit ln_python_exp(X, Y)\n", - "print \"numexpr (1)\"\n", + "print(\"numexpr (1)\")\n", "ne.set_num_threads(1)\n", "%timeit ln_numexpr(X, Y)\n", - "print \"numexpr ({0})\".format(ne.detect_number_of_cores())\n", + "print(\"numexpr ({0})\".format(ne.detect_number_of_cores()))\n", "ne.set_num_threads(ne.detect_number_of_cores())\n", "%timeit ln_numexpr(X, Y)\n", - "print \"hope\"\n", + "print(\"hope\")\n", "%timeit ln_hope(X, Y)\n", "%timeit ln_hope_exp(X, Y)\n", "%timeit ln_hope_opt(X, Y)\n", - "print \"cython\"\n", + "print(\"cython\")\n", "%timeit ln_cython(X, Y)\n", "%timeit ln_cython_exp(X, Y)\n", - "print \"numba\"\n", + "print(\"numba\")\n", "%timeit ln_numba(X, Y)\n", "%timeit ln_numba_exp(X, Y)\n", - "print \"native\"\n", + "print(\"native\")\n", "%timeit ln_native(X, Y)\n", "%timeit ln_native_opt(X, Y)\n", "%timeit ln_native_exp(X, Y)\n" @@ -507,7 +533,7 @@ }, { "cell_type": "code", - "execution_count": 19, + "execution_count": 15, "metadata": { "collapsed": true }, @@ -518,26 +544,26 @@ }, { "cell_type": "code", - "execution_count": 20, + "execution_count": 16, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ - "function: ln_native_exp , av. time sec: 0.00010705, min. time sec: 0.00010586, relative: 1.0\n", - "function: ln_hope_opt , av. time sec: 0.00012398, min. time sec: 0.00012398, relative: 1.2\n", - "function: ln_hope , av. time sec: 0.00012422, min. time sec: 0.00012398, relative: 1.2\n", - "function: ln_native_opt , av. time sec: 0.00012803, min. time sec: 0.00012708, relative: 1.2\n", - "function: ln_hope_exp , av. time sec: 0.00020707, min. time sec: 0.00020695, relative: 1.9\n", - "function: ln_numexpr , av. time sec: 0.00021243, min. time sec: 0.00018501, relative: 2.0\n", - "function: ln_python_exp , av. time sec: 0.00029302, min. time sec: 0.00029206, relative: 2.7\n", - "function: ln_numba_exp , av. time sec: 0.00030351, min. time sec: 0.00029993, relative: 2.8\n", - "function: ln_cython_exp , av. time sec: 0.00030696, min. time sec: 0.00029683, relative: 2.9\n", - "function: ln_native , av. time sec: 0.00215054, min. time sec: 0.00208783, relative: 20.1\n", - "function: ln_cython , av. time sec: 0.00241804, min. time sec: 0.00235510, relative: 22.6\n", - "function: ln_numba , av. time sec: 0.00241804, min. time sec: 0.00237322, relative: 22.6\n", - "function: ln_python , av. time sec: 0.00243747, min. time sec: 0.00235200, relative: 22.8\n" + "function: ln_native_opt , av. time sec: 0.00006836, min. time sec: 0.00006819, relative: 1.0\n", + "function: ln_cython_exp , av. time sec: 0.00010206, min. time sec: 0.00010199, relative: 1.5\n", + "function: ln_native_exp , av. time sec: 0.00011374, min. time sec: 0.00011370, relative: 1.7\n", + "function: ln_numba , av. time sec: 0.00011900, min. time sec: 0.00011891, relative: 1.7\n", + "function: ln_hope_exp , av. time sec: 0.00013583, min. time sec: 0.00011382, relative: 2.0\n", + "function: ln_numexpr , av. time sec: 0.00018525, min. time sec: 0.00017029, relative: 2.7\n", + "function: ln_numba_exp , av. time sec: 0.00025554, min. time sec: 0.00025503, relative: 3.7\n", + "function: ln_python_exp , av. time sec: 0.00028491, min. time sec: 0.00026110, relative: 4.2\n", + "function: ln_cython , av. time sec: 0.00203083, min. time sec: 0.00191422, relative: 29.7\n", + "function: ln_hope_opt , av. time sec: 0.00220507, min. time sec: 0.00220174, relative: 32.3\n", + "function: ln_native , av. time sec: 0.00220511, min. time sec: 0.00220164, relative: 32.3\n", + "function: ln_hope , av. time sec: 0.00221205, min. time sec: 0.00220165, relative: 32.4\n", + "function: ln_python , av. time sec: 0.00238910, min. time sec: 0.00224400, relative: 34.9\n" ] } ], diff --git a/benchmarks/pairwise.html b/benchmarks/pairwise.html new file mode 100644 index 0000000..894573c --- /dev/null +++ b/benchmarks/pairwise.html @@ -0,0 +1,12201 @@ + + + +pairwise + + + + + + + + + + + + + + + + + + + + +
+
+ +
+
+
In [1]:
+
+
+
import hope
+hope.config.optimize = True
+hope.config.verbose = True
+hope.config.keeptemp = True
+import numba
+import numpy as np
+from util import perf_comp_data
+from native_util import load
+%load_ext Cython
+%load_ext version_information
+%version_information numpy, Cython, numba, hope
+
+ +
+
+
+ +
+
+ + +
+ +
Out[1]:
+ + + +
+
SoftwareVersion
Python3.5.1 64bit [GCC 4.2.1 (Apple Inc. build 5666) (dot 3)]
IPython6.1.0
OSDarwin 15.6.0 x86_64 i386 64bit
numpy1.13.1
Cython0.26
numba0.34.0
hope0.6.1
Mon Sep 04 16:43:46 2017 CEST
+
+ +
+ +
+
+ +
+
+
+
In [2]:
+
+
+
# Pure python version
+
+def pairwise_python(X, D):
+    M = X.shape[0]
+    N = X.shape[1]
+    for i in range(M):
+        for j in range(M):
+            d = 0.0
+            for k in range(N):
+                tmp = X[i, k] - X[j, k]
+                d += tmp * tmp
+            D[i, j] = np.sqrt(d)
+
+ +
+
+
+ +
+
+
+
In [3]:
+
+
+
# Numpy python version
+
+def pairwise_numpy(X, D):
+    M = X.shape[0]
+    for i in range(M):
+        D[i, :] = np.sqrt(np.sum((X[i, :] - X[:]) ** 2, axis=1))
+
+ +
+
+
+ +
+
+
+
In [4]:
+
+
+
# numba version
+@numba.jit(nopython=True)
+def pairwise_numba(X, D):
+    M = X.shape[0]
+    N = X.shape[1]
+    for i in range(M):
+        for j in range(M):
+            d = 0.0
+            for k in range(N):
+                tmp = X[i, k] - X[j, k]
+                d += tmp * tmp
+            D[i, j] = np.sqrt(d)
+
+ +
+
+
+ +
+
+
+
In [5]:
+
+
+
%%cython
+
+cimport cython
+from libc.math cimport sqrt
+
+@cython.boundscheck(False)
+@cython.wraparound(False)
+def pairwise_cython(double[:, ::1] X, double[:, ::1] D):
+    cdef size_t M = X.shape[0]
+    cdef size_t N = X.shape[1]
+    cdef double tmp, d
+    for i in range(M):
+        for j in range(M):
+            d = 0.0
+            for k in range(N):
+                tmp = X[i, k] - X[j, k]
+                d += tmp * tmp
+            D[i, j] = sqrt(d)
+
+ +
+
+
+ +
+
+
+
In [6]:
+
+
+
# hope version
+@hope.jit
+def pairwise_hope(X, D, M, N):
+    for i in range(M):
+        for j in range(M):
+            d = 0.0
+            for k in range(N):
+                tmp = X[i, k] - X[j, k]
+                d += tmp * tmp
+            D[i, j] = np.sqrt(d)
+
+ +
+
+
+ +
+
+
+
In [7]:
+
+
+
from native_util import load
+
+native_pairwise_mod = load("pairwise")
+pairwise_native = native_pairwise_mod.run
+
+ +
+
+
+ +
+
+ + +
+ +
+ + +
+
running build_ext
+building 'pairwise' extension
+C compiler: /usr/bin/clang -fno-strict-aliasing -Wsign-compare -Wunreachable-code -fno-common -dynamic -DNDEBUG -g -fwrapv -O3 -Wall -Wstrict-prototypes -arch i386 -arch x86_64 -g
+
+compile options: '-I/Users/uweschmitt/Projects/hope/venv3/lib/python3.5/site-packages/numpy/core/include -I/Users/uweschmitt/Projects/hope/venv3/include -I/Library/Frameworks/Python.framework/Versions/3.5/include/python3.5m -c'
+extra options: '-Wall -Wno-unused-variable -march=native -stdlib=libc++ -std=c++11 -Wno-unreachable-code'
+clang: ././src/pairwise.cpp
+/usr/bin/clang++ -bundle -undefined dynamic_lookup -arch i386 -arch x86_64 -g ./src/pairwise.o -o ./pairwise.cpython-35m-darwin.so
+
+
+
+
+ +
+
+ +
+
+
+
In [8]:
+
+
+
X = np.random.random((1000, 3))
+D = np.empty((1000, 1000))
+
+ +
+
+
+ +
+
+
+
In [9]:
+
+
+
D1 = np.empty((1000, 1000))
+D2 = np.empty((1000, 1000))
+D3 = np.empty((1000, 1000))
+D4 = np.empty((1000, 1000))
+D5 = np.empty((1000, 1000))
+D6 = np.empty((1000, 1000))
+
+pairwise_python(X, D1)
+pairwise_numpy(X, D2)
+pairwise_numba(X, D3)
+pairwise_cython(X, D4)
+pairwise_hope(X, D5, X.shape[0], X.shape[1])
+pairwise_native(X, D6, X.shape[0], X.shape[1])
+
+assert np.allclose(D1, D2)
+assert np.allclose(D1, D3)
+assert np.allclose(D1, D4)
+assert np.allclose(D1, D5)
+assert np.allclose(D1, D6)
+
+ +
+
+
+ +
+
+ + +
+ +
+ + +
+
pairwise_hope(float64^2 X, float64^2 D, int64 M, int64 N)
+	for i.l in (0.J:M.J) {
+		for j.l in (0.J:M.J) {
+			new d.D
+			d.D = 0.0.D
+			for k.l in (0.J:N.J) {
+				new tmp.d
+				tmp.d = (X.d[i.l, k.l] - X.d[j.l, k.l])
+				d.D += (tmp.d * tmp.d)
+			}
+			D.d[i.l, j.l] = numpy.sqrt(d.D)
+		}
+	}
+
+Compiling following functions:
+pairwise_hope(float64^2 X, float64^2 D, int64 M, int64 N)
+running build_ext
+building 'pairwise_hope_f82cb8a6ea3aecbde9f728fa85088b51f5bdf7d8ef8b740b9c0b788a_0' extension
+C compiler: /usr/bin/clang -fno-strict-aliasing -Wsign-compare -Wunreachable-code -fno-common -dynamic -DNDEBUG -g -fwrapv -O3 -Wall -arch i386 -arch x86_64 -g
+
+compile options: '-I/Users/uweschmitt/Projects/hope/venv3/lib/python3.5/site-packages/numpy/core/include -I/Users/uweschmitt/Projects/hope/venv3/include -I/Library/Frameworks/Python.framework/Versions/3.5/include/python3.5m -c'
+extra options: '-Wall -Wno-unused-variable -march=native -stdlib=libc++ -std=c++11 -Wno-unreachable-code'
+clang: /var/folders/k8/zfp7dvcs1m326gz1brql1tv80000gn/T/hope2wag9krd/pairwise_hope_f82cb8a6ea3aecbde9f728fa85088b51f5bdf7d8ef8b740b9c0b788a_0.cpp
+/usr/bin/clang++ -bundle -undefined dynamic_lookup -arch i386 -arch x86_64 -g /var/folders/k8/zfp7dvcs1m326gz1brql1tv80000gn/T/hope2wag9krd/pairwise_hope_f82cb8a6ea3aecbde9f728fa85088b51f5bdf7d8ef8b740b9c0b788a_0.o -o /var/folders/k8/zfp7dvcs1m326gz1brql1tv80000gn/T/hope2wag9krd/pairwise_hope_f82cb8a6ea3aecbde9f728fa85088b51f5bdf7d8ef8b740b9c0b788a_0.cpython-35m-darwin.so
+
+
+
+
+ +
+
+ +
+
+
+
In [10]:
+
+
+
print("naive python")
+%timeit pairwise_python(X, D)
+print("numpy")
+%timeit pairwise_numpy(X, D)
+print("numba")
+%timeit pairwise_numba(X, D)
+print("cython")
+%timeit pairwise_cython(X, D)
+print("hope")
+%timeit pairwise_hope(X, D, X.shape[0], X.shape[1])
+print("native")
+%timeit pairwise_native(X, D, X.shape[0], X.shape[1])
+
+ +
+
+
+ +
+
+ + +
+ +
+ + +
+
naive python
+3.74 s ± 157 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)
+numpy
+43.8 ms ± 555 µs per loop (mean ± std. dev. of 7 runs, 10 loops each)
+numba
+4.67 ms ± 116 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)
+cython
+4.71 ms ± 65.4 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)
+hope
+5.76 ms ± 101 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)
+native
+4.61 ms ± 159 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)
+
+
+
+ +
+
+ +
+
+
+
In [11]:
+
+
+
from util import perf_comp_data
+
+ +
+
+
+ +
+
+
+
In [12]:
+
+
+
M, N = X.shape
+data_list = 4*["X, D"]+ 2*["X, D, M, N"]
+#print data_list
+perf_comp_data(["pairwise_python", 
+                "pairwise_numpy", 
+                "pairwise_numba", 
+                "pairwise_cython", 
+                "pairwise_hope",
+                "pairwise_native"],
+               data_list, rep=100)
+
+ +
+
+
+ +
+
+ + +
+ +
+ + +
+
function: pairwise_numba      , av. time sec:   0.00443349, min. time sec:   0.00402957, relative:       1.0
+function: pairwise_cython     , av. time sec:   0.00445642, min. time sec:   0.00424550, relative:       1.0
+function: pairwise_native     , av. time sec:   0.00452401, min. time sec:   0.00426805, relative:       1.0
+function: pairwise_hope       , av. time sec:   0.00555244, min. time sec:   0.00492547, relative:       1.3
+function: pairwise_numpy      , av. time sec:   0.03890058, min. time sec:   0.03608699, relative:       8.8
+function: pairwise_python     , av. time sec:   3.80517048, min. time sec:   3.42553714, relative:     858.3
+
+
+
+ +
+
+ +
+
+
+ + + + + + diff --git a/benchmarks/pairwise.ipynb b/benchmarks/pairwise.ipynb index af69c4b..b77378e 100644 --- a/benchmarks/pairwise.ipynb +++ b/benchmarks/pairwise.ipynb @@ -2,7 +2,7 @@ "cells": [ { "cell_type": "code", - "execution_count": 1, + "execution_count": 2, "metadata": {}, "outputs": [ { @@ -11,63 +11,63 @@ "Software versions": [ { "module": "Python", - "version": "2.7.8 (default, Jul 13 2014, 17:11:32) [GCC 4.2.1 Compatible Apple LLVM 5.1 (clang-503.0.40)]" + "version": "3.5.1 64bit [GCC 4.2.1 (Apple Inc. build 5666) (dot 3)]" }, { "module": "IPython", - "version": "1.1.0" + "version": "6.1.0" }, { "module": "OS", - "version": "posix [darwin]" + "version": "Darwin 15.6.0 x86_64 i386 64bit" }, { "module": "numpy", - "version": "1.8.1" + "version": "1.13.1" }, { "module": "Cython", - "version": "0.20.2" + "version": "0.26" }, { "module": "numba", - "version": "0.13.3" + "version": "0.34.0" }, { "module": "hope", - "version": "0.3.0" + "version": "0.6.1" } ] }, "text/html": [ - "
SoftwareVersion
Python2.7.8 (default, Jul 13 2014, 17:11:32) [GCC 4.2.1 Compatible Apple LLVM 5.1 (clang-503.0.40)]
IPython1.1.0
OSposix [darwin]
numpy1.8.1
Cython0.20.2
numba0.13.3
hope0.3.0
Thu Sep 04 15:18:18 2014 CEST
" + "
SoftwareVersion
Python3.5.1 64bit [GCC 4.2.1 (Apple Inc. build 5666) (dot 3)]
IPython6.1.0
OSDarwin 15.6.0 x86_64 i386 64bit
numpy1.13.1
Cython0.26
numba0.34.0
hope0.6.1
Mon Sep 04 15:15:37 2017 CEST
" ], "text/latex": [ "\\begin{tabular}{|l|l|}\\hline\n", "{\\bf Software} & {\\bf Version} \\\\ \\hline\\hline\n", - "Python & 2.7.8 (default, Jul 13 2014, 17:11:32) [GCC 4.2.1 Compatible Apple LLVM 5.1 (clang-503.0.40)] \\\\ \\hline\n", - "IPython & 1.1.0 \\\\ \\hline\n", - "OS & posix [darwin] \\\\ \\hline\n", - "numpy & 1.8.1 \\\\ \\hline\n", - "Cython & 0.20.2 \\\\ \\hline\n", - "numba & 0.13.3 \\\\ \\hline\n", - "hope & 0.3.0 \\\\ \\hline\n", - "\\hline \\multicolumn{2}{|l|}{Thu Sep 04 15:18:18 2014 CEST} \\\\ \\hline\n", + "Python & 3.5.1 64bit [GCC 4.2.1 (Apple Inc. build 5666) (dot 3)] \\\\ \\hline\n", + "IPython & 6.1.0 \\\\ \\hline\n", + "OS & Darwin 15.6.0 x86\\_64 i386 64bit \\\\ \\hline\n", + "numpy & 1.13.1 \\\\ \\hline\n", + "Cython & 0.26 \\\\ \\hline\n", + "numba & 0.34.0 \\\\ \\hline\n", + "hope & 0.6.1 \\\\ \\hline\n", + "\\hline \\multicolumn{2}{|l|}{Mon Sep 04 15:15:37 2017 CEST} \\\\ \\hline\n", "\\end{tabular}\n" ], "text/plain": [ "Software versions\n", - "Python 2.7.8 (default, Jul 13 2014, 17:11:32) [GCC 4.2.1 Compatible Apple LLVM 5.1 (clang-503.0.40)]\n", - "IPython 1.1.0\n", - "OS posix [darwin]\n", - "numpy 1.8.1\n", - "Cython 0.20.2\n", - "numba 0.13.3\n", - "hope 0.3.0\n", - "Thu Sep 04 15:18:18 2014 CEST" + "Python 3.5.1 64bit [GCC 4.2.1 (Apple Inc. build 5666) (dot 3)]\n", + "IPython 6.1.0\n", + "OS Darwin 15.6.0 x86_64 i386 64bit\n", + "numpy 1.13.1\n", + "Cython 0.26\n", + "numba 0.34.0\n", + "hope 0.6.1\n", + "Mon Sep 04 15:15:37 2017 CEST" ] }, - "execution_count": 1, + "execution_count": 2, "metadata": {}, "output_type": "execute_result" } @@ -81,15 +81,17 @@ "import numpy as np\n", "from util import perf_comp_data\n", "from native_util import load\n", - "%load_ext cythonmagic\n", + "%load_ext Cython\n", "%load_ext version_information\n", "%version_information numpy, Cython, numba, hope" ] }, { "cell_type": "code", - "execution_count": 2, - "metadata": {}, + "execution_count": 3, + "metadata": { + "collapsed": true + }, "outputs": [], "source": [ "# Pure python version\n", @@ -108,8 +110,10 @@ }, { "cell_type": "code", - "execution_count": 3, - "metadata": {}, + "execution_count": 4, + "metadata": { + "collapsed": true + }, "outputs": [], "source": [ "# Numpy python version\n", @@ -122,8 +126,10 @@ }, { "cell_type": "code", - "execution_count": 4, - "metadata": {}, + "execution_count": 5, + "metadata": { + "collapsed": true + }, "outputs": [], "source": [ "# numba version\n", @@ -142,7 +148,7 @@ }, { "cell_type": "code", - "execution_count": 5, + "execution_count": 6, "metadata": {}, "outputs": [], "source": [ @@ -154,8 +160,8 @@ "@cython.boundscheck(False)\n", "@cython.wraparound(False)\n", "def pairwise_cython(double[:, ::1] X, double[:, ::1] D):\n", - " cdef int M = X.shape[0]\n", - " cdef int N = X.shape[1]\n", + " cdef size_t M = X.shape[0]\n", + " cdef size_t N = X.shape[1]\n", " cdef double tmp, d\n", " for i in range(M):\n", " for j in range(M):\n", @@ -168,8 +174,10 @@ }, { "cell_type": "code", - "execution_count": 6, - "metadata": {}, + "execution_count": 7, + "metadata": { + "collapsed": true + }, "outputs": [], "source": [ "# hope version\n", @@ -186,7 +194,7 @@ }, { "cell_type": "code", - "execution_count": 7, + "execution_count": 8, "metadata": {}, "outputs": [ { @@ -194,13 +202,6 @@ "output_type": "stream", "text": [ "running build_ext\n", - "building 'pairwise' extension\n", - "C compiler: /usr/bin/clang -fno-strict-aliasing -fno-common -dynamic -pipe -Os -fwrapv -DNDEBUG -g -fwrapv -O3 -Wall -Wstrict-prototypes\n", - "\n", - "compile options: '-I/Users/jakeret/Library/Python/2.7/lib/python/site-packages/numpy/core/include -I/opt/local/Library/Frameworks/Python.framework/Versions/2.7/include/python2.7 -c'\n", - "extra options: '-Wall -Wno-unused-variable -march=native -stdlib=libc++ -std=c++11'\n", - "clang: ././src/pairwise.cpp\n", - "/usr/bin/clang++ -bundle -undefined dynamic_lookup -L/opt/local/lib -Wl,-headerpad_max_install_names -L/opt/local/lib/db46 ./src/pairwise.o -o ./pairwise.so\n", "\n" ] } @@ -214,8 +215,10 @@ }, { "cell_type": "code", - "execution_count": 8, - "metadata": {}, + "execution_count": 9, + "metadata": { + "collapsed": true + }, "outputs": [], "source": [ "X = np.random.random((1000, 3))\n", @@ -226,7 +229,39 @@ "cell_type": "code", "execution_count": 10, "metadata": {}, - "outputs": [], + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "pairwise_hope(float64^2 X, float64^2 D, int64 M, int64 N)\n", + "\tfor i.l in (0.J:M.J) {\n", + "\t\tfor j.l in (0.J:M.J) {\n", + "\t\t\tnew d.D\n", + "\t\t\td.D = 0.0.D\n", + "\t\t\tfor k.l in (0.J:N.J) {\n", + "\t\t\t\tnew tmp.d\n", + "\t\t\t\ttmp.d = (X.d[i.l, k.l] - X.d[j.l, k.l])\n", + "\t\t\t\td.D += (tmp.d * tmp.d)\n", + "\t\t\t}\n", + "\t\t\tD.d[i.l, j.l] = numpy.sqrt(d.D)\n", + "\t\t}\n", + "\t}\n", + "\n", + "Compiling following functions:\n", + "pairwise_hope(float64^2 X, float64^2 D, int64 M, int64 N)\n", + "running build_ext\n", + "building 'pairwise_hope_f82cb8a6ea3aecbde9f728fa85088b51f5bdf7d8ef8b740b9c0b788a_0' extension\n", + "C compiler: /usr/bin/clang -fno-strict-aliasing -Wsign-compare -Wunreachable-code -fno-common -dynamic -DNDEBUG -g -fwrapv -O3 -Wall -arch i386 -arch x86_64 -g\n", + "\n", + "compile options: '-I/Users/uweschmitt/Projects/hope/venv3/lib/python3.5/site-packages/numpy/core/include -I/Users/uweschmitt/Projects/hope/venv3/include -I/Library/Frameworks/Python.framework/Versions/3.5/include/python3.5m -c'\n", + "extra options: '-Wall -Wno-unused-variable -march=native -stdlib=libc++ -std=c++11 -Wno-unreachable-code'\n", + "clang: /var/folders/k8/zfp7dvcs1m326gz1brql1tv80000gn/T/hope6c1ewncq/pairwise_hope_f82cb8a6ea3aecbde9f728fa85088b51f5bdf7d8ef8b740b9c0b788a_0.cpp\n", + "/usr/bin/clang++ -bundle -undefined dynamic_lookup -arch i386 -arch x86_64 -g /var/folders/k8/zfp7dvcs1m326gz1brql1tv80000gn/T/hope6c1ewncq/pairwise_hope_f82cb8a6ea3aecbde9f728fa85088b51f5bdf7d8ef8b740b9c0b788a_0.o -o /var/folders/k8/zfp7dvcs1m326gz1brql1tv80000gn/T/hope6c1ewncq/pairwise_hope_f82cb8a6ea3aecbde9f728fa85088b51f5bdf7d8ef8b740b9c0b788a_0.cpython-35m-darwin.so\n", + "\n" + ] + } + ], "source": [ "D1 = np.empty((1000, 1000))\n", "D2 = np.empty((1000, 1000))\n", @@ -251,7 +286,7 @@ }, { "cell_type": "code", - "execution_count": 11, + "execution_count": 12, "metadata": {}, "outputs": [ { @@ -259,39 +294,41 @@ "output_type": "stream", "text": [ "naive python\n", - "1 loops, best of 3: 5.75 s per loop\n", + "3.99 s ± 64.9 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)\n", "numpy\n", - "10 loops, best of 3: 36.6 ms per loop\n", + "42 ms ± 1.26 ms per loop (mean ± std. dev. of 7 runs, 10 loops each)\n", "numba\n", - "100 loops, best of 3: 6.88 ms per loop\n", + "4.63 ms ± 131 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)\n", "cython\n", - "100 loops, best of 3: 4.22 ms per loop\n", + "4.75 ms ± 82.7 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)\n", "hope\n", - "100 loops, best of 3: 6.36 ms per loop\n", + "6.02 ms ± 117 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)\n", "native\n", - "100 loops, best of 3: 4.23 ms per loop\n" + "4.72 ms ± 107 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)\n" ] } ], "source": [ - "print \"naive python\"\n", + "print(\"naive python\")\n", "%timeit pairwise_python(X, D)\n", - "print \"numpy\"\n", + "print(\"numpy\")\n", "%timeit pairwise_numpy(X, D)\n", - "print \"numba\"\n", + "print(\"numba\")\n", "%timeit pairwise_numba(X, D)\n", - "print \"cython\"\n", + "print(\"cython\")\n", "%timeit pairwise_cython(X, D)\n", - "print \"hope\"\n", + "print(\"hope\")\n", "%timeit pairwise_hope(X, D, X.shape[0], X.shape[1])\n", - "print \"native\"\n", + "print(\"native\")\n", "%timeit pairwise_native(X, D, X.shape[0], X.shape[1])" ] }, { "cell_type": "code", - "execution_count": null, - "metadata": {}, + "execution_count": 13, + "metadata": { + "collapsed": true + }, "outputs": [], "source": [ "from util import perf_comp_data" @@ -299,19 +336,19 @@ }, { "cell_type": "code", - "execution_count": 13, + "execution_count": 14, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ - "function: pairwise_native , av. time sec: 0.00420749, min. time sec: 0.00414991, relative: 1.0\n", - "function: pairwise_cython , av. time sec: 0.00421405, min. time sec: 0.00415277, relative: 1.0\n", - "function: pairwise_hope , av. time sec: 0.00635457, min. time sec: 0.00626707, relative: 1.5\n", - "function: pairwise_numba , av. time sec: 0.00690150, min. time sec: 0.00681400, relative: 1.6\n", - "function: pairwise_numpy , av. time sec: 0.03677249, min. time sec: 0.03618908, relative: 8.7\n", - "function: pairwise_python , av. time sec: 5.90122139, min. time sec: 5.63599896, relative: 1402.6\n" + "function: pairwise_numba , av. time sec: 0.00433872, min. time sec: 0.00417360, relative: 1.0\n", + "function: pairwise_cython , av. time sec: 0.00436183, min. time sec: 0.00415240, relative: 1.0\n", + "function: pairwise_native , av. time sec: 0.00436626, min. time sec: 0.00414733, relative: 1.0\n", + "function: pairwise_hope , av. time sec: 0.00550070, min. time sec: 0.00512893, relative: 1.3\n", + "function: pairwise_numpy , av. time sec: 0.03921036, min. time sec: 0.03764664, relative: 9.0\n", + "function: pairwise_python , av. time sec: 4.04625443, min. time sec: 3.89136448, relative: 932.6\n" ] } ], @@ -327,13 +364,6 @@ " \"pairwise_native\"],\n", " data_list, rep=100)" ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [] } ], "metadata": { diff --git a/benchmarks/simplify.html b/benchmarks/simplify.html new file mode 100644 index 0000000..3a46a2e --- /dev/null +++ b/benchmarks/simplify.html @@ -0,0 +1,12656 @@ + + + +simplify + + + + + + + + + + + + + + + + + + + + +
+
+ +
+
+
In [1]:
+
+
+
import hope
+hope.config.optimize = True
+hope.config.verbose = True
+hope.config.keeptemp = True
+import numba
+import numpy as np
+import numexpr as ne
+
+from util import perf_comp_data
+from native_util import load
+%load_ext Cython
+%load_ext version_information
+%version_information numpy, Cython, numba, hope, numexpr
+
+ +
+
+
+ +
+
+ + +
+ +
Out[1]:
+ + + +
+
SoftwareVersion
Python3.5.1 64bit [GCC 4.2.1 (Apple Inc. build 5666) (dot 3)]
IPython6.1.0
OSDarwin 15.6.0 x86_64 i386 64bit
numpy1.13.1
Cython0.26
numba0.34.0
hope0.6.1
numexpr2.6.2
Mon Sep 04 17:35:55 2017 CEST
+
+ +
+ +
+
+ +
+
+
+
In [2]:
+
+
+
def poly(res, arg):
+    res[:] = np.sin(arg)**2 + (arg**3 + arg**2 - arg - 1)/(arg**2 + 2*arg + 1) + np.cos(arg)**2
+hope_poly = hope.jit(poly)
+numba_poly = numba.jit(poly, nopython=False)
+
+native_poly_mod = load("poly")
+native_poly = native_poly_mod.run
+
+ +
+
+
+ +
+
+ + +
+ +
+ + +
+
running build_ext
+building 'poly' extension
+C compiler: /usr/bin/clang -fno-strict-aliasing -Wsign-compare -Wunreachable-code -fno-common -dynamic -DNDEBUG -g -fwrapv -O3 -Wall -Wstrict-prototypes -arch i386 -arch x86_64 -g
+
+compile options: '-I/Users/uweschmitt/Projects/hope/venv3/lib/python3.5/site-packages/numpy/core/include -I/Users/uweschmitt/Projects/hope/venv3/include -I/Library/Frameworks/Python.framework/Versions/3.5/include/python3.5m -c'
+extra options: '-Wall -Wno-unused-variable -march=native -stdlib=libc++ -std=c++11 -Wno-unreachable-code'
+clang: ././src/poly.cpp
+/usr/bin/clang++ -bundle -undefined dynamic_lookup -arch i386 -arch x86_64 -g ./src/poly.o -o ./poly.cpython-35m-darwin.so
+
+
+
+
+ +
+
+ +
+
+
+
In [3]:
+
+
+
%%cython -a
+
+cimport cython
+from libc.math cimport sin
+from libc.math cimport cos
+from libc.math cimport pow
+
+@cython.boundscheck(False)
+@cython.wraparound(False)
+@cython.cdivision(True)
+def cython_poly(double[:] res, double[:] arg):
+    cdef int i
+    cdef double i_arg
+    for i in range(len(arg)):
+        i_arg = arg[i]
+        res[i] = pow(sin(i_arg),2) + (pow(i_arg,3) + pow(i_arg,2) - i_arg - 1)/(pow(i_arg,2) + 2*i_arg + 1) + pow(cos(i_arg),2)
+
+ +
+
+
+ +
+
+ + +
+ +
Out[3]:
+ + + +
+ + + + + + Cython: _cython_magic_c56f3cd16e138e603a8a648f0cd9159f.pyx + + + + +

Generated by Cython 0.26

+

+ Yellow lines hint at Python interaction.
+ Click on a line that starts with a "+" to see the C code that Cython generated for it. +

+
 01: 
+
+02: cimport cython
+
  __pyx_t_1 = PyDict_New(); if (unlikely(!__pyx_t_1)) __PYX_ERR(0, 2, __pyx_L1_error)
+  __Pyx_GOTREF(__pyx_t_1);
+  if (PyDict_SetItem(__pyx_d, __pyx_n_s_test, __pyx_t_1) < 0) __PYX_ERR(0, 2, __pyx_L1_error)
+  __Pyx_DECREF(__pyx_t_1); __pyx_t_1 = 0;
+
 03: from libc.math cimport sin
+
 04: from libc.math cimport cos
+
 05: from libc.math cimport pow
+
 06: 
+
 07: @cython.boundscheck(False)
+
 08: @cython.wraparound(False)
+
 09: @cython.cdivision(True)
+
+10: def cython_poly(double[:] res, double[:] arg):
+
/* Python wrapper */
+static PyObject *__pyx_pw_46_cython_magic_c56f3cd16e138e603a8a648f0cd9159f_1cython_poly(PyObject *__pyx_self, PyObject *__pyx_args, PyObject *__pyx_kwds); /*proto*/
+static PyMethodDef __pyx_mdef_46_cython_magic_c56f3cd16e138e603a8a648f0cd9159f_1cython_poly = {"cython_poly", (PyCFunction)__pyx_pw_46_cython_magic_c56f3cd16e138e603a8a648f0cd9159f_1cython_poly, METH_VARARGS|METH_KEYWORDS, 0};
+static PyObject *__pyx_pw_46_cython_magic_c56f3cd16e138e603a8a648f0cd9159f_1cython_poly(PyObject *__pyx_self, PyObject *__pyx_args, PyObject *__pyx_kwds) {
+  __Pyx_memviewslice __pyx_v_res = { 0, 0, { 0 }, { 0 }, { 0 } };
+  __Pyx_memviewslice __pyx_v_arg = { 0, 0, { 0 }, { 0 }, { 0 } };
+  PyObject *__pyx_r = 0;
+  __Pyx_RefNannyDeclarations
+  __Pyx_RefNannySetupContext("cython_poly (wrapper)", 0);
+  {
+    static PyObject **__pyx_pyargnames[] = {&__pyx_n_s_res,&__pyx_n_s_arg,0};
+    PyObject* values[2] = {0,0};
+    if (unlikely(__pyx_kwds)) {
+      Py_ssize_t kw_args;
+      const Py_ssize_t pos_args = PyTuple_GET_SIZE(__pyx_args);
+      switch (pos_args) {
+        case  2: values[1] = PyTuple_GET_ITEM(__pyx_args, 1);
+        CYTHON_FALLTHROUGH;
+        case  1: values[0] = PyTuple_GET_ITEM(__pyx_args, 0);
+        CYTHON_FALLTHROUGH;
+        case  0: break;
+        default: goto __pyx_L5_argtuple_error;
+      }
+      kw_args = PyDict_Size(__pyx_kwds);
+      switch (pos_args) {
+        case  0:
+        if (likely((values[0] = PyDict_GetItem(__pyx_kwds, __pyx_n_s_res)) != 0)) kw_args--;
+        else goto __pyx_L5_argtuple_error;
+        CYTHON_FALLTHROUGH;
+        case  1:
+        if (likely((values[1] = PyDict_GetItem(__pyx_kwds, __pyx_n_s_arg)) != 0)) kw_args--;
+        else {
+          __Pyx_RaiseArgtupleInvalid("cython_poly", 1, 2, 2, 1); __PYX_ERR(0, 10, __pyx_L3_error)
+        }
+      }
+      if (unlikely(kw_args > 0)) {
+        if (unlikely(__Pyx_ParseOptionalKeywords(__pyx_kwds, __pyx_pyargnames, 0, values, pos_args, "cython_poly") < 0)) __PYX_ERR(0, 10, __pyx_L3_error)
+      }
+    } else if (PyTuple_GET_SIZE(__pyx_args) != 2) {
+      goto __pyx_L5_argtuple_error;
+    } else {
+      values[0] = PyTuple_GET_ITEM(__pyx_args, 0);
+      values[1] = PyTuple_GET_ITEM(__pyx_args, 1);
+    }
+    __pyx_v_res = __Pyx_PyObject_to_MemoryviewSlice_ds_double(values[0]); if (unlikely(!__pyx_v_res.memview)) __PYX_ERR(0, 10, __pyx_L3_error)
+    __pyx_v_arg = __Pyx_PyObject_to_MemoryviewSlice_ds_double(values[1]); if (unlikely(!__pyx_v_arg.memview)) __PYX_ERR(0, 10, __pyx_L3_error)
+  }
+  goto __pyx_L4_argument_unpacking_done;
+  __pyx_L5_argtuple_error:;
+  __Pyx_RaiseArgtupleInvalid("cython_poly", 1, 2, 2, PyTuple_GET_SIZE(__pyx_args)); __PYX_ERR(0, 10, __pyx_L3_error)
+  __pyx_L3_error:;
+  __Pyx_AddTraceback("_cython_magic_c56f3cd16e138e603a8a648f0cd9159f.cython_poly", __pyx_clineno, __pyx_lineno, __pyx_filename);
+  __Pyx_RefNannyFinishContext();
+  return NULL;
+  __pyx_L4_argument_unpacking_done:;
+  __pyx_r = __pyx_pf_46_cython_magic_c56f3cd16e138e603a8a648f0cd9159f_cython_poly(__pyx_self, __pyx_v_res, __pyx_v_arg);
+
+  /* function exit code */
+  __Pyx_RefNannyFinishContext();
+  return __pyx_r;
+}
+
+static PyObject *__pyx_pf_46_cython_magic_c56f3cd16e138e603a8a648f0cd9159f_cython_poly(CYTHON_UNUSED PyObject *__pyx_self, __Pyx_memviewslice __pyx_v_res, __Pyx_memviewslice __pyx_v_arg) {
+  int __pyx_v_i;
+  double __pyx_v_i_arg;
+  PyObject *__pyx_r = NULL;
+  __Pyx_RefNannyDeclarations
+  __Pyx_RefNannySetupContext("cython_poly", 0);
+/* … */
+  /* function exit code */
+  __pyx_r = Py_None; __Pyx_INCREF(Py_None);
+  goto __pyx_L0;
+  __pyx_L1_error:;
+  __Pyx_XDECREF(__pyx_t_1);
+  __Pyx_AddTraceback("_cython_magic_c56f3cd16e138e603a8a648f0cd9159f.cython_poly", __pyx_clineno, __pyx_lineno, __pyx_filename);
+  __pyx_r = NULL;
+  __pyx_L0:;
+  __PYX_XDEC_MEMVIEW(&__pyx_v_res, 1);
+  __PYX_XDEC_MEMVIEW(&__pyx_v_arg, 1);
+  __Pyx_XGIVEREF(__pyx_r);
+  __Pyx_RefNannyFinishContext();
+  return __pyx_r;
+}
+/* … */
+  __pyx_tuple__20 = PyTuple_Pack(4, __pyx_n_s_res, __pyx_n_s_arg, __pyx_n_s_i, __pyx_n_s_i_arg); if (unlikely(!__pyx_tuple__20)) __PYX_ERR(0, 10, __pyx_L1_error)
+  __Pyx_GOTREF(__pyx_tuple__20);
+  __Pyx_GIVEREF(__pyx_tuple__20);
+/* … */
+  __pyx_t_1 = PyCFunction_NewEx(&__pyx_mdef_46_cython_magic_c56f3cd16e138e603a8a648f0cd9159f_1cython_poly, NULL, __pyx_n_s_cython_magic_c56f3cd16e138e603a); if (unlikely(!__pyx_t_1)) __PYX_ERR(0, 10, __pyx_L1_error)
+  __Pyx_GOTREF(__pyx_t_1);
+  if (PyDict_SetItem(__pyx_d, __pyx_n_s_cython_poly, __pyx_t_1) < 0) __PYX_ERR(0, 10, __pyx_L1_error)
+  __Pyx_DECREF(__pyx_t_1); __pyx_t_1 = 0;
+  __pyx_codeobj__21 = (PyObject*)__Pyx_PyCode_New(2, 0, 4, 0, 0, __pyx_empty_bytes, __pyx_empty_tuple, __pyx_empty_tuple, __pyx_tuple__20, __pyx_empty_tuple, __pyx_empty_tuple, __pyx_kp_s_Users_uweschmitt_ipython_cython, __pyx_n_s_cython_poly, 10, __pyx_empty_bytes); if (unlikely(!__pyx_codeobj__21)) __PYX_ERR(0, 10, __pyx_L1_error)
+
 11:     cdef int i
+
 12:     cdef double i_arg
+
+13:     for i in range(len(arg)):
+
  __pyx_t_1 = __pyx_memoryview_fromslice(__pyx_v_arg, 1, (PyObject *(*)(char *)) __pyx_memview_get_double, (int (*)(char *, PyObject *)) __pyx_memview_set_double, 0);; if (unlikely(!__pyx_t_1)) __PYX_ERR(0, 13, __pyx_L1_error)
+  __Pyx_GOTREF(__pyx_t_1);
+  __pyx_t_2 = PyObject_Length(__pyx_t_1); if (unlikely(__pyx_t_2 == -1)) __PYX_ERR(0, 13, __pyx_L1_error)
+  __Pyx_DECREF(__pyx_t_1); __pyx_t_1 = 0;
+  for (__pyx_t_3 = 0; __pyx_t_3 < __pyx_t_2; __pyx_t_3+=1) {
+    __pyx_v_i = __pyx_t_3;
+
+14:         i_arg = arg[i]
+
    __pyx_t_4 = __pyx_v_i;
+    __pyx_v_i_arg = (*((double *) ( /* dim=0 */ (__pyx_v_arg.data + __pyx_t_4 * __pyx_v_arg.strides[0]) )));
+
+15:         res[i] = pow(sin(i_arg),2) + (pow(i_arg,3) + pow(i_arg,2) - i_arg - 1)/(pow(i_arg,2) + 2*i_arg + 1) + pow(cos(i_arg),2)
+
    __pyx_t_5 = __pyx_v_i;
+    *((double *) ( /* dim=0 */ (__pyx_v_res.data + __pyx_t_5 * __pyx_v_res.strides[0]) )) = ((pow(sin(__pyx_v_i_arg), 2.0) + ((((pow(__pyx_v_i_arg, 3.0) + pow(__pyx_v_i_arg, 2.0)) - __pyx_v_i_arg) - 1.0) / ((pow(__pyx_v_i_arg, 2.0) + (2.0 * __pyx_v_i_arg)) + 1.0))) + pow(cos(__pyx_v_i_arg), 2.0));
+  }
+
+
+ +
+ +
+
+ +
+
+
+
In [4]:
+
+
+
%%cython
+cimport cython
+import numpy as np
+cimport numpy as np
+
+@cython.boundscheck(False)
+@cython.wraparound(False)
+def cython_numpy_poly(np.ndarray[np.double_t, ndim=1] res, np.ndarray[np.double_t, ndim=1] arg):
+    res[:] = np.sin(arg)**2 + (arg**3 + arg**2 - arg - 1)/(arg**2 + 2*arg + 1) + np.cos(arg)**2
+
+ +
+
+
+ +
+
+
+
In [5]:
+
+
+
# NumExpr version
+
+import numexpr as ne
+
+def numexpr_poly(res, arg):
+    res[:] = ne.evaluate("sin(arg)**2 + (arg**3 + arg**2 - arg - 1)/(arg**2 + 2*arg + 1) + cos(arg)**2")
+
+ +
+
+
+ +
+
+
+
In [6]:
+
+
+
arg = np.random.random(50000)
+res = np.empty_like(arg)
+
+res1 = np.empty_like(arg)
+res2 = np.empty_like(arg)
+res3 = np.empty_like(arg)
+res4 = np.empty_like(arg)
+res5 = np.empty_like(arg)
+
+poly(res1, arg)
+hope_poly(res2, arg)
+numba_poly(res3, arg)
+native_poly(res4, arg)
+numexpr_poly(res5, arg)
+
+assert np.allclose(res1, res2)
+assert np.allclose(res1, res3)
+assert np.allclose(res1, res4)
+assert np.allclose(res1, res5)
+
+ +
+
+
+ +
+
+ + +
+ +
+ + +
+
poly(float64^1 res, float64^1 arg)
+	(:res@0) {
+		res.d[:res@0] = (((numpy.sin(arg.d[:arg@0]) ** 2.J) + (((((arg.d[:arg@0] ** 3.J) + (arg.d[:arg@0] ** 2.J)) - arg.d[:arg@0]) - 1.J) / (((arg.d[:arg@0] ** 2.J) + (2.J * arg.d[:arg@0])) + 1.J))) + (numpy.cos(arg.d[:arg@0]) ** 2.J))
+	}
+
+Compiling following functions:
+poly(float64^1 res, float64^1 arg)
+running build_ext
+building 'poly_332b2c0332e8513cfd35081ce10b11e6c57ab0ccac824978e78990c6_0' extension
+C compiler: /usr/bin/clang -fno-strict-aliasing -Wsign-compare -Wunreachable-code -fno-common -dynamic -DNDEBUG -g -fwrapv -O3 -Wall -arch i386 -arch x86_64 -g
+
+compile options: '-I/Users/uweschmitt/Projects/hope/venv3/lib/python3.5/site-packages/numpy/core/include -I/Users/uweschmitt/Projects/hope/venv3/include -I/Library/Frameworks/Python.framework/Versions/3.5/include/python3.5m -c'
+extra options: '-Wall -Wno-unused-variable -march=native -stdlib=libc++ -std=c++11 -Wno-unreachable-code'
+clang: /var/folders/k8/zfp7dvcs1m326gz1brql1tv80000gn/T/hope68l2_50k/poly_332b2c0332e8513cfd35081ce10b11e6c57ab0ccac824978e78990c6_0.cpp
+/usr/bin/clang++ -bundle -undefined dynamic_lookup -arch i386 -arch x86_64 -g /var/folders/k8/zfp7dvcs1m326gz1brql1tv80000gn/T/hope68l2_50k/poly_332b2c0332e8513cfd35081ce10b11e6c57ab0ccac824978e78990c6_0.o -o /var/folders/k8/zfp7dvcs1m326gz1brql1tv80000gn/T/hope68l2_50k/poly_332b2c0332e8513cfd35081ce10b11e6c57ab0ccac824978e78990c6_0.cpython-35m-darwin.so
+
+
+
+
+ +
+
+ +
+
+
+
In [7]:
+
+
+
print("python")
+%timeit poly(res, arg)
+print("hope")
+%timeit hope_poly(res, arg)
+print("numba")
+%timeit numba_poly(res, arg)
+print("cython")
+%timeit cython_poly(res, arg)
+print("cython numpy")
+%timeit cython_numpy_poly(res, arg)
+print("native")
+%timeit native_poly(res, arg)
+print("numexpr (1)")
+ne.set_num_threads(1)
+%timeit numexpr_poly(res, arg)
+print("numexpr ({0})".format(ne.detect_number_of_cores()))
+ne.set_num_threads(ne.detect_number_of_cores())
+%timeit numexpr_poly(res, arg)
+
+ +
+
+
+ +
+
+ + +
+ +
+ + +
+
python
+2.66 ms ± 91.9 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)
+hope
+28.9 µs ± 686 ns per loop (mean ± std. dev. of 7 runs, 10000 loops each)
+numba
+685 µs ± 5.93 µs per loop (mean ± std. dev. of 7 runs, 1000 loops each)
+cython
+2.06 ms ± 44.6 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)
+cython numpy
+2.85 ms ± 67.3 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)
+native
+2.17 ms ± 21.5 µs per loop (mean ± std. dev. of 7 runs, 1000 loops each)
+numexpr (1)
+1.44 ms ± 16.7 µs per loop (mean ± std. dev. of 7 runs, 1000 loops each)
+numexpr (8)
+462 µs ± 28.2 µs per loop (mean ± std. dev. of 7 runs, 1000 loops each)
+
+
+
+ +
+
+ +
+
+
+
In [8]:
+
+
+
ne.set_num_threads(8)
+perf_comp_data(["poly", 
+                "hope_poly", 
+                "numba_poly", 
+                "cython_poly", 
+                "cython_numpy_poly", 
+                "native_poly",
+                "numexpr_poly"], 
+               7*["res, arg"], rep=100)
+
+ +
+
+
+ +
+
+ + +
+ +
+ + +
+
function: hope_poly           , av. time sec:   0.00002556, min. time sec:   0.00002529, relative:       1.0
+function: numexpr_poly        , av. time sec:   0.00041609, min. time sec:   0.00038729, relative:      16.3
+function: numba_poly          , av. time sec:   0.00063846, min. time sec:   0.00063759, relative:      25.0
+function: native_poly         , av. time sec:   0.00186274, min. time sec:   0.00185231, relative:      72.9
+function: cython_poly         , av. time sec:   0.00196121, min. time sec:   0.00176564, relative:      76.7
+function: poly                , av. time sec:   0.00256785, min. time sec:   0.00237837, relative:     100.4
+function: cython_numpy_poly   , av. time sec:   0.00263516, min. time sec:   0.00245497, relative:     103.1
+
+
+
+ +
+
+ +
+
+
+
In [9]:
+
+
+
 
+
+ +
+
+
+ +
+
+
+ + + + + + diff --git a/benchmarks/simplify.ipynb b/benchmarks/simplify.ipynb index 5312bce..44bf5d6 100644 --- a/benchmarks/simplify.ipynb +++ b/benchmarks/simplify.ipynb @@ -11,66 +11,66 @@ "Software versions": [ { "module": "Python", - "version": "2.7.8 (default, Jul 13 2014, 17:11:32) [GCC 4.2.1 Compatible Apple LLVM 5.1 (clang-503.0.40)]" + "version": "3.5.1 64bit [GCC 4.2.1 (Apple Inc. build 5666) (dot 3)]" }, { "module": "IPython", - "version": "1.1.0" + "version": "6.1.0" }, { "module": "OS", - "version": "posix [darwin]" + "version": "Darwin 15.6.0 x86_64 i386 64bit" }, { "module": "numpy", - "version": "1.8.1" + "version": "1.13.1" }, { "module": "Cython", - "version": "0.20.2" + "version": "0.26" }, { "module": "numba", - "version": "0.13.3" + "version": "0.34.0" }, { "module": "hope", - "version": "0.3.0" + "version": "0.6.1" }, { "module": "numexpr", - "version": "2.4" + "version": "2.6.2" } ] }, "text/html": [ - "
SoftwareVersion
Python2.7.8 (default, Jul 13 2014, 17:11:32) [GCC 4.2.1 Compatible Apple LLVM 5.1 (clang-503.0.40)]
IPython1.1.0
OSposix [darwin]
numpy1.8.1
Cython0.20.2
numba0.13.3
hope0.3.0
numexpr2.4
Thu Sep 04 15:17:12 2014 CEST
" + "
SoftwareVersion
Python3.5.1 64bit [GCC 4.2.1 (Apple Inc. build 5666) (dot 3)]
IPython6.1.0
OSDarwin 15.6.0 x86_64 i386 64bit
numpy1.13.1
Cython0.26
numba0.34.0
hope0.6.1
numexpr2.6.2
Mon Sep 04 15:28:09 2017 CEST
" ], "text/latex": [ "\\begin{tabular}{|l|l|}\\hline\n", "{\\bf Software} & {\\bf Version} \\\\ \\hline\\hline\n", - "Python & 2.7.8 (default, Jul 13 2014, 17:11:32) [GCC 4.2.1 Compatible Apple LLVM 5.1 (clang-503.0.40)] \\\\ \\hline\n", - "IPython & 1.1.0 \\\\ \\hline\n", - "OS & posix [darwin] \\\\ \\hline\n", - "numpy & 1.8.1 \\\\ \\hline\n", - "Cython & 0.20.2 \\\\ \\hline\n", - "numba & 0.13.3 \\\\ \\hline\n", - "hope & 0.3.0 \\\\ \\hline\n", - "numexpr & 2.4 \\\\ \\hline\n", - "\\hline \\multicolumn{2}{|l|}{Thu Sep 04 15:17:12 2014 CEST} \\\\ \\hline\n", + "Python & 3.5.1 64bit [GCC 4.2.1 (Apple Inc. build 5666) (dot 3)] \\\\ \\hline\n", + "IPython & 6.1.0 \\\\ \\hline\n", + "OS & Darwin 15.6.0 x86\\_64 i386 64bit \\\\ \\hline\n", + "numpy & 1.13.1 \\\\ \\hline\n", + "Cython & 0.26 \\\\ \\hline\n", + "numba & 0.34.0 \\\\ \\hline\n", + "hope & 0.6.1 \\\\ \\hline\n", + "numexpr & 2.6.2 \\\\ \\hline\n", + "\\hline \\multicolumn{2}{|l|}{Mon Sep 04 15:28:09 2017 CEST} \\\\ \\hline\n", "\\end{tabular}\n" ], "text/plain": [ "Software versions\n", - "Python 2.7.8 (default, Jul 13 2014, 17:11:32) [GCC 4.2.1 Compatible Apple LLVM 5.1 (clang-503.0.40)]\n", - "IPython 1.1.0\n", - "OS posix [darwin]\n", - "numpy 1.8.1\n", - "Cython 0.20.2\n", - "numba 0.13.3\n", - "hope 0.3.0\n", - "numexpr 2.4\n", - "Thu Sep 04 15:17:12 2014 CEST" + "Python 3.5.1 64bit [GCC 4.2.1 (Apple Inc. build 5666) (dot 3)]\n", + "IPython 6.1.0\n", + "OS Darwin 15.6.0 x86_64 i386 64bit\n", + "numpy 1.13.1\n", + "Cython 0.26\n", + "numba 0.34.0\n", + "hope 0.6.1\n", + "numexpr 2.6.2\n", + "Mon Sep 04 15:28:09 2017 CEST" ] }, "execution_count": 1, @@ -89,14 +89,14 @@ "\n", "from util import perf_comp_data\n", "from native_util import load\n", - "%load_ext cythonmagic\n", + "%load_ext Cython\n", "%load_ext version_information\n", "%version_information numpy, Cython, numba, hope, numexpr" ] }, { "cell_type": "code", - "execution_count": 2, + "execution_count": 5, "metadata": {}, "outputs": [ { @@ -105,12 +105,12 @@ "text": [ "running build_ext\n", "building 'poly' extension\n", - "C compiler: /usr/bin/clang -fno-strict-aliasing -fno-common -dynamic -pipe -Os -fwrapv -DNDEBUG -g -fwrapv -O3 -Wall -Wstrict-prototypes\n", + "C compiler: /usr/bin/clang -fno-strict-aliasing -Wsign-compare -Wunreachable-code -fno-common -dynamic -DNDEBUG -g -fwrapv -O3 -Wall -Wstrict-prototypes -arch i386 -arch x86_64 -g\n", "\n", - "compile options: '-I/Users/jakeret/Library/Python/2.7/lib/python/site-packages/numpy/core/include -I/opt/local/Library/Frameworks/Python.framework/Versions/2.7/include/python2.7 -c'\n", - "extra options: '-Wall -Wno-unused-variable -march=native -stdlib=libc++ -std=c++11'\n", + "compile options: '-I/Users/uweschmitt/Projects/hope/venv3/lib/python3.5/site-packages/numpy/core/include -I/Users/uweschmitt/Projects/hope/venv3/include -I/Library/Frameworks/Python.framework/Versions/3.5/include/python3.5m -c'\n", + "extra options: '-Wall -Wno-unused-variable -march=native -stdlib=libc++ -std=c++11 -Wno-unreachable-code'\n", "clang: ././src/poly.cpp\n", - "/usr/bin/clang++ -bundle -undefined dynamic_lookup -L/opt/local/lib -Wl,-headerpad_max_install_names -L/opt/local/lib/db46 ./src/poly.o -o ./poly.so\n", + "/usr/bin/clang++ -bundle -undefined dynamic_lookup -arch i386 -arch x86_64 -g ./src/poly.o -o ./poly.cpython-35m-darwin.so\n", "\n" ] } @@ -127,11 +127,532 @@ }, { "cell_type": "code", - "execution_count": 3, + "execution_count": 9, "metadata": {}, - "outputs": [], + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "building '_cython_magic_c56f3cd16e138e603a8a648f0cd9159f' extension\n", + "C compiler: /usr/bin/clang -fno-strict-aliasing -Wsign-compare -Wunreachable-code -fno-common -dynamic -DNDEBUG -g -fwrapv -O3 -Wall -Wstrict-prototypes -arch i386 -arch x86_64 -g\n", + "\n", + "compile options: '-I/Users/uweschmitt/Projects/hope/venv3/include -I/Library/Frameworks/Python.framework/Versions/3.5/include/python3.5m -c'\n", + "clang: /Users/uweschmitt/.ipython/cython/_cython_magic_c56f3cd16e138e603a8a648f0cd9159f.c\n", + "/usr/bin/clang -bundle -undefined dynamic_lookup -arch i386 -arch x86_64 -g /Users/uweschmitt/.ipython/cython/Users/uweschmitt/.ipython/cython/_cython_magic_c56f3cd16e138e603a8a648f0cd9159f.o -o /Users/uweschmitt/.ipython/cython/_cython_magic_c56f3cd16e138e603a8a648f0cd9159f.cpython-35m-darwin.so\n" + ] + }, + { + "data": { + "text/html": [ + "\n", + "\n", + "\n", + "\n", + " \n", + " Cython: _cython_magic_c56f3cd16e138e603a8a648f0cd9159f.pyx\n", + " \n", + " \n", + "\n", + "\n", + "

Generated by Cython 0.26

\n", + "

\n", + " Yellow lines hint at Python interaction.
\n", + " Click on a line that starts with a \"+\" to see the C code that Cython generated for it.\n", + "

\n", + "
 01: 
\n", + "
+02: cimport cython
\n", + "
  __pyx_t_1 = PyDict_New(); if (unlikely(!__pyx_t_1)) __PYX_ERR(0, 2, __pyx_L1_error)\n",
+       "  __Pyx_GOTREF(__pyx_t_1);\n",
+       "  if (PyDict_SetItem(__pyx_d, __pyx_n_s_test, __pyx_t_1) < 0) __PYX_ERR(0, 2, __pyx_L1_error)\n",
+       "  __Pyx_DECREF(__pyx_t_1); __pyx_t_1 = 0;\n",
+       "
 03: from libc.math cimport sin
\n", + "
 04: from libc.math cimport cos
\n", + "
 05: from libc.math cimport pow
\n", + "
 06: 
\n", + "
 07: @cython.boundscheck(False)
\n", + "
 08: @cython.wraparound(False)
\n", + "
 09: @cython.cdivision(True)
\n", + "
+10: def cython_poly(double[:] res, double[:] arg):
\n", + "
/* Python wrapper */\n",
+       "static PyObject *__pyx_pw_46_cython_magic_c56f3cd16e138e603a8a648f0cd9159f_1cython_poly(PyObject *__pyx_self, PyObject *__pyx_args, PyObject *__pyx_kwds); /*proto*/\n",
+       "static PyMethodDef __pyx_mdef_46_cython_magic_c56f3cd16e138e603a8a648f0cd9159f_1cython_poly = {\"cython_poly\", (PyCFunction)__pyx_pw_46_cython_magic_c56f3cd16e138e603a8a648f0cd9159f_1cython_poly, METH_VARARGS|METH_KEYWORDS, 0};\n",
+       "static PyObject *__pyx_pw_46_cython_magic_c56f3cd16e138e603a8a648f0cd9159f_1cython_poly(PyObject *__pyx_self, PyObject *__pyx_args, PyObject *__pyx_kwds) {\n",
+       "  __Pyx_memviewslice __pyx_v_res = { 0, 0, { 0 }, { 0 }, { 0 } };\n",
+       "  __Pyx_memviewslice __pyx_v_arg = { 0, 0, { 0 }, { 0 }, { 0 } };\n",
+       "  PyObject *__pyx_r = 0;\n",
+       "  __Pyx_RefNannyDeclarations\n",
+       "  __Pyx_RefNannySetupContext(\"cython_poly (wrapper)\", 0);\n",
+       "  {\n",
+       "    static PyObject **__pyx_pyargnames[] = {&__pyx_n_s_res,&__pyx_n_s_arg,0};\n",
+       "    PyObject* values[2] = {0,0};\n",
+       "    if (unlikely(__pyx_kwds)) {\n",
+       "      Py_ssize_t kw_args;\n",
+       "      const Py_ssize_t pos_args = PyTuple_GET_SIZE(__pyx_args);\n",
+       "      switch (pos_args) {\n",
+       "        case  2: values[1] = PyTuple_GET_ITEM(__pyx_args, 1);\n",
+       "        CYTHON_FALLTHROUGH;\n",
+       "        case  1: values[0] = PyTuple_GET_ITEM(__pyx_args, 0);\n",
+       "        CYTHON_FALLTHROUGH;\n",
+       "        case  0: break;\n",
+       "        default: goto __pyx_L5_argtuple_error;\n",
+       "      }\n",
+       "      kw_args = PyDict_Size(__pyx_kwds);\n",
+       "      switch (pos_args) {\n",
+       "        case  0:\n",
+       "        if (likely((values[0] = PyDict_GetItem(__pyx_kwds, __pyx_n_s_res)) != 0)) kw_args--;\n",
+       "        else goto __pyx_L5_argtuple_error;\n",
+       "        CYTHON_FALLTHROUGH;\n",
+       "        case  1:\n",
+       "        if (likely((values[1] = PyDict_GetItem(__pyx_kwds, __pyx_n_s_arg)) != 0)) kw_args--;\n",
+       "        else {\n",
+       "          __Pyx_RaiseArgtupleInvalid(\"cython_poly\", 1, 2, 2, 1); __PYX_ERR(0, 10, __pyx_L3_error)\n",
+       "        }\n",
+       "      }\n",
+       "      if (unlikely(kw_args > 0)) {\n",
+       "        if (unlikely(__Pyx_ParseOptionalKeywords(__pyx_kwds, __pyx_pyargnames, 0, values, pos_args, \"cython_poly\") < 0)) __PYX_ERR(0, 10, __pyx_L3_error)\n",
+       "      }\n",
+       "    } else if (PyTuple_GET_SIZE(__pyx_args) != 2) {\n",
+       "      goto __pyx_L5_argtuple_error;\n",
+       "    } else {\n",
+       "      values[0] = PyTuple_GET_ITEM(__pyx_args, 0);\n",
+       "      values[1] = PyTuple_GET_ITEM(__pyx_args, 1);\n",
+       "    }\n",
+       "    __pyx_v_res = __Pyx_PyObject_to_MemoryviewSlice_ds_double(values[0]); if (unlikely(!__pyx_v_res.memview)) __PYX_ERR(0, 10, __pyx_L3_error)\n",
+       "    __pyx_v_arg = __Pyx_PyObject_to_MemoryviewSlice_ds_double(values[1]); if (unlikely(!__pyx_v_arg.memview)) __PYX_ERR(0, 10, __pyx_L3_error)\n",
+       "  }\n",
+       "  goto __pyx_L4_argument_unpacking_done;\n",
+       "  __pyx_L5_argtuple_error:;\n",
+       "  __Pyx_RaiseArgtupleInvalid(\"cython_poly\", 1, 2, 2, PyTuple_GET_SIZE(__pyx_args)); __PYX_ERR(0, 10, __pyx_L3_error)\n",
+       "  __pyx_L3_error:;\n",
+       "  __Pyx_AddTraceback(\"_cython_magic_c56f3cd16e138e603a8a648f0cd9159f.cython_poly\", __pyx_clineno, __pyx_lineno, __pyx_filename);\n",
+       "  __Pyx_RefNannyFinishContext();\n",
+       "  return NULL;\n",
+       "  __pyx_L4_argument_unpacking_done:;\n",
+       "  __pyx_r = __pyx_pf_46_cython_magic_c56f3cd16e138e603a8a648f0cd9159f_cython_poly(__pyx_self, __pyx_v_res, __pyx_v_arg);\n",
+       "\n",
+       "  /* function exit code */\n",
+       "  __Pyx_RefNannyFinishContext();\n",
+       "  return __pyx_r;\n",
+       "}\n",
+       "\n",
+       "static PyObject *__pyx_pf_46_cython_magic_c56f3cd16e138e603a8a648f0cd9159f_cython_poly(CYTHON_UNUSED PyObject *__pyx_self, __Pyx_memviewslice __pyx_v_res, __Pyx_memviewslice __pyx_v_arg) {\n",
+       "  int __pyx_v_i;\n",
+       "  double __pyx_v_i_arg;\n",
+       "  PyObject *__pyx_r = NULL;\n",
+       "  __Pyx_RefNannyDeclarations\n",
+       "  __Pyx_RefNannySetupContext(\"cython_poly\", 0);\n",
+       "/* … */\n",
+       "  /* function exit code */\n",
+       "  __pyx_r = Py_None; __Pyx_INCREF(Py_None);\n",
+       "  goto __pyx_L0;\n",
+       "  __pyx_L1_error:;\n",
+       "  __Pyx_XDECREF(__pyx_t_1);\n",
+       "  __Pyx_AddTraceback(\"_cython_magic_c56f3cd16e138e603a8a648f0cd9159f.cython_poly\", __pyx_clineno, __pyx_lineno, __pyx_filename);\n",
+       "  __pyx_r = NULL;\n",
+       "  __pyx_L0:;\n",
+       "  __PYX_XDEC_MEMVIEW(&__pyx_v_res, 1);\n",
+       "  __PYX_XDEC_MEMVIEW(&__pyx_v_arg, 1);\n",
+       "  __Pyx_XGIVEREF(__pyx_r);\n",
+       "  __Pyx_RefNannyFinishContext();\n",
+       "  return __pyx_r;\n",
+       "}\n",
+       "/* … */\n",
+       "  __pyx_tuple__20 = PyTuple_Pack(4, __pyx_n_s_res, __pyx_n_s_arg, __pyx_n_s_i, __pyx_n_s_i_arg); if (unlikely(!__pyx_tuple__20)) __PYX_ERR(0, 10, __pyx_L1_error)\n",
+       "  __Pyx_GOTREF(__pyx_tuple__20);\n",
+       "  __Pyx_GIVEREF(__pyx_tuple__20);\n",
+       "/* … */\n",
+       "  __pyx_t_1 = PyCFunction_NewEx(&__pyx_mdef_46_cython_magic_c56f3cd16e138e603a8a648f0cd9159f_1cython_poly, NULL, __pyx_n_s_cython_magic_c56f3cd16e138e603a); if (unlikely(!__pyx_t_1)) __PYX_ERR(0, 10, __pyx_L1_error)\n",
+       "  __Pyx_GOTREF(__pyx_t_1);\n",
+       "  if (PyDict_SetItem(__pyx_d, __pyx_n_s_cython_poly, __pyx_t_1) < 0) __PYX_ERR(0, 10, __pyx_L1_error)\n",
+       "  __Pyx_DECREF(__pyx_t_1); __pyx_t_1 = 0;\n",
+       "  __pyx_codeobj__21 = (PyObject*)__Pyx_PyCode_New(2, 0, 4, 0, 0, __pyx_empty_bytes, __pyx_empty_tuple, __pyx_empty_tuple, __pyx_tuple__20, __pyx_empty_tuple, __pyx_empty_tuple, __pyx_kp_s_Users_uweschmitt_ipython_cython, __pyx_n_s_cython_poly, 10, __pyx_empty_bytes); if (unlikely(!__pyx_codeobj__21)) __PYX_ERR(0, 10, __pyx_L1_error)\n",
+       "
 11:     cdef int i
\n", + "
 12:     cdef double i_arg
\n", + "
+13:     for i in range(len(arg)):
\n", + "
  __pyx_t_1 = __pyx_memoryview_fromslice(__pyx_v_arg, 1, (PyObject *(*)(char *)) __pyx_memview_get_double, (int (*)(char *, PyObject *)) __pyx_memview_set_double, 0);; if (unlikely(!__pyx_t_1)) __PYX_ERR(0, 13, __pyx_L1_error)\n",
+       "  __Pyx_GOTREF(__pyx_t_1);\n",
+       "  __pyx_t_2 = PyObject_Length(__pyx_t_1); if (unlikely(__pyx_t_2 == -1)) __PYX_ERR(0, 13, __pyx_L1_error)\n",
+       "  __Pyx_DECREF(__pyx_t_1); __pyx_t_1 = 0;\n",
+       "  for (__pyx_t_3 = 0; __pyx_t_3 < __pyx_t_2; __pyx_t_3+=1) {\n",
+       "    __pyx_v_i = __pyx_t_3;\n",
+       "
+14:         i_arg = arg[i]
\n", + "
    __pyx_t_4 = __pyx_v_i;\n",
+       "    __pyx_v_i_arg = (*((double *) ( /* dim=0 */ (__pyx_v_arg.data + __pyx_t_4 * __pyx_v_arg.strides[0]) )));\n",
+       "
+15:         res[i] = pow(sin(i_arg),2) + (pow(i_arg,3) + pow(i_arg,2) - i_arg - 1)/(pow(i_arg,2) + 2*i_arg + 1) + pow(cos(i_arg),2)
\n", + "
    __pyx_t_5 = __pyx_v_i;\n",
+       "    *((double *) ( /* dim=0 */ (__pyx_v_res.data + __pyx_t_5 * __pyx_v_res.strides[0]) )) = ((pow(sin(__pyx_v_i_arg), 2.0) + ((((pow(__pyx_v_i_arg, 3.0) + pow(__pyx_v_i_arg, 2.0)) - __pyx_v_i_arg) - 1.0) / ((pow(__pyx_v_i_arg, 2.0) + (2.0 * __pyx_v_i_arg)) + 1.0))) + pow(cos(__pyx_v_i_arg), 2.0));\n",
+       "  }\n",
+       "
" + ], + "text/plain": [ + "" + ] + }, + "execution_count": 9, + "metadata": {}, + "output_type": "execute_result" + } + ], "source": [ - "%%cython\n", + "%%cython -a\n", "\n", "cimport cython\n", "from libc.math cimport sin\n", @@ -140,7 +661,10 @@ "\n", "@cython.boundscheck(False)\n", "@cython.wraparound(False)\n", + "@cython.cdivision(True)\n", "def cython_poly(double[:] res, double[:] arg):\n", + " cdef int i\n", + " cdef double i_arg\n", " for i in range(len(arg)):\n", " i_arg = arg[i]\n", " res[i] = pow(sin(i_arg),2) + (pow(i_arg,3) + pow(i_arg,2) - i_arg - 1)/(pow(i_arg,2) + 2*i_arg + 1) + pow(cos(i_arg),2)\n", @@ -149,9 +673,38 @@ }, { "cell_type": "code", - "execution_count": 4, + "execution_count": 10, "metadata": {}, - "outputs": [], + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "building '_cython_magic_2c4125c086131196c2c8dcbbb7f86ef5' extension\n", + "C compiler: /usr/bin/clang -fno-strict-aliasing -Wsign-compare -Wunreachable-code -fno-common -dynamic -DNDEBUG -g -fwrapv -O3 -Wall -Wstrict-prototypes -arch i386 -arch x86_64 -g\n", + "\n", + "compile options: '-I/Users/uweschmitt/Projects/hope/venv3/lib/python3.5/site-packages/numpy/core/include -I/Users/uweschmitt/Projects/hope/venv3/include -I/Library/Frameworks/Python.framework/Versions/3.5/include/python3.5m -c'\n", + "clang: /Users/uweschmitt/.ipython/cython/_cython_magic_2c4125c086131196c2c8dcbbb7f86ef5.c\n", + "In file included from /Users/uweschmitt/.ipython/cython/_cython_magic_2c4125c086131196c2c8dcbbb7f86ef5.c:495:\n", + "In file included from /Users/uweschmitt/Projects/hope/venv3/lib/python3.5/site-packages/numpy/core/include/numpy/arrayobject.h:4:\n", + "In file included from /Users/uweschmitt/Projects/hope/venv3/lib/python3.5/site-packages/numpy/core/include/numpy/ndarrayobject.h:18:\n", + "In file included from /Users/uweschmitt/Projects/hope/venv3/lib/python3.5/site-packages/numpy/core/include/numpy/ndarraytypes.h:1809:\n", + "/Users/uweschmitt/Projects/hope/venv3/lib/python3.5/site-packages/numpy/core/include/numpy/npy_1_7_deprecated_api.h:15:2: warning: \"Using deprecated NumPy API, disable it by \" \"#defining NPY_NO_DEPRECATED_API NPY_1_7_API_VERSION\" [-W#warnings]\n", + "#warning \"Using deprecated NumPy API, disable it by \" \\\n", + " ^\n", + "1 warning generated.\n", + "In file included from /Users/uweschmitt/.ipython/cython/_cython_magic_2c4125c086131196c2c8dcbbb7f86ef5.c:495:\n", + "In file included from /Users/uweschmitt/Projects/hope/venv3/lib/python3.5/site-packages/numpy/core/include/numpy/arrayobject.h:4:\n", + "In file included from /Users/uweschmitt/Projects/hope/venv3/lib/python3.5/site-packages/numpy/core/include/numpy/ndarrayobject.h:18:\n", + "In file included from /Users/uweschmitt/Projects/hope/venv3/lib/python3.5/site-packages/numpy/core/include/numpy/ndarraytypes.h:1809:\n", + "/Users/uweschmitt/Projects/hope/venv3/lib/python3.5/site-packages/numpy/core/include/numpy/npy_1_7_deprecated_api.h:15:2: warning: \"Using deprecated NumPy API, disable it by \" \"#defining NPY_NO_DEPRECATED_API NPY_1_7_API_VERSION\" [-W#warnings]\n", + "#warning \"Using deprecated NumPy API, disable it by \" \\\n", + " ^\n", + "1 warning generated.\n", + "/usr/bin/clang -bundle -undefined dynamic_lookup -arch i386 -arch x86_64 -g /Users/uweschmitt/.ipython/cython/Users/uweschmitt/.ipython/cython/_cython_magic_2c4125c086131196c2c8dcbbb7f86ef5.o -o /Users/uweschmitt/.ipython/cython/_cython_magic_2c4125c086131196c2c8dcbbb7f86ef5.cpython-35m-darwin.so\n" + ] + } + ], "source": [ "%%cython\n", "cimport cython\n", @@ -166,8 +719,10 @@ }, { "cell_type": "code", - "execution_count": 5, - "metadata": {}, + "execution_count": 11, + "metadata": { + "collapsed": true + }, "outputs": [], "source": [ "# NumExpr version\n", @@ -180,9 +735,32 @@ }, { "cell_type": "code", - "execution_count": 7, + "execution_count": 12, "metadata": {}, - "outputs": [], + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "poly(float64^1 res, float64^1 arg)\n", + "\t(:res@0) {\n", + "\t\tres.d[:res@0] = (((numpy.sin(arg.d[:arg@0]) ** 2.J) + (((((arg.d[:arg@0] ** 3.J) + (arg.d[:arg@0] ** 2.J)) - arg.d[:arg@0]) - 1.J) / (((arg.d[:arg@0] ** 2.J) + (2.J * arg.d[:arg@0])) + 1.J))) + (numpy.cos(arg.d[:arg@0]) ** 2.J))\n", + "\t}\n", + "\n", + "Compiling following functions:\n", + "poly(float64^1 res, float64^1 arg)\n", + "running build_ext\n", + "building 'poly_332b2c0332e8513cfd35081ce10b11e6c57ab0ccac824978e78990c6_0' extension\n", + "C compiler: /usr/bin/clang -fno-strict-aliasing -Wsign-compare -Wunreachable-code -fno-common -dynamic -DNDEBUG -g -fwrapv -O3 -Wall -arch i386 -arch x86_64 -g\n", + "\n", + "compile options: '-I/Users/uweschmitt/Projects/hope/venv3/lib/python3.5/site-packages/numpy/core/include -I/Users/uweschmitt/Projects/hope/venv3/include -I/Library/Frameworks/Python.framework/Versions/3.5/include/python3.5m -c'\n", + "extra options: '-Wall -Wno-unused-variable -march=native -stdlib=libc++ -std=c++11 -Wno-unreachable-code'\n", + "clang: /var/folders/k8/zfp7dvcs1m326gz1brql1tv80000gn/T/hopes3ez223s/poly_332b2c0332e8513cfd35081ce10b11e6c57ab0ccac824978e78990c6_0.cpp\n", + "/usr/bin/clang++ -bundle -undefined dynamic_lookup -arch i386 -arch x86_64 -g /var/folders/k8/zfp7dvcs1m326gz1brql1tv80000gn/T/hopes3ez223s/poly_332b2c0332e8513cfd35081ce10b11e6c57ab0ccac824978e78990c6_0.o -o /var/folders/k8/zfp7dvcs1m326gz1brql1tv80000gn/T/hopes3ez223s/poly_332b2c0332e8513cfd35081ce10b11e6c57ab0ccac824978e78990c6_0.cpython-35m-darwin.so\n", + "\n" + ] + } + ], "source": [ "arg = np.random.random(50000)\n", "res = np.empty_like(arg)\n", @@ -207,7 +785,7 @@ }, { "cell_type": "code", - "execution_count": 8, + "execution_count": 14, "metadata": {}, "outputs": [ { @@ -215,41 +793,41 @@ "output_type": "stream", "text": [ "python\n", - "100 loops, best of 3: 2.57 ms per loop\n", + "2.73 ms ± 125 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)\n", "hope\n", - "10000 loops, best of 3: 29 µs per loop\n", + "28.6 µs ± 686 ns per loop (mean ± std. dev. of 7 runs, 10000 loops each)\n", "numba\n", - "100 loops, best of 3: 2.66 ms per loop\n", + "697 µs ± 15.6 µs per loop (mean ± std. dev. of 7 runs, 1000 loops each)\n", "cython\n", - "100 loops, best of 3: 9.97 ms per loop\n", + "2.23 ms ± 54.9 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)\n", "cython numpy\n", - "100 loops, best of 3: 2.56 ms per loop\n", + "3.01 ms ± 60.7 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)\n", "native\n", - "100 loops, best of 3: 2.06 ms per loop\n", + "2.02 ms ± 20.9 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)\n", "numexpr (1)\n", - "1000 loops, best of 3: 1.33 ms per loop\n", + "1.56 ms ± 15.8 µs per loop (mean ± std. dev. of 7 runs, 1000 loops each)\n", "numexpr (8)\n", - "1000 loops, best of 3: 550 µs per loop\n" + "435 µs ± 28 µs per loop (mean ± std. dev. of 7 runs, 1000 loops each)\n" ] } ], "source": [ - "print \"python\"\n", + "print(\"python\")\n", "%timeit poly(res, arg)\n", - "print \"hope\"\n", + "print(\"hope\")\n", "%timeit hope_poly(res, arg)\n", - "print \"numba\"\n", + "print(\"numba\")\n", "%timeit numba_poly(res, arg)\n", - "print \"cython\"\n", + "print(\"cython\")\n", "%timeit cython_poly(res, arg)\n", - "print \"cython numpy\"\n", + "print(\"cython numpy\")\n", "%timeit cython_numpy_poly(res, arg)\n", - "print \"native\"\n", + "print(\"native\")\n", "%timeit native_poly(res, arg)\n", - "print \"numexpr (1)\"\n", + "print(\"numexpr (1)\")\n", "ne.set_num_threads(1)\n", "%timeit numexpr_poly(res, arg)\n", - "print \"numexpr ({0})\".format(ne.detect_number_of_cores())\n", + "print(\"numexpr ({0})\".format(ne.detect_number_of_cores()))\n", "ne.set_num_threads(ne.detect_number_of_cores())\n", "%timeit numexpr_poly(res, arg)\n", "\n" @@ -257,20 +835,20 @@ }, { "cell_type": "code", - "execution_count": 9, + "execution_count": 15, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ - "function: hope_poly , av. time sec: 0.00002789, min. time sec: 0.00002694, relative: 1.0\n", - "function: numexpr_poly , av. time sec: 0.00054300, min. time sec: 0.00048995, relative: 19.5\n", - "function: native_poly , av. time sec: 0.00204802, min. time sec: 0.00201893, relative: 73.4\n", - "function: cython_numpy_poly , av. time sec: 0.00257111, min. time sec: 0.00251794, relative: 92.2\n", - "function: poly , av. time sec: 0.00260139, min. time sec: 0.00251603, relative: 93.3\n", - "function: numba_poly , av. time sec: 0.00272107, min. time sec: 0.00263000, relative: 97.5\n", - "function: cython_poly , av. time sec: 0.00962663, min. time sec: 0.00949407, relative: 345.1\n" + "function: hope_poly , av. time sec: 0.00005208, min. time sec: 0.00002532, relative: 1.0\n", + "function: numexpr_poly , av. time sec: 0.00045749, min. time sec: 0.00039542, relative: 8.8\n", + "function: numba_poly , av. time sec: 0.00063641, min. time sec: 0.00063479, relative: 12.2\n", + "function: cython_poly , av. time sec: 0.00190999, min. time sec: 0.00176367, relative: 36.7\n", + "function: native_poly , av. time sec: 0.00193384, min. time sec: 0.00189017, relative: 37.1\n", + "function: poly , av. time sec: 0.00260737, min. time sec: 0.00242636, relative: 50.1\n", + "function: cython_numpy_poly , av. time sec: 0.00261038, min. time sec: 0.00245425, relative: 50.1\n" ] } ], @@ -289,7 +867,9 @@ { "cell_type": "code", "execution_count": null, - "metadata": {}, + "metadata": { + "collapsed": true + }, "outputs": [], "source": [] } diff --git a/benchmarks/star.html b/benchmarks/star.html new file mode 100644 index 0000000..8d0e357 --- /dev/null +++ b/benchmarks/star.html @@ -0,0 +1,12756 @@ + + + +star + + + + + + + + + + + + + + + + + + + + +
+
+ +
+
+
In [1]:
+
+
+
import hope
+hope.config.optimize = True
+hope.config.verbose = True
+hope.config.keeptemp = True
+import numba
+import numpy as np
+from util import perf_comp_data
+from native_util import load
+%load_ext Cython
+%load_ext version_information
+%version_information numpy, Cython, numba, hope
+
+ +
+
+
+ +
+
+ + +
+ +
Out[1]:
+ + + +
+
SoftwareVersion
Python3.5.1 64bit [GCC 4.2.1 (Apple Inc. build 5666) (dot 3)]
IPython6.1.0
OSDarwin 15.6.0 x86_64 i386 64bit
numpy1.13.1
Cython0.26
numba0.34.0
hope0.6.1
Mon Sep 04 17:36:52 2017 CEST
+
+ +
+ +
+
+ +
+
+
+
In [2]:
+
+
+
b = 3.5
+a = 1. / np.sqrt(2. ** (1. / (b - 1.)) - 1.)
+
+r50 = 2
+
+center = np.array([10.141, 10.414])
+dims = np.array([20, 20])
+# coefficients generated by http://keisan.casio.com/has10/SpecExec.cgi?id=system/2006/1280624821, 7th order
+x1D = np.array([ \
+      0.5 - 0.9491079123427585245262 / 2 \
+    , 0.5 - 0.7415311855993944398639 / 2 \
+    , 0.5 - 0.4058451513773971669066 / 2 \
+    , 0.5 \
+    , 0.5 + 0.4058451513773971669066 / 2 \
+    , 0.5 + 0.7415311855993944398639 / 2 \
+    , 0.5 + 0.9491079123427585245262 / 2 \
+], dtype=np.float32)
+w1D = np.array([ \
+      0.1294849661688696932706 / 2 \
+    , 0.2797053914892766679015 / 2 \
+    , 0.38183005050511894495 / 2 \
+    , 0.4179591836734693877551 / 2 \
+    , 0.38183005050511894495 / 2 \
+    , 0.2797053914892766679015 / 2 \
+    , 0.1294849661688696932706 / 2 \
+], dtype=np.float32)
+w2D = np.outer(w1D, w1D)
+
+print(dims.dtype)
+
+ +
+
+
+ +
+
+ + +
+ +
+ + +
+
int64
+
+
+
+ +
+
+ +
+
+
+
In [3]:
+
+
+
def pdf(density, dims, center, w2D, r50, b, a):
+    for x in range(dims[0]):
+        for y in range(dims[1]):
+            dr = np.sqrt((x - center[0]) ** 2 + (y - center[1]) ** 2)
+            density[x, y] = np.sum(w2D * 2 * (b - 1) / (2 * np.pi * (r50 * a)**2) * (1 + (dr / (r50 * a))**2)**(-b))
+    return density
+
+ +
+
+
+ +
+
+
+
In [4]:
+
+
+
%%cython -f
+#Naive Cython implementation
+cimport cython
+import numpy as np
+cimport numpy as np
+
+@cython.boundscheck(False)
+@cython.wraparound(False)
+def cython_pdf(np.ndarray[np.float32_t, ndim=2] density, 
+               np.ndarray[np.long_t, ndim=1] dims, 
+               np.ndarray[np.float_t, ndim=1] center, 
+               np.ndarray[np.float32_t, ndim=2] w2D, 
+               float r50, 
+               float b, 
+               float a):
+    
+    cdef double dr = 0.0
+    
+    for x in range(dims[0]):
+        for y in range(dims[1]):
+            dr = np.sqrt((x - center[0]) ** 2 + (y - center[1]) ** 2)
+            density[x, y] = np.sum(w2D * 2 * (b - 1) / (2 * np.pi * (r50 * a)**2) * (1 + (dr / (r50 * a))**2)**(-b))
+    return density
+
+ +
+
+
+ +
+
+ + +
+ +
+ + +
+
In file included from /Users/uweschmitt/.ipython/cython/_cython_magic_d756925050f812f526756168f2e13ee4.c:495:
+In file included from /Users/uweschmitt/Projects/hope/venv3/lib/python3.5/site-packages/numpy/core/include/numpy/arrayobject.h:4:
+In file included from /Users/uweschmitt/Projects/hope/venv3/lib/python3.5/site-packages/numpy/core/include/numpy/ndarrayobject.h:18:
+In file included from /Users/uweschmitt/Projects/hope/venv3/lib/python3.5/site-packages/numpy/core/include/numpy/ndarraytypes.h:1809:
+/Users/uweschmitt/Projects/hope/venv3/lib/python3.5/site-packages/numpy/core/include/numpy/npy_1_7_deprecated_api.h:15:2: warning: "Using deprecated NumPy API, disable it by "          "#defining NPY_NO_DEPRECATED_API NPY_1_7_API_VERSION" [-W#warnings]
+#warning "Using deprecated NumPy API, disable it by " \
+ ^
+1 warning generated.
+In file included from /Users/uweschmitt/.ipython/cython/_cython_magic_d756925050f812f526756168f2e13ee4.c:495:
+In file included from /Users/uweschmitt/Projects/hope/venv3/lib/python3.5/site-packages/numpy/core/include/numpy/arrayobject.h:4:
+In file included from /Users/uweschmitt/Projects/hope/venv3/lib/python3.5/site-packages/numpy/core/include/numpy/ndarrayobject.h:18:
+In file included from /Users/uweschmitt/Projects/hope/venv3/lib/python3.5/site-packages/numpy/core/include/numpy/ndarraytypes.h:1809:
+/Users/uweschmitt/Projects/hope/venv3/lib/python3.5/site-packages/numpy/core/include/numpy/npy_1_7_deprecated_api.h:15:2: warning: "Using deprecated NumPy API, disable it by "          "#defining NPY_NO_DEPRECATED_API NPY_1_7_API_VERSION" [-W#warnings]
+#warning "Using deprecated NumPy API, disable it by " \
+ ^
+1 warning generated.
+
+
+
+ +
+
+ +
+
+
+
In [5]:
+
+
+
%%capture
+%%cython -f
+#Modestly tunned Cython implementation
+cimport cython
+import numpy as np
+cimport numpy as np
+
+@cython.boundscheck(False)
+@cython.wraparound(False)
+@cython.cdivision(True)
+def cython_pdf_opt(np.ndarray[np.float32_t, ndim=2] density, 
+               np.ndarray[np.int64_t, ndim=1] dims, 
+               np.ndarray[np.float64_t, ndim=1] center, 
+               np.ndarray[np.float32_t, ndim=2] w2D, 
+               float r50, 
+               float b, 
+               np.float32_t a):
+    
+    cdef np.float32_t pi = np.pi
+    cdef np.float32_t dr = 0.0
+    cdef long x, y
+    cdef double fac, cx, cy
+    
+    cx = center[0]
+    cy = center[1]
+    
+    cdef float s = np.sum(w2D)
+    
+    for x in range(dims[0]):
+        for y in range(dims[1]):
+            dr = ((x - center[0]) ** 2 + (y - center[1]) ** 2)**(0.5)
+            fac = 2. * (b - 1.) / (2. * pi * (r50 * a)**2) * (1. + (dr / (r50 * a))**2)**(-b)
+            density[x, y] = fac * s
+    return density
+
+ +
+
+
+ +
+
+
+
In [6]:
+
+
+
%%cython
+#Aggresively tunned Cython implementation
+cimport cython
+import numpy as np
+cimport numpy as np
+
+@cython.boundscheck(False)
+@cython.wraparound(False)
+@cython.cdivision(True)
+def cython_pdf_unrolled(np.ndarray[np.float32_t, ndim=2] density, 
+               np.ndarray[np.long_t, ndim=1] dims, 
+               np.ndarray[np.float_t, ndim=1] center, 
+               np.ndarray[np.float32_t, ndim=2] w2D, 
+               float r50, 
+               float b, 
+               float a):
+
+    cdef float pi = np.pi
+    cdef int x,y,k,m
+    cdef float s,dr,d
+    cdef Py_ssize_t fac_x = 7
+    cdef Py_ssize_t fac_y = 7
+    cdef np.ndarray[np.float32_t, ndim=2] fac = np.zeros([fac_x, fac_y], dtype=np.float32)
+    cdef float w2D_fac = 2 * (b - 1) / (2 * pi * (r50 * a)**2)
+
+    for k in range(fac_x):
+        for m in range(fac_y):
+            fac[k, m] = w2D[k, m] * w2D_fac
+
+    for x in range(dims[0]):
+        for y in range(dims[1]):
+            dr = ((x - center[0]) ** 2 + (y - center[1]) ** 2)**(1./2)
+            d = (1 + (dr / (r50 * a))**2)**(-b)
+            
+            s = 0
+            for k in range(fac_x):
+                for m in range(fac_y):
+                    s += fac[k, m] * d
+            density[x, y] = s
+    return density
+
+ +
+
+
+ +
+
+
+
In [7]:
+
+
+
hope.config.keeptemp= True
+hope.config.optimize=True
+hope_pdf = hope.jit(pdf) 
+numba_pdf = numba.jit(pdf, nopython=False)
+
+native_pdf_mod = load("pdf")
+native_pdf = native_pdf_mod.run
+
+ +
+
+
+ +
+
+ + +
+ +
+ + +
+
running build_ext
+building 'pdf' extension
+C compiler: /usr/bin/clang -fno-strict-aliasing -Wsign-compare -Wunreachable-code -fno-common -dynamic -DNDEBUG -g -fwrapv -O3 -Wall -Wstrict-prototypes -arch i386 -arch x86_64 -g
+
+compile options: '-I/Users/uweschmitt/Projects/hope/venv3/lib/python3.5/site-packages/numpy/core/include -I/Users/uweschmitt/Projects/hope/venv3/include -I/Library/Frameworks/Python.framework/Versions/3.5/include/python3.5m -c'
+extra options: '-Wall -Wno-unused-variable -march=native -stdlib=libc++ -std=c++11 -Wno-unreachable-code'
+clang: ././src/pdf.cpp
+/usr/bin/clang++ -bundle -undefined dynamic_lookup -arch i386 -arch x86_64 -g ./src/pdf.o -o ./pdf.cpython-35m-darwin.so
+
+
+
+
+ +
+
+ +
+
+
+
In [8]:
+
+
+
density = np.zeros((dims[0], dims[1]), dtype=np.float32)
+pdf(density, dims, center, w2D, r50, b, a)
+
+hdensity = np.zeros((dims[0], dims[1]), dtype=np.float32)
+hope_pdf(hdensity, dims, center, w2D, r50, b, a)
+
+ndensity = np.zeros((dims[0], dims[1]), dtype=np.float32)
+numba_pdf(ndensity, dims, center, w2D, r50, b, a)
+
+cdensity = np.zeros((dims[0], dims[1]), dtype=np.float32)
+cython_pdf(cdensity, dims, center, w2D, r50, b, a)
+
+c2density = np.zeros((dims[0], dims[1]), dtype=np.float32)
+cython_pdf_opt(c2density, dims, center, w2D, r50, b, a)
+
+c3density = np.zeros((dims[0], dims[1]), dtype=np.float32)
+cython_pdf_unrolled(c3density, dims, center, w2D, r50, b, a)
+
+nadensity = np.zeros((dims[0], dims[1]), dtype=np.float32)
+native_pdf(nadensity, dims, center, w2D, r50, b, a)
+
+assert np.allclose(density, hdensity)
+assert np.allclose(density, ndensity)
+assert np.allclose(density, cdensity)
+assert np.allclose(density, c2density)
+assert np.allclose(density, c3density)
+assert np.allclose(density, nadensity)
+
+ +
+
+
+ +
+
+ + +
+ +
+ + +
+
pdf(float32^2 density, int64^1 dims, float64^1 center, float32^2 w2D, int64 r50, float64 b, float64 a)
+	for x.l in (0.J:dims.l[0.J]) {
+		for y.l in (0.J:dims.l[1.J]) {
+			new dr.d
+			dr.d = numpy.sqrt((((x.l - center.d[0.J]) ** 2.J) + ((y.l - center.d[1.J]) ** 2.J)))
+			new __sum0.d
+			__sum0.d = numpy.sum(((((w2D.f[:w2D@0,:w2D@1] * 2.J) * (b.D - 1.J)) / ((2.J * 3.141592653589793.D) * ((r50.J * a.d) ** 2.J))) * ((1.J + ((dr.d / (r50.J * a.d)) ** 2.J)) ** -b.D)))
+			density.f[x.l, y.l] = __sum0.d
+		}
+	}
+	return density.f[:density@0,:density@1]
+
+Compiling following functions:
+pdf(float32^2 density, int64^1 dims, float64^1 center, float32^2 w2D, int64 r50, float64 b, float64 a)
+running build_ext
+building 'pdf_040de60139d62fd4971c5d5623eb6e0b0475c5092ef604e04b27aacc_0' extension
+C compiler: /usr/bin/clang -fno-strict-aliasing -Wsign-compare -Wunreachable-code -fno-common -dynamic -DNDEBUG -g -fwrapv -O3 -Wall -arch i386 -arch x86_64 -g
+
+compile options: '-I/Users/uweschmitt/Projects/hope/venv3/lib/python3.5/site-packages/numpy/core/include -I/Users/uweschmitt/Projects/hope/venv3/include -I/Library/Frameworks/Python.framework/Versions/3.5/include/python3.5m -c'
+extra options: '-Wall -Wno-unused-variable -march=native -stdlib=libc++ -std=c++11 -Wno-unreachable-code'
+clang: /var/folders/k8/zfp7dvcs1m326gz1brql1tv80000gn/T/hopen3lpayny/pdf_040de60139d62fd4971c5d5623eb6e0b0475c5092ef604e04b27aacc_0.cpp
+/usr/bin/clang++ -bundle -undefined dynamic_lookup -arch i386 -arch x86_64 -g /var/folders/k8/zfp7dvcs1m326gz1brql1tv80000gn/T/hopen3lpayny/pdf_040de60139d62fd4971c5d5623eb6e0b0475c5092ef604e04b27aacc_0.o -o /var/folders/k8/zfp7dvcs1m326gz1brql1tv80000gn/T/hopen3lpayny/pdf_040de60139d62fd4971c5d5623eb6e0b0475c5092ef604e04b27aacc_0.cpython-35m-darwin.so
+
+
+
+
+ +
+
+ +
+
+
+
In [9]:
+
+
+
print("Python")
+%timeit pdf(density, dims, center, w2D, r50, b, a)
+print("hope")
+%timeit hope_pdf(density, dims, center, w2D, r50, b, a)
+print("Numba")
+%timeit numba_pdf(density, dims, center, w2D, r50, b, a)
+print("Cython")
+%timeit cython_pdf(density, dims, center, w2D, r50, b, a)
+print("Cython opt")
+%timeit cython_pdf_opt(density, dims, center, w2D, r50, b, a)
+print("Cython unrolled")
+%timeit cython_pdf_unrolled(density, dims, center, w2D, r50, b, a)
+print("Native")
+%timeit native_pdf(density, dims, center, w2D, r50, b, a)
+
+ +
+
+
+ +
+
+ + +
+ +
+ + +
+
Python
+7.85 ms ± 297 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)
+hope
+112 µs ± 1.38 µs per loop (mean ± std. dev. of 7 runs, 10000 loops each)
+Numba
+176 µs ± 1.85 µs per loop (mean ± std. dev. of 7 runs, 10000 loops each)
+Cython
+5.49 ms ± 159 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)
+Cython opt
+26.4 µs ± 614 ns per loop (mean ± std. dev. of 7 runs, 10000 loops each)
+Cython unrolled
+39.1 µs ± 677 ns per loop (mean ± std. dev. of 7 runs, 10000 loops each)
+Native
+51.3 µs ± 1.21 µs per loop (mean ± std. dev. of 7 runs, 10000 loops each)
+
+
+
+ +
+
+ +
+
+
+
In [10]:
+
+
+
funcs = ["pdf", 
+         "numba_pdf", 
+         "hope_pdf", 
+         "cython_pdf", 
+         "cython_pdf_opt", 
+         "cython_pdf_unrolled",
+         "native_pdf", 
+         ]
+perf_comp_data(funcs, 
+               len(funcs)*["density, dims, center, w2D, r50, b, a"], rep=100)
+
+ +
+
+
+ +
+
+ + +
+ +
+ + +
+
function: cython_pdf_opt      , av. time sec:   0.00003660, min. time sec:   0.00002738, relative:       1.0
+function: cython_pdf_unrolled , av. time sec:   0.00003935, min. time sec:   0.00003671, relative:       1.1
+function: native_pdf          , av. time sec:   0.00004619, min. time sec:   0.00004378, relative:       1.3
+function: hope_pdf            , av. time sec:   0.00010971, min. time sec:   0.00010025, relative:       3.0
+function: numba_pdf           , av. time sec:   0.00016097, min. time sec:   0.00016051, relative:       4.4
+function: cython_pdf          , av. time sec:   0.00510810, min. time sec:   0.00480720, relative:     139.6
+function: pdf                 , av. time sec:   0.00751459, min. time sec:   0.00676002, relative:     205.3
+
+
+
+ +
+
+ +
+
+
+
In [11]:
+
+
+
from matplotlib import pyplot as plt
+from mpl_toolkits.mplot3d import Axes3D
+from matplotlib import cm
+from matplotlib.ticker import LinearLocator, FormatStrFormatter
+
+density = np.zeros((dims[0], dims[1]), dtype=np.float32)
+pdf(density, dims, center, w2D, r50, b, a)
+
+fig = plt.figure()
+ax = fig.gca(projection='3d')
+gx, gy = np.mgrid[0:dims[0], 0:dims[1]]
+surf = ax.plot_surface(gx, gy, density, rstride=1, cstride=1, cmap=cm.coolwarm, linewidth=0, antialiased=False)
+fig.colorbar(surf, shrink=0.5, aspect=5)
+plt.show()
+
+ +
+
+
+ +
+
+ + +
+ +
+ + + + +
+ +
+ +
+ +
+
+ +
+
+
+ + + + + + diff --git a/benchmarks/star.ipynb b/benchmarks/star.ipynb index efa2733..7fee6ab 100644 --- a/benchmarks/star.ipynb +++ b/benchmarks/star.ipynb @@ -2,72 +2,82 @@ "cells": [ { "cell_type": "code", - "execution_count": 1, + "execution_count": 47, "metadata": {}, "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "The Cython extension is already loaded. To reload it, use:\n", + " %reload_ext Cython\n", + "The version_information extension is already loaded. To reload it, use:\n", + " %reload_ext version_information\n" + ] + }, { "data": { "application/json": { "Software versions": [ { "module": "Python", - "version": "2.7.8 64bit [GCC 4.2.1 Compatible Apple LLVM 5.1 (clang-503.0.40)]" + "version": "3.5.1 64bit [GCC 4.2.1 (Apple Inc. build 5666) (dot 3)]" }, { "module": "IPython", - "version": "1.1.0" + "version": "6.1.0" }, { "module": "OS", - "version": "Darwin 13.4.0 x86_64 i386 64bit" + "version": "Darwin 15.6.0 x86_64 i386 64bit" }, { "module": "numpy", - "version": "1.8.1" + "version": "1.13.1" }, { "module": "Cython", - "version": "0.20.2" + "version": "0.26" }, { "module": "numba", - "version": "tag: 0.13.3" + "version": "0.34.0" }, { "module": "hope", - "version": "0.3.0" + "version": "0.6.1" } ] }, "text/html": [ - "
SoftwareVersion
Python2.7.8 64bit [GCC 4.2.1 Compatible Apple LLVM 5.1 (clang-503.0.40)]
IPython1.1.0
OSDarwin 13.4.0 x86_64 i386 64bit
numpy1.8.1
Cython0.20.2
numbatag: 0.13.3
hope0.3.0
Tue Nov 25 14:36:16 2014 CET
" + "
SoftwareVersion
Python3.5.1 64bit [GCC 4.2.1 (Apple Inc. build 5666) (dot 3)]
IPython6.1.0
OSDarwin 15.6.0 x86_64 i386 64bit
numpy1.13.1
Cython0.26
numba0.34.0
hope0.6.1
Mon Sep 04 16:22:08 2017 CEST
" ], "text/latex": [ "\\begin{tabular}{|l|l|}\\hline\n", "{\\bf Software} & {\\bf Version} \\\\ \\hline\\hline\n", - "Python & 2.7.8 64bit [GCC 4.2.1 Compatible Apple LLVM 5.1 (clang-503.0.40)] \\\\ \\hline\n", - "IPython & 1.1.0 \\\\ \\hline\n", - "OS & Darwin 13.4.0 x86\\_64 i386 64bit \\\\ \\hline\n", - "numpy & 1.8.1 \\\\ \\hline\n", - "Cython & 0.20.2 \\\\ \\hline\n", - "numba & tag: 0.13.3 \\\\ \\hline\n", - "hope & 0.3.0 \\\\ \\hline\n", - "\\hline \\multicolumn{2}{|l|}{Tue Nov 25 14:36:16 2014 CET} \\\\ \\hline\n", + "Python & 3.5.1 64bit [GCC 4.2.1 (Apple Inc. build 5666) (dot 3)] \\\\ \\hline\n", + "IPython & 6.1.0 \\\\ \\hline\n", + "OS & Darwin 15.6.0 x86\\_64 i386 64bit \\\\ \\hline\n", + "numpy & 1.13.1 \\\\ \\hline\n", + "Cython & 0.26 \\\\ \\hline\n", + "numba & 0.34.0 \\\\ \\hline\n", + "hope & 0.6.1 \\\\ \\hline\n", + "\\hline \\multicolumn{2}{|l|}{Mon Sep 04 16:22:08 2017 CEST} \\\\ \\hline\n", "\\end{tabular}\n" ], "text/plain": [ "Software versions\n", - "Python 2.7.8 64bit [GCC 4.2.1 Compatible Apple LLVM 5.1 (clang-503.0.40)]\n", - "IPython 1.1.0\n", - "OS Darwin 13.4.0 x86_64 i386 64bit\n", - "numpy 1.8.1\n", - "Cython 0.20.2\n", - "numba tag: 0.13.3\n", - "hope 0.3.0\n", - "Tue Nov 25 14:36:16 2014 CET" + "Python 3.5.1 64bit [GCC 4.2.1 (Apple Inc. build 5666) (dot 3)]\n", + "IPython 6.1.0\n", + "OS Darwin 15.6.0 x86_64 i386 64bit\n", + "numpy 1.13.1\n", + "Cython 0.26\n", + "numba 0.34.0\n", + "hope 0.6.1\n", + "Mon Sep 04 16:22:08 2017 CEST" ] }, - "execution_count": 1, + "execution_count": 47, "metadata": {}, "output_type": "execute_result" } @@ -81,7 +91,7 @@ "import numpy as np\n", "from util import perf_comp_data\n", "from native_util import load\n", - "%load_ext cythonmagic\n", + "%load_ext Cython\n", "%load_ext version_information\n", "%version_information numpy, Cython, numba, hope" ] @@ -128,13 +138,15 @@ "], dtype=np.float32)\n", "w2D = np.outer(w1D, w1D)\n", "\n", - "print dims.dtype" + "print(dims.dtype)" ] }, { "cell_type": "code", "execution_count": 3, - "metadata": {}, + "metadata": { + "collapsed": true + }, "outputs": [], "source": [ "def pdf(density, dims, center, w2D, r50, b, a):\n", @@ -147,11 +159,40 @@ }, { "cell_type": "code", - "execution_count": 4, + "execution_count": 44, "metadata": {}, - "outputs": [], + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "building '_cython_magic_f24e61fd7e336481e3f458d73845fd48' extension\n", + "C compiler: /usr/bin/clang -fno-strict-aliasing -Wsign-compare -Wunreachable-code -fno-common -dynamic -DNDEBUG -g -fwrapv -O3 -Wall -arch i386 -arch x86_64 -g\n", + "\n", + "compile options: '-I/Users/uweschmitt/Projects/hope/venv3/lib/python3.5/site-packages/numpy/core/include -I/Users/uweschmitt/Projects/hope/venv3/lib/python3.5/site-packages/numpy/core/include -I/Users/uweschmitt/Projects/hope/venv3/lib/python3.5/site-packages/numpy/core/include -I/Users/uweschmitt/Projects/hope/venv3/lib/python3.5/site-packages/numpy/core/include -I/Users/uweschmitt/Projects/hope/venv3/lib/python3.5/site-packages/numpy/core/include -I/Users/uweschmitt/Projects/hope/venv3/lib/python3.5/site-packages/numpy/core/include -I/Users/uweschmitt/Projects/hope/venv3/lib/python3.5/site-packages/numpy/core/include -I/Users/uweschmitt/Projects/hope/venv3/lib/python3.5/site-packages/numpy/core/include -I/Users/uweschmitt/Projects/hope/venv3/lib/python3.5/site-packages/numpy/core/include -I/Users/uweschmitt/Projects/hope/venv3/lib/python3.5/site-packages/numpy/core/include -I/Users/uweschmitt/Projects/hope/venv3/lib/python3.5/site-packages/numpy/core/include -I/Users/uweschmitt/Projects/hope/venv3/lib/python3.5/site-packages/numpy/core/include -I/Users/uweschmitt/Projects/hope/venv3/lib/python3.5/site-packages/numpy/core/include -I/Users/uweschmitt/Projects/hope/venv3/lib/python3.5/site-packages/numpy/core/include -I/Users/uweschmitt/Projects/hope/venv3/lib/python3.5/site-packages/numpy/core/include -I/Users/uweschmitt/Projects/hope/venv3/lib/python3.5/site-packages/numpy/core/include -I/Users/uweschmitt/Projects/hope/venv3/lib/python3.5/site-packages/numpy/core/include -I/Users/uweschmitt/Projects/hope/venv3/lib/python3.5/site-packages/numpy/core/include -I/Users/uweschmitt/Projects/hope/venv3/lib/python3.5/site-packages/numpy/core/include -I/Users/uweschmitt/Projects/hope/venv3/lib/python3.5/site-packages/numpy/core/include -I/Users/uweschmitt/Projects/hope/venv3/lib/python3.5/site-packages/numpy/core/include -I/Users/uweschmitt/Projects/hope/venv3/include -I/Library/Frameworks/Python.framework/Versions/3.5/include/python3.5m -c'\n", + "clang: /Users/uweschmitt/.ipython/cython/_cython_magic_f24e61fd7e336481e3f458d73845fd48.c\n", + "In file included from /Users/uweschmitt/.ipython/cython/_cython_magic_f24e61fd7e336481e3f458d73845fd48.c:515:\n", + "In file included from /Users/uweschmitt/Projects/hope/venv3/lib/python3.5/site-packages/numpy/core/include/numpy/arrayobject.h:4:\n", + "In file included from /Users/uweschmitt/Projects/hope/venv3/lib/python3.5/site-packages/numpy/core/include/numpy/ndarrayobject.h:18:\n", + "In file included from /Users/uweschmitt/Projects/hope/venv3/lib/python3.5/site-packages/numpy/core/include/numpy/ndarraytypes.h:1809:\n", + "/Users/uweschmitt/Projects/hope/venv3/lib/python3.5/site-packages/numpy/core/include/numpy/npy_1_7_deprecated_api.h:15:2: warning: \"Using deprecated NumPy API, disable it by \" \"#defining NPY_NO_DEPRECATED_API NPY_1_7_API_VERSION\" [-W#warnings]\n", + "#warning \"Using deprecated NumPy API, disable it by \" \\\n", + " ^\n", + "1 warning generated.\n", + "In file included from /Users/uweschmitt/.ipython/cython/_cython_magic_f24e61fd7e336481e3f458d73845fd48.c:515:\n", + "In file included from /Users/uweschmitt/Projects/hope/venv3/lib/python3.5/site-packages/numpy/core/include/numpy/arrayobject.h:4:\n", + "In file included from /Users/uweschmitt/Projects/hope/venv3/lib/python3.5/site-packages/numpy/core/include/numpy/ndarrayobject.h:18:\n", + "In file included from /Users/uweschmitt/Projects/hope/venv3/lib/python3.5/site-packages/numpy/core/include/numpy/ndarraytypes.h:1809:\n", + "/Users/uweschmitt/Projects/hope/venv3/lib/python3.5/site-packages/numpy/core/include/numpy/npy_1_7_deprecated_api.h:15:2: warning: \"Using deprecated NumPy API, disable it by \" \"#defining NPY_NO_DEPRECATED_API NPY_1_7_API_VERSION\" [-W#warnings]\n", + "#warning \"Using deprecated NumPy API, disable it by \" \\\n", + " ^\n", + "1 warning generated.\n", + "/usr/bin/clang -bundle -undefined dynamic_lookup -arch i386 -arch x86_64 -g /Users/uweschmitt/.ipython/cython/Users/uweschmitt/.ipython/cython/_cython_magic_f24e61fd7e336481e3f458d73845fd48.o -o /Users/uweschmitt/.ipython/cython/_cython_magic_f24e61fd7e336481e3f458d73845fd48.cpython-35m-darwin.so\n" + ] + } + ], "source": [ - "%%cython\n", + "%%cython -f\n", "#Naive Cython implementation\n", "cimport cython\n", "import numpy as np\n", @@ -168,23 +209,24 @@ " float a):\n", " \n", " cdef double dr = 0.0\n", - "\n", + " \n", " for x in range(dims[0]):\n", " for y in range(dims[1]):\n", " dr = np.sqrt((x - center[0]) ** 2 + (y - center[1]) ** 2)\n", " density[x, y] = np.sum(w2D * 2 * (b - 1) / (2 * np.pi * (r50 * a)**2) * (1 + (dr / (r50 * a))**2)**(-b))\n", - " return density\n", - "\n", - "\n" + " return density" ] }, { "cell_type": "code", - "execution_count": 5, - "metadata": {}, + "execution_count": 42, + "metadata": { + "collapsed": true + }, "outputs": [], "source": [ - "%%cython\n", + "%%capture\n", + "%%cython -f\n", "#Modestly tunned Cython implementation\n", "cimport cython\n", "import numpy as np\n", @@ -192,6 +234,7 @@ "\n", "@cython.boundscheck(False)\n", "@cython.wraparound(False)\n", + "@cython.cdivision(True)\n", "def cython_pdf_opt(np.ndarray[np.float32_t, ndim=2] density, \n", " np.ndarray[np.int64_t, ndim=1] dims, \n", " np.ndarray[np.float64_t, ndim=1] center, \n", @@ -202,20 +245,56 @@ " \n", " cdef np.float32_t pi = np.pi\n", " cdef np.float32_t dr = 0.0\n", - " cdef int x, y\n", + " cdef long x, y\n", + " cdef double fac, cx, cy\n", + " \n", + " cx = center[0]\n", + " cy = center[1]\n", + " \n", + " cdef float s = np.sum(w2D)\n", " \n", " for x in range(dims[0]):\n", " for y in range(dims[1]):\n", " dr = ((x - center[0]) ** 2 + (y - center[1]) ** 2)**(0.5)\n", - " density[x, y] = np.sum(2. * (b - 1.) / (2. * pi * (r50 * a)**2) * (1. + (dr / (r50 * a))**2)**(-b) * w2D)\n", + " fac = 2. * (b - 1.) / (2. * pi * (r50 * a)**2) * (1. + (dr / (r50 * a))**2)**(-b)\n", + " density[x, y] = fac * s\n", " return density" ] }, { "cell_type": "code", - "execution_count": 6, + "execution_count": 48, "metadata": {}, - "outputs": [], + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "building '_cython_magic_7c009ac0db35df252e00390b32bc6500' extension\n", + "C compiler: /usr/bin/clang -fno-strict-aliasing -Wsign-compare -Wunreachable-code -fno-common -dynamic -DNDEBUG -g -fwrapv -O3 -Wall -arch i386 -arch x86_64 -g\n", + "\n", + "compile options: '-I/Users/uweschmitt/Projects/hope/venv3/lib/python3.5/site-packages/numpy/core/include -I/Users/uweschmitt/Projects/hope/venv3/lib/python3.5/site-packages/numpy/core/include -I/Users/uweschmitt/Projects/hope/venv3/lib/python3.5/site-packages/numpy/core/include -I/Users/uweschmitt/Projects/hope/venv3/lib/python3.5/site-packages/numpy/core/include -I/Users/uweschmitt/Projects/hope/venv3/lib/python3.5/site-packages/numpy/core/include -I/Users/uweschmitt/Projects/hope/venv3/lib/python3.5/site-packages/numpy/core/include -I/Users/uweschmitt/Projects/hope/venv3/lib/python3.5/site-packages/numpy/core/include -I/Users/uweschmitt/Projects/hope/venv3/lib/python3.5/site-packages/numpy/core/include -I/Users/uweschmitt/Projects/hope/venv3/lib/python3.5/site-packages/numpy/core/include -I/Users/uweschmitt/Projects/hope/venv3/lib/python3.5/site-packages/numpy/core/include -I/Users/uweschmitt/Projects/hope/venv3/lib/python3.5/site-packages/numpy/core/include -I/Users/uweschmitt/Projects/hope/venv3/lib/python3.5/site-packages/numpy/core/include -I/Users/uweschmitt/Projects/hope/venv3/lib/python3.5/site-packages/numpy/core/include -I/Users/uweschmitt/Projects/hope/venv3/lib/python3.5/site-packages/numpy/core/include -I/Users/uweschmitt/Projects/hope/venv3/lib/python3.5/site-packages/numpy/core/include -I/Users/uweschmitt/Projects/hope/venv3/lib/python3.5/site-packages/numpy/core/include -I/Users/uweschmitt/Projects/hope/venv3/lib/python3.5/site-packages/numpy/core/include -I/Users/uweschmitt/Projects/hope/venv3/lib/python3.5/site-packages/numpy/core/include -I/Users/uweschmitt/Projects/hope/venv3/lib/python3.5/site-packages/numpy/core/include -I/Users/uweschmitt/Projects/hope/venv3/lib/python3.5/site-packages/numpy/core/include -I/Users/uweschmitt/Projects/hope/venv3/lib/python3.5/site-packages/numpy/core/include -I/Users/uweschmitt/Projects/hope/venv3/lib/python3.5/site-packages/numpy/core/include -I/Users/uweschmitt/Projects/hope/venv3/include -I/Library/Frameworks/Python.framework/Versions/3.5/include/python3.5m -c'\n", + "clang: /Users/uweschmitt/.ipython/cython/_cython_magic_7c009ac0db35df252e00390b32bc6500.c\n", + "In file included from /Users/uweschmitt/.ipython/cython/_cython_magic_7c009ac0db35df252e00390b32bc6500.c:516:\n", + "In file included from /Users/uweschmitt/Projects/hope/venv3/lib/python3.5/site-packages/numpy/core/include/numpy/arrayobject.h:4:\n", + "In file included from /Users/uweschmitt/Projects/hope/venv3/lib/python3.5/site-packages/numpy/core/include/numpy/ndarrayobject.h:18:\n", + "In file included from /Users/uweschmitt/Projects/hope/venv3/lib/python3.5/site-packages/numpy/core/include/numpy/ndarraytypes.h:1809:\n", + "/Users/uweschmitt/Projects/hope/venv3/lib/python3.5/site-packages/numpy/core/include/numpy/npy_1_7_deprecated_api.h:15:2: warning: \"Using deprecated NumPy API, disable it by \" \"#defining NPY_NO_DEPRECATED_API NPY_1_7_API_VERSION\" [-W#warnings]\n", + "#warning \"Using deprecated NumPy API, disable it by \" \\\n", + " ^\n", + "1 warning generated.\n", + "In file included from /Users/uweschmitt/.ipython/cython/_cython_magic_7c009ac0db35df252e00390b32bc6500.c:516:\n", + "In file included from /Users/uweschmitt/Projects/hope/venv3/lib/python3.5/site-packages/numpy/core/include/numpy/arrayobject.h:4:\n", + "In file included from /Users/uweschmitt/Projects/hope/venv3/lib/python3.5/site-packages/numpy/core/include/numpy/ndarrayobject.h:18:\n", + "In file included from /Users/uweschmitt/Projects/hope/venv3/lib/python3.5/site-packages/numpy/core/include/numpy/ndarraytypes.h:1809:\n", + "/Users/uweschmitt/Projects/hope/venv3/lib/python3.5/site-packages/numpy/core/include/numpy/npy_1_7_deprecated_api.h:15:2: warning: \"Using deprecated NumPy API, disable it by \" \"#defining NPY_NO_DEPRECATED_API NPY_1_7_API_VERSION\" [-W#warnings]\n", + "#warning \"Using deprecated NumPy API, disable it by \" \\\n", + " ^\n", + "1 warning generated.\n", + "/usr/bin/clang -bundle -undefined dynamic_lookup -arch i386 -arch x86_64 -g /Users/uweschmitt/.ipython/cython/Users/uweschmitt/.ipython/cython/_cython_magic_7c009ac0db35df252e00390b32bc6500.o -o /Users/uweschmitt/.ipython/cython/_cython_magic_7c009ac0db35df252e00390b32bc6500.cpython-35m-darwin.so\n" + ] + } + ], "source": [ "%%cython\n", "#Aggresively tunned Cython implementation\n", @@ -261,7 +340,7 @@ }, { "cell_type": "code", - "execution_count": 7, + "execution_count": 20, "metadata": {}, "outputs": [ { @@ -270,12 +349,12 @@ "text": [ "running build_ext\n", "building 'pdf' extension\n", - "C compiler: /usr/bin/clang -fno-strict-aliasing -fno-common -dynamic -pipe -Os -fwrapv -DNDEBUG -g -fwrapv -O3 -Wall -Wstrict-prototypes\n", + "C compiler: /usr/bin/clang -fno-strict-aliasing -Wsign-compare -Wunreachable-code -fno-common -dynamic -DNDEBUG -g -fwrapv -O3 -Wall -Wstrict-prototypes -arch i386 -arch x86_64 -g\n", "\n", - "compile options: '-I/Users/jakeret/workspace/virtualenvs/hope_benchmarks/lib/python2.7/site-packages/numpy/core/include -I/opt/local/Library/Frameworks/Python.framework/Versions/2.7/include/python2.7 -c'\n", - "extra options: '-Wall -Wno-unused-variable -march=native -stdlib=libc++ -std=c++11'\n", + "compile options: '-I/Users/uweschmitt/Projects/hope/venv3/lib/python3.5/site-packages/numpy/core/include -I/Users/uweschmitt/Projects/hope/venv3/include -I/Library/Frameworks/Python.framework/Versions/3.5/include/python3.5m -c'\n", + "extra options: '-Wall -Wno-unused-variable -march=native -stdlib=libc++ -std=c++11 -Wno-unreachable-code'\n", "clang: ././src/pdf.cpp\n", - "/usr/bin/clang++ -bundle -undefined dynamic_lookup -L/opt/local/lib -Wl,-headerpad_max_install_names -L/opt/local/lib/db48 ./src/pdf.o -o ./pdf.so\n", + "/usr/bin/clang++ -bundle -undefined dynamic_lookup -arch i386 -arch x86_64 -g ./src/pdf.o -o ./pdf.cpython-35m-darwin.so\n", "\n" ] } @@ -292,8 +371,10 @@ }, { "cell_type": "code", - "execution_count": 8, - "metadata": {}, + "execution_count": 45, + "metadata": { + "collapsed": true + }, "outputs": [], "source": [ "density = np.zeros((dims[0], dims[1]), dtype=np.float32)\n", @@ -327,7 +408,7 @@ }, { "cell_type": "code", - "execution_count": 9, + "execution_count": 46, "metadata": {}, "outputs": [ { @@ -335,54 +416,55 @@ "output_type": "stream", "text": [ "Python\n", - "100 loops, best of 3: 10.8 ms per loop\n", + "7.48 ms ± 175 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)\n", "hope\n", - "10000 loops, best of 3: 106 µs per loop\n", + "119 µs ± 2.65 µs per loop (mean ± std. dev. of 7 runs, 10000 loops each)\n", "Numba\n", - "100 loops, best of 3: 11 ms per loop\n", + "185 µs ± 1.95 µs per loop (mean ± std. dev. of 7 runs, 10000 loops each)\n", "Cython\n", - "100 loops, best of 3: 7.15 ms per loop\n", + "4.97 ms ± 253 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)\n", "Cython opt\n", - "100 loops, best of 3: 2.56 ms per loop\n", + "27.3 µs ± 675 ns per loop (mean ± std. dev. of 7 runs, 10000 loops each)\n", "Cython unrolled\n", - "10000 loops, best of 3: 35.9 µs per loop\n", + "39.1 µs ± 816 ns per loop (mean ± std. dev. of 7 runs, 10000 loops each)\n", "Native\n", - "10000 loops, best of 3: 54.7 µs per loop\n" + "50.4 µs ± 695 ns per loop (mean ± std. dev. of 7 runs, 10000 loops each)\n" ] } ], "source": [ - "print \"Python\"\n", + "print(\"Python\")\n", "%timeit pdf(density, dims, center, w2D, r50, b, a)\n", - "print \"hope\"\n", + "print(\"hope\")\n", "%timeit hope_pdf(density, dims, center, w2D, r50, b, a)\n", - "print \"Numba\"\n", + "print(\"Numba\")\n", "%timeit numba_pdf(density, dims, center, w2D, r50, b, a)\n", - "print \"Cython\"\n", + "print(\"Cython\")\n", "%timeit cython_pdf(density, dims, center, w2D, r50, b, a)\n", - "print \"Cython opt\"\n", + "print(\"Cython opt\")\n", "%timeit cython_pdf_opt(density, dims, center, w2D, r50, b, a)\n", - "print \"Cython unrolled\"\n", + "print(\"Cython unrolled\")\n", "%timeit cython_pdf_unrolled(density, dims, center, w2D, r50, b, a)\n", - "print \"Native\"\n", + "print(\"Native\")\n", "%timeit native_pdf(density, dims, center, w2D, r50, b, a)\n" ] }, { "cell_type": "code", - "execution_count": 10, + "execution_count": 37, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ - "function: native_pdf , av. time sec: 0.00005007, min. time sec: 0.00004888, relative: 1.0\n", - "function: hope_pdf , av. time sec: 0.00010014, min. time sec: 0.00009894, relative: 2.0\n", - "function: cython_pdf_opt , av. time sec: 0.00257397, min. time sec: 0.00239992, relative: 51.4\n", - "function: cython_pdf , av. time sec: 0.00727844, min. time sec: 0.00702214, relative: 145.4\n", - "function: pdf , av. time sec: 0.01095247, min. time sec: 0.01038003, relative: 218.8\n", - "function: numba_pdf , av. time sec: 0.01107156, min. time sec: 0.01068902, relative: 221.1\n" + "function: cython_pdf_opt , av. time sec: 0.00002623, min. time sec: 0.00002379, relative: 1.0\n", + "function: cython_pdf_unrolled , av. time sec: 0.00004890, min. time sec: 0.00004316, relative: 1.9\n", + "function: native_pdf , av. time sec: 0.00005467, min. time sec: 0.00004636, relative: 2.1\n", + "function: hope_pdf , av. time sec: 0.00013511, min. time sec: 0.00010329, relative: 5.2\n", + "function: numba_pdf , av. time sec: 0.00018622, min. time sec: 0.00017234, relative: 7.1\n", + "function: cython_pdf , av. time sec: 0.00461271, min. time sec: 0.00386866, relative: 175.9\n", + "function: pdf , av. time sec: 0.00688719, min. time sec: 0.00635740, relative: 262.6\n" ] } ], @@ -392,7 +474,7 @@ " \"hope_pdf\", \n", " \"cython_pdf\", \n", " \"cython_pdf_opt\", \n", - " #\"cython_pdf_unrolled\",\n", + " \"cython_pdf_unrolled\",\n", " \"native_pdf\", \n", " ]\n", "perf_comp_data(funcs, \n", @@ -431,13 +513,6 @@ "fig.colorbar(surf, shrink=0.5, aspect=5)\n", "plt.show()" ] - }, - { - "cell_type": "code", - "execution_count": 9, - "metadata": {}, - "outputs": [], - "source": [] } ], "metadata": { diff --git a/docs/Makefile b/docs/Makefile index 1005e26..f5036a4 100644 --- a/docs/Makefile +++ b/docs/Makefile @@ -50,6 +50,11 @@ clean: rm -rf $(BUILDDIR)/* html: + @echo copy html versions of benchark notebooks to $(BUILDDIR)/html/benchmarks + mkdir -p $(BUILDDIR) + mkdir -p $(BUILDDIR)/html + mkdir -p $(BUILDDIR)/html/benchmarks + cp ../benchmarks/*.html $(BUILDDIR)/html/benchmarks $(SPHINXBUILD) -b html $(ALLSPHINXOPTS) $(BUILDDIR)/html @echo @echo "Build finished. The HTML pages are in $(BUILDDIR)/html." @@ -174,4 +179,4 @@ xml: pseudoxml: $(SPHINXBUILD) -b pseudoxml $(ALLSPHINXOPTS) $(BUILDDIR)/pseudoxml @echo - @echo "Build finished. The pseudo-XML files are in $(BUILDDIR)/pseudoxml." \ No newline at end of file + @echo "Build finished. The pseudo-XML files are in $(BUILDDIR)/pseudoxml." diff --git a/docs/index.rst b/docs/index.rst index 2815ed2..6c224c3 100644 --- a/docs/index.rst +++ b/docs/index.rst @@ -21,7 +21,10 @@ Contents Benchmarks ---------- -All the benchmarks have been made available online as `IPython notebooks `_ (also on `GitHub `_). If you would like to run the benchmarks on your machine, you can download the notebooks in the nbviewer. +All the benchmarks have been made available online as `IPython notebooks +`_. -**Note**: Make sure to execute the *native_cpp_gen* the first time in order to generate the native C++ code and some Python utility modules. +Test coverage +------------- +`Test coverage reports `_. diff --git a/hope/._transformer.py.swp b/hope/._transformer.py.swp deleted file mode 100644 index a6178a876dda428d3c25011f859aa24931770ed2..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 53248 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 rWxz6E8L$jk1}p=X0n318;D3&Rr+2c%tkM- diff --git a/render_benchmarks_to_html.sh b/render_benchmarks_to_html.sh new file mode 100755 index 0000000..9feaf44 --- /dev/null +++ b/render_benchmarks_to_html.sh @@ -0,0 +1,23 @@ +#!/bin/bash + +function convert +{ + jupyter nbconvert --log-level=10 --to html --execute --ExecutePreprocessor.timeout=-1 "$1" +} + +pushd benchmarks + +rm *.so +rm src/*.o +rm -rf .hope +rm -rf hope + +convert native_cpp_gen.ipynb +convert "HPC Python.ipynb" +convert fibonacci.ipynb +convert simplify.ipynb +convert star.ipynb +convert julialang.org.ipynb +convert numexpr.ipynb +convert pairwise.ipynb +popd diff --git a/tox.ini b/tox.ini index 7025238..69be3a7 100644 --- a/tox.ini +++ b/tox.ini @@ -1,5 +1,5 @@ [tox] -envlist = py27, py34 +envlist = py27, py35 envdir = py27: {toxworkdir}/py27 @@ -19,6 +19,7 @@ commands = pip install -e . # in case we have to compile c extensions py.test -v --basetemp={envtmpdir} --cov-report term-missing --cov-report html --cov hope tests + docs: ./render_benchmarks_to_html.sh docs: sphinx-build -b linkcheck docs/ docs/_build/ # checks external links docs: sphinx-build -b html docs/ docs/_build/ docs: rm -fr docs/_build/htmlcov From 7f1da56469eba107714dae0d564ceb054e8fef33 Mon Sep 17 00:00:00 2001 From: Uwe Date: Tue, 5 Sep 2017 17:55:12 +0200 Subject: [PATCH 14/48] first attempt for improved ci integration --- .gitlab-ci.yml | 10 + benchmarks/.gitignore | 1 + benchmarks/HPC Python.html | 13316 ------------------- benchmarks/fibonacci.html | 12038 ----------------- benchmarks/julialang.org.html | 12617 ------------------ benchmarks/native_cpp_gen.html | 14392 --------------------- benchmarks/native_cpp_gen.nbconvert.html | 14392 --------------------- benchmarks/numexpr.html | 12383 ------------------ benchmarks/pairwise.html | 12201 ----------------- benchmarks/simplify.html | 12656 ------------------ benchmarks/star.html | 12756 ------------------ examples/.gitignore | 2 + tests/test_control_structures.py | 34 +- tox.ini | 52 +- 14 files changed, 72 insertions(+), 116778 deletions(-) delete mode 100644 benchmarks/HPC Python.html delete mode 100644 benchmarks/fibonacci.html delete mode 100644 benchmarks/julialang.org.html delete mode 100644 benchmarks/native_cpp_gen.html delete mode 100644 benchmarks/native_cpp_gen.nbconvert.html delete mode 100644 benchmarks/numexpr.html delete mode 100644 benchmarks/pairwise.html delete mode 100644 benchmarks/simplify.html delete mode 100644 benchmarks/star.html create mode 100644 examples/.gitignore diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 047304d..3b501a8 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -1,3 +1,8 @@ +stages: + - style + - test_code + - test_docs + tests: tags: - python @@ -5,6 +10,7 @@ tests: - pyenv versions - pyenv local 2.7 3.4.6 3.5.3 3.6.2 - tox -r -vv -i https://cosmo-pypi.phys.ethz.ch/simple + stage: test_code style: tags: @@ -13,6 +19,8 @@ style: - pyenv local 2.7 3.4.6 3.5.3 3.6.2 - tox -e style -i https://cosmo-pypi.phys.ethz.ch/simple allow_failure: true + stage: style + docs: tags: @@ -20,6 +28,7 @@ docs: script: - pyenv local 2.7 3.4.6 3.5.3 3.6.2 - tox -e docs -i https://cosmo-pypi.phys.ethz.ch/simple + stage: test_docs publish_docs: tags: @@ -28,3 +37,4 @@ publish_docs: - pyenv local 2.7 3.4.6 3.5.3 3.6.2 - tox -e publish_docs -i https://cosmo-pypi.phys.ethz.ch/simple when: manual + only: master diff --git a/benchmarks/.gitignore b/benchmarks/.gitignore index c408179..f4679ed 100644 --- a/benchmarks/.gitignore +++ b/benchmarks/.gitignore @@ -6,4 +6,5 @@ /util.py /.ipynb_checkpoints/ hope/ +*.html diff --git a/benchmarks/HPC Python.html b/benchmarks/HPC Python.html deleted file mode 100644 index 5e7baad..0000000 --- a/benchmarks/HPC Python.html +++ /dev/null @@ -1,13316 +0,0 @@ - - - -HPC Python - - - - - - - - - - - - - - - - - - - - -
-
- -
-
-
In [1]:
-
-
-
import hope
-hope.config.optimize = True
-hope.config.keeptemp = True
-import numpy as np
-from matplotlib import pyplot as plt
-import warnings
-# warnings.filterwarnings("ignore", category=UserWarning) 
-
-
-%pylab inline
-
- -
-
-
- -
-
- - -
- -
- - -
-
Populating the interactive namespace from numpy and matplotlib
-
-
-
- -
-
- -
-
-
-
-
-

Python Optimizations

-
-
-
-
-
-
-
-

Avoid memory allocations

-
-
-
-
-
-
-
-

zombie apocalypse modeling from http://wiki.scipy.org/Cookbook/Zombie_Apocalypse_ODEINT

- -
-
-
-
-
-
In [2]:
-
-
-
from scipy.integrate import odeint
-
-P, d, B, G, A = 0, 0.0001, 0.0095, 0.0001, 0.0001
-
-def f_nat(y, t):
-    dy = np.empty(3)
-    dy[0] = P - B*y[0]*y[1] - d*y[0]
-    dy[1] = B*y[0]*y[1] + G*y[2] - A*y[0]*y[1]
-    dy[2] = d*y[0] + A*y[0]*y[1] - G*y[2]
-    return dy
-
-y0, t = np.array([500., .001, 0.]), np.linspace(0, 5., 1000)
-
- -
-
-
- -
-
-
-
In [3]:
-
-
-
soln = odeint(f_nat, y0, t)
-plt.figure()
-plt.plot(t, soln[:, 0], label='Living')
-plt.plot(t, soln[:, 1], label='Zombies')
-plt.xlabel('Days from outbreak')
-plt.ylabel('Population')
-plt.legend(loc=0)
-plt.show()
-
- -
-
-
- -
-
- - -
- -
- - - - -
- -
- -
- -
-
- -
-
-
-
In [4]:
-
-
-
@hope.jit
-def f_hope(y, t, P, d, B, G, A):
-    dy = np.empty(3)
-    dy[0] = P - B*y[0]*y[1] - d*y[0]
-    dy[1] = B*y[0]*y[1] + G*y[2] - A*y[0]*y[1]
-    dy[2] = d*y[0] + A*y[0]*y[1] - G*y[2]
-    return dy
-
-@hope.jit
-def f_opt(y, t, dy, P, d, B, G, A):
-    dy[0] = P - B*y[0]*y[1] - d*y[0]
-    dy[1] = B*y[0]*y[1] + G*y[2] - A*y[0]*y[1]
-    dy[2] = d*y[0] + A*y[0]*y[1] - G*y[2]
-    return dy
-
-dy = np.empty(3)
-print("native python")
-%timeit odeint(f_nat, y0, t)
-print("hope")
-%timeit odeint(f_hope, y0, t, args=(P, d, B, G, A))
-print("hope without allocation")
-%timeit odeint(f_opt, y0, t, args=(dy, P, d, B, G, A))
-
- -
-
-
- -
-
- - -
- -
- - -
-
native python
-1.62 ms ± 43.1 µs per loop (mean ± std. dev. of 7 runs, 1000 loops each)
-hope
-353 µs ± 41.9 µs per loop (mean ± std. dev. of 7 runs, 1 loop each)
-hope without allocation
-238 µs ± 15.4 µs per loop (mean ± std. dev. of 7 runs, 1 loop each)
-
-
-
- -
-
- -
-
-
-
-
-

Approximate expensive functions

-
-
-
-
-
-
-
-

tanh

-
-
-
-
-
-
In [5]:
-
-
-
def tanhpoly(x):
-    a = np.fabs(x)
-    b = 1.26175667589988239 + a * (-0.54699348440059470 + a * 2.66559097474027817)
-    return (b * x) / (b * a + 1)
-
-x = np.linspace(-10, 10, 10000)
-
-plt.subplot(2, 1,1)
-plt.plot(x, tanhpoly(x), label="approx")
-plt.plot(x, np.tanh(x), label="tanh")
-plt.ylabel('Tanh(x)')
-plt.legend()
-
-plt.subplot(2, 1,2)
-plt.semilogy()
-plt.plot(x, np.fabs(tanhpoly(x)- np.tanh(x)))
-plt.ylabel('Absolute Error')
-plt.show()
-
- -
-
-
- -
-
- - -
- -
- - - - -
- -
- -
- -
-
- -
-
-
-
In [6]:
-
-
-
@hope.jit
-def tanh_hope(x, y):
-    y[:] = np.tanh(x)
-
-@hope.jit
-def tanhpoly_hope(x, y):
-    a = np.fabs(x)
-    b = 1.26175667589988239 + a * (-0.54699348440059470 + a * 2.66559097474027817)
-    y[:] = (b * x) / (b * a + 1)
-
-y = np.empty_like(x)
-
-print("numpy tanh")
-%timeit tanh_hope(x, y)
-print("polynomial approximation")
-%timeit tanhpoly_hope(x, y)
-
- -
-
-
- -
-
- - -
- -
- - -
-
numpy tanh
-145 µs ± 6.65 µs per loop (mean ± std. dev. of 7 runs, 1 loop each)
-polynomial approximation
-25.8 µs ± 6.41 µs per loop (mean ± std. dev. of 7 runs, 1 loop each)
-
-
-
- -
-
- -
-
-
-
-
-

exp

-
-
-
-
-
-
In [7]:
-
-
-
@hope.jit
-def expapprox(x, y):
-    y[:] = hope.exp(x)
-    return y
-
-x = np.linspace(-16, 0, 10000)
-y = np.empty_like(x)
-
-plt.subplot(3, 1, 1)
-plt.plot(x, expapprox(x, y), label="approx")
-plt.plot(x, np.exp(x), label="exp")
-plt.ylabel('Exp(x)')
-plt.legend()
-
-plt.subplot(3, 1, 2)
-plt.semilogy()
-plt.plot(x, np.fabs(expapprox(x, y)- np.exp(x)) + np.finfo(np.float64).resolution)
-plt.ylabel('Absolute Error')
-
-plt.subplot(3, 1, 3)
-plt.semilogy()
-plt.plot(x, np.fabs(expapprox(x, y)- np.exp(x)) / np.exp(x) + np.finfo(np.float64).resolution)
-plt.ylabel('Relative Error')
-plt.show()
-
- -
-
-
- -
-
- - -
- -
- - - - -
- -
- -
- -
-
- -
-
-
-
In [8]:
-
-
-
@hope.jit
-def exp_hope(x, y):
-    y[:] = np.exp(x)
-
-@hope.jit
-def exppow_hope(x, y):
-    y[:] = hope.exp(x)
-    
-y = np.empty_like(x)
-
-print("numpy exp")
-%timeit exp_hope(x, y)
-print("polynomial exp")
-%timeit exppow_hope(x, y)
-
- -
-
-
- -
-
- - -
- -
- - -
-
numpy exp
-65.6 µs ± 492 ns per loop (mean ± std. dev. of 7 runs, 1 loop each)
-polynomial exp
-19.5 µs ± 701 ns per loop (mean ± std. dev. of 7 runs, 1 loop each)
-
-
-
- -
-
- -
-
-
-
-
-

Avoid floating point powers

-
-
-
-
-
-
In [9]:
-
-
-
def native_pow(x, y):
-    y[:] = 16.11 - 3./(3. * x**2) + 0.5 *(0.4651 * x**-3 + 43.44) * x**-3 + 4. * x**-4
-                                              
-@hope.jit
-def float_pow(x, y):
-    y[:] = 16.11 - 3./(3. * x**2.) + 0.5 *(0.4651 * x**-3. + 43.44) * x**-3. + 4. * x**-4.
-
-@hope.jit
-def int_pow(x, y):
-    y[:] = 16.11 - 3./(3. * x**2) + 0.5 *(0.4651 * x**-3 + 43.44) * x**-3 + 4. * x**-4
-
-x = np.linspace(-10, 10, 10000)
-y = np.empty_like(x)
-
-print("native python")
-%timeit native_pow(x, y)
-print("hope float power")
-%timeit float_pow(x, y)
-print("hope integer power")
-%timeit int_pow(x, y)
-
- -
-
-
- -
-
- - -
- -
- - -
-
native python
-984 µs ± 26.3 µs per loop (mean ± std. dev. of 7 runs, 1000 loops each)
-hope float power
-Integer exponent as flaot: x.d[:x@0]**(-4.0)
-Integer exponent as flaot: x.d[:x@0]**(-3.0)
-Integer exponent as flaot: x.d[:x@0]**(-6.0)
-Integer exponent as flaot: x.d[:x@0]**(-2.0)
-1.13 ms ± 111 µs per loop (mean ± std. dev. of 7 runs, 1 loop each)
-hope integer power
-92.6 µs ± 3.71 µs per loop (mean ± std. dev. of 7 runs, 1 loop each)
-
-
-
- -
-
- -
-
-
-
-
-

Improve interpolation

-
-
-
-
-
-
In [10]:
-
-
-
X = np.empty(1100)
-X[:1024] = np.linspace(-1., 1., 1024)
-X[1024:] = X[1023] + X[1:77] - X[0]
-SN = np.sin(X)
-CN = np.cos(X)
-
-@hope.jit
-def approx(x, sn, cn, X, SN, CN):
-    for i in range(100):
-        sn[i] = np.interp(x[i], X, SN)
-        cn[i] = np.interp(x[i], X, CN)
-
-@hope.jit
-def approx_opt(x, sn, cn, X, SN, CN):
-    for i in range(100):
-        f = (x[i] - X[0]) / (X[1024] - X[0]) * 1024.
-        g = np.floor(f)
-        j = np.int_(g)
-        a = f - g
-        sn[i] = (1 - a) * SN[j] + a * SN[j + 1]
-        cn[i] = (1 - a) * CN[j] + a * CN[j + 1]
-
-x = 2. * random_sample(100) - 1
-sn = np.empty_like(x)
-cn = np.empty_like(x)
-
-%timeit approx(x, sn, cn, X, SN, CN)
-%timeit approx_opt(x, sn, cn, X, SN, CN)
-
- -
-
-
- -
-
- - -
- -
- - -
-
9.44 µs ± 1.01 µs per loop (mean ± std. dev. of 7 runs, 1 loop each)
-1.35 µs ± 595 ns per loop (mean ± std. dev. of 7 runs, 1 loop each)
-
-
-
- -
-
- -
-
-
- - - - - - diff --git a/benchmarks/fibonacci.html b/benchmarks/fibonacci.html deleted file mode 100644 index f11ceef..0000000 --- a/benchmarks/fibonacci.html +++ /dev/null @@ -1,12038 +0,0 @@ - - - -fibonacci - - - - - - - - - - - - - - - - - - - - -
-
- -
-
-
In [1]:
-
-
-
from util import perf_comp_data
-
- -
-
-
- -
-
-
-
In [2]:
-
-
-
def fib(n):
-    if n < 2:
-        return n
-    return fib(n - 1) + fib(n - 2)
-
-assert fib(20) == 6765
-
- -
-
-
- -
-
-
-
In [3]:
-
-
-
from hope import jit
-
-hope_fib = jit(fib)
-
-assert hope_fib(20) == 6765
-
- -
-
-
- -
-
-
-
In [4]:
-
-
-
from numba import jit
-
-numba_fib = jit(fib)
-assert numba_fib(20) == 6765
-
- -
-
-
- -
-
-
-
In [5]:
-
-
-
print("python")
-%timeit fib(20)
-print("hope 1")
-%timeit hope_fib(20)
-print("numba")
-%timeit numba_fib(20)
-
- -
-
-
- -
-
- - -
- -
- - -
-
python
-3.55 ms ± 109 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)
-hope 1
-44.5 µs ± 884 ns per loop (mean ± std. dev. of 7 runs, 10000 loops each)
-numba
-3.49 ms ± 58.6 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)
-
-
-
- -
-
- -
-
-
-
In [6]:
-
-
-
n=20
-perf_comp_data(["fib", "hope_fib", "numba_fib"], 3*["n"])
-
- -
-
-
- -
-
- - -
- -
- - -
-
function: hope_fib            , av. time sec:   0.00003980, min. time sec:   0.00003974, relative:       1.0
-function: numba_fib           , av. time sec:   0.00370073, min. time sec:   0.00327747, relative:      93.0
-function: fib                 , av. time sec:   0.00424831, min. time sec:   0.00329386, relative:     106.7
-
-
-
- -
-
- -
-
-
-
In [7]:
-
-
-
numba_fib.inspect_types()
-
- -
-
-
- -
-
- - -
- -
- - -
-
fib (int64,)
---------------------------------------------------------------------------------
-# File: <ipython-input-2-24febe657cfb>
-# --- LINE 1 --- 
-# label 0
-#   del $const0.2
-
-def fib(n):
-
-    # --- LINE 2 --- 
-    #   n = arg(0, name=n)  :: pyobject
-    #   $const0.2 = const(int, 2)  :: pyobject
-    #   $0.3 = n < $const0.2  :: pyobject
-    #   branch $0.3, 12, 16
-    # label 12
-    #   del $0.3
-    #   del n
-
-    if n < 2:
-
-        # --- LINE 3 --- 
-        #   $12.2 = cast(value=n)  :: pyobject
-        #   return $12.2
-        # label 16
-        #   del $0.3
-        #   del $const16.3
-        #   del $16.4
-        #   del $16.1
-        #   del n
-        #   del $const16.8
-        #   del $16.9
-        #   del $16.6
-        #   del $16.5
-        #   del $16.10
-        #   del $16.11
-
-        return n
-
-    # --- LINE 4 --- 
-    #   $16.1 = global(fib: <function fib at 0x10554e598>)  :: pyobject
-    #   $const16.3 = const(int, 1)  :: pyobject
-    #   $16.4 = n - $const16.3  :: pyobject
-    #   $16.5 = call $16.1($16.4, kws=[], args=[Var($16.4, <ipython-input-2-24febe657cfb> (4))], func=$16.1, vararg=None)  :: pyobject
-    #   $16.6 = global(fib: <function fib at 0x10554e598>)  :: pyobject
-    #   $const16.8 = const(int, 2)  :: pyobject
-    #   $16.9 = n - $const16.8  :: pyobject
-    #   $16.10 = call $16.6($16.9, kws=[], args=[Var($16.9, <ipython-input-2-24febe657cfb> (4))], func=$16.6, vararg=None)  :: pyobject
-    #   $16.11 = $16.5 + $16.10  :: pyobject
-    #   $16.12 = cast(value=$16.11)  :: pyobject
-    #   return $16.12
-
-    return fib(n - 1) + fib(n - 2)
-
-
-================================================================================
-
-
-
- -
-
- -
-
-
-
In [8]:
-
-
-
 
-
- -
-
-
- -
-
-
- - - - - - diff --git a/benchmarks/julialang.org.html b/benchmarks/julialang.org.html deleted file mode 100644 index 19ee7e5..0000000 --- a/benchmarks/julialang.org.html +++ /dev/null @@ -1,12617 +0,0 @@ - - - -julialang.org - - - - - - - - - - - - - - - - - - - - -
-
- -
-
-
In [1]:
-
-
-
import hope
-hope.config.optimize = True
-hope.config.verbose = True
-hope.config.keeptemp = True
-hope.config.prefix = "hope"
-import numba
-import numpy as np
-from util import perf_comp_data
-from native_util import load
-%load_ext Cython
-%load_ext version_information
-%version_information numpy, Cython, numba, hope
-
- -
-
-
- -
-
- - -
- -
Out[1]:
- - - -
-
SoftwareVersion
Python3.5.1 64bit [GCC 4.2.1 (Apple Inc. build 5666) (dot 3)]
IPython6.1.0
OSDarwin 15.6.0 x86_64 i386 64bit
numpy1.13.1
Cython0.26
numba0.34.0
hope0.6.1
Mon Sep 04 16:51:44 2017 CEST
-
- -
- -
-
- -
-
-
-
-
-

fibonacci

-
-
-
-
-
-
In [2]:
-
-
-
def fib(n):
-    if n<2:
-        return n
-    return fib(n-1)+fib(n-2)
-hope_fib = hope.jit(fib)
-numba_fib = numba.jit(fib, nopython=False)
-
-native_fib_mod = load("fib")
-native_fib = native_fib_mod.run 
-
-n=20
-assert fib(20) == 6765
-assert hope_fib(20) == 6765
-assert numba_fib(20) == 6765
-assert native_fib(20) == 6765
-
- -
-
-
- -
-
- - -
- -
- - -
-
running build_ext
-building 'fib' extension
-C compiler: /usr/bin/clang -fno-strict-aliasing -Wsign-compare -Wunreachable-code -fno-common -dynamic -DNDEBUG -g -fwrapv -O3 -Wall -Wstrict-prototypes -arch i386 -arch x86_64 -g
-
-compile options: '-I/Users/uweschmitt/Projects/hope/venv3/lib/python3.5/site-packages/numpy/core/include -I/Users/uweschmitt/Projects/hope/venv3/include -I/Library/Frameworks/Python.framework/Versions/3.5/include/python3.5m -c'
-extra options: '-Wall -Wno-unused-variable -march=native -stdlib=libc++ -std=c++11 -Wno-unreachable-code'
-clang: ././src/fib.cpp
-/usr/bin/clang++ -bundle -undefined dynamic_lookup -arch i386 -arch x86_64 -g ./src/fib.o -o ./fib.cpython-35m-darwin.so
-
-fib(int64 n)
-	if (n.J < 2.J) {
-		return n.J
-	}
-	return (fib((n.J - 1.J)) + fib((n.J - 2.J)))
-
-Compiling following functions:
-fib(int64 n)
-running build_ext
-building 'fib_181039868ed022600dc3388e38604df736dff187257d5d6ae4c4d3ea_0' extension
-C compiler: /usr/bin/clang -fno-strict-aliasing -Wsign-compare -Wunreachable-code -fno-common -dynamic -DNDEBUG -g -fwrapv -O3 -Wall -arch i386 -arch x86_64 -g
-
-compile options: '-I/Users/uweschmitt/Projects/hope/venv3/lib/python3.5/site-packages/numpy/core/include -I/Users/uweschmitt/Projects/hope/venv3/include -I/Library/Frameworks/Python.framework/Versions/3.5/include/python3.5m -c'
-extra options: '-Wall -Wno-unused-variable -march=native -stdlib=libc++ -std=c++11 -Wno-unreachable-code'
-clang: /var/folders/k8/zfp7dvcs1m326gz1brql1tv80000gn/T/hopeimwu5wdk/fib_181039868ed022600dc3388e38604df736dff187257d5d6ae4c4d3ea_0.cpp
-/usr/bin/clang++ -bundle -undefined dynamic_lookup -arch i386 -arch x86_64 -g /var/folders/k8/zfp7dvcs1m326gz1brql1tv80000gn/T/hopeimwu5wdk/fib_181039868ed022600dc3388e38604df736dff187257d5d6ae4c4d3ea_0.o -o /var/folders/k8/zfp7dvcs1m326gz1brql1tv80000gn/T/hopeimwu5wdk/fib_181039868ed022600dc3388e38604df736dff187257d5d6ae4c4d3ea_0.cpython-35m-darwin.so
-
-
-
-
- -
-
- -
-
-
-
In [3]:
-
-
-
%%cython
-
-cimport cython
-
-@cython.boundscheck(False)
-@cython.wraparound(False)
-cpdef int cython_fib(int n):
-    if n<2:
-        return n
-    return cython_fib(n-1)+cython_fib(n-2)
-
-assert cython_fib(20) == 6765
-
- -
-
-
- -
-
-
-
In [4]:
-
-
-
%timeit fib(20)
-%timeit hope_fib(20)
-%timeit numba_fib(20)
-%timeit cython_fib(20)
-%timeit native_fib(20)
-
- -
-
-
- -
-
- - -
- -
- - -
-
3.4 ms ± 269 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)
-44.4 µs ± 2.57 µs per loop (mean ± std. dev. of 7 runs, 10000 loops each)
-3.57 ms ± 123 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)
-46.2 µs ± 1.17 µs per loop (mean ± std. dev. of 7 runs, 10000 loops each)
-44.5 µs ± 1.15 µs per loop (mean ± std. dev. of 7 runs, 10000 loops each)
-
-
-
- -
-
- -
-
-
-
In [5]:
-
-
-
perf_comp_data(["fib", "hope_fib", "numba_fib", "cython_fib", "native_fib"],
-               5*["n"])
-
- -
-
-
- -
-
- - -
- -
- - -
-
function: hope_fib            , av. time sec:   0.00003974, min. time sec:   0.00003969, relative:       1.0
-function: native_fib          , av. time sec:   0.00004625, min. time sec:   0.00004621, relative:       1.2
-function: cython_fib          , av. time sec:   0.00004649, min. time sec:   0.00004646, relative:       1.2
-function: numba_fib           , av. time sec:   0.00333900, min. time sec:   0.00315241, relative:      84.0
-function: fib                 , av. time sec:   0.00345827, min. time sec:   0.00327053, relative:      87.0
-
-
-
- -
-
- -
-
-
-
-
-

quicksort

-
-
-
-
-
-
In [6]:
-
-
-
def qsort_kernel(a, lo, hi):
-    i = lo
-    j = hi
-    if False: return a
-    while i < hi:
-        pivot = a[(lo+hi) // 2]
-        while i <= j:
-            while a[i] < pivot:
-                i += 1
-            while a[j] > pivot:
-                j -= 1
-            if i <= j:
-                tmp = a[i]
-                a[i] = a[j]
-                a[j] = tmp
-                i += 1
-                j -= 1
-        if lo < j:
-            qsort_kernel(a, lo, j)
-        lo = i
-        j = hi
-    return a
-
-hope_qsort_kernel = hope.jit(qsort_kernel)
-numba_qsort_kernel = numba.jit(qsort_kernel)
-
-native_qsort_kernel_mod = load("qsort_kernel")
-native_qsort_kernel = native_qsort_kernel_mod.run
-
- -
-
-
- -
-
- - -
- -
- - -
-
running build_ext
-building 'qsort_kernel' extension
-C compiler: /usr/bin/clang -fno-strict-aliasing -Wsign-compare -Wunreachable-code -fno-common -dynamic -DNDEBUG -g -fwrapv -O3 -Wall -arch i386 -arch x86_64 -g
-
-compile options: '-I/Users/uweschmitt/Projects/hope/venv3/lib/python3.5/site-packages/numpy/core/include -I/Users/uweschmitt/Projects/hope/venv3/include -I/Library/Frameworks/Python.framework/Versions/3.5/include/python3.5m -c'
-extra options: '-Wall -Wno-unused-variable -march=native -stdlib=libc++ -std=c++11 -Wno-unreachable-code'
-clang: ././src/qsort_kernel.cpp
-/usr/bin/clang++ -bundle -undefined dynamic_lookup -arch i386 -arch x86_64 -g ./src/qsort_kernel.o -o ./qsort_kernel.cpython-35m-darwin.so
-
-
-
-
- -
-
- -
-
-
-
In [7]:
-
-
-
def numpy_qsort_kernel(a, lo, hi):
-    np.sort(a)
-
- -
-
-
- -
-
-
-
In [8]:
-
-
-
%%cython
-
-cimport cython
-import numpy as np
-cimport numpy as np
-
-@cython.boundscheck(False)
-@cython.wraparound(False)
-cdef _cython_qsort_kernel(np.double_t * a, int lo, int hi):
-    cdef int i = lo
-    cdef int j = hi
-    cdef double pivot = 0
-    cdef double tmp = 0.0
-    if False: return a
-    while i < hi:
-        pivot = a[(lo+hi) // 2]
-        while i <= j:
-            while a[i] < pivot:
-                i += 1
-            while a[j] > pivot:
-                j -= 1
-            if i <= j:
-                tmp = a[i]
-                a[i] = a[j]
-                a[j] = tmp
-                i += 1
-                j -= 1
-        if lo < j:
-            _cython_qsort_kernel(a, lo, j)
-        lo = i
-        j = hi
-
-def cython_qsort_kernel(np.ndarray[np.double_t, ndim=1] a, int lo, int hi):
-    _cython_qsort_kernel(<np.double_t*> a.data, lo, hi)
-    return a
-
- -
-
-
- -
-
-
-
In [9]:
-
-
-
lst = np.random.random(5000)
-
- -
-
-
- -
-
-
-
In [10]:
-
-
-
psorted = qsort_kernel(lst.copy(), 0, len(lst)-1)
-hsorted = hope_qsort_kernel(lst.copy(), 0, len(lst)-1)
-#nsorted = numba_qsort_kernel(lst.copy(), 0, len(lst)-1)
-csorted = cython_qsort_kernel(lst.copy(), 0, len(lst)-1)
-nasorted = native_qsort_kernel(lst.copy(), 0, len(lst)-1)
-
- -
-
-
- -
-
- - -
- -
- - -
-
qsort_kernel(float64^1 a, int64 lo, int64 hi)
-	new i.J
-	i.J = lo.J
-	new j.J
-	j.J = hi.J
-	if False.P {
-		return a.d[:a@0]
-	}
-	while (i.J < hi.J) {
-		new pivot.d
-		pivot.d = a.d[((lo.J + hi.J) // 2.J)]
-		while (i.J <= j.J) {
-			while (a.d[i.J] < pivot.d) {
-				i.J += 1.J
-			}
-			while (a.d[j.J] > pivot.d) {
-				j.J -= 1.J
-			}
-			if (i.J <= j.J) {
-				new tmp.d
-				tmp.d = a.d[i.J]
-				a.d[i.J] = a.d[j.J]
-				a.d[j.J] = tmp.d
-				i.J += 1.J
-				j.J -= 1.J
-			}
-		}
-		if (lo.J < j.J) {
-			qsort_kernel(a.d[:a@0], lo.J, j.J)
-		}
-		lo.J = i.J
-		j.J = hi.J
-	}
-	return a.d[:a@0]
-
-Compiling following functions:
-qsort_kernel(float64^1 a, int64 lo, int64 hi)
-running build_ext
-building 'qsort_kernel_674d9af4cba0defec637089ed771c5f43e2e28f5e25a2c4f0ec12559_0' extension
-C compiler: /usr/bin/clang -fno-strict-aliasing -Wsign-compare -Wunreachable-code -fno-common -dynamic -DNDEBUG -g -fwrapv -O3 -Wall -arch i386 -arch x86_64 -g
-
-compile options: '-I/Users/uweschmitt/Projects/hope/venv3/lib/python3.5/site-packages/numpy/core/include -I/Users/uweschmitt/Projects/hope/venv3/include -I/Library/Frameworks/Python.framework/Versions/3.5/include/python3.5m -c'
-extra options: '-Wall -Wno-unused-variable -march=native -stdlib=libc++ -std=c++11 -Wno-unreachable-code'
-clang: /var/folders/k8/zfp7dvcs1m326gz1brql1tv80000gn/T/hope9omwep_j/qsort_kernel_674d9af4cba0defec637089ed771c5f43e2e28f5e25a2c4f0ec12559_0.cpp
-/usr/bin/clang++ -bundle -undefined dynamic_lookup -arch i386 -arch x86_64 -g /var/folders/k8/zfp7dvcs1m326gz1brql1tv80000gn/T/hope9omwep_j/qsort_kernel_674d9af4cba0defec637089ed771c5f43e2e28f5e25a2c4f0ec12559_0.o -o /var/folders/k8/zfp7dvcs1m326gz1brql1tv80000gn/T/hope9omwep_j/qsort_kernel_674d9af4cba0defec637089ed771c5f43e2e28f5e25a2c4f0ec12559_0.cpython-35m-darwin.so
-
-
-
-
- -
-
- -
-
-
-
In [11]:
-
-
-
assert np.all(psorted[:-1] <= psorted[1:])
-#assert np.all(hope_qsort_kernel[:-1] <= hope_qsort_kernel[1:])
-#assert np.all(numba_qsort_kernel[:-1] <= numba_qsort_kernel[1:])
-#assert np.all(cython_qsort_kernel[:-1] <= cython_qsort_kernel[1:])
-
-%timeit qsort_kernel(lst.copy(), 0, len(lst)-1)
-%timeit hope_qsort_kernel(lst.copy(), 0, len(lst)-1)
-#%timeit numba_qsort_kernel(lst.copy(), 0, len(lst)-1)
-%timeit cython_qsort_kernel(lst.copy(), 0, len(lst)-1)
-%timeit native_qsort_kernel(lst.copy(), 0, len(lst)-1)
-%timeit np.sort(lst.copy())
-
- -
-
-
- -
-
- - -
- -
- - -
-
26.6 ms ± 380 µs per loop (mean ± std. dev. of 7 runs, 10 loops each)
-379 µs ± 7.76 µs per loop (mean ± std. dev. of 7 runs, 1000 loops each)
-342 µs ± 4.95 µs per loop (mean ± std. dev. of 7 runs, 1000 loops each)
-325 µs ± 7.95 µs per loop (mean ± std. dev. of 7 runs, 1000 loops each)
-272 µs ± 4.69 µs per loop (mean ± std. dev. of 7 runs, 1000 loops each)
-
-
-
- -
-
- -
-
-
-
In [12]:
-
-
-
a = lst.copy()
-
-lo = 0
-hi = len(lst)-1
-
-perf_comp_data(["hope_qsort_kernel", 
-                "qsort_kernel", 
-                #"numpy_qsort_kernel", 
-                "cython_qsort_kernel", 
-                "native_qsort_kernel"],
-               5*["a, lo, hi"], rep=100, extra_setup="from __main__ import lst;a = lst.copy()")
-
- -
-
-
- -
-
- - -
- -
- - -
-
function: native_qsort_kernel , av. time sec:   0.00030064, min. time sec:   0.00029132, relative:       1.0
-function: cython_qsort_kernel , av. time sec:   0.00031280, min. time sec:   0.00031010, relative:       1.0
-function: hope_qsort_kernel   , av. time sec:   0.00034323, min. time sec:   0.00033363, relative:       1.1
-function: qsort_kernel        , av. time sec:   0.02440728, min. time sec:   0.02243393, relative:      81.2
-
-
-
- -
-
- -
-
-
-
-
-

pi sum

-
-
-
-
-
-
In [13]:
-
-
-
def pisum():
-    for j in range(1, 501):
-        sum = 0.0
-        f = 0.0
-        for k in range(1, 10001):
-            sum += 1.0/(k*k)
-    return sum
-
-def pisum_opt():
-    for j in range(1, 501):
-        sum = 0.0
-        f = 0.0
-        for k in range(1, 10001):
-            f += 1.
-            sum += 1.0/(f*f)
-    return sum
-
-hope_pisum = hope.jit(pisum)
-hope_pisum_opt = hope.jit(pisum_opt)
-
-numba_pisum = numba.jit(pisum, nopython=True)
-numba_pisum_opt = numba.jit(pisum_opt, nopython=True)
-
-native_pisum_mod = load("pisum")
-native_pisum = native_pisum_mod.run
-
-native_pisum_opt_mod = load("pisum_opt")
-native_pisum_opt = native_pisum_opt_mod.run
-
-
-assert abs(pisum()-1.644834071848065) < 1e-6
-assert abs(hope_pisum()-1.644834071848065) < 1e-6
-assert abs(hope_pisum_opt()-1.644834071848065) < 1e-6
-assert abs(numba_pisum()-1.644834071848065) < 1e-6
-assert abs(native_pisum()-1.644834071848065) < 1e-6
-assert abs(native_pisum_opt()-1.644834071848065) < 1e-6
-
- -
-
-
- -
-
- - -
- -
- - -
-
running build_ext
-building 'pisum' extension
-C compiler: /usr/bin/clang -fno-strict-aliasing -Wsign-compare -Wunreachable-code -fno-common -dynamic -DNDEBUG -g -fwrapv -O3 -Wall -arch i386 -arch x86_64 -g
-
-compile options: '-I/Users/uweschmitt/Projects/hope/venv3/lib/python3.5/site-packages/numpy/core/include -I/Users/uweschmitt/Projects/hope/venv3/include -I/Library/Frameworks/Python.framework/Versions/3.5/include/python3.5m -c'
-extra options: '-Wall -Wno-unused-variable -march=native -stdlib=libc++ -std=c++11 -Wno-unreachable-code'
-clang: ././src/pisum.cpp
-/usr/bin/clang++ -bundle -undefined dynamic_lookup -arch i386 -arch x86_64 -g ./src/pisum.o -o ./pisum.cpython-35m-darwin.so
-
-running build_ext
-building 'pisum_opt' extension
-C compiler: /usr/bin/clang -fno-strict-aliasing -Wsign-compare -Wunreachable-code -fno-common -dynamic -DNDEBUG -g -fwrapv -O3 -Wall -arch i386 -arch x86_64 -g
-
-compile options: '-I/Users/uweschmitt/Projects/hope/venv3/lib/python3.5/site-packages/numpy/core/include -I/Users/uweschmitt/Projects/hope/venv3/include -I/Library/Frameworks/Python.framework/Versions/3.5/include/python3.5m -c'
-extra options: '-Wall -Wno-unused-variable -march=native -stdlib=libc++ -std=c++11 -Wno-unreachable-code'
-clang: ././src/pisum_opt.cpp
-/usr/bin/clang++ -bundle -undefined dynamic_lookup -arch i386 -arch x86_64 -g ./src/pisum_opt.o -o ./pisum_opt.cpython-35m-darwin.so
-
-pisum()
-	new sum.D
-	for j.l in (1.J:501.J) {
-		sum.D = 0.0.D
-		f.D = 0.0.D
-		for k.l in (1.J:10001.J) {
-			sum.D += (1.0.D / (k.l * k.l))
-		}
-	}
-	return sum.D
-
-Compiling following functions:
-pisum()
-running build_ext
-building 'pisum_49f15c96f2c4d5ce51371d6a5362b67ac5531087a5dd1885eaba960b_0' extension
-C compiler: /usr/bin/clang -fno-strict-aliasing -Wsign-compare -Wunreachable-code -fno-common -dynamic -DNDEBUG -g -fwrapv -O3 -Wall -arch i386 -arch x86_64 -g
-
-compile options: '-I/Users/uweschmitt/Projects/hope/venv3/lib/python3.5/site-packages/numpy/core/include -I/Users/uweschmitt/Projects/hope/venv3/include -I/Library/Frameworks/Python.framework/Versions/3.5/include/python3.5m -c'
-extra options: '-Wall -Wno-unused-variable -march=native -stdlib=libc++ -std=c++11 -Wno-unreachable-code'
-clang: /var/folders/k8/zfp7dvcs1m326gz1brql1tv80000gn/T/hope7mwr_v7o/pisum_49f15c96f2c4d5ce51371d6a5362b67ac5531087a5dd1885eaba960b_0.cpp
-/usr/bin/clang++ -bundle -undefined dynamic_lookup -arch i386 -arch x86_64 -g /var/folders/k8/zfp7dvcs1m326gz1brql1tv80000gn/T/hope7mwr_v7o/pisum_49f15c96f2c4d5ce51371d6a5362b67ac5531087a5dd1885eaba960b_0.o -o /var/folders/k8/zfp7dvcs1m326gz1brql1tv80000gn/T/hope7mwr_v7o/pisum_49f15c96f2c4d5ce51371d6a5362b67ac5531087a5dd1885eaba960b_0.cpython-35m-darwin.so
-
-pisum_opt()
-	new sum.D
-	for j.l in (1.J:501.J) {
-		sum.D = 0.0.D
-		new f.D
-		f.D = 0.0.D
-		for k.l in (1.J:10001.J) {
-			f.D += 1.0.D
-			sum.D += (1.0.D / (f.D * f.D))
-		}
-	}
-	return sum.D
-
-Compiling following functions:
-pisum_opt()
-running build_ext
-building 'pisum_opt_d21ddf605a07e3a4d8a460735cef49c69a69c967957f695741c0feae_0' extension
-C compiler: /usr/bin/clang -fno-strict-aliasing -Wsign-compare -Wunreachable-code -fno-common -dynamic -DNDEBUG -g -fwrapv -O3 -Wall -arch i386 -arch x86_64 -g
-
-compile options: '-I/Users/uweschmitt/Projects/hope/venv3/lib/python3.5/site-packages/numpy/core/include -I/Users/uweschmitt/Projects/hope/venv3/include -I/Library/Frameworks/Python.framework/Versions/3.5/include/python3.5m -c'
-extra options: '-Wall -Wno-unused-variable -march=native -stdlib=libc++ -std=c++11 -Wno-unreachable-code'
-clang: /var/folders/k8/zfp7dvcs1m326gz1brql1tv80000gn/T/hopetzu9xf6n/pisum_opt_d21ddf605a07e3a4d8a460735cef49c69a69c967957f695741c0feae_0.cpp
-/usr/bin/clang++ -bundle -undefined dynamic_lookup -arch i386 -arch x86_64 -g /var/folders/k8/zfp7dvcs1m326gz1brql1tv80000gn/T/hopetzu9xf6n/pisum_opt_d21ddf605a07e3a4d8a460735cef49c69a69c967957f695741c0feae_0.o -o /var/folders/k8/zfp7dvcs1m326gz1brql1tv80000gn/T/hopetzu9xf6n/pisum_opt_d21ddf605a07e3a4d8a460735cef49c69a69c967957f695741c0feae_0.cpython-35m-darwin.so
-
-
-
-
- -
-
- -
-
-
-
In [14]:
-
-
-
%%cython
-
-cimport cython
-
-@cython.boundscheck(False)
-@cython.wraparound(False)
-@cython.locals(f=float)
-@cython.cdivision(True)
-def cython_pisum():
-    cdef double sum = 0.0
-    cdef int j, k
-    for j in range(1, 501):
-        sum = 0.0
-        for k in range(1, 10001):
-            sum += 1.0/(k*k)
-    return sum
-
-@cython.boundscheck(False)
-@cython.wraparound(False)
-@cython.locals(f=float)
-@cython.cdivision(True)
-def cython_pisum_opt():
-    cdef double sum = 0.0
-    cdef int j, k
-    for j in range(1, 501):
-        sum = 0.0
-        f = 0.0
-        for k in range(1, 10001):
-            f += 1.
-            sum += 1.0/(f*f)
-    return sum
-
-
-assert abs(cython_pisum()-1.644834071848065) < 1e-6
-assert abs(cython_pisum_opt()-1.644834071848065) < 1e-6
-
- -
-
-
- -
-
-
-
In [15]:
-
-
-
%timeit pisum()
-%timeit pisum_opt()
-%timeit hope_pisum()
-%timeit hope_pisum_opt()
-%timeit numba_pisum()
-%timeit numba_pisum_opt()
-%timeit cython_pisum()
-%timeit cython_pisum_opt()
-%timeit native_pisum()
-%timeit native_pisum_opt()
-
- -
-
-
- -
-
- - -
- -
- - -
-
747 ms ± 12.2 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)
-729 ms ± 25.6 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)
-43.3 ms ± 2.83 ms per loop (mean ± std. dev. of 7 runs, 10 loops each)
-21.1 ms ± 359 µs per loop (mean ± std. dev. of 7 runs, 10 loops each)
-21.8 ms ± 812 µs per loop (mean ± std. dev. of 7 runs, 10 loops each)
-23 ms ± 947 µs per loop (mean ± std. dev. of 7 runs, 10 loops each)
-22.4 ms ± 148 µs per loop (mean ± std. dev. of 7 runs, 10 loops each)
-22.3 ms ± 562 µs per loop (mean ± std. dev. of 7 runs, 10 loops each)
-39.8 ms ± 476 µs per loop (mean ± std. dev. of 7 runs, 10 loops each)
-21.6 ms ± 503 µs per loop (mean ± std. dev. of 7 runs, 10 loops each)
-
-
-
- -
-
- -
-
-
-
In [16]:
-
-
-
perf_comp_data(["pisum", "pisum_opt", 
-                "hope_pisum", "hope_pisum_opt", 
-                "numba_pisum", "numba_pisum_opt", 
-                "cython_pisum", 
-                "cython_pisum_opt",
-                "native_pisum", "native_pisum_opt",], 
-                None, rep=100)
-
- -
-
-
- -
-
- - -
- -
- - -
-
function: cython_pisum        , av. time sec:   0.02062879, min. time sec:   0.02034618, relative:       1.0
-function: native_pisum_opt    , av. time sec:   0.02066824, min. time sec:   0.02035908, relative:       1.0
-function: numba_pisum_opt     , av. time sec:   0.02070580, min. time sec:   0.02033324, relative:       1.0
-function: numba_pisum         , av. time sec:   0.02073894, min. time sec:   0.02045512, relative:       1.0
-function: hope_pisum_opt      , av. time sec:   0.02078112, min. time sec:   0.02039398, relative:       1.0
-function: cython_pisum_opt    , av. time sec:   0.02091439, min. time sec:   0.02033318, relative:       1.0
-function: native_pisum        , av. time sec:   0.03938080, min. time sec:   0.03867311, relative:       1.9
-function: hope_pisum          , av. time sec:   0.03939245, min. time sec:   0.03843478, relative:       1.9
-function: pisum_opt           , av. time sec:   0.63299016, min. time sec:   0.60915709, relative:      30.7
-function: pisum               , av. time sec:   0.73068575, min. time sec:   0.69684398, relative:      35.4
-
-
-
- -
-
- -
-
-
- - - - - - diff --git a/benchmarks/native_cpp_gen.html b/benchmarks/native_cpp_gen.html deleted file mode 100644 index 06a4d75..0000000 --- a/benchmarks/native_cpp_gen.html +++ /dev/null @@ -1,14392 +0,0 @@ - - - -native_cpp_gen - - - - - - - - - - - - - - - - - - - - -
-
- -
-
-
-
-

IPython magic extension version_information

-
-
-
-
-
-
-
-

Use the '%version_information' IPython magic extension in a notebook to display information about which versions of dependency package that was used to run the notebook. -Installation.

-

Run

- -
pip install git+https://github.com/jrjohansson/version_information
-
-
-
-

to install this extension

- -
-
-
-
-
-
-
-

Installation

-
-
-
-
-
-
In [1]:
-
-
-
%load_ext version_information
-
- -
-
-
- -
-
-
-
In [2]:
-
-
-
%version_information
-
- -
-
-
- -
-
- - -
- -
Out[2]:
- - - -
-
SoftwareVersion
Python3.5.1 64bit [GCC 4.2.1 (Apple Inc. build 5666) (dot 3)]
IPython6.1.0
OSDarwin 15.6.0 x86_64 i386 64bit
Mon Sep 04 17:18:43 2017 CEST
-
- -
- -
-
- -
-
-
-
-
-

Native CPP codes

-
-
-
-
-
-
-
-

Fibonacci CPP

-
-
-
-
-
-
In [3]:
-
-
-
!mkdir -p src
-
- -
-
-
- -
-
-
-
In [4]:
-
-
-
%%file src/fib.cpp
-#define PY_ARRAY_UNIQUE_SYMBOL fkt_ARRAY_API
-#define NPY_NO_DEPRECATED_API NPY_1_7_API_VERSION
-#include <Python.h>
-#include <numpy/arrayobject.h>
-#include <numpy/arrayscalars.h>
-#include <cmath>
-#include <tuple>
-#include <numeric>
-#include <cstdint>
-#include <cstdlib>
-#include <exception>
-#include <functional>
-#include <type_traits>
-
-
-struct PyObj {
-    typedef PyObject * ptr_t;
-    typedef PyArrayObject * arrptr_t;
-    PyObj(): dec(false), ptr(NULL) {}
-    PyObj(ptr_t p): dec(false), ptr(p) {}
-    ~PyObj() { if(dec) Py_DECREF(ptr); }
-    PyObj & operator=(ptr_t p) { if(dec) Py_DECREF(ptr); ptr = p; dec = false; return *this; }
-    PyObj & incref(ptr_t p) { if(dec) Py_DECREF(ptr); ptr = p; dec = (p != NULL); return *this; }
-    operator bool() const { return ptr; }
-    operator ptr_t() const { return ptr; }
-    operator arrptr_t() const { return (arrptr_t)ptr; }
-    bool dec;
-    ptr_t ptr;
-};
-
-inline npy_int64 fib_J(
-      npy_int64 cn
-);
-inline npy_int64 fib_J(
-      npy_int64 cn
-) {
-    if (cn < 2) {
-        return cn;
-    }
-    return fib_J(cn - 1) + fib_J(cn - 2);    
-}
-
-#include <string>
-#include <sstream>
-#include <iostream>
-#include <cxxabi.h>
-#include <execinfo.h>
-#include <signal.h>
-
-void sighandler(int sig);
-
-void sighandler(int sig) {
-    std::ostringstream buffer;
-    buffer << "Abort by " << (sig == SIGSEGV ? "segfault" : "bus error") << std::endl;
-    void * stack[64];
-    std::size_t depth = backtrace(stack, 64);
-    if (!depth)
-        buffer << "  <empty stacktrace, possibly corrupt>" << std::endl;
-    else {
-        char ** symbols = backtrace_symbols(stack, depth);
-        for (std::size_t i = 1; i < depth; ++i) {
-            std::string symbol = symbols[i];
-                if (symbol.find_first_of(' ', 59) != std::string::npos) {
-                    std::string name = symbol.substr(59, symbol.find_first_of(' ', 59) - 59);
-                    int status;
-                    char * demangled = abi::__cxa_demangle(name.c_str(), NULL, NULL, &status);
-                    if (!status) {
-                        buffer << "    "
-                            << symbol.substr(0, 59)
-                            << demangled
-                            << symbol.substr(59 + name.size())
-                            << std::endl;
-                        free(demangled);
-                    } else
-                        buffer << "    " << symbol << std::endl;
-                } else
-                    buffer << "    " << symbol << std::endl;
-            }
-            free(symbols);
-        }
-        std::cerr << buffer.str();
-        std::exit(EXIT_FAILURE);
-    }
-
-
-extern "C" {
-
-    PyObject * create_signature;
-
-    struct sigaction slot;
-
-    PyObject * set_create_signature(PyObject * self, PyObject * args) {
-        if (!PyArg_ParseTuple(args, "O", &create_signature)) {
-            PyErr_SetString(PyExc_ValueError, "Invalid Argument to set_create_signature!");
-            return NULL;
-        }
-        Py_INCREF(create_signature);
-        memset(&slot, 0, sizeof(slot));
-        slot.sa_handler = &sighandler;
-        sigaction(SIGSEGV, &slot, NULL);
-        sigaction(SIGBUS, &slot, NULL);
-        Py_INCREF(Py_None);
-        return Py_None;
-    }
-
-    PyObject * run(PyObject * self, PyObject * args) {
-        {
-            PyObject * pn; npy_int64 cn;
-            if (
-                PyTuple_CheckExact(args) and PyTuple_GET_SIZE(args) == 1
-                and (pn = PyTuple_GET_ITEM(args, 0)) and PyLong_CheckExact(pn)
-            ) {
-                cn = PyLong_AS_LONG(pn);
-                try {
-                    return Py_BuildValue("l", fib_J(
-                          cn
-                    ));
-                } catch (...) {
-                    return NULL;
-                }
-            } else
-                PyErr_Clear();
-        }
-        PyObject * signatures = Py_BuildValue("(sO)", "gANdcQBdcQFjaG9wZS5fYXN0ClZhcmlhYmxlCnECKYFxA31xBChYBAAAAGRpbXNxBUsAWAUAAABk\ndHlwZXEGY2J1aWx0aW5zCmludApxB1gEAAAAbmFtZXEIWAEAAABucQl1YmFhLg==\n", args);
-        if (!signatures) {
-            PyErr_SetString(PyExc_ValueError, "Error building signature string for fib");
-            return NULL;
-        }
-        return PyObject_Call(create_signature, signatures, NULL);
-    }
-
-    PyMethodDef fibMethods[] = {
-        { "set_create_signature", set_create_signature, METH_VARARGS, "signal handler" },
-        { "run", run, METH_VARARGS, "module function" },
-        { NULL, NULL, 0, NULL }
-    };
-
-
-    static struct PyModuleDef fibmodule = {
-        PyModuleDef_HEAD_INIT,
-        "fib",
-        NULL,
-        -1,
-        fibMethods
-    };
-
-
-
-   PyMODINIT_FUNC PyInit_fib(void) {
-        import_array();
-        PyImport_ImportModule("numpy");
-        return PyModule_Create(&fibmodule);
-   }
-}
-
- -
-
-
- -
-
- - -
- -
- - -
-
Overwriting src/fib.cpp
-
-
-
- -
-
- -
-
-
-
-
-

Quicksort CPP

-
-
-
-
-
-
In [5]:
-
-
-
%%file src/qsort_kernel.cpp
-#define PY_ARRAY_UNIQUE_SYMBOL qsort_kernel_ARRAY_API
-#define NPY_NO_DEPRECATED_API NPY_1_7_API_VERSION
-#include <Python.h>
-#include <numpy/arrayobject.h>
-#include <numpy/arrayscalars.h>
-#include <cmath>
-#include <tuple>
-#include <numeric>
-#include <cstdint>
-#include <cstdlib>
-#include <exception>
-#include <functional>
-#include <type_traits>
-struct PyObj {
- typedef PyObject * ptr_t;
- typedef PyArrayObject * arrptr_t;
- PyObj(): dec(false), ptr(NULL) {}
- PyObj(ptr_t p): dec(false), ptr(p) {}
- ~PyObj() { if(dec) Py_DECREF(ptr); }
- PyObj & operator=(ptr_t p) { if(dec) Py_DECREF(ptr); ptr = p; dec = false; return *this; }
- PyObj & incref(ptr_t p) { if(dec) Py_DECREF(ptr); ptr = p; dec = (p != NULL); return *this; }
- operator bool() const { return ptr; }
- operator ptr_t() const { return ptr; }
- operator arrptr_t() const { return (arrptr_t)ptr; }
- bool dec;
- ptr_t ptr;
-};
-inline std::tuple<PyObject *, npy_intp const *, npy_double *> qsort_kernel_d1JJ(PyObject * pa,
-    npy_intp const * __restrict__ sa,
-                                         npy_double * __restrict__ ca,
-                                         npy_int64 clo,
-                                         npy_int64 chi);
-
-inline std::tuple<PyObject *, npy_intp const *, npy_double *> qsort_kernel_d1JJ(PyObject * pa,
-                                         npy_intp const * __restrict__ sa,
-                                         npy_double * __restrict__ ca,
-                                         npy_int64 clo,
-                                         npy_int64 chi){
- npy_int64 ci = clo;
- npy_int64 cj = chi;
- npy_double cpivot;
-
- while (ci < chi) {
-  cpivot = ca[(int)((clo + chi) / 2)];
-
-  while (ci <= cj) {
-   while (ca[ci] < cpivot) {
-    ci += 1;
-   }
-
-   while (ca[cj] > cpivot) {
-    cj -= 1;
-   }
-
-   if (ci <= cj) {
-    auto ctmp = ca[ci];
-    ca[ci] = ca[cj];
-    ca[cj] = ctmp;
-    ci += 1;
-    cj -= 1;
-   }
-  }
-
-  if (clo < cj) {
-   qsort_kernel_d1JJ(pa, sa, ca, clo, cj);
-  }
-
-  clo = ci;
-  cj = chi;
- }
-
- return std::make_tuple((PyObject *)pa, sa, ca);
-}
-
-void sighandler(int sig);
-#include <signal.h>
-extern "C" {
- PyObject * create_signature;
- struct sigaction slot;
- PyObject * set_create_signature(PyObject * self, PyObject * args) {
-  if (!PyArg_ParseTuple(args, "O", &create_signature)) {
-   PyErr_SetString(PyExc_ValueError, "Invalid Argument to set_create_signature!");
-   return NULL;
-  }
-  Py_INCREF(create_signature);
-  memset(&slot, 0, sizeof(slot));
-  slot.sa_handler = &sighandler;
-  sigaction(SIGSEGV, &slot, NULL);
-  sigaction(SIGBUS, &slot, NULL);
-  Py_INCREF(Py_None);
-  return Py_None;
- }
- PyObject * run(PyObject * self, PyObject * args) {
-  {
-   PyObj pa;
-   PyObject * plo; npy_int64 clo;
-   PyObject * phi; npy_int64 chi;
-   if (
-    PyTuple_CheckExact(args) and PyTuple_GET_SIZE(args) == 3
-    and (pa = PyTuple_GET_ITEM(args, 0)) and PyArray_CheckExact(pa)
-    and PyArray_TYPE((PyArrayObject *)pa) == NPY_FLOAT64 and PyArray_NDIM((PyArrayObject *)pa) == 1
-    and (plo = PyTuple_GET_ITEM(args, 1)) and PyLong_CheckExact(plo)
-    and (phi = PyTuple_GET_ITEM(args, 2)) and PyLong_CheckExact(phi)
-   ) {
-    if (!(pa.incref((PyObject *)PyArray_GETCONTIGUOUS((PyArrayObject *)pa)))) {
-     PyErr_SetString(PyExc_ValueError, "Invalid Argument type on a!");
-     return NULL;
-    }
-    clo = PyLong_AS_LONG(plo);
-    chi = PyLong_AS_LONG(phi);
-    try {
-     PyObject * res = std::get<0>(qsort_kernel_d1JJ(
-        pa, PyArray_SHAPE((PyArrayObject *)pa), (npy_double *)PyArray_DATA((PyArrayObject *)pa)
-      , clo
-      , chi
-     ));
-
-     Py_INCREF(res);
-     return res;
-    } catch (...) {
-     return NULL;
-    }
-   } else
-    PyErr_Clear();
-  }
-  PyObject * signatures = Py_BuildValue("(sO)", "(lp0\n(lp1\nccopy_reg\n_reconstructor\np2\n(chope._ast\nVariable\np3\nc__builtin__\nobject\np4\nNtp5\nRp6\n(dp7\nS'name'\np8\nS'a'\np9\nsS'dtype'\np10\nS'float64'\np11\nsS'dims'\np12\nI1\nsbag2\n(g3\ng4\nNtp13\nRp14\n(dp15\ng8\nS'lo'\np16\nsg10\nc__builtin__\nint\np17\nsg12\nI0\nsbag2\n(g3\ng4\nNtp18\nRp19\n(dp20\ng8\nS'hi'\np21\nsg10\ng17\nsg12\nI0\nsbaa.", args);
-  if (!signatures) {
-   PyErr_SetString(PyExc_ValueError, "Error building signature string for qsort_kernel");
-   return NULL;
-  }
-  return PyObject_Call(create_signature, signatures, NULL);
- }
- PyMethodDef qsort_kernelMethods[] = {
-  { "set_create_signature", set_create_signature, METH_VARARGS, "signal handler" },
-  { "run", run, METH_VARARGS, "module function" },
-  { NULL, NULL, 0, NULL }
- };
-}
-
-static struct PyModuleDef qsort_kernel_module = {
-    PyModuleDef_HEAD_INIT,
-    "qsort_kernel",
-    NULL,
-    -1,
-    qsort_kernelMethods
-};
-
-PyMODINIT_FUNC PyInit_qsort_kernel(void) {
-    import_array();
-    PyImport_ImportModule("numpy");
-    return PyModule_Create(&qsort_kernel_module);
-}
-
-#include <string>
-#include <sstream>
-#include <iostream>
-#include <cxxabi.h>
-#include <execinfo.h>
-void sighandler(int sig) {
- std::ostringstream buffer;
- buffer << "Abort by " << (sig == SIGSEGV ? "segfault" : "bus error") << std::endl;
- void * stack[64];
- std::size_t depth = backtrace(stack, 64);
- if (!depth)
-  buffer << "  <empty stacktrace, possibly corrupt>" << std::endl;
- else {
-  char ** symbols = backtrace_symbols(stack, depth);
-  for (std::size_t i = 1; i < depth; ++i) {
-   std::string symbol = symbols[i];
-    if (symbol.find_first_of(' ', 59) != std::string::npos) {
-     std::string name = symbol.substr(59, symbol.find_first_of(' ', 59) - 59);
-     int status;
-     char * demangled = abi::__cxa_demangle(name.c_str(), NULL, NULL, &status);
-     if (!status) {
-      buffer << "    "
-       << symbol.substr(0, 59)
-       << demangled
-       << symbol.substr(59 + name.size())
-       << std::endl;
-      free(demangled);
-     } else
-      buffer << "    " << symbol << std::endl;
-    } else
-     buffer << "    " << symbol << std::endl;
-   }
-   free(symbols);
-  }
-  std::cerr << buffer.str();
-  std::exit(EXIT_FAILURE);
- }
-
- -
-
-
- -
-
- - -
- -
- - -
-
Overwriting src/qsort_kernel.cpp
-
-
-
- -
-
- -
-
-
-
-
-

Pi sum CPP

-
-
-
-
-
-
In [6]:
-
-
-
%%file src/pisum.cpp
-#define PY_ARRAY_UNIQUE_SYMBOL pisum_ARRAY_API
-#define NPY_NO_DEPRECATED_API NPY_1_7_API_VERSION
-#include <Python.h>
-#include <numpy/arrayobject.h>
-#include <numpy/arrayscalars.h>
-#include <cmath>
-#include <tuple>
-#include <numeric>
-#include <cstdint>
-#include <cstdlib>
-#include <exception>
-#include <functional>
-#include <type_traits>
-struct PyObj {
-    typedef PyObject * ptr_t;
-    typedef PyArrayObject * arrptr_t;
-    PyObj(): dec(false), ptr(NULL) {}
-    PyObj(ptr_t p): dec(false), ptr(p) {}
-    ~PyObj() { if(dec) Py_DECREF(ptr); }
-    PyObj & operator=(ptr_t p) { if(dec) Py_DECREF(ptr); ptr = p; dec = false; return *this; }
-    PyObj & incref(ptr_t p) { if(dec) Py_DECREF(ptr); ptr = p; dec = (p != NULL); return *this; }
-    operator bool() const { return ptr; }
-    operator ptr_t() const { return ptr; }
-    operator arrptr_t() const { return (arrptr_t)ptr; }
-    bool dec;
-    ptr_t ptr;
-};
-
-inline npy_double pisum_();
-
-inline npy_double pisum_() {
-    double csum = 0;
-    int ck = 1;
-    for (int cj = 1; cj < 501; ++cj) {
-        csum = 0.0;
-        for (ck = 1; ck < 10001; ++ck) {
-            csum += (1.0 / (double)(ck * ck));
-        }
-    }
-
-    return npy_double(csum);
-}
-
-void sighandler(int sig);
-#include <signal.h>
-extern "C" {
-    PyObject * create_signature;
-    struct sigaction slot;
-    PyObject * set_create_signature(PyObject * self, PyObject * args) {
-        if (!PyArg_ParseTuple(args, "O", &create_signature)) {
-            PyErr_SetString(PyExc_ValueError, "Invalid Argument to set_create_signature!");
-            return NULL;
-        }
-        Py_INCREF(create_signature);
-        memset(&slot, 0, sizeof(slot));
-        slot.sa_handler = &sighandler;
-        sigaction(SIGSEGV, &slot, NULL);
-        sigaction(SIGBUS, &slot, NULL);
-        Py_INCREF(Py_None);
-        return Py_None;
-    }
-    PyObject * run(PyObject * self, PyObject * args) {
-        {                try {
-                    return Py_BuildValue("d", pisum_());
-                } catch (...) {
-                    return NULL;
-                }
-        }
-        PyObject * signatures = Py_BuildValue("(sO)", "(lp0\n(lp1\na.", args);
-        if (!signatures) {
-            PyErr_SetString(PyExc_ValueError, "Error building signature string for pisum");
-            return NULL;
-        }
-        return PyObject_Call(create_signature, signatures, NULL);
-    }
-
-PyMethodDef pisumMethods[] = {
-{ "set_create_signature", set_create_signature, METH_VARARGS, "signal handler" },
-{ "run", run, METH_VARARGS, "module function" },
-{ NULL, NULL, 0, NULL }
-};
-
-static struct PyModuleDef pisum_module = {
-    PyModuleDef_HEAD_INIT,
-    "pisum",
-    NULL,
-    -1,
-    pisumMethods
-};
-
-PyMODINIT_FUNC PyInit_pisum(void) {
-    import_array();
-    PyImport_ImportModule("numpy");
-    return PyModule_Create(&pisum_module);
-}
-}
-
-#include <string>
-#include <sstream>
-#include <iostream>
-#include <cxxabi.h>
-#include <execinfo.h>
-void sighandler(int sig) {
-    std::ostringstream buffer;
-    buffer << "Abort by " << (sig == SIGSEGV ? "segfault" : "bus error") << std::endl;
-    void * stack[64];
-    std::size_t depth = backtrace(stack, 64);
-    if (!depth)
-        buffer << "  <empty stacktrace, possibly corrupt>" << std::endl;
-    else {
-        char ** symbols = backtrace_symbols(stack, depth);
-        for (std::size_t i = 1; i < depth; ++i) {
-            std::string symbol = symbols[i];
-                if (symbol.find_first_of(' ', 59) != std::string::npos) {
-                    std::string name = symbol.substr(59, symbol.find_first_of(' ', 59) - 59);
-                    int status;
-                    char * demangled = abi::__cxa_demangle(name.c_str(), NULL, NULL, &status);
-                    if (!status) {
-                        buffer << "    "
-                            << symbol.substr(0, 59)
-                            << demangled
-                            << symbol.substr(59 + name.size())
-                            << std::endl;
-                        free(demangled);
-                    } else
-                        buffer << "    " << symbol << std::endl;
-                } else
-                    buffer << "    " << symbol << std::endl;
-            }
-            free(symbols);
-        }
-        std::cerr << buffer.str();
-        std::exit(EXIT_FAILURE);
-    }
-
- -
-
-
- -
-
- - -
- -
- - -
-
Overwriting src/pisum.cpp
-
-
-
- -
-
- -
-
-
-
In [7]:
-
-
-
%%file src/pisum_opt.cpp
-#define PY_ARRAY_UNIQUE_SYMBOL pisum_opt_ARRAY_API
-#define NPY_NO_DEPRECATED_API NPY_1_7_API_VERSION
-#include <Python.h>
-#include <numpy/arrayobject.h>
-#include <numpy/arrayscalars.h>
-#include <cmath>
-#include <tuple>
-#include <numeric>
-#include <cstdint>
-#include <cstdlib>
-#include <exception>
-#include <functional>
-#include <type_traits>
-struct PyObj {
-    typedef PyObject * ptr_t;
-    typedef PyArrayObject * arrptr_t;
-    PyObj(): dec(false), ptr(NULL) {}
-    PyObj(ptr_t p): dec(false), ptr(p) {}
-    ~PyObj() { if(dec) Py_DECREF(ptr); }
-    PyObj & operator=(ptr_t p) { if(dec) Py_DECREF(ptr); ptr = p; dec = false; return *this; }
-    PyObj & incref(ptr_t p) { if(dec) Py_DECREF(ptr); ptr = p; dec = (p != NULL); return *this; }
-    operator bool() const { return ptr; }
-    operator ptr_t() const { return ptr; }
-    operator arrptr_t() const { return (arrptr_t)ptr; }
-    bool dec;
-    ptr_t ptr;
-};
-
-inline npy_double pisum_opt_();
-
-inline npy_double pisum_opt_() {
-    npy_double csum = npy_double();
-    npy_intp ck = 1;
-    npy_double cf = 0.0;
-
-    for (npy_intp cj = 1; cj < 501; ++cj) {
-        csum = 0.0;
-        cf = 0.0;
-        for (ck = 1; ck < 10001; ++ck) {
-            cf += 1.0;
-            auto c__sp0 = (cf * cf);
-            csum += (1.0 / c__sp0);
-        }
-    }
-    return csum;
-}
-
-void sighandler(int sig);
-#include <signal.h>
-extern "C" {
-    PyObject * create_signature;
-    struct sigaction slot;
-    PyObject * set_create_signature(PyObject * self, PyObject * args) {
-        if (!PyArg_ParseTuple(args, "O", &create_signature)) {
-            PyErr_SetString(PyExc_ValueError, "Invalid Argument to set_create_signature!");
-            return NULL;
-        }
-        Py_INCREF(create_signature);
-        memset(&slot, 0, sizeof(slot));
-        slot.sa_handler = &sighandler;
-        sigaction(SIGSEGV, &slot, NULL);
-        sigaction(SIGBUS, &slot, NULL);
-        Py_INCREF(Py_None);
-        return Py_None;
-    }
-    PyObject * run(PyObject * self, PyObject * args) {
-        {                try {
-                    return Py_BuildValue("d", pisum_opt_());
-                } catch (...) {
-                    return NULL;
-                }
-        }
-        PyObject * signatures = Py_BuildValue("(sO)", "(lp0\n(lp1\na.", args);
-        if (!signatures) {
-            PyErr_SetString(PyExc_ValueError, "Error building signature string for pisum_opt");
-            return NULL;
-        }
-        return PyObject_Call(create_signature, signatures, NULL);
-    }
-
-    PyMethodDef pisum_optMethods[] = {
-    { "set_create_signature", set_create_signature, METH_VARARGS, "signal handler" },
-    { "run", run, METH_VARARGS, "module function" },
-    { NULL, NULL, 0, NULL }
-    };
-
-    static struct PyModuleDef pisum_opt_module = {
-        PyModuleDef_HEAD_INIT,
-        "pisum_opt",
-        NULL,
-        -1,
-        pisum_optMethods
-    };
-
-    PyMODINIT_FUNC PyInit_pisum_opt(void) {
-        import_array();
-        PyImport_ImportModule("numpy");
-        return PyModule_Create(&pisum_opt_module);
-    }
-}
-
-#include <string>
-#include <sstream>
-#include <iostream>
-#include <cxxabi.h>
-#include <execinfo.h>
-void sighandler(int sig) {
-    std::ostringstream buffer;
-    buffer << "Abort by " << (sig == SIGSEGV ? "segfault" : "bus error") << std::endl;
-    void * stack[64];
-    std::size_t depth = backtrace(stack, 64);
-    if (!depth)
-        buffer << "  <empty stacktrace, possibly corrupt>" << std::endl;
-    else {
-        char ** symbols = backtrace_symbols(stack, depth);
-        for (std::size_t i = 1; i < depth; ++i) {
-            std::string symbol = symbols[i];
-                if (symbol.find_first_of(' ', 59) != std::string::npos) {
-                    std::string name = symbol.substr(59, symbol.find_first_of(' ', 59) - 59);
-                    int status;
-                    char * demangled = abi::__cxa_demangle(name.c_str(), NULL, NULL, &status);
-                    if (!status) {
-                        buffer << "    "
-                            << symbol.substr(0, 59)
-                            << demangled
-                            << symbol.substr(59 + name.size())
-                            << std::endl;
-                        free(demangled);
-                    } else
-                        buffer << "    " << symbol << std::endl;
-                } else
-                    buffer << "    " << symbol << std::endl;
-            }
-            free(symbols);
-        }
-        std::cerr << buffer.str();
-        std::exit(EXIT_FAILURE);
-    }
-
- -
-
-
- -
-
- - -
- -
- - -
-
Overwriting src/pisum_opt.cpp
-
-
-
- -
-
- -
-
-
-
-
-

10th order poly log approx CPP

-
-
-
-
-
-
In [8]:
-
-
-
%%file src/ln.cpp
-#define PY_ARRAY_UNIQUE_SYMBOL ln_hope_ARRAY_API
-#define NPY_NO_DEPRECATED_API NPY_1_7_API_VERSION
-#include <Python.h>
-#include <numpy/arrayobject.h>
-#include <numpy/arrayscalars.h>
-#include <cmath>
-#include <tuple>
-#include <numeric>
-#include <cstdint>
-#include <cstdlib>
-#include <exception>
-#include <functional>
-#include <type_traits>
-struct PyObj {
-    typedef PyObject * ptr_t;
-    typedef PyArrayObject * arrptr_t;
-    PyObj(): dec(false), ptr(NULL) {}
-    PyObj(ptr_t p): dec(false), ptr(p) {}
-    ~PyObj() { if(dec) Py_DECREF(ptr); }
-    PyObj & operator=(ptr_t p) { if(dec) Py_DECREF(ptr); ptr = p; dec = false; return *this; }
-    PyObj & incref(ptr_t p) { if(dec) Py_DECREF(ptr); ptr = p; dec = (p != NULL); return *this; }
-    operator bool() const { return ptr; }
-    operator ptr_t() const { return ptr; }
-    operator arrptr_t() const { return (arrptr_t)ptr; }
-    bool dec;
-    ptr_t ptr;
-};
-
-inline void ln_hope_d1d1(PyObject * pX, npy_intp const * __restrict__ sX, npy_double * __restrict__ cX,
-                                                    PyObject * pY, npy_intp const * __restrict__ sY, npy_double * __restrict__ cY);
-
-inline void ln_hope_d1d1(PyObject * pX, npy_intp const * __restrict__ sX, npy_double * __restrict__ cX,
-                                                    PyObject * pY, npy_intp const * __restrict__ sY, npy_double * __restrict__ cY){
-
-    for (npy_intp i0 = 0; i0 < sY[0] - 0; ++i0) {
-        cY[(int)(i0)] = (cX[i0] - 1) - (std::pow((cX[i0] - 1), 2) / 2) + (std::pow((cX[i0] - 1), 3) / 3) - (std::pow((cX[i0] - 1), 4) / 4) + (std::pow((cX[i0] - 1), 5) / 5) - (std::pow((cX[i0] - 1), 6) / 6) + (std::pow((cX[i0] - 1), 7) / 7) - (std::pow((cX[i0] - 1), 8) / 8) + (std::pow((cX[i0] - 1), 9) / 9);
-    }
-}
-
-void sighandler(int sig);
-#include <signal.h>
-extern "C" {
-    PyObject * create_signature;
-    struct sigaction slot;
-    PyObject * set_create_signature(PyObject * self, PyObject * args) {
-        if (!PyArg_ParseTuple(args, "O", &create_signature)) {
-            PyErr_SetString(PyExc_ValueError, "Invalid Argument to set_create_signature!");
-            return NULL;
-        }
-        Py_INCREF(create_signature);
-        memset(&slot, 0, sizeof(slot));
-        slot.sa_handler = &sighandler;
-        sigaction(SIGSEGV, &slot, NULL);
-        sigaction(SIGBUS, &slot, NULL);
-        Py_INCREF(Py_None);
-        return Py_None;
-    }
-    PyObject * run(PyObject * self, PyObject * args) {
-        {
-            PyObj pX;
-            PyObj pY;
-            if (
-                PyTuple_CheckExact(args) and PyTuple_GET_SIZE(args) == 2
-                and (pX = PyTuple_GET_ITEM(args, 0)) and PyArray_CheckExact(pX)
-                and PyArray_TYPE((PyArrayObject *)pX) == NPY_FLOAT64 and PyArray_NDIM((PyArrayObject *)pX) == 1
-                and (pY = PyTuple_GET_ITEM(args, 1)) and PyArray_CheckExact(pY)
-                and PyArray_TYPE((PyArrayObject *)pY) == NPY_FLOAT64 and PyArray_NDIM((PyArrayObject *)pY) == 1
-            ) {
-                if (!(pX.incref((PyObject *)PyArray_GETCONTIGUOUS((PyArrayObject *)pX)))) {
-                    PyErr_SetString(PyExc_ValueError, "Invalid Argument type on X!");
-                    return NULL;
-                }
-                if (!(pY.incref((PyObject *)PyArray_GETCONTIGUOUS((PyArrayObject *)pY)))) {
-                    PyErr_SetString(PyExc_ValueError, "Invalid Argument type on Y!");
-                    return NULL;
-                }
-                try {
-                    ln_hope_d1d1(
-                          pX, PyArray_SHAPE((PyArrayObject *)pX), (npy_double *)PyArray_DATA((PyArrayObject *)pX)
-                        , pY, PyArray_SHAPE((PyArrayObject *)pY), (npy_double *)PyArray_DATA((PyArrayObject *)pY)
-                    );
-                    Py_INCREF(Py_None);
-                    return Py_None;
-                } catch (...) {
-                    return NULL;
-                }
-            } else
-                PyErr_Clear();
-        }
-        PyObject * signatures = Py_BuildValue("(sO)", "(lp0\n(lp1\nccopy_reg\n_reconstructor\np2\n(chope._ast\nVariable\np3\nc__builtin__\nobject\np4\nNtp5\nRp6\n(dp7\nS'name'\np8\nS'X'\np9\nsS'dtype'\np10\nS'float64'\np11\nsS'dims'\np12\nI1\nsbag2\n(g3\ng4\nNtp13\nRp14\n(dp15\ng8\nS'Y'\np16\nsg10\ng11\nsg12\nI1\nsbaa.", args);
-        if (!signatures) {
-            PyErr_SetString(PyExc_ValueError, "Error building signature string for ln_hope");
-            return NULL;
-        }
-        return PyObject_Call(create_signature, signatures, NULL);
-    }
-
-    PyMethodDef lnMethods[] = {
-        { "set_create_signature", set_create_signature, METH_VARARGS, "signal handler" },
-        { "run", run, METH_VARARGS, "module function" },
-        { NULL, NULL, 0, NULL }
-    };
-
-
-    static struct PyModuleDef lnmodule = {
-        PyModuleDef_HEAD_INIT,
-        "ln",
-        NULL,
-        -1,
-        lnMethods
-    };
-
-
-
-   PyMODINIT_FUNC PyInit_ln(void) {
-        import_array();
-        PyImport_ImportModule("numpy");
-        return PyModule_Create(&lnmodule);
-   }
-}
-#include <string>
-#include <sstream>
-#include <iostream>
-#include <cxxabi.h>
-#include <execinfo.h>
-void sighandler(int sig) {
-    std::ostringstream buffer;
-    buffer << "Abort by " << (sig == SIGSEGV ? "segfault" : "bus error") << std::endl;
-    void * stack[64];
-    std::size_t depth = backtrace(stack, 64);
-    if (!depth)
-        buffer << "  <empty stacktrace, possibly corrupt>" << std::endl;
-    else {
-        char ** symbols = backtrace_symbols(stack, depth);
-        for (std::size_t i = 1; i < depth; ++i) {
-            std::string symbol = symbols[i];
-                if (symbol.find_first_of(' ', 59) != std::string::npos) {
-                    std::string name = symbol.substr(59, symbol.find_first_of(' ', 59) - 59);
-                    int status;
-                    char * demangled = abi::__cxa_demangle(name.c_str(), NULL, NULL, &status);
-                    if (!status) {
-                        buffer << "    "
-                            << symbol.substr(0, 59)
-                            << demangled
-                            << symbol.substr(59 + name.size())
-                            << std::endl;
-                        free(demangled);
-                    } else
-                        buffer << "    " << symbol << std::endl;
-                } else
-                    buffer << "    " << symbol << std::endl;
-            }
-            free(symbols);
-        }
-        std::cerr << buffer.str();
-        std::exit(EXIT_FAILURE);
-    }
-
- -
-
-
- -
-
- - -
- -
- - -
-
Overwriting src/ln.cpp
-
-
-
- -
-
- -
-
-
-
In [9]:
-
-
-
%%file src/ln_exp.cpp
-#define PY_ARRAY_UNIQUE_SYMBOL ln_hope_exp_ARRAY_API
-#define NPY_NO_DEPRECATED_API NPY_1_7_API_VERSION
-#include <Python.h>
-#include <numpy/arrayobject.h>
-#include <numpy/arrayscalars.h>
-#include <cmath>
-#include <tuple>
-#include <numeric>
-#include <cstdint>
-#include <cstdlib>
-#include <exception>
-#include <functional>
-#include <type_traits>
-struct PyObj {
-    typedef PyObject * ptr_t;
-    typedef PyArrayObject * arrptr_t;
-    PyObj(): dec(false), ptr(NULL) {}
-    PyObj(ptr_t p): dec(false), ptr(p) {}
-    ~PyObj() { if(dec) Py_DECREF(ptr); }
-    PyObj & operator=(ptr_t p) { if(dec) Py_DECREF(ptr); ptr = p; dec = false; return *this; }
-    PyObj & incref(ptr_t p) { if(dec) Py_DECREF(ptr); ptr = p; dec = (p != NULL); return *this; }
-    operator bool() const { return ptr; }
-    operator ptr_t() const { return ptr; }
-    operator arrptr_t() const { return (arrptr_t)ptr; }
-    bool dec;
-    ptr_t ptr;
-};
-
-inline void ln_hope_exp_d1d1(PyObject * pX, npy_intp const * __restrict__ sX, npy_double * __restrict__ cX,
-                                                    PyObject * pY, npy_intp const * __restrict__ sY, npy_double * __restrict__ cY);
-
-inline void ln_hope_exp_d1d1(PyObject * pX, npy_intp const * __restrict__ sX, npy_double * __restrict__ cX,
-                                                    PyObject * pY, npy_intp const * __restrict__ sY, npy_double * __restrict__ cY){
-
-    for (npy_intp i0 = 0; i0 < sX[0] - 0; ++i0) {
-        auto cx = (cX[i0] - 1);
-        auto cx2 = (cx * cx);
-        auto cx4 = (cx2 * cx2);
-        auto cx6 = (cx4 * cx2);
-        auto cx8 = (cx4 * cx4);
-        cY[i0] = cx - (cx2 / 2) + (cx * cx2 / 3) - (cx4 / 4) + (cx * cx4 / 5) - (cx6 / 6) + (cx6 * cx / 7) - (cx8 / 8) + (cx8 * cx / 9);
-    }
-}
-
-void sighandler(int sig);
-#include <signal.h>
-extern "C" {
-    PyObject * create_signature;
-    struct sigaction slot;
-    PyObject * set_create_signature(PyObject * self, PyObject * args) {
-        if (!PyArg_ParseTuple(args, "O", &create_signature)) {
-            PyErr_SetString(PyExc_ValueError, "Invalid Argument to set_create_signature!");
-            return NULL;
-        }
-        Py_INCREF(create_signature);
-        memset(&slot, 0, sizeof(slot));
-        slot.sa_handler = &sighandler;
-        sigaction(SIGSEGV, &slot, NULL);
-        sigaction(SIGBUS, &slot, NULL);
-        Py_INCREF(Py_None);
-        return Py_None;
-    }
-    PyObject * run(PyObject * self, PyObject * args) {
-        {
-            PyObj pX;
-            PyObj pY;
-            if (
-                PyTuple_CheckExact(args) and PyTuple_GET_SIZE(args) == 2
-                and (pX = PyTuple_GET_ITEM(args, 0)) and PyArray_CheckExact(pX)
-                and PyArray_TYPE((PyArrayObject *)pX) == NPY_FLOAT64 and PyArray_NDIM((PyArrayObject *)pX) == 1
-                and (pY = PyTuple_GET_ITEM(args, 1)) and PyArray_CheckExact(pY)
-                and PyArray_TYPE((PyArrayObject *)pY) == NPY_FLOAT64 and PyArray_NDIM((PyArrayObject *)pY) == 1
-            ) {
-                if (!(pX.incref((PyObject *)PyArray_GETCONTIGUOUS((PyArrayObject *)pX)))) {
-                    PyErr_SetString(PyExc_ValueError, "Invalid Argument type on X!");
-                    return NULL;
-                }
-                if (!(pY.incref((PyObject *)PyArray_GETCONTIGUOUS((PyArrayObject *)pY)))) {
-                    PyErr_SetString(PyExc_ValueError, "Invalid Argument type on Y!");
-                    return NULL;
-                }
-                try {
-                    ln_hope_exp_d1d1(
-                          pX, PyArray_SHAPE((PyArrayObject *)pX), (npy_double *)PyArray_DATA((PyArrayObject *)pX)
-                        , pY, PyArray_SHAPE((PyArrayObject *)pY), (npy_double *)PyArray_DATA((PyArrayObject *)pY)
-                    );
-                    Py_INCREF(Py_None);
-                    return Py_None;
-                } catch (...) {
-                    return NULL;
-                }
-            } else
-                PyErr_Clear();
-        }
-        PyObject * signatures = Py_BuildValue("(sO)", "(lp0\n(lp1\nccopy_reg\n_reconstructor\np2\n(chope._ast\nVariable\np3\nc__builtin__\nobject\np4\nNtp5\nRp6\n(dp7\nS'name'\np8\nS'X'\np9\nsS'dtype'\np10\nS'float64'\np11\nsS'dims'\np12\nI1\nsbag2\n(g3\ng4\nNtp13\nRp14\n(dp15\ng8\nS'Y'\np16\nsg10\ng11\nsg12\nI1\nsbaa.", args);
-        if (!signatures) {
-            PyErr_SetString(PyExc_ValueError, "Error building signature string for ln_hope_exp");
-            return NULL;
-        }
-        return PyObject_Call(create_signature, signatures, NULL);
-    }
-    PyMethodDef ln_expMethods[] = {
-        { "set_create_signature", set_create_signature, METH_VARARGS, "signal handler" },
-        { "run", run, METH_VARARGS, "module function" },
-        { NULL, NULL, 0, NULL }
-    };
-
-
-    static struct PyModuleDef ln_expmodule = {
-        PyModuleDef_HEAD_INIT,
-        "ln_exp",
-        NULL,
-        -1,
-        ln_expMethods
-    };
-
-
-
-   PyMODINIT_FUNC PyInit_ln_exp(void) {
-        import_array();
-        PyImport_ImportModule("numpy");
-        return PyModule_Create(&ln_expmodule);
-   }
-}
-#include <string>
-#include <sstream>
-#include <iostream>
-#include <cxxabi.h>
-#include <execinfo.h>
-void sighandler(int sig) {
-    std::ostringstream buffer;
-    buffer << "Abort by " << (sig == SIGSEGV ? "segfault" : "bus error") << std::endl;
-    void * stack[64];
-    std::size_t depth = backtrace(stack, 64);
-    if (!depth)
-        buffer << "  <empty stacktrace, possibly corrupt>" << std::endl;
-    else {
-        char ** symbols = backtrace_symbols(stack, depth);
-        for (std::size_t i = 1; i < depth; ++i) {
-            std::string symbol = symbols[i];
-                if (symbol.find_first_of(' ', 59) != std::string::npos) {
-                    std::string name = symbol.substr(59, symbol.find_first_of(' ', 59) - 59);
-                    int status;
-                    char * demangled = abi::__cxa_demangle(name.c_str(), NULL, NULL, &status);
-                    if (!status) {
-                        buffer << "    "
-                            << symbol.substr(0, 59)
-                            << demangled
-                            << symbol.substr(59 + name.size())
-                            << std::endl;
-                        free(demangled);
-                    } else
-                        buffer << "    " << symbol << std::endl;
-                } else
-                    buffer << "    " << symbol << std::endl;
-            }
-            free(symbols);
-        }
-        std::cerr << buffer.str();
-        std::exit(EXIT_FAILURE);
-    }
-
- -
-
-
- -
-
- - -
- -
- - -
-
Overwriting src/ln_exp.cpp
-
-
-
- -
-
- -
-
-
-
In [10]:
-
-
-
%%file src/ln_opt.cpp
-#define PY_ARRAY_UNIQUE_SYMBOL ln_hope_opt_ARRAY_API
-#define NPY_NO_DEPRECATED_API NPY_1_7_API_VERSION
-#include <Python.h>
-#include <numpy/arrayobject.h>
-#include <numpy/arrayscalars.h>
-#include <cmath>
-#include <tuple>
-#include <numeric>
-#include <cstdint>
-#include <cstdlib>
-#include <exception>
-#include <functional>
-#include <type_traits>
-struct PyObj {
-    typedef PyObject * ptr_t;
-    typedef PyArrayObject * arrptr_t;
-    PyObj(): dec(false), ptr(NULL) {}
-    PyObj(ptr_t p): dec(false), ptr(p) {}
-    ~PyObj() { if(dec) Py_DECREF(ptr); }
-    PyObj & operator=(ptr_t p) { if(dec) Py_DECREF(ptr); ptr = p; dec = false; return *this; }
-    PyObj & incref(ptr_t p) { if(dec) Py_DECREF(ptr); ptr = p; dec = (p != NULL); return *this; }
-    operator bool() const { return ptr; }
-    operator ptr_t() const { return ptr; }
-    operator arrptr_t() const { return (arrptr_t)ptr; }
-    bool dec;
-    ptr_t ptr;
-};
-
-inline void ln_hope_opt_d1d1(PyObject * pX, npy_intp const * __restrict__ sX, npy_double * __restrict__ cX,
-                                                    PyObject * pY, npy_intp const * __restrict__ sY, npy_double * __restrict__ cY);
-
-inline void ln_hope_opt_d1d1(PyObject * pX, npy_intp const * __restrict__ sX, npy_double * __restrict__ cX,
-                                                    PyObject * pY, npy_intp const * __restrict__ sY, npy_double * __restrict__ cY){
-
-    for (npy_intp i0 = 0; i0 < sY[0] - 0; ++i0) {
-        auto c__sp0 = (cX[i0] * cX[i0]);
-        auto c__sp1 = (c__sp0 * c__sp0);
-        auto c__sp2 = (c__sp1 * c__sp1);
-        auto c__sp3 = (c__sp2 * cX[i0]);
-        auto c__sp4 = (c__sp0 * cX[i0]);
-        auto c__sp5 = (c__sp4 * c__sp4);
-        auto c__sp6 = (c__sp5 * cX[i0]);
-        auto c__sp7 = (c__sp1 * cX[i0]);
-        cY[(int)(i0)] = (-7129.0 / 2520.0) + (28 * c__sp4) + (-(18 * c__sp0)) + (-(9 * c__sp2 / 8)) + (-(14 * c__sp5)) + (-(63 * c__sp1 / 2)) + (126 * c__sp7 / 5) + (9 * cX[i0]) + (c__sp3 / 9) + (36 * c__sp6 / 7);
-    }
-}
-
-void sighandler(int sig);
-#include <signal.h>
-extern "C" {
-    PyObject * create_signature;
-    struct sigaction slot;
-    PyObject * set_create_signature(PyObject * self, PyObject * args) {
-        if (!PyArg_ParseTuple(args, "O", &create_signature)) {
-            PyErr_SetString(PyExc_ValueError, "Invalid Argument to set_create_signature!");
-            return NULL;
-        }
-        Py_INCREF(create_signature);
-        memset(&slot, 0, sizeof(slot));
-        slot.sa_handler = &sighandler;
-        sigaction(SIGSEGV, &slot, NULL);
-        sigaction(SIGBUS, &slot, NULL);
-        Py_INCREF(Py_None);
-        return Py_None;
-    }
-    PyObject * run(PyObject * self, PyObject * args) {
-        {
-            PyObj pX;
-            PyObj pY;
-            if (
-                PyTuple_CheckExact(args) and PyTuple_GET_SIZE(args) == 2
-                and (pX = PyTuple_GET_ITEM(args, 0)) and PyArray_CheckExact(pX)
-                and PyArray_TYPE((PyArrayObject *)pX) == NPY_FLOAT64 and PyArray_NDIM((PyArrayObject *)pX) == 1
-                and (pY = PyTuple_GET_ITEM(args, 1)) and PyArray_CheckExact(pY)
-                and PyArray_TYPE((PyArrayObject *)pY) == NPY_FLOAT64 and PyArray_NDIM((PyArrayObject *)pY) == 1
-            ) {
-                if (!(pX.incref((PyObject *)PyArray_GETCONTIGUOUS((PyArrayObject *)pX)))) {
-                    PyErr_SetString(PyExc_ValueError, "Invalid Argument type on X!");
-                    return NULL;
-                }
-                if (!(pY.incref((PyObject *)PyArray_GETCONTIGUOUS((PyArrayObject *)pY)))) {
-                    PyErr_SetString(PyExc_ValueError, "Invalid Argument type on Y!");
-                    return NULL;
-                }
-                try {
-                    ln_hope_opt_d1d1(
-                          pX, PyArray_SHAPE((PyArrayObject *)pX), (npy_double *)PyArray_DATA((PyArrayObject *)pX)
-                        , pY, PyArray_SHAPE((PyArrayObject *)pY), (npy_double *)PyArray_DATA((PyArrayObject *)pY)
-                    );
-                    Py_INCREF(Py_None);
-                    return Py_None;
-                } catch (...) {
-                    return NULL;
-                }
-            } else
-                PyErr_Clear();
-        }
-        PyObject * signatures = Py_BuildValue("(sO)", "(lp0\n(lp1\nccopy_reg\n_reconstructor\np2\n(chope._ast\nVariable\np3\nc__builtin__\nobject\np4\nNtp5\nRp6\n(dp7\nS'name'\np8\nS'X'\np9\nsS'dtype'\np10\nS'float64'\np11\nsS'dims'\np12\nI1\nsbag2\n(g3\ng4\nNtp13\nRp14\n(dp15\ng8\nS'Y'\np16\nsg10\ng11\nsg12\nI1\nsbaa.", args);
-        if (!signatures) {
-            PyErr_SetString(PyExc_ValueError, "Error building signature string for ln_hope_opt");
-            return NULL;
-        }
-        return PyObject_Call(create_signature, signatures, NULL);
-    }
-
-    PyMethodDef ln_optMethods[] = {
-        { "set_create_signature", set_create_signature, METH_VARARGS, "signal handler" },
-        { "run", run, METH_VARARGS, "module function" },
-        { NULL, NULL, 0, NULL }
-    };
-
-
-    static struct PyModuleDef ln_optmodule = {
-        PyModuleDef_HEAD_INIT,
-        "ln",
-        NULL,
-        -1,
-        ln_optMethods
-    };
-
-
-
-   PyMODINIT_FUNC PyInit_ln_opt(void) {
-        import_array();
-        PyImport_ImportModule("numpy");
-        return PyModule_Create(&ln_optmodule);
-   }
-}
-#include <string>
-#include <sstream>
-#include <iostream>
-#include <cxxabi.h>
-#include <execinfo.h>
-void sighandler(int sig) {
-    std::ostringstream buffer;
-    buffer << "Abort by " << (sig == SIGSEGV ? "segfault" : "bus error") << std::endl;
-    void * stack[64];
-    std::size_t depth = backtrace(stack, 64);
-    if (!depth)
-        buffer << "  <empty stacktrace, possibly corrupt>" << std::endl;
-    else {
-        char ** symbols = backtrace_symbols(stack, depth);
-        for (std::size_t i = 1; i < depth; ++i) {
-            std::string symbol = symbols[i];
-                if (symbol.find_first_of(' ', 59) != std::string::npos) {
-                    std::string name = symbol.substr(59, symbol.find_first_of(' ', 59) - 59);
-                    int status;
-                    char * demangled = abi::__cxa_demangle(name.c_str(), NULL, NULL, &status);
-                    if (!status) {
-                        buffer << "    "
-                            << symbol.substr(0, 59)
-                            << demangled
-                            << symbol.substr(59 + name.size())
-                            << std::endl;
-                        free(demangled);
-                    } else
-                        buffer << "    " << symbol << std::endl;
-                } else
-                    buffer << "    " << symbol << std::endl;
-            }
-            free(symbols);
-        }
-        std::cerr << buffer.str();
-        std::exit(EXIT_FAILURE);
-    }
-
- -
-
-
- -
-
- - -
- -
- - -
-
Overwriting src/ln_opt.cpp
-
-
-
- -
-
- -
-
-
-
-
-

Simplify CPP

-
-
-
-
-
-
In [11]:
-
-
-
%%file src/poly.cpp
-#define PY_ARRAY_UNIQUE_SYMBOL poly_ARRAY_API
-#define NPY_NO_DEPRECATED_API NPY_1_7_API_VERSION
-#include <Python.h>
-#include <numpy/arrayobject.h>
-#include <numpy/arrayscalars.h>
-#include <cmath>
-#include <tuple>
-#include <numeric>
-#include <cstdint>
-#include <cstdlib>
-#include <exception>
-#include <functional>
-#include <type_traits>
-struct PyObj {
-    typedef PyObject * ptr_t;
-    typedef PyArrayObject * arrptr_t;
-    PyObj(): dec(false), ptr(NULL) {}
-    PyObj(ptr_t p): dec(false), ptr(p) {}
-    ~PyObj() { if(dec) Py_DECREF(ptr); }
-    PyObj & operator=(ptr_t p) { if(dec) Py_DECREF(ptr); ptr = p; dec = false; return *this; }
-    PyObj & incref(ptr_t p) { if(dec) Py_DECREF(ptr); ptr = p; dec = (p != NULL); return *this; }
-    operator bool() const { return ptr; }
-    operator ptr_t() const { return ptr; }
-    operator arrptr_t() const { return (arrptr_t)ptr; }
-    bool dec;
-    ptr_t ptr;
-};
-inline void poly_d1d1(PyObject * pres, npy_intp const * __restrict__ sres, npy_double * __restrict__ cres,
-                                            PyObject * parg, npy_intp const * __restrict__ sarg, npy_double * __restrict__ carg);
-
-inline void poly_d1d1(PyObject * pres, npy_intp const * __restrict__ sres, npy_double * __restrict__ cres,
-                                            PyObject * parg, npy_intp const * __restrict__ sarg, npy_double * __restrict__ carg){
-
-    npy_double arg_i;
-    double sin_arg_i;
-    for (npy_intp i0 = 0; i0 < sres[0] - 0; ++i0) {
-        arg_i = carg[i0];
-        cres[(int)(i0)] = std::pow(std::sin(arg_i), 2) + (std::pow(arg_i, 3) + std::pow(arg_i, 2) - arg_i - 1) / (std::pow(arg_i, 2) + 2 * arg_i + 1) + std::pow(std::cos(arg_i), 2);
-    }
-}
-void sighandler(int sig);
-#include <signal.h>
-extern "C" {
-    PyObject * create_signature;
-    struct sigaction slot;
-    PyObject * set_create_signature(PyObject * self, PyObject * args) {
-        if (!PyArg_ParseTuple(args, "O", &create_signature)) {
-            PyErr_SetString(PyExc_ValueError, "Invalid Argument to set_create_signature!");
-            return NULL;
-        }
-        Py_INCREF(create_signature);
-        memset(&slot, 0, sizeof(slot));
-        slot.sa_handler = &sighandler;
-        sigaction(SIGSEGV, &slot, NULL);
-        sigaction(SIGBUS, &slot, NULL);
-        Py_INCREF(Py_None);
-        return Py_None;
-    }
-    PyObject * run(PyObject * self, PyObject * args) {
-        {
-            PyObj pres;
-            PyObj parg;
-            if (
-                PyTuple_CheckExact(args) and PyTuple_GET_SIZE(args) == 2
-                and (pres = PyTuple_GET_ITEM(args, 0)) and PyArray_CheckExact(pres)
-                and PyArray_TYPE((PyArrayObject *)pres) == NPY_FLOAT64 and PyArray_NDIM((PyArrayObject *)pres) == 1
-                and (parg = PyTuple_GET_ITEM(args, 1)) and PyArray_CheckExact(parg)
-                and PyArray_TYPE((PyArrayObject *)parg) == NPY_FLOAT64 and PyArray_NDIM((PyArrayObject *)parg) == 1
-            ) {
-                if (!(pres.incref((PyObject *)PyArray_GETCONTIGUOUS((PyArrayObject *)pres)))) {
-                    PyErr_SetString(PyExc_ValueError, "Invalid Argument type on res!");
-                    return NULL;
-                }
-                if (!(parg.incref((PyObject *)PyArray_GETCONTIGUOUS((PyArrayObject *)parg)))) {
-                    PyErr_SetString(PyExc_ValueError, "Invalid Argument type on arg!");
-                    return NULL;
-                }
-                try {
-                    poly_d1d1(
-                          pres, PyArray_SHAPE((PyArrayObject *)pres), (npy_double *)PyArray_DATA((PyArrayObject *)pres)
-                        , parg, PyArray_SHAPE((PyArrayObject *)parg), (npy_double *)PyArray_DATA((PyArrayObject *)parg)
-                    );
-                    Py_INCREF(Py_None);
-                    return Py_None;
-                } catch (...) {
-                    return NULL;
-                }
-            } else
-                PyErr_Clear();
-        }
-        PyObject * signatures = Py_BuildValue("(sO)", "(lp0\n(lp1\nccopy_reg\n_reconstructor\np2\n(chope._ast\nVariable\np3\nc__builtin__\nobject\np4\nNtp5\nRp6\n(dp7\nS'name'\np8\nS'res'\np9\nsS'dtype'\np10\nS'float64'\np11\nsS'dims'\np12\nI1\nsbag2\n(g3\ng4\nNtp13\nRp14\n(dp15\ng8\nS'arg'\np16\nsg10\ng11\nsg12\nI1\nsbaa.", args);
-        if (!signatures) {
-            PyErr_SetString(PyExc_ValueError, "Error building signature string for poly");
-            return NULL;
-        }
-        return PyObject_Call(create_signature, signatures, NULL);
-    }
-
-    PyMethodDef polyMethods[] = {
-        { "set_create_signature", set_create_signature, METH_VARARGS, "signal handler" },
-        { "run", run, METH_VARARGS, "module function" },
-        { NULL, NULL, 0, NULL }
-    };
-
-    static struct PyModuleDef polymodule = {
-        PyModuleDef_HEAD_INIT,
-        "poly",
-        NULL,
-        -1,
-        polyMethods
-    };
-
-   PyMODINIT_FUNC PyInit_poly(void) {
-        import_array();
-        PyImport_ImportModule("numpy");
-        return PyModule_Create(&polymodule);
-   }
-}
-#include <string>
-#include <sstream>
-#include <iostream>
-#include <cxxabi.h>
-#include <execinfo.h>
-void sighandler(int sig) {
-    std::ostringstream buffer;
-    buffer << "Abort by " << (sig == SIGSEGV ? "segfault" : "bus error") << std::endl;
-    void * stack[64];
-    std::size_t depth = backtrace(stack, 64);
-    if (!depth)
-        buffer << "  <empty stacktrace, possibly corrupt>" << std::endl;
-    else {
-        char ** symbols = backtrace_symbols(stack, depth);
-        for (std::size_t i = 1; i < depth; ++i) {
-            std::string symbol = symbols[i];
-                if (symbol.find_first_of(' ', 59) != std::string::npos) {
-                    std::string name = symbol.substr(59, symbol.find_first_of(' ', 59) - 59);
-                    int status;
-                    char * demangled = abi::__cxa_demangle(name.c_str(), NULL, NULL, &status);
-                    if (!status) {
-                        buffer << "    "
-                            << symbol.substr(0, 59)
-                            << demangled
-                            << symbol.substr(59 + name.size())
-                            << std::endl;
-                        free(demangled);
-                    } else
-                        buffer << "    " << symbol << std::endl;
-                } else
-                    buffer << "    " << symbol << std::endl;
-            }
-            free(symbols);
-        }
-        std::cerr << buffer.str();
-        std::exit(EXIT_FAILURE);
-    }
-
- -
-
-
- -
-
- - -
- -
- - -
-
Overwriting src/poly.cpp
-
-
-
- -
-
- -
-
-
-
In [12]:
-
-
-
%%file src/poly_opt.cpp
-#define PY_ARRAY_UNIQUE_SYMBOL poly_ARRAY_API
-#define NPY_NO_DEPRECATED_API NPY_1_7_API_VERSION
-#include <Python.h>
-#include <numpy/arrayobject.h>
-#include <numpy/arrayscalars.h>
-#include <cmath>
-#include <tuple>
-#include <numeric>
-#include <cstdint>
-#include <cstdlib>
-#include <exception>
-#include <functional>
-#include <type_traits>
-struct PyObj {
-	typedef PyObject * ptr_t;
-	typedef PyArrayObject * arrptr_t;
-	PyObj(): dec(false), ptr(NULL) {}
-	PyObj(ptr_t p): dec(false), ptr(p) {}
-	~PyObj() { if(dec) Py_DECREF(ptr); }
-	PyObj & operator=(ptr_t p) { if(dec) Py_DECREF(ptr); ptr = p; dec = false; return *this; }
-	PyObj & incref(ptr_t p) { if(dec) Py_DECREF(ptr); ptr = p; dec = (p != NULL); return *this; }
-	operator bool() const { return ptr; }
-	operator ptr_t() const { return ptr; }
-	operator arrptr_t() const { return (arrptr_t)ptr; }
-	bool dec;
-	ptr_t ptr;
-};
-
-inline void poly_d1d1(PyObject * pres, npy_intp const * __restrict__ sres, npy_double * __restrict__ cres, 
-											PyObject * parg, npy_intp const * __restrict__ sarg, npy_double * __restrict__ carg);
-
-inline void poly_d1d1(PyObject * pres, npy_intp const * __restrict__ sres, npy_double * __restrict__ cres, 
-											PyObject * parg, npy_intp const * __restrict__ sarg, npy_double * __restrict__ carg){
-	for (npy_intp i0 = 0; i0 < sres[0] - 0; ++i0) {
-		cres[(int)(i0)] = carg[i0];
-	}
-}
-
-void sighandler(int sig);
-#include <signal.h>
-extern "C" {
-	PyObject * create_signature;
-	struct sigaction slot;
-	PyObject * set_create_signature(PyObject * self, PyObject * args) {
-		if (!PyArg_ParseTuple(args, "O", &create_signature)) {
-			PyErr_SetString(PyExc_ValueError, "Invalid Argument to set_create_signature!");
-			return NULL;
-		}
-		Py_INCREF(create_signature);
-		memset(&slot, 0, sizeof(slot));
-		slot.sa_handler = &sighandler;
-		sigaction(SIGSEGV, &slot, NULL);
-		sigaction(SIGBUS, &slot, NULL);
-		Py_INCREF(Py_None);
-		return Py_None;
-	}
-	PyObject * run(PyObject * self, PyObject * args) {
-		{
-			PyObj pres;
-			PyObj parg;
-			if (
-				PyTuple_CheckExact(args) and PyTuple_GET_SIZE(args) == 2
-				and (pres = PyTuple_GET_ITEM(args, 0)) and PyArray_CheckExact(pres)
-				and PyArray_TYPE((PyArrayObject *)pres) == NPY_FLOAT64 and PyArray_NDIM((PyArrayObject *)pres) == 1
-				and (parg = PyTuple_GET_ITEM(args, 1)) and PyArray_CheckExact(parg)
-				and PyArray_TYPE((PyArrayObject *)parg) == NPY_FLOAT64 and PyArray_NDIM((PyArrayObject *)parg) == 1
-			) {
-				if (!(pres.incref((PyObject *)PyArray_GETCONTIGUOUS((PyArrayObject *)pres)))) {
-					PyErr_SetString(PyExc_ValueError, "Invalid Argument type on res!");
-					return NULL;
-				}
-				if (!(parg.incref((PyObject *)PyArray_GETCONTIGUOUS((PyArrayObject *)parg)))) {
-					PyErr_SetString(PyExc_ValueError, "Invalid Argument type on arg!");
-					return NULL;
-				}
-				try {
-					poly_d1d1(
-						  pres, PyArray_SHAPE((PyArrayObject *)pres), (npy_double *)PyArray_DATA((PyArrayObject *)pres)
-						, parg, PyArray_SHAPE((PyArrayObject *)parg), (npy_double *)PyArray_DATA((PyArrayObject *)parg)
-					);
-					Py_INCREF(Py_None);
-					return Py_None;
-				} catch (...) {
-					return NULL;
-				}
-			} else
-				PyErr_Clear();
-		}
-		PyObject * signatures = Py_BuildValue("(sO)", "(lp0\n(lp1\nccopy_reg\n_reconstructor\np2\n(chope._ast\nVariable\np3\nc__builtin__\nobject\np4\nNtp5\nRp6\n(dp7\nS'name'\np8\nS'res'\np9\nsS'dtype'\np10\nS'float64'\np11\nsS'dims'\np12\nI1\nsbag2\n(g3\ng4\nNtp13\nRp14\n(dp15\ng8\nS'arg'\np16\nsg10\ng11\nsg12\nI1\nsbaa.", args);
-		if (!signatures) {
-			PyErr_SetString(PyExc_ValueError, "Error building signature string for poly");
-			return NULL;
-		}
-		return PyObject_Call(create_signature, signatures, NULL);
-	}
-	PyMethodDef polyMethods[] = {
-		{ "set_create_signature", (PyCFunction)set_create_signature, METH_VARARGS },
-		{ "run", (PyCFunction)run, METH_VARARGS },
-		{ NULL, NULL }
-	};
-	PyMODINIT_FUNC initpoly(void) {
-		import_array();
-		PyImport_ImportModule("numpy");
-		(void)Py_InitModule("poly", polyMethods);
-	}
-}
-#include <string>
-#include <sstream>
-#include <iostream>
-#include <cxxabi.h>
-#include <execinfo.h>
-void sighandler(int sig) {
-	std::ostringstream buffer;
-	buffer << "Abort by " << (sig == SIGSEGV ? "segfault" : "bus error") << std::endl;
-	void * stack[64];
-	std::size_t depth = backtrace(stack, 64);
-	if (!depth)
-		buffer << "  <empty stacktrace, possibly corrupt>" << std::endl;
-	else {
-		char ** symbols = backtrace_symbols(stack, depth);
-		for (std::size_t i = 1; i < depth; ++i) {
-			std::string symbol = symbols[i];
-				if (symbol.find_first_of(' ', 59) != std::string::npos) {
-					std::string name = symbol.substr(59, symbol.find_first_of(' ', 59) - 59);
-					int status;
-					char * demangled = abi::__cxa_demangle(name.c_str(), NULL, NULL, &status);
-					if (!status) {
-						buffer << "    " 
-							<< symbol.substr(0, 59) 
-							<< demangled
-							<< symbol.substr(59 + name.size())
-							<< std::endl;
-						free(demangled);
-					} else
-						buffer << "    " << symbol << std::endl;
-				} else
-					buffer << "    " << symbol << std::endl;
-			}
-			free(symbols);
-		}
-		std::cerr << buffer.str();
-		std::exit(EXIT_FAILURE);
-	}
-
- -
-
-
- -
-
- - -
- -
- - -
-
Overwriting src/poly_opt.cpp
-
-
-
- -
-
- -
-
-
-
-
-

Pairwise distance

-
-
-
-
-
-
In [13]:
-
-
-
%%file src/pairwise.cpp
-#define PY_ARRAY_UNIQUE_SYMBOL pairwise_ARRAY_API
-#define NPY_NO_DEPRECATED_API NPY_1_7_API_VERSION
-#include <Python.h>
-#include <numpy/arrayobject.h>
-#include <numpy/arrayscalars.h>
-#include <cmath>
-#include <tuple>
-#include <numeric>
-#include <cstdint>
-#include <cstdlib>
-#include <exception>
-#include <functional>
-#include <type_traits>
-struct PyObj {
-    typedef PyObject * ptr_t;
-    typedef PyArrayObject * arrptr_t;
-    PyObj(): dec(false), ptr(NULL) {}
-    PyObj(ptr_t p): dec(false), ptr(p) {}
-    ~PyObj() { if(dec) Py_DECREF(ptr); }
-    PyObj & operator=(ptr_t p) { if(dec) Py_DECREF(ptr); ptr = p; dec = false; return *this; }
-    PyObj & incref(ptr_t p) { if(dec) Py_DECREF(ptr); ptr = p; dec = (p != NULL); return *this; }
-    operator bool() const { return ptr; }
-    operator ptr_t() const { return ptr; }
-    operator arrptr_t() const { return (arrptr_t)ptr; }
-    bool dec;
-    ptr_t ptr;
-};
-
-inline void pairwise_d2d2JJ(PyObject * pX,
-                                                        npy_intp const * __restrict__ sX,
-                                                        npy_double * __restrict__ cX,
-                                                        PyObject * pD,
-                                                        npy_intp const * __restrict__ sD,
-                                                        npy_double * __restrict__ cD,
-                                                        npy_int64 const cM,
-                                                        npy_int64 const cN);
-
-inline void pairwise_d2d2JJ(PyObject * pX,
-                                                        npy_intp const * __restrict__ sX,
-                                                        npy_double * __restrict__ cX,
-                                                        PyObject * pD,
-                                                        npy_intp const * __restrict__ sD,
-                                                        npy_double * __restrict__ cD,
-                                                        npy_int64 const cM,
-                                                        npy_int64 const cN){
-
-    npy_double cd = 0.0;
-    npy_double ctmp = 0.0;
-
-    npy_intp cj;
-    npy_intp ck;
-    npy_intp x_i_idx;
-    npy_intp x_j_idx;
-    npy_intp d_i_idx;
-    npy_intp const xp = sX[1];
-    npy_intp const dp = sD[1];
-
-    for (npy_intp ci = 0; ci < cM; ++ci) {
-        x_i_idx = ci*xp;
-        d_i_idx = ci*dp;
-        for (cj = 0; cj < cM; ++cj) {
-            cd = 0.0;
-            x_j_idx = cj*xp;
-            for (ck = 0; ck < cN; ++ck) {
-                ctmp = (cX[(x_i_idx + ck)] - cX[(x_j_idx + ck)]);
-                cd += (ctmp * ctmp);
-            }
-            cD[(d_i_idx + cj)] = std::sqrt(cd);
-        }
-    }
-}
-
-void sighandler(int sig);
-#include <signal.h>
-extern "C" {
-    PyObject * create_signature;
-    struct sigaction slot;
-    PyObject * set_create_signature(PyObject * self, PyObject * args) {
-        if (!PyArg_ParseTuple(args, "O", &create_signature)) {
-            PyErr_SetString(PyExc_ValueError, "Invalid Argument to set_create_signature!");
-            return NULL;
-        }
-        Py_INCREF(create_signature);
-        memset(&slot, 0, sizeof(slot));
-        slot.sa_handler = &sighandler;
-        sigaction(SIGSEGV, &slot, NULL);
-        sigaction(SIGBUS, &slot, NULL);
-        Py_INCREF(Py_None);
-        return Py_None;
-    }
-    PyObject * run(PyObject * self, PyObject * args) {
-        {
-            PyObj pX;
-            PyObj pD;
-            PyObject * pM; npy_int64 cM;
-            PyObject * pN; npy_int64 cN;
-            if (
-                PyTuple_CheckExact(args) and PyTuple_GET_SIZE(args) == 4
-                and (pX = PyTuple_GET_ITEM(args, 0)) and PyArray_CheckExact(pX)
-                and PyArray_TYPE((PyArrayObject *)pX) == NPY_FLOAT64 and PyArray_NDIM((PyArrayObject *)pX) == 2
-                and (pD = PyTuple_GET_ITEM(args, 1)) and PyArray_CheckExact(pD)
-                and PyArray_TYPE((PyArrayObject *)pD) == NPY_FLOAT64 and PyArray_NDIM((PyArrayObject *)pD) == 2
-                and (pM = PyTuple_GET_ITEM(args, 2)) and PyLong_CheckExact(pM)
-                and (pN = PyTuple_GET_ITEM(args, 3)) and PyLong_CheckExact(pN)
-            ) {
-                if (!(pX.incref((PyObject *)PyArray_GETCONTIGUOUS((PyArrayObject *)pX)))) {
-                    PyErr_SetString(PyExc_ValueError, "Invalid Argument type on X!");
-                    return NULL;
-                }
-                if (!(pD.incref((PyObject *)PyArray_GETCONTIGUOUS((PyArrayObject *)pD)))) {
-                    PyErr_SetString(PyExc_ValueError, "Invalid Argument type on D!");
-                    return NULL;
-                }
-                cM = PyLong_AS_LONG(pM);
-                cN = PyLong_AS_LONG(pN);
-                try {
-                    pairwise_d2d2JJ(
-                          pX, PyArray_SHAPE((PyArrayObject *)pX), (npy_double *)PyArray_DATA((PyArrayObject *)pX)
-                        , pD, PyArray_SHAPE((PyArrayObject *)pD), (npy_double *)PyArray_DATA((PyArrayObject *)pD)
-                        , cM
-                        , cN
-                    );
-                    Py_INCREF(Py_None);
-                    return Py_None;
-                } catch (...) {
-                    return NULL;
-                }
-            } else
-                PyErr_Clear();
-        }
-        PyObject * signatures = Py_BuildValue("(sO)", "(lp0\n(lp1\nccopy_reg\n_reconstructor\np2\n(chope._ast\nVariable\np3\nc__builtin__\nobject\np4\nNtp5\nRp6\n(dp7\nS'name'\np8\nS'X'\np9\nsS'dtype'\np10\nS'float64'\np11\nsS'dims'\np12\nI2\nsbag2\n(g3\ng4\nNtp13\nRp14\n(dp15\ng8\nS'D'\np16\nsg10\ng11\nsg12\nI2\nsbag2\n(g3\ng4\nNtp17\nRp18\n(dp19\ng8\nS'M'\np20\nsg10\nc__builtin__\nint\np21\nsg12\nI0\nsbag2\n(g3\ng4\nNtp22\nRp23\n(dp24\ng8\nS'N'\np25\nsg10\ng21\nsg12\nI0\nsbaa.", args);
-        if (!signatures) {
-            PyErr_SetString(PyExc_ValueError, "Error building signature string for pairwise");
-            return NULL;
-        }
-        return PyObject_Call(create_signature, signatures, NULL);
-    }
-
-    PyMethodDef pairwiseMethods[] = {
-        { "set_create_signature", set_create_signature, METH_VARARGS, "signal handler" },
-        { "run", run, METH_VARARGS, "module function" },
-        { NULL, NULL, 0, NULL }
-    };
-
-
-    static struct PyModuleDef pairwisemodule = {
-        PyModuleDef_HEAD_INIT,
-        "pairwise",
-        NULL,
-        -1,
-        pairwiseMethods
-    };
-
-
-   PyMODINIT_FUNC PyInit_pairwise(void) {
-        import_array();
-        PyImport_ImportModule("numpy");
-        return PyModule_Create(&pairwisemodule);
-   }
-}
-#include <string>
-#include <sstream>
-#include <iostream>
-#include <cxxabi.h>
-#include <execinfo.h>
-void sighandler(int sig) {
-    std::ostringstream buffer;
-    buffer << "Abort by " << (sig == SIGSEGV ? "segfault" : "bus error") << std::endl;
-    void * stack[64];
-    std::size_t depth = backtrace(stack, 64);
-    if (!depth)
-        buffer << "  <empty stacktrace, possibly corrupt>" << std::endl;
-    else {
-        char ** symbols = backtrace_symbols(stack, depth);
-        for (std::size_t i = 1; i < depth; ++i) {
-            std::string symbol = symbols[i];
-                if (symbol.find_first_of(' ', 59) != std::string::npos) {
-                    std::string name = symbol.substr(59, symbol.find_first_of(' ', 59) - 59);
-                    int status;
-                    char * demangled = abi::__cxa_demangle(name.c_str(), NULL, NULL, &status);
-                    if (!status) {
-                        buffer << "    "
-                            << symbol.substr(0, 59)
-                            << demangled
-                            << symbol.substr(59 + name.size())
-                            << std::endl;
-                        free(demangled);
-                    } else
-                        buffer << "    " << symbol << std::endl;
-                } else
-                    buffer << "    " << symbol << std::endl;
-            }
-            free(symbols);
-        }
-        std::cerr << buffer.str();
-        std::exit(EXIT_FAILURE);
-    }
-
- -
-
-
- -
-
- - -
- -
- - -
-
Overwriting src/pairwise.cpp
-
-
-
- -
-
- -
-
-
-
-
-

Star point spread function CPP

- -
-
-
-
-
-
In [14]:
-
-
-
%%file src/pdf.cpp
-#define PY_ARRAY_UNIQUE_SYMBOL pdf_ARRAY_API
-#define NPY_NO_DEPRECATED_API NPY_1_7_API_VERSION
-#include <Python.h>
-#include <numpy/arrayobject.h>
-#include <numpy/arrayscalars.h>
-#include <cmath>
-#include <tuple>
-#include <numeric>
-#include <cstdint>
-#include <cstdlib>
-#include <exception>
-#include <functional>
-#include <type_traits>
-struct PyObj {
-    typedef PyObject * ptr_t;
-    typedef PyArrayObject * arrptr_t;
-    PyObj(): dec(false), ptr(NULL) {}
-    PyObj(ptr_t p): dec(false), ptr(p) {}
-    ~PyObj() { if(dec) Py_DECREF(ptr); }
-    PyObj & operator=(ptr_t p) { if(dec) Py_DECREF(ptr); ptr = p; dec = false; return *this; }
-    PyObj & incref(ptr_t p) { if(dec) Py_DECREF(ptr); ptr = p; dec = (p != NULL); return *this; }
-    operator bool() const { return ptr; }
-    operator ptr_t() const { return ptr; }
-    operator arrptr_t() const { return (arrptr_t)ptr; }
-    bool dec;
-    ptr_t ptr;
-};
-
-inline std::tuple<PyObject *, npy_intp const *, npy_float *> pdf_f2l1d1f2JDd(
-      PyObject * pdensity
-  , npy_intp const * __restrict__ sdensity
-  , npy_float * __restrict__ cdensity
-    , PyObject * pdims
-    , npy_intp const * __restrict__ sdims
-    , npy_int64 * __restrict__ cdims
-    , PyObject * pcenter
-    , npy_intp const * __restrict__ scenter
-    , npy_double * __restrict__ ccenter
-    , PyObject * pw2D
-    , npy_intp const * __restrict__ sw2D
-    , npy_float * __restrict__ cw2D
-    , npy_int64 cr50
-    , npy_double cb
-    , npy_double ca);
-
-inline std::tuple<PyObject *, npy_intp const *, npy_float *> pdf_f2l1d1f2JDd(
-      PyObject * pdensity
-  , npy_intp const * __restrict__ sdensity
-  , npy_float * __restrict__ cdensity
-    , PyObject * pdims
-    , npy_intp const * __restrict__ sdims
-    , npy_int64 * __restrict__ cdims
-    , PyObject * pcenter
-    , npy_intp const * __restrict__ scenter
-    , npy_double * __restrict__ ccenter
-    , PyObject * pw2D
-    , npy_intp const * __restrict__ sw2D
-    , npy_float * __restrict__ cw2D
-    , npy_int64 const cr50
-    , npy_double const cb
-    , npy_double const ca) {
-
-    npy_double cdr;
-    npy_double c__sum0;
-    const npy_double x_center = ccenter[0];
-    const npy_double y_center = ccenter[1];
-    const npy_intp len_0_sw2D = sw2D[0];
-    const npy_intp len_1_sw2D = sw2D[1];
-    npy_intp dc[] = {len_0_sw2D, len_1_sw2D};
-    PyObject * pc = PyArray_EMPTY(2, dc, NPY_FLOAT64, 0);
-    npy_intp * sc = PyArray_SHAPE((PyArrayObject *)pc);
-    npy_double * cc = (npy_double *)PyArray_DATA((PyArrayObject *)pc);
-
-    npy_intp cy = 0;
-    npy_intp i0 = 0;
-    npy_intp i1 = 0;
-    npy_intp i2 = 0;
-    npy_intp i3 = 0;
-
-    npy_intp sw2D_i0_idx;
-    npy_intp sw2D_i2_idx;
-    npy_intp density_x_idx;
-
-    auto c__sp0 = ca * ca;
-    auto c__sp1 = cr50 * cr50;
-    auto c__sp2 = 1.0 / (c__sp0 * c__sp1);
-    auto c__sp4 = 0.3183098861846737 * c__sp2 * (-1 + cb);
-
-    for (npy_intp cx = 0; cx < cdims[(int)(0)]; ++cx) {
-
-        density_x_idx = cx*sdensity[1];
-        for (cy = 0; cy < cdims[(int)(1)]; ++cy) {
-
-            cdr = std::sqrt(std::pow(cx - x_center, 2) + std::pow(cy - y_center, 2));
-            auto c__sp3 = cdr * cdr;
-            auto c__sp5 = c__sp4 * std::pow((1 + c__sp2 * c__sp3), -cb);
-
-            for (i0 = 0; i0 < len_0_sw2D - 0; ++i0) {
-
-                sw2D_i0_idx = (i0)*len_1_sw2D;
-                for (i1 = 0; i1 < len_1_sw2D - 0; ++i1) {
-                    cc[sw2D_i0_idx + i1] = c__sp5 * cw2D[sw2D_i0_idx + i1];
-                }
-            }
-
-            c__sum0 = 0;
-            for (i2 = 0; i2 < len_0_sw2D - 0; ++i2) {
-
-                sw2D_i2_idx = (i2)*len_1_sw2D;
-                for (i3 = 0; i3 < len_1_sw2D - 0; ++i3) {
-                    c__sum0 += cc[sw2D_i2_idx + i3];
-                }
-            }
-
-            cdensity[(density_x_idx + cy)] = c__sum0;
-        }
-    }
-    return std::make_tuple((PyObject *)pdensity, sdensity, cdensity);
-}
-
-void sighandler(int sig);
-#include <signal.h>
-extern "C" {
-    PyObject * create_signature;
-    struct sigaction slot;
-    PyObject * set_create_signature(PyObject * self, PyObject * args) {
-        if (!PyArg_ParseTuple(args, "O", &create_signature)) {
-            PyErr_SetString(PyExc_ValueError, "Invalid Argument to set_create_signature!");
-            return NULL;
-        }
-        Py_INCREF(create_signature);
-        memset(&slot, 0, sizeof(slot));
-        slot.sa_handler = &sighandler;
-        sigaction(SIGSEGV, &slot, NULL);
-        sigaction(SIGBUS, &slot, NULL);
-        Py_INCREF(Py_None);
-        return Py_None;
-    }
-    PyObject * run(PyObject * self, PyObject * args) {
-        {
-            PyObj pdensity;
-            PyObj pdims;
-            PyObj pcenter;
-            PyObj pw2D;
-            PyObject * pr50; npy_int64 cr50;
-            PyObject * pb; npy_double cb;
-            PyObject * pa; npy_double ca;
-            if (
-                PyTuple_CheckExact(args) and PyTuple_GET_SIZE(args) == 7
-                and (pdensity = PyTuple_GET_ITEM(args, 0)) and PyArray_CheckExact(pdensity)
-                and PyArray_TYPE((PyArrayObject *)pdensity) == NPY_FLOAT32 and PyArray_NDIM((PyArrayObject *)pdensity) == 2
-                and (pdims = PyTuple_GET_ITEM(args, 1)) and PyArray_CheckExact(pdims)
-                and PyArray_TYPE((PyArrayObject *)pdims) == NPY_INT64 and PyArray_NDIM((PyArrayObject *)pdims) == 1
-                and (pcenter = PyTuple_GET_ITEM(args, 2)) and PyArray_CheckExact(pcenter)
-                and PyArray_TYPE((PyArrayObject *)pcenter) == NPY_FLOAT64 and PyArray_NDIM((PyArrayObject *)pcenter) == 1
-                and (pw2D = PyTuple_GET_ITEM(args, 3)) and PyArray_CheckExact(pw2D)
-                and PyArray_TYPE((PyArrayObject *)pw2D) == NPY_FLOAT32 and PyArray_NDIM((PyArrayObject *)pw2D) == 2
-                and (pr50 = PyTuple_GET_ITEM(args, 4)) and PyLong_CheckExact(pr50)
-                and (pb = PyTuple_GET_ITEM(args, 5)) and PyFloat_CheckExact(pb)
-                and (pa = PyTuple_GET_ITEM(args, 6)) and PyArray_IsScalar(pa, Double)
-            ) {
-                if (!(pdensity.incref((PyObject *)PyArray_GETCONTIGUOUS((PyArrayObject *)pdensity)))) {
-                    PyErr_SetString(PyExc_ValueError, "Invalid Argument type on density!");
-                    return NULL;
-                }
-                if (!(pdims.incref((PyObject *)PyArray_GETCONTIGUOUS((PyArrayObject *)pdims)))) {
-                    PyErr_SetString(PyExc_ValueError, "Invalid Argument type on dims!");
-                    return NULL;
-                }
-                if (!(pcenter.incref((PyObject *)PyArray_GETCONTIGUOUS((PyArrayObject *)pcenter)))) {
-                    PyErr_SetString(PyExc_ValueError, "Invalid Argument type on center!");
-                    return NULL;
-                }
-                if (!(pw2D.incref((PyObject *)PyArray_GETCONTIGUOUS((PyArrayObject *)pw2D)))) {
-                    PyErr_SetString(PyExc_ValueError, "Invalid Argument type on w2D!");
-                    return NULL;
-                }
-                cr50 = PyLong_AS_LONG(pr50);
-                cb = PyFloat_AS_DOUBLE(pb);
-                ca = PyArrayScalar_VAL(pa, Double);
-                try {
-                    PyObject * res = std::get<0>(pdf_f2l1d1f2JDd(
-                          pdensity, PyArray_SHAPE((PyArrayObject *)pdensity), (npy_float *)PyArray_DATA((PyArrayObject *)pdensity)
-                        , pdims, PyArray_SHAPE((PyArrayObject *)pdims), (npy_int64 *)PyArray_DATA((PyArrayObject *)pdims)
-                        , pcenter, PyArray_SHAPE((PyArrayObject *)pcenter), (npy_double *)PyArray_DATA((PyArrayObject *)pcenter)
-                        , pw2D, PyArray_SHAPE((PyArrayObject *)pw2D), (npy_float *)PyArray_DATA((PyArrayObject *)pw2D)
-                        , cr50
-                        , cb
-                        , ca
-                    ));
-
-                    Py_INCREF(res);
-                    return res;
-                } catch (...) {
-                    return NULL;
-                }
-            } else
-                PyErr_Clear();
-        }
-        PyObject * signatures = Py_BuildValue("(sO)", "(lp0\n(lp1\nccopy_reg\n_reconstructor\np2\n(chope._ast\nVariable\np3\nc__builtin__\nobject\np4\nNtp5\nRp6\n(dp7\nS'name'\np8\nS'density'\np9\nsS'dtype'\np10\nS'float32'\np11\nsS'dims'\np12\nI2\nsbag2\n(g3\ng4\nNtp13\nRp14\n(dp15\ng8\ng12\nsg10\nS'int64'\np16\nsg12\nI1\nsbag2\n(g3\ng4\nNtp17\nRp18\n(dp19\ng8\nS'center'\np20\nsg10\nS'float64'\np21\nsg12\nI1\nsbag2\n(g3\ng4\nNtp22\nRp23\n(dp24\ng8\nS'w2D'\np25\nsg10\ng11\nsg12\nI2\nsbag2\n(g3\ng4\nNtp26\nRp27\n(dp28\ng8\nS'r50'\np29\nsg10\nc__builtin__\nint\np30\nsg12\nI0\nsbag2\n(g3\ng4\nNtp31\nRp32\n(dp33\ng8\nS'b'\np34\nsg10\nc__builtin__\nfloat\np35\nsg12\nI0\nsbag2\n(g3\ng4\nNtp36\nRp37\n(dp38\ng8\nS'a'\np39\nsg10\ng21\nsg12\nI0\nsbaa.", args);
-        if (!signatures) {
-            PyErr_SetString(PyExc_ValueError, "Error building signature string for pdf");
-            return NULL;
-        }
-        return PyObject_Call(create_signature, signatures, NULL);
-    }
-
-    PyMethodDef pdfMethods[] = {
-        { "set_create_signature", set_create_signature, METH_VARARGS, "signal handler" },
-        { "run", run, METH_VARARGS, "module function" },
-        { NULL, NULL, 0, NULL }
-    };
-
-    static struct PyModuleDef pdfmodule = {
-        PyModuleDef_HEAD_INIT,
-        "pdf",
-        NULL,
-        -1,
-        pdfMethods
-    };
-
-   PyMODINIT_FUNC PyInit_pdf(void) {
-        import_array();
-        PyImport_ImportModule("numpy");
-        return PyModule_Create(&pdfmodule);
-   }
-}
-#include <string>
-#include <sstream>
-#include <iostream>
-#include <cxxabi.h>
-#include <execinfo.h>
-void sighandler(int sig) {
-    std::ostringstream buffer;
-    buffer << "Abort by " << (sig == SIGSEGV ? "segfault" : "bus error") << std::endl;
-    void * stack[64];
-    std::size_t depth = backtrace(stack, 64);
-    if (!depth)
-        buffer << "  <empty stacktrace, possibly corrupt>" << std::endl;
-    else {
-        char ** symbols = backtrace_symbols(stack, depth);
-        for (std::size_t i = 1; i < depth; ++i) {
-            std::string symbol = symbols[i];
-                if (symbol.find_first_of(' ', 59) != std::string::npos) {
-                    std::string name = symbol.substr(59, symbol.find_first_of(' ', 59) - 59);
-                    int status;
-                    char * demangled = abi::__cxa_demangle(name.c_str(), NULL, NULL, &status);
-                    if (!status) {
-                        buffer << "    "
-                            << symbol.substr(0, 59)
-                            << demangled
-                            << symbol.substr(59 + name.size())
-                            << std::endl;
-                        free(demangled);
-                    } else
-                        buffer << "    " << symbol << std::endl;
-                } else
-                    buffer << "    " << symbol << std::endl;
-            }
-            free(symbols);
-        }
-        std::cerr << buffer.str();
-        std::exit(EXIT_FAILURE);
-    }
-
- -
-
-
- -
-
- - -
- -
- - -
-
Overwriting src/pdf.cpp
-
-
-
- -
-
- -
-
-
-
-
-

Python helper module

-
-
-
-
-
-
In [15]:
-
-
-
%%file native_util.py
-import importlib
-import os
-import glob
-import sys
-
-from numpy.distutils.misc_util import get_numpy_include_dirs
-import setuptools
-from os import listdir
-# import tempfile
-from hope import config
-
-try:
-    # python 3
-    import io
-    file = io.IOBase
-except ImportError:
-    pass
-
-
-def load(name):
-    compile(name, "./src")
-    module = importlib.import_module(name)
-    # module = __import__(name, globals(), locals(), [], -1)
-    return module
-
-
-
-def compile(name, src_folder, target_folder = "./"):
-    localfilename =os.path.join(src_folder, name)
-    
-    outfile, stdout, stderr, argv = None, None, None, sys.argv
-    try:
-        sys.stdout.flush(), sys.stderr.flush()
-        outpath = os.path.join(target_folder, "{0}.out".format(localfilename))
-        if os.path.exists(outpath):
-            os.remove(outpath)
-            
-        so_path = os.path.join(target_folder, "{0}.so".format(name))
-        if os.path.exists(so_path):
-            os.remove(so_path)
-            
-        outfile = open(outpath, 'w')
-        
-        try:
-            sys.stdout.fileno()
-            sys.stderr.fileno()
-            have_fileno = True
-        except OSError:
-            have_fileno = False
-        
-        if have_fileno and isinstance(sys.stdout, file) and  isinstance(sys.stdout, file):
-            stdout, stderr = os.dup(sys.stdout.fileno()), os.dup(sys.stderr.fileno())
-            os.dup2(outfile.fileno(), sys.stdout.fileno())
-            os.dup2(outfile.fileno(), sys.stderr.fileno())
-        else:
-            stdout, stderr = sys.stdout, sys.stderr
-            sys.stdout, sys.stderr = outfile, outfile
-        try:
-            sources = "./{0}.cpp".format(localfilename)
-            sys.argv = ["", "build_ext",
-                        "-b", target_folder,  #--build-lib (-b)     directory for compiled extension modules
-                        "-t", "." #--build-temp - a rel path will result in a dir structure of -b at the cur position 
-                        ]
-    
-            localfilename = str(localfilename)
-            sources = str(sources)
-    
-            setuptools.setup( \
-                  name = name\
-                , ext_modules = [setuptools.Extension( \
-                      name \
-                    , sources = [sources] \
-                    , extra_compile_args = config.cxxflags \
-                  )] \
-                , include_dirs = get_numpy_include_dirs() \
-            )
-        except SystemExit as e:
-            print(sys.stderr.write(str(e)))
-        sys.stdout.flush(), sys.stderr.flush()
-    finally:
-        if isinstance(stdout, int):
-            os.dup2(stdout, sys.stdout.fileno()), os.close(stdout)
-        elif not stdout is None:
-            sys.stdout = stdout
-        if isinstance(stderr, int):
-            os.dup2(stderr, sys.stderr.fileno()), os.close(stderr)
-        elif not stderr is None:
-            sys.stderr = stderr
-        if isinstance(outfile, file):
-            outfile.close()
-        sys.argv = argv
-    
-    with open(outpath) as outfile:
-        out = outfile.read()
-    
-    modules = glob.glob(os.path.join(target_folder, "{}*.so".format(name)))
-        
-    if not modules or out.find("error:") > -1:
-        print(out)
-        raise Exception("Error compiling function {0} (compiled to {1})".format(localfilename, target_folder))
-    
-    if out.find("warning:") > -1:
-        import warnings
-        warnings.warn("A warning has been issued during compilation:\n%s"%out)
-    
-    print(out)
-
-
-def compile_all():
-    src_folder = "./src"
-    func_names = (src_file.split(".cpp")[0] for src_file in listdir(src_folder) if src_file.endswith(".cpp"))
-    for func_name in func_names:
-        compile(func_name, src_folder)
-    
-    
-if __name__ == '__main__':
-    compile_all()
-
- -
-
-
- -
-
- - -
- -
- - -
-
Overwriting native_util.py
-
-
-
- -
-
- -
-
-
-
In [16]:
-
-
-
%%file util.py
-# Copyright (C) 2014 ETH Zurich, Institute for Astronomy
-
-'''
-Created on Aug 4, 2014
-
-author: jakeret
-'''
-from __future__ import print_function, division, absolute_import, unicode_literals
-import math
-
-def perf_comp_data(func_list, data_list, rep=5, number=1, extra_setup=None):
-    ''' Function to compare the performance of different functions.
-    
-    Parameters
-    ==========
-    func_list : list
-        list with function names as strings
-    data_list : list
-        list with data set names as strings
-    rep : int
-        number of repetitions of the whole comparison
-    number : int
-        number of executions for every function
-    '''
-    from timeit import repeat
-    res_list = {}
-    for name in enumerate(func_list):
-        if data_list is None:
-            stmt = "%s()"%(name[1])
-            setup = "from __main__ import %s"%(name[1]) 
-        else:
-            stmt = "%s(%s)"%(name[1], data_list[name[0]])
-            setup = "from __main__ import %s, %s"%(name[1], data_list[name[0]])
-        if extra_setup is not None:
-            stmt = extra_setup + "; " + stmt
-              
-        results = repeat(stmt=stmt, setup=setup, repeat=rep, number=number)
-        
-        res_list[name[1]] = (median(results), min(results))
-        
-#     res_sort = sorted(res_list.iteritems(), key=lambda (k, v): (v, k))
-    res_sort = sorted(iter(res_list.items()), key=lambda k_v: (k_v[1], k_v[0]))
-    for func, (av_time, min_time) in res_sort:
-        rel = av_time / res_sort[0][1][0]
-        print('function: {0!s:20}, av. time sec: {1:>12.8f}, min. time sec: {2:>12.8f}, relative: {3:>9.1f}'.format(func, av_time, min_time, rel))
-
-def median(x):
-    s_x = sorted(x)
-    return (s_x[int(math.floor((len(s_x)-1)/2))] + s_x[int(math.ceil((len(s_x)-1)/2))])/2
-
- -
-
-
- -
-
- - -
- -
- - -
-
Overwriting util.py
-
-
-
- -
-
- -
-
-
- - - - - - diff --git a/benchmarks/native_cpp_gen.nbconvert.html b/benchmarks/native_cpp_gen.nbconvert.html deleted file mode 100644 index 2344978..0000000 --- a/benchmarks/native_cpp_gen.nbconvert.html +++ /dev/null @@ -1,14392 +0,0 @@ - - - -native_cpp_gen.nbconvert - - - - - - - - - - - - - - - - - - - - -
-
- -
-
-
-
-

IPython magic extension version_information

-
-
-
-
-
-
-
-

Use the '%version_information' IPython magic extension in a notebook to display information about which versions of dependency package that was used to run the notebook. -Installation.

-

Run

- -
pip install git+https://github.com/jrjohansson/version_information
-
-
-
-

to install this extension

- -
-
-
-
-
-
-
-

Installation

-
-
-
-
-
-
In [1]:
-
-
-
%load_ext version_information
-
- -
-
-
- -
-
-
-
In [2]:
-
-
-
%version_information
-
- -
-
-
- -
-
- - -
- -
Out[2]:
- - - -
-
SoftwareVersion
Python3.5.1 64bit [GCC 4.2.1 (Apple Inc. build 5666) (dot 3)]
IPython6.1.0
OSDarwin 15.6.0 x86_64 i386 64bit
Mon Sep 04 16:15:36 2017 CEST
-
- -
- -
-
- -
-
-
-
-
-

Native CPP codes

-
-
-
-
-
-
-
-

Fibonacci CPP

-
-
-
-
-
-
In [3]:
-
-
-
!mkdir -p src
-
- -
-
-
- -
-
-
-
In [4]:
-
-
-
%%file src/fib.cpp
-#define PY_ARRAY_UNIQUE_SYMBOL fkt_ARRAY_API
-#define NPY_NO_DEPRECATED_API NPY_1_7_API_VERSION
-#include <Python.h>
-#include <numpy/arrayobject.h>
-#include <numpy/arrayscalars.h>
-#include <cmath>
-#include <tuple>
-#include <numeric>
-#include <cstdint>
-#include <cstdlib>
-#include <exception>
-#include <functional>
-#include <type_traits>
-
-
-struct PyObj {
-    typedef PyObject * ptr_t;
-    typedef PyArrayObject * arrptr_t;
-    PyObj(): dec(false), ptr(NULL) {}
-    PyObj(ptr_t p): dec(false), ptr(p) {}
-    ~PyObj() { if(dec) Py_DECREF(ptr); }
-    PyObj & operator=(ptr_t p) { if(dec) Py_DECREF(ptr); ptr = p; dec = false; return *this; }
-    PyObj & incref(ptr_t p) { if(dec) Py_DECREF(ptr); ptr = p; dec = (p != NULL); return *this; }
-    operator bool() const { return ptr; }
-    operator ptr_t() const { return ptr; }
-    operator arrptr_t() const { return (arrptr_t)ptr; }
-    bool dec;
-    ptr_t ptr;
-};
-
-inline npy_int64 fib_J(
-      npy_int64 cn
-);
-inline npy_int64 fib_J(
-      npy_int64 cn
-) {
-    if (cn < 2) {
-        return cn;
-    }
-    return fib_J(cn - 1) + fib_J(cn - 2);    
-}
-
-#include <string>
-#include <sstream>
-#include <iostream>
-#include <cxxabi.h>
-#include <execinfo.h>
-#include <signal.h>
-
-void sighandler(int sig);
-
-void sighandler(int sig) {
-    std::ostringstream buffer;
-    buffer << "Abort by " << (sig == SIGSEGV ? "segfault" : "bus error") << std::endl;
-    void * stack[64];
-    std::size_t depth = backtrace(stack, 64);
-    if (!depth)
-        buffer << "  <empty stacktrace, possibly corrupt>" << std::endl;
-    else {
-        char ** symbols = backtrace_symbols(stack, depth);
-        for (std::size_t i = 1; i < depth; ++i) {
-            std::string symbol = symbols[i];
-                if (symbol.find_first_of(' ', 59) != std::string::npos) {
-                    std::string name = symbol.substr(59, symbol.find_first_of(' ', 59) - 59);
-                    int status;
-                    char * demangled = abi::__cxa_demangle(name.c_str(), NULL, NULL, &status);
-                    if (!status) {
-                        buffer << "    "
-                            << symbol.substr(0, 59)
-                            << demangled
-                            << symbol.substr(59 + name.size())
-                            << std::endl;
-                        free(demangled);
-                    } else
-                        buffer << "    " << symbol << std::endl;
-                } else
-                    buffer << "    " << symbol << std::endl;
-            }
-            free(symbols);
-        }
-        std::cerr << buffer.str();
-        std::exit(EXIT_FAILURE);
-    }
-
-
-extern "C" {
-
-    PyObject * create_signature;
-
-    struct sigaction slot;
-
-    PyObject * set_create_signature(PyObject * self, PyObject * args) {
-        if (!PyArg_ParseTuple(args, "O", &create_signature)) {
-            PyErr_SetString(PyExc_ValueError, "Invalid Argument to set_create_signature!");
-            return NULL;
-        }
-        Py_INCREF(create_signature);
-        memset(&slot, 0, sizeof(slot));
-        slot.sa_handler = &sighandler;
-        sigaction(SIGSEGV, &slot, NULL);
-        sigaction(SIGBUS, &slot, NULL);
-        Py_INCREF(Py_None);
-        return Py_None;
-    }
-
-    PyObject * run(PyObject * self, PyObject * args) {
-        {
-            PyObject * pn; npy_int64 cn;
-            if (
-                PyTuple_CheckExact(args) and PyTuple_GET_SIZE(args) == 1
-                and (pn = PyTuple_GET_ITEM(args, 0)) and PyLong_CheckExact(pn)
-            ) {
-                cn = PyLong_AS_LONG(pn);
-                try {
-                    return Py_BuildValue("l", fib_J(
-                          cn
-                    ));
-                } catch (...) {
-                    return NULL;
-                }
-            } else
-                PyErr_Clear();
-        }
-        PyObject * signatures = Py_BuildValue("(sO)", "gANdcQBdcQFjaG9wZS5fYXN0ClZhcmlhYmxlCnECKYFxA31xBChYBAAAAGRpbXNxBUsAWAUAAABk\ndHlwZXEGY2J1aWx0aW5zCmludApxB1gEAAAAbmFtZXEIWAEAAABucQl1YmFhLg==\n", args);
-        if (!signatures) {
-            PyErr_SetString(PyExc_ValueError, "Error building signature string for fib");
-            return NULL;
-        }
-        return PyObject_Call(create_signature, signatures, NULL);
-    }
-
-    PyMethodDef fibMethods[] = {
-        { "set_create_signature", set_create_signature, METH_VARARGS, "signal handler" },
-        { "run", run, METH_VARARGS, "module function" },
-        { NULL, NULL, 0, NULL }
-    };
-
-
-    static struct PyModuleDef fibmodule = {
-        PyModuleDef_HEAD_INIT,
-        "fib",
-        NULL,
-        -1,
-        fibMethods
-    };
-
-
-
-   PyMODINIT_FUNC PyInit_fib(void) {
-        import_array();
-        PyImport_ImportModule("numpy");
-        return PyModule_Create(&fibmodule);
-   }
-}
-
- -
-
-
- -
-
- - -
- -
- - -
-
Overwriting src/fib.cpp
-
-
-
- -
-
- -
-
-
-
-
-

Quicksort CPP

-
-
-
-
-
-
In [5]:
-
-
-
%%file src/qsort_kernel.cpp
-#define PY_ARRAY_UNIQUE_SYMBOL qsort_kernel_ARRAY_API
-#define NPY_NO_DEPRECATED_API NPY_1_7_API_VERSION
-#include <Python.h>
-#include <numpy/arrayobject.h>
-#include <numpy/arrayscalars.h>
-#include <cmath>
-#include <tuple>
-#include <numeric>
-#include <cstdint>
-#include <cstdlib>
-#include <exception>
-#include <functional>
-#include <type_traits>
-struct PyObj {
- typedef PyObject * ptr_t;
- typedef PyArrayObject * arrptr_t;
- PyObj(): dec(false), ptr(NULL) {}
- PyObj(ptr_t p): dec(false), ptr(p) {}
- ~PyObj() { if(dec) Py_DECREF(ptr); }
- PyObj & operator=(ptr_t p) { if(dec) Py_DECREF(ptr); ptr = p; dec = false; return *this; }
- PyObj & incref(ptr_t p) { if(dec) Py_DECREF(ptr); ptr = p; dec = (p != NULL); return *this; }
- operator bool() const { return ptr; }
- operator ptr_t() const { return ptr; }
- operator arrptr_t() const { return (arrptr_t)ptr; }
- bool dec;
- ptr_t ptr;
-};
-inline std::tuple<PyObject *, npy_intp const *, npy_double *> qsort_kernel_d1JJ(PyObject * pa,
-    npy_intp const * __restrict__ sa,
-                                         npy_double * __restrict__ ca,
-                                         npy_int64 clo,
-                                         npy_int64 chi);
-
-inline std::tuple<PyObject *, npy_intp const *, npy_double *> qsort_kernel_d1JJ(PyObject * pa,
-                                         npy_intp const * __restrict__ sa,
-                                         npy_double * __restrict__ ca,
-                                         npy_int64 clo,
-                                         npy_int64 chi){
- npy_int64 ci = clo;
- npy_int64 cj = chi;
- npy_double cpivot;
-
- while (ci < chi) {
-  cpivot = ca[(int)((clo + chi) / 2)];
-
-  while (ci <= cj) {
-   while (ca[ci] < cpivot) {
-    ci += 1;
-   }
-
-   while (ca[cj] > cpivot) {
-    cj -= 1;
-   }
-
-   if (ci <= cj) {
-    auto ctmp = ca[ci];
-    ca[ci] = ca[cj];
-    ca[cj] = ctmp;
-    ci += 1;
-    cj -= 1;
-   }
-  }
-
-  if (clo < cj) {
-   qsort_kernel_d1JJ(pa, sa, ca, clo, cj);
-  }
-
-  clo = ci;
-  cj = chi;
- }
-
- return std::make_tuple((PyObject *)pa, sa, ca);
-}
-
-void sighandler(int sig);
-#include <signal.h>
-extern "C" {
- PyObject * create_signature;
- struct sigaction slot;
- PyObject * set_create_signature(PyObject * self, PyObject * args) {
-  if (!PyArg_ParseTuple(args, "O", &create_signature)) {
-   PyErr_SetString(PyExc_ValueError, "Invalid Argument to set_create_signature!");
-   return NULL;
-  }
-  Py_INCREF(create_signature);
-  memset(&slot, 0, sizeof(slot));
-  slot.sa_handler = &sighandler;
-  sigaction(SIGSEGV, &slot, NULL);
-  sigaction(SIGBUS, &slot, NULL);
-  Py_INCREF(Py_None);
-  return Py_None;
- }
- PyObject * run(PyObject * self, PyObject * args) {
-  {
-   PyObj pa;
-   PyObject * plo; npy_int64 clo;
-   PyObject * phi; npy_int64 chi;
-   if (
-    PyTuple_CheckExact(args) and PyTuple_GET_SIZE(args) == 3
-    and (pa = PyTuple_GET_ITEM(args, 0)) and PyArray_CheckExact(pa)
-    and PyArray_TYPE((PyArrayObject *)pa) == NPY_FLOAT64 and PyArray_NDIM((PyArrayObject *)pa) == 1
-    and (plo = PyTuple_GET_ITEM(args, 1)) and PyLong_CheckExact(plo)
-    and (phi = PyTuple_GET_ITEM(args, 2)) and PyLong_CheckExact(phi)
-   ) {
-    if (!(pa.incref((PyObject *)PyArray_GETCONTIGUOUS((PyArrayObject *)pa)))) {
-     PyErr_SetString(PyExc_ValueError, "Invalid Argument type on a!");
-     return NULL;
-    }
-    clo = PyLong_AS_LONG(plo);
-    chi = PyLong_AS_LONG(phi);
-    try {
-     PyObject * res = std::get<0>(qsort_kernel_d1JJ(
-        pa, PyArray_SHAPE((PyArrayObject *)pa), (npy_double *)PyArray_DATA((PyArrayObject *)pa)
-      , clo
-      , chi
-     ));
-
-     Py_INCREF(res);
-     return res;
-    } catch (...) {
-     return NULL;
-    }
-   } else
-    PyErr_Clear();
-  }
-  PyObject * signatures = Py_BuildValue("(sO)", "(lp0\n(lp1\nccopy_reg\n_reconstructor\np2\n(chope._ast\nVariable\np3\nc__builtin__\nobject\np4\nNtp5\nRp6\n(dp7\nS'name'\np8\nS'a'\np9\nsS'dtype'\np10\nS'float64'\np11\nsS'dims'\np12\nI1\nsbag2\n(g3\ng4\nNtp13\nRp14\n(dp15\ng8\nS'lo'\np16\nsg10\nc__builtin__\nint\np17\nsg12\nI0\nsbag2\n(g3\ng4\nNtp18\nRp19\n(dp20\ng8\nS'hi'\np21\nsg10\ng17\nsg12\nI0\nsbaa.", args);
-  if (!signatures) {
-   PyErr_SetString(PyExc_ValueError, "Error building signature string for qsort_kernel");
-   return NULL;
-  }
-  return PyObject_Call(create_signature, signatures, NULL);
- }
- PyMethodDef qsort_kernelMethods[] = {
-  { "set_create_signature", set_create_signature, METH_VARARGS, "signal handler" },
-  { "run", run, METH_VARARGS, "module function" },
-  { NULL, NULL, 0, NULL }
- };
-}
-
-static struct PyModuleDef qsort_kernel_module = {
-    PyModuleDef_HEAD_INIT,
-    "qsort_kernel",
-    NULL,
-    -1,
-    qsort_kernelMethods
-};
-
-PyMODINIT_FUNC PyInit_qsort_kernel(void) {
-    import_array();
-    PyImport_ImportModule("numpy");
-    return PyModule_Create(&qsort_kernel_module);
-}
-
-#include <string>
-#include <sstream>
-#include <iostream>
-#include <cxxabi.h>
-#include <execinfo.h>
-void sighandler(int sig) {
- std::ostringstream buffer;
- buffer << "Abort by " << (sig == SIGSEGV ? "segfault" : "bus error") << std::endl;
- void * stack[64];
- std::size_t depth = backtrace(stack, 64);
- if (!depth)
-  buffer << "  <empty stacktrace, possibly corrupt>" << std::endl;
- else {
-  char ** symbols = backtrace_symbols(stack, depth);
-  for (std::size_t i = 1; i < depth; ++i) {
-   std::string symbol = symbols[i];
-    if (symbol.find_first_of(' ', 59) != std::string::npos) {
-     std::string name = symbol.substr(59, symbol.find_first_of(' ', 59) - 59);
-     int status;
-     char * demangled = abi::__cxa_demangle(name.c_str(), NULL, NULL, &status);
-     if (!status) {
-      buffer << "    "
-       << symbol.substr(0, 59)
-       << demangled
-       << symbol.substr(59 + name.size())
-       << std::endl;
-      free(demangled);
-     } else
-      buffer << "    " << symbol << std::endl;
-    } else
-     buffer << "    " << symbol << std::endl;
-   }
-   free(symbols);
-  }
-  std::cerr << buffer.str();
-  std::exit(EXIT_FAILURE);
- }
-
- -
-
-
- -
-
- - -
- -
- - -
-
Overwriting src/qsort_kernel.cpp
-
-
-
- -
-
- -
-
-
-
-
-

Pi sum CPP

-
-
-
-
-
-
In [6]:
-
-
-
%%file src/pisum.cpp
-#define PY_ARRAY_UNIQUE_SYMBOL pisum_ARRAY_API
-#define NPY_NO_DEPRECATED_API NPY_1_7_API_VERSION
-#include <Python.h>
-#include <numpy/arrayobject.h>
-#include <numpy/arrayscalars.h>
-#include <cmath>
-#include <tuple>
-#include <numeric>
-#include <cstdint>
-#include <cstdlib>
-#include <exception>
-#include <functional>
-#include <type_traits>
-struct PyObj {
-    typedef PyObject * ptr_t;
-    typedef PyArrayObject * arrptr_t;
-    PyObj(): dec(false), ptr(NULL) {}
-    PyObj(ptr_t p): dec(false), ptr(p) {}
-    ~PyObj() { if(dec) Py_DECREF(ptr); }
-    PyObj & operator=(ptr_t p) { if(dec) Py_DECREF(ptr); ptr = p; dec = false; return *this; }
-    PyObj & incref(ptr_t p) { if(dec) Py_DECREF(ptr); ptr = p; dec = (p != NULL); return *this; }
-    operator bool() const { return ptr; }
-    operator ptr_t() const { return ptr; }
-    operator arrptr_t() const { return (arrptr_t)ptr; }
-    bool dec;
-    ptr_t ptr;
-};
-
-inline npy_double pisum_();
-
-inline npy_double pisum_() {
-    double csum = 0;
-    int ck = 1;
-    for (int cj = 1; cj < 501; ++cj) {
-        csum = 0.0;
-        for (ck = 1; ck < 10001; ++ck) {
-            csum += (1.0 / (double)(ck * ck));
-        }
-    }
-
-    return npy_double(csum);
-}
-
-void sighandler(int sig);
-#include <signal.h>
-extern "C" {
-    PyObject * create_signature;
-    struct sigaction slot;
-    PyObject * set_create_signature(PyObject * self, PyObject * args) {
-        if (!PyArg_ParseTuple(args, "O", &create_signature)) {
-            PyErr_SetString(PyExc_ValueError, "Invalid Argument to set_create_signature!");
-            return NULL;
-        }
-        Py_INCREF(create_signature);
-        memset(&slot, 0, sizeof(slot));
-        slot.sa_handler = &sighandler;
-        sigaction(SIGSEGV, &slot, NULL);
-        sigaction(SIGBUS, &slot, NULL);
-        Py_INCREF(Py_None);
-        return Py_None;
-    }
-    PyObject * run(PyObject * self, PyObject * args) {
-        {                try {
-                    return Py_BuildValue("d", pisum_());
-                } catch (...) {
-                    return NULL;
-                }
-        }
-        PyObject * signatures = Py_BuildValue("(sO)", "(lp0\n(lp1\na.", args);
-        if (!signatures) {
-            PyErr_SetString(PyExc_ValueError, "Error building signature string for pisum");
-            return NULL;
-        }
-        return PyObject_Call(create_signature, signatures, NULL);
-    }
-
-PyMethodDef pisumMethods[] = {
-{ "set_create_signature", set_create_signature, METH_VARARGS, "signal handler" },
-{ "run", run, METH_VARARGS, "module function" },
-{ NULL, NULL, 0, NULL }
-};
-
-static struct PyModuleDef pisum_module = {
-    PyModuleDef_HEAD_INIT,
-    "pisum",
-    NULL,
-    -1,
-    pisumMethods
-};
-
-PyMODINIT_FUNC PyInit_pisum(void) {
-    import_array();
-    PyImport_ImportModule("numpy");
-    return PyModule_Create(&pisum_module);
-}
-}
-
-#include <string>
-#include <sstream>
-#include <iostream>
-#include <cxxabi.h>
-#include <execinfo.h>
-void sighandler(int sig) {
-    std::ostringstream buffer;
-    buffer << "Abort by " << (sig == SIGSEGV ? "segfault" : "bus error") << std::endl;
-    void * stack[64];
-    std::size_t depth = backtrace(stack, 64);
-    if (!depth)
-        buffer << "  <empty stacktrace, possibly corrupt>" << std::endl;
-    else {
-        char ** symbols = backtrace_symbols(stack, depth);
-        for (std::size_t i = 1; i < depth; ++i) {
-            std::string symbol = symbols[i];
-                if (symbol.find_first_of(' ', 59) != std::string::npos) {
-                    std::string name = symbol.substr(59, symbol.find_first_of(' ', 59) - 59);
-                    int status;
-                    char * demangled = abi::__cxa_demangle(name.c_str(), NULL, NULL, &status);
-                    if (!status) {
-                        buffer << "    "
-                            << symbol.substr(0, 59)
-                            << demangled
-                            << symbol.substr(59 + name.size())
-                            << std::endl;
-                        free(demangled);
-                    } else
-                        buffer << "    " << symbol << std::endl;
-                } else
-                    buffer << "    " << symbol << std::endl;
-            }
-            free(symbols);
-        }
-        std::cerr << buffer.str();
-        std::exit(EXIT_FAILURE);
-    }
-
- -
-
-
- -
-
- - -
- -
- - -
-
Overwriting src/pisum.cpp
-
-
-
- -
-
- -
-
-
-
In [7]:
-
-
-
%%file src/pisum_opt.cpp
-#define PY_ARRAY_UNIQUE_SYMBOL pisum_opt_ARRAY_API
-#define NPY_NO_DEPRECATED_API NPY_1_7_API_VERSION
-#include <Python.h>
-#include <numpy/arrayobject.h>
-#include <numpy/arrayscalars.h>
-#include <cmath>
-#include <tuple>
-#include <numeric>
-#include <cstdint>
-#include <cstdlib>
-#include <exception>
-#include <functional>
-#include <type_traits>
-struct PyObj {
-    typedef PyObject * ptr_t;
-    typedef PyArrayObject * arrptr_t;
-    PyObj(): dec(false), ptr(NULL) {}
-    PyObj(ptr_t p): dec(false), ptr(p) {}
-    ~PyObj() { if(dec) Py_DECREF(ptr); }
-    PyObj & operator=(ptr_t p) { if(dec) Py_DECREF(ptr); ptr = p; dec = false; return *this; }
-    PyObj & incref(ptr_t p) { if(dec) Py_DECREF(ptr); ptr = p; dec = (p != NULL); return *this; }
-    operator bool() const { return ptr; }
-    operator ptr_t() const { return ptr; }
-    operator arrptr_t() const { return (arrptr_t)ptr; }
-    bool dec;
-    ptr_t ptr;
-};
-
-inline npy_double pisum_opt_();
-
-inline npy_double pisum_opt_() {
-    npy_double csum = npy_double();
-    npy_intp ck = 1;
-    npy_double cf = 0.0;
-
-    for (npy_intp cj = 1; cj < 501; ++cj) {
-        csum = 0.0;
-        cf = 0.0;
-        for (ck = 1; ck < 10001; ++ck) {
-            cf += 1.0;
-            auto c__sp0 = (cf * cf);
-            csum += (1.0 / c__sp0);
-        }
-    }
-    return csum;
-}
-
-void sighandler(int sig);
-#include <signal.h>
-extern "C" {
-    PyObject * create_signature;
-    struct sigaction slot;
-    PyObject * set_create_signature(PyObject * self, PyObject * args) {
-        if (!PyArg_ParseTuple(args, "O", &create_signature)) {
-            PyErr_SetString(PyExc_ValueError, "Invalid Argument to set_create_signature!");
-            return NULL;
-        }
-        Py_INCREF(create_signature);
-        memset(&slot, 0, sizeof(slot));
-        slot.sa_handler = &sighandler;
-        sigaction(SIGSEGV, &slot, NULL);
-        sigaction(SIGBUS, &slot, NULL);
-        Py_INCREF(Py_None);
-        return Py_None;
-    }
-    PyObject * run(PyObject * self, PyObject * args) {
-        {                try {
-                    return Py_BuildValue("d", pisum_opt_());
-                } catch (...) {
-                    return NULL;
-                }
-        }
-        PyObject * signatures = Py_BuildValue("(sO)", "(lp0\n(lp1\na.", args);
-        if (!signatures) {
-            PyErr_SetString(PyExc_ValueError, "Error building signature string for pisum_opt");
-            return NULL;
-        }
-        return PyObject_Call(create_signature, signatures, NULL);
-    }
-
-    PyMethodDef pisum_optMethods[] = {
-    { "set_create_signature", set_create_signature, METH_VARARGS, "signal handler" },
-    { "run", run, METH_VARARGS, "module function" },
-    { NULL, NULL, 0, NULL }
-    };
-
-    static struct PyModuleDef pisum_opt_module = {
-        PyModuleDef_HEAD_INIT,
-        "pisum_opt",
-        NULL,
-        -1,
-        pisum_optMethods
-    };
-
-    PyMODINIT_FUNC PyInit_pisum_opt(void) {
-        import_array();
-        PyImport_ImportModule("numpy");
-        return PyModule_Create(&pisum_opt_module);
-    }
-}
-
-#include <string>
-#include <sstream>
-#include <iostream>
-#include <cxxabi.h>
-#include <execinfo.h>
-void sighandler(int sig) {
-    std::ostringstream buffer;
-    buffer << "Abort by " << (sig == SIGSEGV ? "segfault" : "bus error") << std::endl;
-    void * stack[64];
-    std::size_t depth = backtrace(stack, 64);
-    if (!depth)
-        buffer << "  <empty stacktrace, possibly corrupt>" << std::endl;
-    else {
-        char ** symbols = backtrace_symbols(stack, depth);
-        for (std::size_t i = 1; i < depth; ++i) {
-            std::string symbol = symbols[i];
-                if (symbol.find_first_of(' ', 59) != std::string::npos) {
-                    std::string name = symbol.substr(59, symbol.find_first_of(' ', 59) - 59);
-                    int status;
-                    char * demangled = abi::__cxa_demangle(name.c_str(), NULL, NULL, &status);
-                    if (!status) {
-                        buffer << "    "
-                            << symbol.substr(0, 59)
-                            << demangled
-                            << symbol.substr(59 + name.size())
-                            << std::endl;
-                        free(demangled);
-                    } else
-                        buffer << "    " << symbol << std::endl;
-                } else
-                    buffer << "    " << symbol << std::endl;
-            }
-            free(symbols);
-        }
-        std::cerr << buffer.str();
-        std::exit(EXIT_FAILURE);
-    }
-
- -
-
-
- -
-
- - -
- -
- - -
-
Overwriting src/pisum_opt.cpp
-
-
-
- -
-
- -
-
-
-
-
-

10th order poly log approx CPP

-
-
-
-
-
-
In [8]:
-
-
-
%%file src/ln.cpp
-#define PY_ARRAY_UNIQUE_SYMBOL ln_hope_ARRAY_API
-#define NPY_NO_DEPRECATED_API NPY_1_7_API_VERSION
-#include <Python.h>
-#include <numpy/arrayobject.h>
-#include <numpy/arrayscalars.h>
-#include <cmath>
-#include <tuple>
-#include <numeric>
-#include <cstdint>
-#include <cstdlib>
-#include <exception>
-#include <functional>
-#include <type_traits>
-struct PyObj {
-    typedef PyObject * ptr_t;
-    typedef PyArrayObject * arrptr_t;
-    PyObj(): dec(false), ptr(NULL) {}
-    PyObj(ptr_t p): dec(false), ptr(p) {}
-    ~PyObj() { if(dec) Py_DECREF(ptr); }
-    PyObj & operator=(ptr_t p) { if(dec) Py_DECREF(ptr); ptr = p; dec = false; return *this; }
-    PyObj & incref(ptr_t p) { if(dec) Py_DECREF(ptr); ptr = p; dec = (p != NULL); return *this; }
-    operator bool() const { return ptr; }
-    operator ptr_t() const { return ptr; }
-    operator arrptr_t() const { return (arrptr_t)ptr; }
-    bool dec;
-    ptr_t ptr;
-};
-
-inline void ln_hope_d1d1(PyObject * pX, npy_intp const * __restrict__ sX, npy_double * __restrict__ cX,
-                                                    PyObject * pY, npy_intp const * __restrict__ sY, npy_double * __restrict__ cY);
-
-inline void ln_hope_d1d1(PyObject * pX, npy_intp const * __restrict__ sX, npy_double * __restrict__ cX,
-                                                    PyObject * pY, npy_intp const * __restrict__ sY, npy_double * __restrict__ cY){
-
-    for (npy_intp i0 = 0; i0 < sY[0] - 0; ++i0) {
-        cY[(int)(i0)] = (cX[i0] - 1) - (std::pow((cX[i0] - 1), 2) / 2) + (std::pow((cX[i0] - 1), 3) / 3) - (std::pow((cX[i0] - 1), 4) / 4) + (std::pow((cX[i0] - 1), 5) / 5) - (std::pow((cX[i0] - 1), 6) / 6) + (std::pow((cX[i0] - 1), 7) / 7) - (std::pow((cX[i0] - 1), 8) / 8) + (std::pow((cX[i0] - 1), 9) / 9);
-    }
-}
-
-void sighandler(int sig);
-#include <signal.h>
-extern "C" {
-    PyObject * create_signature;
-    struct sigaction slot;
-    PyObject * set_create_signature(PyObject * self, PyObject * args) {
-        if (!PyArg_ParseTuple(args, "O", &create_signature)) {
-            PyErr_SetString(PyExc_ValueError, "Invalid Argument to set_create_signature!");
-            return NULL;
-        }
-        Py_INCREF(create_signature);
-        memset(&slot, 0, sizeof(slot));
-        slot.sa_handler = &sighandler;
-        sigaction(SIGSEGV, &slot, NULL);
-        sigaction(SIGBUS, &slot, NULL);
-        Py_INCREF(Py_None);
-        return Py_None;
-    }
-    PyObject * run(PyObject * self, PyObject * args) {
-        {
-            PyObj pX;
-            PyObj pY;
-            if (
-                PyTuple_CheckExact(args) and PyTuple_GET_SIZE(args) == 2
-                and (pX = PyTuple_GET_ITEM(args, 0)) and PyArray_CheckExact(pX)
-                and PyArray_TYPE((PyArrayObject *)pX) == NPY_FLOAT64 and PyArray_NDIM((PyArrayObject *)pX) == 1
-                and (pY = PyTuple_GET_ITEM(args, 1)) and PyArray_CheckExact(pY)
-                and PyArray_TYPE((PyArrayObject *)pY) == NPY_FLOAT64 and PyArray_NDIM((PyArrayObject *)pY) == 1
-            ) {
-                if (!(pX.incref((PyObject *)PyArray_GETCONTIGUOUS((PyArrayObject *)pX)))) {
-                    PyErr_SetString(PyExc_ValueError, "Invalid Argument type on X!");
-                    return NULL;
-                }
-                if (!(pY.incref((PyObject *)PyArray_GETCONTIGUOUS((PyArrayObject *)pY)))) {
-                    PyErr_SetString(PyExc_ValueError, "Invalid Argument type on Y!");
-                    return NULL;
-                }
-                try {
-                    ln_hope_d1d1(
-                          pX, PyArray_SHAPE((PyArrayObject *)pX), (npy_double *)PyArray_DATA((PyArrayObject *)pX)
-                        , pY, PyArray_SHAPE((PyArrayObject *)pY), (npy_double *)PyArray_DATA((PyArrayObject *)pY)
-                    );
-                    Py_INCREF(Py_None);
-                    return Py_None;
-                } catch (...) {
-                    return NULL;
-                }
-            } else
-                PyErr_Clear();
-        }
-        PyObject * signatures = Py_BuildValue("(sO)", "(lp0\n(lp1\nccopy_reg\n_reconstructor\np2\n(chope._ast\nVariable\np3\nc__builtin__\nobject\np4\nNtp5\nRp6\n(dp7\nS'name'\np8\nS'X'\np9\nsS'dtype'\np10\nS'float64'\np11\nsS'dims'\np12\nI1\nsbag2\n(g3\ng4\nNtp13\nRp14\n(dp15\ng8\nS'Y'\np16\nsg10\ng11\nsg12\nI1\nsbaa.", args);
-        if (!signatures) {
-            PyErr_SetString(PyExc_ValueError, "Error building signature string for ln_hope");
-            return NULL;
-        }
-        return PyObject_Call(create_signature, signatures, NULL);
-    }
-
-    PyMethodDef lnMethods[] = {
-        { "set_create_signature", set_create_signature, METH_VARARGS, "signal handler" },
-        { "run", run, METH_VARARGS, "module function" },
-        { NULL, NULL, 0, NULL }
-    };
-
-
-    static struct PyModuleDef lnmodule = {
-        PyModuleDef_HEAD_INIT,
-        "ln",
-        NULL,
-        -1,
-        lnMethods
-    };
-
-
-
-   PyMODINIT_FUNC PyInit_ln(void) {
-        import_array();
-        PyImport_ImportModule("numpy");
-        return PyModule_Create(&lnmodule);
-   }
-}
-#include <string>
-#include <sstream>
-#include <iostream>
-#include <cxxabi.h>
-#include <execinfo.h>
-void sighandler(int sig) {
-    std::ostringstream buffer;
-    buffer << "Abort by " << (sig == SIGSEGV ? "segfault" : "bus error") << std::endl;
-    void * stack[64];
-    std::size_t depth = backtrace(stack, 64);
-    if (!depth)
-        buffer << "  <empty stacktrace, possibly corrupt>" << std::endl;
-    else {
-        char ** symbols = backtrace_symbols(stack, depth);
-        for (std::size_t i = 1; i < depth; ++i) {
-            std::string symbol = symbols[i];
-                if (symbol.find_first_of(' ', 59) != std::string::npos) {
-                    std::string name = symbol.substr(59, symbol.find_first_of(' ', 59) - 59);
-                    int status;
-                    char * demangled = abi::__cxa_demangle(name.c_str(), NULL, NULL, &status);
-                    if (!status) {
-                        buffer << "    "
-                            << symbol.substr(0, 59)
-                            << demangled
-                            << symbol.substr(59 + name.size())
-                            << std::endl;
-                        free(demangled);
-                    } else
-                        buffer << "    " << symbol << std::endl;
-                } else
-                    buffer << "    " << symbol << std::endl;
-            }
-            free(symbols);
-        }
-        std::cerr << buffer.str();
-        std::exit(EXIT_FAILURE);
-    }
-
- -
-
-
- -
-
- - -
- -
- - -
-
Overwriting src/ln.cpp
-
-
-
- -
-
- -
-
-
-
In [9]:
-
-
-
%%file src/ln_exp.cpp
-#define PY_ARRAY_UNIQUE_SYMBOL ln_hope_exp_ARRAY_API
-#define NPY_NO_DEPRECATED_API NPY_1_7_API_VERSION
-#include <Python.h>
-#include <numpy/arrayobject.h>
-#include <numpy/arrayscalars.h>
-#include <cmath>
-#include <tuple>
-#include <numeric>
-#include <cstdint>
-#include <cstdlib>
-#include <exception>
-#include <functional>
-#include <type_traits>
-struct PyObj {
-    typedef PyObject * ptr_t;
-    typedef PyArrayObject * arrptr_t;
-    PyObj(): dec(false), ptr(NULL) {}
-    PyObj(ptr_t p): dec(false), ptr(p) {}
-    ~PyObj() { if(dec) Py_DECREF(ptr); }
-    PyObj & operator=(ptr_t p) { if(dec) Py_DECREF(ptr); ptr = p; dec = false; return *this; }
-    PyObj & incref(ptr_t p) { if(dec) Py_DECREF(ptr); ptr = p; dec = (p != NULL); return *this; }
-    operator bool() const { return ptr; }
-    operator ptr_t() const { return ptr; }
-    operator arrptr_t() const { return (arrptr_t)ptr; }
-    bool dec;
-    ptr_t ptr;
-};
-
-inline void ln_hope_exp_d1d1(PyObject * pX, npy_intp const * __restrict__ sX, npy_double * __restrict__ cX,
-                                                    PyObject * pY, npy_intp const * __restrict__ sY, npy_double * __restrict__ cY);
-
-inline void ln_hope_exp_d1d1(PyObject * pX, npy_intp const * __restrict__ sX, npy_double * __restrict__ cX,
-                                                    PyObject * pY, npy_intp const * __restrict__ sY, npy_double * __restrict__ cY){
-
-    for (npy_intp i0 = 0; i0 < sX[0] - 0; ++i0) {
-        auto cx = (cX[i0] - 1);
-        auto cx2 = (cx * cx);
-        auto cx4 = (cx2 * cx2);
-        auto cx6 = (cx4 * cx2);
-        auto cx8 = (cx4 * cx4);
-        cY[i0] = cx - (cx2 / 2) + (cx * cx2 / 3) - (cx4 / 4) + (cx * cx4 / 5) - (cx6 / 6) + (cx6 * cx / 7) - (cx8 / 8) + (cx8 * cx / 9);
-    }
-}
-
-void sighandler(int sig);
-#include <signal.h>
-extern "C" {
-    PyObject * create_signature;
-    struct sigaction slot;
-    PyObject * set_create_signature(PyObject * self, PyObject * args) {
-        if (!PyArg_ParseTuple(args, "O", &create_signature)) {
-            PyErr_SetString(PyExc_ValueError, "Invalid Argument to set_create_signature!");
-            return NULL;
-        }
-        Py_INCREF(create_signature);
-        memset(&slot, 0, sizeof(slot));
-        slot.sa_handler = &sighandler;
-        sigaction(SIGSEGV, &slot, NULL);
-        sigaction(SIGBUS, &slot, NULL);
-        Py_INCREF(Py_None);
-        return Py_None;
-    }
-    PyObject * run(PyObject * self, PyObject * args) {
-        {
-            PyObj pX;
-            PyObj pY;
-            if (
-                PyTuple_CheckExact(args) and PyTuple_GET_SIZE(args) == 2
-                and (pX = PyTuple_GET_ITEM(args, 0)) and PyArray_CheckExact(pX)
-                and PyArray_TYPE((PyArrayObject *)pX) == NPY_FLOAT64 and PyArray_NDIM((PyArrayObject *)pX) == 1
-                and (pY = PyTuple_GET_ITEM(args, 1)) and PyArray_CheckExact(pY)
-                and PyArray_TYPE((PyArrayObject *)pY) == NPY_FLOAT64 and PyArray_NDIM((PyArrayObject *)pY) == 1
-            ) {
-                if (!(pX.incref((PyObject *)PyArray_GETCONTIGUOUS((PyArrayObject *)pX)))) {
-                    PyErr_SetString(PyExc_ValueError, "Invalid Argument type on X!");
-                    return NULL;
-                }
-                if (!(pY.incref((PyObject *)PyArray_GETCONTIGUOUS((PyArrayObject *)pY)))) {
-                    PyErr_SetString(PyExc_ValueError, "Invalid Argument type on Y!");
-                    return NULL;
-                }
-                try {
-                    ln_hope_exp_d1d1(
-                          pX, PyArray_SHAPE((PyArrayObject *)pX), (npy_double *)PyArray_DATA((PyArrayObject *)pX)
-                        , pY, PyArray_SHAPE((PyArrayObject *)pY), (npy_double *)PyArray_DATA((PyArrayObject *)pY)
-                    );
-                    Py_INCREF(Py_None);
-                    return Py_None;
-                } catch (...) {
-                    return NULL;
-                }
-            } else
-                PyErr_Clear();
-        }
-        PyObject * signatures = Py_BuildValue("(sO)", "(lp0\n(lp1\nccopy_reg\n_reconstructor\np2\n(chope._ast\nVariable\np3\nc__builtin__\nobject\np4\nNtp5\nRp6\n(dp7\nS'name'\np8\nS'X'\np9\nsS'dtype'\np10\nS'float64'\np11\nsS'dims'\np12\nI1\nsbag2\n(g3\ng4\nNtp13\nRp14\n(dp15\ng8\nS'Y'\np16\nsg10\ng11\nsg12\nI1\nsbaa.", args);
-        if (!signatures) {
-            PyErr_SetString(PyExc_ValueError, "Error building signature string for ln_hope_exp");
-            return NULL;
-        }
-        return PyObject_Call(create_signature, signatures, NULL);
-    }
-    PyMethodDef ln_expMethods[] = {
-        { "set_create_signature", set_create_signature, METH_VARARGS, "signal handler" },
-        { "run", run, METH_VARARGS, "module function" },
-        { NULL, NULL, 0, NULL }
-    };
-
-
-    static struct PyModuleDef ln_expmodule = {
-        PyModuleDef_HEAD_INIT,
-        "ln_exp",
-        NULL,
-        -1,
-        ln_expMethods
-    };
-
-
-
-   PyMODINIT_FUNC PyInit_ln_exp(void) {
-        import_array();
-        PyImport_ImportModule("numpy");
-        return PyModule_Create(&ln_expmodule);
-   }
-}
-#include <string>
-#include <sstream>
-#include <iostream>
-#include <cxxabi.h>
-#include <execinfo.h>
-void sighandler(int sig) {
-    std::ostringstream buffer;
-    buffer << "Abort by " << (sig == SIGSEGV ? "segfault" : "bus error") << std::endl;
-    void * stack[64];
-    std::size_t depth = backtrace(stack, 64);
-    if (!depth)
-        buffer << "  <empty stacktrace, possibly corrupt>" << std::endl;
-    else {
-        char ** symbols = backtrace_symbols(stack, depth);
-        for (std::size_t i = 1; i < depth; ++i) {
-            std::string symbol = symbols[i];
-                if (symbol.find_first_of(' ', 59) != std::string::npos) {
-                    std::string name = symbol.substr(59, symbol.find_first_of(' ', 59) - 59);
-                    int status;
-                    char * demangled = abi::__cxa_demangle(name.c_str(), NULL, NULL, &status);
-                    if (!status) {
-                        buffer << "    "
-                            << symbol.substr(0, 59)
-                            << demangled
-                            << symbol.substr(59 + name.size())
-                            << std::endl;
-                        free(demangled);
-                    } else
-                        buffer << "    " << symbol << std::endl;
-                } else
-                    buffer << "    " << symbol << std::endl;
-            }
-            free(symbols);
-        }
-        std::cerr << buffer.str();
-        std::exit(EXIT_FAILURE);
-    }
-
- -
-
-
- -
-
- - -
- -
- - -
-
Overwriting src/ln_exp.cpp
-
-
-
- -
-
- -
-
-
-
In [10]:
-
-
-
%%file src/ln_opt.cpp
-#define PY_ARRAY_UNIQUE_SYMBOL ln_hope_opt_ARRAY_API
-#define NPY_NO_DEPRECATED_API NPY_1_7_API_VERSION
-#include <Python.h>
-#include <numpy/arrayobject.h>
-#include <numpy/arrayscalars.h>
-#include <cmath>
-#include <tuple>
-#include <numeric>
-#include <cstdint>
-#include <cstdlib>
-#include <exception>
-#include <functional>
-#include <type_traits>
-struct PyObj {
-    typedef PyObject * ptr_t;
-    typedef PyArrayObject * arrptr_t;
-    PyObj(): dec(false), ptr(NULL) {}
-    PyObj(ptr_t p): dec(false), ptr(p) {}
-    ~PyObj() { if(dec) Py_DECREF(ptr); }
-    PyObj & operator=(ptr_t p) { if(dec) Py_DECREF(ptr); ptr = p; dec = false; return *this; }
-    PyObj & incref(ptr_t p) { if(dec) Py_DECREF(ptr); ptr = p; dec = (p != NULL); return *this; }
-    operator bool() const { return ptr; }
-    operator ptr_t() const { return ptr; }
-    operator arrptr_t() const { return (arrptr_t)ptr; }
-    bool dec;
-    ptr_t ptr;
-};
-
-inline void ln_hope_opt_d1d1(PyObject * pX, npy_intp const * __restrict__ sX, npy_double * __restrict__ cX,
-                                                    PyObject * pY, npy_intp const * __restrict__ sY, npy_double * __restrict__ cY);
-
-inline void ln_hope_opt_d1d1(PyObject * pX, npy_intp const * __restrict__ sX, npy_double * __restrict__ cX,
-                                                    PyObject * pY, npy_intp const * __restrict__ sY, npy_double * __restrict__ cY){
-
-    for (npy_intp i0 = 0; i0 < sY[0] - 0; ++i0) {
-        auto c__sp0 = (cX[i0] * cX[i0]);
-        auto c__sp1 = (c__sp0 * c__sp0);
-        auto c__sp2 = (c__sp1 * c__sp1);
-        auto c__sp3 = (c__sp2 * cX[i0]);
-        auto c__sp4 = (c__sp0 * cX[i0]);
-        auto c__sp5 = (c__sp4 * c__sp4);
-        auto c__sp6 = (c__sp5 * cX[i0]);
-        auto c__sp7 = (c__sp1 * cX[i0]);
-        cY[(int)(i0)] = (-7129.0 / 2520.0) + (28 * c__sp4) + (-(18 * c__sp0)) + (-(9 * c__sp2 / 8)) + (-(14 * c__sp5)) + (-(63 * c__sp1 / 2)) + (126 * c__sp7 / 5) + (9 * cX[i0]) + (c__sp3 / 9) + (36 * c__sp6 / 7);
-    }
-}
-
-void sighandler(int sig);
-#include <signal.h>
-extern "C" {
-    PyObject * create_signature;
-    struct sigaction slot;
-    PyObject * set_create_signature(PyObject * self, PyObject * args) {
-        if (!PyArg_ParseTuple(args, "O", &create_signature)) {
-            PyErr_SetString(PyExc_ValueError, "Invalid Argument to set_create_signature!");
-            return NULL;
-        }
-        Py_INCREF(create_signature);
-        memset(&slot, 0, sizeof(slot));
-        slot.sa_handler = &sighandler;
-        sigaction(SIGSEGV, &slot, NULL);
-        sigaction(SIGBUS, &slot, NULL);
-        Py_INCREF(Py_None);
-        return Py_None;
-    }
-    PyObject * run(PyObject * self, PyObject * args) {
-        {
-            PyObj pX;
-            PyObj pY;
-            if (
-                PyTuple_CheckExact(args) and PyTuple_GET_SIZE(args) == 2
-                and (pX = PyTuple_GET_ITEM(args, 0)) and PyArray_CheckExact(pX)
-                and PyArray_TYPE((PyArrayObject *)pX) == NPY_FLOAT64 and PyArray_NDIM((PyArrayObject *)pX) == 1
-                and (pY = PyTuple_GET_ITEM(args, 1)) and PyArray_CheckExact(pY)
-                and PyArray_TYPE((PyArrayObject *)pY) == NPY_FLOAT64 and PyArray_NDIM((PyArrayObject *)pY) == 1
-            ) {
-                if (!(pX.incref((PyObject *)PyArray_GETCONTIGUOUS((PyArrayObject *)pX)))) {
-                    PyErr_SetString(PyExc_ValueError, "Invalid Argument type on X!");
-                    return NULL;
-                }
-                if (!(pY.incref((PyObject *)PyArray_GETCONTIGUOUS((PyArrayObject *)pY)))) {
-                    PyErr_SetString(PyExc_ValueError, "Invalid Argument type on Y!");
-                    return NULL;
-                }
-                try {
-                    ln_hope_opt_d1d1(
-                          pX, PyArray_SHAPE((PyArrayObject *)pX), (npy_double *)PyArray_DATA((PyArrayObject *)pX)
-                        , pY, PyArray_SHAPE((PyArrayObject *)pY), (npy_double *)PyArray_DATA((PyArrayObject *)pY)
-                    );
-                    Py_INCREF(Py_None);
-                    return Py_None;
-                } catch (...) {
-                    return NULL;
-                }
-            } else
-                PyErr_Clear();
-        }
-        PyObject * signatures = Py_BuildValue("(sO)", "(lp0\n(lp1\nccopy_reg\n_reconstructor\np2\n(chope._ast\nVariable\np3\nc__builtin__\nobject\np4\nNtp5\nRp6\n(dp7\nS'name'\np8\nS'X'\np9\nsS'dtype'\np10\nS'float64'\np11\nsS'dims'\np12\nI1\nsbag2\n(g3\ng4\nNtp13\nRp14\n(dp15\ng8\nS'Y'\np16\nsg10\ng11\nsg12\nI1\nsbaa.", args);
-        if (!signatures) {
-            PyErr_SetString(PyExc_ValueError, "Error building signature string for ln_hope_opt");
-            return NULL;
-        }
-        return PyObject_Call(create_signature, signatures, NULL);
-    }
-
-    PyMethodDef ln_optMethods[] = {
-        { "set_create_signature", set_create_signature, METH_VARARGS, "signal handler" },
-        { "run", run, METH_VARARGS, "module function" },
-        { NULL, NULL, 0, NULL }
-    };
-
-
-    static struct PyModuleDef ln_optmodule = {
-        PyModuleDef_HEAD_INIT,
-        "ln",
-        NULL,
-        -1,
-        ln_optMethods
-    };
-
-
-
-   PyMODINIT_FUNC PyInit_ln_opt(void) {
-        import_array();
-        PyImport_ImportModule("numpy");
-        return PyModule_Create(&ln_optmodule);
-   }
-}
-#include <string>
-#include <sstream>
-#include <iostream>
-#include <cxxabi.h>
-#include <execinfo.h>
-void sighandler(int sig) {
-    std::ostringstream buffer;
-    buffer << "Abort by " << (sig == SIGSEGV ? "segfault" : "bus error") << std::endl;
-    void * stack[64];
-    std::size_t depth = backtrace(stack, 64);
-    if (!depth)
-        buffer << "  <empty stacktrace, possibly corrupt>" << std::endl;
-    else {
-        char ** symbols = backtrace_symbols(stack, depth);
-        for (std::size_t i = 1; i < depth; ++i) {
-            std::string symbol = symbols[i];
-                if (symbol.find_first_of(' ', 59) != std::string::npos) {
-                    std::string name = symbol.substr(59, symbol.find_first_of(' ', 59) - 59);
-                    int status;
-                    char * demangled = abi::__cxa_demangle(name.c_str(), NULL, NULL, &status);
-                    if (!status) {
-                        buffer << "    "
-                            << symbol.substr(0, 59)
-                            << demangled
-                            << symbol.substr(59 + name.size())
-                            << std::endl;
-                        free(demangled);
-                    } else
-                        buffer << "    " << symbol << std::endl;
-                } else
-                    buffer << "    " << symbol << std::endl;
-            }
-            free(symbols);
-        }
-        std::cerr << buffer.str();
-        std::exit(EXIT_FAILURE);
-    }
-
- -
-
-
- -
-
- - -
- -
- - -
-
Overwriting src/ln_opt.cpp
-
-
-
- -
-
- -
-
-
-
-
-

Simplify CPP

-
-
-
-
-
-
In [11]:
-
-
-
%%file src/poly.cpp
-#define PY_ARRAY_UNIQUE_SYMBOL poly_ARRAY_API
-#define NPY_NO_DEPRECATED_API NPY_1_7_API_VERSION
-#include <Python.h>
-#include <numpy/arrayobject.h>
-#include <numpy/arrayscalars.h>
-#include <cmath>
-#include <tuple>
-#include <numeric>
-#include <cstdint>
-#include <cstdlib>
-#include <exception>
-#include <functional>
-#include <type_traits>
-struct PyObj {
-    typedef PyObject * ptr_t;
-    typedef PyArrayObject * arrptr_t;
-    PyObj(): dec(false), ptr(NULL) {}
-    PyObj(ptr_t p): dec(false), ptr(p) {}
-    ~PyObj() { if(dec) Py_DECREF(ptr); }
-    PyObj & operator=(ptr_t p) { if(dec) Py_DECREF(ptr); ptr = p; dec = false; return *this; }
-    PyObj & incref(ptr_t p) { if(dec) Py_DECREF(ptr); ptr = p; dec = (p != NULL); return *this; }
-    operator bool() const { return ptr; }
-    operator ptr_t() const { return ptr; }
-    operator arrptr_t() const { return (arrptr_t)ptr; }
-    bool dec;
-    ptr_t ptr;
-};
-inline void poly_d1d1(PyObject * pres, npy_intp const * __restrict__ sres, npy_double * __restrict__ cres,
-                                            PyObject * parg, npy_intp const * __restrict__ sarg, npy_double * __restrict__ carg);
-
-inline void poly_d1d1(PyObject * pres, npy_intp const * __restrict__ sres, npy_double * __restrict__ cres,
-                                            PyObject * parg, npy_intp const * __restrict__ sarg, npy_double * __restrict__ carg){
-
-    npy_double arg_i;
-    double sin_arg_i;
-    for (npy_intp i0 = 0; i0 < sres[0] - 0; ++i0) {
-        arg_i = carg[i0];
-        cres[(int)(i0)] = std::pow(std::sin(arg_i), 2) + (std::pow(arg_i, 3) + std::pow(arg_i, 2) - arg_i - 1) / (std::pow(arg_i, 2) + 2 * arg_i + 1) + std::pow(std::cos(arg_i), 2);
-    }
-}
-void sighandler(int sig);
-#include <signal.h>
-extern "C" {
-    PyObject * create_signature;
-    struct sigaction slot;
-    PyObject * set_create_signature(PyObject * self, PyObject * args) {
-        if (!PyArg_ParseTuple(args, "O", &create_signature)) {
-            PyErr_SetString(PyExc_ValueError, "Invalid Argument to set_create_signature!");
-            return NULL;
-        }
-        Py_INCREF(create_signature);
-        memset(&slot, 0, sizeof(slot));
-        slot.sa_handler = &sighandler;
-        sigaction(SIGSEGV, &slot, NULL);
-        sigaction(SIGBUS, &slot, NULL);
-        Py_INCREF(Py_None);
-        return Py_None;
-    }
-    PyObject * run(PyObject * self, PyObject * args) {
-        {
-            PyObj pres;
-            PyObj parg;
-            if (
-                PyTuple_CheckExact(args) and PyTuple_GET_SIZE(args) == 2
-                and (pres = PyTuple_GET_ITEM(args, 0)) and PyArray_CheckExact(pres)
-                and PyArray_TYPE((PyArrayObject *)pres) == NPY_FLOAT64 and PyArray_NDIM((PyArrayObject *)pres) == 1
-                and (parg = PyTuple_GET_ITEM(args, 1)) and PyArray_CheckExact(parg)
-                and PyArray_TYPE((PyArrayObject *)parg) == NPY_FLOAT64 and PyArray_NDIM((PyArrayObject *)parg) == 1
-            ) {
-                if (!(pres.incref((PyObject *)PyArray_GETCONTIGUOUS((PyArrayObject *)pres)))) {
-                    PyErr_SetString(PyExc_ValueError, "Invalid Argument type on res!");
-                    return NULL;
-                }
-                if (!(parg.incref((PyObject *)PyArray_GETCONTIGUOUS((PyArrayObject *)parg)))) {
-                    PyErr_SetString(PyExc_ValueError, "Invalid Argument type on arg!");
-                    return NULL;
-                }
-                try {
-                    poly_d1d1(
-                          pres, PyArray_SHAPE((PyArrayObject *)pres), (npy_double *)PyArray_DATA((PyArrayObject *)pres)
-                        , parg, PyArray_SHAPE((PyArrayObject *)parg), (npy_double *)PyArray_DATA((PyArrayObject *)parg)
-                    );
-                    Py_INCREF(Py_None);
-                    return Py_None;
-                } catch (...) {
-                    return NULL;
-                }
-            } else
-                PyErr_Clear();
-        }
-        PyObject * signatures = Py_BuildValue("(sO)", "(lp0\n(lp1\nccopy_reg\n_reconstructor\np2\n(chope._ast\nVariable\np3\nc__builtin__\nobject\np4\nNtp5\nRp6\n(dp7\nS'name'\np8\nS'res'\np9\nsS'dtype'\np10\nS'float64'\np11\nsS'dims'\np12\nI1\nsbag2\n(g3\ng4\nNtp13\nRp14\n(dp15\ng8\nS'arg'\np16\nsg10\ng11\nsg12\nI1\nsbaa.", args);
-        if (!signatures) {
-            PyErr_SetString(PyExc_ValueError, "Error building signature string for poly");
-            return NULL;
-        }
-        return PyObject_Call(create_signature, signatures, NULL);
-    }
-
-    PyMethodDef polyMethods[] = {
-        { "set_create_signature", set_create_signature, METH_VARARGS, "signal handler" },
-        { "run", run, METH_VARARGS, "module function" },
-        { NULL, NULL, 0, NULL }
-    };
-
-    static struct PyModuleDef polymodule = {
-        PyModuleDef_HEAD_INIT,
-        "poly",
-        NULL,
-        -1,
-        polyMethods
-    };
-
-   PyMODINIT_FUNC PyInit_poly(void) {
-        import_array();
-        PyImport_ImportModule("numpy");
-        return PyModule_Create(&polymodule);
-   }
-}
-#include <string>
-#include <sstream>
-#include <iostream>
-#include <cxxabi.h>
-#include <execinfo.h>
-void sighandler(int sig) {
-    std::ostringstream buffer;
-    buffer << "Abort by " << (sig == SIGSEGV ? "segfault" : "bus error") << std::endl;
-    void * stack[64];
-    std::size_t depth = backtrace(stack, 64);
-    if (!depth)
-        buffer << "  <empty stacktrace, possibly corrupt>" << std::endl;
-    else {
-        char ** symbols = backtrace_symbols(stack, depth);
-        for (std::size_t i = 1; i < depth; ++i) {
-            std::string symbol = symbols[i];
-                if (symbol.find_first_of(' ', 59) != std::string::npos) {
-                    std::string name = symbol.substr(59, symbol.find_first_of(' ', 59) - 59);
-                    int status;
-                    char * demangled = abi::__cxa_demangle(name.c_str(), NULL, NULL, &status);
-                    if (!status) {
-                        buffer << "    "
-                            << symbol.substr(0, 59)
-                            << demangled
-                            << symbol.substr(59 + name.size())
-                            << std::endl;
-                        free(demangled);
-                    } else
-                        buffer << "    " << symbol << std::endl;
-                } else
-                    buffer << "    " << symbol << std::endl;
-            }
-            free(symbols);
-        }
-        std::cerr << buffer.str();
-        std::exit(EXIT_FAILURE);
-    }
-
- -
-
-
- -
-
- - -
- -
- - -
-
Overwriting src/poly.cpp
-
-
-
- -
-
- -
-
-
-
In [12]:
-
-
-
%%file src/poly_opt.cpp
-#define PY_ARRAY_UNIQUE_SYMBOL poly_ARRAY_API
-#define NPY_NO_DEPRECATED_API NPY_1_7_API_VERSION
-#include <Python.h>
-#include <numpy/arrayobject.h>
-#include <numpy/arrayscalars.h>
-#include <cmath>
-#include <tuple>
-#include <numeric>
-#include <cstdint>
-#include <cstdlib>
-#include <exception>
-#include <functional>
-#include <type_traits>
-struct PyObj {
-	typedef PyObject * ptr_t;
-	typedef PyArrayObject * arrptr_t;
-	PyObj(): dec(false), ptr(NULL) {}
-	PyObj(ptr_t p): dec(false), ptr(p) {}
-	~PyObj() { if(dec) Py_DECREF(ptr); }
-	PyObj & operator=(ptr_t p) { if(dec) Py_DECREF(ptr); ptr = p; dec = false; return *this; }
-	PyObj & incref(ptr_t p) { if(dec) Py_DECREF(ptr); ptr = p; dec = (p != NULL); return *this; }
-	operator bool() const { return ptr; }
-	operator ptr_t() const { return ptr; }
-	operator arrptr_t() const { return (arrptr_t)ptr; }
-	bool dec;
-	ptr_t ptr;
-};
-
-inline void poly_d1d1(PyObject * pres, npy_intp const * __restrict__ sres, npy_double * __restrict__ cres, 
-											PyObject * parg, npy_intp const * __restrict__ sarg, npy_double * __restrict__ carg);
-
-inline void poly_d1d1(PyObject * pres, npy_intp const * __restrict__ sres, npy_double * __restrict__ cres, 
-											PyObject * parg, npy_intp const * __restrict__ sarg, npy_double * __restrict__ carg){
-	for (npy_intp i0 = 0; i0 < sres[0] - 0; ++i0) {
-		cres[(int)(i0)] = carg[i0];
-	}
-}
-
-void sighandler(int sig);
-#include <signal.h>
-extern "C" {
-	PyObject * create_signature;
-	struct sigaction slot;
-	PyObject * set_create_signature(PyObject * self, PyObject * args) {
-		if (!PyArg_ParseTuple(args, "O", &create_signature)) {
-			PyErr_SetString(PyExc_ValueError, "Invalid Argument to set_create_signature!");
-			return NULL;
-		}
-		Py_INCREF(create_signature);
-		memset(&slot, 0, sizeof(slot));
-		slot.sa_handler = &sighandler;
-		sigaction(SIGSEGV, &slot, NULL);
-		sigaction(SIGBUS, &slot, NULL);
-		Py_INCREF(Py_None);
-		return Py_None;
-	}
-	PyObject * run(PyObject * self, PyObject * args) {
-		{
-			PyObj pres;
-			PyObj parg;
-			if (
-				PyTuple_CheckExact(args) and PyTuple_GET_SIZE(args) == 2
-				and (pres = PyTuple_GET_ITEM(args, 0)) and PyArray_CheckExact(pres)
-				and PyArray_TYPE((PyArrayObject *)pres) == NPY_FLOAT64 and PyArray_NDIM((PyArrayObject *)pres) == 1
-				and (parg = PyTuple_GET_ITEM(args, 1)) and PyArray_CheckExact(parg)
-				and PyArray_TYPE((PyArrayObject *)parg) == NPY_FLOAT64 and PyArray_NDIM((PyArrayObject *)parg) == 1
-			) {
-				if (!(pres.incref((PyObject *)PyArray_GETCONTIGUOUS((PyArrayObject *)pres)))) {
-					PyErr_SetString(PyExc_ValueError, "Invalid Argument type on res!");
-					return NULL;
-				}
-				if (!(parg.incref((PyObject *)PyArray_GETCONTIGUOUS((PyArrayObject *)parg)))) {
-					PyErr_SetString(PyExc_ValueError, "Invalid Argument type on arg!");
-					return NULL;
-				}
-				try {
-					poly_d1d1(
-						  pres, PyArray_SHAPE((PyArrayObject *)pres), (npy_double *)PyArray_DATA((PyArrayObject *)pres)
-						, parg, PyArray_SHAPE((PyArrayObject *)parg), (npy_double *)PyArray_DATA((PyArrayObject *)parg)
-					);
-					Py_INCREF(Py_None);
-					return Py_None;
-				} catch (...) {
-					return NULL;
-				}
-			} else
-				PyErr_Clear();
-		}
-		PyObject * signatures = Py_BuildValue("(sO)", "(lp0\n(lp1\nccopy_reg\n_reconstructor\np2\n(chope._ast\nVariable\np3\nc__builtin__\nobject\np4\nNtp5\nRp6\n(dp7\nS'name'\np8\nS'res'\np9\nsS'dtype'\np10\nS'float64'\np11\nsS'dims'\np12\nI1\nsbag2\n(g3\ng4\nNtp13\nRp14\n(dp15\ng8\nS'arg'\np16\nsg10\ng11\nsg12\nI1\nsbaa.", args);
-		if (!signatures) {
-			PyErr_SetString(PyExc_ValueError, "Error building signature string for poly");
-			return NULL;
-		}
-		return PyObject_Call(create_signature, signatures, NULL);
-	}
-	PyMethodDef polyMethods[] = {
-		{ "set_create_signature", (PyCFunction)set_create_signature, METH_VARARGS },
-		{ "run", (PyCFunction)run, METH_VARARGS },
-		{ NULL, NULL }
-	};
-	PyMODINIT_FUNC initpoly(void) {
-		import_array();
-		PyImport_ImportModule("numpy");
-		(void)Py_InitModule("poly", polyMethods);
-	}
-}
-#include <string>
-#include <sstream>
-#include <iostream>
-#include <cxxabi.h>
-#include <execinfo.h>
-void sighandler(int sig) {
-	std::ostringstream buffer;
-	buffer << "Abort by " << (sig == SIGSEGV ? "segfault" : "bus error") << std::endl;
-	void * stack[64];
-	std::size_t depth = backtrace(stack, 64);
-	if (!depth)
-		buffer << "  <empty stacktrace, possibly corrupt>" << std::endl;
-	else {
-		char ** symbols = backtrace_symbols(stack, depth);
-		for (std::size_t i = 1; i < depth; ++i) {
-			std::string symbol = symbols[i];
-				if (symbol.find_first_of(' ', 59) != std::string::npos) {
-					std::string name = symbol.substr(59, symbol.find_first_of(' ', 59) - 59);
-					int status;
-					char * demangled = abi::__cxa_demangle(name.c_str(), NULL, NULL, &status);
-					if (!status) {
-						buffer << "    " 
-							<< symbol.substr(0, 59) 
-							<< demangled
-							<< symbol.substr(59 + name.size())
-							<< std::endl;
-						free(demangled);
-					} else
-						buffer << "    " << symbol << std::endl;
-				} else
-					buffer << "    " << symbol << std::endl;
-			}
-			free(symbols);
-		}
-		std::cerr << buffer.str();
-		std::exit(EXIT_FAILURE);
-	}
-
- -
-
-
- -
-
- - -
- -
- - -
-
Overwriting src/poly_opt.cpp
-
-
-
- -
-
- -
-
-
-
-
-

Pairwise distance

-
-
-
-
-
-
In [13]:
-
-
-
%%file src/pairwise.cpp
-#define PY_ARRAY_UNIQUE_SYMBOL pairwise_ARRAY_API
-#define NPY_NO_DEPRECATED_API NPY_1_7_API_VERSION
-#include <Python.h>
-#include <numpy/arrayobject.h>
-#include <numpy/arrayscalars.h>
-#include <cmath>
-#include <tuple>
-#include <numeric>
-#include <cstdint>
-#include <cstdlib>
-#include <exception>
-#include <functional>
-#include <type_traits>
-struct PyObj {
-    typedef PyObject * ptr_t;
-    typedef PyArrayObject * arrptr_t;
-    PyObj(): dec(false), ptr(NULL) {}
-    PyObj(ptr_t p): dec(false), ptr(p) {}
-    ~PyObj() { if(dec) Py_DECREF(ptr); }
-    PyObj & operator=(ptr_t p) { if(dec) Py_DECREF(ptr); ptr = p; dec = false; return *this; }
-    PyObj & incref(ptr_t p) { if(dec) Py_DECREF(ptr); ptr = p; dec = (p != NULL); return *this; }
-    operator bool() const { return ptr; }
-    operator ptr_t() const { return ptr; }
-    operator arrptr_t() const { return (arrptr_t)ptr; }
-    bool dec;
-    ptr_t ptr;
-};
-
-inline void pairwise_d2d2JJ(PyObject * pX,
-                                                        npy_intp const * __restrict__ sX,
-                                                        npy_double * __restrict__ cX,
-                                                        PyObject * pD,
-                                                        npy_intp const * __restrict__ sD,
-                                                        npy_double * __restrict__ cD,
-                                                        npy_int64 const cM,
-                                                        npy_int64 const cN);
-
-inline void pairwise_d2d2JJ(PyObject * pX,
-                                                        npy_intp const * __restrict__ sX,
-                                                        npy_double * __restrict__ cX,
-                                                        PyObject * pD,
-                                                        npy_intp const * __restrict__ sD,
-                                                        npy_double * __restrict__ cD,
-                                                        npy_int64 const cM,
-                                                        npy_int64 const cN){
-
-    npy_double cd = 0.0;
-    npy_double ctmp = 0.0;
-
-    npy_intp cj;
-    npy_intp ck;
-    npy_intp x_i_idx;
-    npy_intp x_j_idx;
-    npy_intp d_i_idx;
-    npy_intp const xp = sX[1];
-    npy_intp const dp = sD[1];
-
-    for (npy_intp ci = 0; ci < cM; ++ci) {
-        x_i_idx = ci*xp;
-        d_i_idx = ci*dp;
-        for (cj = 0; cj < cM; ++cj) {
-            cd = 0.0;
-            x_j_idx = cj*xp;
-            for (ck = 0; ck < cN; ++ck) {
-                ctmp = (cX[(x_i_idx + ck)] - cX[(x_j_idx + ck)]);
-                cd += (ctmp * ctmp);
-            }
-            cD[(d_i_idx + cj)] = std::sqrt(cd);
-        }
-    }
-}
-
-void sighandler(int sig);
-#include <signal.h>
-extern "C" {
-    PyObject * create_signature;
-    struct sigaction slot;
-    PyObject * set_create_signature(PyObject * self, PyObject * args) {
-        if (!PyArg_ParseTuple(args, "O", &create_signature)) {
-            PyErr_SetString(PyExc_ValueError, "Invalid Argument to set_create_signature!");
-            return NULL;
-        }
-        Py_INCREF(create_signature);
-        memset(&slot, 0, sizeof(slot));
-        slot.sa_handler = &sighandler;
-        sigaction(SIGSEGV, &slot, NULL);
-        sigaction(SIGBUS, &slot, NULL);
-        Py_INCREF(Py_None);
-        return Py_None;
-    }
-    PyObject * run(PyObject * self, PyObject * args) {
-        {
-            PyObj pX;
-            PyObj pD;
-            PyObject * pM; npy_int64 cM;
-            PyObject * pN; npy_int64 cN;
-            if (
-                PyTuple_CheckExact(args) and PyTuple_GET_SIZE(args) == 4
-                and (pX = PyTuple_GET_ITEM(args, 0)) and PyArray_CheckExact(pX)
-                and PyArray_TYPE((PyArrayObject *)pX) == NPY_FLOAT64 and PyArray_NDIM((PyArrayObject *)pX) == 2
-                and (pD = PyTuple_GET_ITEM(args, 1)) and PyArray_CheckExact(pD)
-                and PyArray_TYPE((PyArrayObject *)pD) == NPY_FLOAT64 and PyArray_NDIM((PyArrayObject *)pD) == 2
-                and (pM = PyTuple_GET_ITEM(args, 2)) and PyLong_CheckExact(pM)
-                and (pN = PyTuple_GET_ITEM(args, 3)) and PyLong_CheckExact(pN)
-            ) {
-                if (!(pX.incref((PyObject *)PyArray_GETCONTIGUOUS((PyArrayObject *)pX)))) {
-                    PyErr_SetString(PyExc_ValueError, "Invalid Argument type on X!");
-                    return NULL;
-                }
-                if (!(pD.incref((PyObject *)PyArray_GETCONTIGUOUS((PyArrayObject *)pD)))) {
-                    PyErr_SetString(PyExc_ValueError, "Invalid Argument type on D!");
-                    return NULL;
-                }
-                cM = PyLong_AS_LONG(pM);
-                cN = PyLong_AS_LONG(pN);
-                try {
-                    pairwise_d2d2JJ(
-                          pX, PyArray_SHAPE((PyArrayObject *)pX), (npy_double *)PyArray_DATA((PyArrayObject *)pX)
-                        , pD, PyArray_SHAPE((PyArrayObject *)pD), (npy_double *)PyArray_DATA((PyArrayObject *)pD)
-                        , cM
-                        , cN
-                    );
-                    Py_INCREF(Py_None);
-                    return Py_None;
-                } catch (...) {
-                    return NULL;
-                }
-            } else
-                PyErr_Clear();
-        }
-        PyObject * signatures = Py_BuildValue("(sO)", "(lp0\n(lp1\nccopy_reg\n_reconstructor\np2\n(chope._ast\nVariable\np3\nc__builtin__\nobject\np4\nNtp5\nRp6\n(dp7\nS'name'\np8\nS'X'\np9\nsS'dtype'\np10\nS'float64'\np11\nsS'dims'\np12\nI2\nsbag2\n(g3\ng4\nNtp13\nRp14\n(dp15\ng8\nS'D'\np16\nsg10\ng11\nsg12\nI2\nsbag2\n(g3\ng4\nNtp17\nRp18\n(dp19\ng8\nS'M'\np20\nsg10\nc__builtin__\nint\np21\nsg12\nI0\nsbag2\n(g3\ng4\nNtp22\nRp23\n(dp24\ng8\nS'N'\np25\nsg10\ng21\nsg12\nI0\nsbaa.", args);
-        if (!signatures) {
-            PyErr_SetString(PyExc_ValueError, "Error building signature string for pairwise");
-            return NULL;
-        }
-        return PyObject_Call(create_signature, signatures, NULL);
-    }
-
-    PyMethodDef pairwiseMethods[] = {
-        { "set_create_signature", set_create_signature, METH_VARARGS, "signal handler" },
-        { "run", run, METH_VARARGS, "module function" },
-        { NULL, NULL, 0, NULL }
-    };
-
-
-    static struct PyModuleDef pairwisemodule = {
-        PyModuleDef_HEAD_INIT,
-        "pairwise",
-        NULL,
-        -1,
-        pairwiseMethods
-    };
-
-
-   PyMODINIT_FUNC PyInit_pairwise(void) {
-        import_array();
-        PyImport_ImportModule("numpy");
-        return PyModule_Create(&pairwisemodule);
-   }
-}
-#include <string>
-#include <sstream>
-#include <iostream>
-#include <cxxabi.h>
-#include <execinfo.h>
-void sighandler(int sig) {
-    std::ostringstream buffer;
-    buffer << "Abort by " << (sig == SIGSEGV ? "segfault" : "bus error") << std::endl;
-    void * stack[64];
-    std::size_t depth = backtrace(stack, 64);
-    if (!depth)
-        buffer << "  <empty stacktrace, possibly corrupt>" << std::endl;
-    else {
-        char ** symbols = backtrace_symbols(stack, depth);
-        for (std::size_t i = 1; i < depth; ++i) {
-            std::string symbol = symbols[i];
-                if (symbol.find_first_of(' ', 59) != std::string::npos) {
-                    std::string name = symbol.substr(59, symbol.find_first_of(' ', 59) - 59);
-                    int status;
-                    char * demangled = abi::__cxa_demangle(name.c_str(), NULL, NULL, &status);
-                    if (!status) {
-                        buffer << "    "
-                            << symbol.substr(0, 59)
-                            << demangled
-                            << symbol.substr(59 + name.size())
-                            << std::endl;
-                        free(demangled);
-                    } else
-                        buffer << "    " << symbol << std::endl;
-                } else
-                    buffer << "    " << symbol << std::endl;
-            }
-            free(symbols);
-        }
-        std::cerr << buffer.str();
-        std::exit(EXIT_FAILURE);
-    }
-
- -
-
-
- -
-
- - -
- -
- - -
-
Overwriting src/pairwise.cpp
-
-
-
- -
-
- -
-
-
-
-
-

Star point spread function CPP

- -
-
-
-
-
-
In [14]:
-
-
-
%%file src/pdf.cpp
-#define PY_ARRAY_UNIQUE_SYMBOL pdf_ARRAY_API
-#define NPY_NO_DEPRECATED_API NPY_1_7_API_VERSION
-#include <Python.h>
-#include <numpy/arrayobject.h>
-#include <numpy/arrayscalars.h>
-#include <cmath>
-#include <tuple>
-#include <numeric>
-#include <cstdint>
-#include <cstdlib>
-#include <exception>
-#include <functional>
-#include <type_traits>
-struct PyObj {
-    typedef PyObject * ptr_t;
-    typedef PyArrayObject * arrptr_t;
-    PyObj(): dec(false), ptr(NULL) {}
-    PyObj(ptr_t p): dec(false), ptr(p) {}
-    ~PyObj() { if(dec) Py_DECREF(ptr); }
-    PyObj & operator=(ptr_t p) { if(dec) Py_DECREF(ptr); ptr = p; dec = false; return *this; }
-    PyObj & incref(ptr_t p) { if(dec) Py_DECREF(ptr); ptr = p; dec = (p != NULL); return *this; }
-    operator bool() const { return ptr; }
-    operator ptr_t() const { return ptr; }
-    operator arrptr_t() const { return (arrptr_t)ptr; }
-    bool dec;
-    ptr_t ptr;
-};
-
-inline std::tuple<PyObject *, npy_intp const *, npy_float *> pdf_f2l1d1f2JDd(
-      PyObject * pdensity
-  , npy_intp const * __restrict__ sdensity
-  , npy_float * __restrict__ cdensity
-    , PyObject * pdims
-    , npy_intp const * __restrict__ sdims
-    , npy_int64 * __restrict__ cdims
-    , PyObject * pcenter
-    , npy_intp const * __restrict__ scenter
-    , npy_double * __restrict__ ccenter
-    , PyObject * pw2D
-    , npy_intp const * __restrict__ sw2D
-    , npy_float * __restrict__ cw2D
-    , npy_int64 cr50
-    , npy_double cb
-    , npy_double ca);
-
-inline std::tuple<PyObject *, npy_intp const *, npy_float *> pdf_f2l1d1f2JDd(
-      PyObject * pdensity
-  , npy_intp const * __restrict__ sdensity
-  , npy_float * __restrict__ cdensity
-    , PyObject * pdims
-    , npy_intp const * __restrict__ sdims
-    , npy_int64 * __restrict__ cdims
-    , PyObject * pcenter
-    , npy_intp const * __restrict__ scenter
-    , npy_double * __restrict__ ccenter
-    , PyObject * pw2D
-    , npy_intp const * __restrict__ sw2D
-    , npy_float * __restrict__ cw2D
-    , npy_int64 const cr50
-    , npy_double const cb
-    , npy_double const ca) {
-
-    npy_double cdr;
-    npy_double c__sum0;
-    const npy_double x_center = ccenter[0];
-    const npy_double y_center = ccenter[1];
-    const npy_intp len_0_sw2D = sw2D[0];
-    const npy_intp len_1_sw2D = sw2D[1];
-    npy_intp dc[] = {len_0_sw2D, len_1_sw2D};
-    PyObject * pc = PyArray_EMPTY(2, dc, NPY_FLOAT64, 0);
-    npy_intp * sc = PyArray_SHAPE((PyArrayObject *)pc);
-    npy_double * cc = (npy_double *)PyArray_DATA((PyArrayObject *)pc);
-
-    npy_intp cy = 0;
-    npy_intp i0 = 0;
-    npy_intp i1 = 0;
-    npy_intp i2 = 0;
-    npy_intp i3 = 0;
-
-    npy_intp sw2D_i0_idx;
-    npy_intp sw2D_i2_idx;
-    npy_intp density_x_idx;
-
-    auto c__sp0 = ca * ca;
-    auto c__sp1 = cr50 * cr50;
-    auto c__sp2 = 1.0 / (c__sp0 * c__sp1);
-    auto c__sp4 = 0.3183098861846737 * c__sp2 * (-1 + cb);
-
-    for (npy_intp cx = 0; cx < cdims[(int)(0)]; ++cx) {
-
-        density_x_idx = cx*sdensity[1];
-        for (cy = 0; cy < cdims[(int)(1)]; ++cy) {
-
-            cdr = std::sqrt(std::pow(cx - x_center, 2) + std::pow(cy - y_center, 2));
-            auto c__sp3 = cdr * cdr;
-            auto c__sp5 = c__sp4 * std::pow((1 + c__sp2 * c__sp3), -cb);
-
-            for (i0 = 0; i0 < len_0_sw2D - 0; ++i0) {
-
-                sw2D_i0_idx = (i0)*len_1_sw2D;
-                for (i1 = 0; i1 < len_1_sw2D - 0; ++i1) {
-                    cc[sw2D_i0_idx + i1] = c__sp5 * cw2D[sw2D_i0_idx + i1];
-                }
-            }
-
-            c__sum0 = 0;
-            for (i2 = 0; i2 < len_0_sw2D - 0; ++i2) {
-
-                sw2D_i2_idx = (i2)*len_1_sw2D;
-                for (i3 = 0; i3 < len_1_sw2D - 0; ++i3) {
-                    c__sum0 += cc[sw2D_i2_idx + i3];
-                }
-            }
-
-            cdensity[(density_x_idx + cy)] = c__sum0;
-        }
-    }
-    return std::make_tuple((PyObject *)pdensity, sdensity, cdensity);
-}
-
-void sighandler(int sig);
-#include <signal.h>
-extern "C" {
-    PyObject * create_signature;
-    struct sigaction slot;
-    PyObject * set_create_signature(PyObject * self, PyObject * args) {
-        if (!PyArg_ParseTuple(args, "O", &create_signature)) {
-            PyErr_SetString(PyExc_ValueError, "Invalid Argument to set_create_signature!");
-            return NULL;
-        }
-        Py_INCREF(create_signature);
-        memset(&slot, 0, sizeof(slot));
-        slot.sa_handler = &sighandler;
-        sigaction(SIGSEGV, &slot, NULL);
-        sigaction(SIGBUS, &slot, NULL);
-        Py_INCREF(Py_None);
-        return Py_None;
-    }
-    PyObject * run(PyObject * self, PyObject * args) {
-        {
-            PyObj pdensity;
-            PyObj pdims;
-            PyObj pcenter;
-            PyObj pw2D;
-            PyObject * pr50; npy_int64 cr50;
-            PyObject * pb; npy_double cb;
-            PyObject * pa; npy_double ca;
-            if (
-                PyTuple_CheckExact(args) and PyTuple_GET_SIZE(args) == 7
-                and (pdensity = PyTuple_GET_ITEM(args, 0)) and PyArray_CheckExact(pdensity)
-                and PyArray_TYPE((PyArrayObject *)pdensity) == NPY_FLOAT32 and PyArray_NDIM((PyArrayObject *)pdensity) == 2
-                and (pdims = PyTuple_GET_ITEM(args, 1)) and PyArray_CheckExact(pdims)
-                and PyArray_TYPE((PyArrayObject *)pdims) == NPY_INT64 and PyArray_NDIM((PyArrayObject *)pdims) == 1
-                and (pcenter = PyTuple_GET_ITEM(args, 2)) and PyArray_CheckExact(pcenter)
-                and PyArray_TYPE((PyArrayObject *)pcenter) == NPY_FLOAT64 and PyArray_NDIM((PyArrayObject *)pcenter) == 1
-                and (pw2D = PyTuple_GET_ITEM(args, 3)) and PyArray_CheckExact(pw2D)
-                and PyArray_TYPE((PyArrayObject *)pw2D) == NPY_FLOAT32 and PyArray_NDIM((PyArrayObject *)pw2D) == 2
-                and (pr50 = PyTuple_GET_ITEM(args, 4)) and PyLong_CheckExact(pr50)
-                and (pb = PyTuple_GET_ITEM(args, 5)) and PyFloat_CheckExact(pb)
-                and (pa = PyTuple_GET_ITEM(args, 6)) and PyArray_IsScalar(pa, Double)
-            ) {
-                if (!(pdensity.incref((PyObject *)PyArray_GETCONTIGUOUS((PyArrayObject *)pdensity)))) {
-                    PyErr_SetString(PyExc_ValueError, "Invalid Argument type on density!");
-                    return NULL;
-                }
-                if (!(pdims.incref((PyObject *)PyArray_GETCONTIGUOUS((PyArrayObject *)pdims)))) {
-                    PyErr_SetString(PyExc_ValueError, "Invalid Argument type on dims!");
-                    return NULL;
-                }
-                if (!(pcenter.incref((PyObject *)PyArray_GETCONTIGUOUS((PyArrayObject *)pcenter)))) {
-                    PyErr_SetString(PyExc_ValueError, "Invalid Argument type on center!");
-                    return NULL;
-                }
-                if (!(pw2D.incref((PyObject *)PyArray_GETCONTIGUOUS((PyArrayObject *)pw2D)))) {
-                    PyErr_SetString(PyExc_ValueError, "Invalid Argument type on w2D!");
-                    return NULL;
-                }
-                cr50 = PyLong_AS_LONG(pr50);
-                cb = PyFloat_AS_DOUBLE(pb);
-                ca = PyArrayScalar_VAL(pa, Double);
-                try {
-                    PyObject * res = std::get<0>(pdf_f2l1d1f2JDd(
-                          pdensity, PyArray_SHAPE((PyArrayObject *)pdensity), (npy_float *)PyArray_DATA((PyArrayObject *)pdensity)
-                        , pdims, PyArray_SHAPE((PyArrayObject *)pdims), (npy_int64 *)PyArray_DATA((PyArrayObject *)pdims)
-                        , pcenter, PyArray_SHAPE((PyArrayObject *)pcenter), (npy_double *)PyArray_DATA((PyArrayObject *)pcenter)
-                        , pw2D, PyArray_SHAPE((PyArrayObject *)pw2D), (npy_float *)PyArray_DATA((PyArrayObject *)pw2D)
-                        , cr50
-                        , cb
-                        , ca
-                    ));
-
-                    Py_INCREF(res);
-                    return res;
-                } catch (...) {
-                    return NULL;
-                }
-            } else
-                PyErr_Clear();
-        }
-        PyObject * signatures = Py_BuildValue("(sO)", "(lp0\n(lp1\nccopy_reg\n_reconstructor\np2\n(chope._ast\nVariable\np3\nc__builtin__\nobject\np4\nNtp5\nRp6\n(dp7\nS'name'\np8\nS'density'\np9\nsS'dtype'\np10\nS'float32'\np11\nsS'dims'\np12\nI2\nsbag2\n(g3\ng4\nNtp13\nRp14\n(dp15\ng8\ng12\nsg10\nS'int64'\np16\nsg12\nI1\nsbag2\n(g3\ng4\nNtp17\nRp18\n(dp19\ng8\nS'center'\np20\nsg10\nS'float64'\np21\nsg12\nI1\nsbag2\n(g3\ng4\nNtp22\nRp23\n(dp24\ng8\nS'w2D'\np25\nsg10\ng11\nsg12\nI2\nsbag2\n(g3\ng4\nNtp26\nRp27\n(dp28\ng8\nS'r50'\np29\nsg10\nc__builtin__\nint\np30\nsg12\nI0\nsbag2\n(g3\ng4\nNtp31\nRp32\n(dp33\ng8\nS'b'\np34\nsg10\nc__builtin__\nfloat\np35\nsg12\nI0\nsbag2\n(g3\ng4\nNtp36\nRp37\n(dp38\ng8\nS'a'\np39\nsg10\ng21\nsg12\nI0\nsbaa.", args);
-        if (!signatures) {
-            PyErr_SetString(PyExc_ValueError, "Error building signature string for pdf");
-            return NULL;
-        }
-        return PyObject_Call(create_signature, signatures, NULL);
-    }
-
-    PyMethodDef pdfMethods[] = {
-        { "set_create_signature", set_create_signature, METH_VARARGS, "signal handler" },
-        { "run", run, METH_VARARGS, "module function" },
-        { NULL, NULL, 0, NULL }
-    };
-
-    static struct PyModuleDef pdfmodule = {
-        PyModuleDef_HEAD_INIT,
-        "pdf",
-        NULL,
-        -1,
-        pdfMethods
-    };
-
-   PyMODINIT_FUNC PyInit_pdf(void) {
-        import_array();
-        PyImport_ImportModule("numpy");
-        return PyModule_Create(&pdfmodule);
-   }
-}
-#include <string>
-#include <sstream>
-#include <iostream>
-#include <cxxabi.h>
-#include <execinfo.h>
-void sighandler(int sig) {
-    std::ostringstream buffer;
-    buffer << "Abort by " << (sig == SIGSEGV ? "segfault" : "bus error") << std::endl;
-    void * stack[64];
-    std::size_t depth = backtrace(stack, 64);
-    if (!depth)
-        buffer << "  <empty stacktrace, possibly corrupt>" << std::endl;
-    else {
-        char ** symbols = backtrace_symbols(stack, depth);
-        for (std::size_t i = 1; i < depth; ++i) {
-            std::string symbol = symbols[i];
-                if (symbol.find_first_of(' ', 59) != std::string::npos) {
-                    std::string name = symbol.substr(59, symbol.find_first_of(' ', 59) - 59);
-                    int status;
-                    char * demangled = abi::__cxa_demangle(name.c_str(), NULL, NULL, &status);
-                    if (!status) {
-                        buffer << "    "
-                            << symbol.substr(0, 59)
-                            << demangled
-                            << symbol.substr(59 + name.size())
-                            << std::endl;
-                        free(demangled);
-                    } else
-                        buffer << "    " << symbol << std::endl;
-                } else
-                    buffer << "    " << symbol << std::endl;
-            }
-            free(symbols);
-        }
-        std::cerr << buffer.str();
-        std::exit(EXIT_FAILURE);
-    }
-
- -
-
-
- -
-
- - -
- -
- - -
-
Overwriting src/pdf.cpp
-
-
-
- -
-
- -
-
-
-
-
-

Python helper module

-
-
-
-
-
-
In [15]:
-
-
-
%%file native_util.py
-import importlib
-import os
-import glob
-import sys
-
-from numpy.distutils.misc_util import get_numpy_include_dirs
-import setuptools
-from os import listdir
-# import tempfile
-from hope import config
-
-try:
-    # python 3
-    import io
-    file = io.IOBase
-except ImportError:
-    pass
-
-
-def load(name):
-    compile(name, "./src")
-    module = importlib.import_module(name)
-    # module = __import__(name, globals(), locals(), [], -1)
-    return module
-
-
-
-def compile(name, src_folder, target_folder = "./"):
-    localfilename =os.path.join(src_folder, name)
-    
-    outfile, stdout, stderr, argv = None, None, None, sys.argv
-    try:
-        sys.stdout.flush(), sys.stderr.flush()
-        outpath = os.path.join(target_folder, "{0}.out".format(localfilename))
-        if os.path.exists(outpath):
-            os.remove(outpath)
-            
-        so_path = os.path.join(target_folder, "{0}.so".format(name))
-        if os.path.exists(so_path):
-            os.remove(so_path)
-            
-        outfile = open(outpath, 'w')
-        
-        try:
-            sys.stdout.fileno()
-            sys.stderr.fileno()
-            have_fileno = True
-        except OSError:
-            have_fileno = False
-        
-        if have_fileno and isinstance(sys.stdout, file) and  isinstance(sys.stdout, file):
-            stdout, stderr = os.dup(sys.stdout.fileno()), os.dup(sys.stderr.fileno())
-            os.dup2(outfile.fileno(), sys.stdout.fileno())
-            os.dup2(outfile.fileno(), sys.stderr.fileno())
-        else:
-            stdout, stderr = sys.stdout, sys.stderr
-            sys.stdout, sys.stderr = outfile, outfile
-        try:
-            sources = "./{0}.cpp".format(localfilename)
-            sys.argv = ["", "build_ext",
-                        "-b", target_folder,  #--build-lib (-b)     directory for compiled extension modules
-                        "-t", "." #--build-temp - a rel path will result in a dir structure of -b at the cur position 
-                        ]
-    
-            localfilename = str(localfilename)
-            sources = str(sources)
-    
-            setuptools.setup( \
-                  name = name\
-                , ext_modules = [setuptools.Extension( \
-                      name \
-                    , sources = [sources] \
-                    , extra_compile_args = config.cxxflags \
-                  )] \
-                , include_dirs = get_numpy_include_dirs() \
-            )
-        except SystemExit as e:
-            print(sys.stderr.write(str(e)))
-        sys.stdout.flush(), sys.stderr.flush()
-    finally:
-        if isinstance(stdout, int):
-            os.dup2(stdout, sys.stdout.fileno()), os.close(stdout)
-        elif not stdout is None:
-            sys.stdout = stdout
-        if isinstance(stderr, int):
-            os.dup2(stderr, sys.stderr.fileno()), os.close(stderr)
-        elif not stderr is None:
-            sys.stderr = stderr
-        if isinstance(outfile, file):
-            outfile.close()
-        sys.argv = argv
-    
-    with open(outpath) as outfile:
-        out = outfile.read()
-    
-    modules = glob.glob(os.path.join(target_folder, "{}*.so".format(name)))
-        
-    if not modules or out.find("error:") > -1:
-        print(out)
-        raise Exception("Error compiling function {0} (compiled to {1})".format(localfilename, target_folder))
-    
-    if out.find("warning:") > -1:
-        import warnings
-        warnings.warn("A warning has been issued during compilation:\n%s"%out)
-    
-    print(out)
-
-
-def compile_all():
-    src_folder = "./src"
-    func_names = (src_file.split(".cpp")[0] for src_file in listdir(src_folder) if src_file.endswith(".cpp"))
-    for func_name in func_names:
-        compile(func_name, src_folder)
-    
-    
-if __name__ == '__main__':
-    compile_all()
-
- -
-
-
- -
-
- - -
- -
- - -
-
Overwriting native_util.py
-
-
-
- -
-
- -
-
-
-
In [16]:
-
-
-
%%file util.py
-# Copyright (C) 2014 ETH Zurich, Institute for Astronomy
-
-'''
-Created on Aug 4, 2014
-
-author: jakeret
-'''
-from __future__ import print_function, division, absolute_import, unicode_literals
-import math
-
-def perf_comp_data(func_list, data_list, rep=5, number=1, extra_setup=None):
-    ''' Function to compare the performance of different functions.
-    
-    Parameters
-    ==========
-    func_list : list
-        list with function names as strings
-    data_list : list
-        list with data set names as strings
-    rep : int
-        number of repetitions of the whole comparison
-    number : int
-        number of executions for every function
-    '''
-    from timeit import repeat
-    res_list = {}
-    for name in enumerate(func_list):
-        if data_list is None:
-            stmt = "%s()"%(name[1])
-            setup = "from __main__ import %s"%(name[1]) 
-        else:
-            stmt = "%s(%s)"%(name[1], data_list[name[0]])
-            setup = "from __main__ import %s, %s"%(name[1], data_list[name[0]])
-        if extra_setup is not None:
-            stmt = extra_setup + "; " + stmt
-              
-        results = repeat(stmt=stmt, setup=setup, repeat=rep, number=number)
-        
-        res_list[name[1]] = (median(results), min(results))
-        
-#     res_sort = sorted(res_list.iteritems(), key=lambda (k, v): (v, k))
-    res_sort = sorted(iter(res_list.items()), key=lambda k_v: (k_v[1], k_v[0]))
-    for func, (av_time, min_time) in res_sort:
-        rel = av_time / res_sort[0][1][0]
-        print('function: {0!s:20}, av. time sec: {1:>12.8f}, min. time sec: {2:>12.8f}, relative: {3:>9.1f}'.format(func, av_time, min_time, rel))
-
-def median(x):
-    s_x = sorted(x)
-    return (s_x[int(math.floor((len(s_x)-1)/2))] + s_x[int(math.ceil((len(s_x)-1)/2))])/2
-
- -
-
-
- -
-
- - -
- -
- - -
-
Overwriting util.py
-
-
-
- -
-
- -
-
-
- - - - - - diff --git a/benchmarks/numexpr.html b/benchmarks/numexpr.html deleted file mode 100644 index 1a33351..0000000 --- a/benchmarks/numexpr.html +++ /dev/null @@ -1,12383 +0,0 @@ - - - -numexpr - - - - - - - - - - - - - - - - - - - - -
-
- -
-
-
In [1]:
-
-
-
import hope
-hope.config.optimize = True
-hope.config.verbose = True
-hope.config.keeptemp = True
-import numba
-import numpy as np
-import numexpr as ne
-
-from util import perf_comp_data
-from native_util import load
-%load_ext Cython
-%load_ext version_information
-%version_information numpy, Cython, numba, hope, numexpr
-
- -
-
-
- -
-
- - -
- -
Out[1]:
- - - -
-
SoftwareVersion
Python3.5.1 64bit [GCC 4.2.1 (Apple Inc. build 5666) (dot 3)]
IPython6.1.0
OSDarwin 15.6.0 x86_64 i386 64bit
numpy1.13.1
Cython0.26
numba0.34.0
hope0.6.1
numexpr2.6.2
Mon Sep 04 16:42:26 2017 CEST
-
- -
- -
-
- -
-
-
-
In [2]:
-
-
-
# Python version
-
-def ln_python(X, Y):
-    Y[:] = (X-1) - (X-1)**2 / 2 + (X-1)**3 / 3 - (X-1)**4 / 4 + (X-1)**5 / 5 - (X-1)**6 / 6 + (X-1)**7 / 7 - (X-1)**8 / 8 + (X-1)**9 / 9
-
- -
-
-
- -
-
-
-
In [3]:
-
-
-
# Python version
-
-def ln_python_exp(X, Y):
-    x = (X - 1)
-    x2 = x*x
-    x4 = x2*x2
-    x6 = x4*x2
-    x8 = x4*x4
-    Y[:] = x - x2 / 2 + x * x2 / 3 - x4 / 4 + x * x4 / 5 - x6 / 6 + x6 * x / 7 - x8 / 8 + x8 * x / 9
-
- -
-
-
- -
-
-
-
In [4]:
-
-
-
# NumExpr version
-def ln_numexpr(X, Y):
-    Y[:] = ne.evaluate("(X-1) - (X-1)**2 / 2 + (X-1)**3 / 3 - (X-1)**4 / 4 + (X-1)**5 / 5 - (X-1)**6 / 6 + (X-1)**7 / 7 - (X-1)**8 / 8 + (X-1)**9 / 9")
-
- -
-
-
- -
-
-
-
In [5]:
-
-
-
# Hope version
-@hope.jit
-def ln_hope(X, Y):
-    Y[:] = (X-1) - (X-1)**2 / 2 + (X-1)**3 / 3 - (X-1)**4 / 4 + (X-1)**5 / 5 - (X-1)**6 / 6 + (X-1)**7 / 7 - (X-1)**8 / 8 + (X-1)**9 / 9
-
- -
-
-
- -
-
-
-
In [6]:
-
-
-
# Hope version
-
-import hope
-hope.config.optimize = False
-@hope.jit
-def ln_hope_exp(X, Y):
-    x = (X - 1)
-    x2 = x*x
-    x4 = x2*x2
-    x6 = x4*x2
-    x8 = x4*x4
-    Y[:] = x - x2 / 2 + x * x2 / 3 - x4 / 4 + x * x4 / 5 - x6 / 6 + x6 * x / 7 - x8 / 8 + x8 * x / 9
-
- -
-
-
- -
-
-
-
In [7]:
-
-
-
# numba version
-
-@numba.jit
-def ln_numba(X, Y):
-    Y[:] = (X-1) - (X-1)**2 / 2 + (X-1)**3 / 3 - (X-1)**4 / 4 + (X-1)**5 / 5 - (X-1)**6 / 6 + (X-1)**7 / 7 - (X-1)**8 / 8 + (X-1)**9 / 9
-
- -
-
-
- -
-
-
-
In [8]:
-
-
-
# numba version
-
-import numba
-
-@numba.jit
-def ln_numba_exp(X, Y):
-    x = (X - 1)
-    x2 = x*x
-    x4 = x2*x2
-    x6 = x4*x2
-    x8 = x4*x4
-    Y[:] = x - x2 / 2 + x * x2 / 3 - x4 / 4 + x * x4 / 5 - x6 / 6 + x6 * x / 7 - x8 / 8 + x8 * x / 9
-
- -
-
-
- -
-
-
-
In [9]:
-
-
-
%%cython
-
-cimport cython
-
-cimport cython
-import numpy as np
-cimport numpy as np
-
-@cython.boundscheck(False)
-@cython.wraparound(False)
-def ln_cython(np.ndarray[np.double_t, ndim=1] X, np.ndarray[np.double_t, ndim=1] Y):
-    cdef size_t i, n
-    cdef double x
-    n = len(X)
-    cdef double *xx = <double *>X.data
-    cdef double *yy = <double *>Y.data
-    for i in range(n):
-        x = xx[i] 
-        yy[i] = (x-1) - (x-1)**2 / 2 + (x-1)**3 / 3 - (x-1)**4 / 4 + (x-1)**5 / 5 - (x-1)**6 / 6 + (x-1)**7 / 7 - (x-1)**8 / 8 + (x-1)**9 / 9
-
-    
-@cython.boundscheck(False)
-@cython.wraparound(False)
-def ln_cython_exp(np.ndarray[np.float_t, ndim=1] X, np.ndarray[np.float_t, ndim=1] Y):
-    cdef size_t i, n
-    cdef double x, x2, x4, x6, x8
-    cdef double *xx = <double *>X.data
-    cdef double *yy = <double *>Y.data
-    n = len(X)
-    for i in range(n):
-        x = xx[i] - 1
-        x2 = x * x
-        x4 = x2 * x2
-        x6 = x2 * x4
-        x8 = x4 * x4
-        yy[i] = x - x2 / 2 + x * x2 / 3 - x4 / 4 + x * x4 / 5 - x6 / 6 + x6 * x / 7 - x8 / 8 + x8 * x / 9
-
- -
-
-
- -
-
-
-
In [10]:
-
-
-
from native_util import load
-native_ln_mod = load("ln")
-ln_native = native_ln_mod.run
-
-native_ln_opt_mod = load("ln_opt")
-ln_native_opt = native_ln_opt_mod.run
-
-native_ln_exp_mod = load("ln_exp")
-ln_native_exp = native_ln_exp_mod.run
-
- -
-
-
- -
-
- - -
- -
- - -
-
running build_ext
-building 'ln' extension
-C compiler: /usr/bin/clang -fno-strict-aliasing -Wsign-compare -Wunreachable-code -fno-common -dynamic -DNDEBUG -g -fwrapv -O3 -Wall -Wstrict-prototypes -arch i386 -arch x86_64 -g
-
-compile options: '-I/Users/uweschmitt/Projects/hope/venv3/lib/python3.5/site-packages/numpy/core/include -I/Users/uweschmitt/Projects/hope/venv3/include -I/Library/Frameworks/Python.framework/Versions/3.5/include/python3.5m -c'
-extra options: '-Wall -Wno-unused-variable -march=native -stdlib=libc++ -std=c++11 -Wno-unreachable-code'
-clang: ././src/ln.cpp
-/usr/bin/clang++ -bundle -undefined dynamic_lookup -arch i386 -arch x86_64 -g ./src/ln.o -o ./ln.cpython-35m-darwin.so
-
-running build_ext
-building 'ln_opt' extension
-C compiler: /usr/bin/clang -fno-strict-aliasing -Wsign-compare -Wunreachable-code -fno-common -dynamic -DNDEBUG -g -fwrapv -O3 -Wall -Wstrict-prototypes -arch i386 -arch x86_64 -g
-
-compile options: '-I/Users/uweschmitt/Projects/hope/venv3/lib/python3.5/site-packages/numpy/core/include -I/Users/uweschmitt/Projects/hope/venv3/include -I/Library/Frameworks/Python.framework/Versions/3.5/include/python3.5m -c'
-extra options: '-Wall -Wno-unused-variable -march=native -stdlib=libc++ -std=c++11 -Wno-unreachable-code'
-clang: ././src/ln_opt.cpp
-/usr/bin/clang++ -bundle -undefined dynamic_lookup -arch i386 -arch x86_64 -g ./src/ln_opt.o -o ./ln_opt.cpython-35m-darwin.so
-
-running build_ext
-building 'ln_exp' extension
-C compiler: /usr/bin/clang -fno-strict-aliasing -Wsign-compare -Wunreachable-code -fno-common -dynamic -DNDEBUG -g -fwrapv -O3 -Wall -Wstrict-prototypes -arch i386 -arch x86_64 -g
-
-compile options: '-I/Users/uweschmitt/Projects/hope/venv3/lib/python3.5/site-packages/numpy/core/include -I/Users/uweschmitt/Projects/hope/venv3/include -I/Library/Frameworks/Python.framework/Versions/3.5/include/python3.5m -c'
-extra options: '-Wall -Wno-unused-variable -march=native -stdlib=libc++ -std=c++11 -Wno-unreachable-code'
-clang: ././src/ln_exp.cpp
-/usr/bin/clang++ -bundle -undefined dynamic_lookup -arch i386 -arch x86_64 -g ./src/ln_exp.o -o ./ln_exp.cpython-35m-darwin.so
-
-
-
-
- -
-
- -
-
-
-
In [11]:
-
-
-
# Hope version - optimized
-
-import hope
-hope.config.optimize = True
-
-@hope.jit
-def ln_hope_opt(X, Y):
-    Y[:] = (X-1) - (X-1)**2 / 2 + (X-1)**3 / 3 - (X-1)**4 / 4 + (X-1)**5 / 5 - (X-1)**6 / 6 + (X-1)**7 / 7 - (X-1)**8 / 8 + (X-1)**9 / 9
-
-
-hope.config.optimize = False
-
- -
-
-
- -
-
-
-
In [12]:
-
-
-
import numpy as np
-
-X = np.random.random(10000).astype(np.float64)
-Y = np.ones_like(X)
-
- -
-
-
- -
-
-
-
In [13]:
-
-
-
Y1 = np.ones_like(X)
-Y2 = np.ones_like(X)
-Y3 = np.ones_like(X)
-Y4 = np.ones_like(X)
-Y5 = np.ones_like(X)
-Y6 = np.ones_like(X)
-Y7 = np.ones_like(X)
-Y8 = np.ones_like(X)
-Y9 = np.ones_like(X)
-Y10 = np.ones_like(X)
-Y11 = np.ones_like(X)
-Y12 = np.ones_like(X)
-Y13 = np.ones_like(X)
-
-ln_python(X, Y1)
-ln_python_exp(X, Y2)
-ln_cython(X, Y3)
-ln_cython_exp(X, Y4)
-ln_numexpr(X, Y5)
-ln_hope(X, Y6)
-ln_hope_exp(X, Y7)
-ln_numba(X, Y8)
-ln_numba_exp(X, Y9)
-ln_native(X, Y10)
-ln_native_opt(X, Y11)
-ln_native_exp(X, Y12)
-ln_hope_opt(X, Y13)
-
-assert np.allclose(Y1,Y2, 1E-10)
-assert np.allclose(Y1,Y3, 1E-10)
-assert np.allclose(Y1,Y4, 1E-10)
-assert np.allclose(Y1,Y5, 1E-10)
-assert np.allclose(Y1,Y6, 1E-10)
-assert np.allclose(Y1,Y7, 1E-10)
-assert np.allclose(Y1,Y8, 1E-10)
-assert np.allclose(Y1,Y9, 1E-10)
-assert np.allclose(Y1,Y10, 1E-10)
-assert np.allclose(Y1,Y11, 1E-10)
-assert np.allclose(Y1,Y12, 1E-10)
-assert np.allclose(Y1,Y13, 1E-10)
-
- -
-
-
- -
-
- - -
- -
- - -
-
Compiling following functions:
-ln_hope(float64^1 X, float64^1 Y)
-running build_ext
-building 'ln_hope_f15670420910980a71b044ad212142e5c0bffbce3030fa3603b54df8_0' extension
-C compiler: /usr/bin/clang -fno-strict-aliasing -Wsign-compare -Wunreachable-code -fno-common -dynamic -DNDEBUG -g -fwrapv -O3 -Wall -arch i386 -arch x86_64 -g
-
-compile options: '-I/Users/uweschmitt/Projects/hope/venv3/lib/python3.5/site-packages/numpy/core/include -I/Users/uweschmitt/Projects/hope/venv3/include -I/Library/Frameworks/Python.framework/Versions/3.5/include/python3.5m -c'
-extra options: '-Wall -Wno-unused-variable -march=native -stdlib=libc++ -std=c++11 -Wno-unreachable-code'
-clang: /var/folders/k8/zfp7dvcs1m326gz1brql1tv80000gn/T/hopeh55im_15/ln_hope_f15670420910980a71b044ad212142e5c0bffbce3030fa3603b54df8_0.cpp
-/usr/bin/clang++ -bundle -undefined dynamic_lookup -arch i386 -arch x86_64 -g /var/folders/k8/zfp7dvcs1m326gz1brql1tv80000gn/T/hopeh55im_15/ln_hope_f15670420910980a71b044ad212142e5c0bffbce3030fa3603b54df8_0.o -o /var/folders/k8/zfp7dvcs1m326gz1brql1tv80000gn/T/hopeh55im_15/ln_hope_f15670420910980a71b044ad212142e5c0bffbce3030fa3603b54df8_0.cpython-35m-darwin.so
-
-Compiling following functions:
-ln_hope_exp(float64^1 X, float64^1 Y)
-running build_ext
-building 'ln_hope_exp_b65cbfbe0daf1548da7f56b10a2288d21522ef64767c336874bf9997_0' extension
-C compiler: /usr/bin/clang -fno-strict-aliasing -Wsign-compare -Wunreachable-code -fno-common -dynamic -DNDEBUG -g -fwrapv -O3 -Wall -arch i386 -arch x86_64 -g
-
-compile options: '-I/Users/uweschmitt/Projects/hope/venv3/lib/python3.5/site-packages/numpy/core/include -I/Users/uweschmitt/Projects/hope/venv3/include -I/Library/Frameworks/Python.framework/Versions/3.5/include/python3.5m -c'
-extra options: '-Wall -Wno-unused-variable -march=native -stdlib=libc++ -std=c++11 -Wno-unreachable-code'
-clang: /var/folders/k8/zfp7dvcs1m326gz1brql1tv80000gn/T/hopehdsxcvb2/ln_hope_exp_b65cbfbe0daf1548da7f56b10a2288d21522ef64767c336874bf9997_0.cpp
-/usr/bin/clang++ -bundle -undefined dynamic_lookup -arch i386 -arch x86_64 -g /var/folders/k8/zfp7dvcs1m326gz1brql1tv80000gn/T/hopehdsxcvb2/ln_hope_exp_b65cbfbe0daf1548da7f56b10a2288d21522ef64767c336874bf9997_0.o -o /var/folders/k8/zfp7dvcs1m326gz1brql1tv80000gn/T/hopehdsxcvb2/ln_hope_exp_b65cbfbe0daf1548da7f56b10a2288d21522ef64767c336874bf9997_0.cpython-35m-darwin.so
-
-Compiling following functions:
-ln_hope_opt(float64^1 X, float64^1 Y)
-running build_ext
-building 'ln_hope_opt_e61495c068774ff7cc57eca53040c8172f643a2ae5e21fb9ddd84b2c_0' extension
-C compiler: /usr/bin/clang -fno-strict-aliasing -Wsign-compare -Wunreachable-code -fno-common -dynamic -DNDEBUG -g -fwrapv -O3 -Wall -arch i386 -arch x86_64 -g
-
-compile options: '-I/Users/uweschmitt/Projects/hope/venv3/lib/python3.5/site-packages/numpy/core/include -I/Users/uweschmitt/Projects/hope/venv3/include -I/Library/Frameworks/Python.framework/Versions/3.5/include/python3.5m -c'
-extra options: '-Wall -Wno-unused-variable -march=native -stdlib=libc++ -std=c++11 -Wno-unreachable-code'
-clang: /var/folders/k8/zfp7dvcs1m326gz1brql1tv80000gn/T/hopeqyup473d/ln_hope_opt_e61495c068774ff7cc57eca53040c8172f643a2ae5e21fb9ddd84b2c_0.cpp
-/usr/bin/clang++ -bundle -undefined dynamic_lookup -arch i386 -arch x86_64 -g /var/folders/k8/zfp7dvcs1m326gz1brql1tv80000gn/T/hopeqyup473d/ln_hope_opt_e61495c068774ff7cc57eca53040c8172f643a2ae5e21fb9ddd84b2c_0.o -o /var/folders/k8/zfp7dvcs1m326gz1brql1tv80000gn/T/hopeqyup473d/ln_hope_opt_e61495c068774ff7cc57eca53040c8172f643a2ae5e21fb9ddd84b2c_0.cpython-35m-darwin.so
-
-
-
-
- -
-
- -
-
-
-
In [14]:
-
-
-
import numexpr as ne
-
-print("python")
-%timeit ln_python(X, Y)
-%timeit ln_python_exp(X, Y)
-print("numexpr (1)")
-ne.set_num_threads(1)
-%timeit ln_numexpr(X, Y)
-print("numexpr ({0})".format(ne.detect_number_of_cores()))
-ne.set_num_threads(ne.detect_number_of_cores())
-%timeit ln_numexpr(X, Y)
-print("hope")
-%timeit ln_hope(X, Y)
-%timeit ln_hope_exp(X, Y)
-%timeit ln_hope_opt(X, Y)
-print("cython")
-%timeit ln_cython(X, Y)
-%timeit ln_cython_exp(X, Y)
-print("numba")
-%timeit ln_numba(X, Y)
-%timeit ln_numba_exp(X, Y)
-print("native")
-%timeit ln_native(X, Y)
-%timeit ln_native_opt(X, Y)
-%timeit ln_native_exp(X, Y)
-
- -
-
-
- -
-
- - -
- -
- - -
-
python
-2.66 ms ± 42.8 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)
-324 µs ± 10.8 µs per loop (mean ± std. dev. of 7 runs, 1000 loops each)
-numexpr (1)
-457 µs ± 13.9 µs per loop (mean ± std. dev. of 7 runs, 1000 loops each)
-numexpr (8)
-207 µs ± 6.11 µs per loop (mean ± std. dev. of 7 runs, 1000 loops each)
-hope
-2.42 ms ± 86.2 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)
-122 µs ± 1.48 µs per loop (mean ± std. dev. of 7 runs, 10000 loops each)
-2.4 ms ± 67.7 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)
-cython
-2.34 ms ± 48.7 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)
-121 µs ± 2.79 µs per loop (mean ± std. dev. of 7 runs, 10000 loops each)
-numba
-128 µs ± 1.36 µs per loop (mean ± std. dev. of 7 runs, 10000 loops each)
-271 µs ± 4.42 µs per loop (mean ± std. dev. of 7 runs, 1000 loops each)
-native
-2.33 ms ± 43 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)
-74 µs ± 600 ns per loop (mean ± std. dev. of 7 runs, 10000 loops each)
-122 µs ± 995 ns per loop (mean ± std. dev. of 7 runs, 10000 loops each)
-
-
-
- -
-
- -
-
-
-
In [15]:
-
-
-
from util import perf_comp_data
-
- -
-
-
- -
-
-
-
In [16]:
-
-
-
func_list = ["ln_python", "ln_python_exp", 
-             "ln_numexpr", 
-             "ln_hope", "ln_hope_exp", "ln_hope_opt", 
-             "ln_cython", "ln_cython_exp", 
-             "ln_numba", "ln_numba_exp", 
-             "ln_native", "ln_native_opt", "ln_native_exp", ]
-perf_comp_data(func_list,
-               len(func_list)*["X, Y"], rep=100)
-
- -
-
-
- -
-
- - -
- -
- - -
-
function: ln_native_opt       , av. time sec:   0.00006836, min. time sec:   0.00006820, relative:       1.0
-function: ln_cython_exp       , av. time sec:   0.00010528, min. time sec:   0.00010499, relative:       1.5
-function: ln_native_exp       , av. time sec:   0.00011371, min. time sec:   0.00011366, relative:       1.7
-function: ln_hope_exp         , av. time sec:   0.00011394, min. time sec:   0.00011386, relative:       1.7
-function: ln_numba            , av. time sec:   0.00011907, min. time sec:   0.00011892, relative:       1.7
-function: ln_numexpr          , av. time sec:   0.00024022, min. time sec:   0.00017358, relative:       3.5
-function: ln_numba_exp        , av. time sec:   0.00025819, min. time sec:   0.00025539, relative:       3.8
-function: ln_python_exp       , av. time sec:   0.00027077, min. time sec:   0.00026273, relative:       4.0
-function: ln_cython           , av. time sec:   0.00215541, min. time sec:   0.00191540, relative:      31.5
-function: ln_native           , av. time sec:   0.00223247, min. time sec:   0.00220383, relative:      32.7
-function: ln_hope             , av. time sec:   0.00226024, min. time sec:   0.00220324, relative:      33.1
-function: ln_hope_opt         , av. time sec:   0.00227007, min. time sec:   0.00220347, relative:      33.2
-function: ln_python           , av. time sec:   0.00232935, min. time sec:   0.00224894, relative:      34.1
-
-
-
- -
-
- -
-
-
-
In [17]:
-
-
-
 
-
- -
-
-
- -
-
-
- - - - - - diff --git a/benchmarks/pairwise.html b/benchmarks/pairwise.html deleted file mode 100644 index 894573c..0000000 --- a/benchmarks/pairwise.html +++ /dev/null @@ -1,12201 +0,0 @@ - - - -pairwise - - - - - - - - - - - - - - - - - - - - -
-
- -
-
-
In [1]:
-
-
-
import hope
-hope.config.optimize = True
-hope.config.verbose = True
-hope.config.keeptemp = True
-import numba
-import numpy as np
-from util import perf_comp_data
-from native_util import load
-%load_ext Cython
-%load_ext version_information
-%version_information numpy, Cython, numba, hope
-
- -
-
-
- -
-
- - -
- -
Out[1]:
- - - -
-
SoftwareVersion
Python3.5.1 64bit [GCC 4.2.1 (Apple Inc. build 5666) (dot 3)]
IPython6.1.0
OSDarwin 15.6.0 x86_64 i386 64bit
numpy1.13.1
Cython0.26
numba0.34.0
hope0.6.1
Mon Sep 04 16:43:46 2017 CEST
-
- -
- -
-
- -
-
-
-
In [2]:
-
-
-
# Pure python version
-
-def pairwise_python(X, D):
-    M = X.shape[0]
-    N = X.shape[1]
-    for i in range(M):
-        for j in range(M):
-            d = 0.0
-            for k in range(N):
-                tmp = X[i, k] - X[j, k]
-                d += tmp * tmp
-            D[i, j] = np.sqrt(d)
-
- -
-
-
- -
-
-
-
In [3]:
-
-
-
# Numpy python version
-
-def pairwise_numpy(X, D):
-    M = X.shape[0]
-    for i in range(M):
-        D[i, :] = np.sqrt(np.sum((X[i, :] - X[:]) ** 2, axis=1))
-
- -
-
-
- -
-
-
-
In [4]:
-
-
-
# numba version
-@numba.jit(nopython=True)
-def pairwise_numba(X, D):
-    M = X.shape[0]
-    N = X.shape[1]
-    for i in range(M):
-        for j in range(M):
-            d = 0.0
-            for k in range(N):
-                tmp = X[i, k] - X[j, k]
-                d += tmp * tmp
-            D[i, j] = np.sqrt(d)
-
- -
-
-
- -
-
-
-
In [5]:
-
-
-
%%cython
-
-cimport cython
-from libc.math cimport sqrt
-
-@cython.boundscheck(False)
-@cython.wraparound(False)
-def pairwise_cython(double[:, ::1] X, double[:, ::1] D):
-    cdef size_t M = X.shape[0]
-    cdef size_t N = X.shape[1]
-    cdef double tmp, d
-    for i in range(M):
-        for j in range(M):
-            d = 0.0
-            for k in range(N):
-                tmp = X[i, k] - X[j, k]
-                d += tmp * tmp
-            D[i, j] = sqrt(d)
-
- -
-
-
- -
-
-
-
In [6]:
-
-
-
# hope version
-@hope.jit
-def pairwise_hope(X, D, M, N):
-    for i in range(M):
-        for j in range(M):
-            d = 0.0
-            for k in range(N):
-                tmp = X[i, k] - X[j, k]
-                d += tmp * tmp
-            D[i, j] = np.sqrt(d)
-
- -
-
-
- -
-
-
-
In [7]:
-
-
-
from native_util import load
-
-native_pairwise_mod = load("pairwise")
-pairwise_native = native_pairwise_mod.run
-
- -
-
-
- -
-
- - -
- -
- - -
-
running build_ext
-building 'pairwise' extension
-C compiler: /usr/bin/clang -fno-strict-aliasing -Wsign-compare -Wunreachable-code -fno-common -dynamic -DNDEBUG -g -fwrapv -O3 -Wall -Wstrict-prototypes -arch i386 -arch x86_64 -g
-
-compile options: '-I/Users/uweschmitt/Projects/hope/venv3/lib/python3.5/site-packages/numpy/core/include -I/Users/uweschmitt/Projects/hope/venv3/include -I/Library/Frameworks/Python.framework/Versions/3.5/include/python3.5m -c'
-extra options: '-Wall -Wno-unused-variable -march=native -stdlib=libc++ -std=c++11 -Wno-unreachable-code'
-clang: ././src/pairwise.cpp
-/usr/bin/clang++ -bundle -undefined dynamic_lookup -arch i386 -arch x86_64 -g ./src/pairwise.o -o ./pairwise.cpython-35m-darwin.so
-
-
-
-
- -
-
- -
-
-
-
In [8]:
-
-
-
X = np.random.random((1000, 3))
-D = np.empty((1000, 1000))
-
- -
-
-
- -
-
-
-
In [9]:
-
-
-
D1 = np.empty((1000, 1000))
-D2 = np.empty((1000, 1000))
-D3 = np.empty((1000, 1000))
-D4 = np.empty((1000, 1000))
-D5 = np.empty((1000, 1000))
-D6 = np.empty((1000, 1000))
-
-pairwise_python(X, D1)
-pairwise_numpy(X, D2)
-pairwise_numba(X, D3)
-pairwise_cython(X, D4)
-pairwise_hope(X, D5, X.shape[0], X.shape[1])
-pairwise_native(X, D6, X.shape[0], X.shape[1])
-
-assert np.allclose(D1, D2)
-assert np.allclose(D1, D3)
-assert np.allclose(D1, D4)
-assert np.allclose(D1, D5)
-assert np.allclose(D1, D6)
-
- -
-
-
- -
-
- - -
- -
- - -
-
pairwise_hope(float64^2 X, float64^2 D, int64 M, int64 N)
-	for i.l in (0.J:M.J) {
-		for j.l in (0.J:M.J) {
-			new d.D
-			d.D = 0.0.D
-			for k.l in (0.J:N.J) {
-				new tmp.d
-				tmp.d = (X.d[i.l, k.l] - X.d[j.l, k.l])
-				d.D += (tmp.d * tmp.d)
-			}
-			D.d[i.l, j.l] = numpy.sqrt(d.D)
-		}
-	}
-
-Compiling following functions:
-pairwise_hope(float64^2 X, float64^2 D, int64 M, int64 N)
-running build_ext
-building 'pairwise_hope_f82cb8a6ea3aecbde9f728fa85088b51f5bdf7d8ef8b740b9c0b788a_0' extension
-C compiler: /usr/bin/clang -fno-strict-aliasing -Wsign-compare -Wunreachable-code -fno-common -dynamic -DNDEBUG -g -fwrapv -O3 -Wall -arch i386 -arch x86_64 -g
-
-compile options: '-I/Users/uweschmitt/Projects/hope/venv3/lib/python3.5/site-packages/numpy/core/include -I/Users/uweschmitt/Projects/hope/venv3/include -I/Library/Frameworks/Python.framework/Versions/3.5/include/python3.5m -c'
-extra options: '-Wall -Wno-unused-variable -march=native -stdlib=libc++ -std=c++11 -Wno-unreachable-code'
-clang: /var/folders/k8/zfp7dvcs1m326gz1brql1tv80000gn/T/hope2wag9krd/pairwise_hope_f82cb8a6ea3aecbde9f728fa85088b51f5bdf7d8ef8b740b9c0b788a_0.cpp
-/usr/bin/clang++ -bundle -undefined dynamic_lookup -arch i386 -arch x86_64 -g /var/folders/k8/zfp7dvcs1m326gz1brql1tv80000gn/T/hope2wag9krd/pairwise_hope_f82cb8a6ea3aecbde9f728fa85088b51f5bdf7d8ef8b740b9c0b788a_0.o -o /var/folders/k8/zfp7dvcs1m326gz1brql1tv80000gn/T/hope2wag9krd/pairwise_hope_f82cb8a6ea3aecbde9f728fa85088b51f5bdf7d8ef8b740b9c0b788a_0.cpython-35m-darwin.so
-
-
-
-
- -
-
- -
-
-
-
In [10]:
-
-
-
print("naive python")
-%timeit pairwise_python(X, D)
-print("numpy")
-%timeit pairwise_numpy(X, D)
-print("numba")
-%timeit pairwise_numba(X, D)
-print("cython")
-%timeit pairwise_cython(X, D)
-print("hope")
-%timeit pairwise_hope(X, D, X.shape[0], X.shape[1])
-print("native")
-%timeit pairwise_native(X, D, X.shape[0], X.shape[1])
-
- -
-
-
- -
-
- - -
- -
- - -
-
naive python
-3.74 s ± 157 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)
-numpy
-43.8 ms ± 555 µs per loop (mean ± std. dev. of 7 runs, 10 loops each)
-numba
-4.67 ms ± 116 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)
-cython
-4.71 ms ± 65.4 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)
-hope
-5.76 ms ± 101 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)
-native
-4.61 ms ± 159 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)
-
-
-
- -
-
- -
-
-
-
In [11]:
-
-
-
from util import perf_comp_data
-
- -
-
-
- -
-
-
-
In [12]:
-
-
-
M, N = X.shape
-data_list = 4*["X, D"]+ 2*["X, D, M, N"]
-#print data_list
-perf_comp_data(["pairwise_python", 
-                "pairwise_numpy", 
-                "pairwise_numba", 
-                "pairwise_cython", 
-                "pairwise_hope",
-                "pairwise_native"],
-               data_list, rep=100)
-
- -
-
-
- -
-
- - -
- -
- - -
-
function: pairwise_numba      , av. time sec:   0.00443349, min. time sec:   0.00402957, relative:       1.0
-function: pairwise_cython     , av. time sec:   0.00445642, min. time sec:   0.00424550, relative:       1.0
-function: pairwise_native     , av. time sec:   0.00452401, min. time sec:   0.00426805, relative:       1.0
-function: pairwise_hope       , av. time sec:   0.00555244, min. time sec:   0.00492547, relative:       1.3
-function: pairwise_numpy      , av. time sec:   0.03890058, min. time sec:   0.03608699, relative:       8.8
-function: pairwise_python     , av. time sec:   3.80517048, min. time sec:   3.42553714, relative:     858.3
-
-
-
- -
-
- -
-
-
- - - - - - diff --git a/benchmarks/simplify.html b/benchmarks/simplify.html deleted file mode 100644 index 3a46a2e..0000000 --- a/benchmarks/simplify.html +++ /dev/null @@ -1,12656 +0,0 @@ - - - -simplify - - - - - - - - - - - - - - - - - - - - -
-
- -
-
-
In [1]:
-
-
-
import hope
-hope.config.optimize = True
-hope.config.verbose = True
-hope.config.keeptemp = True
-import numba
-import numpy as np
-import numexpr as ne
-
-from util import perf_comp_data
-from native_util import load
-%load_ext Cython
-%load_ext version_information
-%version_information numpy, Cython, numba, hope, numexpr
-
- -
-
-
- -
-
- - -
- -
Out[1]:
- - - -
-
SoftwareVersion
Python3.5.1 64bit [GCC 4.2.1 (Apple Inc. build 5666) (dot 3)]
IPython6.1.0
OSDarwin 15.6.0 x86_64 i386 64bit
numpy1.13.1
Cython0.26
numba0.34.0
hope0.6.1
numexpr2.6.2
Mon Sep 04 17:35:55 2017 CEST
-
- -
- -
-
- -
-
-
-
In [2]:
-
-
-
def poly(res, arg):
-    res[:] = np.sin(arg)**2 + (arg**3 + arg**2 - arg - 1)/(arg**2 + 2*arg + 1) + np.cos(arg)**2
-hope_poly = hope.jit(poly)
-numba_poly = numba.jit(poly, nopython=False)
-
-native_poly_mod = load("poly")
-native_poly = native_poly_mod.run
-
- -
-
-
- -
-
- - -
- -
- - -
-
running build_ext
-building 'poly' extension
-C compiler: /usr/bin/clang -fno-strict-aliasing -Wsign-compare -Wunreachable-code -fno-common -dynamic -DNDEBUG -g -fwrapv -O3 -Wall -Wstrict-prototypes -arch i386 -arch x86_64 -g
-
-compile options: '-I/Users/uweschmitt/Projects/hope/venv3/lib/python3.5/site-packages/numpy/core/include -I/Users/uweschmitt/Projects/hope/venv3/include -I/Library/Frameworks/Python.framework/Versions/3.5/include/python3.5m -c'
-extra options: '-Wall -Wno-unused-variable -march=native -stdlib=libc++ -std=c++11 -Wno-unreachable-code'
-clang: ././src/poly.cpp
-/usr/bin/clang++ -bundle -undefined dynamic_lookup -arch i386 -arch x86_64 -g ./src/poly.o -o ./poly.cpython-35m-darwin.so
-
-
-
-
- -
-
- -
-
-
-
In [3]:
-
-
-
%%cython -a
-
-cimport cython
-from libc.math cimport sin
-from libc.math cimport cos
-from libc.math cimport pow
-
-@cython.boundscheck(False)
-@cython.wraparound(False)
-@cython.cdivision(True)
-def cython_poly(double[:] res, double[:] arg):
-    cdef int i
-    cdef double i_arg
-    for i in range(len(arg)):
-        i_arg = arg[i]
-        res[i] = pow(sin(i_arg),2) + (pow(i_arg,3) + pow(i_arg,2) - i_arg - 1)/(pow(i_arg,2) + 2*i_arg + 1) + pow(cos(i_arg),2)
-
- -
-
-
- -
-
- - -
- -
Out[3]:
- - - -
- - - - - - Cython: _cython_magic_c56f3cd16e138e603a8a648f0cd9159f.pyx - - - - -

Generated by Cython 0.26

-

- Yellow lines hint at Python interaction.
- Click on a line that starts with a "+" to see the C code that Cython generated for it. -

-
 01: 
-
+02: cimport cython
-
  __pyx_t_1 = PyDict_New(); if (unlikely(!__pyx_t_1)) __PYX_ERR(0, 2, __pyx_L1_error)
-  __Pyx_GOTREF(__pyx_t_1);
-  if (PyDict_SetItem(__pyx_d, __pyx_n_s_test, __pyx_t_1) < 0) __PYX_ERR(0, 2, __pyx_L1_error)
-  __Pyx_DECREF(__pyx_t_1); __pyx_t_1 = 0;
-
 03: from libc.math cimport sin
-
 04: from libc.math cimport cos
-
 05: from libc.math cimport pow
-
 06: 
-
 07: @cython.boundscheck(False)
-
 08: @cython.wraparound(False)
-
 09: @cython.cdivision(True)
-
+10: def cython_poly(double[:] res, double[:] arg):
-
/* Python wrapper */
-static PyObject *__pyx_pw_46_cython_magic_c56f3cd16e138e603a8a648f0cd9159f_1cython_poly(PyObject *__pyx_self, PyObject *__pyx_args, PyObject *__pyx_kwds); /*proto*/
-static PyMethodDef __pyx_mdef_46_cython_magic_c56f3cd16e138e603a8a648f0cd9159f_1cython_poly = {"cython_poly", (PyCFunction)__pyx_pw_46_cython_magic_c56f3cd16e138e603a8a648f0cd9159f_1cython_poly, METH_VARARGS|METH_KEYWORDS, 0};
-static PyObject *__pyx_pw_46_cython_magic_c56f3cd16e138e603a8a648f0cd9159f_1cython_poly(PyObject *__pyx_self, PyObject *__pyx_args, PyObject *__pyx_kwds) {
-  __Pyx_memviewslice __pyx_v_res = { 0, 0, { 0 }, { 0 }, { 0 } };
-  __Pyx_memviewslice __pyx_v_arg = { 0, 0, { 0 }, { 0 }, { 0 } };
-  PyObject *__pyx_r = 0;
-  __Pyx_RefNannyDeclarations
-  __Pyx_RefNannySetupContext("cython_poly (wrapper)", 0);
-  {
-    static PyObject **__pyx_pyargnames[] = {&__pyx_n_s_res,&__pyx_n_s_arg,0};
-    PyObject* values[2] = {0,0};
-    if (unlikely(__pyx_kwds)) {
-      Py_ssize_t kw_args;
-      const Py_ssize_t pos_args = PyTuple_GET_SIZE(__pyx_args);
-      switch (pos_args) {
-        case  2: values[1] = PyTuple_GET_ITEM(__pyx_args, 1);
-        CYTHON_FALLTHROUGH;
-        case  1: values[0] = PyTuple_GET_ITEM(__pyx_args, 0);
-        CYTHON_FALLTHROUGH;
-        case  0: break;
-        default: goto __pyx_L5_argtuple_error;
-      }
-      kw_args = PyDict_Size(__pyx_kwds);
-      switch (pos_args) {
-        case  0:
-        if (likely((values[0] = PyDict_GetItem(__pyx_kwds, __pyx_n_s_res)) != 0)) kw_args--;
-        else goto __pyx_L5_argtuple_error;
-        CYTHON_FALLTHROUGH;
-        case  1:
-        if (likely((values[1] = PyDict_GetItem(__pyx_kwds, __pyx_n_s_arg)) != 0)) kw_args--;
-        else {
-          __Pyx_RaiseArgtupleInvalid("cython_poly", 1, 2, 2, 1); __PYX_ERR(0, 10, __pyx_L3_error)
-        }
-      }
-      if (unlikely(kw_args > 0)) {
-        if (unlikely(__Pyx_ParseOptionalKeywords(__pyx_kwds, __pyx_pyargnames, 0, values, pos_args, "cython_poly") < 0)) __PYX_ERR(0, 10, __pyx_L3_error)
-      }
-    } else if (PyTuple_GET_SIZE(__pyx_args) != 2) {
-      goto __pyx_L5_argtuple_error;
-    } else {
-      values[0] = PyTuple_GET_ITEM(__pyx_args, 0);
-      values[1] = PyTuple_GET_ITEM(__pyx_args, 1);
-    }
-    __pyx_v_res = __Pyx_PyObject_to_MemoryviewSlice_ds_double(values[0]); if (unlikely(!__pyx_v_res.memview)) __PYX_ERR(0, 10, __pyx_L3_error)
-    __pyx_v_arg = __Pyx_PyObject_to_MemoryviewSlice_ds_double(values[1]); if (unlikely(!__pyx_v_arg.memview)) __PYX_ERR(0, 10, __pyx_L3_error)
-  }
-  goto __pyx_L4_argument_unpacking_done;
-  __pyx_L5_argtuple_error:;
-  __Pyx_RaiseArgtupleInvalid("cython_poly", 1, 2, 2, PyTuple_GET_SIZE(__pyx_args)); __PYX_ERR(0, 10, __pyx_L3_error)
-  __pyx_L3_error:;
-  __Pyx_AddTraceback("_cython_magic_c56f3cd16e138e603a8a648f0cd9159f.cython_poly", __pyx_clineno, __pyx_lineno, __pyx_filename);
-  __Pyx_RefNannyFinishContext();
-  return NULL;
-  __pyx_L4_argument_unpacking_done:;
-  __pyx_r = __pyx_pf_46_cython_magic_c56f3cd16e138e603a8a648f0cd9159f_cython_poly(__pyx_self, __pyx_v_res, __pyx_v_arg);
-
-  /* function exit code */
-  __Pyx_RefNannyFinishContext();
-  return __pyx_r;
-}
-
-static PyObject *__pyx_pf_46_cython_magic_c56f3cd16e138e603a8a648f0cd9159f_cython_poly(CYTHON_UNUSED PyObject *__pyx_self, __Pyx_memviewslice __pyx_v_res, __Pyx_memviewslice __pyx_v_arg) {
-  int __pyx_v_i;
-  double __pyx_v_i_arg;
-  PyObject *__pyx_r = NULL;
-  __Pyx_RefNannyDeclarations
-  __Pyx_RefNannySetupContext("cython_poly", 0);
-/* … */
-  /* function exit code */
-  __pyx_r = Py_None; __Pyx_INCREF(Py_None);
-  goto __pyx_L0;
-  __pyx_L1_error:;
-  __Pyx_XDECREF(__pyx_t_1);
-  __Pyx_AddTraceback("_cython_magic_c56f3cd16e138e603a8a648f0cd9159f.cython_poly", __pyx_clineno, __pyx_lineno, __pyx_filename);
-  __pyx_r = NULL;
-  __pyx_L0:;
-  __PYX_XDEC_MEMVIEW(&__pyx_v_res, 1);
-  __PYX_XDEC_MEMVIEW(&__pyx_v_arg, 1);
-  __Pyx_XGIVEREF(__pyx_r);
-  __Pyx_RefNannyFinishContext();
-  return __pyx_r;
-}
-/* … */
-  __pyx_tuple__20 = PyTuple_Pack(4, __pyx_n_s_res, __pyx_n_s_arg, __pyx_n_s_i, __pyx_n_s_i_arg); if (unlikely(!__pyx_tuple__20)) __PYX_ERR(0, 10, __pyx_L1_error)
-  __Pyx_GOTREF(__pyx_tuple__20);
-  __Pyx_GIVEREF(__pyx_tuple__20);
-/* … */
-  __pyx_t_1 = PyCFunction_NewEx(&__pyx_mdef_46_cython_magic_c56f3cd16e138e603a8a648f0cd9159f_1cython_poly, NULL, __pyx_n_s_cython_magic_c56f3cd16e138e603a); if (unlikely(!__pyx_t_1)) __PYX_ERR(0, 10, __pyx_L1_error)
-  __Pyx_GOTREF(__pyx_t_1);
-  if (PyDict_SetItem(__pyx_d, __pyx_n_s_cython_poly, __pyx_t_1) < 0) __PYX_ERR(0, 10, __pyx_L1_error)
-  __Pyx_DECREF(__pyx_t_1); __pyx_t_1 = 0;
-  __pyx_codeobj__21 = (PyObject*)__Pyx_PyCode_New(2, 0, 4, 0, 0, __pyx_empty_bytes, __pyx_empty_tuple, __pyx_empty_tuple, __pyx_tuple__20, __pyx_empty_tuple, __pyx_empty_tuple, __pyx_kp_s_Users_uweschmitt_ipython_cython, __pyx_n_s_cython_poly, 10, __pyx_empty_bytes); if (unlikely(!__pyx_codeobj__21)) __PYX_ERR(0, 10, __pyx_L1_error)
-
 11:     cdef int i
-
 12:     cdef double i_arg
-
+13:     for i in range(len(arg)):
-
  __pyx_t_1 = __pyx_memoryview_fromslice(__pyx_v_arg, 1, (PyObject *(*)(char *)) __pyx_memview_get_double, (int (*)(char *, PyObject *)) __pyx_memview_set_double, 0);; if (unlikely(!__pyx_t_1)) __PYX_ERR(0, 13, __pyx_L1_error)
-  __Pyx_GOTREF(__pyx_t_1);
-  __pyx_t_2 = PyObject_Length(__pyx_t_1); if (unlikely(__pyx_t_2 == -1)) __PYX_ERR(0, 13, __pyx_L1_error)
-  __Pyx_DECREF(__pyx_t_1); __pyx_t_1 = 0;
-  for (__pyx_t_3 = 0; __pyx_t_3 < __pyx_t_2; __pyx_t_3+=1) {
-    __pyx_v_i = __pyx_t_3;
-
+14:         i_arg = arg[i]
-
    __pyx_t_4 = __pyx_v_i;
-    __pyx_v_i_arg = (*((double *) ( /* dim=0 */ (__pyx_v_arg.data + __pyx_t_4 * __pyx_v_arg.strides[0]) )));
-
+15:         res[i] = pow(sin(i_arg),2) + (pow(i_arg,3) + pow(i_arg,2) - i_arg - 1)/(pow(i_arg,2) + 2*i_arg + 1) + pow(cos(i_arg),2)
-
    __pyx_t_5 = __pyx_v_i;
-    *((double *) ( /* dim=0 */ (__pyx_v_res.data + __pyx_t_5 * __pyx_v_res.strides[0]) )) = ((pow(sin(__pyx_v_i_arg), 2.0) + ((((pow(__pyx_v_i_arg, 3.0) + pow(__pyx_v_i_arg, 2.0)) - __pyx_v_i_arg) - 1.0) / ((pow(__pyx_v_i_arg, 2.0) + (2.0 * __pyx_v_i_arg)) + 1.0))) + pow(cos(__pyx_v_i_arg), 2.0));
-  }
-
-
- -
- -
-
- -
-
-
-
In [4]:
-
-
-
%%cython
-cimport cython
-import numpy as np
-cimport numpy as np
-
-@cython.boundscheck(False)
-@cython.wraparound(False)
-def cython_numpy_poly(np.ndarray[np.double_t, ndim=1] res, np.ndarray[np.double_t, ndim=1] arg):
-    res[:] = np.sin(arg)**2 + (arg**3 + arg**2 - arg - 1)/(arg**2 + 2*arg + 1) + np.cos(arg)**2
-
- -
-
-
- -
-
-
-
In [5]:
-
-
-
# NumExpr version
-
-import numexpr as ne
-
-def numexpr_poly(res, arg):
-    res[:] = ne.evaluate("sin(arg)**2 + (arg**3 + arg**2 - arg - 1)/(arg**2 + 2*arg + 1) + cos(arg)**2")
-
- -
-
-
- -
-
-
-
In [6]:
-
-
-
arg = np.random.random(50000)
-res = np.empty_like(arg)
-
-res1 = np.empty_like(arg)
-res2 = np.empty_like(arg)
-res3 = np.empty_like(arg)
-res4 = np.empty_like(arg)
-res5 = np.empty_like(arg)
-
-poly(res1, arg)
-hope_poly(res2, arg)
-numba_poly(res3, arg)
-native_poly(res4, arg)
-numexpr_poly(res5, arg)
-
-assert np.allclose(res1, res2)
-assert np.allclose(res1, res3)
-assert np.allclose(res1, res4)
-assert np.allclose(res1, res5)
-
- -
-
-
- -
-
- - -
- -
- - -
-
poly(float64^1 res, float64^1 arg)
-	(:res@0) {
-		res.d[:res@0] = (((numpy.sin(arg.d[:arg@0]) ** 2.J) + (((((arg.d[:arg@0] ** 3.J) + (arg.d[:arg@0] ** 2.J)) - arg.d[:arg@0]) - 1.J) / (((arg.d[:arg@0] ** 2.J) + (2.J * arg.d[:arg@0])) + 1.J))) + (numpy.cos(arg.d[:arg@0]) ** 2.J))
-	}
-
-Compiling following functions:
-poly(float64^1 res, float64^1 arg)
-running build_ext
-building 'poly_332b2c0332e8513cfd35081ce10b11e6c57ab0ccac824978e78990c6_0' extension
-C compiler: /usr/bin/clang -fno-strict-aliasing -Wsign-compare -Wunreachable-code -fno-common -dynamic -DNDEBUG -g -fwrapv -O3 -Wall -arch i386 -arch x86_64 -g
-
-compile options: '-I/Users/uweschmitt/Projects/hope/venv3/lib/python3.5/site-packages/numpy/core/include -I/Users/uweschmitt/Projects/hope/venv3/include -I/Library/Frameworks/Python.framework/Versions/3.5/include/python3.5m -c'
-extra options: '-Wall -Wno-unused-variable -march=native -stdlib=libc++ -std=c++11 -Wno-unreachable-code'
-clang: /var/folders/k8/zfp7dvcs1m326gz1brql1tv80000gn/T/hope68l2_50k/poly_332b2c0332e8513cfd35081ce10b11e6c57ab0ccac824978e78990c6_0.cpp
-/usr/bin/clang++ -bundle -undefined dynamic_lookup -arch i386 -arch x86_64 -g /var/folders/k8/zfp7dvcs1m326gz1brql1tv80000gn/T/hope68l2_50k/poly_332b2c0332e8513cfd35081ce10b11e6c57ab0ccac824978e78990c6_0.o -o /var/folders/k8/zfp7dvcs1m326gz1brql1tv80000gn/T/hope68l2_50k/poly_332b2c0332e8513cfd35081ce10b11e6c57ab0ccac824978e78990c6_0.cpython-35m-darwin.so
-
-
-
-
- -
-
- -
-
-
-
In [7]:
-
-
-
print("python")
-%timeit poly(res, arg)
-print("hope")
-%timeit hope_poly(res, arg)
-print("numba")
-%timeit numba_poly(res, arg)
-print("cython")
-%timeit cython_poly(res, arg)
-print("cython numpy")
-%timeit cython_numpy_poly(res, arg)
-print("native")
-%timeit native_poly(res, arg)
-print("numexpr (1)")
-ne.set_num_threads(1)
-%timeit numexpr_poly(res, arg)
-print("numexpr ({0})".format(ne.detect_number_of_cores()))
-ne.set_num_threads(ne.detect_number_of_cores())
-%timeit numexpr_poly(res, arg)
-
- -
-
-
- -
-
- - -
- -
- - -
-
python
-2.66 ms ± 91.9 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)
-hope
-28.9 µs ± 686 ns per loop (mean ± std. dev. of 7 runs, 10000 loops each)
-numba
-685 µs ± 5.93 µs per loop (mean ± std. dev. of 7 runs, 1000 loops each)
-cython
-2.06 ms ± 44.6 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)
-cython numpy
-2.85 ms ± 67.3 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)
-native
-2.17 ms ± 21.5 µs per loop (mean ± std. dev. of 7 runs, 1000 loops each)
-numexpr (1)
-1.44 ms ± 16.7 µs per loop (mean ± std. dev. of 7 runs, 1000 loops each)
-numexpr (8)
-462 µs ± 28.2 µs per loop (mean ± std. dev. of 7 runs, 1000 loops each)
-
-
-
- -
-
- -
-
-
-
In [8]:
-
-
-
ne.set_num_threads(8)
-perf_comp_data(["poly", 
-                "hope_poly", 
-                "numba_poly", 
-                "cython_poly", 
-                "cython_numpy_poly", 
-                "native_poly",
-                "numexpr_poly"], 
-               7*["res, arg"], rep=100)
-
- -
-
-
- -
-
- - -
- -
- - -
-
function: hope_poly           , av. time sec:   0.00002556, min. time sec:   0.00002529, relative:       1.0
-function: numexpr_poly        , av. time sec:   0.00041609, min. time sec:   0.00038729, relative:      16.3
-function: numba_poly          , av. time sec:   0.00063846, min. time sec:   0.00063759, relative:      25.0
-function: native_poly         , av. time sec:   0.00186274, min. time sec:   0.00185231, relative:      72.9
-function: cython_poly         , av. time sec:   0.00196121, min. time sec:   0.00176564, relative:      76.7
-function: poly                , av. time sec:   0.00256785, min. time sec:   0.00237837, relative:     100.4
-function: cython_numpy_poly   , av. time sec:   0.00263516, min. time sec:   0.00245497, relative:     103.1
-
-
-
- -
-
- -
-
-
-
In [9]:
-
-
-
 
-
- -
-
-
- -
-
-
- - - - - - diff --git a/benchmarks/star.html b/benchmarks/star.html deleted file mode 100644 index 8d0e357..0000000 --- a/benchmarks/star.html +++ /dev/null @@ -1,12756 +0,0 @@ - - - -star - - - - - - - - - - - - - - - - - - - - -
-
- -
-
-
In [1]:
-
-
-
import hope
-hope.config.optimize = True
-hope.config.verbose = True
-hope.config.keeptemp = True
-import numba
-import numpy as np
-from util import perf_comp_data
-from native_util import load
-%load_ext Cython
-%load_ext version_information
-%version_information numpy, Cython, numba, hope
-
- -
-
-
- -
-
- - -
- -
Out[1]:
- - - -
-
SoftwareVersion
Python3.5.1 64bit [GCC 4.2.1 (Apple Inc. build 5666) (dot 3)]
IPython6.1.0
OSDarwin 15.6.0 x86_64 i386 64bit
numpy1.13.1
Cython0.26
numba0.34.0
hope0.6.1
Mon Sep 04 17:36:52 2017 CEST
-
- -
- -
-
- -
-
-
-
In [2]:
-
-
-
b = 3.5
-a = 1. / np.sqrt(2. ** (1. / (b - 1.)) - 1.)
-
-r50 = 2
-
-center = np.array([10.141, 10.414])
-dims = np.array([20, 20])
-# coefficients generated by http://keisan.casio.com/has10/SpecExec.cgi?id=system/2006/1280624821, 7th order
-x1D = np.array([ \
-      0.5 - 0.9491079123427585245262 / 2 \
-    , 0.5 - 0.7415311855993944398639 / 2 \
-    , 0.5 - 0.4058451513773971669066 / 2 \
-    , 0.5 \
-    , 0.5 + 0.4058451513773971669066 / 2 \
-    , 0.5 + 0.7415311855993944398639 / 2 \
-    , 0.5 + 0.9491079123427585245262 / 2 \
-], dtype=np.float32)
-w1D = np.array([ \
-      0.1294849661688696932706 / 2 \
-    , 0.2797053914892766679015 / 2 \
-    , 0.38183005050511894495 / 2 \
-    , 0.4179591836734693877551 / 2 \
-    , 0.38183005050511894495 / 2 \
-    , 0.2797053914892766679015 / 2 \
-    , 0.1294849661688696932706 / 2 \
-], dtype=np.float32)
-w2D = np.outer(w1D, w1D)
-
-print(dims.dtype)
-
- -
-
-
- -
-
- - -
- -
- - -
-
int64
-
-
-
- -
-
- -
-
-
-
In [3]:
-
-
-
def pdf(density, dims, center, w2D, r50, b, a):
-    for x in range(dims[0]):
-        for y in range(dims[1]):
-            dr = np.sqrt((x - center[0]) ** 2 + (y - center[1]) ** 2)
-            density[x, y] = np.sum(w2D * 2 * (b - 1) / (2 * np.pi * (r50 * a)**2) * (1 + (dr / (r50 * a))**2)**(-b))
-    return density
-
- -
-
-
- -
-
-
-
In [4]:
-
-
-
%%cython -f
-#Naive Cython implementation
-cimport cython
-import numpy as np
-cimport numpy as np
-
-@cython.boundscheck(False)
-@cython.wraparound(False)
-def cython_pdf(np.ndarray[np.float32_t, ndim=2] density, 
-               np.ndarray[np.long_t, ndim=1] dims, 
-               np.ndarray[np.float_t, ndim=1] center, 
-               np.ndarray[np.float32_t, ndim=2] w2D, 
-               float r50, 
-               float b, 
-               float a):
-    
-    cdef double dr = 0.0
-    
-    for x in range(dims[0]):
-        for y in range(dims[1]):
-            dr = np.sqrt((x - center[0]) ** 2 + (y - center[1]) ** 2)
-            density[x, y] = np.sum(w2D * 2 * (b - 1) / (2 * np.pi * (r50 * a)**2) * (1 + (dr / (r50 * a))**2)**(-b))
-    return density
-
- -
-
-
- -
-
- - -
- -
- - -
-
In file included from /Users/uweschmitt/.ipython/cython/_cython_magic_d756925050f812f526756168f2e13ee4.c:495:
-In file included from /Users/uweschmitt/Projects/hope/venv3/lib/python3.5/site-packages/numpy/core/include/numpy/arrayobject.h:4:
-In file included from /Users/uweschmitt/Projects/hope/venv3/lib/python3.5/site-packages/numpy/core/include/numpy/ndarrayobject.h:18:
-In file included from /Users/uweschmitt/Projects/hope/venv3/lib/python3.5/site-packages/numpy/core/include/numpy/ndarraytypes.h:1809:
-/Users/uweschmitt/Projects/hope/venv3/lib/python3.5/site-packages/numpy/core/include/numpy/npy_1_7_deprecated_api.h:15:2: warning: "Using deprecated NumPy API, disable it by "          "#defining NPY_NO_DEPRECATED_API NPY_1_7_API_VERSION" [-W#warnings]
-#warning "Using deprecated NumPy API, disable it by " \
- ^
-1 warning generated.
-In file included from /Users/uweschmitt/.ipython/cython/_cython_magic_d756925050f812f526756168f2e13ee4.c:495:
-In file included from /Users/uweschmitt/Projects/hope/venv3/lib/python3.5/site-packages/numpy/core/include/numpy/arrayobject.h:4:
-In file included from /Users/uweschmitt/Projects/hope/venv3/lib/python3.5/site-packages/numpy/core/include/numpy/ndarrayobject.h:18:
-In file included from /Users/uweschmitt/Projects/hope/venv3/lib/python3.5/site-packages/numpy/core/include/numpy/ndarraytypes.h:1809:
-/Users/uweschmitt/Projects/hope/venv3/lib/python3.5/site-packages/numpy/core/include/numpy/npy_1_7_deprecated_api.h:15:2: warning: "Using deprecated NumPy API, disable it by "          "#defining NPY_NO_DEPRECATED_API NPY_1_7_API_VERSION" [-W#warnings]
-#warning "Using deprecated NumPy API, disable it by " \
- ^
-1 warning generated.
-
-
-
- -
-
- -
-
-
-
In [5]:
-
-
-
%%capture
-%%cython -f
-#Modestly tunned Cython implementation
-cimport cython
-import numpy as np
-cimport numpy as np
-
-@cython.boundscheck(False)
-@cython.wraparound(False)
-@cython.cdivision(True)
-def cython_pdf_opt(np.ndarray[np.float32_t, ndim=2] density, 
-               np.ndarray[np.int64_t, ndim=1] dims, 
-               np.ndarray[np.float64_t, ndim=1] center, 
-               np.ndarray[np.float32_t, ndim=2] w2D, 
-               float r50, 
-               float b, 
-               np.float32_t a):
-    
-    cdef np.float32_t pi = np.pi
-    cdef np.float32_t dr = 0.0
-    cdef long x, y
-    cdef double fac, cx, cy
-    
-    cx = center[0]
-    cy = center[1]
-    
-    cdef float s = np.sum(w2D)
-    
-    for x in range(dims[0]):
-        for y in range(dims[1]):
-            dr = ((x - center[0]) ** 2 + (y - center[1]) ** 2)**(0.5)
-            fac = 2. * (b - 1.) / (2. * pi * (r50 * a)**2) * (1. + (dr / (r50 * a))**2)**(-b)
-            density[x, y] = fac * s
-    return density
-
- -
-
-
- -
-
-
-
In [6]:
-
-
-
%%cython
-#Aggresively tunned Cython implementation
-cimport cython
-import numpy as np
-cimport numpy as np
-
-@cython.boundscheck(False)
-@cython.wraparound(False)
-@cython.cdivision(True)
-def cython_pdf_unrolled(np.ndarray[np.float32_t, ndim=2] density, 
-               np.ndarray[np.long_t, ndim=1] dims, 
-               np.ndarray[np.float_t, ndim=1] center, 
-               np.ndarray[np.float32_t, ndim=2] w2D, 
-               float r50, 
-               float b, 
-               float a):
-
-    cdef float pi = np.pi
-    cdef int x,y,k,m
-    cdef float s,dr,d
-    cdef Py_ssize_t fac_x = 7
-    cdef Py_ssize_t fac_y = 7
-    cdef np.ndarray[np.float32_t, ndim=2] fac = np.zeros([fac_x, fac_y], dtype=np.float32)
-    cdef float w2D_fac = 2 * (b - 1) / (2 * pi * (r50 * a)**2)
-
-    for k in range(fac_x):
-        for m in range(fac_y):
-            fac[k, m] = w2D[k, m] * w2D_fac
-
-    for x in range(dims[0]):
-        for y in range(dims[1]):
-            dr = ((x - center[0]) ** 2 + (y - center[1]) ** 2)**(1./2)
-            d = (1 + (dr / (r50 * a))**2)**(-b)
-            
-            s = 0
-            for k in range(fac_x):
-                for m in range(fac_y):
-                    s += fac[k, m] * d
-            density[x, y] = s
-    return density
-
- -
-
-
- -
-
-
-
In [7]:
-
-
-
hope.config.keeptemp= True
-hope.config.optimize=True
-hope_pdf = hope.jit(pdf) 
-numba_pdf = numba.jit(pdf, nopython=False)
-
-native_pdf_mod = load("pdf")
-native_pdf = native_pdf_mod.run
-
- -
-
-
- -
-
- - -
- -
- - -
-
running build_ext
-building 'pdf' extension
-C compiler: /usr/bin/clang -fno-strict-aliasing -Wsign-compare -Wunreachable-code -fno-common -dynamic -DNDEBUG -g -fwrapv -O3 -Wall -Wstrict-prototypes -arch i386 -arch x86_64 -g
-
-compile options: '-I/Users/uweschmitt/Projects/hope/venv3/lib/python3.5/site-packages/numpy/core/include -I/Users/uweschmitt/Projects/hope/venv3/include -I/Library/Frameworks/Python.framework/Versions/3.5/include/python3.5m -c'
-extra options: '-Wall -Wno-unused-variable -march=native -stdlib=libc++ -std=c++11 -Wno-unreachable-code'
-clang: ././src/pdf.cpp
-/usr/bin/clang++ -bundle -undefined dynamic_lookup -arch i386 -arch x86_64 -g ./src/pdf.o -o ./pdf.cpython-35m-darwin.so
-
-
-
-
- -
-
- -
-
-
-
In [8]:
-
-
-
density = np.zeros((dims[0], dims[1]), dtype=np.float32)
-pdf(density, dims, center, w2D, r50, b, a)
-
-hdensity = np.zeros((dims[0], dims[1]), dtype=np.float32)
-hope_pdf(hdensity, dims, center, w2D, r50, b, a)
-
-ndensity = np.zeros((dims[0], dims[1]), dtype=np.float32)
-numba_pdf(ndensity, dims, center, w2D, r50, b, a)
-
-cdensity = np.zeros((dims[0], dims[1]), dtype=np.float32)
-cython_pdf(cdensity, dims, center, w2D, r50, b, a)
-
-c2density = np.zeros((dims[0], dims[1]), dtype=np.float32)
-cython_pdf_opt(c2density, dims, center, w2D, r50, b, a)
-
-c3density = np.zeros((dims[0], dims[1]), dtype=np.float32)
-cython_pdf_unrolled(c3density, dims, center, w2D, r50, b, a)
-
-nadensity = np.zeros((dims[0], dims[1]), dtype=np.float32)
-native_pdf(nadensity, dims, center, w2D, r50, b, a)
-
-assert np.allclose(density, hdensity)
-assert np.allclose(density, ndensity)
-assert np.allclose(density, cdensity)
-assert np.allclose(density, c2density)
-assert np.allclose(density, c3density)
-assert np.allclose(density, nadensity)
-
- -
-
-
- -
-
- - -
- -
- - -
-
pdf(float32^2 density, int64^1 dims, float64^1 center, float32^2 w2D, int64 r50, float64 b, float64 a)
-	for x.l in (0.J:dims.l[0.J]) {
-		for y.l in (0.J:dims.l[1.J]) {
-			new dr.d
-			dr.d = numpy.sqrt((((x.l - center.d[0.J]) ** 2.J) + ((y.l - center.d[1.J]) ** 2.J)))
-			new __sum0.d
-			__sum0.d = numpy.sum(((((w2D.f[:w2D@0,:w2D@1] * 2.J) * (b.D - 1.J)) / ((2.J * 3.141592653589793.D) * ((r50.J * a.d) ** 2.J))) * ((1.J + ((dr.d / (r50.J * a.d)) ** 2.J)) ** -b.D)))
-			density.f[x.l, y.l] = __sum0.d
-		}
-	}
-	return density.f[:density@0,:density@1]
-
-Compiling following functions:
-pdf(float32^2 density, int64^1 dims, float64^1 center, float32^2 w2D, int64 r50, float64 b, float64 a)
-running build_ext
-building 'pdf_040de60139d62fd4971c5d5623eb6e0b0475c5092ef604e04b27aacc_0' extension
-C compiler: /usr/bin/clang -fno-strict-aliasing -Wsign-compare -Wunreachable-code -fno-common -dynamic -DNDEBUG -g -fwrapv -O3 -Wall -arch i386 -arch x86_64 -g
-
-compile options: '-I/Users/uweschmitt/Projects/hope/venv3/lib/python3.5/site-packages/numpy/core/include -I/Users/uweschmitt/Projects/hope/venv3/include -I/Library/Frameworks/Python.framework/Versions/3.5/include/python3.5m -c'
-extra options: '-Wall -Wno-unused-variable -march=native -stdlib=libc++ -std=c++11 -Wno-unreachable-code'
-clang: /var/folders/k8/zfp7dvcs1m326gz1brql1tv80000gn/T/hopen3lpayny/pdf_040de60139d62fd4971c5d5623eb6e0b0475c5092ef604e04b27aacc_0.cpp
-/usr/bin/clang++ -bundle -undefined dynamic_lookup -arch i386 -arch x86_64 -g /var/folders/k8/zfp7dvcs1m326gz1brql1tv80000gn/T/hopen3lpayny/pdf_040de60139d62fd4971c5d5623eb6e0b0475c5092ef604e04b27aacc_0.o -o /var/folders/k8/zfp7dvcs1m326gz1brql1tv80000gn/T/hopen3lpayny/pdf_040de60139d62fd4971c5d5623eb6e0b0475c5092ef604e04b27aacc_0.cpython-35m-darwin.so
-
-
-
-
- -
-
- -
-
-
-
In [9]:
-
-
-
print("Python")
-%timeit pdf(density, dims, center, w2D, r50, b, a)
-print("hope")
-%timeit hope_pdf(density, dims, center, w2D, r50, b, a)
-print("Numba")
-%timeit numba_pdf(density, dims, center, w2D, r50, b, a)
-print("Cython")
-%timeit cython_pdf(density, dims, center, w2D, r50, b, a)
-print("Cython opt")
-%timeit cython_pdf_opt(density, dims, center, w2D, r50, b, a)
-print("Cython unrolled")
-%timeit cython_pdf_unrolled(density, dims, center, w2D, r50, b, a)
-print("Native")
-%timeit native_pdf(density, dims, center, w2D, r50, b, a)
-
- -
-
-
- -
-
- - -
- -
- - -
-
Python
-7.85 ms ± 297 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)
-hope
-112 µs ± 1.38 µs per loop (mean ± std. dev. of 7 runs, 10000 loops each)
-Numba
-176 µs ± 1.85 µs per loop (mean ± std. dev. of 7 runs, 10000 loops each)
-Cython
-5.49 ms ± 159 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)
-Cython opt
-26.4 µs ± 614 ns per loop (mean ± std. dev. of 7 runs, 10000 loops each)
-Cython unrolled
-39.1 µs ± 677 ns per loop (mean ± std. dev. of 7 runs, 10000 loops each)
-Native
-51.3 µs ± 1.21 µs per loop (mean ± std. dev. of 7 runs, 10000 loops each)
-
-
-
- -
-
- -
-
-
-
In [10]:
-
-
-
funcs = ["pdf", 
-         "numba_pdf", 
-         "hope_pdf", 
-         "cython_pdf", 
-         "cython_pdf_opt", 
-         "cython_pdf_unrolled",
-         "native_pdf", 
-         ]
-perf_comp_data(funcs, 
-               len(funcs)*["density, dims, center, w2D, r50, b, a"], rep=100)
-
- -
-
-
- -
-
- - -
- -
- - -
-
function: cython_pdf_opt      , av. time sec:   0.00003660, min. time sec:   0.00002738, relative:       1.0
-function: cython_pdf_unrolled , av. time sec:   0.00003935, min. time sec:   0.00003671, relative:       1.1
-function: native_pdf          , av. time sec:   0.00004619, min. time sec:   0.00004378, relative:       1.3
-function: hope_pdf            , av. time sec:   0.00010971, min. time sec:   0.00010025, relative:       3.0
-function: numba_pdf           , av. time sec:   0.00016097, min. time sec:   0.00016051, relative:       4.4
-function: cython_pdf          , av. time sec:   0.00510810, min. time sec:   0.00480720, relative:     139.6
-function: pdf                 , av. time sec:   0.00751459, min. time sec:   0.00676002, relative:     205.3
-
-
-
- -
-
- -
-
-
-
In [11]:
-
-
-
from matplotlib import pyplot as plt
-from mpl_toolkits.mplot3d import Axes3D
-from matplotlib import cm
-from matplotlib.ticker import LinearLocator, FormatStrFormatter
-
-density = np.zeros((dims[0], dims[1]), dtype=np.float32)
-pdf(density, dims, center, w2D, r50, b, a)
-
-fig = plt.figure()
-ax = fig.gca(projection='3d')
-gx, gy = np.mgrid[0:dims[0], 0:dims[1]]
-surf = ax.plot_surface(gx, gy, density, rstride=1, cstride=1, cmap=cm.coolwarm, linewidth=0, antialiased=False)
-fig.colorbar(surf, shrink=0.5, aspect=5)
-plt.show()
-
- -
-
-
- -
-
- - -
- -
- - - - -
- -
- -
- -
-
- -
-
-
- - - - - - diff --git a/examples/.gitignore b/examples/.gitignore new file mode 100644 index 0000000..97e16d1 --- /dev/null +++ b/examples/.gitignore @@ -0,0 +1,2 @@ +hope_calls +hope_numpy diff --git a/tests/test_control_structures.py b/tests/test_control_structures.py index b26686b..ede549c 100644 --- a/tests/test_control_structures.py +++ b/tests/test_control_structures.py @@ -6,10 +6,17 @@ from __future__ import print_function, division, absolute_import, unicode_literals import numpy as np -import hope, itertools, pytest, sys, sysconfig, os, shutil +import hope +import itertools +import pytest +import sys +import sysconfig +import os +import shutil from .utilities import random, check, make_test, JENKINS, min_dtypes, dtypes, shapes, setup_module, setup_method, teardown_module + def test_ifelse_scalar(): def fkt(a, b, c): if b > 0: @@ -30,6 +37,7 @@ def fkt(a, b, c): # for aa in a: # c[0] = a + @pytest.mark.parametrize("dtype", dtypes) def test_for_range_1(dtype): def fkt(a, b, c): @@ -37,29 +45,34 @@ def fkt(a, b, c): c[:, i] = a[i, :] c[i, 1] = a[0, 1] + b[i, 5] hfkt = hope.jit(fkt) - (ao, ah), (bo, bh), (co, ch) = random(dtype, [10, 10]), random(dtype, [10, 10]), random(dtype, [10, 10]) - ao, ah, bo, bh = (ao / 2.).astype(dtype), (ah / 2.).astype(dtype), (bo / 2.).astype(dtype), (bh / 2.).astype(dtype) + (ao, ah), (bo, bh), (co, ch) = random(dtype, [10, 10]), random( + dtype, [10, 10]), random(dtype, [10, 10]) + ao, ah, bo, bh = (ao / 2.).astype(dtype), (ah / 2.).astype(dtype), (bo / + 2.).astype(dtype), (bh / 2.).astype(dtype) ro, rh = fkt(ao, bo, co), hfkt(ah, bh, ch) assert check(co, ch) ro, rh = fkt(ao, bo, co), hfkt(ah, bh, ch) assert check(co, ch) + +@pytest.mark.skipif(sys.version_info.major >= 3, reason="requires python2") @pytest.mark.parametrize("dtype", dtypes) def test_for_xrange_1(dtype): - if sys.version_info.major >= 3: - xrange = range def fkt(a, b, c): for i in xrange(10): c[:, i] = a[i, :] c[i, 1] = a[0, 1] + b[i, 5] hfkt = hope.jit(fkt) - (ao, ah), (bo, bh), (co, ch) = random(dtype, [10, 10]), random(dtype, [10, 10]), random(dtype, [10, 10]) - ao, ah, bo, bh = (ao / 2.).astype(dtype), (ah / 2.).astype(dtype), (bo / 2.).astype(dtype), (bh / 2.).astype(dtype) + (ao, ah), (bo, bh), (co, ch) = random(dtype, [10, 10]), random( + dtype, [10, 10]), random(dtype, [10, 10]) + ao, ah, bo, bh = (ao / 2.).astype(dtype), (ah / 2.).astype(dtype), (bo / + 2.).astype(dtype), (bh / 2.).astype(dtype) ro, rh = fkt(ao, bo, co), hfkt(ah, bh, ch) assert check(co, ch) ro, rh = fkt(ao, bo, co), hfkt(ah, bh, ch) assert check(co, ch) + @pytest.mark.parametrize("dtype", dtypes) def test_for_range_2(dtype): def fkt(a, b, c): @@ -67,12 +80,15 @@ def fkt(a, b, c): c[:, i] = a[i, :] c[i, 1] = a[0, 1] + b[i, 5] hfkt = hope.jit(fkt) - (ao, ah), (bo, bh), (co, ch) = random(dtype, [10, 10]), random(dtype, [10, 10]), random(dtype, [10, 10]) - ao, ah, bo, bh = (ao / 2.).astype(dtype), (ah / 2.).astype(dtype), (bo / 2.).astype(dtype), (bh / 2.).astype(dtype) + (ao, ah), (bo, bh), (co, ch) = random(dtype, [10, 10]), random( + dtype, [10, 10]), random(dtype, [10, 10]) + ao, ah, bo, bh = (ao / 2.).astype(dtype), (ah / 2.).astype(dtype), (bo / + 2.).astype(dtype), (bh / 2.).astype(dtype) assert check(co, ch) ro, rh = fkt(ao, bo, co), hfkt(ah, bh, ch) assert check(co, ch) + @pytest.mark.parametrize("dtype", dtypes) def test_for_iteration_vars(dtype): """ diff --git a/tox.ini b/tox.ini index 69be3a7..97d83a1 100644 --- a/tox.ini +++ b/tox.ini @@ -1,32 +1,48 @@ [tox] envlist = py27, py35 -envdir = - py27: {toxworkdir}/py27 - style: {toxworkdir}/py27 - docs: {toxworkdir}/py27 - publish_docs: {toxworkdir}/py27 - [testenv] deps = pip>=9.0.0 setuptools>=34.0.0 numpy - style: pylint - style: flake8 commands = pip install -r requirements.txt pip install -e . # in case we have to compile c extensions - py.test -v --basetemp={envtmpdir} --cov-report term-missing --cov-report html --cov hope tests + test: py.test -v --basetemp={envtmpdir} --cov-report term-missing --cov-report html --cov hope tests - docs: ./render_benchmarks_to_html.sh - docs: sphinx-build -b linkcheck docs/ docs/_build/ # checks external links - docs: sphinx-build -b html docs/ docs/_build/ - docs: rm -fr docs/_build/htmlcov - docs: mv htmlcov docs/_build +[testenv:style] +deps = + pylint + flake8 +commands = + pylint --disable R0913,R0914 hope tests + flake8 tests hope - publish_docs: publish_docs docs/_build hope - publish_docs: create_index_html +[testenv:docs] +basepython = + python3.5 - style: pylint --disable R0913,R0914 hope tests - style: flake8 tests hope +deps = + pip>=9.0.0 + setuptools>=34.0.0 + numpy + jupyter + numba + Cython + numexpr + version_information +commands = + pip install -r requirements.txt + pip install -e . # in case we have to compile c extensions + + ./render_benchmarks_to_html.sh + sphinx-build -b linkcheck docs/ docs/_build/ # checks external links + sphinx-build -b html docs/ docs/_build/ + rm -fr docs/_build/htmlcov + mv htmlcov docs/_build + +[testenv:publish_docs] +commands = + publish_docs docs/_build hope + create_index_html From b269511acf3e452c85c115bd097b900ec104b609 Mon Sep 17 00:00:00 2001 From: Uwe Date: Tue, 5 Sep 2017 17:58:24 +0200 Subject: [PATCH 15/48] fix .gitlab-ci.yml --- .gitlab-ci.yml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 3b501a8..d25a462 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -37,4 +37,5 @@ publish_docs: - pyenv local 2.7 3.4.6 3.5.3 3.6.2 - tox -e publish_docs -i https://cosmo-pypi.phys.ethz.ch/simple when: manual - only: master + only: + - master From 1a3303369604ae95083fc6c0a0719e083bbc7b87 Mon Sep 17 00:00:00 2001 From: Uwe Date: Tue, 5 Sep 2017 17:59:59 +0200 Subject: [PATCH 16/48] fix .gitlab-ci.yml --- .gitlab-ci.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index d25a462..532cb8b 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -2,6 +2,7 @@ stages: - style - test_code - test_docs + - publish_docs tests: tags: @@ -39,3 +40,4 @@ publish_docs: when: manual only: - master + stage: publish_docs From 2dc95da7eedd8c1cb6faa8dc3dc336241c2341a3 Mon Sep 17 00:00:00 2001 From: Uwe Date: Tue, 5 Sep 2017 19:21:36 +0200 Subject: [PATCH 17/48] fixed broken links in docs/index.rst --- docs/index.rst | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/index.rst b/docs/index.rst index 6c224c3..f1c424c 100644 --- a/docs/index.rst +++ b/docs/index.rst @@ -22,9 +22,9 @@ Benchmarks ---------- All the benchmarks have been made available online as `IPython notebooks -`_. +`_. Test coverage ------------- -`Test coverage reports `_. +`Test coverage reports `_. From 3b9330a696064778d00ea4ba797ddb642384771f Mon Sep 17 00:00:00 2001 From: Uwe Date: Tue, 5 Sep 2017 19:22:43 +0200 Subject: [PATCH 18/48] fixed mistake in tox.ini --- tox.ini | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tox.ini b/tox.ini index 97d83a1..8aaf94b 100644 --- a/tox.ini +++ b/tox.ini @@ -9,7 +9,7 @@ deps = commands = pip install -r requirements.txt pip install -e . # in case we have to compile c extensions - test: py.test -v --basetemp={envtmpdir} --cov-report term-missing --cov-report html --cov hope tests + py.test -v --basetemp={envtmpdir} --cov-report term-missing --cov-report html --cov hope tests [testenv:style] deps = From 68b8d5ad89cbd7eb4ca63e325eb62429f4c25502 Mon Sep 17 00:00:00 2001 From: Uwe Date: Wed, 6 Sep 2017 11:43:35 +0200 Subject: [PATCH 19/48] attempt to pass htmlcov from test job to docs job --- .gitlab-ci.yml | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 532cb8b..61cadbe 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -12,6 +12,9 @@ tests: - pyenv local 2.7 3.4.6 3.5.3 3.6.2 - tox -r -vv -i https://cosmo-pypi.phys.ethz.ch/simple stage: test_code + artifacts: + paths: + - htmlcov/ style: tags: @@ -30,6 +33,8 @@ docs: - pyenv local 2.7 3.4.6 3.5.3 3.6.2 - tox -e docs -i https://cosmo-pypi.phys.ethz.ch/simple stage: test_docs + dependencies: + - tests publish_docs: tags: From 396747727c36b9eef40fec455049509e374b40cd Mon Sep 17 00:00:00 2001 From: Uwe Date: Wed, 6 Sep 2017 12:05:11 +0200 Subject: [PATCH 20/48] only run one test in tox.ini to shorten gitlab ci job run time for debugging --- tox.ini | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tox.ini b/tox.ini index 8aaf94b..9f71426 100644 --- a/tox.ini +++ b/tox.ini @@ -9,7 +9,7 @@ deps = commands = pip install -r requirements.txt pip install -e . # in case we have to compile c extensions - py.test -v --basetemp={envtmpdir} --cov-report term-missing --cov-report html --cov hope tests + py.test -v --basetemp={envtmpdir} --cov-report term-missing --cov-report html --cov hope tests/test_Parser.py [testenv:style] deps = From a361d239ec7949594644487994cfe1ba7544b979 Mon Sep 17 00:00:00 2001 From: Uwe Date: Wed, 6 Sep 2017 12:06:26 +0200 Subject: [PATCH 21/48] fake style job in .gilab-ci.yml to shorten job run time for debugging --- .gitlab-ci.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 61cadbe..1124256 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -21,7 +21,7 @@ style: - python script: - pyenv local 2.7 3.4.6 3.5.3 3.6.2 - - tox -e style -i https://cosmo-pypi.phys.ethz.ch/simple + - false # tox -e style -i https://cosmo-pypi.phys.ethz.ch/simple allow_failure: true stage: style From 76dd4ed9fe523782f9a3901f1ab78c94087b02af Mon Sep 17 00:00:00 2001 From: Uwe Date: Wed, 6 Sep 2017 12:06:58 +0200 Subject: [PATCH 22/48] fake style job in .gilab-ci.yml to shorten job run time for debugging --- .gitlab-ci.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 1124256..ed3e1b1 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -21,7 +21,7 @@ style: - python script: - pyenv local 2.7 3.4.6 3.5.3 3.6.2 - - false # tox -e style -i https://cosmo-pypi.phys.ethz.ch/simple + - false allow_failure: true stage: style From f27520d1d9daa439017de5cf4c3ec4138d4e4fa1 Mon Sep 17 00:00:00 2001 From: Uwe Date: Wed, 6 Sep 2017 12:08:15 +0200 Subject: [PATCH 23/48] fake style job in .gilab-ci.yml to shorten job run time for debugging --- .gitlab-ci.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index ed3e1b1..306f37a 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -21,7 +21,7 @@ style: - python script: - pyenv local 2.7 3.4.6 3.5.3 3.6.2 - - false + - echo done allow_failure: true stage: style From 489e86cd1bb9d2b9a1a7ee21ccd86ece4be60996 Mon Sep 17 00:00:00 2001 From: Uwe Date: Wed, 6 Sep 2017 12:48:42 +0200 Subject: [PATCH 24/48] fake style job in .gilab-ci.yml to shorten job run time for debugging --- .gitlab-ci.yml | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 306f37a..fa80395 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -35,6 +35,10 @@ docs: stage: test_docs dependencies: - tests + artifacts: + paths: + - htmlcov/ + - benchmarks/*.html publish_docs: tags: @@ -43,6 +47,7 @@ publish_docs: - pyenv local 2.7 3.4.6 3.5.3 3.6.2 - tox -e publish_docs -i https://cosmo-pypi.phys.ethz.ch/simple when: manual - only: - - master stage: publish_docs + dependencies: + - tests + - docs From f4cbc3d967ef168e966dc4217674a70d8dd19404 Mon Sep 17 00:00:00 2001 From: Uwe Date: Wed, 6 Sep 2017 13:35:13 +0200 Subject: [PATCH 25/48] fixed publishing docs --- .gitlab-ci.yml | 4 +--- tox.ini | 2 ++ 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index fa80395..ee6fc8e 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -37,8 +37,7 @@ docs: - tests artifacts: paths: - - htmlcov/ - - benchmarks/*.html + - docs/_build publish_docs: tags: @@ -49,5 +48,4 @@ publish_docs: when: manual stage: publish_docs dependencies: - - tests - docs diff --git a/tox.ini b/tox.ini index 9f71426..0d0970c 100644 --- a/tox.ini +++ b/tox.ini @@ -41,6 +41,8 @@ commands = sphinx-build -b html docs/ docs/_build/ rm -fr docs/_build/htmlcov mv htmlcov docs/_build + mkdir -p docs/_build/benchmarks + mv benchmarks/*.html docs/_build/benchmarks [testenv:publish_docs] commands = From 6c1f2b28483082decd3534ee694c8cbbc6f149cb Mon Sep 17 00:00:00 2001 From: Uwe Date: Wed, 6 Sep 2017 14:32:17 +0200 Subject: [PATCH 26/48] still fixing .gitlab-ci.yml and tox for docs generation and publishing --- .gitlab-ci.yml | 8 ++++---- tox.ini | 8 ++------ 2 files changed, 6 insertions(+), 10 deletions(-) diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index ee6fc8e..1ac4dea 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -1,7 +1,7 @@ stages: - style - test_code - - test_docs + - test_and_create_docs - publish_docs tests: @@ -32,7 +32,7 @@ docs: script: - pyenv local 2.7 3.4.6 3.5.3 3.6.2 - tox -e docs -i https://cosmo-pypi.phys.ethz.ch/simple - stage: test_docs + stage: test_and_create_docs dependencies: - tests artifacts: @@ -43,8 +43,8 @@ publish_docs: tags: - python script: - - pyenv local 2.7 3.4.6 3.5.3 3.6.2 - - tox -e publish_docs -i https://cosmo-pypi.phys.ethz.ch/simple + - publish_docs docs/_build hope + - create_index_html when: manual stage: publish_docs dependencies: diff --git a/tox.ini b/tox.ini index 0d0970c..cde9c34 100644 --- a/tox.ini +++ b/tox.ini @@ -42,9 +42,5 @@ commands = rm -fr docs/_build/htmlcov mv htmlcov docs/_build mkdir -p docs/_build/benchmarks - mv benchmarks/*.html docs/_build/benchmarks - -[testenv:publish_docs] -commands = - publish_docs docs/_build hope - create_index_html + /bin/bash -c "mv benchmarks/*.html docs/_build/benchmarks" + /bin/bash -c "mv benchmarks/*.ipynb docs/_build/benchmarks" From 04fa896a4f5fd4347056b318fb112bf1f6640419 Mon Sep 17 00:00:00 2001 From: Uwe Schmitt Date: Wed, 6 Sep 2017 14:47:13 +0200 Subject: [PATCH 27/48] Update pipeline badge README.rst --- README.rst | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/README.rst b/README.rst index 9d434ad..bfbddba 100644 --- a/README.rst +++ b/README.rst @@ -5,9 +5,6 @@ HOPE - combine the ease of Python and the speed of C++ .. image:: https://badge.fury.io/py/hope.svg :target: http://badge.fury.io/py/hope -.. image:: https://travis-ci.org/cosmo-ethz/hope.svg?branch=master - :target: https://travis-ci.org/cosmo-ethz/hope - .. image:: https://coveralls.io/repos/cosmo-ethz/hope/badge.svg?branch=master :target: https://coveralls.io/r/cosmo-ethz/hope?branch=master @@ -17,6 +14,8 @@ HOPE - combine the ease of Python and the speed of C++ .. image:: http://img.shields.io/badge/arXiv-1410.4345-brightgreen.svg?style=flat :target: http://arxiv.org/abs/1410.4345 +.. image:: https://cosmo-gitlab.phys.ethz.ch/cosmo/hope/badges/master/build.svg + **HOPE** is a specialized method-at-a-time JIT compiler written in Python for translating Python source code into C++ and compiles this at runtime. In contrast to other existing JIT compliers, which are designed for general purpose, we have focused our development of the subset of the Python language that is most relevant for astrophysical calculations. By concentrating on this subset, **HOPE** is able to achieve the highest possible performance From 687f3bc642291e9800ef4e95c4cd6914a940445e Mon Sep 17 00:00:00 2001 From: Uwe Schmitt Date: Wed, 6 Sep 2017 14:52:29 +0200 Subject: [PATCH 28/48] Update badges in README.rst --- README.rst | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/README.rst b/README.rst index bfbddba..0a75b8c 100644 --- a/README.rst +++ b/README.rst @@ -5,17 +5,12 @@ HOPE - combine the ease of Python and the speed of C++ .. image:: https://badge.fury.io/py/hope.svg :target: http://badge.fury.io/py/hope -.. image:: https://coveralls.io/repos/cosmo-ethz/hope/badge.svg?branch=master - :target: https://coveralls.io/r/cosmo-ethz/hope?branch=master - -.. image:: https://img.shields.io/badge/docs-latest-blue.svg?style=flat - :target: http://hope.readthedocs.org/en/latest - .. image:: http://img.shields.io/badge/arXiv-1410.4345-brightgreen.svg?style=flat :target: http://arxiv.org/abs/1410.4345 .. image:: https://cosmo-gitlab.phys.ethz.ch/cosmo/hope/badges/master/build.svg +.. image:: https://cosmo-gitlab.phys.ethz.ch/cosmo/hope/badges/master/coverage.svg **HOPE** is a specialized method-at-a-time JIT compiler written in Python for translating Python source code into C++ and compiles this at runtime. In contrast to other existing JIT compliers, which are designed for general purpose, we have focused our development of the subset of the Python language that is most relevant for astrophysical calculations. By concentrating on this subset, **HOPE** is able to achieve the highest possible performance From 0abfa7b44e342268b112e6cc1604ae37ecd7f6af Mon Sep 17 00:00:00 2001 From: Uwe Date: Wed, 6 Sep 2017 15:21:10 +0200 Subject: [PATCH 29/48] tox.ini and .gitlab-ci.yml now run all tests again as test setup seems to work --- .gitlab-ci.yml | 2 ++ tox.ini | 2 +- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 1ac4dea..a863684 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -49,3 +49,5 @@ publish_docs: stage: publish_docs dependencies: - docs + only: + - master diff --git a/tox.ini b/tox.ini index cde9c34..e051351 100644 --- a/tox.ini +++ b/tox.ini @@ -9,7 +9,7 @@ deps = commands = pip install -r requirements.txt pip install -e . # in case we have to compile c extensions - py.test -v --basetemp={envtmpdir} --cov-report term-missing --cov-report html --cov hope tests/test_Parser.py + py.test -v --basetemp={envtmpdir} --cov-report term-missing --cov-report html --cov hope tests [testenv:style] deps = From 281e8f94b24f39f2183338f19be68d10509a29d0 Mon Sep 17 00:00:00 2001 From: Uwe Date: Fri, 8 Sep 2017 10:46:14 +0200 Subject: [PATCH 30/48] reformatted README.rst --- README.rst | 27 ++++++++++++++++++++++----- 1 file changed, 22 insertions(+), 5 deletions(-) diff --git a/README.rst b/README.rst index 9d434ad..ee714d8 100644 --- a/README.rst +++ b/README.rst @@ -18,10 +18,19 @@ HOPE - combine the ease of Python and the speed of C++ :target: http://arxiv.org/abs/1410.4345 -**HOPE** is a specialized method-at-a-time JIT compiler written in Python for translating Python source code into C++ and compiles this at runtime. In contrast to other existing JIT compliers, which are designed for general purpose, we have focused our development of the subset of the Python language that is most relevant for astrophysical calculations. By concentrating on this subset, **HOPE** is able to achieve the highest possible performance +**HOPE** is a specialized method-at-a-time JIT compiler written in Python for +translating Python source code into C++ and compiles this at runtime. In +contrast to other existing JIT compliers, which are designed for general +purpose, we have focused our development of the subset of the Python language +that is most relevant for astrophysical calculations. By concentrating on this +subset, **HOPE** is able to achieve the highest possible performance -By using **HOPE**, the user can benefit from being able to write common numerical code in Python and having the performance of compiled implementation. To enable the **HOPE** JIT compilation, the user needs to add a decorator to the function definition. The package does not require additional information, which ensures that **HOPE** is as non-intrusive as possible: +By using **HOPE**, the user can benefit from being able to write common +numerical code in Python and having the performance of compiled implementation. +To enable the **HOPE** JIT compilation, the user needs to add a decorator to +the function definition. The package does not require additional information, +which ensures that **HOPE** is as non-intrusive as possible: .. code-block:: python @@ -32,14 +41,22 @@ By using **HOPE**, the user can benefit from being able to write common numerica return x + y -The **HOPE** package has been developed at ETH Zurich in the `Software Lab of the Cosmology Research Group `_ of the `ETH Institute of Astronomy `_, and is now publicly available at `GitHub `_. +The **HOPE** package has been developed at ETH Zurich in the `Software Lab of +the Cosmology Research Group +`_ of the `ETH +Institute of Astronomy `_, and is now publicly +available at `GitHub `_. -Further information on the package can be found in our `paper `_, on `readthedocs.org `_ and on our `website `_. +Further information on the package can be found in our `paper +`_, on +`readthedocs.org `_ and on our +`website `_. Installation ------------ -The package has been uploaded to `PyPI `_ and can be installed at the command line via pip:: +The package has been uploaded to `PyPI `_ +and can be installed at the command line via pip:: $ pip install hope From 8e73678dd3fd16c0b1b69dae7b2d129fe20b8aea Mon Sep 17 00:00:00 2001 From: Uwe Date: Fri, 8 Sep 2017 10:48:37 +0200 Subject: [PATCH 31/48] added supported python versions to README.rst --- README.rst | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/README.rst b/README.rst index ee714d8..3ee1124 100644 --- a/README.rst +++ b/README.rst @@ -7,7 +7,7 @@ HOPE - combine the ease of Python and the speed of C++ .. image:: https://travis-ci.org/cosmo-ethz/hope.svg?branch=master :target: https://travis-ci.org/cosmo-ethz/hope - + .. image:: https://coveralls.io/repos/cosmo-ethz/hope/badge.svg?branch=master :target: https://coveralls.io/r/cosmo-ethz/hope?branch=master @@ -23,7 +23,7 @@ translating Python source code into C++ and compiles this at runtime. In contrast to other existing JIT compliers, which are designed for general purpose, we have focused our development of the subset of the Python language that is most relevant for astrophysical calculations. By concentrating on this -subset, **HOPE** is able to achieve the highest possible performance +subset, **HOPE** is able to achieve the highest possible performance. By using **HOPE**, the user can benefit from being able to write common @@ -40,17 +40,22 @@ which ensures that **HOPE** is as non-intrusive as possible: def sum(x, y): return x + y - + The **HOPE** package has been developed at ETH Zurich in the `Software Lab of the Cosmology Research Group `_ of the `ETH Institute of Astronomy `_, and is now publicly -available at `GitHub `_. +available at `GitHub `_. Further information on the package can be found in our `paper `_, on `readthedocs.org `_ and on our -`website `_. +`website `_. + +Python versions supported +------------------------- + +Hope supports Python versions from `2.7` up to `3.6`. Installation ------------ From 594a5256bcdc1f7446e290c7e04a23e076d1968c Mon Sep 17 00:00:00 2001 From: Uwe Date: Fri, 8 Sep 2017 10:50:06 +0200 Subject: [PATCH 32/48] bumped to version 0.7.0 --- AUTHORS.rst | 3 ++- HISTORY.rst | 9 ++++++++- hope/__init__.py | 2 +- setup.py | 2 +- 4 files changed, 12 insertions(+), 4 deletions(-) diff --git a/AUTHORS.rst b/AUTHORS.rst index 5e73ffa..a84b4ec 100644 --- a/AUTHORS.rst +++ b/AUTHORS.rst @@ -20,6 +20,7 @@ We would like to thank several people for helping to test this package before re * Chihway Chang * Sebastian Gaebel * Joerg Herbel +* Uwe Schmitt Citations @@ -35,4 +36,4 @@ Feedback If you have any suggestions or questions about **HOPE** feel free to email me at hope@phys.ethz.ch. -If you encounter any errors or problems with **HOPE**, please let me know! \ No newline at end of file +If you encounter any errors or problems with **HOPE**, please let me know! diff --git a/HISTORY.rst b/HISTORY.rst index 89b5efd..f33c751 100644 --- a/HISTORY.rst +++ b/HISTORY.rst @@ -2,6 +2,13 @@ History ------- + +0.7.0 (2017-09-08) +++++++++++++++++++ + +* Support for Python 3.5 and 3.6 + + 0.6.1 (2016-07-04) ++++++++++++++++++ @@ -90,4 +97,4 @@ AugBitAnd ``a & b`` 0.1.0 (2014-02-27) ++++++++++++++++++ -* Initial creation. \ No newline at end of file +* Initial creation. diff --git a/hope/__init__.py b/hope/__init__.py index e8aca8a..dd85445 100644 --- a/hope/__init__.py +++ b/hope/__init__.py @@ -17,7 +17,7 @@ __all__ = ["jit", "config", "serialize", "unserialize"] __author__ = "Lukas Gamper, Joel Akeret" __email__ = "hope@phys.ethz.ch" -__version__ = "0.6.1" +__version__ = "0.7.0" __credits__ = "ETH Zurich, Institute for Astronomy" from hope import config diff --git a/setup.py b/setup.py index b845c38..ff1ffb0 100644 --- a/setup.py +++ b/setup.py @@ -39,7 +39,7 @@ def run_tests(self): setup( name="hope", - version="0.6.1", + version="0.7.0", description="A Python Just-In-Time compiler for astrophysical computations", long_description=readme + "\n\n" + history, author="Lukas Gamper, Joel Akeret", From 5c5c518db0e60965f2503427a1b8c5a0450d8160 Mon Sep 17 00:00:00 2001 From: Uwe Date: Fri, 8 Sep 2017 10:59:03 +0200 Subject: [PATCH 33/48] activated style check again in .gitlab-ci.yml, was deactivated for debugging purposes --- .gitlab-ci.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index a863684..1dbd1dc 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -21,7 +21,7 @@ style: - python script: - pyenv local 2.7 3.4.6 3.5.3 3.6.2 - - echo done + - tox -e style -i https://cosmo-pypi.phys.ethz.ch/simple allow_failure: true stage: style From bf80ecddb7105d1550c2db6dd286385a4d8f110b Mon Sep 17 00:00:00 2001 From: Uwe Date: Fri, 8 Sep 2017 10:59:40 +0200 Subject: [PATCH 34/48] added py36 to tox.ini --- tox.ini | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tox.ini b/tox.ini index e051351..ca66154 100644 --- a/tox.ini +++ b/tox.ini @@ -1,5 +1,5 @@ [tox] -envlist = py27, py35 +envlist = py27, py35, py36 [testenv] deps = From 246686eb8dfc79b548e266dddd4d9df8f3cbd213 Mon Sep 17 00:00:00 2001 From: Uwe Date: Fri, 8 Sep 2017 11:02:53 +0200 Subject: [PATCH 35/48] added packages needed for running benchmark notebooks to requirements.txt --- requirements.txt | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/requirements.txt b/requirements.txt index 60deee0..452f644 100644 --- a/requirements.txt +++ b/requirements.txt @@ -6,3 +6,9 @@ numpy>=1.9.2 pytest>=2.6.3 pytest-cov>=1.8.0 sympy>=0.7.5 +matplotlib +Cython +jupyter +numba +numexpr +version_information From 9295ad10ff63153d9fe17466390ac49da8211ff9 Mon Sep 17 00:00:00 2001 From: Uwe Date: Fri, 8 Sep 2017 11:19:18 +0200 Subject: [PATCH 36/48] requirements.txt only lists jupyter for python 3 --- requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements.txt b/requirements.txt index 452f644..7dae8a4 100644 --- a/requirements.txt +++ b/requirements.txt @@ -8,7 +8,7 @@ pytest-cov>=1.8.0 sympy>=0.7.5 matplotlib Cython -jupyter +jupyter; python_version >= '3' numba numexpr version_information From bc24faee0ce7f8e5d0c8efd94b06b838557cc1a1 Mon Sep 17 00:00:00 2001 From: Uwe Date: Fri, 8 Sep 2017 11:21:20 +0200 Subject: [PATCH 37/48] updated theme for docs --- docs/conf.py | 9 +++------ requirements.txt | 1 + 2 files changed, 4 insertions(+), 6 deletions(-) diff --git a/docs/conf.py b/docs/conf.py index 6d7e79c..6aae040 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -253,9 +253,6 @@ # If true, do not generate a @detailmenu in the "Top" node's menu. #texinfo_no_detailmenu = False -try: - import sphinx_eth_theme - html_theme = "sphinx_eth_theme" - html_theme_path = [sphinx_eth_theme.get_html_theme_path()] -except ImportError: - html_theme = 'default' \ No newline at end of file +import sphinx_pynpoint_theme +html_theme = "sphinx_pynpoint_theme" +html_theme_path = [sphinx_pynpoint_theme.get_html_theme_path()] diff --git a/requirements.txt b/requirements.txt index 7dae8a4..0f9c7dd 100644 --- a/requirements.txt +++ b/requirements.txt @@ -12,3 +12,4 @@ jupyter; python_version >= '3' numba numexpr version_information +sphinx-pynpoint-theme From 775384a8b1df9afe7d0e49c3d4805a6ee2b50960 Mon Sep 17 00:00:00 2001 From: Uwe Date: Fri, 8 Sep 2017 13:22:20 +0200 Subject: [PATCH 38/48] added missing --extra-index-url to requirements.txt --- requirements.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/requirements.txt b/requirements.txt index 0f9c7dd..425c2eb 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,3 +1,4 @@ +--extra-index-url https://cosmo-pypi.phys.ethz.ch/simple/ Sphinx>=1.2.3 coverage>=3.7.1 flake8>=2.2.4 From 63b806d5f7ea63628e61933ec3e76675baa15102 Mon Sep 17 00:00:00 2001 From: Uwe Date: Fri, 8 Sep 2017 14:40:16 +0200 Subject: [PATCH 39/48] commit ad2957d was incomplete, now also set seed of numpys random generator --- test/.test_optimize.py.swp | Bin 20480 -> 0 bytes tests/conftest.py | 3 +++ 2 files changed, 3 insertions(+) delete mode 100644 test/.test_optimize.py.swp diff --git a/test/.test_optimize.py.swp b/test/.test_optimize.py.swp deleted file mode 100644 index 532594789f30c1dadaa7a1056496f98a038543f5..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 20480 zcmeI2Uu+ab9LEP*5LB=c|2=3jwgm3lyKS$QP@7aSSlTLojIAbc)@8akcf0g%XWiMO z9{zpN2ZM>mgeMcx5cPrRi{OL72P10WL0>SDkBXapLKXe65Wo!z~bwo(eEP+&Is zbpOoG?9BImGyVN$X0YYq-VL!tQ-e&N?-eFjFHRsb~1vWq6i*4KrWV0J9a`(`DB`G}BUbeyxC3Kr5gX&g=WYREIF@BFyV8{+f43Fq4Q{>J#cenNUpeBTtG*@X1?R-d#2S^=$qRzNGD z70?Q31+)TM0j+>mKr5gXxCjcc93i_9y#J&Ee60Uhk zuEss+0nMNe{I!^nFToM;Ja`23f?L5qiwOB1yasLt^TDyJ2ssK40~>UMbzmh}3Pu*9 zJg^VE2VMYq&<9q4h2Yo%Lf!!@z)fHw_!HVG{cO+;lAsR!23@}b?}PoI0PY4Uuo(P; zNtZ9cPEZR*FzNC&_yD{CqJ!dKQgT0_md&l9ge6kAN)nZj9|{so-HT+3Qail0$C8Vs9UvpMeSQL2bpv%lleZYjIV14S>sjIYetFrLx#tER^ow=Z7zxWP`Omtrur0h z0{qTG6s6($B3HJ8s2~})T4pmH1IZ)_t8$vwtiv9hs5$3sHPf=TNyY~8U=q-diKo<0%x$uVcg6ua5`)|mQ8bxWW}Nwmel11 zys({9{|qlsa~lol&8KOQ>H*hSDe!mc9chV_phYfm+xxoH0>+JeC-rO;_M%7KFvhfC^|Jb?fw@>x{;f4StC!D!87&ZR+(nL9F%=-n$;G0J`}-I*9zc~qRP&8NBO203J0YqmIH1^6-m*u z>sTVkO-RChIPs*uzMk}>^|YEja(K6@DsQDxGmY>q(m>ZDX!D(d9ni$uByDZUw9&5q z9=fIM!;4PQURMTAfX{_K^F0!dw<}5&qT>G(h?fr_o>uXHQyg`EhZz4e@D_LzJPEdd zZm=BGf?pBm?*+@j4WJhMf%yIha1iVTFMr}V z<-#}tGr=4l?ld3zocdgri-F@7uy7JkN8$nIkSk$fJVz6Vp_7vfRFlcF?5RKmFOB-# zb7Hc6@@$HV1kcNKcDSB%`jwtmW5O~`yaF^S$LQ+Lq#Q)mbs7shvp#Lz*qCk&D>GFH z9j)S=_2hImTm;EGp3O%qJovpD^SdQa@cO@GmDg=j)zX50Ssp4iK7 JPi(X&@ISeVmq-8r diff --git a/tests/conftest.py b/tests/conftest.py index c0567eb..baeebd5 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -5,4 +5,7 @@ # unforeseeable failures when numpy changed its behaviour handling exponentiation of integer # types. import random +import numpy as np + random.seed(12345) +np.random.seed(12345) From ec7f5a3b4682ca5dc5a419e315b408a0372f4d7f Mon Sep 17 00:00:00 2001 From: Uwe Date: Mon, 11 Sep 2017 11:15:18 +0200 Subject: [PATCH 40/48] rearanged .gitlab-ci.yml to make style check a job indipendent from pipeline --- .gitlab-ci.yml | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 1dbd1dc..c7a1d00 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -1,9 +1,18 @@ stages: - - style - test_code - test_and_create_docs - publish_docs +style: + tags: + - python + script: + - pyenv local 2.7 3.4.6 3.5.3 3.6.2 + - tox -e style -i https://cosmo-pypi.phys.ethz.ch/simple + allow_failure: true + stage: style + + tests: tags: - python @@ -16,15 +25,6 @@ tests: paths: - htmlcov/ -style: - tags: - - python - script: - - pyenv local 2.7 3.4.6 3.5.3 3.6.2 - - tox -e style -i https://cosmo-pypi.phys.ethz.ch/simple - allow_failure: true - stage: style - docs: tags: From c1cf688f4979d7f91c77acbe89caa71b1c6e3832 Mon Sep 17 00:00:00 2001 From: Uwe Date: Mon, 11 Sep 2017 11:18:37 +0200 Subject: [PATCH 41/48] fixed .gitlab-ci.yml --- .gitlab-ci.yml | 1 - 1 file changed, 1 deletion(-) diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index c7a1d00..a4e2dfc 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -10,7 +10,6 @@ style: - pyenv local 2.7 3.4.6 3.5.3 3.6.2 - tox -e style -i https://cosmo-pypi.phys.ethz.ch/simple allow_failure: true - stage: style tests: From 0b37189678eb1638071154b5c0b7b245e3b0884a Mon Sep 17 00:00:00 2001 From: Uwe Date: Mon, 11 Sep 2017 11:25:35 +0200 Subject: [PATCH 42/48] Revert "fixed .gitlab-ci.yml" This reverts commit c1cf688f4979d7f91c77acbe89caa71b1c6e3832. --- .gitlab-ci.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index a4e2dfc..c7a1d00 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -10,6 +10,7 @@ style: - pyenv local 2.7 3.4.6 3.5.3 3.6.2 - tox -e style -i https://cosmo-pypi.phys.ethz.ch/simple allow_failure: true + stage: style tests: From 95efab8347868d2fac35bc74ecde2af123669d43 Mon Sep 17 00:00:00 2001 From: Uwe Date: Mon, 11 Sep 2017 11:25:37 +0200 Subject: [PATCH 43/48] Revert "rearanged .gitlab-ci.yml to make style check a job indipendent from pipeline" This reverts commit ec7f5a3b4682ca5dc5a419e315b408a0372f4d7f. --- .gitlab-ci.yml | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index c7a1d00..1dbd1dc 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -1,18 +1,9 @@ stages: + - style - test_code - test_and_create_docs - publish_docs -style: - tags: - - python - script: - - pyenv local 2.7 3.4.6 3.5.3 3.6.2 - - tox -e style -i https://cosmo-pypi.phys.ethz.ch/simple - allow_failure: true - stage: style - - tests: tags: - python @@ -25,6 +16,15 @@ tests: paths: - htmlcov/ +style: + tags: + - python + script: + - pyenv local 2.7 3.4.6 3.5.3 3.6.2 + - tox -e style -i https://cosmo-pypi.phys.ethz.ch/simple + allow_failure: true + stage: style + docs: tags: From 6eaf2c1b44e8f2f12a73657701ca2f0219b3b2b6 Mon Sep 17 00:00:00 2001 From: Uwe Date: Mon, 11 Sep 2017 11:25:35 +0200 Subject: [PATCH 44/48] Revert last two changes as jobs can not be independent of pipelines --- .gitlab-ci.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index a4e2dfc..c7a1d00 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -10,6 +10,7 @@ style: - pyenv local 2.7 3.4.6 3.5.3 3.6.2 - tox -e style -i https://cosmo-pypi.phys.ethz.ch/simple allow_failure: true + stage: style tests: From 7ab9c54a55da97b90f94be9fb22a84426d9e5357 Mon Sep 17 00:00:00 2001 From: Uwe Date: Mon, 11 Sep 2017 13:07:24 +0200 Subject: [PATCH 45/48] attempt to get more information why rendering notebooks fails Revert "rearanged .gitlab-ci.yml to make style check a job indipendent from pipeline" This reverts commit ec7f5a3b4682ca5dc5a419e315b408a0372f4d7f. --- .gitlab-ci.yml | 20 ++++++++++---------- hope/.options.py.swo | Bin 16384 -> 0 bytes render_benchmarks_to_html.sh | 4 +++- requirements.txt | 11 +++++++---- 4 files changed, 20 insertions(+), 15 deletions(-) delete mode 100644 hope/.options.py.swo diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index c7a1d00..1dbd1dc 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -1,18 +1,9 @@ stages: + - style - test_code - test_and_create_docs - publish_docs -style: - tags: - - python - script: - - pyenv local 2.7 3.4.6 3.5.3 3.6.2 - - tox -e style -i https://cosmo-pypi.phys.ethz.ch/simple - allow_failure: true - stage: style - - tests: tags: - python @@ -25,6 +16,15 @@ tests: paths: - htmlcov/ +style: + tags: + - python + script: + - pyenv local 2.7 3.4.6 3.5.3 3.6.2 + - tox -e style -i https://cosmo-pypi.phys.ethz.ch/simple + allow_failure: true + stage: style + docs: tags: diff --git a/hope/.options.py.swo b/hope/.options.py.swo deleted file mode 100644 index 47b3bd45f244ce01d67ea445dc76736a27ca9b00..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 16384 zcmeHNO>ZPe8Ln)A4GCc*xS>RP-PUSn@Jz2~c3D_yJrvg{P`N=4;nD5s89SEj=^S zRd2l?Prvn6)y(WK-r2ZLSKUR1>m|ni;{WLWa+99@;9bVjBQE`3Unr$JpMEtKn^&(i znr;~TUifq|_GqN=;>{%fF87tZ*oy}|dx-~1#F2Cd!xMDaF3W&r;A1e5s!sjti|kUf zVW#@LGe=+j%Ff47WQSM=ECZGS%YbFTGGH073|Iy%1OKlKsKFWbHk?0`2XH@se`f0a z+5Encdt<6UzqN0c0n318z%pPNunbrRECZGS%YbFTGGH07415L|@B+r}!LL8*13!NM zFV6q(on!1V@H+4vU;}swI0rlf{N&4w9Rptj{`DebKLox3TmqWF%fNHMS>Uftaf&9oa9&=ZBcXb9$PyiCPeB|h$|CS0;)TZqO+rcP`aTVk_*n3O{LqWKrMWRvuTELo-nn^m>(=eH)xy&D zZs~L6_4$M9QmLk3VcJ`ncT`KsqJ>(ZX&+AcTuRsPasQwlr)rR@NoHt)E>Jy4`-5E7 zg&N5o55v~&B;{2%;C>wNO4Usycp6kjmO4q?r$9)RDiKPza8dO2WxG0Vx6RP&V#fb8gsgFIxtr%FHuq_cE#w(o-ou?=C~QI!(X{~ul?=K z<@Ie>DBd?=YlTd|oGx~^pmcZfLFQ^kgVs=VwY61bih~5MhayUkomrndA_^*6!l}|V zsyB#-n8*_lWW}eP~wR$(RoSJ<&5@+^xD?VHHXd<%tNe_XPKsa zR1M1H*meAc1?K?*7;Dl`Dq}#qG}fWpPbq42%*f^`k}2mQ9(ds;wOX{fabxFR`}MW^ zr*+g+%G0`fg78zE?Ry0eQ;x1K-@3bTW8|qRWP{LCdYV&Fq745`Q#e`Z#sspYc!G>u zCQl(fn~YBy=9ej%doiAurtXG1_q)}c}WCiOZm#UW)k6lNOzW5(`@Hvo=&!oa`t+Q&9>>5u|;c%u46*$ zty{inW3$@_RI`U>6`K*Rv8mAzbuPQ#bQc{qvmMO(nr_3vGUkOx-caUiKZr*CHg#%Z zL&fHAZ>?@Ekr!ZdsP|!{*ZUYLkd)af87s@Dwyg`L( zFT2eXY)o#(3xB6Q$)p^CRqJ$a^u5IIwIWlLig-h@-vab1shJsLU^=P(iyPVU^NBc8 zuF!bK4{@&3+iLxvbhkWaV5g96s!!TkpjMfo?RF>C^}XFLMhy}XDYQnu{(E~3#qdz* zGgXbeeHn+T;_Xa-4b@NRJZNJx#S!bA0tD(MgyXNj&X|`?9$C7JIpZ|Y~GyLxX zef}?g-#^3|U$=dr+wEl;unbrRECZGS%YbFTGGH073|Iy%1D1i$90O1Oy6V$wo&D}> U`iI5IpRPVV&qkJtpDAnXKZjiY`~Uy| diff --git a/render_benchmarks_to_html.sh b/render_benchmarks_to_html.sh index 9feaf44..9edb7e2 100755 --- a/render_benchmarks_to_html.sh +++ b/render_benchmarks_to_html.sh @@ -1,5 +1,7 @@ #!/bin/bash +set -e + function convert { jupyter nbconvert --log-level=10 --to html --execute --ExecutePreprocessor.timeout=-1 "$1" @@ -12,7 +14,7 @@ rm src/*.o rm -rf .hope rm -rf hope -convert native_cpp_gen.ipynb +convert native_cpp_gen.ipynb convert "HPC Python.ipynb" convert fibonacci.ipynb convert simplify.ipynb diff --git a/requirements.txt b/requirements.txt index 425c2eb..cb80763 100644 --- a/requirements.txt +++ b/requirements.txt @@ -9,8 +9,11 @@ pytest-cov>=1.8.0 sympy>=0.7.5 matplotlib Cython + +# only needed for createing docs with Python 3 +scipy; python_version >= '3' jupyter; python_version >= '3' -numba -numexpr -version_information -sphinx-pynpoint-theme +numba; python_version >= '3' +numexpr; python_version >= '3' +version_information; python_version >= '3' +sphinx-pynpoint-theme; python_version >= '3' From 327210cef673a1a8e7eba28aabc7e9f6406fc4b5 Mon Sep 17 00:00:00 2001 From: Uwe Date: Mon, 11 Sep 2017 15:47:30 +0200 Subject: [PATCH 46/48] fixed render_benchmarks_to_html.sh --- render_benchmarks_to_html.sh | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/render_benchmarks_to_html.sh b/render_benchmarks_to_html.sh index 9edb7e2..59831df 100755 --- a/render_benchmarks_to_html.sh +++ b/render_benchmarks_to_html.sh @@ -1,6 +1,5 @@ #!/bin/bash -set -e function convert { @@ -9,12 +8,14 @@ function convert pushd benchmarks -rm *.so -rm src/*.o +rm -f *.so +rm -f src/*.o rm -rf .hope rm -rf hope -convert native_cpp_gen.ipynb +set -e + +convert native_cpp_gen.ipynb convert "HPC Python.ipynb" convert fibonacci.ipynb convert simplify.ipynb @@ -22,4 +23,5 @@ convert star.ipynb convert julialang.org.ipynb convert numexpr.ipynb convert pairwise.ipynb + popd From a60c456a6c28541e7afca5b4f249b90c499fddeb Mon Sep 17 00:00:00 2001 From: Uwe Date: Wed, 27 Sep 2017 14:50:08 +0200 Subject: [PATCH 47/48] renamed folder tests -> test --- Makefile | 2 +- {tests => test}/__init__.py | 0 {tests => test}/conftest.py | 0 {tests => test}/test_Parser.py | 0 {tests => test}/test_blocks.py | 0 {tests => test}/test_call.py | 0 {tests => test}/test_cast.py | 0 {tests => test}/test_comparison.py | 0 {tests => test}/test_control_structures.py | 0 {tests => test}/test_dump.py | 0 {tests => test}/test_functions.py | 0 {tests => test}/test_jit.py | 0 {tests => test}/test_object.py | 0 {tests => test}/test_op_binary.py | 0 {tests => test}/test_op_bool.py | 0 {tests => test}/test_op_div.py | 0 {tests => test}/test_op_minus.py | 0 {tests => test}/test_op_mult.py | 0 {tests => test}/test_op_plus.py | 0 {tests => test}/test_op_pow.py | 0 {tests => test}/test_operators.py | 0 {tests => test}/test_optimize.py | 0 {tests => test}/test_options.py | 0 {tests => test}/test_pycosmo.py | 0 {tests => test}/test_rangecheck.py | 0 {tests => test}/test_return.py | 0 {tests => test}/test_serialization.py | 0 {tests => test}/test_slice.py | 0 {tests => test}/test_slice_negative.py | 0 {tests => test}/test_tosource.py | 0 {tests => test}/test_ufig.py | 0 {tests => test}/test_wrapper.py | 0 {tests => test}/utilities.py | 0 33 files changed, 1 insertion(+), 1 deletion(-) rename {tests => test}/__init__.py (100%) rename {tests => test}/conftest.py (100%) rename {tests => test}/test_Parser.py (100%) rename {tests => test}/test_blocks.py (100%) rename {tests => test}/test_call.py (100%) rename {tests => test}/test_cast.py (100%) rename {tests => test}/test_comparison.py (100%) rename {tests => test}/test_control_structures.py (100%) rename {tests => test}/test_dump.py (100%) rename {tests => test}/test_functions.py (100%) rename {tests => test}/test_jit.py (100%) rename {tests => test}/test_object.py (100%) rename {tests => test}/test_op_binary.py (100%) rename {tests => test}/test_op_bool.py (100%) rename {tests => test}/test_op_div.py (100%) rename {tests => test}/test_op_minus.py (100%) rename {tests => test}/test_op_mult.py (100%) rename {tests => test}/test_op_plus.py (100%) rename {tests => test}/test_op_pow.py (100%) rename {tests => test}/test_operators.py (100%) rename {tests => test}/test_optimize.py (100%) rename {tests => test}/test_options.py (100%) rename {tests => test}/test_pycosmo.py (100%) rename {tests => test}/test_rangecheck.py (100%) rename {tests => test}/test_return.py (100%) rename {tests => test}/test_serialization.py (100%) rename {tests => test}/test_slice.py (100%) rename {tests => test}/test_slice_negative.py (100%) rename {tests => test}/test_tosource.py (100%) rename {tests => test}/test_ufig.py (100%) rename {tests => test}/test_wrapper.py (100%) rename {tests => test}/utilities.py (100%) diff --git a/Makefile b/Makefile index c577965..4d78790 100644 --- a/Makefile +++ b/Makefile @@ -28,7 +28,7 @@ lint: flake8 hope test test: - py.test -v -s tests + py.test -v -s test test-all: tox diff --git a/tests/__init__.py b/test/__init__.py similarity index 100% rename from tests/__init__.py rename to test/__init__.py diff --git a/tests/conftest.py b/test/conftest.py similarity index 100% rename from tests/conftest.py rename to test/conftest.py diff --git a/tests/test_Parser.py b/test/test_Parser.py similarity index 100% rename from tests/test_Parser.py rename to test/test_Parser.py diff --git a/tests/test_blocks.py b/test/test_blocks.py similarity index 100% rename from tests/test_blocks.py rename to test/test_blocks.py diff --git a/tests/test_call.py b/test/test_call.py similarity index 100% rename from tests/test_call.py rename to test/test_call.py diff --git a/tests/test_cast.py b/test/test_cast.py similarity index 100% rename from tests/test_cast.py rename to test/test_cast.py diff --git a/tests/test_comparison.py b/test/test_comparison.py similarity index 100% rename from tests/test_comparison.py rename to test/test_comparison.py diff --git a/tests/test_control_structures.py b/test/test_control_structures.py similarity index 100% rename from tests/test_control_structures.py rename to test/test_control_structures.py diff --git a/tests/test_dump.py b/test/test_dump.py similarity index 100% rename from tests/test_dump.py rename to test/test_dump.py diff --git a/tests/test_functions.py b/test/test_functions.py similarity index 100% rename from tests/test_functions.py rename to test/test_functions.py diff --git a/tests/test_jit.py b/test/test_jit.py similarity index 100% rename from tests/test_jit.py rename to test/test_jit.py diff --git a/tests/test_object.py b/test/test_object.py similarity index 100% rename from tests/test_object.py rename to test/test_object.py diff --git a/tests/test_op_binary.py b/test/test_op_binary.py similarity index 100% rename from tests/test_op_binary.py rename to test/test_op_binary.py diff --git a/tests/test_op_bool.py b/test/test_op_bool.py similarity index 100% rename from tests/test_op_bool.py rename to test/test_op_bool.py diff --git a/tests/test_op_div.py b/test/test_op_div.py similarity index 100% rename from tests/test_op_div.py rename to test/test_op_div.py diff --git a/tests/test_op_minus.py b/test/test_op_minus.py similarity index 100% rename from tests/test_op_minus.py rename to test/test_op_minus.py diff --git a/tests/test_op_mult.py b/test/test_op_mult.py similarity index 100% rename from tests/test_op_mult.py rename to test/test_op_mult.py diff --git a/tests/test_op_plus.py b/test/test_op_plus.py similarity index 100% rename from tests/test_op_plus.py rename to test/test_op_plus.py diff --git a/tests/test_op_pow.py b/test/test_op_pow.py similarity index 100% rename from tests/test_op_pow.py rename to test/test_op_pow.py diff --git a/tests/test_operators.py b/test/test_operators.py similarity index 100% rename from tests/test_operators.py rename to test/test_operators.py diff --git a/tests/test_optimize.py b/test/test_optimize.py similarity index 100% rename from tests/test_optimize.py rename to test/test_optimize.py diff --git a/tests/test_options.py b/test/test_options.py similarity index 100% rename from tests/test_options.py rename to test/test_options.py diff --git a/tests/test_pycosmo.py b/test/test_pycosmo.py similarity index 100% rename from tests/test_pycosmo.py rename to test/test_pycosmo.py diff --git a/tests/test_rangecheck.py b/test/test_rangecheck.py similarity index 100% rename from tests/test_rangecheck.py rename to test/test_rangecheck.py diff --git a/tests/test_return.py b/test/test_return.py similarity index 100% rename from tests/test_return.py rename to test/test_return.py diff --git a/tests/test_serialization.py b/test/test_serialization.py similarity index 100% rename from tests/test_serialization.py rename to test/test_serialization.py diff --git a/tests/test_slice.py b/test/test_slice.py similarity index 100% rename from tests/test_slice.py rename to test/test_slice.py diff --git a/tests/test_slice_negative.py b/test/test_slice_negative.py similarity index 100% rename from tests/test_slice_negative.py rename to test/test_slice_negative.py diff --git a/tests/test_tosource.py b/test/test_tosource.py similarity index 100% rename from tests/test_tosource.py rename to test/test_tosource.py diff --git a/tests/test_ufig.py b/test/test_ufig.py similarity index 100% rename from tests/test_ufig.py rename to test/test_ufig.py diff --git a/tests/test_wrapper.py b/test/test_wrapper.py similarity index 100% rename from tests/test_wrapper.py rename to test/test_wrapper.py diff --git a/tests/utilities.py b/test/utilities.py similarity index 100% rename from tests/utilities.py rename to test/utilities.py From f759c4b2218a4058d855814fec54ee0198589043 Mon Sep 17 00:00:00 2001 From: Uwe Date: Wed, 27 Sep 2017 14:50:32 +0200 Subject: [PATCH 48/48] added installation of llvm 4.0 to .travis.yml --- .travis.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index 5edec16..7d05adb 100644 --- a/.travis.yml +++ b/.travis.yml @@ -9,7 +9,7 @@ python: before_install: - sudo add-apt-repository -y ppa:ubuntu-toolchain-r/test - sudo apt-get -qq update - - sudo apt-get -qq install g++-4.8 gcc-4.8 + - sudo apt-get -qq install g++-4.8 gcc-4.8 llvm-4.0-dev - sudo ln -sf /usr/bin/gcc-4.8 /usr/bin/gcc - sudo ln -sf /usr/bin/g++-4.8 /usr/bin/g++ # We do this conditionally because it saves us some downloading if the