@@ -74,83 +74,58 @@ PYBIND11_MODULE(_netgraph_core, m) {
7474 [](std::int32_t num_nodes,
7575 py::array src, py::array dst,
7676 py::array capacity, py::array cost,
77- bool add_reverse ) {
77+ py::object ext_edge_ids_obj ) {
7878 // public API: src/dst are int32; pass through as int32 to core
7979 auto src_s = as_span<std::int32_t >(src, " src" );
8080 auto dst_s = as_span<std::int32_t >(dst, " dst" );
8181 if (src_s.size () != dst_s.size ()) throw py::type_error (" src and dst must have the same length" );
8282 auto cap_s = as_span<double >(capacity, " capacity" );
8383 // Cost dtype must be int64 to match internal Cost type
8484 auto cost_s = as_span<std::int64_t >(cost, " cost" );
85+ // Optional ext_edge_ids
86+ std::span<const std::int64_t > ext_s;
87+ if (!ext_edge_ids_obj.is_none ()) {
88+ py::array ext_arr = ext_edge_ids_obj.cast <py::array>();
89+ ext_s = as_span<std::int64_t >(ext_arr, " ext_edge_ids" );
90+ }
8591 return StrictMultiDiGraph::from_arrays (num_nodes,
8692 src_s,
8793 dst_s,
88- cap_s, cost_s, add_reverse );
94+ cap_s, cost_s, ext_s );
8995 },
9096 py::arg (" num_nodes" ), py::arg (" src" ), py::arg (" dst" ), py::arg (" capacity" ), py::arg (" cost" ),
91- py::kw_only (), py::arg (" add_reverse " ) = false )
97+ py::kw_only (), py::arg (" ext_edge_ids " ) = py::none () )
9298 .def (" num_nodes" , &StrictMultiDiGraph::num_nodes)
9399 .def (" num_edges" , &StrictMultiDiGraph::num_edges)
94- // external link ids removed; EdgeId is the canonical id
95- .def (" capacity_view" , [](py::object self_obj, const StrictMultiDiGraph& g){
100+ .def (" capacity_view" , [](const StrictMultiDiGraph& g){
96101 auto s = g.capacity_view ();
97- py::array out (
98- py::buffer_info (
99- const_cast <double *>(s.data ()),
100- sizeof (double ),
101- py::format_descriptor<double >::format (),
102- 1 ,
103- { s.size () },
104- { sizeof (double ) }
105- ),
106- self_obj
107- );
108- return out;
102+ py::array_t <double > arr (s.size ());
103+ std::memcpy (arr.mutable_data (), s.data (), s.size ()*sizeof (double ));
104+ return arr;
109105 })
110- .def (" edge_src_view" , [](py::object self_obj, const StrictMultiDiGraph& g){
106+ .def (" edge_src_view" , [](const StrictMultiDiGraph& g){
111107 auto s = g.edge_src_view ();
112- py::array out (
113- py::buffer_info (
114- const_cast <std::int32_t *>(s.data ()),
115- sizeof (std::int32_t ),
116- py::format_descriptor<std::int32_t >::format (),
117- 1 ,
118- { s.size () },
119- { sizeof (std::int32_t ) }
120- ),
121- self_obj
122- );
123- return out;
108+ py::array_t <std::int32_t > arr (s.size ());
109+ std::memcpy (arr.mutable_data (), s.data (), s.size ()*sizeof (std::int32_t ));
110+ return arr;
124111 })
125- .def (" edge_dst_view" , [](py::object self_obj, const StrictMultiDiGraph& g){
112+ .def (" edge_dst_view" , [](const StrictMultiDiGraph& g){
126113 auto s = g.edge_dst_view ();
127- py::array out (
128- py::buffer_info (
129- const_cast <std::int32_t *>(s.data ()),
130- sizeof (std::int32_t ),
131- py::format_descriptor<std::int32_t >::format (),
132- 1 ,
133- { s.size () },
134- { sizeof (std::int32_t ) }
135- ),
136- self_obj
137- );
138- return out;
114+ py::array_t <std::int32_t > arr (s.size ());
115+ std::memcpy (arr.mutable_data (), s.data (), s.size ()*sizeof (std::int32_t ));
116+ return arr;
139117 })
140- .def (" cost_view" , [](py::object self_obj, const StrictMultiDiGraph& g){
118+ .def (" ext_edge_ids_view" , [](const StrictMultiDiGraph& g){
119+ auto s = g.ext_edge_ids_view ();
120+ py::array_t <std::int64_t > arr (s.size ());
121+ std::memcpy (arr.mutable_data (), s.data (), s.size ()*sizeof (std::int64_t ));
122+ return arr;
123+ })
124+ .def (" cost_view" , [](const StrictMultiDiGraph& g){
141125 auto s = g.cost_view ();
142- py::array out (
143- py::buffer_info (
144- const_cast <Cost*>(s.data ()),
145- sizeof (Cost),
146- py::format_descriptor<Cost>::format (),
147- 1 ,
148- { s.size () },
149- { sizeof (Cost) }
150- ),
151- self_obj
152- );
153- return out;
126+ py::array_t <std::int64_t > arr (s.size ());
127+ std::memcpy (arr.mutable_data (), s.data (), s.size ()*sizeof (std::int64_t ));
128+ return arr;
154129 })
155130 .def (" row_offsets_view" , [](const StrictMultiDiGraph& g){
156131 auto s = g.row_offsets_view ();
@@ -183,8 +158,6 @@ PYBIND11_MODULE(_netgraph_core, m) {
183158 return arr;
184159 });
185160
186- // PredDAG properties already defined earlier at L200; avoid duplicate class registration
187-
188161 // Backend and Algorithms
189162
190163 // Opaque wrapper types
@@ -208,21 +181,26 @@ PYBIND11_MODULE(_netgraph_core, m) {
208181 std::int32_t num_nodes,
209182 py::array src, py::array dst,
210183 py::array capacity, py::array cost,
211- bool add_reverse ){
184+ py::object ext_edge_ids_obj ){
212185 // Build graph by value, then move-assign into a shared_ptr to own it
186+ std::span<const std::int64_t > ext_s;
187+ if (!ext_edge_ids_obj.is_none ()) {
188+ py::array ext_arr = ext_edge_ids_obj.cast <py::array>();
189+ ext_s = as_span<std::int64_t >(ext_arr, " ext_edge_ids" );
190+ }
213191 StrictMultiDiGraph gv = StrictMultiDiGraph::from_arrays (
214192 num_nodes,
215193 as_span<std::int32_t >(src, " src" ),
216194 as_span<std::int32_t >(dst, " dst" ),
217195 as_span<double >(capacity, " capacity" ),
218196 as_span<std::int64_t >(cost, " cost" ),
219- add_reverse );
197+ ext_s );
220198 auto sp = std::make_shared<StrictMultiDiGraph>();
221199 *sp = std::move (gv);
222200 auto gh = algs.build_graph (std::static_pointer_cast<const StrictMultiDiGraph>(sp));
223201 // No need to keep a Python-side owner; GraphHandle holds shared ownership
224202 return PyGraph{ gh, py::none (), sp->num_nodes (), sp->num_edges () };
225- }, py::arg (" num_nodes" ), py::arg (" src" ), py::arg (" dst" ), py::arg (" capacity" ), py::arg (" cost" ), py::kw_only (), py::arg (" add_reverse " ) = false )
203+ }, py::arg (" num_nodes" ), py::arg (" src" ), py::arg (" dst" ), py::arg (" capacity" ), py::arg (" cost" ), py::kw_only (), py::arg (" ext_edge_ids " ) = py::none () )
226204 .def (" spf" , [](const Algorithms& algs, const PyGraph& pg, std::int32_t src,
227205 py::object dst, py::object selection_obj, py::object residual_obj,
228206 py::object node_mask, py::object edge_mask, bool multipath){
@@ -348,7 +326,6 @@ PYBIND11_MODULE(_netgraph_core, m) {
348326 py::gil_scoped_release rel; auto out = algs.batch_max_flow (pg.handle , pp, o, node_spans, edge_spans); py::gil_scoped_acquire acq; return out;
349327 }, py::arg (" graph" ), py::arg (" pairs" ), py::kw_only (), py::arg (" node_masks" ) = py::none (), py::arg (" edge_masks" ) = py::none (), py::arg (" flow_placement" ) = FlowPlacement::Proportional, py::arg (" shortest_path" ) = false , py::arg (" with_edge_flows" ) = false , py::arg (" with_reachable" ) = false , py::arg (" with_residuals" ) = false );
350328
351- // resolve_to_paths now exposed as a PredDAG instance method
352329 py::class_<PredDAG>(m, " PredDAG" )
353330 .def_property_readonly (" parent_offsets" , [](const PredDAG& d){
354331 py::array_t <std::int32_t > arr (d.parent_offsets .size ());
@@ -390,7 +367,7 @@ PYBIND11_MODULE(_netgraph_core, m) {
390367 }
391368 return arr;
392369 });
393- // CostBucket/CostDistribution removed in favor of parallel arrays on FlowSummary
370+
394371 py::class_<FlowSummary>(m, " FlowSummary" )
395372 .def_readonly (" total_flow" , &FlowSummary::total_flow)
396373 .def_readonly (" min_cut" , &FlowSummary::min_cut)
@@ -422,9 +399,6 @@ PYBIND11_MODULE(_netgraph_core, m) {
422399 return arr;
423400 });
424401
425- // spf_residual removed; unified spf accepts optional residual and EdgeSelection
426-
427-
428402 // FlowState bindings
429403 py::class_<FlowState>(m, " FlowState" )
430404 .def (py::init<const StrictMultiDiGraph&>())
0 commit comments