Skip to content

tririver/mathgr-py

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

14 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

MathGR-Py: A MCP/Python Port of MathGR for Tensor Calculus and General Relativity

mathgr is a Python/SymPy and MCP port of tririver/MathGR, a symbolic toolkit for tensor calculus, general relativity, ADM/FRW decompositions, perturbation series, integration by parts, and TeX export.

The package has two user-facing interfaces:

  • Python interface: normal Python/SymPy API for scripts, notebooks, tests, and direct human use.
  • MCP interface: designed for Codex, Claude Code, and other coding agents. It accepts Python-like expression strings, auto-declares tensors and indices, and exposes tools such as mathgr_compute, mathgr_parse, and mathgr_tex. Example agent call: mathgr_compute("Simp(Dta(U('α'), D('β')) * f(U('β')))").

Wolfram is not needed. The test suite translates upstream MathGR tests and notebook cells to pytest, with an optional Wolfram oracle test for checking the original package.

MathGR-Py is Unicode-first for common Greek physics symbols. Prefer names such as φ, δφ, φ0, ζ, ε, η, α, and β in Python and MCP expressions. Use normal index constructors with UTF-8 labels when useful, for example U1("α"), D1("β"), or DN("i").

Why Python and SymPy?

Wolfram Language is concise and pleasant for human symbolic work. This port exists because Python is easier for AI coding agents to inspect, run, test, modify, and expose through MCP tools. The initial motivation was better agent interaction with MathGR; it is also useful for humans who prefer a Python/SymPy workflow.

Python Interface

Use the Python interface for scripts, notebooks, tests, and direct work in a Python process.

Python Install

From this repository:

uv sync

Use it in another editable environment:

python -m pip install -e .

Requirements:

  • Python 3.12 or newer
  • SymPy 1.13 or newer

Python Usage

Most notebooks can start from the package root import:

import sympy as sp
from mathgr import *

For scripts and libraries, prefer explicit imports from mathgr, mathgr.tensor, mathgr.gr, mathgr.decomp, mathgr.frwadm, mathgr.util, mathgr.ibp, and mathgr.typeset.

Note: from mathgr import tensor imports the tensor-head factory. To inspect the mathgr.tensor submodule itself, use:

import importlib

tensor_module = importlib.import_module("mathgr.tensor")

Declare indices and tensors:

from mathgr import LatinIdx, declare_idx, tensor, Dta, Simp

u, d = declare_idx("U", "D", dim=3, index_set=LatinIdx)
f = tensor("f")

expr = Dta(u("a"), d("b")) * f(u("b"))
print(Simp(expr))

Output:

f(U('a'))

Add tensor symmetries:

from mathgr import DeclareSym, Symmetric

S = tensor("S")
DeclareSym(S, (d, d), Symmetric((1, 2)))

print(Simp(S(d("b"), d("a")) - S(d("a"), d("b"))))

Output:

0

Differentiate symbolically:

from mathgr import Pd

h = tensor("h")
expr = f(u("a")) * h(d("b"))

print(Pd(expr, d("c")))

Pd applies product, sum, and supported power rules and stores derivatives as PdT(expr, PdVars(...)).

Use a metric and GR identities:

from mathgr import LatinIdx, declare_idx, tensor, UseMetric, R, CovD, Simp

u4, d4 = declare_idx("U4", "D4", dim=4, index_set=LatinIdx)
g4 = tensor("g4")
UseMetric(g4, (u4, d4))

second_bianchi = (
    CovD(R(d4("a"), d4("b"), d4("c"), d4("d")), d4("e"))
    + CovD(R(d4("a"), d4("b"), d4("d"), d4("e")), d4("c"))
    + CovD(R(d4("a"), d4("b"), d4("e"), d4("c")), d4("d"))
)

print(Simp(second_bianchi))

Output:

0

Use UTF-8 Greek scalar names directly:

import sympy as sp
import mathgr.typeset as typeset
from mathgr import D1, Pd, ToTeXString

δφ = sp.Symbol("δφ")
typeset.ToTeXTemplate = False
print(Pd(δφ, D1("α")))
print(ToTeXString(Pd(δφ, D1("α"))))

Output:

_PdT(δφ, _PdVars(D1('α')))
\partial_{α} δφ

Split time and space indices:

from mathgr import Decomp0i, Simp, tensor
from mathgr.decomp import UTot, DTot

f = tensor("f")
expr = Decomp0i(f(UTot("mu"), DTot("mu")))

print(expr)
print(Simp(expr))

Output:

f(UE(0), DE(0)) + f(UP('mu'), DN('mu'))
f(UE(0), DE(0)) + f(UP('a'), DN('a'))

Expand perturbation series:

import sympy as sp
from mathgr import Eps, SS, OO

x = sp.symbols("x")
expr = (1 + Eps * x) ** 3

print(SS(2)(expr))
print(OO(2)(expr))

Output:

3*Eps**2*x**2 + 3*Eps*x + 1
3*x**2

Export TeX:

import mathgr.typeset as typeset
from mathgr import DN, UP, Pd, tensor

previous = typeset.ToTeXTemplate
try:
    typeset.ToTeXTemplate = False
    f = tensor("f")
    print(typeset.ToTeXString(f(UP("a"), DN("b"))))
    print(typeset.ToTeXString(Pd(f(DN("i")), DN("j"))))
finally:
    typeset.ToTeXTemplate = previous

Output:

f^{a}_{b}
\partial_{j} f_{i}

Run examples:

uv run python -m examples.general_presentation
uv run python -m examples.decomp_example
uv run python -m examples.second_order_pert
uv run python -m examples.zeta_gauge_action_from_delta_phi

Larger notebook ports may be expensive if you enable full action computation. Many expose flags such as main(compute_action=False).

See doc/manual.md for a more complete API manual.

Create release/source archives from git so virtual environments and caches stay out of the upload:

git archive --format=zip --output mathgr-py-src.zip HEAD

Default local test workflow:

tools/test.sh

This runs fast tests serially, then the slow symbolic bucket with pytest-xdist; override workers with PYTEST_SLOW_WORKERS=8 tools/test.sh. Use uv run pytest -q only when you want the full serial release check.

MCP Interface

Use the MCP interface when you want a coding agent to call MathGR directly after startup. The MCP surface is intentionally different from raw Python: agents send Python-like expression strings and the server auto-declares common objects.

MCP Install

For Codex or Claude Code, you usually do not need to install the MCP server by hand. Start your agent in this repository and ask:

Read the Agent MCP Install Recipe at the end of README.md and install MathGR MCP
for your own future sessions.

The agent should follow that recipe, update its own MCP configuration, then tell you to start a new Codex or Claude Code session. New sessions can call the mathgr MCP tools immediately.

Manual smoke check from this repository:

uv sync
timeout 2s uv run mathgr-mcp

Exit code 0 or 124 from timeout is acceptable. A Python traceback is not.

Portable user-level install:

tools/install_mcp.sh

This stores a uvx --from git+https://github.com/tririver/mathgr-py command in the agent MCP config, so new sessions can start MathGR from any directory and do not depend on this checkout path still existing. Use tools/install_mcp.sh --dev-local only when this repository path is permanent and you intentionally want MCP sessions to run the local checkout.

MCP Usage

Core tools:

  • mathgr_manual: read the full manual or a named section
  • mathgr_compute: first-choice tool for almost all MathGR calculations; auto-declare and evaluate a Python-like expression or notebook block
  • mathgr_tex: render an expression to TeX
  • mathgr_context_get: list stored context declarations and expression source definitions
  • mathgr_context_clear: reset the default or named context
  • mathgr_context_save, mathgr_context_load: save/load context JSON files
  • mathgr_parse: only for debugging; dry-run a Python-like expression and show inferred declarations plus reproducible Python
  • mathgr_inspect: only for debugging; list indices, free/dummy labels, tensor heads, derivative nodes, and Pm2 nodes
  • mathgr_script: only for debugging/reproduction; export Python for a structured calculation
  • mathgr_capabilities: compact API group list
  • mathgr_run_python / mathgr_eval: last-resort debugging escape hatch for raw trusted Python

Agents should use mathgr_compute first for almost every calculation. The compute tool is easier than raw Python because it auto-declares ordinary scalar names, tensor heads, index families, and a persistent default context. For most derivations, do not predefine scalar symbols, tensor heads, or temporary names just to make them exist; write the calculation directly, using multi-line mathgr_compute assignments only when a derived expression should be reused. Use mathgr_parse, mathgr_inspect, and mathgr_script only for debugging. Use mathgr_run_python / mathgr_eval only when mathgr_compute cannot express the workflow. For ordinary calls, pass only the expression string and omit optional fields such as context, output, and timeout_seconds. JSON objects are only the MCP transport format; examples and traces should prefer mathgr_compute("..."). Structured MCP tools default to timeout_seconds=0, which runs in-process for low latency. Set a positive timeout only for risky or potentially long symbolic calls that need subprocess cancellation.

Example:

mathgr_compute("Simp(Dta(U('α'), D('β')) * f(U('β')) + x)")

The MCP server infers:

Dim = sp.Symbol("Dim")
U, D = declare_idx("U", "D", dim=Dim)
f = tensor("f")
x = sp.Symbol("x")

Typical MCP calls:

mathgr_compute("Simp(Dta(U('α'), D('β')) * f(U('β')))")
mathgr_compute("Pd(δφ, D1('α'))")
mathgr_compute("Decomp0i(f(DTot('a')) * f(UTot('a')))")
mathgr_compute("Ibp(y * Pd(x, D('i')))")
mathgr_compute("OO(2)((1 + Eps*x)**3)")
mathgr_compute("Simp(lhs - rhs)")
mathgr_tex("Pd(f(U('a')), D('i'))")

MCP Cookbook

Perturbation expansion:

mathgr_compute("""
L2 = Simp(OO(2)(TSeries(L, (Eps, 0, 3))))
result = L2
""")

Derivative conventions:

notation MCP expression
dot(f) Pd(f, DE(0))
partial_i f Pd(f, DN("i"))
partial_i partial_j f Pd(Pd(f, DN("i")), DN("j"))

When calling the MCP tool with a multi-line block, pass the block body as expr. Do not include Python triple-quote delimiters inside the MCP expr value. Triple quotes are only a convenient way to show multi-line examples in this README.

Flat-gauge ADM scalar-field setup:

mathgr_compute("""
N = 1 + Eps*α
Ni = Eps*Pd(β, DN("i"))
φ = φ0 + Eps*δφ
φdot = Pd(φ, DE(0))
gradφ = Pd(φ, DN("i"))
result = Ni + φdot
""")

For the full ADM scalar-field density, use the FRW ADM helpers as the starting point and then extract the desired perturbative order:

mathgr_compute("""
φ = φ0 + Eps*δφ
i = sp.Wild("i")
flat_rules = {b(DN(i)): 0, Pd(φ0, DN(i)): 0}
L = frwadm.Sqrtg * (frwadm.RADM()/2 + frwadm.DecompG2H(lambda: X(φ)) - V(φ))
L_flat = TReplace(flat_rules)(L.xreplace({ζ: 0}))
L2 = frwadm.Simp(OO(2)(TSeries(L_flat, (Eps, 0, 3))))
result = L2
""", timeout_seconds=30)

Constraint extraction by integration by parts:

mathgr_compute("""
beta_terms = Simp(IbpVariation(L2, β))
constraint_beta = Simp(beta_terms.coeff(β))
result = constraint_beta
""")

Use IbpVariation(expr, β) to move derivatives off β, then take the coefficient of β. Use IbpNB when you want boundary holders dropped.

TeX output:

mathgr_tex("L2", fragment=True)
mathgr_compute("Simp(lhs - rhs)")

Use raw MCP TeX for exact, checkable output tied to the expression tree. For a paper or note, hand-clean notation after the algebra is verified; keep the machine expression for zero-checks such as Simp(lhs - rhs).

Override dimensions when needed:

{"index_dims": {"U/D": 3}}

Use the default context for normal multi-step calculations. Omit context unless you intentionally need parallel or incompatible branches:

mathgr_compute("""
trace = Dta(U('a'), D('a'))
simplified = Simp(Dta(U('a'), D('b')) * f(U('b')))
result = trace
""")
mathgr_compute("simplified")

Result:

f(U('a'))

If context is omitted, mathgr_compute uses and auto-creates "default". Assignments in a compute block persist in that context:

mathgr_compute("""
trace = Dta(U('a'), D('a'))
result = Simp(trace)
""")
mathgr_compute("trace")
mathgr_context_get()

result = ... controls the returned value but is not persisted as context state. Use a regular assignment, or store_as, for durable values.

Named contexts are for explicit forks only, such as comparing baseline and gauge_fixed states. Do not create new contexts for ordinary retries or probes; keep using the default context so traces remain readable.

Top-level declarations persist too:

mathgr_compute("""
gMcp = tensor("gMcp")
UseMetric(gMcp, (U, D))
F = tensor("F")
DeclareSym(F, (D, D), Antisymmetric((1, 2)))
result = Simp(F(D('a'), D('a')))
""")

Compute blocks intentionally reject imports, loops, function/class definitions, with, private attributes, and unsafe builtins. Restricted expression-only lambdas are allowed for local hooks and callbacks. If a scalar name collides with a preloaded API name, pass symbols=[...] to make that name a SymPy symbol for the call/context.

Context files survive agent restarts:

mathgr_context_save(context="default", path=".mathgr/contexts/default.json")
mathgr_context_load(path=".mathgr/contexts/default.json", context="default")

Security note: raw Python tools are intended for trusted symbolic snippets from the agent session, not as a sandbox for hostile code. They run snippets in a child process with a timeout, restore MathGR global state after each call, and block common filesystem/shell/import paths, but Python execution is not a security boundary.

Agent MCP Install Recipe

This section is written for Codex, Claude Code, or another coding agent. If a human asks you to install MathGR MCP, execute these steps from this repository. Use the portable install by default. It records a GitHub uvx command in the user MCP config, not the current checkout path, so it keeps working after a temporary Docker or scratch directory is removed.

Portable user install

  1. Install for all available supported agents:
tools/install_mcp.sh

Equivalent explicit Codex command:

codex mcp remove mathgr || true
codex mcp add mathgr -- env -u VIRTUAL_ENV -u PYTHONPATH \
  /usr/local/bin/uvx --python 3.14 \
  --from git+https://github.com/tririver/mathgr-py \
  mathgr-mcp
codex mcp get mathgr

Equivalent explicit Claude Code command:

claude mcp remove mathgr || true
claude mcp add --scope user mathgr -- env -u VIRTUAL_ENV -u PYTHONPATH \
  /usr/local/bin/uvx --python 3.14 \
  --from git+https://github.com/tririver/mathgr-py \
  mathgr-mcp
claude mcp get mathgr

Once release tags exist, prefer a pinned source such as:

--from git+https://github.com/tririver/mathgr-py@v0.1.0

Development checkout install

Use this only when the absolute repository path is persistent. This records that path in the agent MCP config so MCP sessions run your local checkout.

  1. Resolve the absolute repo path:
REPO="$(pwd)"
  1. Install runtime dependencies and smoke-check the stdio MCP command:
uv --directory "$REPO" sync
timeout 2s uv --directory "$REPO" run mathgr-mcp

Exit code 0 or 124 from timeout is acceptable. Any Python traceback is not acceptable; debug before continuing.

  1. Install for Claude Code if claude is available:
claude mcp remove mathgr || true
claude mcp add --scope user mathgr -- uv --directory "$REPO" run mathgr-mcp
claude mcp get mathgr

This user-scope install makes MathGR MCP available to new Claude Code sessions on this machine as long as $REPO keeps existing. Claude Code can also use this repo's project-scoped .mcp.json when started in the repo and approved by the user.

  1. Install for Codex if codex is available:
codex mcp remove mathgr || true
codex mcp add mathgr -- uv --directory "$REPO" run mathgr-mcp
codex mcp get mathgr

This user-level install makes MathGR MCP available to new Codex sessions on this machine as long as $REPO keeps existing. Codex can also use this repo's project-scoped .codex/config.toml after the repo is trusted.

Docker image install

For disposable containers or fresh scratch directories, use the portable uvx install above. If the image should run a baked-in checkout instead, copy the repo to a stable path such as /opt/mathgr-py and use the development checkout install with REPO=/opt/mathgr-py.

Finish

  • Start a new Codex or Claude Code session so the MCP server is loaded at startup.
  • In Codex, /mcp shows active MCP servers.
  • In Claude Code, /mcp shows connection state and tool count.
  • Ask the agent to call mathgr_capabilities to verify MathGR tools are live.

About

No description, website, or topics provided.

Resources

Stars

1 star

Watchers

0 watching

Forks

Releases

No releases published

Packages

 
 
 

Contributors