Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Cache Bug Lurking #779

Closed
marcharper opened this issue Dec 3, 2016 · 2 comments
Closed

Cache Bug Lurking #779

marcharper opened this issue Dec 3, 2016 · 2 comments

Comments

@marcharper
Copy link
Member

marcharper commented Dec 3, 2016

We have to instantiate the strategy before checking if it's stochastic since various strategies have parameters that could change the classification. This could easily be true for the other classifiers such as memory depth and even say length but I'm not sure if there are instances of the latter.

We need to change the cache to use the strategy names as keys and to check the instances for stochasticity rather than the class classifiers. I don't know if this is yet causing problems since we tend to dynamically generate the cache in the Match class now but there is certainly the potential for a problem. Right now I think Matches are ok because _cache_update_required is properly checking stochasticity (the players are already instantiated). But the DeterministicCache is not quite doing the same thing -- it's checking the lass classifier rather than the instance classifier. Hopefully the deterministic repetition tests are keeping us safe at the moment.

This is the line that I'm worried about:

        if key[0].classifier['stochastic'] or key[1].classifier['stochastic']:
            return False

Right now key[0] and key[1] are player classes and should be instantiated player names. We could also have an issue with multiple instances of the same class (one stochastic, one not):

print (str(axl.GTFT(0)))
print (str(axl.GTFT(0.5)))
print (str(axl.GTFT(0).__class__))
print (str(axl.GTFT(0.5).__class__))
GTFT: 0
GTFT: 0.5
<class 'axelrod.strategies.memoryone.GTFT'>
<class 'axelrod.strategies.memoryone.GTFT'>

Since:

print (str(axl.GTFT(0).classifier['stochastic']))
print (str(axl.GTFT(0.5).classifier['stochastic']))
print (str(axl.GTFT(0).__class__.classifier['stochastic']))
print (str(axl.GTFT(0.5).__class__.classifier['stochastic']))
False
True
True
True

The other issue is key collision:

print (str(axl.GTFT(0)))
print (str(axl.GTFT(1)))
print (str(axl.GTFT(0).__class__))
print (str(axl.GTFT(1).__class__))
GTFT: 0
GTFT: 1
<class 'axelrod.strategies.memoryone.GTFT'>
<class 'axelrod.strategies.memoryone.GTFT'>

Those are both deterministic and different!

print (str(axl.GTFT(0).classifier['stochastic']))
print (str(axl.GTFT(1).classifier['stochastic']))
print (str(axl.GTFT(0).__class__.classifier['stochastic']))
print (str(axl.GTFT(1).__class__.classifier['stochastic']))
False
False
True
True
@marcharper marcharper changed the title Potential Cache Bug Lurking Cache Bug Lurking Dec 3, 2016
@drvinceknight
Copy link
Member

We need to change the cache to use the strategy names as keys and to check the instances for stochasticity rather than the class classifiers.

I'll look at this more closely tomorrow (if a fix hasn't appeared) but that fix seems straightforward enough?

As you say, I think we're mainly ok here because of how the cache is now being used (famous last words!).

@drvinceknight
Copy link
Member

Some code showing where this currently creates a problem (working on a fix):

>>> p1 = axl.MemoryOnePlayer((0,0,0,0))
>>> p2 = axl.MemoryOnePlayer((1,0,1,0))
>>> p3 = axl.MemoryOnePlayer((1,0,1,1))
>>> p1, p2, p3
(Generic Memory One Player,
 Generic Memory One Player,
 Generic Memory One Player)

The above creates 3 players that are not stochastic:

>>> p1.classifier['stochastic'], p2.classifier['stochastic'], p3.classifier['stochastic']
(False, False, False)

Their class is considered stochastic:

>>> p1.__class__.classifier['stochastic'],p2.__class__.classifier['stochastic'],p3.__class__.classifier['stochastic']
(True, True, True)

The Match class then falls over:

>>> m = axl.Match((p1, p2), turns=3)
>>> m.play()
---------------------------------------------------------------------------
ValueError                                Traceback (most recent call last)
<ipython-input-15-c27da921d486> in <module>()
      1 m = axl.Match((p1, p2), turns=3)
----> 2 m.play()

/home/vince/src/Axelrod/axelrod/match.py in play(self)
    124 
    125             if self._cache_update_required:
--> 126                 self._cache[self._cache_key] = result
    127         else:
    128             result = self._cache[self._cache_key]

/home/vince/src/Axelrod/axelrod/deterministic_cache.py in __setitem__(self, key, value)
     55         if not self._is_valid_key(key):
     56             raise ValueError(
---> 57                 'Key must be a tuple of 2 deterministic axelrod Player classes and an integer')
     58 
     59         if not self._is_valid_value(value):

ValueError: Key must be a tuple of 2 deterministic axelrod Player classes and an integer

This is because the valid key check in the cache checks the cache but the Match checks the players (the match is correct).

Note this example shows another error; the name of the Memory one players doesn't take the parameters.

drvinceknight added a commit that referenced this issue Dec 4, 2016
Closes #779

This makes the underlying read/write of the cache be the string repr.
The passed key is the player instances (as opposed to previously going
to have to go get the class).
drvinceknight pushed a commit that referenced this issue Dec 4, 2016
marcharper added a commit that referenced this issue Dec 4, 2016
marcharper added a commit that referenced this issue Dec 4, 2016
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

2 participants