From 4dbe195c01610fecec3ad187a6ebbe2ed07248b6 Mon Sep 17 00:00:00 2001 From: Daniel Cerkoney Date: Wed, 18 Sep 2024 16:36:28 -0400 Subject: [PATCH] Improve graph string representations and bugfix for `typestr` in function `_stringrep` to correctly display subgraph IDs --- src/computational_graph/ComputationalGraph.jl | 6 +- src/computational_graph/io.jl | 77 +++++++++++++------ src/computational_graph/tree_properties.jl | 2 +- src/quantum_operator/expression.jl | 3 +- src/quantum_operator/operator.jl | 3 +- test/computational_graph.jl | 67 +++++++++++----- test/deprecated.jl | 4 +- test/quantum_operator.jl | 17 +++- 8 files changed, 127 insertions(+), 52 deletions(-) diff --git a/src/computational_graph/ComputationalGraph.jl b/src/computational_graph/ComputationalGraph.jl index 32b9bd67..f0f8820a 100644 --- a/src/computational_graph/ComputationalGraph.jl +++ b/src/computational_graph/ComputationalGraph.jl @@ -37,13 +37,13 @@ export multi_product, linear_combination, feynman_diagram, propagator, interacti # export 𝐺ᢠ, 𝐺ᡇ, 𝐺ᡠ, π‘Š, Green2, Interaction -include("tree_properties.jl") -export haschildren, onechild, isleaf, isbranch, ischain, has_zero_subfactors, eldest, count_operation - include("operation.jl") include("io.jl") export plot_tree +include("tree_properties.jl") +export haschildren, onechild, isleaf, isbranch, ischain, has_zero_subfactors, eldest, count_operation + include("eval.jl") export eval! diff --git a/src/computational_graph/io.jl b/src/computational_graph/io.jl index 4f35fa4c..f2b23684 100644 --- a/src/computational_graph/io.jl +++ b/src/computational_graph/io.jl @@ -1,8 +1,13 @@ -function _ops_to_str(ops::Vector{OperatorProduct}) - strs = ["$(o)" for o in ops] +function _ops_to_string(ops::Vector{OperatorProduct}) + strs = [string(o) for o in ops] return join(strs, "|") end +function _ops_to_repr(ops::Vector{OperatorProduct}) + reprs = [repr(o) for o in ops] + return join(reprs, "|") +end + function short(factor, ignore=nothing) if isnothing(ignore) == false && applicable(isapprox, factor, ignore) && factor β‰ˆ ignore return "" @@ -25,31 +30,65 @@ function short_orders(orders) end return orders_no_trailing_zeros end + +function _namestring(graph::AbstractGraph) + return isnothing(name(graph)) ? "" : string(name(graph)) +end -function _namestr(graph::AbstractGraph) - return isempty(name(graph)) ? "" : "-$(name(graph))" +function _namestring(graph::FeynmanGraph) + return isempty(name(graph)) ? "" : string(name(graph), ", ") end function _idstring(graph::AbstractGraph) - return string(id(graph), _namestr(graph)) + return _namestring(graph) end function _idstring(graph::FeynmanGraph) - return string(id(graph), _namestr(graph), ":", _ops_to_str(vertices(graph))) + return string(_namestring(graph), _ops_to_string(vertices(graph))) end -function _stringrep(graph::AbstractGraph, color=true) - idstr = _idstring(graph) - properties = graph.properties - wstr = short(weight(graph)) - ostr = short_orders(orders(graph)) - # =$(node.weight*(2Ο€)^(3*node.id.para.innerLoopNum)) +_idrepr(graph::AbstractGraph) = _idstring(graph) + +function _idrepr(graph::FeynmanGraph) + return string(_namestring(graph), _ops_to_repr(vertices(graph))) +end + +function _propertystring(graph::AbstractGraph) + return isnothing(properties(graph)) ? "" : string(properties(graph)) +end +function _weightstring(graph::AbstractGraph) + return isleaf(graph) ? "=$(short(weight(graph)))" : "=$(short(weight(graph)))=$(operator(graph))" +end + +function _stringrep(graph::AbstractGraph; color=false, plain=false) + if color + idprefix = "\u001b[32m$(id(graph))\u001b[0m: " + else + idprefix = string(id(graph), ": ") + end + idsuffix = plain ? _idstring(graph) : _idrepr(graph) + propertystr = _propertystring(graph) * short_orders(orders(graph)) + wstr = _weightstring(graph) + if isempty(idsuffix) == false && isempty(propertystr) == false + idsuffix *= ", " + end if isleaf(graph) - return isnothing(properties) ? "$(idstr)$(ostr)=$wstr" : "$(idstr)$(properties)$(ostr)=$wstr" + typestr = "" else - return isnothing(properties) ? "$(idstr)$(ostr)=$wstr=$(operator(graph)) " : "$(idstr)$(properties)$(ostr)=$wstr=$(operator(graph)) " + typestr = join(["$(id(g))" for g in subgraphs(graph)], ",") + typestr = "($typestr)" end + return "$(idprefix)$(idsuffix)$(propertystr)$(wstr)$typestr" +end + +""" + print(io::IO, graph::AbstractGraph) + + Write an un-decorated text representation of an AbstractGraph `graph` to the output stream `io`. +""" +function Base.print(io::IO, graph::AbstractGraph) + print(io, _stringrep(graph; plain=true)) end """ @@ -58,13 +97,7 @@ end Write a text representation of an AbstractGraph `graph` to the output stream `io`. """ function Base.show(io::IO, graph::AbstractGraph; kwargs...) - if isleaf(graph) == 0 - typestr = "" - else - typestr = join(["$(id(g))" for g in subgraphs(graph)], ",") - typestr = "($typestr)" - end - print(io, "$(_stringrep(graph, true))$typestr") + print(io, _stringrep(graph)) end Base.show(io::IO, ::MIME"text/plain", graph::AbstractGraph; kwargs...) = Base.show(io, graph; kwargs...) @@ -87,7 +120,7 @@ function plot_tree(graph::AbstractGraph; verbose=0, maxdepth=6) if level > maxdepth return end - name = "$(_stringrep(node, false))" + name = _stringrep(node) nt = t.add_child(name=name) if length(subgraphs(node)) > 0 diff --git a/src/computational_graph/tree_properties.jl b/src/computational_graph/tree_properties.jl index 6f59a452..7d7e5204 100644 --- a/src/computational_graph/tree_properties.jl +++ b/src/computational_graph/tree_properties.jl @@ -1,7 +1,7 @@ ##################### AbstractTrees interface for AbstractGraphs ########################### ## Things that make printing prettier -AbstractTrees.printnode(io::IO, g::AbstractGraph) = print(io, "\u001b[32m$(id(g))\u001b[0m : $g") +AbstractTrees.printnode(io::IO, g::AbstractGraph) = print(io, _stringrep(g; color=true)) ## Guarantee type-stable tree iteration for Graphs and FeynmanGraphs AbstractTrees.NodeType(::Graph) = HasNodeType() diff --git a/src/quantum_operator/expression.jl b/src/quantum_operator/expression.jl index 8e6e9572..20b385fe 100644 --- a/src/quantum_operator/expression.jl +++ b/src/quantum_operator/expression.jl @@ -64,7 +64,8 @@ Base.setindex!(o::OperatorProduct, v::QuantumOperator, i::Int) = o.operators[i] Base.length(o::OperatorProduct) = length(o.operators) Base.size(o::OperatorProduct) = size(o.operators) -Base.show(io::IO, o::OperatorProduct) = print(io, reduce(*, ["$o" for o in o.operators])) +Base.print(io::IO, o::OperatorProduct) = print(io, reduce(*, [string(o) for o in o.operators])) +Base.show(io::IO, o::OperatorProduct) = print(io, reduce(*, [repr(o) for o in o.operators])) Base.show(io::IO, ::MIME"text/plain", o::OperatorProduct) = Base.show(io, o) """ diff --git a/src/quantum_operator/operator.jl b/src/quantum_operator/operator.jl index 2db3fd2f..85439c72 100644 --- a/src/quantum_operator/operator.jl +++ b/src/quantum_operator/operator.jl @@ -65,7 +65,8 @@ Base.:(==)(a::QuantumOperator, b::QuantumOperator) = Base.isequal(a, b) # Relabel constructor QuantumOperator(qo::QuantumOperator, label::Int) = QuantumOperator(qo.operator(), label) -Base.show(io::IO, o::QuantumOperator) = print(io, "$(o.operator)($(o.label))") +Base.print(io::IO, o::QuantumOperator) = print(io, "$(o.operator)($(o.label))") +Base.show(io::IO, o::QuantumOperator) = print(io, "$(repr(o.operator))($(o.label))") Base.show(io::IO, ::MIME"text/plain", o::QuantumOperator) = Base.show(io, o) """ diff --git a/test/computational_graph.jl b/test/computational_graph.jl index bf1853ac..2510f24a 100644 --- a/test/computational_graph.jl +++ b/test/computational_graph.jl @@ -2,6 +2,7 @@ using FeynmanDiagram: ComputationalGraphs as Graphs # π“ž represents a non-trivial unary operation struct O <: Graphs.AbstractOperator end +# Base.show(io::IO, ::Type{O}) = print(io, "O") # π“ž1, π“ž2, and π“ž3 represent trivial unary operations struct O1 <: Graphs.AbstractOperator end @@ -10,6 +11,7 @@ struct O3 <: Graphs.AbstractOperator end Graphs.unary_istrivial(::Type{O}) where {O<:Union{O1,O2,O3}} = true @testset verbose = true "AbstractGraph interface" begin + # Example of a custom graph type with additional type-stable node properties ("color") mutable struct ConcreteGraph <: Graphs.AbstractGraph id::Int name::String @@ -17,33 +19,16 @@ Graphs.unary_istrivial(::Type{O}) where {O<:Union{O1,O2,O3}} = true operator::DataType subgraphs::Vector{ConcreteGraph} subgraph_factors::Vector{Float64} - factor::Float64 weight::Float64 - function ConcreteGraph(subgraphs=[]; name="", orders=zeros(Int, 0), operator=O(), subgraph_factors=[], factor=1.0, weight=1.0) - return new(Graphs.uid(), name, orders, typeof(operator), subgraphs, subgraph_factors, factor, weight) + color::String + function ConcreteGraph(subgraphs=[]; name="", orders=zeros(Int, 0), operator=O(), subgraph_factors=[], weight=1.0, color="black") + return new(Graphs.uid(), name, orders, typeof(operator), subgraphs, subgraph_factors, weight, color) end end - Graphs.uidreset() - g1 = ConcreteGraph(; operator=O1()) - g2 = ConcreteGraph(; operator=O2()) - g3 = ConcreteGraph(; operator=O3()) - g = ConcreteGraph([g1, g2, g3]; subgraph_factors=[2, 3, 5], operator=O()) - gp = ConcreteGraph([g1, g2, g3]; subgraph_factors=[2, 3, 5], operator=O()) - h = ConcreteGraph([g1, g2, g3]; name="h", subgraph_factors=[2, 3, 5], operator=O()) - # weight(g::AbstractGraph) is an abstract method @test isnothing(Graphs.weight(ConcreteGraph())) - # Base.:+(g1::AbstractGraph, g2::AbstractGraph) is an abstract method - err = AssertionError() - try - g1 + g2 - catch err - end - @test err isa ErrorException - @test err.msg == "Method not yet implemented for user-defined graph type ConcreteGraph." - ### AbstractGraph interface for ConcreteGraph ### # Getters @@ -52,6 +37,7 @@ Graphs.unary_istrivial(::Type{O}) where {O<:Union{O1,O2,O3}} = true Graphs.orders(g::ConcreteGraph) = g.orders Graphs.operator(g::ConcreteGraph) = g.operator Graphs.weight(g::ConcreteGraph) = g.weight + Graphs.properties(g::ConcreteGraph) = g.color Graphs.subgraph(g::ConcreteGraph, i=1) = g.subgraphs[i] Graphs.subgraphs(g::ConcreteGraph) = g.subgraphs Graphs.subgraph_factor(g::ConcreteGraph, i=1) = g.subgraph_factors[i] @@ -59,6 +45,7 @@ Graphs.unary_istrivial(::Type{O}) where {O<:Union{O1,O2,O3}} = true # Setters Graphs.set_name!(g::ConcreteGraph, name::AbstractString) = (g.name = name) + Graphs.set_properties!(g::ConcreteGraph, color::AbstractString) = (g.color = color) Graphs.set_subgraph!(g::ConcreteGraph, subgraph::ConcreteGraph, i=1) = (g.subgraphs[i] = subgraph) Graphs.set_subgraphs!(g::ConcreteGraph, subgraphs::Vector{ConcreteGraph}) = (g.subgraphs = subgraphs) Graphs.set_subgraph_factor!(g::ConcreteGraph, subgraph_factor::Float64, i=1) = (g.subgraph_factors[i] = subgraph_factor) @@ -66,6 +53,31 @@ Graphs.unary_istrivial(::Type{O}) where {O<:Union{O1,O2,O3}} = true ############################### + Graphs.uidreset() + g1 = ConcreteGraph(; operator=O1(), color="red", name="g1") + g2 = ConcreteGraph(; operator=O2(), color="green", name="g2") + g3 = ConcreteGraph(; operator=O3(), color="blue", name="g3") + g = ConcreteGraph([g1, g2, g3]; subgraph_factors=[2, 3, 5], operator=O()) + gp = ConcreteGraph([g1, g2, g3]; subgraph_factors=[2, 3, 5], operator=O()) + h = ConcreteGraph([g1, g2, g3]; name="h", subgraph_factors=[2, 3, 5], operator=O()) + + # Base.:+(g1::AbstractGraph, g2::AbstractGraph) is an abstract method + err = AssertionError() + try + g1 + g2 + catch err + end + @test err isa ErrorException + @test err.msg == "Method not yet implemented for user-defined graph type ConcreteGraph." + + @testset "String representation" begin + @test repr(g1) == "1: g1, red=1.0" + @test repr(g2) == "2: g2, green=1.0" + @test repr(g3) == "3: g3, blue=1.0" + @test repr(g) == "4: black=1.0=O(1,2,3)" + @test repr(gp) == "5: black=1.0=O(1,2,3)" + @test repr(h) == "6: h, black=1.0=O(1,2,3)" + end @testset "Traits" begin @test Graphs.unary_istrivial(g1) == true @test Graphs.unary_istrivial(g2) == true @@ -78,6 +90,7 @@ Graphs.unary_istrivial(::Type{O}) where {O<:Union{O1,O2,O3}} = true @test Graphs.orders(g) == zeros(Int, 0) @test Graphs.operator(g) == O @test Graphs.weight(g) == 1.0 + @test Graphs.properties(g) == "black" @test Graphs.subgraph(g) == g1 @test Graphs.subgraph(g, 2) == g2 @test Graphs.subgraphs(g) == [g1, g2, g3] @@ -90,6 +103,10 @@ Graphs.unary_istrivial(::Type{O}) where {O<:Union{O1,O2,O3}} = true @testset "Setters" begin Graphs.set_name!(g, "g") @test Graphs.name(g) == "g" + Graphs.set_properties!(g, "white") + @test Graphs.properties(g) == "white" + @test string(g) == "4: g, white=1.0=O(1,2,3)" + Graphs.set_properties!(g, "black") Graphs.set_subgraph!(g, g2, 1) @test Graphs.subgraph(g) == g2 Graphs.set_subgraphs!(g, [g1, g2, g3]) @@ -271,7 +288,7 @@ end @test h8.subgraph_factors == [36] @test isequiv(h7_lc, FeynmanGraph([g1s,], drop_topology(g1.properties); subgraph_factors=[-36], operator=Graphs.Sum()), :id) end - @testset "Merge multi-pproduct" begin + @testset "Merge multi-product" begin g1 = Graph([]) g2 = Graph([], factor=2) g3 = Graph([], factor=3) @@ -801,6 +818,14 @@ end @test external_operators(g) == reduce(*, V3) end end + + @testset verbose = true "String representation" begin + Graphs.uidreset() + g1 = propagator(𝑓⁻(1)𝑓⁺(2)) + g1p = propagator(𝑓⁻(1)𝑓⁺(2); name="g1p") + @test repr(g1) == "1: f⁻(1)|f⁺(2)=0.0" + @test repr(g1p) == "2: g1p, f⁻(1)|f⁺(2)=0.0" + end end @testset verbose = true "Conversions" begin diff --git a/test/deprecated.jl b/test/deprecated.jl index 44c4066c..1a6a597d 100644 --- a/test/deprecated.jl +++ b/test/deprecated.jl @@ -44,7 +44,7 @@ @test parity5 == 1 end -@testset "feynman_diagram from Wick" +@testset "feynman_diagram from Wick" begin # construct Feynman diagram from FeynmanGraphs g1 = ComputationalGraphs.propagator(𝑓⁺(1)𝑓⁻(2),) g2 = ComputationalGraphs.propagator(𝑓⁺(2)𝑓⁻(1),) @@ -55,4 +55,4 @@ end g = feynman_diagram([g1, g2], [1, 2, 2, 1]; external=[1, 2]) #build Feynman diagram from FeynmanGraphs with topology @test external(g) == [external(g1); external(g2)] @test isempty(internal_vertices(g)) -end \ No newline at end of file +end diff --git a/test/quantum_operator.jl b/test/quantum_operator.jl index 801c53d1..c3406a35 100644 --- a/test/quantum_operator.jl +++ b/test/quantum_operator.jl @@ -73,4 +73,19 @@ end p4 = [3, 5, 1, 2, 4, 6, 7] @test QuantumOperators.parity(p4) == -1 @test QuantumOperators.parity_old(p4) == -1 -end \ No newline at end of file +end + +@testset "String representations" begin + @testset "Operator" begin + o = QuantumOperator(QuantumOperators.Majorana(), 1) + @test repr(o) == "f(1)" + end + @testset "OperatorProduct" begin + op1 = OperatorProduct(QuantumOperator(QuantumOperators.Majorana(), 1)) + s1 = string(op1) + @test repr(op1) == "f(1)" + op2 = 𝑓⁺(1)𝑓⁻(2) + s2 = string(op2) + @test repr(op2) == "f⁺(1)f⁻(2)" + end +end