15#ifndef OR_TOOLS_MATH_OPT_STORAGE_SPARSE_MATRIX_H_
16#define OR_TOOLS_MATH_OPT_STORAGE_SPARSE_MATRIX_H_
24#include "absl/algorithm/container.h"
25#include "absl/container/flat_hash_map.h"
26#include "absl/container/flat_hash_set.h"
27#include "absl/meta/type_traits.h"
28#include "absl/types/span.h"
31#include "ortools/math_opt/sparse_containers.pb.h"
70 inline bool set(VariableId first, VariableId second,
double value);
73 inline double get(VariableId first, VariableId second)
const;
76 void Delete(VariableId variable);
82 std::vector<VariableId>
Variables()
const;
95 std::vector<std::pair<VariableId, double>>
Terms(VariableId variable)
const;
102 std::vector<std::tuple<VariableId, VariableId, double>>
Terms()
const;
118 const absl::flat_hash_map<std::pair<VariableId, VariableId>,
double>&
values()
123 SparseDoubleMatrixProto
Proto()
const;
125 SparseDoubleMatrixProto
Update(
126 const absl::flat_hash_set<VariableId>& deleted_variables,
127 absl::Span<const VariableId> new_variables,
128 const absl::flat_hash_set<std::pair<VariableId, VariableId>>& dirty)
132 inline std::pair<VariableId, VariableId> make_key(VariableId first,
133 VariableId second)
const;
134 void CompactIfNeeded();
137 absl::flat_hash_map<std::pair<VariableId, VariableId>,
double> values_;
138 absl::flat_hash_map<VariableId, std::vector<VariableId>> related_variables_;
141 int64_t nonzeros_ = 0;
174template <
typename RowId,
typename ColumnId>
195 std::vector<ColumnId>
row(RowId row_id)
const;
199 std::vector<RowId>
column(ColumnId column_id)
const;
206 std::vector<std::pair<ColumnId, double>>
RowTerms(RowId row_id)
const;
213 std::vector<std::pair<RowId, double>>
ColumnTerms(ColumnId col_id)
const;
220 std::vector<std::tuple<RowId, ColumnId, double>>
Terms()
const;
232 SparseDoubleMatrixProto
Proto()
const;
235 const absl::flat_hash_set<RowId>& deleted_rows,
236 absl::Span<const RowId> new_rows,
237 const absl::flat_hash_set<ColumnId>& deleted_columns,
238 absl::Span<const ColumnId> new_columns,
239 const absl::flat_hash_set<std::pair<RowId, ColumnId>>& dirty)
const;
242 void CompactIfNeeded();
245 absl::flat_hash_map<std::pair<RowId, ColumnId>,
double> values_;
246 absl::flat_hash_map<RowId, std::vector<ColumnId>> rows_;
247 absl::flat_hash_map<ColumnId, std::vector<RowId>> columns_;
250 int64_t nonzeros_ = 0;
260template <
typename RowId,
typename ColumnId>
262 std::vector<std::tuple<RowId, ColumnId, double>> entries);
274template <
typename RowId,
typename ColumnId>
276 std::vector<std::tuple<RowId, ColumnId, double>> entries) {
277 absl::c_sort(entries);
278 const int num_entries =
static_cast<int>(entries.size());
279 SparseDoubleMatrixProto result;
280 result.mutable_row_ids()->Reserve(num_entries);
281 result.mutable_column_ids()->Reserve(num_entries);
282 result.mutable_coefficients()->Reserve(num_entries);
283 for (
const auto [lin_con,
var,
coef] : entries) {
284 result.add_row_ids(lin_con.value());
285 result.add_column_ids(
var.value());
286 result.add_coefficients(
coef);
297std::pair<VariableId, VariableId> SparseSymmetricMatrix::make_key(
298 const VariableId first,
const VariableId second)
const {
299 return {std::min(first, second), std::max(first, second)};
303 const double value) {
304 const std::pair<VariableId, VariableId> key = make_key(first, second);
305 auto map_iter = values_.find(key);
307 if (map_iter == values_.end()) {
311 related_variables_[first].push_back(second);
312 if (first != second) {
313 related_variables_[second].push_back(first);
315 values_[key] =
value;
319 if (map_iter->second ==
value) {
322 const double old_value = map_iter->second;
323 map_iter->second =
value;
327 }
else if (old_value == 0.0) {
335 const VariableId second)
const {
343template <
typename RowId,
typename ColumnId>
345 const double value) {
346 const std::pair<RowId, ColumnId> key = {
row,
column};
347 const auto it = values_.find(key);
348 if (it == values_.end()) {
354 values_[key] =
value;
358 if (it->second ==
value) {
361 const double old_value = it->second;
366 }
else if (old_value == 0.0) {
373template <
typename RowId,
typename ColumnId>
375 const ColumnId
column)
const {
376 const auto it = values_.find({
row,
column});
377 if (it != values_.end()) {
382template <
typename RowId,
typename ColumnId>
384 const ColumnId
column)
const {
385 const auto it = values_.find({
row,
column});
386 return it != values_.end() && it->second != 0.0;
389template <
typename RowId,
typename ColumnId>
391 const auto row_it = rows_.find(
row);
392 if (row_it == rows_.end()) {
395 for (
const ColumnId
col : row_it->second) {
396 auto val_it = values_.find({row_it->first,
col});
397 if (val_it != values_.end() && val_it->second != 0.0) {
398 val_it->second = 0.0;
405template <
typename RowId,
typename ColumnId>
407 const auto col_it = columns_.find(
column);
408 if (col_it == columns_.end()) {
411 for (
const RowId
row : col_it->second) {
412 auto val_it = values_.find({
row, col_it->first});
413 if (val_it != values_.end() && val_it->second != 0.0) {
414 val_it->second = 0.0;
421template <
typename RowId,
typename ColumnId>
423 const RowId row_id)
const {
424 std::vector<ColumnId> result;
425 const auto it = rows_.find(row_id);
426 if (it != rows_.end()) {
427 for (
const ColumnId
col : it->second) {
428 if (contains(row_id,
col)) {
429 result.push_back(
col);
436template <
typename RowId,
typename ColumnId>
438 const ColumnId column_id)
const {
439 std::vector<RowId> result;
440 const auto it = columns_.find(column_id);
441 if (it != columns_.end()) {
442 for (
const RowId row_id : it->second) {
443 if (contains(row_id, column_id)) {
444 result.push_back(row_id);
451template <
typename RowId,
typename ColumnId>
452std::vector<std::pair<ColumnId, double>>
454 std::vector<std::pair<ColumnId, double>> result;
455 const auto it = rows_.find(row_id);
456 if (it != rows_.end()) {
457 for (
const ColumnId
col : it->second) {
458 const double val = get(row_id,
col);
460 result.push_back({
col, val});
467template <
typename RowId,
typename ColumnId>
468std::vector<std::pair<RowId, double>>
470 std::vector<std::pair<RowId, double>> result;
471 const auto it = columns_.find(col_id);
472 if (it != columns_.end()) {
473 for (
const RowId row_id : it->second) {
474 const double val = get(row_id, col_id);
476 result.push_back({row_id, val});
483template <
typename RowId,
typename ColumnId>
484std::vector<std::tuple<RowId, ColumnId, double>>
486 std::vector<std::tuple<RowId, ColumnId, double>> result;
487 result.reserve(nonzeros_);
488 for (
const auto& [k, v] : values_) {
490 result.push_back({k.first, k.second, v});
496template <
typename RowId,
typename ColumnId>
503template <
typename RowId,
typename ColumnId>
508template <
typename RowId,
typename ColumnId>
510 SparseDoubleMatrixProto result;
511 std::vector<std::tuple<RowId, ColumnId, double>> terms = Terms();
513 for (
const auto [r,
c, v] : terms) {
514 result.add_row_ids(r.value());
515 result.add_column_ids(
c.value());
516 result.add_coefficients(v);
521template <
typename RowId,
typename ColumnId>
523 const absl::flat_hash_set<RowId>& deleted_rows,
524 const absl::Span<const RowId> new_rows,
525 const absl::flat_hash_set<ColumnId>& deleted_columns,
526 const absl::Span<const ColumnId> new_columns,
527 const absl::flat_hash_set<std::pair<RowId, ColumnId>>& dirty)
const {
529 std::vector<std::tuple<LinearConstraintId, VariableId, double>>
536 if (deleted_rows.contains(
row) || deleted_columns.contains(
column)) {
542 for (
const ColumnId new_col : new_columns) {
544 for (
const auto [
row,
coef] : ColumnTerms(new_col)) {
545 matrix_updates.push_back({
row, new_col,
coef});
548 for (
const RowId new_row : new_rows) {
550 for (
const auto [
col,
coef] : RowTerms(new_row)) {
553 if (new_columns.empty() ||
col < new_columns[0]) {
554 matrix_updates.push_back({new_row,
col,
coef});
561template <
typename RowId,
typename ColumnId>
562void SparseMatrix<RowId, ColumnId>::CompactIfNeeded() {
563 const int64_t zeros = values_.size() - nonzeros_;
564 if (values_.empty() ||
570 for (
auto row_it = rows_.begin(); row_it != rows_.end();) {
571 const RowId
row = row_it->first;
572 std::vector<ColumnId>& row_entries = row_it->second;
574 for (int64_t read = 0; read < row_entries.size(); ++read) {
575 const ColumnId
col = row_entries[read];
576 const auto val_it = values_.find({
row,
col});
577 if (val_it != values_.end() && val_it->second != 0.0) {
578 row_entries[write] =
col;
583 rows_.erase(row_it++);
585 row_entries.resize(write);
592 for (
auto col_it = columns_.begin(); col_it != columns_.end();) {
593 const ColumnId
col = col_it->first;
594 std::vector<RowId>& col_entries = col_it->second;
596 for (int64_t read = 0; read < col_entries.size(); ++read) {
597 const RowId
row = col_entries[read];
598 const auto val_it = values_.find({
row,
col});
599 if (val_it != values_.end()) {
600 if (val_it->second != 0.0) {
601 col_entries[write] =
row;
604 values_.erase(val_it);
609 columns_.erase(col_it++);
611 col_entries.resize(write);
std::vector< RowId > column(ColumnId column_id) const
bool set(RowId row, ColumnId column, double value)
SparseDoubleMatrixProto Update(const absl::flat_hash_set< RowId > &deleted_rows, absl::Span< const RowId > new_rows, const absl::flat_hash_set< ColumnId > &deleted_columns, absl::Span< const ColumnId > new_columns, const absl::flat_hash_set< std::pair< RowId, ColumnId > > &dirty) const
std::vector< ColumnId > row(RowId row_id) const
void Clear()
Removes all terms from the matrix.
double get(RowId row, ColumnId column) const
Zero is returned if the value is not present.
std::vector< std::pair< RowId, double > > ColumnTerms(ColumnId col_id) const
void DeleteColumn(ColumnId column)
Zeros out all coefficients for this variable.
std::vector< std::tuple< RowId, ColumnId, double > > Terms() const
int64_t impl_detail_matrix_storage_size() const
void DeleteRow(RowId row)
Zeros out all coefficients for this variable.
bool contains(RowId row, ColumnId column) const
Returns true if the value is present (nonzero).
SparseDoubleMatrixProto Proto() const
int64_t nonzeros() const
The number of (row, column) keys with nonzero value.
std::vector< std::pair< ColumnId, double > > RowTerms(RowId row_id) const
SparseDoubleMatrixProto Update(const absl::flat_hash_set< VariableId > &deleted_variables, absl::Span< const VariableId > new_variables, const absl::flat_hash_set< std::pair< VariableId, VariableId > > &dirty) const
std::vector< VariableId > RelatedVariables(VariableId variable) const
std::vector< std::tuple< VariableId, VariableId, double > > Terms() const
const absl::flat_hash_map< std::pair< VariableId, VariableId >, double > & values() const
void Clear()
Removes all terms from the matrix.
int64_t impl_detail_matrix_storage_size() const
std::vector< VariableId > Variables() const
double get(VariableId first, VariableId second) const
Zero is returned if the value is not present.
void Delete(VariableId variable)
Zeros out all coefficients for this variable.
SparseDoubleMatrixProto Proto() const
bool set(VariableId first, VariableId second, double value)
const MapUtilMappedT< Collection > & FindWithDefault(const Collection &collection, const KeyType &key, const MapUtilMappedT< Collection > &value)
constexpr double kZerosCleanup
SparseDoubleMatrixProto EntriesToMatrixProto(std::vector< std::tuple< RowId, ColumnId, double > > entries)
entries must have unique (row, column) values but can be in any order.
An object oriented wrapper for quadratic constraints in ModelStorage.