166 {
167 if (chunks.size() < 2) {
168 throw std::invalid_argument("Need at least two chunks for silhouette score");
169 }
170
171 double total_score = 0.0;
172 size_t total_points = 0;
173
174 for (size_t i = 0; i < chunks.size(); ++i) {
175 for (const auto& point : chunks[i]) {
176
177 double a = 0.0;
178 size_t same_chunk_count = 0;
179 for (const auto& other_point : chunks[i]) {
180 if (&point != &other_point) {
182 if (dist < std::numeric_limits<double>::max()) {
183 a += dist;
184 ++same_chunk_count;
185 }
186 }
187 }
188 a = same_chunk_count > 0 ? a / same_chunk_count : 0.0;
189
190
191 double b = std::numeric_limits<double>::max();
192 for (size_t j = 0; j < chunks.size(); ++j) {
193 if (i != j) {
194 double avg_dist = 0.0;
195 size_t valid_dist = 0;
196 for (const auto& other_point : chunks[j]) {
198 if (dist < std::numeric_limits<double>::max()) {
199 avg_dist += dist;
200 ++valid_dist;
201 }
202 }
203 if (valid_dist > 0) {
204 b = std::min(b, avg_dist / valid_dist);
205 }
206 }
207 }
208
209 if (std::isfinite(a) && std::isfinite(b) && b < std::numeric_limits<double>::max()) {
210 double max_ab = std::max(a, b);
211 if (max_ab > 0) {
212 total_score += (b - a) / max_ab;
213 ++total_points;
214 }
215 }
216 }
217 }
218
219 if (total_points == 0) {
220 throw std::runtime_error("No valid silhouette scores computed");
221 }
222
223 return total_score / total_points;
224 }