Program Listing for File factory.hpp

Return to documentation for file (src/navtk/factory.hpp)

#pragma once

#include <type_traits>

#include <xtensor/core/xexpression.hpp>
#include <xtensor/core/xmath.hpp>
#include <xtensor/views/xbroadcast.hpp>

#include <navtk/inspect.hpp>
#include <navtk/tensors.hpp>

namespace navtk {

template <class T, IfTensorOfDim<T, 1>* = nullptr>
inline Vector to_vec(const T& m) {
    if constexpr (xt::is_xexpression<T>::value == true) {
        return m;
    } else {
        return Vector(m);
    }
}

template <class T, IfTensorOfDim<T, 2>* = nullptr>
inline Matrix to_matrix(const T& m, std::size_t = 1) {
    if constexpr (xt::is_xexpression<T>::value == true) {
        return m;
    } else {
        return Matrix(m);
    }
}

template <class T, IfTensorOfDim<T, 3>* = nullptr>
inline Tensor<3> to_tensor_3d(const T& m, std::size_t = 1) {
    if constexpr (xt::is_xexpression<T>::value == true) {
        return m;
    } else {
        return Tensor<3>(m);
    }
}

#ifndef NEED_DOXYGEN_EXHALE_WORKAROUND

template <class T, IfTensorOfDim<T, 0>* = nullptr>
inline Vector to_vec(const T& m) {
    if constexpr (std::ranges::range<T>) {
        if (has_zero_size(m)) return Vector{};
        return Vector{m()};
    } else if constexpr (std::is_arithmetic<T>::value == true) {
        return Vector({m});
    } else {
        return Vector{m()};
    }
}

template <class T, IfTensorOfDim<T, 2>* = nullptr>
Vector to_vec(const T& m) {
    auto mat = to_matrix(m);
    if (has_zero_size(mat)) {
        return Vector{};
    } else if (mat.shape()[0] == 1 && mat.shape()[1] == 1) {
        return Tensor<1>{mat(0, 0)};
    } else if (mat.shape()[0] == 1 || mat.shape()[1] == 1) {
        return xt::squeeze(mat);
    }

    // Combine data from all rows into one row
    return xt::flatten(mat);
}

template <class T, IfTensorOfDim<T, 3>* = nullptr>
Vector to_vec(const T& m) {
    auto tensor = to_tensor_3d(m);
    if (has_zero_size(tensor)) {
        return Vector{};
    } else if (tensor.shape()[0] == 1 && tensor.shape()[1] == 1 && tensor.shape()[2] == 1) {
        return Tensor<1>{tensor(0, 0, 0)};
    }

    // if there is at least one dimmension that is not being used
    if (tensor.shape()[0] == 1 || tensor.shape()[1] == 1 || tensor.shape()[2] == 1) {
        auto squeezed = xt::squeeze(tensor);
        if (squeezed.dimension() == 1) {
            return squeezed;
        } else if (squeezed.dimension() == 2) {
            return xt::flatten(squeezed);
        }
    }

    return xt::flatten(tensor);
}

template <typename T, IfEigenInterface<T>* = nullptr>
Vector to_vec(const T& m) {

    // TODO: PNTOS-56 Instead, could attempt to block memory copy if we can determine memory layout
    // of m
    auto rows = m.rows();
    auto cols = m.cols();

    Vector out = xt::zeros<Scalar>({rows * cols});
    for (decltype(rows) i = 0; i < rows; i++) {
        for (decltype(cols) j = 0; j < cols; j++) {
            out(i * cols + j) = m(i, j);
        }
    }
    return out;
}

template <class T, IfTensorOfDim<T, 0>* = nullptr>
inline Matrix to_matrix(const T& m, std::size_t = 1) {
    if constexpr (std::ranges::range<T>) {
        if (has_zero_size(m)) return Matrix{};
        return Matrix{{m()}};
    } else if constexpr (std::is_arithmetic<T>::value == true) {
        return Matrix{{m}};
    } else {
        return Matrix{{m()}};
    }
}

template <class T, IfTensorOfDim<T, 1>* = nullptr>
Matrix to_matrix(const T& m, std::size_t axis = 1) {
    auto vec = to_vec(m);
    return xt::expand_dims(vec, axis);
}

template <class T, IfTensorOfDim<T, 3>* = nullptr>
Matrix to_matrix(const T& m, std::size_t axis = 1) {
    auto tensor = to_tensor_3d(m);
    if (has_zero_size(tensor)) {
        return Matrix{};
    }

    if (tensor.shape()[0] == 1 && tensor.shape()[1] == 1 && tensor.shape()[2] == 1) {
        return Tensor<2>{{tensor(0, 0, 0)}};
    }

    if (tensor.shape()[0] == 1 || tensor.shape()[1] == 1 || tensor.shape()[2] == 1) {
        auto squeezed = xt::squeeze(tensor);
        if (squeezed.dimension() == 1) {
            return xt::expand_dims(squeezed, axis);
        } else if (squeezed.dimension() == 2) {
            return squeezed;
        }
    }

    auto flattened = xt::flatten(tensor);

    return xt::expand_dims(flattened, axis);
}

template <typename T, IfEigenInterface<T>* = nullptr>
Matrix to_matrix(const T& m, std::size_t = 1) {

    // TODO: PNTOS-56 Instead, could attempt to block memory copy if we can determine memory layout
    // of m
    auto rows = m.rows();
    auto cols = m.cols();

    Matrix out = xt::zeros<Scalar>({rows, cols});
    for (decltype(rows) i = 0; i < rows; i++) {
        for (decltype(cols) j = 0; j < cols; j++) {
            out(i, j) = m(i, j);
        }
    }
    return out;
}

template <typename T, std::size_t rows, std::size_t cols>
Matrix to_matrix(T (&data)[rows][cols]) {
    Matrix out = xt::zeros<Scalar>({rows, cols});
    for (Size ii = 0; ii < rows; ++ii)
        for (Size jj = 0; jj < cols; ++jj) out(ii, jj) = data[ii][jj];
    return out;
}

template <class T, IfTensorOfDim<T, 0>* = nullptr>
inline Tensor<3> to_tensor_3d(const T& m, std::size_t = 1) {
    if constexpr (xt::is_xexpression<T>::value == true) {
        if (has_zero_size(m)) return Tensor<3>{};
        return Tensor<3>{{{m()}}};
    } else if constexpr (std::is_arithmetic<T>::value == true) {
        return Tensor<3>{{{m}}};
    } else {
        return Tensor<3>{{{m()}}};
    }
}

template <class T, IfTensorOfDim<T, 1>* = nullptr>
Tensor<3> to_tensor_3d(const T& m, std::size_t axis = 2) {
    auto vec       = to_vec(m);
    const size_t N = vec.shape()[0];

    std::array<size_t, 3> shape = {1, 1, 1};
    shape[axis]                 = N;

    return xt::reshape_view(vec, shape);
}

template <class T, IfTensorOfDim<T, 2>* = nullptr>
Tensor<3> to_tensor_3d(const T& m, std::size_t axis = 0) {
    auto mat = to_matrix(m);
    return xt::expand_dims(mat, axis);
}

template <typename T, std::size_t index, std::size_t rows, std::size_t cols>
Tensor<3> to_tensor_3d(T (&data)[index][rows][cols]) {
    auto out = xt::zeros<Scalar>({index, rows, cols});
    for (Size ii = 0; ii < index; ++ii)
        for (Size jj = 0; jj < rows; ++jj)
            for (Size kk = 0; kk < cols; ++kk) out(ii, jj, kk) = data[ii][jj][kk];
    return out;
}
#endif

Matrix eye(Size rows, Size cols, int diagonal_index = 0);

Matrix eye(Size size);

#ifndef NEED_DOXYGEN_EXHALE_WORKAROUND
template <typename... T>
Tensor<sizeof...(T)> empty(T... dim) {
    using tensor_shape_type = typename Tensor<sizeof...(T)>::shape_type::value_type;
    return Tensor<sizeof...(T)>::from_shape({static_cast<tensor_shape_type>(dim)...});
}

template <typename... T>
Tensor<sizeof...(T)> zeros(T... dim) {
    return xt::zeros<Scalar>({Size(dim)...});
}

template <typename... T>
Tensor<sizeof...(T)> fill(Scalar value, T... dim) {
    return zeros(dim...) + value;
}

template <typename... T>
Tensor<sizeof...(T)> ones(T... dim) {
    return xt::ones<Scalar>({Size(dim)...});
}
#endif

Matrix block_diag(std::initializer_list<Matrix> matrices);

template <typename... T>
Matrix block_diag(T&&... matrices) {
    return block_diag({to_matrix(matrices, 0)...});
}

}  // namespace navtk