/* Copyright (C) 2013 Tom Bachmann This file is part of FLINT. FLINT is free software: you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License (LGPL) as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. See . */ // Common code shared among matrix classes #ifndef FLINTXX_MATRIX_H #define FLINTXX_MATRIX_H #include "flint_classes.h" #include "mp.h" #include "rules.h" #include "stdmath.h" #include "ltuple.h" #include "traits.h" #include "tuple.h" #include "../permxx.h" namespace flint { FLINT_DEFINE_BINOP(solve) FLINT_DEFINE_BINOP(solve_fflu) FLINT_DEFINE_THREEARY(mat_at) FLINT_DEFINE_THREEARY(solve_fflu_precomp) FLINT_DEFINE_UNOP(charpoly) FLINT_DEFINE_UNOP(det) FLINT_DEFINE_UNOP(det_fflu) FLINT_DEFINE_UNOP(det_interpolate) FLINT_DEFINE_UNOP(nullspace) FLINT_DEFINE_UNOP(rref) FLINT_DEFINE_UNOP(trace) FLINT_DEFINE_UNOP(transpose) FLINT_DEFINE_THREEARY(fflu) FLINT_DEFINE_THREEARY_HERE_2DEFAULT(fflu, permxx*, 0, bool, false) template struct matrix_traits : rules::UNIMPLEMENTED { }; // override this for the non-reference type of your choice // { // template static slong rows(const M&); // template static slong cols(const M&); // template static ??? at(const M&, slong, slong); // template static ??? at(M&, slong, slong); // }; namespace traits { template struct is_mat : is_implemented< matrix_traits::type> > { }; template struct is_mat >::type> : mp::false_ { }; } // traits namespace matrices { namespace mdetail { // Some helper traits used below. template struct second_is_mat_data : mp::false_ { }; template struct second_is_mat_data > > : traits::is_mat::type> { }; template struct second_is_mat : second_is_mat_data { }; template struct both_mat : mp::and_< traits::is_mat< typename traits::basetype::type>, traits::is_mat< typename traits::basetype::type> > { }; // A more convenient way to obtain the traits associated to a non-immediate // or non-nonref expression. template struct immediate_traits : matrix_traits::type> { }; } // mdetail // For matrix expressions to create temporaries, it is necessary to know the // dimensions of the result of a computation. This is a generic implementation, // which assumes that the output dimensions are the same as the dimensions of // the first argument, which is assumed to be a matrix, except if there are // precisely two arguments only the second of which is a matrix, in which case // we assume its the dimension of that. // This implementation works correctly in many cases, e.g. matrix-addition or // matrix-scalar multiplication. template struct outsize_generic { template static slong rows(const Mat& m, typename mp::disable_if >::type* = 0) { return m._data().head.rows(); } template static slong cols(const Mat& m, typename mp::disable_if >::type* = 0) { return m._data().head.cols(); } template static slong rows(const Mat& m, typename mp::enable_if >::type* = 0) { return m._data().tail.head.rows(); } template static slong cols(const Mat& m, typename mp::enable_if >::type* = 0) { return m._data().tail.head.cols(); } }; // This is the expression template used for computing the dimensions of an // operation. Without further specialisation, it is just the generic // implementation described above. // If you introduce a new operation where the generic implementation is // incorrect, you must specialise this template. template struct outsize : outsize_generic { }; // Specialise immediates, where the dimensions are stored with the object. template<> struct outsize { template static slong rows(const Mat& m) { return mdetail::immediate_traits::rows(m); } template static slong cols(const Mat& m) { return mdetail::immediate_traits::cols(m); } }; // Specialise multiplication. For matrix-matrix multiplication, use // the usual formula. For matrix-scalar multiplication, use the generic // implementation. template<> struct outsize { template static slong rows(const Mat& m, typename mp::enable_if >::type* = 0) { return m._data().head.rows(); } template static slong cols(const Mat& m, typename mp::enable_if >::type* = 0) { return m._data().tail.head.cols(); } template static slong rows(const Mat& m, typename mp::disable_if >::type* = 0) { return outsize_generic::rows(m); } template static slong cols(const Mat& m, typename mp::disable_if >::type* = 0) { return outsize_generic::cols(m); } }; // Any particular multipication algorithm also has to be specialised. template<> struct outsize : outsize { }; // Specialise transpose. template<> struct outsize { template static slong rows(const Mat& m) { return m._data().head.cols(); } template static slong cols(const Mat& m) { return m._data().head.rows(); } }; // Specialise nullspace. Note that the nullspace computation functions in // flint return a matrix the columns of which span the nullspace. Since the // nullity is not known in advance in general, we have to allocate a square // matrix. template<> struct outsize { template static slong rows(const Mat& m) { return m._data().head.cols(); } template static slong cols(const Mat& m) { return m._data().head.cols(); } }; // This is a bit of a hack. Matrix operations returning a tuple typically // only return one matrix. We key outsize on the inner operation to find out // the dimensions. So e.g. solve(A, X).get<1>() (say) will invoke outsize // with ltuple_get_op<1> as argument, which then invokes outsize with solve_op // and (A, X) as argument. template struct outsize > { template static slong rows(const Mat& m) { return outsize< typename Mat::data_t::head_t::operation_t>::rows(m._data().head); } template static slong cols(const Mat& m) { return outsize< typename Mat::data_t::head_t::operation_t>::cols(m._data().head); } }; // This is not actually a matrix expression, but called by the above ... template<> struct outsize { template static slong rows(const Mat& m) { return m._data().second().rows(); } template static slong cols(const Mat& m) { return m._data().second().cols(); } }; template<> struct outsize : outsize { }; template<> struct outsize { template static slong rows(const Mat& m) { return m._data().tail.second().rows(); } template static slong cols(const Mat& m) { return m._data().tail.second().cols(); } }; namespace mdetail { struct base_traits { template static slong rows(const M& m) { return matrices::outsize::rows(m); } template static slong cols(const M& m) { return matrices::outsize::cols(m); } }; } // mdetail // These traits classes are useful for implementing unified coefficient access. // See fmpz_matxx etc for example usage. template struct generic_traits : mdetail::base_traits { template struct at { typedef FLINT_THREEARY_ENABLE_RETTYPE(mat_at, Mat, T, U) entry_ref_t; typedef entry_ref_t entry_srcref_t; static entry_srcref_t get(const Mat& m, T i, U j) { return mat_at(m, i, j); } }; }; template struct generic_traits_srcref : mdetail::base_traits { template struct at { typedef Srcref entry_ref_t; typedef Srcref entry_srcref_t; template static Srcref get(M m, T i, U j) { return mdetail::immediate_traits::at(m, i, j); } }; }; template struct generic_traits_ref : mdetail::base_traits { template struct at { typedef Ref entry_ref_t; typedef Ref entry_srcref_t; template static Ref get(M m, T i, U j) { return mdetail::immediate_traits::at(m, i, j); } }; }; template struct generic_traits_nonref : mdetail::base_traits { template struct at { typedef Ref entry_ref_t; typedef Srcref entry_srcref_t; template static Ref get(M& m, T i, U j) { return mdetail::immediate_traits::at(m, i, j); } template static Srcref get(const M& m, T i, U j) { return mdetail::immediate_traits::at(m, i, j); } }; }; } // matrices // immediate functions template inline typename mp::enable_if, slong>::type rank(const Mat& m) { return m.rank(); } template inline typename mp::enable_if, slong>::type find_pivot_any(const Mat& m, slong start, slong end, slong c) { return m.find_pivot_any(start, end, c); } template inline typename mp::enable_if, slong>::type find_pivot_partial(const Mat& m, slong start, slong end, slong c) { return m.find_pivot_partial(start, end, c); } } // flint // Define rows(), cols(), create_temporary() and at() methods. // For this to work, Traits must behave like the above generic traits. // Also your matrix class must have a static create_temporary_rowscols function. // See fmpz_mat for an example. #define FLINTXX_DEFINE_MATRIX_METHODS(Traits) \ template \ typename Traits::template at::entry_ref_t at(T i, U j) \ {return Traits::template at::get(*this, i, j);} \ template \ typename Traits::template at::entry_srcref_t at(T i, U j) const \ {return Traits::template at::get(*this, i, j);} \ \ slong rows() const {return Traits::rows(*this);} \ slong cols() const {return Traits::cols(*this);} \ evaluated_t create_temporary() const \ { \ return create_temporary_rowscols(*this, rows(), cols()); \ } // Disable temporary merging. Requires create_temporary_rowscols. // TODO do we really need the ltuple code everywhere? #define FLINTXX_DEFINE_TEMPORARY_RULES(Matrix) \ namespace traits { \ template<> struct use_temporary_merging : mp::false_ { }; \ } /* traits */ \ namespace rules { \ template \ struct use_default_temporary_instantiation : mp::false_ { }; \ template \ struct instantiate_temporaries >::type> \ { \ /* The only case where this should ever happen is if Expr is an ltuple */ \ /* TODO static assert this */ \ static Matrix get(const Expr& e) \ { \ typedef typename Expr::operation_t op_t; \ return Matrix::create_temporary_rowscols(e, \ matrices::outsize::rows(e), \ matrices::outsize::cols(e)); \ } \ }; \ template \ struct instantiate_temporaries >::type> \ { \ static Matrix get(const Expr& e) \ { \ return e.create_temporary(); \ } \ }; \ } /* rules */ // Add a fflu() member function to the matrix class. #define FLINTXX_DEFINE_MEMBER_FFLU \ template typename detail::nary_op_helper2::enable::type \ fflu(permxx* p, const T& t) const \ { \ return flint::fflu(*this, p, t); \ } #endif