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

Move classifiers outside of Players #1300

Merged
merged 43 commits into from
Apr 7, 2020

Conversation

gaffney2010
Copy link
Member

I want to make it so that we can allow classifiers to be a function.

This will allow us to store the result of:
#740
to a classifier after we program.

It will also allow us to program in the memory-depth of FSMs that I had done previously; currently, we just ran and copied the values in.

There are probably other places where this functionality will help.

I thought about replacing the default_classifier dict with a dict whose values are lambdas. But decided against this because 1) it would really clutter the Player code and spread out classifier logic when we want to handle different strategies differently, and 2) it would make memoizing difficult (see below).

I decided to move the classifiers out of the Player class, and keep a table lookup in a singleton class called Classifiers. I made a function that runs all classifiers on all strategies and saves the result to a yaml function; Classifiers reads from this. This is necessary for classifiers that take a while to calculate. (This was the problem with memory-depth on FSMs.) I did a yaml file so that developers can check the calculated classifiers.

I continue to use the classifier dict in the strategies, where it exists, with the new logic as more of a replacement of the default classifier. This makes the change backwards compatible, but is also necessary for factory-generated strategies. I store the classifiers to the yaml also, which isn't necessary, but provides a nice check.

Most of the change here is switching classifier lookups to use the Classifiers class. This isn't technically necessary, because of the backwards compatibility, but this is how we should lookup.

@marcharper
Copy link
Member

Do the tests pass locally?

Copy link
Member

@marcharper marcharper left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you also run black on these commits (if not done so already)? I'm surprised that so many indentations changed. It's fine if that's what black did.

axelrod/classifier.py Outdated Show resolved Hide resolved
axelrod/classifier.py Outdated Show resolved Hide resolved
axelrod/classifier.py Outdated Show resolved Hide resolved
axelrod/classifier.py Outdated Show resolved Hide resolved
axelrod/classifier.py Show resolved Hide resolved
axelrod/classifier.py Outdated Show resolved Hide resolved
axelrod/classifier.py Outdated Show resolved Hide resolved
axelrod/classifier.py Outdated Show resolved Hide resolved
axelrod/classifier.py Outdated Show resolved Hide resolved
axelrod/classifier.py Outdated Show resolved Hide resolved
requirements.txt Outdated Show resolved Hide resolved
@drvinceknight
Copy link
Member

@gaffney2010
Copy link
Member Author

This is great feedback. Thanks!

axelrod/match.py Outdated Show resolved Hide resolved
@marcharper
Copy link
Member

Looks really nice. I left a few minor comments/questions but otherwise I'm happy with it.

@drvinceknight
Copy link
Member

Thanks for my requested modifications. Looks good to me 👍

@drvinceknight
Copy link
Member

The failing test looks like it's got something to do with the filter set:

    self.assertTrue(passes_filterset(self.TestStrategy, full_passing_filterset_1))
164  File "C:\projects\axelrod\axelrod\strategies\_filters.py", line 198, in passes_filterset
165    passes_filters.append(filter_function.function(**kwargs))
166  File "C:\projects\axelrod\axelrod\strategies\_filters.py", line 49, in passes_operator_filter
167    return operator(classifier_value, value)
168TypeError: '>=' not supported between instances of 'NoneType' and 'int'
169
170======================================================================
171FAIL: test_filtered_strategies (axelrod.tests.unit.test_filters.TestFilters)
172----------------------------------------------------------------------
173Traceback (most recent call last):
174  File "C:\projects\axelrod\axelrod\tests\unit\test_filters.py", line 160, in test_filtered_strategies
175    [StochasticTestStrategy, UsesLengthTestStrategy],
176AssertionError: Lists differ: [] != [<class 'axelrod.tests.unit.test_filters.T[177 chars]gy'>]
177
178Second list contains 2 additional elements.
179First extra element 0:
180<class 'axelrod.tests.unit.test_filters.TestFilters.test_filtered_strategies.<locals>.StochasticTestStrategy'>
181
182- []
183+ [<class 'axelrod.tests.unit.test_filters.TestFilters.test_filtered_strategies.<locals>.StochasticTestStrategy'>,
184+  <class 'axelrod.tests.unit.test_filters.TestFilters.test_filtered_strategies.<locals>.UsesLengthTestStrategy'>]

@marcharper
Copy link
Member

======================================================================
ERROR: test_will_lookup_key_for_classes_that_cant_init (axelrod.tests.unit.test_classification.TestClassification)
----------------------------------------------------------------------
Traceback (most recent call last):
  File "C:\projects\axelrod\axelrod\classifier.py", line 192, in classify_player_for_this_classifier
    player = player()
TypeError: __init__() missing 1 required positional argument: 'x'
During handling of the above exception, another exception occurred:
Traceback (most recent call last):
  File "C:\projects\axelrod\axelrod\tests\unit\test_classification.py", line 85, in test_will_lookup_key_for_classes_that_cant_init
    self.assertEqual(Classifiers["memory_depth"](TitForTatWithNonTrivialInitialzer), 1)
  File "C:\projects\axelrod\axelrod\classifier.py", line 201, in classify_player_for_this_classifier
    "Passed player class doesn't have a trivial initializer."
Exception: Passed player class doesn't have a trivial initializer.
----------------------------------------------------------------------

Also the warning comes up a lot during the tests, perhaps we want to suppress it, or change various invocations to use an instance instead.

@gaffney2010
Copy link
Member Author

I don't know how I'm supposed to get that last line of coverage. I can do something hacky...

Copy link
Member

@drvinceknight drvinceknight left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

axelrod/tests/unit/test_classification.py Outdated Show resolved Hide resolved
Co-Authored-By: Vince Knight <vincent.knight@gmail.com>
@drvinceknight
Copy link
Member

Thanks @gaffney2010 looks good 👍

@drvinceknight drvinceknight merged commit ecad2a1 into Axelrod-Python:master Apr 7, 2020
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

4 participants