Kernel Quantum Probability Library
The KQP library aims at providing tools for working with quantums probabilities
factory.hpp
1 /*
2  This file is part of the Kernel Quantum Probability library (KQP).
3 
4  KQP is free software: you can redistribute it and/or modify
5  it under the terms of the GNU General Public License as published by
6  the Free Software Foundation, either version 3 of the License, or
7  (at your option) any later version.
8 
9  KQP is distributed in the hope that it will be useful,
10  but WITHOUT ANY WARRANTY; without even the implied warranty of
11  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12  GNU General Public License for more details.
13 
14  You should have received a copy of the GNU General Public License
15  along with KQP. If not, see <http://www.gnu.org/licenses/>.
16  */
17 
18 #ifndef __KQP_KERNEL_EVD_FACTORY_H__
19 #define __KQP_KERNEL_EVD_FACTORY_H__
20 
21 #include <kqp/cleanup.hpp>
22 
23 #include <kqp/kernel_evd/dense_direct.hpp>
24 #include <kqp/kernel_evd/accumulator.hpp>
25 #include <kqp/kernel_evd/incremental.hpp>
26 #include <kqp/kernel_evd/divide_and_conquer.hpp>
27 
28 #include <kqp/picojson.hpp>
29 
30 namespace kqp {
31  template<typename _Scalar> struct BuilderFactory;
32 
37  typedef boost::shared_ptr<BuilderFactoryBase> Ptr;
38 
39  virtual ~BuilderFactoryBase() {}
40 
42  virtual boost::shared_ptr< KernelEVDBase > getBuilder() = 0;
43 
44 #ifndef SWIG
45 
46  inline static Ptr getFactory(const boost::shared_ptr<AbstractSpace> &space, picojson::value &json);
47 
53  inline static boost::shared_ptr<CleanerBase> getCleaner(picojson::value &json, const std::string &context = "");
54 
55  virtual void configure(const std::string &context, picojson::object &json) {
56  (void)context; (void)json;
57  }
58 #endif
59  };
60 
61 #ifndef SWIG
62  template<typename Scalar> class IncrementalFactory;
63  template<typename Scalar> class DirectFactory;
64  template<typename Scalar> class DivideAndConquerFactory;
65  template<typename Scalar> class AccumulatorFactory;
66 
67  template<typename _Scalar>
69  typedef _Scalar Scalar;
70  KQP_SCALAR_TYPEDEFS(Scalar);
71 
72  FSpaceCPtr m_space;
73 
74  BuilderFactory() {}
75 
76  void setSpace(const FSpaceCPtr &space) {
77  this->m_space = space;
78  }
79 
80  virtual ~BuilderFactory() {}
81 
82  typedef boost::shared_ptr<Selector<Real>> SelectorPtr;
83  typedef boost::shared_ptr<Cleaner<Real>> CleanerPtr;
84 
85  virtual boost::shared_ptr<KernelEVDBase> getBuilder() = 0;
86 
87  static boost::shared_ptr<BuilderFactory<Scalar>> getFactory(const boost::shared_ptr<const AbstractSpace> &space, picojson::value &json, const std::string &context = "") {
88  if (json.is<picojson::null>())
89  KQP_THROW_EXCEPTION_F(kqp::illegal_argument_exception, "No builder defined [%s]", %context);
90  if (!json.is<picojson::object>())
91  KQP_THROW_EXCEPTION_F(kqp::illegal_argument_exception, "Builder is not a JSON object", %context);
92 
93  return BuilderFactory<Scalar>::getFactory(space, json.get<picojson::object>(), context);
94  }
95 
96  static boost::shared_ptr<BuilderFactory<Scalar>> getFactory(const boost::shared_ptr<const AbstractSpace> &space, picojson::object &json, const std::string &context = "") {
97 
98  std::string kevdName = get<std::string>(context, json, "name");
99 
100  typedef typename Eigen::NumTraits<Scalar>::Real Real;
101 
102  boost::shared_ptr<BuilderFactory<Scalar>> builderFactory;
103 
104  if (kevdName == "incremental")
105  builderFactory.reset(new IncrementalFactory<Scalar>());
106 
107  else if (kevdName == "direct")
108  builderFactory.reset(new DirectFactory<Scalar>());
109 
110  else if (kevdName == "accumulator")
111  builderFactory.reset(new AccumulatorFactory<Scalar>());
112 
113  else if (kevdName == "divide-and-conquer")
114  builderFactory.reset(new DivideAndConquerFactory<Scalar>());
115 
116  else KQP_THROW_EXCEPTION_F(illegal_argument_exception, "Unknown kernel-evd builder type [%s]", %kevdName);
117 
118  builderFactory->setSpace(our_dynamic_cast<const SpaceBase<Scalar>>(space));
119  builderFactory->configure(context, json);
120  return builderFactory;
121  }
122 
123 
124 
125  static SelectorPtr getSelector(const std::string &context, picojson::value &json) {
126  boost::shared_ptr<ChainSelector<Real>> chain(new ChainSelector<Real>());
127  if (json.is<picojson::null>())
128  return chain;
129  if (!json.is<picojson::array>())
130  KQP_THROW_EXCEPTION_F(kqp::illegal_argument_exception, "Error in JSON [%s]: should be an array", %context);
131 
132  picojson::array array = json.get<picojson::array>();
133  for(size_t i = 0; i < array.size(); i++) {
134  auto &jsonSel = array[i];
135  std::string contextSel = context + "[" + boost::lexical_cast<std::string>(i) + "]";
136  std::string name = get<std::string>(contextSel, jsonSel, "name");
137  if (name == "rank") {
138  Index maxRank = getNumeric<Index>(contextSel, jsonSel, "max");
139  Index resetRank = getNumeric<Index>(contextSel, jsonSel, "reset");
140  chain->add(SelectorPtr(new RankSelector<Real,true>(maxRank, resetRank)));
141  } else if (name == "ratio") {
142  typename RatioSelector<Real>::AggregatorPtr aggregator(new Mean<Real>());
143  Real ratio = (Real)get<double>(contextSel, jsonSel, "ratio", Eigen::NumTraits<Real>::epsilon());
144  chain->add(SelectorPtr(new RatioSelector<Real>(ratio, aggregator)));
145  } else KQP_THROW_EXCEPTION_F(kqp::illegal_argument_exception, "Unknown selector [%s] in [%s]", %name %context);
146  }
147 
148  return chain;
149  }
150 
151  static boost::shared_ptr<Cleaner<Scalar>> getCleaner(const std::string &context, picojson::value &json) override {
152  boost::shared_ptr<CleanerList<Real>> list(new CleanerList<Real>());
153  if (json.is<picojson::null>())
154  return list;
155 
156  if (!json.is<picojson::array>())
157  KQP_THROW_EXCEPTION_F(kqp::illegal_argument_exception, "Error in JSON [%s]: should be an array", %context);
158 
159  picojson::array array = json.get<picojson::array>();
160 
161  for(size_t i = 0; i < array.size(); i++) {
162  auto &jsonCleaner = array[i];
163  std::string contextCleaner = context + "[" + boost::lexical_cast<std::string>(i) + "]";
164  std::string name = get<std::string>(contextCleaner, jsonCleaner, "name");
165  if (name == "rank") {
166  auto rankSelector = getSelector(contextCleaner + ".selector", jsonCleaner.get<picojson::object>()["selector"]);
167  list->add(CleanerPtr(new CleanerRank<Scalar>(rankSelector)));
168  } else if (name == "unused") {
169  list->add(CleanerPtr(new CleanerUnused<Scalar>()));
170  } else if (name == "null") {
171  boost::shared_ptr<CleanerNullSpace<Scalar>> cleaner(new CleanerNullSpace<Scalar>());
172  cleaner->epsilon(get<double>(contextCleaner, jsonCleaner, "epsilon", Eigen::NumTraits<Real>::epsilon()));
173 
174  double maxRatio = get<double>(contextCleaner, jsonCleaner, "max-ratio", std::numeric_limits<Real>::infinity());
175  double resetRatio = get<double>(contextCleaner, jsonCleaner, "reset-ratio", 0);
176  cleaner->setPreImagesPerRank(resetRatio, maxRatio);
177 
178  Index maxRank = getNumeric<Index>(contextCleaner, jsonCleaner, "max-rank", -1);
179  if (maxRank < 0)
180  maxRank = std::numeric_limits<Index>::max();
181  Index resetRank = getNumeric<Index>(contextCleaner, jsonCleaner, "reset-rank", 0);
182  cleaner->setRankRange(resetRank, maxRank);
183 
184  list->add(cleaner);
185  } else if (name == "qp") {
186  boost::shared_ptr<CleanerQP<Real>> cleaner(new CleanerQP<Scalar>());
187 
188  double maxRatio = get<double>(contextCleaner, jsonCleaner, "max-ratio", std::numeric_limits<Real>::infinity());
189  double resetRatio = get<double>(contextCleaner, jsonCleaner, "reset-ratio", 0);
190  cleaner->setPreImagesPerRank(resetRatio, maxRatio);
191 
192  Index maxRank = getNumeric<Index>(contextCleaner, jsonCleaner, "max-rank", -1);
193  if (maxRank < 0)
194  maxRank = std::numeric_limits<Index>::max();
195  Index resetRank = getNumeric<Index>(contextCleaner, jsonCleaner, "reset-rank", 0);
196  cleaner->setRankRange(resetRank, maxRank);
197 
198  list->add(cleaner);
199  } else KQP_THROW_EXCEPTION_F(kqp::illegal_argument_exception, "Unknown cleaner [%s] in [%s]", %name %context);
200 
201  }
202 
203  return list;
204  }
205 
206  };
207 
208  inline typename BuilderFactoryBase::Ptr BuilderFactoryBase::getFactory(const boost::shared_ptr<AbstractSpace> &space, picojson::value &json) {
209  std::string scalarName = get<std::string>("", json, "scalar", "double");
210  if (scalarName == "double") return Ptr(BuilderFactory<double>::getFactory(space, json));
211  KQP_THROW_EXCEPTION_F(illegal_argument_exception, "Unknown scalar type [%s]", %scalarName);
212  }
213 
214  inline typename boost::shared_ptr<CleanerBase> BuilderFactoryBase::getCleaner(picojson::value &json, const std::string &context) {
215  std::string scalarName = get<std::string>("", json, "scalar", "double");
216  if (scalarName == "double") return boost::shared_ptr<CleanerBase>(BuilderFactory<double>::getCleaner(context, json.get<picojson::object>()["list"]));
217  KQP_THROW_EXCEPTION_F(illegal_argument_exception, "Unknown scalar type [%s]", %scalarName);
218  }
219 
220 
221 
222  template<typename Scalar>
223  class DirectFactory : public BuilderFactory<Scalar> {
224  public:
225  KQP_SCALAR_TYPEDEFS(Scalar);
226  virtual ~DirectFactory() {}
227  virtual boost::shared_ptr<KernelEVDBase> getBuilder() override {
228  return boost::shared_ptr<KernelEVDBase>(new DenseDirectBuilder<Scalar>(this->m_space->dimension()));
229  };
230  virtual std::string getName() const { return "direct"; }
231  };
232 
233  template<typename Scalar>
234  class AccumulatorFactory : public BuilderFactory<Scalar> {
235  public:
236  KQP_SCALAR_TYPEDEFS(Scalar);
237  virtual ~AccumulatorFactory() {}
238 
239  virtual boost::shared_ptr<KernelEVDBase> getBuilder() {
240  if (this->m_space->canLinearlyCombine())
241  return boost::shared_ptr<KernelEVD<Scalar>>(new AccumulatorKernelEVD<Scalar,true>(this->m_space));
242 
243  return boost::shared_ptr<KernelEVDBase>(new AccumulatorKernelEVD<Scalar,false>(this->m_space));
244  }
245 
246  virtual std::string getName() const {
247  return "accumulator";
248  }
249  };
250 
251 
252  template<typename Scalar>
253  class IncrementalFactory : public BuilderFactory<Scalar> {
254  public:
255  KQP_SCALAR_TYPEDEFS(Scalar);
256  boost::shared_ptr<Selector<Real>> m_selector;
257  float targetPreImageRatio, maxPreImageRatio;
258 
260  targetPreImageRatio(std::numeric_limits<float>::infinity()),
261  maxPreImageRatio(std::numeric_limits<float>::infinity())
262  {}
263 
264  virtual ~IncrementalFactory() {}
265 
266  virtual std::string getName() const { return "incremental"; }
267 
268 
269  virtual void configure(const std::string &context, picojson::object &json) override {
270  BuilderFactory<Scalar>::configure(context, json);
271  m_selector = this->getSelector(context + ".selector", json["selector"]);
272 
273  if (!this->m_space->canLinearlyCombine()) {
274  targetPreImageRatio = get<double>(context, json, "pre-images");
275  maxPreImageRatio = get<double>(context, json, "max-pre-images", targetPreImageRatio);
276  }
277  }
278 
279  virtual boost::shared_ptr<KernelEVDBase> getBuilder() {
280  IncrementalKernelEVD<Scalar> * builder = new IncrementalKernelEVD<Scalar>(this->m_space);
281  builder->setSelector(m_selector);
282  builder->setPreImagesPerRank(this->targetPreImageRatio, this->maxPreImageRatio);
283 
284  return boost::shared_ptr<KernelEVD<Scalar>>(builder);
285  }
286 
287  };
288 
289  template<typename Scalar> struct BuilderChooser;
290 
291 
293  template<typename Scalar>
294  class DivideAndConquerFactory : public BuilderFactory<Scalar> {
295  public:
296  KQP_SCALAR_TYPEDEFS(Scalar);
297  typedef boost::shared_ptr<KernelEVD<Scalar>> KEVDPtr;
298  typedef boost::shared_ptr<Cleaner<Real>> CleanerPtr;
299 
300  typedef boost::shared_ptr<BuilderFactory<Scalar>> BuilderPtr;
301  BuilderPtr builder, merger;
302  CleanerPtr builderCleaner, mergerCleaner;
303 
304  Index batchSize;
305 
306  virtual std::string getName() const { return "divide-and-conquer"; }
307 
308  DivideAndConquerFactory() : batchSize(100) {}
309  virtual ~DivideAndConquerFactory() {}
310 
311  virtual void configure(const std::string &context, picojson::object &json) override {
312  BuilderFactory<Scalar>::configure(context, json);
313 
314  batchSize = getNumeric<int>(context, json, "batch", batchSize);
315 
316  builder = BuilderFactory<Scalar>::getFactory(this->m_space, json["builder"], context + ".builder");
317  merger = BuilderFactory<Scalar>::getFactory(this->m_space, json["merger"], context + ".merger");
318 
319  builderCleaner = this->getCleaner(context + ".builder.cleaner", json["builder"].get<picojson::object>()["cleaner"]);
320  mergerCleaner = this->getCleaner(context + ".merger.cleaner", json["merger"].get<picojson::object>()["cleaner"]);
321  }
322 
323  virtual boost::shared_ptr<KernelEVDBase> getBuilder() override {
324  boost::shared_ptr<DivideAndConquerBuilder<Scalar>> dc(new DivideAndConquerBuilder<Scalar>(this->m_space));
325  dc->setBatchSize(batchSize);
326 
327 
328  dc->setBuilder(our_dynamic_cast<KernelEVD<Scalar>>(builder->getBuilder()));
329  dc->setBuilderCleaner(builderCleaner);
330 
331  dc->setMerger(our_dynamic_cast<KernelEVD<Scalar>>(merger->getBuilder()));
332  dc->setMergerCleaner(mergerCleaner);
333  return dc;
334  }
335  };
336 
337 #endif // SWIG
338 
339 }
340 
341 
342 #endif