OR-Tools  9.3
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.
61 const google::protobuf::RepeatedField<int64_t>& from_new,
62 google::protobuf::RepeatedField<int64_t>& into_old,
63 const google::protobuf::RepeatedField<int64_t>& deleted);
64
65// Merges the `from_new` sparse vector into the `into_old` one. When the two
66// vectors have overlaps, the value in `from_new` is used to overwrite the one
67// in `into_old`.
68//
69// The input `deleted` should contains a sorted list of unique ids of elements
70// that have been deleted and should be removed from the merge.
71//
72// The SparseVector type is either SparseDoubleVectorProto or
73// SparseBoolVectorProto.
74template <typename SparseVector>
76 const SparseVector& from_new, SparseVector& into_old,
77 const google::protobuf::RepeatedField<int64_t>& deleted);
78
79// Merges the `from_new` sparse matrix into the `into_old` one. When the two
80// matrices have overlaps, the value in `from_new` is used to overwrite the one
81// in `into_old`.
82//
83// The input `deleted_rows` and `deleted_columns` should contains sorted lists
84// of unique ids of rows and cols that have been deleted and should be removed
85// from the merge.
87 const SparseDoubleMatrixProto& from_new, SparseDoubleMatrixProto& into_old,
88 const google::protobuf::RepeatedField<int64_t>& deleted_rows,
89 const google::protobuf::RepeatedField<int64_t>& deleted_columns);
90
91// Updates a "property" repeated field of a ModelUpdateProto.new_variables or
92// ModelUpdateProto.new_linear_constraints.
93//
94// The `ids` input corresponds to VariablesProto.ids (or
95// LinearConstraintsProto.ids), and the values one to one property (for example
96// VariablesProto.lower_bounds). Values corresponding to ids in `deleted` are
97// removed. For the ids that have a value in `updates`, this value is used to
98// replace the existing one.
99//
100// The type SparseVector can either be a sparse proto like
101// SparseDoubleVectorProto or a SparseVectorView. The type RepeatedField is
102// usually a google::protobuf::RepeatedField but it can be also a
103// RepeatedPtrField<std::string> to deal with the `names` property.
104template <typename RepeatedField, typename SparseVector>
106 const google::protobuf::RepeatedField<int64_t>& ids, RepeatedField& values,
107 const google::protobuf::RepeatedField<int64_t>& deleted,
108 const SparseVector& updates);
109
110} // namespace internal
111
113// Inline functions implementations.
115
116namespace internal {
117
118template <typename SparseVector>
120 const SparseVector& from_new, SparseVector& into_old,
121 const google::protobuf::RepeatedField<int64_t>& deleted) {
122 CHECK_EQ(from_new.ids_size(), from_new.values_size());
123 CHECK_EQ(into_old.ids_size(), into_old.values_size());
124
125 SparseVector result;
126 auto& result_ids = *result.mutable_ids();
127 auto& result_values = *result.mutable_values();
128
129 int from_new_i = 0;
130 int into_old_i = 0;
131 int deleted_i = 0;
132
133 // Functions that adds the input pair (id, value) to the result if the input
134 // id is not in deleted. It updates deleted_i as a side effect too.
135 const auto add_if_not_deleted =
136 [&](const int64_t id, const sparse_value_type<SparseVector>& value) {
137 while (deleted_i < deleted.size() && deleted[deleted_i] < id) {
138 ++deleted_i;
139 }
140 if (deleted_i == deleted.size() || deleted[deleted_i] != id) {
141 result_ids.Add(id);
142 result_values.Add(value);
143 }
144 };
145
146 while (from_new_i < from_new.ids_size() && into_old_i < into_old.ids_size()) {
147 if (from_new.ids(from_new_i) < into_old.ids(into_old_i)) {
148 add_if_not_deleted(from_new.ids(from_new_i), from_new.values(from_new_i));
149 ++from_new_i;
150 } else if (from_new.ids(from_new_i) > into_old.ids(into_old_i)) {
151 add_if_not_deleted(into_old.ids(into_old_i), into_old.values(into_old_i));
152 ++into_old_i;
153 } else { // from_new.ids(from_new_i) == into_old.ids(into_old_i)
154 add_if_not_deleted(from_new.ids(from_new_i), from_new.values(from_new_i));
155 ++from_new_i;
156 ++into_old_i;
157 }
158 }
159
160 // At this point either from_new_i == from_new.ids_size() or to_i ==
161 // to.ids_size() (or both). And the one that is not empty, if it exists, has
162 // elements greater than all other elements already inserted.
163 for (; from_new_i < from_new.ids_size(); ++from_new_i) {
164 add_if_not_deleted(from_new.ids(from_new_i), from_new.values(from_new_i));
165 }
166 for (; into_old_i < into_old.ids_size(); ++into_old_i) {
167 add_if_not_deleted(into_old.ids(into_old_i), into_old.values(into_old_i));
168 }
169
170 into_old.Swap(&result);
171}
172
173template <typename RepeatedField, typename SparseVector>
175 const google::protobuf::RepeatedField<int64_t>& ids, RepeatedField& values,
176 const google::protobuf::RepeatedField<int64_t>& deleted,
177 const SparseVector& updates) {
178 int next_insertion_point = 0;
179 int deleted_i = 0;
180 int updates_i = 0;
181
182 for (int i = 0; i < ids.size(); ++i) {
183 const int64_t id = ids[i];
184
185 while (deleted_i < deleted.size() && deleted[deleted_i] < id) {
186 ++deleted_i;
187 }
188 if (deleted_i < deleted.size() && deleted[deleted_i] == id) {
189 continue;
190 }
191
192 while (updates_i < updates.ids_size() && updates.ids(updates_i) < id) {
193 ++updates_i;
194 }
195 if (updates_i < updates.ids_size() && updates.ids(updates_i) == id) {
196 values[next_insertion_point] = updates.values(updates_i);
197 } else {
198 // Here we use SwapElements() to prevent copies when `values` is a
199 // RepeatedPtrField<std::string>.
200 values.SwapElements(next_insertion_point, i);
201 }
202 ++next_insertion_point;
203 }
204
205 // We can't use value.Truncate() here since RepeatedPtrField<std::string> does
206 // not implement it.
207 google::protobuf::util::Truncate(&values, next_insertion_point);
208}
209
210} // namespace internal
211} // namespace math_opt
212} // namespace operations_research
213
214#endif // OR_TOOLS_MATH_OPT_CORE_MODEL_UPDATE_MERGE_H_
#define CHECK_EQ(val1, val2)
Definition: base/logging.h:703
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.