Custom Per-Problem Optimizers¶
There are situations when it is desirable to use an optimization algorithm that is custom-tailored towards a specific optimization problem. It doesn’t make sense to offer this algorithm for all problems, e.g. because its defaults are specially adjusted for only that problem.
For this purpose, you can declare a custom optimizer provider. This provider is queried by host applications when putting together the list of available optimization algorithms. The optimizers that it returns are added to the list if the provider is appropriate for the selected optimization problem.
There are two ways to declare a custom optimizer provider:
Your optimization problem defines the
get_optimizers()
method of theCustomOptimizerProvider
protocol.You define an an entry point in the group
cernml.custom_optimizers
whose name is the registry ID of the matching optimization problem. This entry point should either point at a subclass ofCustomOptimizerProvider
or at a bare function that acts likeget_optimizers()
.
Examples for both approaches are shown below.
# mypackage/__init__.py
from cernml import coi
class MyEnv1(coi.SingleOptimizable, coi.CustomOptimizerProvider):
# ...
@classmethod
def get_optimizers(cls):
return {"MyCustomOptimizer-v1": ...}
coi.register("MyOptimizationProblem-v1", entry_point=MyEnv1)
class MyEnv2(coi.OptEnv, coi.CustomOptimizerProvider):
# ...
@classmethod
def get_optimizers(cls):
return {"MyCustomOptimizer-v2": ...}
coi.register("MyOptimizationProblem-v2", entry_point=MyEnv2)
# pyproject.toml
[project.entry-points.'cernml.envs']
MyNamespace = 'mypackage'
[project.entry-points.'cernml.custom_optimizers']
'MyNamespace/MyOptimizationProblem-v1' = 'mypackage:ProviderClass'
'MyNamespace/MyOptimizationProblem-v2' = 'mypackage:provider_func'
# mypackage/__init__.py
from cernml import coi
class MyEnv1(coi.SingleOptimizable): ...
coi.register("MyNamespace/MyEnv1", MyEnv1)
class MyEnv2(coi.OptEnv): ...
coi.register("MyNamespace/MyEnv2", MyEnv2)
class ProviderClass(coi.CustomOptimizerProvider):
@classmethod
def get_optimizers(cls):
return {"MyCustomOptimizer-v1": ...}
def provider_func():
return {"MyCustomOptimizer-v2": ...}
# setup.cfg
[options.entry_points]
cernml.envs =
MyNamespace = mypackage
cernml.custom_optimizers =
MyNamespace/MyOptimizationProblem-v1 = mypackage:ProviderClass
MyNamespace/MyOptimizationProblem-v2 = mypackage:provider_func
# mypackage/__init__.py
from cernml import coi
class MyEnv1(coi.SingleOptimizable): ...
coi.register("MyNamespace/MyEnv1", MyEnv1)
class MyEnv2(coi.OptEnv): ...
coi.register("MyNamespace/MyEnv2", MyEnv2)
class ProviderClass(coi.CustomOptimizerProvider):
@classmethod
def get_optimizers(cls):
return {"MyCustomOptimizer-v1": ...}
def provider_func():
return {"MyCustomOptimizer-v2": ...}
# setup.py
from setuptools import setup
# ...
setup(
# ...,
entry_points={
"cernml.envs": [
"MyNamespace = mypackage",
],
"cernml.custom_optimizers": [
"MyNamespace/MyOptimizationProblem-v1 = mypackage:ProviderClass",
"MyNamespace/MyOptimizationProblem-v2 = mypackage:provider_func",
],
},
)
# mypackage/__init__.py
from cernml import coi
class MyEnv1(coi.SingleOptimizable): ...
coi.register("MyNamespace/MyEnv1", MyEnv1)
class MyEnv2(coi.OptEnv): ...
coi.register("MyNamespace/MyEnv2", MyEnv2)
class ProviderClass(coi.CustomOptimizerProvider):
@classmethod
def get_optimizers(cls):
return {"MyCustomOptimizer-v1": ...}
def provider_func():
return {"MyCustomOptimizer-v2": ...}
Custom Per-Environment Policies¶
Similar to the custom optimizer providers, there are many cases in RL where an algorithm is only really applicable to a single environment. For this purpose, you can declare a custom policy provider. It works very similar to optimizer providers, but differs in some aspects of its API.
There are two ways to declare a custom policy provider:
Your environment problem defines the
get_policy_names()
andload_policy()
methods of theCustomPolicyProvider
abstract base class.You define an an entry point in the group
cernml.custom_policies
that has the sameregistry
name as the environment that it is appropriate for. This entry point should point at a subclass ofCustomPolicyProvider
and that class should be instantiable by calling it with no arguments.
Examples for both approaches are shown below.
# pyproject.toml
[project.entry-points.'cernml.envs']
MyEnv-v1 = 'mypackage:MyEnv1'
[project.entry-points.'cernml.custom_policies']
MyEnv-v1 = 'mypackage:ProviderClass'
# mypackage/__init__.py
import gym
from stable_baselines3 import TD3
from cernml import coi
class MyEnv1(gym.Env): ...
class ProviderClass(coi.CustomPolicyProvider):
@classmethod
def get_policy_names(cls):
return ["MyCustomPolicy-v1", ...]
def load_policy(self, name):
filename = ...
return TD3.load(filename)
# setup.cfg
[options.entry_points]
cernml.envs =
MyEnv-v1 = mypackage:MyEnv1
cernml.custom_policies =
MyEnv-v1 = mypackage:ProviderClass
# mypackage/__init__.py
import gym
from cernml import coi
class MyEnv1(gym.Env): ...
class ProviderClass(coi.CustomPolicyProvider):
@classmethod
def get_policy_names(cls):
return ["MyCustomPolicy-v1", ...]
def load_policy(self, name):
filename = ...
return TD3.load(filename)
# setup.py
from setuptools import setup
# ...
setup(
# ...,
entry_points={
"cernml.envs": [
"MyEnv-v1 = mypackage:MyEnv1",
],
"cernml.custom_policies": [
"MyEnv-v1 = mypackage:ProviderClass",
],
},
)
# mypackage/__init__.py
import gym
from cernml import coi
class MyEnv1(gym.Env): ...
class ProviderClass(coi.CustomPolicyProvider):
@classmethod
def get_policy_names(cls):
return ["MyCustomPolicy-v1", ...]
def load_policy(self, name):
filename = ...
return TD3.load(filename)
# mypackage/__init__.py
import gym
from cernml import coi
class MyEnv1(gym.Env, coi.CustomPolicyProvider):
# ...
@classmethod
def get_policy_names(cls):
return ["MyCustomPolicy-v1", ...]
def load_policy(self, name):
filename = ...
return TD3.load(filename)
coi.register("MyEnv-v1", entry_point=MyEnv1)