mlpack
ns_model_impl.hpp
Go to the documentation of this file.
1 
15 #ifndef MLPACK_METHODS_NEIGHBOR_SEARCH_NS_MODEL_IMPL_HPP
16 #define MLPACK_METHODS_NEIGHBOR_SEARCH_NS_MODEL_IMPL_HPP
17 
18 // In case it hasn't been included yet.
19 #include "ns_model.hpp"
20 
21 namespace mlpack {
22 namespace neighbor {
23 
26 template<typename SortPolicy,
27  template<typename TreeMetricType,
28  typename TreeStatType,
29  typename TreeMatType> class TreeType,
30  template<typename RuleType> class DualTreeTraversalType,
31  template<typename RuleType> class SingleTreeTraversalType>
32 void NSWrapper<
33  SortPolicy, TreeType, DualTreeTraversalType, SingleTreeTraversalType
34 >::Train(arma::mat&& referenceSet,
35  const size_t /* leafSize */,
36  const double /* tau */,
37  const double /* rho */)
38 {
39  ns.Train(std::move(referenceSet));
40 }
41 
44 template<typename SortPolicy,
45  template<typename TreeMetricType,
46  typename TreeStatType,
47  typename TreeMatType> class TreeType,
48  template<typename RuleType> class DualTreeTraversalType,
49  template<typename RuleType> class SingleTreeTraversalType>
50 void NSWrapper<
51  SortPolicy, TreeType, DualTreeTraversalType, SingleTreeTraversalType
52 >::Search(arma::mat&& querySet,
53  const size_t k,
54  arma::Mat<size_t>& neighbors,
55  arma::mat& distances,
56  const size_t /* leafSize */,
57  const double /* rho */)
58 {
59  ns.Search(std::move(querySet), k, neighbors, distances);
60 }
61 
64 template<typename SortPolicy,
65  template<typename TreeMetricType,
66  typename TreeStatType,
67  typename TreeMatType> class TreeType,
68  template<typename RuleType> class DualTreeTraversalType,
69  template<typename RuleType> class SingleTreeTraversalType>
70 void NSWrapper<
71  SortPolicy, TreeType, DualTreeTraversalType, SingleTreeTraversalType
72 >::Search(const size_t k,
73  arma::Mat<size_t>& neighbors,
74  arma::mat& distances)
75 {
76  ns.Search(k, neighbors, distances);
77 }
78 
81 template<typename SortPolicy,
82  template<typename TreeMetricType,
83  typename TreeStatType,
84  typename TreeMatType> class TreeType,
85  template<typename RuleType> class DualTreeTraversalType,
86  template<typename RuleType> class SingleTreeTraversalType>
87 void LeafSizeNSWrapper<
88  SortPolicy, TreeType, DualTreeTraversalType, SingleTreeTraversalType
89 >::Train(arma::mat&& referenceSet,
90  const size_t leafSize,
91  const double /* tau */,
92  const double /* rho */)
93 {
94  if (ns.SearchMode() == NAIVE_MODE)
95  {
96  ns.Train(std::move(referenceSet));
97  }
98  else
99  {
100  // Build the tree with the specified leaf size.
101  std::vector<size_t> oldFromNewReferences;
102  typename decltype(ns)::Tree referenceTree(std::move(referenceSet),
103  oldFromNewReferences, leafSize);
104  ns.Train(std::move(referenceTree));
105  ns.oldFromNewReferences = std::move(oldFromNewReferences);
106  }
107 }
108 
111 template<typename SortPolicy,
112  template<typename TreeMetricType,
113  typename TreeStatType,
114  typename TreeMatType> class TreeType,
115  template<typename RuleType> class DualTreeTraversalType,
116  template<typename RuleType> class SingleTreeTraversalType>
117 void LeafSizeNSWrapper<
118  SortPolicy, TreeType, DualTreeTraversalType, SingleTreeTraversalType
119 >::Search(arma::mat&& querySet,
120  const size_t k,
121  arma::Mat<size_t>& neighbors,
122  arma::mat& distances,
123  const size_t leafSize,
124  const double /* rho */)
125 {
126  if (ns.SearchMode() == DUAL_TREE_MODE)
127  {
128  // We actually have to do the mapping of query points ourselves, since the
129  // NeighborSearch class does not provide a way for us to specify the leaf
130  // size when building the query tree. (Therefore we must also build the
131  // query tree manually.)
132  std::vector<size_t> oldFromNewQueries;
133  typename decltype(ns)::Tree queryTree(std::move(querySet),
134  oldFromNewQueries, leafSize);
135 
136  arma::Mat<size_t> neighborsOut;
137  arma::mat distancesOut;
138  ns.Search(queryTree, k, neighborsOut, distancesOut);
139 
140  // Unmap the query points.
141  distances.set_size(distancesOut.n_rows, distancesOut.n_cols);
142  neighbors.set_size(neighborsOut.n_rows, neighborsOut.n_cols);
143  for (size_t i = 0; i < neighborsOut.n_cols; ++i)
144  {
145  neighbors.col(oldFromNewQueries[i]) = neighborsOut.col(i);
146  distances.col(oldFromNewQueries[i]) = distancesOut.col(i);
147  }
148  }
149  else
150  {
151  ns.Search(querySet, k, neighbors, distances);
152  }
153 }
154 
156 template<typename SortPolicy>
157 void SpillNSWrapper<SortPolicy>::Train(arma::mat&& referenceSet,
158  const size_t leafSize,
159  const double tau,
160  const double rho)
161 {
162  typename decltype(ns)::Tree tree(std::move(referenceSet), tau, leafSize,
163  rho);
164  ns.Train(std::move(tree));
165 }
166 
169 template<typename SortPolicy>
170 void SpillNSWrapper<SortPolicy>::Search(arma::mat&& querySet,
171  const size_t k,
172  arma::Mat<size_t>& neighbors,
173  arma::mat& distances,
174  const size_t leafSize,
175  const double rho)
176 {
177  if (ns.SearchMode() == DUAL_TREE_MODE)
178  {
179  // For Dual Tree Search on SpillTrees, the queryTree must be built with
180  // non overlapping (tau = 0).
181  typename decltype(ns)::Tree queryTree(std::move(querySet), 0 /* tau */,
182  leafSize, rho);
183  ns.Search(queryTree, k, neighbors, distances);
184  }
185  else
186  {
187  ns.Search(querySet, k, neighbors, distances);
188  }
189 }
190 
195 template<typename SortPolicy>
196 NSModel<SortPolicy>::NSModel(TreeTypes treeType, bool randomBasis) :
197  treeType(treeType),
198  randomBasis(randomBasis),
199  leafSize(20),
200  tau(0.0),
201  rho(0.7),
202  nSearch(NULL)
203 {
204  // Nothing to do.
205 }
206 
207 template<typename SortPolicy>
209  treeType(other.treeType),
210  randomBasis(other.randomBasis),
211  q(other.q),
212  leafSize(other.leafSize),
213  tau(other.tau),
214  rho(other.rho),
215  nSearch(other.nSearch->Clone())
216 {
217  // Nothing to do.
218 }
219 
220 template<typename SortPolicy>
222  treeType(other.treeType),
223  randomBasis(other.randomBasis),
224  q(std::move(other.q)),
225  leafSize(other.leafSize),
226  tau(other.tau),
227  rho(other.rho),
228  nSearch(other.nSearch)
229 {
230  // Reset parameters of the other model.
231  other.treeType = TreeTypes::KD_TREE;
232  other.randomBasis = false;
233  other.leafSize = 20;
234  other.tau = 0.0;
235  other.rho = 0.7;
236  other.nSearch = NULL;
237 }
238 
239 template<typename SortPolicy>
241 {
242  if (this != &other)
243  {
244  delete nSearch;
245 
246  treeType = other.treeType;
247  randomBasis = other.randomBasis;
248  q = other.q;
249  leafSize = other.leafSize;
250  tau = other.tau;
251  rho = other.rho;
252  nSearch = other.nSearch->Clone();
253  }
254 
255  return *this;
256 }
257 
258 template<typename SortPolicy>
260 {
261  if (this != &other)
262  {
263  delete nSearch;
264 
265  treeType = other.treeType;
266  randomBasis = other.randomBasis;
267  q = std::move(other.q);
268  leafSize = other.leafSize;
269  tau = other.tau;
270  rho = other.rho;
271  nSearch = other.nSearch;
272 
273  // Reset parameters of the other model.
274  other.treeType = TreeTypes::KD_TREE;
275  other.randomBasis = false;
276  other.leafSize = 20;
277  other.tau = 0.0;
278  other.rho = 0.7;
279  other.nSearch = NULL;
280  }
281 
282  return *this;
283 }
284 
286 template<typename SortPolicy>
288 {
289  delete nSearch;
290 }
291 
293 template<typename SortPolicy>
294 template<typename Archive>
295 void NSModel<SortPolicy>::serialize(Archive& ar, const uint32_t /* version */)
296 {
297  ar(CEREAL_NVP(treeType));
298  ar(CEREAL_NVP(randomBasis));
299  ar(CEREAL_NVP(q));
300  ar(CEREAL_NVP(leafSize));
301  ar(CEREAL_NVP(tau));
302  ar(CEREAL_NVP(rho));
303 
304  // This should never happen, but just in case, be clean with memory.
305  if (cereal::is_loading<Archive>())
306  InitializeModel(DUAL_TREE_MODE, 0.0); // Values will be overwritten.
307 
308  // Avoid polymorphic serialization by explicitly serializing the correct type.
309  switch (treeType)
310  {
311  case KD_TREE:
312  {
314  dynamic_cast<LeafSizeNSWrapper<SortPolicy,
315  tree::KDTree>&>(*nSearch);
316  ar(CEREAL_NVP(typedSearch));
317  break;
318  }
319  case COVER_TREE:
320  {
322  dynamic_cast<NSWrapper<SortPolicy,
323  tree::StandardCoverTree>&>(*nSearch);
324  ar(CEREAL_NVP(typedSearch));
325  break;
326  }
327  case R_TREE:
328  {
330  dynamic_cast<NSWrapper<SortPolicy, tree::RTree>&>(*nSearch);
331  ar(CEREAL_NVP(typedSearch));
332  break;
333  }
334  case R_STAR_TREE:
335  {
337  dynamic_cast<NSWrapper<SortPolicy, tree::RStarTree>&>(*nSearch);
338  ar(CEREAL_NVP(typedSearch));
339  break;
340  }
341  case BALL_TREE:
342  {
344  dynamic_cast<LeafSizeNSWrapper<SortPolicy,
345  tree::BallTree>&>(*nSearch);
346  ar(CEREAL_NVP(typedSearch));
347  break;
348  }
349  case X_TREE:
350  {
352  dynamic_cast<NSWrapper<SortPolicy, tree::XTree>&>(*nSearch);
353  ar(CEREAL_NVP(typedSearch));
354  break;
355  }
356  case HILBERT_R_TREE:
357  {
359  dynamic_cast<NSWrapper<SortPolicy, tree::HilbertRTree>&>(*nSearch);
360  ar(CEREAL_NVP(typedSearch));
361  break;
362  }
363  case R_PLUS_TREE:
364  {
366  dynamic_cast<NSWrapper<SortPolicy, tree::RPlusTree>&>(*nSearch);
367  ar(CEREAL_NVP(typedSearch));
368  break;
369  }
370  case R_PLUS_PLUS_TREE:
371  {
373  dynamic_cast<NSWrapper<SortPolicy, tree::RPlusPlusTree>&>(*nSearch);
374  ar(CEREAL_NVP(typedSearch));
375  break;
376  }
377  case SPILL_TREE:
378  {
379  SpillNSWrapper<SortPolicy>& typedSearch =
380  dynamic_cast<SpillNSWrapper<SortPolicy>&>(*nSearch);
381  ar(CEREAL_NVP(typedSearch));
382  break;
383  }
384  case VP_TREE:
385  {
387  dynamic_cast<NSWrapper<SortPolicy, tree::VPTree>&>(*nSearch);
388  ar(CEREAL_NVP(typedSearch));
389  break;
390  }
391  case RP_TREE:
392  {
394  dynamic_cast<NSWrapper<SortPolicy, tree::RPTree>&>(*nSearch);
395  ar(CEREAL_NVP(typedSearch));
396  break;
397  }
398  case MAX_RP_TREE:
399  {
401  dynamic_cast<NSWrapper<SortPolicy, tree::MaxRPTree>&>(*nSearch);
402  ar(CEREAL_NVP(typedSearch));
403  break;
404  }
405  case UB_TREE:
406  {
408  dynamic_cast<NSWrapper<SortPolicy, tree::UBTree>&>(*nSearch);
409  ar(CEREAL_NVP(typedSearch));
410  break;
411  }
412  case OCTREE:
413  {
415  dynamic_cast<LeafSizeNSWrapper<SortPolicy,
416  tree::Octree>&>(*nSearch);
417  ar(CEREAL_NVP(typedSearch));
418  break;
419  }
420  }
421 }
422 
424 template<typename SortPolicy>
425 const arma::mat& NSModel<SortPolicy>::Dataset() const
426 {
427  return nSearch->Dataset();
428 }
429 
431 template<typename SortPolicy>
433 {
434  return nSearch->SearchMode();
435 }
436 
438 template<typename SortPolicy>
440 {
441  return nSearch->SearchMode();
442 }
443 
444 template<typename SortPolicy>
446 {
447  return nSearch->Epsilon();
448 }
449 
450 template<typename SortPolicy>
452 {
453  return nSearch->Epsilon();
454 }
455 
457 template<typename SortPolicy>
459  const double epsilon)
460 {
461  // Clear existing memory.
462  if (nSearch)
463  delete nSearch;
464 
465  switch (treeType)
466  {
467  case KD_TREE:
468  nSearch = new LeafSizeNSWrapper<SortPolicy, tree::KDTree>(searchMode,
469  epsilon);
470  break;
471  case COVER_TREE:
472  nSearch = new NSWrapper<SortPolicy, tree::StandardCoverTree>(searchMode,
473  epsilon);
474  break;
475  case R_TREE:
476  nSearch = new NSWrapper<SortPolicy, tree::RTree>(searchMode, epsilon);
477  break;
478  case R_STAR_TREE:
479  nSearch = new NSWrapper<SortPolicy, tree::RStarTree>(searchMode, epsilon);
480  break;
481  case BALL_TREE:
482  nSearch = new LeafSizeNSWrapper<SortPolicy, tree::BallTree>(searchMode,
483  epsilon);
484  break;
485  case X_TREE:
486  nSearch = new NSWrapper<SortPolicy, tree::XTree>(searchMode, epsilon);
487  break;
488  case HILBERT_R_TREE:
489  nSearch = new NSWrapper<SortPolicy, tree::HilbertRTree>(searchMode,
490  epsilon);
491  break;
492  case R_PLUS_TREE:
493  nSearch = new NSWrapper<SortPolicy, tree::RPlusTree>(searchMode, epsilon);
494  break;
495  case R_PLUS_PLUS_TREE:
496  nSearch = new NSWrapper<SortPolicy, tree::RPlusPlusTree>(searchMode,
497  epsilon);
498  break;
499  case VP_TREE:
500  nSearch = new NSWrapper<SortPolicy, tree::VPTree>(searchMode, epsilon);
501  break;
502  case RP_TREE:
503  nSearch = new NSWrapper<SortPolicy, tree::RPTree>(searchMode, epsilon);
504  break;
505  case MAX_RP_TREE:
506  nSearch = new NSWrapper<SortPolicy, tree::MaxRPTree>(searchMode, epsilon);
507  break;
508  case SPILL_TREE:
509  nSearch = new SpillNSWrapper<SortPolicy>(searchMode, epsilon);
510  break;
511  case UB_TREE:
512  nSearch = new NSWrapper<SortPolicy, tree::UBTree>(searchMode, epsilon);
513  break;
514  case OCTREE:
515  nSearch = new LeafSizeNSWrapper<SortPolicy, tree::Octree>(searchMode,
516  epsilon);
517  break;
518  }
519 }
520 
522 template<typename SortPolicy>
523 void NSModel<SortPolicy>::BuildModel(arma::mat&& referenceSet,
524  const NeighborSearchMode searchMode,
525  const double epsilon)
526 {
527  // Initialize random basis if necessary.
528  if (randomBasis)
529  {
530  Log::Info << "Creating random basis..." << std::endl;
531  while (true)
532  {
533  // [Q, R] = qr(randn(d, d));
534  // Q = Q * diag(sign(diag(R)));
535  arma::mat r;
536  if (arma::qr(q, r, arma::randn<arma::mat>(referenceSet.n_rows,
537  referenceSet.n_rows)))
538  {
539  arma::vec rDiag(r.n_rows);
540  for (size_t i = 0; i < rDiag.n_elem; ++i)
541  {
542  if (r(i, i) < 0)
543  rDiag(i) = -1;
544  else if (r(i, i) > 0)
545  rDiag(i) = 1;
546  else
547  rDiag(i) = 0;
548  }
549 
550  q *= arma::diagmat(rDiag);
551 
552  // Check if the determinant is positive.
553  if (arma::det(q) >= 0)
554  break;
555  }
556  }
557  }
558 
559  // Do we need to modify the reference set?
560  if (randomBasis)
561  referenceSet = q * referenceSet;
562 
563  if (searchMode != NAIVE_MODE)
564  {
565  Timer::Start("tree_building");
566  Log::Info << "Building reference tree..." << std::endl;
567  }
568 
569  InitializeModel(searchMode, epsilon);
570  nSearch->Train(std::move(referenceSet), leafSize, tau, rho);
571 
572  if (searchMode != NAIVE_MODE)
573  {
574  Timer::Stop("tree_building");
575  Log::Info << "Tree built." << std::endl;
576  }
577 }
578 
580 template<typename SortPolicy>
581 void NSModel<SortPolicy>::Search(arma::mat&& querySet,
582  const size_t k,
583  arma::Mat<size_t>& neighbors,
584  arma::mat& distances)
585 {
586  // We may need to map the query set randomly.
587  if (randomBasis)
588  querySet = q * querySet;
589 
590  Log::Info << "Searching for " << k << " neighbors with ";
591 
592  switch (SearchMode())
593  {
594  case NAIVE_MODE:
595  Log::Info << "brute-force (naive) search..." << std::endl;
596  break;
597  case SINGLE_TREE_MODE:
598  Log::Info << "single-tree " << TreeName() << " search..." << std::endl;
599  break;
600  case DUAL_TREE_MODE:
601  Log::Info << "dual-tree " << TreeName() << " search..." << std::endl;
602  break;
603  case GREEDY_SINGLE_TREE_MODE:
604  Log::Info << "greedy single-tree " << TreeName() << " search..."
605  << std::endl;
606  break;
607  }
608 
609  nSearch->Search(std::move(querySet), k, neighbors, distances, leafSize, rho);
610 }
611 
613 template<typename SortPolicy>
614 void NSModel<SortPolicy>::Search(const size_t k,
615  arma::Mat<size_t>& neighbors,
616  arma::mat& distances)
617 {
618  Log::Info << "Searching for " << k << " neighbors with ";
619 
620  switch (SearchMode())
621  {
622  case NAIVE_MODE:
623  Log::Info << "brute-force (naive) search..." << std::endl;
624  break;
625  case SINGLE_TREE_MODE:
626  Log::Info << "single-tree " << TreeName() << " search..." << std::endl;
627  break;
628  case DUAL_TREE_MODE:
629  Log::Info << "dual-tree " << TreeName() << " search..." << std::endl;
630  break;
631  case GREEDY_SINGLE_TREE_MODE:
632  Log::Info << "greedy single-tree " << TreeName() << " search..."
633  << std::endl;
634  break;
635  }
636 
637  if (Epsilon() != 0 && SearchMode() != NAIVE_MODE)
638  Log::Info << "Maximum of " << Epsilon() * 100 << "% relative error."
639  << std::endl;
640 
641  nSearch->Search(k, neighbors, distances);
642 }
643 
645 template<typename SortPolicy>
646 std::string NSModel<SortPolicy>::TreeName() const
647 {
648  switch (treeType)
649  {
650  case KD_TREE:
651  return "kd-tree";
652  case COVER_TREE:
653  return "cover tree";
654  case R_TREE:
655  return "R tree";
656  case R_STAR_TREE:
657  return "R* tree";
658  case BALL_TREE:
659  return "ball tree";
660  case X_TREE:
661  return "X tree";
662  case HILBERT_R_TREE:
663  return "Hilbert R tree";
664  case R_PLUS_TREE:
665  return "R+ tree";
666  case R_PLUS_PLUS_TREE:
667  return "R++ tree";
668  case SPILL_TREE:
669  return "Spill tree";
670  case VP_TREE:
671  return "vantage point tree";
672  case RP_TREE:
673  return "random projection tree (mean split)";
674  case MAX_RP_TREE:
675  return "random projection tree (max split)";
676  case UB_TREE:
677  return "UB tree";
678  case OCTREE:
679  return "octree";
680  default:
681  return "unknown tree";
682  }
683 }
684 
685 } // namespace neighbor
686 } // namespace mlpack
687 
688 #endif
static void Start(const std::string &name)
Start the given timer.
Definition: timers.cpp:28
Linear algebra utility functions, generally performed on matrices or vectors.
Definition: cv.hpp:1
NeighborSearchMode SearchMode() const
Expose SearchMode.
Definition: ns_model_impl.hpp:432
NSModel(TreeTypes treeType=TreeTypes::KD_TREE, bool randomBasis=false)
Initialize the NSModel with the given type and whether or not a random basis should be used...
Definition: ns_model_impl.hpp:196
NSWrapper is a wrapper class for most NeighborSearch types.
Definition: ns_model.hpp:99
virtual void Search(arma::mat &&querySet, const size_t k, arma::Mat< size_t > &neighbors, arma::mat &distances, const size_t leafSize, const double rho)=0
Perform bichromatic neighbor search (i.e.
TreeTypes
Enum type to identify each accepted tree type.
Definition: ns_model.hpp:337
LeafSizeNSWrapper wraps any NeighborSearch types that take a leaf size for tree construction.
Definition: neighbor_search.hpp:40
Definition: pointer_wrapper.hpp:23
virtual const arma::mat & Dataset() const =0
Return a reference to the dataset.
void BuildModel(arma::mat &&referenceSet, const NeighborSearchMode searchMode, const double epsilon=0)
Build the reference tree.
Definition: ns_model_impl.hpp:523
A binary space partitioning tree, such as a KD-tree or a ball tree.
Definition: binary_space_tree.hpp:54
double Epsilon() const
Expose Epsilon.
Definition: ns_model_impl.hpp:445
const arma::mat & Dataset() const
Expose the dataset.
Definition: ns_model_impl.hpp:425
virtual void Train(arma::mat &&referenceSet, const size_t leafSize, const double tau, const double rho)=0
Train the NeighborSearch model with the given parameters.
The NSModel class provides an easy way to serialize a model, abstracts away the different types of tr...
Definition: ns_model.hpp:333
void InitializeModel(const NeighborSearchMode searchMode, const double epsilon)
Initialize the model type. (This does not perform any training.)
Definition: ns_model_impl.hpp:458
Definition: hmm_train_main.cpp:300
virtual void Train(arma::mat &&referenceSet, const size_t leafSize, const double tau, const double rho)
Train the model using the given parameters.
Definition: ns_model_impl.hpp:157
void serialize(Archive &ar, const uint32_t)
Serialize the neighbor search model.
Definition: ns_model_impl.hpp:295
static void Stop(const std::string &name)
Stop the given timer.
Definition: timers.cpp:36
NeighborSearchMode
NeighborSearchMode represents the different neighbor search modes available.
Definition: neighbor_search.hpp:43
virtual double Epsilon() const =0
Get the approximation parameter epsilon.
virtual void Search(arma::mat &&querySet, const size_t k, arma::Mat< size_t > &neighbors, arma::mat &distances, const size_t leafSize, const double rho)
Perform bichromatic search (i.e.
Definition: ns_model_impl.hpp:170
static MLPACK_EXPORT util::PrefixedOutStream Info
Prints informational messages if –verbose is specified, prefixed with [INFO ].
Definition: log.hpp:84
Definition: octree.hpp:25
The SpillNSWrapper class wraps the NeighborSearch class when the spill tree is used.
Definition: ns_model.hpp:253
virtual NeighborSearchMode SearchMode() const =0
Get the search mode.
NSModel & operator=(const NSModel &other)
Copy the given NSModel.
Definition: ns_model_impl.hpp:240
std::string TreeName() const
Return a string representation of the current tree type.
Definition: ns_model_impl.hpp:646
void Search(arma::mat &&querySet, const size_t k, arma::Mat< size_t > &neighbors, arma::mat &distances)
Perform neighbor search. The query set will be reordered.
Definition: ns_model_impl.hpp:581
A cover tree is a tree specifically designed to speed up nearest-neighbor computation in high-dimensi...
Definition: cover_tree.hpp:99
virtual NSWrapperBase * Clone() const =0
Create a new NSWrapperBase that is the same as this one.
~NSModel()
Clean memory, if necessary.
Definition: ns_model_impl.hpp:287