Loading...
Searching...
No Matches
SparseVector.h
1//
2// Created by henri on 24.10.24.
3//
4
5#ifndef IDOL_SPARSEVECTOR_H
6#define IDOL_SPARSEVECTOR_H
7
8#include <vector>
9#include "idol/general/utils/exceptions/Exception.h"
10#include "idol/general/numericals.h"
11#include <idol/mixed-integer/modeling/variables/Var.h>
12#include <idol/general/utils/Map.h>
13
14namespace idol {
15 class Var;
16 class Ctr;
17
18 namespace impl {
19
20 template<class T>
21 struct hash_object {
22 auto operator()(const T& t_obj) const { return std::hash<unsigned int>()(t_obj.id()); }
23 };
24
25 template<class T>
26 struct equal_to_object {
27 auto operator()(const T& t_a, const T& t_b) const { return t_a.id() == t_b.id(); }
28 };
29
30 template<class T>
31 struct less_object {
32 auto operator()(const T& t_a, const T& t_b) const { return t_a.id() < t_b.id(); }
33 };
34
35 }
36}
37
38template<> struct std::hash<idol::Var> : public idol::impl::hash_object<idol::Var> {};
39template<> struct std::equal_to<idol::Var> : public idol::impl::equal_to_object<idol::Var> {};
40template<> struct std::less<idol::Var> : public idol::impl::less_object<idol::Var> {};
41
42template<> struct std::hash<idol::Ctr> : public idol::impl::hash_object<idol::Ctr> {};
43template<> struct std::equal_to<idol::Ctr> : public idol::impl::equal_to_object<idol::Ctr> {};
44template<> struct std::less<idol::Ctr> : public idol::impl::less_object<idol::Ctr> {};
45
46namespace idol {
47 template<class, class>
48 class SparseVector;
49}
50
51#ifdef IDOL_USE_TSL
52#include <tsl/sparse_map.h>
53#include <tsl/ordered_map.h>
54#define IDOL_REF_VALUE(t_iterator) t_iterator.value()
55#else
56#ifdef IDOL_USE_ROBINHOOD
57#include <robin_hood.h>
58#define IDOL_REF_VALUE(t_iterator) t_iterator->second
59#else
60#include <map>
61#define IDOL_REF_VALUE(t_iterator) t_iterator->second
62#endif
63#endif
64
65template<class IndexT, class ValueT>
67private:
68#ifdef IDOL_USE_TSL
69 using map_t = std::conditional_t<
70 sizeof(ValueT) < 32,
71 tsl::sparse_map<IndexT, ValueT>,
72 tsl::ordered_map<IndexT, ValueT>
73 >;
74#else
75 #ifdef IDOL_USE_ROBINHOOD
76 using map_t = robin_hood::unordered_map<IndexT, ValueT>;
77#else
78 using map_t = std::unordered_map<IndexT, ValueT>;
79#endif
80#endif
81 map_t m_map;
82
83 static ValueT s_zero_value;
84public:
85 SparseVector() = default;
86
87 SparseVector(const IndexT& t_index, const ValueT& t_value) : m_map({ { t_index, t_value} }) {
88 if (::idol::is_zero(t_value, Tolerance::Sparsity)) {
89 m_map.clear();
90 }
91 }
92
93 SparseVector(const SparseVector&) = default;
94 SparseVector(SparseVector&&) = default;
95
96 virtual ~SparseVector() = default;
97
98 SparseVector& operator=(const SparseVector&) = default;
99 SparseVector& operator=(SparseVector&&) noexcept = default;
100
101 virtual SparseVector& operator+=(const SparseVector& t_vector);
102 virtual SparseVector& operator-=(const SparseVector& t_vector);
103 virtual SparseVector& operator*=(std::conditional_t<std::is_arithmetic_v<ValueT>, ValueT, double> t_scalar);
104 virtual SparseVector& operator/=(std::conditional_t<std::is_arithmetic_v<ValueT>, ValueT, double> t_scalar);
105 SparseVector operator-() const;
106
107 [[nodiscard]] unsigned int size() const { return m_map.size(); }
108
109 [[nodiscard]] bool empty() const { return m_map.empty(); }
110
111 [[nodiscard]] bool has_index(const IndexT& t_index) const { return m_map.find(t_index) != m_map.end(); }
112
113 [[nodiscard]] const ValueT& get(const IndexT& t_index1) const;
114
115 void set(const IndexT& t_index, const ValueT& t_value);
116
117 [[nodiscard]] virtual bool is_zero(double t_tolerance) const;
118
119 void remove(const IndexT& t_index) { m_map.erase(t_index); }
120
121 void clear() { m_map.clear(); }
122
123 std::pair<double, double> range() const;
124
125 void reserve(unsigned int t_capacity) {
126#ifdef IDOL_USE_TSL
127 m_map.reserve(t_capacity);
128#endif
129 }
130
131 using iterator = typename map_t::iterator;
132 using const_iterator = typename map_t::const_iterator;
133
134 [[nodiscard]] iterator begin() { return m_map.begin(); }
135
136 [[nodiscard]] iterator end() { return m_map.end(); }
137
138 [[nodiscard]] const_iterator begin() const { return m_map.cbegin(); }
139
140 [[nodiscard]] const_iterator end() const { return m_map.cend(); }
141
142 [[nodiscard]] const_iterator cbegin() const { return m_map.cbegin(); }
143
144 [[nodiscard]] const_iterator cend() const { return m_map.cend(); }
145
146 SparseVector& merge_without_conflict(const SparseVector& t_vec);
147};
148
149template<class IndexT, class ValueT> ValueT idol::SparseVector<IndexT, ValueT>::s_zero_value {};
150
151template<class IndexT, class ValueT>
153idol::SparseVector<IndexT, ValueT>::operator-() const {
154 SparseVector result;
155
156 for (const auto& [var, val] : m_map) {
157 result.m_map.emplace(var, -val);
158 }
159
160 return result;
161}
162
163template<class IndexT, class ValueT>
165idol::SparseVector<IndexT, ValueT>::operator/=(std::conditional_t<std::is_arithmetic_v<ValueT>, ValueT, double> t_scalar) {
166
167 std::vector<IndexT> to_be_removed;
168 to_be_removed.reserve(m_map.size());
169
170 for (auto it = m_map.begin(), end = m_map.end() ; it != end ; ++it) {
171 IDOL_REF_VALUE(it) /= t_scalar;
172 if (::idol::is_zero(it->second, Tolerance::Sparsity)) {
173 to_be_removed.emplace_back(it->first);
174 }
175 }
176
177 for (const auto& index : to_be_removed) {
178 m_map.erase(index);
179 }
180
181 return *this;
182}
183
184template<class IndexT, class ValueT>
186idol::SparseVector<IndexT, ValueT>::operator-=(const SparseVector &t_vector) {
187
188 if (this == &t_vector) {
189 m_map.clear();
190 return *this;
191 }
192
193 for (const auto& [var, val] : t_vector.m_map) {
194 ValueT minus_val{};
195 minus_val -= val;
196 auto [it, inserted] = m_map.emplace(var, minus_val);
197 if (!inserted) {
198 IDOL_REF_VALUE(it) += std::move(minus_val);
199 if (::idol::is_zero(it->second, Tolerance::Sparsity)) {
200 m_map.erase(it);
201 }
202 }
203 }
204
205 return *this;
206}
207
208template<class IndexT, class ValueT>
210idol::SparseVector<IndexT, ValueT>::operator*=(std::conditional_t<std::is_arithmetic_v<ValueT>, ValueT, double> t_scalar) {
211
212 std::vector<IndexT> to_be_removed;
213 to_be_removed.reserve(m_map.size());
214
215 for (auto it = m_map.begin(), end = m_map.end() ; it != end ; ++it) {
216 IDOL_REF_VALUE(it) *= t_scalar;
217 if (::idol::is_zero(it->second, Tolerance::Sparsity)) {
218 to_be_removed.emplace_back(it->first);
219 }
220 }
221
222 for (const auto& index : to_be_removed) {
223 m_map.erase(index);
224 }
225
226 return *this;
227}
228
229template<class IndexT, class ValueT>
230bool idol::SparseVector<IndexT, ValueT>::is_zero(double t_tolerance) const {
231 for (const auto& [var, val] : m_map) {
232 if (!::idol::is_zero(val, t_tolerance)) {
233 return false;
234 }
235 }
236 return true;
237}
238
239template <class IndexT, class ValueT>
240std::pair<double, double> idol::SparseVector<IndexT, ValueT>::range() const {
241 double min = Inf, max = -Inf;
242 for (const auto& [var, val] : m_map) {
243 const double abs_val = std::abs(val);
244 min = std::min(min, abs_val);
245 max = std::max(max, abs_val);
246 }
247 return std::make_pair(min, max);
248}
249
250template<class IndexT, class ValueT>
252idol::SparseVector<IndexT, ValueT>::operator+=(const SparseVector &t_vector) {
253
254 if (this == &t_vector) {
255 return operator*=(2.);
256 }
257
258 for (const auto& [var, val] : t_vector.m_map) {
259 auto [it, inserted] = m_map.emplace(var, val);
260 if (!inserted) {
261 IDOL_REF_VALUE(it) += val;
262 if (::idol::is_zero(it->second, Tolerance::Sparsity)) {
263 m_map.erase(it);
264 }
265 }
266 }
267
268 return *this;
269}
270
271template<class IndexT, class ValueT>
273idol::SparseVector<IndexT, ValueT>::merge_without_conflict(const SparseVector &t_vec) {
274
275 for (const auto& [var, val] : t_vec.m_map) {
276 auto [it, inserted] = m_map.emplace(var, val);
277 if (!inserted) {
278 throw Exception("Found conflict when explicitly merging without conflict.");
279 }
280 }
281
282 return *this;
283}
284
285template<class IndexT, class ValueT>
286const ValueT& idol::SparseVector<IndexT, ValueT>::get(const IndexT &t_index1) const {
287
288 const auto it = m_map.find(t_index1);
289
290 if (it == m_map.end()) {
291 return s_zero_value;
292 }
293
294 return it->second;
295}
296
297template<class IndexT, class ValueT>
298void idol::SparseVector<IndexT, ValueT>::set(const IndexT &t_index, const ValueT &t_value) {
299
300 if (::idol::is_zero(t_value, Tolerance::Sparsity)) {
301 remove(t_index);
302 return;
303 }
304
305 auto [it, inserted] = m_map.emplace(t_index, t_value);
306
307 if (!inserted) {
308 IDOL_REF_VALUE(it) = t_value;
309 }
310
311}
312
313namespace idol {
314
315 template<class IndexT, class ValueT>
316 static std::ostream & operator<<(std::ostream &t_stream, const idol::SparseVector<IndexT, ValueT> &t_vector) {
317
318 for (const auto& [index, value] : t_vector) {
319 t_stream << index << ": " << value << '\n';
320 }
321
322 return t_stream;
323 }
324
325 template<class IndexT, class ValueT>
327 return std::move(t_x) += t_y;
328 }
329
330 template<class IndexT, class ValueT>
332 return std::move(t_y) += t_x;
333 }
334
335 template<class IndexT, class ValueT>
337 return SparseVector(t_x) += t_y;
338 }
339
340 template<class IndexT, class ValueT>
342 return std::move(t_x) -= t_y;
343 }
344
345 template<class IndexT, class ValueT>
346 SparseVector<IndexT, ValueT> operator+(SparseVector<IndexT, ValueT> t_x, double t_factor) {
347 return std::move(t_x) *= t_factor;
348 }
349
350 template<class IndexT, class ValueT>
351 SparseVector<IndexT, ValueT> operator+(double t_factor, SparseVector<IndexT, ValueT> t_x) {
352 return std::move(t_x) *= t_factor;
353 }
354
355}
356
357#endif //IDOL_SPARSEVECTOR_H
unsigned int id() const
Definition Object.h:60
static double Sparsity
Definition numericals.h:37