@@ -80,47 +80,77 @@ void HNSWIndex::add(int id, std::span<const float> vec) {
8080 }
8181
8282 // Create new node
83- auto node = std::make_unique<Node>();
84- node->id = id;
85- node->level = random_level ();
86- node->vector .assign (vec.begin (), vec.end ());
87- node->neighbors .resize (node->level + 1 );
88-
89- size_t node_idx = nodes_.size ();
90- id_to_index_[id] = node_idx;
83+ int node_level = random_level ();
84+ std::vector<float > node_vec (vec.begin (), vec.end ());
9185
9286 // If this is the first node
9387 if (entry_point_ == -1 ) {
94- entry_point_ = static_cast <int >(node_idx);
95- max_level_ = node->level ;
88+ auto node = std::make_unique<Node>();
89+ node->id = id;
90+ node->level = node_level;
91+ node->vector = node_vec;
92+ node->neighbors .resize (node_level + 1 );
93+
94+ entry_point_ = id;
95+ max_level_ = node_level;
9696 nodes_.push_back (std::move (node));
97+ id_to_index_[id] = 0 ;
9798 return ;
9899 }
99100
101+ // Build neighbor lists for new node
102+ std::vector<std::vector<int >> new_node_neighbors (node_level + 1 );
103+
100104 // Search for nearest neighbors at each layer
101105 std::vector<int > entry_points = {entry_point_};
102106
103107 // Greedy search from top to target layer
104- for (int lc = max_level_; lc > node-> level ; --lc) {
105- auto results = search_layer (vec , entry_points[0 ], 1 , lc);
108+ for (int lc = max_level_; lc > node_level ; --lc) {
109+ auto results = search_layer (node_vec , entry_points[0 ], 1 , lc);
106110 if (!results.empty ()) {
107111 entry_points[0 ] = results[0 ].id ;
108112 }
109113 }
110114
111115 // Insert at all layers from top to 0
112- for (int lc = node-> level ; lc >= 0 ; --lc) {
116+ for (int lc = node_level ; lc >= 0 ; --lc) {
113117 int ef = std::max (params_.ef_construction , params_.M );
114- auto candidates = search_layer (vec , entry_points[0 ], ef, lc);
118+ auto candidates = search_layer (node_vec , entry_points[0 ], ef, lc);
115119
116120 // Select M neighbors
117121 int M = (lc == 0 ) ? params_.max_M0 : params_.max_M ;
118- auto neighbors = select_neighbors_heuristic (vec , candidates, M);
122+ auto neighbors = select_neighbors_heuristic (node_vec , candidates, M);
119123
120- // Add bidirectional links
121- for (int neighbor_id : neighbors) {
122- size_t neighbor_idx = id_to_index_[neighbor_id];
123- nodes_[node_idx]->neighbors [lc].push_back (neighbor_id);
124+ new_node_neighbors[lc] = neighbors;
125+
126+ // Update entry point for next layer
127+ if (!candidates.empty ()) {
128+ entry_points[0 ] = candidates[0 ].id ;
129+ }
130+ }
131+
132+ // Now create and add the node
133+ auto node = std::make_unique<Node>();
134+ node->id = id;
135+ node->level = node_level;
136+ node->vector = node_vec;
137+ node->neighbors = new_node_neighbors;
138+
139+ size_t node_idx = nodes_.size ();
140+ nodes_.push_back (std::move (node));
141+ id_to_index_[id] = node_idx;
142+
143+ // Add bidirectional links
144+ for (int lc = node_level; lc >= 0 ; --lc) {
145+ for (int neighbor_id : new_node_neighbors[lc]) {
146+ auto neighbor_it = id_to_index_.find (neighbor_id);
147+ if (neighbor_it == id_to_index_.end ()) continue ;
148+
149+ size_t neighbor_idx = neighbor_it->second ;
150+
151+ // Check if neighbor has this layer
152+ if (lc >= static_cast <int >(nodes_[neighbor_idx]->neighbors .size ())) continue ;
153+
124154 nodes_[neighbor_idx]->neighbors [lc].push_back (id);
125155
126156 // Prune if necessary
@@ -129,30 +159,23 @@ void HNSWIndex::add(int id, std::span<const float> vec) {
129159 auto & neighbor_vec = nodes_[neighbor_idx]->vector ;
130160 std::vector<SearchResult> neighbor_candidates;
131161 for (int conn_id : nodes_[neighbor_idx]->neighbors [lc]) {
132- if (conn_id == id) continue ;
133- size_t conn_idx = id_to_index_[conn_id];
162+ auto conn_it = id_to_index_.find (conn_id);
163+ if (conn_it == id_to_index_.end ()) continue ;
164+ size_t conn_idx = conn_it->second ;
134165 float dist = distance (neighbor_vec, nodes_[conn_idx]->vector );
135166 neighbor_candidates.push_back ({conn_id, dist});
136167 }
137- neighbor_candidates.push_back ({id, distance (neighbor_vec, vec)});
138168
139169 auto pruned = select_neighbors_heuristic (neighbor_vec, neighbor_candidates, max_conn);
140170 nodes_[neighbor_idx]->neighbors [lc] = pruned;
141171 }
142172 }
143-
144- // Update entry point for next layer
145- if (!candidates.empty ()) {
146- entry_points[0 ] = candidates[0 ].id ;
147- }
148173 }
149174
150- nodes_.push_back (std::move (node));
151-
152175 // Update entry point if necessary
153- if (nodes_[node_idx]-> level > max_level_) {
154- max_level_ = nodes_[node_idx]-> level ;
155- entry_point_ = static_cast < int >(node_idx) ;
176+ if (node_level > max_level_) {
177+ max_level_ = node_level ;
178+ entry_point_ = id ;
156179 }
157180}
158181
0 commit comments