Skip to content
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

Reorganize code into modules #396

Merged
merged 42 commits into from
Aug 6, 2021
Merged
Show file tree
Hide file tree
Changes from 41 commits
Commits
Show all changes
42 commits
Select commit Hold shift + click to select a range
45493c9
Reorganize dag algorithms into new file
IvanIsCoding Jul 24, 2021
ccf9f4c
Reorganize shortest path algorithms in new file
IvanIsCoding Jul 24, 2021
f218504
Move astar to short path file
IvanIsCoding Jul 24, 2021
db353d2
Reorganize matching in new file
IvanIsCoding Jul 24, 2021
794e97d
Reorganize random_circuit in new file
IvanIsCoding Jul 24, 2021
9f0d235
Run cargo fmt
IvanIsCoding Jul 24, 2021
229e781
Move layout to its own module
IvanIsCoding Jul 24, 2021
6307658
Run cargo fmt
IvanIsCoding Jul 24, 2021
e0b9b94
Move isomorphism to its own module
IvanIsCoding Jul 24, 2021
8d5ef8e
Move matching to its own module
IvanIsCoding Jul 24, 2021
bad296f
Move shortest_path to its own module
IvanIsCoding Jul 24, 2021
f635076
Remove weight_callable duplication
IvanIsCoding Jul 24, 2021
2ceb35d
Move floyd_warshall to its own file
IvanIsCoding Jul 24, 2021
0d8ec61
Move num_shortest_path_unweighted to its own file
IvanIsCoding Jul 25, 2021
13db6c6
Move all_pairs_dijkstra to its own file
IvanIsCoding Jul 25, 2021
170f2f5
Rename to dag_algo
IvanIsCoding Jul 25, 2021
e12b8f2
Move tree algorithms to its own file
IvanIsCoding Jul 25, 2021
159bc7a
Move connectivity to its own file
IvanIsCoding Jul 25, 2021
c288ab3
Move dag_algo to its own module
IvanIsCoding Jul 25, 2021
b80f7ef
Move traversal to its own module
IvanIsCoding Jul 25, 2021
b8b5eb1
Temporarily allow module inception
IvanIsCoding Jul 25, 2021
22abf85
Rename isomorphism and layouts
IvanIsCoding Jul 26, 2021
420d47d
Move simple_path to its own file
IvanIsCoding Jul 26, 2021
8eb199a
Move transitivity to its own file
IvanIsCoding Jul 26, 2021
e875d0d
Move core_number to its own file
IvanIsCoding Jul 26, 2021
7d81bda
Create representation_algo
IvanIsCoding Jul 26, 2021
326b62e
Move digraph_union to union
IvanIsCoding Jul 26, 2021
24bfce3
Move graph_greedy_coloring to its own file
IvanIsCoding Jul 26, 2021
c23040e
Move code from traversal to dag_algo
IvanIsCoding Jul 27, 2021
5c37a5e
Revert "Rename isomorphism and layouts"
IvanIsCoding Jul 27, 2021
c521fdc
Move more items into connectivity module
IvanIsCoding Jul 27, 2021
ed7e6ac
Rename random_circuit to random_graph
IvanIsCoding Jul 27, 2021
58f019a
Merge remote-tracking branch 'upstream/main' into reorganize-code
IvanIsCoding Jul 29, 2021
9984350
Merge branch 'main' into reorganize-code
IvanIsCoding Aug 1, 2021
a0a23dd
Merge remote-tracking branch 'upstream/main' into reorganize-code
IvanIsCoding Aug 5, 2021
aaf7517
Merge branch 'reorganize-code' of github.com:IvanIsCoding/retworkx in…
IvanIsCoding Aug 5, 2021
ea4bd18
Avoid module inception in isomorphism.rs
IvanIsCoding Aug 6, 2021
955a6bd
Avoid module inception in layout.rs
IvanIsCoding Aug 6, 2021
cc75817
Simplify names in layout module
IvanIsCoding Aug 6, 2021
e55d07e
Add details to CONTRIBUTING.md
IvanIsCoding Aug 6, 2021
bb63b2e
Fix tox -edocs issues
IvanIsCoding Aug 6, 2021
bf7e3fe
Minor CONTRIBUTING.md fixes
mtreinish Aug 6, 2021
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
68 changes: 68 additions & 0 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,74 @@ with Qiskit; the general guidelines and advice still apply here.
In addition to the general guidelines there are specific details for
contributing to retworkx, these are documented below.

### Making changes to the code

Retworkx is implemented primarily in Rust with a thin layer of Python.
Because of that, most of your code changes will involve modifications to
Rust files in `src`. To understand which files you need to change, we invite
you for an overview of our simplified source tree:

```
├── src/
│ ├── lib.rs
│ ├── tiny.rs
│ ├── large/
│ │ ├── mod.rs
│ │ ├── pure_rust_code.rs
│ │ └── more_pure_rust_code.rs
```

#### Module exports in `lib.rs`

To add new functions, you will need to export them in `lib.rs`. `lib.rs` will
import functions defined in Rust modules (see the next section), and export
them to Python using `m.add_wrapped(wrap_pyfunction!(your_new_function))?;`

#### Adding and changing functions in modules

To add and change functions, you will need to modify module files. Modules contain pyfunctions
that will be exported, and can be defined either as a single file such as `tiny.rs` or as a
directory with `mod.rs` such as `large/`.

Rust functions that are exported to Python are annotated with `#[pyfunction]`. The
annotation gives them power to interact both with the Python interpreter and pure
Rust code. To change an existing function, search for its name and edit the code that
already exists.

If you want to add a new function, find the module you'd like to insert it in
or create a new one like `your_module.rs`. Then, start with the boilerplate bellow:

```rust
/// Docstring containing description of the function
#[pyfunction]
#[pyo3(text_signature = "(graph, /)")]
pub fn your_new_function(
py: Python,
graph: &graph::PyGraph,
) -> PyResult<()> {
/* Your code goes here */
}
```

> __NOTE:__ If you create a new `your_module.rs`, remember to declare and import it in `lib.rs`:
> ```rust
> mod your_module;
> use your_module::your_new_function;
> ```

#### Module directories: when a single file is not enough

Sometimes you will find that it is hard to organize a module in a tiny
file like `tiny.rs`. In those cases, we suggest moving the files to a directory
and splitting them following the structure of `large/`.

Module directories have a `mod.rs` file containing the pyfunctions. The pyfunctions
in that file then delegate most of logic by importing and calling pure Rust code from
`pure_rust_code.rs` and `more_pure_rust_code.rs`.

> __NOTE:__ Do you still have questions about making your first contribution?
> Contact us at the [\#retworkx channel in Qiskit Slack](https://qiskit.slack.com/messages/retworkx/)

### Tests

Once you've made a code change, it is important to verify that your change
Expand Down
78 changes: 78 additions & 0 deletions src/coloring.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
// 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.

#![allow(clippy::float_cmp)]

use crate::graph;

use ahash::RandomState;
use hashbrown::{HashMap, HashSet};
use indexmap::IndexMap;
use std::cmp::Reverse;

use pyo3::prelude::*;
use pyo3::types::PyDict;
use pyo3::Python;

use petgraph::graph::NodeIndex;
use petgraph::prelude::*;
use petgraph::visit::NodeCount;

use rayon::prelude::*;

/// Color a PyGraph using a largest_first strategy greedy graph coloring.
///
/// :param PyGraph: The input PyGraph object to color
///
/// :returns: A dictionary where keys are node indices and the value is
/// the color
/// :rtype: dict
#[pyfunction]
#[pyo3(text_signature = "(graph, /)")]
fn graph_greedy_color(
py: Python,
graph: &graph::PyGraph,
) -> PyResult<PyObject> {
let mut colors: IndexMap<usize, usize, RandomState> =
IndexMap::with_hasher(RandomState::default());
let mut node_vec: Vec<NodeIndex> = graph.graph.node_indices().collect();
let mut sort_map: HashMap<NodeIndex, usize> =
HashMap::with_capacity(graph.node_count());
for k in node_vec.iter() {
sort_map.insert(*k, graph.graph.edges(*k).count());
}
node_vec.par_sort_by_key(|k| Reverse(sort_map.get(k)));
for u_index in node_vec {
let mut neighbor_colors: HashSet<usize> = HashSet::new();
for edge in graph.graph.edges(u_index) {
let target = edge.target().index();
let existing_color = match colors.get(&target) {
Some(node) => node,
None => continue,
};
neighbor_colors.insert(*existing_color);
}
let mut count: usize = 0;
loop {
if !neighbor_colors.contains(&count) {
break;
}
count += 1;
}
colors.insert(u_index.index(), count);
}
let out_dict = PyDict::new(py);
for (index, color) in colors {
out_dict.set_item(index, color)?;
}
Ok(out_dict.into())
}
96 changes: 96 additions & 0 deletions src/connectivity/core_number.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
// 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.

#![allow(clippy::float_cmp)]

use hashbrown::{HashMap, HashSet};

use pyo3::prelude::*;
use pyo3::types::PyDict;
use pyo3::Python;

use petgraph::graph::NodeIndex;
use petgraph::prelude::*;
use petgraph::EdgeType;

use rayon::prelude::*;

pub fn core_number<Ty>(
py: Python,
graph: &StableGraph<PyObject, PyObject, Ty>,
) -> PyResult<PyObject>
where
Ty: EdgeType,
{
let node_num = graph.node_count();
if node_num == 0 {
return Ok(PyDict::new(py).into());
}

let mut cores: HashMap<NodeIndex, usize> = HashMap::with_capacity(node_num);
let mut node_vec: Vec<NodeIndex> = graph.node_indices().collect();
let mut degree_map: HashMap<NodeIndex, usize> =
HashMap::with_capacity(node_num);
let mut nbrs: HashMap<NodeIndex, HashSet<NodeIndex>> =
HashMap::with_capacity(node_num);
let mut node_pos: HashMap<NodeIndex, usize> =
HashMap::with_capacity(node_num);

for k in node_vec.iter() {
let k_nbrs: HashSet<NodeIndex> =
graph.neighbors_undirected(*k).collect();
let k_deg = k_nbrs.len();

nbrs.insert(*k, k_nbrs);
cores.insert(*k, k_deg);
degree_map.insert(*k, k_deg);
}
node_vec.par_sort_by_key(|k| degree_map.get(k));

let mut bin_boundaries: Vec<usize> =
Vec::with_capacity(degree_map[&node_vec[node_num - 1]] + 1);
bin_boundaries.push(0);
let mut curr_degree = 0;
for (i, v) in node_vec.iter().enumerate() {
node_pos.insert(*v, i);
let v_degree = degree_map[v];
if v_degree > curr_degree {
for _ in 0..v_degree - curr_degree {
bin_boundaries.push(i);
}
curr_degree = v_degree;
}
}

for v_ind in 0..node_vec.len() {
let v = node_vec[v_ind];
let v_nbrs = nbrs[&v].clone();
for u in v_nbrs {
if cores[&u] > cores[&v] {
nbrs.get_mut(&u).unwrap().remove(&v);
let pos = node_pos[&u];
let bin_start = bin_boundaries[cores[&u]];
*node_pos.get_mut(&u).unwrap() = bin_start;
*node_pos.get_mut(&node_vec[bin_start]).unwrap() = pos;
node_vec.swap(bin_start, pos);
bin_boundaries[cores[&u]] += 1;
*cores.get_mut(&u).unwrap() -= 1;
}
}
}

let out_dict = PyDict::new(py);
for (v_index, core) in cores {
out_dict.set_item(v_index.index(), core)?;
}
Ok(out_dict.into())
}
Loading