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

Introduction [5 minutes]

Description

Introduction of the topic, speakers, and software.

Intro to SymPy Expressions [25 minutes]

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

Code Printers [30 minutes]

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.

Ordinary Differential Equations

  • [10 minutes] Refresher on ordinary differential equations
  • [5 minutes] Exercise: write an analytic Jacobian function by hand

Lambdify

  • [10 minutes] Introduction to lambdify
  • [5 minutes] Exercise: Create a function from a SymPy expression

Chemical Kinetics Introduction

  • [5 minutes] Governings equations for systems of ODEs in chemical kinetics.
  • [5 minutes] Exercise: generate a function evaluating the rhs of an ODE system

Chemical Kinetics Symbolic Construction

  • [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

The Harder Way: C Code Generation, Custom Printers, and CSE [55 minutes]

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.

The Easy "Hard" Way: Cythonizing Your Code [1 hour]

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


In [ ]: