代码生成#

此模块提供从SymPy表达式直接生成可编译代码的功能。这个 codegen function是SymPy中代码生成功能的用户界面。下面给出了一些用户可能要用到的框架的实现细节。

备注

这个 codegen callable不是自动出现在sympy命名空间中的,要使用它,必须首先执行

>>> from sympy.utilities.codegen import codegen

实施细节#

这里我们展示内部结构中最重要的部分,因为高级用户可能希望直接使用它,例如通过为特定应用程序的代码生成器子类化。 很可能您希望使用上面介绍的codegen()函数。

基本假设:

  • 必须将Fortran/例程转换为描述通用数据结构的C/例程。。。代码。此数据结构涵盖了一种或多种受支持语言的所有功能。

  • CodeGen类的后代将多个例程实例转换为可编译代码。每个派生类都转换为一种特定的语言。

  • 在许多情况下,人们需要一个简单的工作流程。最后一部分中的友好函数是例程/CodeGen基础上的一个简单api。它们更容易使用,但功能较弱。

例行程序#

例程类是codegen模块中非常重要的一部分。将codegen实用程序视为将数学表达式转换为编程语言中的一组语句的转换器,例程实例负责提取和存储有关如何将数学封装在函数调用中的信息。因此,由例程构造函数决定例程需要哪些参数以及是否应该有返回值。

API引用#

module for generating C, C++, Fortran77, Fortran90, Julia, Rust and Octave/Matlab routines that evaluate SymPy expressions. This module is work in progress. Only the milestones with a '+' character in the list below have been completed.

---怎么样sympy.utilities.codegen不同于sympy.printing.ccode?---

We considered the idea to extend the printing routines for SymPy functions in such a way that it prints complete compilable code, but this leads to a few unsurmountable issues that can only be tackled with dedicated code generator:

  • 对于C,需要代码和头文件,而打印例程只生成一个字符串。这个代码生成器可以扩展为支持f2py的.pyf文件。

  • SymPy函数不涉及编程技术问题,例如输入、输出和输入输出参数。其他示例是连续或非连续数组,包括其他库(如gsl或其他库)的头。

  • It is highly interesting to evaluate several SymPy functions in one C routine, eventually sharing common intermediate results with the help of the cse routine. This is more than just printing.

  • 从编程的角度来看,带有常量的表达式应该尽可能多地在代码生成器中求值。这与打印不同。

---基本假设---

  • 必须将Fortran/例程转换为描述通用数据结构的C/例程。。。代码。此数据结构涵盖了一种或多种受支持语言的所有功能。

  • CodeGen类的后代将多个例程实例转换为可编译代码。每个派生类都转换为一种特定的语言。

  • 在许多情况下,人们需要一个简单的工作流程。最后一部分中的友好函数是例程/CodeGen基础上的一个简单api。它们更容易使用,但功能较弱。

---里程碑---

  • 第一个使用标量输入参数的工作版本,生成C代码,测试

  • 友好的函数,比严格的例程/CodeGen工作流更容易使用。

  • 整数和实数作为输入和输出

  • 输出参数

  • InputOutput参数

  • 正确排序输入/输出参数

  • 连续数组参数(numpy矩阵)

  • 同时为f2py生成.pyf代码(在autowrap模块中)

  • 分离常数,并预先以双重精度评估它们

  • Fortran 90语言

  • 倍频程/Matlab

  • 公共子表达式消除

  • 生成代码中的用户定义注释

  • 可选的额外的包含行,用于可以计算特殊函数的库/对象

  • 测试其他C编译器和库:gcc,tcc,libtcc,gcc+gsl。。。

  • Contiguous array arguments (SymPy matrices)

  • Non-contiguous array arguments (SymPy matrices)

  • ccode must raise an error when it encounters something that cannot be translated into c. ccode(integrate(sin(x)/x, x)) does not make sense.

  • 复数作为输入和输出

  • 默认的复杂数据类型

  • 在头中包含额外的信息:日期、用户、主机名、sha1散列、。。。

  • Fortran语言77

  • C++

  • Python

  • 朱莉娅

  • Rust

class sympy.utilities.codegen.Argument(name, datatype=None, dimensions=None, precision=None)[源代码]#

抽象参数数据结构:名称和数据类型。

这种结构在下面的后代中得到了改进。

class sympy.utilities.codegen.CCodeGen(project='project', printer=None, preprocessor_statements=None, cse=False)[源代码]#

C代码生成器。

从CodeGen继承的.write()方法将分别输出代码文件和接口文件<prefix>.c和<prefix>.h。

dump_c(routines, f, prefix, header=True, empty=True)[源代码]#

通过调用特定于语言的方法来编写代码。

生成的文件包含低级代码中例程的所有定义,并在适当的情况下引用头文件。

参数:

日常工作 :列表

例程实例的列表。

f :文件类型

文件的写入位置。

前缀 :字符串

文件名前缀,用于引用正确的头文件。只使用前缀的基名称。

页眉 :bool,可选

如果为True,则在每个源文件的顶部包含头注释。 [默认值:True]

空的 :bool,可选

如果为True,则包含空行来构造源文件。 [默认值:True]

dump_h(routines, f, prefix, header=True, empty=True)[源代码]#

写入C头文件。

此文件包含所有函数声明。

参数:

日常工作 :列表

例程实例的列表。

f :文件类型

文件的写入位置。

前缀 :字符串

文件名前缀,用于构造include保护。只使用前缀的基名称。

页眉 :bool,可选

如果为True,则在每个源文件的顶部包含头注释。 [默认值:True]

空的 :bool,可选

如果为True,则包含空行来构造源文件。 [默认值:True]

get_prototype(routine)[源代码]#

返回例程的函数原型的字符串。

如果例程有多个结果对象,则引发CodeGenerError。

参见:https://en.wikipedia.org/wiki/Function_原型

class sympy.utilities.codegen.CodeGen(project='project', cse=False)[源代码]#

代码生成器的抽象类。

dump_code(routines, f, prefix, header=True, empty=True)[源代码]#

通过调用特定于语言的方法来编写代码。

生成的文件包含低级代码中例程的所有定义,并在适当的情况下引用头文件。

参数:

日常工作 :列表

例程实例的列表。

f :文件类型

文件的写入位置。

前缀 :字符串

文件名前缀,用于引用正确的头文件。只使用前缀的基名称。

页眉 :bool,可选

如果为True,则在每个源文件的顶部包含头注释。 [默认值:True]

空的 :bool,可选

如果为True,则包含空行来构造源文件。 [默认值:True]

routine(name, expr, argument_sequence=None, global_vars=None)[源代码]#

创建适合此语言的例程对象。

这种实现至少适用于C/Fortran。如有必要,子类可以重写它。

这里,我们假设最多有一个返回值(l值),它必须是标量。其他输出是输出参数(例如,指针在右侧或通过引用传递)。矩阵总是通过OutputArguments返回。如果 argument_sequence 如果为“无”,则参数将按字母顺序排序,但所有输入参数都将首先排序,然后输出参数和InOutArguments。

write(routines, prefix, to_files=False, header=True, empty=True)[源代码]#

编写给定例程的所有源代码文件。

生成的源将作为(文件名、内容)元组的列表返回,或写入文件(见下文)。每个文件名由给定的前缀组成,并附加适当的扩展名。

参数:

日常工作 :列表

要写入的例程实例的列表

前缀 :字符串

输出文件的前缀

to_files :bool,可选

如果为True,则输出将写入文件。否则,将返回(filename,contents)元组的列表。 [默认值:False]

页眉 :bool,可选

如果为True,则在每个源文件的顶部包含头注释。 [默认值:True]

空的 :bool,可选

如果为True,则包含空行来构造源文件。 [默认值:True]

class sympy.utilities.codegen.DataType(cname, fname, pyname, jlname, octname, rsname)[源代码]#

以不同语言保存特定数据类型的字符串。

class sympy.utilities.codegen.FCodeGen(project='project', printer=None)[源代码]#

Fortran 95代码生成器

从CodeGen继承的.write()方法将分别输出代码文件和接口文件<prefix>.f90和<prefix>.h。

dump_f95(routines, f, prefix, header=True, empty=True)[源代码]#

通过调用特定于语言的方法来编写代码。

生成的文件包含低级代码中例程的所有定义,并在适当的情况下引用头文件。

参数:

日常工作 :列表

例程实例的列表。

f :文件类型

文件的写入位置。

前缀 :字符串

文件名前缀,用于引用正确的头文件。只使用前缀的基名称。

页眉 :bool,可选

如果为True,则在每个源文件的顶部包含头注释。 [默认值:True]

空的 :bool,可选

如果为True,则包含空行来构造源文件。 [默认值:True]

dump_h(routines, f, prefix, header=True, empty=True)[源代码]#

将接口写入头文件。

此文件包含所有函数声明。

参数:

日常工作 :列表

例程实例的列表。

f :文件类型

文件的写入位置。

前缀 :字符串

文件名前缀。

页眉 :bool,可选

如果为True,则在每个源文件的顶部包含头注释。 [默认值:True]

空的 :bool,可选

如果为True,则包含空行来构造源文件。 [默认值:True]

get_interface(routine)[源代码]#

返回函数接口的字符串。

例程应该有一个结果对象,可以是None。如果例程有多个结果对象,则引发CodeGenerError。

参见:https://en.wikipedia.org/wiki/Function_原型

class sympy.utilities.codegen.JuliaCodeGen(project='project', printer=None)[源代码]#

Julia代码生成器。

从CodeGen继承的.write()方法将输出一个代码文件<prefix>.jl。

dump_jl(routines, f, prefix, header=True, empty=True)[源代码]#

通过调用特定于语言的方法来编写代码。

生成的文件包含低级代码中例程的所有定义,并在适当的情况下引用头文件。

参数:

日常工作 :列表

例程实例的列表。

f :文件类型

文件的写入位置。

前缀 :字符串

文件名前缀,用于引用正确的头文件。只使用前缀的基名称。

页眉 :bool,可选

如果为True,则在每个源文件的顶部包含头注释。 [默认值:True]

空的 :bool,可选

如果为True,则包含空行来构造源文件。 [默认值:True]

routine(name, expr, argument_sequence, global_vars)[源代码]#

专门为茱莉亚创造日常生活。

class sympy.utilities.codegen.OctaveCodeGen(project='project', printer=None)[源代码]#

倍频程代码的生成器。

从CodeGen继承的.write()方法将输出一个代码文件<prefix>.m。

m文件通常包含一个函数。函数名应该与文件名匹配 (prefix ). 如果你通过多次 name_expr 对,后者被假定为主函数访问的私有函数。

只应将输入传递给 argument_sequence :输出根据其在中的顺序排序 name_expr .

dump_m(routines, f, prefix, header=True, empty=True, inline=True)[源代码]#

通过调用特定于语言的方法来编写代码。

生成的文件包含低级代码中例程的所有定义,并在适当的情况下引用头文件。

参数:

日常工作 :列表

例程实例的列表。

f :文件类型

文件的写入位置。

前缀 :字符串

文件名前缀,用于引用正确的头文件。只使用前缀的基名称。

页眉 :bool,可选

如果为True,则在每个源文件的顶部包含头注释。 [默认值:True]

空的 :bool,可选

如果为True,则包含空行来构造源文件。 [默认值:True]

routine(name, expr, argument_sequence, global_vars)[源代码]#

为八度音阶专门的常规创作。

class sympy.utilities.codegen.OutputArgument(name, result_var, expr, datatype=None, dimensions=None, precision=None)[源代码]#

输出参数总是在例程中初始化。

class sympy.utilities.codegen.Result(expr, name=None, result_var=None, datatype=None, dimensions=None, precision=None)[源代码]#

返回值的表达式。

The name result is used to avoid conflicts with the reserved word "return" in the Python language. It is also shorter than ReturnValue.

它们可能需要也可能不需要在目的地中有一个名称(例如,“return(x*y)”可能会返回一个值而不会命名它)。

class sympy.utilities.codegen.Routine(name, arguments, results, local_vars, global_vars)[源代码]#

表达式集的求值例程的一般描述。

CodeGen类可以将该类的实例转换为特定语言的代码。例程规范涵盖了这些语言中的所有特性。当目标语言中不存在某些功能时,CodeGen部分必须引发异常。例如,在Python中可以有多个返回值,但在C或Fortran中则不行。另一个例子:Fortran和Python支持复数,而C不支持。

property result_variables#

返回OutputArgument、InOutArgument和Result的列表。

If return values are present, they are at the end of the list.

property variables#

返回例程中可能使用的所有变量的集合。

对于具有未命名返回值的例程,可能使用或不使用的虚拟对象将包含在集合中。

class sympy.utilities.codegen.RustCodeGen(project='project', printer=None)[源代码]#

生锈代码生成器。

从CodeGen继承的.write()方法将输出一个代码文件<prefix>.rs

dump_rs(routines, f, prefix, header=True, empty=True)[源代码]#

通过调用特定于语言的方法来编写代码。

生成的文件包含低级代码中例程的所有定义,并在适当的情况下引用头文件。

参数:

日常工作 :列表

例程实例的列表。

f :文件类型

文件的写入位置。

前缀 :字符串

文件名前缀,用于引用正确的头文件。只使用前缀的基名称。

页眉 :bool,可选

如果为True,则在每个源文件的顶部包含头注释。 [默认值:True]

空的 :bool,可选

如果为True,则包含空行来构造源文件。 [默认值:True]

get_prototype(routine)[源代码]#

返回例程的函数原型的字符串。

如果例程有多个结果对象,则引发CodeGenerError。

参见:https://en.wikipedia.org/wiki/Function_原型

routine(name, expr, argument_sequence, global_vars)[源代码]#

铁锈的专业常规制作。

sympy.utilities.codegen.codegen(name_expr, language=None, prefix=None, project='project', to_files=False, header=True, empty=True, argument_sequence=None, global_vars=None, standard=None, code_gen=None, printer=None)[源代码]#

为给定语言的表达式生成源代码。

参数:

name_expr :元组,或元组列表

单个(名称,表达式)元组或(名称,表达式)元组的列表。每个元组对应于一个例程。如果表达式是相等的(类相等的实例),则左侧被视为输出参数。如果expression是iterable,那么例程将有多个输出。

语言 :字符串,

指示源代码语言的字符串。这不区分大小写。目前支持'C'、'F95'和'Octave'Octave'生成的代码兼容Octave和Matlab。

前缀 :字符串,可选

包含源代码的文件名的前缀。将附加依赖于语言的后缀。如果省略,则使用第一个名称表达式元组的名称。

项目 :字符串,可选

项目名称,用于生成唯一的预处理器指令。 [默认值:“项目”]

to_files :bool,可选

如果为True,代码将以给定的前缀写入一个或多个文件,否则将返回具有这些文件名称和内容的字符串。 [默认值:False]

页眉 :bool,可选

如果为True,则在每个源文件的顶部写入头。 [默认值:True]

空的 :bool,可选

如果为True,则使用空行来构造代码。 [默认值:True]

argument_sequence :iterable,可选

按首选顺序的例程的参数序列。如果缺少必需的参数,则引发CodeGenerError。多余的参数在没有警告的情况下被使用。如果省略,参数将按字母顺序排序,但所有输入参数都要先排序,然后输出或输入输出参数。

global_vars :iterable,可选

例程使用的全局变量序列。这里列出的变量不会显示为函数参数。

standard : string, optional

code_gen : CodeGen instance, optional

CodeGen子类的实例。覆盖 language .

printer : Printer instance, optional

An instance of a Printer subclass.

实例

>>> from sympy.utilities.codegen import codegen
>>> from sympy.abc import x, y, z
>>> [(c_name, c_code), (h_name, c_header)] = codegen(
...     ("f", x+y*z), "C89", "test", header=False, empty=False)
>>> print(c_name)
test.c
>>> print(c_code)
#include "test.h"
#include <math.h>
double f(double x, double y, double z) {
   double f_result;
   f_result = x + y*z;
   return f_result;
}

>>> print(h_name)
test.h
>>> print(c_header)
#ifndef PROJECT__TEST__H
#define PROJECT__TEST__H
double f(double x, double y, double z);
#endif

另一个使用相等对象来给出命名输出的示例。这里的文件名(前缀)取自第一个(name,expr)对。

>>> from sympy.abc import f, g
>>> from sympy import Eq
>>> [(c_name, c_code), (h_name, c_header)] = codegen(
...      [("myfcn", x + y), ("fcn2", [Eq(f, 2*x), Eq(g, y)])],
...      "C99", header=False, empty=False)
>>> print(c_name)
myfcn.c
>>> print(c_code)
#include "myfcn.h"
#include <math.h>
double myfcn(double x, double y) {
   double myfcn_result;
   myfcn_result = x + y;
   return myfcn_result;
}
void fcn2(double x, double y, double *f, double *g) {
   (*f) = 2*x;
   (*g) = y;
}

如果生成的函数将是定义了各种全局变量的大型项目的一部分,则可以使用“全局变量”选项从函数签名中删除指定的变量

>>> from sympy.utilities.codegen import codegen
>>> from sympy.abc import x, y, z
>>> [(f_name, f_code), header] = codegen(
...     ("f", x+y*z), "F95", header=False, empty=False,
...     argument_sequence=(x, y), global_vars=(z,))
>>> print(f_code)
REAL*8 function f(x, y)
implicit none
REAL*8, intent(in) :: x
REAL*8, intent(in) :: y
f = x + y*z
end function
sympy.utilities.codegen.get_default_datatype(expr, complex_allowed=None)[源代码]#

根据表达式派生适当的数据类型。

sympy.utilities.codegen.make_routine(name, expr, argument_sequence=None, global_vars=None, language='F95')[源代码]#

从表达式生成适当例程的工厂。

参数:

name :字符串

生成代码中此例程的名称。

expr :表达式或表达式的列表/元组

例程实例将表示的SymPy表达式。如果给定一个表达式列表或元组,则例程将被视为具有多个返回值和/或输出参数。

argument_sequence :列表或元组,可选

按优先顺序列出例程的参数。如果省略,则结果取决于语言,例如,字母顺序或与给定表达式的顺序相同。

global_vars :iterable,可选

例程使用的全局变量序列。这里列出的变量不会显示为函数参数。

语言 :字符串,可选

指定目标语言。例程本身应该与语言无关,但是创建例程的精确方式、错误检查等都取决于语言。 [默认值:“F95”] .

笔记

A decision about whether to use output arguments or return values is made depending on both the language and the particular mathematical expressions. For an expression of type Equality, the left hand side is typically made into an OutputArgument (or perhaps an InOutArgument if appropriate). Otherwise, typically, the calculated expression is made a return values of the routine.

实例

>>> from sympy.utilities.codegen import make_routine
>>> from sympy.abc import x, y, f, g
>>> from sympy import Eq
>>> r = make_routine('test', [Eq(f, 2*x), Eq(g, x + y)])
>>> [arg.result_var for arg in r.results]
[]
>>> [arg.name for arg in r.arguments]
[x, y, f, g]
>>> [arg.name for arg in r.result_variables]
[f, g]
>>> r.local_vars
set()

另一个更复杂的例子,它混合了指定的和自动指定的名称。也有矩阵输出。

>>> from sympy import Matrix
>>> r = make_routine('fcn', [x*y, Eq(f, 1), Eq(g, x + g), Matrix([[x, 2]])])
>>> [arg.result_var for arg in r.results]  
[result_5397460570204848505]
>>> [arg.expr for arg in r.results]
[x*y]
>>> [arg.name for arg in r.arguments]  
[x, y, f, g, out_8598435338387848786]

我们可以更仔细地研究各种论点:

>>> from sympy.utilities.codegen import (InputArgument, OutputArgument,
...                                      InOutArgument)
>>> [a.name for a in r.arguments if isinstance(a, InputArgument)]
[x, y]
>>> [a.name for a in r.arguments if isinstance(a, OutputArgument)]  
[f, out_8598435338387848786]
>>> [a.expr for a in r.arguments if isinstance(a, OutputArgument)]
[1, Matrix([[x, 2]])]
>>> [a.name for a in r.arguments if isinstance(a, InOutArgument)]
[g]
>>> [a.expr for a in r.arguments if isinstance(a, InOutArgument)]
[g + x]