1 /*
2  * Copyright (C) 1998-2018 ALPS Collaboration. See COPYRIGHT.TXT
3  * All rights reserved. Use is subject to license terms. See LICENSE.TXT
4  * For use in publications, see ACKNOWLEDGE.TXT
5  */
6 
7 #pragma once
8 
9 #include <alps/config.hpp>
10 
11 #include <alps/accumulators/wrappers.hpp>
12 #include <alps/accumulators/feature/weight.hpp>
13 
14 #include <alps/hdf5/archive.hpp>
15 #include <alps/utilities/stacktrace.hpp>
16 #include <alps/utilities/short_print.hpp>
17 
18 #include <boost/utility.hpp>
19 #include <boost/shared_ptr.hpp>
20 
21 #include <stdexcept>
22 #include <type_traits>
23 
24 namespace alps {
25     namespace accumulators {
26         // this should be called namespace tag { template<typename T> struct weight_holder; }
27         // but gcc <= 4.4 has lookup error, so name it different
28         template<typename T> struct weight_holder_tag;
29 
30         namespace impl {
31 
32             template<typename T, typename W, typename B> struct Accumulator<T, weight_holder_tag<W>, B> : public B {
33 
34                 public:
35                     typedef W weight_type;
36                     typedef Result<T, weight_holder_tag<W>, typename B::result_type> result_type;
37 
38                     // TODO: add external weight!
39 
Accumulatoralps::accumulators::impl::Accumulator40                     Accumulator(): B(), m_owner(true), m_weight(new ::alps::accumulators::derived_accumulator_wrapper<W>(W())) {}
41 
Accumulatoralps::accumulators::impl::Accumulator42                     Accumulator(Accumulator const & arg): B(arg), m_owner(arg.m_owner), m_weight(arg.m_weight) {}
43 
Accumulatoralps::accumulators::impl::Accumulator44                     template<typename ArgumentPack> Accumulator(ArgumentPack const & args, typename std::enable_if<!is_accumulator<ArgumentPack>::value, int>::type = 0)
45                         : B(args), m_owner(true), m_weight(new ::alps::accumulators::derived_accumulator_wrapper<W>(W()))
46                     {}
47 
weightalps::accumulators::impl::Accumulator48                     base_wrapper<T> const * weight() const {
49                         // TODO: make library for scalar type
50                         return m_weight.get();
51                     }
52 
operator ()alps::accumulators::impl::Accumulator53                     void operator()(T const & val) {
54                         // TODO: throw if weight is owned ...
55                         B::operator()(val);
56                     }
57 
58                     template<typename X> typename std::enable_if<std::conditional<
59                           std::is_scalar<typename value_type<weight_type>::type>::value
60                         , typename std::is_convertible<X, typename value_type<weight_type>::type>::type
61                         , typename std::is_same<X, typename value_type<weight_type>::type>::type
operator ()alps::accumulators::impl::Accumulator62                     >::value>::type operator()(T const & val, X const & weight) {
63                         // TODO: how do we make sure, weight is updated only once?
64                         B::operator()(val);
65                         (m_weight->template extract<W>())(weight);
66                     }
67 
68                     template<typename X> typename std::enable_if<!std::conditional<
69                           std::is_scalar<typename value_type<weight_type>::type>::value
70                         , typename std::is_convertible<X, typename value_type<weight_type>::type>::type
71                         , typename std::is_same<X, typename value_type<weight_type>::type>::type
operator ()alps::accumulators::impl::Accumulator72                     >::value>::type operator()(T const & /*val*/, X const & /*weight*/) {
73                         throw std::runtime_error("Invalid type for binary call operator" + ALPS_STACKTRACE);
74                     }
75 
printalps::accumulators::impl::Accumulator76                     template<typename S> void print(S & os, bool terse=false) const {
77                         B::print(os, terse);
78                         os << ", weight: ";
79                         m_weight->print(os, terse);
80                     }
81 
savealps::accumulators::impl::Accumulator82                     void save(hdf5::archive & ar) const {
83                         B::save(ar);
84                         ar["weight/value"] = *weight();
85                     }
86 
loadalps::accumulators::impl::Accumulator87                     void load(hdf5::archive & ar) { // TODO: make archive const
88                         B::load(ar);
89                         ar["weight/value"] >> *m_weight;
90                     }
91 
rankalps::accumulators::impl::Accumulator92                     static std::size_t rank() { return B::rank() + 1; }
can_loadalps::accumulators::impl::Accumulator93                     static bool can_load(hdf5::archive & ar) { // TODO: make archive const
94                         using alps::hdf5::get_extent;
95 
96                         ar.set_context("weight/value");
97                         bool is = weight_type::can_load(ar);
98                         ar.set_context("../..");
99 
100                         return is && B::can_load(ar);
101                     }
102 
resetalps::accumulators::impl::Accumulator103                     void reset() {
104                         B::reset();
105                         m_weight->reset();
106                     }
107 
108                    /// Merge placeholder \remark FIXME: always throws
109                     template <typename A>
mergealps::accumulators::impl::Accumulator110                     void merge(const A& /*rhs*/)
111                     {
112                       throw std::logic_error("Merging weight_holder accumulators is not yet implemented"
113                                              +ALPS_STACKTRACE);
114                     }
115 
116 #ifdef ALPS_HAVE_MPI
collective_mergealps::accumulators::impl::Accumulator117                     void collective_merge(
118                           alps::mpi::communicator const & comm
119                         , int root
120                     ) {
121                         B::collective_merge(comm, root);
122                         m_weight->collective_merge(comm, root);
123                     }
124 
collective_mergealps::accumulators::impl::Accumulator125                     void collective_merge(
126                           alps::mpi::communicator const & comm
127                         , int root
128                     ) const {
129                         B::collective_merge(comm, root);
130                         m_weight->collective_merge(comm, root);
131                     }
132 #endif
133 
owns_weightalps::accumulators::impl::Accumulator134                     bool owns_weight() const {
135                         return m_owner;
136                     }
137 
138                 private:
139                     bool m_owner;
140                     boost::shared_ptr< ::alps::accumulators::base_wrapper<typename value_type<weight_type>::type> > m_weight;
141             };
142 
143             template<typename T, typename W, typename B> class Result<T, weight_holder_tag<W>, B> : public B {
144 
145                 public:
146                     typedef W weight_type;
147                     typedef typename detail::make_scalar_result_type<impl::Result,T,weight_holder_tag<W>,B>::type scalar_result_type;
148 
Result()149                     Result()
150                         : B()
151                         , m_owner(true)
152                         , m_weight(new ::alps::accumulators::derived_result_wrapper<W>(W()))
153                     {}
154 
Result(A const & acc)155                     template<typename A> Result(A const & acc)
156                         : B(acc)
157                         , m_owner(acc.owns_weight())
158                         // TODO: implement shared weight
159                         , m_weight(acc.weight()->result())
160                     {}
161 
weight() const162                     base_wrapper<typename value_type<weight_type>::type> const * weight() const {
163                         return m_weight.get();
164                     }
165 
print(S & os,bool terse=false) const166                     template<typename S> void print(S & os, bool terse=false) const {
167                         B::print(os, terse);
168                         os << ", weight: ";
169                         m_weight->print(os, terse);
170                     }
171 
save(hdf5::archive & ar) const172                     void save(hdf5::archive & ar) const {
173                         B::save(ar);
174                         ar["weight/value"] = *weight();
175                     }
176 
load(hdf5::archive & ar)177                     void load(hdf5::archive & ar) {
178                         B::load(ar);
179                         ar["weight/value"] >> *m_weight;
180                     }
181 
rank()182                     static std::size_t rank() { return B::rank() + 1; }
can_load(hdf5::archive & ar)183                     static bool can_load(hdf5::archive & ar) { // TODO: make archive const
184                         using alps::hdf5::get_extent;
185 
186                         ar.set_context("weight/value");
187                         bool is = weight_type::can_load(ar);
188                         ar.set_context("../..");
189 
190                         return is && B::can_load(ar);
191                     }
192 
193                 protected:
194                     bool m_owner;
195                     boost::shared_ptr< ::alps::accumulators::base_wrapper<typename value_type<weight_type>::type> > m_weight;
196             };
197 
198         }
199     }
200 }
201