TTK
Loading...
Searching...
No Matches
PersistenceDiagramAuction.cpp
Go to the documentation of this file.
2
4 const int kdt_index) {
5 double const max_price = getMaximalPrice();
6 double epsilon = epsilon_;
7 if(epsilon_ < 1e-6 * max_price) {
8 // Risks of floating point limits reached...
9 epsilon = 1e-6 * max_price;
10 }
11 while(unassignedBidders_.size() > 0) {
12 n_biddings++;
13 int const pos = unassignedBidders_.front();
14 Bidder &b = this->bidders_[pos];
16
17 GoodDiagram &all_goods = b.isDiagonal() ? diagonal_goods_ : goods_;
18 Good &twin_good = b.id_ >= 0 ? diagonal_goods_[b.id_] : goods_[-b.id_ - 1];
19 // double eps = epsilon_*(1+0.05*n_biddings/bidders_.size());
20 int idx_reassigned;
21 if(b.isDiagonal()) {
22 if(use_kdt_) {
23 idx_reassigned = b.runDiagonalKDTBidding(
24 &all_goods, twin_good, wasserstein_, epsilon, geometricalFactor_,
26 kdt_index);
27 } else {
28 idx_reassigned = b.runDiagonalBidding(
29 &all_goods, twin_good, wasserstein_, epsilon, geometricalFactor_,
31 }
32 } else {
33 if(use_kdt_) {
34 // We can use the kd-tree to speed up the search
35 idx_reassigned = b.runKDTBidding(&all_goods, twin_good, wasserstein_,
36 epsilon, geometricalFactor_,
37 nonMatchingWeight_, &kdt_, kdt_index);
38 } else {
39 idx_reassigned
40 = b.runBidding(&all_goods, twin_good, wasserstein_, epsilon,
42 }
43 }
44 if(idx_reassigned >= 0) {
45 Bidder &reassigned = bidders_[idx_reassigned];
46 reassigned.resetProperty();
47 unassignedBidders_.push(idx_reassigned);
48 }
49 }
50}
51
53 double max_price = 0;
54 for(size_t i = 0; i < goods_.size(); ++i) {
55 Good const &g = goods_[i];
56 double const price = g.getPrice();
57 if(price > max_price) {
58 max_price = price;
59 }
60 }
61
62 for(size_t i = 0; i < diagonal_goods_.size(); ++i) {
63 Good const &g = diagonal_goods_[i];
64 double const price = g.getPrice();
65 if(price > max_price) {
66 max_price = price;
67 }
68 }
69
70 return max_price;
71}
72
74 std::vector<MatchingType> &matchings, bool get_diagonal_matches) {
75 double wassersteinDistance = 0;
76 for(size_t i = 0; i < bidders_.size(); i++) {
77 Bidder const &b = bidders_[i];
78 if(!b.isDiagonal()) {
79 int const good_id = b.getProperty().id_;
80 double cost;
81
82 if(good_id > -1) {
83 // good is not diagonal
84 cost = b.cost(b.getProperty(), wasserstein_, geometricalFactor_,
85 nonMatchingWeight_);
86 MatchingType const t = std::make_tuple(i, good_id, cost);
87 matchings.emplace_back(t);
88 } else {
89 if(!b.getProperty().isDiagonal()) {
90 this->printWrn("both the bidder and the good are diagonal points");
91 }
92 cost = 2.0 * Geometry::pow(std::abs((b.y_ - b.x_) / 2.0), wasserstein_);
93 if(get_diagonal_matches) {
94 MatchingType const t = std::make_tuple(i, good_id, cost);
95 matchings.emplace_back(t);
96 }
97 }
98 wassersteinDistance += cost;
99 } else {
100 // b is diagonal
101 const Good &g = b.getProperty();
102 double const cost
103 = 2.0 * Geometry::pow(std::abs((g.y_ - g.x_) / 2.0), wasserstein_);
104 if(get_diagonal_matches) {
105 MatchingType const t = std::make_tuple(b.id_, g.id_, cost);
106 matchings.emplace_back(t);
107 }
108 wassersteinDistance += cost;
109 }
110 }
111 return wassersteinDistance;
112}
113
115 lowerBoundCost_ = 0;
116 for(unsigned int i = 0; i < bidders_.size(); ++i) {
117 if(bidders_[i].isDiagonal())
118 continue;
119
120 // Get closest good
121 double bestCost = std::numeric_limits<double>::max();
122 std::vector<KDT *> neighbours;
123 std::vector<double> costs;
124 if(use_kdt_) {
125 std::array<double, 5> coordinates;
126 bidders_[i].GetKDTCoordinates(geometricalFactor_, coordinates);
127 kdt_.getKClosest(1, coordinates, neighbours, costs, kdt_index);
128 int const bestIndex = neighbours[0]->id_;
129 bestCost = bidders_[i].cost(goods_[bestIndex], wasserstein_,
130 geometricalFactor_, nonMatchingWeight_);
131 } else {
132 for(unsigned int j = 0; j < goods_.size(); ++j) {
133 double const cost = bidders_[i].cost(
134 goods_[j], wasserstein_, geometricalFactor_, nonMatchingWeight_);
135 if(cost < bestCost)
136 bestCost = cost;
137 }
138 }
139
140 // Compare with diagonal good
141 Good g{bidders_[i].x_, bidders_[i].y_, true, -bidders_[i].id_ - 1};
142 g.SetCriticalCoordinates(bidders_[i].GetCriticalCoordinates());
143 g.projectOnDiagonal();
144 double const cost = bidders_[i].cost(
145 g, wasserstein_, geometricalFactor_, nonMatchingWeight_);
146 if(cost < bestCost)
147 bestCost = cost;
148
149 // Update lower bound
150 lowerBoundCost_ += bestCost;
151 }
152 return lowerBoundCost_;
153}
154
155double ttk::PersistenceDiagramAuction::run(std::vector<MatchingType> &matchings,
156 const int kdt_index) {
157 initLowerBoundCostWeight(delta_lim_);
158 initLowerBoundCost(kdt_index);
159 initializeEpsilon();
160 int n_biddings = 0;
161 double delta = 5;
162 while(delta > delta_lim_) {
163 epsilon_ /= 5;
164 this->buildUnassignedBidders();
165 this->reinitializeGoods();
166 this->runAuctionRound(n_biddings, kdt_index);
167 delta = this->getRelativePrecision();
168 }
169 double const wassersteinDistance
170 = this->getMatchingsAndDistance(matchings, true);
171 return wassersteinDistance;
172}
173
176 const int wasserstein,
177 const double geometricalFactor,
178 const double nonMatchingWeight) const {
179
180 if(is_diagonal_ && g.isDiagonal()) {
181 return 0;
182 } else if(is_diagonal_) {
183 return nonMatchingWeight
184 * (geometricalFactor
185 * (2
186 * Geometry::pow(std::abs(g.y_ / 2 - g.x_ / 2), wasserstein))
187 + (1 - geometricalFactor)
188 * getPairGeometricalLength(wasserstein));
189 } else if(g.isDiagonal()) {
190 return nonMatchingWeight
191 * (geometricalFactor
192 * (2 * Geometry::pow(std::abs(y_ / 2 - x_ / 2), wasserstein))
193 + (1 - geometricalFactor)
194 * g.getPairGeometricalLength(wasserstein));
195 } else {
196 return geometricalFactor
197 * (Geometry::pow(std::abs(x_ - g.x_), wasserstein)
198 + Geometry::pow(std::abs(y_ - g.y_), wasserstein))
199 + (1 - geometricalFactor)
200 * (Geometry::pow(
201 std::abs(coords_[0] - g.coords_[0]), wasserstein)
203 std::abs(coords_[1] - g.coords_[1]), wasserstein)
205 std::abs(coords_[2] - g.coords_[2]), wasserstein));
206 }
207}
208
210 Good &twinGood,
211 int wasserstein,
212 double epsilon,
213 double geometricalFactor,
214 double nonMatchingWeight) {
215 double best_val = std::numeric_limits<double>::lowest();
216 double second_val = std::numeric_limits<double>::lowest();
217 Good *best_good{};
218 for(size_t i = 0; i < goods->size(); i++) {
219 Good &g = (*goods)[i];
220 double val
221 = -this->cost(g, wasserstein, geometricalFactor, nonMatchingWeight);
222 val -= g.getPrice();
223 if(val > best_val) {
224 second_val = best_val;
225 best_val = val;
226 best_good = &g;
227 } else if(val > second_val) {
228 second_val = val;
229 }
230 }
231 // And now check for the corresponding twin bidder
232 Good &g = twinGood;
233 double val
234 = -this->cost(g, wasserstein, geometricalFactor, nonMatchingWeight);
235 val -= g.getPrice();
236 if(val > best_val) {
237 second_val = best_val;
238 best_val = val;
239 best_good = &g;
240 } else if(val > second_val) {
241 second_val = val;
242 }
243
244 if(second_val == std::numeric_limits<double>::lowest()) {
245 // There is only one acceptable good for the bidder
246 second_val = best_val;
247 }
248 if(best_good == nullptr) {
249 return -1;
250 }
251 double const old_price = best_good->getPrice();
252 double new_price = old_price + best_val - second_val + epsilon;
253 if(new_price > std::numeric_limits<double>::max() / 2) {
254 new_price = old_price + epsilon;
255 }
256 // Assign bidder to best_good
257 this->setProperty(*best_good);
258 this->setPricePaid(new_price);
259
260 // Assign best_good to bidder and unassign the previous owner of best_good
261 // if need be
262 int const idx_reassigned = best_good->getOwner();
263 best_good->assign(this->position_in_auction_, new_price);
264 return idx_reassigned;
265}
266
268 GoodDiagram *goods,
269 Good &twinGood,
270 int wasserstein,
271 double epsilon,
272 double geometricalFactor,
273 double nonMatchingWeight,
274 std::priority_queue<std::pair<int, double>,
275 std::vector<std::pair<int, double>>,
276 Compare> &diagonal_queue) {
277
278 // First, find the lowest and second lowest weights for diagonal goods
279 // Take this time to update the weights in the priority queue of the goods
280 // tested. It is not necessary to update weights for all diagonal bidders in
281 // the priority queue since weights can only increase and we are interested
282 // only in the lowest ones
283 bool updated_top_pair
284 = false; // Boolean which equals true iff the top pair in the priority
285 // queue is given the good price
286 bool const non_empty_goods = goods->size() > 0;
287 std::pair<int, double> best_pair;
288 if(non_empty_goods) {
289 while(!updated_top_pair) {
290 std::pair<int, double> top_pair = diagonal_queue.top();
291 diagonal_queue.pop();
292
293 double const queue_weight = top_pair.second;
294 const auto &good = (*goods)[top_pair.first];
295 if(good.getPrice() > queue_weight) {
296 // If the weight in the priority queue is not the good one, update
297 std::get<1>(top_pair) = good.getPrice();
298 diagonal_queue.push(top_pair);
299 } else {
300 updated_top_pair = true;
301 best_pair = top_pair;
302 }
303 }
304 }
305
306 std::pair<int, double> second_pair;
307 if(non_empty_goods && diagonal_queue.size() > 0) {
308 bool updated_second_pair = false;
309 while(!updated_second_pair) {
310 second_pair = diagonal_queue.top();
311 double const queue_weight = second_pair.second;
312 const auto &good = (*goods)[second_pair.first];
313 if(good.getPrice() != queue_weight) {
314 // If the weight in the priority queue is not the good one, update it
315 diagonal_queue.pop();
316 std::get<1>(second_pair) = good.getPrice();
317 diagonal_queue.push(second_pair);
318 } else {
319 updated_second_pair = true;
320 }
321 }
322 }
323
324 Good *best_good{};
325 double best_val = 0;
326 double second_val = 0;
327 if(non_empty_goods) {
328 best_val = -best_pair.second;
329 second_val = diagonal_queue.size() > 0 ? -second_pair.second : best_val;
330 best_good = &(*goods)[best_pair.first];
331 }
332
333 // And now check for the corresponding twin bidder
334 bool is_twin = false;
335 Good &g = twinGood;
336 double val
337 = -this->cost(g, wasserstein, geometricalFactor, nonMatchingWeight);
338 val -= g.getPrice();
339 if(non_empty_goods) {
340 if(val > best_val) {
341 second_val = best_val;
342 best_val = val;
343 best_good = &g;
344 is_twin = true;
345 } else if(val > second_val || diagonal_queue.empty()) {
346 second_val = val;
347 }
348 } else {
349 best_val = val;
350 best_good = &g;
351 is_twin = true;
352 second_val = best_val;
353 }
354
355 if(second_val == std::numeric_limits<double>::lowest()) {
356 // There is only one acceptable good for the bidder
357 second_val = best_val;
358 }
359 double const old_price = best_good->getPrice();
360 double new_price = old_price + best_val - second_val + epsilon;
361 if(new_price > std::numeric_limits<double>::max() / 2) {
362 new_price = old_price + epsilon;
363 }
364
365 // Assign bidder to best_good
366 this->setProperty(*best_good);
367 this->setPricePaid(new_price);
368
369 // Assign best_good to bidder and unassign the previous owner of best_good
370 // if need be
371 int const idx_reassigned = best_good->getOwner();
372 best_good->assign(this->position_in_auction_, new_price);
373 if(!is_twin) {
374 std::get<1>(best_pair) = new_price;
375 }
376 if(non_empty_goods) {
377 diagonal_queue.push(best_pair);
378 }
379 return idx_reassigned;
380}
381
383 GoodDiagram *goods,
384 Good &twinGood,
385 int wasserstein,
386 double epsilon,
387 double geometricalFactor,
388 double nonMatchingWeight,
389 std::vector<KDT *> &correspondence_kdt_map,
390 std::priority_queue<std::pair<int, double>,
391 std::vector<std::pair<int, double>>,
392 Compare> &diagonal_queue,
393 const int kdt_index) {
395
396 // First, find the lowest and second lowest weights for diagonal goods
397 // Take this time to update the weights in the priority queue of the goods
398 // tested. It is not necessary to update weights for all diagonal bidders in
399 // the priority queue since weights can only increase and we are interested
400 // only in the lowest ones
401 bool updated_top_pair
402 = false; // Boolean which equals true iff the top pair in the priority
403 // queue is given the good price
404 bool const non_empty_goods = goods->size() > 0;
405 std::pair<int, double> best_pair;
406 if(non_empty_goods) {
407 while(!updated_top_pair) {
408 std::pair<int, double> top_pair = diagonal_queue.top();
409 double const queue_weight = top_pair.second;
410 const auto &good = (*goods)[top_pair.first];
411
412 diagonal_queue.pop();
413 if(good.getPrice() > queue_weight) {
414 // If the weight in the priority queue is not the good one, update it
415 std::get<1>(top_pair) = good.getPrice();
416 diagonal_queue.push(top_pair);
417 } else {
418 updated_top_pair = true;
419 best_pair = top_pair;
420 }
421 }
422 }
423 std::pair<int, double> second_pair;
424 if(non_empty_goods && diagonal_queue.size() > 0) {
425 bool updated_second_pair = false;
426 while(!updated_second_pair) {
427 second_pair = diagonal_queue.top();
428 double const queue_weight = second_pair.second;
429 const auto &good = (*goods)[second_pair.first];
430 if(good.getPrice() > queue_weight) {
431 // If the weight in the priority queue is not the good one, update it
432 diagonal_queue.pop();
433 std::get<1>(second_pair) = good.getPrice();
434 diagonal_queue.push(second_pair);
435 } else {
436 updated_second_pair = true;
437 }
438 }
439 }
440
441 Good *best_good{};
442 double best_val = 0;
443 double second_val = 0;
444 if(non_empty_goods) {
445 best_val = -best_pair.second;
446 second_val = diagonal_queue.size() > 0 ? -second_pair.second : best_val;
447 best_good = &(*goods)[best_pair.first];
448 }
449
450 // And now check for the corresponding twin bidder
451 bool is_twin = false;
452 Good &g = twinGood;
453 double val
454 = -this->cost(g, wasserstein, geometricalFactor, nonMatchingWeight);
455 val -= g.getPrice();
456 if(non_empty_goods) {
457 if(val > best_val) {
458 second_val = best_val;
459 best_val = val;
460 best_good = &g;
461 is_twin = true;
462 } else if(val > second_val || diagonal_queue.empty()) {
463 second_val = val;
464 }
465 } else {
466 best_val = val;
467 best_good = &g;
468 is_twin = true;
469 second_val = best_val;
470 }
471
472 if(second_val == std::numeric_limits<double>::lowest()) {
473 // There is only one acceptable good for the bidder
474 second_val = best_val;
475 }
476 double const old_price = best_good->getPrice();
477 double new_price = old_price + best_val - second_val + epsilon;
478 if(new_price > std::numeric_limits<double>::max() / 2) {
479 new_price = old_price + epsilon;
480 }
481
482 // Assign bidder to best_good
483 this->setProperty(*best_good);
484 this->setPricePaid(new_price);
485
486 // Assign best_good to bidder and unassign the previous owner of best_good
487 // if need be
488 int const idx_reassigned = best_good->getOwner();
489 best_good->assign(this->position_in_auction_, new_price);
490 if(is_twin) {
491 // Update weight in KDTree if the closest good is in it
492 correspondence_kdt_map[best_good->id_]->updateWeight(new_price, kdt_index);
493 if(non_empty_goods) {
494 diagonal_queue.push(best_pair);
495 }
496 } else {
497 if(non_empty_goods) {
498 std::get<1>(best_pair) = new_price;
499 diagonal_queue.push(best_pair);
500 }
501 }
502 return idx_reassigned;
503}
504
506 Good &twinGood,
507 int wasserstein,
508 double epsilon,
509 double geometricalFactor,
510 double nonMatchingWeight,
511 KDT *kdt,
512 const int kdt_index) {
513
515 std::vector<KDT *> neighbours;
516 std::vector<double> costs;
517
518 std::array<double, 5> coordinates;
519 GetKDTCoordinates(geometricalFactor, coordinates);
520
521 kdt->getKClosest(2, coordinates, neighbours, costs, kdt_index);
522 double best_val, second_val;
523 KDT *closest_kdt;
524 Good *best_good{};
525 if(costs.size() == 2) {
526 std::array<int, 2> idx{0, 1};
527 std::sort(idx.begin(), idx.end(),
528 [&costs](int &a, int &b) { return costs[a] < costs[b]; });
529
530 closest_kdt = neighbours[idx[0]];
531 best_good = &(*goods)[closest_kdt->id_];
532 // Value is defined as the opposite of cost (each bidder aims at
533 // maximizing it)
534 best_val = -costs[idx[0]];
535 second_val = -costs[idx[1]];
536 } else {
537 // If the kdtree contains only one point
538 closest_kdt = neighbours[0];
539 best_good = &(*goods)[closest_kdt->id_];
540 best_val = -costs[0];
541 second_val = best_val;
542 }
543 // And now check for the corresponding twin bidder
544 bool twin_chosen = false;
545 Good &g = twinGood;
546 double val
547 = -this->cost(g, wasserstein, geometricalFactor, nonMatchingWeight);
548 val -= g.getPrice();
549 if(val > best_val) {
550 second_val = best_val;
551 best_val = val;
552 best_good = &g;
553 twin_chosen = true;
554 } else if(val > second_val) {
555 second_val = val;
556 }
557
558 if(second_val == std::numeric_limits<double>::lowest()) {
559 // There is only one acceptable good for the bidder
560 second_val = best_val;
561 }
562 double const old_price = best_good->getPrice();
563 double new_price = old_price + best_val - second_val + epsilon;
564 if(new_price > std::numeric_limits<double>::max() / 2) {
565 new_price = old_price + epsilon;
566 }
567 // Assign bidder to best_good
568 this->setProperty(*best_good);
569 this->setPricePaid(new_price);
570 // Assign best_good to bidder and unassign the previous owner of best_good
571 // if need be
572 int const idx_reassigned = best_good->getOwner();
573
574 best_good->assign(this->position_in_auction_, new_price);
575 // Update the price in the KDTree
576 if(!twin_chosen) {
577 closest_kdt->updateWeight(new_price, kdt_index);
578 }
579 return idx_reassigned;
580}
int runDiagonalBidding(GoodDiagram *goods, Good &twinGood, int wasserstein, double epsilon, double geometricalFactor, double nonMatchingWeight, std::priority_queue< std::pair< int, double >, std::vector< std::pair< int, double > >, Compare > &diagonal_queue)
int runKDTBidding(GoodDiagram *goods, Good &diagonalGood, int wasserstein, double epsilon, double geometricalFactor, double nonMatchingWeight, KDT *kdt, const int kdt_index=0)
int runBidding(GoodDiagram *goods, Good &diagonalGood, int wasserstein, double epsilon, double geometricalFactor, double nonMatchingWeight)
const Good & getProperty() const
int runDiagonalKDTBidding(GoodDiagram *goods, Good &diagonalGood, int wasserstein, double epsilon, double geometricalFactor, double nonMatchingWeight, std::vector< KDT * > &correspondence_kdt_map, std::priority_queue< std::pair< int, double >, std::vector< std::pair< int, double > >, Compare > &diagonal_queue, const int kdt_index=0)
TTK KD-Tree.
Definition KDTree.h:21
void updateWeight(const dataType new_weight, const int weight_index=0)
Definition KDTree.h:88
void getKClosest(const unsigned int k, const Container &coordinates, KDTreeMap &neighbours, std::vector< dataType > &costs, const int weight_index=0)
Definition KDTree.h:433
int id_
Definition KDTree.h:44
double getPairGeometricalLength(const int wasserstein) const
double cost(const PersistenceDiagramAuctionActor &g, const int wasserstein, const double geometricalFactor, const double nonMatchingWeight) const
void runAuctionRound(int &n_biddings, const int kdt_index=0)
std::vector< KDT * > & correspondence_kdt_map_
std::priority_queue< std::pair< int, double >, std::vector< std::pair< int, double > >, Compare > diagonal_queue_
double initLowerBoundCost(const int kdt_index=0)
double getMatchingsAndDistance(std::vector< MatchingType > &matchings, bool get_diagonal_matches=false)
T1 pow(const T1 val, const T2 n)
Definition Geometry.h:456
std::tuple< int, int, double > MatchingType
Matching between two Persistence Diagram pairs.
std::vector< Good > GoodDiagram