API Documentation
opt_einsum.contract
contract
contract(
subscripts: str,
*operands: ArrayType,
out: ArrayType = ...,
use_blas: bool = ...,
optimize: OptimizeKind = ...,
memory_limit: _MemoryLimit = ...,
backend: BackendType = ...,
**kwargs: Any
) -> ArrayType
contract(
subscripts: ArrayType,
*operands: Union[ArrayType, Collection[int]],
out: ArrayType = ...,
use_blas: bool = ...,
optimize: OptimizeKind = ...,
memory_limit: _MemoryLimit = ...,
backend: BackendType = ...,
**kwargs: Any
) -> ArrayType
contract(
subscripts: Union[str, ArrayType],
*operands: Union[ArrayType, Collection[int]],
out: Optional[ArrayType] = None,
use_blas: bool = True,
optimize: OptimizeKind = True,
memory_limit: _MemoryLimit = None,
backend: BackendType = "auto",
**kwargs: Any
) -> ArrayType
Evaluates the Einstein summation convention on the operands. A drop in replacement for NumPy's einsum function that optimizes the order of contraction to reduce overall scaling at the cost of several intermediate arrays.
Parameters:
-
subscripts
Union[str, ArrayType]
) –Specifies the subscripts for summation.
-
*operands
Union[ArrayType, Collection[int]]
, default:()
) –These are the arrays for the operation.
-
out
Optional[ArrayType]
, default:None
) –A output array in which set the resulting output.
-
use_blas
bool
, default:True
) –Do you use BLAS for valid operations, may use extra memory for more intermediates.
-
optimize
OptimizeKind
, default:True
) –- Choose the type of path the contraction will be optimized with
- if a list is given uses this as the path.
'optimal'
An algorithm that explores all possible ways of contracting the listed tensors. Scales factorially with the number of terms in the contraction.'dp'
A faster (but essentially optimal) algorithm that uses dynamic programming to exhaustively search all contraction paths without outer-products.'greedy'
An cheap algorithm that heuristically chooses the best pairwise contraction at each step. Scales linearly in the number of terms in the contraction.'random-greedy'
Run a randomized version of the greedy algorithm 32 times and pick the best path.'random-greedy-128'
Run a randomized version of the greedy algorithm 128 times and pick the best path.'branch-all'
An algorithm like optimal but that restricts itself to searching 'likely' paths. Still scales factorially.'branch-2'
An even more restricted version of 'branch-all' that only searches the best two options at each step. Scales exponentially with the number of terms in the contraction.'auto', None, True
Choose the best of the above algorithms whilst aiming to keep the path finding time below 1ms.'auto-hq'
Aim for a high quality contraction, choosing the best of the above algorithms whilst aiming to keep the path finding time below 1sec.False
will not optimize the contraction.
-
memory_limit
_MemoryLimit
, default:None
) –- Give the upper bound of the largest intermediate tensor contract will build.
- None or -1 means there is no limit.
max_input
means the limit is set as largest input tensor.- A positive integer is taken as an explicit limit on the number of elements.
The default is None. Note that imposing a limit can make contractions exponentially slower to perform.
-
backend
BackendType
, default:'auto'
) –Which library to use to perform the required
tensordot
,transpose
andeinsum
calls. Should match the types of arrays supplied, Seecontract_expression
for generating expressions which convert numpy arrays to and from the backend library automatically.
Returns:
-
ArrayType
–The result of the einsum expression.
Notes
This function should produce a result identical to that of NumPy's einsum
function. The primary difference is contract
will attempt to form
intermediates which reduce the overall scaling of the given einsum contraction.
By default the worst intermediate formed will be equal to that of the largest
input array. For large einsum expressions with many input arrays this can
provide arbitrarily large (1000 fold+) speed improvements.
For contractions with just two tensors this function will attempt to use NumPy's built-in BLAS functionality to ensure that the given operation is performed optimally. When NumPy is linked to a threaded BLAS, potential speedups are on the order of 20-100 for a six core machine.
opt_einsum.contract_path
contract_path
contract_path(
subscripts: str,
*operands: ArrayType,
use_blas: bool = True,
optimize: OptimizeKind = True,
memory_limit: _MemoryLimit = None,
shapes: bool = False,
**kwargs: Any
) -> Tuple[PathType, PathInfo]
contract_path(
subscripts: ArrayType,
*operands: Union[ArrayType, Collection[int]],
use_blas: bool = True,
optimize: OptimizeKind = True,
memory_limit: _MemoryLimit = None,
shapes: bool = False,
**kwargs: Any
) -> Tuple[PathType, PathInfo]
contract_path(
subscripts: Any,
*operands: Any,
use_blas: bool = True,
optimize: OptimizeKind = True,
memory_limit: _MemoryLimit = None,
shapes: bool = False,
**kwargs: Any
) -> Tuple[PathType, PathInfo]
Find a contraction order path
, without performing the contraction.
Parameters:
-
subscripts
Any
) –Specifies the subscripts for summation.
-
*operands
Any
, default:()
) –These are the arrays for the operation.
-
use_blas
bool
, default:True
) –Do you use BLAS for valid operations, may use extra memory for more intermediates.
-
optimize
OptimizeKind
, default:True
) –Choose the type of path the contraction will be optimized with. - if a list is given uses this as the path. -
'optimal'
An algorithm that explores all possible ways of contracting the listed tensors. Scales factorially with the number of terms in the contraction. -'dp'
A faster (but essentially optimal) algorithm that uses dynamic programming to exhaustively search all contraction paths without outer-products. -'greedy'
An cheap algorithm that heuristically chooses the best pairwise contraction at each step. Scales linearly in the number of terms in the contraction. -'random-greedy'
Run a randomized version of the greedy algorithm 32 times and pick the best path. -'random-greedy-128'
Run a randomized version of the greedy algorithm 128 times and pick the best path. -'branch-all'
An algorithm like optimal but that restricts itself to searching 'likely' paths. Still scales factorially. -'branch-2'
An even more restricted version of 'branch-all' that only searches the best two options at each step. Scales exponentially with the number of terms in the contraction. -'auto'
Choose the best of the above algorithms whilst aiming to keep the path finding time below 1ms. -'auto-hq'
Aim for a high quality contraction, choosing the best of the above algorithms whilst aiming to keep the path finding time below 1sec. -
memory_limit
_MemoryLimit
, default:None
) –Give the upper bound of the largest intermediate tensor contract will build. - None or -1 means there is no limit -
max_input
means the limit is set as largest input tensor - a positive integer is taken as an explicit limit on the number of elementsThe default is None. Note that imposing a limit can make contractions exponentially slower to perform.
-
shapes
bool
, default:False
) –Whether
contract_path
should assume arrays (the default) or array shapes have been supplied.
Returns:
-
path
(PathType
) –The optimized einsum contraciton path
-
PathInfo
(PathInfo
) –A printable object containing various information about the path found.
Notes
The resulting path indicates which terms of the input contraction should be contracted first, the result of this contraction is then appended to the end of the contraction list.
Examples:
We can begin with a chain dot example. In this case, it is optimal to
contract the b and c tensors represented by the first element of the path (1,
2). The resulting tensor is added to the end of the contraction and the
remaining contraction, (0, 1)
, is then executed.
a = np.random.rand(2, 2)
b = np.random.rand(2, 5)
c = np.random.rand(5, 2)
path_info = opt_einsum.contract_path('ij,jk,kl->il', a, b, c)
print(path_info[0])
#> [(1, 2), (0, 1)]
print(path_info[1])
#> Complete contraction: ij,jk,kl->il
#> Naive scaling: 4
#> Optimized scaling: 3
#> Naive FLOP count: 1.600e+02
#> Optimized FLOP count: 5.600e+01
#> Theoretical speedup: 2.857
#> Largest intermediate: 4.000e+00 elements
#> -------------------------------------------------------------------------
#> scaling current remaining
#> -------------------------------------------------------------------------
#> 3 kl,jk->jl ij,jl->il
#> 3 jl,ij->il il->il
A more complex index transformation example.
I = np.random.rand(10, 10, 10, 10)
C = np.random.rand(10, 10)
path_info = oe.contract_path('ea,fb,abcd,gc,hd->efgh', C, C, I, C, C)
print(path_info[0])
#> [(0, 2), (0, 3), (0, 2), (0, 1)]
print(path_info[1])
#> Complete contraction: ea,fb,abcd,gc,hd->efgh
#> Naive scaling: 8
#> Optimized scaling: 5
#> Naive FLOP count: 8.000e+08
#> Optimized FLOP count: 8.000e+05
#> Theoretical speedup: 1000.000
#> Largest intermediate: 1.000e+04 elements
#> --------------------------------------------------------------------------
#> scaling current remaining
#> --------------------------------------------------------------------------
#> 5 abcd,ea->bcde fb,gc,hd,bcde->efgh
#> 5 bcde,fb->cdef gc,hd,cdef->efgh
#> 5 cdef,gc->defg hd,defg->efgh
#> 5 defg,hd->efgh efgh->efgh
opt_einsum.contract_expression
contract_expression
contract_expression(
subscripts: str,
*operands: Union[ArrayType, TensorShapeType],
constants: Union[Collection[int], None] = ...,
use_blas: bool = ...,
optimize: OptimizeKind = ...,
memory_limit: _MemoryLimit = ...,
**kwargs: Any
) -> ContractExpression
contract_expression(
subscripts: Union[ArrayType, TensorShapeType],
*operands: Union[
ArrayType, TensorShapeType, Collection[int]
],
constants: Union[Collection[int], None] = ...,
use_blas: bool = ...,
optimize: OptimizeKind = ...,
memory_limit: _MemoryLimit = ...,
**kwargs: Any
) -> ContractExpression
contract_expression(
subscripts: Union[str, ArrayType, TensorShapeType],
*shapes: Union[
ArrayType, TensorShapeType, Collection[int]
],
constants: Union[Collection[int], None] = None,
use_blas: bool = True,
optimize: OptimizeKind = True,
memory_limit: _MemoryLimit = None,
**kwargs: Any
) -> ContractExpression
Generate a reusable expression for a given contraction with specific shapes, which can, for example, be cached.
Parameters:
subscripts: Specifies the subscripts for summation.
shapes: Shapes of the arrays to optimize the contraction for.
constants: The indices of any constant arguments in `shapes`, in which case the
actual array should be supplied at that position rather than just a
shape. If these are specified, then constant parts of the contraction
between calls will be reused. Additionally, if a GPU-enabled backend is
used for example, then the constant tensors will be kept on the GPU,
minimizing transfers.
kwargs: Passed on to `contract_path` or `einsum`. See `contract`.
Returns:
-
ContractExpression
–Callable with signature
expr(*arrays, out=None, backend='numpy')
where the array's shapes should matchshapes
.
Notes
The out
keyword argument should be supplied to the generated expression
rather than this function.
The backend
keyword argument should also be supplied to the generated
expression. If numpy arrays are supplied, if possible they will be
converted to and back from the correct backend array type.
The generated expression will work with any arrays which have
the same rank (number of dimensions) as the original shapes, however, if
the actual sizes are different, the expression may no longer be optimal.
Constant operations will be computed upon the first call with a particular
backend, then subsequently reused.
Examples: Basic usage:
expr = contract_expression("ab,bc->ac", (3, 4), (4, 5))
a, b = np.random.rand(3, 4), np.random.rand(4, 5)
c = expr(a, b)
np.allclose(c, a @ b)
#> True
Supply a
as a constant:
expr = contract_expression("ab,bc->ac", a, (4, 5), constants=[0])
expr
#> <ContractExpression('[ab],bc->ac', constants=[0])>
c = expr(b)
np.allclose(c, a @ b)
#> True
opt_einsum.contract.ContractExpression
ContractExpression
ContractExpression(
contraction: str,
contraction_list: ContractionListType,
constants_dict: Dict[int, ArrayType],
**kwargs: Any
)
Helper class for storing an explicit contraction_list
which can
then be repeatedly called solely with the array arguments.
Methods:
-
evaluate_constants
–Convert any constant operands to the correct backend form, and
evaluate_constants
evaluate_constants(backend: Optional[str] = 'auto') -> None
Convert any constant operands to the correct backend form, and
perform as many contractions as possible to create a new list of
operands, stored in self._evaluated_constants[backend]
. This also
makes sure self.contraction_list
only contains the remaining,
non-const operations.
opt_einsum.contract.PathInfo
PathInfo
PathInfo(
contraction_list: ContractionListType,
input_subscripts: str,
output_subscript: str,
indices: ArrayIndexType,
path: PathType,
scale_list: Sequence[int],
naive_cost: int,
opt_cost: int,
size_list: Sequence[int],
size_dict: Dict[str, int],
)
A printable object to contain information about a contraction path.
opt_einsum.get_symbol
get_symbol
get_symbol(i: int) -> str
Get the symbol corresponding to int i
- runs through the usual 52
letters before resorting to unicode characters, starting at chr(192)
and skipping surrogates.
Examples:
get_symbol(2)
#> 'c'
get_symbol(200)
#> 'Ŕ'
get_symbol(20000)
#> '京'
opt_einsum.shared_intermediates
shared_intermediates
shared_intermediates(
cache: Optional[CacheType] = None,
) -> Generator[CacheType, None, None]
Context in which contract intermediate results are shared.
Note that intermediate computations will not be garbage collected until 1. this context exits, and 2. the yielded cache is garbage collected (if it was captured).
Parameters:
- cache - (dict) If specified, a user-stored dict in which intermediate results will be stored. This can be used to interleave sharing contexts.
Returns:
- cache - (dict) A dictionary in which sharing results are stored. If ignored, sharing results will be garbage collected when this context is exited. This dict can be passed to another context to resume sharing.
opt_einsum.paths.optimal
optimal
optimal(
inputs: List[ArrayIndexType],
output: ArrayIndexType,
size_dict: Dict[str, int],
memory_limit: Optional[int] = None,
) -> PathType
Computes all possible pair contractions in a depth-first recursive manner,
sieving results based on memory_limit
and the best path found so far.
Parameters:
-
inputs
List[ArrayIndexType]
) –List of sets that represent the lhs side of the einsum subscript.
-
output
ArrayIndexType
) –Set that represents the rhs side of the overall einsum subscript.
-
size_dict
Dict[str, int]
) –Dictionary of index sizes.
-
memory_limit
Optional[int]
, default:None
) –The maximum number of elements in a temporary array.
Returns:
-
path
(PathType
) –The optimal contraction order within the memory limit constraint.
Examples:
isets = [set('abd'), set('ac'), set('bdc')]
oset = set('')
idx_sizes = {'a': 1, 'b':2, 'c':3, 'd':4}
optimal(isets, oset, idx_sizes, 5000)
#> [(0, 2), (0, 1)]
opt_einsum.paths.greedy
greedy
greedy(
inputs: List[ArrayIndexType],
output: ArrayIndexType,
size_dict: Dict[str, int],
memory_limit: Optional[int] = None,
choose_fn: Any = None,
cost_fn: str = "memory-removed",
) -> PathType
Finds the path by a three stage algorithm:
- Eagerly compute Hadamard products.
- Greedily compute contractions to maximize
removed_size
- Greedily compute outer products.
This algorithm scales quadratically with respect to the maximum number of elements sharing a common dim.
Parameters:
-
inputs
List[ArrayIndexType]
) –List of sets that represent the lhs side of the einsum subscript
-
output
ArrayIndexType
) –Set that represents the rhs side of the overall einsum subscript
-
size_dict
Dict[str, int]
) –Dictionary of index sizes
-
memory_limit
Optional[int]
, default:None
) –The maximum number of elements in a temporary array
-
choose_fn
Any
, default:None
) –A function that chooses which contraction to perform from the queue
-
cost_fn
str
, default:'memory-removed'
) –A function that assigns a potential contraction a cost.
Returns:
-
path
(PathType
) –The contraction order (a list of tuples of ints).
Examples:
isets = [set('abd'), set('ac'), set('bdc')]
oset = set('')
idx_sizes = {'a': 1, 'b':2, 'c':3, 'd':4}
greedy(isets, oset, idx_sizes)
#> [(0, 2), (0, 1)]
opt_einsum.paths.branch
branch
branch(
inputs: List[ArrayIndexType],
output: ArrayIndexType,
size_dict: Dict[str, int],
memory_limit: Optional[int] = None,
nbranch: Optional[int] = None,
cutoff_flops_factor: int = 4,
minimize: str = "flops",
cost_fn: str = "memory-removed",
) -> PathType
opt_einsum.paths.PathOptimizer
PathOptimizer
Base class for different path optimizers to inherit from.
Subclassed optimizers should define a call method with signature:
def __call__(self, inputs: List[ArrayIndexType], output: ArrayIndexType, size_dict: dict[str, int], memory_limit: int | None = None) -> list[tuple[int, ...]]:
\"\"\"
Parameters:
inputs: The indices of each input array.
outputs: The output indices
size_dict: The size of each index
memory_limit: If given, the maximum allowed memory.
\"\"\"
# ... compute path here ...
return path
where path
is a list of int-tuples specifying a contraction order.
opt_einsum.paths.BranchBound
BranchBound
BranchBound(
nbranch: Optional[int] = None,
cutoff_flops_factor: int = 4,
minimize: str = "flops",
cost_fn: str = "memory-removed",
)
Bases: PathOptimizer
as well sieving by memory_limit
and the best path found so far.
Parameters:
-
nbranch
Optional[int]
, default:None
) –How many branches to explore at each contraction step. If None, explore all possible branches. If an integer, branch into this many paths at each step. Defaults to None.
-
cutoff_flops_factor
int
, default:4
) –If at any point, a path is doing this much worse than the best path found so far was, terminate it. The larger this is made, the more paths will be fully explored and the slower the algorithm. Defaults to 4.
-
minimize
str
, default:'flops'
) –Whether to optimize the path with regard primarily to the total estimated flop-count, or the size of the largest intermediate. The option not chosen will still be used as a secondary criterion.
-
cost_fn
str
, default:'memory-removed'
) –A function that returns a heuristic 'cost' of a potential contraction with which to sort candidates. Should have signature
cost_fn(size12, size1, size2, k12, k1, k2)
.
opt_einsum.path_random.RandomOptimizer
RandomOptimizer
RandomOptimizer(
max_repeats: int = 32,
max_time: Optional[float] = None,
minimize: str = "flops",
parallel: Union[bool, Decimal, int] = False,
pre_dispatch: int = 128,
)
Bases: PathOptimizer
Base class for running any random path finder that benefits
from repeated calling, possibly in a parallel fashion. Custom random
optimizers should subclass this, and the setup
method should be
implemented with the following signature:
def setup(self, inputs, output, size_dict):
# custom preparation here ...
return trial_fn, trial_args
Where trial_fn
itself should have the signature::
def trial_fn(r, *trial_args):
# custom computation of path here
return ssa_path, cost, size
Where r
is the run number and could for example be used to seed a
random number generator. See RandomGreedy
for an example.
Parameters:
-
max_repeats
int
, default:32
) –The maximum number of repeat trials to have.
-
max_time
Optional[float]
, default:None
) –The maximum amount of time to run the algorithm for.
-
minimize
str
, default:'flops'
) –Whether to favour paths that minimize the total estimated flop-count or the size of the largest intermediate created.
-
parallel
Union[bool, Decimal, int]
, default:False
) –Whether to parallelize the random trials, by default
False
. IfTrue
, use aconcurrent.futures.ProcessPoolExecutor
with the same number of processes as cores. If an integer is specified, use that many processes instead. Finally, you can supply a custom executor-pool which should have an API matching that of the python 3 standard library moduleconcurrent.futures
. Namely, asubmit
method that returnsFuture
objects, themselves withresult
andcancel
methods. -
pre_dispatch
int
, default:128
) –If running in parallel, how many jobs to pre-dispatch so as to avoid submitting all jobs at once. Should also be more than twice the number of workers to avoid under-subscription. Default: 128.
Attributes:
-
path
(PathType
) –The best path found so far.
-
costs
(List[int]
) –The list of each trial's costs found so far.
-
sizes
(List[int]
) –The list of each trial's largest intermediate size so far.
path
property
path: PathType
The best path found so far.
opt_einsum.path_random.RandomGreedy
RandomGreedy
RandomGreedy(
cost_fn: str = "memory-removed-jitter",
temperature: float = 1.0,
rel_temperature: bool = True,
nbranch: int = 8,
**kwargs: Any
)
Bases: RandomOptimizer
with which to sort candidates. Should have signature
`cost_fn(size12, size1, size2, k12, k1, k2)`.
temperature: When choosing a possible contraction, its relative probability will be
proportional to exp(-cost / temperature)
. Thus the larger
temperature
is, the further random paths will stray from the normal
'greedy' path. Conversely, if set to zero, only paths with exactly the
same cost as the best at each step will be explored.
rel_temperature: Whether to normalize the temperature
at each step to the scale of
the best cost. This is generally beneficial as the magnitude of costs
can vary significantly throughout a contraction. If False, the
algorithm will end up branching when the absolute cost is low, but
stick to the 'greedy' path when the cost is high - this can also be
beneficial.
nbranch: How many potential paths to calculate probability for and choose from at each step.
kwargs: Supplied to RandomOptimizer.
Attributes:
-
choose_fn
(Any
) –The function that chooses which contraction to take - make this a
-
path
(PathType
) –The best path found so far.
choose_fn
property
choose_fn: Any
The function that chooses which contraction to take - make this a
property so that temperature
and nbranch
etc. can be updated
between runs.
path
property
path: PathType
The best path found so far.
opt_einsum.paths.DynamicProgramming
DynamicProgramming
DynamicProgramming(
minimize: str = "flops",
cost_cap: Union[bool, int] = True,
search_outer: bool = False,
)
Bases: PathOptimizer
Finds the optimal path of pairwise contractions without intermediate outer products based a dynamic programming approach presented in Phys. Rev. E 90, 033315 (2014) (the corresponding preprint is publicly available at https://arxiv.org/abs/1304.6112). This method is especially well-suited in the area of tensor network states, where it usually outperforms all the other optimization strategies.
This algorithm shows exponential scaling with the number of inputs in the worst case scenario (see example below). If the graph to be contracted consists of disconnected subgraphs, the algorithm scales linearly in the number of disconnected subgraphs and only exponentially with the number of inputs per subgraph.
Parameters:
-
minimize
str
, default:'flops'
) –What to minimize: - 'flops' - minimize the number of flops - 'size' - minimize the size of the largest intermediate - 'write' - minimize the size of all intermediate tensors - 'combo' - minimize
flops + alpha * write
summed over intermediates, a default ratio of alpha=64 is used, or it can be customized withf'combo-{alpha}'
- 'limit' - minimizemax(flops, alpha * write)
summed over intermediates, a default ratio of alpha=64 is used, or it can be customized withf'limit-{alpha}'
- callable - a custom local cost function -
cost_cap
Union[bool, int]
, default:True
) –How to implement cost-capping: - True - iteratively increase the cost-cap - False - implement no cost-cap at all - int - use explicit cost cap
-
search_outer
bool
, default:False
) –In rare circumstances the optimal contraction may involve an outer product, this option allows searching such contractions but may well slow down the path finding considerably on all but very small graphs.