Skip to content

Commit

Permalink
Merge pull request #166 from madcpf/empty
Browse files Browse the repository at this point in the history
[Quantum Chinese Chess] Add SplitJump and MergeJump
  • Loading branch information
madcpf authored Nov 29, 2023
2 parents eef96ad + 939ecc2 commit 6f1a18d
Show file tree
Hide file tree
Showing 2 changed files with 189 additions and 8 deletions.
61 changes: 61 additions & 0 deletions unitary/examples/quantum_chinese_chess/move.py
Original file line number Diff line number Diff line change
Expand Up @@ -94,6 +94,8 @@ class Jump(QuantumEffect):
- CAPTURE
- EXCLUDED
- BASIC
All types of pieces could make Jump move.
"""

def __init__(
Expand Down Expand Up @@ -156,3 +158,62 @@ def effect(self, *objects) -> Iterator[cirq.Operation]:
target_0.reset(source_0)
source_0.reset()
return iter(())


class SplitJump(QuantumEffect):
"""SplitJump from source_0 to target_0 and target_1. The only accepted (default) move_variant is
- BASIC.
All types of pieces could make SplitJump move, except KING. This is implemented with a
PhasedSplit().
"""

def __init__(
self,
):
return

def num_dimension(self) -> Optional[int]:
return 2

def num_objects(self) -> Optional[int]:
return 3

def effect(self, *objects) -> Iterator[cirq.Operation]:
source_0, target_0, target_1 = objects
# Make the split jump.
source_0.is_entangled = True
alpha.PhasedSplit()(source_0, target_0, target_1)
# Pass the classical properties of the source piece to the target pieces.
target_0.reset(source_0)
target_1.reset(source_0)
return iter(())


class MergeJump(QuantumEffect):
"""MergeJump from source_0 to source_1 to target_0. The only accepted (default) move_variant is
- BASIC.
All types of pieces could make MergeJump move, except KING.
"""

def __init__(
self,
):
return

def num_dimension(self) -> Optional[int]:
return 2

def num_objects(self) -> Optional[int]:
return 3

def effect(self, *objects) -> Iterator[cirq.Operation]:
source_0, source_1, target_0 = objects
# Make the merge jump.
alpha.PhasedMove(-0.5)(source_0, target_0)
alpha.PhasedMove(-0.5)(source_0, target_0)
alpha.PhasedMove(-0.5)(source_1, target_0)
# Pass the classical properties of the source pieces to the target piece.
target_0.reset(source_0)
return iter(())
136 changes: 128 additions & 8 deletions unitary/examples/quantum_chinese_chess/move_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -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.move import Move, Jump
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
import pytest
Expand Down Expand Up @@ -148,7 +148,7 @@ def test_to_str():


def test_jump_classical():
# Target is empty.
"""Target is empty."""
board = set_board(["a1", "b1"])
world = board.board
# TODO(): try move all varaibles declarations of a1 = world["a1"] into a function.
Expand All @@ -161,7 +161,7 @@ def test_jump_classical():


def test_jump_capture_quantum_source():
# Source is in quantum state.
"""Source is in quantum state."""
board = set_board(["a1", "b1"])
world = board.board
alpha.PhasedSplit()(world["a1"], world["a2"], world["a3"])
Expand All @@ -180,7 +180,7 @@ def test_jump_capture_quantum_source():


def test_jump_capture_quantum_target():
# Target is in quantum state.
"""Target is in quantum state."""
board = set_board(["a1", "b1"])
world = board.board
alpha.PhasedSplit()(world["b1"], world["b2"], world["b3"])
Expand All @@ -192,7 +192,7 @@ def test_jump_capture_quantum_target():


def test_jump_capture_quantum_source_and_target():
# Both source and target are in quantum state.
"""Both source and target are in quantum state."""
board = set_board(["a1", "b1"])
world = board.board
alpha.PhasedSplit()(world["a1"], world["a2"], world["a3"])
Expand Down Expand Up @@ -221,7 +221,7 @@ def test_jump_capture_quantum_source_and_target():


def test_jump_excluded_quantum_target():
# Target is in quantum state.
"""Target is in quantum state."""
board = set_board(["a1", "b1"])
world = board.board
alpha.PhasedSplit()(world["b1"], world["b2"], world["b3"])
Expand All @@ -237,7 +237,7 @@ def test_jump_excluded_quantum_target():


def test_jump_excluded_quantum_source_and_target():
# Both source and target are in quantum state.
"""Both source and target are in quantum state."""
board = set_board(["a1", "b1"])
world = board.board
alpha.PhasedSplit()(world["a1"], world["a2"], world["a3"])
Expand All @@ -257,7 +257,7 @@ def test_jump_excluded_quantum_source_and_target():


def test_jump_basic():
# Source is in quantum state.
"""Source is in quantum state."""
board = set_board(["a1"])
world = board.board
alpha.PhasedSplit()(world["a1"], world["a2"], world["a3"])
Expand All @@ -266,3 +266,123 @@ def test_jump_basic():
assert len(board_probabilities) == 2
assert_fifty_fifty(board_probabilities, locations_to_bitboard(["d1"]))
assert_fifty_fifty(board_probabilities, locations_to_bitboard(["a3"]))


def test_split_jump_classical_source():
"""Source is in classical state."""
board = set_board(["a1"])
world = board.board
SplitJump()(world["a1"], world["a2"], world["a3"])
board_probabilities = get_board_probability_distribution(board, 1000)
assert len(board_probabilities) == 2
assert_fifty_fifty(board_probabilities, locations_to_bitboard(["a2"]))
assert_fifty_fifty(board_probabilities, locations_to_bitboard(["a3"]))
assert world["a2"].type_ == Type.ROOK
assert world["a2"].color == Color.RED
assert world["a2"].is_entangled == True
assert world["a3"].type_ == Type.ROOK
assert world["a3"].color == Color.RED
assert world["a3"].is_entangled == True


def test_split_jump_quantum_source():
"""Source is in quantum state."""
board = set_board(["a1"])
world = board.board
alpha.PhasedSplit()(world["a1"], world["a2"], world["a3"])
SplitJump()(world["a3"], world["a4"], world["a5"])
assert_sample_distribution(
board,
{
locations_to_bitboard(["a2"]): 0.5,
locations_to_bitboard(["a4"]): 0.25,
locations_to_bitboard(["a5"]): 0.25,
},
)
assert world["a4"].is_entangled == True
assert world["a5"].is_entangled == True


def test_merge_jump_perfect_merge():
"""Two quantum pieces split from one source could be merge back to one."""
board = set_board(["a1"])
world = board.board
SplitJump()(world["a1"], world["a2"], world["a3"])
MergeJump()(world["a2"], world["a3"], world["a1"])
assert_samples_in(board, {locations_to_bitboard(["a1"]): 1.0})


def test_merge_jump_imperfect_merge_scenario_1():
"""Imperfect merge scenario 1"""
board = set_board(["a1"])
world = board.board
SplitJump()(world["a1"], world["a2"], world["a3"])
SplitJump()(world["a3"], world["a4"], world["a5"])
# a2 has prob. 0.5 to be occupied, while a4 has prob. 0.25 to be occupied
MergeJump()(world["a2"], world["a4"], world["a6"])
# Accoding to matrix calculations, the ending coefficient of
# a5 to be occupied: -1/2;
# a6 to be occupied: 1/2 + i/2/sqrt(2)
# a4 to be occupied: -i/2 -1/2/sqrt(2)
# a2 to be occupied: 0
assert_sample_distribution(
board,
{
locations_to_bitboard(["a5"]): 1.0 / 4,
locations_to_bitboard(["a6"]): 3.0 / 8,
locations_to_bitboard(["a4"]): 3.0 / 8,
},
)


def test_merge_jump_imperfect_merge_scenario_2():
"""Imperfect merge scenario 2
Two quantum pieces split from two sources could not be merge into to one.
"""
board = set_board(["a1", "b1"])
world = board.board
SplitJump()(world["a1"], world["a2"], world["a3"])
SplitJump()(world["b1"], world["b2"], world["b3"])
MergeJump()(world["a2"], world["b2"], world["c2"])
# According to matrix calculations, the ending coefficient of
# [a3, b3]: 1/2
# [a3, c2]: i/2/sqrt(2)
# [a3, b2]: -1/2/sqrt(2)
# [b3, c2]: i/2/sqrt(2)
# [b2, b3]: 1/2/sqrt(2)
# [b2, c2]: 1/2
assert_sample_distribution(
board,
{
locations_to_bitboard(["a3", "b3"]): 1.0 / 4,
locations_to_bitboard(["a3", "c2"]): 1.0 / 8,
locations_to_bitboard(["a3", "b2"]): 1.0 / 8,
locations_to_bitboard(["b3", "c2"]): 1.0 / 8,
locations_to_bitboard(["b2", "b3"]): 1.0 / 8,
locations_to_bitboard(["b2", "c2"]): 1.0 / 4,
},
)


def test_merge_jump_imperfect_merge_scenario_3():
"""Imperfect merge scenario 3.
This is a simplied version of the scenario above, where we unhook a3 and b3.
"""
board = set_board(["a1", "b1"])
world = board.board
SplitJump()(world["a1"], world["a2"], world["a3"])
SplitJump()(world["b1"], world["b2"], world["b3"])
board.board.unhook(world["a3"])
board.board.unhook(world["b3"])
# Now the only quantum pieces in the board are a2 and b2.
MergeJump()(world["a2"], world["b2"], world["c2"])
# The expected distribution is same as above by summing over a3 and b3.
assert_sample_distribution(
board,
{
locations_to_bitboard([]): 1.0 / 4,
locations_to_bitboard(["c2"]): 1.0 / 4,
locations_to_bitboard(["b2"]): 1.0 / 4,
locations_to_bitboard(["b2", "c2"]): 1.0 / 4,
},
)

0 comments on commit 6f1a18d

Please sign in to comment.