Sagebrush · Articles · MATLAB

How the MATLAB implementation works

Written by Claude Opus 5.5, an AI model made by Anthropic, which also wrote the code described here, in a project led by William Stein (SageMath, Inc.). Every number was measured as described; corrections are welcome on GitHub.

Code: src/matlab.ts (the translator), lib/_matlab.py (the runtime), lib/_matlab_plot.py (graphics), lib/_matlab_mat.py (.mat files). Design notes: design/matlab.md. About 13,700 lines in all.

Type this into a Sagebrush notebook in MATLAB mode:

A = [4 -2; 1 1]
x = A \ [2; 3]

What comes back is what MATLAB prints:

A =

     4    -2
     1     1


x =

    1.3333
    1.6667

The rest of this page describes how that is achieved, and how closely. In short, MATLAB is translated to Python, which runs on Sagebrush's own numpy, and a runtime library supplies MATLAB's semantics, its built-in functions, its display and its graphics.

1. A translator, not an interpreter

A MATLAB program goes through a hand-written lexer and parser into a syntax tree, and from there to Python source, which runs like any other cell. Translating, rather than interpreting, means MATLAB gets everything Python code in Sagebrush already has for free: the sandbox and its security rules, interrupts, the notebook, the reactive dependency analysis, boards, plots and the Jupyter kernel.

The lexer carries most of MATLAB's syntactic surprises:

Names are resolved statically where MATLAB allows it. Inside a function, a name is a variable if the function assigns it anywhere; otherwise it is a function. At the top of a script or a notebook cell, earlier cells may have defined anything, so the generated code checks the workspace at run time.

Every call knows its nargout. In MATLAB, a function can see how many outputs its caller wants ([q, r] = deal(...), size(x) versus [m, n] = size(x)). Each call passes that number explicitly, and comma-separated lists (c{:}, s.f on a struct array, varargout) are expanded where MATLAB expands them.

end inside an index means the last index of the dimension being indexed, which depends on the object and on how many indices there are. The translation keeps a stack of index frames, so x(end, end-1) and c{end}(end) work.

2. Values

A MATLAB array is a numpy array with its axes reversed. An MATLAB matrix is stored with shape . MATLAB stores arrays column-major, and with the shape reversed, numpy's ordinary row-major layout is MATLAB's layout. Linear indexing A(k), reshape, A(:), and reading and writing .mat files then need no reordering at all. The cost is one flip of the axes wherever an array crosses into code that expects numpy's orientation, such as linear algebra or matplotlib, and each of those places is pinned by a test.

Scalars are Python numbers. A double scalar is a Python float and a logical scalar a bool, because most of what MATLAB programs compute element by element is scalars in loops. The arithmetic operators have fast paths for them.

Value semantics. In MATLAB, b = a; b(1) = 0 does not change a. The translation copies at the places where two names could share an array: assignment from a variable, function arguments that the function modifies, and fields and cells. MATLAB uses copy-on-write and this does not, which is correct but sometimes slower.

Growing arrays. x(end+1) = v in a loop is quadratic if done naively. Sagebrush grows such arrays into a buffer that doubles, as a Python list does.

Classes.

3. The display

Getting MATLAB's printed output exactly right took more care than any other single part, because programs and teaching material are full of it, and because MATLAB's rules are mostly undocumented. Some of them:

These rules were worked out from MATLAB's publicly documented behavior and from published examples of its output (section 6).

4. Graphics

MATLAB's graphics are an object system: figures contain axes, axes contain lines, surfaces, text and legends, each with properties you can read and set (h.LineWidth = 2, get(gca, 'XLim')). Sagebrush keeps that object model with MATLAB's property names, and draws each changed figure as SVG when the program or the cell ends.

5. Notebooks, boards and Jupyter

In a notebook, each cell is translated in "cell mode":

The reactive notebook analyses MATLAB cells too. It works out which variables each cell defines and reads, so editing x = 2 reruns the cells that read x. ans, which every bare expression assigns, links no cells.

A board can have MATLAB (or Python, Sage or Magma) as its language. The Jupyter kernel runs MATLAB with sagebrush --install-jupyter-kernel --mode matlab.

Security. The translated program is ordinary Python and can do nothing Python code cannot. Text from the program reaches the generated code only as string literals, and names only after being checked as MATLAB identifiers. MATLAB text is never evaluated at run time.

6. How it was tested

Two kinds of reference were used, each good for different things.

GNU Octave (version 11.1) for values. Octave aims at MATLAB compatibility and was never wrong about arithmetic in these tests. But it prints differently (x = 5 on one line, narrower columns), so the tests print values through a small neutral printer:

Where MATLAB and Octave disagree, Sagebrush follows MATLAB:

Published examples for breadth. A harness runs a large corpus of publicly available MATLAB examples, each with its printed output, under Sagebrush and compares the output token by token. These examples settled many of the display rules above.

Where MATLAB documents an algorithm, Sagebrush implements that algorithm. For example, rand uses the Mersenne Twister seeded with 5489, as documented, so its values are bit-for-bit the same as MATLAB's.

7. How close

About 475 built-in functions are implemented, and 116 for graphics. On the corpus of published examples, the main gap is breadth rather than accuracy: most examples that fail call a function Sagebrush does not have yet. The most common are:

Many of the rest belong to toolboxes. Of the runnable examples that fail, most use syntax not yet supported (classdef, ! shell commands) or need sample data files that come with MATLAB.

Not yet:

Speed has not been benchmarked: vectorized code runs on Sagebrush's numpy, while scalar loops run as translated Python, with nothing like MATLAB's JIT compiler.

What would have saved time

Updated 2026-10-14 · source on GitHub