Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion Project.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
name = "ITensorBase"
uuid = "4795dd04-0d67-49bb-8f44-b89c448a1dc7"
version = "0.8.3"
version = "0.8.4"
authors = ["ITensor developers <support@itensor.org> and contributors"]

[workspace]
Expand Down
13 changes: 11 additions & 2 deletions docs/make.jl
Original file line number Diff line number Diff line change
@@ -1,7 +1,11 @@
using Documenter: Documenter, DocMeta, deploydocs, makedocs
using ITensorBase: ITensorBase
using ITensorBase
using ITensorFormatter: ITensorFormatter

# `using ITensorBase` (rather than `using ITensorBase: ITensorBase`) binds the exported
# names in `Main`. The tensor `show` qualifies the element type relative to the active
# module, so without it the doctests and `@example` blocks render `ITensorBase.ITensor`
# instead of `ITensor`.
DocMeta.setdocmeta!(ITensorBase, :DocTestSetup, :(using ITensorBase); recursive = true)

ITensorFormatter.make_index!(pkgdir(ITensorBase))
Expand All @@ -15,7 +19,12 @@ makedocs(;
edit_link = "main",
assets = ["assets/favicon.ico", "assets/extras.css"]
),
pages = ["Home" => "index.md", "Reference" => "reference.md"]
pages = [
"Home" => "index.md",
"User Interface" => "user_interface.md",
"Developer Interface" => "dev_interface.md",
"Reference" => "reference.md",
]
)

deploydocs(;
Expand Down
61 changes: 61 additions & 0 deletions docs/src/dev_interface.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
# Developer Interface

```@meta
CurrentModule = ITensorBase
```

This page covers the named-array model that indices and tensors are built on, the interface
for implementing a new tensor type, and experimental features that are not yet part of the
stable user-facing API. For the stable user-facing API, see the [User Interface](@ref).

## Named array types

A concrete tensor type subtypes [`AbstractITensor`](@ref). [`NamedUnitRange`](@ref) is the
named-range type that a tensor's dimensions are ([`Index`](@ref) is the flavor keyed by an
index name).

```@docs; canonical=false
AbstractITensor
NamedUnitRange
```

## Named array operations

Construct named objects with [`named`](@ref) and [`nameddims`](@ref), recover their parts
with [`name`](@ref), [`unnamed`](@ref), and [`dimnames`](@ref), and query their types with
[`dimnametype`](@ref), [`nametype`](@ref), and [`unnamedtype`](@ref). [`setname`](@ref) and
[`replacedimnames`](@ref) rename, and [`aligndims`](@ref) and [`aligneddims`](@ref) reorder a
tensor's dimensions by name (a copy and a view, respectively).

```@docs; canonical=false
named
nameddims
name
unnamed
dimnames
dimnametype
nametype
unnamedtype
setname
replacedimnames
aligndims
aligneddims
```

## Experimental

These features support building and applying operators, where an operator is a tensor whose
dimension names are split into a codomain (output) set and a domain (input) set. The API is
still being refined and is subject to change. Build an operator with [`operator`](@ref) or
allocate one with [`similar_operator`](@ref), apply it to a tensor with [`apply`](@ref), and
recover its underlying tensor and name sets with [`state`](@ref), [`codomainnames`](@ref),
and [`domainnames`](@ref).

```@docs; canonical=false
operator
similar_operator
apply
state
codomainnames
domainnames
```
90 changes: 90 additions & 0 deletions docs/src/user_interface.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
# User Interface

```@meta
CurrentModule = ITensorBase
```

This page covers the stable, user-facing API. For the lower-level named-array model, tensor
construction internals and accessors, and experimental features, see the
[Developer Interface](@ref). For a complete alphabetical listing of every documented name,
see the [Reference](@ref).

## Indices and tensors

An [`ITensor`](@ref) labels its dimensions by name, and an [`Index`](@ref) is a named
dimension. Get a tensor's indices with [`inds`](@ref), make distinct copies of an index with
[`prime`](@ref) and [`noprime`](@ref), and mint a fresh unique name with [`uniquename`](@ref).

```@docs; canonical=false
Index
ITensor
inds
prime
noprime
uniquename
```

## Constructors

Build a tensor by calling a `Base` array constructor on indices instead of sizes. `randn`,
`rand`, `zeros`, `ones`, and `fill` all accept indices and return an `ITensor` carrying them.
These are `Base` functions extended to accept indices, so they are shown here by example
rather than in the [Reference](@ref).

```@example userinterface
using ITensorBase: Index
i, j = Index(2), Index(3)
randn(i, j)
```

```@example userinterface
zeros(i, j)
```

## Algebra

ITensors support the standard arithmetic. `*` contracts over shared indices, leaving the
unshared ones.

```@example userinterface
k = Index(2)
a, b = randn(i, j), randn(j, k)
a * b
```

`+` and `-` add and subtract tensors. They are matched up by index, so the operands need not
list their indices in the same order.

```@example userinterface
c = randn(j, i)
a + c
```

Multiplying by a scalar scales the tensor.

```@example userinterface
2 * a
```

## Broadcasting

Broadcasting works elementwise and matches operands by name, so they need not share index
order. Its advantage over the plain arithmetic above is fusion: a dotted expression is
evaluated in a single pass over the elements, without allocating the intermediate tensors the
undotted form does.

For example, the plain expression below allocates `2 * a` and `3 * c` before adding them:

```@example userinterface
2 * a + 3 * c
```

Adding dots fuses the whole expression into one pass, giving the same result with no
intermediates:

```@example userinterface
2 .* a .+ 3 .* c
```

Non-linear broadcasting (functions of one or more tensors, such as `sin.(a)` or `a .^ 2`) is
experimental and incompletely supported, and is subject to change.
7 changes: 4 additions & 3 deletions src/ITensorBase.jl
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
module ITensorBase

export ITensor, Index, aligndims, dimnametype, named, nameddims,
operator, similar_operator
export AbstractITensor, ITensor, Index, NamedUnitRange, aligndims, aligneddims,
apply, codomainnames, dimnames, dimnametype, domainnames, inds, named,
nameddims, noprime, operator, prime, similar_operator, state, uniquename
using Compat: @compat
@compat public to_inds
@compat public @names
@compat public name, nametype, replacedimnames, setname, unnamed, unnamedtype

# Named-array machinery (relocated from NamedDimsArrays.jl).
include("isnamed.jl")
Expand Down
134 changes: 134 additions & 0 deletions src/abstractitensor.jl
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,17 @@ using TensorAlgebra: TensorAlgebra, permuteddims, zero!
# https://github.com/mcabbott/NamedPlus.jl
# https://pytorch.org/docs/stable/named_tensor.html

"""
AbstractITensor{DimName}

Supertype for tensors whose dimensions are labeled by names of type `DimName` rather
than ordered by position. Subtypes such as [`ITensor`](@ref) line their dimensions up
by name under contraction, addition, and indexing. Unlike an `AbstractArray`, the rank
and element type live in the data rather than the type, so `ndims` and `eltype` are not
fixed at the type level.

See also [`ITensor`](@ref), [`dimnames`](@ref), [`inds`](@ref).
"""
abstract type AbstractITensor{DimName} end

# Rank and element type live in the data, not the type, so the type-level `ndims`
Expand All @@ -16,6 +27,30 @@ abstract type AbstractITensor{DimName} end
# supplied directly below rather than inherited.
Base.ndims(::Type{<:AbstractITensor}) = Any

"""
dimnames(a::AbstractITensor)
dimnames(a::AbstractITensor, dim::Int)

The dimension names of `a`, as a collection in dimension order. The second form returns
the name of dimension `dim`.

# Examples

```jldoctest
julia> a = nameddims(zeros(2, 3), (:i, :j));

julia> dimnames(a)
2-element Vector{Symbol}:
:i
:j

julia> dimnames(a, 2)
:j
```

See also [`inds`](@ref), [`nameddims`](@ref).
"""
function dimnames end
dimnames(a::AbstractITensor) = throw(MethodError(dimnames, a))
function dimnames(a::AbstractITensor, dim::Int)
return dimnames(a)[dim]
Expand Down Expand Up @@ -52,6 +87,29 @@ unnamed(a::AbstractITensor) = throw(MethodError(unnamed, a))
unnamed(a::AbstractITensor, inds) = unnamed(aligneddims(a, inds))
unname(a::AbstractITensor, inds) = unnamed(aligndims(a, inds))

"""
inds(a::AbstractITensor)
inds(a::AbstractITensor, dim::Int)

The named axes (indices) of `a`, as a `Tuple` with one entry per dimension. Each entry
pairs a dimension's axis with its name. The second form returns the index of dimension
`dim`. Compare with [`dimnames`](@ref), which returns just the names without the axes.

# Examples

```jldoctest
julia> a = nameddims(zeros(2, 3), (:i, :j));

julia> inds(a)
(named(Base.OneTo(2), :i), named(Base.OneTo(3), :j))

julia> inds(a, 1)
named(Base.OneTo(2), :i)
```

See also [`dimnames`](@ref), [`nameddims`](@ref).
"""
function inds end
# Output the named axes/indices of the named dims array, as a `Tuple` (even though
# the dimension names are stored as a `Vector`).
inds(a::AbstractITensor) = named.(axes(unnamed(a)), Tuple(dimnames(a)))
Expand Down Expand Up @@ -92,6 +150,18 @@ end
nameddims(a::AbstractArray, inds)

Construct a named dimensions array from an unnamed array `a` and named dimensions `inds`.

# Examples

```jldoctest
julia> nameddims(zeros(2, 3), (:i, :j))
named(Base.OneTo(2), :i)×named(Base.OneTo(3), :j) ITensor{Symbol}:
2×3 Matrix{Float64}:
0.0 0.0 0.0
0.0 0.0 0.0
```

See also [`ITensor`](@ref), [`named`](@ref).
"""
function nameddims(a::AbstractArray, inds)
return ITensor(a, inds)
Expand Down Expand Up @@ -337,6 +407,30 @@ function setdimnames(a::AbstractITensor, dimnames)
return nameddims(unnamed(a), dimnames)
end

"""
replacedimnames(a::AbstractITensor, replacements::Pair...)
replacedimnames(f, a::AbstractITensor)

Return a tensor with the same data as `a` but with its dimension names replaced. The
first form takes `old => new` pairs, replacing matching names and leaving the rest
unchanged. The second form replaces each name with `f(name)`.

# Examples

```jldoctest
julia> using ITensorBase: replacedimnames

julia> a = nameddims(zeros(2, 3), (:i, :j));

julia> dimnames(replacedimnames(a, :i => :k))
2-element Vector{Symbol}:
:k
:j
```

See also [`dimnames`](@ref).
"""
function replacedimnames end
function replacedimnames(a::AbstractITensor, replacements::Pair...)
new_dimnames = replace(dimnames(a), replacements...)
return nameddims(unnamed(a), new_dimnames)
Expand Down Expand Up @@ -770,6 +864,26 @@ end

# Permute/align dimensions

"""
aligndims(a::AbstractITensor, dims)

Reorder the dimensions of `a` into the order given by `dims`, matched by name. Returns a
tensor with the same data and dimension names as `a` but with the dimensions permuted, and
throws a `NameMismatch` if `dims` is not a permutation of `a`'s dimension names.

# Examples

```jldoctest
julia> a = nameddims(zeros(2, 3), (:i, :j));

julia> aligndims(a, (:j, :i))
named(Base.OneTo(3), :j)×named(Base.OneTo(2), :i) ITensor{Symbol}:
3×2 Matrix{Float64}:
0.0 0.0
0.0 0.0
0.0 0.0
```
"""
function aligndims(a::AbstractITensor, dims)
new_dimnames = name.(dims)
perm = Tuple(getperm(dimnames(a), new_dimnames))
Expand All @@ -781,6 +895,26 @@ function aligndims(a::AbstractITensor, dims)
return nameddims(permutedims(unnamed(a), perm), new_dimnames)
end

"""
aligneddims(a::AbstractITensor, dims)

Like [`aligndims`](@ref), but returns a lazily-permuted view that shares data with `a`
instead of copying. Reorders the dimensions of `a` into the order given by `dims`, matched by
name, and throws a `NameMismatch` if `dims` is not a permutation of `a`'s dimension names.

# Examples

```jldoctest
julia> a = nameddims(reshape(1:6, 2, 3), (:i, :j));

julia> dimnames(aligneddims(a, (:j, :i)))
2-element Vector{Symbol}:
:j
:i
```

See also [`aligndims`](@ref).
"""
function aligneddims(a::AbstractITensor, dims)
new_dimnames = name.(dims)
perm = getperm(dimnames(a), new_dimnames)
Expand Down
Loading
Loading