diff --git a/unitary/alpha/quantum_object_test.py b/unitary/alpha/quantum_object_test.py index 70b76b25..2b06c859 100644 --- a/unitary/alpha/quantum_object_test.py +++ b/unitary/alpha/quantum_object_test.py @@ -47,10 +47,20 @@ def test_add_world_after_state_change(simulator, compile_to_qubits): assert board.peek() == [[1]] -@pytest.mark.parametrize("compile_to_qubits", [False, True]) -def test_qutrit(compile_to_qubits): +@pytest.mark.parametrize( + ("simulator", "compile_to_qubits"), + [ + (cirq.Simulator, False), + (cirq.Simulator, True), + # Cannot use SparseSimulator without `compile_to_qubits` due to issue #78. + (alpha.SparseSimulator, True), + ], +) +def test_qutrit(simulator, compile_to_qubits): piece = alpha.QuantumObject("t", 2) - board = alpha.QuantumWorld(piece, compile_to_qubits=compile_to_qubits) + board = alpha.QuantumWorld( + piece, sampler=simulator(), compile_to_qubits=compile_to_qubits + ) assert board.peek() == [[2]] piece += 1 assert board.peek() == [[0]] @@ -64,15 +74,25 @@ def test_qutrit(compile_to_qubits): assert board.peek() == [[2]] -@pytest.mark.parametrize("compile_to_qubits", [False, True]) -def test_enum(compile_to_qubits): +@pytest.mark.parametrize( + ("simulator", "compile_to_qubits"), + [ + (cirq.Simulator, False), + (cirq.Simulator, True), + # Cannot use SparseSimulator without `compile_to_qubits` due to issue #78. + (alpha.SparseSimulator, True), + ], +) +def test_enum(simulator, compile_to_qubits): class Color(enum.Enum): RED = 0 YELLOW = 1 GREEN = 2 piece = alpha.QuantumObject("t", Color.YELLOW) - board = alpha.QuantumWorld(piece, compile_to_qubits=compile_to_qubits) + board = alpha.QuantumWorld( + piece, sampler=simulator(), compile_to_qubits=compile_to_qubits + ) assert board.peek() == [[Color.YELLOW]] piece += Color.YELLOW assert board.peek() == [[Color.GREEN]] diff --git a/unitary/alpha/quantum_world.py b/unitary/alpha/quantum_world.py index daa00dff..416ac0a7 100644 --- a/unitary/alpha/quantum_world.py +++ b/unitary/alpha/quantum_world.py @@ -36,8 +36,11 @@ class QuantumWorld: This object also has a history so that effects can be undone. This object should be initialized with a sampler that determines - how to evaluate the quantum game state. If not specified, this - defaults to the built-in cirq Simulator. + how to evaluate the quantum game state. If not specified, this + defaults to a noiseless simulator optimized for sparse state vectors. + You may also use e.g. cirq.Simulator, a noiseless simulator using + dense state vectors, which natively supports qudits unlike the sparse + simulator. Setting the `compile_to_qubits` option results in an internal state representation of ancilla qubits for every qudit in the world. That @@ -48,12 +51,14 @@ class QuantumWorld: def __init__( self, objects: Optional[List[QuantumObject]] = None, - sampler=cirq.Simulator(), - compile_to_qubits: bool = False, + sampler: cirq.Sampler = SparseSimulator(), + compile_to_qubits: Optional[bool] = None, ): self.clear() self.sampler = sampler self.use_sparse = isinstance(sampler, SparseSimulator) + if compile_to_qubits is None: + compile_to_qubits = self.use_sparse self.compile_to_qubits = compile_to_qubits if isinstance(objects, QuantumObject): diff --git a/unitary/alpha/quantum_world_test.py b/unitary/alpha/quantum_world_test.py index 508c3901..cd5c368a 100644 --- a/unitary/alpha/quantum_world_test.py +++ b/unitary/alpha/quantum_world_test.py @@ -100,7 +100,9 @@ def test_two_qubits(simulator, compile_to_qubits): def test_two_qutrits(compile_to_qubits): light = alpha.QuantumObject("yellow", StopLight.YELLOW) light2 = alpha.QuantumObject("green", StopLight.GREEN) - board = alpha.QuantumWorld([light, light2], compile_to_qubits=compile_to_qubits) + board = alpha.QuantumWorld( + [light, light2], sampler=cirq.Simulator(), compile_to_qubits=compile_to_qubits + ) assert board.peek(convert_to_enum=False) == [[1, 2]] assert board.peek() == [[StopLight.YELLOW, StopLight.GREEN]] assert board.peek([light], count=2) == [[StopLight.YELLOW], [StopLight.YELLOW]] @@ -630,19 +632,31 @@ def test_combine_incompatibly_sparse_worlds(compile_to_qubits): world2.combine_with(world1) -@pytest.mark.parametrize("compile_to_qubits", [False, True]) -def test_combine_worlds(compile_to_qubits): +@pytest.mark.parametrize( + ("simulator", "compile_to_qubits"), + [ + (cirq.Simulator, False), + (cirq.Simulator, True), + # Cannot use SparseSimulator without `compile_to_qubits` due to issue #78. + (alpha.SparseSimulator, True), + ], +) +def test_combine_worlds(simulator, compile_to_qubits): l1 = alpha.QuantumObject("l1", Light.GREEN) l2 = alpha.QuantumObject("l2", Light.RED) l3 = alpha.QuantumObject("l3", Light.RED) - world1 = alpha.QuantumWorld([l1, l2, l3], compile_to_qubits=compile_to_qubits) + world1 = alpha.QuantumWorld( + [l1, l2, l3], sampler=simulator(), compile_to_qubits=compile_to_qubits + ) # Split and pop to test post-selection after combining alpha.Split()(l1, l2, l3) result = world1.pop() l4 = alpha.QuantumObject("stop_light", StopLight.YELLOW) - world2 = alpha.QuantumWorld([l4], compile_to_qubits=compile_to_qubits) + world2 = alpha.QuantumWorld( + [l4], sampler=simulator(), compile_to_qubits=compile_to_qubits + ) world2.combine_with(world1) results = world2.peek(count=100) diff --git a/unitary/alpha/qudit_effects_test.py b/unitary/alpha/qudit_effects_test.py index 963ddac6..c2c3d8a2 100644 --- a/unitary/alpha/qudit_effects_test.py +++ b/unitary/alpha/qudit_effects_test.py @@ -15,6 +15,8 @@ import enum import pytest +import cirq + import unitary.alpha as alpha @@ -24,9 +26,17 @@ class StopLight(enum.Enum): GREEN = 2 -@pytest.mark.parametrize("compile_to_qubits", [False, True]) -def test_qudit_cycle(compile_to_qubits): - board = alpha.QuantumWorld(compile_to_qubits=compile_to_qubits) +@pytest.mark.parametrize( + ("simulator", "compile_to_qubits"), + [ + (cirq.Simulator, False), + (cirq.Simulator, True), + # Cannot use SparseSimulator without `compile_to_qubits` due to issue #78. + (alpha.SparseSimulator, True), + ], +) +def test_qudit_cycle(simulator, compile_to_qubits): + board = alpha.QuantumWorld(sampler=simulator(), compile_to_qubits=compile_to_qubits) piece = alpha.QuantumObject("t", StopLight.GREEN) board.add_object(piece) alpha.QuditCycle(3)(piece) @@ -43,9 +53,17 @@ def test_qudit_cycle(compile_to_qubits): assert all(result == [StopLight.YELLOW] for result in results) -@pytest.mark.parametrize("compile_to_qubits", [False, True]) -def test_qudit_flip(compile_to_qubits): - board = alpha.QuantumWorld(compile_to_qubits=compile_to_qubits) +@pytest.mark.parametrize( + ("simulator", "compile_to_qubits"), + [ + (cirq.Simulator, False), + (cirq.Simulator, True), + # Cannot use SparseSimulator without `compile_to_qubits` due to issue #78. + (alpha.SparseSimulator, True), + ], +) +def test_qudit_flip(simulator, compile_to_qubits): + board = alpha.QuantumWorld(sampler=simulator(), compile_to_qubits=compile_to_qubits) piece = alpha.QuantumObject("t", StopLight.GREEN) board.add_object(piece) alpha.QuditFlip(3, 0, 2)(piece) diff --git a/unitary/alpha/sparse_vector_simulator.py b/unitary/alpha/sparse_vector_simulator.py index 67d79a20..9a0d9d66 100644 --- a/unitary/alpha/sparse_vector_simulator.py +++ b/unitary/alpha/sparse_vector_simulator.py @@ -18,8 +18,7 @@ Supports standard unitary Cirq gates, plus a post-selection operator. -Does not work with 3+-state qudits. TODO: integrate with qutrit-to-qubit conversion -when that is available. +Does not work with 3+-state qudits. """ import cirq import numpy as np diff --git a/unitary/examples/quantum_rpg/qaracter.py b/unitary/examples/quantum_rpg/qaracter.py index c48f7f38..00bfa197 100644 --- a/unitary/examples/quantum_rpg/qaracter.py +++ b/unitary/examples/quantum_rpg/qaracter.py @@ -162,7 +162,8 @@ def qar_sheet(self) -> str: self.get_object_by_name(self.quantum_object_name(i)).qubit for i in range(1, self.level + 1) ] - return self.circuit.to_text_diagram(qubit_order=all_qubits) + canonical_circuit = cirq.Circuit(cirq.flatten_op_tree(self.circuit)) + return canonical_circuit.to_text_diagram(qubit_order=all_qubits) def qar_status(self) -> str: """Prints out the qaracter's name/class/level and circuit. diff --git a/unitary/examples/tictactoe/tic_tac_split_test.py b/unitary/examples/tictactoe/tic_tac_split_test.py index e4b1f31c..a26d2117 100644 --- a/unitary/examples/tictactoe/tic_tac_split_test.py +++ b/unitary/examples/tictactoe/tic_tac_split_test.py @@ -99,7 +99,9 @@ def test_tic_tac_split( ): a = alpha.QuantumObject("a", tictactoe.TicTacSquare.EMPTY) b = alpha.QuantumObject("b", tictactoe.TicTacSquare.EMPTY) - board = alpha.QuantumWorld([a, b], compile_to_qubits=compile_to_qubits) + board = alpha.QuantumWorld( + [a, b], sampler=cirq.Simulator(), compile_to_qubits=compile_to_qubits + ) tictactoe.TicTacSplit(mark, ruleset)(a, b) results = board.peek(count=1000) on_a = [mark, tictactoe.TicTacSquare.EMPTY] @@ -114,7 +116,9 @@ 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) - board = alpha.QuantumWorld([a, b, c], compile_to_qubits=compile_to_qubits) + 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) @@ -133,7 +137,9 @@ 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) - board = alpha.QuantumWorld([a, b, c], compile_to_qubits=compile_to_qubits) + 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) @@ -160,7 +166,9 @@ 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) - board = alpha.QuantumWorld([a, b, c], compile_to_qubits=compile_to_qubits) + 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) diff --git a/unitary/examples/tictactoe/tic_tac_toe.py b/unitary/examples/tictactoe/tic_tac_toe.py index 49418f32..0be2af27 100644 --- a/unitary/examples/tictactoe/tic_tac_toe.py +++ b/unitary/examples/tictactoe/tic_tac_toe.py @@ -15,6 +15,8 @@ import sys from typing import Dict, List, TextIO +import cirq + from unitary.alpha import QuantumObject, QuantumWorld from unitary.alpha.qudit_effects import QuditFlip from unitary.examples.tictactoe.enums import ( @@ -146,7 +148,9 @@ def clear(self, run_on_hardware: bool = False) -> None: self.empty_squares.add(name) self.squares[name] = QuantumObject(name, TicTacSquare.EMPTY) self.board = QuantumWorld( - list(self.squares.values()), compile_to_qubits=run_on_hardware + list(self.squares.values()), + sampler=cirq.Simulator(), + compile_to_qubits=run_on_hardware, ) def result(self) -> TicTacResult: @@ -171,7 +175,7 @@ def measure(self) -> None: if self.last_result[idx] == TicTacSquare.EMPTY: self.empty_squares.add(name) self.squares[name] = QuantumObject(name, self.last_result[idx]) - self.board = QuantumWorld(list(self.squares.values())) + self.board = QuantumWorld(list(self.squares.values()), sampler=cirq.Simulator()) def move(self, move: str, mark: TicTacSquare) -> TicTacResult: """Make a move on the TicTacToe board.