Skip to content

Basic implementation of undirected graphs #861

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

Merged
merged 22 commits into from
Dec 31, 2020
Merged

Conversation

behackl
Copy link
Member

@behackl behackl commented Dec 17, 2020

Motivation

I feel it is a missed opportunity that Manim currently does not offer (easy) access to Graphs, and I don't want to hack together Dot-and-Line-constructs any more.

Overview / Explanation for Changes

This PR introduces a new module, manim.mobject.graph containing (for now) one class, Graph, which serves as an interface for rendering graphs. For now, there (sadly) are no useful utility functions for graph manipulation -- however, I've tried to keep everything as customizable as possible. In particular, I find it useful to have the edges actually attached to the vertices (in the sense that moving a vertex should also move the corresponding edge); this is done here by adding appropriate updaters.

Furthermore, this PR adds networkx as a dependency: I feel strongly that if we are going to include Graphs in Manim, we should utilize existing code as much as possible. With this PR, this interface to networkx might not be fully exploited yet -- but it is not unused; it currently gives us access to several algorithms for graph layouting (which I, personally, see as a strong requirement for a useable Graph class. I don't even want to think about positioning vertices manually (but of course, it is possible with this PR)).

See some simple examples for this graph in the documentation: https://manimce--861.org.readthedocs.build/en/861/reference/manim.mobject.graph.Graph.html#manim.mobject.graph.Graph

Oneline Summary of Changes

- Added :class:`~.Graph`, a basic implementation for graph theoretic graphs (:pr:`PR NUMBER HERE`)

Testing Status

Docs and corresponding examples build locally and almost* look as intended.

Further Comments

Depends on #872 (for fixing the ShowCreation animation: first vertices are drawn, then edges -- but the edges are still behind the vertices).

Acknowledgements

Reviewer Checklist

  • Newly added functions/classes are either private or have a docstring
  • Newly added functions/classes have tests added and (optional) examples in the docs
  • Newly added documentation builds, looks correctly formatted, and adds no additional build warnings
  • The oneline summary has been included in the wiki

@behackl
Copy link
Member Author

behackl commented Dec 17, 2020

I should also mention (sorry for forgetting about it, initially): this is based on prior work by @XorUnison, I basically expanded and refined the interface that #210 implemented.

@kolibril13
Copy link
Member

The examples https://manimce--861.org.readthedocs.build/en/861/reference/manim.mobject.graph.Graph.html#movingvertices
look very nice! Thanks for this contribution, I will have a look at the code soon.
One thing to note by now:
The name "GraphScene" for Plotting and "Graph" for graph theory can be a bit misleading.
But I think there were thoughts of removing "GraphScene" and "ZoomedScene" sometime in future so that the potential misunderstanding that Graphs are related to GraphScene won't be an issue anymore.

Copy link
Member

@naveen521kk naveen521kk left a comment

Choose a reason for hiding this comment

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

I would suggest you type your code, either using python typing, or atleast in the doc strings. That would save a lot of time for users, and typing it later would be super hard, rather than the person who created it typing it.

@behackl
Copy link
Member Author

behackl commented Dec 17, 2020

I would suggest you type your code, either using python typing, or atleast in the doc strings. That would save a lot of time for users, and typing it later would be super hard, rather than the person who created it typing it.

Sure, I was planning to add type hints anyways -- just not for the initial, possibly still very unstable first version. I'd like to get some general feedback on the interface first.

@XorUnison
Copy link
Collaborator

XorUnison commented Dec 17, 2020

The name "GraphScene" for Plotting and "Graph" for graph theory can be a bit misleading.

Yeah. Back when I made the according PR I settled for calling it EdgeVertexGraph instead. I bit unwieldy, but makes it really really clear what the class is about.

The examples look decent, but there's this strange jerking motion after ShowCreation.

@kolibril13
Copy link
Member

Yeah. Back when I made the according PR I settled for calling it EdgeVertexGraph instead. I bit unwieldy, but makes it really really clear what the class is about.

As graph theory is a very niche topic and most people won't deal with it when using manim, I am also in favour of calling it EdgeVertexGraph or something similar.

@behackl
Copy link
Member Author

behackl commented Dec 17, 2020

Yeah. Back when I made the according PR I settled for calling it EdgeVertexGraph instead. I bit unwieldy, but makes it really really clear what the class is about.

As graph theory is a very niche topic and most people won't deal with it when using manim, I am also in favour of calling it EdgeVertexGraph or something similar.

While sure, graph theory might not be something that is usually taught in schools, I strongly disagree with graph theory being a niche topic.

Regarding names: I'd like to keep the name as Graph, because,

I don't see the big potential for confusion, but I'm open for discussing potential user scenarios.

@behackl
Copy link
Member Author

behackl commented Dec 17, 2020

The examples look decent, but there's this strange jerking motion after ShowCreation.

See the additional comments in the PR description above; in an ideal world we would be able to make the vertices render first, and have the edges be added behind them. I could not find a way to do so yet; I think z_index has been removed and I am not sure whether there are any suitable alternatives. I'd be happy about suggestions! :-)

@XorUnison
Copy link
Collaborator

See the additional comments in the PR description above; in an ideal world we would be able to make the vertices render first, and have the edges be added behind them. I could not find a way to do so yet; I think z_index has been removed and I am not sure whether there are any suitable alternatives. I'd be happy about suggestions! :-)

Right, but that's not what I was talking about. I did figure it out in the meantime however.

See... there's a bit of a strange issue happening with your updater. While in principle a great idea, something strange, and frankly very unexpected happens when the updater grabs the centers of the vertices here: self[v].get_center().
Say you have a Dot d at [0,0,0], then d.get_center() returns your point [0,0,0]...usually. And that's a reasonable assumption. You might think it would do that during ShowCreation as well, which it should I think.
Though during ShowCreation when the Dot gets drawn along it's perimeter this causes d.get_center() to actually throw slightly altered values. I've confirmed this with a full dump of positions by inserting print(self[v].get_center()). I'd say is unexpected behavior and a bug, and that is causing the jerky motion in your example. If you really want to see this in action, make the dots in your graph larger (using label is enough), and ramp the run time up to 10 or something. You can see that the edges follow some imaginary temporary center of the Dot as its drawn.

So now the quesion is what we're gonna do about it? Fixing this is technically speaking a breaking change, but any sort of introduction animation shouldn't affect the supposed center of the object.

@behackl
Copy link
Member Author

behackl commented Dec 18, 2020

See the additional comments in the PR description above; in an ideal world we would be able to make the vertices render first, and have the edges be added behind them. I could not find a way to do so yet; I think z_index has been removed and I am not sure whether there are any suitable alternatives. I'd be happy about suggestions! :-)

Right, but that's not what I was talking about. I did figure it out in the meantime however.

No, we are talking about the same issue: I know that get_center is not stable during creation, that is -- as you also discovered -- the reason why the edges are moving a little when the vertices are drawn.

Changing the order of appearance (vertices first, edges after) does not only create a better looking animation, it also fixes the jerking motion because the edges are only drawn after the center of the vertices has stabilized, so to say.

But we can only do that properly if there is a way to set the position (i.e., vertices in the foreground, edges in the background) separate from the order of drawing. Otherwise, labeled (or otherwise colored) graphs will have visible edges reaching into the vertices. For Dots, this could also be fixed by setting the buff of the lines to the radius of the vertices -- but this will break if vertex_type is changed, or if some vertices are configured to be larger than others.

Another option would be to get the point on the boundary of the vertex that the line between two vertices passes through -- I haven't investigated that yet, though.

@behackl
Copy link
Member Author

behackl commented Dec 18, 2020

I've managed to get it working:

LabeledModifiedGraph

But the only way I could find now uses z_index (and I needed to patch the sorting w.r.t. z_index in extract_mobject_family_members to sort all mobjects with respect to their z_index), so that it reads like

    if only_those_with_points:
        method = Mobject.family_members_with_points
    else:
        method = Mobject.get_family
    extracted_mobjects = remove_list_redundancies(list(it.chain(*[method(m) for m in mobjects])))
    if use_z_index:
        return sorted(extracted_mobjects, key=lambda m: m.z_index)
    return extracted_mobjects

However, z_index is proposed to be removed via #864, and I don't see any available alternative. Ideas, anyone?

@kolibril13
Copy link
Member

While sure, graph theory might not be something that is usually taught in schools, I strongly disagree with graph theory being a niche topic.

Thanks for giving these arguments, then I am fine with calling ith "Graph".
But we should also stay aware that manim could one day also be used by high school students, that were not in contact with graph theory and could get mislead by the Graph class. However, this is a very hypothetical case, and as you mentioned that we have a great RTD side, I think we can stay with it like it is.

Copy link
Member

@eulertour eulertour left a comment

Choose a reason for hiding this comment

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

LGTM

@behackl
Copy link
Member Author

behackl commented Dec 21, 2020

This branch now contains the fix for the reordering using z_index, but only works with #872.

Thus, this PR now depends on #872.

@behackl behackl added the pr:dependent This PR or issue requires that another PR is merged first label Dec 21, 2020
@behackl behackl added the new feature Enhancement specifically adding a new feature (feature request should be used for issues instead) label Dec 21, 2020
@github-actions github-actions bot removed the pr:dependent This PR or issue requires that another PR is merged first label Dec 23, 2020
@github-actions
Copy link
Contributor

🎉 Great news! Looks like all the dependencies have been resolved:

💡 To add or remove a dependency please update this issue/PR description.

Brought to you by Dependent Issues (:robot: ). Happy coding!

@behackl
Copy link
Member Author

behackl commented Dec 29, 2020

I've added type hints via 45e52ba.

I am working on extending this basic interface (as a first step, methods for modifying the graph like add_vertex, remove_vertex ... seem useful to me), but in order to keep these graph-related PRs small and (somewhat) easy to review, I'd suggest to leave this PR as-is (except for changes requested by reviewers, ofc) and add further functionality via follow-up PRs. Any thoughts about that?

@kolibril13 kolibril13 added this to the Release v0.2.0 milestone Dec 31, 2020
@kolibril13
Copy link
Member

I just tested it, and it works nicely on my machine!
One thing that could be a nice feature would be to change the stroke_width when calling Graph.
At the moment, https://manimce--861.org.readthedocs.build/en/861/reference/manim.mobject.graph.Graph.html#labeledmodifiedgraph
e.g. here it is difficult to distinguish which lines are in the foreground and which in the background.

@behackl
Copy link
Member Author

behackl commented Dec 31, 2020

I just tested it, and it works nicely on my machine!
One thing that could be a nice feature would be to change the stroke_width when calling Graph.
At the moment, https://manimce--861.org.readthedocs.build/en/861/reference/manim.mobject.graph.Graph.html#labeledmodifiedgraph
e.g. here it is difficult to distinguish which lines are in the foreground and which in the background.

Doing something like

Graph(vertices, edges, edge_config={'stroke_width': 5})

or so does what you want. In case of a situation that is like the example above, where some edges get a custom styling, the parameter has to be added to each of the custom style dictionaries as well.

Feedback on the interface is very welcome! I'd like to get this basic version in for the release, and then we can iterate on the interface and further custom methods, so that the graph class becomes more useful. :-)

@behackl
Copy link
Member Author

behackl commented Dec 31, 2020

Tests pass and documentation renders correctly, let's ship it! :-)

@behackl behackl merged commit e16f9a6 into ManimCommunity:master Dec 31, 2020
@behackl behackl deleted the graph branch December 31, 2020 20:10
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
new feature Enhancement specifically adding a new feature (feature request should be used for issues instead)
Projects
None yet
Development

Successfully merging this pull request may close these issues.

5 participants