Scientific Modeling Cheatsheet
MATLAB – Python – Julia Quick Reference
Note on MATLAB Code: The MATLAB examples from the original QuantEcon sections have been verified. Additional MATLAB examples in the extended scientific computing sections have not been fully tested due to licensing constraints. We welcome community contributions to verify and fix any MATLAB code issues. Please submit corrections via pull request to the repository at https://github.com/SciML/Julia_Modeling_Workshop.
Dependencies and Setup
Description | MATLAB | Python | Julia |
---|---|---|---|
Basic numerical | % Built-in |
import numpy as np
from numpy import linalg as la |
using LinearAlgebra
using Statistics
using SparseArrays |
Scientific computing | % Built-in ODE solvers |
from scipy import integrate
from scipy import optimize |
using DifferentialEquations
using Optimization
using NonlinearSolve |
Plotting | % Built-in plotting |
import matplotlib.pyplot as plt |
using Plots |
Creating Vectors
Description | MATLAB | Python | Julia |
---|---|---|---|
Row vector | A = [1 2 3] |
A = np.array([[1, 2, 3]]) |
A = [1 2 3] |
Column vector | A = [1; 2; 3] |
A = np.array([[1], [2], [3]]) |
A = [1 2 3]' # Transpose of row vector
A = [1; 2; 3] # Using semicolons
A = [1, 2, 3] # 1-D array (column) |
1-D array | Not applicable |
A = np.array([1, 2, 3]) |
A = [1, 2, 3] |
Integers from 1 to n | A = 1:n |
A = np.arange(1, n+1) |
A = 1:n |
Integers j to n, step k | A = j:k:n |
A = np.arange(j, n+1, k) |
A = j:k:n |
Linearly spaced | A = linspace(1, 5, k) |
A = np.linspace(1, 5, k) |
A = range(1, 5, length=k) |
Creating Matrices
Description | MATLAB | Python | Julia |
---|---|---|---|
Create matrix | A = [1 2; 3 4] |
A = np.array([[1, 2], [3, 4]]) |
A = [1 2; 3 4] |
Zeros | A = zeros(2, 2) |
A = np.zeros((2, 2)) |
A = zeros(2, 2) |
Ones | A = ones(2, 2) |
A = np.ones((2, 2)) |
A = ones(2, 2) |
Identity | A = eye(2) |
A = np.eye(2) |
A = I
A = Matrix(I, 2, 2) |
Diagonal | A = diag([1 2 3]) |
A = np.diag([1, 2, 3]) |
A = Diagonal([1, 2, 3]) |
Random uniform | A = rand(2, 2) |
A = np.random.rand(2, 2) |
A = rand(2, 2) |
Random normal | A = randn(2, 2) |
A = np.random.randn(2, 2) |
A = randn(2, 2) |
Sparse | A = sparse(B) |
from scipy import sparse
A = sparse.csr_matrix(B) |
using SparseArrays
A = sparse(B)
# Create empty sparse matrix
A = spzeros(2, 2)
A[1, 2] = 4
A[2, 2] = 1 |
Tridiagonal | A = diag(x, -1) + diag(y, 0) + diag(z, 1) |
from scipy.sparse import diags
A = diags([x, y, z], [-1, 0, 1]) |
# Example vectors
x = [1, 2, 3]
y = [4, 5, 6, 7]
z = [8, 9, 10]
Tridiagonal(x, y, z) |
Manipulating Vectors and Matrices
Description | MATLAB | Python | Julia |
---|---|---|---|
Transpose | A.' |
A.T |
transpose(A)
A' |
Complex conjugate transpose | A' |
A.conj().T |
A' |
Concatenate horizontally | A = [B C] |
A = np.hstack([B, C])
A = np.concatenate([B, C], axis=1) |
A = [[1 2] [1 2]] # For literals
A = [B C] # For variables
A = hcat(B, C) # Function form |
Concatenate vertically | A = [B; C] |
A = np.vstack([B, C])
A = np.concatenate([B, C], axis=0) |
A = [[1 2]; [1 2]] # For literals
A = [B; C] # For variables
A = vcat(B, C) # Function form |
Reshape (to 5x2) | A = reshape(A, 5, 2) |
A = A.reshape(5, 2) |
A = reshape(A, 5, 2) |
Convert matrix to vector | A(:) |
A.flatten() |
A[:]
vec(A) |
Flip left/right | fliplr(A) |
np.fliplr(A) |
reverse(A, dims=2) |
Flip up/down | flipud(A) |
np.flipud(A) |
reverse(A, dims=1) |
Repeat matrix (3x4 block) | repmat(A, 3, 4) |
np.tile(A, (3, 4)) |
repeat(A, 3, 4) |
Preallocate similar array | B = zeros(size(A)) |
B = np.empty_like(A) |
x = rand(3, 3)
y = similar(x)
y = similar(x, 2, 2) |
Broadcasting functions | arrayfun(@(x) x^2, A) |
A**2 # NumPy broadcasts automatically |
f(x) = x^2
g(x, y) = x + 2 + y^2
x = 1:10
y = 2:11
f.(x) # Apply f to each element
g.(x, y) # Apply g element-wise |
Sort rows | sort(A) |
np.sort(A) |
sort(A, dims=1) |
Sort columns | sort(A, 2) |
np.sort(A, axis=1) |
sort(A, dims=2) |
Unique values | unique(A) |
np.unique(A) |
unique(A) |
Accessing Vector/Matrix Elements
Description | MATLAB | Python | Julia |
---|---|---|---|
Access element (row i, col j) | A(i, j) |
A[i-1, j-1] |
A[i, j] |
Access row i | A(i, :) |
A[i-1, :] |
A[i, :] |
Access column j | A(:, j) |
A[:, j-1] |
A[:, j] |
First k rows | A(1:k, :) |
A[:k, :] |
A[1:k, :] |
First k columns | A(:, 1:k) |
A[:, :k] |
A[:, 1:k] |
Last k rows | A(end-k+1:end, :) |
A[-k:, :] |
A[end-k+1:end, :] |
Select rows [1, 3, 5] | A([1 3 5], :) |
A[[0, 2, 4], :] |
A[[1, 3, 5], :] |
Remove row 2 | A(2, :) = [] |
A = np.delete(A, 1, axis=0) |
A = A[setdiff(1:end, 2), :] |
Diagonal elements | diag(A) |
np.diag(A) |
diag(A) |
Get dimensions | size(A) |
A.shape |
size(A)
nrow, ncol = size(A) |
Number of dimensions | ndims(A) |
A.ndim |
ndims(A) |
Number of elements | numel(A) |
A.size |
length(A) |
Mathematical Operations
Description | MATLAB | Python | Julia |
---|---|---|---|
Dot product | dot(A, B) |
np.dot(A, B)
A @ B |
dot(A, B)
A ⋅ B |
Matrix multiplication | A * B |
A @ B
np.matmul(A, B) |
A * B |
Element-wise multiplication | A .* B |
A * B |
A .* B |
Element-wise division | A ./ B |
A / B |
A ./ B |
Element-wise power | A .^ 2 |
A ** 2 |
A .^ 2 |
Matrix power | A^2 |
np.linalg.matrix_power(A, 2) |
A^2 |
Inverse | inv(A) |
np.linalg.inv(A) |
inv(A) |
Determinant | det(A) |
np.linalg.det(A) |
det(A) |
Eigenvalues & vectors | [V, D] = eig(A) |
D, V = np.linalg.eig(A) |
D, V = eigen(A) |
Singular value decomposition | [U, S, V] = svd(A) |
U, S, V = np.linalg.svd(A) |
U, S, V = svd(A) |
Cholesky decomposition | chol(A) |
np.linalg.cholesky(A) |
cholesky(A) |
LU decomposition | [L, U, P] = lu(A) |
P, L, U = scipy.linalg.lu(A) |
L, U, p = lu(A) |
QR decomposition | [Q, R] = qr(A) |
Q, R = np.linalg.qr(A) |
Q, R = qr(A) |
Rank | rank(A) |
np.linalg.matrix_rank(A) |
rank(A) |
Trace | trace(A) |
np.trace(A) |
tr(A) |
Norm | norm(A) |
np.linalg.norm(A) |
norm(A) |
Condition number | cond(A) |
np.linalg.cond(A) |
cond(A) |
Solve Ax = b | A \ b |
np.linalg.solve(A, b) |
A \ b |
Least squares | A \ b |
np.linalg.lstsq(A, b)[0] |
A \ b |
Sum / Max / Min
Description | MATLAB | Python | Julia |
---|---|---|---|
Sum of all elements | sum(A(:)) |
np.sum(A) |
sum(A) |
Sum along columns | sum(A) |
np.sum(A, axis=0) |
sum(A, dims=1) |
Sum along rows | sum(A, 2) |
np.sum(A, axis=1) |
sum(A, dims=2) |
Cumulative sum | cumsum(A) |
np.cumsum(A) |
cumsum(A) |
Max of all elements | max(A(:)) |
np.max(A) |
maximum(A) |
Max along columns | max(A) |
np.max(A, axis=0) |
maximum(A, dims=1) |
Max along rows | max(A, [], 2) |
np.max(A, axis=1) |
maximum(A, dims=2) |
Max and index | [val, idx] = max(A) |
idx = np.argmax(A)
val = A.flat[idx] |
val, idx = findmax(A) |
Min of all elements | min(A(:)) |
np.min(A) |
minimum(A) |
Min along columns | min(A) |
np.min(A, axis=0) |
minimum(A, dims=1) |
Min along rows | min(A, [], 2) |
np.min(A, axis=1) |
minimum(A, dims=2) |
Min and index | [val, idx] = min(A) |
idx = np.argmin(A)
val = A.flat[idx] |
val, idx = findmin(A) |
Mean | mean(A) |
np.mean(A) |
mean(A) |
Median | median(A) |
np.median(A) |
median(A) |
Standard deviation | std(A) |
np.std(A) |
std(A) |
Variance | var(A) |
np.var(A) |
var(A) |
Programming
Description | MATLAB | Python | Julia |
---|---|---|---|
Comment | % This is a comment |
# This is a comment |
# This is a comment |
Multiline comment | %{
This is a
multiline comment
%} |
"""
This is a
multiline comment
""" |
#=
This is a
multiline comment
=# |
For loop | for i = 1:10
disp(i)
end |
for i in range(1, 11):
print(i) |
for i in 1:10
println(i)
end |
While loop | while x < 10
x = x + 1
end |
while x < 10:
x = x + 1 |
while x < 10
x = x + 1
end |
If statement | if x > 0
disp('positive')
elseif x < 0
disp('negative')
else
disp('zero')
end |
if x > 0:
print('positive')
elif x < 0:
print('negative')
else:
print('zero') |
if x > 0
println("positive")
elseif x < 0
println("negative")
else
println("zero")
end |
Function definition | function y = f(x)
y = x^2;
end |
def f(x):
return x**2 |
function f(x)
return x^2
end
f(x) = x^2 |
Anonymous function | f = @(x) x^2 |
f = lambda x: x**2 |
f = x -> x^2 |
Try-catch | try
% code
catch ME
disp(ME.message)
end |
try:
# code
except Exception as e:
print(e) |
try
# code
catch e
println(e)
end |
Print to console | disp('text')
fprintf('text\n') |
print('text') |
println("text")
print("text") |
String formatting | sprintf('x = %d', x) |
f'x = {x}'
'x = {}'.format(x) |
"x = $x"
@sprintf("x = %d", x) |
Ternary operator | y = (x > 0) * 1 + (x <= 0) * (-1) |
y = 1 if x > 0 else -1 |
y = x > 0 ? 1 : -1 |
List comprehension | y = arrayfun(@(x) x^2, 1:10) |
y = [x**2 for x in range(1, 11)] |
y = [x^2 for x in 1:10] |
Map function | arrayfun(@(x) x^2, A) |
list(map(lambda x: x**2, A)) |
map(x -> x^2, A) |
Filter function | A(A > 0) |
list(filter(lambda x: x > 0, A)) |
filter(x -> x > 0, A) |
Type checking | isa(x, 'double') |
isinstance(x, float) |
isa(x, Float64) |
Tuples | % Cell arrays serve similar purpose
A = {1, 'text', [1 2 3]} |
t = (1, 'text', [1, 2, 3])
# Named tuple
from collections import namedtuple
Point = namedtuple('Point', ['x', 'y'])
p = Point(1, 2) |
# Tuple
t = (1, "text", [1, 2, 3])
# Named tuple
t = (x=1, y=2, z=3)
t.x # Access element
# Unpacking
a, b, c = t |
Closures | function f = makemultiplier(x)
function g = mult(y)
g = x * y;
end
f = @mult;
end |
def make_multiplier(x):
def mult(y):
return x * y
return mult |
function make_multiplier(x)
return y -> x * y
end
# Or with mutable state
function counter()
count = 0
return () -> (count += 1)
end |
In-place modification | A(:) = B(:) |
A[:] = B[:] |
# Convention: ! indicates mutation
function f!(out, x)
out .= x.^2
end
x = rand(10)
y = similar(x)
f!(y, x)
# ODE example
function mysys!(du, u, p, t)
du[1] = u[2]
du[2] = -u[1]
end
# Modify array in-place
A .= B # Element-wise assignment
A .+= 1 # Add 1 to all elements |
Timing code | tic; % code; toc |
import time
start = time.time()
# code
print(time.time() - start) |
@time begin
# code
end |
Differential Equations
Single ODE: dy/dt = f(t,y)
Description | MATLAB | Python | Julia |
---|---|---|---|
Define ODE | f = @(t,y) -2*y + sin(t) |
def f(t, y):
return -2*y + np.sin(t) |
f(u, p, t) = -2*u + sin(t) |
Solve ODE | [t, y] = ode45(f, [0, 10], 1) |
from scipy.integrate import solve_ivp
sol = solve_ivp(f, [0, 10], [1]) |
prob = ODEProblem(f, 1.0, (0.0, 10.0))
sol = solve(prob) |
Specify time points | tspan = 0:0.1:10;
[t, y] = ode45(f, tspan, 1) |
t_eval = np.linspace(0, 10, 101)
sol = solve_ivp(f, [0, 10], [1],
t_eval=t_eval) |
# Save at regular intervals
sol = solve(prob, saveat=0.1)
# Save at specific times (same as 0:0.1:10)
t_save = 0:0.1:10
sol = solve(prob, saveat=t_save) |
System of ODEs
Description | MATLAB | Python | Julia |
---|---|---|---|
Define system | function dydt = mysys(t, y)
dydt = zeros(2,1);
dydt(1) = y(2);
dydt(2) = -y(1) - 0.1*y(2);
end |
def mysys(t, y):
return [y[1],
-y[0] - 0.1*y[1]] |
function mysys!(du, u, p, t)
du[1] = u[2]
du[2] = -u[1] - 0.1*u[2]
end |
Solve system | [t, y] = ode45(@mysys, [0, 20], [1; 0]) |
sol = solve_ivp(mysys, [0, 20], [1, 0]) |
u0 = [1.0, 0.0]
tspan = (0.0, 20.0)
prob = ODEProblem(mysys!, u0, tspan)
sol = solve(prob) |
Stiff solver | [t, y] = ode15s(@mysys, [0, 20], [1; 0]) |
sol = solve_ivp(mysys, [0, 20], [1, 0],
method='BDF') |
sol = solve(prob, FBDF()) |
DAEs (Mass Matrix Form): M*du/dt = f(u,p,t)
Description | MATLAB | Python | Julia |
---|---|---|---|
Define mass matrix DAE | % ROBER DAE problem
M = [1 0 0; 0 1 0; 0 0 0];
function dydt = rober(t, y)
k1 = 0.04; k2 = 3e7; k3 = 1e4;
dydt = [-k1*y(1) + k3*y(2)*y(3);
k1*y(1) - k2*y(2)^2 - k3*y(2)*y(3);
y(1) + y(2) + y(3) - 1];
end |
# Not possible in SciPy
# Assimulo/SUNDIALS exist but
# are less maintained libraries |
# ROBER DAE example
using DifferentialEquations
function rober(du, u, p, t)
y₁, y₂, y₃ = u
k₁, k₂, k₃ = p
du[1] = -k₁*y₁ + k₃*y₂*y₃
du[2] = k₁*y₁ - k₂*y₂^2 - k₃*y₂*y₃
du[3] = y₁ + y₂ + y₃ - 1
end
M = [1.0 0 0
0 1.0 0
0 0 0] # singular mass matrix
f_ode = ODEFunction(rober, mass_matrix=M)
u0 = [1.0, 0, 0]
tspan = (0.0, 1e-5)
p = [0.04, 3e7, 1e4]
prob = ODEProblem(f_ode, u0, tspan, p) |
Solve mass matrix DAE | options = odeset('Mass', M, ...
'MStateDependence', 'none');
y0 = [1; 0; 0];
[t, y] = ode15s(@rober, [0 1e-5], y0, options) |
# Not possible in SciPy |
sol = solve(prob, Rodas5())
# Automatically detects DAE and uses
# appropriate algorithm |
Fully Implicit DAEs: f(du/dt, u, p, t) = 0
Description | MATLAB | Python | Julia |
---|---|---|---|
Define implicit DAE | function res = implicitdae(t, y, yp)
% Residual form: res = f(t,y,y') = 0
res = [yp(1) - y(2);
yp(2) + 9.81 + y(3)*y(1);
y(1)^2 + y(2)^2 - 1];
end |
# Not possible in SciPy
# Assimulo/SUNDIALS exist but are
# less maintained third-party libraries
# Example would use assimulo.solvers.IDA |
using DifferentialEquations
function residual!(res, du, u, p, t)
res[1] = du[1] - u[2]
res[2] = du[2] + 9.81 + u[3]*u[1]
res[3] = u[1]^2 + u[2]^2 - 1
end
prob = DAEProblem(residual!, du0, u0, tspan,
differential_vars=[true,true,false]) |
Solve implicit DAE | [y0, yp0] = decic(@implicitdae, 0, ...
guess_y, [], guess_yp, []);
[t, y] = ode15i(@implicitdae, [0 10], y0, yp0) |
# Would require Assimulo installation
# and IDA solver setup |
using Sundials
sol = solve(prob, IDA())
# or DFBDF() for stiff DAEs |
Nonlinear Equation Solving
Single Equation
Description | MATLAB | Python | Julia |
---|---|---|---|
Find root | f = @(x) x^3 - 2*x - 5;
x = fzero(f, 2) |
from scipy.optimize import fsolve
f = lambda x: x**3 - 2*x - 5
x = fsolve(f, 2) |
using NonlinearSolve
f(u, p) = u^3 - 2u - 5
prob = NonlinearProblem(f, 2.0)
sol = solve(prob) |
With derivative | f = @(x) x^3 - 2*x - 5;
df = @(x) 3*x^2 - 2;
x = fzero(@(x) [f(x), df(x)], 2) |
from scipy.optimize import newton
f = lambda x: x**3 - 2*x - 5
df = lambda x: 3*x**2 - 2
x = newton(f, 2, fprime=df) |
f(u, p) = u^3 - 2u - 5
df(u, p) = 3u^2 - 2
prob = NonlinearProblem(f, 2.0)
sol = solve(prob, NewtonRaphson()) |
System of Equations
Description | MATLAB | Python | Julia |
---|---|---|---|
Define system | function F = mysys(x)
F = [x(1)^2 + x(2)^2 - 1;
x(1) - x(2)^2];
end |
def mysys(x):
return [x[0]**2 + x[1]**2 - 1,
x[0] - x[1]**2] |
function mysys!(F, x)
F[1] = x[1]^2 + x[2]^2 - 1
F[2] = x[1] - x[2]^2
end |
Solve | x0 = [1; 1];
x = fsolve(@mysys, x0) |
x0 = [1, 1]
x = fsolve(mysys, x0) |
using NonlinearSolve
x0 = [1.0, 1.0]
prob = NonlinearProblem(mysys!, x0)
sol = solve(prob) |
Optimization
⚠️ Python Compatibility Warning: SciPy optimization (used in this section) is incompatible with:
- PyTorch autodiff - Cannot use PyTorch gradients with scipy.optimize
- PyTorch optimizers (torch.optim) - Different API and tensor requirements
- CasADi optimization - Must use CasADi's own solvers
For gradient-based optimization with autodiff, PyTorch requires using torch.optim optimizers (Adam, SGD, etc.) with tensor inputs.
Unconstrained
Description | MATLAB | Python | Julia |
---|---|---|---|
Minimize scalar | f = @(x) (x-2)^2 + 3;
x = fminbnd(f, 0, 5) |
from scipy.optimize import minimize_scalar
f = lambda x: (x-2)**2 + 3
res = minimize_scalar(f, bounds=(0, 5),
method='bounded') |
using Optimization, OptimizationOptimJL
f(x, p) = (first(x)-2)^2 + 3
prob = OptimizationProblem(f, [2.5], lb=[0.0], ub=[5.0])
sol = solve(prob) |
Minimize multivariate | f = @(x) x(1)^2 + x(2)^2;
x0 = [0; 0];
x = fminsearch(f, x0) |
from scipy.optimize import minimize
f = lambda x: x[0]**2 + x[1]**2
x0 = [0, 0]
res = minimize(f, x0) |
f(x, p) = x[1]^2 + x[2]^2
prob = OptimizationProblem(f, [0.0, 0.0])
sol = solve(prob, Optim.LBFGS()) |
With gradient | options = optimoptions('fminunc',
'GradObj','on');
x = fminunc(@(x) deal(f(x), grad(x)),
x0, options) |
def grad(x):
return np.array([2*x[0], 2*x[1]])
res = minimize(f, x0, jac=grad) |
using OptimizationOptimJL, ADTypes
f(x, p) = x[1]^2 + x[2]^2
optf = OptimizationFunction(f, ADTypes.AutoForwardDiff())
prob = OptimizationProblem(optf, [0.0, 0.0])
sol = solve(prob, Optim.LBFGS()) |
Constrained
Description | MATLAB | Python | Julia |
---|---|---|---|
Box constraints | lb = [0; 0]; ub = [1; 1];
x = fmincon(f, x0, [], [], [], [], lb, ub) |
bounds = [(0, 1), (0, 1)]
res = minimize(f, x0, bounds=bounds) |
prob = OptimizationProblem(f, [0.5, 0.5],
lb=[0.0, 0.0], ub=[1.0, 1.0])
sol = solve(prob) |
Interpolation and Curve Fitting
Description | MATLAB | Python | Julia |
---|---|---|---|
Linear interpolation | x = [1, 2, 3, 4, 5];
y = [2, 4, 1, 3, 5];
xi = 1:0.1:5;
yi = interp1(x, y, xi, 'linear') |
from scipy import interpolate
f = interpolate.interp1d(x, y)
xi = np.linspace(1, 5, 41)
yi = f(xi) |
using DataInterpolations
itp = LinearInterpolation(y, x)
xi = 1:0.1:5
yi = itp.(xi) |
Cubic spline | yi = interp1(x, y, xi, 'spline') |
f = interpolate.interp1d(x, y, kind='cubic')
yi = f(xi) |
itp = CubicSpline(y, x)
yi = itp.(xi) |
Polynomial fit | p = polyfit(x, y, 2);
yfit = polyval(p, x) |
p = np.polyfit(x, y, 2)
yfit = np.polyval(p, x) |
using Polynomials
p = fit(x, y, 2)
yfit = p.(x) |
Nonlinear fit | model = @(b,x) b(1)*exp(b(2)*x);
b0 = [1, 0.1];
b = lsqcurvefit(model, b0, x, y) |
from scipy.optimize import curve_fit
def model(x, a, b):
return a * np.exp(b * x)
popt, pcov = curve_fit(model, x, y) |
using NonlinearSolve
function model!(resid, p, (x, y))
pred = p[1] * exp.(p[2] * x)
resid .= y .- pred
end
prob = NonlinearLeastSquaresProblem(model!, [1.0, 0.1], (x, y))
sol = solve(prob) |
Numerical Integration
Description | MATLAB | Python | Julia |
---|---|---|---|
1D integration | f = @(x) exp(-x.^2);
I = integral(f, 0, 1) |
from scipy import integrate
f = lambda x: np.exp(-x**2)
I, err = integrate.quad(f, 0, 1) |
using Integrals
f(x, p) = exp(-x^2)
prob = IntegralProblem(f, 0, 1)
sol = solve(prob, QuadGKJL())
I = sol.u |
2D integration | f = @(x,y) exp(-x.^2 - y.^2);
I = integral2(f, 0, 1, 0, 1) |
f = lambda y, x: np.exp(-x**2 - y**2)
I, err = integrate.dblquad(f, 0, 1, 0, 1) |
using Integrals
f(x, p) = exp(-x[1]^2 - x[2]^2)
prob = IntegralProblem(f, [0, 0], [1, 1])
sol = solve(prob, HCubatureJL())
I = sol.u |
Trapezoidal | x = linspace(0, 1, 100);
y = exp(-x.^2);
I = trapz(x, y) |
x = np.linspace(0, 1, 100)
y = np.exp(-x**2)
I = np.trapz(y, x) |
using DataInterpolations
x = range(0, 1, length=100)
y = exp.(-x.^2)
itp = LinearInterpolation(y, x)
I = DataInterpolations.integral(itp, first(x), last(x)) |
FFT and Signal Processing
Description | MATLAB | Python | Julia |
---|---|---|---|
FFT | y = fft(x);
freq = (0:length(x)-1)*fs/length(x); |
y = np.fft.fft(x)
freq = np.fft.fftfreq(len(x), 1/fs) |
using FFTW
y = fft(x)
freq = fftfreq(length(x), fs) |
Inverse FFT | x = ifft(y) |
x = np.fft.ifft(y) |
x = ifft(y) |
Power spectrum | [pxx, f] = pwelch(x, [], [], [], fs) |
from scipy import signal
f, pxx = signal.welch(x, fs) |
using DSP
pxx = welch_pgram(x, fs=fs) |
Filter (lowpass) | [b, a] = butter(4, 0.2);
y = filter(b, a, x) |
b, a = signal.butter(4, 0.2)
y = signal.filtfilt(b, a, x) |
using DSP
H = Butterworth(4, 0.2)
y = filt(H, x) |
Convolution | y = conv(x, h) |
y = np.convolve(x, h) |
y = conv(x, h) |
Statistics and Distributions
Statistical Functions
Description | MATLAB | Python | Julia |
---|---|---|---|
Mean, std | m = mean(x);
s = std(x); |
m = np.mean(x)
s = np.std(x) |
using Statistics
m = mean(x)
s = std(x) |
Median, quantiles | med = median(x);
q = quantile(x, [0.25, 0.75]); |
med = np.median(x)
q = np.quantile(x, [0.25, 0.75]) |
med = median(x)
q = quantile(x, [0.25, 0.75]) |
Correlation | R = corrcoef(x, y) |
R = np.corrcoef(x, y) |
R = cor(x, y) |
Distributions
Description | MATLAB | Python | Julia |
---|---|---|---|
Normal | x = randn(100, 1);
y = normrnd(mu, sigma, [100, 1]);
p = normpdf(x, mu, sigma); |
from scipy import stats
x = np.random.randn(100)
y = np.random.normal(mu, sigma, 100)
p = stats.norm.pdf(x, mu, sigma) |
using Distributions
d = Normal(mu, sigma)
x = rand(d, 100)
p = pdf(d, x) |
Uniform | x = rand(100, 1);
y = unifrnd(a, b, [100, 1]); |
x = np.random.rand(100)
y = np.random.uniform(a, b, 100) |
d = Uniform(a, b)
x = rand(d, 100) |
Fit to data | pd = fitdist(x, 'Normal');
params = pd.ParameterValues; |
params = stats.norm.fit(x) |
d = fit(Normal, x) |
Partial Differential Equations
1D Heat Equation: ∂u/∂t = α∂²u/∂x²
Description | MATLAB | Python | Julia |
---|---|---|---|
Method of lines | % Spatial discretization
nx = 50; dx = 1/(nx-1);
x = linspace(0, 1, nx)';
D2 = gallery('tridiag', nx, 1, -2, 1)/dx^2;
% Time integration
alpha = 0.01;
f = @(t,u) alpha*D2*u;
[t, u] = ode45(f, [0, 1], sin(pi*x)); |
# Spatial discretization
nx = 50
x = np.linspace(0, 1, nx)
dx = x[1] - x[0]
D2 = (np.diag(np.ones(nx-1), 1) -
2*np.diag(np.ones(nx), 0) +
np.diag(np.ones(nx-1), -1))/dx**2
# Time integration
alpha = 0.01
def f(t, u):
return alpha * D2 @ u
sol = solve_ivp(f, [0, 1], np.sin(np.pi*x)) |
# Spatial discretization
nx = 50
x = range(0, 1, length=nx)
dx = x[2] - x[1]
D2 = Tridiagonal(ones(nx-1), -2*ones(nx),
ones(nx-1))/dx^2
# Time integration
α = 0.01
f!(du, u, p, t) = mul!(du, α*D2, u)
prob = ODEProblem(f!, sin.(π*x), (0.0, 1.0))
sol = solve(prob) |
Symbolic Computing
⚠️ Python Compatibility Warning: SymPy (used for Python symbolic computing) is incompatible with:
- PyTorch tensors - SymPy uses its own symbolic types, cannot mix with tensors
- CasADi - Both are symbolic systems with incompatible representations
- SciPy operations - Cannot use SciPy functions on symbolic expressions
- NumPy arrays in symbolic expressions - must use SymPy operations
Basic Symbolic Operations
Description | MATLAB | Python | Julia |
---|---|---|---|
Define symbols | syms x y z |
import sympy as sp
x, y, z = sp.symbols('x y z') |
using Symbolics
@variables x y z |
Create expression | expr = x^2 + 2*x*y + y^2 |
expr = x**2 + 2*x*y + y**2 |
expr = x^2 + 2*x*y + y^2 |
Substitute values | subs(expr, x, 2) |
expr.subs(x, 2) |
substitute(expr, x => 2) |
Simplify | simplify(expr) |
sp.simplify(expr) |
simplify(expr) |
Expand | expand(expr) |
sp.expand(expr) |
expand(expr) |
Factor | factor(expr) |
sp.factor(expr) |
using SymbolicUtils
SymbolicUtils.simplify(expr) |
Generate function (out-of-place) | matlabFunction(expr, 'Vars', [x, y]) |
f = sp.lambdify([x, y], expr) |
f = build_function(expr, [x, y])
f_expr = eval(f[1]) |
Generate function (in-place) | % Not available natively |
# Not available in SymPy |
f = build_function(expr, [x, y])
f! = eval(f[2]) # mutating version |
Symbolic in numerical solver | % Not compatible with ODE solvers |
# Not compatible with scipy.integrate |
# Can use symbolic parameters in ODE solvers
using DifferentialEquations, Symbolics
@variables t x(t) a b
D = Differential(t)
eqs = [D(x) ~ a*x + b]
@named sys = System(eqs)
sys = complete(sys)
# Solve with symbolic parameters
u0 = [x => 1.0]
tspan = (0.0, 1.0)
p = [a => -2.0, b => 1.0]
prob = ODEProblem(sys, u0, tspan, p)
sol = solve(prob, Euler(), dt=0.25, adaptive=false) |
Calculus
Description | MATLAB | Python | Julia |
---|---|---|---|
Derivative | diff(expr, x) |
sp.diff(expr, x) |
D = Differential(x)
expand_derivatives(D(expr)) |
Partial derivative | diff(expr, x, 2) |
sp.diff(expr, x, 2) |
D = Differential(x)
expand_derivatives(D^2(expr)) |
Gradient | gradient(f, [x, y, z]) |
[sp.diff(f, var) for var in [x, y, z]] |
Symbolics.gradient(f, [x, y, z]) |
Jacobian | jacobian([f1; f2], [x, y]) |
sp.Matrix([f1, f2]).jacobian([x, y]) |
Symbolics.jacobian([f1, f2], [x, y]) |
Integral (indefinite) | int(expr, x) |
sp.integrate(expr, x) |
using SymbolicIntegration
symbolic_integrate(expr, x) |
Integral (definite) | int(expr, x, a, b) |
sp.integrate(expr, (x, a, b)) |
using Integrals, Symbolics
f = build_function(expr, x)
prob = IntegralProblem((u,p) -> f[1](u), (a, b))
solve(prob, QuadGKJL()) |
Equation Solving
Description | MATLAB | Python | Julia |
---|---|---|---|
Solve equation | solve(x^2 - 4 == 0, x) |
sp.solve(x**2 - 4, x) |
using Symbolics
symbolic_solve(x^2 - 4, x) |
Solve system | solve([x + y == 5, x - y == 1], [x, y]) |
sp.solve([x + y - 5, x - y - 1], [x, y]) |
using Symbolics
symbolic_solve([x + y ~ 5, x - y ~ 1], [x, y]) |
Automatic Differentiation
⚠️ Python Compatibility Warning: PyTorch (used for AD) is incompatible with:
- SciPy functions (scipy.integrate, scipy.optimize, etc.) - must use PyTorch equivalents or pure Python
- NumPy operations - must convert arrays to tensors and use torch functions
- CasADi (used in Component-Based Modeling section) - cannot mix symbolic and AD frameworks
Forward Mode AD
Description | MATLAB | Python | Julia |
---|---|---|---|
Setup | % Not available natively |
import torch |
using ForwardDiff |
Gradient (scalar → scalar) | % Not available natively
% Use symbolic or finite differences |
from torch.autograd.functional import jvp
def f(x):
return x**2 + torch.sin(x)
x = torch.tensor(2.0)
v = torch.tensor(1.0) # tangent vector
y, df = jvp(f, x, v) # df is derivative at x=2.0 |
f(x) = x^2 + sin(x)
df = ForwardDiff.derivative(f, 2.0) |
Gradient (vector → scalar) | % Not available natively |
from torch.autograd.functional import jvp
def f(x):
return torch.sum(x**2)
x = torch.tensor([1.0, 2.0])
# Compute gradient via multiple forward passes
grad = []
for i in range(2):
v = torch.zeros(2)
v[i] = 1.0
_, g = jvp(f, x, v)
grad.append(g)
grad = torch.stack(grad) |
f(x) = sum(x.^2)
grad = ForwardDiff.gradient(f, [1.0, 2.0]) |
Jacobian | % Not available natively |
from torch.autograd.functional import jacobian
def f(x):
return torch.stack([x[0]**2, x[0]*x[1]])
x = torch.tensor([1.0, 2.0])
jac = jacobian(f, x) |
f(x) = [x[1]^2, x[1]*x[2]]
jac = ForwardDiff.jacobian(f, [1.0, 2.0]) |
Hessian | % Not available natively |
from torch.autograd.functional import hessian
def f(x):
return torch.sum(x**2) # scalar function for Hessian
x = torch.tensor([1.0, 2.0])
hess = hessian(f, x) |
hess = ForwardDiff.hessian(f, [1.0, 2.0]) |
Dual numbers | % Not available natively |
# Not exposed |
using ForwardDiff
x = ForwardDiff.Dual(2.0, 1.0)
y = x^2 + sin(x)
# value: y.value
# derivative: y.partials[1] |
Consistency requirements | % N/A |
# Must replace ALL NumPy/SciPy calls:
# np.sin → torch.sin
# scipy.integrate → not compatible
# scipy.optimize → not compatible
# Cannot use with CasADi |
# ForwardDiff: requires generic functions
# (no explicit Float64 types)
# Must use generic operations
# e.g. zero(x) instead of 0.0 |
Reverse Mode AD (Backpropagation)
Description | MATLAB | Python | Julia |
---|---|---|---|
Setup | % Not available natively |
import torch |
using Enzyme |
Gradient | % Not available natively |
def f(x):
return x**2 + torch.sin(x)
x = torch.tensor(2.0, requires_grad=True)
y = f(x)
y.backward()
grad = x.grad # gradient at x=2.0 |
f(x) = x^2 + sin(x)
x = 2.0
dx = 0.0
autodiff(Reverse, f, Active, Duplicated(x, Ref(dx)))
# gradient is in dx |
Vector gradient | % Not available natively |
def f(x):
return torch.sum(x**2)
x = torch.tensor([1.0, 2.0], requires_grad=True)
y = f(x)
y.backward()
grad = x.grad # gradient at [1.0, 2.0] |
f(x) = sum(x.^2)
x = [1.0, 2.0]
dx = zero(x)
autodiff(Reverse, f, Active, Duplicated(x, dx))
# gradient is in dx |
Jacobian-vector product (JVP) | % Not available natively |
from torch.autograd.functional import jvp
def f(x):
return torch.stack([x[0]**2, x[0]*x[1], x[1]**2])
x = torch.tensor([2.0, 3.0])
v = torch.tensor([1.0, 0.0]) # tangent vector
y, jvp_result = jvp(f, x, v) |
f(x) = [x[1]^2, x[1]*x[2], x[2]^2]
x = [2.0, 3.0]
v = [1.0, 0.0] # tangent vector
y, jvp = autodiff(Forward, f, Duplicated, Duplicated(x, v))
# jvp contains J*v |
Vector-Jacobian product (VJP) | % Not available natively |
def f(x):
return torch.stack([x[0]**2, x[0]*x[1], x[1]**2])
x = torch.tensor([2.0, 3.0], requires_grad=True)
y = f(x)
v = torch.tensor([1.0, 0.0, 1.0]) # cotangent vector
y.backward(v)
vjp = x.grad # v^T * J |
function f!(y, x)
y[1] = x[1]^2
y[2] = x[1]*x[2]
y[3] = x[2]^2
end
x = [2.0, 3.0]
y = zeros(3)
dx = zeros(2)
dy = [1.0, 0.0, 1.0] # cotangent vector
autodiff(Reverse, f!, Const,
Duplicated(y, dy), Duplicated(x, dx))
# dx contains v^T * J |
Consistency requirements | % N/A |
# Must replace ALL NumPy/SciPy calls:
# np.exp → torch.exp
# scipy functions → not compatible
# No mixed tensor/array operations
# Incompatible with CasADi |
# Enzyme: works with most Julia code
# Including mutating functions, BLAS
# Type-stable code recommended
# for best performance |
Component-Based Modeling
⚠️ Python Compatibility Warning: This section uses CasADi for Python examples, which is incompatible with:
- PyTorch tensors - CasADi uses its own symbolic types
- SciPy solvers on CasADi functions - must use CasADi's own solvers
- NumPy operations on symbolic expressions - use CasADi operations
Julia's ModelingToolkit.jl uses the same solvers from earlier sections (DifferentialEquations.jl, NonlinearSolve.jl, Optimization.jl).
System Definition
Description | MATLAB | Python | Julia |
---|---|---|---|
Define ODE system (Cartesian pendulum) | % Use Simulink or write function |
import casadi as ca
x = ca.SX.sym('x')
y = ca.SX.sym('y')
vx = ca.SX.sym('vx')
vy = ca.SX.sym('vy')
lam = ca.SX.sym('lam')
states = ca.vertcat(x, y, vx, vy, lam)
odes = ca.vertcat(vx, vy, -lam*x, -lam*y - 9.81)
algs = x**2 + y**2 - 1
dae = {'x': states, 'ode': odes, 'alg': algs} |
using ModelingToolkit
@parameters t g L
@variables x(t) y(t) vx(t) vy(t) λ(t)
D = Differential(t)
eqs = [D(x) ~ vx,
D(y) ~ vy,
D(vx) ~ -λ * x/L,
D(vy) ~ -λ * y/L - g,
x^2 + y^2 ~ L^2]
@named pendulum = System(eqs) |
Simplify system | % Manual simplification |
# CasADi cannot do acausal simplification |
pendulum = complete(pendulum)
pendulum_simplified = mtkcompile(pendulum) |
Transient simulation | % Create function handle and solve |
# Note: CasADi cannot directly solve
# this DAE model - would need integrator
# setup with algebraic constraints,
# high index causes instability |
u0 = [x => 1.0, y => 0.0,
vx => 0.0, vy => 0.0]
tspan = (0.0, 10.0)
p = [g => 9.81, L => 1.0]
prob = ODEProblem(pendulum_simplified, u0, tspan, p)
sol = solve(prob) |
Steady state solutions | % Use fsolve or symbolic solve |
# CasADi has its own rootfinder
import casadi as ca
# Create rootfinder for algebraic equations
rf = ca.rootfinder('rf', 'newton', dae)
sol = rf(x0, p) |
ss_prob = NonlinearProblem(
pendulum_simplified, u0, p)
ss_sol = solve(ss_prob) |
Generate LaTeX | % Not available for Simulink models |
# Not available in CasADi |
using Latexify
latexify(pendulum) |
Component-Based Modeling
Description | MATLAB | Python | Julia |
---|---|---|---|
Create component | % Use Simulink blocks |
# No direct equivalent
# Consider PyMola or OpenModelica |
function Mass(; name, m = 1.0,
x = 0.0, v = 0.0)
@parameters t
@variables x(t) v(t) F(t)
@parameters m
D = Differential(t)
eqs = [D(x) ~ v,
D(v) ~ F/m]
System(eqs, t, [x, v, F], [m]; name)
end
@named mass1 = Mass(m = 2.0) |
Connect systems | % Simulink connections |
# Manual composition |
@named spring = Spring(k = 100)
@named damper = Damper(c = 10)
connections = [
spring.x ~ mass1.x,
damper.x ~ mass1.x,
mass1.F ~ spring.F + damper.F
]
@named model = System(connections, t,
systems = [mass1, spring, damper])
model = complete(model) |
Simplify | % Simulink cannot simplify |
# Manual |
model_simplified = mtkcompile(model) |