Automatic Code Generation with SymPy
Watch the video of the tutorial online
This tutorial will introduce code generation concepts using the SymPy library. SymPy is a pure Python library for symbolic mathematics. Code generation refers to the act of converting a SymPy symbolic expression into equivalent code in some language, typically for numeric evaluation. This allows one to use SymPy to symbolically model a problem and generate fast numerical code for specific platforms that executes that model. This is a powerful tool that is useful to scientists in many domains. Code generation allows users to speed up existing code, to deal only with the high level mathematics of a problem, avoids mathematical errors and typos, makes it possible to deal with expressions that would otherwise be too large to write by hand, and opens possibilities to perform automatic mathematical optimizations of expressions.
SymPy supports generating code for C, C++, Fortran, MATLAB/Octave, Python, Cython, Julia, JavaScript, LLVM, Rust, Haskell, Mathematica, Tensorflow, and Theano, and can easily be extended to other languages. SymPy’s code generation is used by libraries such as PyDy, pyodesys, sympybotics, pycalphad, and many other programs.
You can run this tutorial online with Binder. Otherwise, follow the instructions in the README to install the tutorial materials.
Instructors
- Björn Dahlgren, KTH Royal Institute of Technology
- Kenneth Lyons, University of California, Davis
- Aaron Meurer, University of South Carolina
- Jason Moore, University of California, Davis
Learning objectives
Attendees will be able to:
- write SymPy expressions describing mathematical functions and identify the
function arguments and outputs,
- use the SymPy code printers to transform SymPy expressions representing
common domain specific functions into multiple output languages,
- use the SymPy code generation routines to output compilable C code and use
Cython to access these functions in Python,
- generate custom vectorized functions with the three SymPy functions:
lambdify, ufuncify, and autowrap,
- create both custom code printers that make use of specialized C libraries and
common subexpression elimination (CSE),
- subclass the core SymPy printers and create a printer for a custom language.
Outline
Description
Introduction of the topic, speakers, and software.
Description
Writing common domain specific mathematical expressions with SymPy.
Motivating Examples
Long expressions, Matrix operations, and Loop Fusion from classical mechanics, chemical kinetics, nuclear dynamics, and materials science.
- Expressions
- Reminders on common gotchas
- Floating point representation
- Undefined Functions
- Derivatives
- Matrices
- Matrix Symbols
- Indexed
Description
Printing expressions in multiple languages (C, Fortran, Rust, Julia, Octave, Javascript, etc)
Motivating Example
2D interactive plot in a Jupyter notebook by Javascript injection
- [15 min] Code printing
- Exercise: Codegen your own function
- [15 min] Exercise: Plotting SymPy Functions with Javascript
The Easy Way: High Level Code Generation [55 minutes]
Description
Generate python functions from symbolic expressions.
Motivating Example
Generate a Jacobian function for a chemical kinetic problem.
- [10 minutes] Refresher on ordinary differential equations
- [5 minutes] Exercise: write an analytic Jacobian function by hand
- [10 minutes] Introduction to
lambdify
- [5 minutes] Exercise: Create a function from a SymPy expression
- [5 minutes] Governings equations for systems of ODEs in chemical kinetics.
- [5 minutes] Exercise: generate a function evaluating the rhs of an ODE system
- [10 minutes] Exercise: construct SymPy expressions from a simple data structure
- [10 minutes] Exercise: derive a Jacobian symbolically and generate a function evaluating it
10 Minute Break
Description
Convert SymPy matrices to a C program and speed up numerical execution by extending the printer with common subexpression elimination.
Motivating Example
Evaluate a chemical kinetic ordinary differential equation and its Jacobian using C.
Section 1
- [5 minutes] Introduction
- [5 minutes] Exercise: Load the ODE expression, compute the Jacobian, and inspect the results.
- [5 minutes] Printing C Code
- [5 minutes] Exercise: Generate C code for the Jacobian expression.
Section 2
- [5 minutes] Extending and Customizing Printers
- [10 minutes] Exercise: Construct custom exponential printing and symbol names.
Section 3
- [5 minutes] Common Sub-expression Elimination
- [15 minutes] Exercise: Generate C code (w/ CSE) for the ODE and Jacobian expressions.
- [5 minutes] Bonus: Compile and execute the C code.
Description
Generate C code to evaluate an ordinary differential equation and and its Jacobian and wrap it for use with SciPy’s integrators. Additionally, demonstrate how one might access external C libraries.
Motivating Example
Integrating the water radiolysis system of ODEs.
Section 1: Introduction to Cython
- [2 minutes] Explanation of Cython
- [3 minutes] Example: writing Cython and compiling in a notebook
Section 2: Generating C Code with SymPy's codegen()
- [2 minutes] Review the water radiolysis system
- [2 minutes] First exposure to
codegen
- [3 minutes] Exercise generate code with a cleaner function signature
- [3 minutes] Exercise generate an even cleaner function signature
Section 3. Wrapping the Generated Code with Cython
- [5 minutes] Overview of the wrapping process
- [3 minutes] Explanation of the build process
- [3 minutes] Explanation of the wrapper code
- [3 minutes] Exercise: use the wrapped function with some random inputs
- [3 minutes] Use
odeint
to integrate the ODEs using the wrapped function
Section 4. Generating and Compiling a C Extension Module Automatically
- [3 minutes] Overview of the
autowrap
function
- [5 minutes] Exercise: explore the
autowrap
generated files
- [5 minutes] Exercise: use the wrapped function with some random inputs
- [2 minutes] Writing a wrapper around the wrapper
- [8 minutes] Exercise: generate a function to compute the Jacobian
Section 5. Using a Custom Printer and an External Library with autowrap
- [2 minutes] Overview of
CodeGen
and CodePrinter
- [2 minutes] Explanation of fastapprox library
- [8 minutes] Exercise: implement a custom code printer to use
fastpow
instead of pow
- [3 minutes] Including
fastpow.h
- [3 minutes] Using the new function and proving it worked
- [2 minutes] Exercise: compare outputs of the callables using
pow
and fastpow
functions
The attendees will come away with a powerful set of tools that will allow them
to develop high performance numerical code using Python that compliments NumPy
and SciPy. This tutorial will be ideal for users of the SciPy Stack that would
like to increase the performance of their Python code, get into some of the
depths of how low-level languages can interact and be used from Python, or to
learn a new technique for expressing mathematical models in Python.
Bonus Materials