Source code for chipiron.players.move_selector.treevalue.indices.test_indices

"""
This module contains functions for testing the indices used in the move selector tree.

The main functions in this module are:
- `make_tree_from_file`: Creates a move and value tree from a YAML file.
- `check_from_file`: Compares the indices computed from the tree with the expected indices from a YAML file.
- `check_index`: Tests the indices for a specific index computation type and tree file.
- `test_indices`: Runs the index tests for multiple index computation types and tree files.
"""

from enum import Enum
from math import isclose
from typing import Any

import chess
import yaml

import chipiron.players.move_selector.treevalue.node_factory as node_factory
import chipiron.players.move_selector.treevalue.search_factory as search_factories
import chipiron.players.move_selector.treevalue.tree_manager as tree_manager
import chipiron.players.move_selector.treevalue.trees as trees
from chipiron.environments.chess_env.board.factory import (
    create_board_chi_from_pychess_board,
)
from chipiron.players.move_selector.treevalue.indices.index_manager.factory import (
    create_exploration_index_manager,
)
from chipiron.players.move_selector.treevalue.indices.index_manager.node_exploration_manager import (
    NodeExplorationIndexManager,
    update_all_indices,
)
from chipiron.players.move_selector.treevalue.indices.node_indices.index_types import (
    IndexComputationType,
)
from chipiron.players.move_selector.treevalue.nodes.algorithm_node.algorithm_node import (
    AlgorithmNode,
)
from chipiron.players.move_selector.treevalue.nodes.itree_node import ITreeNode
from chipiron.players.move_selector.treevalue.tree_manager.tree_expander import (
    TreeExpansion,
    TreeExpansions,
)
from chipiron.players.move_selector.treevalue.trees.descendants import RangedDescendants
from chipiron.players.move_selector.treevalue.trees.move_and_value_tree import (
    MoveAndValueTree,
)
from chipiron.utils.small_tools import path


[docs]class TestResult(Enum): """ Enumeration for the test results. """ __test__ = False PASSED = 0 FAILED = 1 WARNING = 2
[docs]def make_tree_from_file( file_path: path, index_computation: IndexComputationType ) -> MoveAndValueTree: """ Creates a move and value tree from a file. Args: file_path (path): The path to the file containing the tree data. index_computation (IndexComputationType): The type of index computation to use. Returns: MoveAndValueTree: The created move and value tree. """ # atm it is very ad hoc to test index so takes a lots of shortcut, will be made more general when needed with open(file_path, "r") as file: tree_yaml = yaml.safe_load(file) print("tree", tree_yaml) yaml_nodes = tree_yaml["nodes"] node_factory_name: str = "Base_with_algorithm_tree_node" tree_node_factory: node_factory.Base[Any] = node_factory.create_node_factory( node_factory_name=node_factory_name ) search_factory: search_factories.SearchFactoryP = search_factories.SearchFactory( node_selector_args=None, opening_type=None, random_generator=None, index_computation=index_computation, ) algorithm_node_factory: node_factory.AlgorithmNodeFactory algorithm_node_factory = node_factory.AlgorithmNodeFactory( tree_node_factory=tree_node_factory, board_representation_factory=None, exploration_index_data_create=search_factory.node_index_create, ) descendants: RangedDescendants = RangedDescendants() algo_tree_manager: tree_manager.AlgorithmNodeTreeManager = ( tree_manager.create_algorithm_node_tree_manager( node_evaluator=None, algorithm_node_factory=algorithm_node_factory, index_computation=index_computation, index_updater=None, ) ) half_moves = {} id_nodes = {} for yaml_node in yaml_nodes: # print('yaml_node[id] ',yaml_node['id'] ) if yaml_node["id"] == 0: tree_expansions = TreeExpansions() board = chess.Board.from_chess960_pos(yaml_node["id"]) board.turn = chess.WHITE board_chi = create_board_chi_from_pychess_board(chess_board=board) root_node: ITreeNode[Any] = algorithm_node_factory.create( board=board_chi, half_move=0, count=yaml_node["id"], parent_node=None, move_from_parent=None, modifications=None, ) assert isinstance(root_node, AlgorithmNode) root_node.minmax_evaluation.value_white_minmax = yaml_node["value"] half_moves[yaml_node["id"]] = 0 id_nodes[yaml_node["id"]] = root_node descendants.add_descendant(root_node) move_and_value_tree: MoveAndValueTree = MoveAndValueTree( root_node=root_node, descendants=descendants ) tree_expansions.add( TreeExpansion( child_node=root_node, parent_node=None, board_modifications=None, creation_child_node=True, move=None, ) ) root_node.tree_node.all_legal_moves_generated = True # algo_tree_manager.update_backward(tree_expansions=tree_expansions) else: tree_expansions = TreeExpansions() first_parent = yaml_node["parents"] half_move = half_moves[first_parent] + 1 half_moves[yaml_node["id"]] = half_move parent_node = id_nodes[first_parent] board = chess.Board.from_chess960_pos(yaml_node["id"]) board.turn = not parent_node.tree_node.board_.turn board_chi = create_board_chi_from_pychess_board(chess_board=board) tree_expansion: TreeExpansion = algo_tree_manager.tree_manager.open_node( tree=move_and_value_tree, parent_node=parent_node, board=board_chi, modifications=None, move=yaml_node["id"], ) tree_expansions.add( TreeExpansion( child_node=tree_expansion.child_node, parent_node=tree_expansion.parent_node, board_modifications=tree_expansion.board_modifications, creation_child_node=tree_expansion.creation_child_node, move=yaml_node["id"], ) ) assert isinstance(tree_expansion.child_node, AlgorithmNode) tree_expansion.child_node.tree_node.all_legal_moves_generated = True id_nodes[yaml_node["id"]] = tree_expansion.child_node tree_expansion.child_node.minmax_evaluation.value_white_minmax = yaml_node[ "value" ] tree_expansion.child_node.minmax_evaluation.value_white_evaluator = ( yaml_node["value"] ) assert tree_expansion.move is not None parent_node.minmax_evaluation.moves_not_over.append(tree_expansion.move) algo_tree_manager.update_backward(tree_expansions=tree_expansions) # print('move_and_value_tree', move_and_value_tree.descendants) return move_and_value_tree
[docs]def check_from_file(file_path: path, tree: MoveAndValueTree) -> None: """ Check the values in the given file against the values in the tree. Args: file_path (str): The path to the file containing the values to check. tree (MoveAndValueTree): The tree containing the values to compare against. Returns: None """ with open(file_path, "r") as file: tree_yaml = yaml.safe_load(file) print("tree", tree_yaml) yaml_nodes = tree_yaml["nodes"] tree_nodes: trees.RangedDescendants = tree.descendants half_move: int for half_move in tree_nodes: # todo how are we sure that the hm comes in order? # print('hmv', half_move) parent_node: ITreeNode[Any] for parent_node in tree_nodes[half_move].values(): assert isinstance(parent_node, AlgorithmNode) yaml_index = eval(str(yaml_nodes[parent_node.id]["index"])) assert parent_node.exploration_index_data is not None print( f"id {parent_node.id} expected value {yaml_index} " f"|| computed value {parent_node.exploration_index_data.index}" f' {type(yaml_nodes[parent_node.id]["index"])}' f" {type(parent_node.exploration_index_data.index)}" f"{(yaml_index == parent_node.exploration_index_data.index)}" ) if yaml_index is None: assert parent_node.exploration_index_data.index is None else: assert parent_node.exploration_index_data.index is not None assert isclose( yaml_index, parent_node.exploration_index_data.index, abs_tol=1e-8 )
[docs]def check_index(index_computation: IndexComputationType, tree_file: path) -> TestResult: """ Checks the index for a given tree file and index computation type. Args: index_computation (IndexComputationType): The type of index computation. tree_file (path): The path to the tree file. Returns: TestResult: The result of the index check. Raises: None """ tree_path = f"tests/data/trees/{tree_file}/{tree_file}.yaml" tree: MoveAndValueTree = make_tree_from_file( index_computation=index_computation, file_path=tree_path ) index_manager: NodeExplorationIndexManager = create_exploration_index_manager( index_computation=index_computation ) print("index_manager", index_manager) update_all_indices(tree, index_manager) # print_all_indices( # tree # ) file_index = ( f"tests/data/trees/{tree_file}/{tree_file}_{index_computation.value}.yaml" ) check_from_file(file_path=file_index, tree=tree) return TestResult.PASSED
[docs]def test_indices() -> None: """ Test the index computations on multiple tree files. This function iterates over a list of index computations and tree files, and performs a test for each combination. The results of the tests are stored in a dictionary. Returns: None """ index_computations: list[IndexComputationType] = [ IndexComputationType.MinGlobalChange, IndexComputationType.RecurZipf, IndexComputationType.MinLocalChange, ] tree_files = ["tree_1", "tree_2"] results: dict[TestResult, int] = {} for tree_file in tree_files: if tree_file == "tree_2": index_computations_ = [ IndexComputationType.MinGlobalChange, IndexComputationType.MinLocalChange, ] else: index_computations_ = index_computations for index_computation in index_computations_: print(f"---testing {index_computation} on {tree_file}") res: TestResult = check_index( index_computation=index_computation, tree_file=tree_file ) if res in results: results[res] += 1 else: results[res] = 1 print(f"finished Test: {results}") assert results[TestResult.PASSED] == 5
if __name__ == "__main__": test_indices()