Skip to content

Commit 70551ca

Browse files
authored
Merge pull request #47 from graspologic-org/dev
1.2.2 Release
2 parents eb7ad2d + 0ea1c92 commit 70551ca

33 files changed

+551
-517
lines changed

.github/workflows/build.yml

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -12,10 +12,10 @@ jobs:
1212
runs-on: "ubuntu-latest"
1313
steps:
1414
- uses: actions/checkout@v4
15-
- name: Set up Python 3.8
15+
- name: Set up Python 3.12
1616
uses: actions/setup-python@v5
1717
with:
18-
python-version: 3.8
18+
python-version: 3.12
1919
- name: Materialize build number
2020
run: |
2121
pip install -U pip
@@ -38,10 +38,10 @@ jobs:
3838
os: ["ubuntu-latest", "windows-latest", "macos-latest"]
3939
steps:
4040
- uses: actions/checkout@v4
41-
- name: Set up Python 3.9
41+
- name: Set up Python 3.12
4242
uses: actions/setup-python@v5
4343
with:
44-
python-version: 3.9
44+
python-version: 3.12
4545
- uses: actions/download-artifact@v4
4646
with:
4747
name: cargo-toml
@@ -106,10 +106,10 @@ jobs:
106106
if: github.ref=='refs/heads/main' || github.ref=='refs/heads/dev'
107107
steps:
108108
- uses: actions/checkout@v4
109-
- name: Set up Python 3.8
109+
- name: Set up Python 3.12
110110
uses: actions/setup-python@v2
111111
with:
112-
python-version: 3.8
112+
python-version: 3.12
113113
- uses: actions/download-artifact@v4
114114
with:
115115
name: dist-ubuntu-latest

.rustfmt.toml

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,2 @@
1-
fn_args_layout = "vertical"
2-
empty_item_single_line = false
1+
fn_params_layout = "vertical"
32

clippy.toml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
too-many-arguments-threshold=20 # for what it is worth, clippy is absolutely right and pythonic-ness is absolutely wrong
2+
enum-variant-name-threshold=10 # it doesn't like the repetition in "Error" in my export-to-python error types
File renamed without changes.

packages/cli/Cargo.toml

Lines changed: 3 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,13 @@
11
[package]
22
name = "cli"
3-
version = "0.1.0"
4-
authors = ["Dwayne Pryce <dwpryce@microsoft.com>"]
3+
version = "0.1.1"
4+
authors = ["Dax Pryce <daxpryce@microsoft.com>"]
55
edition = "2018"
66
license = "MIT"
77
description = "CLI Runner for the topologic associated crates (network_partitions and eventually network_automatic_layouts)"
88

99
[dependencies]
10-
clap = "2.34"
10+
clap = "4.5"
1111
rand = "0.8"
1212
rand_xorshift = "0.3"
1313
network_partitions={path = "../network_partitions"}
14-
15-
[features]
16-
logging = ["network_partitions/logging"]

packages/cli/src/args.rs

Lines changed: 23 additions & 51 deletions
Original file line numberDiff line numberDiff line change
@@ -33,27 +33,30 @@ pub struct CliArgs {
3333
pub skip_first_line: bool,
3434
}
3535

36-
impl TryFrom<ArgMatches<'_>> for CliArgs {
36+
impl TryFrom<ArgMatches> for CliArgs {
3737
type Error = ParseCliError;
3838

39-
fn try_from(matches: ArgMatches<'_>) -> Result<Self, Self::Error> {
40-
let source_edges = matches
41-
.value_of(SOURCE_EDGES)
39+
fn try_from(matches: ArgMatches) -> Result<Self, Self::Error> {
40+
let source_edges: &str = matches
41+
.get_one(SOURCE_EDGES)
42+
.cloned()
4243
.ok_or(ParseCliError::RequiredValueError)?;
43-
let output = matches
44-
.value_of(OUTPUT)
44+
let output: &str = matches
45+
.get_one(OUTPUT)
46+
.cloned()
4547
.ok_or(ParseCliError::RequiredValueError)?;
46-
let separator = matches
47-
.value_of(SEPARATOR)
48+
let separator: &str = matches
49+
.get_one(SEPARATOR)
50+
.cloned()
4851
.ok_or(ParseCliError::RequiredValueError)?;
49-
let source_index: usize = matches.value_of(SOURCE_INDEX).as_a()?;
50-
let target_index: usize = matches.value_of(TARGET_INDEX).as_a()?;
51-
let weight_index: Option<usize> = matches.value_of(WEIGHT_INDEX).as_a()?;
52-
let seed: Option<usize> = matches.value_of(SEED).as_a()?;
53-
let iterations: usize = matches.value_of(ITERATIONS).as_a()?;
54-
let resolution: f64 = matches.value_of(RESOLUTION).as_a()?;
55-
let randomness: f64 = matches.value_of(RANDOMNESS).as_a()?;
56-
let quality_option: Option<&str> = matches.value_of(QUALITY);
52+
let source_index: usize = *matches.get_one(SOURCE_INDEX).unwrap();
53+
let target_index: usize = *matches.get_one(TARGET_INDEX).unwrap();
54+
let weight_index: Option<usize> = matches.get_one(WEIGHT_INDEX).copied();
55+
let seed: Option<usize> = matches.get_one(SEED).cloned();
56+
let iterations: usize = *matches.get_one(ITERATIONS).unwrap();
57+
let resolution: f64 = *matches.get_one(RESOLUTION).unwrap();
58+
let randomness: f64 = *matches.get_one(RANDOMNESS).unwrap();
59+
let quality_option: Option<&str> = matches.get_one(QUALITY).cloned();
5760
let use_modularity: bool = match quality_option {
5861
Some(quality_value) => {
5962
if quality_value == "cpm" {
@@ -66,7 +69,7 @@ impl TryFrom<ArgMatches<'_>> for CliArgs {
6669
}
6770
None => Err(ParseCliError::RequiredValueError),
6871
}?;
69-
let skip_first_line: bool = matches.is_present(HAS_HEADER);
72+
let skip_first_line: bool = matches.contains_id(HAS_HEADER);
7073
let cli_args: CliArgs = CliArgs {
7174
source_edges: source_edges.into(),
7275
output_path: output.into(),
@@ -81,7 +84,7 @@ impl TryFrom<ArgMatches<'_>> for CliArgs {
8184
use_modularity,
8285
skip_first_line,
8386
};
84-
return Ok(cli_args);
87+
Ok(cli_args)
8588
}
8689
}
8790

@@ -94,43 +97,12 @@ pub enum ParseCliError {
9497

9598
impl From<ParseFloatError> for ParseCliError {
9699
fn from(_: ParseFloatError) -> Self {
97-
return ParseCliError::NotANumber;
100+
ParseCliError::NotANumber
98101
}
99102
}
100103

101104
impl From<ParseIntError> for ParseCliError {
102105
fn from(_: ParseIntError) -> Self {
103-
return ParseCliError::NotANumber;
104-
}
105-
}
106-
107-
trait As<T> {
108-
fn as_a(&self) -> Result<T, ParseCliError>;
109-
}
110-
111-
impl As<f64> for Option<&str> {
112-
fn as_a(&self) -> Result<f64, ParseCliError> {
113-
self.map(|cli_arg| cli_arg.parse::<f64>().unwrap())
114-
.ok_or(ParseCliError::RequiredValueError)
115-
}
116-
}
117-
118-
impl As<usize> for Option<&str> {
119-
fn as_a(&self) -> Result<usize, ParseCliError> {
120-
self.map(|cli_arg| cli_arg.parse::<usize>().unwrap())
121-
.ok_or(ParseCliError::RequiredValueError)
122-
}
123-
}
124-
125-
impl As<Option<usize>> for Option<&str> {
126-
fn as_a(&self) -> Result<Option<usize>, ParseCliError> {
127-
let result = match self {
128-
Some(cli_arg) => {
129-
let parse_result = cli_arg.parse::<usize>();
130-
Ok(parse_result.map(|value| Some(value))?)
131-
}
132-
None => Ok(None),
133-
};
134-
return result;
106+
ParseCliError::NotANumber
135107
}
136108
}

packages/cli/src/leiden.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -75,9 +75,9 @@ pub fn leiden(
7575
let mut output_file: File =
7676
File::create(output_path).expect("Unable to open output file for writing");
7777
for item in &clustering {
78-
write!(
78+
writeln!(
7979
output_file,
80-
"{},{}\n",
80+
"{},{}",
8181
labeled_network.label_for(item.node_id),
8282
item.cluster
8383
)

packages/cli/src/main.rs

Lines changed: 28 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,7 @@
11
// Copyright (c) Microsoft Corporation.
22
// Licensed under the MIT license.
33

4-
#![feature(in_band_lifetimes)]
5-
use clap::{App, Arg};
4+
use clap::{Arg, ArgAction, Command};
65
use std::convert::TryFrom;
76

87
mod args;
@@ -11,83 +10,82 @@ mod leiden;
1110
use crate::args::*;
1211

1312
fn main() {
14-
let matches = App::new("leiden_cli")
13+
let matches = Command::new("leiden_cli")
1514
.version("0.1.0")
1615
.author("Dwayne Pryce <[email protected]>")
1716
.about("Runs leiden over a provided edge list and outputs the results")
1817
.arg(
19-
Arg::with_name(SOURCE_EDGES)
18+
Arg::new(SOURCE_EDGES)
2019
.help("The edge list that defines the graph's connections")
2120
.required(true)
2221
.index(1),
2322
)
2423
.arg(
25-
Arg::with_name(OUTPUT)
24+
Arg::new(OUTPUT)
2625
.help("The output for the communities detected")
2726
.required(true)
2827
.index(2),
2928
)
3029
.arg(
31-
Arg::with_name(SEPARATOR)
32-
.short("s")
30+
Arg::new(SEPARATOR)
31+
.short('s')
3332
.help("The character to split the edge list on")
34-
.takes_value(true)
33+
.action(ArgAction::Set)
3534
.default_value("\t"),
3635
)
3736
.arg(
38-
Arg::with_name(SOURCE_INDEX)
39-
.takes_value(true)
37+
Arg::new(SOURCE_INDEX)
38+
.action(ArgAction::Set)
4039
.help("0-based index of source column from edge file")
4140
.default_value("0"),
4241
)
4342
.arg(
44-
Arg::with_name(TARGET_INDEX)
45-
.takes_value(true)
43+
Arg::new(TARGET_INDEX)
44+
.action(ArgAction::Set)
4645
.help("0-based index of target column from edge file")
4746
.default_value("1"),
4847
)
4948
.arg(
50-
Arg::with_name(WEIGHT_INDEX)
51-
.takes_value(true)
49+
Arg::new(WEIGHT_INDEX)
50+
.action(ArgAction::Set)
5251
.help("0-based index of weight column from edge file")
5352
)
5453
.arg(
55-
Arg::with_name(SEED)
56-
.takes_value(true)
54+
Arg::new(SEED)
55+
.action(ArgAction::Set)
5756
.help("A seed value to start the PRNG")
5857
.long("seed"),
5958
)
6059
.arg(
61-
Arg::with_name(ITERATIONS)
62-
.takes_value(true)
60+
Arg::new(ITERATIONS)
61+
.action(ArgAction::Set)
6362
.help("Leiden is an inherently recursive algorithm, however it may find itself (due to randomness) at a localized maximum. Setting iterations to a number larger than 1 may allow you to jump out of a local maximum and continue until a better optimum partitioning is found (note that any n > 1 will mean that leiden will be run again for a minimum of n-1 more times, though it may be run for many more than that")
64-
.short("i")
63+
.short('i')
6564
.default_value("1"),
6665
)
6766
.arg(
68-
Arg::with_name(RESOLUTION)
69-
.takes_value(true)
67+
Arg::new(RESOLUTION)
68+
.action(ArgAction::Set)
7069
.help("")
71-
.short("r")
70+
.short('r')
7271
.default_value("1.0")
7372
)
7473
.arg(
75-
Arg::with_name(RANDOMNESS)
76-
.takes_value(true)
74+
Arg::new(RANDOMNESS)
75+
.action(ArgAction::Set)
7776
.help("")
7877
.default_value("1E-2"),
7978
)
8079
.arg(
81-
Arg::with_name(QUALITY)
82-
.takes_value(true)
80+
Arg::new(QUALITY)
81+
.action(ArgAction::Set)
8382
.help("Quality function to use")
84-
.short("q")
85-
.possible_value("modularity")
86-
.possible_value("cpm")
83+
.short('q')
84+
.value_parser(["modularity", "cpm"])
8785
.default_value("modularity"),
8886
)
8987
.arg(
90-
Arg::with_name(HAS_HEADER)
88+
Arg::new(HAS_HEADER)
9189
.help("Flag must be added if the source file contains a header line")
9290
.long("has_header")
9391
)
Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,14 @@
11
[package]
22
name = "network_partitions"
33
version = "0.1.0"
4-
authors = ["Dwayne Pryce <dwpryce@microsoft.com>"]
4+
authors = ["Dax Pryce <daxpryce@microsoft.com>"]
55
edition = "2018"
66
license = "MIT"
77
description = "Leiden community detection as per https://arxiv.org/abs/1810.08473"
88

99
[dependencies]
1010
rand = "0.8"
11-
chrono = { version = "0.4", optional = true }
1211

1312
[dev-dependencies]
1413
rand_xorshift = "0.3"
1514

16-
[features]
17-
logging = ["chrono"]
18-
debug = []

0 commit comments

Comments
 (0)