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 fromcotengraoropt_einsumContractionTree: a contraction tree generated previouslySequence[tuple[int]]: an explicit path of ‘linear recycled ids’, specified manually or generated previouslySequence[str]: an explicit path supplied as an edge ordering
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]
['Ď', 'ý', 'ì']