1414
1515import typing as tp
1616
17+ import implicit .gpu
1718import numpy as np
1819import pandas as pd
1920import pytest
21+ from implicit .gpu import HAS_CUDA
2022
2123from rectools import Columns
2224from rectools .dataset import Dataset
3335)
3436
3537try :
36- import cupy as cp
38+ import cupy as cp # pylint: disable=import-error, unused-import
3739except ImportError : # pragma: no cover
3840 cp = None
3941
@@ -45,7 +47,7 @@ def dataset(self) -> Dataset:
4547 return DATASET
4648
4749 @pytest .mark .parametrize (
48- "filter_viewed,expected,use_gpu " ,
50+ "filter_viewed,expected" ,
4951 (
5052 (
5153 True ,
@@ -56,7 +58,6 @@ def dataset(self) -> Dataset:
5658 Columns .Rank : [1 , 2 , 1 , 2 ],
5759 }
5860 ),
59- False ,
6061 ),
6162 (
6263 False ,
@@ -67,8 +68,33 @@ def dataset(self) -> Dataset:
6768 Columns .Rank : [1 , 2 , 1 , 2 ],
6869 }
6970 ),
70- False ,
7171 ),
72+ ),
73+ )
74+ def test_basic (
75+ self ,
76+ dataset : Dataset ,
77+ filter_viewed : bool ,
78+ expected : pd .DataFrame ,
79+ ) -> None :
80+ model = PureSVDModel (factors = 2 , use_gpu = False ).fit (dataset )
81+ actual = model .recommend (
82+ users = np .array ([10 , 20 ]),
83+ dataset = dataset ,
84+ k = 2 ,
85+ filter_viewed = filter_viewed ,
86+ )
87+ pd .testing .assert_frame_equal (actual .drop (columns = Columns .Score ), expected )
88+ pd .testing .assert_frame_equal (
89+ actual .sort_values ([Columns .User , Columns .Score ], ascending = [True , False ]).reset_index (drop = True ),
90+ actual ,
91+ )
92+
93+ # SciPy's svds and cupy's svds results can be different and use_gpu fallback causes errors
94+ @pytest .mark .skipif (HAS_CUDA is False , reason = "CUDA is not available" )
95+ @pytest .mark .parametrize (
96+ "filter_viewed,expected" ,
97+ (
7298 (
7399 True ,
74100 pd .DataFrame (
@@ -78,7 +104,6 @@ def dataset(self) -> Dataset:
78104 Columns .Rank : [1 , 2 , 1 , 2 ],
79105 }
80106 ),
81- True ,
82107 ),
83108 (
84109 False ,
@@ -89,18 +114,16 @@ def dataset(self) -> Dataset:
89114 Columns .Rank : [1 , 2 , 1 , 2 ],
90115 }
91116 ),
92- True ,
93117 ),
94118 ),
95119 )
96- def test_basic (
120+ def test_basic_gpu (
97121 self ,
98122 dataset : Dataset ,
99123 filter_viewed : bool ,
100124 expected : pd .DataFrame ,
101- use_gpu : bool ,
102125 ) -> None :
103- model = PureSVDModel (factors = 2 , use_gpu = use_gpu ).fit (dataset )
126+ model = PureSVDModel (factors = 2 , use_gpu = True ).fit (dataset )
104127 actual = model .recommend (
105128 users = np .array ([10 , 20 ]),
106129 dataset = dataset ,
@@ -297,12 +320,18 @@ def test_dumps_loads(self, dataset: Dataset) -> None:
297320
298321class TestPureSVDModelConfiguration :
299322
300- def test_from_config (self ) -> None :
323+ def setup_method (self ) -> None :
324+ implicit .gpu .HAS_CUDA = True # To avoid errors when test without cuda
325+
326+ @pytest .mark .parametrize ("use_gpu" , (False , True ))
327+ def test_from_config (self , mocker , use_gpu : bool ) -> None :
328+ mocker .patch ("rectools.models.pure_svd.cp" , return_value = True )
301329 config = {
302330 "factors" : 100 ,
303331 "tol" : 0 ,
304332 "maxiter" : 100 ,
305333 "random_state" : 32 ,
334+ "use_gpu" : use_gpu ,
306335 "verbose" : 0 ,
307336 }
308337 model = PureSVDModel .from_config (config )
@@ -315,10 +344,11 @@ def test_from_config(self) -> None:
315344 @pytest .mark .parametrize ("random_state" , (None , 42 ))
316345 @pytest .mark .parametrize ("simple_types" , (False , True ))
317346 @pytest .mark .parametrize ("use_gpu" , (False , True ))
318- def test_get_config (self , random_state : tp .Optional [int ], simple_types : bool , use_gpu : bool ) -> None :
347+ def test_get_config (self , mocker , random_state : tp .Optional [int ], simple_types : bool , use_gpu : bool ) -> None :
348+ mocker .patch ("rectools.models.pure_svd.cp" , return_value = True )
319349 model = PureSVDModel (
320350 factors = 100 ,
321- tol = 1 ,
351+ tol = 1.0 ,
322352 maxiter = 100 ,
323353 random_state = random_state ,
324354 use_gpu = use_gpu ,
@@ -328,7 +358,7 @@ def test_get_config(self, random_state: tp.Optional[int], simple_types: bool, us
328358 expected = {
329359 "cls" : "PureSVDModel" if simple_types else PureSVDModel ,
330360 "factors" : 100 ,
331- "tol" : 1 ,
361+ "tol" : 1.0 ,
332362 "maxiter" : 100 ,
333363 "random_state" : random_state ,
334364 "use_gpu" : use_gpu ,
0 commit comments