2021 Sep 15

Convert ODEs to DSP—With a Tool

Since my last post, I embraced the mathematical challenges of DSP. I quickly tired of writing—and miswriting—equations by hand. Then, I betrayed all of my mathematics instructors by using computer algebra to check my results. I betrayed them again when I felt the computer was simply better than me at the job. But I vindicated computer scientists when I resolved to automate the symbolic computations.

I’ve been a SageMath user since 2013, back when it was called Sage and it only ran on Linux. Sage was a main reason I began using Linux, and I feel a strong bond with the project. However, after eight years, I’ve learned more about software development, and Sage feels inconsistent, complex, opaque, and difficult to use. Sage’s goal is to compete with proprietary software like Mathematica. And as an open source project, it opted to integrate many independent mathematics packages under one interface. Mathematica is old enough to have crufty corners, but it has no such burden as integrating dozens of unrelated bits of software, and I do not doubt that it achieves much smoother integration than Sage ever will. (Maxima, Sage’s default backend for common symbolic calculations, has no programmatic interface—no API—and fully assumes that only a human being sitting at a terminal is interacting with it.)

If not for Sage, however, I would not have heard of SymPy, one of its components. SymPy alone is a powerful, self-sufficient computer algebra system, despite Sage treating it like a second-class citizen compared to Maxima. SymPy’s scope has proven sufficient to handle DSP design tasks, which rarely stray from dynamical systems. Because it is written in pure Python, it is much easier to understand and debug.

Python, despite its popularity, is not a language I ever did serious work in; but SymPy showed me its utility. My prior experience with Go and Objective–C set my expectations of Python. Inheritance is not really Pythonic because it’s implicit; much like the Go runtime isn’t really Go-like because it’s extremely complex. Languages aren’t a purity contest, but an attempt to strike a useful balance. Python optimizes more for writability and thus rapid development; Go asks programmers to write more code, but gives them transparency in exchange. Python brings big problems, like computer algebra systems, within reach; Go asks the programmer to think again, in such cases. I know this from building kappa.

The result of my learning Python has been a library I have simply named ode2dsp. I hope DSP engineers immediately know what it does. There is a pun, of course, that the library is an ode to digital signal processing. But no, code is not poetry, and DSP is not worthy of a song, even though it might be involved in music production.

I was surprised that the mathematical part of ode2dsp has less code than the code generating part. The initial target language, Faust, differs from Python in syntax, and thus requires translation.

ode2dsp separates the concerns of mathematics and code generation; the mathematical part knows nothing about the target languages. The mathematical data structures, although initially developed for generating Faust, can be adapted to suit code generators for other targets.

In all, there isn’t much code: 641 lines, including docstrings and tests. This will increase as the project achieves its lesser goals, such as stability analysis, which will further help DSP engineers eliminate errors in their designs.

It’s tempting to include pretty code formatting as a goal, but I fear this is too hard and different a problem to be included in a tool devoted to DSP. If you need the output in a certain style, you should probably find a code formatter. Unfortunately, I don’t know of any for Faust.


Discuss this post by emailing my public inbox. Please note the etiquette guidelines.

© 2021 Karl Schultheisz — Lancaster, PA, USA