<jupyter_start><jupyter_text>Import numpy library and create a random matrix<jupyter_code>import numpy as np
n = 4
A = np.random.random((n,n))
A<jupyter_output><empty_output><jupyter_text>Create another matrix and multiply the two<jupyter_code>B = np.random.random((n,n))
np.dot(A,B)<jupyter_output><empty_output><jupyter_text>Import Cyclops Tensor Framework library and convert numpy matrices to CTF matrices<jupyter_code>import ctf
tA = ctf.astensor(A)
tB = ctf.astensor(B)
ctf.dot(tA,tB)<jupyter_output><empty_output><jupyter_text>Use CTF index-based notation to perform multiplication<jupyter_code>tC = ctf.zeros((n,n))
tC.i("ij") << tA.i("ik")*tB.i("kj")
tC<jupyter_output><empty_output><jupyter_text>The particular character `'i','j','k'` don't matter, we can replace them with `'z','?','+'`<jupyter_code>tC = ctf.zeros((n,n))
tC.i("z?") << tA.i("z+")*tB.i("+?")
tC<jupyter_output><empty_output><jupyter_text>`numpy` actually has similar functionality via `einsum`<jupyter_code>np.einsum("ik,kj->ij",A,B)
ctf.einsum("ik,kj->ij",tA,tB)<jupyter_output><empty_output><jupyter_text>This notation can be used to contract tensor networks, for instance the tensor train (MPS)<jupyter_code>k = 2 # rank
W = ctf.tensor([k,n,k])
V = ctf.tensor([k,n])
W.fill_random(-1.0,1.0)
V.fill_random(-1.0,1.0)
Z = ctf.tensor([n,n,n,n,n,n])
Z.i("ijklmn") << V.i("ai")*W.i("ajb")*W.i("bkc")*W.i("cld")*W.i("dme")*V.i("en")
print(Z[1:3,0,2,1,0:3,1].reshape((3,2)))<jupyter_output>array([[ 0.07366687,  0.12803461],
       [ 0.06423109,  0.02295772],
       [ 0.05320817,  0.02112921]])<jupyter_text>Using `np.einsum` the contractions look as follows<jupyter_code>V2 = V.to_nparray()
W2 = W.to_nparray()
Z2 = np.einsum("ai,ajb,bkc,cld,dme,en->ijklmn",V2,W2,W2,W2,W2,V2)
print(Z2[1:3,0,2,1,0:3,1].reshape((3,2)))

#same possible with CTF
Z = ctf.einsum("ai,ajb,bkc,cld,dme,en->ijklmn",V,W,W,W,W,V)
print(Z[1:3,0,2,1,0:3,1].reshape((3,2)))<jupyter_output>[[ 0.07366687  0.12803461]
 [ 0.06423109  0.02295772]
 [ 0.05320817  0.02112921]]
array([[ 0.07366687,  0.12803461],
       [ 0.06423109,  0.02295772],
       [ 0.05320817,  0.02112921]])<jupyter_text>To contract together a CP decomposition, we need to use Hadamard products<jupyter_code>U = ctf.tensor([k,n])
U.fill_random(-1.0,1.0)

Z.set_zero()

#note that the `a` index appears in multiple operands
Z.i("ijklmn") << U.i("ai")*U.i("aj")*U.i("ak")*U.i("al")*U.i("am")*U.i("an")
print(Z[1:3,0,2,1,0:3,1].reshape((3,2)))

U2 = U.to_nparray()
Z2 = np.einsum("ai,aj,ak,al,am,an->ijklmn",U2,U2,U2,U2,U2,U2)
print(Z2[1:3,0,2,1,0:3,1].reshape((3,2)))<jupyter_output>array([[-0.06735958,  0.01278285],
       [-0.05052931,  0.02043648],
       [-0.05052931,  0.04237852]])
[[-0.06735958  0.01278285]
 [-0.05052931  0.02043648]
 [-0.05052931  0.04237852]]<jupyter_text>Lets test the preformance of the MPS contractions for a different rank<jupyter_code>import time
for k in np.arange(4)*2+2:
    t = time.time()
    W = ctf.tensor([k,n,k])
    V = ctf.tensor([k,n])
    W.fill_random(-1.0,1.0)
    V.fill_random(-1.0,1.0)
    Z = ctf.tensor([n,n,n,n,n,n])
    Z.i("ijklmn") << V.i("ai")*W.i("ajb")*W.i("bkc")*W.i("cld")*W.i("dme")*V.i("en")
    V2 = V.to_nparray()
    W2 = W.to_nparray()
    #time CTF, including all initialization and conversions
    print("ctf   k =",k,"took",time.time()-t,"seconds.")
    t2 = time.time()
    Z2 = np.einsum("ai,ajb,bkc,cld,dme,en->ijklmn",V2,W2,W2,W2,W2,V2)
    print("numpy k =",k,"took",time.time()-t2,"seconds.")<jupyter_output>ctf   k = 2 took 0.028398990631103516 seconds.
numpy k = 2 took 0.012132644653320312 seconds.
ctf   k = 4 took 0.17014527320861816 seconds.
numpy k = 4 took 0.22895264625549316 seconds.
ctf   k = 6 took 0.88639235496521 seconds.
numpy k = 6 took 1.2816572189331055 seconds.
ctf   k = 8 took 4.034878253936768 seconds.
numpy k = 8 took 4.610719680786133 seconds.<jupyter_text>Lets create a sparse tensor of total size $4^{12}$<jupyter_code>Z = ctf.tensor([n,n,n,n,n,n,n,n,n,n,n,n],sp=1)
Z.fill_sp_random(-1.,1.,.00001)
Z.read_local_nnz()
#create a random vector in a sparse representation
v = ctf.tensor([n],sp=1)
v.fill_sp_random(0.,1.,1.)

#create an order 12 sparse tensor
Z = ctf.tensor([n,n,n,n,n,n,n,n,n,n,n,n],sp=1)

#fill tensor so that .001% of entries are nonzero
Z.fill_sp_random(0.,1.,.00001)

#set diagonal to zero
Z.i("iiiiiiiiiiii") << 1. 

str12 = "1234567890ab"
for i in range(1,12)[::-1]:
    #normalize tensor
    Z.i(str12[0:i]).scl(1./ctf.sum(Z))
    #create tensor with one less dimension
    Z_new = ctf.tensor([n for j in range(i)],sp=1)
    #contract tensor over its last mode with a vector
    Z_new.i(str12[0:i]) << Z.i(str12[0:i+1])*v.i(str12[i])
    #replace old tensor with lower-dimensional one
    Z = Z_new

#read the nonzeros from Z stored on this processor
inds, vals = Z.read_local_nnz()
print(Z.ndim)
print(inds,vals)<jupyter_output>1
[0 1 2 3] [ 0.00282648  0.02617537  0.54507062  0.00662197]
{'fraction_non_alphanumeric': 0.19930992490359245, 'fraction_numerical': 0.12604018672620257, 'mean_word_length': 5.167709637046308, 'pattern_counts': {'":': 0, '<': 54, '<?xml version=': 0, '>': 46, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 1, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'chain_length': '11', 'dir': 'jupyter-structured-clean-dedup', 'id': '331246', 'license': 'BSD-2-Clause', 'n_tokens_mistral': 2638, 'n_tokens_neox': 2325, 'n_words': 384, 'path': 'doc/CTF_introductory_demo.ipynb', 'repo_name': 'airmler/ctf', 'size': '4927'}