From 9fdefcfdbfb6e4f6b02e6a6f0a3b207547c3e022 Mon Sep 17 00:00:00 2001 From: "Jason K. Moore" Date: Sat, 2 Sep 2023 21:44:33 +0200 Subject: [PATCH 01/15] Use pycompilation if installed. --- opty/utils.py | 58 +++++++++++++++++++++++++++++++++------------------ 1 file changed, 38 insertions(+), 20 deletions(-) diff --git a/opty/utils.py b/opty/utils.py index 7d73473d..86ebcb09 100644 --- a/opty/utils.py +++ b/opty/utils.py @@ -23,6 +23,8 @@ import_kwargs={'fromlist': ['']}, catch=(RuntimeError,)) +pycompilation = sm.external.import_module('pycompilation') + def building_docs(): if 'READTHEDOCS' in os.environ: @@ -324,27 +326,43 @@ def ufuncify_matrix(args, expr, const=None, tmp_dir=None, parallel=False): files[d['file_prefix'] + '.pyx'] = _cython_template.format(**d) files[d['file_prefix'] + '_setup.py'] = _setup_template.format(**d) - workingdir = os.getcwd() - os.chdir(codedir) + if pycompilation: + sources = [ + (d['file_prefix'] + '_h.h', files[d['file_prefix'] + '_h.h']), + (d['file_prefix'] + '_c.c', files[d['file_prefix'] + '_c.c']), + (d['file_prefix'] + '.pyx', files[d['file_prefix'] + '.pyx']), + ] - try: - sys.path.append(codedir) - for filename, code in files.items(): - with open(filename, 'w') as f: - f.write(code) - cmd = [sys.executable, d['file_prefix'] + '_setup.py', 'build_ext', - '--inplace'] - subprocess.call(cmd, stderr=subprocess.STDOUT, stdout=subprocess.PIPE) - cython_module = importlib.import_module(d['file_prefix']) - finally: - module_counter += 1 - sys.path.remove(codedir) - os.chdir(workingdir) - if tmp_dir is None: - # NOTE : I can't figure out how to get rmtree to work on Windows, - # so I don't delete the directory on Windows. - if sys.platform != "win32": - shutil.rmtree(codedir) + options = ['warn', 'pic'] + if parallel: + options += ['openmp'] + + cython_module = pycompilation.compile_link_import_strings( + sources, options=options, std='c99', logger=True, + include_dirs=[np.get_include()], build_dir=codedir) + else: + workingdir = os.getcwd() + os.chdir(codedir) + + try: + sys.path.append(codedir) + for filename, code in files.items(): + with open(filename, 'w') as f: + f.write(code) + cmd = [sys.executable, d['file_prefix'] + '_setup.py', 'build_ext', + '--inplace'] + subprocess.call(cmd, stderr=subprocess.STDOUT, + stdout=subprocess.PIPE) + cython_module = importlib.import_module(d['file_prefix']) + finally: + module_counter += 1 + sys.path.remove(codedir) + os.chdir(workingdir) + if tmp_dir is None: + # NOTE : I can't figure out how to get rmtree to work on Windows, + # so I don't delete the directory on Windows. + if sys.platform != "win32": + shutil.rmtree(codedir) return getattr(cython_module, d['routine_name'] + '_loop') From 77e75dd3f41e094040a606a74caedbb4314f6a14 Mon Sep 17 00:00:00 2001 From: "Jason K. Moore" Date: Sun, 3 Sep 2023 06:37:59 +0200 Subject: [PATCH 02/15] Drop use of header file, pycompilation now works. --- opty/utils.py | 16 ++++------------ 1 file changed, 4 insertions(+), 12 deletions(-) diff --git a/opty/utils.py b/opty/utils.py index 86ebcb09..2a82aa34 100644 --- a/opty/utils.py +++ b/opty/utils.py @@ -105,7 +105,6 @@ def parse_free(free, n, q, N): _c_template = """\ #include -#include "{file_prefix}_h.h" void {routine_name}(double matrix[{matrix_output_size}], {input_args}) {{ @@ -113,18 +112,13 @@ def parse_free(free, n, q, N): }} """ -_h_template = """\ -void {routine_name}(double matrix[{matrix_output_size}], {input_args}); -""" - _cython_template = """\ import numpy as np from cython.parallel import prange cimport numpy as np cimport cython -cdef extern from "{file_prefix}_h.h"{head_gil}: - void {routine_name}(double matrix[{matrix_output_size}], {input_args}) +cdef extern void c_{routine_name} "{routine_name}" (double matrix[{matrix_output_size}], {input_args}){head_gil} @cython.boundscheck(False) @cython.wraparound(False) @@ -135,7 +129,7 @@ def {routine_name}_loop(np.ndarray[np.double_t, ndim=2] matrix, {numpy_typed_inp cdef int i for i in {loop_sig}: - {routine_name}(&matrix[i, 0], {indexed_input_args}) + c_{routine_name}(&matrix[i, 0], {indexed_input_args}) return matrix.reshape(n, {num_rows}, {num_cols}) """ @@ -322,23 +316,21 @@ def ufuncify_matrix(args, expr, const=None, tmp_dir=None, parallel=False): files = {} files[d['file_prefix'] + '_c.c'] = _c_template.format(**d) - files[d['file_prefix'] + '_h.h'] = _h_template.format(**d) files[d['file_prefix'] + '.pyx'] = _cython_template.format(**d) files[d['file_prefix'] + '_setup.py'] = _setup_template.format(**d) if pycompilation: sources = [ - (d['file_prefix'] + '_h.h', files[d['file_prefix'] + '_h.h']), (d['file_prefix'] + '_c.c', files[d['file_prefix'] + '_c.c']), (d['file_prefix'] + '.pyx', files[d['file_prefix'] + '.pyx']), ] - options = ['warn', 'pic'] + options = [] if parallel: options += ['openmp'] cython_module = pycompilation.compile_link_import_strings( - sources, options=options, std='c99', logger=True, + sources, options=options, std='c99', include_dirs=[np.get_include()], build_dir=codedir) else: workingdir = os.getcwd() From a1b872f054fa1ef77742ab695bd4bf5a1f94ce16 Mon Sep 17 00:00:00 2001 From: "Jason K. Moore" Date: Sun, 3 Sep 2023 06:47:36 +0200 Subject: [PATCH 03/15] Check if pycompilation works in CI. --- .github/workflows/run-tests.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.github/workflows/run-tests.yml b/.github/workflows/run-tests.yml index 46f43b75..4e6ce7f6 100644 --- a/.github/workflows/run-tests.yml +++ b/.github/workflows/run-tests.yml @@ -41,4 +41,6 @@ jobs: python setup.py install python -c "import opty" py.test --cov=opty opty/ + python -m pip install pycompilation + py.test --cov=opty opty/ cd docs && make clean && make html && cd .. From 2077964a3e39f0c86e673225dbfa746e5b4329ab Mon Sep 17 00:00:00 2001 From: "Jason K. Moore" Date: Tue, 5 Sep 2023 11:15:00 +0200 Subject: [PATCH 04/15] Use SymPy's compilation module instead of pycompilation. --- opty/utils.py | 24 ++++++++++++++++-------- 1 file changed, 16 insertions(+), 8 deletions(-) diff --git a/opty/utils.py b/opty/utils.py index 2a82aa34..e6b2e533 100644 --- a/opty/utils.py +++ b/opty/utils.py @@ -8,6 +8,7 @@ import importlib from functools import wraps import warnings +import logging from distutils.ccompiler import new_compiler from distutils.errors import CompileError from distutils.sysconfig import customize_compiler @@ -23,7 +24,7 @@ import_kwargs={'fromlist': ['']}, catch=(RuntimeError,)) -pycompilation = sm.external.import_module('pycompilation') +from sympy.utilities._compilation import compilation as pycompilation def building_docs(): @@ -319,7 +320,8 @@ def ufuncify_matrix(args, expr, const=None, tmp_dir=None, parallel=False): files[d['file_prefix'] + '.pyx'] = _cython_template.format(**d) files[d['file_prefix'] + '_setup.py'] = _setup_template.format(**d) - if pycompilation: + try: + logging.info('opty:Compiling with sympy.utilities._compilation module.') sources = [ (d['file_prefix'] + '_c.c', files[d['file_prefix'] + '_c.c']), (d['file_prefix'] + '.pyx', files[d['file_prefix'] + '.pyx']), @@ -327,12 +329,18 @@ def ufuncify_matrix(args, expr, const=None, tmp_dir=None, parallel=False): options = [] if parallel: - options += ['openmp'] - - cython_module = pycompilation.compile_link_import_strings( - sources, options=options, std='c99', - include_dirs=[np.get_include()], build_dir=codedir) - else: + options += ['-fopenmp'] + + cython_module, info = pycompilation.compile_link_import_strings( + sources, + compile_kwargs={ + "std": 'c99', + "include_dirs": [np.get_include()], + 'flags': options}, + link_kwargs={'flags': options}, + build_dir=codedir) + except: + logging.info("opty:Compiling with Opty's compilation functions.") workingdir = os.getcwd() os.chdir(codedir) From 257d59fb98ee13f3f4a6f280ffd0d16ecdb6c6cf Mon Sep 17 00:00:00 2001 From: "Jason K. Moore" Date: Tue, 5 Sep 2023 11:37:07 +0200 Subject: [PATCH 05/15] Remove the pycomplilation CI test. --- .github/workflows/run-tests.yml | 2 -- 1 file changed, 2 deletions(-) diff --git a/.github/workflows/run-tests.yml b/.github/workflows/run-tests.yml index 4e6ce7f6..46f43b75 100644 --- a/.github/workflows/run-tests.yml +++ b/.github/workflows/run-tests.yml @@ -41,6 +41,4 @@ jobs: python setup.py install python -c "import opty" py.test --cov=opty opty/ - python -m pip install pycompilation - py.test --cov=opty opty/ cd docs && make clean && make html && cd .. From d9e2bcd772f4b86d36cf589e381933e07cc2568b Mon Sep 17 00:00:00 2001 From: "Jason K. Moore" Date: Sun, 26 Oct 2025 06:33:49 +0100 Subject: [PATCH 06/15] Removed remaining merge cruft. --- opty/utils.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/opty/utils.py b/opty/utils.py index ea765b06..b89ffccf 100644 --- a/opty/utils.py +++ b/opty/utils.py @@ -808,7 +808,7 @@ def ufuncify_matrix(args, expr, const=None, tmp_dir=None, parallel=False, # so I don't delete the directory on Windows. if sys.platform != "win32": shutil.rmtree(codedir) -======= + sys.path.append(codedir) for filename, code in files.items(): with open(filename, 'w') as f: @@ -862,7 +862,6 @@ def ufuncify_matrix(args, expr, const=None, tmp_dir=None, parallel=False, # so I don't delete the directory on Windows. if sys.platform != "win32": shutil.rmtree(codedir) ->>>>>>> master return getattr(cython_module, d['routine_name'] + '_loop') From 5b570e9cf10c7d60ed151d342ca036faadeab214 Mon Sep 17 00:00:00 2001 From: "Jason K. Moore" Date: Sun, 26 Oct 2025 06:38:32 +0100 Subject: [PATCH 07/15] Actually corrected the merge conflict, missed a big chunk. --- opty/utils.py | 96 +++++++++++++++++++++------------------------------ 1 file changed, 40 insertions(+), 56 deletions(-) diff --git a/opty/utils.py b/opty/utils.py index b89ffccf..675cc052 100644 --- a/opty/utils.py +++ b/opty/utils.py @@ -799,70 +799,54 @@ def ufuncify_matrix(args, expr, const=None, tmp_dir=None, parallel=False, subprocess.call(cmd, stderr=subprocess.STDOUT, stdout=subprocess.PIPE) cython_module = importlib.import_module(d['file_prefix']) + # NOTE : This may not always work on Windows (seems to be dependent + # on how Python is invoked). There is explanation in + # https://github.com/python/cpython/issues/105312 but it is not + # crystal clear what the solution is. + # device_encoding() takes: 0: stdin, 1: stdout, 2: stderr + # device_encoding() always returns UTF-8 on Unix but will return + # different encodings on Windows and only if it is "attached to a + # terminal". + # locale.getencoding() tries to guess the encoding + if sys.platform == 'win32': + try: # Python >= 3.11 + encoding = locale.getencoding() + except AttributeError: # Python < 3.11 + encoding = locale.getlocale()[1] + else: + encoding = None + try: + proc = subprocess.run(cmd, capture_output=True, text=True, + encoding=encoding) + # On Windows this can raise a UnicodeDecodeError, but only in the + # subprocess. + except UnicodeDecodeError: + stdout = 'STDOUT not captured, decoding error.' + stderr = 'STDERR not captured, decoding error.' + else: + stdout = proc.stdout + stderr = proc.stderr + + if show_compile_output: + print(stdout) + print(stderr) + try: + cython_module = importlib.import_module(d['file_prefix']) + except ImportError as error: + msg = ('Unable to import the compiled Cython module {}, ' + 'compilation likely failed. STDERR output from ' + 'compilation:\n{}') + raise ImportError(msg.format(d['file_prefix'], stderr)) from error finally: module_counter += 1 sys.path.remove(codedir) os.chdir(workingdir) if tmp_dir is None: - # NOTE : I can't figure out how to get rmtree to work on Windows, - # so I don't delete the directory on Windows. + # NOTE : I can't figure out how to get rmtree to work on + # Windows, so I don't delete the directory on Windows. if sys.platform != "win32": shutil.rmtree(codedir) - sys.path.append(codedir) - for filename, code in files.items(): - with open(filename, 'w') as f: - f.write(code) - cmd = [sys.executable, d['file_prefix'] + '_setup.py', 'build_ext', - '--inplace'] - # NOTE : This may not always work on Windows (seems to be dependent on - # how Python is invoked). There is explanation in - # https://github.com/python/cpython/issues/105312 but it is not crystal - # clear what the solution is. - # device_encoding() takes: 0: stdin, 1: stdout, 2: stderr - # device_encoding() always returns UTF-8 on Unix but will return - # different encodings on Windows and only if it is "attached to a - # terminal". - # locale.getencoding() tries to guess the encoding - if sys.platform == 'win32': - try: # Python >= 3.11 - encoding = locale.getencoding() - except AttributeError: # Python < 3.11 - encoding = locale.getlocale()[1] - else: - encoding = None - try: - proc = subprocess.run(cmd, capture_output=True, text=True, - encoding=encoding) - # On Windows this can raise a UnicodeDecodeError, but only in the - # subprocess. - except UnicodeDecodeError: - stdout = 'STDOUT not captured, decoding error.' - stderr = 'STDERR not captured, decoding error.' - else: - stdout = proc.stdout - stderr = proc.stderr - - if show_compile_output: - print(stdout) - print(stderr) - try: - cython_module = importlib.import_module(d['file_prefix']) - except ImportError as error: - msg = ('Unable to import the compiled Cython module {}, ' - 'compilation likely failed. STDERR output from ' - 'compilation:\n{}') - raise ImportError(msg.format(d['file_prefix'], stderr)) from error - finally: - module_counter += 1 - sys.path.remove(codedir) - os.chdir(workingdir) - if tmp_dir is None: - # NOTE : I can't figure out how to get rmtree to work on Windows, - # so I don't delete the directory on Windows. - if sys.platform != "win32": - shutil.rmtree(codedir) - return getattr(cython_module, d['routine_name'] + '_loop') From ca32b5f8c143894f96732073da4a3abe823c6f4a Mon Sep 17 00:00:00 2001 From: "Jason K. Moore" Date: Sun, 26 Oct 2025 07:00:50 +0100 Subject: [PATCH 08/15] SymPy compilation raises a ModuleNotFoundError instead of an ImportError. --- opty/tests/test_utils.py | 17 ++++++++++++----- 1 file changed, 12 insertions(+), 5 deletions(-) diff --git a/opty/tests/test_utils.py b/opty/tests/test_utils.py index 2056e931..28b6ab28 100644 --- a/opty/tests/test_utils.py +++ b/opty/tests/test_utils.py @@ -267,10 +267,10 @@ def test_ufuncify_matrix(): n = 10000 - a_vals = np.random.random(n) - b_vals = np.random.random(n) + a_vals = np.abs(np.random.random(n)) + b_vals = np.abs(np.random.random(n)) c_vals = np.abs(np.random.random(n)) - c_val = np.random.random(1)[0] + c_val = np.abs(np.random.random(1))[0] def eval_matrix_loop_numpy(a_vals, b_vals, c_vals): """Since the number of matrix elements are typically much smaller @@ -330,10 +330,17 @@ def eval_matrix_loop_numpy(a_vals, b_vals, c_vals): # NOTE : Will not compile due to d_{badsym} being an invalid C variable # name. - with pytest.raises(ImportError) as error: + # opty's compilation will raise an ImportError + #with pytest.raises(ImportError) as error: + #utils.ufuncify_matrix((a, b, d), sym_mat.xreplace({c: d})) +# + #assert error.match("double d_{badsym}") + + # sympy.utilities._compilation raises ModuleNotFoundError + with pytest.raises(ModuleNotFoundError) as error: utils.ufuncify_matrix((a, b, d), sym_mat.xreplace({c: d})) - assert error.match("double d_{badsym}") + assert error.match("No module named") def test_substitute_matrix(): From fa182ed65ce2d0d8cf92a7ab6b1e550a3097370a Mon Sep 17 00:00:00 2001 From: "Jason K. Moore" Date: Sun, 26 Oct 2025 07:32:04 +0100 Subject: [PATCH 09/15] Add temporary error to see if sympy's compilation works at all. --- opty/utils.py | 1 + 1 file changed, 1 insertion(+) diff --git a/opty/utils.py b/opty/utils.py index 675cc052..a99a4bd9 100644 --- a/opty/utils.py +++ b/opty/utils.py @@ -785,6 +785,7 @@ def ufuncify_matrix(args, expr, const=None, tmp_dir=None, parallel=False, link_kwargs={'flags': options}, build_dir=codedir) except: + raise RuntimeError("SymPy's compilation failed.") logging.info("opty:Compiling with Opty's compilation functions.") workingdir = os.getcwd() os.chdir(codedir) From 175630dfe1c2b93ce2a4bc055876194abb359114 Mon Sep 17 00:00:00 2001 From: "Jason K. Moore" Date: Sun, 26 Oct 2025 07:43:43 +0100 Subject: [PATCH 10/15] Only run sympys compilation to see actual errors. --- opty/utils.py | 161 +++++++++++++++++++++++++------------------------- 1 file changed, 80 insertions(+), 81 deletions(-) diff --git a/opty/utils.py b/opty/utils.py index a99a4bd9..995460a3 100644 --- a/opty/utils.py +++ b/opty/utils.py @@ -765,88 +765,87 @@ def ufuncify_matrix(args, expr, const=None, tmp_dir=None, parallel=False, files[d['file_prefix'] + '.pyx'] = _cython_template.format(**d) files[d['file_prefix'] + '_setup.py'] = _setup_template.format(**d) - try: - logger.info('opty:Compiling with sympy.utilities._compilation module.') - sources = [ - (d['file_prefix'] + '_c.c', files[d['file_prefix'] + '_c.c']), - (d['file_prefix'] + '.pyx', files[d['file_prefix'] + '.pyx']), - ] - - options = [] - if parallel: - options += ['-fopenmp'] - - cython_module, info = pycompilation.compile_link_import_strings( - sources, - compile_kwargs={ - "std": 'c99', - "include_dirs": [np.get_include()], - 'flags': options}, - link_kwargs={'flags': options}, - build_dir=codedir) - except: - raise RuntimeError("SymPy's compilation failed.") - logging.info("opty:Compiling with Opty's compilation functions.") - workingdir = os.getcwd() - os.chdir(codedir) + logger.info('opty:Compiling with sympy.utilities._compilation module.') + sources = [ + (d['file_prefix'] + '_c.c', files[d['file_prefix'] + '_c.c']), + (d['file_prefix'] + '.pyx', files[d['file_prefix'] + '.pyx']), + ] - try: - sys.path.append(codedir) - for filename, code in files.items(): - with open(filename, 'w') as f: - f.write(code) - cmd = [sys.executable, d['file_prefix'] + '_setup.py', 'build_ext', - '--inplace'] - subprocess.call(cmd, stderr=subprocess.STDOUT, - stdout=subprocess.PIPE) - cython_module = importlib.import_module(d['file_prefix']) - # NOTE : This may not always work on Windows (seems to be dependent - # on how Python is invoked). There is explanation in - # https://github.com/python/cpython/issues/105312 but it is not - # crystal clear what the solution is. - # device_encoding() takes: 0: stdin, 1: stdout, 2: stderr - # device_encoding() always returns UTF-8 on Unix but will return - # different encodings on Windows and only if it is "attached to a - # terminal". - # locale.getencoding() tries to guess the encoding - if sys.platform == 'win32': - try: # Python >= 3.11 - encoding = locale.getencoding() - except AttributeError: # Python < 3.11 - encoding = locale.getlocale()[1] - else: - encoding = None - try: - proc = subprocess.run(cmd, capture_output=True, text=True, - encoding=encoding) - # On Windows this can raise a UnicodeDecodeError, but only in the - # subprocess. - except UnicodeDecodeError: - stdout = 'STDOUT not captured, decoding error.' - stderr = 'STDERR not captured, decoding error.' - else: - stdout = proc.stdout - stderr = proc.stderr - - if show_compile_output: - print(stdout) - print(stderr) - try: - cython_module = importlib.import_module(d['file_prefix']) - except ImportError as error: - msg = ('Unable to import the compiled Cython module {}, ' - 'compilation likely failed. STDERR output from ' - 'compilation:\n{}') - raise ImportError(msg.format(d['file_prefix'], stderr)) from error - finally: - module_counter += 1 - sys.path.remove(codedir) - os.chdir(workingdir) - if tmp_dir is None: - # NOTE : I can't figure out how to get rmtree to work on - # Windows, so I don't delete the directory on Windows. - if sys.platform != "win32": - shutil.rmtree(codedir) + options = [] + if parallel: + options += ['-fopenmp'] + + cython_module, info = pycompilation.compile_link_import_strings( + sources, + compile_kwargs={ + "std": 'c99', + "include_dirs": [np.get_include()], + 'flags': options}, + link_kwargs={'flags': options}, + build_dir=codedir) + + ###raise RuntimeError("SymPy's compilation failed.") + ###logging.info("opty:Compiling with Opty's compilation functions.") + ###workingdir = os.getcwd() + ###os.chdir(codedir) +### + ###try: + ###sys.path.append(codedir) + ###for filename, code in files.items(): + ###with open(filename, 'w') as f: + ###f.write(code) + ###cmd = [sys.executable, d['file_prefix'] + '_setup.py', 'build_ext', + ###'--inplace'] + ###subprocess.call(cmd, stderr=subprocess.STDOUT, + ###stdout=subprocess.PIPE) + ###cython_module = importlib.import_module(d['file_prefix']) + #### NOTE : This may not always work on Windows (seems to be dependent + #### on how Python is invoked). There is explanation in + #### https://github.com/python/cpython/issues/105312 but it is not + #### crystal clear what the solution is. + #### device_encoding() takes: 0: stdin, 1: stdout, 2: stderr + #### device_encoding() always returns UTF-8 on Unix but will return + #### different encodings on Windows and only if it is "attached to a + #### terminal". + #### locale.getencoding() tries to guess the encoding + ###if sys.platform == 'win32': + ###try: # Python >= 3.11 + ###encoding = locale.getencoding() + ###except AttributeError: # Python < 3.11 + ###encoding = locale.getlocale()[1] + ###else: + ###encoding = None + ###try: + ###proc = subprocess.run(cmd, capture_output=True, text=True, + ###encoding=encoding) + #### On Windows this can raise a UnicodeDecodeError, but only in the + #### subprocess. + ###except UnicodeDecodeError: + ###stdout = 'STDOUT not captured, decoding error.' + ###stderr = 'STDERR not captured, decoding error.' + ###else: + ###stdout = proc.stdout + ###stderr = proc.stderr +### + ###if show_compile_output: + ###print(stdout) + ###print(stderr) + ###try: + ###cython_module = importlib.import_module(d['file_prefix']) + ###except ImportError as error: + ###msg = ('Unable to import the compiled Cython module {}, ' + ###'compilation likely failed. STDERR output from ' + ###'compilation:\n{}') + ###raise ImportError(msg.format(d['file_prefix'], stderr)) from error + ###finally: + ###module_counter += 1 + ###sys.path.remove(codedir) + ###os.chdir(workingdir) + ###if tmp_dir is None: + #### NOTE : I can't figure out how to get rmtree to work on + #### Windows, so I don't delete the directory on Windows. + ###if sys.platform != "win32": + ###shutil.rmtree(codedir) return getattr(cython_module, d['routine_name'] + '_loop') From 2f044e18c46aa2bcd30f8977fb9d16b25bc7c528 Mon Sep 17 00:00:00 2001 From: "Jason K. Moore" Date: Sun, 26 Oct 2025 12:35:33 +0100 Subject: [PATCH 11/15] Use gnu99 compiler to get math symbols. --- opty/tests/test_utils.py | 11 +++-------- opty/utils.py | 5 +++-- 2 files changed, 6 insertions(+), 10 deletions(-) diff --git a/opty/tests/test_utils.py b/opty/tests/test_utils.py index 28b6ab28..40729826 100644 --- a/opty/tests/test_utils.py +++ b/opty/tests/test_utils.py @@ -4,6 +4,7 @@ import numpy as np from numpy import testing import sympy as sym +from sympy.utilities._compilation.util import CompileError try: from scipy import sparse except ImportError: @@ -331,16 +332,10 @@ def eval_matrix_loop_numpy(a_vals, b_vals, c_vals): # NOTE : Will not compile due to d_{badsym} being an invalid C variable # name. # opty's compilation will raise an ImportError - #with pytest.raises(ImportError) as error: - #utils.ufuncify_matrix((a, b, d), sym_mat.xreplace({c: d})) -# - #assert error.match("double d_{badsym}") - - # sympy.utilities._compilation raises ModuleNotFoundError - with pytest.raises(ModuleNotFoundError) as error: + with pytest.raises((ImportError, CompileError)) as error: utils.ufuncify_matrix((a, b, d), sym_mat.xreplace({c: d})) - assert error.match("No module named") + assert error.match("double d_{badsym}") def test_substitute_matrix(): diff --git a/opty/utils.py b/opty/utils.py index 995460a3..5461d51e 100644 --- a/opty/utils.py +++ b/opty/utils.py @@ -19,6 +19,7 @@ import numpy as np import sympy as sm +from sympy.utilities._compilation import compilation as pycompilation import sympy.physics.mechanics as me from sympy.utilities.iterables import numbered_symbols from sympy.printing.c import C99CodePrinter @@ -218,7 +219,6 @@ def add_to_cache(node): return (list(required_replacements.items()), [replaced_jacobian.xreplace(unrequired_replacements)]) -from sympy.utilities._compilation import compilation as pycompilation def building_docs(): @@ -778,7 +778,8 @@ def ufuncify_matrix(args, expr, const=None, tmp_dir=None, parallel=False, cython_module, info = pycompilation.compile_link_import_strings( sources, compile_kwargs={ - "std": 'c99', + # NOTE : Failed to recognize M_PI if the std is c99, but gnu adds # it. + "std": 'gnu99', "include_dirs": [np.get_include()], 'flags': options}, link_kwargs={'flags': options}, From 5dadc27fddeeecd88371e481c69ed7fa599f9286 Mon Sep 17 00:00:00 2001 From: "Jason K. Moore" Date: Sun, 26 Oct 2025 13:00:52 +0100 Subject: [PATCH 12/15] Use clang if on mac. --- opty/utils.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/opty/utils.py b/opty/utils.py index 5461d51e..ff38ef44 100644 --- a/opty/utils.py +++ b/opty/utils.py @@ -781,7 +781,8 @@ def ufuncify_matrix(args, expr, const=None, tmp_dir=None, parallel=False, # NOTE : Failed to recognize M_PI if the std is c99, but gnu adds # it. "std": 'gnu99', "include_dirs": [np.get_include()], - 'flags': options}, + 'flags': options, + 'preferred_vendor': 'llvm' if os.name == 'darwin' else 'gnu'}, link_kwargs={'flags': options}, build_dir=codedir) From 228c68adea6e06665f612b43eb1f237eb2b93d2f Mon Sep 17 00:00:00 2001 From: "Jason K. Moore" Date: Sun, 26 Oct 2025 13:09:00 +0100 Subject: [PATCH 13/15] Check sys.platform not os.name. --- opty/utils.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/opty/utils.py b/opty/utils.py index ff38ef44..270e3bae 100644 --- a/opty/utils.py +++ b/opty/utils.py @@ -778,11 +778,13 @@ def ufuncify_matrix(args, expr, const=None, tmp_dir=None, parallel=False, cython_module, info = pycompilation.compile_link_import_strings( sources, compile_kwargs={ - # NOTE : Failed to recognize M_PI if the std is c99, but gnu adds # it. + # NOTE : Failed to recognize M_PI if the std is c99, so gnu99. + # std dialects: + # https://gcc.gnu.org/onlinedocs/gcc/C-Dialect-Options.html "std": 'gnu99', "include_dirs": [np.get_include()], 'flags': options, - 'preferred_vendor': 'llvm' if os.name == 'darwin' else 'gnu'}, + 'preferred_vendor': 'llvm' if sys.platform == 'darwin' else 'gnu'}, link_kwargs={'flags': options}, build_dir=codedir) From 1a4b3043dc4e4754113c83d24d71554eeec839e8 Mon Sep 17 00:00:00 2001 From: "Jason K. Moore" Date: Sun, 26 Oct 2025 13:19:06 +0100 Subject: [PATCH 14/15] Don't use gnu99 with clang. --- opty/utils.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/opty/utils.py b/opty/utils.py index 270e3bae..71dfaca5 100644 --- a/opty/utils.py +++ b/opty/utils.py @@ -781,7 +781,7 @@ def ufuncify_matrix(args, expr, const=None, tmp_dir=None, parallel=False, # NOTE : Failed to recognize M_PI if the std is c99, so gnu99. # std dialects: # https://gcc.gnu.org/onlinedocs/gcc/C-Dialect-Options.html - "std": 'gnu99', + "std": 'c++11' if sys.platform == 'darwin' else 'gnu99', "include_dirs": [np.get_include()], 'flags': options, 'preferred_vendor': 'llvm' if sys.platform == 'darwin' else 'gnu'}, From 103270fd6a3d55a899a7d3a820862cbf2fc8abaf Mon Sep 17 00:00:00 2001 From: "Jason K. Moore" Date: Sun, 26 Oct 2025 13:29:04 +0100 Subject: [PATCH 15/15] Try c99 with clang. --- opty/utils.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/opty/utils.py b/opty/utils.py index 71dfaca5..e58e789c 100644 --- a/opty/utils.py +++ b/opty/utils.py @@ -781,7 +781,7 @@ def ufuncify_matrix(args, expr, const=None, tmp_dir=None, parallel=False, # NOTE : Failed to recognize M_PI if the std is c99, so gnu99. # std dialects: # https://gcc.gnu.org/onlinedocs/gcc/C-Dialect-Options.html - "std": 'c++11' if sys.platform == 'darwin' else 'gnu99', + "std": 'c99' if sys.platform == 'darwin' else 'gnu99', "include_dirs": [np.get_include()], 'flags': options, 'preferred_vendor': 'llvm' if sys.platform == 'darwin' else 'gnu'},