diff --git a/Project.toml b/Project.toml index 64af22b6..e1dc76cb 100644 --- a/Project.toml +++ b/Project.toml @@ -1,7 +1,7 @@ name = "ITensorNetworks" uuid = "2919e153-833c-4bdc-8836-1ea460a35fc7" authors = ["Matthew Fishman , Joseph Tindall and contributors"] -version = "0.11.15" +version = "0.11.24" [deps] AbstractTrees = "1520ce14-60c1-5f80-bbc7-55ef81b5835c" @@ -62,20 +62,20 @@ DocStringExtensions = "0.9" EinExprs = "0.6.4" Graphs = "1.8" GraphsFlows = "0.1.1" -ITensorMPS = "0.2.2" -ITensors = "0.6.8" -IsApprox = "0.1" +ITensorMPS = "0.3" +ITensors = "0.7" +IsApprox = "0.1, 1, 2" IterTools = "1.4.0" -KrylovKit = "0.6, 0.7" +KrylovKit = "0.6, 0.7, 0.8" MacroTools = "0.5" NDTensors = "0.3" NamedGraphs = "0.6.0" -OMEinsumContractionOrders = "0.8.3" +OMEinsumContractionOrders = "0.8.3, 0.9" Observers = "0.2.4" PackageExtensionCompat = "1" SerializedElementArrays = "0.1" SimpleTraits = "0.9" -SparseArrayKit = "0.3" +SparseArrayKit = "0.3, 0.4" SplitApplyCombine = "1.2" StaticArrays = "1.5.12" StructWalk = "0.2" diff --git a/README.md b/README.md index 6ce69026..3e0dbb7c 100644 --- a/README.md +++ b/README.md @@ -51,17 +51,17 @@ and 3 edge(s): with vertex data: 4-element Dictionaries.Dictionary{Int64, Any} - 1 │ ((dim=2|id=739|"1,2"),) - 2 │ ((dim=2|id=739|"1,2"), (dim=2|id=920|"2,3")) - 3 │ ((dim=2|id=920|"2,3"), (dim=2|id=761|"3,4")) - 4 │ ((dim=2|id=761|"3,4"),) + 1 │ ((dim=2|id=664|"1,2"),) + 2 │ ((dim=2|id=664|"1,2"), (dim=2|id=561|"2,3")) + 3 │ ((dim=2|id=561|"2,3"), (dim=2|id=47|"3,4")) + 4 │ ((dim=2|id=47|"3,4"),) julia> tn[1] -ITensor ord=1 (dim=2|id=739|"1,2") +ITensor ord=1 (dim=2|id=664|"1,2") NDTensors.EmptyStorage{NDTensors.EmptyNumber, NDTensors.Dense{NDTensors.EmptyNumber, Vector{NDTensors.EmptyNumber}}} julia> tn[2] -ITensor ord=2 (dim=2|id=739|"1,2") (dim=2|id=920|"2,3") +ITensor ord=2 (dim=2|id=664|"1,2") (dim=2|id=561|"2,3") NDTensors.EmptyStorage{NDTensors.EmptyNumber, NDTensors.Dense{NDTensors.EmptyNumber, Vector{NDTensors.EmptyNumber}}} julia> neighbors(tn, 1) @@ -107,13 +107,13 @@ and 4 edge(s): with vertex data: 4-element Dictionaries.Dictionary{Tuple{Int64, Int64}, Any} - (1, 1) │ ((dim=2|id=74|"1×1,2×1"), (dim=2|id=723|"1×1,1×2")) - (2, 1) │ ((dim=2|id=74|"1×1,2×1"), (dim=2|id=823|"2×1,2×2")) - (1, 2) │ ((dim=2|id=723|"1×1,1×2"), (dim=2|id=712|"1×2,2×2")) - (2, 2) │ ((dim=2|id=823|"2×1,2×2"), (dim=2|id=712|"1×2,2×2")) + (1, 1) │ ((dim=2|id=68|"1×1,2×1"), (dim=2|id=516|"1×1,1×2")) + (2, 1) │ ((dim=2|id=68|"1×1,2×1"), (dim=2|id=538|"2×1,2×2")) + (1, 2) │ ((dim=2|id=516|"1×1,1×2"), (dim=2|id=278|"1×2,2×2")) + (2, 2) │ ((dim=2|id=538|"2×1,2×2"), (dim=2|id=278|"1×2,2×2")) julia> tn[1, 1] -ITensor ord=2 (dim=2|id=74|"1×1,2×1") (dim=2|id=723|"1×1,1×2") +ITensor ord=2 (dim=2|id=68|"1×1,2×1") (dim=2|id=516|"1×1,1×2") NDTensors.EmptyStorage{NDTensors.EmptyNumber, NDTensors.Dense{NDTensors.EmptyNumber, Vector{NDTensors.EmptyNumber}}} julia> neighbors(tn, (1, 1)) @@ -137,8 +137,8 @@ and 1 edge(s): with vertex data: 2-element Dictionaries.Dictionary{Tuple{Int64, Int64}, Any} - (1, 1) │ ((dim=2|id=74|"1×1,2×1"), (dim=2|id=723|"1×1,1×2")) - (1, 2) │ ((dim=2|id=723|"1×1,1×2"), (dim=2|id=712|"1×2,2×2")) + (1, 1) │ ((dim=2|id=68|"1×1,2×1"), (dim=2|id=516|"1×1,1×2")) + (1, 2) │ ((dim=2|id=516|"1×1,1×2"), (dim=2|id=278|"1×2,2×2")) julia> tn_2 = subgraph(v -> v[1] == 2, tn) ITensorNetworks.ITensorNetwork{Tuple{Int64, Int64}} with 2 vertices: @@ -151,8 +151,8 @@ and 1 edge(s): with vertex data: 2-element Dictionaries.Dictionary{Tuple{Int64, Int64}, Any} - (2, 1) │ ((dim=2|id=74|"1×1,2×1"), (dim=2|id=823|"2×1,2×2")) - (2, 2) │ ((dim=2|id=823|"2×1,2×2"), (dim=2|id=712|"1×2,2×2")) + (2, 1) │ ((dim=2|id=68|"1×1,2×1"), (dim=2|id=538|"2×1,2×2")) + (2, 2) │ ((dim=2|id=538|"2×1,2×2"), (dim=2|id=278|"1×2,2×2")) ``` @@ -178,9 +178,9 @@ and 2 edge(s): with vertex data: 3-element Dictionaries.Dictionary{Int64, Vector{ITensors.Index}} - 1 │ ITensors.Index[(dim=2|id=683|"S=1/2,Site,n=1")] - 2 │ ITensors.Index[(dim=2|id=123|"S=1/2,Site,n=2")] - 3 │ ITensors.Index[(dim=2|id=656|"S=1/2,Site,n=3")] + 1 │ ITensors.Index[(dim=2|id=549|"S=1/2,Site,n=1")] + 2 │ ITensors.Index[(dim=2|id=718|"S=1/2,Site,n=2")] + 3 │ ITensors.Index[(dim=2|id=254|"S=1/2,Site,n=3")] and edge data: 0-element Dictionaries.Dictionary{NamedGraphs.NamedEdge{Int64}, Vector{ITensors.Index}} @@ -198,9 +198,9 @@ and 2 edge(s): with vertex data: 3-element Dictionaries.Dictionary{Int64, Any} - 1 │ ((dim=2|id=683|"S=1/2,Site,n=1"), (dim=2|id=382|"1,2")) - 2 │ ((dim=2|id=123|"S=1/2,Site,n=2"), (dim=2|id=382|"1,2"), (dim=2|id=190|"2,3… - 3 │ ((dim=2|id=656|"S=1/2,Site,n=3"), (dim=2|id=190|"2,3")) + 1 │ ((dim=2|id=549|"S=1/2,Site,n=1"), (dim=2|id=149|"1,2")) + 2 │ ((dim=2|id=718|"S=1/2,Site,n=2"), (dim=2|id=149|"1,2"), (dim=2|id=113|"2,3… + 3 │ ((dim=2|id=254|"S=1/2,Site,n=3"), (dim=2|id=113|"2,3")) julia> tn2 = ITensorNetwork(s; link_space=2) ITensorNetworks.ITensorNetwork{Int64} with 3 vertices: @@ -215,9 +215,9 @@ and 2 edge(s): with vertex data: 3-element Dictionaries.Dictionary{Int64, Any} - 1 │ ((dim=2|id=683|"S=1/2,Site,n=1"), (dim=2|id=934|"1,2")) - 2 │ ((dim=2|id=123|"S=1/2,Site,n=2"), (dim=2|id=934|"1,2"), (dim=2|id=614|"2,3… - 3 │ ((dim=2|id=656|"S=1/2,Site,n=3"), (dim=2|id=614|"2,3")) + 1 │ ((dim=2|id=549|"S=1/2,Site,n=1"), (dim=2|id=407|"1,2")) + 2 │ ((dim=2|id=718|"S=1/2,Site,n=2"), (dim=2|id=407|"1,2"), (dim=2|id=205|"2,3… + 3 │ ((dim=2|id=254|"S=1/2,Site,n=3"), (dim=2|id=205|"2,3")) julia> @visualize tn1; ⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀ @@ -228,19 +228,19 @@ julia> @visualize tn1; ⠀⠀⠀⠀⠀⡇⠉⠢⢄⡀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀ ⠀⠀⠀⠀⠀⡇⠀⠀⠀⠈⠒⠤⡀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀ ⠀⠀⠀⠀⠀⡇⠀⠀⠀⠀⠀⠀⠈⠑⠢2⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀ - ⠀⠀⠀⠀⠀2⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠉⠢⢄⡀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀ + ⠀⠀⠀⠀⠀⡇⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠉⠢⢄⡀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀ ⠀⠀⠀⠀⠀⡇⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠈⠒⠤⡀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀ - ⠀⠀⠀⠀⠀⡇⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠈⠑⠢⣀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀ + ⠀⠀⠀⠀⠀2⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠈⠑⠢⣀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀ ⠀⠀⠀⠀⠀⡇⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀tn12⢄⡀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀ - ⠀⠀⠀⠀⠀⠁⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⡇⠀⠀⠈⠑⠤⣀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀ - ⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⡇⠀⠀⠀⠀⠀⠀⠑⠢⢄⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀ - ⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀2⠀⠀⠀⠀⠀⠀⠀⠀⠀2⠒⢄⡀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀ - ⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⡇⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠈⠑⠤⣀⠀⠀⠀⠀⠀⠀⠀⠀⠀ - ⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⡇⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠑⠢⢄⠀⠀⠀⠀⠀⠀ + ⠀⠀⠀⠀⠀⡇⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⡇⠀⠀⠈⠒⠤⡀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀ + ⠀⠀⠀⠀⠀⡇⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⡇⠀⠀⠀⠀⠀⠈⠑⠢⣀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀ + ⠀⠀⠀⠀⠀⡇⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⡇⠀⠀⠀⠀⠀⠀⠀⠀⠀2⠢⢄⡀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀ + ⠀⠀⠀⠀⠀⡇⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⡇⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠈⠒⠤⡀⠀⠀⠀⠀⠀⠀⠀⠀⠀ + ⠀⠀⠀⠀⠀⠃⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀2⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠈⠑⠢⣀⠀⠀⠀⠀⠀⠀ ⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⡇⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀tn13⠀⠀⠀⠀ - ⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠃⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢸⠀⠀⠀⠀⠀ - ⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢸⠀⠀⠀⠀⠀ - ⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀2⠀⠀⠀⠀⠀ + ⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⡇⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢸⠀⠀⠀⠀⠀ + ⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⡇⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢸⠀⠀⠀⠀⠀ + ⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⡇⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢸⠀⠀⠀⠀⠀ ⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀ julia> @visualize tn2; @@ -252,19 +252,19 @@ julia> @visualize tn2; ⠀⠀⠀⠀⠀⡇⠉⠢⢄⡀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀ ⠀⠀⠀⠀⠀⡇⠀⠀⠀⠈⠒⠤⡀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀ ⠀⠀⠀⠀⠀⡇⠀⠀⠀⠀⠀⠀⠈⠑⠢2⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀ - ⠀⠀⠀⠀⠀2⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠉⠢⢄⡀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀ + ⠀⠀⠀⠀⠀⡇⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠉⠢⢄⡀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀ ⠀⠀⠀⠀⠀⡇⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠈⠒⠤⡀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀ - ⠀⠀⠀⠀⠀⡇⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠈⠑⠢⣀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀ + ⠀⠀⠀⠀⠀2⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠈⠑⠢⣀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀ ⠀⠀⠀⠀⠀⡇⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀tn22⢄⡀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀ - ⠀⠀⠀⠀⠀⠁⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⡇⠀⠀⠈⠑⠤⣀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀ - ⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⡇⠀⠀⠀⠀⠀⠀⠑⠢⢄⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀ - ⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀2⠀⠀⠀⠀⠀⠀⠀⠀⠀2⠒⢄⡀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀ - ⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⡇⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠈⠑⠤⣀⠀⠀⠀⠀⠀⠀⠀⠀⠀ - ⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⡇⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠑⠢⢄⠀⠀⠀⠀⠀⠀ + ⠀⠀⠀⠀⠀⡇⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⡇⠀⠀⠈⠒⠤⡀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀ + ⠀⠀⠀⠀⠀⡇⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⡇⠀⠀⠀⠀⠀⠈⠑⠢⣀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀ + ⠀⠀⠀⠀⠀⡇⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⡇⠀⠀⠀⠀⠀⠀⠀⠀⠀2⠢⢄⡀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀ + ⠀⠀⠀⠀⠀⡇⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⡇⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠈⠒⠤⡀⠀⠀⠀⠀⠀⠀⠀⠀⠀ + ⠀⠀⠀⠀⠀⠃⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀2⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠈⠑⠢⣀⠀⠀⠀⠀⠀⠀ ⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⡇⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀tn23⠀⠀⠀⠀ - ⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠃⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢸⠀⠀⠀⠀⠀ - ⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢸⠀⠀⠀⠀⠀ - ⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀2⠀⠀⠀⠀⠀ + ⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⡇⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢸⠀⠀⠀⠀⠀ + ⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⡇⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢸⠀⠀⠀⠀⠀ + ⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⡇⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢸⠀⠀⠀⠀⠀ ⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀ julia> Z = prime(tn1; sites=[]) ⊗ tn2; @@ -274,20 +274,20 @@ julia> @visualize Z; ⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀ ⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀ ⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀ - ⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀Z(1, 2)⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀ - ⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢀⠤⠊⠀⠈⠑⠢⢄⡀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀ - ⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢀⠔⠁⠀⠀⠀⠀⠀⠀⠀⠈⠑⠢2⡀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀ - ⠀⠀⠀⠀⠀⠀⠀⠀⣀⠔2⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠈⠑⠢⢄⡀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀ - ⠀⠀⠀⠀⠀⠀⡠⠊⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀Z(2, 2)⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀ - ⠀⠀Z(1, 1)⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢀⠔⠁⠀⠈⠑⠢⢄⡀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀ - ⠀⠀⠀⠀⠀⠀⠀⠀⠉⠢⢄⡀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⡠⠔⠁⠀⠀⠀⠀⠀⠀⠀⠈⠑⠢2⡀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀ - ⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀(2)'⢄⡀⠀⠀⠀⠀⠀⠀⠀⠀⡠2⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠈⠒⠤⣀⠀⠀⠀⠀⠀⠀⠀⠀ - ⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠈⠑⠢⢄⠀⠀⢀⠔⠊⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀Z(3, 2)⠀⠀ - ⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀Z(2, 1)⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⡠⠊⠁⠀⠀⠀⠀⠀ - ⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠈⠑⠢⢄⡀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀2⡠⠊⠀⠀⠀⠀⠀⠀⠀⠀ - ⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀(2)'⢄⡀⠀⠀⠀⠀⠀⠀⠀⢀⠔⠉⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀ - ⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠈⠑⠢⢄⡀⠀⢀⠔⠁⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀ - ⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀Z(3, 1)⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀ + ⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢀⣀Z(3, 1)⠀⠀ + ⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀Z(3, 2)⠤⠔⠒⠒⠒⠒2⠉⠉⠉⠉⠁⠀⣀⠔⠉⠀⠀⠀⠀⠀⠀ + ⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢸⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⣀⠔⠊⠀⠀⠀⠀⠀⠀⠀⠀⠀ + ⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⡸⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀(2)'⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀ + ⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⡇⠀⠀⠀⠀⠀⠀⠀⠀⢀⠤⠊⠁⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀ + ⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢀2⠀⠀⠀Z(2, 1)⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀ + ⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢀⣀⡠⢼⠔⠒⠊2⠥⠊⠁⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀ + ⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⣀⣀⡠⠤(2)'⠁⠀⠀⡜⢀⠤⠊⠁⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀ + ⠀⠀Z(1, 1)⠒⠒⠉⠉⠀⠀⠀⠀⠀⠀⠀⠀Z(2, 2)⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀ + ⠀⠀⠀⠀⠀⠑⡄⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢀⡠⠒⠁⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀ + ⠀⠀⠀⠀⠀⠀⠘⢄⠀⠀⠀⠀⠀⠀⠀⠀⠀2⠒⠁⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀ + ⠀⠀⠀⠀⠀⠀⠀2⢆⠀⠀⠀⠀⠀⡠⠔⠉⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀ + ⠀⠀⠀⠀⠀⠀⠀⠀⠀⢣⠀⡠⠔⠉⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀ + ⠀⠀⠀⠀⠀⠀⠀Z(1, 2)⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀ ⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀ ⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀ ⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀ @@ -305,20 +305,20 @@ julia> @visualize Z̃; ⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀ ⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀ ⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀ - ⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀Z̃(2, 1)⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀ - ⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀(2)'⠤⠤⠔⠒⠒⠉⠉⠀⠀⢱⠀⠈⠉⠑⠒⠢⠤⢄⣀2⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀ - ⠀⠀⠀⠀⠀⠀⠀⣀⣀⠤⠤⠔⠒⠊⠉⠉⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠈⡆⠀⠀⠀⠀⠀⠀⠀⠀⠀⠉⠉⠒⠒⠤⠤⢄⣀⡀⠀⠀⠀⠀⠀ - ⠀Z̃(3, 1)⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢱⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀Z̃(1, 2)⠀⠀ - ⠀⠀⠀⠀⠀⢸⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠈⡆⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢀⠔⠁⠀⠀⠀⠀⠀⠀⠀ - ⠀⠀⠀⠀⠀⠀⡇⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀2⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⣀⠔⠁⠀⠀⠀⠀⠀⠀⠀⠀⠀ - ⠀⠀⠀⠀⠀⠀⢸⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⡇⠀⠀⠀⠀⠀⠀⠀⡠2⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀ - ⠀⠀⠀⠀⠀⠀⠀⡇⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢸⠀⠀⠀⠀⠀⡠⠊⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀ - ⠀⠀⠀⠀⠀⠀⠀2⡀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⡇⠀⢀⠤⠊⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀ - ⠀⠀⠀⠀⠀⠀⠀⠀⢇⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀Z̃(2, 2)⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀ - ⠀⠀⠀⠀⠀⠀⠀⠀⠸⡀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢀⣀⡠⠤⠒⠊⠉⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀ - ⠀⠀⠀⠀⠀⠀⠀⠀⠀⢇⠀⠀⠀⠀⠀⠀⠀⠀⢀⣀⠤2⠒⠉⠁⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀ - ⠀⠀⠀⠀⠀⠀⠀⠀⠀⠸⡀⠀⣀⡠⠤⠒⠊⠉⠁⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀ - ⠀⠀⠀⠀⠀⠀Z̃(3, 2)⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀ + ⠀⠀⠀⠀⠀⠀⠀⠀⠀Z̃(3, 2)⣀⣀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀ + ⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢀⠜⠀⠀⠀⠀⠀⠀⠉⠉⠉⠉⠉⠑⠒⠒⠒⠒⠢2⠤⠤⠤⠤⣀⣀⣀⣀⣀⡀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀ + ⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢀⠎⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠈⠉Z̃(3, 1)⠀⠀ + ⠀⠀⠀⠀⠀⠀⠀⠀⠀⡠⠃⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⡠⠊⠀⠀⠀⠀⠀⠀ + ⠀⠀⠀⠀⠀⠀⠀⠀⡔2⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢠⠊⠀⠀⠀⠀⠀⠀⠀⠀ + ⠀⠀⠀⠀⠀⠀⢀⠜⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀(2)'⠀⠀⠀⠀⠀⠀⠀⠀⠀ + ⠀⠀⠀⠀⠀⢠⠊⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⡰⠁⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀ + ⠀Z̃(2, 2)⠤⠤⠤⠤⢄⣀⣀⣀⣀⣀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⡠⠊⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀ + ⠀⠀⠀⠀⠀⠀⠣⡀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠉2⠉⠉⠉⠒⠒⠒⠒⠒⠢⠤Z̃(2, 1)⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀ + ⠀⠀⠀⠀⠀⠀⠀⠑⡄⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢀⣀⠤⠒⠊⠁⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀ + ⠀⠀⠀⠀⠀⠀⠀⠀2⢄⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢀⡠⠤⠒⠉⠁⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀ + ⠀⠀⠀⠀⠀⠀⠀⠀⠀⠈⢢⠀⠀⠀⠀⠀⠀⠀⠀⣀⡠⠤⠒⠉2⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀ + ⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠱⡀⠀⣀⡠⠔⠒⠉⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀ + ⠀⠀⠀⠀⠀⠀⠀⠀Z̃(1, 2)⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀ ⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀ ⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀ ⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀ diff --git a/examples/test.jl b/examples/test.jl new file mode 100644 index 00000000..6bf1bfcf --- /dev/null +++ b/examples/test.jl @@ -0,0 +1,21 @@ +using ITensorNetworks: IndsNetwork, siteinds, ttn +using ITensorNetworks.ModelHamiltonians: ising +using ITensors: Index, OpSum, terms, sites +using NamedGraphs.NamedGraphGenerators: named_grid +using NamedGraphs.GraphsExtensions: rem_vertex + +function filter_terms(H, verts) + H_new = OpSum() + for term in terms(H) + if isempty(filter(v -> v ∈ verts, sites(term))) + H_new += term + end + end + return H_new +end + +g = named_grid((8,1)) +s = siteinds("S=1/2", g) +H = ising(s) +H_mod = filter_terms(H, [(4,1)]) +ttno = ttn(H_mod, s) \ No newline at end of file diff --git a/src/abstractitensornetwork.jl b/src/abstractitensornetwork.jl index 6f6ee164..73c915a1 100644 --- a/src/abstractitensornetwork.jl +++ b/src/abstractitensornetwork.jl @@ -7,6 +7,7 @@ using Graphs: add_edge!, add_vertex!, bfs_tree, + center, dst, edges, edgetype, @@ -18,6 +19,7 @@ using Graphs: using ITensors: ITensors, ITensor, + @Algorithm_str, addtags, combiner, commoninds, @@ -40,10 +42,10 @@ using ITensorMPS: ITensorMPS, add, linkdim, linkinds, siteinds using .ITensorsExtensions: ITensorsExtensions, indtype, promote_indtype using LinearAlgebra: LinearAlgebra, factorize using MacroTools: @capture -using NamedGraphs: NamedGraphs, NamedGraph, not_implemented +using NamedGraphs: NamedGraphs, NamedGraph, not_implemented, steiner_tree using NamedGraphs.GraphsExtensions: ⊔, directed_graph, incident_edges, rename_vertices, vertextype -using NDTensors: NDTensors, dim +using NDTensors: NDTensors, dim, Algorithm using SplitApplyCombine: flatten abstract type AbstractITensorNetwork{V} <: AbstractDataGraph{V,ITensor,ITensor} end @@ -584,7 +586,9 @@ function LinearAlgebra.factorize(tn::AbstractITensorNetwork, edge::Pair; kwargs. end # For ambiguity error; TODO: decide whether to use graph mutating methods when resulting graph is unchanged? -function _orthogonalize_edge(tn::AbstractITensorNetwork, edge::AbstractEdge; kwargs...) +function gauge_edge( + alg::Algorithm"orthogonalize", tn::AbstractITensorNetwork, edge::AbstractEdge; kwargs... +) # tn = factorize(tn, edge; kwargs...) # # TODO: Implement as `only(common_neighbors(tn, src(edge), dst(edge)))` # new_vertex = only(neighbors(tn, src(edge)) ∩ neighbors(tn, dst(edge))) @@ -598,23 +602,43 @@ function _orthogonalize_edge(tn::AbstractITensorNetwork, edge::AbstractEdge; kwa return tn end -function ITensorMPS.orthogonalize(tn::AbstractITensorNetwork, edge::AbstractEdge; kwargs...) - return _orthogonalize_edge(tn, edge; kwargs...) +# For ambiguity error; TODO: decide whether to use graph mutating methods when resulting graph is unchanged? +function gauge_walk( + alg::Algorithm, tn::AbstractITensorNetwork, edges::Vector{<:AbstractEdge}; kwargs... +) + tn = copy(tn) + for edge in edges + tn = gauge_edge(alg, tn, edge; kwargs...) + end + return tn +end + +function gauge_walk(alg::Algorithm, tn::AbstractITensorNetwork, edge::Pair; kwargs...) + return gauge_edge(alg::Algorithm, tn, edgetype(tn)(edge); kwargs...) end -function ITensorMPS.orthogonalize(tn::AbstractITensorNetwork, edge::Pair; kwargs...) - return orthogonalize(tn, edgetype(tn)(edge); kwargs...) +function gauge_walk( + alg::Algorithm, tn::AbstractITensorNetwork, edges::Vector{<:Pair}; kwargs... +) + return gauge_walk(alg, tn, edgetype(tn).(edges); kwargs...) end -# Orthogonalize an ITensorNetwork towards a source vertex, treating +# Gauge a ITensorNetwork towards a region, treating # the network as a tree spanned by a spanning tree. -# TODO: Rename `tree_orthogonalize`. -function ITensorMPS.orthogonalize(ψ::AbstractITensorNetwork, source_vertex) - spanning_tree_edges = post_order_dfs_edges(bfs_tree(ψ, source_vertex), source_vertex) - for e in spanning_tree_edges - ψ = orthogonalize(ψ, e) - end - return ψ +function tree_gauge(alg::Algorithm, ψ::AbstractITensorNetwork, region::Vector) + region_center = + length(region) != 1 ? first(center(steiner_tree(ψ, region))) : only(region) + path = post_order_dfs_edges(bfs_tree(ψ, region_center), region_center) + path = filter(e -> !((src(e) ∈ region) && (dst(e) ∈ region)), path) + return gauge_walk(alg, ψ, path) +end + +function tree_gauge(alg::Algorithm, ψ::AbstractITensorNetwork, region) + return tree_gauge(alg, ψ, [region]) +end + +function tree_orthogonalize(ψ::AbstractITensorNetwork, region; kwargs...) + return tree_gauge(Algorithm("orthogonalize"), ψ, region; kwargs...) end # TODO: decide whether to use graph mutating methods when resulting graph is unchanged? @@ -759,7 +783,7 @@ end # Link dimensions # -function ITensors.maxlinkdim(tn::AbstractITensorNetwork) +function ITensorMPS.maxlinkdim(tn::AbstractITensorNetwork) md = 1 for e in edges(tn) md = max(md, linkdim(tn, e)) diff --git a/src/apply.jl b/src/apply.jl index d38f04f9..6a55f45f 100644 --- a/src/apply.jl +++ b/src/apply.jl @@ -200,7 +200,7 @@ function ITensors.apply( v⃗ = neighbor_vertices(ψ, o) if length(v⃗) == 1 if ortho - ψ = orthogonalize(ψ, v⃗[1]) + ψ = tree_orthogonalize(ψ, v⃗[1]) end oψᵥ = apply(o, ψ[v⃗[1]]) if normalize @@ -215,7 +215,7 @@ function ITensors.apply( error("Vertices where the gates are being applied must be neighbors for now.") end if ortho - ψ = orthogonalize(ψ, v⃗[1]) + ψ = tree_orthogonalize(ψ, v⃗[1]) end if variational_optimization_only || !is_product_env ψᵥ₁, ψᵥ₂ = full_update_bp( diff --git a/src/caches/beliefpropagationcache.jl b/src/caches/beliefpropagationcache.jl index d4c73ed0..787438df 100644 --- a/src/caches/beliefpropagationcache.jl +++ b/src/caches/beliefpropagationcache.jl @@ -16,7 +16,6 @@ using NDTensors: NDTensors default_message(elt, inds_e) = ITensor[denseblocks(delta(elt, i)) for i in inds_e] default_messages(ptn::PartitionedGraph) = Dictionary() -default_message_norm(m::ITensor) = norm(m) function default_message_update(contract_list::Vector{ITensor}; normalize=true, kwargs...) sequence = optimal_contraction_sequence(contract_list) updated_messages = contract(contract_list; sequence, kwargs...) @@ -107,7 +106,7 @@ end function message(bp_cache::BeliefPropagationCache, edge::PartitionEdge) mts = messages(bp_cache) - return get(mts, edge, default_message(bp_cache, edge)) + return get(() -> default_message(bp_cache, edge), mts, edge) end function messages(bp_cache::BeliefPropagationCache, edges; kwargs...) return map(edge -> message(bp_cache, edge; kwargs...), edges) @@ -153,24 +152,16 @@ end function environment(bp_cache::BeliefPropagationCache, verts::Vector) partition_verts = partitionvertices(bp_cache, verts) messages = environment(bp_cache, partition_verts) - central_tensors = ITensor[ - tensornetwork(bp_cache)[v] for v in setdiff(vertices(bp_cache, partition_verts), verts) - ] + central_tensors = factors(bp_cache, setdiff(vertices(bp_cache, partition_verts), verts)) return vcat(messages, central_tensors) end -function factors(bp_cache::BeliefPropagationCache, vertices) - tn = tensornetwork(bp_cache) - return map(vertex -> tn[vertex], vertices) -end - -function factor(bp_cache::BeliefPropagationCache, vertex) - return only(factors(bp_cache, [vertex])) +function factors(bp_cache::BeliefPropagationCache, verts::Vector) + return ITensor[tensornetwork(bp_cache)[v] for v in verts] end function factor(bp_cache::BeliefPropagationCache, vertex::PartitionVertex) - ptn = partitioned_tensornetwork(bp_cache) - return collect(eachtensor(subgraph(ptn, vertex))) + return factors(bp_cache, vertices(bp_cache, vertex)) end """ diff --git a/src/inner.jl b/src/inner.jl index 166a2c6f..43486703 100644 --- a/src/inner.jl +++ b/src/inner.jl @@ -1,4 +1,5 @@ -using ITensors: inner, scalar, loginner +using ITensors: inner, scalar +using ITensorMPS: ITensorMPS, loginner using LinearAlgebra: norm, norm_sqr default_contract_alg(tns::Tuple) = "bp" @@ -53,7 +54,7 @@ function ITensors.inner( return scalar(tn; sequence) end -function ITensors.loginner( +function ITensorMPS.loginner( ϕ::AbstractITensorNetwork, ψ::AbstractITensorNetwork; alg=default_contract_alg((ϕ, ψ)), @@ -62,7 +63,7 @@ function ITensors.loginner( return loginner(Algorithm(alg), ϕ, ψ; kwargs...) end -function ITensors.loginner( +function ITensorMPS.loginner( ϕ::AbstractITensorNetwork, A::AbstractITensorNetwork, ψ::AbstractITensorNetwork; @@ -72,13 +73,13 @@ function ITensors.loginner( return loginner(Algorithm(alg), ϕ, A, ψ; kwargs...) end -function ITensors.loginner( +function ITensorMPS.loginner( alg::Algorithm"exact", ϕ::AbstractITensorNetwork, ψ::AbstractITensorNetwork; kwargs... ) return log(inner(alg, ϕ, ψ); kwargs...) end -function ITensors.loginner( +function ITensorMPS.loginner( alg::Algorithm"exact", ϕ::AbstractITensorNetwork, A::AbstractITensorNetwork, @@ -88,7 +89,7 @@ function ITensors.loginner( return log(inner(alg, ϕ, A, ψ); kwargs...) end -function ITensors.loginner( +function ITensorMPS.loginner( alg::Algorithm"bp", ϕ::AbstractITensorNetwork, ψ::AbstractITensorNetwork; @@ -99,7 +100,7 @@ function ITensors.loginner( return logscalar(alg, tn; kwargs...) end -function ITensors.loginner( +function ITensorMPS.loginner( alg::Algorithm"bp", ϕ::AbstractITensorNetwork, A::AbstractITensorNetwork, diff --git a/src/solvers/alternating_update/alternating_update.jl b/src/solvers/alternating_update/alternating_update.jl index 2cd5de71..750f3f36 100644 --- a/src/solvers/alternating_update/alternating_update.jl +++ b/src/solvers/alternating_update/alternating_update.jl @@ -9,8 +9,8 @@ function alternating_update( nsites, # define default for each level of solver implementation updater, # this specifies the update performed locally outputlevel=default_outputlevel(), - region_printer=nothing, - sweep_printer=nothing, + region_printer=default_region_printer, + sweep_printer=default_sweep_printer, (sweep_observer!)=nothing, (region_observer!)=nothing, root_vertex=GraphsExtensions.default_root_vertex(init_state), @@ -59,7 +59,7 @@ function alternating_update( (sweep_observer!)=nothing, sweep_printer=default_sweep_printer,#? (region_observer!)=nothing, - region_printer=nothing, + region_printer=default_region_printer, ) state = copy(init_state) @assert !isnothing(sweep_plans) diff --git a/src/solvers/alternating_update/region_update.jl b/src/solvers/alternating_update/region_update.jl index b92adc8c..c741c82a 100644 --- a/src/solvers/alternating_update/region_update.jl +++ b/src/solvers/alternating_update/region_update.jl @@ -1,44 +1,3 @@ -#ToDo: generalize beyond 2-site -#ToDo: remove concept of orthogonality center for generality -function current_ortho(sweep_plan, which_region_update) - regions = first.(sweep_plan) - region = regions[which_region_update] - current_verts = support(region) - if !isa(region, AbstractEdge) && length(region) == 1 - return only(current_verts) - end - if which_region_update == length(regions) - # look back by one should be sufficient, but may be brittle? - overlapping_vertex = only( - intersect(current_verts, support(regions[which_region_update - 1])) - ) - return overlapping_vertex - else - # look forward - other_regions = filter( - x -> !(issetequal(x, current_verts)), support.(regions[(which_region_update + 1):end]) - ) - # find the first region that has overlapping support with current region - ind = findfirst(x -> !isempty(intersect(support(x), support(region))), other_regions) - if isnothing(ind) - # look backward - other_regions = reverse( - filter( - x -> !(issetequal(x, current_verts)), - support.(regions[1:(which_region_update - 1)]), - ), - ) - ind = findfirst(x -> !isempty(intersect(support(x), support(region))), other_regions) - end - @assert !isnothing(ind) - future_verts = union(support(other_regions[ind])) - # return ortho_ceter as the vertex in current region that does not overlap with following one - overlapping_vertex = intersect(current_verts, future_verts) - nonoverlapping_vertex = only(setdiff(current_verts, overlapping_vertex)) - return nonoverlapping_vertex - end -end - function region_update( projected_operator, state; @@ -64,14 +23,13 @@ function region_update( # ToDo: remove orthogonality center on vertex for generality # region carries same information - ortho_vertex = current_ortho(sweep_plan, which_region_update) if !isnothing(transform_operator) projected_operator = transform_operator( state, projected_operator; outputlevel, transform_operator_kwargs... ) end state, projected_operator, phi = extracter( - state, projected_operator, region, ortho_vertex; extracter_kwargs..., internal_kwargs + state, projected_operator, region; extracter_kwargs..., internal_kwargs ) # create references, in case solver does (out-of-place) modify PH or state state! = Ref(state) @@ -97,9 +55,8 @@ function region_update( # drho = noise * noiseterm(PH, phi, ortho) # TODO: actually implement this for trees... # so noiseterm is a solver #end - state, spec = inserter( - state, phi, region, ortho_vertex; inserter_kwargs..., internal_kwargs - ) + #if isa(region, AbstractEdge) && + state, spec = inserter(state, phi, region; inserter_kwargs..., internal_kwargs) all_kwargs = (; which_region_update, sweep_plan, diff --git a/src/solvers/contract.jl b/src/solvers/contract.jl index 1e6ddeec..0fff3894 100644 --- a/src/solvers/contract.jl +++ b/src/solvers/contract.jl @@ -1,5 +1,6 @@ using Graphs: nv, vertices -using ITensors: ITensors, linkinds, sim +using ITensors: ITensors, sim +using ITensorMPS: linkinds using ITensors.NDTensors: Algorithm, @Algorithm_str, contract using NamedGraphs: vertextype diff --git a/src/solvers/defaults.jl b/src/solvers/defaults.jl index b5d315ff..09c2ae2f 100644 --- a/src/solvers/defaults.jl +++ b/src/solvers/defaults.jl @@ -1,4 +1,4 @@ -using Printf: @printf +using Printf: @printf, @sprintf using ITensorMPS: maxlinkdim default_outputlevel() = 0 default_nsites() = 2 @@ -7,10 +7,12 @@ default_extracter() = default_extracter default_inserter() = default_inserter default_checkdone() = (; kws...) -> false default_transform_operator() = nothing + +format(x) = @sprintf("%s", x) +format(x::AbstractFloat) = @sprintf("%.1E", x) + function default_region_printer(; - cutoff, - maxdim, - mindim, + inserter_kwargs, outputlevel, state, sweep_plan, @@ -23,9 +25,11 @@ function default_region_printer(; region = first(sweep_plan[which_region_update]) @printf("Sweep %d, region=%s \n", which_sweep, region) print(" Truncated using") - @printf(" cutoff=%.1E", cutoff) - @printf(" maxdim=%d", maxdim) - @printf(" mindim=%d", mindim) + for key in [:cutoff, :maxdim, :mindim] + if haskey(inserter_kwargs, key) + print(" ", key, "=", format(inserter_kwargs[key])) + end + end println() if spec != nothing @printf( diff --git a/src/solvers/extract/extract.jl b/src/solvers/extract/extract.jl index feb57c2f..1013d1bd 100644 --- a/src/solvers/extract/extract.jl +++ b/src/solvers/extract/extract.jl @@ -7,18 +7,20 @@ # insert_local_tensors takes that tensor and factorizes it back # apart and puts it back into the network. # -function default_extracter(state, projected_operator, region, ortho; internal_kwargs) - state = orthogonalize(state, ortho) + +function default_extracter(state, projected_operator, region; internal_kwargs) if isa(region, AbstractEdge) - other_vertex = only(setdiff(support(region), [ortho])) - left_inds = uniqueinds(state[ortho], state[other_vertex]) - #ToDo: replace with call to factorize + # TODO: add functionality for orthogonalizing onto a bond so that can be called instead + vsrc, vdst = src(region), dst(region) + state = orthogonalize(state, vsrc) + left_inds = uniqueinds(state[vsrc], state[vdst]) U, S, V = svd( - state[ortho], left_inds; lefttags=tags(state, region), righttags=tags(state, region) + state[vsrc], left_inds; lefttags=tags(state, region), righttags=tags(state, region) ) - state[ortho] = U + state[vsrc] = U local_tensor = S * V else + state = orthogonalize(state, region) local_tensor = prod(state[v] for v in region) end projected_operator = position(projected_operator, state, region) diff --git a/src/solvers/insert/insert.jl b/src/solvers/insert/insert.jl index 11aed223..01fb35bd 100644 --- a/src/solvers/insert/insert.jl +++ b/src/solvers/insert/insert.jl @@ -6,8 +6,7 @@ function default_inserter( state::AbstractTTN, phi::ITensor, - region, - ortho_vert; + region; normalize=false, maxdim=nothing, mindim=nothing, @@ -16,16 +15,14 @@ function default_inserter( ) state = copy(state) spec = nothing - other_vertex = setdiff(support(region), [ortho_vert]) - if !isempty(other_vertex) - v = only(other_vertex) - e = edgetype(state)(ortho_vert, v) - indsTe = inds(state[ortho_vert]) + if length(region) == 2 + v = last(region) + e = edgetype(state)(first(region), last(region)) + indsTe = inds(state[first(region)]) L, phi, spec = factorize(phi, indsTe; tags=tags(state, e), maxdim, mindim, cutoff) - state[ortho_vert] = L - + state[first(region)] = L else - v = ortho_vert + v = only(region) end state[v] = phi state = set_ortho_region(state, [v]) @@ -36,16 +33,14 @@ end function default_inserter( state::AbstractTTN, phi::ITensor, - region::NamedEdge, - ortho; + region::NamedEdge; cutoff=nothing, maxdim=nothing, mindim=nothing, normalize=false, internal_kwargs, ) - v = only(setdiff(support(region), [ortho])) - state[v] *= phi - state = set_ortho_region(state, [v]) + state[dst(region)] *= phi + state = set_ortho_region(state, [dst(region)]) return state, nothing end diff --git a/src/solvers/sweep_plans/sweep_plans.jl b/src/solvers/sweep_plans/sweep_plans.jl index 69221995..52915e2b 100644 --- a/src/solvers/sweep_plans/sweep_plans.jl +++ b/src/solvers/sweep_plans/sweep_plans.jl @@ -13,10 +13,11 @@ end support(r) = r -function reverse_region(edges, which_edge; nsites=1, region_kwargs=(;)) +function reverse_region(edges, which_edge; reverse_edge=false, nsites=1, region_kwargs=(;)) current_edge = edges[which_edge] if nsites == 1 - return [(current_edge, region_kwargs)] + !reverse_edge && return [(current_edge, region_kwargs)] + reverse_edge && return [(reverse(current_edge), region_kwargs)] elseif nsites == 2 if last(edges) == current_edge return () @@ -62,25 +63,24 @@ function forward_sweep( dir::Base.ForwardOrdering, graph::AbstractGraph; root_vertex=GraphsExtensions.default_root_vertex(graph), + reverse_edges=false, region_kwargs, reverse_kwargs=region_kwargs, reverse_step=false, kwargs..., ) edges = post_order_dfs_edges(graph, root_vertex) - regions = collect( - flatten(map(i -> forward_region(edges, i; region_kwargs, kwargs...), eachindex(edges))) - ) - + regions = map(eachindex(edges)) do i + forward_region(edges, i; region_kwargs, kwargs...) + end + regions = collect(flatten(regions)) if reverse_step - reverse_regions = collect( - flatten( - map( - i -> reverse_region(edges, i; region_kwargs=reverse_kwargs, kwargs...), - eachindex(edges), - ), - ), - ) + reverse_regions = map(eachindex(edges)) do i + reverse_region( + edges, i; reverse_edge=reverse_edges, region_kwargs=reverse_kwargs, kwargs... + ) + end + reverse_regions = collect(flatten(reverse_regions)) _check_reverse_sweeps(regions, reverse_regions, graph; kwargs...) regions = interleave(regions, reverse_regions) end @@ -90,7 +90,7 @@ end #ToDo: is there a better name for this? unidirectional_sweep? traversal? function forward_sweep(dir::Base.ReverseOrdering, args...; kwargs...) - return reverse(forward_sweep(Base.Forward, args...; kwargs...)) + return reverse(forward_sweep(Base.Forward, args...; reverse_edges=true, kwargs...)) end function default_sweep_plans( diff --git a/src/tebd.jl b/src/tebd.jl index edf5a188..d1d96017 100644 --- a/src/tebd.jl +++ b/src/tebd.jl @@ -23,7 +23,7 @@ function tebd( ψ = apply(u⃗, ψ; cutoff, maxdim, normalize=true, ortho, kwargs...) if ortho for v in vertices(ψ) - ψ = orthogonalize(ψ, v) + ψ = tree_orthogonalize(ψ, v) end end end diff --git a/src/treetensornetworks/abstracttreetensornetwork.jl b/src/treetensornetworks/abstracttreetensornetwork.jl index c8dccb1f..f6c8f49f 100644 --- a/src/treetensornetworks/abstracttreetensornetwork.jl +++ b/src/treetensornetworks/abstracttreetensornetwork.jl @@ -1,9 +1,15 @@ using Graphs: has_vertex using NamedGraphs.GraphsExtensions: - GraphsExtensions, edge_path, leaf_vertices, post_order_dfs_edges, post_order_dfs_vertices + GraphsExtensions, + edge_path, + leaf_vertices, + post_order_dfs_edges, + post_order_dfs_vertices, + a_star +using NamedGraphs: namedgraph_a_star, steiner_tree using IsApprox: IsApprox, Approx -using ITensors: @Algorithm_str, directsum, hasinds, permute, plev -using ITensorMPS: linkind, loginner, lognorm, orthogonalize +using ITensors: ITensors, Algorithm, @Algorithm_str, directsum, hasinds, permute, plev +using ITensorMPS: ITensorMPS, linkind, loginner, lognorm, orthogonalize using TupleTools: TupleTools abstract type AbstractTreeTensorNetwork{V} <: AbstractITensorNetwork{V} end @@ -29,30 +35,27 @@ function set_ortho_region(tn::AbstractTTN, new_region) return error("Not implemented") end -# -# Orthogonalization -# - -function ITensorMPS.orthogonalize(tn::AbstractTTN, ortho_center; kwargs...) - if isone(length(ortho_region(tn))) && ortho_center == only(ortho_region(tn)) - return tn - end - # TODO: Rewrite this in a more general way. - if isone(length(ortho_region(tn))) - edge_list = edge_path(tn, only(ortho_region(tn)), ortho_center) - else - edge_list = post_order_dfs_edges(tn, ortho_center) - end - for e in edge_list - tn = orthogonalize(tn, e) +function gauge(alg::Algorithm, ttn::AbstractTTN, region::Vector; kwargs...) + issetequal(region, ortho_region(ttn)) && return ttn + st = steiner_tree(ttn, union(region, ortho_region(ttn))) + path = post_order_dfs_edges(st, first(region)) + path = filter(e -> !((src(e) ∈ region) && (dst(e) ∈ region)), path) + if !isempty(path) + ttn = typeof(ttn)(gauge_walk(alg, ITensorNetwork(ttn), path; kwargs...)) end - return set_ortho_region(tn, typeof(ortho_region(tn))([ortho_center])) + return set_ortho_region(ttn, region) end -# For ambiguity error +function gauge(alg::Algorithm, ttn::AbstractTTN, region; kwargs...) + return gauge(alg, ttn, [region]; kwargs...) +end + +function ITensorMPS.orthogonalize(ttn::AbstractTTN, region; kwargs...) + return gauge(Algorithm("orthogonalize"), ttn, region; kwargs...) +end -function ITensorMPS.orthogonalize(tn::AbstractTTN, edge::AbstractEdge; kwargs...) - return typeof(tn)(orthogonalize(ITensorNetwork(tn), edge; kwargs...)) +function tree_orthogonalize(ttn::AbstractTTN, args...; kwargs...) + return orthogonalize(ttn, args...; kwargs...) end # @@ -273,13 +276,13 @@ end Base.:+(tn::AbstractTTN) = tn -ITensors.add(tns::AbstractTTN...; kwargs...) = +(tns...; kwargs...) +ITensorMPS.add(tns::AbstractTTN...; kwargs...) = +(tns...; kwargs...) function Base.:-(tn1::AbstractTTN, tn2::AbstractTTN; kwargs...) return +(tn1, -tn2; kwargs...) end -function ITensors.add(tn1::AbstractTTN, tn2::AbstractTTN; kwargs...) +function ITensorMPS.add(tn1::AbstractTTN, tn2::AbstractTTN; kwargs...) return +(tn1, tn2; kwargs...) end diff --git a/test/Project.toml b/test/Project.toml index 70eb14d3..923e3bbe 100644 --- a/test/Project.toml +++ b/test/Project.toml @@ -21,7 +21,7 @@ NDTensors = "23ae76d9-e61a-49c4-8f12-3f1a16adf9cf" NamedGraphs = "678767b0-92e7-4007-89e4-4527a8725b19" OMEinsumContractionOrders = "6f22d1fd-8eed-4bb7-9776-e7d684900715" Observers = "338f10d5-c7f1-4033-a7d1-f9dec39bcaa0" -OrdinaryDiffEq = "1dea7af3-3e70-54e6-95c3-0bf5283fa5ed" +OrdinaryDiffEqTsit5 = "b1df2697-797e-41e3-8120-5422d3b24e4a" Pkg = "44cfe95a-1eb2-52ea-b672-e2afdf69b78f" Random = "9a3f8284-a2c9-5f02-9a11-845980a1fd5c" SplitApplyCombine = "03a91e81-4c3e-53e1-a0a4-9c0c8f19dd66" diff --git a/test/test_itensornetwork.jl b/test/test_itensornetwork.jl index ba3caa01..53e2928f 100644 --- a/test/test_itensornetwork.jl +++ b/test/test_itensornetwork.jl @@ -51,6 +51,7 @@ using ITensorNetworks: orthogonalize, random_tensornetwork, siteinds, + tree_orthogonalize, ttn using LinearAlgebra: factorize using NamedGraphs: NamedEdge @@ -287,13 +288,13 @@ const elts = (Float32, Float64, Complex{Float32}, Complex{Float64}) @test nv(tn_ortho) == 5 @test nv(tn) == 4 @test Z ≈ Z̃ - tn_ortho = orthogonalize(tn, 4 => 3) + tn_ortho = tree_orthogonalize(tn, [3, 4]) Z̃ = norm_sqr(tn_ortho) @test nv(tn_ortho) == 4 @test nv(tn) == 4 @test Z ≈ Z̃ - tn_ortho = orthogonalize(tn, 1) + tn_ortho = tree_orthogonalize(tn, 1) Z̃ = norm_sqr(tn_ortho) @test Z ≈ Z̃ Z̃ = inner(tn_ortho, tn) diff --git a/test/test_treetensornetworks/test_solvers/ITensorNetworksTestSolversUtils/solvers.jl b/test/test_treetensornetworks/test_solvers/ITensorNetworksTestSolversUtils/solvers.jl index 82924f74..9b9568be 100644 --- a/test/test_treetensornetworks/test_solvers/ITensorNetworksTestSolversUtils/solvers.jl +++ b/test/test_treetensornetworks/test_solvers/ITensorNetworksTestSolversUtils/solvers.jl @@ -1,7 +1,7 @@ -using OrdinaryDiffEq: ODEProblem, Tsit5, solve -using ITensors: ITensor using ITensorNetworks: TimeDependentSum, to_vec +using ITensors: ITensor using KrylovKit: exponentiate +using OrdinaryDiffEqTsit5: ODEProblem, Tsit5, solve function ode_solver( H::TimeDependentSum, diff --git a/test/test_treetensornetworks/test_solvers/Project.toml b/test/test_treetensornetworks/test_solvers/Project.toml index 77225041..e4716249 100644 --- a/test/test_treetensornetworks/test_solvers/Project.toml +++ b/test/test_treetensornetworks/test_solvers/Project.toml @@ -7,7 +7,8 @@ ITensors = "9136182c-28ba-11e9-034c-db9fb085ebd5" KrylovKit = "0b1a1467-8014-51b9-945f-bf0ae24f4b77" NamedGraphs = "678767b0-92e7-4007-89e4-4527a8725b19" Observers = "338f10d5-c7f1-4033-a7d1-f9dec39bcaa0" -OrdinaryDiffEq = "1dea7af3-3e70-54e6-95c3-0bf5283fa5ed" +OrdinaryDiffEqTsit5 = "b1df2697-797e-41e3-8120-5422d3b24e4a" Random = "9a3f8284-a2c9-5f02-9a11-845980a1fd5c" StableRNGs = "860ef19b-820b-49d6-a774-d7a799459cd3" +Suppressor = "fd094767-a336-5f1f-9728-57cf17d0bbfb" Test = "8dfed614-e22c-5e08-85e1-65c5234f0b40" diff --git a/test/test_treetensornetworks/test_solvers/test_dmrg.jl b/test/test_treetensornetworks/test_solvers/test_dmrg.jl index b352d43c..004ec561 100644 --- a/test/test_treetensornetworks/test_solvers/test_dmrg.jl +++ b/test/test_treetensornetworks/test_solvers/test_dmrg.jl @@ -1,7 +1,7 @@ @eval module $(gensym()) using DataGraphs: edge_data, vertex_data using Dictionaries: Dictionary -using Graphs: nv, vertices +using Graphs: nv, vertices, uniform_tree using ITensorMPS: ITensorMPS using ITensorNetworks: ITensorNetworks, @@ -19,9 +19,11 @@ using ITensorNetworks.ITensorsExtensions: replace_vertices using ITensorNetworks.ModelHamiltonians: ModelHamiltonians using ITensors: ITensors using KrylovKit: eigsolve +using NamedGraphs: NamedGraph, rename_vertices using NamedGraphs.NamedGraphGenerators: named_comb_tree using Observers: observer using StableRNGs: StableRNG +using Suppressor: @capture_out using Test: @test, @test_broken, @testset # This is needed since `eigen` is broken @@ -76,6 +78,31 @@ ITensors.disable_auto_fermion() new_E = inner(psi', H, psi) @test new_E ≈ orig_E =# + + # + # Test outputlevels are working + # + prev_output = "" + for outputlevel in 0:2 + output = @capture_out begin + e, psi = dmrg( + H, + psi; + outputlevel, + nsweeps, + maxdim, + cutoff, + nsites, + updater_kwargs=(; krylovdim=3, maxiter=1), + ) + end + if outputlevel == 0 + @test length(output) == 0 + else + @test length(output) > length(prev_output) + end + prev_output = output + end end @testset "Observers" begin @@ -139,7 +166,7 @@ end nsweeps, maxdim, cutoff, - outputlevel=2, + outputlevel=0, transform_operator=ITensorNetworks.cache_operator_to_disk, transform_operator_kwargs=(; write_when_maxdim_exceeds=11), ) @@ -287,11 +314,12 @@ end nsites = 2 nsweeps = 10 - c = named_comb_tree((3, 2)) - s = siteinds("S=1/2", c) - os = ModelHamiltonians.heisenberg(c) - H = ttn(os, s) rng = StableRNG(1234) + g = NamedGraph(uniform_tree(10)) + g = rename_vertices(v -> (v, 1), g) + s = siteinds("S=1/2", g) + os = ModelHamiltonians.heisenberg(g) + H = ttn(os, s) psi = random_ttn(rng, s; link_space=5) e, psi = dmrg(H, psi; nsweeps, maxdim, nsites) diff --git a/test/test_treetensornetworks/test_solvers/test_tdvp_time_dependent.jl b/test/test_treetensornetworks/test_solvers/test_tdvp_time_dependent.jl index 17f1cc71..4101bc83 100644 --- a/test/test_treetensornetworks/test_solvers/test_tdvp_time_dependent.jl +++ b/test/test_treetensornetworks/test_solvers/test_tdvp_time_dependent.jl @@ -1,12 +1,12 @@ @eval module $(gensym()) -using ITensors: contract using ITensorNetworks: ITensorNetworks, TimeDependentSum, ttn, mpo, mps, siteinds, tdvp using ITensorNetworks.ModelHamiltonians: ModelHamiltonians -using OrdinaryDiffEq: Tsit5 +using ITensors: contract using KrylovKit: exponentiate using LinearAlgebra: norm using NamedGraphs: AbstractNamedEdge using NamedGraphs.NamedGraphGenerators: named_comb_tree +using OrdinaryDiffEqTsit5: Tsit5 using Test: @test, @test_broken, @testset include(