Passed
Pull Request — master (#110)
by
unknown
02:49 queued 01:19
created

experiments   A

Complexity

Total Complexity 5

Size/Duplication

Total Lines 248
Duplicated Lines 0 %

Importance

Changes 0
Metric Value
wmc 5
eloc 31
dl 0
loc 248
rs 10
c 0
b 0
f 0

5 Methods

Rating   Name   Duplication   Size   Complexity  
A MyExperiment.get_test_params() 0 26 1
A MyExperiment._score() 0 23 1
A MyExperiment._paramnames() 0 11 1
A MyExperiment.__init__() 0 10 1
A MyExperiment._get_score_params() 0 17 1
1
# copyright: hyperactive developers, MIT License (see LICENSE file)
2
"""Extension template for experiments.
3
4
Purpose of this implementation template:
5
    quick implementation of new estimators following the template
6
    NOT a concrete class to import! This is NOT a base class or concrete class!
7
    This is to be used as a "fill-in" coding template.
8
9
How to use this implementation template to implement a new estimator:
10
- make a copy of the template in a suitable location, give it a descriptive name.
11
- work through all the "todo" comments below
12
- fill in code for mandatory methods, and optionally for optional methods
13
- do not write to reserved variables: _tags, _tags_dynamic
14
- you can add more private methods, but do not override BaseEstimator's private methods
15
    an easy way to be safe is to prefix your methods with "_custom"
16
- change docstrings for functions and the file
17
- ensure interface compatibility by hyperactive.utils.check_estimator
18
- once complete: use as a local library, or contribute to hyperactive via PR
19
20
Mandatory methods:
21
    scoring         - _score(self, params: dict) -> np.float64
22
    parameter names - _paramnames(self) -> list[str]
23
24
Testing - required for automated test framework and check_estimator usage:
25
    get default parameters for test instance(s) - get_test_params()
26
"""
27
# todo: write an informative docstring for the file or module, remove the above
28
# todo: add an appropriate copyright notice for your estimator
29
#       estimators contributed should have the copyright notice at the top
30
#       estimators of your own do not need to have permissive or MIT copyright
31
32
# todo: uncomment the following line, enter authors' GitHub IDs
33
# __author__ = [authorGitHubID, anotherAuthorGitHubID]
34
35
from hyperactive.base import BaseExperiment
36
37
# todo: add any necessary imports here
38
39
# todo: for imports of soft dependencies:
40
# make sure to fill in the "python_dependencies" tag with the package import name
41
# import soft dependencies only inside methods of the class, not at the top of the file
42
43
44
class MyExperiment(BaseExperiment):
45
    """Custom experiment. todo: write docstring.
46
47
    todo: describe your custom experiment here
48
49
    Parameters
50
    ----------
51
    parama : int
52
        descriptive explanation of parama
53
    paramb : string, optional (default='default')
54
        descriptive explanation of paramb
55
    paramc : boolean, optional (default=MyOtherEstimator(foo=42))
56
        descriptive explanation of paramc
57
    and so on
58
59
    Examples
60
    --------
61
    >>> from somehwere import MyExperiment
62
    >>> great_example(code)
63
    >>> multi_line_expressions(
64
    ...     require_dots_on_new_lines_so_that_expression_continues_properly
65
    ... )
66
    """
67
68
    # todo: fill in tags - most tags have sensible defaults below
69
    _tags = {
70
        # tags and full specifications are available in the tag API reference
71
        # TO BE ADDED
72
        #
73
        # property tags: subtype
74
        # ----------------------
75
        #
76
        "property:randomness": "random",
77
        # valid values: "random", "deterministic"
78
        # if "deterministic", two calls of score must result in the same value
79
        #
80
        # --------------
81
        # packaging info
82
        # --------------
83
        #
84
        # ownership and contribution tags
85
        # -------------------------------
86
        #
87
        # author = author(s) of th estimator
88
        # an author is anyone with significant contribution to the code at some point
89
        "authors": ["author1", "author2"],
90
        # valid values: str or list of str, should be GitHub handles
91
        # this should follow best scientific contribution practices
92
        # scope is the code, not the methodology (method is per paper citation)
93
        # if interfacing a 3rd party estimator, ensure to give credit to the
94
        # authors of the interfaced estimator
95
        #
96
        # maintainer = current maintainer(s) of the estimator
97
        # per algorithm maintainer role, see governance document
98
        # this is an "owner" type role, with rights and maintenance duties
99
        # for 3rd party interfaces, the scope is the class only
100
        "maintainers": ["maintainer1", "maintainer2"],
101
        # valid values: str or list of str, should be GitHub handles
102
        # remove tag if maintained by package core team
103
        #
104
        # dependency tags: python version and soft dependencies
105
        # -----------------------------------------------------
106
        #
107
        # python version requirement
108
        "python_version": None,
109
        # valid values: str, PEP 440 valid python version specifiers
110
        # raises exception at construction if local python version is incompatible
111
        # delete tag if no python version requirement
112
        #
113
        # soft dependency requirement
114
        "python_dependencies": None,
115
        # valid values: str or list of str, PEP 440 valid package version specifiers
116
        # raises exception at construction if modules at strings cannot be imported
117
        # delete tag if no soft dependency requirement
118
    }
119
120
    # todo: add any hyper-parameters and components to constructor
121
    def __init__(self, parama, paramb="default", paramc=None):
122
        # todo: write any hyper-parameters to self
123
        self.parama = parama
124
        self.paramb = paramb
125
        self.paramc = paramc
126
        # IMPORTANT: the self.params should never be overwritten or mutated from now on
127
        # for handling defaults etc, write to other attributes, e.g., self._parama
128
129
        # leave this as is
130
        super().__init__()
131
132
        # todo: optional, parameter checking logic (if applicable) should happen here
133
        # if writes derived values to self, should *not* overwrite self.parama etc
134
        # instead, write to self._parama, self._newparam (starting with _)
135
136
    # todo: implement this, mandatory
137
    def _paramnames(self):
138
        """Return the parameter names of the search.
139
140
        Returns
141
        -------
142
        list of str
143
            The parameter names of the search parameters.
144
        """
145
        # for every instance, this should return the correct parameter names
146
        # i.e., the maximal set of keys of the dict expected by _score
147
        return ["score_param1", "score_param2"]
148
149
    # todo: implement this, mandatory
150
    def _score(self, params):
151
        """Score the parameters.
152
153
        Parameters
154
        ----------
155
        params : dict with string keys
156
            Parameters to score.
157
158
        Returns
159
        -------
160
        float
161
            The score of the parameters.
162
        dict
163
            Additional metadata about the search.
164
        """
165
        # params is a dictionary with keys being paramnames or subset thereof
166
        # IMPORTANT: avoid side effects to params!
167
        #
168
        # the method may work if only a subste of the parameters in paramnames is passed
169
        # but this is not necessary
170
        value = 42  # must be numpy.float64
171
        metadata = {"some": "metadata"}  # can be any dict
172
        return value, metadata
173
174
175
    # todo: implement this for testing purposes!
176
    #   required to run local automated unit and integration testing of estimator
177
    #   method should return default parameters, so that a test instance can be created
178
    @classmethod
179
    def get_test_params(cls, parameter_set="default"):
180
        """Return testing parameter settings for the estimator.
181
182
        Parameters
183
        ----------
184
        parameter_set : str, default="default"
185
            Name of the set of test parameters to return, for use in tests. If no
186
            special parameters are defined for a value, will return `"default"` set.
187
            There are currently no reserved values for this type of estimator.
188
189
        Returns
190
        -------
191
        params : dict or list of dict, default = {}
192
            Parameters to create testing instances of the class
193
            Each dict are parameters to construct an "interesting" test instance, i.e.,
194
            `MyClass(**params)` or `MyClass(**params[i])` creates a valid test instance.
195
            `create_test_instance` uses the first (or only) dictionary in `params`
196
        """
197
        # todo: set the testing parameters for the estimators
198
        # Testing parameters can be dictionary or list of dictionaries.
199
        # Testing parameter choice should cover internal cases well.
200
        #   for "simple" extension, ignore the parameter_set argument.
201
        paramset1 = {"parama": 0, "paramb": "default", "paramc": None}
202
        paramset2 = {"parama": 1, "paramb": "foo", "paramc": 42}
203
        return [paramset1, paramset2]
204
205
        # this method can, if required, use:
206
        #   class properties (e.g., inherited); parent class test case
207
        #   imported objects such as estimators from sklearn
208
        # important: all such imports should be *inside get_test_params*, not at the top
209
        #            since imports are used only at testing time
210
        #
211
        # A good parameter set should primarily satisfy two criteria,
212
        #   1. Chosen set of parameters should have a low testing time,
213
        #      ideally in the magnitude of few seconds for the entire test suite.
214
        #       This is vital for the cases where default values result in
215
        #       "big" models which not only increases test time but also
216
        #       run into the risk of test workers crashing.
217
        #   2. There should be a minimum two such parameter sets with different
218
        #      sets of values to ensure a wide range of code coverage is provided.
219
        #
220
        # example 1: specify params as dictionary
221
        # any number of params can be specified
222
        # params = {"est": value0, "parama": value1, "paramb": value2}
223
        #
224
        # example 2: specify params as list of dictionary
225
        # note: Only first dictionary will be used by create_test_instance
226
        # params = [{"est": value1, "parama": value2},
227
        #           {"est": value3, "parama": value4}]
228
        #
229
        # return params
230
231
    @classmethod
232
    def _get_score_params(self):
233
        """Return settings for testing the score function. Used in tests only.
234
235
        Returns a list, the i-th element corresponds to self.get_test_params()[i].
236
        It should be a valid call for self.score.
237
238
        Returns
239
        -------
240
        list of dict
241
            The parameters to be used for scoring.
242
        """
243
        # dict keys should be same as paramnames return
244
        # or subset, only if _score allows for subsets of parameters
245
        score_params1 = {"score_param1": "foo", "score_param2": "bar"}
246
        score_params2 = {"score_param1": "baz", "score_param2": "qux"}
247
        return [score_params1, score_params2]
248