1 /**
2  * @file tests/cf_test.cpp
3  * @author Mudit Raj Gupta
4  * @author Haritha Nair
5  *
6  * Test file for CF class.
7  *
8  * mlpack is free software; you may redistribute it and/or modify it under the
9  * terms of the 3-clause BSD license.  You should have received a copy of the
10  * 3-clause BSD license along with mlpack.  If not, see
11  * http://www.opensource.org/licenses/BSD-3-Clause for more information.
12  */
13 
14 #include <mlpack/core.hpp>
15 #include <mlpack/methods/cf/cf.hpp>
16 #include <mlpack/methods/cf/decomposition_policies/batch_svd_method.hpp>
17 #include <mlpack/methods/cf/decomposition_policies/bias_svd_method.hpp>
18 #include <mlpack/methods/cf/decomposition_policies/randomized_svd_method.hpp>
19 #include <mlpack/methods/cf/decomposition_policies/regularized_svd_method.hpp>
20 #include <mlpack/methods/cf/decomposition_policies/svd_complete_method.hpp>
21 #include <mlpack/methods/cf/decomposition_policies/svd_incomplete_method.hpp>
22 #include <mlpack/methods/cf/decomposition_policies/svdplusplus_method.hpp>
23 #include <mlpack/methods/cf/normalization/no_normalization.hpp>
24 #include <mlpack/methods/cf/normalization/overall_mean_normalization.hpp>
25 #include <mlpack/methods/cf/normalization/user_mean_normalization.hpp>
26 #include <mlpack/methods/cf/normalization/item_mean_normalization.hpp>
27 #include <mlpack/methods/cf/normalization/z_score_normalization.hpp>
28 #include <mlpack/methods/cf/normalization/combined_normalization.hpp>
29 #include <mlpack/methods/cf/neighbor_search_policies/lmetric_search.hpp>
30 #include <mlpack/methods/cf/neighbor_search_policies/cosine_search.hpp>
31 #include <mlpack/methods/cf/neighbor_search_policies/pearson_search.hpp>
32 #include <mlpack/methods/cf/interpolation_policies/average_interpolation.hpp>
33 #include <mlpack/methods/cf/interpolation_policies/similarity_interpolation.hpp>
34 #include <mlpack/methods/cf/interpolation_policies/regression_interpolation.hpp>
35 
36 #include <iostream>
37 
38 #include "catch.hpp"
39 #include "test_catch_tools.hpp"
40 #include "serialization_catch.hpp"
41 
42 using namespace mlpack;
43 using namespace mlpack::cf;
44 using namespace std;
45 
46 // Get train and test datasets.
GetDatasets(arma::mat & dataset,arma::mat & savedCols)47 static void GetDatasets(arma::mat& dataset, arma::mat& savedCols)
48 {
49   data::Load("GroupLensSmall.csv", dataset);
50   savedCols.set_size(3, 50);
51 
52   // Save the columns we've removed.
53   savedCols.fill(/* random very large value */ 10000000);
54   size_t currentCol = 0;
55   for (size_t i = 0; i < dataset.n_cols; ++i)
56   {
57     if (currentCol == 50)
58       break;
59 
60     if (dataset(2, i) > 4.5) // 5-star rating.
61     {
62       // Make sure we don't have this user yet.  This is a slow way to do this
63       // but I don't particularly care here because it's in the tests.
64       bool found = false;
65       for (size_t j = 0; j < currentCol; ++j)
66       {
67         if (savedCols(0, j) == dataset(0, i))
68         {
69           found = true;
70           break;
71         }
72       }
73 
74       // If this user doesn't already exist in savedCols, add them.
75       // Otherwise ignore this point.
76       if (!found)
77       {
78         savedCols.col(currentCol) = dataset.col(i);
79         dataset.shed_col(i);
80         ++currentCol;
81       }
82     }
83   }
84 }
85 
86 /**
87  * Make sure that correct number of recommendations are generated when query
88  * set. Default case.
89  */
90 template<typename DecompositionPolicy>
GetRecommendationsAllUsers()91 void GetRecommendationsAllUsers()
92 {
93   DecompositionPolicy decomposition;
94   // Dummy number of recommendations.
95   size_t numRecs = 3;
96   // GroupLensSmall.csv dataset has 200 users.
97   size_t numUsers = 200;
98 
99   // Matrix to save recommendations into.
100   arma::Mat<size_t> recommendations;
101 
102   // Load GroupLens data.
103   arma::mat dataset;
104   data::Load("GroupLensSmall.csv", dataset);
105 
106   CFType<DecompositionPolicy> c(dataset, decomposition, 5, 5, 30);
107 
108   // Generate recommendations when query set is not specified.
109   c.GetRecommendations(numRecs, recommendations);
110 
111   // Check if correct number of recommendations are generated.
112   REQUIRE(recommendations.n_rows == numRecs);
113 
114   // Check if recommendations are generated for all users.
115   REQUIRE(recommendations.n_cols == numUsers);
116 }
117 
118 /**
119  * Make sure that the recommendations are generated for queried users only.
120  */
121 template<typename DecompositionPolicy>
GetRecommendationsQueriedUser()122 void GetRecommendationsQueriedUser()
123 {
124   DecompositionPolicy decomposition;
125   // Number of users that we will search for recommendations for.
126   size_t numUsers = 10;
127 
128   // Default number of recommendations.
129   size_t numRecsDefault = 5;
130 
131   // Create dummy query set.
132   arma::Col<size_t> users = arma::zeros<arma::Col<size_t> >(numUsers, 1);
133   for (size_t i = 0; i < numUsers; ++i)
134     users(i) = i;
135 
136   // Matrix to save recommendations into.
137   arma::Mat<size_t> recommendations;
138 
139   // Load GroupLens data.
140   arma::mat dataset;
141   data::Load("GroupLensSmall.csv", dataset);
142 
143   CFType<DecompositionPolicy> c(dataset, decomposition, 5, 5, 30);
144 
145   // Generate recommendations when query set is specified.
146   c.GetRecommendations(numRecsDefault, recommendations, users);
147 
148   // Check if correct number of recommendations are generated.
149   REQUIRE(recommendations.n_rows == numRecsDefault);
150 
151   // Check if recommendations are generated for the right number of users.
152   REQUIRE(recommendations.n_cols == numUsers);
153 }
154 
155 /**
156  * Make sure recommendations that are generated are reasonably accurate.
157  */
158 template<typename DecompositionPolicy,
159          typename NormalizationType = NoNormalization>
RecommendationAccuracy(const size_t allowedFailures=17)160 void RecommendationAccuracy(const size_t allowedFailures = 17)
161 {
162   DecompositionPolicy decomposition;
163 
164   // Small GroupLens dataset.
165   arma::mat dataset;
166 
167   // Save the columns we've removed.
168   arma::mat savedCols;
169 
170   GetDatasets(dataset, savedCols);
171 
172   CFType<DecompositionPolicy,
173       NormalizationType> c(dataset, decomposition, 5, 5, 30);
174 
175   // Obtain 150 recommendations for the users in savedCols, and make sure the
176   // missing item shows up in most of them.  First, create the list of users,
177   // which requires casting from doubles...
178   arma::Col<size_t> users(50);
179   for (size_t i = 0; i < 50; ++i)
180     users(i) = (size_t) savedCols(0, i);
181   arma::Mat<size_t> recommendations;
182   size_t numRecs = 150;
183   c.GetRecommendations(numRecs, recommendations, users);
184 
185   REQUIRE(recommendations.n_rows == numRecs);
186   REQUIRE(recommendations.n_cols == 50);
187 
188   size_t failures = 0;
189   for (size_t i = 0; i < 50; ++i)
190   {
191     size_t targetItem = (size_t) savedCols(1, i);
192     bool found = false;
193     // Make sure the target item shows up in the recommendations.
194     for (size_t j = 0; j < numRecs; ++j)
195     {
196       const size_t user = users(i);
197       const size_t item = recommendations(j, i);
198       if (item == targetItem)
199       {
200         found = true;
201       }
202       else
203       {
204         // Make sure we aren't being recommended an item that the user already
205         // rated.
206         REQUIRE((double) c.CleanedData()(item, user) == 0.0);
207       }
208     }
209 
210     if (!found)
211       ++failures;
212   }
213 
214   // Make sure the right item showed up in at least 2/3 of the recommendations.
215   REQUIRE(failures < allowedFailures);
216 }
217 
218 // Make sure that Predict() is returning reasonable results.
219 template<typename DecompositionPolicy,
220          typename NormalizationType = OverallMeanNormalization,
221          typename NeighborSearchPolicy = EuclideanSearch,
222          typename InterpolationPolicy = AverageInterpolation>
CFPredict(const double rmseBound=1.5)223 void CFPredict(const double rmseBound = 1.5)
224 {
225   DecompositionPolicy decomposition;
226 
227   // Small GroupLens dataset.
228   arma::mat dataset;
229 
230   // Save the columns we've removed.
231   arma::mat savedCols;
232 
233   GetDatasets(dataset, savedCols);
234 
235   CFType<DecompositionPolicy,
236       NormalizationType> c(dataset, decomposition, 5, 5, 30);
237 
238   // Now, for each removed rating, make sure the prediction is... reasonably
239   // accurate.
240   double totalError = 0.0;
241   for (size_t i = 0; i < savedCols.n_cols; ++i)
242   {
243     const double prediction = c.template Predict<NeighborSearchPolicy,
244         InterpolationPolicy>(savedCols(0, i), savedCols(1, i));
245 
246     const double error = std::pow(prediction - savedCols(2, i), 2.0);
247     totalError += error;
248   }
249 
250   const double rmse = std::sqrt(totalError / savedCols.n_cols);
251 
252   // The root mean square error should be less than ?.
253   REQUIRE(rmse < rmseBound);
254 }
255 
256 // Do the same thing as the previous test, but ensure that the ratings we
257 // predict with the batch Predict() are the same as the individual Predict()
258 // calls.
259 template<typename DecompositionPolicy>
BatchPredict()260 void BatchPredict()
261 {
262   DecompositionPolicy decomposition;
263 
264   // Small GroupLens dataset.
265   arma::mat dataset;
266 
267   // Save the columns we've removed.
268   arma::mat savedCols;
269 
270   GetDatasets(dataset, savedCols);
271 
272   CFType<DecompositionPolicy> c(dataset, decomposition, 5, 5, 30);
273 
274   // Get predictions for all user/item pairs we held back.
275   arma::Mat<size_t> combinations(2, savedCols.n_cols);
276   for (size_t i = 0; i < savedCols.n_cols; ++i)
277   {
278     combinations(0, i) = size_t(savedCols(0, i));
279     combinations(1, i) = size_t(savedCols(1, i));
280   }
281 
282   arma::vec predictions;
283   c.Predict(combinations, predictions);
284 
285   for (size_t i = 0; i < combinations.n_cols; ++i)
286   {
287     const double prediction = c.Predict(combinations(0, i), combinations(1, i));
288     REQUIRE(prediction == Approx(predictions[i]).epsilon(1e-10));
289   }
290 }
291 
292 /**
293  * Make sure we can train an already-trained model and it works okay.
294  */
295 template<typename DecompositionPolicy>
Train(DecompositionPolicy & decomposition)296 void Train(DecompositionPolicy& decomposition)
297 {
298   // Generate random data.
299   arma::sp_mat randomData;
300   randomData.sprandu(100, 100, 0.3);
301   CFType<DecompositionPolicy> c(randomData, decomposition, 5, 5, 30);
302 
303   // Small GroupLens dataset.
304   arma::mat dataset;
305 
306   // Save the columns we've removed.
307   arma::mat savedCols;
308 
309   GetDatasets(dataset, savedCols);
310 
311   // Make data into sparse matrix.
312   arma::sp_mat cleanedData;
313   CFType<DecompositionPolicy>::CleanData(dataset, cleanedData);
314 
315   // Now retrain.
316   c.Train(dataset, decomposition, 30);
317 
318   // Get predictions for all user/item pairs we held back.
319   arma::Mat<size_t> combinations(2, savedCols.n_cols);
320   for (size_t i = 0; i < savedCols.n_cols; ++i)
321   {
322     combinations(0, i) = size_t(savedCols(0, i));
323     combinations(1, i) = size_t(savedCols(1, i));
324   }
325 
326   arma::vec predictions;
327   c.Predict(combinations, predictions);
328 
329   for (size_t i = 0; i < combinations.n_cols; ++i)
330   {
331     const double prediction = c.Predict(combinations(0, i),
332       combinations(1, i));
333     REQUIRE(prediction == Approx(predictions[i]).epsilon(1e-10));
334   }
335 }
336 
337 /**
338  * Make sure we can train an already-trained model and it works okay
339  * for policies that use coordinate lists.
340  */
341 template<typename DecompositionPolicy>
TrainWithCoordinateList(DecompositionPolicy & decomposition)342 void TrainWithCoordinateList(DecompositionPolicy& decomposition)
343 {
344   arma::mat randomData(3, 100);
345   randomData.row(0) = arma::linspace<arma::rowvec>(0, 99, 100);
346   randomData.row(1) = arma::linspace<arma::rowvec>(0, 99, 100);
347   randomData.row(2).fill(3);
348   CFType<DecompositionPolicy> c(randomData, decomposition, 5, 5, 30);
349 
350   // Now retrain with data we know about.
351   // Small GroupLens dataset.
352   arma::mat dataset;
353 
354   // Save the columns we've removed.
355   arma::mat savedCols;
356 
357   GetDatasets(dataset, savedCols);
358 
359   // Now retrain.
360   c.Train(dataset, decomposition, 30);
361 
362   // Get predictions for all user/item pairs we held back.
363   arma::Mat<size_t> combinations(2, savedCols.n_cols);
364   for (size_t i = 0; i < savedCols.n_cols; ++i)
365   {
366     combinations(0, i) = size_t(savedCols(0, i));
367     combinations(1, i) = size_t(savedCols(1, i));
368   }
369 
370   arma::vec predictions;
371   c.Predict(combinations, predictions);
372 
373   for (size_t i = 0; i < combinations.n_cols; ++i)
374   {
375     const double prediction = c.Predict(combinations(0, i), combinations(1, i));
376     REQUIRE(prediction == Approx(predictions[i]).epsilon(1e-10));
377   }
378 }
379 
380 /**
381  * Make sure we can train a model after using the empty constructor.
382  */
383 template<typename DecompositionPolicy>
EmptyConstructorTrain()384 void EmptyConstructorTrain()
385 {
386   DecompositionPolicy decomposition;
387   // Use default constructor.
388   CFType<DecompositionPolicy> c;
389 
390   // Now retrain with data we know about.
391   // Small GroupLens dataset.
392   arma::mat dataset;
393 
394   // Save the columns we've removed.
395   arma::mat savedCols;
396 
397   GetDatasets(dataset, savedCols);
398 
399   c.Train(dataset, decomposition, 30);
400 
401   // Get predictions for all user/item pairs we held back.
402   arma::Mat<size_t> combinations(2, savedCols.n_cols);
403   for (size_t i = 0; i < savedCols.n_cols; ++i)
404   {
405     combinations(0, i) = size_t(savedCols(0, i));
406     combinations(1, i) = size_t(savedCols(1, i));
407   }
408 
409   arma::vec predictions;
410   c.Predict(combinations, predictions);
411 
412   for (size_t i = 0; i < combinations.n_cols; ++i)
413   {
414     const double prediction = c.Predict(combinations(0, i),
415         combinations(1, i));
416     REQUIRE(prediction == Approx(predictions[i]).epsilon(1e-10));
417   }
418 }
419 
420 /**
421  * Ensure we can load and save the CF model.
422  */
423 template<typename DecompositionPolicy,
424          typename NormalizationType = NoNormalization>
Serialization()425 void Serialization()
426 {
427   DecompositionPolicy decomposition;
428   // Load a dataset to train on.
429   arma::mat dataset;
430   data::Load("GroupLensSmall.csv", dataset);
431 
432   arma::sp_mat cleanedData;
433   CFType<DecompositionPolicy,
434       NormalizationType>::CleanData(dataset, cleanedData);
435 
436   CFType<DecompositionPolicy,
437       NormalizationType> c(cleanedData, decomposition, 5, 5, 30);
438 
439   arma::sp_mat randomData;
440   randomData.sprandu(100, 100, 0.3);
441 
442   CFType<DecompositionPolicy,
443       NormalizationType> cXml(randomData, decomposition, 5, 5, 30);
444   CFType<DecompositionPolicy,
445       NormalizationType> cBinary;
446   CFType<DecompositionPolicy,
447       NormalizationType> cText(cleanedData, decomposition, 5, 5, 30);
448 
449   SerializeObjectAll(c, cXml, cText, cBinary);
450 
451   // Check the internals.
452   REQUIRE(c.NumUsersForSimilarity() == cXml.NumUsersForSimilarity());
453   REQUIRE(c.NumUsersForSimilarity() == cBinary.NumUsersForSimilarity());
454   REQUIRE(c.NumUsersForSimilarity() == cText.NumUsersForSimilarity());
455 
456   REQUIRE(c.Rank() == cXml.Rank());
457   REQUIRE(c.Rank() == cBinary.Rank());
458   REQUIRE(c.Rank() == cText.Rank());
459 
460   CheckMatrices(c.Decomposition().W(), cXml.Decomposition().W(),
461       cBinary.Decomposition().W(), cText.Decomposition().W());
462   CheckMatrices(c.Decomposition().H(), cXml.Decomposition().H(),
463       cBinary.Decomposition().H(), cText.Decomposition().H());
464 
465   REQUIRE(c.CleanedData().n_rows == cXml.CleanedData().n_rows);
466   REQUIRE(c.CleanedData().n_rows == cBinary.CleanedData().n_rows);
467   REQUIRE(c.CleanedData().n_rows == cText.CleanedData().n_rows);
468 
469   REQUIRE(c.CleanedData().n_cols == cXml.CleanedData().n_cols);
470   REQUIRE(c.CleanedData().n_cols == cBinary.CleanedData().n_cols);
471   REQUIRE(c.CleanedData().n_cols == cText.CleanedData().n_cols);
472 
473   REQUIRE(c.CleanedData().n_nonzero == cXml.CleanedData().n_nonzero);
474   REQUIRE(c.CleanedData().n_nonzero == cBinary.CleanedData().n_nonzero);
475   REQUIRE(c.CleanedData().n_nonzero == cText.CleanedData().n_nonzero);
476 
477   c.CleanedData().sync();
478 
479   for (size_t i = 0; i <= c.CleanedData().n_cols; ++i)
480   {
481     REQUIRE(c.CleanedData().col_ptrs[i] == cXml.CleanedData().col_ptrs[i]);
482     REQUIRE(c.CleanedData().col_ptrs[i] == cBinary.CleanedData().col_ptrs[i]);
483     REQUIRE(c.CleanedData().col_ptrs[i] == cText.CleanedData().col_ptrs[i]);
484   }
485 
486   for (size_t i = 0; i <= c.CleanedData().n_nonzero; ++i)
487   {
488     REQUIRE(c.CleanedData().row_indices[i] ==
489         cXml.CleanedData().row_indices[i]);
490     REQUIRE(c.CleanedData().row_indices[i] ==
491         cBinary.CleanedData().row_indices[i]);
492     REQUIRE(c.CleanedData().row_indices[i] ==
493         cText.CleanedData().row_indices[i]);
494 
495     REQUIRE(c.CleanedData().values[i] ==
496       Approx(cXml.CleanedData().values[i]).epsilon(1e-7));
497     REQUIRE(c.CleanedData().values[i] ==
498       Approx(cBinary.CleanedData().values[i]).epsilon(1e-7));
499     REQUIRE(c.CleanedData().values[i] ==
500       Approx(cText.CleanedData().values[i]).epsilon(1e-7));
501   }
502 }
503 
504 /**
505  * Make sure that correct number of recommendations are generated when query
506  * set for randomized SVD.
507  */
508 TEST_CASE("CFGetRecommendationsAllUsersRandSVDTest", "[CFTest]")
509 {
510   GetRecommendationsAllUsers<RandomizedSVDPolicy>();
511 }
512 
513 /**
514  * Make sure that correct number of recommendations are generated when query
515  * set for regularized SVD.
516  */
517 TEST_CASE("CFGetRecommendationsAllUsersRegSVDTest", "[CFTest]")
518 {
519   GetRecommendationsAllUsers<RegSVDPolicy>();
520 }
521 
522 /**
523  * Make sure that correct number of recommendations are generated when query
524  * set for Batch SVD.
525  */
526 
527 TEST_CASE("CFGetRecommendationsAllUsersBatchSVDTest", "[CFTest]")
528 {
529   GetRecommendationsAllUsers<BatchSVDPolicy>();
530 }
531 
532 /**
533  * Make sure that correct number of recommendations are generated when query
534  * set for NMF.
535  */
536 TEST_CASE("CFGetRecommendationsAllUsersNMFTest", "[CFTest]")
537 {
538   GetRecommendationsAllUsers<NMFPolicy>();
539 }
540 
541 /**
542  * Make sure that correct number of recommendations are generated when query
543  * set for SVD Complete Incremental method.
544  */
545 TEST_CASE("CFGetRecommendationsAllUsersSVDCompleteTest", "[CFTest]")
546 {
547   GetRecommendationsAllUsers<SVDCompletePolicy>();
548 }
549 
550 /**
551  * Make sure that correct number of recommendations are generated when query
552  * set for SVD Incomplete Incremental method.
553  */
554 TEST_CASE("CFGetRecommendationsAllUsersSVDIncompleteTest", "[CFTest]")
555 {
556   GetRecommendationsAllUsers<SVDIncompletePolicy>();
557 }
558 
559 /**
560  * Make sure that correct number of recommendations are generated when query
561  * set for Bias SVD method.
562  */
563 TEST_CASE("CFGetRecommendationsAllUsersBiasSVDTest", "[CFTest]")
564 {
565   GetRecommendationsAllUsers<BiasSVDPolicy>();
566 }
567 
568 /**
569  * Make sure that correct number of recommendations are generated when query
570  * set for SVDPlusPlus method.
571  */
572 TEST_CASE("CFGetRecommendationsAllUsersSVDPPTest", "[CFTest]")
573 {
574   GetRecommendationsAllUsers<SVDPlusPlusPolicy>();
575 }
576 
577 /**
578  * Make sure that the recommendations are generated for queried users only
579  * for randomized SVD.
580  */
581 TEST_CASE("CFGetRecommendationsQueriedUserRandSVDTest", "[CFTest]")
582 {
583   GetRecommendationsQueriedUser<RandomizedSVDPolicy>();
584 }
585 
586 /**
587  * Make sure that the recommendations are generated for queried users only
588  * for regularized SVD.
589  */
590 TEST_CASE("CFGetRecommendationsQueriedUserRegSVDTest", "[CFTest]")
591 {
592   GetRecommendationsQueriedUser<RegSVDPolicy>();
593 }
594 
595 /**
596  * Make sure that the recommendations are generated for queried users only
597  * for batch SVD.
598  */
599 TEST_CASE("CFGetRecommendationsQueriedUserBatchSVDTest", "[CFTest]")
600 {
601   GetRecommendationsQueriedUser<BatchSVDPolicy>();
602 }
603 
604 /**
605  * Make sure that the recommendations are generated for queried users only
606  * for NMF.
607  */
608 TEST_CASE("CFGetRecommendationsQueriedUserNMFTest", "[CFTest]")
609 {
610   GetRecommendationsQueriedUser<NMFPolicy>();
611 }
612 
613 /**
614  * Make sure that the recommendations are generated for queried users only
615  * for SVD Complete Incremental method.
616  */
617 TEST_CASE("CFGetRecommendationsQueriedUserSVDCompleteTest", "[CFTest]")
618 {
619   GetRecommendationsQueriedUser<SVDCompletePolicy>();
620 }
621 
622 /**
623  * Make sure that the recommendations are generated for queried users only
624  * for SVD Incomplete Incremental method.
625  */
626 TEST_CASE("CFGetRecommendationsQueriedUserSVDIncompleteTest", "[CFTest]")
627 {
628   GetRecommendationsQueriedUser<SVDIncompletePolicy>();
629 }
630 
631 /**
632  * Make sure that the recommendations are generated for queried users only
633  * for Bias SVD method.
634  */
635 TEST_CASE("CFGetRecommendationsQueriedUserBiasSVDTest", "[CFTest]")
636 {
637   GetRecommendationsQueriedUser<BiasSVDPolicy>();
638 }
639 
640 /**
641  * Make sure that the recommendations are generated for queried users only
642  * for SVDPlusPlus method.
643  */
644 TEST_CASE("CFGetRecommendationsQueriedUserSVDPPTest", "[CFTest]")
645 {
646   GetRecommendationsQueriedUser<SVDPlusPlusPolicy>();
647 }
648 
649 /**
650  * Make sure recommendations that are generated are reasonably accurate
651  * for randomized SVD.
652  */
653 TEST_CASE("RecommendationAccuracyRandSVDTest", "[CFTest]")
654 {
655   RecommendationAccuracy<RandomizedSVDPolicy>();
656 }
657 
658 /**
659  * Make sure recommendations that are generated are reasonably accurate
660  * for regularized SVD.
661  */
662 TEST_CASE("RecommendationAccuracyRegSVDTest", "[CFTest]")
663 {
664   RecommendationAccuracy<RegSVDPolicy>();
665 }
666 
667 /**
668  * Make sure recommendations that are generated are reasonably accurate
669  * for batch SVD.
670  */
671 TEST_CASE("RecommendationAccuracyBatchSVDTest", "[CFTest]")
672 {
673   RecommendationAccuracy<BatchSVDPolicy>();
674 }
675 
676 /**
677  * Make sure recommendations that are generated are reasonably accurate
678  * for NMF.
679  */
680 TEST_CASE("RecommendationAccuracyNMFTest", "[CFTest]")
681 {
682   RecommendationAccuracy<NMFPolicy>();
683 }
684 
685 /**
686  * Make sure recommendations that are generated are reasonably accurate
687  * for SVD Complete Incremental method.
688  */
689 TEST_CASE("RecommendationAccuracySVDCompleteTest", "[CFTest]")
690 {
691   RecommendationAccuracy<SVDCompletePolicy>();
692 }
693 
694 /**
695  * Make sure recommendations that are generated are reasonably accurate
696  * for SVD Incomplete Incremental method.
697  */
698 TEST_CASE("RecommendationAccuracySVDIncompleteTest", "[CFTest]")
699 {
700   RecommendationAccuracy<SVDIncompletePolicy>();
701 }
702 
703 /**
704  * Make sure recommendations that are generated are reasonably accurate
705  * for Bias SVD method.
706  */
707 TEST_CASE("RecommendationAccuracyBiasSVDTest", "[CFTest]")
708 {
709   // This algorithm seems to be far less effective than others.
710   // We therefore allow failures on 44% of the runs.
711   RecommendationAccuracy<BiasSVDPolicy>(22);
712 }
713 
714 /**
715  * Make sure recommendations that are generated are reasonably accurate
716  * for SVDPlusPlus method.
717  */
718 // This test is commented out because it fails and we haven't solved it yet.
719 // Please refer to issue #1501 for more info about this test.
720 // TEST_CASE("RecommendationAccuracySVDPPTest", "[CFTest]")
721 // {
722 //   RecommendationAccuracy<SVDPlusPlusPolicy>();
723 // }
724 
725 // Make sure that Predict() is returning reasonable results for randomized SVD.
726 TEST_CASE("CFPredictRandSVDTest", "[CFTest]")
727 {
728   CFPredict<RandomizedSVDPolicy>();
729 }
730 
731 // Make sure that Predict() is returning reasonable results for regularized SVD.
732 TEST_CASE("CFPredictRegSVDTest", "[CFTest]")
733 {
734   CFPredict<RegSVDPolicy>();
735 }
736 
737 // Make sure that Predict() is returning reasonable results for batch SVD.
738 TEST_CASE("CFPredictBatchSVDTest", "[CFTest]")
739 {
740   CFPredict<BatchSVDPolicy>();
741 }
742 
743 // Make sure that Predict() is returning reasonable results for NMF.
744 TEST_CASE("CFPredictNMFTest", "[CFTest]")
745 {
746   CFPredict<NMFPolicy>();
747 }
748 
749 /**
750  * Make sure that Predict() is returning reasonable results for SVD Complete
751  * Incremental method.
752  */
753 TEST_CASE("CFPredictSVDCompleteTest", "[CFTest]")
754 {
755   CFPredict<SVDCompletePolicy>();
756 }
757 
758 /**
759  * Make sure that Predict() is returning reasonable results for SVD Incomplete
760  * Incremental method.
761  */
762 TEST_CASE("CFPredictSVDIncompleteTest", "[CFTest]")
763 {
764   CFPredict<SVDIncompletePolicy>();
765 }
766 
767 /**
768  * Make sure that Predict() is returning reasonable results for Bias SVD
769  * method.
770  */
771 TEST_CASE("CFPredictBiasSVDTest", "[CFTest]")
772 {
773   CFPredict<BiasSVDPolicy>();
774 }
775 
776 /**
777  * Make sure that Predict() is returning reasonable results for SVDPlusPlus
778  * method.
779  */
780 TEST_CASE("CFPredictSVDPPTest", "[CFTest]")
781 {
782   CFPredict<SVDPlusPlusPolicy>();
783 }
784 
785 // Compare batch Predict() and individual Predict() for randomized SVD.
786 TEST_CASE("CFBatchPredictRandSVDTest", "[CFTest]")
787 {
788   BatchPredict<RandomizedSVDPolicy>();
789 }
790 
791 // Compare batch Predict() and individual Predict() for regularized SVD.
792 TEST_CASE("CFBatchPredictRegSVDTest", "[CFTest]")
793 {
794   BatchPredict<RegSVDPolicy>();
795 }
796 
797 // Compare batch Predict() and individual Predict() for batch SVD.
798 TEST_CASE("CFBatchPredictBatchSVDTest", "[CFTest]")
799 {
800   BatchPredict<BatchSVDPolicy>();
801 }
802 
803 // Compare batch Predict() and individual Predict() for NMF.
804 TEST_CASE("CFBatchPredictNMFTest", "[CFTest]")
805 {
806   BatchPredict<NMFPolicy>();
807 }
808 
809 // Compare batch Predict() and individual Predict() for
810 // SVD Complete Incremental method.
811 TEST_CASE("CFBatchPredictSVDCompleteTest", "[CFTest]")
812 {
813   BatchPredict<SVDCompletePolicy>();
814 }
815 
816 // Compare batch Predict() and individual Predict() for
817 // SVD Incomplete Incremental method.
818 TEST_CASE("CFBatchPredictSVDIncompleteTest", "[CFTest]")
819 {
820   BatchPredict<SVDIncompletePolicy>();
821 }
822 
823 // Compare batch Predict() and individual Predict() for
824 // Bias SVD method.
825 TEST_CASE("CFBatchPredictBiasSVDTest", "[CFTest]")
826 {
827   BatchPredict<BiasSVDPolicy>();
828 }
829 
830 // Compare batch Predict() and individual Predict() for
831 // SVDPlusPlus method.
832 TEST_CASE("CFBatchPredictSVDPPTest", "[CFTest]")
833 {
834   BatchPredict<SVDPlusPlusPolicy>();
835 }
836 
837 /**
838  * Make sure we can train an already-trained model and it works okay for
839  * randomized SVD.
840  */
841 TEST_CASE("TrainRandSVDTest", "[CFTest]")
842 {
843   RandomizedSVDPolicy decomposition;
844   Train(decomposition);
845 }
846 
847 /**
848  * Make sure we can train an already-trained model and it works okay for
849  * regularized SVD.
850  */
851 TEST_CASE("TrainRegSVDTest", "[CFTest]")
852 {
853   RegSVDPolicy decomposition;
854   TrainWithCoordinateList(decomposition);
855 }
856 
857 /**
858  * Make sure we can train an already-trained model and it works okay for
859  * batch SVD.
860  */
861 TEST_CASE("TrainBatchSVDTest", "[CFTest]")
862 {
863   BatchSVDPolicy decomposition;
864   Train(decomposition);
865 }
866 
867 /**
868  * Make sure we can train an already-trained model and it works okay for
869  * NMF.
870  */
871 TEST_CASE("TrainNMFTest", "[CFTest]")
872 {
873   NMFPolicy decomposition;
874   Train(decomposition);
875 }
876 
877 /**
878  * Make sure we can train an already-trained model and it works okay for
879  * SVD Complete Incremental method.
880  */
881 TEST_CASE("TrainSVDCompleteTest", "[CFTest]")
882 {
883   SVDCompletePolicy decomposition;
884   Train(decomposition);
885 }
886 
887 /**
888  * Make sure we can train an already-trained model and it works okay for
889  * SVD Incomplete Incremental method.
890  */
891 TEST_CASE("TrainSVDIncompleteTest", "[CFTest]")
892 {
893   SVDIncompletePolicy decomposition;
894   Train(decomposition);
895 }
896 
897 /**
898  * Make sure we can train an already-trained model and it works okay for
899  * BiasSVD method.
900  */
901 TEST_CASE("TrainBiasSVDTest", "[CFTest]")
902 {
903   BiasSVDPolicy decomposition;
904   TrainWithCoordinateList(decomposition);
905 }
906 
907 /**
908  * Make sure we can train an already-trained model and it works okay for
909  * SVDPlusPlus method.
910  */
911 TEST_CASE("TrainSVDPPTest", "[CFTest]")
912 {
913   SVDPlusPlusPolicy decomposition;
914   TrainWithCoordinateList(decomposition);
915 }
916 
917 /**
918  * Make sure we can train a model after using the empty constructor when
919  * using randomized SVD.
920  */
921 TEST_CASE("EmptyConstructorTrainRandSVDTest", "[CFTest]")
922 {
923   EmptyConstructorTrain<RandomizedSVDPolicy>();
924 }
925 
926 /**
927  * Make sure we can train a model after using the empty constructor when
928  * using regularized SVD.
929  */
930 TEST_CASE("EmptyConstructorTrainRegSVDTest", "[CFTest]")
931 {
932   EmptyConstructorTrain<RegSVDPolicy>();
933 }
934 
935 /**
936  * Make sure we can train a model after using the empty constructor when
937  * using batch SVD.
938  */
939 TEST_CASE("EmptyConstructorTrainBatchSVDTest", "[CFTest]")
940 {
941   EmptyConstructorTrain<BatchSVDPolicy>();
942 }
943 
944 /**
945  * Make sure we can train a model after using the empty constructor when
946  * using NMF.
947  */
948 TEST_CASE("EmptyConstructorTrainNMFTest", "[CFTest]")
949 {
950   EmptyConstructorTrain<NMFPolicy>();
951 }
952 
953 /**
954  * Make sure we can train a model after using the empty constructor when
955  * using SVD Complete Incremental method.
956  */
957 TEST_CASE("EmptyConstructorTrainSVDCompleteTest", "[CFTest]")
958 {
959   EmptyConstructorTrain<SVDCompletePolicy>();
960 }
961 
962 /**
963  * Make sure we can train a model after using the empty constructor when
964  * using SVD Incomplete Incremental method.
965  */
966 TEST_CASE("EmptyConstructorTrainSVDIncompleteTest", "[CFTest]")
967 {
968   EmptyConstructorTrain<SVDIncompletePolicy>();
969 }
970 
971 /**
972  * Ensure we can load and save the CF model using randomized SVD policy.
973  */
974 TEST_CASE("SerializationRandSVDTest", "[CFTest]")
975 {
976   Serialization<RandomizedSVDPolicy>();
977 }
978 
979 /**
980  * Ensure we can load and save the CF model using batch SVD policy.
981  */
982 TEST_CASE("SerializationBatchSVDTest", "[CFTest]")
983 {
984   Serialization<BatchSVDPolicy>();
985 }
986 
987 /**
988  * Ensure we can load and save the CF model using NMF policy.
989  */
990 TEST_CASE("SerializationNMFTest", "[CFTest]")
991 {
992   Serialization<NMFPolicy>();
993 }
994 
995 /**
996  * Ensure we can load and save the CF model using SVD Complete Incremental.
997  */
998 TEST_CASE("SerializationSVDCompleteTest", "[CFTest]")
999 {
1000   Serialization<SVDCompletePolicy>();
1001 }
1002 
1003 /**
1004  * Ensure we can load and save the CF model using SVD Incomplete Incremental.
1005  */
1006 TEST_CASE("SerializationSVDIncompleteTest", "[CFTest]")
1007 {
1008   Serialization<SVDIncompletePolicy>();
1009 }
1010 
1011 /**
1012  * Make sure that Predict() is returning reasonable results for NMF and
1013  * OverallMeanNormalization.
1014  */
1015 TEST_CASE("CFPredictOverallMeanNormalization", "[CFTest]")
1016 {
1017   CFPredict<NMFPolicy, OverallMeanNormalization>(2.0);
1018 }
1019 
1020 /**
1021  * Make sure that Predict() is returning reasonable results for NMF and
1022  * UserMeanNormalization.
1023  */
1024 TEST_CASE("CFPredictUserMeanNormalization", "[CFTest]")
1025 {
1026   CFPredict<NMFPolicy, UserMeanNormalization>(2.0);
1027 }
1028 
1029 /**
1030  * Make sure that Predict() is returning reasonable results for NMF and
1031  * ItemMeanNormalization.
1032  */
1033 TEST_CASE("CFPredictItemMeanNormalization", "[CFTest]")
1034 {
1035   CFPredict<NMFPolicy, ItemMeanNormalization>(2.0);
1036 }
1037 
1038 /**
1039  * Make sure that Predict() is returning reasonable results for NMF and
1040  * ZScoreNormalization.
1041  */
1042 TEST_CASE("CFPredictZScoreNormalization", "[CFTest]")
1043 {
1044   CFPredict<NMFPolicy, ZScoreNormalization>(2.0);
1045 }
1046 
1047 /**
1048  * Make sure that Predict() is returning reasonable results for NMF and
1049  * CombinedNormalization<OverallMeanNormalization, UserMeanNormalization,
1050  * ItemMeanNormalization>.
1051  */
1052 TEST_CASE("CFPredictCombinedNormalization", "[CFTest]")
1053 {
1054   CFPredict<NMFPolicy,
1055             CombinedNormalization<
1056                 OverallMeanNormalization,
1057                 UserMeanNormalization,
1058                 ItemMeanNormalization>>(2.0);
1059 }
1060 
1061 /**
1062  * Make sure that Predict() works with NoNormalization.
1063  */
1064 TEST_CASE("CFPredictNoNormalization", "[CFTest]")
1065 {
1066   CFPredict<RegSVDPolicy, NoNormalization>(2.0);
1067 }
1068 
1069 /**
1070  * Make sure recommendations that are generated are reasonably accurate
1071  * for OverallMeanNormalization.
1072  */
1073 TEST_CASE("RecommendationAccuracyOverallMeanNormalizationTest", "[CFTest]")
1074 {
1075   RecommendationAccuracy<NMFPolicy, OverallMeanNormalization>();
1076 }
1077 
1078 /**
1079  * Make sure recommendations that are generated are reasonably accurate
1080  * for UserMeanNormalization.
1081  */
1082 TEST_CASE("RecommendationAccuracyUserMeanNormalizationTest", "[CFTest]")
1083 {
1084   RecommendationAccuracy<NMFPolicy, UserMeanNormalization>();
1085 }
1086 
1087 /**
1088  * Make sure recommendations that are generated are reasonably accurate
1089  * for ItemMeanNormalization.
1090  */
1091 TEST_CASE("RecommendationAccuracyItemMeanNormalizationTest", "[CFTest]")
1092 {
1093   RecommendationAccuracy<NMFPolicy, ItemMeanNormalization>();
1094 }
1095 
1096 /**
1097  * Make sure recommendations that are generated are reasonably accurate
1098  * for ZScoreNormalization.
1099  */
1100 TEST_CASE("RecommendationAccuracyZScoreNormalizationTest", "[CFTest]")
1101 {
1102   RecommendationAccuracy<NMFPolicy, ZScoreNormalization>();
1103 }
1104 
1105 /**
1106  * Make sure recommendations that are generated are reasonably accurate
1107  * for CombinedNormalization.
1108  */
1109 TEST_CASE("RecommendationAccuracyCombinedNormalizationTest", "[CFTest]")
1110 {
1111   RecommendationAccuracy<NMFPolicy,
1112                          CombinedNormalization<
1113                            OverallMeanNormalization,
1114                            UserMeanNormalization,
1115                            ItemMeanNormalization>>();
1116 }
1117 
1118 /**
1119  * Ensure we can load and save the CF model using OverallMeanNormalization.
1120  */
1121 TEST_CASE("SerializationOverallMeanNormalizationTest", "[CFTest]")
1122 {
1123   Serialization<NMFPolicy, OverallMeanNormalization>();
1124 }
1125 
1126 /**
1127  * Ensure we can load and save the CF model using UserMeanNormalization.
1128  */
1129 TEST_CASE("SerializationUserMeanNormalizationTest", "[CFTest]")
1130 {
1131   Serialization<NMFPolicy, UserMeanNormalization>();
1132 }
1133 
1134 /**
1135  * Ensure we can load and save the CF model using ItemMeanNormalization.
1136  */
1137 TEST_CASE("SerializationItemMeanNormalizationTest", "[CFTest]")
1138 {
1139   Serialization<NMFPolicy, ItemMeanNormalization>();
1140 }
1141 
1142 /**
1143  * Ensure we can load and save the CF model using ZScoreMeanNormalization.
1144  */
1145 TEST_CASE("SerializationZScoreNormalizationTest", "[CFTest]")
1146 {
1147   Serialization<NMFPolicy, ZScoreNormalization>();
1148 }
1149 
1150 /**
1151  * Ensure we can load and save the CF model using CombinedNormalization.
1152  */
1153 TEST_CASE("SerializationCombinedNormalizationTest", "[CFTest]")
1154 {
1155   Serialization<NMFPolicy,
1156                 CombinedNormalization<
1157                     OverallMeanNormalization,
1158                     UserMeanNormalization,
1159                     ItemMeanNormalization>>();
1160 }
1161 
1162 /**
1163  * Make sure that Predict() is returning reasonable results for
1164  * EuclideanSearch.
1165  */
1166 TEST_CASE("CFPredictEuclideanSearch", "[CFTest]")
1167 {
1168   CFPredict<NMFPolicy, OverallMeanNormalization, EuclideanSearch>(2.0);
1169 }
1170 
1171 /**
1172  * Make sure that Predict() is returning reasonable results for
1173  * CosineSearch.
1174  */
1175 TEST_CASE("CFPredictCosineSearch", "[CFTest]")
1176 {
1177   CFPredict<NMFPolicy, OverallMeanNormalization, CosineSearch>(2.0);
1178 }
1179 
1180 /**
1181  * Make sure that Predict() is returning reasonable results for
1182  * PearsonSearch.
1183  */
1184 TEST_CASE("CFPredictPearsonSearch", "[CFTest]")
1185 {
1186   CFPredict<NMFPolicy, OverallMeanNormalization, PearsonSearch>(2.0);
1187 }
1188 
1189 /**
1190  * Make sure that Predict() is returning reasonable results for
1191  * AverageInterpolation.
1192  */
1193 TEST_CASE("CFPredictAverageInterpolation", "[CFTest]")
1194 {
1195   CFPredict<NMFPolicy,
1196             OverallMeanNormalization,
1197             EuclideanSearch,
1198             AverageInterpolation>(2.0);
1199 }
1200 
1201 /**
1202  * Make sure that Predict() is returning reasonable results for
1203  * SimilarityInterpolation.
1204  */
1205 TEST_CASE("CFPredictSimilarityInterpolation", "[CFTest]")
1206 {
1207   CFPredict<NMFPolicy,
1208             OverallMeanNormalization,
1209             EuclideanSearch,
1210             SimilarityInterpolation>(2.0);
1211 }
1212 
1213 /**
1214  * Make sure that Predict() is returning reasonable results for
1215  * RegressionInterpolation.
1216  */
1217 TEST_CASE("CFPredictRegressionInterpolation", "[CFTest]")
1218 {
1219   CFPredict<RegSVDPolicy,
1220             OverallMeanNormalization,
1221             EuclideanSearch,
1222             RegressionInterpolation>(2.0);
1223 }
1224