From 27c59e315acef797e9ed9ed71afcf7a4b8b3c575 Mon Sep 17 00:00:00 2001 From: "Documenter.jl" Date: Tue, 13 Feb 2024 15:18:46 +0000 Subject: [PATCH] build based on 216e800 --- dev/.documenter-siteinfo.json | 2 +- dev/alternatives/index.html | 2 +- dev/assets/favicon.ico | Bin 152721 -> 131 bytes dev/counters/index.html | 2 +- dev/einexpr/index.html | 2 +- dev/index.html | 2 +- dev/optimizers/exhaustive/index.html | 2 +- dev/optimizers/greedy/index.html | 2 +- dev/slicing/index.html | 2 +- 9 files changed, 8 insertions(+), 8 deletions(-) diff --git a/dev/.documenter-siteinfo.json b/dev/.documenter-siteinfo.json index 167c368..4d02ac0 100644 --- a/dev/.documenter-siteinfo.json +++ b/dev/.documenter-siteinfo.json @@ -1 +1 @@ -{"documenter":{"julia_version":"1.10.0","generation_timestamp":"2024-02-13T15:13:26","documenter_version":"1.2.1"}} \ No newline at end of file +{"documenter":{"julia_version":"1.10.0","generation_timestamp":"2024-02-13T15:18:43","documenter_version":"1.2.1"}} \ No newline at end of file diff --git a/dev/alternatives/index.html b/dev/alternatives/index.html index 7f4c013..e41b424 100644 --- a/dev/alternatives/index.html +++ b/dev/alternatives/index.html @@ -1,2 +1,2 @@ -Alternatives · EinExprs.jl

Alternatives

EinExprs is deeply inspired by opt_einsum and cotengra. Actually most of the contraction path search and slicing algorithms have been rewritten from there. If you happen to be working in Python, you should definetely check out these libraries.

Differences with `opt_einsum`

Although the differences are minimal, any user coming from opt_einsum should be aware that:

  • The "optimal" contraction path solver in opt_einsum is known as Exhaustive in EinExprs.
  • The "random-greedy" contraction path solver in opt_einsum is the Greedy optimizer in EinExprs but with a random choose function.
  • When counting FLOPs, opt_einsum gives a value $\times 2$ higher than the EinExprs.flops counter.[1]

Although we believe there is no similar project in the Julia world, there are some overlapping libraries that may suit you if EinExprs doesn't fit your case.

  • 1We are not sure of the reason behind this mismatch or which package gives the correct answer, but since the factor remains constant, it should not affect when comparing contraction paths during the minimization step.
+Alternatives · EinExprs.jl

Alternatives

EinExprs is deeply inspired by opt_einsum and cotengra. Actually most of the contraction path search and slicing algorithms have been rewritten from there. If you happen to be working in Python, you should definetely check out these libraries.

Differences with `opt_einsum`

Although the differences are minimal, any user coming from opt_einsum should be aware that:

  • The "optimal" contraction path solver in opt_einsum is known as Exhaustive in EinExprs.
  • The "random-greedy" contraction path solver in opt_einsum is the Greedy optimizer in EinExprs but with a random choose function.
  • When counting FLOPs, opt_einsum gives a value $\times 2$ higher than the EinExprs.flops counter.[1]

Although we believe there is no similar project in the Julia world, there are some overlapping libraries that may suit you if EinExprs doesn't fit your case.

  • 1We are not sure of the reason behind this mismatch or which package gives the correct answer, but since the factor remains constant, it should not affect when comparing contraction paths during the minimization step.
diff --git a/dev/assets/favicon.ico b/dev/assets/favicon.ico index d5b1abdc527e9a2960a13378cc00eb93a714e21e..0d1c49c47e9df8d909242fa27dc1b4ccf8536319 100644 GIT binary patch literal 131 zcmWN{%MrpL6ac_`s-OV_JOo0z;e8NhR5F4&SiL#zWl#Bx*59hmd5m4_N1L~28S7>J z;!6Fc$04LoyY$9t)C@lC*?0_FEQoI79KjJ~Yk(aSq+&vGxtL6Mmz)R+V-#Qu0k4FJ NX&LP&%6f=kmp?!_D8B#z literal 152721 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 -Resource counting · EinExprs.jl

Resource counting

As explained before, EinExprs are symbolic expressions representing Einstein summation equations (i.e. tensor summation, permutation, contraction, ...) so no tensor operation is actually performed on them. Many times, information about the execution cost is needed to optimize the contraction path. In general, this is a hard task but thanks to Einsteam summation notation only representing linear algebra operations, and these operations are easy to estimate, we can count the resource requirements of any contraction path.

Currently there are 3 resource counters:

EinExprs.flopsFunction
flops(path::EinExpr)

Count the number of mathematical operations will be performed by the contraction of the root of the path tree.

source
EinExprs.removedsizeFunction
removedsize(path::EinExpr)

Count the amount of memory that will be freed after performing the contraction of the root of the path tree.

source
EinExprs.removedrankFunction
removedrank(path::EinExpr)

Count the rank reduction after performing the contraction of the root of the path tree.

source
Tip

These methods only count the resources spent of the contraction on the root of the tree. In order to count the resources of the whole tree, use mapreduce with Branches:

mapreduce(flops, +, Branches(path))
+Resource counting · EinExprs.jl

Resource counting

As explained before, EinExprs are symbolic expressions representing Einstein summation equations (i.e. tensor summation, permutation, contraction, ...) so no tensor operation is actually performed on them. Many times, information about the execution cost is needed to optimize the contraction path. In general, this is a hard task but thanks to Einsteam summation notation only representing linear algebra operations, and these operations are easy to estimate, we can count the resource requirements of any contraction path.

Currently there are 3 resource counters:

EinExprs.flopsFunction
flops(path::EinExpr)

Count the number of mathematical operations will be performed by the contraction of the root of the path tree.

source
EinExprs.removedsizeFunction
removedsize(path::EinExpr)

Count the amount of memory that will be freed after performing the contraction of the root of the path tree.

source
EinExprs.removedrankFunction
removedrank(path::EinExpr)

Count the rank reduction after performing the contraction of the root of the path tree.

source
Tip

These methods only count the resources spent of the contraction on the root of the tree. In order to count the resources of the whole tree, use mapreduce with Branches:

mapreduce(flops, +, Branches(path))
diff --git a/dev/einexpr/index.html b/dev/einexpr/index.html index 0dd4952..fc2db93 100644 --- a/dev/einexpr/index.html +++ b/dev/einexpr/index.html @@ -2,4 +2,4 @@ Einsum Expressions · EinExprs.jl

Einsum Expressions

Let's take an arbitrary tensor network like the following.

An arbitrary tensor network

This graph is a graphical notation equivalent to the following equation.

\[\sum_{i j k l m n o p} A_{mi} B_{ijp} C_{jkn} D_{pkl} E_{mno} F_{ol}\]

A naïve implementation of this equation is easy to implement.

result = zero(reduce(promote_type, eltype.([A,B,C,D,E,F])))
 for (i,j,k,l,m,n,o,p) in Iterators.product(1:I, 1:J, 1:K, 1:L, 1:M, 1:N, 1:O, 1:P)
     result += A[m,i] * B[i,j,p] * C[j,k,n] * D[p,k,l] * E[m,n,o] * F[o,l]
-end

But it has a cost of $\prod_\alpha \dim(\alpha)$ where $\alpha \in \{i,j,k,l,m,n,o,p\}$ which is of $\mathcal{O}(\exp(n))$ time complexity.

Basic utilities

The EinExpr type has a stable definition: it's secure to access its fields using the dot-syntax (e.g. path.head), but we recommend to use the following methods instead. They provide a much more stable API and can be used in a functional manner.

EinExprs.headFunction
head(path::EinExpr)

Return the indices of the resulting tensor from contracting path.

See also: inds, args.

source
EinExprs.argsFunction
args(path::EinExpr)

Return the children of the path, which correspond to input tensors for the contraction step in the top of the path.

See also: head.

source
args(sexpr::SizedEinExpr)

Note

Unlike args(::EinExpr), this function returns SizedEinExpr objects.

source
Missing docstring.

Missing docstring for Base.size(::EinExpr). Check Documenter's build log for details.

Some other useful methods are:

Base.ndimsMethod
ndims(path::EinExpr)

Return the number of indices of the resulting tensor from contracting path.

source
EinExprs.indsFunction
inds(path)

Return all the involved indices in path. If a tensor is passed, then it is equivalent to calling head.

See also: head.

source
EinExprs.sumindsFunction
suminds(path)

Indices of summation of an EinExpr.

\[\mathtt{path} \equiv \sum_{j k l m n o p} A_{mi} B_{ijp} C_{jkn} D_{pkl} E_{mno} F_{ol}\]

suminds(path) == [:j, :k, :l, :m, :n, :o, :p]
source
EinExprs.parsumindsFunction
parsuminds(path)

Indices of summation of possible pairwise tensors contractions between children of path.

source
EinExprs.selectFunction
select(path::EinExpr, i)

Return the child elements that contain i indices.

source

Construction by summation

One option for constructing EinExprs manually is to use the sum methods. For example, imagine that we have the following tensor equation and we want to contract first tensors $E_{mno}$ and $F_{ol}$. The resulting equation would be equivalent to adding a summatory to $E$ and $F$ as written in the right-hand side.

\[\sum_{i j k l m n o p} A_{mi} B_{ijp} C_{jkn} D_{pkl} E_{mno} F_{ol} = \sum_{i j k l m n p} A_{mi} B_{ijp} C_{jkn} D_{pkl} \sum_o E_{mno} F_{ol}\]

In EinExprs, we advocate for code that it's almost as easy as writing math. As such, one can write sum([E, F]) to create a new EinExpr where common indices are contracted or sum!(path, :o) for the in-place version where $E$ and $F$ are children of path.

Missing docstring.

Missing docstring for Base.sum(::EinExpr, ::Union{Symbol, Tuple{Vararg{Symbol}}, AbstractVector{<:Symbol}}). Check Documenter's build log for details.

In order to reverse the operation and unfix the contraction, the user may call the collapse! function.

EinExprs.collapse!Function
collapse!(path::EinExpr)

Collapses all sub-branches, merging all tensor leaves into the args field.

source

AbstractTrees integration

EinExpr type integrates with the AbstractTrees package in order to implement some of the tree-traversing algorithms. The interface is public and thus any user can use it to implement their own methods.

For example, the AbstractTrees.Leaves function returns an iterator through the leaves of any tree; i.e. the initial tensors in our case. We implement the Branches function in order to walk through the non-terminal nodes; i.e. the intermediate tensors.

EinExprs exports a variant of these methods which return collections.

EinExprs.leavesFunction
leaves(path::EinExpr[, i])

Return the terminal leaves of the path, which correspond to the initial input tensors. If i is specified, then only return the $i$-th tensor.

See also: branches.

source
EinExprs.branchesFunction
branches(path::EinExpr[, i])

Return the non-terminal branches of the path, which correspond to intermediate tensors result of contraction steps. If i is specified, then only return the $i$-th EinExpr.

See also: leaves, Branches.

source
+end

But it has a cost of $\prod_\alpha \dim(\alpha)$ where $\alpha \in \{i,j,k,l,m,n,o,p\}$ which is of $\mathcal{O}(\exp(n))$ time complexity.

Basic utilities

The EinExpr type has a stable definition: it's secure to access its fields using the dot-syntax (e.g. path.head), but we recommend to use the following methods instead. They provide a much more stable API and can be used in a functional manner.

EinExprs.headFunction
head(path::EinExpr)

Return the indices of the resulting tensor from contracting path.

See also: inds, args.

source
EinExprs.argsFunction
args(path::EinExpr)

Return the children of the path, which correspond to input tensors for the contraction step in the top of the path.

See also: head.

source
args(sexpr::SizedEinExpr)

Note

Unlike args(::EinExpr), this function returns SizedEinExpr objects.

source
Missing docstring.

Missing docstring for Base.size(::EinExpr). Check Documenter's build log for details.

Some other useful methods are:

Base.ndimsMethod
ndims(path::EinExpr)

Return the number of indices of the resulting tensor from contracting path.

source
EinExprs.indsFunction
inds(path)

Return all the involved indices in path. If a tensor is passed, then it is equivalent to calling head.

See also: head.

source
EinExprs.sumindsFunction
suminds(path)

Indices of summation of an EinExpr.

\[\mathtt{path} \equiv \sum_{j k l m n o p} A_{mi} B_{ijp} C_{jkn} D_{pkl} E_{mno} F_{ol}\]

suminds(path) == [:j, :k, :l, :m, :n, :o, :p]
source
EinExprs.parsumindsFunction
parsuminds(path)

Indices of summation of possible pairwise tensors contractions between children of path.

source
EinExprs.selectFunction
select(path::EinExpr, i)

Return the child elements that contain i indices.

source

Construction by summation

One option for constructing EinExprs manually is to use the sum methods. For example, imagine that we have the following tensor equation and we want to contract first tensors $E_{mno}$ and $F_{ol}$. The resulting equation would be equivalent to adding a summatory to $E$ and $F$ as written in the right-hand side.

\[\sum_{i j k l m n o p} A_{mi} B_{ijp} C_{jkn} D_{pkl} E_{mno} F_{ol} = \sum_{i j k l m n p} A_{mi} B_{ijp} C_{jkn} D_{pkl} \sum_o E_{mno} F_{ol}\]

In EinExprs, we advocate for code that it's almost as easy as writing math. As such, one can write sum([E, F]) to create a new EinExpr where common indices are contracted or sum!(path, :o) for the in-place version where $E$ and $F$ are children of path.

Missing docstring.

Missing docstring for Base.sum(::EinExpr, ::Union{Symbol, Tuple{Vararg{Symbol}}, AbstractVector{<:Symbol}}). Check Documenter's build log for details.

In order to reverse the operation and unfix the contraction, the user may call the collapse! function.

EinExprs.collapse!Function
collapse!(path::EinExpr)

Collapses all sub-branches, merging all tensor leaves into the args field.

source

AbstractTrees integration

EinExpr type integrates with the AbstractTrees package in order to implement some of the tree-traversing algorithms. The interface is public and thus any user can use it to implement their own methods.

For example, the AbstractTrees.Leaves function returns an iterator through the leaves of any tree; i.e. the initial tensors in our case. We implement the Branches function in order to walk through the non-terminal nodes; i.e. the intermediate tensors.

EinExprs exports a variant of these methods which return collections.

EinExprs.leavesFunction
leaves(path::EinExpr[, i])

Return the terminal leaves of the path, which correspond to the initial input tensors. If i is specified, then only return the $i$-th tensor.

See also: branches.

source
EinExprs.branchesFunction
branches(path::EinExpr[, i])

Return the non-terminal branches of the path, which correspond to intermediate tensors result of contraction steps. If i is specified, then only return the $i$-th EinExpr.

See also: leaves, Branches.

source
diff --git a/dev/index.html b/dev/index.html index a2868d3..79ee840 100644 --- a/dev/index.html +++ b/dev/index.html @@ -1,3 +1,3 @@ Home · EinExprs.jl
-

EinExprs is a Julia package that provides EinExprs: symbolic expressions representing a Einstein summation. These summations may be used to represent contraction paths of large tensor networks.

It is a complete redesign of OptimizedEinsum, which indeed was a Julia fork of opt_einsum. It powers Tenet but can easily be adapted to work with other packages.

  1. Einsum Expressions
  2. Optimizers
  3. Resource counting
  4. Slicing
  5. Alternatives

Planned features

  • Optimizers
    • Hypergraph Partitioning
    • Branch & Bound
    • Dynamic Programming
  • Subtree reconfiguration
  • Resource counting
    • Array structure-aware analysis
  • Execution planning
  • Permutation order optimization
  • Compilation
+

EinExprs is a Julia package that provides EinExprs: symbolic expressions representing a Einstein summation. These summations may be used to represent contraction paths of large tensor networks.

It is a complete redesign of OptimizedEinsum, which indeed was a Julia fork of opt_einsum. It powers Tenet but can easily be adapted to work with other packages.

  1. Einsum Expressions
  2. Optimizers
  3. Resource counting
  4. Slicing
  5. Alternatives

Planned features

  • Optimizers
    • Hypergraph Partitioning
    • Branch & Bound
    • Dynamic Programming
  • Subtree reconfiguration
  • Resource counting
    • Array structure-aware analysis
  • Execution planning
  • Permutation order optimization
  • Compilation
diff --git a/dev/optimizers/exhaustive/index.html b/dev/optimizers/exhaustive/index.html index 04b6abd..f9d72fa 100644 --- a/dev/optimizers/exhaustive/index.html +++ b/dev/optimizers/exhaustive/index.html @@ -1,2 +1,2 @@ -Exhaustive · EinExprs.jl

Exhaustive optimizer

EinExprs.ExhaustiveType
Exhaustive(; outer = false)

Exhaustive contraction path optimizers. It guarantees to find the optimal contraction path but at a large cost.

Keywords

  • outer instructs to consider outer products (aka tensor products) on the search for the optimal contraction path. It rarely provides an advantage over only considering inner products and thus, it is false by default.
Warning

The functionality of outer = true has not been yet implemented.

Implementation

The algorithm has a $\mathcal{O}(n!)$ time complexity if outer = true and $\mathcal{O}(\exp(n))$ if outer = false.

source
+Exhaustive · EinExprs.jl

Exhaustive optimizer

EinExprs.ExhaustiveType
Exhaustive(; outer = false)

Exhaustive contraction path optimizers. It guarantees to find the optimal contraction path but at a large cost.

Keywords

  • outer instructs to consider outer products (aka tensor products) on the search for the optimal contraction path. It rarely provides an advantage over only considering inner products and thus, it is false by default.
Warning

The functionality of outer = true has not been yet implemented.

Implementation

The algorithm has a $\mathcal{O}(n!)$ time complexity if outer = true and $\mathcal{O}(\exp(n))$ if outer = false.

source
diff --git a/dev/optimizers/greedy/index.html b/dev/optimizers/greedy/index.html index 2cb5aac..f94ea96 100644 --- a/dev/optimizers/greedy/index.html +++ b/dev/optimizers/greedy/index.html @@ -1,2 +1,2 @@ -Greedy · EinExprs.jl

Greedy optimizer

EinExprs.GreedyType
Greedy(; metric = removedsize, choose = pop!)

Greedy contraction path solver. Greedily selects contractions that maximize a metric.

Keywords

  • metric is a function that evaluates candidate pairwise tensor contractions. Defaults to removedsize.
  • choose is a function that extracts a pairwise tensor contraction between candidates. Defaults to candidate that maximize metric using pop!.
  • outer If true, consider outer products as candidates. Defaults to false.

Implementation

The implementation uses a binary heaptree to sort candidate pairwise tensor contractions. Then recursively,

  1. Selects and extracts a candidate from the heaptree using the choose function.
  2. Updates the metric of the candidates which contain neighbouring indices to the one selected.
  3. Append the selected index to the path and go back to step 1.
source
+Greedy · EinExprs.jl

Greedy optimizer

EinExprs.GreedyType
Greedy(; metric = removedsize, choose = pop!)

Greedy contraction path solver. Greedily selects contractions that maximize a metric.

Keywords

  • metric is a function that evaluates candidate pairwise tensor contractions. Defaults to removedsize.
  • choose is a function that extracts a pairwise tensor contraction between candidates. Defaults to candidate that maximize metric using pop!.
  • outer If true, consider outer products as candidates. Defaults to false.

Implementation

The implementation uses a binary heaptree to sort candidate pairwise tensor contractions. Then recursively,

  1. Selects and extracts a candidate from the heaptree using the choose function.
  2. Updates the metric of the candidates which contain neighbouring indices to the one selected.
  3. Append the selected index to the path and go back to step 1.
source
diff --git a/dev/slicing/index.html b/dev/slicing/index.html index 569b653..3bc4043 100644 --- a/dev/slicing/index.html +++ b/dev/slicing/index.html @@ -1,4 +1,4 @@ Slicing · EinExprs.jl

Slicing

Base.selectdimFunction
selectdim(path::EinExpr, index, i)

Project index to dimension i in a EinExpr. This is equivalent to tensor cutting aka slicing.

Arguments

  • path Contraction path.
  • index Index to cut.
  • i Dimension of index to select.

See also: view.

source
Base.viewFunction
view(path::EinExpr, cuttings...)

Project indices in contraction path to some of its dimensions. This is equivalent to:

reduce(cuttings) do path, (index, i)
     selectdim(path, index, i)
-end

Arguments

  • path Target contraction path.
  • cuttings List of Pair{Symbol,Int} representing the tensor cuttings aka slices.

See also: selectdim.

source
EinExprs.findslicesFunction
findslices(scorer, path::EinExpr; size, slices, overhead, temperature = 0.01, skip = head(path))

Search for indices to be cut/sliced such that the conditions given by size, overhead and slices are fulfilled. Reimplementation based on contengra's SliceFinder algorithm.

Arguments

  • scorer Heuristic function (or functor) that accepts a path and a candidate index for cutting, and returns a score.
  • path The contraction path target for tensor cutting aka slicing.

Keyword Arguments

  • size If specified, the largest intermediate tensor of the slice won't surpass this size (in number of elements).
  • slices If specified, there will be at least slices different slices when cutting all returnt indices.
  • overhead If specified, the amount of redundant operations between a slice and the original contraction won't supass this ratio.
  • temperature Temperature of the Boltzmann-like noise added for diffusing results.
  • skip Indices not to be considered for slicing.
source
+end

Arguments

  • path Target contraction path.
  • cuttings List of Pair{Symbol,Int} representing the tensor cuttings aka slices.

See also: selectdim.

source
EinExprs.findslicesFunction
findslices(scorer, path::EinExpr; size, slices, overhead, temperature = 0.01, skip = head(path))

Search for indices to be cut/sliced such that the conditions given by size, overhead and slices are fulfilled. Reimplementation based on contengra's SliceFinder algorithm.

Arguments

  • scorer Heuristic function (or functor) that accepts a path and a candidate index for cutting, and returns a score.
  • path The contraction path target for tensor cutting aka slicing.

Keyword Arguments

  • size If specified, the largest intermediate tensor of the slice won't surpass this size (in number of elements).
  • slices If specified, there will be at least slices different slices when cutting all returnt indices.
  • overhead If specified, the amount of redundant operations between a slice and the original contraction won't supass this ratio.
  • temperature Temperature of the Boltzmann-like noise added for diffusing results.
  • skip Indices not to be considered for slicing.
source