OR-Tools  9.3
hungarian.cc
Go to the documentation of this file.
1// Copyright 2010-2021 Google LLC
2// Licensed under the Apache License, Version 2.0 (the "License");
3// you may not use this file except in compliance with the License.
4// You may obtain a copy of the License at
5//
6// http://www.apache.org/licenses/LICENSE-2.0
7//
8// Unless required by applicable law or agreed to in writing, software
9// distributed under the License is distributed on an "AS IS" BASIS,
10// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
11// See the License for the specific language governing permissions and
12// limitations under the License.
13
15
16#include <algorithm>
17#include <cstdio>
18#include <limits>
19
20#include "absl/strings/str_format.h"
22
23namespace operations_research {
24
26 static constexpr int kHungarianOptimizerRowNotFound = -1;
27 static constexpr int kHungarianOptimizerColNotFound = -2;
28
29 public:
30 // Setup the initial conditions for the algorithm.
31
32 // Parameters: costs is a matrix of the cost of assigning each agent to
33 // each task. costs[i][j] is the cost of assigning agent i to task j.
34 // All the costs must be non-negative. This matrix does not have to
35 // be square (i.e. we can have different numbers of agents and tasks), but it
36 // must be regular (i.e. there must be the same number of entries in each row
37 // of the matrix).
38 explicit HungarianOptimizer(const std::vector<std::vector<double>>& costs);
39
40 // Find an assignment which maximizes the total cost.
41 // Returns the assignment in the two vectors passed as argument.
42 // preimage[i] is assigned to image[i].
43 void Maximize(std::vector<int>* preimage, std::vector<int>* image);
44
45 // Like Maximize(), but minimizing the cost instead.
46 void Minimize(std::vector<int>* preimage, std::vector<int>* image);
47
48 private:
49 typedef void (HungarianOptimizer::*Step)();
50
51 typedef enum { NONE, PRIME, STAR } Mark;
52
53 // Convert the final cost matrix into a set of assignments of preimage->image.
54 // Returns the assignment in the two vectors passed as argument, the same as
55 // Minimize and Maximize
56 void FindAssignments(std::vector<int>* preimage, std::vector<int>* image);
57
58 // Is the cell (row, col) starred?
59 bool IsStarred(int row, int col) const { return marks_[row][col] == STAR; }
60
61 // Mark cell (row, col) with a star
62 void Star(int row, int col) {
63 marks_[row][col] = STAR;
64 stars_in_col_[col]++;
65 }
66
67 // Remove a star from cell (row, col)
68 void UnStar(int row, int col) {
69 marks_[row][col] = NONE;
70 stars_in_col_[col]--;
71 }
72
73 // Find a column in row 'row' containing a star, or return
74 // kHungarianOptimizerColNotFound if no such column exists.
75 int FindStarInRow(int row) const;
76
77 // Find a row in column 'col' containing a star, or return
78 // kHungarianOptimizerRowNotFound if no such row exists.
79 int FindStarInCol(int col) const;
80
81 // Is cell (row, col) marked with a prime?
82 bool IsPrimed(int row, int col) const { return marks_[row][col] == PRIME; }
83
84 // Mark cell (row, col) with a prime.
85 void Prime(int row, int col) { marks_[row][col] = PRIME; }
86
87 // Find a column in row containing a prime, or return
88 // kHungarianOptimizerColNotFound if no such column exists.
89 int FindPrimeInRow(int row) const;
90
91 // Remove the prime marks_ from every cell in the matrix.
92 void ClearPrimes();
93
94 // Does column col contain a star?
95 bool ColContainsStar(int col) const { return stars_in_col_[col] > 0; }
96
97 // Is row 'row' covered?
98 bool RowCovered(int row) const { return rows_covered_[row]; }
99
100 // Cover row 'row'.
101 void CoverRow(int row) { rows_covered_[row] = true; }
102
103 // Uncover row 'row'.
104 void UncoverRow(int row) { rows_covered_[row] = false; }
105
106 // Is column col covered?
107 bool ColCovered(int col) const { return cols_covered_[col]; }
108
109 // Cover column col.
110 void CoverCol(int col) { cols_covered_[col] = true; }
111
112 // Uncover column col.
113 void UncoverCol(int col) { cols_covered_[col] = false; }
114
115 // Uncover ever row and column in the matrix.
116 void ClearCovers();
117
118 // Find the smallest uncovered cell in the matrix.
119 double FindSmallestUncovered() const;
120
121 // Find an uncovered zero and store its coordinates in (zeroRow_, zeroCol_)
122 // and return true, or return false if no such cell exists.
123 bool FindZero(int* zero_row, int* zero_col) const;
124
125 // Print the matrix to stdout (for debugging.)
126 void PrintMatrix();
127
128 // Run the Munkres algorithm!
129 void DoMunkres();
130
131 // Step 1.
132 // For each row of the matrix, find the smallest element and subtract it
133 // from every element in its row. Go to Step 2.
134 void ReduceRows();
135
136 // Step 2.
137 // Find a zero (Z) in the matrix. If there is no starred zero in its row
138 // or column, star Z. Repeat for every element in the matrix. Go to step 3.
139 // Note: profiling shows this method to use 9.2% of the CPU - the next
140 // slowest step takes 0.6%. I can't think of a way of speeding it up though.
141 void StarZeroes();
142
143 // Step 3.
144 // Cover each column containing a starred zero. If all columns are
145 // covered, the starred zeros describe a complete set of unique assignments.
146 // In this case, terminate the algorithm. Otherwise, go to step 4.
147 void CoverStarredZeroes();
148
149 // Step 4.
150 // Find a noncovered zero and prime it. If there is no starred zero in the
151 // row containing this primed zero, Go to Step 5. Otherwise, cover this row
152 // and uncover the column containing the starred zero. Continue in this manner
153 // until there are no uncovered zeros left, then go to Step 6.
154 void PrimeZeroes();
155
156 // Step 5.
157 // Construct a series of alternating primed and starred zeros as follows.
158 // Let Z0 represent the uncovered primed zero found in Step 4. Let Z1 denote
159 // the starred zero in the column of Z0 (if any). Let Z2 denote the primed
160 // zero in the row of Z1 (there will always be one). Continue until the
161 // series terminates at a primed zero that has no starred zero in its column.
162 // Unstar each starred zero of the series, star each primed zero of the
163 // series, erase all primes and uncover every line in the matrix. Return to
164 // Step 3.
165 void MakeAugmentingPath();
166
167 // Step 6.
168 // Add the smallest uncovered value in the matrix to every element of each
169 // covered row, and subtract it from every element of each uncovered column.
170 // Return to Step 4 without altering any stars, primes, or covered lines.
171 void AugmentPath();
172
173 // The size of the problem, i.e. max(#agents, #tasks).
174 int matrix_size_;
175
176 // The expanded cost matrix.
177 std::vector<std::vector<double>> costs_;
178
179 // The greatest cost in the initial cost matrix.
180 double max_cost_;
181
182 // Which rows and columns are currently covered.
183 std::vector<bool> rows_covered_;
184 std::vector<bool> cols_covered_;
185
186 // The marks_ (star/prime/none) on each element of the cost matrix.
187 std::vector<std::vector<Mark>> marks_;
188
189 // The number of stars in each column - used to speed up coverStarredZeroes.
190 std::vector<int> stars_in_col_;
191
192 // Representation of a path_ through the matrix - used in step 5.
193 std::vector<int> preimage_; // i.e. the agents
194 std::vector<int> image_; // i.e. the tasks
195
196 // The width_ and height_ of the initial (non-expanded) cost matrix.
197 int width_;
198 int height_;
199
200 // The current state of the algorithm
201 HungarianOptimizer::Step state_;
202};
203
205 const std::vector<std::vector<double>>& costs)
206 : matrix_size_(0),
207 costs_(),
208 max_cost_(0),
209 rows_covered_(),
210 cols_covered_(),
211 marks_(),
212 stars_in_col_(),
213 preimage_(),
214 image_(),
215 width_(0),
216 height_(0),
217 state_(nullptr) {
218 width_ = costs.size();
219
220 if (width_ > 0) {
221 height_ = costs[0].size();
222 } else {
223 height_ = 0;
224 }
225
226 matrix_size_ = std::max(width_, height_);
227 max_cost_ = 0;
228
229 // Generate the expanded cost matrix by adding extra 0-valued elements in
230 // order to make a square matrix. At the same time, find the greatest cost
231 // in the matrix (used later if we want to maximize rather than minimize the
232 // overall cost.)
233 costs_.resize(matrix_size_);
234 for (int row = 0; row < matrix_size_; ++row) {
235 costs_[row].resize(matrix_size_);
236 }
237
238 for (int row = 0; row < matrix_size_; ++row) {
239 for (int col = 0; col < matrix_size_; ++col) {
240 if ((row >= width_) || (col >= height_)) {
241 costs_[row][col] = 0;
242 } else {
243 costs_[row][col] = costs[row][col];
244 max_cost_ = std::max(max_cost_, costs_[row][col]);
245 }
246 }
247 }
248
249 // Initially, none of the cells of the matrix are marked.
250 marks_.resize(matrix_size_);
251 for (int row = 0; row < matrix_size_; ++row) {
252 marks_[row].resize(matrix_size_);
253 for (int col = 0; col < matrix_size_; ++col) {
254 marks_[row][col] = NONE;
255 }
256 }
257
258 stars_in_col_.resize(matrix_size_);
259
260 rows_covered_.resize(matrix_size_);
261 cols_covered_.resize(matrix_size_);
262
263 preimage_.resize(matrix_size_ * 2);
264 image_.resize(matrix_size_ * 2);
265}
266
267// Find an assignment which maximizes the total cost.
268// Return an array of pairs of integers. Each pair (i, j) corresponds to
269// assigning agent i to task j.
270void HungarianOptimizer::Maximize(std::vector<int>* preimage,
271 std::vector<int>* image) {
272 // Find a maximal assignment by subtracting each of the
273 // original costs from max_cost_ and then minimizing.
274 for (int row = 0; row < width_; ++row) {
275 for (int col = 0; col < height_; ++col) {
276 costs_[row][col] = max_cost_ - costs_[row][col];
277 }
278 }
279 Minimize(preimage, image);
280}
281
282// Find an assignment which minimizes the total cost.
283// Return an array of pairs of integers. Each pair (i, j) corresponds to
284// assigning agent i to task j.
285void HungarianOptimizer::Minimize(std::vector<int>* preimage,
286 std::vector<int>* image) {
287 DoMunkres();
288 FindAssignments(preimage, image);
289}
290
291// Convert the final cost matrix into a set of assignments of agents -> tasks.
292// Return an array of pairs of integers, the same as the return values of
293// Minimize() and Maximize()
294void HungarianOptimizer::FindAssignments(std::vector<int>* preimage,
295 std::vector<int>* image) {
296 preimage->clear();
297 image->clear();
298 for (int row = 0; row < width_; ++row) {
299 for (int col = 0; col < height_; ++col) {
300 if (IsStarred(row, col)) {
301 preimage->push_back(row);
302 image->push_back(col);
303 break;
304 }
305 }
306 }
307 // TODO(user)
308 // result_size = min(width_, height_);
309 // CHECK image.size() == result_size
310 // CHECK preimage.size() == result_size
311}
312
313// Find a column in row 'row' containing a star, or return
314// kHungarianOptimizerColNotFound if no such column exists.
315int HungarianOptimizer::FindStarInRow(int row) const {
316 for (int col = 0; col < matrix_size_; ++col) {
317 if (IsStarred(row, col)) {
318 return col;
319 }
320 }
321
322 return kHungarianOptimizerColNotFound;
323}
324
325// Find a row in column 'col' containing a star, or return
326// kHungarianOptimizerRowNotFound if no such row exists.
327int HungarianOptimizer::FindStarInCol(int col) const {
328 if (!ColContainsStar(col)) {
329 return kHungarianOptimizerRowNotFound;
330 }
331
332 for (int row = 0; row < matrix_size_; ++row) {
333 if (IsStarred(row, col)) {
334 return row;
335 }
336 }
337
338 // NOTREACHED
339 return kHungarianOptimizerRowNotFound;
340}
341
342// Find a column in row containing a prime, or return
343// kHungarianOptimizerColNotFound if no such column exists.
344int HungarianOptimizer::FindPrimeInRow(int row) const {
345 for (int col = 0; col < matrix_size_; ++col) {
346 if (IsPrimed(row, col)) {
347 return col;
348 }
349 }
350
351 return kHungarianOptimizerColNotFound;
352}
353
354// Remove the prime marks from every cell in the matrix.
355void HungarianOptimizer::ClearPrimes() {
356 for (int row = 0; row < matrix_size_; ++row) {
357 for (int col = 0; col < matrix_size_; ++col) {
358 if (IsPrimed(row, col)) {
359 marks_[row][col] = NONE;
360 }
361 }
362 }
363}
364
365// Uncovery ever row and column in the matrix.
366void HungarianOptimizer::ClearCovers() {
367 for (int x = 0; x < matrix_size_; x++) {
368 UncoverRow(x);
369 UncoverCol(x);
370 }
371}
372
373// Find the smallest uncovered cell in the matrix.
374double HungarianOptimizer::FindSmallestUncovered() const {
375 double minval = std::numeric_limits<double>::max();
376
377 for (int row = 0; row < matrix_size_; ++row) {
378 if (RowCovered(row)) {
379 continue;
380 }
381
382 for (int col = 0; col < matrix_size_; ++col) {
383 if (ColCovered(col)) {
384 continue;
385 }
386
387 minval = std::min(minval, costs_[row][col]);
388 }
389 }
390
391 return minval;
392}
393
394// Find an uncovered zero and store its co-ordinates in (zeroRow, zeroCol)
395// and return true, or return false if no such cell exists.
396bool HungarianOptimizer::FindZero(int* zero_row, int* zero_col) const {
397 for (int row = 0; row < matrix_size_; ++row) {
398 if (RowCovered(row)) {
399 continue;
400 }
401
402 for (int col = 0; col < matrix_size_; ++col) {
403 if (ColCovered(col)) {
404 continue;
405 }
406
407 if (costs_[row][col] == 0) {
408 *zero_row = row;
409 *zero_col = col;
410 return true;
411 }
412 }
413 }
414
415 return false;
416}
417
418// Print the matrix to stdout (for debugging.)
419void HungarianOptimizer::PrintMatrix() {
420 for (int row = 0; row < matrix_size_; ++row) {
421 for (int col = 0; col < matrix_size_; ++col) {
422 absl::PrintF("%g ", costs_[row][col]);
423
424 if (IsStarred(row, col)) {
425 absl::PrintF("*");
426 }
427
428 if (IsPrimed(row, col)) {
429 absl::PrintF("'");
430 }
431 }
432 absl::PrintF("\n");
433 }
434}
435
436// Run the Munkres algorithm!
437void HungarianOptimizer::DoMunkres() {
438 state_ = &HungarianOptimizer::ReduceRows;
439 while (state_ != nullptr) {
440 (this->*state_)();
441 }
442}
443
444// Step 1.
445// For each row of the matrix, find the smallest element and subtract it
446// from every element in its row. Go to Step 2.
447void HungarianOptimizer::ReduceRows() {
448 for (int row = 0; row < matrix_size_; ++row) {
449 double min_cost = costs_[row][0];
450 for (int col = 1; col < matrix_size_; ++col) {
451 min_cost = std::min(min_cost, costs_[row][col]);
452 }
453 for (int col = 0; col < matrix_size_; ++col) {
454 costs_[row][col] -= min_cost;
455 }
456 }
457 state_ = &HungarianOptimizer::StarZeroes;
458}
459
460// Step 2.
461// Find a zero (Z) in the matrix. If there is no starred zero in its row
462// or column, star Z. Repeat for every element in the matrix. Go to step 3.
463void HungarianOptimizer::StarZeroes() {
464 // Since no rows or columns are covered on entry to this step, we use the
465 // covers as a quick way of marking which rows & columns have stars in them.
466 for (int row = 0; row < matrix_size_; ++row) {
467 if (RowCovered(row)) {
468 continue;
469 }
470
471 for (int col = 0; col < matrix_size_; ++col) {
472 if (ColCovered(col)) {
473 continue;
474 }
475
476 if (costs_[row][col] == 0) {
477 Star(row, col);
478 CoverRow(row);
479 CoverCol(col);
480 break;
481 }
482 }
483 }
484
485 ClearCovers();
486 state_ = &HungarianOptimizer::CoverStarredZeroes;
487}
488
489// Step 3.
490// Cover each column containing a starred zero. If all columns are
491// covered, the starred zeros describe a complete set of unique assignments.
492// In this case, terminate the algorithm. Otherwise, go to step 4.
493void HungarianOptimizer::CoverStarredZeroes() {
494 int num_covered = 0;
495
496 for (int col = 0; col < matrix_size_; ++col) {
497 if (ColContainsStar(col)) {
498 CoverCol(col);
499 num_covered++;
500 }
501 }
502
503 if (num_covered >= matrix_size_) {
504 state_ = nullptr;
505 return;
506 }
507 state_ = &HungarianOptimizer::PrimeZeroes;
508}
509
510// Step 4.
511// Find a noncovered zero and prime it. If there is no starred zero in the
512// row containing this primed zero, Go to Step 5. Otherwise, cover this row
513// and uncover the column containing the starred zero. Continue in this manner
514// until there are no uncovered zeros left, then go to Step 6.
515
516void HungarianOptimizer::PrimeZeroes() {
517 // This loop is guaranteed to terminate in at most matrix_size_ iterations,
518 // as findZero() returns a location only if there is at least one uncovered
519 // zero in the matrix. Each iteration, either one row is covered or the
520 // loop terminates. Since there are matrix_size_ rows, after that many
521 // iterations there are no uncovered cells and hence no uncovered zeroes,
522 // so the loop terminates.
523 for (;;) {
524 int zero_row, zero_col;
525 if (!FindZero(&zero_row, &zero_col)) {
526 // No uncovered zeroes.
527 state_ = &HungarianOptimizer::AugmentPath;
528 return;
529 }
530
531 Prime(zero_row, zero_col);
532 int star_col = FindStarInRow(zero_row);
533
534 if (star_col != kHungarianOptimizerColNotFound) {
535 CoverRow(zero_row);
536 UncoverCol(star_col);
537 } else {
538 preimage_[0] = zero_row;
539 image_[0] = zero_col;
540 state_ = &HungarianOptimizer::MakeAugmentingPath;
541 return;
542 }
543 }
544}
545
546// Step 5.
547// Construct a series of alternating primed and starred zeros as follows.
548// Let Z0 represent the uncovered primed zero found in Step 4. Let Z1 denote
549// the starred zero in the column of Z0 (if any). Let Z2 denote the primed
550// zero in the row of Z1 (there will always be one). Continue until the
551// series terminates at a primed zero that has no starred zero in its column.
552// Unstar each starred zero of the series, star each primed zero of the
553// series, erase all primes and uncover every line in the matrix. Return to
554// Step 3.
555void HungarianOptimizer::MakeAugmentingPath() {
556 bool done = false;
557 int count = 0;
558
559 // Note: this loop is guaranteed to terminate within matrix_size_ iterations
560 // because:
561 // 1) on entry to this step, there is at least 1 column with no starred zero
562 // (otherwise we would have terminated the algorithm already.)
563 // 2) each row containing a star also contains exactly one primed zero.
564 // 4) each column contains at most one starred zero.
565 //
566 // Since the path_ we construct visits primed and starred zeroes alternately,
567 // and terminates if we reach a primed zero in a column with no star, our
568 // path_ must either contain matrix_size_ or fewer stars (in which case the
569 // loop iterates fewer than matrix_size_ times), or it contains more. In
570 // that case, because (1) implies that there are fewer than
571 // matrix_size_ stars, we must have visited at least one star more than once.
572 // Consider the first such star that we visit more than once; it must have
573 // been reached immediately after visiting a prime in the same row. By (2),
574 // this prime is unique and so must have also been visited more than once.
575 // Therefore, that prime must be in the same column as a star that has been
576 // visited more than once, contradicting the assumption that we chose the
577 // first multiply visited star, or it must be in the same column as more
578 // than one star, contradicting (3). Therefore, we never visit any star
579 // more than once and the loop terminates within matrix_size_ iterations.
580
581 while (!done) {
582 // First construct the alternating path...
583 int row = FindStarInCol(image_[count]);
584
585 if (row != kHungarianOptimizerRowNotFound) {
586 count++;
587 preimage_[count] = row;
588 image_[count] = image_[count - 1];
589 } else {
590 done = true;
591 }
592
593 if (!done) {
594 int col = FindPrimeInRow(preimage_[count]);
595 count++;
596 preimage_[count] = preimage_[count - 1];
597 image_[count] = col;
598 }
599 }
600
601 // Then modify it.
602 for (int i = 0; i <= count; ++i) {
603 int row = preimage_[i];
604 int col = image_[i];
605
606 if (IsStarred(row, col)) {
607 UnStar(row, col);
608 } else {
609 Star(row, col);
610 }
611 }
612
613 ClearCovers();
614 ClearPrimes();
615 state_ = &HungarianOptimizer::CoverStarredZeroes;
616}
617
618// Step 6
619// Add the smallest uncovered value in the matrix to every element of each
620// covered row, and subtract it from every element of each uncovered column.
621// Return to Step 4 without altering any stars, primes, or covered lines.
622void HungarianOptimizer::AugmentPath() {
623 double minval = FindSmallestUncovered();
624
625 for (int row = 0; row < matrix_size_; ++row) {
626 for (int col = 0; col < matrix_size_; ++col) {
627 if (RowCovered(row)) {
628 costs_[row][col] += minval;
629 }
630
631 if (!ColCovered(col)) {
632 costs_[row][col] -= minval;
633 }
634 }
635 }
636
637 state_ = &HungarianOptimizer::PrimeZeroes;
638}
639
640bool InputContainsNan(const std::vector<std::vector<double>>& input) {
641 for (const auto& subvector : input) {
642 for (const auto& num : subvector) {
643 if (std::isnan(num)) {
644 LOG(ERROR) << "The provided input contains " << num << ".";
645 return true;
646 }
647 }
648 }
649 return false;
650}
651
653 const std::vector<std::vector<double>>& cost,
654 absl::flat_hash_map<int, int>* direct_assignment,
655 absl::flat_hash_map<int, int>* reverse_assignment) {
656 if (InputContainsNan(cost)) {
657 LOG(ERROR) << "Returning before invoking the Hungarian optimizer.";
658 return;
659 }
660 std::vector<int> agent;
661 std::vector<int> task;
662 HungarianOptimizer hungarian_optimizer(cost);
663 hungarian_optimizer.Minimize(&agent, &task);
664 for (int i = 0; i < agent.size(); ++i) {
665 (*direct_assignment)[agent[i]] = task[i];
666 (*reverse_assignment)[task[i]] = agent[i];
667 }
668}
669
671 const std::vector<std::vector<double>>& cost,
672 absl::flat_hash_map<int, int>* direct_assignment,
673 absl::flat_hash_map<int, int>* reverse_assignment) {
674 if (InputContainsNan(cost)) {
675 LOG(ERROR) << "Returning before invoking the Hungarian optimizer.";
676 return;
677 }
678 std::vector<int> agent;
679 std::vector<int> task;
680 HungarianOptimizer hungarian_optimizer(cost);
681 hungarian_optimizer.Maximize(&agent, &task);
682 for (int i = 0; i < agent.size(); ++i) {
683 (*direct_assignment)[agent[i]] = task[i];
684 (*reverse_assignment)[task[i]] = agent[i];
685 }
686}
687
688} // namespace operations_research
int64_t max
Definition: alldiff_cst.cc:140
int64_t min
Definition: alldiff_cst.cc:139
#define LOG(severity)
Definition: base/logging.h:420
HungarianOptimizer(const std::vector< std::vector< double > > &costs)
Definition: hungarian.cc:204
void Minimize(std::vector< int > *preimage, std::vector< int > *image)
Definition: hungarian.cc:285
void Maximize(std::vector< int > *preimage, std::vector< int > *image)
Definition: hungarian.cc:270
const int ERROR
Definition: log_severity.h:32
ColIndex col
Definition: markowitz.cc:183
RowIndex row
Definition: markowitz.cc:182
Collection of objects used to extend the Constraint Solver library.
void MinimizeLinearAssignment(const std::vector< std::vector< double > > &cost, absl::flat_hash_map< int, int > *direct_assignment, absl::flat_hash_map< int, int > *reverse_assignment)
Definition: hungarian.cc:652
bool InputContainsNan(const std::vector< std::vector< double > > &input)
Definition: hungarian.cc:640
void MaximizeLinearAssignment(const std::vector< std::vector< double > > &cost, absl::flat_hash_map< int, int > *direct_assignment, absl::flat_hash_map< int, int > *reverse_assignment)
Definition: hungarian.cc:670
static int input(yyscan_t yyscanner)
int64_t cost