From 889298e278a7280f2bff1cda5c47b2b2fb816aad Mon Sep 17 00:00:00 2001 From: erik Date: Mon, 27 Jan 2025 21:36:53 +0000 Subject: [PATCH 01/59] feat: Move the package to use poetry for installation. --- .bumpversion.cfg | 8 ++++++++ pyproject.toml | 25 +++++++++++++++++++++++++ 2 files changed, 33 insertions(+) create mode 100644 .bumpversion.cfg create mode 100644 pyproject.toml diff --git a/.bumpversion.cfg b/.bumpversion.cfg new file mode 100644 index 0000000..1541736 --- /dev/null +++ b/.bumpversion.cfg @@ -0,0 +1,8 @@ +[bumpversion] +current_version = 0.0.1 +commit = False +tag = False + +[bumpversion:file:pyproject.toml] +search = version = "{current_version}" +replace = version = "{new_version}" diff --git a/pyproject.toml b/pyproject.toml new file mode 100644 index 0000000..a5f2c0f --- /dev/null +++ b/pyproject.toml @@ -0,0 +1,25 @@ +[tool.poetry] +name = "tangled-adjudicate" +version = "0.0.1" +description = "Tangled adjudicators" +authors = ["Geordie Rose "] +license = "MIT" +homepage = "https://www.snowdropquantum.com/" +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 = "*" +matplotlib = "*" +gdown = "*" + +[tool.poetry.group.dev.dependencies] +# Add development dependencies here if needed +# pytest = "^7.0.0" + +[build-system] +requires = ["poetry-core>=1.0.0"] +build-backend = "poetry.core.masonry.api" From 80abf92edf36df8aca4c303c4600b8dc906135cb Mon Sep 17 00:00:00 2001 From: erik Date: Mon, 27 Jan 2025 22:20:28 +0000 Subject: [PATCH 02/59] refactor: Convert to base class and subclasses to implement different adjudicators. Precursor to including in game service. --- pyproject.toml | 2 + setup.py | 13 - tangled_adjudicate/__init__.py | 5 + tangled_adjudicate/adjudicators/adjudicate.py | 2 +- .../adjudicators/adjudicator.py | 135 +++++++++ .../adjudicators/lookup_table.py | 117 ++++++++ .../adjudicators/quantum_annealing.py | 257 ++++++++++++++++++ .../adjudicators/schrodinger.py | 103 +++++++ .../adjudicators/simulated_annealing.py | 113 ++++++++ 9 files changed, 733 insertions(+), 14 deletions(-) delete mode 100644 setup.py create mode 100644 tangled_adjudicate/adjudicators/adjudicator.py create mode 100644 tangled_adjudicate/adjudicators/lookup_table.py create mode 100644 tangled_adjudicate/adjudicators/quantum_annealing.py create mode 100644 tangled_adjudicate/adjudicators/schrodinger.py create mode 100644 tangled_adjudicate/adjudicators/simulated_annealing.py diff --git a/pyproject.toml b/pyproject.toml index a5f2c0f..a4b3e92 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -13,6 +13,7 @@ packages = [ [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 = "*" @@ -23,3 +24,4 @@ gdown = "*" [build-system] requires = ["poetry-core>=1.0.0"] build-backend = "poetry.core.masonry.api" + diff --git a/setup.py b/setup.py deleted file mode 100644 index c32a1d1..0000000 --- a/setup.py +++ /dev/null @@ -1,13 +0,0 @@ -from setuptools import setup - -setup( - name='tangled-adjudicate', - version='0.0.1', - packages=['tests', 'tangled_adjudicate', 'tangled_adjudicate.utils', 'tangled_adjudicate.schrodinger', 'tangled_adjudicate.adjudicators'], - url='https://www.snowdropquantum.com/', - license='MIT', - author='Geordie Rose', - author_email='geordie@snowdropquantum.com', - description='Tangled adjudicators', - install_requires=['dwave-ocean-sdk', 'matplotlib', 'gdown'] -) diff --git a/tangled_adjudicate/__init__.py b/tangled_adjudicate/__init__.py index e69de29..2fea5db 100644 --- a/tangled_adjudicate/__init__.py +++ b/tangled_adjudicate/__init__.py @@ -0,0 +1,5 @@ +from .adjudicators.adjudicator import Adjudicator, GameState, AdjudicationResult +from .adjudicators.lookup_table import LookupTableAdjudicator +from .adjudicators.schrodinger import SchrodingerEquationAdjudicator +from .adjudicators.simulated_annealing import SimulatedAnnealingAdjudicator +from .adjudicators.quantum_annealing import QuantumAnnealingAdjudicator diff --git a/tangled_adjudicate/adjudicators/adjudicate.py b/tangled_adjudicate/adjudicators/adjudicate.py index e90fa53..1f04785 100644 --- a/tangled_adjudicate/adjudicators/adjudicate.py +++ b/tangled_adjudicate/adjudicators/adjudicate.py @@ -19,7 +19,7 @@ from dwave.system.testing import MockDWaveSampler -class Adjudicator(object): +class old_Adjudicator(object): def __init__(self, params): self.params = params self.results_dict = None diff --git a/tangled_adjudicate/adjudicators/adjudicator.py b/tangled_adjudicate/adjudicators/adjudicator.py new file mode 100644 index 0000000..6985089 --- /dev/null +++ b/tangled_adjudicate/adjudicators/adjudicator.py @@ -0,0 +1,135 @@ +from abc import ABC, abstractmethod +from typing import Any, TypedDict, List, Tuple, Optional, Dict, Union, Set +import numpy as np +import numpy.typing as npt + +class GameState(TypedDict): + num_nodes: int + edges: List[Tuple[int, int, int]] # (node1, node2, weight) + player1_id: str + player2_id: str + turn_count: int + current_player_index: int + player1_node: Optional[int] + player2_node: Optional[int] + +class AdjudicationResult(TypedDict): + game_state: GameState + adjudicator: str + winner: Optional[str] # 'red', 'blue', 'draw', or None + score: Optional[float] + influence_vector: Optional[npt.NDArray[np.float64]] + correlation_matrix: Optional[npt.NDArray[np.float64]] + parameters: Dict[str, Union[str, int, float, bool]] + +class IsingModel(TypedDict): + h: Dict[int, float] # Local fields + j: Dict[Tuple[int, int], float] # Coupling strengths + +class Adjudicator(ABC): + """Base interface for game state adjudication implementations.""" + + def __init__(self) -> None: + """Initialize base adjudicator.""" + self._parameters: Dict[str, Any] = {} + + @abstractmethod + def setup(self, **kwargs) -> None: + """Optional setup method for implementation-specific initialization.""" + pass + + @abstractmethod + def adjudicate(self, game_state: GameState) -> AdjudicationResult: + """Adjudicate the given game state.""" + pass + + def _validate_game_state(self, game_state: GameState) -> None: + """Validate the game state structure and contents.""" + required_keys = { + 'num_nodes', 'edges', 'player1_id', 'player2_id', + 'turn_count', 'current_player_index', 'player1_node', 'player2_node' + } + + if not all(key in game_state for key in required_keys): + missing_keys = required_keys - set(game_state.keys()) + raise ValueError(f"Game state missing required keys: {missing_keys}") + + if game_state['num_nodes'] < 1: + raise ValueError("Number of nodes must be positive") + + for edge in game_state['edges']: + if len(edge) != 3: + raise ValueError(f"Invalid edge format: {edge}") + if not (0 <= edge[0] < game_state['num_nodes'] and + 0 <= edge[1] < game_state['num_nodes']): + raise ValueError(f"Edge vertices out of range: {edge}") + + def _game_state_to_ising(self, game_state: GameState) -> IsingModel: + """Convert game state to Ising model parameters. + + Args: + game_state: The current game state + + Returns: + IsingModel containing h (local fields) and j (coupling strengths) + """ + h = {i: 0.0 for i in range(game_state['num_nodes'])} + j = {} + + for edge in game_state['edges']: + v1, v2, weight = edge + if v1 > v2: + v1, v2 = v2, v1 + j[(v1, v2)] = float(weight) + + return IsingModel(h=h, j=j) + + def _find_isolated_vertices(self, game_state: GameState) -> Set[int]: + """Find vertices with no connections in the graph. + + Args: + game_state: The current game state + + Returns: + Set of isolated vertex indices + """ + connected_vertices = set() + for edge in game_state['edges']: + connected_vertices.add(edge[0]) + connected_vertices.add(edge[1]) + + all_vertices = set(range(game_state['num_nodes'])) + return all_vertices - connected_vertices + + def _compute_winner_score_and_influence( + self, + game_state: GameState, + correlation_matrix: npt.NDArray[np.float64], + epsilon: float = 1e-6 + ) -> Tuple[Optional[str], Optional[float], npt.NDArray[np.float64]]: + """Compute winner, score and influence from correlation matrix.""" + if not isinstance(correlation_matrix, np.ndarray): + raise ValueError("Correlation matrix must be a numpy array") + + if correlation_matrix.shape[0] != correlation_matrix.shape[1]: + raise ValueError("Correlation matrix must be square") + + if correlation_matrix.shape[0] != game_state['num_nodes']: + raise ValueError("Correlation matrix size must match number of nodes") + + influence_vector = np.sum(correlation_matrix, axis=0) + + if game_state['player1_node'] is None or game_state['player2_node'] is None: + return None, None, influence_vector + + score = (influence_vector[game_state['player1_node']] - + influence_vector[game_state['player2_node']]) + + if score > epsilon: + winner = 'red' + elif score < -epsilon: + winner = 'blue' + else: + winner = 'draw' + + return winner, score, influence_vector \ No newline at end of file diff --git a/tangled_adjudicate/adjudicators/lookup_table.py b/tangled_adjudicate/adjudicators/lookup_table.py new file mode 100644 index 0000000..994c3f3 --- /dev/null +++ b/tangled_adjudicate/adjudicators/lookup_table.py @@ -0,0 +1,117 @@ +import os +import pickle +from typing import Dict, Optional +import numpy as np + +from ..utils.utilities import ( + convert_erik_game_state_to_my_game_state, + get_tso, + build_results_dict +) +from .adjudicator import Adjudicator, GameState, AdjudicationResult + +class LookupTableAdjudicator(Adjudicator): + """Adjudicator implementation using pre-computed lookup tables.""" + + def __init__(self) -> None: + """Initialize the lookup table adjudicator.""" + super().__init__() + self.data_dir: Optional[str] = None + self.results_dict: Optional[Dict[str, str]] = None + + def setup(self, **kwargs) -> None: + """Configure lookup table parameters. + + Args: + data_dir: Directory containing lookup table data files + + Raises: + ValueError: If parameters are invalid or data directory doesn't exist + """ + 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'] + + self._parameters = {'data_dir': self.data_dir} + + def _load_lookup_table(self, num_nodes: int) -> None: + """Load the appropriate lookup table for the given graph size. + + Args: + num_nodes: Number of nodes in the graph + + Raises: + ValueError: If lookup table is not available for this graph size + RuntimeError: If lookup table file cannot be loaded + """ + if num_nodes not in [3, 4]: + raise ValueError( + "Lookup table only available for complete graphs with 3 or 4 vertices" + ) + + if not self.data_dir: + raise RuntimeError("Data directory not set. Call setup() first.") + + graph_number = num_nodes - 1 # Convert from num_nodes to graph_number + file_path = os.path.join( + self.data_dir, + f'graph_{graph_number}_terminal_state_outcomes.pkl' + ) + + # Generate lookup table if it doesn't exist + if not os.path.exists(file_path): + get_tso(graph_number, file_path) + + try: + with open(file_path, 'rb') as fp: + results = pickle.load(fp) + self.results_dict = build_results_dict(results) + except Exception as e: + raise RuntimeError(f"Failed to load lookup table: {str(e)}") + + def adjudicate(self, game_state: GameState) -> AdjudicationResult: + """Adjudicate the game state using the lookup table. + + Args: + game_state: The current game state + + Returns: + AdjudicationResult containing the adjudication details + + Raises: + ValueError: If the game state is invalid or unsupported + RuntimeError: If lookup table is not loaded + """ + self._validate_game_state(game_state) + + # Load lookup table if needed + if (self.results_dict is None or + len(next(iter(self.results_dict.keys()))) != game_state['num_nodes']): + self._load_lookup_table(game_state['num_nodes']) + + if not self.results_dict: + raise RuntimeError("Failed to load lookup table") + + # Convert game state to lookup format + lookup_state = convert_erik_game_state_to_my_game_state(game_state) + + try: + winner = self.results_dict[str(lookup_state)] + except KeyError: + raise ValueError( + f"Game state not found in lookup table: {lookup_state}" + ) + + return AdjudicationResult( + game_state=game_state, + adjudicator='lookup_table', + winner=winner, + score=None, # Lookup table doesn't provide scores + influence_vector=None, + correlation_matrix=None, + parameters=self._parameters + ) + \ No newline at end of file diff --git a/tangled_adjudicate/adjudicators/quantum_annealing.py b/tangled_adjudicate/adjudicators/quantum_annealing.py new file mode 100644 index 0000000..0728021 --- /dev/null +++ b/tangled_adjudicate/adjudicators/quantum_annealing.py @@ -0,0 +1,257 @@ +from typing import Dict, Any, List, Optional +import numpy as np +from dataclasses import dataclass +from dwave.system import DWaveSampler, FixedEmbeddingComposite +from dwave.system.testing import MockDWaveSampler + +from ..utils.find_graph_automorphisms import get_automorphisms +from ..utils.find_hardware_embeddings import get_embeddings +from .adjudicator import Adjudicator, GameState, AdjudicationResult + +@dataclass +class QAParameters: + """Parameters for quantum annealing.""" + num_reads: int = 1000 + anneal_time: float = 5.0 # ns + num_chip_runs: int = 1 + use_gauge_transform: bool = True + use_shim: bool = False + shim_iterations: int = 1 + alpha_phi: float = 0.1 + use_mock: bool = False + solver_name: Optional[str] = None + graph_number: Optional[int] = None + +class QuantumAnnealingAdjudicator(Adjudicator): + """Adjudicator implementation using D-Wave quantum annealing.""" + + def __init__(self) -> None: + """Initialize the quantum annealing adjudicator.""" + super().__init__() + self.params = QAParameters() + self.embeddings: List[List[int]] = [] + self.automorphisms: List[Dict[int, int]] = [] + self.sampler: Optional[FixedEmbeddingComposite] = None + + def setup(self, **kwargs) -> None: + """Configure quantum annealing parameters and initialize D-Wave connection. + + Args: + num_reads: Number of annealing reads per run + anneal_time: Annealing time in nanoseconds + num_chip_runs: Number of separate chip programming runs + use_gauge_transform: Whether to apply gauge transformations + use_shim: Whether to use shimming process + shim_iterations: Number of shimming iterations if shimming is used + alpha_phi: Learning rate for flux bias offsets + use_mock: Whether to use mock D-Wave sampler (for testing) + solver_name: Name of D-Wave solver to use + graph_number: Graph number for embedding lookup + + Raises: + ValueError: If parameters are invalid + RuntimeError: If D-Wave connection fails + """ + # Update parameters from kwargs + for key, value in kwargs.items(): + if hasattr(self.params, key): + setattr(self.params, key, value) + else: + raise ValueError(f"Unknown parameter: {key}") + + # Validate parameters + if self.params.num_reads <= 0: + raise ValueError("num_reads must be positive") + if self.params.anneal_time <= 0: + raise ValueError("anneal_time must be positive") + if self.params.num_chip_runs <= 0: + raise ValueError("num_chip_runs must be positive") + if self.params.shim_iterations <= 0: + raise ValueError("shim_iterations must be positive") + if self.params.alpha_phi <= 0 or self.params.alpha_phi > 1: + raise ValueError("alpha_phi must be in (0, 1]") + + # Get graph-specific data if graph number provided + if self.params.graph_number is not None: + self.automorphisms = get_automorphisms(self.params.graph_number) + self.embeddings = get_embeddings( + self.params.graph_number, + self.params.solver_name + ) + + # Initialize sampler + try: + if self.params.use_mock: + base_sampler = MockDWaveSampler( + topology_type='zephyr', + topology_shape=[6, 4] + ) + else: + base_sampler = DWaveSampler(solver=self.params.solver_name) + + # Store for later use in adjudicate + self._base_sampler = base_sampler + + except Exception as e: + raise RuntimeError(f"Failed to initialize D-Wave sampler: {str(e)}") + + # Store parameters + self._parameters = self.params.__dict__ + + def _apply_gauge_transform( + self, + samples: np.ndarray, + flip_indices: List[int] + ) -> np.ndarray: + """Apply gauge transformation to samples. + + Args: + samples: Sample array to transform + flip_indices: Indices where spins should be flipped + + Returns: + Transformed sample array + """ + samples = samples.copy() + samples[:, flip_indices] = -samples[:, flip_indices] + return samples + + def _process_embedding( + self, + game_state: GameState, + automorphism: Dict[int, int] + ) -> Dict[int, List[int]]: + """Process embedding with given automorphism. + + Args: + game_state: Current game state + automorphism: Graph automorphism to apply + + Returns: + Processed embedding mapping + """ + inverted_automorphism = {v: k for k, v in automorphism.items()} + num_vertices = game_state['num_nodes'] + + embedding_map = {} + for embedding_idx, embedding in enumerate(self.embeddings): + for vertex in range(num_vertices): + logical_idx = num_vertices * embedding_idx + vertex + physical_qubit = embedding[inverted_automorphism[vertex]] + embedding_map[logical_idx] = [physical_qubit] + + return embedding_map + + def adjudicate(self, game_state: GameState) -> AdjudicationResult: + """Adjudicate the game state using quantum annealing. + + Args: + game_state: The current game state + + Returns: + AdjudicationResult containing the adjudication details + + Raises: + ValueError: If the game state is invalid + RuntimeError: If quantum annealing fails + """ + if not self._base_sampler: + raise RuntimeError("Sampler not initialized. Call setup() first.") + + self._validate_game_state(game_state) + + num_vertices = game_state['num_nodes'] + num_embeddings = len(self.embeddings) + total_samples = np.zeros((1, num_vertices)) # Initial array for stacking + + # Process each chip run + for _ in range(self.params.num_chip_runs): + # Select random automorphism + automorphism = np.random.choice(self.automorphisms) + embedding_map = self._process_embedding(game_state, automorphism) + + # Create sampler with fixed embedding + sampler = FixedEmbeddingComposite( + self._base_sampler, + embedding=embedding_map + ) + + # Get Ising model + ising_model = self._game_state_to_ising(game_state) + + # Set up sampling parameters + sample_kwargs = { + 'num_reads': self.params.num_reads, + 'answer_mode': 'raw' + } + + if not self.params.use_mock: + sample_kwargs.update({ + 'annealing_time': self.params.anneal_time / 1000, + 'auto_scale': False + }) + + # Perform sampling + response = sampler.sample_ising( + ising_model['h'], + ising_model['j'], + **sample_kwargs + ) + + # Process samples + samples = np.array(response.record.sample) + + # Apply gauge transform if enabled + if self.params.use_gauge_transform: + flip_indices = np.random.choice( + [0, 1], + size=samples.shape[1], + p=[0.5, 0.5] + ).nonzero()[0] + samples = self._apply_gauge_transform(samples, flip_indices) + + # Stack samples for all embeddings + processed_samples = samples[:, :num_vertices] + for k in range(1, num_embeddings): + processed_samples = np.vstack(( + processed_samples, + samples[:, k*num_vertices:(k+1)*num_vertices] + )) + + total_samples = np.vstack((total_samples, processed_samples)) + + # Remove initial zero row + total_samples = np.delete(total_samples, 0, axis=0) + + # Handle isolated vertices + isolated_vertices = self._find_isolated_vertices(game_state) + if isolated_vertices: + random_samples = np.random.choice( + [1, -1], + size=(total_samples.shape[0], len(isolated_vertices)) + ) + for i, vertex in enumerate(isolated_vertices): + total_samples[:, vertex] = random_samples[:, i] + + # Calculate correlation matrix + sample_count = (self.params.num_reads * num_embeddings * + self.params.num_chip_runs) + correlation_matrix = ( + np.einsum('si,sj->ij', total_samples, total_samples) / sample_count - + np.eye(num_vertices) + ) + + # Compute results + winner, score, influence_vector = self._compute_winner_score_and_influence( + game_state, correlation_matrix + ) + + return AdjudicationResult( + game_state=game_state, + adjudicator='quantum_annealing', + winner=winner, + score=score, + influence_vector=influence_vector, + correlation_matrix=correlation_matrix, + parameters=self._parameters + ) \ No newline at end of file diff --git a/tangled_adjudicate/adjudicators/schrodinger.py b/tangled_adjudicate/adjudicators/schrodinger.py new file mode 100644 index 0000000..ff3506f --- /dev/null +++ b/tangled_adjudicate/adjudicators/schrodinger.py @@ -0,0 +1,103 @@ +from typing import Dict, Any +import numpy as np +from ..schrodinger.schrodinger_functions import evolve_schrodinger + +from .adjudicator import Adjudicator, GameState, AdjudicationResult + +class SchrodingerEquationAdjudicator(Adjudicator): + """Adjudicator implementation using Schrödinger equation evolution.""" + + def __init__(self) -> None: + """Initialize the adjudicator with default values.""" + super().__init__() + self.anneal_time: float = 5.0 # ns + self.s_min: float = 0.001 + self.s_max: float = 0.999 + + def setup(self, **kwargs) -> None: + """Configure the Schrödinger equation parameters. + + Args: + anneal_time: Annealing time in nanoseconds (default: 5.0) + s_min: Minimum annealing parameter (default: 0.001) + s_max: Maximum annealing parameter (default: 0.999) + + Raises: + ValueError: If parameters are invalid + """ + if 'anneal_time' in kwargs: + if not isinstance(kwargs['anneal_time'], (int, float)) or kwargs['anneal_time'] <= 0: + raise ValueError("anneal_time must be a positive number") + self.anneal_time = float(kwargs['anneal_time']) + + if 's_min' in kwargs: + if not isinstance(kwargs['s_min'], (int, float)) or not 0 <= kwargs['s_min'] < 1: + raise ValueError("s_min must be in [0, 1)") + self.s_min = float(kwargs['s_min']) + + if 's_max' in kwargs: + if not isinstance(kwargs['s_max'], (int, float)) or not 0 < kwargs['s_max'] <= 1: + raise ValueError("s_max must be in (0, 1]") + self.s_max = float(kwargs['s_max']) + + if self.s_min >= self.s_max: + raise ValueError("s_min must be less than s_max") + + self._parameters = { + 'anneal_time': self.anneal_time, + 's_min': self.s_min, + 's_max': self.s_max + } + + def adjudicate(self, game_state: GameState) -> AdjudicationResult: + """Adjudicate the game state using Schrödinger equation evolution. + + Args: + game_state: The current game state + + Returns: + AdjudicationResult containing the adjudication details + + Raises: + ValueError: If the game state is invalid + """ + self._validate_game_state(game_state) + + # Convert game state to Ising model + ising_model = self._game_state_to_ising(game_state) + + # Evolve Schrödinger equation + correlation_matrix = evolve_schrodinger( + ising_model['h'], + ising_model['j'], + s_min=self.s_min, + s_max=self.s_max, + tf=self.anneal_time, + n_qubits=game_state['num_nodes'] + ) + + # Make symmetric (evolve_schrodinger returns upper triangular) + correlation_matrix = correlation_matrix + correlation_matrix.T + + # Handle isolated vertices + isolated_vertices = self._find_isolated_vertices(game_state) + if isolated_vertices: + for vertex in isolated_vertices: + correlation_matrix[:, vertex] = 0 + correlation_matrix[vertex, :] = 0 + + # Compute results + winner, score, influence_vector = self._compute_winner_score_and_influence( + game_state, correlation_matrix + ) + + return AdjudicationResult( + game_state=game_state, + adjudicator='schrodinger_equation', + winner=winner, + score=score, + influence_vector=influence_vector, + correlation_matrix=correlation_matrix, + parameters=self._parameters + ) + \ No newline at end of file diff --git a/tangled_adjudicate/adjudicators/simulated_annealing.py b/tangled_adjudicate/adjudicators/simulated_annealing.py new file mode 100644 index 0000000..73250a3 --- /dev/null +++ b/tangled_adjudicate/adjudicators/simulated_annealing.py @@ -0,0 +1,113 @@ +from typing import Dict, Any +import neal +import numpy as np + +from .adjudicator import Adjudicator, GameState, AdjudicationResult + +class SimulatedAnnealingAdjudicator(Adjudicator): + """Adjudicator implementation using simulated annealing.""" + + def __init__(self) -> None: + """Initialize the adjudicator with default values.""" + super().__init__() + self.num_reads: int = 1000 + self.num_sweeps: int = 16 + self.beta_max: float = 3.0 + + def setup(self, **kwargs) -> None: + """Configure the simulated annealing parameters. + + Args: + num_reads: Number of annealing reads (default: 1000) + num_sweeps: Number of sweeps per read (default: 16) + beta_max: Maximum inverse temperature (default: 3.0) + + Raises: + ValueError: If parameters are invalid + """ + if 'num_reads' in kwargs: + if not isinstance(kwargs['num_reads'], int) or kwargs['num_reads'] <= 0: + raise ValueError("num_reads must be a positive integer") + self.num_reads = kwargs['num_reads'] + + if 'num_sweeps' in kwargs: + if not isinstance(kwargs['num_sweeps'], int) or kwargs['num_sweeps'] <= 0: + raise ValueError("num_sweeps must be a positive integer") + self.num_sweeps = kwargs['num_sweeps'] + + if 'beta_max' in kwargs: + if not isinstance(kwargs['beta_max'], (int, float)) or kwargs['beta_max'] <= 0: + raise ValueError("beta_max must be a positive number") + self.beta_max = float(kwargs['beta_max']) + + self._parameters = { + 'num_reads': self.num_reads, + 'num_sweeps': self.num_sweeps, + 'beta_max': self.beta_max + } + + def adjudicate(self, game_state: GameState) -> AdjudicationResult: + """Adjudicate the game state using simulated annealing. + + Args: + game_state: The current game state + + Returns: + AdjudicationResult containing the adjudication details + + Raises: + ValueError: If the game state is invalid + """ + self._validate_game_state(game_state) + + # Convert game state to Ising model + ising_model = self._game_state_to_ising(game_state) + sampler = neal.SimulatedAnnealingSampler() + + # Calculate beta range based on coupling strengths + beta_range = [ + 1 / np.sqrt(np.sum([Jij ** 2 for Jij in ising_model['j'].values()]) + 0.001), + self.beta_max + ] + + # Perform simulated annealing + response = sampler.sample_ising( + ising_model['h'], + ising_model['j'], + beta_range=beta_range, + num_reads=self.num_reads, + num_sweeps=self.num_sweeps, + randomize_order=True + ) + + # Calculate correlation matrix + samples = np.array(response.record.sample, dtype=float) + correlation_matrix = ( + np.einsum('si,sj->ij', samples, samples) / self.num_reads - + np.eye(game_state['num_nodes']) + ) + + # Handle isolated vertices + isolated_vertices = self._find_isolated_vertices(game_state) + if isolated_vertices: + samples = np.random.choice([1, -1], size=(self.num_reads, len(isolated_vertices))) + for i, vertex in enumerate(isolated_vertices): + correlation_matrix[:, vertex] = np.mean(samples[:, i]) + correlation_matrix[vertex, :] = np.mean(samples[:, i]) + correlation_matrix[vertex, vertex] = 0 + + # Compute results + winner, score, influence_vector = self._compute_winner_score_and_influence( + game_state, correlation_matrix + ) + + return AdjudicationResult( + game_state=game_state, + adjudicator='simulated_annealing', + winner=winner, + score=score, + influence_vector=influence_vector, + correlation_matrix=correlation_matrix, + parameters=self._parameters + ) + \ No newline at end of file From 2b6a68f51f52c5bb4f271f7400395db309a0b7be Mon Sep 17 00:00:00 2001 From: Geordie Rose Date: Wed, 29 Jan 2025 11:15:08 -0800 Subject: [PATCH 03/59] changed get_embeddings to have data_dir as input parameter --- tangled_adjudicate/utils/find_hardware_embeddings.py | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/tangled_adjudicate/utils/find_hardware_embeddings.py b/tangled_adjudicate/utils/find_hardware_embeddings.py index 5f29aa3..b27eea9 100644 --- a/tangled_adjudicate/utils/find_hardware_embeddings.py +++ b/tangled_adjudicate/utils/find_hardware_embeddings.py @@ -150,7 +150,7 @@ def raster_embedding_search(hardware_graph, subgraph, raster_breadth=2, delete_u return embmat -def get_embeddings(source_graph_number, qc_solver_to_use): +def get_embeddings(source_graph_number, qc_solver_to_use, data_dir): # generates multiple parallel embeddings into hardware for your graph # the smaller the graph, the longer this takes -- e.g. source_graph_number == 1 takes about 4 minutes # @@ -166,9 +166,7 @@ def get_embeddings(source_graph_number, qc_solver_to_use): file_name = ('embeddings_graph_number_' + str(source_graph_number) + '_raster_breadth_' + str(raster_breadth) + '_gridsize_' + str(grid_size) + '_qc_' + qc_solver_to_use + '.pkl') - data_dir = os.path.join(os.getcwd(), '..', 'data') # checks to see if /data exists; if not, creates it - - if not os.path.isdir(data_dir): + if not os.path.isdir(data_dir): # checks to see if /data exists; if not, creates it os.mkdir(data_dir) file_path = os.path.join(data_dir, file_name) From 3bdd1b8676a855ab7bbf860db8ad15066255e1c1 Mon Sep 17 00:00:00 2001 From: Geordie Rose Date: Wed, 29 Jan 2025 11:21:43 -0800 Subject: [PATCH 04/59] added self.j_map to base Adjudicator __init__ and use it in _game_state_to_ising to get correct J values --- tangled_adjudicate/adjudicators/adjudicator.py | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/tangled_adjudicate/adjudicators/adjudicator.py b/tangled_adjudicate/adjudicators/adjudicator.py index 6985089..1556ddd 100644 --- a/tangled_adjudicate/adjudicators/adjudicator.py +++ b/tangled_adjudicate/adjudicators/adjudicator.py @@ -32,6 +32,10 @@ class Adjudicator(ABC): def __init__(self) -> None: """Initialize base adjudicator.""" self._parameters: Dict[str, Any] = {} + self.j_map = {0: 0.0, # edge (i, j) uncolored , J_ij=0 + 1: 0.0, # edge (i, j) colored gray, J_ij=0 + 2: -1.0, # edge (i, j) colored green, FM coupling, J_ij=-1.0 + 3: 1.0} # edge (i, j) colored purple, AFM coupling, J_ij=+1.0 @abstractmethod def setup(self, **kwargs) -> None: @@ -77,10 +81,10 @@ def _game_state_to_ising(self, game_state: GameState) -> IsingModel: j = {} for edge in game_state['edges']: - v1, v2, weight = edge + v1, v2, edge_label = edge if v1 > v2: v1, v2 = v2, v1 - j[(v1, v2)] = float(weight) + j[(v1, v2)] = float(self.j_map[edge_label]) return IsingModel(h=h, j=j) From 71d861a0e54926ee6afb14ea9d17789308503545 Mon Sep 17 00:00:00 2001 From: Geordie Rose Date: Wed, 29 Jan 2025 11:46:43 -0800 Subject: [PATCH 05/59] changed to compare old and new; quantum_annealing not added yet --- .../utils/how_to_adjudicate_states.py | 108 +++++++++++++++--- 1 file changed, 90 insertions(+), 18 deletions(-) diff --git a/tangled_adjudicate/utils/how_to_adjudicate_states.py b/tangled_adjudicate/utils/how_to_adjudicate_states.py index 942671b..da5d02a 100644 --- a/tangled_adjudicate/utils/how_to_adjudicate_states.py +++ b/tangled_adjudicate/utils/how_to_adjudicate_states.py @@ -6,7 +6,13 @@ import time import numpy as np -from tangled_adjudicate.adjudicators.adjudicate import Adjudicator +from tangled_adjudicate.adjudicators.adjudicate import old_Adjudicator + +from tangled_adjudicate.adjudicators.simulated_annealing import SimulatedAnnealingAdjudicator +from tangled_adjudicate.adjudicators.quantum_annealing import QuantumAnnealingAdjudicator +from tangled_adjudicate.adjudicators.lookup_table import LookupTableAdjudicator +from tangled_adjudicate.adjudicators.schrodinger import SchrodingerEquationAdjudicator + from tangled_adjudicate.utils.parameters import Params from tangled_adjudicate.utils.game_graph_properties import GraphProperties from tangled_adjudicate.utils.generate_terminal_states import convert_state_string_to_game_state @@ -17,14 +23,18 @@ def main(): # there are two example_game_state dictionaries provided, which are terminal states in graph_number 2 and 3 # respectively, that are of the sort that are closest to the draw line at score = +- 1/2 - # solver_list = ['simulated_annealing', 'schrodinger_equation', 'quantum_annealing', 'look_up'] - solver_list = ['simulated_annealing', 'lookup_table'] + # solver_list = ['simulated_annealing', 'schrodinger_equation', 'quantum_annealing', 'lookup_table'] + solver_list = ['simulated_annealing', 'schrodinger_equation', 'lookup_table'] precision_digits = 4 # just to clean up print output np.set_printoptions(suppress=True) # remove scientific notation params = Params() - adjudicator = Adjudicator(params) + old_adjudicator = old_Adjudicator(params) + + args = {'data_dir': os.path.join(os.getcwd(), '..', 'data'), + 'graph_number': params.GRAPH_NUMBER, + 'solver_name': params.QC_SOLVER_TO_USE} example_game_state = None @@ -49,35 +59,97 @@ def main(): print('this introduction only has included game states for graphs 2 and 3. If you want a different' 'graph please add a new example_game_state here!') + # if 'simulated_annealing' in solver_list: + # sa_adjudicator = SimulatedAnnealingAdjudicator() + # sa_adjudicator.setup() + # start = time.time() + # new_sa_results = sa_adjudicator.adjudicate(example_game_state) + # print('elapsed time for simulated_annealing was', round(time.time() - start, precision_digits), 'seconds.') + # + # if 'quantum_annealing' in solver_list: + # qa_adjudicator = QuantumAnnealingAdjudicator() + # qa_adjudicator.setup(**args) + # new_qa_results = qa_adjudicator.adjudicate(example_game_state) + # + # if 'lookup_table' in solver_list: + # lt_adjudicator = LookupTableAdjudicator() + # lt_adjudicator.setup(**args) + # new_lt_results = lt_adjudicator.adjudicate(example_game_state) + # + # if 'schrodinger_equation' in solver_list: + # se_adjudicator = SchrodingerEquationAdjudicator() + # se_adjudicator.setup() + # new_se_results = se_adjudicator.adjudicate(example_game_state) + for solver_to_use in solver_list: start = time.time() # equivalent to e.g. results = adjudicator.simulated_annealing(example_game_state) - results = getattr(adjudicator, solver_to_use)(example_game_state) + old_results = getattr(old_adjudicator, solver_to_use)(example_game_state) + + print('elapsed time for old', solver_to_use, 'was', round(time.time() - start, precision_digits), 'seconds.') + + start = time.time() - print('elapsed time for', solver_to_use, 'was', round(time.time() - start, precision_digits), 'seconds.') + adjudicator = None - if results['correlation_matrix'] is None: - print('correlation matrix:', None) + if solver_to_use == 'simulated_annealing': + adjudicator = SimulatedAnnealingAdjudicator() else: - print('correlation matrix:') - print(np.round(results['correlation_matrix'], precision_digits)) + if solver_to_use == 'quantum_annealing': + adjudicator = QuantumAnnealingAdjudicator() + else: + if solver_to_use == 'lookup_table': + adjudicator = LookupTableAdjudicator() + else: + if solver_to_use == 'schrodinger_equation': + adjudicator = SchrodingerEquationAdjudicator() + + adjudicator.setup(**args) + new_results = adjudicator.adjudicate(example_game_state) + + print('elapsed time for new', solver_to_use, 'was', round(time.time() - start, precision_digits), 'seconds.') + + if old_results['correlation_matrix'] is None: + print('old correlation matrix:', None) + else: + print('old correlation matrix:') + print(np.round(old_results['correlation_matrix'], precision_digits)) + + if new_results['correlation_matrix'] is None: + print('new correlation matrix:', None) + else: + print('new correlation matrix:') + print(np.round(new_results['correlation_matrix'], precision_digits)) + + print('old winner:', old_results['winner']) + print('new winner:', new_results['winner']) - print('winner:', results['winner']) + if old_results['score'] is None: + print('old score:', old_results['score']) + else: + print('old score:', round(old_results['score'], precision_digits)) + + if new_results['score'] is None: + print('new score:', new_results['score']) + else: + print('new score:', round(new_results['score'], precision_digits)) - if results['score'] is None: - print('score:', results['score']) + if old_results['influence_vector'] is None: + print('old influence vector:', None) else: - print('score:', round(results['score'], precision_digits)) + print('old influence vector:', [round(old_results['influence_vector'][k], precision_digits) + for k in range(len(old_results['influence_vector']))]) - if results['influence_vector'] is None: - print('influence vector:', None) + if new_results['influence_vector'] is None: + print('new influence vector:', None) else: - print('influence vector:', [round(results['influence_vector'][k], precision_digits) - for k in range(len(results['influence_vector']))]) + print('new influence vector:', [round(new_results['influence_vector'][k], precision_digits) + for k in range(len(new_results['influence_vector']))]) print() + print() if __name__ == "__main__": From 07c0fa55ef379f7c38f73e614783bfd27d8ca76d Mon Sep 17 00:00:00 2001 From: Geordie Rose Date: Wed, 29 Jan 2025 13:02:33 -0800 Subject: [PATCH 06/59] added 'data_dir' kwarg requirement, and requirement to always load/compute automorphisms & embeddings --- .../adjudicators/quantum_annealing.py | 34 +++++++++++++------ 1 file changed, 24 insertions(+), 10 deletions(-) diff --git a/tangled_adjudicate/adjudicators/quantum_annealing.py b/tangled_adjudicate/adjudicators/quantum_annealing.py index 0728021..1d58b5a 100644 --- a/tangled_adjudicate/adjudicators/quantum_annealing.py +++ b/tangled_adjudicate/adjudicators/quantum_annealing.py @@ -1,3 +1,4 @@ +import os from typing import Dict, Any, List, Optional import numpy as np from dataclasses import dataclass @@ -8,6 +9,7 @@ from ..utils.find_hardware_embeddings import get_embeddings from .adjudicator import Adjudicator, GameState, AdjudicationResult + @dataclass class QAParameters: """Parameters for quantum annealing.""" @@ -21,6 +23,8 @@ class QAParameters: use_mock: bool = False solver_name: Optional[str] = None graph_number: Optional[int] = None + data_dir: Optional[str] = None + class QuantumAnnealingAdjudicator(Adjudicator): """Adjudicator implementation using D-Wave quantum annealing.""" @@ -70,13 +74,23 @@ def setup(self, **kwargs) -> None: raise ValueError("shim_iterations must be positive") if self.params.alpha_phi <= 0 or self.params.alpha_phi > 1: raise ValueError("alpha_phi must be in (0, 1]") - - # Get graph-specific data if graph number provided - if self.params.graph_number is not None: - self.automorphisms = get_automorphisms(self.params.graph_number) - self.embeddings = get_embeddings( - self.params.graph_number, - self.params.solver_name + + # load directory for automorphisms & embeddings + 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.params.data_dir = kwargs['data_dir'] + + self._parameters = {'data_dir': self.params.data_dir} + + # we need these so always compute / load in + self.automorphisms = get_automorphisms(self.params.graph_number, self.params.data_dir) + self.embeddings = get_embeddings( + self.params.graph_number, + self.params.solver_name, + self.params.data_dir ) # Initialize sampler @@ -234,8 +248,8 @@ def adjudicate(self, game_state: GameState) -> AdjudicationResult: total_samples[:, vertex] = random_samples[:, i] # Calculate correlation matrix - sample_count = (self.params.num_reads * num_embeddings * - self.params.num_chip_runs) + sample_count = (self.params.num_reads * num_embeddings * self.params.num_chip_runs) + correlation_matrix = ( np.einsum('si,sj->ij', total_samples, total_samples) / sample_count - np.eye(num_vertices) @@ -254,4 +268,4 @@ def adjudicate(self, game_state: GameState) -> AdjudicationResult: influence_vector=influence_vector, correlation_matrix=correlation_matrix, parameters=self._parameters - ) \ No newline at end of file + ) From 86047af3a4b1bf0108ea87d1406649a16fdb0710 Mon Sep 17 00:00:00 2001 From: Geordie Rose Date: Wed, 29 Jan 2025 13:17:35 -0800 Subject: [PATCH 07/59] PEP --- tangled_adjudicate/adjudicators/adjudicator.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/tangled_adjudicate/adjudicators/adjudicator.py b/tangled_adjudicate/adjudicators/adjudicator.py index 1556ddd..87379f4 100644 --- a/tangled_adjudicate/adjudicators/adjudicator.py +++ b/tangled_adjudicate/adjudicators/adjudicator.py @@ -3,6 +3,7 @@ import numpy as np import numpy.typing as npt + class GameState(TypedDict): num_nodes: int edges: List[Tuple[int, int, int]] # (node1, node2, weight) @@ -13,6 +14,7 @@ class GameState(TypedDict): player1_node: Optional[int] player2_node: Optional[int] + class AdjudicationResult(TypedDict): game_state: GameState adjudicator: str @@ -22,10 +24,12 @@ class AdjudicationResult(TypedDict): correlation_matrix: Optional[npt.NDArray[np.float64]] parameters: Dict[str, Union[str, int, float, bool]] + class IsingModel(TypedDict): h: Dict[int, float] # Local fields j: Dict[Tuple[int, int], float] # Coupling strengths + class Adjudicator(ABC): """Base interface for game state adjudication implementations.""" @@ -136,4 +140,4 @@ def _compute_winner_score_and_influence( else: winner = 'draw' - return winner, score, influence_vector \ No newline at end of file + return winner, score, influence_vector From d5b363d622e30c4404bbbe19ed183e1b2e00e76d Mon Sep 17 00:00:00 2001 From: Geordie Rose Date: Wed, 29 Jan 2025 15:32:31 -0800 Subject: [PATCH 08/59] temporarily set USE_QC and USE_MOCK_DWAVE_SAMPLER to True for testing --- tangled_adjudicate/utils/parameters.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tangled_adjudicate/utils/parameters.py b/tangled_adjudicate/utils/parameters.py index dce9240..2361743 100644 --- a/tangled_adjudicate/utils/parameters.py +++ b/tangled_adjudicate/utils/parameters.py @@ -19,8 +19,8 @@ def __init__(self): # The defaults here are no shimming, no gauge transforms, only use M=1 automorphism, and collect a lot of # samples (N=1000) - self.USE_QC = False # set to False if you just want to use e.g. simulated annealer - self.USE_MOCK_DWAVE_SAMPLER = False # set to True if you want a software version of the hardware (doesn't sample like the HW tho so don't trust it, just for debugging) + self.USE_QC = True # set to False if you just want to use e.g. simulated annealer + self.USE_MOCK_DWAVE_SAMPLER = True # set to True if you want a software version of the hardware (doesn't sample like the HW tho so don't trust it, just for debugging) self.QC_SOLVER_TO_USE = 'Advantage2_prototype2.6' # modify if you want to use a different QC self.NUMBER_OF_CHIP_RUNS = 1 # this is M From 89392c3f64e3a5cc8c21fcc3c20f0a529190d95f Mon Sep 17 00:00:00 2001 From: Geordie Rose Date: Wed, 29 Jan 2025 15:35:08 -0800 Subject: [PATCH 09/59] added self.data_dir to __init__, changed variable names to be the same as the new thing --- tangled_adjudicate/adjudicators/adjudicate.py | 57 ++++++++++--------- 1 file changed, 29 insertions(+), 28 deletions(-) diff --git a/tangled_adjudicate/adjudicators/adjudicate.py b/tangled_adjudicate/adjudicators/adjudicate.py index 1f04785..73f5263 100644 --- a/tangled_adjudicate/adjudicators/adjudicate.py +++ b/tangled_adjudicate/adjudicators/adjudicate.py @@ -23,9 +23,10 @@ class old_Adjudicator(object): def __init__(self, params): self.params = params self.results_dict = None + self.data_dir = os.path.join(os.getcwd(), '..', 'data') if self.params.USE_QC: # if using QC, get embeddings and automorphisms - self.automorphisms = get_automorphisms(self.params.GRAPH_NUMBER) - self.embeddings = get_embeddings(self.params.GRAPH_NUMBER, self.params.QC_SOLVER_TO_USE) + self.automorphisms = get_automorphisms(self.params.GRAPH_NUMBER, self.data_dir) + self.embeddings = get_embeddings(self.params.GRAPH_NUMBER, self.params.QC_SOLVER_TO_USE, self.data_dir) def compute_winner_score_and_influence_from_correlation_matrix(self, game_state, correlation_matrix): # correlation_matrix is assumed to be symmetric matrix with zeros on diagonal (so that self-correlation of @@ -134,10 +135,10 @@ def schrodinger_equation(self, game_state): def quantum_annealing(self, game_state): - number_of_embeddings = len(self.embeddings) # e.g. P=343 - number_of_problem_variables = game_state['num_nodes'] # e.g. 3 + num_vertices = game_state['num_nodes'] # e.g. 3 + num_embeddings = len(self.embeddings) # e.g. P=343 + total_samples = np.zeros((1, num_vertices)) # 0th layer to get vstack going, remove at the end - samples = np.zeros((1, number_of_problem_variables)) # 0th layer to get vstack going, remove at the end shim_stats = None all_samples = None indices_of_flips = None @@ -180,14 +181,14 @@ def quantum_annealing(self, game_state): # this finds any isolated vertices that may be in the graph -- we will replace the samples returned for these # at the end with true 50/50 statistics, so we don't have to worry about them - isolated_vertices = find_isolated_vertices(number_of_problem_variables, base_jay) + isolated_vertices = find_isolated_vertices(num_vertices, base_jay) # We now enter a loop where each pass through the loop programs the chip to specific values of h and J but # now for the entire chip. We do this by first selecting one automorphism and embedding it in multiple # parallel ways across the entire chip, and then optionally applying a gauge transform across all the qubits # used. This latter process chooses different random gauges for each of the embedded instances. - for chip_run_idx in range(self.params.NUMBER_OF_CHIP_RUNS): + for _ in range(self.params.NUMBER_OF_CHIP_RUNS): # ******************************************************************* # Step 1: Randomly select an automorphism and embed it multiple times @@ -198,9 +199,9 @@ def quantum_annealing(self, game_state): permuted_embedding = [] - for each_embedding in self.embeddings[:number_of_embeddings]: # each_embedding is like [1093, 1098, 136]; 343 of these for three-vertex graph + for each_embedding in self.embeddings[:num_embeddings]: # each_embedding is like [1093, 1098, 136]; 343 of these for three-vertex graph this_embedding = [] - for each_vertex in range(number_of_problem_variables): # each_vertex ranges from 0 to 2 + for each_vertex in range(num_vertices): # each_vertex ranges from 0 to 2 this_embedding.append(each_embedding[inverted_automorphism_to_use[each_vertex]]) permuted_embedding.append(this_embedding) @@ -209,9 +210,9 @@ def quantum_annealing(self, game_state): embedding_to_use = {} - for embedding_idx in range(number_of_embeddings): - for each_vertex in range(number_of_problem_variables): # up to 0..1037 - embedding_to_use[number_of_problem_variables * embedding_idx + each_vertex] = \ + for embedding_idx in range(num_embeddings): + for each_vertex in range(num_vertices): # up to 0..1037 + embedding_to_use[num_vertices * embedding_idx + each_vertex] = \ [permuted_embedding[embedding_idx][each_vertex]] # ***************************************************************************************************** @@ -226,17 +227,17 @@ def quantum_annealing(self, game_state): full_h = {} full_j = {} - for embedding_idx in range(number_of_embeddings): - for each_vertex in range(number_of_problem_variables): - full_h[number_of_problem_variables * embedding_idx + each_vertex] = 0 + for embedding_idx in range(num_embeddings): + for each_vertex in range(num_vertices): + full_h[num_vertices * embedding_idx + each_vertex] = 0 for k, v in base_jay.items(): edge_under_automorph = (min(automorphism_to_use[k[0]], automorphism_to_use[k[1]]), max(automorphism_to_use[k[0]], automorphism_to_use[k[1]])) full_j[edge_under_automorph] = v - for j in range(1, number_of_embeddings): - full_j[(edge_under_automorph[0] + number_of_problem_variables * j, - edge_under_automorph[1] + number_of_problem_variables * j)] = v + for j in range(1, num_embeddings): + full_j[(edge_under_automorph[0] + num_vertices * j, + edge_under_automorph[1] + num_vertices * j)] = v # ************************************************************************** # Step 3: Choose random gauge, modify h, J parameters for full chip using it @@ -311,11 +312,11 @@ def quantum_annealing(self, game_state): # *********************************** # this should make a big fat stack of the results in BLUE variable ordering - all_samples_processed_blue = all_samples[:, range(number_of_problem_variables)] - for k in range(1, number_of_embeddings): + all_samples_processed_blue = all_samples[:, range(num_vertices)] + for k in range(1, num_embeddings): all_samples_processed_blue = np.vstack((all_samples_processed_blue, - all_samples[:, range(number_of_problem_variables * k, - number_of_problem_variables * (k + 1))])) + all_samples[:, range(num_vertices * k, + num_vertices * (k + 1))])) # ********************************************************************** # Step 9: Reorder columns to make them BLACK order instead of BLUE order @@ -327,23 +328,23 @@ def quantum_annealing(self, game_state): # Step 10: Add new samples to the stack, all in BLACK order # ********************************************************* - samples = np.vstack((samples, all_samples_processed_black)) + total_samples = np.vstack((total_samples, all_samples_processed_black)) # *************************************************************** # Step 11: Post process samples stack to extract return variables # *************************************************************** - samples = np.delete(samples, (0), axis=0) # delete first row of zeros + total_samples = np.delete(total_samples, (0), axis=0) # delete first row of zeros # replace columns where there are disconnected variables with truly random samples for idx in isolated_vertices: - samples[:, idx] = np.random.choice([1, -1], size=samples.shape[0]) + total_samples[:, idx] = np.random.choice([1, -1], size=total_samples.shape[0]) - sample_count = self.params.NUM_READS_QC * number_of_embeddings * self.params.NUMBER_OF_CHIP_RUNS + sample_count = self.params.NUM_READS_QC * num_embeddings * self.params.NUMBER_OF_CHIP_RUNS # this is a full matrix with zeros on the diagonal that uses all the samples correlation_matrix = \ - (np.einsum('si,sj->ij', samples, samples) / sample_count - + (np.einsum('si,sj->ij', total_samples, total_samples) / sample_count - np.eye(int(game_state['num_nodes']))) winner, score_difference, influence_vector = ( @@ -383,4 +384,4 @@ def lookup_table(self, game_state): 'winner': winner, 'score': None, 'influence_vector': None, 'correlation_matrix': None, 'parameters': self.params} - return return_dictionary \ No newline at end of file + return return_dictionary From ee97af56c0abbc78450f412df86c827aa0720fef Mon Sep 17 00:00:00 2001 From: Geordie Rose Date: Wed, 29 Jan 2025 15:35:35 -0800 Subject: [PATCH 10/59] just quantum_annealing to test --- tangled_adjudicate/utils/how_to_adjudicate_states.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tangled_adjudicate/utils/how_to_adjudicate_states.py b/tangled_adjudicate/utils/how_to_adjudicate_states.py index da5d02a..f209542 100644 --- a/tangled_adjudicate/utils/how_to_adjudicate_states.py +++ b/tangled_adjudicate/utils/how_to_adjudicate_states.py @@ -24,7 +24,7 @@ def main(): # respectively, that are of the sort that are closest to the draw line at score = +- 1/2 # solver_list = ['simulated_annealing', 'schrodinger_equation', 'quantum_annealing', 'lookup_table'] - solver_list = ['simulated_annealing', 'schrodinger_equation', 'lookup_table'] + solver_list = ['quantum_annealing'] precision_digits = 4 # just to clean up print output np.set_printoptions(suppress=True) # remove scientific notation From 1469ff949eeefd74d348a75de6037c5775be7b2b Mon Sep 17 00:00:00 2001 From: Geordie Rose Date: Wed, 29 Jan 2025 15:36:28 -0800 Subject: [PATCH 11/59] Work in Progress -- going through carefully --- .../adjudicators/quantum_annealing.py | 57 +++++++++++-------- 1 file changed, 34 insertions(+), 23 deletions(-) diff --git a/tangled_adjudicate/adjudicators/quantum_annealing.py b/tangled_adjudicate/adjudicators/quantum_annealing.py index 1d58b5a..5406f94 100644 --- a/tangled_adjudicate/adjudicators/quantum_annealing.py +++ b/tangled_adjudicate/adjudicators/quantum_annealing.py @@ -20,7 +20,7 @@ class QAParameters: use_shim: bool = False shim_iterations: int = 1 alpha_phi: float = 0.1 - use_mock: bool = False + use_mock: bool = True solver_name: Optional[str] = None graph_number: Optional[int] = None data_dir: Optional[str] = None @@ -35,7 +35,8 @@ def __init__(self) -> None: self.params = QAParameters() self.embeddings: List[List[int]] = [] self.automorphisms: List[Dict[int, int]] = [] - self.sampler: Optional[FixedEmbeddingComposite] = None + self.shim_stats: Dict[str] = {} + # self.sampler: Optional[FixedEmbeddingComposite] = None def setup(self, **kwargs) -> None: """Configure quantum annealing parameters and initialize D-Wave connection. @@ -83,23 +84,16 @@ def setup(self, **kwargs) -> None: raise ValueError(f"Directory not found: {kwargs['data_dir']}") self.params.data_dir = kwargs['data_dir'] - self._parameters = {'data_dir': self.params.data_dir} + # self._parameters = {'data_dir': self.params.data_dir} # we need these so always compute / load in self.automorphisms = get_automorphisms(self.params.graph_number, self.params.data_dir) - self.embeddings = get_embeddings( - self.params.graph_number, - self.params.solver_name, - self.params.data_dir - ) + self.embeddings = get_embeddings(self.params.graph_number, self.params.solver_name, self.params.data_dir) # Initialize sampler try: if self.params.use_mock: - base_sampler = MockDWaveSampler( - topology_type='zephyr', - topology_shape=[6, 4] - ) + base_sampler = MockDWaveSampler(topology_type='zephyr', topology_shape=[6, 4]) else: base_sampler = DWaveSampler(solver=self.params.solver_name) @@ -108,7 +102,13 @@ def setup(self, **kwargs) -> None: except Exception as e: raise RuntimeError(f"Failed to initialize D-Wave sampler: {str(e)}") - + + # initialize shim_stats if required + if self.params.use_shim: + self.shim_stats = {'qubit_magnetizations': [], + 'average_absolute_value_of_magnetization': [], + 'all_flux_bias_offsets': []} + # Store parameters self._parameters = self.params.__dict__ @@ -177,7 +177,13 @@ def adjudicate(self, game_state: GameState) -> AdjudicationResult: num_vertices = game_state['num_nodes'] num_embeddings = len(self.embeddings) total_samples = np.zeros((1, num_vertices)) # Initial array for stacking - + + all_samples = None + indices_of_flips = None + + if self.params.use_mock and self.params.use_shim: + print('D-Wave mock sampler is not set up to use the shimming process, turn shim off if using mock!') + # Process each chip run for _ in range(self.params.num_chip_runs): # Select random automorphism @@ -185,36 +191,41 @@ def adjudicate(self, game_state: GameState) -> AdjudicationResult: embedding_map = self._process_embedding(game_state, automorphism) # Create sampler with fixed embedding - sampler = FixedEmbeddingComposite( - self._base_sampler, - embedding=embedding_map - ) + sampler = FixedEmbeddingComposite(self._base_sampler, embedding=embedding_map) # Get Ising model ising_model = self._game_state_to_ising(game_state) # Set up sampling parameters - sample_kwargs = { + sampler_kwargs = { 'num_reads': self.params.num_reads, 'answer_mode': 'raw' } if not self.params.use_mock: - sample_kwargs.update({ + sampler_kwargs.update({ + 'fast_anneal': True, 'annealing_time': self.params.anneal_time / 1000, 'auto_scale': False }) - + + if self.params.use_shim: + sampler_kwargs.update({'readout_thermalization': 100., + 'auto_scale': False, + 'flux_drift_compensation': True, + 'flux_biases': [0] * base_sampler.properties['num_qubits']}) + # Perform sampling response = sampler.sample_ising( ising_model['h'], ising_model['j'], - **sample_kwargs + **sampler_kwargs ) # Process samples samples = np.array(response.record.sample) - + + # todo this is in the wrong order # Apply gauge transform if enabled if self.params.use_gauge_transform: flip_indices = np.random.choice( From d9a0f5beca4667e37a9bb0de6d9e2c4600ef4b35 Mon Sep 17 00:00:00 2001 From: Geordie Rose Date: Thu, 30 Jan 2025 10:32:05 -0800 Subject: [PATCH 12/59] chnaged variable names to match erik --- tangled_adjudicate/adjudicators/adjudicate.py | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/tangled_adjudicate/adjudicators/adjudicate.py b/tangled_adjudicate/adjudicators/adjudicate.py index 73f5263..3aec01f 100644 --- a/tangled_adjudicate/adjudicators/adjudicate.py +++ b/tangled_adjudicate/adjudicators/adjudicate.py @@ -194,8 +194,8 @@ def quantum_annealing(self, game_state): # Step 1: Randomly select an automorphism and embed it multiple times # ******************************************************************* - automorphism_to_use = random.choice(self.automorphisms) # eg {0:0, 1:2, 2:1} - inverted_automorphism_to_use = {v: k for k, v in automorphism_to_use.items()} # swaps key <-> values + automorphism = random.choice(self.automorphisms) # eg {0:0, 1:2, 2:1} + inverted_automorphism_to_use = {v: k for k, v in automorphism.items()} # swaps key <-> values permuted_embedding = [] @@ -208,11 +208,11 @@ def quantum_annealing(self, game_state): # given that permuted_embedding looks like [[1229, 1235, 563], [872, 242, 866], ...] # this next part converts into the format {0: [1229], 1: [1235], 2: [563], 3: [872], 4: [242], 5: [866]} - embedding_to_use = {} + embedding_map = {} for embedding_idx in range(num_embeddings): for each_vertex in range(num_vertices): # up to 0..1037 - embedding_to_use[num_vertices * embedding_idx + each_vertex] = \ + embedding_map[num_vertices * embedding_idx + each_vertex] = \ [permuted_embedding[embedding_idx][each_vertex]] # ***************************************************************************************************** @@ -232,8 +232,8 @@ def quantum_annealing(self, game_state): full_h[num_vertices * embedding_idx + each_vertex] = 0 for k, v in base_jay.items(): - edge_under_automorph = (min(automorphism_to_use[k[0]], automorphism_to_use[k[1]]), - max(automorphism_to_use[k[0]], automorphism_to_use[k[1]])) + edge_under_automorph = (min(automorphism[k[0]], automorphism[k[1]]), + max(automorphism[k[0]], automorphism[k[1]])) full_j[edge_under_automorph] = v for j in range(1, num_embeddings): full_j[(edge_under_automorph[0] + num_vertices * j, @@ -260,7 +260,7 @@ def quantum_annealing(self, game_state): sampler_kwargs.update({'h': full_h, 'J': full_j}) - sampler = FixedEmbeddingComposite(base_sampler, embedding=embedding_to_use) # applies the embedding + sampler = FixedEmbeddingComposite(base_sampler, embedding=embedding_map) # applies the embedding # ************************************************************************* # Step 5: Optionally start shimming process in the BLUE with RED STAR basis @@ -286,7 +286,7 @@ def quantum_annealing(self, game_state): shim_stats['average_absolute_value_of_magnetization'].append(np.sum([abs(k) for k in magnetization])/len(magnetization)) qubit_magnetization = [0] * base_sampler.properties['num_qubits'] - for k, v in embedding_to_use.items(): + for k, v in embedding_map.items(): qubit_magnetization[v[0]] = magnetization[k] # check shim_stats['qubit_magnetizations'].append(qubit_magnetization) @@ -322,7 +322,7 @@ def quantum_annealing(self, game_state): # Step 9: Reorder columns to make them BLACK order instead of BLUE order # ********************************************************************** - all_samples_processed_black = all_samples_processed_blue[:, [automorphism_to_use[i] for i in range(all_samples_processed_blue.shape[1])]] + all_samples_processed_black = all_samples_processed_blue[:, [automorphism[i] for i in range(all_samples_processed_blue.shape[1])]] # ********************************************************* # Step 10: Add new samples to the stack, all in BLACK order From f7d765f5d14048b05eecadc793814160f9f7013e Mon Sep 17 00:00:00 2001 From: Geordie Rose Date: Thu, 30 Jan 2025 10:32:19 -0800 Subject: [PATCH 13/59] WIP --- .../adjudicators/quantum_annealing.py | 234 +++++++++++++++++- 1 file changed, 222 insertions(+), 12 deletions(-) diff --git a/tangled_adjudicate/adjudicators/quantum_annealing.py b/tangled_adjudicate/adjudicators/quantum_annealing.py index 5406f94..4577321 100644 --- a/tangled_adjudicate/adjudicators/quantum_annealing.py +++ b/tangled_adjudicate/adjudicators/quantum_annealing.py @@ -133,26 +133,41 @@ def _apply_gauge_transform( def _process_embedding( self, game_state: GameState, - automorphism: Dict[int, int] + automorphism: Dict[int, int], + num_embeddings: int ) -> Dict[int, List[int]]: """Process embedding with given automorphism. Args: game_state: Current game state automorphism: Graph automorphism to apply + num_embeddings: number of embeddings to use; default is all of them Returns: Processed embedding mapping """ - inverted_automorphism = {v: k for k, v in automorphism.items()} + num_vertices = game_state['num_nodes'] - + + inverted_automorphism_to_use = {v: k for k, v in automorphism.items()} # swaps key <-> values + + permuted_embedding = [] + + for each_embedding in self.embeddings[:num_embeddings]: # each_embedding is like [1093, 1098, 136]; 343 of these for three-vertex graph + this_embedding = [] + for each_vertex in range(num_vertices): # each_vertex ranges from 0 to 2 + this_embedding.append(each_embedding[inverted_automorphism_to_use[each_vertex]]) + permuted_embedding.append(this_embedding) + + # given that permuted_embedding looks like [[1229, 1235, 563], [872, 242, 866], ...] + # this next part converts into the format {0: [1229], 1: [1235], 2: [563], 3: [872], 4: [242], 5: [866]} + embedding_map = {} - for embedding_idx, embedding in enumerate(self.embeddings): - for vertex in range(num_vertices): - logical_idx = num_vertices * embedding_idx + vertex - physical_qubit = embedding[inverted_automorphism[vertex]] - embedding_map[logical_idx] = [physical_qubit] + + for embedding_idx in range(num_embeddings): + for each_vertex in range(num_vertices): # up to 0..1037 + embedding_map[num_vertices * embedding_idx + each_vertex] = \ + [permuted_embedding[embedding_idx][each_vertex]] return embedding_map @@ -181,15 +196,210 @@ def adjudicate(self, game_state: GameState) -> AdjudicationResult: all_samples = None indices_of_flips = None + # set up sampler kwargs if self.params.use_mock and self.params.use_shim: print('D-Wave mock sampler is not set up to use the shimming process, turn shim off if using mock!') - # Process each chip run + sampler_kwargs = { + 'num_reads': self.params.num_reads, + 'answer_mode': 'raw' + } + + if self.params.use_mock: + base_sampler = MockDWaveSampler(topology_type='zephyr', topology_shape=[6, 4]) + else: + base_sampler = DWaveSampler(solver=self.params.QC_SOLVER_TO_USE) + sampler_kwargs.update({ + 'fast_anneal': True, + 'annealing_time': self.params.anneal_time / 1000, + 'auto_scale': False + }) + + if self.params.use_shim: + shim_stats = {'qubit_magnetizations': [], + 'average_absolute_value_of_magnetization': [], + 'all_flux_bias_offsets': []} + sampler_kwargs.update({'readout_thermalization': 100., + 'auto_scale': False, + 'flux_drift_compensation': True, + 'flux_biases': [0] * base_sampler.properties['num_qubits']}) + shim_iterations = self.params.shim_iterations + else: + shim_iterations = 1 # if we don't shim, just run through shim step only once + + # ********************************************************** + # Step 0: convert game_state to the desired base Ising model + # ********************************************************** + + # for tangled, h_j=0 for all vertices j in the game graph, and J_ij is one of +1, -1, or 0 for all vertex + # pairs i,j. I named the "base" values (the actual problem defined on the game graph we are asked to solve) + # base_ising_model. + + base_ising_model = self._game_state_to_ising(game_state) + + # this finds any isolated vertices that may be in the graph -- we will replace the samples returned for these + # at the end with true 50/50 statistics, so we don't have to worry about them + + isolated_vertices = self._find_isolated_vertices(game_state) + + # We now enter a loop where each pass through the loop programs the chip to specific values of h and J but + # now for the entire chip. We do this by first selecting one automorphism and embedding it in multiple + # parallel ways across the entire chip, and then optionally applying a gauge transform across all the qubits + # used. This latter process chooses different random gauges for each of the embedded instances. + for _ in range(self.params.num_chip_runs): - # Select random automorphism + + # ******************************************************************* + # Step 1: Randomly select an automorphism and embed it multiple times + # ******************************************************************* + automorphism = np.random.choice(self.automorphisms) - embedding_map = self._process_embedding(game_state, automorphism) - + embedding_map = self._process_embedding(game_state, automorphism, num_embeddings) + + # ***************************************************************************************************** + # Step 2: Set h, J parameters for full chip using parallel embeddings of a randomly chosen automorphism + # ***************************************************************************************************** + + # compute full_h and full_j which are h, jay values for the entire chip assuming the above automorphism + # I am calling the problem definition and variable ordering before the automorphism the BLACK or BASE + # situation. After the automorphism the problem definition and variable labels change -- I'm calling the + # situation after the automorphism has been applied the BLUE situation. + + full_h = {} + full_j = {} + + for embedding_idx in range(num_embeddings): + for each_vertex in range(num_vertices): + full_h[num_vertices * embedding_idx + each_vertex] = 0 + + for k, v in base_ising_model['j'].items(): # is this correct? + edge_under_automorph = (min(automorphism[k[0]], automorphism[k[1]]), + max(automorphism[k[0]], automorphism[k[1]])) + full_j[edge_under_automorph] = v + for j in range(1, num_embeddings): + full_j[(edge_under_automorph[0] + num_vertices * j, + edge_under_automorph[1] + num_vertices * j)] = v + + # ************************************************************************** + # Step 3: Choose random gauge, modify h, J parameters for full chip using it + # ************************************************************************** + + # next we optionally apply a random gauge transformation. I call the situation after the gauge + # transformation has been applied the BLUE with RED STAR situation. + + if self.params.use_gauge_transform: + flip_map = [random.choice([-1, 1]) for _ in full_h] # random list of +1, -1 values of len # qubits + indices_of_flips = [i for i, x in enumerate(flip_map) if x == -1] # the indices of the -1 values + + for edge_key, j_val in full_j.items(): # for each edge and associated J value + full_j[edge_key] = j_val * flip_map[edge_key[0]] * flip_map[edge_key[1]] # Jij -> J_ij g_i g_j + + # ***************************************** + # Step 4: Choose sampler and its parameters + # ***************************************** + + sampler_kwargs.update({'h': full_h, + 'J': full_j}) + + sampler = FixedEmbeddingComposite(base_sampler, embedding=embedding_map) # applies the embedding + + # ************************************************************************* + # Step 5: Optionally start shimming process in the BLUE with RED STAR basis + # ************************************************************************* + + # all of this in the BLUE with RED STAR basis, ie post automorph, post gauge transform + for shim_iteration_idx in range(shim_iterations): + + # ************************************** + # Step 6: Generate samples from hardware + # ************************************** + + ss = sampler.sample_ising(**sampler_kwargs) + all_samples = ss.record.sample + + if self.params.use_shim: + + # ************************************************************* + # Step 6a: Compute average values of each qubit == magnetization + # ************************************************************* + + magnetization = np.sum(all_samples, axis=0)/self.params.NUM_READS_QC # BLUE with RED STAR label ordering + shim_stats['average_absolute_value_of_magnetization'].append(np.sum([abs(k) for k in magnetization])/len(magnetization)) + + qubit_magnetization = [0] * base_sampler.properties['num_qubits'] + for k, v in embedding_map.items(): + qubit_magnetization[v[0]] = magnetization[k] # check + + shim_stats['qubit_magnetizations'].append(qubit_magnetization) + + # ************************************** + # Step 6b: Adjust flux bias offset terms + # ************************************** + + for k in range(base_sampler.properties['num_qubits']): + sampler_kwargs['flux_biases'][k] -= self.params.ALPHA_PHI * qubit_magnetization[k] + + shim_stats['all_flux_bias_offsets'].append(sampler_kwargs['flux_biases']) + + # ***************************************************************************************************** + # Step 7: Reverse gauge transform, from BLUE with RED STAR to just BLUE, after shimming process is done + # ***************************************************************************************************** + + if self.params.use_gauge_transform: + all_samples[:, indices_of_flips] = -all_samples[:, indices_of_flips] + + # *********************************** + # Step 8: Stack samples in BLUE order + # *********************************** + + # this should make a big fat stack of the results in BLUE variable ordering + all_samples_processed_blue = all_samples[:, range(num_vertices)] + for k in range(1, num_embeddings): + all_samples_processed_blue = np.vstack((all_samples_processed_blue, + all_samples[:, range(num_vertices * k, + num_vertices * (k + 1))])) + + # ********************************************************************** + # Step 9: Reorder columns to make them BLACK order instead of BLUE order + # ********************************************************************** + + all_samples_processed_black = all_samples_processed_blue[:, [automorphism[i] for i in range(all_samples_processed_blue.shape[1])]] + + # ********************************************************* + # Step 10: Add new samples to the stack, all in BLACK order + # ********************************************************* + + total_samples = np.vstack((total_samples, all_samples_processed_black)) + + # *************************************************************** + # Step 11: Post process samples stack to extract return variables + # *************************************************************** + + total_samples = np.delete(total_samples, (0), axis=0) # delete first row of zeros + + # replace columns where there are disconnected variables with truly random samples + for idx in isolated_vertices: + total_samples[:, idx] = np.random.choice([1, -1], size=total_samples.shape[0]) + + sample_count = self.params.NUM_READS_QC * num_embeddings * self.params.NUMBER_OF_CHIP_RUNS + + # this is a full matrix with zeros on the diagonal that uses all the samples + correlation_matrix = \ + (np.einsum('si,sj->ij', total_samples, total_samples) / sample_count - + np.eye(int(game_state['num_nodes']))) + + winner, score_difference, influence_vector = ( + self.compute_winner_score_and_influence_from_correlation_matrix(game_state, correlation_matrix)) + + # todo make this compatible with output of erik's version + return_dictionary = {'game_state': game_state, 'adjudicator': 'quantum_annealing', + 'winner': winner, 'score': score_difference, 'influence_vector': influence_vector, + 'correlation_matrix': correlation_matrix, 'parameters': self.params} + + return return_dictionary + + + # Create sampler with fixed embedding sampler = FixedEmbeddingComposite(self._base_sampler, embedding=embedding_map) From 5ef74981445b0bde327baf8bf90e705bf8fb34e1 Mon Sep 17 00:00:00 2001 From: Geordie Rose Date: Fri, 31 Jan 2025 12:22:17 -0800 Subject: [PATCH 14/59] fixed epsilon value --- tangled_adjudicate/adjudicators/adjudicator.py | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/tangled_adjudicate/adjudicators/adjudicator.py b/tangled_adjudicate/adjudicators/adjudicator.py index 87379f4..7757d1f 100644 --- a/tangled_adjudicate/adjudicators/adjudicator.py +++ b/tangled_adjudicate/adjudicators/adjudicator.py @@ -113,7 +113,7 @@ def _compute_winner_score_and_influence( self, game_state: GameState, correlation_matrix: npt.NDArray[np.float64], - epsilon: float = 1e-6 + epsilon: float = 0.5 ) -> Tuple[Optional[str], Optional[float], npt.NDArray[np.float64]]: """Compute winner, score and influence from correlation matrix.""" if not isinstance(correlation_matrix, np.ndarray): @@ -130,8 +130,7 @@ def _compute_winner_score_and_influence( if game_state['player1_node'] is None or game_state['player2_node'] is None: return None, None, influence_vector - score = (influence_vector[game_state['player1_node']] - - influence_vector[game_state['player2_node']]) + score = influence_vector[game_state['player1_node']] - influence_vector[game_state['player2_node']] if score > epsilon: winner = 'red' From 67423c9953a0b98eb0db9062718a639209ff9782 Mon Sep 17 00:00:00 2001 From: Geordie Rose Date: Fri, 31 Jan 2025 12:28:19 -0800 Subject: [PATCH 15/59] comment and PEP changes --- tangled_adjudicate/adjudicators/adjudicator.py | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/tangled_adjudicate/adjudicators/adjudicator.py b/tangled_adjudicate/adjudicators/adjudicator.py index 7757d1f..0063b8f 100644 --- a/tangled_adjudicate/adjudicators/adjudicator.py +++ b/tangled_adjudicate/adjudicators/adjudicator.py @@ -6,7 +6,7 @@ class GameState(TypedDict): num_nodes: int - edges: List[Tuple[int, int, int]] # (node1, node2, weight) + edges: List[Tuple[int, int, int]] # (node1, node2, edge_label=0,1,2,3) player1_id: str player2_id: str turn_count: int @@ -68,8 +68,7 @@ def _validate_game_state(self, game_state: GameState) -> None: for edge in game_state['edges']: if len(edge) != 3: raise ValueError(f"Invalid edge format: {edge}") - if not (0 <= edge[0] < game_state['num_nodes'] and - 0 <= edge[1] < game_state['num_nodes']): + if not (0 <= edge[0] < game_state['num_nodes'] and 0 <= edge[1] < game_state['num_nodes']): raise ValueError(f"Edge vertices out of range: {edge}") def _game_state_to_ising(self, game_state: GameState) -> IsingModel: From 9c2c30f905758eb58422ff6252e789849c3441ee Mon Sep 17 00:00:00 2001 From: Geordie Rose Date: Fri, 31 Jan 2025 12:29:59 -0800 Subject: [PATCH 16/59] PEP --- tangled_adjudicate/adjudicators/lookup_table.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tangled_adjudicate/adjudicators/lookup_table.py b/tangled_adjudicate/adjudicators/lookup_table.py index 994c3f3..de2a49b 100644 --- a/tangled_adjudicate/adjudicators/lookup_table.py +++ b/tangled_adjudicate/adjudicators/lookup_table.py @@ -10,6 +10,7 @@ ) from .adjudicator import Adjudicator, GameState, AdjudicationResult + class LookupTableAdjudicator(Adjudicator): """Adjudicator implementation using pre-computed lookup tables.""" @@ -88,8 +89,7 @@ def adjudicate(self, game_state: GameState) -> AdjudicationResult: self._validate_game_state(game_state) # Load lookup table if needed - if (self.results_dict is None or - len(next(iter(self.results_dict.keys()))) != game_state['num_nodes']): + if (self.results_dict is None or len(next(iter(self.results_dict.keys()))) != game_state['num_nodes']): self._load_lookup_table(game_state['num_nodes']) if not self.results_dict: From 54e37e2c8d095eaa894155c63db1ab6dfa28ebd0 Mon Sep 17 00:00:00 2001 From: Geordie Rose Date: Fri, 31 Jan 2025 12:32:14 -0800 Subject: [PATCH 17/59] reverted to the original code but with the new wrappings --- .../adjudicators/quantum_annealing.py | 158 +++--------------- 1 file changed, 21 insertions(+), 137 deletions(-) diff --git a/tangled_adjudicate/adjudicators/quantum_annealing.py b/tangled_adjudicate/adjudicators/quantum_annealing.py index 4577321..552d9b6 100644 --- a/tangled_adjudicate/adjudicators/quantum_annealing.py +++ b/tangled_adjudicate/adjudicators/quantum_annealing.py @@ -16,12 +16,12 @@ class QAParameters: num_reads: int = 1000 anneal_time: float = 5.0 # ns num_chip_runs: int = 1 - use_gauge_transform: bool = True + use_gauge_transform: bool = False use_shim: bool = False shim_iterations: int = 1 alpha_phi: float = 0.1 use_mock: bool = True - solver_name: Optional[str] = None + solver_name: str = 'Advantage2_prototype2.6' graph_number: Optional[int] = None data_dir: Optional[str] = None @@ -36,8 +36,7 @@ def __init__(self) -> None: self.embeddings: List[List[int]] = [] self.automorphisms: List[Dict[int, int]] = [] self.shim_stats: Dict[str] = {} - # self.sampler: Optional[FixedEmbeddingComposite] = None - + def setup(self, **kwargs) -> None: """Configure quantum annealing parameters and initialize D-Wave connection. @@ -84,8 +83,6 @@ def setup(self, **kwargs) -> None: raise ValueError(f"Directory not found: {kwargs['data_dir']}") self.params.data_dir = kwargs['data_dir'] - # self._parameters = {'data_dir': self.params.data_dir} - # we need these so always compute / load in self.automorphisms = get_automorphisms(self.params.graph_number, self.params.data_dir) self.embeddings = get_embeddings(self.params.graph_number, self.params.solver_name, self.params.data_dir) @@ -111,25 +108,7 @@ def setup(self, **kwargs) -> None: # Store parameters self._parameters = self.params.__dict__ - - def _apply_gauge_transform( - self, - samples: np.ndarray, - flip_indices: List[int] - ) -> np.ndarray: - """Apply gauge transformation to samples. - - Args: - samples: Sample array to transform - flip_indices: Indices where spins should be flipped - - Returns: - Transformed sample array - """ - samples = samples.copy() - samples[:, flip_indices] = -samples[:, flip_indices] - return samples - + def _process_embedding( self, game_state: GameState, @@ -184,6 +163,7 @@ def adjudicate(self, game_state: GameState) -> AdjudicationResult: ValueError: If the game state is invalid RuntimeError: If quantum annealing fails """ + if not self._base_sampler: raise RuntimeError("Sampler not initialized. Call setup() first.") @@ -200,15 +180,12 @@ def adjudicate(self, game_state: GameState) -> AdjudicationResult: if self.params.use_mock and self.params.use_shim: print('D-Wave mock sampler is not set up to use the shimming process, turn shim off if using mock!') - sampler_kwargs = { + sampler_kwargs = { 'num_reads': self.params.num_reads, 'answer_mode': 'raw' } - if self.params.use_mock: - base_sampler = MockDWaveSampler(topology_type='zephyr', topology_shape=[6, 4]) - else: - base_sampler = DWaveSampler(solver=self.params.QC_SOLVER_TO_USE) + if not self.params.use_mock: sampler_kwargs.update({ 'fast_anneal': True, 'annealing_time': self.params.anneal_time / 1000, @@ -216,9 +193,6 @@ def adjudicate(self, game_state: GameState) -> AdjudicationResult: }) if self.params.use_shim: - shim_stats = {'qubit_magnetizations': [], - 'average_absolute_value_of_magnetization': [], - 'all_flux_bias_offsets': []} sampler_kwargs.update({'readout_thermalization': 100., 'auto_scale': False, 'flux_drift_compensation': True, @@ -272,7 +246,7 @@ def adjudicate(self, game_state: GameState) -> AdjudicationResult: for each_vertex in range(num_vertices): full_h[num_vertices * embedding_idx + each_vertex] = 0 - for k, v in base_ising_model['j'].items(): # is this correct? + for k, v in base_ising_model['j'].items(): edge_under_automorph = (min(automorphism[k[0]], automorphism[k[1]]), max(automorphism[k[0]], automorphism[k[1]])) full_j[edge_under_automorph] = v @@ -288,7 +262,7 @@ def adjudicate(self, game_state: GameState) -> AdjudicationResult: # transformation has been applied the BLUE with RED STAR situation. if self.params.use_gauge_transform: - flip_map = [random.choice([-1, 1]) for _ in full_h] # random list of +1, -1 values of len # qubits + flip_map = [np.random.choice([-1, 1]) for _ in full_h] # random list of +1, -1 values of len # qubits indices_of_flips = [i for i, x in enumerate(flip_map) if x == -1] # the indices of the -1 values for edge_key, j_val in full_j.items(): # for each edge and associated J value @@ -301,7 +275,7 @@ def adjudicate(self, game_state: GameState) -> AdjudicationResult: sampler_kwargs.update({'h': full_h, 'J': full_j}) - sampler = FixedEmbeddingComposite(base_sampler, embedding=embedding_map) # applies the embedding + sampler = FixedEmbeddingComposite(self._base_sampler, embedding=embedding_map) # applies the embedding # ************************************************************************* # Step 5: Optionally start shimming process in the BLUE with RED STAR basis @@ -323,23 +297,23 @@ def adjudicate(self, game_state: GameState) -> AdjudicationResult: # Step 6a: Compute average values of each qubit == magnetization # ************************************************************* - magnetization = np.sum(all_samples, axis=0)/self.params.NUM_READS_QC # BLUE with RED STAR label ordering - shim_stats['average_absolute_value_of_magnetization'].append(np.sum([abs(k) for k in magnetization])/len(magnetization)) + magnetization = np.sum(all_samples, axis=0)/self.params.num_reads # BLUE with RED STAR label ordering + self.shim_stats['average_absolute_value_of_magnetization'].append(np.sum([abs(k) for k in magnetization])/len(magnetization)) - qubit_magnetization = [0] * base_sampler.properties['num_qubits'] + qubit_magnetization = [0] * self._base_sampler.properties['num_qubits'] for k, v in embedding_map.items(): qubit_magnetization[v[0]] = magnetization[k] # check - shim_stats['qubit_magnetizations'].append(qubit_magnetization) + self.shim_stats['qubit_magnetizations'].append(qubit_magnetization) # ************************************** # Step 6b: Adjust flux bias offset terms # ************************************** - for k in range(base_sampler.properties['num_qubits']): - sampler_kwargs['flux_biases'][k] -= self.params.ALPHA_PHI * qubit_magnetization[k] + for k in range(self._base_sampler.properties['num_qubits']): + sampler_kwargs['flux_biases'][k] -= self.params.alpha_phi * qubit_magnetization[k] - shim_stats['all_flux_bias_offsets'].append(sampler_kwargs['flux_biases']) + self.shim_stats['all_flux_bias_offsets'].append(sampler_kwargs['flux_biases']) # ***************************************************************************************************** # Step 7: Reverse gauge transform, from BLUE with RED STAR to just BLUE, after shimming process is done @@ -381,106 +355,16 @@ def adjudicate(self, game_state: GameState) -> AdjudicationResult: for idx in isolated_vertices: total_samples[:, idx] = np.random.choice([1, -1], size=total_samples.shape[0]) - sample_count = self.params.NUM_READS_QC * num_embeddings * self.params.NUMBER_OF_CHIP_RUNS + sample_count = self.params.num_reads * num_embeddings * self.params.num_chip_runs # this is a full matrix with zeros on the diagonal that uses all the samples correlation_matrix = \ (np.einsum('si,sj->ij', total_samples, total_samples) / sample_count - - np.eye(int(game_state['num_nodes']))) - - winner, score_difference, influence_vector = ( - self.compute_winner_score_and_influence_from_correlation_matrix(game_state, correlation_matrix)) - - # todo make this compatible with output of erik's version - return_dictionary = {'game_state': game_state, 'adjudicator': 'quantum_annealing', - 'winner': winner, 'score': score_difference, 'influence_vector': influence_vector, - 'correlation_matrix': correlation_matrix, 'parameters': self.params} + np.eye(num_vertices)) - return return_dictionary - - - - # Create sampler with fixed embedding - sampler = FixedEmbeddingComposite(self._base_sampler, embedding=embedding_map) - - # Get Ising model - ising_model = self._game_state_to_ising(game_state) - - # Set up sampling parameters - sampler_kwargs = { - 'num_reads': self.params.num_reads, - 'answer_mode': 'raw' - } - - if not self.params.use_mock: - sampler_kwargs.update({ - 'fast_anneal': True, - 'annealing_time': self.params.anneal_time / 1000, - 'auto_scale': False - }) - - if self.params.use_shim: - sampler_kwargs.update({'readout_thermalization': 100., - 'auto_scale': False, - 'flux_drift_compensation': True, - 'flux_biases': [0] * base_sampler.properties['num_qubits']}) - - # Perform sampling - response = sampler.sample_ising( - ising_model['h'], - ising_model['j'], - **sampler_kwargs - ) - - # Process samples - samples = np.array(response.record.sample) - - # todo this is in the wrong order - # Apply gauge transform if enabled - if self.params.use_gauge_transform: - flip_indices = np.random.choice( - [0, 1], - size=samples.shape[1], - p=[0.5, 0.5] - ).nonzero()[0] - samples = self._apply_gauge_transform(samples, flip_indices) - - # Stack samples for all embeddings - processed_samples = samples[:, :num_vertices] - for k in range(1, num_embeddings): - processed_samples = np.vstack(( - processed_samples, - samples[:, k*num_vertices:(k+1)*num_vertices] - )) - - total_samples = np.vstack((total_samples, processed_samples)) - - # Remove initial zero row - total_samples = np.delete(total_samples, 0, axis=0) - - # Handle isolated vertices - isolated_vertices = self._find_isolated_vertices(game_state) - if isolated_vertices: - random_samples = np.random.choice( - [1, -1], - size=(total_samples.shape[0], len(isolated_vertices)) - ) - for i, vertex in enumerate(isolated_vertices): - total_samples[:, vertex] = random_samples[:, i] - - # Calculate correlation matrix - sample_count = (self.params.num_reads * num_embeddings * self.params.num_chip_runs) - - correlation_matrix = ( - np.einsum('si,sj->ij', total_samples, total_samples) / sample_count - - np.eye(num_vertices) - ) - # Compute results - winner, score, influence_vector = self._compute_winner_score_and_influence( - game_state, correlation_matrix - ) - + winner, score, influence_vector = self._compute_winner_score_and_influence(game_state, correlation_matrix) + return AdjudicationResult( game_state=game_state, adjudicator='quantum_annealing', From b543d6a7dbc40a32c7320f893272fe7e83c77062 Mon Sep 17 00:00:00 2001 From: Geordie Rose Date: Fri, 31 Jan 2025 12:35:04 -0800 Subject: [PATCH 18/59] PEP --- tangled_adjudicate/adjudicators/schrodinger.py | 1 + 1 file changed, 1 insertion(+) diff --git a/tangled_adjudicate/adjudicators/schrodinger.py b/tangled_adjudicate/adjudicators/schrodinger.py index ff3506f..7c0d377 100644 --- a/tangled_adjudicate/adjudicators/schrodinger.py +++ b/tangled_adjudicate/adjudicators/schrodinger.py @@ -4,6 +4,7 @@ from .adjudicator import Adjudicator, GameState, AdjudicationResult + class SchrodingerEquationAdjudicator(Adjudicator): """Adjudicator implementation using Schrödinger equation evolution.""" From 5178d5245efad5df2e155d59fc26fba126c67dfa Mon Sep 17 00:00:00 2001 From: Geordie Rose Date: Fri, 31 Jan 2025 12:39:12 -0800 Subject: [PATCH 19/59] PEP and removed the remove vertices stuff (not required for SA) --- .../adjudicators/simulated_annealing.py | 25 ++++++------------- 1 file changed, 8 insertions(+), 17 deletions(-) diff --git a/tangled_adjudicate/adjudicators/simulated_annealing.py b/tangled_adjudicate/adjudicators/simulated_annealing.py index 73250a3..e8bb234 100644 --- a/tangled_adjudicate/adjudicators/simulated_annealing.py +++ b/tangled_adjudicate/adjudicators/simulated_annealing.py @@ -4,6 +4,7 @@ from .adjudicator import Adjudicator, GameState, AdjudicationResult + class SimulatedAnnealingAdjudicator(Adjudicator): """Adjudicator implementation using simulated annealing.""" @@ -82,24 +83,14 @@ def adjudicate(self, game_state: GameState) -> AdjudicationResult: # Calculate correlation matrix samples = np.array(response.record.sample, dtype=float) - correlation_matrix = ( - np.einsum('si,sj->ij', samples, samples) / self.num_reads - - np.eye(game_state['num_nodes']) - ) - - # Handle isolated vertices - isolated_vertices = self._find_isolated_vertices(game_state) - if isolated_vertices: - samples = np.random.choice([1, -1], size=(self.num_reads, len(isolated_vertices))) - for i, vertex in enumerate(isolated_vertices): - correlation_matrix[:, vertex] = np.mean(samples[:, i]) - correlation_matrix[vertex, :] = np.mean(samples[:, i]) - correlation_matrix[vertex, vertex] = 0 - + + # creates symmetric matrix with zeros on diagonal (so that self-correlation of one is not counted) -- this is + # the standard for computing influence vector + correlation_matrix = (np.einsum('si,sj->ij', samples, samples) / self.num_reads - + np.eye(game_state['num_nodes'])) + # Compute results - winner, score, influence_vector = self._compute_winner_score_and_influence( - game_state, correlation_matrix - ) + winner, score, influence_vector = self._compute_winner_score_and_influence(game_state, correlation_matrix) return AdjudicationResult( game_state=game_state, From 3a2e57542971306b2678e66e0c3961ec96abf54a Mon Sep 17 00:00:00 2001 From: Geordie Rose Date: Fri, 31 Jan 2025 14:35:47 -0800 Subject: [PATCH 20/59] removed references to old adjudicators --- .../utils/how_to_adjudicate_states.py | 105 ++++-------------- 1 file changed, 23 insertions(+), 82 deletions(-) diff --git a/tangled_adjudicate/utils/how_to_adjudicate_states.py b/tangled_adjudicate/utils/how_to_adjudicate_states.py index f209542..ba07f1d 100644 --- a/tangled_adjudicate/utils/how_to_adjudicate_states.py +++ b/tangled_adjudicate/utils/how_to_adjudicate_states.py @@ -1,45 +1,36 @@ """ how to use provided solvers to adjudicate Tangled terminal states """ -import pickle import sys import os -import ast import time import numpy as np -from tangled_adjudicate.adjudicators.adjudicate import old_Adjudicator - from tangled_adjudicate.adjudicators.simulated_annealing import SimulatedAnnealingAdjudicator from tangled_adjudicate.adjudicators.quantum_annealing import QuantumAnnealingAdjudicator from tangled_adjudicate.adjudicators.lookup_table import LookupTableAdjudicator from tangled_adjudicate.adjudicators.schrodinger import SchrodingerEquationAdjudicator -from tangled_adjudicate.utils.parameters import Params -from tangled_adjudicate.utils.game_graph_properties import GraphProperties -from tangled_adjudicate.utils.generate_terminal_states import convert_state_string_to_game_state - def main(): # this code shows how to use the four different adjudicators # there are two example_game_state dictionaries provided, which are terminal states in graph_number 2 and 3 # respectively, that are of the sort that are closest to the draw line at score = +- 1/2 - # solver_list = ['simulated_annealing', 'schrodinger_equation', 'quantum_annealing', 'lookup_table'] - solver_list = ['quantum_annealing'] + # set graph_number + graph_number = 2 + + # choose solvers to use + solver_list = ['simulated_annealing', 'schrodinger_equation', 'quantum_annealing', 'lookup_table'] precision_digits = 4 # just to clean up print output np.set_printoptions(suppress=True) # remove scientific notation - params = Params() - old_adjudicator = old_Adjudicator(params) - args = {'data_dir': os.path.join(os.getcwd(), '..', 'data'), - 'graph_number': params.GRAPH_NUMBER, - 'solver_name': params.QC_SOLVER_TO_USE} + 'graph_number': graph_number} example_game_state = None # draw; score=0; ferromagnetic ring - if params.GRAPH_NUMBER == 2: + if graph_number == 2: example_game_state = {'num_nodes': 3, 'edges': [(0, 1, 2), (0, 2, 2), (1, 2, 2)], 'player1_id': 'player1', 'player2_id': 'player2', 'turn_count': 5, 'current_player_index': 1, 'player1_node': 1, 'player2_node': 2} @@ -47,10 +38,10 @@ def main(): # red wins, score +2/3; this is one of the states closest to the draw line # note that quantum_annealing in this default uses the D-Wave mock software solver and won't give # the right answer as its samples aren't unbiased -- if you want the quantum_annealing solver to - # run on hardware set self.USE_MOCK_DWAVE_SAMPLER = False in /utils/parameters.py and ensure you have + # run on hardware set QAParameters.use_mock = False in /adjudicators/quantum_annealing.py and ensure you have # hardware access and everything is set up - if params.GRAPH_NUMBER == 3: + if graph_number == 3: example_game_state = {'num_nodes': 4, 'edges': [(0, 1, 3), (0, 2, 1), (0, 3, 3), (1, 2, 1), (1, 3, 3), (2, 3, 1)], 'player1_id': 'player1', 'player2_id': 'player2', 'turn_count': 8, @@ -59,39 +50,10 @@ def main(): print('this introduction only has included game states for graphs 2 and 3. If you want a different' 'graph please add a new example_game_state here!') - # if 'simulated_annealing' in solver_list: - # sa_adjudicator = SimulatedAnnealingAdjudicator() - # sa_adjudicator.setup() - # start = time.time() - # new_sa_results = sa_adjudicator.adjudicate(example_game_state) - # print('elapsed time for simulated_annealing was', round(time.time() - start, precision_digits), 'seconds.') - # - # if 'quantum_annealing' in solver_list: - # qa_adjudicator = QuantumAnnealingAdjudicator() - # qa_adjudicator.setup(**args) - # new_qa_results = qa_adjudicator.adjudicate(example_game_state) - # - # if 'lookup_table' in solver_list: - # lt_adjudicator = LookupTableAdjudicator() - # lt_adjudicator.setup(**args) - # new_lt_results = lt_adjudicator.adjudicate(example_game_state) - # - # if 'schrodinger_equation' in solver_list: - # se_adjudicator = SchrodingerEquationAdjudicator() - # se_adjudicator.setup() - # new_se_results = se_adjudicator.adjudicate(example_game_state) - for solver_to_use in solver_list: start = time.time() - # equivalent to e.g. results = adjudicator.simulated_annealing(example_game_state) - old_results = getattr(old_adjudicator, solver_to_use)(example_game_state) - - print('elapsed time for old', solver_to_use, 'was', round(time.time() - start, precision_digits), 'seconds.') - - start = time.time() - adjudicator = None if solver_to_use == 'simulated_annealing': @@ -107,49 +69,28 @@ def main(): adjudicator = SchrodingerEquationAdjudicator() adjudicator.setup(**args) - new_results = adjudicator.adjudicate(example_game_state) - - print('elapsed time for new', solver_to_use, 'was', round(time.time() - start, precision_digits), 'seconds.') - - if old_results['correlation_matrix'] is None: - print('old correlation matrix:', None) - else: - print('old correlation matrix:') - print(np.round(old_results['correlation_matrix'], precision_digits)) + results = adjudicator.adjudicate(example_game_state) - if new_results['correlation_matrix'] is None: - print('new correlation matrix:', None) - else: - print('new correlation matrix:') - print(np.round(new_results['correlation_matrix'], precision_digits)) - - print('old winner:', old_results['winner']) - print('new winner:', new_results['winner']) + print('elapsed time for', solver_to_use, 'was', round(time.time() - start, precision_digits), 'seconds.') - if old_results['score'] is None: - print('old score:', old_results['score']) + if results['correlation_matrix'] is None: + print('correlation matrix:', None) else: - print('old score:', round(old_results['score'], precision_digits)) + print('correlation matrix:') + print(np.round(results['correlation_matrix'], precision_digits)) - if new_results['score'] is None: - print('new score:', new_results['score']) - else: - print('new score:', round(new_results['score'], precision_digits)) + print('winner:', results['winner']) - if old_results['influence_vector'] is None: - print('old influence vector:', None) + if results['score'] is None: + print('score:', results['score']) else: - print('old influence vector:', [round(old_results['influence_vector'][k], precision_digits) - for k in range(len(old_results['influence_vector']))]) + print('score:', round(results['score'], precision_digits)) - if new_results['influence_vector'] is None: - print('new influence vector:', None) + if results['influence_vector'] is None: + print('influence vector:', None) else: - print('new influence vector:', [round(new_results['influence_vector'][k], precision_digits) - for k in range(len(new_results['influence_vector']))]) - - print() - print() + print('influence vector:', [round(results['influence_vector'][k], precision_digits) + for k in range(len(results['influence_vector']))]) if __name__ == "__main__": From 53051e2b6c88cfae84e26194e1bc5d5f0fd96995 Mon Sep 17 00:00:00 2001 From: Geordie Rose Date: Fri, 31 Jan 2025 21:09:40 -0800 Subject: [PATCH 21/59] changed a couple variable names and the function name of convert_my_game_state_to_erik_game_state --- tangled_adjudicate/utils/utilities.py | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/tangled_adjudicate/utils/utilities.py b/tangled_adjudicate/utils/utilities.py index 48ade0a..8a219ca 100644 --- a/tangled_adjudicate/utils/utilities.py +++ b/tangled_adjudicate/utils/utilities.py @@ -85,9 +85,11 @@ def convert_erik_game_state_to_my_game_state(game_state): return my_state -def convert_to_erik_game_state_for_adjudication(my_state, number_of_vertices, list_of_edge_tuples): +def convert_my_game_state_to_erik_game_state(my_state, number_of_vertices, list_of_edge_tuples): my_vertices = my_state[:number_of_vertices] + my_edges = my_state[number_of_vertices:] + turn_count = 0 try: @@ -102,16 +104,12 @@ def convert_to_erik_game_state_for_adjudication(my_state, number_of_vertices, li except ValueError: player_2_vertex = -1 - my_edges = my_state[number_of_vertices:] - turn_count += my_edges.count(1) + my_edges.count(2) + my_edges.count(3) # if turn_count is even, it's player 1 (red)'s turn current_player_idx = 1 if turn_count % 2 == 0 else 2 - erik_edges = [] - for k in range(len(list_of_edge_tuples)): - erik_edges.append((list_of_edge_tuples[k][0], list_of_edge_tuples[k][1], my_edges[k])) + erik_edges = [(list_of_edge_tuples[k][0], list_of_edge_tuples[k][1], my_edges[k]) for k in range(len(my_edges))] game_state = {'num_nodes': number_of_vertices, # 'edges': [(0, 1, 3), (0, 2, 1), (0, 3, 3), (1, 2, 1), (1, 3, 3), (2, 3, 1)], From f5208b05299aae650718fd37ac04317fb5305088 Mon Sep 17 00:00:00 2001 From: Geordie Rose Date: Fri, 31 Jan 2025 21:11:05 -0800 Subject: [PATCH 22/59] removed all references to old_adjudicator --- .../utils/adjudicate_all_terminal_states.py | 51 +++++++++++++------ 1 file changed, 36 insertions(+), 15 deletions(-) diff --git a/tangled_adjudicate/utils/adjudicate_all_terminal_states.py b/tangled_adjudicate/utils/adjudicate_all_terminal_states.py index 7157885..ab6bfb7 100644 --- a/tangled_adjudicate/utils/adjudicate_all_terminal_states.py +++ b/tangled_adjudicate/utils/adjudicate_all_terminal_states.py @@ -1,17 +1,19 @@ """ generate and adjudicate all Tangled terminal states for tiny graphs """ - import sys import os import time import pickle import numpy as np -from tangled_adjudicate.adjudicators.adjudicate import Adjudicator +from tangled_adjudicate.adjudicators.simulated_annealing import SimulatedAnnealingAdjudicator +from tangled_adjudicate.adjudicators.quantum_annealing import QuantumAnnealingAdjudicator +from tangled_adjudicate.adjudicators.lookup_table import LookupTableAdjudicator +from tangled_adjudicate.adjudicators.schrodinger import SchrodingerEquationAdjudicator + from tangled_adjudicate.utils.generate_terminal_states import generate_all_tangled_terminal_states -from tangled_adjudicate.utils.parameters import Params -def generate_adjudication_results_for_all_terminal_states(solver_to_use): +def generate_adjudication_results_for_all_terminal_states(graph_number, solver_to_use): # uses up to three different adjudicators provided to evaluate all unique terminal states for tiny graphs # (in the default here, graphs 2 and 3). Note this only works for tiny graphs as the number of terminal states # grows like 3 ** edge_count. @@ -20,19 +22,35 @@ def generate_adjudication_results_for_all_terminal_states(solver_to_use): # that hasn't been called yet, it adds that key and its results. If you call it in a case where there are # already results, it will ask you if you want to overwrite them. - if solver_to_use not in ['simulated_annealing', 'schrodinger_equation', 'quantum_annealing']: + if solver_to_use not in ['simulated_annealing', 'schrodinger_equation', 'quantum_annealing', 'lookup_table']: sys.exit(print('the solver' + solver_to_use + 'is not in the allowed list -- please take a look!')) precision_digits = 4 # just to clean up print output np.set_printoptions(suppress=True) # remove scientific notation - params = Params() # your graph_number will be set here, make sure it's what you want! - adjudicator = Adjudicator(params) - game_states = generate_all_tangled_terminal_states(params.GRAPH_NUMBER) + adjudicator = None + + args = {'data_dir': os.path.join(os.getcwd(), '..', 'data'), + 'graph_number': graph_number} + + if solver_to_use == 'simulated_annealing': + adjudicator = SimulatedAnnealingAdjudicator() + else: + if solver_to_use == 'quantum_annealing': + adjudicator = QuantumAnnealingAdjudicator() + else: + if solver_to_use == 'lookup_table': + adjudicator = LookupTableAdjudicator() + else: + if solver_to_use == 'schrodinger_equation': + adjudicator = SchrodingerEquationAdjudicator() + + adjudicator.setup(**args) + + game_states = generate_all_tangled_terminal_states(graph_number) - file_name_prefix = "graph_" + str(params.GRAPH_NUMBER) data_dir = os.path.join(os.getcwd(), '..', 'data') - file_path = os.path.join(data_dir, file_name_prefix + "_terminal_states_adjudication_results.pkl") + file_path = os.path.join(data_dir, "graph_" + str(graph_number) + "_terminal_states_adjudication_results.pkl") if os.path.isfile(file_path): with open(file_path, "rb") as fp: @@ -42,16 +60,18 @@ def generate_adjudication_results_for_all_terminal_states(solver_to_use): # at this point, either we have loaded some adjudication_results from an existing file, or we have a new empty dict if solver_to_use in adjudication_results: # this means we loaded this in already - user_input = input('results already exist, overwrite (y/n)?') + user_input = input('results already exist for ' + solver_to_use + ', overwrite (y/n)?') if user_input.lower() != 'y': - sys.exit(print('exiting!')) + return None # now we proceed to compute and store result print('beginning adjudication using the ' + solver_to_use + ' solver...') start = time.time() adjudication_results[solver_to_use] = {} + for k, v in game_states.items(): - adjudication_results[solver_to_use][k] = getattr(adjudicator, solver_to_use)(v['game_state']) + adjudication_results[solver_to_use][k] = adjudicator.adjudicate(v['game_state']) + print('elapsed time was', round(time.time() - start, precision_digits), 'seconds.') # store it -- this should leave any previously loaded solver results intact @@ -61,10 +81,11 @@ def generate_adjudication_results_for_all_terminal_states(solver_to_use): def main(): - solver_list = ['simulated_annealing', 'schrodinger_equation', 'quantum_annealing'] + graph_number = 2 + solver_list = ['simulated_annealing', 'schrodinger_equation', 'quantum_annealing', 'lookup_table'] for solver_to_use in solver_list: - generate_adjudication_results_for_all_terminal_states(solver_to_use) + generate_adjudication_results_for_all_terminal_states(graph_number, solver_to_use) if __name__ == "__main__": From 50a6df4af1ac5bf7c482873d3518a9edce3a4b4a Mon Sep 17 00:00:00 2001 From: Geordie Rose Date: Fri, 31 Jan 2025 21:11:45 -0800 Subject: [PATCH 23/59] WIP need to integrate bug fixes from tangled-cruft/visualize_and_enumerate_ky --- .../utils/generate_terminal_states.py | 78 +++++++++++-------- 1 file changed, 46 insertions(+), 32 deletions(-) diff --git a/tangled_adjudicate/utils/generate_terminal_states.py b/tangled_adjudicate/utils/generate_terminal_states.py index 931a7b0..cb625a0 100644 --- a/tangled_adjudicate/utils/generate_terminal_states.py +++ b/tangled_adjudicate/utils/generate_terminal_states.py @@ -9,29 +9,44 @@ from tangled_adjudicate.utils.game_graph_properties import GraphProperties from tangled_adjudicate.utils.find_graph_automorphisms import get_automorphisms - - -def convert_state_string_to_game_state(graph, terminal_state_string): - - vertex_list = terminal_state_string[:graph.vertex_count] - edge_list = terminal_state_string[graph.vertex_count:] - edges = [(graph.edge_list[k][0], graph.edge_list[k][1], edge_list[k]) for k in range(len(edge_list))] - - turn_count = vertex_list.count(1) + vertex_list.count(2) + len(edge_list) - edge_list.count(0) - - # if turn_count is even, it's red's turn - if not turn_count % 2: - current_player_index = 1 - else: - current_player_index = 2 - - game_state = {'num_nodes': graph.vertex_count, 'edges': edges, - 'player1_id': 'player1', 'player2_id': 'player2', 'turn_count': turn_count, - 'current_player_index': current_player_index, - 'player1_node': vertex_list.index(1), 'player2_node': vertex_list.index(2)} - - return game_state - +from tangled_adjudicate.utils.utilities import convert_my_game_state_to_erik_game_state + + +# def convert_state_string_to_game_state(my_state, number_of_vertices, list_of_edge_tuples): +# +# my_vertices = my_state[:number_of_vertices] +# my_edges = my_state[number_of_vertices:] +# +# turn_count = 0 +# +# try: +# player_1_vertex = my_vertices.index(1) +# turn_count += 1 +# except ValueError: +# player_1_vertex = -1 +# +# try: +# player_2_vertex = my_vertices.index(2) +# turn_count += 1 +# except ValueError: +# player_2_vertex = -1 +# +# turn_count += my_edges.count(1) + my_edges.count(2) + my_edges.count(3) +# +# # if turn_count is even, it's player 1 (red)'s turn +# current_player_idx = 1 if turn_count % 2 == 0 else 2 +# +# erik_edges = [(list_of_edge_tuples[k][0], list_of_edge_tuples[k][1], my_edges[k]) for k in range(len(my_edges))] +# +# game_state = {'num_nodes': number_of_vertices, 'edges': erik_edges, +# 'player1_id': 'player1', 'player2_id': 'player2', 'turn_count': turn_count, +# 'current_player_index': current_player_idx, +# 'player1_node': player_1_vertex, 'player2_node': player_2_vertex} +# +# return game_state + + +# todo change this to the visualize_and_enumerate code def generate_all_tangled_terminal_states(graph_number): # this loads or generates all possible terminal game states for the graph indexed by graph_number and groups them @@ -47,8 +62,7 @@ def generate_all_tangled_terminal_states(graph_number): graph = GraphProperties(graph_number) script_dir = os.path.dirname(os.path.abspath(__file__)) # Get the directory of the current script data_dir = os.path.join(script_dir, '..', 'data') - file_path = os.path.join(data_dir, - "graph_" + str(graph_number) + "_unique_terminal_states.pkl") + file_path = os.path.join(data_dir, "graph_" + str(graph_number) + "_unique_terminal_states.pkl") if os.path.isfile(file_path): # if the file already exists, just load it with open(file_path, "rb") as fp: @@ -129,10 +143,10 @@ def generate_all_tangled_terminal_states(graph_number): game_states = {} - for each in terminal_states: - game_states[str(each)] = {} - game_states[str(each)]['game_state'] = convert_state_string_to_game_state(graph, each) - game_states[str(each)]['automorphisms'] = good_states[str(each)] + for my_game_state in terminal_states: + game_states[str(my_game_state)] = {} + game_states[str(my_game_state)]['game_state'] = convert_my_game_state_to_erik_game_state(my_game_state, graph.vertex_count, graph.edge_list) + game_states[str(my_game_state)]['automorphisms'] = good_states[str(my_game_state)] data_dir = os.path.join(os.getcwd(), '..', 'data') @@ -145,9 +159,9 @@ def generate_all_tangled_terminal_states(graph_number): def main(): # this generates all terminal states for graphs 2 and 3 - for graph_number in range(2, 4): - gs = generate_all_tangled_terminal_states(graph_number) - + gs2 = generate_all_tangled_terminal_states(graph_number=2) + gs3 = generate_all_tangled_terminal_states(graph_number=3) + print() if __name__ == "__main__": sys.exit(main()) From 9e15235b12287a61ef13d3f94afebc89372e0943 Mon Sep 17 00:00:00 2001 From: Geordie Rose Date: Sat, 1 Feb 2025 09:59:46 -0800 Subject: [PATCH 24/59] Integrated bug fixes from tangled-cruft/visualize_and_enumerate_k4 --- .../utils/generate_terminal_states.py | 154 ++++++++---------- 1 file changed, 67 insertions(+), 87 deletions(-) diff --git a/tangled_adjudicate/utils/generate_terminal_states.py b/tangled_adjudicate/utils/generate_terminal_states.py index cb625a0..39f8bee 100644 --- a/tangled_adjudicate/utils/generate_terminal_states.py +++ b/tangled_adjudicate/utils/generate_terminal_states.py @@ -12,56 +12,27 @@ from tangled_adjudicate.utils.utilities import convert_my_game_state_to_erik_game_state -# def convert_state_string_to_game_state(my_state, number_of_vertices, list_of_edge_tuples): -# -# my_vertices = my_state[:number_of_vertices] -# my_edges = my_state[number_of_vertices:] -# -# turn_count = 0 -# -# try: -# player_1_vertex = my_vertices.index(1) -# turn_count += 1 -# except ValueError: -# player_1_vertex = -1 -# -# try: -# player_2_vertex = my_vertices.index(2) -# turn_count += 1 -# except ValueError: -# player_2_vertex = -1 -# -# turn_count += my_edges.count(1) + my_edges.count(2) + my_edges.count(3) -# -# # if turn_count is even, it's player 1 (red)'s turn -# current_player_idx = 1 if turn_count % 2 == 0 else 2 -# -# erik_edges = [(list_of_edge_tuples[k][0], list_of_edge_tuples[k][1], my_edges[k]) for k in range(len(my_edges))] -# -# game_state = {'num_nodes': number_of_vertices, 'edges': erik_edges, -# 'player1_id': 'player1', 'player2_id': 'player2', 'turn_count': turn_count, -# 'current_player_index': current_player_idx, -# 'player1_node': player_1_vertex, 'player2_node': player_2_vertex} -# -# return game_state - - -# todo change this to the visualize_and_enumerate code - def generate_all_tangled_terminal_states(graph_number): # this loads or generates all possible terminal game states for the graph indexed by graph_number and groups them # into lists where each member of the list is connected by an automorphism. Running this function requires either - # loading or generating an automorphism file.The dictionary game_states has as its key a string with the canonical + # loading or generating an automorphism file. The dictionary game_states has as its key a string with the canonical # member of each of these, with the further ['automorphisms'] key being a list of all the states that are symmetries # of the canonical key. The key ['game_state'] is the representation of the key as a game_state object. # # Note that this requires enumerating all possible terminal states, the number of which is # (vertex_count choose 2) * 2 * 3**edge_count, which grows exponentially with edge count. You can do this easily # for graph_number 1, 2, 3, 4, but 5 and up get stupidly large. + # + # graph_number 2 should have 27 keys, and each ['automorphisms'] sub-key should have 6 entries + # graph_number 3 should have 405 keys, and each ['automorphisms'] sub-key should have 12-24 entries (the reason + # why there aren't always 24 is that for some of these keys different automorphisms bring you to the same state) graph = GraphProperties(graph_number) + script_dir = os.path.dirname(os.path.abspath(__file__)) # Get the directory of the current script data_dir = os.path.join(script_dir, '..', 'data') + list_of_automorphisms = get_automorphisms(graph_number, data_dir=data_dir) + file_path = os.path.join(data_dir, "graph_" + str(graph_number) + "_unique_terminal_states.pkl") if os.path.isfile(file_path): # if the file already exists, just load it @@ -71,13 +42,11 @@ def generate_all_tangled_terminal_states(graph_number): # add check to make sure you don't ask for something too large print('***************************') user_input = input('There are ' + str(math.comb(graph.vertex_count, 2) * 2 * 3**graph.edge_count) + - ' terminal states -- proceed (y/n)?') + ' total non-unique terminal states -- proceed (y/n)?') if user_input.lower() != 'y': sys.exit(print('exiting...')) print('***************************') - list_of_automorphisms = get_automorphisms(graph_number, data_dir=data_dir) - possible_vertex_states = [] for positions in itertools.permutations(range(graph.vertex_count), 2): lst = [0] * graph.vertex_count @@ -90,65 +59,76 @@ def generate_all_tangled_terminal_states(graph_number): elements = [1, 2, 3] possible_edge_states = list(itertools.product(elements, repeat=graph.edge_count)) + # all_states is a list of lists enumerating ALL the game states all_states = [j + list(k) for k in possible_edge_states for j in possible_vertex_states] - same_group_of_states = {} + # this next part creates a dictionary where the keys are each of the elements of all_states and the values are + # lists of all the states connected to the key by an automorphism. Note that different automorphisms can lead + # to the same state, so at some point the list is converted to a set and then back to a list + + all_states_with_symmetries = {} + all_states_no_symmetries = {} + # iterate over all enumerated states for state in all_states: + + # create a list for all the symmetric states + list_of_states_connected_by_symmetry = [] + + # get indices of the red and blue vertices only_vertices = state[:graph.vertex_count] red_vertex_index = only_vertices.index(1) blue_vertex_index = only_vertices.index(2) - same_group_of_states[str(state)] = [] + + # iterate over all automorphisms for automorph in list_of_automorphisms: - new_red_vertex_index = automorph[red_vertex_index] - new_blue_vertex_index = automorph[blue_vertex_index] - transformed_each = [0] * graph.vertex_count - transformed_each[new_red_vertex_index] = 1 - transformed_each[new_blue_vertex_index] = 2 - - edge = np.zeros((graph.vertex_count, graph.vertex_count)) - new_edge = np.zeros((graph.vertex_count, graph.vertex_count)) - cnt = graph.vertex_count - for j in range(graph.vertex_count): - for i in range(j): - edge[i, j] = state[cnt] - cnt += 1 - - cnt = graph.vertex_count - for j in range(graph.vertex_count): - for i in range(j): - if automorph[i] < automorph[j]: - new_edge[i, j] = edge[automorph[i], automorph[j]] - else: - new_edge[i, j] = edge[automorph[j], automorph[i]] - cnt += 1 - - for j in range(graph.vertex_count): - for i in range(j): - transformed_each.append(int(new_edge[i, j])) - same_group_of_states[str(state)].append(transformed_each) - - good_states = {} - cnt = 0 - for k, v in same_group_of_states.items(): - if not cnt % (math.comb(graph.vertex_count, 2) * 2): # 4 choose 2 = 6 * 2 = 12 ..... 3 choose 2 = 3 *2 = 6 math.comb(graph.vertex_count, 2) * 2 - good_states[k] = v - cnt += 1 - - terminal_states = [] - for k, v in good_states.items(): - terminal_states.append(ast.literal_eval(k)) - - print('there are', len(terminal_states), 'unique terminal states. Writing to disk ...') + + # initialize the state we want to compute (transforming state under automorph) + state_transformed_under_automorph = [0] * graph.vertex_count + + # write transformed vertices into the transformed state -- this finishes the vertex part + state_transformed_under_automorph[automorph[red_vertex_index]] = 1 + state_transformed_under_automorph[automorph[blue_vertex_index]] = 2 + + # now we want to transform the edges under the automorphism + for edge_idx in range(graph.edge_count): + first_vertex = automorph[graph.edge_list[edge_idx][0]] + second_vertex = automorph[graph.edge_list[edge_idx][1]] + if first_vertex < second_vertex: + transformed_edge = (first_vertex, second_vertex) + else: + transformed_edge = (second_vertex, first_vertex) + + transformed_edge_idx = graph.edge_list.index(transformed_edge) + + state_transformed_under_automorph.append(state[graph.vertex_count + transformed_edge_idx]) + + list_of_states_connected_by_symmetry.append(str(state_transformed_under_automorph)) + + # remove duplicates + all_states_with_symmetries[str(state)] = list(dict.fromkeys(list_of_states_connected_by_symmetry)) + all_states_no_symmetries[str(state)] = list_of_states_connected_by_symmetry + + sorted_all_states_with_symmetries = dict(sorted(all_states_with_symmetries.items())) + + uniques = [] + duplicates = [] + + for k, v in sorted_all_states_with_symmetries.items(): + if k not in duplicates: + uniques.append(k) + for j in range(1, len(v)): + duplicates.append(v[j]) + + unique_terminal_states = [ast.literal_eval(k) for k in uniques] + print('there are', len(unique_terminal_states), 'unique terminal states. Writing to disk ...') game_states = {} - for my_game_state in terminal_states: + for my_game_state in unique_terminal_states: game_states[str(my_game_state)] = {} game_states[str(my_game_state)]['game_state'] = convert_my_game_state_to_erik_game_state(my_game_state, graph.vertex_count, graph.edge_list) - game_states[str(my_game_state)]['automorphisms'] = good_states[str(my_game_state)] - - data_dir = os.path.join(os.getcwd(), '..', 'data') + game_states[str(my_game_state)]['automorphisms'] = all_states_with_symmetries[str(my_game_state)] with open(os.path.join(data_dir, "graph_" + str(graph_number) + "_unique_terminal_states.pkl"), "wb") as fp: pickle.dump(game_states, fp) @@ -161,7 +141,7 @@ def main(): # this generates all terminal states for graphs 2 and 3 gs2 = generate_all_tangled_terminal_states(graph_number=2) gs3 = generate_all_tangled_terminal_states(graph_number=3) - print() + if __name__ == "__main__": sys.exit(main()) From 546331752e9aa5a40c783ac4116a69481f2565be Mon Sep 17 00:00:00 2001 From: Geordie Rose Date: Sat, 1 Feb 2025 10:03:32 -0800 Subject: [PATCH 25/59] fragile and not great but enough to check adjudication results --- .../utils/compare_adjudication_results.py | 26 ++++++++++++------- 1 file changed, 16 insertions(+), 10 deletions(-) diff --git a/tangled_adjudicate/utils/compare_adjudication_results.py b/tangled_adjudicate/utils/compare_adjudication_results.py index 168fe90..9ffe222 100644 --- a/tangled_adjudicate/utils/compare_adjudication_results.py +++ b/tangled_adjudicate/utils/compare_adjudication_results.py @@ -7,10 +7,8 @@ def compare_adjudication_results(graph_number, solvers_to_use): - # solvers_to_use is a list of solvers of length either 2 or 3 comprising 2 or 3 of - # ['schrodinger_equation', 'simulated_annealing', 'quantum_annealing'] - # - # indexing_solvers = {1: 'schrodinger_equation', 2: 'simulated_annealing', 3: 'quantum_annealing'} + # solvers_to_use is a list of solvers of length 2, 3, or 4 comprising 2, 3, or 4 of + # ['schrodinger_equation', 'simulated_annealing', 'quantum_annealing', 'lookup_table'] # load adjudication results obtained from running /utils/adjudicate_all_terminal_states.py data_dir = os.path.join(os.getcwd(), '..', 'data') @@ -42,7 +40,7 @@ def compare_adjudication_results(graph_number, solvers_to_use): for k0, value_dict in adjudication_results.items(): # k will be solver name string if k0 in solvers_to_use: # if we want to add this, add it for k1, v in value_dict.items(): - game_result[k1].append([k0, v['winner'], v['score']]) + game_result[k1].append([k0, v['winner'], v['score']]) # score will be None for lookup_table comparisons = {} for k, v in game_result.items(): # k is game state string @@ -60,7 +58,14 @@ def compare_adjudication_results(graph_number, solvers_to_use): to_plot = [] for k, v in scores.items(): - to_plot.append(v) + if v[0] is not None: + to_plot.append(v) + + if 'lookup_table' in solvers_to_use: + solvers_to_use.remove('lookup_table') + + if len(solvers_to_use) < 2: + print('need at least two of SA, QA, SE to generate score comparisons... lookup_table does not generate scores!') red_text = solvers_to_use[0] + ': red' blue_text = solvers_to_use[1] + ': blue' @@ -93,7 +98,7 @@ def compare_adjudication_results(graph_number, solvers_to_use): if graph_number == 3: if len(to_plot) == 2: - plt.hist(to_plot, range=[-2, 2], bins=400, color=['red', 'blue'], stacked=True) + plt.hist(to_plot, range=[-4, 4], bins=800, color=['red', 'blue'], stacked=True) else: plt.hist(to_plot, range=[-4, 4], bins=800, color=['red', 'blue', 'cyan'], stacked=True) @@ -115,10 +120,11 @@ def compare_adjudication_results(graph_number, solvers_to_use): def main(): - solvers_to_use = ['simulated_annealing', 'schrodinger_equation', 'quantum_annealing'] + solvers_to_use = ['simulated_annealing', 'schrodinger_equation', 'quantum_annealing', 'lookup_table'] + compare_adjudication_results(graph_number=2, solvers_to_use=solvers_to_use) - for graph_number in range(2, 4): - compare_adjudication_results(graph_number=graph_number, solvers_to_use=solvers_to_use) + solvers_to_use = ['simulated_annealing', 'quantum_annealing', 'lookup_table'] + compare_adjudication_results(graph_number=3, solvers_to_use=solvers_to_use) if __name__ == "__main__": From 3ad6120c32de7790a7e84b803895f444a3d37fbe Mon Sep 17 00:00:00 2001 From: Geordie Rose Date: Sat, 1 Feb 2025 10:07:43 -0800 Subject: [PATCH 26/59] moved over to new way of calling adjudicators and checked vs graph 2 and 3 results -- seems all good! --- tangled_adjudicate/utils/adjudicate_all_terminal_states.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/tangled_adjudicate/utils/adjudicate_all_terminal_states.py b/tangled_adjudicate/utils/adjudicate_all_terminal_states.py index ab6bfb7..a9c749d 100644 --- a/tangled_adjudicate/utils/adjudicate_all_terminal_states.py +++ b/tangled_adjudicate/utils/adjudicate_all_terminal_states.py @@ -81,6 +81,9 @@ def generate_adjudication_results_for_all_terminal_states(graph_number, solver_t def main(): + # note: generating all schrodinger_equation adjudication results for graph 3 or bigger takes forever + # I spot checked new subclass version and all spot checks were good + graph_number = 2 solver_list = ['simulated_annealing', 'schrodinger_equation', 'quantum_annealing', 'lookup_table'] From 6dfad1498a3139f6c06619fa40035640b9952c36 Mon Sep 17 00:00:00 2001 From: Geordie Rose Date: Sat, 1 Feb 2025 10:11:47 -0800 Subject: [PATCH 27/59] moved Params and MinimalAdjudicationParameters into adjudicate --- tangled_adjudicate/adjudicators/adjudicate.py | 45 +++++++++++++++++-- tangled_adjudicate/utils/parameters.py | 41 ----------------- 2 files changed, 42 insertions(+), 44 deletions(-) delete mode 100644 tangled_adjudicate/utils/parameters.py diff --git a/tangled_adjudicate/adjudicators/adjudicate.py b/tangled_adjudicate/adjudicators/adjudicate.py index 3aec01f..8ec6160 100644 --- a/tangled_adjudicate/adjudicators/adjudicate.py +++ b/tangled_adjudicate/adjudicators/adjudicate.py @@ -1,5 +1,4 @@ -""" Adjudicator class for Tangled game states using Schrödinger Equation, Simulated Annealing, D-Wave hardware, and -Look Up table """ +""" old style -- moved Params and MinimalAdjudicationParameters here -- to be deprecated""" import sys import os import random @@ -12,13 +11,53 @@ convert_erik_game_state_to_my_game_state) from tangled_adjudicate.utils.find_graph_automorphisms import get_automorphisms from tangled_adjudicate.utils.find_hardware_embeddings import get_embeddings -from tangled_adjudicate.utils.parameters import Params from tangled_adjudicate.schrodinger.schrodinger_functions import evolve_schrodinger from dwave.system import DWaveSampler, FixedEmbeddingComposite from dwave.system.testing import MockDWaveSampler +class Params(object): + def __init__(self): + self.GRAPH_NUMBER = 2 # this is the index of the graph to use; defined in /utils/game_graph_properties.py + # just a reminder which are which: + # 1 = 2 vertices + # 2 = 3 vertices in triangle + # 3 = 4 vertices, 6 edges + # 4 = 6 vertices, 1 hexagon, 6 edges + # 5 = 10 vertices, 15 edges, petersen graph + # 6 = 16 vertices, 32 edges, non-planar, tesseract + + self.EPSILON = 0.5 # this is the boundary between a draw and a win + + self.NUM_READS_SA = 1000 # this is for simulated annealing + + # These are parameters related to the use of QC hardware, if you're not using QC you can just leave these + # The defaults here are no shimming, no gauge transforms, only use M=1 automorphism, and collect a lot of + # samples (N=1000) + + self.USE_QC = True # set to False if you just want to use e.g. simulated annealer + self.USE_MOCK_DWAVE_SAMPLER = True # set to True if you want a software version of the hardware (doesn't sample like the HW tho so don't trust it, just for debugging) + self.QC_SOLVER_TO_USE = 'Advantage2_prototype2.6' # modify if you want to use a different QC + + self.NUMBER_OF_CHIP_RUNS = 1 # this is M + self.NUM_READS_QC = 1000 # this is N + self.ANNEAL_TIME_IN_NS = 5 # this is the fastest the QC can sweep + + self.USE_GAUGE_TRANSFORM = False + self.USE_SHIM = False + + self.ALPHA_PHI = 0.00001 + self.SHIM_ITERATIONS = 10 + + +class MinimalAdjudicationParameters(object): + def __init__(self): + self.EPSILON = 0.5 # this is the boundary between a draw and a win + self.USE_QC = False + self.NUM_READS_SA = 1000 # this is for simulated annealing + + class old_Adjudicator(object): def __init__(self, params): self.params = params diff --git a/tangled_adjudicate/utils/parameters.py b/tangled_adjudicate/utils/parameters.py deleted file mode 100644 index 2361743..0000000 --- a/tangled_adjudicate/utils/parameters.py +++ /dev/null @@ -1,41 +0,0 @@ -""" adjudication and support parameters """ - -class Params(object): - def __init__(self): - self.GRAPH_NUMBER = 2 # this is the index of the graph to use; defined in /utils/game_graph_properties.py - # just a reminder which are which: - # 1 = 2 vertices - # 2 = 3 vertices in triangle - # 3 = 4 vertices, 6 edges - # 4 = 6 vertices, 1 hexagon, 6 edges - # 5 = 10 vertices, 15 edges, petersen graph - # 6 = 16 vertices, 32 edges, non-planar, tesseract - - self.EPSILON = 0.5 # this is the boundary between a draw and a win - - self.NUM_READS_SA = 1000 # this is for simulated annealing - - # These are parameters related to the use of QC hardware, if you're not using QC you can just leave these - # The defaults here are no shimming, no gauge transforms, only use M=1 automorphism, and collect a lot of - # samples (N=1000) - - self.USE_QC = True # set to False if you just want to use e.g. simulated annealer - self.USE_MOCK_DWAVE_SAMPLER = True # set to True if you want a software version of the hardware (doesn't sample like the HW tho so don't trust it, just for debugging) - self.QC_SOLVER_TO_USE = 'Advantage2_prototype2.6' # modify if you want to use a different QC - - self.NUMBER_OF_CHIP_RUNS = 1 # this is M - self.NUM_READS_QC = 1000 # this is N - self.ANNEAL_TIME_IN_NS = 5 # this is the fastest the QC can sweep - - self.USE_GAUGE_TRANSFORM = False - self.USE_SHIM = False - - self.ALPHA_PHI = 0.00001 - self.SHIM_ITERATIONS = 10 - - -class MinimalAdjudicationParameters(object): - def __init__(self): - self.EPSILON = 0.5 # this is the boundary between a draw and a win - self.USE_QC = False - self.NUM_READS_SA = 1000 # this is for simulated annealing From 71bb9af67fb6b7277369c94a0130584689d3415a Mon Sep 17 00:00:00 2001 From: Geordie Rose Date: Sat, 1 Feb 2025 10:12:39 -0800 Subject: [PATCH 28/59] changed file name to indicate deprecation --- .../adjudicators/{adjudicate.py => deprecated_adjudicate.py} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename tangled_adjudicate/adjudicators/{adjudicate.py => deprecated_adjudicate.py} (100%) diff --git a/tangled_adjudicate/adjudicators/adjudicate.py b/tangled_adjudicate/adjudicators/deprecated_adjudicate.py similarity index 100% rename from tangled_adjudicate/adjudicators/adjudicate.py rename to tangled_adjudicate/adjudicators/deprecated_adjudicate.py From 9acd462cc1ebace07e687f7eb63b38302b983fea Mon Sep 17 00:00:00 2001 From: Geordie Rose Date: Sat, 1 Feb 2025 10:16:40 -0800 Subject: [PATCH 29/59] removed cruft --- .../adjudicators/deprecated_adjudicate.py | 426 ------------------ tangled_adjudicate/utils/utilities.py | 46 -- 2 files changed, 472 deletions(-) delete mode 100644 tangled_adjudicate/adjudicators/deprecated_adjudicate.py diff --git a/tangled_adjudicate/adjudicators/deprecated_adjudicate.py b/tangled_adjudicate/adjudicators/deprecated_adjudicate.py deleted file mode 100644 index 8ec6160..0000000 --- a/tangled_adjudicate/adjudicators/deprecated_adjudicate.py +++ /dev/null @@ -1,426 +0,0 @@ -""" old style -- moved Params and MinimalAdjudicationParameters here -- to be deprecated""" -import sys -import os -import random -import pickle -import neal -import numpy as np - -from tangled_adjudicate.utils.utilities import (game_state_to_ising_model, game_state_is_terminal, - find_isolated_vertices, get_tso, build_results_dict, - convert_erik_game_state_to_my_game_state) -from tangled_adjudicate.utils.find_graph_automorphisms import get_automorphisms -from tangled_adjudicate.utils.find_hardware_embeddings import get_embeddings -from tangled_adjudicate.schrodinger.schrodinger_functions import evolve_schrodinger - -from dwave.system import DWaveSampler, FixedEmbeddingComposite -from dwave.system.testing import MockDWaveSampler - - -class Params(object): - def __init__(self): - self.GRAPH_NUMBER = 2 # this is the index of the graph to use; defined in /utils/game_graph_properties.py - # just a reminder which are which: - # 1 = 2 vertices - # 2 = 3 vertices in triangle - # 3 = 4 vertices, 6 edges - # 4 = 6 vertices, 1 hexagon, 6 edges - # 5 = 10 vertices, 15 edges, petersen graph - # 6 = 16 vertices, 32 edges, non-planar, tesseract - - self.EPSILON = 0.5 # this is the boundary between a draw and a win - - self.NUM_READS_SA = 1000 # this is for simulated annealing - - # These are parameters related to the use of QC hardware, if you're not using QC you can just leave these - # The defaults here are no shimming, no gauge transforms, only use M=1 automorphism, and collect a lot of - # samples (N=1000) - - self.USE_QC = True # set to False if you just want to use e.g. simulated annealer - self.USE_MOCK_DWAVE_SAMPLER = True # set to True if you want a software version of the hardware (doesn't sample like the HW tho so don't trust it, just for debugging) - self.QC_SOLVER_TO_USE = 'Advantage2_prototype2.6' # modify if you want to use a different QC - - self.NUMBER_OF_CHIP_RUNS = 1 # this is M - self.NUM_READS_QC = 1000 # this is N - self.ANNEAL_TIME_IN_NS = 5 # this is the fastest the QC can sweep - - self.USE_GAUGE_TRANSFORM = False - self.USE_SHIM = False - - self.ALPHA_PHI = 0.00001 - self.SHIM_ITERATIONS = 10 - - -class MinimalAdjudicationParameters(object): - def __init__(self): - self.EPSILON = 0.5 # this is the boundary between a draw and a win - self.USE_QC = False - self.NUM_READS_SA = 1000 # this is for simulated annealing - - -class old_Adjudicator(object): - def __init__(self, params): - self.params = params - self.results_dict = None - self.data_dir = os.path.join(os.getcwd(), '..', 'data') - if self.params.USE_QC: # if using QC, get embeddings and automorphisms - self.automorphisms = get_automorphisms(self.params.GRAPH_NUMBER, self.data_dir) - self.embeddings = get_embeddings(self.params.GRAPH_NUMBER, self.params.QC_SOLVER_TO_USE, self.data_dir) - - def compute_winner_score_and_influence_from_correlation_matrix(self, game_state, correlation_matrix): - # correlation_matrix is assumed to be symmetric matrix with zeros on diagonal (so that self-correlation of - # one is not counted) -- this is the standard for computing influence vector - # - # returns: - # winner: if game_state is terminal, string -- one of 'red' (player 1), 'blue' (player 2), 'draw' - # if game_state not terminal, returns None - # score: if game_state is terminal, returns a real number which is the score of the game (difference - # between two players' influences obtained from the influence vector) - # if game_state not terminal, returns None - # influence_vector: a vector of real numbers of length == number of vertices; this stores each vertex's - # influence, which is the sum over all elements of the correlation matrix it is part of - - influence_vector = np.sum(correlation_matrix, axis=0) - - if game_state_is_terminal(game_state): - score = influence_vector[game_state['player1_node']] - influence_vector[game_state['player2_node']] - - if score > self.params.EPSILON: # more positive than epsilon, red wins - winner = 'red' - else: - if score < -self.params.EPSILON: - winner = 'blue' - else: - winner = 'draw' - else: - score = None - winner = None - - return winner, score, influence_vector - - # all four solver functions input game_state, e.g.: - # - # game_state = {'num_nodes': 6, 'edges': [(0, 1, 1), (0, 2, 1), (0, 3, 2), (0, 4, 3), (0, 5, 2), (1, 2, 1), - # (1, 3, 2), (1, 4, 3), (1, 5, 3), (2, 3, 1), (2, 4, 2), (2, 5, 3), (3, 4, 2), (3, 5, 1), (4, 5, 2)], - # 'player1_id': 'player1', 'player2_id': 'player2', 'turn_count': 17, 'current_player_index': 1, - # 'player1_node': 1, 'player2_node': 3} - # - # and return a dictionary that contains the following keys: - # - # 'game_state': a copy of the input game_state dictionary - # 'adjudicator': a string, one of 'simulated_annealing', 'quantum_annealing', 'schrodinger_equation' - # 'winner': if both players have chosen vertices, a string, one of 'red', 'blue', 'draw', otherwise None - # 'score': if both players have chosen vertices, the difference in influence scores as a real number, otherwise None - # 'influence_vector': a vector of real numbers of length vertex_count (one real number per vertex in the game graph) - # 'correlation_matrix': symmetric real-valued matrix of spin-spin correlations with zeros on diagonals - # 'parameters': a copy of the parameters dictionary - - def simulated_annealing(self, game_state): - - h, jay = game_state_to_ising_model(game_state) - sampler = neal.SimulatedAnnealingSampler() - - # Approx match: (1) mean energy and (2) rate of local excitations for square-lattice high precision spin glass - # at 5ns (Advantage2 prototype 2.5) - - # Limits relaxation to local minima. Can vary by model/protocol/QPU. Assumes max(|J|) is scaled to 1. - beta_max = 3 - # Limits equilibration. Can vary by model/protocol/QPU - num_sweeps = 16 - beta_range = [1 / np.sqrt(np.sum([Jij ** 2 for Jij in jay.values()]) + 0.001), beta_max] # 0.001 for J==0 - seed = None # Choose seed=None if reproducibility is not desired - - # randomize_order=True implements standard symmetry-respecting Metropolis algorithm - ss = sampler.sample_ising(h, jay, beta_range=beta_range, num_reads=self.params.NUM_READS_SA, - num_sweeps=num_sweeps, randomize_order=True, seed=seed) - - samps = np.array(ss.record.sample, dtype=float) # casting may not be necessary. - - # creates symmetric matrix with zeros on diagonal (so that self-correlation of one is not counted) -- this is - # the standard for computing influence vector - correlation_matrix = (np.einsum('si,sj->ij', samps, samps) / self.params.NUM_READS_SA - - np.eye(int(game_state['num_nodes']))) - - winner, score_difference, influence_vector = ( - self.compute_winner_score_and_influence_from_correlation_matrix(game_state, correlation_matrix)) - - return_dictionary = {'game_state': game_state, 'adjudicator': 'simulated_annealing', - 'winner': winner, 'score': score_difference, 'influence_vector': influence_vector, - 'correlation_matrix': correlation_matrix, 'parameters': self.params} - - return return_dictionary - - def schrodinger_equation(self, game_state): - - h, jay = game_state_to_ising_model(game_state) - - s_min = 0.001 # beginning and ending anneal times - s_max = 0.999 - - correlation_matrix = ( - evolve_schrodinger(h, jay, s_min=s_min, s_max=s_max, tf=self.params.ANNEAL_TIME_IN_NS, - n_qubits=game_state['num_nodes'])) - # what's returned here is upper triangular with zeros on the diagonal, so we need to add the transpose - correlation_matrix = correlation_matrix + correlation_matrix.T - - winner, score_difference, influence_vector = ( - self.compute_winner_score_and_influence_from_correlation_matrix(game_state, correlation_matrix)) - - return_dictionary = {'game_state': game_state, 'adjudicator': 'schrodinger_equation', - 'winner': winner, 'score': score_difference, 'influence_vector': influence_vector, - 'correlation_matrix': correlation_matrix, 'parameters': self.params} - - return return_dictionary - - def quantum_annealing(self, game_state): - - num_vertices = game_state['num_nodes'] # e.g. 3 - num_embeddings = len(self.embeddings) # e.g. P=343 - total_samples = np.zeros((1, num_vertices)) # 0th layer to get vstack going, remove at the end - - shim_stats = None - all_samples = None - indices_of_flips = None - - if self.params.USE_MOCK_DWAVE_SAMPLER and self.params.USE_SHIM: - print('D-Wave mock sampler is not set up to use the shimming process, turn shim off if using mock!') - - sampler_kwargs = dict(num_reads=self.params.NUM_READS_QC, - answer_mode='raw') - - if self.params.USE_MOCK_DWAVE_SAMPLER: - base_sampler = MockDWaveSampler(topology_type='zephyr', topology_shape=[6, 4]) - else: - base_sampler = DWaveSampler(solver=self.params.QC_SOLVER_TO_USE) - sampler_kwargs.update({'fast_anneal': True, - 'annealing_time': self.params.ANNEAL_TIME_IN_NS / 1000}) - - if self.params.USE_SHIM: - shim_stats = {'qubit_magnetizations': [], - 'average_absolute_value_of_magnetization': [], - 'all_flux_bias_offsets': []} - sampler_kwargs.update({'readout_thermalization': 100., - 'auto_scale': False, - 'flux_drift_compensation': True, - 'flux_biases': [0] * base_sampler.properties['num_qubits']}) - shim_iterations = self.params.SHIM_ITERATIONS - else: - shim_iterations = 1 # if we don't shim, just run through shim step only once - - # ********************************************************** - # Step 0: convert game_state to the desired base Ising model - # ********************************************************** - - # for tangled, h_j=0 for all vertices j in the game graph, and J_ij is one of +1, -1, or 0 for all vertex - # pairs i,j. I named the "base" values (the actual problem defined on the game graph we are asked to solve) - # base_h (all zero) and base_jay (not all zero). - - base_h, base_jay = game_state_to_ising_model(game_state) - - # this finds any isolated vertices that may be in the graph -- we will replace the samples returned for these - # at the end with true 50/50 statistics, so we don't have to worry about them - - isolated_vertices = find_isolated_vertices(num_vertices, base_jay) - - # We now enter a loop where each pass through the loop programs the chip to specific values of h and J but - # now for the entire chip. We do this by first selecting one automorphism and embedding it in multiple - # parallel ways across the entire chip, and then optionally applying a gauge transform across all the qubits - # used. This latter process chooses different random gauges for each of the embedded instances. - - for _ in range(self.params.NUMBER_OF_CHIP_RUNS): - - # ******************************************************************* - # Step 1: Randomly select an automorphism and embed it multiple times - # ******************************************************************* - - automorphism = random.choice(self.automorphisms) # eg {0:0, 1:2, 2:1} - inverted_automorphism_to_use = {v: k for k, v in automorphism.items()} # swaps key <-> values - - permuted_embedding = [] - - for each_embedding in self.embeddings[:num_embeddings]: # each_embedding is like [1093, 1098, 136]; 343 of these for three-vertex graph - this_embedding = [] - for each_vertex in range(num_vertices): # each_vertex ranges from 0 to 2 - this_embedding.append(each_embedding[inverted_automorphism_to_use[each_vertex]]) - permuted_embedding.append(this_embedding) - - # given that permuted_embedding looks like [[1229, 1235, 563], [872, 242, 866], ...] - # this next part converts into the format {0: [1229], 1: [1235], 2: [563], 3: [872], 4: [242], 5: [866]} - - embedding_map = {} - - for embedding_idx in range(num_embeddings): - for each_vertex in range(num_vertices): # up to 0..1037 - embedding_map[num_vertices * embedding_idx + each_vertex] = \ - [permuted_embedding[embedding_idx][each_vertex]] - - # ***************************************************************************************************** - # Step 2: Set h, J parameters for full chip using parallel embeddings of a randomly chosen automorphism - # ***************************************************************************************************** - - # compute full_h and full_j which are h, jay values for the entire chip assuming the above automorphism - # I am calling the problem definition and variable ordering before the automorphism the BLACK or BASE - # situation. After the automorphism the problem definition and variable labels change -- I'm calling the - # situation after the automorphism has been applied the BLUE situation. - - full_h = {} - full_j = {} - - for embedding_idx in range(num_embeddings): - for each_vertex in range(num_vertices): - full_h[num_vertices * embedding_idx + each_vertex] = 0 - - for k, v in base_jay.items(): - edge_under_automorph = (min(automorphism[k[0]], automorphism[k[1]]), - max(automorphism[k[0]], automorphism[k[1]])) - full_j[edge_under_automorph] = v - for j in range(1, num_embeddings): - full_j[(edge_under_automorph[0] + num_vertices * j, - edge_under_automorph[1] + num_vertices * j)] = v - - # ************************************************************************** - # Step 3: Choose random gauge, modify h, J parameters for full chip using it - # ************************************************************************** - - # next we optionally apply a random gauge transformation. I call the situation after the gauge - # transformation has been applied the BLUE with RED STAR situation. - - if self.params.USE_GAUGE_TRANSFORM: - flip_map = [random.choice([-1, 1]) for _ in full_h] # random list of +1, -1 values of len # qubits - indices_of_flips = [i for i, x in enumerate(flip_map) if x == -1] # the indices of the -1 values - - for edge_key, j_val in full_j.items(): # for each edge and associated J value - full_j[edge_key] = j_val * flip_map[edge_key[0]] * flip_map[edge_key[1]] # Jij -> J_ij g_i g_j - - # ***************************************** - # Step 4: Choose sampler and its parameters - # ***************************************** - - sampler_kwargs.update({'h': full_h, - 'J': full_j}) - - sampler = FixedEmbeddingComposite(base_sampler, embedding=embedding_map) # applies the embedding - - # ************************************************************************* - # Step 5: Optionally start shimming process in the BLUE with RED STAR basis - # ************************************************************************* - - # all of this in the BLUE with RED STAR basis, ie post automorph, post gauge transform - for shim_iteration_idx in range(shim_iterations): - - # ************************************** - # Step 6: Generate samples from hardware - # ************************************** - - ss = sampler.sample_ising(**sampler_kwargs) - all_samples = ss.record.sample - - if self.params.USE_SHIM: - - # ************************************************************* - # Step 6a: Compute average values of each qubit == magnetization - # ************************************************************* - - magnetization = np.sum(all_samples, axis=0)/self.params.NUM_READS_QC # BLUE with RED STAR label ordering - shim_stats['average_absolute_value_of_magnetization'].append(np.sum([abs(k) for k in magnetization])/len(magnetization)) - - qubit_magnetization = [0] * base_sampler.properties['num_qubits'] - for k, v in embedding_map.items(): - qubit_magnetization[v[0]] = magnetization[k] # check - - shim_stats['qubit_magnetizations'].append(qubit_magnetization) - - # ************************************** - # Step 6b: Adjust flux bias offset terms - # ************************************** - - for k in range(base_sampler.properties['num_qubits']): - sampler_kwargs['flux_biases'][k] -= self.params.ALPHA_PHI * qubit_magnetization[k] - - shim_stats['all_flux_bias_offsets'].append(sampler_kwargs['flux_biases']) - - # ***************************************************************************************************** - # Step 7: Reverse gauge transform, from BLUE with RED STAR to just BLUE, after shimming process is done - # ***************************************************************************************************** - - if self.params.USE_GAUGE_TRANSFORM: - all_samples[:, indices_of_flips] = -all_samples[:, indices_of_flips] - - # *********************************** - # Step 8: Stack samples in BLUE order - # *********************************** - - # this should make a big fat stack of the results in BLUE variable ordering - all_samples_processed_blue = all_samples[:, range(num_vertices)] - for k in range(1, num_embeddings): - all_samples_processed_blue = np.vstack((all_samples_processed_blue, - all_samples[:, range(num_vertices * k, - num_vertices * (k + 1))])) - - # ********************************************************************** - # Step 9: Reorder columns to make them BLACK order instead of BLUE order - # ********************************************************************** - - all_samples_processed_black = all_samples_processed_blue[:, [automorphism[i] for i in range(all_samples_processed_blue.shape[1])]] - - # ********************************************************* - # Step 10: Add new samples to the stack, all in BLACK order - # ********************************************************* - - total_samples = np.vstack((total_samples, all_samples_processed_black)) - - # *************************************************************** - # Step 11: Post process samples stack to extract return variables - # *************************************************************** - - total_samples = np.delete(total_samples, (0), axis=0) # delete first row of zeros - - # replace columns where there are disconnected variables with truly random samples - for idx in isolated_vertices: - total_samples[:, idx] = np.random.choice([1, -1], size=total_samples.shape[0]) - - sample_count = self.params.NUM_READS_QC * num_embeddings * self.params.NUMBER_OF_CHIP_RUNS - - # this is a full matrix with zeros on the diagonal that uses all the samples - correlation_matrix = \ - (np.einsum('si,sj->ij', total_samples, total_samples) / sample_count - - np.eye(int(game_state['num_nodes']))) - - winner, score_difference, influence_vector = ( - self.compute_winner_score_and_influence_from_correlation_matrix(game_state, correlation_matrix)) - - return_dictionary = {'game_state': game_state, 'adjudicator': 'quantum_annealing', - 'winner': winner, 'score': score_difference, 'influence_vector': influence_vector, - 'correlation_matrix': correlation_matrix, 'parameters': self.params} - - return return_dictionary - - def lookup_table(self, game_state): - - if self.results_dict is None: - # If using graphs 2 or 3, you can use precomputed terminal state adjudications (faster for testing) - # str(game_state['num_nodes'] - 1) is a hack -- num_nodes=3 is graph 2 and num_nodes=4 is graph 3 - # as long as both are complete graphs - graph_number = game_state['num_nodes'] - 1 - - if graph_number not in [2, 3]: - sys.exit(print('lookup table only enabled for complete graphs on 3 and 4 vertices.')) - - script_dir = os.path.dirname(os.path.abspath(__file__)) # Get the directory of the current script - - file_path = os.path.join(script_dir, '..', 'data', - 'graph_' + str(graph_number) + '_terminal_state_outcomes.pkl') - if not os.path.exists(file_path): - get_tso(graph_number, file_path) - with open(file_path, 'rb') as fp: - results = pickle.load(fp) - self.results_dict = build_results_dict(results) - - my_state = convert_erik_game_state_to_my_game_state(game_state) - winner = self.results_dict[str(my_state)] - - return_dictionary = {'game_state': game_state, 'adjudicator': 'lookup_table', - 'winner': winner, 'score': None, 'influence_vector': None, - 'correlation_matrix': None, 'parameters': self.params} - - return return_dictionary diff --git a/tangled_adjudicate/utils/utilities.py b/tangled_adjudicate/utils/utilities.py index 8a219ca..0a51fd1 100644 --- a/tangled_adjudicate/utils/utilities.py +++ b/tangled_adjudicate/utils/utilities.py @@ -3,52 +3,6 @@ import networkx as nx -def game_state_to_ising_model(game_state): - # maps edge state to J value 0, 1 => J = 0; 2 => J = -1 FM; 3 => J = +1 AFM - edge_state_map = {0: 0, 1: 0, 2: -1, 3: 1} - - vertex_count = game_state['num_nodes'] - edge_list = [(each[0], each[1]) for each in game_state['edges']] - - h = {} - jay = {} - - for k in range(vertex_count): - h[k] = 0 - - for k in range(len(edge_list)): - jay[edge_list[k]] = edge_state_map[game_state['edges'][k][2]] - - return h, jay - - -def game_state_is_terminal(game_state): - # a state is terminal if both players have chosen vertices and all edges have been played - # game_state = {'num_nodes': 6, 'edges': [(0, 1, 1), (0, 2, 1), (0, 3, 2), (0, 4, 3), (0, 5, 2), (1, 2, 1), - # (1, 3, 2), (1, 4, 3), (1, 5, 3), (2, 3, 1), (2, 4, 2), (2, 5, 3), (3, 4, 2), (3, 5, 1), (4, 5, 2)], - # 'player1_id': 'player1', 'player2_id': 'player2', 'turn_count': 17, 'current_player_index': 1, - # 'player1_node': 1, 'player2_node': 3} - - edge_states = [each[2] for each in game_state['edges']] - - if edge_states.count(0) == 0 and game_state['player1_node'] != -1 and game_state['player2_node'] != -1: - return True - else: - return False - - -def find_isolated_vertices(n_var, base_jay): - # returns a list of isolated / disconnected vertices if there are any; returns empty list if not - my_graph = nx.Graph() - my_graph.add_nodes_from([k for k in range(n_var)]) - my_graph.add_edges_from([k for k, v in base_jay.items() if v != 0]) - - # Find isolated vertices (vertices with no edges) - isolated_vertices = list(nx.isolates(my_graph)) - - return isolated_vertices - - def get_tso(graph_number, file_path): # get terminal state outcomes if graph_number == 2: From 90187d602c9ba72348bf67fcffefc8c20c90deec Mon Sep 17 00:00:00 2001 From: Geordie Rose Date: Sat, 1 Feb 2025 10:18:10 -0800 Subject: [PATCH 30/59] PEP --- tangled_adjudicate/utils/utilities.py | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/tangled_adjudicate/utils/utilities.py b/tangled_adjudicate/utils/utilities.py index 0a51fd1..2f461e3 100644 --- a/tangled_adjudicate/utils/utilities.py +++ b/tangled_adjudicate/utils/utilities.py @@ -1,6 +1,5 @@ """ a place to put utility functions """ import gdown -import networkx as nx def get_tso(graph_number, file_path): @@ -40,7 +39,7 @@ def convert_erik_game_state_to_my_game_state(game_state): def convert_my_game_state_to_erik_game_state(my_state, number_of_vertices, list_of_edge_tuples): - + # extract erik state from geordie state my_vertices = my_state[:number_of_vertices] my_edges = my_state[number_of_vertices:] @@ -66,10 +65,12 @@ def convert_my_game_state_to_erik_game_state(my_state, number_of_vertices, list_ erik_edges = [(list_of_edge_tuples[k][0], list_of_edge_tuples[k][1], my_edges[k]) for k in range(len(my_edges))] game_state = {'num_nodes': number_of_vertices, - # 'edges': [(0, 1, 3), (0, 2, 1), (0, 3, 3), (1, 2, 1), (1, 3, 3), (2, 3, 1)], 'edges': erik_edges, - 'player1_id': 'player1', 'player2_id': 'player2', 'turn_count': turn_count, - 'current_player_index': current_player_idx, 'player1_node': player_1_vertex, + 'player1_id': 'player1', + 'player2_id': 'player2', + 'turn_count': turn_count, + 'current_player_index': current_player_idx, + 'player1_node': player_1_vertex, 'player2_node': player_2_vertex} return game_state From 537c80a9976b6d01e50c5ee4cad7ffd0f5c3dce5 Mon Sep 17 00:00:00 2001 From: Geordie Rose Date: Sat, 1 Feb 2025 10:20:48 -0800 Subject: [PATCH 31/59] changed # graphs to 11 --- README.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 99f2ef0..d61ac2c 100644 --- a/README.md +++ b/README.md @@ -33,9 +33,9 @@ The full D-Wave setup instructions are [here](https://docs.ocean.dwavesys.com/en ## Tangled Game Graph Specification -A Tangled game graph is specified by a graph number, which label specific graphs included here. I've included ten graphs -numbered 1 through 10. Each graph requires specification of vertex count (how many vertices the graph has) and an -explicit edge list, which are included for these ten graphs. If you'd like to add a new graph, it's simple! Just add +A Tangled game graph is specified by a graph number, which label specific graphs included here. I've included eleven +graphs numbered 1 through 11. Each graph requires specification of vertex count (how many vertices the graph has) and +an explicit edge list, which are included for these 11 graphs. If you'd like to add a new graph, it's simple! Just add it to the GraphProperties class, found in the /utils/game_graph_properties.py file. ## Tangled Game State Specification: Expected Input Format For Adjudicators From 9336986a7d5578afb50b4e1d67c5279f742aab62 Mon Sep 17 00:00:00 2001 From: Geordie Rose Date: Sun, 2 Feb 2025 13:45:25 -0800 Subject: [PATCH 32/59] num_reads to 10000 --- tangled_adjudicate/adjudicators/simulated_annealing.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tangled_adjudicate/adjudicators/simulated_annealing.py b/tangled_adjudicate/adjudicators/simulated_annealing.py index e8bb234..e058330 100644 --- a/tangled_adjudicate/adjudicators/simulated_annealing.py +++ b/tangled_adjudicate/adjudicators/simulated_annealing.py @@ -11,7 +11,7 @@ class SimulatedAnnealingAdjudicator(Adjudicator): def __init__(self) -> None: """Initialize the adjudicator with default values.""" super().__init__() - self.num_reads: int = 1000 + self.num_reads: int = 10000 self.num_sweeps: int = 16 self.beta_max: float = 3.0 From b59f5a24cc76525f17208355a908c4e8dfad5a9d Mon Sep 17 00:00:00 2001 From: Geordie Rose Date: Sun, 2 Feb 2025 13:46:27 -0800 Subject: [PATCH 33/59] added graph 11 --- tangled_adjudicate/utils/game_graph_properties.py | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/tangled_adjudicate/utils/game_graph_properties.py b/tangled_adjudicate/utils/game_graph_properties.py index 7968122..bafa1ee 100644 --- a/tangled_adjudicate/utils/game_graph_properties.py +++ b/tangled_adjudicate/utils/game_graph_properties.py @@ -2,8 +2,8 @@ import sys # A Tangled game graph is specified by a graph number, which label specific graphs included here. In this module there -# are 10 included graphs numbered 1 through 10. Each graph requires specification of vertex count (how many vertices -# the graph has) and an explicit edge list, which are included for these ten graphs. If you'd like to add a new graph, +# are 11 included graphs numbered 1 through 11. Each graph requires specification of vertex count (how many vertices +# the graph has) and an explicit edge list, which are included for these 11 graphs. If you'd like to add a new graph, # it's simple -- just add it to the GraphProperties class. @@ -157,6 +157,12 @@ def __init__(self, graph_number): (23, 33), (23, 41), (23, 35), (23, 43), (23, 37), (23, 45), (23, 39), (23, 47), (24, 25), (26, 27), (28, 29), (30, 31), (32, 33), (34, 35), (36, 37), (38, 39), (40, 41), (42, 43), (44, 45), (46, 47)] + elif graph_number == 11: + # minimal graph for testing; 3 vertices 2 edges + self.vertex_count = 3 + + self.edge_list = [(0, 1), (1, 2)] + else: print('Bad graph_number in GraphProperties initialization -- no graph corresponding to your choice exists.') @@ -166,7 +172,7 @@ def __init__(self, graph_number): def main(): # this is a debugging tool to make sure everything looks right! - for graph_number in range(1, 11): + for graph_number in range(1, 12): g = GraphProperties(graph_number=graph_number) print('****') print('graph', graph_number, 'has', g.vertex_count, 'vertices and', g.edge_count, 'edges.') From 6cbf399b6a62e7cae3f408aab0fcce864bb4532d Mon Sep 17 00:00:00 2001 From: Geordie Rose Date: Mon, 3 Feb 2025 08:18:06 -0800 Subject: [PATCH 34/59] moved sampler initialization into __init__ to make it a class variable --- .../adjudicators/simulated_annealing.py | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/tangled_adjudicate/adjudicators/simulated_annealing.py b/tangled_adjudicate/adjudicators/simulated_annealing.py index e058330..c9fda3a 100644 --- a/tangled_adjudicate/adjudicators/simulated_annealing.py +++ b/tangled_adjudicate/adjudicators/simulated_annealing.py @@ -11,10 +11,11 @@ class SimulatedAnnealingAdjudicator(Adjudicator): def __init__(self) -> None: """Initialize the adjudicator with default values.""" super().__init__() - self.num_reads: int = 10000 + self.sampler = neal.SimulatedAnnealingSampler() + self.num_reads: int = 1000 self.num_sweeps: int = 16 self.beta_max: float = 3.0 - + def setup(self, **kwargs) -> None: """Configure the simulated annealing parameters. @@ -40,7 +41,7 @@ def setup(self, **kwargs) -> None: if not isinstance(kwargs['beta_max'], (int, float)) or kwargs['beta_max'] <= 0: raise ValueError("beta_max must be a positive number") self.beta_max = float(kwargs['beta_max']) - + self._parameters = { 'num_reads': self.num_reads, 'num_sweeps': self.num_sweeps, @@ -63,7 +64,7 @@ def adjudicate(self, game_state: GameState) -> AdjudicationResult: # Convert game state to Ising model ising_model = self._game_state_to_ising(game_state) - sampler = neal.SimulatedAnnealingSampler() + # sampler = neal.SimulatedAnnealingSampler() # Calculate beta range based on coupling strengths beta_range = [ @@ -72,7 +73,7 @@ def adjudicate(self, game_state: GameState) -> AdjudicationResult: ] # Perform simulated annealing - response = sampler.sample_ising( + response = self.sampler.sample_ising( ising_model['h'], ising_model['j'], beta_range=beta_range, From 195909359c325d0efa5371960c964c882482c29a Mon Sep 17 00:00:00 2001 From: Geordie Rose Date: Thu, 6 Feb 2025 10:45:15 -0800 Subject: [PATCH 35/59] changed default num_reads in SA to 10000 --- tangled_adjudicate/adjudicators/simulated_annealing.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tangled_adjudicate/adjudicators/simulated_annealing.py b/tangled_adjudicate/adjudicators/simulated_annealing.py index c9fda3a..49eaf85 100644 --- a/tangled_adjudicate/adjudicators/simulated_annealing.py +++ b/tangled_adjudicate/adjudicators/simulated_annealing.py @@ -12,7 +12,7 @@ def __init__(self) -> None: """Initialize the adjudicator with default values.""" super().__init__() self.sampler = neal.SimulatedAnnealingSampler() - self.num_reads: int = 1000 + self.num_reads: int = 10000 self.num_sweeps: int = 16 self.beta_max: float = 3.0 From 139e1530653e6d6da96346b0ae7492dfe4bc860a Mon Sep 17 00:00:00 2001 From: Geordie Rose Date: Sat, 8 Feb 2025 11:37:12 -0800 Subject: [PATCH 36/59] added moser spindle as graph 12 --- .../utils/game_graph_properties.py | 17 ++++++++++++++--- 1 file changed, 14 insertions(+), 3 deletions(-) diff --git a/tangled_adjudicate/utils/game_graph_properties.py b/tangled_adjudicate/utils/game_graph_properties.py index bafa1ee..f68d819 100644 --- a/tangled_adjudicate/utils/game_graph_properties.py +++ b/tangled_adjudicate/utils/game_graph_properties.py @@ -2,15 +2,15 @@ import sys # A Tangled game graph is specified by a graph number, which label specific graphs included here. In this module there -# are 11 included graphs numbered 1 through 11. Each graph requires specification of vertex count (how many vertices +# are 12 included graphs numbered 1 through 12. Each graph requires specification of vertex count (how many vertices # the graph has) and an explicit edge list, which are included for these 11 graphs. If you'd like to add a new graph, # it's simple -- just add it to the GraphProperties class. class GraphProperties(object): def __init__(self, graph_number): - # graph_number is an integer, currently in the range 1 to 10, that labels which graph we are using. - # to add a new graph, simply define a new graph_number (say 11) and provide its vertex_count and edge_list + # graph_number is an integer, currently in the range 1 to 12, that labels which graph we are using. + # to add a new graph, simply define a new graph_number (say 13) and provide its vertex_count and edge_list # following the pattern here. if graph_number == 1: @@ -163,6 +163,17 @@ def __init__(self, graph_number): self.edge_list = [(0, 1), (1, 2)] + elif graph_number == 12: + # moser spindle; smaller than petersen graph, only 8 automorphisms; 7 vertices, 11 edges + self.vertex_count = 7 + + self.edge_list = [(0, 1), (0, 2), (0, 3), + (1, 4), (1, 5), + (2, 4), (2, 6), + (3, 5), (3, 6), + (4, 6), + (5, 6)] + else: print('Bad graph_number in GraphProperties initialization -- no graph corresponding to your choice exists.') From 1af743a8c5748edffce27e28dcd57cb38aedde0e Mon Sep 17 00:00:00 2001 From: Geordie Rose Date: Mon, 17 Mar 2025 10:02:29 -0700 Subject: [PATCH 37/59] added RSG and snark graphs --- .../utils/game_graph_properties.py | 126 ++++++++++++++++-- 1 file changed, 115 insertions(+), 11 deletions(-) diff --git a/tangled_adjudicate/utils/game_graph_properties.py b/tangled_adjudicate/utils/game_graph_properties.py index f68d819..d0ec913 100644 --- a/tangled_adjudicate/utils/game_graph_properties.py +++ b/tangled_adjudicate/utils/game_graph_properties.py @@ -2,15 +2,15 @@ import sys # A Tangled game graph is specified by a graph number, which label specific graphs included here. In this module there -# are 12 included graphs numbered 1 through 12. Each graph requires specification of vertex count (how many vertices -# the graph has) and an explicit edge list, which are included for these 11 graphs. If you'd like to add a new graph, +# are 16 included graphs numbered 1 through 16. Each graph requires specification of vertex count (how many vertices +# the graph has) and an explicit edge list, which are included for these 16 graphs. If you'd like to add a new graph, # it's simple -- just add it to the GraphProperties class. class GraphProperties(object): def __init__(self, graph_number): - # graph_number is an integer, currently in the range 1 to 12, that labels which graph we are using. - # to add a new graph, simply define a new graph_number (say 13) and provide its vertex_count and edge_list + # graph_number is an integer, currently in the range 1 to 16, that labels which graph we are using. + # to add a new graph, simply define a new graph_number (say 17) and provide its vertex_count and edge_list # following the pattern here. if graph_number == 1: @@ -167,12 +167,116 @@ def __init__(self, graph_number): # moser spindle; smaller than petersen graph, only 8 automorphisms; 7 vertices, 11 edges self.vertex_count = 7 - self.edge_list = [(0, 1), (0, 2), (0, 3), - (1, 4), (1, 5), - (2, 4), (2, 6), - (3, 5), (3, 6), - (4, 6), - (5, 6)] + self.edge_list = [(0, 1), (0, 4), (0, 6), + (1, 2), (1, 5), + (2, 3), (2, 5), + (3, 4), (3, 5), + (3, 6), + (4, 6)] + + elif graph_number == 13: + # second Blanusa snark, 4 automorphisms, mirror symmetric; leftmost = 0, rightmost = 1; 18 vertices, 27 edges + + self.vertex_count = 18 + + self.edge_list = [(0, 2), (0, 4), (0, 16), + (1, 3), (1, 5), (1, 17), + (2, 3), (2, 6), + (3, 8), + (4, 5), (4, 9), + (5, 11), + (6, 9), (6, 14), + (7, 10), (7, 12), (7, 13), + (8, 11), (8, 15), + (9, 12), + (10, 14), (10, 15), + (11, 13), + (12, 16), + (13, 17), + (14, 16), + (15, 17)] + + elif graph_number == 14: + # first Loupekine snark, 8 automorphisms, mirror symmetric; leftmost = 15, rightmost = 12; 22 vertices, 33 edges + + self.vertex_count = 22 + + self.edge_list = [(0,1), (0,2), (0,9), # original 1<->2, 1<->3, 1<->10 + (1,3), (1,20), # original 2<->4, 2<->21 + (2,5), (2,6), # original 3<->6, 3<->7 + (3,4), (3,6), # original 4<->5, 4<->7 + (4,5), (4,7), # original 5<->6, 5<->8 + (5,16), # original 6<->17 + (6,10), # original 7<->11 + (7,12), (7,14), # original 8<->13, 8<->15 + (8,9), (8,11), (8,14), # original 9<->10, 9<->12, 9<->15 + (9,13), # original 10<->14 + (10,11), (10,15), # original 11<->12, 11<->16 + (11,12), # original 12<->13 + (12,13), # original 13<->14 + (13,21), # original 14<->22 + (14,17), # original 15<->18 + (15,18), (15,19), # original 16<->19, 16<->20 + (16,17), (16,19), # original 17<->18, 17<->20 + (17,18), # original 18<->19 + (18,20), # original 19<->21 + (19,21), # original 20<->22 + (20,21)] # original 21<->22 + + elif graph_number == 15: + # Szekeres snark; 50 vertices, 75 edges; vertex 1 left, vertex 4 right + + self.vertex_count = 50 + + self.edge_list = [(0, 6), (0, 9), (0, 12), (1, 15), (1, 18), (1, 21), (2, 24), (2, 27), (2, 30), (3, 33), (3, 36), (3, 39), (4, 42), (4, 45), (4, 48), (5, 6), (5, 10), (5, 22), (6, 7), (7, 8), (7, 38), (8, 9), (8, 13), (9, 10), (10, 11), (11, 12), (11, 25), (12, 13), (13, 41), (14, 15), (14, 19), (14, 31), (15, 16), (16, 17), (16, 47), (17, 18), (17, 22), (18, 19), (19, 20), (20, 21), (20, 34), (21, 22), (23, 24), (23, 28), (23, 40), (24, 25), (25, 26), (26, 27), (26, 31), (27, 28), (28, 29), (29, 30), (29, 43), (30, 31), (32, 33), (32, 37), (32, 49), (33, 34), (34, 35), (35, 36), (35, 40), (36, 37), (37, 38), (38, 39), (39, 40), (41, 42), (41, 46), (42, 43), (43, 44), (44, 45), (44, 49), (45, 46), (46, 47), (47, 48), (48, 49)] + + elif graph_number == 16: + # Descartes snark; 210 vertices, 315 edges; vertex 1 left, vertex 4 right + + self.vertex_count = 210 + + self.edge_list = [(0, 18), (0, 33), (0, 38), (1, 2), (1, 18), (1, 22), (2, 3), (2, 8), (3, 4), (3, 7), + (4, 12), (4, 30), (5, 6), (5, 8), (5, 13), (6, 7), (6, 11), (7, 10), (8, 9), (9, 10), + (9, 11), (10, 14), (11, 12), (12, 15), (13, 14), (13, 209), (14, 15), (15, 193), + (16, 20), (16, 21), (16, 38), (17, 18), (17, 19), (17, 20), (19, 21), (19, 37), + (20, 73), (21, 22), (22, 74), (23, 25), (23, 26), (23, 34), (24, 26), (24, 29), + (24, 31), (25, 29), (25, 39), (26, 41), (27, 28), (27, 36), (27, 44), (28, 35), + (28, 42), (29, 32), (30, 31), (30, 34), (31, 40), (32, 33), (32, 41), (33, 34), + (35, 39), (35, 41), (36, 45), (36, 105), (37, 38), (37, 72), (39, 40), (40, 99), + (42, 43), (42, 50), (43, 46), (43, 47), (44, 47), (44, 49), (45, 46), (45, 48), + (46, 49), (47, 48), (48, 52), (49, 51), (50, 51), (50, 58), (51, 52), (52, 53), + (53, 54), (53, 88), (54, 55), (54, 84), (55, 56), (55, 91), (56, 57), (56, 66), + (57, 58), (57, 63), (58, 62), (59, 60), (59, 62), (59, 181), (60, 63), (60, 65), + (61, 62), (61, 64), (61, 65), (63, 64), (64, 180), (65, 66), (66, 179), (67, 68), + (67, 75), (67, 151), (68, 69), (68, 148), (69, 70), (69, 83), (70, 71), (70, 81), + (71, 72), (71, 76), (72, 73), (73, 74), (74, 75), (75, 155), (76, 77), (76, 79), + (77, 78), (77, 120), (78, 81), (78, 82), (79, 80), (79, 82), (80, 81), (80, 121), + (82, 83), (83, 122), (84, 85), (84, 86), (85, 89), (85, 171), (86, 87), (86, 90), + (87, 88), (87, 170), (88, 89), (89, 90), (90, 91), (91, 172), (92, 94), (92, 99), + (92, 105), (93, 95), (93, 96), (93, 100), (94, 96), (94, 97), (95, 97), (95, 104), + (96, 103), (97, 98), (98, 101), (98, 102), (99, 100), (100, 101), (101, 135), + (102, 103), (102, 114), (103, 104), (104, 105), (106, 107), (106, 114), (106, 119), + (107, 110), (107, 111), (108, 109), (108, 111), (108, 115), (109, 110), (109, 118), + (110, 113), (111, 112), (112, 113), (112, 117), (113, 116), (114, 115), (115, 116), + (116, 133), (117, 118), (117, 123), (118, 119), (119, 120), (120, 121), (121, 122), + (122, 125), (123, 124), (123, 147), (124, 125), (124, 142), (125, 140), (126, 127), + (126, 132), (126, 133), (127, 128), (127, 129), (128, 131), (128, 136), (129, 130), + (129, 138), (130, 131), (130, 134), (131, 132), (132, 139), (133, 134), (134, 135), + (135, 136), (136, 137), (137, 138), (137, 192), (138, 139), (139, 164), (140, 141), + (140, 178), (141, 143), (141, 144), (142, 144), (142, 145), (143, 145), (143, 147), + (144, 146), (145, 177), (146, 147), (146, 176), (148, 149), (148, 173), (149, 152), + (149, 153), (150, 151), (150, 152), (150, 174), (151, 153), (152, 155), (153, 154), + (154, 155), (154, 175), (156, 159), (156, 160), (156, 163), (157, 158), (157, 159), + (157, 165), (158, 160), (158, 168), (159, 162), (160, 161), (161, 162), (161, 166), + (162, 167), (163, 164), (163, 169), (164, 165), (165, 166), (166, 190), (167, 168), + (167, 175), (168, 169), (169, 170), (170, 171), (171, 172), (172, 173), (173, 174), + (174, 175), (176, 177), (176, 208), (177, 178), (178, 179), (179, 180), (180, 181), + (181, 198), (182, 184), (182, 186), (182, 189), (183, 186), (183, 187), (183, 191), + (184, 185), (184, 187), (185, 192), (185, 193), (186, 194), (187, 188), (188, 189), + (188, 195), (189, 190), (190, 191), (191, 192), (193, 194), (194, 195), (195, 206), + (196, 197), (196, 198), (196, 208), (197, 201), (197, 203), (198, 200), (199, 200), + (199, 203), (199, 204), (200, 202), (201, 202), (201, 204), (202, 209), (203, 207), + (204, 205), (205, 206), (205, 208), (206, 207), (207, 209)] else: @@ -183,7 +287,7 @@ def __init__(self, graph_number): def main(): # this is a debugging tool to make sure everything looks right! - for graph_number in range(1, 12): + for graph_number in range(1, 17): g = GraphProperties(graph_number=graph_number) print('****') print('graph', graph_number, 'has', g.vertex_count, 'vertices and', g.edge_count, 'edges.') From 4fd8b3d7ec97434f48570ddff68eb12cfccccdfd Mon Sep 17 00:00:00 2001 From: Geordie Rose Date: Mon, 24 Mar 2025 09:42:11 -0700 Subject: [PATCH 38/59] fixed output num_reads --- tangled_adjudicate/adjudicators/simulated_annealing.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/tangled_adjudicate/adjudicators/simulated_annealing.py b/tangled_adjudicate/adjudicators/simulated_annealing.py index 49eaf85..28d5c9c 100644 --- a/tangled_adjudicate/adjudicators/simulated_annealing.py +++ b/tangled_adjudicate/adjudicators/simulated_annealing.py @@ -12,7 +12,7 @@ def __init__(self) -> None: """Initialize the adjudicator with default values.""" super().__init__() self.sampler = neal.SimulatedAnnealingSampler() - self.num_reads: int = 10000 + self.num_reads: int = 100000 self.num_sweeps: int = 16 self.beta_max: float = 3.0 @@ -61,7 +61,9 @@ def adjudicate(self, game_state: GameState) -> AdjudicationResult: ValueError: If the game state is invalid """ self._validate_game_state(game_state) - + # this is just so that the data structure returned stores correct number, as this could have been changed + self._parameters['num_reads'] = self.num_reads + # Convert game state to Ising model ising_model = self._game_state_to_ising(game_state) # sampler = neal.SimulatedAnnealingSampler() From d39a486fc01c4919e308db43a326f969c45bfdf7 Mon Sep 17 00:00:00 2001 From: Geordie Rose Date: Mon, 24 Mar 2025 09:43:10 -0700 Subject: [PATCH 39/59] added conversion of input state from string in convert_my_game_state_to_erik_game_state --- tangled_adjudicate/utils/utilities.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/tangled_adjudicate/utils/utilities.py b/tangled_adjudicate/utils/utilities.py index 2f461e3..4874ebc 100644 --- a/tangled_adjudicate/utils/utilities.py +++ b/tangled_adjudicate/utils/utilities.py @@ -1,4 +1,5 @@ """ a place to put utility functions """ +import ast import gdown @@ -40,6 +41,9 @@ def convert_erik_game_state_to_my_game_state(game_state): def convert_my_game_state_to_erik_game_state(my_state, number_of_vertices, list_of_edge_tuples): # extract erik state from geordie state + if isinstance(my_state, str): + my_state = ast.literal_eval(my_state) + my_vertices = my_state[:number_of_vertices] my_edges = my_state[number_of_vertices:] From 5514b50c118f172b5892fa401f96b4f4e70fbbc4 Mon Sep 17 00:00:00 2001 From: Geordie Rose Date: Mon, 24 Mar 2025 09:44:10 -0700 Subject: [PATCH 40/59] added graphs 17 and 18 (cube and 3-prism) --- .../utils/game_graph_properties.py | 24 +++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/tangled_adjudicate/utils/game_graph_properties.py b/tangled_adjudicate/utils/game_graph_properties.py index d0ec913..f03ce06 100644 --- a/tangled_adjudicate/utils/game_graph_properties.py +++ b/tangled_adjudicate/utils/game_graph_properties.py @@ -278,6 +278,30 @@ def __init__(self, graph_number): (199, 203), (199, 204), (200, 202), (201, 202), (201, 204), (202, 209), (203, 207), (204, 205), (205, 206), (205, 208), (206, 207), (207, 209)] + elif graph_number == 17: + # cube graph; 8 vertices, 12 edges + + self.vertex_count = 8 + + self.edge_list = [(0, 1), (0, 2), (0, 4), + (1, 3), (1, 5), + (2, 3), (2, 6), + (3, 7), + (4, 5), (4, 6), + (5, 7), + (6, 7)] + + elif graph_number == 18: + # 3-prism graph; 6 vertices, 9 edges + + self.vertex_count = 6 + + self.edge_list = [(0, 1), (0, 2), (0, 3), + (1, 2), (1, 4), + (2, 5), + (3, 4), (3, 5), + (4, 5)] + else: print('Bad graph_number in GraphProperties initialization -- no graph corresponding to your choice exists.') From 2a85a43c06eca27962cb9f93d308c02334a45134 Mon Sep 17 00:00:00 2001 From: Geordie Rose Date: Mon, 14 Jul 2025 11:57:20 -0700 Subject: [PATCH 41/59] changed default num_reads to 10000 --- tangled_adjudicate/adjudicators/simulated_annealing.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tangled_adjudicate/adjudicators/simulated_annealing.py b/tangled_adjudicate/adjudicators/simulated_annealing.py index 28d5c9c..0335f94 100644 --- a/tangled_adjudicate/adjudicators/simulated_annealing.py +++ b/tangled_adjudicate/adjudicators/simulated_annealing.py @@ -12,7 +12,7 @@ def __init__(self) -> None: """Initialize the adjudicator with default values.""" super().__init__() self.sampler = neal.SimulatedAnnealingSampler() - self.num_reads: int = 100000 + self.num_reads: int = 10000 self.num_sweeps: int = 16 self.beta_max: float = 3.0 From 8efc9703ab86814aada070ecf1cbe26c35c8d887 Mon Sep 17 00:00:00 2001 From: Geordie Rose Date: Mon, 14 Jul 2025 11:57:42 -0700 Subject: [PATCH 42/59] changed default num_reads to 10000 --- tangled_adjudicate/adjudicators/simulated_annealing.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tangled_adjudicate/adjudicators/simulated_annealing.py b/tangled_adjudicate/adjudicators/simulated_annealing.py index 0335f94..8ece9d9 100644 --- a/tangled_adjudicate/adjudicators/simulated_annealing.py +++ b/tangled_adjudicate/adjudicators/simulated_annealing.py @@ -20,7 +20,7 @@ def setup(self, **kwargs) -> None: """Configure the simulated annealing parameters. Args: - num_reads: Number of annealing reads (default: 1000) + num_reads: Number of annealing reads (default: 10000) num_sweeps: Number of sweeps per read (default: 16) beta_max: Maximum inverse temperature (default: 3.0) From 7444bb30b6286ba3a04fb5663d12ce2f0262be14 Mon Sep 17 00:00:00 2001 From: Geordie Rose Date: Mon, 14 Jul 2025 12:11:16 -0700 Subject: [PATCH 43/59] updated to fix # samples bug and changed some default params --- .../adjudicators/quantum_annealing.py | 22 ++++++++++++++----- 1 file changed, 16 insertions(+), 6 deletions(-) diff --git a/tangled_adjudicate/adjudicators/quantum_annealing.py b/tangled_adjudicate/adjudicators/quantum_annealing.py index 552d9b6..08f69b0 100644 --- a/tangled_adjudicate/adjudicators/quantum_annealing.py +++ b/tangled_adjudicate/adjudicators/quantum_annealing.py @@ -13,15 +13,15 @@ @dataclass class QAParameters: """Parameters for quantum annealing.""" - num_reads: int = 1000 - anneal_time: float = 5.0 # ns + num_reads: int = 10000 + anneal_time: float = 40.0 # ns num_chip_runs: int = 1 use_gauge_transform: bool = False use_shim: bool = False shim_iterations: int = 1 alpha_phi: float = 0.1 - use_mock: bool = True - solver_name: str = 'Advantage2_prototype2.6' + use_mock: bool = False + solver_name: str = 'Advantage2_system1.3' graph_number: Optional[int] = None data_dir: Optional[str] = None @@ -180,8 +180,13 @@ def adjudicate(self, game_state: GameState) -> AdjudicationResult: if self.params.use_mock and self.params.use_shim: print('D-Wave mock sampler is not set up to use the shimming process, turn shim off if using mock!') + # here the 'num_reads' parameter is set so that the actual sample count that's returned is what the user set + # for num_reads -- we get extra samples from both num_embeddings and self.params.num_chip_runs. For example + # if num_embeddings = 343 and self.params.num_chip_runs = 2, and the user asks for self.params.num_reads = 10,000 + # then the actual 'num_reads' sent to the solver would be int(10000/343/2) = 14. This will return a total of + # approximately 10,000 samples. sampler_kwargs = { - 'num_reads': self.params.num_reads, + 'num_reads': int(self.params.num_reads / num_embeddings / self.params.num_chip_runs), 'answer_mode': 'raw' } @@ -355,7 +360,9 @@ def adjudicate(self, game_state: GameState) -> AdjudicationResult: for idx in isolated_vertices: total_samples[:, idx] = np.random.choice([1, -1], size=total_samples.shape[0]) - sample_count = self.params.num_reads * num_embeddings * self.params.num_chip_runs + # I changed sample count to what should be the actual sample count + # sample_count = self.params.num_reads * num_embeddings * self.params.num_chip_runs + sample_count = int(self.params.num_reads / num_embeddings / self.params.num_chip_runs) * num_embeddings * self.params.num_chip_runs # this is a full matrix with zeros on the diagonal that uses all the samples correlation_matrix = \ @@ -365,6 +372,9 @@ def adjudicate(self, game_state: GameState) -> AdjudicationResult: # Compute results winner, score, influence_vector = self._compute_winner_score_and_influence(game_state, correlation_matrix) + # add samples to the result returned + self._parameters.update({'samples' : total_samples}) + return AdjudicationResult( game_state=game_state, adjudicator='quantum_annealing', From 1c277ab1fca75d557c04a5870ec43e566659e9f2 Mon Sep 17 00:00:00 2001 From: Geordie Rose Date: Mon, 14 Jul 2025 12:13:01 -0700 Subject: [PATCH 44/59] chaged deault anneal time to 40 ns --- tangled_adjudicate/adjudicators/schrodinger.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tangled_adjudicate/adjudicators/schrodinger.py b/tangled_adjudicate/adjudicators/schrodinger.py index 7c0d377..55e1db1 100644 --- a/tangled_adjudicate/adjudicators/schrodinger.py +++ b/tangled_adjudicate/adjudicators/schrodinger.py @@ -11,7 +11,7 @@ class SchrodingerEquationAdjudicator(Adjudicator): def __init__(self) -> None: """Initialize the adjudicator with default values.""" super().__init__() - self.anneal_time: float = 5.0 # ns + self.anneal_time: float = 40.0 # ns self.s_min: float = 0.001 self.s_max: float = 0.999 @@ -19,7 +19,7 @@ def setup(self, **kwargs) -> None: """Configure the Schrödinger equation parameters. Args: - anneal_time: Annealing time in nanoseconds (default: 5.0) + anneal_time: Annealing time in nanoseconds (default: 40.0) s_min: Minimum annealing parameter (default: 0.001) s_max: Maximum annealing parameter (default: 0.999) From 7b7f97f569e8e08788054be3f94a8c3186c5035b Mon Sep 17 00:00:00 2001 From: Geordie Rose Date: Mon, 14 Jul 2025 12:13:37 -0700 Subject: [PATCH 45/59] anneal sched for advantage2.1.3 --- .../schrodinger/advantage2.1.3.txt | 1001 +++++++++++++++++ 1 file changed, 1001 insertions(+) create mode 100644 tangled_adjudicate/schrodinger/advantage2.1.3.txt diff --git a/tangled_adjudicate/schrodinger/advantage2.1.3.txt b/tangled_adjudicate/schrodinger/advantage2.1.3.txt new file mode 100644 index 0000000..0638931 --- /dev/null +++ b/tangled_adjudicate/schrodinger/advantage2.1.3.txt @@ -0,0 +1,1001 @@ +0.000 11.31481618 0.55821572 +0.001 11.29651238 0.55912224 +0.002 11.27820859 0.56002876 +0.003 11.25990480 0.56093529 +0.004 11.24160100 0.56184181 +0.005 11.22329553 0.56274866 +0.006 11.20488941 0.56367502 +0.007 11.18648329 0.56460139 +0.008 11.16807717 0.56552776 +0.009 11.14967105 0.56645412 +0.010 11.13126159 0.56738115 +0.011 11.11275355 0.56832767 +0.012 11.09424551 0.56927419 +0.013 11.07573747 0.57022070 +0.014 11.05722943 0.57116722 +0.015 11.03869818 0.57211842 +0.016 11.02008902 0.57308531 +0.017 11.00147987 0.57405221 +0.018 10.98287071 0.57501910 +0.019 10.96426156 0.57598600 +0.020 10.94560574 0.57696248 +0.021 10.92689512 0.57795023 +0.022 10.90818451 0.57893797 +0.023 10.88947389 0.57992571 +0.024 10.87076327 0.58091346 +0.025 10.85197660 0.58191713 +0.026 10.83316540 0.58292594 +0.027 10.81435421 0.58393474 +0.028 10.79554301 0.58494355 +0.029 10.77671205 0.58595658 +0.030 10.75780050 0.58698682 +0.031 10.73888895 0.58801705 +0.032 10.71997740 0.58904728 +0.033 10.70106585 0.59007752 +0.034 10.68209047 0.59112164 +0.035 10.66307886 0.59217366 +0.036 10.64406725 0.59322567 +0.037 10.62505563 0.59427768 +0.038 10.60602180 0.59533462 +0.039 10.58691127 0.59640858 +0.040 10.56780075 0.59748253 +0.041 10.54869023 0.59855648 +0.042 10.52957971 0.59963044 +0.043 10.51038133 0.60072426 +0.044 10.49117145 0.60182068 +0.045 10.47196157 0.60291710 +0.046 10.45275169 0.60401352 +0.047 10.43348821 0.60512229 +0.048 10.41417975 0.60624143 +0.049 10.39487129 0.60736057 +0.050 10.37556283 0.60847971 +0.051 10.35621741 0.60960754 +0.052 10.33681146 0.61074958 +0.053 10.31740551 0.61189162 +0.054 10.29799957 0.61303366 +0.055 10.27857032 0.61418128 +0.056 10.25906595 0.61534689 +0.057 10.23956157 0.61651250 +0.058 10.22005720 0.61767811 +0.059 10.20054368 0.61884595 +0.060 10.18094223 0.62003525 +0.061 10.16134078 0.62122455 +0.062 10.14173933 0.62241385 +0.063 10.12212231 0.62360703 +0.064 10.10242527 0.62482010 +0.065 10.08272822 0.62603318 +0.066 10.06303118 0.62724625 +0.067 10.04331124 0.62846513 +0.068 10.02351757 0.62970270 +0.069 10.00372389 0.63094027 +0.070 9.98393021 0.63217784 +0.071 9.96410676 0.63342310 +0.072 9.94421733 0.63468541 +0.073 9.92432790 0.63594771 +0.074 9.90443848 0.63721002 +0.075 9.88449748 0.63848590 +0.076 9.86451346 0.63977311 +0.077 9.84452944 0.64106032 +0.078 9.82454542 0.64234752 +0.079 9.80448538 0.64365513 +0.080 9.78440594 0.64496794 +0.081 9.76432651 0.64628075 +0.082 9.74424251 0.64759481 +0.083 9.72406845 0.64893350 +0.084 9.70389438 0.65027219 +0.085 9.68372032 0.65161088 +0.086 9.66350521 0.65296100 +0.087 9.64323821 0.65432559 +0.088 9.62297121 0.65569018 +0.089 9.60270421 0.65705477 +0.090 9.58235342 0.65844315 +0.091 9.56199338 0.65983417 +0.092 9.54163333 0.66122518 +0.093 9.52123977 0.66262589 +0.094 9.50078642 0.66404391 +0.095 9.48033307 0.66546193 +0.096 9.45987972 0.66687995 +0.097 9.43934655 0.66832151 +0.098 9.41880149 0.66976658 +0.099 9.39825643 0.67121165 +0.100 9.37766438 0.67267084 +0.101 9.35702856 0.67414318 +0.102 9.33639273 0.67561553 +0.103 9.31574179 0.67709251 +0.104 9.29501433 0.67859292 +0.105 9.27428687 0.68009333 +0.106 9.25355941 0.68159374 +0.107 9.23275824 0.68311716 +0.108 9.21194005 0.68464589 +0.109 9.19112185 0.68617463 +0.110 9.17025140 0.68771998 +0.111 9.14934425 0.68927700 +0.112 9.12843710 0.69083403 +0.113 9.10749237 0.69240323 +0.114 9.08649631 0.69398906 +0.115 9.06550026 0.69557490 +0.116 9.04448107 0.69716837 +0.117 9.02339531 0.69878383 +0.118 9.00230956 0.70039929 +0.119 8.98121560 0.70201751 +0.120 8.96004167 0.70366264 +0.121 8.93886774 0.70530777 +0.122 8.91768618 0.70695551 +0.123 8.89642599 0.70863022 +0.124 8.87516580 0.71030493 +0.125 8.85389625 0.71198291 +0.126 8.83254946 0.71368788 +0.127 8.81120266 0.71539285 +0.128 8.78984471 0.71710178 +0.129 8.76841061 0.71883783 +0.130 8.74697651 0.72057388 +0.131 8.72552974 0.72231452 +0.132 8.70400978 0.72408172 +0.133 8.68248981 0.72584892 +0.134 8.66094375 0.72762576 +0.135 8.63933988 0.72942397 +0.136 8.61773601 0.73122219 +0.137 8.59608959 0.73303642 +0.138 8.57440167 0.73486629 +0.139 8.55271375 0.73669616 +0.140 8.53096645 0.73854881 +0.141 8.50919387 0.74041116 +0.142 8.48742129 0.74227352 +0.143 8.46557350 0.74416529 +0.144 8.44371702 0.74606045 +0.145 8.42184569 0.74796153 +0.146 8.39990741 0.74988929 +0.147 8.37796913 0.75181705 +0.148 8.35598624 0.75376293 +0.149 8.33396690 0.75572360 +0.150 8.31194756 0.75768426 +0.151 8.28985324 0.75967595 +0.152 8.26775232 0.76167036 +0.153 8.24562777 0.76367474 +0.154 8.22344476 0.76570376 +0.155 8.20126175 0.76773278 +0.156 8.17902612 0.76978440 +0.157 8.15676300 0.77184784 +0.158 8.13448557 0.77391754 +0.159 8.11214455 0.77601507 +0.160 8.08980352 0.77811260 +0.161 8.06740607 0.78023530 +0.162 8.04498688 0.78236768 +0.163 8.02254725 0.78450936 +0.164 8.00004947 0.78667745 +0.165 7.97755169 0.78884554 +0.166 7.95499090 0.79104281 +0.167 7.93241420 0.79324745 +0.168 7.90981130 0.79546446 +0.169 7.88715801 0.79770523 +0.170 7.86450099 0.79994781 +0.171 7.84177341 0.80222431 +0.172 7.81904583 0.80450081 +0.173 7.79626164 0.80680503 +0.174 7.77345942 0.80911808 +0.175 7.75062223 0.81144859 +0.176 7.72774503 0.81379906 +0.177 7.70485469 0.81615622 +0.178 7.68190218 0.81854499 +0.179 7.65894968 0.82093377 +0.180 7.63593200 0.82335632 +0.181 7.61290566 0.82578337 +0.182 7.58982915 0.82823691 +0.183 7.56673100 0.83070189 +0.184 7.54359372 0.83318793 +0.185 7.52042404 0.83569140 +0.186 7.49722595 0.83821045 +0.187 7.47398448 0.84075330 +0.188 7.45072543 0.84330598 +0.189 7.42741193 0.84588909 +0.190 7.40409177 0.84847600 +0.191 7.38070645 0.85110005 +0.192 7.35732112 0.85372410 +0.193 7.33386875 0.85638711 +0.194 7.31041414 0.85905141 +0.195 7.28689374 0.86175466 +0.196 7.26337195 0.86445873 +0.197 7.23978342 0.86720306 +0.198 7.21619409 0.86994787 +0.199 7.19253746 0.87273407 +0.200 7.16887983 0.87552087 +0.201 7.14515553 0.87834946 +0.202 7.12142946 0.88117919 +0.203 7.09763759 0.88405095 +0.204 7.07384236 0.88692491 +0.205 7.04998527 0.88983916 +0.206 7.02611508 0.89276210 +0.207 7.00219488 0.89571824 +0.208 6.97825172 0.89868993 +0.209 6.95426834 0.90168882 +0.210 6.93025209 0.90471037 +0.211 6.90620543 0.90775289 +0.212 6.88211600 0.91082549 +0.213 6.85800601 0.91391254 +0.214 6.83384332 0.91703737 +0.215 6.80966994 0.92016988 +0.216 6.78543522 0.92334723 +0.217 6.76118659 0.92653494 +0.218 6.73689133 0.92975743 +0.219 6.71256587 0.93300286 +0.220 6.68821189 0.93626997 +0.221 6.66381044 0.93957387 +0.222 6.63939169 0.94289125 +0.223 6.61492029 0.94625024 +0.224 6.59042556 0.94962802 +0.225 6.56589546 0.95303431 +0.226 6.54132479 0.95647395 +0.227 6.51673603 0.95992846 +0.228 6.49209058 0.96343053 +0.229 6.46742528 0.96694956 +0.230 6.44272394 0.97049944 +0.231 6.41798082 0.97408578 +0.232 6.39321453 0.97769251 +0.233 6.36840563 0.98133717 +0.234 6.34356251 0.98501288 +0.235 6.31869630 0.98870961 +0.236 6.29378017 0.99245257 +0.237 6.26883739 0.99622073 +0.238 6.24386781 1.00001421 +0.239 6.21884867 1.00385551 +0.240 6.19380726 1.00771868 +0.241 6.16873292 1.01161428 +0.242 6.14361468 1.01555400 +0.243 6.11846913 1.01952163 +0.244 6.09329633 1.02351723 +0.245 6.06807616 1.02756238 +0.246 6.04282797 1.03163746 +0.247 6.01755273 1.03574158 +0.248 5.99223642 1.03989047 +0.249 5.96688635 1.04407697 +0.250 5.94150946 1.04829364 +0.251 5.91609777 1.05254989 +0.252 5.89064730 1.05685123 +0.253 5.86516942 1.06118491 +0.254 5.83966167 1.06555419 +0.255 5.81411533 1.06997025 +0.256 5.78853525 1.07442810 +0.257 5.76292627 1.07892227 +0.258 5.73728847 1.08345305 +0.259 5.71161343 1.08803199 +0.260 5.68590448 1.09265575 +0.261 5.66016714 1.09731764 +0.262 5.63440150 1.10201798 +0.263 5.60860001 1.10676789 +0.264 5.58276506 1.11156518 +0.265 5.55690213 1.11640272 +0.266 5.53100896 1.12128443 +0.267 5.50508631 1.12620994 +0.268 5.47913051 1.13118563 +0.269 5.45314173 1.13621239 +0.270 5.42712409 1.14128476 +0.271 5.40107777 1.14640325 +0.272 5.37500294 1.15156836 +0.273 5.34889979 1.15678061 +0.274 5.32276315 1.16204992 +0.275 5.29659670 1.16737123 +0.276 5.27040303 1.17274104 +0.277 5.24418188 1.17816072 +0.278 5.21793213 1.18363339 +0.279 5.19165458 1.18915870 +0.280 5.16534950 1.19473730 +0.281 5.13901715 1.20036983 +0.282 5.11265632 1.20606009 +0.283 5.08626731 1.21180897 +0.284 5.05985198 1.21761397 +0.285 5.03341064 1.22347577 +0.286 5.00694361 1.22939507 +0.287 4.98045122 1.23537259 +0.288 4.95393400 1.24140850 +0.289 4.92739192 1.24750446 +0.290 4.90082479 1.25366257 +0.291 4.87423346 1.25988251 +0.292 4.84761830 1.26616504 +0.293 4.82097970 1.27251095 +0.294 4.79431807 1.27892104 +0.295 4.76763383 1.28539612 +0.296 4.74092738 1.29193700 +0.297 4.71419918 1.29854450 +0.298 4.68744967 1.30521946 +0.299 4.66067929 1.31196273 +0.300 4.63388864 1.31877475 +0.301 4.60707802 1.32565698 +0.302 4.58024777 1.33261087 +0.303 4.55339875 1.33963602 +0.304 4.52653145 1.34673332 +0.305 4.49964640 1.35390365 +0.306 4.47274413 1.36114791 +0.307 4.44582516 1.36846701 +0.308 4.41888968 1.37586372 +0.309 4.39193783 1.38334141 +0.310 4.36497108 1.39089685 +0.311 4.33799001 1.39853100 +0.312 4.31099527 1.40624455 +0.313 4.28398760 1.41403762 +0.314 4.25696749 1.42191194 +0.315 4.22993580 1.42986702 +0.316 4.20289180 1.43791448 +0.317 4.17583745 1.44604613 +0.318 4.14877357 1.45426165 +0.319 4.12170083 1.46256199 +0.320 4.09461991 1.47094814 +0.321 4.06753051 1.47943367 +0.322 4.04043450 1.48800736 +0.323 4.01333259 1.49667009 +0.324 3.98622553 1.50542287 +0.325 3.95911397 1.51426953 +0.326 3.93199848 1.52321830 +0.327 3.90488038 1.53225745 +0.328 3.87776036 1.54139125 +0.329 3.85063919 1.55063239 +0.330 3.82351790 1.55996712 +0.331 3.79639728 1.56939818 +0.332 3.76927853 1.57894205 +0.333 3.74216228 1.58858281 +0.334 3.71504937 1.59832151 +0.335 3.68794161 1.60817870 +0.336 3.66083914 1.61813613 +0.337 3.63374284 1.62819488 +0.338 3.60665498 1.63837436 +0.339 3.57957500 1.64865350 +0.340 3.55250562 1.65905347 +0.341 3.52544644 1.66955905 +0.342 3.49839966 1.68018237 +0.343 3.47136583 1.69091939 +0.344 3.44434603 1.70177082 +0.345 3.41734226 1.71274432 +0.346 3.39035399 1.72382864 +0.347 3.36338515 1.73504367 +0.348 3.33643354 1.74636835 +0.349 3.30950404 1.75782629 +0.350 3.28259443 1.76939832 +0.351 3.25570937 1.78110451 +0.352 3.22884746 1.79293128 +0.353 3.20201109 1.80488583 +0.354 3.17520230 1.81697238 +0.355 3.14841980 1.82918008 +0.356 3.12166974 1.84153138 +0.357 3.09494868 1.85400626 +0.358 3.06826084 1.86661796 +0.359 3.04160707 1.87936498 +0.360 3.01498700 1.89224168 +0.361 2.98840622 1.90526462 +0.362 2.96186244 1.91842087 +0.363 2.93535740 1.93171277 +0.364 2.90889630 1.94515389 +0.365 2.88247742 1.95873405 +0.366 2.85610188 1.97245324 +0.367 2.82977288 1.98631771 +0.368 2.80349325 2.00033202 +0.369 2.77726175 2.01448860 +0.370 2.75107988 2.02878839 +0.371 2.72495354 2.04324430 +0.372 2.69888110 2.05784789 +0.373 2.67286327 2.07259769 +0.374 2.64690328 2.08749878 +0.375 2.62100357 2.10255390 +0.376 2.59516644 2.11776513 +0.377 2.56939207 2.13312938 +0.378 2.54368220 2.14864748 +0.379 2.51803860 2.16432026 +0.380 2.49246305 2.18014850 +0.381 2.46695733 2.19613298 +0.382 2.44152622 2.21228075 +0.383 2.41616909 2.22858701 +0.384 2.39088742 2.24505161 +0.385 2.36568276 2.26167459 +0.386 2.34055811 2.27845886 +0.387 2.31551505 2.29540428 +0.388 2.29055550 2.31251136 +0.389 2.26568136 2.32978056 +0.390 2.24089455 2.34721229 +0.391 2.21619702 2.36480690 +0.392 2.19159070 2.38256473 +0.393 2.16707752 2.40048602 +0.394 2.14265944 2.41857100 +0.395 2.11833841 2.43681982 +0.396 2.09411637 2.45523258 +0.397 2.06999537 2.47380948 +0.398 2.04597811 2.49255167 +0.399 2.02206565 2.51145763 +0.400 1.99825994 2.53052726 +0.401 1.97456292 2.54976036 +0.402 1.95097655 2.56915670 +0.403 1.92750275 2.58871595 +0.404 1.90414346 2.60843776 +0.405 1.88090060 2.62832168 +0.406 1.85777609 2.64836723 +0.407 1.83477184 2.66857382 +0.408 1.81188975 2.68894084 +0.409 1.78913155 2.70946739 +0.410 1.76650065 2.73015462 +0.411 1.74399879 2.75100144 +0.412 1.72162621 2.77200497 +0.413 1.69938472 2.79316424 +0.414 1.67727613 2.81447822 +0.415 1.65530220 2.83594581 +0.416 1.63346469 2.85756583 +0.417 1.61176663 2.87933844 +0.418 1.59021142 2.90126403 +0.419 1.56879785 2.92333805 +0.420 1.54752759 2.94555906 +0.421 1.52640218 2.96792545 +0.422 1.50542308 2.99043552 +0.423 1.48459182 3.01308758 +0.424 1.46391352 3.03588323 +0.425 1.44338805 3.05881901 +0.426 1.42301499 3.08189132 +0.427 1.40279577 3.10509825 +0.428 1.38273179 3.12843786 +0.429 1.36282443 3.15190814 +0.430 1.34308018 3.17551111 +0.431 1.32349596 3.19924099 +0.432 1.30407233 3.22309500 +0.433 1.28481050 3.24707092 +0.434 1.26571168 3.27116648 +0.435 1.24677736 3.29537959 +0.436 1.22801393 3.31971144 +0.437 1.20941683 3.34415562 +0.438 1.19098708 3.36870963 +0.439 1.17272561 3.39337097 +0.440 1.15463333 3.41813707 +0.441 1.13671217 3.44300595 +0.442 1.11896704 3.46797728 +0.443 1.10139360 3.49304531 +0.444 1.08399255 3.51820735 +0.445 1.06676460 3.54346071 +0.446 1.04971072 3.56880284 +0.447 1.03283136 3.59423088 +0.448 1.01613181 3.61974424 +0.449 0.99960845 3.64533806 +0.450 0.98326108 3.67100925 +0.451 0.96709007 3.69675496 +0.452 0.95109572 3.72257235 +0.453 0.93527829 3.74845857 +0.454 0.91963798 3.77441078 +0.455 0.90417829 3.80042726 +0.456 0.88889800 3.82650457 +0.457 0.87379522 3.85263920 +0.458 0.85886992 3.87882829 +0.459 0.84412226 3.90506908 +0.460 0.82955237 3.93135881 +0.461 0.81516009 3.95769467 +0.462 0.80094521 3.98407387 +0.463 0.78690746 4.01049362 +0.464 0.77304654 4.03695118 +0.465 0.75936208 4.06344382 +0.466 0.74585448 4.08996897 +0.467 0.73252462 4.11652415 +0.468 0.71936983 4.14310633 +0.469 0.70638964 4.16971291 +0.470 0.69358293 4.19634127 +0.471 0.68094930 4.22298892 +0.472 0.66848847 4.24965343 +0.473 0.65619970 4.27633233 +0.474 0.64408221 4.30302322 +0.475 0.63213518 4.32972373 +0.476 0.62035776 4.35643153 +0.477 0.60874903 4.38314435 +0.478 0.59730806 4.40985997 +0.479 0.58603388 4.43657621 +0.480 0.57492547 4.46329093 +0.481 0.56398183 4.49000206 +0.482 0.55320085 4.51670762 +0.483 0.54258219 4.54340563 +0.484 0.53212796 4.57009393 +0.485 0.52183353 4.59677084 +0.486 0.51169769 4.62343454 +0.487 0.50171921 4.65008330 +0.488 0.49189684 4.67671542 +0.489 0.48222930 4.70332925 +0.490 0.47271527 4.72992320 +0.491 0.46335343 4.75649574 +0.492 0.45414242 4.78304537 +0.493 0.44508090 4.80957068 +0.494 0.43616983 4.83606982 +0.495 0.42740768 4.86254143 +0.496 0.41878944 4.88898489 +0.497 0.41031370 4.91539900 +0.498 0.40197904 4.94178262 +0.499 0.39378527 4.96813437 +0.500 0.38573629 4.99445182 +0.501 0.37782384 5.02073552 +0.502 0.37004645 5.04698451 +0.503 0.36240263 5.07319790 +0.504 0.35489090 5.09937480 +0.505 0.34751582 5.12551262 +0.506 0.34027180 5.15161164 +0.507 0.33315338 5.17767229 +0.508 0.32615886 5.20369402 +0.509 0.31929669 5.22967289 +0.510 0.31255702 5.25561092 +0.511 0.30593657 5.28150814 +0.512 0.29943692 5.30736298 +0.513 0.29306163 5.33317301 +0.514 0.28680088 5.35894072 +0.515 0.28065323 5.38466572 +0.516 0.27462456 5.41034471 +0.517 0.26870912 5.43597889 +0.518 0.26290196 5.46156931 +0.519 0.25720521 5.48711420 +0.520 0.25161984 5.51261213 +0.521 0.24613618 5.53806636 +0.522 0.24076109 5.56347304 +0.523 0.23549009 5.58883320 +0.524 0.23031638 5.61414911 +0.525 0.22524887 5.63941582 +0.526 0.22027842 5.66463672 +0.527 0.21540093 5.68981303 +0.528 0.21062711 5.71493873 +0.529 0.20594364 5.74001953 +0.530 0.20135141 5.76505430 +0.531 0.19685568 5.79003955 +0.532 0.19244303 5.81498150 +0.533 0.18812643 5.83987260 +0.534 0.18389154 5.86471973 +0.535 0.17974501 5.88951864 +0.536 0.17568204 5.91427121 +0.537 0.17170007 5.93897823 +0.538 0.16780327 5.96363662 +0.539 0.16398046 5.98825216 +0.540 0.16024421 6.01281685 +0.541 0.15657932 6.03733899 +0.542 0.15299453 6.06181272 +0.543 0.14948097 6.08624278 +0.544 0.14604371 6.11062542 +0.545 0.14267550 6.13496456 +0.546 0.13938124 6.15925646 +0.547 0.13615327 6.18350544 +0.548 0.13299693 6.20770742 +0.549 0.12990422 6.23186707 +0.550 0.12688085 6.25598000 +0.551 0.12391852 6.28005121 +0.552 0.12102329 6.30407600 +0.553 0.11818660 6.32805971 +0.554 0.11541511 6.35199710 +0.555 0.11269828 6.37589512 +0.556 0.11004655 6.39974564 +0.557 0.10744717 6.42355747 +0.558 0.10490850 6.44732382 +0.559 0.10242279 6.47104996 +0.560 0.09999228 6.49473366 +0.561 0.09761588 6.51837522 +0.562 0.09528949 6.54197729 +0.563 0.09301816 6.56553534 +0.564 0.09079251 6.58905637 +0.565 0.08862156 6.61253248 +0.566 0.08649529 6.63597141 +0.567 0.08441804 6.65936900 +0.568 0.08238768 6.68272652 +0.569 0.08039945 6.70604758 +0.570 0.07846097 6.72932498 +0.571 0.07656331 6.75256606 +0.572 0.07470811 6.77576887 +0.573 0.07289732 6.79893111 +0.574 0.07112454 6.82205814 +0.575 0.06939473 6.84514482 +0.576 0.06770365 6.86819464 +0.577 0.06604902 6.89120930 +0.578 0.06443639 6.91418285 +0.579 0.06285818 6.93712224 +0.580 0.06131531 6.96002610 +0.581 0.05981122 6.98289040 +0.582 0.05833977 7.00572090 +0.583 0.05690122 7.02851689 +0.584 0.05549933 7.05127386 +0.585 0.05412805 7.07399775 +0.586 0.05278737 7.09668815 +0.587 0.05148130 7.11934005 +0.588 0.05020394 7.14195957 +0.589 0.04895500 7.16454659 +0.590 0.04773882 7.18709554 +0.591 0.04654960 7.20961269 +0.592 0.04538703 7.23209797 +0.593 0.04425385 7.25454755 +0.594 0.04314756 7.27696396 +0.595 0.04206620 7.29934915 +0.596 0.04101025 7.32170206 +0.597 0.03998154 7.34401979 +0.598 0.03897614 7.36630695 +0.599 0.03799357 7.38856371 +0.600 0.03703637 7.41078618 +0.601 0.03610197 7.43297730 +0.602 0.03518896 7.45513854 +0.603 0.03429746 7.47726929 +0.604 0.03342953 7.49936608 +0.605 0.03258174 7.52143334 +0.606 0.03175366 7.54347125 +0.607 0.03094499 7.56547982 +0.608 0.03015813 7.58745484 +0.609 0.02938965 7.60940109 +0.610 0.02863915 7.63131874 +0.611 0.02790626 7.65320796 +0.612 0.02719322 7.67506453 +0.613 0.02649708 7.69689282 +0.614 0.02581738 7.71869314 +0.615 0.02515386 7.74046549 +0.616 0.02450757 7.76220754 +0.617 0.02387754 7.78392043 +0.618 0.02326259 7.80560583 +0.619 0.02266239 7.82726390 +0.620 0.02207660 7.84889479 +0.621 0.02150635 7.87049584 +0.622 0.02095038 7.89206896 +0.623 0.02040782 7.91361539 +0.624 0.01987835 7.93513527 +0.625 0.01936170 7.95662876 +0.626 0.01885904 7.97809287 +0.627 0.01836887 7.99953024 +0.628 0.01789066 8.02094152 +0.629 0.01742417 8.04232681 +0.630 0.01696912 8.06368625 +0.631 0.01652550 8.08501942 +0.632 0.01609393 8.10632431 +0.633 0.01567298 8.12760377 +0.634 0.01526241 8.14885795 +0.635 0.01486198 8.17008697 +0.636 0.01447146 8.19129096 +0.637 0.01409105 8.21246888 +0.638 0.01372084 8.23361999 +0.639 0.01335981 8.25474648 +0.640 0.01300778 8.27584837 +0.641 0.01266457 8.29692570 +0.642 0.01232998 8.31797859 +0.643 0.01200379 8.33900716 +0.644 0.01168624 8.36001025 +0.645 0.01137711 8.38098794 +0.646 0.01107577 8.40194169 +0.647 0.01078204 8.42287160 +0.648 0.01049572 8.44377778 +0.649 0.01021665 8.46466035 +0.650 0.00994465 8.48551942 +0.651 0.00967968 8.50635464 +0.652 0.00942200 8.52716459 +0.653 0.00917090 8.54795125 +0.654 0.00892622 8.56871472 +0.655 0.00868780 8.58945510 +0.656 0.00845549 8.61017250 +0.657 0.00822915 8.63086701 +0.658 0.00800862 8.65153874 +0.659 0.00779377 8.67218779 +0.660 0.00758445 8.69281425 +0.661 0.00738089 8.71341671 +0.662 0.00718269 8.73399629 +0.663 0.00698960 8.75455364 +0.664 0.00680150 8.77508878 +0.665 0.00661828 8.79560174 +0.666 0.00643984 8.81609259 +0.667 0.00626604 8.83656144 +0.668 0.00609677 8.85700837 +0.669 0.00593192 8.87743347 +0.670 0.00577138 8.89783683 +0.671 0.00561503 8.91821853 +0.672 0.00546278 8.93857866 +0.673 0.00531473 8.95891619 +0.674 0.00517062 8.97923198 +0.675 0.00503028 8.99952652 +0.676 0.00489362 9.01979987 +0.677 0.00476057 9.04005201 +0.678 0.00463102 9.06028302 +0.679 0.00450490 9.08049299 +0.680 0.00438212 9.10068200 +0.681 0.00426259 9.12085012 +0.682 0.00414622 9.14099743 +0.683 0.00403294 9.16112400 +0.684 0.00392267 9.18122992 +0.685 0.00381533 9.20131526 +0.686 0.00371084 9.22138009 +0.687 0.00360916 9.24142432 +0.688 0.00351019 9.26144817 +0.689 0.00341387 9.28145171 +0.690 0.00332012 9.30143499 +0.691 0.00322888 9.32139809 +0.692 0.00314008 9.34134108 +0.693 0.00305367 9.36126403 +0.694 0.00296958 9.38116700 +0.695 0.00288775 9.40104998 +0.696 0.00280817 9.42091274 +0.697 0.00273072 9.44075573 +0.698 0.00265536 9.46057901 +0.699 0.00258203 9.48038273 +0.700 0.00251066 9.50016697 +0.701 0.00244122 9.51993168 +0.702 0.00237366 9.53967694 +0.703 0.00230792 9.55940281 +0.704 0.00224396 9.57910934 +0.705 0.00218174 9.59879661 +0.706 0.00212120 9.61846468 +0.707 0.00206231 9.63811360 +0.708 0.00200501 9.65774344 +0.709 0.00194928 9.67735425 +0.710 0.00189505 9.69694609 +0.711 0.00184230 9.71651908 +0.712 0.00179098 9.73607335 +0.713 0.00174106 9.75560884 +0.714 0.00169249 9.77512560 +0.715 0.00164526 9.79462369 +0.716 0.00159931 9.81410316 +0.717 0.00155462 9.83356408 +0.718 0.00151118 9.85300615 +0.719 0.00146892 9.87242974 +0.720 0.00142782 9.89183493 +0.721 0.00138808 9.91122681 +0.722 0.00134922 9.93059581 +0.723 0.00131141 9.94994603 +0.724 0.00127465 9.96927802 +0.725 0.00123926 9.98860048 +0.726 0.00120458 10.00789870 +0.727 0.00117109 10.02718418 +0.728 0.00113848 10.04645097 +0.729 0.00110655 10.06569488 +0.730 0.00107542 10.08491905 +0.731 0.00104515 10.10412543 +0.732 0.00101572 10.12331407 +0.733 0.00098710 10.14248500 +0.734 0.00095927 10.16163829 +0.735 0.00093220 10.18077391 +0.736 0.00090588 10.19989213 +0.737 0.00088029 10.21899288 +0.738 0.00085541 10.23807621 +0.739 0.00083121 10.25714216 +0.740 0.00080768 10.27619078 +0.741 0.00078482 10.29522164 +0.742 0.00076262 10.31423453 +0.743 0.00074104 10.33323024 +0.744 0.00072005 10.35220880 +0.745 0.00069964 10.37117026 +0.746 0.00067980 10.39011467 +0.747 0.00066052 10.40904196 +0.748 0.00064176 10.42795235 +0.749 0.00062353 10.44684591 +0.750 0.00060580 10.46572264 +0.751 0.00058856 10.48458259 +0.752 0.00057181 10.50342580 +0.753 0.00055551 10.52225231 +0.754 0.00053967 10.54106217 +0.755 0.00052431 10.55985421 +0.756 0.00050938 10.57862925 +0.757 0.00049486 10.59738776 +0.758 0.00048075 10.61612978 +0.759 0.00046704 10.63485526 +0.760 0.00045370 10.65356439 +0.761 0.00044074 10.67225726 +0.762 0.00042813 10.69093388 +0.763 0.00041588 10.70959427 +0.764 0.00040396 10.72823849 +0.765 0.00039238 10.74686645 +0.766 0.00038116 10.76547636 +0.767 0.00037026 10.78407022 +0.768 0.00035965 10.80264806 +0.769 0.00034935 10.82120991 +0.770 0.00033933 10.83975581 +0.771 0.00032959 10.85828574 +0.772 0.00032012 10.87679992 +0.773 0.00031091 10.89529837 +0.774 0.00030196 10.91378108 +0.775 0.00029329 10.93224623 +0.776 0.00028487 10.95069518 +0.777 0.00027668 10.96912852 +0.778 0.00026872 10.98754628 +0.779 0.00026098 11.00594850 +0.780 0.00025346 11.02433520 +0.781 0.00024615 11.04270642 +0.782 0.00023904 11.06106219 +0.783 0.00023214 11.07940179 +0.784 0.00022546 11.09772423 +0.785 0.00021896 11.11603144 +0.786 0.00021264 11.13432346 +0.787 0.00020649 11.15260031 +0.788 0.00020052 11.17086202 +0.789 0.00019472 11.18910863 +0.790 0.00018907 11.20734016 +0.791 0.00018361 11.22555399 +0.792 0.00017831 11.24375267 +0.793 0.00017315 11.26193638 +0.794 0.00016813 11.28010514 +0.795 0.00016326 11.29825894 +0.796 0.00015852 11.31639802 +0.797 0.00015391 11.33452241 +0.798 0.00014946 11.35262921 +0.799 0.00014513 11.37072098 +0.800 0.00014092 11.38879812 +0.801 0.00013682 11.40686066 +0.802 0.00013285 11.42490862 +0.803 0.00012898 11.44294203 +0.804 0.00012523 11.46095970 +0.805 0.00012159 11.47896090 +0.806 0.00011806 11.49694764 +0.807 0.00011462 11.51491991 +0.808 0.00011129 11.53287785 +0.809 0.00010804 11.55082157 +0.810 0.00010488 11.56875057 +0.811 0.00010184 11.58666228 +0.812 0.00009887 11.60455982 +0.813 0.00009599 11.62244322 +0.814 0.00009319 11.64031251 +0.815 0.00009046 11.65816771 +0.816 0.00008782 11.67600821 +0.817 0.00008526 11.69383177 +0.818 0.00008277 11.71164133 +0.819 0.00008036 11.72943686 +0.820 0.00007801 11.74721852 +0.821 0.00007572 11.76498646 +0.822 0.00007350 11.78273881 +0.823 0.00007136 11.80047528 +0.824 0.00006927 11.81819806 +0.825 0.00006724 11.83590716 +0.826 0.00006527 11.85360262 +0.827 0.00006335 11.87128436 +0.828 0.00006150 11.88894864 +0.829 0.00005970 11.90659936 +0.830 0.00005795 11.92423653 +0.831 0.00005625 11.94186016 +0.832 0.00005460 11.95947045 +0.833 0.00005300 11.97706448 +0.834 0.00005144 11.99464383 +0.835 0.00004993 12.01220991 +0.836 0.00004847 12.02976275 +0.837 0.00004704 12.04730236 +0.838 0.00004566 12.06482597 +0.839 0.00004432 12.08233488 +0.840 0.00004302 12.09983064 +0.841 0.00004175 12.11731328 +0.842 0.00004052 12.13478281 +0.843 0.00003933 12.15223664 +0.844 0.00003817 12.16967561 +0.845 0.00003705 12.18710173 +0.846 0.00003596 12.20451501 +0.847 0.00003489 12.22191547 +0.848 0.00003387 12.23929905 +0.849 0.00003287 12.25666919 +0.850 0.00003190 12.27402659 +0.851 0.00003096 12.29137125 +0.852 0.00003004 12.30870243 +0.853 0.00002916 12.32601699 +0.854 0.00002830 12.34331890 +0.855 0.00002746 12.36060813 +0.856 0.00002665 12.37788486 +0.857 0.00002586 12.39514640 +0.858 0.00002510 12.41239323 +0.859 0.00002436 12.42962768 +0.860 0.00002364 12.44684977 +0.861 0.00002294 12.46405879 +0.862 0.00002226 12.48125111 +0.863 0.00002160 12.49843115 +0.864 0.00002096 12.51559891 +0.865 0.00002034 12.53275442 +0.866 0.00001974 12.54989394 +0.867 0.00001916 12.56701991 +0.868 0.00001859 12.58413367 +0.869 0.00001804 12.60123551 +0.870 0.00001750 12.61832282 +0.871 0.00001698 12.63539521 +0.872 0.00001648 12.65245568 +0.873 0.00001599 12.66950424 +0.874 0.00001552 12.68653929 +0.875 0.00001506 12.70355861 +0.876 0.00001461 12.72056608 +0.877 0.00001418 12.73756173 +0.878 0.00001375 12.75454485 +0.879 0.00001335 12.77151145 +0.880 0.00001295 12.78846619 +0.881 0.00001257 12.80540940 +0.882 0.00001219 12.82234053 +0.883 0.00001183 12.83925478 +0.884 0.00001148 12.85615753 +0.885 0.00001114 12.87304877 +0.886 0.00001080 12.88992784 +0.887 0.00001048 12.90679031 +0.888 0.00001017 12.92364134 +0.889 0.00000987 12.94048094 +0.890 0.00000957 12.95730829 +0.891 0.00000929 12.97411931 +0.892 0.00000901 12.99091887 +0.893 0.00000875 13.00770733 +0.894 0.00000848 13.02448283 +0.895 0.00000823 13.04124281 +0.896 0.00000799 13.05799169 +0.897 0.00000775 13.07472951 +0.898 0.00000752 13.09145303 +0.899 0.00000729 13.10816252 +0.900 0.00000708 13.12486100 +0.901 0.00000686 13.14154847 +0.902 0.00000666 13.15822034 +0.903 0.00000646 13.17487966 +0.904 0.00000627 13.19152801 +0.905 0.00000608 13.20816545 +0.906 0.00000590 13.22478579 +0.907 0.00000572 13.24139545 +0.908 0.00000555 13.25799445 +0.909 0.00000539 13.27457990 +0.910 0.00000523 13.29115099 +0.911 0.00000507 13.30771146 +0.912 0.00000492 13.32426132 +0.913 0.00000477 13.34079511 +0.914 0.00000463 13.35731723 +0.915 0.00000449 13.37382878 +0.916 0.00000436 13.39032826 +0.917 0.00000423 13.40681213 +0.918 0.00000410 13.42328570 +0.919 0.00000398 13.43974898 +0.920 0.00000386 13.45619667 +0.921 0.00000374 13.47263244 +0.922 0.00000363 13.48905796 +0.923 0.00000352 13.50547126 +0.924 0.00000342 13.52186939 +0.925 0.00000331 13.53825732 +0.926 0.00000321 13.55463505 +0.927 0.00000312 13.57099703 +0.928 0.00000302 13.58734746 +0.929 0.00000293 13.60368798 +0.930 0.00000285 13.62001552 +0.931 0.00000276 13.63632891 +0.932 0.00000268 13.65263245 +0.933 0.00000260 13.66892549 +0.934 0.00000252 13.68520199 +0.935 0.00000244 13.70146869 +0.936 0.00000237 13.71772559 +0.937 0.00000230 13.73396715 +0.938 0.00000223 13.75019717 +0.939 0.00000216 13.76641744 +0.940 0.00000210 13.78262480 +0.941 0.00000204 13.79881825 +0.942 0.00000197 13.81500224 +0.943 0.00000192 13.83117517 +0.944 0.00000186 13.84733247 +0.945 0.00000180 13.86348034 +0.946 0.00000175 13.87961878 +0.947 0.00000170 13.89574018 +0.948 0.00000164 13.91185209 +0.949 0.00000159 13.92795460 +0.950 0.00000155 13.94404184 +0.951 0.00000150 13.96011793 +0.952 0.00000146 13.97618466 +0.953 0.00000141 13.99223774 +0.954 0.00000137 14.00827823 +0.955 0.00000133 14.02430964 +0.956 0.00000129 14.04032834 +0.957 0.00000125 14.05633353 +0.958 0.00000121 14.07232968 +0.959 0.00000118 14.08831414 +0.960 0.00000114 14.10428418 +0.961 0.00000111 14.12024521 +0.962 0.00000107 14.13619556 +0.963 0.00000104 14.15213060 +0.964 0.00000101 14.16805662 +0.965 0.00000098 14.18397288 +0.966 0.00000095 14.19987304 +0.967 0.00000092 14.21576455 +0.968 0.00000089 14.23164645 +0.969 0.00000087 14.24751216 +0.970 0.00000084 14.26336924 +0.971 0.00000081 14.27921689 +0.972 0.00000079 14.29504828 +0.973 0.00000077 14.31087108 +0.974 0.00000074 14.32668463 +0.975 0.00000072 14.34248184 +0.976 0.00000070 14.35827045 +0.977 0.00000068 14.37404992 +0.978 0.00000066 14.38981307 +0.979 0.00000064 14.40556797 +0.980 0.00000062 14.42131311 +0.981 0.00000060 14.43704263 +0.982 0.00000058 14.45276394 +0.983 0.00000056 14.46847485 +0.984 0.00000055 14.48417087 +0.985 0.00000053 14.49985871 +0.986 0.00000051 14.51553554 +0.987 0.00000050 14.53119819 +0.988 0.00000048 14.54685266 +0.989 0.00000047 14.56249544 +0.990 0.00000046 14.57812489 +0.991 0.00000044 14.59374646 +0.992 0.00000043 14.60935499 +0.993 0.00000042 14.62495159 +0.994 0.00000040 14.64054034 +0.995 0.00000039 14.65611474 +0.996 0.00000038 14.67167861 +0.997 0.00000037 14.68723465 +0.998 0.00000036 14.70277506 +0.999 0.00000035 14.71830633 +1.000 0.00000033 14.73382979 From 1d137c9ec6a5b22d27e392262bd4d3d291916ce0 Mon Sep 17 00:00:00 2001 From: Geordie Rose Date: Mon, 14 Jul 2025 12:14:39 -0700 Subject: [PATCH 46/59] changed to use default anneal sched for advantage2.1.3 --- tangled_adjudicate/schrodinger/sparse_matrices.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tangled_adjudicate/schrodinger/sparse_matrices.py b/tangled_adjudicate/schrodinger/sparse_matrices.py index 787049e..6253307 100644 --- a/tangled_adjudicate/schrodinger/sparse_matrices.py +++ b/tangled_adjudicate/schrodinger/sparse_matrices.py @@ -76,8 +76,8 @@ 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') - data = np.loadtxt(file_path) # Import SR8 qubit information + file_path = os.path.join(os.getcwd(), '..', 'schrodinger', 'advantage2.1.3.txt') + data = np.loadtxt(file_path) # Import annealing schedule info for QC being used # these are both 1001 dimensional row vectors delta_qubit = data[:, 1] / 2 From f1b3d471441afc125a863d2727b5fbae89bec75e Mon Sep 17 00:00:00 2001 From: Geordie Rose Date: Mon, 14 Jul 2025 12:29:32 -0700 Subject: [PATCH 47/59] added probabilities in z basis, plotting if required --- .../schrodinger/schrodinger_functions.py | 32 +++++++++++++++++-- 1 file changed, 29 insertions(+), 3 deletions(-) diff --git a/tangled_adjudicate/schrodinger/schrodinger_functions.py b/tangled_adjudicate/schrodinger/schrodinger_functions.py index cb32599..398512f 100644 --- a/tangled_adjudicate/schrodinger/schrodinger_functions.py +++ b/tangled_adjudicate/schrodinger/schrodinger_functions.py @@ -4,6 +4,7 @@ from tangled_adjudicate.schrodinger.sparse_matrices import (create_pauli_matrices_for_full_size_hamiltonian, load_schedule_data, create_sparse_hamiltonian, compute_eigenvalues_and_eigenvectors) +from tangled_adjudicate.utils.utilities import plot_energies, plot_probabilities, plots_for_paper def initialize_wavefunction(eigenvalues, eigenvectors, n_i, gap_initial): @@ -50,7 +51,13 @@ def calculate_correlation_matrix(sz, psi): return correlation_matrix -def evolve_schrodinger(h, jay, s_min, s_max, tf, n_qubits, verbose=False): +def evolve_schrodinger(h, jay, s_min, s_max, tf, n_qubits, generate_plots=False, verbose=False): + + # todo: it's possible 2 pi tf here is actually t, so tf = 40 ns means t=2*pi*40 ns, ie longer than we want + + ########### + # tf /= 2*pi + ########### # tf = real annealing time in nanoseconds # s_min = 0.1 # Initial dimensionless anneal time (s=t/t_f), lower bound 0 @@ -71,16 +78,23 @@ def evolve_schrodinger(h, jay, s_min, s_max, tf, n_qubits, verbose=False): gap_initial = 0.001 # Threshold gap for initialization in superposition number_of_levels = 2 ** n_qubits # max number, can reduce if you want + # definition of annealing schedules has factor of 2 in Hamiltonian + schedule_energy_reduction_factor = 0.5 + # load and return (1001,) vectors for delta and big_e for typical D-Wave annealing schedule; if you want to try to # exactly match hardware, you will have to use specific data from whatever system you're simulating delta_qubit, big_e_qubit = load_schedule_data() + delta_qubit *= schedule_energy_reduction_factor + big_e_qubit *= schedule_energy_reduction_factor + # Identity and Pauli matrices -- sx, sy, sz are dicts with qubit # as key, should be sparse csr format sx, sy, sz = create_pauli_matrices_for_full_size_hamiltonian(n_qubits=n_qubits) s = s_min energies = [] probabilities = [] + probabilities_in_z_basis = [] gap_old = 0 psi = None eigenvectors = None @@ -88,12 +102,12 @@ def evolve_schrodinger(h, jay, s_min, s_max, tf, n_qubits, verbose=False): correlation_matrix = np.zeros((n_qubits, n_qubits)) # this is the correlation function we use for gameplay start = time.time() - + s_list = [] while s <= s_max: if verbose: print('computing for s = ', s) - + s_list.append(s) # eigenenergies and wavefunctions at s n_s = 1000 * s # checked that s=0.3 gives n_low = 300 @@ -128,6 +142,7 @@ def evolve_schrodinger(h, jay, s_min, s_max, tf, n_qubits, verbose=False): # probability of being in eigenvector N; NOT probabilities of being in the sigma_z basis probabilities.append(trunc_prob) # check P = [P abs(Vn'*psi).^2] + probabilities_in_z_basis.append([np.real_if_close(psi[j].conj() * psi[j])[0] for j in range(len(psi))]) gap = min(eigenvalues[1:n_adaptive]-eigenvalues[0: n_adaptive - 1]) # Determining the integration step based on the gap size @@ -140,6 +155,9 @@ def evolve_schrodinger(h, jay, s_min, s_max, tf, n_qubits, verbose=False): s = s_max gap_old = gap + diagonal_elements = np.diag(big_h.toarray()) + sorted_indices = np.argsort(diagonal_elements) + if verbose: print('This function call took', time.time() - start, 'seconds.') @@ -147,6 +165,14 @@ def evolve_schrodinger(h, jay, s_min, s_max, tf, n_qubits, verbose=False): final_probabilities = probabilities[-1] final_eigenvectors = eigenvectors + if generate_plots: + + graph_number = 19 + print('generating plot for graph_number', graph_number, 'check this is correct...') + # plot_energies(s_list, energies) + # plot_probabilities(s_list, probabilities_in_z_basis) + plots_for_paper(graph_number, s_list, energies, probabilities_in_z_basis, sorted_indices) + if verbose: print('final correlation matrix:') print(correlation_matrix) From 1b2ce1c741ddc0e72cafb6b851c11ec08c34a4f2 Mon Sep 17 00:00:00 2001 From: Geordie Rose Date: Mon, 14 Jul 2025 12:29:50 -0700 Subject: [PATCH 48/59] removing old schedule --- .../schrodinger/new_schedule.txt | 1001 ----------------- 1 file changed, 1001 deletions(-) delete mode 100644 tangled_adjudicate/schrodinger/new_schedule.txt diff --git a/tangled_adjudicate/schrodinger/new_schedule.txt b/tangled_adjudicate/schrodinger/new_schedule.txt deleted file mode 100644 index 860b8cc..0000000 --- a/tangled_adjudicate/schrodinger/new_schedule.txt +++ /dev/null @@ -1,1001 +0,0 @@ -0 3.8771432 0.19753668 -0.001 3.8697344 0.19791432 -0.002 3.8623216 0.19829304 -0.003 3.8549084 0.19867212 -0.004 3.8474956 0.1990516 -0.005 3.8400724 0.19943304 -0.006 3.8326416 0.1998162 -0.007 3.8251936 0.20020268 -0.008 3.8177448 0.20058968 -0.009 3.8102692 0.20098176 -0.01 3.8027656 0.20137908 -0.011 3.7952616 0.20177684 -0.012 3.787758 0.20217496 -0.013 3.780238 0.20257632 -0.014 3.7727152 0.2029786 -0.015 3.765192 0.20338128 -0.016 3.7576688 0.20378436 -0.017 3.7501324 0.2041902 -0.018 3.74259 0.20459744 -0.019 3.7350328 0.20500788 -0.02 3.7274676 0.20542012 -0.021 3.7198788 0.20583724 -0.022 3.7122524 0.20626176 -0.023 3.7046256 0.20668676 -0.024 3.6969988 0.20711216 -0.025 3.689354 0.20754136 -0.026 3.6817076 0.20797128 -0.027 3.6740612 0.20840164 -0.028 3.6664148 0.20883244 -0.029 3.6587516 0.20926676 -0.03 3.6510856 0.20970208 -0.031 3.6434124 0.21013932 -0.032 3.6357268 0.2105794 -0.033 3.628022 0.21102372 -0.034 3.620278 0.21147624 -0.035 3.6125316 0.21192964 -0.036 3.6047852 0.2123836 -0.037 3.5970176 0.21284212 -0.038 3.5892504 0.21330116 -0.039 3.5814828 0.21376064 -0.04 3.5737156 0.21422068 -0.041 3.5659268 0.21468532 -0.042 3.558138 0.21515052 -0.043 3.5503472 0.21561664 -0.044 3.5425364 0.2160874 -0.045 3.5347096 0.21656212 -0.046 3.5268364 0.21704704 -0.047 3.5189588 0.2175334 -0.048 3.5110788 0.2180208 -0.049 3.5031808 0.21851252 -0.05 3.4952828 0.2190048 -0.051 3.4873844 0.21949764 -0.052 3.4794836 0.21999168 -0.053 3.4715648 0.22048996 -0.054 3.4636456 0.2209888 -0.055 3.4557268 0.2214882 -0.056 3.4477904 0.22199212 -0.057 3.4398372 0.22250028 -0.058 3.431836 0.22301992 -0.059 3.423828 0.2235416 -0.06 3.4158156 0.22406496 -0.061 3.4077856 0.22459272 -0.062 3.3997556 0.22512116 -0.063 3.3917256 0.22565016 -0.064 3.3836892 0.22618124 -0.065 3.3756372 0.22671644 -0.066 3.3675852 0.22725224 -0.067 3.3595328 0.22778868 -0.068 3.3514684 0.2283288 -0.069 3.343384 0.22887428 -0.07 3.3352504 0.22943224 -0.071 3.3271076 0.22999308 -0.072 3.3189584 0.23055616 -0.073 3.3107948 0.23112324 -0.074 3.3026316 0.23169108 -0.075 3.294468 0.2322596 -0.076 3.2862952 0.23283096 -0.077 3.2781108 0.23340576 -0.078 3.2699264 0.23398128 -0.079 3.261742 0.23455756 -0.08 3.253552 0.23513596 -0.081 3.2453368 0.23572168 -0.082 3.2370684 0.23632176 -0.083 3.2287848 0.23692656 -0.084 3.2204908 0.23753476 -0.085 3.2121816 0.2381476 -0.086 3.2038724 0.2387612 -0.087 3.1955632 0.23937564 -0.088 3.1872384 0.23999464 -0.089 3.1789036 0.24061716 -0.09 3.1705684 0.24124048 -0.091 3.1622336 0.2418646 -0.092 3.1538984 0.24248952 -0.093 3.1455352 0.2431232 -0.094 3.1371192 0.24377244 -0.095 3.1286828 0.24442832 -0.096 3.120234 0.2450884 -0.097 3.1117728 0.2457528 -0.098 3.1033116 0.24641812 -0.099 3.0948504 0.24708432 -0.1 3.0863708 0.24775648 -0.101 3.0778844 0.24843132 -0.102 3.069398 0.24910708 -0.103 3.060912 0.24978376 -0.104 3.0524256 0.25046136 -0.105 3.0439204 0.25114576 -0.106 3.0353504 0.25185088 -0.107 3.0267516 0.25256584 -0.108 3.0181352 0.25328704 -0.109 3.0095064 0.25401288 -0.11 3.000878 0.2547398 -0.111 2.9922492 0.25546776 -0.112 2.983594 0.25620468 -0.113 2.9749356 0.25694364 -0.114 2.9662772 0.25768372 -0.115 2.9576188 0.25842484 -0.116 2.9489604 0.25916704 -0.117 2.9402916 0.25991372 -0.118 2.9315624 0.26068188 -0.119 2.9228012 0.261462 -0.12 2.9140236 0.26224856 -0.121 2.9052388 0.26303876 -0.122 2.8964536 0.26383012 -0.123 2.887668 0.26462296 -0.124 2.8788596 0.26542444 -0.125 2.8700512 0.26622712 -0.126 2.8612428 0.26703104 -0.127 2.852434 0.26783616 -0.128 2.8436256 0.26864248 -0.129 2.8348148 0.26945092 -0.13 2.825938 0.270285 -0.131 2.8170168 0.27113668 -0.132 2.808074 0.27199744 -0.133 2.7991248 0.27286172 -0.134 2.790176 0.27372736 -0.135 2.781222 0.2745962 -0.136 2.7722452 0.27547452 -0.137 2.7632684 0.27635424 -0.138 2.7542916 0.27723536 -0.139 2.7453148 0.27811792 -0.14 2.736338 0.27900184 -0.141 2.7273612 0.2798872 -0.142 2.7183344 0.28079432 -0.143 2.7092544 0.2817244 -0.144 2.700152 0.28266484 -0.145 2.691046 0.28360816 -0.146 2.68194 0.28455304 -0.147 2.6728256 0.28550284 -0.148 2.6636932 0.28646108 -0.149 2.6545612 0.28742092 -0.15 2.6454292 0.28838236 -0.151 2.6362972 0.2893454 -0.152 2.6271652 0.29031004 -0.153 2.6180332 0.29127632 -0.154 2.6088516 0.29226692 -0.155 2.5995756 0.2933024 -0.156 2.5902644 0.29435536 -0.157 2.580952 0.29541072 -0.158 2.5716396 0.296468 -0.159 2.5623108 0.2975344 -0.16 2.5529636 0.29861072 -0.161 2.5436164 0.29968892 -0.162 2.5342696 0.30076912 -0.163 2.5249224 0.30185124 -0.164 2.5155756 0.30293528 -0.165 2.5062284 0.30402132 -0.166 2.4968576 0.30512184 -0.167 2.4874044 0.30626784 -0.168 2.4779212 0.307431 -0.169 2.4684384 0.3085964 -0.17 2.4589552 0.30976396 -0.171 2.4494548 0.31094232 -0.172 2.4399444 0.31212804 -0.173 2.4304336 0.313316 -0.174 2.4209232 0.31450624 -0.175 2.4114124 0.31569872 -0.176 2.401902 0.31689344 -0.177 2.3923912 0.31809044 -0.178 2.3828672 0.31929776 -0.179 2.3732384 0.32056996 -0.18 2.363574 0.32186496 -0.181 2.3539096 0.3231626 -0.182 2.3442452 0.32446284 -0.183 2.3345576 0.32577892 -0.184 2.3248628 0.32710156 -0.185 2.315168 0.32842692 -0.186 2.3054728 0.32975492 -0.187 2.295778 0.33108564 -0.188 2.2860832 0.332419 -0.189 2.2763884 0.33375504 -0.19 2.2666856 0.33509924 -0.191 2.2568924 0.33650756 -0.192 2.247056 0.33794808 -0.193 2.2372192 0.33939164 -0.194 2.2273828 0.34083832 -0.195 2.2175184 0.342306 -0.196 2.2076516 0.34377832 -0.197 2.1977848 0.34525384 -0.198 2.187918 0.34673248 -0.199 2.1780512 0.34821432 -0.2 2.1681844 0.34969928 -0.201 2.1583176 0.35118744 -0.202 2.1484448 0.35268364 -0.203 2.1384744 0.35426184 -0.204 2.128442 0.35589372 -0.205 2.1184096 0.35752936 -0.206 2.1083752 0.3591704 -0.207 2.0983112 0.36083752 -0.208 2.0882472 0.36250844 -0.209 2.0781832 0.36418328 -0.21 2.0681196 0.36586196 -0.211 2.0580556 0.36754448 -0.212 2.0479916 0.36923088 -0.213 2.037928 0.37092112 -0.214 2.027862 0.3726174 -0.215 2.017718 0.37439424 -0.216 2.0075068 0.37624316 -0.217 1.9972952 0.3780966 -0.218 1.9870776 0.37996076 -0.219 1.9768356 0.38185156 -0.22 1.9665936 0.383747 -0.221 1.956352 0.38564716 -0.222 1.94611 0.38755204 -0.223 1.935868 0.3894616 -0.224 1.9256264 0.39137584 -0.225 1.9153844 0.39329476 -0.226 1.9051428 0.3952184 -0.227 1.89483 0.39723532 -0.228 1.8844512 0.39934356 -0.229 1.874066 0.401466 -0.23 1.8636708 0.403606 -0.231 1.853258 0.405772 -0.232 1.8428452 0.407944 -0.233 1.8324324 0.4101216 -0.234 1.8220196 0.4123048 -0.235 1.8116068 0.414494 -0.236 1.801194 0.4166892 -0.237 1.7907812 0.41889 -0.238 1.7803684 0.4210964 -0.239 1.7699016 0.4233968 -0.24 1.7593848 0.4257888 -0.241 1.7488588 0.4282068 -0.242 1.7383188 0.430652 -0.243 1.727766 0.4331236 -0.244 1.7172136 0.435602 -0.245 1.7066608 0.4380876 -0.246 1.6961084 0.4405804 -0.247 1.6855556 0.4430804 -0.248 1.6750032 0.4455872 -0.249 1.6644504 0.4481012 -0.25 1.6538976 0.450622 -0.251 1.643304 0.4532456 -0.252 1.6326684 0.4559784 -0.253 1.6220192 0.4587596 -0.254 1.6113568 0.4615776 -0.255 1.6006876 0.4644188 -0.256 1.590018 0.4672688 -0.257 1.5793488 0.4701276 -0.258 1.5686796 0.4729948 -0.259 1.5580104 0.4758708 -0.26 1.5473412 0.478756 -0.261 1.536672 0.4816496 -0.262 1.5260028 0.4845516 -0.263 1.5153072 0.4875632 -0.264 1.5045824 0.4907012 -0.265 1.4938488 0.4938992 -0.266 1.4831024 0.4971528 -0.267 1.4723528 0.5004272 -0.268 1.4616032 0.5037124 -0.269 1.4508536 0.5070084 -0.27 1.440104 0.5103152 -0.271 1.4293544 0.5136324 -0.272 1.4186048 0.5169604 -0.273 1.4078552 0.5202996 -0.274 1.3971056 0.5236492 -0.275 1.3863464 0.5271104 -0.276 1.3755764 0.5307368 -0.277 1.3648068 0.534432 -0.278 1.3540304 0.5382216 -0.279 1.343254 0.5420272 -0.28 1.3324776 0.545846 -0.281 1.3217008 0.549678 -0.282 1.3109244 0.5535236 -0.283 1.3001476 0.5573828 -0.284 1.2893712 0.5612552 -0.285 1.2785948 0.5651408 -0.286 1.267818 0.56904 -0.287 1.2570496 0.5730812 -0.288 1.2463012 0.5773176 -0.289 1.2355588 0.581614 -0.29 1.224828 0.586048 -0.291 1.2140976 0.5904984 -0.292 1.2033668 0.594966 -0.293 1.192636 0.5994504 -0.294 1.1819056 0.6039516 -0.295 1.1711748 0.6084696 -0.296 1.160444 0.6130044 -0.297 1.1497136 0.617556 -0.298 1.1389844 0.6221312 -0.299 1.1282792 0.6268188 -0.3 1.117632 0.6317296 -0.301 1.1069968 0.6367028 -0.302 1.0964024 0.641832 -0.303 1.085808 0.6469816 -0.304 1.0752136 0.6521516 -0.305 1.0646192 0.6573424 -0.306 1.0540252 0.662554 -0.307 1.0434308 0.667786 -0.308 1.0328364 0.6730384 -0.309 1.022242 0.6783116 -0.31 1.011654 0.6836216 -0.311 1.0011024 0.6890436 -0.312 0.990654 0.69471 -0.313 0.9802352 0.7004704 -0.314 0.9698732 0.706378 -0.315 0.9595208 0.7123292 -0.316 0.9491684 0.718306 -0.317 0.9388164 0.7243076 -0.318 0.928464 0.730334 -0.319 0.9181116 0.7363856 -0.32 0.9077596 0.742462 -0.321 0.8974072 0.7485632 -0.322 0.8870672 0.7547128 -0.323 0.876766 0.7609576 -0.324 0.8666036 0.7674368 -0.325 0.8565004 0.774042 -0.326 0.8464596 0.7807672 -0.327 0.8364612 0.787576 -0.328 0.8264632 0.794414 -0.329 0.8164652 0.801282 -0.33 0.8064672 0.8081792 -0.331 0.7964688 0.815106 -0.332 0.7864708 0.8220628 -0.333 0.7764728 0.8290488 -0.334 0.7665092 0.8361072 -0.335 0.7566048 0.843268 -0.336 0.7469208 0.8506916 -0.337 0.7373784 0.8583104 -0.338 0.7278844 0.8660156 -0.339 0.7184656 0.8738216 -0.34 0.7090468 0.8816624 -0.341 0.699628 0.8895384 -0.342 0.6902088 0.8974492 -0.343 0.68079 0.9053952 -0.344 0.6713712 0.913376 -0.345 0.6619524 0.921392 -0.346 0.652576 0.9294784 -0.347 0.6432476 0.9376408 -0.348 0.6341284 0.94598 -0.349 0.6252028 0.9545012 -0.35 0.6162912 0.963072 -0.351 0.6074956 0.9717484 -0.352 0.5987096 0.9804692 -0.353 0.5899232 0.9892288 -0.354 0.5811368 0.9980276 -0.355 0.5723504 1.0068652 -0.356 0.563564 1.015742 -0.357 0.554778 1.0246576 -0.358 0.54606 1.0336516 -0.359 0.5373964 1.0427164 -0.36 0.5289524 1.0519124 -0.361 0.5208176 1.0613004 -0.362 0.5126832 1.0707304 -0.363 0.5046412 1.0802304 -0.364 0.4966508 1.0897884 -0.365 0.4886604 1.0993888 -0.366 0.48067 1.1090308 -0.367 0.4726796 1.1187152 -0.368 0.4646892 1.1284416 -0.369 0.4566984 1.13821 -0.37 0.4487968 1.14805 -0.371 0.440942 1.1579484 -0.372 0.4332812 1.16793 -0.373 0.4259612 1.1780396 -0.374 0.4186416 1.1881928 -0.375 0.4113812 1.1983924 -0.376 0.4042276 1.20864 -0.377 0.3970738 1.2189312 -0.378 0.38992016 1.229266 -0.379 0.38276652 1.2396444 -0.38 0.37561288 1.2500668 -0.381 0.36845928 1.2605324 -0.382 0.361433 1.2710504 -0.383 0.3544498 1.2816148 -0.384 0.34771708 1.2922136 -0.385 0.34150448 1.3028412 -0.386 0.33529188 1.313512 -0.387 0.3290908 1.324224 -0.388 0.3230446 1.3349496 -0.389 0.3169984 1.3457184 -0.39 0.3109522 1.3565304 -0.391 0.304906 1.3673856 -0.392 0.2988598 1.378284 -0.393 0.2928136 1.389226 -0.394 0.28689824 1.4001908 -0.395 0.28100764 1.4111944 -0.396 0.27528508 1.4222008 -0.397 0.2699778 1.4331348 -0.398 0.26467056 1.4441108 -0.399 0.25936332 1.4551288 -0.4 0.25418408 1.4661352 -0.401 0.24904028 1.4771684 -0.402 0.24389652 1.4882428 -0.403 0.23875272 1.4993588 -0.404 0.23360896 1.5105164 -0.405 0.22846516 1.5217148 -0.406 0.22347548 1.5328988 -0.407 0.21849656 1.5441196 -0.408 0.21368884 1.555312 -0.409 0.20932268 1.5663412 -0.41 0.20496988 1.5774028 -0.411 0.20061708 1.5885032 -0.412 0.19633684 1.5995984 -0.413 0.19213008 1.6106864 -0.414 0.18792332 1.6218132 -0.415 0.1837166 1.632978 -0.416 0.17950984 1.6441812 -0.417 0.17530744 1.6554204 -0.418 0.171253 1.666614 -0.419 0.16719856 1.6778452 -0.42 0.16329204 1.68903 -0.421 0.15974772 1.70002 -0.422 0.1562266 1.7110308 -0.423 0.15270548 1.7220772 -0.424 0.1492102 1.7331384 -0.425 0.14581204 1.7441584 -0.426 0.14241384 1.7552132 -0.427 0.13901564 1.7663028 -0.428 0.13561744 1.7774276 -0.429 0.13223448 1.7885756 -0.43 0.12896088 1.7996776 -0.431 0.12568732 1.810814 -0.432 0.12254572 1.8218872 -0.433 0.11975616 1.8327004 -0.434 0.11701584 1.8435044 -0.435 0.11427552 1.85434 -0.436 0.11153516 1.8652072 -0.437 0.10889732 1.8760076 -0.438 0.10626792 1.8868308 -0.439 0.10363852 1.8976852 -0.44 0.10100912 1.9085712 -0.441 0.09840452 1.9194656 -0.442 0.09588988 1.9303096 -0.443 0.09337924 1.9411808 -0.444 0.09098584 1.9519788 -0.445 0.08882992 1.962562 -0.446 0.08672964 1.9731164 -0.447 0.08462932 1.9836988 -0.448 0.082529 1.99431 -0.449 0.08048404 2.0048852 -0.45 0.07847048 2.0154516 -0.451 0.07645696 2.026046 -0.452 0.07444344 2.0366684 -0.453 0.07245828 2.0472876 -0.454 0.0705364 2.057866 -0.455 0.06863172 2.0684532 -0.456 0.0668042 2.0789864 -0.457 0.06515708 2.089324 -0.458 0.063579 2.0996024 -0.459 0.06200092 2.1099056 -0.46 0.06042284 2.1202344 -0.461 0.05887012 2.1305532 -0.462 0.05736432 2.1408324 -0.463 0.05585852 2.151136 -0.464 0.05435268 2.1614644 -0.465 0.0528788 2.1717768 -0.466 0.05145208 2.1820528 -0.467 0.05005244 2.1923192 -0.468 0.04870764 2.2025408 -0.469 0.04746972 2.2126316 -0.47 0.04629692 2.2226512 -0.471 0.04512416 2.2326936 -0.472 0.04395136 2.2427584 -0.473 0.04278168 2.2528408 -0.474 0.0416568 2.2628712 -0.475 0.04053192 2.272924 -0.476 0.039407064 2.2829992 -0.477 0.038307652 2.2930576 -0.478 0.03723398 2.3030988 -0.479 0.0361858 2.313124 -0.48 0.035165404 2.3231292 -0.481 0.034220368 2.3330276 -0.482 0.033345056 2.3428276 -0.483 0.03246974 2.3526484 -0.484 0.031594428 2.3624896 -0.485 0.030719116 2.3723516 -0.486 0.02987452 2.3821736 -0.487 0.029038672 2.3919984 -0.488 0.02820282 2.4018432 -0.489 0.027392968 2.411662 -0.49 0.0266011 2.4214688 -0.491 0.025838016 2.4312452 -0.492 0.025092076 2.4410116 -0.493 0.024391192 2.4507048 -0.494 0.023753132 2.4602884 -0.495 0.023115072 2.4698908 -0.496 0.022477012 2.4795116 -0.497 0.021838956 2.4891512 -0.498 0.021214172 2.498778 -0.499 0.020603092 2.5083908 -0.5 0.019992016 2.518022 -0.501 0.019401424 2.5276288 -0.502 0.018820256 2.5372336 -0.503 0.018263376 2.5468072 -0.504 0.017713556 2.5563848 -0.505 0.017190064 2.5659132 -0.506 0.0167236 2.5753144 -0.507 0.016257136 2.5847324 -0.508 0.015790672 2.594168 -0.509 0.015324212 2.6036208 -0.51 0.01486272 2.613076 -0.511 0.014420444 2.6224916 -0.512 0.013978164 2.6319244 -0.513 0.013558748 2.641314 -0.514 0.013145828 2.6507036 -0.515 0.012761444 2.660038 -0.516 0.012379432 2.6693828 -0.517 0.012010116 2.6787032 -0.518 0.011687432 2.6878908 -0.519 0.011364748 2.697094 -0.52 0.01104206 2.7063128 -0.521 0.010719376 2.7155476 -0.522 0.010396692 2.724798 -0.523 0.010088148 2.7340092 -0.524 0.009780848 2.7432316 -0.525 0.009489084 2.7524172 -0.526 0.009200936 2.761606 -0.527 0.008930284 2.7707536 -0.528 0.008659628 2.7799164 -0.529 0.008394468 2.789072 -0.53 0.008165364 2.7980944 -0.531 0.007937004 2.8071284 -0.532 0.007708648 2.8161768 -0.533 0.007480292 2.82524 -0.534 0.007251932 2.8343176 -0.535 0.00703118 2.8433708 -0.536 0.006814836 2.8524156 -0.537 0.006611748 2.8614172 -0.538 0.006412328 2.8704176 -0.539 0.006224276 2.8793848 -0.54 0.00603622 2.888366 -0.541 0.005850032 2.8973512 -0.542 0.005691208 2.906204 -0.543 0.005533928 2.9150624 -0.544 0.005376648 2.923934 -0.545 0.005219368 2.9328188 -0.546 0.005062088 2.9417172 -0.547 0.004907036 2.950614 -0.548 0.004756684 2.9594928 -0.549 0.004614116 2.9683416 -0.55 0.004474796 2.9771864 -0.551 0.004341056 2.9860148 -0.552 0.00420732 2.9948564 -0.553 0.004074064 3.0037072 -0.554 0.003958324 3.0124472 -0.555 0.003844928 3.0211832 -0.556 0.003731532 3.0299316 -0.557 0.003618132 3.0386928 -0.558 0.003504736 3.0474664 -0.559 0.003391668 3.0562504 -0.56 0.003284804 3.064994 -0.561 0.003183716 3.0737096 -0.562 0.003086396 3.082412 -0.563 0.002992608 3.0911028 -0.564 0.00289882 3.099806 -0.565 0.002805036 3.1085216 -0.566 0.0027233 3.1171368 -0.567 0.002644448 3.1257376 -0.568 0.0025656 3.1343496 -0.569 0.002486752 3.142974 -0.57 0.0024079 3.15161 -0.571 0.002329052 3.160258 -0.572 0.002254136 3.1688752 -0.573 0.002183592 3.1774616 -0.574 0.002116428 3.1860304 -0.575 0.002050972 3.1945952 -0.576 0.001985512 3.203172 -0.577 0.001920056 3.21176 -0.578 0.001861556 3.220274 -0.579 0.001805972 3.2287636 -0.58 0.001750388 3.237264 -0.581 0.0016948 3.245776 -0.582 0.001639216 3.2542988 -0.583 0.001583628 3.2628328 -0.584 0.0015308 3.2713376 -0.585 0.001481948 3.2797972 -0.586 0.001436244 3.28823 -0.587 0.001391268 3.2966648 -0.588 0.001346292 3.3051108 -0.589 0.001301316 3.3135672 -0.59 0.0012615 3.3219476 -0.591 0.001225072 3.3302816 -0.592 0.001188648 3.3386256 -0.593 0.001152224 3.3469804 -0.594 0.0011158 3.3553456 -0.595 0.001079376 3.3637212 -0.596 0.001044256 3.3720832 -0.597 0.001011672 3.3804044 -0.598 0.00098146 3.3886976 -0.599 0.00095134 3.3969996 -0.6 0.00092122 3.4053116 -0.601 0.000891104 3.413634 -0.602 0.00086322 3.421912 -0.603 0.000837556 3.4301468 -0.604 0.000811896 3.4383912 -0.605 0.000786232 3.4466456 -0.606 0.000760568 3.4549096 -0.607 0.000734908 3.4631836 -0.608 0.000710156 3.471446 -0.609 0.000687556 3.4796592 -0.61 0.000666728 3.4878416 -0.611 0.000645904 3.4960336 -0.612 0.000625076 3.5042352 -0.613 0.000604248 3.5124464 -0.614 0.00058482 3.520622 -0.615 0.000567492 3.52874 -0.616 0.000550164 3.5368672 -0.617 0.000532836 3.545004 -0.618 0.000515508 3.55315 -0.619 0.00049818 3.5613052 -0.62 0.000481528 3.5694472 -0.621 0.000466104 3.5775532 -0.622 0.000451868 3.585628 -0.623 0.000437628 3.5937124 -0.624 0.000423392 3.6018056 -0.625 0.000409152 3.6099076 -0.626 0.000395568 3.6179912 -0.627 0.000383512 3.626018 -0.628 0.000371456 3.6340536 -0.629 0.000359404 3.642098 -0.63 0.000347348 3.6501512 -0.631 0.000335296 3.6582136 -0.632 0.00032376 3.6662608 -0.633 0.000312964 3.6742812 -0.634 0.00030304 3.6822656 -0.635 0.000293116 3.6902588 -0.636 0.000283192 3.6982608 -0.637 0.000273268 3.7062712 -0.638 0.00026372 3.7142672 -0.639 0.000255728 3.7221768 -0.64 0.000247736 3.7300948 -0.641 0.000239744 3.7380216 -0.642 0.000231752 3.7459564 -0.643 0.000223764 3.7539 -0.644 0.00021622 3.7618236 -0.645 0.000209148 3.7697272 -0.646 0.0002027 3.7775924 -0.647 0.000196256 3.7854652 -0.648 0.000189808 3.7933468 -0.649 0.000183364 3.801236 -0.65 0.000177028 3.8091236 -0.651 0.000171664 3.8169308 -0.652 0.000166312 3.8247452 -0.653 0.000160956 3.832568 -0.654 0.0001556 3.8403984 -0.655 0.000150248 3.8482368 -0.656 0.00014522 3.856054 -0.657 0.000140504 3.8638532 -0.658 0.000136108 3.8716252 -0.659 0.000131768 3.8793976 -0.66 0.000127428 3.8871776 -0.661 0.000123092 3.8949656 -0.662 0.00011878 3.9027576 -0.663 0.000115184 3.9104616 -0.664 0.000111624 3.9181692 -0.665 0.000108064 3.925884 -0.666 0.000104504 3.9336064 -0.667 0.00010094 3.9413368 -0.668 0.000097644 3.9490404 -0.669 0.0000946 3.956722 -0.67 0.000091676 3.9643916 -0.671 0.000088836 3.9720532 -0.672 0.000085996 3.9797224 -0.673 0.000083156 3.9873988 -0.674 0.000080316 3.9950828 -0.675 0.000077856 4.002704 -0.676 0.000075444 4.01032 -0.677 0.000073032 4.017944 -0.678 0.00007062 4.02558 -0.679 0.00006822 4.033216 -0.68 0.000065956 4.040836 -0.681 0.000063852 4.048436 -0.682 0.000061768 4.056036 -0.683 0.000059776 4.06362 -0.684 0.000057788 4.071212 -0.685 0.000055796 4.078812 -0.686 0.000053804 4.086416 -0.687 0.000052084 4.09396 -0.688 0.000050436 4.101492 -0.689 0.000048784 4.109028 -0.69 0.000047136 4.116576 -0.691 0.000045508 4.12412 -0.692 0.000043992 4.131648 -0.693 0.000042584 4.139156 -0.694 0.00004118 4.146668 -0.695 0.00003984 4.154164 -0.696 0.000038512 4.161664 -0.697 0.00003718 4.169168 -0.698 0.000035852 4.17668 -0.699 0.000034672 4.184144 -0.7 0.000033564 4.191588 -0.701 0.000032456 4.19904 -0.702 0.000031348 4.2065 -0.703 0.000030256 4.21396 -0.704 0.000029244 4.2214 -0.705 0.000028284 4.228828 -0.706 0.000027328 4.236264 -0.707 0.0000264 4.243688 -0.708 0.000025488 4.251112 -0.709 0.00002458 4.258536 -0.71 0.000023668 4.265972 -0.711 0.000022856 4.27336 -0.712 0.000022112 4.28072 -0.713 0.000021364 4.288084 -0.714 0.00002062 4.295456 -0.715 0.000019896 4.302824 -0.716 0.000019244 4.310164 -0.717 0.000018628 4.317492 -0.718 0.000018016 4.324824 -0.719 0.000017412 4.332156 -0.72 0.000016828 4.339476 -0.721 0.000016248 4.3468 -0.722 0.000015664 4.354136 -0.723 0.000015132 4.361432 -0.724 0.000014652 4.3687 -0.725 0.000014168 4.375968 -0.726 0.000013688 4.383248 -0.727 0.00001322 4.390516 -0.728 0.000012796 4.397764 -0.729 0.000012388 4.405008 -0.73 0.000011976 4.412256 -0.731 0.000011572 4.419508 -0.732 0.000011184 4.426744 -0.733 0.000010796 4.433984 -0.734 0.000010408 4.441232 -0.735 0.000010044 4.448452 -0.736 0.000009724 4.455632 -0.737 0.000009404 4.46282 -0.738 0.000009084 4.470012 -0.739 0.000008776 4.477192 -0.74 0.0000085 4.484348 -0.741 0.000008232 4.491504 -0.742 0.000007964 4.498664 -0.743 0.000007696 4.505828 -0.744 0.000007436 4.51298 -0.745 0.000007184 4.520128 -0.746 0.000006928 4.527284 -0.747 0.000006684 4.534428 -0.748 0.000006476 4.541524 -0.749 0.000006264 4.548624 -0.75 0.000006052 4.555728 -0.751 0.000005848 4.562828 -0.752 0.000005668 4.569904 -0.753 0.000005484 4.57698 -0.754 0.000005304 4.584064 -0.755 0.000005124 4.591152 -0.756 0.000004944 4.598236 -0.757 0.000004772 4.605312 -0.758 0.0000046 4.612396 -0.759 0.000004432 4.619472 -0.76 0.000004288 4.626488 -0.761 0.000004148 4.633512 -0.762 0.000004004 4.64054 -0.763 0.000003872 4.647552 -0.764 0.000003752 4.654544 -0.765 0.000003632 4.66154 -0.766 0.000003512 4.66854 -0.767 0.000003392 4.675548 -0.768 0.000003272 4.682556 -0.769 0.00000316 4.689548 -0.77 0.000003048 4.696548 -0.771 0.000002936 4.703544 -0.772 0.00000284 4.710484 -0.773 0.000002744 4.717428 -0.774 0.000002652 4.724376 -0.775 0.000002564 4.731308 -0.776 0.000002484 4.73822 -0.777 0.000002404 4.74514 -0.778 0.000002328 4.752064 -0.779 0.000002248 4.758992 -0.78 0.000002168 4.765928 -0.781 0.000002092 4.772848 -0.782 0.000002016 4.779772 -0.783 0.00000194 4.786696 -0.784 0.000001876 4.793568 -0.785 0.000001816 4.80044 -0.786 0.000001752 4.807316 -0.787 0.000001692 4.814168 -0.788 0.00000164 4.821004 -0.789 0.000001588 4.827848 -0.79 0.000001536 4.834696 -0.791 0.000001484 4.841548 -0.792 0.000001428 4.848408 -0.793 0.00000138 4.855256 -0.794 0.000001328 4.862096 -0.795 0.00000128 4.86894 -0.796 0.000001236 4.875728 -0.797 0.000001196 4.882512 -0.798 0.000001156 4.8893 -0.799 0.00000112 4.896064 -0.8 0.000001084 4.902816 -0.801 0.000001052 4.909576 -0.802 0.000001016 4.916336 -0.803 0.000000984 4.923104 -0.804 0.000000948 4.929876 -0.805 0.000000916 4.936648 -0.806 0.000000884 4.943408 -0.807 0.000000852 4.950176 -0.808 0.000000824 4.9569 -0.809 0.000000796 4.963612 -0.81 0.000000768 4.970328 -0.811 0.000000744 4.977012 -0.812 0.00000072 4.983696 -0.813 0.0000007 4.990384 -0.814 0.000000676 4.997076 -0.815 0.000000652 5.003772 -0.816 0.000000632 5.010472 -0.817 0.000000608 5.017176 -0.818 0.000000588 5.023864 -0.819 0.000000564 5.030556 -0.82 0.000000544 5.037212 -0.821 0.000000528 5.043852 -0.822 0.000000508 5.050488 -0.823 0.000000496 5.0571 -0.824 0.00000048 5.063708 -0.825 0.000000464 5.070324 -0.826 0.000000448 5.076944 -0.827 0.000000432 5.083564 -0.828 0.00000042 5.090192 -0.829 0.000000404 5.096824 -0.83 0.000000388 5.103448 -0.831 0.000000376 5.110068 -0.832 0.00000036 5.116664 -0.833 0.000000348 5.123236 -0.834 0.000000336 5.129812 -0.835 0.000000328 5.136356 -0.836 0.000000316 5.142908 -0.837 0.000000304 5.14946 -0.838 0.000000296 5.156016 -0.839 0.000000284 5.16258 -0.84 0.000000276 5.169144 -0.841 0.000000264 5.175716 -0.842 0.000000256 5.182276 -0.843 0.000000244 5.188832 -0.844 0.000000236 5.195356 -0.845 0.000000228 5.201844 -0.846 0.00000022 5.208328 -0.847 0.000000216 5.21478 -0.848 0.000000208 5.221236 -0.849 0.0000002 5.2277 -0.85 0.000000196 5.234164 -0.851 0.000000188 5.240632 -0.852 0.000000184 5.247108 -0.853 0.000000176 5.253584 -0.854 0.000000172 5.26006 -0.855 0.000000164 5.266532 -0.856 0.00000016 5.272984 -0.857 0.000000152 5.279408 -0.858 0.000000148 5.28582 -0.859 0.000000144 5.292216 -0.86 0.00000014 5.298612 -0.861 0.000000136 5.305016 -0.862 0.000000132 5.311424 -0.863 0.000000128 5.317832 -0.864 0.00000012 5.324248 -0.865 0.000000116 5.330664 -0.866 0.000000112 5.337088 -0.867 0.000000108 5.343496 -0.868 0.000000104 5.349892 -0.869 0.0000001 5.356248 -0.87 0.000000096 5.362592 -0.871 0.000000096 5.368916 -0.872 0.000000092 5.375248 -0.873 0.000000088 5.38158 -0.874 0.000000088 5.38792 -0.875 0.000000084 5.39426 -0.876 0.00000008 5.400608 -0.877 0.000000076 5.406956 -0.878 0.000000076 5.413308 -0.879 0.000000072 5.419656 -0.88 0.000000068 5.425988 -0.881 0.000000068 5.432288 -0.882 0.000000064 5.438568 -0.883 0.000000064 5.444844 -0.884 0.00000006 5.45112 -0.885 0.00000006 5.4574 -0.886 0.000000056 5.463684 -0.887 0.000000056 5.469968 -0.888 0.000000052 5.47626 -0.889 0.000000052 5.482556 -0.89 0.000000048 5.488852 -0.891 0.000000048 5.495148 -0.892 0.000000044 5.501428 -0.893 0.000000044 5.507656 -0.894 0.00000004 5.513856 -0.895 0.00000004 5.520048 -0.896 0.00000004 5.526244 -0.897 0.00000004 5.532444 -0.898 0.000000036 5.538644 -0.899 0.000000036 5.544852 -0.9 0.000000036 5.55106 -0.901 0.000000032 5.557276 -0.902 0.000000032 5.563492 -0.903 0.000000032 5.569712 -0.904 0.000000028 5.575916 -0.905 0.000000028 5.582076 -0.906 0.000000028 5.588204 -0.907 0.000000028 5.594328 -0.908 0.000000028 5.60046 -0.909 0.000000024 5.606592 -0.91 0.000000024 5.612728 -0.911 0.000000024 5.618868 -0.912 0.000000024 5.625008 -0.913 0.000000024 5.631156 -0.914 0.00000002 5.637304 -0.915 0.00000002 5.64346 -0.916 0.00000002 5.6496 -0.917 0.00000002 5.655692 -0.918 0.00000002 5.661744 -0.919 0.000000016 5.667796 -0.92 0.000000016 5.673856 -0.921 0.000000016 5.679912 -0.922 0.000000016 5.685976 -0.923 0.000000016 5.692044 -0.924 0.000000016 5.698112 -0.925 0.000000016 5.704184 -0.926 0.000000016 5.71026 -0.927 0.000000012 5.71634 -0.928 0.000000012 5.722412 -0.929 0.000000012 5.728444 -0.93 0.000000012 5.73444 -0.931 0.000000012 5.740444 -0.932 0.000000012 5.746444 -0.933 0.000000012 5.752452 -0.934 0.000000012 5.75846 -0.935 0.000000012 5.764472 -0.936 0.000000012 5.770488 -0.937 0.000000008 5.776508 -0.938 0.000000008 5.782532 -0.939 0.000000008 5.788556 -0.94 0.000000008 5.79458 -0.941 0.000000008 5.800556 -0.942 0.000000008 5.806488 -0.943 0.000000008 5.812428 -0.944 0.000000008 5.818368 -0.945 0.000000008 5.824312 -0.946 0.000000008 5.830256 -0.947 0.000000008 5.836208 -0.948 0.000000008 5.84216 -0.949 0.000000008 5.848116 -0.95 0.000000008 5.854072 -0.951 0.000000008 5.860036 -0.952 0.000000004 5.866 -0.953 0.000000004 5.871916 -0.954 0.000000004 5.877792 -0.955 0.000000004 5.883672 -0.956 0.000000004 5.889556 -0.957 0.000000004 5.89544 -0.958 0.000000004 5.901328 -0.959 0.000000004 5.90722 -0.96 0.000000004 5.913116 -0.961 0.000000004 5.919012 -0.962 0.000000004 5.924912 -0.963 0.000000004 5.930816 -0.964 0.000000004 5.93672 -0.965 0.000000004 5.94258 -0.966 0.000000004 5.948388 -0.967 0.000000004 5.9542 -0.968 0.000000004 5.960012 -0.969 0.000000004 5.965828 -0.97 0.000000004 5.971644 -0.971 0.000000004 5.977468 -0.972 0.000000004 5.983292 -0.973 0.000000004 5.98912 -0.974 0.000000004 5.994948 -0.975 0.000000004 6.00078 -0.976 0.000000004 6.006616 -0.977 0.000000004 6.01242 -0.978 0.000000004 6.018168 -0.979 0.000000004 6.023924 -0.98 0.000000004 6.02968 -0.981 0.000000004 6.03544 -0.982 0.000000004 6.041204 -0.983 0.000000004 6.046968 -0.984 0.000000004 6.052736 -0.985 0.000000004 6.058504 -0.986 0 6.06428 -0.987 0 6.070056 -0.988 0 6.075832 -0.989 0 6.08158 -0.99 0 6.087272 -0.991 0 6.092968 -0.992 0 6.098664 -0.993 0 6.104364 -0.994 0 6.110068 -0.995 0 6.115772 -0.996 0 6.12148 -0.997 0 6.127192 -0.998 0 6.132904 -0.999 0 6.13862 -1 0 6.144336 From b6d0285f91a7b1d02efcce977d3030afba13b63c Mon Sep 17 00:00:00 2001 From: Geordie Rose Date: Mon, 14 Jul 2025 12:31:25 -0700 Subject: [PATCH 49/59] added vertex_ownership and graphs 19, 20 --- .../utils/game_graph_properties.py | 34 +++++++++++++++++++ 1 file changed, 34 insertions(+) diff --git a/tangled_adjudicate/utils/game_graph_properties.py b/tangled_adjudicate/utils/game_graph_properties.py index f03ce06..a7cd6b9 100644 --- a/tangled_adjudicate/utils/game_graph_properties.py +++ b/tangled_adjudicate/utils/game_graph_properties.py @@ -13,6 +13,25 @@ def __init__(self, graph_number): # to add a new graph, simply define a new graph_number (say 17) and provide its vertex_count and edge_list # following the pattern here. + self.vertex_ownership = {2: (0, 1), + 3: (0, 2), + 4: (0, 3), + 5: (5, 7), + 6: (14, 10), + 7: (1, 4), + 8: (0, 11), + 10: (0, 23), + 11: (0, 2), + 12: (2, 4), + 13: (0, 1), + 14: (7, 16), + 15: (1, 4), + 16: (136, 31), + 17: (0, 7), + 18: (0, 4), + 19: (0, 4), + 20: (0, 2)} + if graph_number == 1: # K_2, complete graph on 2 vertices, 1 edge self.vertex_count = 2 @@ -302,6 +321,21 @@ def __init__(self, graph_number): (3, 4), (3, 5), (4, 5)] + elif graph_number == 19: # barbell graph; 6 vertices, 7 edges + + self.vertex_count = 6 + + self.edge_list = [(0, 1), (0, 2), + (1, 2), + (2, 5), + (3, 4), (3, 5), + (4, 5)] + + elif graph_number == 20: # diamond graph; 4 vertices, 5 edges + + self.vertex_count = 4 + self.edge_list = [(0, 1), (0, 3), (1, 2), (1, 3), (2, 3)] + else: print('Bad graph_number in GraphProperties initialization -- no graph corresponding to your choice exists.') From 9b9e1dc95a6458ced1ff6961eab545dd9284cff9 Mon Sep 17 00:00:00 2001 From: Geordie Rose Date: Mon, 14 Jul 2025 12:33:35 -0700 Subject: [PATCH 50/59] changed default grid_size to 12 --- tangled_adjudicate/utils/find_hardware_embeddings.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tangled_adjudicate/utils/find_hardware_embeddings.py b/tangled_adjudicate/utils/find_hardware_embeddings.py index b27eea9..08820e6 100644 --- a/tangled_adjudicate/utils/find_hardware_embeddings.py +++ b/tangled_adjudicate/utils/find_hardware_embeddings.py @@ -161,7 +161,7 @@ def get_embeddings(source_graph_number, qc_solver_to_use, data_dir): # these parameters seem to work well to get a lot of embeddings, you can try to change them if you want raster_breadth = 2 - grid_size = 6 + grid_size = 12 file_name = ('embeddings_graph_number_' + str(source_graph_number) + '_raster_breadth_' + str(raster_breadth) + '_gridsize_' + str(grid_size) + '_qc_' + qc_solver_to_use + '.pkl') From 8116042ab3dde85673d84fd2706b9819c1efa7a4 Mon Sep 17 00:00:00 2001 From: Geordie Rose Date: Mon, 14 Jul 2025 14:47:09 -0700 Subject: [PATCH 51/59] generates unique terminal states for both fixed and free variants --- .../utils/generate_terminal_states.py | 265 +++++++++++++----- 1 file changed, 202 insertions(+), 63 deletions(-) diff --git a/tangled_adjudicate/utils/generate_terminal_states.py b/tangled_adjudicate/utils/generate_terminal_states.py index 39f8bee..495a5a3 100644 --- a/tangled_adjudicate/utils/generate_terminal_states.py +++ b/tangled_adjudicate/utils/generate_terminal_states.py @@ -5,27 +5,96 @@ import pickle import math import ast +import time + +from tqdm import tqdm import numpy as np +from collections import defaultdict from tangled_adjudicate.utils.game_graph_properties import GraphProperties from tangled_adjudicate.utils.find_graph_automorphisms import get_automorphisms from tangled_adjudicate.utils.utilities import convert_my_game_state_to_erik_game_state +def extract_unique_terminal_states_for_fixed_vertices(graph_number): + + ############################################################################################################# + # output file is a dictionary with unique states as keys, and values are lists of states that are equivalent; + # if there are no equivalent states, this list is empty + ############################################################################################################# + + graph = GraphProperties(graph_number) + + script_dir = os.path.dirname(os.path.abspath(__file__)) # Get the directory of the current script + data_dir = os.path.join(script_dir, '..', 'data') + + file_path = os.path.join(data_dir, "graph_" + str(graph_number) + "_unique_terminal_states.pkl") + + with open(file_path, "rb") as fp: + game_states = pickle.load(fp) + + file_path = os.path.join(data_dir, "graph_" + str(graph_number) + "_unique_terminal_states_fixed_vertices.pkl") + + user_input = None + + if os.path.isfile(file_path): # if the file already exists, just load it + user_input = input("graph_" + str(graph_number) + "_unique_terminal_states_fixed_vertices.pkl already exists, overwrite (y/n)?") + + if user_input is None or user_input.lower() == 'y': + unique_states = {} + for k, v in tqdm(game_states.items(), total=len(game_states)): + key_to_use = None + for each in v['automorphisms']: # each is a string like '[0,1,2,1,1,1]' + possible_state = ast.literal_eval(each) # possible_state is a list like [0,1,2,1,1,1] + if possible_state[graph.vertex_ownership[graph_number][0]] == 1 and possible_state[graph.vertex_ownership[graph_number][1]] == 2: + if key_to_use is None: + key_to_use = str(possible_state) + unique_states[key_to_use] = [] + unique_states[key_to_use].append(str(possible_state)) + if key_to_use is not None: + my_set = set(unique_states[key_to_use]) + my_set.discard(key_to_use) + unique_states[key_to_use] = list(my_set) + + print('there are', len(unique_states), 'unique states...') + + with open(file_path, "wb") as fp: + pickle.dump(unique_states, fp) + + def generate_all_tangled_terminal_states(graph_number): # this loads or generates all possible terminal game states for the graph indexed by graph_number and groups them # into lists where each member of the list is connected by an automorphism. Running this function requires either - # loading or generating an automorphism file. The dictionary game_states has as its key a string with the canonical - # member of each of these, with the further ['automorphisms'] key being a list of all the states that are symmetries + # loading or generating an automorphism file. + # + ####################################################################################################### + # The dictionary game_states has as its key a string with the canonical member of each of these, + # with the further ['automorphisms'] key being a list of all the states that are symmetries # of the canonical key. The key ['game_state'] is the representation of the key as a game_state object. + ####################################################################################################### # # Note that this requires enumerating all possible terminal states, the number of which is - # (vertex_count choose 2) * 2 * 3**edge_count, which grows exponentially with edge count. You can do this easily - # for graph_number 1, 2, 3, 4, but 5 and up get stupidly large. + # (vertex_count choose 2) * 2 * 3**edge_count, which grows exponentially with edge count. # # graph_number 2 should have 27 keys, and each ['automorphisms'] sub-key should have 6 entries # graph_number 3 should have 405 keys, and each ['automorphisms'] sub-key should have 12-24 entries (the reason # why there aren't always 24 is that for some of these keys different automorphisms bring you to the same state) + # graph_number 20 should have 756 keys + # graph_number 4 should have 1,836 keys + # graph_number 19 should have 10,449 keys + # graph_number 18 should have 49,572 keys + # graph_number 17 should have 623,322 keys + # graph_number 12 should have 1,030,077 keys + # + # for the fixed_vertices variant: + # graph_number 2 has 27 unique states + # graph_number 20 has 135 unique states + # graph_number 3 has 405 unique states + # graph_number 4 has 378 unique states + # graph_number 19 has 2,187 unique states + # graph_number 18 has 19,683 unique states + # graph_number 17 has 89,694 unique states + # graph_number 12 has 177,147 unique states graph = GraphProperties(graph_number) @@ -35,10 +104,12 @@ def generate_all_tangled_terminal_states(graph_number): file_path = os.path.join(data_dir, "graph_" + str(graph_number) + "_unique_terminal_states.pkl") - if os.path.isfile(file_path): # if the file already exists, just load it - with open(file_path, "rb") as fp: - game_states = pickle.load(fp) - else: + user_input = None + + if os.path.isfile(file_path): # file already exists + user_input = input("graph_" + str(graph_number) + "_unique_terminal_states.pkl already exists, overwrite (y/n)?") + + if user_input is None or user_input.lower() == 'y': # add check to make sure you don't ask for something too large print('***************************') user_input = input('There are ' + str(math.comb(graph.vertex_count, 2) * 2 * 3**graph.edge_count) + @@ -47,100 +118,168 @@ def generate_all_tangled_terminal_states(graph_number): sys.exit(print('exiting...')) print('***************************') - possible_vertex_states = [] - for positions in itertools.permutations(range(graph.vertex_count), 2): - lst = [0] * graph.vertex_count - lst[positions[0]] = 1 - lst[positions[1]] = 2 - possible_vertex_states.append(lst) + start_of_this = time.time() - possible_vertex_states.sort() + # Create possible_vertex_states as numpy array + ################################################################################################# + vertex_permutations = list(itertools.permutations(range(graph.vertex_count), 2)) + possible_vertex_states = np.zeros((len(vertex_permutations), graph.vertex_count), dtype=np.uint8) - elements = [1, 2, 3] - possible_edge_states = list(itertools.product(elements, repeat=graph.edge_count)) + for i, positions in enumerate(vertex_permutations): + possible_vertex_states[i, positions[0]] = 1 + possible_vertex_states[i, positions[1]] = 2 + # Sort the array lexicographically + possible_vertex_states = possible_vertex_states[ + np.lexsort([possible_vertex_states[:, i] for i in range(graph.vertex_count - 1, -1, -1)])] + ################################################################################################# + + # Create possible_edge_states as numpy array + ################################################################################################# + elements = np.array([1, 2, 3], dtype=np.uint8) + edge_product = list(itertools.product(elements, repeat=graph.edge_count)) + possible_edge_states = np.array(edge_product, dtype=np.uint8) + ################################################################################################# + + # Create all_states as a single numpy array + # First determine the dimensions of the final array + num_vertex_states = possible_vertex_states.shape[0] + num_edge_states = possible_edge_states.shape[0] + total_states = num_vertex_states * num_edge_states + total_elements = graph.vertex_count + graph.edge_count + + # Pre-allocate the full array + all_states = np.zeros((total_states, total_elements), dtype=np.uint8) + + # Fill the array efficiently using broadcasting + for i, edge_state in enumerate(possible_edge_states): + start_idx = i * num_vertex_states + end_idx = start_idx + num_vertex_states + + # Copy vertex states + all_states[start_idx:end_idx, :graph.vertex_count] = possible_vertex_states + + # Set edge states (broadcasting to all corresponding vertex state combinations) + all_states[start_idx:end_idx, graph.vertex_count:] = edge_state + + ################## # all_states is a list of lists enumerating ALL the game states - all_states = [j + list(k) for k in possible_edge_states for j in possible_vertex_states] + # NOTE: this might be VERY BIG + # all_states = [j + list(k) for k in possible_edge_states for j in possible_vertex_states] # this next part creates a dictionary where the keys are each of the elements of all_states and the values are # lists of all the states connected to the key by an automorphism. Note that different automorphisms can lead # to the same state, so at some point the list is converted to a set and then back to a list + print('generating all_states took', time.time() - start_of_this, 'seconds...') + + start_of_this = time.time() all_states_with_symmetries = {} - all_states_no_symmetries = {} - # iterate over all enumerated states - for state in all_states: + # Pre-compute edge transformation lookup table for each automorphism + edge_transform_lookup = [] - # create a list for all the symmetric states - list_of_states_connected_by_symmetry = [] + # this outputs edge_transform_lookup, which is a list indexed by automorphism # that shows how the + # original graph.edge_list maps under the automorphism - # get indices of the red and blue vertices - only_vertices = state[:graph.vertex_count] - red_vertex_index = only_vertices.index(1) - blue_vertex_index = only_vertices.index(2) + for automorph in list_of_automorphisms: + edge_map_local = np.zeros(graph.edge_count, dtype=np.int32) + for edge_idx in range(graph.edge_count): + first_vertex = automorph[graph.edge_list[edge_idx][0]] + second_vertex = automorph[graph.edge_list[edge_idx][1]] + if first_vertex < second_vertex: + transformed_edge = (first_vertex, second_vertex) + else: + transformed_edge = (second_vertex, first_vertex) - # iterate over all automorphisms - for automorph in list_of_automorphisms: + transformed_edge_idx = graph.edge_list.index(transformed_edge) + # edge_map_local[edge_idx] = transformed_edge_idx + edge_map_local[transformed_edge_idx] = edge_idx + edge_transform_lookup.append(edge_map_local) - # initialize the state we want to compute (transforming state under automorph) - state_transformed_under_automorph = [0] * graph.vertex_count + # iterate over all enumerated states + for idx, state in tqdm(enumerate(all_states), total=len(all_states)): - # write transformed vertices into the transformed state -- this finishes the vertex part - state_transformed_under_automorph[automorph[red_vertex_index]] = 1 - state_transformed_under_automorph[automorph[blue_vertex_index]] = 2 + # Create a list for all states connected to state by automorphisms + list_of_states_connected_by_symmetry = [] - # now we want to transform the edges under the automorphism - for edge_idx in range(graph.edge_count): - first_vertex = automorph[graph.edge_list[edge_idx][0]] - second_vertex = automorph[graph.edge_list[edge_idx][1]] - if first_vertex < second_vertex: - transformed_edge = (first_vertex, second_vertex) - else: - transformed_edge = (second_vertex, first_vertex) + # Find indices of red and blue vertices (more efficient than .index()) + red_vertex_index = np.where(state[:graph.vertex_count] == 1)[0][0] + blue_vertex_index = np.where(state[:graph.vertex_count] == 2)[0][0] - transformed_edge_idx = graph.edge_list.index(transformed_edge) + # Iterate over all automorphisms + for i, automorph in enumerate(list_of_automorphisms): + # Initialize transformed state array + state_transformed = np.zeros(graph.vertex_count + graph.edge_count, dtype=np.uint8) - state_transformed_under_automorph.append(state[graph.vertex_count + transformed_edge_idx]) + # Transform vertices + state_transformed[automorph[red_vertex_index]] = 1 + state_transformed[automorph[blue_vertex_index]] = 2 - list_of_states_connected_by_symmetry.append(str(state_transformed_under_automorph)) + # Transform edges (using pre-computed lookup) + state_transformed[graph.vertex_count:] = state[graph.vertex_count + edge_transform_lookup[i]] - # remove duplicates - all_states_with_symmetries[str(state)] = list(dict.fromkeys(list_of_states_connected_by_symmetry)) - all_states_no_symmetries[str(state)] = list_of_states_connected_by_symmetry + # Convert to string for dictionary key + # state_transformed is an ndarray; + # list_of_states_connected_by_symmetry.append(np.array2string(state_transformed, separator=',')) + list_of_states_connected_by_symmetry.append(state_transformed.tobytes()) + all_states_with_symmetries[state.tobytes()] = list(dict.fromkeys(list_of_states_connected_by_symmetry)) + print('generating all_states_no_symmetries took', time.time() - start_of_this, 'seconds...') + start = time.time() sorted_all_states_with_symmetries = dict(sorted(all_states_with_symmetries.items())) + print('sorting took', time.time() - start, 'seconds...') uniques = [] - duplicates = [] + + start = time.time() + + duplicates = set() # Using a set for O(1) lookups for k, v in sorted_all_states_with_symmetries.items(): - if k not in duplicates: + if k not in duplicates: # O(1) lookup in a set uniques.append(k) - for j in range(1, len(v)): - duplicates.append(v[j]) - unique_terminal_states = [ast.literal_eval(k) for k in uniques] - print('there are', len(unique_terminal_states), 'unique terminal states. Writing to disk ...') + # Add all values except the first to duplicates + # This is more efficient than appending in a loop + if len(v) > 1: + duplicates.update(v[1:]) + + print('extracting uniques took', time.time() - start, 'seconds...') + + # note: uniques is a list of strings, so don't have to do ast.literal_eval thing + # unique_terminal_states = [ast.literal_eval(k) for k in uniques] + print('there are', len(uniques), 'unique terminal states. Writing to disk ...') game_states = {} - for my_game_state in unique_terminal_states: - game_states[str(my_game_state)] = {} - game_states[str(my_game_state)]['game_state'] = convert_my_game_state_to_erik_game_state(my_game_state, graph.vertex_count, graph.edge_list) - game_states[str(my_game_state)]['automorphisms'] = all_states_with_symmetries[str(my_game_state)] + reconstructed = ['['+', '.join(map(str, np.frombuffer(each, dtype=np.uint8).reshape((graph.vertex_count + graph.edge_count,)))) + ']' for each in uniques] + + for idx in range(len(reconstructed)): + game_states[reconstructed[idx]] = {} + game_states[reconstructed[idx]]['game_state'] = convert_my_game_state_to_erik_game_state(reconstructed[idx], graph.vertex_count, graph.edge_list) + game_states[reconstructed[idx]]['automorphisms'] = ['['+', '.join(map(str, np.frombuffer(each, dtype=np.uint8).reshape((graph.vertex_count + graph.edge_count,)))) + ']' for each in all_states_with_symmetries[uniques[idx]]] with open(os.path.join(data_dir, "graph_" + str(graph_number) + "_unique_terminal_states.pkl"), "wb") as fp: pickle.dump(game_states, fp) - return game_states - def main(): - # this generates all terminal states for graphs 2 and 3 - gs2 = generate_all_tangled_terminal_states(graph_number=2) - gs3 = generate_all_tangled_terminal_states(graph_number=3) + graph_number = 3 # graph 11 is the RSG/P_3; graph 2 is K_3; graph 3 is K_4 + + ################################################################ + # Generate all unique terminal states for the graph_number given + ################################################################ + # writes out "graph_" + str(graph_number) + "_unique_terminal_states.pkl" to disk + generate_all_tangled_terminal_states(graph_number=graph_number) + + ########################################################################################## + # Generates all unique terminal states for the graph_number given for fixed vertex variant + ########################################################################################## + # reads "graph_" + str(graph_number) + "_unique_terminal_states.pkl" + # writes "graph_" + str(graph_number) + "_unique_terminal_states_fixed_vertices.pkl" to disk + extract_unique_terminal_states_for_fixed_vertices(graph_number=graph_number) if __name__ == "__main__": From 1e9ae76393356a1a2b9a321449cf8640458f59bb Mon Sep 17 00:00:00 2001 From: Geordie Rose Date: Mon, 14 Jul 2025 17:09:56 -0700 Subject: [PATCH 52/59] removed cruft --- .../utils/adjudicate_all_terminal_states.py | 242 ++++++++++++++---- 1 file changed, 195 insertions(+), 47 deletions(-) diff --git a/tangled_adjudicate/utils/adjudicate_all_terminal_states.py b/tangled_adjudicate/utils/adjudicate_all_terminal_states.py index a9c749d..e78c1bd 100644 --- a/tangled_adjudicate/utils/adjudicate_all_terminal_states.py +++ b/tangled_adjudicate/utils/adjudicate_all_terminal_states.py @@ -1,56 +1,100 @@ -""" generate and adjudicate all Tangled terminal states for tiny graphs """ +""" adjudicate all Tangled terminal states for small graphs """ import sys import os import time import pickle +import cProfile +import pstats +from concurrent.futures.process import ProcessPoolExecutor +import multiprocessing as mp + import numpy as np +from tqdm import tqdm from tangled_adjudicate.adjudicators.simulated_annealing import SimulatedAnnealingAdjudicator from tangled_adjudicate.adjudicators.quantum_annealing import QuantumAnnealingAdjudicator -from tangled_adjudicate.adjudicators.lookup_table import LookupTableAdjudicator from tangled_adjudicate.adjudicators.schrodinger import SchrodingerEquationAdjudicator -from tangled_adjudicate.utils.generate_terminal_states import generate_all_tangled_terminal_states +from tangled_adjudicate.utils.game_graph_properties import GraphProperties +from tangled_adjudicate.utils.utilities import convert_my_game_state_to_erik_game_state -def generate_adjudication_results_for_all_terminal_states(graph_number, solver_to_use): - # uses up to three different adjudicators provided to evaluate all unique terminal states for tiny graphs - # (in the default here, graphs 2 and 3). Note this only works for tiny graphs as the number of terminal states - # grows like 3 ** edge_count. - # solver_to_use is a string, one of 'simulated_annealing', 'schrodinger_equation', or 'quantum_annealing' - # the results are stored in a dictionary whose keys are the solvers. When you call this function using a solver - # that hasn't been called yet, it adds that key and its results. If you call it in a case where there are - # already results, it will ask you if you want to overwrite them. +def safe_nested_access_and_create(d, keys, default_value=None): + """ + Safely access a nested dictionary and create missing keys. - if solver_to_use not in ['simulated_annealing', 'schrodinger_equation', 'quantum_annealing', 'lookup_table']: - sys.exit(print('the solver' + solver_to_use + 'is not in the allowed list -- please take a look!')) + Args: + d: The dictionary to access/modify + keys: List of keys to navigate through + default_value: Value to set if the full path doesn't exist - precision_digits = 4 # just to clean up print output - np.set_printoptions(suppress=True) # remove scientific notation + Returns: + The value at the nested location, or default_value if created + """ + if not keys: + return d - adjudicator = None + current = d + for i, key in enumerate(keys[:-1]): + # If we're not at the final key, we need to ensure the path exists + if key not in current or not isinstance(current[key], dict): + current[key] = {} + current = current[key] - args = {'data_dir': os.path.join(os.getcwd(), '..', 'data'), - 'graph_number': graph_number} + # For the final key, get or set the value + last_key = keys[-1] + if last_key not in current: + current[last_key] = default_value - if solver_to_use == 'simulated_annealing': - adjudicator = SimulatedAnnealingAdjudicator() + return current[last_key] + + +def parallel_generate_adjudication_results(graph_number, solver_to_use, params, variant='fixed'): + ############################################################################################################ + # output file is + # os.path.join(data_dir, "graph_" + str(graph_number) + "_adjudicated_unique_terminal_states_for_paper.pkl") + ############################################################################################################ + # + # it stores a dict adjudication_results[first_key]['simulated_annealing'][num_reads] = results + # adjudication_results[first_key]['schrodinger_equation'][anneal_time] = results + # adjudication_results[first_key]['quantum_annealing'][num_reads][anneal_time] = results + # first_key is one of 'fixed', 'free' + # num_reads, anneal_time are ints + + # solver_to_use is a string, one of 'simulated_annealing', 'schrodinger_equation', 'quantum_annealing'. + # When you call this function using a solver / parameter that hasn't been called yet, it adds that key and its + # results. If you call it in a case where there are already results, it will ask you if you want to overwrite them. + + graph = GraphProperties(graph_number) + + script_dir = os.path.dirname(os.path.abspath(__file__)) # Get the directory of the current script + data_dir = os.path.join(script_dir, '..', 'data') + + # load in already computed lists of unique terminal states -- free and fixed are different files + if variant == 'fixed': + file_path = os.path.join(data_dir, "graph_" + str(graph_number) + "_unique_terminal_states_fixed_vertices.pkl") else: - if solver_to_use == 'quantum_annealing': - adjudicator = QuantumAnnealingAdjudicator() + if variant == 'free': + file_path = os.path.join(data_dir, "graph_" + str(graph_number) + "_unique_terminal_states.pkl") else: - if solver_to_use == 'lookup_table': - adjudicator = LookupTableAdjudicator() - else: - if solver_to_use == 'schrodinger_equation': - adjudicator = SchrodingerEquationAdjudicator() + sys.exit(print('variant needs to be either fixed or free!')) - adjudicator.setup(**args) + if os.path.exists(file_path): + with open(file_path, "rb") as fp: + game_states = pickle.load(fp) + else: + sys.exit(print('you need to run generate_terminal_states.py first!')) - game_states = generate_all_tangled_terminal_states(graph_number) + if solver_to_use not in ['simulated_annealing', 'schrodinger_equation', 'quantum_annealing']: + sys.exit(print('the solver' + solver_to_use + 'is not in the allowed list -- please take a look!')) + + precision_digits = 4 # just to clean up print output + np.set_printoptions(suppress=True) # remove scientific notation + + args = {'data_dir': os.path.join(os.getcwd(), '..', 'data'), + 'graph_number': graph_number} - data_dir = os.path.join(os.getcwd(), '..', 'data') - file_path = os.path.join(data_dir, "graph_" + str(graph_number) + "_terminal_states_adjudication_results.pkl") + file_path = os.path.join(data_dir, "graph_" + str(graph_number) + "_adjudicated_unique_terminal_states_for_paper.pkl") if os.path.isfile(file_path): with open(file_path, "rb") as fp: @@ -59,37 +103,141 @@ def generate_adjudication_results_for_all_terminal_states(graph_number, solver_t adjudication_results = {} # at this point, either we have loaded some adjudication_results from an existing file, or we have a new empty dict - if solver_to_use in adjudication_results: # this means we loaded this in already - user_input = input('results already exist for ' + solver_to_use + ', overwrite (y/n)?') - if user_input.lower() != 'y': - return None + if variant in adjudication_results: # this means we loaded this in already + if solver_to_use in adjudication_results[variant]: + if solver_to_use in ['simulated_annealing']: + if params['num_reads'] in adjudication_results[variant][solver_to_use]: + user_input = ( + input('results already exist for ' + variant + " " + + solver_to_use + str(params['num_reads']) + ' , overwrite (y/n)?')) + if user_input.lower() != 'y': + return None + if solver_to_use in ['quantum_annealing']: + if params['num_reads'] in adjudication_results[variant][solver_to_use]: + if params['anneal_time'] in adjudication_results[variant][solver_to_use][params['num_reads']]: + user_input = input('results already exist for ' + variant + " " + solver_to_use + str(params['num_reads']) + str(params['anneal_time']) + ', overwrite (y/n)?') + if user_input.lower() != 'y': + return None + if solver_to_use in ['schrodinger_equation']: + if params['anneal_time'] in adjudication_results[variant][solver_to_use]: + user_input = input('results already exist for ' + variant + " " + solver_to_use + str(params['anneal_time']) + ' , overwrite (y/n)?') + if user_input.lower() != 'y': + return None + + # initialize adjudicator + adjudicator = None + + # this should modify adjudication_results in place to add the new key without changing anything else + if solver_to_use == 'simulated_annealing': + adjudicator = SimulatedAnnealingAdjudicator() + args.update({'num_reads': params['num_reads']}) + safe_nested_access_and_create(adjudication_results, + [variant, solver_to_use, params['num_reads']], default_value={}) + else: + if solver_to_use == 'quantum_annealing': + adjudicator = QuantumAnnealingAdjudicator() + args.update({'num_reads': params['num_reads'], 'anneal_time': params['anneal_time']}) + safe_nested_access_and_create(adjudication_results, + [variant, solver_to_use, params['num_reads'], params['anneal_time']], + default_value={}) + else: + if solver_to_use == 'schrodinger_equation': + adjudicator = SchrodingerEquationAdjudicator() + args.update({'anneal_time': params['anneal_time']}) + safe_nested_access_and_create(adjudication_results, + [variant, solver_to_use, params['anneal_time']], default_value={}) + + adjudicator.setup(**args) # now we proceed to compute and store result - print('beginning adjudication using the ' + solver_to_use + ' solver...') - start = time.time() - adjudication_results[solver_to_use] = {} + print('beginning adjudication using the ' + solver_to_use + ' solver with parameters ' + str(args)) + start_adj = time.time() - for k, v in game_states.items(): - adjudication_results[solver_to_use][k] = adjudicator.adjudicate(v['game_state']) + if solver_to_use in ['quantum_annealing']: # no parallelism with quantum annealing -- one at a time + items = list(game_states.items()) + for item in items: + k, result = process_game_state(item, variant, graph, adjudicator) + adjudication_results[variant][solver_to_use][params['num_reads']][params['anneal_time']][k] = result - print('elapsed time was', round(time.time() - start, precision_digits), 'seconds.') + else: + max_workers = 18 # parallelism with simulated annealing or schrodinger equation + with ProcessPoolExecutor(max_workers=max_workers) as executor: + # Create a list of argument tuples for each game state + items = list(game_states.items()) + + # Submit all tasks and track with tqdm + futures = [] + for item in items: + future = executor.submit(process_game_state, item, variant, graph, adjudicator) + futures.append(future) + + # Process results as they complete + for future in tqdm(futures, total=len(futures)): + k, result = future.result() + if solver_to_use == 'simulated_annealing': + adjudication_results[variant][solver_to_use][params['num_reads']][k] = result + else: + if solver_to_use == 'schrodinger_equation': + adjudication_results[variant][solver_to_use][params['anneal_time']][k] = result + + print('elapsed time was', round(time.time() - start_adj, precision_digits), 'seconds.') # store it -- this should leave any previously loaded solver results intact with open(file_path, "wb") as fp: pickle.dump(adjudication_results, fp) +def process_game_state(item, variant, graph, adjudicator): + + k, v = item + + # the fixed variant just stores the state as keys, whereas the free version already has the game state explicit + if variant == 'fixed': + game_state = convert_my_game_state_to_erik_game_state(k, graph.vertex_count, graph.edge_list) + else: + game_state = v['game_state'] + + result = adjudicator.adjudicate(game_state) + + return k, result + + def main(): - # note: generating all schrodinger_equation adjudication results for graph 3 or bigger takes forever - # I spot checked new subclass version and all spot checks were good + # given we have computed and stored terminal states, this adjudicates all of them and stores the results + + graph_number = 11 # 19 is barbell graph; 2 is K_3; 11 is RSG + variant = 'fixed' # alternative is 'free' + + # list of solvers to use to adjudicate + solver_list = ['simulated_annealing', 'schrodinger_equation', 'quantum_annealing'] - graph_number = 2 - solver_list = ['simulated_annealing', 'schrodinger_equation', 'quantum_annealing', 'lookup_table'] + print('*******************************') + print('adjudicating all terminal states for graph_number', graph_number) + print('*******************************') + + # default solver parameters + params = {'simulated_annealing': {'num_reads': 10000}, + 'schrodinger_equation': {'anneal_time': 40}, + 'quantum_annealing': {'num_reads': 10000, 'anneal_time': 350}} # quantum_annealing uses D-Wave hardware for solver_to_use in solver_list: - generate_adjudication_results_for_all_terminal_states(graph_number, solver_to_use) + print('using', solver_to_use, 'solver ...') + parallel_generate_adjudication_results(graph_number=graph_number, + solver_to_use=solver_to_use, + params=params[solver_to_use], + variant=variant) if __name__ == "__main__": - sys.exit(main()) + mp.set_start_method("spawn", force=True) # Ensures correct behavior in PyCharm + + start = time.time() + + with cProfile.Profile() as pr: + main() + + print('total time elapsed:', time.time() - start, 'seconds...') + + stats = pstats.Stats(pr) + stats.strip_dirs().sort_stats('tottime').print_stats(10) # show top 10 results From d5acefcf7284e9ad55e797e05d409fe2b144e4ad Mon Sep 17 00:00:00 2001 From: Geordie Rose Date: Tue, 15 Jul 2025 12:53:46 -0700 Subject: [PATCH 53/59] changed to call evaluate_winner imported from utilities.py --- tangled_adjudicate/adjudicators/adjudicator.py | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) diff --git a/tangled_adjudicate/adjudicators/adjudicator.py b/tangled_adjudicate/adjudicators/adjudicator.py index 0063b8f..341d804 100644 --- a/tangled_adjudicate/adjudicators/adjudicator.py +++ b/tangled_adjudicate/adjudicators/adjudicator.py @@ -3,6 +3,8 @@ import numpy as np import numpy.typing as npt +from tangled_adjudicate.utils.utilities import evaluate_winner + class GameState(TypedDict): num_nodes: int @@ -130,12 +132,7 @@ def _compute_winner_score_and_influence( return None, None, influence_vector score = influence_vector[game_state['player1_node']] - influence_vector[game_state['player2_node']] - - if score > epsilon: - winner = 'red' - elif score < -epsilon: - winner = 'blue' - else: - winner = 'draw' + + winner = evaluate_winner(score, epsilon) return winner, score, influence_vector From 993756df7b5c4c9afcdd015c17e933ea6e1bf366 Mon Sep 17 00:00:00 2001 From: Geordie Rose Date: Tue, 15 Jul 2025 12:59:37 -0700 Subject: [PATCH 54/59] shows how to use solvers to adjudicate graphs in Phase 1 paper --- .../utils/how_to_adjudicate_states.py | 71 ++++++++++++------- 1 file changed, 45 insertions(+), 26 deletions(-) diff --git a/tangled_adjudicate/utils/how_to_adjudicate_states.py b/tangled_adjudicate/utils/how_to_adjudicate_states.py index ba07f1d..c7f8673 100644 --- a/tangled_adjudicate/utils/how_to_adjudicate_states.py +++ b/tangled_adjudicate/utils/how_to_adjudicate_states.py @@ -4,6 +4,8 @@ import time import numpy as np +from tangled_adjudicate.utils.game_graph_properties import GraphProperties + from tangled_adjudicate.adjudicators.simulated_annealing import SimulatedAnnealingAdjudicator from tangled_adjudicate.adjudicators.quantum_annealing import QuantumAnnealingAdjudicator from tangled_adjudicate.adjudicators.lookup_table import LookupTableAdjudicator @@ -12,43 +14,60 @@ def main(): # this code shows how to use the four different adjudicators - # there are two example_game_state dictionaries provided, which are terminal states in graph_number 2 and 3 - # respectively, that are of the sort that are closest to the draw line at score = +- 1/2 # set graph_number - graph_number = 2 + graph_number = 11 # 11 is P_3, 2 is K_3, 20 is diamond graph, 19 is barbell graph + + # get graph properties + graph = GraphProperties(graph_number=graph_number) - # choose solvers to use - solver_list = ['simulated_annealing', 'schrodinger_equation', 'quantum_annealing', 'lookup_table'] + # choose solver(s) to use + # solver_list = ['simulated_annealing', 'schrodinger_equation', 'lookup_table', 'quantum_annealing'] + solver_list = ['simulated_annealing', 'schrodinger_equation', 'lookup_table'] precision_digits = 4 # just to clean up print output np.set_printoptions(suppress=True) # remove scientific notation args = {'data_dir': os.path.join(os.getcwd(), '..', 'data'), - 'graph_number': graph_number} - + 'graph_number': graph_number, + 'lookup_args': {'solver': 'quantum_annealing', # one of 'simulated_annealing', 'schrodinger_equation', 'quantum_annealing'; must have already been created to use this solver + 'epsilon': 0.5, + 'anneal_time': 350, + 'num_reads': 100000}} example_game_state = None - # draw; score=0; ferromagnetic ring - if graph_number == 2: - example_game_state = {'num_nodes': 3, 'edges': [(0, 1, 2), (0, 2, 2), (1, 2, 2)], + # these are in the order introduced in the Phase 1 paper; yes, I realize my numbering scheme is ludicrous + + if graph_number == 11: # P_3 graph, S = [2,3], score = +2 (red) + example_game_state = {'num_nodes': 3, 'edges': [(0, 1, 2), (1, 2, 3)], + 'player1_id': 'player1', 'player2_id': 'player2', 'turn_count': 4, + 'current_player_index': 1, 'player1_node': graph.vertex_ownership[graph_number][0], + 'player2_node': graph.vertex_ownership[graph_number][1]} + + if graph_number == 2: # K_3 graph, S = [3,3,3], score = 0 (draw) + example_game_state = {'num_nodes': 3, 'edges': [(0, 1, 3), (0, 2, 3), (1, 2, 3)], 'player1_id': 'player1', 'player2_id': 'player2', 'turn_count': 5, - 'current_player_index': 1, 'player1_node': 1, 'player2_node': 2} - else: - # red wins, score +2/3; this is one of the states closest to the draw line - # note that quantum_annealing in this default uses the D-Wave mock software solver and won't give - # the right answer as its samples aren't unbiased -- if you want the quantum_annealing solver to - # run on hardware set QAParameters.use_mock = False in /adjudicators/quantum_annealing.py and ensure you have - # hardware access and everything is set up - - if graph_number == 3: - example_game_state = {'num_nodes': 4, 'edges': [(0, 1, 3), (0, 2, 1), (0, 3, 3), - (1, 2, 1), (1, 3, 3), (2, 3, 1)], - 'player1_id': 'player1', 'player2_id': 'player2', 'turn_count': 8, - 'current_player_index': 2, 'player1_node': 2, 'player2_node': 3} - else: - print('this introduction only has included game states for graphs 2 and 3. If you want a different' - 'graph please add a new example_game_state here!') + 'current_player_index': 1, 'player1_node': graph.vertex_ownership[graph_number][0], + 'player2_node': graph.vertex_ownership[graph_number][1]} + + if graph_number == 20: # Diamond graph; S = [1,2,2,2,3]; SA scores +4/3, QA & SE scores = +2 (red) + example_game_state = {'num_nodes': 4, 'edges': [(0, 1, 1), (0, 3, 2), + (1, 2, 2), (1, 3, 2), (2, 3, 3)], + 'player1_id': 'player1', 'player2_id': 'player2', 'turn_count': 8, + 'current_player_index': 2, 'player1_node': graph.vertex_ownership[graph_number][0], + 'player2_node': graph.vertex_ownership[graph_number][1]} + + if graph_number == 19: # Barbell graph; S =[3,2,2,3,2,3,2]; SA scores -4/9, QA & SE scores +1/2 + example_game_state = {'num_nodes': 6, 'edges': [(0, 1, 3), (0, 2, 2), (1, 2, 2), + (2, 5, 3), (3, 4, 2), (3, 5, 3), + (4, 5, 2)], + 'player1_id': 'player1', 'player2_id': 'player2', 'turn_count': 9, + 'current_player_index': 1, 'player1_node': graph.vertex_ownership[graph_number][0], + 'player2_node': graph.vertex_ownership[graph_number][1]} + + if example_game_state is None: + sys.exit(print('this introduction only includes game states for graphs 2, 11, 19, and 20. ' + 'If you want a different graph please add a new example_game_state here!')) for solver_to_use in solver_list: From 90cb899c65f77e9bef122104454fb7b622a8b5d2 Mon Sep 17 00:00:00 2001 From: Geordie Rose Date: Tue, 15 Jul 2025 13:00:33 -0700 Subject: [PATCH 55/59] modified to read/write with solver parameters --- .../adjudicators/lookup_table.py | 110 +++++++++--------- 1 file changed, 56 insertions(+), 54 deletions(-) diff --git a/tangled_adjudicate/adjudicators/lookup_table.py b/tangled_adjudicate/adjudicators/lookup_table.py index de2a49b..4853007 100644 --- a/tangled_adjudicate/adjudicators/lookup_table.py +++ b/tangled_adjudicate/adjudicators/lookup_table.py @@ -3,29 +3,31 @@ from typing import Dict, Optional import numpy as np -from ..utils.utilities import ( - convert_erik_game_state_to_my_game_state, - get_tso, - build_results_dict -) +from ..utils.utilities import convert_erik_game_state_to_my_game_state, load_lookup_table from .adjudicator import Adjudicator, GameState, AdjudicationResult class LookupTableAdjudicator(Adjudicator): - """Adjudicator implementation using pre-computed lookup tables.""" + """Adjudicator implementation using pre-computed lookup tables""" def __init__(self) -> None: - """Initialize the lookup table adjudicator.""" + """Initialize the lookup table adjudicator""" super().__init__() self.data_dir: Optional[str] = None - self.results_dict: Optional[Dict[str, str]] = None - + self.lookup_table: Optional[Dict[str, str]] = None + self.solver: Optional[str] = None + self.epsilon: Optional[float] = None + self.anneal_time: Optional[int] = None + self.num_reads: Optional[int] = None + self.graph_number: Optional[int] = None + def setup(self, **kwargs) -> None: """Configure lookup table parameters. Args: data_dir: Directory containing lookup table data files - + lookup_args: Dictionary containing solver, epsilon, anneal_time and num_reads for lookup table + graph_number: int Raises: ValueError: If parameters are invalid or data directory doesn't exist """ @@ -35,44 +37,46 @@ def setup(self, **kwargs) -> None: if not os.path.isdir(kwargs['data_dir']): raise ValueError(f"Directory not found: {kwargs['data_dir']}") self.data_dir = kwargs['data_dir'] - - self._parameters = {'data_dir': self.data_dir} - - def _load_lookup_table(self, num_nodes: int) -> None: - """Load the appropriate lookup table for the given graph size. - - Args: - num_nodes: Number of nodes in the graph - + + if 'lookup_args' in kwargs: + if kwargs['lookup_args']['solver'] not in ['simulated_annealing', 'schrodinger_equation', 'quantum_annealing']: + raise ValueError("solver must be one of 'simulated_annealing', 'schrodinger_equation', 'quantum_annealing'") + self.solver = kwargs['lookup_args']['solver'] + if not isinstance(kwargs['lookup_args']['epsilon'], float): + raise ValueError("epsilon must be a float") + self.epsilon = kwargs['lookup_args']['epsilon'] + if not isinstance(kwargs['lookup_args']['anneal_time'], int): + raise ValueError("anneal_time must be an int") + self.anneal_time = kwargs['lookup_args']['anneal_time'] + if not isinstance(kwargs['lookup_args']['num_reads'], int): + raise ValueError("num_reads must be an int") + self.num_reads = kwargs['lookup_args']['num_reads'] + + if 'graph_number' in kwargs: + if not isinstance(kwargs['graph_number'], int): + raise ValueError("graph_number must be an int") + self.graph_number = kwargs['graph_number'] + + self._parameters = {'data_dir': self.data_dir, + 'solver': self.solver, + 'epsilon': self.epsilon, + 'anneal_time': self.anneal_time, + 'num_reads': self.num_reads, + 'graph_number': self.graph_number} + + def _get_lookup_table(self) -> None: + """Load the appropriate lookup table for the given graph_number and solver_used + Raises: - ValueError: If lookup table is not available for this graph size RuntimeError: If lookup table file cannot be loaded """ - if num_nodes not in [3, 4]: - raise ValueError( - "Lookup table only available for complete graphs with 3 or 4 vertices" - ) - if not self.data_dir: raise RuntimeError("Data directory not set. Call setup() first.") - - graph_number = num_nodes - 1 # Convert from num_nodes to graph_number - file_path = os.path.join( - self.data_dir, - f'graph_{graph_number}_terminal_state_outcomes.pkl' - ) - - # Generate lookup table if it doesn't exist - if not os.path.exists(file_path): - get_tso(graph_number, file_path) - - try: - with open(file_path, 'rb') as fp: - results = pickle.load(fp) - self.results_dict = build_results_dict(results) - except Exception as e: - raise RuntimeError(f"Failed to load lookup table: {str(e)}") - + + self.lookup_table = load_lookup_table(data_dir=self.data_dir, graph_number=self.graph_number, + solver=self.solver, epsilon=self.epsilon, + anneal_time=self.anneal_time, num_reads=self.num_reads) + def adjudicate(self, game_state: GameState) -> AdjudicationResult: """Adjudicate the game state using the lookup table. @@ -89,28 +93,26 @@ def adjudicate(self, game_state: GameState) -> AdjudicationResult: self._validate_game_state(game_state) # Load lookup table if needed - if (self.results_dict is None or len(next(iter(self.results_dict.keys()))) != game_state['num_nodes']): - self._load_lookup_table(game_state['num_nodes']) + if self.lookup_table is None: + self._get_lookup_table() - if not self.results_dict: + if not self.lookup_table: raise RuntimeError("Failed to load lookup table") # Convert game state to lookup format lookup_state = convert_erik_game_state_to_my_game_state(game_state) try: - winner = self.results_dict[str(lookup_state)] - except KeyError: - raise ValueError( - f"Game state not found in lookup table: {lookup_state}" - ) - + winner = self.lookup_table[str(lookup_state)] + except KeyError: # key not in results_dict + raise RuntimeError("key not in lookup table...") + return AdjudicationResult( game_state=game_state, adjudicator='lookup_table', winner=winner, - score=None, # Lookup table doesn't provide scores - influence_vector=None, + score=None, # Lookup table doesn't provide scores; note though all these are + influence_vector=None, # available if we need them from the raw data correlation_matrix=None, parameters=self._parameters ) From 9ff33f5da1883109efd7bc50ade6044976226a89 Mon Sep 17 00:00:00 2001 From: Geordie Rose Date: Tue, 15 Jul 2025 13:01:38 -0700 Subject: [PATCH 56/59] removed mock sampler; removed exception for unused params --- .../adjudicators/quantum_annealing.py | 17 ++++------------- 1 file changed, 4 insertions(+), 13 deletions(-) diff --git a/tangled_adjudicate/adjudicators/quantum_annealing.py b/tangled_adjudicate/adjudicators/quantum_annealing.py index 08f69b0..75ad64d 100644 --- a/tangled_adjudicate/adjudicators/quantum_annealing.py +++ b/tangled_adjudicate/adjudicators/quantum_annealing.py @@ -3,7 +3,6 @@ import numpy as np from dataclasses import dataclass from dwave.system import DWaveSampler, FixedEmbeddingComposite -from dwave.system.testing import MockDWaveSampler from ..utils.find_graph_automorphisms import get_automorphisms from ..utils.find_hardware_embeddings import get_embeddings @@ -14,7 +13,7 @@ class QAParameters: """Parameters for quantum annealing.""" num_reads: int = 10000 - anneal_time: float = 40.0 # ns + anneal_time: int = 40 # ns num_chip_runs: int = 1 use_gauge_transform: bool = False use_shim: bool = False @@ -60,9 +59,7 @@ def setup(self, **kwargs) -> None: for key, value in kwargs.items(): if hasattr(self.params, key): setattr(self.params, key, value) - else: - raise ValueError(f"Unknown parameter: {key}") - + # Validate parameters if self.params.num_reads <= 0: raise ValueError("num_reads must be positive") @@ -89,14 +86,8 @@ def setup(self, **kwargs) -> None: # Initialize sampler try: - if self.params.use_mock: - base_sampler = MockDWaveSampler(topology_type='zephyr', topology_shape=[6, 4]) - else: - base_sampler = DWaveSampler(solver=self.params.solver_name) - - # Store for later use in adjudicate - self._base_sampler = base_sampler - + base_sampler = DWaveSampler(solver=self.params.solver_name) + self._base_sampler = base_sampler # Store for later use in adjudicate except Exception as e: raise RuntimeError(f"Failed to initialize D-Wave sampler: {str(e)}") From 47cc3f61500f595cc94c140ed186fc42341b084c Mon Sep 17 00:00:00 2001 From: Geordie Rose Date: Tue, 15 Jul 2025 15:29:30 -0700 Subject: [PATCH 57/59] rewrote to focus on looking for mismatches (no plots) --- .../utils/compare_adjudication_results.py | 198 ++++++++---------- 1 file changed, 87 insertions(+), 111 deletions(-) diff --git a/tangled_adjudicate/utils/compare_adjudication_results.py b/tangled_adjudicate/utils/compare_adjudication_results.py index 9ffe222..bca7c60 100644 --- a/tangled_adjudicate/utils/compare_adjudication_results.py +++ b/tangled_adjudicate/utils/compare_adjudication_results.py @@ -2,129 +2,105 @@ import sys import os import pickle -import matplotlib.pyplot as plt from itertools import combinations +from collections import defaultdict +from tangled_adjudicate.utils.utilities import evaluate_winner, load_lookup_table +from tangled_adjudicate.utils.game_graph_properties import GraphProperties -def compare_adjudication_results(graph_number, solvers_to_use): - # solvers_to_use is a list of solvers of length 2, 3, or 4 comprising 2, 3, or 4 of - # ['schrodinger_equation', 'simulated_annealing', 'quantum_annealing', 'lookup_table'] - # load adjudication results obtained from running /utils/adjudicate_all_terminal_states.py - data_dir = os.path.join(os.getcwd(), '..', 'data') - file_name = "graph_" + str(graph_number) + "_terminal_states_adjudication_results.pkl" - - with open(os.path.join(data_dir, file_name), "rb") as fp: - adjudication_results = pickle.load(fp) - - # check to make sure the entries in solvers_to_use have adjudication results already - for each in solvers_to_use: - if each not in adjudication_results: - sys.exit(print('no adjudication results found for solver ' + - each + '. Run adjudicate_all_terminal_states.py using this solver first.')) - - # OK so we now have some adjudication results to compare. For each canonical terminal state we will generate a - # list with three booleans, corresponding to 1v2, 1v3, 2v3 respectively. The boolean is True if the pair agree and - # False if they don't. - - # initialize game_result dict, load in results from the different solvers requested - game_result = {} # this is a dict whose keys are the canonical terminal states - scores = {} # holds the scores for all the terminal states in a list - - for k0, value_dict in adjudication_results.items(): # k0 is the solver name string - for k1, v in value_dict.items(): # k1 is the game state string - game_result[k1] = [] - for each in solvers_to_use: - scores[each] = [] - - for k0, value_dict in adjudication_results.items(): # k will be solver name string - if k0 in solvers_to_use: # if we want to add this, add it - for k1, v in value_dict.items(): - game_result[k1].append([k0, v['winner'], v['score']]) # score will be None for lookup_table - - comparisons = {} - for k, v in game_result.items(): # k is game state string - comparisons[k] = [] - for a, b in combinations(v, 2): - comparisons[k].append(a[1] == b[1]) - - for k, v in comparisons.items(): - if False in v: - print('key ', k, 'has a mismatch!') - - for k, v in game_result.items(): - for each in v: - scores[each[0]].append(each[2]) - - to_plot = [] - for k, v in scores.items(): - if v[0] is not None: - to_plot.append(v) - - if 'lookup_table' in solvers_to_use: - solvers_to_use.remove('lookup_table') - - if len(solvers_to_use) < 2: - print('need at least two of SA, QA, SE to generate score comparisons... lookup_table does not generate scores!') +def compare_adjudication_results(graph_number, solvers_to_use, lookup_table_solvers_to_use, epsilon, anneal_time, num_reads): + # this compares results from a set of solvers + parameters + # solvers_to_use is a list of solvers in ['schrodinger_equation', 'simulated_annealing', 'quantum_annealing', 'lookup_table'] + # lookup_table_solvers_to_use is a list of solvers whose lookup tables you want to use - red_text = solvers_to_use[0] + ': red' - blue_text = solvers_to_use[1] + ': blue' - cyan_text = None + graph = GraphProperties(graph_number) - if len(solvers_to_use) == 3: - cyan_text = solvers_to_use[2] + ': cyan' - - if graph_number == 2: - - if len(to_plot) == 2: - plt.hist(to_plot, range=[-2, 2], bins=200, color=['red', 'blue'], stacked=True) - else: - plt.hist(to_plot, range=[-2, 2], bins=200, color=['red', 'blue', 'cyan'], stacked=True) - - plt.text(1, 20, r'Three Vertex Graph', fontsize=12) - plt.text(1, 18, red_text, fontsize=8) - plt.text(1, 17, blue_text, fontsize=8) - if len(solvers_to_use) == 3: - plt.text(1, 16, cyan_text, fontsize=8) - - plt.ylim(0, 26) - - plt.xlabel('Score') - plt.ylabel('Terminal State Count') - - plt.vlines(x=0.5, ymin=0, ymax=20, colors='green', ls=':', lw=1) - plt.vlines(x=-0.5, ymin=0, ymax=20, colors='green', ls=':', lw=1) - - if graph_number == 3: - - if len(to_plot) == 2: - plt.hist(to_plot, range=[-4, 4], bins=800, color=['red', 'blue'], stacked=True) - else: - plt.hist(to_plot, range=[-4, 4], bins=800, color=['red', 'blue', 'cyan'], stacked=True) - - plt.text(2.5, 70, r'Four Vertex Graph', fontsize=12) - plt.text(2.5, 65, red_text, fontsize=8) - plt.text(2.5, 61, blue_text, fontsize=8) - if len(solvers_to_use) == 3: - plt.text(2.5, 57, cyan_text, fontsize=8) - plt.ylim(0, 100) - - plt.xlabel('Score') - plt.ylabel('Terminal State Count') - - plt.vlines(x=0.5, ymin=0, ymax=70, colors='green', ls=':', lw=1) - plt.vlines(x=-0.5, ymin=0, ymax=70, colors='green', ls=':', lw=1) - - plt.show() + data_dir = os.path.join(os.getcwd(), '..', 'data') + raw_file_path = os.path.join(data_dir, + "graph_" + str(graph_number) + "_adjudicated_unique_terminal_states_for_paper.pkl") + + try: + with open(raw_file_path, 'rb') as fp: + raw_adjudication_data = pickle.load(fp) + except Exception as e: + raise RuntimeError(f"Failed to load raw adjudication data: {str(e)}") + + results = defaultdict(lambda: defaultdict(dict)) + + for solver in solvers_to_use: + if solver == 'simulated_annealing': + for k, v in raw_adjudication_data['fixed']['simulated_annealing'][num_reads['simulated_annealing']].items(): + results[k][solver] = [v['score'], evaluate_winner(v['score'], epsilon=epsilon)] + if solver == 'schrodinger_equation': + for k, v in raw_adjudication_data['fixed']['schrodinger_equation'][anneal_time['schrodinger_equation']].items(): + results[k][solver] = [v['score'], evaluate_winner(v['score'], epsilon=epsilon)] + if solver == 'quantum_annealing': + for k, v in raw_adjudication_data['fixed']['quantum_annealing'][num_reads['quantum_annealing']][anneal_time['quantum_annealing']].items(): + results[k][solver] = [v['score'], evaluate_winner(v['score'], epsilon=epsilon)] + if solver == 'lookup_table': + for each in lookup_table_solvers_to_use: + lookup_table = load_lookup_table(data_dir, graph_number, each, epsilon, anneal_time[each], num_reads[each]) + for k, v in lookup_table.items(): + results[k][solver][each] = [None, v] + + cnt = 0 + current_biggest_score_difference = 0 + big_key = None + big_max_index = None + big_min_index = None + pos_val = None + neg_val = None + + for k, v in results.items(): # key is '[1, 0, 2, 1, 1]' + # Only look at the ones where 1 is at red's position and 2 is at blue's position, + # not the reversed ones which are only used for lookup table + alphazero + vertices = ''.join(k.strip('[]').split(', ')[:graph.vertex_count]) + pos_1 = vertices.find('1') + pos_2 = vertices.find('2') + if pos_1 == graph.vertex_ownership[graph_number][0] and pos_2 == graph.vertex_ownership[graph_number][1]: + list_of_results = [] + scores = [] + for solver in solvers_to_use: + if solver == 'lookup_table': + for lut_solver in lookup_table_solvers_to_use: + list_of_results.append(v[solver][lut_solver][1]) + else: + list_of_results.append(v[solver][1]) + scores.append(v[solver][0]) + if len(set(list_of_results)) > 1: + print('key ', k, 'has a mismatch; values are ', list_of_results) + cnt += 1 + + most_positive = max(scores) + most_negative = min(scores) + biggest_score_difference = most_positive - most_negative + + if biggest_score_difference > current_biggest_score_difference: + big_key = k + current_biggest_score_difference = biggest_score_difference + neg_val = most_negative + pos_val = most_positive + big_max_index = scores.index(most_positive) + big_min_index = scores.index(most_negative) + + print('total adjudication mismatches:', cnt) + print('key with biggest mismatch:', big_key) + print('biggest score mismatch:', current_biggest_score_difference) + print('solvers:', solvers_to_use[big_max_index], solvers_to_use[big_min_index]) + print('respective scores:', pos_val, neg_val) def main(): + graph_number = 19 # 11 is P_3, 2 is K_3, 20 is diamond graph, 19 is barbell graph solvers_to_use = ['simulated_annealing', 'schrodinger_equation', 'quantum_annealing', 'lookup_table'] - compare_adjudication_results(graph_number=2, solvers_to_use=solvers_to_use) + lookup_table_solvers_to_use = ['simulated_annealing', 'schrodinger_equation', 'quantum_annealing'] + epsilon = 0.25 # 0.25 for 19, 0.5 for the rest + anneal_time = {'quantum_annealing': 350, 'schrodinger_equation': 40, 'simulated_annealing': None} + num_reads = {'quantum_annealing': 100000, 'schrodinger_equation': None, 'simulated_annealing': 100000} - solvers_to_use = ['simulated_annealing', 'quantum_annealing', 'lookup_table'] - compare_adjudication_results(graph_number=3, solvers_to_use=solvers_to_use) + compare_adjudication_results(graph_number, solvers_to_use, lookup_table_solvers_to_use, epsilon, anneal_time, num_reads) if __name__ == "__main__": From 45d684aa68c03643e400cfd06abe46990fafe476 Mon Sep 17 00:00:00 2001 From: Geordie Rose Date: Tue, 15 Jul 2025 15:32:47 -0700 Subject: [PATCH 58/59] rewrote load_lookup_table, added evaluate_winner, commented out crufty plots --- tangled_adjudicate/utils/utilities.py | 429 +++++++++++++++++++++++++- 1 file changed, 413 insertions(+), 16 deletions(-) diff --git a/tangled_adjudicate/utils/utilities.py b/tangled_adjudicate/utils/utilities.py index 4874ebc..8bd5ea4 100644 --- a/tangled_adjudicate/utils/utilities.py +++ b/tangled_adjudicate/utils/utilities.py @@ -1,27 +1,106 @@ """ a place to put utility functions """ +import os +import pickle import ast import gdown +import matplotlib.pyplot as plt +import numpy as np +from tangled_adjudicate.utils.game_graph_properties import GraphProperties -def get_tso(graph_number, file_path): - # get terminal state outcomes - if graph_number == 2: - tso_url = 'https://drive.google.com/uc?id=14NcsNuPmHA4fE-Q5Wr4XVRxbYPOTcVNP' - gdown.download(tso_url, file_path, quiet=False) - if graph_number == 3: - tso_url = 'https://drive.google.com/uc?id=1Ob09q0WOHZp4gRd-A5h6af0TNaA3Ek6A' - gdown.download(tso_url, file_path, quiet=False) +def swap_ones_and_twos(lst): + # Find indices of 1 and 2 + index_of_one = lst.index(1) + index_of_two = lst.index(2) + # Swap the values + lst[index_of_one] = 2 + lst[index_of_two] = 1 -def build_results_dict(results): - # data is a list of elements like [[[0, 2, 1, 1, 1, 1], 'draw'], [[1, 0, 2, 1, 1, 1], 'draw']] - # the full data for 3-vertex 3-edge Tangled are in - # (os.path.join(os.getcwd(), '..', 'data', "three_vertex_terminal_state_outcomes_test.txt") - results_dict = {} - for each in results: - results_dict[str(each[0])] = each[1] - return results_dict + return lst + + +def evaluate_winner(score, epsilon): + if score > epsilon: + winner = 'red' + elif score < -epsilon: + winner = 'blue' + else: + winner = 'draw' + + return winner + + +def load_lookup_table(data_dir, graph_number, solver, epsilon, anneal_time, num_reads): + # returns a dict whose keys are the strings of the game states and values are 'draw', 'red', or 'blue' + # lookup_table = {'0, 2, 1, 1, 1, 1': 'draw', '1, 0, 2, 1, 1, 1': 'draw'} + + graph = GraphProperties(graph_number) + + raw_file_path = os.path.join(data_dir, + "graph_" + str(graph_number) + "_adjudicated_unique_terminal_states_for_paper.pkl") + + lookup_table_file_path = os.path.join(data_dir, + "graph_" + str(graph_number) + "_" + str(solver) + "_" + str(epsilon) + "_" + str(anneal_time) + "_" + str(num_reads) + ".pkl") + + if os.path.isfile(lookup_table_file_path): + with open(lookup_table_file_path, "rb") as fp: + lookup_table = pickle.load(fp) + return lookup_table # we are done! + + # in this case, the table still needs to be extracted + lookup_table = {} + + try: + with open(raw_file_path, 'rb') as fp: + raw_adjudication_data = pickle.load(fp) + except Exception as e: + raise RuntimeError(f"Failed to load raw adjudication data: {str(e)}") + + # assume game is type 'fixed' + # we use epsilon here to compute the winner and not use the existing winner + if solver in ['simulated_annealing']: + for k, v in raw_adjudication_data['fixed']['simulated_annealing'][num_reads].items(): + lookup_table[k] = evaluate_winner(v['score'], epsilon=epsilon) + else: + if solver in ['schrodinger_equation']: + for k, v in raw_adjudication_data['fixed']['schrodinger_equation'][anneal_time].items(): + lookup_table[k] = evaluate_winner(v['score'], epsilon=epsilon) + else: + if solver in ['quantum_annealing']: + for k, v in raw_adjudication_data['fixed']['quantum_annealing'][num_reads][anneal_time].items(): + lookup_table[k] = evaluate_winner(v['score'], epsilon=epsilon) + else: + print('something went wrong, solver type not found...') + + # For alphazero, there is the canonical board which is where the pieces actually are. As the agent is playing + # against itself, there's also a concept of switching places of the pieces. However these switched states are not + # in the data file. I think we swap where the 1 and 2 are in the vertex spots and swap 'winner' from + # 'red' <--> 'blue' and keep 'draw' the same + + full_results = {} + + for k, v in lookup_table.items(): + key_list = ast.literal_eval(k) + new_key = swap_ones_and_twos(key_list[:graph.vertex_count]) + key_list[graph.vertex_count:] + if v == 'draw': + full_results[str(new_key)] = 'draw' + else: + if v == 'red': + full_results[str(new_key)] = 'blue' + else: + if v == 'blue': + full_results[str(new_key)] = 'red' + else: + print('something went wrong with the new key adds in build_results_dict...') + + full_results.update(lookup_table) + + with open(lookup_table_file_path, 'wb') as fp: + pickle.dump(full_results, fp) + + return full_results def convert_erik_game_state_to_my_game_state(game_state): @@ -78,3 +157,321 @@ def convert_my_game_state_to_erik_game_state(my_state, number_of_vertices, list_ 'player2_node': player_2_vertex} return game_state + + +def plots_for_paper(graph_number, anneal_time, energies, probabilities_in_z_basis, sorted_indices_energies, use_zoom=True): + # generates plots for paper where top figure is terminal state count vs score, next is zoomed in same thing + # (optional), middle is schrodinger eigenenergies vs s, and bottom is schrodinger probabilities vs s + + # read in adjudication results -- need to run compare_adjudication_results.py first to generate + data_dir = os.path.join(os.getcwd(), '..', 'data') + with open(os.path.join(data_dir, "graph_" + str(graph_number) + "_data_for_plots.pkl"), "rb") as fp: + terminal_state_data = pickle.load(fp) + + to_plot = [] + for k, v in terminal_state_data.items(): + to_plot.append(v) + + if use_zoom: + num_plots = 4 + else: + num_plots = 3 + + plt.rcParams.update({ + 'font.size': 16, # Default font size + 'axes.labelsize': 16, # X and Y labels + 'xtick.labelsize': 16, # X tick labels + 'ytick.labelsize': 16, # Y tick labels + 'legend.fontsize': 14 # Legend + }) + + fig, axes = plt.subplots(num_plots, 1, figsize=(10, 4*num_plots)) + + # data for score plots + + graph_data = {11: {'name': '$P_3$ Path Graph', 'top_val': 15, 'width': 2, 'bin_count': 101, 'text_step': 0.5, 'first_offset': 1, + 'lr_offset': 1, 'prob_y_lim': 0.8, 'num_vert': 3, 'draw_boundary': 0.5, 'y_offset': [2.5, 6]}, + 2: {'name': '$K_3$', 'top_val': 50, 'width': 2, 'bin_count': 101, 'text_step': 1.5, 'first_offset': 4, + 'lr_offset': 1, 'prob_y_lim': 0.25, 'num_vert': 3, 'draw_boundary': 0.5, 'y_offset': [4.5, 8]}, + 20: {'name': 'Diamond Graph', 'top_val': 250, 'width': 4, 'bin_count': 201, 'text_step': 7.5, 'first_offset': 20, + 'lr_offset': 2, 'prob_y_lim': 0.3, 'num_vert': 4, 'draw_boundary': 0.5, 'y_offset': [1, 1], + 'zoom': {'top_val': 10, 'width': 2, 'bin_count': 101, 'text_step': 7.5, 'first_offset': 20, 'lr_offset': 2}}, + 3: {'name': '$K_4$', 'top_val': 800, 'width': 4, 'bin_count': 401, 'text_step': 25, 'first_offset': 70, + 'lr_offset': 2, 'prob_y_lim': 1, 'num_vert': 4, 'draw_boundary': 0.5, 'y_offset': [2.5, 6], + 'zoom': {'top_val': 80, 'width': 4, 'bin_count': 401, 'text_step': 2, 'first_offset': 7, 'lr_offset': 2}}, + 19: {'name': 'Barbell', 'top_val': 2500, 'width': 8, 'bin_count': 201, 'text_step': 6, 'first_offset': 12, 'lr_offset': 0.4, + 'prob_y_lim': 0.2, 'num_vert': 6, 'draw_boundary': 0.25, 'y_offset': [2.5, 6], + 'zoom': {'top_val': 100, 'width': 1, 'bin_count': 51, 'text_step': 70, 'first_offset': 150, 'lr_offset': 4}}, + 18: {'name': '3-Prism', 'top_val': 20000, 'width': 8, 'bin_count': 801, 'text_step': 500, 'first_offset': 2000, + 'lr_offset': 4, 'prob_y_lim': 1, 'num_vert': 6, 'draw_boundary': 0.5, 'y_offset': [2.5, 6], + 'zoom': {'top_val': 1000, 'width': 1, 'bin_count': 201, 'text_step': 25, 'first_offset': 100, 'lr_offset': 0.4}},} + + binary_labels = [ + r'$|\!\!' + format(i, '0' + str(graph_data[graph_number]['num_vert']) + 'b').replace('0', r'\uparrow\!\!\!\!').replace( + '1', r'\downarrow\!\!\!\!') + r'\; \rangle$' + for i in range(len(probabilities_in_z_basis[-1]))] + + col = ['gray', 'cyan', 'orange'] + + axis_to_use = 0 + axes[axis_to_use].hist(to_plot, + range=[-graph_data[graph_number]['width'], graph_data[graph_number]['width']], + bins=graph_data[graph_number]['bin_count'], + color=col, + stacked=True, + alpha=0.7, + label=['Simulated Annealing', 'Schrodinger Equation', 'Advantage2.1.3']) + + axes[axis_to_use].set_ylabel('Terminal State Count') + axes[axis_to_use].set_xlabel('Score') + axes[axis_to_use].grid(True, alpha=0.3) + axes[axis_to_use].vlines(x=graph_data[graph_number]['draw_boundary'], ymin=0, ymax=graph_data[graph_number]['top_val'], + colors='green', ls=':', lw=1) + axes[axis_to_use].vlines(x=-graph_data[graph_number]['draw_boundary'], ymin=0, ymax=graph_data[graph_number]['top_val'], + colors='green', ls=':', lw=1) + + axes[axis_to_use].set_ylim([0, graph_data[graph_number]['top_val']]) + axes[axis_to_use].legend() + axis_to_use += 1 + + if use_zoom: + axes[axis_to_use].hist(to_plot, + range=[-graph_data[graph_number]['zoom']['width'], graph_data[graph_number]['zoom']['width']], + bins=graph_data[graph_number]['zoom']['bin_count'], + color=col, + stacked=True, + alpha=0.7) + + axes[axis_to_use].set_ylabel('Terminal State Count') + axes[axis_to_use].set_xlabel('Score') + axes[axis_to_use].grid(True, alpha=0.3) + axes[axis_to_use].vlines(x=graph_data[graph_number]['draw_boundary'], ymin=0, + ymax=graph_data[graph_number]['zoom']['top_val'], + colors='green', ls=':', lw=1) + axes[axis_to_use].vlines(x=-graph_data[graph_number]['draw_boundary'], ymin=0, + ymax=graph_data[graph_number]['zoom']['top_val'], + colors='green', ls=':', lw=1) + + axes[axis_to_use].set_ylim([0, graph_data[graph_number]['zoom']['top_val']]) + axis_to_use += 1 + + # process data for energy plot + + # Flatten the data and assign rank-based colors + s_expanded = [] + E_flattened = [] + colors = [] + + for i, energy_list in enumerate(energies): + # Sort energies and get their ranks + sorted_indices = np.argsort(energy_list) # Indices that would sort the array + ranks = np.argsort(sorted_indices) # Rank of each element (0=lowest, 1=second lowest, etc.) + + # Extend the lists + s_expanded.extend([anneal_time[i]] * len(energy_list)) + E_flattened.extend(energy_list) + colors.extend(ranks) + + axes[axis_to_use].scatter(s_expanded, E_flattened, + c=colors, # Color by rank + s=1, # Small point size + alpha=0.8, + cmap='tab10') # Discrete colormap + + axes[axis_to_use].set_ylabel('Energy [GHz]') + # axes[axis_to_use].set_xlabel(r'$s = t/t_a$') + axes[axis_to_use].grid(True, alpha=0.3) + + # Add labels with collision detection + last_s = anneal_time[-1] + base_x_offset = 0.1 # 0.08, 0.09 + y_range = np.max(energies[-1]) - np.min(energies[-1]) + y_threshold = 0.05 * y_range # Define "close" as within 5% of y-range + + used_y_values = [] # Keep track of previously used y-values + + for j in range(energies[-1].size): + last_energy = energies[-1][j] + + # Check if current last_prob is close to any previously used y-value + x_offset_multiplier = 0 + for used_y in used_y_values: + if abs(last_energy - used_y) < y_threshold: + x_offset_multiplier += 1 + + current_x_offset = base_x_offset * x_offset_multiplier + if last_energy < -5: + y_offset = 0.01 * y_range + graph_data[graph_number]['y_offset'][0] + else: + if last_energy > 5: + y_offset = 0.01 * y_range - graph_data[graph_number]['y_offset'][1] + else: + y_offset = 0.01 * y_range + + # axes[axis_to_use].text(last_s - current_x_offset, + # last_energy + y_offset, + # binary_labels[sorted_indices_energies[j]], + # fontsize=8, + # ha='right', + # va='bottom') + + # Add current y-value to the list of used values + used_y_values.append(last_energy) + + # probabilities plot + axis_to_use += 1 + prob_array = np.array(probabilities_in_z_basis) + + for j in range(prob_array.shape[1]): + cols = [prob_array.shape[1]-1-j]*len(anneal_time) + axes[axis_to_use].scatter(anneal_time, prob_array[:, j], c=cols, s=1, alpha=0.7, cmap='tab10', vmin=0, vmax=prob_array.shape[1]-1) + + # Add labels with collision detection + last_s = anneal_time[-1] + + y_range = np.max(prob_array) - np.min(prob_array) + y_threshold = 0.05 * y_range # Define "close" as within 5% of y-range + + used_y_values = [] # Keep track of previously used y-values + + for j in range(prob_array.shape[1]): + last_prob = probabilities_in_z_basis[-1][j] + + # Check if current last_prob is close to any previously used y-value + x_offset_multiplier = 0 + for used_y in used_y_values: + if abs(last_prob - used_y) < y_threshold: + x_offset_multiplier += 1 + + current_x_offset = base_x_offset * x_offset_multiplier + y_offset = 0.025 * y_range + + if last_prob > 0.01: + axes[axis_to_use].text(last_s - current_x_offset, + last_prob + y_offset, + binary_labels[j], + fontsize=12, + ha='right', + va='bottom') + + # Add current y-value to the list of used values + used_y_values.append(last_prob) + + axes[axis_to_use].set_ylim([0, graph_data[graph_number]['prob_y_lim']]) + + axes[axis_to_use].set_ylabel('Probabilities') + axes[axis_to_use].set_xlabel(r'$s = t/t_a$') + axes[axis_to_use].grid(True, alpha=0.3) + + # Share x-axis between last two plots + axes[axis_to_use].sharex(axes[axis_to_use-1]) + axes[axis_to_use-1].tick_params(labelbottom=False) + + fig.align_ylabels(axes) + plt.tight_layout() + plt.show() + + +# crufty plots + +# def plot_energies(s, energies): +# # plots energies as a function of anneal parameter s +# +# plt.rcParams.update({ +# 'font.size': 24, # Default font size +# 'axes.labelsize': 24, # X and Y labels +# 'axes.titlesize': 24, # Title +# 'xtick.labelsize': 16, # X tick labels +# 'ytick.labelsize': 16, # Y tick labels +# 'legend.fontsize': 16 # Legend +# }) +# +# # Flatten the data and assign rank-based colors +# s_expanded = [] +# E_flattened = [] +# colors = [] +# +# for i, energy_list in enumerate(energies): +# # Sort energies and get their ranks +# sorted_indices = np.argsort(energy_list) # Indices that would sort the array +# ranks = np.argsort(sorted_indices) # Rank of each element (0=lowest, 1=second lowest, etc.) +# +# # Extend the lists +# s_expanded.extend([s[i]] * len(energy_list)) +# E_flattened.extend(energy_list) +# colors.extend(ranks) +# +# plt.figure(figsize=(10, 3)) +# scatter = plt.scatter(s_expanded, E_flattened, +# c=colors, # Color by rank +# s=1, # Small point size +# alpha=0.8, +# cmap='tab10') # Discrete colormap +# +# # plt.colorbar(scatter, label='Energy Rank (0=lowest)') +# plt.xlabel(r'$s = t/t_a$') +# plt.ylabel('Energy [GHz]') +# # plt.title('Energy vs s (colored by rank within each s)') +# plt.tight_layout() +# plt.show() +# +# +# def plot_probabilities(anneal_time, probabilities_in_z_basis): +# # plots probabilities in Z basis as a function of anneal parameter s +# +# plt.rcParams.update({ +# 'font.size': 24, # Default font size +# 'axes.labelsize': 24, # X and Y labels +# 'axes.titlesize': 24, # Title +# 'xtick.labelsize': 16, # X tick labels +# 'ytick.labelsize': 16, # Y tick labels +# 'legend.fontsize': 16 # Legend +# }) +# +# prob_array = np.array(probabilities_in_z_basis) +# +# plt.figure(figsize=(10, 4)) +# +# for j in range(prob_array.shape[1]): +# plt.scatter(anneal_time, prob_array[:, j], s=1, alpha=0.7) +# +# # Add labels with collision detection +# last_s = anneal_time[-1] +# base_x_offset = 0.15 +# y_range = np.max(prob_array) - np.min(prob_array) +# y_threshold = 0.05 * y_range # Define "close" as within 5% of y-range +# +# used_y_values = [] # Keep track of previously used y-values +# # +# # binary_labels = ['|' + format(i, '04b').replace('0', '↑').replace('1', '↓') + '>' for i in range(len(probabilities_in_z_basis[-1]))] +# +# for j in range(prob_array.shape[1]): +# last_prob = probabilities_in_z_basis[-1][j] +# +# # Check if current last_prob is close to any previously used y-value +# x_offset_multiplier = 0 +# for used_y in used_y_values: +# if abs(last_prob - used_y) < y_threshold: +# x_offset_multiplier += 1 +# +# current_x_offset = base_x_offset * x_offset_multiplier +# y_offset = 0.025 * y_range +# +# if last_prob > 0.01: +# plt.text(last_s - current_x_offset, +# last_prob + y_offset, +# binary_labels[j], +# fontsize=16, +# ha='right', +# va='bottom') +# +# # Add current y-value to the list of used values +# used_y_values.append(last_prob) +# +# plt.ylim([0, 0.3]) +# plt.xlabel('$s=t/t_a$') +# plt.ylabel('Probabilities') +# plt.tight_layout() +# plt.grid(True, alpha=0.3) +# +# plt.show() \ No newline at end of file From 35309142acf511f471775164538db7c14a0a2def Mon Sep 17 00:00:00 2001 From: Geordie Rose Date: Wed, 16 Jul 2025 08:17:43 -0700 Subject: [PATCH 59/59] removed unused plot imports --- tangled_adjudicate/schrodinger/schrodinger_functions.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tangled_adjudicate/schrodinger/schrodinger_functions.py b/tangled_adjudicate/schrodinger/schrodinger_functions.py index 398512f..e3c34b4 100644 --- a/tangled_adjudicate/schrodinger/schrodinger_functions.py +++ b/tangled_adjudicate/schrodinger/schrodinger_functions.py @@ -4,7 +4,7 @@ from tangled_adjudicate.schrodinger.sparse_matrices import (create_pauli_matrices_for_full_size_hamiltonian, load_schedule_data, create_sparse_hamiltonian, compute_eigenvalues_and_eigenvectors) -from tangled_adjudicate.utils.utilities import plot_energies, plot_probabilities, plots_for_paper +from tangled_adjudicate.utils.utilities import plots_for_paper def initialize_wavefunction(eigenvalues, eigenvectors, n_i, gap_initial):