{"id":16977872,"url":"https://github.com/jcmgray/symmray","last_synced_at":"2025-04-12T00:32:21.709Z","repository":{"id":245020808,"uuid":"536305811","full_name":"jcmgray/symmray","owner":"jcmgray","description":"A minimal block sparse symmetric and fermionic tensor python library","archived":false,"fork":false,"pushed_at":"2025-03-23T20:31:56.000Z","size":744,"stargazers_count":13,"open_issues_count":0,"forks_count":3,"subscribers_count":2,"default_branch":"main","last_synced_at":"2025-04-04T19:22:54.766Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":null,"language":"Python","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"apache-2.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/jcmgray.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2022-09-13T20:55:33.000Z","updated_at":"2025-03-23T20:32:00.000Z","dependencies_parsed_at":"2024-07-20T00:16:37.422Z","dependency_job_id":"742bf847-6445-4659-abef-03b41b6c3cc5","html_url":"https://github.com/jcmgray/symmray","commit_stats":{"total_commits":107,"total_committers":4,"mean_commits":26.75,"dds":0.03738317757009346,"last_synced_commit":"d9e77bf064ce480d5ac0a887b29707fe982c092f"},"previous_names":["jcmgray/symmray"],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/jcmgray%2Fsymmray","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/jcmgray%2Fsymmray/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/jcmgray%2Fsymmray/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/jcmgray%2Fsymmray/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/jcmgray","download_url":"https://codeload.github.com/jcmgray/symmray/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":248501425,"owners_count":21114674,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2024-10-14T01:30:00.120Z","updated_at":"2025-04-12T00:32:21.693Z","avatar_url":"https://github.com/jcmgray.png","language":"Python","funding_links":[],"categories":[],"sub_categories":[],"readme":"# symmray\n\nA minimal library for block sparse, abelian symetric and fermionic arrays,\ndesigned to look as much as possible like standard ndarrays, whose blocks can\nbe backed by `numpy`, `torch` or any other `autoray` compatible library.\n\n\n## Installation\n\n**Installing the latest version directly from github:**\n\nIf you want to checkout the latest version of features and fixes, you can\ninstall directly from the github repository:\n```bash\npip install -U git+https://github.com/jcmgray/symmray.git\n```\n\n**Installing a local, editable development version:**\n\nIf you want to make changes to the source code and test them out, you can\ninstall a local editable version of the package:\n```bash\ngit clone https://github.com/jcmgray/symmray.git\npip install --no-deps -U -e symmray/\n```\n\n\n## Usage\n\n`symmray` objects are designed so that, as much as possible, one can interact\nwith them in the same way as standard arrays. You can use the functions from\nthe ``symmray`` namespace directly:\n\n```python\nimport symmray as sr\n\nz = sr.tensordot(x, y, axes=[(5, 2), (3, 7)])\n```\n\nor you can use the automatic dispatch library `autoray` to support multiple\nbackends including `symmray`:\n\n```python\nimport autoray as ar\n\nz = ar.do(\"tensordot\", x, y, axes=[(5, 2), (3, 7)])\n```\n\n`symmray` also uses `autoray` internally to handle manipulating blocks within\nan array, meaning that these can be `numpy`, `torch`, `jax` or any other\n`autoray` compatible library.\n\nWhilst block sparse arrays do not have such a well defined notion of shape as\ndense arrays, for ease and compatibility with other libraries, `symmray` arrays\ndo have a `.shape` attribute which is the shape of the dense array that would\nbe returned by calling `to_dense` on the array, and a similarly defined\n`.size`. Likewise, `symmray` supports fusing and unfusing of indices via\n`reshape` (as long as it is clear what is meant by the new shape).\n\n### Quick-start tensor networks and Hamiltonians\n\n`symmray` provides constructors for various `quimb.tensor.TensorNetwork` networks:\n\n- `TN_abelian_from_edges_rand`\n- `TN_fermionic_from_edges_rand`\n- `PEPS_abelian_rand` (2D specific)\n- `PEPS_fermionic_rand` (2D specific)\n\nAlong with constructors for common hamiltonians:\n\n- `ham_tfim_from_edges`\n- `ham_heisenberg_from_edges`\n- `ham_fermi_hubbard_from_edges`\n- `ham_fermi_hubbard_spinless_from_edges` (AKA 't-V' model)\n\nThese constructors automatically chooose various defaults. See the `examples`\nfolder for usage.\n\n\n### Block sparse abelian symmetric arrays\n\nThe core `AbelianArray` object consists of 4 main components:\n\n1. `.indices`: a sequence of `BlockIndex` instances describing the charge\n   distribution and 'dualness' of each dimension.\n2. `.charge`: an overall charge for the array, which sets which combinations\n   of index charges ('sectors') are allowed.\n3. `.blocks`: a dict mapping each non-zero sector to a 'raw' array.\n4. `.symmetry`: an object defining allowed charges and how they combine.\n\nSpecific subclasses of `AbelianArray` have a static `.symmetry` class\nattribute.\n\nThe `BlockIndex` object consists of 2 main components:\n\n1. `.chargemap`: a dict mapping each charge to its size. The total size of the\n   index is the sum of the sizes of all charges.\n2. `.dual`: a boolean indicating whether the index is 'dual' or not. By\n   convention:\n\n    - `dual=False` means index flows 'outwards' / (+ve) / ket-like\n    - `dual=True` means index flows 'inwards' / (-ve) / bra-like\n\nOne convenient way to create `AbelianArray` instances is via the `from_fill_fn`\nmethod, which takes a function with signature `fn(shape) -\u003e array_like` and\nuses it to fill each valid sector of the array.\n\n```python\nimport symmray as sr\nimport numpy as np\n\nindices = (\n    sr.BlockIndex(chargemap={0: 3, 1: 4}, dual=False),\n    sr.BlockIndex(chargemap={0: 5, 1: 6}, dual=True),\n)\n\nx = sr.Z2Array.from_fill_fn(\n    fill_fn=np.ones,\n    indices=indices,\n    charge=1,\n)\nx\n# Z2Array(shape~(7, 11):[+-], charge=1, num_blocks=2)\n\nx.blocks\n# {(0,\n#   1): array([[1., 1., 1., 1., 1., 1.],\n#         [1., 1., 1., 1., 1., 1.],\n#         [1., 1., 1., 1., 1., 1.]]),\n#  (1,\n#   0): array([[1., 1., 1., 1., 1.],\n#         [1., 1., 1., 1., 1.],\n#         [1., 1., 1., 1., 1.],\n#         [1., 1., 1., 1., 1.]])}\n```\n\nWe can pictorially represent this like so:\n\n![simple-symmetric-array-pic](docs/images/simple-symmetric-array-pic.png)\n\nYou can also create `AbelianArray` instances using the methods:\n\n- `AbelianArray(indices, charge, blocks)`\n- `AbelianArray.from_blocks(blocks, duals, charges)` which calculates the\n  index chargemaps from the sectors and blocks themselves.\n- `AbelianArray.random(indices, charge)` which uses a random `fill_fn`\n- `AbelianArray.from_dense(array, index_maps, duals, charge)` which converts\n  a dense array to a block sparse array given a mapping for each axis, which\n  specifies the charge of each linear index in the dense array.\n\nKey functions which match the numpy versions are:\n\n- `conj`\n- `reshape`\n- `tensordot`\n- `trace`\n- `transpose`\n\nWith additional `symmray` specific key functions:\n\n- `fuse`\n- `multiply_diagonal`\n\nFuse in particular is a crucial function for A. performing efficient\ncontractions, B. performing linear algebra decompositions, as well as various\nother tensor network manipulations. You can either fuse and unfuse directly, or\nby using `reshape`. Note that if a `symmray` array is quite sparse (e.g. with\nU1 symmetry), then the resulting fused/reshaped shape will not necessarily\nmatch the dense specification.\n\nThe key function `tensordot` can use one of two methods.\n\n- `tensordot(x, y, axes, method=\"fused\")`: fuse the two arrays into block\n  diagonal matrices and then unfuse the result. This can be much faster, though\n  possibly requires explicitly filling missing blocks with zeros.\n- `tensordot(x, y, axes, method=\"blockwise\")`: compute the contraction by\n  directly looping over the blocks of `x` and `y` and contracting them. This\n  has quite high overhead for large numbers of blocks.\n\n\n### Fermionic arrays\n\nThe approach to fermionic arrays `symmray` takes is equivalent to the\n'Grassmann' or graded algebra approach. This associates a fermionic parity to\neach charge, combined with the directionality specified by `dual`, allows\nall fermionic swaps and the relevant sector phase changes to be handled\nessentially locally.\n\nThe core `FermionicArray` is a subclass of `AbelianArray` and instantiated in\nthe same way:\n\n```python\nindices = (\n    sr.BlockIndex(chargemap={-1: 2, 0: 2, 1: 3}, dual=False),\n    sr.BlockIndex(chargemap={0: 2, 2: 3, 3: 4}, dual=True),\n)\n\nx = sr.U1FermionicArray.random(\n    indices=indices,\n    charge=-2,\n)\nprint(x)\n# U1FermionicArray(ndim=2, charge=-2, indices=[\n#     (7 = 2+2+3 : +[-1,0,1])\n#     (9 = 2+3+4 : -[0,2,3])\n# ], num_blocks=2, backend=numpy, dtype=float64)\n```\n\nPhases are lazily tracked into the attribute `.phases` when:\n\n- transposing\n- fusing\n- conjugating\n- contracting via tensordot or `__matmul__` / `@`\n- tracing\n- linear algebra decompositions\n\nvia the methods:\n\n- `FermionicArray.phase_flip`: virtually insert 'parity' tensors on some axes\n- `FermionicArray.phase_transpose`: compute the phase of a 'virtual' transpose\n\nAnd inserted when needed using:\n\n- `FermionicArray.phase_sync`: actually multiply the blocks by the phases.\n\n\n#### Multiple odd-parity fermionic arrays - `oddpos`\n\nIf you want to work with networks involving multiple odd-parity tensors then\nyou must supply any *sortable* label `oddpos` to the `FermionicArray`\nconstructor, which acts like a sequence of dummy indices with odd-parity.\nWhenever two arrays with `oddpos` are contracted, a global phase\nis possibly inserted coming from sorting these dummy odd-parity indices.\n\nAn initial single value of ``oddpos`` is converted into a length 1 tuple, and\nthese are then concatenated and sorted when two arrays are contracted. For\nexample, if `a` and `b` have accrued the following `oddpos` values:\n```\noddpos_a = (2, 3, 5)\noddpos_b = (4, 6,)\n```\ntheir contraction would result in:\n```\n(2, 3, 5, 4, 6)\n-\u003e sort introduces phase -\u003e\n(2, 3, 4, 5, 6)\n-\u003e neighboring oddpos pairs can then be cancelled -\u003e\noddpos_new = (6,)\n```\n\nThis gives a canonical sign to the overall network that is handled\nautomatically and locally (once the initial `oddpos` values are chosen.)\nThe phase is tracked lazily via `FermionicArray.phase_global`.\n\nIf for some reason you would like to create a `FermionicArray` with multiple\nlabels then you should supply a **list** of labels.\n\n\n#### Conjugation and tensor networks\n\nConjugating a fermionic array is handled by the `.conj()` method, with two\nnotable options, `phase_permutation=True` by default and `phase_dual=False` by\ndefault. The former applies phases as if we had reversed the order of axes\n(though we don't change the data layout). The latter applies 'virtual' parity\ntensors to dual indices, which can be desired if they are the 'dangling' legs\nof a tensor network.\n\nBy default, only the first happens. This implies if you have a tensor network\nwavefunction $|\\psi\\rangle$ where all *dangling* indices are ket-like\n(dual=False), then conjugating each tensor locally gives you the bra\nwavefunction $\\langle\\psi|$ such that the contraction $\\langle\\psi|\\psi\\rangle$\nyields the norm squared of the wavefunction.\n\nIf the tensor network has both bra-like and ket-like dangling indices\n(e.g. in the infinite setting or using cluster approximations), then the\ndangling dual legs of the conjugated network must be explicitly phase-flipped.\n\nFor example, in the following network:\n\n![tn-conjugate-phase](docs/images/tn-conjugate-phase.png)\n\nthe only index that needs to be phase-flipped beyond `phase_permutation` is the\norange dangling 'ket' index in the bra $\\langle\\psi|$, indicated by the red\ndiamond.\n\n\n#### Local fermionic operators\n\nMany tensor network algorithms involve applying local fermionic operators to\nthe wavefunction. Such local operators need to be expressed in a local basis\nwith a particular ordering and resulting phases. `symmray` provides several\ncommon operators:\n\n- `fermi_hubbard_local_array`:\n\n$$\n-t (a_\\uparrow^\\dagger b_\\uparrow + b_\\uparrow^\\dagger a_\\uparrow + a_\\downarrow^\\dagger b_\\downarrow + b_\\downarrow^\\dagger a_\\downarrow) + U(a_\\uparrow^\\dagger a_\\uparrow a_\\downarrow^\\dagger a_\\downarrow + b_\\uparrow^\\dagger b_\\uparrow b_\\downarrow^\\dagger b_\\downarrow) - \\mu (a_\\uparrow^\\dagger a_\\uparrow + a_\\downarrow^\\dagger a_\\downarrow + b_\\uparrow^\\dagger b_\\uparrow + b_\\downarrow^\\dagger b_\\downarrow)\n$$\n\n- `fermi_hubbard_spinless_local_array`:\n\n$$\n-t(a^\\dagger b + b^\\dagger a) + V a^\\dagger a b^\\dagger b - \\mu(a^\\dagger a + b^\\dagger b)\n$$\n\n- `fermi_number_operator_spinful_local_array`:\n\n$$\na_\\uparrow^\\dagger a_\\uparrow + a_\\downarrow^\\dagger a_\\downarrow\n$$\n\n- `fermi_number_operator_spinless_local_array`:\n\n$$\na^\\dagger a\n$$\n\n- `fermi_spin_operator_local_array`:\n\n$$\n\\frac{1}{2}(a_\\uparrow^\\dagger a_\\uparrow - a_\\downarrow^\\dagger a_\\downarrow)\n$$\n\n plus lower level functions for building custom ones:\n\n- `build_local_fermionic_array`\n- `build_local_fermionic_elements`\n\nThese latter functions take a specification of `terms`, which is a sequence of tuples of the\nform `(coeff, ops)` where `ops` is a sequence of symbolic `FermionicOperator`\nobjects, (or equivalent pair `(label, op)`).\n\nSecondly they take a specification of `bases`. This is a sequence of each local\nbasis, each a sequence of `FermionicOperator` objects.\n\nFor example, imagine we want to build the term:\n\n$$8 n_a n_b  - 2 (n_a + n_b)= 8 a^\\dagger a b^\\dagger b - 2 a^\\dagger a - 2 b^\\dagger b$$\n\ninto an array with elements defined:\n\n$$o = \\langle i' | \\langle j' | \\hat O | i \\rangle | j \\rangle$$\n\nwhere the two bases are given by: $| i \\rangle = \\{|0\\rangle, a^\\dagger|0\\rangle\\}$ and $| j \\rangle = \\{|0\\rangle, b^\\dagger|0\\rangle\\}$. We can build this operator as follows:\n\n```python\na, b = map(sr.FermionicOperator, 'ab')\n# you can also use strings or pairs like\n# adag = 'a+' or ('a', '+')\n# a    = 'a-' or ('a', '-')\n\nterms = [\n    (+8, (a.dag, a, b.dag, b)),\n    (-2, (a.dag, a)),\n    (-2, (b.dag, b)),\n]\n\nbases = [\n    [(), (a.dag,)],\n    [(), (b.dag,)],\n]\n\n# get just the non-zero elements (with phases)\nsr.build_local_fermionic_elements(\n    terms, bases\n)\n# {(0, 1, 0, 1): -2.0, (1, 0, 1, 0): -2.0, (1, 1, 1, 1): -4.0}\n```\n\nTo build an actual fermionic array we need to specify a symmetry and a `index_map` for each local basis that maps each index to a charge. For example, if we want to build the above operator into a `U1FermionicArray` we could do:\n\n```python\nsr.build_local_fermionic_array(\n    terms,\n    bases,\n    symmetry=\"U1\",\n    index_maps=[\n        (0, 1),  # charges for basis i\n        (0, 1),  # charges for basis j\n    ]\n)\n# U1FermionicArray(shape~(2, 2, 2, 2):[++--], charge=0, num_blocks=6)\n```\n\nFermi-hubbard and spinless fermi-hubbard operators have built-in local functions:\n```python\nsr.fermi_hubbard_local_array(\"U1U1\", t=1.0, U=8.0, mu=5).blocks\n# {((0, 0), (0, 0), (0, 0), (0, 0)): array([[[[0.]]]]),\n#  ((0, 0), (0, 1), (0, 0), (0, 1)): array([[[[-5.]]]]),\n#  ((0, 0), (0, 1), (0, 1), (0, 0)): array([[[[-1.]]]]),\n#  ((0, 0), (1, 0), (0, 0), (1, 0)): array([[[[-5.]]]]),\n#  ((0, 0), (1, 0), (1, 0), (0, 0)): array([[[[-1.]]]]),\n#  ((0, 0), (1, 1), (0, 0), (1, 1)): array([[[[-2.]]]]),\n#  ((0, 0), (1, 1), (0, 1), (1, 0)): array([[[[1.]]]]),\n#  ((0, 0), (1, 1), (1, 0), (0, 1)): array([[[[-1.]]]]),\n#  ((0, 0), (1, 1), (1, 1), (0, 0)): array([[[[0.]]]]),\n#  ((0, 1), (0, 0), (0, 0), (0, 1)): array([[[[-1.]]]]),\n#  ((0, 1), (0, 0), (0, 1), (0, 0)): array([[[[-5.]]]]),\n#  ((0, 1), (0, 1), (0, 1), (0, 1)): array([[[[10.]]]]),\n#  ((0, 1), (1, 0), (0, 0), (1, 1)): array([[[[-1.]]]]),\n#  ((0, 1), (1, 0), (0, 1), (1, 0)): array([[[[10.]]]]),\n#  ((0, 1), (1, 0), (1, 0), (0, 1)): array([[[[0.]]]]),\n#  ((0, 1), (1, 0), (1, 1), (0, 0)): array([[[[-1.]]]]),\n#  ((0, 1), (1, 1), (0, 1), (1, 1)): array([[[[-7.]]]]),\n#  ((0, 1), (1, 1), (1, 1), (0, 1)): array([[[[1.]]]]),\n#  ((1, 0), (0, 0), (0, 0), (1, 0)): array([[[[-1.]]]]),\n#  ((1, 0), (0, 0), (1, 0), (0, 0)): array([[[[-5.]]]]),\n#  ((1, 0), (0, 1), (0, 0), (1, 1)): array([[[[1.]]]]),\n#  ((1, 0), (0, 1), (0, 1), (1, 0)): array([[[[0.]]]]),\n#  ((1, 0), (0, 1), (1, 0), (0, 1)): array([[[[10.]]]]),\n#  ((1, 0), (0, 1), (1, 1), (0, 0)): array([[[[1.]]]]),\n#  ((1, 0), (1, 0), (1, 0), (1, 0)): array([[[[10.]]]]),\n#  ((1, 0), (1, 1), (1, 0), (1, 1)): array([[[[-7.]]]]),\n#  ((1, 0), (1, 1), (1, 1), (1, 0)): array([[[[1.]]]]),\n#  ((1, 1), (0, 0), (0, 0), (1, 1)): array([[[[0.]]]]),\n#  ((1, 1), (0, 0), (0, 1), (1, 0)): array([[[[1.]]]]),\n#  ((1, 1), (0, 0), (1, 0), (0, 1)): array([[[[-1.]]]]),\n#  ((1, 1), (0, 0), (1, 1), (0, 0)): array([[[[-2.]]]]),\n#  ((1, 1), (0, 1), (0, 1), (1, 1)): array([[[[1.]]]]),\n#  ((1, 1), (0, 1), (1, 1), (0, 1)): array([[[[-7.]]]]),\n#  ((1, 1), (1, 0), (1, 0), (1, 1)): array([[[[1.]]]]),\n#  ((1, 1), (1, 0), (1, 1), (1, 0)): array([[[[-7.]]]]),\n#  ((1, 1), (1, 1), (1, 1), (1, 1)): array([[[[-4.]]]])}\n```\n\n(Note that zero blocks are stored - for the sake of correctness when fusing and exponentiating.) The spinful versions uses the local basis:\n\n$$\n|i\\rangle =\n\\{|00\\rangle, c_{\\downarrow}|00\\rangle, c_{\\uparrow}|00\\rangle, c_{\\uparrow}c_{\\downarrow}|00\\rangle\\}\n$$\n\nwhich has a U1 `index_map` of charges `[0, 1, 1, 2]` or a U1U1 `index_map` of\ncharges `[(0, 0), (0, 1), (1, 0), (1, 1)]`.\n\nBoth `fermi_hubbard_local_array` and `fermi_hubbard_spinless_local_array` also take a `coordinations` argument which specifies the lattice coordination of the two sites. This scales any *on-site* (i.e. 1-local) terms by inverse coordination, so that these terms can be included in the pairwise (i.e. 2-local) arrays without overcounting. For example in a 1D open chain the boundary `coordinations` would be `(1, 2)` and `(2, 1)`, whereas the bulk would be `(2, 2)`. The utility function `sr.parse_edges_to_site_info` fills in coordination information.\n\n\n### Linear Algebra\n\n`symmray` supports abelian and fermionic versions of the following functions:\n\n- `norm`: frobenius norm\n- `svd`: singular value decomposition\n- `qr`: QR decomposition\n- `eigh`: hermitian eigendecomposition\n- `expm`: matrix exponential\n\nTensor network specific functions as used by `quimb`:\n\n- `svd_truncated`: svd with truncation based on maximum bond dimension and/or\n  a cutoff threshold with various modes.\n- `qr_stabilized`: qr decomposition with sign stabilization of the R matrix\n  diagonal, which is beneficial for gradient based optimization.\n\n\n#### Diagonal matrices and vectors: ``BlockVector``\n\nDecompositions such as SVD and eigendecomposition return singular and eigen\nvalues as a special type of block sparse array, a `BlockVector`. These are\nessentially just a dict of single charges to blocks, and don't contain any\ndualness information. Simple math operations are supported, as well as\nmultiplying them into a tensor with the function `multiply_diagonal`.\n\n\n### Symmetries\n\n`symmray` has the following symmetries built in:\n\n- `Z2`: parity symmetry\n- `U1`: abelian charge symmetry\n- `Z2Z2`: two parity symmetries\n- `U1U1`: two abelian charge symmetries\n\nThese are encasuplated in classes which describe:\n\n- the zero charge and valid charges\n- how to combine charges\n- how to negate charges\n- ...\n\nSee the `symmray.symmetries` module for how to define your own symmetries. You\ncan supply these directly to `AbelianArray` and `FermionicArray` constructors\n(dynamic symmetry), or you can create your own specific subclasses of these\nclasses (static symmetry), such as ``U1U1FermionicArray``.\n\n\n## Other libraries\n\nSome notable other libraries with overlapping functionality:\n\n- `abeliantensors`: https://github.com/mhauru/abeliantensors\n- `yastn`: https://github.com/yastn/yastn\n- `pyblock3`: https://github.com/block-hczhai/pyblock3-preview\n- `tensornetwork`: https://github.com/google/TensorNetwork\n- `grassmanntn`: https://github.com/ayosprakob/grassmanntn\n- `TensorKit.jl`: https://github.com/Jutho/TensorKit.jl\n\n\n## References\n\nAn incomplete but helpful list:\n\n**Abelian symmetries**:\n\n- \"Tensor network decompositions in the presence of a global symmetry\" - *Sukhwinder Singh, Robert N. C. Pfeifer, Guifre Vidal* - https://arxiv.org/abs/0907.2994\n\n- \"Implementing global Abelian symmetries in projected entangled-pair state algorithms\" - *B. Bauer, P. Corboz, R. Orus, M. Troyer* - https://arxiv.org/abs/1010.3595\n\n- \"Advances on Tensor Network Theory: Symmetries, Fermions, Entanglement, and Holography\" - *Roman Orus* - https://arxiv.org/abs/1407.6552\n\n**Fermionic Tensor Networks**\n\n- \"Fermionic Projected Entangled Pair States\" - *Christina V. Kraus, Norbert Schuch, Frank Verstraete, J. Ignacio Cirac* - https://arxiv.org/abs/0904.4667\n\n- \"Fermionic multi-scale entanglement renormalization ansatz\" - *Philippe Corboz, Guifre Vidal* - https://arxiv.org/abs/0907.3184\n\n- \"Simulation of strongly correlated fermions in two spatial dimensions with fermionic Projected Entangled-Pair States\" - *Philippe Corboz, Roman Orus, Bela Bauer, Guifre Vidal* - https://arxiv.org/abs/0912.0646\n\n- \"Fermionic Implementation of Projected Entangled Pair States Algorithm\" - *Iztok Pizorn, Frank Verstraete* - https://arxiv.org/abs/1003.2743\n\n`symmray` is most closely related to the following **'local' approaches**:\n\n- \"Grassmann tensor network states and its renormalization for strongly correlated fermionic and bosonic states\" - *Zheng-Cheng Gu, Frank Verstraete, Xiao-Gang Wen* - https://arxiv.org/abs/1004.2563\n\n- \"Gradient optimization of fermionic projected entangled pair states on directed lattices\" - *Shao-Jun Dong, Chao Wang, Yongjian Han, Guang-can Guo, Lixin He* - https://arxiv.org/abs/1812.03657\n\n- \"Fermionic tensor network methods\" - *Quinten Mortier, Lukas Devos, Lander Burgelman, Bram Vanhecke, Nick Bultinck, Frank Verstraete, Jutho Haegeman, Laurens Vanderstraeten* - https://arxiv.org/abs/2404.14611\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fjcmgray%2Fsymmray","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fjcmgray%2Fsymmray","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fjcmgray%2Fsymmray/lists"}