{"id":357,"date":"2024-03-08T18:06:17","date_gmt":"2024-03-08T17:06:17","guid":{"rendered":"https:\/\/bowfinger.de\/blog\/?p=357"},"modified":"2024-03-08T18:06:18","modified_gmt":"2024-03-08T17:06:18","slug":"using-sympys-common-subexpression-elimination-to-generate-code","status":"publish","type":"post","link":"https:\/\/bowfinger.de\/blog\/2024\/03\/using-sympys-common-subexpression-elimination-to-generate-code\/","title":{"rendered":"Using Sympy&#8217;s Common Subexpression Elimination to generate Code"},"content":{"rendered":"\n<p>A nifty feature of <code>sympy<\/code> is its code generator. This allows generating code in many languages from expressions derived in <code>sympy<\/code>. I&#8217;ve been using this for a while and came across the Common Subexpression Elimination (CSE) function (again), recently.<\/p>\n\n\n\n<p>In earlier* versions of <code>sympy<\/code> I did not find it of great use. But now I was pleasantly surprised how much it matured. (* I cannot say for sure when I last tried using it; it must&#8217;ve been a long while ago.)<\/p>\n\n\n\n<p>What is it and what is it good for?<\/p>\n\n\n\n<p>Let&#8217;s say we want to generate code for a PI controller in C. The literature says its transfer function (in DIN notation) reads:<\/p>\n\n\n\n<p><img loading=\"lazy\" decoding=\"async\" src=\"https:\/\/bowfinger.de\/blog\/wp-content\/ql-cache\/quicklatex.com-766fc99801862a860dc92b288f9b04fb_l3.png\" class=\"ql-img-inline-formula quicklatex-auto-format\" alt=\"&#32;&#71;&#40;&#115;&#41;&#32;&#61;&#32;&#75;&#95;&#112;&#32;&#43;&#32;&#92;&#102;&#114;&#97;&#99;&#123;&#75;&#95;&#105;&#125;&#123;&#115;&#125;&#32;\" title=\"Rendered by QuickLaTeX.com\" height=\"22\" width=\"123\" style=\"vertical-align: -6px;\"\/><\/p>\n\n\n\n<p>Then furthermore the representation for a discrete time system with time step <img loading=\"lazy\" decoding=\"async\" src=\"https:\/\/bowfinger.de\/blog\/wp-content\/ql-cache\/quicklatex.com-cdf3c1c8a93e19a95eef6b3cbbc7be00_l3.png\" class=\"ql-img-inline-formula quicklatex-auto-format\" alt=\"&#84;&#95;&#115;\" title=\"Rendered by QuickLaTeX.com\" height=\"15\" width=\"16\" style=\"vertical-align: -3px;\"\/> becomes<\/p>\n\n\n\n<p><img loading=\"lazy\" decoding=\"async\" src=\"https:\/\/bowfinger.de\/blog\/wp-content\/ql-cache\/quicklatex.com-94feb8b4287014731f4569c815a0795e_l3.png\" class=\"ql-img-inline-formula quicklatex-auto-format\" alt=\"&#32;&#71;&#40;&#122;&#41;&#32;&#61;&#32;&#75;&#95;&#112;&#32;&#43;&#32;&#92;&#102;&#114;&#97;&#99;&#123;&#75;&#95;&#105;&#32;&#84;&#95;&#115;&#125;&#123;&#50;&#125;&#32;&#43;&#32;&#92;&#102;&#114;&#97;&#99;&#123;&#92;&#102;&#114;&#97;&#99;&#123;&#75;&#95;&#105;&#32;&#84;&#95;&#115;&#125;&#123;&#50;&#125;&#32;&#45;&#32;&#75;&#95;&#112;&#125;&#123;&#122;&#125;&#32;\" title=\"Rendered by QuickLaTeX.com\" height=\"31\" width=\"226\" style=\"vertical-align: -6px;\"\/><\/p>\n\n\n\n<p>In order to efficiently and flexibly calculate the response we want to implement it in a second order IIR (infinite impulse response) filter structure. This structure is basically a representation of <img loading=\"lazy\" decoding=\"async\" src=\"https:\/\/bowfinger.de\/blog\/wp-content\/ql-cache\/quicklatex.com-b9e34bb65b292c2765a5f7fb24ae9fe9_l3.png\" class=\"ql-img-inline-formula quicklatex-auto-format\" alt=\"&#32;&#71;&#95;&#122;&#32;\" title=\"Rendered by QuickLaTeX.com\" height=\"15\" width=\"21\" style=\"vertical-align: -3px;\"\/> as a rational function<\/p>\n\n\n\n<p><img loading=\"lazy\" decoding=\"async\" src=\"https:\/\/bowfinger.de\/blog\/wp-content\/ql-cache\/quicklatex.com-5950154371cb34716502d28eac320e51_l3.png\" class=\"ql-img-inline-formula quicklatex-auto-format\" alt=\"&#32;&#92;&#102;&#114;&#97;&#99;&#123;&#32;&#98;&#95;&#48;&#32;&#43;&#32;&#98;&#95;&#49;&#32;&#122;&#94;&#123;&#45;&#49;&#125;&#32;&#43;&#32;&#98;&#95;&#50;&#32;&#122;&#94;&#123;&#45;&#50;&#125;&#32;&#125;&#123;&#32;&#97;&#95;&#48;&#32;&#43;&#32;&#97;&#95;&#49;&#32;&#122;&#94;&#123;&#45;&#49;&#125;&#32;&#43;&#32;&#97;&#95;&#50;&#32;&#122;&#94;&#123;&#45;&#50;&#125;&#32;&#125;&#32;\" title=\"Rendered by QuickLaTeX.com\" height=\"28\" width=\"110\" style=\"vertical-align: -9px;\"\/><\/p>\n\n\n\n<p>so we need to take the expression apart such that we can determine the coefficients a_1, a_2, b_0, &#8230; (by convention, the problem is scaled such that a_0 = 1.<\/p>\n\n\n\n<p>This is all very cumbersome to do manually (and I suck at doing this, especially as I insert quite embarrassing mistakes all the time). So, <code>sympy<\/code> to the rescue:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code lang=\"python\" class=\"language-python\">import sympy as sy\n\nKp, Ki, Ts = sy.symbols(\"K_p K_i T_s\", real=True)\nz = sy.symbols(\"z\", complex=True)\nG_z = Kp + Ki*Ts\/2 + (Ki*Ts\/2 - Kp)\/z\n\ndisplay(G_z)\n\nn, d = G_z.ratsimp().as_numer_denom()\nn, d = n.as_poly(z), d.as_poly(z)\n\ndisplay(n, d)<\/code><\/pre>\n\n\n\n<p>We first define the symbols needed. Then set up <code>G_z<\/code> according to our equation above. After displaying it for good measure, we do a rational simplification <code>ratsimp()<\/code> on it and split it into numerator <code>n<\/code> and denominator <code>d<\/code>.<\/p>\n\n\n\n<p>Now, we represent <code>n<\/code> and <code>d<\/code> as polynomials of <code>z<\/code>. And this is how we obtain our coefficients <code>a1<\/code>, <code>a2<\/code>, and so on. We continue:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code lang=\"python\" class=\"language-python\">scale = d.LC()\n\nn, d = (n.as_expr()\/scale).as_poly(z), (d.as_expr()\/scale).as_poly(z)\nN, D = n.degree(), d.degree()<\/code><\/pre>\n\n\n\n<p>We find the scaling factor as the leading coefficient (<code>LC()<\/code>) of the denominator <code>d<\/code>, scale both accordingly and determine the degree of each (this is mostly to determine for how many terms we have to iterate when printing etc.).<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code lang=\"python\" class=\"language-python\">Bs = n.all_coeffs()\nAs = d.all_coeffs()\n\ndisplay(Bs)\ndisplay(As)<\/code><\/pre>\n\n\n\n<p>Now, basically <code>Bs<\/code> contains all coefficients <code>[b0 b1]<\/code> and likewise for <code>As<\/code>. Here&#8217;s a shortcut, see if you can spot it.<\/p>\n\n\n\n<p>And now comes the <code>cse<\/code> magic:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code lang=\"python\" class=\"language-python\">commons, exprs = sy.cse(Bs)<\/code><\/pre>\n\n\n\n<p>This returns a tuple. The first element is a list of tuples of arbitrarily assigned place holder variables <code>x0<\/code>, <code>x1<\/code>, and so on. The second element are the original expressions, but the placeholders are substituted. In this example:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code class=\"\">(\n  [\n    (x0, K_i*T_s\/2)\n  ],\n  [K_p + x0, -K_p + x0]\n)<\/code><\/pre>\n\n\n\n<p>Which means, instead of calculating <code>K_i*T_s\/2<\/code> twice, we can do it once, assign it to <code>x0<\/code> and use that on the expressions.<\/p>\n\n\n\n<p>You can even concatenate <code>As<\/code> and <code>Bs<\/code> for good measure (not really necessary here, since the As only contain 1 and 0, but in other controller topologies this may change).<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code lang=\"python\" class=\"language-python\">commons, exprs = sy.cse(As + Bs)\n\n# Printing C code for instance\nfor com in commons:\n    print( sy.ccode(com[1], assign_to=repr(com[0])) )\n\nfor expr, name in zip(exprs, \"a0 a1 b0 b1\".split(\" \")):\n    print( sy.ccode(expr, assign_to=f\"coeffs.{name:s}\") )<\/code><\/pre>\n\n\n\n<p>We obtain the common terms <code>commons<\/code> and the reduced expressions <code>exprs<\/code>, then print the common terms assigning them to their respective placeholder variables, and finally print the reduced expressions. This produces this five liner in C:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code lang=\"c\" class=\"language-c\">x0 = (1.0\/2.0)*K_i*T_s;\ncoeffs.a0 = 1;\ncoeffs.a1 = 0;\ncoeffs.b0 = K_p + x0;\ncoeffs.b1 = -K_p + x0;<\/code><\/pre>\n\n\n\n<p>Now, how is this useful you ask? Well, for this toy example it certainly is a lot of work for five lines I could&#8217;ve just typed out.<\/p>\n\n\n\n<p>But if you go into a PID controller with damped differential term, and you want your code to be efficient (i.e. have as few operations as possible), this is very handy. Just by changing the symbols and the expression for <code>G_z<\/code> we obtain this:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code lang=\"c\" class=\"language-c\">x0 = 2*T_v;\nx1 = 1.0\/(T_s + x0);\nx2 = 4*T_v;\nx3 = T_i*T_s;\nx4 = 2*x3;\nx5 = T_i*x2;\nx6 = 1.0\/(x4 + x5);\nx7 = K_p*x4;\nx8 = K_p*T_s*x0;\nx9 = pow(T_s, 2);\nx10 = 4*K_p*T_d*T_i + K_p*x5;\nx11 = K_p*x9 + x10;\nc.a0 = 1;\nc.a1 = -x1*x2;\nc.a2 = x1*(-T_s + 2*T_v);\nc.b0 = x6*(x11 + x7 + x8);\nc.b1 = (K_p*x9 - x10)\/(T_i*x0 + x3);\nc.b2 = x6*(x11 - x7 - x8);<\/code><\/pre>\n\n\n\n<p>I did this manually once, and only obtained five or six of the placeholders. That&#8217;s the power of <code>cse()<\/code>.<\/p>\n\n\n\n<p>Oh, you need the code in Python for testing? No problem, just use <code>pycode<\/code> instead (and work around that <code>assign_to<\/code> parameter).<\/p>\n\n\n\n<p>So, there you have it.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>A nifty feature of sympy is its code generator. This allows generating code in many languages from expressions derived in sympy. I&#8217;ve been using this for a while and came across the Common Subexpression Elimination (CSE) function (again), recently. In earlier* versions of sympy I did not find it of great use. But now I&hellip;<\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"closed","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"inline_featured_image":false,"footnotes":""},"categories":[1],"tags":[],"class_list":["post-357","post","type-post","status-publish","format-standard","hentry","category-uncategorized"],"_links":{"self":[{"href":"https:\/\/bowfinger.de\/blog\/wp-json\/wp\/v2\/posts\/357","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/bowfinger.de\/blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/bowfinger.de\/blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/bowfinger.de\/blog\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/bowfinger.de\/blog\/wp-json\/wp\/v2\/comments?post=357"}],"version-history":[{"count":9,"href":"https:\/\/bowfinger.de\/blog\/wp-json\/wp\/v2\/posts\/357\/revisions"}],"predecessor-version":[{"id":367,"href":"https:\/\/bowfinger.de\/blog\/wp-json\/wp\/v2\/posts\/357\/revisions\/367"}],"wp:attachment":[{"href":"https:\/\/bowfinger.de\/blog\/wp-json\/wp\/v2\/media?parent=357"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/bowfinger.de\/blog\/wp-json\/wp\/v2\/categories?post=357"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/bowfinger.de\/blog\/wp-json\/wp\/v2\/tags?post=357"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}