Wolfram Language Paclet Repository

Community-contributed installable additions to the Wolfram Language

Primary Navigation

    • Cloud & Deployment
    • Core Language & Structure
    • Data Manipulation & Analysis
    • Engineering Data & Computation
    • External Interfaces & Connections
    • Financial Data & Computation
    • Geographic Data & Computation
    • Geometry
    • Graphs & Networks
    • Higher Mathematical Computation
    • Images
    • Knowledge Representation & Natural Language
    • Machine Learning
    • Notebook Documents & Presentation
    • Scientific and Medical Data & Computation
    • Social, Cultural & Linguistic Data
    • Strings & Text
    • Symbolic & Numeric Computation
    • System Operation & Setup
    • Time-Related Computation
    • User Interface Construction
    • Visualization & Graphics
    • Random Paclet
    • Alphabetical List
  • Using Paclets
    • Get Started
    • Download Definition Notebook
  • Learn More about Wolfram Language

TensorNetworks

Tutorials

  • Index conventions and contraction path

Guides

  • TensorNetworks

Tech Notes

  • Building Tensor Networks
  • Contraction Paths and Execution
  • Named Indices and Metrics
  • Matrix Product States
  • A Working Tour of the Symmetry Functions
  • Tensor Networks Overview
  • Young Tableaux and Tensor Symmetries

Symbols

  • ActivateTensors
  • ArraySymmetry
  • BinaryTensorNetwork
  • BinaryTensorNetworkQ
  • CanonicalPath
  • CanonicalPathQ
  • ContractIndices
  • ContractionTree
  • Dimension
  • DimensionQ
  • EinsteinSummation
  • GreedyContractionPath
  • HookFactor
  • HookLength
  • HookLengths
  • IndexArray
  • IndexArrayQ
  • IndexContract
  • IndexedMultiply
  • IndexJuggling
  • IndexPart
  • IndexTensor
  • IndexTensorQ
  • InitializeTensorNetwork
  • MetricTensor
  • MetricTensorQ
  • MPSCanonicalForm
  • MPSCanonicalQ
  • MPSEntanglementEntropy
  • MPSNormalize
  • MPSNorm
  • MPSOverlap
  • MPSSchmidtValues
  • MPSTruncate
  • OptimalContractionPath
  • PartitionQ
  • PathIndexContractions
  • PathQ
  • PathToTreePath
  • RandomTensorNetwork
  • SchurDimension
  • Shape
  • ShapeQ
  • SparseTensorNetwork
  • TableauColumns
  • TableauDimension
  • TableauRows
  • TableauShape
  • TableauSize
  • TableauWeylDimension
  • TensorNetworkAdd
  • TensorNetworkContraction
  • TensorNetworkContractions
  • TensorNetworkContract
  • TensorNetworkData
  • TensorNetworkDelete
  • TensorNetworkFindContractionPath
  • TensorNetworkFreeIndices
  • TensorNetworkGraphData
  • TensorNetworkGraphQ
  • TensorNetworkIndexDimensions
  • TensorNetworkIndexGraph
  • TensorNetworkIndices
  • TensorNetwork
  • TensorNetworkQ
  • TensorNetworkRemoveCycles
  • TensorNetworkReplaceIndices
  • TensorNetworkSize
  • TensorNetworkTensors
  • TensorNetworkToNetGraph
  • ToTensorNetworkGraph
  • TransposePartition
  • TreePathQ
  • TreePathToPath
  • YoungProject
  • YoungSymmetrize
  • YoungTableau
  • YoungTableauQ
  • $TensorNetworkContractionMethods
Named Indices and Metrics
Plain
TensorNetwork
labels indices by integer position and treats every leg the same. For physics on a manifold and for symbolic tensor algebra one needs more: every index should carry a name, a variance (upper or lower), and a dimension, and contractions must respect the geometry encoded by a metric. The
Wolfram`TensorNetworks`IndexArray`
subpackage adds exactly that layer in five stages:
Dimension
describes a single index,
Shape
collects an ordered tuple of them,
IndexArray
wraps a numerical tensor with a Shape,
IndexTensor
attaches a metric for automatic raising and lowering, and
MetricTensor
exposes a catalog of the standard pseudo-Riemannian geometries. This tutorial works each layer in turn so that by the end one is computing with Schwarzschild and Kerr metrics with the same comfort as adding two matrices.
Needs["Wolfram`TensorNetworks`"]
1. Why Named Indices?
In tensor calculus an index is more than a position. It is a label on a chart of basis vectors. The expression
ρ
T
μν
names three indices, declares two of them lower and one upper, and implicitly fixes which basis they live in. None of that survives
TensorNetwork
's integer-labelled hyperedges: the contraction graph is correct but the indices have no identity beyond their numerical position, and one cannot distinguish a covariant pair from a contravariant pair without external bookkeeping.
The
IndexArray
layer fills that gap. The data carrier is a stack:
Dimension
[d, name] holds a size and a label and uses the sign of d for the variance – positive means contravariant (upper), negative means covariant (lower);
Shape
glues a list of Dimensions in order;
IndexArray
marries a tensor to a Shape and validates the match;
IndexTensor
attaches one or more metrics so that operations across mismatched variance can use the metric automatically; and
MetricTensor
specialises that wrapping to a rank-two, square, symmetric (or antisymmetric) object – precisely the geometric metric of differential geometry – and adds a curated set of named built-ins.
Two practical pay-offs are worth stating up front. First, every operation that contracts two indices can verify that the indices actually match by name and that their variances are compatible:
μ
v
w
μ
is allowed but
μ
v
μ
w
requires a metric to fix the sign mismatch. Second, the same Shape data drives display: an IndexArray prints with its index symbols decorated as bars and underbars to mark variance, making the type of an expression visible at a glance. The remainder of the tutorial walks each layer with worked examples.
2. Dimension and Shape
Load the named-index context. The IndexArray context is loaded implicitly by the main paclet but is also exposed for direct use:
Needs["Wolfram`TensorNetworks`IndexArray`"]
Needs["Wolfram`Arrays`"]
A
Shape
constructed from plain positive integers gives upper indices with default names
i
,
j
,
k
, … drawn from the latin alphabet starting at
i
:
Shape
[3,3]
The two equivalent ways to introduce variance are to pass an explicit Dimension list with negative sizes for lower indices, or to use the (size, name) form where a negated name marks the index as lower. Both produce the same Shape:
Shape

Dimension
[3,"i"],
Dimension
[-3,"j"]​​
Shape
[{3,3},{"i",-"j"}]
Property dispatch exposes the underlying data. The most useful keys are "Names" for the symbolic labels, "Dimensions" for the unsigned sizes (this is what
ArrayDimensions
on the underlying tensor returns), "Variance" for the per-index Boolean (
True
= upper), and "Rank" for the number of free indices. Together they recover everything one might want to know about the index structure:
s=
Shape
[{3,3,4},{"i",-"j","k"}]​​;​​​​{s["Names"],s["Dimensions"],s["Variance"],s["Rank"]}
Shape is the type and not the data: it is purely declarative and contains no tensor entries. Two Shapes compare equal under
Equal
when their dimensions match (with zero dimensions treated specially), independent of names – handy when threading shapes through transformations that rename indices.

2.3. DimensionQ and ShapeQ

The predicates
DimensionQ
and
ShapeQ
are the cheap validity check applied to each component before passing it to a constructor. A bare integer is not a Dimension, and a bare list of integers is not a Shape, even though the constructors will lift them when given the chance:
DimensionQ

Dimension
[3,"i"]​​
DimensionQ

Dimension
[3,"i",{"x","y","z"}]​​
DimensionQ
[3]
ShapeQ mirrors the pattern at the next level: a fully specified Shape of Dimensions, and a Shape built from raw integers (auto-converted at construction) both pass; a bare list does not.
ShapeQ

Shape

Dimension
[3,"i"],
Dimension
[-3,"j"]​​
ShapeQ

Shape
[3,3]​​
ShapeQ
[{3,3}]
3. The IndexArray Object
An
IndexArray
wraps a numerical (or symbolic) tensor together with a Shape that names its axes. There are three ways to construct one. The most explicit takes a tensor and a fully specified Shape; ArrayDimensions on the tensor must match Shape["Dimensions"], and validity is cached at construction:
mat={{1,2,3},{4,5,6},{7,8,9}}​​;​​​​ia=
IndexArray
mat,
Shape
[{3,3},{"i",-"j"}]​​;​​​​{ia["Names"],ia["Variance"],ia["SignedDimensions"]}
A shorter variance-only form passes a list of Booleans, one per axis, with
True
for upper indices and
False
for lower. Names are auto-generated. The shortest form supplies only the tensor and lets the wrapper derive everything from
ArrayDimensions
:
ia2=
IndexArray
[mat,{True,False}]​​;​​​​ia3=
IndexArray
[mat]​​;​​​​{ia2["SignedDimensions"],ia3["Names"],ia3["Dimensions"]}
Two distinct subscript forms surface the most common manipulations. Part syntax ia[[i, j]] slices, calling
IndexPart
under the hood; function-call syntax ia["k", "l"] renames or permutes axes, calling
IndexJuggling
. A negated name in the juggling form lowers (or raises) the index by toggling its sign:
m={{1.0,2.0},{3.0,4.0}}​​;​​​​ia=
IndexArray
m,
Shape
[{2,2},{"i","j"}]​​;​​​​{ia["k","l"]["Names"],ia[-"i","j"]["Variance"],ia〚1,2〛["Tensor"]}
Familiar matrix algebra is overloaded to preserve the Shape:
Transpose
swaps both the data and the index list,
Inverse
inverts the data and toggles every index sign (because the inverse of a (1,1) tensor is naturally a (1,1) tensor with the role of rows and columns reversed), and
Plus
of two IndexArrays succeeds whenever the multisets of (name, dimension) agree, permuting one operand as needed:
{Transpose[ia]["Names"],Transpose[ia]["Tensor"],Inverse[ia]["Tensor"]}
Other useful properties are "Tensor" (the unwrapped array; identical to
Normal
[ia] for explicit data), "Shape" (the full Shape object), "Symbol" (the rendered glyph used in box form, with bars and underbars to mark variance), and "Symmetry" (the permutation symmetry reported by
ArraySymmetry
).

3.4. IndexArrayQ

The predicate
IndexArrayQ
holds when the argument is an IndexArray expression whose wrapped tensor's dimensions agree with the declared Shape under any recorded assumptions. A bare matrix fails because it is not wrapped, even though every IndexArray constructor would happily accept it:
m=RandomReal[1,{3,3}]​​;​​​​ia=
IndexArray
m,
Shape
[3,3]​​;​​​​
IndexArrayQ
[ia]​​
IndexArrayQ
[m]
4. IndexTensor and the Metric
An IndexArray knows what its indices are called and how they covary, but it does not yet know how to identify an upper index with its lower partner. That bridge is a metric: a rank-two symmetric (or antisymmetric) IndexArray on indices of equal sign and dimension. The
IndexTensor
wrapper attaches such a metric to an IndexArray (or declares the IndexArray itself to be a metric). With a metric in place, contractions of mismatched-variance indices succeed by inserting the metric automatically.
To see this concretely, take the Euclidean metric on
3

and form the contraction of two upper-index vectors. Without a metric
i
u
i
v
is geometrically ill-formed (two upper indices cannot contract), but with the metric attached one of them is lowered on the way to the contraction and the inner product comes out as expected:
g=
MetricTensor
["Euclidean"[3]]​​;​​​​u=
IndexTensor

IndexArray
{1,2,3},
Shape

Dimension
[3,"i"],g["IndexArray"]​​;​​​​v=
IndexTensor

IndexArray
{4,5,6},
Shape

Dimension
[3,"i"],g["IndexArray"]​​;​​​​Activate
IndexContract
[{u,v}]["Tensor"]
The contraction returns an
Inactive
[
TensorContract
] expression so the index structure stays inspectable; one call to
Activate
evaluates it. For the Euclidean metric the result is the familiar dot product
1×4+2×5+3×6=32
. Switching the metric to
diag(-1,1,1,1)
(Minkowski) changes the sign of the time component and gives the spacetime norm:
g=
MetricTensor
["Minkowski"[4]]​​;​​​​u=
IndexTensor

IndexArray
{1,0,0,0},
Shape

Dimension
[4,"i"],g["IndexArray"]​​;​​​​Activate
IndexContract
[{u,u}]["Tensor"]
Lowering an index outside of a contraction is equally direct. Function-call syntax with a negated name swaps the variance flag, and behind the scenes the IndexTensor inserts the metric so that the data is mapped through
u
μ
=
g
μν
ν
u
:
u=
IndexTensor

IndexArray
{2,1,0,0},
Shape

Dimension
[4,"i"],g["IndexArray"]​​;​​​​Activate[u[-"i"]["Tensor"]]
The upper four-vector
μ
u
=(2,1,0,0)
becomes
u
μ
=(-2,1,0,0)
under the Minkowski signature, exactly as expected from
η
00
=-1
. The same machinery is used by
IndexContract
whenever a pair of indices sharing a name has mismatched signs: the index whose sign disagrees with the metric's natural form is dressed by the metric (or its inverse), and the contraction proceeds without further user intervention.

4.3. IndexTensorQ

The predicate
IndexTensorQ
validates the inner IndexArray and confirms that each attached metric is rank-two with matching dimensions on the indices it covers. An IndexArray on its own does not satisfy the predicate – the metric attachment is what makes the wrapping an IndexTensor:
m=RandomReal[1,{3,3}]​​;​​​​ia=
IndexArray
m,
Shape
[3,3]​​;​​​​metric=
MetricTensor
["Euclidean"[3]]["IndexTensor"]["IndexArray"]​​;​​​​it=
IndexTensor
[ia,metric]​​;​​​​
IndexTensorQ
[it]
5. The MetricTensor Catalog
Substitute Schwarzschild for Minkowski and the diagonal entries of the metric encode the line element
Reading those four coefficients off Diagonal[g["Matrix"]] is the quickest sanity check that the right metric is loaded:

5.5. MetricTensorQ

6. Array utilities

6.1. ArrayRank

6.2. ArrayName

6.3. ArrayPart

6.4. ArrayTranspose

6.5. ArrayContract

6.6. SimplifyArray

6.7. ZeroArrayQ

© 2026 Wolfram. All rights reserved.

  • Legal & Privacy Policy
  • Contact Us
  • WolframAlpha.com
  • WolframCloud.com