source: branches/source_numpy_conversion/anuga/caching/test_caching.py @ 7177

Last change on this file since 7177 was 5909, checked in by rwilson, 16 years ago

NumPy? conversion.

File size: 24.6 KB
Line 
1import unittest
2import numpy
3
4from copy import deepcopy
5       
6from anuga.caching import *
7from anuga.caching.dummy_classes_for_testing import Dummy, Dummy_memorytest
8
9
10# Define a test function to be cached
11#
12def f(a,b,c,N,x=0,y='abcdefg'):
13  """f(a,b,c,N)
14     Do something time consuming and produce a complex result.
15  """
16
17  import string
18
19  B = []
20  for n in range(N):
21    s = str(n+2.0/(n + 4.0))+'.a'*10
22    B.append((a,b,c,s,n,x,y))
23  return(B)
24 
25def f_numeric(A, B):
26  """Operation on Numeric arrays
27  """
28 
29  return 3.1*A + B + 1
30 
31 
32def f_object(A, B):
33  """Operation of objecs of class Dummy
34  """
35 
36  return A.value+B.value, A.another+B.another
37 
38
39def f_generic(A):
40  return A
41 
42def clear_and_create_cache(Dummy, verbose=False):
43
44  a = cache(Dummy, 'clear', verbose=verbose)
45  a = cache(Dummy, args=(9,10),
46            verbose=verbose)
47     
48
49def retrieve_cache(Dummy, verbose=False):
50  if verbose: print 'Check that cache is there'
51 
52  X = cache(Dummy, args=(9,10), test=1,
53            verbose=verbose)     
54           
55  msg = 'Cached value was not found'
56  assert X is not None, msg
57
58     
59
60   
61
62class Test_Caching(unittest.TestCase):
63    def setUp(self):
64        set_option('verbose', 0)  #Why
65
66        pass
67
68    def tearDown(self):
69        pass
70
71    def test_simple(self):
72        """Check set_option (and switch stats off)
73        """
74
75        set_option('savestat', 0)
76        assert options['savestat'] == 0
77        set_option('verbose', 0)
78        assert options['verbose'] == 0       
79       
80
81    def test_basic_caching(self):
82
83        verbose=False
84        # Make some test input arguments
85        #
86        N = 5000  #Make N fairly small here
87
88        a = [1,2]
89        b = ('Thou shalt count the number three',4)
90        c = {'Five is right out': 6, (7,8): 9}
91        x = 3
92        y = 'holy hand granate'
93
94        # Test caching
95        #
96
97        comprange = 2
98
99        for comp in range(comprange):
100 
101            # Evaluate and store
102            #
103            T1 = cache(f, (a,b,c,N), {'x':x, 'y':y}, evaluate=1, \
104                       compression=comp, verbose=verbose)
105
106            # Retrieve
107            #                           
108            T2 = cache(f, (a,b,c,N), {'x':x, 'y':y}, compression=comp) 
109
110            # Reference result
111            #   
112            T3 = f(a,b,c,N,x=x,y=y)  # Compute without caching
113
114
115            assert T1 == T2, 'Cached result does not match computed result'
116            assert T2 == T3, 'Cached result does not match computed result'
117           
118
119           
120    def test_caching_of_numeric_arrays(self):
121        """test_caching_of_numeric_arrays
122       
123        Test that Numeric arrays can be recognised by caching even if their id's are different
124        """
125       
126        verbose = False
127       
128        # Make some test input arguments
129        A0 = numpy.arange(5)
130        B0 = numpy.array([1.1, 2.2, 0.0, -5, -5])
131       
132        A1 = A0.copy()
133        B1 = B0.copy()
134       
135        # Check that their ids are different
136        assert id(A0) != id(A1)
137        assert id(B0) != id(B1)       
138       
139       
140        # Test caching
141        comprange = 2
142        for comp in range(comprange):
143 
144            # Evaluate and store
145            T1 = cache(f_numeric, (A0, B0), evaluate=1,
146                       compression=comp, verbose=verbose)
147
148            # Retrieve
149            T2 = cache(f_numeric, (A1, B1), 
150                       compression=comp, test=1, verbose=verbose) 
151                       
152            # Check for presence of cached result
153            msg = 'Different array objects with same contents were not recognised'           
154            assert T2 is not None, msg
155
156            # Reference result
157            T3 = f_numeric(A0, B0) # Compute without caching
158
159
160            assert numpy.alltrue(T1 == T2), 'Cached result does not match computed result'
161            assert numpy.alltrue(T2 == T3), 'Cached result does not match computed result'
162           
163
164    def test_hash_collision(self):
165        """test_hash_collision(self):
166       
167        Test that hash collisons are dealt with correctly
168        """
169       
170        verbose = False
171       
172        # Make test input arguments
173        A0 = numpy.arange(5)*1.0
174        B = ('x', 15)
175       
176        # Create different A that hashes to the same address (having the same average)
177        A1 = numpy.array([2.0, 2.0, 2.0, 2.0, 2.0])       
178       
179        assert myhash(A0) == myhash(A1)
180           
181           
182        # Test caching
183        comprange = 2
184        for comp in range(comprange):
185       
186            # Clear
187            cache(f_numeric, (A0, A0), clear=1,
188                  compression=comp, verbose=verbose)       
189            cache(f_numeric, (A1, A1), clear=1,
190                  compression=comp, verbose=verbose)                         
191 
192 
193            # Evaluate and store
194            T1 = cache(f_numeric, (A0, A0), evaluate=1,
195                       compression=comp, verbose=verbose)
196
197           
198            # Check that A1 doesn't trigger retrieval of the previous result
199            # even though it hashes to the same address
200            T2 = cache(f_numeric, (A1, A1), 
201                       compression=comp, verbose=verbose) 
202           
203
204            print 'T1=%s' % str(T1)
205            print 'T2=%s' % str(T2)
206            assert numpy.alltrue(T2 != T1)
207
208           
209
210           
211           
212    def test_caching_of_dictionaries(self):
213        """test_caching_of_dictionaries
214       
215        Real example from ANUGA that caused some
216        hashing problems
217        """
218   
219
220        verbose = False #True
221       
222        D = {'point_attributes': None, 
223             'use_cache': True, 
224             'vertex_coordinates': None, 
225             'verbose': True, 
226             'max_read_lines': 500, 
227             'acceptable_overshoot': 1.01, 
228             'mesh': None, 
229             'data_origin': None, 
230             'alpha': 0.02, 
231             'mesh_origin': None, 
232             'attribute_name': None, 
233             'triangles': None}         
234       
235        DD = deepcopy(D) # Mangles the dictionary ordering
236       
237        assert myhash(DD) == myhash(D)
238
239        # Also test caching now that we are at it
240        comprange = 2
241        for comp in range(comprange):
242 
243            # Evaluate and store using D
244            T1 = cache(f_generic, D, evaluate=1,
245                       compression=comp, verbose=verbose)
246
247            # Retrieve using copy (DD)
248            T2 = cache(f_generic, DD, 
249                       compression=comp, test=1, verbose=verbose) 
250                       
251            # Check for presence of cached result
252            msg = 'Cached object was not found'           
253            assert T2 is not None, msg
254
255            # Reference result
256            T3 = f_generic(D) # Compute without caching
257
258           
259            msg = 'Cached result does not match computed result' 
260           
261            # Compare dictionaries
262            for key in T1:
263                assert T1[key] == T2[key]
264                assert T2[key] == T3[key]               
265               
266           
267           
268           
269
270    def test_caching_of_objects(self):
271        """test_caching_of_objects
272       
273        Test that Objecs can be recognised as input variabelse
274        by caching even if their id's are different
275        """
276   
277
278        verbose = False
279       
280        # Make some test input arguments
281        A0 = Dummy(5, 7)
282        B0 = Dummy(2.2, -5)
283       
284        A0.new_attribute = 'x' 
285        B0.new_attribute = 'x'       
286       
287        A1 = deepcopy(A0)
288        B1 = deepcopy(B0)       
289       
290        # Check that their ids are different
291        assert id(A0) != id(A1)
292        assert id(B0) != id(B1)       
293       
294       
295        # Test caching
296        comprange = 2
297        for comp in range(comprange):
298 
299            # Evaluate and store
300            T1 = cache(f_object, (A0, B0), evaluate=1,
301                       compression=comp, verbose=verbose)
302
303            # Retrieve
304            T2 = cache(f_object, (A1, B1), 
305                       compression=comp, 
306                       test=1, 
307                       verbose=verbose) 
308                       
309            # Check for presence of cached result
310            msg = 'Different objects with same attributes were not recognised'
311            assert T2 is not None, msg
312
313            # Reference result
314            T3 = f_object(A0, B0) # Compute without caching
315
316
317            assert T1 == T2, 'Cached result does not match computed result'
318            assert T2 == T3, 'Cached result does not match computed result'
319           
320
321    def test_caching_of_circular_structures(self):
322        """test_caching_of_circular_structures
323       
324        Test that Caching doesn't recurse infinitely in case of
325        circular or self-referencing structures
326        """
327       
328        verbose = False
329       
330        # Create input argument
331        A = Dummy(5, 7)
332        B = {'x': 10, 'A': A}
333        C = [B, 15]
334        A.value = C # Make it circular
335        A.x = [1,2,C,5,A] # More circular and self referential
336       
337        AA = deepcopy(A)
338
339        # Test caching
340        comprange = 2
341        for comp in range(comprange):
342 
343            # Evaluate and store
344            T1 = cache(f_generic, A, 
345                       evaluate=1,
346                       compression=comp, verbose=verbose)
347
348            # Retrieve
349            T2 = cache(f_generic, AA, 
350                       compression=comp, 
351                       test=1, verbose=verbose) 
352                       
353            # Check for presence of cached result
354            msg = 'Cached object was not found'           
355            assert T2 is not None, msg
356
357            # Reference result
358            T3 = f_generic(A) # Compute without caching
359
360           
361            msg = 'Cached result does not match computed result' 
362            assert str(T1) == str(T2), msg
363            assert str(T2) == str(T3), msg
364                                   
365           
366    def Xtest_caching_of_complex_circular_structure(self):
367        """test_caching_of_complex_circular_structure
368       
369        Test that Caching can handle a realistic
370        complex structure. This one is inspired by
371        ANUGA's Okushiri example, although reduced in size.
372        """
373       
374        pass
375
376       
377    def test_uniqueness_of_hash_values(self):
378        """test_uniqueness_of_hash_values(self):
379       
380        Test that Caching can handle a realistic
381        complex structure by hashing it consistently and
382        uniquely.
383        """
384       
385        verbose = False
386       
387        # Create input argument
388        A = Dummy(5, 7)
389        B = {'x': 10, 'A': A}
390        C = [B, numpy.array([1.2, 3, 5, 0.1])]
391        A.value = C # Make it circular
392
393        # Create identical but separate object   
394        AA = Dummy(None, None)
395        BB = {'A': AA, 'x': 10}
396        CC = [BB, numpy.array([1.200, 3.000, 5.00, 1.0/10])]
397        AA.value = CC # Make it circular
398        AA.another = 3+4       
399       
400       
401        assert myhash(A) == myhash(AA)     
402           
403           
404       
405        # Also test caching now that we are at it
406        comprange = 2
407        for comp in range(comprange):
408 
409            # Evaluate and store using A
410            T1 = cache(f_generic, A, evaluate=1,
411                       compression=comp, verbose=verbose)
412
413            # Retrieve using copy (AA)
414            T2 = cache(f_generic, AA, 
415                       compression=comp, test=1, verbose=verbose) 
416                       
417            # Check for presence of cached result
418            msg = 'Cached object was not found'           
419            assert T2 is not None, msg
420
421            # Reference result
422            T3 = f_generic(A) # Compute without caching
423
424           
425            msg = 'Cached result does not match computed result' 
426            assert str(T1) == str(T2), msg
427            assert str(T2) == str(T3), msg
428           
429           
430
431    def test_caching_of_simple_circular_dictionaries(self):
432        """test_caching_of_circular_structures
433       
434        Test that Caching doesn't recurse infinitely in case of
435        circular or self-referencing structures
436        """
437       
438        verbose = False #True
439       
440        # Create input argument
441        A = {'x': 10, 'B': None}
442        B = [A, 15]
443        A['B'] = B # Make it circular
444       
445        # Test caching
446        comprange = 2
447        for comp in range(comprange):
448 
449            # Evaluate and store
450            T1 = cache(f_generic, A, evaluate=1,
451                       compression=comp, verbose=verbose)
452                       
453
454            # Retrieve
455            T2 = cache(f_generic, A, 
456                       compression=comp, test=1, verbose=verbose) 
457                       
458            # Check for presence of cached result
459            msg = 'Cached object was not found'           
460            assert T2 is not None, msg
461
462            # Reference result
463            T3 = f_generic(A) # Compute without caching
464
465
466            msg = 'Cached result does not match computed result'
467            assert str(T1) == str(T2), msg
468            assert str(T2) == str(T3), msg
469
470           
471           
472    def test_cachefiles(self):
473        """Test existence of cachefiles
474        """       
475        N = 5000  #Make N fairly small here
476
477        a = [1,2]
478        b = ('Thou shalt count the number three',4)
479        c = {'Five is right out': 6, (7,8): 9}
480        x = 3
481        y = 'holy hand granate'
482
483       
484        FN = cache(f,(a,b,c,N), {'x':x, 'y':y}, verbose=0, \
485                  return_filename = 1)
486
487
488        assert FN[:2] == 'f['
489
490        CD = checkdir(cachedir)
491        compression = 1
492
493        (datafile,compressed0) = myopen(CD+FN+'_'+file_types[0],"rb",compression)
494        (argsfile,compressed1) = myopen(CD+FN+'_'+file_types[1],"rb",compression)
495        (admfile,compressed2) =  myopen(CD+FN+'_'+file_types[2],"rb",compression)
496
497        datafile.close()
498        argsfile.close()
499        admfile.close()
500
501    def test_test(self):       
502        """Test 'test' function when cache is present
503        """
504       
505        verbose = False
506       
507        N = 5 
508
509        a = [1,2]
510        b = ('Thou shalt count the number three',4)
511        c = {'Five is right out': 6, (7,8): 9}
512        x = 3
513        y = 'holy hand granate'
514       
515
516        T1 = cache(f, (a,b,c,N), {'x':x, 'y':y}, 
517                   evaluate=1, 
518                   verbose=verbose)
519       
520        T2 = cache(f, (a,b,c,N), {'x':x, 'y':y}, 
521                   test=1,
522                   verbose=verbose)
523                   
524                   
525        # Check for presence of cached result
526        msg = 'Different objects with same attributes were not recognised'
527        assert T2 is not None, msg                   
528                   
529        assert T1 == T2, "Option 'test' when cache file present failed"     
530
531
532    def test_clear(self):       
533        """Test that 'clear' works
534        """
535
536        N = 5000  #Make N fairly small here
537
538        a = [1,2]
539        b = ('Thou shalt count the number three',4)
540        c = {'Five is right out': 6, (7,8): 9}
541        x = 3
542        y = 'holy hand granate'
543       
544
545        T1 = cache(f,(a,b,c,N), {'x':x, 'y':y}, evaluate = 1)
546       
547       
548        cache(f, (a,b,c,N), {'x':x, 'y':y}, clear = 1)   
549
550 
551        # Test 'test' function when cache is absent
552       
553       
554        T4 = cache(f, (a,b,c,N), {'x':x, 'y':y}, test=1)
555        #print 'T4', T4
556        assert T4 is None, "Option 'test' when cache absent failed"
557
558
559    def test_dependencies(self):
560       
561        # Make a dependency file
562        CD = checkdir(cachedir)       
563
564        DepFN = CD + 'testfile.tmp'
565        DepFN_wildcard = CD + 'test*.tmp'
566        Depfile = open(DepFN,'w')
567        Depfile.write('We are the knights who say NI!')
568        Depfile.close()
569
570       
571        # Test
572        #
573
574        N = 5000  #Make N fairly small here
575
576        a = [1,2]
577        b = ('Thou shalt count the number three',4)
578        c = {'Five is right out': 6, (7,8): 9}
579        x = 3
580        y = 'holy hand granate'
581       
582        T1 = cache(f,(a,b,c,N), {'x':x, 'y':y}, dependencies=DepFN) 
583        T2 = cache(f,(a,b,c,N), {'x':x, 'y':y}, dependencies=DepFN)                     
584                       
585        assert T1 == T2, 'Dependencies do not work'
586
587
588        # Test basic wildcard dependency
589        T3 = cache(f,(a,b,c,N), {'x':x, 'y':y}, dependencies=DepFN_wildcard)                     
590   
591        assert T1 == T3, 'Dependencies with wildcards do not work'
592
593
594        # Test that changed timestamp in dependencies triggers recomputation
595 
596        # Modify dependency file
597        Depfile = open(DepFN,'a')
598        Depfile.write('You must cut down the mightiest tree in the forest with a Herring')
599        Depfile.close()
600 
601        T3 = cache(f,(a,b,c,N), {'x':x, 'y':y}, dependencies=DepFN, test = 1)
602       
603        assert T3 is None, 'Changed dependencies not recognised'
604 
605        # Test recomputation when dependencies have changed
606        #
607        T3 = cache(f,(a,b,c,N), {'x':x, 'y':y}, dependencies=DepFN)                       
608        assert T1 == T3, 'Recomputed value with changed dependencies failed'
609
610    #def test_performance(self):       
611    #    """Performance test (with statistics)
612    #    Don't really rely on this as it will depend on specific computer.
613    #    """
614    #
615    #    import time
616    #    set_option('savestat', 1)
617    #
618    #    N = 300000   #Should be large on fast computers...
619    #    a = [1,2]
620    #    b = ('Thou shalt count the number three',4)
621    #    c = {'Five is right out': 6, (7,8): 9}
622    #    x = 3
623    #    y = 'holy hand granate'
624    #     
625    #   
626    #    tt = time.time()
627    #    T1 = cache(f,(a,b,c,N), {'x':x, 'y':y})
628    #    t1 = time.time() - tt
629    #
630    #    tt = time.time()
631    #    T2 = cache(f,(a,b,c,N), {'x':x, 'y':y})
632    #    t2 = time.time() - tt
633     
634    #    assert T1 == T2
635    #     assert t1 > t2, 'WARNING: Performance a bit low - this could be specific to current platform. Try to increase N in this test'
636    #    #test_OK('Performance test: relative time saved = %s pct' \
637    #    #        %str(round((t1-t2)*100/t1,2)))
638
639
640    def test_statsfile(self):                   
641        """Test presence of statistics file
642        """
643        import os, string
644        statsfile  = '.cache_stat'  # Basefilename for cached statistics.
645       
646        CD = checkdir(cachedir)               
647        DIRLIST = os.listdir(CD)
648        SF = []
649        for FN in DIRLIST:
650            if string.find(FN,statsfile) >= 0:
651                try:
652                    fid = open(CD+FN,'r')
653                    fid.close()
654                except:
655                    raise 'Statistics files cannot be opened'         
656 
657         
658
659    # def test_network_cachedir(self):
660
661#         #set_option('cachedir', 'H:\\.python_cache\\')
662#         set_option('cachedir', 'V:\\2\\cit\\.python_cache\\')
663#         set_option('verbose', 1)
664
665       
666#         # Make some test input arguments
667#         #
668#         N = 5000  #Make N fairly small here
669
670#         a = [1,2]
671#         b = ('Thou shalt count the number three',4)
672#         c = {'Five is right out': 6, (7,8): 9}
673#         x = 3
674#         y = 'holy hand granate'
675
676#         # Test caching
677#         #
678
679#         comprange = 2
680
681#         for comp in range(comprange):
682 
683#             # Evaluate and store
684#             #
685#             T1 = cache(f, (a,b,c,N), {'x':x, 'y':y}, evaluate=1, \
686#                        compression=comp)
687
688#             # Retrieve
689#             #                           
690#             T2 = cache(f, (a,b,c,N), {'x':x, 'y':y}, compression=comp)
691
692#             # Reference result
693#             #   
694#             T3 = f(a,b,c,N,x=x,y=y)  # Compute without caching
695
696
697#             assert T1 == T2, 'Cached result does not match computed result'
698#             assert T2 == T3, 'Cached result does not match computed result'
699             
700
701
702    def Will_fail_test_objects(self):
703      """
704      This test shows how instances can't be effectively cached.
705      myhash uses hash which uses id which uses the memory address.
706     
707      This will be a NIL problem once caching can handle instances with different id's and
708      identical content.
709     
710      The test is disabled.
711      """
712     
713
714     
715      verbose = True
716      #verbose = False
717
718      for i in range(2):
719        if verbose: print "clear cache"
720        a = cache(Dummy, 'clear')
721       
722        if verbose: print "cache for first time"
723        a = cache(Dummy, args=(9,10), verbose=verbose)
724        hash_value = myhash(a)
725       
726        #print "hash_value",hash_value
727        if verbose: print "cache for second time"
728        a = cache(Dummy, args=(9,10), verbose=verbose)
729       
730        #print "myhash(a)",myhash(a)
731        assert hash_value == myhash(a)
732
733
734    # This test works in the caching dir and in anuga_core, but not in the
735    # anuga_core/source/anuga dir
736    # This has to do with pickle (see e.g. http://telin.ugent.be/~slippens/drupal/pickleproblem)
737    # The error message is
738    # PicklingError: Can't pickle test_caching.Dummy: it's not the same object as test_caching.Dummy
739    #
740    # This problem was fixed by moving the class into the separate module
741   
742    def test_objects_are_created(self):
743      """
744      This test shows how instances can be created from cache
745      as long as input arguments are unchanged.
746
747      Such instances will have different id's and cannot be currently be used as input
748      arguments in subsequent caches. However, this is still useful.
749
750      Do it for all combinations of compression
751
752      """
753
754      verbose = False
755
756      for compression_store in [False, True]:
757        for compression_retrieve in [False, True]:       
758       
759          if verbose: print 'clear cache'
760          a = cache(Dummy, 'clear')
761       
762          if verbose: print 'cache for first time'
763          a = cache(Dummy, args=(9,10),
764                    compression=compression_store,
765                    verbose=verbose)
766         
767          if verbose: print 'Check that cache is there'
768          assert cache(Dummy, args=(9,10), test=1,
769                       compression=compression_retrieve,
770                       verbose=verbose)
771
772
773
774    # NOTE (Ole): This test has been commented out because,
775    #             although the test will pass (not anymore!)
776    #             inside the caching dir and also at the anuga_core level,
777    #             it won't pass at the anuga_core/source/anuga level.
778    # It may have to do with the comments above.
779    #
780    # But this is a very nice test to run occasionally within the caching
781    # area
782    def Xtest_objects_are_created_memory(self):
783      """
784     
785      This test shows how instances can be created from cache
786      as long as input arguments are unchanged - even if the class
787      lives in different memory locations.
788
789      This is using cache created in the main program below
790      """
791
792      verbose = True #False
793
794      # Redefine class Dummy_memorytest
795      class Dummy_memorytest:
796        def __init__(self, value, another):
797          self.value = value     
798
799      # Make sure that class has been redefined to another address
800      print
801      print 'Initial_addr  ', initial_addr
802      print 'Redefined addr', `Dummy_memorytest`
803      msg = 'Redefined class ended up at same memory location as '
804      msg += 'original class making this test irrelevant. Try to run '
805      msg += 'it again and see if this error goes away.'
806      msg += 'If it persists contact Ole.Nielsen@ga.gov.au'
807      assert initial_addr != `Dummy_memorytest`, msg   
808
809     
810      retrieve_cache(Dummy_memorytest, verbose=verbose)     
811         
812# Cache created for use with 'test_objects_are_created_memory'
813#initial_addr = `Dummy_memorytest`
814#clear_and_create_cache(Dummy_memorytest, verbose=False)
815 
816
817
818#-------------------------------------------------------------
819if __name__ == "__main__":
820    #suite = unittest.makeSuite(Test_Caching, 'test_hash_collision')
821    suite = unittest.makeSuite(Test_Caching, 'test')
822    runner = unittest.TextTestRunner()
823    runner.run(suite)
Note: See TracBrowser for help on using the repository browser.