30 bool AreColumnsProportional(
const SparseColumn&
a,
const SparseColumn&
b,
34 if (
a.num_entries() !=
b.num_entries())
return false;
36 bool a_is_larger =
true;
37 for (
const EntryIndex i :
a.AllEntryIndices()) {
38 if (
a.EntryRow(i) !=
b.EntryRow(i))
return false;
41 if (multiple == 0.0) {
42 a_is_larger = std::abs(coeff_a) > std::abs(coeff_b);
43 multiple = a_is_larger ? coeff_a / coeff_b : coeff_b / coeff_a;
46 if (std::abs(coeff_a / coeff_b - multiple) > tolerance)
return false;
48 if (std::abs(coeff_b / coeff_a - multiple) > tolerance)
return false;
56 struct ColumnFingerprint {
57 ColumnFingerprint(ColIndex _col,
int64 _hash,
double _value)
67 bool operator<(
const ColumnFingerprint& other)
const {
68 if (
hash == other.hash) {
69 return value < other.value;
71 return hash < other.hash;
78 bool AreProportionalCandidates(ColumnFingerprint
a, ColumnFingerprint
b,
80 if (
a.hash !=
b.hash)
return false;
81 return std::abs(
a.value -
b.value) < tolerance;
88 ColumnFingerprint ComputeFingerprint(ColIndex
col,
const SparseColumn& column) {
89 int64 non_zero_pattern_hash = 0;
94 non_zero_pattern_hash =
96 sum += e.coefficient();
97 min_abs =
std::min(min_abs, std::abs(e.coefficient()));
98 max_abs =
std::max(max_abs, std::abs(e.coefficient()));
105 const double inverse_dynamic_range = min_abs / max_abs;
106 const double scaled_average =
108 (
static_cast<double>(column.num_entries().value()) * max_abs);
109 return ColumnFingerprint(
col, non_zero_pattern_hash,
110 inverse_dynamic_range + scaled_average);
117 const ColIndex num_cols = matrix.
num_cols();
121 std::vector<ColumnFingerprint> fingerprints;
122 for (ColIndex
col(0);
col < num_cols; ++
col) {
124 fingerprints.push_back(ComputeFingerprint(
col, matrix.
column(
col)));
127 std::sort(fingerprints.begin(), fingerprints.end());
131 for (
int i = 0; i < fingerprints.size(); ++i) {
132 const ColIndex col_a = fingerprints[i].col;
134 for (
int j = i + 1; j < fingerprints.size(); ++j) {
135 const ColIndex col_b = fingerprints[j].col;
141 if (!AreProportionalCandidates(fingerprints[i], fingerprints[j],
145 if (AreColumnsProportional(matrix.
column(col_a), matrix.
column(col_b),
147 mapping[col_b] = col_a;
155 for (ColIndex
col(0);
col < num_cols; ++
col) {
157 const ColIndex new_representative = mapping[mapping[
col]];
159 mapping[
col] = new_representative;
162 mapping[mapping[
col]] =
col;
173 const ColIndex num_cols = matrix.
num_cols();
175 for (ColIndex col_a(0); col_a < num_cols; ++col_a) {
178 for (ColIndex col_b(col_a + 1); col_b < num_cols; ++col_b) {
181 if (AreColumnsProportional(matrix.
column(col_a), matrix.
column(col_b),
183 mapping[col_b] = col_a;
199 for (ColIndex
col(0);
col < num_cols; ++
col) {
209 for (EntryIndex i(0); i < end; ++i) {
234 const ColIndex first_identity_col =