Skip to content

Commit

Permalink
up
Browse files Browse the repository at this point in the history
  • Loading branch information
Pengfei Chen committed Oct 25, 2023
1 parent 40463a1 commit e3a7fbc
Show file tree
Hide file tree
Showing 6 changed files with 43 additions and 32 deletions.
13 changes: 10 additions & 3 deletions unitary/alpha/quantum_world.py
Original file line number Diff line number Diff line change
Expand Up @@ -306,10 +306,17 @@ def _interpret_result(self, result: Union[int, Iterable[int]]) -> int:
return result

def unhook(self, object: QuantumObject) -> None:
"""Replaces all usages of the given object in the circuit with a new ancilla with value=0."""
# Creates a new ancilla.
"""Replace all usages of the given `object` in the circuit with a new ancilla,
so that
- all former operations on `object` will be applied on the new ancilla;
- future operations on `object` start with its new reset value.
Note that we don't do force measurement on it, since we don't care about its
current value but just want to reset it.
"""
# Create a new ancilla.
new_ancilla = self._add_ancilla(object.name)
# Replace operations using the qubit of the given object with the new ancilla.
# Replace operations of the given `object` with the new ancilla.
qubit_remapping_dict = {
object.qubit: new_ancilla.qubit,
new_ancilla.qubit: object.qubit,
Expand Down
12 changes: 12 additions & 0 deletions unitary/examples/quantum_chinese_chess/board.py
Original file line number Diff line number Diff line change
Expand Up @@ -184,3 +184,15 @@ def flying_general_check(self) -> bool:
# If there are no pieces between two KINGs, the check successes. Game ends.
return True
# TODO(): add check when there are quantum pieces in between.

def sample(self, repetitions: int) -> List[int]:
"""Sample the current board by the given `repetitions`.
Returns a list of 90-bit bitstring, each corresponding to one sample.
"""
samples = self.board.peek(count=repetitions, convert_to_enum=False)
# Convert peek results (in List[List[int]]) into List[int].
samples = [
int("0b" + "".join([str(i) for i in sample[::-1]]), base=2)
for sample in samples
]
return samples
7 changes: 4 additions & 3 deletions unitary/examples/quantum_chinese_chess/chess.py
Original file line number Diff line number Diff line change
Expand Up @@ -386,15 +386,16 @@ def apply_move(self, str_to_parse: str) -> None:
quantum_pieces_1,
)

print(move_type, " ", move_variant)
if self.debug_level > 1:
print(move_type, " ", move_variant)

# Apply the move accoding to its type.
if move_type == MoveType.CLASSICAL:
if source_0.type_ == Type.KING:
# Update the locations of KING.
self.board.king_locations[self.current_player] = targets[0]
# TODO(): only make such prints for a certain debug level.
print(f"Updated king locations: {self.board.king_locations}.")
if self.debug_level > 1:
print(f"Updated king locations: {self.board.king_locations}.")
if target_0.type_ == Type.KING:
# King is captured, then the game is over.
self.game_state = GameState(self.current_player)
Expand Down
6 changes: 3 additions & 3 deletions unitary/examples/quantum_chinese_chess/move.py
Original file line number Diff line number Diff line change
Expand Up @@ -165,9 +165,9 @@ def effect(self, *objects) -> Iterator[cirq.Operation]:
target_0.reset()
elif self.move_variant == MoveVariant.CLASSICAL:
if target_0.type_ != Type.EMPTY:
# For classical moves with target_0 occupied, we replace the qubit of target_0 with
# a new ancilla, and set its classical properties to be EMPTY.
world.unhook(target_0)
# For classical moves with target_0 occupied, we flip target_0 to be empty,
# and set its classical properties to be EMPTY.
alpha.Flip()(target_0)
target_0.reset()

# Make the jump move.
Expand Down
15 changes: 10 additions & 5 deletions unitary/examples/quantum_chinese_chess/move_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,6 @@
assert_this_or_that,
assert_prob_about,
assert_fifty_fifty,
sample_board,
get_board_probability_distribution,
print_samples,
set_board,
Expand Down Expand Up @@ -156,7 +155,7 @@ def test_jump_classical():
assert_samples_in(board, [locations_to_bitboard(["b1"])])


def test_jump_capture():
def test_jump_capture_quantum_source():
# Source is in quantum state.
board = set_board(["a1", "b1"])
world = board.board
Expand All @@ -166,14 +165,16 @@ def test_jump_capture():
assert_fifty_fifty(board_probabilities, locations_to_bitboard(["a2", "b1"]))
assert_fifty_fifty(board_probabilities, locations_to_bitboard(["a3", "b1"]))
Jump(MoveVariant.CAPTURE)(world["a2"], world["b1"])
# pop() will break the supersition and only one of the following two states are possible.
# pop() will break the superposition and only one of the following two states are possible.
# We check the ancilla to learn if the jump was applied or not.
source_is_occupied = world.post_selection[world["ancilla_a2_0"]]
if source_is_occupied:
assert_samples_in(board, [locations_to_bitboard(["b1"])])
else:
assert_samples_in(board, [locations_to_bitboard(["a3", "b1"])])


def test_jump_capture_quantum_target():
# Target is in quantum state.
board = set_board(["a1", "b1"])
world = board.board
Expand All @@ -184,6 +185,8 @@ def test_jump_capture():
assert_fifty_fifty(board_probabilities, locations_to_bitboard(["b2"]))
assert_fifty_fifty(board_probabilities, locations_to_bitboard(["b2", "b3"]))


def test_jump_capture_quantum_source_and_target():
# Both source and target are in quantum state.
board = set_board(["a1", "b1"])
world = board.board
Expand Down Expand Up @@ -212,13 +215,13 @@ def test_jump_capture():
assert_fifty_fifty(board_probabilities, locations_to_bitboard(["a3", "b3"]))


def test_jump_excluded():
def test_jump_excluded_quantum_target():
# Target is in quantum state.
board = set_board(["a1", "b1"])
world = board.board
alpha.PhasedSplit()(world["b1"], world["b2"], world["b3"])
Jump(MoveVariant.EXCLUDED)(world["a1"], world["b2"])
# pop() will break the supersition and only one of the following two states are possible.
# pop() will break the superposition and only one of the following two states are possible.
# We check the ancilla to learn if the jump was applied or not.
target_is_occupied = world.post_selection[world["ancilla_b2_0"]]
print(target_is_occupied)
Expand All @@ -227,6 +230,8 @@ def test_jump_excluded():
else:
assert_samples_in(board, [locations_to_bitboard(["b2", "b3"])])


def test_jump_excluded_quantum_source_and_target():
# Both source and target are in quantum state.
board = set_board(["a1", "b1"])
world = board.board
Expand Down
22 changes: 4 additions & 18 deletions unitary/examples/quantum_chinese_chess/test_utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -78,19 +78,6 @@ def bitboard_to_locations(bitboard: int) -> List[str]:
return locations


def sample_board(board: Board, repetitions: int) -> List[int]:
"""Sample the given `board` by the given `repetitions`.
Returns a list of 90-bit bitstring, each corresponding to one sample.
"""
samples = board.board.peek(count=repetitions, convert_to_enum=False)
# Convert peek results (in List[List[int]]) into List[int].
samples = [
int("0b" + "".join([str(i) for i in sample[::-1]]), base=2)
for sample in samples
]
return samples


def print_samples(samples: List[int]) -> None:
"""Aggregate all the samples and print the dictionary of {locations: count}."""
sample_dict = {}
Expand All @@ -111,7 +98,7 @@ def get_board_probability_distribution(
"""
board_probabilities: Dict[int, float] = {}

samples = sample_board(board, repetitions)
samples = board.sample(repetitions)
for sample in samples:
if sample not in board_probabilities:
board_probabilities[sample] = 0.0
Expand All @@ -128,8 +115,7 @@ def assert_samples_in(board: Board, probabilities: Dict[int, float]) -> None:
the given `probabilities` (i.e. a map from bitstring into its possibility),
and that each possibility is represented at least once in the samples.
"""
samples = sample_board(board, 500)
assert len(samples) == 500
samples = board.sample(500)
all_in = all(sample in probabilities for sample in samples)
assert all_in, print_samples(samples)
# Make sure each possibility is represented at least once.
Expand All @@ -148,7 +134,7 @@ def assert_sample_distribution(
"""
n_samples = 500
assert abs(sum(probabilities.values()) - 1) < 1e-9
samples = sample_board(board, n_samples)
samples = board.sample(n_samples)
counts = defaultdict(int)
for sample in samples:
assert sample in probabilities, bitboard_to_locations(sample)
Expand Down Expand Up @@ -176,7 +162,7 @@ def assert_this_or_that(samples: List[int], this: int, that: int) -> None:


def assert_prob_about(
probabilities: Dict[int, float], that: int, expected: float, atol: float = 0.05
probabilities: Dict[int, float], that: int, expected: float, atol: float = 0.06
) -> None:
"""Checks that the probability of `that` is within `atol` of the value of `expected`."""
assert that in probabilities, print_samples(list(probabilities.keys()))
Expand Down

0 comments on commit e3a7fbc

Please sign in to comment.