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