xAct Oracle Quirks
Reserved names: avoid N, I, E, C, D, O as symbol names. Context isolation: use context_id parameter to prevent symbol pollution. Key gotcha: ToCanonical only handles mono-term symmetries, not Bianchi identity.
This document captures quirks, edge cases, and gotchas discovered while working with xAct through the Oracle HTTP server.
Symbol Context Pollution (RESOLVED)
Status: Resolved (2026-01-26). Symbol isolation is now handled via the
context_idparameter to ensure a clean evaluation environment.
The Problem
When using wolframclient to evaluate expressions, symbols are parsed in Global` context before xAct sees them. This causes:
- Tensors like
S[-a,-b]becomeGlobalS[Times[-1, Globala], ...]instead of xAct tensor expressions ToCanonicaland other xAct functions don't recognize the expressions as tensors- Curvature tensors like
RiemannCDare created inGlobal` context and not treated as proper xAct curvatures
Solution Implemented
The Oracle now supports a context_id parameter for /evaluate-with-init. When provided:
- Expression is wrapped in
Begin["xActxTensor"]; ToExpression[...]; End[] ToExpressiondelays parsing until after context is set- All symbols are created in
xActxTensor`` context - xAct functions properly recognize tensors
Usage
# Python client
client.evaluate_with_xact(expr, context_id="unique_id")
# Or via HTTP
POST /evaluate-with-init
{"expr": "...", "context_id": "unique_id"}Test Fixture
@pytest.fixture
def context_id() -> str:
return uuid.uuid4().hex[:8]Loading & Initialization
xAct Load Time
- Loading xAct (
Needs["xActxTensor"]) takes ~2-3 seconds on first call (persistent kernel) - xAct is loaded once and reused across all
/evaluate-with-initcalls - Subsequent calls complete in ~5-10ms (kernel already initialized)
- Mark integration tests with
@pytest.mark.slowto skip during normal development
Index Naming
- xAct requires indices to be defined with the manifold:
DefManifold[M, 4, {a,b,c,d}] - Using undefined indices causes cryptic errors
- Indices are case-sensitive
- Do NOT use
Nas a manifold name — it's Mathematica's built-in numeric conversion function
Reserved Names to Avoid
N— Mathematica's numeric conversionI— imaginary unitE— Euler's numberC— used for constants in solutionsD— derivative operatorO— big-O notation
Output Format
Unicode Characters
- xAct may output Greek letters as Unicode:
μ,νinstead of\[Mu],\[Nu] - Normalization pipeline should handle both forms
- Example:
T[-μ,-ν]andT[-\[Mu],-\[Nu]]should normalize identically
Dummy Index Naming
- xAct generates internal dummy indices like
$1234 - These may appear in output even for simple expressions
- Normalization must canonicalize these to
$1, $2, ...
FullForm vs InputForm
- wolframclient returns expressions in Python object form, converted via
str() - This produces FullForm-like output:
Times[-1, a]instead of-a - Comparisons must account for these format differences
Tensor Operations
DefTensor Syntax
(* Correct: indices with positions *)
DefTensor[T[-a,-b], M, Symmetric[{-a,-b}]]
(* Wrong: missing index positions *)
DefTensor[T[a,b], M, Symmetric[{a,b}]] (* May silently fail *)ToCanonical Behavior
ToCanonicalreturns the canonical form but may not simplify to zero- For testing equality, use
ToCanonical[expr1 - expr2]and check for0 - Sometimes
Simplifyis needed afterToCanonical - ToCanonical only works if tensors are properly registered with xAct (see Context Pollution issue)
Metric Contraction
- Use
ContractMetricexplicitly; it's not automatic - Order matters:
g[a,b] V[-b] // ContractMetric≠ContractMetric[g[a,b]] V[-b]
DefMetric Functions
SignDetOfMetric[g]— returns the sign of the metric determinant (-1 for Lorentzian)SignatureOfMetric[g]— throwsHold[Throw[None]]in some cases (may require explicit signature spec)- Use
SignDetOfMetricfor testing metric properties
Comparator Implications
Tier 1: Normalized Comparison
- Most xAct outputs differ only in dummy index naming
- Proper normalization handles the majority of equality cases
Tier 2: Symbolic Simplify
Simplify[(expr1) - (expr2)]works for most algebraic expressions- May timeout for complex tensor expressions
- xAct-specific simplification rules require xAct context
Tier 3: Numeric Sampling
- Tensor expressions with free indices cannot be directly sampled
- Need to substitute concrete index values or use trace operations
- Fallback for expressions Simplify cannot handle
- Works for scalar expressions (Sin[x]^2 + Cos[x]^2 == 1)
Known Issues
Session State
- The Oracle uses a persistent Wolfram kernel via WSTP (wolframclient)
- Tensor definitions persist across API calls within the same kernel session
- The kernel restarts automatically on timeout or error (xAct is reloaded)
- Symbol pollution: once a symbol is created in
Global`, it stays there
Error Messages
- xAct errors are often unhelpful:
"Syntax error" Hold[Throw[None]]indicates an internal xAct exception was caught- Check that all indices are defined before use
- Verify manifold dimensions match tensor rank
Integration Tests
37 integration tests across three suites verify Oracle behavior:
test_xact_basics.py(12 tests) — manifold/metric/tensor definitions, canonicalization, contraction, curvature, numeric samplingtest_isolation.py(6 tests) — kernel cleanup, state isolation between test files, dirty-kernel recoverytest_tensor_tier3.py(19 tests) — tensor numeric sampling, tier-3 comparison fallback, context tracking
Note:
ToCanonicaldoes not apply multi-term symmetries like the first Bianchi identity. Integration tests verify mono-term symmetries (antisymmetry, pair exchange) thatToCanonicalsupports.
Performance Tips
- Batch related operations into single expressions
- Use simpler test manifolds (dim 2-3) for unit tests
- Skip slow tests during development:
pytest -m "not slow" - The Oracle uses a persistent kernel - xAct loads once (~2s) and stays loaded
- Use unique symbol names per test to avoid protection errors
Implementation Notes
Why Begin/ToExpression Instead of Block
The original plan suggested Block[{$Context = ...}, expr], but this doesn't work because:
Blockonly affects evaluation context, not parsing context- Symbols are parsed BEFORE
Blockexecutes - All symbols end up in
Global` anyway
The solution uses ToExpression to delay parsing:
Begin["xAct`xTensor`"];
With[{result$$ = ToExpression["expr"]}, End[]; result$$]