Skip to content

Commit

Permalink
Improve graph string representations and bugfix for typestr in func…
Browse files Browse the repository at this point in the history
…tion `_stringrep` to correctly display subgraph IDs
  • Loading branch information
dcerkoney committed Sep 18, 2024
1 parent e5eead6 commit 4dbe195
Show file tree
Hide file tree
Showing 8 changed files with 127 additions and 52 deletions.
6 changes: 3 additions & 3 deletions src/computational_graph/ComputationalGraph.jl
Original file line number Diff line number Diff line change
Expand Up @@ -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!

Expand Down
77 changes: 55 additions & 22 deletions src/computational_graph/io.jl
Original file line number Diff line number Diff line change
@@ -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 ""
Expand All @@ -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

"""
Expand All @@ -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...)

Expand All @@ -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
Expand Down
2 changes: 1 addition & 1 deletion src/computational_graph/tree_properties.jl
Original file line number Diff line number Diff line change
@@ -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()
Expand Down
3 changes: 2 additions & 1 deletion src/quantum_operator/expression.jl
Original file line number Diff line number Diff line change
Expand Up @@ -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)

"""
Expand Down
3 changes: 2 additions & 1 deletion src/quantum_operator/operator.jl
Original file line number Diff line number Diff line change
Expand Up @@ -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)

"""
Expand Down
67 changes: 46 additions & 21 deletions test/computational_graph.jl
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -10,40 +11,24 @@ 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
orders::Vector{Int}
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
Expand All @@ -52,20 +37,47 @@ 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]
Graphs.subgraph_factors(g::ConcreteGraph) = g.subgraph_factors

# 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)
Graphs.set_subgraph_factors!(g::ConcreteGraph, subgraph_factors::AbstractVector) = (g.subgraph_factors = subgraph_factors)

###############################

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
Expand All @@ -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]
Expand All @@ -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])
Expand Down Expand Up @@ -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)
Expand Down Expand Up @@ -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
Expand Down
4 changes: 2 additions & 2 deletions test/deprecated.jl
Original file line number Diff line number Diff line change
Expand Up @@ -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),)
Expand All @@ -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
end
17 changes: 16 additions & 1 deletion test/quantum_operator.jl
Original file line number Diff line number Diff line change
Expand Up @@ -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
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

0 comments on commit 4dbe195

Please sign in to comment.