java-topology/defects/open3d/patch/open3d-0001-pose-graph-connectivity-find.md

92 lines
3.8 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# UNDF: UNDF-2026-000000482
# open3d-0001: ValidatePoseGraphConnectivity — O(V²×E) std::find on component vector inside BFS×edge scan
## Location
`cpp/open3d/pipelines/registration/GlobalOptimization.cpp` lines 367404
Repository: https://github.com/isl-org/Open3D
## Severity
**HIGH** — Called twice during pose graph validation (for all edges, then certain edges only) before every global optimization run. With V nodes and E edges in the pose graph, the BFS expands V nodes, each scanning all E edges, and for each adjacent node does a O(V) `std::find` on `component`. Total: O(V²×E). For large 3D reconstruction datasets (thousands of frames/cameras), this is a significant pre-optimization bottleneck.
## Complexity
- Before: O(V² × E) — std::find on `component` (O(V)) called inside while-loop (V iters) × edge-scan (E iters)
- After: O(V × E) — O(1) unordered_set membership replaces the O(V) linear scan
## Defective Code
```cpp
// GlobalOptimization.cpp:367-404
static bool ValidatePoseGraphConnectivity(const PoseGraph &pose_graph,
bool ignore_uncertain_edges = false) {
size_t n_nodes = pose_graph.nodes_.size();
size_t n_edges = pose_graph.edges_.size();
std::vector<int> nodes_to_explore{};
std::vector<int> component{}; // membership tracked as a vector
if (n_nodes > 0) {
nodes_to_explore.push_back(0);
component.push_back(0);
}
while (!nodes_to_explore.empty()) {
int i = nodes_to_explore.back();
nodes_to_explore.pop_back();
for (size_t j = 0; j < n_edges; j++) {
const PoseGraphEdge &t = pose_graph.edges_[j];
if (ignore_uncertain_edges && t.uncertain_) continue;
int adjacent_node{-1};
if (t.source_node_id_ == i) adjacent_node = t.target_node_id_;
else if (t.target_node_id_ == i) adjacent_node = t.source_node_id_;
if (adjacent_node != -1) {
auto find_result = std::find(component.begin(), component.end(),
adjacent_node); // O(V) per call
if (find_result == component.end()) {
nodes_to_explore.push_back(adjacent_node);
component.push_back(adjacent_node);
}
}
}
}
return component.size() == n_nodes;
}
```
**Problem:** `component` is a `std::vector<int>`. The `std::find` membership check is O(V)
called for every edge of every explored node. BFS visits V nodes, each scanning E edges,
each doing O(V) find → O(V²×E) total.
## Fixed Code
```cpp
static bool ValidatePoseGraphConnectivity(const PoseGraph &pose_graph,
bool ignore_uncertain_edges = false) {
size_t n_nodes = pose_graph.nodes_.size();
size_t n_edges = pose_graph.edges_.size();
std::vector<int> nodes_to_explore{};
std::unordered_set<int> component_set; // O(1) membership
if (n_nodes > 0) {
nodes_to_explore.push_back(0);
component_set.insert(0);
}
while (!nodes_to_explore.empty()) {
int i = nodes_to_explore.back();
nodes_to_explore.pop_back();
for (size_t j = 0; j < n_edges; j++) {
const PoseGraphEdge &t = pose_graph.edges_[j];
if (ignore_uncertain_edges && t.uncertain_) continue;
int adjacent_node{-1};
if (t.source_node_id_ == i) adjacent_node = t.target_node_id_;
else if (t.target_node_id_ == i) adjacent_node = t.source_node_id_;
if (adjacent_node != -1) {
if (component_set.insert(adjacent_node).second) { // O(1)
nodes_to_explore.push_back(adjacent_node);
}
}
}
}
return component_set.size() == n_nodes;
}
```
## CWE
CWE-407: Inefficient Algorithmic Complexity — O(V²×E) → O(V×E)