/*
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