# How I had to translate Matlab code into Maple

Posted on Tue 18 August 2020 in Posts

In this short post, I wanted to point out one interesting application of regular expressions I had to work on for my PhD research project. The code was meant as a technical tool to help tranlate some ordingary differential equation models from numerical (Matlab) to symbolic (Maple) code.

## The original code

The original *.m files were pulled from this webpage using `wget` and their own API. Ordingary differential equation (ODE) models in those files contain a special function called `xdot`. It returns an array of `n` elements, which form right-hand side of an ODE.

The function is usually defined in the form

``````function xdot=f(x,t)
% define parameters as
C = 1.0;
% ...
xdot = zeros(..., ...);
% define each xdot(i) separately
end
``````

What I wanted to see in the maple code was the following:

``````#  define parameters as symbolic constants
C := C:

#  define system of odes as array

sigma := [
diff(x1(t), t) = <...>,
<...>
diff(xn(t), t) = <...>
]:
``````

The best way to solve it I saw was to use regular expressions.

## Step by step

Firstly, I had to get rid of Matlab comment and turn them into Maple compatible ones, hence the line

``````out_program = re.findall(
r"function xdot=f\(x,t\)(.*?)end", content, re.DOTALL
).replace("%", "#")
``````

After that, we want to look at `if` statements. In Maple, conditional structure like `if` statements are written as

``````if <condition> then <code> end:
``````

To do that, we utilize groups: `(..)`. The regex is

``````out_program = re.sub(
r"if(\(\w*\))(.*;)end",
r"if \1 then \2\nend if:",
out_program,
flags=re.DOTALL,
)
``````

The `if(\(\w*\))(.*;)end` regex gets the condition `\(\w*\)` and the code (.*;) between `if` / `end` to place those between `if`, `then`, `end` in positions marked by `\1` and `\2`.

Remeber, that we do not want to have `xdot(i)=...` assigned manually anymore in Maple, we want to see Maple syntax: `diff(x(t),t) = ...`, so we do the following regex:

``````out_program = re.sub(
r"xdot\((\d+)\) \=( .*);", r"\ndiff(x\1(t), t) = \2,", out_program
)
``````

Again, notice the groups that capture stuff we want to preserve, namely the index of `xdot` and the right-hand sides.

Next few lines do some cosmetic work, namely, rename any `x(i)` appearance into `xi(t)`, then make all variable assingments symbolic constants (i.e. if we have `c=0` we want to have `c:=c`). Finally, if we have Matlab assignments that do not use constants (i.e. `c=0; x = c+x;`) we want to keep them (i.e. `c:=c: x:=c+x:`).

``````out_program = re.sub(r"x\((\d+)\)", r"x\1(t)", out_program) # x(i) -> xi(t)
out_program = re.sub(r"(\w+)\=\d*\..*", r"\1:=\1:", out_program) # c=NUMBER -> c:=c:
out_program = re.sub(r"(\w+)\=(.*);", r"\1:=\2:", out_program) # Left-side = ANYTHING NOT METNIONED ABOVE -> same but with := sign
``````

Finally, putting it all together we can run:

``````import re
from glob import glob
from tqdm.auto import tqdm

files = glob("files/*/*/*.m")

for i in tqdm(range(len(files))):
with open(files[i], "r") as f:
try:
except:
try:
out_program = re.findall(
r"function xdot=f\(x,t\)(.*?)end", content, re.DOTALL
).replace("%", "#")
out_program = re.sub(
r"if(\(\w*\))(.*;)end",
r"if \1 then \2\nend if:",
out_program,
flags=re.DOTALL,
)
out_program = re.sub(
r"xdot\((\d+)\) \=( .*);", r"\ndiff(x\1(t), t) = \2,", out_program
)
out_program = re.sub(r"(xdot=zeros.*)", r"# \1", out_program) # comment out declaration of xdot

out_program = re.sub(r"x\((\d+)\)", r"x\1(t)", out_program)
out_program = re.sub(r"(\w+)\=\d*\..*", r"\1:=\1:", out_program)
out_program = re.sub(r"(\w+)\=(.*);", r"\1:=\2:", out_program)
# cosmetic work to make maple run and not complain about trailing comma
out_program = re.sub(
r"(diff.*)", r"sigma := [\n\1]:", out_program, flags=re.DOTALL
).replace(",\n]", "\n]")

outname = files[i].split(".") + ".mpl"

with open(outname, "w") as output:
output.write(out_program)
import os

os.system("mkdir -p new_examples")
os.system(f"cp {outname} new_examples")
except: