This repository has been archived by the owner on Sep 6, 2024. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat(lab-01): monotonic iterative added
- Loading branch information
Alexey N. Alexandrov
authored and
Alexey N. Alexandrov
committed
Mar 11, 2024
1 parent
8861927
commit 414b7fd
Showing
9 changed files
with
866 additions
and
215 deletions.
There are no files selected for viewing
Binary file not shown.
Empty file.
580 changes: 580 additions & 0 deletions
580
game_theory/01-rk-monotonous_algorithm/monotonic_algorithm.ipynb
Large diffs are not rendered by default.
Oops, something went wrong.
Large diffs are not rendered by default.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Empty file.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,113 @@ | ||
"""Монотонный итеративный алгоритм решения матричной игры (n x m)-игры с нулевой суммой.""" | ||
import logging | ||
import random | ||
|
||
import numpy as np | ||
from numpy import ndarray | ||
|
||
from game_theory.utils.matrix_games.game_matrix import GameMatrix | ||
from game_theory.utils.matrix_games.types import IndexType, ValueType | ||
|
||
_logger = logging.getLogger(__name__) | ||
|
||
|
||
class Monotonic: | ||
"""Класс инкапсулирует решение матричной игры монотонным итеративным методом.""" | ||
|
||
def __init__(self, game_matrix: GameMatrix): | ||
self.game: GameMatrix = game_matrix | ||
|
||
self.iteration_number: int = 0 | ||
self.strategy_index: IndexType | ||
self.strategy_x: np.ndarray[float] | ||
self.scores_c: np.ndarray[ValueType] | ||
self.price_v: ValueType | ||
self.indicator_mask_j: np.ndarray[bool] | ||
|
||
def solve(self): | ||
# Решение за игрока A. | ||
_logger.info("Решение игры относительно игрока A") | ||
( | ||
price_a, | ||
strategy_a, | ||
) = self._base_solve(self.game.matrix.copy()) | ||
# Решения за игрока B. | ||
_logger.info("Решение игры относительно игрока B") | ||
price_b, strategy_b = self._base_solve(self.game.matrix.T.copy()) | ||
return (price_a, strategy_a), (price_b, strategy_b) | ||
|
||
def _base_solve(self, matrix: np.ndarray[ValueType]): | ||
m, n = matrix.shape | ||
self.iteration_number = 0 | ||
_logger.info("Итерация 0:") | ||
# Выбираем произвольную (x^0) чистую стратегию (выставляя 1 только в одну позицию). | ||
self.strategy_index = random.randint(0, m - 1) | ||
self.strategy_x = np.array([0] * n) | ||
self.strategy_x[self.strategy_index] = 1 | ||
# Выбираем вектор (c^0), соответствующий выбранной стратегии. | ||
self.scores_c: np.ndarray = matrix[self.strategy_index].copy() | ||
# Текущая цена игры. | ||
self.price_v = np.min(self.scores_c) | ||
# Вектор-индикатор, который показывает принадлежность к множеству. | ||
self.indicator_mask_j: np.ndarray[bool] = self.scores_c == self.price_v | ||
self.__log_calculated_parameters() | ||
|
||
alpha_values = np.array((np.inf, np.inf)) | ||
# Выполняем итерации без заданной точности, то есть пока α_N не станет 0. | ||
while not np.allclose(alpha_values, [0, 1]): | ||
optimal_strategy_x_, optimal_scores_c_, alpha_values = self.perform_iteration(matrix) | ||
|
||
alpha, _ = alpha_values | ||
self.strategy_x = (1 - alpha) * self.strategy_x + alpha * optimal_strategy_x_ | ||
self.scores_c = (1 - alpha) * self.scores_c + alpha * optimal_scores_c_ | ||
self.price_v = np.min(self.scores_c) | ||
self.indicator_mask_j = self.scores_c == self.price_v | ||
self.__log_calculated_parameters() | ||
|
||
return self.price_v, self.strategy_x.copy() | ||
|
||
def perform_iteration(self, matrix: np.ndarray[ValueType]) -> tuple[ndarray, ndarray, ndarray]: | ||
self.iteration_number += 1 | ||
i = self.iteration_number | ||
_logger.info(f"Итерация {self.iteration_number}:") | ||
# Выбираем только столбцы, удовлетворяющие нашему индикатору. | ||
sub_game_matrix_a = GameMatrix(matrix[:, self.indicator_mask_j].copy()) | ||
_logger.info(f"Рассмотрим подыгру Г^{i}: " f"\n{np.around(sub_game_matrix_a.matrix, 3)}") | ||
# Решаем подыгру и находим оптимальную стратегию x_. | ||
_, optimal_strategy_x_ = sub_game_matrix_a.solve() | ||
optimal_scores_c_: np.ndarray = self.__reduce_sum(matrix, np.array(optimal_strategy_x_)) | ||
_logger.info( | ||
f"Оптимальная стратегия игрока: " | ||
f"\n\t‾x_{i} = {optimal_strategy_x_}" | ||
f"\n\t‾c_{i} = {tuple(optimal_scores_c_)}" | ||
) | ||
# Находим оптимальную стратегию игрока в подыгре из двух строк. | ||
sub_game_gamma = GameMatrix(np.stack((self.scores_c, optimal_scores_c_))) | ||
_logger.info( | ||
f"Находим оптимальную стратегию игрока в подыгре из двух строк: " f"\n{np.around(sub_game_gamma.matrix, 3)}" | ||
) | ||
sub_game_gamma = sub_game_gamma.reduce_dimension(method="nbr_drop") | ||
_logger.info(f"Матрица после уменьшения размерности: " f"\n{np.around(sub_game_gamma.matrix, 3)}") | ||
_, alpha_values = sub_game_gamma.solve() | ||
alpha_values = (alpha_values[1], alpha_values[0]) | ||
_logger.info( | ||
f"В результате получена оптимальная стратегия (α_{i}, 1 - α_{i}) = " f"{np.around(alpha_values, 3)}" | ||
) | ||
return np.array(optimal_strategy_x_), optimal_scores_c_, np.array(alpha_values) | ||
|
||
@staticmethod | ||
def __reduce_sum(lhs: np.ndarray, rhs: np.ndarray) -> np.ndarray: | ||
return np.sum((rhs * lhs.T).T, 0) | ||
|
||
def __log_calculated_parameters(self, accuracy=3): | ||
j_indexes = list(*np.where(self.indicator_mask_j)) | ||
_logger.info( | ||
"\n".join( | ||
[ | ||
f"x^{self.iteration_number} = {np.around(self.strategy_x, accuracy)}", | ||
f"c^{self.iteration_number} = {np.around(self.scores_c, accuracy)}", | ||
f"v^{self.iteration_number} = {round(self.price_v, accuracy)}", | ||
f"J^{self.iteration_number} = {j_indexes}", | ||
] | ||
) | ||
) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters