Skip to content

Conversation

@jack-dunham
Copy link
Contributor

This PR includes the following changes to [PartitionedGraphs]

  • Added some additional NamedGraphs interface functions to various data types.
  • Constructing a graph from a QuotientView now directly calls quotient_graph.
  • Calling edges and vertices on QuotientView now returns according to the interface priority.
  • Constructing graphs from QuotientViews of AbstractSimpleGraphs will now return a graph of similar type.

- `vertices(::QuotientView)` now directly returns the keys
`paritioned_vertices` return value.
- `edges(::QuotientView)` now directly returns the edges of the
`quotient_graph` return value (to coincide with interface overloading
priority)
- Converting a `QuotientView` to graph now calls `quotient_graph`
directly
- Adding methods for return directed/undirected graph types.

Fix imports

Fix rebase
This behaves similarly to `partitionedgraph` function.
…ype by default

This function can now be used `SimpleGraph` etc without promoting to
`NamedGraph`.
This function constructs a graph with no edges, but with vertices of the
quotient graph.
…ype as a parameter

Allows for more generic quotient graph types.
- argument `vertices` must be of type `Base.OneTo{Int}`.
…e, and triple argument methods.

This interface is to overload `similar_graph(graph)`, i.e. the single
argument method.
This function now acts similarly to `similar_graph`.
Co-authored-by: Matt Fishman <mtfishman@users.noreply.github.com>
@mtfishman
Copy link
Member

@jack-dunham I had forgetten, but Base.to_index handles both scalar indexes and array of indices: https://github.com/JuliaLang/julia/blob/v1.12.3/base/indices.jl#L279-L315 (Base.to_indices refers to multiple dimensions). So maybe we could just go with to_graph_index for both single vertices/edges and sets of vertices/edges.

end
end

quotients(qvs::QuotientVertexVertices) = getfield(qvs, :quotients)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this is a case where it would be better to not define this function and instead directly use qvs.quotients, to make it clearer this is just accessing data in the struct and not really a generic function.

Comment on lines +51 to +54
function QuotientEdges{V, E}(edges::Es) where {V, E, Es}
@assert E <: eltype(Es)
return new{V, E, Es}(edges)
end
Copy link
Member

@mtfishman mtfishman Dec 19, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Related to offline discussions, I think we should remove this constructor and just get the edge type from the input (we can revisit if use cases arise that don't have reasonable alternatives).

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I thought I had checked this! Thanks for picking this up.

Comment on lines 70 to 82
function Base.iterate(qes::QuotientEdges, state = nothing)
if isnothing(state)
out = iterate(qes.edges)
else
out = iterate(qes.edges, state)
end
if isnothing(out)
return nothing
else
(v, s) = out
return (QuotientEdge(v), s)
end
end
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The iterate definitions seem a bit complicated to me. It looks like they are all basically a lazy map over iterating over one of the fields. It seems like that can be implemented more simply like this:

struct Wrapper{T}
    data::T
end
Base.length(w::Wrapper) = length(w.data)
Base.eltype(w::Wrapper) = String # Maybe use `Base.promote_op` if the map is more complicated?
function Base.iterate(w::Wrapper, state...)
    return iterate(Iterators.map(string, w.data), state...)
end

which enables:

julia> x = Wrapper([1, 2, 3])
Wrapper{Vector{Int64}}([1, 2, 3])

julia> collect(x)
3-element Vector{String}:
 "1"
 "2"
 "3"

I think then we wouldn't need iterate_graph_indices.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't like these iteration definitions either; I assumed there was a better solution. Thanks.

end
end

quotients(qes::QuotientEdgeEdges) = getfield(qes, :quotients)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this function needed? Could we just use direct field access?

Vertices(vertices::Vs) where {Vs} = new{eltype(Vs), Vs}(vertices)
end

Vertices(v1, v2, vertices...) = Vertices(vcat([v1, v2], collect(vertices)))
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not a fan of this constructor, I think we should keep constructors simpler and stricter, for example just have one kind of expected input (in this case, only accept a single collection of vertices). Wouldn't this kind of use case already be handled by the basic constructor, and you can pass a Tuple of vertices, i.e. Vertices(("a", "b", "c"))?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As a concrete issue with this constructor, it makes Vertices(["a"]) ambiguous, since it isn't clear if that is supposed to be the set of vertices, or just the length-1 case of passing multiple arguments (which then if I understand this constructor, would interpret ["a"] as a vertex itself). So it is better to just avoid that kind of ambiguity and keep constructors stricter.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That's fair. I mostly liked the syntax g[Vertices("a", "b")] when indexing as it cuts down on the number of nested brackets, i.e. g[Vertices(("a","b"))]. My motivation was that multiple arguments should be interpreted as a collection of vertices, and a single argument should be interpreted as an object representing a collection of vertices.

I realize it is probably not worth the inconsistency.

end
end

Edges(e1, e2, edges...) = Edges(vcat([e1, e2], collect(edges)))
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same comment as above.

Comment on lines 35 to 48
Base.iterate(gi::AbstractGraphIndices, state = nothing) = iterate_graph_indices(identity, gi, state)
function iterate_graph_indices(f, gi::AbstractGraphIndices, state)
if isnothing(state)
out = iterate(parent_graph_indices(gi))
else
out = iterate(parent_graph_indices(gi), state)
end
if isnothing(out)
return nothing
else
(v, s) = out
return (f(v), s)
end
end
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same comment as above regarding a simpler iterate definition.

Comment on lines 48 to 50
function Base.iterate(qvs::QuotientVertices, state = nothing)
return NamedGraphs.iterate_graph_indices(QuotientVertex, qvs, state)
end
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same comment as above regarding a simpler iterate definition.

Comment on lines 133 to 135
function Base.iterate(qvs::QuotientVertexVertices, state = nothing)
return NamedGraphs.iterate_graph_indices(v -> quotient_index(qvs)[v], qvs, state)
end
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same comment as above regarding a simpler iterate definition.

end
end

QuotientVertices(vertices...) = QuotientVertices(collect(vertices))
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same comment as the ones for the analogous Vertices and Edges constructors.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants