Spaces:
Sleeping
Sleeping
import os | |
import tempfile | |
from sympy.core.symbol import (Symbol, symbols) | |
from sympy.codegen.ast import ( | |
Assignment, Print, Declaration, FunctionDefinition, Return, real, | |
FunctionCall, Variable, Element, integer | |
) | |
from sympy.codegen.fnodes import ( | |
allocatable, ArrayConstructor, isign, dsign, cmplx, kind, literal_dp, | |
Program, Module, use, Subroutine, dimension, assumed_extent, ImpliedDoLoop, | |
intent_out, size, Do, SubroutineCall, sum_, array, bind_C | |
) | |
from sympy.codegen.futils import render_as_module | |
from sympy.core.expr import unchanged | |
from sympy.external import import_module | |
from sympy.printing.codeprinter import fcode | |
from sympy.utilities._compilation import has_fortran, compile_run_strings, compile_link_import_strings | |
from sympy.utilities._compilation.util import may_xfail | |
from sympy.testing.pytest import skip, XFAIL | |
cython = import_module('cython') | |
np = import_module('numpy') | |
def test_size(): | |
x = Symbol('x', real=True) | |
sx = size(x) | |
assert fcode(sx, source_format='free') == 'size(x)' | |
def test_size_assumed_shape(): | |
if not has_fortran(): | |
skip("No fortran compiler found.") | |
a = Symbol('a', real=True) | |
body = [Return((sum_(a**2)/size(a))**.5)] | |
arr = array(a, dim=[':'], intent='in') | |
fd = FunctionDefinition(real, 'rms', [arr], body) | |
render_as_module([fd], 'mod_rms') | |
(stdout, stderr), info = compile_run_strings([ | |
('rms.f90', render_as_module([fd], 'mod_rms')), | |
('main.f90', ( | |
'program myprog\n' | |
'use mod_rms, only: rms\n' | |
'real*8, dimension(4), parameter :: x = [4, 2, 2, 2]\n' | |
'print *, dsqrt(7d0) - rms(x)\n' | |
'end program\n' | |
)) | |
], clean=True) | |
assert '0.00000' in stdout | |
assert stderr == '' | |
assert info['exit_status'] == os.EX_OK | |
# https://github.com/sympy/sympy/issues/20265 | |
def test_ImpliedDoLoop(): | |
if not has_fortran(): | |
skip("No fortran compiler found.") | |
a, i = symbols('a i', integer=True) | |
idl = ImpliedDoLoop(i**3, i, -3, 3, 2) | |
ac = ArrayConstructor([-28, idl, 28]) | |
a = array(a, dim=[':'], attrs=[allocatable]) | |
prog = Program('idlprog', [ | |
a.as_Declaration(), | |
Assignment(a, ac), | |
Print([a]) | |
]) | |
fsrc = fcode(prog, standard=2003, source_format='free') | |
(stdout, stderr), info = compile_run_strings([('main.f90', fsrc)], clean=True) | |
for numstr in '-28 -27 -1 1 27 28'.split(): | |
assert numstr in stdout | |
assert stderr == '' | |
assert info['exit_status'] == os.EX_OK | |
def test_Program(): | |
x = Symbol('x', real=True) | |
vx = Variable.deduced(x, 42) | |
decl = Declaration(vx) | |
prnt = Print([x, x+1]) | |
prog = Program('foo', [decl, prnt]) | |
if not has_fortran(): | |
skip("No fortran compiler found.") | |
(stdout, stderr), info = compile_run_strings([('main.f90', fcode(prog, standard=90))], clean=True) | |
assert '42' in stdout | |
assert '43' in stdout | |
assert stderr == '' | |
assert info['exit_status'] == os.EX_OK | |
def test_Module(): | |
x = Symbol('x', real=True) | |
v_x = Variable.deduced(x) | |
sq = FunctionDefinition(real, 'sqr', [v_x], [Return(x**2)]) | |
mod_sq = Module('mod_sq', [], [sq]) | |
sq_call = FunctionCall('sqr', [42.]) | |
prg_sq = Program('foobar', [ | |
use('mod_sq', only=['sqr']), | |
Print(['"Square of 42 = "', sq_call]) | |
]) | |
if not has_fortran(): | |
skip("No fortran compiler found.") | |
(stdout, stderr), info = compile_run_strings([ | |
('mod_sq.f90', fcode(mod_sq, standard=90)), | |
('main.f90', fcode(prg_sq, standard=90)) | |
], clean=True) | |
assert '42' in stdout | |
assert str(42**2) in stdout | |
assert stderr == '' | |
# https://github.com/sympy/sympy/issues/20265 | |
def test_Subroutine(): | |
# Code to generate the subroutine in the example from | |
# http://www.fortran90.org/src/best-practices.html#arrays | |
r = Symbol('r', real=True) | |
i = Symbol('i', integer=True) | |
v_r = Variable.deduced(r, attrs=(dimension(assumed_extent), intent_out)) | |
v_i = Variable.deduced(i) | |
v_n = Variable('n', integer) | |
do_loop = Do([ | |
Assignment(Element(r, [i]), literal_dp(1)/i**2) | |
], i, 1, v_n) | |
sub = Subroutine("f", [v_r], [ | |
Declaration(v_n), | |
Declaration(v_i), | |
Assignment(v_n, size(r)), | |
do_loop | |
]) | |
x = Symbol('x', real=True) | |
v_x3 = Variable.deduced(x, attrs=[dimension(3)]) | |
mod = Module('mymod', definitions=[sub]) | |
prog = Program('foo', [ | |
use(mod, only=[sub]), | |
Declaration(v_x3), | |
SubroutineCall(sub, [v_x3]), | |
Print([sum_(v_x3), v_x3]) | |
]) | |
if not has_fortran(): | |
skip("No fortran compiler found.") | |
(stdout, stderr), info = compile_run_strings([ | |
('a.f90', fcode(mod, standard=90)), | |
('b.f90', fcode(prog, standard=90)) | |
], clean=True) | |
ref = [1.0/i**2 for i in range(1, 4)] | |
assert str(sum(ref))[:-3] in stdout | |
for _ in ref: | |
assert str(_)[:-3] in stdout | |
assert stderr == '' | |
def test_isign(): | |
x = Symbol('x', integer=True) | |
assert unchanged(isign, 1, x) | |
assert fcode(isign(1, x), standard=95, source_format='free') == 'isign(1, x)' | |
def test_dsign(): | |
x = Symbol('x') | |
assert unchanged(dsign, 1, x) | |
assert fcode(dsign(literal_dp(1), x), standard=95, source_format='free') == 'dsign(1d0, x)' | |
def test_cmplx(): | |
x = Symbol('x') | |
assert unchanged(cmplx, 1, x) | |
def test_kind(): | |
x = Symbol('x') | |
assert unchanged(kind, x) | |
def test_literal_dp(): | |
assert fcode(literal_dp(0), source_format='free') == '0d0' | |
def test_bind_C(): | |
if not has_fortran(): | |
skip("No fortran compiler found.") | |
if not cython: | |
skip("Cython not found.") | |
if not np: | |
skip("NumPy not found.") | |
a = Symbol('a', real=True) | |
s = Symbol('s', integer=True) | |
body = [Return((sum_(a**2)/s)**.5)] | |
arr = array(a, dim=[s], intent='in') | |
fd = FunctionDefinition(real, 'rms', [arr, s], body, attrs=[bind_C('rms')]) | |
f_mod = render_as_module([fd], 'mod_rms') | |
with tempfile.TemporaryDirectory() as folder: | |
mod, info = compile_link_import_strings([ | |
('rms.f90', f_mod), | |
('_rms.pyx', ( | |
"#cython: language_level={}\n".format("3") + | |
"cdef extern double rms(double*, int*)\n" | |
"def py_rms(double[::1] x):\n" | |
" cdef int s = x.size\n" | |
" return rms(&x[0], &s)\n")) | |
], build_dir=folder) | |
assert abs(mod.py_rms(np.array([2., 4., 2., 2.])) - 7**0.5) < 1e-14 | |