API Reference (Julia)

Modules

XActModule
XAct

The primary entry point for the Julia port of the xAct suite. Bundles XCore, XPerm, and XTensor into a unified namespace.

source
XAct.XCoreModule
XCore

Julia port of the xAct/xCore utility layer required by xTensor and xPerm.

Only Category-A symbols (used by xTensor/xPerm) are implemented here. Category-B symbols are aliased to Julia built-ins. Category-C symbols (display, FoldedRule, testing framework, namespace management) are intentionally omitted — see sxAct-8wd design notes for the full categorisation.

FoldedRule decision: SKIP. FoldedRule and its companions (CollapseFoldedRule, DependentRules, IndependentRules) are not referenced anywhere in xTensor or xPerm. They are a Wolfram-specific rule-sequencing idiom with no downstream consumers in the packages we are porting.

source
XAct.XTensor.XPermModule
XPerm

Julia implementation of Butler-Portugal tensor index canonicalization.

Permutations are 1-indexed image vectors (perm[i] = j means point i → j). Signed permutations extend to degree n+2 where positions n+1, n+2 encode sign.

References:

  • xperm.c: C reference implementation (GPL, not used at runtime)
  • SymPy tensor_can.py: Python reference for Schreier-Sims
  • Butler (1991): "Fundamental Algorithms for Permutation Groups"
  • Niehoff (2018): Direct sorting shortcut for Sym/Antisym groups
source
XAct.XTensorModule
XTensor

Abstract tensor algebra for the xAct/sxAct system. Implements DefManifold, DefMetric, DefTensor, ToCanonical, and Contract.

Curvature tensors are auto-created by def_metric!. Condition evaluation (Assert, Evaluate) is handled in the Python adapter layer.

Reference: specs/2026-03-06-xperm-xtensor-design.md

source
XAct.XInvarModule
XInvar

Invariant permutation representation system for the Invar pipeline. Implements InvariantCase, RPerm, RInv types, MaxIndex table, and case enumeration for Riemann tensor invariant classification.

Reference: Martín-García, Yllanes & Portugal (2008) arXiv:0802.1274 Wolfram source: resources/xAct/Invar/Invar.m

source
XAct.TExprLayerModule
TExprLayer

Typed expression layer for xAct. Provides Idx, DnIdx, TensorHead, CovDHead, TTensor, TProd, TSum, TScalar, TSymbol, TCovD, the @indices macro, tensor() / covd() lookups, tostring() serialisation, and TExpr overloads for all engine functions.

source

Functions and Types

XAct.reset_state!Method
reset_state!()

Perform a global reset of all xAct subcomponents (XCore, XPerm, XTensor).

source
XAct.validate_deriv_ordersMethod
validate_deriv_orders(deriv_orders) → nothing

Validate that derivative orders are non-negative and sorted non-decreasing.

source
XAct.validate_identifierMethod
validate_identifier(name; context="") → Symbol

Validate that name is a safe ASCII identifier: [A-Za-z_][A-Za-z0-9_]*. Returns the name as a Symbol on success; throws ArgumentError otherwise.

source
XAct.validate_orderMethod
validate_order(order; context="perturbation order") → nothing

Validate that a perturbation order is >= 1.

source
XAct.validate_permMethod
validate_perm(p; context="permutation") → nothing

Validate that p is a well-formed permutation: elements in 1:n with no duplicates.

source
XAct.XCore.AtomQMethod
AtomQ(x) -> Bool

Return true for any value (analogous to Mathematica AtomQ). In Julia, every Symbol, Number, and String is atomic.

source
XAct.XCore.CheckOptionsMethod
CheckOptions(opts...)

Validate that every element (after flattening one level) is a Pair. Returns a flat Vector{Pair} on success; throws on invalid structure.

Each argument must be either a Pair (kept as-is) or an iterable of Pairs (flattened one level). Iterating over a bare Pair would yield its key and value, not a single rule, so Pair arguments are treated as atomic here.

source
XAct.XCore.FindSymbolsMethod
FindSymbols(expr) -> Vector{Symbol}

Recursively collect all Symbols in expr (including inside Expr args and collections). Returns a deduplicated vector.

source
XAct.XCore.JustOneMethod
JustOne(list)

Return the single element of a one-element collection; throw otherwise. Used inside xTensor for pattern-match assertions.

source
XAct.XCore.LinkSymbolsMethod
LinkSymbols(symbols::Vector{Symbol}) -> Symbol

Create a single symbol by interleaving symbols with LinkCharacter.

Analogous to Mathematica LinkSymbols[{s1, s2, ...}].

source
XAct.XCore.MakeDaggerSymbolMethod
MakeDaggerSymbol(s::Symbol) -> Symbol

Toggle the dagger character:

  • If present, remove it.
  • If absent, insert it before the first $ in the name, or append it.
source
XAct.XCore.MapIfPlusMethod
MapIfPlus(f, expr)

Map f over a vector/tuple (multi-term, analogous to Plus-headed expr), or apply f once to expr if it is a scalar.

source
XAct.XCore.NoPatternMethod
NoPattern(expr)

Identity function. Julia has no Pattern/Blank wrappers; this is a no-op shim preserving call-site compatibility with xTensor code that calls NoPattern.

source
XAct.XCore.ReportSetMethod
ReportSet(ref::Ref, value; verbose=true)

Assign value to ref[], printing a report if the value changed.

Analogous to Mathematica ReportSet[var, value] which assigns and prints when the variable changes. Mathematica uses HoldFirst to capture the variable name; Julia cannot do that, so callers pass a Ref wrapper.

Not used by xTensor or xPerm downstream.

source
XAct.XCore.ReportSetOptionMethod
ReportSetOption(symbol, option => value)

No-op shim. Mathematica's ReportSetOption sets an option on a symbol and prints if the value changed, relying on Options/SetOptions. Julia has no built-in option-dictionary system; packages manage their own option storage.

Not used by xTensor or xPerm downstream.

source
XAct.XCore.SetNumberOfArgumentsMethod
SetNumberOfArguments(f, n)

No-op compatibility shim. Julia enforces arity through method dispatch; wrong-arity calls produce MethodError automatically. This function is provided so that xTensor/xPerm code that calls SetNumberOfArguments at load time does not error.

source
XAct.XCore.SubHeadMethod
SubHead(expr) -> Symbol

Return the innermost atomic head of a nested expression. For a bare Symbol, returns itself. For an Expr, recurses into expr.head.

source
XAct.XCore.SymbolJoinMethod
SymbolJoin(symbols...)

Create a new Symbol by concatenating the string representations of all arguments. Analogous to Mathematica SymbolJoin, used 21× in xTensor for composite names.

source
XAct.XCore.SymbolNameMethod
SymbolName(s::Symbol) -> String

Return the string name of a symbol (analogous to Mathematica SymbolName).

source
XAct.XCore.ThreadArrayMethod
ThreadArray(head, left::AbstractArray, right::AbstractArray)

Map head over corresponding pairs from left and right, preserving array shape. Analogous to Mathematica ThreadArray[head[left, right]] which threads head over matching elements at full array depth via MapThread.

Julia's map on two arrays already threads element-wise; this is a thin wrapper for call-site compatibility. Not used by xTensor or xPerm downstream.

source
XAct.XCore.UnlinkSymbolMethod
UnlinkSymbol(s::Symbol) -> Vector{Symbol}

Split a symbol at each occurrence of LinkCharacter, returning the parts as symbols.

Analogous to Mathematica UnlinkSymbol[symbol].

source
XAct.XCore.ValidateSymbolMethod
ValidateSymbol(name::Symbol)

Throw if name collides with any symbol already registered in the xAct registry, or if it is exported by Julia's Base. Returns nothing on success.

Error conditions (mirrors Mathematica ValidateSymbol):

  • name is in _symbol_registry → already used by that package
  • name is a Base export → reserved by Julia
source
XAct.XCore.push_unevaluated!Function
push_unevaluated!(collection, value)

Append value to collection. Julia evaluates eagerly so this is an alias for push!; the "unevaluated" qualifier is a Mathematica artefact.

source
XAct.XCore.register_symbolMethod
register_symbol(name, package)

Register name (a Symbol or string) as owned by package.

  • If the name is already registered by the same package, the call is a no-op (idempotent).
  • If the name is already registered by a different package, throws an error.
  • On success, also appends name to the per-package name list if package is one of the known xAct package labels.
source
XAct.XCore.strip_varianceMethod
strip_variance(s) -> String

Strip the leading "-" (covariant marker) from an index string. Returns the bare index label. E.g. strip_variance("-a")"a", strip_variance("a")"a".

source
XAct.XCore.xEvaluateAtMethod
xEvaluateAt(expr, positions)

No-op shim. In Mathematica, this forces evaluation at given subexpression positions. Julia evaluates eagerly; there is nothing to force. Provided for call-site compatibility.

source
XAct.XCore.xTagSet!Method
xTagSet!(tag, key, value)

Assign value to key in the tag-indexed store for tag. Analogous to Mathematica xTagSet[{tag, lhs}, rhs].

source
XAct.XCore.xTension!Method
xTension!(package, defcommand, moment, func)

Register func to be called at moment ("Beginning" or "End") during execution of defcommand. package is a string label used for grouping (stored as metadata only; hooks fire in registration order).

source
XAct.XCore.xUpAppendTo!Method
xUpAppendTo!(property, tag, element)

Append element to the upvalue list property[tag], initialising to [] if absent.

source
XAct.XCore.xUpSet!Method
xUpSet!(property, tag, value)

Attach value as the property upvalue of tag. Equivalent to Mathematica xUpSet[property[tag], value].

Returns value.

source
XAct.XCore.xUpSetDelayed!Method
xUpSetDelayed!(property, tag, thunk)

Attach a zero-argument function thunk as a delayed upvalue. Callers retrieve the value by invoking the stored thunk.

source
XAct.XCore.XHoldType
XHold{T}

Wrapper analogous to Mathematica's xHold / HoldForm: prevents the contained value from being printed at high precedence. Julia evaluates eagerly, so this is purely a display/typing artefact.

source
XAct.XCore.DaggerCharacterConstant

Global dagger character appended to daggered symbol names. Default is the Unicode dagger (U+2020), matching xCore's \[Dagger].

source
XAct.XCore.LinkCharacterConstant

Global link character used to join symbol parts in LinkSymbols. Default is (U+2040, UnderBracket), matching xCore's \[UnderBracket].

source
XAct.XTensor.XPerm.CyclesMethod
Cycles(cycle1, cycle2, ...) → Vector{Int}

Create a permutation image-vector from cycle notation. Cycles([1,2,3,4]) produces the 4-cycle 1→2→3→4→1. Cycles() returns Int[] (identity of degree 0).

source
XAct.XTensor.XPerm.DeleteRedundantGeneratorsMethod
DeleteRedundantGenerators(sgs) → StrongGenSet

Remove generators from sgs that are redundant (expressible as products of the remaining generators). Iterates through the flat generator list, removing each generator whose removal does not change the group order.

source
XAct.XTensor.XPerm.DiminoFunction
Dimino(GS, names) → Group

Enumerate all elements of the group generated by GS using the Dimino algorithm. Returns a Group with elements in coset-expansion order (identity first). names is an optional Vector{Pair{String,Vector{Int}}} mapping name → permutation vector. Named elements appear as their String name; others appear as their permutation vector.

The algorithm: for each new generator s not yet in G, iterate through all current elements and left-multiply by s, appending new elements. This produces xPerm-compatible coset ordering.

source
XAct.XTensor.XPerm.OrbitMethod
Orbit(pt, genset_or_sgs) → Vector{Int}

Return the orbit of pt under the generators, in BFS discovery order (matching xPerm's Mathematica output).

source
XAct.XTensor.XPerm.OrbitsMethod
Orbits(genset_or_sgs [, n]) → Vector{Vector{Int}}

Return all orbits of the generators, each in BFS discovery order.

source
XAct.XTensor.XPerm.PermDegMethod
PermDeg(sgs) → Int

Return the physical degree (number of non-sign points) of the group. For a GenSet (Vector{Vector{Int}}), returns the maximum permutation length.

source
XAct.XTensor.XPerm.PermMemberQMethod
PermMemberQ(perm, sgs) → Bool

Test whether perm is an element of the group described by sgs. ID is treated as the identity permutation of degree sgs.n. An empty permutation Int[] is also treated as identity.

source
XAct.XTensor.XPerm.PermWordMethod
PermWord(perm, sgs) → PermWordResult

Decompose perm as a product of coset representatives from the stabilizer chain. Returns the word [residual, u_k, ..., u_1] (residual first, then coset reps in reverse sift order) such that:

perm = u_1 ∘ u_2 ∘ ... ∘ u_k ∘ residual

Named generators (variables in Julia's Main scope) are returned as strings.

source
XAct.XTensor.XPerm.PermuteMethod
Permute(a, b, ...) → Vector{Int}

Compose permutations left-to-right: Permute(a, b) means apply a first, then b, i.e. compose(b, a) in right-to-left convention.

source
XAct.XTensor.XPerm.SchreierMethod
Schreier(args...) → SchreierResult or MultiSchreierResult

WL-style constructor for SchreierResult (used in assertion comparisons).

  • 3-arg form: Schreier(orbit, labels, parents)
  • 4+-arg form: Schreier(orbit1, orbit2, ..., labels, parents)
source
XAct.XTensor.XPerm.SchreierOrbitMethod
SchreierOrbit(root, GS, n, names) → SchreierResult

BFS from root under generators GS (each named by names[i]) in [1..n]. Returns a SchreierResult with orbit, label vector, and parent vector.

source
XAct.XTensor.XPerm.SchreierOrbitsMethod
SchreierOrbits(GS, n, names) → MultiSchreierResult

Compute all orbits under generators GS (named by names) in [1..n]. Returns all orbits and combined label/parent vectors.

source
XAct.XTensor.XPerm.StablePointsMethod
StablePoints(perm) → Vector{Int}
StablePoints(gs::Vector) → Vector{Int}
StablePoints(sgs::StrongGenSet) → Vector{Int}

Return the sorted list of points fixed by all generators.

source
XAct.XTensor.XPerm._canonicalize_antisymmetricMethod
_canonicalize_antisymmetric(indices, slots) → (Vector{String}, Int)

Niehoff shortcut for Antisymmetric groups: sort slot positions by bare label name. Returns (newindices, sign) where sign=parity(sortpermutation), or ([], 0) if repeated.

source
XAct.XTensor.XPerm._canonicalize_riemannMethod
_canonicalize_riemann(indices, slots) → (Vector{String}, Int)

Butler-Portugal via enumeration for the Riemann symmetry group (order 8). Finds the lex-min (by bare label) arrangement among the 8 group elements.

source
XAct.XTensor.XPerm._canonicalize_youngMethod
_canonicalize_young(indices, partition, slots) → (Vector{String}, Int)

Canonicalize indices at slots under the Young symmetry group defined by partition.

The Young symmetry group is {c·r : c ∈ colgroup, r ∈ rowgroup} with sign = sgn(c). We enumerate all orbit elements and return the lexicographically minimal representative together with its sign s such that T[canonical] = s · T[original].

source
XAct.XTensor.XPerm._riemann_8_elementsMethod
_riemann_8_elements(i, j, k, l) → Vector{Tuple{NTuple{4,Int}, Int}}

Return all 8 elements of the Riemann symmetry group as (slotimage, sign) pairs. slotimage[m] = which original slot position goes to position m.

source
XAct.XTensor.XPerm._siftMethod
_sift(p, sgs_levels, n) → (residual, depth)

Sift permutation p through the partial BSGS represented by sgs_levels. Returns (residual, depth) where:

  • If residual is identity at depth == length(sgs_levels)+1, p ∈ group.
  • Otherwise, the residual is a new generator for level depth.
source
XAct.XTensor.XPerm._sift_with_cacheMethod
_sift_with_cache(p, base, level_GS, sv_cache, n) → (residual, depth)

Like _sift but uses a shared sv_cache of SchreierVectors (entries are nothing when invalidated). Recomputes and stores missing entries. This avoids redundant BFS inside the tight Schreier-Sims loop.

source
XAct.XTensor.XPerm._young_columnsMethod
_young_columns(tab) → Vector{Vector{Int}}

Return the columns of a YoungTableau as lists of slot positions. Column j contains tab.filling[i][j] for each row i that has ≥ j elements.

source
XAct.XTensor.XPerm.antisymmetric_sgsMethod
antisymmetric_sgs(slots, n) → StrongGenSet

Alternating-sign group A_k on slots. Adjacent transpositions each carry sign=-1 (transposed via n+1 ↔ n+2 in extended rep). Returns signed StrongGenSet (signed=true).

source
XAct.XTensor.XPerm.canonical_permMethod
canonical_perm(perm, sgs, free_points, dummy_groups) → (Perm, Int)

Returns (canonical_perm, sign) where sign ∈ {-1, 0, +1}. Returns (Int[], 0) if the expression is zero (repeated antisymmetric index).

source
XAct.XTensor.XPerm.canonicalize_slotsFunction
canonicalize_slots(indices, sym_type, slots[, partition]) → (Vector{String}, Int)

Apply symmetry canonicalization to indices at the given slots. symtype: one of :Symmetric, :Antisymmetric, :GradedSymmetric, :RiemannSymmetric, :YoungSymmetry, :NoSymmetry For :YoungSymmetry, partition must be provided (e.g. [2,1]). Returns (newindices, sign) where sign ∈ {-1, 0, +1}.

source
XAct.XTensor.XPerm.col_antisymmetry_sgsMethod
col_antisymmetry_sgs(tableau, n) → StrongGenSet

Return the signed StrongGenSet for the column antisymmetrization group of tableau. The column group permutes within each column, with sign = sign(permutation). Generators are adjacent transpositions within each column (degree n, signed = true).

For partition [λ₁, λ₂, ...], column j contains the j-th element of each row that is long enough.

source
XAct.XTensor.XPerm.double_coset_repMethod
double_coset_rep(perm, sgs, dummy_groups) → (Perm, Int)

Find the canonical representative of S · perm · D where D is the dummy symmetry group.

dummy_groups is a Vector of Vector{Int}: each inner vector lists positions (1-indexed) that are freely exchangeable (dummy index relabeling symmetry). For each group, every transposition of two positions in the group is a generator of D.

Algorithm:

  1. Build generators of D from transpositions within each dummy group.
  2. Enumerate all elements of D by BFS over the Cayley graph (small groups in practice).
  3. For each d ∈ D, compute rightcosetrep(perm ∘ d, sgs).
  4. Return the lex-minimum result (by comparing the returned unsigned perm).

For Tier 1 tests (no dummy indices), dummygroups is empty → reduces to rightcoset_rep.

source
XAct.XTensor.XPerm.riemann_sgsMethod
riemann_sgs(slots, n) → StrongGenSet

Riemann symmetry group on exactly 4 slots (i,j,k,l) (1-indexed). Generators (signed): g1 = swap slots i,j with sign=-1 (antisym in first pair) g2 = swap slots k,l with sign=-1 (antisym in second pair) g3 = cycle (i↔k, j↔l) with sign=+1 (pair exchange) Group order = 8. Returns signed StrongGenSet.

source
XAct.XTensor.XPerm.right_coset_repMethod
right_coset_rep(perm, sgs) → (Perm, Int)

Find the lex-minimum (by base order) element of the right coset S · perm, where S is the group described by sgs. Returns (canonical_perm, sign).

  • sign = +1 always for unsigned groups (sgs.signed == false)
  • sign extracted from position n+1 for signed groups
source
XAct.XTensor.XPerm.row_symmetry_sgsMethod
row_symmetry_sgs(tableau, n) → StrongGenSet

Return the unsigned StrongGenSet for the row symmetrization group of tableau. The row group is the direct product S{λ₁} × S{λ₂} × ... where S_{λᵢ} permutes within row i. Generators are adjacent transpositions within each row.

The group acts on points 1..n (unsigned permutations, sign = +1 for all elements).

source
XAct.XTensor.XPerm.schreier_simsMethod
schreier_sims(initbase, generators, n) → StrongGenSet

Build a Strong Generating Set via the basic Schreier-Sims algorithm. initbase — initial base (vector of points; extended during computation) generators — initial generators (Perm or SignedPerm of degree n or n+2) n — number of physical points (1..n)

source
XAct.XTensor.XPerm.schreier_vectorMethod
schreier_vector(root, GS, n) → SchreierVector

Compute the Schreier vector for the orbit of root under the generators GS, where each generator acts on points 1..n (or 1..n+2 for signed; only 1..n matter).

source
XAct.XTensor.XPerm.standard_tableauMethod
standard_tableau(partition, indices) → YoungTableau

Construct a Young tableau for partition using the given indices (slot positions). The filling is assigned left-to-right within each row, top-to-bottom across rows.

Arguments

  • partition::Vector{Int}: row lengths in descending order, must sum to length(indices).
  • indices::Vector{Int}: 1-indexed slot positions to fill into the tableau.

Example

# Partition [3,2] on indices [1,2,3,4,5]
tab = standard_tableau([3, 2], [1, 2, 3, 4, 5])
# tab.filling == [[1, 2, 3], [4, 5]]
source
XAct.XTensor.XPerm.symmetric_sgsMethod
symmetric_sgs(slots, n) → StrongGenSet

Symmetric group S_k on slots (1-indexed positions in 1..n). Generators: adjacent transpositions of consecutive slot positions. Returns unsigned StrongGenSet (signed=false).

source
XAct.XTensor.XPerm.trace_schreierMethod
trace_schreier(sv, p, GS) → Perm

Recover the group element (product of generators) that maps sv.root to point p, by tracing the Schreier tree backwards from p to root. Returns the permutation u such that u(root) = p.

source
XAct.XTensor.XPerm.young_projectorMethod
young_projector(tableau, n) → Vector{Tuple{Vector{Int}, Int}}

Compute the Young projector (symmetrizer) Pλ = Qλ · R_λ for the given Young tableau.

The Young projector is: Pλ = Σ{q ∈ colgroup} Σ{r ∈ row_group} sign(q) · q · r

Returns a vector of (perm, sign) pairs representing the expansion of Pλ in Sn, where perm is a permutation of degree n and sign ∈ {-1, +1}.

Duplicate permutations are collected and their signs summed; entries with net sign zero are dropped. The result is the "support" of the projector.

Arguments

  • tableau::YoungTableau: the Young tableau.
  • n::Int: degree of the symmetric group (total number of physical points).

Example

# Partition [2,1]: hook shape on n=3 indices [1,2,3]
# Row group: {e, (12)};  Column group: {e, -(13)}
# P = e·e + e·(12) - (13)·e - (13)·(12)
tab = standard_tableau([2, 1], [1, 2, 3])
terms = young_projector(tab, 3)
# 4 terms (before collecting): each with sign ±1
source
XAct.XTensor.XPerm.GroupType
Group(elem1, elem2, ...) → Group

Ordered list of group elements as returned by xPerm's Dimino. Each element is either a String (named generator or "ID") or a Vector{Int} (Cycles form). Equality treats "ID" and Int[] as equivalent identity representations.

source
XAct.XTensor.XPerm.PermWordResultType
PermWordResult

Result of PermWord: a list of permutation-vector coset representatives. Stores actual Vector{Int} values (suitable for splatting into Permute). show() outputs WL-compatible {elem, ...} notation, substituting names from Julia's Main scope for any generator that matches a named variable.

source
XAct.XTensor.XPerm.SchreierResultType
SchreierResult

Result of SchreierOrbit: orbit, label vector (generator name or 0), parent vector. Displays as WL-style Schreier[{orbit}, {labels}, {parents}].

source
XAct.XTensor.XPerm.SchreierVectorType

Schreier vector for orbit(root, generators, n). orbit — sorted list of points reachable from root under the generators. nu — length-n vector; nu[i] = index (1-based) into GS of the generator that moved point i into the orbit tree, or 0 if i ∉ orbit. w — length-n vector; w[i] = the predecessor point from which i was reached in BFS, or 0 if i ∉ orbit. root — the starting point.

source
XAct.XTensor.XPerm.StrongGenSetType

Strong Generating Set for a permutation group G ≤ S_n. base[i] — a point moved by the i-th stabilizer but fixed by all later ones. GS — flat list of generators; each is a Perm (unsigned) or SignedPerm (signed). n — degree of the physical points (1..n). signed — true iff generators are signed (degree n+2); false iff unsigned (degree n).

source
XAct.XTensor.XPerm.StrongGenSetMethod
StrongGenSet(base, genset) → StrongGenSet

WL-style constructor: build a strong generating set via Schreier-Sims from a base vector and a generator list.

source
XAct.XTensor.XPerm.YoungTableauType
YoungTableau

Represents a Young tableau for a partition λ = (λ₁ ≥ λ₂ ≥ ... ≥ λₖ) of n.

Fields: partition — row lengths in descending order, e.g. [3, 2, 1] for a 6-index tensor. filling — filling[row] = sorted list of index positions (1-indexed) in that row.

The standard filling places indices left-to-right in each row: row 1: positions 1..λ₁ row 2: positions λ₁+1..λ₁+λ₂ etc.

For a custom filling (e.g. to apply the symmetrizer to specific slot positions), use standard_tableau(partition, indices) which reindexes from an arbitrary list of n slot positions.

source
XAct.XTensor.AllContractionsMethod
AllContractions(expression, metric_name) → Vector{String}

Find all independent scalar contractions of an expression with a metric. For a rank-2 tensor T{ab}, this contracts g^{ab} T{ab} → scalar. Returns a vector of simplified scalar expressions (one per contraction pattern).

source
XAct.XTensor.CTensorQMethod
CTensorQ(tensor, bases...) → Bool

Return true if component values are stored for the given tensor and bases.

source
XAct.XTensor.CollectTensorsMethod
CollectTensors(expression) → String

Collect like terms in a tensor expression by canonicalizing each term and combining terms with identical canonical monomials.

source
XAct.XTensor.CommuteCovDsMethod
CommuteCovDs(expr, covd_name, idx1, idx2) → String

Apply the commutation identity for two covariant derivatives: ∇{idx1} ∇{idx2} T = ∇{idx2} ∇{idx1} T + curvature correction terms

where correction terms are given by the Riemann curvature:

  • For each covariant slot -aᵢ: correction - RiemannCD[-aᵢ, e, idx1, idx2] T[..., -e, ...]
  • For each contravariant slot aᵢ: correction + RiemannCD[aᵢ, -e, idx1, idx2] T[..., e, ...]

Here e is a fresh dummy index (contravariant/covariant in Riemann, covariant/contravariant in T).

Args: expr : String expression containing covd[idx1][covd[idx2][tensor[slots]]] covd_name : Symbol — e.g. :CVD idx1 : String — first derivative index, e.g. "-cva" idx2 : String — second derivative index, e.g. "-cvb"

Returns a String expression suitable for ToCanonical.

source
XAct.XTensor.ContractMethod
Contract(expression::String) → String

Perform metric contraction (ContractMetric) on a tensor expression.

For each metric factor in the expression, contracts its indices with matching indices in other factors (raises/lowers indices, removes the metric).

Physics rules applied:

  • Weyl-type tensors (traceless): any self-trace → term is 0
  • Einstein tensor trace: tr(G_{ab}) → (1 - dim/2) * RicciScalar
source
XAct.XTensor.FromBasisMethod
FromBasis(tensor, bases) → String

Return the abstract-index expression string for a tensor whose components are stored in the given bases. Verifies components exist, then reconstructs the symbolic form using the tensor's declared index slots.

source
XAct.XTensor.IBPMethod
IBP(expr, covd_name) → String

Integrate expr by parts with respect to covd_name. For each term:

  • Pure divergence covd[-a][V[a]]: dropped (→ 0)
  • Product A * covd[-a][B]: → -(covd[-a][A]) * B (mod total derivative)
  • Otherwise: unchanged Result is passed through Simplify.
source
XAct.XTensor.JacobianMethod
Jacobian(basis1, basis2) → Any

Return the Jacobian determinant of the transformation from basis1 to basis2.

source
XAct.XTensor.MakeTraceFreeMethod
MakeTraceFree(expression, metric_name) → String

Compute the trace-free part of a rank-2 tensor expression with respect to the given metric. For T_{ab}:

T{ab}^TF = T{ab} - (1/dim) g{ab} g^{cd} T{cd}

where dim is the manifold dimension.

source
XAct.XTensor.PerturbationAtOrderMethod
PerturbationAtOrder(background, order) → Symbol

Return the name of the perturbation tensor registered for background at perturbation order. Throws an error if no such perturbation is registered.

Examples

PerturbationAtOrder(:g, 1)   # → :Pertg1
PerturbationAtOrder(:g, 2)   # → :Pertg2
source
XAct.XTensor.PerturbationOrderMethod
PerturbationOrder(tensor_name) → Int

Return the perturbation order of a registered perturbation tensor. Throws an error if tensor_name is not a registered perturbation.

Examples

PerturbationOrder(:Pertg1)   # → 1
PerturbationOrder(:Pertg2)   # → 2
source
XAct.XTensor.RegisterIdentity!Method
RegisterIdentity!(tensor_name, identity)

Register a multi-term identity for a tensor. Identities are applied during canonicalization by _apply_identities!.

source
XAct.XTensor.SignDetOfMetricMethod
SignDetOfMetric(metric_name) → Int

Return the sign of the determinant (+1 Riemannian, -1 Lorentzian) for a registered metric.

source
XAct.XTensor.SimplifyMethod
Simplify(expression::AbstractString) → String

Algebraic simplification of a tensor expression.

Iterates Contract followed by ToCanonical until the expression stops changing (convergence), providing:

  • Metric contraction (index raising/lowering, self-trace → dimension)
  • Weyl-tracelessness and Einstein-trace physics rules
  • Index canonicalization and sign normalization
  • Like-term collection (sum simplification)
  • Bianchi identity reduction
  • Einstein tensor expansion

For example, g^{ab}g_{ab} is reduced to n (the manifold dimension) in a single pass without requiring a prior Contract call.

source
XAct.XTensor.SymmetryOfMethod
SymmetryOf(expression) → String

Determine the symmetry type of an expression by examining its behavior under index permutations. Returns "Symmetric", "Antisymmetric", or "NoSymmetry". For expressions with 0 or 1 free indices, returns "Symmetric".

source
XAct.XTensor.ToBasisMethod
ToBasis(expr_str, basis) → CTensorObj

Convert an abstract-index tensor expression to component form in the given basis.

Handles single tensors, products, sums, and automatically contracts dummy (repeated) indices via einsum.

Examples

ToBasis("g[-a,-b]", :Polar)           # metric components
ToBasis("g[-a,-b] * v[a]", :Polar)    # contraction g_{ab} v^a
ToBasis("T[-a,-b] + S[-a,-b]", :Polar) # sum of tensors
source
XAct.XTensor.TraceBasisDummyMethod
TraceBasisDummy(tensor, bases) → CTensorObj

Automatically find and contract all pairs of basis indices where one slot is covariant and the other is contravariant (with the same basis), mirroring Wolfram's TraceBasisDummy. Returns the contracted CTensorObj.

For a rank-2 mixed tensor like T^a_{b} with both slots in the same basis, this computes the trace.

source
XAct.XTensor.ValidateSymbolInSessionMethod
ValidateSymbolInSession(name::Symbol)

Check that name is not already used as a manifold, tensor, metric, vbundle, covariant derivative, or perturbation in the current session. Throws on collision. Analogous to Wolfram ValidateSymbolInSession.

source
XAct.XTensor.VarDMethod
VarD(expr, field_name, covd_name) → String

Euler-Lagrange derivative of Lagrangian expr w.r.t. field field_name. Uses IBP to move derivatives off the field variation. Result is simplified.

source
XAct.XTensor._apply_identities!Method
_apply_identities!(coeff_map, key_order)

Apply all registered multi-term identities to the canonical term map. Replaces the hardcoded _bianchi_reduce! with a general framework.

source
XAct.XTensor._apply_single_identity!Method
_apply_single_identity!(coeff_map, key_order, id)

Apply one multi-term identity to the canonical term map.

Groups single-factor terms by sector (values at fixedslots + sorted values at cycledslots), then for each complete sector eliminates the designated term.

source
XAct.XTensor._apply_trace_rulesMethod
_apply_trace_rules(...) → (:zero, nothing) | (:replaced, TermAST) | (:none, nothing)

Dispatch trace rules through registries. Returns:

  • (:zero, nothing) when the trace vanishes (traceless tensor)
  • (:replaced, term) when a trace rule fired
  • (:none, nothing) when no rule matched (caller should fall through)
source
XAct.XTensor._bianchi_reduce!Method
_bianchi_reduce!(coeff_map, key_order)

Legacy wrapper: delegates to _apply_identities!. Kept for backward compatibility; new code should call _apply_identities! directly.

source
XAct.XTensor._collect_covd_termsMethod
_collect_covd_terms(terms::Vector{String}) -> Vector{String}

Group CovD terms by body (ignoring coefficient), sum coefficients, and return only terms with non-zero coefficient. This ensures e.g. T - T = 0.

source
XAct.XTensor._commute_covd_pairMethod
_commute_covd_pair(covd_str, covd_indices, swap_pos, tensor_name, inner_slots, manifold_sym)

Commute the adjacent CovD pair at positions swap_pos and swap_pos+1 in a CovD chain, producing the swapped chain plus Riemann correction terms.

The Riemann correction acts on ALL effective indices below the swap point: the CovD indices from swap_pos+2 to end, plus the innermost tensor's slots.

Returns a vector of string terms (the swapped main term + correction terms).

source
XAct.XTensor._compositionsMethod

Generate all compositions of n into k non-negative integer parts.

Returns compositions in descending order of the first element, so that for order=1 the term with the first factor perturbed comes first (matching the natural Leibniz convention).

source
XAct.XTensor._contract_one_metricMethod

Find one metric factor and contract it with another factor (or apply a trace rule). Returns the updated TermAST, the same TermAST if no metric found, or nothing if zero.

source
XAct.XTensor._einsum_evalFunction
_einsum_eval(factor_arrays, factor_labels, dummy_labels, assignment, dim) → Float64

Recursively sum over dummy indices, then evaluate the product of all factors. Uses a pre-allocated index buffer per factor and a flat assignment vector (keyed by label ordinal) to avoid Dict/Array allocation in the inner loop.

source
XAct.XTensor._extract_covd_chainMethod
_extract_covd_chain(term::AbstractString, covd_str::AbstractString)

Find a CovD chain in a single term string and return: (covdindices, innertensorname, innerslots, prefix, suffix)

where covd_indices is the list of CovD indices in outer-to-inner order, inner_tensor_name is the innermost tensor name (e.g. "T"), inner_slots is the list of slot strings (e.g. ["-a","-b"]), prefix is everything before the CovD chain (coefficient etc.), and suffix is everything after the CovD chain.

Returns nothing if no CovD chain with ≥ 2 derivatives is found.

source
XAct.XTensor._extract_leading_coeffMethod

Extract leading coefficient from term body string. Returns (coeff::Rational{Int}, remaining_str). Matches (N/D) rest or N rest (integer followed by whitespace). Otherwise coeff=1//1.

source
XAct.XTensor._factor_as_metricMethod

Find which registered metric a tensor factor corresponds to, and its variance. Returns (covdkey, MetricObj, :contravariant | :covariant) or nothing. :contravariant — g^{ab}: both indices up (no '-' prefix) → raises indices :covariant — g{ab}: both indices down ('-' prefix) → lowers indices

source
XAct.XTensor._find_unsorted_pairMethod
_find_unsorted_pair(covd_indices::Vector{String}) -> Union{Int, Nothing}

Find the first adjacent pair of CovD indices that is out of canonical (lexicographic) order. Returns the index i such that covd_indices[i] > covd_indices[i+1], or nothing if all are sorted.

Comparison is on the bare index name (stripping the leading '-').

source
XAct.XTensor._ibp_term_factorsMethod

Apply one IBP step to factors of a single term. Returns (new_coeff, new_body) or nothing (no CovD found). Returns (0//1, "0") for a pure total divergence.

source
XAct.XTensor._join_term_stringsMethod

Join a vector of signed term strings into a sum expression. Each element may start with "-" (negative) or not (positive). Adjacent terms are separated by " - " or " + " as appropriate.

source
XAct.XTensor._leibniz_covdMethod

Expand covd[der_idx][f1 f2 ... fn] via Leibniz product rule. Returns a Vector of term strings, each with CovD applied to one factor.

source
XAct.XTensor._make_bianchi_identityMethod
_make_bianchi_identity(tensor_name)

Construct the first Bianchi identity R_{a[bcd]} = 0 for a 4-slot tensor with RiemannSymmetric symmetry.

Canonical forms for indices p < q < r < s: X₁ = R[p,q,r,s] → cycled ranks [1,2,3] X₂ = R[p,r,q,s] → cycled ranks [2,1,3] X₃ = R[p,s,q,r] → cycled ranks [3,1,2] Identity: X₁ - X₂ + X₃ = 0; eliminate X₃.

source
XAct.XTensor._parse_symmetryMethod
_parse_symmetry(sym_str, slot_specs) → SymmetrySpec

Parse a Wolfram symmetry string like "Symmetric[{-bta,-btb}]" into a SymmetrySpec. slot_specs is the tensor's slot list for mapping labels → slot positions.

source
XAct.XTensor._rebuild_covd_exprMethod
_rebuild_covd_expr(covd_str, covd_indices, tensor_name, slots)

Rebuild a CovD chain expression from parts: covd[i1][covd[i2][...covd[in][tensor[slots]]...]]

source
XAct.XTensor._safe_simplifyMethod

Simplify expr only if it contains no CovD-applied factors (patterns like CovD[-a][inner]). _parse_monomial truncates such factors at the first bracket group, so Simplify would corrupt them. A quick regex scan over registered CovD names is sufficient; full factor parsing is not needed.

source
XAct.XTensor._split_covd_coeffMethod
_split_covd_coeff(term) -> (Rational{Int}, String)

Split a CovD term string into (coefficient, body). Handles leading signs and integer/rational coefficients, e.g. "- 3 SCD[-a][T[-b]]"(-3//1, "SCD[-a][T[-b]]").

source
XAct.XTensor._split_expression_termsMethod
_split_expression_terms(expr::AbstractString) -> Vector{String}

Split a tensor expression string into additive terms, preserving signs. Each returned term includes its leading sign ('+' or '-') if not the first term. Returns individual term strings that can be recombined with ' '.

source
XAct.XTensor._split_string_termsMethod

Split expression into signed string terms. Returns Vector{Tuple{Int, String}} = [(sign, body), ...]. Splits on top-level + and -, tracking bracket depth.

source
XAct.XTensor._swap_indicesMethod
_swap_indices(expr, label_a, label_b) → String

Swap two index labels in an expression string, handling both covariant (-label) and contravariant (label) forms.

source
XAct.XTensor._tobasis_termMethod
_tobasis_term(term, basis, dim) → (Array, Vector{Symbol})

Evaluate a single parsed term to component form. Returns (array, freeindexlabels). Uses einsum-style evaluation: for each assignment of free index values, sums over all dummy index values the product of factor components.

source
XAct.XTensor._vard_term_contributionsMethod

Compute all EL contributions from a single term for variational derivative w.r.t. field. Returns Vector{Tuple{Rational{Int}, String}} of (coeff, body) contributions.

source
XAct.XTensor.change_basisMethod
change_basis(array, bases, slot, from_basis, to_basis) → Array

Apply a basis change to a specific slot of a component array.

  • array — the component array (Vector for rank-1, Matrix for rank-2, etc.)
  • bases — vector of basis symbols for each slot (unused, reserved for future)
  • slot — 1-indexed slot to transform
  • from_basis — current basis of that slot
  • to_basis — target basis

For rank-1 (vector): result = M * v For rank-2 (matrix): transforms the specified slot using the transformation matrix.

source
XAct.XTensor.check_metric_consistencyMethod
check_metric_consistency(metric_name) → Bool

Verify that a registered metric is internally consistent: its metric tensor is symmetric and its inverse (raised-index version) is registered as its own symmetric tensor. Currently validates:

  1. The metric tensor exists in the tensor registry.
  2. The metric is recorded in the metric registry (via def_metric!).
  3. The metric tensor is symmetric (rank-2 with Symmetric symmetry).

Returns true if all checks pass, false otherwise (never throws).

source
XAct.XTensor.check_perturbation_orderMethod
check_perturbation_order(tensor_name, order) → Bool

Verify that a perturbation tensor is registered with the given perturbation order. Returns true if tensor_name is a registered perturbation of exactly order, false otherwise.

source
XAct.XTensor.christoffel!Method
christoffel!(metric, basis; metric_derivs=nothing) → CTensorObj

Compute and store Christoffel symbols (second kind) from metric CTensor components.

The Christoffel symbol is:

Γ^a_{bc} = (1/2) g^{ad} (∂_b g_{dc} + ∂_c g_{bd} - ∂_d g_{bc})

Arguments:

  • metric: the metric tensor symbol (must have stored components in basis)
  • basis: the coordinate basis (chart) in which to compute
  • metric_derivs: optional rank-3 array where dg[c,a,b] = ∂_c g_{ab}. If omitted, assumes constant metric (all derivatives zero → all Christoffels zero).

Returns the CTensorObj stored under the auto-created Christoffel tensor name.

source
XAct.XTensor.component_valueMethod
component_value(tensor, indices, bases) → Any

Return a single component value from a stored CTensor. indices are 1-based integer indices into the array.

source
XAct.XTensor.ctensor_contractMethod
ctensor_contract(tensor, bases, slot1, slot2) → CTensorObj

Contract (trace) two indices of a CTensor. Both slots must be in the same basis. The result has rank reduced by 2. For rank-2, this is the matrix trace.

source
XAct.XTensor.def_basis!Method
def_basis!(name, vbundle, cnumbers) → BasisObj

Define a basis of vector fields on a vector bundle. cnumbers are integer labels for the basis elements (length must equal dim of vbundle). Auto-creates a parallel derivative symbol PD<name>.

source
XAct.XTensor.def_chart!Method
def_chart!(name, manifold, cnumbers, scalars) → ChartObj

Define a coordinate chart on a manifold. Internally creates a BasisObj (coordinate basis) and registers the coordinate scalar fields as tensors. scalars are the coordinate field names, e.g. [:t, :r, :theta, :phi].

source
XAct.XTensor.def_metric!Method
def_metric!(signdet, metric_expr, covd_name) → MetricObj

Define a metric tensor and auto-create curvature tensors. metricexpr: e.g. "Cng[-cna,-cnb]" covdname: e.g. "Cnd" (used as suffix for auto-created curvature tensors)

source
XAct.XTensor.def_perturbation!Method
def_perturbation!(tensor, background, order) → PerturbationObj

Register a perturbation tensor.

  • tensor — name of the perturbed tensor (e.g. :Pertg1)
  • background — name of the background tensor it perturbs (e.g. :g)
  • order — perturbation order (≥ 1)

The perturbed tensor must already be registered (via def_tensor!). The background tensor must already be registered (via def_tensor! or def_metric!). Raises an error if either tensor is unknown, order < 1, or the perturbation is already defined.

source
XAct.XTensor.def_tensor!Method
def_tensor!(name, index_specs, manifold; symmetry_str=nothing) → TensorObj

Define a new abstract tensor. index_specs: vector of strings like ["-bta","-btb"] or ["bta"].

source
XAct.XTensor.def_tensor!Method
def_tensor!(name, index_specs, manifolds::Vector{Symbol}; symmetry_str=nothing) → TensorObj

Multi-index-set variant: each index label must belong to one of the given manifolds. The first manifold in the list is used as the primary manifold stored in TensorObj.manifold. This enables tensors that mix indices from e.g. spacetime and internal gauge manifolds.

source
XAct.XTensor.get_componentsMethod
get_components(tensor, bases) → CTensorObj

Retrieve stored component values for a tensor in the given bases. If not directly stored, attempts to transform from a stored basis configuration using registered basis changes.

source
XAct.XTensor.perturbMethod
perturb(expr::AbstractString, order::Int) → String

Apply the Leibniz rule to expand perturbations of a tensor expression at the given order.

Supported forms

  • Single tensor name — looks up registered perturbation for that background. Index decorations (e.g. Cng[-a,-b]) are stripped before lookup.
  • Sum A + Bperturb(A,n) + perturb(B,n).
  • Difference A - Bperturb(A,n) - perturb(B,n).
  • Product A B or A * B — general Leibniz (multinomial) rule: $δⁿ(A₁⋯Aₖ) = Σ C(n;i₁,…,iₖ) δⁱ¹(A₁)⋯δⁱᵏ(Aₖ)$ where the sum runs over all non-negative integer compositions $i₁+⋯+iₖ = n$ and $C$ is the multinomial coefficient.
  • Numeric coefficient c A — coefficient passes through unchanged.
  • Factor with no registered perturbation — treated as background (variation = 0).
source
XAct.XTensor.perturbMethod
perturb(tensor_name::Symbol, order::Int) → String

Look up the registered perturbation tensor for tensor_name at the given perturbation order. Returns the perturbation tensor name as a String, or throws an error if no such perturbation is registered.

source
XAct.XTensor.perturb_curvatureMethod
perturb_curvature(covd_name, metric_pert_name; order=1) → Dict{String,String}

Return the first-order perturbation formulas for the Riemann tensor, Ricci tensor, Ricci scalar, and Christoffel symbol perturbation for the metric associated with covd_name, using metric_pert_name as the first-order metric perturbation h_{ab}.

The formulas are returned in the system's CovD string notation using the manifold's first four abstract index labels. Index positions: a = idxs[1], b = idxs[2], c = idxs[3], d = idxs[4]

Standard GR perturbation theory (xPert conventions)

First-order Christoffel perturbation: δΓ^a{bc} = (1/2) g^{ad} (∇b h{cd} + ∇c h{bd} - ∇d h_{bc})

First-order Riemann perturbation (fully covariant): δR{abcd} = ∇c δΓ{abd} - ∇d δΓ_{abc} (with all indices lowered using the background metric)

First-order Ricci perturbation: δR{ab} = ∇c δΓ^c{ab} - ∇b δΓ^c{ac} = (1/2)(∇c ∇a h^cb + ∇c ∇b h^ca - □h{ab} - ∇a ∇b h)

First-order Ricci scalar perturbation: δR = g^{ab} δR{ab} - R{ab} h^{ab}

The returned dict has keys: "Christoffel1" — δΓ expressed in CovD notation (mixed index) "Riemann1" — δR{abcd} in CovD notation "Ricci1" — δR{ab} in CovD notation "RicciScalar1" — δR string formula (contracted Ricci)

All expressions use abstract index labels from the metric's manifold.

source
XAct.XTensor.set_basis_change!Method
set_basis_change!(from_basis, to_basis, matrix) → BasisChangeObj

Register a coordinate transformation between two bases. The matrix transforms components from from_basis to to_basis. Both the forward (from→to) and inverse (to→from) directions are stored.

Validates:

  • Both bases exist (via BasisQ)
  • Both bases belong to the same vector bundle
  • Matrix is square with size matching the basis dimension
  • Matrix is invertible (non-singular)
source
XAct.XTensor.set_components!Method
set_components!(tensor, array, bases; weight=0) → CTensorObj

Store component values for a tensor in the given bases.

Validates:

  • Tensor exists (via TensorQ or MetricQ)
  • Each basis exists (via BasisQ)
  • Array rank matches number of bases
  • Each array dimension matches the basis dimension (length of CNumbersOf)
source
XAct.XTensor.set_symbol_hooks!Method
set_symbol_hooks!(validate, register)

Install XCore symbol-validation and registration hooks.

Called by XAct.jl after loading both XCore and XTensor:

XTensor.set_symbol_hooks!(XCore.ValidateSymbol, XCore.register_symbol)
source
XAct.XTensor.BasisObjType

A basis of vector fields on a vector bundle (non-coordinate frame). Created by def_basis! or internally by def_chart!.

source
XAct.XTensor.MultiTermIdentityType
MultiTermIdentity

A multi-term identity relating N canonical tensor terms by a linear relation.

The identity asserts: Σᵢ coefficients[i] * T[slotperms[i](freeindices)] = 0

Fields:

  • name: identity label (e.g. :FirstBianchi)
  • tensor: which tensor this applies to (e.g. :RiemannCD)
  • n_slots: total tensor rank (4 for Riemann)
  • fixed_slots: slot positions held constant across terms
  • cycled_slots: slot positions permuted across terms
  • slot_perms: for each term, the rank-permutation of cycled_slot values
  • coefficients: coefficient of each term in the identity (Σ coefficients[i] * X_i = 0)
  • eliminate: which term index to eliminate (reduce away)

Example — First Bianchi identity R_{a[bcd]} = 0: Three canonical forms for 4 distinct indices p < q < r < s: X₁ = R[p,q,r,s] → cycled ranks [1,2,3] X₂ = R[p,r,q,s] → cycled ranks [2,1,3] X₃ = R[p,s,q,r] → cycled ranks [3,1,2] Identity: X₁ - X₂ + X₃ = 0; eliminate X₃.

source
XAct.XTensor.PerturbationObjType

A perturbation of a tensor: records the perturbed tensor name, its background tensor name, and the perturbation order (1 = first order, 2 = second order, ...).

source
XAct.XTensor.SessionType
Session

Holds all mutable state for one xAct session. Replaces 22 global containers. Enables concurrent sessions, proper reset semantics, and structural thread safety.

The default session shares its dict objects with the module-level globals, so existing code that reads/writes globals implicitly uses the default session.

source
XAct.XTensor.SymmetrySpecType

Describes the permutation symmetry of a tensor's slot group. type — one of: :Symmetric, :Antisymmetric, :GradedSymmetric, :RiemannSymmetric, :YoungSymmetry, :NoSymmetry slots — 1-indexed positions (within this tensor's slot list) that the symmetry acts on. For :RiemannSymmetric, exactly 4 elements. For :NoSymmetry, empty.

source
XAct.XInvar.InvSimplifyFunction
InvSimplify(rinv::RInv, level::Int=6; db::InvarDB, dim=nothing) -> InvExpr

Simplify a single Riemann invariant using pre-computed database rules. Returns a linear combination of independent invariants.

Levels:

  • 1: identity (no simplification)
  • 2: cyclic identity rules
  • 3: + Bianchi identity rules
  • 4: + CovD commutation rules
  • 5: + dimension-dependent rules (requires integer dim)
  • 6: + dual reduction rules (requires dim == 4)

Source: Invar.m:628-678

source
XAct.XInvar.InvSimplifyFunction
InvSimplify(expr::InvExpr, level::Int=6; db::InvarDB, dim=nothing) -> InvExpr

Simplify a linear combination of Riemann invariants.

Dual invariants (n_epsilon == 1) require dim == 4. An ArgumentError is raised if any dual term is present and dim is not 4.

source
XAct.XInvar.InvToPermMethod
InvToPerm(rinv::RInv; db::InvarDB) -> RPerm

Reverse lookup: given an RInv, return the canonical RPerm from the database.

For dual invariants (rinv.case.n_epsilon == 1), looks up in the dual permutation tables.

Throws ArgumentError if the invariant index is not found.

source
XAct.XInvar.InvarCasesMethod
InvarCases(order, degree) -> Vector{InvariantCase}

Non-dual cases for a given order and degree (number of Riemann tensors).

source
XAct.XInvar.InvarCasesMethod
InvarCases(order) -> Vector{InvariantCase}

Non-dual cases for a given even derivative order (2 ≤ order ≤ 14). Degrees enumerate from highest to lowest.

source
XAct.XInvar.InvarCasesMethod
InvarCases() -> Vector{InvariantCase}

All non-dual invariant cases through order 14 (48 cases). Matches Wolfram InvarCases[].

source
XAct.XInvar.InvarDualCasesMethod
InvarDualCases() -> Vector{InvariantCase}

All dual invariant cases through order 10. Matches Wolfram InvarDualCases[].

source
XAct.XInvar.LoadInvarDBMethod
LoadInvarDB(dbdir::String; dim::Int=4) -> InvarDB

Load all Invar database files from dbdir.

The database directory should contain a Riemann/ subdirectory with the standard step structure. Missing files are skipped with a warning.

Arguments:

  • dbdir: Path to the directory containing Riemann/.
  • dim: Spacetime dimension for dimension-dependent rules (steps 5, 6). Default: 4.
source
XAct.XInvar.MaxDualIndexMethod
MaxDualIndex(case) -> Int

Number of independent dual Riemann invariants for a given case.

Source: Invar.m:455-483

source
XAct.XInvar.MaxIndexMethod
MaxIndex(case) -> Int

Number of independent Riemann invariants for a given case. Accepts InvariantCase, Vector{Int} (deriv_orders), or Int (pure algebraic degree).

Source: Invar.m:389-451

source
XAct.XInvar.PermDegreeMethod
PermDegree(case::InvariantCase) -> Int

Permutation degree (number of index slots) for an invariant case.

Formula: 4 * n_riemanns + sum(deriv_orders) + 4 * n_epsilon

Source: Invar.m:696

source
XAct.XInvar.PermToInvMethod
PermToInv(rperm::RPerm; db::InvarDB) -> RInv

Look up the invariant label for a canonical RPerm from the loaded database.

The RPerm's permutation must already be in canonical form (as produced by RiemannToPerm). Returns the corresponding RInv with the database index.

For dual invariants (rperm.case.n_epsilon == 1), looks up in the dual permutation tables.

Throws ArgumentError if the permutation is not found in the database.

source
XAct.XInvar.PermToRiemannMethod
PermToRiemann(rperm::RPerm; covd::Symbol=rperm.metric, curvature_relations::Bool=false) -> String

Convert an RPerm back to a tensor expression string.

The contraction permutation is an involution: perm[i]=j means slot i contracts with slot j. Each pair gets a unique index name. The lower-numbered slot gets the covariant (down) index, the higher slot gets the contravariant (up) index.

If curvature_relations=true, contracted Riemann tensors are replaced with Ricci or RicciScalar where applicable.

source
XAct.XInvar.RiemannSimplifyMethod
RiemannSimplify(expr, metric; covd, level, curvature_relations, db, dim) -> String

Simplify a Riemann scalar expression using the Invar database.

Pipeline: parse → RiemannToPerm → PermToInv → InvSimplify → InvToPerm → PermToRiemann.

Arguments

  • expr::String: tensor expression (fully contracted scalar)
  • metric::Symbol: the metric symbol
  • covd::Symbol=metric: covariant derivative name (determines tensor prefixes)
  • level::Int=6: InvSimplify level (1-6)
  • curvature_relations::Bool=false: replace contracted Riemanns with Ricci/RicciScalar
  • db::InvarDB: loaded Invar database
  • dim::Union{Int,Nothing}=nothing: manifold dimension (needed for levels 5-6)

Returns

A simplified tensor expression string, or "0" if all terms cancel.

Source: Invar.m:834-839

source
XAct.XInvar.RiemannToPermMethod
RiemannToPerm(expr::String, metric::Symbol; covd::Symbol=metric)

Convert a Riemann scalar expression into canonical RPerm permutation form.

Returns Vector{Tuple{Rational{Int}, RPerm}} — one entry per term.

The expression should use pre-canonicalized index ordering. The covd keyword determines the tensor name prefix (default: same as metric).

Examples

RiemannToPerm("RiemannCD[-a,-b,-c,-d] RiemannCD[a,b,c,d]", :g; covd=:CD)
RiemannToPerm("RicciScalarCD[]", :g; covd=:CD)
source
XAct.XInvar._all_block_permutationsMethod
_all_block_permutations(groups, n, slot_ranges) -> Vector{Tuple{Vector{Int}, Int}}

Generate all block permutations respecting same-derivative-order groups. Returns (blockperm, sign) pairs where blockperm[i] = position that factor i maps to.

source
XAct.XInvar._all_permutations_ofMethod
_all_permutations_of(indices::Vector{Int}) -> Vector{Tuple{Vector{Int}, Int}}

Generate all permutations of indices as (permutedlist, signrelativetosorted) pairs.

source
XAct.XInvar._apply_block_perm_to_contractionMethod
_apply_block_perm_to_contraction(perm, block_perm, slot_ranges, degree) -> Vector{Int}

Apply a block (factor) permutation to a contraction permutation. block_perm[i] = j means factor i moves to position j.

source
XAct.XInvar._apply_step_rulesMethod
_apply_step_rules(expr::InvExpr, step::Int, db::InvarDB; dim::Int=4) -> InvExpr

Apply substitution rules from database step to each term in the expression. Dependent invariants are replaced with linear combinations of independent ones.

For dual invariants (n_epsilon == 1), uses db.dual_rules instead of db.rules.

source
XAct.XInvar._backtrack_riemann_syms!Method
_backtrack_riemann_syms!(perm, sign, factor, ...)

Recursively apply Riemann symmetries to factors factor..n_factors, pruning branches where frozen positions are already worse than best_perm.

Uses two pruning levels:

  • Frozen: position j where slot_to_factor[j] ≤ k and slot_to_factor[perm[j]] ≤ k — value is exact, compare directly.
  • Bounded: position j where slot_to_factor[j] ≤ k but slot_to_factor[perm[j]] > k — value will change but is bounded to [slot_lb[v], slot_ub[v]]. Prune if lb > best, keep if ub < best.
source
XAct.XInvar._build_case_dispatchMethod
_build_case_dispatch(index_to_perm, case) -> Dict{Vector{Int}, Int}

Build a reverse lookup for a single case: canonical involution → invariant index. DB permutations are converted from Invar labeling convention to contraction involutions and canonicalized to match the output of _canonicalize_contraction_perm.

source
XAct.XInvar._build_case_dispatch_rawMethod
_build_case_dispatch_raw(index_to_perm) -> Dict{Vector{Int}, Int}

Build a reverse lookup for a single case using raw DB permutations (no conversion). Used for dual cases where the epsilon tensor slots complicate canonicalization.

source
XAct.XInvar._canonicalize_contraction_permMethod
_canonicalize_contraction_perm(perm, case) -> (canonical_perm, sign)

Canonicalize a contraction permutation under the symmetry group of a product of Riemann tensors. The symmetry group combines:

  1. Riemann pair symmetries (8 elements per factor): swap (a,b) sign=-1, swap (c,d) sign=-1, exchange pairs (a,b)↔(c,d) sign=+1.
  2. Block permutations of factors with the same derivative order (sign = parity).

Returns the lexicographically minimal permutation and its sign.

source
XAct.XInvar._case_to_filenameMethod
_case_to_filename(deriv_orders::Vector{Int}) -> String

Encode a case vector as a filename component. [0,0]"0_0", [0]"0", [1,3]"1_3".

Matches Wolfram intercase (Invar.m:252).

source
XAct.XInvar._classify_caseMethod
_classify_case(expr::String, metric::Symbol) -> InvariantCase

Classify a single monomial (product of Riemann tensors, possibly with CovD derivatives) into an InvariantCase. Ricci/RicciScalar should already be replaced with Riemann.

source
XAct.XInvar._cycles_to_imagesMethod
_cycles_to_images(cycles::Vector{Vector{Int}}, degree::Int) -> Vector{Int}

Convert a list of disjoint cycles to images (one-line) notation.

Each cycle [a, b, c, ...] means a→b, b→c, ..., last→a. Fixed points map to themselves.

Example: [[2,1],[4,3]] on degree 4 → [2,1,4,3] Example: [[1,3,2]] on degree 4 → [3,2,1,4] (1→3, 3→2, 2→1, 4→4)

source
XAct.XInvar._dinv_filenameMethod
_dinv_filename(step::Int, case::Vector{Int}; dim::Int=0) -> String

Build a DInv (dual) filename. Matches Wolfram dualfilename (Invar.m:259-260).

source
XAct.XInvar._ensure_case_dispatchMethod
_ensure_case_dispatch(db::InvarDB, case_key::Vector{Int}, is_dual::Bool) -> Dict{Vector{Int}, Int}

Return the cached dispatch table for a specific case, building it lazily if needed. Non-dual cases convert from Invar labeling to canonical involutions. Dual cases use raw DB perms (looked up via _involution_to_invar_perm conversion).

source
XAct.XInvar._ensure_invar_dbFunction
_ensure_invar_db(; dbdir::String="", dim::Int=4) -> InvarDB

Load the Invar database for the given dimension if not already cached. Returns the cached instance. If dbdir is empty, searches standard resource paths.

source
XAct.XInvar._extract_contraction_permMethod
_extract_contraction_perm(expr::String, case::InvariantCase) -> Vector{Int}

Extract the contraction permutation from a monomial string.

Slot assignment: for each Riemann factor (left to right), CovD indices come before the 4 Riemann indices. The result is an involution: perm[i] = j and perm[j] = i for each contracted pair (i, j).

source
XAct.XInvar._find_covd_splitMethod
_find_covd_split(content::String) -> Union{Int, Nothing}

Find the position of '][' in content that indicates a CovD split. Returns the position of the ']' or nothing.

source
XAct.XInvar._fresh_indicesMethod
_fresh_indices(n::Int, used::Set{String}) -> Vector{String}

Generate n fresh index names not in used. Mutates used by adding the new names.

source
XAct.XInvar._inv_expr_to_stringMethod
_inv_expr_to_string(expr::InvExpr; covd, curvature_relations, db) -> String

Convert a simplified InvExpr back to a tensor expression string.

source
XAct.XInvar._invar_perm_to_involutionMethod
_invar_perm_to_involution(σ::Vector{Int}) -> Vector{Int}

Convert a Wolfram Invar "canonical labeling" permutation to a contraction involution. In the Invar convention, σ(i) gives the position of slot i in a canonical paired arrangement where pairs occupy consecutive positions (1,2), (3,4), etc. The returned involution maps each slot to the slot it contracts with: invol[i] = j means slot i and slot j share the same dummy index.

source
XAct.XInvar._involution_to_invar_permMethod
_involution_to_invar_perm(invol::Vector{Int}) -> Vector{Int}

Convert a contraction involution back to the Wolfram Invar "canonical labeling" convention. Contracted pairs are assigned consecutive positions (1,2), (3,4), etc. in the order they appear (scanning slots left to right).

source
XAct.XInvar._parse_coefficientMethod
_parse_coefficient(s::String) -> Rational{Int}

Parse a coefficient string that may include:

  • Empty or "+" → 1//1
  • "-" → -1//1
  • "2*" → 2//1
  • "-3/2*" → -3//2
  • "sigma*" → 1//1 (sigma is the sign of the metric determinant, treated as symbolic)
  • "- sigma*" → -1//1

Note: sigma appears in some step-6 (dual) rules. We store it as ±1 since the actual sign is resolved at application time.

source
XAct.XInvar._parse_idx_listMethod
_parse_idx_list(s::String) -> Vector{String}

Parse a comma-separated index list like "-a,-b,c,d" into ["-a", "-b", "c", "d"].

source
XAct.XInvar._parse_invar_monomialMethod
_parse_invar_monomial(mono::String) -> (Rational{Int}, Vector{_InvarFactor})

Parse a monomial string into its numeric coefficient and tensor factors. Handles CovD notation: CD[-e][RiemannCD[-a,-b,-c,-d]] is parsed as a single factor with covd_indices=["-e"] and tensor RiemannCD with indices ["-a","-b","-c","-d"].

source
XAct.XInvar._parse_invar_sumMethod
_parse_invar_sum(expr::String) -> Vector{Tuple{Int, String}}

Split an expression on top-level + and - (not inside brackets) into signed monomial strings. Returns (sign, monomial_string) pairs.

source
XAct.XInvar._parse_linear_combinationMethod
_parse_linear_combination(s::String) -> Vector{Tuple{Int, Rational{Int}}}

Parse a linear combination of RInv/DualRInv terms.

Examples:

  • RInv[{0,0},1] - RInv[{0,0},2] → [(1, 1//1), (2, -1//1)]
  • 2*RInv[{0,0},1] → [(1, 2//1)]
  • -3/2*RInv[{0,0},1] + RInv[{0,0},2] → [(1, -3//2), (2, 1//1)]
  • RInv[{0,0},2]/2 → [(2, 1//2)] (trailing division)
  • -RInv[{0,0,0},5]/4+RInv[{0,0,0},8] → [(5, -1//4), (8, 1//1)]
source
XAct.XInvar._parse_maple_perm_lineMethod
_parse_maple_perm_line(line::String) -> Union{Nothing, Vector{Int}}

Parse a single line of step-1 (Maple format) perm file.

Handles two formats:

  1. Legacy: RInv[{0,0},1] := [[2,1],[4,3],[6,5],[8,7]];
  2. Current (xact.es download): [[2,3],[4,5],[6,7]]: (bare cycles + trailing colon)

The Wolfram parser (ReadInvarPerms, Invar.m:284) uses readline to strip the prefix before := and the trailing ; or :, then replacebrackets and ToExpression.

Returns nothing for blank or comment lines.

source
XAct.XInvar._parse_mma_ruleMethod
_parse_mma_rule(line::String) -> Union{Nothing, Tuple{Int, Vector{Tuple{Int, Rational{Int}}}}}

Parse a single line of steps 2-6 (Mathematica format) rule file.

Format: RInv[{0,0},3] -> RInv[{0,0},1] - RInv[{0,0},2]

Returns (dependent_index, [(independent_index, coefficient), ...]) or nothing.

The LHS is always a single RInv[{case},idx] or DualRInv[{case},idx]. The RHS is a linear combination of RInv/DualRInv terms with rational coefficients.

source
XAct.XInvar._parse_nested_intlistMethod
_parse_nested_intlist(s::String) -> Vector{Vector{Int}}

Parse a string like [[2,1],[4,3],[6,5],[8,7]] into a vector of int vectors. Handles both [...] (Maple) and {...} (Mathematica) bracket styles.

source
XAct.XInvar._parse_one_factorMethod
_parse_one_factor(s, pos) -> (_InvarFactor or nothing, new_pos)

Parse one tensor factor starting at position pos. Handles both plain TensorName[indices] and CovD-wrapped CovD[-i][...CovD[-j][Tensor[indices]]...].

source
XAct.XInvar._perm_sign_ofMethod
_perm_sign_of(arr::Vector{Int}) -> Int

Compute the sign (parity) of the permutation arr relative to its sorted order.

source
XAct.XInvar._ricci_to_riemannMethod
_ricci_to_riemann(expr::String, covd::Symbol) -> String

Replace Ricci and RicciScalar tensors with their contracted Riemann equivalents.

  • RicciCD[-a,-b]RiemannCD[xa,-a,-xa,-b] (contracted Riemann)
  • RicciScalarCD[]RiemannCD[xa,xb,-xa,-xb] (double-contracted Riemann)

The covd name determines the tensor prefix (e.g., covd=:CD → RiemannCD, RicciCD, etc.).

source
XAct.XInvar._riemann_to_ricciMethod
_riemann_to_ricci(expr::String, covd::Symbol) -> String

Replace contracted Riemann patterns with Ricci/RicciScalar where applicable. A Riemann factor with indices that self-contract can be simplified.

source
XAct.XInvar._rinv_filenameMethod
_rinv_filename(step::Int, case::Vector{Int}; dim::Int=0) -> String

Build an RInv filename. Matches Wolfram filename (Invar.m:255-256).

filename[step:(5|6), case, dim] = filename[step, case] * "_" * dim
filename[step, case] = "RInv-" * intercase(case) * "-" * step
source
XAct.XInvar._step_subdirMethod
_step_subdir(step::Int; dim::Int=4) -> String

Return the subdirectory name for a given step.

Step 1: "1"
Step 2: "2"
Step 3: "3"
Step 4: "4"
Step 5: "5_$dim" (e.g. "5_4")
Step 6: "6_$dim" (e.g. "6_4")
source
XAct.XInvar._swap_slots!Method
_swap_slots!(perm::Vector{Int}, a::Int, b::Int)

Conjugate the contraction permutation by the transposition (a b). This swaps slots a and b: perm → (a b) ∘ perm ∘ (a b).

source
XAct.XInvar.read_invar_permsMethod
read_invar_perms(filepath::String; degree::Int=0) -> Dict{Int, Vector{Int}}

Read a step-1 (Maple format) permutation basis file. Returns Dict mapping invariant index (1-based, positional) to permutation in images notation.

Each line in the file defines one invariant's contraction permutation. Lines are indexed sequentially: line 1 = invariant 1, line 2 = invariant 2, etc.

The degree parameter specifies the permutation degree (number of index slots). If 0, the degree is inferred from the max element in the cycles (may be too small if the last positions are fixed points).

Reference: Wolfram ReadInvarPerms (Invar.m:284).

source
XAct.XInvar.read_invar_rulesMethod
read_invar_rules(filepath::String) -> Dict{Int, Vector{Tuple{Int, Rational{Int}}}}

Read a steps 2-6 (Mathematica format) rule file. Returns Dict mapping dependent invariant index to its linear combination of independent invariants: [(index, coefficient), ...].

Reference: Wolfram ReadInvarRules (Invar.m:293).

source
XAct.XInvar.InvarDBType
InvarDB

Holds all loaded database state: permutation bases (step 1) and substitution rules (steps 2-6).

Fields:

  • perms: case → (index → permutation in images notation)
  • dual_perms: case → (index → permutation in images notation)
  • rules: step → (case → (dependentindex → [(independentindex, coefficient)]))
  • dual_rules: step → (case → (dependentindex → [(independentindex, coefficient)]))
source
XAct.XInvar.InvariantCaseType
InvariantCase(deriv_orders, n_epsilon=0)

Classifies a Riemann scalar monomial by the derivative orders on each Riemann factor and the number of epsilon (Levi-Civita) tensors.

  • deriv_orders: sorted non-decreasing list; length = degree (number of Riemanns)
  • n_epsilon: 0 = non-dual, 1 = dual (4D only)

The derivative order of an invariant case is 2 * degree + sum(deriv_orders).

source
XAct.XInvar.RInvType
RInv(metric, case, index)

A labeled Riemann invariant with a canonical index (1-based) from the Invar database.

source
XAct.XInvar.RPermType
RPerm(metric, case, perm)

A Riemann invariant in permutation representation. The permutation encodes the contraction pattern of indices across all tensor factors in images notation.

source
XAct.XInvar._perm_dispatchConstant

Global cached dispatch tables. Built lazily per case on first PermToInv call. Perm→index mapping is dimension-independent (structural, not rule-dependent).

source
XAct.TExprLayer._parse_to_texprMethod
_parse_to_texpr(s::AbstractString) -> TExpr

Parse an engine output string into a typed expression tree.

Supported formats (same as _to_string output):

  • "0"TScalar(0//1)
  • "Name[i1,i2]"TTensor
  • "Name[-i][operand]"TCovD
  • "2 * Name[...]", "(1/2) * Name[...]", "-Name[...]"TProd
  • "A + B", "A - B"TSum
source
XAct.TExprLayer.covdMethod
covd(name::Symbol) -> CovDHead

Look up a registered covariant derivative and return a CovDHead handle. Throws if name is not a defined CovD.

source
XAct.TExprLayer.tensorMethod
tensor(name::Symbol) -> TensorHead

Look up a registered tensor and return a TensorHead handle. Throws if the tensor is not defined (e.g. after reset_state!()).

source
XAct.TExprLayer.TSymbolType

Bare symbol returned by the engine (e.g. a perturbation tensor name without indices). Serialises back to the bare name string.

source
XAct.TExprLayer.@indicesMacro
@indices M a b c d ...

Declare index variables bound to manifold M. Generates runtime Idx constructor calls (with validation) that assign each name in the current scope.

Example:

def_manifold!(:M, 4, [:a, :b, :c, :d])
@indices M a b c d
# a = Idx(:a, :M), b = Idx(:b, :M), ...
source

Typed Expression API (TExprLayer)

The TExprLayer module provides a typed, validated expression layer on top of the string-based engine API.

Current behavior: engine functions accept TExpr inputs, serialize them into the existing string-based engine, then reconstruct typed results on the way out where supported.

Index Types

# Declare index variables bound to manifold M
def_manifold!(:M, 4, [:a, :b, :c, :d])
@indices M a b c d
# a = Idx(:a, :M), b = Idx(:b, :M), ...

# Covariant (down) index via negation
-a   # DnIdx wrapping a
--a  # back to Idx (identity)
TypeDescription
Idx(label, manifold)Contravariant (up) index bound to a manifold
DnIdx(parent)Covariant (down) index; produced by -idx
SlotIdxUnion of Idx and DnIdx

Expression Types

TypeDescriptionExample
TTensorTensor with indices appliedT[-a, -b]
TProdProduct with rational coefficient2 * T[-a] * V[a]
TSumSum of expressionsT[-a,-b] + S[-a,-b]
TCovDCovariant derivative applied to an expression, e.g. a scalar fieldCD[-a] acting on a scalar tensor

Handles (not TExpr)

TypeDescription
TensorHeadLightweight tensor name handle; apply indices via T[...]
CovDHeadCovariant derivative handle; apply index via CD[-a]

Factory Functions

# Declare typed index variables
@indices M a b c d       # binds a, b, c, d as Idx(:a,:M), ...

# Look up registered tensor by name
Riem = tensor(:RiemannCD)   # or tensor("RiemannCD")
g    = tensor(:g)

# Look up registered covariant derivative
CD = covd(:CD)

Operator Overloading

def_manifold!(:M, 4, [:a, :b, :c, :d])
def_metric!(-1, "g[-a,-b]", :CD)
@indices M a b c d

# Apply indices
Riem = tensor(:RiemannCD)
expr = Riem[-a, -b, -c, -d]          # TTensor

# Arithmetic
T = tensor(:T)
prod = T[-a, -b] * T[a, c]           # TProd
sum  = T[-a, -b] + T[-b, -a]         # TSum
neg  = -T[-a, -b]                    # TProd(coeff=-1, ...)
scl  = 2 * T[-a, -b]                 # TProd(coeff=2, ...)

# Covariant derivative
CD = covd(:CD)
phi = tensor(:phi)
deriv = CD[-a](scalar_field)          # TCovD over a scalar expression

Validation at Construction Time

Errors are raised when the expression is built, not deep in the engine:

# Wrong slot count
T[-a]                   # ERROR: T has 2 slots, got 1

# Index from wrong manifold
@indices N p q
T[-p, -q]               # ERROR: p is from manifold N, but T expects M

Engine Integration

All engine functions accept TExpr in addition to String:

# These two are equivalent:
ToCanonical("RiemannCD[-a,-b,-c,-d] + RiemannCD[-a,-c,-d,-b]")

Riem = tensor(:RiemannCD)
@indices M a b c d
ToCanonical(Riem[-a,-b,-c,-d] + Riem[-a,-c,-d,-b])

Supported: ToCanonical, Contract, Simplify, perturb, CommuteCovDs, SortCovDs, IBP, TotalDerivativeQ, VarD.