Skip to content

Move classifiers outside of Players#1300

Merged
drvinceknight merged 43 commits into
Axelrod-Python:masterfrom
gaffney2010:classifiers
Apr 7, 2020
Merged

Move classifiers outside of Players#1300
drvinceknight merged 43 commits into
Axelrod-Python:masterfrom
gaffney2010:classifiers

Conversation

@gaffney2010

Copy link
Copy Markdown
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
Copy Markdown
Member

Do the tests pass locally?

@marcharper marcharper left a comment

Copy link
Copy Markdown
Member

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.

Comment thread axelrod/classifier.py Outdated
Comment thread axelrod/classifier.py Outdated
Comment thread axelrod/classifier.py Outdated
Comment thread axelrod/classifier.py Outdated
Comment thread axelrod/classifier.py
Comment thread axelrod/classifier.py Outdated
Comment thread axelrod/classifier.py Outdated
Comment thread axelrod/classifier.py Outdated
Comment thread axelrod/classifier.py Outdated
Comment thread axelrod/classifier.py Outdated
Comment thread docs/tutorials/contributing/strategy/adding_the_new_strategy.rst Outdated
Comment thread docs/tutorials/advanced/classification_of_strategies.rst Outdated
Comment thread docs/tutorials/advanced/classification_of_strategies.rst Outdated
Comment thread requirements.txt Outdated
@drvinceknight

Copy link
Copy Markdown
Member

@gaffney2010

Copy link
Copy Markdown
Member Author

This is great feedback. Thanks!

Comment thread axelrod/tests/unit/test_classification.py Outdated
Comment thread axelrod/classifier.py
Comment thread axelrod/classifier.py
Comment thread axelrod/match.py Outdated
@marcharper

Copy link
Copy Markdown
Member

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

@drvinceknight

Copy link
Copy Markdown
Member

Thanks for my requested modifications. Looks good to me 👍

@drvinceknight

Copy link
Copy Markdown
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
Copy Markdown
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
Copy Markdown
Member Author

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

@drvinceknight drvinceknight left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Comment thread axelrod/tests/unit/test_classification.py Outdated
Co-Authored-By: Vince Knight <vincent.knight@gmail.com>
@drvinceknight

Copy link
Copy Markdown
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.

4 participants