Skip to content
GitLab
Menu
Projects
Groups
Snippets
Help
Help
Support
Community forum
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in / Register
Toggle navigation
Menu
Open sidebar
Tiago Peixoto
graph-tool
Commits
e7363032
Commit
e7363032
authored
Jan 25, 2018
by
Tiago Peixoto
Browse files
inference: Fix issues with overlapping SBM and self-loops
parent
54fa4532
Changes
13
Hide whitespace changes
Inline
Side-by-side
doc/test_inference.py
View file @
e7363032
...
...
@@ -24,13 +24,13 @@ graph_tool.inference.set_test(True)
g
=
collection
.
data
[
"football"
]
# add self-loops
for
i
in
range
(
10
):
v
=
numpy
.
random
.
randint
(
g
.
num_vertices
())
g
.
add_edge
(
v
,
v
)
#
for i in range(10):
#
v = numpy.random.randint(g.num_vertices())
#
g.add_edge(v, v)
# add parallel edges
for
e
in
list
(
g
.
edges
())[:
10
]:
g
.
add_edge
(
e
.
source
(),
e
.
target
())
#
for e in list(g.edges())[:10]:
#
g.add_edge(e.source(), e.target())
ec
=
g
.
new_ep
(
"int"
,
randint
(
0
,
10
,
g
.
num_edges
()))
...
...
@@ -88,7 +88,7 @@ for pvals in iter_ranges(pranges):
if
not
deg_corr
and
degree_dl_kind
!=
"uniform"
:
continue
if
overlap
and
degree_dl_kind
!=
"distributed"
:
if
overlap
and
deg_corr
and
degree_dl_kind
!=
"distributed"
:
# FIXME
continue
if
(
rec
is
not
None
or
layered
!=
False
)
and
not
exact
:
...
...
@@ -213,7 +213,7 @@ for pvals in iter_ranges(pranges):
if
not
deg_corr
and
degree_dl_kind
!=
"uniform"
:
continue
if
overlap
and
degree_dl_kind
!=
"distributed"
:
if
overlap
and
deg_corr
and
degree_dl_kind
!=
"distributed"
:
# FIXME
continue
print
(
params
,
file
=
out
)
...
...
src/graph/inference/blockmodel/graph_blockmodel.hh
View file @
e7363032
...
...
@@ -345,6 +345,19 @@ public:
{
eops
([](
auto
&
,
auto
&
){},
[](
auto
&
,
auto
&
){},
[](
auto
delta
,
auto
&
)
{
return
delta
==
0
;
});
if
(
_coupled_state
!=
nullptr
)
{
_p_entries
.
clear
();
std
::
vector
<
double
>
dummy
;
entries_op
(
m_entries
,
_emat
,
[
&
](
auto
r
,
auto
s
,
auto
&
,
auto
delta
,
auto
&
)
{
if
(
delta
==
0
)
return
;
_p_entries
.
emplace_back
(
r
,
s
,
delta
,
dummy
);
});
}
}
else
{
...
...
@@ -352,6 +365,8 @@ public:
{
if
(
delta
!=
0
)
return
false
;
if
(
get
<
0
>
(
edelta
).
empty
())
return
true
;
for
(
size_t
i
=
0
;
i
<
this
->
_rec_types
.
size
();
++
i
)
{
if
(
get
<
0
>
(
edelta
)[
i
]
!=
0
)
...
...
@@ -479,23 +494,26 @@ public:
_Lrecdx
[
i
+
1
]
+=
_recdx
[
i
]
*
_B_E_D
;
}
}
}
if
(
_coupled_state
!=
nullptr
)
{
_p_entries
.
clear
();
entries_op
(
m_entries
,
_emat
,
[
&
](
auto
r
,
auto
s
,
auto
&
,
auto
delta
,
auto
&
edelta
)
{
if
(
delta
==
0
)
return
;
_p_entries
.
emplace_back
(
r
,
s
,
delta
,
get
<
0
>
(
edelta
));
});
if
(
!
_p_entries
.
empty
())
_coupled_state
->
propagate_delta
(
m_entries
.
get_move
().
first
,
m_entries
.
get_move
().
second
,
_p_entries
);
if
(
_coupled_state
!=
nullptr
)
{
_p_entries
.
clear
();
entries_op
(
m_entries
,
_emat
,
[
&
](
auto
r
,
auto
s
,
auto
&
,
auto
delta
,
auto
&
edelta
)
{
if
(
skip
(
delta
,
edelta
))
return
;
_p_entries
.
emplace_back
(
r
,
s
,
delta
,
get
<
0
>
(
edelta
));
});
}
}
// if (_coupled_state != nullptr && !_p_entries.empty())
// {
// _coupled_state->propagate_delta(m_entries.get_move().first,
// m_entries.get_move().second,
// _p_entries);
// }
}
void
propagate_delta
(
size_t
u
,
size_t
v
,
...
...
@@ -1254,6 +1272,7 @@ public:
void
coupled_resize_vertex
(
size_t
v
)
{
_b
.
resize
(
num_vertices
(
_g
));
_bfield
.
resize
(
num_vertices
(
_g
));
init_vertex_weight
(
v
);
_pclabel
.
resize
(
num_vertices
(
_g
));
_ignore_degrees
.
resize
(
num_vertices
(
_g
));
...
...
@@ -1354,13 +1373,16 @@ public:
deltal
/=
2
;
vector
<
int
>
deltam
(
num_vertices
(
_bg
),
0
);
for
(
auto
e
:
in_edges_range
(
v
,
_g
))
if
(
graph_tool
::
is_directed
(
_g
))
{
vertex_t
u
=
source
(
e
,
_g
);
if
(
u
==
v
)
continue
;
vertex_t
s
=
_b
[
u
];
deltam
[
s
]
+=
_eweight
[
e
];
for
(
auto
e
:
in_edges_range
(
v
,
_g
))
{
vertex_t
u
=
source
(
e
,
_g
);
if
(
u
==
v
)
continue
;
vertex_t
s
=
_b
[
u
];
deltam
[
s
]
+=
_eweight
[
e
];
}
}
double
dS
=
0
;
...
...
@@ -1478,17 +1500,14 @@ public:
{
assert
(
size_t
(
_b
[
v
])
==
r
||
r
==
null_group
);
if
(
r
==
nr
)
{
m_entries
.
set_move
(
r
,
nr
,
num_vertices
(
_bg
));
return
0
;
}
if
(
r
!=
null_group
&&
nr
!=
null_group
&&
!
allow_move
(
r
,
nr
))
return
std
::
numeric_limits
<
double
>::
infinity
();
get_move_entries
(
v
,
r
,
nr
,
m_entries
,
[](
auto
)
{
return
false
;
});
if
(
r
==
nr
)
return
0
;
double
dS
=
0
;
if
(
ea
.
adjacency
)
{
...
...
@@ -2111,7 +2130,7 @@ public:
size_t
w
=
0
;
size_t
kout
=
0
,
kin
=
0
;
degs_op
(
v
,
_vweight
,
_eweight
,
_degs
,
_g
,
degs_op
(
v
,
_vweight
,
_eweight
,
_degs
,
_g
,
/// <- look here
[
&
]
(
size_t
din
,
size_t
dout
,
auto
c
)
{
kout
+=
dout
*
c
;
...
...
@@ -2139,7 +2158,7 @@ public:
int
mst
=
mts
;
int
mtm
=
mtp
;
if
(
is_directed_
::
apply
<
g_t
>::
type
::
value
)
if
(
graph_tool
::
is_directed
(
_g
)
)
{
mst
=
0
;
const
auto
&
me
=
m_entries
.
get_me
(
s
,
t
,
_emat
);
...
...
@@ -2152,7 +2171,7 @@ public:
{
int
dts
=
m_entries
.
get_delta
(
t
,
s
);
int
dst
=
dts
;
if
(
is_directed_
::
apply
<
g_t
>::
type
::
value
)
if
(
graph_tool
::
is_directed
(
_g
)
)
dst
=
m_entries
.
get_delta
(
s
,
t
);
mts
+=
dts
;
...
...
@@ -2171,7 +2190,7 @@ public:
}
}
if
(
is_directed_
::
apply
<
g_t
>::
type
::
value
)
if
(
graph_tool
::
is_directed
(
_g
)
)
{
p
+=
ew
*
((
mts
+
mst
+
c
)
/
(
mtp
+
mtm
+
c
*
B
));
}
...
...
@@ -2191,11 +2210,14 @@ public:
sum_prob
(
e
,
target
(
e
,
_g
));
}
for
(
auto
e
:
in_edges_range
(
v
,
_g
))
if
(
graph_tool
::
is_directed
(
_g
))
{
if
(
source
(
e
,
_g
)
==
v
)
continue
;
sum_prob
(
e
,
source
(
e
,
_g
));
for
(
auto
e
:
in_edges_range
(
v
,
_g
))
{
if
(
source
(
e
,
_g
)
==
v
)
continue
;
sum_prob
(
e
,
source
(
e
,
_g
));
}
}
if
(
w
>
0
)
...
...
src/graph/inference/blockmodel/graph_blockmodel_elist.hh
View file @
e7363032
...
...
@@ -201,8 +201,11 @@ public:
// update the half-edge lists
for
(
auto
e
:
out_edges_range
(
v
,
g
))
remove_edge
(
e
,
b
,
g
);
for
(
auto
e
:
in_edges_range
(
v
,
g
))
remove_edge
(
e
,
b
,
g
);
if
(
graph_tool
::
is_directed
(
g
))
{
for
(
auto
e
:
in_edges_range
(
v
,
g
))
remove_edge
(
e
,
b
,
g
);
}
}
template
<
class
Vertex
,
class
Vprop
,
class
Eprop
>
...
...
@@ -213,8 +216,11 @@ public:
//update the half-edge lists
for
(
auto
e
:
out_edges_range
(
v
,
g
))
insert_edge
(
e
,
eweight
[
e
],
b
,
g
);
for
(
auto
e
:
in_edges_range
(
v
,
g
))
insert_edge
(
e
,
eweight
[
e
],
b
,
g
);
if
(
graph_tool
::
is_directed
(
g
))
{
for
(
auto
e
:
in_edges_range
(
v
,
g
))
insert_edge
(
e
,
eweight
[
e
],
b
,
g
);
}
}
template
<
class
Edge
,
class
RNG
>
...
...
src/graph/inference/blockmodel/graph_blockmodel_entries.hh
View file @
e7363032
...
...
@@ -24,7 +24,7 @@ namespace std
template
<
class
T
,
class
V
>
void
operator
+=
(
vector
<
T
>&
ret
,
const
V
&
v
)
{
ret
.
resize
(
max
(
ret
.
size
(),
v
.
size
()));
ret
.
resize
(
std
::
max
(
ret
.
size
(),
v
.
size
()));
for
(
size_t
i
=
0
;
i
<
v
.
size
();
++
i
)
ret
[
i
]
+=
v
[
i
];
}
...
...
@@ -32,7 +32,7 @@ void operator+=(vector<T>& ret, const V& v)
template
<
class
T
,
class
V
>
void
operator
-=
(
vector
<
T
>&
ret
,
const
V
&
v
)
{
ret
.
resize
(
max
(
ret
.
size
(),
v
.
size
()));
ret
.
resize
(
std
::
max
(
ret
.
size
(),
v
.
size
()));
for
(
size_t
i
=
0
;
i
<
v
.
size
();
++
i
)
ret
[
i
]
-=
v
[
i
];
}
...
...
@@ -161,7 +161,7 @@ public:
auto
&
out_field
=
First
?
_r_out_field
:
_nr_out_field
;
if
(
is_directed_
::
apply
<
Graph
>::
type
::
value
)
{
auto
&
in_field
=
(
First
?
_r_in_field
:
_nr_in_field
);
auto
&
in_field
=
(
First
?
_r_in_field
:
_nr_in_field
);
return
(
Source
||
s
==
t
)
?
out_field
[
t
]
:
in_field
[
s
];
}
else
...
...
@@ -191,7 +191,7 @@ public:
f
=
_entries
.
size
();
_entries
.
emplace_back
(
s
,
t
);
_delta
.
emplace_back
();
if
(
sizeof
...(
delta
)
>
0
)
//
if (sizeof...(delta) > 0)
_edelta
.
emplace_back
();
}
...
...
@@ -251,7 +251,8 @@ public:
const
vector
<
pair
<
size_t
,
size_t
>>&
get_entries
()
{
return
_entries
;
}
const
vector
<
int
>&
get_delta
()
{
return
_delta
;
}
const
vector
<
std
::
tuple
<
EVals
...
>>&
get_edelta
()
{
return
_edelta
;
}
const
vector
<
std
::
tuple
<
EVals
...
>>&
get_edelta
()
{
//_edelta.resize(_delta.size());
return
_edelta
;
}
template
<
class
Emat
>
vector
<
bedge_t
>&
get_mes
(
Emat
&
emat
)
...
...
@@ -261,7 +262,7 @@ public:
{
auto
&
rs
=
_entries
[
i
];
_mes
.
push_back
(
emat
.
get_me
(
rs
.
first
,
rs
.
second
));
assert
(
_mes
.
back
()
!=
emat
.
get_null_edge
()
||
get
<
0
>
(
_delta
[
i
]
)
>=
0
);
assert
(
_mes
.
back
()
!=
emat
.
get_null_edge
()
||
_delta
[
i
]
>=
0
);
}
return
_mes
;
}
...
...
@@ -425,6 +426,9 @@ void move_entries(Vertex v, size_t r, size_t nr, VProp& _b, Graph& g,
{
m_entries
.
set_move
(
r
,
nr
,
B
);
if
(
r
==
nr
)
return
;
if
(
r
!=
null_group
)
{
if
(
nr
!=
null_group
)
...
...
@@ -459,9 +463,7 @@ void entries_op(MEntries& m_entries, EMat& emat, OP&& op)
auto
&
entry
=
entries
[
i
];
auto
er
=
entry
.
first
;
auto
es
=
entry
.
second
;
op
(
er
,
es
,
mes
[
i
],
delta
[
i
],
edelta
[
i
]);
// warning: edelta may be
// empty, but should be
// eliminated after compilation
op
(
er
,
es
,
mes
[
i
],
delta
[
i
],
edelta
[
i
]);
}
}
...
...
src/graph/inference/layers/graph_blockmodel_layers.cc
View file @
e7363032
...
...
@@ -445,12 +445,15 @@ ldegs_map_t get_layered_block_degs(GraphInterface& gi, boost::any aeweight,
ls
.
insert
(
l
);
}
for
(
auto
e
:
in_edges_range
(
v
,
g
))
if
(
graph_tool
::
is_directed
(
g
))
{
auto
w
=
eweight
[
e
];
auto
l
=
ec
[
e
];
kin
[
l
]
+=
w
;
ls
.
insert
(
l
);
for
(
auto
e
:
in_edges_range
(
v
,
g
))
{
auto
w
=
eweight
[
e
];
auto
l
=
ec
[
e
];
kin
[
l
]
+=
w
;
ls
.
insert
(
l
);
}
}
for
(
auto
l
:
ls
)
...
...
src/graph/inference/layers/graph_blockmodel_layers_util.hh
View file @
e7363032
...
...
@@ -34,22 +34,16 @@ double virtual_move_covariate(size_t v, size_t r, size_t s, State& state,
if
(
reset
)
state
.
get_move_entries
(
v
,
r
,
s
,
m_entries
);
auto
&
entries
=
m_entries
.
get_entries
();
auto
&
delta
=
m_entries
.
get_delta
();
double
dS
=
0
;
for
(
size_t
i
=
0
;
i
<
entries
.
size
();
++
i
)
{
auto
&
entry
=
entries
[
i
];
auto
er
=
entry
.
first
;
auto
es
=
entry
.
second
;
int
d
=
delta
[
i
];
int
ers
=
get_beprop
(
er
,
es
,
state
.
_mrs
,
state
.
_emat
);
assert
(
ers
+
d
>=
0
);
dS
-=
-
lgamma_fast
(
ers
+
1
);
dS
+=
-
lgamma_fast
(
ers
+
d
+
1
);
}
entries_op
(
m_entries
,
state
.
_emat
,
[
&
](
auto
,
auto
,
auto
&
me
,
auto
d
,
auto
&
)
{
int
ers
=
(
me
!=
state
.
_emat
.
get_null_edge
())
?
state
.
_mrs
[
me
]
:
0
;
assert
(
ers
+
d
>=
0
);
dS
-=
-
lgamma_fast
(
ers
+
1
);
dS
+=
-
lgamma_fast
(
ers
+
d
+
1
);
});
return
dS
;
}
...
...
src/graph/inference/overlap/graph_blockmodel_overlap.hh
View file @
e7363032
...
...
@@ -483,7 +483,7 @@ public:
}
if
(
is_partition_stats_enabled
())
get_partition_stats
(
v
).
move_vertex
(
v
,
r
,
nr
,
_deg_corr
,
_g
);
get_partition_stats
(
v
).
move_vertex
(
v
,
r
,
nr
,
_g
);
remove_vertex
(
v
);
add_vertex
(
v
,
nr
);
...
...
@@ -567,9 +567,6 @@ public:
int
dwr
=
_wr
[
r
]
-
_overlap_stats
.
virtual_remove_size
(
v
,
r
,
kin
,
kout
);
int
dwnr
=
_overlap_stats
.
virtual_add_size
(
v
,
nr
)
-
_wr
[
nr
];
if
(
_deg_corr
)
dS
+=
_overlap_stats
.
virtual_move_dS
(
v
,
r
,
nr
,
_g
,
kin
,
kout
);
if
(
multigraph
)
dS
+=
_overlap_stats
.
virtual_move_parallel_dS
(
v
,
r
,
nr
,
_b
,
_g
);
...
...
@@ -631,6 +628,9 @@ public:
dS
=
virtual_move_sparse
<
true
>
(
v
,
nr
,
ea
.
multigraph
,
m_entries
);
else
dS
=
virtual_move_sparse
<
false
>
(
v
,
nr
,
ea
.
multigraph
,
m_entries
);
if
(
_deg_corr
&&
ea
.
deg_entropy
)
dS
+=
_overlap_stats
.
virtual_move_deg_dS
(
v
,
r
,
nr
,
_g
);
}
double
dS_dl
=
0
;
...
...
@@ -1425,12 +1425,15 @@ public:
break
;
}
for
(
auto
e
:
in_edges_range
(
t
,
g
))
if
(
graph_tool
::
is_directed
(
g
))
{
if
(
!
be
[
e
].
empty
()
||
source
(
e
,
g
)
!=
s
)
continue
;
be
[
e
]
=
{
_b
[
u
],
_b
[
v
]};
break
;
for
(
auto
e
:
in_edges_range
(
t
,
g
))
{
if
(
!
be
[
e
].
empty
()
||
source
(
e
,
g
)
!=
s
)
continue
;
be
[
e
]
=
{
_b
[
u
],
_b
[
v
]};
break
;
}
}
}
}
...
...
src/graph/inference/overlap/graph_blockmodel_overlap_partition.hh
View file @
e7363032
...
...
@@ -868,9 +868,41 @@ struct overlap_partition_stats_t
return
S_a
-
S_b
;
}
// template <class Graph, class EWeight>
// double get_delta_deg(size_t v, size_t r, size_t nr, const EWeight&,
// const Graph& g, size_t in_deg = 0,
// size_t out_deg = 0)
// {
// if (r == nr)
// return 0;
// r = get_r(r);
// nr = get_r(nr);
// double S_b = 0, S_a = 0;
// size_t u = get_v(_overlap_stats.get_node(v));
// auto& bv = _bvs[u];
// bv_t n_bv;
// const cdeg_t& deg = _degs[u];
// cdeg_t n_deg;
// get_n_bv(v, r, nr, bv, deg, n_bv, n_deg, g, in_deg,
// out_deg);
// for (auto& k : deg)
// S_b -= lgamma_fast(get<0>(k) + 1) + lgamma_fast(get<1>(k) + 1);
// for (auto& k : n_deg)
// S_a -= lgamma_fast(get<0>(k) + 1) + lgamma_fast(get<1>(k) + 1);
// return S_a - S_b;
// }
template
<
class
Graph
>
void
move_vertex
(
size_t
v
,
size_t
r
,
size_t
nr
,
bool
,
Graph
&
g
,
size_t
in_deg
=
0
,
size_t
out_deg
=
0
)
void
move_vertex
(
size_t
v
,
size_t
r
,
size_t
nr
,
Graph
&
g
,
size_t
in_deg
=
0
,
size_t
out_deg
=
0
)
{
if
(
r
==
nr
)
return
;
...
...
src/graph/inference/overlap/graph_blockmodel_overlap_util.hh
View file @
e7363032
...
...
@@ -70,8 +70,11 @@ public:
for
(
auto
e
:
out_edges_range
(
v
,
g
))
_out_neighbors
[
v
]
=
target
(
e
,
g
);
for
(
auto
e
:
in_edges_range
(
v
,
g
))
_in_neighbors
[
v
]
=
source
(
e
,
g
);
if
(
graph_tool
::
is_directed
(
g
))
{
for
(
auto
e
:
in_edges_range
(
v
,
g
))
_in_neighbors
[
v
]
=
source
(
e
,
g
);
}
}
// parallel edges
...
...
@@ -87,8 +90,6 @@ public:
auto
w
=
_out_neighbors
[
u
];
if
(
w
==
_null
)
continue
;
if
(
!
graph_tool
::
is_directed
(
g
)
&&
size_t
(
node_index
[
w
])
<
i
)
continue
;
out_us
[
node_index
[
w
]].
push_back
(
u
);
}
...
...
@@ -96,21 +97,34 @@ public:
{
if
(
uc
.
second
.
size
()
>
1
)
{
_parallel_bundles
.
resize
(
_parallel_bundles
.
size
()
+
1
);
auto
&
h
=
_parallel_bundles
.
back
();
_parallel_bundles
.
emplace_back
();
for
(
auto
u
:
uc
.
second
)
{
auto
w
=
_out_neighbors
[
u
];
assert
(
w
!=
_null
);
_mi
[
u
]
=
_mi
[
w
]
=
_parallel_bundles
.
size
()
-
1
;
size_t
r
=
b
[
u
];
size_t
s
=
b
[
w
];
if
(
!
graph_tool
::
is_directed
(
g
)
&&
r
>
s
)
std
::
swap
(
r
,
s
);
h
[
std
::
make_tuple
(
r
,
s
,
!
graph_tool
::
is_directed
(
g
)
&&
node_index
[
u
]
==
node_index
[
w
])]
++
;
bool
is_loop
=
(
!
graph_tool
::
is_directed
(
g
)
&&
(
node_index
[
u
]
==
node_index
[
w
]));
auto
k
=
std
::
make_tuple
(
r
,
s
,
is_loop
);
if
(
_mi
[
w
]
==
-
1
)
{
_mi
[
w
]
=
_parallel_bundles
.
size
()
-
1
;
auto
&
h
=
_parallel_bundles
.
back
();
h
[
k
]
++
;
}
else
if
(
is_loop
)
{
auto
&
h
=
_parallel_bundles
[
_mi
[
w
]];
h
[
k
]
++
;
}
_mi
[
u
]
=
_mi
[
w
];
}
if
(
_parallel_bundles
.
back
().
empty
())
_parallel_bundles
.
pop_back
();
}
}
}
...
...
@@ -131,24 +145,27 @@ public:
if
(
m
!=
-
1
)
{
size_t
r
,
s
;
auto
u
=
_out_neighbors
[
v
];
if
(
u
==
_null
)
auto
w
=
_out_neighbors
[
v
];
if
(
w
==
_null
)
{
u
=
_in_neighbors
[
v
];
r
=
b
[
u
];
w
=
_in_neighbors
[
v
];
r
=
b
[
w
];
s
=
v_r
;
}
else
{
r
=
v_r
;
s
=
b
[
u
];
s
=
b
[
w
];
}
auto
&
h
=
_parallel_bundles
[
m
];
if
(
!
graph_tool
::
is_directed
(
g
)
&&
r
>
s
)
std
::
swap
(
r
,
s
);
h
[
std
::
make_tuple
(
r
,
s
,
!
graph_tool
::
is_directed
(
g
)
&&
_node_index
[
u
]
==
_node_index
[
v
])]
++
;
bool
is_loop
=
(
!
graph_tool
::
is_directed
(
g
)
&&
(
_node_index
[
w
]
==
_node_index
[
v
]));
if
(
is_loop
)
h
[
std
::
make_tuple
(
r
,
s
,
is_loop
)]
+=
2
;
else
h
[
std
::
make_tuple
(
r
,
s
,
is_loop
)]
++
;
}
}
...
...
@@ -170,26 +187,29 @@ public:
if
(
m
!=
-
1
)
{
size_t
r
,
s
;
auto
u
=
_out_neighbors
[
v
];
if
(
u
==
_null
)
auto
w
=
_out_neighbors
[
v
];
if
(
w
==
_null
)
{
u
=
_in_neighbors
[
v
];
r
=
b
[
u
];
w
=
_in_neighbors
[
v
];
r
=
b
[
w
];
s
=
v_r
;
}
else
{
r
=
v_r
;
s
=
b
[
u
];
s
=
b
[
w
];
}
auto
&
h
=
_parallel_bundles
[
m
];
if
(
!
graph_tool
::
is_directed
(
g
)
&&
r
>
s
)