tatami
C++ API for different matrix representations
Loading...
Searching...
No Matches
arithmetic_helpers.hpp
Go to the documentation of this file.
1#ifndef TATAMI_ISOMETRIC_UNARY_ARITHMETIC_HELPERS_H
2#define TATAMI_ISOMETRIC_UNARY_ARITHMETIC_HELPERS_H
3
6#include <vector>
7#include <limits>
8#include <type_traits>
9
16namespace tatami {
17
21template<ArithmeticOperation op_, bool right_, typename InputValue_, typename Index_, typename Scalar_, typename OutputValue_>
22void delayed_arithmetic_run_simple(const InputValue_* input, Index_ length, Scalar_ scalar, OutputValue_* output) {
23 if constexpr(std::is_same<InputValue_, OutputValue_>::value) {
24 input = output; // basically an assertion to the compiler to skip aliasing protection.
25 }
26 for (Index_ i = 0; i < length; ++i) {
27 output[i] = delayed_arithmetic<op_, right_>(input[i], scalar);
28 }
29}
30
31// The '*_actual_sparse' and '*_zero' functions should be mirrors of each other;
32// we enforce this by putting their logic all in the same place.
33template<bool check_only_, ArithmeticOperation op_, bool right_, typename OutputValue_, typename InputValue_, typename Scalar_>
34auto delayed_arithmetic_zeroish(Scalar_ scalar) {
35 if constexpr(has_unsafe_divide_by_zero<op_, right_, InputValue_, Scalar_>()) {
36 if constexpr(right_) {
37 if (scalar) {
38 OutputValue_ val = delayed_arithmetic<op_, right_, InputValue_, Scalar_>(0, scalar);
39 if constexpr(check_only_) {
40 return val == 0;
41 } else {
42 return val;
43 }
44 }
45 }
46
47 if constexpr(check_only_) {
48 return false;
49 } else {
50 throw std::runtime_error("division by zero is not supported");
51 return 0;
52 }
53
54 } else {
55 OutputValue_ val = delayed_arithmetic<op_, right_, InputValue_, Scalar_>(0, scalar);
56 if constexpr(check_only_) {
57 return val == 0;
58 } else {
59 return val;
60 }
61 }
62}
63
64template<ArithmeticOperation op_, bool right_, typename OutputValue_, typename InputValue_, typename Scalar_>
65bool delayed_arithmetic_actual_sparse(Scalar_ scalar) {
66 return delayed_arithmetic_zeroish<true, op_, right_, OutputValue_, InputValue_, Scalar_>(scalar);
67}
68
69template<ArithmeticOperation op_, bool right_, typename OutputValue_, typename InputValue_, typename Scalar_>
70OutputValue_ delayed_arithmetic_zero(Scalar_ scalar) {
71 return delayed_arithmetic_zeroish<false, op_, right_, OutputValue_, InputValue_, Scalar_>(scalar);
72}
92template<ArithmeticOperation op_, bool right_, typename OutputValue_, typename InputValue_, typename Index_, typename Scalar_>
93class DelayedUnaryIsometricArithmeticScalarHelper final : public DelayedUnaryIsometricOperationHelper<OutputValue_, InputValue_, Index_> {
94public:
98 DelayedUnaryIsometricArithmeticScalarHelper(Scalar_ scalar) : my_scalar(scalar) {
99 my_sparse = delayed_arithmetic_actual_sparse<op_, right_, OutputValue_, InputValue_>(my_scalar);
100 }
101
102private:
103 Scalar_ my_scalar;
104 bool my_sparse;
105
106public:
107 std::optional<Index_> nrow() const {
108 return std::nullopt;
109 }
110
111 std::optional<Index_> ncol() const {
112 return std::nullopt;
113 }
114
115public:
116 bool zero_depends_on_row() const {
117 return false;
118 }
119
121 return false;
122 }
123
125 return false;
126 }
127
129 return false;
130 }
131
132public:
133 void dense(bool, Index_, Index_, Index_ length, const InputValue_* input, OutputValue_* output) const {
134 delayed_arithmetic_run_simple<op_, right_>(input, length, my_scalar, output);
135 }
136
137 void dense(bool, Index_, const std::vector<Index_>& indices, const InputValue_* input, OutputValue_* output) const {
138 delayed_arithmetic_run_simple<op_, right_>(input, static_cast<Index_>(indices.size()), my_scalar, output);
139 }
140
141public:
142 bool is_sparse() const {
143 return my_sparse;
144 }
145
146 void sparse(bool, Index_, Index_ number, const InputValue_* input_value, const Index_*, OutputValue_* output_value) const {
147 delayed_arithmetic_run_simple<op_, right_>(input_value, number, my_scalar, output_value);
148 }
149
150 OutputValue_ fill(bool, Index_) const {
151 // We perform the operation with the InputValue_ before casting it to
152 // the OutputValue_, which is consistent with the behavior of all other
153 // methods. See ../arithmetic_utils.hpp for some comments about the
154 // safety of this cast when the value is known at compile time.
155 return delayed_arithmetic_zero<op_, right_, OutputValue_, InputValue_>(my_scalar);
156 }
157};
158
167template<typename OutputValue_, typename InputValue_, typename Index_, typename Scalar_>
169
179template<bool right_, typename OutputValue_, typename InputValue_, typename Index_, typename Scalar_>
181
190template<typename OutputValue_, typename InputValue_, typename Index_, typename Scalar_>
192
202template<bool right_, typename OutputValue_, typename InputValue_, typename Index_, typename Scalar_>
204
214template<bool right_, typename OutputValue_, typename InputValue_, typename Index_, typename Scalar_>
216
226template<bool right_, typename OutputValue_, typename InputValue_, typename Index_, typename Scalar_>
228
238template<bool right_, typename OutputValue_, typename InputValue_, typename Index_, typename Scalar_>
240
244// Back-compatibility only.
245template<typename OutputValue_ = double, typename InputValue_ = double, typename Index_ = int, typename Scalar_>
246std::shared_ptr<DelayedUnaryIsometricOperationHelper<OutputValue_, InputValue_, Index_> > make_DelayedUnaryIsometricAddScalar(Scalar_ scalar) {
247 return std::make_shared<DelayedUnaryIsometricAddScalarHelper<OutputValue_, InputValue_, Index_, Scalar_> >(std::move(scalar));
248}
249
250template<bool right_, typename OutputValue_ = double, typename InputValue_ = double, typename Index_ = int, typename Scalar_>
251std::shared_ptr<DelayedUnaryIsometricOperationHelper<OutputValue_, InputValue_, Index_> > make_DelayedUnaryIsometricSubtractScalar(Scalar_ scalar) {
252 return std::make_shared<DelayedUnaryIsometricSubtractScalarHelper<right_, OutputValue_, InputValue_, Index_, Scalar_> >(std::move(scalar));
253}
254
255template<typename OutputValue_ = double, typename InputValue_ = double, typename Index_ = int, typename Scalar_>
256std::shared_ptr<DelayedUnaryIsometricOperationHelper<OutputValue_, InputValue_, Index_> > make_DelayedUnaryIsometricMultiplyScalar(Scalar_ scalar) {
257 return std::make_shared<DelayedUnaryIsometricMultiplyScalarHelper<OutputValue_, InputValue_, Index_, Scalar_> >(std::move(scalar));
258}
259
260template<bool right_, typename OutputValue_ = double, typename InputValue_ = double, typename Index_ = int, typename Scalar_>
261std::shared_ptr<DelayedUnaryIsometricOperationHelper<OutputValue_, InputValue_, Index_> > make_DelayedUnaryIsometricDivideScalar(Scalar_ scalar) {
262 return std::make_shared<DelayedUnaryIsometricDivideScalarHelper<right_, OutputValue_, InputValue_, Index_, Scalar_> >(std::move(scalar));
263}
264
265template<bool right_, typename OutputValue_ = double, typename InputValue_ = double, typename Index_ = int, typename Scalar_>
266std::shared_ptr<DelayedUnaryIsometricOperationHelper<OutputValue_, InputValue_, Index_> > make_DelayedUnaryIsometricModuloScalar(Scalar_ scalar) {
267 return std::make_shared<DelayedUnaryIsometricModuloScalarHelper<right_, OutputValue_, InputValue_, Index_, Scalar_> >(std::move(scalar));
268}
269
270template<bool right_, typename OutputValue_ = double, typename InputValue_ = double, typename Index_ = int, typename Scalar_>
271std::shared_ptr<DelayedUnaryIsometricOperationHelper<OutputValue_, InputValue_, Index_> > make_DelayedUnaryIsometricPowerScalar(Scalar_ scalar) {
272 return std::make_shared<DelayedUnaryIsometricPowerScalarHelper<right_, OutputValue_, InputValue_, Index_, Scalar_> >(std::move(scalar));
273}
274
275template<bool right_, typename OutputValue_ = double, typename InputValue_ = double, typename Index_ = int, typename Scalar_>
276std::shared_ptr<DelayedUnaryIsometricOperationHelper<OutputValue_, InputValue_, Index_> > make_DelayedUnaryIsometricIntegerDivideScalar(Scalar_ scalar) {
277 return std::make_shared<DelayedUnaryIsometricIntegerDivideScalarHelper<right_, OutputValue_, InputValue_, Index_, Scalar_> >(std::move(scalar));
278}
299template<ArithmeticOperation op_, bool right_, typename OutputValue_, typename InputValue_, typename Index_, typename Vector_>
300class DelayedUnaryIsometricArithmeticVectorHelper final : public DelayedUnaryIsometricOperationHelper<OutputValue_, InputValue_, Index_> {
301public:
309 DelayedUnaryIsometricArithmeticVectorHelper(Vector_ vector, bool by_row) : my_vector(std::move(vector)), my_by_row(by_row) {
310 for (auto x : my_vector) {
311 if (!delayed_arithmetic_actual_sparse<op_, right_, OutputValue_, InputValue_>(x)) {
312 my_sparse = false;
313 break;
314 }
315 }
316 }
317
318private:
319 Vector_ my_vector;
320 bool my_by_row;
321 bool my_sparse = true;
322
323public:
324 std::optional<Index_> nrow() const {
325 if (my_by_row) {
326 return my_vector.size();
327 } else {
328 return std::nullopt;
329 }
330 }
331
332 std::optional<Index_> ncol() const {
333 if (my_by_row) {
334 return std::nullopt;
335 } else {
336 return my_vector.size();
337 }
338 }
339
340public:
341 bool zero_depends_on_row() const {
342 return my_by_row;
343 }
344
346 return !my_by_row;
347 }
348
350 return my_by_row;
351 }
352
354 return !my_by_row;
355 }
356
357public:
358 void dense(bool row, Index_ idx, Index_ start, Index_ length, const InputValue_* input, OutputValue_* output) const {
359 if (row == my_by_row) {
360 delayed_arithmetic_run_simple<op_, right_>(input, length, my_vector[idx], output);
361 } else {
362 if constexpr(std::is_same<InputValue_, OutputValue_>::value) {
363 input = output; // basically an assertion to the compiler to skip aliasing protection.
364 }
365 for (Index_ i = 0; i < length; ++i) {
366 output[i] = delayed_arithmetic<op_, right_>(input[i], my_vector[i + start]);
367 }
368 }
369 }
370
371 void dense(bool row, Index_ idx, const std::vector<Index_>& indices, const InputValue_* input, OutputValue_* output) const {
372 if (row == my_by_row) {
373 delayed_arithmetic_run_simple<op_, right_>(input, static_cast<Index_>(indices.size()), my_vector[idx], output);
374 } else {
375 if constexpr(std::is_same<InputValue_, OutputValue_>::value) {
376 input = output; // basically an assertion to the compiler to skip aliasing protection.
377 }
378 Index_ length = indices.size();
379 for (Index_ i = 0; i < length; ++i) {
380 output[i] = delayed_arithmetic<op_, right_>(input[i], my_vector[indices[i]]);
381 }
382 }
383 }
384
385public:
386 bool is_sparse() const {
387 return my_sparse;
388 }
389
390 void sparse(bool row, Index_ idx, Index_ number, const InputValue_* input_value, const Index_* index, OutputValue_* output_value) const {
391 if (row == my_by_row) {
392 delayed_arithmetic_run_simple<op_, right_>(input_value, number, my_vector[idx], output_value);
393 } else {
394 if constexpr(std::is_same<InputValue_, OutputValue_>::value) {
395 input_value = output_value; // basically an assertion to the compiler to skip aliasing protection.
396 }
397 for (Index_ i = 0; i < number; ++i) {
398 output_value[i] = delayed_arithmetic<op_, right_>(input_value[i], my_vector[index[i]]);
399 }
400 }
401 }
402
403 OutputValue_ fill(bool row, Index_ idx) const {
404 if (row == my_by_row) {
405 return delayed_arithmetic_zero<op_, right_, OutputValue_, InputValue_>(my_vector[idx]);
406 } else {
407 // We should only get to this point if it's sparse, otherwise no
408 // single fill value would work across the length of my_vector.
409 return 0;
410 }
411 }
412};
413
422template<typename OutputValue_, typename InputValue_, typename Index_, typename Vector_>
424
434template<bool right_, typename OutputValue_, typename InputValue_, typename Index_, typename Vector_>
436
445template<typename OutputValue_, typename InputValue_, typename Index_, typename Vector_>
447
457template<bool right_, typename OutputValue_, typename InputValue_, typename Index_, typename Vector_>
459
469template<bool right_, typename OutputValue_, typename InputValue_, typename Index_, typename Vector_>
471
481template<bool right_, typename OutputValue_, typename InputValue_, typename Index_, typename Vector_>
483
493template<bool right_, typename OutputValue_, typename InputValue_, typename Index_, typename Vector_>
495
499// Back-compatibility only.
500template<typename OutputValue_ = double, typename InputValue_ = double, typename Index_ = int, typename Vector_>
501std::shared_ptr<DelayedUnaryIsometricOperationHelper<OutputValue_, InputValue_, Index_> > make_DelayedUnaryIsometricAddVector(Vector_ vector, bool by_row) {
502 return std::make_shared<DelayedUnaryIsometricAddVectorHelper<OutputValue_, InputValue_, Index_, Vector_> >(std::move(vector), by_row);
503}
504
505template<bool right_, typename OutputValue_ = double, typename InputValue_ = double, typename Index_ = int, typename Vector_>
506std::shared_ptr<DelayedUnaryIsometricOperationHelper<OutputValue_, InputValue_, Index_> > make_DelayedUnaryIsometricSubtractVector(Vector_ vector, bool by_row) {
507 return std::make_shared<DelayedUnaryIsometricSubtractVectorHelper<right_, OutputValue_, InputValue_, Index_, Vector_> >(std::move(vector), by_row);
508}
509
510template<typename OutputValue_ = double, typename InputValue_ = double, typename Index_ = int, typename Vector_>
511std::shared_ptr<DelayedUnaryIsometricOperationHelper<OutputValue_, InputValue_, Index_> > make_DelayedUnaryIsometricMultiplyVector(Vector_ vector, bool by_row) {
512 return std::make_shared<DelayedUnaryIsometricMultiplyVectorHelper<OutputValue_, InputValue_, Index_, Vector_> >(std::move(vector), by_row);
513}
514
515template<bool right_, typename OutputValue_ = double, typename InputValue_ = double, typename Index_ = int, typename Vector_>
516std::shared_ptr<DelayedUnaryIsometricOperationHelper<OutputValue_, InputValue_, Index_> > make_DelayedUnaryIsometricDivideVector(Vector_ vector, bool by_row) {
517 return std::make_shared<DelayedUnaryIsometricDivideVectorHelper<right_, OutputValue_, InputValue_, Index_, Vector_> >(std::move(vector), by_row);
518}
519
520template<bool right_, typename OutputValue_ = double, typename InputValue_ = double, typename Index_ = int, typename Vector_>
521std::shared_ptr<DelayedUnaryIsometricOperationHelper<OutputValue_, InputValue_, Index_> > make_DelayedUnaryIsometricModuloVector(Vector_ vector, bool by_row) {
522 return std::make_shared<DelayedUnaryIsometricModuloVectorHelper<right_, OutputValue_, InputValue_, Index_, Vector_> >(std::move(vector), by_row);
523}
524
525template<bool right_, typename OutputValue_ = double, typename InputValue_ = double, typename Index_ = int, typename Vector_>
526std::shared_ptr<DelayedUnaryIsometricOperationHelper<OutputValue_, InputValue_, Index_> > make_DelayedUnaryIsometricPowerVector(Vector_ vector, bool by_row) {
527 return std::make_shared<DelayedUnaryIsometricPowerVectorHelper<right_, OutputValue_, InputValue_, Index_, Vector_> >(std::move(vector), by_row);
528}
529
530template<bool right_, typename OutputValue_ = double, typename InputValue_ = double, typename Index_ = int, typename Vector_>
531std::shared_ptr<DelayedUnaryIsometricOperationHelper<OutputValue_, InputValue_, Index_> > make_DelayedUnaryIsometricIntegerDivideVector(Vector_ vector, bool by_row) {
532 return std::make_shared<DelayedUnaryIsometricIntegerDivideVectorHelper<right_, OutputValue_, InputValue_, Index_, Vector_> >(std::move(vector), by_row);
533}
538}
539
540#endif
Utilities for delayed arithmetic operations.
Helper for delayed unary isometric scalar arithmetic.
Definition arithmetic_helpers.hpp:93
bool zero_depends_on_row() const
Definition arithmetic_helpers.hpp:116
void sparse(bool, Index_, Index_ number, const InputValue_ *input_value, const Index_ *, OutputValue_ *output_value) const
Definition arithmetic_helpers.hpp:146
OutputValue_ fill(bool, Index_) const
Definition arithmetic_helpers.hpp:150
bool non_zero_depends_on_row() const
Definition arithmetic_helpers.hpp:124
DelayedUnaryIsometricArithmeticScalarHelper(Scalar_ scalar)
Definition arithmetic_helpers.hpp:98
std::optional< Index_ > nrow() const
Definition arithmetic_helpers.hpp:107
bool is_sparse() const
Definition arithmetic_helpers.hpp:142
void dense(bool, Index_, const std::vector< Index_ > &indices, const InputValue_ *input, OutputValue_ *output) const
Definition arithmetic_helpers.hpp:137
void dense(bool, Index_, Index_, Index_ length, const InputValue_ *input, OutputValue_ *output) const
Definition arithmetic_helpers.hpp:133
bool zero_depends_on_column() const
Definition arithmetic_helpers.hpp:120
std::optional< Index_ > ncol() const
Definition arithmetic_helpers.hpp:111
bool non_zero_depends_on_column() const
Definition arithmetic_helpers.hpp:128
Helper for delayed unary isometric vector arithmetic.
Definition arithmetic_helpers.hpp:300
void dense(bool row, Index_ idx, const std::vector< Index_ > &indices, const InputValue_ *input, OutputValue_ *output) const
Definition arithmetic_helpers.hpp:371
bool is_sparse() const
Definition arithmetic_helpers.hpp:386
void dense(bool row, Index_ idx, Index_ start, Index_ length, const InputValue_ *input, OutputValue_ *output) const
Definition arithmetic_helpers.hpp:358
bool non_zero_depends_on_column() const
Definition arithmetic_helpers.hpp:353
std::optional< Index_ > nrow() const
Definition arithmetic_helpers.hpp:324
void sparse(bool row, Index_ idx, Index_ number, const InputValue_ *input_value, const Index_ *index, OutputValue_ *output_value) const
Definition arithmetic_helpers.hpp:390
bool non_zero_depends_on_row() const
Definition arithmetic_helpers.hpp:349
DelayedUnaryIsometricArithmeticVectorHelper(Vector_ vector, bool by_row)
Definition arithmetic_helpers.hpp:309
bool zero_depends_on_column() const
Definition arithmetic_helpers.hpp:345
std::optional< Index_ > ncol() const
Definition arithmetic_helpers.hpp:332
OutputValue_ fill(bool row, Index_ idx) const
Definition arithmetic_helpers.hpp:403
bool zero_depends_on_row() const
Definition arithmetic_helpers.hpp:341
Helper operation interface for DelayedUnaryIsometricOperation.
Definition helper_interface.hpp:27
Flexible representations for matrix data.
Definition Extractor.hpp:15
Interface for tatami::DelayedUnaryIsometricOperation helpers.