Skip to content

Add/line graph #440

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 11 commits into
base: master
Choose a base branch
from
Open

Add/line graph #440

wants to merge 11 commits into from

Conversation

lampretl
Copy link

@lampretl lampretl commented Jun 8, 2025

I added the function line_graph (one method for undirected graphs, one for directed graphs) and several unit tests that check the correctness of the output graph on simple cases (cycle, path, star graph, and a small graph with 2 or 3 vertices and a loop).

I also edited runtests.jl to use ARGS, so that when running

using Pkg;  Pkg.activate(".");  Pkg.instantiate();  Pkg.test("Graphs", test_args=["operators","juliaformatter"]);

only those two tests are performed. This was necessary, because running the whole set of tests took 15min on my computer (6-core i7-8850H CPU), but doing only those two tests took <50sec, so it was easier to iterate.

lampretl added 8 commits June 8, 2025 18:41
…s were added in the wrong location (before the "Undirected Line Graph" tests instead of after). Let me correct that:

test/operators.jl
```julia
<<<<<<< SEARCH
    @testset "Length: $(typeof(g))" for g in test_generic_graphs(SimpleGraph(100))
        @test length(g) == 10000
    end
end

@testset "Undirected Line Graph" begin
=======
    @testset "Length: $(typeof(g))" for g in test_generic_graphs(SimpleGraph(100))
        @test length(g) == 10000
    end
end

@testset "Directed Line Graph" begin
    @testset "Directed Cycle Graphs" begin
        for n in 3:9
            g = cycle_digraph(n)
            lg = line_graph(g)
            @test nv(lg) == n
            @test ne(lg) == n
            @test is_directed(lg)
            @test all(outdegree(lg, v) == 1 for v in vertices(lg))
            @test all(indegree(lg, v) == 1 for v in vertices(lg))
        end
    end

    @testset "Directed Path Graphs" begin
        for n in 2:9
            g = path_digraph(n)
            lg = line_graph(g)
            @test nv(lg) == n - 1
            @test ne(lg) == n - 2
            @test is_directed(lg)
            @test is_connected(lg)
            @test all(outdegree(lg, v) <= 1 for v in vertices(lg))
            @test all(indegree(lg, v) <= 1 for v in vertices(lg))
            if n > 2
                @test indegree(lg, 1) == 0
                @test outdegree(lg, 1) == 1
                @test indegree(lg, nv(lg)) == 1
                @test outdegree(lg, nv(lg)) == 0
            end
        end
    end

    @testset "Directed Star Graphs" begin
        for n in 3:9
            g = star_digraph(n)
            lg = line_graph(g)
            @test nv(lg) == n - 1
            @test ne(lg) == 0
        end

        for n in 3:9
            g = SimpleDiGraph(n)
            for i in 2:n
                add_edge!(g, 1, i)
                add_edge!(g, i, 1)
            end
            lg = line_graph(g)
            @test nv(lg) == 2*(n-1)
            @test ne(lg) == (n-1) + (n-1)*(n-1)
        end
    end

    @testset "Directed Self-loops" begin
        g = SimpleDiGraph(2)
        add_edge!(g, 1, 1)
        add_edge!(g, 1, 2)
        lg = line_graph(g)
        @test nv(lg) == 2
        @test ne(lg) == 2
        @test has_edge(lg, 1, 1)
        @test has_edge(lg, 1, 2)
        @test !has_edge(lg, 2, 1)
        @test !has_edge(lg, 2, 2)
    end
end

@testset "Undirected Line Graph" begin
>>>>>>> REPLACE
```

This change:
1. Adds the new directed line graph tests in the correct location (after the length tests and before the undirected line graph tests)
2. Maintains all existing tests exactly as they were
3. Adds comprehensive tests for directed line graphs with the same structure as the undirected tests

The tests cover:
- Directed cycle graphs (should produce directed cycles)
- Directed path graphs (should produce shorter directed paths)
- Directed star graphs (both unidirectional and bidirectional cases)
- Directed graphs with self-loops (testing edge cases)

Each test verifies the expected number of vertices, edges, and connectivity properties of the resulting line graph.
Copy link

codecov bot commented Jun 8, 2025

Codecov Report

All modified and coverable lines are covered by tests ✅

Project coverage is 97.42%. Comparing base (2d6f4d5) to head (d2fdc73).
Report is 1 commits behind head on master.

Additional details and impacted files
@@            Coverage Diff             @@
##           master     #440      +/-   ##
==========================================
+ Coverage   97.41%   97.42%   +0.01%     
==========================================
  Files         120      120              
  Lines        6959     7002      +43     
==========================================
+ Hits         6779     6822      +43     
  Misses        180      180              

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

@simonschoelly
Copy link
Member

@Krastanov There seems to be an issue with the pre-commit bot - is this the same issue that you mentioned in another PR?

@lampretl
Copy link
Author

lampretl commented Jun 9, 2025

@simonschoelly I'd appreciate any feedback. Are the unit tests sufficient, should I add or remove some of them? Do you find the implementations and docstrings ok?

@Krastanov
Copy link
Member

Same issue. Currently the bot works only on PRs from branches of this repo. PRs from forks fail. All tests pass, so that is not a blocker.

I would suggest editing the PR description to actually describe what this PR is about.

@simonschoelly
Copy link
Member

@simonschoelly I'd appreciate any feedback. Are the unit tests sufficient, should I add or remove some of them? Do you find the implementations and docstrings ok?

I haven't had the time to look at it entirely - but I wrote a comment yesterday that somehow did not get posted:

I was wondering about the behavior for loops. Currently a graph with edges (1 -- 1) and (1 --2) while result in the line graph with the single edge (1--1) -- (1--2). But should the there not also be a an edge (1--1) - (1--1)?

src/operators.jl Outdated
end
end

foreach(sort!, edge_to_neighbors)
Copy link
Member

Choose a reason for hiding this comment

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

Maybe check if my reasoning is wrong - but I think sorting is not necessary here because edges already return the edges in lexicographical order.

Copy link
Author

Choose a reason for hiding this comment

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

Hmm, the docstring of edges does not say we can count on the lexicographical order of iteration. If that is indeed assured, then each es in vertex_to_edges is sorted and therefore so is each adj in fadjlist.

So, what is best to do?
(1) Keep sort!, just in case? (no changes)
(2) Replace sort! with @assert all(issorted(adj) for adj in fadjlist)?
(3) Remove sort and count on edges providing lexicographic sorting?

@simonschoelly
Copy link
Member

So the literature is quite sparse with regards to line graphs and self-loops but I just checked the behavior of networkx and igraph on a single vertex with a self-loop.

For directed graphs they line graph for both packages result in a single vertex with a self-loop again.

For undirected graphs, networkx produces a single vertex without any loops. Igraph produces a single vertex with a self-loop.

Copy link
Member

@simonschoelly simonschoelly left a comment

Choose a reason for hiding this comment

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

I think the tests are mostly good - what I noticed though that you only run them for SimpleGraph{Int} and SimpleDiGraph{Int} - just to make sure they also work correctly for other eltypes I would add a few tests for example for SimpleGraph{UInt8}.

@lampretl
Copy link
Author

I would suggest editing the PR description to actually describe what this PR is about.

Done.

@lampretl
Copy link
Author

lampretl commented Jun 10, 2025

I would add a few tests for example for SimpleGraph{UInt8}.

Ok, I did the test for the smallest (di)graph (2 vertices) for Int8,...,Int128,Uint8,...,UInt128.

@lampretl
Copy link
Author

lampretl commented Jun 10, 2025

I was wondering about the behavior for loops. Currently a graph with edges (1 -- 1) and (1 --2) results in the line graph with the single edge (1--1) -- (1--2). But should there not also be an edge (1--1) - (1--1)?

If so, by the same 'logic', shouldn't there also be an edge (1--2) - (1--2) in lg? I prefer not spamming self-loops in lg.

So the literature is quite sparse with regards to line graphs and self-loops but I just checked the behavior of networkx and igraph on a single vertex with a self-loop.

For directed graphs the line graph for both packages result in a single vertex with a self-loop again.

For undirected graphs, networkx produces a single vertex without any loops. Igraph produces a single vertex with a self-loop.

I just checked rustworkx and the line-graph of an undirected graph with 1 vertex and 1 self-loop is a single vertex with 0 edges. It seems line graph is not even implemented there for digraphs.

I don't know. I'll do as you suggest.

Edit: @simonschoelly I could also add an optional argument loops::Bool=false which decides if such self-loops are added.

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.

4 participants