/******************************************************************************* * * TRIQS: a Toolbox for Research in Interacting Quantum Systems * * Copyright (C) 2011 by O. Parcollet * * TRIQS is free software: you can redistribute it and/or modify it under the * terms of the GNU General Public License as published by the Free Software * Foundation, either version 3 of the License, or (at your option) any later * version. * * TRIQS is distributed in the hope that it will be useful, but WITHOUT ANY * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more * details. * * You should have received a copy of the GNU General Public License along with * TRIQS. If not, see . * ******************************************************************************/ #ifndef TRIQS_ARRAYS_LINALG_DET_INV_H #define TRIQS_ARRAYS_LINALG_DET_INV_H #include #include #include #include "../impl/common.hpp" #include "../matrix.hpp" #include "../blas_lapack/getrf.hpp" #include "../blas_lapack/getri.hpp" namespace triqs { namespace arrays { /// Error which occurs during the matrix inversion class matrix_inverse_exception : public triqs::runtime_error {}; /** * Lazy result of inverse(M) where M can be : * * a matrix, a matrix_view * * any matrix expression * The object is lazy, it does not do any computation. * It can be copied at no cost * It keeps view of the object A if it a matrix, a copy if it is a formal expression. */ template struct inverse_lazy; /// template struct determinant_lazy; /// Lazy inversion template inverse_lazy inverse (A const & a) { return inverse_lazy(a); } /// Lazy computation of det template determinant_lazy determinant (A const & a) { return determinant_lazy(a); } // ----------------- implementation ----------------------------------------- //worker takes a contiguous view and compute the det and inverse in two steps. //it is separated in case of multiple use (no reallocation of ipvi, etc...) //NB a view does not resize, only its elements can be changed template class det_and_inverse_worker { static_assert ( (is_matrix_view::value),"class must have be a view"); typedef typename ViewType::value_type VT; typedef matrix_view V_type; ViewType V; const size_t dim; triqs::arrays::vector ipiv; short step; public: det_and_inverse_worker (ViewType const & a): V(a), dim(first_dim(a)), ipiv(dim), step(0) { if (first_dim(a)!=second_dim(a)) TRIQS_RUNTIME_ERROR<<"Inverse/Det error : non-square matrix. Dimensions are : ("< V_type fortran_view (MT const &x) { return (x.indexmap().memory_layout_is_c() ? x.transpose() : x);} void _step1(V_type & W) { if (step >0) return; step=1; info = lapack::getrf(W, ipiv); if (info<0) throw matrix_inverse_exception() << "Inverse/Det error : failure of getrf lapack routine "; } void _compute_det(V_type const & W) { if (step>1) return; _det =1; for (size_t i =0; i struct inverse_lazy_impl : TRIQS_CONCEPT_TAG_NAME(ImmutableMatrix) { typedef typename std::remove_const::type value_type; typedef typename A::domain_type domain_type; typedef typename const_view_type_if_exists_else_type::type A_type; const A_type a; inverse_lazy_impl(A const & a_):a (a_) { if (first_dim(a) != second_dim(a)) TRIQS_RUNTIME_ERROR<< "Inverse : matrix is not square but of size "<< first_dim(a)<<" x "<< second_dim(a); } //typename A::shape_type shape() const { return a.shape();} domain_type domain() const { return a.domain(); } template value_type operator() (K0 const & k0, K1 const & k1) const { activate(); return _id->M(k0,k1); } friend std::ostream & operator<<(std::ostream & out,inverse_lazy_impl const&x){return out<<"inverse("< M_type; typedef matrix_view M_view_type; M_type M; internal_data(inverse_lazy_impl const & P):M(P.a){det_and_inverse_worker worker(M); worker.inverse();} }; friend struct internal_data; mutable std::shared_ptr _id; void activate() const { if (!_id) _id= std::make_shared(*this);} }; // The general case template struct inverse_lazy >::type > : inverse_lazy_impl { inverse_lazy(A const & a):inverse_lazy_impl(a) { } }; // for matrix and matrix_views, we have more optimisation possible .... template struct inverse_lazy >::type >:inverse_lazy_impl{ inverse_lazy(A const & a):inverse_lazy_impl(a) { } template // Optimized implementation of = friend void triqs_arrays_assign_delegation (MT & lhs, inverse_lazy const & rhs) { static_assert(is_matrix_or_view::value, "Internal error"); if ((lhs.indexmap().memory_indices_layout() !=rhs.a.indexmap().memory_indices_layout())|| (lhs.data_start() != rhs.a.data_start()) || !(has_contiguous_data(lhs))) { rhs.activate(); lhs = rhs._id->M;} else {// if M = inverse(M) with the SAME object, then we do not need to copy the data blas_lapack_tools::reflexive_qcache C(lhs);// a reflexive cache will use a temporary "regrouping" copy if and only if needed det_and_inverse_worker W(C());// the worker to make the inversion of the lhs... W.inverse(); // worker is working ... } } friend std::ostream & operator<<(std::ostream & out,inverse_lazy const&x){return out<<"inverse("< struct determinant_lazy { // : { Tag::expression_terminal, Tag::scalar_expression_terminal { typedef typename A::value_type value_type; typedef typename const_view_type_if_exists_else_type::type A_type; A_type a; determinant_lazy(A const & a_):a(a_){} operator value_type() { activate(); return _id->det; } value_type const & operator()() { activate(); return _id->det; } friend std::ostream & operator<<(std::ostream & out, determinant_lazy const & x){ return out<<"determinant("< worker(M); det = worker.det();} }; friend struct internal_data; mutable std::shared_ptr _id; void activate() const { if (!_id) _id= std::make_shared(*this);} }; }} // namespace triqs::arrays #endif