use std::collections::HashMap;
use std::path::Path;
use std::sync::Arc;
use anyhow::{bail, Result};
use dsi_progress_logger::{concurrent_progress_logger, ProgressLog};
use rayon::prelude::*;
use sux::dict::elias_fano::{EfSeqDict, EliasFano, EliasFanoBuilder};
use sux::traits::indexed_dict::{IndexedDict, IndexedSeq};
use crate::graph::*;
use crate::mph::SwhidMphf;
use crate::properties;
use crate::{NodeType, OutOfBoundError, SWHID};
mod contents;
mod iterators;
mod label_names;
mod maps;
mod persons;
mod strings;
mod timestamps;
pub trait ContractionBackend
where
for<'a> Self: IndexedSeq<Input = NodeId, Output<'a> = NodeId>
+ IndexedDict<Input = NodeId, Output<'a> = NodeId>,
{
}
impl<B> ContractionBackend for B where
for<'a> Self: IndexedSeq<Input = NodeId, Output<'a> = NodeId>
+ IndexedDict<Input = NodeId, Output<'a> = NodeId>
{
}
pub unsafe trait MonotoneContractionBackend: ContractionBackend {}
unsafe impl<H, L> MonotoneContractionBackend for EliasFano<H, L> where Self: ContractionBackend {}
unsafe impl<N: MonotoneContractionBackend> MonotoneContractionBackend for &N {}
#[derive(Copy, Clone, Debug)]
pub struct Contraction<N>(pub N)
where
for<'a> N: IndexedSeq<Input = NodeId, Output<'a> = NodeId>;
impl<N> Contraction<N>
where
for<'a> N: IndexedSeq<Input = NodeId, Output<'a> = NodeId>,
{
#[inline(always)]
pub fn underlying_node_id(&self, self_node: NodeId) -> NodeId {
self.0.get(self_node)
}
pub fn num_nodes(&self) -> usize {
self.0.len()
}
}
impl<N: ContractionBackend> Contraction<N> {
#[inline(always)]
pub fn node_id_from_underlying(&self, underlying_node: NodeId) -> Option<NodeId> {
self.0.index_of(underlying_node)
}
}
pub struct ContiguousSubgraph<
G: SwhGraph,
N: ContractionBackend,
MAPS: properties::MaybeMaps,
TIMESTAMPS: properties::MaybeTimestamps,
PERSONS: properties::MaybePersons,
CONTENTS: properties::MaybeContents,
STRINGS: properties::MaybeStrings,
LABELNAMES: properties::MaybeLabelNames,
> {
inner: Arc<ContiguousSubgraphInner<G, N>>,
properties:
properties::SwhGraphProperties<MAPS, TIMESTAMPS, PERSONS, CONTENTS, STRINGS, LABELNAMES>,
}
impl<G: SwhGraphWithProperties, N: ContractionBackend>
ContiguousSubgraph<
G,
N,
properties::NoMaps,
properties::NoTimestamps,
properties::NoPersons,
properties::NoContents,
properties::NoStrings,
properties::NoLabelNames,
>
{
pub fn new_from_contraction(graph: G, contraction: Contraction<N>) -> Self {
let path = graph.properties().path.clone();
let num_nodes = contraction.num_nodes();
let inner = Arc::new(ContiguousSubgraphInner {
underlying_graph: graph,
contraction,
});
Self {
properties: properties::SwhGraphProperties {
path,
num_nodes,
maps: properties::NoMaps,
timestamps: properties::NoTimestamps,
persons: properties::NoPersons,
contents: properties::NoContents,
strings: properties::NoStrings,
label_names: properties::NoLabelNames,
label_names_are_in_base64_order: Default::default(),
},
inner,
}
}
}
impl<G: SwhGraph, N: ContractionBackend>
ContiguousSubgraph<
G,
N,
properties::NoMaps,
properties::NoTimestamps,
properties::NoPersons,
properties::NoContents,
properties::NoStrings,
properties::NoLabelNames,
>
{
pub fn underlying_graph(&self) -> &G {
&self.inner.underlying_graph
}
pub fn contraction(&self) -> &Contraction<N> {
&self.inner.contraction
}
pub fn into_parts(self) -> (G, Contraction<N>) {
let inner = Arc::into_inner(self.inner).expect("Dangling references to ContiguousSubgraph::inner for ContiguousSubgraph with no properties");
(inner.underlying_graph, inner.contraction)
}
}
impl<G: SwhGraphWithProperties>
ContiguousSubgraph<
G,
EfSeqDict,
properties::NoMaps,
properties::NoTimestamps,
properties::NoPersons,
properties::NoContents,
properties::NoStrings,
properties::NoLabelNames,
>
{
pub fn new_from_noncontiguous_graph(graph: G) -> Self
where
G: Sync,
{
let actual_num_nodes = graph.actual_num_nodes().unwrap_or_else(|_| {
let mut pl = concurrent_progress_logger!(
item_name = "node",
expected_updates = Some(graph.num_nodes()),
);
pl.start("Computing number of nodes");
let actual_num_nodes = graph
.par_iter_nodes(pl.clone())
.filter(|&node| graph.has_node(node))
.count();
pl.done();
actual_num_nodes
});
let mut nodes_efb = EliasFanoBuilder::new(actual_num_nodes, graph.num_nodes());
for node in 0..graph.num_nodes() {
if graph.has_node(node) {
nodes_efb.push(node);
}
}
let contraction = Contraction(nodes_efb.build_with_seq_and_dict());
Self::new_from_contraction(graph, contraction)
}
}
struct ContiguousSubgraphInner<G: SwhGraph, N: ContractionBackend> {
underlying_graph: G,
contraction: Contraction<N>,
}
impl<
G: SwhGraph,
N: ContractionBackend,
MAPS: properties::MaybeMaps,
TIMESTAMPS: properties::MaybeTimestamps,
PERSONS: properties::MaybePersons,
CONTENTS: properties::MaybeContents,
STRINGS: properties::MaybeStrings,
LABELNAMES: properties::MaybeLabelNames,
> SwhGraph
for ContiguousSubgraph<G, N, MAPS, TIMESTAMPS, PERSONS, CONTENTS, STRINGS, LABELNAMES>
{
#[inline(always)]
fn path(&self) -> &Path {
self.inner.underlying_graph.path()
}
#[inline(always)]
fn is_transposed(&self) -> bool {
self.inner.underlying_graph.is_transposed()
}
#[inline(always)]
fn num_nodes(&self) -> usize {
self.inner.contraction.num_nodes()
}
fn has_node(&self, node_id: NodeId) -> bool {
node_id < self.num_nodes()
&& self
.inner
.underlying_graph
.has_node(self.inner.contraction.underlying_node_id(node_id))
}
#[inline(always)]
fn num_arcs(&self) -> u64 {
self.inner.underlying_graph.num_arcs()
}
fn num_nodes_by_type(&self) -> Result<HashMap<NodeType, usize>> {
bail!("num_nodes_by_type is not supported by ContiguousSubgraph")
}
fn num_arcs_by_type(&self) -> Result<HashMap<(NodeType, NodeType), usize>> {
bail!("num_arcs_by_type is not supported by ContiguousSubgraph")
}
fn has_arc(&self, src_node_id: NodeId, dst_node_id: NodeId) -> bool {
self.inner.underlying_graph.has_arc(
self.inner.contraction.underlying_node_id(src_node_id),
self.inner.contraction.underlying_node_id(dst_node_id),
)
}
}
impl<
G: SwhGraphWithProperties,
N: ContractionBackend,
MAPS: properties::MaybeMaps,
TIMESTAMPS: properties::MaybeTimestamps,
PERSONS: properties::MaybePersons,
CONTENTS: properties::MaybeContents,
STRINGS: properties::MaybeStrings,
LABELNAMES: properties::MaybeLabelNames,
> SwhGraphWithProperties
for ContiguousSubgraph<G, N, MAPS, TIMESTAMPS, PERSONS, CONTENTS, STRINGS, LABELNAMES>
{
type Maps = MAPS;
type Timestamps = TIMESTAMPS;
type Persons = PERSONS;
type Contents = CONTENTS;
type Strings = STRINGS;
type LabelNames = LABELNAMES;
#[inline(always)]
fn properties(
&self,
) -> &properties::SwhGraphProperties<
Self::Maps,
Self::Timestamps,
Self::Persons,
Self::Contents,
Self::Strings,
Self::LabelNames,
> {
&self.properties
}
}