OR-Tools  9.2
model_update_merge.h
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
14#ifndef OR_TOOLS_MATH_OPT_CORE_MODEL_UPDATE_MERGE_H_
15#define OR_TOOLS_MATH_OPT_CORE_MODEL_UPDATE_MERGE_H_
16
17#include <algorithm>
18#include <cstdint>
19
23#include "ortools/math_opt/model_update.pb.h"
24#include "ortools/math_opt/sparse_containers.pb.h"
25
26namespace operations_research {
27namespace math_opt {
28
29// Merges the `from_new` update into the `into_old` one.
30//
31// The `from_new` update must represent an update that happens after the
32// `into_old` one is applied. Thus when the two updates have overlaps, the
33// `from_new` one overrides the value of the `into_old` one (i.e. the `from_new`
34// update is expected to be more recent).
35//
36// This function also CHECKs that the ids of new variables and constraints in
37// `from_new` are greater than the ones in `into_old` (as expected if `from_new`
38// happens after `into_old`).
39//
40// Note that the complexity is O(size(from_new) + size(into_old)) thus if you
41// need to merge a long list of updates this may be not efficient enough. In
42// that case an n-way merge would be needed to be implemented here.
43void MergeIntoUpdate(const ModelUpdateProto& from_new,
44 ModelUpdateProto& into_old);
45
46namespace internal {
47
48// Removes from the sorted list `ids` all elements found in the sorted list
49// `deleted`. The elements should be unique in each sorted list.
50void RemoveDeletedIds(google::protobuf::RepeatedField<int64_t>& ids,
51 const google::protobuf::RepeatedField<int64_t>& deleted);
52
53// Merges the `from_new` list of sorted ids into the `into_old` one. Elements
54// appearing in `from_new` that already exist in `into_old` are ignored.
55//
56// The input `deleted` should contains a sorted list of ids of elements that
57// have been deleted and should be removed from the merge.
58//
59// The elements should be unique in each sorted list.
60void MergeIntoSortedIds(const google::protobuf::RepeatedField<int64_t>& from_new,
61 google::protobuf::RepeatedField<int64_t>& into_old,
62 const google::protobuf::RepeatedField<int64_t>& deleted);
63
64// Merges the `from_new` sparse vector into the `into_old` one. When the two
65// vectors have overlaps, the value in `from_new` is used to overwrite the one
66// in `into_old`.
67//
68// The input `deleted` should contains a sorted list of unique ids of elements
69// that have been deleted and should be removed from the merge.
70//
71// The SparseVector type is either SparseDoubleVectorProto or
72// SparseBoolVectorProto.
73template <typename SparseVector>
74void MergeIntoSparseVector(const SparseVector& from_new, SparseVector& into_old,
75 const google::protobuf::RepeatedField<int64_t>& deleted);
76
77// Merges the `from_new` sparse matrix into the `into_old` one. When the two
78// matrices have overlaps, the value in `from_new` is used to overwrite the one
79// in `into_old`.
80//
81// The input `deleted_rows` and `deleted_columns` should contains sorted lists
82// of unique ids of rows and cols that have been deleted and should be removed
83// from the merge.
85 const SparseDoubleMatrixProto& from_new, SparseDoubleMatrixProto& into_old,
86 const google::protobuf::RepeatedField<int64_t>& deleted_rows,
87 const google::protobuf::RepeatedField<int64_t>& deleted_columns);
88
89// Updates a "property" repeated field of a ModelUpdateProto.new_variables or
90// ModelUpdateProto.new_linear_constraints.
91//
92// The `ids` input corresponds to VariablesProto.ids (or
93// LinearConstraintsProto.ids), and the values one to one property (for example
94// VariablesProto.lower_bounds). Values corresponding to ids in `deleted` are
95// removed. For the ids that have a value in `updates`, this value is used to
96// replace the existing one.
97//
98// The type SparseVector can either be a sparse proto like
99// SparseDoubleVectorProto or a SparseVectorView. The type RepeatedField is
100// usually a google::protobuf::RepeatedField but it can be also a
101// RepeatedPtrField<std::string> to deal with the `names` property.
102template <typename RepeatedField, typename SparseVector>
103void UpdateNewElementProperty(const google::protobuf::RepeatedField<int64_t>& ids,
104 RepeatedField& values,
105 const google::protobuf::RepeatedField<int64_t>& deleted,
106 const SparseVector& updates);
107
108} // namespace internal
109
111// Inline functions implementations.
113
114namespace internal {
115
116template <typename SparseVector>
117void MergeIntoSparseVector(const SparseVector& from_new, SparseVector& into_old,
118 const google::protobuf::RepeatedField<int64_t>& deleted) {
119 CHECK_EQ(from_new.ids_size(), from_new.values_size());
120 CHECK_EQ(into_old.ids_size(), into_old.values_size());
121
122 SparseVector result;
123 auto& result_ids = *result.mutable_ids();
124 auto& result_values = *result.mutable_values();
125
126 int from_new_i = 0;
127 int into_old_i = 0;
128 int deleted_i = 0;
129
130 // Functions that adds the input pair (id, value) to the result if the input
131 // id is not in deleted. It updates deleted_i as a side effect too.
132 const auto add_if_not_deleted =
133 [&](const int64_t id, const sparse_value_type<SparseVector>& value) {
134 while (deleted_i < deleted.size() && deleted[deleted_i] < id) {
135 ++deleted_i;
136 }
137 if (deleted_i == deleted.size() || deleted[deleted_i] != id) {
138 result_ids.Add(id);
139 result_values.Add(value);
140 }
141 };
142
143 while (from_new_i < from_new.ids_size() && into_old_i < into_old.ids_size()) {
144 if (from_new.ids(from_new_i) < into_old.ids(into_old_i)) {
145 add_if_not_deleted(from_new.ids(from_new_i), from_new.values(from_new_i));
146 ++from_new_i;
147 } else if (from_new.ids(from_new_i) > into_old.ids(into_old_i)) {
148 add_if_not_deleted(into_old.ids(into_old_i), into_old.values(into_old_i));
149 ++into_old_i;
150 } else { // from_new.ids(from_new_i) == into_old.ids(into_old_i)
151 add_if_not_deleted(from_new.ids(from_new_i), from_new.values(from_new_i));
152 ++from_new_i;
153 ++into_old_i;
154 }
155 }
156
157 // At this point either from_new_i == from_new.ids_size() or to_i ==
158 // to.ids_size() (or both). And the one that is not empty, if it exists, has
159 // elements greater than all other elements already inserted.
160 for (; from_new_i < from_new.ids_size(); ++from_new_i) {
161 add_if_not_deleted(from_new.ids(from_new_i), from_new.values(from_new_i));
162 }
163 for (; into_old_i < into_old.ids_size(); ++into_old_i) {
164 add_if_not_deleted(into_old.ids(into_old_i), into_old.values(into_old_i));
165 }
166
167 into_old.Swap(&result);
168}
169
170template <typename RepeatedField, typename SparseVector>
171void UpdateNewElementProperty(const google::protobuf::RepeatedField<int64_t>& ids,
172 RepeatedField& values,
173 const google::protobuf::RepeatedField<int64_t>& deleted,
174 const SparseVector& updates) {
175 int next_insertion_point = 0;
176 int deleted_i = 0;
177 int updates_i = 0;
178
179 for (int i = 0; i < ids.size(); ++i) {
180 const int id = ids[i];
181
182 while (deleted_i < deleted.size() && deleted[deleted_i] < id) {
183 ++deleted_i;
184 }
185 if (deleted_i < deleted.size() && deleted[deleted_i] == id) {
186 continue;
187 }
188
189 while (updates_i < updates.ids_size() && updates.ids(updates_i) < id) {
190 ++updates_i;
191 }
192 if (updates_i < updates.ids_size() && updates.ids(updates_i) == id) {
193 values[next_insertion_point] = updates.values(updates_i);
194 } else {
195 // Here we use SwapElements() to prevent copies when `values` is a
196 // RepeatedPtrField<std::string>.
197 values.SwapElements(next_insertion_point, i);
198 }
199 ++next_insertion_point;
200 }
201
202 // We can't use value.Truncate() here since RepeatedPtrField<std::string> does
203 // not implement it.
204 google::protobuf::util::Truncate(&values, next_insertion_point);
205}
206
207} // namespace internal
208} // namespace math_opt
209} // namespace operations_research
210
211#endif // OR_TOOLS_MATH_OPT_CORE_MODEL_UPDATE_MERGE_H_
#define CHECK_EQ(val1, val2)
Definition: base/logging.h:702
int64_t value
void Truncate(RepeatedPtrField< T > *array, int new_size)
Definition: protobuf_util.h:28
void MergeIntoSparseDoubleMatrix(const SparseDoubleMatrixProto &from_new, SparseDoubleMatrixProto &into_old, const google::protobuf::RepeatedField< int64_t > &deleted_rows, const google::protobuf::RepeatedField< int64_t > &deleted_columns)
void MergeIntoSortedIds(const google::protobuf::RepeatedField< int64_t > &from_new, google::protobuf::RepeatedField< int64_t > &into_old, const google::protobuf::RepeatedField< int64_t > &deleted)
void UpdateNewElementProperty(const google::protobuf::RepeatedField< int64_t > &ids, RepeatedField &values, const google::protobuf::RepeatedField< int64_t > &deleted, const SparseVector &updates)
void RemoveDeletedIds(google::protobuf::RepeatedField< int64_t > &ids, const google::protobuf::RepeatedField< int64_t > &deleted)
void MergeIntoSparseVector(const SparseVector &from_new, SparseVector &into_old, const google::protobuf::RepeatedField< int64_t > &deleted)
typename std::remove_reference< decltype(SparseVector().values())>::type::value_type sparse_value_type
void MergeIntoUpdate(const ModelUpdateProto &from_new, ModelUpdateProto &into_old)
Collection of objects used to extend the Constraint Solver library.