High Level¶
You can use cotengra
as a drop-in replacement for numpy.einsum
or
opt_einsum.contract
,
and benefit from the improved optimization, contraction routines and features such as slicing, with the following high level interface functions.
Traditional einsum style where the contraction is specified as a compact and more human-readable equation
string:
Programmatic style, where the indices can be specified as sequences of arbitrary hashable objects:
The following are equivalent ways to perform a matrix multiplication and transpose of x
and y
:
import cotengra as ctg
# einsum style
z = ctg.einsum("ab,bc->ca", x, y)
# programmatic style
z = ctg.array_contract(
arrays=(x, y),
inputs=[(0, 1), (1, 2)],
output=(2, 0),
)
The {_tree}
functions return a ContractionTree
object
which can be used to inspect the order and various properties such as contraction
cost and width. The {_expression}
functions return a function that performs the
contraction, which can be called with any matching input arrays.
All of these functions take an optimize
kwarg which specifies the
contraction strategy. It can be one of the following:
str
: a preset such as'auto'
,'auto-hq'
,'greedy'
or'optimal'
PathOptimizer
: a custom optimizer fromcotengra
oropt_einsum
ContractionTree
: a contraction tree generated previouslySequence[tuple[int]]
: an explicit path, specified manually or generated previously
If the method provides sliced indices then the contraction will utilize these to reduce the memory. The default is the cotengra
preset 'auto'
. If you explicitly want to use an opt_einsum
preset then you can supply optimize='opt_einsum:auto'
for example.
See the docstring of array_contract_expression
for other options.
einsum
interfaces:¶
Here the contraction is specified using the string equation form of
numpy.einsum
and other einsum
implementations. For example:
"ab,bc->ac"
matrix multiplication"Xab,Xbc->Xac"
batch matrix multiplication"ab,ab->ab"
hadamard (elementwise) product
Hint
cotengra
supports all types of explicit equation (such as repeated and hyper/batch indices appearing any number of times).
The einsum
versions also support automatically expanded dimensions using ellipsis ...
.
If the right hand side is not specified then the output indices are computed as every index that appears once on the left hand side inputs, in sorted order. For example 'ba'
is completed as a transposition: 'ba->ab'
.
Lets generate a more non-trivial example:
%config InlineBackend.figure_formats = ['svg']
import cotengra as ctg
# generate the 'inputs and output' format contraction
inputs, output, shapes, size_dict = ctg.utils.lattice_equation([4, 5])
# generate the 'eq' format
eq = ctg.utils.inputs_output_to_eq(inputs, output)
print(eq)
# make example arrays
arrays = ctg.utils.make_arrays_from_inputs(inputs, size_dict, seed=42)
ab,cbd,edf,gfh,ih,ajk,clkm,enmo,gpoq,irq,jst,lutv,nwvx,pyxz,rAz,sB,uBC,wCD,yDE,AE->
And perform the contraction:
ctg.einsum(eq, *arrays)
array(776.48544934)
If we wanted to inspect the contraction before we perform it we could build the contraction tree first. Here we supply just the shapes of the arrays:
tree = ctg.einsum_tree(eq, *shapes)
# some typical quantities of interest:
tree.contract_stats()
{'flops': 964, 'write': 293, 'size': 32}
The contraction tree has many methods to inspect and manipulate the contraction.
tree.plot_rubberband()
(<Figure size 500x500 with 1 Axes>, <Axes: >)
Sometimes it is useful to generate the function that will perform the contraction for any given inputs. This can be done with the einsum_expression
function:
expr = ctg.einsum_expression(eq, *shapes)
expr(*arrays)
array(776.48544934)
arrays = ctg.utils.make_arrays_from_inputs(inputs, size_dict, seed=43)
expr(*arrays)
array(2252.66569969)
Note
einsum
itself caches the expressions it uses by default for presets, so at least in terms of performance savings this is often not necessary.
array_contract
interfaces¶
The einsum format is less convenient for dynamically generated contractions.
The alternate interface cotengra
provides are the array_contract
functions. These specify a contraction with the following three arguments:
- `inputs : Sequence[Sequence[Hashable]]`, the indices of each input
- `output : Sequence[Hashable]`, the output indices
- `size_dict : Mapping[Hashable, int]`, the size of each index
The indices are mapped (‘canonicalized’) into single letters in the order they appear on the inputs
, allowing matching geometries to be cached.
If output
is not specified it is calculated as the indices that appear once in inputs
, in the order they appear in inputs
. Note this is slightly different to einsum
which sorts the output indices, since we can only require the indices to be hashable.
Note
You can also supply shapes
to array_contract_tree
and array_contract_expression
rather than build size_dict
manually.
As an example, we’ll directly contract the arrays from a tensor network generated with quimb
:
import quimb.tensor as qtn
tn = qtn.TN_rand_reg(100, 3, D=2, seed=42)
tn.draw(edge_color=True)
tn
TensorNetworkGen(tensors=100, indices=150)
Tensor(shape=(2, 2, 2), inds=[_67af2fAAAAC, _67af2fAAAAB, _67af2fAAAAA], tags={I0}),
backend=numpy, dtype=float64, data=array([[[ 0.41832997, 0.60557617], [ 0.02878786, -1.084246 ]], [[ 1.46422098, 0.29072736], [-1.33075642, -0.03472346]]])Tensor(shape=(2, 2, 2), inds=[_67af2fAAAAF, _67af2fAAAAE, _67af2fAAAAD], tags={I1}),
backend=numpy, dtype=float64, data=array([[[ 0.28041847, 0.10749307], [-1.92080086, 1.57864499]], [[ 1.00595719, 0.45121505], [-0.59343367, 0.09382112]]])Tensor(shape=(2, 2, 2), inds=[_67af2fAAAAI, _67af2fAAAAH, _67af2fAAAAG], tags={I2}),
backend=numpy, dtype=float64, data=array([[[ 1.85195867, -0.25590475], [-0.28298637, 0.415816 ]], [[-1.08877401, -1.96729165], [ 0.88737846, -1.32823784]]])Tensor(shape=(2, 2, 2), inds=[_67af2fAAAAL, _67af2fAAAAK, _67af2fAAAAJ], tags={I3}),
backend=numpy, dtype=float64, data=array([[[-0.13157981, -0.36196929], [ 0.7820311 , 0.28266399]], [[-1.00595013, 0.01851214], [-1.24315953, 2.60337585]]])Tensor(shape=(2, 2, 2), inds=[_67af2fAAAAN, _67af2fAAAAM, _67af2fAAAAB], tags={I4}),
backend=numpy, dtype=float64, data=array([[[ 0.15139223, -0.51553062], [-0.2196374 , 0.40234591]], [[ 1.36128828, 0.74287737], [ 0.93685218, 0.17547031]]])Tensor(shape=(2, 2, 2), inds=[_67af2fAAAAQ, _67af2fAAAAP, _67af2fAAAAO], tags={I5}),
backend=numpy, dtype=float64, data=array([[[ 1.52520418, 0.09821447], [-1.16490357, 0.52358791]], [[-1.06559789, -0.31079113], [ 0.5559524 , -0.09963476]]])Tensor(shape=(2, 2, 2), inds=[_67af2fAAAAT, _67af2fAAAAS, _67af2fAAAAR], tags={I6}),
backend=numpy, dtype=float64, data=array([[[-0.25769078, -1.58951869], [-1.81491229, 0.53617305]], [[ 1.27138979, -0.55403891], [ 1.72433064, -0.31178569]]])Tensor(shape=(2, 2, 2), inds=[_67af2fAAAAW, _67af2fAAAAV, _67af2fAAAAU], tags={I7}),
backend=numpy, dtype=float64, data=array([[[ 0.06331837, 1.38212765], [ 0.58472813, -0.50975014]], [[ 0.2513335 , 0.40621724], [ 0.8656376 , -0.53392518]]])Tensor(shape=(2, 2, 2), inds=[_67af2fAAAAZ, _67af2fAAAAY, _67af2fAAAAX], tags={I8}),
backend=numpy, dtype=float64, data=array([[[-0.03877829, 1.14263416], [-0.46350628, 2.26692259]], [[-0.5287392 , 0.32461586], [-0.1544165 , -0.81960771]]])Tensor(shape=(2, 2, 2), inds=[_67af2fAAAAc, _67af2fAAAAb, _67af2fAAAAa], tags={I9}),
backend=numpy, dtype=float64, data=array([[[-1.20293573, 0.09544837], [-1.3617434 , 0.27737016]], [[ 0.30665917, -1.40419209], [-1.53897176, 1.59692719]]])Tensor(shape=(2, 2, 2), inds=[_67af2fAAAAf, _67af2fAAAAe, _67af2fAAAAd], tags={I10}),
backend=numpy, dtype=float64, data=array([[[ 1.26835706, -0.74444453], [-1.37903328, -0.37289418]], [[ 0.22521904, -0.7968999 ], [-0.19003288, 0.40520818]]])Tensor(shape=(2, 2, 2), inds=[_67af2fAAAAh, _67af2fAAAAg, _67af2fAAAAO], tags={I11}),
backend=numpy, dtype=float64, data=array([[[-1.56670601, 1.62226236], [-0.55864531, 1.285027 ]], [[-0.64887992, 0.64242269], [ 2.17043334, -0.54919245]]])Tensor(shape=(2, 2, 2), inds=[_67af2fAAAAk, _67af2fAAAAj, _67af2fAAAAi], tags={I12}),
backend=numpy, dtype=float64, data=array([[[ 0.03192423, -0.74439149], [ 1.30574661, 0.87000251]], [[ 0.79849565, 0.83417813], [-0.37099355, -0.67691076]]])Tensor(shape=(2, 2, 2), inds=[_67af2fAAAAn, _67af2fAAAAm, _67af2fAAAAl], tags={I13}),
backend=numpy, dtype=float64, data=array([[[-0.51472382, -1.67062395], [ 1.15948922, 0.57182887]], [[-0.74479429, -0.29743726], [-1.31841887, -0.25337446]]])Tensor(shape=(2, 2, 2), inds=[_67af2fAAAAq, _67af2fAAAAp, _67af2fAAAAo], tags={I14}),
backend=numpy, dtype=float64, data=array([[[-1.33318094, 1.5421289 ], [-0.30571997, -1.00505008]], [[ 1.12605809, -0.13951594], [ 0.20790904, -0.52911824]]])Tensor(shape=(2, 2, 2), inds=[_67af2fAAAAs, _67af2fAAAAr, _67af2fAAAAk], tags={I15}),
backend=numpy, dtype=float64, data=array([[[ 0.3674769 , 1.55241718], [ 0.82071427, 0.14560001]], [[ 0.19105139, 1.47534647], [ 0.11034297, -1.12803854]]])Tensor(shape=(2, 2, 2), inds=[_67af2fAAAAv, _67af2fAAAAu, _67af2fAAAAt], tags={I16}),
backend=numpy, dtype=float64, data=array([[[ 0.55952408, 0.64356438], [-0.95897595, 0.25066991]], [[-0.01199984, 0.40876493], [-0.28508488, -0.42547401]]])Tensor(shape=(2, 2, 2), inds=[_67af2fAAAAx, _67af2fAAAAw, _67af2fAAAAF], tags={I17}),
backend=numpy, dtype=float64, data=array([[[ 0.64288936, 0.15768144], [-1.26677523, 0.25459598]], [[ 0.74840043, 0.82142352], [-0.27199821, 0.87673783]]])Tensor(shape=(2, 2, 2), inds=[_67af2fAAABA, _67af2fAAAAz, _67af2fAAAAy], tags={I18}),
backend=numpy, dtype=float64, data=array([[[-0.96158539, -1.96465654], [ 0.3811346 , -0.52300382]], [[ 1.83535089, -1.43337649], [-0.59776266, -0.40870927]]])Tensor(shape=(2, 2, 2), inds=[_67af2fAAABD, _67af2fAAABC, _67af2fAAABB], tags={I19}),
backend=numpy, dtype=float64, data=array([[[ 0.68397816, -0.42211949], [-0.1650454 , 0.50112425]], [[-0.84532849, 0.5558993 ], [ 2.06932322, 0.1431952 ]]])Tensor(shape=(2, 2, 2), inds=[_67af2fAAABE, _67af2fAAAAx, _67af2fAAAAH], tags={I20}),
backend=numpy, dtype=float64, data=array([[[ 0.66338863, 1.06152374], [ 0.20970869, -0.1046703 ]], [[-0.11430137, 0.31375736], [-1.29794829, 0.71623433]]])Tensor(shape=(2, 2, 2), inds=[_67af2fAAABF, _67af2fAAAAp, _67af2fAAAAU], tags={I21}),
backend=numpy, dtype=float64, data=array([[[ 0.45682495, -0.06321074], [ 0.05871331, -0.21005493]], [[-0.48018253, -1.02270796], [-4.09288747, -1.06758198]]])Tensor(shape=(2, 2, 2), inds=[_67af2fAAABH, _67af2fAAABG, _67af2fAAAAJ], tags={I22}),
backend=numpy, dtype=float64, data=array([[[ 1.3837506 , 0.54885024], [-0.44582365, 0.27466897]], [[ 0.33042849, 0.20524129], [ 1.16451197, -0.47649984]]])Tensor(shape=(2, 2, 2), inds=[_67af2fAAABJ, _67af2fAAABI, _67af2fAAAAe], tags={I23}),
backend=numpy, dtype=float64, data=array([[[-1.2017799 , 0.05584251], [-0.98095115, -0.20473152]], [[ 0.2602656 , 0.05370239], [-1.63822223, 0.02741674]]])Tensor(shape=(2, 2, 2), inds=[_67af2fAAABK, _67af2fAAAAg, _67af2fAAAAM], tags={I24}),
backend=numpy, dtype=float64, data=array([[[ 1.77715336, -0.28063285], [ 0.8070599 , 0.04899588]], [[ 0.71085377, 1.32883486], [ 0.27595826, 2.20806878]]])Tensor(shape=(2, 2, 2), inds=[_67af2fAAABM, _67af2fAAABL, _67af2fAAAAV], tags={I25}),
backend=numpy, dtype=float64, data=array([[[-0.51588489, 0.81952451], [-0.83499587, -0.04589686]], [[-0.56797951, -0.62835871], [ 0.07071926, -0.88126988]]])Tensor(shape=(2, 2, 2), inds=[_67af2fAAABP, _67af2fAAABO, _67af2fAAABN], tags={I26}),
backend=numpy, dtype=float64, data=array([[[ 2.52843728, 0.42337001], [-0.84121891, -0.68472719]], [[ 1.20186868, -0.71322565], [-0.94592688, 0.34291128]]])Tensor(shape=(2, 2, 2), inds=[_67af2fAAABS, _67af2fAAABR, _67af2fAAABQ], tags={I27}),
backend=numpy, dtype=float64, data=array([[[-0.2171143 , 0.6168768 ], [-1.33975764, -0.11793883]], [[-0.01325147, -1.58992986], [ 1.97251808, 1.97324821]]])Tensor(shape=(2, 2, 2), inds=[_67af2fAAABU, _67af2fAAABT, _67af2fAAABF], tags={I28}),
backend=numpy, dtype=float64, data=array([[[ 0.79460659, -0.95131942], [-0.62124143, -0.4642856 ]], [[ 0.15497854, -1.07938866], [ 0.33352787, 1.24106592]]])Tensor(shape=(2, 2, 2), inds=[_67af2fAAABW, _67af2fAAABV, _67af2fAAABJ], tags={I29}),
backend=numpy, dtype=float64, data=array([[[ 0.74599076, -0.26003485], [ 2.1604772 , -0.49252775]], [[ 0.13855393, 0.04263864], [-0.38309191, 1.79375069]]])Tensor(shape=(2, 2, 2), inds=[_67af2fAAABZ, _67af2fAAABY, _67af2fAAABX], tags={I30}),
backend=numpy, dtype=float64, data=array([[[ 1.88481868, -0.0695583 ], [-0.60641381, 0.90302332]], [[ 0.41724814, 0.66447103], [-0.16009207, -1.67700053]]])Tensor(shape=(2, 2, 2), inds=[_67af2fAAABc, _67af2fAAABb, _67af2fAAABa], tags={I31}),
backend=numpy, dtype=float64, data=array([[[ 0.38689291, -1.0936539 ], [-0.13263404, 1.46114905]], [[ 0.12281697, 1.31833095], [ 1.83668743, -0.14130299]]])Tensor(shape=(2, 2, 2), inds=[_67af2fAAABe, _67af2fAAABd, _67af2fAAAAD], tags={I32}),
backend=numpy, dtype=float64, data=array([[[-0.31227164, 0.77419936], [ 0.13490232, 0.3719042 ]], [[-0.77619116, 1.05731633], [ 0.14662734, 0.88291095]]])Tensor(shape=(2, 2, 2), inds=[_67af2fAAABh, _67af2fAAABg, _67af2fAAABf], tags={I33}),
backend=numpy, dtype=float64, data=array([[[-0.67215056, 0.05571073], [-1.03576912, -1.28441133]], [[-1.33272379, 0.74270892], [-0.81299007, -0.27344781]]])Tensor(shape=(2, 2, 2), inds=[_67af2fAAABj, _67af2fAAABi, _67af2fAAABf], tags={I34}),
backend=numpy, dtype=float64, data=array([[[-0.8951296 , 1.26369747], [ 2.24931229, -0.82915018]], [[ 1.78783198, 0.02225253], [ 1.71827061, -0.72247824]]])Tensor(shape=(2, 2, 2), inds=[_67af2fAAABm, _67af2fAAABl, _67af2fAAABk], tags={I35}),
backend=numpy, dtype=float64, data=array([[[-1.41833885, -0.17283426], [ 0.23216175, 0.63922096]], [[-0.53334221, 0.56416889], [ 0.64977318, -0.69770602]]])Tensor(shape=(2, 2, 2), inds=[_67af2fAAABo, _67af2fAAABn, _67af2fAAABl], tags={I36}),
backend=numpy, dtype=float64, data=array([[[ 1.34170008, -0.02143791], [ 1.16329068, -1.45774186]], [[ 0.77342921, 0.45629851], [-0.57959124, 1.35134186]]])Tensor(shape=(2, 2, 2), inds=[_67af2fAAABp, _67af2fAAAAQ, _67af2fAAAAL], tags={I37}),
backend=numpy, dtype=float64, data=array([[[-0.33926804, -0.29612723], [-0.03585335, -1.89036142]], [[-1.18933749, 0.21242447], [ 0.02652267, -0.27630153]]])Tensor(shape=(2, 2, 2), inds=[_67af2fAAABs, _67af2fAAABr, _67af2fAAABq], tags={I38}),
backend=numpy, dtype=float64, data=array([[[ 1.54389242, -0.46439793], [-0.03863546, -2.16611661]], [[-1.64557186, -1.37941789], [-0.02262371, 1.24117565]]])Tensor(shape=(2, 2, 2), inds=[_67af2fAAABu, _67af2fAAABt, _67af2fAAABL], tags={I39}),
backend=numpy, dtype=float64, data=array([[[-0.38368313, 0.62433256], [ 0.69722479, 1.0513984 ]], [[-0.73418882, -0.23299285], [-0.75439293, 0.17953454]]])Tensor(shape=(2, 2, 2), inds=[_67af2fAAABw, _67af2fAAABv, _67af2fAAAAK], tags={I40}),
backend=numpy, dtype=float64, data=array([[[ 0.05877584, -1.41417106], [-0.11633639, -0.26464487]], [[-2.84431328, 1.90038606], [ 0.25512732, -1.40045801]]])Tensor(shape=(2, 2, 2), inds=[_67af2fAAABx, _67af2fAAABw, _67af2fAAAAb], tags={I41}),
backend=numpy, dtype=float64, data=array([[[ 1.64707772, -0.81599775], [ 1.3777959 , 0.84016492]], [[-0.53326295, 0.37130353], [ 0.71351521, -0.18891297]]])Tensor(shape=(2, 2, 2), inds=[_67af2fAAABy, _67af2fAAABZ, _67af2fAAABE], tags={I42}),
backend=numpy, dtype=float64, data=array([[[-1.52825934, -0.48924217], [-1.53611577, -0.95677416]], [[-1.21581794, -0.40168864], [-0.03305816, -0.04687855]]])Tensor(shape=(2, 2, 2), inds=[_67af2fAAACB, _67af2fAAACA, _67af2fAAABz], tags={I43}),
backend=numpy, dtype=float64, data=array([[[ 0.78738459, -0.58063465], [-0.02645525, -0.76479373]], [[-0.71402249, 0.33955049], [-1.33190651, 0.044711 ]]])Tensor(shape=(2, 2, 2), inds=[_67af2fAAACD, _67af2fAAACC, _67af2fAAABh], tags={I44}),
backend=numpy, dtype=float64, data=array([[[-0.02008487, 0.98077792], [ 1.8685142 , 0.14106384]], [[-0.54273423, 1.75408101], [ 2.09132258, -0.01678777]]])Tensor(shape=(2, 2, 2), inds=[_67af2fAAACC, _67af2fAAACA, _67af2fAAAAc], tags={I45}),
backend=numpy, dtype=float64, data=array([[[ 0.03333921, 1.61852048], [-0.4632809 , 0.25798995]], [[ 0.82237162, 0.31547219], [-1.56309635, -0.71530198]]])Tensor(shape=(2, 2, 2), inds=[_67af2fAAABu, _67af2fAAABQ, _67af2fAAABH], tags={I46}),
backend=numpy, dtype=float64, data=array([[[ 0.12616229, 0.24306265], [-1.21393031, -0.03326173]], [[-1.56211681, 0.61171481], [-0.10485063, 1.25027403]]])Tensor(shape=(2, 2, 2), inds=[_67af2fAAACE, _67af2fAAACD, _67af2fAAAAn], tags={I47}),
backend=numpy, dtype=float64, data=array([[[ 0.82565929, 0.03612089], [-0.3786331 , -2.45768323]], [[ 0.5555774 , 0.05666101], [-0.96264526, -1.49662903]]])Tensor(shape=(2, 2, 2), inds=[_67af2fAAACG, _67af2fAAACF, _67af2fAAAAa], tags={I48}),
backend=numpy, dtype=float64, data=array([[[ 0.86181319, -0.39336792], [-0.21088054, -1.52093029]], [[-0.68659009, -0.04785194], [-0.04965364, -0.08275545]]])Tensor(shape=(2, 2, 2), inds=[_67af2fAAACH, _67af2fAAABC, _67af2fAAAAr], tags={I49}),
backend=numpy, dtype=float64, data=array([[[-1.11921376, 1.35283861], [ 1.16737392, -0.1649528 ]], [[ 0.41631827, -0.47844357], [ 0.25467172, -0.20455254]]])Tensor(shape=(2, 2, 2), inds=[_67af2fAAACJ, _67af2fAAACI, _67af2fAAAAI], tags={I50}),
backend=numpy, dtype=float64, data=array([[[ 0.57486997, 1.05275919], [ 0.97019501, 0.1345963 ]], [[-0.99522698, 0.37310525], [-0.15596329, 0.92255026]]])Tensor(shape=(2, 2, 2), inds=[_67af2fAAACK, _67af2fAAABM, _67af2fAAAAi], tags={I51}),
backend=numpy, dtype=float64, data=array([[[0.39814605, 1.30281331], [1.41125229, 0.76097404]], [[0.51736235, 0.34134732], [1.68736855, 0.07948978]]])Tensor(shape=(2, 2, 2), inds=[_67af2fAAACM, _67af2fAAACL, _67af2fAAABD], tags={I52}),
backend=numpy, dtype=float64, data=array([[[ 1.48196655, 0.41736173], [-2.08082564, -0.99010177]], [[ 0.49097649, 1.19824281], [-0.83588013, -0.111188 ]]])Tensor(shape=(2, 2, 2), inds=[_67af2fAAACO, _67af2fAAACN, _67af2fAAABT], tags={I53}),
backend=numpy, dtype=float64, data=array([[[ 0.93669257, 1.00292715], [-1.1592509 , 0.17165121]], [[-0.95672409, 0.44413637], [-0.24374346, 1.45953642]]])Tensor(shape=(2, 2, 2), inds=[_67af2fAAACE, _67af2fAAAAj, _67af2fAAAAY], tags={I54}),
backend=numpy, dtype=float64, data=array([[[ 0.62266766, 0.34755705], [-0.48303148, -1.09935182]], [[ 0.41925794, 1.06491403], [-0.41431341, -1.57496432]]])Tensor(shape=(2, 2, 2), inds=[_67af2fAAACP, _67af2fAAABR, _67af2fAAABI], tags={I55}),
backend=numpy, dtype=float64, data=array([[[-1.51382364, 0.36708892], [-1.62472029, 1.67648859]], [[ 0.98760363, -0.78162987], [-0.63497334, -0.62930064]]])Tensor(shape=(2, 2, 2), inds=[_67af2fAAACR, _67af2fAAACQ, _67af2fAAAAv], tags={I56}),
backend=numpy, dtype=float64, data=array([[[-0.42659068, 1.32173848], [-0.03978853, -0.10435659]], [[ 0.03709526, -0.6325428 ], [-1.27154466, 0.32421925]]])Tensor(shape=(2, 2, 2), inds=[_67af2fAAACS, _67af2fAAACF, _67af2fAAABj], tags={I57}),
backend=numpy, dtype=float64, data=array([[[ 0.27335833, 1.19676365], [ 0.6535591 , 0.08589129]], [[ 0.47844404, 0.221359 ], [-0.19211651, -0.23340824]]])Tensor(shape=(2, 2, 2), inds=[_67af2fAAACT, _67af2fAAACJ, _67af2fAAABy], tags={I58}),
backend=numpy, dtype=float64, data=array([[[-0.4957821 , 0.64799884], [ 0.34294736, -0.46004224]], [[-0.09062532, -0.26219847], [-1.07842502, -1.74451027]]])Tensor(shape=(2, 2, 2), inds=[_67af2fAAABP, _67af2fAAAAu, _67af2fAAAAm], tags={I59}),
backend=numpy, dtype=float64, data=array([[[-0.77202293, -1.27249467], [ 0.84691268, -1.72488911]], [[ 0.99898935, 0.17427921], [-0.71807075, 0.22366337]]])Tensor(shape=(2, 2, 2), inds=[_67af2fAAACV, _67af2fAAACU, _67af2fAAABb], tags={I60}),
backend=numpy, dtype=float64, data=array([[[-0.00701465, -1.35254573], [ 0.55302397, -1.00111538]], [[ 0.4508672 , -0.6071619 ], [-1.72811239, 0.38531025]]])Tensor(shape=(2, 2, 2), inds=[_67af2fAAACX, _67af2fAAACW, _67af2fAAAAT], tags={I61}),
backend=numpy, dtype=float64, data=array([[[-1.0977206 , -1.21844911], [-1.0680861 , -0.81158037]], [[ 0.88338946, -0.61570669], [-0.07811817, 0.42106163]]])Tensor(shape=(2, 2, 2), inds=[_67af2fAAABX, _67af2fAAABS, _67af2fAAABN], tags={I62}),
backend=numpy, dtype=float64, data=array([[[-1.15124221, -0.64150838], [ 0.95555201, 0.99141828]], [[-1.18426722, 0.78212112], [ 1.3265312 , -0.02626513]]])Tensor(shape=(2, 2, 2), inds=[_67af2fAAACG, _67af2fAAABp, _67af2fAAAAG], tags={I63}),
backend=numpy, dtype=float64, data=array([[[-1.54744898, 0.25224055], [-1.62104324, -0.13977993]], [[-0.03909069, 1.58248776], [ 0.55074243, 0.60963354]]])Tensor(shape=(2, 2, 2), inds=[_67af2fAAACa, _67af2fAAACZ, _67af2fAAACY], tags={I64}),
backend=numpy, dtype=float64, data=array([[[-0.49185897, 0.16316046], [ 0.05892211, -0.63315672]], [[ 1.30455739, 0.02073824], [ 0.32342008, -0.77156756]]])Tensor(shape=(2, 2, 2), inds=[_67af2fAAACU, _67af2fAAACO, _67af2fAAABv], tags={I65}),
backend=numpy, dtype=float64, data=array([[[-0.29301534, -0.16665405], [ 0.05232625, 0.78690332]], [[ 0.7520511 , -0.29549456], [ 1.38767439, 0.17473486]]])Tensor(shape=(2, 2, 2), inds=[_67af2fAAACc, _67af2fAAACb, _67af2fAAACB], tags={I66}),
backend=numpy, dtype=float64, data=array([[[ 0.79521858, -0.26720061], [-0.10519735, -0.56633547]], [[-0.09871576, -1.46478992], [ 0.4915131 , 0.50935446]]])Tensor(shape=(2, 2, 2), inds=[_67af2fAAACd, _67af2fAAACc, _67af2fAAABc], tags={I67}),
backend=numpy, dtype=float64, data=array([[[ 0.75544235, 1.77164135], [-1.22289025, 1.49047828]], [[ 0.12158551, -2.31867424], [ 0.01848823, 0.85483554]]])Tensor(shape=(2, 2, 2), inds=[_67af2fAAACg, _67af2fAAACf, _67af2fAAACe], tags={I68}),
backend=numpy, dtype=float64, data=array([[[ 0.7227368 , -0.22551489], [-0.4888639 , 0.70066707]], [[-1.08746465, -0.97158584], [ 0.85236144, 0.96269801]]])Tensor(shape=(2, 2, 2), inds=[_67af2fAAACh, _67af2fAAACS, _67af2fAAABm], tags={I69}),
backend=numpy, dtype=float64, data=array([[[ 0.3296755 , -0.91499217], [ 0.92829453, 1.42199598]], [[-1.99166231, 0.0074796 ], [-0.94931243, -1.12781258]]])Tensor(shape=(2, 2, 2), inds=[_67af2fAAACV, _67af2fAAACP, _67af2fAAABk], tags={I70}),
backend=numpy, dtype=float64, data=array([[[-0.28388004, 2.44399239], [-0.23098441, -1.59771318]], [[-2.56786834, 1.1907446 ], [ 1.49135773, 0.5853944 ]]])Tensor(shape=(2, 2, 2), inds=[_67af2fAAACd, _67af2fAAAAs, _67af2fAAAAW], tags={I71}),
backend=numpy, dtype=float64, data=array([[[ 0.83576201, 2.15339076], [-0.80696793, 0.24723496]], [[ 0.48071583, 0.22638018], [ 0.99993802, -0.17183673]]])Tensor(shape=(2, 2, 2), inds=[_67af2fAAACi, _67af2fAAACM, _67af2fAAABB], tags={I72}),
backend=numpy, dtype=float64, data=array([[[-1.72953586, 0.8824201 ], [ 0.94282464, -1.49052868]], [[ 0.49363039, 1.02366011], [ 1.3219318 , 0.13656684]]])Tensor(shape=(2, 2, 2), inds=[_67af2fAAACj, _67af2fAAACf, _67af2fAAABG], tags={I73}),
backend=numpy, dtype=float64, data=array([[[ 0.65258668, 0.22272843], [-0.37710862, -1.4005133 ]], [[-0.87819954, 1.06389014], [-2.67796998, 1.16469057]]])Tensor(shape=(2, 2, 2), inds=[_67af2fAAACk, _67af2fAAACN, _67af2fAAAAt], tags={I74}),
backend=numpy, dtype=float64, data=array([[[ 1.10930311, 0.96710875], [-0.18449632, -2.19632474]], [[ 2.32847207, -0.19463999], [ 0.94570235, 0.43298386]]])Tensor(shape=(2, 2, 2), inds=[_67af2fAAACl, _67af2fAAACX, _67af2fAAAAl], tags={I75}),
backend=numpy, dtype=float64, data=array([[[-0.1160343 , 1.01658209], [-1.79924617, 0.6967389 ]], [[ 1.53000807, -0.91475514], [ 1.22643248, 1.18258538]]])Tensor(shape=(2, 2, 2), inds=[_67af2fAAACm, _67af2fAAAAo, _67af2fAAAAh], tags={I76}),
backend=numpy, dtype=float64, data=array([[[ 0.6211931 , -1.46372333], [-0.67258141, 0.69158572]], [[-0.25236025, 1.14300204], [-0.41196788, -0.4113092 ]]])Tensor(shape=(2, 2, 2), inds=[_67af2fAAACg, _67af2fAAACL, _67af2fAAABY], tags={I77}),
backend=numpy, dtype=float64, data=array([[[-0.27685081, 0.45897355], [-0.10725809, -0.17560083]], [[-0.75294937, -1.1838208 ], [ 0.68087654, -1.09662417]]])Tensor(shape=(2, 2, 2), inds=[_67af2fAAACk, _67af2fAAABe, _67af2fAAABA], tags={I78}),
backend=numpy, dtype=float64, data=array([[[-0.92110031, 2.04748767], [-0.20930679, 0.09577991]], [[-0.40793428, 0.34942966], [-1.05992335, -0.70095478]]])Tensor(shape=(2, 2, 2), inds=[_67af2fAAACl, _67af2fAAABz, _67af2fAAAAN], tags={I79}),
backend=numpy, dtype=float64, data=array([[[-0.42925512, 0.1648491 ], [-0.06745807, -0.49105142]], [[-0.66084483, -0.71542717], [-1.39008283, 1.19607223]]])Tensor(shape=(2, 2, 2), inds=[_67af2fAAACj, _67af2fAAABo, _67af2fAAAAy], tags={I80}),
backend=numpy, dtype=float64, data=array([[[ 2.02950952, -1.22765565], [ 1.24918745, -0.59412117]], [[-1.15263769, -0.8188466 ], [-1.23054708, 1.60579691]]])Tensor(shape=(2, 2, 2), inds=[_67af2fAAACn, _67af2fAAACh, _67af2fAAAAA], tags={I81}),
backend=numpy, dtype=float64, data=array([[[ 1.06905335, 0.45742907], [ 0.93140848, -0.28482561]], [[-1.26345935, -1.16125411], [-1.58408487, -0.8977538 ]]])Tensor(shape=(2, 2, 2), inds=[_67af2fAAACo, _67af2fAAACW, _67af2fAAAAE], tags={I82}),
backend=numpy, dtype=float64, data=array([[[-0.31384969, -1.14049149], [-1.19614084, 1.25242178]], [[ 0.24187925, 0.02246282], [-0.48479543, 0.01896328]]])Tensor(shape=(2, 2, 2), inds=[_67af2fAAACr, _67af2fAAACq, _67af2fAAACp], tags={I83}),
backend=numpy, dtype=float64, data=array([[[-0.8081965 , -1.6399214 ], [ 0.15667124, -0.26231046]], [[-0.98283266, 1.06778207], [-0.64056352, -1.38303642]]])Tensor(shape=(2, 2, 2), inds=[_67af2fAAACm, _67af2fAAACR, _67af2fAAAAd], tags={I84}),
backend=numpy, dtype=float64, data=array([[[-0.14068929, 0.41546312], [-0.93706581, 0.65419382]], [[-0.80648235, -0.74373297], [ 1.33716318, -1.61944607]]])Tensor(shape=(2, 2, 2), inds=[_67af2fAAACe, _67af2fAAACQ, _67af2fAAABr], tags={I85}),
backend=numpy, dtype=float64, data=array([[[-1.61184631, 1.62894392], [ 0.40530824, 0.71803393]], [[-1.07766989, -0.75596443], [ 0.02612967, 0.8684165 ]]])Tensor(shape=(2, 2, 2), inds=[_67af2fAAACs, _67af2fAAABV, _67af2fAAAAR], tags={I86}),
backend=numpy, dtype=float64, data=array([[[-1.00723475, -1.04873348], [ 0.84360271, 2.15955728]], [[ 0.49557051, 0.64082816], [-0.88432175, 0.49417542]]])Tensor(shape=(2, 2, 2), inds=[_67af2fAAACo, _67af2fAAACa, _67af2fAAABO], tags={I87}),
backend=numpy, dtype=float64, data=array([[[ 0.17196303, 2.17154549], [-0.95691292, -1.77934294]], [[ 0.1758767 , 0.27865058], [-0.97245654, -0.32586481]]])Tensor(shape=(2, 2, 2), inds=[_67af2fAAACt, _67af2fAAACn, _67af2fAAABg], tags={I88}),
backend=numpy, dtype=float64, data=array([[[ 0.16454028, 1.0543336 ], [-0.14183872, -0.06806919]], [[-1.25950768, -0.66979446], [-0.61563759, 0.32709842]]])Tensor(shape=(2, 2, 2), inds=[_67af2fAAACK, _67af2fAAAAX, _67af2fAAAAC], tags={I89}),
backend=numpy, dtype=float64, data=array([[[ 0.05836501, -0.14479672], [ 0.24275703, -0.66770472]], [[-0.9993962 , 1.10414673], [-2.17430902, -1.47743442]]])Tensor(shape=(2, 2, 2), inds=[_67af2fAAACT, _67af2fAAABx, _67af2fAAABK], tags={I90}),
backend=numpy, dtype=float64, data=array([[[-0.49148804, -0.50568072], [-0.38951434, -0.23834332]], [[-0.83087066, -0.07078329], [-0.66152192, -0.29215283]]])Tensor(shape=(2, 2, 2), inds=[_67af2fAAABn, _67af2fAAABa, _67af2fAAAAw], tags={I91}),
backend=numpy, dtype=float64, data=array([[[-0.14846772, 1.19470227], [-0.44202288, 0.23673868]], [[-0.03347083, -0.32092881], [ 0.42150777, 0.5883475 ]]])Tensor(shape=(2, 2, 2), inds=[_67af2fAAACq, _67af2fAAABU, _67af2fAAAAf], tags={I92}),
backend=numpy, dtype=float64, data=array([[[-0.86598189, -0.49039864], [ 1.51802956, -0.59100812]], [[ 1.37447989, 0.35109993], [ 0.35337737, 0.96290664]]])Tensor(shape=(2, 2, 2), inds=[_67af2fAAABs, _67af2fAAABW, _67af2fAAAAz], tags={I93}),
backend=numpy, dtype=float64, data=array([[[-0.42153279, -0.06794624], [ 0.13617862, 1.19429339]], [[-0.67560454, 0.87065649], [-0.39001935, 0.63779384]]])Tensor(shape=(2, 2, 2), inds=[_67af2fAAACt, _67af2fAAAAq, _67af2fAAAAZ], tags={I94}),
backend=numpy, dtype=float64, data=array([[[ 1.20231593, -0.26320706], [-1.38778291, -0.22438736]], [[-0.27412015, -0.0637929 ], [ 0.92141209, 0.76731005]]])Tensor(shape=(2, 2, 2), inds=[_67af2fAAACi, _67af2fAAABi, _67af2fAAABd], tags={I95}),
backend=numpy, dtype=float64, data=array([[[ 0.88076909, -0.1284671 ], [-0.9217294 , 0.99217325]], [[-1.02812186, -0.47629981], [ 1.76037976, 0.42173594]]])Tensor(shape=(2, 2, 2), inds=[_67af2fAAACs, _67af2fAAACr, _67af2fAAACH], tags={I96}),
backend=numpy, dtype=float64, data=array([[[-0.8272083 , -0.77226563], [ 1.40038892, -0.45392213]], [[ 0.99849229, -0.15310644], [-1.83419131, -0.15131365]]])Tensor(shape=(2, 2, 2), inds=[_67af2fAAACb, _67af2fAAABt, _67af2fAAAAS], tags={I97}),
backend=numpy, dtype=float64, data=array([[[ 0.67219424, -0.67334425], [ 1.0021386 , -0.55126219]], [[ 0.59799654, 0.36190119], [-1.33290845, -2.0234266 ]]])Tensor(shape=(2, 2, 2), inds=[_67af2fAAACp, _67af2fAAACZ, _67af2fAAAAP], tags={I98}),
backend=numpy, dtype=float64, data=array([[[-0.96087989, 0.66463361], [-0.29470099, 0.47419775]], [[ 0.28326056, 0.42166369], [-0.45476309, -2.62552245]]])Tensor(shape=(2, 2, 2), inds=[_67af2fAAACY, _67af2fAAACI, _67af2fAAABq], tags={I99}),
backend=numpy, dtype=float64, data=array([[[ 0.53322615, 0.07497022], [-0.12286424, -0.00459035]], [[ 0.74915553, -0.93343275], [-0.5978229 , 0.35686287]]])...
Although quimb
can automatically generate the einsum equation in this case,
it might be generally annoying to construct the string. Instead we can directly
use the tensor networks index names:
arrays = []
inputs = []
for t in tn:
arrays.append(t.data)
inputs.append(t.inds)
In this case they are randomly generated unique indentifiers, but they could be anything hashable, as long as they match the shapes of the arrays and encode the geometry of the contraction.
inputs[:5]
[('_67af2fAAAAC', '_67af2fAAAAB', '_67af2fAAAAA'),
('_67af2fAAAAF', '_67af2fAAAAE', '_67af2fAAAAD'),
('_67af2fAAAAI', '_67af2fAAAAH', '_67af2fAAAAG'),
('_67af2fAAAAL', '_67af2fAAAAK', '_67af2fAAAAJ'),
('_67af2fAAAAN', '_67af2fAAAAM', '_67af2fAAAAB')]
Perform the contraction, using the higher quality auto preset:
%%time
ctg.array_contract(arrays, inputs, optimize="auto-hq")
CPU times: user 356 ms, sys: 585 µs, total: 356 ms
Wall time: 322 ms
array(-1.78478002e+15)
Because the contraction expression is cached if we perform the contraction again with the same preset it will be faster:
%%time
ctg.array_contract(arrays, inputs, optimize="auto-hq")
CPU times: user 157 ms, sys: 4.63 ms, total: 162 ms
Wall time: 33.1 ms
array(-1.78478002e+15)
If you want to relate the internal contraction ‘symbols’ (single unicode
letters) that appear on the contraction tree to the supplied ‘indices’
(arbitrary hashable objects) then you can call the
get_symbol_map
function:
symbol_map = ctg.get_symbol_map(inputs)
[symbol_map[ind] for ind in t.inds]
['Ď', 'ý', 'ì']