import numpy as np from sklearn.ensemble import RandomForestRegressor from typing import Any, Optional, Sequence, Tuple, Union def p( prices_historical: Union[np.ndarray, None], demand_historical: Union[np.ndarray, None], information_dump: Optional[Any], ) -> Tuple[Tuple[float, float, float], Any]: """Return the average of the last observed prices. Parameters ---------- prices_historical : Union[np.ndarray, None] The shape is (number competitors) x (number of products = 3) x (past iterations) and contains the past prices of each competitor. You are at index 0. Equal to `None` in the first time period. demand_historical : Union[np.ndarray, None] The shape is (number of products = 3) x (past iterations) and contains the history of your own past observed demand. Equal to `None` in the first time period. information_dump : Optional[Any] Some information object you like to pass to yourself at the next iteration Returns ------- Tuple[Tuple[float, float, float], Any] A tuple (or list) of length three (containing the prices) and the information dump. """ lb, ub = 0.1, 100 # Check if we are in the very first call to our function and then return random prices if prices_historical is None: # "price_profile" will be be used for efficient price optimization later information_dump = {'price_profile': [np.random.uniform(lb, ub, size=3) for _ in range(10)]} return np.round(np.random.uniform(lb, ub, 3), 1), information_dump m, n, t = prices_historical.shape if t < 10: return np.round(np.random.uniform(lb, ub, 3), 1), information_dump if np.random.uniform() > (t / 100): return np.random.uniform(lb, ub, 3), information_dump if 'model' not in information_dump or not t % 100: information_dump['model'] = _train_model(prices_historical, demand_historical, m, n, t) # Optimize prices # The price profile contains sets of prices that we try out when optimizing prices # Each time we throw away the worst one and sample a random new one X = prices_historical.reshape(m * n, t).T predicted_profit = [ information_dump['model'] .predict(np.hstack([prices, X[-1, n:]]).reshape(1, -1))[0] for prices in information_dump['price_profile'] ] worst_p, best_p = np.argmin(predicted_profit), np.argmax(predicted_profit) best_prices = information_dump['price_profile'][best_p] information_dump['price_profile'][worst_p] = np.random.uniform( best_prices * 0.8, best_prices * 1.2 ) return np.clip(best_prices, 0.1, 100.0), information_dump def _train_model(prices_historical, demand_historical, m, n, t): y = np.sum(prices_historical[0] * demand_historical, axis=0) X = prices_historical.reshape(m * n, t).T regr = RandomForestRegressor(n_estimators=50) regr.fit(X, y) return regr