Lakin Wecker
Lakin Wecker

Reputation: 41

Unable to use glm::vec2 as the scalar type for a Matrix and solve using Linear Least Squares

I've read https://eigen.tuxfamily.org/dox/structEigen_1_1NumTraits.html and https://eigen.tuxfamily.org/dox/TopicCustomizing_CustomScalar.html and written what I think is the appropriate code, but obviously it's not. I'm trying to use a glm::vec2 as the scalar type and solve a linear least squares problem, but this is my current error:

[1/4] Automatic MOC for target curves
Generating MOC predefs moc_predefs.h
[2/3] Building CXX object CMakeFiles/curves.dir/src/main.cpp.o
FAILED: CMakeFiles/curves.dir/src/main.cpp.o 
/usr/bin/c++  -DGLM_FORCE_CXX14=1 -DQT_CORE_LIB -DQT_GUI_LIB -DQT_NO_DEBUG -DQT_WIDGETS_LIB -I. -I../ -Icurves_autogen/include -I../glm -I../blaze -isystem /usr/include/qt -isystem /usr/include/qt/QtWidgets -isystem /usr/include/qt/QtGui -isystem /usr/include/qt/QtCore -isystem /usr/lib/qt/mkspecs/linux-g++ -fPIC -std=c++1z -MD -MT CMakeFiles/curves.dir/src/main.cpp.o -MF CMakeFiles/curves.dir/src/main.cpp.o.d -o CMakeFiles/curves.dir/src/main.cpp.o -c ../src/main.cpp
In file included from ../Eigen/SVD:37:0,
                 from ../Eigen/Dense:5,
                 from ../src/lls_solver.hpp:16,
                 from ../src/main.cpp:4:
../Eigen/src/SVD/JacobiSVD.h: In instantiation of ‘Eigen::JacobiSVD<MatrixType, QRPreconditioner>& Eigen::JacobiSVD<MatrixType, QRPreconditioner>::compute(const MatrixType&, unsigned int) [with _MatrixType = Eigen::Matrix<glm::vec<2, float, (glm::qualifier)0>, -1, -1, 1, -1, -1>; int QRPreconditioner = 2; Eigen::JacobiSVD<MatrixType, QRPreconditioner>::MatrixType = Eigen::Matrix<glm::vec<2, float, (glm::qualifier)0>, -1, -1, 1, -1, -1>]’:
../Eigen/src/SVD/JacobiSVD.h:548:14:   required from ‘Eigen::JacobiSVD<MatrixType, QRPreconditioner>::JacobiSVD(const MatrixType&, unsigned int) [with _MatrixType = Eigen::Matrix<glm::vec<2, float, (glm::qualifier)0>, -1, -1, 1, -1, -1>; int QRPreconditioner = 2; Eigen::JacobiSVD<MatrixType, QRPreconditioner>::MatrixType = Eigen::Matrix<glm::vec<2, float, (glm::qualifier)0>, -1, -1, 1, -1, -1>]’
../Eigen/src/SVD/JacobiSVD.h:799:10:   required from ‘Eigen::JacobiSVD<typename Eigen::DenseBase<Derived>::PlainObject> Eigen::MatrixBase<Derived>::jacobiSvd(unsigned int) const [with Derived = Eigen::Matrix<glm::vec<2, float, (glm::qualifier)0>, -1, -1, 1, -1, -1>; typename Eigen::DenseBase<Derived>::PlainObject = Eigen::Matrix<glm::vec<2, float, (glm::qualifier)0>, -1, -1, 1, -1, -1>]’
../src/lls_solver.hpp:84:79:   required from ‘vlw::control_points<PointT> vlw::lls_solver(const vlw::control_points<PointT>&, uint32_t, vlw::basis_function_set<PointT>*) [with PointT = glm::vec<2, float, (glm::qualifier)0>; uint32_t = unsigned int]’
../src/main.cpp:180:103:   required from here
../Eigen/src/SVD/JacobiSVD.h:683:29: error: no match for ‘operator/’ (operand types are ‘const MatrixType {aka const Eigen::Matrix<glm::vec<2, float, (glm::qualifier)0>, -1, -1, 1, -1, -1>}’ and ‘Eigen::JacobiSVD<Eigen::Matrix<glm::vec<2, float, (glm::qualifier)0>, -1, -1, 1, -1, -1>, 2>::RealScalar {aka float}’)
     m_scaledMatrix = matrix / scale;
                      ~~~~~~~^~~~~~~
In file included from ../Eigen/Core:72:0,
                 from ../src/lls_solver.hpp:15,
                 from ../src/main.cpp:4:
../Eigen/src/Core/../plugins/CommonCwiseBinaryOps.h:69:40: note: candidate: template<class T> typename Eigen::internal::enable_if<true, const Eigen::CwiseBinaryOp<Eigen::internal::scalar_quotient_op<typename Eigen::internal::traits<T>::Scalar, typename Eigen::internal::promote_scalar_arg<typename Eigen::internal::traits<T>::Scalar, T, Eigen::internal::has_ReturnType<Eigen::ScalarBinaryOpTraits<typename Eigen::internal::traits<T>::Scalar, T, Eigen::internal::scalar_quotient_op<typename Eigen::internal::traits<T>::Scalar, T> > >::value>::type>, const Derived, const typename Eigen::internal::plain_constant_type<Derived, typename Eigen::internal::promote_scalar_arg<typename Eigen::internal::traits<T>::Scalar, T, Eigen::internal::has_ReturnType<Eigen::ScalarBinaryOpTraits<typename Eigen::internal::traits<T>::Scalar, T, Eigen::internal::scalar_quotient_op<typename Eigen::internal::traits<T>::Scalar, T> > >::value>::type>::type> >::type Eigen::MatrixBase<Derived>::operator/(const T&) const [with T = T; Derived = Eigen::Matrix<glm::vec<2, float, (glm::qualifier)0>, -1, -1, 1, -1, -1>]
 EIGEN_MAKE_SCALAR_BINARY_OP_ONTHERIGHT(operator/,quotient)
                                        ^
../Eigen/src/Core/util/Macros.h:941:4: note: in definition of macro ‘EIGEN_MAKE_SCALAR_BINARY_OP_ONTHERIGHT’
   (METHOD)(const T& scalar) const { \
    ^~~~~~
../Eigen/src/Core/../plugins/CommonCwiseBinaryOps.h:69:40: note:   template argument deduction/substitution failed:
 EIGEN_MAKE_SCALAR_BINARY_OP_ONTHERIGHT(operator/,quotient)
                                        ^
../Eigen/src/Core/util/Macros.h:941:4: note: in definition of macro ‘EIGEN_MAKE_SCALAR_BINARY_OP_ONTHERIGHT’
   (METHOD)(const T& scalar) const { \
    ^~~~~~
../Eigen/src/Core/../plugins/CommonCwiseBinaryOps.h: In substitution of ‘template<class T> typename Eigen::internal::enable_if<true, const Eigen::CwiseBinaryOp<Eigen::internal::scalar_quotient_op<glm::vec<2, float, (glm::qualifier)0>, typename Eigen::internal::promote_scalar_arg<glm::vec<2, float, (glm::qualifier)0>, T, Eigen::internal::has_ReturnType<Eigen::ScalarBinaryOpTraits<glm::vec<2, float, (glm::qualifier)0>, T, Eigen::internal::scalar_quotient_op<glm::vec<2, float, (glm::qualifier)0>, T> > >::value>::type>, const Eigen::Matrix<glm::vec<2, float, (glm::qualifier)0>, -1, -1, 1, -1, -1>, const typename Eigen::internal::plain_constant_type<Eigen::Matrix<glm::vec<2, float, (glm::qualifier)0>, -1, -1, 1, -1, -1>, typename Eigen::internal::promote_scalar_arg<glm::vec<2, float, (glm::qualifier)0>, T, Eigen::internal::has_ReturnType<Eigen::ScalarBinaryOpTraits<glm::vec<2, float, (glm::qualifier)0>, T, Eigen::internal::scalar_quotient_op<glm::vec<2, float, (glm::qualifier)0>, T> > >::value>::type>::type> >::type Eigen::MatrixBase<Eigen::Matrix<glm::vec<2, float, (glm::qualifier)0>, -1, -1, 1, -1, -1> >::operator/<T>(const T&) const [with T = float]’:
../Eigen/src/SVD/JacobiSVD.h:683:29:   required from ‘Eigen::JacobiSVD<MatrixType, QRPreconditioner>& Eigen::JacobiSVD<MatrixType, QRPreconditioner>::compute(const MatrixType&, unsigned int) [with _MatrixType = Eigen::Matrix<glm::vec<2, float, (glm::qualifier)0>, -1, -1, 1, -1, -1>; int QRPreconditioner = 2; Eigen::JacobiSVD<MatrixType, QRPreconditioner>::MatrixType = Eigen::Matrix<glm::vec<2, float, (glm::qualifier)0>, -1, -1, 1, -1, -1>]’
../Eigen/src/SVD/JacobiSVD.h:548:14:   required from ‘Eigen::JacobiSVD<MatrixType, QRPreconditioner>::JacobiSVD(const MatrixType&, unsigned int) [with _MatrixType = Eigen::Matrix<glm::vec<2, float, (glm::qualifier)0>, -1, -1, 1, -1, -1>; int QRPreconditioner = 2; Eigen::JacobiSVD<MatrixType, QRPreconditioner>::MatrixType = Eigen::Matrix<glm::vec<2, float, (glm::qualifier)0>, -1, -1, 1, -1, -1>]’
../Eigen/src/SVD/JacobiSVD.h:799:10:   required from ‘Eigen::JacobiSVD<typename Eigen::DenseBase<Derived>::PlainObject> Eigen::MatrixBase<Derived>::jacobiSvd(unsigned int) const [with Derived = Eigen::Matrix<glm::vec<2, float, (glm::qualifier)0>, -1, -1, 1, -1, -1>; typename Eigen::DenseBase<Derived>::PlainObject = Eigen::Matrix<glm::vec<2, float, (glm::qualifier)0>, -1, -1, 1, -1, -1>]’
../src/lls_solver.hpp:84:79:   required from ‘vlw::control_points<PointT> vlw::lls_solver(const vlw::control_points<PointT>&, uint32_t, vlw::basis_function_set<PointT>*) [with PointT = glm::vec<2, float, (glm::qualifier)0>; uint32_t = unsigned int]’
../src/main.cpp:180:103:   required from here
../Eigen/src/Core/../plugins/CommonCwiseBinaryOps.h:69:40: error: no type named ‘type’ in ‘struct Eigen::internal::promote_scalar_arg<glm::vec<2, float, (glm::qualifier)0>, float, false>’
 EIGEN_MAKE_SCALAR_BINARY_OP_ONTHERIGHT(operator/,quotient)
                                        ^
../Eigen/src/Core/util/Macros.h:941:4: note: in definition of macro ‘EIGEN_MAKE_SCALAR_BINARY_OP_ONTHERIGHT’
   (METHOD)(const T& scalar) const { \
    ^~~~~~
In file included from /usr/include/qt/QtCore/QMargins:1:0,
                 from /usr/include/qt/QtGui/qwindow.h:46,
                 from /usr/include/qt/QtGui/QWindow:1,
                 from ../src/openglwindow.h:51,
                 from ../src/main.cpp:5:
../Eigen/src/SVD/JacobiSVD.h: In instantiation of ‘Eigen::JacobiSVD<MatrixType, QRPreconditioner>& Eigen::JacobiSVD<MatrixType, QRPreconditioner>::compute(const MatrixType&, unsigned int) [with _MatrixType = Eigen::Matrix<glm::vec<2, float, (glm::qualifier)0>, -1, -1, 1, -1, -1>; int QRPreconditioner = 2; Eigen::JacobiSVD<MatrixType, QRPreconditioner>::MatrixType = Eigen::Matrix<glm::vec<2, float, (glm::qualifier)0>, -1, -1, 1, -1, -1>]’:
../Eigen/src/SVD/JacobiSVD.h:548:14:   required from ‘Eigen::JacobiSVD<MatrixType, QRPreconditioner>::JacobiSVD(const MatrixType&, unsigned int) [with _MatrixType = Eigen::Matrix<glm::vec<2, float, (glm::qualifier)0>, -1, -1, 1, -1, -1>; int QRPreconditioner = 2; Eigen::JacobiSVD<MatrixType, QRPreconditioner>::MatrixType = Eigen::Matrix<glm::vec<2, float, (glm::qualifier)0>, -1, -1, 1, -1, -1>]’
../Eigen/src/SVD/JacobiSVD.h:799:10:   required from ‘Eigen::JacobiSVD<typename Eigen::DenseBase<Derived>::PlainObject> Eigen::MatrixBase<Derived>::jacobiSvd(unsigned int) const [with Derived = Eigen::Matrix<glm::vec<2, float, (glm::qualifier)0>, -1, -1, 1, -1, -1>; typename Eigen::DenseBase<Derived>::PlainObject = Eigen::Matrix<glm::vec<2, float, (glm::qualifier)0>, -1, -1, 1, -1, -1>]’
../src/lls_solver.hpp:84:79:   required from ‘vlw::control_points<PointT> vlw::lls_solver(const vlw::control_points<PointT>&, uint32_t, vlw::basis_function_set<PointT>*) [with PointT = glm::vec<2, float, (glm::qualifier)0>; uint32_t = unsigned int]’
../src/main.cpp:180:103:   required from here

This is the code:

#include "basis_function_set.hpp"
#include "control_points.hpp"
#include "knots.hpp"

#include "debug.hpp"

//#include <blaze/Math.h>

#include <cstdint>
#include <iostream>

#include <Eigen/Core>
#include <Eigen/Dense>

namespace Eigen {
template<> struct NumTraits<glm::vec2>
 : NumTraits<double> // permits to get the epsilon, dummy_precision, lowest, highest functions
{
  typedef glm::vec2::value_type Real;
  typedef glm::vec2::value_type NonInteger;
  typedef glm::vec2 Literal;
  typedef glm::vec2 Nested;

  enum {
    IsComplex = 0,
    IsInteger = 0,
    IsSigned = 1,
    RequireInitialization = 1,
    ReadCost = 2,
    AddCost = 2,
    MulCost = 2
  };
};
}
namespace glm {
    inline const vec2& conj(const vec2& x)  { return x; }
    inline const vec2& real(const vec2& x)  { return x; }
    inline vec2 imag(const vec2&)    { return vec2{0.}; }
    inline vec2 abs(const vec2&  x)  { return vec2{fabs(x.x), fabs(x.y)}; }
    inline vec2 abs2(const vec2& x)  { return x*x; }
}


namespace vlw {
    template <typename PointT>
    vlw::control_points<PointT> lls_solver(
        const vlw::control_points<PointT> &data,
        uint32_t cp_size,
        vlw::basis_function_set<PointT> *basis_set
    ) {
        using MatrixVec2 = Eigen::Matrix<PointT, Eigen::Dynamic, Eigen::Dynamic, Eigen::RowMajor>;
        using VectorVec2 = Eigen::Matrix<PointT, Eigen::Dynamic, 1>;
        using ScalarT = typename PointT::value_type;

        const auto N = cp_size;
        const auto M = data.size();
        auto t = uniform_knots<ScalarT>(
            basis_set->get_open_knot_size(cp_size),
            basis_set->get_open_knot_repeated_size(cp_size)
        );
        ScalarT knot_range = ((t[t.size()-1] - t[0])-0.00001);
        MatrixVec2 A( N, M );
        for (uint32_t m = 0; m < M; ++m) {
            for (uint32_t i = 0; i < N; ++i) {
                auto u = static_cast<ScalarT>(m) / (static_cast<ScalarT>(M-1)+0.00001);
                u = (u * knot_range) + t[0];
                auto n = basis_set->get(i)->evaluate(i, u, t);
                PointT p;
                for (uint32_t d = 0; d < PointT::length(); ++d) {
                    p[d] = n;
                }
                A(m, i) = p;
            }
        }
        vlw::control_points<PointT> retval{cp_size, PointT{0.0}};
        VectorVec2 b(M);
        for (uint32_t j = 0; j < data.size(); ++j) {
            b(j) = data[j];
        }
        VectorVec2 x = A.jacobiSvd(Eigen::ComputeThinU | Eigen::ComputeThinV).solve(b);
        //MatrixVec2 AtA = A.transpose() * A;
        //VectorVec2 b = A.transpose() * y;
        std::cout << A << std::endl;
        std::cout << "--" << std::endl;
        std::cout << b << std::endl;
        VectorVec2 x = AtA.colPivHouseholderQr().solve(b);
        //std::cout << "--" << std::endl;
        //std::cout << x << std::endl;


        return retval;
    }
}; // end namespace vlw

Upvotes: 1

Views: 363

Answers (1)

Lakin Wecker
Lakin Wecker

Reputation: 41

I waited a full 24 hours before posting the question, and then managed to get it working within hours of posting it. Such is life. The following solution is slightly different than the original solution in that I've switched to solving the normal equations, rather than using jacobiSVD, but it works.

#include "basis_function_set.hpp"
#include "control_points.hpp"
#include "knots.hpp"

#include "debug.hpp"

//#include <blaze/Math.h>

#include <cstdint>
#include <iostream>

#include <Eigen/Core>
#include <Eigen/Dense>

namespace Eigen {
template<> struct NumTraits<glm::vec2> : GenericNumTraits<glm::vec2>
{
    typedef glm::vec2 ReturnType;
    typedef glm::vec2 Real;
    typedef glm::vec2 NonInteger;
    typedef glm::vec2 Literal;
    typedef glm::vec2 Nested;

    static inline glm::vec2 epsilon() {
        return glm::vec2{
                std::numeric_limits<glm::vec2::value_type>::epsilon(),
                std::numeric_limits<glm::vec2::value_type>::epsilon()
        };
    }
    static inline glm::vec2::value_type digits10() { return std::numeric_limits<glm::vec2::value_type>::digits10; }
    static inline glm::vec2::value_type dummy_precision() { return 0.; }
    static inline glm::vec2 lowest() {
        return glm::vec2{
                std::numeric_limits<glm::vec2::value_type>::min(),
                std::numeric_limits<glm::vec2::value_type>::min()
        };
    }
    static inline glm::vec2 highest() {
        return glm::vec2{
                std::numeric_limits<glm::vec2::value_type>::max(),
                std::numeric_limits<glm::vec2::value_type>::max()
        };
    }
    static inline glm::vec2 infinity() {
        return glm::vec2{
                std::numeric_limits<glm::vec2::value_type>::infinity(),
                std::numeric_limits<glm::vec2::value_type>::infinity()
        };
    }
    static inline glm::vec2 quiet_NaN() {
        return glm::vec2{
                std::numeric_limits<glm::vec2::value_type>::quiet_NaN(),
                std::numeric_limits<glm::vec2::value_type>::quiet_NaN()
        };
    }


    enum {
        IsComplex = 0,
        IsInteger = 0,
        IsSigned = 1,
        RequireInitialization = 1,
        ReadCost = 2,
        AddCost = 2,
        MulCost = 2
    };
};
}
namespace glm {
    inline const vec2& conj(const vec2& x)  { return x; }
    inline const vec2& real(const vec2& x)  { return x; }
    inline vec2::value_type imag(const vec2&)    { return 0.; }
    inline vec2::value_type abs2(const vec2 &x)  { return length2(x)*length2(x); }
    inline vec2 operator/(const vec2 &lhs, float x ) { return vec2{lhs.x/x, lhs.y/x}; }
    inline bool operator<(const vec2 &lhs, const vec2 &rhs) { return length(lhs)<length(rhs); }
    inline bool operator<=(const vec2 &lhs, const vec2 &rhs) { return length(lhs)<=length(rhs); }
    inline bool operator>(const vec2 &lhs, const vec2 &rhs) { return length(lhs)>length(rhs); }
    inline bool operator>= (const vec2 &lhs, const vec2 &rhs) { return length(lhs)>=length(rhs); }
    inline vec2 operator* (long int x, const vec2 &rhs) { return static_cast<glm::vec2::value_type>(x) * rhs; }

}


namespace vlw {
    template <typename PointT>
    vlw::control_points<PointT> lls_solver(
        const vlw::control_points<PointT> &data,
        uint32_t cp_size,
        vlw::basis_function_set<PointT> *basis_set
    ) {
        using MatrixVec2 = Eigen::Matrix<PointT, Eigen::Dynamic, Eigen::Dynamic, Eigen::RowMajor>;
        using VectorVec2 = Eigen::Matrix<PointT, Eigen::Dynamic, 1>;
        using ScalarT = typename PointT::value_type;

        const auto N = cp_size;
        const auto M = data.size();
        auto t = uniform_knots<ScalarT>(
                basis_set->get_open_knot_size(cp_size),
                basis_set->get_open_knot_repeated_size(cp_size)
        );
        ScalarT knot_range = ((t[t.size() - 1] - t[0]) - 0.00001);
        MatrixVec2 A(N, M);
        for (uint32_t m = 0; m < M; ++m) {
            for (uint32_t i = 0; i < N; ++i) {
                auto u = static_cast<ScalarT>(m) / (static_cast<ScalarT>(M - 1) + 0.00001);
                u = (u * knot_range) + t[0];
                auto n = basis_set->get(i)->evaluate(i, u, t);
                PointT p;
                for (uint32_t d = 0; d < PointT::length(); ++d) {
                    p[d] = n;
                }
                A(m, i) = p;
            }
        }
        vlw::control_points<PointT> retval{cp_size, PointT{0.0}};
        VectorVec2 b(M);
        for (uint32_t j = 0; j < data.size(); ++j) {
            b(j) = data[j];
        }
        //VectorVec2 x = A.jacobiSvd(Eigen::ComputeThinU | Eigen::ComputeThinV).solve(b);
        MatrixVec2 AtA = A.transpose() * A;
        b = A.transpose() * b;
//        std::cout << A << std::endl;
//        std::cout << "--" << std::endl;
//        std::cout << b << std::endl;
        VectorVec2 x = AtA.colPivHouseholderQr().solve(b);
        for (uint32_t i = 0; i < x.size(); ++i) {
            PointT cp;
            for (uint32_t d = 0; d < PointT::length(); ++d) {
                cp[d] = x[i][d];
            }
            retval[i] = cp;
        }

        return retval;
    }
}; // end namespace vlw

Upvotes: 1

Related Questions