1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
use alloc::{vec, vec::Vec};
use num_traits::AsPrimitive;
use super::{NodeScorer, motifs::MotifCountOrdering};
use crate::{
impls::SortedIterator,
traits::{
UndirectedMonopartiteMonoplexGraph,
algorithms::node_ordering::motifs::build_undirected_motif_context,
},
};
pub(super) fn triangle_counts<G>(graph: &G) -> Vec<usize>
where
G: UndirectedMonopartiteMonoplexGraph,
{
triangle_counts_with_ordering(graph, MotifCountOrdering::IncreasingDegree)
}
pub(super) fn triangle_counts_with_ordering<G>(
graph: &G,
ordering: MotifCountOrdering,
) -> Vec<usize>
where
G: UndirectedMonopartiteMonoplexGraph,
{
let context = build_undirected_motif_context(graph, ordering);
let n = context.nodes.len();
if n == 0 {
return Vec::new();
}
let mut counts = vec![0usize; n];
for root_index in 0..n {
if !context.in_cover[root_index] {
continue;
}
let root_node = context.nodes[root_index];
for neighbor in graph.neighbors(root_node) {
let neighbor_index = neighbor.as_();
if neighbor_index == root_index
|| !context.in_cover[neighbor_index]
|| context.rank[neighbor_index] >= context.rank[root_index]
{
continue;
}
for common in graph.neighbors(root_node).sorted_intersection(graph.neighbors(neighbor))
{
let common_index = common.as_();
if common_index == root_index || common_index == neighbor_index {
continue;
}
if context.in_cover[common_index]
&& context.rank[common_index] >= context.rank[neighbor_index]
{
continue;
}
counts[root_index] += 1;
counts[neighbor_index] += 1;
counts[common_index] += 1;
}
}
}
counts
}
/// Triangle-count scorer.
///
/// The score of a node is the number of distinct triangles incident to that
/// node. The ordering selects the paper's vertex-cover construction heuristic.
/// [`MotifCountOrdering::IncreasingDegree`] is the preferred and default mode
/// for triangle counting.
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub struct TriangleCountScorer {
ordering: MotifCountOrdering,
}
impl TriangleCountScorer {
/// Creates a triangle-count scorer with the selected cover-ordering
/// heuristic.
///
/// # Examples
/// ```
/// use geometric_traits::{
/// impls::{CSR2D, SortedVec, SymmetricCSR2D},
/// prelude::*,
/// traits::{
/// SquareMatrix, VocabularyBuilder,
/// algorithms::{
/// MotifCountOrdering, NodeScorer, TriangleCountScorer, randomized_graphs::cycle_graph,
/// },
/// },
/// };
///
/// fn wrap_undi(matrix: SymmetricCSR2D<CSR2D<usize, usize, usize>>) -> UndiGraph<usize> {
/// let nodes: SortedVec<usize> = GenericVocabularyBuilder::default()
/// .expected_number_of_symbols(matrix.order())
/// .symbols((0..matrix.order()).enumerate())
/// .build()
/// .unwrap();
/// UndiGraph::from((nodes, matrix))
/// }
///
/// let graph = wrap_undi(cycle_graph(3));
/// let scorer = TriangleCountScorer::new(MotifCountOrdering::IncreasingDegree);
///
/// assert_eq!(scorer.score_nodes(&graph), vec![1, 1, 1]);
/// ```
#[inline]
#[must_use]
pub const fn new(ordering: MotifCountOrdering) -> Self {
Self { ordering }
}
/// Returns the selected cover-ordering heuristic.
///
/// # Examples
/// ```
/// use geometric_traits::traits::algorithms::{MotifCountOrdering, TriangleCountScorer};
///
/// let scorer = TriangleCountScorer::new(MotifCountOrdering::IncreasingDegree);
/// assert_eq!(scorer.ordering(), MotifCountOrdering::IncreasingDegree);
/// ```
#[inline]
#[must_use]
pub const fn ordering(self) -> MotifCountOrdering {
self.ordering
}
}
impl Default for TriangleCountScorer {
#[inline]
fn default() -> Self {
Self::new(MotifCountOrdering::IncreasingDegree)
}
}
impl<G> NodeScorer<G> for TriangleCountScorer
where
G: UndirectedMonopartiteMonoplexGraph,
{
type Score = usize;
fn score_nodes(&self, graph: &G) -> Vec<Self::Score> {
triangle_counts_with_ordering(graph, self.ordering)
}
}