diff --git a/docs/Doxyfile b/docs/Doxyfile index 1a9291b0..0bbd88d9 100644 --- a/docs/Doxyfile +++ b/docs/Doxyfile @@ -997,7 +997,8 @@ RECURSIVE = YES EXCLUDE = ../include/tatami/subset/utils.hpp \ ../include/tatami/sparse/secondary_extraction.hpp \ ../include/tatami/sparse/primary_extraction.hpp \ - ../include/tatami/isometric/binary/utils.hpp + ../include/tatami/isometric/binary/utils.hpp \ + ../include/tatami/isometric/depends_utils.hpp # The EXCLUDE_SYMLINKS tag can be used to select whether or not files or # directories that are symbolic links (a Unix file system feature) are excluded diff --git a/include/tatami/isometric/binary/DelayedBinaryIsometricOperation.hpp b/include/tatami/isometric/binary/DelayedBinaryIsometricOperation.hpp index cdbc4ac4..4c7004cb 100644 --- a/include/tatami/isometric/binary/DelayedBinaryIsometricOperation.hpp +++ b/include/tatami/isometric/binary/DelayedBinaryIsometricOperation.hpp @@ -5,13 +5,14 @@ #include "../../utils/new_extractor.hpp" #include "../../utils/copy.hpp" #include "../../dense/SparsifiedWrapper.hpp" +#include "../depends_utils.hpp" #include #include #include /** - * @file DelayedBinaryIsometricOp.hpp + * @file DelayedBinaryIsometricOperation.hpp * * @brief Delayed binary isometric operations. */ @@ -23,31 +24,6 @@ namespace tatami { */ namespace DelayedBinaryIsometricOperation_internal { -template -class MaybeOracleDepends { -public: - MaybeOracleDepends(const MaybeOracle& oracle, bool row) { - if ((row && Operation_::zero_depends_on_row) || (!row && Operation_::zero_depends_on_column)) { - my_oracle = oracle; - } - } - - Index_ get(Index_ i) { - if constexpr(oracle_) { - if constexpr(Operation_::zero_depends_on_row || Operation_::zero_depends_on_column) { - if (my_oracle) { - return my_oracle->get(my_used++); - } - } - } - return i; - } - -private: - MaybeOracle my_oracle; - typename std::conditional::type my_used = 0; -}; - /******************** *** Dense simple *** ********************/ @@ -64,7 +40,7 @@ class DenseSimpleFull : public DenseExtractor { const Options& opt) : my_operation(operation), my_row(row), - my_oracle(oracle, row) + my_oracle(oracle, my_operation, row) { my_left_ext = new_extractor(left, my_row, oracle, opt); my_right_ext = new_extractor(right, my_row, std::move(oracle), opt); @@ -83,7 +59,7 @@ class DenseSimpleFull : public DenseExtractor { private: const Operation_& my_operation; bool my_row; - MaybeOracleDepends my_oracle; + DelayedIsometricOperation_internal::MaybeOracleDepends my_oracle; std::unique_ptr > my_left_ext, my_right_ext; Index_ my_extent; @@ -104,7 +80,7 @@ class DenseSimpleBlock : public DenseExtractor { const Options& opt) : my_operation(operation), my_row(row), - my_oracle(oracle, row), + my_oracle(oracle, my_operation, row), my_block_start(block_start), my_block_length(block_length) { @@ -124,7 +100,7 @@ class DenseSimpleBlock : public DenseExtractor { private: const Operation_& my_operation; bool my_row; - MaybeOracleDepends my_oracle; + DelayedIsometricOperation_internal::MaybeOracleDepends my_oracle; Index_ my_block_start, my_block_length; std::unique_ptr > my_left_ext, my_right_ext; @@ -144,7 +120,7 @@ class DenseSimpleIndex : public DenseExtractor { const Options& opt) : my_operation(operation), my_row(row), - my_oracle(oracle, row), + my_oracle(oracle, my_operation, row), my_indices_ptr(std::move(indices_ptr)) { my_left_ext = new_extractor(left, my_row, oracle, my_indices_ptr, opt); @@ -163,7 +139,7 @@ class DenseSimpleIndex : public DenseExtractor { private: const Operation_& my_operation; bool my_row; - MaybeOracleDepends my_oracle; + DelayedIsometricOperation_internal::MaybeOracleDepends my_oracle; VectorPtr my_indices_ptr; std::unique_ptr > my_left_ext, my_right_ext; @@ -186,7 +162,7 @@ class DenseExpandedFull : public DenseExtractor { Options opt) : my_operation(op), my_row(row), - my_oracle(oracle, row) + my_oracle(oracle, my_operation, row) { opt.sparse_extract_value = true; opt.sparse_extract_index = true; @@ -210,9 +186,10 @@ class DenseExpandedFull : public DenseExtractor { i = my_oracle.get(i); auto num = my_operation.sparse(my_row, i, lres, rres, my_output_vbuffer.data(), my_output_ibuffer.data(), true, true); - // Avoid calling zero() if possible, as this might throw zero-related errors in non-IEEE platforms. + // Avoid calling my_operation.fill() if possible, as this might throw + // zero-related errors in non-IEEE platforms. if (num < my_extent) { - std::fill_n(buffer, my_extent, my_operation.template fill(i)); + std::fill_n(buffer, my_extent, my_operation.template fill(my_row, i)); } for (Index_ j = 0; j < num; ++j) { @@ -224,7 +201,7 @@ class DenseExpandedFull : public DenseExtractor { private: const Operation_& my_operation; bool my_row; - MaybeOracleDepends my_oracle; + DelayedIsometricOperation_internal::MaybeOracleDepends my_oracle; std::unique_ptr > my_left_ext, my_right_ext; Index_ my_extent; @@ -246,7 +223,7 @@ class DenseExpandedBlock : public DenseExtractor { Options opt) : my_operation(operation), my_row(row), - my_oracle(oracle, row), + my_oracle(oracle, my_operation, row), my_block_start(block_start), my_block_length(block_length) { @@ -271,9 +248,10 @@ class DenseExpandedBlock : public DenseExtractor { i = my_oracle.get(i); auto num = my_operation.sparse(my_row, i, lres, rres, my_output_vbuffer.data(), my_output_ibuffer.data(), true, true); - // Avoid calling zero() if possible, as this might throw zero-related errors in non-IEEE platforms. + // Avoid calling my_operation.fill() if possible, as this might throw + // zero-related errors in non-IEEE platforms. if (num < my_block_length) { - std::fill_n(buffer, my_block_length, my_operation.template fill(i)); + std::fill_n(buffer, my_block_length, my_operation.template fill(my_row, i)); } for (Index_ j = 0; j < num; ++j) { @@ -285,7 +263,7 @@ class DenseExpandedBlock : public DenseExtractor { private: const Operation_& my_operation; bool my_row; - MaybeOracleDepends my_oracle; + DelayedIsometricOperation_internal::MaybeOracleDepends my_oracle; Index_ my_block_start, my_block_length; std::unique_ptr > my_left_ext, my_right_ext; @@ -306,7 +284,7 @@ class DenseExpandedIndex : public DenseExtractor { Options opt) : my_operation(operation), my_row(row), - my_oracle(oracle, row), + my_oracle(oracle, my_operation, row), my_extent(indices_ptr->size()) { // Create a remapping vector to map the extracted indices back to the @@ -342,9 +320,10 @@ class DenseExpandedIndex : public DenseExtractor { i = my_oracle.get(i); auto num = my_operation.sparse(my_row, i, lres, rres, my_output_vbuffer.data(), my_output_ibuffer.data(), true, true); - // Avoid calling zero() if possible, as this might throw zero-related errors in non-IEEE platforms. + // Avoid calling my_operation.fill() if possible, as this might throw + // zero-related errors in non-IEEE platforms. if (num < my_extent) { - std::fill_n(buffer, my_extent, my_operation.template fill(i)); + std::fill_n(buffer, my_extent, my_operation.template fill(my_row, i)); } for (Index_ j = 0; j < num; ++j) { @@ -356,7 +335,7 @@ class DenseExpandedIndex : public DenseExtractor { private: const Operation_& my_operation; bool my_row; - MaybeOracleDepends my_oracle; + DelayedIsometricOperation_internal::MaybeOracleDepends my_oracle; Index_ my_extent; std::vector my_remapping; @@ -383,7 +362,7 @@ class Sparse : public SparseExtractor { Options opt) : my_operation(operation), my_row(row), - my_oracle(oracle, row) + my_oracle(oracle, my_operation, row) { initialize(my_row ? left->ncol() : left->nrow(), opt); my_left_ext = new_extractor(left, my_row, oracle, opt); @@ -401,7 +380,7 @@ class Sparse : public SparseExtractor { Options opt) : my_operation(operation), my_row(row), - my_oracle(oracle, row) + my_oracle(oracle, my_operation, row) { initialize(block_length, opt); my_left_ext = new_extractor(left, my_row, oracle, block_start, block_length, opt); @@ -418,7 +397,7 @@ class Sparse : public SparseExtractor { Options opt) : my_operation(operation), my_row(row), - my_oracle(oracle, row) + my_oracle(oracle, my_operation, row) { initialize(indices_ptr->size(), opt); // do this before the move. my_left_ext = new_extractor(left, my_row, oracle, indices_ptr, opt); @@ -466,7 +445,7 @@ class Sparse : public SparseExtractor { private: const Operation_& my_operation; bool my_row; - MaybeOracleDepends my_oracle; + DelayedIsometricOperation_internal::MaybeOracleDepends my_oracle; std::unique_ptr > my_left_ext, my_right_ext; std::vector my_left_vbuffer, my_right_vbuffer; @@ -513,7 +492,7 @@ class DelayedBinaryIsometricOperation : public Matrix { my_prefer_rows_proportion = (my_left->prefer_rows_proportion() + my_right->prefer_rows_proportion()) / 2; - if constexpr(is_advanced) { + if constexpr(!Operation_::is_basic) { if (my_operation.is_sparse()) { my_is_sparse = my_left->is_sparse() && my_right->is_sparse(); @@ -531,8 +510,6 @@ class DelayedBinaryIsometricOperation : public Matrix { double my_is_sparse_proportion = 0; bool my_is_sparse = false; - static constexpr bool is_advanced = (!Operation_::zero_depends_on_row || !Operation_::zero_depends_on_column); - public: Index_ nrow() const { return my_left->nrow(); @@ -650,11 +627,9 @@ class DelayedBinaryIsometricOperation : public Matrix { template std::unique_ptr > dense_internal(bool row, Args_&& ... args) const { - if constexpr(is_advanced) { + if constexpr(!Operation_::is_basic) { if (my_left->is_sparse() && my_right->is_sparse()) { - // If we don't depend on the rows, then we don't need row indices when 'row = false'. - // Similarly, if we don't depend on columns, then we don't column row indices when 'row = true'. - if ((!Operation_::zero_depends_on_row && !row) || (!Operation_::zero_depends_on_column && row)) { + if (DelayedIsometricOperation_internal::can_dense_expand(my_operation, row)) { return dense_expanded_internal(row, std::forward(args)...); } } @@ -682,7 +657,7 @@ class DelayedBinaryIsometricOperation : public Matrix { private: template std::unique_ptr > sparse_internal(bool row, MaybeOracle oracle, const Options& opt) const { - if constexpr(is_advanced) { + if constexpr(!Operation_::is_basic) { if (my_is_sparse) { return std::make_unique >( my_left.get(), @@ -704,7 +679,7 @@ class DelayedBinaryIsometricOperation : public Matrix { template std::unique_ptr > sparse_internal(bool row, MaybeOracle oracle, Index_ block_start, Index_ block_length, const Options& opt) const { - if constexpr(is_advanced) { + if constexpr(!Operation_::is_basic) { if (my_is_sparse) { return std::make_unique >( my_left.get(), @@ -729,7 +704,7 @@ class DelayedBinaryIsometricOperation : public Matrix { template std::unique_ptr > sparse_internal(bool row, MaybeOracle oracle, VectorPtr indices_ptr, const Options& opt) const { - if constexpr(is_advanced) { + if constexpr(!Operation_::is_basic) { if (my_is_sparse) { return std::make_unique >( my_left.get(), diff --git a/include/tatami/isometric/binary/arithmetic_helpers.hpp b/include/tatami/isometric/binary/arithmetic_helpers.hpp index 692cd52c..b521e624 100644 --- a/include/tatami/isometric/binary/arithmetic_helpers.hpp +++ b/include/tatami/isometric/binary/arithmetic_helpers.hpp @@ -31,9 +31,7 @@ class DelayedBinaryIsometricArithmetic { op_ == ArithmeticOperation::SUBTRACT || op_ == ArithmeticOperation::MULTIPLY); - static constexpr bool zero_depends_on_row = false; - - static constexpr bool zero_depends_on_column = false; + static constexpr bool is_basic = false; /** * @endcond */ @@ -72,7 +70,7 @@ class DelayedBinaryIsometricArithmetic { } template - Value_ fill(Index_) const { + Value_ fill(bool, Index_) const { if constexpr(known_sparse) { return 0; } else if constexpr(op_ == ArithmeticOperation::POWER) { diff --git a/include/tatami/isometric/binary/boolean_helpers.hpp b/include/tatami/isometric/binary/boolean_helpers.hpp index 35f44528..b81ce796 100644 --- a/include/tatami/isometric/binary/boolean_helpers.hpp +++ b/include/tatami/isometric/binary/boolean_helpers.hpp @@ -28,9 +28,7 @@ struct DelayedBinaryIsometricBoolean { // It's sparse if f(0, 0) == 0. static constexpr bool known_sparse = (op_ != BooleanOperation::EQUAL); - static constexpr bool zero_depends_on_row = false; - - static constexpr bool zero_depends_on_column = false; + static constexpr bool is_basic = false; /** * @endcond */ @@ -69,7 +67,7 @@ struct DelayedBinaryIsometricBoolean { } template - Value_ fill(Index_) const { + Value_ fill(bool, Index_) const { if constexpr(known_sparse) { return 0; } else { diff --git a/include/tatami/isometric/binary/compare_helpers.hpp b/include/tatami/isometric/binary/compare_helpers.hpp index 4e1b40d3..743e1363 100644 --- a/include/tatami/isometric/binary/compare_helpers.hpp +++ b/include/tatami/isometric/binary/compare_helpers.hpp @@ -30,9 +30,7 @@ struct DelayedBinaryIsometricCompare { op_ != CompareOperation::GREATER_THAN_OR_EQUAL && op_ != CompareOperation::LESS_THAN_OR_EQUAL); - static constexpr bool zero_depends_on_row = false; - - static constexpr bool zero_depends_on_column = false; + static constexpr bool is_basic = false; /** * @endcond */ @@ -76,7 +74,7 @@ struct DelayedBinaryIsometricCompare { } template - Value_ fill(Index_) const { + Value_ fill(bool, Index_) const { if constexpr(known_sparse) { return 0; } else { diff --git a/include/tatami/isometric/binary/mock_helpers.hpp b/include/tatami/isometric/binary/mock_helpers.hpp index f6d438ab..bee43c22 100644 --- a/include/tatami/isometric/binary/mock_helpers.hpp +++ b/include/tatami/isometric/binary/mock_helpers.hpp @@ -33,6 +33,7 @@ class DelayedBinaryIsometricMockBasic { * If true, `left_buffer` and `right_buffer` hold the contents of the `i`-th row from both matrices; * otherwise, they hold the contents of the `i`-th column. * @param i Index of the extracted row (if `row = true`) or column (otherwise). + * Unlike `DelayedBinaryIsometricMockAdvanced::dense()`, this is always guaranteed to be the actual index and not a placeholder. * @param start Start of the contiguous block of columns (if `row = true`) or rows (otherwise) extracted from `i`. * @param length Length of the contiguous block. * @param[in,out] left_buffer Contents of the row/column extracted from the left matrix. @@ -68,6 +69,7 @@ class DelayedBinaryIsometricMockBasic { * If true, `left_buffer` and `right_buffer` hold the contents of the `i`-th row from both matrices; * otherwise, they hold the contents of the `i`-th column. * @param i Index of the extracted row (if `row = true`) or column (otherwise). + * Unlike `DelayedBinaryIsometricMockAdvanced::dense()`, this is always guaranteed to be the actual index and not a placeholder. * @param indices Sorted and unique indices of columns (if `row = true`) or rows (otherwise) extracted from `i`. * @param[in,out] left_buffer Contents of the row/column extracted from the left matrix. * This has `length` addressable elements, and the result of the operation should be stored here. @@ -89,16 +91,10 @@ class DelayedBinaryIsometricMockBasic { } /** - * Conversion of zeros to non-zero values is dependent on rows. - * This should be `true`, otherwise an advanced operation is expected (see `DelayedBinaryIsometricMockAdvanced`). + * Whether this is a basic operation. + * This should be true, otherwise an advanced operation is expected (see `DelayedBinaryIsometricMockAdvanced`). */ - static constexpr bool zero_depends_on_row = true; - - /** - * Conversion of zeros to non-zero values is dependent on columns. - * This should be `true`, otherwise an advanced operation is expected (see `DelayedBinaryIsometricMockAdvanced`). - */ - static constexpr bool zero_depends_on_column = true; + static constexpr bool is_basic = true; }; /** @@ -119,38 +115,76 @@ class DelayedBinaryIsometricMockAdvanced { * @tparam Value_ Type of matrix value. * @tparam Index_ Type of index value. * - * @param i The index of the row containing the zero, if `zero_depends_on_row = true`; - * the index of the column containing the zero, if `zero_depends_on_column = true`; - * or ignored, if neither are true. + * @param row Whether `i` refers to the row or column index. + * @param i The index of the row (if `row = true`) or column (otherwise) containing the zeros. + * This argument should be ignored if the operation does not depend on the row/column (i.e., when all of `zero_depends_on_row()` and friends return false), + * in which case an arbitrary placeholder may be supplied. * - * @return The result of the operation being applied on zeros from both the left and right matrices. - * This should be constant for all elements in the row/column/matrix, depending on the interpretation of `i`. + * @return The result of `OP(lz, rz)` where `OP` is the operation, + * `lz` is a structural zero from the `i`-th row/column of the left matrix, + * and `rz` is a structural zero from the `i`-th row/column of the left matrix, * * This method will be called with an explicit `Value_` template parameter. * Implementations of this method should either ensure that `Index_` is deducible or use a fixed integer type in the method signature. */ template - Value_ fill([[maybe_unused]] Index_ i) const { + Value_ fill([[maybe_unused]] bool row, [[maybe_unused]] Index_ i) const { return 0; } /** - * Conversion of zeros to non-zero values is not dependent on rows. - * Implementations of the advanced operation interface may set this to `true` provided that `zero_depends_on_column = false`; - * at least one of these must be false, otherwise a basic operation interface is expected (see `DelayedBinaryIsometricMockBasic`). + * Whether this is a basic operation. + * This should be false, otherwise a basic operation interface is expected (see `DelayedBinaryIsometricMockBasic`). + */ + static constexpr bool is_basic = false; + + /** + * @return Whether applying the operation to a pair of structural zeros (one from each matrix) + * yields a value that depends on the identity of the row containing those zeros. + * + * This method is only called when `is_sparse()` returns false. + * It is not necessary to explicitly return `false` here for sparsity-preserving operations, + * as `DelayedBinaryIsometricOperation` will automatically recognize such operations as being row-independent. * - * This value is only used when `is_sparse()` returns false. + * This method may be omitted from the class definition, in which case it is assumed to always return false. */ - static constexpr bool zero_depends_on_row = false; + bool zero_depends_on_row() const { + return false; + } /** - * Conversion of zeros to non-zero values is not dependent on columns. - * Implementations of the advanced operation interface may set this to `true` provided that `zero_depends_on_row = false`; - * at least one of these must be false, otherwise a basic operation interface is expected (see `DelayedBinaryIsometricMockBasic`). + * @return Whether applying the operation to a pair of structural zeros (one from each matrix) + * yields a value that depends on the identity of the column containing those zeros. * - * This value is only used when `is_sparse()` returns false. + * This method is only called when `is_sparse()` returns false. + * It is not necessary to explicitly return `false` here for sparsity-preserving operations, + * as `DelayedBinaryIsometricOperation` will automatically recognize such operations as being row-independent. + * + * This method may be omitted from the class definition, in which case it is assumed to always return false. */ - static constexpr bool zero_depends_on_column = false; + bool zero_depends_on_column() const { + return false; + } + + /** + * @return Whether the result of the operation depends on the identity of the row containing the operands, + * where at least one of the operands is non-zero. + * + * This method may be omitted from the class definition, in which case it is assumed to always return false. + */ + bool non_zero_depends_on_row() const { + return false; + } + + /** + * @return Whether the result of the operation depends on the identity of the column containing the operands, + * where at least one of the operands is non-zero. + * + * This method may be omitted from the class definition, in which case it is assumed to always return false. + */ + bool non_zero_depends_on_column() const { + return false; + } /** * This method should apply the operation to corresponding values of `left_buffer` and `right_buffer`. @@ -164,6 +198,8 @@ class DelayedBinaryIsometricMockAdvanced { * If true, `left_buffer` and `right_buffer` hold the contents of the `i`-th row from both matrices; * otherwise, they hold the contents of the `i`-th column. * @param i Index of the extracted row (if `row = true`) or column (otherwise). + * This argument should be ignored if the operation does not depend on the row/column (i.e., when all of `zero_depends_on_row()` and friends return false), + * in which case an arbitrary placeholder may be supplied. * @param start Start of the contiguous block of columns (if `row = true`) or rows (otherwise) extracted from `i`. * @param length Length of the contiguous block. * @param[in,out] left_buffer Contents of the row/column extracted from the left matrix. @@ -199,6 +235,8 @@ class DelayedBinaryIsometricMockAdvanced { * If true, `left_buffer` and `right_buffer` hold the contents of the `i`-th row from both matrices; * otherwise, they hold the contents of the `i`-th column. * @param i Index of the extracted row (if `row = true`) or column (otherwise). + * This argument should be ignored if the operation does not depend on the row/column (i.e., when all of `zero_depends_on_row()` and friends return false), + * in which case an arbitrary placeholder may be supplied. * @param indices Sorted and unique indices of columns (if `row = true`) or rows (otherwise) extracted from `i`. * @param[in,out] left_buffer Contents of the row/column extracted from the left matrix. * This has `length` addressable elements, and the result of the operation should be stored here. @@ -234,6 +272,8 @@ class DelayedBinaryIsometricMockAdvanced { * If true, `left_buffer` and `right_buffer` hold the contents of the `i`-th row from both matrices; * otherwise, they hold the contents of the `i`-th column. * @param i Index of the extracted row (if `row = true`) or column (otherwise). + * This argument should be ignored if the operation does not depend on the row/column (i.e., when all of `zero_depends_on_row()` and friends return false), + * in which case an arbitrary placeholder may be supplied. * @param left Contents of row/column `i` extracted from the left matrix. * @param right Contents of row/column `i` extracted from the right matrix. * @param[out] output_value Pointer to an array for storing output values of the operation. @@ -279,8 +319,7 @@ class DelayedBinaryIsometricMockAdvanced { } /** - * @return Does this operation preserve sparsity? - * This may return false. + * @return Whether this operation preserves sparsity. */ bool is_sparse() const { return true; diff --git a/include/tatami/isometric/depends_utils.hpp b/include/tatami/isometric/depends_utils.hpp new file mode 100644 index 00000000..2e721346 --- /dev/null +++ b/include/tatami/isometric/depends_utils.hpp @@ -0,0 +1,184 @@ +#ifndef TATAMI_DELAYED_ISOMETRIC_OPERATION_UTILS_HPP +#define TATAMI_DELAYED_ISOMETRIC_OPERATION_UTILS_HPP + +#include +#include "../utils/new_extractor.hpp" + +namespace tatami { + +namespace DelayedIsometricOperation_internal { + +// Some SFINAE shenanigans for checking if zero handling depends on the row ID of the zero value. +template +struct has_zero_depends_on_row { + static constexpr bool value = false; +}; + +template +struct has_zero_depends_on_row().zero_depends_on_row(), 0)> { + static constexpr bool value = true; +}; + +template +bool zero_depends_on_row(const Operation_& op) { + if constexpr(!has_zero_depends_on_row::value) { + return false; + } else { + return op.zero_depends_on_row(); + } +} + +// ... Checking if zero handling depends on the column ID of the zero value. +template +struct has_zero_depends_on_column { + static constexpr bool value = false; +}; + +template +struct has_zero_depends_on_column().zero_depends_on_column(), 0)> { + static constexpr bool value = true; +}; + +template +bool zero_depends_on_column(const Operation_& op) { + if constexpr(!has_zero_depends_on_column::value) { + return false; + } else { + return op.zero_depends_on_column(); + } +} + +// ... Checking if non-zero processing depends on the row ID. +template +struct has_non_zero_depends_on_row { + static constexpr bool value = false; +}; + +template +struct has_non_zero_depends_on_row().non_zero_depends_on_row(), 0)> { + static constexpr bool value = true; +}; + +template +bool non_zero_depends_on_row(const Operation_& op) { + if constexpr(!has_non_zero_depends_on_row::value) { + return false; + } else { + return op.non_zero_depends_on_row(); + } +} + +// ... Checking if non-zero processing depends on the column ID. +template +struct has_non_zero_depends_on_column { + static constexpr bool value = false; +}; + +template +struct has_non_zero_depends_on_column().non_zero_depends_on_column(), 0)> { + static constexpr bool value = true; +}; + +template +bool non_zero_depends_on_column(const Operation_& op) { + if constexpr(!has_non_zero_depends_on_column::value) { + return false; + } else { + return op.non_zero_depends_on_column(); + } +} + +// Check if the oracle requires the row or column ID. +template +class MaybeOracleDepends { +public: + MaybeOracleDepends(const MaybeOracle& oracle, const Operation_& op, bool row) { + if constexpr(oracle_) { + if constexpr(Operation_::is_basic) { + my_oracle = oracle; + } else if ([&]() { + // Only storing if the oracle if we need the row/column index + // on the target dimension to apply the operation. + if (row) { + if (non_zero_depends_on_row(op)) { + return true; + } else if (!op.is_sparse() && zero_depends_on_row(op)) { // if it's sparse, zeros remain zero so they can't depend on the row. + return true; + } + } else { + if (non_zero_depends_on_column(op)) { + return true; + } else if (!op.is_sparse() && zero_depends_on_column(op)) { // ditto for the columns. + return true; + } + } + return false; + }()) + { + my_oracle = oracle; + } + } + } + + Index_ get(Index_ i) { + if constexpr(oracle_) { + if constexpr(Operation_::is_basic) { + return my_oracle->get(my_used++); + } else if constexpr( + // If none of these are present, we can assume that they're all + // false, allowing us to elide the my_oracle=NULL check. + has_zero_depends_on_row::value || + has_zero_depends_on_column::value || + has_non_zero_depends_on_row::value || + has_non_zero_depends_on_column::value) + { + if (my_oracle) { + return my_oracle->get(my_used++); + } + } + } + return i; + } + +private: + MaybeOracle my_oracle; + typename std::conditional::type my_used = 0; +}; + +template +bool can_dense_expand(const Operation_& op, bool row) { + if (!op.is_sparse()) { // as I said before: if it's sparse, zeros remain zero so they can't depend on either the row or column. + if (row) { + // If zero processing doesn't depend on the column identity, then + // during row extraction, we can use a constant value to fill in the + // zero values for the entire row. + return !zero_depends_on_column(op); + } else { + // Similarly, if zero processing doesn't depend on + // columns, then during row extraction, we can fill in the + // zero values across columns with a constant value. + return !zero_depends_on_row(op); + } + } + + return true; +} + +template +bool needs_sparse_indices(const Operation_& op, bool row) { + if (row) { + // If we depend on columns, then we need column indices + // when the rows are the target dimension. + return non_zero_depends_on_column(op); + } else { + // Similarly, if we depend on the rows, then we need row + // indices when the columns are the target dimension. + return non_zero_depends_on_row(op); + } +} + +} + +} + +#endif diff --git a/include/tatami/isometric/unary/DelayedUnaryIsometricOperation.hpp b/include/tatami/isometric/unary/DelayedUnaryIsometricOperation.hpp index 7273ae44..9770a15f 100644 --- a/include/tatami/isometric/unary/DelayedUnaryIsometricOperation.hpp +++ b/include/tatami/isometric/unary/DelayedUnaryIsometricOperation.hpp @@ -4,6 +4,7 @@ #include "../../base/Matrix.hpp" #include "../../utils/copy.hpp" #include "../../utils/new_extractor.hpp" +#include "../depends_utils.hpp" #include #include @@ -22,48 +23,6 @@ namespace tatami { */ namespace DelayedUnaryIsometricOperation_internal { -template -class MaybeOracleDepends { -public: - MaybeOracleDepends(const MaybeOracle& oracle, bool row) { - if constexpr(oracle_) { - if constexpr(Operation_::zero_depends_on_row && Operation_::zero_depends_on_column) { - // Put this in a constexpr in case Operation_ only satisfies the basic interface, - // in which case it won't have the non_zero_depends_* members. - my_oracle = oracle; - } else if ( - (row && (Operation_::zero_depends_on_row || Operation_::non_zero_depends_on_row)) || - (!row && (Operation_::zero_depends_on_column || Operation_::non_zero_depends_on_column)) - ) { - my_oracle = oracle; - } - } - } - - Index_ get(Index_ i) { - if constexpr(oracle_) { - if constexpr(Operation_::zero_depends_on_row && Operation_::zero_depends_on_column) { - // Put this in a constexpr in case Operation_ only satisfies the basic interface, - // in which case it won't have the non_zero_depends_* members. - return my_oracle->get(my_used++); - } else if constexpr(Operation_::zero_depends_on_row || - Operation_::non_zero_depends_on_row || - Operation_::zero_depends_on_column || - Operation_::non_zero_depends_on_column) - { - if (my_oracle) { - return my_oracle->get(my_used++); - } - } - } - return i; - } - -private: - MaybeOracle my_oracle; - typename std::conditional::type my_used = 0; -}; - /** * DenseBasic is used if: * @@ -85,7 +44,7 @@ class DenseBasicFull : public DenseExtractor { const Options& opt) : my_operation(operation), my_row(row), - my_oracle(oracle, row), + my_oracle(oracle, my_operation, row), my_extent(row ? matrix->ncol() : matrix->nrow()), my_ext(new_extractor(matrix, row, std::move(oracle), opt)) {} @@ -93,7 +52,7 @@ class DenseBasicFull : public DenseExtractor { private: const Operation_& my_operation; bool my_row; - MaybeOracleDepends my_oracle; + DelayedIsometricOperation_internal::MaybeOracleDepends my_oracle; Index_ my_extent; std::unique_ptr > my_ext; @@ -119,7 +78,7 @@ class DenseBasicBlock : public DenseExtractor { const Options& opt) : my_operation(operation), my_row(row), - my_oracle(oracle, row), + my_oracle(oracle, my_operation, row), my_block_start(block_start), my_block_length(block_length), my_ext(new_extractor(matrix, row, std::move(oracle), block_start, block_length, opt)) @@ -128,7 +87,7 @@ class DenseBasicBlock : public DenseExtractor { private: const Operation_& my_operation; bool my_row; - MaybeOracleDepends my_oracle; + DelayedIsometricOperation_internal::MaybeOracleDepends my_oracle; Index_ my_block_start, my_block_length; std::unique_ptr > my_ext; @@ -153,7 +112,7 @@ class DenseBasicIndex : public DenseExtractor { const Options& opt) : my_operation(operation), my_row(row), - my_oracle(oracle, row), + my_oracle(oracle, my_operation, row), my_indices_ptr(indices_ptr), my_ext(new_extractor(matrix, row, std::move(oracle), std::move(indices_ptr), opt)) {} @@ -161,7 +120,7 @@ class DenseBasicIndex : public DenseExtractor { private: const Operation_& my_operation; bool my_row; - MaybeOracleDepends my_oracle; + DelayedIsometricOperation_internal::MaybeOracleDepends my_oracle; VectorPtr my_indices_ptr; std::unique_ptr > my_ext; @@ -197,7 +156,7 @@ class DenseExpandedFull : public DenseExtractor { Options opt) : my_operation(operation), my_row(row), - my_oracle(oracle, row), + my_oracle(oracle, my_operation, row), my_extent(row ? matrix->ncol() : matrix->nrow()), my_vbuffer(my_extent), my_ibuffer(my_extent) @@ -210,7 +169,7 @@ class DenseExpandedFull : public DenseExtractor { private: const Operation_& my_operation; bool my_row; - MaybeOracleDepends my_oracle; + DelayedIsometricOperation_internal::MaybeOracleDepends my_oracle; Index_ my_extent; std::vector my_vbuffer; @@ -228,7 +187,7 @@ class DenseExpandedFull : public DenseExtractor { // avoid calling zero() if possible, as this might throw zero-related errors in non-IEEE platforms. if (range.number < my_extent) { - std::fill_n(buffer, my_extent, my_operation.template fill(i)); + std::fill_n(buffer, my_extent, my_operation.template fill(my_row, i)); } for (Index_ i = 0; i < range.number; ++i) { @@ -252,7 +211,7 @@ class DenseExpandedBlock : public DenseExtractor { Options opt) : my_operation(operation), my_row(row), - my_oracle(oracle, row), + my_oracle(oracle, my_operation, row), my_block_start(block_start), my_block_length(block_length), my_vbuffer(block_length), @@ -266,7 +225,7 @@ class DenseExpandedBlock : public DenseExtractor { private: const Operation_& my_operation; bool my_row; - MaybeOracleDepends my_oracle; + DelayedIsometricOperation_internal::MaybeOracleDepends my_oracle; Index_ my_block_start, my_block_length; std::vector my_vbuffer; @@ -284,7 +243,7 @@ class DenseExpandedBlock : public DenseExtractor { // avoid calling zero() if possible, as this might throw zero-related errors in non-IEEE platforms. if (range.number < my_block_length) { - std::fill_n(buffer, my_block_length, my_operation.template fill(i)); + std::fill_n(buffer, my_block_length, my_operation.template fill(my_row, i)); } for (Index_ i = 0; i < range.number; ++i) { @@ -307,7 +266,7 @@ class DenseExpandedIndex : public DenseExtractor { Options opt) : my_operation(operation), my_row(row), - my_oracle(oracle, row) + my_oracle(oracle, my_operation, row) { opt.sparse_extract_value = true; opt.sparse_extract_index = true; @@ -334,7 +293,7 @@ class DenseExpandedIndex : public DenseExtractor { private: const Operation_& my_operation; bool my_row; - MaybeOracleDepends my_oracle; + DelayedIsometricOperation_internal::MaybeOracleDepends my_oracle; Index_ my_extent; std::vector my_vbuffer; @@ -355,7 +314,7 @@ class DenseExpandedIndex : public DenseExtractor { // avoid calling zero() if possible, as this might throw zero-related errors in non-IEEE platforms. if (range.number < my_extent) { - std::fill_n(buffer, my_extent, my_operation.template fill(i)); + std::fill_n(buffer, my_extent, my_operation.template fill(my_row, i)); } for (Index_ i = 0; i < range.number; ++i) { @@ -384,7 +343,7 @@ class SparseSimple : public SparseExtractor { const Options& opt) : my_operation(operation), my_row(row), - my_oracle(oracle, row), + my_oracle(oracle, my_operation, row), my_ext(new_extractor(matrix, row, std::move(oracle), opt)) {} @@ -398,7 +357,7 @@ class SparseSimple : public SparseExtractor { const Options& opt) : my_operation(operation), my_row(row), - my_oracle(oracle, row), + my_oracle(oracle, my_operation, row), my_ext(new_extractor(matrix, row, std::move(oracle), block_start, block_length, opt)) {} @@ -411,14 +370,14 @@ class SparseSimple : public SparseExtractor { const Options& opt) : my_operation(operation), my_row(row), - my_oracle(oracle, row), + my_oracle(oracle, my_operation, row), my_ext(new_extractor(matrix, row, std::move(oracle), std::move(indices_ptr), opt)) {} private: const Operation_& my_operation; bool my_row; - MaybeOracleDepends my_oracle; + DelayedIsometricOperation_internal::MaybeOracleDepends my_oracle; std::unique_ptr > my_ext; public: @@ -451,7 +410,7 @@ class SparseNeedsIndices : public SparseExtractor { Options opt) : my_operation(operation), my_row(row), - my_oracle(oracle, row) + my_oracle(oracle, my_operation, row) { initialize(opt, row ? matrix->ncol() : matrix->nrow()); my_ext = new_extractor(matrix, row, std::move(oracle), opt); @@ -467,7 +426,7 @@ class SparseNeedsIndices : public SparseExtractor { Options opt) : my_operation(operation), my_row(row), - my_oracle(oracle, row) + my_oracle(oracle, my_operation, row) { initialize(opt, block_length); my_ext = new_extractor(matrix, row, std::move(oracle), block_start, block_length, opt); @@ -482,7 +441,7 @@ class SparseNeedsIndices : public SparseExtractor { Options opt) : my_operation(operation), my_row(row), - my_oracle(oracle, row) + my_oracle(oracle, my_operation, row) { initialize(opt, indices_ptr->size()); my_ext = new_extractor(matrix, row, std::move(oracle), std::move(indices_ptr), opt); @@ -511,7 +470,7 @@ class SparseNeedsIndices : public SparseExtractor { const Operation_& my_operation; bool my_row; bool my_report_value, my_report_index; - MaybeOracleDepends my_oracle; + DelayedIsometricOperation_internal::MaybeOracleDepends my_oracle; std::vector my_ibuffer; std::unique_ptr > my_ext; @@ -566,8 +525,6 @@ class DelayedUnaryIsometricOperation : public Matrix { std::shared_ptr > my_matrix; Operation_ my_operation; - static constexpr bool is_advanced = (!Operation_::zero_depends_on_row || !Operation_::zero_depends_on_column); - public: Index_ nrow() const { return my_matrix->nrow(); @@ -578,7 +535,7 @@ class DelayedUnaryIsometricOperation : public Matrix { } bool is_sparse() const { - if constexpr(is_advanced) { + if constexpr(!Operation_::is_basic) { if (my_operation.is_sparse()) { return my_matrix->is_sparse(); } @@ -587,7 +544,7 @@ class DelayedUnaryIsometricOperation : public Matrix { } double is_sparse_proportion() const { - if constexpr(is_advanced) { + if constexpr(!Operation_::is_basic) { if (my_operation.is_sparse()) { return my_matrix->is_sparse_proportion(); } @@ -647,11 +604,9 @@ class DelayedUnaryIsometricOperation : public Matrix { template std::unique_ptr > dense_internal(bool row, Args_&& ... args) const { - if constexpr(is_advanced) { + if constexpr(!Operation_::is_basic) { if (my_matrix->is_sparse()) { - // If we don't depend on the rows, then we don't need row indices when 'row = false'. - // Similarly, if we don't depend on columns, then we don't column row indices when 'row = true'. - if ((!Operation_::zero_depends_on_row && !row) || (!Operation_::zero_depends_on_column && row)) { + if (DelayedIsometricOperation_internal::can_dense_expand(my_operation, row)) { return dense_expanded_internal(row, std::forward(args)...); } } @@ -707,24 +662,23 @@ class DelayedUnaryIsometricOperation : public Matrix { template std::unique_ptr > sparse_internal(bool row, MaybeOracle oracle, Args_&& ... args) const { - if constexpr(is_advanced) { + if constexpr(!Operation_::is_basic) { if (my_operation.is_sparse() && my_matrix->is_sparse()) { - if ((!Operation_::non_zero_depends_on_row && !row) || (!Operation_::non_zero_depends_on_column && row)) { - // If we don't depend on the rows, then we don't need row indices when 'row = false'. - // Similarly, if we don't depend on columns, then we don't column row indices when 'row = true'. - return std::make_unique >( + if (DelayedIsometricOperation_internal::needs_sparse_indices(my_operation, row)) { + return std::make_unique >( my_matrix.get(), - my_operation, + my_operation, row, - std::move(oracle), + std::move(oracle), std::forward(args)... ); + } else { - return std::make_unique >( + return std::make_unique >( my_matrix.get(), - my_operation, + my_operation, row, - std::move(oracle), + std::move(oracle), std::forward(args)... ); } diff --git a/include/tatami/isometric/unary/arithmetic_helpers.hpp b/include/tatami/isometric/unary/arithmetic_helpers.hpp index b70e873b..b3f46073 100644 --- a/include/tatami/isometric/unary/arithmetic_helpers.hpp +++ b/include/tatami/isometric/unary/arithmetic_helpers.hpp @@ -90,13 +90,7 @@ class DelayedUnaryIsometricArithmeticScalar { /** * @cond */ - static constexpr bool zero_depends_on_row = false; - - static constexpr bool zero_depends_on_column = false; - - static constexpr bool non_zero_depends_on_row = false; - - static constexpr bool non_zero_depends_on_column = false; + static constexpr bool is_basic = false; bool is_sparse() const { return my_sparse; @@ -126,7 +120,7 @@ class DelayedUnaryIsometricArithmeticScalar { } template - Value_ fill(Index_) const { + Value_ fill(bool, Index_) const { return delayed_arithmetic_zero(my_scalar); } /** @@ -142,20 +136,20 @@ class DelayedUnaryIsometricArithmeticScalar { * @tparam op_ The arithmetic operation. * @tparam right_ Whether the vector's values should be on the right hand side of the arithmetic operation. * Ignored for some `op_`. - * @tparam margin_ Matrix dimension along which the operation is to occur. - * If 0, each element of the vector is assumed to correspond to a row, and that value is subtracted from all entries in the same row of the matrix. - * If 1, each element of the vector is assumed to correspond to a column instead. * @tparam Value_ Type of the data value. * @tparam Vector_ Type of the vector. */ -template > +template > class DelayedUnaryIsometricArithmeticVector { public: /** * @param vector Vector of values to use in the operation. - * This should be of length equal to the number of rows if `margin_ = 0`, otherwise it should be of length equal to the number of columns. + * This should be of length equal to the number of rows if `by_row = true`, otherwise it should be of length equal to the number of columns. + * @param by_row Whether `vector` corresponds to the rows. + * If true, each element of the vector is assumed to correspond to a row, and that element is used as an operand with all entries in the same row of the matrix. + * If false, each element of the vector is assumed to correspond to a column instead. */ - DelayedUnaryIsometricArithmeticVector(Vector_ vector) : my_vector(std::move(vector)) { + DelayedUnaryIsometricArithmeticVector(Vector_ vector, bool by_row) : my_vector(std::move(vector)), my_by_row(by_row) { for (auto x : my_vector) { if (!delayed_arithmetic_actual_sparse(x)) { my_sparse = false; @@ -166,19 +160,30 @@ class DelayedUnaryIsometricArithmeticVector { private: const Vector_ my_vector; + bool my_by_row; bool my_sparse = true; public: /** * @cond */ - static constexpr bool zero_depends_on_row = (margin_ == 0); + static constexpr bool is_basic = false; + + bool zero_depends_on_row() const { + return my_by_row; + } - static constexpr bool zero_depends_on_column = (margin_ == 1); + bool zero_depends_on_column() const { + return !my_by_row; + } - static constexpr bool non_zero_depends_on_row = (margin_ == 0); + bool non_zero_depends_on_row() const { + return my_by_row; + } - static constexpr bool non_zero_depends_on_column = (margin_ == 1); + bool non_zero_depends_on_column() const { + return !my_by_row; + } bool is_sparse() const { return my_sparse; @@ -193,7 +198,7 @@ class DelayedUnaryIsometricArithmeticVector { */ template void dense(bool row, Index_ idx, Index_ start, Index_ length, Value_* buffer) const { - if (row == (margin_ == 0)) { + if (row == my_by_row) { delayed_arithmetic_run_simple(my_vector[idx], length, buffer); } else { for (Index_ i = 0; i < length; ++i) { @@ -204,7 +209,7 @@ class DelayedUnaryIsometricArithmeticVector { template void dense(bool row, Index_ idx, const std::vector& indices, Value_* buffer) const { - if (row == (margin_ == 0)) { + if (row == my_by_row) { delayed_arithmetic_run_simple(my_vector[idx], indices.size(), buffer); } else { for (Index_ i = 0, length = indices.size(); i < length; ++i) { @@ -215,7 +220,7 @@ class DelayedUnaryIsometricArithmeticVector { template void sparse(bool row, Index_ idx, Index_ number, Value_* buffer, const Index_* indices) const { - if (row == (margin_ == 0)) { + if (row == my_by_row) { delayed_arithmetic_run_simple(my_vector[idx], number, buffer); } else { for (Index_ i = 0; i < number; ++i) { @@ -225,8 +230,14 @@ class DelayedUnaryIsometricArithmeticVector { } template - Value_ fill(Index_ idx) const { - return delayed_arithmetic_zero(my_vector[idx]); + Value_ fill(bool row, Index_ idx) const { + if (row == my_by_row) { + return delayed_arithmetic_zero(my_vector[idx]); + } else { + // We should only get to this point if it's sparse, otherwise no + // single fill value would work across the length of my_vector. + return 0; + } } /** * @endcond @@ -323,106 +334,106 @@ DelayedUnaryIsometricArithmeticScalar > -DelayedUnaryIsometricArithmeticVector make_DelayedUnaryIsometricAddVector(Vector_ vector) { - return DelayedUnaryIsometricArithmeticVector(std::move(vector)); +template > +DelayedUnaryIsometricArithmeticVector make_DelayedUnaryIsometricAddVector(Vector_ vector, bool by_row) { + return DelayedUnaryIsometricArithmeticVector(std::move(vector), by_row); } /** * @tparam right_ Whether the scalar should be on the right hand side of the subtraction. - * @tparam margin_ Matrix dimension along which the subtraction is to occur, see `DelayedUnaryIsometricArithmeticVector`. * @tparam Value_ Type of the data value. * @tparam Vector_ Type of the vector. * * @param vector Vector to subtract from (or be subtracted by) the rows/columns. + * @param by_row Whether each element of `vector` corresponds to a row, see `DelayedUnaryIsometricArithmeticVector`. * @return A helper class for delayed vector subtraction, * to be used as the `operation` in a `DelayedUnaryIsometricOperation`. */ -template > -DelayedUnaryIsometricArithmeticVector make_DelayedUnaryIsometricSubtractVector(Vector_ vector) { - return DelayedUnaryIsometricArithmeticVector(std::move(vector)); +template > +DelayedUnaryIsometricArithmeticVector make_DelayedUnaryIsometricSubtractVector(Vector_ vector, bool by_row) { + return DelayedUnaryIsometricArithmeticVector(std::move(vector), by_row); } /** - * @tparam margin_ Matrix dimension along which the multiplication is to occur, see `DelayedUnaryIsometricArithmeticVector`. * @tparam Value_ Type of the data value. * @tparam Vector_ Type of the vector. * * @param vector Vector to multiply the rows/columns. + * @param by_row Whether each element of `vector` corresponds to a row, see `DelayedUnaryIsometricArithmeticVector`. * @return A helper class for delayed vector multiplication, * to be used as the `operation` in a `DelayedUnaryIsometricOperation`. */ -template > -DelayedUnaryIsometricArithmeticVector make_DelayedUnaryIsometricMultiplyVector(Vector_ vector) { - return DelayedUnaryIsometricArithmeticVector(std::move(vector)); +template > +DelayedUnaryIsometricArithmeticVector make_DelayedUnaryIsometricMultiplyVector(Vector_ vector, bool by_row) { + return DelayedUnaryIsometricArithmeticVector(std::move(vector), by_row); } /** * @tparam right_ Whether the scalar should be on the right hand side of the division. - * @tparam margin_ Matrix dimension along which the division is to occur, see `DelayedUnaryIsometricArithmeticVector`. * @tparam Value_ Type of the data value. * @tparam Vector_ Type of the vector. * * @param vector Vector to divide (or be divided by) the rows/columns. + * @param by_row Whether each element of `vector` corresponds to a row, see `DelayedUnaryIsometricArithmeticVector`. * @return A helper class for delayed vector division, * to be used as the `operation` in a `DelayedUnaryIsometricOperation`. */ -template > -DelayedUnaryIsometricArithmeticVector make_DelayedUnaryIsometricDivideVector(Vector_ vector) { - return DelayedUnaryIsometricArithmeticVector(std::move(vector)); +template > +DelayedUnaryIsometricArithmeticVector make_DelayedUnaryIsometricDivideVector(Vector_ vector, bool by_row) { + return DelayedUnaryIsometricArithmeticVector(std::move(vector), by_row); } /** * @tparam right_ Whether the scalar should be on the right hand side of the power transformation. - * @tparam margin_ Matrix dimension along which the power transformation is to occur, see `DelayedUnaryIsometricArithmeticVector`. * @tparam Value_ Type of the data value. * @tparam Vector_ Type of the vector. * * @param vector Vector to use in the power transformation of the rows/columns. + * @param by_row Whether each element of `vector` corresponds to a row, see `DelayedUnaryIsometricArithmeticVector`. * @return A helper class for delayed vector power transformation, * to be used as the `operation` in a `DelayedUnaryIsometricOperation`. */ -template > -DelayedUnaryIsometricArithmeticVector make_DelayedUnaryIsometricPowerVector(Vector_ vector) { - return DelayedUnaryIsometricArithmeticVector(std::move(vector)); +template > +DelayedUnaryIsometricArithmeticVector make_DelayedUnaryIsometricPowerVector(Vector_ vector, bool by_row) { + return DelayedUnaryIsometricArithmeticVector(std::move(vector), by_row); } /** * @tparam right_ Whether the scalar should be on the right hand side of the modulus. - * @tparam margin_ Matrix dimension along which the modulus is to occur, see `DelayedUnaryIsometricArithmeticVector`. * @tparam Value_ Type of the data value. * @tparam Vector_ Type of the vector. * * @param vector Vector to use in the modulus of the rows/columns. + * @param by_row Whether each element of `vector` corresponds to a row, see `DelayedUnaryIsometricArithmeticVector`. * @return A helper class for delayed vector modulus, * to be used as the `operation` in a `DelayedUnaryIsometricOperation`. */ -template > -DelayedUnaryIsometricArithmeticVector make_DelayedUnaryIsometricModuloVector(Vector_ vector) { - return DelayedUnaryIsometricArithmeticVector(std::move(vector)); +template > +DelayedUnaryIsometricArithmeticVector make_DelayedUnaryIsometricModuloVector(Vector_ vector, bool by_row) { + return DelayedUnaryIsometricArithmeticVector(std::move(vector), by_row); } /** * @tparam right_ Whether the scalar should be on the right hand side of the integer division. - * @tparam margin_ Matrix dimension along which the integer division is to occur, see `DelayedUnaryIsometricArithmeticVector`. * @tparam Value_ Type of the data value. * @tparam Vector_ Type of the vector. * * @param vector Vector to integer divide (or be integer divided by) the rows/columns. + * @param by_row Whether each element of `vector` corresponds to a row, see `DelayedUnaryIsometricArithmeticVector`. * @return A helper class for delayed vector division, * to be used as the `operation` in a `DelayedUnaryIsometricOperation`. */ -template > -DelayedUnaryIsometricArithmeticVector make_DelayedUnaryIsometricIntegerDivideVector(Vector_ vector) { - return DelayedUnaryIsometricArithmeticVector(std::move(vector)); +template > +DelayedUnaryIsometricArithmeticVector make_DelayedUnaryIsometricIntegerDivideVector(Vector_ vector, bool by_row) { + return DelayedUnaryIsometricArithmeticVector(std::move(vector), by_row); } } diff --git a/include/tatami/isometric/unary/boolean_helpers.hpp b/include/tatami/isometric/unary/boolean_helpers.hpp index 16efaec7..8cc71869 100644 --- a/include/tatami/isometric/unary/boolean_helpers.hpp +++ b/include/tatami/isometric/unary/boolean_helpers.hpp @@ -58,13 +58,7 @@ class DelayedUnaryIsometricBooleanScalar { /** * @cond */ - static constexpr bool zero_depends_on_row = false; - - static constexpr bool zero_depends_on_column = false; - - static constexpr bool non_zero_depends_on_row = false; - - static constexpr bool non_zero_depends_on_column = false; + static constexpr bool is_basic = false; bool is_sparse() const { return my_sparse; @@ -93,7 +87,7 @@ class DelayedUnaryIsometricBooleanScalar { } template - Value_ fill(Index_) const { + Value_ fill(bool, Index_) const { Value_ output = 0; delayed_boolean_run(output, my_scalar); return output; @@ -116,13 +110,7 @@ class DelayedUnaryIsometricBooleanNotOperation { /** * @cond */ - static constexpr bool zero_depends_on_row = false; - - static constexpr bool zero_depends_on_column = false; - - static constexpr bool non_zero_depends_on_row = false; - - static constexpr bool non_zero_depends_on_column = false; + static constexpr bool is_basic = false; bool is_sparse() const { return false; @@ -159,7 +147,7 @@ class DelayedUnaryIsometricBooleanNotOperation { } template - Value_ fill(Index_) const { + Value_ fill(bool, Index_) const { return 1; } /** @@ -173,20 +161,20 @@ class DelayedUnaryIsometricBooleanNotOperation { * This should be used as the `Operation_` in the `DelayedUnaryIsometricOperation` class. * * @tparam op_ The boolean operation. - * @tparam margin_ Matrix dimension along which the operation is to occur. - * If 0, each element of the vector is assumed to correspond to a row, and that value is subtracted from all entries in the same row of the matrix. - * If 1, each element of the vector is assumed to correspond to a column instead. * @tparam Value_ Type of the data value. * @tparam Vector_ Type of the vector. */ -template > +template > class DelayedUnaryIsometricBooleanVector { public: /** * @param vector Vector of values to use in the operation. - * This should be of length equal to the number of rows if `margin_ = 0`, otherwise it should be of length equal to the number of columns. + * This should be of length equal to the number of rows if `by_row = true`, otherwise it should be of length equal to the number of columns. + * @param by_row Whether `vector` corresponds to the rows. + * If true, each element of the vector is assumed to correspond to a row, and that element is used as an operand with all entries in the same row of the matrix. + * If false, each element of the vector is assumed to correspond to a column instead. */ - DelayedUnaryIsometricBooleanVector(Vector_ vector) : my_vector(std::move(vector)) { + DelayedUnaryIsometricBooleanVector(Vector_ vector, bool by_row) : my_vector(std::move(vector)), my_by_row(by_row) { for (auto x : my_vector) { if (!delayed_boolean_actual_sparse(x)) { my_sparse = false; @@ -197,19 +185,30 @@ class DelayedUnaryIsometricBooleanVector { private: const Vector_ my_vector; + bool my_by_row; bool my_sparse = true; public: /** * @cond */ - static constexpr bool zero_depends_on_row = (margin_ == 0); + static constexpr bool is_basic = false; - static constexpr bool zero_depends_on_column = (margin_ == 1); + bool zero_depends_on_row() const { + return my_by_row; + } - static constexpr bool non_zero_depends_on_row = (margin_ == 0); + bool zero_depends_on_column() const { + return !my_by_row; + } - static constexpr bool non_zero_depends_on_column = (margin_ == 1); + bool non_zero_depends_on_row() const { + return my_by_row; + } + + bool non_zero_depends_on_column() const { + return !my_by_row; + } bool is_sparse() const { return my_sparse; @@ -224,7 +223,7 @@ class DelayedUnaryIsometricBooleanVector { */ template void dense(bool row, Index_ idx, Index_ start, Index_ length, Value_* buffer) const { - if (row == (margin_ == 0)) { + if (row == my_by_row) { delayed_boolean_run_simple(my_vector[idx], length, buffer); } else { for (Index_ i = 0; i < length; ++i) { @@ -235,7 +234,7 @@ class DelayedUnaryIsometricBooleanVector { template void dense(bool row, Index_ idx, const std::vector& indices, Value_* buffer) const { - if (row == (margin_ == 0)) { + if (row == my_by_row) { delayed_boolean_run_simple(my_vector[idx], indices.size(), buffer); } else { for (Index_ i = 0, length = indices.size(); i < length; ++i) { @@ -246,7 +245,7 @@ class DelayedUnaryIsometricBooleanVector { template void sparse(bool row, Index_ idx, Index_ number, Value_* buffer, const Index_* indices) const { - if (row == (margin_ == 0)) { + if (row == my_by_row) { delayed_boolean_run_simple(my_vector[idx], number, buffer); } else { for (Index_ i = 0; i < number; ++i) { @@ -256,10 +255,16 @@ class DelayedUnaryIsometricBooleanVector { } template - Value_ fill(Index_ idx) const { - Value_ output = 0; - delayed_boolean_run(output, my_vector[idx]); - return output; + Value_ fill(bool row, Index_ idx) const { + if (row == my_by_row) { + Value_ output = 0; + delayed_boolean_run(output, my_vector[idx]); + return output; + } else { + // We should only get to this point if it's sparse, otherwise no + // single fill value would work across the length of my_vector. + return 0; + } } /** * @endcond @@ -323,53 +328,53 @@ DelayedUnaryIsometricBooleanScalar make_DelayedUnaryIso /** * @tparam Value_ Type of the data value. * @tparam Vector_ Type of the vector. - * @tparam margin_ Matrix dimension along which the comparison is to occur, see `DelayedUnaryIsometricBooleanVector`. * @param vector Vector of values to be used in the operation. + * @param by_row Whether each element of `vector` corresponds to a row, see `DelayedUnaryIsometricBooleanVector`. * @return A helper class for a delayed AND operation with a vector, * to be used as the `operation` in a `DelayedUnaryIsometricOperation`. */ -template > -DelayedUnaryIsometricBooleanVector make_DelayedUnaryIsometricBooleanAndVector(Vector_ vector) { - return DelayedUnaryIsometricBooleanVector(std::move(vector)); +template > +DelayedUnaryIsometricBooleanVector make_DelayedUnaryIsometricBooleanAndVector(Vector_ vector, bool by_row) { + return DelayedUnaryIsometricBooleanVector(std::move(vector), by_row); } /** * @tparam Value_ Type of the data value. * @tparam Vector_ Type of the vector. - * @tparam margin_ Matrix dimension along which the comparison is to occur, see `DelayedUnaryIsometricBooleanVector`. * @param vector Vector of values to be used in the operation. + * @param by_row Whether each element of `vector` corresponds to a row, see `DelayedUnaryIsometricBooleanVector`. * @return A helper class for a delayed OR operation with a vector, * to be used as the `operation` in a `DelayedUnaryIsometricOperation`. */ -template > -DelayedUnaryIsometricBooleanVector make_DelayedUnaryIsometricBooleanOrVector(Vector_ vector) { - return DelayedUnaryIsometricBooleanVector(std::move(vector)); +template > +DelayedUnaryIsometricBooleanVector make_DelayedUnaryIsometricBooleanOrVector(Vector_ vector, bool by_row) { + return DelayedUnaryIsometricBooleanVector(std::move(vector), by_row); } /** * @tparam Value_ Type of the data value. * @tparam Vector_ Type of the vector. - * @tparam margin_ Matrix dimension along which the comparison is to occur, see `DelayedUnaryIsometricBooleanVector`. * @param vector Vector of values to be used in the operation. + * @param by_row Whether each element of `vector` corresponds to a row, see `DelayedUnaryIsometricBooleanVector`. * @return A helper class for a delayed XOR operation with a vector, * to be used as the `operation` in a `DelayedUnaryIsometricOperation`. */ -template > -DelayedUnaryIsometricBooleanVector make_DelayedUnaryIsometricBooleanXorVector(Vector_ vector) { - return DelayedUnaryIsometricBooleanVector(std::move(vector)); +template > +DelayedUnaryIsometricBooleanVector make_DelayedUnaryIsometricBooleanXorVector(Vector_ vector, bool by_row) { + return DelayedUnaryIsometricBooleanVector(std::move(vector), by_row); } /** * @tparam Value_ Type of the data value. * @tparam Vector_ Type of the vector. - * @tparam margin_ Matrix dimension along which the comparison is to occur, see `DelayedUnaryIsometricBooleanVector`. * @param vector Vector of values to be used in the operation. + * @param by_row Whether each element of `vector` corresponds to a row, see `DelayedUnaryIsometricBooleanVector`. * @return A helper class for a delayed boolean equality operation with a vector, * to be used as the `operation` in a `DelayedUnaryIsometricOperation`. */ -template > -DelayedUnaryIsometricBooleanVector make_DelayedUnaryIsometricBooleanEqualVector(Vector_ vector) { - return DelayedUnaryIsometricBooleanVector(std::move(vector)); +template > +DelayedUnaryIsometricBooleanVector make_DelayedUnaryIsometricBooleanEqualVector(Vector_ vector, bool by_row) { + return DelayedUnaryIsometricBooleanVector(std::move(vector), by_row); } } diff --git a/include/tatami/isometric/unary/compare_helpers.hpp b/include/tatami/isometric/unary/compare_helpers.hpp index 709853d3..bba3a220 100644 --- a/include/tatami/isometric/unary/compare_helpers.hpp +++ b/include/tatami/isometric/unary/compare_helpers.hpp @@ -59,13 +59,7 @@ class DelayedUnaryIsometricCompareScalar { /** * @cond */ - static constexpr bool zero_depends_on_row = false; - - static constexpr bool zero_depends_on_column = false; - - static constexpr bool non_zero_depends_on_row = false; - - static constexpr bool non_zero_depends_on_column = false; + static constexpr bool is_basic = false; bool is_sparse() const { return my_sparse; @@ -94,7 +88,7 @@ class DelayedUnaryIsometricCompareScalar { } template - Value_ fill(Index_) const { + Value_ fill(bool, Index_) const { Value_ output = 0; delayed_compare_run(output, my_scalar); return output; @@ -110,20 +104,20 @@ class DelayedUnaryIsometricCompareScalar { * This should be used as the `Operation_` in the `DelayedUnaryIsometricOperation` class. * * @tparam op_ The comparison operation. - * @tparam margin_ Matrix dimension along which the operation is to occur. - * If 0, each element of the vector is assumed to correspond to a row, and that value is subtracted from all entries in the same row of the matrix. - * If 1, each element of the vector is assumed to correspond to a column instead. * @tparam Value_ Type of the data value. * @tparam Vector_ Type of the vector. */ -template > +template > class DelayedUnaryIsometricCompareVector { public: /** * @param vector Vector of values to use in the operation. - * This should be of length equal to the number of rows if `MARGIN = 0`, otherwise it should be of length equal to the number of columns. + * This should be of length equal to the number of rows if `by_row = true`, otherwise it should be of length equal to the number of columns. + * @param by_row Whether `vector` corresponds to the rows. + * If true, each element of the vector is assumed to correspond to a row, and that element is used as an operand with all entries in the same row of the matrix. + * If false, each element of the vector is assumed to correspond to a column instead. */ - DelayedUnaryIsometricCompareVector(Vector_ vector) : my_vector(std::move(vector)) { + DelayedUnaryIsometricCompareVector(Vector_ vector, bool by_row) : my_vector(std::move(vector)), my_by_row(by_row) { for (auto x : my_vector) { if (!delayed_compare_actual_sparse(x)) { my_sparse = false; @@ -134,19 +128,30 @@ class DelayedUnaryIsometricCompareVector { private: const Vector_ my_vector; + bool my_by_row; bool my_sparse = true; public: /** * @cond */ - static constexpr bool zero_depends_on_row = (margin_ == 0); + static constexpr bool is_basic = false; - static constexpr bool zero_depends_on_column = (margin_ == 1); + bool zero_depends_on_row() const { + return my_by_row; + } + + bool zero_depends_on_column() const { + return !my_by_row; + } - static constexpr bool non_zero_depends_on_row = (margin_ == 0); + bool non_zero_depends_on_row() const { + return my_by_row; + } - static constexpr bool non_zero_depends_on_column = (margin_ == 1); + bool non_zero_depends_on_column() const { + return !my_by_row; + } bool is_sparse() const { return my_sparse; @@ -161,7 +166,7 @@ class DelayedUnaryIsometricCompareVector { */ template void dense(bool row, Index_ idx, Index_ start, Index_ length, Value_* buffer) const { - if (row == (margin_ == 0)) { + if (row == my_by_row) { delayed_compare_run_simple(my_vector[idx], length, buffer); } else { for (Index_ i = 0; i < length; ++i) { @@ -172,7 +177,7 @@ class DelayedUnaryIsometricCompareVector { template void dense(bool row, Index_ idx, const std::vector& indices, Value_* buffer) const { - if (row == (margin_ == 0)) { + if (row == my_by_row) { delayed_compare_run_simple(my_vector[idx], indices.size(), buffer); } else { for (Index_ i = 0, length = indices.size(); i < length; ++i) { @@ -183,7 +188,7 @@ class DelayedUnaryIsometricCompareVector { template void sparse(bool row, Index_ idx, Index_ number, Value_* buffer, const Index_* indices) const { - if (row == (margin_ == 0)) { + if (row == my_by_row) { delayed_compare_run_simple(my_vector[idx], number, buffer); } else { for (Index_ i = 0; i < number; ++i) { @@ -193,10 +198,16 @@ class DelayedUnaryIsometricCompareVector { } template - Value_ fill(Index_ idx) const { - Value_ output = 0; - delayed_compare_run(output, my_vector[idx]); - return output; + Value_ fill(bool row, Index_ idx) const { + if (row == my_by_row) { + Value_ output = 0; + delayed_compare_run(output, my_vector[idx]); + return output; + } else { + // We should only get to this point if it's sparse, otherwise no + // single fill value would work across the length of my_vector. + return 0; + } } /** * @endcond @@ -276,81 +287,81 @@ DelayedUnaryIsometricCompareScalar } /** - * @tparam margin_ Matrix dimension along which the comparison is to occur, see `DelayedUnaryIsometricCompareVector`. * @tparam Value_ Type of the data value. * @tparam Vector_ Type of the vector. * @param vector Vector of values to be compared. + * @param by_row Whether each element of `vector` corresponds to a row, see `DelayedUnaryIsometricCompareVector`. * @return A helper class for a delayed equality comparison to a vector, * to be used as the `operation` in a `DelayedUnaryIsometricOperation`. */ -template > -DelayedUnaryIsometricCompareVector make_DelayedUnaryIsometricEqualVector(Vector_ vector) { - return DelayedUnaryIsometricCompareVector(std::move(vector)); +template > +DelayedUnaryIsometricCompareVector make_DelayedUnaryIsometricEqualVector(Vector_ vector, bool by_row) { + return DelayedUnaryIsometricCompareVector(std::move(vector), by_row); } /** - * @tparam margin_ Matrix dimension along which the comparison is to occur, see `DelayedUnaryIsometricCompareVector`. * @tparam Value_ Type of the data value. * @tparam Vector_ Type of the vector. * @param vector Vector of values to be compared. + * @param by_row Whether each element of `vector` corresponds to a row, see `DelayedUnaryIsometricCompareVector`. * @return A helper class for a delayed greater-than comparison to a vector, * to be used as the `operation` in a `DelayedUnaryIsometricOperation`. */ -template > -DelayedUnaryIsometricCompareVector make_DelayedUnaryIsometricGreaterThanVector(Vector_ vector) { - return DelayedUnaryIsometricCompareVector(std::move(vector)); +template > +DelayedUnaryIsometricCompareVector make_DelayedUnaryIsometricGreaterThanVector(Vector_ vector, bool by_row) { + return DelayedUnaryIsometricCompareVector(std::move(vector), by_row); } /** - * @tparam margin_ Matrix dimension along which the comparison is to occur, see `DelayedUnaryIsometricCompareVector`. * @tparam Value_ Type of the data value. * @tparam Vector_ Type of the vector. * @param vector Vector of values to be compared. + * @param by_row Whether each element of `vector` corresponds to a row, see `DelayedUnaryIsometricCompareVector`. * @return A helper class for a delayed less-than comparison to a vector, * to be used as the `operation` in a `DelayedUnaryIsometricOperation`. */ -template > -DelayedUnaryIsometricCompareVector make_DelayedUnaryIsometricLessThanVector(Vector_ vector) { - return DelayedUnaryIsometricCompareVector(std::move(vector)); +template > +DelayedUnaryIsometricCompareVector make_DelayedUnaryIsometricLessThanVector(Vector_ vector, bool by_row) { + return DelayedUnaryIsometricCompareVector(std::move(vector), by_row); } /** - * @tparam margin_ Matrix dimension along which the comparison is to occur, see `DelayedUnaryIsometricCompareVector`. * @tparam Value_ Type of the data value. * @tparam Vector_ Type of the vector. * @param vector Vector of values to be compared. + * @param by_row Whether each element of `vector` corresponds to a row, see `DelayedUnaryIsometricCompareVector`. * @return A helper class for a delayed greater-than-or-equal comparison to a vector, * to be used as the `operation` in a `DelayedUnaryIsometricOperation`. */ -template > -DelayedUnaryIsometricCompareVector make_DelayedUnaryIsometricGreaterThanOrEqualVector(Vector_ vector) { - return DelayedUnaryIsometricCompareVector(std::move(vector)); +template > +DelayedUnaryIsometricCompareVector make_DelayedUnaryIsometricGreaterThanOrEqualVector(Vector_ vector, bool by_row) { + return DelayedUnaryIsometricCompareVector(std::move(vector), by_row); } /** - * @tparam margin_ Matrix dimension along which the comparison is to occur, see `DelayedUnaryIsometricCompareVector`. * @tparam Value_ Type of the data value. * @tparam Vector_ Type of the vector. * @param vector Vector of values to be compared. + * @param by_row Whether each element of `vector` corresponds to a row, see `DelayedUnaryIsometricCompareVector`. * @return A helper class for a delayed less-than-or-equal comparison to a vector, * to be used as the `operation` in a `DelayedUnaryIsometricOperation`. */ -template > -DelayedUnaryIsometricCompareVector make_DelayedUnaryIsometricLessThanOrEqualVector(Vector_ vector) { - return DelayedUnaryIsometricCompareVector(std::move(vector)); +template > +DelayedUnaryIsometricCompareVector make_DelayedUnaryIsometricLessThanOrEqualVector(Vector_ vector, bool by_row) { + return DelayedUnaryIsometricCompareVector(std::move(vector), by_row); } /** - * @tparam margin_ Matrix dimension along which the comparison is to occur, see `DelayedUnaryIsometricCompareVector`. * @tparam Value_ Type of the data value. * @tparam Vector_ Type of the vector. * @param vector Vector of values to be compared. + * @param by_row Whether each element of `vector` corresponds to a row, see `DelayedUnaryIsometricCompareVector`. * @return A helper class for a delayed non-equality comparison to a vector, * to be used as the `operation` in a `DelayedUnaryIsometricOperation`. */ -template > -DelayedUnaryIsometricCompareVector make_DelayedUnaryIsometricNotEqualVector(Vector_ vector) { - return DelayedUnaryIsometricCompareVector(std::move(vector)); +template > +DelayedUnaryIsometricCompareVector make_DelayedUnaryIsometricNotEqualVector(Vector_ vector, bool by_row) { + return DelayedUnaryIsometricCompareVector(std::move(vector), by_row); } } diff --git a/include/tatami/isometric/unary/math_helpers.hpp b/include/tatami/isometric/unary/math_helpers.hpp index 69f96b06..0db3ed8e 100644 --- a/include/tatami/isometric/unary/math_helpers.hpp +++ b/include/tatami/isometric/unary/math_helpers.hpp @@ -24,13 +24,7 @@ class DelayedUnaryIsometricAbs { /** * @cond */ - static constexpr bool zero_depends_on_row = false; - - static constexpr bool zero_depends_on_column = false; - - static constexpr bool non_zero_depends_on_row = false; - - static constexpr bool non_zero_depends_on_column = false; + static constexpr bool is_basic = false; bool is_sparse() const { return true; @@ -68,7 +62,7 @@ class DelayedUnaryIsometricAbs { } template - Value_ fill(Index_) const { + Value_ fill(bool, Index_) const { return 0; } /** @@ -89,13 +83,7 @@ class DelayedUnaryIsometricSign { /** * @cond */ - static constexpr bool zero_depends_on_row = false; - - static constexpr bool zero_depends_on_column = false; - - static constexpr bool non_zero_depends_on_row = false; - - static constexpr bool non_zero_depends_on_column = false; + static constexpr bool is_basic = false; bool is_sparse() const { return true; @@ -134,7 +122,7 @@ class DelayedUnaryIsometricSign { } template - Value_ fill(Index_) const { + Value_ fill(bool, Index_) const { return 0; } /** @@ -167,13 +155,7 @@ class DelayedUnaryIsometricLog { /** * @cond */ - static const bool zero_depends_on_row = false; - - static const bool zero_depends_on_column = false; - - static constexpr bool non_zero_depends_on_row = false; - - static constexpr bool non_zero_depends_on_column = false; + static constexpr bool is_basic = false; bool is_sparse() const { return false; @@ -212,7 +194,7 @@ class DelayedUnaryIsometricLog { } template - Value_ fill(Index_) const { + Value_ fill(bool, Index_) const { // Use the implementation-defined value. return std::log(static_cast(0)); } @@ -234,13 +216,7 @@ class DelayedUnaryIsometricSqrt { /** * @cond */ - static constexpr bool zero_depends_on_row = false; - - static constexpr bool zero_depends_on_column = false; - - static constexpr bool non_zero_depends_on_row = false; - - static constexpr bool non_zero_depends_on_column = false; + static constexpr bool is_basic = false; bool is_sparse() const { return true; @@ -277,7 +253,7 @@ class DelayedUnaryIsometricSqrt { } template - Value_ fill(Index_) const { + Value_ fill(bool, Index_) const { return 0; } /** @@ -298,13 +274,7 @@ class DelayedUnaryIsometricCeiling { /** * @cond */ - static constexpr bool zero_depends_on_row = false; - - static constexpr bool zero_depends_on_column = false; - - static constexpr bool non_zero_depends_on_row = false; - - static constexpr bool non_zero_depends_on_column = false; + static constexpr bool is_basic = false; bool is_sparse() const { return true; @@ -341,7 +311,7 @@ class DelayedUnaryIsometricCeiling { } template - Value_ fill(Index_) const { + Value_ fill(bool, Index_) const { return 0; } /** @@ -362,13 +332,7 @@ class DelayedUnaryIsometricFloor { /** * @cond */ - static constexpr bool zero_depends_on_row = false; - - static constexpr bool zero_depends_on_column = false; - - static constexpr bool non_zero_depends_on_row = false; - - static constexpr bool non_zero_depends_on_column = false; + static constexpr bool is_basic = false; bool is_sparse() const { return true; @@ -405,7 +369,7 @@ class DelayedUnaryIsometricFloor { } template - Value_ fill(Index_) const { + Value_ fill(bool, Index_) const { return 0; } /** @@ -426,13 +390,7 @@ class DelayedUnaryIsometricTrunc { /** * @cond */ - static constexpr bool zero_depends_on_row = false; - - static constexpr bool zero_depends_on_column = false; - - static constexpr bool non_zero_depends_on_row = false; - - static constexpr bool non_zero_depends_on_column = false; + static constexpr bool is_basic = false; bool is_sparse() const { return true; @@ -469,7 +427,7 @@ class DelayedUnaryIsometricTrunc { } template - Value_ fill(Index_) const { + Value_ fill(bool, Index_) const { return 0; } /** @@ -502,13 +460,7 @@ class DelayedUnaryIsometricLog1p { /** * @cond */ - static constexpr bool zero_depends_on_row = false; - - static constexpr bool zero_depends_on_column = false; - - static constexpr bool non_zero_depends_on_row = false; - - static constexpr bool non_zero_depends_on_column = false; + static constexpr bool is_basic = false; bool is_sparse() const { return true; @@ -547,7 +499,7 @@ class DelayedUnaryIsometricLog1p { } template - Value_ fill(Index_) const { + Value_ fill(bool, Index_) const { return 0; } /** @@ -568,13 +520,7 @@ class DelayedUnaryIsometricRound { /** * @cond */ - static constexpr bool zero_depends_on_row = false; - - static constexpr bool zero_depends_on_column = false; - - static constexpr bool non_zero_depends_on_row = false; - - static constexpr bool non_zero_depends_on_column = false; + static constexpr bool is_basic = false; bool is_sparse() const { return true; @@ -611,7 +557,7 @@ class DelayedUnaryIsometricRound { } template - Value_ fill(Index_) const { + Value_ fill(bool, Index_) const { return 0; } /** @@ -632,13 +578,7 @@ class DelayedUnaryIsometricExp { /** * @cond */ - static constexpr bool zero_depends_on_row = false; - - static constexpr bool zero_depends_on_column = false; - - static constexpr bool non_zero_depends_on_row = false; - - static constexpr bool non_zero_depends_on_column = false; + static constexpr bool is_basic = false; bool is_sparse() const { return false; @@ -675,7 +615,7 @@ class DelayedUnaryIsometricExp { } template - Value_ fill(Index_) const { + Value_ fill(bool, Index_) const { return 1.0; } /** @@ -696,13 +636,7 @@ class DelayedUnaryIsometricExpm1 { /** * @cond */ - static constexpr bool zero_depends_on_row = false; - - static constexpr bool zero_depends_on_column = false; - - static constexpr bool non_zero_depends_on_row = false; - - static constexpr bool non_zero_depends_on_column = false; + static constexpr bool is_basic = false; bool is_sparse() const { return true; @@ -739,7 +673,7 @@ class DelayedUnaryIsometricExpm1 { } template - Value_ fill(Index_) const { + Value_ fill(bool, Index_) const { return 0; } /** @@ -760,13 +694,7 @@ class DelayedUnaryIsometricAcos { /** * @cond */ - static constexpr bool zero_depends_on_row = false; - - static constexpr bool zero_depends_on_column = false; - - static constexpr bool non_zero_depends_on_row = false; - - static constexpr bool non_zero_depends_on_column = false; + static constexpr bool is_basic = false; bool is_sparse() const { return false; @@ -803,7 +731,7 @@ class DelayedUnaryIsometricAcos { } template - Value_ fill(Index_) const { + Value_ fill(bool, Index_) const { // Use the implementation-defined special value. return std::acos(0); } @@ -825,13 +753,7 @@ class DelayedUnaryIsometricAcosh { /** * @cond */ - static constexpr bool zero_depends_on_row = false; - - static constexpr bool zero_depends_on_column = false; - - static constexpr bool non_zero_depends_on_row = false; - - static constexpr bool non_zero_depends_on_column = false; + static constexpr bool is_basic = false; bool is_sparse() const { return false; @@ -868,7 +790,7 @@ class DelayedUnaryIsometricAcosh { } template - Value_ fill(Index_) const { + Value_ fill(bool, Index_) const { // Use the implementation-defined special value. return std::acosh(static_cast(0)); } @@ -890,13 +812,7 @@ class DelayedUnaryIsometricAsin { /** * @cond */ - static constexpr bool zero_depends_on_row = false; - - static constexpr bool zero_depends_on_column = false; - - static constexpr bool non_zero_depends_on_row = false; - - static constexpr bool non_zero_depends_on_column = false; + static constexpr bool is_basic = false; bool is_sparse() const { return true; @@ -933,7 +849,7 @@ class DelayedUnaryIsometricAsin { } template - Value_ fill(Index_) const { + Value_ fill(bool, Index_) const { return 0; } /** @@ -954,13 +870,7 @@ class DelayedUnaryIsometricAsinh { /** * @cond */ - static constexpr bool zero_depends_on_row = false; - - static constexpr bool zero_depends_on_column = false; - - static constexpr bool non_zero_depends_on_row = false; - - static constexpr bool non_zero_depends_on_column = false; + static constexpr bool is_basic = false; bool is_sparse() const { return true; @@ -997,7 +907,7 @@ class DelayedUnaryIsometricAsinh { } template - Value_ fill(Index_) const { + Value_ fill(bool, Index_) const { return 0; } /** @@ -1018,13 +928,7 @@ class DelayedUnaryIsometricAtan { /** * @cond */ - static constexpr bool zero_depends_on_row = false; - - static constexpr bool zero_depends_on_column = false; - - static constexpr bool non_zero_depends_on_row = false; - - static constexpr bool non_zero_depends_on_column = false; + static constexpr bool is_basic = false; bool is_sparse() const { return true; @@ -1061,7 +965,7 @@ class DelayedUnaryIsometricAtan { } template - Value_ fill(Index_) const { + Value_ fill(bool, Index_) const { return 0; } /** @@ -1082,13 +986,7 @@ class DelayedUnaryIsometricAtanh { /** * @cond */ - static constexpr bool zero_depends_on_row = false; - - static constexpr bool zero_depends_on_column = false; - - static constexpr bool non_zero_depends_on_row = false; - - static constexpr bool non_zero_depends_on_column = false; + static constexpr bool is_basic = false; bool is_sparse() const { return true; @@ -1125,7 +1023,7 @@ class DelayedUnaryIsometricAtanh { } template - Value_ fill(Index_) const { + Value_ fill(bool, Index_) const { return 0; } /** @@ -1146,13 +1044,7 @@ class DelayedUnaryIsometricCos { /** * @cond */ - static constexpr bool zero_depends_on_row = false; - - static constexpr bool zero_depends_on_column = false; - - static constexpr bool non_zero_depends_on_row = false; - - static constexpr bool non_zero_depends_on_column = false; + static constexpr bool is_basic = false; bool is_sparse() const { return false; @@ -1189,7 +1081,7 @@ class DelayedUnaryIsometricCos { } template - Value_ fill(Index_) const { + Value_ fill(bool, Index_) const { return 1.0; } /** @@ -1210,13 +1102,7 @@ class DelayedUnaryIsometricCosh { /** * @cond */ - static constexpr bool zero_depends_on_row = false; - - static constexpr bool zero_depends_on_column = false; - - static constexpr bool non_zero_depends_on_row = false; - - static constexpr bool non_zero_depends_on_column = false; + static constexpr bool is_basic = false; bool is_sparse() const { return false; @@ -1253,7 +1139,7 @@ class DelayedUnaryIsometricCosh { } template - Value_ fill(Index_) const { + Value_ fill(bool, Index_) const { return 1.0; } /** @@ -1274,13 +1160,7 @@ class DelayedUnaryIsometricSin { /** * @cond */ - static constexpr bool zero_depends_on_row = false; - - static constexpr bool zero_depends_on_column = false; - - static constexpr bool non_zero_depends_on_row = false; - - static constexpr bool non_zero_depends_on_column = false; + static constexpr bool is_basic = false; bool is_sparse() const { return true; @@ -1317,7 +1197,7 @@ class DelayedUnaryIsometricSin { } template - Value_ fill(Index_) const { + Value_ fill(bool, Index_) const { return 0; } /** @@ -1338,13 +1218,7 @@ class DelayedUnaryIsometricSinh { /** * @cond */ - static constexpr bool zero_depends_on_row = false; - - static constexpr bool zero_depends_on_column = false; - - static constexpr bool non_zero_depends_on_row = false; - - static constexpr bool non_zero_depends_on_column = false; + static constexpr bool is_basic = false; bool is_sparse() const { return true; @@ -1381,7 +1255,7 @@ class DelayedUnaryIsometricSinh { } template - Value_ fill(Index_) const { + Value_ fill(bool, Index_) const { return 0; } /** @@ -1402,13 +1276,7 @@ class DelayedUnaryIsometricTan { /** * @cond */ - static constexpr bool zero_depends_on_row = false; - - static constexpr bool zero_depends_on_column = false; - - static constexpr bool non_zero_depends_on_row = false; - - static constexpr bool non_zero_depends_on_column = false; + static constexpr bool is_basic = false; bool is_sparse() const { return true; @@ -1445,7 +1313,7 @@ class DelayedUnaryIsometricTan { } template - Value_ fill(Index_) const { + Value_ fill(bool, Index_) const { return 0; } /** @@ -1466,13 +1334,7 @@ class DelayedUnaryIsometricTanh { /** * @cond */ - static constexpr bool zero_depends_on_row = false; - - static constexpr bool zero_depends_on_column = false; - - static constexpr bool non_zero_depends_on_row = false; - - static constexpr bool non_zero_depends_on_column = false; + static constexpr bool is_basic = false; bool is_sparse() const { return true; @@ -1509,7 +1371,7 @@ class DelayedUnaryIsometricTanh { } template - Value_ fill(Index_) const { + Value_ fill(bool, Index_) const { return 0; } /** @@ -1530,13 +1392,7 @@ class DelayedUnaryIsometricGamma { /** * @cond */ - static constexpr bool zero_depends_on_row = false; - - static constexpr bool zero_depends_on_column = false; - - static constexpr bool non_zero_depends_on_row = false; - - static constexpr bool non_zero_depends_on_column = false; + static constexpr bool is_basic = false; bool is_sparse() const { return false; @@ -1573,7 +1429,7 @@ class DelayedUnaryIsometricGamma { } template - Value_ fill(Index_) const { + Value_ fill(bool, Index_) const { // Use the implementation-defined special value. return std::tgamma(static_cast(0)); } @@ -1595,13 +1451,7 @@ class DelayedUnaryIsometricLgamma { /** * @cond */ - static constexpr bool zero_depends_on_row = false; - - static constexpr bool zero_depends_on_column = false; - - static constexpr bool non_zero_depends_on_row = false; - - static constexpr bool non_zero_depends_on_column = false; + static constexpr bool is_basic = false; bool is_sparse() const { return false; @@ -1638,7 +1488,7 @@ class DelayedUnaryIsometricLgamma { } template - Value_ fill(Index_) const { + Value_ fill(bool, Index_) const { // Use the implementation-defined special value. return std::lgamma(static_cast(0)); } diff --git a/include/tatami/isometric/unary/mock_helpers.hpp b/include/tatami/isometric/unary/mock_helpers.hpp index ca0cbdec..6cc50dad 100644 --- a/include/tatami/isometric/unary/mock_helpers.hpp +++ b/include/tatami/isometric/unary/mock_helpers.hpp @@ -32,6 +32,7 @@ class DelayedUnaryIsometricMockBasic { * @param row Whether the rows are the target dimension. * If true, `buffer` contains row `i`, otherwise it contains column `i`. * @param i Index of the extracted row (if `row = true`) or column (otherwise). + * Unlike `DelayedUnaryIsometricMockAdvanced::dense()`, this is always guaranteed to be available. * @param start Start of the contiguous block of columns (if `row = true`) or rows (otherwise) extracted from `i`. * @param length Length of the contiguous block. * @param[in,out] buffer Contents of the row/column extracted from the matrix. @@ -63,6 +64,7 @@ class DelayedUnaryIsometricMockBasic { * @param row Whether the rows are the target dimension. * If true, `buffer` contains row `i`, otherwise it contains column `i`. * @param i Index of the extracted row (if `row = true`) or column (otherwise). + * Unlike `DelayedUnaryIsometricMockAdvanced::dense()`, this is always guaranteed to be available. * @param indices Sorted and unique indices of columns (if `row = true`) or rows (otherwise) extracted from `i`. * @param[in,out] buffer Contents of the row/column extracted from the matrix. * This has `length` addressable elements, and the result of the operation should be stored here. @@ -81,16 +83,10 @@ class DelayedUnaryIsometricMockBasic { } /** - * Conversion of zeros to non-zero values is dependent on the row of origin. + * Whether this is a basic operation. * This should be true, otherwise an advanced operation interface is expected (see `DelayedUnaryIsometricMockAdvanced`). */ - static constexpr bool zero_depends_on_row = true; - - /** - * Conversion of zeros to non-zero values is dependent on the column of origin. - * This should be true, otherwise an advanced operation interface is expected (see `DelayedUnaryIsometricMockAdvanced`). - */ - static constexpr bool zero_depends_on_column = true; + static constexpr bool is_basic = true; }; /** @@ -118,6 +114,8 @@ class DelayedUnaryIsometricMockAdvanced { * @param row Whether the rows are the target dimension. * If true, `buffer` contains row `i`, otherwise it contains column `i`. * @param i Index of the extracted row (if `row = true`) or column (otherwise). + * This argument should be ignored if the operation does not depend on the row/column (i.e., when all of `zero_depends_on_row()` and friends return false), + * in which case an arbitrary placeholder may be supplied. * @param start Start of the contiguous block of columns (if `row = true`) or rows (otherwise) extracted from `i`. * @param length Length of the contiguous block. * @param[in,out] buffer Contents of the row/column extracted from the matrix. @@ -149,6 +147,8 @@ class DelayedUnaryIsometricMockAdvanced { * @param row Whether the rows are the target dimension. * If true, `buffer` contains row `i`, otherwise it contains column `i`. * @param i Index of the extracted row (if `row = true`) or column (otherwise). + * This argument should be ignored if the operation does not depend on the row/column (i.e., when all of `zero_depends_on_row()` and friends return false), + * in which case an arbitrary placeholder may be supplied. * @param indices Sorted and unique indices of columns (if `row = true`) or rows (otherwise) extracted from `i`. * @param[in,out] buffer Contents of the row/column extracted from the matrix. * This has `length` addressable elements, and the result of the operation should be stored here. @@ -178,6 +178,8 @@ class DelayedUnaryIsometricMockAdvanced { * @param row Whether the rows are the target dimension. * If true, `buffer` contains row `i`, otherwise it contains column `i`. * @param i Index of the extracted row (if `row = true`) or column (otherwise). + * This argument should be ignored if the operation does not depend on the row/column (i.e., when all of `zero_depends_on_row()` and friends return false), + * in which case an arbitrary placeholder may be supplied. * @param num Number of non-zero elements for row/column `i`. * @param[in,out] value Pointer to an array of values of the non-zero elements. * This is guaranteed to have `num` addressable elements. @@ -187,7 +189,7 @@ class DelayedUnaryIsometricMockAdvanced { * This method is expected to iterate over `value` and modify it in place, * i.e., replace each value with the result of the operation on that value. * - * If `non_zero_depends_on_row && !row` or `non_zero_depends_on_column && row`, `index` is guaranteed to be non-NULL. + * If `non_zero_depends_on_row() && !row` or `non_zero_depends_on_column() && row`, `index` is guaranteed to be non-NULL. * Otherwise, it may be NULL and should be ignored. * Even if non-NULL, indices are not guaranteed to be sorted. * @@ -209,52 +211,72 @@ class DelayedUnaryIsometricMockAdvanced { * @tparam Value_ Type of matrix value. * @tparam Index_ Type of index value. * - * @param i The index of the row containing the zero, if `zero_depends_on_row = true`; - * the index of the column containing the zero, if `zero_depends_on_column = true`; - * or ignored, if both `zero_depends_on_row` and `zero_depends_on_column` are both false. + * @param row Whether `i` refers to the row or column index. + * @param i The index of the row (if `row = true`) or column (otherwise) containing the zeros. + * This argument should be ignored if the operation does not depend on the row/column (i.e., when all of `zero_depends_on_row()` and friends return false), + * in which case an arbitrary placeholder may be supplied. * - * @return The result of the operation being applied on zeros from both the left and right matrices. - * This should be constant for all elements in the row/column/matrix, depending on the interpretation of `i`. + * @return The result of the operation being applied on zeros from the `i`-th row/column of the matrix. * * This method will be called with an explicit `Value_` template parameter. * Implementations of this method should either ensure that `Index_` is deducible or use a fixed integer type in the method signature. */ template - Value_ fill([[maybe_unused]] Index_ i) const { + Value_ fill([[maybe_unused]] bool row, [[maybe_unused]] Index_ i) const { return 0; } /** - * Conversion of zeros to non-zero values is not dependent on the row of origin. - * Implementations of the advanced operation interface may set this to `true` provided that `zero_depends_on_column = false`; - * at least one of these must be false, otherwise a basic operation interface is expected (see `DelayedUnaryIsometricMockBasic`). + * Whether this is a basic operation. + * This should be false, otherwise a basic operation interface is expected (see `DelayedUnaryIsometricMockBasic`). + */ + static constexpr bool is_basic = false; + + /** + * @return Whether the operation will convert a structural zero to a non-zero value, + * in a manner that depends on the identity of the column in which the structural zero occurs. * - * This value is only used if `is_sparse()` returns false. + * This method is only called when `is_sparse()` returns false. + * It is not necessary to explicitly return `false` here for sparsity-preserving operations, + * as `DelayedUnaryIsometricOperation` will automatically recognize such operations as being row-independent. + * + * This method may be omitted from the class definition, in which case it is assumed to always return false. */ - static constexpr bool zero_depends_on_row = false; + bool zero_depends_on_row() const { + return false; + } /** - * Conversion of zeros to non-zero values is not dependent on the column of origin. - * Implementations of the advanced operation interface may set this to `true` provided that `zero_depends_on_row = false`. - * at least one of these must be false, otherwise a basic operation interface is expected (see `DelayedUnaryIsometricMockBasic`). + * @return Whether the operation will convert a structural zero to a non-zero value, + * in a manner that depends on the identity of the column in which the structural zero occurs. + * + * This method is only called when `is_sparse()` returns false. + * It is not necessary to explicitly return `false` here for sparsity-preserving operations, + * as `DelayedUnaryIsometricOperation` will automatically recognize such operations as being row-independent. * - * This value is only used if `is_sparse()` returns false. + * This method may be omitted from the class definition, in which case it is assumed to always return false. */ - static constexpr bool zero_depends_on_column = false; + bool zero_depends_on_column() const { + return false; + } /** - * Whether the operation requires the identity of the row of origin. - * This only determines whether `index = NULL` in `sparse()`. - * May be true or false. + * @return Whether the result of the operation on a non-zero operand depends on the identity of the row containing the operand. + * + * This method may be omitted from the class definition, in which case it is assumed to always return false. */ - static constexpr bool non_zero_depends_on_row = false; + bool non_zero_depends_on_row() const { + return false; + } /** - * Whether the operation requires the identity of the column of origin. - * This only determines whether `index = NULL` in `sparse()`. - * May be true or false. + * @return Whether the result of the operation on a non-zero operand depends on the identity of the column containing the operand. + * + * This method may also omitted from the class definition, in which case it is assumed to always return false. */ - static constexpr bool non_zero_depends_on_column = false; + bool non_zero_depends_on_column() const { + return false; + } /** * @return Does this operation preserve sparsity? diff --git a/tests/src/isometric/binary/DelayedBinaryIsometricOperation.cpp b/tests/src/isometric/binary/DelayedBinaryIsometricOperation.cpp index eb8aa706..e18149a6 100644 --- a/tests/src/isometric/binary/DelayedBinaryIsometricOperation.cpp +++ b/tests/src/isometric/binary/DelayedBinaryIsometricOperation.cpp @@ -33,6 +33,147 @@ TEST(DelayedBinaryIsometricOperation, Misshappen) { }, "should be the same"); } +class BinaryMockMissing { +public: + static constexpr bool is_basic = false; + + bool is_sparse() const { return false; } +}; + +class BinaryMockDerived : public tatami::DelayedBinaryIsometricMockAdvanced { +public: + BinaryMockDerived(bool sparse = false, bool zero_row = true, bool zero_col = true, bool non_zero_row = true, bool non_zero_col = true) : + my_sparse(sparse), my_zero_row(zero_row), my_zero_col(zero_col), my_non_zero_row(non_zero_row), my_non_zero_col(non_zero_col) {} + +private: + bool my_sparse, my_zero_row, my_zero_col, my_non_zero_row, my_non_zero_col; + +public: + static constexpr bool is_basic = false; + bool is_sparse() const { return my_sparse; } + bool zero_depends_on_row() const { return my_zero_row; } + bool zero_depends_on_column() const { return my_zero_col; } + bool non_zero_depends_on_row() const { return my_non_zero_row; } + bool non_zero_depends_on_column() const { return my_non_zero_col; } +}; + +TEST(DelayedBinaryIsometricOperation, DependsChecks) { + auto optr = std::make_shared >(10, 20); + + { + // Check that the oracle is correctly ignored. + tatami::DelayedIsometricOperation_internal::MaybeOracleDepends oracle1(optr, {}, true); + EXPECT_EQ(oracle1.get(0), 0); + tatami::DelayedIsometricOperation_internal::MaybeOracleDepends oracle2(optr, {}, false); + EXPECT_EQ(oracle2.get(0), 0); + + EXPECT_TRUE(tatami::DelayedIsometricOperation_internal::can_dense_expand(tatami::DelayedBinaryIsometricMockAdvanced(), true)); + EXPECT_TRUE(tatami::DelayedIsometricOperation_internal::can_dense_expand(tatami::DelayedBinaryIsometricMockAdvanced(), false)); + } + + { + // Check that the oracle is correctly ignored. + tatami::DelayedIsometricOperation_internal::MaybeOracleDepends oracle1(optr, {}, true); + EXPECT_EQ(oracle1.get(0), 0); + tatami::DelayedIsometricOperation_internal::MaybeOracleDepends oracle2(optr, {}, false); + EXPECT_EQ(oracle2.get(0), 0); + + EXPECT_TRUE(tatami::DelayedIsometricOperation_internal::can_dense_expand(BinaryMockMissing(), true)); + EXPECT_TRUE(tatami::DelayedIsometricOperation_internal::can_dense_expand(BinaryMockMissing(), false)); + } + + { + // Check that the oracle is correctly used. + tatami::DelayedIsometricOperation_internal::MaybeOracleDepends oracle1(optr, {}, true); + EXPECT_EQ(oracle1.get(0), 10); + tatami::DelayedIsometricOperation_internal::MaybeOracleDepends oracle2(optr, {}, false); + EXPECT_EQ(oracle2.get(0), 10); + + EXPECT_FALSE(tatami::DelayedIsometricOperation_internal::can_dense_expand(BinaryMockDerived(), true)); + EXPECT_FALSE(tatami::DelayedIsometricOperation_internal::can_dense_expand(BinaryMockDerived(), false)); + } + + { + // Check that the oracle is correctly used for rows. + { + BinaryMockDerived mock(false, true, false, false, false); // zero-dependency for non-sparse operation. + tatami::DelayedIsometricOperation_internal::MaybeOracleDepends oracle1(optr, mock, true); + EXPECT_EQ(oracle1.get(0), 10); + tatami::DelayedIsometricOperation_internal::MaybeOracleDepends oracle2(optr, mock, false); + EXPECT_EQ(oracle2.get(0), 0); + + EXPECT_TRUE(tatami::DelayedIsometricOperation_internal::can_dense_expand(mock, true)); + EXPECT_FALSE(tatami::DelayedIsometricOperation_internal::can_dense_expand(mock, false)); + } + + { + BinaryMockDerived mock(false, false, false, true, false); // non-zero dependency. + tatami::DelayedIsometricOperation_internal::MaybeOracleDepends oracle1(optr, mock, true); + EXPECT_EQ(oracle1.get(0), 10); + tatami::DelayedIsometricOperation_internal::MaybeOracleDepends oracle2(optr, mock, false); + EXPECT_EQ(oracle2.get(0), 0); + + EXPECT_TRUE(tatami::DelayedIsometricOperation_internal::can_dense_expand(mock, true)); + EXPECT_TRUE(tatami::DelayedIsometricOperation_internal::can_dense_expand(mock, false)); + } + + { + BinaryMockDerived mock(true, true, false, false, false); // zero dependency ignored for sparse operations. + tatami::DelayedIsometricOperation_internal::MaybeOracleDepends oracle1(optr, mock, true); + EXPECT_EQ(oracle1.get(0), 0); + tatami::DelayedIsometricOperation_internal::MaybeOracleDepends oracle2(optr, mock, false); + EXPECT_EQ(oracle2.get(0), 0); + + EXPECT_TRUE(tatami::DelayedIsometricOperation_internal::can_dense_expand(mock, true)); + EXPECT_TRUE(tatami::DelayedIsometricOperation_internal::can_dense_expand(mock, false)); + } + } + + { + // Check that the oracle is correctly used for columns. + { + BinaryMockDerived mock(false, false, true, false, false); // zero-dependency for non-sparse operation. + tatami::DelayedIsometricOperation_internal::MaybeOracleDepends oracle1(optr, mock, true); + EXPECT_EQ(oracle1.get(0), 0); + tatami::DelayedIsometricOperation_internal::MaybeOracleDepends oracle2(optr, mock, false); + EXPECT_EQ(oracle2.get(0), 10); + + EXPECT_FALSE(tatami::DelayedIsometricOperation_internal::can_dense_expand(mock, true)); + EXPECT_TRUE(tatami::DelayedIsometricOperation_internal::can_dense_expand(mock, false)); + } + + { + BinaryMockDerived mock(false, false, false, false, true); // non-zero dependency. + tatami::DelayedIsometricOperation_internal::MaybeOracleDepends oracle1(optr, mock, true); + EXPECT_EQ(oracle1.get(0), 0); + tatami::DelayedIsometricOperation_internal::MaybeOracleDepends oracle2(optr, mock, false); + EXPECT_EQ(oracle2.get(0), 10); + + EXPECT_TRUE(tatami::DelayedIsometricOperation_internal::can_dense_expand(mock, true)); + EXPECT_TRUE(tatami::DelayedIsometricOperation_internal::can_dense_expand(mock, false)); + } + + { + BinaryMockDerived mock(true, false, true, false, false); // zero dependency ignored for sparse operations. + tatami::DelayedIsometricOperation_internal::MaybeOracleDepends oracle1(optr, mock, true); + EXPECT_EQ(oracle1.get(0), 0); + tatami::DelayedIsometricOperation_internal::MaybeOracleDepends oracle2(optr, mock, false); + EXPECT_EQ(oracle2.get(0), 0); + + EXPECT_TRUE(tatami::DelayedIsometricOperation_internal::can_dense_expand(mock, true)); + EXPECT_TRUE(tatami::DelayedIsometricOperation_internal::can_dense_expand(mock, false)); + } + } + + { + // Check that the oracle is respected in the basic case. + tatami::DelayedIsometricOperation_internal::MaybeOracleDepends oracle1(optr, {}, true); + EXPECT_EQ(oracle1.get(0), 10); + tatami::DelayedIsometricOperation_internal::MaybeOracleDepends oracle2(optr, {}, false); + EXPECT_EQ(oracle2.get(0), 10); + } +} + class DelayedBinaryIsometricOperationTest : public ::testing::TestWithParam > { protected: inline static int nrow = 23, ncol = 42; diff --git a/tests/src/isometric/unary/DelayedUnaryIsometricOperation.cpp b/tests/src/isometric/unary/DelayedUnaryIsometricOperation.cpp index 94f88633..56b51f16 100644 --- a/tests/src/isometric/unary/DelayedUnaryIsometricOperation.cpp +++ b/tests/src/isometric/unary/DelayedUnaryIsometricOperation.cpp @@ -17,7 +17,7 @@ TEST(DelayedUnaryIsometricOperation, ConstOverload) { auto dense = std::shared_ptr(new tatami::DenseRowMatrix(nrow, ncol, simulated)); auto vec = std::vector(nrow); - auto op = tatami::make_DelayedUnaryIsometricAddVector<0>(vec); + auto op = tatami::make_DelayedUnaryIsometricAddVector(vec, true); auto mat = tatami::make_DelayedUnaryIsometricOperation(dense, std::move(op)); // cursory checks. @@ -25,6 +25,105 @@ TEST(DelayedUnaryIsometricOperation, ConstOverload) { EXPECT_EQ(mat->ncol(), dense->ncol()); } +class UnaryMockMissing { +public: + static constexpr bool is_basic = false; + + bool is_sparse() const { return false; } +}; + +TEST(DelayedUnaryIsometricOperation, DependsChecks) { + auto optr = std::make_shared >(10, 20); + + { + // Check that the oracle is correctly ignored. + tatami::DelayedIsometricOperation_internal::MaybeOracleDepends oracle1(optr, {}, true); + EXPECT_EQ(oracle1.get(0), 0); + tatami::DelayedIsometricOperation_internal::MaybeOracleDepends oracle2(optr, {}, false); + EXPECT_EQ(oracle2.get(0), 0); + + EXPECT_TRUE(tatami::DelayedIsometricOperation_internal::can_dense_expand(tatami::DelayedUnaryIsometricMockAdvanced(), true)); + EXPECT_TRUE(tatami::DelayedIsometricOperation_internal::can_dense_expand(tatami::DelayedUnaryIsometricMockAdvanced(), false)); + EXPECT_FALSE(tatami::DelayedIsometricOperation_internal::needs_sparse_indices(tatami::DelayedUnaryIsometricMockAdvanced(), true)); + EXPECT_FALSE(tatami::DelayedIsometricOperation_internal::needs_sparse_indices(tatami::DelayedUnaryIsometricMockAdvanced(), false)); + } + + { + // Check that the oracle is correctly ignored. + tatami::DelayedIsometricOperation_internal::MaybeOracleDepends oracle1(optr, {}, true); + EXPECT_EQ(oracle1.get(0), 0); + tatami::DelayedIsometricOperation_internal::MaybeOracleDepends oracle2(optr, {}, false); + EXPECT_EQ(oracle2.get(0), 0); + + EXPECT_TRUE(tatami::DelayedIsometricOperation_internal::can_dense_expand(UnaryMockMissing(), true)); + EXPECT_TRUE(tatami::DelayedIsometricOperation_internal::can_dense_expand(UnaryMockMissing(), false)); + EXPECT_FALSE(tatami::DelayedIsometricOperation_internal::needs_sparse_indices(UnaryMockMissing(), true)); + EXPECT_FALSE(tatami::DelayedIsometricOperation_internal::needs_sparse_indices(UnaryMockMissing(), false)); + } + + { + // Check that the oracle is correctly used for rows. + { + auto mock = tatami::make_DelayedUnaryIsometricAddVector({1.0, 2.0}, true); + tatami::DelayedIsometricOperation_internal::MaybeOracleDepends oracle1(optr, mock, true); + EXPECT_EQ(oracle1.get(0), 10); + tatami::DelayedIsometricOperation_internal::MaybeOracleDepends oracle2(optr, mock, false); + EXPECT_EQ(oracle2.get(0), 0); + + EXPECT_TRUE(tatami::DelayedIsometricOperation_internal::can_dense_expand(mock, true)); + EXPECT_FALSE(tatami::DelayedIsometricOperation_internal::can_dense_expand(mock, false)); + } + + { + auto mock = tatami::make_DelayedUnaryIsometricAddVector({0.0, 0.0}, true); + tatami::DelayedIsometricOperation_internal::MaybeOracleDepends oracle1(optr, mock, true); + EXPECT_EQ(oracle1.get(0), 10); + tatami::DelayedIsometricOperation_internal::MaybeOracleDepends oracle2(optr, mock, false); + EXPECT_EQ(oracle2.get(0), 0); + + EXPECT_TRUE(tatami::DelayedIsometricOperation_internal::can_dense_expand(mock, true)); + EXPECT_TRUE(tatami::DelayedIsometricOperation_internal::can_dense_expand(mock, false)); + EXPECT_FALSE(tatami::DelayedIsometricOperation_internal::needs_sparse_indices(mock, true)); + EXPECT_TRUE(tatami::DelayedIsometricOperation_internal::needs_sparse_indices(mock, false)); + } + } + + { + // Check that the oracle is correctly used for columns. + { + auto mock = tatami::make_DelayedUnaryIsometricAddVector({1.0, 2.0}, false); + tatami::DelayedIsometricOperation_internal::MaybeOracleDepends oracle1(optr, mock, true); + EXPECT_EQ(oracle1.get(0), 0); + tatami::DelayedIsometricOperation_internal::MaybeOracleDepends oracle2(optr, mock, false); + EXPECT_EQ(oracle2.get(0), 10); + + EXPECT_FALSE(tatami::DelayedIsometricOperation_internal::can_dense_expand(mock, true)); + EXPECT_TRUE(tatami::DelayedIsometricOperation_internal::can_dense_expand(mock, false)); + } + + { + auto mock = tatami::make_DelayedUnaryIsometricAddVector({0.0, 0.0}, false); + tatami::DelayedIsometricOperation_internal::MaybeOracleDepends oracle1(optr, mock, true); + EXPECT_EQ(oracle1.get(0), 0); + tatami::DelayedIsometricOperation_internal::MaybeOracleDepends oracle2(optr, mock, false); + EXPECT_EQ(oracle2.get(0), 10); + + EXPECT_TRUE(tatami::DelayedIsometricOperation_internal::can_dense_expand(mock, true)); + EXPECT_TRUE(tatami::DelayedIsometricOperation_internal::can_dense_expand(mock, false)); + EXPECT_TRUE(tatami::DelayedIsometricOperation_internal::needs_sparse_indices(mock, true)); + EXPECT_FALSE(tatami::DelayedIsometricOperation_internal::needs_sparse_indices(mock, false)); + } + } + + { + // Check that the oracle is respected in the basic case. + tatami::DelayedIsometricOperation_internal::MaybeOracleDepends oracle1(optr, {}, true); + EXPECT_EQ(oracle1.get(0), 10); + tatami::DelayedIsometricOperation_internal::MaybeOracleDepends oracle2(optr, {}, false); + EXPECT_EQ(oracle2.get(0), 10); + } +} + class DelayedUnaryIsometricOperationTest : public ::testing::TestWithParam > { protected: inline static int nrow = 57, ncol = 37; diff --git a/tests/src/isometric/unary/arithmetic_scalar_helpers.cpp b/tests/src/isometric/unary/arithmetic_scalar_helpers.cpp index e3a8b8ac..cf4171fa 100644 --- a/tests/src/isometric/unary/arithmetic_scalar_helpers.cpp +++ b/tests/src/isometric/unary/arithmetic_scalar_helpers.cpp @@ -338,5 +338,5 @@ TEST(DelayedUnaryIsometricArithmeticScalar, NonFiniteMultiply) { TEST(DelayedUnaryIsometricArithmeticScalar, NonIeee754Divide) { auto op = tatami::make_DelayedUnaryIsometricDivideScalar(5.0); - tatami_test::throws_error([&]() { op.template fill(5); }, "IEEE-754"); + tatami_test::throws_error([&]() { op.template fill(true, 5); }, "IEEE-754"); } diff --git a/tests/src/isometric/unary/arithmetic_vector_helpers.cpp b/tests/src/isometric/unary/arithmetic_vector_helpers.cpp index 904c625d..426778f1 100644 --- a/tests/src/isometric/unary/arithmetic_vector_helpers.cpp +++ b/tests/src/isometric/unary/arithmetic_vector_helpers.cpp @@ -185,15 +185,9 @@ class DelayedUnaryIsometricAddVectorUtils : public DelayedUnaryIsometricArithmet std::shared_ptr& dense_ptr, std::shared_ptr& sparse_ptr) { - if (row) { - auto op = tatami::make_DelayedUnaryIsometricAddVector<0>(vec); - dense_ptr = tatami::make_DelayedUnaryIsometricOperation(dense, op); - sparse_ptr = tatami::make_DelayedUnaryIsometricOperation(sparse, op); - } else { - auto op = tatami::make_DelayedUnaryIsometricAddVector<1>(vec); - dense_ptr = tatami::make_DelayedUnaryIsometricOperation(dense, op); - sparse_ptr = tatami::make_DelayedUnaryIsometricOperation(sparse, op); - } + auto op = tatami::make_DelayedUnaryIsometricAddVector(vec, row); + dense_ptr = tatami::make_DelayedUnaryIsometricOperation(dense, op); + sparse_ptr = tatami::make_DelayedUnaryIsometricOperation(sparse, op); } protected: @@ -290,26 +284,14 @@ class DelayedUnaryIsometricSubtractVectorUtils : public DelayedUnaryIsometricAri std::shared_ptr& dense_ptr, std::shared_ptr& sparse_ptr) { - if (row) { - if (right) { - auto op = tatami::make_DelayedUnaryIsometricSubtractVector(vec); - dense_ptr = tatami::make_DelayedUnaryIsometricOperation(dense, op); - sparse_ptr = tatami::make_DelayedUnaryIsometricOperation(sparse, op); - } else { - auto op = tatami::make_DelayedUnaryIsometricSubtractVector(vec); - dense_ptr = tatami::make_DelayedUnaryIsometricOperation(dense, op); - sparse_ptr = tatami::make_DelayedUnaryIsometricOperation(sparse, op); - } + if (right) { + auto op = tatami::make_DelayedUnaryIsometricSubtractVector(vec, row); + dense_ptr = tatami::make_DelayedUnaryIsometricOperation(dense, op); + sparse_ptr = tatami::make_DelayedUnaryIsometricOperation(sparse, op); } else { - if (right) { - auto op = tatami::make_DelayedUnaryIsometricSubtractVector(vec); - dense_ptr = tatami::make_DelayedUnaryIsometricOperation(dense, op); - sparse_ptr = tatami::make_DelayedUnaryIsometricOperation(sparse, op); - } else { - auto op = tatami::make_DelayedUnaryIsometricSubtractVector(vec); - dense_ptr = tatami::make_DelayedUnaryIsometricOperation(dense, op); - sparse_ptr = tatami::make_DelayedUnaryIsometricOperation(sparse, op); - } + auto op = tatami::make_DelayedUnaryIsometricSubtractVector(vec, row); + dense_ptr = tatami::make_DelayedUnaryIsometricOperation(dense, op); + sparse_ptr = tatami::make_DelayedUnaryIsometricOperation(sparse, op); } } @@ -422,15 +404,9 @@ class DelayedUnaryIsometricMultiplyVectorUtils : public DelayedUnaryIsometricAri std::shared_ptr& dense_ptr, std::shared_ptr& sparse_ptr) { - if (row) { - auto op = tatami::make_DelayedUnaryIsometricMultiplyVector<0>(vec); - dense_ptr = tatami::make_DelayedUnaryIsometricOperation(dense, op); - sparse_ptr = tatami::make_DelayedUnaryIsometricOperation(sparse, op); - } else { - auto op = tatami::make_DelayedUnaryIsometricMultiplyVector<1>(vec); - dense_ptr = tatami::make_DelayedUnaryIsometricOperation(dense, op); - sparse_ptr = tatami::make_DelayedUnaryIsometricOperation(sparse, op); - } + auto op = tatami::make_DelayedUnaryIsometricMultiplyVector(vec, row); + dense_ptr = tatami::make_DelayedUnaryIsometricOperation(dense, op); + sparse_ptr = tatami::make_DelayedUnaryIsometricOperation(sparse, op); } protected: @@ -524,26 +500,14 @@ class DelayedUnaryIsometricDivideVectorUtils : public DelayedUnaryIsometricArith std::shared_ptr& dense_ptr, std::shared_ptr& sparse_ptr) { - if (row) { - if (right) { - auto op = tatami::make_DelayedUnaryIsometricDivideVector(vec); - dense_ptr = tatami::make_DelayedUnaryIsometricOperation(dense, op); - sparse_ptr = tatami::make_DelayedUnaryIsometricOperation(sparse, op); - } else { - auto op = tatami::make_DelayedUnaryIsometricDivideVector(vec); - dense_ptr = tatami::make_DelayedUnaryIsometricOperation(dense, op); - sparse_ptr = tatami::make_DelayedUnaryIsometricOperation(sparse, op); - } + if (right) { + auto op = tatami::make_DelayedUnaryIsometricDivideVector(vec, row); + dense_ptr = tatami::make_DelayedUnaryIsometricOperation(dense, op); + sparse_ptr = tatami::make_DelayedUnaryIsometricOperation(sparse, op); } else { - if (right) { - auto op = tatami::make_DelayedUnaryIsometricDivideVector(vec); - dense_ptr = tatami::make_DelayedUnaryIsometricOperation(dense, op); - sparse_ptr = tatami::make_DelayedUnaryIsometricOperation(sparse, op); - } else { - auto op = tatami::make_DelayedUnaryIsometricDivideVector(vec); - dense_ptr = tatami::make_DelayedUnaryIsometricOperation(dense, op); - sparse_ptr = tatami::make_DelayedUnaryIsometricOperation(sparse, op); - } + auto op = tatami::make_DelayedUnaryIsometricDivideVector(vec, row); + dense_ptr = tatami::make_DelayedUnaryIsometricOperation(dense, op); + sparse_ptr = tatami::make_DelayedUnaryIsometricOperation(sparse, op); } } @@ -727,26 +691,14 @@ class DelayedUnaryIsometricPowerVectorUtils : public DelayedUnaryIsometricArithm auto dense_tmp = tatami::make_DelayedUnaryIsometricOperation(dense, op0); auto sparse_tmp = tatami::make_DelayedUnaryIsometricOperation(sparse, op0); - if (row) { - if (right) { - auto op = tatami::make_DelayedUnaryIsometricPowerVector(vec); - dense_ptr = tatami::make_DelayedUnaryIsometricOperation(dense_tmp, op); - sparse_ptr = tatami::make_DelayedUnaryIsometricOperation(sparse_tmp, op); - } else { - auto op = tatami::make_DelayedUnaryIsometricPowerVector(vec); - dense_ptr = tatami::make_DelayedUnaryIsometricOperation(dense_tmp, op); - sparse_ptr = tatami::make_DelayedUnaryIsometricOperation(sparse_tmp, op); - } + if (right) { + auto op = tatami::make_DelayedUnaryIsometricPowerVector(vec, row); + dense_ptr = tatami::make_DelayedUnaryIsometricOperation(dense_tmp, op); + sparse_ptr = tatami::make_DelayedUnaryIsometricOperation(sparse_tmp, op); } else { - if (right) { - auto op = tatami::make_DelayedUnaryIsometricPowerVector(vec); - dense_ptr = tatami::make_DelayedUnaryIsometricOperation(dense_tmp, op); - sparse_ptr = tatami::make_DelayedUnaryIsometricOperation(sparse_tmp, op); - } else { - auto op = tatami::make_DelayedUnaryIsometricPowerVector(vec); - dense_ptr = tatami::make_DelayedUnaryIsometricOperation(dense_tmp, op); - sparse_ptr = tatami::make_DelayedUnaryIsometricOperation(sparse_tmp, op); - } + auto op = tatami::make_DelayedUnaryIsometricPowerVector(vec, row); + dense_ptr = tatami::make_DelayedUnaryIsometricOperation(dense_tmp, op); + sparse_ptr = tatami::make_DelayedUnaryIsometricOperation(sparse_tmp, op); } } @@ -1020,26 +972,14 @@ class DelayedUnaryIsometricModuloVectorUtils : public DelayedUnaryIsometricArith std::shared_ptr& dense_ptr, std::shared_ptr& sparse_ptr) { - if (row) { - if (right) { - auto op = tatami::make_DelayedUnaryIsometricModuloVector(vec); - dense_ptr = tatami::make_DelayedUnaryIsometricOperation(dense, op); - sparse_ptr = tatami::make_DelayedUnaryIsometricOperation(sparse, op); - } else { - auto op = tatami::make_DelayedUnaryIsometricModuloVector(vec); - dense_ptr = tatami::make_DelayedUnaryIsometricOperation(dense, op); - sparse_ptr = tatami::make_DelayedUnaryIsometricOperation(sparse, op); - } + if (right) { + auto op = tatami::make_DelayedUnaryIsometricModuloVector(vec, row); + dense_ptr = tatami::make_DelayedUnaryIsometricOperation(dense, op); + sparse_ptr = tatami::make_DelayedUnaryIsometricOperation(sparse, op); } else { - if (right) { - auto op = tatami::make_DelayedUnaryIsometricModuloVector(vec); - dense_ptr = tatami::make_DelayedUnaryIsometricOperation(dense, op); - sparse_ptr = tatami::make_DelayedUnaryIsometricOperation(sparse, op); - } else { - auto op = tatami::make_DelayedUnaryIsometricModuloVector(vec); - dense_ptr = tatami::make_DelayedUnaryIsometricOperation(dense, op); - sparse_ptr = tatami::make_DelayedUnaryIsometricOperation(sparse, op); - } + auto op = tatami::make_DelayedUnaryIsometricModuloVector(vec, row); + dense_ptr = tatami::make_DelayedUnaryIsometricOperation(dense, op); + sparse_ptr = tatami::make_DelayedUnaryIsometricOperation(sparse, op); } } @@ -1161,26 +1101,14 @@ class DelayedUnaryIsometricIntegerDivideVectorUtils : public DelayedUnaryIsometr std::shared_ptr& dense_ptr, std::shared_ptr& sparse_ptr) { - if (row) { - if (right) { - auto op = tatami::make_DelayedUnaryIsometricIntegerDivideVector(vec); - dense_ptr = tatami::make_DelayedUnaryIsometricOperation(dense, op); - sparse_ptr = tatami::make_DelayedUnaryIsometricOperation(sparse, op); - } else { - auto op = tatami::make_DelayedUnaryIsometricIntegerDivideVector(vec); - dense_ptr = tatami::make_DelayedUnaryIsometricOperation(dense, op); - sparse_ptr = tatami::make_DelayedUnaryIsometricOperation(sparse, op); - } + if (right) { + auto op = tatami::make_DelayedUnaryIsometricIntegerDivideVector(vec, row); + dense_ptr = tatami::make_DelayedUnaryIsometricOperation(dense, op); + sparse_ptr = tatami::make_DelayedUnaryIsometricOperation(sparse, op); } else { - if (right) { - auto op = tatami::make_DelayedUnaryIsometricIntegerDivideVector(vec); - dense_ptr = tatami::make_DelayedUnaryIsometricOperation(dense, op); - sparse_ptr = tatami::make_DelayedUnaryIsometricOperation(sparse, op); - } else { - auto op = tatami::make_DelayedUnaryIsometricIntegerDivideVector(vec); - dense_ptr = tatami::make_DelayedUnaryIsometricOperation(dense, op); - sparse_ptr = tatami::make_DelayedUnaryIsometricOperation(sparse, op); - } + auto op = tatami::make_DelayedUnaryIsometricIntegerDivideVector(vec, row); + dense_ptr = tatami::make_DelayedUnaryIsometricOperation(dense, op); + sparse_ptr = tatami::make_DelayedUnaryIsometricOperation(sparse, op); } } diff --git a/tests/src/isometric/unary/boolean_vector_helpers.cpp b/tests/src/isometric/unary/boolean_vector_helpers.cpp index e3722453..93696ef9 100644 --- a/tests/src/isometric/unary/boolean_vector_helpers.cpp +++ b/tests/src/isometric/unary/boolean_vector_helpers.cpp @@ -43,16 +43,9 @@ TEST_P(DelayedUnaryIsometricBooleanVectorTest, AND) { fill_default_vector(vec); } - std::shared_ptr dense_mod, sparse_mod; - if (row) { - auto op = tatami::make_DelayedUnaryIsometricBooleanAndVector<0>(vec); - dense_mod = tatami::make_DelayedUnaryIsometricOperation(dense, op); - sparse_mod = tatami::make_DelayedUnaryIsometricOperation(sparse, op); - } else { - auto op = tatami::make_DelayedUnaryIsometricBooleanAndVector<1>(vec); - dense_mod = tatami::make_DelayedUnaryIsometricOperation(dense, op); - sparse_mod = tatami::make_DelayedUnaryIsometricOperation(sparse, op); - } + auto op = tatami::make_DelayedUnaryIsometricBooleanAndVector(vec, row); + auto dense_mod = tatami::make_DelayedUnaryIsometricOperation(dense, op); + auto sparse_mod = tatami::make_DelayedUnaryIsometricOperation(sparse, op); EXPECT_FALSE(dense_mod->is_sparse()); EXPECT_EQ(dense->nrow(), dense_mod->nrow()); @@ -85,16 +78,9 @@ TEST_P(DelayedUnaryIsometricBooleanVectorTest, OR) { fill_default_vector(vec); } - std::shared_ptr dense_mod, sparse_mod; - if (row) { - auto op = tatami::make_DelayedUnaryIsometricBooleanOrVector<0>(vec); - dense_mod = tatami::make_DelayedUnaryIsometricOperation(dense, op); - sparse_mod = tatami::make_DelayedUnaryIsometricOperation(sparse, op); - } else { - auto op = tatami::make_DelayedUnaryIsometricBooleanOrVector<1>(vec); - dense_mod = tatami::make_DelayedUnaryIsometricOperation(dense, op); - sparse_mod = tatami::make_DelayedUnaryIsometricOperation(sparse, op); - } + auto op = tatami::make_DelayedUnaryIsometricBooleanOrVector(vec, row); + auto dense_mod = tatami::make_DelayedUnaryIsometricOperation(dense, op); + auto sparse_mod = tatami::make_DelayedUnaryIsometricOperation(sparse, op); EXPECT_FALSE(dense_mod->is_sparse()); EXPECT_EQ(dense->nrow(), dense_mod->nrow()); @@ -131,16 +117,9 @@ TEST_P(DelayedUnaryIsometricBooleanVectorTest, XOR) { fill_default_vector(vec); } - std::shared_ptr dense_mod, sparse_mod; - if (row) { - auto op = tatami::make_DelayedUnaryIsometricBooleanXorVector<0>(vec); - dense_mod = tatami::make_DelayedUnaryIsometricOperation(dense, op); - sparse_mod = tatami::make_DelayedUnaryIsometricOperation(sparse, op); - } else { - auto op = tatami::make_DelayedUnaryIsometricBooleanXorVector<1>(vec); - dense_mod = tatami::make_DelayedUnaryIsometricOperation(dense, op); - sparse_mod = tatami::make_DelayedUnaryIsometricOperation(sparse, op); - } + auto op = tatami::make_DelayedUnaryIsometricBooleanXorVector(vec, row); + auto dense_mod = tatami::make_DelayedUnaryIsometricOperation(dense, op); + auto sparse_mod = tatami::make_DelayedUnaryIsometricOperation(sparse, op); EXPECT_FALSE(dense_mod->is_sparse()); EXPECT_EQ(dense->nrow(), dense_mod->nrow()); @@ -179,16 +158,9 @@ TEST_P(DelayedUnaryIsometricBooleanVectorTest, EQUAL) { fill_default_vector(vec); } - std::shared_ptr dense_mod, sparse_mod; - if (row) { - auto op = tatami::make_DelayedUnaryIsometricBooleanEqualVector<0>(vec); - dense_mod = tatami::make_DelayedUnaryIsometricOperation(dense, op); - sparse_mod = tatami::make_DelayedUnaryIsometricOperation(sparse, op); - } else { - auto op = tatami::make_DelayedUnaryIsometricBooleanEqualVector<1>(vec); - dense_mod = tatami::make_DelayedUnaryIsometricOperation(dense, op); - sparse_mod = tatami::make_DelayedUnaryIsometricOperation(sparse, op); - } + auto op = tatami::make_DelayedUnaryIsometricBooleanEqualVector(vec, row); + auto dense_mod = tatami::make_DelayedUnaryIsometricOperation(dense, op); + auto sparse_mod = tatami::make_DelayedUnaryIsometricOperation(sparse, op); EXPECT_FALSE(dense_mod->is_sparse()); EXPECT_EQ(dense->nrow(), dense_mod->nrow()); diff --git a/tests/src/isometric/unary/compare_vector_helpers.cpp b/tests/src/isometric/unary/compare_vector_helpers.cpp index 8e0cffc5..eed8bd52 100644 --- a/tests/src/isometric/unary/compare_vector_helpers.cpp +++ b/tests/src/isometric/unary/compare_vector_helpers.cpp @@ -58,16 +58,9 @@ TEST_P(DelayedUnaryIsometricCompareVectorTest, Equal) { fill_default_vector(vec); } - std::shared_ptr dense_mod, sparse_mod; - if (row) { - auto op = tatami::make_DelayedUnaryIsometricEqualVector<0>(vec); - dense_mod = tatami::make_DelayedUnaryIsometricOperation(this->dense, op); - sparse_mod = tatami::make_DelayedUnaryIsometricOperation(this->sparse, op); - } else { - auto op = tatami::make_DelayedUnaryIsometricEqualVector<1>(vec); - dense_mod = tatami::make_DelayedUnaryIsometricOperation(this->dense, op); - sparse_mod = tatami::make_DelayedUnaryIsometricOperation(this->sparse, op); - } + auto op = tatami::make_DelayedUnaryIsometricEqualVector(vec, row); + auto dense_mod = tatami::make_DelayedUnaryIsometricOperation(this->dense, op); + auto sparse_mod = tatami::make_DelayedUnaryIsometricOperation(this->sparse, op); EXPECT_FALSE(dense_mod->is_sparse()); EXPECT_EQ(dense->nrow(), dense_mod->nrow()); @@ -110,16 +103,9 @@ TEST_P(DelayedUnaryIsometricCompareVectorTest, GreaterThan) { fill_default_vector(vec); } - std::shared_ptr dense_mod, sparse_mod; - if (row) { - auto op = tatami::make_DelayedUnaryIsometricGreaterThanVector<0>(vec); - dense_mod = tatami::make_DelayedUnaryIsometricOperation(this->dense, op); - sparse_mod = tatami::make_DelayedUnaryIsometricOperation(this->sparse, op); - } else { - auto op = tatami::make_DelayedUnaryIsometricGreaterThanVector<1>(vec); - dense_mod = tatami::make_DelayedUnaryIsometricOperation(this->dense, op); - sparse_mod = tatami::make_DelayedUnaryIsometricOperation(this->sparse, op); - } + auto op = tatami::make_DelayedUnaryIsometricGreaterThanVector(vec, row); + auto dense_mod = tatami::make_DelayedUnaryIsometricOperation(this->dense, op); + auto sparse_mod = tatami::make_DelayedUnaryIsometricOperation(this->sparse, op); EXPECT_FALSE(dense_mod->is_sparse()); EXPECT_EQ(dense->nrow(), dense_mod->nrow()); @@ -163,15 +149,9 @@ TEST_P(DelayedUnaryIsometricCompareVectorTest, LessThan) { } std::shared_ptr dense_mod, sparse_mod; - if (row) { - auto op = tatami::make_DelayedUnaryIsometricLessThanVector<0>(vec); - dense_mod = tatami::make_DelayedUnaryIsometricOperation(this->dense, op); - sparse_mod = tatami::make_DelayedUnaryIsometricOperation(this->sparse, op); - } else { - auto op = tatami::make_DelayedUnaryIsometricLessThanVector<1>(vec); - dense_mod = tatami::make_DelayedUnaryIsometricOperation(this->dense, op); - sparse_mod = tatami::make_DelayedUnaryIsometricOperation(this->sparse, op); - } + auto op = tatami::make_DelayedUnaryIsometricLessThanVector(vec, row); + dense_mod = tatami::make_DelayedUnaryIsometricOperation(this->dense, op); + sparse_mod = tatami::make_DelayedUnaryIsometricOperation(this->sparse, op); EXPECT_FALSE(dense_mod->is_sparse()); EXPECT_EQ(dense->nrow(), dense_mod->nrow()); @@ -214,16 +194,9 @@ TEST_P(DelayedUnaryIsometricCompareVectorTest, GreaterThanOrEqual) { fill_default_vector(vec); } - std::shared_ptr dense_mod, sparse_mod; - if (row) { - auto op = tatami::make_DelayedUnaryIsometricGreaterThanOrEqualVector<0>(vec); - dense_mod = tatami::make_DelayedUnaryIsometricOperation(this->dense, op); - sparse_mod = tatami::make_DelayedUnaryIsometricOperation(this->sparse, op); - } else { - auto op = tatami::make_DelayedUnaryIsometricGreaterThanOrEqualVector<1>(vec); - dense_mod = tatami::make_DelayedUnaryIsometricOperation(this->dense, op); - sparse_mod = tatami::make_DelayedUnaryIsometricOperation(this->sparse, op); - } + auto op = tatami::make_DelayedUnaryIsometricGreaterThanOrEqualVector(vec, row); + auto dense_mod = tatami::make_DelayedUnaryIsometricOperation(this->dense, op); + auto sparse_mod = tatami::make_DelayedUnaryIsometricOperation(this->sparse, op); EXPECT_FALSE(dense_mod->is_sparse()); EXPECT_EQ(dense->nrow(), dense_mod->nrow()); @@ -266,16 +239,9 @@ TEST_P(DelayedUnaryIsometricCompareVectorTest, LessThanOrEqual) { fill_default_vector(vec); } - std::shared_ptr dense_mod, sparse_mod; - if (row) { - auto op = tatami::make_DelayedUnaryIsometricLessThanOrEqualVector<0>(vec); - dense_mod = tatami::make_DelayedUnaryIsometricOperation(this->dense, op); - sparse_mod = tatami::make_DelayedUnaryIsometricOperation(this->sparse, op); - } else { - auto op = tatami::make_DelayedUnaryIsometricLessThanOrEqualVector<1>(vec); - dense_mod = tatami::make_DelayedUnaryIsometricOperation(this->dense, op); - sparse_mod = tatami::make_DelayedUnaryIsometricOperation(this->sparse, op); - } + auto op = tatami::make_DelayedUnaryIsometricLessThanOrEqualVector(vec, row); + auto dense_mod = tatami::make_DelayedUnaryIsometricOperation(this->dense, op); + auto sparse_mod = tatami::make_DelayedUnaryIsometricOperation(this->sparse, op); EXPECT_FALSE(dense_mod->is_sparse()); EXPECT_EQ(dense->nrow(), dense_mod->nrow()); @@ -312,16 +278,9 @@ TEST_P(DelayedUnaryIsometricCompareVectorTest, NotEqual) { fill_default_vector(vec); } - std::shared_ptr dense_mod, sparse_mod; - if (row) { - auto op = tatami::make_DelayedUnaryIsometricNotEqualVector<0>(vec); - dense_mod = tatami::make_DelayedUnaryIsometricOperation(this->dense, op); - sparse_mod = tatami::make_DelayedUnaryIsometricOperation(this->sparse, op); - } else { - auto op = tatami::make_DelayedUnaryIsometricNotEqualVector<1>(vec); - dense_mod = tatami::make_DelayedUnaryIsometricOperation(this->dense, op); - sparse_mod = tatami::make_DelayedUnaryIsometricOperation(this->sparse, op); - } + auto op = tatami::make_DelayedUnaryIsometricNotEqualVector(vec, row); + auto dense_mod = tatami::make_DelayedUnaryIsometricOperation(this->dense, op); + auto sparse_mod = tatami::make_DelayedUnaryIsometricOperation(this->sparse, op); EXPECT_FALSE(dense_mod->is_sparse()); EXPECT_EQ(dense->nrow(), dense_mod->nrow());