petgraph-0.5.1/.cargo_vcs_info.json 0000644 00000000112 13662222312 0012740 0 ustar 00 {
"git": {
"sha1": "397b9fce8e5cf6befd142593737da22160ad447f"
}
}
petgraph-0.5.1/.github/ISSUE_TEMPLATE.md 0100644 0001750 0001750 00000002754 13577670250 0015641 0 ustar 00 0000000 0000000
### Test Case
Insert your test case here, ideally in the form of Rust code that creates a
graph.
### Steps To Reproduce
Insert instructions for reproducing the bug with the above test case:
* First, ...
* Then, ...
* ...
Bonus points if these STR are in the form of a `#[test]` that demonstrates the
bug, and can be used as a regression test once the bug is fixed
### Actual Results
Insert a panic backtrace here, or wrong answer, or whatever.
### Expected Results
The correct result / behavior that you expect when following the STR with the
test case. Maybe a `None` returned instead of a panic, or some node A instead of
the node B that was returned.
### Summary
A couple sentence summary of the requested feature.
### Motivation
What does adding this feature enable that isn't possible without it? Does it
improve performance? Turn dynamic checks into statically enforced properties?
Explain why this feature should be implemented.
### Details
* What code needs to change? New traits? Which modules?
* Is this a new graph algorithm? Include links to papers, Wikipedia, or other
implementations of this algorithm that exist.
* Are you willing to implement this yourself? Mentor someone else and help them
implement it?
petgraph-0.5.1/.gitignore 0100644 0001750 0001750 00000000051 13577670250 0013550 0 ustar 00 0000000 0000000 # Generated by Cargo
/target/
Cargo.lock
petgraph-0.5.1/.travis.yml 0100644 0001750 0001750 00000001411 13662133536 0013666 0 ustar 00 0000000 0000000 language: rust
jobs:
include:
- rust: 1.37.0
env:
- ALL=' '
- rust: stable
env:
- FEATURES='unstable quickcheck'
- CHECKFMT=1
- rust: beta
- rust: nightly
- rust: nightly
env:
- FEATURES='unstable quickcheck'
- BENCH=1
branches:
only:
- master
before_script:
- |
if [ "${CHECKFMT}" == "1"]; then
rustup component add rustfmt --toolchain stable
fi
script:
- |
cargo build --verbose --no-default-features &&
cargo test --verbose --no-default-features &&
cargo build --verbose --features "$FEATURES" &&
cargo test ${ALL:---all} --verbose --features "$FEATURES" &&
if [ "${CHECKFMT}" == "1"]; then
cargo +stable fmt -- --check
fi
petgraph-0.5.1/CONTRIBUTING.rst 0100644 0001750 0001750 00000006447 13577670250 0014240 0 ustar 00 0000000 0000000 Contributing to ``petgraph``
============================
Hi! We'd love to have your contributions! If you want help or mentorship, reach
out to us in a GitHub issue, or ping ``bluss`` in `#rust on irc.mozilla.org`_
and introduce yourself.
.. _`\#rust on irc.mozilla.org`: irc://irc.mozilla.org#rust
* `Building`_
* `Testing`_
* `Pull Requests`_
* `Bug Fixes`_
* `Performance Improvements`_
* `Implementing New Algorithms`_
* `Where We Need Help`_
* `Team`_
Building
--------
::
$ cargo build
Testing
-------
::
$ cargo test --features all
Pull Requests
-------------
All pull requests are reviewed by a team_ member before merging.
Additionally, different kinds of pull requests have different requirements.
Bug Fixes
.........
We love getting bug fixes!
Make sure to include a regression test, so that we can be sure that we never
accidentally re-introduce the bug again.
Performance Improvements
........................
You made an algorithm faster? Awesome.
When submitting performance improvement, include the following:
* A new ``#[bench]`` function that exercises this code path, if one doesn't
already exist
* Before and after ``cargo bench`` scores, optionally formatted using
`cargo-benchcmp`_
.. _`cargo-benchcmp`: https://github.com/BurntSushi/cargo-benchcmp
Implementing New Algorithms
...........................
Implementing new graph algorithms is encouraged!
If you're going to implement a new algorithm, make sure that you do the
following:
* Add a ``quickcheck`` property test for the new algorithm
* Document what the algorithm does and in what situations it should be used
* Document the big-O running time of the algorithm
* Include links to relevant reading materials, such as a paper or Wikipedia
* Make the algorithm work with generic graphs, constraining the generic graph
type parameter with our existing graph traits, like ``Visitable``, or with new
graph traits
Any team_ member can review a pull request implementing a new algorithm, but the
final decision whether or not the algorithm is appropriate for inclusion in the
``petgraph`` crate is left to ``@bluss``.
Additionally, assuming that the new algorithm is merged into ``petgraph``, you
are *strongly* encouraged to join the ``petgraph`` team_! *You* are the best
person to review any future bug fixes, performance improvements, and whatever
other changes that affect this new algorithm.
Where We Need Help
------------------
* Issues labeled `"help wanted"`_ are issues where we could use a little help
from you.
* Issues Labeled `"mentored"`_ are issues that don't really involve any more
investigation, just implementation. We've outlined what needs to be done, and
a team_ member has volunteered to help whoever claims the issue implement it,
and get the implementation merged.
.. _`"help wanted"`:
https://github.com/bluss/petgraph/issues?q=is%3Aopen+is%3Aissue+label%3A%22help+wanted%22
.. _`"mentored"`:
https://github.com/bluss/petgraph/issues?q=is%3Aopen+is%3Aissue+label%3A%22mentored%22
Team
----
The ``petgraph`` team consists of:
* ``@bluss``
* ``@fitzgen``
We need more team members to help spread out reviewing and maintenance
responsibilities — want to join us? `Drop a comment in this issue!`_
.. _`Drop a comment in this issue!`: https://github.com/bluss/petgraph/issues/TODO
petgraph-0.5.1/Cargo.toml 0000644 00000003603 13662222312 0010746 0 ustar 00 # THIS FILE IS AUTOMATICALLY GENERATED BY CARGO
#
# When uploading crates to the registry Cargo will automatically
# "normalize" Cargo.toml files for maximal compatibility
# with all versions of Cargo and also rewrite `path` dependencies
# to registry (e.g., crates.io) dependencies
#
# If you believe there's an error in this file please file an
# issue against the rust-lang/cargo repository. If you're
# editing this file be aware that the upstream Cargo.toml
# will likely look very different (and much more reasonable)
[package]
edition = "2018"
name = "petgraph"
version = "0.5.1"
authors = ["bluss", "mitchmindtree"]
description = "Graph data structure library. Provides graph types and graph algorithms."
documentation = "https://docs.rs/petgraph/"
keywords = ["data-structure", "graph", "unionfind", "graph-algorithms"]
categories = ["data-structures"]
license = "MIT/Apache-2.0"
repository = "https://github.com/petgraph/petgraph"
[package.metadata.docs.rs]
features = ["serde-1", "quickcheck"]
[package.metadata.release]
no-dev-version = true
[profile.bench]
debug = true
[profile.release]
[lib]
name = "petgraph"
bench = false
[dependencies.fixedbitset]
version = "0.2.0"
default-features = false
[dependencies.indexmap]
version = "1.0.2"
[dependencies.quickcheck]
version = "0.8"
optional = true
default-features = false
[dependencies.serde]
version = "1.0"
optional = true
[dependencies.serde_derive]
version = "1.0"
optional = true
[dev-dependencies.defmac]
version = "0.1"
[dev-dependencies.itertools]
version = "0.8"
default-features = false
[dev-dependencies.odds]
version = "0.2.19"
[dev-dependencies.rand]
version = "0.5.5"
[features]
all = ["unstable", "quickcheck", "matrix_graph", "stable_graph", "graphmap"]
default = ["graphmap", "stable_graph", "matrix_graph"]
generate = []
graphmap = []
matrix_graph = []
serde-1 = ["serde", "serde_derive"]
stable_graph = []
unstable = ["generate"]
petgraph-0.5.1/Cargo.toml.orig 0100644 0001750 0001750 00000002624 13662135703 0014451 0 ustar 00 0000000 0000000 [package]
name = "petgraph"
version = "0.5.1"
license = "MIT/Apache-2.0"
authors = [
"bluss",
"mitchmindtree",
]
description = "Graph data structure library. Provides graph types and graph algorithms."
documentation = "https://docs.rs/petgraph/"
repository = "https://github.com/petgraph/petgraph"
keywords = ["data-structure", "graph", "unionfind", "graph-algorithms"]
categories = ["data-structures"]
edition = "2018"
[lib]
name = "petgraph"
bench = false
[profile.release]
[profile.bench]
debug = true
[dependencies]
fixedbitset = { version = "0.2.0", default-features = false }
quickcheck = { optional = true, version = "0.8", default-features = false }
indexmap = { version = "1.0.2" }
serde = { version = "1.0", optional = true }
serde_derive = { version = "1.0", optional = true }
[dev-dependencies]
rand = "0.5.5"
odds = { version = "0.2.19" }
defmac = "0.1"
itertools = { version = "0.8", default-features = false }
[features]
default = ["graphmap", "stable_graph", "matrix_graph"]
graphmap = []
serde-1 = ["serde", "serde_derive"]
stable_graph = []
matrix_graph = []
# For unstable features
generate = []
unstable = ["generate"]
# feature flags for testing use only
all = ["unstable", "quickcheck", "matrix_graph", "stable_graph", "graphmap"]
[workspace]
members = ["serialization-tests"]
[package.metadata.docs.rs]
features = ["serde-1", "quickcheck"]
[package.metadata.release]
no-dev-version = true
petgraph-0.5.1/LICENSE-APACHE 0100644 0001750 0001750 00000025137 13577670250 0013520 0 ustar 00 0000000 0000000 Apache License
Version 2.0, January 2004
http://www.apache.org/licenses/
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
1. Definitions.
"License" shall mean the terms and conditions for use, reproduction,
and distribution as defined by Sections 1 through 9 of this document.
"Licensor" shall mean the copyright owner or entity authorized by
the copyright owner that is granting the License.
"Legal Entity" shall mean the union of the acting entity and all
other entities that control, are controlled by, or are under common
control with that entity. For the purposes of this definition,
"control" means (i) the power, direct or indirect, to cause the
direction or management of such entity, whether by contract or
otherwise, or (ii) ownership of fifty percent (50%) or more of the
outstanding shares, or (iii) beneficial ownership of such entity.
"You" (or "Your") shall mean an individual or Legal Entity
exercising permissions granted by this License.
"Source" form shall mean the preferred form for making modifications,
including but not limited to software source code, documentation
source, and configuration files.
"Object" form shall mean any form resulting from mechanical
transformation or translation of a Source form, including but
not limited to compiled object code, generated documentation,
and conversions to other media types.
"Work" shall mean the work of authorship, whether in Source or
Object form, made available under the License, as indicated by a
copyright notice that is included in or attached to the work
(an example is provided in the Appendix below).
"Derivative Works" shall mean any work, whether in Source or Object
form, that is based on (or derived from) the Work and for which the
editorial revisions, annotations, elaborations, or other modifications
represent, as a whole, an original work of authorship. For the purposes
of this License, Derivative Works shall not include works that remain
separable from, or merely link (or bind by name) to the interfaces of,
the Work and Derivative Works thereof.
"Contribution" shall mean any work of authorship, including
the original version of the Work and any modifications or additions
to that Work or Derivative Works thereof, that is intentionally
submitted to Licensor for inclusion in the Work by the copyright owner
or by an individual or Legal Entity authorized to submit on behalf of
the copyright owner. For the purposes of this definition, "submitted"
means any form of electronic, verbal, or written communication sent
to the Licensor or its representatives, including but not limited to
communication on electronic mailing lists, source code control systems,
and issue tracking systems that are managed by, or on behalf of, the
Licensor for the purpose of discussing and improving the Work, but
excluding communication that is conspicuously marked or otherwise
designated in writing by the copyright owner as "Not a Contribution."
"Contributor" shall mean Licensor and any individual or Legal Entity
on behalf of whom a Contribution has been received by Licensor and
subsequently incorporated within the Work.
2. Grant of Copyright License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
copyright license to reproduce, prepare Derivative Works of,
publicly display, publicly perform, sublicense, and distribute the
Work and such Derivative Works in Source or Object form.
3. Grant of Patent License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
(except as stated in this section) patent license to make, have made,
use, offer to sell, sell, import, and otherwise transfer the Work,
where such license applies only to those patent claims licensable
by such Contributor that are necessarily infringed by their
Contribution(s) alone or by combination of their Contribution(s)
with the Work to which such Contribution(s) was submitted. If You
institute patent litigation against any entity (including a
cross-claim or counterclaim in a lawsuit) alleging that the Work
or a Contribution incorporated within the Work constitutes direct
or contributory patent infringement, then any patent licenses
granted to You under this License for that Work shall terminate
as of the date such litigation is filed.
4. Redistribution. You may reproduce and distribute copies of the
Work or Derivative Works thereof in any medium, with or without
modifications, and in Source or Object form, provided that You
meet the following conditions:
(a) You must give any other recipients of the Work or
Derivative Works a copy of this License; and
(b) You must cause any modified files to carry prominent notices
stating that You changed the files; and
(c) You must retain, in the Source form of any Derivative Works
that You distribute, all copyright, patent, trademark, and
attribution notices from the Source form of the Work,
excluding those notices that do not pertain to any part of
the Derivative Works; and
(d) If the Work includes a "NOTICE" text file as part of its
distribution, then any Derivative Works that You distribute must
include a readable copy of the attribution notices contained
within such NOTICE file, excluding those notices that do not
pertain to any part of the Derivative Works, in at least one
of the following places: within a NOTICE text file distributed
as part of the Derivative Works; within the Source form or
documentation, if provided along with the Derivative Works; or,
within a display generated by the Derivative Works, if and
wherever such third-party notices normally appear. The contents
of the NOTICE file are for informational purposes only and
do not modify the License. You may add Your own attribution
notices within Derivative Works that You distribute, alongside
or as an addendum to the NOTICE text from the Work, provided
that such additional attribution notices cannot be construed
as modifying the License.
You may add Your own copyright statement to Your modifications and
may provide additional or different license terms and conditions
for use, reproduction, or distribution of Your modifications, or
for any such Derivative Works as a whole, provided Your use,
reproduction, and distribution of the Work otherwise complies with
the conditions stated in this License.
5. Submission of Contributions. Unless You explicitly state otherwise,
any Contribution intentionally submitted for inclusion in the Work
by You to the Licensor shall be under the terms and conditions of
this License, without any additional terms or conditions.
Notwithstanding the above, nothing herein shall supersede or modify
the terms of any separate license agreement you may have executed
with Licensor regarding such Contributions.
6. Trademarks. This License does not grant permission to use the trade
names, trademarks, service marks, or product names of the Licensor,
except as required for reasonable and customary use in describing the
origin of the Work and reproducing the content of the NOTICE file.
7. Disclaimer of Warranty. Unless required by applicable law or
agreed to in writing, Licensor provides the Work (and each
Contributor provides its Contributions) on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
implied, including, without limitation, any warranties or conditions
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
PARTICULAR PURPOSE. You are solely responsible for determining the
appropriateness of using or redistributing the Work and assume any
risks associated with Your exercise of permissions under this License.
8. Limitation of Liability. In no event and under no legal theory,
whether in tort (including negligence), contract, or otherwise,
unless required by applicable law (such as deliberate and grossly
negligent acts) or agreed to in writing, shall any Contributor be
liable to You for damages, including any direct, indirect, special,
incidental, or consequential damages of any character arising as a
result of this License or out of the use or inability to use the
Work (including but not limited to damages for loss of goodwill,
work stoppage, computer failure or malfunction, or any and all
other commercial damages or losses), even if such Contributor
has been advised of the possibility of such damages.
9. Accepting Warranty or Additional Liability. While redistributing
the Work or Derivative Works thereof, You may choose to offer,
and charge a fee for, acceptance of support, warranty, indemnity,
or other liability obligations and/or rights consistent with this
License. However, in accepting such obligations, You may act only
on Your own behalf and on Your sole responsibility, not on behalf
of any other Contributor, and only if You agree to indemnify,
defend, and hold each Contributor harmless for any liability
incurred by, or claims asserted against, such Contributor by reason
of your accepting any such warranty or additional liability.
END OF TERMS AND CONDITIONS
APPENDIX: How to apply the Apache License to your work.
To apply the Apache License to your work, attach the following
boilerplate notice, with the fields enclosed by brackets "[]"
replaced with your own identifying information. (Don't include
the brackets!) The text should be enclosed in the appropriate
comment syntax for the file format. We also recommend that a
file or class name and description of purpose be included on the
same "printed page" as the copyright notice for easier
identification within third-party archives.
Copyright [yyyy] [name of copyright owner]
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
petgraph-0.5.1/LICENSE-MIT 0100644 0001750 0001750 00000002023 13577670250 0013215 0 ustar 00 0000000 0000000 Copyright (c) 2015
Permission is hereby granted, free of charge, to any
person obtaining a copy of this software and associated
documentation files (the "Software"), to deal in the
Software without restriction, including without
limitation the rights to use, copy, modify, merge,
publish, distribute, sublicense, and/or sell copies of
the Software, and to permit persons to whom the Software
is furnished to do so, subject to the following
conditions:
The above copyright notice and this permission notice
shall be included in all copies or substantial portions
of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF
ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED
TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT
SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR
IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
DEALINGS IN THE SOFTWARE.
petgraph-0.5.1/Makefile 0100644 0001750 0001750 00000001726 13577670250 0013232 0 ustar 00 0000000 0000000 DOCCRATES = petgraph fixedbitset
# deps to delete the generated docs
RMDOCS =
FEATURES = unstable
VERSIONS = $(patsubst %,target/VERS/%,$(DOCCRATES))
docs: mkdocs mksvgs subst $(RMDOCS)
# https://www.gnu.org/software/make/manual/html_node/Automatic-Variables.html
$(VERSIONS): Cargo.toml
mkdir -p $(@D)
cargo pkgid $(@F) | sed -e "s/.*#\(\|.*:\)//" > "$@"
$(DOCCRATES): %: target/VERS/%
# Put in the crate version into the docs
find ./doc/$@ -name "*.html" -exec sed -i -e "s/
\(.*\) - Rust/$@ $(shell cat $<) - \1 - Rust/g" {} \;
subst: $(DOCCRATES)
mkdocs: Cargo.toml
cargo doc --features=$(FEATURES)
rm -rf ./doc
cp -r ./target/doc ./doc
- cat ./custom.css >> doc/main.css
$(RMDOCS): mkdocs
rm -r ./doc/$@
sed -i "/searchIndex\['$@'\]/d" doc/search-index.js
mksvgs: mkdocs graph-example.dot
dot -Tsvg < ./graph-example.dot > graph-example.svg
mv graph-example.svg ./doc/petgraph/graph/
.PHONY: docs mkdocs mksvgs subst $(DOCCRATES) $(RMDOCS)
petgraph-0.5.1/README.rst 0100644 0001750 0001750 00000032237 13662135054 0013253 0 ustar 00 0000000 0000000
petgraph
========
Graph data structure library. Known to support Rust 1.37 and later.
Please read the `API documentation here`__
__ https://docs.rs/petgraph/
|build_status|_ |crates|_
.. |build_status| image:: https://travis-ci.org/petgraph/petgraph.svg?branch=master
.. _build_status: https://travis-ci.org/petgraph/petgraph
.. |crates| image:: http://meritbadge.herokuapp.com/petgraph
.. _crates: https://crates.io/crates/petgraph
Crate feature flags:
- ``graphmap`` (default) enable ``GraphMap``.
- ``stable_graph`` (default) enable ``StableGraph``.
- ``matrix_graph`` (default) enable ``MatrixGraph``.
- ``serde-1`` (optional) enable serialization for ``Graph, StableGraph`` using
serde 1.0. Requires Rust version as required by serde.
Recent Changes
--------------
- 0.5.1
- Implement ``Default`` for traversals.
- Export ``EdgesConnecting`` publicly.
- Implement ``is_bipartite_graph``.
- Add ``FilterNode`` implementation for ``FixedBitSet`` and ``HashSet``.
- Implement ``node_weights_mut`` and ``edge_weights_mut`` for ``StableGraph``.
- Add configurable functions for adding attributes to dotfile features.
- 0.5.0
- Breaking changes:
- The iterative DFS implementation, ``Dfs``, now marks nodes visited when
they are pushed onto the stack, not when they're popped off. This may
require changes to callers that use ``Dfs::from_parts`` or manipulate
its internals.
- The ``IntoEdgesDirected`` trait now has a stricter contract for
undirected graphs. Custom implementations of this trait may have to be
updated. See the `trait documentation`__ for more.
- Other changes:
- Upgrade to Rust 2018 edition
- Fix clippy warnings and unify code formatting
- Improved and enhanced documentation
- Update dependencies including modern quickcheck
- Numerous bugfixes and refactorings
- Added ``MatrixGraph`` implementation
__ https://docs.rs/petgraph/0.5/petgraph/visit/trait.IntoEdgesDirected.html
- 0.4.13
- Fix clippy warnings by @jonasbb
- Add docs for ``Csr`` by @ksadorf
- Fix conflict with new stable method ``find_map`` in new Rust
- 0.4.12
- Newtype ``Time`` now also implements ``Hash``
- Documentation updates for ``Frozen``.
- 0.4.11
- Fix ``petgraph::graph::NodeReferences`` to be publicly visible
- Small doc typo and code style files by @shepmaster and @waywardmonkeys
- Fix a future compat warning with pointer casts
- 0.4.10
- Add graph trait ``IntoEdgesDirected``
- Update dependencies
- 0.4.9
- Fix ``bellman_ford`` to work correctly with undirected graphs (#152) by
@carrutstick
- Performance improvements for ``Graph, Stablegraph``'s ``.map()``.
- 0.4.8
- ``StableGraph`` learned new methods nearing parity with ``Graph``. Note
that the ``StableGraph`` methods preserve index stability even in the batch
removal methods like ``filter_map`` and ``retain_edges``.
+ Added ``.filter_map()``, which maps associated node and edge data
+ Added ``.retain_edges()``, ``.edge_indices()`` and ``.clear_edges()``
- Existing ``Graph`` iterators gained some trait impls:
+ ``.node_indices(), .edge_indices()`` are ``ExactSizeIterator``
+ ``.node_references()`` is now
``DoubleEndedIterator + ExactSizeIterator``.
+ ``.edge_references()`` is now ``ExactSizeIterator``.
- Implemented ``From`` for ``Graph``.
- 0.4.7
- New algorithm by @jmcomets: A* search algorithm in ``petgraph::algo::astar``
- One ``StableGraph`` bug fix whose patch was supposed to be in the previous
version:
+ ``add_edge(m, n, _)`` now properly always panics if nodes m or n don't
exist in the graph.
- 0.4.6
- New optional crate feature: ``"serde-1"``, which enables serialization
for ``Graph`` and ``StableGraph`` using serde.
- Add methods ``new``, ``add_node`` to ``Csr`` by @jmcomets
- Add indexing with ``[]`` by node index, ``NodeCompactIndexable`` for
``Csr`` by @jmcomets
- Amend doc for ``GraphMap::into_graph`` (it has a case where it can panic)
- Add implementation of ``From`` for ``StableGraph``.
- Add implementation of ``IntoNodeReferences`` for ``&StableGraph``.
- Add method ``StableGraph::map`` that maps associated data
- Add method ``StableGraph::find_edge_undirected``
- Many ``StableGraph`` bug fixes involving node vacancies (holes left by
deletions):
+ ``neighbors(n)`` and similar neighbor and edge iterator methods now
handle n being a vacancy properly. (This produces an empty iterator.)
+ ``find_edge(m, n)`` now handles m being a vacancy correctly too
+ ``StableGraph::node_bound`` was fixed for empty graphs and returns 0
- Add implementation of ``DoubleEndedIterator`` to ``Graph, StableGraph``'s
edge references iterators.
- Debug output for ``Graph`` now shows node and edge count. ``Graph, StableGraph``
show nothing for the edges list if it's empty (no label).
- ``Arbitrary`` implementation for ``StableGraph`` now can produce graphs with
vacancies (used by quickcheck)
- 0.4.5
- Fix ``max`` ambiguity error with current rust nightly by @daboross (#153)
- 0.4.4
- Add ``GraphMap::all_edges_mut()`` iterator by @Binero
- Add ``StableGraph::retain_nodes`` by @Rupsbant
- Add ``StableGraph::index_twice_mut`` by @christolliday
- 0.4.3
- Add crate categories
- 0.4.2
- Move the ``visit.rs`` file due to changed rules for a module’s directory
ownership in Rust, resolving a future compat warning.
- The error types ``Cycle, NegativeCycle`` now implement ``PartialEq``.
- 0.4.1
- Add new algorithm ``simple_fast`` for computing dominators in a control-flow
graph.
- 0.4.0
- Breaking changes in ``Graph``:
- ``Graph::edges`` and the other edges methods now return an iterator of
edge references
- Other breaking changes:
- ``toposort`` now returns an error if the graph had a cycle.
- ``is_cyclic_directed`` no longer takes a dfs space argument. It is
now recursive.
- ``scc`` was renamed to ``kosaraju_scc``.
- ``min_spanning_tree`` now returns an iterator that needs to be
made into a specific graph type deliberately.
- ``dijkstra`` now uses the ``IntoEdges`` trait.
- ``NodeIndexable`` changed its method signatures.
- ``IntoExternals`` was removed, and many other smaller adjustments
in graph traits. ``NodeId`` must now implement ``PartialEq``, for example.
- ``DfsIter, BfsIter`` were removed in favour of a more general approach
with the ``Walker`` trait and its iterator conversion.
- New features:
- New graph traits, for example ``IntoEdges`` which returns
an iterator of edge references. Everything implements the graph traits
much more consistently.
- Traits for associated data access and building graphs: ``DataMap``,
``Build, Create, FromElements``.
- Graph adaptors: ``EdgeFiltered``. ``Filtered`` was renamed to ``NodeFiltered``.
- New algorithms: bellman-ford
- New graph: compressed sparse row (``Csr``).
- ``GraphMap`` implements ``NodeIndexable``.
- ``Dot`` was generalized
- 0.3.2
- Add ``depth_first_search``, a recursive dfs visitor that emits discovery,
finishing and edge classification events.
- Add graph adaptor ``Filtered``.
- impl ``Debug, NodeIndexable`` for ``Reversed``.
- 0.3.1
- Add ``.edges(), .edges_directed()`` to ``StableGraph``. Note that these
differ from ``Graph``, because this is the signature they will all use
in the future.
- Add ``.update_edge()`` to ``StableGraph``.
- Add reexports of common items in ``stable_graph`` module (for example
``NodeIndex``).
- Minor performance improvements to graph iteration
- Improved docs for ``visit`` module.
- 0.3.0
- Overhaul all graph visitor traits so that they use the ``IntoIterator``
style. This makes them composable.
- Multiple graph algorithms use new visitor traits.
- **Help is welcome to port more algorithms (and create new graph traits in
the process)!**
- ``GraphMap`` can now have directed edges. ``GraphMap::new`` is now generic
in the edge type. ``DiGraphMap`` and ``UnGraphMap`` are new type aliases.
- Add type aliases ``DiGraph, UnGraph, StableDiGraph, StableUnGraph``
- ``GraphMap`` is based on the indexmap crate. Deterministic iteration
order, faster iteration, no side tables needed to convert to ``Graph``.
- Improved docs for a lot of types and functions.
- Add graph visitor ``DfsPostOrder``
- ``Dfs`` gained new methods ``from_parts`` and ``reset``.
- New algo ``has_path_connecting``.
- New algo ``tarjan_scc``, a second scc implementation.
- Document traversal order in ``Dfs, DfsPostOrder, scc, tarjan_scc``.
- Optional graph visitor workspace reuse in ``has_path_connecting``,
``is_cyclic_directed, toposort``.
- Improved ``Debug`` formatting for ``Graph, StableGraph``.
- Add a prelude module
- ``GraphMap`` now has a method ``.into_graph()`` that makes a ``Graph``.
- ``Graph::retain_nodes, retain_edges`` now expose the self graph only
as wrapped in ``Frozen``, so that weights can be mutated but the
graph structure not.
- Enable ``StableGraph`` by default
- Add method ``Graph::contains_edge``.
- Renamed ``EdgeDirection`` → ``Direction``.
- Remove ``SubTopo``.
- Require Rust 1.12 or later
- 0.2.10
- Fix compilation with rust nightly
- 0.2.9
- Fix a bug in SubTopo (#81)
- 0.2.8
- Add Graph methods reserve_nodes, reserve_edges, reserve_exact_nodes,
reserve_exact_edges, shrink_to_fit_edges, shrink_to_fit_nodes, shrink_to_fit
- 0.2.7
- Update URLs
- 0.2.6
- Fix warning about type parameter defaults (no functional change)
- 0.2.5
- Add SubTopo, a topo walker for the subgraph reachable from a starting point.
- Add condensation, which forms the graph of a graph’s strongly connected
components.
- 0.2.4
- Fix an algorithm error in scc (#61). This time we have a test that
crosschecks the result of the algorithm vs another implementation, for
greater confidence in its correctness.
- 0.2.3
- Require Rust 1.6: Due to changes in how rust uses type parameter defaults.
- Implement Graph::clone_from.
- 0.2.2
- Require Rust 1.5
- ``Dot`` passes on the alternate flag to node and edge label formatting
- Add ``Clone`` impl for some iterators
- Document edge iteration order for ``Graph::neighbors``
- Add *experimental feature* ``StableGraph``, using feature flag ``stable_graph``
- 0.2.1
- Add algorithm ``is_isomorphic_matching``
- 0.2.0
- New Features
- Add Graph::neighbors().detach() to step edges without borrowing.
This is more general than, and replaces now deprecated
walk_edges_directed. (#39)
- Implement Default for Graph, GraphMap
- Add method EdgeDirection::opposite()
- Breaking changes
- Graph::neighbors() for undirected graphs and Graph::neighbors_undirected
for any graph now visit self loop edges once, not twice. (#31)
- Renamed Graph::without_edges to Graph::externals
- Removed Graph::edges_both
- GraphMap::add_edge now returns ``Option``
- Element type of ``GraphMap::all_edges()`` changed to ``(N, N, &E)``
- Minor breaking changes
- IntoWeightedEdge changed a type parameter to associated type
- IndexType is now an unsafe trait
- Removed IndexType::{one, zero}, use method new instead.
- Removed MinScored
- Ptr moved to the graphmap module.
- Directed, Undirected are now void enums.
- Fields of graphmap::Edges are now private (#19)
- 0.1.18
- Fix bug on calling GraphMap::add_edge with existing edge (#35)
- 0.1.17
- Add Graph::capacity(), GraphMap::capacity()
- Fix bug in Graph::reverse()
- Graph and GraphMap have `quickcheck::Arbitrary` implementations,
if optional feature `check` is enabled.
- 0.1.16
- Add Graph::node_indices(), Graph::edge_indices()
- Add Graph::retain_nodes(), Graph::retain_edges()
- Add Graph::extend_with_edges(), Graph::from_edges()
- Add functions petgraph::graph::{edge_index, node_index};
- Add GraphMap::extend(), GraphMap::from_edges()
- Add petgraph::dot::Dot for simple graphviz dot output
- 0.1.15
- Add Graph::clear_edges()
- Add Graph::edge_endpoints()
- Add Graph::map() and Graph::filter_map()
- 0.1.14
- Add new topological order visitor Topo
- New graph traits NeighborsDirected, Externals, Revisitable
- 0.1.13
- Add iterator GraphMap::all_edges
- 0.1.12
- Fix an algorithm error in scc (#14)
- 0.1.11
- Update for well-formedness warnings (Rust RFC 1214), adding
new lifetime bounds on NeighborIter and Dfs, impact should be minimal.
- 0.1.10
- Fix bug in WalkEdges::next_neighbor()
- 0.1.9
- Fix Dfs/Bfs for a rustc bugfix that disallowed them
- Add method next_neighbor() to WalkEdges
- 0.1.8
- Add Graph::walk_edges_directed()
- Add Graph::index_twice_mut()
- 0.1.7
- Add Graph::edges_directed()
- 0.1.6
- Add Graph::node_weights_mut and Graph::edge_weights_mut
- 0.1.4
- Add back DfsIter, BfsIter
License
-------
Dual-licensed to be compatible with the Rust project.
Licensed under the Apache License, Version 2.0
http://www.apache.org/licenses/LICENSE-2.0 or the MIT license
http://opensource.org/licenses/MIT, at your
option. This file may not be copied, modified, or distributed
except according to those terms.
petgraph-0.5.1/benches/common/factories.rs 0100644 0001750 0001750 00000020633 13601260027 0016775 0 ustar 00 0000000 0000000 use std::marker::PhantomData;
use petgraph::data::Build;
use petgraph::prelude::*;
use petgraph::visit::NodeIndexable;
use petgraph::EdgeType;
/// Petersen A and B are isomorphic
///
/// http://www.dharwadker.org/tevet/isomorphism/
const PETERSEN_A: &str = "
0 1 0 0 1 0 1 0 0 0
1 0 1 0 0 0 0 1 0 0
0 1 0 1 0 0 0 0 1 0
0 0 1 0 1 0 0 0 0 1
1 0 0 1 0 1 0 0 0 0
0 0 0 0 1 0 0 1 1 0
1 0 0 0 0 0 0 0 1 1
0 1 0 0 0 1 0 0 0 1
0 0 1 0 0 1 1 0 0 0
0 0 0 1 0 0 1 1 0 0
";
const PETERSEN_B: &str = "
0 0 0 1 0 1 0 0 0 1
0 0 0 1 1 0 1 0 0 0
0 0 0 0 0 0 1 1 0 1
1 1 0 0 0 0 0 1 0 0
0 1 0 0 0 0 0 0 1 1
1 0 0 0 0 0 1 0 1 0
0 1 1 0 0 1 0 0 0 0
0 0 1 1 0 0 0 0 1 0
0 0 0 0 1 1 0 1 0 0
1 0 1 0 1 0 0 0 0 0
";
/// An almost full set, isomorphic
const FULL_A: &str = "
1 1 1 1 1 1 1 1 1 1
1 1 1 1 1 1 1 1 1 1
1 1 1 1 1 1 1 1 1 1
1 1 1 1 1 1 1 1 1 1
1 1 1 1 1 1 1 1 1 1
1 1 1 1 1 1 1 1 1 1
1 1 1 1 1 1 1 1 1 1
1 1 1 1 1 1 1 1 1 1
1 1 1 1 0 1 1 1 0 1
1 1 1 1 1 1 1 1 1 1
";
const FULL_B: &str = "
1 1 1 1 1 1 1 1 1 1
1 1 1 1 1 1 1 1 1 1
1 1 0 1 1 1 0 1 1 1
1 1 1 1 1 1 1 1 1 1
1 1 1 1 1 1 1 1 1 1
1 1 1 1 1 1 1 1 1 1
1 1 1 1 1 1 1 1 1 1
1 1 1 1 1 1 1 1 1 1
1 1 1 1 1 1 1 1 1 1
1 1 1 1 1 1 1 1 1 1
";
/// Praust A and B are not isomorphic
const PRAUST_A: &str = "
0 1 1 1 1 0 0 0 1 0 0 0 0 0 0 0 0 0 0 0
1 0 1 1 0 1 0 0 0 1 0 0 0 0 0 0 0 0 0 0
1 1 0 1 0 0 1 0 0 0 1 0 0 0 0 0 0 0 0 0
1 1 1 0 0 0 0 1 0 0 0 1 0 0 0 0 0 0 0 0
1 0 0 0 0 1 1 1 0 0 0 0 1 0 0 0 0 0 0 0
0 1 0 0 1 0 1 1 0 0 0 0 0 1 0 0 0 0 0 0
0 0 1 0 1 1 0 1 0 0 0 0 0 0 1 0 0 0 0 0
0 0 0 1 1 1 1 0 0 0 0 0 0 0 0 1 0 0 0 0
1 0 0 0 0 0 0 0 0 1 1 1 0 0 0 0 1 0 0 0
0 1 0 0 0 0 0 0 1 0 1 1 0 0 0 0 0 1 0 0
0 0 1 0 0 0 0 0 1 1 0 1 0 0 0 0 0 0 1 0
0 0 0 1 0 0 0 0 1 1 1 0 0 0 0 0 0 0 0 1
0 0 0 0 1 0 0 0 0 0 0 0 0 1 1 1 0 1 0 0
0 0 0 0 0 1 0 0 0 0 0 0 1 0 1 1 1 0 0 0
0 0 0 0 0 0 1 0 0 0 0 0 1 1 0 1 0 0 0 1
0 0 0 0 0 0 0 1 0 0 0 0 1 1 1 0 0 0 1 0
0 0 0 0 0 0 0 0 1 0 0 0 0 1 0 0 0 1 1 1
0 0 0 0 0 0 0 0 0 1 0 0 1 0 0 0 1 0 1 1
0 0 0 0 0 0 0 0 0 0 1 0 0 0 0 1 1 1 0 1
0 0 0 0 0 0 0 0 0 0 0 1 0 0 1 0 1 1 1 0
";
const PRAUST_B: &str = "
0 1 1 1 1 0 0 0 1 0 0 0 0 0 0 0 0 0 0 0
1 0 1 1 0 1 0 0 0 1 0 0 0 0 0 0 0 0 0 0
1 1 0 1 0 0 1 0 0 0 1 0 0 0 0 0 0 0 0 0
1 1 1 0 0 0 0 1 0 0 0 1 0 0 0 0 0 0 0 0
1 0 0 0 0 1 1 1 0 0 0 0 1 0 0 0 0 0 0 0
0 1 0 0 1 0 1 1 0 0 0 0 0 0 0 0 0 0 0 1
0 0 1 0 1 1 0 1 0 0 0 0 0 0 1 0 0 0 0 0
0 0 0 1 1 1 1 0 0 0 0 0 0 0 0 0 0 1 0 0
1 0 0 0 0 0 0 0 0 1 1 1 0 0 0 0 1 0 0 0
0 1 0 0 0 0 0 0 1 0 1 1 0 1 0 0 0 0 0 0
0 0 1 0 0 0 0 0 1 1 0 1 0 0 0 0 0 0 1 0
0 0 0 1 0 0 0 0 1 1 1 0 0 0 0 1 0 0 0 0
0 0 0 0 1 0 0 0 0 0 0 0 0 1 1 0 0 1 0 1
0 0 0 0 0 0 0 0 0 1 0 0 1 0 0 1 1 0 1 0
0 0 0 0 0 0 1 0 0 0 0 0 1 0 0 1 0 1 0 1
0 0 0 0 0 0 0 0 0 0 0 1 0 1 1 0 1 0 1 0
0 0 0 0 0 0 0 0 1 0 0 0 0 1 0 1 0 1 1 0
0 0 0 0 0 0 0 1 0 0 0 0 1 0 1 0 1 0 0 1
0 0 0 0 0 0 0 0 0 0 1 0 0 1 0 1 1 0 0 1
0 0 0 0 0 1 0 0 0 0 0 0 1 0 1 0 0 1 1 0
";
const BIGGER: &str = "
0 0 0 0 0 0 0 0 1 0 1 0 0 0 0 0 0 0 1 1 0 1 1 1 1 0 0 0 1 0 0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 1 0 0 0 1 1 0 0 0 0 0 0 0 1 0 1 0 1 1 0 1 0 0 0 1 0 0 0 0 0 0 0 0 0 0
0 1 0 1 0 0 0 0 0 0 1 0 0 0 0 0 0 0 0 0 1 1 0 1 0 0 1 0 0 0 1 0 0 0 0 0 0 0 0 0
0 0 0 0 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
0 0 0 1 0 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
0 0 0 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0 0 0 0 1 0 1 0 1 0 0 0 0 0 0 1 0 0 0 0 0 0 0 0 1 1 1 0 1 0 0
0 0 0 0 0 1 0 0 0 0 0 0 1 0 0 1 1 0 0 0 0 0 0 0 0 1 0 0 0 0 0 0 1 0 1 1 1 0 0 0
0 0 0 0 0 0 1 0 0 0 0 0 1 1 0 1 0 0 0 1 0 0 0 0 0 0 1 0 0 0 0 0 1 1 0 1 0 0 0 1
0 0 0 0 0 0 0 1 0 0 0 0 1 1 1 0 0 0 1 0 0 0 0 0 0 0 0 1 0 0 0 0 1 1 1 0 0 0 1 0
0 0 0 0 0 0 0 0 0 0 0 0 0 1 0 0 0 1 1 1 0 1 1 0 0 0 0 0 1 0 0 0 0 1 0 0 0 1 1 1
0 0 0 0 1 0 0 0 0 0 0 0 1 0 0 0 1 0 1 1 0 1 1 1 0 0 0 0 0 1 0 0 1 0 0 0 1 0 1 1
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 0 0 0 0 0 0 0 0 0 0 1 0 0 0 0 1 1 1 0 1
0 0 0 0 0 0 0 0 0 0 0 1 0 0 1 0 1 1 1 1 0 0 0 0 0 0 0 0 0 0 0 1 0 0 1 0 1 1 1 0
0 1 1 0 1 0 0 0 1 0 0 0 0 0 0 0 0 0 0 0 1 1 1 1 1 0 0 0 1 0 0 0 0 0 0 0 0 0 0 0
1 0 1 0 0 1 0 0 0 1 0 0 0 0 0 0 0 1 1 0 1 0 1 1 0 1 0 0 0 1 0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
0 0 1 0 1 1 0 1 0 0 0 0 0 0 1 0 0 0 0 0 0 0 1 0 1 1 0 1 0 0 0 0 0 0 1 0 0 0 0 0
0 0 0 0 1 1 1 0 0 0 0 0 0 0 0 1 0 0 0 0 0 0 0 1 1 1 1 0 0 0 0 0 0 0 0 1 0 0 0 0
1 0 0 0 0 0 0 0 0 1 1 1 0 0 0 0 1 0 0 0 1 0 0 0 0 0 0 0 0 1 1 1 0 0 0 0 1 0 0 0
0 1 0 0 0 0 0 0 1 0 1 1 0 0 0 0 0 1 0 0 0 1 0 0 0 0 0 0 1 0 1 1 0 0 0 0 0 1 0 0
0 0 1 0 0 0 0 0 1 1 0 1 0 0 0 0 0 0 1 0 0 0 1 0 0 0 0 0 1 1 0 1 0 0 0 0 0 0 1 0
0 0 0 1 0 0 0 0 1 1 1 0 0 0 0 0 0 0 0 1 0 0 0 1 0 0 0 0 1 1 1 0 0 0 0 0 0 0 0 1
0 0 0 0 1 0 0 0 0 0 0 0 0 1 1 1 0 1 0 0 0 0 0 0 1 0 0 0 0 0 0 0 0 1 1 1 0 1 0 0
0 0 0 0 0 1 0 0 0 0 0 0 1 0 1 1 1 0 0 0 0 0 0 0 0 1 0 0 0 0 0 0 1 0 1 1 1 0 0 0
0 1 0 0 0 0 1 0 0 0 0 0 1 1 0 1 0 0 0 1 0 0 0 0 0 0 1 0 0 0 0 0 1 1 0 1 0 0 0 1
0 1 0 0 0 0 0 1 0 0 0 0 1 1 1 0 0 0 1 0 0 0 0 0 0 0 0 1 0 0 0 0 1 1 1 0 0 0 1 0
0 1 0 0 0 0 0 0 1 0 0 0 0 1 0 0 0 1 1 1 0 0 0 0 0 0 0 0 1 0 0 0 0 1 0 0 0 1 1 1
0 1 0 0 0 0 0 0 0 1 0 0 1 0 0 0 1 0 1 1 0 0 0 0 0 0 0 0 0 1 0 0 1 0 0 0 1 0 1 1
0 1 0 0 0 0 0 0 0 0 1 0 0 0 0 1 1 1 0 1 0 0 0 0 0 0 0 0 0 0 1 0 0 0 0 1 1 1 0 1
0 1 0 0 0 0 0 0 0 0 0 1 0 0 1 0 1 1 1 0 0 0 0 0 0 0 0 0 0 0 0 1 0 0 1 0 1 1 1 0
";
/// Parse a text adjacency matrix format into a directed graph
fn parse_graph(s: &str) -> G
where
Ty: EdgeType,
G: Default + Build + NodeIndexable,
{
let mut g: G = Default::default();
let s = s.trim();
let lines = s.lines().filter(|l| !l.is_empty());
for (row, line) in lines.enumerate() {
for (col, word) in line.split(' ').filter(|s| !s.is_empty()).enumerate() {
let has_edge = word.parse::().unwrap();
assert!(has_edge == 0 || has_edge == 1);
if has_edge == 0 {
continue;
}
while col >= g.node_count() || row >= g.node_count() {
g.add_node(());
}
let a = g.from_index(row);
let b = g.from_index(col);
g.update_edge(a, b, ());
}
}
g
}
pub struct GraphFactory> {
ty: PhantomData,
g: PhantomData,
}
impl GraphFactory
where
Ty: EdgeType,
G: Default + Build + NodeIndexable,
{
fn new() -> Self {
GraphFactory {
ty: PhantomData,
g: PhantomData,
}
}
pub fn petersen_a(self) -> G {
parse_graph::(PETERSEN_A)
}
pub fn petersen_b(self) -> G {
parse_graph::(PETERSEN_B)
}
pub fn full_a(self) -> G {
parse_graph::(FULL_A)
}
pub fn full_b(self) -> G {
parse_graph::(FULL_B)
}
pub fn praust_a(self) -> G {
parse_graph::(PRAUST_A)
}
pub fn praust_b(self) -> G {
parse_graph::(PRAUST_B)
}
pub fn bigger(self) -> G {
parse_graph::(BIGGER)
}
}
pub fn graph() -> GraphFactory> {
GraphFactory::new()
}
pub fn ungraph() -> GraphFactory> {
graph()
}
pub fn digraph() -> GraphFactory> {
graph()
}
pub fn stable_graph() -> GraphFactory> {
GraphFactory::new()
}
pub fn stable_ungraph() -> GraphFactory> {
stable_graph()
}
pub fn stable_digraph() -> GraphFactory> {
stable_graph()
}
petgraph-0.5.1/benches/common/mod.rs 0100644 0001750 0001750 00000000045 13577670507 0015614 0 ustar 00 0000000 0000000 mod factories;
pub use factories::*;
petgraph-0.5.1/benches/dijkstra.rs 0100644 0001750 0001750 00000001572 13662133536 0015355 0 ustar 00 0000000 0000000 #![feature(test)]
extern crate petgraph;
extern crate test;
use petgraph::prelude::*;
use std::cmp::{max, min};
use test::Bencher;
use petgraph::algo::dijkstra;
#[bench]
fn dijkstra_bench(bench: &mut Bencher) {
static NODE_COUNT: usize = 10_000;
let mut g = Graph::new_undirected();
let nodes: Vec> = (0..NODE_COUNT).into_iter().map(|i| g.add_node(i)).collect();
for i in 0..NODE_COUNT {
let n1 = nodes[i];
let neighbour_count = i % 8 + 3;
let j_from = max(0, i as i32 - neighbour_count as i32 / 2) as usize;
let j_to = min(NODE_COUNT, j_from + neighbour_count);
for j in j_from..j_to {
let n2 = nodes[j];
let distance = (i + 3) % 10;
g.add_edge(n1, n2, distance);
}
}
bench.iter(|| {
let _scores = dijkstra(&g, nodes[0], None, |e| *e.weight());
});
}
petgraph-0.5.1/benches/iso.rs 0100644 0001750 0001750 00000001777 13601260027 0014330 0 ustar 00 0000000 0000000 #![feature(test)]
extern crate petgraph;
extern crate test;
use test::Bencher;
#[allow(dead_code)]
mod common;
use common::*;
use petgraph::algo::is_isomorphic;
#[bench]
fn petersen_iso_bench(bench: &mut Bencher) {
let a = digraph().petersen_a();
let b = digraph().petersen_b();
bench.iter(|| is_isomorphic(&a, &b));
}
#[bench]
fn petersen_undir_iso_bench(bench: &mut Bencher) {
let a = ungraph().petersen_a();
let b = ungraph().petersen_b();
bench.iter(|| is_isomorphic(&a, &b));
}
#[bench]
fn full_iso_bench(bench: &mut Bencher) {
let a = ungraph().full_a();
let b = ungraph().full_b();
bench.iter(|| is_isomorphic(&a, &b));
}
#[bench]
fn praust_dir_no_iso_bench(bench: &mut Bencher) {
let a = digraph().praust_a();
let b = digraph().praust_b();
bench.iter(|| is_isomorphic(&a, &b));
}
#[bench]
fn praust_undir_no_iso_bench(bench: &mut Bencher) {
let a = ungraph().praust_a();
let b = ungraph().praust_b();
bench.iter(|| is_isomorphic(&a, &b));
}
petgraph-0.5.1/benches/matrix_graph.rs 0100644 0001750 0001750 00000017562 13601260027 0016222 0 ustar 00 0000000 0000000 #![feature(test)]
extern crate petgraph;
extern crate test;
use test::Bencher;
use petgraph::algo;
use petgraph::matrix_graph::{node_index, MatrixGraph};
use petgraph::{Directed, EdgeType, Incoming, Outgoing};
#[bench]
fn add_100_nodes(b: &mut test::Bencher) {
b.iter(|| {
let mut g = MatrixGraph::<(), ()>::with_capacity(100);
for _ in 0..100 {
let _ = g.add_node(());
}
});
}
#[bench]
fn add_100_edges_to_self(b: &mut test::Bencher) {
let mut g = MatrixGraph::<(), ()>::with_capacity(100);
let nodes: Vec<_> = (0..100).map(|_| g.add_node(())).collect();
let g = g;
b.iter(|| {
let mut g = g.clone();
for &node in nodes.iter() {
g.add_edge(node, node, ());
}
});
}
#[bench]
fn add_5_edges_for_each_of_100_nodes(b: &mut test::Bencher) {
let mut g = MatrixGraph::<(), ()>::with_capacity(100);
let nodes: Vec<_> = (0..100).map(|_| g.add_node(())).collect();
let g = g;
let edges_to_add: Vec<_> = nodes
.iter()
.enumerate()
.map(|(i, &node)| {
let edges: Vec<_> = (0..5)
.map(|j| (i + j + 1) % nodes.len())
.map(|j| (node, nodes[j]))
.collect();
edges
})
.flatten()
.collect();
b.iter(|| {
let mut g = g.clone();
for &(source, target) in edges_to_add.iter() {
g.add_edge(source, target, ());
}
});
}
#[bench]
fn add_edges_from_root(bench: &mut test::Bencher) {
bench.iter(|| {
let mut gr = MatrixGraph::new();
let a = gr.add_node(());
for _ in 0..100 {
let b = gr.add_node(());
gr.add_edge(a, b, ());
}
});
}
#[bench]
fn add_adjacent_edges(bench: &mut test::Bencher) {
bench.iter(|| {
let mut gr = MatrixGraph::new();
let mut prev = None;
for _ in 0..100 {
let b = gr.add_node(());
if let Some(a) = prev {
gr.add_edge(a, b, ());
}
prev = Some(b);
}
});
}
/// An almost full set
const FULL: &str = "
1 1 1 1 1 1 1 1 1 1
1 1 1 1 1 1 1 1 1 1
1 1 1 1 1 1 1 1 1 1
1 1 1 1 1 1 1 1 1 1
1 1 1 1 1 1 1 1 1 1
1 1 1 1 1 1 1 1 1 1
1 1 1 1 1 1 1 1 1 1
1 1 1 1 1 1 1 1 1 1
1 1 1 1 0 1 1 1 0 1
1 1 1 1 1 1 1 1 1 1
";
const BIGGER: &str = "
0 0 0 0 0 0 0 0 1 0 1 0 0 0 0 0 0 0 1 1 0 1 1 1 1 0 0 0 1 0 0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 1 0 0 0 1 1 0 0 0 0 0 0 0 1 0 1 0 1 1 0 1 0 0 0 1 0 0 0 0 0 0 0 0 0 0
0 1 0 1 0 0 0 0 0 0 1 0 0 0 0 0 0 0 0 0 1 1 0 1 0 0 1 0 0 0 1 0 0 0 0 0 0 0 0 0
0 0 0 0 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
0 0 0 1 0 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
0 0 0 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0 0 0 0 1 0 1 0 1 0 0 0 0 0 0 1 0 0 0 0 0 0 0 0 1 1 1 0 1 0 0
0 0 0 0 0 1 0 0 0 0 0 0 1 0 0 1 1 0 0 0 0 0 0 0 0 1 0 0 0 0 0 0 1 0 1 1 1 0 0 0
0 0 0 0 0 0 1 0 0 0 0 0 1 1 0 1 0 0 0 1 0 0 0 0 0 0 1 0 0 0 0 0 1 1 0 1 0 0 0 1
0 0 0 0 0 0 0 1 0 0 0 0 1 1 1 0 0 0 1 0 0 0 0 0 0 0 0 1 0 0 0 0 1 1 1 0 0 0 1 0
0 0 0 0 0 0 0 0 0 0 0 0 0 1 0 0 0 1 1 1 0 1 1 0 0 0 0 0 1 0 0 0 0 1 0 0 0 1 1 1
0 0 0 0 1 0 0 0 0 0 0 0 1 0 0 0 1 0 1 1 0 1 1 1 0 0 0 0 0 1 0 0 1 0 0 0 1 0 1 1
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 0 0 0 0 0 0 0 0 0 0 1 0 0 0 0 1 1 1 0 1
0 0 0 0 0 0 0 0 0 0 0 1 0 0 1 0 1 1 1 1 0 0 0 0 0 0 0 0 0 0 0 1 0 0 1 0 1 1 1 0
0 1 1 0 1 0 0 0 1 0 0 0 0 0 0 0 0 0 0 0 1 1 1 1 1 0 0 0 1 0 0 0 0 0 0 0 0 0 0 0
1 0 1 0 0 1 0 0 0 1 0 0 0 0 0 0 0 1 1 0 1 0 1 1 0 1 0 0 0 1 0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
0 0 1 0 1 1 0 1 0 0 0 0 0 0 1 0 0 0 0 0 0 0 1 0 1 1 0 1 0 0 0 0 0 0 1 0 0 0 0 0
0 0 0 0 1 1 1 0 0 0 0 0 0 0 0 1 0 0 0 0 0 0 0 1 1 1 1 0 0 0 0 0 0 0 0 1 0 0 0 0
1 0 0 0 0 0 0 0 0 1 1 1 0 0 0 0 1 0 0 0 1 0 0 0 0 0 0 0 0 1 1 1 0 0 0 0 1 0 0 0
0 1 0 0 0 0 0 0 1 0 1 1 0 0 0 0 0 1 0 0 0 1 0 0 0 0 0 0 1 0 1 1 0 0 0 0 0 1 0 0
0 0 1 0 0 0 0 0 1 1 0 1 0 0 0 0 0 0 1 0 0 0 1 0 0 0 0 0 1 1 0 1 0 0 0 0 0 0 1 0
0 0 0 1 0 0 0 0 1 1 1 0 0 0 0 0 0 0 0 1 0 0 0 1 0 0 0 0 1 1 1 0 0 0 0 0 0 0 0 1
0 0 0 0 1 0 0 0 0 0 0 0 0 1 1 1 0 1 0 0 0 0 0 0 1 0 0 0 0 0 0 0 0 1 1 1 0 1 0 0
0 0 0 0 0 1 0 0 0 0 0 0 1 0 1 1 1 0 0 0 0 0 0 0 0 1 0 0 0 0 0 0 1 0 1 1 1 0 0 0
0 1 0 0 0 0 1 0 0 0 0 0 1 1 0 1 0 0 0 1 0 0 0 0 0 0 1 0 0 0 0 0 1 1 0 1 0 0 0 1
0 1 0 0 0 0 0 1 0 0 0 0 1 1 1 0 0 0 1 0 0 0 0 0 0 0 0 1 0 0 0 0 1 1 1 0 0 0 1 0
0 1 0 0 0 0 0 0 1 0 0 0 0 1 0 0 0 1 1 1 0 0 0 0 0 0 0 0 1 0 0 0 0 1 0 0 0 1 1 1
0 1 0 0 0 0 0 0 0 1 0 0 1 0 0 0 1 0 1 1 0 0 0 0 0 0 0 0 0 1 0 0 1 0 0 0 1 0 1 1
0 1 0 0 0 0 0 0 0 0 1 0 0 0 0 1 1 1 0 1 0 0 0 0 0 0 0 0 0 0 1 0 0 0 0 1 1 1 0 1
0 1 0 0 0 0 0 0 0 0 0 1 0 0 1 0 1 1 1 0 0 0 0 0 0 0 0 0 0 0 0 1 0 0 1 0 1 1 1 0
";
/// Parse a text adjacency matrix format into a directed graph
fn parse_matrix(s: &str) -> MatrixGraph<(), (), Ty> {
let mut gr = MatrixGraph::default();
let s = s.trim();
let lines = s.lines().filter(|l| !l.is_empty());
for (row, line) in lines.enumerate() {
for (col, word) in line.split(' ').filter(|s| !s.is_empty()).enumerate() {
let has_edge = word.parse::().unwrap();
assert!(has_edge == 0 || has_edge == 1);
if has_edge == 0 {
continue;
}
while col >= gr.node_count() || row >= gr.node_count() {
gr.add_node(());
}
gr.add_edge(node_index(row), node_index(col), ());
}
}
gr
}
#[bench]
fn full_edges_out(bench: &mut Bencher) {
let a = parse_matrix::(FULL);
bench.iter(|| a.edges_directed(node_index(1), Outgoing).count())
}
#[bench]
fn full_edges_in(bench: &mut Bencher) {
let a = parse_matrix::(FULL);
bench.iter(|| a.edges_directed(node_index(1), Incoming).count())
}
#[bench]
fn full_neighbors_out(bench: &mut Bencher) {
let a = parse_matrix::(FULL);
bench.iter(|| a.neighbors_directed(node_index(1), Outgoing).count())
}
#[bench]
fn full_neighbors_in(bench: &mut Bencher) {
let a = parse_matrix::(FULL);
bench.iter(|| a.neighbors_directed(node_index(1), Incoming).count())
}
#[bench]
fn full_sccs(bench: &mut Bencher) {
let a = parse_matrix::(FULL);
bench.iter(|| algo::kosaraju_scc(&a));
}
#[bench]
fn bigger_edges_out(bench: &mut Bencher) {
let a = parse_matrix::(BIGGER);
bench.iter(|| a.edges_directed(node_index(1), Outgoing).count())
}
#[bench]
fn bigger_edges_in(bench: &mut Bencher) {
let a = parse_matrix::(BIGGER);
bench.iter(|| a.edges_directed(node_index(1), Incoming).count())
}
#[bench]
fn bigger_neighbors_out(bench: &mut Bencher) {
let a = parse_matrix::(BIGGER);
bench.iter(|| a.neighbors_directed(node_index(1), Outgoing).count())
}
#[bench]
fn bigger_neighbors_in(bench: &mut Bencher) {
let a = parse_matrix::(BIGGER);
bench.iter(|| a.neighbors_directed(node_index(1), Incoming).count())
}
#[bench]
fn bigger_sccs(bench: &mut Bencher) {
let a = parse_matrix::(BIGGER);
bench.iter(|| algo::kosaraju_scc(&a));
}
petgraph-0.5.1/benches/ograph.rs 0100644 0001750 0001750 00000002116 13601260027 0015002 0 ustar 00 0000000 0000000 #![feature(test)]
extern crate petgraph;
extern crate test;
use petgraph::graph::Graph;
#[bench]
fn bench_inser(b: &mut test::Bencher) {
let mut og = Graph::new();
let fst = og.add_node(0i32);
for x in 1..125 {
let n = og.add_node(x);
og.add_edge(fst, n, ());
}
b.iter(|| og.add_node(1))
}
#[bench]
fn bench_add_edge(b: &mut test::Bencher) {
let mut og = Graph::new();
for _ in 0..100 {
og.add_node(());
}
b.iter(|| {
for (a, b) in og.node_indices().zip(og.node_indices().skip(1)) {
og.add_edge(a, b, ());
}
og.clear_edges();
})
}
#[bench]
fn bench_remove(b: &mut test::Bencher) {
// removal is very slow in a big graph.
// and this one doesn't even have many nodes.
let mut og = Graph::new();
let fst = og.add_node(0i32);
let mut prev = fst;
for x in 1..1250 {
let n = og.add_node(x);
og.add_edge(prev, n, ());
prev = n;
}
//println!("{}", og);
b.iter(|| {
for _ in 0..100 {
og.remove_node(fst);
}
})
}
petgraph-0.5.1/benches/stable_graph.rs 0100644 0001750 0001750 00000003777 13601260027 0016173 0 ustar 00 0000000 0000000 #![feature(test)]
extern crate petgraph;
extern crate test;
use petgraph::prelude::*;
use test::Bencher;
#[allow(dead_code)]
mod common;
use common::*;
use petgraph::stable_graph::node_index;
#[bench]
fn full_edges_default(bench: &mut Bencher) {
let a = stable_digraph().full_a();
bench.iter(|| a.edges(node_index(1)).count())
}
#[bench]
fn full_edges_out(bench: &mut Bencher) {
let a = stable_digraph().full_a();
bench.iter(|| a.edges_directed(node_index(1), Outgoing).count())
}
#[bench]
fn full_edges_in(bench: &mut Bencher) {
let a = stable_digraph().full_a();
bench.iter(|| a.edges_directed(node_index(1), Incoming).count())
}
#[bench]
fn neighbors_default(bench: &mut Bencher) {
let a = stable_digraph().full_a();
bench.iter(|| a.neighbors(node_index(1)).count())
}
#[bench]
fn neighbors_out(bench: &mut Bencher) {
let a = stable_digraph().full_a();
bench.iter(|| a.neighbors_directed(node_index(1), Outgoing).count())
}
#[bench]
fn neighbors_in(bench: &mut Bencher) {
let a = stable_digraph().full_a();
bench.iter(|| a.neighbors_directed(node_index(1), Incoming).count())
}
#[bench]
fn sccs_stable_graph(bench: &mut Bencher) {
let a = stable_digraph().bigger();
bench.iter(|| petgraph::algo::kosaraju_scc(&a));
}
#[bench]
fn sccs_graph(bench: &mut Bencher) {
let a = digraph().bigger();
bench.iter(|| petgraph::algo::kosaraju_scc(&a));
}
#[bench]
fn stable_graph_map(bench: &mut Bencher) {
let a = stable_digraph().bigger();
bench.iter(|| a.map(|i, _| i, |i, _| i));
}
#[bench]
fn graph_map(bench: &mut Bencher) {
let a = digraph().bigger();
bench.iter(|| a.map(|i, _| i, |i, _| i));
}
#[bench]
fn stable_graph_retain_nodes(bench: &mut Bencher) {
let mut a = stable_digraph().bigger();
bench.iter(|| a.retain_nodes(|_gr, i| (i.index() + 1) % 3700 != 0));
}
#[bench]
fn stable_graph_retain_edges(bench: &mut Bencher) {
let mut a = stable_digraph().bigger();
bench.iter(|| a.retain_edges(|_gr, i| (i.index() + 1) % 3700 != 0));
}
petgraph-0.5.1/benches/unionfind.rs 0100644 0001750 0001750 00000010007 13601260027 0015511 0 ustar 00 0000000 0000000 #![feature(test)]
extern crate petgraph;
extern crate test;
use test::Bencher;
#[allow(dead_code)]
mod common;
use common::*;
use petgraph::algo::{connected_components, is_cyclic_undirected, min_spanning_tree};
#[bench]
fn connected_components_praust_undir_bench(bench: &mut Bencher) {
let a = ungraph().praust_a();
let b = ungraph().praust_b();
bench.iter(|| (connected_components(&a), connected_components(&b)));
}
#[bench]
fn connected_components_praust_dir_bench(bench: &mut Bencher) {
let a = digraph().praust_a();
let b = digraph().praust_b();
bench.iter(|| (connected_components(&a), connected_components(&b)));
}
#[bench]
fn connected_components_full_undir_bench(bench: &mut Bencher) {
let a = ungraph().full_a();
let b = ungraph().full_b();
bench.iter(|| (connected_components(&a), connected_components(&b)));
}
#[bench]
fn connected_components_full_dir_bench(bench: &mut Bencher) {
let a = digraph().full_a();
let b = digraph().full_b();
bench.iter(|| (connected_components(&a), connected_components(&b)));
}
#[bench]
fn connected_components_petersen_undir_bench(bench: &mut Bencher) {
let a = ungraph().petersen_a();
let b = ungraph().petersen_b();
bench.iter(|| (connected_components(&a), connected_components(&b)));
}
#[bench]
fn connected_components_petersen_dir_bench(bench: &mut Bencher) {
let a = digraph().petersen_a();
let b = digraph().petersen_b();
bench.iter(|| (connected_components(&a), connected_components(&b)));
}
#[bench]
fn is_cyclic_undirected_praust_undir_bench(bench: &mut Bencher) {
let a = ungraph().praust_a();
let b = ungraph().praust_b();
bench.iter(|| (is_cyclic_undirected(&a), is_cyclic_undirected(&b)));
}
#[bench]
fn is_cyclic_undirected_praust_dir_bench(bench: &mut Bencher) {
let a = digraph().praust_a();
let b = digraph().praust_b();
bench.iter(|| (is_cyclic_undirected(&a), is_cyclic_undirected(&b)));
}
#[bench]
fn is_cyclic_undirected_full_undir_bench(bench: &mut Bencher) {
let a = ungraph().full_a();
let b = ungraph().full_b();
bench.iter(|| (is_cyclic_undirected(&a), is_cyclic_undirected(&b)));
}
#[bench]
fn is_cyclic_undirected_full_dir_bench(bench: &mut Bencher) {
let a = digraph().full_a();
let b = digraph().full_b();
bench.iter(|| (is_cyclic_undirected(&a), is_cyclic_undirected(&b)));
}
#[bench]
fn is_cyclic_undirected_petersen_undir_bench(bench: &mut Bencher) {
let a = ungraph().petersen_a();
let b = ungraph().petersen_b();
bench.iter(|| (is_cyclic_undirected(&a), is_cyclic_undirected(&b)));
}
#[bench]
fn is_cyclic_undirected_petersen_dir_bench(bench: &mut Bencher) {
let a = digraph().petersen_a();
let b = digraph().petersen_b();
bench.iter(|| (is_cyclic_undirected(&a), is_cyclic_undirected(&b)));
}
#[bench]
fn min_spanning_tree_praust_undir_bench(bench: &mut Bencher) {
let a = ungraph().praust_a();
let b = ungraph().praust_b();
bench.iter(|| (min_spanning_tree(&a), min_spanning_tree(&b)));
}
#[bench]
fn min_spanning_tree_praust_dir_bench(bench: &mut Bencher) {
let a = digraph().praust_a();
let b = digraph().praust_b();
bench.iter(|| (min_spanning_tree(&a), min_spanning_tree(&b)));
}
#[bench]
fn min_spanning_tree_full_undir_bench(bench: &mut Bencher) {
let a = ungraph().full_a();
let b = ungraph().full_b();
bench.iter(|| (min_spanning_tree(&a), min_spanning_tree(&b)));
}
#[bench]
fn min_spanning_tree_full_dir_bench(bench: &mut Bencher) {
let a = digraph().full_a();
let b = digraph().full_b();
bench.iter(|| (min_spanning_tree(&a), min_spanning_tree(&b)));
}
#[bench]
fn min_spanning_tree_petersen_undir_bench(bench: &mut Bencher) {
let a = ungraph().petersen_a();
let b = ungraph().petersen_b();
bench.iter(|| (min_spanning_tree(&a), min_spanning_tree(&b)));
}
#[bench]
fn min_spanning_tree_petersen_dir_bench(bench: &mut Bencher) {
let a = digraph().petersen_a();
let b = digraph().petersen_b();
bench.iter(|| (min_spanning_tree(&a), min_spanning_tree(&b)));
}
petgraph-0.5.1/custom.css 0100644 0001750 0001750 00000001025 13577670250 0013606 0 ustar 00 0000000 0000000
.docblock pre.rust { background: #eeeeff; }
pre.trait, pre.fn, pre.struct, pre.enum, pre.typedef { background: #fcfefc; }
/* Small “example” label for doc examples */
.docblock pre.rust::before {
content: "example";
float: right;
font-style: italic;
font-size: 0.8em;
margin-top: -10px;
margin-right: -5px;
}
/* Fixup where display in trait listing */
pre.trait .where::before {
content: '\a ';
}
.docblock code {
background-color: inherit;
font-weight: bold;
padding: 0 0.1em;
}
petgraph-0.5.1/graph-example.dot 0100644 0001750 0001750 00000000336 13577670250 0015030 0 ustar 00 0000000 0000000 digraph {
rankdir = "LR";
splines = true;
0 [label="petgraph"]
1 [label="fixedbitset"]
2 [label="quickcheck"]
3 [label="rand"]
4 [label="libc"]
0 -> 1
0 -> 2
2 -> 3
3 -> 4
2 -> 4
}
petgraph-0.5.1/src/algo/dominators.rs 0100644 0001750 0001750 00000020676 13601260027 0016016 0 ustar 00 0000000 0000000 //! Compute dominators of a control-flow graph.
//!
//! # The Dominance Relation
//!
//! In a directed graph with a root node **R**, a node **A** is said to *dominate* a
//! node **B** iff every path from **R** to **B** contains **A**.
//!
//! The node **A** is said to *strictly dominate* the node **B** iff **A** dominates
//! **B** and **A ≠ B**.
//!
//! The node **A** is said to be the *immediate dominator* of a node **B** iff it
//! strictly dominates **B** and there does not exist any node **C** where **A**
//! dominates **C** and **C** dominates **B**.
use std::cmp::Ordering;
use std::collections::{HashMap, HashSet};
use std::hash::Hash;
use crate::visit::{DfsPostOrder, GraphBase, IntoNeighbors, Visitable, Walker};
/// The dominance relation for some graph and root.
#[derive(Debug, Clone)]
pub struct Dominators
where
N: Copy + Eq + Hash,
{
root: N,
dominators: HashMap,
}
impl Dominators
where
N: Copy + Eq + Hash,
{
/// Get the root node used to construct these dominance relations.
pub fn root(&self) -> N {
self.root
}
/// Get the immediate dominator of the given node.
///
/// Returns `None` for any node that is not reachable from the root, and for
/// the root itself.
pub fn immediate_dominator(&self, node: N) -> Option {
if node == self.root {
None
} else {
self.dominators.get(&node).cloned()
}
}
/// Iterate over the given node's strict dominators.
///
/// If the given node is not reachable from the root, then `None` is
/// returned.
pub fn strict_dominators(&self, node: N) -> Option> {
if self.dominators.contains_key(&node) {
Some(DominatorsIter {
dominators: self,
node: self.immediate_dominator(node),
})
} else {
None
}
}
/// Iterate over all of the given node's dominators (including the given
/// node itself).
///
/// If the given node is not reachable from the root, then `None` is
/// returned.
pub fn dominators(&self, node: N) -> Option> {
if self.dominators.contains_key(&node) {
Some(DominatorsIter {
dominators: self,
node: Some(node),
})
} else {
None
}
}
}
/// Iterator for a node's dominators.
pub struct DominatorsIter<'a, N>
where
N: 'a + Copy + Eq + Hash,
{
dominators: &'a Dominators,
node: Option,
}
impl<'a, N> Iterator for DominatorsIter<'a, N>
where
N: 'a + Copy + Eq + Hash,
{
type Item = N;
fn next(&mut self) -> Option {
let next = self.node.take();
if let Some(next) = next {
self.node = self.dominators.immediate_dominator(next);
}
next
}
}
/// The undefined dominator sentinel, for when we have not yet discovered a
/// node's dominator.
const UNDEFINED: usize = ::std::usize::MAX;
/// This is an implementation of the engineered ["Simple, Fast Dominance
/// Algorithm"][0] discovered by Cooper et al.
///
/// This algorithm is **O(|V|²)**, and therefore has slower theoretical running time
/// than the Lengauer-Tarjan algorithm (which is **O(|E| log |V|)**. However,
/// Cooper et al found it to be faster in practice on control flow graphs of up
/// to ~30,000 vertices.
///
/// [0]: http://www.cs.rice.edu/~keith/EMBED/dom.pdf
pub fn simple_fast(graph: G, root: G::NodeId) -> Dominators
where
G: IntoNeighbors + Visitable,
::NodeId: Eq + Hash,
{
let (post_order, predecessor_sets) = simple_fast_post_order(graph, root);
let length = post_order.len();
debug_assert!(length > 0);
debug_assert!(post_order.last() == Some(&root));
// From here on out we use indices into `post_order` instead of actual
// `NodeId`s wherever possible. This greatly improves the performance of
// this implementation, but we have to pay a little bit of upfront cost to
// convert our data structures to play along first.
// Maps a node to its index into `post_order`.
let node_to_post_order_idx: HashMap<_, _> = post_order
.iter()
.enumerate()
.map(|(idx, &node)| (node, idx))
.collect();
// Maps a node's `post_order` index to its set of predecessors's indices
// into `post_order` (as a vec).
let idx_to_predecessor_vec =
predecessor_sets_to_idx_vecs(&post_order, &node_to_post_order_idx, predecessor_sets);
let mut dominators = vec![UNDEFINED; length];
dominators[length - 1] = length - 1;
let mut changed = true;
while changed {
changed = false;
// Iterate in reverse post order, skipping the root.
for idx in (0..length - 1).rev() {
debug_assert!(post_order[idx] != root);
// Take the intersection of every predecessor's dominator set; that
// is the current best guess at the immediate dominator for this
// node.
let new_idom_idx = {
let mut predecessors = idx_to_predecessor_vec[idx]
.iter()
.filter(|&&p| dominators[p] != UNDEFINED);
let new_idom_idx = predecessors.next().expect(
"Because the root is initialized to dominate itself, and is the \
first node in every path, there must exist a predecessor to this \
node that also has a dominator",
);
predecessors.fold(*new_idom_idx, |new_idom_idx, &predecessor_idx| {
intersect(&dominators, new_idom_idx, predecessor_idx)
})
};
debug_assert!(new_idom_idx < length);
if new_idom_idx != dominators[idx] {
dominators[idx] = new_idom_idx;
changed = true;
}
}
}
// All done! Translate the indices back into proper `G::NodeId`s.
debug_assert!(!dominators.iter().any(|&dom| dom == UNDEFINED));
Dominators {
root,
dominators: dominators
.into_iter()
.enumerate()
.map(|(idx, dom_idx)| (post_order[idx], post_order[dom_idx]))
.collect(),
}
}
fn intersect(dominators: &[usize], mut finger1: usize, mut finger2: usize) -> usize {
loop {
match finger1.cmp(&finger2) {
Ordering::Less => finger1 = dominators[finger1],
Ordering::Greater => finger2 = dominators[finger2],
Ordering::Equal => return finger1,
}
}
}
fn predecessor_sets_to_idx_vecs(
post_order: &[N],
node_to_post_order_idx: &HashMap,
mut predecessor_sets: HashMap>,
) -> Vec>
where
N: Copy + Eq + Hash,
{
post_order
.iter()
.map(|node| {
predecessor_sets
.remove(node)
.map(|predecessors| {
predecessors
.into_iter()
.map(|p| *node_to_post_order_idx.get(&p).unwrap())
.collect()
})
.unwrap_or_else(Vec::new)
})
.collect()
}
fn simple_fast_post_order(
graph: G,
root: G::NodeId,
) -> (Vec, HashMap>)
where
G: IntoNeighbors + Visitable,
::NodeId: Eq + Hash,
{
let mut post_order = vec![];
let mut predecessor_sets = HashMap::new();
for node in DfsPostOrder::new(graph, root).iter(graph) {
post_order.push(node);
for successor in graph.neighbors(node) {
predecessor_sets
.entry(successor)
.or_insert_with(HashSet::new)
.insert(node);
}
}
(post_order, predecessor_sets)
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_iter_dominators() {
let doms: Dominators = Dominators {
root: 0,
dominators: [(2, 1), (1, 0), (0, 0)].iter().cloned().collect(),
};
let all_doms: Vec<_> = doms.dominators(2).unwrap().collect();
assert_eq!(vec![2, 1, 0], all_doms);
assert_eq!(None::<()>, doms.dominators(99).map(|_| unreachable!()));
let strict_doms: Vec<_> = doms.strict_dominators(2).unwrap().collect();
assert_eq!(vec![1, 0], strict_doms);
assert_eq!(
None::<()>,
doms.strict_dominators(99).map(|_| unreachable!())
);
}
}
petgraph-0.5.1/src/algo/mod.rs 0100644 0001750 0001750 00000063075 13662133536 0014431 0 ustar 00 0000000 0000000 //! Graph algorithms.
//!
//! It is a goal to gradually migrate the algorithms to be based on graph traits
//! so that they are generally applicable. For now, some of these still require
//! the `Graph` type.
pub mod dominators;
use std::cmp::min;
use std::collections::{BinaryHeap, HashMap};
use crate::prelude::*;
use super::graph::IndexType;
use super::unionfind::UnionFind;
use super::visit::{
GraphBase, GraphRef, IntoEdgeReferences, IntoEdges, IntoNeighbors, IntoNeighborsDirected,
IntoNodeIdentifiers, NodeCompactIndexable, NodeCount, NodeIndexable, Reversed, VisitMap,
Visitable,
};
use super::EdgeType;
use crate::data::Element;
use crate::scored::MinScored;
use crate::visit::Walker;
use crate::visit::{Data, IntoNodeReferences, NodeRef};
pub use super::astar::astar;
pub use super::dijkstra::dijkstra;
pub use super::isomorphism::{is_isomorphic, is_isomorphic_matching};
pub use super::simple_paths::all_simple_paths;
/// \[Generic\] Return the number of connected components of the graph.
///
/// For a directed graph, this is the *weakly* connected components.
/// # Example
/// ```rust
/// use petgraph::Graph;
/// use petgraph::algo::connected_components;
/// use petgraph::prelude::*;
///
/// let mut graph : Graph<(),(),Directed>= Graph::new();
/// let a = graph.add_node(()); // node with no weight
/// let b = graph.add_node(());
/// let c = graph.add_node(());
/// let d = graph.add_node(());
/// let e = graph.add_node(());
/// let f = graph.add_node(());
/// let g = graph.add_node(());
/// let h = graph.add_node(());
///
/// graph.extend_with_edges(&[
/// (a, b),
/// (b, c),
/// (c, d),
/// (d, a),
/// (e, f),
/// (f, g),
/// (g, h),
/// (h, e)
/// ]);
/// // a ----> b e ----> f
/// // ^ | ^ |
/// // | v | v
/// // d <---- c h <---- g
///
/// assert_eq!(connected_components(&graph),2);
/// graph.add_edge(b,e,());
/// assert_eq!(connected_components(&graph),1);
/// ```
pub fn connected_components(g: G) -> usize
where
G: NodeCompactIndexable + IntoEdgeReferences,
{
let mut vertex_sets = UnionFind::new(g.node_bound());
for edge in g.edge_references() {
let (a, b) = (edge.source(), edge.target());
// union the two vertices of the edge
vertex_sets.union(g.to_index(a), g.to_index(b));
}
let mut labels = vertex_sets.into_labeling();
labels.sort();
labels.dedup();
labels.len()
}
/// \[Generic\] Return `true` if the input graph contains a cycle.
///
/// Always treats the input graph as if undirected.
pub fn is_cyclic_undirected(g: G) -> bool
where
G: NodeIndexable + IntoEdgeReferences,
{
let mut edge_sets = UnionFind::new(g.node_bound());
for edge in g.edge_references() {
let (a, b) = (edge.source(), edge.target());
// union the two vertices of the edge
// -- if they were already the same, then we have a cycle
if !edge_sets.union(g.to_index(a), g.to_index(b)) {
return true;
}
}
false
}
/// \[Generic\] Perform a topological sort of a directed graph.
///
/// If the graph was acyclic, return a vector of nodes in topological order:
/// each node is ordered before its successors.
/// Otherwise, it will return a `Cycle` error. Self loops are also cycles.
///
/// To handle graphs with cycles, use the scc algorithms or `DfsPostOrder`
/// instead of this function.
///
/// If `space` is not `None`, it is used instead of creating a new workspace for
/// graph traversal. The implementation is iterative.
pub fn toposort(
g: G,
space: Option<&mut DfsSpace>,
) -> Result, Cycle>
where
G: IntoNeighborsDirected + IntoNodeIdentifiers + Visitable,
{
// based on kosaraju scc
with_dfs(g, space, |dfs| {
dfs.reset(g);
let mut finished = g.visit_map();
let mut finish_stack = Vec::new();
for i in g.node_identifiers() {
if dfs.discovered.is_visited(&i) {
continue;
}
dfs.stack.push(i);
while let Some(&nx) = dfs.stack.last() {
if dfs.discovered.visit(nx) {
// First time visiting `nx`: Push neighbors, don't pop `nx`
for succ in g.neighbors(nx) {
if succ == nx {
// self cycle
return Err(Cycle(nx));
}
if !dfs.discovered.is_visited(&succ) {
dfs.stack.push(succ);
}
}
} else {
dfs.stack.pop();
if finished.visit(nx) {
// Second time: All reachable nodes must have been finished
finish_stack.push(nx);
}
}
}
}
finish_stack.reverse();
dfs.reset(g);
for &i in &finish_stack {
dfs.move_to(i);
let mut cycle = false;
while let Some(j) = dfs.next(Reversed(g)) {
if cycle {
return Err(Cycle(j));
}
cycle = true;
}
}
Ok(finish_stack)
})
}
/// \[Generic\] Return `true` if the input directed graph contains a cycle.
///
/// This implementation is recursive; use `toposort` if an alternative is
/// needed.
pub fn is_cyclic_directed(g: G) -> bool
where
G: IntoNodeIdentifiers + IntoNeighbors + Visitable,
{
use crate::visit::{depth_first_search, DfsEvent};
depth_first_search(g, g.node_identifiers(), |event| match event {
DfsEvent::BackEdge(_, _) => Err(()),
_ => Ok(()),
})
.is_err()
}
type DfsSpaceType = DfsSpace<::NodeId, ::Map>;
/// Workspace for a graph traversal.
#[derive(Clone, Debug)]
pub struct DfsSpace {
dfs: Dfs,
}
impl DfsSpace
where
N: Copy + PartialEq,
VM: VisitMap,
{
pub fn new(g: G) -> Self
where
G: GraphRef + Visitable,
{
DfsSpace { dfs: Dfs::empty(g) }
}
}
impl Default for DfsSpace
where
VM: VisitMap + Default,
{
fn default() -> Self {
DfsSpace {
dfs: Dfs {
stack: <_>::default(),
discovered: <_>::default(),
},
}
}
}
/// Create a Dfs if it's needed
fn with_dfs(g: G, space: Option<&mut DfsSpaceType>, f: F) -> R
where
G: GraphRef + Visitable,
F: FnOnce(&mut Dfs) -> R,
{
let mut local_visitor;
let dfs = if let Some(v) = space {
&mut v.dfs
} else {
local_visitor = Dfs::empty(g);
&mut local_visitor
};
f(dfs)
}
/// \[Generic\] Check if there exists a path starting at `from` and reaching `to`.
///
/// If `from` and `to` are equal, this function returns true.
///
/// If `space` is not `None`, it is used instead of creating a new workspace for
/// graph traversal.
pub fn has_path_connecting(
g: G,
from: G::NodeId,
to: G::NodeId,
space: Option<&mut DfsSpace>,
) -> bool
where
G: IntoNeighbors + Visitable,
{
with_dfs(g, space, |dfs| {
dfs.reset(g);
dfs.move_to(from);
dfs.iter(g).any(|x| x == to)
})
}
/// Renamed to `kosaraju_scc`.
#[deprecated(note = "renamed to kosaraju_scc")]
pub fn scc(g: G) -> Vec>
where
G: IntoNeighborsDirected + Visitable + IntoNodeIdentifiers,
{
kosaraju_scc(g)
}
/// \[Generic\] Compute the *strongly connected components* using [Kosaraju's algorithm][1].
///
/// [1]: https://en.wikipedia.org/wiki/Kosaraju%27s_algorithm
///
/// Return a vector where each element is a strongly connected component (scc).
/// The order of node ids within each scc is arbitrary, but the order of
/// the sccs is their postorder (reverse topological sort).
///
/// For an undirected graph, the sccs are simply the connected components.
///
/// This implementation is iterative and does two passes over the nodes.
pub fn kosaraju_scc(g: G) -> Vec>
where
G: IntoNeighborsDirected + Visitable + IntoNodeIdentifiers,
{
let mut dfs = DfsPostOrder::empty(g);
// First phase, reverse dfs pass, compute finishing times.
// http://stackoverflow.com/a/26780899/161659
let mut finish_order = Vec::with_capacity(0);
for i in g.node_identifiers() {
if dfs.discovered.is_visited(&i) {
continue;
}
dfs.move_to(i);
while let Some(nx) = dfs.next(Reversed(g)) {
finish_order.push(nx);
}
}
let mut dfs = Dfs::from_parts(dfs.stack, dfs.discovered);
dfs.reset(g);
let mut sccs = Vec::new();
// Second phase
// Process in decreasing finishing time order
for i in finish_order.into_iter().rev() {
if dfs.discovered.is_visited(&i) {
continue;
}
// Move to the leader node `i`.
dfs.move_to(i);
let mut scc = Vec::new();
while let Some(nx) = dfs.next(g) {
scc.push(nx);
}
sccs.push(scc);
}
sccs
}
/// \[Generic\] Compute the *strongly connected components* using [Tarjan's algorithm][1].
///
/// [1]: https://en.wikipedia.org/wiki/Tarjan%27s_strongly_connected_components_algorithm
///
/// Return a vector where each element is a strongly connected component (scc).
/// The order of node ids within each scc is arbitrary, but the order of
/// the sccs is their postorder (reverse topological sort).
///
/// For an undirected graph, the sccs are simply the connected components.
///
/// This implementation is recursive and does one pass over the nodes.
pub fn tarjan_scc(g: G) -> Vec>
where
G: IntoNodeIdentifiers + IntoNeighbors + NodeIndexable,
{
#[derive(Copy, Clone, Debug)]
struct NodeData {
index: Option,
lowlink: usize,
on_stack: bool,
}
#[derive(Debug)]
struct Data<'a, G>
where
G: NodeIndexable,
G::NodeId: 'a,
{
index: usize,
nodes: Vec,
stack: Vec,
sccs: &'a mut Vec>,
}
fn scc_visit(v: G::NodeId, g: G, data: &mut Data)
where
G: IntoNeighbors + NodeIndexable,
{
macro_rules! node {
($node:expr) => {
data.nodes[g.to_index($node)]
};
}
if node![v].index.is_some() {
// already visited
return;
}
let v_index = data.index;
node![v].index = Some(v_index);
node![v].lowlink = v_index;
node![v].on_stack = true;
data.stack.push(v);
data.index += 1;
for w in g.neighbors(v) {
match node![w].index {
None => {
scc_visit(w, g, data);
node![v].lowlink = min(node![v].lowlink, node![w].lowlink);
}
Some(w_index) => {
if node![w].on_stack {
// Successor w is in stack S and hence in the current SCC
let v_lowlink = &mut node![v].lowlink;
*v_lowlink = min(*v_lowlink, w_index);
}
}
}
}
// If v is a root node, pop the stack and generate an SCC
if let Some(v_index) = node![v].index {
if node![v].lowlink == v_index {
let mut cur_scc = Vec::new();
loop {
let w = data.stack.pop().unwrap();
node![w].on_stack = false;
cur_scc.push(w);
if g.to_index(w) == g.to_index(v) {
break;
}
}
data.sccs.push(cur_scc);
}
}
}
let mut sccs = Vec::new();
{
let map = vec![
NodeData {
index: None,
lowlink: !0,
on_stack: false
};
g.node_bound()
];
let mut data = Data {
index: 0,
nodes: map,
stack: Vec::new(),
sccs: &mut sccs,
};
for n in g.node_identifiers() {
scc_visit(n, g, &mut data);
}
}
sccs
}
/// [Graph] Condense every strongly connected component into a single node and return the result.
///
/// If `make_acyclic` is true, self-loops and multi edges are ignored, guaranteeing that
/// the output is acyclic.
/// # Example
/// ```rust
/// use petgraph::Graph;
/// use petgraph::algo::condensation;
/// use petgraph::prelude::*;
///
/// let mut graph : Graph<(),(),Directed> = Graph::new();
/// let a = graph.add_node(()); // node with no weight
/// let b = graph.add_node(());
/// let c = graph.add_node(());
/// let d = graph.add_node(());
/// let e = graph.add_node(());
/// let f = graph.add_node(());
/// let g = graph.add_node(());
/// let h = graph.add_node(());
///
/// graph.extend_with_edges(&[
/// (a, b),
/// (b, c),
/// (c, d),
/// (d, a),
/// (b, e),
/// (e, f),
/// (f, g),
/// (g, h),
/// (h, e)
/// ]);
///
/// // a ----> b ----> e ----> f
/// // ^ | ^ |
/// // | v | v
/// // d <---- c h <---- g
///
/// let condensed_graph = condensation(graph,false);
/// let A = NodeIndex::new(0);
/// let B = NodeIndex::new(1);
/// assert_eq!(condensed_graph.node_count(), 2);
/// assert_eq!(condensed_graph.edge_count(), 9);
/// assert_eq!(condensed_graph.neighbors(A).collect::>(), vec![A, A, A, A]);
/// assert_eq!(condensed_graph.neighbors(B).collect::>(), vec![A, B, B, B, B]);
/// ```
/// If `make_acyclic` is true, self-loops and multi edges are ignored:
///
/// ```rust
/// # use petgraph::Graph;
/// # use petgraph::algo::condensation;
/// # use petgraph::prelude::*;
/// #
/// # let mut graph : Graph<(),(),Directed> = Graph::new();
/// # let a = graph.add_node(()); // node with no weight
/// # let b = graph.add_node(());
/// # let c = graph.add_node(());
/// # let d = graph.add_node(());
/// # let e = graph.add_node(());
/// # let f = graph.add_node(());
/// # let g = graph.add_node(());
/// # let h = graph.add_node(());
/// #
/// # graph.extend_with_edges(&[
/// # (a, b),
/// # (b, c),
/// # (c, d),
/// # (d, a),
/// # (b, e),
/// # (e, f),
/// # (f, g),
/// # (g, h),
/// # (h, e)
/// # ]);
/// let acyclic_condensed_graph = condensation(graph, true);
/// let A = NodeIndex::new(0);
/// let B = NodeIndex::new(1);
/// assert_eq!(acyclic_condensed_graph.node_count(), 2);
/// assert_eq!(acyclic_condensed_graph.edge_count(), 1);
/// assert_eq!(acyclic_condensed_graph.neighbors(B).collect::>(), vec![A]);
/// ```
pub fn condensation(
g: Graph,
make_acyclic: bool,
) -> Graph, E, Ty, Ix>
where
Ty: EdgeType,
Ix: IndexType,
{
let sccs = kosaraju_scc(&g);
let mut condensed: Graph, E, Ty, Ix> = Graph::with_capacity(sccs.len(), g.edge_count());
// Build a map from old indices to new ones.
let mut node_map = vec![NodeIndex::end(); g.node_count()];
for comp in sccs {
let new_nix = condensed.add_node(Vec::new());
for nix in comp {
node_map[nix.index()] = new_nix;
}
}
// Consume nodes and edges of the old graph and insert them into the new one.
let (nodes, edges) = g.into_nodes_edges();
for (nix, node) in nodes.into_iter().enumerate() {
condensed[node_map[nix]].push(node.weight);
}
for edge in edges {
let source = node_map[edge.source().index()];
let target = node_map[edge.target().index()];
if make_acyclic {
if source != target {
condensed.update_edge(source, target, edge.weight);
}
} else {
condensed.add_edge(source, target, edge.weight);
}
}
condensed
}
/// \[Generic\] Compute a *minimum spanning tree* of a graph.
///
/// The input graph is treated as if undirected.
///
/// Using Kruskal's algorithm with runtime **O(|E| log |E|)**. We actually
/// return a minimum spanning forest, i.e. a minimum spanning tree for each connected
/// component of the graph.
///
/// The resulting graph has all the vertices of the input graph (with identical node indices),
/// and **|V| - c** edges, where **c** is the number of connected components in `g`.
///
/// Use `from_elements` to create a graph from the resulting iterator.
pub fn min_spanning_tree(g: G) -> MinSpanningTree
where
G::NodeWeight: Clone,
G::EdgeWeight: Clone + PartialOrd,
G: IntoNodeReferences + IntoEdgeReferences + NodeIndexable,
{
// Initially each vertex is its own disjoint subgraph, track the connectedness
// of the pre-MST with a union & find datastructure.
let subgraphs = UnionFind::new(g.node_bound());
let edges = g.edge_references();
let mut sort_edges = BinaryHeap::with_capacity(edges.size_hint().0);
for edge in edges {
sort_edges.push(MinScored(
edge.weight().clone(),
(edge.source(), edge.target()),
));
}
MinSpanningTree {
graph: g,
node_ids: Some(g.node_references()),
subgraphs,
sort_edges,
node_map: HashMap::new(),
node_count: 0,
}
}
/// An iterator producing a minimum spanning forest of a graph.
pub struct MinSpanningTree
where
G: Data + IntoNodeReferences,
{
graph: G,
node_ids: Option,
subgraphs: UnionFind,
sort_edges: BinaryHeap>,
node_map: HashMap,
node_count: usize,
}
impl Iterator for MinSpanningTree
where
G: IntoNodeReferences + NodeIndexable,
G::NodeWeight: Clone,
G::EdgeWeight: PartialOrd,
{
type Item = Element;
fn next(&mut self) -> Option {
let g = self.graph;
if let Some(ref mut iter) = self.node_ids {
if let Some(node) = iter.next() {
self.node_map.insert(g.to_index(node.id()), self.node_count);
self.node_count += 1;
return Some(Element::Node {
weight: node.weight().clone(),
});
}
}
self.node_ids = None;
// Kruskal's algorithm.
// Algorithm is this:
//
// 1. Create a pre-MST with all the vertices and no edges.
// 2. Repeat:
//
// a. Remove the shortest edge from the original graph.
// b. If the edge connects two disjoint trees in the pre-MST,
// add the edge.
while let Some(MinScored(score, (a, b))) = self.sort_edges.pop() {
// check if the edge would connect two disjoint parts
let (a_index, b_index) = (g.to_index(a), g.to_index(b));
if self.subgraphs.union(a_index, b_index) {
let (&a_order, &b_order) =
match (self.node_map.get(&a_index), self.node_map.get(&b_index)) {
(Some(a_id), Some(b_id)) => (a_id, b_id),
_ => panic!("Edge references unknown node"),
};
return Some(Element::Edge {
source: a_order,
target: b_order,
weight: score,
});
}
}
None
}
}
/// An algorithm error: a cycle was found in the graph.
#[derive(Clone, Debug, PartialEq)]
pub struct Cycle(N);
impl Cycle {
/// Return a node id that participates in the cycle
pub fn node_id(&self) -> N
where
N: Copy,
{
self.0
}
}
/// An algorithm error: a cycle of negative weights was found in the graph.
#[derive(Clone, Debug, PartialEq)]
pub struct NegativeCycle(());
/// \[Generic\] Compute shortest paths from node `source` to all other.
///
/// Using the [Bellman–Ford algorithm][bf]; negative edge costs are
/// permitted, but the graph must not have a cycle of negative weights
/// (in that case it will return an error).
///
/// On success, return one vec with path costs, and another one which points
/// out the predecessor of a node along a shortest path. The vectors
/// are indexed by the graph's node indices.
///
/// [bf]: https://en.wikipedia.org/wiki/Bellman%E2%80%93Ford_algorithm
///
/// # Example
/// ```rust
/// use petgraph::Graph;
/// use petgraph::algo::bellman_ford;
/// use petgraph::prelude::*;
///
/// let mut g = Graph::new();
/// let a = g.add_node(()); // node with no weight
/// let b = g.add_node(());
/// let c = g.add_node(());
/// let d = g.add_node(());
/// let e = g.add_node(());
/// let f = g.add_node(());
/// g.extend_with_edges(&[
/// (0, 1, 2.0),
/// (0, 3, 4.0),
/// (1, 2, 1.0),
/// (1, 5, 7.0),
/// (2, 4, 5.0),
/// (4, 5, 1.0),
/// (3, 4, 1.0),
/// ]);
///
/// // Graph represented with the weight of each edge
/// //
/// // 2 1
/// // a ----- b ----- c
/// // | 4 | 7 |
/// // d f | 5
/// // | 1 | 1 |
/// // \------ e ------/
///
/// let path = bellman_ford(&g, a);
/// assert_eq!(path, Ok((vec![0.0 , 2.0, 3.0, 4.0, 5.0, 6.0],
/// vec![None, Some(a),Some(b),Some(a), Some(d), Some(e)]
/// ))
/// );
/// // Node f (indice 5) can be reach from a with a path costing 6.
/// // Predecessor of f is Some(e) which predecessor is Some(d) which predecessor is Some(a).
/// // Thus the path from a to f is a <-> d <-> e <-> f
///
/// let graph_with_neg_cycle = Graph::<(), f32, Undirected>::from_edges(&[
/// (0, 1, -2.0),
/// (0, 3, -4.0),
/// (1, 2, -1.0),
/// (1, 5, -25.0),
/// (2, 4, -5.0),
/// (4, 5, -25.0),
/// (3, 4, -1.0),
/// ]);
///
/// assert!(bellman_ford(&graph_with_neg_cycle, NodeIndex::new(0)).is_err());
/// ```
pub fn bellman_ford(
g: G,
source: G::NodeId,
) -> Result<(Vec, Vec