Skip to main content

Python/NumPy Compilation Target

The Python compilation target generates NumPy-compatible Python code from mathematical expressions, enabling seamless integration with scientific Python workflows.

Overview​

The PythonTarget class compiles Compute Engine expressions to Python code that uses NumPy for numerical operations. The generated code is:

  • Vectorized: Works with both scalars and NumPy arrays
  • Readable: Clean, idiomatic Python code
  • Efficient: Leverages NumPy's optimized implementations
  • Compatible: Works with SciPy, Pandas, and other scientific Python libraries

Basic Usage​

import { ComputeEngine, PythonTarget } from '@cortex-js/compute-engine';

const ce = new ComputeEngine();
const python = new PythonTarget();

// Compile a simple expression
const expr = ce.parse('x^2 + y^2');
const code = python.compile(expr);
console.log(code); // "x ** 2 + y ** 2"

Using with compile()​

To use the Python target with compile(), register it first:

import { ComputeEngine, PythonTarget, compile } from '@cortex-js/compute-engine';

const ce = new ComputeEngine();

// Register the Python target (only need to do this once)
ce.registerCompilationTarget('python', new PythonTarget({ includeImports: true }));

// Now you can use it throughout your code
const expr = ce.parse('\\sin(x) + \\cos(y)');
const result = compile(expr, { to: 'python' });
console.log(result.code);
// → import numpy as np
//
// np.sin(x) + np.cos(y)

Features​

1. Expression Compilation​

Convert mathematical notation to Python code:

const python = new PythonTarget();

// Polynomial
ce.parse('x^4 + 3x^3 + 2x^2 + x + 1');
// → "x ** 4 + 3 * x ** 3 + 2 * x ** 2 + x + 1"

// Trigonometric
ce.parse('\\sin(x) + \\cos(y)');
// → "np.sin(x) + np.cos(y)"

// Square root
ce.parse('\\sqrt{x^2 + y^2}');
// → "np.sqrt(x ** 2 + y ** 2)"

// Constants
ce.parse('2\\pi r');
// → "2 * np.pi * r"

2. Function Generation​

Generate complete Python functions:

const python = new PythonTarget({ includeImports: true });

const expr = ce.parse('\\sqrt{(x_2-x_1)^2 + (y_2-y_1)^2}');
const func = python.compileFunction(
expr,
'euclidean_distance',
['x_1', 'y_1', 'x_2', 'y_2'],
'Calculate Euclidean distance between two points'
);

Generates:

import numpy as np

def euclidean_distance(x_1, y_1, x_2, y_2):
"""Calculate Euclidean distance between two points"""
return np.sqrt((x_2 - x_1) ** 2 + (y_2 - y_1) ** 2)

3. Lambda Functions​

Create Python lambda expressions:

const expr = ce.parse('x^2 + 2x + 1');
const lambda = python.compileLambda(expr, ['x']);
// → "lambda x: x ** 2 + 2 * x + 1"

4. Vectorized Functions​

Generate NumPy-vectorized functions:

const expr = ce.parse('\\sin(x) * \\cos(x)');
const func = python.compileVectorized(expr, 'trig_product', ['x']);

Generates:

import numpy as np

def _trig_product_scalar(x):
return np.sin(x) * np.cos(x)

# Vectorized version
trig_product = np.vectorize(_trig_product_scalar)

Supported Functions​

Trigonometric​

  • sin, cos, tan → np.sin, np.cos, np.tan
  • arcsin, arccos, arctan → np.arcsin, np.arccos, np.arctan
  • sinh, cosh, tanh → np.sinh, np.cosh, np.tanh

Exponential & Logarithmic​

  • exp → np.exp
  • ln → np.log
  • log10 → np.log10
  • log2 → np.log2

Power & Roots​

  • x^n → x ** n
  • sqrt → np.sqrt
  • power → np.power

Rounding​

  • abs → np.abs
  • floor → np.floor
  • ceiling → np.ceil
  • round → np.round

Statistics​

  • sum → np.sum
  • mean → np.mean
  • median → np.median
  • std → np.std

Linear Algebra​

  • dot → np.dot
  • cross → np.cross
  • norm → np.linalg.norm
  • det → np.linalg.det
  • inv → np.linalg.inv

Configuration Options​

new PythonTarget({
includeImports: true, // Add "import numpy as np" to output
useScipy: false // Include scipy.special for advanced functions
})

Real-World Examples​

Physics: Kinematic Equation​

const expr = ce.parse('u \\cdot t + \\frac{1}{2} a \\cdot t^2');
const kinematics = python.compileFunction(
expr,
'position',
['u', 'a', 't'],
'Position with initial velocity u, acceleration a, at time t'
);

Generates:

import numpy as np

def position(u, a, t):
"""Position with initial velocity u, acceleration a, at time t"""
return 0.5 * a * t ** 2 + t * u

Mathematics: Quadratic Formula​

const expr = ce.parse('\\frac{-b + \\sqrt{b^2 - 4ac}}{2a}');
const quadratic = python.compileFunction(
expr,
'quadratic_root',
['a', 'b', 'c'],
'Calculate one root of a quadratic equation'
);

Generates:

import numpy as np

def quadratic_root(a, b, c):
"""Calculate one root of a quadratic equation"""
return (-b + np.sqrt(b ** 2 + -(4 * a * c))) / (2 * a)

Statistics: Gaussian Function​

const expr = ce.parse('e^{-x^2}');
const gaussian = python.compileFunction(
expr,
'gaussian',
['x'],
'Unnormalized Gaussian function'
);

Generates:

import numpy as np

def gaussian(x):
"""Unnormalized Gaussian function"""
return np.e ** (-x ** 2)

Use Cases​

1. Scientific Computing​

Generate NumPy code for numerical analysis and simulations:

# Generated function works with arrays
import numpy as np

# ... generated function here ...

x = np.linspace(0, 10, 100)
y = gaussian(x) # Vectorized operation

2. Machine Learning​

Create feature engineering functions:

const expr = ce.parse('\\frac{x - \\mu}{\\sigma}');
const normalize = python.compileFunction(expr, 'z_score', ['x', 'mu', 'sigma']);
// Use in ML pipeline for feature normalization

3. Data Analysis​

Convert formulas to vectorized Pandas operations:

import pandas as pd
# Generated function
def calculate_metric(x, y):
return np.sqrt(x ** 2 + y ** 2)

# Use with DataFrames
df['result'] = calculate_metric(df['col1'], df['col2'])

4. Education​

Show Python equivalents of mathematical notation:

// Students see: ∑(xᵢ - μ)²/n
const expr = ce.parse('\\frac{\\sum(x - \\mu)^2}{n}');
const code = python.compile(expr);
// Students learn: np.sum((x - mu) ** 2) / n

5. Code Generation​

Automated function creation from specifications:

// Read formulas from config/database
const formulas = loadFormulas();
formulas.forEach(formula => {
const expr = ce.parse(formula.latex);
const code = python.compileFunction(expr, formula.name, formula.params);
writeToFile(`generated/${formula.name}.py`, code);
});

Integration Examples​

With Jupyter Notebooks​

# Cell 1: Generated functions
%load generated_functions.py

# Cell 2: Use with data
import numpy as np
import matplotlib.pyplot as plt

x = np.linspace(-5, 5, 1000)
y = gaussian(x)

plt.plot(x, y)
plt.title('Generated Gaussian Function')
plt.show()

With SciPy​

// Generate function using SciPy special functions
const python = new PythonTarget({ includeImports: true, useScipy: true });
const expr = ce.parse('\\Gamma(x)');
const code = python.compile(expr);
// → Uses scipy.special.gamma

Complete Pipeline​

// 1. Parse LaTeX from document
const latex = extractLatexFromDocument('paper.tex');

// 2. Compile to Python
const python = new PythonTarget({ includeImports: true });
const functions = latex.formulas.map(f =>
python.compileFunction(ce.parse(f.equation), f.name, f.variables)
);

// 3. Generate Python module
const module = functions.join('\n\n');
fs.writeFileSync('generated_math.py', module);

// 4. Use in Python
// from generated_math import *

Comparison with Other Targets​

FeatureJavaScriptGLSLPython
ExecutionNode.js/BrowserGPU (WebGL)Python Runtime
Use CaseCPU ComputationGraphics/ParallelScientific Computing
OutputExecutable FunctionShader Code (String)Python Code (String)
PerformanceFast (JIT)Very Fast (Parallel)Fast (NumPy/C)
IntegrationDirect in JSWebGL ContextImport in Python

Best Practices​

1. Choose the Right Target​

  • JavaScript: Browser/Node.js execution, immediate use
  • Python: Scientific workflows, NumPy integration
  • GLSL: GPU parallel computation, graphics

2. Handle Imports Properly​

// For standalone scripts
const python = new PythonTarget({ includeImports: true });

// For modules (import at top of file separately)
const python = new PythonTarget({ includeImports: false });

3. Vectorization​

Generated functions work with both scalars and arrays:

# Scalar
result = euclidean_distance(1, 2, 4, 6) # Single value

# Arrays (element-wise)
x1 = np.array([1, 2, 3])
y1 = np.array([1, 2, 3])
x2 = np.array([4, 5, 6])
y2 = np.array([4, 5, 6])
result = euclidean_distance(x1, y1, x2, y2) # Array of distances

4. Type Safety​

Remember that generated Python code is untyped. Add type hints manually if needed:

def euclidean_distance(x_1: float, y_1: float, x_2: float, y_2: float) -> float:
return np.sqrt((x_2 - x_1) ** 2 + (y_2 - y_1) ** 2)

Non-Finite Values​

The engine tells the signed infinities +\infty and -\infty apart from complex infinity \tilde\infty, and both from NaN. NumPy floats have only np.inf, -np.inf and np.nan, so the generated code carries a projection of the interpreted value:

const python = new PythonTarget();

python.compile(ce.parse('\\infty')); // → "np.inf"
python.compile(ce.parse('-\\infty')); // → "-np.inf"
python.compile(ce.box(NaN)); // → "np.nan"

The signed infinities and NaN survive the trip unchanged. Complex infinity does not: it has no direction, and there is no NumPy float that means "infinite magnitude, direction unknown", so it projects onto np.inf. That is what you see at a pole, where the interpreter answers \tilde\infty but the folded constant is positive:

python.compile(ce.parse('\\frac{1}{0}')); // → "np.inf"
python.compile(ce.parse('\\ln(0)')); // → "-np.inf" (genuinely signed)

Absence markers project the same way. An unmatched cases expression evaluates to Missing in the interpreter, which is not a number; the generated Python falls through to float('nan'):

python.compile(ce.parse('\\begin{cases} x^2 & x > 0 \\\\ -x & x < 0 \\end{cases}'));
// → "((x ** 2) if (0 < x) else ((-x) if (x < 0) else float('nan')))"

Keep the projection in mind when reading a type off an expression before compiling it. Every bare numeric type name — integer, real, complex — denotes a finite value, so a symbol typed real never stands for np.inf; the non-finite values belong to the separate infinity and nan types.

Limitations​

  1. Not Executable in JavaScript: Python code must be run in a Python environment
  2. Type Information Lost: Generated code is untyped (can add hints manually)
  3. Some Simplifications: Expressions are canonicalized (e.g., x/2 → 0.5 * x)
  4. Requires NumPy: Most functions need NumPy to be installed
  5. Complex Infinity Is Flattened: \tilde\infty compiles to np.inf — see Non-Finite Values

See Also​