Skip to content

Commit cd6dcd9

Browse files
committed
extension templates, docstrings, small uniformization
1 parent 415db9d commit cd6dcd9

10 files changed

Lines changed: 520 additions & 11 deletions

File tree

extension_templates/experiments.py

Lines changed: 247 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,247 @@
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]

0 commit comments

Comments
 (0)