From 1410b69e1a55cf374145df3d9db1b24fcfca5a95 Mon Sep 17 00:00:00 2001 From: "AW-3070-LAPTOP\\erik" Date: Sun, 16 Mar 2025 17:57:08 +0000 Subject: [PATCH 1/3] Updated package dependencies to work with python 3.12.6 Fixed lookup_table adjudicator to create data dir if it doesn't exist. --- pyproject.toml | 16 ++++++---------- tangled_adjudicate/adjudicators/lookup_table.py | 4 ++-- 2 files changed, 8 insertions(+), 12 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index a4b3e92..80b03d1 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -5,17 +5,14 @@ description = "Tangled adjudicators" authors = ["Geordie Rose "] license = "MIT" homepage = "https://www.snowdropquantum.com/" -packages = [ - { include = "tangled_adjudicate" }, - { include = "tests" }, -] +packages = [{ include = "tangled_adjudicate" }, { include = "tests" }] [tool.poetry.dependencies] -python = "^3.8" # You may want to adjust this based on your needs -dwave-ocean-sdk = "*" -dwave-neal = ">=0.6.0" -matplotlib = "*" -gdown = "*" +python = "^3.12" # You may want to adjust this based on your needs +dwave-ocean-sdk = "6.7.1" +dwave-neal = "0.6.0" +matplotlib = "3.10.1" +gdown = "5.2.0" [tool.poetry.group.dev.dependencies] # Add development dependencies here if needed @@ -24,4 +21,3 @@ gdown = "*" [build-system] requires = ["poetry-core>=1.0.0"] build-backend = "poetry.core.masonry.api" - diff --git a/tangled_adjudicate/adjudicators/lookup_table.py b/tangled_adjudicate/adjudicators/lookup_table.py index de2a49b..0874532 100644 --- a/tangled_adjudicate/adjudicators/lookup_table.py +++ b/tangled_adjudicate/adjudicators/lookup_table.py @@ -32,9 +32,9 @@ def setup(self, **kwargs) -> None: if 'data_dir' in kwargs: if not isinstance(kwargs['data_dir'], str): raise ValueError("data_dir must be a string") - if not os.path.isdir(kwargs['data_dir']): - raise ValueError(f"Directory not found: {kwargs['data_dir']}") self.data_dir = kwargs['data_dir'] + # Make the data directory if it doesn't exist + os.makedirs(self.data_dir, exist_ok=True) self._parameters = {'data_dir': self.data_dir} From bb200400c34a4a9bc8257751af70e9e8da35c421 Mon Sep 17 00:00:00 2001 From: "AW-3070-LAPTOP\\erik" Date: Mon, 17 Mar 2025 04:28:48 +0000 Subject: [PATCH 2/3] Added unit test. Fixed data file loading. Currently, QuantumAnnealing adjudicator doesn't work. --- pyproject.toml | 3 +- .../schrodinger/sparse_matrices.py | 17 ++- tests/test_adjudicators.py | 127 ++++++++++++++++++ 3 files changed, 145 insertions(+), 2 deletions(-) create mode 100644 tests/test_adjudicators.py diff --git a/pyproject.toml b/pyproject.toml index 80b03d1..5510b2a 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -16,7 +16,8 @@ gdown = "5.2.0" [tool.poetry.group.dev.dependencies] # Add development dependencies here if needed -# pytest = "^7.0.0" +pytest = "^7.0.0" +tangled-game-engine = { path = "../../tangled-game-package" } [build-system] requires = ["poetry-core>=1.0.0"] diff --git a/tangled_adjudicate/schrodinger/sparse_matrices.py b/tangled_adjudicate/schrodinger/sparse_matrices.py index 787049e..687a1ce 100644 --- a/tangled_adjudicate/schrodinger/sparse_matrices.py +++ b/tangled_adjudicate/schrodinger/sparse_matrices.py @@ -4,6 +4,9 @@ from scipy import sparse from scipy.linalg import eigh, eig from scipy.sparse.linalg import eigsh +import importlib.resources +import importlib.util +import pathlib def create_2d_pauli_matrices(verbose=False): @@ -76,7 +79,19 @@ def create_pauli_matrices_for_full_size_hamiltonian(n_qubits, verbose=False): def load_schedule_data(file_path=None, verbose=False): # data is a numpy array if file_path is None: - file_path = os.path.join(os.getcwd(), '..', 'schrodinger', 'new_schedule.txt') + # Get the path to the schedule file using package resources + try: + # For Python 3.9+ + with importlib.resources.files('tangled_adjudicate.schrodinger').joinpath('new_schedule.txt').open('r') as f: + file_path = str(importlib.resources.files('tangled_adjudicate.schrodinger').joinpath('new_schedule.txt')) + except (ImportError, AttributeError): + # Fallback for older Python versions + package_path = pathlib.Path(importlib.util.find_spec('tangled_adjudicate').origin).parent + file_path = os.path.join(package_path, 'schrodinger', 'new_schedule.txt') + + if verbose: + print(f"Using schedule file: {file_path}") + data = np.loadtxt(file_path) # Import SR8 qubit information # these are both 1001 dimensional row vectors diff --git a/tests/test_adjudicators.py b/tests/test_adjudicators.py new file mode 100644 index 0000000..e95fbad --- /dev/null +++ b/tests/test_adjudicators.py @@ -0,0 +1,127 @@ +import os +import random +import pytest +import traceback +from typing import Dict, List, Tuple, Optional + +from tangled_game_engine.tangled_game.game import Game +from tangled_game_engine.tangled_game.game_types import Edge, Vertex + +from tangled_adjudicate.adjudicators.lookup_table import LookupTableAdjudicator +from tangled_adjudicate.adjudicators.quantum_annealing import QuantumAnnealingAdjudicator +from tangled_adjudicate.adjudicators.simulated_annealing import SimulatedAnnealingAdjudicator +from tangled_adjudicate.adjudicators.schrodinger import SchrodingerEquationAdjudicator + + +def play_random_game(graph_id: str = "k_3") -> Dict: + """ + Create a game with the specified graph_id and play it with random legal moves. + + Args: + graph_id: The ID of the graph to use for the game + + Returns: + The final game state as a dictionary + """ + # Create a new game + game = Game() + game.create_game(graph_id=graph_id, player1_invite="player1", player2_invite="player2") + + # Join the game + game.join_game("player1", 1) + game.join_game("player2", 2) + + # Play the game until it's over + while not game.is_game_over(): + # Get legal moves for the current player + current_player_id = game.player1_id if game.current_player_index == 1 else game.player2_id + legal_moves = game.get_legal_moves(current_player_id) + + # Filter out quit moves + legal_moves = [move for move in legal_moves if move[0] != Game.MoveType.QUIT.value] + + # Choose a random move + if legal_moves: + move = random.choice(legal_moves) + game.make_move(current_player_id, move[0], move[1], move[2]) + + # Return the final game state + return game.get_game_state() + + +def test_adjudicators(): + """ + Test all adjudicators with a random game on the k_3 graph. + """ + # Set random seed for reproducibility + random.seed(42) + + # Play a random game + game_state = play_random_game(graph_id="k_3") + + # Print the final game state for debugging + print("\nFinal Game State:") + print(f"Player 1 node: {game_state['player1_node']}") + print(f"Player 2 node: {game_state['player2_node']}") + print(f"Edges: {game_state['edges']}") + + # Create a temporary directory for lookup table data + temp_dir = os.path.join(os.path.dirname(__file__), "temp_data") + os.makedirs(temp_dir, exist_ok=True) + + # Test each adjudicator + adjudicators = [ + (LookupTableAdjudicator(), {"data_dir": temp_dir}), + (SimulatedAnnealingAdjudicator(), {}), + (SchrodingerEquationAdjudicator(), {}), + # (QuantumAnnealingAdjudicator(), {"use_mock": True, "graph_number": 2, "data_dir": temp_dir}) + ] + + results = [] + + for adjudicator, params in adjudicators: + try: + # Setup the adjudicator + adjudicator.setup(**params) + + # Adjudicate the game state + result = adjudicator.adjudicate(game_state) + + # Store the result + results.append(result) + + # Print the result + print(f"\n{result['adjudicator']} Result:") + print(f"Winner: {result['winner']}") + if result['score'] is not None: + print(f"Score: {result['score']}") + + # Assert that the result has the expected fields + assert 'winner' in result, f"{result['adjudicator']} result missing 'winner' field" + assert 'game_state' in result, f"{result['adjudicator']} result missing 'game_state' field" + assert 'adjudicator' in result, f"{result['adjudicator']} result missing 'adjudicator' field" + + except Exception as e: + # Print the full traceback + print(f"\nError in {adjudicator.__class__.__name__}:") + traceback.print_exc() + pytest.fail(f"Adjudicator {adjudicator.__class__.__name__} failed: {str(e)}") + + # Compare results from different adjudicators + if len(results) > 1: + print("\nComparing adjudicator results:") + for i in range(len(results)): + for j in range(i+1, len(results)): + adj1 = results[i]['adjudicator'] + adj2 = results[j]['adjudicator'] + winner1 = results[i]['winner'] + winner2 = results[j]['winner'] + + print(f"{adj1} vs {adj2}: {winner1} vs {winner2}") + + # Note: We don't assert equality because different adjudicators might give different results + # This is just for information + + +if __name__ == "__main__": + test_adjudicators() \ No newline at end of file From 8504a9ee6c3ad8a40180a6619595f8682f933168 Mon Sep 17 00:00:00 2001 From: "AW-3070-LAPTOP\\erik" Date: Mon, 17 Mar 2025 18:58:24 +0000 Subject: [PATCH 3/3] Bumped version --- .bumpversion.cfg | 2 +- pyproject.toml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.bumpversion.cfg b/.bumpversion.cfg index 1541736..000d79d 100644 --- a/.bumpversion.cfg +++ b/.bumpversion.cfg @@ -1,5 +1,5 @@ [bumpversion] -current_version = 0.0.1 +current_version = 0.0.2 commit = False tag = False diff --git a/pyproject.toml b/pyproject.toml index 5510b2a..4ce4237 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [tool.poetry] name = "tangled-adjudicate" -version = "0.0.1" +version = "0.0.2" description = "Tangled adjudicators" authors = ["Geordie Rose "] license = "MIT"