diff --git a/Cargo.toml b/Cargo.toml index 69b3c494..6537d375 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -3,11 +3,11 @@ name = "rust_graph" version = "0.1.9" authors = ["Zhengyi Yang "] autoexamples = true +edition="2018" [features] default = [] usize_id = [] -ldbc = ["regex"] [dependencies] indexmap = { version = "1.0.2",features = ["serde-1"] } @@ -20,17 +20,39 @@ bincode = "1.0.1" log = "0.4" csv = "1" counter = "0.4.3" -regex = {version = "1", optional = true } -fnv = "1.0.6" +serde_cbor = "0.9.0" fixedbitset = "0.1.9" +hashbrown = {version = "0.2.0", features = ["serde"] } +rayon = "1.0.3" +serde_json = "1.0.39" +fxhash = "0.2.1" +rocksdb = "0.12.2" +lru = "0.1.15" +scoped_threadpool = "0.1.9" +# tikv-client = { git = "https://github.com/tikv/client-rust.git" } +tikv-client = { git = "https://github.com/tz70s/client-rust.git", branch = "fix-raw-scan-range" } +futures-preview = { version = "0.3.0-alpha.17", features = ["compat"] } +walkdir = "2.0.0" +tokio = "=0.2.13" +#protobuf = { version = "2.0"} +# +#[dependencies.hdfs] +#git="https://github.com/UNSW-database/hdfs-rs.git" +#default-features = false +#optional = true [dev-dependencies] tempfile = "3.0.4" -pbr = "1.0.1" clap = "2.32.0" +criterion = "0.2" +tempdir = "0.3.7" -[[example]] -name = "ldbc_to_graphmap" -required-features = ["ldbc"] +#[patch.crates-io] +#hdfs = {} +[build-dependencies] +protoc-rust = "2.0" + +[patch.crates-io] +raft-proto = { git = "https://github.com/tikv/raft-rs", rev = "e624c1d48460940a40d8aa69b5329460d9af87dd" } \ No newline at end of file diff --git a/LICENSE b/LICENSE index 1a00df9c..ffd17c3b 100644 --- a/LICENSE +++ b/LICENSE @@ -1,18 +1,18 @@ -Copyright (c) 2018 UNSW Sydney, Data and Knowledge Group. - -Licensed to the Apache Software Foundation (ASF) under one -or more contributor license agreements. See the NOTICE file -distributed with this work for additional information -regarding copyright ownership. The ASF licenses this file -to you 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. +Copyright (c) 2018 UNSW Sydney, Data and Knowledge Group. + +Licensed to the Apache Software Foundation (ASF) under one +or more contributor license agreements. See the NOTICE file +distributed with this work for additional information +regarding copyright ownership. The ASF licenses this file +to you 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. diff --git a/README.md b/README.md index c5e4945d..85179d88 100644 --- a/README.md +++ b/README.md @@ -1,3 +1,118 @@ # rust\_graph\_lib -A graph libary written in Rust. \ No newline at end of file +A graph libary written in Rust. Note that You need to install cmake, g++, clang, golang on your machine first and use Rust nightly to build this project. + +## Setup for hdfs reading support + +### 0. Explanations for `build` and `running` stage in `hdfs_lib` +The function for reading files from `hdfs` is based on a library [`hdfs-rs`](https://github.com/hyunsik/hdfs-rs). Because the library is not update for a few years, so I fixed some bugs in the source code and stored in `src/io/hdfs//hdfs_lib`. The project is regard it as a local crate. (Just as `Cago.toml` shows: `hdfs={path="src//io//hdfs//hdfs_lib"}`). +* In the library, we were calling`libhdfs C APIs`[(docs here)](http://hadoop.apache.org/docs/r3.0.0/hadoop-project-dist/hadoop-hdfs/LibHdfs.html) (supported by hadoop) to implement functions. And encapsulate the `libhdfs C APIs` in the library. +* In the path `hdfs_lib/src/native`, there are static library(`libhdfs.a`) and shared object(`libhdfs.so`) for calling `C APIs` in rust. It helps to guarantee that the project will compile successfully even without the hadoop environment. +* In the file `hdfs_lib/build.rs`, we use `build.rs`[(docs here)](https://doc.rust-lang.org/cargo/reference/build-scripts.html#outputs-of-the-build-script) to pass environment variable `rustc-link-search` to prompt compiler to find the static and shared object. +* In the running time for calling `libhdfs C APIs`, it will using `libhdfs.so`,`libjvm.so`,`Java Environment` and `Hadoop jars`.So, +please ensure that you have finished the following steps, if you want to use the functions for `hdfs`. + +### 1. Download hadoop and environment variables +#### 1.1 Requirement: +* Hadoop version >= [2.6.5](http://mirror.bit.edu.cn/apache/hadoop/common/hadoop-2.6.5/) +* Java >=1.8 +* Linux Environment +* In the running time of `libhdfs C APIs` +* Checking in the hadoop you have installed contains `libhdfs.so` in path `$HADOOP_HOME/lib/native/`.(In the pre-build version, hadoop contains it by default) +* Checking in the java you have installed contains `libjvm.so` in path `$JAVA_HOME/jre/lib/amd64/server/`. + +#### 1.2 Environment variables: +Edit shell environment as following command: +``` +vim ~/.bashrc + +# Change the path to your own and appending to the file +export JAVA_HOME=/{YOUR_JAVA_INSTALLED_PATH} +export JRE_HOME=${JAVA_HOME}/jre +export HADOOP_HOME=/{YOUR_HADOOP_INSTALLED_PATH} +export LD_LIBRARY_PATH=${HADOOP_HOME}/lib/native:${JAVA_HOME}/jre/lib/amd64/server #for libhdfs.o linking +CLASSPATH=.:${JAVA_HOME}/lib:${JRE_HOME}/lib +CLASSPATH=${CLASSPATH}":"`find ${HADOOP_HOME}/share/hadoop | awk '{path=path":"$0}END{print path}'` # hadoop's jars +export CLASSPATH +export PATH=${JAVA_HOME}/bin:$HOME/.cargo/bin:$HADOOP_HOME/sbin:$HADOOP_HOME/bin:$PATH + +#flush the environment variable to all shell session +source ~/.bashrc +``` + +### 2. Configuring a pseudo hadoop and hdfs environment +Of course, you can build a real cluster by yourself. What we need in the code is the `hdfs path` and `port`. + +First of all, entering the configure files directory:`cd $HADOOP_HOME/etc/hadoop` + +#### 2.1 Configure `core-site.xml` +```xml + + + hadoop.tmp.dir + file:/usr/local/hadoop/tmp + Abase for other temporary directories. + + + fs.defaultFS + hdfs://localhost:9000 + + + hadoop.http.staticuser.user + cy + + +``` + +#### 2.2 Configure `hdfs-site.xml` +```xml + + + dfs.replication + 1 + + + dfs.namenode.name.dir + file:/usr/local/hadoop/tmp/dfs/name + + + dfs.datanode.data.dir + file:/usr/local/hadoop/tmp/dfs/data + + + dfs.permissions.enabled + false + + +``` + +#### 2.3 Configure `hadoop-env.sh` (Non-essential. Only for JAVA_HOME can't find during starting hdfs) +``` +export JAVA_HOME=/{YOUR_JAVA_INSTALLED_PATH} +``` +### 3. Starting hdfs and checking hdfs status +* Starting hdfs: `./$HADOOP_HOME/sbin/start-dfs.sh` +You'll get a output as following if you are successful: +``` +Starting namenodes on [localhost] +Starting datanodes +Starting secondary namenodes [{MACHINE-NAME}] +``` +* And you can use `jps` command to verify hdfs status. +``` +jps +17248 Jps +16609 DataNode +16482 NameNode +4198 Main +16847 SecondaryNameNode +``` +* Now you can open a explorer to visit hdfs page +`http://localhost:9870/dfshealth.html#tab-overview` +The port maybe different in different version hadoop. Please check on hadoop website. + +### 4. Testing and using hdfs support +For now, you can using `hdfs support` in this library to read from local pseudo hdfs cluster(or real hdfs cluster). +* In order to avoid tests failure in this library. We mark the tests for `hdfs support` as `ignore`. +So, if you want to test them, please using the following command to test the `hdfs support` independently: +`cargo test -- --ignored` diff --git a/bench-tikv-rocksdb.md b/bench-tikv-rocksdb.md new file mode 100644 index 00000000..578b8802 --- /dev/null +++ b/bench-tikv-rocksdb.md @@ -0,0 +1,92 @@ +# Benchmark TiKV and Rocksdb + +## 1. Benchmark TiKV and Rocksdb on a single machine +I have deployed two pd-servers and each pd-server manages one tikv-server. + +(1) The following tests are all based on 100 operations and we record the average time for each operation. +### Insert raw node/edge property operation +| TiKV | Rocksdb | +|------------ |---------------| +| 26~34ms | 55~105ms | + +### Extend one raw node/edge property operation +| TiKV | Rocksdb | +|--------------|-----------------| +| 0.24~0.34ms | 0.4~1.3ms | + +### Get node/edge property(all) operation +Note that this is not a fair comparison because the Rocksdb's `get` operation in `rust_graph_lib` is simply fetch k-v from memory(it reads all k-v pairs into memory when connecting to it, it is more like the `tikv`'s `batch_get` operation) while `TiKV` needs to connect to pd-server and reads data from disk into memory to return it. With the connection time and reading all kv pairs into memory time counted in, the single get operation for rocksdb is actually around `105ms`. + +| TiKV | Rocksdb | +|--------|--------------| +| 3~4ms | 0.03~0.06ms | + +(2) The following `batch_get` operation test is based on batchly get 1000 keys and we record the average time. +### Batch get node/edge property(all) operation + | TiKV | + |----------| + | 0.008ms | + +## 2. Benchmark TiKV on a cluster +I have deployed two pd-servers and each pd-server manages four tikv-servers(totally there are two pd-servers and eight tikv-servers and they are all on different machines). + +### Insert raw node/edge property operation +| TiKV | +|------------ | +| 90ms | + +### Extend one raw node/edge property operation +| TiKV | +|------------| +| 0.6~0.9ms | + +### Get node/edge property(all) operation +| TiKV | +|--------| +| 4~5ms | + +### Batch get node/edge property(all) operation +| TiKV | +|-------------------| +| 0.008ms ~ 0.01ms | + +(Batch get 1000 node/edge properties, and it takes 0.008s ~ 0.01s in total) + +## 3. `Batch` operations performance comparing between TiKV on a cluster and RocksDB +I have deployed three pd-servers and each pd-server manages three tikv-servers(totally there are three pd-servers and six tikv-servers and they are all on different machines). +And using one server to running test program. + +### Batch put operation on DG10 +1. current_thread::Runtime + +|Batch Size|TiKV|RocksDB| +|---|---|---| +|100|610.306s|873.912s| +|500|450.900s|774.325s| +|1000|531.744s|755.248s| +|10000|1739.592s|751.675s| + +2. tokio::Runtime(ThreadPool) + +|Batch Size|TiKV|RocksDB| +|---|---|---| +|100|357.481s|776.099s| +|500|427.291s|873.144s| +|1000|528.148s|786.991s| +|10000|2039.108s|784.073s| + +### Batch put operation on DG60 +|Batch Size|TiKV|RocksDB| +|---|---|---| +|100|2525.971s|13516.203s| +|500|2666.998s|8638.527s| +|1000|2654.622s|8247.292s| +|10000|4054.167s|8104.632s| + +### Batch get node/edge property(all) operation +|Batch Size|TiKV|RocksDB| +|---|---|---| +|100|0.078s ~ 0.079s|0.387s ~ 0.425s| +|500|0.382s ~ 0.387s|1.848s ~ 1.938s| +|1000|0.468s ~ 0.476s|3.869s ~ 3.871s| +(RocksDB using `while` iteration to simulate `batch_get`) \ No newline at end of file diff --git a/benches/property.rs b/benches/property.rs new file mode 100644 index 00000000..cc05bc5f --- /dev/null +++ b/benches/property.rs @@ -0,0 +1,159 @@ +/* + * Copyright (c) 2018 UNSW Sydney, Data and Knowledge Group. + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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. + */ +#![feature(test)] +extern crate rust_graph; +extern crate serde_json; +extern crate test; +extern crate tikv_client; + +use std::time::Instant; +use test::Bencher; + +use rust_graph::property::tikv_property::*; +use rust_graph::property::PropertyGraph; +use serde_json::{json, to_vec}; + +use tikv_client::Config; + +const NODE_PD_SERVER_ADDR: &str = "192.168.2.2:2379"; +const EDGE_PD_SERVER_ADDR: &str = "192.168.2.3:2379"; + +#[bench] +fn bench_tikv_insert_raw_node(b: &mut Bencher) { + let mut graph = TikvProperty::new( + Config::new(vec![NODE_PD_SERVER_ADDR.to_owned()]), + Config::new(vec![EDGE_PD_SERVER_ADDR.to_owned()]), + false, + ) + .unwrap(); + + let new_prop = json!({"name":"jack"}); + let raw_prop = to_vec(&new_prop).unwrap(); + + b.iter(|| graph.insert_node_raw(0u32, raw_prop.clone())); +} + +#[bench] +fn bench_tikv_insert_raw_edge(b: &mut Bencher) { + let mut graph = TikvProperty::new( + Config::new(vec![NODE_PD_SERVER_ADDR.to_owned()]), + Config::new(vec![EDGE_PD_SERVER_ADDR.to_owned()]), + false, + ) + .unwrap(); + + let new_prop = json!({"length":"15"}); + let raw_prop = to_vec(&new_prop).unwrap(); + + b.iter(|| graph.insert_edge_raw(0u32, 1u32, raw_prop.clone())); +} + +#[bench] +fn bench_tikv_extend_raw_node(b: &mut Bencher) { + let mut graph = TikvProperty::new( + Config::new(vec![NODE_PD_SERVER_ADDR.to_owned()]), + Config::new(vec![EDGE_PD_SERVER_ADDR.to_owned()]), + false, + ) + .unwrap(); + + let new_prop = json!({"name":"jack"}); + let raw_prop = to_vec(&new_prop).unwrap(); + let raw_properties = vec![(0u32, raw_prop)].into_iter(); + b.iter(|| graph.extend_node_raw(raw_properties.clone()).unwrap()); +} + +#[bench] +fn bench_tikv_extend_raw_edge(b: &mut Bencher) { + let mut graph = TikvProperty::new( + Config::new(vec![NODE_PD_SERVER_ADDR.to_owned()]), + Config::new(vec![EDGE_PD_SERVER_ADDR.to_owned()]), + false, + ) + .unwrap(); + + let new_prop = json!({"length":"15"}); + let raw_prop = to_vec(&new_prop).unwrap(); + let raw_properties = vec![((0u32, 1u32), raw_prop)].into_iter(); + b.iter(|| graph.extend_edge_raw(raw_properties.clone()).unwrap()); +} + +#[bench] +fn bench_tikv_get_node_property_all(b: &mut Bencher) { + { + let mut graph0 = TikvProperty::new( + Config::new(vec![NODE_PD_SERVER_ADDR.to_owned()]), + Config::new(vec![EDGE_PD_SERVER_ADDR.to_owned()]), + false, + ) + .unwrap(); + + graph0 + .insert_node_property(0u32, json!({"name": "jack"})) + .unwrap(); + + assert_eq!( + graph0.get_node_property_all(0u32).unwrap(), + Some(json!({"name": "jack"})) + ); + } + + let graph1 = TikvProperty::open( + Config::new(vec![NODE_PD_SERVER_ADDR.to_owned()]), + Config::new(vec![EDGE_PD_SERVER_ADDR.to_owned()]), + false, + true, + ) + .unwrap(); + + b.iter(|| graph1.get_node_property_all(0u32).unwrap()); +} + +#[bench] +fn bench_tikv_get_edge_property_all(b: &mut Bencher) { + { + let mut graph0 = TikvProperty::new( + Config::new(vec![NODE_PD_SERVER_ADDR.to_owned()]), + Config::new(vec![EDGE_PD_SERVER_ADDR.to_owned()]), + false, + ) + .unwrap(); + + graph0 + .insert_edge_property(0u32, 1u32, json!({"name": "jack"})) + .unwrap(); + + assert_eq!( + graph0.get_edge_property_all(0u32, 1u32).unwrap(), + Some(json!({"name": "jack"})) + ); + } + + let graph1 = TikvProperty::open( + Config::new(vec![NODE_PD_SERVER_ADDR.to_owned()]), + Config::new(vec![EDGE_PD_SERVER_ADDR.to_owned()]), + false, + true, + ) + .unwrap(); + + b.iter(|| graph1.get_node_property_all(0u32, 1u32).unwrap()); +} diff --git a/benches/tikv_rocksdb_batch_test.rs b/benches/tikv_rocksdb_batch_test.rs new file mode 100644 index 00000000..b947900f --- /dev/null +++ b/benches/tikv_rocksdb_batch_test.rs @@ -0,0 +1,223 @@ +/* + * Copyright (c) 2018 UNSW Sydney, Data and Knowledge Group. + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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. + */ +#![feature(async_await)] +extern crate rust_graph; +extern crate tempdir; +extern crate tikv_client; +use serde_json::{json, to_vec}; + +use rust_graph::io::rocksdb::rocksdb_loader::RocksDBLoader; +use rust_graph::io::tikv::tikv_loader::TikvLoader; +use rust_graph::io::{CSVReader, GraphLoader}; +use rust_graph::property::tikv_property::TikvProperty; +use rust_graph::property::{PropertyGraph, RocksProperty}; +use std::str::FromStr; +use std::time::Instant; +use test::Bencher; +use tikv_client::Config; + +#[bench] +fn bench_tikv_batch_get_node_property_all(b: &mut Bencher) { + let batch_size = 500; + let mut graph = TikvProperty::new( + Config::new(vec![ + "192.168.2.3::2379", + "192.168.2.4:2379", + "192.168.2.5:2379", + ]), + Config::new(vec![ + "192.168.2.3::2379", + "192.168.2.4:2379", + "192.168.2.5:2379", + ]), + false, + ) + .unwrap(); + + let mut raw_props = Vec::new(); + for i in 0..batch_size { + let key = i.to_string(); + let value = (i + 1).to_string(); + let new_prop = json!({ key: value }); + let raw_prop = to_vec(&new_prop).unwrap(); + raw_props.extend(vec![(i, raw_prop)]); + } + graph.extend_node_raw(raw_props.into_iter()).unwrap(); + + let keys = (0..batch_size).collect(); + + b.iter(|| { + let pairs = graph.batch_get_node_property_all(keys).unwrap(); + }); + + assert_eq!(pairs.unwrap().len(), batch_size); + let mut i = 0u32; + while i < batch_size { + let node_property = graph.get_node_property_all(i).unwrap(); + assert_eq!( + Some(json!({i.to_string(): (i + 1).to_string()})), + node_property + ); + i += 1; + } +} + +#[bench] +fn bench_tikv_batch_get_edge_property_all(b: &mut Bencher) { + let batch_size = 500; + let mut graph = TikvProperty::new( + Config::new(vec![ + "192.168.2.3::2379", + "192.168.2.4:2379", + "192.168.2.5:2379", + ]), + Config::new(vec![ + "192.168.2.3::2379", + "192.168.2.4:2379", + "192.168.2.5:2379", + ]), + false, + ) + .unwrap(); + + let mut raw_props = Vec::new(); + for i in 0..batch_size { + let key = i.to_string(); + let value = (i + 1).to_string(); + let new_prop = json!({ key: value }); + let raw_prop = to_vec(&new_prop).unwrap(); + raw_props.extend(vec![((i, i + 1), raw_prop)]); + } + graph.extend_edge_raw(raw_props.into_iter()).unwrap(); + + let keys = (0..batch_size).map(|x| (x, x + 1)).collect(); + + b.iter(|| { + let pairs = graph.batch_get_edge_property_all(keys).unwrap(); + }); + + assert_eq!(pairs.unwrap().len(), batch_size); + let mut i = 0u32; + while i < batch_size { + let edge_property = graph.get_edge_property_all(i, i + 1).unwrap(); + assert_eq!( + Some(json!({i.to_string(): (i + 1).to_string()})), + edge_property + ); + i += 1; + } +} + +#[bench] +fn bench_rocksdb_get_node_property_all(b: &mut Bencher) { + let node = tempdir::TempDir::new("node").unwrap(); + let edge = tempdir::TempDir::new("edge").unwrap(); + let batch_size = 500; + + let node_path = node.path(); + let edge_path = edge.path(); + { + let mut graph0 = RocksProperty::new(node_path, edge_path, false).unwrap(); + for i in 0..batch_size { + graph0 + .insert_node_property(0u32, json!({"name": "jack"})) + .unwrap(); + } + } + let graph1 = RocksProperty::open(node_path, edge_path, false, true).unwrap(); + + b.iter(|| { + for i in 0..batch_size { + graph1.get_node_property_all(0u32).unwrap(); + } + }); +} + +#[bench] +fn bench_rocksdb_get_edge_property_all(b: &mut Bencher) { + let node = tempdir::TempDir::new("node").unwrap(); + let edge = tempdir::TempDir::new("edge").unwrap(); + let batch_size = 500; + + let node_path = node.path(); + let edge_path = edge.path(); + { + let mut graph0 = RocksProperty::new(node_path, edge_path, false).unwrap(); + for i in 0..batch_size { + graph0 + .insert_edge_property(i, 1u32, json!({"name": "jack"})) + .unwrap(); + } + } + let graph1 = RocksProperty::open(node_path, edge_path, false, true).unwrap(); + b.iter(|| { + for i in 0..batch_size { + graph1.get_edge_property_all(0u32, 1u32).unwrap(); + } + }); +} + +fn load_graph_to_tikv(nodes: &str, edges: &str, thread_cnt: usize, batch_size: usize) { + let node_pd_server_addr: Vec<&str> = + vec!["192.168.2.3:2379", "192.168.2.4:2379", "192.168.2.5:2379"]; + let edge_pd_server_addr: Vec<&str> = + vec!["192.168.2.3:2379", "192.168.2.4:2379", "192.168.2.5:2379"]; + + let reader = CSVReader::::new(vec![nodes], vec![edges]) + .headers(true) + .flexible(true) + .with_separator("bar"); + + let tike_loader = TikvLoader::new( + Config::new(node_pd_server_addr.to_owned()), + Config::new(edge_pd_server_addr.to_owned()), + false, + ); + + let start = Instant::now(); + tike_loader.load(&reader, thread_cnt, batch_size); + let duration = start.elapsed(); + let total_time = duration.as_secs() as f64 + duration.subsec_nanos() as f64 * 1e-9; + println!("Finished tikv graph insertion in {} seconds.", total_time); +} + +fn load_graph_to_rocksdb(nodes: &str, edges: &str, batch_size: usize) { + let node = tempdir::TempDir::new_in(".", "node").unwrap(); + let edge = tempdir::TempDir::new_in(".", "edge").unwrap(); + + let node_path = node.path(); + let edge_path = edge.path(); + let rocks_db_loader = RocksDBLoader::new(node_path, edge_path, false); + + let reader = CSVReader::::new(vec![nodes], vec![edges]) + .headers(true) + .flexible(true) + .with_separator("bar"); + + let start = Instant::now(); + rocks_db_loader.load(&reader, 1, batch_size); + let duration = start.elapsed(); + let total_time = duration.as_secs() as f64 + duration.subsec_nanos() as f64 * 1e-9; + println!( + "Finished rocksdb graph insertion in {} seconds.", + total_time + ); +} diff --git a/examples/csv_to_static.rs b/examples/csv_to_static.rs index d790f4a2..59b1131a 100644 --- a/examples/csv_to_static.rs +++ b/examples/csv_to_static.rs @@ -1,143 +1,182 @@ -/* - * Copyright (c) 2018 UNSW Sydney, Data and Knowledge Group. - * - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you 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. - */ -extern crate clap; -extern crate rust_graph; - -use std::path::Path; -use std::time::Instant; - -use clap::{App, Arg}; - -use rust_graph::io::read_from_csv; -use rust_graph::io::serde::Serialize; -use rust_graph::{DiGraphMap, UnGraphMap}; - -fn main() { - let matches = App::new("CSV to StaticGraph Converter") - .arg( - Arg::with_name("node_file") - .short("n") - .long("node") - .takes_value(true), - ).arg( - Arg::with_name("edge_file") - .short("e") - .long("edge") - .required(true) - .takes_value(true), - ).arg( - Arg::with_name("out_file") - .short("o") - .long("out") - .takes_value(true), - ).arg( - Arg::with_name("separator") - .short("s") - .long("separator") - .long_help("allowed separator: [comma|space|tab]") - .takes_value(true), - ).arg( - Arg::with_name("has_headers") - .short("h") - .long("headers") - .multiple(true), - ).arg( - Arg::with_name("is_flexible") - .short("f") - .long("flexible") - .multiple(true), - ).arg( - Arg::with_name("is_directed") - .short("d") - .long("directed") - .multiple(true), - ).arg( - Arg::with_name("reorder_node_id") - .short("i") - .long("reorder_nodes") - .multiple(true), - ).arg( - Arg::with_name("reorded_label_id") - .short("l") - .long("reorder_labels") - .multiple(true), - ).get_matches(); - - let node_file = matches.value_of("node_file").map(Path::new); - let edge_file = Path::new(matches.value_of("edge_file").unwrap()); - let out_file = Path::new(matches.value_of("out_file").unwrap_or("graph.static")); - let separator = matches.value_of("separator"); - let is_directed = matches.is_present("is_directed"); - let has_headers = matches.is_present("has_headers"); - let is_flexible = matches.is_present("is_flexible"); - let reorder_node_id = matches.is_present("reorder_node_id"); - let reorder_label_id = matches.is_present("reorder_label_id"); - - let start = Instant::now(); - - if is_directed { - let mut g = DiGraphMap::::new(); - println!("Reading graph"); - read_from_csv( - &mut g, - node_file, - edge_file, - separator, - has_headers, - is_flexible, - ).expect("Error when loading csv"); - - println!("Converting graph"); - let static_graph = g - .reorder_id(reorder_node_id, reorder_label_id, reorder_label_id) - .take_graph() - .unwrap() - .into_static(); - - static_graph.export(out_file).unwrap() - } else { - let mut g = UnGraphMap::::new(); - println!("Reading graph"); - read_from_csv( - &mut g, - node_file, - edge_file, - separator, - has_headers, - is_flexible, - ).expect("Error when loading csv"); - - println!("Converting graph"); - let static_graph = g - .reorder_id(reorder_node_id, reorder_label_id, reorder_label_id) - .take_graph() - .unwrap() - .into_static(); - - static_graph.export(out_file).unwrap() - } - - let duration = start.elapsed(); - println!( - "Finished in {} seconds.", - duration.as_secs() as f64 + duration.subsec_nanos() as f64 * 1e-9 - ); -} +/* + * Copyright (c) 2018 UNSW Sydney, Data and Knowledge Group. + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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. + */ +extern crate clap; +extern crate rust_graph; + +use std::path::PathBuf; +use std::time::Instant; + +use clap::{App, Arg}; + +use rust_graph::io::read_from_csv; +use rust_graph::io::serde::Serialize; +use rust_graph::{DiGraphMap, UnGraphMap}; + +fn main() { + let matches = App::new("CSV to StaticGraph Converter") + .arg( + Arg::with_name("node_file") + .short("n") + .long("node") + .takes_value(true), + ) + .arg( + Arg::with_name("edge_file") + .short("e") + .long("edge") + .required(true) + .takes_value(true), + ) + .arg( + Arg::with_name("out_file") + .short("o") + .long("out") + .takes_value(true), + ) + .arg( + Arg::with_name("separator") + .short("s") + .long("separator") + .long_help("allowed separator: [comma|space|tab]") + .takes_value(true), + ) + .arg( + Arg::with_name("has_headers") + .short("h") + .long("headers") + .multiple(true), + ) + .arg( + Arg::with_name("is_flexible") + .short("f") + .long("flexible") + .multiple(true), + ) + .arg( + Arg::with_name("is_directed") + .short("d") + .long("directed") + .multiple(true), + ) + .arg( + Arg::with_name("reorder_node_id") + .short("i") + .long("reorder_nodes") + .multiple(true), + ) + .arg( + Arg::with_name("reorded_label_id") + .short("l") + .long("reorder_labels") + .multiple(true), + ) + .get_matches(); + + let node_file = matches.value_of("node_file").map(PathBuf::from); + let edge_file = PathBuf::from(matches.value_of("edge_file").unwrap()); + + let node_path = match node_file { + Some(p) => { + if p.is_dir() { + let mut vec = ::std::fs::read_dir(p) + .unwrap() + .map(|x| x.unwrap().path()) + .collect::>(); + vec.sort(); + + vec + } else { + vec![p] + } + } + None => Vec::new(), + }; + + let edge_path = if edge_file.is_dir() { + let mut vec = ::std::fs::read_dir(edge_file) + .unwrap() + .map(|x| x.unwrap().path()) + .collect::>(); + vec.sort(); + + vec + } else { + vec![edge_file] + }; + + let out_file = PathBuf::from(matches.value_of("out_file").unwrap_or("graph.static")); + let separator = matches.value_of("separator"); + let is_directed = matches.is_present("is_directed"); + let has_headers = matches.is_present("has_headers"); + let is_flexible = matches.is_present("is_flexible"); + let reorder_node_id = matches.is_present("reorder_node_id"); + let reorder_label_id = matches.is_present("reorder_label_id"); + + let start = Instant::now(); + + if is_directed { + let mut g = DiGraphMap::::new(); + println!("Reading graph"); + read_from_csv( + &mut g, + node_path, + edge_path, + separator, + has_headers, + is_flexible, + ); + + println!("Converting graph"); + let static_graph = g + .reorder_id(reorder_node_id, reorder_label_id, reorder_label_id) + .take_graph() + .unwrap() + .into_static(); + + static_graph.export(out_file).unwrap() + } else { + let mut g = UnGraphMap::::new(); + println!("Reading graph"); + read_from_csv( + &mut g, + node_path, + edge_path, + separator, + has_headers, + is_flexible, + ); + + println!("Converting graph"); + let static_graph = g + .reorder_id(reorder_node_id, reorder_label_id, reorder_label_id) + .take_graph() + .unwrap() + .into_static(); + + static_graph.export(out_file).unwrap() + } + + let duration = start.elapsed(); + println!( + "Finished in {} seconds.", + duration.as_secs() as f64 + duration.subsec_nanos() as f64 * 1e-9 + ); +} diff --git a/examples/graph_to_tikv.rs b/examples/graph_to_tikv.rs new file mode 100644 index 00000000..89e57ed3 --- /dev/null +++ b/examples/graph_to_tikv.rs @@ -0,0 +1,84 @@ +#![feature(async_await)] +extern crate rust_graph; +extern crate tempfile; + +use rust_graph::graph_impl::DiGraphMap; +use rust_graph::io::{write_to_csv, GraphLoader}; +use rust_graph::prelude::*; + +use rust_graph::io::csv::CSVReader; +use rust_graph::io::tikv::tikv_loader::TikvLoader; +use serde_cbor::{to_vec, ObjectKey, Value}; +use std::collections::BTreeMap; +use tempfile::TempDir; +use tikv_client::raw::Client; +use tikv_client::Config; + +//const NODE_PD_SERVER_ADDR: &str = "192.168.2.2:2379"; +//const EDGE_PD_SERVER_ADDR: &str = "192.168.2.7:2379"; + +const NODE_PD_SERVER_ADDR: &str = "127.0.0.1:2379"; +const EDGE_PD_SERVER_ADDR: &str = "127.0.0.1:2379"; + +fn main() { + //Construct test csv files + let tmp_dir = TempDir::new().unwrap(); + let tmp_dir_path = tmp_dir.path(); + + let mut g = DiGraphMap::<&str>::new(); + + g.add_node(0, Some("n0")); + g.add_node(1, Some("n1")); + g.add_node(2, Some("n2")); + + g.add_edge(0, 1, Some("e0")); + g.add_edge(0, 2, Some("e1")); + g.add_edge(1, 0, Some("e2")); + let path_to_nodes = tmp_dir_path.join("nodes_1.csv"); + let path_to_edges = tmp_dir_path.join("edges_1.csv"); + assert!(write_to_csv(&g, &path_to_nodes, &path_to_edges).is_ok()); + + //Construct csvReader + let reader = CSVReader::::new(vec![path_to_nodes], vec![path_to_edges]) + .headers(true) + .flexible(true); + + TikvLoader::new( + Config::new(vec![NODE_PD_SERVER_ADDR.to_owned()]), + Config::new(vec![EDGE_PD_SERVER_ADDR.to_owned()]), + false, + ) + .load(&reader, 1, 10); + + //Verifying nodes and edges storing in tikv + futures::executor::block_on(async { + let client = Client::new(Config::new(vec![NODE_PD_SERVER_ADDR.to_owned()])).unwrap(); + let _node = client + .get(bincode::serialize(&0).unwrap()) + .await + .expect("Get node info failed!"); + println!("Node0 from tikv: {:?}", _node); + match _node { + Some(value_bytes) => { + let bytes: Vec = value_bytes.into(); + let empty_map: BTreeMap = BTreeMap::new(); + assert_eq!(bytes, to_vec(&(Some("n0"), empty_map)).unwrap()); + } + None => assert!(false), + } + let client = Client::new(Config::new(vec![EDGE_PD_SERVER_ADDR.to_owned()])).unwrap(); + let _edge = client + .get(bincode::serialize(&(0, 1)).unwrap()) + .await + .expect("Get node info failed!"); + println!("Edge(0,1) from tikv: {:?}", _edge); + match _edge { + Some(value_bytes) => { + let bytes: Vec = value_bytes.into(); + let empty_map: BTreeMap = BTreeMap::new(); + assert_eq!(bytes, to_vec(&(Some("e0"), empty_map)).unwrap()); + } + None => assert!(false), + } + }); +} diff --git a/examples/ldbc_to_graphmap.rs b/examples/ldbc_to_graphmap.rs deleted file mode 100644 index e992cffb..00000000 --- a/examples/ldbc_to_graphmap.rs +++ /dev/null @@ -1,67 +0,0 @@ -/* - * Copyright (c) 2018 UNSW Sydney, Data and Knowledge Group. - * - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you 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. - */ -extern crate rust_graph; - -use std::path::Path; -use std::time::Instant; - -use rust_graph::io::serde::Serialize; -use rust_graph::io::*; -use rust_graph::prelude::*; - -fn main() { - let args: Vec<_> = std::env::args().collect(); - - let ldbc_dir = Path::new(&args[1]); - let output_dir = Path::new(&args[2]); - - let start = Instant::now(); - - println!("Loading {:?}", &ldbc_dir); - let g = read_ldbc_from_path::(ldbc_dir); - let num_of_nodes = g.node_count(); - let num_of_edges = g.edge_count(); - - println!("{} nodes, {} edges.", num_of_nodes, num_of_edges); - - println!("Node labels: {:?}", g.get_node_label_map()); - println!("Edge labels: {:?}", g.get_edge_label_map()); - - let dir_name = ldbc_dir - .components() - .last() - .unwrap() - .as_os_str() - .to_str() - .unwrap(); - let export_filename = format!("{}_{}_{}.graphmap", dir_name, num_of_nodes, num_of_edges); - let export_path = output_dir.join(export_filename); - - println!("Exporting to {:?}...", export_path); - - g.export(export_path).unwrap(); - - let duration = start.elapsed(); - println!( - "Finished in {} seconds.", - duration.as_secs() as f64 + duration.subsec_nanos() as f64 * 1e-9 - ); -} diff --git a/examples/static_to_csv.rs b/examples/static_to_csv.rs index 24b5ecff..a6a96980 100644 --- a/examples/static_to_csv.rs +++ b/examples/static_to_csv.rs @@ -1,59 +1,59 @@ -/* - * Copyright (c) 2018 UNSW Sydney, Data and Knowledge Group. - * - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you 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. - */ -extern crate rust_graph; - -use std::fs::create_dir_all; -use std::path::Path; -use std::time::Instant; - -use rust_graph::io::serde::Deserialize; -use rust_graph::io::write_to_csv; -use rust_graph::prelude::*; -use rust_graph::UnGraphMap; - -fn main() { - let args: Vec<_> = std::env::args().collect(); - - let in_file = Path::new(&args[1]); - let out_dir = Path::new(&args[2]); - - let start = Instant::now(); - - println!("Loading {:?}", &in_file); - let g = UnGraphMap::::import(in_file).expect("Deserializer error"); - - println!("{:?}", g.get_node_label_map()); - println!("{:?}", g.get_edge_label_map()); - - if !out_dir.exists() { - create_dir_all(out_dir).unwrap(); - } - - println!("Exporting to {:?}...", &out_dir); - - write_to_csv(&g, out_dir.join("nodes.csv"), out_dir.join("edges.csv")).unwrap(); - - let duration = start.elapsed(); - println!( - "Finished in {} seconds.", - duration.as_secs() as f64 + duration.subsec_nanos() as f64 * 1e-9 - ); -} +/* + * Copyright (c) 2018 UNSW Sydney, Data and Knowledge Group. + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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. + */ +extern crate rust_graph; + +use std::fs::create_dir_all; +use std::path::Path; +use std::time::Instant; + +use rust_graph::io::serde::Deserialize; +use rust_graph::io::write_to_csv; +use rust_graph::prelude::*; +use rust_graph::UnGraphMap; + +fn main() { + let args: Vec<_> = std::env::args().collect(); + + let in_file = Path::new(&args[1]); + let out_dir = Path::new(&args[2]); + + let start = Instant::now(); + + println!("Loading {:?}", &in_file); + let g = UnGraphMap::::import(in_file).expect("Deserializer error"); + + println!("{:?}", g.get_node_label_map()); + println!("{:?}", g.get_edge_label_map()); + + if !out_dir.exists() { + create_dir_all(out_dir).unwrap(); + } + + println!("Exporting to {:?}...", &out_dir); + + write_to_csv(&g, out_dir.join("nodes.csv"), out_dir.join("edges.csv")).unwrap(); + + let duration = start.elapsed(); + println!( + "Finished in {} seconds.", + duration.as_secs() as f64 + duration.subsec_nanos() as f64 * 1e-9 + ); +} diff --git a/examples/static_to_mmap.rs b/examples/static_to_mmap.rs deleted file mode 100644 index 3a3769c0..00000000 --- a/examples/static_to_mmap.rs +++ /dev/null @@ -1,56 +0,0 @@ -/* - * Copyright (c) 2018 UNSW Sydney, Data and Knowledge Group. - * - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you 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. - */ -extern crate clap; -extern crate rust_graph; - -use clap::{App, Arg}; -use rust_graph::graph_impl::{DiStaticGraph, UnStaticGraph}; -use rust_graph::io::serde::Deserialize; - -fn main() { - let matches = App::new("StaticGraph to MMap") - .arg( - Arg::with_name("graph") - .short("g") - .long("graph") - .required(true) - .takes_value(true), - ).arg(Arg::with_name("directed").short("d").long("directed")) - .arg( - Arg::with_name("output") - .short("o") - .long("output") - .required(true) - .takes_value(true), - ).get_matches(); - - let graph = matches.value_of("graph").unwrap(); - let output = matches.value_of("output").unwrap(); - let is_directed = matches.is_present("directed"); - - if !is_directed { - let graph = UnStaticGraph::::import(graph).unwrap(); - graph.dump_mmap(output).expect("Dump graph error"); - } else { - let graph = DiStaticGraph::::import(graph).unwrap(); - graph.dump_mmap(output).expect("Dump graph error"); - } -} diff --git a/examples/stats.rs b/examples/stats.rs index 5a6376d8..608ffc9a 100644 --- a/examples/stats.rs +++ b/examples/stats.rs @@ -1,68 +1,68 @@ -/* - * Copyright (c) 2018 UNSW Sydney, Data and Knowledge Group. - * - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you 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. - */ -extern crate rust_graph; - -use std::time::Instant; - -use rust_graph::io::serde::Deserialize; -use rust_graph::prelude::*; -use rust_graph::{UnGraphMap, UnStaticGraph}; - -fn main() { - let args: Vec<_> = std::env::args().skip(1).collect(); - - let start = Instant::now(); - - for arg in args { - println!("------------------------------"); - println!("Loading {}", &arg); - - let g = UnStaticGraph::::import(arg).unwrap(); - - let max_degree = g.node_indices().map(|i| g.degree(i)).max().unwrap(); - - println!("Max degree: {}", max_degree); - - let node_labels_counter = g.get_node_label_counter(); - let edge_labels_counter = g.get_edge_label_counter(); - - println!("Node labels:"); - - for (label, count) in node_labels_counter.most_common() { - println!("- {} : {}", label, count); - } - - println!(); - println!("Edge labels:"); - - for (label, count) in edge_labels_counter.most_common() { - println!("- {} : {}", label, count); - } - - println!("------------------------------"); - } - - let duration = start.elapsed(); - println!( - "Finished in {} seconds.", - duration.as_secs() as f64 + duration.subsec_nanos() as f64 * 1e-9 - ); -} +/* + * Copyright (c) 2018 UNSW Sydney, Data and Knowledge Group. + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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. + */ +extern crate rust_graph; + +use std::time::Instant; + +use rust_graph::io::serde::Deserialize; +use rust_graph::prelude::*; +use rust_graph::UnStaticGraph; + +fn main() { + let args: Vec<_> = std::env::args().skip(1).collect(); + + let start = Instant::now(); + + for arg in args { + println!("------------------------------"); + println!("Loading {}", &arg); + + let g = UnStaticGraph::::import(arg).unwrap(); + + let max_degree = g.node_indices().map(|i| g.degree(i)).max().unwrap(); + + println!("Max degree: {}", max_degree); + + let node_labels_counter = g.get_node_label_counter(); + let edge_labels_counter = g.get_edge_label_counter(); + + println!("Node labels:"); + + for (label, count) in node_labels_counter.most_common() { + println!("- {} : {}", label, count); + } + + println!(); + println!("Edge labels:"); + + for (label, count) in edge_labels_counter.most_common() { + println!("- {} : {}", label, count); + } + + println!("------------------------------"); + } + + let duration = start.elapsed(); + println!( + "Finished in {} seconds.", + duration.as_secs() as f64 + duration.subsec_nanos() as f64 * 1e-9 + ); +} diff --git a/examples/tikv_rocksdb_test.rs b/examples/tikv_rocksdb_test.rs new file mode 100644 index 00000000..43dbf870 --- /dev/null +++ b/examples/tikv_rocksdb_test.rs @@ -0,0 +1,827 @@ +/* + * Copyright (c) 2018 UNSW Sydney, Data and Knowledge Group. + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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. + */ +#![feature(async_await)] +extern crate rust_graph; +extern crate serde_json; +extern crate tempdir; +extern crate tikv_client; + +use rust_graph::property::rocks_property::*; +use rust_graph::property::tikv_property::*; +use rust_graph::property::PropertyGraph; +use serde_json::{json, to_vec}; +use std::time::Instant; +use tikv_client::Config; + +const NODE_PD_SERVER_ADDR: &str = "192.168.2.2:2379"; +const EDGE_PD_SERVER_ADDR: &str = "192.168.2.7:2379"; + +fn main() { + println!("Testing tikv..."); + test_insert_raw_node(); + println!("test_insert_raw_node success..."); + test_insert_raw_edge(); + println!("test_insert_raw_edge success..."); + test_insert_property_node(); + println!("test_insert_property_node success..."); + test_insert_property_edge(); + println!("test_insert_property_edge success..."); + test_extend_raw_node(); + println!("test_extend_raw_node success..."); + test_extend_raw_edge(); + println!("test_extend_raw_edge success..."); + test_extend_property_node(); + println!("test_extend_property_node success..."); + test_extend_property_edge(); + println!("test_extend_property_edge success..."); + test_open_existing_db(); + println!("test_open_existing_db success..."); + test_open_readonly_db(); + println!("test_open_readonly_db success..."); + test_open_writable_db(); + println!("test_open_writable_db success..."); + test_scan_node_property(); + println!("test_scan_node_property success..."); + test_scan_edge_property(); + println!("test_scan_edge_property success..."); + + println!("\nTesting tikv time ..."); + + println!("Test tikv_insert_raw_node time..."); + time_tikv_insert_raw_node(); + println!("Test tikv_insert_raw_edge time..."); + time_tikv_insert_raw_edge(); + println!("Test tikv_extend_raw_node time..."); + time_tikv_extend_raw_node(); + println!("Test tikv_extend_edge_node time..."); + time_tikv_extend_raw_edge(); + println!("Test tikv_get_node_property_all time..."); + time_tikv_get_node_property_all(); + println!("Test tikv_get_edge_property_all time..."); + time_tikv_get_edge_property_all(); + + println!("Testing rocksdb time ..."); + + println!("Test rocksdb_insert_raw_node time..."); + time_rocksdb_insert_raw_node(); + println!("Test rocksdb_insert_raw_edge time..."); + time_rocksdb_insert_raw_edge(); + println!("Test rocksdb_extend_raw_node time..."); + time_rocksdb_extend_raw_node(); + println!("Test rocksdb_extend_edge_edge time..."); + time_rocksdb_extend_raw_edge(); + println!("Test rocksdb_get_node_property_all time..."); + time_rocksdb_get_node_property_all(); + println!("Test rocksdb_get_edge_property_all time..."); + time_rocksdb_get_edge_property_all(); + + println!("Test tikv_batch_get_node_property_all time..."); + time_tikv_batch_get_node_property_all(); + println!("Test tikv_batch_get_edge_property_all time..."); + time_tikv_batch_get_edge_property_all(); +} + +fn time_tikv_batch_get_node_property_all() { + let mut graph = TikvProperty::new( + Config::new(vec![NODE_PD_SERVER_ADDR.to_owned()]), + Config::new(vec![EDGE_PD_SERVER_ADDR.to_owned()]), + false, + ) + .unwrap(); + + let mut raw_props = Vec::new(); + for i in 0..1000u32 { + let key = i.to_string(); + let value = (i + 1).to_string(); + let new_prop = json!({ key: value }); + let raw_prop = to_vec(&new_prop).unwrap(); + raw_props.extend(vec![(i, raw_prop)]); + } + graph.extend_node_raw(raw_props.into_iter()).unwrap(); + + let keys = (0..1000u32).collect(); + let start = Instant::now(); + let pairs = graph.batch_get_node_property_all(keys).unwrap(); + let duration = start.elapsed(); + let total_time = duration.as_secs() as f64 + duration.subsec_nanos() as f64 * 1e-9; + println!( + "Finished 1000 tikv node property batch get in {} seconds, and it takes {}ms per get.", + total_time, total_time + ); + assert_eq!(pairs.unwrap().len(), 1000); + let mut i = 0u32; + while i < 1000u32 { + let node_property = graph.get_node_property_all(i).unwrap(); + assert_eq!( + Some(json!({i.to_string(): (i + 1).to_string()})), + node_property + ); + i += 1; + } +} + +fn time_tikv_batch_get_edge_property_all() { + let mut graph = TikvProperty::new( + Config::new(vec![NODE_PD_SERVER_ADDR.to_owned()]), + Config::new(vec![EDGE_PD_SERVER_ADDR.to_owned()]), + false, + ) + .unwrap(); + + let mut raw_props = Vec::new(); + for i in 0..1000u32 { + let key = i.to_string(); + let value = (i + 1).to_string(); + let new_prop = json!({ key: value }); + let raw_prop = to_vec(&new_prop).unwrap(); + raw_props.extend(vec![((i, i + 1), raw_prop)]); + } + graph.extend_edge_raw(raw_props.into_iter()).unwrap(); + + let keys = (0..1000u32).map(|x| (x, x + 1)).collect(); + let start = Instant::now(); + let pairs = graph.batch_get_edge_property_all(keys).unwrap(); + let duration = start.elapsed(); + let total_time = duration.as_secs() as f64 + duration.subsec_nanos() as f64 * 1e-9; + println!( + "Finished 1000 tikv edge property batch get in {} seconds, and it takes {}ms per get.", + total_time, total_time + ); + assert_eq!(pairs.unwrap().len(), 1000); + let mut i = 0u32; + while i < 1000u32 { + let edge_property = graph.get_edge_property_all(i, i + 1).unwrap(); + assert_eq!( + Some(json!({i.to_string(): (i + 1).to_string()})), + edge_property + ); + i += 1; + } +} + +fn time_tikv_insert_raw_node() { + let mut graph = TikvProperty::new( + Config::new(vec![NODE_PD_SERVER_ADDR.to_owned()]), + Config::new(vec![EDGE_PD_SERVER_ADDR.to_owned()]), + false, + ) + .unwrap(); + + let mut raw_props = Vec::new(); + for i in 0..100u32 { + let key = i.to_string(); + let value = (i + 1).to_string(); + let new_prop = json!({ key: value }); + let raw_prop = to_vec(&new_prop).unwrap(); + raw_props.push(raw_prop); + } + + let start = Instant::now(); + for i in 0..100 { + graph.insert_node_raw(i, raw_props[i].clone()).unwrap(); + } + let duration = start.elapsed(); + let total_time = duration.as_secs() as f64 + duration.subsec_nanos() as f64 * 1e-9; + + println!( + "Finished 100 tikv node property insertion in {} seconds, and it takes {}ms per insertion.", + total_time, + total_time * 10f64 + ); +} + +fn time_tikv_insert_raw_edge() { + let mut graph = TikvProperty::new( + Config::new(vec![NODE_PD_SERVER_ADDR.to_owned()]), + Config::new(vec![EDGE_PD_SERVER_ADDR.to_owned()]), + false, + ) + .unwrap(); + + let mut raw_props = Vec::new(); + for i in 0..100u32 { + let key = i.to_string(); + let value = (i + 1).to_string(); + let new_prop = json!({ key: value }); + let raw_prop = to_vec(&new_prop).unwrap(); + raw_props.push(raw_prop); + } + + let start = Instant::now(); + for i in 0..100 { + graph + .insert_edge_raw(i, i + 1, raw_props[i].clone()) + .unwrap(); + } + let duration = start.elapsed(); + let total_time = duration.as_secs() as f64 + duration.subsec_nanos() as f64 * 1e-9; + + println!( + "Finished 100 tikv edge property insertion in {} seconds, and it takes {}ms per insertion.", + total_time, + total_time * 10f64 + ); +} + +fn time_tikv_extend_raw_node() { + let mut graph = TikvProperty::new( + Config::new(vec![NODE_PD_SERVER_ADDR.to_owned()]), + Config::new(vec![EDGE_PD_SERVER_ADDR.to_owned()]), + false, + ) + .unwrap(); + + let mut raw_props = Vec::new(); + for i in 0..100u32 { + let key = i.to_string(); + let value = (i + 1).to_string(); + let new_prop = json!({ key: value }); + let raw_prop = to_vec(&new_prop).unwrap(); + raw_props.extend(vec![(i, raw_prop)]); + } + + let start = Instant::now(); + graph.extend_node_raw(raw_props.into_iter()).unwrap(); + let duration = start.elapsed(); + let total_time = duration.as_secs() as f64 + duration.subsec_nanos() as f64 * 1e-9; + + println!( + "Finished 100 tikv node property extension in {} seconds, and it takes {}ms per extension.", + total_time, + total_time * 10f64 + ); +} + +fn time_tikv_extend_raw_edge() { + let mut graph = TikvProperty::new( + Config::new(vec![NODE_PD_SERVER_ADDR.to_owned()]), + Config::new(vec![EDGE_PD_SERVER_ADDR.to_owned()]), + false, + ) + .unwrap(); + + let mut raw_props = Vec::new(); + for i in 0..100u32 { + let key = i.to_string(); + let value = (i + 1).to_string(); + let new_prop = json!({ key: value }); + let raw_prop = to_vec(&new_prop).unwrap(); + raw_props.extend(vec![((i, i + 1), raw_prop)]); + } + + let start = Instant::now(); + graph.extend_edge_raw(raw_props.into_iter()).unwrap(); + let duration = start.elapsed(); + let total_time = duration.as_secs() as f64 + duration.subsec_nanos() as f64 * 1e-9; + + println!( + "Finished 100 tikv edge property extension in {} seconds, and it takes {}ms per extension.", + total_time, + total_time * 10f64 + ); +} + +fn time_tikv_get_node_property_all() { + let mut graph0 = TikvProperty::new( + Config::new(vec![NODE_PD_SERVER_ADDR.to_owned()]), + Config::new(vec![EDGE_PD_SERVER_ADDR.to_owned()]), + false, + ) + .unwrap(); + + let mut raw_props = Vec::new(); + for i in 0..100u32 { + let key = i.to_string(); + let value = (i + 1).to_string(); + let new_prop = json!({ key: value }); + let raw_prop = to_vec(&new_prop).unwrap(); + raw_props.extend(vec![(i, raw_prop)]); + } + + graph0.extend_node_raw(raw_props.into_iter()).unwrap(); + + let start = Instant::now(); + graph0.get_node_property_all(0u32).unwrap(); + let duration = start.elapsed(); + let total_time = duration.as_secs() as f64 + duration.subsec_nanos() as f64 * 1e-9; + + println!( + "Finished tikv_get_node_property_all() in {}ms", + total_time * 1000f64 + ); +} + +fn time_tikv_get_edge_property_all() { + let mut graph0 = TikvProperty::new( + Config::new(vec![NODE_PD_SERVER_ADDR.to_owned()]), + Config::new(vec![EDGE_PD_SERVER_ADDR.to_owned()]), + false, + ) + .unwrap(); + + let mut raw_props = Vec::new(); + for i in 0..100u32 { + let key = i.to_string(); + let value = (i + 1).to_string(); + let new_prop = json!({ key: value }); + let raw_prop = to_vec(&new_prop).unwrap(); + raw_props.extend(vec![((i, i + 1), raw_prop)]); + } + + graph0.extend_edge_raw(raw_props.into_iter()).unwrap(); + + let start = Instant::now(); + graph0.get_edge_property_all(0u32, 1u32).unwrap(); + let duration = start.elapsed(); + let total_time = duration.as_secs() as f64 + duration.subsec_nanos() as f64 * 1e-9; + + println!( + "Finished tikv_get_edge_property_all() in {}ms", + total_time * 1000f64 + ); +} + +fn time_rocksdb_insert_raw_node() { + let node = tempdir::TempDir::new("node").unwrap(); + let edge = tempdir::TempDir::new("edge").unwrap(); + + let node_path = node.path(); + let edge_path = edge.path(); + + let mut graph = RocksProperty::new(node_path, edge_path, false).unwrap(); + + let mut raw_props = Vec::new(); + for i in 0..100u32 { + let key = i.to_string(); + let value = (i + 1).to_string(); + let new_prop = json!({ key: value }); + let raw_prop = to_vec(&new_prop).unwrap(); + raw_props.push(raw_prop); + } + + let start = Instant::now(); + for i in 0..100 { + graph.insert_node_raw(i, raw_props[i].clone()).unwrap(); + } + let duration = start.elapsed(); + let total_time = duration.as_secs() as f64 + duration.subsec_nanos() as f64 * 1e-9; + + println!( + "Finished 100 rocksdb node property insertion in {} seconds, and it takes {}ms per insertion.", + total_time, + total_time * 10f64 + ); +} + +fn time_rocksdb_insert_raw_edge() { + let node = tempdir::TempDir::new("node").unwrap(); + let edge = tempdir::TempDir::new("edge").unwrap(); + + let node_path = node.path(); + let edge_path = edge.path(); + + let mut graph = RocksProperty::new(node_path, edge_path, false).unwrap(); + + let mut raw_props = Vec::new(); + for i in 0..100u32 { + let key = i.to_string(); + let value = (i + 1).to_string(); + let new_prop = json!({ key: value }); + let raw_prop = to_vec(&new_prop).unwrap(); + raw_props.push(raw_prop); + } + + let start = Instant::now(); + for i in 0..100 { + graph + .insert_edge_raw(i, i + 1, raw_props[i].clone()) + .unwrap(); + } + let duration = start.elapsed(); + let total_time = duration.as_secs() as f64 + duration.subsec_nanos() as f64 * 1e-9; + + println!( + "Finished 100 rocksdb edge property insertion in {} seconds, and it takes {}ms per insertion.", + total_time, + total_time * 10f64 + ); +} + +fn time_rocksdb_extend_raw_node() { + let node = tempdir::TempDir::new("node").unwrap(); + let edge = tempdir::TempDir::new("edge").unwrap(); + + let node_path = node.path(); + let edge_path = edge.path(); + + let mut raw_props = Vec::new(); + for i in 0..100u32 { + let key = i.to_string(); + let value = (i + 1).to_string(); + let new_prop = json!({ key: value }); + let raw_prop = to_vec(&new_prop).unwrap(); + raw_props.extend(vec![(i, raw_prop)]); + } + let mut graph = RocksProperty::new(node_path, edge_path, false).unwrap(); + + let start = Instant::now(); + graph.extend_node_raw(raw_props.into_iter()).unwrap(); + let duration = start.elapsed(); + let total_time = duration.as_secs() as f64 + duration.subsec_nanos() as f64 * 1e-9; + + println!( + "Finished 100 rocksdb node property extension in {} seconds, and it takes {}ms per extension.", + total_time, + total_time * 10f64 + ); +} + +fn time_rocksdb_extend_raw_edge() { + let node = tempdir::TempDir::new("node").unwrap(); + let edge = tempdir::TempDir::new("edge").unwrap(); + + let node_path = node.path(); + let edge_path = edge.path(); + + let mut raw_props = Vec::new(); + for i in 0..100u32 { + let key = i.to_string(); + let value = (i + 1).to_string(); + let new_prop = json!({ key: value }); + let raw_prop = to_vec(&new_prop).unwrap(); + raw_props.extend(vec![((i, i + 1), raw_prop)]); + } + let mut graph = RocksProperty::new(node_path, edge_path, false).unwrap(); + + let start = Instant::now(); + graph.extend_edge_raw(raw_props.into_iter()).unwrap(); + let duration = start.elapsed(); + let total_time = duration.as_secs() as f64 + duration.subsec_nanos() as f64 * 1e-9; + + println!( + "Finished 100 rocksdb edge property extension in {} seconds, and it takes {}ms per extension.", + total_time, + total_time * 10f64 + ); +} + +fn time_rocksdb_get_node_property_all() { + let node = tempdir::TempDir::new("node").unwrap(); + let edge = tempdir::TempDir::new("edge").unwrap(); + + let node_path = node.path(); + let edge_path = edge.path(); + { + let mut graph0 = RocksProperty::new(node_path, edge_path, false).unwrap(); + graph0 + .insert_node_property(0u32, json!({"name": "jack"})) + .unwrap(); + } + let graph1 = RocksProperty::open(node_path, edge_path, false, true).unwrap(); + + let start = Instant::now(); + graph1.get_node_property_all(0u32).unwrap(); + let duration = start.elapsed(); + let total_time = duration.as_secs() as f64 + duration.subsec_nanos() as f64 * 1e-9; + + println!( + "Finished rocksdb_get_node_property_all() in {}ms.", + total_time * 1000f64 + ); +} + +fn time_rocksdb_get_edge_property_all() { + let node = tempdir::TempDir::new("node").unwrap(); + let edge = tempdir::TempDir::new("edge").unwrap(); + + let node_path = node.path(); + let edge_path = edge.path(); + { + let mut graph0 = RocksProperty::new(node_path, edge_path, false).unwrap(); + graph0 + .insert_edge_property(0u32, 1u32, json!({"name": "jack"})) + .unwrap(); + } + let graph1 = RocksProperty::open(node_path, edge_path, false, true).unwrap(); + let start = Instant::now(); + graph1.get_edge_property_all(0u32, 1u32).unwrap(); + let duration = start.elapsed(); + let total_time = duration.as_secs() as f64 + duration.subsec_nanos() as f64 * 1e-9; + + println!( + "Finished rocksdb_get_edge_property_all() in {}ms.", + total_time * 1000f64 + ); +} + +fn test_insert_raw_node() { + let mut graph = TikvProperty::new( + Config::new(vec![NODE_PD_SERVER_ADDR.to_owned()]), + Config::new(vec![EDGE_PD_SERVER_ADDR.to_owned()]), + false, + ) + .unwrap(); + + let new_prop = json!({"name":"jack"}); + let raw_prop = to_vec(&new_prop).unwrap(); + + graph.insert_node_raw(0u32, raw_prop).unwrap(); + let node_property = graph.get_node_property_all(0u32).unwrap(); + + assert_eq!(Some(json!({"name":"jack"})), node_property); +} + +fn test_insert_raw_edge() { + let mut graph = TikvProperty::new( + Config::new(vec![NODE_PD_SERVER_ADDR.to_owned()]), + Config::new(vec![EDGE_PD_SERVER_ADDR.to_owned()]), + false, + ) + .unwrap(); + + let new_prop = json!({"length":"15"}); + let raw_prop = to_vec(&new_prop).unwrap(); + + graph.insert_edge_raw(0u32, 1u32, raw_prop).unwrap(); + let edge_property = graph.get_edge_property_all(0u32, 1u32).unwrap(); + + assert_eq!(Some(json!({"length":"15"})), edge_property); +} + +fn test_insert_property_node() { + let mut graph = TikvProperty::new( + Config::new(vec![NODE_PD_SERVER_ADDR.to_owned()]), + Config::new(vec![EDGE_PD_SERVER_ADDR.to_owned()]), + false, + ) + .unwrap(); + + let new_prop = json!({"name":"jack"}); + + graph.insert_node_property(0u32, new_prop).unwrap(); + let node_property = graph.get_node_property_all(0u32).unwrap(); + + assert_eq!(Some(json!({"name":"jack"})), node_property); +} + +fn test_insert_property_edge() { + let mut graph = TikvProperty::new( + Config::new(vec![NODE_PD_SERVER_ADDR.to_owned()]), + Config::new(vec![EDGE_PD_SERVER_ADDR.to_owned()]), + false, + ) + .unwrap(); + + let new_prop = json!({"length":"15"}); + + graph.insert_edge_property(0u32, 1u32, new_prop).unwrap(); + let edge_property = graph.get_edge_property_all(0u32, 1u32).unwrap(); + + assert_eq!(Some(json!({"length":"15"})), edge_property); +} + +fn test_extend_raw_node() { + let mut graph = TikvProperty::new( + Config::new(vec![NODE_PD_SERVER_ADDR.to_owned()]), + Config::new(vec![EDGE_PD_SERVER_ADDR.to_owned()]), + false, + ) + .unwrap(); + + let new_prop = json!({"name":"jack"}); + let raw_prop = to_vec(&new_prop).unwrap(); + let raw_properties = vec![(0u32, raw_prop)].into_iter(); + graph.extend_node_raw(raw_properties).unwrap(); + + let node_property = graph.get_node_property_all(0u32).unwrap(); + + assert_eq!(Some(json!({"name":"jack"})), node_property); +} + +fn test_extend_raw_edge() { + let mut graph = TikvProperty::new( + Config::new(vec![NODE_PD_SERVER_ADDR.to_owned()]), + Config::new(vec![EDGE_PD_SERVER_ADDR.to_owned()]), + false, + ) + .unwrap(); + + let new_prop = json!({"length":"15"}); + let raw_prop = to_vec(&new_prop).unwrap(); + let raw_properties = vec![((0u32, 1u32), raw_prop)].into_iter(); + graph.extend_edge_raw(raw_properties).unwrap(); + let edge_property = graph.get_edge_property_all(0u32, 1u32).unwrap(); + + assert_eq!(Some(json!({"length":"15"})), edge_property); +} + +fn test_extend_property_node() { + let mut graph = TikvProperty::new( + Config::new(vec![NODE_PD_SERVER_ADDR.to_owned()]), + Config::new(vec![EDGE_PD_SERVER_ADDR.to_owned()]), + false, + ) + .unwrap(); + + let new_prop = json!({"name":"jack"}); + + let properties = vec![(0u32, new_prop)].into_iter(); + graph.extend_node_property(properties).unwrap(); + + let node_property = graph.get_node_property_all(0u32).unwrap(); + + assert_eq!(Some(json!({"name":"jack"})), node_property); +} + +fn test_extend_property_edge() { + let mut graph = TikvProperty::new( + Config::new(vec![NODE_PD_SERVER_ADDR.to_owned()]), + Config::new(vec![EDGE_PD_SERVER_ADDR.to_owned()]), + false, + ) + .unwrap(); + + let new_prop = json!({"length":"15"}); + + let properties = vec![((0u32, 1u32), new_prop)].into_iter(); + graph.extend_edge_property(properties).unwrap(); + let edge_property = graph.get_edge_property_all(0u32, 1u32).unwrap(); + + assert_eq!(Some(json!({"length":"15"})), edge_property); +} + +fn test_open_existing_db() { + { + let mut graph0 = TikvProperty::new( + Config::new(vec![NODE_PD_SERVER_ADDR.to_owned()]), + Config::new(vec![EDGE_PD_SERVER_ADDR.to_owned()]), + false, + ) + .unwrap(); + + graph0 + .insert_node_property(0u32, json!({"name": "jack"})) + .unwrap(); + + assert_eq!( + graph0.get_node_property_all(0u32).unwrap(), + Some(json!({"name": "jack"})) + ); + } + + let graph1 = TikvProperty::open( + Config::new(vec![NODE_PD_SERVER_ADDR.to_owned()]), + Config::new(vec![EDGE_PD_SERVER_ADDR.to_owned()]), + false, + true, + ) + .unwrap(); + + assert_eq!( + graph1.get_node_property_all(0u32).unwrap(), + Some(json!({"name": "jack"})) + ); +} + +fn test_open_writable_db() { + { + let mut graph0 = TikvProperty::new( + Config::new(vec![NODE_PD_SERVER_ADDR.to_owned()]), + Config::new(vec![EDGE_PD_SERVER_ADDR.to_owned()]), + false, + ) + .unwrap(); + + graph0 + .insert_node_property(0u32, json!({"name": "jack"})) + .unwrap(); + + assert_eq!( + graph0.get_node_property_all(0u32).unwrap(), + Some(json!({"name": "jack"})) + ); + } + let mut graph1 = TikvProperty::open( + Config::new(vec![NODE_PD_SERVER_ADDR.to_owned()]), + Config::new(vec![EDGE_PD_SERVER_ADDR.to_owned()]), + false, + false, + ) + .unwrap(); + graph1 + .insert_node_property(1u32, json!({"name": "tom"})) + .unwrap(); + assert_eq!( + graph1.get_node_property_all(1u32).unwrap(), + Some(json!({"name": "tom"})) + ); +} + +fn test_open_readonly_db() { + { + let mut graph0 = TikvProperty::new( + Config::new(vec![NODE_PD_SERVER_ADDR.to_owned()]), + Config::new(vec![EDGE_PD_SERVER_ADDR.to_owned()]), + false, + ) + .unwrap(); + + graph0 + .insert_node_property(0u32, json!({"name": "jack"})) + .unwrap(); + + assert_eq!( + graph0.get_node_property_all(0u32).unwrap(), + Some(json!({"name": "jack"})) + ); + } + + let mut graph1 = TikvProperty::open( + Config::new(vec![NODE_PD_SERVER_ADDR.to_owned()]), + Config::new(vec![EDGE_PD_SERVER_ADDR.to_owned()]), + false, + true, + ) + .unwrap(); + assert_eq!( + graph1.get_node_property_all(0u32).unwrap(), + Some(json!({"name": "jack"})) + ); + + let err = graph1 + .insert_node_property(1u32, json!({"name": "tom"})) + .is_err(); + assert_eq!(err, true); +} + +fn test_scan_node_property() { + let mut graph = TikvProperty::new( + Config::new(vec![NODE_PD_SERVER_ADDR.to_owned()]), + Config::new(vec![EDGE_PD_SERVER_ADDR.to_owned()]), + false, + ) + .unwrap(); + + graph + .insert_node_property(0u32, json!({"name": "jack"})) + .unwrap(); + + graph + .insert_node_property(1u32, json!({"name": "tom"})) + .unwrap(); + + let mut iter = graph.scan_node_property_all(); + assert_eq!( + (0u32, json!({"name": "jack"})), + iter.next().unwrap().unwrap() + ); + assert_eq!( + (1u32, json!({"name": "tom"})), + iter.next().unwrap().unwrap() + ); +} + +fn test_scan_edge_property() { + let mut graph = TikvProperty::new( + Config::new(vec![NODE_PD_SERVER_ADDR.to_owned()]), + Config::new(vec![EDGE_PD_SERVER_ADDR.to_owned()]), + false, + ) + .unwrap(); + + graph + .insert_edge_property(0u32, 1u32, json!({"length": "5"})) + .unwrap(); + + graph + .insert_edge_property(1u32, 2u32, json!({"length": "10"})) + .unwrap(); + + let mut iter = graph.scan_edge_property_all(); + assert_eq!( + ((0u32, 1u32), json!({"length": "5"})), + iter.next().unwrap().unwrap() + ); + assert_eq!( + ((1u32, 2u32), json!({"length": "10"})), + iter.next().unwrap().unwrap() + ); +} diff --git a/examples/vary_density.rs b/examples/vary_density.rs deleted file mode 100644 index 335125e7..00000000 --- a/examples/vary_density.rs +++ /dev/null @@ -1,110 +0,0 @@ -/* - * Copyright (c) 2018 UNSW Sydney, Data and Knowledge Group. - * - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you 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. - */ -extern crate pbr; -extern crate rand; -extern crate rust_graph; - -use std::path::Path; -use std::time::Instant; - -use pbr::ProgressBar; -use rand::{thread_rng, Rng}; - -use rust_graph::graph_impl::UnGraphMap; -use rust_graph::io::serde::{Deserialize, Serialize}; -use rust_graph::prelude::*; - -fn main() { - let args: Vec<_> = std::env::args().collect(); - - let in_graph = Path::new(&args[1]); - let out_dir = Path::new(&args[2]); - - let average_degrees: Vec = args.iter().skip(3).map(|n| n.parse().unwrap()).collect(); - - println!("average_degrees:{:?}", average_degrees); - - let start = Instant::now(); - - let mut rng = thread_rng(); - - println!("Loading {:?}", &in_graph); - let mut g = UnGraphMap::::import(in_graph).unwrap(); - - let num_of_nodes = g.node_count(); - let num_of_edges = g.edge_count(); - - println!("Average degree: {}", 2 * num_of_edges / num_of_nodes); - assert_eq!(g.max_seen_id().unwrap().id(), num_of_nodes - 1); - - for d in average_degrees { - println!("Targeting average degree {}: ", d); - - let target_num_of_edges = d * num_of_nodes / 2; - - assert!(target_num_of_edges > num_of_edges); - - let i = target_num_of_edges - num_of_edges; - let nodes = DefaultId::new(num_of_nodes); - - let mut pb = ProgressBar::new(i as u64); - - for _ in 0..i { - pb.inc(); - loop { - let s = rng.gen_range(0, nodes); - let t = rng.gen_range(0, nodes); - if s != t && !g.has_edge(s, t) { - g.add_edge(s, t, None); - break; - } - } - } - - let file_name = in_graph - .components() - .last() - .unwrap() - .as_os_str() - .to_str() - .unwrap(); - let export_filename = format!( - "{}_{}_{}_{}.graphmap", - file_name, - g.node_count(), - g.edge_count(), - d - ); - let export_path = out_dir.join(export_filename); - - pb.finish_print("done"); - - println!("Exporting to {:?}...", export_path); - - &g.export(export_path).unwrap(); - } - - let duration = start.elapsed(); - println!( - "Finished in {} seconds.", - duration.as_secs() as f64 + duration.subsec_nanos() as f64 * 1e-9 - ); -} diff --git a/examples/vary_labels.rs b/examples/vary_labels.rs deleted file mode 100644 index 4e770fff..00000000 --- a/examples/vary_labels.rs +++ /dev/null @@ -1,62 +0,0 @@ -/* - * Copyright (c) 2018 UNSW Sydney, Data and Knowledge Group. - * - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you 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. - */ -extern crate rand; -extern crate rust_graph; - -use std::path::Path; - -use rand::{thread_rng, Rng}; - -use rust_graph::graph_impl::UnStaticGraph; -use rust_graph::io::serde::{Deserialize, Serialize}; -use rust_graph::prelude::*; - -fn main() { - let args: Vec<_> = std::env::args().collect(); - - let in_graph = Path::new(&args[1]); - let out_file = Path::new(&args[2]); - - let mut rng = thread_rng(); - - let mut graph = UnStaticGraph::::import(in_graph).unwrap(); - - graph.remove_edge_labels(); - - { - let node_label_map = graph.get_node_label_map_mut(); - for i in 11..15 { - node_label_map.add_item(i); - } - } - - { - let labels = graph.get_labels_mut().as_mut().unwrap(); - for label in labels.iter_mut() { - let r = rng.gen_range(0, 15); - if r > 10 { - *label = r; - } - } - } - - graph.export(out_file).unwrap(); -} diff --git a/how-to-use-tikv.md b/how-to-use-tikv.md new file mode 100644 index 00000000..dfcca697 --- /dev/null +++ b/how-to-use-tikv.md @@ -0,0 +1,141 @@ +## 1. Deploy TiKV +You can refer to [deploy-tikv](https://github.com/tikv/tikv/blob/master/docs/how-to/deploy/using-binary.md) to easily deploy a TikV cluster on a single machine or on a cluster. + +### Example of deploying two Tikv clusters in ECNU cluster +We can deploy two Tikv clusters on ecnu00(to store node properties) and ecnu05(to store edge properties) respectively: +```shell script +## deploy one Tikv cluster on ecnu00 used as the node properties storage engine +ssh ecnu00 +cd Shares/tidb-latest-linux-amd64 +./bin/pd-server --name=pd1 --data-dir=pd1 --client-urls="http://192.168.2.2:2379" --peer-urls="http://192.168.2.2:2380" --initial-cluster="pd1=http://192.168.2.2:2380" --log-file=pd1.log & + +./bin/tikv-server --pd-endpoints="192.168.2.2:2379" --addr="192.168.2.2:20160" --data-dir=tikv-ecnu00-1 --log-file=tikv-ecnu00-1.log & + +## Check tikv-servers' status, if you start them, you should see all the tikv instances with status `Up` +./bin/pd-ctl store -d -u http://ecnu00:2379 + +## deploy one Tikv clusters on ecnu05 used as the edge properties storage engine +ssh ecnu05 +cd Shares/tidb-latest-linux-amd64 +./bin/pd-server --name=pd2 --data-dir=pd2 --client-urls="http://192.168.2.7:2379" --peer-urls="http://192.168.2.7:2380" --initial-cluster="pd2=http://192.168.2.7:2380" --log-file=pd2.log & + +./bin/tikv-server --pd-endpoints="192.168.2.7:2379" --addr="192.168.2.7:20160" --data-dir=tikv-ecnu05-1 --log-file=tikv-ecnu05-1.log & + +## Check tikv-servers' status, if you start them, you should see all the tikv instances with status `Up` +./bin/pd-ctl store -d -u http://ecnu05:2379 +``` +What's more, you can also deploy the tikv-servers on different machines, the upper example deploys tikv-servers on a single machine. + +## 2. Connect to TiKV Server +You can add the official [tikv-client](https://github.com/tikv/client-rust) dependency to your `Cargo.toml` file and use it to interact with TiKV: +```toml +[dependencies] +# ...Your other dependencies... +tikv-client = { git = "https://github.com/tikv/client-rust.git" } +``` +Note that since many interfaces in `tikv-client` are `async`, you have to use rust edition 2018 to use the keyword `async` and `await`. You should first use command `cargo fix --edition` then add `edition=2018` to your `Cargo.toml`: +```toml +[package] +# ...Your package settings... +edition="2018" +``` +Then, you can connect to `TiKV` now. In fact, we connect to pd-server(s) not tikv-server(s), where pd-server is to manage the tikv-server(s) including the meta-data management and load balance, and tikv-server is the K-V storage engine. + +Note that this project should be built in Linux or WSL, and you have to type command `rustup default nightly` to use nightly. + +Here is an example to connect to `TiKV`: + +```rust +#![feature(async_await)] +use tikv_client::{raw::Client, Config, Key, KvPair, Result, Value}; + +const KEY: &str = "TiKV"; +const VALUE: &str = "Rust"; + +async fn main() -> Result<()> { + // Create a configuration to use for the example. + let config = Config::new(vec!["192.168.2.2:2379"]); + + // When we first create a client we receive a `Connect` structure which must be resolved before + // the client is actually connected and usable. + let client = Client::new(config)?; + + // Requests are created from the connected client. These calls return structures which + // implement `Future`. This means the `Future` must be resolved before the action ever takes + // place. + // + // Here we set the key `TiKV` to have the value `Rust` associated with it. + client.put(KEY.to_owned(), VALUE.to_owned()).await.unwrap(); // Returns a `tikv_client::Error` on failure. + println!("Put key {:?}, value {:?}.", KEY, VALUE); + + // Unlike a standard Rust HashMap all calls take owned values. This is because under the hood + // protobufs must take ownership of the data. If we only took a borrow we'd need to internally + // clone it. This is against Rust API guidelines, so you must manage this yourself. + // + // Above, you saw we can use a `&'static str`, this is primarily for making examples short. + // This type is practical to use for real things, and usage forces an internal copy. + // + // It is best to pass a `Vec` in terms of explictness and speed. `String`s and a few other + // types are supported as well, but it all ends up as `Vec` in the end. + let value: Option = client.get(KEY.to_owned()).await?; + assert_eq!(value, Some(Value::from(VALUE.to_owned()))); + println!("Get key `{}` returned value {:?}.", KEY, value); +} +``` + +Here is another example using our `rust_graph_lib` to store property graph in `TikV`. The properties are in json format. +```rust +#![feature(async_await)] +extern crate rust_graph; +extern crate serde_json; +extern crate tikv_client; + +use rust_graph::property::tikv_property::*; +use rust_graph::property::PropertyGraph; +use serde_json::{json, to_vec}; +use tikv_client::Config; + +/// The pd-server that is responsible to store node properties in its managed tikv-servers +const NODE_PD_SERVER_ADDR: &str = "192.168.2.2:2379"; + +/// The pd-server that is responsible to store edge properties in its managed tikv-servers +const EDGE_PD_SERVER_ADDR: &str = "192.168.2.3:2379"; + +fn main() { + let mut graph = TikvProperty::new( + Config::new(vec![NODE_PD_SERVER_ADDR.to_owned()]), + Config::new(vec![EDGE_PD_SERVER_ADDR.to_owned()]), + false, + ) + .unwrap(); + // insert node property + let new_prop = json!({"name":"jack"}); + let raw_prop = to_vec(&new_prop).unwrap(); + + graph.insert_node_raw(0u32, raw_prop).unwrap(); + let node_property = graph.get_node_property_all(0u32).unwrap(); + + assert_eq!(Some(json!({"name":"jack"})), node_property); + + // insert edge property + let edge_prop = json!({"length":"15"}); + let raw_edge_prop = to_vec(&edge_prop).unwrap(); + + graph.insert_edge_raw(0u32, 1u32, raw_edge_prop).unwrap(); + let edge_property = graph.get_edge_property_all(0u32, 1u32).unwrap(); + + assert_eq!(Some(json!({"length":"15"})), edge_property); +} +``` + +Note that you can config some security settings in `tikv_client::Config`. The `tikv_client::Config` structure is defined as: +```rust +pub struct Config { + pub(crate) pd_endpoints: Vec, + pub(crate) ca_path: Option, + pub(crate) cert_path: Option, + pub(crate) key_path: Option, + pub(crate) timeout: Duration, +} +``` +where `pd_endpoints` is a vector of pd-servers' addresses. By default, `tikv-client` will use an insecure connection over instead of one protected by Transport Layer Security (TLS). Your deployment may have chosen to rely on security measures such as a private network, or a VPN layer to provid secure transmission. To use a TLS secured connection, use the `with_security` method to set the required parameters. TiKV does not currently offer encrypted storage (or encryption-at-rest). \ No newline at end of file diff --git a/src/algorithm/bfs.rs b/src/algorithm/bfs.rs index 39d527f6..b5a02102 100644 --- a/src/algorithm/bfs.rs +++ b/src/algorithm/bfs.rs @@ -1,143 +1,162 @@ -/* - * Copyright (c) 2018 UNSW Sydney, Data and Knowledge Group. - * - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you 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. - */ -use std::collections::VecDeque; -use std::hash::Hash; - -use fixedbitset::FixedBitSet; - -use prelude::*; - -/// A breadth first search (BFS) of a graph. -/// -/// The traversal starts at a given node and only traverses nodes reachable -/// from it. -/// -/// `Bfs` is not recursive. - -/// Example: -/// -/// ``` -/// use rust_graph::prelude::*; -/// use rust_graph::graph_impl::UnGraphMap; -/// use rust_graph::algorithm::Bfs; -/// -/// let mut graph = UnGraphMap::::new(); -/// -/// graph.add_edge(0, 1, None); -/// graph.add_edge(1, 2, None); -/// graph.add_edge(2, 3, None); -/// -/// let mut bfs =Bfs::new(&graph, Some(0)); -/// let mut i = 0; -/// -/// for n in bfs { -/// assert_eq!(n, i); -/// i = i + 1; -/// } -/// -/// ``` -/// -#[derive(Clone)] -pub struct Bfs<'a, Id: IdType, NL: Eq + Hash + 'a, EL: Eq + Hash + 'a, L: IdType = Id> { - /// The queue of nodes to visit - queue: VecDeque, - /// The set of discovered nodes - discovered: FixedBitSet, - /// The reference to the graph that algorithm is running on - graph: &'a GeneralGraph, -} - -impl<'a, Id: IdType, NL: Eq + Hash + 'a, EL: Eq + Hash + 'a, L: IdType> Bfs<'a, Id, NL, EL, L> { - /// Create a new **Bfs** by initialising empty discovered set, and put **start** - /// in the queue of nodes to visit. - pub fn new>(graph: &'a G, start: Option) -> Self { - let mut discovered: FixedBitSet = - FixedBitSet::with_capacity(graph.max_seen_id().unwrap().id() + 1); - let mut queue: VecDeque = VecDeque::new(); - - discovered.insert_range(..); - - if let Some(start) = start { - if !graph.has_node(start) { - panic!("Starting node doesn't exist on graph") - } else { - queue.push_back(start); - discovered.set(start.id(), false); - } - } else { - if graph.node_count() == 0 { - panic!("Graph is empty") - } else { - let id = graph.node_indices().next().unwrap(); - queue.push_back(id); - discovered.set(id.id(), false); - } - } - - Bfs { - queue, - discovered, - graph, - } - } - - /// Return the next node in the bfs, or **None** if the traversal is done. - pub fn next(&mut self) -> Option { - if self.queue.len() == 0 { - if let Some(id) = self.next_unvisited_node() { - self.queue.push_back(id); - self.discovered.set(id.id(), false); - } - } - - if let Some(current_node) = self.queue.pop_front() { - for neighbour in self.graph.neighbors_iter(current_node) { - if self.discovered.contains(neighbour.id()) { - self.discovered.set(neighbour.id(), false); - self.queue.push_back(neighbour); - } - } - Some(current_node) - } else { - None - } - } - - /// Randomly pick a unvisited node from the set. - fn next_unvisited_node(&self) -> Option { - for node in self.discovered.ones() { - if self.graph.has_node(Id::new(node)) { - return Some(Id::new(node)); - } - } - None - } -} - -impl<'a, Id: IdType, NL: Eq + Hash + 'a, EL: Eq + Hash + 'a, L: IdType> Iterator - for Bfs<'a, Id, NL, EL, L> -{ - type Item = Id; - - fn next(&mut self) -> Option { - self.next() - } -} +/* + * Copyright (c) 2018 UNSW Sydney, Data and Knowledge Group. + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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. + */ +use std::collections::VecDeque; +use std::hash::Hash; +use std::marker::PhantomData; + +use fixedbitset::FixedBitSet; + +use crate::prelude::*; + +/// A breadth first search (BFS) of a graph. +/// +/// The traversal starts at a given node and only traverses nodes reachable +/// from it. +/// +/// `Bfs` is not recursive. + +/// Example: +/// +/// ``` +/// use rust_graph::prelude::*; +/// use rust_graph::graph_impl::UnGraphMap; +/// use rust_graph::algorithm::Bfs; +/// +/// let mut graph = UnGraphMap::::new(); +/// +/// graph.add_edge(0, 1, None); +/// graph.add_edge(1, 2, None); +/// graph.add_edge(2, 3, None); +/// +/// let mut bfs =Bfs::new(&graph, Some(0)); +/// let mut i = 0; +/// +/// for n in bfs { +/// assert_eq!(n, i); +/// i = i + 1; +/// } +/// +/// ``` +/// +#[derive(Clone)] +pub struct Bfs< + 'a, + Id: IdType, + NL: Eq + Hash + 'a, + EL: Eq + Hash + 'a, + L: IdType, + G: GeneralGraph + ?Sized, +> { + /// The queue of nodes to visit + queue: VecDeque, + /// The set of discovered nodes + discovered: FixedBitSet, + /// The reference to the graph that algorithm is running on + graph: &'a G, + + _ph: PhantomData<(NL, EL, L)>, +} + +impl< + 'a, + Id: IdType, + NL: Eq + Hash + 'a, + EL: Eq + Hash + 'a, + L: IdType, + G: GeneralGraph + ?Sized, + > Bfs<'a, Id, NL, EL, L, G> +{ + /// Create a new **Bfs** by initialising empty discovered set, and put **start** + /// in the queue of nodes to visit. + pub fn new(graph: &'a G, start: Option) -> Self { + let mut discovered: FixedBitSet = + FixedBitSet::with_capacity(graph.max_seen_id().unwrap().id() + 1); + let mut queue: VecDeque = VecDeque::new(); + + discovered.insert_range(..); + + if let Some(start) = start { + if !graph.has_node(start) { + panic!("Starting node doesn't exist on graph") + } else { + queue.push_back(start); + discovered.set(start.id(), false); + } + } else if graph.node_count() == 0 { + panic!("Graph is empty") + } else { + let id = graph.node_indices().next().unwrap(); + queue.push_back(id); + discovered.set(id.id(), false); + } + + Bfs { + queue, + discovered, + graph, + _ph: PhantomData, + } + } + + /// Randomly pick a unvisited node from the set. + fn next_unvisited_node(&self) -> Option { + for node in self.discovered.ones() { + if self.graph.has_node(Id::new(node)) { + return Some(Id::new(node)); + } + } + None + } +} + +impl< + 'a, + Id: IdType, + NL: Eq + Hash + 'a, + EL: Eq + Hash + 'a, + L: IdType, + G: GeneralGraph + ?Sized, + > Iterator for Bfs<'a, Id, NL, EL, L, G> +{ + type Item = Id; + + /// Return the next node in the bfs, or **None** if the traversal is done. + fn next(&mut self) -> Option { + if self.queue.is_empty() { + if let Some(id) = self.next_unvisited_node() { + self.queue.push_back(id); + self.discovered.set(id.id(), false); + } + } + + if let Some(current_node) = self.queue.pop_front() { + for neighbour in self.graph.neighbors_iter(current_node) { + if self.discovered.contains(neighbour.id()) { + self.discovered.set(neighbour.id(), false); + self.queue.push_back(neighbour); + } + } + Some(current_node) + } else { + None + } + } +} diff --git a/src/algorithm/conn_comp.rs b/src/algorithm/conn_comp.rs index e3347507..847b3e87 100644 --- a/src/algorithm/conn_comp.rs +++ b/src/algorithm/conn_comp.rs @@ -1,202 +1,202 @@ -/* - * Copyright (c) 2018 UNSW Sydney, Data and Knowledge Group. - * - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you 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. - */ -use std::cell::RefMut; -use std::cell::{Ref, RefCell}; -use std::collections::HashMap; -use std::hash::Hash; - -use prelude::*; - -/// Detection of Connected Component (ConnComp) of a graph. -/// -/// `ConnComp` is not recursive. -/// The detection iterates through every edge. -/// Nodes that are involved in each edge is merged together. -/// -/// There are k loops and each loop processing the root array costs log(n). -/// Therefore, time complexity is O(k*log(n)). -/// -/// `ConnComp` does not itself borrow the graph, and because of this you can run -/// a detection over a graph while still retaining mutable access to it -/// Example: -/// -/// ``` -/// use rust_graph::prelude::*; -/// use rust_graph::graph_impl::UnGraphMap; -/// use rust_graph::algorithm::ConnComp; -/// -/// let mut graph = UnGraphMap::::new(); -/// -/// graph.add_edge(0, 1, None); -/// graph.add_edge(1, 2, None); -/// graph.add_edge(3, 4, None); -/// -/// let mut cc = ConnComp::new(&graph); -/// cc.get_connected_nodes(0); -/// cc.is_connected(0, 1); -/// -/// ``` -/// -#[derive(Debug, Clone)] -pub struct ConnComp { - /// The map of each node to its root - parent_ref: RefCell>, - /// The number of connected components found - count: usize, -} - -impl ConnComp { - /// Create a new **ConnComp** by initialising empty root map, and set count to be number - /// of nodes in graph. - pub fn new( - graph: &GeneralGraph, - ) -> Self { - let mut cc = ConnComp::with_capacity(graph.node_count()); - cc.run_detection(graph); - cc - } - - /// Create a empty **ConnComp**. - pub fn empty() -> Self { - Self::with_capacity(0) - } - - /// Create a new **ConnComp**. - pub fn with_capacity(node_count: usize) -> Self { - ConnComp { - parent_ref: RefCell::new(HashMap::with_capacity(node_count)), - count: 0, - } - } - - /// Get immutable reference of parent map - pub fn parent(&self) -> Ref> { - self.parent_ref.borrow() - } - - /// Run the detection upon every edge. Update the root map based on every edge - pub fn run_detection( - &mut self, - graph: &GeneralGraph, - ) { - for edge in graph.edges() { - self.process_new_edge(&edge); - } - } - - /// Update the root map based on a newly given edge - /// Can be called at anytime after instantiating a ConnComp instance - pub fn process_new_edge(&mut self, edge: &EdgeTrait) { - let x = edge.get_start(); - let y = edge.get_target(); - - if !self.parent().contains_key(&x) { - self.mut_parent().insert(x, x); - self.count += 1; - } - - if !self.parent().contains_key(&y) { - self.mut_parent().insert(y, y); - self.count += 1; - } - - let x_root = self.get_root(x).unwrap(); - let y_root = self.get_root(y).unwrap(); - - if x_root != y_root { - self.count -= 1; - self.mut_parent().insert(x_root, y_root); - } - } - - /// Get the parent of a node. - pub fn get_parent(&self, node: Id) -> Option { - if let Some(id) = self.parent().get(&node) { - Some(*id) - } else { - None - } - } - - /// Get the root of a node. - pub fn get_root(&self, mut node: Id) -> Option { - while self.parent().get(&node) != Some(&node) { - let p = self.parent()[&node]; - let pp = self.parent()[&p]; - - self.mut_parent().insert(node, pp); - node = pp; - } - - if self.parent().get(&node) != None { - Some(node) - } else { - None - } - } - - /// Check if two nodes are belong to the same component. - pub fn is_connected(&self, node0: Id, node1: Id) -> bool { - if !self.parent().contains_key(&node0) || !self.parent().contains_key(&node1) { - false - } else { - self.get_root(node0) == self.get_root(node1) - } - } - - /// Clear the state. - pub fn reset(&mut self) { - self.mut_parent().clear(); - self.count = 0; - } - - /// Get the number of components. - pub fn get_count(&self) -> usize { - return self.count; - } - - /// Get all nodes in the component of the given node. - pub fn get_connected_nodes(&self, node: Id) -> Option> { - if self.parent().contains_key(&node) { - let mut result: Vec = Vec::new(); - let root_id = self.get_root(node); - let mut keys: Vec = Vec::new(); - - for key in self.parent().keys() { - keys.push(*key); - } - - for n in keys { - if self.get_root(n) == root_id { - result.push(n); - } - } - Some(result) - } else { - None - } - } - - /// Get mutable reference of parent map - fn mut_parent(&self) -> RefMut> { - self.parent_ref.borrow_mut() - } -} +/* + * Copyright (c) 2018 UNSW Sydney, Data and Knowledge Group. + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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. + */ +use hashbrown::HashMap; +use std::cell::RefMut; +use std::cell::{Ref, RefCell}; +use std::hash::Hash; + +use crate::prelude::*; + +/// Detection of Connected Component (ConnComp) of a graph. +/// +/// `ConnComp` is not recursive. +/// The detection iterates through every edge. +/// Nodes that are involved in each edge is merged together. +/// +/// There are k loops and each loop processing the root array costs log(n). +/// Therefore, time complexity is O(k*log(n)). +/// +/// `ConnComp` does not itself borrow the graph, and because of this you can run +/// a detection over a graph while still retaining mutable access to it +/// Example: +/// +/// ``` +/// use rust_graph::prelude::*; +/// use rust_graph::graph_impl::UnGraphMap; +/// use rust_graph::algorithm::ConnComp; +/// +/// let mut graph = UnGraphMap::::new(); +/// +/// graph.add_edge(0, 1, None); +/// graph.add_edge(1, 2, None); +/// graph.add_edge(3, 4, None); +/// +/// let mut cc = ConnComp::new(&graph); +/// cc.get_connected_nodes(0); +/// cc.is_connected(0, 1); +/// +/// ``` +/// +#[derive(Debug, Clone)] +pub struct ConnComp { + /// The map of each node to its root + parent_ref: RefCell>, + /// The number of connected components found + count: usize, +} + +impl ConnComp { + /// Create a new **ConnComp** by initialising empty root map, and set count to be number + /// of nodes in graph. + pub fn new( + graph: &dyn GeneralGraph, + ) -> Self { + let mut cc = ConnComp::with_capacity(graph.node_count()); + cc.run_detection(graph); + cc + } + + /// Create a empty **ConnComp**. + pub fn empty() -> Self { + Self::with_capacity(0) + } + + /// Create a new **ConnComp**. + pub fn with_capacity(node_count: usize) -> Self { + ConnComp { + parent_ref: RefCell::new(HashMap::with_capacity(node_count)), + count: 0, + } + } + + /// Get immutable reference of parent map + pub fn parent(&self) -> Ref> { + self.parent_ref.borrow() + } + + /// Run the detection upon every edge. Update the root map based on every edge + pub fn run_detection( + &mut self, + graph: &dyn GeneralGraph, + ) { + for edge in graph.edges() { + self.process_new_edge(&edge); + } + } + + /// Update the root map based on a newly given edge + /// Can be called at anytime after instantiating a ConnComp instance + pub fn process_new_edge(&mut self, edge: &dyn EdgeTrait) { + let x = edge.get_start(); + let y = edge.get_target(); + + if !self.parent().contains_key(&x) { + self.mut_parent().insert(x, x); + self.count += 1; + } + + if !self.parent().contains_key(&y) { + self.mut_parent().insert(y, y); + self.count += 1; + } + + let x_root = self.get_root(x).unwrap(); + let y_root = self.get_root(y).unwrap(); + + if x_root != y_root { + self.count -= 1; + self.mut_parent().insert(x_root, y_root); + } + } + + /// Get the parent of a node. + pub fn get_parent(&self, node: Id) -> Option { + if let Some(id) = self.parent().get(&node) { + Some(*id) + } else { + None + } + } + + /// Get the root of a node. + pub fn get_root(&self, mut node: Id) -> Option { + while self.parent().get(&node) != Some(&node) { + let p = self.parent()[&node]; + let pp = self.parent()[&p]; + + self.mut_parent().insert(node, pp); + node = pp; + } + + if self.parent().get(&node) != None { + Some(node) + } else { + None + } + } + + /// Check if two nodes are belong to the same component. + pub fn is_connected(&self, node0: Id, node1: Id) -> bool { + if !self.parent().contains_key(&node0) || !self.parent().contains_key(&node1) { + false + } else { + self.get_root(node0) == self.get_root(node1) + } + } + + /// Clear the state. + pub fn reset(&mut self) { + self.mut_parent().clear(); + self.count = 0; + } + + /// Get the number of components. + pub fn get_count(&self) -> usize { + self.count + } + + /// Get all nodes in the component of the given node. + pub fn get_connected_nodes(&self, node: Id) -> Option> { + if self.parent().contains_key(&node) { + let mut result: Vec = Vec::new(); + let root_id = self.get_root(node); + let mut keys: Vec = Vec::new(); + + for key in self.parent().keys() { + keys.push(*key); + } + + for n in keys { + if self.get_root(n) == root_id { + result.push(n); + } + } + Some(result) + } else { + None + } + } + + /// Get mutable reference of parent map + fn mut_parent(&self) -> RefMut> { + self.parent_ref.borrow_mut() + } +} diff --git a/src/algorithm/conn_subgraphs.rs b/src/algorithm/conn_subgraphs.rs index c8dea816..b68abb71 100644 --- a/src/algorithm/conn_subgraphs.rs +++ b/src/algorithm/conn_subgraphs.rs @@ -1,170 +1,122 @@ -/* - * Copyright (c) 2018 UNSW Sydney, Data and Knowledge Group. - * - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you 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. - */ -use std::hash::Hash; - -use algorithm::conn_comp::ConnComp; -use graph_impl::graph_map::new_general_graphmap; -use prelude::*; - -/// Enumeration of Connected subgraphs of a graph. -/// -/// `ConnSubgraph` is not recursive. -/// The algorithm first gets all connected components using ConnComp algorithm. -/// Then generates a vector of subgraphs according to nodes and edges -/// corresponding to each component. -/// -/// Example: -/// -/// ``` -/// use rust_graph::algorithm::ConnSubgraph; -/// use rust_graph::prelude::*; -/// use rust_graph::graph_impl::UnGraphMap; -/// -/// let mut graph = UnGraphMap::::new(); -/// graph.add_node(1, Some(0)); -/// graph.add_node(2, Some(1)); -/// graph.add_node(3, Some(2)); -/// graph.add_node(4, Some(3)); -/// -/// graph.add_edge(1, 2, Some(10)); -/// graph.add_edge(3, 4, Some(20)); -/// -/// let cs = ConnSubgraph::new(&graph); -/// let subgraphs = cs.into_result(); -/// ``` -/// -pub struct ConnSubgraph< - 'a, - Id: IdType, - NL: Eq + Hash + Clone + 'a, - EL: Eq + Hash + Clone + 'a, - L: IdType, -> { - /// The result vector of subgraphs - subgraphs: Vec + 'a>>, - /// The vector of roots. e.g roots[subgraph_index] = subgraph_root_id. - roots: Vec, - /// The Connected Components of given graph - cc: ConnComp, -} - -impl<'a, Id: IdType, NL: Eq + Hash + Clone + 'a, EL: Eq + Hash + Clone + 'a, L: IdType> - ConnSubgraph<'a, Id, NL, EL, L> -{ - /// Create a new **ConnSubgraph** by initialising empty result subgraph vector, and create a ConnComp - /// instance with given graph. Then run the enumeration. - pub fn new(graph: &GeneralGraph) -> Self { - let mut cs = ConnSubgraph::empty(graph); - - cs.run_subgraph_enumeration(graph); - cs - } - - /// Create a new **ConnSubgraph** by initialising empty result subgraph vector, and create a ConnComp - /// instance with given graph. - pub fn empty(graph: &GeneralGraph) -> Self { - ConnSubgraph { - subgraphs: Vec::new(), - roots: Vec::new(), - cc: ConnComp::new(graph), - } - } - - /// Run the graph enumeration by adding each node and edge to the subgraph that it - /// corresponds to. - pub fn run_subgraph_enumeration(&mut self, graph: &GeneralGraph) { - self.process_nodes(graph); - self.process_edges(graph); - } - - /// Get the subgraph from a given root node id. - pub fn root_to_subgraph(&self, root: Id) -> Option { - for index in 0..self.roots.len() { - if self.roots[index] == root { - return Some(index); - } - } - None - } - - /// Return the result vector of subgraphs. - pub fn into_result(self) -> Vec + 'a>> { - self.subgraphs - } - - /// Add nodes to their corresponding subgraphs - fn process_nodes(&mut self, graph: &GeneralGraph) { - for id in graph.node_indices() { - let root = self.cc.get_root(id).unwrap(); - let label = graph.get_node_label(id).cloned(); - let index = self.root_to_subgraph(root); - - if let Some(index) = index { - self.subgraphs[index] - .as_mut_graph() - .unwrap() - .add_node(id, label); - } else { - if graph.is_directed() { - self.subgraphs.push(new_general_graphmap(true)); - } else { - self.subgraphs.push(new_general_graphmap(false)); - } - self.roots.push(root); - let length = self.subgraphs.len(); - self.subgraphs[length - 1] - .as_mut_graph() - .unwrap() - .add_node(id, label); - } - } - } - - /// Add edges to their corresponding subgraphs - fn process_edges(&mut self, graph: &GeneralGraph) { - for edge in graph.edges() { - let start = edge.get_start(); - let target = edge.get_target(); - let root = self.cc.get_root(start).unwrap(); - let label = graph.get_edge_label(start, target).cloned(); - let index = self.root_to_subgraph(root); - - if let Some(index) = index { - self.subgraphs[index] - .as_mut_graph() - .unwrap() - .add_edge(start, target, label); - } else { - if graph.is_directed() { - self.subgraphs.push(new_general_graphmap(true)); - } else { - self.subgraphs.push(new_general_graphmap(false)); - } - self.roots.push(root); - let length = self.subgraphs.len(); - self.subgraphs[length - 1] - .as_mut_graph() - .unwrap() - .add_edge(start, target, label); - } - } - } -} +/* + * Copyright (c) 2018 UNSW Sydney, Data and Knowledge Group. + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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. + */ +use itertools::Itertools; +use std::hash::Hash; + +use crate::algorithm::conn_comp::ConnComp; +use crate::graph_impl::graph_map::new_general_graphmap; +use crate::prelude::*; + +/// Enumeration of Connected subgraphs of a graph. +/// +/// `ConnSubgraph` is not recursive. +/// The algorithm first gets all the possible combination of edges which can form subgraphs. +/// Then generates a vector of subgraphs according to nodes and edges +/// corresponding to each component. +/// +/// Example: +/// +/// ``` +/// use rust_graph::algorithm::ConnSubgraph; +/// use rust_graph::prelude::*; +/// use rust_graph::graph_impl::UnGraphMap; +/// +/// let mut graph = UnGraphMap::::new(); +/// graph.add_node(1, Some(0)); +/// graph.add_node(2, Some(1)); +/// graph.add_node(3, Some(2)); +/// graph.add_node(4, Some(3)); +/// +/// graph.add_edge(1, 2, Some(10)); +/// graph.add_edge(3, 4, Some(20)); +/// +/// let cs = ConnSubgraph::new(&graph); +/// let subgraphs = cs.into_result(); +/// ``` +/// +pub struct ConnSubgraph< + 'a, + Id: IdType, + NL: Eq + Hash + Clone + 'a, + EL: Eq + Hash + Clone + 'a, + L: IdType, +> { + /// The result vector of subgraphs + subgraphs: Vec + 'a>>, +} + +impl<'a, Id: IdType, NL: Eq + Hash + Clone + 'a, EL: Eq + Hash + Clone + 'a, L: IdType> + ConnSubgraph<'a, Id, NL, EL, L> +{ + /// Create a new **ConnSubgraph** by initialising empty result subgraph vector, and create a ConnComp + /// instance with given graph. Then run the enumeration. + pub fn new(graph: &dyn GeneralGraph) -> Self { + let mut cs = ConnSubgraph::empty(); + + cs.run_subgraph_enumeration(graph); + cs + } + + /// Create a new **ConnSubgraph** by initialising empty result subgraph vector, and create a ConnComp + /// instance with given graph. + pub fn empty() -> Self { + ConnSubgraph { + subgraphs: Vec::new(), + } + } + + /// Run the graph enumeration by adding each node and edge to the subgraph that it + /// corresponds to. + pub fn run_subgraph_enumeration(&mut self, graph: &dyn GeneralGraph) { + if graph.edge_count() != 0 { + let mut num_edges: usize = 1; + while num_edges <= graph.edge_count() { + for edge_vec in graph.edges().combinations(num_edges) { + let mut g_tmp = new_general_graphmap(graph.is_directed()); + for edge in edge_vec { + let mut_g = g_tmp.as_mut_graph().unwrap(); + let (start, target) = (edge.get_start(), edge.get_target()); + + let node_label_one = graph.get_node_label(start); + let node_label_two = graph.get_node_label(target); + + mut_g.add_node(start, node_label_one.cloned()); + mut_g.add_node(target, node_label_two.cloned()); + + let edge_label = graph.get_edge_label(start, target); + + mut_g.add_edge(start, target, edge_label.cloned()); + } + + if g_tmp.node_count() > 0 && ConnComp::new(g_tmp.as_ref()).get_count() == 1 { + self.subgraphs.push(g_tmp); + } + } + num_edges += 1; + } + } + } + + /// Return the result vector of subgraphs. + pub fn into_result(self) -> Vec + 'a>> { + self.subgraphs + } +} diff --git a/src/algorithm/dfs.rs b/src/algorithm/dfs.rs index ef3e3424..c50f3416 100644 --- a/src/algorithm/dfs.rs +++ b/src/algorithm/dfs.rs @@ -1,172 +1,188 @@ -/* - * Copyright (c) 2018 UNSW Sydney, Data and Knowledge Group. - * - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you 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. - */ -use std::hash::Hash; - -use fixedbitset::FixedBitSet; - -use prelude::*; - -/// A depth first search (Dfs) of a graph. -/// -/// The traversal starts at a given node and only traverses nodes reachable -/// from it. -/// -/// `Dfs` is not recursive. -/// -/// Example: -/// -/// ``` -/// use rust_graph::prelude::*; -/// use rust_graph::graph_impl::UnGraphMap; -/// use rust_graph::algorithm::Dfs; -/// -/// let mut graph = UnGraphMap::::new(); -/// -/// graph.add_edge(0, 1, None); -/// graph.add_edge(1, 2, None); -/// graph.add_edge(2, 3, None); -/// -/// let mut dfs = Dfs::new(&graph, Some(0)); -/// let mut i = 0; -/// -/// for n in dfs { -/// assert_eq!(n, i); -/// i = i + 1; -/// } -/// -/// ``` -/// -#[derive(Clone)] -pub struct Dfs<'a, Id: IdType, NL: Eq + Hash + 'a, EL: Eq + Hash + 'a, L: IdType = Id> { - /// The stack of nodes to visit - stack: Vec, - /// The map of discovered nodes - discovered: FixedBitSet, - /// The reference to the graph that algorithm is running on - graph: &'a GeneralGraph, -} - -impl<'a, Id: IdType, NL: Eq + Hash + 'a, EL: Eq + Hash + 'a, L: IdType> Dfs<'a, Id, NL, EL, L> { - /// Create a new **Dfs** by initialising empty prev_discovered map, and put **start** - /// in the queue of nodes to visit. - pub fn new>(graph: &'a G, start: Option) -> Self { - let mut dfs = Dfs::with_capacity(graph); - dfs.move_to(start); - dfs - } - - /// Create a `Dfs` from a vector and a map - pub fn from_parts>( - stack: Vec, - discovered: FixedBitSet, - graph: &'a G, - ) -> Self { - Dfs { - stack, - discovered, - graph, - } - } - - /// Create a new **Dfs**. - pub fn with_capacity>(graph: &'a G) -> Self { - let mut discovered: FixedBitSet = - FixedBitSet::with_capacity(graph.max_seen_id().unwrap().id() + 1); - discovered.insert_range(..); - - Dfs { - stack: Vec::new(), - discovered, - graph, - } - } - - /// Clear the visit state - pub fn reset(&mut self) { - self.discovered.clear(); - self.stack.clear(); - self.discovered.insert_range(..); - } - - /// Return the next node in the Dfs, or **None** if the traversal is done. - pub fn next(&mut self) -> Option { - if self.stack.len() == 0 { - if let Some(id) = self.next_unvisited_node() { - self.stack.push(id); - self.discovered.set(id.id(), false); - } - } - - if let Some(current_node) = self.stack.pop() { - for neighbour in self.graph.neighbors_iter(current_node) { - if self.discovered.contains(neighbour.id()) { - self.discovered.set(neighbour.id(), false); - self.stack.push(neighbour); - } - } - Some(current_node) - } else { - None - } - } - - /// Randomly pick a unvisited node from the map. - fn next_unvisited_node(&self) -> Option { - for node in self.discovered.ones() { - if self.graph.has_node(Id::new(node)) { - return Some(Id::new(node)); - } - } - None - } - - /// Clear the stack and restart the dfs from a particular node. - fn move_to(&mut self, start: Option) { - if let Some(start) = start { - if !self.graph.has_node(start) { - panic!("Node {:?} is not in the graph.", start); - } else { - self.discovered.set(start.id(), false); - self.stack.clear(); - self.stack.push(start); - } - } else { - if self.graph.node_count() == 0 { - panic!("Graph is empty") - } else { - let id = self.graph.node_indices().next().unwrap(); - self.discovered.set(id.id(), false); - self.stack.clear(); - self.stack.push(id); - } - } - } -} - -impl<'a, Id: IdType, NL: Eq + Hash + 'a, EL: Eq + Hash + 'a, L: IdType> Iterator - for Dfs<'a, Id, NL, EL, L> -{ - type Item = Id; - - fn next(&mut self) -> Option { - self.next() - } -} +/* + * Copyright (c) 2018 UNSW Sydney, Data and Knowledge Group. + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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. + */ +use std::hash::Hash; +use std::marker::PhantomData; + +use fixedbitset::FixedBitSet; + +use crate::prelude::*; + +/// A depth first search (Dfs) of a graph. +/// +/// The traversal starts at a given node and only traverses nodes reachable +/// from it. +/// +/// `Dfs` is not recursive. +/// +/// Example: +/// +/// ``` +/// use rust_graph::prelude::*; +/// use rust_graph::graph_impl::UnGraphMap; +/// use rust_graph::algorithm::Dfs; +/// +/// let mut graph = UnGraphMap::::new(); +/// +/// graph.add_edge(0, 1, None); +/// graph.add_edge(1, 2, None); +/// graph.add_edge(2, 3, None); +/// +/// let mut dfs = Dfs::new(&graph, Some(0)); +/// let mut i = 0; +/// +/// for n in dfs { +/// assert_eq!(n, i); +/// i = i + 1; +/// } +/// +/// ``` +/// +#[derive(Clone)] +pub struct Dfs< + 'a, + Id: IdType, + NL: Eq + Hash + 'a, + EL: Eq + Hash + 'a, + L: IdType, + G: GeneralGraph + ?Sized, +> { + /// The stack of nodes to visit + stack: Vec, + /// The map of discovered nodes + discovered: FixedBitSet, + /// The reference to the graph that algorithm is running on + graph: &'a G, + + _ph: PhantomData<(NL, EL, L)>, +} + +impl< + 'a, + Id: IdType, + NL: Eq + Hash + 'a, + EL: Eq + Hash + 'a, + L: IdType, + G: GeneralGraph + ?Sized, + > Dfs<'a, Id, NL, EL, L, G> +{ + /// Create a new **Dfs** by initialising empty prev_discovered map, and put **start** + /// in the queue of nodes to visit. + pub fn new(graph: &'a G, start: Option) -> Self { + let mut dfs = Dfs::with_capacity(graph); + dfs.move_to(start); + dfs + } + + /// Create a `Dfs` from a vector and a map + pub fn from_parts(stack: Vec, discovered: FixedBitSet, graph: &'a G) -> Self { + Dfs { + stack, + discovered, + graph, + _ph: PhantomData, + } + } + + /// Create a new **Dfs**. + pub fn with_capacity(graph: &'a G) -> Self { + let mut discovered: FixedBitSet = + FixedBitSet::with_capacity(graph.max_seen_id().unwrap().id() + 1); + discovered.insert_range(..); + + Dfs { + stack: Vec::new(), + discovered, + graph, + _ph: PhantomData, + } + } + + /// Clear the visit state + pub fn reset(&mut self) { + self.discovered.clear(); + self.stack.clear(); + self.discovered.insert_range(..); + } + + /// Randomly pick a unvisited node from the map. + fn next_unvisited_node(&self) -> Option { + for node in self.discovered.ones() { + if self.graph.has_node(Id::new(node)) { + return Some(Id::new(node)); + } + } + None + } + + /// Clear the stack and restart the dfs from a particular node. + fn move_to(&mut self, start: Option) { + if let Some(start) = start { + if !self.graph.has_node(start) { + panic!("Node {:?} is not in the graph.", start); + } else { + self.discovered.set(start.id(), false); + self.stack.clear(); + self.stack.push(start); + } + } else if self.graph.node_count() == 0 { + panic!("Graph is empty") + } else { + let id = self.graph.node_indices().next().unwrap(); + self.discovered.set(id.id(), false); + self.stack.clear(); + self.stack.push(id); + } + } +} + +impl< + 'a, + Id: IdType, + NL: Eq + Hash + 'a, + EL: Eq + Hash + 'a, + L: IdType, + G: GeneralGraph + ?Sized, + > Iterator for Dfs<'a, Id, NL, EL, L, G> +{ + type Item = Id; + + /// Return the next node in the Dfs, or **None** if the traversal is done. + fn next(&mut self) -> Option { + if self.stack.is_empty() { + if let Some(id) = self.next_unvisited_node() { + self.stack.push(id); + self.discovered.set(id.id(), false); + } + } + + if let Some(current_node) = self.stack.pop() { + for neighbour in self.graph.neighbors_iter(current_node) { + if self.discovered.contains(neighbour.id()) { + self.discovered.set(neighbour.id(), false); + self.stack.push(neighbour); + } + } + Some(current_node) + } else { + None + } + } +} diff --git a/src/algorithm/graph_induce.rs b/src/algorithm/graph_induce.rs new file mode 100644 index 00000000..0412e0ed --- /dev/null +++ b/src/algorithm/graph_induce.rs @@ -0,0 +1,105 @@ +/* + * Copyright (c) 2018 UNSW Sydney, Data and Knowledge Group. + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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. + */ +use std::hash::Hash; + +use crate::generic::dtype::IdType; +use crate::graph_impl::graph_map::new_general_graphmap; +use crate::prelude::*; + +macro_rules! induce { + ($graph0:ident,$graph1:ident,$graph:ident) => { + for id in $graph1.node_indices() { + let mut_graph = $graph.as_mut_graph().unwrap(); + mut_graph.add_node(id, $graph1.get_node_label(id).cloned()); + } + + for (src, dst) in $graph1.edge_indices() { + let mut_graph = $graph.as_mut_graph().unwrap(); + mut_graph.add_edge(src, dst, $graph1.get_edge_label(src, dst).cloned()); + } + + for (src, dst) in $graph0.edge_indices() { + if $graph.has_edge(src, dst) { + continue; + } + if $graph.has_node(src) + && $graph.has_node(dst) + && $graph.get_node_label(src) == $graph0.get_node_label(src) + && $graph.get_node_label(dst) == $graph0.get_node_label(dst) + { + let mut_graph = $graph.as_mut_graph().unwrap(); + mut_graph.add_edge(src, dst, $graph0.get_edge_label(src, dst).cloned()); + } + } + }; +} + +/// Graph Induce of two graphs, g0 and g1. g0 contains edges that g1 may not contain. +/// +/// Firstly, nodes and edges from g1 are added into result graph. +/// The edges from g0 are added into result graph if they have both of their ends in the result graph. +/// +/// Example: +/// +/// ``` +/// use rust_graph::algorithm::graph_induce; +/// use rust_graph::prelude::*; +/// use rust_graph::graph_impl::UnGraphMap; +/// +/// let mut graph0 = UnGraphMap::::new(); +/// graph0.add_node(1, Some(1)); +/// graph0.add_node(2, Some(2)); +/// graph0.add_node(3, Some(3)); +/// graph0.add_node(4, Some(4)); +/// graph0.add_edge(1, 2, Some(12)); +/// graph0.add_edge(2, 3, Some(23)); +/// graph0.add_edge(3, 4, Some(34)); +/// graph0.add_edge(1, 4, Some(14)); +/// graph0.add_edge(1, 3, Some(13)); +/// +/// +/// let mut graph1 = UnGraphMap::::new(); +/// graph1.add_node(1, Some(1)); +/// graph1.add_node(2, Some(2)); +/// graph1.add_node(3, Some(3)); +/// graph1.add_edge(1, 2, Some(12)); +/// graph1.add_edge(2, 3, Some(23)); +/// +/// let result_graph = graph_induce(&graph0, &graph1); +/// +/// ``` +/// +pub fn graph_induce< + 'a, + 'b, + 'c, + Id: IdType + 'c, + NL: Eq + Hash + Clone + 'c, + EL: Eq + Hash + Clone + 'c, + L: IdType + 'c, +>( + graph0: &'a dyn GeneralGraph, + graph1: &'b dyn GeneralGraph, +) -> Box + 'c> { + let mut result_graph = new_general_graphmap(graph0.is_directed()); + induce!(graph0, graph1, result_graph); + result_graph +} diff --git a/src/algorithm/graph_minus.rs b/src/algorithm/graph_minus.rs index cfd3a0c8..835cb0e9 100644 --- a/src/algorithm/graph_minus.rs +++ b/src/algorithm/graph_minus.rs @@ -1,169 +1,189 @@ -/* - * Copyright (c) 2018 UNSW Sydney, Data and Knowledge Group. - * - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you 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. - */ -use std::hash::Hash; -use std::ops::Sub; - -use generic::dtype::IdType; -use graph_impl::graph_map::{new_general_graphmap, TypedDiGraphMap, TypedUnGraphMap}; -use prelude::*; - -macro_rules! sub_graph { - ($graph0:ident,$graph1:ident,$graph:ident) => { - for id in $graph0.node_indices() { - $graph.add_node(id, $graph0.get_node_label(id).cloned()); - } - for (src, dst) in $graph0.edge_indices() { - $graph.add_edge(src, dst, $graph0.get_edge_label(src, dst).cloned()); - } - for id in $graph1.node_indices() { - $graph.remove_node(id); - } - for (src, dst) in $graph1.edge_indices() { - $graph.remove_edge(src, dst); - } - }; -} - -/// Graph Subtraction of two graphs, g0 and g1. -/// -/// Firstly, nodes and edges from g0 are added to the result graph. -/// Then nodes and edges from g1 are removed from the result graph. -/// -/// Example: -/// -/// ``` -/// use rust_graph::algorithm::graph_minus; -/// use rust_graph::prelude::*; -/// use rust_graph::graph_impl::DiGraphMap; -/// -/// let mut graph0 = DiGraphMap::::new(); -/// graph0.add_node(1, Some(0)); -/// graph0.add_node(2, Some(1)); -/// graph0.add_node(3, Some(2)); -/// graph0.add_node(4, Some(3)); -/// graph0.add_edge(1, 2, Some(10)); -/// graph0.add_edge(3, 4, Some(20)); -/// -/// let mut graph1 = DiGraphMap::::new(); -/// graph1.add_node(3, Some(2)); -/// graph1.add_node(4, Some(3)); -/// graph1.add_edge(3, 4, Some(20)); -/// -/// let result_graph = graph_minus(&graph0, &graph1); -/// -/// ``` -/// -pub fn graph_minus< - 'a, - 'b, - 'c, - Id: IdType + 'c, - NL: Eq + Hash + Clone + 'c, - EL: Eq + Hash + Clone + 'c, - L: IdType + 'c, ->( - graph0: &'a GeneralGraph, - graph1: &'b GeneralGraph, -) -> Box + 'c> { - let mut result_graph = new_general_graphmap(graph0.is_directed()); - { - let graph = result_graph.as_mut_graph().unwrap(); - sub_graph!(graph0, graph1, graph); - } - result_graph -} - -/// Trait implementation for general graphs subtraction. -impl<'a, Id: IdType, NL: Hash + Eq + Clone, EL: Hash + Eq + Clone, L: IdType> Sub - for &'a GeneralGraph -{ - type Output = Box + 'a>; - - fn sub(self, other: &'a GeneralGraph) -> Box + 'a> { - graph_minus(self, other) - } -} - -/// Trait implementation for boxed general graphs subtraction. -impl<'a, Id: IdType, NL: Hash + Eq + Clone + 'a, EL: Hash + Eq + Clone + 'a, L: IdType> Sub - for Box + 'a> -{ - type Output = Box + 'a>; - - fn sub( - self, - other: Box + 'a>, - ) -> Box + 'a> { - graph_minus(self.as_ref(), other.as_ref()) - } -} - -/// Trait implementation for TypedDiGraphMap addition. -impl Sub - for TypedDiGraphMap -{ - type Output = TypedDiGraphMap; - - fn sub(self, other: TypedDiGraphMap) -> TypedDiGraphMap { - let mut graph = TypedDiGraphMap::new(); - sub_graph!(self, other, graph); - graph - } -} - -/// Trait implementation for TypedUnGraphMap addition. -impl Sub - for TypedUnGraphMap -{ - type Output = TypedUnGraphMap; - - fn sub(self, other: TypedUnGraphMap) -> TypedUnGraphMap { - let mut graph = TypedUnGraphMap::new(); - sub_graph!(self, other, graph); - graph - } -} - -/// Trait implementation for boxed TypedDiGraphMap addition. -impl Sub - for Box> -{ - type Output = TypedDiGraphMap; - - fn sub(self, other: Box>) -> TypedDiGraphMap { - let mut graph = TypedDiGraphMap::new(); - sub_graph!(self, other, graph); - graph - } -} - -/// Trait implementation for boxed TypedUnGraphMap addition. -impl Sub - for Box> -{ - type Output = TypedUnGraphMap; - - fn sub(self, other: Box>) -> TypedUnGraphMap { - let mut graph = TypedUnGraphMap::new(); - sub_graph!(self, other, graph); - graph - } -} +/* + * Copyright (c) 2018 UNSW Sydney, Data and Knowledge Group. + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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. + */ +use std::hash::Hash; +use std::ops::Sub; + +use crate::generic::dtype::IdType; +use crate::graph_impl::graph_map::{new_general_graphmap, TypedDiGraphMap, TypedUnGraphMap}; +use crate::prelude::*; + +macro_rules! sub_graph { + ($graph0:ident,$graph1:ident,$graph:ident) => { + for id in $graph0.node_indices() { + let mut_graph = $graph.as_mut_graph().unwrap(); + mut_graph.add_node(id, $graph0.get_node_label(id).cloned()); + } + + for (src, dst) in $graph0.edge_indices() { + let mut_graph = $graph.as_mut_graph().unwrap(); + mut_graph.add_edge(src, dst, $graph0.get_edge_label(src, dst).cloned()); + } + + for (src, dst) in $graph1.edge_indices() { + if $graph.has_edge(src, dst) + && $graph.get_node_label(src) == $graph1.get_node_label(src) + && $graph.get_node_label(dst) == $graph1.get_node_label(dst) + && $graph.get_edge_label(src, dst) == $graph1.get_edge_label(src, dst) + { + let mut_graph = $graph.as_mut_graph().unwrap(); + mut_graph.remove_edge(src, dst); + } + } + + let mut all_nodes = Vec::new(); + for id in $graph.node_indices() { + all_nodes.push(id); + } + + for id in all_nodes { + if $graph.total_degree(id) == 0 { + let mut_graph = $graph.as_mut_graph().unwrap(); + mut_graph.remove_node(id); + } + } + }; +} + +/// Graph Subtraction of two graphs, g0 and g1. +/// +/// Firstly, nodes and edges from g0 are added to the result graph. +/// Then nodes and edges from g1 are removed from the result graph. +/// +/// Example: +/// +/// ``` +/// use rust_graph::algorithm::graph_minus; +/// use rust_graph::prelude::*; +/// use rust_graph::graph_impl::DiGraphMap; +/// +/// let mut graph0 = DiGraphMap::::new(); +/// graph0.add_node(1, Some(0)); +/// graph0.add_node(2, Some(1)); +/// graph0.add_node(3, Some(2)); +/// graph0.add_node(4, Some(3)); +/// graph0.add_edge(1, 2, Some(10)); +/// graph0.add_edge(3, 4, Some(20)); +/// +/// let mut graph1 = DiGraphMap::::new(); +/// graph1.add_node(3, Some(2)); +/// graph1.add_node(4, Some(3)); +/// graph1.add_edge(3, 4, Some(20)); +/// +/// let result_graph = graph_minus(&graph0, &graph1); +/// +/// ``` +/// +pub fn graph_minus< + 'a, + 'b, + 'c, + Id: IdType + 'c, + NL: Eq + Hash + Clone + 'c, + EL: Eq + Hash + Clone + 'c, + L: IdType + 'c, +>( + graph0: &'a dyn GeneralGraph, + graph1: &'b dyn GeneralGraph, +) -> Box + 'c> { + let mut result_graph = new_general_graphmap(graph0.is_directed()); + sub_graph!(graph0, graph1, result_graph); + result_graph +} + +/// Trait implementation for general graphs subtraction. +impl<'a, Id: IdType, NL: Hash + Eq + Clone, EL: Hash + Eq + Clone, L: IdType> Sub + for &'a dyn GeneralGraph +{ + type Output = Box + 'a>; + + fn sub( + self, + other: &'a dyn GeneralGraph, + ) -> Box + 'a> { + graph_minus(self, other) + } +} + +/// Trait implementation for boxed general graphs subtraction. +impl<'a, Id: IdType, NL: Hash + Eq + Clone + 'a, EL: Hash + Eq + Clone + 'a, L: IdType> Sub + for Box + 'a> +{ + type Output = Box + 'a>; + + fn sub( + self, + other: Box + 'a>, + ) -> Box + 'a> { + graph_minus(self.as_ref(), other.as_ref()) + } +} + +/// Trait implementation for TypedDiGraphMap addition. +impl Sub + for TypedDiGraphMap +{ + type Output = TypedDiGraphMap; + + fn sub(self, other: TypedDiGraphMap) -> TypedDiGraphMap { + let mut graph = TypedDiGraphMap::new(); + sub_graph!(self, other, graph); + graph + } +} + +/// Trait implementation for TypedUnGraphMap addition. +impl Sub + for TypedUnGraphMap +{ + type Output = TypedUnGraphMap; + + fn sub(self, other: TypedUnGraphMap) -> TypedUnGraphMap { + let mut graph = TypedUnGraphMap::new(); + sub_graph!(self, other, graph); + graph + } +} + +/// Trait implementation for boxed TypedDiGraphMap addition. +impl Sub + for Box> +{ + type Output = TypedDiGraphMap; + + fn sub(self, other: Box>) -> TypedDiGraphMap { + let mut graph = TypedDiGraphMap::new(); + sub_graph!(self, other, graph); + graph + } +} + +/// Trait implementation for boxed TypedUnGraphMap addition. +impl Sub + for Box> +{ + type Output = TypedUnGraphMap; + + fn sub(self, other: Box>) -> TypedUnGraphMap { + let mut graph = TypedUnGraphMap::new(); + sub_graph!(self, other, graph); + graph + } +} diff --git a/src/algorithm/graph_union.rs b/src/algorithm/graph_union.rs index d7329443..45b0814a 100644 --- a/src/algorithm/graph_union.rs +++ b/src/algorithm/graph_union.rs @@ -1,166 +1,171 @@ -/* - * Copyright (c) 2018 UNSW Sydney, Data and Knowledge Group. - * - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you 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. - */ -use std::hash::Hash; -use std::ops::Add; - -use generic::dtype::IdType; -use graph_impl::graph_map::{new_general_graphmap, TypedDiGraphMap, TypedUnGraphMap}; -use prelude::*; - -macro_rules! add_graph { - ($graph0:ident,$graph1:ident,$graph:ident) => { - for id in $graph0.node_indices() { - $graph.add_node(id, $graph0.get_node_label(id).cloned()); - } - for id in $graph1.node_indices() { - $graph.add_node(id, $graph1.get_node_label(id).cloned()); - } - for (src, dst) in $graph0.edge_indices() { - $graph.add_edge(src, dst, $graph0.get_edge_label(src, dst).cloned()); - } - for (src, dst) in $graph1.edge_indices() { - $graph.add_edge(src, dst, $graph1.get_edge_label(src, dst).cloned()); - } - }; -} - -/// Graph Union of two graphs, g0 and g1. -/// -/// Firstly, nodes and edges from g0 are added to the result graph. -/// Then nodes and edges from g1 are added to the result graph. -/// -/// Example: -/// -/// ``` -/// use rust_graph::algorithm::graph_union; -/// use rust_graph::prelude::*; -/// use rust_graph::graph_impl::DiGraphMap; -/// -/// let mut graph0 = DiGraphMap::::new(); -/// graph0.add_node(1, Some(0)); -/// graph0.add_node(2, Some(1)); -/// graph0.add_edge(1, 2, Some(10)); -/// -/// let mut graph1 = DiGraphMap::::new(); -/// graph1.add_node(3, Some(2)); -/// graph1.add_node(4, Some(3)); -/// graph1.add_edge(3, 4, Some(20)); -/// -/// let result_graph = graph_union(&graph0, &graph1); -/// -/// ``` -/// -pub fn graph_union< - 'a, - 'b, - 'c, - Id: IdType + 'c, - NL: Eq + Hash + Clone + 'c, - EL: Eq + Hash + Clone + 'c, - L: IdType + 'c, ->( - graph0: &'a GeneralGraph, - graph1: &'b GeneralGraph, -) -> Box + 'c> { - let mut result_graph = new_general_graphmap(graph0.is_directed()); - { - let graph = result_graph.as_mut_graph().unwrap(); - add_graph!(graph0, graph1, graph); - } - result_graph -} - -/// Trait implementation for boxed general graphs addition. -impl<'a, Id: IdType, NL: Hash + Eq + Clone + 'a, EL: Hash + Eq + Clone + 'a, L: IdType> Add - for Box + 'a> -{ - type Output = Box + 'a>; - - fn add( - self, - other: Box + 'a>, - ) -> Box + 'a> { - graph_union(self.as_ref(), other.as_ref()) - } -} - -/// Trait implementation for general graphs addition. -impl<'a, Id: IdType, NL: Hash + Eq + Clone, EL: Hash + Eq + Clone, L: IdType> Add - for &'a GeneralGraph -{ - type Output = Box + 'a>; - - fn add(self, other: &'a GeneralGraph) -> Box + 'a> { - graph_union(self, other) - } -} - -/// Trait implementation for TypedDiGraphMap addition. -impl Add - for TypedDiGraphMap -{ - type Output = TypedDiGraphMap; - - fn add(self, other: TypedDiGraphMap) -> TypedDiGraphMap { - let mut graph = TypedDiGraphMap::new(); - add_graph!(self, other, graph); - graph - } -} - -/// Trait implementation for TypedUnGraphMap addition. -impl Add - for TypedUnGraphMap -{ - type Output = TypedUnGraphMap; - - fn add(self, other: TypedUnGraphMap) -> TypedUnGraphMap { - let mut graph = TypedUnGraphMap::new(); - add_graph!(self, other, graph); - graph - } -} - -/// Trait implementation for boxed TypedDiGraphMap addition. -impl Add - for Box> -{ - type Output = TypedDiGraphMap; - - fn add(self, other: Box>) -> TypedDiGraphMap { - let mut graph = TypedDiGraphMap::new(); - add_graph!(self, other, graph); - graph - } -} - -/// Trait implementation for boxed TypedUnGraphMap addition. -impl Add - for Box> -{ - type Output = TypedUnGraphMap; - - fn add(self, other: Box>) -> TypedUnGraphMap { - let mut graph = TypedUnGraphMap::new(); - add_graph!(self, other, graph); - graph - } -} +/* + * Copyright (c) 2018 UNSW Sydney, Data and Knowledge Group. + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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. + */ +use std::hash::Hash; +use std::ops::Add; + +use crate::generic::dtype::IdType; +use crate::graph_impl::graph_map::{new_general_graphmap, TypedDiGraphMap, TypedUnGraphMap}; +use crate::prelude::*; + +macro_rules! add_graph { + ($graph0:ident,$graph1:ident,$graph:ident) => { + for id in $graph0.node_indices() { + $graph.add_node(id, $graph0.get_node_label(id).cloned()); + } + for id in $graph1.node_indices() { + $graph.add_node(id, $graph1.get_node_label(id).cloned()); + } + for (src, dst) in $graph0.edge_indices() { + $graph.add_edge(src, dst, $graph0.get_edge_label(src, dst).cloned()); + } + for (src, dst) in $graph1.edge_indices() { + $graph.add_edge(src, dst, $graph1.get_edge_label(src, dst).cloned()); + } + }; +} + +/// Graph Union of two graphs, g0 and g1. +/// +/// If a node exists in both g0 and g1, then its resulting label is from g1. +/// +/// Firstly, nodes and edges from g0 are added to the result graph. +/// Then nodes and edges from g1 are added to the result graph. +/// +/// Example: +/// +/// ``` +/// use rust_graph::algorithm::graph_union; +/// use rust_graph::prelude::*; +/// use rust_graph::graph_impl::DiGraphMap; +/// +/// let mut graph0 = DiGraphMap::::new(); +/// graph0.add_node(1, Some(0)); +/// graph0.add_node(2, Some(1)); +/// graph0.add_edge(1, 2, Some(10)); +/// +/// let mut graph1 = DiGraphMap::::new(); +/// graph1.add_node(3, Some(2)); +/// graph1.add_node(4, Some(3)); +/// graph1.add_edge(3, 4, Some(20)); +/// +/// let result_graph = graph_union(&graph0, &graph1); +/// +/// ``` +/// +pub fn graph_union< + 'a, + 'b, + 'c, + Id: IdType + 'c, + NL: Eq + Hash + Clone + 'c, + EL: Eq + Hash + Clone + 'c, + L: IdType + 'c, +>( + graph0: &'a dyn GeneralGraph, + graph1: &'b dyn GeneralGraph, +) -> Box + 'c> { + let mut result_graph = new_general_graphmap(graph0.is_directed()); + { + let graph = result_graph.as_mut_graph().unwrap(); + add_graph!(graph0, graph1, graph); + } + result_graph +} + +/// Trait implementation for boxed general graphs addition. +impl<'a, Id: IdType, NL: Hash + Eq + Clone + 'a, EL: Hash + Eq + Clone + 'a, L: IdType> Add + for Box + 'a> +{ + type Output = Box + 'a>; + + fn add( + self, + other: Box + 'a>, + ) -> Box + 'a> { + graph_union(self.as_ref(), other.as_ref()) + } +} + +/// Trait implementation for general graphs addition. +impl<'a, Id: IdType, NL: Hash + Eq + Clone, EL: Hash + Eq + Clone, L: IdType> Add + for &'a dyn GeneralGraph +{ + type Output = Box + 'a>; + + fn add( + self, + other: &'a dyn GeneralGraph, + ) -> Box + 'a> { + graph_union(self, other) + } +} + +/// Trait implementation for TypedDiGraphMap addition. +impl Add + for TypedDiGraphMap +{ + type Output = TypedDiGraphMap; + + fn add(self, other: TypedDiGraphMap) -> TypedDiGraphMap { + let mut graph = TypedDiGraphMap::new(); + add_graph!(self, other, graph); + graph + } +} + +/// Trait implementation for TypedUnGraphMap addition. +impl Add + for TypedUnGraphMap +{ + type Output = TypedUnGraphMap; + + fn add(self, other: TypedUnGraphMap) -> TypedUnGraphMap { + let mut graph = TypedUnGraphMap::new(); + add_graph!(self, other, graph); + graph + } +} + +/// Trait implementation for boxed TypedDiGraphMap addition. +impl Add + for Box> +{ + type Output = TypedDiGraphMap; + + fn add(self, other: Box>) -> TypedDiGraphMap { + let mut graph = TypedDiGraphMap::new(); + add_graph!(self, other, graph); + graph + } +} + +/// Trait implementation for boxed TypedUnGraphMap addition. +impl Add + for Box> +{ + type Output = TypedUnGraphMap; + + fn add(self, other: Box>) -> TypedUnGraphMap { + let mut graph = TypedUnGraphMap::new(); + add_graph!(self, other, graph); + graph + } +} diff --git a/src/algorithm/mod.rs b/src/algorithm/mod.rs index b36bffbd..c9b26a27 100644 --- a/src/algorithm/mod.rs +++ b/src/algorithm/mod.rs @@ -1,33 +1,35 @@ -/* - * Copyright (c) 2018 UNSW Sydney, Data and Knowledge Group. - * - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you 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. - */ -pub mod bfs; -pub mod conn_comp; -pub mod conn_subgraphs; -pub mod dfs; -pub mod graph_minus; -pub mod graph_union; - -pub use algorithm::bfs::Bfs; -pub use algorithm::conn_comp::ConnComp; -pub use algorithm::conn_subgraphs::ConnSubgraph; -pub use algorithm::dfs::Dfs; -pub use algorithm::graph_minus::graph_minus; -pub use algorithm::graph_union::graph_union; +/* + * Copyright (c) 2018 UNSW Sydney, Data and Knowledge Group. + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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. + */ +pub mod bfs; +pub mod conn_comp; +pub mod conn_subgraphs; +pub mod dfs; +pub mod graph_induce; +pub mod graph_minus; +pub mod graph_union; + +pub use crate::algorithm::bfs::Bfs; +pub use crate::algorithm::conn_comp::ConnComp; +pub use crate::algorithm::conn_subgraphs::ConnSubgraph; +pub use crate::algorithm::dfs::Dfs; +pub use crate::algorithm::graph_induce::graph_induce; +pub use crate::algorithm::graph_minus::graph_minus; +pub use crate::algorithm::graph_union::graph_union; diff --git a/src/generic/dtype.rs b/src/generic/dtype.rs index e94a3a39..2d389bb8 100644 --- a/src/generic/dtype.rs +++ b/src/generic/dtype.rs @@ -1,126 +1,118 @@ -/* - * Copyright (c) 2018 UNSW Sydney, Data and Knowledge Group. - * - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you 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. - */ -use serde::Serialize; -use std::fmt::Debug; -use std::hash::Hash; -use std::marker::{Send, Sync}; - -/// The default data type for graph indices is `u32`. -#[cfg(not(feature = "usize_id"))] -pub type DefaultId = u32; - -/// The default data type for graph indices can be set to `usize` by setting `feature="usize_id"`. -#[cfg(feature = "usize_id")] -pub type DefaultId = usize; - -pub type DefaultTy = Directed; - -pub type Void = (); - -pub trait GraphType: Debug + PartialEq + Eq + Copy + Clone + Hash + Serialize { - fn is_directed() -> bool; -} - -/// Marker for directed graph -#[derive(Debug, PartialEq, Eq, Copy, Clone, Hash, Serialize, Deserialize)] -pub enum Directed {} - -/// Marker for undirected graph -#[derive(Debug, PartialEq, Eq, Copy, Clone, Hash, Serialize, Deserialize)] -pub enum Undirected {} - -impl GraphType for Directed { - #[inline(always)] - fn is_directed() -> bool { - true - } -} - -impl GraphType for Undirected { - #[inline(always)] - fn is_directed() -> bool { - false - } -} - -pub unsafe trait IdType: - 'static + Copy + Clone + Default + Hash + Debug + Ord + Send + Sync + Serialize -{ - fn new(x: usize) -> Self; - fn id(&self) -> usize; - fn max_value() -> Self; - fn max_usize() -> usize; - fn increment(&self) -> Self; -} - -unsafe impl IdType for () { - #[inline(always)] - fn new(_: usize) -> Self { - () - } - #[inline(always)] - fn id(&self) -> usize { - 0 - } - #[inline(always)] - fn max_value() -> Self { - () - } - #[inline(always)] - fn max_usize() -> usize { - 0 - } - #[inline(always)] - fn increment(&self) -> Self { - () - } -} - -macro_rules! impl_id_type { - ($($type:ident,)*) => ( - $( - unsafe impl IdType for $type { - #[inline(always)] - fn new(x: usize) -> Self { - x as $type - } - #[inline(always)] - fn id(&self) -> usize { - *self as usize - } - #[inline(always)] - fn max_value() -> Self { - ::std::$type::MAX - } - #[inline(always)] - fn max_usize() -> usize { - ::std::$type::MAX as usize - } - #[inline(always)] - fn increment(&self) -> Self { - *self + 1 - } - } - )* - ) -} - -impl_id_type!(u8, u16, u32, usize,); +/* + * Copyright (c) 2018 UNSW Sydney, Data and Knowledge Group. + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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. + */ +use std::fmt::Debug; +use std::hash::Hash; +use std::marker::{Send, Sync}; + +/// The default data type for graph indices is `u32`. +#[cfg(not(feature = "usize_id"))] +pub type DefaultId = u32; + +/// The default data type for graph indices can be set to `usize` by setting `feature="usize_id"`. +#[cfg(feature = "usize_id")] +pub type DefaultId = usize; + +pub type DefaultTy = Directed; + +pub type Void = (); + +pub trait GraphType: Debug + PartialEq + Eq + Copy + Clone + Hash { + fn is_directed() -> bool; +} + +/// Marker for directed graph +#[derive(Debug, PartialEq, Eq, Copy, Clone, Hash, Serialize, Deserialize)] +pub enum Directed {} + +/// Marker for undirected graph +#[derive(Debug, PartialEq, Eq, Copy, Clone, Hash, Serialize, Deserialize)] +pub enum Undirected {} + +impl GraphType for Directed { + #[inline(always)] + fn is_directed() -> bool { + true + } +} + +impl GraphType for Undirected { + #[inline(always)] + fn is_directed() -> bool { + false + } +} + +pub unsafe trait IdType: + 'static + Copy + Clone + Default + Hash + Debug + Ord + Eq + Send + Sync +{ + fn new(x: usize) -> Self; + fn id(&self) -> usize; + fn max_value() -> Self; + fn max_usize() -> usize; + #[inline(always)] + fn increment(&mut self) {} +} + +unsafe impl IdType for () { + #[inline(always)] + fn new(_: usize) -> Self {} + #[inline(always)] + fn id(&self) -> usize { + 0 + } + #[inline(always)] + fn max_value() -> Self {} + #[inline(always)] + fn max_usize() -> usize { + 0 + } +} + +macro_rules! impl_id_type { + ($($type:ident,)*) => ( + $( + unsafe impl IdType for $type { + #[inline(always)] + fn new(x: usize) -> Self { + x as $type + } + #[inline(always)] + fn id(&self) -> usize { + *self as usize + } + #[inline(always)] + fn max_value() -> Self { + ::std::$type::MAX + } + #[inline(always)] + fn max_usize() -> usize { + ::std::$type::MAX as usize + } + #[inline(always)] + fn increment(&mut self) { + *self+=1; + } + } + )* + ) +} + +impl_id_type!(u8, u16, u32, usize,); diff --git a/src/generic/edge.rs b/src/generic/edge.rs index ed4431b7..70f01d2a 100644 --- a/src/generic/edge.rs +++ b/src/generic/edge.rs @@ -1,194 +1,194 @@ -/* - * Copyright (c) 2018 UNSW Sydney, Data and Knowledge Group. - * - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you 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. - */ -use generic::IdType; -pub use graph_impl::graph_map::{Edge, MutEdge}; - -pub trait EdgeTrait { - #[inline(always)] - fn is_none(&self) -> bool { - false - } - #[inline(always)] - fn is_some(&self) -> bool { - !self.is_none() - } - fn get_start(&self) -> Id; - fn get_target(&self) -> Id; - fn get_label_id(&self) -> Option; -} - -pub trait MutEdgeTrait: EdgeTrait { - fn set_label_id(&mut self, label: Option); -} - -#[derive(Debug, PartialEq, Eq)] -pub enum MutEdgeType<'a, Id: IdType, L: IdType = Id> { - EdgeRef(MutEdge<'a, Id, L>), - None, -} - -#[derive(Debug, PartialEq, Eq)] -pub enum OwnedEdgeType { - Edge(Edge), - None, -} - -#[derive(Debug, Clone, PartialEq, Eq)] -pub enum EdgeType { - Edge(Edge), - None, -} - -impl<'a, Id: IdType, L: IdType> MutEdgeType<'a, Id, L> { - #[inline(always)] - pub fn unwrap_edge(self) -> Edge { - match self { - MutEdgeType::EdgeRef(_) => panic!("'unwrap_edge()` on `EdgeRef`"), - MutEdgeType::None => panic!("`unwrap_edge()` on `None`"), - } - } - - #[inline(always)] - pub fn unwrap_edge_ref(self) -> MutEdge<'a, Id, L> { - match self { - MutEdgeType::EdgeRef(edge) => edge, - MutEdgeType::None => panic!("`unwrap_edge_ref()` on `None`"), - } - } -} - -impl<'a, Id: IdType, L: IdType> EdgeTrait for MutEdgeType<'a, Id, L> { - #[inline(always)] - fn is_none(&self) -> bool { - match self { - MutEdgeType::None => true, - _ => false, - } - } - - #[inline(always)] - fn get_start(&self) -> Id { - match self { - MutEdgeType::EdgeRef(edge) => edge.get_start(), - MutEdgeType::None => panic!("`get_start()` on `None`"), - } - } - - #[inline(always)] - fn get_target(&self) -> Id { - match self { - MutEdgeType::EdgeRef(edge) => edge.get_target(), - MutEdgeType::None => panic!("`get_target()` on `None`"), - } - } - - #[inline(always)] - fn get_label_id(&self) -> Option { - match self { - MutEdgeType::EdgeRef(edge) => edge.get_label_id(), - MutEdgeType::None => panic!("`get_label_id()` on `None`"), - } - } -} - -impl<'a, Id: IdType, L: IdType> MutEdgeTrait for MutEdgeType<'a, Id, L> { - #[inline(always)] - fn set_label_id(&mut self, label: Option) { - match self { - MutEdgeType::EdgeRef(edge) => edge.set_label_id(label), - MutEdgeType::None => panic!("`set_label_id()` on `None`"), - } - } -} - -impl EdgeTrait for OwnedEdgeType { - fn is_none(&self) -> bool { - match self { - OwnedEdgeType::None => true, - _ => false, - } - } - - fn get_start(&self) -> Id { - match self { - OwnedEdgeType::Edge(edge) => edge.get_start(), - OwnedEdgeType::None => panic!("`get_start()` on `None`"), - } - } - - fn get_target(&self) -> Id { - match self { - OwnedEdgeType::Edge(edge) => edge.get_target(), - OwnedEdgeType::None => panic!("`get_target()` on `None`"), - } - } - - fn get_label_id(&self) -> Option { - match self { - OwnedEdgeType::Edge(edge) => edge.get_label_id(), - OwnedEdgeType::None => panic!("`get_label_id()` on `None`"), - } - } -} - -impl EdgeType { - #[inline(always)] - pub fn unwrap(self) -> Edge { - match self { - EdgeType::Edge(edge) => edge, - EdgeType::None => panic!("`unwrap()` on `None`"), - } - } -} - -impl EdgeTrait for EdgeType { - #[inline(always)] - fn is_none(&self) -> bool { - match *self { - EdgeType::None => true, - _ => false, - } - } - - #[inline(always)] - fn get_start(&self) -> Id { - match self { - EdgeType::Edge(edge) => edge.get_start(), - EdgeType::None => panic!("`get_start()` on `None`"), - } - } - - #[inline(always)] - fn get_target(&self) -> Id { - match self { - EdgeType::Edge(edge) => edge.get_target(), - EdgeType::None => panic!("`get_target()` on`None`"), - } - } - - #[inline(always)] - fn get_label_id(&self) -> Option { - match self { - EdgeType::Edge(edge) => edge.get_label_id(), - EdgeType::None => None, - } - } -} +/* + * Copyright (c) 2018 UNSW Sydney, Data and Knowledge Group. + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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. + */ +use crate::generic::IdType; +pub use crate::graph_impl::graph_map::{Edge, MutEdge}; + +pub trait EdgeTrait { + #[inline(always)] + fn is_none(&self) -> bool { + false + } + #[inline(always)] + fn is_some(&self) -> bool { + !self.is_none() + } + fn get_start(&self) -> Id; + fn get_target(&self) -> Id; + fn get_label_id(&self) -> Option; +} + +pub trait MutEdgeTrait: EdgeTrait { + fn set_label_id(&mut self, label: Option); +} + +#[derive(Debug, PartialEq, Eq)] +pub enum MutEdgeType<'a, Id: IdType, L: IdType = Id> { + EdgeRef(MutEdge<'a, Id, L>), + None, +} + +#[derive(Debug, PartialEq, Eq)] +pub enum OwnedEdgeType { + Edge(Edge), + None, +} + +#[derive(Debug, Clone, PartialEq, Eq)] +pub enum EdgeType { + Edge(Edge), + None, +} + +impl<'a, Id: IdType, L: IdType> MutEdgeType<'a, Id, L> { + #[inline(always)] + pub fn unwrap_edge(self) -> Edge { + match self { + MutEdgeType::EdgeRef(_) => panic!("'unwrap_edge()` on `EdgeRef`"), + MutEdgeType::None => panic!("`unwrap_edge()` on `None`"), + } + } + + #[inline(always)] + pub fn unwrap_edge_ref(self) -> MutEdge<'a, Id, L> { + match self { + MutEdgeType::EdgeRef(edge) => edge, + MutEdgeType::None => panic!("`unwrap_edge_ref()` on `None`"), + } + } +} + +impl<'a, Id: IdType, L: IdType> EdgeTrait for MutEdgeType<'a, Id, L> { + #[inline(always)] + fn is_none(&self) -> bool { + match self { + MutEdgeType::None => true, + _ => false, + } + } + + #[inline(always)] + fn get_start(&self) -> Id { + match self { + MutEdgeType::EdgeRef(edge) => edge.get_start(), + MutEdgeType::None => panic!("`get_start()` on `None`"), + } + } + + #[inline(always)] + fn get_target(&self) -> Id { + match self { + MutEdgeType::EdgeRef(edge) => edge.get_target(), + MutEdgeType::None => panic!("`get_target()` on `None`"), + } + } + + #[inline(always)] + fn get_label_id(&self) -> Option { + match self { + MutEdgeType::EdgeRef(edge) => edge.get_label_id(), + MutEdgeType::None => panic!("`get_label_id()` on `None`"), + } + } +} + +impl<'a, Id: IdType, L: IdType> MutEdgeTrait for MutEdgeType<'a, Id, L> { + #[inline(always)] + fn set_label_id(&mut self, label: Option) { + match self { + MutEdgeType::EdgeRef(edge) => edge.set_label_id(label), + MutEdgeType::None => panic!("`set_label_id()` on `None`"), + } + } +} + +impl EdgeTrait for OwnedEdgeType { + fn is_none(&self) -> bool { + match self { + OwnedEdgeType::None => true, + _ => false, + } + } + + fn get_start(&self) -> Id { + match self { + OwnedEdgeType::Edge(edge) => edge.get_start(), + OwnedEdgeType::None => panic!("`get_start()` on `None`"), + } + } + + fn get_target(&self) -> Id { + match self { + OwnedEdgeType::Edge(edge) => edge.get_target(), + OwnedEdgeType::None => panic!("`get_target()` on `None`"), + } + } + + fn get_label_id(&self) -> Option { + match self { + OwnedEdgeType::Edge(edge) => edge.get_label_id(), + OwnedEdgeType::None => panic!("`get_label_id()` on `None`"), + } + } +} + +impl EdgeType { + #[inline(always)] + pub fn unwrap(self) -> Edge { + match self { + EdgeType::Edge(edge) => edge, + EdgeType::None => panic!("`unwrap()` on `None`"), + } + } +} + +impl EdgeTrait for EdgeType { + #[inline(always)] + fn is_none(&self) -> bool { + match *self { + EdgeType::None => true, + _ => false, + } + } + + #[inline(always)] + fn get_start(&self) -> Id { + match self { + EdgeType::Edge(edge) => edge.get_start(), + EdgeType::None => panic!("`get_start()` on `None`"), + } + } + + #[inline(always)] + fn get_target(&self) -> Id { + match self { + EdgeType::Edge(edge) => edge.get_target(), + EdgeType::None => panic!("`get_target()` on`None`"), + } + } + + #[inline(always)] + fn get_label_id(&self) -> Option { + match self { + EdgeType::Edge(edge) => edge.get_label_id(), + EdgeType::None => None, + } + } +} diff --git a/src/generic/graph.rs b/src/generic/graph.rs index ad9f7ae5..29e97a46 100644 --- a/src/generic/graph.rs +++ b/src/generic/graph.rs @@ -1,329 +1,380 @@ -/* - * Copyright (c) 2018 UNSW Sydney, Data and Knowledge Group. - * - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you 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. - */ -use std::borrow::Cow; -use std::hash::{Hash, Hasher}; - -use itertools::Itertools; - -use counter::Counter; - -use generic::{ - EdgeTrait, EdgeType, IdType, Iter, MapTrait, MutEdgeType, MutNodeType, NodeTrait, NodeType, - OwnedEdgeType, OwnedNodeType, -}; -use graph_impl::graph_map::new_general_graphmap; -use graph_impl::GraphImpl; -use map::SetMap; - -pub trait GeneralGraph: - GraphTrait + GraphLabelTrait -{ - fn as_graph(&self) -> &GraphTrait; - - fn as_labeled_graph(&self) -> &GraphLabelTrait; - - fn as_general_graph(&self) -> &GeneralGraph; - - #[inline(always)] - fn as_digraph(&self) -> Option<&DiGraphTrait> { - None - } - - #[inline(always)] - fn as_mut_graph(&mut self) -> Option<&mut MutGraphTrait> { - None - } -} - -impl Clone - for Box> -{ - fn clone(&self) -> Self { - let g = if self.as_digraph().is_some() { - new_general_graphmap(true) - } else { - new_general_graphmap(false) - }; - - ::algorithm::graph_union(self.as_ref(), g.as_ref()) - } -} - -pub trait GraphTrait { - /// Get an immutable reference to the node. - fn get_node(&self, id: Id) -> NodeType; - - /// Get an immutable reference to the edge. - fn get_edge(&self, start: Id, target: Id) -> EdgeType; - - /// Check if the node is in the graph. - fn has_node(&self, id: Id) -> bool; - - /// Check if the edge is in the graph. - fn has_edge(&self, start: Id, target: Id) -> bool; - - /// Return the number of nodes in the graph. - fn node_count(&self) -> usize; - - /// Return the number of edges in the graph. - fn edge_count(&self) -> usize; - - /// Whether if the graph is directed or not. - fn is_directed(&self) -> bool; - - /// Return an iterator over the node indices of the graph. - fn node_indices(&self) -> Iter; - - /// Return an iterator over the edge indices of the graph. - fn edge_indices(&self) -> Iter<(Id, Id)>; - - /// Return an iterator of all nodes in the graph. - fn nodes(&self) -> Iter>; - - /// Return an iterator over all edges in the graph. - fn edges(&self) -> Iter>; - - /// Return the degree of a node. - fn degree(&self, id: Id) -> usize; - - /// Return an iterator over the indices of all nodes adjacent to a given node. - fn neighbors_iter(&self, id: Id) -> Iter; - - /// Return the indices(either owned or borrowed) of all nodes adjacent to a given node. - fn neighbors(&self, id: Id) -> Cow<[Id]>; - - /// Return the maximum id has been seen until now. - fn max_seen_id(&self) -> Option; - - /// Return how the graph structure is implementated, namely, GraphMap or StaticGraph. - fn implementation(&self) -> GraphImpl; - - fn get_node_label_id_counter(&self) -> Counter { - self.nodes().filter_map(|n| n.get_label_id()).collect() - } - - fn get_edge_label_id_counter(&self) -> Counter { - self.edges().filter_map(|e| e.get_label_id()).collect() - } - - /// Return the maximum id the graph can represent. - #[inline(always)] - fn max_possible_id(&self) -> Id { - Id::max_value() - } - - /// Return the maximum label id the graph can represent. - #[inline(always)] - fn max_possible_label_id(&self) -> L { - L::max_value() - } -} - -pub trait MutGraphTrait { - /// Add a new node with specific id and label. - /// *NOTE*: The label will be converted to an `usize` integer. - fn add_node(&mut self, id: Id, label: Option) -> bool; - - /// Get a mutable reference to the node. - fn get_node_mut(&mut self, id: Id) -> MutNodeType; - - /// Remove the node and return it. - fn remove_node(&mut self, id: Id) -> OwnedNodeType; - - /// Add a new edge (`start`,`target)` with a specific label. - /// *NOTE*: The label will be converted to an `usize` integer. - fn add_edge(&mut self, start: Id, target: Id, label: Option) -> bool; - - /// Get a mutable reference to the edge. - fn get_edge_mut(&mut self, start: Id, target: Id) -> MutEdgeType; - - /// Remove the edge (`start`,`target)` and return it. - fn remove_edge(&mut self, start: Id, target: Id) -> OwnedEdgeType; - - /// Return an iterator of all nodes(mutable) in the graph. - fn nodes_mut(&mut self) -> Iter>; - - /// Return an iterator over all edges(mutable) in the graph. - fn edges_mut(&mut self) -> Iter>; -} - -pub trait GraphLabelTrait: - GraphTrait -{ - /// Return the node label - id mapping. - fn get_node_label_map(&self) -> &SetMap; - - /// Return the edge label - id mapping. - fn get_edge_label_map(&self) -> &SetMap; - - /// Lookup the node label by its id. - #[inline(always)] - fn get_node_label(&self, node_id: Id) -> Option<&NL> { - match self.get_node(node_id).get_label_id() { - Some(label_id) => self.get_node_label_map().get_item(label_id.id()), - None => None, - } - } - - /// Lookup the edge label by its id. - #[inline(always)] - fn get_edge_label(&self, start: Id, target: Id) -> Option<&EL> { - match self.get_edge(start, target).get_label_id() { - Some(label_id) => self.get_edge_label_map().get_item(label_id.id()), - None => None, - } - } - - /// Return an iterator over the set of all node labels. - #[inline] - fn node_labels<'a>(&'a self) -> Iter<'a, &NL> { - self.get_node_label_map().items() - } - - /// Return an iterator over the set of all edge labels. - #[inline] - fn edge_labels<'a>(&'a self) -> Iter<'a, &EL> { - self.get_edge_label_map().items() - } - - #[inline] - fn has_node_labels(&self) -> bool { - self.node_labels().next().is_some() - } - - #[inline] - fn has_edge_labels(&self) -> bool { - self.edge_labels().next().is_some() - } - - #[inline] - fn num_of_node_labels(&self) -> usize { - self.node_labels().count() - } - - #[inline] - fn num_of_edge_labels(&self) -> usize { - self.edge_labels().count() - } - - #[inline] - fn get_node_label_counter(&self) -> Counter<&NL> { - self.node_indices() - .filter_map(|n| self.get_node_label(n)) - .collect() - } - - #[inline] - fn get_edge_label_counter(&self) -> Counter<&EL> { - self.edge_indices() - .filter_map(|(s, d)| self.get_edge_label(s, d)) - .collect() - } -} - -pub trait MutGraphLabelTrait: - MutGraphTrait + GraphLabelTrait -{ - /// Update the node label. - fn update_node_label(&mut self, node_id: Id, label: Option) -> bool; - - /// Update the edge label. - fn update_edge_label(&mut self, start: Id, target: Id, label: Option) -> bool; -} - -/// Trait for undirected graphs. -pub trait UnGraphTrait: GraphTrait {} - -/// Trait for directed graphs. -pub trait DiGraphTrait: GraphTrait { - /// Return the in-degree of a node. - fn in_degree(&self, id: Id) -> usize; - - /// Return an iterator over the indices of all nodes with a edge from a given node. - fn in_neighbors_iter(&self, id: Id) -> Iter; - - /// Return the indices(either owned or borrowed) of all nodes with a edge from a given node. - fn in_neighbors(&self, id: Id) -> Cow<[Id]>; -} - -pub fn equal( - g: &GeneralGraph, - gg: &GeneralGraph, -) -> bool { - if g.is_directed() != gg.is_directed() - || g.node_count() != gg.node_count() - || g.edge_count() != gg.edge_count() - { - return false; - } - - for n in g.node_indices() { - if !gg.has_node(n) || g.get_node_label(n) != gg.get_node_label(n) { - return false; - } - } - - for (s, d) in g.edge_indices() { - if !gg.has_edge(s, d) || g.get_edge_label(s, d) != gg.get_edge_label(s, d) { - return false; - } - } - - true -} - -impl PartialEq - for Box> -{ - fn eq(&self, other: &Box>) -> bool { - equal(self.as_ref(), other.as_ref()) - } -} - -impl Eq for Box> {} - -impl Hash - for Box> -{ - fn hash(&self, state: &mut H) { - { - self.as_digraph().is_some().hash(state); - - let nodes = self.node_indices().sorted(); - nodes.hash(state); - - let node_labels = nodes - .into_iter() - .map(|n| self.get_node_label(n)) - .collect_vec(); - node_labels.hash(state); - } - { - let edges = self.edge_indices().sorted(); - edges.hash(state); - let edge_labels = edges - .into_iter() - .map(|(s, d)| self.get_edge_label(s, d)) - .collect_vec(); - edge_labels.hash(state); - } - } -} +/* + * Copyright (c) 2018 UNSW Sydney, Data and Knowledge Group. + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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. + */ +use std::borrow::Cow; +use std::cmp::Ordering; +use std::hash::{Hash, Hasher}; + +use counter::Counter; +use itertools::Itertools; + +use crate::generic::{ + EdgeTrait, EdgeType, IdType, Iter, MapTrait, MutEdgeType, MutNodeType, NodeTrait, NodeType, + OwnedEdgeType, OwnedNodeType, +}; +use crate::graph_impl::graph_map::new_general_graphmap; +use crate::graph_impl::GraphImpl; +use crate::map::SetMap; + +pub trait GeneralGraph: + GraphTrait + GraphLabelTrait +{ + fn as_graph(&self) -> &dyn GraphTrait; + + fn as_labeled_graph(&self) -> &dyn GraphLabelTrait; + + fn as_general_graph(&self) -> &dyn GeneralGraph; + + #[inline(always)] + fn as_digraph(&self) -> Option<&dyn DiGraphTrait> { + None + } + + #[inline(always)] + fn as_mut_graph(&mut self) -> Option<&mut dyn MutGraphTrait> { + None + } +} + +impl Clone + for Box> +{ + fn clone(&self) -> Self { + let g = if self.as_digraph().is_some() { + new_general_graphmap(true) + } else { + new_general_graphmap(false) + }; + + crate::algorithm::graph_union(self.as_ref(), g.as_ref()) + } +} + +pub trait GraphTrait { + /// Get an immutable reference to the node. + fn get_node(&self, id: Id) -> NodeType; + + /// Get an immutable reference to the edge. + fn get_edge(&self, start: Id, target: Id) -> EdgeType; + + /// Check if the node is in the graph. + fn has_node(&self, id: Id) -> bool; + + /// Check if the edge is in the graph. + fn has_edge(&self, start: Id, target: Id) -> bool; + + /// Return the number of nodes in the graph. + fn node_count(&self) -> usize; + + /// Return the number of edges in the graph. + fn edge_count(&self) -> usize; + + /// Whether if the graph is directed or not. + fn is_directed(&self) -> bool; + + /// Return an iterator over the node indices of the graph. + fn node_indices(&self) -> Iter; + + /// Return an iterator over the edge indices of the graph. + fn edge_indices(&self) -> Iter<(Id, Id)>; + + /// Return an iterator of all nodes in the graph. + fn nodes(&self) -> Iter>; + + /// Return an iterator over all edges in the graph. + fn edges(&self) -> Iter>; + + /// Return the degree of a node. + fn degree(&self, id: Id) -> usize; + + /// Return total degree of a node. + fn total_degree(&self, id: Id) -> usize; + + /// Return an iterator over the indices of all nodes adjacent to a given node. + fn neighbors_iter(&self, id: Id) -> Iter; + + /// Return the indices(either owned or borrowed) of all nodes adjacent to a given node. + fn neighbors(&self, id: Id) -> Cow<[Id]>; + + /// Return the maximum id has been seen until now. + fn max_seen_id(&self) -> Option; + + /// Return how the graph structure is implementated, namely, GraphMap or StaticGraph. + fn implementation(&self) -> GraphImpl; + + fn get_node_label_id_counter(&self) -> Counter { + self.nodes().filter_map(|n| n.get_label_id()).collect() + } + + fn get_edge_label_id_counter(&self) -> Counter { + self.edges().filter_map(|e| e.get_label_id()).collect() + } + + /// Return the maximum id the graph can represent. + #[inline(always)] + fn max_possible_id(&self) -> Id { + Id::max_value() + } + + /// Return the maximum label id the graph can represent. + #[inline(always)] + fn max_possible_label_id(&self) -> L { + L::max_value() + } +} + +pub trait MutGraphTrait { + /// Add a new node with specific id and label. + /// *NOTE*: The label will be converted to an `usize` integer. + fn add_node(&mut self, id: Id, label: Option) -> bool; + + /// Get a mutable reference to the node. + fn get_node_mut(&mut self, id: Id) -> MutNodeType; + + /// Remove the node and return it. + fn remove_node(&mut self, id: Id) -> OwnedNodeType; + + /// Add a new edge (`start`,`target)` with a specific label. + /// *NOTE*: The label will be converted to an `usize` integer. + fn add_edge(&mut self, start: Id, target: Id, label: Option) -> bool; + + /// Get a mutable reference to the edge. + fn get_edge_mut(&mut self, start: Id, target: Id) -> MutEdgeType; + + /// Remove the edge (`start`,`target)` and return it. + fn remove_edge(&mut self, start: Id, target: Id) -> OwnedEdgeType; + + /// Return an iterator of all nodes(mutable) in the graph. + fn nodes_mut(&mut self) -> Iter>; + + /// Return an iterator over all edges(mutable) in the graph. + fn edges_mut(&mut self) -> Iter>; +} + +pub trait GraphLabelTrait: + GraphTrait +{ + /// Return the node label - id mapping. + fn get_node_label_map(&self) -> &SetMap; + + /// Return the edge label - id mapping. + fn get_edge_label_map(&self) -> &SetMap; + + /// Lookup the node label by its id. + #[inline(always)] + fn get_node_label(&self, node_id: Id) -> Option<&NL> { + match self.get_node(node_id).get_label_id() { + Some(label_id) => self.get_node_label_map().get_item(label_id.id()), + None => None, + } + } + + /// Lookup the edge label by its id. + #[inline(always)] + fn get_edge_label(&self, start: Id, target: Id) -> Option<&EL> { + match self.get_edge(start, target).get_label_id() { + Some(label_id) => self.get_edge_label_map().get_item(label_id.id()), + None => None, + } + } + + /// Return an iterator over the set of all node labels. + #[inline] + fn node_labels<'a>(&'a self) -> Iter<'a, &NL> { + self.get_node_label_map().items() + } + + /// Return an iterator over the set of all edge labels. + #[inline] + fn edge_labels<'a>(&'a self) -> Iter<'a, &EL> { + self.get_edge_label_map().items() + } + + #[inline] + fn has_node_labels(&self) -> bool { + self.node_labels().next().is_some() + } + + #[inline] + fn has_edge_labels(&self) -> bool { + self.edge_labels().next().is_some() + } + + #[inline] + fn num_of_node_labels(&self) -> usize { + self.node_labels().count() + } + + #[inline] + fn num_of_edge_labels(&self) -> usize { + self.edge_labels().count() + } + + #[inline] + fn get_node_label_counter(&self) -> Counter<&NL> { + self.node_indices() + .filter_map(|n| self.get_node_label(n)) + .collect() + } + + #[inline] + fn get_edge_label_counter(&self) -> Counter<&EL> { + self.edge_indices() + .filter_map(|(s, d)| self.get_edge_label(s, d)) + .collect() + } +} + +pub trait MutGraphLabelTrait: + MutGraphTrait + GraphLabelTrait +{ + /// Update the node label. + fn update_node_label(&mut self, node_id: Id, label: Option) -> bool; + + /// Update the edge label. + fn update_edge_label(&mut self, start: Id, target: Id, label: Option) -> bool; +} + +/// Trait for undirected graphs. +pub trait UnGraphTrait: GraphTrait {} + +/// Trait for directed graphs. +pub trait DiGraphTrait: GraphTrait { + /// Return the in-degree of a node. + fn in_degree(&self, id: Id) -> usize; + + /// Return an iterator over the indices of all nodes with a edge from a given node. + fn in_neighbors_iter(&self, id: Id) -> Iter; + + /// Return the indices(either owned or borrowed) of all nodes with a edge from a given node. + fn in_neighbors(&self, id: Id) -> Cow<[Id]>; +} + +pub fn equal( + g: &dyn GeneralGraph, + gg: &dyn GeneralGraph, +) -> bool { + if g.is_directed() != gg.is_directed() + || g.node_count() != gg.node_count() + || g.edge_count() != gg.edge_count() + { + return false; + } + + for n in g.node_indices() { + if !gg.has_node(n) || g.get_node_label(n) != gg.get_node_label(n) { + return false; + } + } + + for (s, d) in g.edge_indices() { + if !gg.has_edge(s, d) || g.get_edge_label(s, d) != gg.get_edge_label(s, d) { + return false; + } + } + + true +} + +impl PartialEq + for Box> +{ + fn eq(&self, other: &Box>) -> bool { + equal(self.as_ref(), other.as_ref()) + } +} + +impl Eq + for Box> +{ +} + +impl Hash + for Box> +{ + fn hash(&self, state: &mut H) { + { + self.as_digraph().is_some().hash(state); + + let nodes = self.node_indices().sorted(); + nodes.hash(state); + + let node_labels = nodes + .into_iter() + .map(|n| self.get_node_label(n)) + .collect_vec(); + node_labels.hash(state); + } + { + let edges = self.edge_indices().sorted(); + edges.hash(state); + let edge_labels = edges + .into_iter() + .map(|(s, d)| self.get_edge_label(s, d)) + .collect_vec(); + edge_labels.hash(state); + } + } +} + +impl PartialOrd + for Box> +{ + fn partial_cmp(&self, other: &Self) -> Option { + if self.node_count() != other.node_count() { + return Some(self.node_count().cmp(&other.node_count())); + } else { + for (node1, node2) in self.node_indices().zip(other.node_indices()) { + if node1 != node2 { + return Some(node1.cmp(&node2)); + } else { + let deg1 = self.degree(node1); + let deg2 = self.degree(node2); + + if deg1 != deg2 { + return Some(deg1.cmp(°2)); + } else { + for (nbr1, nbr2) in + self.neighbors_iter(node1).zip(other.neighbors_iter(node2)) + { + if nbr1 != nbr2 { + return Some(nbr1.cmp(&nbr2)); + } + } + } + } + } + } + + None + } +} + +impl Ord + for Box> +{ + fn cmp(&self, other: &Self) -> Ordering { + if let Some(ord) = self.partial_cmp(other) { + ord + } else { + Ordering::Equal + } + } +} diff --git a/src/generic/iter.rs b/src/generic/iter.rs index 76d3eaa4..d27cab10 100644 --- a/src/generic/iter.rs +++ b/src/generic/iter.rs @@ -1,58 +1,58 @@ -/* - * Copyright (c) 2018 UNSW Sydney, Data and Knowledge Group. - * - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you 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. - */ -use std::iter::empty; - -pub struct Iter<'a, T> { - inner: Box + 'a>, -} - -impl<'a, T> Iter<'a, T> { - pub fn new(iter: Box + 'a>) -> Self { - Iter { inner: iter } - } -} - -impl<'a, T: 'a> Iter<'a, T> { - pub fn empty() -> Self { - Iter { - inner: Box::new(empty()), - } - } -} - -impl<'a, T> Iterator for Iter<'a, T> { - type Item = T; - - #[inline(always)] - fn next(&mut self) -> Option { - self.inner.next() - } - - #[inline(always)] - fn size_hint(&self) -> (usize, Option) { - self.inner.size_hint() - } - - #[inline(always)] - fn count(self) -> usize { - self.inner.count() - } -} +/* + * Copyright (c) 2018 UNSW Sydney, Data and Knowledge Group. + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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. + */ +use std::iter::empty; + +pub struct Iter<'a, T> { + inner: Box + 'a>, +} + +impl<'a, T> Iter<'a, T> { + pub fn new(iter: Box + 'a>) -> Self { + Iter { inner: iter } + } +} + +impl<'a, T: 'a> Iter<'a, T> { + pub fn empty() -> Self { + Iter { + inner: Box::new(empty()), + } + } +} + +impl<'a, T> Iterator for Iter<'a, T> { + type Item = T; + + #[inline(always)] + fn next(&mut self) -> Option { + self.inner.next() + } + + #[inline(always)] + fn size_hint(&self) -> (usize, Option) { + self.inner.size_hint() + } + + #[inline(always)] + fn count(self) -> usize { + self.inner.count() + } +} diff --git a/src/generic/map.rs b/src/generic/map.rs index 1aab1659..fe4a8ca4 100644 --- a/src/generic/map.rs +++ b/src/generic/map.rs @@ -1,45 +1,45 @@ -/* - * Copyright (c) 2018 UNSW Sydney, Data and Knowledge Group. - * - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you 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. - */ -use generic::Iter; - -pub trait MapTrait { - fn get_item(&self, id: usize) -> Option<&L>; - fn find_index(&self, item: &L) -> Option; - - fn contains(&self, item: &L) -> bool; - - fn items(&self) -> Iter<&L>; - fn items_vec(self) -> Vec; - - fn len(&self) -> usize; - - #[inline(always)] - fn is_empty(&self) -> bool { - self.len() == 0 - } -} - -pub trait MutMapTrait { - /// Add a new item to the map and return its index - fn add_item(&mut self, item: L) -> usize; - - fn pop_item(&mut self) -> Option; -} +/* + * Copyright (c) 2018 UNSW Sydney, Data and Knowledge Group. + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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. + */ +use crate::generic::Iter; + +pub trait MapTrait { + fn get_item(&self, id: usize) -> Option<&L>; + fn find_index(&self, item: &L) -> Option; + + fn contains(&self, item: &L) -> bool; + + fn items(&self) -> Iter<&L>; + fn items_vec(self) -> Vec; + + fn len(&self) -> usize; + + #[inline(always)] + fn is_empty(&self) -> bool { + self.len() == 0 + } +} + +pub trait MutMapTrait { + /// Add a new item to the map and return its index + fn add_item(&mut self, item: L) -> usize; + + fn pop_item(&mut self) -> Option; +} diff --git a/src/generic/mod.rs b/src/generic/mod.rs index 81668edc..429910ca 100644 --- a/src/generic/mod.rs +++ b/src/generic/mod.rs @@ -1,39 +1,41 @@ -/* - * Copyright (c) 2018 UNSW Sydney, Data and Knowledge Group. - * - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you 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. - */ -pub mod dtype; -pub mod edge; -pub mod graph; -pub mod iter; -pub mod map; -pub mod node; - -pub use generic::dtype::{DefaultId, DefaultTy, Directed, GraphType, IdType, Undirected, Void}; -pub use generic::edge::{ - Edge, EdgeTrait, EdgeType, MutEdge, MutEdgeTrait, MutEdgeType, OwnedEdgeType, -}; -pub use generic::graph::{ - DiGraphTrait, GeneralGraph, GraphLabelTrait, GraphTrait, MutGraphLabelTrait, MutGraphTrait, - UnGraphTrait, -}; -pub use generic::iter::Iter; -pub use generic::map::{MapTrait, MutMapTrait}; -pub use generic::node::{MutNodeTrait, MutNodeType, NodeTrait, NodeType, OwnedNodeType}; -pub use graph_impl::graph_map::{MutNodeMapTrait, NodeMapTrait}; +/* + * Copyright (c) 2018 UNSW Sydney, Data and Knowledge Group. + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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. + */ +pub mod dtype; +pub mod edge; +pub mod graph; +pub mod iter; +pub mod map; +pub mod node; + +pub use crate::generic::dtype::{ + DefaultId, DefaultTy, Directed, GraphType, IdType, Undirected, Void, +}; +pub use crate::generic::edge::{ + Edge, EdgeTrait, EdgeType, MutEdge, MutEdgeTrait, MutEdgeType, OwnedEdgeType, +}; +pub use crate::generic::graph::{ + DiGraphTrait, GeneralGraph, GraphLabelTrait, GraphTrait, MutGraphLabelTrait, MutGraphTrait, + UnGraphTrait, +}; +pub use crate::generic::iter::Iter; +pub use crate::generic::map::{MapTrait, MutMapTrait}; +pub use crate::generic::node::{MutNodeTrait, MutNodeType, NodeTrait, NodeType, OwnedNodeType}; +pub use crate::graph_impl::graph_map::{MutNodeMapTrait, NodeMapTrait}; diff --git a/src/generic/node.rs b/src/generic/node.rs index 29bbfbc6..3449d824 100644 --- a/src/generic/node.rs +++ b/src/generic/node.rs @@ -1,176 +1,176 @@ -/* - * Copyright (c) 2018 UNSW Sydney, Data and Knowledge Group. - * - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you 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. - */ -use generic::IdType; -pub use graph_impl::graph_map::NodeMap; -pub use graph_impl::static_graph::StaticNode; - -pub trait NodeTrait { - #[inline(always)] - fn is_none(&self) -> bool { - false - } - #[inline(always)] - fn is_some(&self) -> bool { - !self.is_none() - } - fn get_id(&self) -> Id; - fn get_label_id(&self) -> Option; -} - -pub trait MutNodeTrait: NodeTrait { - fn set_label_id(&mut self, label: Option); -} - -#[derive(Debug, PartialEq, Eq)] -pub enum MutNodeType<'a, Id: 'a + IdType, L: 'a + IdType = Id> { - NodeMapRef(&'a mut NodeMap), - None, -} - -#[derive(Debug, PartialEq, Eq)] -pub enum OwnedNodeType { - NodeMap(NodeMap), - None, -} - -#[derive(Debug, Clone, PartialEq, Eq)] -pub enum NodeType<'a, Id: 'a + IdType, L: 'a + IdType = Id> { - NodeMap(&'a NodeMap), - StaticNode(StaticNode), - None, -} - -impl<'a, Id: IdType, L: IdType> MutNodeType<'a, Id, L> { - #[inline(always)] - pub fn unwrap_nodemap_ref(self) -> &'a mut NodeMap { - match self { - MutNodeType::NodeMapRef(node) => node, - MutNodeType::None => panic!("`unwrap_nodemap_ref()` on `None`"), - } - } -} - -impl<'a, Id: IdType, L: IdType> NodeTrait for MutNodeType<'a, Id, L> { - #[inline(always)] - fn is_none(&self) -> bool { - match *self { - MutNodeType::None => true, - _ => false, - } - } - - #[inline(always)] - fn get_id(&self) -> Id { - match self { - MutNodeType::NodeMapRef(node) => node.get_id(), - MutNodeType::None => panic!("`get_id()` on `None`"), - } - } - - #[inline(always)] - fn get_label_id(&self) -> Option { - match self { - MutNodeType::NodeMapRef(node) => node.get_label_id(), - MutNodeType::None => panic!("`get_label_id()` on `None`"), - } - } -} - -impl<'a, Id: IdType, L: IdType> MutNodeTrait for MutNodeType<'a, Id, L> { - #[inline(always)] - fn set_label_id(&mut self, label: Option) { - match self { - MutNodeType::NodeMapRef(node) => node.set_label_id(label), - MutNodeType::None => panic!("`set_label_id()` on `None`"), - } - } -} - -impl NodeTrait for OwnedNodeType { - fn is_none(&self) -> bool { - match *self { - OwnedNodeType::None => true, - _ => false, - } - } - - fn get_id(&self) -> Id { - match self { - OwnedNodeType::NodeMap(node) => node.get_id(), - OwnedNodeType::None => panic!("`get_id()` on `None`"), - } - } - - fn get_label_id(&self) -> Option { - match self { - OwnedNodeType::NodeMap(node) => node.get_label_id(), - OwnedNodeType::None => panic!("`get_label_id()` on `None`"), - } - } -} - -impl<'a, Id: IdType, L: IdType> NodeType<'a, Id, L> { - #[inline(always)] - pub fn unwrap_nodemap(self) -> &'a NodeMap { - match self { - NodeType::NodeMap(node) => node, - NodeType::StaticNode(_) => panic!("`unwrap_nodemap()` on `StaticNode`"), - NodeType::None => panic!("`unwrap_nodemap()` on `None`"), - } - } - - #[inline(always)] - pub fn unwrap_staticnode(self) -> StaticNode { - match self { - NodeType::NodeMap(_) => panic!("`unwrap_staticnode()` on `NodeMap`"), - NodeType::StaticNode(node) => node, - NodeType::None => panic!("`unwrap_staticnode()` on `None`"), - } - } -} - -impl<'a, Id: IdType, L: IdType> NodeTrait for NodeType<'a, Id, L> { - #[inline(always)] - fn is_none(&self) -> bool { - match *self { - NodeType::None => true, - _ => false, - } - } - - #[inline(always)] - fn get_id(&self) -> Id { - match self { - NodeType::NodeMap(node) => node.get_id(), - NodeType::StaticNode(ref node) => node.get_id(), - NodeType::None => panic!("`get_id()` on `None`"), - } - } - - #[inline(always)] - fn get_label_id(&self) -> Option { - match self { - NodeType::NodeMap(node) => node.get_label_id(), - NodeType::StaticNode(ref node) => node.get_label_id(), - NodeType::None => None, - } - } -} +/* + * Copyright (c) 2018 UNSW Sydney, Data and Knowledge Group. + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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. + */ +use crate::generic::IdType; +pub use crate::graph_impl::graph_map::NodeMap; +pub use crate::graph_impl::static_graph::StaticNode; + +pub trait NodeTrait { + #[inline(always)] + fn is_none(&self) -> bool { + false + } + #[inline(always)] + fn is_some(&self) -> bool { + !self.is_none() + } + fn get_id(&self) -> Id; + fn get_label_id(&self) -> Option; +} + +pub trait MutNodeTrait: NodeTrait { + fn set_label_id(&mut self, label: Option); +} + +#[derive(Debug, PartialEq, Eq)] +pub enum MutNodeType<'a, Id: 'a + IdType, L: 'a + IdType = Id> { + NodeMapRef(&'a mut NodeMap), + None, +} + +#[derive(Debug, PartialEq, Eq)] +pub enum OwnedNodeType { + NodeMap(NodeMap), + None, +} + +#[derive(Debug, Clone, PartialEq, Eq)] +pub enum NodeType<'a, Id: 'a + IdType, L: 'a + IdType = Id> { + NodeMap(&'a NodeMap), + StaticNode(StaticNode), + None, +} + +impl<'a, Id: IdType, L: IdType> MutNodeType<'a, Id, L> { + #[inline(always)] + pub fn unwrap(self) -> &'a mut NodeMap { + match self { + MutNodeType::NodeMapRef(node) => node, + MutNodeType::None => panic!("`unwrap_nodemap_ref()` on `None`"), + } + } +} + +impl<'a, Id: IdType, L: IdType> NodeTrait for MutNodeType<'a, Id, L> { + #[inline(always)] + fn is_none(&self) -> bool { + match *self { + MutNodeType::None => true, + _ => false, + } + } + + #[inline(always)] + fn get_id(&self) -> Id { + match self { + MutNodeType::NodeMapRef(node) => node.get_id(), + MutNodeType::None => panic!("`get_id()` on `None`"), + } + } + + #[inline(always)] + fn get_label_id(&self) -> Option { + match self { + MutNodeType::NodeMapRef(node) => node.get_label_id(), + MutNodeType::None => panic!("`get_label_id()` on `None`"), + } + } +} + +impl<'a, Id: IdType, L: IdType> MutNodeTrait for MutNodeType<'a, Id, L> { + #[inline(always)] + fn set_label_id(&mut self, label: Option) { + match self { + MutNodeType::NodeMapRef(node) => node.set_label_id(label), + MutNodeType::None => panic!("`set_label_id()` on `None`"), + } + } +} + +impl NodeTrait for OwnedNodeType { + fn is_none(&self) -> bool { + match *self { + OwnedNodeType::None => true, + _ => false, + } + } + + fn get_id(&self) -> Id { + match self { + OwnedNodeType::NodeMap(node) => node.get_id(), + OwnedNodeType::None => panic!("`get_id()` on `None`"), + } + } + + fn get_label_id(&self) -> Option { + match self { + OwnedNodeType::NodeMap(node) => node.get_label_id(), + OwnedNodeType::None => panic!("`get_label_id()` on `None`"), + } + } +} + +impl<'a, Id: IdType, L: IdType> NodeType<'a, Id, L> { + #[inline(always)] + pub fn unwrap_nodemap(self) -> &'a NodeMap { + match self { + NodeType::NodeMap(node) => node, + NodeType::StaticNode(_) => panic!("`unwrap_nodemap()` on `StaticNode`"), + NodeType::None => panic!("`unwrap_nodemap()` on `None`"), + } + } + + #[inline(always)] + pub fn unwrap_staticnode(self) -> StaticNode { + match self { + NodeType::NodeMap(_) => panic!("`unwrap_staticnode()` on `NodeMap`"), + NodeType::StaticNode(node) => node, + NodeType::None => panic!("`unwrap_staticnode()` on `None`"), + } + } +} + +impl<'a, Id: IdType, L: IdType> NodeTrait for NodeType<'a, Id, L> { + #[inline(always)] + fn is_none(&self) -> bool { + match *self { + NodeType::None => true, + _ => false, + } + } + + #[inline(always)] + fn get_id(&self) -> Id { + match self { + NodeType::NodeMap(node) => node.get_id(), + NodeType::StaticNode(ref node) => node.get_id(), + NodeType::None => panic!("`get_id()` on `None`"), + } + } + + #[inline(always)] + fn get_label_id(&self) -> Option { + match self { + NodeType::NodeMap(node) => node.get_label_id(), + NodeType::StaticNode(ref node) => node.get_label_id(), + NodeType::None => None, + } + } +} diff --git a/src/graph_gen/general.rs b/src/graph_gen/general.rs index 912c5786..da8060c4 100644 --- a/src/graph_gen/general.rs +++ b/src/graph_gen/general.rs @@ -1,97 +1,97 @@ -/* - * Copyright (c) 2018 UNSW Sydney, Data and Knowledge Group. - * - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you 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. - */ -use std::hash::Hash; - -use rand::thread_rng; - -use generic::{GraphType, IdType, MutGraphTrait}; -use graph_impl::TypedGraphMap; -use map::SetMap; - -use graph_gen::helper::{complete_edge_pairs, random_edge_label, random_node_label}; - -pub fn empty_graph( - n: usize, - node_label: Vec, - edge_label: Vec, -) -> TypedGraphMap -where - Id: IdType, - NL: Hash + Eq + Clone, - EL: Hash + Eq + Clone, - Ty: GraphType, -{ - let mut rng = thread_rng(); - - let node_label_map = SetMap::from_vec(node_label); - let edge_label_map = SetMap::from_vec(edge_label); - - let mut g = TypedGraphMap::with_label_map(node_label_map, edge_label_map); - - for i in 0..n { - let label = random_node_label(&mut rng, &g); - g.add_node(Id::new(i), label); - } - - g -} - -pub fn complete_graph( - n: usize, - node_label: Vec, - edge_label: Vec, -) -> TypedGraphMap -where - Id: IdType, - NL: Hash + Eq + Clone, - EL: Hash + Eq + Clone, - Ty: GraphType, -{ - let mut rng = thread_rng(); - - let mut g = empty_graph::(n, node_label, edge_label); - for (s, d) in complete_edge_pairs::(n) { - let label = random_edge_label(&mut rng, &g); - g.add_edge(Id::new(s), Id::new(d), label); - } - - g -} - -pub fn empty_graph_unlabeled(n: usize) -> TypedGraphMap -where - Id: IdType, - NL: Hash + Eq + Clone, - EL: Hash + Eq + Clone, - Ty: GraphType, -{ - empty_graph(n, Vec::new(), Vec::new()) -} - -pub fn complete_graph_unlabeled(n: usize) -> TypedGraphMap -where - Id: IdType, - NL: Hash + Eq + Clone, - EL: Hash + Eq + Clone, - Ty: GraphType, -{ - complete_graph(n, Vec::new(), Vec::new()) -} +/* + * Copyright (c) 2018 UNSW Sydney, Data and Knowledge Group. + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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. + */ +use std::hash::Hash; + +use rand::thread_rng; + +use crate::generic::{GraphType, IdType, MutGraphTrait}; +use crate::graph_impl::TypedGraphMap; +use crate::map::SetMap; + +use crate::graph_gen::helper::{complete_edge_pairs, random_edge_label, random_node_label}; + +pub fn empty_graph( + n: usize, + node_label: Vec, + edge_label: Vec, +) -> TypedGraphMap +where + Id: IdType, + NL: Hash + Eq + Clone, + EL: Hash + Eq + Clone, + Ty: GraphType, +{ + let mut rng = thread_rng(); + + let node_label_map = SetMap::from_vec(node_label); + let edge_label_map = SetMap::from_vec(edge_label); + + let mut g = TypedGraphMap::with_label_map(node_label_map, edge_label_map); + + for i in 0..n { + let label = random_node_label(&mut rng, &g); + g.add_node(Id::new(i), label); + } + + g +} + +pub fn complete_graph( + n: usize, + node_label: Vec, + edge_label: Vec, +) -> TypedGraphMap +where + Id: IdType, + NL: Hash + Eq + Clone, + EL: Hash + Eq + Clone, + Ty: GraphType, +{ + let mut rng = thread_rng(); + + let mut g = empty_graph::(n, node_label, edge_label); + for (s, d) in complete_edge_pairs::(n) { + let label = random_edge_label(&mut rng, &g); + g.add_edge(Id::new(s), Id::new(d), label); + } + + g +} + +pub fn empty_graph_unlabeled(n: usize) -> TypedGraphMap +where + Id: IdType, + NL: Hash + Eq + Clone, + EL: Hash + Eq + Clone, + Ty: GraphType, +{ + empty_graph(n, Vec::new(), Vec::new()) +} + +pub fn complete_graph_unlabeled(n: usize) -> TypedGraphMap +where + Id: IdType, + NL: Hash + Eq + Clone, + EL: Hash + Eq + Clone, + Ty: GraphType, +{ + complete_graph(n, Vec::new(), Vec::new()) +} diff --git a/src/graph_gen/helper.rs b/src/graph_gen/helper.rs index 983f4229..5da30c05 100644 --- a/src/graph_gen/helper.rs +++ b/src/graph_gen/helper.rs @@ -1,84 +1,84 @@ -/* - * Copyright (c) 2018 UNSW Sydney, Data and Knowledge Group. - * - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you 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. - */ -use std::hash::Hash; - -use itertools::Itertools; -use rand::{Rng, ThreadRng}; - -use generic::{GraphLabelTrait, GraphType, IdType, Iter, MapTrait}; -use graph_impl::TypedGraphMap; - -pub fn complete_edge_pairs<'a, Ty>(n: usize) -> Iter<'a, (usize, usize)> -where - Ty: 'a + GraphType, -{ - if Ty::is_directed() { - Iter::new(Box::new( - (0..n) - .tuple_combinations() - .flat_map(|(s, d)| vec![(s, d), (d, s)]), - )) - } else { - Iter::new(Box::new((0..n).tuple_combinations())) - } -} - -pub fn random_node_label( - rng: &mut ThreadRng, - g: &TypedGraphMap, -) -> Option -where - Id: IdType, - NL: Hash + Eq + Clone, - EL: Hash + Eq + Clone, - Ty: GraphType, -{ - let labels = g.get_node_label_map(); - - if labels.is_empty() { - return None; - } - - let random_index = rng.gen_range(0, labels.len()); - - labels.get_item(random_index).cloned() -} - -pub fn random_edge_label( - rng: &mut ThreadRng, - g: &TypedGraphMap, -) -> Option -where - Id: IdType, - NL: Hash + Eq + Clone, - EL: Hash + Eq + Clone, - Ty: GraphType, -{ - let labels = g.get_edge_label_map(); - - if labels.is_empty() { - return None; - } - - let random_index = rng.gen_range(0, labels.len()); - - labels.get_item(random_index).cloned() -} +/* + * Copyright (c) 2018 UNSW Sydney, Data and Knowledge Group. + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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. + */ +use std::hash::Hash; + +use itertools::Itertools; +use rand::{Rng, ThreadRng}; + +use crate::generic::{GraphLabelTrait, GraphType, IdType, Iter, MapTrait}; +use crate::graph_impl::TypedGraphMap; + +pub fn complete_edge_pairs<'a, Ty>(n: usize) -> Iter<'a, (usize, usize)> +where + Ty: 'a + GraphType, +{ + if Ty::is_directed() { + Iter::new(Box::new( + (0..n) + .tuple_combinations() + .flat_map(|(s, d)| vec![(s, d), (d, s)]), + )) + } else { + Iter::new(Box::new((0..n).tuple_combinations())) + } +} + +pub fn random_node_label( + rng: &mut ThreadRng, + g: &TypedGraphMap, +) -> Option +where + Id: IdType, + NL: Hash + Eq + Clone, + EL: Hash + Eq + Clone, + Ty: GraphType, +{ + let labels = g.get_node_label_map(); + + if labels.is_empty() { + return None; + } + + let random_index = rng.gen_range(0, labels.len()); + + labels.get_item(random_index).cloned() +} + +pub fn random_edge_label( + rng: &mut ThreadRng, + g: &TypedGraphMap, +) -> Option +where + Id: IdType, + NL: Hash + Eq + Clone, + EL: Hash + Eq + Clone, + Ty: GraphType, +{ + let labels = g.get_edge_label_map(); + + if labels.is_empty() { + return None; + } + + let random_index = rng.gen_range(0, labels.len()); + + labels.get_item(random_index).cloned() +} diff --git a/src/graph_gen/mod.rs b/src/graph_gen/mod.rs index 21eb2005..6c745777 100644 --- a/src/graph_gen/mod.rs +++ b/src/graph_gen/mod.rs @@ -1,30 +1,30 @@ -/* - * Copyright (c) 2018 UNSW Sydney, Data and Knowledge Group. - * - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you 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. - */ -pub mod general; -pub mod helper; -pub mod random; - -pub use graph_gen::general::{ - complete_graph, complete_graph_unlabeled, empty_graph, empty_graph_unlabeled, -}; -pub use graph_gen::random::{ - random_gnm_graph, random_gnm_graph_unlabeled, random_gnp_graph, random_gnp_graph_unlabeled, -}; +/* + * Copyright (c) 2018 UNSW Sydney, Data and Knowledge Group. + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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. + */ +pub mod general; +pub mod helper; +pub mod random; + +pub use crate::graph_gen::general::{ + complete_graph, complete_graph_unlabeled, empty_graph, empty_graph_unlabeled, +}; +pub use crate::graph_gen::random::{ + random_gnm_graph, random_gnm_graph_unlabeled, random_gnp_graph, random_gnp_graph_unlabeled, +}; diff --git a/src/graph_gen/random.rs b/src/graph_gen/random.rs index 81422861..e97903f4 100644 --- a/src/graph_gen/random.rs +++ b/src/graph_gen/random.rs @@ -1,114 +1,114 @@ -/* - * Copyright (c) 2018 UNSW Sydney, Data and Knowledge Group. - * - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you 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. - */ -use std::hash::Hash; - -use rand::seq::sample_iter; -use rand::{thread_rng, Rng}; - -use generic::GraphType; -use generic::IdType; -use generic::MutGraphTrait; - -use graph_gen::general::empty_graph; -use graph_gen::helper::{complete_edge_pairs, random_edge_label}; -use graph_impl::TypedGraphMap; - -pub fn random_gnp_graph( - n: usize, - p: f32, - node_label: Vec, - edge_label: Vec, -) -> TypedGraphMap -where - Id: IdType, - NL: Hash + Eq + Clone, - EL: Hash + Eq + Clone, - Ty: GraphType, -{ - if p < 0f32 || p > 1f32 { - panic!("p must be in the range of [0,1]"); - } - - let mut rng = thread_rng(); - - let mut g = empty_graph::(n, node_label, edge_label); - - for (s, d) in complete_edge_pairs::(n) { - if rng.gen_range(0f32, 1f32) < p { - let label = random_edge_label(&mut rng, &g); - g.add_edge(Id::new(s), Id::new(d), label); - } - } - - g -} - -pub fn random_gnm_graph( - n: usize, - m: usize, - node_label: Vec, - edge_label: Vec, -) -> TypedGraphMap -where - Id: IdType, - NL: Hash + Eq + Clone, - EL: Hash + Eq + Clone, - Ty: GraphType, -{ - let mut rng = thread_rng(); - - let mut g = empty_graph::(n, node_label, edge_label); - let sampled_edges = sample_iter(&mut rng, complete_edge_pairs::(n), m); - - if let Ok(mut edges) = sampled_edges { - for (s, d) in edges.drain(..) { - let label = random_edge_label(&mut rng, &g); - g.add_edge(Id::new(s), Id::new(d), label); - } - - g - } else { - panic!("m is too large."); - } -} - -pub fn random_gnp_graph_unlabeled(n: usize, p: f32) -> TypedGraphMap -where - Id: IdType, - NL: Hash + Eq + Clone, - EL: Hash + Eq + Clone, - Ty: GraphType, -{ - random_gnp_graph(n, p, Vec::new(), Vec::new()) -} - -pub fn random_gnm_graph_unlabeled( - n: usize, - m: usize, -) -> TypedGraphMap -where - Id: IdType, - NL: Hash + Eq + Clone, - EL: Hash + Eq + Clone, - Ty: GraphType, -{ - random_gnm_graph(n, m, Vec::new(), Vec::new()) -} +/* + * Copyright (c) 2018 UNSW Sydney, Data and Knowledge Group. + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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. + */ +use std::hash::Hash; + +use rand::seq::sample_iter; +use rand::{thread_rng, Rng}; + +use crate::generic::GraphType; +use crate::generic::IdType; +use crate::generic::MutGraphTrait; + +use crate::graph_gen::general::empty_graph; +use crate::graph_gen::helper::{complete_edge_pairs, random_edge_label}; +use crate::graph_impl::TypedGraphMap; + +pub fn random_gnp_graph( + n: usize, + p: f32, + node_label: Vec, + edge_label: Vec, +) -> TypedGraphMap +where + Id: IdType, + NL: Hash + Eq + Clone, + EL: Hash + Eq + Clone, + Ty: GraphType, +{ + if p < 0f32 || p > 1f32 { + panic!("p must be in the range of [0,1]"); + } + + let mut rng = thread_rng(); + + let mut g = empty_graph::(n, node_label, edge_label); + + for (s, d) in complete_edge_pairs::(n) { + if rng.gen_range(0f32, 1f32) < p { + let label = random_edge_label(&mut rng, &g); + g.add_edge(Id::new(s), Id::new(d), label); + } + } + + g +} + +pub fn random_gnm_graph( + n: usize, + m: usize, + node_label: Vec, + edge_label: Vec, +) -> TypedGraphMap +where + Id: IdType, + NL: Hash + Eq + Clone, + EL: Hash + Eq + Clone, + Ty: GraphType, +{ + let mut rng = thread_rng(); + + let mut g = empty_graph::(n, node_label, edge_label); + let sampled_edges = sample_iter(&mut rng, complete_edge_pairs::(n), m); + + if let Ok(mut edges) = sampled_edges { + for (s, d) in edges.drain(..) { + let label = random_edge_label(&mut rng, &g); + g.add_edge(Id::new(s), Id::new(d), label); + } + + g + } else { + panic!("m is too large."); + } +} + +pub fn random_gnp_graph_unlabeled(n: usize, p: f32) -> TypedGraphMap +where + Id: IdType, + NL: Hash + Eq + Clone, + EL: Hash + Eq + Clone, + Ty: GraphType, +{ + random_gnp_graph(n, p, Vec::new(), Vec::new()) +} + +pub fn random_gnm_graph_unlabeled( + n: usize, + m: usize, +) -> TypedGraphMap +where + Id: IdType, + NL: Hash + Eq + Clone, + EL: Hash + Eq + Clone, + Ty: GraphType, +{ + random_gnm_graph(n, m, Vec::new(), Vec::new()) +} diff --git a/src/graph_impl/graph_map/edge.rs b/src/graph_impl/graph_map/edge.rs index 41d10bff..13e6f4ca 100644 --- a/src/graph_impl/graph_map/edge.rs +++ b/src/graph_impl/graph_map/edge.rs @@ -1,111 +1,111 @@ -/* - * Copyright (c) 2018 UNSW Sydney, Data and Knowledge Group. - * - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you 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. - */ -use generic::{EdgeTrait, IdType, MutEdgeTrait}; - -#[derive(Debug, PartialEq, Eq)] -pub struct MutEdge<'a, Id: IdType, L: IdType = Id> { - src: Id, - dst: Id, - label: &'a mut Option, -} - -impl<'a, Id: IdType, L: IdType> MutEdge<'a, Id, L> { - #[inline(always)] - pub fn new(start: Id, target: Id, label: &'a mut Option) -> Self { - MutEdge { - src: start, - dst: target, - label, - } - } -} - -impl<'a, Id: IdType, L: IdType> EdgeTrait for MutEdge<'a, Id, L> { - #[inline(always)] - fn get_start(&self) -> Id { - self.src - } - - #[inline(always)] - fn get_target(&self) -> Id { - self.dst - } - - #[inline(always)] - fn get_label_id(&self) -> Option { - *self.label - } -} - -impl<'a, Id: IdType, L: IdType> MutEdgeTrait for MutEdge<'a, Id, L> { - #[inline(always)] - fn set_label_id(&mut self, label: Option) { - *self.label = label; - } -} - -#[derive(Debug, PartialEq, Eq, Clone)] -pub struct Edge { - src: Id, - dst: Id, - label: Option, -} - -impl Edge { - #[inline(always)] - pub fn new(start: Id, target: Id, label: Option) -> Self { - Edge { - src: start, - dst: target, - label, - } - } - - #[inline(always)] - pub fn new_static(start: Id, target: Id, label: L) -> Self { - Edge { - src: start, - dst: target, - label: if label == L::max_value() { - None - } else { - Some(label) - }, - } - } -} - -impl EdgeTrait for Edge { - #[inline(always)] - fn get_start(&self) -> Id { - self.src - } - - #[inline(always)] - fn get_target(&self) -> Id { - self.dst - } - - #[inline(always)] - fn get_label_id(&self) -> Option { - self.label - } -} +/* + * Copyright (c) 2018 UNSW Sydney, Data and Knowledge Group. + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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. + */ +use crate::generic::{EdgeTrait, IdType, MutEdgeTrait}; + +#[derive(Debug, PartialEq, Eq)] +pub struct MutEdge<'a, Id: IdType, L: IdType = Id> { + src: Id, + dst: Id, + label: &'a mut Option, +} + +impl<'a, Id: IdType, L: IdType> MutEdge<'a, Id, L> { + #[inline(always)] + pub fn new(start: Id, target: Id, label: &'a mut Option) -> Self { + MutEdge { + src: start, + dst: target, + label, + } + } +} + +impl<'a, Id: IdType, L: IdType> EdgeTrait for MutEdge<'a, Id, L> { + #[inline(always)] + fn get_start(&self) -> Id { + self.src + } + + #[inline(always)] + fn get_target(&self) -> Id { + self.dst + } + + #[inline(always)] + fn get_label_id(&self) -> Option { + *self.label + } +} + +impl<'a, Id: IdType, L: IdType> MutEdgeTrait for MutEdge<'a, Id, L> { + #[inline(always)] + fn set_label_id(&mut self, label: Option) { + *self.label = label; + } +} + +#[derive(Debug, PartialEq, Eq, Clone)] +pub struct Edge { + src: Id, + dst: Id, + label: Option, +} + +impl Edge { + #[inline(always)] + pub fn new(start: Id, target: Id, label: Option) -> Self { + Edge { + src: start, + dst: target, + label, + } + } + + #[inline(always)] + pub fn new_static(start: Id, target: Id, label: L) -> Self { + Edge { + src: start, + dst: target, + label: if label == L::max_value() { + None + } else { + Some(label) + }, + } + } +} + +impl EdgeTrait for Edge { + #[inline(always)] + fn get_start(&self) -> Id { + self.src + } + + #[inline(always)] + fn get_target(&self) -> Id { + self.dst + } + + #[inline(always)] + fn get_label_id(&self) -> Option { + self.label + } +} diff --git a/src/graph_impl/graph_map/graph.rs b/src/graph_impl/graph_map/graph.rs index ff183676..2b14f59e 100644 --- a/src/graph_impl/graph_map/graph.rs +++ b/src/graph_impl/graph_map/graph.rs @@ -1,998 +1,1002 @@ -/* - * Copyright (c) 2018 UNSW Sydney, Data and Knowledge Group. - * - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you 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. - */ -use std::borrow::Cow; -use std::collections::{BTreeMap, BTreeSet, HashMap}; -use std::hash::{Hash, Hasher}; -use std::marker::PhantomData; -use std::mem; - -use fnv::{FnvBuildHasher, FnvHashMap}; -use itertools::Itertools; -use serde; - -use generic::{ - DefaultId, DefaultTy, DiGraphTrait, Directed, EdgeType, GeneralGraph, GraphLabelTrait, - GraphTrait, GraphType, IdType, Iter, MapTrait, MutEdgeType, MutGraphLabelTrait, MutGraphTrait, - MutMapTrait, MutNodeTrait, MutNodeType, NodeTrait, NodeType, OwnedEdgeType, OwnedNodeType, - UnGraphTrait, Undirected, -}; -use graph_impl::graph_map::{Edge, MutNodeMapTrait, NodeMap, NodeMapTrait}; -use graph_impl::{EdgeVec, GraphImpl, TypedStaticGraph}; -use io::serde::{Deserialize, Serialize}; -use map::SetMap; - -pub type TypedDiGraphMap = TypedGraphMap; -pub type TypedUnGraphMap = TypedGraphMap; -pub type GraphMap = TypedGraphMap; - -/// Shortcut of creating a new directed graph where `L` is the data type of labels. -/// # Example -/// ``` -/// use rust_graph::DiGraphMap; -/// let g = DiGraphMap::<&str>::new(); -/// ``` -pub type DiGraphMap = GraphMap; - -/// Shortcut of creating a new undirected graph where `L` is the data type of labels. -/// # Example -/// ``` -/// use rust_graph::UnGraphMap; -/// let g = UnGraphMap::<&str>::new(); -/// ``` -pub type UnGraphMap = GraphMap; - -pub fn new_general_graphmap<'a, Id: IdType, NL: Hash + Eq + 'a, EL: Hash + Eq + 'a, L: IdType>( - is_directed: bool, -) -> Box + 'a> { - if is_directed { - Box::new(TypedDiGraphMap::::new()) as Box + 'a> - } else { - Box::new(TypedUnGraphMap::::new()) as Box + 'a> - } -} - -/// A graph data structure that nodes and edges are stored in hash maps. -#[derive(Debug, Clone, Serialize, Deserialize)] -pub struct TypedGraphMap { - /// A map . - node_map: FnvHashMap>, - /// Num of edges. - num_of_edges: usize, - /// A map of node labels. - node_label_map: SetMap, - /// A map of edge labels. - edge_label_map: SetMap, - /// The maximum id has been seen until now. - max_id: Option, - /// A marker of thr graph type, namely, directed or undirected. - graph_type: PhantomData, -} - -impl PartialEq - for TypedGraphMap -{ - fn eq(&self, other: &TypedGraphMap) -> bool { - if !self.node_count() == other.node_count() || !self.edge_count() == other.edge_count() { - return false; - } - - for n in self.node_indices() { - if !other.has_node(n) || self.get_node_label(n) != other.get_node_label(n) { - return false; - } - } - - for (s, d) in self.edge_indices() { - if !other.has_edge(s, d) || self.get_edge_label(s, d) != other.get_edge_label(s, d) { - return false; - } - } - - true - } -} - -impl Eq - for TypedGraphMap -{} - -impl Hash - for TypedGraphMap -{ - fn hash(&self, state: &mut H) { - { - let nodes = self.node_indices().sorted(); - nodes.hash(state); - - let node_labels = nodes - .into_iter() - .map(|n| self.get_node_label(n)) - .collect_vec(); - node_labels.hash(state); - } - { - let edges = self.edge_indices().sorted(); - edges.hash(state); - let edge_labels = edges - .into_iter() - .map(|(s, d)| self.get_edge_label(s, d)) - .collect_vec(); - edge_labels.hash(state); - } - } -} - -impl Serialize - for TypedGraphMap -where - Id: serde::Serialize, - NL: serde::Serialize, - EL: serde::Serialize, - L: serde::Serialize, -{} - -impl Deserialize - for TypedGraphMap -where - Id: for<'de> serde::Deserialize<'de>, - NL: for<'de> serde::Deserialize<'de>, - EL: for<'de> serde::Deserialize<'de>, - L: for<'de> serde::Deserialize<'de>, -{} - -impl - TypedGraphMap -{ - /// Constructs a new graph. - pub fn new() -> Self { - TypedGraphMap { - node_map: FnvHashMap::>::default(), - num_of_edges: 0, - node_label_map: SetMap::new(), - edge_label_map: SetMap::new(), - max_id: None, - graph_type: PhantomData, - } - } - - pub fn with_capacity(nodes: usize, node_labels: usize, edge_labels: usize) -> Self { - TypedGraphMap { - node_map: HashMap::with_capacity_and_hasher(nodes, FnvBuildHasher::default()), - num_of_edges: 0, - node_label_map: SetMap::with_capacity(node_labels), - edge_label_map: SetMap::with_capacity(edge_labels), - max_id: None, - graph_type: PhantomData, - } - } - - pub fn shrink_to_fit(&mut self) { - self.node_map.shrink_to_fit(); - } - - /// Constructs a new graph using existing label-id mapping. - /// # Example - /// ``` - /// use rust_graph::prelude::*; - /// use rust_graph::UnGraphMap; - /// - /// let mut g = UnGraphMap::<&str>::new(); - /// g.add_node(0, Some("a")); - /// g.add_node(1, Some("b")); - /// g.add_edge(0, 1, None); - /// - /// let mut p = UnGraphMap::with_label_map(g.get_node_label_map().clone(), - /// g.get_edge_label_map().clone()); - /// p.add_node(1, Some("b")); - /// p.add_node(0, Some("a")); - /// p.add_edge(0, 1, None); - /// - /// assert_eq!(g.get_node(0).get_label_id(), p.get_node(0).get_label_id()); - /// assert_eq!(g.get_node(1).get_label_id(), p.get_node(1).get_label_id()); - /// - /// ``` - pub fn with_label_map(node_label_map: SetMap, edge_label_map: SetMap) -> Self { - TypedGraphMap { - node_map: FnvHashMap::default(), - num_of_edges: 0, - node_label_map, - edge_label_map, - max_id: None, - graph_type: PhantomData, - } - } - - pub fn from_edges>(edges: I) -> Self { - let mut g = TypedGraphMap::new(); - for (src, dst) in edges { - g.add_node(src, None); - g.add_node(dst, None); - g.add_edge(src, dst, None); - } - - g - } - - pub fn add_node_label(&mut self, label: Option) -> Option { - label.map(|l| L::new(self.node_label_map.add_item(l))) - } - - pub fn add_edge_label(&mut self, label: Option) -> Option { - label.map(|l| L::new(self.edge_label_map.add_item(l))) - } - - /// Re-compute the number of edges - pub fn refine_edge_count(&mut self) { - let count = self.edge_indices().count(); - self.num_of_edges = count; - } -} - -impl Default - for TypedGraphMap -{ - fn default() -> Self { - Self::new() - } -} - -impl - MutGraphTrait for TypedGraphMap -{ - /// Add a node with `id` and `label`. If the node of the `id` already presents, - /// replace the node's label with the new `label` and return `false`. - /// Otherwise, add the node and return `true`. - #[inline] - fn add_node(&mut self, id: Id, label: Option) -> bool { - let label_id = label.map(|x| L::new(self.node_label_map.add_item(x))); - - if self.has_node(id) { - // Node already exist, updating its label. - - let nodemap = self.node_map.get_mut(&id).unwrap(); - nodemap.set_label_id(label_id); - - return false; - } - - let new_node = NodeMap::new(id, label_id); - self.node_map.insert(id, new_node); - match self.max_id { - Some(i) => { - if i < id { - self.max_id = Some(id) - } - } - None => self.max_id = Some(id), - } - - true - } - - #[inline] - fn get_node_mut(&mut self, id: Id) -> MutNodeType { - match self.node_map.get_mut(&id) { - Some(node) => MutNodeType::NodeMapRef(node), - None => MutNodeType::None, - } - } - - #[inline] - fn remove_node(&mut self, id: Id) -> OwnedNodeType { - match self.node_map.remove(&id) { - Some(node) => { - if self.is_directed() { - for neighbor in node.neighbors_iter() { - let nodemap = self.node_map.get_mut(&neighbor).unwrap(); - nodemap.remove_in_edge(id); - } - for in_neighbor in node.in_neighbors_iter() { - let nodemap = self.node_map.get_mut(&in_neighbor).unwrap(); - nodemap.remove_edge(id); - } - } else { - for neighbor in node.neighbors_iter() { - let nodemap = self.node_map.get_mut(&neighbor).unwrap(); - nodemap.remove_edge(id); - } - } - - self.num_of_edges -= node.degree() + node.in_degree(); - - OwnedNodeType::NodeMap(node) - } - None => OwnedNodeType::None, - } - } - - /// Add the edge with given `start` and `target` vertices. - /// If either end does not exist, add a new node with corresponding id - /// and `None` label. If the edge already presents, return `false`, - /// otherwise add the new edge and return `true`. - #[inline] - fn add_edge(&mut self, start: Id, target: Id, label: Option) -> bool { - if !self.has_node(start) { - self.add_node(start, None); - } - if !self.has_node(target) { - self.add_node(target, None); - } - - let label_id = label.map(|x| L::new(self.edge_label_map.add_item(x))); - - let result; - - { - let nodemap = self.node_map.get_mut(&start).unwrap(); - result = nodemap.add_edge(target, label_id); - } - - if self.is_directed() { - let nodemap = self.node_map.get_mut(&target).unwrap(); - nodemap.add_in_edge(start); - } else if start != target { - let nodemap = self.node_map.get_mut(&target).unwrap(); - nodemap.add_edge(start, label_id); - } - - self.num_of_edges += 1; - - result - } - - #[inline] - fn get_edge_mut(&mut self, start: Id, target: Id) -> MutEdgeType { - if !self.has_edge(start, target) { - return MutEdgeType::None; - } - - let nodemap = self.node_map.get_mut(&start).unwrap(); - nodemap.get_neighbor_mut(target) - } - - #[inline] - fn remove_edge(&mut self, start: Id, target: Id) -> OwnedEdgeType { - if !self.has_edge(start, target) { - return OwnedEdgeType::None; - } - - let edge; - - { - let nodemap = self.node_map.get_mut(&start).unwrap(); - edge = nodemap.remove_edge(target); - } - - if self.is_directed() { - let nodemap = self.node_map.get_mut(&target).unwrap(); - nodemap.remove_in_edge(start); - } else { - let nodemap = self.node_map.get_mut(&target).unwrap(); - nodemap.remove_edge(start); - } - - self.num_of_edges -= 1; - - edge - } - - #[inline] - fn nodes_mut(&mut self) -> Iter> { - Iter::new(Box::new( - self.node_map.values_mut().map(MutNodeType::NodeMapRef), - )) - } - - #[inline] - fn edges_mut(&mut self) -> Iter> { - if self.is_directed() { - Iter::new(Box::new( - self.node_map - .values_mut() - .flat_map(|n| n.neighbors_iter_mut()), - )) - } else { - Iter::new(Box::new( - self.node_map - .values_mut() - .flat_map(|n| n.non_less_neighbors_iter_mut()), - )) - } - } -} - -impl GraphTrait - for TypedGraphMap -{ - #[inline] - fn get_node(&self, id: Id) -> NodeType { - match self.node_map.get(&id) { - Some(node) => NodeType::NodeMap(node), - None => NodeType::None, - } - } - - #[inline] - fn get_edge(&self, start: Id, target: Id) -> EdgeType { - if !self.has_edge(start, target) { - return EdgeType::None; - } - - let nodemap = self.node_map.get(&start).unwrap(); - let label_id = nodemap.get_neighbor(target).unwrap(); - - EdgeType::Edge(Edge::new(start, target, label_id)) - } - - #[inline] - fn has_node(&self, id: Id) -> bool { - self.node_map.contains_key(&id) - } - - #[inline] - fn has_edge(&self, start: Id, target: Id) -> bool { - match self.get_node(start) { - NodeType::NodeMap(node) => node.has_neighbor(target), - _ => false, - } - } - - #[inline] - fn node_count(&self) -> usize { - self.node_map.len() - } - - #[inline] - fn edge_count(&self) -> usize { - self.num_of_edges - } - - #[inline(always)] - fn is_directed(&self) -> bool { - Ty::is_directed() - } - - #[inline] - fn node_indices(&self) -> Iter { - Iter::new(Box::new(self.node_map.keys().map(|x| *x))) - } - - #[inline] - fn edge_indices(&self) -> Iter<(Id, Id)> { - if self.is_directed() { - Iter::new(Box::new( - self.node_map - .values() - .flat_map(|n| n.neighbors_iter().map(move |i| (n.get_id(), i))), - )) - } else { - Iter::new(Box::new(self.node_map.values().flat_map(|n| { - n.non_less_neighbors_iter().map(move |i| (n.get_id(), i)) - }))) - } - } - - #[inline] - fn nodes(&self) -> Iter> { - Iter::new(Box::new(self.node_map.values().map(NodeType::NodeMap))) - } - - #[inline] - fn edges(&self) -> Iter> { - if self.is_directed() { - Iter::new(Box::new( - self.node_map - .values() - .flat_map(|n| n.neighbors_iter_full()) - .map(EdgeType::Edge), - )) - } else { - Iter::new(Box::new( - self.node_map - .values() - .flat_map(|n| n.non_less_neighbors_iter_full()) - .map(EdgeType::Edge), - )) - } - } - - #[inline] - fn degree(&self, id: Id) -> usize { - match self.get_node(id) { - NodeType::NodeMap(node) => node.degree(), - NodeType::None => panic!("Node {:?} do not exist.", id), - _ => panic!("Unknown error."), - } - } - - #[inline] - fn neighbors_iter(&self, id: Id) -> Iter { - match self.get_node(id) { - NodeType::NodeMap(node) => node.neighbors_iter(), - NodeType::None => panic!("Node {:?} do not exist.", id), - _ => panic!("Unknown error."), - } - } - - #[inline] - fn neighbors(&self, id: Id) -> Cow<[Id]> { - match self.get_node(id) { - NodeType::NodeMap(node) => node.neighbors().into(), - NodeType::None => panic!("Node {:?} do not exist.", id), - _ => panic!("Unknown error."), - } - } - - #[inline] - fn max_seen_id(&self) -> Option { - self.max_id - } - - #[inline(always)] - fn implementation(&self) -> GraphImpl { - GraphImpl::GraphMap - } -} - -impl - GraphLabelTrait for TypedGraphMap -{ - #[inline(always)] - fn get_node_label_map(&self) -> &SetMap { - &self.node_label_map - } - - #[inline(always)] - fn get_edge_label_map(&self) -> &SetMap { - &self.edge_label_map - } -} - -impl - MutGraphLabelTrait for TypedGraphMap -{ - #[inline] - fn update_node_label(&mut self, node_id: Id, label: Option) -> bool { - if !self.has_node(node_id) { - return false; - } - - self.add_node(node_id, label); - - true - } - - #[inline] - fn update_edge_label(&mut self, start: Id, target: Id, label: Option) -> bool { - if !self.has_edge(start, target) { - return false; - } - - self.add_edge(start, target, label); - - true - } -} - -impl UnGraphTrait - for TypedUnGraphMap -{} - -impl DiGraphTrait - for TypedDiGraphMap -{ - #[inline] - fn in_degree(&self, id: Id) -> usize { - match self.get_node(id) { - NodeType::NodeMap(node) => node.in_degree(), - NodeType::None => panic!("Node {:?} do not exist.", id), - _ => panic!("Unknown error."), - } - } - - #[inline] - fn in_neighbors_iter(&self, id: Id) -> Iter { - match self.get_node(id) { - NodeType::NodeMap(ref node) => node.in_neighbors_iter(), - NodeType::None => panic!("Node {:?} do not exist.", id), - _ => panic!("Unknown error."), - } - } - - #[inline] - fn in_neighbors(&self, id: Id) -> Cow<[Id]> { - match self.get_node(id) { - NodeType::NodeMap(ref node) => node.in_neighbors().into(), - NodeType::None => panic!("Node {:?} do not exist.", id), - _ => panic!("Unknown error."), - } - } -} - -impl GeneralGraph - for TypedUnGraphMap -{ - #[inline(always)] - fn as_graph(&self) -> &GraphTrait { - self - } - - #[inline(always)] - fn as_labeled_graph(&self) -> &GraphLabelTrait { - self - } - - #[inline(always)] - fn as_general_graph(&self) -> &GeneralGraph { - self - } - - #[inline(always)] - fn as_mut_graph(&mut self) -> Option<&mut MutGraphTrait> { - Some(self) - } -} - -impl GeneralGraph - for TypedDiGraphMap -{ - #[inline(always)] - fn as_graph(&self) -> &GraphTrait { - self - } - - #[inline(always)] - fn as_labeled_graph(&self) -> &GraphLabelTrait { - self - } - - #[inline(always)] - fn as_general_graph(&self) -> &GeneralGraph { - self - } - - #[inline(always)] - fn as_digraph(&self) -> Option<&DiGraphTrait> { - Some(self) - } - - #[inline(always)] - fn as_mut_graph(&mut self) -> Option<&mut MutGraphTrait> { - Some(self) - } -} - -impl - TypedGraphMap -{ - pub fn reorder_id( - self, - reorder_node_id: bool, - reorder_node_label: bool, - reorder_edge_label: bool, - ) -> ReorderResult { - let node_id_map: Option> = if reorder_node_id { - Some( - self.node_map - .values() - .map(|n| (n.get_id(), n.degree() + n.in_degree())) - .sorted_by_key(|&(_, d)| d) - .into_iter() - .map(|(n, _)| n) - .collect(), - ) - } else { - None - }; - - let node_label_map: Option> = if reorder_node_label { - Some( - self.get_node_label_id_counter() - .most_common() - .into_iter() - .rev() - .skip_while(|(_, f)| *f == 0) - .map(|(n, _)| n) - .collect(), - ) - } else { - None - }; - - let edge_label_map: Option> = if reorder_edge_label { - Some( - self.get_edge_label_id_counter() - .most_common() - .into_iter() - .rev() - .skip_while(|(_, f)| *f == 0) - .map(|(n, _)| n) - .collect(), - ) - } else { - None - }; - - let graph = Some(self.reorder_id_with(&node_id_map, &node_label_map, &edge_label_map)); - - ReorderResult { node_id_map, graph } - } - - pub fn reorder_id_with( - self, - node_id_map: &Option>, - node_label_map: &Option>, - edge_label_map: &Option>, - ) -> Self { - if node_id_map.is_none() && node_label_map.is_none() && edge_label_map.is_none() { - return self; - } - - let num_of_edges = self.edge_count(); - - let mut new_node_map = FnvHashMap::default(); - - for (_, node) in self.node_map { - let new_node_id = if let Some(ref map) = node_id_map { - Id::new(map.find_index(&node.id).unwrap()) - } else { - node.id - }; - - let new_node_label = if let Some(ref map) = node_label_map { - node.label.map(|i| L::new(map.find_index(&i).unwrap())) - } else { - node.label - }; - - let new_neighbors = if node_id_map.is_some() || edge_label_map.is_some() { - node.neighbors - .into_iter() - .map(|(n, l)| { - let new_n = if let Some(ref map) = node_id_map { - Id::new(map.find_index(&n).unwrap()) - } else { - n - }; - - let new_l = l.map(|i| { - if let Some(ref map) = edge_label_map { - L::new(map.find_index(&i).unwrap()) - } else { - i - } - }); - - (new_n, new_l) - }).collect() - } else { - node.neighbors - }; - - let new_in_neighbors = if let Some(ref map) = node_id_map { - node.in_neighbors - .into_iter() - .map(|n| Id::new(map.find_index(&n).unwrap())) - .collect() - } else { - node.in_neighbors - }; - - let new_node = NodeMap { - id: new_node_id, - label: new_node_label, - neighbors: new_neighbors, - in_neighbors: new_in_neighbors, - }; - - new_node_map.insert(new_node_id, new_node); - } - - new_node_map.shrink_to_fit(); - - let new_node_label_map = if let Some(ref map) = node_label_map { - reorder_label_map(map, self.node_label_map) - } else { - self.node_label_map - }; - - let new_edge_label_map = if let Some(ref map) = edge_label_map { - reorder_label_map(map, self.edge_label_map) - } else { - self.edge_label_map - }; - - let new_max_id = new_node_map.keys().max().map(|i| *i); - - TypedGraphMap { - node_map: new_node_map, - num_of_edges, - edge_label_map: new_edge_label_map, - node_label_map: new_node_label_map, - max_id: new_max_id, - graph_type: PhantomData, - } - } - - pub fn into_static(mut self) -> TypedStaticGraph { - let num_of_nodes = self.node_count(); - let num_of_edges = self.edge_count(); - - let mut offset = 0usize; - let mut offset_vec = Vec::new(); - let mut edge_vec = Vec::new(); - let mut edge_labels = if self.has_edge_labels() { - Some(Vec::new()) - } else { - None - }; - - let mut node_labels = if self.has_node_labels() { - Some(Vec::new()) - } else { - None - }; - - let (mut in_offset, mut in_offset_vec, mut in_edge_vec) = if self.is_directed() { - (Some(0usize), Some(Vec::new()), Some(Vec::new())) - } else { - (None, None, None) - }; - - let mut nid = Id::new(0); - let max_nid = self.node_indices().max().unwrap(); - - offset_vec.push(offset); - - if let (Some(_in_offset), Some(_in_offset_vec)) = (in_offset, in_offset_vec.as_mut()) { - _in_offset_vec.push(_in_offset); - } - - while nid <= max_nid { - if let Some(mut node) = self.node_map.remove(&nid) { - let neighbors = mem::replace(&mut node.neighbors, BTreeMap::new()); - offset += neighbors.len(); - - if let Some(ref mut _edge_labels) = edge_labels { - for (n, l) in neighbors { - edge_vec.push(n); - _edge_labels.push(match l { - Some(_l) => _l, - None => L::max_value(), - }); - } - } else { - edge_vec.extend(neighbors.keys()); - } - - if let (Some(_in_offset), Some(_in_edge_vec)) = - (in_offset.as_mut(), in_edge_vec.as_mut()) - { - let in_neighbors = mem::replace(&mut node.in_neighbors, BTreeSet::new()); - - *_in_offset += in_neighbors.len(); - _in_edge_vec.extend(in_neighbors); - } - - if let Some(ref mut _node_labels) = node_labels { - match node.label { - Some(label) => _node_labels.push(label), - None => _node_labels.push(L::max_value()), - } - } - } else if let Some(ref mut _node_labels) = node_labels { - _node_labels.push(L::max_value()); - } - - offset_vec.push(offset); - - if let (Some(_in_offset), Some(_in_offset_vec)) = (in_offset, in_offset_vec.as_mut()) { - _in_offset_vec.push(_in_offset); - } - - nid = nid.increment(); - - //shrink the map to save memory - self.shrink_to_fit(); - } - - let mut edge_vec = EdgeVec::from_raw(offset_vec, edge_vec, edge_labels); - edge_vec.shrink_to_fit(); - - let in_edge_vec = - if let (Some(_in_offset_vec), Some(_in_edge_vec)) = (in_offset_vec, in_edge_vec) { - let mut _edge = EdgeVec::new(_in_offset_vec, _in_edge_vec); - _edge.shrink_to_fit(); - Some(_edge) - } else { - None - }; - - if let Some(ref mut _labels) = node_labels { - _labels.shrink_to_fit(); - } - - let node_label_map = self.node_label_map; - let edge_label_map = self.edge_label_map; - - TypedStaticGraph::from_raw( - num_of_nodes, - num_of_edges, - edge_vec, - in_edge_vec, - node_labels, - node_label_map, - edge_label_map, - ) - } -} - -fn reorder_label_map(new_map: &impl MapTrait, old_map: impl MapTrait) -> SetMap -where - Id: IdType, - L: Hash + Eq, -{ - let mut old_map_vec: Vec<_> = old_map.items_vec().into_iter().map(|i| Some(i)).collect(); - let mut result = SetMap::new(); - - for i in new_map.items() { - let l = old_map_vec[i.id()].take().unwrap(); - result.add_item(l); - } - - result -} - -#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] -pub struct ReorderResult { - node_id_map: Option>, - graph: Option>, -} - -impl - ReorderResult -{ - #[inline] - pub fn take_graph(&mut self) -> Option> { - self.graph.take() - } - - #[inline] - pub fn get_node_id_map(&self) -> Option<&SetMap> { - self.node_id_map.as_ref() - } - - #[inline] - pub fn get_original_node_id(&self, id: Id) -> Id { - match self.get_node_id_map() { - Some(map) => *map.get_item(id.id()).unwrap(), - None => id, - } - } - - #[inline] - pub fn find_new_node_id(&self, id: Id) -> Id { - match self.get_node_id_map() { - Some(map) => Id::new(map.find_index(&id).unwrap()), - None => id, - } - } -} +/* + * Copyright (c) 2018 UNSW Sydney, Data and Knowledge Group. + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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. + */ +use std::borrow::Cow; +use std::collections::{BTreeMap, BTreeSet}; +use std::hash::{Hash, Hasher}; +use std::marker::PhantomData; +use std::mem; + +use hashbrown::HashMap; +use itertools::Itertools; +use serde; + +use crate::generic::{ + DefaultId, DefaultTy, DiGraphTrait, Directed, EdgeType, GeneralGraph, GraphLabelTrait, + GraphTrait, GraphType, IdType, Iter, MapTrait, MutEdgeType, MutGraphLabelTrait, MutGraphTrait, + MutMapTrait, MutNodeTrait, MutNodeType, NodeTrait, NodeType, OwnedEdgeType, OwnedNodeType, + UnGraphTrait, Undirected, +}; +use crate::graph_impl::graph_map::{Edge, MutNodeMapTrait, NodeMap, NodeMapTrait}; +use crate::graph_impl::{EdgeVec, GraphImpl, TypedStaticGraph}; +use crate::io::serde::{Deserialize, Serialize}; +use crate::map::SetMap; + +pub type TypedDiGraphMap = TypedGraphMap; +pub type TypedUnGraphMap = TypedGraphMap; +pub type GraphMap = TypedGraphMap; + +/// Shortcut of creating a new directed graph where `L` is the data type of labels. +/// # Example +/// ``` +/// use rust_graph::DiGraphMap; +/// let g = DiGraphMap::<&str>::new(); +/// ``` +pub type DiGraphMap = GraphMap; + +/// Shortcut of creating a new undirected graph where `L` is the data type of labels. +/// # Example +/// ``` +/// use rust_graph::UnGraphMap; +/// let g = UnGraphMap::<&str>::new(); +/// ``` +pub type UnGraphMap = GraphMap; + +pub fn new_general_graphmap<'a, Id: IdType, NL: Hash + Eq + 'a, EL: Hash + Eq + 'a, L: IdType>( + is_directed: bool, +) -> Box + 'a> { + if is_directed { + Box::new(TypedDiGraphMap::::new()) + as Box + 'a> + } else { + Box::new(TypedUnGraphMap::::new()) + as Box + 'a> + } +} + +/// A graph data structure that nodes and edges are stored in hash maps. +#[derive(Debug, Clone, Serialize, Deserialize)] +pub struct TypedGraphMap { + /// A map . + node_map: HashMap>, + /// Num of edges. + num_of_edges: usize, + /// A map of node labels. + node_label_map: SetMap, + /// A map of edge labels. + edge_label_map: SetMap, + /// The maximum id has been seen until now. + max_id: Option, + /// A marker of thr graph type, namely, directed or undirected. + graph_type: PhantomData, +} + +impl PartialEq + for TypedGraphMap +{ + fn eq(&self, other: &TypedGraphMap) -> bool { + if !self.node_count() == other.node_count() || !self.edge_count() == other.edge_count() { + return false; + } + + for n in self.node_indices() { + if !other.has_node(n) || self.get_node_label(n) != other.get_node_label(n) { + return false; + } + } + + for (s, d) in self.edge_indices() { + if !other.has_edge(s, d) || self.get_edge_label(s, d) != other.get_edge_label(s, d) { + return false; + } + } + + true + } +} + +impl Eq + for TypedGraphMap +{ +} + +impl Hash + for TypedGraphMap +{ + fn hash(&self, state: &mut H) { + { + let nodes = self.node_indices().sorted(); + nodes.hash(state); + + let node_labels = nodes + .into_iter() + .map(|n| self.get_node_label(n)) + .collect_vec(); + node_labels.hash(state); + } + { + let edges = self.edge_indices().sorted(); + edges.hash(state); + let edge_labels = edges + .into_iter() + .map(|(s, d)| self.get_edge_label(s, d)) + .collect_vec(); + edge_labels.hash(state); + } + } +} + +impl Serialize + for TypedGraphMap +where + Id: serde::Serialize, + NL: serde::Serialize, + EL: serde::Serialize, + L: serde::Serialize, +{ +} + +impl Deserialize + for TypedGraphMap +where + Id: for<'de> serde::Deserialize<'de>, + NL: for<'de> serde::Deserialize<'de>, + EL: for<'de> serde::Deserialize<'de>, + L: for<'de> serde::Deserialize<'de>, +{ +} + +impl + TypedGraphMap +{ + /// Constructs a new graph. + pub fn new() -> Self { + TypedGraphMap { + node_map: HashMap::new(), + num_of_edges: 0, + node_label_map: SetMap::new(), + edge_label_map: SetMap::new(), + max_id: None, + graph_type: PhantomData, + } + } + + pub fn with_capacity(nodes: usize, node_labels: usize, edge_labels: usize) -> Self { + TypedGraphMap { + node_map: HashMap::with_capacity(nodes), + num_of_edges: 0, + node_label_map: SetMap::with_capacity(node_labels), + edge_label_map: SetMap::with_capacity(edge_labels), + max_id: None, + graph_type: PhantomData, + } + } + + pub fn shrink_to_fit(&mut self) { + self.node_map.shrink_to_fit(); + } + + /// Constructs a new graph using existing label-id mapping. + /// # Example + /// ``` + /// use rust_graph::prelude::*; + /// use rust_graph::UnGraphMap; + /// + /// let mut g = UnGraphMap::<&str>::new(); + /// g.add_node(0, Some("a")); + /// g.add_node(1, Some("b")); + /// g.add_edge(0, 1, None); + /// + /// let mut p = UnGraphMap::with_label_map(g.get_node_label_map().clone(), + /// g.get_edge_label_map().clone()); + /// p.add_node(1, Some("b")); + /// p.add_node(0, Some("a")); + /// p.add_edge(0, 1, None); + /// + /// assert_eq!(g.get_node(0).get_label_id(), p.get_node(0).get_label_id()); + /// assert_eq!(g.get_node(1).get_label_id(), p.get_node(1).get_label_id()); + /// + /// ``` + pub fn with_label_map(node_label_map: SetMap, edge_label_map: SetMap) -> Self { + TypedGraphMap { + node_map: HashMap::new(), + num_of_edges: 0, + node_label_map, + edge_label_map, + max_id: None, + graph_type: PhantomData, + } + } + + pub fn add_node_label(&mut self, label: Option) -> Option { + label.map(|l| L::new(self.node_label_map.add_item(l))) + } + + pub fn add_edge_label(&mut self, label: Option) -> Option { + label.map(|l| L::new(self.edge_label_map.add_item(l))) + } + + /// Re-compute the number of edges + pub fn refine_edge_count(&mut self) { + let count = self.edge_indices().count(); + self.num_of_edges = count; + } +} + +impl Default + for TypedGraphMap +{ + fn default() -> Self { + Self::new() + } +} + +impl + MutGraphTrait for TypedGraphMap +{ + /// Add a node with `id` and `label`. If the node of the `id` already presents, + /// replace the node's label with the new `label` and return `false`. + /// Otherwise, add the node and return `true`. + #[inline] + fn add_node(&mut self, id: Id, label: Option) -> bool { + let label_id = label.map(|x| L::new(self.node_label_map.add_item(x))); + + if self.has_node(id) { + // Node already exist, updating its label. + + let nodemap = self.node_map.get_mut(&id).unwrap(); + nodemap.set_label_id(label_id); + + return false; + } + + let new_node = NodeMap::new(id, label_id); + self.node_map.insert(id, new_node); + match self.max_id { + Some(i) => { + if i < id { + self.max_id = Some(id) + } + } + None => self.max_id = Some(id), + } + + true + } + + #[inline] + fn get_node_mut(&mut self, id: Id) -> MutNodeType { + match self.node_map.get_mut(&id) { + Some(node) => MutNodeType::NodeMapRef(node), + None => MutNodeType::None, + } + } + + #[inline] + fn remove_node(&mut self, id: Id) -> OwnedNodeType { + match self.node_map.remove(&id) { + Some(node) => { + if self.is_directed() { + for neighbor in node.neighbors_iter() { + let nodemap = self.node_map.get_mut(&neighbor).unwrap(); + nodemap.remove_in_edge(id); + } + for in_neighbor in node.in_neighbors_iter() { + let nodemap = self.node_map.get_mut(&in_neighbor).unwrap(); + nodemap.remove_edge(id); + } + } else { + for neighbor in node.neighbors_iter() { + let nodemap = self.node_map.get_mut(&neighbor).unwrap(); + nodemap.remove_edge(id); + } + } + + self.num_of_edges -= node.degree() + node.in_degree(); + + OwnedNodeType::NodeMap(node) + } + None => OwnedNodeType::None, + } + } + + /// Add the edge with given `start` and `target` vertices. + /// If either end does not exist, add a new node with corresponding id + /// and `None` label. If the edge already presents, return `false`, + /// otherwise add the new edge and return `true`. + #[inline] + fn add_edge(&mut self, start: Id, target: Id, label: Option) -> bool { + if !self.has_node(start) { + self.add_node(start, None); + } + if !self.has_node(target) { + self.add_node(target, None); + } + + if !self.has_edge(start, target) { + self.num_of_edges += 1; + } + + let label_id = label.map(|x| L::new(self.edge_label_map.add_item(x))); + + let result; + + { + let nodemap = self.node_map.get_mut(&start).unwrap(); + result = nodemap.add_edge(target, label_id); + } + + if self.is_directed() { + let nodemap = self.node_map.get_mut(&target).unwrap(); + nodemap.add_in_edge(start); + } else if start != target { + let nodemap = self.node_map.get_mut(&target).unwrap(); + nodemap.add_edge(start, label_id); + } + + result + } + + #[inline] + fn get_edge_mut(&mut self, start: Id, target: Id) -> MutEdgeType { + if !self.has_edge(start, target) { + return MutEdgeType::None; + } + + let nodemap = self.node_map.get_mut(&start).unwrap(); + nodemap.get_neighbor_mut(target) + } + + #[inline] + fn remove_edge(&mut self, start: Id, target: Id) -> OwnedEdgeType { + if !self.has_edge(start, target) { + return OwnedEdgeType::None; + } + + let edge; + + { + let nodemap = self.node_map.get_mut(&start).unwrap(); + edge = nodemap.remove_edge(target); + } + + if self.is_directed() { + let nodemap = self.node_map.get_mut(&target).unwrap(); + nodemap.remove_in_edge(start); + } else { + let nodemap = self.node_map.get_mut(&target).unwrap(); + nodemap.remove_edge(start); + } + + self.num_of_edges -= 1; + + edge + } + + #[inline] + fn nodes_mut(&mut self) -> Iter> { + Iter::new(Box::new( + self.node_map.values_mut().map(MutNodeType::NodeMapRef), + )) + } + + #[inline] + fn edges_mut(&mut self) -> Iter> { + if self.is_directed() { + Iter::new(Box::new( + self.node_map + .values_mut() + .flat_map(|n| n.neighbors_iter_mut()), + )) + } else { + Iter::new(Box::new( + self.node_map + .values_mut() + .flat_map(|n| n.non_less_neighbors_iter_mut()), + )) + } + } +} + +impl GraphTrait + for TypedGraphMap +{ + #[inline] + fn get_node(&self, id: Id) -> NodeType { + match self.node_map.get(&id) { + Some(node) => NodeType::NodeMap(node), + None => NodeType::None, + } + } + + #[inline] + fn get_edge(&self, start: Id, target: Id) -> EdgeType { + if !self.has_edge(start, target) { + return EdgeType::None; + } + + let nodemap = self.node_map.get(&start).unwrap(); + let label_id = nodemap.get_neighbor(target).unwrap(); + + EdgeType::Edge(Edge::new(start, target, label_id)) + } + + #[inline] + fn has_node(&self, id: Id) -> bool { + self.node_map.contains_key(&id) + } + + #[inline] + fn has_edge(&self, start: Id, target: Id) -> bool { + match self.node_map.get(&start) { + Some(node) => node.has_neighbor(target), + None => false, + } + } + + #[inline] + fn node_count(&self) -> usize { + self.node_map.len() + } + + #[inline] + fn edge_count(&self) -> usize { + self.num_of_edges + } + + #[inline(always)] + fn is_directed(&self) -> bool { + Ty::is_directed() + } + + #[inline] + fn node_indices(&self) -> Iter { + Iter::new(Box::new(self.node_map.keys().map(|x| *x))) + } + + #[inline] + fn edge_indices(&self) -> Iter<(Id, Id)> { + if self.is_directed() { + Iter::new(Box::new( + self.node_map + .values() + .flat_map(|n| n.neighbors_iter().map(move |i| (n.get_id(), i))), + )) + } else { + Iter::new(Box::new(self.node_map.values().flat_map(|n| { + n.non_less_neighbors_iter().map(move |i| (n.get_id(), i)) + }))) + } + } + + #[inline] + fn nodes(&self) -> Iter> { + Iter::new(Box::new(self.node_map.values().map(NodeType::NodeMap))) + } + + #[inline] + fn edges(&self) -> Iter> { + if self.is_directed() { + Iter::new(Box::new( + self.node_map + .values() + .flat_map(|n| n.neighbors_iter_full()) + .map(EdgeType::Edge), + )) + } else { + Iter::new(Box::new( + self.node_map + .values() + .flat_map(|n| n.non_less_neighbors_iter_full()) + .map(EdgeType::Edge), + )) + } + } + + #[inline] + fn degree(&self, id: Id) -> usize { + match self.node_map.get(&id) { + Some(node) => node.degree(), + None => panic!("Node {:?} do not exist.", id), + } + } + + #[inline] + fn total_degree(&self, id: Id) -> usize { + match self.node_map.get(&id) { + Some(node) => node.degree() + node.in_degree(), + None => panic!("Node {:?} do not exist.", id), + } + } + + #[inline] + fn neighbors_iter(&self, id: Id) -> Iter { + match self.node_map.get(&id) { + Some(node) => node.neighbors_iter(), + None => panic!("Node {:?} do not exist.", id), + } + } + + #[inline] + fn neighbors(&self, id: Id) -> Cow<[Id]> { + match self.node_map.get(&id) { + Some(node) => node.neighbors().into(), + None => panic!("Node {:?} do not exist.", id), + } + } + + #[inline] + fn max_seen_id(&self) -> Option { + self.max_id + } + + #[inline(always)] + fn implementation(&self) -> GraphImpl { + GraphImpl::GraphMap + } +} + +impl + GraphLabelTrait for TypedGraphMap +{ + #[inline(always)] + fn get_node_label_map(&self) -> &SetMap { + &self.node_label_map + } + + #[inline(always)] + fn get_edge_label_map(&self) -> &SetMap { + &self.edge_label_map + } +} + +impl + MutGraphLabelTrait for TypedGraphMap +{ + #[inline] + fn update_node_label(&mut self, node_id: Id, label: Option) -> bool { + if !self.has_node(node_id) { + return false; + } + + self.add_node(node_id, label); + + true + } + + #[inline] + fn update_edge_label(&mut self, start: Id, target: Id, label: Option) -> bool { + if !self.has_edge(start, target) { + return false; + } + + self.add_edge(start, target, label); + + true + } +} + +impl UnGraphTrait + for TypedUnGraphMap +{ +} + +impl DiGraphTrait + for TypedDiGraphMap +{ + #[inline] + fn in_degree(&self, id: Id) -> usize { + match self.get_node(id) { + NodeType::NodeMap(node) => node.in_degree(), + NodeType::None => panic!("Node {:?} do not exist.", id), + _ => panic!("Unknown error."), + } + } + + #[inline] + fn in_neighbors_iter(&self, id: Id) -> Iter { + match self.get_node(id) { + NodeType::NodeMap(ref node) => node.in_neighbors_iter(), + NodeType::None => panic!("Node {:?} do not exist.", id), + _ => panic!("Unknown error."), + } + } + + #[inline] + fn in_neighbors(&self, id: Id) -> Cow<[Id]> { + match self.get_node(id) { + NodeType::NodeMap(ref node) => node.in_neighbors().into(), + NodeType::None => panic!("Node {:?} do not exist.", id), + _ => panic!("Unknown error."), + } + } +} + +impl GeneralGraph + for TypedUnGraphMap +{ + #[inline(always)] + fn as_graph(&self) -> &dyn GraphTrait { + self + } + + #[inline(always)] + fn as_labeled_graph(&self) -> &dyn GraphLabelTrait { + self + } + + #[inline(always)] + fn as_general_graph(&self) -> &dyn GeneralGraph { + self + } + + #[inline(always)] + fn as_mut_graph(&mut self) -> Option<&mut dyn MutGraphTrait> { + Some(self) + } +} + +impl GeneralGraph + for TypedDiGraphMap +{ + #[inline(always)] + fn as_graph(&self) -> &dyn GraphTrait { + self + } + + #[inline(always)] + fn as_labeled_graph(&self) -> &dyn GraphLabelTrait { + self + } + + #[inline(always)] + fn as_general_graph(&self) -> &dyn GeneralGraph { + self + } + + #[inline(always)] + fn as_digraph(&self) -> Option<&dyn DiGraphTrait> { + Some(self) + } + + #[inline(always)] + fn as_mut_graph(&mut self) -> Option<&mut dyn MutGraphTrait> { + Some(self) + } +} + +impl + TypedGraphMap +{ + pub fn reorder_id( + self, + reorder_node_id: bool, + reorder_node_label: bool, + reorder_edge_label: bool, + ) -> ReorderResult { + let node_id_map: Option> = if reorder_node_id { + Some( + self.node_map + .values() + .map(|n| (n.get_id(), n.degree() + n.in_degree())) + .sorted_by_key(|&(_, d)| d) + .into_iter() + .map(|(n, _)| n) + .collect(), + ) + } else { + None + }; + + let node_label_map: Option> = if reorder_node_label { + Some( + self.get_node_label_id_counter() + .most_common() + .into_iter() + .rev() + .skip_while(|(_, f)| *f == 0) + .map(|(n, _)| n) + .collect(), + ) + } else { + None + }; + + let edge_label_map: Option> = if reorder_edge_label { + Some( + self.get_edge_label_id_counter() + .most_common() + .into_iter() + .rev() + .skip_while(|(_, f)| *f == 0) + .map(|(n, _)| n) + .collect(), + ) + } else { + None + }; + + let graph = Some(self.reorder_id_with(&node_id_map, &node_label_map, &edge_label_map)); + + ReorderResult { node_id_map, graph } + } + + pub fn reorder_id_with( + self, + node_id_map: &Option>, + node_label_map: &Option>, + edge_label_map: &Option>, + ) -> Self { + if node_id_map.is_none() && node_label_map.is_none() && edge_label_map.is_none() { + return self; + } + + let num_of_edges = self.edge_count(); + + let mut new_node_map = HashMap::new(); + + for (_, node) in self.node_map { + let new_node_id = if let Some(ref map) = node_id_map { + Id::new(map.find_index(&node.id).unwrap()) + } else { + node.id + }; + + let new_node_label = if let Some(ref map) = node_label_map { + node.label.map(|i| L::new(map.find_index(&i).unwrap())) + } else { + node.label + }; + + let new_neighbors = if node_id_map.is_some() || edge_label_map.is_some() { + node.neighbors + .into_iter() + .map(|(n, l)| { + let new_n = if let Some(ref map) = node_id_map { + Id::new(map.find_index(&n).unwrap()) + } else { + n + }; + + let new_l = l.map(|i| { + if let Some(ref map) = edge_label_map { + L::new(map.find_index(&i).unwrap()) + } else { + i + } + }); + + (new_n, new_l) + }) + .collect() + } else { + node.neighbors + }; + + let new_in_neighbors = if let Some(ref map) = node_id_map { + node.in_neighbors + .into_iter() + .map(|n| Id::new(map.find_index(&n).unwrap())) + .collect() + } else { + node.in_neighbors + }; + + let new_node = NodeMap { + id: new_node_id, + label: new_node_label, + neighbors: new_neighbors, + in_neighbors: new_in_neighbors, + }; + + new_node_map.insert(new_node_id, new_node); + } + + new_node_map.shrink_to_fit(); + + let new_node_label_map = if let Some(ref map) = node_label_map { + reorder_label_map(map, self.node_label_map) + } else { + self.node_label_map + }; + + let new_edge_label_map = if let Some(ref map) = edge_label_map { + reorder_label_map(map, self.edge_label_map) + } else { + self.edge_label_map + }; + + let new_max_id = new_node_map.keys().max().map(|i| *i); + + TypedGraphMap { + node_map: new_node_map, + num_of_edges, + edge_label_map: new_edge_label_map, + node_label_map: new_node_label_map, + max_id: new_max_id, + graph_type: PhantomData, + } + } + + pub fn into_static(mut self) -> TypedStaticGraph { + let max_nid = self.node_indices().max().unwrap(); + + let num_of_nodes = max_nid.id() + 1; //self.node_count(); + let num_of_edges = self.edge_count(); + + let mut offset = 0usize; + let mut offset_vec = Vec::new(); + let mut edge_vec = Vec::new(); + let mut edge_labels = if self.has_edge_labels() { + Some(Vec::new()) + } else { + None + }; + + let mut node_labels = if self.has_node_labels() { + Some(Vec::new()) + } else { + None + }; + + let (mut in_offset, mut in_offset_vec, mut in_edge_vec) = if self.is_directed() { + (Some(0usize), Some(Vec::new()), Some(Vec::new())) + } else { + (None, None, None) + }; + + let mut nid = Id::new(0); + + offset_vec.push(offset); + + if let (Some(_in_offset), Some(_in_offset_vec)) = (in_offset, in_offset_vec.as_mut()) { + _in_offset_vec.push(_in_offset); + } + + while nid <= max_nid { + if let Some(mut node) = self.node_map.remove(&nid) { + let neighbors = mem::replace(&mut node.neighbors, BTreeMap::new()); + offset += neighbors.len(); + + if let Some(ref mut _edge_labels) = edge_labels { + for (n, l) in neighbors { + edge_vec.push(n); + _edge_labels.push(match l { + Some(_l) => _l, + None => L::max_value(), + }); + } + } else { + edge_vec.extend(neighbors.keys()); + } + + if let (Some(_in_offset), Some(_in_edge_vec)) = + (in_offset.as_mut(), in_edge_vec.as_mut()) + { + let in_neighbors = mem::replace(&mut node.in_neighbors, BTreeSet::new()); + + *_in_offset += in_neighbors.len(); + _in_edge_vec.extend(in_neighbors); + } + + if let Some(ref mut _node_labels) = node_labels { + match node.label { + Some(label) => _node_labels.push(label), + None => _node_labels.push(L::max_value()), + } + } + } else if let Some(ref mut _node_labels) = node_labels { + _node_labels.push(L::max_value()); + } + + offset_vec.push(offset); + + if let (Some(_in_offset), Some(_in_offset_vec)) = (in_offset, in_offset_vec.as_mut()) { + _in_offset_vec.push(_in_offset); + } + + nid.increment(); + + //shrink the map to save memory + self.shrink_to_fit(); + } + + let mut edge_vec = EdgeVec::from_raw(offset_vec, edge_vec, edge_labels); + edge_vec.shrink_to_fit(); + + let in_edge_vec = + if let (Some(_in_offset_vec), Some(_in_edge_vec)) = (in_offset_vec, in_edge_vec) { + let mut _edge = EdgeVec::new(_in_offset_vec, _in_edge_vec); + _edge.shrink_to_fit(); + Some(_edge) + } else { + None + }; + + if let Some(ref mut _labels) = node_labels { + _labels.shrink_to_fit(); + } + + let node_label_map = self.node_label_map; + let edge_label_map = self.edge_label_map; + + TypedStaticGraph::from_raw( + num_of_nodes, + num_of_edges, + edge_vec, + in_edge_vec, + node_labels, + node_label_map, + edge_label_map, + ) + } +} + +fn reorder_label_map(new_map: &impl MapTrait, old_map: impl MapTrait) -> SetMap +where + Id: IdType, + L: Hash + Eq, +{ + let mut old_map_vec: Vec<_> = old_map.items_vec().into_iter().map(|i| Some(i)).collect(); + let mut result = SetMap::new(); + + for i in new_map.items() { + let l = old_map_vec[i.id()].take().unwrap(); + result.add_item(l); + } + + result +} + +#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] +pub struct ReorderResult { + node_id_map: Option>, + graph: Option>, +} + +impl + ReorderResult +{ + #[inline] + pub fn take_graph(&mut self) -> Option> { + self.graph.take() + } + + #[inline] + pub fn get_node_id_map(&self) -> Option<&SetMap> { + self.node_id_map.as_ref() + } + + #[inline] + pub fn get_original_node_id(&self, id: Id) -> Id { + match self.get_node_id_map() { + Some(map) => *map.get_item(id.id()).unwrap(), + None => id, + } + } + + #[inline] + pub fn find_new_node_id(&self, id: Id) -> Id { + match self.get_node_id_map() { + Some(map) => Id::new(map.find_index(&id).unwrap()), + None => id, + } + } +} diff --git a/src/graph_impl/graph_map/mod.rs b/src/graph_impl/graph_map/mod.rs index c117f352..e85ca038 100644 --- a/src/graph_impl/graph_map/mod.rs +++ b/src/graph_impl/graph_map/mod.rs @@ -1,47 +1,47 @@ -/* - * Copyright (c) 2018 UNSW Sydney, Data and Knowledge Group. - * - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you 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. - */ -//! An implementation of graph data structure that supports directed graph, undirected graph, -//! node label, edge label, self loop, but not multi-edge. -//! -//! A unique id of type `usize` must be given to each node when creating the graph. -//! -//! # Example -//! ``` -//! use rust_graph::prelude::*; -//! use rust_graph::UnGraphMap; -//! -//! let mut g = UnGraphMap::<&str>::new(); -//! g.add_node(0,None); -//! g.add_node(1,Some("node label")); -//! g.add_edge(0,1,Some("edge label")); -//! ``` - -pub mod edge; -pub mod graph; -pub mod node; - -pub use graph_impl::graph_map::edge::{Edge, MutEdge}; -pub use graph_impl::graph_map::graph::{ - new_general_graphmap, DiGraphMap, GraphMap, TypedDiGraphMap, TypedGraphMap, TypedUnGraphMap, - UnGraphMap, -}; -pub use graph_impl::graph_map::node::NodeMap; -pub use graph_impl::graph_map::node::{MutNodeMapTrait, NodeMapTrait}; +/* + * Copyright (c) 2018 UNSW Sydney, Data and Knowledge Group. + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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. + */ +//! An implementation of graph data structure that supports directed graph, undirected graph, +//! node label, edge label, self loop, but not multi-edge. +//! +//! A unique id of type `usize` must be given to each node when creating the graph. +//! +//! # Example +//! ``` +//! use rust_graph::prelude::*; +//! use rust_graph::UnGraphMap; +//! +//! let mut g = UnGraphMap::<&str>::new(); +//! g.add_node(0,None); +//! g.add_node(1,Some("node label")); +//! g.add_edge(0,1,Some("edge label")); +//! ``` + +pub mod edge; +pub mod graph; +pub mod node; + +pub use crate::graph_impl::graph_map::edge::{Edge, MutEdge}; +pub use crate::graph_impl::graph_map::graph::{ + new_general_graphmap, DiGraphMap, GraphMap, TypedDiGraphMap, TypedGraphMap, TypedUnGraphMap, + UnGraphMap, +}; +pub use crate::graph_impl::graph_map::node::NodeMap; +pub use crate::graph_impl::graph_map::node::{MutNodeMapTrait, NodeMapTrait}; diff --git a/src/graph_impl/graph_map/node.rs b/src/graph_impl/graph_map/node.rs index b0c85425..5a86db63 100644 --- a/src/graph_impl/graph_map/node.rs +++ b/src/graph_impl/graph_map/node.rs @@ -1,225 +1,225 @@ -/* - * Copyright (c) 2018 UNSW Sydney, Data and Knowledge Group. - * - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you 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. - */ -use std::collections::{BTreeMap, BTreeSet}; - -use generic::{IdType, Iter, MutEdgeType, MutNodeTrait, NodeTrait, OwnedEdgeType}; -use graph_impl::graph_map::{Edge, MutEdge}; - -#[derive(Debug, Hash, PartialEq, Eq, Clone, Serialize, Deserialize)] -pub struct NodeMap { - pub(crate) id: Id, - pub(crate) label: Option, - pub(crate) neighbors: BTreeMap>, - pub(crate) in_neighbors: BTreeSet, -} - -impl NodeMap { - #[inline(always)] - pub fn new(id: Id, label: Option) -> Self { - NodeMap { - id, - label, - neighbors: BTreeMap::new(), - in_neighbors: BTreeSet::new(), - } - } -} - -pub trait NodeMapTrait { - fn has_in_neighbor(&self, id: Id) -> bool; - fn has_neighbor(&self, id: Id) -> bool; - fn in_degree(&self) -> usize; - fn degree(&self) -> usize; - fn neighbors_iter(&self) -> Iter; - fn in_neighbors_iter(&self) -> Iter; - fn neighbors(&self) -> Vec; - fn in_neighbors(&self) -> Vec; - fn get_neighbor(&self, id: Id) -> Option>; - fn non_less_neighbors_iter(&self) -> Iter; - fn neighbors_iter_full(&self) -> Iter>; - fn non_less_neighbors_iter_full(&self) -> Iter>; -} - -pub trait MutNodeMapTrait { - fn add_in_edge(&mut self, adj: Id) -> bool; - fn add_edge(&mut self, adj: Id, label: Option) -> bool; - fn remove_in_edge(&mut self, adj: Id) -> bool; - fn remove_edge(&mut self, adj: Id) -> OwnedEdgeType; - fn get_neighbor_mut(&mut self, id: Id) -> MutEdgeType; - fn neighbors_iter_mut(&mut self) -> Iter>; - fn non_less_neighbors_iter_mut(&mut self) -> Iter>; -} - -impl NodeTrait for NodeMap { - #[inline(always)] - fn get_id(&self) -> Id { - self.id - } - - #[inline(always)] - fn get_label_id(&self) -> Option { - self.label - } -} - -impl MutNodeTrait for NodeMap { - #[inline(always)] - fn set_label_id(&mut self, label: Option) { - self.label = label; - } -} - -impl NodeMapTrait for NodeMap { - #[inline] - fn has_in_neighbor(&self, id: Id) -> bool { - self.in_neighbors.contains(&id) - } - - #[inline] - fn has_neighbor(&self, id: Id) -> bool { - self.neighbors.contains_key(&id) - } - - #[inline] - fn in_degree(&self) -> usize { - self.in_neighbors.len() - } - - #[inline] - fn degree(&self) -> usize { - self.neighbors.len() - } - - #[inline] - fn neighbors_iter(&self) -> Iter { - Iter::new(Box::new(self.neighbors.keys().map(|x| *x))) - } - - #[inline] - fn in_neighbors_iter(&self) -> Iter { - Iter::new(Box::new(self.in_neighbors.iter().map(|x| *x))) - } - - #[inline] - fn neighbors(&self) -> Vec { - self.neighbors.keys().cloned().collect() - } - - #[inline] - fn in_neighbors(&self) -> Vec { - self.in_neighbors.iter().cloned().collect() - } - - #[inline] - fn get_neighbor(&self, id: Id) -> Option> { - self.neighbors.get(&id).map(|x| *x) - } - - #[inline] - fn non_less_neighbors_iter(&self) -> Iter { - Iter::new(Box::new(self.neighbors.range(self.id..).map(|(&id, _)| id))) - } - - #[inline] - fn neighbors_iter_full(&self) -> Iter> { - let nid = self.id; - - Iter::new(Box::new( - self.neighbors - .iter() - .map(move |(&n, &l)| Edge::new(nid, n, l)), - )) - } - - #[inline] - fn non_less_neighbors_iter_full(&self) -> Iter> { - let nid = self.id; - - Iter::new(Box::new( - self.neighbors - .range(self.get_id()..) - .map(move |(&n, &l)| Edge::new(nid, n, l)), - )) - } -} - -impl MutNodeMapTrait for NodeMap { - #[inline] - fn add_in_edge(&mut self, adj: Id) -> bool { - if self.has_in_neighbor(adj) { - return false; - } - self.in_neighbors.insert(adj); - - true - } - - #[inline] - fn add_edge(&mut self, adj: Id, label: Option) -> bool { - let mut result = false; - let edge_label = self.neighbors.entry(adj).or_insert_with(|| { - result = true; - - None - }); - *edge_label = label; - - result - } - - #[inline] - fn remove_in_edge(&mut self, adj: Id) -> bool { - self.in_neighbors.remove(&adj) - } - - #[inline] - fn remove_edge(&mut self, adj: Id) -> OwnedEdgeType { - match self.neighbors.remove(&adj) { - Some(edge) => OwnedEdgeType::Edge(Edge::new(self.get_id(), adj, edge)), - None => OwnedEdgeType::None, - } - } - - #[inline] - fn get_neighbor_mut(&mut self, id: Id) -> MutEdgeType { - let nid = self.get_id(); - match self.neighbors.get_mut(&id) { - Some(edge) => MutEdgeType::EdgeRef(MutEdge::new(nid, id, edge)), - None => MutEdgeType::None, - } - } - - #[inline] - fn neighbors_iter_mut(&mut self) -> Iter> { - let nid = self.get_id(); - Iter::new(Box::new(self.neighbors.iter_mut().map(move |(n, l)| { - MutEdgeType::EdgeRef(MutEdge::new(nid, *n, l)) - }))) - } - - #[inline] - fn non_less_neighbors_iter_mut(&mut self) -> Iter> { - let nid = self.get_id(); - Iter::new(Box::new(self.neighbors.range_mut(self.id..).map( - move |(n, l)| MutEdgeType::EdgeRef(MutEdge::new(nid, *n, l)), - ))) - } -} +/* + * Copyright (c) 2018 UNSW Sydney, Data and Knowledge Group. + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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. + */ +use std::collections::{BTreeMap, BTreeSet}; + +use crate::generic::{IdType, Iter, MutEdgeType, MutNodeTrait, NodeTrait, OwnedEdgeType}; +use crate::graph_impl::graph_map::{Edge, MutEdge}; + +#[derive(Debug, Hash, PartialEq, Eq, Clone, Serialize, Deserialize)] +pub struct NodeMap { + pub(crate) id: Id, + pub(crate) label: Option, + pub(crate) neighbors: BTreeMap>, + pub(crate) in_neighbors: BTreeSet, +} + +impl NodeMap { + #[inline(always)] + pub fn new(id: Id, label: Option) -> Self { + NodeMap { + id, + label, + neighbors: BTreeMap::new(), + in_neighbors: BTreeSet::new(), + } + } +} + +pub trait NodeMapTrait { + fn has_in_neighbor(&self, id: Id) -> bool; + fn has_neighbor(&self, id: Id) -> bool; + fn in_degree(&self) -> usize; + fn degree(&self) -> usize; + fn neighbors_iter(&self) -> Iter; + fn in_neighbors_iter(&self) -> Iter; + fn neighbors(&self) -> Vec; + fn in_neighbors(&self) -> Vec; + fn get_neighbor(&self, id: Id) -> Option>; + fn non_less_neighbors_iter(&self) -> Iter; + fn neighbors_iter_full(&self) -> Iter>; + fn non_less_neighbors_iter_full(&self) -> Iter>; +} + +pub trait MutNodeMapTrait { + fn add_in_edge(&mut self, adj: Id) -> bool; + fn add_edge(&mut self, adj: Id, label: Option) -> bool; + fn remove_in_edge(&mut self, adj: Id) -> bool; + fn remove_edge(&mut self, adj: Id) -> OwnedEdgeType; + fn get_neighbor_mut(&mut self, id: Id) -> MutEdgeType; + fn neighbors_iter_mut(&mut self) -> Iter>; + fn non_less_neighbors_iter_mut(&mut self) -> Iter>; +} + +impl NodeTrait for NodeMap { + #[inline(always)] + fn get_id(&self) -> Id { + self.id + } + + #[inline(always)] + fn get_label_id(&self) -> Option { + self.label + } +} + +impl MutNodeTrait for NodeMap { + #[inline(always)] + fn set_label_id(&mut self, label: Option) { + self.label = label; + } +} + +impl NodeMapTrait for NodeMap { + #[inline] + fn has_in_neighbor(&self, id: Id) -> bool { + self.in_neighbors.contains(&id) + } + + #[inline] + fn has_neighbor(&self, id: Id) -> bool { + self.neighbors.contains_key(&id) + } + + #[inline] + fn in_degree(&self) -> usize { + self.in_neighbors.len() + } + + #[inline] + fn degree(&self) -> usize { + self.neighbors.len() + } + + #[inline] + fn neighbors_iter(&self) -> Iter { + Iter::new(Box::new(self.neighbors.keys().map(|x| *x))) + } + + #[inline] + fn in_neighbors_iter(&self) -> Iter { + Iter::new(Box::new(self.in_neighbors.iter().map(|x| *x))) + } + + #[inline] + fn neighbors(&self) -> Vec { + self.neighbors.keys().cloned().collect() + } + + #[inline] + fn in_neighbors(&self) -> Vec { + self.in_neighbors.iter().cloned().collect() + } + + #[inline] + fn get_neighbor(&self, id: Id) -> Option> { + self.neighbors.get(&id).map(|x| *x) + } + + #[inline] + fn non_less_neighbors_iter(&self) -> Iter { + Iter::new(Box::new(self.neighbors.range(self.id..).map(|(&id, _)| id))) + } + + #[inline] + fn neighbors_iter_full(&self) -> Iter> { + let nid = self.id; + + Iter::new(Box::new( + self.neighbors + .iter() + .map(move |(&n, &l)| Edge::new(nid, n, l)), + )) + } + + #[inline] + fn non_less_neighbors_iter_full(&self) -> Iter> { + let nid = self.id; + + Iter::new(Box::new( + self.neighbors + .range(self.get_id()..) + .map(move |(&n, &l)| Edge::new(nid, n, l)), + )) + } +} + +impl MutNodeMapTrait for NodeMap { + #[inline] + fn add_in_edge(&mut self, adj: Id) -> bool { + if self.has_in_neighbor(adj) { + return false; + } + self.in_neighbors.insert(adj); + + true + } + + #[inline] + fn add_edge(&mut self, adj: Id, label: Option) -> bool { + let mut result = false; + let edge_label = self.neighbors.entry(adj).or_insert_with(|| { + result = true; + + None + }); + *edge_label = label; + + result + } + + #[inline] + fn remove_in_edge(&mut self, adj: Id) -> bool { + self.in_neighbors.remove(&adj) + } + + #[inline] + fn remove_edge(&mut self, adj: Id) -> OwnedEdgeType { + match self.neighbors.remove(&adj) { + Some(edge) => OwnedEdgeType::Edge(Edge::new(self.get_id(), adj, edge)), + None => OwnedEdgeType::None, + } + } + + #[inline] + fn get_neighbor_mut(&mut self, id: Id) -> MutEdgeType { + let nid = self.get_id(); + match self.neighbors.get_mut(&id) { + Some(edge) => MutEdgeType::EdgeRef(MutEdge::new(nid, id, edge)), + None => MutEdgeType::None, + } + } + + #[inline] + fn neighbors_iter_mut(&mut self) -> Iter> { + let nid = self.get_id(); + Iter::new(Box::new(self.neighbors.iter_mut().map(move |(n, l)| { + MutEdgeType::EdgeRef(MutEdge::new(nid, *n, l)) + }))) + } + + #[inline] + fn non_less_neighbors_iter_mut(&mut self) -> Iter> { + let nid = self.get_id(); + Iter::new(Box::new(self.neighbors.range_mut(self.id..).map( + move |(n, l)| MutEdgeType::EdgeRef(MutEdge::new(nid, *n, l)), + ))) + } +} diff --git a/src/graph_impl/graph_vec/mod.rs b/src/graph_impl/graph_vec/mod.rs new file mode 100644 index 00000000..dc062b85 --- /dev/null +++ b/src/graph_impl/graph_vec/mod.rs @@ -0,0 +1,410 @@ +/* + * Copyright (c) 2018 UNSW Sydney, Data and Knowledge Group. + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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. + */ +use std::hash::Hash; + +use hashbrown::HashMap; +use rayon::prelude::*; + +use crate::generic::{DefaultId, GraphType, IdType, MutMapTrait}; +use crate::graph_impl::static_graph::edge_vec::EdgeVecTrait; +use crate::graph_impl::static_graph::edge_vec::OffsetIndex; +use crate::graph_impl::{EdgeVec, TypedStaticGraph}; +use crate::map::SetMap; +use itertools::Itertools; + +pub type GraphVec = TypedGraphVec; + +#[derive(Debug, Clone, Serialize, Deserialize)] +pub struct TypedGraphVec { + nodes: HashMap, + edges: Vec<((Id, Id), L)>, + in_edges: Vec<(Id, Id)>, + node_label_map: SetMap, + edge_label_map: SetMap, + + max_id: Option, + has_node_label: bool, + has_edge_label: bool, +} + +impl TypedGraphVec { + pub fn new() -> Self { + TypedGraphVec { + nodes: HashMap::new(), + edges: Vec::new(), + in_edges: Vec::new(), + node_label_map: SetMap::new(), + edge_label_map: SetMap::new(), + + max_id: None, + has_node_label: false, + has_edge_label: false, + } + } + + pub fn with_capacity(nodes: usize, edges: usize) -> Self { + TypedGraphVec { + nodes: HashMap::with_capacity(nodes), + edges: Vec::with_capacity(edges), + in_edges: Vec::new(), + node_label_map: SetMap::new(), + edge_label_map: SetMap::new(), + + max_id: None, + has_node_label: false, + has_edge_label: false, + } + } + + pub fn with_label_map(node_label_map: SetMap, edge_label_map: SetMap) -> Self { + TypedGraphVec { + nodes: HashMap::new(), + edges: Vec::new(), + in_edges: Vec::new(), + node_label_map, + edge_label_map, + + max_id: None, + has_node_label: false, + has_edge_label: false, + } + } + + #[inline] + pub fn add_node(&mut self, id: Id, label: Option) { + let label_id = match label { + Some(l) => { + if !self.has_node_label { + self.has_node_label = true; + } + L::new(self.node_label_map.add_item(l)) + } + None => L::max_value(), + }; + + self.set_max(id); + + self.nodes.insert(id, label_id); + } + + #[inline] + pub fn add_edge(&mut self, src: Id, dst: Id, label: Option) { + let label_id = match label { + Some(l) => { + if !self.has_edge_label { + self.has_edge_label = true; + } + L::new(self.edge_label_map.add_item(l)) + } + None => L::max_value(), + }; + + self.set_max(src); + self.set_max(dst); + + self.edges.push(((src, dst), label_id)); + } + + #[inline] + pub fn add_in_edge(&mut self, src: Id, dst: Id) { + self.set_max(src); + self.set_max(dst); + + self.in_edges.push((src, dst)); + } + + #[inline(always)] + pub fn is_directed(&self) -> bool { + !self.in_edges.is_empty() + } + + #[inline(always)] + pub fn is_empty(&self) -> bool { + self.nodes.is_empty() && self.edges.is_empty() && self.in_edges.is_empty() + } + + #[inline(always)] + pub fn node_count(&self) -> usize { + self.nodes.len() + } + + #[inline(always)] + pub fn edge_count(&self) -> usize { + self.edges.len() + } + + pub fn into_static(self) -> TypedStaticGraph { + if self.is_empty() { + return TypedStaticGraph::empty(); + } + + let max_id = self.max_id.unwrap(); + + let node_labels = Self::get_node_labels(self.nodes, max_id, self.has_node_label); + let edge_vec = Self::get_edge_vec(self.edges, max_id, self.has_edge_label); + let in_edge_vec = if Ty::is_directed() { + Some(Self::get_in_edge_vec(self.in_edges, max_id)) + } else { + None + }; + + TypedStaticGraph::from_raw( + max_id.id() + 1, + if Ty::is_directed() { + edge_vec.num_edges() + } else { + edge_vec.num_edges() >> 1 + }, + edge_vec, + in_edge_vec, + node_labels, + self.node_label_map, + self.edge_label_map, + ) + } + + fn get_node_labels( + nodes: HashMap, + max_node_id: Id, + has_node_label: bool, + ) -> Option> { + info!("Creating node labels"); + + if !has_node_label { + return None; + } + let mut nodes = nodes.into_iter().collect_vec(); + + // TODO + nodes.par_sort_unstable(); + // nodes.dedup_by_key(|&mut (i, _)| i); + + let mut labels = Vec::new(); + let mut current = Id::new(0); + + let mut last = Id::new(0); + + for (i, l) in nodes.into_iter() { + while i > current { + labels.push(OL::max_value()); + current.increment(); + } + labels.push(OL::new(l.id())); + current.increment(); + + last = i; + } + + let last = last.id(); + if last < max_node_id.id() { + for _ in 0..max_node_id.id() - last { + labels.push(OL::max_value()); + } + } + + Some(labels) + } + + fn get_edge_vec( + mut graph: Vec<((Id, Id), L)>, + max_node_id: Id, + has_edge_label: bool, + ) -> EdgeVec { + info!("Creating edges"); + + // TODO + graph.par_sort_unstable(); + graph.dedup_by_key(|&mut (e, _)| e); + + let mut offsets = OffsetIndex::new(); + let mut edges = Vec::new(); + let mut labels = if has_edge_label { + Some(Vec::new()) + } else { + None + }; + + let mut offset = 0usize; + offsets.push(offset); + + let mut current = Id::new(0); + + let mut last = Id::new(0); + + for ((s, d), l) in graph.into_iter() { + while s > current { + offsets.push(offset); + current.increment(); + } + + edges.push(d); + if let Some(_labels) = labels.as_mut() { + _labels.push(OL::new(l.id())); + } + + offset += 1; + + last = s; + } + + offset = edges.len(); + offsets.push(offset); + + let last = last.id(); + if last < max_node_id.id() { + for _ in 0..max_node_id.id() - last { + offsets.push(offset); + } + } + + EdgeVec::from_raw_index(offsets, edges, labels) + } + + fn get_in_edge_vec(mut graph: Vec<(Id, Id)>, max_node_id: Id) -> EdgeVec { + info!("Creating in-edges"); + + // TODO + graph.par_sort_unstable(); + + let iter = graph.into_iter().dedup(); + + let mut offsets = Vec::new(); + let mut edges = Vec::new(); + + let mut offset = 0usize; + offsets.push(offset); + + let mut current = Id::new(0); + + let mut last = Id::new(0); + + for (s, d) in iter { + while s > current { + offsets.push(offset); + current.increment(); + } + + edges.push(d); + offset += 1; + + last = s; + } + + offset = edges.len(); + offsets.push(offset); + + let last = last.id(); + if last < max_node_id.id() { + for _ in 0..max_node_id.id() - last { + offsets.push(offset); + } + } + + EdgeVec::new(offsets, edges) + } + + fn set_max(&mut self, id: Id) { + if self.max_id.map_or(true, |m| id > m) { + self.max_id = Some(id); + } + } +} + +#[cfg(test)] +mod tests { + use super::*; + use crate::graph_impl::{DiStaticGraph, UnStaticGraph}; + use crate::prelude::*; + + #[test] + fn test_undirected() { + let mut g = GraphVec::<&str>::new(); + g.add_node(0, Some("node0")); + g.add_node(2, Some("node2")); + g.add_node(2, Some("node2")); + g.add_edge(0, 1, Some("(0,1)")); + g.add_edge(1, 0, Some("(0,1)")); + g.add_edge(0, 3, Some("(0,3)")); + + let un_graph = g.clone().into_static::(); + + let un_graph_true = UnStaticGraph::<&str, &str, u16>::from_raw( + 4, + 1, + EdgeVec::with_labels(vec![0, 2, 3, 3, 3], vec![1, 3, 0], vec![0, 1, 0]), + None, + Some(vec![0, u16::max_value(), 1, u16::max_value()]), + vec!["node0", "node2"].into(), + vec!["(0,1)", "(0,3)"].into(), + ); + assert_eq!(format!("{:?}", un_graph), format!("{:?}", un_graph_true)); + } + + #[test] + fn test_directed() { + let mut g = GraphVec::<&str>::new(); + g.add_node(0, Some("node0")); + g.add_node(2, Some("node2")); + g.add_edge(0, 1, Some("(0,1)")); + g.add_in_edge(1, 0); + g.add_edge(0, 3, Some("(0,3)")); + + assert_eq!(g.node_count(), 2); + assert_eq!(g.edge_count(), 2); + + let di_graph = g.clone().into_static::(); + + let di_graph_true = DiStaticGraph::<&str>::from_raw( + 4, + 2, + EdgeVec::with_labels(vec![0, 2, 2, 2, 2], vec![1, 3], vec![0, 1]), + Some(EdgeVec::new(vec![0, 0, 1, 1, 1], vec![0])), + Some(vec![0, u32::max_value(), 1, u32::max_value()]), + vec!["node0", "node2"].into(), + vec!["(0,1)", "(0,3)"].into(), + ); + + assert_eq!(format!("{:?}", di_graph), format!("{:?}", di_graph_true)); + } + + #[test] + fn test_no_node() { + let mut g = GraphVec::<&str>::new(); + g.add_edge(0, 1, None); + g.add_edge(0, 2, None); + g.add_edge(1, 0, None); + g.add_edge(2, 0, None); + + let un_graph = g.clone().into_static::(); + + let un_graph_true = UnStaticGraph::<()>::from_raw( + 3, + 2, + EdgeVec::new(vec![0, 2, 3, 4], vec![1, 2, 0, 0]), + None, + None, + SetMap::new(), + SetMap::new(), + ); + + assert_eq!(format!("{:?}", un_graph), format!("{:?}", un_graph_true)); + } +} diff --git a/src/graph_impl/mod.rs b/src/graph_impl/mod.rs index 06e82b83..aca04ae7 100644 --- a/src/graph_impl/mod.rs +++ b/src/graph_impl/mod.rs @@ -1,53 +1,51 @@ -/* - * Copyright (c) 2018 UNSW Sydney, Data and Knowledge Group. - * - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you 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. - */ -pub mod graph_map; -pub mod static_graph; - -pub use graph_impl::graph_map::{ - DiGraphMap, Edge, GraphMap, MutEdge, TypedDiGraphMap, TypedGraphMap, TypedUnGraphMap, - UnGraphMap, -}; -pub use graph_impl::static_graph::mmap::{EdgeVecMmap, StaticGraphMmap}; -pub use graph_impl::static_graph::{ - DiStaticGraph, EdgeVec, StaticGraph, TypedDiStaticGraph, TypedStaticGraph, TypedUnStaticGraph, - UnStaticGraph, -}; - -#[derive(Eq, PartialEq, Copy, Clone, Debug)] -pub enum GraphImpl { - GraphMap, - StaticGraph, - StaicGraphMmap, -} - -impl ::std::str::FromStr for GraphImpl { - type Err = String; - - fn from_str(s: &str) -> Result { - let s = s.to_lowercase(); - match s.as_ref() { - "graphmap" => Ok(GraphImpl::GraphMap), - "staticgraph" => Ok(GraphImpl::StaticGraph), - "staticgraphmmap" => Ok(GraphImpl::StaicGraphMmap), - _other => Err(format!("Unsupported implementation {:?}", _other)), - } - } -} +/* + * Copyright (c) 2018 UNSW Sydney, Data and Knowledge Group. + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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. + */ +pub mod graph_map; +pub mod graph_vec; +pub mod static_graph; + +pub use crate::graph_impl::graph_map::{ + DiGraphMap, Edge, GraphMap, MutEdge, TypedDiGraphMap, TypedGraphMap, TypedUnGraphMap, + UnGraphMap, +}; +pub use crate::graph_impl::graph_vec::{GraphVec, TypedGraphVec}; +pub use crate::graph_impl::static_graph::{ + DiStaticGraph, EdgeVec, StaticGraph, TypedDiStaticGraph, TypedStaticGraph, TypedUnStaticGraph, + UnStaticGraph, +}; + +#[derive(Eq, PartialEq, Copy, Clone, Debug, Serialize, Deserialize)] +pub enum GraphImpl { + GraphMap, + StaticGraph, +} + +impl ::std::str::FromStr for GraphImpl { + type Err = String; + fn from_str(s: &str) -> Result { + let s = s.to_lowercase(); + match s.as_ref() { + "graphmap" => Ok(GraphImpl::GraphMap), + "staticgraph" => Ok(GraphImpl::StaticGraph), + _other => Err(format!("Unsupported implementation {:?}", _other)), + } + } +} diff --git a/src/graph_impl/static_graph/edge_vec.rs b/src/graph_impl/static_graph/edge_vec.rs index 2d425d52..31ce0d65 100644 --- a/src/graph_impl/static_graph/edge_vec.rs +++ b/src/graph_impl/static_graph/edge_vec.rs @@ -18,10 +18,128 @@ * specific language governing permissions and limitations * under the License. */ -use generic::IdType; -use io::mmap::dump; -use std::fs::File; -use std::io::Result; +use crate::generic::{IdType, Iter}; +use itertools::Itertools; +use std::ops::Add; + +static BITS_U32: usize = 32; + +/// To main the offset in the more compact form, instead of directly using `Vec`, +/// we introduce a two-level index, the base level records the base offset using `Vec`, +/// if an `u32` is overflow, we shift the overflow bits' information to the second level. +/// The second-level index is a `Vec`, where the `i-th` element maintains the offsets in +/// the base level that has the overflow bits represent the number `i`. For example, if we have +/// the second-level index as `vec![1000, 50000, 600]`, it means that the first 1000 elements +/// in the `base_level` index has offset bit `0` (no offset), while from 1000 ~ 50000, has offset +/// bit `1`, meaning there actual offset should be `1 << 32 | base_level[i]`. +#[derive(Debug, Hash, PartialEq, Eq, Clone, Serialize, Deserialize)] +pub struct OffsetIndex { + /// The base-level index + base_level: Vec, + /// The second-level index + second_level: Vec, +} + +impl From> for OffsetIndex { + fn from(offsets: Vec) -> Self { + let mut offset_idx = Self::with_capacity(offsets.len()); + + for offset in offsets { + offset_idx.push(offset); + } + + offset_idx + } +} + +impl OffsetIndex { + pub fn new() -> Self { + Self { + base_level: Vec::new(), + second_level: Vec::new(), + } + } + + pub fn with_capacity(capacity: usize) -> Self { + Self { + base_level: Vec::with_capacity(capacity), + second_level: Vec::new(), + } + } + + pub fn len(&self) -> usize { + self.base_level.len() + } + + pub fn clear(&mut self) { + self.base_level.clear(); + self.second_level.clear(); + } + + pub fn is_empty(&self) -> bool { + self.base_level.is_empty() + } + + pub fn index(&self, index: usize) -> usize { + if index < self.len() { + let mut oflow_bit = 0; + for (i, &off) in self.second_level.iter().enumerate() { + if index < off { + oflow_bit = i; + break; + } + } + oflow_bit << BITS_U32 | self.base_level[index] as usize + } else { + panic!("index {} is overflowed", index) + } + } + + pub fn last(&self) -> Option { + if self.is_empty() { + None + } else { + Some(self.index(self.len() - 1)) + } + } + + pub fn push(&mut self, offset: usize) { + let u32_part = offset as u32; + let oflow_part = offset >> BITS_U32; + + let last_off = self.last().unwrap_or(0); + assert!(last_off <= offset); + + if self.is_empty() { + self.second_level.push(0); + } else { + let last_off_oflow = self.second_level[last_off >> BITS_U32]; + + while self.second_level.len() < oflow_part + 1 { + self.second_level.push(last_off_oflow); + } + } + + self.second_level[oflow_part] += 1; + self.base_level.push(u32_part); + } + + pub fn shrink_to_fit(&mut self) { + self.base_level.shrink_to_fit(); + self.second_level.shrink_to_fit(); + } + + fn extend>(&mut self, iter: T) { + for elem in iter.into_iter() { + self.push(elem); + } + } + + pub fn iter(&self) -> Iter { + let len = self.len(); + Iter::new(Box::new((0..len).map(move |i| self.index(i)))) + } +} /// With the node indexed from 0 .. num_nodes - 1, we can maintain the edges in a compact way, /// using `offset` and `edges`, in which `offset[node]` maintain the start index of the given @@ -29,17 +147,17 @@ use std::io::Result; /// `edges[offsets[node]]` (included) to `edges[offsets[node+1]]` (excluded), /// /// *Note*: The edges must be sorted according to the starting node, that is, -/// The sub-vector `edges[offsets[node]]` (included) - `edges[offsets[node + 1]]` (excluded) +/// The sub-vector from `edges[offsets[node]]` (included) to `edges[offsets[node + 1]]` (excluded) /// for any `node` should be sorted. #[derive(Debug, Hash, PartialEq, Eq, Clone, Serialize, Deserialize)] pub struct EdgeVec { - offsets: Vec, + offsets: OffsetIndex, edges: Vec, labels: Option>, } pub trait EdgeVecTrait { - fn get_offsets(&self) -> &[usize]; + fn get_offsets(&self) -> &OffsetIndex; fn get_edges(&self) -> &[Id]; fn get_labels(&self) -> &[L]; @@ -56,8 +174,12 @@ pub trait EdgeVecTrait { #[inline] fn neighbors(&self, node: Id) -> &[Id] { assert!(self.has_node(node)); - let start = self.get_offsets()[node.id()].id(); - let end = self.get_offsets()[node.id() + 1].id(); + // if !self.has_node(node) { + // error!("Node {:?} does not exist", node); + // return &[]; + // } + let start = self.get_offsets().index(node.id()); + let end = self.get_offsets().index(node.id() + 1); &self.get_edges()[start..end] } @@ -65,8 +187,12 @@ pub trait EdgeVecTrait { #[inline] fn degree(&self, node: Id) -> usize { assert!(self.has_node(node)); - let start = self.get_offsets()[node.id()].id(); - let end = self.get_offsets()[node.id() + 1].id(); + // if !self.has_node(node) { + // error!("Node {:?} does not exist", node); + // return 0; + // } + let start = self.get_offsets().index(node.id()); + let end = self.get_offsets().index(node.id() + 1); end - start } @@ -85,7 +211,7 @@ pub trait EdgeVecTrait { let found = neighbors.binary_search(&target); match found { Err(_) => None, - Ok(idx) => Some(self.get_offsets()[start.id()].id() + idx), + Ok(idx) => Some(self.get_offsets().index(start.id()) + idx), } } } @@ -114,7 +240,7 @@ pub trait EdgeVecTrait { impl EdgeVec { pub fn new(offsets: Vec, edges: Vec) -> Self { EdgeVec { - offsets, + offsets: offsets.into(), edges, labels: None, } @@ -129,7 +255,7 @@ impl EdgeVec { ); } EdgeVec { - offsets, + offsets: offsets.into(), edges, labels: Some(labels), } @@ -142,6 +268,14 @@ impl EdgeVec { } } + pub fn from_raw_index(offsets: OffsetIndex, edges: Vec, labels: Option>) -> Self { + EdgeVec { + offsets, + edges, + labels, + } + } + pub fn remove_labels(&mut self) { self.labels = None; } @@ -161,29 +295,11 @@ impl EdgeVec { labels.shrink_to_fit(); } } - - /// Dump self to bytearray in order to be deserialised as `EdgeVecMmap`. - pub fn dump_mmap(&self, prefix: &str) -> Result<()> { - let offsets_file = format!("{}.offsets", prefix); - let edges_file = format!("{}.edges", prefix); - let labels_file = format!("{}.labels", prefix); - - unsafe { - dump(self.get_offsets(), File::create(offsets_file)?)?; - dump(self.get_edges(), File::create(edges_file)?)?; - - if !self.get_labels().is_empty() { - dump(self.get_labels(), File::create(labels_file)?) - } else { - Ok(()) - } - } - } } impl EdgeVecTrait for EdgeVec { #[inline] - fn get_offsets(&self) -> &[usize] { + fn get_offsets(&self) -> &OffsetIndex { &self.offsets } @@ -203,6 +319,277 @@ impl EdgeVecTrait for EdgeVec { impl Default for EdgeVec { fn default() -> Self { - EdgeVec::new(Vec::new(), Vec::new()) + EdgeVec::new(vec![0], Vec::new()) + } +} + +/// Add two `EdgeVec`s following the rules: +/// * The `edges` will be the merged vector, duplication will be removed. +/// * The `labels` if some, will be the merged vector. We assume that the label is the same +/// for two same edges (same `src` and `dst`) is the same, hence the label will be randomly +/// picked up in either `EdgeVec`. If they contain different labels, it will end with indefinite +/// results. +/// * The `offsets` will be of the length of the longer one, and reshifted according to the +/// merged `edges`. +/// +/// # Panic +/// +/// One `EdgeVec` has `Some(labels)`, but the other has `None`. +impl Add for EdgeVec { + type Output = EdgeVec; + + fn add(self, other: EdgeVec) -> Self::Output { + assert_eq!(self.labels.is_some(), other.labels.is_some()); + let (smaller, larger) = if self.offsets.len() <= other.offsets.len() { + (self, other) + } else { + (other, self) + }; + + if smaller.offsets.is_empty() { + return larger; + } + + // let len = smaller.edges.len() + larger.edges.len(); + let s_num_nodes = smaller.offsets.len() - 1; + let num_nodes = larger.offsets.len() - 1; + + let mut edges = Vec::new(); //Vec::with_capacity(len); + let mut labels = Vec::new(); //Vec::with_capacity(len); + let mut offsets = OffsetIndex::new(); //Vec::with_capacity(num_nodes + 1); + offsets.push(0); + + for node in 0..s_num_nodes { + let (s1, e1) = (smaller.offsets.index(node), smaller.offsets.index(node + 1)); + let (s2, e2) = (larger.offsets.index(node), larger.offsets.index(node + 1)); + let mut num_nbrs = 0; + + if smaller.labels.is_none() { + let merged_nbrs = smaller + .edges + .iter() + .skip(s1) + .take(e1 - s1) + .merge(larger.edges.iter().skip(s2).take(e2 - s2)) + .dedup(); + + for &nbr in merged_nbrs { + edges.push(nbr); + num_nbrs += 1; + } + } else { + let merged_nbrs = smaller + .edges + .iter() + .skip(s1) + .take(e1 - s1) + .zip( + smaller + .labels + .as_ref() + .unwrap() + .iter() + .skip(s1) + .take(e1 - s1), + ) + .merge( + larger.edges.iter().skip(s2).take(e2 - s2).zip( + larger + .labels + .as_ref() + .unwrap() + .iter() + .skip(s2) + .take(e2 - s2), + ), + ) + .unique_by(|x| x.0); + + for (&nbr, &lab) in merged_nbrs { + edges.push(nbr); + labels.push(lab); + num_nbrs += 1; + } + } + + let offset = offsets.last().unwrap() + num_nbrs; + offsets.push(offset); + } + + if s_num_nodes < num_nodes { + let last_off = larger.offsets.index(s_num_nodes); + edges.extend(larger.edges.iter().skip(last_off)); + if larger.labels.is_some() { + labels.extend(larger.labels.as_ref().unwrap().iter().skip(last_off)); + } + + let extra_off = offsets.last().unwrap() - larger.offsets.index(s_num_nodes); + offsets.extend( + larger + .offsets + .iter() + .skip(s_num_nodes + 1) + .map(|x| x + extra_off), + ); + } + + EdgeVec { + offsets, + edges, + labels: if smaller.labels.is_none() { + None + } else { + Some(labels) + }, + } + } +} + +#[cfg(test)] +mod test { + use super::*; + + #[test] + fn test_offset_index() { + let offset_index = OffsetIndex { + base_level: vec![0, 1, 2, 3, 4, 5], + second_level: vec![3, 5, 6], + }; + + let exp_offsets = vec![ + 0_usize, + 1, + 2, + 1 << BITS_U32 | 3, + 1 << BITS_U32 | 4, + 2 << BITS_U32 | 5, + ]; + let mut i = 0; + while i < offset_index.len() { + let offset = offset_index.index(i); + assert_eq!(exp_offsets[i], offset); + i += 1; + } + + let mut offset_index = OffsetIndex::new(); + offset_index.push(0); + offset_index.push(1); + assert_eq!(offset_index.len(), 2); + assert_eq!(offset_index.index(0), 0); + assert_eq!(offset_index.index(1), 1); + + offset_index.push(1 << BITS_U32 | 3); + assert_eq!(offset_index.last().unwrap(), 1 << BITS_U32 | 3); + + offset_index.push(5 << BITS_U32 | 4); + assert_eq!(offset_index.last().unwrap(), 5 << BITS_U32 | 4); + + let offset_index = OffsetIndex::from(exp_offsets.clone()); + let mut i = 0; + while i < offset_index.len() { + let offset = offset_index.index(i); + assert_eq!(exp_offsets[i], offset); + i += 1; + } + } + + #[test] + fn test_merge() { + let edges1 = EdgeVec::::new(vec![0, 2, 4, 6, 8], vec![1_u32, 3, 0, 2, 1, 3, 0, 2]); + + let edges2 = EdgeVec::::new(vec![0, 1, 2, 3, 4], vec![2_u32, 3, 0, 1]); + + let edges = edges1 + edges2; + + assert_eq!(edges.offsets, vec![0, 3, 6, 9, 12].into()); + + assert_eq!(edges.edges, vec![1, 2, 3, 0, 2, 3, 0, 1, 3, 0, 1, 2]); + + assert!(edges.labels.is_none()); + } + + #[test] + fn test_merge_with_label() { + let edges1 = EdgeVec::::with_labels( + vec![0, 2, 4, 6, 8], + vec![1_u32, 3, 0, 2, 1, 3, 0, 2], + vec![1, 3, 1, 3, 3, 5, 3, 5], + ); + + let edges2 = EdgeVec::::with_labels( + vec![0, 1, 2, 3, 4], + vec![2_u32, 3, 0, 1], + vec![2, 4, 2, 4], + ); + + let edges = edges1 + edges2; + + assert_eq!(edges.offsets, vec![0, 3, 6, 9, 12].into()); + + assert_eq!(edges.edges, vec![1, 2, 3, 0, 2, 3, 0, 1, 3, 0, 1, 2]); + + assert_eq!(edges.labels, Some(vec![1, 2, 3, 1, 3, 4, 2, 3, 5, 3, 4, 5])); + } + + #[test] + fn test_merge_with_comm_edges() { + let edges1 = EdgeVec::::with_labels( + vec![0, 2, 4, 6, 8], + vec![1_u32, 3, 0, 2, 1, 3, 0, 2], + vec![1, 3, 1, 3, 3, 5, 3, 5], + ); + + let edges2 = EdgeVec::::with_labels( + vec![0, 2, 5, 7, 8], + vec![1_u32, 2, 0, 2, 3, 0, 1, 1], + vec![1, 2, 1, 3, 4, 2, 3, 4], + ); + + let edges = edges1 + edges2; + + assert_eq!(edges.offsets, vec![0, 3, 6, 9, 12].into()); + + assert_eq!(edges.edges, vec![1, 2, 3, 0, 2, 3, 0, 1, 3, 0, 1, 2]); + + assert_eq!(edges.labels, Some(vec![1, 2, 3, 1, 3, 4, 2, 3, 5, 3, 4, 5])); + } + + #[test] + fn test_merge_with_more_nodes() { + let edges1 = EdgeVec::::with_labels( + vec![0, 2, 4, 6], + vec![1_u32, 2, 0, 2, 0, 1], + vec![1, 2, 1, 3, 2, 3], + ); + + let edges2 = EdgeVec::::with_labels( + vec![0, 1, 2, 3, 6, 7], + vec![3_u32, 3, 3, 0, 1, 2, 2], + vec![3, 4, 5, 3, 4, 5, 6], + ); + + let edges = edges1 + edges2; + + assert_eq!(edges.offsets, vec![0, 3, 6, 9, 12, 13].into()); + + assert_eq!(edges.edges, vec![1, 2, 3, 0, 2, 3, 0, 1, 3, 0, 1, 2, 2]); + + assert_eq!( + edges.labels, + Some(vec![1, 2, 3, 1, 3, 4, 2, 3, 5, 3, 4, 5, 6]) + ); + } + + #[test] + fn test_merge_with_empty_edges() { + let edges1 = EdgeVec::::new(vec![0, 0], vec![]); + + let edges2 = EdgeVec::::new(vec![0, 1, 2, 3, 6, 7], vec![3_u32, 3, 3, 0, 1, 2, 2]); + + let edges = edges1 + edges2; + + assert_eq!(edges.offsets, vec![0, 1, 2, 3, 6, 7].into()); + + assert_eq!(edges.edges, vec![3_u32, 3, 3, 0, 1, 2, 2]); } } diff --git a/src/graph_impl/static_graph/graph.rs b/src/graph_impl/static_graph/graph.rs index 395b7d6f..6b88e6d8 100644 --- a/src/graph_impl/static_graph/graph.rs +++ b/src/graph_impl/static_graph/graph.rs @@ -1,665 +1,705 @@ -/* - * Copyright (c) 2018 UNSW Sydney, Data and Knowledge Group. - * - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you 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. - */ -use std::borrow::Cow; -use std::hash::{Hash, Hasher}; -use std::marker::PhantomData; - -use bincode::Result; -use itertools::Itertools; -use serde; - -use generic::{ - DefaultId, DefaultTy, DiGraphTrait, Directed, EdgeType, GeneralGraph, GraphLabelTrait, - GraphTrait, GraphType, IdType, Iter, NodeType, UnGraphTrait, Undirected, -}; -use graph_impl::static_graph::mmap::graph_mmap::StaticGraphMmapAux; -use graph_impl::static_graph::node::StaticNode; -use graph_impl::static_graph::static_edge_iter::StaticEdgeIndexIter; -use graph_impl::static_graph::{EdgeVec, EdgeVecTrait}; -use graph_impl::{Edge, GraphImpl}; -use io::mmap::dump; -use io::serde::{Deserialize, Serialize, Serializer}; -use map::SetMap; - -pub type TypedUnStaticGraph = TypedStaticGraph; -pub type TypedDiStaticGraph = TypedStaticGraph; -pub type StaticGraph = - TypedStaticGraph; -pub type UnStaticGraph = StaticGraph; -pub type DiStaticGraph = StaticGraph; - -/// `StaticGraph` is a memory-compact graph data structure. -/// The labels of both nodes and edges, if exist, are encoded as `Integer`. -#[derive(Debug, Clone, Default, Serialize, Deserialize)] -pub struct TypedStaticGraph -{ - num_nodes: usize, - num_edges: usize, - edge_vec: EdgeVec, - in_edge_vec: Option>, - // Maintain the node's labels, whose index is aligned with `offsets`. - labels: Option>, - // A marker of thr graph type, namely, directed or undirected. - graph_type: PhantomData, - // A map of node labels. - node_label_map: SetMap, - // A map of edge labels. - edge_label_map: SetMap, -} - -impl PartialEq - for TypedStaticGraph -{ - fn eq(&self, other: &TypedStaticGraph) -> bool { - if !self.node_count() == other.node_count() || !self.edge_count() == other.edge_count() { - return false; - } - - for n in self.node_indices() { - if !other.has_node(n) || self.get_node_label(n) != other.get_node_label(n) { - return false; - } - } - - for (s, d) in self.edge_indices() { - if !other.has_edge(s, d) || self.get_edge_label(s, d) != other.get_edge_label(s, d) { - return false; - } - } - - true - } -} - -impl Eq - for TypedStaticGraph -{} - -impl Hash - for TypedStaticGraph -{ - fn hash(&self, state: &mut H) { - { - let nodes = self.node_indices().sorted(); - nodes.hash(state); - - let node_labels = nodes - .into_iter() - .map(|n| self.get_node_label(n)) - .collect_vec(); - node_labels.hash(state); - } - { - let edges = self.edge_indices().sorted(); - edges.hash(state); - let edge_labels = edges - .into_iter() - .map(|(s, d)| self.get_edge_label(s, d)) - .collect_vec(); - edge_labels.hash(state); - } - } -} -impl Serialize - for TypedStaticGraph -where - Id: serde::Serialize, - NL: serde::Serialize, - EL: serde::Serialize, - L: serde::Serialize, -{} - -impl Deserialize - for TypedStaticGraph -where - Id: for<'de> serde::Deserialize<'de>, - NL: for<'de> serde::Deserialize<'de>, - EL: for<'de> serde::Deserialize<'de>, - L: for<'de> serde::Deserialize<'de>, -{} - -impl - TypedStaticGraph -{ - pub fn new( - edges: EdgeVec, - in_edges: Option>, - num_nodes: Option, - num_edges: Option, - ) -> Self { - if Ty::is_directed() { - if in_edges.is_none() { - panic!("In edges should be provided for directed graph."); - } - - let num_of_in_edges = in_edges.as_ref().unwrap().num_edges(); - let num_of_out_edges = edges.num_edges(); - if num_of_in_edges != num_of_out_edges { - debug!( - "{} out edges but {} in edges.", - num_of_out_edges, num_of_in_edges - ); - } - } - - let num_nodes = if let Some(num) = num_nodes { - if num != edges.num_nodes() { - debug!( - "number of nodes ({}) does not match the length of edge vector ({})", - num, - edges.num_nodes() - ); - } - num - } else { - edges.num_nodes() - }; - - let num_edges = if let Some(num) = num_edges { - num - } else { - if Ty::is_directed() { - edges.num_edges() - } else { - edges.num_edges() >> 1 - } - }; - - TypedStaticGraph { - num_nodes, - num_edges, - edge_vec: edges, - in_edge_vec: in_edges, - labels: None, - node_label_map: SetMap::::new(), - edge_label_map: SetMap::::new(), - graph_type: PhantomData, - } - } - - pub fn with_labels( - edges: EdgeVec, - in_edges: Option>, - labels: Vec, - node_label_map: SetMap, - edge_label_map: SetMap, - num_nodes: Option, - num_edges: Option, - ) -> Self { - if Ty::is_directed() { - if in_edges.is_none() { - panic!("In edges should be provided for directed graph."); - } - - let num_of_in_edges = in_edges.as_ref().unwrap().num_edges(); - let num_of_out_edges = edges.num_edges(); - if num_of_in_edges != num_of_out_edges { - debug!( - "{} out edges but {} in edges.", - num_of_out_edges, num_of_in_edges - ); - } - } - - let num_nodes = if let Some(num) = num_nodes { - if num != edges.num_nodes() { - debug!( - "number of nodes ({}) does not match the length of edge vector ({})", - num, - edges.num_nodes() - ); - } - num - } else { - edges.num_nodes() - }; - - let num_edges = if let Some(num) = num_edges { - num - } else { - if Ty::is_directed() { - edges.num_edges() - } else { - edges.num_edges() >> 1 - } - }; - - if num_nodes != labels.len() { - debug!("{} nodes, but {} labels", num_nodes, labels.len()); - } - - TypedStaticGraph { - num_nodes, - num_edges, - edge_vec: edges, - in_edge_vec: in_edges, - labels: Some(labels), - node_label_map, - edge_label_map, - graph_type: PhantomData, - } - } - - pub fn from_raw( - num_nodes: usize, - num_edges: usize, - edge_vec: EdgeVec, - in_edge_vec: Option>, - labels: Option>, - node_label_map: SetMap, - edge_label_map: SetMap, - ) -> Self { - if num_nodes != edge_vec.num_nodes() { - debug!( - "number of nodes ({}) does not match the length of edge vector ({})", - num_nodes, - edge_vec.num_nodes() - ); - } - - if Ty::is_directed() { - if Ty::is_directed() { - if in_edge_vec.is_none() { - panic!("In edges should be provided for directed graph."); - } - - let num_of_in_edges = in_edge_vec.as_ref().unwrap().num_edges(); - let num_of_out_edges = edge_vec.num_edges(); - if num_of_in_edges != num_of_out_edges { - debug!( - "{} out edges but {} in edges.", - num_of_out_edges, num_of_in_edges - ); - } - } - - if num_edges != edge_vec.num_edges() { - warn!( - "Directed: num_edges {}, edge_vec {} edges", - num_edges, - edge_vec.num_edges() - ); - } - } else if num_edges != edge_vec.num_edges() >> 1 { - warn!( - "undirected: num_edges {}, edge_vec {} edges, graph may contain self loop.", - num_edges, - edge_vec.num_edges() - ); - } - - if labels.is_some() { - let num_of_labels = labels.as_ref().unwrap().len(); - if num_nodes != num_of_labels { - debug!( - "there are {} nodes, but {} labels", - num_nodes, num_of_labels - ); - } - } - - TypedStaticGraph { - num_nodes, - num_edges, - edge_vec, - in_edge_vec, - labels, - node_label_map, - edge_label_map, - graph_type: PhantomData, - } - } - - #[inline] - pub fn get_edge_vec(&self) -> &EdgeVec { - &self.edge_vec - } - - #[inline] - pub fn get_in_edge_vec(&self) -> &Option> { - &self.in_edge_vec - } - - #[inline] - pub fn get_labels(&self) -> &Option> { - &self.labels - } - - #[inline] - pub fn get_node_label_map(&self) -> &SetMap { - &self.node_label_map - } - - #[inline] - pub fn get_edge_label_map(&self) -> &SetMap { - &self.edge_label_map - } - - #[inline] - pub fn get_edge_vec_mut(&mut self) -> &mut EdgeVec { - &mut self.edge_vec - } - - #[inline] - pub fn get_in_edge_vec_mut(&mut self) -> &mut Option> { - &mut self.in_edge_vec - } - - #[inline] - pub fn get_labels_mut(&mut self) -> &mut Option> { - &mut self.labels - } - - #[inline] - pub fn get_node_label_map_mut(&mut self) -> &mut SetMap { - &mut self.node_label_map - } - - #[inline] - pub fn get_edge_label_map_mut(&mut self) -> &mut SetMap { - &mut self.edge_label_map - } - - #[inline] - pub fn remove_node_labels(&mut self) { - self.labels = None; - self.node_label_map = SetMap::new(); - } - - #[inline] - pub fn remove_edge_labels(&mut self) { - self.edge_vec.remove_labels(); - self.edge_label_map = SetMap::new(); - if let Some(ref mut e) = self.in_edge_vec.as_mut() { - e.remove_labels() - } - } - - #[inline] - pub fn remove_labels(&mut self) { - self.remove_node_labels(); - self.remove_edge_labels(); - } - - #[inline] - pub fn shrink_to_fit(&mut self) { - self.edge_vec.shrink_to_fit(); - if let Some(ref mut in_edge_vec) = self.in_edge_vec { - in_edge_vec.shrink_to_fit(); - } - if let Some(ref mut labels) = self.labels { - labels.shrink_to_fit(); - } - } - #[inline] - pub fn find_edge_index(&self, start: Id, target: Id) -> Option { - self.edge_vec.find_edge_index(start, target) - } -} - -impl - TypedStaticGraph -where - NL: serde::Serialize + Clone, - EL: serde::Serialize + Clone, -{ - pub fn dump_mmap(&self, prefix: &str) -> Result<()> { - let edges_prefix = format!("{}_OUT", prefix); - let in_edges_prefix = format!("{}_IN", prefix); - let label_file = format!("{}.labels", prefix); - - let aux_map_file = format!("{}_aux.bin", prefix); - - self.edge_vec.dump_mmap(&edges_prefix)?; - if let Some(ref in_edges) = self.in_edge_vec { - in_edges.dump_mmap(&in_edges_prefix)?; - } - - if let Some(ref labels) = self.labels { - unsafe { - dump(labels, ::std::fs::File::create(label_file)?)?; - } - } - - let aux_file = StaticGraphMmapAux::new( - self.num_nodes, - self.num_edges, - self.node_label_map.clone(), - self.edge_label_map.clone(), - ); - - Serializer::export(&aux_file, &aux_map_file)?; - - Ok(()) - } -} - -impl GraphTrait - for TypedStaticGraph -{ - #[inline] - fn get_node(&self, id: Id) -> NodeType { - if !self.has_node(id) { - return NodeType::None; - } - - match self.labels { - Some(ref labels) => NodeType::StaticNode(StaticNode::new_static(id, labels[id.id()])), - None => NodeType::StaticNode(StaticNode::new(id, None)), - } - } - - #[inline] - fn get_edge(&self, start: Id, target: Id) -> EdgeType { - if !self.has_edge(start, target) { - return EdgeType::None; - } - - let _label = self.edge_vec.find_edge_label_id(start, target); - match _label { - Some(label) => EdgeType::Edge(Edge::new_static(start, target, *label)), - None => EdgeType::Edge(Edge::new(start, target, None)), - } - } - - #[inline] - fn has_node(&self, id: Id) -> bool { - id.id() < self.num_nodes - } - - #[inline] - fn has_edge(&self, start: Id, target: Id) -> bool { - self.edge_vec.has_edge(start, target) - } - - #[inline] - fn node_count(&self) -> usize { - self.num_nodes - } - - #[inline] - fn edge_count(&self) -> usize { - self.num_edges - } - - #[inline(always)] - fn is_directed(&self) -> bool { - Ty::is_directed() - } - - #[inline] - fn node_indices(&self) -> Iter { - Iter::new(Box::new((0..self.num_nodes).map(Id::new))) - } - - #[inline] - fn edge_indices(&self) -> Iter<(Id, Id)> { - Iter::new(Box::new(StaticEdgeIndexIter::new( - Box::new(&self.edge_vec), - self.is_directed(), - ))) - } - - #[inline] - fn nodes(&self) -> Iter> { - match self.labels { - None => { - let node_iter = self - .node_indices() - .map(|i| NodeType::StaticNode(StaticNode::new(i, None))); - - Iter::new(Box::new(node_iter)) - } - Some(ref labels) => { - let node_iter = self - .node_indices() - .zip(labels.iter()) - .map(|n| NodeType::StaticNode(StaticNode::new_static(n.0, *n.1))); - - Iter::new(Box::new(node_iter)) - } - } - } - - #[inline] - fn edges(&self) -> Iter> { - let labels = self.edge_vec.get_labels(); - if labels.is_empty() { - Iter::new(Box::new( - self.edge_indices() - .map(|i| EdgeType::Edge(Edge::new(i.0, i.1, None))), - )) - } else { - Iter::new(Box::new(self.edge_indices().zip(labels.iter()).map(|e| { - EdgeType::Edge(Edge::new_static((e.0).0, (e.0).1, *e.1)) - }))) - } - } - - #[inline] - fn degree(&self, id: Id) -> usize { - self.edge_vec.degree(id) - } - - #[inline] - fn neighbors_iter(&self, id: Id) -> Iter { - let neighbors = self.edge_vec.neighbors(id); - - Iter::new(Box::new(neighbors.iter().map(|x| *x))) - } - - #[inline] - fn neighbors(&self, id: Id) -> Cow<[Id]> { - self.edge_vec.neighbors(id).into() - } - - #[inline] - fn max_seen_id(&self) -> Option { - Some(Id::new(self.node_count() - 1)) - } - - #[inline] - fn implementation(&self) -> GraphImpl { - GraphImpl::StaticGraph - } -} - -impl - GraphLabelTrait for TypedStaticGraph -{ - #[inline(always)] - fn get_node_label_map(&self) -> &SetMap { - &self.node_label_map - } - - #[inline(always)] - fn get_edge_label_map(&self) -> &SetMap { - &self.edge_label_map - } -} - -impl UnGraphTrait - for TypedUnStaticGraph -{} - -impl DiGraphTrait - for TypedDiStaticGraph -{ - #[inline] - fn in_degree(&self, id: Id) -> usize { - self.in_neighbors(id).len() - } - - #[inline] - fn in_neighbors_iter(&self, id: Id) -> Iter { - let in_neighbors = self.in_edge_vec.as_ref().unwrap().neighbors(id); - - Iter::new(Box::new(in_neighbors.iter().map(|x| *x))) - } - - #[inline] - fn in_neighbors(&self, id: Id) -> Cow<[Id]> { - self.in_edge_vec.as_ref().unwrap().neighbors(id).into() - } -} - -impl GeneralGraph - for TypedUnStaticGraph -{ - #[inline(always)] - fn as_graph(&self) -> &GraphTrait { - self - } - - #[inline(always)] - fn as_labeled_graph(&self) -> &GraphLabelTrait { - self - } - - #[inline(always)] - fn as_general_graph(&self) -> &GeneralGraph { - self - } -} - -impl GeneralGraph - for TypedDiStaticGraph -{ - #[inline(always)] - fn as_graph(&self) -> &GraphTrait { - self - } - - #[inline(always)] - fn as_labeled_graph(&self) -> &GraphLabelTrait { - self - } - - #[inline(always)] - fn as_general_graph(&self) -> &GeneralGraph { - self - } - - #[inline(always)] - fn as_digraph(&self) -> Option<&DiGraphTrait> { - Some(self) - } -} +/* + * Copyright (c) 2018 UNSW Sydney, Data and Knowledge Group. + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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. + */ +use std::borrow::Cow; +use std::hash::{Hash, Hasher}; +use std::marker::PhantomData; + +use itertools::Itertools; +use serde; + +use crate::generic::{ + DefaultId, DefaultTy, DiGraphTrait, Directed, EdgeType, GeneralGraph, GraphLabelTrait, + GraphTrait, GraphType, IdType, Iter, MapTrait, MutMapTrait, NodeType, UnGraphTrait, Undirected, +}; +use crate::graph_impl::static_graph::node::StaticNode; +use crate::graph_impl::static_graph::static_edge_iter::StaticEdgeIndexIter; +use crate::graph_impl::static_graph::{EdgeVec, EdgeVecTrait}; +use crate::graph_impl::{Edge, GraphImpl}; +use crate::io::serde::{Deserialize, Serialize}; +use crate::map::SetMap; +use std::ops::Add; + +pub type TypedUnStaticGraph = TypedStaticGraph; +pub type TypedDiStaticGraph = TypedStaticGraph; +pub type StaticGraph = + TypedStaticGraph; +pub type UnStaticGraph = StaticGraph; +pub type DiStaticGraph = StaticGraph; + +/// `StaticGraph` is a memory-compact graph data structure. +/// The labels of both nodes and edges, if exist, are encoded as `Integer`. +#[derive(Debug, Clone, Default, Serialize, Deserialize)] +pub struct TypedStaticGraph +{ + num_nodes: usize, + num_edges: usize, + edge_vec: EdgeVec, + in_edge_vec: Option>, + // Maintain the node's labels, whose index is aligned with `offsets`. + labels: Option>, + // A marker of thr graph type, namely, directed or undirected. + graph_type: PhantomData, + // A map of node labels. + node_label_map: SetMap, + // A map of edge labels. + edge_label_map: SetMap, +} + +impl PartialEq + for TypedStaticGraph +{ + fn eq(&self, other: &TypedStaticGraph) -> bool { + if !self.node_count() == other.node_count() || !self.edge_count() == other.edge_count() { + return false; + } + + for n in self.node_indices() { + if !other.has_node(n) || self.get_node_label(n) != other.get_node_label(n) { + return false; + } + } + + for (s, d) in self.edge_indices() { + if !other.has_edge(s, d) || self.get_edge_label(s, d) != other.get_edge_label(s, d) { + return false; + } + } + + true + } +} + +impl Eq + for TypedStaticGraph +{ +} + +impl Hash + for TypedStaticGraph +{ + fn hash(&self, state: &mut H) { + { + let nodes = self.node_indices().sorted(); + nodes.hash(state); + + let node_labels = nodes + .into_iter() + .map(|n| self.get_node_label(n)) + .collect_vec(); + node_labels.hash(state); + } + { + let edges = self.edge_indices().sorted(); + edges.hash(state); + let edge_labels = edges + .into_iter() + .map(|(s, d)| self.get_edge_label(s, d)) + .collect_vec(); + edge_labels.hash(state); + } + } +} + +impl Serialize + for TypedStaticGraph +where + Id: serde::Serialize, + NL: serde::Serialize, + EL: serde::Serialize, + L: serde::Serialize, +{ +} + +impl Deserialize + for TypedStaticGraph +where + Id: for<'de> serde::Deserialize<'de>, + NL: for<'de> serde::Deserialize<'de>, + EL: for<'de> serde::Deserialize<'de>, + L: for<'de> serde::Deserialize<'de>, +{ +} + +impl + TypedStaticGraph +{ + pub fn empty() -> Self { + Self::new(EdgeVec::default(), None, None, None) + } + + pub fn new( + edges: EdgeVec, + in_edges: Option>, + num_nodes: Option, + num_edges: Option, + ) -> Self { + if Ty::is_directed() { + if in_edges.is_none() { + panic!("In edges should be provided for directed graph."); + } + + let num_of_in_edges = in_edges.as_ref().unwrap().num_edges(); + let num_of_out_edges = edges.num_edges(); + if num_of_in_edges != num_of_out_edges { + debug!( + "{} out edges but {} in edges.", + num_of_out_edges, num_of_in_edges + ); + } + } + + let num_nodes = if let Some(num) = num_nodes { + if num != edges.num_nodes() { + debug!( + "number of nodes ({}) does not match the length of edge vector ({})", + num, + edges.num_nodes() + ); + } + num + } else { + edges.num_nodes() + }; + + let num_edges = if let Some(num) = num_edges { + num + } else if Ty::is_directed() { + edges.num_edges() + } else { + edges.num_edges() >> 1 + }; + + TypedStaticGraph { + num_nodes, + num_edges, + edge_vec: edges, + in_edge_vec: in_edges, + labels: None, + node_label_map: SetMap::::new(), + edge_label_map: SetMap::::new(), + graph_type: PhantomData, + } + } + + pub fn with_labels( + edges: EdgeVec, + in_edges: Option>, + labels: Vec, + node_label_map: SetMap, + edge_label_map: SetMap, + num_nodes: Option, + num_edges: Option, + ) -> Self { + if Ty::is_directed() { + if in_edges.is_none() { + panic!("In edges should be provided for directed graph."); + } + + let num_of_in_edges = in_edges.as_ref().unwrap().num_edges(); + let num_of_out_edges = edges.num_edges(); + if num_of_in_edges != num_of_out_edges { + debug!( + "{} out edges but {} in edges.", + num_of_out_edges, num_of_in_edges + ); + } + } + + let num_nodes = if let Some(num) = num_nodes { + if num != edges.num_nodes() { + debug!( + "Number of nodes ({}) does not match the length of edge vector ({})", + num, + edges.num_nodes() + ); + } + num + } else { + edges.num_nodes() + }; + + let num_edges = if let Some(num) = num_edges { + num + } else if Ty::is_directed() { + edges.num_edges() + } else { + edges.num_edges() >> 1 + }; + + if num_nodes != labels.len() { + debug!("{} nodes, but {} labels", num_nodes, labels.len()); + } + + TypedStaticGraph { + num_nodes, + num_edges, + edge_vec: edges, + in_edge_vec: in_edges, + labels: Some(labels), + node_label_map, + edge_label_map, + graph_type: PhantomData, + } + } + + pub fn from_raw( + num_nodes: usize, + num_edges: usize, + edge_vec: EdgeVec, + in_edge_vec: Option>, + labels: Option>, + node_label_map: SetMap, + edge_label_map: SetMap, + ) -> Self { + if num_nodes != edge_vec.num_nodes() { + debug!( + "Number of nodes ({}) does not match the length of edge vector ({})", + num_nodes, + edge_vec.num_nodes() + ); + } + + if Ty::is_directed() { + if Ty::is_directed() { + if in_edge_vec.is_none() { + panic!("In edges should be provided for directed graph."); + } + + let num_of_in_edges = in_edge_vec.as_ref().unwrap().num_edges(); + let num_of_out_edges = edge_vec.num_edges(); + if num_of_in_edges != num_of_out_edges { + debug!( + "{} out edges but {} in edges.", + num_of_out_edges, num_of_in_edges + ); + } + } + + if num_edges != edge_vec.num_edges() { + debug!( + "Directed: num_edges {}, edge_vec {} edges", + num_edges, + edge_vec.num_edges() + ); + } + } else if num_edges != edge_vec.num_edges() >> 1 { + debug!( + "Undirected: num_edges {}, edge_vec {} edges", + num_edges, + edge_vec.num_edges() + ); + } + + if labels.is_some() { + let num_of_labels = labels.as_ref().unwrap().len(); + if num_nodes != num_of_labels { + debug!( + "There are {} nodes, but {} labels", + num_nodes, num_of_labels + ); + } + } + + TypedStaticGraph { + num_nodes, + num_edges, + edge_vec, + in_edge_vec, + labels, + node_label_map, + edge_label_map, + graph_type: PhantomData, + } + } + + #[inline] + pub fn get_edge_vec(&self) -> &EdgeVec { + &self.edge_vec + } + + #[inline] + pub fn get_in_edge_vec(&self) -> &Option> { + &self.in_edge_vec + } + + #[inline] + pub fn get_labels(&self) -> &Option> { + &self.labels + } + + #[inline] + pub fn get_node_label_map(&self) -> &SetMap { + &self.node_label_map + } + + #[inline] + pub fn get_edge_label_map(&self) -> &SetMap { + &self.edge_label_map + } + + #[inline] + pub fn get_edge_vec_mut(&mut self) -> &mut EdgeVec { + &mut self.edge_vec + } + + #[inline] + pub fn get_in_edge_vec_mut(&mut self) -> &mut Option> { + &mut self.in_edge_vec + } + + #[inline] + pub fn get_labels_mut(&mut self) -> &mut Option> { + &mut self.labels + } + + #[inline] + pub fn get_node_label_map_mut(&mut self) -> &mut SetMap { + &mut self.node_label_map + } + + #[inline] + pub fn get_edge_label_map_mut(&mut self) -> &mut SetMap { + &mut self.edge_label_map + } + + #[inline] + pub fn remove_node_labels(&mut self) { + self.labels = None; + self.node_label_map = SetMap::new(); + } + + #[inline] + pub fn remove_edge_labels(&mut self) { + self.edge_vec.remove_labels(); + self.edge_label_map = SetMap::new(); + if let Some(ref mut e) = self.in_edge_vec.as_mut() { + e.remove_labels() + } + } + + #[inline] + pub fn remove_labels(&mut self) { + self.remove_node_labels(); + self.remove_edge_labels(); + } + + #[inline] + pub fn shrink_to_fit(&mut self) { + self.edge_vec.shrink_to_fit(); + if let Some(ref mut in_edge_vec) = self.in_edge_vec { + in_edge_vec.shrink_to_fit(); + } + if let Some(ref mut labels) = self.labels { + labels.shrink_to_fit(); + } + } + #[inline] + pub fn find_edge_index(&self, start: Id, target: Id) -> Option { + self.edge_vec.find_edge_index(start, target) + } +} + +impl GraphTrait + for TypedStaticGraph +{ + #[inline] + fn get_node(&self, id: Id) -> NodeType { + if !self.has_node(id) { + return NodeType::None; + } + + match self.labels { + Some(ref labels) => NodeType::StaticNode(StaticNode::new_static(id, labels[id.id()])), + None => NodeType::StaticNode(StaticNode::new(id, None)), + } + } + + #[inline] + fn get_edge(&self, start: Id, target: Id) -> EdgeType { + if !self.has_edge(start, target) { + return EdgeType::None; + } + + let _label = self.edge_vec.find_edge_label_id(start, target); + match _label { + Some(label) => EdgeType::Edge(Edge::new_static(start, target, *label)), + None => EdgeType::Edge(Edge::new(start, target, None)), + } + } + + #[inline] + fn has_node(&self, id: Id) -> bool { + id.id() < self.num_nodes + } + + #[inline] + fn has_edge(&self, start: Id, target: Id) -> bool { + self.edge_vec.has_edge(start, target) + } + + #[inline] + fn node_count(&self) -> usize { + self.num_nodes + } + + #[inline] + fn edge_count(&self) -> usize { + self.num_edges + } + + #[inline(always)] + fn is_directed(&self) -> bool { + Ty::is_directed() + } + + #[inline] + fn node_indices(&self) -> Iter { + Iter::new(Box::new((0..self.num_nodes).map(Id::new))) + } + + #[inline] + fn edge_indices(&self) -> Iter<(Id, Id)> { + Iter::new(Box::new(StaticEdgeIndexIter::new( + Box::new(&self.edge_vec), + self.is_directed(), + ))) + } + + #[inline] + fn nodes(&self) -> Iter> { + match self.labels { + None => { + let node_iter = self + .node_indices() + .map(|i| NodeType::StaticNode(StaticNode::new(i, None))); + + Iter::new(Box::new(node_iter)) + } + Some(ref labels) => { + let node_iter = self + .node_indices() + .zip(labels.iter()) + .map(|n| NodeType::StaticNode(StaticNode::new_static(n.0, *n.1))); + + Iter::new(Box::new(node_iter)) + } + } + } + + #[inline] + fn edges(&self) -> Iter> { + let labels = self.edge_vec.get_labels(); + if labels.is_empty() { + Iter::new(Box::new( + self.edge_indices() + .map(|i| EdgeType::Edge(Edge::new(i.0, i.1, None))), + )) + } else { + Iter::new(Box::new(self.edge_indices().zip(labels.iter()).map(|e| { + EdgeType::Edge(Edge::new_static((e.0).0, (e.0).1, *e.1)) + }))) + } + } + + #[inline] + fn degree(&self, id: Id) -> usize { + self.edge_vec.degree(id) + } + + #[inline] + fn total_degree(&self, id: Id) -> usize { + let mut degree = self.degree(id); + if self.is_directed() { + degree += self.in_edge_vec.as_ref().unwrap().neighbors(id).len() + } + + degree + } + + #[inline] + fn neighbors_iter(&self, id: Id) -> Iter { + let neighbors = self.edge_vec.neighbors(id); + + Iter::new(Box::new(neighbors.iter().map(|x| *x))) + } + + #[inline] + fn neighbors(&self, id: Id) -> Cow<[Id]> { + self.edge_vec.neighbors(id).into() + } + + #[inline] + fn max_seen_id(&self) -> Option { + Some(Id::new(self.node_count() - 1)) + } + + #[inline] + fn implementation(&self) -> GraphImpl { + GraphImpl::StaticGraph + } +} + +impl + GraphLabelTrait for TypedStaticGraph +{ + #[inline(always)] + fn get_node_label_map(&self) -> &SetMap { + &self.node_label_map + } + + #[inline(always)] + fn get_edge_label_map(&self) -> &SetMap { + &self.edge_label_map + } +} + +impl UnGraphTrait + for TypedUnStaticGraph +{ +} + +impl DiGraphTrait + for TypedDiStaticGraph +{ + #[inline] + fn in_degree(&self, id: Id) -> usize { + self.in_neighbors(id).len() + } + + #[inline] + fn in_neighbors_iter(&self, id: Id) -> Iter { + let in_neighbors = self.in_edge_vec.as_ref().unwrap().neighbors(id); + + Iter::new(Box::new(in_neighbors.iter().map(|x| *x))) + } + + #[inline] + fn in_neighbors(&self, id: Id) -> Cow<[Id]> { + self.in_edge_vec.as_ref().unwrap().neighbors(id).into() + } +} + +impl GeneralGraph + for TypedUnStaticGraph +{ + #[inline(always)] + fn as_graph(&self) -> &dyn GraphTrait { + self + } + + #[inline(always)] + fn as_labeled_graph(&self) -> &dyn GraphLabelTrait { + self + } + + #[inline(always)] + fn as_general_graph(&self) -> &dyn GeneralGraph { + self + } +} + +impl GeneralGraph + for TypedDiStaticGraph +{ + #[inline(always)] + fn as_graph(&self) -> &dyn GraphTrait { + self + } + + #[inline(always)] + fn as_labeled_graph(&self) -> &dyn GraphLabelTrait { + self + } + + #[inline(always)] + fn as_general_graph(&self) -> &dyn GeneralGraph { + self + } + + #[inline(always)] + fn as_digraph(&self) -> Option<&dyn DiGraphTrait> { + Some(self) + } +} + +fn _merge_labels(_labels1: Option>, _labels2: Option>) -> Option> { + match (_labels1, _labels2) { + (None, None) => None, + (Some(labels1), Some(labels2)) => { + let (smaller, larger) = if labels1.len() <= labels2.len() { + (labels1, labels2) + } else { + (labels2, labels1) + }; + + let mut result = Vec::with_capacity(larger.len()); + let slen = smaller.len(); + result.extend(smaller.into_iter()); + result.extend(larger.into_iter().skip(slen)); + + Some(result) + } + (Some(labels), None) => Some(labels), + (None, Some(labels)) => Some(labels), + } +} + +impl Add + for TypedStaticGraph +{ + type Output = TypedStaticGraph; + + fn add(self, other: TypedStaticGraph) -> Self::Output { + let mut node_label_map = self.node_label_map.clone(); + for item in other.node_label_map.items() { + node_label_map.add_item(item.clone()); + } + + let mut edge_label_map = self.edge_label_map.clone(); + for item in other.edge_label_map.items() { + edge_label_map.add_item(item.clone()); + } + + let mut graph = TypedStaticGraph { + num_nodes: 0, + num_edges: 0, + edge_vec: self.edge_vec + other.edge_vec, + in_edge_vec: match (self.in_edge_vec, other.in_edge_vec) { + (None, None) => None, + (Some(left), Some(right)) => Some(left + right), + _ => panic!("Can not merge Some `in_edge_vec` with None."), + }, + labels: _merge_labels(self.labels, other.labels), + graph_type: PhantomData, + node_label_map, + edge_label_map, + }; + + graph.num_nodes = graph.edge_vec.num_nodes(); + graph.num_edges = if Ty::is_directed() { + graph.edge_vec.num_edges() + } else { + graph.edge_vec.num_edges() >> 1 + }; + + graph + } +} diff --git a/src/graph_impl/static_graph/mmap/edge_vec_mmap.rs b/src/graph_impl/static_graph/mmap/edge_vec_mmap.rs deleted file mode 100644 index 7b3af213..00000000 --- a/src/graph_impl/static_graph/mmap/edge_vec_mmap.rs +++ /dev/null @@ -1,74 +0,0 @@ -/* - * Copyright (c) 2018 UNSW Sydney, Data and Knowledge Group. - * - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you 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. - */ -use std::fs::metadata; - -use generic::IdType; -use graph_impl::static_graph::EdgeVecTrait; -use io::mmap::TypedMemoryMap; - -/// A mmap version of `EdgeVec`. -pub struct EdgeVecMmap { - offsets: TypedMemoryMap, - edges: TypedMemoryMap, - labels: Option>, -} - -impl EdgeVecMmap { - pub fn new(prefix: &str) -> Self { - let offsets_file = format!("{}.offsets", prefix); - let edges_file = format!("{}.edges", prefix); - let labels_file = format!("{}.labels", prefix); - - if metadata(&labels_file).is_ok() { - EdgeVecMmap { - offsets: TypedMemoryMap::new(&offsets_file), - edges: TypedMemoryMap::new(&edges_file), - labels: Some(TypedMemoryMap::new(&labels_file)), - } - } else { - EdgeVecMmap { - offsets: TypedMemoryMap::new(&offsets_file), - edges: TypedMemoryMap::new(&edges_file), - labels: None, - } - } - } -} - -impl EdgeVecTrait for EdgeVecMmap { - #[inline(always)] - fn get_offsets(&self) -> &[usize] { - &self.offsets[..] - } - - #[inline(always)] - fn get_edges(&self) -> &[Id] { - &self.edges[..] - } - - #[inline(always)] - fn get_labels(&self) -> &[L] { - match self.labels { - Some(ref labels) => &labels[..], - None => &[], - } - } -} diff --git a/src/graph_impl/static_graph/mmap/graph_mmap.rs b/src/graph_impl/static_graph/mmap/graph_mmap.rs deleted file mode 100644 index 038cb26c..00000000 --- a/src/graph_impl/static_graph/mmap/graph_mmap.rs +++ /dev/null @@ -1,341 +0,0 @@ -/* - * Copyright (c) 2018 UNSW Sydney, Data and Knowledge Group. - * - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you 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. - */ -use std::borrow::Cow; -use std::fs::metadata; -use std::hash::Hash; - -use serde; - -use generic::{ - DiGraphTrait, EdgeType, GeneralGraph, GraphLabelTrait, GraphTrait, IdType, Iter, NodeType, -}; -use graph_impl::static_graph::mmap::EdgeVecMmap; -use graph_impl::static_graph::node::StaticNode; -use graph_impl::static_graph::static_edge_iter::StaticEdgeIndexIter; -use graph_impl::static_graph::EdgeVecTrait; -use graph_impl::{Edge, GraphImpl}; -use io::mmap::TypedMemoryMap; -use io::serde::Deserializer; -use map::SetMap; - -pub struct StaticGraphMmap { - /// Outgoing edges, or edges for undirected - edges: EdgeVecMmap, - /// Incoming edges for directed, `None` for undirected - in_edges: Option>, - /// Maintain the node's labels, whose index is aligned with `offsets`. - labels: Option>, - - num_nodes: usize, - num_edges: usize, - node_label_map: SetMap, - edge_label_map: SetMap, -} - -#[derive(Serialize, Deserialize)] -pub struct StaticGraphMmapAux { - num_nodes: usize, - num_edges: usize, - node_label_map: SetMap, - edge_label_map: SetMap, -} - -impl StaticGraphMmapAux { - pub fn new( - num_nodes: usize, - num_edges: usize, - node_label_map: SetMap, - edge_label_map: SetMap, - ) -> Self { - StaticGraphMmapAux { - num_nodes, - num_edges, - node_label_map, - edge_label_map, - } - } - - pub fn empty(num_nodes: usize, num_edges: usize) -> Self { - StaticGraphMmapAux { - num_nodes, - num_edges, - node_label_map: SetMap::new(), - edge_label_map: SetMap::new(), - } - } -} - -impl StaticGraphMmap -where - for<'de> NL: serde::Deserialize<'de>, - for<'de> EL: serde::Deserialize<'de>, -{ - pub fn new(prefix: &str) -> Self { - let edge_prefix = format!("{}_OUT", prefix); - let in_edge_prefix = format!("{}_IN", prefix); - let labels_file = format!("{}.labels", prefix); - - let aux_map_file = format!("{}_aux.bin", prefix); - - let edges = EdgeVecMmap::new(&edge_prefix); - - let in_edges = if metadata(&format!("{}.offsets", in_edge_prefix)).is_ok() { - Some(EdgeVecMmap::new(&in_edge_prefix)) - } else { - None - }; - - let labels = if metadata(&labels_file).is_ok() { - Some(TypedMemoryMap::new(&labels_file)) - } else { - None - }; - - let aux_file = if metadata(&aux_map_file).is_ok() { - Deserializer::import(&aux_map_file).unwrap() - } else { - let num_node = edges.num_nodes(); - let num_edge = if in_edges.is_some() { - edges.num_edges() - } else { - edges.num_edges() >> 1 - }; - - StaticGraphMmapAux::empty(num_node, num_edge) - }; - - StaticGraphMmap { - num_nodes: aux_file.num_nodes, - num_edges: aux_file.num_edges, - edges, - in_edges, - labels, - node_label_map: aux_file.node_label_map, - edge_label_map: aux_file.edge_label_map, - } - } -} - -impl StaticGraphMmap { - #[inline] - pub fn inner_neighbors(&self, id: Id) -> &[Id] { - self.edges.neighbors(id) - } - - #[inline] - pub fn inner_in_neighbors(&self, id: Id) -> &[Id] { - if let Some(ref in_edges) = self.in_edges { - in_edges.neighbors(id) - } else { - &[] - } - } -} - -impl GraphTrait - for StaticGraphMmap -{ - #[inline] - fn get_node(&self, id: Id) -> NodeType { - if !self.has_node(id) { - return NodeType::None; - } - - match self.labels { - Some(ref labels) => { - NodeType::StaticNode(StaticNode::new_static(id, labels[..][id.id()])) - } - None => NodeType::StaticNode(StaticNode::new(id, None)), - } - } - - #[inline] - fn get_edge(&self, start: Id, target: Id) -> EdgeType { - if !self.has_edge(start, target) { - return EdgeType::None; - } - - let _label = self.edges.find_edge_label_id(start, target); - match _label { - Some(label) => EdgeType::Edge(Edge::new_static(start, target, *label)), - None => EdgeType::Edge(Edge::new(start, target, None)), - } - } - - #[inline] - fn has_node(&self, id: Id) -> bool { - id.id() < self.num_nodes - } - - #[inline] - fn has_edge(&self, start: Id, target: Id) -> bool { - let neighbors = self.neighbors(start); - // The neighbors must be sorted anyway - let pos = neighbors.binary_search(&target); - - pos.is_ok() - } - - #[inline] - fn node_count(&self) -> usize { - self.num_nodes - } - - #[inline] - fn edge_count(&self) -> usize { - self.num_edges - } - - #[inline] - fn is_directed(&self) -> bool { - // A directed graph should have in-coming edges ready - self.in_edges.is_some() - } - - #[inline] - fn node_indices(&self) -> Iter { - Iter::new(Box::new((0..self.num_nodes).map(Id::new))) - } - - #[inline] - fn edge_indices(&self) -> Iter<(Id, Id)> { - Iter::new(Box::new(StaticEdgeIndexIter::new( - Box::new(&self.edges), - self.is_directed(), - ))) - } - - #[inline] - fn nodes(&self) -> Iter> { - match self.labels { - None => Iter::new(Box::new( - self.node_indices() - .map(|i| NodeType::StaticNode(StaticNode::new(i, None))), - )), - Some(ref labels) => Iter::new(Box::new( - self.node_indices() - .zip(labels[..].iter()) - .map(|n| NodeType::StaticNode(StaticNode::new_static(n.0, *n.1))), - )), - } - } - - #[inline] - fn edges(&self) -> Iter> { - let labels = self.edges.get_labels(); - if labels.is_empty() { - Iter::new(Box::new( - self.edge_indices() - .map(|i| EdgeType::Edge(Edge::new(i.0, i.1, None))), - )) - } else { - Iter::new(Box::new(self.edge_indices().zip(labels.iter()).map(|e| { - EdgeType::Edge(Edge::new_static((e.0).0, (e.0).1, *e.1)) - }))) - } - } - - #[inline] - fn degree(&self, id: Id) -> usize { - self.neighbors(id).len() - } - - #[inline] - fn neighbors_iter(&self, id: Id) -> Iter { - Iter::new(Box::new(self.edges.neighbors(id).iter().map(|x| *x))) - } - - #[inline] - fn neighbors(&self, id: Id) -> Cow<[Id]> { - self.edges.neighbors(id).into() - } - - #[inline] - fn max_seen_id(&self) -> Option { - Some(Id::new(self.node_count() - 1)) - } - - #[inline(always)] - fn implementation(&self) -> GraphImpl { - GraphImpl::StaicGraphMmap - } -} - -impl GraphLabelTrait - for StaticGraphMmap -{ - #[inline(always)] - fn get_node_label_map(&self) -> &SetMap { - &self.node_label_map - } - - #[inline(always)] - fn get_edge_label_map(&self) -> &SetMap { - &self.edge_label_map - } -} - -impl DiGraphTrait - for StaticGraphMmap -{ - #[inline] - fn in_degree(&self, id: Id) -> usize { - self.inner_in_neighbors(id).len() - } - - #[inline] - fn in_neighbors_iter(&self, id: Id) -> Iter { - Iter::new(Box::new(self.inner_in_neighbors(id).iter().map(|x| *x))) - } - - #[inline] - fn in_neighbors(&self, id: Id) -> Cow<[Id]> { - self.inner_in_neighbors(id).into() - } -} - -impl GeneralGraph - for StaticGraphMmap -{ - #[inline(always)] - fn as_graph(&self) -> &GraphTrait { - self - } - - #[inline(always)] - fn as_labeled_graph(&self) -> &GraphLabelTrait { - self - } - - #[inline(always)] - fn as_general_graph(&self) -> &GeneralGraph { - self - } - - #[inline(always)] - fn as_digraph(&self) -> Option<&DiGraphTrait> { - if self.is_directed() { - Some(self) - } else { - None - } - } -} diff --git a/src/graph_impl/static_graph/mod.rs b/src/graph_impl/static_graph/mod.rs index 6c202fd7..88128841 100644 --- a/src/graph_impl/static_graph/mod.rs +++ b/src/graph_impl/static_graph/mod.rs @@ -1,32 +1,31 @@ -/* - * Copyright (c) 2018 UNSW Sydney, Data and Knowledge Group. - * - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you 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. - */ -pub mod edge_vec; -pub mod graph; -pub mod mmap; -pub mod node; -pub mod static_edge_iter; - -pub use graph_impl::static_graph::edge_vec::{EdgeVec, EdgeVecTrait}; -pub use graph_impl::static_graph::graph::{ - DiStaticGraph, StaticGraph, TypedDiStaticGraph, TypedStaticGraph, TypedUnStaticGraph, - UnStaticGraph, -}; -pub use graph_impl::static_graph::node::StaticNode; +/* + * Copyright (c) 2018 UNSW Sydney, Data and Knowledge Group. + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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. + */ +pub mod edge_vec; +pub mod graph; +pub mod node; +pub mod static_edge_iter; + +pub use crate::graph_impl::static_graph::edge_vec::{EdgeVec, EdgeVecTrait}; +pub use crate::graph_impl::static_graph::graph::{ + DiStaticGraph, StaticGraph, TypedDiStaticGraph, TypedStaticGraph, TypedUnStaticGraph, + UnStaticGraph, +}; +pub use crate::graph_impl::static_graph::node::StaticNode; diff --git a/src/graph_impl/static_graph/node.rs b/src/graph_impl/static_graph/node.rs index 2916b96d..9249ac63 100644 --- a/src/graph_impl/static_graph/node.rs +++ b/src/graph_impl/static_graph/node.rs @@ -1,58 +1,58 @@ -/* - * Copyright (c) 2018 UNSW Sydney, Data and Knowledge Group. - * - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you 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. - */ -use generic::{IdType, NodeTrait}; - -#[derive(Debug, Clone, PartialEq, Eq)] -pub struct StaticNode { - id: Id, - label: Option, -} - -impl StaticNode { - #[inline(always)] - pub fn new(id: Id, label: Option) -> Self { - StaticNode { id, label } - } - - #[inline(always)] - pub fn new_static(id: Id, label: L) -> Self { - StaticNode { - id, - label: if label == L::max_value() { - None - } else { - Some(label) - }, - } - } -} - -impl NodeTrait for StaticNode { - #[inline(always)] - fn get_id(&self) -> Id { - self.id - } - - #[inline(always)] - fn get_label_id(&self) -> Option { - self.label - } -} +/* + * Copyright (c) 2018 UNSW Sydney, Data and Knowledge Group. + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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. + */ +use crate::generic::{IdType, NodeTrait}; + +#[derive(Debug, Clone, PartialEq, Eq)] +pub struct StaticNode { + id: Id, + label: Option, +} + +impl StaticNode { + #[inline(always)] + pub fn new(id: Id, label: Option) -> Self { + StaticNode { id, label } + } + + #[inline(always)] + pub fn new_static(id: Id, label: L) -> Self { + StaticNode { + id, + label: if label == L::max_value() { + None + } else { + Some(label) + }, + } + } +} + +impl NodeTrait for StaticNode { + #[inline(always)] + fn get_id(&self) -> Id { + self.id + } + + #[inline(always)] + fn get_label_id(&self) -> Option { + self.label + } +} diff --git a/src/graph_impl/static_graph/static_edge_iter.rs b/src/graph_impl/static_graph/static_edge_iter.rs index 8906c7ae..8a311191 100644 --- a/src/graph_impl/static_graph/static_edge_iter.rs +++ b/src/graph_impl/static_graph/static_edge_iter.rs @@ -1,93 +1,93 @@ -/* - * Copyright (c) 2018 UNSW Sydney, Data and Knowledge Group. - * - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you 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. - */ -use generic::IdType; -use graph_impl::static_graph::EdgeVecTrait; - -pub struct StaticEdgeIndexIter<'a, Id: IdType, L: IdType> { - edge_vec: Box<&'a EdgeVecTrait>, - curr_node: usize, - curr_neighbor_index: usize, - is_directed: bool, -} - -impl<'a, Id: IdType, L: IdType> StaticEdgeIndexIter<'a, Id, L> { - #[inline] - pub fn new(edge_vec: Box<&'a EdgeVecTrait>, is_directed: bool) -> Self { - StaticEdgeIndexIter { - edge_vec, - curr_node: 0, - curr_neighbor_index: 0, - is_directed, - } - } -} - -impl<'a, Id: IdType, L: IdType> Iterator for StaticEdgeIndexIter<'a, Id, L> { - type Item = (Id, Id); - - #[inline] - fn next(&mut self) -> Option { - let mut node: usize; - let mut neighbors: &[Id]; - - loop { - while self.edge_vec.has_node(Id::new(self.curr_node)) - && self.curr_neighbor_index >= self.edge_vec.degree(Id::new(self.curr_node)) - { - self.curr_node += 1; - self.curr_neighbor_index = 0; - } - - node = self.curr_node; - if !self.edge_vec.has_node(Id::new(node)) { - return None; - } - - neighbors = self.edge_vec.neighbors(Id::new(node)); - - if !self.is_directed && neighbors[self.curr_neighbor_index] < Id::new(node) { - match neighbors.binary_search(&Id::new(node)) { - Ok(index) => { - self.curr_neighbor_index = index; - break; - } - Err(index) => { - if index < neighbors.len() { - self.curr_neighbor_index = index; - break; - } else { - self.curr_node += 1; - self.curr_neighbor_index = 0; - } - } - } - } else { - break; - } - } - - let neighbor = neighbors[self.curr_neighbor_index]; - let edge = (Id::new(node), neighbor); - self.curr_neighbor_index += 1; - - Some(edge) - } -} +/* + * Copyright (c) 2018 UNSW Sydney, Data and Knowledge Group. + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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. + */ +use crate::generic::IdType; +use crate::graph_impl::static_graph::EdgeVecTrait; + +pub struct StaticEdgeIndexIter<'a, Id: IdType, L: IdType> { + edge_vec: Box<&'a dyn EdgeVecTrait>, + curr_node: usize, + curr_neighbor_index: usize, + is_directed: bool, +} + +impl<'a, Id: IdType, L: IdType> StaticEdgeIndexIter<'a, Id, L> { + #[inline] + pub fn new(edge_vec: Box<&'a dyn EdgeVecTrait>, is_directed: bool) -> Self { + StaticEdgeIndexIter { + edge_vec, + curr_node: 0, + curr_neighbor_index: 0, + is_directed, + } + } +} + +impl<'a, Id: IdType, L: IdType> Iterator for StaticEdgeIndexIter<'a, Id, L> { + type Item = (Id, Id); + + #[inline] + fn next(&mut self) -> Option { + let mut node: usize; + let mut neighbors: &[Id]; + + loop { + while self.edge_vec.has_node(Id::new(self.curr_node)) + && self.curr_neighbor_index >= self.edge_vec.degree(Id::new(self.curr_node)) + { + self.curr_node += 1; + self.curr_neighbor_index = 0; + } + + node = self.curr_node; + if !self.edge_vec.has_node(Id::new(node)) { + return None; + } + + neighbors = self.edge_vec.neighbors(Id::new(node)); + + if !self.is_directed && neighbors[self.curr_neighbor_index] < Id::new(node) { + match neighbors.binary_search(&Id::new(node)) { + Ok(index) => { + self.curr_neighbor_index = index; + break; + } + Err(index) => { + if index < neighbors.len() { + self.curr_neighbor_index = index; + break; + } else { + self.curr_node += 1; + self.curr_neighbor_index = 0; + } + } + } + } else { + break; + } + } + + let neighbor = neighbors[self.curr_neighbor_index]; + let edge = (Id::new(node), neighbor); + self.curr_neighbor_index += 1; + + Some(edge) + } +} diff --git a/src/graph_into_tikv.rs b/src/graph_into_tikv.rs new file mode 100644 index 00000000..da164120 --- /dev/null +++ b/src/graph_into_tikv.rs @@ -0,0 +1,42 @@ +#![feature(async_await)] +extern crate rust_graph; +extern crate serde_json; +extern crate tikv_client; + +use rust_graph::property::tikv_property::*; +use rust_graph::property::PropertyGraph; +use serde_json::{json, to_vec}; +use tikv_client::{Config, Key, KvPair, RawClient as Client, Result, ToOwnedRange, Value}; + +/// The pd-server that is responsible to store node properties in its managed tikv-servers +const NODE_PD_SERVER_ADDR: &str = "192.168.2.2:2379"; + +/// The pd-server that is responsible to store edge properties in its managed tikv-servers +const EDGE_PD_SERVER_ADDR: &str = "192.168.2.7:2379"; + +fn main() { + let args = parse_args("raw"); + let mut graph = TikvProperty::new( + Config::new(vec![NODE_PD_SERVER_ADDR.to_owned()]), + Config::new(vec![EDGE_PD_SERVER_ADDR.to_owned()]), + false, + ) + .unwrap(); + // insert node property + let new_prop = json!({"name":"jack"}); + let raw_prop = to_vec(&new_prop).unwrap(); + + graph.insert_node_raw(0u32, raw_prop).unwrap(); + let node_property = graph.get_node_property_all(0u32).unwrap(); + + assert_eq!(Some(json!({"name":"jack"})), node_property); + + // insert edge property + let edge_prop = json!({"length":"15"}); + let raw_edge_prop = to_vec(&edge_prop).unwrap(); + + graph.insert_edge_raw(0u32, 1u32, raw_edge_prop).unwrap(); + let edge_property = graph.get_edge_property_all(0u32, 1u32).unwrap(); + + assert_eq!(Some(json!({"length":"15"})), edge_property); +} diff --git a/src/io/csv/mod.rs b/src/io/csv/mod.rs index 989f0e9c..f18eb721 100644 --- a/src/io/csv/mod.rs +++ b/src/io/csv/mod.rs @@ -27,13 +27,15 @@ use std::io::Result; use std::path::Path; use serde::{Deserialize, Serialize}; +pub use serde_cbor::Value as CborValue; -use generic::{GeneralGraph, IdType, MutGraphTrait}; -pub use io::csv::reader::CSVReader; -pub use io::csv::writer::CSVWriter; +use crate::generic::{GeneralGraph, IdType, MutGraphTrait}; +pub use crate::io::csv::reader::CSVReader; +pub use crate::io::csv::writer::CSVWriter; +pub use crate::io::ReadGraphTo; pub fn write_to_csv( - g: &GeneralGraph, + g: &dyn GeneralGraph, path_to_nodes: P, path_to_edges: P, ) -> Result<()> @@ -49,27 +51,25 @@ where pub fn read_from_csv( g: &mut G, - path_to_nodes: Option

, - path_to_edges: P, + path_to_nodes: Vec

, + path_to_edges: Vec

, separator: Option<&str>, has_headers: bool, is_flexible: bool, -) -> Result<()> -where +) where for<'de> Id: IdType + Serialize + Deserialize<'de>, - for<'de> NL: Hash + Eq + Serialize + Deserialize<'de>, - for<'de> EL: Hash + Eq + Serialize + Deserialize<'de>, + for<'de> NL: Hash + Eq + Serialize + Deserialize<'de> + 'static, + for<'de> EL: Hash + Eq + Serialize + Deserialize<'de> + 'static, G: MutGraphTrait, P: AsRef, { - match separator { - Some(sep) => CSVReader::with_separator(path_to_nodes, path_to_edges, sep) - .headers(has_headers) - .flexible(is_flexible) - .read(g), - None => CSVReader::new(path_to_nodes, path_to_edges) - .headers(has_headers) - .flexible(is_flexible) - .read(g), + let mut reader = CSVReader::new(path_to_nodes, path_to_edges) + .headers(has_headers) + .flexible(is_flexible); + + if let Some(sep) = separator { + reader = reader.with_separator(sep); } + + reader.read(g) } diff --git a/src/io/csv/reader.rs b/src/io/csv/reader.rs index 7c992757..531e0acb 100644 --- a/src/io/csv/reader.rs +++ b/src/io/csv/reader.rs @@ -18,6 +18,9 @@ * specific language governing permissions and limitations * under the License. */ +extern crate walkdir; + +use std::collections::BTreeMap; /// Nodes: /// node_id node_label(optional) /// @@ -26,28 +29,31 @@ /// /// **Note**: Rows that are unable to parse will be skipped. use std::hash::Hash; -use std::io::Result; use std::marker::PhantomData; use std::path::{Path, PathBuf}; -use csv::ReaderBuilder; +use self::walkdir::{DirEntry, WalkDir}; +use crate::csv::ReaderBuilder; +use crate::generic::{IdType, Iter}; +use crate::io::csv::record::{EdgeRecord, NodeRecord, PropEdgeRecord, PropNodeRecord}; +use crate::io::csv::CborValue; +use crate::io::{ReadGraph, ReadGraphTo}; +use itertools::Itertools; use serde::Deserialize; +use serde_cbor::{from_value, to_value}; -use generic::{IdType, Iter, MutGraphTrait}; -use io::csv::record::{EdgeRecord, NodeRecord}; - -#[derive(Debug)] -pub struct CSVReader<'a, Id: IdType, NL: Hash + Eq + 'a, EL: Hash + Eq + 'a> { - path_to_nodes: Option, - path_to_edges: PathBuf, +#[derive(Debug, Default)] +pub struct CSVReader { + path_to_nodes: Vec, + path_to_edges: Vec, separator: u8, has_headers: bool, // Whether the number of fields in records is allowed to change or not. is_flexible: bool, - _ph: PhantomData<(&'a Id, &'a NL, &'a EL)>, + _ph: PhantomData<(Id, NL, EL)>, } -impl<'a, Id: IdType, NL: Hash + Eq + 'a, EL: Hash + Eq + 'a> Clone for CSVReader<'a, Id, NL, EL> { +impl Clone for CSVReader { fn clone(&self) -> Self { CSVReader { path_to_nodes: self.path_to_nodes.clone(), @@ -60,11 +66,23 @@ impl<'a, Id: IdType, NL: Hash + Eq + 'a, EL: Hash + Eq + 'a> Clone for CSVReader } } -impl<'a, Id: IdType, NL: Hash + Eq + 'a, EL: Hash + Eq + 'a> CSVReader<'a, Id, NL, EL> { - pub fn new>(path_to_nodes: Option

, path_to_edges: P) -> Self { +impl CSVReader { + pub fn new>(path_to_nodes: Vec

, path_to_edges: Vec

) -> Self { + let mut path_to_nodes: Vec = path_to_nodes + .into_iter() + .flat_map(|p| list_files(p)) + .collect_vec(); + path_to_nodes.sort(); + + let mut path_to_edges: Vec = path_to_edges + .into_iter() + .flat_map(|p| list_files(p)) + .collect_vec(); + path_to_edges.sort(); + CSVReader { - path_to_nodes: path_to_nodes.map(|x| x.as_ref().to_path_buf()), - path_to_edges: path_to_edges.as_ref().to_path_buf(), + path_to_nodes, + path_to_edges, separator: b',', has_headers: true, is_flexible: false, @@ -72,15 +90,12 @@ impl<'a, Id: IdType, NL: Hash + Eq + 'a, EL: Hash + Eq + 'a> CSVReader<'a, Id, N } } - pub fn with_separator>( - path_to_nodes: Option

, - path_to_edges: P, - separator: &str, - ) -> Self { + pub fn with_separator(mut self, separator: &str) -> Self { let sep_string = match separator { "comma" => ",", "space" => " ", "tab" => "\t", + "bar" => "|", other => other, }; @@ -88,14 +103,10 @@ impl<'a, Id: IdType, NL: Hash + Eq + 'a, EL: Hash + Eq + 'a> CSVReader<'a, Id, N panic!("Invalid separator {}.", sep_string); } - CSVReader { - path_to_nodes: path_to_nodes.map(|x| x.as_ref().to_path_buf()), - path_to_edges: path_to_edges.as_ref().to_path_buf(), - separator: sep_string.chars().next().unwrap() as u8, - has_headers: true, - is_flexible: false, - _ph: PhantomData, - } + let sep = sep_string.chars().next().unwrap() as u8; + self.separator = sep; + + self } pub fn headers(mut self, has_headers: bool) -> Self { @@ -109,116 +120,232 @@ impl<'a, Id: IdType, NL: Hash + Eq + 'a, EL: Hash + Eq + 'a> CSVReader<'a, Id, N } } -impl<'a, Id: IdType, NL: Hash + Eq + 'a, EL: Hash + Eq + 'a> CSVReader<'a, Id, NL, EL> +impl ReadGraph + for CSVReader where for<'de> Id: Deserialize<'de>, for<'de> NL: Deserialize<'de>, for<'de> EL: Deserialize<'de>, { - pub fn read, L: IdType>(&self, g: &mut G) -> Result<()> { - if let Some(ref path_to_nodes) = self.path_to_nodes { - info!( - "Adding nodes from {}", - path_to_nodes.as_path().to_str().unwrap() - ); - let rdr = ReaderBuilder::new() - .has_headers(self.has_headers) - .flexible(self.is_flexible) - .delimiter(self.separator) - .from_path(path_to_nodes.as_path())?; - - for (i, result) in rdr.into_deserialize().enumerate() { - match result { - Ok(_result) => { - let record: NodeRecord = _result; - record.add_to_graph(g); - } - Err(e) => warn!("Line {:?}: Error when reading csv: {:?}", i + 1, e), - } - } - } + fn get_node_iter(&self, idx: usize) -> Option)>> { + // get specific node_id + let node_file = self.path_to_nodes.get(idx).cloned(); + let has_headers = self.has_headers; + let is_flexible = self.is_flexible; + let separator = self.separator; - info!( - "Adding edges from {}", - self.path_to_edges.as_path().to_str().unwrap() - ); - - let rdr = ReaderBuilder::new() - .has_headers(self.has_headers) - .flexible(self.is_flexible) - .delimiter(self.separator) - .from_path(self.path_to_edges.as_path())?; - - for (i, result) in rdr.into_deserialize().enumerate() { - match result { - Ok(_result) => { - let record: EdgeRecord = _result; - record.add_to_graph(g); - } - Err(e) => warn!("Line {:?}: Error when reading csv: {:?}", i + 1, e), - } - } + node_file + .map(move |path_to_nodes| { // move: transfer ownership of value + let path_str = path_to_nodes.to_str().unwrap().to_owned(); + let rdr = ReaderBuilder::new() + .has_headers(has_headers) + .flexible(is_flexible) + .delimiter(separator) + .from_path(path_to_nodes.as_path()) + .unwrap(); + + (rdr, path_str) + }) + .map(|(rdr, path)| { + rdr.into_deserialize() + .enumerate() + .filter_map(move |(i, result)| { + if i == 0 { + info!("Reading nodes from {}", path); + } + + match result { + Ok(_result) => { + let record: NodeRecord = _result; + + Some((record.id, record.label)) + } + Err(e) => { + warn!("Line {:?}: Error when reading csv: {:?}", i + 1, e); - Ok(()) + None + } + } + }) + }) + .map(|iter| Iter::new(Box::new(iter))) } - pub fn node_iter(&self) -> Result)>> { - if let Some(ref path_to_nodes) = self.path_to_nodes { - info!( - "Reading nodes from {}", - path_to_nodes.as_path().to_str().unwrap() - ); - let rdr = ReaderBuilder::new() - .has_headers(self.has_headers) - .flexible(self.is_flexible) - .delimiter(self.separator) - .from_path(path_to_nodes.as_path())?; - - let rdr = rdr - .into_deserialize() - .enumerate() - .filter_map(|(i, result)| match result { - Ok(_result) => { - let record: NodeRecord = _result; - Some((record.id, record.label)) + fn get_edge_iter(&self, idx: usize) -> Option)>> { + let edge_file = self.path_to_edges.get(idx).cloned(); + let has_headers = self.has_headers; + let is_flexible = self.is_flexible; + let separator = self.separator; + + edge_file + .map(move |path_to_edges| { + let path_str = path_to_edges.to_str().unwrap().to_owned(); + let rdr = ReaderBuilder::new() + .has_headers(has_headers) + .flexible(is_flexible) + .delimiter(separator) + .from_path(path_to_edges.as_path()) + .unwrap(); + + (rdr, path_str) + }) + .map(|(rdr, path)| { + rdr.into_deserialize() + .enumerate() + .filter_map(move |(i, result)| { + if i == 0 { + info!("Reading edges from {}", path); + } + + match result { + Ok(_result) => { + let record: EdgeRecord = _result; + + Some((record.src, record.dst, record.label)) + } + Err(e) => { + warn!("Line {:?}: Error when reading csv: {:?}", i + 1, e); + + None + } + } + }) + }) + .map(|iter| Iter::new(Box::new(iter))) + } + + fn get_prop_node_iter(&self, idx: usize) -> Option, CborValue)>> { + assert!(self.has_headers); + + let node_file = self.path_to_nodes.get(idx).cloned(); + let has_headers = self.has_headers; + let is_flexible = self.is_flexible; + let separator = self.separator; + + node_file + .map(move |path_to_nodes| { + let path_str = path_to_nodes.to_str().unwrap().to_owned(); + let rdr = ReaderBuilder::new() + .has_headers(has_headers) + .flexible(is_flexible) + .delimiter(separator) + .from_path(path_to_nodes.as_path()) + .unwrap(); + + (rdr, path_str) + }) + .map(|(rdr, path)| { + rdr.into_deserialize().enumerate().map(move |(i, result)| { + if i == 0 { + info!("Reading nodes from {}", path); } - Err(e) => { - warn!("Line {:?}: Error when reading csv: {:?}", i + 1, e); - None + + let mut record: PropNodeRecord = + result.expect(&format!("Error when reading line {}", i + 1)); + + parse_prop_map(&mut record.properties); + let prop = to_value(record.properties) + .expect(&format!("Error when parsing line {} to Cbor", i + 1)); + + (record.id, record.label, prop) + }) + }) + .map(|iter| Iter::new(Box::new(iter))) + } + + fn get_prop_edge_iter(&self, idx: usize) -> Option, CborValue)>> { + assert!(self.has_headers); + + let edge_file = self.path_to_edges.get(idx).cloned(); + let has_headers = self.has_headers; + let is_flexible = self.is_flexible; + let separator = self.separator; + + edge_file + .map(move |path_to_edges| { + let path_str = path_to_edges.to_str().unwrap().to_owned(); + let rdr = ReaderBuilder::new() + .has_headers(has_headers) + .flexible(is_flexible) + .delimiter(separator) + .from_path(path_to_edges.as_path()) + .unwrap(); + + (rdr, path_str) + }) + .map(|(rdr, path)| { + rdr.into_deserialize().enumerate().map(move |(i, result)| { + if i == 0 { + info!("Reading edges from {}", path); } - }); - Ok(Iter::new(Box::new(rdr))) - } else { - Ok(Iter::empty()) + let mut record: PropEdgeRecord = + result.expect(&format!("Error when reading line {}", i + 1)); + + parse_prop_map(&mut record.properties); + let prop = to_value(record.properties) + .expect(&format!("Error when parsing line {} to Cbor", i + 1)); + + (record.src, record.dst, record.label, prop) + }) + }) + .map(|iter| Iter::new(Box::new(iter))) + } + + fn num_of_node_files(&self) -> usize { + self.path_to_nodes.len() + } + + fn num_of_edge_files(&self) -> usize { + self.path_to_edges.len() + } +} + +impl ReadGraphTo + for CSVReader +where + for<'de> Id: Deserialize<'de>, + for<'de> NL: Deserialize<'de>, + for<'de> EL: Deserialize<'de>, +{ +} + +pub fn parse_prop_map(props: &mut BTreeMap) { + for (_, value) in props.iter_mut() { + if value.is_string() { + let result = from_value::(value.clone()); + if result.is_err() { + continue; + } + let parsed = result.unwrap(); + *value = parsed } } +} + +fn list_files>(p: P) -> Vec { + let p = p.as_ref().to_path_buf(); - pub fn edge_iter(&self) -> Result)>> { - info!( - "Reading edges from {}", - self.path_to_edges.as_path().to_str().unwrap() - ); - let rdr = ReaderBuilder::new() - .has_headers(self.has_headers) - .flexible(self.is_flexible) - .delimiter(self.separator) - .from_path(self.path_to_edges.as_path())?; - - let rdr = rdr - .into_deserialize() - .enumerate() - .filter_map(|(i, result)| match result { - Ok(_result) => { - let record: EdgeRecord = _result; - Some((record.start, record.target, record.label)) - } - Err(e) => { - warn!("Line {:?}: Error when reading csv: {:?}", i + 1, e); - None - } - }); - - Ok(Iter::new(Box::new(rdr))) + if p.is_dir() { + let mut vec = WalkDir::new(p) + .into_iter() + .filter_entry(|e| !is_hidden(e)) + .filter_map(|e| e.ok()) + .map(|e| e.path().to_path_buf()) + .filter(|p| p.is_file()) + .collect::>(); + vec.sort(); + + vec + } else { + vec![p] } } + +fn is_hidden(entry: &DirEntry) -> bool { + entry + .file_name() + .to_str() + .map(|s| s.starts_with(".")) + .unwrap_or(false) +} diff --git a/src/io/csv/record.rs b/src/io/csv/record.rs index e2bc16ec..27c15f1c 100644 --- a/src/io/csv/record.rs +++ b/src/io/csv/record.rs @@ -1,259 +1,330 @@ -/* - * Copyright (c) 2018 UNSW Sydney, Data and Knowledge Group. - * - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you 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. - */ -use std::fmt; -use std::hash::Hash; -use std::marker::PhantomData; - -use serde; -use serde::de::{self, Deserialize, Deserializer, MapAccess, SeqAccess, Visitor}; - -use generic::{IdType, MutGraphTrait}; - -#[derive(Debug, Serialize)] -pub struct NodeRecord { - #[serde(rename = "nodeId:ID")] - pub(crate) id: Id, - #[serde(rename = ":LABEL")] - pub(crate) label: Option, -} - -#[derive(Debug, Serialize)] -pub struct EdgeRecord { - #[serde(rename = ":START_ID")] - pub(crate) start: Id, - #[serde(rename = ":END_ID")] - pub(crate) target: Id, - #[serde(rename = ":TYPE")] - pub(crate) label: Option, -} - -impl NodeRecord { - #[inline] - pub fn new(id: Id, label: Option) -> Self { - NodeRecord { id, label } - } - - #[inline] - pub fn add_to_graph, L: IdType>(self, g: &mut G) { - g.add_node(self.id, self.label); - } -} - -impl EdgeRecord { - #[inline] - pub fn new(start: Id, target: Id, label: Option) -> Self { - EdgeRecord { - start, - target, - label, - } - } - - #[inline] - pub fn add_to_graph, L: IdType>(self, g: &mut G) { - g.add_edge(self.start, self.target, self.label); - } -} - -impl<'de, Id: IdType, N: Hash + Eq> Deserialize<'de> for NodeRecord -where - Id: serde::Deserialize<'de>, - N: serde::Deserialize<'de>, -{ - fn deserialize(deserializer: D) -> Result - where - D: Deserializer<'de>, - { - #[derive(Deserialize)] - #[serde(field_identifier)] - enum Field { - #[serde(rename = "nodeId:ID")] - Id, - #[serde(rename = ":LABEL")] - Label, - } - - struct NodeRecordVisitor { - _id: PhantomData, - _n: PhantomData, - }; - - impl<'de, Id: IdType, N: Hash + Eq> Visitor<'de> for NodeRecordVisitor - where - Id: serde::Deserialize<'de>, - N: serde::Deserialize<'de>, - { - type Value = NodeRecord; - - fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result { - formatter.write_str("struct NodeRecord") - } - - fn visit_seq(self, mut seq: V) -> Result, V::Error> - where - V: SeqAccess<'de>, - { - let id = seq - .next_element()? - .ok_or_else(|| de::Error::invalid_length(0, &self))?; - let label = seq.next_element().unwrap_or(None); - - Ok(NodeRecord::new(id, label)) - } - - fn visit_map(self, mut map: V) -> Result, V::Error> - where - V: MapAccess<'de>, - { - let mut id = None; - let mut label = None; - while let Some(key) = map.next_key()? { - match key { - Field::Id => { - if id.is_some() { - return Err(de::Error::duplicate_field("id")); - } - id = Some(map.next_value()?); - } - Field::Label => { - if label.is_some() { - return Err(de::Error::duplicate_field("label")); - } - label = Some(map.next_value().unwrap_or(None)); - } - } - } - let id = id.ok_or_else(|| de::Error::missing_field("id"))?; - let label = label.unwrap_or(None); - Ok(NodeRecord::new(id, label)) - } - } - - const FIELDS: &[&str] = &["id", "label"]; - deserializer.deserialize_struct( - "NodeRecord", - FIELDS, - NodeRecordVisitor { - _id: PhantomData, - _n: PhantomData, - }, - ) - } -} - -impl<'de, Id: IdType, E: Hash + Eq> Deserialize<'de> for EdgeRecord -where - Id: serde::Deserialize<'de>, - E: serde::Deserialize<'de>, -{ - fn deserialize(deserializer: D) -> Result - where - D: Deserializer<'de>, - { - #[derive(Deserialize)] - #[serde(field_identifier)] - enum Field { - #[serde(rename = ":START_ID")] - Start, - #[serde(rename = ":END_ID")] - Target, - #[serde(rename = ":TYPE")] - Label, - } - - struct EdgeRecordVisitor { - _id: PhantomData, - _e: PhantomData, - }; - - impl<'de, Id: IdType, E: Hash + Eq> Visitor<'de> for EdgeRecordVisitor - where - Id: serde::Deserialize<'de>, - E: serde::Deserialize<'de>, - { - type Value = EdgeRecord; - - fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result { - formatter.write_str("struct EdgeRecord") - } - - fn visit_seq(self, mut seq: V) -> Result, V::Error> - where - V: SeqAccess<'de>, - { - let start = seq - .next_element()? - .ok_or_else(|| de::Error::invalid_length(0, &self))?; - let target = seq - .next_element()? - .ok_or_else(|| de::Error::invalid_length(1, &self))?; - let label = seq.next_element().unwrap_or(None); - - Ok(EdgeRecord::new(start, target, label)) - } - - fn visit_map(self, mut map: V) -> Result, V::Error> - where - V: MapAccess<'de>, - { - let mut start = None; - let mut target = None; - let mut label = None; - while let Some(key) = map.next_key()? { - match key { - Field::Start => { - if start.is_some() { - return Err(de::Error::duplicate_field("start")); - } - start = Some(map.next_value()?); - } - Field::Target => { - if target.is_some() { - return Err(de::Error::duplicate_field("target")); - } - target = Some(map.next_value()?); - } - Field::Label => { - if label.is_some() { - return Err(de::Error::duplicate_field("label")); - } - label = Some(map.next_value().unwrap_or(None)); - } - } - } - let start = start.ok_or_else(|| de::Error::missing_field("start"))?; - let target = target.ok_or_else(|| de::Error::missing_field("target"))?; - let label = label.unwrap_or(None); - Ok(EdgeRecord::new(start, target, label)) - } - } - - const FIELDS: &[&str] = &["start", "target", "label"]; - deserializer.deserialize_struct( - "EdgeRecord", - FIELDS, - EdgeRecordVisitor { - _id: PhantomData, - _e: PhantomData, - }, - ) - } -} +/* + * Copyright (c) 2018 UNSW Sydney, Data and Knowledge Group. + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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. + */ +use std::collections::BTreeMap; +use std::fmt; +use std::hash::Hash; +use std::marker::PhantomData; + +use serde; +use serde::de::{self, Deserialize, Deserializer, MapAccess, SeqAccess, Visitor}; + +use crate::generic::{IdType, MutGraphTrait}; +use crate::io::csv::CborValue; + +#[derive(Debug, Serialize)] +pub struct NodeRecord { + #[serde(rename = "nodeId:ID")] + pub(crate) id: Id, + #[serde(rename = ":LABEL")] + pub(crate) label: Option, +} + +#[derive(Debug, Serialize)] +pub struct EdgeRecord { + #[serde(rename = ":START_ID")] + pub(crate) src: Id, + #[serde(rename = ":END_ID")] + pub(crate) dst: Id, + #[serde(rename = ":TYPE")] + pub(crate) label: Option, +} + +#[derive(Debug, Deserialize, Serialize)] +pub struct PropNodeRecord { + #[serde(rename = "nodeId:ID")] + pub(crate) id: Id, + #[serde(rename = ":LABEL")] + pub(crate) label: Option, + + #[serde(flatten)] + pub(crate) properties: BTreeMap, +} + +#[derive(Debug, Deserialize, Serialize)] +pub struct PropEdgeRecord { + #[serde(rename = ":START_ID")] + pub(crate) src: Id, + #[serde(rename = ":END_ID")] + pub(crate) dst: Id, + #[serde(rename = ":TYPE")] + pub(crate) label: Option, + + #[serde(flatten)] + pub(crate) properties: BTreeMap, +} + +impl NodeRecord { + #[inline] + pub fn new(id: Id, label: Option) -> Self { + NodeRecord { id, label } + } + + #[inline] + pub fn add_to_graph, L: IdType>(self, g: &mut G) { + g.add_node(self.id, self.label); + } +} + +impl EdgeRecord { + #[inline] + pub fn new(start: Id, target: Id, label: Option) -> Self { + EdgeRecord { + src: start, + dst: target, + label, + } + } + + #[inline] + pub fn add_to_graph, L: IdType>(self, g: &mut G) { + g.add_edge(self.src, self.dst, self.label); + } +} + +impl From> for PropEdgeRecord { + fn from(r: EdgeRecord) -> Self { + let src = r.src; + let dst = r.dst; + let label = r.label; + + Self { + src, + dst, + label, + properties: BTreeMap::new(), + } + } +} + +impl<'de, Id: IdType, N: Hash + Eq> Deserialize<'de> for NodeRecord +where + Id: serde::Deserialize<'de>, + N: serde::Deserialize<'de>, +{ + fn deserialize(deserializer: D) -> Result + where + D: Deserializer<'de>, + { + #[derive(Deserialize)] + #[serde(field_identifier)] + enum Field { + #[serde(rename = "nodeId:ID")] + Id, + #[serde(rename = ":LABEL")] + Label, + } + + struct NodeRecordVisitor { + _id: PhantomData, + _n: PhantomData, + }; + + impl<'de, Id: IdType, N: Hash + Eq> Visitor<'de> for NodeRecordVisitor + where + Id: serde::Deserialize<'de>, + N: serde::Deserialize<'de>, + { + type Value = NodeRecord; + + fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result { + formatter.write_str("struct NodeRecord") + } + + fn visit_seq(self, mut seq: V) -> Result, V::Error> + where + V: SeqAccess<'de>, + { + let id = seq + .next_element()? + .ok_or_else(|| de::Error::invalid_length(0, &self))?; + let label = seq.next_element().unwrap_or(None); + + Ok(NodeRecord::new(id, label)) + } + + fn visit_map(self, mut map: V) -> Result, V::Error> + where + V: MapAccess<'de>, + { + let mut id = None; + let mut label = None; + + loop { + if id.is_some() && label.is_some() { + break; + } + + let result = map.next_key(); + + match result { + Ok(n) => match n { + Some(key) => match key { + Field::Id => { + if id.is_some() { + return Err(de::Error::duplicate_field("id")); + } + id = Some(map.next_value()?); + } + Field::Label => { + if label.is_some() { + return Err(de::Error::duplicate_field("label")); + } + label = Some(map.next_value().unwrap_or(None)); + } + }, + None => break, + }, + Err(_e) => continue, // skip any unknown fields + } + } + + let id = id.ok_or_else(|| de::Error::missing_field("id"))?; + let label = label.unwrap_or(None); + + Ok(NodeRecord::new(id, label)) + } + } + + const FIELDS: &[&str] = &["id", "label"]; + deserializer.deserialize_struct( + "NodeRecord", + FIELDS, + NodeRecordVisitor { + _id: PhantomData, + _n: PhantomData, + }, + ) + } +} + +impl<'de, Id: IdType, E: Hash + Eq> Deserialize<'de> for EdgeRecord +where + Id: serde::Deserialize<'de>, + E: serde::Deserialize<'de>, +{ + fn deserialize(deserializer: D) -> Result + where + D: Deserializer<'de>, + { + #[derive(Deserialize)] + #[serde(field_identifier)] + enum Field { + #[serde(rename = ":START_ID")] + Start, + #[serde(rename = ":END_ID")] + Target, + #[serde(rename = ":TYPE")] + Label, + } + + struct EdgeRecordVisitor { + _id: PhantomData, + _e: PhantomData, + }; + + impl<'de, Id: IdType, E: Hash + Eq> Visitor<'de> for EdgeRecordVisitor + where + Id: serde::Deserialize<'de>, + E: serde::Deserialize<'de>, + { + type Value = EdgeRecord; + + fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result { + formatter.write_str("struct EdgeRecord") + } + + fn visit_seq(self, mut seq: V) -> Result, V::Error> + where + V: SeqAccess<'de>, + { + let start = seq + .next_element()? + .ok_or_else(|| de::Error::invalid_length(0, &self))?; + let target = seq + .next_element()? + .ok_or_else(|| de::Error::invalid_length(1, &self))?; + let label = seq.next_element().unwrap_or(None); + + Ok(EdgeRecord::new(start, target, label)) + } + + fn visit_map(self, mut map: V) -> Result, V::Error> + where + V: MapAccess<'de>, + { + let mut start = None; + let mut target = None; + let mut label = None; + + loop { + if start.is_some() && target.is_some() && label.is_some() { + break; + } + + let result = map.next_key(); + + match result { + Ok(n) => match n { + Some(key) => match key { + Field::Start => { + if start.is_some() { + return Err(de::Error::duplicate_field("start")); + } + start = Some(map.next_value()?); + } + Field::Target => { + if target.is_some() { + return Err(de::Error::duplicate_field("target")); + } + target = Some(map.next_value()?); + } + Field::Label => { + if label.is_some() { + return Err(de::Error::duplicate_field("label")); + } + label = Some(map.next_value().unwrap_or(None)); + } + }, + None => break, + }, + Err(_e) => continue, // skip any unknown fields + } + } + + let start = start.ok_or_else(|| de::Error::missing_field("start"))?; + let target = target.ok_or_else(|| de::Error::missing_field("target"))?; + let label = label.unwrap_or(None); + + Ok(EdgeRecord::new(start, target, label)) + } + } + + const FIELDS: &[&str] = &["start", "target", "label"]; + deserializer.deserialize_struct( + "EdgeRecord", + FIELDS, + EdgeRecordVisitor { + _id: PhantomData, + _e: PhantomData, + }, + ) + } +} diff --git a/src/io/csv/writer.rs b/src/io/csv/writer.rs index a4537aa4..6a022cc8 100644 --- a/src/io/csv/writer.rs +++ b/src/io/csv/writer.rs @@ -1,134 +1,134 @@ -/* - * Copyright (c) 2018 UNSW Sydney, Data and Knowledge Group. - * - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you 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. - */ -use std::hash::Hash; -use std::io::Result; -use std::path::{Path, PathBuf}; - -use csv::WriterBuilder; -use serde::Serialize; - -use generic::GeneralGraph; -use generic::IdType; -use io::csv::record::{EdgeRecord, NodeRecord}; - -pub struct CSVWriter<'a, Id, NL, EL, L> -where - Id: 'a + IdType + Serialize, - NL: 'a + Hash + Eq + Serialize, - EL: 'a + Hash + Eq + Serialize, - L: 'a + IdType + Serialize, -{ - g: &'a GeneralGraph, - path_to_nodes: PathBuf, - path_to_edges: PathBuf, - separator: u8, -} - -impl<'a, Id, NL, EL, L> CSVWriter<'a, Id, NL, EL, L> -where - Id: 'a + IdType + Serialize, - NL: 'a + Hash + Eq + Serialize, - EL: 'a + Hash + Eq + Serialize, - - L: 'a + IdType + Serialize, -{ - pub fn new>( - g: &'a GeneralGraph, - path_to_nodes: P, - path_to_edges: P, - ) -> Self { - CSVWriter { - g, - path_to_nodes: path_to_nodes.as_ref().to_path_buf(), - path_to_edges: path_to_edges.as_ref().to_path_buf(), - separator: b',', - } - } - - pub fn with_separator>( - g: &'a GeneralGraph, - path_to_nodes: P, - path_to_edges: P, - separator: &str, - ) -> Self { - let sep_string = match separator { - "comma" => ",", - "space" => " ", - "tab" => "\t", - other => other, - }; - - if sep_string.len() != 1 { - panic!("Invalid separator {}.", sep_string); - } - - CSVWriter { - g, - path_to_nodes: path_to_nodes.as_ref().to_path_buf(), - path_to_edges: path_to_edges.as_ref().to_path_buf(), - separator: sep_string.chars().next().unwrap() as u8, - } - } -} - -impl<'a, Id, NL, EL, L> CSVWriter<'a, Id, NL, EL, L> -where - Id: 'a + IdType + Serialize, - NL: 'a + Hash + Eq + Serialize, - EL: 'a + Hash + Eq + Serialize, - L: 'a + IdType + Serialize, -{ - pub fn write(&self) -> Result<()> { - let g = self.g.as_labeled_graph(); - - info!( - "csv::Writer::write - Writing nodes to {}", - self.path_to_nodes.as_path().to_str().unwrap() - ); - - let mut wtr = WriterBuilder::new() - .delimiter(self.separator) - .from_path(self.path_to_nodes.as_path())?; - - for id in self.g.node_indices() { - wtr.serialize(NodeRecord::new(id, g.get_node_label(id)))?; - } - - info!( - "csv::Writer::write - Writing edges to {}", - self.path_to_edges.as_path().to_str().unwrap() - ); - - let mut wtr = WriterBuilder::new() - .delimiter(self.separator) - .from_path(self.path_to_edges.as_path())?; - - for (start, target) in self.g.edge_indices() { - wtr.serialize(EdgeRecord::new( - start, - target, - g.get_edge_label(start, target), - ))?; - } - - Ok(()) - } -} +/* + * Copyright (c) 2018 UNSW Sydney, Data and Knowledge Group. + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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. + */ +use std::hash::Hash; +use std::io::Result; +use std::path::{Path, PathBuf}; + +use csv::WriterBuilder; +use serde::Serialize; + +use crate::generic::GeneralGraph; +use crate::generic::IdType; +use crate::io::csv::record::{EdgeRecord, NodeRecord}; + +pub struct CSVWriter<'a, Id, NL, EL, L> +where + Id: 'a + IdType + Serialize, + NL: 'a + Hash + Eq + Serialize, + EL: 'a + Hash + Eq + Serialize, + L: 'a + IdType + Serialize, +{ + g: &'a dyn GeneralGraph, + path_to_nodes: PathBuf, + path_to_edges: PathBuf, + separator: u8, +} + +impl<'a, Id, NL, EL, L> CSVWriter<'a, Id, NL, EL, L> +where + Id: 'a + IdType + Serialize, + NL: 'a + Hash + Eq + Serialize, + EL: 'a + Hash + Eq + Serialize, + + L: 'a + IdType + Serialize, +{ + pub fn new>( + g: &'a dyn GeneralGraph, + path_to_nodes: P, + path_to_edges: P, + ) -> Self { + CSVWriter { + g, + path_to_nodes: path_to_nodes.as_ref().to_path_buf(), + path_to_edges: path_to_edges.as_ref().to_path_buf(), + separator: b',', + } + } + + pub fn with_separator>( + g: &'a dyn GeneralGraph, + path_to_nodes: P, + path_to_edges: P, + separator: &str, + ) -> Self { + let sep_string = match separator { + "comma" => ",", + "space" => " ", + "tab" => "\t", + other => other, + }; + + if sep_string.len() != 1 { + panic!("Invalid separator {}.", sep_string); + } + + CSVWriter { + g, + path_to_nodes: path_to_nodes.as_ref().to_path_buf(), + path_to_edges: path_to_edges.as_ref().to_path_buf(), + separator: sep_string.chars().next().unwrap() as u8, + } + } +} + +impl<'a, Id, NL, EL, L> CSVWriter<'a, Id, NL, EL, L> +where + Id: 'a + IdType + Serialize, + NL: 'a + Hash + Eq + Serialize, + EL: 'a + Hash + Eq + Serialize, + L: 'a + IdType + Serialize, +{ + pub fn write(&self) -> Result<()> { + let g = self.g.as_labeled_graph(); + + info!( + "csv::Writer::write - Writing nodes to {}", + self.path_to_nodes.as_path().to_str().unwrap() + ); + + let mut wtr = WriterBuilder::new() + .delimiter(self.separator) + .from_path(self.path_to_nodes.as_path())?; + + for id in self.g.node_indices() { + wtr.serialize(NodeRecord::new(id, g.get_node_label(id)))?; + } + + info!( + "csv::Writer::write - Writing edges to {}", + self.path_to_edges.as_path().to_str().unwrap() + ); + + let mut wtr = WriterBuilder::new() + .delimiter(self.separator) + .from_path(self.path_to_edges.as_path())?; + + for (start, target) in self.g.edge_indices() { + wtr.serialize(EdgeRecord::new( + start, + target, + g.get_edge_label(start, target), + ))?; + } + + Ok(()) + } +} diff --git a/examples/remove_labels.rs b/src/io/graph_loader.rs similarity index 64% rename from examples/remove_labels.rs rename to src/io/graph_loader.rs index 8b60606a..757b53c8 100644 --- a/examples/remove_labels.rs +++ b/src/io/graph_loader.rs @@ -18,23 +18,22 @@ * specific language governing permissions and limitations * under the License. */ -extern crate rust_graph; -use std::path::Path; +use crate::generic::IdType; +use crate::io::ReadGraph; +use serde::{Deserialize, Serialize}; +use std::hash::Hash; -use rust_graph::graph_impl::UnStaticGraph; -use rust_graph::io::serde::{Deserialize, Serialize}; -use rust_graph::prelude::*; - -fn main() { - let args: Vec<_> = std::env::args().collect(); - - let in_file = Path::new(&args[1]); - let out_file = Path::new(&args[2]); - - let mut graph = UnStaticGraph::::import(in_file).unwrap(); - - graph.remove_labels(); - - graph.export(out_file).unwrap(); +pub trait GraphLoader<'a, Id: IdType, NL: Hash + Eq, EL: Hash + Eq> +where + for<'de> Id: Deserialize<'de> + Serialize, + for<'de> NL: Deserialize<'de> + Serialize, + for<'de> EL: Deserialize<'de> + Serialize, +{ + fn load( + &self, + reader: &'a (dyn ReadGraph + Sync), + thread_cnt: usize, + batch_size: usize, + ); } diff --git a/src/io/ldbc/mod.rs b/src/io/hdfs/mod.rs similarity index 50% rename from src/io/ldbc/mod.rs rename to src/io/hdfs/mod.rs index 66756274..a410f03c 100644 --- a/src/io/ldbc/mod.rs +++ b/src/io/hdfs/mod.rs @@ -18,18 +18,38 @@ * specific language governing permissions and limitations * under the License. */ -pub mod node; -pub mod relation; -pub mod scheme; +pub mod reader; -pub use io::ldbc::scheme::Scheme; - -use generic::{GraphType, IdType}; -use graph_impl::TypedGraphMap; +use std::hash::Hash; use std::path::Path; -pub fn read_ldbc_from_path>( - path: P, -) -> TypedGraphMap { - self::scheme::Scheme::init().from_path(path).unwrap() +use serde::{Deserialize, Serialize}; + +pub use crate::io::hdfs::reader::HDFSReader; +use crate::io::ReadGraphTo; +use generic::{IdType, MutGraphTrait}; + +pub fn read_from_hdfs( + g: &mut G, + path_to_nodes: Vec

, + path_to_edges: Vec

, + separator: Option<&str>, + has_headers: bool, + is_flexible: bool, +) where + for<'de> Id: IdType + Serialize + Deserialize<'de>, + for<'de> NL: Hash + Eq + Serialize + Deserialize<'de> + 'static, + for<'de> EL: Hash + Eq + Serialize + Deserialize<'de> + 'static, + G: MutGraphTrait, + P: AsRef, +{ + let mut reader = HDFSReader::new(path_to_nodes, path_to_edges) + .headers(has_headers) + .flexible(is_flexible); + + if let Some(sep) = separator { + reader = reader.with_separator(sep); + } + + reader.read(g) } diff --git a/src/io/hdfs/reader.rs b/src/io/hdfs/reader.rs new file mode 100644 index 00000000..0d4fbe4d --- /dev/null +++ b/src/io/hdfs/reader.rs @@ -0,0 +1,380 @@ +/* + * Copyright (c) 2018 UNSW Sydney, Data and Knowledge Group. + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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. + */ +/// Nodes: +/// node_id node_label(optional) +/// +/// Edges: +/// src dst edge_label(optional) +/// +/// **Note**: Rows that are unable to parse will be skipped. +use std::hash::Hash; +use std::marker::PhantomData; +use std::path::{Path, PathBuf}; + +use crate::io::csv::reader::parse_prop_map; +use crate::io::csv::record::{EdgeRecord, NodeRecord, PropEdgeRecord, PropNodeRecord}; +use crate::io::csv::CborValue; +use crate::io::{ReadGraph, ReadGraphTo}; +use csv::ReaderBuilder; +use generic::{IdType, Iter}; +use hashbrown::HashMap; +use hdfs::{HdfsFs, HdfsFsCache}; +use serde::Deserialize; +use serde_cbor::to_value; +use std::cell::RefCell; +use std::rc::Rc; + +#[derive(Debug, Default)] +pub struct HDFSReader<'a, Id: IdType, NL: Hash + Eq, EL: Hash + Eq = NL> { + path_to_nodes: Vec, + path_to_edges: Vec, + separator: u8, + has_headers: bool, + // Whether the number of fields in records is allowed to change or not. + is_flexible: bool, + map: HashMap>, + _ph: PhantomData<(Id, NL, EL)>, +} + +impl<'a, Id: IdType, NL: Hash + Eq, EL: Hash + Eq> Clone for HDFSReader<'a, Id, NL, EL> { + fn clone(&self) -> Self { + HDFSReader { + path_to_nodes: self.path_to_nodes.clone(), + path_to_edges: self.path_to_edges.clone(), + separator: self.separator.clone(), + has_headers: self.has_headers.clone(), + is_flexible: self.is_flexible.clone(), + map: HashMap::new(), + _ph: PhantomData, + } + } +} + +impl<'a, Id: IdType, NL: Hash + Eq, EL: Hash + Eq> HDFSReader<'a, Id, NL, EL> { + /// **Note**: `path_to_nodes` or `path_to_edges` need to be formatted as `hdfs://localhost:9000/xx/xxx.csv`. + pub fn new>(path_to_nodes: Vec

, path_to_edges: Vec

) -> Self { + let mut reader = HDFSReader { + path_to_nodes: path_to_nodes + .into_iter() + .flat_map(|p| list_hdfs_files(p)) + .collect(), + path_to_edges: path_to_edges + .into_iter() + .flat_map(|p| list_hdfs_files(p)) + .collect(), + separator: b',', + has_headers: true, + is_flexible: false, + map: HashMap::new(), + _ph: PhantomData, + }; + //storing fs into map cache + let hdfs_cache = Rc::new(RefCell::new(HdfsFsCache::new())); + for path_to_node in reader.path_to_nodes.clone() { + let str_node_path = path_to_node.as_path().to_str().unwrap(); + let fs: HdfsFs = hdfs_cache.borrow_mut().get(str_node_path).ok().unwrap(); + reader.map.insert(String::from(str_node_path), fs); + } + for path_to_edge in reader.path_to_edges.clone() { + let str_edge_path = path_to_edge.as_path().to_str().unwrap(); + let fs: HdfsFs = hdfs_cache.borrow_mut().get(str_edge_path).ok().unwrap(); + reader.map.insert(String::from(str_edge_path), fs); + } + reader + } + + pub fn with_separator(mut self, separator: &str) -> Self { + let sep_string = match separator { + "comma" => ",", + "space" => " ", + "tab" => "\t", + "bar" => "|", + other => other, + }; + + if sep_string.len() != 1 { + panic!("Invalid separator {}.", sep_string); + } + + let sep = sep_string.chars().next().unwrap() as u8; + self.separator = sep; + + self + } + + pub fn headers(mut self, has_headers: bool) -> Self { + self.has_headers = has_headers; + self + } + + pub fn flexible(mut self, is_flexible: bool) -> Self { + self.is_flexible = is_flexible; + self + } +} + +impl<'a, Id: IdType, NL: Hash + Eq + 'static, EL: Hash + Eq + 'static> ReadGraph + for HDFSReader<'a, Id, NL, EL> +where + for<'de> Id: Deserialize<'de>, + for<'de> NL: Deserialize<'de>, + for<'de> EL: Deserialize<'de>, +{ + fn get_node_iter(&self, idx: usize) -> Option)>> { + let node_file = self.path_to_nodes.get(idx).cloned(); + let has_headers = self.has_headers; + let is_flexible = self.is_flexible; + let separator = self.separator; + + node_file + .map(move |path_to_nodes| { + let path_str = path_to_nodes.to_str().unwrap().to_owned(); + + let fs = self.map.get(&path_str).unwrap(); + let node_file_reader = fs.open(&path_str).unwrap(); + if !node_file_reader.is_readable() { + warn!("{:?} are not avaliable!", &path_str); + } + + let rdr = ReaderBuilder::new() + .has_headers(has_headers) + .flexible(is_flexible) + .delimiter(separator) + .from_reader(node_file_reader); + + (rdr, path_str) + }) + .map(|(rdr, path)| { + rdr.into_deserialize() + .enumerate() + .filter_map(move |(i, result)| { + if i == 0 { + info!("Reading nodes from {}", path); + } + + match result { + Ok(_result) => { + let record: NodeRecord = _result; + + Some((record.id, record.label)) + } + Err(e) => { + warn!("Line {:?}: Error when reading csv: {:?}", i + 1, e); + + None + } + } + }) + }) + .map(|iter| Iter::new(Box::new(iter))) + } + + fn get_edge_iter(&self, idx: usize) -> Option)>> { + let edge_file = self.path_to_edges.get(idx).cloned(); + let has_headers = self.has_headers; + let is_flexible = self.is_flexible; + let separator = self.separator; + + edge_file + .map(move |path_to_edges| { + let path_str = path_to_edges.to_str().unwrap().to_owned(); + + let fs = self.map.get(&path_str).unwrap(); + let edge_file_reader = fs.open(&path_str).unwrap(); + if !edge_file_reader.is_readable() { + warn!("{:?} are not avaliable!", &path_str); + } + + let rdr = ReaderBuilder::new() + .has_headers(has_headers) + .flexible(is_flexible) + .delimiter(separator) + .from_reader(edge_file_reader); + + (rdr, path_str) + }) + .map(|(rdr, path)| { + rdr.into_deserialize() + .enumerate() + .filter_map(move |(i, result)| { + if i == 0 { + info!("Reading edges from {}", path); + } + + match result { + Ok(_result) => { + let record: EdgeRecord = _result; + + Some((record.src, record.dst, record.label)) + } + Err(e) => { + warn!("Line {:?}: Error when reading csv: {:?}", i + 1, e); + + None + } + } + }) + }) + .map(|iter| Iter::new(Box::new(iter))) + } + + fn get_prop_node_iter(&self, idx: usize) -> Option, CborValue)>> { + assert!(self.has_headers); + + let node_file = self.path_to_nodes.get(idx).cloned(); + let has_headers = self.has_headers; + let is_flexible = self.is_flexible; + let separator = self.separator; + + node_file + .map(move |path_to_nodes| { + let path_str = path_to_nodes.to_str().unwrap().to_owned(); + + let fs = self.map.get(&path_str).unwrap(); + let node_file_reader = fs.open(&path_str).unwrap(); + if !node_file_reader.is_readable() { + warn!("{:?} are not avaliable!", &path_str); + } + + let rdr = ReaderBuilder::new() + .has_headers(has_headers) + .flexible(is_flexible) + .delimiter(separator) + .from_reader(node_file_reader); + + (rdr, path_str) + }) + .map(|(rdr, path)| { + rdr.into_deserialize().enumerate().map(move |(i, result)| { + if i == 0 { + info!("Reading nodes from {}", path); + } + + let mut record: PropNodeRecord = + result.expect(&format!("Error when reading line {}", i + 1)); + + parse_prop_map(&mut record.properties); + let prop = to_value(record.properties) + .expect(&format!("Error when parsing line {} to Cbor", i + 1)); + + (record.id, record.label, prop) + }) + }) + .map(|iter| Iter::new(Box::new(iter))) + } + + fn get_prop_edge_iter(&self, idx: usize) -> Option, CborValue)>> { + let edge_file = self.path_to_edges.get(idx).cloned(); + let has_headers = self.has_headers; + let is_flexible = self.is_flexible; + let separator = self.separator; + + edge_file + .map(move |path_to_edges| { + let path_str = path_to_edges.to_str().unwrap().to_owned(); + + let fs = self.map.get(&path_str).unwrap(); + let edge_file_reader = fs.open(&path_str).unwrap(); + if !edge_file_reader.is_readable() { + warn!("{:?} are not avaliable!", &path_str); + } + + let rdr = ReaderBuilder::new() + .has_headers(has_headers) + .flexible(is_flexible) + .delimiter(separator) + .from_reader(edge_file_reader); + + (rdr, path_str) + }) + .map(|(rdr, path)| { + rdr.into_deserialize().enumerate().map(move |(i, result)| { + if i == 0 { + info!("Reading edges from {}", path); + } + + let mut record: PropEdgeRecord = + result.expect(&format!("Error when reading line {}", i + 1)); + + parse_prop_map(&mut record.properties); + let prop = to_value(record.properties) + .expect(&format!("Error when parsing line {} to Cbor", i + 1)); + + (record.src, record.dst, record.label, prop) + }) + }) + .map(|iter| Iter::new(Box::new(iter))) + } + + fn num_of_node_files(&self) -> usize { + self.path_to_nodes.len() + } + + fn num_of_edge_files(&self) -> usize { + self.path_to_edges.len() + } +} + +impl<'a, Id: IdType, NL: Hash + Eq + 'static, EL: Hash + Eq + 'static> ReadGraphTo + for HDFSReader<'a, Id, NL, EL> +where + for<'de> Id: Deserialize<'de>, + for<'de> NL: Deserialize<'de>, + for<'de> EL: Deserialize<'de>, +{ +} + +/// enumerate files in a root directory `p` +fn list_hdfs_files>(p: P) -> Vec { + let root_str_path = p.as_ref().to_str().unwrap(); + let hdfs_cache = Rc::new(RefCell::new(HdfsFsCache::new())); + let fs: HdfsFs = hdfs_cache.borrow_mut().get(root_str_path).ok().unwrap(); + let root_file_status = fs.get_file_status(root_str_path); + if root_file_status.is_err() { + //open fail or other unknown error by libhdfs + return vec![]; + } + if root_file_status.unwrap().is_file() { + //Path is a file + return vec![p.as_ref().to_path_buf()]; + } + + //Directory Handler + let mut pending_path = vec![root_str_path]; + let mut fold_path_vec = vec![]; + while pending_path.len() > 0 { + let cur_file_path = pending_path.pop().unwrap(); + let file_status = fs.list_status(cur_file_path); + if file_status.is_err() { + //empty directory or other unknown error by libhdfs + continue; + } + for status in file_status.unwrap().into_iter() { + if status.is_directory() { + pending_path.push(status.name()); + } else { + fold_path_vec.push(Path::new(status.name()).to_path_buf()); + } + } + } + + fold_path_vec +} diff --git a/src/io/ldbc/node.rs b/src/io/ldbc/node.rs deleted file mode 100644 index 371d9c87..00000000 --- a/src/io/ldbc/node.rs +++ /dev/null @@ -1,72 +0,0 @@ -/* - * Copyright (c) 2018 UNSW Sydney, Data and Knowledge Group. - * - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you 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. - */ -use std::collections::HashMap; -use std::path::PathBuf; - -use csv::StringRecord; -use regex::Regex; - -use generic::{GraphTrait, GraphType, IdType, MutGraphTrait}; -use graph_impl::graph_map::TypedGraphMap; - -#[derive(Debug)] -pub struct Node { - name: String, - id_index: usize, - label_index: usize, - file_name_start: Regex, -} - -impl Node { - pub fn new(name: &str, id_index: usize, label_index: usize, file_name_start: &str) -> Self { - Node { - name: name.to_owned(), - id_index, - label_index, - file_name_start: Regex::new(&format!(r"^{}[_\d]*.csv", file_name_start)[..]).unwrap(), - } - } - - pub fn is_match(&self, path: &PathBuf) -> bool { - let filename = path.as_path().file_name().unwrap().to_str().unwrap(); - - self.file_name_start.is_match(filename) - } - - pub fn add_node( - &self, - record: StringRecord, - g: &mut TypedGraphMap, - node_id_map: &mut HashMap, - ) { - let str_id = self.name.clone() + &record[self.id_index]; - - let id = *node_id_map.entry(str_id).or_insert_with(|| { - if let Some(i) = g.max_seen_id() { - i.increment() - } else { - Id::new(0) - } - }); - - g.add_node(id, Some(record[self.label_index].to_owned())); - } -} diff --git a/src/io/ldbc/relation.rs b/src/io/ldbc/relation.rs deleted file mode 100644 index 75f6782b..00000000 --- a/src/io/ldbc/relation.rs +++ /dev/null @@ -1,99 +0,0 @@ -/* - * Copyright (c) 2018 UNSW Sydney, Data and Knowledge Group. - * - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you 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. - */ -use std::collections::HashMap; -use std::path::PathBuf; - -use csv::StringRecord; -use regex::Regex; - -use generic::{GraphTrait, GraphType, IdType, MutGraphTrait}; -use graph_impl::graph_map::TypedGraphMap; - -#[derive(Debug)] -pub struct Relation { - start_label: String, - target_label: String, - edge_label: String, - start_index: usize, - target_index: usize, - file_name_start: Regex, -} - -impl Relation { - pub fn new( - start_label: &str, - target_label: &str, - edge_label: &str, - start_index: usize, - target_index: usize, - file_name_start: &str, - ) -> Self { - Relation { - start_label: start_label.to_owned(), - target_label: target_label.to_owned(), - edge_label: edge_label.to_owned(), - start_index, - target_index, - file_name_start: Regex::new(&format!("{}{}", file_name_start, r"[_\d]*.csv")[..]) - .unwrap(), - } - } - - pub fn is_match(&self, path: &PathBuf) -> bool { - let filename = path.as_path().file_name().unwrap().to_str().unwrap(); - - self.file_name_start.is_match(filename) - } - - pub fn add_edge( - &self, - record: StringRecord, - g: &mut TypedGraphMap, - node_id_map: &mut HashMap, - ) { - let start_str_id = self.start_label.clone() + &record[self.start_index]; - let target_str_id = self.target_label.clone() + &record[self.target_index]; - - let start_id = *node_id_map.entry(start_str_id).or_insert_with(|| { - let i = if let Some(i) = g.max_seen_id() { - i.increment() - } else { - Id::new(0) - }; - g.add_node(i, Some(self.start_label.clone())); - - i - }); - - let target_id = *node_id_map.entry(target_str_id).or_insert_with(|| { - let i = if let Some(i) = g.max_seen_id() { - i.increment() - } else { - Id::new(0) - }; - g.add_node(i, Some(self.target_label.clone())); - - i - }); - - g.add_edge(start_id, target_id, Some(self.edge_label.clone())); - } -} diff --git a/src/io/ldbc/scheme.rs b/src/io/ldbc/scheme.rs deleted file mode 100644 index 6d79ff39..00000000 --- a/src/io/ldbc/scheme.rs +++ /dev/null @@ -1,254 +0,0 @@ -/* - * Copyright (c) 2018 UNSW Sydney, Data and Knowledge Group. - * - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you 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. - */ -use std::collections::HashMap; -use std::fs; -use std::io::Result; -use std::path::Path; - -use generic::{GraphType, IdType}; -use graph_impl::graph_map::TypedGraphMap; -use io::ldbc::node::Node; -use io::ldbc::relation::Relation; - -use csv::ReaderBuilder; - -#[derive(Debug)] -pub struct Scheme { - relations: Vec, - nodes: Vec, - delimiter: u8, -} - -impl Scheme { - pub fn init() -> Self { - let n1 = Node::new("organisation", 0, 1, "organisation_"); - let n2 = Node::new("place", 0, 3, "place_"); - - let r1 = Relation::new( - "comment", - "person", - "hasCreator", - 0, - 1, - "comment_hasCreator_person_", - ); - let r2 = Relation::new("comment", "tag", "hasTag", 0, 1, "comment_hasTag_tag_"); - let r3 = Relation::new( - "comment", - "place", - "isLocatedIn", - 0, - 1, - "comment_isLocatedIn_place_", - ); - let r4 = Relation::new( - "comment", - "comment", - "replyOf", - 0, - 1, - "comment_replyOf_comment_", - ); - let r5 = Relation::new("comment", "post", "replyOf", 0, 1, "comment_replyOf_post_"); - let r6 = Relation::new( - "forum", - "post", - "containerOf", - 0, - 1, - "forum_containerOf_post_", - ); - let r7 = Relation::new( - "forum", - "post", - "containerOf", - 0, - 1, - "forum_containerOf_post_", - ); - let r8 = Relation::new( - "forum", - "person", - "hasMember", - 0, - 1, - "forum_hasMember_person_", - ); - let r9 = Relation::new( - "forum", - "person", - "hasModerator", - 0, - 1, - "forum_hasModerator_person_", - ); - let r10 = Relation::new( - "forum", - "person", - "hasModerator", - 0, - 1, - "forum_hasModerator_person_", - ); - let r11 = Relation::new("forum", "tag", "hasTag", 0, 1, "forum_hasTag_tag_"); - let r12 = Relation::new( - "organisation", - "place", - "isLocatedIn", - 0, - 1, - "organisation_isLocatedIn_place_", - ); - let r13 = Relation::new( - "person", - "tag", - "hasInterest", - 0, - 1, - "person_hasInterest_tag_", - ); - let r14 = Relation::new( - "person", - "place", - "isLocatedIn", - 0, - 1, - "person_isLocatedIn_place_", - ); - let r15 = Relation::new("person", "person", "knows", 0, 1, "person_knows_person_"); - let r16 = Relation::new("person", "comment", "likes", 0, 1, "person_likes_comment_"); - let r17 = Relation::new("person", "post", "likes", 0, 1, "person_likes_post_"); - let r18 = Relation::new( - "person", - "organisation", - "studyAt", - 0, - 1, - "person_studyAt_organisation_", - ); - let r19 = Relation::new( - "person", - "organisation", - "workAt", - 0, - 1, - "person_workAt_organisation_", - ); - let r20 = Relation::new("place", "place", "isPartOf", 0, 1, "place_isPartOf_place_"); - let r21 = Relation::new( - "post", - "person", - "hasCreator", - 0, - 1, - "post_hasCreator_person_", - ); - let r22 = Relation::new("post", "tag", "hasTag", 0, 1, "post_hasTag_tag_"); - let r23 = Relation::new( - "post", - "place", - "isLocatedIn", - 0, - 1, - "post_isLocatedIn_place_", - ); - let r24 = Relation::new("tag", "tagclass", "hasType", 0, 1, "tag_hasType_tagclass_"); - let r25 = Relation::new( - "tagclass", - "tagclass", - "isSubclassOf", - 0, - 1, - "tagclass_isSubclassOf_tagclass_", - ); - - Scheme { - relations: vec![ - r1, r2, r3, r4, r5, r6, r7, r8, r9, r10, r11, r12, r13, r14, r15, r16, r17, r18, - r19, r20, r21, r22, r23, r24, r25, - ], - nodes: vec![n1, n2], - delimiter: b'|', - } - } - - pub fn from_path>( - &self, - path: P, - ) -> Result> { - if !path.as_ref().is_dir() { - panic!("path must be a dir") - } - - let mut files_in_dir = Vec::new(); - - for entry in fs::read_dir(path)? { - let dir = entry?; - let path = dir.path(); - if path.is_file() { - files_in_dir.push(path); - } - } - - let mut g = TypedGraphMap::new(); - let mut node_id_map = HashMap::::new(); - - info!("ldbc::Scheme::from_path - Adding nodes."); - for node in self.nodes.iter() { - for path in files_in_dir.iter() { - if node.is_match(path) { - info!("Reading file '{}'", path.to_str().unwrap()); - - let mut rdr = ReaderBuilder::new() - .delimiter(self.delimiter) - .from_path(path.clone())?; - - for result in rdr.records() { - let record = result?; - - node.add_node::(record, &mut g, &mut node_id_map); - } - } - } - } - - info!("ldbc::Scheme::from_path - Adding relations."); - for relation in self.relations.iter() { - for path in files_in_dir.iter() { - if relation.is_match(path) { - info!("Reading file '{}'", path.to_str().unwrap()); - - let mut rdr = ReaderBuilder::new() - .delimiter(self.delimiter) - .from_path(path.clone())?; - - for result in rdr.records() { - let record = result?; - - relation.add_edge::(record, &mut g, &mut node_id_map); - } - } - } - } - - Ok(g) - } -} diff --git a/src/io/mmap.rs b/src/io/mmap.rs index 87248170..4c7644b0 100644 --- a/src/io/mmap.rs +++ b/src/io/mmap.rs @@ -1,123 +1,123 @@ -/* - * Copyright (c) 2018 UNSW Sydney, Data and Knowledge Group. - * - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you 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. - */ -//! This implements some `Trait` for dumping and loading memory-mapped (mmap) file. -//! Memory-mapped file is a mechanism in Unix-like system to reduce memory cost. -//! More details can be found here: https://en.wikipedia.org/wiki/Memory-mapped_file. - -extern crate memmap; - -use std::fs::File; -use std::io::{BufWriter, Result, Write}; -use std::marker::PhantomData; -use std::mem; -use std::ops; -use std::path::Path; -use std::slice; - -pub struct TypedMemoryMap { - pub map: memmap::Mmap, - pub len: usize, - type_len: usize, - // in bytes (needed because map extends to full block) - phn: PhantomData, -} - -impl TypedMemoryMap { - pub fn new>(filename: P) -> Self { - let file = File::open(filename).expect("error opening file"); - let size = file.metadata().expect("error reading metadata").len() as usize; - let type_len = mem::size_of::(); - TypedMemoryMap { - map: unsafe { memmap::Mmap::map(&file).unwrap() }, - len: size / type_len, - type_len, - phn: PhantomData, - } - } - - pub fn with_mmap(map: memmap::Mmap) -> Self { - let size = map.len(); - let type_len = mem::size_of::(); - Self { - map, - len: size / type_len, - type_len, - phn: PhantomData, - } - } -} - -impl ops::Index for TypedMemoryMap { - type Output = [T]; - #[inline] - fn index(&self, _index: ops::RangeFull) -> &[T] { - unsafe { slice::from_raw_parts(self.map.as_ptr() as *const T, self.len) } - } -} - -impl ops::Index> for TypedMemoryMap { - type Output = [T]; - #[inline] - fn index(&self, _index: ops::RangeFrom) -> &[T] { - let index = _index.start; - unsafe { - slice::from_raw_parts( - self.map.as_ptr().add(index * self.type_len) as *const T, - self.len - index, - ) - } - } -} - -impl ops::Index> for TypedMemoryMap { - type Output = [T]; - #[inline] - fn index(&self, _index: ops::RangeTo) -> &[T] { - unsafe { slice::from_raw_parts(self.map.as_ptr() as *const T, _index.end) } - } -} - -#[inline] -pub fn typed_as_byte_slice(slice: &[T]) -> &[u8] { - unsafe { - slice::from_raw_parts( - slice.as_ptr() as *const u8, - slice.len() * mem::size_of::(), - ) - } -} - -pub unsafe fn dump(data: &[T], write: W) -> Result<()> { - let mut writer = BufWriter::new(write); - let mut slice = typed_as_byte_slice(data); - - while !slice.is_empty() { - let to_write = if slice.len() < 10000 { - slice.len() - } else { - 10000 - }; - writer.write_all(&slice[..to_write])?; - slice = &slice[to_write..]; - } - - writer.flush() -} +/* + * Copyright (c) 2018 UNSW Sydney, Data and Knowledge Group. + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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. + */ +//! This implements some `Trait` for dumping and loading memory-mapped (mmap) file. +//! Memory-mapped file is a mechanism in Unix-like system to reduce memory cost. +//! More details can be found here: https://en.wikipedia.org/wiki/Memory-mapped_file. + +extern crate memmap; + +use std::fs::File; +use std::io::{BufWriter, Result, Write}; +use std::marker::PhantomData; +use std::mem; +use std::ops; +use std::path::Path; +use std::slice; + +pub struct TypedMemoryMap { + pub map: memmap::Mmap, + pub len: usize, + type_len: usize, + // in bytes (needed because map extends to full block) + phn: PhantomData, +} + +impl TypedMemoryMap { + pub fn new>(filename: P) -> Self { + let file = File::open(filename).expect("error opening file"); + let size = file.metadata().expect("error reading metadata").len() as usize; + let type_len = mem::size_of::(); + TypedMemoryMap { + map: unsafe { memmap::Mmap::map(&file).unwrap() }, + len: size / type_len, + type_len, + phn: PhantomData, + } + } + + pub fn with_mmap(map: memmap::Mmap) -> Self { + let size = map.len(); + let type_len = mem::size_of::(); + Self { + map, + len: size / type_len, + type_len, + phn: PhantomData, + } + } +} + +impl ops::Index for TypedMemoryMap { + type Output = [T]; + #[inline] + fn index(&self, _index: ops::RangeFull) -> &[T] { + unsafe { slice::from_raw_parts(self.map.as_ptr() as *const T, self.len) } + } +} + +impl ops::Index> for TypedMemoryMap { + type Output = [T]; + #[inline] + fn index(&self, _index: ops::RangeFrom) -> &[T] { + let index = _index.start; + unsafe { + slice::from_raw_parts( + self.map.as_ptr().add(index * self.type_len) as *const T, + self.len - index, + ) + } + } +} + +impl ops::Index> for TypedMemoryMap { + type Output = [T]; + #[inline] + fn index(&self, _index: ops::RangeTo) -> &[T] { + unsafe { slice::from_raw_parts(self.map.as_ptr() as *const T, _index.end) } + } +} + +#[inline] +pub fn typed_as_byte_slice(slice: &[T]) -> &[u8] { + unsafe { + slice::from_raw_parts( + slice.as_ptr() as *const u8, + slice.len() * mem::size_of::(), + ) + } +} + +pub unsafe fn dump(data: &[T], write: W) -> Result<()> { + let mut writer = BufWriter::new(write); + let mut slice = typed_as_byte_slice(data); + + while !slice.is_empty() { + let to_write = if slice.len() < 10000 { + slice.len() + } else { + 10000 + }; + writer.write_all(&slice[..to_write])?; + slice = &slice[to_write..]; + } + + writer.flush() +} diff --git a/src/io/mod.rs b/src/io/mod.rs index a839e81a..c44974ae 100644 --- a/src/io/mod.rs +++ b/src/io/mod.rs @@ -19,12 +19,19 @@ * under the License. */ pub mod csv; +pub mod graph_loader; pub mod mmap; +pub mod read_graph; +pub mod rocksdb; pub mod serde; +pub mod tikv; -pub use io::csv::{read_from_csv, write_to_csv}; +pub use crate::io::csv::{read_from_csv, write_to_csv, CSVReader, CSVWriter}; +pub use crate::io::graph_loader::GraphLoader; +pub use crate::io::read_graph::{ReadGraph, ReadGraphTo}; +pub use crate::io::serde::{Deserialize, Deserializer, Serialize, Serializer}; -#[cfg(feature = "ldbc")] -pub mod ldbc; -#[cfg(feature = "ldbc")] -pub use io::ldbc::read_ldbc_from_path; +#[cfg(feature = "hdfs")] +pub mod hdfs; +#[cfg(feature = "hdfs")] +pub use io::hdfs::{read_from_hdfs, HDFSReader}; diff --git a/src/io/read_graph.rs b/src/io/read_graph.rs new file mode 100644 index 00000000..27528bf9 --- /dev/null +++ b/src/io/read_graph.rs @@ -0,0 +1,93 @@ +/* + * Copyright (c) 2018 UNSW Sydney, Data and Knowledge Group. + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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. + */ +use crate::generic::{IdType, Iter, MutGraphTrait}; +use crate::io::csv::CborValue; +use itertools::Itertools; +use serde::Deserialize; +use std::hash::Hash; + +pub trait ReadGraph +where + for<'de> Id: Deserialize<'de>, + for<'de> NL: Deserialize<'de>, + for<'de> EL: Deserialize<'de>, +{ + fn get_node_iter(&self, idx: usize) -> Option)>>; + fn get_edge_iter(&self, idx: usize) -> Option)>>; + fn get_prop_node_iter(&self, idx: usize) -> Option, CborValue)>>; + fn get_prop_edge_iter(&self, idx: usize) -> Option, CborValue)>>; + fn num_of_node_files(&self) -> usize; + fn num_of_edge_files(&self) -> usize; + + fn node_iter(&self) -> Iter<(Id, Option)> { + let iter_vec = (0..self.num_of_node_files()) + .map(|i| self.get_node_iter(i).unwrap()) + .collect_vec(); + let iter = iter_vec.into_iter().flat_map(|x| x); + + Iter::new(Box::new(iter)) + } + + fn edge_iter(&self) -> Iter<(Id, Id, Option)> { + let iter_vec = (0..self.num_of_edge_files()) + .map(|i| self.get_edge_iter(i).unwrap()) + .collect_vec(); + let iter = iter_vec.into_iter().flat_map(|x| x); + + Iter::new(Box::new(iter)) + } + + fn prop_node_iter(&self) -> Iter<(Id, Option, CborValue)> { + let iter_vec = (0..self.num_of_node_files()) + .map(|i| self.get_prop_node_iter(i).unwrap()) + .collect_vec(); + let iter = iter_vec.into_iter().flat_map(|x| x); + + Iter::new(Box::new(iter)) + } + + fn prop_edge_iter(&self) -> Iter<(Id, Id, Option, CborValue)> { + let iter_vec = (0..self.num_of_edge_files()) + .map(|i| self.get_prop_edge_iter(i).unwrap()) + .collect_vec(); + let iter = iter_vec.into_iter().flat_map(|x| x); + + Iter::new(Box::new(iter)) + } +} + +pub trait ReadGraphTo: + ReadGraph +where + for<'de> Id: Deserialize<'de>, + for<'de> NL: Deserialize<'de>, + for<'de> EL: Deserialize<'de>, +{ + fn read, L: IdType>(&self, g: &mut G) { + for (n, label) in self.node_iter() { + g.add_node(n, label); // store graph info from the CSVReader + } + + for (s, d, label) in self.edge_iter() { + g.add_edge(s, d, label); + } + } +} diff --git a/src/graph_impl/static_graph/mmap/mod.rs b/src/io/rocksdb/mod.rs similarity index 72% rename from src/graph_impl/static_graph/mmap/mod.rs rename to src/io/rocksdb/mod.rs index 5f5c0544..3c6de0da 100644 --- a/src/graph_impl/static_graph/mmap/mod.rs +++ b/src/io/rocksdb/mod.rs @@ -18,11 +18,4 @@ * specific language governing permissions and limitations * under the License. */ -//! This file defines a mmap version of `StaticGraph`, so that when the graph is huge, -//! we can rely on mmap to save physical memory usage. - -pub mod edge_vec_mmap; -pub mod graph_mmap; - -pub use graph_impl::static_graph::mmap::edge_vec_mmap::EdgeVecMmap; -pub use graph_impl::static_graph::mmap::graph_mmap::StaticGraphMmap; +pub mod rocksdb_loader; diff --git a/src/io/rocksdb/rocksdb_loader.rs b/src/io/rocksdb/rocksdb_loader.rs new file mode 100644 index 00000000..711bbf41 --- /dev/null +++ b/src/io/rocksdb/rocksdb_loader.rs @@ -0,0 +1,176 @@ +/* + * Copyright (c) 2018 UNSW Sydney, Data and Knowledge Group. + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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. + */ +use crate::generic::{IdType, Iter}; +use crate::io::tikv::{serialize_edge_item, serialize_node_item}; +use crate::io::{GraphLoader, ReadGraph}; +use itertools::Itertools; +use rocksdb::Options; +use rocksdb::{WriteBatch, DB as Tree}; +use serde::{Deserialize, Serialize}; +use serde_cbor::Value; +use std::hash::Hash; +use std::path::Path; + +#[derive(Debug)] +pub struct RocksDBLoader { + node_property: Tree, + edge_property: Tree, + is_directed: bool, +} + +impl RocksDBLoader { + pub fn new(node_path: &Path, edge_path: &Path, is_directed: bool) -> Self { + Tree::destroy(&Options::default(), &node_path).expect("Destroy node db failed"); + Tree::destroy(&Options::default(), &edge_path).expect("Destroy edge db failed"); + + let mut opts = Options::default(); + opts.create_if_missing(true); + + let node_tree = Tree::open(&opts, node_path).expect("Open node db failed"); + let edge_tree = Tree::open(&opts, edge_path).expect("Open edge db failed"); + RocksDBLoader { + node_property: node_tree, + is_directed, + edge_property: edge_tree, + } + } +} + +impl<'a, Id: IdType, NL: Hash + Eq + 'static, EL: Hash + Eq + 'static> GraphLoader<'a, Id, NL, EL> + for RocksDBLoader +where + for<'de> Id: Deserialize<'de> + Serialize, + for<'de> NL: Deserialize<'de> + Serialize, + for<'de> EL: Deserialize<'de> + Serialize, +{ + ///loading graph into tikv + fn load( + &self, + reader: &'a (dyn ReadGraph + Sync), + thread_cnt: usize, + batch_size: usize, + ) { + let mut scope = scoped_threadpool::Pool::new(2); + scope.scoped(|scope| { + scope.execute(|| { + parallel_nodes_loading(&self.node_property, reader, thread_cnt, batch_size); + }); + scope.execute(|| { + parallel_edges_loading(&self.edge_property, reader, thread_cnt, batch_size); + }); + }); + } +} +fn parallel_nodes_loading<'a, Id: IdType, NL: Hash + Eq + 'static, EL: Hash + Eq + 'static>( + node_property: &Tree, + reader: &'a (dyn ReadGraph + Sync), + thread_cnt: usize, + batch_size: usize, +) where + for<'de> Id: Deserialize<'de> + Serialize, + for<'de> NL: Deserialize<'de> + Serialize, + for<'de> EL: Deserialize<'de> + Serialize, +{ + let mut thread_pool = scoped_threadpool::Pool::new(thread_cnt as u32); + let node_file_cnt = reader.num_of_node_files(); + thread_pool.scoped(|scope| { + for tid in 0..thread_cnt { + let batch_file_cnt = node_file_cnt / thread_cnt + 1; + let start_index = tid * batch_file_cnt; + scope.execute(move || { + (start_index..start_index + batch_file_cnt).for_each(|fid| { + if let Some(node_iter) = reader.get_prop_node_iter(fid) { + load_nodes_to_tikv(node_property, node_iter, batch_size); + } + }); + }) + } + }); +} + +fn parallel_edges_loading<'a, Id: IdType, NL: Hash + Eq + 'static, EL: Hash + Eq + 'static>( + edge_property: &Tree, + reader: &'a (dyn ReadGraph + Sync), + thread_cnt: usize, + batch_size: usize, +) where + for<'de> Id: Deserialize<'de> + Serialize, + for<'de> NL: Deserialize<'de> + Serialize, + for<'de> EL: Deserialize<'de> + Serialize, +{ + let mut thread_pool = scoped_threadpool::Pool::new(thread_cnt as u32); + let edge_file_cnt = reader.num_of_edge_files(); + thread_pool.scoped(|scope| { + for tid in 0..thread_cnt { + let batch_file_cnt = edge_file_cnt / thread_cnt + 1; + let start_index = tid * batch_file_cnt; + scope.execute(move || { + (start_index..start_index + batch_file_cnt).for_each(|fid| { + if let Some(edge_iter) = reader.get_prop_edge_iter(fid) { + load_edges_to_tikv(edge_property, edge_iter, batch_size); + } + }); + }) + } + }); +} + +fn load_nodes_to_tikv<'a, Id: IdType, NL: Hash + Eq + 'static>( + node_property: &Tree, + node_iter: Iter<(Id, Option, Value)>, + batch_size: usize, +) where + for<'de> Id: Deserialize<'de> + Serialize, + for<'de> NL: Deserialize<'de> + Serialize, +{ + let chunks = node_iter.chunks(batch_size); + for chunk in &chunks { + let mut batch = WriteBatch::default(); + for x in chunk { + let pair = serialize_node_item(x); + let _ = batch.put(pair.0, pair.1); + } + node_property + .write(batch) + .expect("Insert node property failed!"); + } +} + +fn load_edges_to_tikv( + edge_property: &Tree, + edge_iter: Iter<(Id, Id, Option, Value)>, + batch_size: usize, +) where + for<'de> Id: Deserialize<'de> + Serialize, + for<'de> EL: Deserialize<'de> + Serialize, +{ + let chunks = edge_iter.chunks(batch_size); + for chunk in &chunks { + let mut batch = WriteBatch::default(); + for x in chunk { + let pair = serialize_edge_item(x); + let _ = batch.put(pair.0, pair.1); + } + edge_property + .write(batch) + .expect("Insert edges property failed!"); + } +} diff --git a/src/io/serde.rs b/src/io/serde.rs index 58eea950..c36e95f1 100644 --- a/src/io/serde.rs +++ b/src/io/serde.rs @@ -1,77 +1,77 @@ -/* - * Copyright (c) 2018 UNSW Sydney, Data and Knowledge Group. - * - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you 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. - */ -use std::fs::File; -use std::io::{BufReader, BufWriter}; -use std::path::Path; - -use serde::{de, ser}; - -pub use bincode::Result; -use bincode::{deserialize_from, serialize_into}; - -pub struct Serializer; -pub struct Deserializer; - -pub trait Serialize: ser::Serialize { - #[inline(always)] - fn export

(&self, path: P) -> Result<()> - where - P: AsRef, - { - Serializer::export(&self, path) - } -} - -pub trait Deserialize: de::DeserializeOwned { - #[inline(always)] - fn import

(path: P) -> Result - where - P: AsRef, - { - Deserializer::import(path) - } -} - -impl Serializer { - #[inline(always)] - pub fn export(obj: &T, path: P) -> Result<()> - where - T: ser::Serialize, - P: AsRef, - { - let mut writer = BufWriter::new(File::create(path)?); - - serialize_into(&mut writer, &obj) - } -} - -impl Deserializer { - #[inline(always)] - pub fn import(path: P) -> Result - where - T: de::DeserializeOwned, - P: AsRef, - { - let mut reader = BufReader::new(File::open(path)?); - - deserialize_from(&mut reader) - } -} +/* + * Copyright (c) 2018 UNSW Sydney, Data and Knowledge Group. + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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. + */ +use std::fs::File; +use std::io::{BufReader, BufWriter}; +use std::path::Path; + +use serde::{de, ser}; + +pub use bincode::Result; +use bincode::{deserialize_from, serialize_into}; + +pub struct Serializer; +pub struct Deserializer; + +pub trait Serialize: ser::Serialize { + #[inline(always)] + fn export

(&self, path: P) -> Result<()> + where + P: AsRef, + { + Serializer::export(&self, path) + } +} + +pub trait Deserialize: de::DeserializeOwned { + #[inline(always)] + fn import

(path: P) -> Result + where + P: AsRef, + { + Deserializer::import(path) + } +} + +impl Serializer { + #[inline(always)] + pub fn export(obj: &T, path: P) -> Result<()> + where + T: ser::Serialize, + P: AsRef, + { + let mut writer = BufWriter::new(File::create(path)?); + + serialize_into(&mut writer, &obj) + } +} + +impl Deserializer { + #[inline(always)] + pub fn import(path: P) -> Result + where + T: de::DeserializeOwned, + P: AsRef, + { + let mut reader = BufReader::new(File::open(path)?); + + deserialize_from(&mut reader) + } +} diff --git a/src/io/tikv/mod.rs b/src/io/tikv/mod.rs new file mode 100644 index 00000000..7d886ae5 --- /dev/null +++ b/src/io/tikv/mod.rs @@ -0,0 +1,75 @@ +/* + * Copyright (c) 2018 UNSW Sydney, Data and Knowledge Group. + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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. + */ +use std::collections::BTreeMap; + +pub mod tikv_loader; +use crate::generic::IdType; +use serde::{Deserialize, Serialize}; +use serde_cbor::{to_value, Value}; +use std::hash::Hash; + +pub fn serialize_node_item<'a, Id: IdType, NL: Hash + Eq + 'static>( + mut x: (Id, Option, Value), +) -> (Vec, Vec) +where + for<'de> Id: Deserialize<'de> + Serialize, + for<'de> NL: Deserialize<'de> + Serialize, +{ + let mut default_map = BTreeMap::new(); + let props_map = x.2.as_object_mut().unwrap_or(&mut default_map); + if x.1.is_some() { + let label = to_value(x.1.unwrap()); + if label.is_ok() { + return ( + bincode::serialize(&x.0).unwrap(), + serde_cbor::to_vec(&(Some(label.unwrap()), props_map)).unwrap(), + ); + } + } + ( + bincode::serialize(&(x.0)).unwrap(), + serde_cbor::to_vec(&(Option::::None, props_map)).unwrap(), + ) +} + +pub fn serialize_edge_item<'a, Id: IdType, EL: Hash + Eq + 'static>( + mut x: (Id, Id, Option, Value), +) -> (Vec, Vec) +where + for<'de> Id: Deserialize<'de> + Serialize, + for<'de> EL: Deserialize<'de> + Serialize, +{ + let mut default_map = BTreeMap::new(); + let props_map = x.3.as_object_mut().unwrap_or(&mut default_map); + if let Some(l) = x.2 { + let label = to_value(l); + if label.is_ok() { + return ( + bincode::serialize(&(x.0, x.1)).unwrap(), + serde_cbor::to_vec(&(Some(label.unwrap()), props_map)).unwrap(), + ); + } + } + ( + bincode::serialize(&(x.0, x.1)).unwrap(), + serde_cbor::to_vec(&(Option::::None, props_map)).unwrap(), + ) +} diff --git a/src/io/tikv/tikv_loader.rs b/src/io/tikv/tikv_loader.rs new file mode 100644 index 00000000..d9d55da3 --- /dev/null +++ b/src/io/tikv/tikv_loader.rs @@ -0,0 +1,212 @@ +/* + * Copyright (c) 2018 UNSW Sydney, Data and Knowledge Group. + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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. + */ +use crate::generic::{IdType, Iter}; +use crate::io::csv::CborValue as Value; +use crate::io::tikv::{serialize_edge_item, serialize_node_item}; +use crate::io::{GraphLoader, ReadGraph}; +use futures::executor::block_on; +use itertools::Itertools; +use serde::{Deserialize, Serialize}; +use std::hash::Hash; +use std::sync::Arc; +use tikv_client::raw::Client; +use tikv_client::Config; +use tokio::runtime::Runtime; + +#[derive(Debug)] +pub struct TikvLoader { + node_property_config: Config, + edge_property_config: Config, + is_directed: bool, +} + +impl TikvLoader { + pub fn new( + node_property_config: Config, + edge_property_config: Config, + is_directed: bool, + ) -> Self { + TikvLoader { + node_property_config, + edge_property_config, + is_directed, + } + } +} + +impl<'a, Id: IdType, NL: Hash + Eq + 'static, EL: Hash + Eq + 'static> GraphLoader<'a, Id, NL, EL> + for TikvLoader +where + for<'de> Id: Deserialize<'de> + Serialize, + for<'de> NL: Deserialize<'de> + Serialize, + for<'de> EL: Deserialize<'de> + Serialize, +{ + ///loading graph into tikv + fn load( + &self, + reader: &'a (dyn ReadGraph + Sync), + thread_cnt: usize, + batch_size: usize, + ) { + let mut scope = scoped_threadpool::Pool::new(2); + let rt = Arc::new(Runtime::new().unwrap()); + let node_runtime = rt.clone(); + let edge_runtime = rt.clone(); + scope.scoped(|scope| { + scope.execute(|| { + parallel_nodes_loading( + &self.node_property_config, + reader, + node_runtime, + thread_cnt, + batch_size, + ); + }); + scope.execute(|| { + parallel_edges_loading( + &self.edge_property_config, + reader, + edge_runtime, + thread_cnt, + batch_size, + ); + }); + }); + } +} + +/// Deliver files for threads. +fn parallel_nodes_loading<'a, Id: IdType, NL: Hash + Eq + 'static, EL: Hash + Eq + 'static>( + node_config: &Config, + reader: &'a (dyn ReadGraph + Sync), + rt: Arc, + thread_cnt: usize, + batch_size: usize, +) where + for<'de> Id: Deserialize<'de> + Serialize, + for<'de> NL: Deserialize<'de> + Serialize, + for<'de> EL: Deserialize<'de> + Serialize, +{ + let mut thread_pool = scoped_threadpool::Pool::new(thread_cnt as u32); + let node_file_cnt = reader.num_of_node_files(); + thread_pool.scoped(|scope| { + for tid in 0..thread_cnt { + let batch_file_cnt = node_file_cnt / thread_cnt + 1; + let start_index = tid * batch_file_cnt; + let node_runtime = rt.clone(); + scope.execute(move || { + for fid in start_index..start_index + batch_file_cnt { + if let Some(node_iter) = reader.get_prop_node_iter(fid) { + load_nodes_to_tikv( + node_config.clone(), + node_iter, + node_runtime.clone(), + batch_size, + ); + } + } + }) + } + }); +} + +fn parallel_edges_loading<'a, Id: IdType, NL: Hash + Eq + 'static, EL: Hash + Eq + 'static>( + edge_config: &Config, + reader: &'a (dyn ReadGraph + Sync), + rt: Arc, + thread_cnt: usize, + batch_size: usize, +) where + for<'de> Id: Deserialize<'de> + Serialize, + for<'de> NL: Deserialize<'de> + Serialize, + for<'de> EL: Deserialize<'de> + Serialize, +{ + let mut thread_pool = scoped_threadpool::Pool::new(thread_cnt as u32); + let edge_file_cnt = reader.num_of_edge_files(); + thread_pool.scoped(|scope| { + for tid in 0..thread_cnt { + let batch_file_cnt = edge_file_cnt / thread_cnt + 1; + let start_index = tid * batch_file_cnt; + let edge_runtime = rt.clone(); + scope.execute(move || { + for fid in start_index..start_index + batch_file_cnt { + if let Some(edge_iter) = reader.get_prop_edge_iter(fid) { + load_edges_to_tikv( + edge_config.clone(), + edge_iter, + edge_runtime.clone(), + batch_size, + ); + } + } + }) + } + }); +} + +fn load_nodes_to_tikv<'a, Id: IdType, NL: Hash + Eq + 'static>( + node_config: Config, + node_iter: Iter<(Id, Option, Value)>, + rt: Arc, + batch_size: usize, +) where + for<'de> Id: Deserialize<'de> + Serialize, + for<'de> NL: Deserialize<'de> + Serialize, +{ + let client = Client::new(node_config.clone()).unwrap(); + let chunks = node_iter.chunks(batch_size); + let mut task_controller = vec![]; + for chunk in &chunks { + let batch_nodes = chunk.map(|x| serialize_node_item(x)).collect_vec(); + let local_client = client.clone(); + task_controller.push(rt.spawn(async move { + let _ = local_client.batch_put(batch_nodes).await; + })); + } + block_on(async { + futures::future::join_all(task_controller).await; + }); +} + +//https://github.com/tikv/tikv/issues/3611 +fn load_edges_to_tikv( + edge_config: Config, + edge_iter: Iter<(Id, Id, Option, Value)>, + rt: Arc, + batch_size: usize, +) where + for<'de> Id: Deserialize<'de> + Serialize, + for<'de> EL: Deserialize<'de> + Serialize, +{ + let client = Client::new(edge_config.clone()).unwrap(); + let chunks = edge_iter.chunks(batch_size); + let mut task_controller = vec![]; + for chunk in &chunks { + let batch_edges = chunk.map(|x| serialize_edge_item(x)).collect_vec(); + let local_client = client.clone(); + task_controller.push(rt.spawn(async move { + let _ = local_client.batch_put(batch_edges).await; + })); + } + block_on(async { + futures::future::join_all(task_controller).await; + }); +} diff --git a/src/lib.rs b/src/lib.rs index 2f73abb0..3983a45d 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,53 +1,61 @@ -/* - * Copyright (c) 2018 UNSW Sydney, Data and Knowledge Group. - * - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you 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. - */ -extern crate bincode; -extern crate counter; -extern crate csv; -extern crate fixedbitset; -extern crate fnv; -extern crate indexmap; -extern crate itertools; -extern crate rand; -extern crate serde; - -#[macro_use] -extern crate log; - -#[macro_use] -extern crate serde_derive; - -#[cfg(feature = "ldbc")] -extern crate regex; - -pub mod algorithm; -pub mod generic; -pub mod graph_gen; -pub mod graph_impl; -pub mod io; -pub mod map; -pub mod prelude; - -pub use graph_impl::{ - DiGraphMap, DiStaticGraph, GraphMap, StaticGraph, StaticGraphMmap, UnGraphMap, UnStaticGraph, -}; - -pub static VERSION: &str = env!("CARGO_PKG_VERSION"); -pub static NAME: &str = env!("CARGO_PKG_NAME"); +/* + * Copyright (c) 2018 UNSW Sydney, Data and Knowledge Group. + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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. + */ +#![feature(async_await)] +#![feature(in_band_lifetimes)] + +extern crate bincode; +extern crate counter; +extern crate csv; +extern crate fixedbitset; +extern crate fxhash; +extern crate hashbrown; +extern crate indexmap; +extern crate itertools; +extern crate rand; +extern crate rayon; +extern crate rocksdb; +extern crate serde; +extern crate serde_cbor; +extern crate serde_json; +extern crate tikv_client; + +//extern crate sled; +#[macro_use] +extern crate log; +#[macro_use] +extern crate serde_derive; + +#[cfg(feature = "hdfs")] +extern crate hdfs; + +pub mod algorithm; +pub mod generic; +pub mod graph_gen; +pub mod graph_impl; +pub mod io; +pub mod map; +pub mod prelude; +pub mod property; + +pub use graph_impl::{DiGraphMap, DiStaticGraph, GraphMap, StaticGraph, UnGraphMap, UnStaticGraph}; + +pub static VERSION: &str = env!("CARGO_PKG_VERSION"); +pub static NAME: &str = env!("CARGO_PKG_NAME"); diff --git a/src/map/mod.rs b/src/map/mod.rs index 2636a0fc..3f9e3118 100644 --- a/src/map/mod.rs +++ b/src/map/mod.rs @@ -1,27 +1,27 @@ -/* - * Copyright (c) 2018 UNSW Sydney, Data and Knowledge Group. - * - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you 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. - */ -/// Implementations of id-item mapping table that -/// maps arbitrary data to `usize` integer. -pub mod set_map; -pub mod vec_map; - -pub use map::set_map::SetMap; -pub use map::vec_map::VecMap; +/* + * Copyright (c) 2018 UNSW Sydney, Data and Knowledge Group. + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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. + */ +/// Implementations of id-item mapping table that +/// maps arbitrary data to `usize` integer. +pub mod set_map; +pub mod vec_map; + +pub use crate::map::set_map::SetMap; +pub use crate::map::vec_map::VecMap; diff --git a/src/map/set_map.rs b/src/map/set_map.rs index 7ea6271d..e9a8a218 100644 --- a/src/map/set_map.rs +++ b/src/map/set_map.rs @@ -1,193 +1,192 @@ -/* - * Copyright (c) 2018 UNSW Sydney, Data and Knowledge Group. - * - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you 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. - */ -use std::hash::{Hash, Hasher}; -use std::iter::FromIterator; - -use indexmap::IndexSet; -use serde; - -use generic::{Iter, MapTrait, MutMapTrait}; -use io::serde::{Deserialize, Serialize}; -use map::VecMap; - -/// More efficient but less compact. -#[derive(Debug, PartialEq, Eq, Clone, Serialize, Deserialize)] -pub struct SetMap { - labels: IndexSet, -} - -impl Serialize for SetMap where L: serde::Serialize {} - -impl Deserialize for SetMap where L: for<'de> serde::Deserialize<'de> {} - -impl SetMap { - pub fn new() -> Self { - SetMap { - labels: IndexSet::::new(), - } - } - - pub fn with_capacity(capacity: usize) -> Self { - SetMap { - labels: IndexSet::::with_capacity(capacity), - } - } - - pub fn with_data(data: IndexSet) -> Self { - SetMap { labels: data } - } - - pub fn from_vec(vec: Vec) -> Self { - let indexset: IndexSet<_> = vec.into_iter().collect(); - - SetMap::with_data(indexset) - } - - pub fn clear(&mut self) { - self.labels.clear(); - } -} - -impl Default for SetMap { - fn default() -> Self { - SetMap::new() - } -} - -impl MapTrait for SetMap { - /// *O(1)* - #[inline] - fn get_item(&self, id: usize) -> Option<&L> { - self.labels.get_index(id) - } - - /// *O(1)* - #[inline] - fn find_index(&self, item: &L) -> Option { - match self.labels.get_full(item) { - Some((i, _)) => Some(i), - None => None, - } - } - - /// *O(1)* - #[inline] - fn contains(&self, item: &L) -> bool { - self.labels.contains(item) - } - - #[inline] - fn items<'a>(&'a self) -> Iter<'a, &L> { - Iter::new(Box::new(self.labels.iter())) - } - - #[inline] - fn items_vec(self) -> Vec { - self.labels.into_iter().collect() - } - - /// *O(1)* - #[inline] - fn len(&self) -> usize { - self.labels.len() - } -} - -impl MutMapTrait for SetMap { - /// *O(1)* - #[inline] - fn add_item(&mut self, item: L) -> usize { - if self.labels.contains(&item) { - self.labels.get_full(&item).unwrap().0 - } else { - self.labels.insert(item); - - self.len() - 1 - } - } - - /// *O(1)* - #[inline] - fn pop_item(&mut self) -> Option { - self.labels.pop() - } -} - -impl Hash for SetMap { - fn hash(&self, state: &mut H) { - for l in self.items() { - l.hash(state); - } - } -} - -impl FromIterator for SetMap { - fn from_iter>(iter: T) -> Self { - let mut map = SetMap::new(); - - for i in iter { - map.add_item(i); - } - - map - } -} - -impl From> for SetMap { - fn from(vec: Vec) -> Self { - SetMap::from_vec(vec) - } -} - -impl<'a, L: Hash + Eq + Clone> From<&'a Vec> for SetMap { - fn from(vec: &'a Vec) -> Self { - SetMap::from_vec(vec.clone()) - } -} - -impl From> for SetMap { - fn from(vec_map: VecMap) -> Self { - let data = vec_map.items_vec(); - - SetMap::from_vec(data) - } -} - -impl<'a, L: Hash + Eq + Clone> From<&'a VecMap> for SetMap { - fn from(vec_map: &'a VecMap) -> Self { - let data = vec_map.clone().items_vec(); - - SetMap::from_vec(data) - } -} - -#[macro_export] -macro_rules! setmap { - ( $( $x:expr ),* ) => { - { - let mut temp_map = SetMap::new(); - $( - temp_map.add_item($x); - )* - temp_map - } - }; -} +/* + * Copyright (c) 2018 UNSW Sydney, Data and Knowledge Group. + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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. + */ +use std::hash::{Hash, Hasher}; +use std::iter::FromIterator; + +use fxhash::FxBuildHasher; +use indexmap::IndexSet; +use serde; + +use crate::generic::{Iter, MapTrait, MutMapTrait}; +use crate::io::{Deserialize, Serialize}; +use crate::map::VecMap; + +type FxIndexSet = IndexSet; + +/// More efficient but less compact. +#[derive(Debug, PartialEq, Eq, Clone, Serialize, Deserialize)] +pub struct SetMap { + labels: FxIndexSet, +} + +impl Serialize for SetMap where L: serde::Serialize {} + +impl Deserialize for SetMap where L: for<'de> serde::Deserialize<'de> {} + +impl SetMap { + pub fn new() -> Self { + SetMap { + labels: FxIndexSet::default(), + } + } + + pub fn with_capacity(capacity: usize) -> Self { + SetMap { + labels: IndexSet::with_capacity_and_hasher(capacity, FxBuildHasher::default()), + } + } + + pub fn from_vec(vec: Vec) -> Self { + SetMap { + labels: vec.into_iter().collect(), + } + } + + pub fn clear(&mut self) { + self.labels.clear(); + } +} + +impl Default for SetMap { + fn default() -> Self { + SetMap::new() + } +} + +impl MapTrait for SetMap { + /// *O(1)* + #[inline] + fn get_item(&self, id: usize) -> Option<&L> { + self.labels.get_index(id) + } + + /// *O(1)* + #[inline] + fn find_index(&self, item: &L) -> Option { + match self.labels.get_full(item) { + Some((i, _)) => Some(i), + None => None, + } + } + + /// *O(1)* + #[inline] + fn contains(&self, item: &L) -> bool { + self.labels.contains(item) + } + + #[inline] + fn items<'a>(&'a self) -> Iter<'a, &L> { + Iter::new(Box::new(self.labels.iter())) + } + + #[inline] + fn items_vec(self) -> Vec { + self.labels.into_iter().collect() + } + + /// *O(1)* + #[inline] + fn len(&self) -> usize { + self.labels.len() + } +} + +impl MutMapTrait for SetMap { + /// *O(1)* + #[inline] + fn add_item(&mut self, item: L) -> usize { + if self.labels.contains(&item) { + self.labels.get_full(&item).unwrap().0 + } else { + self.labels.insert(item); + + self.len() - 1 + } + } + + /// *O(1)* + #[inline] + fn pop_item(&mut self) -> Option { + self.labels.pop() + } +} + +impl Hash for SetMap { + fn hash(&self, state: &mut H) { + for l in self.items() { + l.hash(state); + } + } +} + +impl FromIterator for SetMap { + fn from_iter>(iter: T) -> Self { + let mut map = SetMap::new(); + + for i in iter { + map.add_item(i); + } + + map + } +} + +impl From> for SetMap { + fn from(vec: Vec) -> Self { + SetMap::from_vec(vec) + } +} + +impl<'a, L: Hash + Eq + Clone> From<&'a Vec> for SetMap { + fn from(vec: &'a Vec) -> Self { + SetMap::from_vec(vec.clone()) + } +} + +impl From> for SetMap { + fn from(vec_map: VecMap) -> Self { + let data = vec_map.items_vec(); + + SetMap::from_vec(data) + } +} + +impl<'a, L: Hash + Eq + Clone> From<&'a VecMap> for SetMap { + fn from(vec_map: &'a VecMap) -> Self { + let data = vec_map.clone().items_vec(); + + SetMap::from_vec(data) + } +} + +#[macro_export] +macro_rules! setmap { + ( $( $x:expr ),* ) => { + { + let mut temp_map = SetMap::new(); + $( + temp_map.add_item($x); + )* + temp_map + } + }; +} diff --git a/src/map/vec_map.rs b/src/map/vec_map.rs index 305d9fc5..059ff846 100644 --- a/src/map/vec_map.rs +++ b/src/map/vec_map.rs @@ -1,186 +1,186 @@ -/* - * Copyright (c) 2018 UNSW Sydney, Data and Knowledge Group. - * - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you 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. - */ -use std::hash::Hash; -use std::iter::FromIterator; - -use serde; - -use generic::{Iter, MapTrait, MutMapTrait}; -use io::serde::{Deserialize, Serialize}; -use map::SetMap; - -/// Less efficient but more compact. -#[derive(Debug, Hash, PartialEq, Eq, Clone, Serialize, Deserialize)] -pub struct VecMap { - labels: Vec, -} - -impl Serialize for VecMap where L: serde::Serialize {} - -impl Deserialize for VecMap where L: for<'de> serde::Deserialize<'de> {} - -impl VecMap { - pub fn new() -> Self { - VecMap { - labels: Vec::::new(), - } - } - - pub fn with_capacity(capacity: usize) -> Self { - VecMap { - labels: Vec::::with_capacity(capacity), - } - } - - pub fn with_data(labels: Vec) -> Self { - VecMap { labels } - } - - pub fn shrink_to_fit(&mut self) { - self.labels.shrink_to_fit(); - } - - pub fn clear(&mut self) { - self.labels.clear(); - } -} - -impl Default for VecMap { - fn default() -> Self { - VecMap::new() - } -} - -impl MapTrait for VecMap { - /// *O(1)* - #[inline] - fn get_item(&self, id: usize) -> Option<&L> { - self.labels.get(id) - } - - /// *O(n)* - #[inline] - fn find_index(&self, item: &L) -> Option { - for (i, elem) in self.labels.iter().enumerate() { - if elem == item { - return Some(i); - } - } - - None - } - - /// *O(n)* - #[inline] - fn contains(&self, item: &L) -> bool { - self.find_index(item).is_some() - } - - #[inline] - fn items(&self) -> Iter<&L> { - Iter::new(Box::new(self.labels.iter())) - } - - #[inline] - fn items_vec(self) -> Vec { - self.labels - } - - /// *O(1)* - #[inline] - fn len(&self) -> usize { - self.labels.len() - } -} - -impl MutMapTrait for VecMap { - /// *O(n)* - #[inline] - fn add_item(&mut self, item: L) -> usize { - match self.find_index(&item) { - Some(i) => i, - None => { - self.labels.push(item); - - self.len() - 1 - } - } - } - - /// *O(1)* - #[inline] - fn pop_item(&mut self) -> Option { - self.labels.pop() - } -} - -impl FromIterator for VecMap { - fn from_iter>(iter: T) -> Self { - let mut map = VecMap::new(); - - for i in iter { - map.add_item(i); - } - - map - } -} - -impl From> for VecMap { - fn from(vec: Vec) -> Self { - VecMap::with_data(vec) - } -} - -impl<'a, L: Eq + Clone> From<&'a Vec> for VecMap { - fn from(vec: &'a Vec) -> Self { - VecMap::with_data(vec.clone()) - } -} - -impl From> for VecMap { - fn from(set_map: SetMap) -> Self { - let data = set_map.items_vec(); - - VecMap::with_data(data) - } -} - -impl<'a, L: Hash + Eq + Clone> From<&'a SetMap> for VecMap { - fn from(set_map: &'a SetMap) -> Self { - let data = set_map.clone().items_vec(); - - VecMap::with_data(data) - } -} - -#[macro_export] -macro_rules! vecmap { - ( $( $x:expr ),* ) => { - { - let mut temp_map = VecMap::new(); - $( - temp_map.add_item($x); - )* - temp_map - } - }; -} +/* + * Copyright (c) 2018 UNSW Sydney, Data and Knowledge Group. + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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. + */ +use std::hash::Hash; +use std::iter::FromIterator; + +use serde; + +use crate::generic::{Iter, MapTrait, MutMapTrait}; +use crate::io::{Deserialize, Serialize}; +use crate::map::SetMap; + +/// Less efficient but more compact. +#[derive(Debug, Hash, PartialEq, Eq, Clone, Serialize, Deserialize)] +pub struct VecMap { + labels: Vec, +} + +impl Serialize for VecMap where L: serde::Serialize {} + +impl Deserialize for VecMap where L: for<'de> serde::Deserialize<'de> {} + +impl VecMap { + pub fn new() -> Self { + VecMap { + labels: Vec::::new(), + } + } + + pub fn with_capacity(capacity: usize) -> Self { + VecMap { + labels: Vec::::with_capacity(capacity), + } + } + + pub fn from_vec(labels: Vec) -> Self { + VecMap { labels } + } + + pub fn shrink_to_fit(&mut self) { + self.labels.shrink_to_fit(); + } + + pub fn clear(&mut self) { + self.labels.clear(); + } +} + +impl Default for VecMap { + fn default() -> Self { + VecMap::new() + } +} + +impl MapTrait for VecMap { + /// *O(1)* + #[inline] + fn get_item(&self, id: usize) -> Option<&L> { + self.labels.get(id) + } + + /// *O(n)* + #[inline] + fn find_index(&self, item: &L) -> Option { + for (i, elem) in self.labels.iter().enumerate() { + if elem == item { + return Some(i); + } + } + + None + } + + /// *O(n)* + #[inline] + fn contains(&self, item: &L) -> bool { + self.find_index(item).is_some() + } + + #[inline] + fn items(&self) -> Iter<&L> { + Iter::new(Box::new(self.labels.iter())) + } + + #[inline] + fn items_vec(self) -> Vec { + self.labels + } + + /// *O(1)* + #[inline] + fn len(&self) -> usize { + self.labels.len() + } +} + +impl MutMapTrait for VecMap { + /// *O(n)* + #[inline] + fn add_item(&mut self, item: L) -> usize { + match self.find_index(&item) { + Some(i) => i, + None => { + self.labels.push(item); + + self.len() - 1 + } + } + } + + /// *O(1)* + #[inline] + fn pop_item(&mut self) -> Option { + self.labels.pop() + } +} + +impl FromIterator for VecMap { + fn from_iter>(iter: T) -> Self { + let mut map = VecMap::new(); + + for i in iter { + map.add_item(i); + } + + map + } +} + +impl From> for VecMap { + fn from(vec: Vec) -> Self { + VecMap::from_vec(vec) + } +} + +impl<'a, L: Eq + Clone> From<&'a Vec> for VecMap { + fn from(vec: &'a Vec) -> Self { + VecMap::from_vec(vec.clone()) + } +} + +impl From> for VecMap { + fn from(set_map: SetMap) -> Self { + let data = set_map.items_vec(); + + VecMap::from_vec(data) + } +} + +impl<'a, L: Hash + Eq + Clone> From<&'a SetMap> for VecMap { + fn from(set_map: &'a SetMap) -> Self { + let data = set_map.clone().items_vec(); + + VecMap::from_vec(data) + } +} + +#[macro_export] +macro_rules! vecmap { + ( $( $x:expr ),* ) => { + { + let mut temp_map = VecMap::new(); + $( + temp_map.add_item($x); + )* + temp_map + } + }; +} diff --git a/src/prelude/mod.rs b/src/prelude/mod.rs index b22a0d37..229130d8 100644 --- a/src/prelude/mod.rs +++ b/src/prelude/mod.rs @@ -1,31 +1,31 @@ -/* - * Copyright (c) 2018 UNSW Sydney, Data and Knowledge Group. - * - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you 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. - */ -//! Commonly used items and traits. -//! # Example -//! ``` -//! use rust_graph::prelude::*; -//! ``` - -pub use generic::{ - DefaultId, DiGraphTrait, Directed, EdgeTrait, GeneralGraph, GraphLabelTrait, GraphTrait, - GraphType, IdType, Iter, MapTrait, MutEdgeType, MutGraphLabelTrait, MutGraphTrait, MutMapTrait, - MutNodeTrait, NodeTrait, UnGraphTrait, Undirected, Void, -}; +/* + * Copyright (c) 2018 UNSW Sydney, Data and Knowledge Group. + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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. + */ +//! Commonly used items and traits. +//! # Example +//! ``` +//! use rust_graph::prelude::*; +//! ``` + +pub use crate::generic::{ + DefaultId, DiGraphTrait, Directed, EdgeTrait, GeneralGraph, GraphLabelTrait, GraphTrait, + GraphType, IdType, Iter, MapTrait, MutEdgeType, MutGraphLabelTrait, MutGraphTrait, MutMapTrait, + MutNodeTrait, NodeTrait, UnGraphTrait, Undirected, Void, +}; diff --git a/src/property/cached_property.rs b/src/property/cached_property.rs new file mode 100644 index 00000000..819927f5 --- /dev/null +++ b/src/property/cached_property.rs @@ -0,0 +1,447 @@ +/* + * Copyright (c) 2018 UNSW Sydney, Data and Knowledge Group. + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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. + */ + +use std::mem::swap; + +use hashbrown::HashMap; + +use serde_cbor::from_slice; +use serde_json::to_value; +use serde_json::Value as JsonValue; + +use crate::generic::{DefaultId, IdType, Iter}; +use crate::property::{PropertyError, PropertyGraph}; + +#[derive(Debug, PartialEq, Clone, Serialize, Deserialize)] +pub struct CachedProperty { + node_property: HashMap, + edge_property: HashMap<(Id, Id), JsonValue>, + is_directed: bool, +} + +impl CachedProperty { + pub fn new(is_directed: bool) -> Self { + CachedProperty { + node_property: HashMap::new(), + edge_property: HashMap::new(), + is_directed, + } + } + + pub fn with_capacity(num_of_nodes: usize, num_of_edges: usize, is_directed: bool) -> Self { + CachedProperty { + node_property: HashMap::with_capacity(num_of_nodes), + edge_property: HashMap::with_capacity(num_of_edges), + is_directed, + } + } + + pub fn with_data< + N: IntoIterator, + E: IntoIterator, + >( + node_property: N, + edge_property: E, + is_directed: bool, + ) -> Self { + CachedProperty { + node_property: node_property.into_iter().collect(), + edge_property: edge_property.into_iter().collect(), + is_directed, + } + } + + pub fn shrink_to_fit(&mut self) { + self.node_property.shrink_to_fit(); + self.edge_property.shrink_to_fit(); + } + + #[inline(always)] + pub fn is_directed(&self) -> bool { + self.is_directed + } + + #[inline(always)] + fn swap_edge(&self, a: &mut Id, b: &mut Id) { + if !self.is_directed && a > b { + swap(a, b) + } + } +} + +impl PropertyGraph for CachedProperty { + #[inline] + fn get_node_property( + &self, + id: Id, + names: Vec, + ) -> Result, PropertyError> { + match self.node_property.get(&id) { + Some(value) => { + let mut result = HashMap::::new(); + for name in names { + if value.get(&name).is_some() { + result.insert(name.clone(), value[&name].clone()); + } + } + Ok(Some(to_value(result)?)) + } + None => Ok(None), + } + } + + #[inline] + fn get_edge_property( + &self, + mut src: Id, + mut dst: Id, + names: Vec, + ) -> Result, PropertyError> { + if !self.is_directed { + self.swap_edge(&mut src, &mut dst); + } + + match self.edge_property.get(&(src, dst)) { + Some(value) => { + let mut result = HashMap::::new(); + for name in names { + if value.get(&name).is_some() { + result.insert(name.clone(), value[&name].clone()); + } + } + Ok(Some(to_value(result)?)) + } + None => Ok(None), + } + } + + #[inline] + fn get_node_property_all(&self, id: Id) -> Result, PropertyError> { + match self.node_property.get(&id) { + Some(value) => Ok(Some(value.clone())), + None => Ok(None), + } + } + + #[inline] + fn get_edge_property_all( + &self, + mut src: Id, + mut dst: Id, + ) -> Result, PropertyError> { + if !self.is_directed { + self.swap_edge(&mut src, &mut dst); + } + + match self.edge_property.get(&(src, dst)) { + Some(value) => Ok(Some(value.clone())), + None => Ok(None), + } + } + fn insert_node_property( + &mut self, + id: Id, + prop: JsonValue, + ) -> Result, PropertyError> { + let value = self.node_property.insert(id, prop); + Ok(value) + } + + fn insert_edge_property( + &mut self, + mut src: Id, + mut dst: Id, + prop: JsonValue, + ) -> Result, PropertyError> { + if !self.is_directed { + self.swap_edge(&mut src, &mut dst); + } + + let value = self.edge_property.insert((src, dst), prop); + Ok(value) + } + + fn extend_node_property>( + &mut self, + props: I, + ) -> Result<(), PropertyError> { + Ok(self.node_property.extend(props)) + } + + fn extend_edge_property>( + &mut self, + props: I, + ) -> Result<(), PropertyError> { + let is_directed = self.is_directed; + let props = props.into_iter().map(|x| { + let (mut src, mut dst) = x.0; + let prop = x.1; + + if is_directed && src > dst { + swap(&mut src, &mut dst); + } + ((src, dst), prop) + }); + + Ok(self.edge_property.extend(props)) + } + + fn insert_node_raw( + &mut self, + id: Id, + prop: Vec, + ) -> Result, PropertyError> { + let value_parsed: JsonValue = from_slice(&prop)?; + self.insert_node_property(id, value_parsed) + } + + fn insert_edge_raw( + &mut self, + src: Id, + dst: Id, + prop: Vec, + ) -> Result, PropertyError> { + let value_parsed: JsonValue = from_slice(&prop)?; + self.insert_edge_property(src, dst, value_parsed) + } + + fn extend_node_raw)>>( + &mut self, + props: I, + ) -> Result<(), PropertyError> { + let props = props + .into_iter() + .map(|x| (x.0, from_slice(&(x.1)).unwrap())); + self.extend_node_property(props) + } + + fn extend_edge_raw)>>( + &mut self, + props: I, + ) -> Result<(), PropertyError> { + let props = props.into_iter().map(|x| (x.0, from_slice(&x.1).unwrap())); + self.extend_edge_property(props) + } + + fn scan_node_property_all(&self) -> Iter> { + Iter::new(Box::new( + self.node_property + .iter() + .map(|(id, value)| Ok((*id, value.clone()))), + )) + } + + fn scan_edge_property_all(&self) -> Iter> { + Iter::new(Box::new( + self.edge_property + .iter() + .map(|(id, value)| Ok((*id, value.clone()))), + )) + } +} + +#[cfg(test)] +mod test { + use super::*; + use serde_cbor::to_vec; + use serde_json::json; + + #[test] + fn test_undirected() { + let mut node_property = HashMap::new(); + let mut edge_property = HashMap::new(); + + node_property.insert( + 0u32, + json!({ + "name":"John", + "age":12, + "is_member":true, + "scores":json!([9,8,10]), + }), + ); + + node_property.insert( + 1, + json!({ + "name":"Marry", + "age":13, + "is_member":false, + "scores":json!([10,10,9]), + }), + ); + + edge_property.insert( + (0, 1), + json!({ + "friend_since":"2018-11-15", + }), + ); + + let graph = CachedProperty::with_data(node_property, edge_property, false); + + assert_eq!( + graph.get_node_property(0, vec!["age".to_owned()]).unwrap(), + Some(json!({"age":12})) + ); + assert_eq!( + graph + .get_node_property(0, vec!["age".to_owned(), "name".to_owned()]) + .unwrap(), + Some(json!({"age":12,"name":"John"})) + ); + assert_eq!( + graph + .get_node_property(1, vec!["is_member".to_owned()]) + .unwrap(), + Some(json!({"is_member":false})) + ); + assert_eq!( + graph + .get_node_property(1, vec!["is_member".to_owned(), "scores".to_owned()]) + .unwrap(), + Some(json!({"is_member":false,"scores":[10,10,9]})) + ); + assert_eq!( + graph.get_node_property(2, vec!["age".to_owned()]).unwrap(), + None + ); + assert_eq!( + graph + .get_node_property(0, vec!["age".to_owned(), "gender".to_owned()]) + .unwrap(), + Some(json!({ + "age":12 + })) + ); + assert_eq!( + graph.get_node_property_all(0).unwrap(), + Some(json!({ + "name":"John", + "age":12, + "is_member":true, + "scores":[9,8,10], + })) + ); + + let edge_property = graph + .get_edge_property(0, 1, vec!["friend_since".to_owned()]) + .unwrap() + .unwrap(); + assert!(edge_property["friend_since"] == "2018-11-15"); + assert_eq!(edge_property.as_object().unwrap().len(), 1); + } + + #[test] + fn test_directed() { + let mut node_property = HashMap::new(); + let mut edge_property = HashMap::new(); + + node_property.insert(0u32, json!({})); + node_property.insert(1, json!({})); + edge_property.insert((0, 1), json!({})); + let graph = CachedProperty::with_data(node_property, edge_property, true); + + assert_eq!(graph.get_edge_property_all(1, 0).unwrap(), None); + } + + #[test] + fn test_insert_raw_node() { + let mut node_property = HashMap::new(); + let mut edge_property = HashMap::new(); + + node_property.insert(0u32, json!({})); + node_property.insert(1, json!({})); + edge_property.insert((0, 1), json!({})); + + let mut graph = CachedProperty::with_data(node_property, edge_property, true); + + let new_prop = json!({"name":"jack"}); + let raw_prop = to_vec(&new_prop).unwrap(); + + graph.insert_node_raw(0u32, raw_prop).unwrap(); + let node_property = graph.get_node_property_all(0u32).unwrap(); + + assert_eq!(Some(json!({"name":"jack"})), node_property); + } + + #[test] + fn test_insert_raw_edge() { + let mut node_property = HashMap::new(); + let mut edge_property = HashMap::new(); + + node_property.insert(0u32, json!({})); + node_property.insert(1, json!({})); + edge_property.insert((0, 1), json!({})); + + let mut graph = CachedProperty::with_data(node_property, edge_property, true); + + let new_prop = json!({"length":"5"}); + let raw_prop = to_vec(&new_prop).unwrap(); + + graph.insert_edge_raw(0u32, 1u32, raw_prop).unwrap(); + let edge_property = graph.get_edge_property_all(0u32, 1u32).unwrap(); + + assert_eq!(Some(json!({"length":"5"})), edge_property); + } + + #[test] + fn test_extend_raw_node() { + let mut node_property = HashMap::new(); + let mut edge_property = HashMap::new(); + + node_property.insert(0u32, json!({})); + node_property.insert(1, json!({})); + edge_property.insert((0, 1), json!({})); + + let mut graph = CachedProperty::with_data(node_property, edge_property, true); + + let new_prop = json!({"name":"jack"}); + let raw_prop = to_vec(&new_prop).unwrap(); + + let raw_properties = vec![(0u32, raw_prop)].into_iter(); + graph.extend_node_raw(raw_properties).unwrap(); + let node_property = graph.get_node_property_all(0u32).unwrap(); + + assert_eq!(Some(json!({"name":"jack"})), node_property); + } + + #[test] + fn test_extend_raw_edge() { + let mut node_property = HashMap::new(); + let mut edge_property = HashMap::new(); + + node_property.insert(0u32, json!({})); + node_property.insert(1, json!({})); + edge_property.insert((0, 1), json!({})); + + let mut graph = CachedProperty::with_data(node_property, edge_property, true); + + let new_prop = json!({"length":"15"}); + let raw_prop = to_vec(&new_prop).unwrap(); + + let raw_properties = vec![((0u32, 1u32), raw_prop)].into_iter(); + graph.extend_edge_raw(raw_properties).unwrap(); + let edge_property = graph.get_edge_property_all(0u32, 1u32).unwrap(); + + assert_eq!(Some(json!({"length":"15"})), edge_property); + } +} diff --git a/src/property/fake_property.rs b/src/property/fake_property.rs new file mode 100644 index 00000000..2840d2ef --- /dev/null +++ b/src/property/fake_property.rs @@ -0,0 +1,143 @@ +/* + * Copyright (c) 2018 UNSW Sydney, Data and Knowledge Group. + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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. + */ + +use std::path::Path; + +use serde::de::DeserializeOwned; +use serde::Serialize; +use serde_json::Value as JsonValue; + +use crate::generic::{IdType, Iter}; +use crate::property::{PropertyError, PropertyGraph}; + +#[derive(Debug, PartialEq, Eq, Clone, Serialize, Deserialize)] +pub struct FakeProperty; + +impl FakeProperty { + pub fn new>(_node_path: P, _edge_path: P, _is_directed: bool) -> Self { + FakeProperty + } +} + +impl PropertyGraph for FakeProperty { + #[inline] + fn get_node_property( + &self, + _id: Id, + _names: Vec, + ) -> Result, PropertyError> { + Ok(None) + } + + #[inline] + fn get_edge_property( + &self, + mut _src: Id, + mut _dst: Id, + _names: Vec, + ) -> Result, PropertyError> { + Ok(None) + } + + #[inline] + fn get_node_property_all(&self, _id: Id) -> Result, PropertyError> { + Ok(None) + } + + #[inline] + fn get_edge_property_all( + &self, + mut _src: Id, + mut _dst: Id, + ) -> Result, PropertyError> { + Ok(None) + } + + fn insert_node_property( + &mut self, + _id: Id, + _prop: JsonValue, + ) -> Result, PropertyError> { + Ok(None) + } + + fn insert_edge_property( + &mut self, + _src: Id, + _dst: Id, + _prop: JsonValue, + ) -> Result, PropertyError> { + Ok(None) + } + + fn extend_node_property>( + &mut self, + _props: I, + ) -> Result<(), PropertyError> { + Ok(()) + } + + fn extend_edge_property>( + &mut self, + _props: I, + ) -> Result<(), PropertyError> { + Ok(()) + } + + fn insert_node_raw( + &mut self, + _id: Id, + _prop: Vec, + ) -> Result, PropertyError> { + Ok(None) + } + + fn insert_edge_raw( + &mut self, + mut _src: Id, + mut _dst: Id, + _prop: Vec, + ) -> Result, PropertyError> { + Ok(None) + } + + fn extend_node_raw)>>( + &mut self, + _props: I, + ) -> Result<(), PropertyError> { + Ok(()) + } + + fn extend_edge_raw)>>( + &mut self, + _props: I, + ) -> Result<(), PropertyError> { + Ok(()) + } + + fn scan_node_property_all(&self) -> Iter> { + Iter::empty() + } + + fn scan_edge_property_all(&self) -> Iter> { + Iter::empty() + } +} diff --git a/src/property/mod.rs b/src/property/mod.rs new file mode 100644 index 00000000..eeebd6b4 --- /dev/null +++ b/src/property/mod.rs @@ -0,0 +1,230 @@ +/* + * Copyright (c) 2018 UNSW Sydney, Data and Knowledge Group. + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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. + */ +pub mod cached_property; +pub mod fake_property; +pub mod rocks_property; +pub mod tikv_property; +//pub mod sled_property; + +pub use crate::property::cached_property::CachedProperty; +pub use crate::property::fake_property::FakeProperty; +pub use crate::property::rocks_property::RocksProperty; +//pub use property::sled_property::SledProperty; + +use crate::generic::IdType; +pub use crate::generic::Iter; +use serde::de::DeserializeOwned; +use serde::Serialize; +use serde_json::Value as JsonValue; +use std::hash::Hash; + +//use std::path::{Path, PathBuf}; + + +pub trait ExtendTikvEdgeTrait< + Id: IdType + Serialize + DeserializeOwned, + EL: Hash + Eq + Serialize + DeserializeOwned, +>: PropertyGraph +{ + fn insert_labeled_edge_property( + &mut self, + src: Id, + dst: Id, + label: EL, + direction: bool, + prop: JsonValue, + ) -> Result, PropertyError>; + + fn get_edge_property_all_with_label( + &mut self, + src: Id, + dst: Id, + label:EL, + direction:bool, + //names: Vec, + ) -> Result, PropertyError>; + + fn insert_labeled_edge_raw( + &mut self, + src: Id, + dst: Id, + label: EL, + direction: bool, + prop: Vec, + ) -> Result, PropertyError>; +} + +pub trait ExtendTikvNodeTrait< + Id: IdType + Serialize + DeserializeOwned, + EL: Hash + Eq + Serialize + DeserializeOwned, +>: PropertyGraph +{ + fn get_node_property_all_with_label( + &mut self, + id: Id, + label: EL, + //names: Vec, + ) -> Result, PropertyError>; + + fn insert_labeled_node_property( + &mut self, + id: Id, + label: EL, + prop: JsonValue, + ) -> Result, PropertyError>; + + fn insert_labeled_node_raw( + &mut self, + id: Id, + label: EL, + prop: Vec, + ) -> Result, PropertyError>; +} + +pub trait PropertyGraph { + fn get_node_property( + &self, + id: Id, + names: Vec, + ) -> Result, PropertyError>; + fn get_edge_property( + &self, + src: Id, + dst: Id, + names: Vec, + ) -> Result, PropertyError>; + fn get_node_property_all(&self, id: Id) -> Result, PropertyError>; + fn get_edge_property_all(&self, src: Id, dst: Id) -> Result, PropertyError>; + + fn insert_node_property( + &mut self, + id: Id, + prop: JsonValue, + ) -> Result, PropertyError>; + fn insert_edge_property( + &mut self, + src: Id, + dst: Id, + prop: JsonValue, + ) -> Result, PropertyError>; + + fn extend_node_property>( + &mut self, + props: I, + ) -> Result<(), PropertyError>; + fn extend_edge_property>( + &mut self, + props: I, + ) -> Result<(), PropertyError>; + + fn insert_node_raw( + &mut self, + id: Id, + prop: Vec, + ) -> Result, PropertyError>; + fn insert_edge_raw( + &mut self, + src: Id, + dst: Id, + prop: Vec, + ) -> Result, PropertyError>; + + fn extend_node_raw)>>( + &mut self, + props: I, + ) -> Result<(), PropertyError>; + fn extend_edge_raw)>>( + &mut self, + props: I, + ) -> Result<(), PropertyError>; + + fn scan_node_property_all(&self) -> Iter>; + + fn scan_edge_property_all(&self) -> Iter>; +} + +#[derive(Debug)] +pub enum PropertyError { + //SledError(sled::Error<()>), + ModifyReadOnlyError, + RocksError(rocksdb::Error), + TiKVError(tikv_client::Error), + BincodeError(std::boxed::Box), + JsonError(serde_json::Error), + CborError(serde_cbor::error::Error), + DBNotFoundError, + LruZeroCapacity, + + JsonObjectFieldError, + BooleanExpressionError, + StringExpressionError, + NumberExpressionError, + EdgeNotFoundError, + NodeNotFoundError, + UnknownError, + CrossComparisonError, + + NoLabelInMapError, +} + +//impl From> for PropertyError { +// fn from(error: sled::Error<()>) -> Self { +// PropertyError::SledError(error) +// } +//} + +impl From for PropertyError { + fn from(error: rocksdb::Error) -> Self { + PropertyError::RocksError(error) + } +} + +impl From> for PropertyError { + fn from(error: std::boxed::Box) -> Self { + PropertyError::BincodeError(error) + } +} + +impl From for PropertyError { + fn from(error: serde_json::Error) -> Self { + PropertyError::JsonError(error) + } +} + +impl From for PropertyError { + fn from(error: serde_cbor::error::Error) -> Self { + PropertyError::CborError(error) + } +} + +impl From for PropertyError { + fn from(error: tikv_client::Error) -> Self { + PropertyError::TiKVError(error) + } +} + +impl From<()> for PropertyError { + fn from(_error: ()) -> Self { + PropertyError::UnknownError + } +} + + diff --git a/src/property/rocks_property.rs b/src/property/rocks_property.rs new file mode 100644 index 00000000..2f2d1909 --- /dev/null +++ b/src/property/rocks_property.rs @@ -0,0 +1,684 @@ +/* + * Copyright (c) 2018 UNSW Sydney, Data and Knowledge Group. + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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. + */ + +use std::collections::BTreeMap; +use std::mem::swap; +use std::path::Path; + +use bincode; +use rocksdb::DB as Tree; +use rocksdb::{IteratorMode, Options, WriteBatch}; +use serde::de::DeserializeOwned; +use serde::Serialize; +use serde_cbor::{from_slice, to_vec}; +use serde_json::to_value; +use serde_json::Value as JsonValue; + +use crate::generic::{IdType, Iter}; +use crate::property::{PropertyError, PropertyGraph}; + +pub struct RocksProperty { + node_property: Tree, + edge_property: Tree, + is_directed: bool, + read_only: bool, +// node_label: Vec, +// edge_label: Vev, +} + +impl RocksProperty { + pub fn new>( + node_path: P, + edge_path: P, + is_directed: bool, + ) -> Result { + Tree::destroy(&Options::default(), &node_path)?; + Tree::destroy(&Options::default(), &edge_path)?; + + let mut opts = Options::default(); + opts.create_if_missing(true); + + let node_tree = Tree::open(&opts, node_path)?; + let edge_tree = Tree::open(&opts, edge_path)?; + +// let node_label = Vec::new();//12.23 +// let edge_label = Vec::new(); + + Ok(RocksProperty { + node_property: node_tree, + edge_property: edge_tree, + is_directed, + read_only: false, +// node_label, +// edge_label, + }) + } + + pub fn open>( + node_path: P, + edge_path: P, + is_directed: bool, + read_only: bool, + ) -> Result { + if !(node_path.as_ref().exists() || edge_path.as_ref().exists()) { + Err(PropertyError::DBNotFoundError) + } else { + let opts = Options::default(); + // opts.set_allow_os_buffer(false); + // let mut block = BlockBasedOptions::default(); + // block.disable_cache(); + // opts.set_block_based_table_factory(&block); + + let node_tree = Tree::open(&opts, node_path)?; + let edge_tree = Tree::open(&opts, edge_path)?; + +// let node_label = Vec::new();//12.23 +// let edge_label = Vec::new(); + + Ok(RocksProperty { + node_property: node_tree, + edge_property: edge_tree, + is_directed, + read_only, +// node_label, +// edge_label, + }) + } + } + + pub fn with_data>( + node_path: P, + edge_path: P, + node_property: N, + edge_property: E, + is_directed: bool, + ) -> Result + where + N: Iterator, + E: Iterator, + { + let mut prop = Self::new(node_path, edge_path, is_directed)?; + prop.extend_node_property(node_property)?; + prop.extend_edge_property(edge_property)?; + + Ok(prop) + } + + pub fn flush(&self) -> Result<(), PropertyError> { + if self.read_only { + panic!("Trying to modify a read-only db."); + } + + self.node_property.flush()?; + self.edge_property.flush()?; + + Ok(()) + } + + #[inline(always)] + pub fn is_directed(&self) -> bool { + self.is_directed + } + + #[inline(always)] + fn swap_edge(&self, a: &mut Id, b: &mut Id) { + if !self.is_directed && a > b { + swap(a, b) + } + } +} + +impl PropertyGraph for RocksProperty { + #[inline] + fn get_node_property( + &self, + id: Id, + names: Vec, + ) -> Result, PropertyError> { + let id_bytes = bincode::serialize(&id)?; + let _value = self.node_property.get(&id_bytes)?; + match _value { + Some(value_bytes) => { + let value_parsed: JsonValue = from_slice(&value_bytes)?; + let mut result = BTreeMap::::new(); + for name in names { + if value_parsed.get(&name).is_some() { + result.insert(name.clone(), value_parsed[&name].clone()); + } + } + Ok(Some(to_value(result)?)) + } + None => Ok(None), + } + } + + #[inline] + fn get_edge_property( + &self, + mut src: Id, + mut dst: Id, + names: Vec, + ) -> Result, PropertyError> { + if !self.is_directed { + self.swap_edge(&mut src, &mut dst); + } + + let id_bytes = bincode::serialize(&(src, dst))?; + let _value = self.edge_property.get(&id_bytes)?; + match _value { + Some(value_bytes) => { + let value_parsed: JsonValue = from_slice(&value_bytes)?; + let mut result = BTreeMap::::new(); + for name in names { + if value_parsed.get(&name).is_some() { + result.insert(name.clone(), value_parsed[&name].clone()); + } + } + Ok(Some(to_value(result)?)) + } + None => Ok(None), + } + } + + #[inline] + fn get_node_property_all(&self, id: Id) -> Result, PropertyError> { + let id_bytes = bincode::serialize(&id)?; + let _value = self.node_property.get(&id_bytes)?; + match _value { + Some(value_bytes) => { + let value_parsed: JsonValue = from_slice(&value_bytes)?; + Ok(Some(value_parsed.clone())) + } + None => Ok(None), + } + } + + #[inline] + fn get_edge_property_all( + &self, + mut src: Id, + mut dst: Id, + ) -> Result, PropertyError> { + if !self.is_directed { + self.swap_edge(&mut src, &mut dst); + } + + let id_bytes = bincode::serialize(&(src, dst))?; + let _value = self.edge_property.get(&id_bytes)?; + match _value { + Some(value_bytes) => { + let value_parsed: JsonValue = from_slice(&value_bytes)?; + Ok(Some(value_parsed.clone())) + } + None => Ok(None), + } + } + + fn insert_node_property( + &mut self, + id: Id, + prop: JsonValue, + ) -> Result, PropertyError> { + let names_bytes = to_vec(&prop)?; + self.insert_node_raw(id, names_bytes) + } + + fn insert_edge_property( + &mut self, + src: Id, + dst: Id, + prop: JsonValue, + ) -> Result, PropertyError> { + let names_bytes = to_vec(&prop)?; + self.insert_edge_raw(src, dst, names_bytes) + } + + fn extend_node_property>( + &mut self, + props: I, + ) -> Result<(), PropertyError> { + let props = props.into_iter().map(|x| (x.0, to_vec(&x.1).unwrap())); + self.extend_node_raw(props) + } + + fn extend_edge_property>( + &mut self, + props: I, + ) -> Result<(), PropertyError> { + let props = props.into_iter().map(|x| (x.0, to_vec(&x.1).unwrap())); + self.extend_edge_raw(props) + } + + fn insert_node_raw( + &mut self, + id: Id, + prop: Vec, + ) -> Result, PropertyError> { + if self.read_only { + return Err(PropertyError::ModifyReadOnlyError); + } + + let id_bytes = bincode::serialize(&id)?; + let value = self.get_node_property_all(id)?; + self.node_property.put(id_bytes, prop)?; + self.node_property.flush()?; + Ok(value) + } + + fn insert_edge_raw( + &mut self, + mut src: Id, + mut dst: Id, + prop: Vec, + ) -> Result, PropertyError> { + if self.read_only { + return Err(PropertyError::ModifyReadOnlyError); + } + + if !self.is_directed { + self.swap_edge(&mut src, &mut dst); + } + + let id_bytes = bincode::serialize(&(src, dst))?; + let value = self.get_edge_property_all(src, dst)?; + self.edge_property.put(id_bytes, prop)?; + self.edge_property.flush()?; + Ok(value) + } + + fn extend_node_raw)>>( + &mut self, + props: I, + ) -> Result<(), PropertyError> { + if self.read_only { + return Err(PropertyError::ModifyReadOnlyError); + } + + let mut batch = WriteBatch::default(); + for (id, prop) in props { + let id_bytes = bincode::serialize(&id)?; + batch.put(id_bytes, prop)?; + } + + self.node_property.write(batch)?; + self.node_property.flush()?; + Ok(()) + } + + fn extend_edge_raw)>>( + &mut self, + props: I, + ) -> Result<(), PropertyError> { + if self.read_only { + return Err(PropertyError::ModifyReadOnlyError); + } + + let mut batch = WriteBatch::default(); + for (id, prop) in props { + let (mut src, mut dst) = id; + if !self.is_directed { + self.swap_edge(&mut src, &mut dst); + } + + let id_bytes = bincode::serialize(&(src, dst))?; + batch.put(id_bytes, prop)?; + } + + self.edge_property.write(batch)?; + self.edge_property.flush()?; + Ok(()) + } + + fn scan_node_property_all(&self) -> Iter> { + Iter::new(Box::new( + self.node_property + .iterator(IteratorMode::Start) + .map(|(id_bytes, value_bytes)| { + let id: Id = bincode::deserialize(&id_bytes)?; + let value_parsed: JsonValue = from_slice(&value_bytes)?; + + Ok((id, value_parsed)) + }), + )) + } + + fn scan_edge_property_all(&self) -> Iter> { + Iter::new(Box::new( + self.edge_property + .iterator(IteratorMode::Start) + .map(|(id_bytes, value_bytes)| { + let id: (Id, Id) = bincode::deserialize(&id_bytes)?; + let value_parsed: JsonValue = from_slice(&value_bytes)?; + + Ok((id, value_parsed)) + }), + )) + } +} + +#[cfg(test)] +mod test { + extern crate tempdir; + + use super::*; + use serde_json::json; + + #[test] + fn test_insert_raw_node() { + let node = tempdir::TempDir::new("node").unwrap(); + let edge = tempdir::TempDir::new("edge").unwrap(); + + let node_path = node.path(); + let edge_path = edge.path(); + + let mut graph = RocksProperty::new(node_path, edge_path, false).unwrap(); + + let new_prop = json!({"name":"jack"}); + let raw_prop = to_vec(&new_prop).unwrap(); + + graph.insert_node_raw(0u32, raw_prop).unwrap(); + let node_property = graph.get_node_property_all(0u32).unwrap(); + + assert_eq!(Some(json!({"name":"jack"})), node_property); + } + + #[test] + fn test_insert_raw_edge() { + let node = tempdir::TempDir::new("node").unwrap(); + let edge = tempdir::TempDir::new("edge").unwrap(); + + let node_path = node.path(); + let edge_path = edge.path(); + + let mut graph = RocksProperty::new(node_path, edge_path, false).unwrap(); + + let new_prop = json!({"length":"15"}); + let raw_prop = to_vec(&new_prop).unwrap(); + + graph.insert_edge_raw(0u32, 1u32, raw_prop).unwrap(); + let node_property = graph.get_edge_property_all(0u32, 1u32).unwrap(); + + assert_eq!(Some(json!({"length":"15"})), node_property); + } + + #[test] + fn test_insert_property_node() { + let node = tempdir::TempDir::new("node").unwrap(); + let edge = tempdir::TempDir::new("edge").unwrap(); + + let node_path = node.path(); + let edge_path = edge.path(); + + let mut graph = RocksProperty::new(node_path, edge_path, false).unwrap(); + + let new_prop = json!({"name":"jack"}); + + graph.insert_node_property(0u32, new_prop).unwrap(); + let node_property = graph.get_node_property_all(0u32).unwrap(); + + assert_eq!(Some(json!({"name":"jack"})), node_property); + } + + #[test] + fn test_insert_property_edge() { + let node = tempdir::TempDir::new("node").unwrap(); + let edge = tempdir::TempDir::new("edge").unwrap(); + + let node_path = node.path(); + let edge_path = edge.path(); + + let mut graph = RocksProperty::new(node_path, edge_path, false).unwrap(); + + let new_prop = json!({"length":"15"}); + + graph.insert_edge_property(0u32, 1u32, new_prop).unwrap(); + let node_property = graph.get_edge_property_all(0u32, 1u32).unwrap(); + + assert_eq!(Some(json!({"length":"15"})), node_property); + } + + #[test] + fn test_extend_raw_node() { + let node = tempdir::TempDir::new("node").unwrap(); + let edge = tempdir::TempDir::new("edge").unwrap(); + + let node_path = node.path(); + let edge_path = edge.path(); + + let mut graph = RocksProperty::new(node_path, edge_path, false).unwrap(); + + let new_prop = json!({"name":"jack"}); + let raw_prop = to_vec(&new_prop).unwrap(); + let raw_properties = vec![(0u32, raw_prop)].into_iter(); + graph.extend_node_raw(raw_properties).unwrap(); + + let node_property = graph.get_node_property_all(0u32).unwrap(); + + assert_eq!(Some(json!({"name":"jack"})), node_property); + } + + #[test] + fn test_extend_raw_edge() { + let node = tempdir::TempDir::new("node").unwrap(); + let edge = tempdir::TempDir::new("edge").unwrap(); + + let node_path = node.path(); + let edge_path = edge.path(); + + let mut graph = RocksProperty::new(node_path, edge_path, false).unwrap(); + + let new_prop = json!({"length":"15"}); + let raw_prop = to_vec(&new_prop).unwrap(); + let raw_properties = vec![((0u32, 1u32), raw_prop)].into_iter(); + graph.extend_edge_raw(raw_properties).unwrap(); + let edge_property = graph.get_edge_property_all(0u32, 1u32).unwrap(); + + assert_eq!(Some(json!({"length":"15"})), edge_property); + } + + #[test] + fn test_extend_property_node() { + let node = tempdir::TempDir::new("node").unwrap(); + let edge = tempdir::TempDir::new("edge").unwrap(); + + let node_path = node.path(); + let edge_path = edge.path(); + + let mut graph = RocksProperty::new(node_path, edge_path, false).unwrap(); + + let new_prop = json!({"name":"jack"}); + + let properties = vec![(0u32, new_prop)].into_iter(); + graph.extend_node_property(properties).unwrap(); + + let node_property = graph.get_node_property_all(0u32).unwrap(); + + assert_eq!(Some(json!({"name":"jack"})), node_property); + } + + #[test] + fn test_extend_property_edge() { + let node = tempdir::TempDir::new("node").unwrap(); + let edge = tempdir::TempDir::new("edge").unwrap(); + + let node_path = node.path(); + let edge_path = edge.path(); + + let mut graph = RocksProperty::new(node_path, edge_path, false).unwrap(); + + let new_prop = json!({"length":"15"}); + + let properties = vec![((0u32, 1u32), new_prop)].into_iter(); + graph.extend_edge_property(properties).unwrap(); + let edge_property = graph.get_edge_property_all(0u32, 1u32).unwrap(); + + assert_eq!(Some(json!({"length":"15"})), edge_property); + } + + #[test] + fn test_open_existing_db() { + let node = tempdir::TempDir::new("node").unwrap(); + let edge = tempdir::TempDir::new("edge").unwrap(); + + let node_path = node.path(); + let edge_path = edge.path(); + + { + let mut graph0 = RocksProperty::new(node_path, edge_path, false).unwrap(); + + graph0 + .insert_node_property(0u32, json!({"name": "jack"})) + .unwrap(); + + assert_eq!( + graph0.get_node_property_all(0u32).unwrap(), + Some(json!({"name": "jack"})) + ); + } + + let graph1 = RocksProperty::open(node_path, edge_path, false, false).unwrap(); + assert_eq!( + graph1.get_node_property_all(0u32).unwrap(), + Some(json!({"name": "jack"})) + ); + } + + #[test] + fn test_open_writable_db() { + let node = tempdir::TempDir::new("node").unwrap(); + let edge = tempdir::TempDir::new("edge").unwrap(); + + let node_path = node.path(); + let edge_path = edge.path(); + + { + let mut graph0 = RocksProperty::new(node_path, edge_path, false).unwrap(); + + graph0 + .insert_node_property(0u32, json!({"name": "jack"})) + .unwrap(); + + assert_eq!( + graph0.get_node_property_all(0u32).unwrap(), + Some(json!({"name": "jack"})) + ); + } + let mut graph1 = RocksProperty::open(node_path, edge_path, false, false).unwrap(); + graph1 + .insert_node_property(1u32, json!({"name": "tom"})) + .unwrap(); + assert_eq!( + graph1.get_node_property_all(1u32).unwrap(), + Some(json!({"name": "tom"})) + ); + } + + #[test] + fn test_open_readonly_db() { + let node = tempdir::TempDir::new("node").unwrap(); + let edge = tempdir::TempDir::new("edge").unwrap(); + + let node_path = node.path(); + let edge_path = edge.path(); + + { + let mut graph0 = RocksProperty::new(node_path, edge_path, false).unwrap(); + + graph0 + .insert_node_property(0u32, json!({"name": "jack"})) + .unwrap(); + + assert_eq!( + graph0.get_node_property_all(0u32).unwrap(), + Some(json!({"name": "jack"})) + ); + } + + let mut graph1 = RocksProperty::open(node_path, edge_path, false, true).unwrap(); + assert_eq!( + graph1.get_node_property_all(0u32).unwrap(), + Some(json!({"name": "jack"})) + ); + + let err = graph1 + .insert_node_property(1u32, json!({"name": "tom"})) + .is_err(); + assert_eq!(err, true); + } + + #[test] + fn test_scan_node_property() { + let node = tempdir::TempDir::new("node").unwrap(); + let edge = tempdir::TempDir::new("edge").unwrap(); + + let node_path = node.path(); + let edge_path = edge.path(); + + let mut graph0 = RocksProperty::new(node_path, edge_path, false).unwrap(); + + graph0 + .insert_node_property(0u32, json!({"name": "jack"})) + .unwrap(); + + graph0 + .insert_node_property(1u32, json!({"name": "tom"})) + .unwrap(); + + let mut iter = graph0.scan_node_property_all(); + assert_eq!( + (0u32, json!({"name": "jack"})), + iter.next().unwrap().unwrap() + ); + assert_eq!( + (1u32, json!({"name": "tom"})), + iter.next().unwrap().unwrap() + ); + } + + #[test] + fn test_scan_edge_property() { + let node = tempdir::TempDir::new("node").unwrap(); + let edge = tempdir::TempDir::new("edge").unwrap(); + + let node_path = node.path(); + let edge_path = edge.path(); + + let mut graph0 = RocksProperty::new(node_path, edge_path, false).unwrap(); + + graph0 + .insert_edge_property(0u32, 1u32, json!({"length": "5"})) + .unwrap(); + + graph0 + .insert_edge_property(1u32, 2u32, json!({"length": "10"})) + .unwrap(); + + let mut iter = graph0.scan_edge_property_all(); + assert_eq!( + ((0u32, 1u32), json!({"length": "5"})), + iter.next().unwrap().unwrap() + ); + assert_eq!( + ((1u32, 2u32), json!({"length": "10"})), + iter.next().unwrap().unwrap() + ); + } +} diff --git a/src/property/sled_property.rs b/src/property/sled_property.rs new file mode 100644 index 00000000..ae2a3c6a --- /dev/null +++ b/src/property/sled_property.rs @@ -0,0 +1,889 @@ +/* + * Copyright (c) 2018 UNSW Sydney, Data and Knowledge Group. + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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. + */ + +use std::collections::BTreeMap; +use std::mem::swap; +use std::path::Path; + +use bincode; +use serde::de::DeserializeOwned; +use serde::Serialize; +use serde_cbor::{from_slice, to_vec}; +use serde_json::to_value; +use serde_json::Value as JsonValue; +use sled::ConfigBuilder; +use sled::Db as Tree; + +use generic::{IdType, Iter}; +use property::{PropertyError, PropertyGraph}; + +pub struct SledProperty { + node_property: Tree, + edge_property: Tree, + is_directed: bool, +} + +impl SledProperty { + pub fn new>( + node_path: P, + edge_path: P, + is_directed: bool, + ) -> Result { + let node_tree = Tree::start_default(node_path)?; + let edge_tree = Tree::start_default(edge_path)?; + + node_tree.clear()?; + edge_tree.clear()?; + + node_tree.flush()?; + edge_tree.flush()?; + + Ok(SledProperty { + node_property: node_tree, + edge_property: edge_tree, + is_directed, + }) + } + + pub fn open>( + node_path: P, + edge_path: P, + is_directed: bool, + read_only: bool, + ) -> Result { + let node_config = ConfigBuilder::default() + .path(node_path) + .read_only(read_only); + let edge_config = ConfigBuilder::default() + .path(edge_path) + .read_only(read_only); + + let node_config = node_config.build(); + let edge_config = edge_config.build(); + + let node_tree = Tree::start(node_config)?; + let edge_tree = Tree::start(edge_config)?; + + Ok(SledProperty { + node_property: node_tree, + edge_property: edge_tree, + is_directed, + }) + } + + pub fn with_data>( + node_path: P, + edge_path: P, + node_property: N, + edge_property: E, + is_directed: bool, + ) -> Result + where + N: Iterator, + E: Iterator, + { + let node_config = ConfigBuilder::default().path(node_path).build(); + let edge_config = ConfigBuilder::default().path(edge_path).build(); + + let node_tree = Tree::start(node_config.clone())?; + let edge_tree = Tree::start(edge_config.clone())?; + + node_tree.clear()?; + edge_tree.clear()?; + + for (id, names) in node_property { + let id_bytes = bincode::serialize(&id)?; + let names_bytes = to_vec(&names)?; + node_tree.set(id_bytes, names_bytes)?; + } + + for (edge, names) in edge_property { + println!("edges: {:?} {:?}", edge, names); + let id_bytes = bincode::serialize(&edge)?; + let names_bytes = to_vec(&names)?; + edge_tree.set(id_bytes, names_bytes)?; + } + + Ok(SledProperty { + node_property: node_tree, + edge_property: edge_tree, + is_directed, + }) + } + + pub fn flush(&self) -> Result<(), PropertyError> { + self.node_property.flush()?; + self.edge_property.flush()?; + + Ok(()) + } + + #[inline(always)] + pub fn is_directed(&self) -> bool { + self.is_directed + } + + #[inline(always)] + fn swap_edge(&self, a: &mut Id, b: &mut Id) { + if !self.is_directed && a > b { + swap(a, b) + } + } +} + +impl PropertyGraph for SledProperty { + #[inline] + fn get_node_property( + &self, + id: Id, + names: Vec, + ) -> Result, PropertyError> { + let id_bytes = bincode::serialize(&id)?; + let _value = self.node_property.get(&id_bytes)?; + match _value { + Some(value_bytes) => { + let value_parsed: JsonValue = from_slice(&value_bytes)?; + let mut result = BTreeMap::::new(); + for name in names { + if value_parsed.get(&name).is_some() { + result.insert(name.clone(), value_parsed[&name].clone()); + } + } + Ok(Some(to_value(result)?)) + } + None => Ok(None), + } + } + + #[inline] + fn get_edge_property( + &self, + mut src: Id, + mut dst: Id, + names: Vec, + ) -> Result, PropertyError> { + if !self.is_directed { + self.swap_edge(&mut src, &mut dst); + } + + let id_bytes = bincode::serialize(&(src, dst))?; + let _value = self.edge_property.get(&id_bytes)?; + match _value { + Some(value_bytes) => { + let value_parsed: JsonValue = from_slice(&value_bytes)?; + let mut result = BTreeMap::::new(); + for name in names { + if value_parsed.get(&name).is_some() { + result.insert(name.clone(), value_parsed[&name].clone()); + } + } + Ok(Some(to_value(result)?)) + } + None => Ok(None), + } + } + + #[inline] + fn get_node_property_all(&self, id: Id) -> Result, PropertyError> { + let id_bytes = bincode::serialize(&id)?; + let _value = self.node_property.get(&id_bytes)?; + match _value { + Some(value_bytes) => { + let value_parsed: JsonValue = from_slice(&value_bytes)?; + Ok(Some(value_parsed.clone())) + } + None => Ok(None), + } + } + + #[inline] + fn get_edge_property_all( + &self, + mut src: Id, + mut dst: Id, + ) -> Result, PropertyError> { + if !self.is_directed { + self.swap_edge(&mut src, &mut dst); + } + + let id_bytes = bincode::serialize(&(src, dst))?; + let _value = self.edge_property.get(&id_bytes)?; + match _value { + Some(value_bytes) => { + let value_parsed: JsonValue = from_slice(&value_bytes)?; + Ok(Some(value_parsed.clone())) + } + None => Ok(None), + } + } + fn insert_node_property( + &mut self, + id: Id, + prop: JsonValue, + ) -> Result, PropertyError> { + let names_bytes = to_vec(&prop)?; + self.insert_node_raw(id, names_bytes) + } + + fn insert_edge_property( + &mut self, + src: Id, + dst: Id, + prop: JsonValue, + ) -> Result, PropertyError> { + let names_bytes = to_vec(&prop)?; + self.insert_edge_raw(src, dst, names_bytes) + } + + fn extend_node_property>( + &mut self, + props: I, + ) -> Result<(), PropertyError> { + let props = props.into_iter().map(|x| (x.0, to_vec(&x.1).unwrap())); + self.extend_node_raw(props) + } + + fn extend_edge_property>( + &mut self, + props: I, + ) -> Result<(), PropertyError> { + let props = props.into_iter().map(|x| (x.0, to_vec(&x.1).unwrap())); + self.extend_edge_raw(props) + } + fn insert_node_raw( + &mut self, + id: Id, + prop: Vec, + ) -> Result, PropertyError> { + let id_bytes = bincode::serialize(&id)?; + let _value = self.node_property.set(id_bytes, prop)?; + self.node_property.flush()?; + + match _value { + Some(value_bytes) => { + let value_parsed: JsonValue = from_slice(&value_bytes)?; + Ok(Some(value_parsed)) + } + None => Ok(None), + } + } + + fn insert_edge_raw( + &mut self, + mut src: Id, + mut dst: Id, + prop: Vec, + ) -> Result, PropertyError> { + if !self.is_directed { + self.swap_edge(&mut src, &mut dst); + } + + let id_bytes = bincode::serialize(&(src, dst))?; + let _value = self.edge_property.set(id_bytes, prop)?; + self.edge_property.flush()?; + + match _value { + Some(value_bytes) => { + let value_parsed: JsonValue = from_slice(&value_bytes)?; + Ok(Some(value_parsed)) + } + None => Ok(None), + } + } + + fn extend_node_raw)>>( + &mut self, + props: I, + ) -> Result<(), PropertyError> { + for (id, prop) in props { + let id_bytes = bincode::serialize(&id)?; + let _value = self.node_property.set(id_bytes, prop)?; + } + self.node_property.flush()?; + + Ok(()) + } + + fn extend_edge_raw)>>( + &mut self, + props: I, + ) -> Result<(), PropertyError> { + for (id, prop) in props { + let (mut src, mut dst) = id; + if !self.is_directed { + self.swap_edge(&mut src, &mut dst); + } + + let id_bytes = bincode::serialize(&(src, dst))?; + let _value = self.edge_property.set(id_bytes, prop)?; + } + self.edge_property.flush()?; + + Ok(()) + } + + fn scan_node_property_all(&self) -> Iter> { + Iter::new(Box::new(self.node_property.iter().map( + |result| match result { + Ok((id_bytes, value_bytes)) => { + let id: Id = bincode::deserialize(&id_bytes)?; + let value_parsed: JsonValue = from_slice(&value_bytes)?; + + Ok((id, value_parsed)) + } + Err(e) => Err(PropertyError::SledError(e)), + }, + ))) + } + + fn scan_edge_property_all(&self) -> Iter> { + Iter::new(Box::new(self.edge_property.iter().map( + |result| match result { + Ok((id_bytes, value_bytes)) => { + let id: (Id, Id) = bincode::deserialize(&id_bytes)?; + let value_parsed: JsonValue = from_slice(&value_bytes)?; + + Ok((id, value_parsed)) + } + Err(e) => Err(PropertyError::SledError(e)), + }, + ))) + } +} + +#[cfg(test)] +mod test { + extern crate tempdir; + + use super::*; + use serde_json::json; + use std::collections::HashMap; + + #[test] + fn test_undirected() { + let mut node_property = HashMap::new(); + let mut edge_property = HashMap::new(); + + node_property.insert( + 0u32, + json!({ + "name":"John", + "age":12, + "is_member":true, + "scores":[9,8,10], + }), + ); + + node_property.insert( + 1, + json!({ + "name":"Marry", + "age":13, + "is_member":false, + "scores":[10,10,9], + }), + ); + + edge_property.insert( + (0, 1), + json!({ + "friend_since":"2018-11-15", + }), + ); + + let node = tempdir::TempDir::new("node").unwrap(); + let edge = tempdir::TempDir::new("edge").unwrap(); + + let node_path = node.path(); + let edge_path = edge.path(); + + let graph = SledProperty::with_data( + node_path, + edge_path, + node_property.into_iter(), + edge_property.into_iter(), + false, + ) + .unwrap(); + assert_eq!( + graph + .get_node_property(0u32, vec!["age".to_owned()]) + .unwrap(), + Some(json!({"age":12})) + ); + assert_eq!( + graph + .get_node_property(0u32, vec!["age".to_owned(), "name".to_owned()]) + .unwrap(), + Some(json!({"age":12,"name":"John"})) + ); + assert_eq!( + graph + .get_node_property(1u32, vec!["is_member".to_owned()]) + .unwrap(), + Some(json!({"is_member":false})) + ); + assert_eq!( + graph + .get_node_property(1u32, vec!["is_member".to_owned(), "scores".to_owned()]) + .unwrap(), + Some(json!({"is_member":false,"scores":[10,10,9]})) + ); + assert_eq!( + graph + .get_node_property(2u32, vec!["age".to_owned()]) + .unwrap(), + None + ); + assert_eq!( + graph + .get_node_property(0u32, vec!["age".to_owned(), "gender".to_owned()]) + .unwrap(), + Some(json!({ + "age":12 + })) + ); + assert_eq!( + graph.get_node_property_all(0u32).unwrap(), + Some(json!({ + "name":"John", + "age":12, + "is_member":true, + "scores":json!([9,8,10]), + })) + ); + + let edge_property = graph + .get_edge_property(0u32, 1u32, vec!["friend_since".to_owned()]) + .unwrap() + .unwrap(); + assert!(edge_property["friend_since"] == "2018-11-15"); + assert_eq!(edge_property.as_object().unwrap().len(), 1); + } + + #[test] + fn test_directed() { + let mut node_property = HashMap::new(); + let mut edge_property = HashMap::new(); + + node_property.insert(0u32, json!({})); + node_property.insert(1, json!({})); + edge_property.insert((0, 1), json!({})); + + let node = tempdir::TempDir::new("node").unwrap(); + let edge = tempdir::TempDir::new("edge").unwrap(); + + let node_path = node.path(); + let edge_path = edge.path(); + + let graph = SledProperty::with_data( + node_path, + edge_path, + node_property.into_iter(), + edge_property.into_iter(), + false, + ) + .unwrap(); + let edge_property = graph.get_edge_property_all(1u32, 0u32).unwrap(); + assert_eq!(Some(json!({})), edge_property); + } + + #[test] + fn test_insert_raw_node() { + let mut node_property = HashMap::new(); + let mut edge_property = HashMap::new(); + + node_property.insert(0u32, json!({})); + node_property.insert(1, json!({})); + edge_property.insert((0, 1), json!({})); + + let node = tempdir::TempDir::new("node").unwrap(); + let edge = tempdir::TempDir::new("edge").unwrap(); + + let node_path = node.path(); + let edge_path = edge.path(); + + let mut graph = SledProperty::with_data( + node_path, + edge_path, + node_property.into_iter(), + edge_property.into_iter(), + false, + ) + .unwrap(); + + let new_prop = json!({"name":"jack"}); + let raw_prop = to_vec(&new_prop).unwrap(); + + graph.insert_node_raw(0u32, raw_prop).unwrap(); + let node_property = graph.get_node_property_all(0u32).unwrap(); + + assert_eq!(Some(json!({"name":"jack"})), node_property); + } + + #[test] + fn test_insert_raw_edge() { + let mut node_property = HashMap::new(); + let mut edge_property = HashMap::new(); + + node_property.insert(0u32, json!({})); + node_property.insert(1, json!({})); + edge_property.insert((0, 1), json!({})); + + let node = tempdir::TempDir::new("node").unwrap(); + let edge = tempdir::TempDir::new("edge").unwrap(); + + let node_path = node.path(); + let edge_path = edge.path(); + + let mut graph = SledProperty::with_data( + node_path, + edge_path, + node_property.into_iter(), + edge_property.into_iter(), + false, + ) + .unwrap(); + + let new_prop = json!({"length":"5"}); + let raw_prop = to_vec(&new_prop).unwrap(); + + graph.insert_edge_raw(0u32, 1u32, raw_prop).unwrap(); + let edge_property = graph.get_edge_property_all(0u32, 1u32).unwrap(); + + assert_eq!(Some(json!({"length":"5"})), edge_property); + } + + #[test] + fn test_extend_raw_node() { + let mut node_property = HashMap::new(); + let mut edge_property = HashMap::new(); + + node_property.insert(0u32, json!({})); + node_property.insert(1, json!({})); + edge_property.insert((0, 1), json!({})); + + let node = tempdir::TempDir::new("node").unwrap(); + let edge = tempdir::TempDir::new("edge").unwrap(); + + let node_path = node.path(); + let edge_path = edge.path(); + + let mut graph = SledProperty::with_data( + node_path, + edge_path, + node_property.into_iter(), + edge_property.into_iter(), + false, + ) + .unwrap(); + + let new_prop = json!({"name":"jack"}); + let raw_prop = to_vec(&new_prop).unwrap(); + + let raw_properties = vec![(0u32, raw_prop)].into_iter(); + graph.extend_node_raw(raw_properties).unwrap(); + let node_property = graph.get_node_property_all(0u32).unwrap(); + + assert_eq!(Some(json!({"name":"jack"})), node_property); + } + + #[test] + fn test_extend_raw_edge() { + let mut node_property = HashMap::new(); + let mut edge_property = HashMap::new(); + + node_property.insert(0u32, json!({})); + node_property.insert(1, json!({})); + edge_property.insert((0, 1), json!({})); + + let node = tempdir::TempDir::new("node").unwrap(); + let edge = tempdir::TempDir::new("edge").unwrap(); + + let node_path = node.path(); + let edge_path = edge.path(); + + let mut graph = SledProperty::with_data( + node_path, + edge_path, + node_property.into_iter(), + edge_property.into_iter(), + false, + ) + .unwrap(); + + let new_prop = json!({"length":"15"}); + let raw_prop = to_vec(&new_prop).unwrap(); + + let raw_properties = vec![((0u32, 1u32), raw_prop)].into_iter(); + graph.extend_edge_raw(raw_properties).unwrap(); + let edge_property = graph.get_edge_property_all(0u32, 1u32).unwrap(); + + assert_eq!(Some(json!({"length":"15"})), edge_property); + } + + #[test] + fn test_create_new_sled() { + let node = tempdir::TempDir::new("node").unwrap(); + let edge = tempdir::TempDir::new("edge").unwrap(); + + let node_path = node.path(); + let edge_path = edge.path(); + + { + let mut graph0 = SledProperty::new(node_path, edge_path, false).unwrap(); + + graph0 + .insert_node_property(0u32, json!({"name": "jack"})) + .unwrap(); + + assert_eq!( + graph0.get_node_property_all(0u32).unwrap(), + Some(json!({"name": "jack"})) + ); + } + + let graph1 = SledProperty::new(node_path, edge_path, false).unwrap(); + + assert_eq!(graph1.get_node_property_all(0u32).unwrap(), None); + } + + #[test] + fn test_open_writable_sled() { + let node = tempdir::TempDir::new("node").unwrap(); + let edge = tempdir::TempDir::new("edge").unwrap(); + + let node_path = node.path(); + let edge_path = edge.path(); + + { + let mut graph0 = SledProperty::new(node_path, edge_path, false).unwrap(); + + graph0 + .insert_node_property(0u32, json!({"name": "jack"})) + .unwrap(); + + assert_eq!( + graph0.get_node_property_all(0u32).unwrap(), + Some(json!({"name": "jack"})) + ); + } + + let mut graph1 = SledProperty::open(node_path, edge_path, false, false).unwrap(); + assert_eq!( + graph1.get_node_property_all(0u32).unwrap(), + Some(json!({"name": "jack"})) + ); + + graph1 + .insert_node_property(1u32, json!({"name": "tom"})) + .unwrap(); + assert_eq!( + graph1.get_node_property_all(1u32).unwrap(), + Some(json!({"name": "tom"})) + ); + } + + #[test] + fn test_open_readonly_sled() { + let node = tempdir::TempDir::new("node").unwrap(); + let edge = tempdir::TempDir::new("edge").unwrap(); + + let node_path = node.path(); + let edge_path = edge.path(); + + { + let mut graph0 = SledProperty::new(node_path, edge_path, false).unwrap(); + + graph0 + .insert_node_property(0u32, json!({"name": "jack"})) + .unwrap(); + + assert_eq!( + graph0.get_node_property_all(0u32).unwrap(), + Some(json!({"name": "jack"})) + ); + } + + let mut graph1 = SledProperty::open(node_path, edge_path, false, true).unwrap(); + assert_eq!( + graph1.get_node_property_all(0u32).unwrap(), + Some(json!({"name": "jack"})) + ); + + let err = graph1 + .insert_node_property(1u32, json!({"name": "tom"})) + .is_err(); + assert_eq!(err, true); + } + + #[test] + fn test_open_compressed_sled_with_compression() { + let node = tempdir::TempDir::new("node").unwrap(); + let edge = tempdir::TempDir::new("edge").unwrap(); + + let node_path = node.path(); + let edge_path = edge.path(); + + { + let mut graph0 = SledProperty::open(node_path, edge_path, false, false).unwrap(); + + graph0 + .insert_node_property(0u32, json!({"name": "jack"})) + .unwrap(); + + assert_eq!( + graph0.get_node_property_all(0u32).unwrap(), + Some(json!({"name": "jack"})) + ); + } + + let mut graph1 = SledProperty::open(node_path, edge_path, false, false).unwrap(); + assert_eq!( + graph1.get_node_property_all(0u32).unwrap(), + Some(json!({"name": "jack"})) + ); + + graph1 + .insert_node_property(1u32, json!({"name": "tom"})) + .unwrap(); + assert_eq!( + graph1.get_node_property_all(1u32).unwrap(), + Some(json!({"name": "tom"})) + ); + } + + #[test] + #[should_panic] + fn test_open_compressed_sled_without_compression() { + let node = tempdir::TempDir::new("node").unwrap(); + let edge = tempdir::TempDir::new("edge").unwrap(); + + let node_path = node.path(); + let edge_path = edge.path(); + + { + let mut graph0 = SledProperty::open(node_path, edge_path, false, false).unwrap(); + + graph0 + .insert_node_property(0u32, json!({"name": "jack"})) + .unwrap(); + + assert_eq!( + graph0.get_node_property_all(0u32).unwrap(), + Some(json!({"name": "jack"})) + ); + } + + let err = SledProperty::open(node_path, edge_path, false, false).is_err(); + + assert_eq!(err, true); + } + + #[test] + fn test_open_sled_disable_snapshot() { + let node = tempdir::TempDir::new("node").unwrap(); + let edge = tempdir::TempDir::new("edge").unwrap(); + + let node_path = node.path(); + let edge_path = edge.path(); + + { + let mut graph0 = SledProperty::open(node_path, edge_path, false, false).unwrap(); + + graph0 + .insert_node_property(0u32, json!({"name": "jack"})) + .unwrap(); + + assert_eq!( + graph0.get_node_property_all(0u32).unwrap(), + Some(json!({"name": "jack"})) + ); + } + + let graph1 = SledProperty::open(node_path, edge_path, false, false).unwrap(); + + assert_eq!( + graph1.get_node_property_all(0u32).unwrap(), + Some(json!({"name": "jack"})) + ); + } + + #[test] + fn test_scan_node_property() { + let node = tempdir::TempDir::new("node").unwrap(); + let edge = tempdir::TempDir::new("edge").unwrap(); + + let node_path = node.path(); + let edge_path = edge.path(); + + let mut graph0 = SledProperty::new(node_path, edge_path, false).unwrap(); + + graph0 + .insert_node_property(0u32, json!({"name": "jack"})) + .unwrap(); + + graph0 + .insert_node_property(1u32, json!({"name": "tom"})) + .unwrap(); + + let mut iter = graph0.scan_node_property_all(); + assert_eq!( + (0u32, json!({"name": "jack"})), + iter.next().unwrap().unwrap() + ); + assert_eq!( + (1u32, json!({"name": "tom"})), + iter.next().unwrap().unwrap() + ); + } + + #[test] + fn test_scan_edge_property() { + let node = tempdir::TempDir::new("node").unwrap(); + let edge = tempdir::TempDir::new("edge").unwrap(); + + let node_path = node.path(); + let edge_path = edge.path(); + + let mut graph0 = SledProperty::new(node_path, edge_path, false).unwrap(); + + graph0 + .insert_edge_property(0u32, 1u32, json!({"length": "5"})) + .unwrap(); + + graph0 + .insert_edge_property(1u32, 2u32, json!({"length": "10"})) + .unwrap(); + + let mut iter: Iter> = + graph0.scan_edge_property_all(); + + assert_eq!( + ((0u32, 1u32), json!({"length": "5"})), + iter.next().unwrap().unwrap() + ); + assert_eq!( + ((1u32, 2u32), json!({"length": "10"})), + iter.next().unwrap().unwrap() + ); + } +} diff --git a/src/property/tikv_property.rs b/src/property/tikv_property.rs new file mode 100644 index 00000000..b38ccd4f --- /dev/null +++ b/src/property/tikv_property.rs @@ -0,0 +1,1220 @@ +/* + * Copyright (c) 2018 UNSW Sydney, Data and Knowledge Group. + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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. + */ + +use std::collections::BTreeMap; +use std::mem::swap; + +use crate::serde_json::Value as JsonValue; +use serde::de::DeserializeOwned; +use serde::Serialize; +use serde_cbor::{from_slice, to_vec}; +use serde_json::to_value; + +use tikv_client::{raw::Client, ColumnFamily, Config, Error, KvPair, RawClient, ToOwnedRange}; + +use crate::generic::{IdType, Iter}; +use crate::itertools::Itertools; +use crate::property::{ExtendTikvEdgeTrait, ExtendTikvNodeTrait, PropertyError, PropertyGraph}; +use futures::executor::block_on; +use tokio::runtime::Runtime; +//use property::{ExtendTikvEdgeTrait, ExtendTikvNodeTrait}; +use futures::future::Either; +use futures::prelude::*; +use futures::StreamExt; +use std::future::Future; +use std::hash::{Hash, Hasher}; + +use fxhash::FxBuildHasher; +use indexmap::IndexSet; +use std::iter::FromIterator; + +use crate::generic::{MapTrait, MutMapTrait}; + +use crate::generic; +use crate::io::{Deserialize, ReadGraphTo, GraphLoader}; +use crate::map::{SetMap, VecMap}; + +use std::path::{Path, PathBuf}; +use crate::io::ReadGraph; +use crate::io::CSVReader; +use crate::io::tikv::tikv_loader::TikvLoader; + +use std::fmt::{Debug, Display}; + + + +pub struct TikvProperty { + node_client: Client, + edge_client: Client, + rt: Option, + is_directed: bool, + read_only: bool, + label_map: SetMap, +} + +impl TikvProperty { + /// New tikv-client with destroying all kv-pairs first if any + pub fn new( + node_property_config: Config, + edge_property_config: Config, + is_directed: bool, + ) -> Result { + let node_client = + Client::new(node_property_config.clone()).expect("Connect to pd-server error!"); + let edge_client = + Client::new(edge_property_config.clone()).expect("Connect to pd-server error!"); + let node_client_clone = node_client.clone(); + block_on(async move { + node_client_clone + .delete_range("".to_owned()..) + .await + .expect("Delete all node properties failed!"); + }); + + let edge_client_clone = node_client.clone(); + block_on(async { + edge_client_clone + .delete_range("".to_owned()..) + .await + .expect("Delete all edge properties failed!"); + }); + + let rt = Runtime::new().unwrap(); + Ok(TikvProperty { + node_client, + edge_client, + rt: Some(rt), + is_directed, + read_only: false, + label_map: SetMap::new(), + }) + } + + pub fn open( + node_property_config: Config, + edge_property_config: Config, + is_directed: bool, + read_only: bool, + ) -> Result { + let rt = Runtime::new().unwrap(); + let node_client = + Client::new(node_property_config.clone()).expect("Connect to pd-server error!"); + let edge_client = + Client::new(edge_property_config.clone()).expect("Connect to pd-server error!"); + Ok(TikvProperty { + node_client, + edge_client, + rt: Some(rt), + is_directed, + read_only, + label_map: SetMap::new(), + }) + } + + pub fn with_data( + node_property_config: Config, + edge_property_config: Config, + node_property: N, + edge_property: E, + is_directed: bool, + ) -> Result + where + N: Iterator, + E: Iterator, + { + let mut prop = Self::new(node_property_config, edge_property_config, is_directed)?; + prop.extend_node_property(node_property)?; + prop.extend_edge_property(edge_property)?; + + Ok(prop) + } + + #[inline(always)] + fn swap_edge(&self, a: &mut Id, b: &mut Id) { + if !self.is_directed && a > b { + swap(a, b) + } + } + + fn get_property( + &self, + key: Vec, + names: Vec, + is_node_property: bool, + ) -> Result, PropertyError> { + block_on(async { + let client = if is_node_property { + self.node_client.clone() + } else { + self.edge_client.clone() + }; + let _value = client.get(key).await?; + match _value { + Some(value_bytes) => { + let value_parsed: JsonValue = from_slice((&value_bytes).into())?; + let mut result = BTreeMap::::new(); + for name in names { + if value_parsed.get(&name).is_some() { + result.insert(name.clone(), value_parsed[&name].clone()); + } + } + Ok(Some(to_value(result)?)) + } + None => Ok(None), + } + }) + } + + pub fn get_property_all( + &self, + key: Vec, + is_node_property: bool, + ) -> Result, PropertyError> { + block_on(async { + let client = if is_node_property { + self.node_client.clone() + } else { + self.edge_client.clone() + }; + let _value = client.get(key).await?; + match _value { + Some(value_bytes) => { + let value_parsed: JsonValue = from_slice((&value_bytes).into())?; + Ok(Some(value_parsed)) + } + None => Ok(None), + } + }) + } + + #[inline] + pub fn batch_get_node_property_all( + &self, + keys: Vec, + ) -> Result>, PropertyError> { + let ids_bytes = keys + .into_iter() + .map(|x| bincode::serialize(&x).unwrap()) + .collect(); + self.batch_get_node_property(ids_bytes) + } + + #[inline] + pub fn batch_get_edge_property_all( + &self, + keys: Vec<(Id, Id)>, + ) -> Result>, PropertyError> { + let ids_bytes = keys + .into_iter() + .map(|x| { + let (mut src, mut dst) = (x.0, x.1); + self.swap_edge(&mut src, &mut dst); + bincode::serialize(&(src, dst)).unwrap() + }) + .collect(); + self.batch_get_edge_property(ids_bytes) + } + + fn batch_get_node_property( + &self, + keys: Vec>, + ) -> Result>, PropertyError> { + block_on(async { + let client = self.node_client.clone(); + let kv_pairs = client.batch_get(keys).await?; + + if kv_pairs.is_empty() { + Ok(None) + } else { + let mut pairs_parsed = Vec::new(); + for kv_pair in kv_pairs { + let key_parsed = bincode::deserialize(kv_pair.key().into())?; + let value_parsed: JsonValue = from_slice(kv_pair.value().into())?; + pairs_parsed.push((key_parsed, value_parsed)); + } + Ok(Some(pairs_parsed)) + } + }) + } + + pub fn batch_get_edge_property( + &self, + keys: Vec>, + ) -> Result>, PropertyError> { + block_on(async { + let client = self.edge_client.clone(); + let kv_pairs = client.batch_get(keys).await?; + + if kv_pairs.is_empty() { + Ok(None) + } else { + let mut pairs_parsed = Vec::new(); + for kv_pair in kv_pairs { + let key_parsed = bincode::deserialize(kv_pair.key().into())?; + let value_parsed: JsonValue = from_slice(kv_pair.value().into())?; + pairs_parsed.push((key_parsed, value_parsed)); + } + Ok(Some(pairs_parsed)) + } + }) + } +} + +impl + PropertyGraph for TikvProperty +{ + #[inline] + fn get_node_property( + &self, + id: Id, + names: Vec, + ) -> Result, PropertyError> { + let id_bytes = bincode::serialize(&id)?; + self.get_property(id_bytes, names, true) + } + + #[inline] + fn get_edge_property( + &self, + mut src: Id, + mut dst: Id, + names: Vec, + ) -> Result, PropertyError> { + self.swap_edge(&mut src, &mut dst); + + let id_bytes = bincode::serialize(&(src, dst))?; + self.get_property(id_bytes, names, false) + } + + #[inline] + fn get_node_property_all(&self, id: Id) -> Result, PropertyError> { + let id_bytes = bincode::serialize(&id)?; + self.get_property_all(id_bytes, true) + } + + #[inline] + fn get_edge_property_all( + &self, + mut src: Id, + mut dst: Id, + ) -> Result, PropertyError> { + self.swap_edge(&mut src, &mut dst); + let id_bytes = bincode::serialize(&(src, dst))?; + self.get_property_all(id_bytes, false) + } + + fn insert_node_property( + &mut self, + id: Id, + prop: JsonValue, + ) -> Result, PropertyError> { + let names_bytes = to_vec(&prop)?; + self.insert_node_raw(id, names_bytes) + } + + fn insert_edge_property( + &mut self, + src: Id, + dst: Id, + prop: JsonValue, + ) -> Result, PropertyError> { + let names_bytes = to_vec(&prop)?; + self.insert_edge_raw(src, dst, names_bytes) + } + + fn extend_node_property>( + &mut self, + props: I, + ) -> Result<(), PropertyError> { + let props = props.into_iter().map(|x| (x.0, to_vec(&x.1).unwrap())); + self.extend_node_raw(props) + } + + fn extend_edge_property>( + &mut self, + props: I, + ) -> Result<(), PropertyError> { + let props = props.into_iter().map(|x| (x.0, to_vec(&x.1).unwrap())); + self.extend_edge_raw(props) + } + + fn insert_node_raw( + &mut self, + id: Id, + prop: Vec, + ) -> Result, PropertyError> { + if self.read_only { + return Err(PropertyError::ModifyReadOnlyError); + } + + let id_bytes = bincode::serialize(&id)?; + let value = self.get_node_property_all(id)?; + + let client = self.node_client.clone(); + block_on(async { + client + .put(id_bytes, prop) + .await + .expect("Insert node property failed!"); + }); + + Ok(value) + } + + fn insert_edge_raw( + &mut self, + mut src: Id, + mut dst: Id, + prop: Vec, + ) -> Result, PropertyError> { + if self.read_only { + return Err(PropertyError::ModifyReadOnlyError); + } + + self.swap_edge(&mut src, &mut dst); + + let id_bytes = bincode::serialize(&(src, dst))?; + let value = self.get_edge_property_all(src, dst)?; + + let client = self.edge_client.clone(); + block_on(async { + client + .put(id_bytes, prop) + .await + .expect("Insert edge property failed!"); + }); + + Ok(value) + } + + fn extend_node_raw)>>( + &mut self, + props: I, + ) -> Result<(), PropertyError> { + if self.read_only { + return Err(PropertyError::ModifyReadOnlyError); + } + + let properties = props + .into_iter() + .map(|x| (bincode::serialize(&(x.0)).unwrap(), x.1)) + .collect_vec(); + + let client = self.node_client.clone(); + self.rt.as_ref().unwrap().spawn(async move { + client + .batch_put(properties) + .await + .expect("Batch insert node property failed!"); + }); + + Ok(()) + } + + fn extend_edge_raw)>>( + &mut self, + props: I, + ) -> Result<(), PropertyError> { + if self.read_only { + return Err(PropertyError::ModifyReadOnlyError); + } + + let properties = props + .into_iter() + .map(|x| { + let (mut src, mut dst) = x.0; + self.swap_edge(&mut src, &mut dst); + (bincode::serialize(&(src, dst)).unwrap(), x.1) + }) + .collect_vec(); + + let client = self.edge_client.clone(); + self.rt.as_ref().unwrap().spawn(async move { + client + .batch_put(properties) + .await + .expect("Batch insert node property failed!"); + }); + + Ok(()) + } + + fn scan_node_property_all(&self) -> Iter> { + let client = self.node_client.clone(); + block_on(async { + let result: Vec = client.scan("".to_owned().., 2).await.unwrap(); + + Iter::new(Box::new(result.into_iter().map(|pair| { + let (id_bytes, value_bytes) = (pair.key(), pair.value()); + let id: Id = bincode::deserialize(id_bytes.into())?; + let value_parsed: JsonValue = from_slice(value_bytes.into())?; + + Ok((id, value_parsed)) + }))) + }) + } + + fn scan_edge_property_all(&self) -> Iter> { + let client = self.edge_client.clone(); + block_on(async { + let result: Vec = client.scan("".to_owned().., 2).await.unwrap(); + + Iter::new(Box::new(result.into_iter().map(|pair| { + let (id_bytes, value_bytes) = (pair.key(), pair.value()); + let id: (Id, Id) = bincode::deserialize(id_bytes.into())?; + let value_parsed: JsonValue = from_slice(value_bytes.into())?; + + Ok((id, value_parsed)) + }))) + }) + } +} + +impl + ExtendTikvEdgeTrait for TikvProperty +{ + fn insert_labeled_edge_property( + &mut self, + src: Id, + dst: Id, + label: EL, + direction: bool, + prop: JsonValue, + ) -> Result, PropertyError> { + let names_bytes = to_vec(&prop)?; + self.insert_labeled_edge_raw(src, dst, label, direction, names_bytes) + } + + fn insert_labeled_edge_raw( + &mut self, + mut src: Id, + mut dst: Id, + label: EL, + direction: bool, + prop: Vec, + ) -> Result, PropertyError> { + if self.read_only { + return Err(PropertyError::ModifyReadOnlyError); + } + + if direction == true { + self.is_directed = true; + } + + self.swap_edge(&mut src, &mut dst); + + let label_id = self.label_map.add_item(label); + //self.insert_labeled_edge_raw(src, dst, label, direction, prop); + + let id_bytes = bincode::serialize(&(src, direction, label_id, dst))?; + println!("{:#?}", id_bytes); + + let value = self.get_edge_property_all(src, dst)?; + + let client = self.edge_client.clone(); + block_on(async { + client + .put(id_bytes, prop) + .await + .expect("Insert edge property failed!"); + }); + + Ok(value) + } + + fn get_edge_property_all_with_label( + &mut self, + src: Id, + dst: Id, + label: EL, + direction: bool, + ) -> Result, PropertyError> { + let label_id = self.label_map.add_item(label); + let key = bincode::serialize(&(src, direction, label_id, dst))?; + //let id_bytes = bincode::serialize(&id)?; + self.get_property_all(key, true) + } +} + +impl + ExtendTikvNodeTrait for TikvProperty +{ + fn insert_labeled_node_property( + &mut self, + id: Id, + label: EL, + prop: JsonValue, + ) -> Result, PropertyError> { + let names_bytes = to_vec(&prop).unwrap(); + self.insert_labeled_node_raw(id, label, names_bytes) + } + + fn insert_labeled_node_raw( + &mut self, + id: Id, + label: EL, + prop: Vec, + ) -> Result, PropertyError> { + if self.read_only { + return Err(PropertyError::ModifyReadOnlyError); + } + let label_id = self.label_map.add_item(label); + let id_bytes = bincode::serialize(&(id, label_id))?; + let value = self.get_node_property_all(id)?; + + let client = self.node_client.clone(); + block_on(async { + client + .put(id_bytes, prop) + .await + .expect("Insert node property failed!"); + }); + + Ok(value) + } + + fn get_node_property_all_with_label( + &mut self, + id: Id, + label: EL, + ) -> Result, PropertyError> { + let label_id = self.label_map.add_item(label); + let key = bincode::serialize(&(id, label_id))?; + //let id_bytes = bincode::serialize(&id)?; + self.get_property_all(key, true) + } +} + +#[cfg(test)] +mod test { + extern crate tikv_client; + + use super::*; + use serde_json::json; + use tempfile::TempDir; + use crate::DiGraphMap; + use crate::generic::MutGraphTrait; + use crate::io::write_to_csv; + use serde_cbor::{to_vec, ObjectKey, Value}; + + // const NODE_PD_SERVER_ADDR: &str = "59.78.194.63:2379"; + // const EDGE_PD_SERVER_ADDR: &str = "59.78.194.63:2379"; + const NODE_PD_SERVER_ADDR: &str = "127.0.0.1:2379"; + const EDGE_PD_SERVER_ADDR: &str = "127.0.0.1:2379"; + + #[test] + fn test_load_graph_to_tikv() { + //Construct test csv files + let tmp_dir = TempDir::new().unwrap(); + let tmp_dir_path = tmp_dir.path(); + + let mut g = DiGraphMap::<&str>::new(); + + // node: (id, label) + // edge: (id, id ,label) + g.add_node(0, Some("n0")); + g.add_node(1, Some("n1")); + g.add_node(2, Some("n2")); + + g.add_edge(0, 1, Some("e0")); + g.add_edge(0, 2, Some("e1")); + g.add_edge(1, 0, Some("e2")); + let path_to_nodes = tmp_dir_path.join("nodes_1.csv"); + let path_to_edges = tmp_dir_path.join("edges_1.csv"); + assert!(write_to_csv(&g, &path_to_nodes, &path_to_edges).is_ok()); + + let tikv_loader = TikvLoader::new( + Config::new(vec![NODE_PD_SERVER_ADDR.to_owned()]), + Config::new(vec![EDGE_PD_SERVER_ADDR.to_owned()]), + true, + ); + + // our function + let data_loader = DataLoader::new( + tikv_loader, + vec![path_to_nodes], + vec![path_to_edges], + true, + true, + true, + 1, + 10, + ); + + data_loader.load_graph_to_tikv(); + + block_on(async { + let client = Client::new(Config::new(vec![NODE_PD_SERVER_ADDR.to_owned()])).unwrap(); + let _node = client + .get(bincode::serialize(&0).unwrap()) + .await + .expect("Get node info failed!"); +// println!("Node0 from tikv: {:?}", _node); + match _node { + Some(value_bytes) => { + let bytes: Vec = value_bytes.into(); + let empty_map: BTreeMap = BTreeMap::new(); + assert_eq!(bytes, to_vec(&(Some("n0"), empty_map)).unwrap()); + } + None => assert!(false), + _ => {} + } + + let client = Client::new(Config::new(vec![EDGE_PD_SERVER_ADDR.to_owned()])).unwrap(); + let _edge = client + .get(bincode::serialize(&(0, 1)).unwrap()) + .await + .expect("Get edge info failed!"); +// println!("Edge(0,1) from tikv: {:?}", _edge); + match _edge { + Some(value_bytes) => { + let bytes: Vec = value_bytes.into(); + let empty_map: BTreeMap = BTreeMap::new(); + assert_eq!(bytes, to_vec(&(Some("e0"), empty_map)).unwrap()); + } + None => assert!(false), + _ => {} + } + }); + } + + #[test] + fn test_find_neighbors() { + let mut graph = TikvProperty::new( + Config::new(vec![NODE_PD_SERVER_ADDR.to_owned()]), + Config::new(vec![EDGE_PD_SERVER_ADDR.to_owned()]), + false, + ) + .unwrap(); + + let new_prop1 = json!({"edge":"eight to four,label one"}); + let raw_prop1 = to_vec(&new_prop1).unwrap(); + + graph + .insert_labeled_edge_raw(8u32, 4u32, 1, true, raw_prop1) // EL: 1 + .unwrap(); + + let new_prop2 = json!({"edge":"eight to five,label two"}); + let raw_prop2 = to_vec(&new_prop2).unwrap(); + + graph + .insert_labeled_edge_raw(8u32, 5u32, 2, true, raw_prop2) + .unwrap(); + + let new_prop3 = json!({"edge":"nine to six,label three"}); + let raw_prop3 = to_vec(&new_prop3).unwrap(); + + graph + .insert_labeled_edge_raw(9u32, 6u32, 3, true, raw_prop3) + .unwrap(); + + let pairs_parsed = graph.find_neighbors(8u32, true, None, Some(4)).unwrap(); + + + let mut expected_result = Vec::new(); + expected_result.push(( + (8u32, true, &1, 4u32), + json!({"edge":"eight to four,label one"}), + )); + expected_result.push(( + (8u32, true, &2, 5u32), + json!({"edge":"eight to five,label two"}), + )); + + assert_eq!(Some(expected_result), pairs_parsed); + } + + #[test] + fn test_insert_labeled_raw_node() { + let mut graph = TikvProperty::new( + Config::new(vec![NODE_PD_SERVER_ADDR.to_owned()]), + Config::new(vec![EDGE_PD_SERVER_ADDR.to_owned()]), + false, + ) + .unwrap(); + + let new_prop = json!({"name":"kat"}); + let raw_prop = to_vec(&new_prop).unwrap(); + + graph.insert_labeled_node_raw(6u32, 1, raw_prop).unwrap(); + //let key = bincode::serialize(&(6u32, 1)).unwrap(); + let node_property = graph.get_node_property_all_with_label(6u32, 1).unwrap(); + + assert_eq!(Some(json!({"name":"kat"})), node_property); + } + + #[test] + fn test_insert_labeled_raw_edge() { + let mut graph = TikvProperty::new( + Config::new(vec![NODE_PD_SERVER_ADDR.to_owned()]), + Config::new(vec![EDGE_PD_SERVER_ADDR.to_owned()]), + false, + ) + .unwrap(); + + let new_prop = json!({"name":"jackson"}); + let raw_prop = to_vec(&new_prop).unwrap(); + + graph + .insert_labeled_edge_raw(4u32, 9u32, 1, true, raw_prop) + .unwrap(); + + let edge_property = graph + .get_edge_property_all_with_label(4u32, 9u32, 1, true) + .unwrap(); + + assert_eq!(Some(json!({"name":"jackson"})), edge_property); + } + + #[test] + fn test_insert_raw_edge() { + let mut graph = TikvProperty::::new( + Config::new(vec![NODE_PD_SERVER_ADDR.to_owned()]), + Config::new(vec![EDGE_PD_SERVER_ADDR.to_owned()]), + false, + ) + .unwrap(); + + let new_prop = json!({"length":"15"}); + let raw_prop = to_vec(&new_prop).unwrap(); + + graph.insert_edge_raw(0u32, 1u32, raw_prop).unwrap(); + let node_property = graph.get_edge_property_all(0u32, 1u32).unwrap(); + + assert_eq!(Some(json!({"length":"15"})), node_property); + } + + #[test] + fn test_insert_raw_node() { + let mut graph = TikvProperty::::new( + Config::new(vec![NODE_PD_SERVER_ADDR.to_owned()]), + Config::new(vec![EDGE_PD_SERVER_ADDR.to_owned()]), + false, + ) + .unwrap(); + + let new_prop = json!({"length":"15"}); + let raw_prop = to_vec(&new_prop).unwrap(); + + graph.insert_node_raw(0u32, raw_prop).unwrap(); + let node_property = graph.get_node_property_all(0u32).unwrap(); + + assert_eq!(Some(json!({"length":"15"})), node_property); + } + + #[test] + fn test_insert_property_node() { + let mut graph = TikvProperty::::new( + Config::new(vec![NODE_PD_SERVER_ADDR.to_owned()]), + Config::new(vec![EDGE_PD_SERVER_ADDR.to_owned()]), + false, + ) + .unwrap(); + + let new_prop = json!({"name":"jack"}); + + graph.insert_node_property(0u32, new_prop).unwrap(); + let node_property = graph.get_node_property_all(0u32).unwrap(); + + assert_eq!(Some(json!({"name":"jack"})), node_property); + } + + #[test] + fn test_insert_property_edge() { + let mut graph = TikvProperty::::new( + Config::new(vec![NODE_PD_SERVER_ADDR.to_owned()]), + Config::new(vec![EDGE_PD_SERVER_ADDR.to_owned()]), + false, + ) + .unwrap(); + + let new_prop = json!({"length":"15"}); + + graph.insert_edge_property(0u32, 1u32, new_prop).unwrap(); + let node_property = graph.get_edge_property_all(0u32, 1u32).unwrap(); + + assert_eq!(Some(json!({"length":"15"})), node_property); + } + + #[test] + fn test_extend_raw_node() { + let mut graph = TikvProperty::::new( + Config::new(vec![NODE_PD_SERVER_ADDR.to_owned()]), + Config::new(vec![EDGE_PD_SERVER_ADDR.to_owned()]), + false, + ) + .unwrap(); + + let new_prop = json!({"name":"jack"}); + let raw_prop = to_vec(&new_prop).unwrap(); + let raw_properties = vec![(0u32, raw_prop)].into_iter(); + graph.extend_node_raw(raw_properties).unwrap(); + + let node_property = graph.get_node_property_all(0u32).unwrap(); + + assert_eq!(Some(json!({"name":"jack"})), node_property); + } + + #[test] + fn test_extend_raw_edge() { + let mut graph = TikvProperty::::new( + Config::new(vec![NODE_PD_SERVER_ADDR.to_owned()]), + Config::new(vec![EDGE_PD_SERVER_ADDR.to_owned()]), + false, + ) + .unwrap(); + + let new_prop = json!({"length":"15"}); + let raw_prop = to_vec(&new_prop).unwrap(); + let raw_properties = vec![((0u32, 1u32), raw_prop)].into_iter(); + graph.extend_edge_raw(raw_properties).unwrap(); + let edge_property = graph.get_edge_property_all(0u32, 1u32).unwrap(); + + assert_eq!(Some(json!({"length":"15"})), edge_property); + } + + #[test] + fn test_extend_property_node() { + let mut graph = TikvProperty::::new( + Config::new(vec![NODE_PD_SERVER_ADDR.to_owned()]), + Config::new(vec![EDGE_PD_SERVER_ADDR.to_owned()]), + false, + ) + .unwrap(); + + let new_prop = json!({"name":"jack"}); + + let properties = vec![(0u32, new_prop)].into_iter(); + graph.extend_node_property(properties).unwrap(); + + let node_property = graph.get_node_property_all(0u32).unwrap(); + + assert_eq!(Some(json!({"name":"jack"})), node_property); + } + + #[test] + fn test_extend_property_edge() { + let mut graph = TikvProperty::::new( + Config::new(vec![NODE_PD_SERVER_ADDR.to_owned()]), + Config::new(vec![EDGE_PD_SERVER_ADDR.to_owned()]), + false, + ) + .unwrap(); + + let new_prop = json!({"length":"15"}); + + let properties = vec![((0u32, 1u32), new_prop)].into_iter(); + graph.extend_edge_property(properties).unwrap(); + let edge_property = graph.get_edge_property_all(0u32, 1u32).unwrap(); + + assert_eq!(Some(json!({"length":"15"})), edge_property); + } + + #[test] + fn test_open_existing_db() { + { + let mut graph0 = TikvProperty::::new( + Config::new(vec![NODE_PD_SERVER_ADDR.to_owned()]), + Config::new(vec![EDGE_PD_SERVER_ADDR.to_owned()]), + false, + ) + .unwrap(); + + graph0 + .insert_node_property(0u32, json!({"name": "jack"})) + .unwrap(); + + assert_eq!( + graph0.get_node_property_all(0u32).unwrap(), + Some(json!({"name": "jack"})) + ); + } + + let graph1 = TikvProperty::::open( + Config::new(vec![NODE_PD_SERVER_ADDR.to_owned()]), + Config::new(vec![EDGE_PD_SERVER_ADDR.to_owned()]), + false, + true, + ) + .unwrap(); + + assert_eq!( + graph1.get_node_property_all(0u32).unwrap(), + Some(json!({"name": "jack"})) + ); + } + + #[test] + fn test_open_writable_db() { + { + let mut graph0 = TikvProperty::::new( + Config::new(vec![NODE_PD_SERVER_ADDR.to_owned()]), + Config::new(vec![EDGE_PD_SERVER_ADDR.to_owned()]), + false, + ) + .unwrap(); + + graph0 + .insert_node_property(0u32, json!({"name": "jack"})) + .unwrap(); + + assert_eq!( + graph0.get_node_property_all(0u32).unwrap(), + Some(json!({"name": "jack"})) + ); + } + let mut graph1 = TikvProperty::::open( + Config::new(vec![NODE_PD_SERVER_ADDR.to_owned()]), + Config::new(vec![EDGE_PD_SERVER_ADDR.to_owned()]), + false, + false, + ) + .unwrap(); + graph1 + .insert_node_property(1u32, json!({"name": "tom"})) + .unwrap(); + assert_eq!( + graph1.get_node_property_all(1u32).unwrap(), + Some(json!({"name": "tom"})) + ); + } + + #[test] + fn test_open_readonly_db() { + { + let mut graph0 = TikvProperty::::new( + Config::new(vec![NODE_PD_SERVER_ADDR.to_owned()]), + Config::new(vec![EDGE_PD_SERVER_ADDR.to_owned()]), + false, + ) + .unwrap(); + + graph0 + .insert_node_property(0u32, json!({"name": "jack"})) + .unwrap(); + + assert_eq!( + graph0.get_node_property_all(0u32).unwrap(), + Some(json!({"name": "jack"})) + ); + } + + let mut graph1 = TikvProperty::::open( + Config::new(vec![NODE_PD_SERVER_ADDR.to_owned()]), + Config::new(vec![EDGE_PD_SERVER_ADDR.to_owned()]), + false, + true, + ) + .unwrap(); + assert_eq!( + graph1.get_node_property_all(0u32).unwrap(), + Some(json!({"name": "jack"})) + ); + + let err = graph1 + .insert_node_property(1u32, json!({"name": "tom"})) + .is_err(); + assert_eq!(err, true); + } + + #[test] + fn test_scan_node_property() { + let mut graph = TikvProperty::::new( + Config::new(vec![NODE_PD_SERVER_ADDR.to_owned()]), + Config::new(vec![EDGE_PD_SERVER_ADDR.to_owned()]), + false, + ) + .unwrap(); + + graph + .insert_node_property(0u32, json!({"name": "jack"})) + .unwrap(); + + graph + .insert_node_property(1u32, json!({"name": "tom"})) + .unwrap(); + + let mut iter = graph.scan_node_property_all(); + assert_eq!( + (0u32, json!({"name": "jack"})), + iter.next().unwrap().unwrap() + ); + assert_eq!( + (1u32, json!({"name": "tom"})), + iter.next().unwrap().unwrap() + ); + } + + #[test] + fn test_scan_edge_property() { + let mut graph = TikvProperty::::new( + Config::new(vec![NODE_PD_SERVER_ADDR.to_owned()]), + Config::new(vec![EDGE_PD_SERVER_ADDR.to_owned()]), + false, + ) + .unwrap(); + + graph + .insert_edge_property(0u32, 1u32, json!({"length": "5"})) + .unwrap(); + + graph + .insert_edge_property(1u32, 2u32, json!({"length": "10"})) + .unwrap(); + + let mut iter = graph.scan_edge_property_all(); + assert_eq!( + ((0u32, 1u32), json!({"length": "5"})), + iter.next().unwrap().unwrap() + ); + assert_eq!( + ((1u32, 2u32), json!({"length": "10"})), + iter.next().unwrap().unwrap() + ); + } +} + +pub trait PrefixScan< + Id: IdType + Serialize + DeserializeOwned, + EL: Hash + Eq + Serialize + DeserializeOwned, + LabelId: IdType = Id, +> +{ + /// find neighbors of src with given direction and label(Option) + fn find_neighbors( + &self, + src: Id, + direction: bool, + label: Option, + scan_limit: Option, + ) -> Result>, PropertyError>; +} + +impl< + Id: IdType + Serialize + DeserializeOwned, + EL: Hash + Eq + Serialize + DeserializeOwned, + > PrefixScan for TikvProperty +{ + fn find_neighbors( + &self, + src: Id, + direction: bool, + label: Option, + scan_limit: Option, + ) -> Result>, PropertyError> { + // if there's any, find certain labeled edges, or search through all labels. + let (label_from, label_to) = if let Some(label) = label { + let id = self.label_map.find_index(&label); + if id == None { + // Some(label).expect("There's no such label in the record!"); + return Err(PropertyError::NoLabelInMapError); + } + (id.unwrap(), id.unwrap()) + } else { + (0, self.label_map.len()) + }; + + let left = + bincode::serialize(&(src, direction, label_from, usize::min_value())).unwrap(); + let right = + bincode::serialize(&(src, direction, label_to, usize::max_value())).unwrap(); + + let limit = match scan_limit { + Some(limit) => limit, + None => 10240, //max scan limit is 10240 + }; + + let client = self.edge_client.clone(); + block_on(async { + let inclusive_range = left..=right; + + let req = client.scan(inclusive_range.to_owned(), limit); + let kv_pairs = req.await?; + if kv_pairs.is_empty() { + Ok(None) + } else { + let mut pairs_parsed = Vec::new(); + for kv_pair in kv_pairs { + let key_parsed: (Id, bool, usize, Id) = + bincode::deserialize(kv_pair.key().into())?; + let label_id = key_parsed.2; + let label = self.label_map.get_item(label_id).unwrap(); + let key: (Id, bool, &EL, Id) = + (key_parsed.0, key_parsed.1, label, key_parsed.3); + let value_parsed: JsonValue = from_slice(kv_pair.value().into())?; + pairs_parsed.push((key, value_parsed)); + } + Ok(Some(pairs_parsed)) + } + }) + } +} + + +/// store the graph(.csv) into TiKV. +pub struct DataLoader { + path_to_nodes: Vec, + path_to_edges: Vec, + is_directed: bool, +// separator: u8, + has_headers: bool, + is_flexible: bool, + thread_cnt: usize, + batch_size: usize, + tikv_loader: TikvLoader, +} + +impl DataLoader { + pub fn new( + tikv_loader: TikvLoader, + path_to_nodes: Vec, + path_to_edges: Vec, + is_directed: bool, + has_headers: bool, + is_flexible: bool, + thread_cnt: usize, + batch_size: usize) -> Self { + DataLoader { + tikv_loader, + path_to_nodes, + path_to_edges, + is_directed, + has_headers, + is_flexible, + thread_cnt, + batch_size, + } + } + + fn load_graph_to_tikv(&self) + -> Result<(), PropertyError> { + // send file address into reader + let reader = CSVReader::::new( + self.path_to_nodes.clone(), + self.path_to_edges.clone()) + .headers(self.has_headers) + .flexible(self.is_flexible); + // 2. load .csv files into tikv + self.tikv_loader.load(&reader, self.thread_cnt, self.batch_size); + + Ok(()) + } +} diff --git a/tests/algorithm.rs b/tests/algorithm.rs index 9dafc7e3..09b9605e 100644 --- a/tests/algorithm.rs +++ b/tests/algorithm.rs @@ -1,1073 +1,1122 @@ -/* - * Copyright (c) 2018 UNSW Sydney, Data and Knowledge Group. - * - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you 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. - */ -extern crate rust_graph; - -use rust_graph::algorithm::{graph_minus, graph_union, Bfs, ConnComp, ConnSubgraph, Dfs}; -use rust_graph::graph_impl::{DiGraphMap, UnGraphMap}; -use rust_graph::prelude::*; - -#[test] -fn test_cc_undirected_one_component() { - let mut graph = UnGraphMap::::new(); - graph.add_edge(1, 2, None); - graph.add_edge(2, 3, None); - - let cc = ConnComp::new(&graph); - - assert_eq!(cc.get_count(), 1); - - assert_eq!(cc.is_connected(1, 2), true); - assert_eq!(cc.is_connected(2, 3), true); - assert_eq!(cc.is_connected(1, 3), true); - - assert_eq!(cc.get_connected_nodes(1).unwrap().len(), 3); - assert_eq!(cc.get_connected_nodes(2).unwrap().len(), 3); - assert_eq!(cc.get_connected_nodes(3).unwrap().len(), 3); -} - -#[test] -fn test_cc_undirected_seperate_components() { - let mut graph = UnGraphMap::::new(); - graph.add_edge(1, 2, None); - graph.add_edge(3, 4, None); - - let cc = ConnComp::new(&graph); - - assert_eq!(cc.get_count(), 2); - - assert_eq!(cc.is_connected(1, 2), true); - assert_eq!(cc.is_connected(2, 3), false); - assert_eq!(cc.is_connected(1, 3), false); - assert_eq!(cc.is_connected(1, 4), false); - assert_eq!(cc.is_connected(2, 4), false); - assert_eq!(cc.is_connected(3, 4), true); - - assert_eq!(cc.get_connected_nodes(1).unwrap().len(), 2); - assert_eq!(cc.get_connected_nodes(2).unwrap().len(), 2); - assert_eq!(cc.get_connected_nodes(3).unwrap().len(), 2); - assert_eq!(cc.get_connected_nodes(4).unwrap().len(), 2); -} - -#[test] -fn test_cc_directed_one_component() { - let mut graph = DiGraphMap::::new(); - graph.add_edge(1, 2, None); - graph.add_edge(2, 3, None); - - let cc = ConnComp::new(&graph); - - assert_eq!(cc.get_count(), 1); - - assert_eq!(cc.is_connected(1, 2), true); - assert_eq!(cc.is_connected(2, 3), true); - assert_eq!(cc.is_connected(1, 3), true); - - assert_eq!(cc.get_connected_nodes(1).unwrap().len(), 3); - assert_eq!(cc.get_connected_nodes(2).unwrap().len(), 3); - assert_eq!(cc.get_connected_nodes(3).unwrap().len(), 3); -} - -#[test] -fn test_cc_directed_seperate_components() { - let mut graph = DiGraphMap::::new(); - graph.add_edge(1, 2, None); - graph.add_edge(3, 4, None); - - let cc = ConnComp::new(&graph); - - assert_eq!(cc.get_count(), 2); - - assert_eq!(cc.is_connected(1, 2), true); - assert_eq!(cc.is_connected(2, 3), false); - assert_eq!(cc.is_connected(1, 3), false); - assert_eq!(cc.is_connected(1, 4), false); - assert_eq!(cc.is_connected(2, 4), false); - assert_eq!(cc.is_connected(3, 4), true); - - assert_eq!(cc.get_connected_nodes(1).unwrap().len(), 2); - assert_eq!(cc.get_connected_nodes(2).unwrap().len(), 2); - assert_eq!(cc.get_connected_nodes(3).unwrap().len(), 2); - assert_eq!(cc.get_connected_nodes(4).unwrap().len(), 2); -} - -#[test] -fn test_bfs_undirected_one_component() { - let mut graph = UnGraphMap::::new(); - graph.add_edge(1, 2, None); - graph.add_edge(2, 3, None); - - let mut bfs = Bfs::new(&graph, Some(1)); - let x = bfs.next(); - assert_eq!(x, Some(1)); - let x = bfs.next(); - assert_eq!(x, Some(2)); - let x = bfs.next(); - assert_eq!(x, Some(3)); - let x = bfs.next(); - assert_eq!(x, None); -} - -#[test] -fn test_bfs_undirected_radomly_chosen_start() { - let mut graph = UnGraphMap::::new(); - graph.add_edge(1, 2, None); - - let mut bfs = Bfs::new(&graph, None); - let x = bfs.next(); - let result = x == Some(1) || x == Some(2); - assert_eq!(result, true); -} - -#[test] -fn test_bfs_undirected_seperate_components() { - let mut graph = UnGraphMap::::new(); - graph.add_edge(1, 2, None); - graph.add_edge(3, 4, None); - - let mut bfs = Bfs::new(&graph, Some(1)); - let x = bfs.next(); - assert_eq!(x, Some(1)); - let x = bfs.next(); - assert_eq!(x, Some(2)); - let x = bfs.next(); - let result = x == Some(3) || x == Some(4); - assert_eq!(result, true); -} - -#[test] -fn test_bfs_directed_one_component() { - let mut graph = DiGraphMap::::new(); - graph.add_edge(2, 1, None); - graph.add_edge(3, 1, None); - - let mut bfs = Bfs::new(&graph, Some(1)); - let x = bfs.next(); - assert_eq!(x, Some(1)); - let x = bfs.next(); - let result = x == Some(3) || x == Some(2); - assert_eq!(result, true); -} - -#[test] -fn test_bfs_directed_radomly_chosen_start() { - let mut graph = DiGraphMap::::new(); - graph.add_edge(1, 2, None); - - let mut bfs = Bfs::new(&graph, None); - let x = bfs.next(); - let result = x == Some(1) || x == Some(2); - assert_eq!(result, true); -} - -#[test] -fn test_bfs_directed_seperate_components() { - let mut graph = DiGraphMap::::new(); - graph.add_edge(1, 2, None); - graph.add_edge(3, 4, None); - - let mut bfs = Bfs::new(&graph, Some(1)); - let x = bfs.next(); - assert_eq!(x, Some(1)); - let x = bfs.next(); - assert_eq!(x, Some(2)); - let x = bfs.next(); - let result = x == Some(3) || x == Some(4); - assert_eq!(result, true); -} - -#[test] -fn test_dfs_undirected_one_component() { - let mut graph = UnGraphMap::::new(); - graph.add_edge(1, 2, None); - graph.add_edge(2, 3, None); - - let mut dfs = Dfs::new(&graph, Some(1)); - let x = dfs.next(); - assert_eq!(x, Some(1)); - let x = dfs.next(); - assert_eq!(x, Some(2)); - let x = dfs.next(); - assert_eq!(x, Some(3)); - let x = dfs.next(); - assert_eq!(x, None); -} - -#[test] -fn test_dfs_undirected_radomly_chosen_start() { - let mut graph = UnGraphMap::::new(); - graph.add_edge(1, 2, None); - - let mut dfs = Dfs::new(&graph, None); - let x = dfs.next(); - let result = x == Some(1) || x == Some(2); - assert_eq!(result, true); -} - -#[test] -fn test_dfs_undirected_seperate_components() { - let mut graph = UnGraphMap::::new(); - graph.add_edge(1, 2, None); - graph.add_edge(3, 4, None); - - let mut dfs = Dfs::new(&graph, Some(1)); - let x = dfs.next(); - assert_eq!(x, Some(1)); - let x = dfs.next(); - assert_eq!(x, Some(2)); - let x = dfs.next(); - let result = x == Some(3) || x == Some(4); - assert_eq!(result, true); -} - -#[test] -fn test_dfs_directed_one_component() { - let mut graph = DiGraphMap::::new(); - graph.add_edge(2, 1, None); - graph.add_edge(3, 1, None); - - let mut dfs = Dfs::new(&graph, Some(1)); - let x = dfs.next(); - assert_eq!(x, Some(1)); - let x = dfs.next(); - let result = x == Some(3) || x == Some(2); - assert_eq!(result, true); -} - -#[test] -fn test_dfs_directed_radomly_chosen_start() { - let mut graph = DiGraphMap::::new(); - graph.add_edge(1, 2, None); - - let mut dfs = Dfs::new(&graph, None); - let x = dfs.next(); - let result = x == Some(1) || x == Some(2); - assert_eq!(result, true); -} - -#[test] -fn test_dfs_directed_seperate_components() { - let mut graph = DiGraphMap::::new(); - graph.add_edge(1, 2, None); - graph.add_edge(3, 4, None); - - let mut dfs = Dfs::new(&graph, Some(1)); - let x = dfs.next(); - assert_eq!(x, Some(1)); - let x = dfs.next(); - assert_eq!(x, Some(2)); - let x = dfs.next(); - let result = x == Some(3) || x == Some(4); - assert_eq!(result, true); -} - -#[test] -fn test_conn_subgraphs_undirected_seperate_components() { - let mut graph = UnGraphMap::::new(); - graph.add_node(1, Some(0)); - graph.add_node(2, Some(1)); - graph.add_node(3, Some(2)); - graph.add_node(4, Some(3)); - - graph.add_edge(1, 2, Some(10)); - graph.add_edge(3, 4, Some(20)); - - let cs = ConnSubgraph::new(&graph); - let subgraphs = cs.into_result(); - assert_eq!(subgraphs.len(), 2); - - assert_eq!(subgraphs[0].has_node(1), true); - assert_eq!(subgraphs[0].has_node(2), true); - assert_eq!(subgraphs[0].has_node(3), false); - assert_eq!(subgraphs[0].has_node(4), false); - assert_eq!(subgraphs[1].has_node(1), false); - assert_eq!(subgraphs[1].has_node(2), false); - assert_eq!(subgraphs[1].has_node(3), true); - assert_eq!(subgraphs[1].has_node(4), true); - - assert_eq!(subgraphs[0].has_edge(1, 2), true); - assert_eq!(subgraphs[0].has_edge(3, 4), false); - assert_eq!(subgraphs[1].has_edge(1, 2), false); - assert_eq!(subgraphs[1].has_edge(3, 4), true); - assert_eq!(subgraphs[0].has_edge(2, 1), true); - assert_eq!(subgraphs[0].has_edge(4, 3), false); - assert_eq!(subgraphs[1].has_edge(2, 1), false); - assert_eq!(subgraphs[1].has_edge(4, 3), true); - - assert_eq!(subgraphs[0].get_node_label(1), Some(&0)); - assert_eq!(subgraphs[0].get_node_label(2), Some(&1)); - assert_eq!(subgraphs[1].get_node_label(3), Some(&2)); - assert_eq!(subgraphs[1].get_node_label(4), Some(&3)); - - assert_eq!(graph.get_edge_label(1, 2), Some(&10)); - assert_eq!(graph.get_edge_label(3, 4), Some(&20)); - assert_eq!(graph.get_edge_label(2, 1), Some(&10)); - assert_eq!(graph.get_edge_label(4, 3), Some(&20)); -} - -#[test] -fn test_conn_subgraphs_directed_seperate_components() { - let mut graph = DiGraphMap::::new(); - graph.add_node(1, Some(0)); - graph.add_node(2, Some(1)); - graph.add_node(3, Some(2)); - graph.add_node(4, Some(3)); - - graph.add_edge(1, 2, Some(10)); - graph.add_edge(3, 4, Some(20)); - - let cs = ConnSubgraph::new(&graph); - let subgraphs = cs.into_result(); - assert_eq!(subgraphs.len(), 2); - - assert_eq!(subgraphs[0].has_node(1), true); - assert_eq!(subgraphs[0].has_node(2), true); - assert_eq!(subgraphs[0].has_node(3), false); - assert_eq!(subgraphs[0].has_node(4), false); - assert_eq!(subgraphs[1].has_node(1), false); - assert_eq!(subgraphs[1].has_node(2), false); - assert_eq!(subgraphs[1].has_node(3), true); - assert_eq!(subgraphs[1].has_node(4), true); - - assert_eq!(subgraphs[0].has_edge(1, 2), true); - assert_eq!(subgraphs[0].has_edge(3, 4), false); - assert_eq!(subgraphs[1].has_edge(1, 2), false); - assert_eq!(subgraphs[1].has_edge(3, 4), true); - assert_eq!(subgraphs[0].has_edge(2, 1), false); - assert_eq!(subgraphs[0].has_edge(4, 3), false); - assert_eq!(subgraphs[1].has_edge(2, 1), false); - assert_eq!(subgraphs[1].has_edge(4, 3), false); - - assert_eq!(subgraphs[0].get_node_label(1), Some(&0)); - assert_eq!(subgraphs[0].get_node_label(2), Some(&1)); - assert_eq!(subgraphs[1].get_node_label(3), Some(&2)); - assert_eq!(subgraphs[1].get_node_label(4), Some(&3)); - - assert_eq!(graph.get_edge_label(1, 2), Some(&10)); - assert_eq!(graph.get_edge_label(3, 4), Some(&20)); - assert_eq!(graph.get_edge_label(2, 1), None); - assert_eq!(graph.get_edge_label(4, 3), None); -} - -#[test] -fn test_graph_union_directed_graphs() { - let mut graph0 = DiGraphMap::::new(); - graph0.add_node(1, Some(0)); - graph0.add_node(2, Some(1)); - graph0.add_edge(1, 2, Some(10)); - - let mut graph1 = DiGraphMap::::new(); - graph1.add_node(3, Some(2)); - graph1.add_node(4, Some(3)); - graph1.add_edge(3, 4, Some(20)); - - let result_graph = graph_union(&graph0, &graph1); - - assert_eq!(result_graph.node_count(), 4); - assert_eq!(result_graph.edge_count(), 2); - - assert_eq!(result_graph.has_node(1), true); - assert_eq!(result_graph.has_node(2), true); - assert_eq!(result_graph.has_node(3), true); - assert_eq!(result_graph.has_node(4), true); - - assert_eq!(result_graph.has_edge(1, 2), true); - assert_eq!(result_graph.has_edge(3, 4), true); - assert_eq!(result_graph.has_edge(2, 1), false); - assert_eq!(result_graph.has_edge(4, 3), false); - assert_eq!(result_graph.has_edge(2, 3), false); - assert_eq!(result_graph.has_edge(1, 4), false); - - assert_eq!(result_graph.get_node_label(1), Some(&0)); - assert_eq!(result_graph.get_node_label(2), Some(&1)); - assert_eq!(result_graph.get_node_label(3), Some(&2)); - assert_eq!(result_graph.get_node_label(4), Some(&3)); - - assert_eq!(result_graph.get_edge_label(1, 2), Some(&10)); - assert_eq!(result_graph.get_edge_label(3, 4), Some(&20)); - assert_eq!(result_graph.get_edge_label(1, 4), None); - assert_eq!(result_graph.get_edge_label(2, 3), None); - assert_eq!(result_graph.get_edge_label(2, 1), None); - assert_eq!(result_graph.get_edge_label(4, 3), None); -} - -#[test] -fn test_graph_union_undirected_graphs() { - let mut graph0 = UnGraphMap::::new(); - graph0.add_node(1, Some(0)); - graph0.add_node(2, Some(1)); - graph0.add_edge(1, 2, Some(10)); - - let mut graph1 = UnGraphMap::::new(); - graph1.add_node(3, Some(2)); - graph1.add_node(4, Some(3)); - graph1.add_edge(3, 4, Some(20)); - - let result_graph = graph_union(&graph0, &graph1); - - assert_eq!(result_graph.node_count(), 4); - assert_eq!(result_graph.edge_count(), 2); - - assert_eq!(result_graph.has_node(1), true); - assert_eq!(result_graph.has_node(2), true); - assert_eq!(result_graph.has_node(3), true); - assert_eq!(result_graph.has_node(4), true); - - assert_eq!(result_graph.has_edge(1, 2), true); - assert_eq!(result_graph.has_edge(3, 4), true); - assert_eq!(result_graph.has_edge(2, 1), true); - assert_eq!(result_graph.has_edge(4, 3), true); - assert_eq!(result_graph.has_edge(2, 3), false); - assert_eq!(result_graph.has_edge(1, 4), false); - - assert_eq!(result_graph.get_node_label(1), Some(&0)); - assert_eq!(result_graph.get_node_label(2), Some(&1)); - assert_eq!(result_graph.get_node_label(3), Some(&2)); - assert_eq!(result_graph.get_node_label(4), Some(&3)); - - assert_eq!(result_graph.get_edge_label(1, 2), Some(&10)); - assert_eq!(result_graph.get_edge_label(3, 4), Some(&20)); - assert_eq!(result_graph.get_edge_label(1, 4), None); - assert_eq!(result_graph.get_edge_label(2, 3), None); - assert_eq!(result_graph.get_edge_label(2, 1), Some(&10)); - assert_eq!(result_graph.get_edge_label(4, 3), Some(&20)); -} - -#[test] -fn test_graph_add_directed_graphs() { - let mut graph0 = DiGraphMap::::new(); - graph0.add_node(1, Some(0)); - graph0.add_node(2, Some(1)); - graph0.add_edge(1, 2, Some(10)); - - let mut graph1 = DiGraphMap::::new(); - graph1.add_node(3, Some(2)); - graph1.add_node(4, Some(3)); - graph1.add_edge(3, 4, Some(20)); - - let result_graph = graph0.as_general_graph() + graph1.as_general_graph(); - - assert_eq!(result_graph.node_count(), 4); - assert_eq!(result_graph.edge_count(), 2); - - assert_eq!(result_graph.has_node(1), true); - assert_eq!(result_graph.has_node(2), true); - assert_eq!(result_graph.has_node(3), true); - assert_eq!(result_graph.has_node(4), true); - - assert_eq!(result_graph.has_edge(1, 2), true); - assert_eq!(result_graph.has_edge(3, 4), true); - assert_eq!(result_graph.has_edge(2, 1), false); - assert_eq!(result_graph.has_edge(4, 3), false); - assert_eq!(result_graph.has_edge(2, 3), false); - assert_eq!(result_graph.has_edge(1, 4), false); - - assert_eq!(result_graph.get_node_label(1), Some(&0)); - assert_eq!(result_graph.get_node_label(2), Some(&1)); - assert_eq!(result_graph.get_node_label(3), Some(&2)); - assert_eq!(result_graph.get_node_label(4), Some(&3)); - - assert_eq!(result_graph.get_edge_label(1, 2), Some(&10)); - assert_eq!(result_graph.get_edge_label(3, 4), Some(&20)); - assert_eq!(result_graph.get_edge_label(1, 4), None); - assert_eq!(result_graph.get_edge_label(2, 3), None); - assert_eq!(result_graph.get_edge_label(2, 1), None); - assert_eq!(result_graph.get_edge_label(4, 3), None); -} - -#[test] -fn test_graph_add_undirected_graphs() { - let mut graph0 = UnGraphMap::::new(); - graph0.add_node(1, Some(0)); - graph0.add_node(2, Some(1)); - graph0.add_edge(1, 2, Some(10)); - - let mut graph1 = UnGraphMap::::new(); - graph1.add_node(3, Some(2)); - graph1.add_node(4, Some(3)); - graph1.add_edge(3, 4, Some(20)); - - let box0: Box> = Box::new(graph0); - let box1: Box> = Box::new(graph1); - let result_graph = box0 + box1; - - assert_eq!(result_graph.node_count(), 4); - assert_eq!(result_graph.edge_count(), 2); - - assert_eq!(result_graph.has_node(1), true); - assert_eq!(result_graph.has_node(2), true); - assert_eq!(result_graph.has_node(3), true); - assert_eq!(result_graph.has_node(4), true); - - assert_eq!(result_graph.has_edge(1, 2), true); - assert_eq!(result_graph.has_edge(3, 4), true); - assert_eq!(result_graph.has_edge(2, 1), true); - assert_eq!(result_graph.has_edge(4, 3), true); - assert_eq!(result_graph.has_edge(2, 3), false); - assert_eq!(result_graph.has_edge(1, 4), false); - - assert_eq!(result_graph.get_node_label(1), Some(&0)); - assert_eq!(result_graph.get_node_label(2), Some(&1)); - assert_eq!(result_graph.get_node_label(3), Some(&2)); - assert_eq!(result_graph.get_node_label(4), Some(&3)); - - assert_eq!(result_graph.get_edge_label(1, 2), Some(&10)); - assert_eq!(result_graph.get_edge_label(3, 4), Some(&20)); - assert_eq!(result_graph.get_edge_label(1, 4), None); - assert_eq!(result_graph.get_edge_label(2, 3), None); - assert_eq!(result_graph.get_edge_label(2, 1), Some(&10)); - assert_eq!(result_graph.get_edge_label(4, 3), Some(&20)); -} - -#[test] -fn test_graph_add_boxed_directed_generalgraphs() { - let mut graph0 = DiGraphMap::::new(); - graph0.add_node(1, Some(0)); - graph0.add_node(2, Some(1)); - graph0.add_edge(1, 2, Some(10)); - - let mut graph1 = DiGraphMap::::new(); - graph1.add_node(3, Some(2)); - graph1.add_node(4, Some(3)); - graph1.add_edge(3, 4, Some(20)); - - let box0: Box> = Box::new(graph0); - let box1: Box> = Box::new(graph1); - let result_graph = box0 + box1; - - assert_eq!(result_graph.node_count(), 4); - assert_eq!(result_graph.edge_count(), 2); - - assert_eq!(result_graph.has_node(1), true); - assert_eq!(result_graph.has_node(2), true); - assert_eq!(result_graph.has_node(3), true); - assert_eq!(result_graph.has_node(4), true); - - assert_eq!(result_graph.has_edge(1, 2), true); - assert_eq!(result_graph.has_edge(3, 4), true); - assert_eq!(result_graph.has_edge(2, 1), false); - assert_eq!(result_graph.has_edge(4, 3), false); - assert_eq!(result_graph.has_edge(2, 3), false); - assert_eq!(result_graph.has_edge(1, 4), false); - - assert_eq!(result_graph.get_node_label(1), Some(&0)); - assert_eq!(result_graph.get_node_label(2), Some(&1)); - assert_eq!(result_graph.get_node_label(3), Some(&2)); - assert_eq!(result_graph.get_node_label(4), Some(&3)); - - assert_eq!(result_graph.get_edge_label(1, 2), Some(&10)); - assert_eq!(result_graph.get_edge_label(3, 4), Some(&20)); - assert_eq!(result_graph.get_edge_label(1, 4), None); - assert_eq!(result_graph.get_edge_label(2, 3), None); - assert_eq!(result_graph.get_edge_label(2, 1), None); - assert_eq!(result_graph.get_edge_label(4, 3), None); -} - -#[test] -fn test_graph_add_boxed_undirected_generalgraphs() { - let mut graph0 = UnGraphMap::::new(); - graph0.add_node(1, Some(0)); - graph0.add_node(2, Some(1)); - graph0.add_edge(1, 2, Some(10)); - - let mut graph1 = UnGraphMap::::new(); - graph1.add_node(3, Some(2)); - graph1.add_node(4, Some(3)); - graph1.add_edge(3, 4, Some(20)); - - let box0: Box> = Box::new(graph0); - let box1: Box> = Box::new(graph1); - let result_graph = box0 + box1; - - assert_eq!(result_graph.node_count(), 4); - assert_eq!(result_graph.edge_count(), 2); - - assert_eq!(result_graph.has_node(1), true); - assert_eq!(result_graph.has_node(2), true); - assert_eq!(result_graph.has_node(3), true); - assert_eq!(result_graph.has_node(4), true); - - assert_eq!(result_graph.has_edge(1, 2), true); - assert_eq!(result_graph.has_edge(3, 4), true); - assert_eq!(result_graph.has_edge(2, 1), true); - assert_eq!(result_graph.has_edge(4, 3), true); - assert_eq!(result_graph.has_edge(2, 3), false); - assert_eq!(result_graph.has_edge(1, 4), false); - - assert_eq!(result_graph.get_node_label(1), Some(&0)); - assert_eq!(result_graph.get_node_label(2), Some(&1)); - assert_eq!(result_graph.get_node_label(3), Some(&2)); - assert_eq!(result_graph.get_node_label(4), Some(&3)); - - assert_eq!(result_graph.get_edge_label(1, 2), Some(&10)); - assert_eq!(result_graph.get_edge_label(3, 4), Some(&20)); - assert_eq!(result_graph.get_edge_label(1, 4), None); - assert_eq!(result_graph.get_edge_label(2, 3), None); - assert_eq!(result_graph.get_edge_label(2, 1), Some(&10)); - assert_eq!(result_graph.get_edge_label(4, 3), Some(&20)); -} - -#[test] -fn test_graph_add_directed_typedgraphs() { - let mut graph0 = DiGraphMap::::new(); - graph0.add_node(1, Some(0)); - graph0.add_node(2, Some(1)); - graph0.add_edge(1, 2, Some(10)); - - let mut graph1 = DiGraphMap::::new(); - graph1.add_node(3, Some(2)); - graph1.add_node(4, Some(3)); - graph1.add_edge(3, 4, Some(20)); - - let result_graph = graph0 + graph1; - - assert_eq!(result_graph.node_count(), 4); - assert_eq!(result_graph.edge_count(), 2); - - assert_eq!(result_graph.has_node(1), true); - assert_eq!(result_graph.has_node(2), true); - assert_eq!(result_graph.has_node(3), true); - assert_eq!(result_graph.has_node(4), true); - - assert_eq!(result_graph.has_edge(1, 2), true); - assert_eq!(result_graph.has_edge(3, 4), true); - assert_eq!(result_graph.has_edge(2, 1), false); - assert_eq!(result_graph.has_edge(4, 3), false); - assert_eq!(result_graph.has_edge(2, 3), false); - assert_eq!(result_graph.has_edge(1, 4), false); - - assert_eq!(result_graph.get_node_label(1), Some(&0)); - assert_eq!(result_graph.get_node_label(2), Some(&1)); - assert_eq!(result_graph.get_node_label(3), Some(&2)); - assert_eq!(result_graph.get_node_label(4), Some(&3)); - - assert_eq!(result_graph.get_edge_label(1, 2), Some(&10)); - assert_eq!(result_graph.get_edge_label(3, 4), Some(&20)); - assert_eq!(result_graph.get_edge_label(1, 4), None); - assert_eq!(result_graph.get_edge_label(2, 3), None); - assert_eq!(result_graph.get_edge_label(2, 1), None); - assert_eq!(result_graph.get_edge_label(4, 3), None); -} - -#[test] -fn test_graph_add_undirected_typedgraphs() { - let mut graph0 = UnGraphMap::::new(); - graph0.add_node(1, Some(0)); - graph0.add_node(2, Some(1)); - graph0.add_edge(1, 2, Some(10)); - - let mut graph1 = UnGraphMap::::new(); - graph1.add_node(3, Some(2)); - graph1.add_node(4, Some(3)); - graph1.add_edge(3, 4, Some(20)); - - let result_graph = graph0 + graph1; - - assert_eq!(result_graph.node_count(), 4); - assert_eq!(result_graph.edge_count(), 2); - - assert_eq!(result_graph.has_node(1), true); - assert_eq!(result_graph.has_node(2), true); - assert_eq!(result_graph.has_node(3), true); - assert_eq!(result_graph.has_node(4), true); - - assert_eq!(result_graph.has_edge(1, 2), true); - assert_eq!(result_graph.has_edge(3, 4), true); - assert_eq!(result_graph.has_edge(2, 1), true); - assert_eq!(result_graph.has_edge(4, 3), true); - assert_eq!(result_graph.has_edge(2, 3), false); - assert_eq!(result_graph.has_edge(1, 4), false); - - assert_eq!(result_graph.get_node_label(1), Some(&0)); - assert_eq!(result_graph.get_node_label(2), Some(&1)); - assert_eq!(result_graph.get_node_label(3), Some(&2)); - assert_eq!(result_graph.get_node_label(4), Some(&3)); - - assert_eq!(result_graph.get_edge_label(1, 2), Some(&10)); - assert_eq!(result_graph.get_edge_label(3, 4), Some(&20)); - assert_eq!(result_graph.get_edge_label(1, 4), None); - assert_eq!(result_graph.get_edge_label(2, 3), None); - assert_eq!(result_graph.get_edge_label(2, 1), Some(&10)); - assert_eq!(result_graph.get_edge_label(4, 3), Some(&20)); -} - -#[test] -fn test_graph_minus_directed_graphs() { - let mut graph0 = DiGraphMap::::new(); - graph0.add_node(1, Some(0)); - graph0.add_node(2, Some(1)); - graph0.add_node(3, Some(2)); - graph0.add_node(4, Some(3)); - graph0.add_edge(1, 2, Some(10)); - graph0.add_edge(3, 4, Some(20)); - - let mut graph1 = DiGraphMap::::new(); - graph1.add_node(3, Some(2)); - graph1.add_node(4, Some(3)); - graph1.add_edge(3, 4, Some(20)); - - let result_graph = graph_minus(&graph0, &graph1); - - assert_eq!(result_graph.node_count(), 2); - assert_eq!(result_graph.edge_count(), 1); - - assert_eq!(result_graph.has_node(1), true); - assert_eq!(result_graph.has_node(2), true); - assert_eq!(result_graph.has_node(3), false); - assert_eq!(result_graph.has_node(4), false); - - assert_eq!(result_graph.has_edge(1, 2), true); - assert_eq!(result_graph.has_edge(3, 4), false); - assert_eq!(result_graph.has_edge(2, 1), false); - assert_eq!(result_graph.has_edge(4, 3), false); - assert_eq!(result_graph.has_edge(2, 3), false); - assert_eq!(result_graph.has_edge(1, 4), false); - - assert_eq!(result_graph.get_node_label(1), Some(&0)); - assert_eq!(result_graph.get_node_label(2), Some(&1)); - assert_eq!(result_graph.get_node_label(3), None); - assert_eq!(result_graph.get_node_label(4), None); - - assert_eq!(result_graph.get_edge_label(1, 2), Some(&10)); - assert_eq!(result_graph.get_edge_label(3, 4), None); - assert_eq!(result_graph.get_edge_label(1, 4), None); - assert_eq!(result_graph.get_edge_label(2, 3), None); - assert_eq!(result_graph.get_edge_label(2, 1), None); - assert_eq!(result_graph.get_edge_label(4, 3), None); -} - -#[test] -fn test_graph_minus_undirected_graphs() { - let mut graph0 = UnGraphMap::::new(); - graph0.add_node(1, Some(0)); - graph0.add_node(2, Some(1)); - graph0.add_node(3, Some(2)); - graph0.add_node(4, Some(3)); - graph0.add_edge(1, 2, Some(10)); - graph0.add_edge(3, 4, Some(20)); - - let mut graph1 = UnGraphMap::::new(); - graph1.add_node(3, Some(2)); - graph1.add_node(4, Some(3)); - graph1.add_edge(3, 4, Some(20)); - - let result_graph = graph_minus(&graph0, &graph1); - - assert_eq!(result_graph.node_count(), 2); - assert_eq!(result_graph.edge_count(), 1); - - assert_eq!(result_graph.has_node(1), true); - assert_eq!(result_graph.has_node(2), true); - assert_eq!(result_graph.has_node(3), false); - assert_eq!(result_graph.has_node(4), false); - - assert_eq!(result_graph.has_edge(1, 2), true); - assert_eq!(result_graph.has_edge(3, 4), false); - assert_eq!(result_graph.has_edge(2, 1), true); - assert_eq!(result_graph.has_edge(4, 3), false); - assert_eq!(result_graph.has_edge(2, 3), false); - assert_eq!(result_graph.has_edge(1, 4), false); - - assert_eq!(result_graph.get_node_label(1), Some(&0)); - assert_eq!(result_graph.get_node_label(2), Some(&1)); - assert_eq!(result_graph.get_node_label(3), None); - assert_eq!(result_graph.get_node_label(4), None); - - assert_eq!(result_graph.get_edge_label(1, 2), Some(&10)); - assert_eq!(result_graph.get_edge_label(3, 4), None); - assert_eq!(result_graph.get_edge_label(1, 4), None); - assert_eq!(result_graph.get_edge_label(2, 3), None); - assert_eq!(result_graph.get_edge_label(2, 1), Some(&10)); - assert_eq!(result_graph.get_edge_label(4, 3), None); -} - -#[test] -fn test_graph_sub_directed_graphs() { - let mut graph0 = DiGraphMap::::new(); - graph0.add_node(1, Some(0)); - graph0.add_node(2, Some(1)); - graph0.add_node(3, Some(2)); - graph0.add_node(4, Some(3)); - graph0.add_edge(1, 2, Some(10)); - graph0.add_edge(3, 4, Some(20)); - - let mut graph1 = DiGraphMap::::new(); - graph1.add_node(3, Some(2)); - graph1.add_node(4, Some(3)); - graph1.add_edge(3, 4, Some(20)); - - let result_graph = graph0 - graph1; - - assert_eq!(result_graph.node_count(), 2); - assert_eq!(result_graph.edge_count(), 1); - - assert_eq!(result_graph.has_node(1), true); - assert_eq!(result_graph.has_node(2), true); - assert_eq!(result_graph.has_node(3), false); - assert_eq!(result_graph.has_node(4), false); - - assert_eq!(result_graph.has_edge(1, 2), true); - assert_eq!(result_graph.has_edge(3, 4), false); - assert_eq!(result_graph.has_edge(2, 1), false); - assert_eq!(result_graph.has_edge(4, 3), false); - assert_eq!(result_graph.has_edge(2, 3), false); - assert_eq!(result_graph.has_edge(1, 4), false); - - assert_eq!(result_graph.get_node_label(1), Some(&0)); - assert_eq!(result_graph.get_node_label(2), Some(&1)); - assert_eq!(result_graph.get_node_label(3), None); - assert_eq!(result_graph.get_node_label(4), None); - - assert_eq!(result_graph.get_edge_label(1, 2), Some(&10)); - assert_eq!(result_graph.get_edge_label(3, 4), None); - assert_eq!(result_graph.get_edge_label(1, 4), None); - assert_eq!(result_graph.get_edge_label(2, 3), None); - assert_eq!(result_graph.get_edge_label(2, 1), None); - assert_eq!(result_graph.get_edge_label(4, 3), None); -} - -#[test] -fn test_graph_sub_undirected_graphs() { - let mut graph0 = UnGraphMap::::new(); - graph0.add_node(1, Some(0)); - graph0.add_node(2, Some(1)); - graph0.add_node(3, Some(2)); - graph0.add_node(4, Some(3)); - graph0.add_edge(1, 2, Some(10)); - graph0.add_edge(3, 4, Some(20)); - - let mut graph1 = UnGraphMap::::new(); - graph1.add_node(3, Some(2)); - graph1.add_node(4, Some(3)); - graph1.add_edge(3, 4, Some(20)); - - let result_graph = graph0 - graph1; - - assert_eq!(result_graph.node_count(), 2); - assert_eq!(result_graph.edge_count(), 1); - - assert_eq!(result_graph.has_node(1), true); - assert_eq!(result_graph.has_node(2), true); - assert_eq!(result_graph.has_node(3), false); - assert_eq!(result_graph.has_node(4), false); - - assert_eq!(result_graph.has_edge(1, 2), true); - assert_eq!(result_graph.has_edge(3, 4), false); - assert_eq!(result_graph.has_edge(2, 1), true); - assert_eq!(result_graph.has_edge(4, 3), false); - assert_eq!(result_graph.has_edge(2, 3), false); - assert_eq!(result_graph.has_edge(1, 4), false); - - assert_eq!(result_graph.get_node_label(1), Some(&0)); - assert_eq!(result_graph.get_node_label(2), Some(&1)); - assert_eq!(result_graph.get_node_label(3), None); - assert_eq!(result_graph.get_node_label(4), None); - - assert_eq!(result_graph.get_edge_label(1, 2), Some(&10)); - assert_eq!(result_graph.get_edge_label(3, 4), None); - assert_eq!(result_graph.get_edge_label(1, 4), None); - assert_eq!(result_graph.get_edge_label(2, 3), None); - assert_eq!(result_graph.get_edge_label(2, 1), Some(&10)); - assert_eq!(result_graph.get_edge_label(4, 3), None); -} - -#[test] -fn test_graph_sub_boxed_directed_generalgraphs() { - let mut graph0 = DiGraphMap::::new(); - graph0.add_node(1, Some(0)); - graph0.add_node(2, Some(1)); - graph0.add_node(3, Some(2)); - graph0.add_node(4, Some(3)); - graph0.add_edge(1, 2, Some(10)); - graph0.add_edge(3, 4, Some(20)); - - let mut graph1 = DiGraphMap::::new(); - graph1.add_node(3, Some(2)); - graph1.add_node(4, Some(3)); - graph1.add_edge(3, 4, Some(20)); - - let box0: Box> = Box::new(graph0); - let box1: Box> = Box::new(graph1); - let result_graph = box0 - box1; - - assert_eq!(result_graph.node_count(), 2); - assert_eq!(result_graph.edge_count(), 1); - - assert_eq!(result_graph.has_node(1), true); - assert_eq!(result_graph.has_node(2), true); - assert_eq!(result_graph.has_node(3), false); - assert_eq!(result_graph.has_node(4), false); - - assert_eq!(result_graph.has_edge(1, 2), true); - assert_eq!(result_graph.has_edge(3, 4), false); - assert_eq!(result_graph.has_edge(2, 1), false); - assert_eq!(result_graph.has_edge(4, 3), false); - assert_eq!(result_graph.has_edge(2, 3), false); - assert_eq!(result_graph.has_edge(1, 4), false); - - assert_eq!(result_graph.get_node_label(1), Some(&0)); - assert_eq!(result_graph.get_node_label(2), Some(&1)); - assert_eq!(result_graph.get_node_label(3), None); - assert_eq!(result_graph.get_node_label(4), None); - - assert_eq!(result_graph.get_edge_label(1, 2), Some(&10)); - assert_eq!(result_graph.get_edge_label(3, 4), None); - assert_eq!(result_graph.get_edge_label(1, 4), None); - assert_eq!(result_graph.get_edge_label(2, 3), None); - assert_eq!(result_graph.get_edge_label(2, 1), None); - assert_eq!(result_graph.get_edge_label(4, 3), None); -} - -#[test] -fn test_graph_sub_boxed_undirected_generalgraphs() { - let mut graph0 = UnGraphMap::::new(); - graph0.add_node(1, Some(0)); - graph0.add_node(2, Some(1)); - graph0.add_node(3, Some(2)); - graph0.add_node(4, Some(3)); - graph0.add_edge(1, 2, Some(10)); - graph0.add_edge(3, 4, Some(20)); - - let mut graph1 = UnGraphMap::::new(); - graph1.add_node(3, Some(2)); - graph1.add_node(4, Some(3)); - graph1.add_edge(3, 4, Some(20)); - - let box0: Box> = Box::new(graph0); - let box1: Box> = Box::new(graph1); - let result_graph = box0 - box1; - - assert_eq!(result_graph.node_count(), 2); - assert_eq!(result_graph.edge_count(), 1); - - assert_eq!(result_graph.has_node(1), true); - assert_eq!(result_graph.has_node(2), true); - assert_eq!(result_graph.has_node(3), false); - assert_eq!(result_graph.has_node(4), false); - - assert_eq!(result_graph.has_edge(1, 2), true); - assert_eq!(result_graph.has_edge(3, 4), false); - assert_eq!(result_graph.has_edge(2, 1), true); - assert_eq!(result_graph.has_edge(4, 3), false); - assert_eq!(result_graph.has_edge(2, 3), false); - assert_eq!(result_graph.has_edge(1, 4), false); - - assert_eq!(result_graph.get_node_label(1), Some(&0)); - assert_eq!(result_graph.get_node_label(2), Some(&1)); - assert_eq!(result_graph.get_node_label(3), None); - assert_eq!(result_graph.get_node_label(4), None); - - assert_eq!(result_graph.get_edge_label(1, 2), Some(&10)); - assert_eq!(result_graph.get_edge_label(3, 4), None); - assert_eq!(result_graph.get_edge_label(1, 4), None); - assert_eq!(result_graph.get_edge_label(2, 3), None); - assert_eq!(result_graph.get_edge_label(2, 1), Some(&10)); - assert_eq!(result_graph.get_edge_label(4, 3), None); -} - -#[test] -fn test_graph_sub_boxed_directed_typedgraphs() { - let mut graph0 = DiGraphMap::::new(); - graph0.add_node(1, Some(0)); - graph0.add_node(2, Some(1)); - graph0.add_node(3, Some(2)); - graph0.add_node(4, Some(3)); - graph0.add_edge(1, 2, Some(10)); - graph0.add_edge(3, 4, Some(20)); - - let mut graph1 = DiGraphMap::::new(); - graph1.add_node(3, Some(2)); - graph1.add_node(4, Some(3)); - graph1.add_edge(3, 4, Some(20)); - - let result_graph = Box::new(graph0) - Box::new(graph1); - - assert_eq!(result_graph.node_count(), 2); - assert_eq!(result_graph.edge_count(), 1); - - assert_eq!(result_graph.has_node(1), true); - assert_eq!(result_graph.has_node(2), true); - assert_eq!(result_graph.has_node(3), false); - assert_eq!(result_graph.has_node(4), false); - - assert_eq!(result_graph.has_edge(1, 2), true); - assert_eq!(result_graph.has_edge(3, 4), false); - assert_eq!(result_graph.has_edge(2, 1), false); - assert_eq!(result_graph.has_edge(4, 3), false); - assert_eq!(result_graph.has_edge(2, 3), false); - assert_eq!(result_graph.has_edge(1, 4), false); - - assert_eq!(result_graph.get_node_label(1), Some(&0)); - assert_eq!(result_graph.get_node_label(2), Some(&1)); - assert_eq!(result_graph.get_node_label(3), None); - assert_eq!(result_graph.get_node_label(4), None); - - assert_eq!(result_graph.get_edge_label(1, 2), Some(&10)); - assert_eq!(result_graph.get_edge_label(3, 4), None); - assert_eq!(result_graph.get_edge_label(1, 4), None); - assert_eq!(result_graph.get_edge_label(2, 3), None); - assert_eq!(result_graph.get_edge_label(2, 1), None); - assert_eq!(result_graph.get_edge_label(4, 3), None); -} - -#[test] -fn test_graph_sub_boxed_undirected_typedgraphs() { - let mut graph0 = UnGraphMap::::new(); - graph0.add_node(1, Some(0)); - graph0.add_node(2, Some(1)); - graph0.add_node(3, Some(2)); - graph0.add_node(4, Some(3)); - graph0.add_edge(1, 2, Some(10)); - graph0.add_edge(3, 4, Some(20)); - - let mut graph1 = UnGraphMap::::new(); - graph1.add_node(3, Some(2)); - graph1.add_node(4, Some(3)); - graph1.add_edge(3, 4, Some(20)); - - let result_graph = Box::new(graph0) - Box::new(graph1); - - assert_eq!(result_graph.node_count(), 2); - assert_eq!(result_graph.edge_count(), 1); - - assert_eq!(result_graph.has_node(1), true); - assert_eq!(result_graph.has_node(2), true); - assert_eq!(result_graph.has_node(3), false); - assert_eq!(result_graph.has_node(4), false); - - assert_eq!(result_graph.has_edge(1, 2), true); - assert_eq!(result_graph.has_edge(3, 4), false); - assert_eq!(result_graph.has_edge(2, 1), true); - assert_eq!(result_graph.has_edge(4, 3), false); - assert_eq!(result_graph.has_edge(2, 3), false); - assert_eq!(result_graph.has_edge(1, 4), false); - - assert_eq!(result_graph.get_node_label(1), Some(&0)); - assert_eq!(result_graph.get_node_label(2), Some(&1)); - assert_eq!(result_graph.get_node_label(3), None); - assert_eq!(result_graph.get_node_label(4), None); - - assert_eq!(result_graph.get_edge_label(1, 2), Some(&10)); - assert_eq!(result_graph.get_edge_label(3, 4), None); - assert_eq!(result_graph.get_edge_label(1, 4), None); - assert_eq!(result_graph.get_edge_label(2, 3), None); - assert_eq!(result_graph.get_edge_label(2, 1), Some(&10)); - assert_eq!(result_graph.get_edge_label(4, 3), None); -} +/* + * Copyright (c) 2018 UNSW Sydney, Data and Knowledge Group. + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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. + */ +extern crate rust_graph; + +use rust_graph::algorithm::{graph_induce, graph_minus, graph_union, Bfs, ConnComp, Dfs}; +use rust_graph::graph_impl::{DiGraphMap, UnGraphMap}; +use rust_graph::prelude::*; + +#[test] +fn test_cc_undirected_one_component() { + let mut graph = UnGraphMap::::new(); + graph.add_edge(1, 2, None); + graph.add_edge(2, 3, None); + + let cc = ConnComp::new(&graph); + + assert_eq!(cc.get_count(), 1); + + assert_eq!(cc.is_connected(1, 2), true); + assert_eq!(cc.is_connected(2, 3), true); + assert_eq!(cc.is_connected(1, 3), true); + + assert_eq!(cc.get_connected_nodes(1).unwrap().len(), 3); + assert_eq!(cc.get_connected_nodes(2).unwrap().len(), 3); + assert_eq!(cc.get_connected_nodes(3).unwrap().len(), 3); +} + +#[test] +fn test_cc_undirected_seperate_components() { + let mut graph = UnGraphMap::::new(); + graph.add_edge(1, 2, Some(5)); + graph.add_edge(3, 4, Some(5)); + graph.add_edge(3, 4, Some(10)); + + let cc = ConnComp::new(&graph); + + assert_eq!(cc.get_count(), 2); + + assert_eq!(cc.is_connected(1, 2), true); + assert_eq!(cc.is_connected(2, 3), false); + assert_eq!(cc.is_connected(1, 3), false); + assert_eq!(cc.is_connected(1, 4), false); + assert_eq!(cc.is_connected(2, 4), false); + assert_eq!(cc.is_connected(3, 4), true); + + assert_eq!(cc.get_connected_nodes(1).unwrap().len(), 2); + assert_eq!(cc.get_connected_nodes(2).unwrap().len(), 2); + assert_eq!(cc.get_connected_nodes(3).unwrap().len(), 2); + assert_eq!(cc.get_connected_nodes(4).unwrap().len(), 2); +} + +#[test] +fn test_cc_directed_one_component() { + let mut graph = DiGraphMap::::new(); + graph.add_edge(1, 2, None); + graph.add_edge(2, 3, None); + + let cc = ConnComp::new(&graph); + + assert_eq!(cc.get_count(), 1); + + assert_eq!(cc.is_connected(1, 2), true); + assert_eq!(cc.is_connected(2, 3), true); + assert_eq!(cc.is_connected(1, 3), true); + + assert_eq!(cc.get_connected_nodes(1).unwrap().len(), 3); + assert_eq!(cc.get_connected_nodes(2).unwrap().len(), 3); + assert_eq!(cc.get_connected_nodes(3).unwrap().len(), 3); +} + +#[test] +fn test_cc_directed_seperate_components() { + let mut graph = DiGraphMap::::new(); + graph.add_edge(1, 2, None); + graph.add_edge(3, 4, None); + + let cc = ConnComp::new(&graph); + + assert_eq!(cc.get_count(), 2); + + assert_eq!(cc.is_connected(1, 2), true); + assert_eq!(cc.is_connected(2, 3), false); + assert_eq!(cc.is_connected(1, 3), false); + assert_eq!(cc.is_connected(1, 4), false); + assert_eq!(cc.is_connected(2, 4), false); + assert_eq!(cc.is_connected(3, 4), true); + + assert_eq!(cc.get_connected_nodes(1).unwrap().len(), 2); + assert_eq!(cc.get_connected_nodes(2).unwrap().len(), 2); + assert_eq!(cc.get_connected_nodes(3).unwrap().len(), 2); + assert_eq!(cc.get_connected_nodes(4).unwrap().len(), 2); +} + +#[test] +fn test_bfs_undirected_one_component() { + let mut graph = UnGraphMap::::new(); + graph.add_edge(1, 2, None); + graph.add_edge(2, 3, None); + + let mut bfs = Bfs::new(&graph, Some(1)); + let x = bfs.next(); + assert_eq!(x, Some(1)); + let x = bfs.next(); + assert_eq!(x, Some(2)); + let x = bfs.next(); + assert_eq!(x, Some(3)); + let x = bfs.next(); + assert_eq!(x, None); +} + +#[test] +fn test_bfs_undirected_radomly_chosen_start() { + let mut graph = UnGraphMap::::new(); + graph.add_edge(1, 2, None); + + let mut bfs = Bfs::new(&graph, None); + let x = bfs.next(); + let result = x == Some(1) || x == Some(2); + assert_eq!(result, true); +} + +#[test] +fn test_bfs_undirected_seperate_components() { + let mut graph = UnGraphMap::::new(); + graph.add_edge(1, 2, None); + graph.add_edge(3, 4, None); + + let mut bfs = Bfs::new(&graph, Some(1)); + let x = bfs.next(); + assert_eq!(x, Some(1)); + let x = bfs.next(); + assert_eq!(x, Some(2)); + let x = bfs.next(); + let result = x == Some(3) || x == Some(4); + assert_eq!(result, true); +} + +#[test] +fn test_bfs_directed_one_component() { + let mut graph = DiGraphMap::::new(); + graph.add_edge(2, 1, None); + graph.add_edge(3, 1, None); + + let mut bfs = Bfs::new(&graph, Some(1)); + let x = bfs.next(); + assert_eq!(x, Some(1)); + let x = bfs.next(); + let result = x == Some(3) || x == Some(2); + assert_eq!(result, true); +} + +#[test] +fn test_bfs_directed_radomly_chosen_start() { + let mut graph = DiGraphMap::::new(); + graph.add_edge(1, 2, None); + + let mut bfs = Bfs::new(&graph, None); + let x = bfs.next(); + let result = x == Some(1) || x == Some(2); + assert_eq!(result, true); +} + +#[test] +fn test_bfs_directed_seperate_components() { + let mut graph = DiGraphMap::::new(); + graph.add_edge(1, 2, None); + graph.add_edge(3, 4, None); + + let mut bfs = Bfs::new(&graph, Some(1)); + let x = bfs.next(); + assert_eq!(x, Some(1)); + let x = bfs.next(); + assert_eq!(x, Some(2)); + let x = bfs.next(); + let result = x == Some(3) || x == Some(4); + assert_eq!(result, true); +} + +#[test] +fn test_dfs_undirected_one_component() { + let mut graph = UnGraphMap::::new(); + graph.add_edge(1, 2, None); + graph.add_edge(2, 3, None); + + let mut dfs = Dfs::new(&graph, Some(1)); + let x = dfs.next(); + assert_eq!(x, Some(1)); + let x = dfs.next(); + assert_eq!(x, Some(2)); + let x = dfs.next(); + assert_eq!(x, Some(3)); + let x = dfs.next(); + assert_eq!(x, None); +} + +#[test] +fn test_dfs_undirected_radomly_chosen_start() { + let mut graph = UnGraphMap::::new(); + graph.add_edge(1, 2, None); + + let mut dfs = Dfs::new(&graph, None); + let x = dfs.next(); + let result = x == Some(1) || x == Some(2); + assert_eq!(result, true); +} + +#[test] +fn test_dfs_undirected_seperate_components() { + let mut graph = UnGraphMap::::new(); + graph.add_edge(1, 2, None); + graph.add_edge(3, 4, None); + + let mut dfs = Dfs::new(&graph, Some(1)); + let x = dfs.next(); + assert_eq!(x, Some(1)); + let x = dfs.next(); + assert_eq!(x, Some(2)); + let x = dfs.next(); + let result = x == Some(3) || x == Some(4); + assert_eq!(result, true); +} + +#[test] +fn test_dfs_directed_one_component() { + let mut graph = DiGraphMap::::new(); + graph.add_edge(2, 1, None); + graph.add_edge(3, 1, None); + + let mut dfs = Dfs::new(&graph, Some(1)); + let x = dfs.next(); + assert_eq!(x, Some(1)); + let x = dfs.next(); + let result = x == Some(3) || x == Some(2); + assert_eq!(result, true); +} + +#[test] +fn test_dfs_directed_radomly_chosen_start() { + let mut graph = DiGraphMap::::new(); + graph.add_edge(1, 2, None); + + let mut dfs = Dfs::new(&graph, None); + let x = dfs.next(); + let result = x == Some(1) || x == Some(2); + assert_eq!(result, true); +} + +#[test] +fn test_dfs_directed_seperate_components() { + let mut graph = DiGraphMap::::new(); + graph.add_edge(1, 2, None); + graph.add_edge(3, 4, None); + + let mut dfs = Dfs::new(&graph, Some(1)); + let x = dfs.next(); + assert_eq!(x, Some(1)); + let x = dfs.next(); + assert_eq!(x, Some(2)); + let x = dfs.next(); + let result = x == Some(3) || x == Some(4); + assert_eq!(result, true); +} + +#[test] +fn test_graph_union_directed_graphs() { + let mut graph0 = DiGraphMap::::new(); + graph0.add_node(1, Some(0)); + graph0.add_node(2, Some(1)); + graph0.add_edge(1, 2, Some(10)); + + let mut graph1 = DiGraphMap::::new(); + graph1.add_node(3, Some(2)); + graph1.add_node(4, Some(3)); + graph1.add_edge(3, 4, Some(20)); + + let result_graph = graph_union(&graph0, &graph1); + + assert_eq!(result_graph.node_count(), 4); + assert_eq!(result_graph.edge_count(), 2); + + assert_eq!(result_graph.has_node(1), true); + assert_eq!(result_graph.has_node(2), true); + assert_eq!(result_graph.has_node(3), true); + assert_eq!(result_graph.has_node(4), true); + + assert_eq!(result_graph.has_edge(1, 2), true); + assert_eq!(result_graph.has_edge(3, 4), true); + assert_eq!(result_graph.has_edge(2, 1), false); + assert_eq!(result_graph.has_edge(4, 3), false); + assert_eq!(result_graph.has_edge(2, 3), false); + assert_eq!(result_graph.has_edge(1, 4), false); + + assert_eq!(result_graph.get_node_label(1), Some(&0)); + assert_eq!(result_graph.get_node_label(2), Some(&1)); + assert_eq!(result_graph.get_node_label(3), Some(&2)); + assert_eq!(result_graph.get_node_label(4), Some(&3)); + + assert_eq!(result_graph.get_edge_label(1, 2), Some(&10)); + assert_eq!(result_graph.get_edge_label(3, 4), Some(&20)); + assert_eq!(result_graph.get_edge_label(1, 4), None); + assert_eq!(result_graph.get_edge_label(2, 3), None); + assert_eq!(result_graph.get_edge_label(2, 1), None); + assert_eq!(result_graph.get_edge_label(4, 3), None); +} + +#[test] +fn test_graph_union_undirected_graphs() { + let mut graph0 = UnGraphMap::::new(); + graph0.add_node(1, Some(0)); + graph0.add_node(2, Some(1)); + graph0.add_edge(1, 2, Some(10)); + + let mut graph1 = UnGraphMap::::new(); + graph1.add_node(3, Some(2)); + graph1.add_node(4, Some(3)); + graph1.add_edge(3, 4, Some(20)); + + let result_graph = graph_union(&graph0, &graph1); + + assert_eq!(result_graph.node_count(), 4); + assert_eq!(result_graph.edge_count(), 2); + + assert_eq!(result_graph.has_node(1), true); + assert_eq!(result_graph.has_node(2), true); + assert_eq!(result_graph.has_node(3), true); + assert_eq!(result_graph.has_node(4), true); + + assert_eq!(result_graph.has_edge(1, 2), true); + assert_eq!(result_graph.has_edge(3, 4), true); + assert_eq!(result_graph.has_edge(2, 1), true); + assert_eq!(result_graph.has_edge(4, 3), true); + assert_eq!(result_graph.has_edge(2, 3), false); + assert_eq!(result_graph.has_edge(1, 4), false); + + assert_eq!(result_graph.get_node_label(1), Some(&0)); + assert_eq!(result_graph.get_node_label(2), Some(&1)); + assert_eq!(result_graph.get_node_label(3), Some(&2)); + assert_eq!(result_graph.get_node_label(4), Some(&3)); + + assert_eq!(result_graph.get_edge_label(1, 2), Some(&10)); + assert_eq!(result_graph.get_edge_label(3, 4), Some(&20)); + assert_eq!(result_graph.get_edge_label(1, 4), None); + assert_eq!(result_graph.get_edge_label(2, 3), None); + assert_eq!(result_graph.get_edge_label(2, 1), Some(&10)); + assert_eq!(result_graph.get_edge_label(4, 3), Some(&20)); +} + +#[test] +fn test_graph_add_directed_graphs() { + let mut graph0 = DiGraphMap::::new(); + graph0.add_node(1, Some(0)); + graph0.add_node(2, Some(1)); + graph0.add_edge(1, 2, Some(10)); + + let mut graph1 = DiGraphMap::::new(); + graph1.add_node(3, Some(2)); + graph1.add_node(4, Some(3)); + graph1.add_edge(3, 4, Some(20)); + + let result_graph = graph0.as_general_graph() + graph1.as_general_graph(); + + assert_eq!(result_graph.node_count(), 4); + assert_eq!(result_graph.edge_count(), 2); + + assert_eq!(result_graph.has_node(1), true); + assert_eq!(result_graph.has_node(2), true); + assert_eq!(result_graph.has_node(3), true); + assert_eq!(result_graph.has_node(4), true); + + assert_eq!(result_graph.has_edge(1, 2), true); + assert_eq!(result_graph.has_edge(3, 4), true); + assert_eq!(result_graph.has_edge(2, 1), false); + assert_eq!(result_graph.has_edge(4, 3), false); + assert_eq!(result_graph.has_edge(2, 3), false); + assert_eq!(result_graph.has_edge(1, 4), false); + + assert_eq!(result_graph.get_node_label(1), Some(&0)); + assert_eq!(result_graph.get_node_label(2), Some(&1)); + assert_eq!(result_graph.get_node_label(3), Some(&2)); + assert_eq!(result_graph.get_node_label(4), Some(&3)); + + assert_eq!(result_graph.get_edge_label(1, 2), Some(&10)); + assert_eq!(result_graph.get_edge_label(3, 4), Some(&20)); + assert_eq!(result_graph.get_edge_label(1, 4), None); + assert_eq!(result_graph.get_edge_label(2, 3), None); + assert_eq!(result_graph.get_edge_label(2, 1), None); + assert_eq!(result_graph.get_edge_label(4, 3), None); +} + +#[test] +fn test_graph_add_undirected_graphs() { + let mut graph0 = UnGraphMap::::new(); + graph0.add_node(1, Some(0)); + graph0.add_node(2, Some(1)); + graph0.add_edge(1, 2, Some(10)); + + let mut graph1 = UnGraphMap::::new(); + graph1.add_node(3, Some(2)); + graph1.add_node(4, Some(3)); + graph1.add_edge(3, 4, Some(20)); + + let box0: Box> = Box::new(graph0); + let box1: Box> = Box::new(graph1); + let result_graph = box0 + box1; + + assert_eq!(result_graph.node_count(), 4); + assert_eq!(result_graph.edge_count(), 2); + + assert_eq!(result_graph.has_node(1), true); + assert_eq!(result_graph.has_node(2), true); + assert_eq!(result_graph.has_node(3), true); + assert_eq!(result_graph.has_node(4), true); + + assert_eq!(result_graph.has_edge(1, 2), true); + assert_eq!(result_graph.has_edge(3, 4), true); + assert_eq!(result_graph.has_edge(2, 1), true); + assert_eq!(result_graph.has_edge(4, 3), true); + assert_eq!(result_graph.has_edge(2, 3), false); + assert_eq!(result_graph.has_edge(1, 4), false); + + assert_eq!(result_graph.get_node_label(1), Some(&0)); + assert_eq!(result_graph.get_node_label(2), Some(&1)); + assert_eq!(result_graph.get_node_label(3), Some(&2)); + assert_eq!(result_graph.get_node_label(4), Some(&3)); + + assert_eq!(result_graph.get_edge_label(1, 2), Some(&10)); + assert_eq!(result_graph.get_edge_label(3, 4), Some(&20)); + assert_eq!(result_graph.get_edge_label(1, 4), None); + assert_eq!(result_graph.get_edge_label(2, 3), None); + assert_eq!(result_graph.get_edge_label(2, 1), Some(&10)); + assert_eq!(result_graph.get_edge_label(4, 3), Some(&20)); +} + +#[test] +fn test_graph_add_boxed_directed_generalgraphs() { + let mut graph0 = DiGraphMap::::new(); + graph0.add_node(1, Some(0)); + graph0.add_node(2, Some(1)); + graph0.add_edge(1, 2, Some(10)); + + let mut graph1 = DiGraphMap::::new(); + graph1.add_node(3, Some(2)); + graph1.add_node(4, Some(3)); + graph1.add_edge(3, 4, Some(20)); + + let box0: Box> = Box::new(graph0); + let box1: Box> = Box::new(graph1); + let result_graph = box0 + box1; + + assert_eq!(result_graph.node_count(), 4); + assert_eq!(result_graph.edge_count(), 2); + + assert_eq!(result_graph.has_node(1), true); + assert_eq!(result_graph.has_node(2), true); + assert_eq!(result_graph.has_node(3), true); + assert_eq!(result_graph.has_node(4), true); + + assert_eq!(result_graph.has_edge(1, 2), true); + assert_eq!(result_graph.has_edge(3, 4), true); + assert_eq!(result_graph.has_edge(2, 1), false); + assert_eq!(result_graph.has_edge(4, 3), false); + assert_eq!(result_graph.has_edge(2, 3), false); + assert_eq!(result_graph.has_edge(1, 4), false); + + assert_eq!(result_graph.get_node_label(1), Some(&0)); + assert_eq!(result_graph.get_node_label(2), Some(&1)); + assert_eq!(result_graph.get_node_label(3), Some(&2)); + assert_eq!(result_graph.get_node_label(4), Some(&3)); + + assert_eq!(result_graph.get_edge_label(1, 2), Some(&10)); + assert_eq!(result_graph.get_edge_label(3, 4), Some(&20)); + assert_eq!(result_graph.get_edge_label(1, 4), None); + assert_eq!(result_graph.get_edge_label(2, 3), None); + assert_eq!(result_graph.get_edge_label(2, 1), None); + assert_eq!(result_graph.get_edge_label(4, 3), None); +} + +#[test] +fn test_graph_add_boxed_undirected_generalgraphs() { + let mut graph0 = UnGraphMap::::new(); + graph0.add_node(1, Some(0)); + graph0.add_node(2, Some(1)); + graph0.add_edge(1, 2, Some(10)); + + let mut graph1 = UnGraphMap::::new(); + graph1.add_node(3, Some(2)); + graph1.add_node(4, Some(3)); + graph1.add_edge(3, 4, Some(20)); + + let box0: Box> = Box::new(graph0); + let box1: Box> = Box::new(graph1); + let result_graph = box0 + box1; + + assert_eq!(result_graph.node_count(), 4); + assert_eq!(result_graph.edge_count(), 2); + + assert_eq!(result_graph.has_node(1), true); + assert_eq!(result_graph.has_node(2), true); + assert_eq!(result_graph.has_node(3), true); + assert_eq!(result_graph.has_node(4), true); + + assert_eq!(result_graph.has_edge(1, 2), true); + assert_eq!(result_graph.has_edge(3, 4), true); + assert_eq!(result_graph.has_edge(2, 1), true); + assert_eq!(result_graph.has_edge(4, 3), true); + assert_eq!(result_graph.has_edge(2, 3), false); + assert_eq!(result_graph.has_edge(1, 4), false); + + assert_eq!(result_graph.get_node_label(1), Some(&0)); + assert_eq!(result_graph.get_node_label(2), Some(&1)); + assert_eq!(result_graph.get_node_label(3), Some(&2)); + assert_eq!(result_graph.get_node_label(4), Some(&3)); + + assert_eq!(result_graph.get_edge_label(1, 2), Some(&10)); + assert_eq!(result_graph.get_edge_label(3, 4), Some(&20)); + assert_eq!(result_graph.get_edge_label(1, 4), None); + assert_eq!(result_graph.get_edge_label(2, 3), None); + assert_eq!(result_graph.get_edge_label(2, 1), Some(&10)); + assert_eq!(result_graph.get_edge_label(4, 3), Some(&20)); +} + +#[test] +fn test_graph_minus_directed_boxed_typedgraphs() { + let mut graph0 = DiGraphMap::::new(); + graph0.add_node(1, Some(0)); + graph0.add_node(2, Some(1)); + graph0.add_node(3, Some(2)); + graph0.add_node(4, Some(3)); + graph0.add_edge(1, 2, Some(10)); + graph0.add_edge(3, 4, Some(20)); + + let mut graph1 = DiGraphMap::::new(); + graph1.add_node(3, Some(2)); + graph1.add_node(4, Some(3)); + graph1.add_edge(3, 4, Some(20)); + + let result_graph = Box::new(graph0) - Box::new(graph1); + + assert_eq!(result_graph.node_count(), 2); + assert_eq!(result_graph.edge_count(), 1); + + assert_eq!(result_graph.has_node(1), true); + assert_eq!(result_graph.has_node(2), true); + assert_eq!(result_graph.has_node(3), false); + assert_eq!(result_graph.has_node(4), false); + + assert_eq!(result_graph.has_edge(1, 2), true); + assert_eq!(result_graph.has_edge(3, 4), false); + assert_eq!(result_graph.has_edge(2, 1), false); + assert_eq!(result_graph.has_edge(4, 3), false); + assert_eq!(result_graph.has_edge(2, 3), false); + assert_eq!(result_graph.has_edge(1, 4), false); + + assert_eq!(result_graph.get_node_label(1), Some(&0)); + assert_eq!(result_graph.get_node_label(2), Some(&1)); + assert_eq!(result_graph.get_node_label(3), None); + assert_eq!(result_graph.get_node_label(4), None); + + assert_eq!(result_graph.get_edge_label(1, 2), Some(&10)); + assert_eq!(result_graph.get_edge_label(3, 4), None); + assert_eq!(result_graph.get_edge_label(1, 4), None); + assert_eq!(result_graph.get_edge_label(2, 3), None); + assert_eq!(result_graph.get_edge_label(2, 1), None); + assert_eq!(result_graph.get_edge_label(4, 3), None); +} + +#[test] +fn test_graph_add_directed_typedgraphs() { + let mut graph0 = DiGraphMap::::new(); + graph0.add_node(1, Some(0)); + graph0.add_node(2, Some(1)); + graph0.add_edge(1, 2, Some(10)); + + let mut graph1 = DiGraphMap::::new(); + graph1.add_node(3, Some(2)); + graph1.add_node(4, Some(3)); + graph1.add_edge(3, 4, Some(20)); + + let result_graph = graph0 + graph1; + + assert_eq!(result_graph.node_count(), 4); + assert_eq!(result_graph.edge_count(), 2); + + assert_eq!(result_graph.has_node(1), true); + assert_eq!(result_graph.has_node(2), true); + assert_eq!(result_graph.has_node(3), true); + assert_eq!(result_graph.has_node(4), true); + + assert_eq!(result_graph.has_edge(1, 2), true); + assert_eq!(result_graph.has_edge(3, 4), true); + assert_eq!(result_graph.has_edge(2, 1), false); + assert_eq!(result_graph.has_edge(4, 3), false); + assert_eq!(result_graph.has_edge(2, 3), false); + assert_eq!(result_graph.has_edge(1, 4), false); + + assert_eq!(result_graph.get_node_label(1), Some(&0)); + assert_eq!(result_graph.get_node_label(2), Some(&1)); + assert_eq!(result_graph.get_node_label(3), Some(&2)); + assert_eq!(result_graph.get_node_label(4), Some(&3)); + + assert_eq!(result_graph.get_edge_label(1, 2), Some(&10)); + assert_eq!(result_graph.get_edge_label(3, 4), Some(&20)); + assert_eq!(result_graph.get_edge_label(1, 4), None); + assert_eq!(result_graph.get_edge_label(2, 3), None); + assert_eq!(result_graph.get_edge_label(2, 1), None); + assert_eq!(result_graph.get_edge_label(4, 3), None); +} + +#[test] +fn test_graph_add_undirected_typedgraphs() { + let mut graph0 = UnGraphMap::::new(); + graph0.add_node(1, Some(0)); + graph0.add_node(2, Some(1)); + graph0.add_edge(1, 2, Some(10)); + + let mut graph1 = UnGraphMap::::new(); + graph1.add_node(3, Some(2)); + graph1.add_node(4, Some(3)); + graph1.add_edge(3, 4, Some(20)); + + let result_graph = graph0 + graph1; + + assert_eq!(result_graph.node_count(), 4); + assert_eq!(result_graph.edge_count(), 2); + + assert_eq!(result_graph.has_node(1), true); + assert_eq!(result_graph.has_node(2), true); + assert_eq!(result_graph.has_node(3), true); + assert_eq!(result_graph.has_node(4), true); + + assert_eq!(result_graph.has_edge(1, 2), true); + assert_eq!(result_graph.has_edge(3, 4), true); + assert_eq!(result_graph.has_edge(2, 1), true); + assert_eq!(result_graph.has_edge(4, 3), true); + assert_eq!(result_graph.has_edge(2, 3), false); + assert_eq!(result_graph.has_edge(1, 4), false); + + assert_eq!(result_graph.get_node_label(1), Some(&0)); + assert_eq!(result_graph.get_node_label(2), Some(&1)); + assert_eq!(result_graph.get_node_label(3), Some(&2)); + assert_eq!(result_graph.get_node_label(4), Some(&3)); + + assert_eq!(result_graph.get_edge_label(1, 2), Some(&10)); + assert_eq!(result_graph.get_edge_label(3, 4), Some(&20)); + assert_eq!(result_graph.get_edge_label(1, 4), None); + assert_eq!(result_graph.get_edge_label(2, 3), None); + assert_eq!(result_graph.get_edge_label(2, 1), Some(&10)); + assert_eq!(result_graph.get_edge_label(4, 3), Some(&20)); +} + +#[test] +fn test_graph_minus_directed_graphs() { + let mut graph0 = DiGraphMap::::new(); + graph0.add_node(1, Some(0)); + graph0.add_node(2, Some(1)); + graph0.add_node(3, Some(2)); + graph0.add_node(4, Some(3)); + graph0.add_edge(1, 2, Some(10)); + graph0.add_edge(3, 4, Some(20)); + + let mut graph1 = DiGraphMap::::new(); + graph1.add_node(3, Some(2)); + graph1.add_node(4, Some(3)); + graph1.add_edge(3, 4, Some(20)); + + let result_graph = graph_minus(&graph0, &graph1); + + assert_eq!(result_graph.node_count(), 2); + assert_eq!(result_graph.edge_count(), 1); + + assert_eq!(result_graph.has_node(1), true); + assert_eq!(result_graph.has_node(2), true); + assert_eq!(result_graph.has_node(3), false); + assert_eq!(result_graph.has_node(4), false); + + assert_eq!(result_graph.has_edge(1, 2), true); + assert_eq!(result_graph.has_edge(3, 4), false); + assert_eq!(result_graph.has_edge(2, 1), false); + assert_eq!(result_graph.has_edge(4, 3), false); + assert_eq!(result_graph.has_edge(2, 3), false); + assert_eq!(result_graph.has_edge(1, 4), false); + + assert_eq!(result_graph.get_node_label(1), Some(&0)); + assert_eq!(result_graph.get_node_label(2), Some(&1)); + assert_eq!(result_graph.get_node_label(3), None); + assert_eq!(result_graph.get_node_label(4), None); + + assert_eq!(result_graph.get_edge_label(1, 2), Some(&10)); + assert_eq!(result_graph.get_edge_label(3, 4), None); + assert_eq!(result_graph.get_edge_label(1, 4), None); + assert_eq!(result_graph.get_edge_label(2, 3), None); + assert_eq!(result_graph.get_edge_label(2, 1), None); + assert_eq!(result_graph.get_edge_label(4, 3), None); +} + +#[test] +fn test_graph_minus_undirected_graphs() { + let mut graph0 = UnGraphMap::::new(); + graph0.add_node(1, Some(0)); + graph0.add_node(2, Some(1)); + graph0.add_node(3, Some(2)); + graph0.add_node(4, Some(3)); + graph0.add_edge(1, 2, Some(10)); + graph0.add_edge(3, 4, Some(20)); + + let mut graph1 = UnGraphMap::::new(); + graph1.add_node(3, Some(2)); + graph1.add_node(4, Some(3)); + graph1.add_edge(3, 4, Some(20)); + + let result_graph = graph_minus(&graph0, &graph1); + + assert_eq!(result_graph.node_count(), 2); + assert_eq!(result_graph.edge_count(), 1); + + assert_eq!(result_graph.has_node(1), true); + assert_eq!(result_graph.has_node(2), true); + assert_eq!(result_graph.has_node(3), false); + assert_eq!(result_graph.has_node(4), false); + + assert_eq!(result_graph.has_edge(1, 2), true); + assert_eq!(result_graph.has_edge(3, 4), false); + assert_eq!(result_graph.has_edge(2, 1), true); + assert_eq!(result_graph.has_edge(4, 3), false); + assert_eq!(result_graph.has_edge(2, 3), false); + assert_eq!(result_graph.has_edge(1, 4), false); + + assert_eq!(result_graph.get_node_label(1), Some(&0)); + assert_eq!(result_graph.get_node_label(2), Some(&1)); + assert_eq!(result_graph.get_node_label(3), None); + assert_eq!(result_graph.get_node_label(4), None); + + assert_eq!(result_graph.get_edge_label(1, 2), Some(&10)); + assert_eq!(result_graph.get_edge_label(3, 4), None); + assert_eq!(result_graph.get_edge_label(1, 4), None); + assert_eq!(result_graph.get_edge_label(2, 3), None); + assert_eq!(result_graph.get_edge_label(2, 1), Some(&10)); + assert_eq!(result_graph.get_edge_label(4, 3), None); +} + +#[test] +fn test_graph_sub_directed_graphs() { + let mut graph0 = DiGraphMap::::new(); + graph0.add_node(1, Some(0)); + graph0.add_node(2, Some(1)); + graph0.add_node(3, Some(2)); + graph0.add_node(4, Some(3)); + graph0.add_edge(1, 2, Some(10)); + graph0.add_edge(3, 4, Some(20)); + + let mut graph1 = DiGraphMap::::new(); + graph1.add_node(3, Some(2)); + graph1.add_node(4, Some(3)); + graph1.add_edge(3, 4, Some(20)); + + let result_graph = graph0 - graph1; + + assert_eq!(result_graph.node_count(), 2); + assert_eq!(result_graph.edge_count(), 1); + + assert_eq!(result_graph.has_node(1), true); + assert_eq!(result_graph.has_node(2), true); + assert_eq!(result_graph.has_node(3), false); + assert_eq!(result_graph.has_node(4), false); + + assert_eq!(result_graph.has_edge(1, 2), true); + assert_eq!(result_graph.has_edge(3, 4), false); + assert_eq!(result_graph.has_edge(2, 1), false); + assert_eq!(result_graph.has_edge(4, 3), false); + assert_eq!(result_graph.has_edge(2, 3), false); + assert_eq!(result_graph.has_edge(1, 4), false); + + assert_eq!(result_graph.get_node_label(1), Some(&0)); + assert_eq!(result_graph.get_node_label(2), Some(&1)); + assert_eq!(result_graph.get_node_label(3), None); + assert_eq!(result_graph.get_node_label(4), None); + + assert_eq!(result_graph.get_edge_label(1, 2), Some(&10)); + assert_eq!(result_graph.get_edge_label(3, 4), None); + assert_eq!(result_graph.get_edge_label(1, 4), None); + assert_eq!(result_graph.get_edge_label(2, 3), None); + assert_eq!(result_graph.get_edge_label(2, 1), None); + assert_eq!(result_graph.get_edge_label(4, 3), None); +} + +#[test] +fn test_graph_sub_undirected_graphs() { + let mut graph0 = UnGraphMap::::new(); + graph0.add_node(1, Some(0)); + graph0.add_node(2, Some(1)); + graph0.add_node(3, Some(2)); + graph0.add_node(4, Some(3)); + graph0.add_edge(1, 2, Some(10)); + graph0.add_edge(3, 4, Some(20)); + + let mut graph1 = UnGraphMap::::new(); + graph1.add_node(3, Some(2)); + graph1.add_node(4, Some(3)); + graph1.add_edge(3, 4, Some(20)); + + let result_graph = graph0 - graph1; + + assert_eq!(result_graph.node_count(), 2); + assert_eq!(result_graph.edge_count(), 1); + + assert_eq!(result_graph.has_node(1), true); + assert_eq!(result_graph.has_node(2), true); + assert_eq!(result_graph.has_node(3), false); + assert_eq!(result_graph.has_node(4), false); + + assert_eq!(result_graph.has_edge(1, 2), true); + assert_eq!(result_graph.has_edge(3, 4), false); + assert_eq!(result_graph.has_edge(2, 1), true); + assert_eq!(result_graph.has_edge(4, 3), false); + assert_eq!(result_graph.has_edge(2, 3), false); + assert_eq!(result_graph.has_edge(1, 4), false); + + assert_eq!(result_graph.get_node_label(1), Some(&0)); + assert_eq!(result_graph.get_node_label(2), Some(&1)); + assert_eq!(result_graph.get_node_label(3), None); + assert_eq!(result_graph.get_node_label(4), None); + + assert_eq!(result_graph.get_edge_label(1, 2), Some(&10)); + assert_eq!(result_graph.get_edge_label(3, 4), None); + assert_eq!(result_graph.get_edge_label(1, 4), None); + assert_eq!(result_graph.get_edge_label(2, 3), None); + assert_eq!(result_graph.get_edge_label(2, 1), Some(&10)); + assert_eq!(result_graph.get_edge_label(4, 3), None); +} + +#[test] +fn test_graph_sub_boxed_directed_generalgraphs() { + let mut graph0 = DiGraphMap::::new(); + graph0.add_node(1, Some(0)); + graph0.add_node(2, Some(1)); + graph0.add_node(3, Some(2)); + graph0.add_node(4, Some(3)); + graph0.add_edge(1, 2, Some(10)); + graph0.add_edge(3, 4, Some(20)); + + let mut graph1 = DiGraphMap::::new(); + graph1.add_node(3, Some(2)); + graph1.add_node(4, Some(3)); + graph1.add_edge(3, 4, Some(20)); + + let box0: Box> = Box::new(graph0); + let box1: Box> = Box::new(graph1); + let result_graph = box0 - box1; + + assert_eq!(result_graph.node_count(), 2); + assert_eq!(result_graph.edge_count(), 1); + + assert_eq!(result_graph.has_node(1), true); + assert_eq!(result_graph.has_node(2), true); + assert_eq!(result_graph.has_node(3), false); + assert_eq!(result_graph.has_node(4), false); + + assert_eq!(result_graph.has_edge(1, 2), true); + assert_eq!(result_graph.has_edge(3, 4), false); + assert_eq!(result_graph.has_edge(2, 1), false); + assert_eq!(result_graph.has_edge(4, 3), false); + assert_eq!(result_graph.has_edge(2, 3), false); + assert_eq!(result_graph.has_edge(1, 4), false); + + assert_eq!(result_graph.get_node_label(1), Some(&0)); + assert_eq!(result_graph.get_node_label(2), Some(&1)); + assert_eq!(result_graph.get_node_label(3), None); + assert_eq!(result_graph.get_node_label(4), None); + + assert_eq!(result_graph.get_edge_label(1, 2), Some(&10)); + assert_eq!(result_graph.get_edge_label(3, 4), None); + assert_eq!(result_graph.get_edge_label(1, 4), None); + assert_eq!(result_graph.get_edge_label(2, 3), None); + assert_eq!(result_graph.get_edge_label(2, 1), None); + assert_eq!(result_graph.get_edge_label(4, 3), None); +} + +#[test] +fn test_graph_sub_boxed_undirected_generalgraphs() { + let mut graph0 = UnGraphMap::::new(); + graph0.add_node(1, Some(0)); + graph0.add_node(2, Some(1)); + graph0.add_node(3, Some(2)); + graph0.add_node(4, Some(3)); + graph0.add_edge(1, 2, Some(10)); + graph0.add_edge(3, 4, Some(20)); + + let mut graph1 = UnGraphMap::::new(); + graph1.add_node(3, Some(2)); + graph1.add_node(4, Some(3)); + graph1.add_edge(3, 4, Some(20)); + + let box0: Box> = Box::new(graph0); + let box1: Box> = Box::new(graph1); + let result_graph = box0 - box1; + + assert_eq!(result_graph.node_count(), 2); + assert_eq!(result_graph.edge_count(), 1); + + assert_eq!(result_graph.has_node(1), true); + assert_eq!(result_graph.has_node(2), true); + assert_eq!(result_graph.has_node(3), false); + assert_eq!(result_graph.has_node(4), false); + + assert_eq!(result_graph.has_edge(1, 2), true); + assert_eq!(result_graph.has_edge(3, 4), false); + assert_eq!(result_graph.has_edge(2, 1), true); + assert_eq!(result_graph.has_edge(4, 3), false); + assert_eq!(result_graph.has_edge(2, 3), false); + assert_eq!(result_graph.has_edge(1, 4), false); + + assert_eq!(result_graph.get_node_label(1), Some(&0)); + assert_eq!(result_graph.get_node_label(2), Some(&1)); + assert_eq!(result_graph.get_node_label(3), None); + assert_eq!(result_graph.get_node_label(4), None); + + assert_eq!(result_graph.get_edge_label(1, 2), Some(&10)); + assert_eq!(result_graph.get_edge_label(3, 4), None); + assert_eq!(result_graph.get_edge_label(1, 4), None); + assert_eq!(result_graph.get_edge_label(2, 3), None); + assert_eq!(result_graph.get_edge_label(2, 1), Some(&10)); + assert_eq!(result_graph.get_edge_label(4, 3), None); +} + +#[test] +fn test_graph_sub_boxed_directed_typedgraphs() { + let mut graph0 = DiGraphMap::::new(); + graph0.add_node(1, Some(0)); + graph0.add_node(2, Some(1)); + graph0.add_node(3, Some(2)); + graph0.add_node(4, Some(3)); + graph0.add_edge(1, 2, Some(10)); + graph0.add_edge(3, 4, Some(20)); + + let mut graph1 = DiGraphMap::::new(); + graph1.add_node(3, Some(2)); + graph1.add_node(4, Some(3)); + graph1.add_edge(3, 4, Some(20)); + + let result_graph = Box::new(graph0) - Box::new(graph1); + + assert_eq!(result_graph.node_count(), 2); + assert_eq!(result_graph.edge_count(), 1); + + assert_eq!(result_graph.has_node(1), true); + assert_eq!(result_graph.has_node(2), true); + assert_eq!(result_graph.has_node(3), false); + assert_eq!(result_graph.has_node(4), false); + + assert_eq!(result_graph.has_edge(1, 2), true); + assert_eq!(result_graph.has_edge(3, 4), false); + assert_eq!(result_graph.has_edge(2, 1), false); + assert_eq!(result_graph.has_edge(4, 3), false); + assert_eq!(result_graph.has_edge(2, 3), false); + assert_eq!(result_graph.has_edge(1, 4), false); + + assert_eq!(result_graph.get_node_label(1), Some(&0)); + assert_eq!(result_graph.get_node_label(2), Some(&1)); + assert_eq!(result_graph.get_node_label(3), None); + assert_eq!(result_graph.get_node_label(4), None); + + assert_eq!(result_graph.get_edge_label(1, 2), Some(&10)); + assert_eq!(result_graph.get_edge_label(3, 4), None); + assert_eq!(result_graph.get_edge_label(1, 4), None); + assert_eq!(result_graph.get_edge_label(2, 3), None); + assert_eq!(result_graph.get_edge_label(2, 1), None); + assert_eq!(result_graph.get_edge_label(4, 3), None); +} + +#[test] +fn test_graph_sub_boxed_undirected_typedgraphs() { + let mut graph0 = UnGraphMap::::new(); + graph0.add_node(1, Some(0)); + graph0.add_node(2, Some(1)); + graph0.add_node(3, Some(2)); + graph0.add_node(4, Some(3)); + graph0.add_edge(1, 2, Some(10)); + graph0.add_edge(3, 4, Some(20)); + + let mut graph1 = UnGraphMap::::new(); + graph1.add_node(3, Some(2)); + graph1.add_node(4, Some(3)); + graph1.add_edge(3, 4, Some(20)); + + let result_graph = Box::new(graph0) - Box::new(graph1); + + assert_eq!(result_graph.node_count(), 2); + assert_eq!(result_graph.edge_count(), 1); + + assert_eq!(result_graph.has_node(1), true); + assert_eq!(result_graph.has_node(2), true); + assert_eq!(result_graph.has_node(3), false); + assert_eq!(result_graph.has_node(4), false); + + assert_eq!(result_graph.has_edge(1, 2), true); + assert_eq!(result_graph.has_edge(3, 4), false); + assert_eq!(result_graph.has_edge(2, 1), true); + assert_eq!(result_graph.has_edge(4, 3), false); + assert_eq!(result_graph.has_edge(2, 3), false); + assert_eq!(result_graph.has_edge(1, 4), false); + + assert_eq!(result_graph.get_node_label(1), Some(&0)); + assert_eq!(result_graph.get_node_label(2), Some(&1)); + assert_eq!(result_graph.get_node_label(3), None); + assert_eq!(result_graph.get_node_label(4), None); + + assert_eq!(result_graph.get_edge_label(1, 2), Some(&10)); + assert_eq!(result_graph.get_edge_label(3, 4), None); + assert_eq!(result_graph.get_edge_label(1, 4), None); + assert_eq!(result_graph.get_edge_label(2, 3), None); + assert_eq!(result_graph.get_edge_label(2, 1), Some(&10)); + assert_eq!(result_graph.get_edge_label(4, 3), None); +} + +#[test] +fn test_graph_induce_undirected_graphs() { + let mut graph0 = UnGraphMap::::new(); + graph0.add_node(1, Some(1)); + graph0.add_node(2, Some(2)); + graph0.add_node(3, Some(3)); + graph0.add_node(4, Some(4)); + graph0.add_edge(1, 2, Some(12)); + graph0.add_edge(2, 3, Some(23)); + graph0.add_edge(3, 4, Some(34)); + graph0.add_edge(1, 4, Some(14)); + graph0.add_edge(1, 3, Some(13)); + + let mut graph1 = UnGraphMap::::new(); + graph1.add_node(1, Some(1)); + graph1.add_node(2, Some(2)); + graph1.add_node(3, Some(3)); + graph1.add_edge(1, 2, Some(12)); + graph1.add_edge(2, 3, Some(23)); + graph1.add_edge(2, 3, Some(23)); + graph1.add_edge(2, 3, Some(23)); + graph1.add_edge(2, 3, Some(23)); + + let result_graph = graph_induce(&graph0, &graph1); + + assert_eq!(result_graph.node_count(), 3); + assert_eq!(result_graph.edge_count(), 3); + + assert_eq!(result_graph.has_node(1), true); + assert_eq!(result_graph.has_node(3), true); + assert_eq!(result_graph.has_node(2), true); + assert_eq!(result_graph.has_node(4), false); + + assert_eq!(result_graph.has_edge(1, 3), true); + assert_eq!(result_graph.has_edge(1, 2), true); + assert_eq!(result_graph.has_edge(2, 3), true); + assert_eq!(result_graph.has_edge(3, 1), true); + + assert_eq!(result_graph.get_node_label(1), Some(&1)); + assert_eq!(result_graph.get_node_label(3), Some(&3)); + assert_eq!(result_graph.get_node_label(2), Some(&2)); + assert_eq!(result_graph.get_node_label(4), None); + + assert_eq!(result_graph.get_edge_label(1, 3), Some(&13)); + assert_eq!(result_graph.get_edge_label(3, 1), Some(&13)); +} + +#[test] +fn test_graph_induce_directed_graphs() { + let mut graph0 = DiGraphMap::::new(); + graph0.add_node(1, Some(1)); + graph0.add_node(2, Some(2)); + graph0.add_node(3, Some(3)); + graph0.add_node(4, Some(4)); + graph0.add_edge(1, 2, Some(12)); + graph0.add_edge(2, 3, Some(23)); + graph0.add_edge(3, 4, Some(34)); + graph0.add_edge(1, 4, Some(14)); + graph0.add_edge(1, 3, Some(13)); + + let mut graph1 = DiGraphMap::::new(); + graph1.add_node(1, Some(1)); + graph1.add_node(2, Some(2)); + graph1.add_node(3, Some(3)); + graph1.add_edge(1, 2, Some(12)); + graph1.add_edge(2, 3, Some(23)); + + let result_graph = graph_induce(&graph0, &graph1); + + assert_eq!(result_graph.node_count(), 3); + assert_eq!(result_graph.edge_count(), 3); + + assert_eq!(result_graph.has_node(1), true); + assert_eq!(result_graph.has_node(3), true); + assert_eq!(result_graph.has_node(2), true); + assert_eq!(result_graph.has_node(4), false); + + assert_eq!(result_graph.has_edge(1, 3), true); + assert_eq!(result_graph.has_edge(1, 2), true); + assert_eq!(result_graph.has_edge(2, 3), true); + assert_eq!(result_graph.has_edge(3, 1), false); + + assert_eq!(result_graph.get_node_label(1), Some(&1)); + assert_eq!(result_graph.get_node_label(3), Some(&3)); + assert_eq!(result_graph.get_node_label(2), Some(&2)); + assert_eq!(result_graph.get_node_label(4), None); + + assert_eq!(result_graph.get_edge_label(1, 3), Some(&13)); + assert_eq!(result_graph.get_edge_label(3, 1), None); +} diff --git a/tests/converter.rs b/tests/converter.rs index e5e67865..7a7f51ed 100644 --- a/tests/converter.rs +++ b/tests/converter.rs @@ -1,115 +1,115 @@ -/* - * Copyright (c) 2018 UNSW Sydney, Data and Knowledge Group. - * - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you 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. - */ -extern crate rust_graph; - -use rust_graph::prelude::*; - -#[test] -fn test_undirected() { - let mut g = rust_graph::UnGraphMap::<&str>::new(); - - g.add_node(0, Some("n")); - g.add_node(1, Some("n")); - g.add_node(2, Some("m")); - g.add_node(3, None); - - g.add_edge(0, 1, Some("a")); - g.add_edge(1, 2, Some("b")); - g.add_edge(2, 0, None); - - let g = g.into_static(); - - let edges: Vec<_> = g.edge_indices().collect(); - - assert_eq!(edges, vec![(0, 1), (0, 2), (1, 2)]); - - assert_eq!(g.get_node_label(0), Some(&"n")); - assert_eq!(g.get_node_label(1), Some(&"n")); - assert_eq!(g.get_node_label(2), Some(&"m")); - assert_eq!(g.get_node_label(3), None); - - assert_eq!(g.get_edge_label(0, 1), Some(&"a")); - assert_eq!(g.get_edge_label(1, 2), Some(&"b")); - assert_eq!(g.get_edge_label(2, 0), None); -} - -#[test] -fn test_undirected_reorder() { - let mut g = rust_graph::UnGraphMap::<&str>::new(); - - g.add_node(0, Some("n")); - g.add_node(1, Some("n")); - g.add_node(2, Some("m")); - g.add_node(3, None); - - g.add_edge(0, 1, Some("a")); - g.add_edge(1, 2, Some("b")); - g.add_edge(2, 0, None); - - let mut reorder_result = g.reorder_id(true, true, true); - - let g = reorder_result.take_graph().unwrap().into_static(); - - let edges: Vec<_> = g.edge_indices().collect(); - - assert_eq!(edges, vec![(1, 2), (1, 3), (2, 3)]); - - let node0 = reorder_result.find_new_node_id(0); - let node1 = reorder_result.find_new_node_id(1); - let node2 = reorder_result.find_new_node_id(2); - let node3 = reorder_result.find_new_node_id(3); - - assert_eq!(g.get_node_label(node0), Some(&"n")); - assert_eq!(g.get_node_label(node1), Some(&"n")); - assert_eq!(g.get_node_label(node2), Some(&"m")); - assert_eq!(g.get_node_label(node3), None); - - assert_eq!(g.get_edge_label(node0, node1), Some(&"a")); - assert_eq!(g.get_edge_label(node1, node2), Some(&"b")); - assert_eq!(g.get_edge_label(node0, node2), None); -} - -#[test] -fn test_directed() { - let mut g = rust_graph::DiGraphMap::<&str>::new(); - - g.add_node(0, None); - g.add_node(1, None); - g.add_node(2, None); - g.add_node(3, None); - - g.add_edge(1, 0, Some("a")); - g.add_edge(2, 0, Some("a")); - g.add_edge(2, 3, Some("b")); - g.add_edge(3, 0, Some("a")); - g.add_edge(3, 1, Some("a")); - g.add_edge(3, 2, Some("a")); - - let g = g - .reorder_id(true, true, true) - .take_graph() - .unwrap() - .into_static(); - - let edges: Vec<_> = g.edge_indices().collect(); - - assert_eq!(edges, vec![(0, 1), (2, 1), (2, 3), (3, 0), (3, 1), (3, 2)]); -} +/* + * Copyright (c) 2018 UNSW Sydney, Data and Knowledge Group. + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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. + */ +extern crate rust_graph; + +use rust_graph::prelude::*; + +#[test] +fn test_undirected() { + let mut g = rust_graph::UnGraphMap::<&str>::new(); + + g.add_node(0, Some("n")); + g.add_node(1, Some("n")); + g.add_node(2, Some("m")); + g.add_node(3, None); + + g.add_edge(0, 1, Some("a")); + g.add_edge(1, 2, Some("b")); + g.add_edge(2, 0, None); + + let g = g.into_static(); + + let edges: Vec<_> = g.edge_indices().collect(); + + assert_eq!(edges, vec![(0, 1), (0, 2), (1, 2)]); + + assert_eq!(g.get_node_label(0), Some(&"n")); + assert_eq!(g.get_node_label(1), Some(&"n")); + assert_eq!(g.get_node_label(2), Some(&"m")); + assert_eq!(g.get_node_label(3), None); + + assert_eq!(g.get_edge_label(0, 1), Some(&"a")); + assert_eq!(g.get_edge_label(1, 2), Some(&"b")); + assert_eq!(g.get_edge_label(2, 0), None); +} + +#[test] +fn test_undirected_reorder() { + let mut g = rust_graph::UnGraphMap::<&str>::new(); + + g.add_node(0, Some("n")); + g.add_node(1, Some("n")); + g.add_node(2, Some("m")); + g.add_node(3, None); + + g.add_edge(0, 1, Some("a")); + g.add_edge(1, 2, Some("b")); + g.add_edge(2, 0, None); + + let mut reorder_result = g.reorder_id(true, true, true); + + let g = reorder_result.take_graph().unwrap().into_static(); + + let edges: Vec<_> = g.edge_indices().collect(); + + assert_eq!(edges, vec![(1, 2), (1, 3), (2, 3)]); + + let node0 = reorder_result.find_new_node_id(0); + let node1 = reorder_result.find_new_node_id(1); + let node2 = reorder_result.find_new_node_id(2); + let node3 = reorder_result.find_new_node_id(3); + + assert_eq!(g.get_node_label(node0), Some(&"n")); + assert_eq!(g.get_node_label(node1), Some(&"n")); + assert_eq!(g.get_node_label(node2), Some(&"m")); + assert_eq!(g.get_node_label(node3), None); + + assert_eq!(g.get_edge_label(node0, node1), Some(&"a")); + assert_eq!(g.get_edge_label(node1, node2), Some(&"b")); + assert_eq!(g.get_edge_label(node0, node2), None); +} + +#[test] +fn test_directed() { + let mut g = rust_graph::DiGraphMap::<&str>::new(); + + g.add_node(0, None); + g.add_node(1, None); + g.add_node(2, None); + g.add_node(3, None); + + g.add_edge(1, 0, Some("a")); + g.add_edge(2, 0, Some("a")); + g.add_edge(2, 3, Some("b")); + g.add_edge(3, 0, Some("a")); + g.add_edge(3, 1, Some("a")); + g.add_edge(3, 2, Some("a")); + + let g = g + .reorder_id(true, true, true) + .take_graph() + .unwrap() + .into_static(); + + let edges: Vec<_> = g.edge_indices().collect(); + + assert_eq!(edges, vec![(0, 1), (2, 1), (2, 3), (3, 0), (3, 1), (3, 2)]); +} diff --git a/tests/graph_gen.rs b/tests/graph_gen.rs index bd546986..e847786b 100644 --- a/tests/graph_gen.rs +++ b/tests/graph_gen.rs @@ -1,52 +1,52 @@ -/* - * Copyright (c) 2018 UNSW Sydney, Data and Knowledge Group. - * - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you 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. - */ -extern crate rust_graph; - -use rust_graph::prelude::*; - -use rust_graph::graph_gen::{complete_graph_unlabeled, empty_graph_unlabeled}; -use rust_graph::graph_gen::{random_gnm_graph_unlabeled, random_gnp_graph_unlabeled}; -use rust_graph::graph_impl::{DiGraphMap, UnGraphMap}; - -#[test] -fn test_random_gnp_graph() { - let num_of_nodes = 100; - - let empty: DiGraphMap = random_gnp_graph_unlabeled(num_of_nodes, 0f32); - assert_eq!(empty, empty_graph_unlabeled(num_of_nodes)); - - let clique: UnGraphMap = random_gnp_graph_unlabeled(num_of_nodes, 1f32); - assert_eq!(clique, complete_graph_unlabeled(num_of_nodes)); -} - -#[test] -fn test_random_gnm_graph() { - let num_of_nodes = 100; - let num_of_edges = 1000; - - let g1: DiGraphMap = random_gnm_graph_unlabeled(num_of_nodes, num_of_edges); - assert_eq!(g1.node_count(), num_of_nodes); - assert_eq!(g1.edge_count(), num_of_edges); - - let g2: UnGraphMap = random_gnm_graph_unlabeled(num_of_nodes, num_of_edges); - assert_eq!(g2.node_count(), num_of_nodes); - assert_eq!(g2.edge_count(), num_of_edges); -} +/* + * Copyright (c) 2018 UNSW Sydney, Data and Knowledge Group. + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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. + */ +extern crate rust_graph; + +use rust_graph::prelude::*; + +use rust_graph::graph_gen::{complete_graph_unlabeled, empty_graph_unlabeled}; +use rust_graph::graph_gen::{random_gnm_graph_unlabeled, random_gnp_graph_unlabeled}; +use rust_graph::graph_impl::{DiGraphMap, UnGraphMap}; + +#[test] +fn test_random_gnp_graph() { + let num_of_nodes = 100; + + let empty: DiGraphMap = random_gnp_graph_unlabeled(num_of_nodes, 0f32); + assert_eq!(empty, empty_graph_unlabeled(num_of_nodes)); + + let clique: UnGraphMap = random_gnp_graph_unlabeled(num_of_nodes, 1f32); + assert_eq!(clique, complete_graph_unlabeled(num_of_nodes)); +} + +#[test] +fn test_random_gnm_graph() { + let num_of_nodes = 100; + let num_of_edges = 1000; + + let g1: DiGraphMap = random_gnm_graph_unlabeled(num_of_nodes, num_of_edges); + assert_eq!(g1.node_count(), num_of_nodes); + assert_eq!(g1.edge_count(), num_of_edges); + + let g2: UnGraphMap = random_gnm_graph_unlabeled(num_of_nodes, num_of_edges); + assert_eq!(g2.node_count(), num_of_nodes); + assert_eq!(g2.edge_count(), num_of_edges); +} diff --git a/tests/graph_map.rs b/tests/graph_map.rs index bd9e13c0..bd38d9aa 100644 --- a/tests/graph_map.rs +++ b/tests/graph_map.rs @@ -1,440 +1,470 @@ -/* - * Copyright (c) 2018 UNSW Sydney, Data and Knowledge Group. - * - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you 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. - */ -extern crate rust_graph; - -use std::collections::HashMap; - -use rust_graph::generic::{MutEdgeType, MutNodeType}; -use rust_graph::graph_impl::graph_map::{Edge, MutEdge, NodeMap, NodeMapTrait}; -use rust_graph::prelude::*; -use rust_graph::{DiGraphMap, UnGraphMap}; - -#[test] -fn test_add_get_node() { - let mut g = UnGraphMap::<&str>::new(); - - g.add_node(0, Some("a")); - assert_eq!(g.node_count(), 1); - g.add_node(1, Some("b")); - assert_eq!(g.node_count(), 2); - g.add_node(2, Some("a")); - assert_eq!(g.node_count(), 3); - g.add_node(3, None); - assert_eq!(g.node_count(), 4); - - let n0_expected = NodeMap::new(0, Some(0)); - let n1_expected = NodeMap::new(1, Some(1)); - let mut n2_expected = NodeMap::new(2, Some(0)); - let mut n3_expected = NodeMap::new(3, None); - - assert_eq!(g.get_node(0).unwrap_nodemap(), &n0_expected); - assert_eq!(g.get_node(1).unwrap_nodemap(), &n1_expected); - assert_eq!(g.get_node_mut(2), MutNodeType::NodeMapRef(&mut n2_expected)); - assert_eq!(g.get_node_mut(3), MutNodeType::NodeMapRef(&mut n3_expected)); - assert!(g.get_node(4).is_none()); -} - -#[test] -fn test_duplicate_node() { - let mut g = UnGraphMap::<&str>::new(); - - g.add_node(0, Some("a")); - assert!(!g.add_node(0, None)); -} - -#[test] -fn test_remove_node_directed() { - let mut g = DiGraphMap::<&str>::new(); - - g.add_node(0, None); - g.add_node(1, None); - g.add_node(2, None); - - g.add_edge(0, 1, None); - g.add_edge(0, 2, None); - g.add_edge(1, 0, None); - - g.remove_node(0); - - assert_eq!(g.node_count(), 2); - assert_eq!(g.edge_count(), 0); - - assert!(!g.has_node(0)); - - assert!(!g.has_edge(0, 1)); - assert!(!g.has_edge(0, 2)); - assert!(!g.has_edge(1, 0)); -} - -#[test] -fn test_remove_node_undirected() { - let mut g = UnGraphMap::<&str>::new(); - - g.add_node(0, None); - g.add_node(1, None); - g.add_node(2, None); - - g.add_edge(0, 1, None); - g.add_edge(0, 2, None); - g.add_edge(1, 2, None); - - g.remove_node(0); - - assert_eq!(g.node_count(), 2); - assert_eq!(g.edge_count(), 1); - - assert!(!g.has_node(0)); - - assert!(!g.has_edge(0, 1)); - assert!(!g.has_edge(1, 0)); - assert!(!g.has_edge(0, 2)); - assert!(!g.has_edge(2, 0)); -} - -#[test] -fn test_add_get_edge_directed() { - let mut g = DiGraphMap::<&str>::new(); - - g.add_node(0, None); - g.add_node(1, None); - g.add_node(2, None); - - g.add_edge(0, 1, Some("a")); - assert_eq!(g.edge_count(), 1); - g.add_edge(1, 2, Some("b")); - assert_eq!(g.edge_count(), 2); - g.add_edge(2, 0, Some("a")); - assert_eq!(g.edge_count(), 3); - g.add_edge(1, 0, None); - assert_eq!(g.edge_count(), 4); - g.add_edge(0, 0, None); - assert_eq!(g.edge_count(), 5); - - let e0_expected = Edge::new(0, 1, Some(0)); - let e1_expected = Edge::new(1, 2, Some(1)); - let mut e2_expected = Some(DefaultId::new(0)); - - assert_eq!(g.get_edge(0, 1).unwrap(), e0_expected); - assert_eq!(g.get_edge(1, 2).unwrap(), e1_expected); - assert_eq!( - g.get_edge_mut(2, 0), - MutEdgeType::EdgeRef(MutEdge::new(2, 0, &mut e2_expected)) - ); - assert_eq!( - g.get_edge_mut(1, 0), - MutEdgeType::EdgeRef(MutEdge::new(1, 0, &mut None)) - ); - - assert!(g.get_edge(1, 3).is_none()); - assert!(g.get_edge_mut(2, 1).is_none()); - - assert!(g.has_edge(0, 0)); -} - -#[test] -fn test_add_get_edge_undirected() { - let mut g = UnGraphMap::<&str>::new(); - - g.add_node(0, None); - g.add_node(1, None); - g.add_node(2, None); - g.add_node(3, None); - - g.add_edge(0, 1, Some("a")); - assert_eq!(g.edge_count(), 1); - g.add_edge(1, 2, Some("b")); - assert_eq!(g.edge_count(), 2); - g.add_edge(2, 0, Some("a")); - assert_eq!(g.edge_count(), 3); - g.add_edge(0, 0, None); - assert_eq!(g.edge_count(), 4); - - let e0_expected = Edge::new(0, 1, Some(0)); - let e1_expected = Edge::new(1, 2, Some(1)); - let e1_expected_1 = Edge::new(2, 1, Some(1)); - let mut e2_expected = Some(DefaultId::new(0)); - - assert_eq!(g.get_edge(0, 1).unwrap(), e0_expected); - assert_eq!(g.get_edge(1, 2).unwrap(), e1_expected); - assert_eq!(g.get_edge(2, 1).unwrap(), e1_expected_1); - assert_eq!( - g.get_edge_mut(2, 0), - MutEdgeType::EdgeRef(MutEdge::new(2, 0, &mut e2_expected)) - ); - assert_eq!( - g.get_edge_mut(0, 2), - MutEdgeType::EdgeRef(MutEdge::new(0, 2, &mut e2_expected)) - ); - - assert!(g.get_edge(1, 3).is_none()); - assert!(g.get_edge_mut(0, 3).is_none()); - - assert!(g.has_edge(0, 0)); -} - -#[test] -fn test_multi_adding() { - let mut g = DiGraphMap::<&str>::new(); - g.add_node(0, None); - g.add_node(1, None); - g.add_edge(0, 1, None); - - assert!(!g.add_node(0, Some("label"))); - assert_eq!(g.get_node_label(0), Some(&"label")); - - assert!(!g.add_edge(0, 1, Some("label"))); - assert_eq!(g.get_edge_label(0, 1), Some(&"label")); - - let mut g = UnGraphMap::<&str>::new(); - g.add_node(0, None); - g.add_node(1, None); - g.add_edge(0, 1, None); - - assert!(!g.add_node(0, Some("label"))); - assert_eq!(g.get_node_label(0), Some(&"label")); - - assert!(!g.add_edge(1, 0, Some("label"))); - assert_eq!(g.get_edge_label(0, 1), Some(&"label")); -} - -#[test] -fn test_remove_edge_directed() { - let mut g = DiGraphMap::<&str>::new(); - g.add_node(0, None); - g.add_node(1, None); - g.add_node(2, None); - g.add_edge(0, 1, None); - g.add_edge(1, 0, None); - g.add_edge(1, 2, None); - - g.remove_edge(0, 1); - - assert_eq!(g.edge_count(), 2); - assert!(!g.has_edge(0, 1)); - assert!(!g.get_node(0).unwrap_nodemap().neighbors().contains(&1)); - assert!(!g.get_node(1).unwrap_nodemap().in_neighbors().contains(&0)); - - g.remove_edge(1, 2); - assert_eq!(g.edge_count(), 1); - assert!(!g.has_edge(1, 2)); - assert!(!g.get_node(1).unwrap_nodemap().neighbors().contains(&2)); - assert!(!g.get_node(2).unwrap_nodemap().in_neighbors().contains(&1)); - - assert!(g.has_edge(1, 0)); -} - -#[test] -fn test_remove_edge_undirected() { - let mut g = UnGraphMap::<&str>::new(); - g.add_node(0, None); - g.add_node(1, None); - g.add_node(2, None); - g.add_edge(0, 1, None); - g.add_edge(2, 1, None); - - g.remove_edge(1, 0); - assert_eq!(g.edge_count(), 1); - assert!(!g.has_edge(0, 1)); - assert!(!g.get_node(0).unwrap_nodemap().neighbors().contains(&1)); - assert!(!g.get_node(1).unwrap_nodemap().neighbors().contains(&0)); - - g.remove_edge(1, 2); - assert_eq!(g.edge_count(), 0); - assert!(!g.get_node(1).unwrap_nodemap().neighbors().contains(&2)); - assert!(!g.get_node(2).unwrap_nodemap().neighbors().contains(&1)); -} - -#[test] -fn test_get_node_edge_label() { - let mut g = DiGraphMap::<&str>::new(); - g.add_node(0, Some("0")); - g.add_node(1, None); - g.add_edge(0, 1, Some("(0,1)")); - g.add_edge(1, 0, None); - - assert_eq!(g.get_node_label(0), Some(&"0")); - assert_eq!(g.get_node_label(1), None); - - assert_eq!(g.get_edge_label(0, 1), Some(&"(0,1)")); - assert_eq!(g.get_edge_label(1, 0), None); -} - -#[test] -fn test_iter() { - let mut g = DiGraphMap::<&str>::new(); - g.add_node(0, Some("0")); - g.add_node(1, Some("1")); - g.add_node(2, None); - g.add_edge(0, 1, Some("(0,1)")); - g.add_edge(1, 0, Some("(1,0)")); - g.add_edge(1, 2, None); - - let node_ids: Vec<_> = g.node_indices().collect(); - assert_eq!(node_ids.len(), 3); - assert!(node_ids.contains(&&0)); - assert!(node_ids.contains(&&1)); - assert!(node_ids.contains(&&2)); - - let edge_ids: Vec<_> = g.edge_indices().collect(); - assert_eq!(edge_ids.len(), 3); - assert!(edge_ids.contains(&&(0, 1))); - assert!(edge_ids.contains(&&(1, 0))); - assert!(edge_ids.contains(&&(1, 2))); - - let nodes: Vec<_> = g.nodes().collect(); - assert_eq!(nodes.len(), 3); - assert!(nodes.contains(&g.get_node(0))); - assert!(nodes.contains(&g.get_node(1))); - assert!(nodes.contains(&g.get_node(2))); - - let edges: Vec<_> = g.edges().collect(); - assert_eq!(edges.len(), 3); - assert!(edges.contains(&g.get_edge(0, 1))); - assert!(edges.contains(&g.get_edge(1, 0))); - assert!(edges.contains(&g.get_edge(1, 2))); - - let node_labels: Vec<_> = g.node_labels().collect(); - assert_eq!(node_labels.len(), 2); - assert!(node_labels.contains(&&"0")); - assert!(node_labels.contains(&&"1")); - - let edge_labels: Vec<_> = g.edge_labels().collect(); - assert_eq!(edge_labels.len(), 2); - assert!(edge_labels.contains(&&"(0,1)")); - assert!(edge_labels.contains(&&"(1,0)")); - - let neighbors_of_1: Vec<_> = g.neighbors_iter(1).collect(); - assert_eq!(neighbors_of_1.len(), 2); - assert!(neighbors_of_1.contains(&&0)); - assert!(neighbors_of_1.contains(&&2)); -} - -#[test] -fn test_iter_mut() { - let mut g = DiGraphMap::<&str>::new(); - g.add_node(0, None); - g.add_node(1, None); - g.add_edge(0, 1, None); - g.add_edge(1, 0, None); - - let mut n0 = g.get_node(0).unwrap_nodemap().clone(); - let mut n1 = g.get_node(1).unwrap_nodemap().clone(); - - { - let nodes: Vec<_> = g.nodes_mut().map(|n| n.unwrap_nodemap_ref()).collect(); - assert_eq!(nodes.len(), 2); - assert!(nodes.contains(&&mut n0)); - assert!(nodes.contains(&&mut n1)); - } - - let mut e0 = g.get_edge(0, 1).unwrap().clone().get_label_id(); - let mut e1 = g.get_edge(1, 0).unwrap().clone().get_label_id(); - - { - let edges: Vec<_> = g.edges_mut().map(|e| e.unwrap_edge_ref()).collect(); - assert_eq!(edges.len(), 2); - assert!(edges.contains(&MutEdge::new(0, 1, &mut e0))); - assert!(edges.contains(&MutEdge::new(1, 0, &mut e1))); - } -} - -#[test] -fn test_neighbors() { - let mut g = DiGraphMap::::new(); - g.add_node(0, Some(0)); - g.add_node(1, Some(0)); - g.add_node(2, None); - - g.add_edge(0, 1, Some(0)); - g.add_edge(0, 2, Some(0)); - g.add_edge(2, 1, None); - - assert_eq!(g.neighbors(0)[..], [1, 2]); - assert_eq!(&g.in_neighbors(1)[..], [0, 2]); - - assert_eq!(g.degree(0), 2); - assert_eq!(g.in_degree(1), 2); - - assert!(g.neighbors(1).is_empty()); - assert!(g.in_neighbors(0).is_empty()); - - assert_eq!(g.degree(1), 0); - assert_eq!(g.in_degree(0), 0); -} - -#[test] -fn test_max_id() { - let mut g = DiGraphMap::::new(); - - assert_eq!(g.max_seen_id(), None); - - g.add_node(1, Some(0)); - assert_eq!(g.max_seen_id(), Some(1)); - - g.add_node(0, Some(0)); - assert_eq!(g.max_seen_id(), Some(1)); - - g.add_node(2, None); - assert_eq!(g.max_seen_id(), Some(2)); - - g.add_edge(3, 4, Some(0)); - assert_eq!(g.max_seen_id(), Some(4)); - - g.add_edge(6, 5, Some(0)); - assert_eq!(g.max_seen_id(), Some(6)); -} - -#[test] -fn test_clone() { - let mut g = DiGraphMap::::new(); - g.add_node(0, Some(0)); - g.add_node(1, Some(0)); - g.add_node(2, Some(1)); - g.add_node(3, None); - - g.add_edge(0, 1, Some(0)); - g.add_edge(1, 2, Some(0)); - g.add_edge(2, 3, Some(1)); - g.add_edge(3, 1, None); - - assert_eq!(g, g.clone()); -} - -#[test] -fn test_stats() { - let mut g = DiGraphMap::::new(); - g.add_node(0, Some(0)); - g.add_node(1, Some(0)); - g.add_node(2, Some(1)); - g.add_node(3, None); - - g.add_edge(0, 1, Some(0)); - g.add_edge(1, 2, Some(0)); - g.add_edge(2, 3, Some(1)); - g.add_edge(3, 1, None); - - let mut expected_counter = HashMap::new(); - expected_counter.insert(0, 2); - expected_counter.insert(1, 1); - - assert_eq!(g.get_node_label_id_counter().into_map(), expected_counter); - assert_eq!(g.get_edge_label_id_counter().into_map(), expected_counter) -} +/* + * Copyright (c) 2018 UNSW Sydney, Data and Knowledge Group. + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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. + */ +extern crate rust_graph; + +use std::collections::HashMap; + +use rust_graph::generic::{MutEdgeType, MutNodeType}; +use rust_graph::graph_impl::graph_map::{Edge, MutEdge, NodeMap, NodeMapTrait}; +use rust_graph::prelude::*; +use rust_graph::{DiGraphMap, UnGraphMap}; + +#[test] +fn test_add_get_node() { + let mut g = UnGraphMap::<&str>::new(); + + g.add_node(0, Some("a")); + assert_eq!(g.node_count(), 1); + g.add_node(1, Some("b")); + assert_eq!(g.node_count(), 2); + g.add_node(2, Some("a")); + assert_eq!(g.node_count(), 3); + g.add_node(3, None); + assert_eq!(g.node_count(), 4); + + let n0_expected = NodeMap::new(0, Some(0)); + let n1_expected = NodeMap::new(1, Some(1)); + let mut n2_expected = NodeMap::new(2, Some(0)); + let mut n3_expected = NodeMap::new(3, None); + + assert_eq!(g.get_node(0).unwrap_nodemap(), &n0_expected); + assert_eq!(g.get_node(1).unwrap_nodemap(), &n1_expected); + assert_eq!(g.get_node_mut(2), MutNodeType::NodeMapRef(&mut n2_expected)); + assert_eq!(g.get_node_mut(3), MutNodeType::NodeMapRef(&mut n3_expected)); + assert!(g.get_node(4).is_none()); +} + +#[test] +fn test_duplicate_node() { + let mut g = UnGraphMap::<&str>::new(); + + g.add_node(0, Some("a")); + assert!(!g.add_node(0, None)); +} + +#[test] +fn test_remove_node_directed() { + let mut g = DiGraphMap::<&str>::new(); + + g.add_node(0, None); + g.add_node(1, None); + g.add_node(2, None); + + g.add_edge(0, 1, None); + g.add_edge(0, 2, None); + g.add_edge(1, 0, None); + + g.remove_node(0); + + assert_eq!(g.node_count(), 2); + assert_eq!(g.edge_count(), 0); + + assert!(!g.has_node(0)); + + assert!(!g.has_edge(0, 1)); + assert!(!g.has_edge(0, 2)); + assert!(!g.has_edge(1, 0)); +} + +#[test] +fn test_remove_node_undirected() { + let mut g = UnGraphMap::<&str>::new(); + + g.add_node(0, None); + g.add_node(1, None); + g.add_node(2, None); + + g.add_edge(0, 1, None); + g.add_edge(0, 2, None); + g.add_edge(1, 2, None); + + g.remove_node(0); + + assert_eq!(g.node_count(), 2); + assert_eq!(g.edge_count(), 1); + + assert!(!g.has_node(0)); + + assert!(!g.has_edge(0, 1)); + assert!(!g.has_edge(1, 0)); + assert!(!g.has_edge(0, 2)); + assert!(!g.has_edge(2, 0)); +} + +#[test] +fn test_add_get_edge_directed() { + let mut g = DiGraphMap::<&str>::new(); + + g.add_node(0, None); + g.add_node(1, None); + g.add_node(2, None); + + g.add_edge(0, 1, Some("a")); + assert_eq!(g.edge_count(), 1); + g.add_edge(1, 2, Some("b")); + assert_eq!(g.edge_count(), 2); + g.add_edge(2, 0, Some("a")); + assert_eq!(g.edge_count(), 3); + g.add_edge(1, 0, None); + assert_eq!(g.edge_count(), 4); + g.add_edge(0, 0, None); + assert_eq!(g.edge_count(), 5); + + let e0_expected = Edge::new(0, 1, Some(0)); + let e1_expected = Edge::new(1, 2, Some(1)); + let mut e2_expected = Some(DefaultId::new(0)); + + assert_eq!(g.get_edge(0, 1).unwrap(), e0_expected); + assert_eq!(g.get_edge(1, 2).unwrap(), e1_expected); + assert_eq!( + g.get_edge_mut(2, 0), + MutEdgeType::EdgeRef(MutEdge::new(2, 0, &mut e2_expected)) + ); + assert_eq!( + g.get_edge_mut(1, 0), + MutEdgeType::EdgeRef(MutEdge::new(1, 0, &mut None)) + ); + + assert!(g.get_edge(1, 3).is_none()); + assert!(g.get_edge_mut(2, 1).is_none()); + + assert!(g.has_edge(0, 0)); +} + +#[test] +fn test_add_get_edge_undirected() { + let mut g = UnGraphMap::<&str>::new(); + + g.add_node(0, None); + g.add_node(1, None); + g.add_node(2, None); + g.add_node(3, None); + + g.add_edge(0, 1, Some("a")); + assert_eq!(g.edge_count(), 1); + g.add_edge(1, 2, Some("b")); + assert_eq!(g.edge_count(), 2); + g.add_edge(2, 0, Some("a")); + assert_eq!(g.edge_count(), 3); + g.add_edge(0, 0, None); + assert_eq!(g.edge_count(), 4); + + let e0_expected = Edge::new(0, 1, Some(0)); + let e1_expected = Edge::new(1, 2, Some(1)); + let e1_expected_1 = Edge::new(2, 1, Some(1)); + let mut e2_expected = Some(DefaultId::new(0)); + + assert_eq!(g.get_edge(0, 1).unwrap(), e0_expected); + assert_eq!(g.get_edge(1, 2).unwrap(), e1_expected); + assert_eq!(g.get_edge(2, 1).unwrap(), e1_expected_1); + assert_eq!( + g.get_edge_mut(2, 0), + MutEdgeType::EdgeRef(MutEdge::new(2, 0, &mut e2_expected)) + ); + assert_eq!( + g.get_edge_mut(0, 2), + MutEdgeType::EdgeRef(MutEdge::new(0, 2, &mut e2_expected)) + ); + + assert!(g.get_edge(1, 3).is_none()); + assert!(g.get_edge_mut(0, 3).is_none()); + + assert!(g.has_edge(0, 0)); +} + +#[test] +fn test_multi_adding() { + let mut g = DiGraphMap::<&str>::new(); + g.add_node(0, None); + g.add_node(1, None); + g.add_edge(0, 1, None); + + assert!(!g.add_node(0, Some("label"))); + assert_eq!(g.get_node_label(0), Some(&"label")); + + assert!(!g.add_edge(0, 1, Some("label"))); + assert_eq!(g.get_edge_label(0, 1), Some(&"label")); + + let mut g = UnGraphMap::<&str>::new(); + g.add_node(0, None); + g.add_node(1, None); + g.add_edge(0, 1, None); + + assert!(!g.add_node(0, Some("label"))); + assert_eq!(g.get_node_label(0), Some(&"label")); + + assert!(!g.add_edge(1, 0, Some("label"))); + assert_eq!(g.get_edge_label(0, 1), Some(&"label")); +} + +#[test] +fn test_remove_edge_directed() { + let mut g = DiGraphMap::<&str>::new(); + g.add_node(0, None); + g.add_node(1, None); + g.add_node(2, None); + g.add_edge(0, 1, None); + g.add_edge(1, 0, None); + g.add_edge(1, 2, None); + + g.remove_edge(0, 1); + + assert_eq!(g.edge_count(), 2); + assert!(!g.has_edge(0, 1)); + assert!(!g.get_node(0).unwrap_nodemap().neighbors().contains(&1)); + assert!(!g.get_node(1).unwrap_nodemap().in_neighbors().contains(&0)); + + g.remove_edge(1, 2); + assert_eq!(g.edge_count(), 1); + assert!(!g.has_edge(1, 2)); + assert!(!g.get_node(1).unwrap_nodemap().neighbors().contains(&2)); + assert!(!g.get_node(2).unwrap_nodemap().in_neighbors().contains(&1)); + + assert!(g.has_edge(1, 0)); +} + +#[test] +fn test_remove_edge_undirected() { + let mut g = UnGraphMap::<&str>::new(); + g.add_node(0, None); + g.add_node(1, None); + g.add_node(2, None); + g.add_edge(0, 1, None); + g.add_edge(2, 1, None); + + g.remove_edge(1, 0); + assert_eq!(g.edge_count(), 1); + assert!(!g.has_edge(0, 1)); + assert!(!g.get_node(0).unwrap_nodemap().neighbors().contains(&1)); + assert!(!g.get_node(1).unwrap_nodemap().neighbors().contains(&0)); + + g.remove_edge(1, 2); + assert_eq!(g.edge_count(), 0); + assert!(!g.get_node(1).unwrap_nodemap().neighbors().contains(&2)); + assert!(!g.get_node(2).unwrap_nodemap().neighbors().contains(&1)); +} + +#[test] +fn test_get_node_edge_label() { + let mut g = DiGraphMap::<&str>::new(); + g.add_node(0, Some("0")); + g.add_node(1, None); + g.add_edge(0, 1, Some("(0,1)")); + g.add_edge(1, 0, None); + + assert_eq!(g.get_node_label(0), Some(&"0")); + assert_eq!(g.get_node_label(1), None); + + assert_eq!(g.get_edge_label(0, 1), Some(&"(0,1)")); + assert_eq!(g.get_edge_label(1, 0), None); +} + +#[test] +fn test_iter() { + let mut g = DiGraphMap::<&str>::new(); + g.add_node(0, Some("0")); + g.add_node(1, Some("1")); + g.add_node(2, None); + g.add_edge(0, 1, Some("(0,1)")); + g.add_edge(1, 0, Some("(1,0)")); + g.add_edge(1, 2, None); + + let node_ids: Vec<_> = g.node_indices().collect(); + assert_eq!(node_ids.len(), 3); + assert!(node_ids.contains(&&0)); + assert!(node_ids.contains(&&1)); + assert!(node_ids.contains(&&2)); + + let edge_ids: Vec<_> = g.edge_indices().collect(); + assert_eq!(edge_ids.len(), 3); + assert!(edge_ids.contains(&&(0, 1))); + assert!(edge_ids.contains(&&(1, 0))); + assert!(edge_ids.contains(&&(1, 2))); + + let nodes: Vec<_> = g.nodes().collect(); + assert_eq!(nodes.len(), 3); + assert!(nodes.contains(&g.get_node(0))); + assert!(nodes.contains(&g.get_node(1))); + assert!(nodes.contains(&g.get_node(2))); + + let edges: Vec<_> = g.edges().collect(); + assert_eq!(edges.len(), 3); + assert!(edges.contains(&g.get_edge(0, 1))); + assert!(edges.contains(&g.get_edge(1, 0))); + assert!(edges.contains(&g.get_edge(1, 2))); + + let node_labels: Vec<_> = g.node_labels().collect(); + assert_eq!(node_labels.len(), 2); + assert!(node_labels.contains(&&"0")); + assert!(node_labels.contains(&&"1")); + + let edge_labels: Vec<_> = g.edge_labels().collect(); + assert_eq!(edge_labels.len(), 2); + assert!(edge_labels.contains(&&"(0,1)")); + assert!(edge_labels.contains(&&"(1,0)")); + + let neighbors_of_1: Vec<_> = g.neighbors_iter(1).collect(); + assert_eq!(neighbors_of_1.len(), 2); + assert!(neighbors_of_1.contains(&&0)); + assert!(neighbors_of_1.contains(&&2)); +} + +#[test] +fn test_iter_mut() { + let mut g = DiGraphMap::<&str>::new(); + g.add_node(0, None); + g.add_node(1, None); + g.add_edge(0, 1, None); + g.add_edge(1, 0, None); + + let mut n0 = g.get_node(0).unwrap_nodemap().clone(); + let mut n1 = g.get_node(1).unwrap_nodemap().clone(); + + { + let nodes: Vec<_> = g.nodes_mut().map(|n| n.unwrap()).collect(); + assert_eq!(nodes.len(), 2); + assert!(nodes.contains(&&mut n0)); + assert!(nodes.contains(&&mut n1)); + } + + let mut e0 = g.get_edge(0, 1).unwrap().clone().get_label_id(); + let mut e1 = g.get_edge(1, 0).unwrap().clone().get_label_id(); + + { + let edges: Vec<_> = g.edges_mut().map(|e| e.unwrap_edge_ref()).collect(); + assert_eq!(edges.len(), 2); + assert!(edges.contains(&MutEdge::new(0, 1, &mut e0))); + assert!(edges.contains(&MutEdge::new(1, 0, &mut e1))); + } +} + +#[test] +fn test_neighbors() { + let mut g = DiGraphMap::::new(); + g.add_node(0, Some(0)); + g.add_node(1, Some(0)); + g.add_node(2, None); + + g.add_edge(0, 1, Some(0)); + g.add_edge(0, 2, Some(0)); + g.add_edge(2, 1, None); + + assert_eq!(g.neighbors(0)[..], [1, 2]); + assert_eq!(&g.in_neighbors(1)[..], [0, 2]); + + assert_eq!(g.degree(0), 2); + assert_eq!(g.in_degree(1), 2); + + assert!(g.neighbors(1).is_empty()); + assert!(g.in_neighbors(0).is_empty()); + + assert_eq!(g.degree(1), 0); + assert_eq!(g.in_degree(0), 0); +} + +#[test] +fn test_max_id() { + let mut g = DiGraphMap::::new(); + + assert_eq!(g.max_seen_id(), None); + + g.add_node(1, Some(0)); + assert_eq!(g.max_seen_id(), Some(1)); + + g.add_node(0, Some(0)); + assert_eq!(g.max_seen_id(), Some(1)); + + g.add_node(2, None); + assert_eq!(g.max_seen_id(), Some(2)); + + g.add_edge(3, 4, Some(0)); + assert_eq!(g.max_seen_id(), Some(4)); + + g.add_edge(6, 5, Some(0)); + assert_eq!(g.max_seen_id(), Some(6)); +} + +#[test] +fn test_clone() { + let mut g = DiGraphMap::::new(); + g.add_node(0, Some(0)); + g.add_node(1, Some(0)); + g.add_node(2, Some(1)); + g.add_node(3, None); + + g.add_edge(0, 1, Some(0)); + g.add_edge(1, 2, Some(0)); + g.add_edge(2, 3, Some(1)); + g.add_edge(3, 1, None); + + assert_eq!(g, g.clone()); +} + +#[test] +fn test_stats() { + let mut g = DiGraphMap::::new(); + g.add_node(0, Some(0)); + g.add_node(1, Some(0)); + g.add_node(2, Some(1)); + g.add_node(3, None); + + g.add_edge(0, 1, Some(0)); + g.add_edge(1, 2, Some(0)); + g.add_edge(2, 3, Some(1)); + g.add_edge(3, 1, None); + + let mut expected_counter = HashMap::new(); + expected_counter.insert(0, 2); + expected_counter.insert(1, 1); + + assert_eq!(g.get_node_label_id_counter().into_map(), expected_counter); + assert_eq!(g.get_edge_label_id_counter().into_map(), expected_counter) +} + +#[test] +fn test_total_degree_directed() { + let mut g = DiGraphMap::::new(); + g.add_node(0, Some(0)); + g.add_node(1, Some(0)); + g.add_node(2, Some(0)); + + g.add_edge(0, 1, Some(0)); + g.add_edge(0, 2, Some(0)); + g.add_edge(2, 1, None); + + assert_eq!(g.degree(2), 1); + assert_eq!(g.total_degree(2), 2); +} + +#[test] +fn test_total_degree_undirected() { + let mut g = UnGraphMap::::new(); + g.add_node(0, Some(0)); + g.add_node(1, Some(0)); + g.add_node(2, Some(0)); + + g.add_edge(0, 1, Some(0)); + g.add_edge(0, 2, Some(0)); + g.add_edge(2, 1, None); + + assert_eq!(g.degree(2), 2); + assert_eq!(g.total_degree(2), 2); +} diff --git a/tests/io.rs b/tests/io.rs index 724bbe2f..443ccea8 100644 --- a/tests/io.rs +++ b/tests/io.rs @@ -23,9 +23,10 @@ extern crate tempfile; use rust_graph::graph_gen::{random_gnm_graph, random_gnm_graph_unlabeled}; use rust_graph::graph_impl::{DiGraphMap, GraphMap, UnGraphMap}; +#[cfg(feature = "hdfs")] +use rust_graph::io::hdfs::read_from_hdfs; use rust_graph::io::{read_from_csv, write_to_csv}; use rust_graph::prelude::*; - use tempfile::TempDir; #[test] @@ -42,15 +43,13 @@ fn test_cvs_unlabeled() { assert!(write_to_csv(&g, &path_to_nodes, &path_to_edges).is_ok()); let mut g_ = GraphMap::new(); - assert!( - read_from_csv( - &mut g_, - Some(path_to_nodes), - path_to_edges, - None, - true, - true - ).is_ok() + read_from_csv( + &mut g_, + vec![path_to_nodes], + vec![path_to_edges], + None, + true, + true, ); assert_eq!(g, g_); @@ -60,15 +59,13 @@ fn test_cvs_unlabeled() { assert!(write_to_csv(&g, &path_to_nodes, &path_to_edges).is_ok()); let mut g_ = GraphMap::new(); - assert!( - read_from_csv( - &mut g_, - Some(path_to_nodes), - path_to_edges, - None, - true, - true - ).is_ok() + read_from_csv( + &mut g_, + vec![path_to_nodes], + vec![path_to_edges], + None, + true, + true, ); assert_eq!(g, g_); } @@ -91,15 +88,13 @@ fn test_cvs_labeled() { assert!(write_to_csv(&g, &path_to_nodes, &path_to_edges).is_ok()); let mut g_ = GraphMap::with_label_map(node_labels.into(), edge_labels.into()); - assert!( - read_from_csv( - &mut g_, - Some(path_to_nodes), - path_to_edges, - None, - true, - true - ).is_ok() + read_from_csv( + &mut g_, + vec![path_to_nodes], + vec![path_to_edges], + None, + true, + true, ); assert_eq!(g, g_); @@ -110,15 +105,36 @@ fn test_cvs_labeled() { assert!(write_to_csv(&g, &path_to_nodes, &path_to_edges).is_ok()); let mut g_ = GraphMap::with_label_map(node_labels.into(), edge_labels.into()); - assert!( - read_from_csv( - &mut g_, - Some(path_to_nodes), - path_to_edges, - None, - true, - true - ).is_ok() + read_from_csv( + &mut g_, + vec![path_to_nodes], + vec![path_to_edges], + None, + true, + true, ); assert_eq!(g, g_); } + +/// Because of the requirement of hadoop environment on local, we `ignore` the test here. +/// If you have configure the environment according to `README.md`, use parameter `--ignore` to test it. +#[test] +#[cfg(feature = "hdfs")] +#[ignore] +fn test_csv_hdfs_read() { + let path_to_nodes = "hdfs://localhost:9000/labelled/nodes/"; + let path_to_edges = "hdfs://localhost:9000/labelled/edges/edges.csv"; + let node_labels = &vec!["a".to_owned(), "b".to_owned()]; + let edge_labels = &vec![1, 2, 3]; + let mut g_: DiGraphMap = + GraphMap::with_label_map(node_labels.into(), edge_labels.into()); + + read_from_hdfs( + &mut g_, + vec![path_to_nodes], + vec![path_to_edges], + Some(","), + true, + false, + ); +} diff --git a/tests/map.rs b/tests/map.rs index a90edc23..07fc9f0c 100644 --- a/tests/map.rs +++ b/tests/map.rs @@ -1,95 +1,95 @@ -/* - * Copyright (c) 2018 UNSW Sydney, Data and Knowledge Group. - * - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you 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. - */ -#[macro_use] -extern crate rust_graph; - -use rust_graph::prelude::*; - -use rust_graph::map::{SetMap, VecMap}; - -#[test] -fn test_set_map() { - let mut label_map = SetMap::<&str>::new(); - - assert_eq!(label_map.len(), 0); - - assert_eq!(label_map.add_item("zero"), 0); - assert_eq!(label_map.add_item("first"), 1); - assert_eq!(label_map.add_item("zero"), 0); - assert_eq!(label_map.add_item("first"), 1); - - assert_eq!(label_map.len(), 2); - assert_eq!(label_map.get_item(0), Some(&"zero")); - assert_eq!(label_map.get_item(1), Some(&"first")); - - assert_eq!(label_map.get_item(2), None); - - assert!(label_map.contains(&"zero")); - assert!(!label_map.contains(&"five")); - - assert_eq!(label_map.find_index(&"zero"), Some(0)); - assert_eq!(label_map.find_index(&"first"), Some(1)); - assert_eq!(label_map.find_index(&"five"), None); - - let items: Vec<_> = label_map.items().collect(); - assert_eq!(items, vec![&"zero", &"first"]); -} - -#[test] -fn test_vec_map() { - let mut label_map = VecMap::<&str>::new(); - - assert_eq!(label_map.len(), 0); - - assert_eq!(label_map.add_item("zero"), 0); - assert_eq!(label_map.add_item("first"), 1); - assert_eq!(label_map.add_item("zero"), 0); - assert_eq!(label_map.add_item("first"), 1); - - assert_eq!(label_map.len(), 2); - assert_eq!(label_map.get_item(0), Some(&"zero")); - assert_eq!(label_map.get_item(1), Some(&"first")); - - assert_eq!(label_map.get_item(2), None); - - assert!(label_map.contains(&"zero")); - assert!(!label_map.contains(&"five")); - - assert_eq!(label_map.find_index(&"zero"), Some(0)); - assert_eq!(label_map.find_index(&"first"), Some(1)); - assert_eq!(label_map.find_index(&"five"), None); - - let items: Vec<_> = label_map.items().collect(); - assert_eq!(items, vec![&"zero", &"first"]); -} - -#[test] -fn test_macro() { - let setmap1 = setmap![1u32, 2, 3]; - assert_eq!(setmap1.clone().items_vec(), vec![1u32, 2, 3]); - let vecmap1 = vecmap![1u32, 2, 3]; - assert_eq!(vecmap1.clone().items_vec(), vec![1u32, 2, 3]); - - let setmap2: SetMap<_> = (1u32..4).collect(); - assert_eq!(setmap2, setmap1); - let vecmap2: VecMap<_> = (1u32..4).collect(); - assert_eq!(vecmap2, vecmap1); -} +/* + * Copyright (c) 2018 UNSW Sydney, Data and Knowledge Group. + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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. + */ +#[macro_use] +extern crate rust_graph; + +use rust_graph::prelude::*; + +use rust_graph::map::{SetMap, VecMap}; + +#[test] +fn test_set_map() { + let mut label_map = SetMap::<&str>::new(); + + assert_eq!(label_map.len(), 0); + + assert_eq!(label_map.add_item("zero"), 0); + assert_eq!(label_map.add_item("first"), 1); + assert_eq!(label_map.add_item("zero"), 0); + assert_eq!(label_map.add_item("first"), 1); + + assert_eq!(label_map.len(), 2); + assert_eq!(label_map.get_item(0), Some(&"zero")); + assert_eq!(label_map.get_item(1), Some(&"first")); + + assert_eq!(label_map.get_item(2), None); + + assert!(label_map.contains(&"zero")); + assert!(!label_map.contains(&"five")); + + assert_eq!(label_map.find_index(&"zero"), Some(0)); + assert_eq!(label_map.find_index(&"first"), Some(1)); + assert_eq!(label_map.find_index(&"five"), None); + + let items: Vec<_> = label_map.items().collect(); + assert_eq!(items, vec![&"zero", &"first"]); +} + +#[test] +fn test_vec_map() { + let mut label_map = VecMap::<&str>::new(); + + assert_eq!(label_map.len(), 0); + + assert_eq!(label_map.add_item("zero"), 0); + assert_eq!(label_map.add_item("first"), 1); + assert_eq!(label_map.add_item("zero"), 0); + assert_eq!(label_map.add_item("first"), 1); + + assert_eq!(label_map.len(), 2); + assert_eq!(label_map.get_item(0), Some(&"zero")); + assert_eq!(label_map.get_item(1), Some(&"first")); + + assert_eq!(label_map.get_item(2), None); + + assert!(label_map.contains(&"zero")); + assert!(!label_map.contains(&"five")); + + assert_eq!(label_map.find_index(&"zero"), Some(0)); + assert_eq!(label_map.find_index(&"first"), Some(1)); + assert_eq!(label_map.find_index(&"five"), None); + + let items: Vec<_> = label_map.items().collect(); + assert_eq!(items, vec![&"zero", &"first"]); +} + +#[test] +fn test_macro() { + let setmap1 = setmap![1u32, 2, 3]; + assert_eq!(setmap1.clone().items_vec(), vec![1u32, 2, 3]); + let vecmap1 = vecmap![1u32, 2, 3]; + assert_eq!(vecmap1.clone().items_vec(), vec![1u32, 2, 3]); + + let setmap2: SetMap<_> = (1u32..4).collect(); + assert_eq!(setmap2, setmap1); + let vecmap2: VecMap<_> = (1u32..4).collect(); + assert_eq!(vecmap2, vecmap1); +} diff --git a/tests/static_graph.rs b/tests/static_graph.rs index b8fad7a5..68e7885a 100644 --- a/tests/static_graph.rs +++ b/tests/static_graph.rs @@ -1,228 +1,167 @@ -/* - * Copyright (c) 2018 UNSW Sydney, Data and Knowledge Group. - * - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you 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. - */ -#[macro_use] -extern crate rust_graph; -extern crate tempfile; - -use tempfile::TempDir; - -use rust_graph::generic::DefaultId; -use rust_graph::graph_impl::static_graph::mmap::EdgeVecMmap; -use rust_graph::graph_impl::static_graph::EdgeVecTrait; -use rust_graph::graph_impl::static_graph::StaticNode; -use rust_graph::graph_impl::Edge; -use rust_graph::graph_impl::EdgeVec; -use rust_graph::map::SetMap; -use rust_graph::prelude::*; -use rust_graph::{DiStaticGraph, UnStaticGraph}; - -#[test] -fn test_directed() { - let edge_vec = EdgeVec::new(vec![0, 2, 3, 4], vec![1, 2, 0, 0]); - let in_edge_vec = EdgeVec::new(vec![0, 2, 3, 4], vec![1, 2, 0, 0]); - let g = DiStaticGraph::::new(edge_vec, Some(in_edge_vec), None, None); - - assert_eq!(g.node_count(), 3); - assert_eq!(g.edge_count(), 4); - - assert_eq!(g.neighbors(0)[..], [1, 2]); - assert_eq!(&g.neighbors(1)[..], &[0]); - assert_eq!(&g.neighbors(2)[..], &[0]); - - assert_eq!(g.degree(0), 2); - assert_eq!(g.degree(1), 1); - assert_eq!(g.degree(2), 1); - - assert_eq!(g.in_neighbors(0).into_owned(), vec![1, 2]); - assert_eq!(g.in_neighbors(1).into_owned(), vec![0]); - assert_eq!(g.in_neighbors(2).into_owned(), vec![0]); - - assert_eq!(g.in_degree(0), 2); - assert_eq!(g.in_degree(1), 1); - assert_eq!(g.in_degree(2), 1); - - let node_0 = StaticNode::::new(0, None); - let node_1 = StaticNode::::new(1, None); - let node_2 = StaticNode::::new(2, None); - - let edge_0_1 = Edge::::new(0, 1, None); - let edge_0_2 = Edge::::new(0, 2, None); - let edge_1_0 = Edge::::new(1, 0, None); - let edge_2_0 = Edge::::new(2, 0, None); - - assert_eq!(g.get_node(0).unwrap_staticnode(), node_0); - assert_eq!(g.get_node(1).unwrap_staticnode(), node_1); - assert_eq!(g.get_node(2).unwrap_staticnode(), node_2); - - println!("{:?}------{:?}", &g, g.get_node(3)); - assert!(g.get_node(3).is_none()); - - assert_eq!(g.get_edge(0, 1).unwrap(), edge_0_1); - assert_eq!(g.get_edge(0, 2).unwrap(), edge_0_2); - assert_eq!(g.get_edge(1, 0).unwrap(), edge_1_0); - assert_eq!(g.get_edge(2, 0).unwrap(), edge_2_0); - assert!(g.get_edge(2, 3).is_none()); - - let nodes: Vec<_> = g.nodes().collect(); - assert_eq!(nodes.len(), 3); - assert!(nodes.contains(&g.get_node(0))); - assert!(nodes.contains(&g.get_node(1))); - assert!(nodes.contains(&g.get_node(2))); - - let edges: Vec<_> = g.edges().collect(); - assert_eq!(edges.len(), 4); - assert!(edges.contains(&g.get_edge(0, 1))); - assert!(edges.contains(&g.get_edge(0, 2))); - assert!(edges.contains(&g.get_edge(1, 0))); - assert!(edges.contains(&g.get_edge(2, 0))); -} - -#[test] -fn test_undirected() { - let edge_vec = EdgeVec::new(vec![0, 2, 4, 6], vec![1, 2, 0, 2, 0, 1]); - let g = UnStaticGraph::::new(edge_vec, None, None, None); - let edges: Vec<_> = g.edge_indices().collect(); - assert_eq!(edges, vec![(0, 1), (0, 2), (1, 2)]) -} - -#[test] -fn test_labeled() { - let edge_vec = EdgeVec::with_labels(vec![0, 2, 3, 4], vec![1, 2, 0, 0], vec![0, 1, 0, 1]); - let in_edge_vec = EdgeVec::new(vec![0, 2, 3, 4], vec![1, 2, 0, 0]); - let labels = vec![1, 0, 1]; - let g = DiStaticGraph::<&str>::with_labels( - edge_vec, - Some(in_edge_vec), - labels, - setmap!["a", "b"], - setmap!["a", "b"], - None, - None, - ); - - assert_eq!(g.get_node_label(0), Some(&"b")); - assert_eq!(g.get_node_label(1), Some(&"a")); - assert_eq!(g.get_node_label(2), Some(&"b")); - assert_eq!(g.get_node_label(4), None); - - assert_eq!(g.get_edge_label(0, 1), Some(&"a")); - assert_eq!(g.get_edge_label(0, 2), Some(&"b")); - assert_eq!(g.get_edge_label(1, 0), Some(&"a")); - assert_eq!(g.get_edge_label(2, 0), Some(&"b")); - assert_eq!(g.get_edge_label(2, 3), None); - - let node_0 = StaticNode::new(0 as DefaultId, Some(1)); - let node_1 = StaticNode::new(1 as DefaultId, Some(0)); - let node_2 = StaticNode::new(2 as DefaultId, Some(1)); - - let edge_0_1 = Edge::new(0 as DefaultId, 1, Some(0)); - let edge_0_2 = Edge::new(0 as DefaultId, 2, Some(1)); - let edge_1_0 = Edge::new(1 as DefaultId, 0, Some(0)); - let edge_2_0 = Edge::new(2 as DefaultId, 0, Some(1)); - - assert_eq!(g.get_node(0).unwrap_staticnode(), node_0); - assert_eq!(g.get_node(1).unwrap_staticnode(), node_1); - assert_eq!(g.get_node(2).unwrap_staticnode(), node_2); - assert!(g.get_node(3).is_none()); - - assert_eq!(g.get_edge(0, 1).unwrap(), edge_0_1); - assert_eq!(g.get_edge(0, 2).unwrap(), edge_0_2); - assert_eq!(g.get_edge(1, 0).unwrap(), edge_1_0); - assert_eq!(g.get_edge(2, 0).unwrap(), edge_2_0); - assert!(g.get_edge(2, 3).is_none()); - - let nodes: Vec<_> = g.nodes().collect(); - assert_eq!(nodes.len(), 3); - assert!(nodes.contains(&g.get_node(0))); - assert!(nodes.contains(&g.get_node(1))); - assert!(nodes.contains(&g.get_node(2))); - - let edges: Vec<_> = g.edges().collect(); - assert_eq!(edges.len(), 4); - assert!(edges.contains(&g.get_edge(0, 1))); - assert!(edges.contains(&g.get_edge(0, 2))); - assert!(edges.contains(&g.get_edge(1, 0))); - assert!(edges.contains(&g.get_edge(2, 0))); -} - -#[test] -fn test_clone() { - let edge_vec = EdgeVec::new(vec![0, 2, 3, 4], vec![1, 2, 0, 0]); - let in_edge_vec = EdgeVec::new(vec![0, 2, 3, 4], vec![1, 2, 0, 0]); - let g = DiStaticGraph::::new(edge_vec, Some(in_edge_vec), None, None); - assert_eq!(g, g.clone()); -} - -#[test] -fn test_edge_vec_mmap() { - let offsets = vec![0, 3, 5, 8, 10]; - let edges = vec![1, 2, 3, 0, 2, 0, 1, 3, 0, 2]; - - let tmp_dir = TempDir::new().unwrap(); - let tmp_dir_path = tmp_dir.path(); - let prefix = tmp_dir_path.join("edgevec").to_str().unwrap().to_owned(); - - let edgevec = EdgeVec::::new(offsets, edges); - edgevec.dump_mmap(&prefix).expect("Dump edgevec error"); - - let edgevec_mmap = EdgeVecMmap::::new(&prefix); - - assert_eq!(edgevec.num_nodes(), edgevec_mmap.num_nodes()); - for node in 0..edgevec.num_nodes() as DefaultId { - assert_eq!(edgevec.neighbors(node), edgevec_mmap.neighbors(node)) - } - for node in 0..edgevec.num_nodes() as DefaultId { - for &nbr in edgevec_mmap.neighbors(node) { - assert!(edgevec_mmap.find_edge_label_id(node, nbr).is_none()); - } - } -} - -#[test] -fn test_edge_vec_mmap_label() { - let offsets = vec![0, 3, 5, 8, 10]; - let edges = vec![1, 2, 3, 0, 2, 0, 1, 3, 0, 2]; - let labels = vec![0, 4, 3, 0, 1, 4, 1, 2, 3, 2]; - - let tmp_dir = TempDir::new().unwrap(); - let tmp_dir_path = tmp_dir.path(); - let prefix = tmp_dir_path.join("edgevecl").to_str().unwrap().to_owned(); - - let edgevec = EdgeVec::::with_labels(offsets, edges, labels); - edgevec.dump_mmap(&prefix).expect("Dump edgevec error"); - - let edgevec_mmap = EdgeVecMmap::::new(&prefix); - - assert_eq!(edgevec.num_nodes(), edgevec_mmap.num_nodes()); - for node in 0..edgevec.num_nodes() as DefaultId { - assert_eq!(edgevec.neighbors(node), edgevec_mmap.neighbors(node)) - } - - let expected_label = [[0, 0, 4, 3], [0, 0, 1, 0], [4, 1, 0, 2], [3, 0, 2, 0]]; - for node in 0..edgevec_mmap.num_nodes() as DefaultId { - for &nbr in edgevec_mmap.neighbors(node) { - assert_eq!( - *edgevec_mmap.find_edge_label_id(node, nbr).unwrap(), - expected_label[node.id()][nbr.id()] - ); - } - } -} +/* + * Copyright (c) 2018 UNSW Sydney, Data and Knowledge Group. + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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. + */ +#[macro_use] +extern crate rust_graph; +extern crate tempfile; + +use rust_graph::generic::DefaultId; +use rust_graph::graph_impl::static_graph::StaticNode; +use rust_graph::graph_impl::Edge; +use rust_graph::graph_impl::EdgeVec; +use rust_graph::map::SetMap; +use rust_graph::prelude::*; +use rust_graph::{DiStaticGraph, UnStaticGraph}; + +#[test] +fn test_directed() { + let edge_vec = EdgeVec::new(vec![0, 2, 3, 4], vec![1, 2, 0, 0]); + let in_edge_vec = EdgeVec::new(vec![0, 2, 3, 4], vec![1, 2, 0, 0]); + let g = DiStaticGraph::::new(edge_vec, Some(in_edge_vec), None, None); + + assert_eq!(g.node_count(), 3); + assert_eq!(g.edge_count(), 4); + + assert_eq!(g.neighbors(0)[..], [1, 2]); + assert_eq!(&g.neighbors(1)[..], &[0]); + assert_eq!(&g.neighbors(2)[..], &[0]); + + assert_eq!(g.degree(0), 2); + assert_eq!(g.degree(1), 1); + assert_eq!(g.degree(2), 1); + + assert_eq!(g.in_neighbors(0).into_owned(), vec![1, 2]); + assert_eq!(g.in_neighbors(1).into_owned(), vec![0]); + assert_eq!(g.in_neighbors(2).into_owned(), vec![0]); + + assert_eq!(g.in_degree(0), 2); + assert_eq!(g.in_degree(1), 1); + assert_eq!(g.in_degree(2), 1); + + let node_0 = StaticNode::::new(0, None); + let node_1 = StaticNode::::new(1, None); + let node_2 = StaticNode::::new(2, None); + + let edge_0_1 = Edge::::new(0, 1, None); + let edge_0_2 = Edge::::new(0, 2, None); + let edge_1_0 = Edge::::new(1, 0, None); + let edge_2_0 = Edge::::new(2, 0, None); + + assert_eq!(g.get_node(0).unwrap_staticnode(), node_0); + assert_eq!(g.get_node(1).unwrap_staticnode(), node_1); + assert_eq!(g.get_node(2).unwrap_staticnode(), node_2); + + assert!(g.get_node(3).is_none()); + + assert_eq!(g.get_edge(0, 1).unwrap(), edge_0_1); + assert_eq!(g.get_edge(0, 2).unwrap(), edge_0_2); + assert_eq!(g.get_edge(1, 0).unwrap(), edge_1_0); + assert_eq!(g.get_edge(2, 0).unwrap(), edge_2_0); + assert!(g.get_edge(2, 3).is_none()); + + let nodes: Vec<_> = g.nodes().collect(); + assert_eq!(nodes.len(), 3); + assert!(nodes.contains(&g.get_node(0))); + assert!(nodes.contains(&g.get_node(1))); + assert!(nodes.contains(&g.get_node(2))); + + let edges: Vec<_> = g.edges().collect(); + assert_eq!(edges.len(), 4); + assert!(edges.contains(&g.get_edge(0, 1))); + assert!(edges.contains(&g.get_edge(0, 2))); + assert!(edges.contains(&g.get_edge(1, 0))); + assert!(edges.contains(&g.get_edge(2, 0))); +} + +#[test] +fn test_undirected() { + let edge_vec = EdgeVec::new(vec![0, 2, 4, 6], vec![1, 2, 0, 2, 0, 1]); + let g = UnStaticGraph::::new(edge_vec, None, None, None); + let edges: Vec<_> = g.edge_indices().collect(); + assert_eq!(edges, vec![(0, 1), (0, 2), (1, 2)]) +} + +#[test] +fn test_labeled() { + let edge_vec = EdgeVec::with_labels(vec![0, 2, 3, 4], vec![1, 2, 0, 0], vec![0, 1, 0, 1]); + let in_edge_vec = EdgeVec::new(vec![0, 2, 3, 4], vec![1, 2, 0, 0]); + let labels = vec![1, 0, 1]; + let g = DiStaticGraph::<&str>::with_labels( + edge_vec, + Some(in_edge_vec), + labels, + setmap!["a", "b"], + setmap!["a", "b"], + None, + None, + ); + + assert_eq!(g.get_node_label(0), Some(&"b")); + assert_eq!(g.get_node_label(1), Some(&"a")); + assert_eq!(g.get_node_label(2), Some(&"b")); + assert_eq!(g.get_node_label(4), None); + + assert_eq!(g.get_edge_label(0, 1), Some(&"a")); + assert_eq!(g.get_edge_label(0, 2), Some(&"b")); + assert_eq!(g.get_edge_label(1, 0), Some(&"a")); + assert_eq!(g.get_edge_label(2, 0), Some(&"b")); + assert_eq!(g.get_edge_label(2, 3), None); + + let node_0 = StaticNode::new(0 as DefaultId, Some(1)); + let node_1 = StaticNode::new(1 as DefaultId, Some(0)); + let node_2 = StaticNode::new(2 as DefaultId, Some(1)); + + let edge_0_1 = Edge::new(0 as DefaultId, 1, Some(0)); + let edge_0_2 = Edge::new(0 as DefaultId, 2, Some(1)); + let edge_1_0 = Edge::new(1 as DefaultId, 0, Some(0)); + let edge_2_0 = Edge::new(2 as DefaultId, 0, Some(1)); + + assert_eq!(g.get_node(0).unwrap_staticnode(), node_0); + assert_eq!(g.get_node(1).unwrap_staticnode(), node_1); + assert_eq!(g.get_node(2).unwrap_staticnode(), node_2); + assert!(g.get_node(3).is_none()); + + assert_eq!(g.get_edge(0, 1).unwrap(), edge_0_1); + assert_eq!(g.get_edge(0, 2).unwrap(), edge_0_2); + assert_eq!(g.get_edge(1, 0).unwrap(), edge_1_0); + assert_eq!(g.get_edge(2, 0).unwrap(), edge_2_0); + assert!(g.get_edge(2, 3).is_none()); + + let nodes: Vec<_> = g.nodes().collect(); + assert_eq!(nodes.len(), 3); + assert!(nodes.contains(&g.get_node(0))); + assert!(nodes.contains(&g.get_node(1))); + assert!(nodes.contains(&g.get_node(2))); + + let edges: Vec<_> = g.edges().collect(); + assert_eq!(edges.len(), 4); + assert!(edges.contains(&g.get_edge(0, 1))); + assert!(edges.contains(&g.get_edge(0, 2))); + assert!(edges.contains(&g.get_edge(1, 0))); + assert!(edges.contains(&g.get_edge(2, 0))); +} + +#[test] +fn test_clone() { + let edge_vec = EdgeVec::new(vec![0, 2, 3, 4], vec![1, 2, 0, 0]); + let in_edge_vec = EdgeVec::new(vec![0, 2, 3, 4], vec![1, 2, 0, 0]); + let g = DiStaticGraph::::new(edge_vec, Some(in_edge_vec), None, None); + assert_eq!(g, g.clone()); +} diff --git a/tidb-lightning.toml b/tidb-lightning.toml new file mode 100644 index 00000000..740c87e7 --- /dev/null +++ b/tidb-lightning.toml @@ -0,0 +1,24 @@ +[mydumper.csv] +# 字段分隔符,必须为 ASCII 字符。 +separator = ',' + +# 引用定界符,可以为 ASCII 字符或空字符。 +delimiter = '"' + +# CSV 文件是否包含表头。 +# 如果为 true,首行将会被跳过。 +header = true + +# CSV 是否包含 NULL。 +# 如果为 true,CSV 文件的任何列都不能解析为 NULL。 +not-null = false + +# 如果 `not-null` 为 false(即 CSV 可以包含 NULL), +# 为以下值的字段将会被解析为 NULL。 +null = '\N' + +# 是否解析字段内的反斜线转义符。 +backslash-escape = true + +# 是否移除以分隔符结束的行。 +trim-last-separator = false \ No newline at end of file diff --git a/tikv-importer.toml b/tikv-importer.toml new file mode 100644 index 00000000..6a34b4c2 --- /dev/null +++ b/tikv-importer.toml @@ -0,0 +1,57 @@ +# TiKV Importer 配置文件模版 + +# 日志文件。 +log-file = "tikv-importer.log" +# 日志等级:trace、debug、info、warn、error、off。 +log-level = "info" + +[server] +# tikv-importer 监听的地址,tidb-lightning 需要连到这个地址进行数据写入。 +addr = "127.0.0.1:8287" + +[metric] +# 给 Prometheus 客户端的推送任务名称。 +job = "tikv-importer" +# 给 Prometheus 客户端的推送间隔。 +interval = "15s" +# Prometheus Pushgateway 地址。 +address = "" + +[import] +# 存储引擎文档 (engine file) 的文件夹路径。 +import-dir = "/mnt/ssd/data.import/" + + + + + + + + + +[lightning] + +# 转换数据的并发数,默认为逻辑 CPU 数量,不需要配置。 +# 混合部署的情况下可以配置为逻辑 CPU 的 75% 大小。 +# region-concurrency = + +# 日志 +level = "info" +file = "tidb-lightning.log" + +[tikv-importer] +# tikv-importer 的监听地址,需改成 tikv-importer 服务器的实际地址。 +addr = "127.0.0.1:8287" + +[mydumper] +# Mydumper 源数据目录。 +data-source-dir = "/home/aida/Downloads/tidb/tidb-latest-linux-amd64/tidb-toolkit-v3.0.0-linux-amd64/data/my_database" + +[tidb] +# 目标集群的信息。tidb-server 的监听地址,填一个即可。 +host = "127.0.0.1" +port = 4000 +user = "root" +password = "" +# 表架构信息在从 TiDB 的“状态端口”获取。 +status-port = 10080 \ No newline at end of file