Skip to content

Commit ecffc57

Browse files
Merge pull request #3243 from verilog-to-routing/feature-libvtr-math
[LibVTR][Math] Fixed Issues with Median Calculation
2 parents 7641911 + 304cd39 commit ecffc57

File tree

6 files changed

+131
-41
lines changed

6 files changed

+131
-41
lines changed

libs/librrgraph/src/utils/alloc_and_load_rr_indexed_data.cpp

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
#include "alloc_and_load_rr_indexed_data.h"
22

3+
#include <algorithm>
34
#include <cmath> /* Needed only for sqrt call (remove if sqrt removed) */
45
#include <fstream>
56
#include <iomanip>
@@ -592,8 +593,10 @@ static void load_rr_indexed_data_T_values(const RRGraphView& rr_graph,
592593
auto switch_Cinternal_total_histogram = build_histogram(switch_Cinternal_total[RRIndexedDataId(cost_index)], 10);
593594

594595
// Sort Rnode and Cnode
595-
float Cnode = vtr::median(C_total[RRIndexedDataId(cost_index)]);
596-
float Rnode = vtr::median(R_total[RRIndexedDataId(cost_index)]);
596+
std::sort(C_total[RRIndexedDataId(cost_index)].begin(), C_total[RRIndexedDataId(cost_index)].end());
597+
std::sort(R_total[RRIndexedDataId(cost_index)].begin(), R_total[RRIndexedDataId(cost_index)].end());
598+
float Cnode = vtr::median_presorted<float>(C_total[RRIndexedDataId(cost_index)]);
599+
float Rnode = vtr::median_presorted<float>(R_total[RRIndexedDataId(cost_index)]);
597600
float Rsw = get_histogram_mode(switch_R_total_histogram);
598601
float Tsw = get_histogram_mode(switch_T_total_histogram);
599602
float Cinternalsw = get_histogram_mode(switch_Cinternal_total_histogram);

libs/libvtrutil/src/vtr_math.cpp

Lines changed: 0 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
11
#include <map>
2-
#include <algorithm>
32

43
#include "vtr_assert.h"
54
#include "vtr_error.h"
@@ -22,19 +21,6 @@ int ipow(int base, int exp) {
2221
return result;
2322
}
2423

25-
float median(std::vector<float> vector) {
26-
VTR_ASSERT(vector.size() > 0);
27-
28-
std::sort(vector.begin(), vector.end());
29-
30-
auto size = vector.size();
31-
if (size % 2 == 0) {
32-
return (float)(vector[size / 2 - 1] + vector[size / 2]) / 2;
33-
}
34-
35-
return (float)vector[size / 2];
36-
}
37-
3824
/**
3925
* @brief Linear interpolation/Extrapolation
4026
*

libs/libvtrutil/src/vtr_math.h

Lines changed: 54 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
#pragma once
22

3+
#include <algorithm>
4+
#include <iterator>
35
#include <map>
46
#include <vector>
57
#include <cmath>
@@ -16,9 +18,6 @@ namespace vtr {
1618
///@brief Calculates the value pow(base, exp)
1719
int ipow(int base, int exp);
1820

19-
///@brief Returns the median of an input vector.
20-
float median(std::vector<float> vector);
21-
2221
///@brief Linear interpolation/Extrapolation
2322
template<typename X, typename Y>
2423
Y linear_interpolate_or_extrapolate(const std::map<X, Y>* xy_map, X requested_x);
@@ -35,23 +34,57 @@ T safe_ratio(T numerator, T denominator) {
3534
return numerator / denominator;
3635
}
3736

38-
///@brief Returns the median of the elements in range [first, last]
39-
template<typename InputIterator>
40-
double median(InputIterator first, InputIterator last) {
41-
auto len = std::distance(first, last);
42-
auto iter = first + len / 2;
43-
44-
if (len % 2 == 0) {
45-
return (*iter + *(iter + 1)) / 2;
46-
} else {
37+
///@brief Returns the median of the elements in range [first, last)
38+
/// If there are an odd number of elements in the range, returns the
39+
/// average of the two middle elements (equal distance from the start and end).
40+
/// NOTE: This method assumes that the container that first and last point to are
41+
/// pre-sorted
42+
template<typename ResultTy = double, typename InputIterator>
43+
ResultTy median_presorted(const InputIterator first, const InputIterator last) {
44+
// If the distance between first and last is 0 (meaning the set is empty),
45+
// return a quiet NaN. This should be handled by the user of this code.
46+
// NOTE: This returns a NaN of double type.
47+
if (std::distance(first, last) == 0)
48+
return std::nan("");
49+
50+
// Get the distance from the first element to the last element that is included
51+
// in the set of elements that we are getting the median of. Since "last" is
52+
// implicitly not included in this set, this is the distance from first to
53+
// last minus 1.
54+
auto dist_to_last_inclusive = std::distance(first, last) - 1;
55+
56+
if (dist_to_last_inclusive % 2 == 0) {
57+
// If the distance to the last inclusive element is even, then there is
58+
// only a single median element. Return it.
59+
auto iter = first + (dist_to_last_inclusive / 2);
4760
return *iter;
61+
} else {
62+
// If the distance to the last inclusive element is odd, then there are
63+
// two median elements. Return the average of the two.
64+
auto iter_1 = first + (dist_to_last_inclusive / 2);
65+
auto iter_2 = first + (dist_to_last_inclusive / 2) + 1;
66+
// Note: To ensure that the division works properly, need to cast one
67+
// of the operands to the result type.
68+
return static_cast<ResultTy>(*iter_1 + *iter_2) / 2;
4869
}
4970
}
5071

51-
///@brief Returns the median of a whole container
52-
template<typename Container>
53-
double median(Container c) {
54-
return median(std::begin(c), std::end(c));
72+
///@brief Returns the median of a whole container, assuming the container has
73+
/// not been pre-sorted.
74+
/// Note: This function is pass by value since the container needs to be
75+
/// sorted. If the container is already sorted, use median_presorted to
76+
/// avoid the copy.
77+
template<typename ResultTy = double, typename Container>
78+
ResultTy median(Container c) {
79+
std::sort(std::begin(c), std::end(c));
80+
return median_presorted<ResultTy>(std::begin(c), std::end(c));
81+
}
82+
83+
///@brief Returns the median of a whole container, assuming that it is already
84+
/// sorted.
85+
template<typename ResultTy = double, typename Container>
86+
ResultTy median_presorted(const Container &c) {
87+
return median_presorted<ResultTy>(std::begin(c), std::end(c));
5588
}
5689

5790
/**
@@ -66,7 +99,7 @@ double median(Container c) {
6699
* geomean = exp( (1 / n) * (log(v_1) + log(v_2) + ... + log(v_n)))
67100
*/
68101
template<typename InputIterator>
69-
double geomean(InputIterator first, InputIterator last, double init = 1.) {
102+
double geomean(const InputIterator first, const InputIterator last, double init = 1.) {
70103
double log_sum = std::log(init);
71104
size_t n = 0;
72105
for (auto iter = first; iter != last; ++iter) {
@@ -83,13 +116,13 @@ double geomean(InputIterator first, InputIterator last, double init = 1.) {
83116

84117
///@brief Returns the geometric mean of a whole container
85118
template<typename Container>
86-
double geomean(Container c) {
119+
double geomean(const Container &c) {
87120
return geomean(std::begin(c), std::end(c));
88121
}
89122

90-
///@brief Returns the arithmatic mean of the elements in range [first, last]
123+
///@brief Returns the arithmatic mean of the elements in range [first, last)
91124
template<typename InputIterator>
92-
double arithmean(InputIterator first, InputIterator last, double init = 0.) {
125+
double arithmean(const InputIterator first, const InputIterator last, double init = 0.) {
93126
double sum = init;
94127
size_t n = 0;
95128
for (auto iter = first; iter != last; ++iter) {
@@ -106,7 +139,7 @@ double arithmean(InputIterator first, InputIterator last, double init = 0.) {
106139

107140
///@brief Returns the aritmatic mean of a whole container
108141
template<typename Container>
109-
double arithmean(Container c) {
142+
double arithmean(const Container &c) {
110143
return arithmean(std::begin(c), std::end(c));
111144
}
112145

libs/libvtrutil/test/test_math.cpp

Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,6 @@
1+
#include <cmath>
12
#include <limits>
3+
#include <vector>
24

35
#include "catch2/catch_test_macros.hpp"
46
#include "catch2/catch_approx.hpp"
@@ -56,3 +58,67 @@ TEST_CASE("Is Close", "[vtr_math]") {
5658
REQUIRE(vtr::isclose(32.2, 32.4, 1e-9, 0.2));
5759
REQUIRE(!vtr::isclose(32.2, 32.4, 1e-9, 0.1));
5860
}
61+
62+
TEST_CASE("Median", "[vtr_math]") {
63+
// Check the median of a vector with a singular value.
64+
std::vector<double> single_vec = {1.0};
65+
REQUIRE(vtr::median(single_vec) == 1.0);
66+
REQUIRE(vtr::median_presorted(single_vec) == 1.0);
67+
68+
// Check the median of a vector with two elements.
69+
std::vector<double> double_vec = {1.0, 2.0};
70+
REQUIRE(vtr::median(double_vec) == 1.5);
71+
REQUIRE(vtr::median_presorted(double_vec) == 1.5);
72+
73+
// Check the median of a vector with an odd length.
74+
std::vector<double> odd_vec = {1.0, 2.0, 3.0, 4.0, 5.0};
75+
REQUIRE(vtr::median(odd_vec) == 3.0);
76+
REQUIRE(vtr::median_presorted(odd_vec) == 3.0);
77+
78+
// Check the median of a vector with a median length.
79+
std::vector<double> even_vec = {1.0, 2.0, 3.0, 4.0};
80+
REQUIRE(vtr::median(even_vec) == 2.5);
81+
82+
// Check unsorted list gives correct median.
83+
std::vector<double> unsorted_odd_vec = {2.0, 3.0, 1.0, 5.0, 4.0};
84+
REQUIRE(vtr::median(unsorted_odd_vec) == 3.0);
85+
std::vector<double> unsorted_even_vec = {2.0, 3.0, 1.0, 4.0};
86+
REQUIRE(vtr::median(unsorted_even_vec) == 2.5);
87+
88+
// Check the median of a negative, odd-lengthed vector works.
89+
std::vector<double> negative_odd_vec = {-1.0, -2.0, -3.0, -4.0, -5.0};
90+
REQUIRE(vtr::median(negative_odd_vec) == -3.0);
91+
REQUIRE(vtr::median_presorted(negative_odd_vec) == -3.0);
92+
93+
// Check the median of a negative, even-lengthed vector works.
94+
std::vector<double> negative_even_vec = {-1.0, -2.0, -3.0, -4.0};
95+
REQUIRE(vtr::median(negative_even_vec) == -2.5);
96+
REQUIRE(vtr::median_presorted(negative_even_vec) == -2.5);
97+
98+
// Check the median of an fp vector with an odd length.
99+
std::vector<float> fp_odd_vec = {1.0f, 2.0f, 3.0f, 4.0f, 5.0f};
100+
REQUIRE(vtr::median(fp_odd_vec) == 3.0);
101+
REQUIRE(vtr::median<float>(fp_odd_vec) == 3.0f);
102+
REQUIRE(vtr::median_presorted<float>(fp_odd_vec) == 3.0f);
103+
104+
// Check the median of an fp vector with a median length.
105+
std::vector<float> fp_even_vec = {1.0f, 2.0f, 3.0f, 4.0f};
106+
REQUIRE(vtr::median(fp_even_vec) == 2.5);
107+
REQUIRE(vtr::median<float>(fp_even_vec) == 2.5f);
108+
REQUIRE(vtr::median_presorted<float>(fp_even_vec) == 2.5f);
109+
110+
// Check the median of an integral, odd-lengthed vecor.
111+
std::vector<int> int_vec_odd = {1, 2, 3, 4, 5};
112+
REQUIRE(vtr::median(int_vec_odd) == 3.0);
113+
REQUIRE(vtr::median_presorted(int_vec_odd) == 3.0);
114+
115+
// Check the median of an integral, even-lengthed vecor.
116+
std::vector<int> int_vec_even = {1, 2, 3, 4};
117+
REQUIRE(vtr::median(int_vec_even) == 2.5);
118+
REQUIRE(vtr::median_presorted(int_vec_even) == 2.5);
119+
120+
// Check that trying to get the median of an empty vector returns a NaN.
121+
std::vector<double> empty_vec;
122+
REQUIRE(std::isnan(vtr::median(empty_vec)));
123+
REQUIRE(std::isnan(vtr::median_presorted(empty_vec)));
124+
}

vpr/src/place/delay_model/compute_delta_delays_utils.cpp

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -705,8 +705,8 @@ static float delay_reduce(std::vector<float>& delays, e_reducer reducer) {
705705
auto itr = std::max_element(delays.begin(), delays.end());
706706
delay = *itr;
707707
} else if (reducer == e_reducer::MEDIAN) {
708-
std::stable_sort(delays.begin(), delays.end());
709-
delay = vtr::median(delays.begin(), delays.end());
708+
std::sort(delays.begin(), delays.end());
709+
delay = vtr::median_presorted<float>(delays.begin(), delays.end());
710710
} else if (reducer == e_reducer::ARITHMEAN) {
711711
delay = vtr::arithmean(delays.begin(), delays.end());
712712
} else if (reducer == e_reducer::GEOMEAN) {

vpr/src/route/router_lookahead/router_lookahead_map_utils.cpp

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
#include "globals.h"
1515
#include "physical_types_util.h"
1616
#include "vpr_context.h"
17+
#include "vpr_error.h"
1718
#include "vpr_utils.h"
1819
#include "vtr_math.h"
1920
#include "vtr_time.h"
@@ -263,8 +264,9 @@ Cost_Entry Expansion_Cost_Entry::get_median_entry() const {
263264
* with the largest number of entries */
264265

265266
// This is code that needs to be revisited. For the time being, if the median entry
266-
// method calculation is used an assertion is thrown.
267-
VTR_ASSERT_MSG(false, "Get median entry calculation method is not implemented!");
267+
// method calculation is used an error is thrown.
268+
VPR_FATAL_ERROR(VPR_ERROR_ROUTE,
269+
"Get median entry calculation method is not implemented!");
268270

269271
int num_bins = 10;
270272

0 commit comments

Comments
 (0)