From aeccd3b39d7447fc4b6cb96c0af04967ec0848b8 Mon Sep 17 00:00:00 2001 From: Peter Solodov Date: Wed, 22 May 2024 15:57:48 -0400 Subject: [PATCH 01/15] moved fox in a hole to examples directory --- {unitary/examples => examples}/fox_in_a_hole/README.md | 0 {unitary/examples => examples}/fox_in_a_hole/__init__.py | 0 {unitary/examples => examples}/fox_in_a_hole/fox_in_a_hole.py | 0 .../examples => examples}/fox_in_a_hole/fox_in_a_hole_test.py | 2 +- 4 files changed, 1 insertion(+), 1 deletion(-) rename {unitary/examples => examples}/fox_in_a_hole/README.md (100%) rename {unitary/examples => examples}/fox_in_a_hole/__init__.py (100%) rename {unitary/examples => examples}/fox_in_a_hole/fox_in_a_hole.py (100%) rename {unitary/examples => examples}/fox_in_a_hole/fox_in_a_hole_test.py (98%) diff --git a/unitary/examples/fox_in_a_hole/README.md b/examples/fox_in_a_hole/README.md similarity index 100% rename from unitary/examples/fox_in_a_hole/README.md rename to examples/fox_in_a_hole/README.md diff --git a/unitary/examples/fox_in_a_hole/__init__.py b/examples/fox_in_a_hole/__init__.py similarity index 100% rename from unitary/examples/fox_in_a_hole/__init__.py rename to examples/fox_in_a_hole/__init__.py diff --git a/unitary/examples/fox_in_a_hole/fox_in_a_hole.py b/examples/fox_in_a_hole/fox_in_a_hole.py similarity index 100% rename from unitary/examples/fox_in_a_hole/fox_in_a_hole.py rename to examples/fox_in_a_hole/fox_in_a_hole.py diff --git a/unitary/examples/fox_in_a_hole/fox_in_a_hole_test.py b/examples/fox_in_a_hole/fox_in_a_hole_test.py similarity index 98% rename from unitary/examples/fox_in_a_hole/fox_in_a_hole_test.py rename to examples/fox_in_a_hole/fox_in_a_hole_test.py index 81deca60..8f49a7a9 100644 --- a/unitary/examples/fox_in_a_hole/fox_in_a_hole_test.py +++ b/examples/fox_in_a_hole/fox_in_a_hole_test.py @@ -16,7 +16,7 @@ import pytest -import unitary.examples.fox_in_a_hole.fox_in_a_hole as fh +from . import fox_in_a_hole as fh def test_classical_game_basics(): From d0c7a6d80a1acbe251cded812772e59a11bd96fb Mon Sep 17 00:00:00 2001 From: Peter Solodov Date: Wed, 22 May 2024 16:45:10 -0400 Subject: [PATCH 02/15] moved tictactoe --- .../tictactoe/__init__.py | 7 +- .../tictactoe/ascii_board.py | 7 +- .../examples => examples}/tictactoe/enums.py | 0 .../tictactoe/tic_tac_split.py | 3 +- .../tictactoe/tic_tac_split_test.py | 85 ++++++----- .../tictactoe/tic_tac_toe.py | 12 +- .../tictactoe/tic_tac_toe_test.py | 133 +++++++++--------- 7 files changed, 122 insertions(+), 125 deletions(-) rename {unitary/examples => examples}/tictactoe/__init__.py (72%) rename {unitary/examples => examples}/tictactoe/ascii_board.py (95%) rename {unitary/examples => examples}/tictactoe/enums.py (100%) rename {unitary/examples => examples}/tictactoe/tic_tac_split.py (97%) rename {unitary/examples => examples}/tictactoe/tic_tac_split_test.py (67%) rename {unitary/examples => examples}/tictactoe/tic_tac_toe.py (98%) rename {unitary/examples => examples}/tictactoe/tic_tac_toe_test.py (55%) diff --git a/unitary/examples/tictactoe/__init__.py b/examples/tictactoe/__init__.py similarity index 72% rename from unitary/examples/tictactoe/__init__.py rename to examples/tictactoe/__init__.py index f510f903..a2e4f4df 100644 --- a/unitary/examples/tictactoe/__init__.py +++ b/examples/tictactoe/__init__.py @@ -11,7 +11,6 @@ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. - -from unitary.examples.tictactoe.enums import TicTacSquare, TicTacResult, TicTacRules -from unitary.examples.tictactoe.tic_tac_split import TicTacSplit -from unitary.examples.tictactoe.tic_tac_toe import TicTacToe, GameInterface +from .tic_tac_toe import TicTacToe, GameInterface +from .tic_tac_split import TicTacSplit +from .enums import TicTacSquare, TicTacResult, TicTacRules diff --git a/unitary/examples/tictactoe/ascii_board.py b/examples/tictactoe/ascii_board.py similarity index 95% rename from unitary/examples/tictactoe/ascii_board.py rename to examples/tictactoe/ascii_board.py index 042b94cb..0bb312ef 100644 --- a/unitary/examples/tictactoe/ascii_board.py +++ b/examples/tictactoe/ascii_board.py @@ -12,9 +12,10 @@ # See the License for the specific language governing permissions and # limitations under the License. -from unitary.examples.tictactoe.enums import TicTacSquare, TicTacResult, TicTacRules -from unitary.examples.tictactoe.tic_tac_toe import TicTacToe -import argparse, textwrap +from .enums import TicTacSquare, TicTacResult, TicTacRules +from .tic_tac_toe import TicTacToe +import argparse +import textwrap help_str = """\ In classical TicTacToe, players alternate in putting their token (either an diff --git a/unitary/examples/tictactoe/enums.py b/examples/tictactoe/enums.py similarity index 100% rename from unitary/examples/tictactoe/enums.py rename to examples/tictactoe/enums.py diff --git a/unitary/examples/tictactoe/tic_tac_split.py b/examples/tictactoe/tic_tac_split.py similarity index 97% rename from unitary/examples/tictactoe/tic_tac_split.py rename to examples/tictactoe/tic_tac_split.py index da3f0bbd..c5e452bc 100644 --- a/unitary/examples/tictactoe/tic_tac_split.py +++ b/examples/tictactoe/tic_tac_split.py @@ -19,7 +19,8 @@ from unitary.alpha import QuantumEffect, QuantumObject from unitary.alpha.qudit_gates import QuditXGate, QuditISwapPowGate -from unitary.examples.tictactoe.enums import TicTacSquare, TicTacRules + +from .enums import TicTacSquare, TicTacRules class QuditSplitGate(cirq.Gate): diff --git a/unitary/examples/tictactoe/tic_tac_split_test.py b/examples/tictactoe/tic_tac_split_test.py similarity index 67% rename from unitary/examples/tictactoe/tic_tac_split_test.py rename to examples/tictactoe/tic_tac_split_test.py index a26d2117..6a6c2640 100644 --- a/unitary/examples/tictactoe/tic_tac_split_test.py +++ b/examples/tictactoe/tic_tac_split_test.py @@ -15,21 +15,22 @@ import pytest import cirq +from unitary import alpha -import unitary.alpha as alpha -import unitary.examples.tictactoe as tictactoe +from . import enums +from . import tic_tac_split -_E = tictactoe.TicTacSquare.EMPTY -_O = tictactoe.TicTacSquare.O -_X = tictactoe.TicTacSquare.X +_E = enums.TicTacSquare.EMPTY +_O = enums.TicTacSquare.O +_X = enums.TicTacSquare.X @pytest.mark.parametrize( "mark, swap_state1, swap_state2", - [(tictactoe.TicTacSquare.X, 0, 1), (tictactoe.TicTacSquare.O, 0, 2)], + [(enums.TicTacSquare.X, 0, 1), (enums.TicTacSquare.O, 0, 2)], ) def test_tic_tac_split_gate( - mark: tictactoe.TicTacSquare, swap_state1: int, swap_state2: int + mark: enums.TicTacSquare, swap_state1: int, swap_state2: int ): q0 = cirq.NamedQid("a", dimension=3) q1 = cirq.NamedQid("b", dimension=3) @@ -42,8 +43,8 @@ def test_tic_tac_split_gate( c.append(alpha.qudit_gates.QuditXGate(3, 0, a)(q0)) if b > 0: c.append(alpha.qudit_gates.QuditXGate(3, 0, b)(q1)) - c.append(tictactoe.tic_tac_split.QuditSplitGate(mark)(q0, q1)) - c.append(tictactoe.tic_tac_split.QuditSplitGate(mark)(q0, q1)) + c.append(tic_tac_split.QuditSplitGate(mark)(q0, q1)) + c.append(tic_tac_split.QuditSplitGate(mark)(q0, q1)) c.append(cirq.measure(q0, key="a")) c.append(cirq.measure(q1, key="b")) results = sim.run(c, repetitions=10) @@ -61,17 +62,15 @@ def test_tic_tac_split_gate( def test_invalid_mark_for_gate(): with pytest.raises(ValueError, match="Not a valid square"): - _ = tictactoe.tic_tac_split.QuditSplitGate(True) + _ = tic_tac_split.QuditSplitGate(True) with pytest.raises(ValueError, match="Not a valid square"): - _ = tictactoe.tic_tac_split.QuditSplitGate(0) + _ = tic_tac_split.QuditSplitGate(0) def test_diagram(): q0 = cirq.NamedQid("a", dimension=3) q1 = cirq.NamedQid("b", dimension=3) - c = cirq.Circuit( - tictactoe.tic_tac_split.QuditSplitGate(tictactoe.TicTacSquare.X)(q0, q1) - ) + c = cirq.Circuit(tic_tac_split.QuditSplitGate(enums.TicTacSquare.X)(q0, q1)) assert ( str(c) == """ @@ -86,26 +85,26 @@ def test_diagram(): @pytest.mark.parametrize( "mark, ruleset", [ - (tictactoe.TicTacSquare.X, tictactoe.TicTacRules.QUANTUM_V2), - (tictactoe.TicTacSquare.X, tictactoe.TicTacRules.QUANTUM_V3), - (tictactoe.TicTacSquare.O, tictactoe.TicTacRules.QUANTUM_V2), - (tictactoe.TicTacSquare.O, tictactoe.TicTacRules.QUANTUM_V3), + (enums.TicTacSquare.X, enums.TicTacRules.QUANTUM_V2), + (enums.TicTacSquare.X, enums.TicTacRules.QUANTUM_V3), + (enums.TicTacSquare.O, enums.TicTacRules.QUANTUM_V2), + (enums.TicTacSquare.O, enums.TicTacRules.QUANTUM_V3), ], ) def test_tic_tac_split( - mark: tictactoe.TicTacSquare, - ruleset: tictactoe.TicTacRules, + mark: enums.TicTacSquare, + ruleset: enums.TicTacRules, compile_to_qubits: bool, ): - a = alpha.QuantumObject("a", tictactoe.TicTacSquare.EMPTY) - b = alpha.QuantumObject("b", tictactoe.TicTacSquare.EMPTY) + a = alpha.QuantumObject("a", enums.TicTacSquare.EMPTY) + b = alpha.QuantumObject("b", enums.TicTacSquare.EMPTY) board = alpha.QuantumWorld( [a, b], sampler=cirq.Simulator(), compile_to_qubits=compile_to_qubits ) - tictactoe.TicTacSplit(mark, ruleset)(a, b) + tic_tac_split.TicTacSplit(mark, ruleset)(a, b) results = board.peek(count=1000) - on_a = [mark, tictactoe.TicTacSquare.EMPTY] - on_b = [tictactoe.TicTacSquare.EMPTY, mark] + on_a = [mark, enums.TicTacSquare.EMPTY] + on_b = [enums.TicTacSquare.EMPTY, mark] assert any(r == on_a for r in results) assert any(r == on_b for r in results) assert all(r == on_a or r == on_b for r in results) @@ -113,15 +112,15 @@ def test_tic_tac_split( @pytest.mark.parametrize("compile_to_qubits", [False, True]) def test_tic_tac_split_entangled_v2(compile_to_qubits): - a = alpha.QuantumObject("a", tictactoe.TicTacSquare.EMPTY) - b = alpha.QuantumObject("b", tictactoe.TicTacSquare.EMPTY) - c = alpha.QuantumObject("c", tictactoe.TicTacSquare.EMPTY) + a = alpha.QuantumObject("a", enums.TicTacSquare.EMPTY) + b = alpha.QuantumObject("b", enums.TicTacSquare.EMPTY) + c = alpha.QuantumObject("c", enums.TicTacSquare.EMPTY) board = alpha.QuantumWorld( [a, b, c], sampler=cirq.Simulator(), compile_to_qubits=compile_to_qubits ) - ruleset = tictactoe.TicTacRules.QUANTUM_V2 - tictactoe.TicTacSplit(tictactoe.TicTacSquare.X, ruleset)(a, b) - tictactoe.TicTacSplit(tictactoe.TicTacSquare.O, ruleset)(b, c) + ruleset = enums.TicTacRules.QUANTUM_V2 + tic_tac_split.TicTacSplit(enums.TicTacSquare.X, ruleset)(a, b) + tic_tac_split.TicTacSplit(enums.TicTacSquare.O, ruleset)(b, c) results = board.peek(count=1000) on_ab = [_X, _O, _E] on_ac = [_X, _E, _O] @@ -134,15 +133,15 @@ def test_tic_tac_split_entangled_v2(compile_to_qubits): @pytest.mark.parametrize("compile_to_qubits", [False, True]) def test_tic_tac_split_entangled_v3(compile_to_qubits): - a = alpha.QuantumObject("a", tictactoe.TicTacSquare.EMPTY) - b = alpha.QuantumObject("b", tictactoe.TicTacSquare.EMPTY) - c = alpha.QuantumObject("c", tictactoe.TicTacSquare.EMPTY) + a = alpha.QuantumObject("a", enums.TicTacSquare.EMPTY) + b = alpha.QuantumObject("b", enums.TicTacSquare.EMPTY) + c = alpha.QuantumObject("c", enums.TicTacSquare.EMPTY) board = alpha.QuantumWorld( [a, b, c], sampler=cirq.Simulator(), compile_to_qubits=compile_to_qubits ) - ruleset = tictactoe.TicTacRules.QUANTUM_V3 - tictactoe.TicTacSplit(tictactoe.TicTacSquare.X, ruleset)(a, b) - tictactoe.TicTacSplit(tictactoe.TicTacSquare.O, ruleset)(b, c) + ruleset = enums.TicTacRules.QUANTUM_V3 + tic_tac_split.TicTacSplit(enums.TicTacSquare.X, ruleset)(a, b) + tic_tac_split.TicTacSplit(enums.TicTacSquare.O, ruleset)(b, c) results = board.peek(count=1000) # This sequence of moves, for just three squares, should produce: # EEE -> XEE + EXE -> XEO + EXE -> XEO + XOE + EXE + EEX @@ -163,15 +162,15 @@ def test_tic_tac_split_entangled_v3(compile_to_qubits): @pytest.mark.parametrize("compile_to_qubits", [False, True]) def test_tic_tac_split_entangled_v3_empty(compile_to_qubits): - a = alpha.QuantumObject("a", tictactoe.TicTacSquare.EMPTY) - b = alpha.QuantumObject("b", tictactoe.TicTacSquare.EMPTY) - c = alpha.QuantumObject("c", tictactoe.TicTacSquare.EMPTY) + a = alpha.QuantumObject("a", enums.TicTacSquare.EMPTY) + b = alpha.QuantumObject("b", enums.TicTacSquare.EMPTY) + c = alpha.QuantumObject("c", enums.TicTacSquare.EMPTY) board = alpha.QuantumWorld( [a, b, c], sampler=cirq.Simulator(), compile_to_qubits=compile_to_qubits ) - ruleset = tictactoe.TicTacRules.QUANTUM_V3 - tictactoe.TicTacSplit(tictactoe.TicTacSquare.X, ruleset)(a, b) - tictactoe.TicTacSplit(tictactoe.TicTacSquare.O, ruleset)(c, b) + ruleset = enums.TicTacRules.QUANTUM_V3 + tic_tac_split.TicTacSplit(enums.TicTacSquare.X, ruleset)(a, b) + tic_tac_split.TicTacSplit(enums.TicTacSquare.O, ruleset)(c, b) results = board.peek(count=1000) # This sequence of moves, for just three squares, should produce: # EEE -> XEE + EXE -> XEO + EXO -> XEO + XOE + EXO + EOX diff --git a/unitary/examples/tictactoe/tic_tac_toe.py b/examples/tictactoe/tic_tac_toe.py similarity index 98% rename from unitary/examples/tictactoe/tic_tac_toe.py rename to examples/tictactoe/tic_tac_toe.py index 0be2af27..ca51b00d 100644 --- a/unitary/examples/tictactoe/tic_tac_toe.py +++ b/examples/tictactoe/tic_tac_toe.py @@ -19,13 +19,9 @@ from unitary.alpha import QuantumObject, QuantumWorld from unitary.alpha.qudit_effects import QuditFlip -from unitary.examples.tictactoe.enums import ( - TicTacSquare, - TicTacResult, - TicTacRules, - GameMoves, -) -from unitary.examples.tictactoe.tic_tac_split import TicTacSplit + +from .enums import TicTacSquare, TicTacResult, TicTacRules, GameMoves +from .tic_tac_split import TicTacSplit _SQUARE_NAMES = "abcdefghi" _MARK_SYMBOLS = {TicTacSquare.EMPTY: ".", TicTacSquare.X: "X", TicTacSquare.O: "O"} @@ -327,7 +323,7 @@ def print_welcome(self) -> str: message = """ Welcome to quantum tic tac toe! Here is the board: - """ +""" message += _BOARD_MAP return message diff --git a/unitary/examples/tictactoe/tic_tac_toe_test.py b/examples/tictactoe/tic_tac_toe_test.py similarity index 55% rename from unitary/examples/tictactoe/tic_tac_toe_test.py rename to examples/tictactoe/tic_tac_toe_test.py index 7dc8bd47..05c9efd4 100644 --- a/unitary/examples/tictactoe/tic_tac_toe_test.py +++ b/examples/tictactoe/tic_tac_toe_test.py @@ -16,53 +16,54 @@ import io from unittest.mock import MagicMock -import unitary.examples.tictactoe as tictactoe +from . import enums +from . import tic_tac_toe -_E = tictactoe.TicTacSquare.EMPTY -_O = tictactoe.TicTacSquare.O -_X = tictactoe.TicTacSquare.X +_E = enums.TicTacSquare.EMPTY +_O = enums.TicTacSquare.O +_X = enums.TicTacSquare.X @pytest.mark.parametrize( "result,expected", [ - ([_E, _E, _E, _E, _E, _E, _E, _E, _E], tictactoe.TicTacResult.UNFINISHED), - ([_E, _E, _X, _X, _E, _E, _O, _E, _E], tictactoe.TicTacResult.UNFINISHED), - ([_E, _E, _X, _X, _E, _X, _O, _E, _X], tictactoe.TicTacResult.X_WINS), - ([_E, _X, _E, _X, _X, _O, _O, _X, _E], tictactoe.TicTacResult.X_WINS), - ([_X, _E, _E, _X, _O, _O, _X, _E, _E], tictactoe.TicTacResult.X_WINS), - ([_X, _X, _X, _X, _O, _O, _E, _E, _E], tictactoe.TicTacResult.X_WINS), - ([_E, _E, _E, _X, _X, _X, _E, _O, _E], tictactoe.TicTacResult.X_WINS), - ([_E, _E, _E, _E, _E, _E, _X, _X, _X], tictactoe.TicTacResult.X_WINS), - ([_X, _E, _E, _E, _X, _E, _E, _E, _X], tictactoe.TicTacResult.X_WINS), - ([_E, _E, _X, _E, _X, _E, _X, _E, _E], tictactoe.TicTacResult.X_WINS), - ([_O, _E, _E, _E, _O, _E, _E, _E, _O], tictactoe.TicTacResult.O_WINS), - ([_E, _E, _O, _E, _O, _E, _O, _E, _E], tictactoe.TicTacResult.O_WINS), - ([_O, _O, _O, _E, _E, _E, _E, _E, _E], tictactoe.TicTacResult.O_WINS), - ([_E, _E, _E, _O, _O, _O, _E, _E, _E], tictactoe.TicTacResult.O_WINS), - ([_E, _E, _E, _E, _E, _E, _O, _O, _O], tictactoe.TicTacResult.O_WINS), - ([_O, _E, _E, _O, _E, _E, _O, _E, _E], tictactoe.TicTacResult.O_WINS), - ([_E, _O, _E, _E, _O, _E, _E, _O, _E], tictactoe.TicTacResult.O_WINS), - ([_E, _E, _O, _E, _E, _O, _E, _E, _O], tictactoe.TicTacResult.O_WINS), - ([_E, _E, _O, _E, _E, _O, _E, _E, _O], tictactoe.TicTacResult.O_WINS), - ([_X, _E, _O, _X, _E, _O, _X, _E, _O], tictactoe.TicTacResult.BOTH_WIN), - ([_X, _X, _X, _O, _O, _O, _E, _E, _E], tictactoe.TicTacResult.BOTH_WIN), - ([_E, _E, _E, _O, _O, _O, _X, _X, _X], tictactoe.TicTacResult.BOTH_WIN), - ([_X, _O, _O, _O, _X, _X, _O, _X, _O], tictactoe.TicTacResult.DRAW), - ([_X, _O, _X, _X, _O, _X, _O, _X, _O], tictactoe.TicTacResult.DRAW), + ([_E, _E, _E, _E, _E, _E, _E, _E, _E], enums.TicTacResult.UNFINISHED), + ([_E, _E, _X, _X, _E, _E, _O, _E, _E], enums.TicTacResult.UNFINISHED), + ([_E, _E, _X, _X, _E, _X, _O, _E, _X], enums.TicTacResult.X_WINS), + ([_E, _X, _E, _X, _X, _O, _O, _X, _E], enums.TicTacResult.X_WINS), + ([_X, _E, _E, _X, _O, _O, _X, _E, _E], enums.TicTacResult.X_WINS), + ([_X, _X, _X, _X, _O, _O, _E, _E, _E], enums.TicTacResult.X_WINS), + ([_E, _E, _E, _X, _X, _X, _E, _O, _E], enums.TicTacResult.X_WINS), + ([_E, _E, _E, _E, _E, _E, _X, _X, _X], enums.TicTacResult.X_WINS), + ([_X, _E, _E, _E, _X, _E, _E, _E, _X], enums.TicTacResult.X_WINS), + ([_E, _E, _X, _E, _X, _E, _X, _E, _E], enums.TicTacResult.X_WINS), + ([_O, _E, _E, _E, _O, _E, _E, _E, _O], enums.TicTacResult.O_WINS), + ([_E, _E, _O, _E, _O, _E, _O, _E, _E], enums.TicTacResult.O_WINS), + ([_O, _O, _O, _E, _E, _E, _E, _E, _E], enums.TicTacResult.O_WINS), + ([_E, _E, _E, _O, _O, _O, _E, _E, _E], enums.TicTacResult.O_WINS), + ([_E, _E, _E, _E, _E, _E, _O, _O, _O], enums.TicTacResult.O_WINS), + ([_O, _E, _E, _O, _E, _E, _O, _E, _E], enums.TicTacResult.O_WINS), + ([_E, _O, _E, _E, _O, _E, _E, _O, _E], enums.TicTacResult.O_WINS), + ([_E, _E, _O, _E, _E, _O, _E, _E, _O], enums.TicTacResult.O_WINS), + ([_E, _E, _O, _E, _E, _O, _E, _E, _O], enums.TicTacResult.O_WINS), + ([_X, _E, _O, _X, _E, _O, _X, _E, _O], enums.TicTacResult.BOTH_WIN), + ([_X, _X, _X, _O, _O, _O, _E, _E, _E], enums.TicTacResult.BOTH_WIN), + ([_E, _E, _E, _O, _O, _O, _X, _X, _X], enums.TicTacResult.BOTH_WIN), + ([_X, _O, _O, _O, _X, _X, _O, _X, _O], enums.TicTacResult.DRAW), + ([_X, _O, _X, _X, _O, _X, _O, _X, _O], enums.TicTacResult.DRAW), ], ) def test_eval_board(result, expected): - assert tictactoe.tic_tac_toe.eval_board(result) == expected + assert tic_tac_toe.eval_board(result) == expected @pytest.mark.parametrize("run_on_hardware", [False, True]) def test_measure(run_on_hardware): - board = tictactoe.TicTacToe(run_on_hardware=run_on_hardware) - board.move("ab", tictactoe.TicTacSquare.X) - board.move("cd", tictactoe.TicTacSquare.O) - board.move("ef", tictactoe.TicTacSquare.X) - board.move("gh", tictactoe.TicTacSquare.O) + board = tic_tac_toe.TicTacToe(run_on_hardware=run_on_hardware) + board.move("ab", enums.TicTacSquare.X) + board.move("cd", enums.TicTacSquare.O) + board.move("ef", enums.TicTacSquare.X) + board.move("gh", enums.TicTacSquare.O) results = board.sample(count=1000) assert len(results) == 1000 assert all(result.count("X") == 2 for result in results) @@ -75,7 +76,7 @@ def test_measure(run_on_hardware): assert len(result_set) == 16 # This should trigger a measurement - board.move("i", tictactoe.TicTacSquare.X) + board.move("i", enums.TicTacSquare.X) results = board.sample(count=1000) assert len(results) == 1000 assert all(result.count("X") == 3 for result in results) @@ -89,10 +90,10 @@ def test_measure(run_on_hardware): # Now try an operation after measurement if results[0][0] == ".": - board.move("a", tictactoe.TicTacSquare.O) + board.move("a", enums.TicTacSquare.O) expected = "OX" + results[0][2:] else: - board.move("b", tictactoe.TicTacSquare.O) + board.move("b", enums.TicTacSquare.O) expected = "XO" + results[0][2:] results = board.sample(count=1000) assert len(results) == 1000 @@ -101,14 +102,14 @@ def test_measure(run_on_hardware): @pytest.mark.parametrize("run_on_hardware", [False, True]) def test_sample(run_on_hardware): - board = tictactoe.TicTacToe(run_on_hardware=run_on_hardware) - board.move("a", tictactoe.TicTacSquare.X) - board.move("e", tictactoe.TicTacSquare.O) + board = tic_tac_toe.TicTacToe(run_on_hardware=run_on_hardware) + board.move("a", enums.TicTacSquare.X) + board.move("e", enums.TicTacSquare.O) results = board.sample(count=100) assert len(results) == 100 assert all(result == "X...O...." for result in results) - board.move("h", tictactoe.TicTacSquare.O) - board.move("i", tictactoe.TicTacSquare.X) + board.move("h", enums.TicTacSquare.O) + board.move("i", enums.TicTacSquare.X) results = board.sample(count=200) assert len(results) == 200 assert all(result == "X...O..OX" for result in results) @@ -116,8 +117,8 @@ def test_sample(run_on_hardware): @pytest.mark.parametrize("run_on_hardware", [False, True]) def test_split(run_on_hardware): - board = tictactoe.TicTacToe(run_on_hardware=run_on_hardware) - board.move("ab", tictactoe.TicTacSquare.X) + board = tic_tac_toe.TicTacToe(run_on_hardware=run_on_hardware) + board.move("ab", enums.TicTacSquare.X) results = board.sample(count=1000) assert len(results) == 1000 in_a = "X........" @@ -130,24 +131,24 @@ def test_split(run_on_hardware): @pytest.mark.parametrize("run_on_hardware", [False, True]) def test_rulesets(run_on_hardware): # Try to make a quantum move with classical rules - board = tictactoe.TicTacToe( - tictactoe.TicTacRules.CLASSICAL, run_on_hardware=run_on_hardware + board = tic_tac_toe.TicTacToe( + enums.TicTacRules.CLASSICAL, run_on_hardware=run_on_hardware ) with pytest.raises(ValueError): - board.move("ab", tictactoe.TicTacSquare.X) + board.move("ab", enums.TicTacSquare.X) # Try to make a split move on non-empty squares at minimal quantum - board = tictactoe.TicTacToe( - tictactoe.TicTacRules.QUANTUM_V1, run_on_hardware=run_on_hardware + board = tic_tac_toe.TicTacToe( + enums.TicTacRules.QUANTUM_V1, run_on_hardware=run_on_hardware ) - board.move("a", tictactoe.TicTacSquare.X) + board.move("a", enums.TicTacSquare.X) with pytest.raises(ValueError): - board.move("ab", tictactoe.TicTacSquare.O) + board.move("ab", enums.TicTacSquare.O) def test_welcome(): - board = tictactoe.TicTacToe() - game = tictactoe.GameInterface(board) + board = tic_tac_toe.TicTacToe() + game = tic_tac_toe.GameInterface(board) output = game.print_welcome() assert ( @@ -155,7 +156,7 @@ def test_welcome(): == """ Welcome to quantum tic tac toe! Here is the board: - + a | b | c ----------- d | e | f @@ -167,8 +168,8 @@ def test_welcome(): def test_help(): output = io.StringIO() - board = tictactoe.TicTacToe() - game = tictactoe.GameInterface(board, output) + board = tic_tac_toe.TicTacToe() + game = tic_tac_toe.GameInterface(board, output) game.get_move = MagicMock(return_value="help") game.player_move() @@ -189,8 +190,8 @@ def test_help(): def test_map(): output = io.StringIO() - board = tictactoe.TicTacToe() - game = tictactoe.GameInterface(board, output) + board = tic_tac_toe.TicTacToe() + game = tic_tac_toe.GameInterface(board, output) game.get_move = MagicMock(return_value="map") game.player_move() @@ -211,8 +212,8 @@ def test_map(): def test_exit(): output = io.StringIO() - board = tictactoe.TicTacToe() - game = tictactoe.GameInterface(board, file=output) + board = tic_tac_toe.TicTacToe() + game = tic_tac_toe.GameInterface(board, file=output) game.get_move = MagicMock(return_value="exit") game.player_move() @@ -220,8 +221,8 @@ def test_exit(): def test_player_alternates(): - board = tictactoe.TicTacToe() - game = tictactoe.GameInterface(board) + board = tic_tac_toe.TicTacToe() + game = tic_tac_toe.GameInterface(board) game.get_move = MagicMock(return_value="a") game.player_move() @@ -229,10 +230,10 @@ def test_player_alternates(): def test_print_board(): - board = tictactoe.TicTacToe() - game = tictactoe.GameInterface(board) - board.move("a", tictactoe.TicTacSquare.X) - board.move("e", tictactoe.TicTacSquare.O) + board = tic_tac_toe.TicTacToe() + game = tic_tac_toe.GameInterface(board) + board.move("a", enums.TicTacSquare.X) + board.move("e", enums.TicTacSquare.O) output = game.print_board() assert ( From 936221544088d3b9607ea0c60c86c5bb54cd4737 Mon Sep 17 00:00:00 2001 From: Peter Solodov Date: Wed, 22 May 2024 16:50:18 -0400 Subject: [PATCH 03/15] renamed directory to match module name --- examples/{tictactoe => tic_tac_toe}/__init__.py | 0 examples/{tictactoe => tic_tac_toe}/ascii_board.py | 0 examples/{tictactoe => tic_tac_toe}/enums.py | 0 examples/{tictactoe => tic_tac_toe}/tic_tac_split.py | 0 examples/{tictactoe => tic_tac_toe}/tic_tac_split_test.py | 0 examples/{tictactoe => tic_tac_toe}/tic_tac_toe.py | 0 examples/{tictactoe => tic_tac_toe}/tic_tac_toe_test.py | 0 7 files changed, 0 insertions(+), 0 deletions(-) rename examples/{tictactoe => tic_tac_toe}/__init__.py (100%) rename examples/{tictactoe => tic_tac_toe}/ascii_board.py (100%) rename examples/{tictactoe => tic_tac_toe}/enums.py (100%) rename examples/{tictactoe => tic_tac_toe}/tic_tac_split.py (100%) rename examples/{tictactoe => tic_tac_toe}/tic_tac_split_test.py (100%) rename examples/{tictactoe => tic_tac_toe}/tic_tac_toe.py (100%) rename examples/{tictactoe => tic_tac_toe}/tic_tac_toe_test.py (100%) diff --git a/examples/tictactoe/__init__.py b/examples/tic_tac_toe/__init__.py similarity index 100% rename from examples/tictactoe/__init__.py rename to examples/tic_tac_toe/__init__.py diff --git a/examples/tictactoe/ascii_board.py b/examples/tic_tac_toe/ascii_board.py similarity index 100% rename from examples/tictactoe/ascii_board.py rename to examples/tic_tac_toe/ascii_board.py diff --git a/examples/tictactoe/enums.py b/examples/tic_tac_toe/enums.py similarity index 100% rename from examples/tictactoe/enums.py rename to examples/tic_tac_toe/enums.py diff --git a/examples/tictactoe/tic_tac_split.py b/examples/tic_tac_toe/tic_tac_split.py similarity index 100% rename from examples/tictactoe/tic_tac_split.py rename to examples/tic_tac_toe/tic_tac_split.py diff --git a/examples/tictactoe/tic_tac_split_test.py b/examples/tic_tac_toe/tic_tac_split_test.py similarity index 100% rename from examples/tictactoe/tic_tac_split_test.py rename to examples/tic_tac_toe/tic_tac_split_test.py diff --git a/examples/tictactoe/tic_tac_toe.py b/examples/tic_tac_toe/tic_tac_toe.py similarity index 100% rename from examples/tictactoe/tic_tac_toe.py rename to examples/tic_tac_toe/tic_tac_toe.py diff --git a/examples/tictactoe/tic_tac_toe_test.py b/examples/tic_tac_toe/tic_tac_toe_test.py similarity index 100% rename from examples/tictactoe/tic_tac_toe_test.py rename to examples/tic_tac_toe/tic_tac_toe_test.py From 1d0817f321fb71975f1c90c13d1cf3547f2af132 Mon Sep 17 00:00:00 2001 From: Peter Solodov Date: Wed, 22 May 2024 16:53:59 -0400 Subject: [PATCH 04/15] Added explanation as to how to run tic tac toe now --- examples/tic_tac_toe/README.md | 7 +++++++ examples/tic_tac_toe/__init__.py | 3 --- 2 files changed, 7 insertions(+), 3 deletions(-) create mode 100644 examples/tic_tac_toe/README.md diff --git a/examples/tic_tac_toe/README.md b/examples/tic_tac_toe/README.md new file mode 100644 index 00000000..56719dfb --- /dev/null +++ b/examples/tic_tac_toe/README.md @@ -0,0 +1,7 @@ +# How to use the game + +This is a command line game. After cloning the Unitary library, change the directory into `examples/`. You can use command line flags to run the game: + +``` +python -m tic_tac_toe.tic_tac_toe +``` diff --git a/examples/tic_tac_toe/__init__.py b/examples/tic_tac_toe/__init__.py index a2e4f4df..2f493751 100644 --- a/examples/tic_tac_toe/__init__.py +++ b/examples/tic_tac_toe/__init__.py @@ -11,6 +11,3 @@ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. -from .tic_tac_toe import TicTacToe, GameInterface -from .tic_tac_split import TicTacSplit -from .enums import TicTacSquare, TicTacResult, TicTacRules From 39a725c7eb11c7ceb2daddf6b5961a3671f3a188 Mon Sep 17 00:00:00 2001 From: Peter Solodov Date: Wed, 22 May 2024 17:29:57 -0400 Subject: [PATCH 05/15] moved quantum rpg --- .../quantum_rpg/README.md | 2 +- .../quantum_rpg/__init__.py | 0 .../quantum_rpg/ascii_art.py | 0 .../quantum_rpg/battle.py | 9 +++------ .../quantum_rpg/battle_test.py | 9 +++++---- .../examples => examples}/quantum_rpg/bb84.py | 6 +++--- .../quantum_rpg/bb84_test.py | 11 ++++++----- .../quantum_rpg/classes.py | 5 +++-- .../quantum_rpg/classes_test.py | 7 ++++--- .../quantum_rpg/encounter.py | 9 ++++----- .../quantum_rpg/encounter_test.py | 13 +++++++------ .../quantum_rpg/enums.py | 0 .../quantum_rpg/exceptions.py | 0 .../final_state_preparation/__init__.py | 0 .../classical_frontier.py | 14 +++++++------- .../final_state_world.py | 8 ++++---- .../final_state_world_test.py | 10 +++++----- .../final_state_preparation/hadamard_hills.py | 16 ++++++++-------- .../final_state_preparation/monsters.py | 6 +++--- .../oxtail_university.py | 18 +++++++++--------- .../quantum_perimeter.py | 4 ++-- .../quantum_rpg/game_state.py | 4 ++-- .../quantum_rpg/game_state_test.py | 5 +++-- .../quantum_rpg/input_helpers.py | 2 +- .../quantum_rpg/input_helpers_test.py | 2 +- .../examples => examples}/quantum_rpg/item.py | 4 ++-- .../quantum_rpg/item_test.py | 3 ++- .../quantum_rpg/main_loop.py | 19 +++++++++---------- .../quantum_rpg/main_loop_test.py | 17 +++++++++-------- .../examples => examples}/quantum_rpg/npcs.py | 5 +++-- .../quantum_rpg/npcs_test.py | 11 ++++++----- .../quantum_rpg/qaracter.py | 6 +++--- .../quantum_rpg/qaracter_test.py | 7 ++++--- .../quantum_rpg/world.py | 7 +++---- .../quantum_rpg/world_test.py | 4 ++-- .../quantum_rpg/xp_utils.py | 7 ++++--- .../quantum_rpg/xp_utils_test.py | 7 ++++--- 37 files changed, 132 insertions(+), 125 deletions(-) rename {unitary/examples => examples}/quantum_rpg/README.md (99%) rename {unitary/examples => examples}/quantum_rpg/__init__.py (100%) rename {unitary/examples => examples}/quantum_rpg/ascii_art.py (100%) rename {unitary/examples => examples}/quantum_rpg/battle.py (97%) rename {unitary/examples => examples}/quantum_rpg/battle_test.py (97%) rename {unitary/examples => examples}/quantum_rpg/bb84.py (97%) rename {unitary/examples => examples}/quantum_rpg/bb84_test.py (94%) rename {unitary/examples => examples}/quantum_rpg/classes.py (97%) rename {unitary/examples => examples}/quantum_rpg/classes_test.py (93%) rename {unitary/examples => examples}/quantum_rpg/encounter.py (87%) rename {unitary/examples => examples}/quantum_rpg/encounter_test.py (87%) rename {unitary/examples => examples}/quantum_rpg/enums.py (100%) rename {unitary/examples => examples}/quantum_rpg/exceptions.py (100%) rename {unitary/examples => examples}/quantum_rpg/final_state_preparation/__init__.py (100%) rename {unitary/examples => examples}/quantum_rpg/final_state_preparation/classical_frontier.py (96%) rename {unitary/examples => examples}/quantum_rpg/final_state_preparation/final_state_world.py (67%) rename {unitary/examples => examples}/quantum_rpg/final_state_preparation/final_state_world_test.py (95%) rename {unitary/examples => examples}/quantum_rpg/final_state_preparation/hadamard_hills.py (97%) rename {unitary/examples => examples}/quantum_rpg/final_state_preparation/monsters.py (91%) rename {unitary/examples => examples}/quantum_rpg/final_state_preparation/oxtail_university.py (97%) rename {unitary/examples => examples}/quantum_rpg/final_state_preparation/quantum_perimeter.py (85%) rename {unitary/examples => examples}/quantum_rpg/game_state.py (97%) rename {unitary/examples => examples}/quantum_rpg/game_state_test.py (95%) rename {unitary/examples => examples}/quantum_rpg/input_helpers.py (98%) rename {unitary/examples => examples}/quantum_rpg/input_helpers_test.py (97%) rename {unitary/examples => examples}/quantum_rpg/item.py (95%) rename {unitary/examples => examples}/quantum_rpg/item_test.py (97%) rename {unitary/examples => examples}/quantum_rpg/main_loop.py (93%) rename {unitary/examples => examples}/quantum_rpg/main_loop_test.py (97%) rename {unitary/examples => examples}/quantum_rpg/npcs.py (98%) rename {unitary/examples => examples}/quantum_rpg/npcs_test.py (94%) rename {unitary/examples => examples}/quantum_rpg/qaracter.py (98%) rename {unitary/examples => examples}/quantum_rpg/qaracter_test.py (97%) rename {unitary/examples => examples}/quantum_rpg/world.py (95%) rename {unitary/examples => examples}/quantum_rpg/world_test.py (97%) rename {unitary/examples => examples}/quantum_rpg/xp_utils.py (95%) rename {unitary/examples => examples}/quantum_rpg/xp_utils_test.py (95%) diff --git a/unitary/examples/quantum_rpg/README.md b/examples/quantum_rpg/README.md similarity index 99% rename from unitary/examples/quantum_rpg/README.md rename to examples/quantum_rpg/README.md index 86fb7dc2..0fbd5bc2 100644 --- a/unitary/examples/quantum_rpg/README.md +++ b/examples/quantum_rpg/README.md @@ -23,7 +23,7 @@ The second part explains the rules and principles behind the Quantum RPG. In order to run the game, clone the github repo, make sure that any requirements are installed with a command such as `pip install -r requirements.txt` -then run `python -m unitary.examples.quantum_rpg.main_loop` +then run `python -m examples.quantum_rpg.main_loop` (you may need to make sure that the Unitary library is in your PYTHONPATH). If you do not have a python environment handy or are not familiar with setting diff --git a/unitary/examples/quantum_rpg/__init__.py b/examples/quantum_rpg/__init__.py similarity index 100% rename from unitary/examples/quantum_rpg/__init__.py rename to examples/quantum_rpg/__init__.py diff --git a/unitary/examples/quantum_rpg/ascii_art.py b/examples/quantum_rpg/ascii_art.py similarity index 100% rename from unitary/examples/quantum_rpg/ascii_art.py rename to examples/quantum_rpg/ascii_art.py diff --git a/unitary/examples/quantum_rpg/battle.py b/examples/quantum_rpg/battle.py similarity index 97% rename from unitary/examples/quantum_rpg/battle.py rename to examples/quantum_rpg/battle.py index 2aca9391..8dbcdd63 100644 --- a/unitary/examples/quantum_rpg/battle.py +++ b/examples/quantum_rpg/battle.py @@ -12,14 +12,11 @@ # See the License for the specific language governing permissions and # limitations under the License. import enum -import io -import sys from typing import List, Optional, Set -import unitary.examples.quantum_rpg.game_state as game_state -import unitary.examples.quantum_rpg.input_helpers as input_helpers -from unitary.examples.quantum_rpg.qaracter import Qaracter -from unitary.examples.quantum_rpg.xp_utils import EncounterXp +from . import game_state, input_helpers +from .qaracter import Qaracter +from .xp_utils import EncounterXp # Size of the player side of battle status, # for text alingment diff --git a/unitary/examples/quantum_rpg/battle_test.py b/examples/quantum_rpg/battle_test.py similarity index 97% rename from unitary/examples/quantum_rpg/battle_test.py rename to examples/quantum_rpg/battle_test.py index 4edfb253..59be39fa 100644 --- a/unitary/examples/quantum_rpg/battle_test.py +++ b/examples/quantum_rpg/battle_test.py @@ -13,10 +13,11 @@ # limitations under the License. import io -import unitary.examples.quantum_rpg.battle as battle -import unitary.examples.quantum_rpg.classes as classes -import unitary.examples.quantum_rpg.game_state as game_state -import unitary.examples.quantum_rpg.npcs as npcs + +from . import battle +from . import classes +from . import game_state +from . import npcs def test_battle(): diff --git a/unitary/examples/quantum_rpg/bb84.py b/examples/quantum_rpg/bb84.py similarity index 97% rename from unitary/examples/quantum_rpg/bb84.py rename to examples/quantum_rpg/bb84.py index 2ac3d86f..adb6d6c1 100644 --- a/unitary/examples/quantum_rpg/bb84.py +++ b/examples/quantum_rpg/bb84.py @@ -15,9 +15,9 @@ import random import re -from unitary.examples.quantum_rpg.game_state import GameState -from unitary.examples.quantum_rpg.item import EXAMINE, TALK, Item -from unitary.examples.quantum_rpg.world import Direction, Location +from .game_state import GameState +from .item import EXAMINE, TALK, Item +from .world import Direction, Location _WORDS = [ diff --git a/unitary/examples/quantum_rpg/bb84_test.py b/examples/quantum_rpg/bb84_test.py similarity index 94% rename from unitary/examples/quantum_rpg/bb84_test.py rename to examples/quantum_rpg/bb84_test.py index 06333993..c4a506eb 100644 --- a/unitary/examples/quantum_rpg/bb84_test.py +++ b/examples/quantum_rpg/bb84_test.py @@ -15,11 +15,12 @@ import io import pytest -import unitary.examples.quantum_rpg.bb84 as bb84 -import unitary.examples.quantum_rpg.game_state as game_state -import unitary.examples.quantum_rpg.input_helpers as input_helpers -import unitary.examples.quantum_rpg.main_loop as main_loop -import unitary.examples.quantum_rpg.world as world + +from . import bb84 +from . import game_state +from . import input_helpers +from . import main_loop +from . import world def test_to_bin(): diff --git a/unitary/examples/quantum_rpg/classes.py b/examples/quantum_rpg/classes.py similarity index 97% rename from unitary/examples/quantum_rpg/classes.py rename to examples/quantum_rpg/classes.py index c22e2da5..10015536 100644 --- a/unitary/examples/quantum_rpg/classes.py +++ b/examples/quantum_rpg/classes.py @@ -12,10 +12,11 @@ # See the License for the specific language governing permissions and # limitations under the License. -from typing import Any, Callable, Dict +from typing import Callable, Dict from unitary import alpha -from unitary.examples.quantum_rpg import qaracter + +from . import qaracter class Engineer(qaracter.Qaracter): diff --git a/unitary/examples/quantum_rpg/classes_test.py b/examples/quantum_rpg/classes_test.py similarity index 93% rename from unitary/examples/quantum_rpg/classes_test.py rename to examples/quantum_rpg/classes_test.py index 99e3b229..0a461efb 100644 --- a/unitary/examples/quantum_rpg/classes_test.py +++ b/examples/quantum_rpg/classes_test.py @@ -13,9 +13,10 @@ # limitations under the License. import unitary.alpha as alpha -import unitary.examples.quantum_rpg.classes as classes -import unitary.examples.quantum_rpg.enums as enums -import unitary.examples.quantum_rpg.qaracter as qaracter + +from . import classes +from . import enums +from . import qaracter def test_engineer(): diff --git a/unitary/examples/quantum_rpg/encounter.py b/examples/quantum_rpg/encounter.py similarity index 87% rename from unitary/examples/quantum_rpg/encounter.py rename to examples/quantum_rpg/encounter.py index 54436885..04680222 100644 --- a/unitary/examples/quantum_rpg/encounter.py +++ b/examples/quantum_rpg/encounter.py @@ -17,11 +17,10 @@ import random -import unitary.examples.quantum_rpg.battle as battle -import unitary.examples.quantum_rpg.game_state as game_state -import unitary.examples.quantum_rpg.qaracter as qaracter -import unitary.examples.quantum_rpg.xp_utils as xp_utils -from typing import Sequence +from . import battle +from . import game_state +from . import qaracter +from . import xp_utils class Encounter: diff --git a/unitary/examples/quantum_rpg/encounter_test.py b/examples/quantum_rpg/encounter_test.py similarity index 87% rename from unitary/examples/quantum_rpg/encounter_test.py rename to examples/quantum_rpg/encounter_test.py index a36fc881..0d647db8 100644 --- a/unitary/examples/quantum_rpg/encounter_test.py +++ b/examples/quantum_rpg/encounter_test.py @@ -15,12 +15,13 @@ import io import unitary.alpha as alpha -import unitary.examples.quantum_rpg.battle as battle -import unitary.examples.quantum_rpg.classes as classes -import unitary.examples.quantum_rpg.encounter as encounter -import unitary.examples.quantum_rpg.game_state as game_state -import unitary.examples.quantum_rpg.npcs as npcs -import unitary.examples.quantum_rpg.xp_utils as xp_utils + +from . import battle +from . import classes +from . import encounter +from . import game_state +from . import npcs +from . import xp_utils def test_trigger(): diff --git a/unitary/examples/quantum_rpg/enums.py b/examples/quantum_rpg/enums.py similarity index 100% rename from unitary/examples/quantum_rpg/enums.py rename to examples/quantum_rpg/enums.py diff --git a/unitary/examples/quantum_rpg/exceptions.py b/examples/quantum_rpg/exceptions.py similarity index 100% rename from unitary/examples/quantum_rpg/exceptions.py rename to examples/quantum_rpg/exceptions.py diff --git a/unitary/examples/quantum_rpg/final_state_preparation/__init__.py b/examples/quantum_rpg/final_state_preparation/__init__.py similarity index 100% rename from unitary/examples/quantum_rpg/final_state_preparation/__init__.py rename to examples/quantum_rpg/final_state_preparation/__init__.py diff --git a/unitary/examples/quantum_rpg/final_state_preparation/classical_frontier.py b/examples/quantum_rpg/final_state_preparation/classical_frontier.py similarity index 96% rename from unitary/examples/quantum_rpg/final_state_preparation/classical_frontier.py rename to examples/quantum_rpg/final_state_preparation/classical_frontier.py index d265b3ee..ae33ea6e 100644 --- a/unitary/examples/quantum_rpg/final_state_preparation/classical_frontier.py +++ b/examples/quantum_rpg/final_state_preparation/classical_frontier.py @@ -13,17 +13,17 @@ # limitations under the License. import unitary.alpha as alpha -from unitary.examples.quantum_rpg.classes import Engineer -from unitary.examples.quantum_rpg.encounter import Encounter -from unitary.examples.quantum_rpg.exceptions import UntimelyDeathException -from unitary.examples.quantum_rpg.game_state import GameState -from unitary.examples.quantum_rpg.final_state_preparation.monsters import ( +from ..classes import Engineer +from ..encounter import Encounter +from ..exceptions import UntimelyDeathException +from ..game_state import GameState +from .monsters import ( green_foam, blue_foam, red_foam, ) -from unitary.examples.quantum_rpg.item import EXAMINE, TALK, Item -from unitary.examples.quantum_rpg.world import Direction, Location +from ..item import EXAMINE, TALK, Item +from ..world import Direction, Location RICHARD = Item( keyword_actions=[ diff --git a/unitary/examples/quantum_rpg/final_state_preparation/final_state_world.py b/examples/quantum_rpg/final_state_preparation/final_state_world.py similarity index 67% rename from unitary/examples/quantum_rpg/final_state_preparation/final_state_world.py rename to examples/quantum_rpg/final_state_preparation/final_state_world.py index 43c3c700..2c9e65f5 100644 --- a/unitary/examples/quantum_rpg/final_state_preparation/final_state_world.py +++ b/examples/quantum_rpg/final_state_preparation/final_state_world.py @@ -13,10 +13,10 @@ # limitations under the License. """Module to combine all the zones of the RPG world into one list.""" -import unitary.examples.quantum_rpg.final_state_preparation.classical_frontier as classical_frontier -import unitary.examples.quantum_rpg.final_state_preparation.oxtail_university as oxtail_university -import unitary.examples.quantum_rpg.final_state_preparation.hadamard_hills as hadamard_hills -import unitary.examples.quantum_rpg.final_state_preparation.quantum_perimeter as quantum_perimeter +from . import classical_frontier +from . import oxtail_university +from . import hadamard_hills +from . import quantum_perimeter WORLD = [ diff --git a/unitary/examples/quantum_rpg/final_state_preparation/final_state_world_test.py b/examples/quantum_rpg/final_state_preparation/final_state_world_test.py similarity index 95% rename from unitary/examples/quantum_rpg/final_state_preparation/final_state_world_test.py rename to examples/quantum_rpg/final_state_preparation/final_state_world_test.py index aa8fd028..e6b1a071 100644 --- a/unitary/examples/quantum_rpg/final_state_preparation/final_state_world_test.py +++ b/examples/quantum_rpg/final_state_preparation/final_state_world_test.py @@ -15,11 +15,11 @@ import pytest import io -import unitary.examples.quantum_rpg.classes as classes -import unitary.examples.quantum_rpg.exceptions as exceptions -import unitary.examples.quantum_rpg.game_state as game_state -import unitary.examples.quantum_rpg.final_state_preparation.final_state_world as final_state -from unitary.examples.quantum_rpg.world import Direction, World +from .. import classes +from .. import exceptions +from .. import game_state +from ..final_state_preparation import final_state_world as final_state +from ..world import Direction, World OPPOSITE_DIR = { diff --git a/unitary/examples/quantum_rpg/final_state_preparation/hadamard_hills.py b/examples/quantum_rpg/final_state_preparation/hadamard_hills.py similarity index 97% rename from unitary/examples/quantum_rpg/final_state_preparation/hadamard_hills.py rename to examples/quantum_rpg/final_state_preparation/hadamard_hills.py index 34220d78..2f3dcf5d 100644 --- a/unitary/examples/quantum_rpg/final_state_preparation/hadamard_hills.py +++ b/examples/quantum_rpg/final_state_preparation/hadamard_hills.py @@ -13,14 +13,14 @@ # limitations under the License. import unitary.alpha as alpha -from unitary.examples.quantum_rpg.bb84 import ALICE, BOB, DOOR -from unitary.examples.quantum_rpg.encounter import Encounter -from unitary.examples.quantum_rpg.game_state import GameState -from unitary.examples.quantum_rpg.npcs import SchrodingerCat -from unitary.examples.quantum_rpg.item import EXAMINE, TALK, Item -from unitary.examples.quantum_rpg.world import Direction, Location -from unitary.examples.quantum_rpg.xp_utils import EncounterXp -from unitary.examples.quantum_rpg.final_state_preparation.monsters import ( +from ..bb84 import ALICE, BOB, DOOR +from ..encounter import Encounter +from ..game_state import GameState +from ..npcs import SchrodingerCat +from ..item import EXAMINE, TALK, Item +from ..world import Direction, Location +from ..xp_utils import EncounterXp +from .monsters import ( blue_foam, red_foam, purple_foam, diff --git a/unitary/examples/quantum_rpg/final_state_preparation/monsters.py b/examples/quantum_rpg/final_state_preparation/monsters.py similarity index 91% rename from unitary/examples/quantum_rpg/final_state_preparation/monsters.py rename to examples/quantum_rpg/final_state_preparation/monsters.py index 540d05eb..3a30ad4d 100644 --- a/unitary/examples/quantum_rpg/final_state_preparation/monsters.py +++ b/examples/quantum_rpg/final_state_preparation/monsters.py @@ -13,9 +13,9 @@ # limitations under the License. import unitary.alpha as alpha -from unitary.examples.quantum_rpg.encounter import Encounter -from unitary.examples.quantum_rpg.npcs import BlueFoam, GreenFoam, RedFoam, PurpleFoam -from unitary.examples.quantum_rpg.xp_utils import EncounterXp +from ..encounter import Encounter +from ..npcs import BlueFoam, GreenFoam, RedFoam, PurpleFoam +from ..xp_utils import EncounterXp _BLUE_XP = EncounterXp( [ diff --git a/unitary/examples/quantum_rpg/final_state_preparation/oxtail_university.py b/examples/quantum_rpg/final_state_preparation/oxtail_university.py similarity index 97% rename from unitary/examples/quantum_rpg/final_state_preparation/oxtail_university.py rename to examples/quantum_rpg/final_state_preparation/oxtail_university.py index bf30232f..dab84240 100644 --- a/unitary/examples/quantum_rpg/final_state_preparation/oxtail_university.py +++ b/examples/quantum_rpg/final_state_preparation/oxtail_university.py @@ -13,24 +13,24 @@ # limitations under the License. import unitary.alpha as alpha -from unitary.examples.quantum_rpg.classes import Engineer -from unitary.examples.quantum_rpg.encounter import Encounter -from unitary.examples.quantum_rpg.game_state import GameState -from unitary.examples.quantum_rpg.input_helpers import get_user_input_qaracter_name -from unitary.examples.quantum_rpg.final_state_preparation.monsters import ( +from ..classes import Engineer +from ..encounter import Encounter +from ..game_state import GameState +from ..input_helpers import get_user_input_qaracter_name +from .monsters import ( green_foam, blue_foam, ) -from unitary.examples.quantum_rpg.item import EXAMINE, TALK, Item -from unitary.examples.quantum_rpg.npcs import ( +from ..item import EXAMINE, TALK, Item +from ..npcs import ( BlueFoam, GreenFoam, RedFoam, PurpleFoam, Observer, ) -from unitary.examples.quantum_rpg.xp_utils import EncounterXp -from unitary.examples.quantum_rpg.world import Direction, Location +from ..xp_utils import EncounterXp +from ..world import Direction, Location def _engineer_joins(state: GameState, world) -> str: diff --git a/unitary/examples/quantum_rpg/final_state_preparation/quantum_perimeter.py b/examples/quantum_rpg/final_state_preparation/quantum_perimeter.py similarity index 85% rename from unitary/examples/quantum_rpg/final_state_preparation/quantum_perimeter.py rename to examples/quantum_rpg/final_state_preparation/quantum_perimeter.py index e1f76f99..8b0c726f 100644 --- a/unitary/examples/quantum_rpg/final_state_preparation/quantum_perimeter.py +++ b/examples/quantum_rpg/final_state_preparation/quantum_perimeter.py @@ -1,5 +1,5 @@ -from unitary.examples.quantum_rpg.item import EXAMINE, Item -from unitary.examples.quantum_rpg.world import Direction, Location +from ..item import EXAMINE, Item +from ..world import Direction, Location CONSTRUCTION_SIGN = Item( keyword_actions=[ diff --git a/unitary/examples/quantum_rpg/game_state.py b/examples/quantum_rpg/game_state.py similarity index 97% rename from unitary/examples/quantum_rpg/game_state.py rename to examples/quantum_rpg/game_state.py index f4a79e45..2f04fabe 100644 --- a/unitary/examples/quantum_rpg/game_state.py +++ b/examples/quantum_rpg/game_state.py @@ -17,8 +17,8 @@ import io import sys -import unitary.examples.quantum_rpg.input_helpers as input_helpers -import unitary.examples.quantum_rpg.qaracter as qaracter +from . import input_helpers +from . import qaracter _SAVE_DELIMITER = ";" diff --git a/unitary/examples/quantum_rpg/game_state_test.py b/examples/quantum_rpg/game_state_test.py similarity index 95% rename from unitary/examples/quantum_rpg/game_state_test.py rename to examples/quantum_rpg/game_state_test.py index e7f03ee6..b744e161 100644 --- a/unitary/examples/quantum_rpg/game_state_test.py +++ b/examples/quantum_rpg/game_state_test.py @@ -13,8 +13,9 @@ # limitations under the License. import unitary.alpha as alpha -import unitary.examples.quantum_rpg.qaracter as qaracter -import unitary.examples.quantum_rpg.game_state as game_state + +from . import qaracter +from . import game_state def test_serialization() -> None: diff --git a/unitary/examples/quantum_rpg/input_helpers.py b/examples/quantum_rpg/input_helpers.py similarity index 98% rename from unitary/examples/quantum_rpg/input_helpers.py rename to examples/quantum_rpg/input_helpers.py index f4649624..6f3f5829 100644 --- a/unitary/examples/quantum_rpg/input_helpers.py +++ b/examples/quantum_rpg/input_helpers.py @@ -4,7 +4,7 @@ import sys -from unitary.examples.quantum_rpg import qaracter +from . import qaracter _USER_INPUT = Callable[[str], str] _INVALID_MESSAGE = "Invalid number selected." diff --git a/unitary/examples/quantum_rpg/input_helpers_test.py b/examples/quantum_rpg/input_helpers_test.py similarity index 97% rename from unitary/examples/quantum_rpg/input_helpers_test.py rename to examples/quantum_rpg/input_helpers_test.py index 86339245..549f62e8 100644 --- a/unitary/examples/quantum_rpg/input_helpers_test.py +++ b/examples/quantum_rpg/input_helpers_test.py @@ -2,7 +2,7 @@ import pytest -from unitary.examples.quantum_rpg import input_helpers +from . import input_helpers def test_get_user_input_function(): diff --git a/unitary/examples/quantum_rpg/item.py b/examples/quantum_rpg/item.py similarity index 95% rename from unitary/examples/quantum_rpg/item.py rename to examples/quantum_rpg/item.py index 27ddf511..860a5319 100644 --- a/unitary/examples/quantum_rpg/item.py +++ b/examples/quantum_rpg/item.py @@ -1,7 +1,7 @@ -from typing import Any, Callable, Optional, Sequence, Tuple, TYPE_CHECKING, Union import re +from typing import Any, Callable, Optional, Sequence, Tuple, Union -import unitary.examples.quantum_rpg.game_state as game_state +from . import game_state # Common synonyms for action keywords for different objects: EXAMINE = ["read", "look", "examine", "investigate", "search"] diff --git a/unitary/examples/quantum_rpg/item_test.py b/examples/quantum_rpg/item_test.py similarity index 97% rename from unitary/examples/quantum_rpg/item_test.py rename to examples/quantum_rpg/item_test.py index a2956d5c..72960176 100644 --- a/unitary/examples/quantum_rpg/item_test.py +++ b/examples/quantum_rpg/item_test.py @@ -1,5 +1,6 @@ import re -import unitary.examples.quantum_rpg.item as item + +from . import item def test_item_str(): diff --git a/unitary/examples/quantum_rpg/main_loop.py b/examples/quantum_rpg/main_loop.py similarity index 93% rename from unitary/examples/quantum_rpg/main_loop.py rename to examples/quantum_rpg/main_loop.py index f1ade2ad..18fc87fc 100644 --- a/unitary/examples/quantum_rpg/main_loop.py +++ b/examples/quantum_rpg/main_loop.py @@ -17,16 +17,15 @@ import io import sys -import unitary.examples.quantum_rpg.ascii_art as ascii_art -import unitary.examples.quantum_rpg.battle as battle -import unitary.examples.quantum_rpg.classes as classes -import unitary.examples.quantum_rpg.exceptions as exceptions -import unitary.examples.quantum_rpg.game_state as game_state -import unitary.examples.quantum_rpg.input_helpers as input_helpers -import unitary.examples.quantum_rpg.encounter as encounter -import unitary.examples.quantum_rpg.world as world -import unitary.examples.quantum_rpg.xp_utils as xp_utils -import unitary.examples.quantum_rpg.final_state_preparation.final_state_world as final_state_world +from . import ascii_art +from . import battle +from . import classes +from . import exceptions +from . import game_state +from . import input_helpers +from . import world +from . import xp_utils +from .final_state_preparation import final_state_world class Command(enum.Enum): diff --git a/unitary/examples/quantum_rpg/main_loop_test.py b/examples/quantum_rpg/main_loop_test.py similarity index 97% rename from unitary/examples/quantum_rpg/main_loop_test.py rename to examples/quantum_rpg/main_loop_test.py index 4cbbe870..182e126b 100644 --- a/unitary/examples/quantum_rpg/main_loop_test.py +++ b/examples/quantum_rpg/main_loop_test.py @@ -16,14 +16,15 @@ from typing import cast import unitary.alpha as alpha -import unitary.examples.quantum_rpg.ascii_art as ascii_art -import unitary.examples.quantum_rpg.classes as classes -import unitary.examples.quantum_rpg.encounter as encounter -import unitary.examples.quantum_rpg.game_state as game_state -import unitary.examples.quantum_rpg.item as item -import unitary.examples.quantum_rpg.main_loop as main_loop -import unitary.examples.quantum_rpg.npcs as npcs -import unitary.examples.quantum_rpg.world as world + +from . import ascii_art +from . import classes +from . import encounter +from . import game_state +from . import item +from . import main_loop +from . import npcs +from . import world _COUNTER = "counter" diff --git a/unitary/examples/quantum_rpg/npcs.py b/examples/quantum_rpg/npcs.py similarity index 98% rename from unitary/examples/quantum_rpg/npcs.py rename to examples/quantum_rpg/npcs.py index 14b195e3..740bc2d5 100644 --- a/unitary/examples/quantum_rpg/npcs.py +++ b/examples/quantum_rpg/npcs.py @@ -16,8 +16,9 @@ import random import unitary.alpha as alpha -import unitary.examples.quantum_rpg.enums as enums -import unitary.examples.quantum_rpg.qaracter as qaracter + +from . import enums +from . import qaracter # QUANTOPEDIA ENTRIES diff --git a/unitary/examples/quantum_rpg/npcs_test.py b/examples/quantum_rpg/npcs_test.py similarity index 94% rename from unitary/examples/quantum_rpg/npcs_test.py rename to examples/quantum_rpg/npcs_test.py index 596b5902..1ae3014a 100644 --- a/unitary/examples/quantum_rpg/npcs_test.py +++ b/examples/quantum_rpg/npcs_test.py @@ -13,11 +13,12 @@ # limitations under the License. import unitary.alpha as alpha -import unitary.examples.quantum_rpg.battle as battle -import unitary.examples.quantum_rpg.classes as classes -import unitary.examples.quantum_rpg.enums as enums -import unitary.examples.quantum_rpg.game_state as game_state -import unitary.examples.quantum_rpg.npcs as npcs + +from . import battle +from . import classes +from . import enums +from . import game_state +from . import npcs def test_observer(): diff --git a/unitary/examples/quantum_rpg/qaracter.py b/examples/quantum_rpg/qaracter.py similarity index 98% rename from unitary/examples/quantum_rpg/qaracter.py rename to examples/quantum_rpg/qaracter.py index 00bfa197..17cb86b8 100644 --- a/unitary/examples/quantum_rpg/qaracter.py +++ b/examples/quantum_rpg/qaracter.py @@ -18,7 +18,7 @@ import cirq from unitary import alpha -from unitary.examples.quantum_rpg import enums +from . import enums _GATE_DELIMITER = "," _FIELD_DELIMITER = "#" @@ -219,10 +219,10 @@ def from_save_file( class_name = lines[1] # Avoid circular import - import unitary.examples.quantum_rpg.classes + from . import classes class_tuples = inspect.getmembers( - sys.modules["unitary.examples.quantum_rpg.classes"], inspect.isclass + sys.modules["quantum_rpg.classes"], inspect.isclass ) new_cls = cls for cls_name, cls_type in class_tuples: diff --git a/unitary/examples/quantum_rpg/qaracter_test.py b/examples/quantum_rpg/qaracter_test.py similarity index 97% rename from unitary/examples/quantum_rpg/qaracter_test.py rename to examples/quantum_rpg/qaracter_test.py index a9fb8d14..5e9c0737 100644 --- a/unitary/examples/quantum_rpg/qaracter_test.py +++ b/examples/quantum_rpg/qaracter_test.py @@ -13,9 +13,10 @@ # limitations under the License. import unitary.alpha as alpha -import unitary.examples.quantum_rpg.classes as classes -import unitary.examples.quantum_rpg.enums as enums -import unitary.examples.quantum_rpg.qaracter as qaracter + +from . import classes +from . import enums +from . import qaracter def test_initialization() -> None: diff --git a/unitary/examples/quantum_rpg/world.py b/examples/quantum_rpg/world.py similarity index 95% rename from unitary/examples/quantum_rpg/world.py rename to examples/quantum_rpg/world.py index c7fb30ce..02d206e5 100644 --- a/unitary/examples/quantum_rpg/world.py +++ b/examples/quantum_rpg/world.py @@ -12,12 +12,11 @@ # See the License for the specific language governing permissions and # limitations under the License. -from typing import Callable, Dict, List, Optional, Sequence - import dataclasses -import unitary.examples.quantum_rpg.encounter as encounter -import unitary.examples.quantum_rpg.item as item import enum +from typing import Dict, List, Optional, Sequence + +from . import encounter, item class Direction(enum.Enum): diff --git a/unitary/examples/quantum_rpg/world_test.py b/examples/quantum_rpg/world_test.py similarity index 97% rename from unitary/examples/quantum_rpg/world_test.py rename to examples/quantum_rpg/world_test.py index 6fb95d9c..b51a706a 100644 --- a/unitary/examples/quantum_rpg/world_test.py +++ b/examples/quantum_rpg/world_test.py @@ -12,8 +12,8 @@ # See the License for the specific language governing permissions and # limitations under the License. -import unitary.examples.quantum_rpg.item as item -import unitary.examples.quantum_rpg.world as world +from . import item +from . import world EXAMPLE_WORLD = [ diff --git a/unitary/examples/quantum_rpg/xp_utils.py b/examples/quantum_rpg/xp_utils.py similarity index 95% rename from unitary/examples/quantum_rpg/xp_utils.py rename to examples/quantum_rpg/xp_utils.py index c5488c4e..c5a6674e 100644 --- a/unitary/examples/quantum_rpg/xp_utils.py +++ b/examples/quantum_rpg/xp_utils.py @@ -19,9 +19,10 @@ import sys import unitary.alpha as alpha -import unitary.examples.quantum_rpg.game_state as game_state -import unitary.examples.quantum_rpg.input_helpers as input_helpers -import unitary.examples.quantum_rpg.qaracter as qaracter + +from . import game_state +from . import input_helpers +from . import qaracter class EncounterXp: diff --git a/unitary/examples/quantum_rpg/xp_utils_test.py b/examples/quantum_rpg/xp_utils_test.py similarity index 95% rename from unitary/examples/quantum_rpg/xp_utils_test.py rename to examples/quantum_rpg/xp_utils_test.py index bc79286f..79672d43 100644 --- a/unitary/examples/quantum_rpg/xp_utils_test.py +++ b/examples/quantum_rpg/xp_utils_test.py @@ -17,9 +17,10 @@ import cirq import unitary.alpha as alpha -import unitary.examples.quantum_rpg.classes as classes -import unitary.examples.quantum_rpg.game_state as game_state -import unitary.examples.quantum_rpg.xp_utils as xp_utils + +from . import classes +from . import game_state +from . import xp_utils def test_choose(): From ced46d03e8226c1e0dc1f95d05172a28fd1734b2 Mon Sep 17 00:00:00 2001 From: Peter Solodov Date: Wed, 22 May 2024 17:40:24 -0400 Subject: [PATCH 06/15] moved Chinese chess --- examples/quantum_chinese_chess/README.md | 7 +++++++ .../quantum_chinese_chess/__init__.py | 0 .../quantum_chinese_chess/board.py | 6 +++--- .../quantum_chinese_chess/board_test.py | 8 ++++---- .../quantum_chinese_chess/chess.py | 6 +++--- .../quantum_chinese_chess/chess_test.py | 8 ++++---- .../quantum_chinese_chess/enums.py | 0 .../quantum_chinese_chess/enums_test.py | 2 +- .../quantum_chinese_chess/move.py | 4 ++-- .../quantum_chinese_chess/move_test.py | 14 ++++++++------ .../quantum_chinese_chess/piece.py | 2 +- .../quantum_chinese_chess/piece_test.py | 7 ++++--- .../quantum_chinese_chess/test_utils.py | 12 +++++++----- examples/quantum_rpg/README.md | 2 +- examples/tic_tac_toe/README.md | 4 ++-- 15 files changed, 47 insertions(+), 35 deletions(-) create mode 100644 examples/quantum_chinese_chess/README.md rename {unitary/examples => examples}/quantum_chinese_chess/__init__.py (100%) rename {unitary/examples => examples}/quantum_chinese_chess/board.py (98%) rename {unitary/examples => examples}/quantum_chinese_chess/board_test.py (97%) rename {unitary/examples => examples}/quantum_chinese_chess/chess.py (99%) rename {unitary/examples => examples}/quantum_chinese_chess/chess_test.py (98%) rename {unitary/examples => examples}/quantum_chinese_chess/enums.py (100%) rename {unitary/examples => examples}/quantum_chinese_chess/enums_test.py (95%) rename {unitary/examples => examples}/quantum_chinese_chess/move.py (99%) rename {unitary/examples => examples}/quantum_chinese_chess/move_test.py (99%) rename {unitary/examples => examples}/quantum_chinese_chess/piece.py (97%) rename {unitary/examples => examples}/quantum_chinese_chess/piece_test.py (94%) rename {unitary/examples => examples}/quantum_chinese_chess/test_utils.py (96%) diff --git a/examples/quantum_chinese_chess/README.md b/examples/quantum_chinese_chess/README.md new file mode 100644 index 00000000..badb4c69 --- /dev/null +++ b/examples/quantum_chinese_chess/README.md @@ -0,0 +1,7 @@ +# How to use the game + +After cloning the Unitary library you can use command line flags to run the game: + +``` +python -m examples.quantum_chinese_chess.chess +``` diff --git a/unitary/examples/quantum_chinese_chess/__init__.py b/examples/quantum_chinese_chess/__init__.py similarity index 100% rename from unitary/examples/quantum_chinese_chess/__init__.py rename to examples/quantum_chinese_chess/__init__.py diff --git a/unitary/examples/quantum_chinese_chess/board.py b/examples/quantum_chinese_chess/board.py similarity index 98% rename from unitary/examples/quantum_chinese_chess/board.py rename to examples/quantum_chinese_chess/board.py index ce77b851..87170f9a 100644 --- a/unitary/examples/quantum_chinese_chess/board.py +++ b/examples/quantum_chinese_chess/board.py @@ -14,7 +14,7 @@ import numpy as np from typing import List, Tuple import unitary.alpha as alpha -from unitary.examples.quantum_chinese_chess.enums import ( +from .enums import ( SquareState, Color, Type, @@ -22,8 +22,8 @@ MoveVariant, TerminalType, ) -from unitary.examples.quantum_chinese_chess.piece import Piece -from unitary.examples.quantum_chinese_chess.move import Jump +from .piece import Piece +from .move import Jump # The default initial state of the game. diff --git a/unitary/examples/quantum_chinese_chess/board_test.py b/examples/quantum_chinese_chess/board_test.py similarity index 97% rename from unitary/examples/quantum_chinese_chess/board_test.py rename to examples/quantum_chinese_chess/board_test.py index 20b64f13..93649056 100644 --- a/unitary/examples/quantum_chinese_chess/board_test.py +++ b/examples/quantum_chinese_chess/board_test.py @@ -12,16 +12,16 @@ # See the License for the specific language governing permissions and # limitations under the License. # -from unitary.examples.quantum_chinese_chess.enums import ( +from .enums import ( Language, Color, Type, SquareState, TerminalType, ) -from unitary.examples.quantum_chinese_chess.board import Board -from unitary.examples.quantum_chinese_chess.piece import Piece -from unitary.examples.quantum_chinese_chess.test_utils import ( +from .board import Board +from .piece import Piece +from .test_utils import ( locations_to_bitboard, assert_samples_in, assert_sample_distribution, diff --git a/unitary/examples/quantum_chinese_chess/chess.py b/examples/quantum_chinese_chess/chess.py similarity index 99% rename from unitary/examples/quantum_chinese_chess/chess.py rename to examples/quantum_chinese_chess/chess.py index e0c35ebe..34543f6e 100644 --- a/unitary/examples/quantum_chinese_chess/chess.py +++ b/examples/quantum_chinese_chess/chess.py @@ -12,8 +12,8 @@ # See the License for the specific language governing permissions and # limitations under the License. from typing import Tuple, List -from unitary.examples.quantum_chinese_chess.board import Board -from unitary.examples.quantum_chinese_chess.enums import ( +from .board import Board +from .enums import ( Language, GameState, Type, @@ -22,7 +22,7 @@ MoveVariant, TerminalType, ) -from unitary.examples.quantum_chinese_chess.move import ( +from .move import ( Jump, SplitJump, MergeJump, diff --git a/unitary/examples/quantum_chinese_chess/chess_test.py b/examples/quantum_chinese_chess/chess_test.py similarity index 98% rename from unitary/examples/quantum_chinese_chess/chess_test.py rename to examples/quantum_chinese_chess/chess_test.py index ba307107..50f08c30 100644 --- a/unitary/examples/quantum_chinese_chess/chess_test.py +++ b/examples/quantum_chinese_chess/chess_test.py @@ -14,16 +14,16 @@ import pytest import io import sys -from unitary.examples.quantum_chinese_chess.test_utils import ( +from .test_utils import ( set_board, assert_sample_distribution, locations_to_bitboard, assert_samples_in, ) from unitary import alpha -from unitary.examples.quantum_chinese_chess.chess import QuantumChineseChess -from unitary.examples.quantum_chinese_chess.piece import Piece -from unitary.examples.quantum_chinese_chess.enums import ( +from .chess import QuantumChineseChess +from .piece import Piece +from .enums import ( Language, Color, Type, diff --git a/unitary/examples/quantum_chinese_chess/enums.py b/examples/quantum_chinese_chess/enums.py similarity index 100% rename from unitary/examples/quantum_chinese_chess/enums.py rename to examples/quantum_chinese_chess/enums.py diff --git a/unitary/examples/quantum_chinese_chess/enums_test.py b/examples/quantum_chinese_chess/enums_test.py similarity index 95% rename from unitary/examples/quantum_chinese_chess/enums_test.py rename to examples/quantum_chinese_chess/enums_test.py index 2f77c6e4..2dd047d4 100644 --- a/unitary/examples/quantum_chinese_chess/enums_test.py +++ b/examples/quantum_chinese_chess/enums_test.py @@ -11,7 +11,7 @@ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. -from unitary.examples.quantum_chinese_chess.enums import Type, Color, Language +from .enums import Type, Color, Language def test_type_of(): diff --git a/unitary/examples/quantum_chinese_chess/move.py b/examples/quantum_chinese_chess/move.py similarity index 99% rename from unitary/examples/quantum_chinese_chess/move.py rename to examples/quantum_chinese_chess/move.py index f1fddcfa..6f602612 100644 --- a/unitary/examples/quantum_chinese_chess/move.py +++ b/examples/quantum_chinese_chess/move.py @@ -15,8 +15,8 @@ import cirq from unitary import alpha from unitary.alpha.quantum_effect import QuantumEffect -from unitary.examples.quantum_chinese_chess.piece import Piece -from unitary.examples.quantum_chinese_chess.enums import MoveType, MoveVariant, Type +from .piece import Piece +from .enums import MoveType, MoveVariant, Type # TODO(): now the class is no longer the base class of all chess moves. Maybe convert this class diff --git a/unitary/examples/quantum_chinese_chess/move_test.py b/examples/quantum_chinese_chess/move_test.py similarity index 99% rename from unitary/examples/quantum_chinese_chess/move_test.py rename to examples/quantum_chinese_chess/move_test.py index ec901de5..27e38273 100644 --- a/unitary/examples/quantum_chinese_chess/move_test.py +++ b/examples/quantum_chinese_chess/move_test.py @@ -11,20 +11,22 @@ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. -from unitary.examples.quantum_chinese_chess.move import * -from unitary.examples.quantum_chinese_chess.board import Board -from unitary.examples.quantum_chinese_chess.piece import Piece +from typing import List + import pytest from unitary import alpha -from typing import List -from unitary.examples.quantum_chinese_chess.enums import ( + +from .move import * +from .board import Board +from .piece import Piece +from .enums import ( MoveType, MoveVariant, SquareState, Type, Color, ) -from unitary.examples.quantum_chinese_chess.test_utils import ( +from .test_utils import ( locations_to_bitboard, assert_samples_in, assert_sample_distribution, diff --git a/unitary/examples/quantum_chinese_chess/piece.py b/examples/quantum_chinese_chess/piece.py similarity index 97% rename from unitary/examples/quantum_chinese_chess/piece.py rename to examples/quantum_chinese_chess/piece.py index fc6736e8..801a0c75 100644 --- a/unitary/examples/quantum_chinese_chess/piece.py +++ b/examples/quantum_chinese_chess/piece.py @@ -13,7 +13,7 @@ # limitations under the License. from typing import Optional from unitary.alpha import QuantumObject -from unitary.examples.quantum_chinese_chess.enums import ( +from .enums import ( SquareState, Language, Color, diff --git a/unitary/examples/quantum_chinese_chess/piece_test.py b/examples/quantum_chinese_chess/piece_test.py similarity index 94% rename from unitary/examples/quantum_chinese_chess/piece_test.py rename to examples/quantum_chinese_chess/piece_test.py index 650960ea..e5a94139 100644 --- a/unitary/examples/quantum_chinese_chess/piece_test.py +++ b/examples/quantum_chinese_chess/piece_test.py @@ -12,14 +12,15 @@ # See the License for the specific language governing permissions and # limitations under the License. # -from unitary.examples.quantum_chinese_chess.enums import ( +from unitary.alpha import QuantumWorld + +from .enums import ( SquareState, Language, Color, Type, ) -from unitary.examples.quantum_chinese_chess.piece import Piece -from unitary.alpha import QuantumWorld +from .piece import Piece def test_symbol(): diff --git a/unitary/examples/quantum_chinese_chess/test_utils.py b/examples/quantum_chinese_chess/test_utils.py similarity index 96% rename from unitary/examples/quantum_chinese_chess/test_utils.py rename to examples/quantum_chinese_chess/test_utils.py index 8f4c6f24..a4033a58 100644 --- a/unitary/examples/quantum_chinese_chess/test_utils.py +++ b/examples/quantum_chinese_chess/test_utils.py @@ -11,14 +11,16 @@ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. -from unitary.alpha import QuantumObject, QuantumWorld -from unitary.examples.quantum_chinese_chess.enums import SquareState, Type, Color -from unitary.examples.quantum_chinese_chess.board import Board -from unitary.examples.quantum_chinese_chess.piece import Piece -from unitary import alpha from typing import List, Dict from collections import defaultdict + from scipy.stats import chisquare +from unitary import alpha +from unitary.alpha import QuantumObject, QuantumWorld + +from .enums import SquareState, Type, Color +from .board import Board +from .piece import Piece _EMPTY_FEN = "9/9/9/9/9/9/9/9/9/9 w---1" diff --git a/examples/quantum_rpg/README.md b/examples/quantum_rpg/README.md index 0fbd5bc2..bc96d2c9 100644 --- a/examples/quantum_rpg/README.md +++ b/examples/quantum_rpg/README.md @@ -23,7 +23,7 @@ The second part explains the rules and principles behind the Quantum RPG. In order to run the game, clone the github repo, make sure that any requirements are installed with a command such as `pip install -r requirements.txt` -then run `python -m examples.quantum_rpg.main_loop` + * [ ] then run `python -m examples.quantum_rpg.main_loop` (you may need to make sure that the Unitary library is in your PYTHONPATH). If you do not have a python environment handy or are not familiar with setting diff --git a/examples/tic_tac_toe/README.md b/examples/tic_tac_toe/README.md index 56719dfb..29012645 100644 --- a/examples/tic_tac_toe/README.md +++ b/examples/tic_tac_toe/README.md @@ -1,7 +1,7 @@ # How to use the game -This is a command line game. After cloning the Unitary library, change the directory into `examples/`. You can use command line flags to run the game: +After cloning the Unitary library you can use command line flags to run the game: ``` -python -m tic_tac_toe.tic_tac_toe +python -m examples.tic_tac_toe.tic_tac_toe ``` From 0f4f903579022c711fb5c4b266c26cd914c46d18 Mon Sep 17 00:00:00 2001 From: Peter Solodov Date: Wed, 22 May 2024 17:42:12 -0400 Subject: [PATCH 07/15] removed unnedede __init__ --- unitary/examples/__init__.py | 14 -------------- 1 file changed, 14 deletions(-) delete mode 100644 unitary/examples/__init__.py diff --git a/unitary/examples/__init__.py b/unitary/examples/__init__.py deleted file mode 100644 index 64dd7178..00000000 --- a/unitary/examples/__init__.py +++ /dev/null @@ -1,14 +0,0 @@ -# Copyright 2022 Google -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# https://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -# From 02ec75e1ad20834b2c776caaa0f4a230ca90cae5 Mon Sep 17 00:00:00 2001 From: Peter Solodov Date: Wed, 22 May 2024 17:43:45 -0400 Subject: [PATCH 08/15] fixed doc --- examples/quantum_rpg/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/quantum_rpg/README.md b/examples/quantum_rpg/README.md index bc96d2c9..0fbd5bc2 100644 --- a/examples/quantum_rpg/README.md +++ b/examples/quantum_rpg/README.md @@ -23,7 +23,7 @@ The second part explains the rules and principles behind the Quantum RPG. In order to run the game, clone the github repo, make sure that any requirements are installed with a command such as `pip install -r requirements.txt` - * [ ] then run `python -m examples.quantum_rpg.main_loop` +then run `python -m examples.quantum_rpg.main_loop` (you may need to make sure that the Unitary library is in your PYTHONPATH). If you do not have a python environment handy or are not familiar with setting From 47edb011b16ff6cea2c70d97e75e766533745081 Mon Sep 17 00:00:00 2001 From: Peter Solodov Date: Wed, 26 Jun 2024 17:45:06 -0400 Subject: [PATCH 09/15] sort exists for printing fixes #179 --- examples/quantum_rpg/main_loop_test.py | 6 +++--- examples/quantum_rpg/world.py | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/examples/quantum_rpg/main_loop_test.py b/examples/quantum_rpg/main_loop_test.py index 182e126b..4ef57daa 100644 --- a/examples/quantum_rpg/main_loop_test.py +++ b/examples/quantum_rpg/main_loop_test.py @@ -597,7 +597,7 @@ def test_main_begin(): A bent sign sticks out of the ground at an angle. -Exits: south, north. +Exits: north, south. """ ) @@ -622,7 +622,7 @@ def test_main_load(): by some mirage. To proceed, you will need to move around this strange occurance. -Exits: south, east, west. +Exits: east, south, west. """ ) @@ -655,7 +655,7 @@ def test_main_bad_save_file(): by some mirage. To proceed, you will need to move around this strange occurance. -Exits: south, east, west. +Exits: east, south, west. """ ) diff --git a/examples/quantum_rpg/world.py b/examples/quantum_rpg/world.py index 02d206e5..47d9d0a4 100644 --- a/examples/quantum_rpg/world.py +++ b/examples/quantum_rpg/world.py @@ -66,7 +66,7 @@ class Location: items: Optional[List[item.Item]] = None def _exits(self) -> str: - return ", ".join([ex.value for ex in self.exits]) + "." + return ", ".join(sorted(ex.value for ex in self.exits)) + "." def _item_str(self) -> str: if not self.items: From ade798744cb3880bbf6605f0b849ca0115ec17b0 Mon Sep 17 00:00:00 2001 From: Peter Solodov Date: Wed, 26 Jun 2024 23:09:36 -0400 Subject: [PATCH 10/15] implemented quantopedia command Fixes #177 --- examples/quantum_rpg/main_loop.py | 56 +++++++++++++++++++++----- examples/quantum_rpg/main_loop_test.py | 4 +- examples/quantum_rpg/npcs.py | 21 ++++++---- 3 files changed, 63 insertions(+), 18 deletions(-) diff --git a/examples/quantum_rpg/main_loop.py b/examples/quantum_rpg/main_loop.py index 18fc87fc..186433a3 100644 --- a/examples/quantum_rpg/main_loop.py +++ b/examples/quantum_rpg/main_loop.py @@ -12,10 +12,11 @@ # See the License for the specific language governing permissions and # limitations under the License. -from typing import List, Optional, Sequence import enum import io import sys +import textwrap +from typing import List, Optional, Sequence from . import ascii_art from . import battle @@ -23,38 +24,59 @@ from . import exceptions from . import game_state from . import input_helpers +from . import npcs from . import world from . import xp_utils from .final_state_preparation import final_state_world -class Command(enum.Enum): - """Command parsing utility. +class Error(Exception): + """Base class for locally defined exceptions.""" + pass - Currently only supports 'quit' but future expansion planned. - """ + +class AmbiguousCommandError(Error): + """Raised when entered command is ambiguous.""" + pass + + +class Command(enum.Enum): + """Enumeration of available commands.""" LOAD = "load" LOOK = "look" STATUS = "status" SAVE = "save" HELP = "help" + QUANTOPEDIA = "quantopedia" QUIT = "quit" @classmethod def parse(cls, s: str) -> Optional["Command"]: - """Parses a string as a Direction. + """Parses a string as a command. Allows prefixes, like 'e' to be parsed as EAST. """ if not s: return None lower_s = s.lower() - for cmd in Command: + candidates = [] + for cmd in cls: if cmd.value.startswith(lower_s): - return cmd + candidates.append(cmd) + if len(candidates) == 1: + return candidates[0] + if len(candidates) > 1: + raise AmbiguousCommandError return None + @classmethod + def help(cls) -> str: + cmds = ["Available commands:"] + for cmd in Command: + cmds.append(f" {cmd.value}") + return "\n".join(cmds) + class MainLoop: def __init__(self, world: world.World, state: game_state.GameState): @@ -135,7 +157,14 @@ def loop(self, user_input: Optional[Sequence[str]] = None) -> None: print(msg, file=self.file) print_room_description = False continue - input_cmd = Command.parse(current_input) + try: + input_cmd = Command.parse(current_input) + except AmbiguousCommandError: + print(f"Ambiguous command '{current_input}'.", + Command.help(), + file=self.file) + print_room_description = False + continue if input_cmd == Command.QUIT: return elif input_cmd == Command.STATUS: @@ -144,6 +173,15 @@ def loop(self, user_input: Optional[Sequence[str]] = None) -> None: elif input_cmd == Command.HELP: print(ascii_art.HELP, file=self.file) print_room_description = False + elif input_cmd == Command.QUANTOPEDIA: + print(file=self.file) + for npc_class in npcs.Npc.__subclasses__(): + print(npc_class.__name__, file=self.file) + print( + textwrap.indent(npc_class.quantopedia_entry(), " "), + file=self.file) + print(file=self.file) + print_room_description = False elif input_cmd == Command.LOAD: print( "Paste the save file here to load the game from that point.", diff --git a/examples/quantum_rpg/main_loop_test.py b/examples/quantum_rpg/main_loop_test.py index 4ef57daa..93c9e620 100644 --- a/examples/quantum_rpg/main_loop_test.py +++ b/examples/quantum_rpg/main_loop_test.py @@ -105,8 +105,8 @@ def example_world(): def test_parse_commands() -> None: assert main_loop.Command.parse("x") is None - assert main_loop.Command.parse("q") is main_loop.Command.QUIT - assert main_loop.Command.parse("Q") is main_loop.Command.QUIT + assert main_loop.Command.parse("qui") is main_loop.Command.QUIT + assert main_loop.Command.parse("Qui") is main_loop.Command.QUIT assert main_loop.Command.parse("Quit") is main_loop.Command.QUIT assert main_loop.Command.parse("quit") is main_loop.Command.QUIT diff --git a/examples/quantum_rpg/npcs.py b/examples/quantum_rpg/npcs.py index 740bc2d5..ac0c2701 100644 --- a/examples/quantum_rpg/npcs.py +++ b/examples/quantum_rpg/npcs.py @@ -91,7 +91,8 @@ def quantopedia_index(self) -> int: """ return 0 - def quantopedia_entry(self) -> str: + @classmethod + def quantopedia_entry(cls) -> str: """Explanatory text to the players about the NPC.""" return "Nothing known about this NPC." @@ -112,7 +113,8 @@ def act_on_enemy_qubit(self, enemy_qubit, action_choice, **kwargs) -> str: def quantopedia_index(self) -> int: return _FOAM_QUANTOPEDIA - def quantopedia_entry(self) -> str: + @classmethod + def quantopedia_entry(cls) -> str: return ( "Observers are known to frequent quantum events.\n" "They will measure qubits in order to find out their values." @@ -137,7 +139,8 @@ def act_on_enemy_qubit(self, enemy_qubit, action_choice, **kwargs) -> str: def quantopedia_index(self) -> int: return _FOAM_QUANTOPEDIA - def quantopedia_entry(self) -> str: + @classmethod + def quantopedia_entry(cls) -> str: return ( "Blue foam are the simplest kind of quantum errors. Blue foam\n" "are usually found in the |0> state and can be measured.\n" @@ -165,7 +168,8 @@ def act_on_enemy_qubit(self, enemy_qubit, action_choice, **kwargs) -> str: def quantopedia_index(self) -> int: return _FOAM_QUANTOPEDIA - def quantopedia_entry(self) -> str: + @classmethod + def quantopedia_entry(cls) -> str: return ( "Green foam are a simple kind of quantum error. Green foam\n" "are usually found in the |0> state and can be measured immediately.\n" @@ -195,7 +199,8 @@ def act_on_enemy_qubit(self, enemy_qubit, action_choice, **kwargs) -> str: def quantopedia_index(self) -> int: return _FOAM_QUANTOPEDIA - def quantopedia_entry(self) -> str: + @classmethod + def quantopedia_entry(cls) -> str: return ( "Red foam are a slightly more dangerous type of quantum error.\n" "They are usually found in the |1> state and must be flipped\n" @@ -224,7 +229,8 @@ def act_on_enemy_qubit(self, enemy_qubit, action_choice, **kwargs) -> str: def quantopedia_index(self) -> int: return _FOAM_QUANTOPEDIA - def quantopedia_entry(self) -> str: + @classmethod + def quantopedia_entry(cls) -> str: return ( "Purple foam are a combination of red and blue form.\n" "They are found in a |+> state which is a combination of\n" @@ -268,7 +274,8 @@ def act_on_enemy_qubit(self, enemy_qubit, action_choice, **kwargs) -> str: def quantopedia_index(self) -> int: return _HILLS_QUANTOPEDIA - def quantopedia_entry(self) -> str: + @classmethod + def quantopedia_entry(cls) -> str: return ( "Schrödinger's cat are found in a superposition of zero and one.\n" "This cat contains multiple qubits that are entangled so that all\n" From 15fa4bcc1124cda4a8a5c52d35b6765dddfb00ed Mon Sep 17 00:00:00 2001 From: Peter Solodov Date: Tue, 2 Jul 2024 16:31:55 -0400 Subject: [PATCH 11/15] Make Quit case-sensitive Quit command is matched first before user input is lower-cased. --- examples/quantum_rpg/bb84_test.py | 4 +-- examples/quantum_rpg/main_loop.py | 9 ++++++- examples/quantum_rpg/main_loop_test.py | 36 ++++++++++++++------------ 3 files changed, 29 insertions(+), 20 deletions(-) diff --git a/examples/quantum_rpg/bb84_test.py b/examples/quantum_rpg/bb84_test.py index c4a506eb..e9eee03d 100644 --- a/examples/quantum_rpg/bb84_test.py +++ b/examples/quantum_rpg/bb84_test.py @@ -81,7 +81,7 @@ def test_alice_bob(): "look display", "look keyboard", "type aaaa", - "quit", + "Quit", ], file=io.StringIO(), ) @@ -159,7 +159,7 @@ def test_alice_bob(): state.file = file = io.StringIO() key = bb84.solve_bb84(state.state_dict["alice"], state.state_dict["bob"]) state.get_user_input = input_helpers.get_user_input_function( - [f"type {key}", "north", "south", "quit"] + [f"type {key}", "north", "south", "Quit"] ) loop = main_loop.MainLoop(example_world, state) loop.loop() diff --git a/examples/quantum_rpg/main_loop.py b/examples/quantum_rpg/main_loop.py index 186433a3..66be33ea 100644 --- a/examples/quantum_rpg/main_loop.py +++ b/examples/quantum_rpg/main_loop.py @@ -49,7 +49,7 @@ class Command(enum.Enum): SAVE = "save" HELP = "help" QUANTOPEDIA = "quantopedia" - QUIT = "quit" + QUIT = "Quit" @classmethod def parse(cls, s: str) -> Optional["Command"]: @@ -59,9 +59,16 @@ def parse(cls, s: str) -> Optional["Command"]: """ if not s: return None + # Quit is a special case, it's case-sensitive. It's matched first before + # input is lower-cased and matched against every other command. + if cls.QUIT.value.startswith(s): + return cls.QUIT lower_s = s.lower() candidates = [] for cmd in cls: + # Quit has already been handled above. + if cmd == cls.QUIT: + continue if cmd.value.startswith(lower_s): candidates.append(cmd) if len(candidates) == 1: diff --git a/examples/quantum_rpg/main_loop_test.py b/examples/quantum_rpg/main_loop_test.py index 93c9e620..869f76ab 100644 --- a/examples/quantum_rpg/main_loop_test.py +++ b/examples/quantum_rpg/main_loop_test.py @@ -105,16 +105,18 @@ def example_world(): def test_parse_commands() -> None: assert main_loop.Command.parse("x") is None - assert main_loop.Command.parse("qui") is main_loop.Command.QUIT + assert main_loop.Command.parse("Q") is main_loop.Command.QUIT assert main_loop.Command.parse("Qui") is main_loop.Command.QUIT assert main_loop.Command.parse("Quit") is main_loop.Command.QUIT - assert main_loop.Command.parse("quit") is main_loop.Command.QUIT + assert main_loop.Command.parse("Quitt") is None + assert main_loop.Command.parse("quit") is None + assert main_loop.Command.parse("load") is main_loop.Command.LOAD def test_simple_main_loop() -> None: c = classes.Analyst("Mensing") state = game_state.GameState( - party=[c], user_input=["look", "quit"], file=io.StringIO() + party=[c], user_input=["look", "Quit"], file=io.StringIO() ) loop = main_loop.MainLoop(state=state, world=world.World(example_world())) loop.loop() @@ -140,7 +142,7 @@ def test_simple_main_loop() -> None: def test_empty_command() -> None: state = game_state.GameState( - party=[], user_input=["", "", "quit"], file=io.StringIO() + party=[], user_input=["", "", "Quit"], file=io.StringIO() ) loop = main_loop.MainLoop(state=state, world=world.World(example_world())) loop.loop() @@ -168,10 +170,10 @@ def test_status() -> None: c2.add_quantum_effect(alpha.Superposition(), 1) c2.add_quantum_effect(alpha.Flip(effect_fraction=0.5), 2) state = game_state.GameState( - party=[c, c2], user_input=["status", "quit"], file=io.StringIO() + party=[c, c2], user_input=["status", "Quit"], file=io.StringIO() ) loop = main_loop.MainLoop(state=state, world=world.World(example_world())) - loop.loop(user_input=["quit"]) + loop.loop(user_input=["Quit"]) assert ( cast(io.StringIO, state.file).getvalue().replace("\t", " ").strip() == r""" @@ -197,7 +199,7 @@ def test_status() -> None: def test_do_simple_move() -> None: c = classes.Analyst("Mensing") state = game_state.GameState( - party=[c], user_input=["e", "read sign", "w", "quit"], file=io.StringIO() + party=[c], user_input=["e", "read sign", "w", "Quit"], file=io.StringIO() ) loop = main_loop.MainLoop(world.World(example_world()), state) loop.loop() @@ -235,7 +237,7 @@ def test_do_simple_move() -> None: def test_save() -> None: c = classes.Analyst("Mensing") state = game_state.GameState( - party=[c], user_input=["e", "save", "quit"], file=io.StringIO() + party=[c], user_input=["e", "save", "Quit"], file=io.StringIO() ) loop = main_loop.MainLoop(world.World(example_world()), state) loop.loop() @@ -267,7 +269,7 @@ def test_load() -> None: c = classes.Analyst("Broglie") state = game_state.GameState( party=[c], - user_input=["load", "2;1;Mensing#Analyst#1", "quit"], + user_input=["load", "2;1;Mensing#Analyst#1", "Quit"], file=io.StringIO(), ) loop = main_loop.MainLoop(state=state, world=world.World(example_world())) @@ -299,7 +301,7 @@ def test_bad_load() -> None: c = classes.Analyst("Broglie") state = game_state.GameState( party=[c], - user_input=["load", "", "quit"], + user_input=["load", "", "Quit"], file=io.StringIO(), ) loop = main_loop.MainLoop(state=state, world=world.World(example_world())) @@ -330,7 +332,7 @@ def test_battle() -> None: c = classes.Analyst("Mensing") state = game_state.GameState( party=[c], - user_input=["e", "south", "m", "1", "1", "", "quit"], + user_input=["e", "south", "m", "1", "1", "", "Quit"], file=io.StringIO(), ) loop = main_loop.MainLoop(state=state, world=world.World(example_world())) @@ -391,7 +393,7 @@ def test_lost_battle() -> None: c = classes.Engineer("Mensing") state = game_state.GameState( party=[c], - user_input=["e", "south", "x", "1", "1", "", "quit"], + user_input=["e", "south", "x", "1", "1", "", "Quit"], file=io.StringIO(), ) assert state.party[0].name == "Mensing" @@ -460,7 +462,7 @@ def test_escaped_battle(): c.add_quantum_effect(alpha.Flip(), 1) state = game_state.GameState( party=[c], - user_input=["e", "south", "x", "1", "1", "", "quit"], + user_input=["e", "south", "x", "1", "1", "", "Quit"], file=io.StringIO(), ) assert state.party[0].name == "Mensing" @@ -525,7 +527,7 @@ def test_item_function(): c = classes.Analyst("michalakis") state = game_state.GameState( party=[c], - user_input=["press button", "press button", "press button", "quit"], + user_input=["press button", "press button", "press button", "Quit"], file=io.StringIO(), ) loop = main_loop.MainLoop(world.World(example_world()), state) @@ -566,7 +568,7 @@ def test_main_help(): def test_main_begin(): state = game_state.GameState( - party=[], user_input=["1", "nova", "n", "quit"], file=io.StringIO() + party=[], user_input=["1", "nova", "n", "Quit"], file=io.StringIO() ) loop = main_loop.main(state) @@ -606,7 +608,7 @@ def test_main_begin(): def test_main_load(): state = game_state.GameState( party=[], - user_input=["2", "classical3;1;Doug#Analyst#1", "quit"], + user_input=["2", "classical3;1;Doug#Analyst#1", "Quit"], file=io.StringIO(), ) loop = main_loop.main(state) @@ -631,7 +633,7 @@ def test_main_load(): def test_main_bad_save_file(): state = game_state.GameState( party=[], - user_input=["2", "", "2", "classical3;1;Doug#Analyst#1", "quit"], + user_input=["2", "", "2", "classical3;1;Doug#Analyst#1", "Quit"], file=io.StringIO(), ) loop = main_loop.main(state) From 31bf745e476724153468c6846ff3841c2ff56091 Mon Sep 17 00:00:00 2001 From: madcpf Date: Sat, 6 Jul 2024 22:29:33 -0700 Subject: [PATCH 12/15] Add generalized Hadamard gate --- unitary/alpha/quantum_effect_test.py | 2 +- unitary/alpha/quantum_world.py | 6 ++--- unitary/alpha/qubit_effects.py | 13 ++++++---- unitary/alpha/qubit_effects_test.py | 20 +++++++++++++++- unitary/alpha/qudit_gates.py | 36 ++++++++++++++++++++++++++++ unitary/alpha/qudit_gates_test.py | 18 ++++++++++++++ 6 files changed, 85 insertions(+), 10 deletions(-) diff --git a/unitary/alpha/quantum_effect_test.py b/unitary/alpha/quantum_effect_test.py index 2b2623ca..04044e57 100644 --- a/unitary/alpha/quantum_effect_test.py +++ b/unitary/alpha/quantum_effect_test.py @@ -91,4 +91,4 @@ def test_no_qutrits(compile_to_qubits): piece = alpha.QuantumObject("q0", 2) board.add_object(piece) with pytest.raises(ValueError, match="Cannot apply effect to qids"): - alpha.Superposition()(piece) + alpha.Phase()(piece) diff --git a/unitary/alpha/quantum_world.py b/unitary/alpha/quantum_world.py index 416ac0a7..1f272785 100644 --- a/unitary/alpha/quantum_world.py +++ b/unitary/alpha/quantum_world.py @@ -116,9 +116,9 @@ def copy(self) -> "QuantumWorld": for remap in self.qubit_remapping_dict: new_dict = {} for key_obj, value_obj in remap.items(): - new_dict[new_world.get_object_by_name(key_obj.name)] = ( - new_world.get_object_by_name(value_obj.name) - ) + new_dict[ + new_world.get_object_by_name(key_obj.name) + ] = new_world.get_object_by_name(value_obj.name) new_world.qubit_remapping_dict.append(new_dict) new_world.qubit_remapping_dict_length = self.qubit_remapping_dict_length.copy() return new_world diff --git a/unitary/alpha/qubit_effects.py b/unitary/alpha/qubit_effects.py index 4776e42b..f786046b 100644 --- a/unitary/alpha/qubit_effects.py +++ b/unitary/alpha/qubit_effects.py @@ -19,6 +19,7 @@ import cirq from unitary.alpha.quantum_effect import QuantumEffect +from unitary.alpha.qudit_gates import QuditHadamardGate class Phase(QuantumEffect): @@ -67,14 +68,16 @@ def __eq__(self, other): class Superposition(QuantumEffect): - """Takes a qubit in a basis state into a superposition.""" - - def num_dimension(self) -> Optional[int]: - return 2 + """Transforms a qubit (or qudit) in a basis state into a (equal, in terms of + absolute magnitude) superposition of all basis states. + """ def effect(self, *objects): for q in objects: - yield cirq.H(q.qubit) + if q.qubit.dimension == 2: + yield cirq.H(q.qubit) + else: + yield QuditHadamardGate(dimension=q.qubit.dimension)(q.qubit) def __eq__(self, other): return isinstance(other, Superposition) or NotImplemented diff --git a/unitary/alpha/qubit_effects_test.py b/unitary/alpha/qubit_effects_test.py index 4d35e6ca..90d627e1 100644 --- a/unitary/alpha/qubit_effects_test.py +++ b/unitary/alpha/qubit_effects_test.py @@ -13,6 +13,7 @@ # limitations under the License. # +import enum import pytest import cirq @@ -20,6 +21,12 @@ import unitary.alpha as alpha +class StopLight(enum.Enum): + RED = 0 + YELLOW = 1 + GREEN = 2 + + @pytest.mark.parametrize("compile_to_qubits", [False, True]) @pytest.mark.parametrize("simulator", [cirq.Simulator, alpha.SparseSimulator]) def test_flip(simulator, compile_to_qubits): @@ -78,7 +85,7 @@ def test_partial_phase(simulator, compile_to_qubits): @pytest.mark.parametrize("compile_to_qubits", [False, True]) @pytest.mark.parametrize("simulator", [cirq.Simulator, alpha.SparseSimulator]) -def test_superposition(simulator, compile_to_qubits): +def test_qubit_superposition(simulator, compile_to_qubits): board = alpha.QuantumWorld(sampler=simulator(), compile_to_qubits=compile_to_qubits) piece = alpha.QuantumObject("t", 0) board.add_object(piece) @@ -87,6 +94,17 @@ def test_superposition(simulator, compile_to_qubits): assert str(alpha.Superposition()) == "Superposition" +def test_qudit_superposition(): + board = alpha.QuantumWorld(sampler=cirq.Simulator(), compile_to_qubits=False) + piece = alpha.QuantumObject("t", StopLight.GREEN) + board.add_object(piece) + alpha.Superposition()(piece) + results = board.peek([piece], count=100) + assert any(result == [StopLight.RED] for result in results) + assert any(result == [StopLight.YELLOW] for result in results) + assert any(result == [StopLight.GREEN] for result in results) + + @pytest.mark.parametrize("compile_to_qubits", [False, True]) @pytest.mark.parametrize("simulator", [cirq.Simulator, alpha.SparseSimulator]) def test_move(simulator, compile_to_qubits): diff --git a/unitary/alpha/qudit_gates.py b/unitary/alpha/qudit_gates.py index 628d9a45..a2446b4b 100644 --- a/unitary/alpha/qudit_gates.py +++ b/unitary/alpha/qudit_gates.py @@ -202,3 +202,39 @@ def _circuit_diagram_info_(self, args): return cirq.CircuitDiagramInfo( wire_symbols=("iSwap", "iSwap"), exponent=self._diagram_exponent(args) ) + + +class QuditHadamardGate(cirq.Gate): + """Performs a Hadamard operation on the given qudit. + This is the equivalent of a H gate for qubits. When applied to a given pure state, + the state will be transformed to a (equal, in terms of absolute magnitude) superposition of + all pure states. + Args: + dimension: dimension of the qudits, for instance, + a dimension of 3 would be a qutrit. + """ + + def __init__(self, dimension: int): + self.dimension = dimension + + def _qid_shape_(self): + return (self.dimension,) + + def _unitary_(self): + arr = ( + 1.0 + / np.sqrt(self.dimension) + * np.ones((self.dimension, self.dimension), dtype=np.complex64) + ) + w = np.exp(1j * 2 * np.pi / self.dimension) + # Note: this unitary matrice always has first row and first column elements equal to one, + # so we only do calculation for rest of the elements. + for i in range(1, self.dimension): + for j in range(1, self.dimension): + arr[i, j] *= w ** (i * j) + return arr + + def _circuit_diagram_info_(self, args): + return cirq.CircuitDiagramInfo( + wire_symbols=("H", "H"), exponent=self._diagram_exponent(args) + ) diff --git a/unitary/alpha/qudit_gates_test.py b/unitary/alpha/qudit_gates_test.py index a228da33..bb34dafc 100644 --- a/unitary/alpha/qudit_gates_test.py +++ b/unitary/alpha/qudit_gates_test.py @@ -180,6 +180,9 @@ def test_control_of_0_x(dest: int): qudit_gates.QuditISwapPowGate(3), qudit_gates.QuditSwapPowGate(3, exponent=0.5), qudit_gates.QuditISwapPowGate(3, exponent=0.5), + qudit_gates.QuditHadamardGate(2), + qudit_gates.QuditHadamardGate(3), + qudit_gates.QuditHadamardGate(4), ], ) def test_gates_are_unitary(gate: cirq.Gate): @@ -294,3 +297,18 @@ def test_sqrt_iswap(q0: int, q1: int): results = sim.run(c, repetitions=1000) assert np.all(results.measurements["m0"] == q1) assert np.all(results.measurements["m1"] == q0) + + +@pytest.mark.parametrize( + "d, q0", [(2, 0), (2, 1), (3, 0), (3, 1), (3, 2), (4, 0), (4, 1), (4, 2), (4, 3)] +) +def test_hadamard(d: int, q0: int): + qutrit0 = cirq.NamedQid("q0", dimension=d) + c = cirq.Circuit() + c.append(qudit_gates.QuditPlusGate(d, addend=q0)(qutrit0)) + c.append(qudit_gates.QuditHadamardGate(d)(qutrit0)) + c.append(cirq.measure(qutrit0, key="m0")) + sim = cirq.Simulator() + results = sim.run(c, repetitions=1000) + for each_possible_outcome in range(d): + assert np.any(results.measurements["m0"] == each_possible_outcome) From a761e9c2b90a122c20304d82ee32d119c8b0ee27 Mon Sep 17 00:00:00 2001 From: madcpf Date: Sat, 6 Jul 2024 22:36:06 -0700 Subject: [PATCH 13/15] reformat --- unitary/alpha/quantum_world.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/unitary/alpha/quantum_world.py b/unitary/alpha/quantum_world.py index 1f272785..416ac0a7 100644 --- a/unitary/alpha/quantum_world.py +++ b/unitary/alpha/quantum_world.py @@ -116,9 +116,9 @@ def copy(self) -> "QuantumWorld": for remap in self.qubit_remapping_dict: new_dict = {} for key_obj, value_obj in remap.items(): - new_dict[ - new_world.get_object_by_name(key_obj.name) - ] = new_world.get_object_by_name(value_obj.name) + new_dict[new_world.get_object_by_name(key_obj.name)] = ( + new_world.get_object_by_name(value_obj.name) + ) new_world.qubit_remapping_dict.append(new_dict) new_world.qubit_remapping_dict_length = self.qubit_remapping_dict_length.copy() return new_world From 7b74de0e94744bf7d7600b9c7aaae768c38523a5 Mon Sep 17 00:00:00 2001 From: madcpf Date: Sat, 6 Jul 2024 22:51:32 -0700 Subject: [PATCH 14/15] Update qudit gates and tests --- unitary/alpha/qudit_effects.py | 8 ++-- unitary/alpha/qudit_gates.py | 64 +++++++++++++++---------------- unitary/alpha/qudit_gates_test.py | 33 ++++++++++++++-- 3 files changed, 64 insertions(+), 41 deletions(-) diff --git a/unitary/alpha/qudit_effects.py b/unitary/alpha/qudit_effects.py index 632a724f..a8c29e1c 100644 --- a/unitary/alpha/qudit_effects.py +++ b/unitary/alpha/qudit_effects.py @@ -23,7 +23,7 @@ class Cycle(QuantumEffect): - """Cycles a qubit from |0> to |1>, |1> to |2>, etc. + """Cycles a qudit from |0> to |1>, |1> to |2>, etc. Essentially adds `addend` to the state, where `addend` is the parameter supplied at creation. @@ -55,11 +55,11 @@ def __init__(self, dimension, num=1): class Flip(QuantumEffect): - """Flips two states of a qubit, leaving all other states unchanged. + """Flips two states of a qudit, leaving all other states unchanged. - For instance, Flip(state0 = 0, state1=2) is a qutrit effect + For instance, Flip(state0 = 0, state1 = 2) is a qutrit effect that flips |0> to |2>, |2> to |0> and leaves - |1> alone. This is also sometimes referred to as the X_0_2 gate. + |1> alone. This is also sometimes referred as the X_02 gate. For a partial flip, use the `effect_fraction` argument. Note that this is only applied so far on qubits and not yet for diff --git a/unitary/alpha/qudit_gates.py b/unitary/alpha/qudit_gates.py index a2446b4b..93240b57 100644 --- a/unitary/alpha/qudit_gates.py +++ b/unitary/alpha/qudit_gates.py @@ -24,8 +24,8 @@ class QuditXGate(cirq.Gate): 'destination_state' parameter that is passed in. All other states are left alone. - For example, QuditXGate(dimension=3, state=1) - is a X_01 gate that leaves the |2〉 state alone. + For example, QuditXGate(dimension=3, source_state=0, destination_state=1) + is a X_01 gate that leaves the |2〉state alone. """ def __init__( @@ -34,17 +34,21 @@ def __init__( self.dimension = dimension self.source_state = source_state self.destination_state = destination_state + if self.source_state >= self.dimension: + raise ValueError("Source state must be smaller than dimension.") + if self.destination_state >= self.dimension: + raise ValueError("Destination state must be smaller than dimension.") def _qid_shape_(self): return (self.dimension,) def _unitary_(self): - arr = np.zeros((self.dimension, self.dimension)) - arr[self.source_state, self.destination_state] = 1 - arr[self.destination_state, self.source_state] = 1 - for i in range(self.dimension): - if i != self.source_state and i != self.destination_state: - arr[i, i] = 1 + arr = np.eye(self.dimension) + if self.source_state != self.destination_state: + arr[self.source_state, self.source_state] = 0 + arr[self.destination_state, self.destination_state] = 0 + arr[self.source_state, self.destination_state] = 1 + arr[self.destination_state, self.source_state] = 1 return arr def _circuit_diagram_info_(self, args): @@ -52,10 +56,9 @@ def _circuit_diagram_info_(self, args): class QuditPlusGate(cirq.Gate): - """Cycles all the states using a permutation gate. - - This gate adds a number to each state. For instance, - `QuditPlusGate(dimension=3, addend=1)` + """Cycles all the states by `addend` using a permutation gate. + This gate adds a number to each state. For instance,`QuditPlusGate(dimension=3, addend=1)` + will cycle state vector (a, b, c) to (c, a, b), and will cycle state |0> to |1>, |1> to |2>, |2> to |0>. """ def __init__(self, dimension: int, addend: int = 1): @@ -78,19 +81,16 @@ def _circuit_diagram_info_(self, args): class QuditControlledXGate(cirq.Gate): """A Qudit controlled-X gate. - This gate takes the dimension of the qudit (e.g. 3 for qutrits) - as well as the control and destination gates to produce a + This gate takes the dimension of the qudit as well as the control and destination states to produce a controlled-X 2-qudit gate. - Note that there are two parameters for this gate. The first - is the control state, which determines when the X gate on the - second qudit is activated. For instance, if this is set to 2, - then the X gate will be activated when the first qudit is - in the |2> state. - - The state parameter specifies the destination state of the - second qudit. For instance, if set to 1, it will perform a - X_01 gate when activated by the control. + Args: + dimension: dimension of the qudits, for instance, a dimension of 3 would be a qutrit. + control_state: the state of first qudit that when satisfied the X gate on the second qudit will be activated. + For instance, if `control_state` is set to 2, then the X gate will be + activated when the first qudit is in the |2> state. + state: the destination state of the second qudit. For instance, if set to 1, it will perform a + X_01 gate when activated by `control_state`. """ def __init__(self, dimension: int, control_state: int = 1, state: int = 1): @@ -103,14 +103,12 @@ def _qid_shape_(self): def _unitary_(self): size = self.dimension * self.dimension - arr = np.zeros((size, size), dtype=np.complex64) + arr = np.eye(size, dtype=np.complex64) control_block_offset = self.control_state * self.dimension + arr[control_block_offset, control_block_offset] = 0 + arr[control_block_offset + self.state, control_block_offset + self.state] = 0 arr[control_block_offset, control_block_offset + self.state] = 1 arr[control_block_offset + self.state, control_block_offset] = 1 - for x in range(self.dimension): - for y in range(self.dimension): - if x != self.control_state or (y != self.state and y != 0): - arr[x * self.dimension + y, x * self.dimension + y] = 1 return arr @@ -139,14 +137,14 @@ def _qid_shape_(self): def _unitary_(self): size = self.dimension * self.dimension arr = np.zeros((size, size), dtype=np.complex64) + g = np.exp(1j * np.pi * self.exponent / 2) + coeff = -1j * g * np.sin(np.pi * self.exponent / 2) + diag = g * np.cos(np.pi * self.exponent / 2) for x in range(self.dimension): for y in range(self.dimension): if x == y: arr[x * self.dimension + y][x * self.dimension + y] = 1 continue - g = np.exp(1j * np.pi * self.exponent / 2) - coeff = -1j * g * np.sin(np.pi * self.exponent / 2) - diag = g * np.cos(np.pi * self.exponent / 2) arr[x * self.dimension + y, y * self.dimension + x] = coeff arr[x * self.dimension + y, x * self.dimension + y] = diag return arr @@ -186,13 +184,13 @@ def _qid_shape_(self): def _unitary_(self): size = self.dimension * self.dimension arr = np.zeros((size, size), dtype=np.complex64) + coeff = 1j * np.sin(np.pi * self.exponent / 2) + diag = np.cos(np.pi * self.exponent / 2) for x in range(self.dimension): for y in range(self.dimension): if x == y: arr[x * self.dimension + y][x * self.dimension + y] = 1 continue - coeff = 1j * np.sin(np.pi * self.exponent / 2) - diag = np.cos(np.pi * self.exponent / 2) arr[x * self.dimension + y, y * self.dimension + x] = coeff arr[x * self.dimension + y, x * self.dimension + y] = diag diff --git a/unitary/alpha/qudit_gates_test.py b/unitary/alpha/qudit_gates_test.py index bb34dafc..117459d6 100644 --- a/unitary/alpha/qudit_gates_test.py +++ b/unitary/alpha/qudit_gates_test.py @@ -19,7 +19,7 @@ import unitary.alpha.qudit_gates as qudit_gates -@pytest.mark.parametrize("state", [1, 2]) +@pytest.mark.parametrize("state", [0, 1, 2]) def test_qutrit_x(state: int): qutrit = cirq.NamedQid("a", dimension=3) sim = cirq.Simulator() @@ -38,7 +38,7 @@ def test_qutrit_x(state: int): assert np.all(results.measurements["m"] == 0) -@pytest.mark.parametrize("num_gates", [1, 2, 3, 4, 5, 6]) +@pytest.mark.parametrize("num_gates", [0, 1, 2, 3, 4, 5, 6]) def test_qutrit_plus_one(num_gates: int): qutrit = cirq.NamedQid("a", dimension=3) c = cirq.Circuit() @@ -50,7 +50,7 @@ def test_qutrit_plus_one(num_gates: int): assert np.all(results.measurements["m"] == num_gates % 3) -@pytest.mark.parametrize("num_gates", [1, 2, 3, 4, 5, 6]) +@pytest.mark.parametrize("num_gates", [0, 1, 2, 3, 4, 5, 6]) def test_qutrit_plus_addend(num_gates: int): qutrit = cirq.NamedQid("a", dimension=3) c = cirq.Circuit() @@ -101,7 +101,7 @@ def test_control_x(control: int, dest: int): assert np.all(results.measurements["m1"] == 0) # Control is excited to a non-controlling state and has no effect. - non_active = 2 - control + 1 + non_active = 3 - control c = cirq.Circuit( qudit_gates.QuditXGate(3, 0, non_active)(qutrit0), qudit_gates.QuditControlledXGate(3, control, dest)(qutrit0, qutrit1), @@ -112,6 +112,19 @@ def test_control_x(control: int, dest: int): assert np.all(results.measurements["m0"] == non_active) assert np.all(results.measurements["m1"] == 0) + # 2nd qutrit is excited to a non-dest state and has no effect. + non_active = 3 - dest + c = cirq.Circuit( + qudit_gates.QuditXGate(3, 0, control)(qutrit0), + qudit_gates.QuditXGate(3, 0, non_active)(qutrit1), + qudit_gates.QuditControlledXGate(3, control, dest)(qutrit0, qutrit1), + cirq.measure(qutrit0, key="m0"), + cirq.measure(qutrit1, key="m1"), + ) + results = sim.run(c, repetitions=1000) + assert np.all(results.measurements["m0"] == control) + assert np.all(results.measurements["m1"] == non_active) + @pytest.mark.parametrize("dest", [1, 2]) def test_control_of_0_x(dest: int): @@ -162,6 +175,18 @@ def test_control_of_0_x(dest: int): assert np.all(results.measurements["m0"] == 2) assert np.all(results.measurements["m1"] == 0) + # 2nd qutrit is in the non-dest state and has no effect + non_active = 3 - dest + c = cirq.Circuit( + qudit_gates.QuditXGate(3, 0, non_active)(qutrit1), + qudit_gates.QuditControlledXGate(3, 0, dest)(qutrit0, qutrit1), + cirq.measure(qutrit0, key="m0"), + cirq.measure(qutrit1, key="m1"), + ) + results = sim.run(c, repetitions=1000) + assert np.all(results.measurements["m0"] == 0) + assert np.all(results.measurements["m1"] == non_active) + @pytest.mark.parametrize( "gate", From 1dc6d953ce9110ec932e893d779a9ba880e57c33 Mon Sep 17 00:00:00 2001 From: Doug Strain Date: Mon, 8 Jul 2024 09:37:05 -0700 Subject: [PATCH 15/15] Add documentation for TicTacToe --- examples/tic_tac_toe/README.md | 52 +++++++++++++ examples/tic_tac_toe/ascii_board.py | 113 ---------------------------- examples/tic_tac_toe/enums.py | 16 ++++ examples/tic_tac_toe/tic_tac_toe.py | 17 +++-- 4 files changed, 77 insertions(+), 121 deletions(-) delete mode 100644 examples/tic_tac_toe/ascii_board.py diff --git a/examples/tic_tac_toe/README.md b/examples/tic_tac_toe/README.md index 29012645..ac3304c6 100644 --- a/examples/tic_tac_toe/README.md +++ b/examples/tic_tac_toe/README.md @@ -5,3 +5,55 @@ After cloning the Unitary library you can use command line flags to run the game ``` python -m examples.tic_tac_toe.tic_tac_toe ``` + +## Rules of the game + +There are nine positions on the 3x3 board, labeled a through h. +The object is to get three in a row. Players alternate turns, +placing either an X or an O on each square. + +In the quantum version, there is an additional move for placing +an X or an O on two squares in superposition, called the split move. + +Once the board is full (i.e. each of the squares has either X, +O, or a superposition of them), the board is measured and the +result is determined. + +## Quantum representation + +Each square is represented as a Qutrit. This square can either be: + +* |0> meaning that the square is empty +* |1> meaning that the square has an X +* |2> meaning that the square has an O + +Placing an X or O will do the qutrit version of an "X" gate +on that square. Since these are qutrits (not qubits), the +definition of this gate is that it is swapping the amplitude +of the zero state with either the one or two state +(depending on whether this is an X or O move). +For example, if someone places an O on a square, then placing +an X on that same square has no effect, since the zero state +has zero amplitude. + +Note that there are two variants of the rules with regards to +the split move. In one rule, the qutrit X gate is applied +followed by a square root of iSWAP gate (restricted to either the +|1> or |2> subspace depending on the move). + +In the other variant, the corresponding states |01> and |10> +(or |02> and |20>) are swapped. + +## Code organization + +The qutrit operations (such as the split move) are defined in +`tic_tac_split.py`. These classes can be used as examples of +how to create your own gates and effects using unitary. + +The game itself is defined in `tic_tac_toe.py`. The `GameInterface` +class defines the main game loop and input/output. + +The `TicTacToe` class keeps track of the game state and also +allows you to sample (measure) the board. + +Enums used by the example are stored in `enums.py`. diff --git a/examples/tic_tac_toe/ascii_board.py b/examples/tic_tac_toe/ascii_board.py deleted file mode 100644 index 0bb312ef..00000000 --- a/examples/tic_tac_toe/ascii_board.py +++ /dev/null @@ -1,113 +0,0 @@ -# Copyright 2023 The Unitary Authors -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# https://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -from .enums import TicTacSquare, TicTacResult, TicTacRules -from .tic_tac_toe import TicTacToe -import argparse -import textwrap - -help_str = """\ -In classical TicTacToe, players alternate in putting their token (either an -X or an O) on the squares of a 3x3 board. Each of the 3x3 squares of the board -is labeled by its own letter as follows: - - | | - a | b | c - _____|_____|_____ - | | - d | e | f - _____|_____|_____ - | | - g | h | i - | | - -When it's your turn, to put your token on an empty square just input the -corresponding square's letter. That is, to put your token on the top-right -square, input 'c'. - -In Quantum TicTacToe, you get access to so-called split moves, in which your -token can be put on two squares simultaneously. To input a quantum move, -enter the two letters of the involved squares. For example, a quantum move -putting your token in the top left and bottom right squares has input 'ai'. - -Split moves can be made without restrictions using the default, fully quantum, -ruleset. If you'd like to allow split moves only on empty squares, choose the -minimal quantum ruleset. If you'd like no quantum moves at all, choose the -classical ruleset. Choose different rulesets can be done using the -r option, -see below. -""" - - -def _flip_turn(turn: TicTacSquare): - return TicTacSquare.O if turn == TicTacSquare.X else TicTacSquare.X - - -class AsciiBoard: - def __init__(self, rules: TicTacRules): - self.board = TicTacToe(rules) - - def play(self): - turn = TicTacSquare.X - result = TicTacResult.UNFINISHED - - while result == TicTacResult.UNFINISHED: - print(self.board.print()) - move = input(f"{turn.name} turn to move: ").lower() - if move == "q": - exit() - - try: - result = self.board.move(move, turn) - except Exception as e: - print(e) - continue - - turn = _flip_turn(turn) - - print(f"Result: {result.name}") - print(self.board.print()) - - -if __name__ == "__main__": - parser = argparse.ArgumentParser( - description=textwrap.dedent(help_str), - formatter_class=argparse.RawTextHelpFormatter, - ) - parser.add_argument( - "-r", - dest="rules", - default=3, - type=int, - choices=range(1, 4), - help=textwrap.dedent( - """ - Set the ruleset: - 1: Classical - 2: Minimal Quantum - Allow split moves, but only on empty squares - 3: Fully Quantum (Default) - Allow split moves, no restrictions""" - ), - ) - args = parser.parse_args() - - print( - "Starting a new game of Quantum TicTacToe with " - "ruleset '%s'" % TicTacRules(args.rules - 1) - ) - print("Change the ruleset using the -r option. Or use -h for more help.") - print("Input 'q' to exit.") - - # Start a new game with the chosen ruleset (defaults to FULLY_QUANTUM) - AsciiBoard(TicTacRules(args.rules - 1)).play() diff --git a/examples/tic_tac_toe/enums.py b/examples/tic_tac_toe/enums.py index 14ac512e..43c1a863 100644 --- a/examples/tic_tac_toe/enums.py +++ b/examples/tic_tac_toe/enums.py @@ -17,12 +17,20 @@ class GameMoves(enum.Enum): + """Possible inputs for the ASCII version of tic tac toe.""" + EXIT = "exit" MAP = "map" HELP = "help" class TicTacSquare(enum.Enum): + """Possible states of one tic tac toe square. + + For the quantum version of tic tac toe, these + are represented as qutrits (qubits with three states). + """ + EMPTY = 0 X = 1 O = 2 @@ -35,6 +43,14 @@ def from_result(cls, value: Union[enum.Enum, int]): class TicTacResult(enum.Enum): + """End results of a tic tac toe game. + + Either one side has won or it is a draw. + If the game has continued past the end state, + it is possible both sides have completed a three-in-a-row. + If the game is not complete, the result is UNFINISHED. + """ + UNFINISHED = 0 X_WINS = 1 O_WINS = 2 diff --git a/examples/tic_tac_toe/tic_tac_toe.py b/examples/tic_tac_toe/tic_tac_toe.py index ca51b00d..d483b057 100644 --- a/examples/tic_tac_toe/tic_tac_toe.py +++ b/examples/tic_tac_toe/tic_tac_toe.py @@ -79,6 +79,13 @@ def _result_to_str(result: List[TicTacSquare]) -> str: def eval_board(result: List[TicTacSquare]) -> TicTacResult: + """Determines who has won the tic tac toe board. + + This function checks all the possible three-in-a-row positions + (all cols, all rows, and the two diagonals. Depending on which + player(s) have a three in a row (X's, O's, both, or neither) + returns the result of the tic tac toe board. + """ x_wins = False o_wins = False still_empty = False @@ -90,18 +97,12 @@ def eval_board(result: List[TicTacSquare]) -> TicTacResult: if any(result[check[idx]] == TicTacSquare.EMPTY for idx in range(3)): still_empty = True if x_wins: - if o_wins: - return TicTacResult.BOTH_WIN - else: - return TicTacResult.X_WINS + return TicTacResult.BOTH_WIN if o_wins else TicTacResult.X_WINS else: if o_wins: return TicTacResult.O_WINS else: - if still_empty: - return TicTacResult.UNFINISHED - else: - return TicTacResult.DRAW + return TicTacResult.UNFINISHED if still_empty else TicTacResult.DRAW class TicTacToe: