Typesetting differential equations

Posted on September 18, 2017

This semester I am teaching a course involving linear differential equations, so I have typeset many differential equations that look like this:

Simple example

Believe me, writing such equations by hand gets tedious very quickly. So, I wanted a macro \LDE (for Linear Differential Equation), so that I could just type

\LDE{1, -3, 2}{2, 1}

and get the above result. This post describes how to write such a macro using LuaTeX + ConTeXt.

I’ll explain the implementation using a top-down approach. Writing macros in TeX is tough, so let’s delegate all the high lifting to Lua. So, we define a ConTeXt command that simply invokes a Lua function with the appropriate arguments. This can be done using:

\startluacode
-- ... lua function LDE to be added ...
interfaces.definecommand {
    name      = "LDE",
    macro     = LDE,
    arguments = {
        {"content", "list" },
        {"content", "list" },
    }
}
\stopluacode

The name argument specifies the name of the TeX command (\LDE in this case); the macro argument specifies the name of the Lua function (LDE in this case) which gets called when the TeX macro is invoked; the arguments tables specifies the list of arguments of the TeX macros. We specify that the macro has two arguments, both of them are content arguemnts (that is, the macro is invoked as \LDE{...}{...}) and each arguement should be passed on to the Lua function as a list (i.e., a Lua table of strings; other options include hash (for key-value list), number, and string).

Now all we need to do is define the function LDE, which takes two comma separated lists as arguments and typesets the resulting differential equation. The basic logic is straight forward. Suppose we have defined a function derivative that typesets a derivative (taking care of adding a + sign if the coefficient of an intermediate term is positive; not showing the coefficient if the coefficient is +1 or -1; and not showing the term if the coefficient is 0). Then, the LDE function is:

\startluacode
-- Lua function derivative to be added

local LDE = function(LHS, RHS)
  for i = 1, #LHS do
    context(derivative(LHS[i], "y(t)", #LHS - i, i == 1))
  end

  context("=")
  for i = 1, #RHS do
    context(derivative(RHS[i], "u(t)", #RHS - i, i == 1))
  end
end

interfaces.definecommand { ... }
\stopluacode

The last term in the call to the derivative function passes a flag that indicates whether or not the term is the first term.

I’ll not go into the details of the derivative function because it is straight forward. So, here is the complete macro with a usage example:

\startluacode
  local find, format = string.find, string.format

  local derivative = function(coefficient, var, order, first)
    -- If the coefficient is zero or empty, don't typeset anything
    if coefficient == nil or coefficient == "" or coefficient == "0" then
       return
    end

    -- If the coefficient is 1 or -1, we do not want to print the 1.
    if coefficient == "1" then
        coefficient = ""
    elseif coefficient == "-1" then
        coefficient = "-"
    end

    -- If this is not the first coefficient by is a positive coefficient, 
    -- then add a "+" sign
    if not first then
      if find(coefficient, "-") ~= 1 then
         coefficient = "+" .. coefficient
      end
    end

    -- Format the return value depending on the order of the derivative
    local str
    if order == 0 then
       str = format("%s %s", coefficient, var)
    elseif order == 1 then
       str = format("%s \\dfrac{ d %s } { dt }", coefficient, var)
    else
       str = format("%s \\dfrac{ d^{%s} %s } { dt^{%s} }", coefficient, order, var, order)
    end
    return str
  end

  local LDE = function(LHS, RHS)
    LHS = settings_to_array(LHS)
    RHS = settings_to_array(RHS)

    for i = 1, #LHS do
      context(derivative(LHS[i], "y(t)", #LHS - i, i == 1))
    end
    context("=")
    for i = 1, #RHS do
      context(derivative(RHS[i], "u(t)", #RHS - i, i == 1))
    end
  end


interfaces.definecommand {
    name      = "LDE",
    macro     = LDE,
    arguments = {
        {"content", "list" },
        {"content", "list" },
    }
}

\stopluacode

\starttext
\startformula
  \LDE{2,1,-3}{4,0,6}
\stopformula
\startformula
  \LDE{a_2, -a_1,a_0}{b_1,b_0}
\stopformula
\stoptext

This entry was posted in Macros and tagged luatex, programming, separating content and presentation.