11import numpy as np
22
33from . import BaseAgent
4+ from ..exploration import EpsilonGreedy , ExplorationStrategy , Softmax
45
56
67class BaseGreedyAgent (BaseAgent ):
78 """
8- Base class for agents that use either an epsilon-greedy or softmax policy to encourage exploration .
9+ Base class for agents that use an exploration strategy over action values .
910
1011 Arguments:
12+ exploration_strategy: Optional strategy object for converting values to a policy
1113 epsilon: For epsilon-greedy policy
1214 temperature: For softmax policy
1315 decay_type: Select formula for decreasing rate (0: exponential, 1: linear)
1416
1517 Both epsilon and temperature can be either a fixed float or a tuple (start,decay constant,min) for decreasing exploration.
1618 """
1719 def __init__ (self , * ,
20+ exploration_strategy : ExplorationStrategy | None = None ,
1821 epsilon : float | tuple [float ] | None = None ,
1922 temperature : float | tuple [float ] | None = None ,
2023 decay_type : float = 0 ,
2124 ** kwargs ):
2225 super ().__init__ (** kwargs )
2326
24- if epsilon is not None and temperature is not None :
25- raise RuntimeError ("Cannot specify both epsilon and temperature" )
26- if epsilon is None and temperature is None :
27- epsilon = 0.1 # default value
27+ if exploration_strategy is not None and (epsilon is not None or temperature is not None ):
28+ raise RuntimeError ("Cannot specify exploration_strategy with epsilon or temperature" )
29+ if exploration_strategy is None :
30+ if epsilon is not None and temperature is not None :
31+ raise RuntimeError ("Cannot specify both epsilon and temperature" )
32+ if epsilon is None and temperature is None :
33+ epsilon = 0.1 # default value
2834
2935 assert epsilon is None or isinstance (epsilon ,float ) or (isinstance (epsilon ,tuple ) and len (epsilon )== 3 )
3036 assert temperature is None or isinstance (temperature ,float ) or (isinstance (temperature ,tuple ) and len (temperature )== 3 )
@@ -33,34 +39,20 @@ def __init__(self, *,
3339 self .temperature = temperature
3440 self .decay_type = int (decay_type )
3541 assert self .decay_type in [0 ,1 ]
42+ if exploration_strategy is not None :
43+ self .exploration_strategy = exploration_strategy
44+ elif epsilon is not None :
45+ self .exploration_strategy = EpsilonGreedy (epsilon , self .decay_type )
46+ elif temperature is not None :
47+ self .exploration_strategy = Softmax (temperature , self .decay_type )
48+ else :
49+ raise RuntimeError ("Invalid state" )
3650
3751 def build_greedy_policy (self , values : np .ndarray ) -> np .ndarray :
3852 """
39- Construct probabilities based on given reward estimates and selected policy
53+ Construct probabilities from reward estimates using the configured strategy.
4054 """
41- if self .epsilon is not None :
42- return self .build_epsilon_greedy_policy (values )
43- if self .temperature is not None :
44- return self .build_softmax_policy (values )
45- raise RuntimeError ("Invalid state" )
46-
47- def build_epsilon_greedy_policy (self , values : np .ndarray ) -> np .ndarray :
48- # Exploitation: sample uniformly across actions with highest value
49- best_actions = np .isclose (values , values .max ())
50- exploit = np .ones_like (values )* best_actions / best_actions .sum ()
51-
52- # Exploration: sample uniformly across all actions
53- explore = np .ones_like (values ) / self .num_actions
54-
55- epsilon = self .parse_parameter (self .epsilon )
56- return (1 - epsilon ) * exploit + epsilon * explore
57-
58- def build_softmax_policy (self , values : np .ndarray ) -> np .ndarray :
59- temperature = self .parse_parameter (self .temperature )
60-
61- # Numerically stable softmax
62- exp = np .exp ((values - values .max ()) / temperature )
63- return exp / exp .sum ()
55+ return self .exploration_strategy .get_probabilities (self , values )
6456
6557 def parse_parameter (self , parameter : float | tuple [float ]) -> float :
6658 """
0 commit comments