123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461 |
- # Tests that require installed backends go into
- # sympy/test_external/test_autowrap
- import os
- import tempfile
- import shutil
- from io import StringIO
- from sympy.core import symbols, Eq
- from sympy.utilities.autowrap import (autowrap, binary_function,
- CythonCodeWrapper, UfuncifyCodeWrapper, CodeWrapper)
- from sympy.utilities.codegen import (
- CCodeGen, C99CodeGen, CodeGenArgumentListError, make_routine
- )
- from sympy.testing.pytest import raises
- from sympy.testing.tmpfiles import TmpFileManager
- def get_string(dump_fn, routines, prefix="file", **kwargs):
- """Wrapper for dump_fn. dump_fn writes its results to a stream object and
- this wrapper returns the contents of that stream as a string. This
- auxiliary function is used by many tests below.
- The header and the empty lines are not generator to facilitate the
- testing of the output.
- """
- output = StringIO()
- dump_fn(routines, output, prefix, **kwargs)
- source = output.getvalue()
- output.close()
- return source
- def test_cython_wrapper_scalar_function():
- x, y, z = symbols('x,y,z')
- expr = (x + y)*z
- routine = make_routine("test", expr)
- code_gen = CythonCodeWrapper(CCodeGen())
- source = get_string(code_gen.dump_pyx, [routine])
- expected = (
- "cdef extern from 'file.h':\n"
- " double test(double x, double y, double z)\n"
- "\n"
- "def test_c(double x, double y, double z):\n"
- "\n"
- " return test(x, y, z)")
- assert source == expected
- def test_cython_wrapper_outarg():
- from sympy.core.relational import Equality
- x, y, z = symbols('x,y,z')
- code_gen = CythonCodeWrapper(C99CodeGen())
- routine = make_routine("test", Equality(z, x + y))
- source = get_string(code_gen.dump_pyx, [routine])
- expected = (
- "cdef extern from 'file.h':\n"
- " void test(double x, double y, double *z)\n"
- "\n"
- "def test_c(double x, double y):\n"
- "\n"
- " cdef double z = 0\n"
- " test(x, y, &z)\n"
- " return z")
- assert source == expected
- def test_cython_wrapper_inoutarg():
- from sympy.core.relational import Equality
- x, y, z = symbols('x,y,z')
- code_gen = CythonCodeWrapper(C99CodeGen())
- routine = make_routine("test", Equality(z, x + y + z))
- source = get_string(code_gen.dump_pyx, [routine])
- expected = (
- "cdef extern from 'file.h':\n"
- " void test(double x, double y, double *z)\n"
- "\n"
- "def test_c(double x, double y, double z):\n"
- "\n"
- " test(x, y, &z)\n"
- " return z")
- assert source == expected
- def test_cython_wrapper_compile_flags():
- from sympy.core.relational import Equality
- x, y, z = symbols('x,y,z')
- routine = make_routine("test", Equality(z, x + y))
- code_gen = CythonCodeWrapper(CCodeGen())
- expected = """\
- from setuptools import setup
- from setuptools import Extension
- from Cython.Build import cythonize
- cy_opts = {'compiler_directives': {'language_level': '3'}}
- ext_mods = [Extension(
- 'wrapper_module_%(num)s', ['wrapper_module_%(num)s.pyx', 'wrapped_code_%(num)s.c'],
- include_dirs=[],
- library_dirs=[],
- libraries=[],
- extra_compile_args=['-std=c99'],
- extra_link_args=[]
- )]
- setup(ext_modules=cythonize(ext_mods, **cy_opts))
- """ % {'num': CodeWrapper._module_counter}
- temp_dir = tempfile.mkdtemp()
- TmpFileManager.tmp_folder(temp_dir)
- setup_file_path = os.path.join(temp_dir, 'setup.py')
- code_gen._prepare_files(routine, build_dir=temp_dir)
- with open(setup_file_path) as f:
- setup_text = f.read()
- assert setup_text == expected
- code_gen = CythonCodeWrapper(CCodeGen(),
- include_dirs=['/usr/local/include', '/opt/booger/include'],
- library_dirs=['/user/local/lib'],
- libraries=['thelib', 'nilib'],
- extra_compile_args=['-slow-math'],
- extra_link_args=['-lswamp', '-ltrident'],
- cythonize_options={'compiler_directives': {'boundscheck': False}}
- )
- expected = """\
- from setuptools import setup
- from setuptools import Extension
- from Cython.Build import cythonize
- cy_opts = {'compiler_directives': {'boundscheck': False}}
- ext_mods = [Extension(
- 'wrapper_module_%(num)s', ['wrapper_module_%(num)s.pyx', 'wrapped_code_%(num)s.c'],
- include_dirs=['/usr/local/include', '/opt/booger/include'],
- library_dirs=['/user/local/lib'],
- libraries=['thelib', 'nilib'],
- extra_compile_args=['-slow-math', '-std=c99'],
- extra_link_args=['-lswamp', '-ltrident']
- )]
- setup(ext_modules=cythonize(ext_mods, **cy_opts))
- """ % {'num': CodeWrapper._module_counter}
- code_gen._prepare_files(routine, build_dir=temp_dir)
- with open(setup_file_path) as f:
- setup_text = f.read()
- assert setup_text == expected
- expected = """\
- from setuptools import setup
- from setuptools import Extension
- from Cython.Build import cythonize
- cy_opts = {'compiler_directives': {'boundscheck': False}}
- import numpy as np
- ext_mods = [Extension(
- 'wrapper_module_%(num)s', ['wrapper_module_%(num)s.pyx', 'wrapped_code_%(num)s.c'],
- include_dirs=['/usr/local/include', '/opt/booger/include', np.get_include()],
- library_dirs=['/user/local/lib'],
- libraries=['thelib', 'nilib'],
- extra_compile_args=['-slow-math', '-std=c99'],
- extra_link_args=['-lswamp', '-ltrident']
- )]
- setup(ext_modules=cythonize(ext_mods, **cy_opts))
- """ % {'num': CodeWrapper._module_counter}
- code_gen._need_numpy = True
- code_gen._prepare_files(routine, build_dir=temp_dir)
- with open(setup_file_path) as f:
- setup_text = f.read()
- assert setup_text == expected
- TmpFileManager.cleanup()
- def test_cython_wrapper_unique_dummyvars():
- from sympy.core.relational import Equality
- from sympy.core.symbol import Dummy
- x, y, z = Dummy('x'), Dummy('y'), Dummy('z')
- x_id, y_id, z_id = [str(d.dummy_index) for d in [x, y, z]]
- expr = Equality(z, x + y)
- routine = make_routine("test", expr)
- code_gen = CythonCodeWrapper(CCodeGen())
- source = get_string(code_gen.dump_pyx, [routine])
- expected_template = (
- "cdef extern from 'file.h':\n"
- " void test(double x_{x_id}, double y_{y_id}, double *z_{z_id})\n"
- "\n"
- "def test_c(double x_{x_id}, double y_{y_id}):\n"
- "\n"
- " cdef double z_{z_id} = 0\n"
- " test(x_{x_id}, y_{y_id}, &z_{z_id})\n"
- " return z_{z_id}")
- expected = expected_template.format(x_id=x_id, y_id=y_id, z_id=z_id)
- assert source == expected
- def test_autowrap_dummy():
- x, y, z = symbols('x y z')
- # Uses DummyWrapper to test that codegen works as expected
- f = autowrap(x + y, backend='dummy')
- assert f() == str(x + y)
- assert f.args == "x, y"
- assert f.returns == "nameless"
- f = autowrap(Eq(z, x + y), backend='dummy')
- assert f() == str(x + y)
- assert f.args == "x, y"
- assert f.returns == "z"
- f = autowrap(Eq(z, x + y + z), backend='dummy')
- assert f() == str(x + y + z)
- assert f.args == "x, y, z"
- assert f.returns == "z"
- def test_autowrap_args():
- x, y, z = symbols('x y z')
- raises(CodeGenArgumentListError, lambda: autowrap(Eq(z, x + y),
- backend='dummy', args=[x]))
- f = autowrap(Eq(z, x + y), backend='dummy', args=[y, x])
- assert f() == str(x + y)
- assert f.args == "y, x"
- assert f.returns == "z"
- raises(CodeGenArgumentListError, lambda: autowrap(Eq(z, x + y + z),
- backend='dummy', args=[x, y]))
- f = autowrap(Eq(z, x + y + z), backend='dummy', args=[y, x, z])
- assert f() == str(x + y + z)
- assert f.args == "y, x, z"
- assert f.returns == "z"
- f = autowrap(Eq(z, x + y + z), backend='dummy', args=(y, x, z))
- assert f() == str(x + y + z)
- assert f.args == "y, x, z"
- assert f.returns == "z"
- def test_autowrap_store_files():
- x, y = symbols('x y')
- tmp = tempfile.mkdtemp()
- TmpFileManager.tmp_folder(tmp)
- f = autowrap(x + y, backend='dummy', tempdir=tmp)
- assert f() == str(x + y)
- assert os.access(tmp, os.F_OK)
- TmpFileManager.cleanup()
- def test_autowrap_store_files_issue_gh12939():
- x, y = symbols('x y')
- tmp = './tmp'
- saved_cwd = os.getcwd()
- temp_cwd = tempfile.mkdtemp()
- try:
- os.chdir(temp_cwd)
- f = autowrap(x + y, backend='dummy', tempdir=tmp)
- assert f() == str(x + y)
- assert os.access(tmp, os.F_OK)
- finally:
- os.chdir(saved_cwd)
- shutil.rmtree(temp_cwd)
- def test_binary_function():
- x, y = symbols('x y')
- f = binary_function('f', x + y, backend='dummy')
- assert f._imp_() == str(x + y)
- def test_ufuncify_source():
- x, y, z = symbols('x,y,z')
- code_wrapper = UfuncifyCodeWrapper(C99CodeGen("ufuncify"))
- routine = make_routine("test", x + y + z)
- source = get_string(code_wrapper.dump_c, [routine])
- expected = """\
- #include "Python.h"
- #include "math.h"
- #include "numpy/ndarraytypes.h"
- #include "numpy/ufuncobject.h"
- #include "numpy/halffloat.h"
- #include "file.h"
- static PyMethodDef wrapper_module_%(num)sMethods[] = {
- {NULL, NULL, 0, NULL}
- };
- static void test_ufunc(char **args, npy_intp *dimensions, npy_intp* steps, void* data)
- {
- npy_intp i;
- npy_intp n = dimensions[0];
- char *in0 = args[0];
- char *in1 = args[1];
- char *in2 = args[2];
- char *out0 = args[3];
- npy_intp in0_step = steps[0];
- npy_intp in1_step = steps[1];
- npy_intp in2_step = steps[2];
- npy_intp out0_step = steps[3];
- for (i = 0; i < n; i++) {
- *((double *)out0) = test(*(double *)in0, *(double *)in1, *(double *)in2);
- in0 += in0_step;
- in1 += in1_step;
- in2 += in2_step;
- out0 += out0_step;
- }
- }
- PyUFuncGenericFunction test_funcs[1] = {&test_ufunc};
- static char test_types[4] = {NPY_DOUBLE, NPY_DOUBLE, NPY_DOUBLE, NPY_DOUBLE};
- static void *test_data[1] = {NULL};
- #if PY_VERSION_HEX >= 0x03000000
- static struct PyModuleDef moduledef = {
- PyModuleDef_HEAD_INIT,
- "wrapper_module_%(num)s",
- NULL,
- -1,
- wrapper_module_%(num)sMethods,
- NULL,
- NULL,
- NULL,
- NULL
- };
- PyMODINIT_FUNC PyInit_wrapper_module_%(num)s(void)
- {
- PyObject *m, *d;
- PyObject *ufunc0;
- m = PyModule_Create(&moduledef);
- if (!m) {
- return NULL;
- }
- import_array();
- import_umath();
- d = PyModule_GetDict(m);
- ufunc0 = PyUFunc_FromFuncAndData(test_funcs, test_data, test_types, 1, 3, 1,
- PyUFunc_None, "wrapper_module_%(num)s", "Created in SymPy with Ufuncify", 0);
- PyDict_SetItemString(d, "test", ufunc0);
- Py_DECREF(ufunc0);
- return m;
- }
- #else
- PyMODINIT_FUNC initwrapper_module_%(num)s(void)
- {
- PyObject *m, *d;
- PyObject *ufunc0;
- m = Py_InitModule("wrapper_module_%(num)s", wrapper_module_%(num)sMethods);
- if (m == NULL) {
- return;
- }
- import_array();
- import_umath();
- d = PyModule_GetDict(m);
- ufunc0 = PyUFunc_FromFuncAndData(test_funcs, test_data, test_types, 1, 3, 1,
- PyUFunc_None, "wrapper_module_%(num)s", "Created in SymPy with Ufuncify", 0);
- PyDict_SetItemString(d, "test", ufunc0);
- Py_DECREF(ufunc0);
- }
- #endif""" % {'num': CodeWrapper._module_counter}
- assert source == expected
- def test_ufuncify_source_multioutput():
- x, y, z = symbols('x,y,z')
- var_symbols = (x, y, z)
- expr = x + y**3 + 10*z**2
- code_wrapper = UfuncifyCodeWrapper(C99CodeGen("ufuncify"))
- routines = [make_routine("func{}".format(i), expr.diff(var_symbols[i]), var_symbols) for i in range(len(var_symbols))]
- source = get_string(code_wrapper.dump_c, routines, funcname='multitest')
- expected = """\
- #include "Python.h"
- #include "math.h"
- #include "numpy/ndarraytypes.h"
- #include "numpy/ufuncobject.h"
- #include "numpy/halffloat.h"
- #include "file.h"
- static PyMethodDef wrapper_module_%(num)sMethods[] = {
- {NULL, NULL, 0, NULL}
- };
- static void multitest_ufunc(char **args, npy_intp *dimensions, npy_intp* steps, void* data)
- {
- npy_intp i;
- npy_intp n = dimensions[0];
- char *in0 = args[0];
- char *in1 = args[1];
- char *in2 = args[2];
- char *out0 = args[3];
- char *out1 = args[4];
- char *out2 = args[5];
- npy_intp in0_step = steps[0];
- npy_intp in1_step = steps[1];
- npy_intp in2_step = steps[2];
- npy_intp out0_step = steps[3];
- npy_intp out1_step = steps[4];
- npy_intp out2_step = steps[5];
- for (i = 0; i < n; i++) {
- *((double *)out0) = func0(*(double *)in0, *(double *)in1, *(double *)in2);
- *((double *)out1) = func1(*(double *)in0, *(double *)in1, *(double *)in2);
- *((double *)out2) = func2(*(double *)in0, *(double *)in1, *(double *)in2);
- in0 += in0_step;
- in1 += in1_step;
- in2 += in2_step;
- out0 += out0_step;
- out1 += out1_step;
- out2 += out2_step;
- }
- }
- PyUFuncGenericFunction multitest_funcs[1] = {&multitest_ufunc};
- static char multitest_types[6] = {NPY_DOUBLE, NPY_DOUBLE, NPY_DOUBLE, NPY_DOUBLE, NPY_DOUBLE, NPY_DOUBLE};
- static void *multitest_data[1] = {NULL};
- #if PY_VERSION_HEX >= 0x03000000
- static struct PyModuleDef moduledef = {
- PyModuleDef_HEAD_INIT,
- "wrapper_module_%(num)s",
- NULL,
- -1,
- wrapper_module_%(num)sMethods,
- NULL,
- NULL,
- NULL,
- NULL
- };
- PyMODINIT_FUNC PyInit_wrapper_module_%(num)s(void)
- {
- PyObject *m, *d;
- PyObject *ufunc0;
- m = PyModule_Create(&moduledef);
- if (!m) {
- return NULL;
- }
- import_array();
- import_umath();
- d = PyModule_GetDict(m);
- ufunc0 = PyUFunc_FromFuncAndData(multitest_funcs, multitest_data, multitest_types, 1, 3, 3,
- PyUFunc_None, "wrapper_module_%(num)s", "Created in SymPy with Ufuncify", 0);
- PyDict_SetItemString(d, "multitest", ufunc0);
- Py_DECREF(ufunc0);
- return m;
- }
- #else
- PyMODINIT_FUNC initwrapper_module_%(num)s(void)
- {
- PyObject *m, *d;
- PyObject *ufunc0;
- m = Py_InitModule("wrapper_module_%(num)s", wrapper_module_%(num)sMethods);
- if (m == NULL) {
- return;
- }
- import_array();
- import_umath();
- d = PyModule_GetDict(m);
- ufunc0 = PyUFunc_FromFuncAndData(multitest_funcs, multitest_data, multitest_types, 1, 3, 3,
- PyUFunc_None, "wrapper_module_%(num)s", "Created in SymPy with Ufuncify", 0);
- PyDict_SetItemString(d, "multitest", ufunc0);
- Py_DECREF(ufunc0);
- }
- #endif""" % {'num': CodeWrapper._module_counter}
- assert source == expected
|