Main Content

Results for

Prototyping MATLAB code in Claude's container with Octave
Duncan Carlsmith, Department of Physics, University of Wisconsin-Madison
Simulated rigid molecules trajectories
Fig. 1: Simulated rigid molecules trajectories
When working with AI to develop MATLAB code for a chaotic-dynamics simulator, I discovered a useful workflow trick: Claude's container ships with GNU Octave. Claude can write .m files and execute them directly, as it can Python, getting fast feedback before you ever push to your local system. The related workflow described here may be possible using any agentic AI offering access to Octave.
The code simulates a classical 2D monatomic or diatomic (rigid or flexible) molecule undergoing a volume compression and compares the adiabatic coefficient in the simulation to the predictions of the ideal gas model and classical thermodynamics, assuming equipartition of energy between the translational, rotational, and vibrational degrees of freedom. Energy is injected by the moving container wall to various degrees of freedom during collisions, and the goal is to see how well the degrees of freedom thermalize through wall collisions alone without intermolecular collisions, and to study wall shape effects. The simulation uses an impact dynamics model for the collisions and tightly controlled state propagation.
My setup includes Claude, an ngrok link to speed up file transfer between a chat container and my local file system, and MATLAB MCP, enabling Claude to push and run MATLAB R2025b and collect output. I decided to first develop in Python entirely in the container, telling Claude to think in advance about porting to MATLAB. This approach allowed Claude to debug quite a package of Python code without the overhead of transferring code and results to and from my system. At various stages, we backed up the Python along with status reports to my filesystem in case the chat failed.
The next stage was to port to MATLAB. When Claude just launched into testing the MATLAB code with Octave in its container, I was taken aback - I hadn’t ever thought of that or known it was a built-in option. We proceeded to make floating-point comparisons of the Python and Octave simulation outputs (which agreed to ~1e-13) to debug the Octave. Finally, we simply transferred about 30 files of Octave/MATLAB code to my filesystem and verified it worked.
The version problem
Claude's container runs Octave 8.4.0, released in late 2023. The current Octave is 11.1.0, released February 2026. Octave is actively maintained, with three or four releases per year. Newer classdef improvements, advances with sparse/diagonal matrices, and various function flags available in 11.1 are lacking in 8.4. For my simulation code, none of this turned out to matter.
Why not just upgrade?
According to Claude, “the container is Ubuntu 24.04 LTS, and its repositories only offer 8.4.0. A standard apt-get install chain failed in interesting ways — Ubuntu's security archive sometimes drops point-version .deb files that the local package index still references, breaking unrelated dependency chains. The openssh-client package got 404'd, which cascaded through openmpi-bin, stopping the install.” Claude suggested “building from source (45+ minutes of compile, plus C++20 and Fortran toolchains), a third-party PPA, or Flatpak (which isn't installed).” My answer was: “I don’t understand all that gobbledygook. Live with 8.4, but write code that will also work in modern MATLAB.”
Octave 8.4 handles array operations, slicing, broadcasting, structs, .mat file I/O via load/save, anonymous functions, fzero and other root-finders, ODE solvers, +package/ namespace folders, sparse matrices, and basic plotting. For our project — a wall-collision simulator with a Forest-Ruth symplectic integrator and Brent's method root-finding — every line of Python code ran identically in MATLAB. The numerical answers differed at the floating-point-precision level (Octave's fzero and MATLAB's fzero round slightly differently, apparently).
Gotchas
A few pitfalls were encountered that you might note if you try this workflow.
1. Nested function definitions inside loops or after executable code
Octave is relaxed about where you put helper function blocks. MATLAB R2025b is strict: local functions go at the end of a file, not inside another function body or — fatally — inside a for loop. I had written:
for i = 1:n
if condition
function f = f_trial(t) % LEGAL in Octave, ERROR in MATLAB
...
end
root = fzero(@f_trial, ...);
end
end
MATLAB rejected this with "Function definition is misplaced or improperly nested." The fix is to move helpers to the end of the file as proper local functions, and use anonymous functions for closures over loop-local variables:
for i = 1:n
if condition
r0 = snapshot_r; % capture in loop scope
f_trial = @(t) helper(t, r0, ...); % captures at creation
root = fzero(f_trial, ...);
end
end
function f = helper(t, r0, ...) % at file end
...
end
2. MATLAB's arguments...end validation block
A MATLAB feature for argument typing and defaults is unsupported in Octave. The modern MATLAB style:
function log = simulate(state, schedule, opts)
arguments
state struct
schedule struct
opts.N_max (1,1) double = 200000
opts.verbose (1,1) logical = false
end
% opts.N_max and opts.verbose available, types and sizes validated
...
end
Octave's parser doesn't recognize the arguments keyword. The fallback is old-style manual parsing:
function log = simulate(state, schedule, varargin)
N_max = 200000;
verbose = false;
for k = 1:2:numel(varargin)
switch varargin{k}
case 'N_max', N_max = varargin{k+1};
case 'verbose', verbose = varargin{k+1};
end
end
...
end
3. Hardcoded paths
Not an Octave-specific problem, but Claude's container has different paths than your local filesystem. Resolve data files relative to the script:
this_dir = fileparts(mfilename('fullpath'));
data = load(fullfile(this_dir, 'comparison', 'data.mat'));
4. Variable name collision
Again, not an Octave-specific problem, but variable name collisions (with Python objects in your workspace, or stale function caches after Claude pushes a new file) can cause confusing errors. clear all resets both.
5. Floating-point differences that get amplified
Octave's fzero and MATLAB's fzero round differently. In chaotic dynamics, such differences can be amplified. For the simulator we built, the same flexible molecule initial condition ran to 215 collisions in Octave and 226 in MATLAB R2025b — identical physics, different trajectories. That such tiny differences could be amplified in this simulation was verified with tests in the three languages - Python, Octave, and MATLAB.
Speed
Claude pointed out that performance varies with language and offered a comparison for a flexible molecule example with ~215-230 wall collisions): Python (CPython 3 + NumPy + scipy.brentq), ~3.3 s, 1×;MATLAB R2025b, 0.5 s, 0.15× (6.6x faster than Python); Octave 8.4 in Claude's container, ~10 s, 3× (3x slower than Python).
In this most challenging case, the integrator has to step between collisions to properly simulate the coupled rotations and vibrations. The 20x speed improvement of MATLAB over Octave might be due to the MATLAB JIT.
Conclusion
When I first started developing MATLAB Live Scripts for physics education, I suggested Octave as an open-source alternative for those educators who had no MATLAB site license. Knowing the limitations of Octave and not knowing if its development would continue, I have not limited myself to Octave-compatible code. I am nonetheless tickled that this workflow succeeded. I might have skipped the Python prototype for this project. Of course, for those projects featuring MATLAB toolboxes not supported by Octave or Python, prototyping MATLAB code in these languages is more complicated.
Conflict of interest
The author declares he has no financial interest in Mathworks or Anthropic. This article is informational and does not constitute an endorsement by the University of Wisconsin-Madison of any vendor or product. Claude is a trademark of Anthropic. MATLAB is a trademark of Mathworks.