Use Observation Functions

Using any environment, the observation 1 recieved by the user to take the next action can be customized changing the ObservationFunction used by the solver. The environment is not extracting data directly but delegates that responsibility to an ObservationFunction object. The object has complete access to the solver and extract the data it needs.

Using a different observation function is as easy as passing it as a parameter when creating an environment. For instance with the Branching environment:

>>> env = ecole.environment.Branching(observation_function=ecole.observation.Nothing())
>>> env.observation_function  
ecole.observation.Nothing()
>>> obs, _, _, _, _ = env.reset("path/to/problem")
>>> obs is None
True

Environments have an observation function set as default parameter for convenience.

>>> env = ecole.environment.Branching()
>>> env.observation_function  
ecole.observation.NodeBipartite()
>>> obs, _, _, _, _ = env.reset("path/to/problem")
>>> obs  
ecole.observation.NodeBipartiteObs(...)

See the reference for the list of available observation function, as well as the documention for explanation on how to create one.

No Observation Function

To not use any observation function, for instance for a learning with a bandit algorithm, explicitly pass None to the environment constructor.

>>> env = ecole.environment.Branching(observation_function=None)
>>> env.observation_function  
ecole.observation.nothing()
>>> obs, _, _, _, _= env.reset("path/to/problem")
>>> obs is None
True

Multiple Observation Functions

To use multiple observation functions, wrap them in a list or dict.

>>> obs_func = {
...    "some_name": ecole.observation.NodeBipartite(),
...    "other_name": ecole.observation.Nothing(),
... }
>>> env = ecole.environment.Branching(observation_function=obs_func)
>>> obs, _, _, _, _ = env.reset("path/to/problem")
>>> obs  
{'some_name': ecole.observation.NodeBipartiteObs(), 'other_name': None}

Similarily with a tuple

>>> obs_func = (
...    ecole.observation.NodeBipartite(), ecole.observation.Nothing()
... )
>>> env = ecole.environment.Branching(observation_function=obs_func)
>>> obs, _, _, _, _ = env.reset("path/to/problem")
>>> obs  
[ecole.observation.NodeBipartiteObs(), None]
1

We chose to use observation, according to the Partially Observable Markov Decision Process, because the state is really the whole state of the solver.