# 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 367–404
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)
