From 4d54a01d7ae3e23ca1871d28fc5dbd001ca85898 Mon Sep 17 00:00:00 2001 From: Pengfei Chen Date: Fri, 10 May 2024 15:16:57 -0700 Subject: [PATCH 1/6] update --- unitary/alpha/__init__.py | 2 + unitary/alpha/qudit_effects.py | 41 +++++++++--- unitary/alpha/qudit_effects_test.py | 11 ++++ unitary/alpha/qudit_gates.py | 97 +++++++++++++++++++---------- unitary/alpha/qudit_gates_test.py | 50 +++++++++++++-- 5 files changed, 157 insertions(+), 44 deletions(-) diff --git a/unitary/alpha/__init__.py b/unitary/alpha/__init__.py index 472a0c71..d69ed72e 100644 --- a/unitary/alpha/__init__.py +++ b/unitary/alpha/__init__.py @@ -43,8 +43,10 @@ from unitary.alpha.qudit_effects import ( Cycle, Flip, + Superpose, QuditCycle, QuditFlip, + QuditSuperpose, ) from unitary.alpha.quantum_object import ( diff --git a/unitary/alpha/qudit_effects.py b/unitary/alpha/qudit_effects.py index 632a724f..0ca1f2b1 100644 --- a/unitary/alpha/qudit_effects.py +++ b/unitary/alpha/qudit_effects.py @@ -18,12 +18,12 @@ import cirq -from unitary.alpha.qudit_gates import QuditPlusGate, QuditXGate +from unitary.alpha.qudit_gates import QuditPlusGate, QuditXGate, QuditHadamardGate from unitary.alpha.quantum_effect import QuantumEffect 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,15 +55,13 @@ 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 - qudits. These effects will be cumulative. For instance, two quarter flips (effect_fraction=0.25) will be equivalent to a half @@ -127,3 +125,32 @@ class QuditFlip(Flip): def __init__(self, dimension: int, state0: int, state1: int): super().__init__(state0=state0, state1=state1) + + +class Superpose(QuantumEffect): + """Transforms each pure state to a (equal, in terms of absolute magnitude) superposition of + all pure states. + """ + + def __init__(self): + pass + + def effect(self, *objects): + for q in objects: + if q.qubit.dimension == 2: + yield cirq.H(q.qubit) + else: + yield QuditHadamardGate(dimension=q.qubit.dimension)( + q.qubit + ) + + +class QuditSuperpose(Superpose): + """Equivalent to Superpose. + + Exists only for backwards compatibiltity. + Will be removed in 2024. + """ + + def __init__(self, dimension: int): + super().__init__() diff --git a/unitary/alpha/qudit_effects_test.py b/unitary/alpha/qudit_effects_test.py index c2c3d8a2..02cc384b 100644 --- a/unitary/alpha/qudit_effects_test.py +++ b/unitary/alpha/qudit_effects_test.py @@ -75,3 +75,14 @@ def test_qudit_flip(simulator, compile_to_qubits): alpha.QuditFlip(3, 0, 1)(piece) results = board.peek([piece], count=100) assert all(result == [StopLight.GREEN] for result in results) + + +def test_qudit_superpose(): + board = alpha.QuantumWorld(sampler=cirq.Simulator(), compile_to_qubits=False) + piece = alpha.QuantumObject("t", StopLight.GREEN) + board.add_object(piece) + alpha.QuditSuperpose(3)(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) diff --git a/unitary/alpha/qudit_gates.py b/unitary/alpha/qudit_gates.py index 628d9a45..e44acfbc 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,10 @@ def _circuit_diagram_info_(self, args): class QuditPlusGate(cirq.Gate): - """Cycles all the states using a permutation gate. + """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)` + 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 +82,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 +104,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 +138,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 +185,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 @@ -202,3 +201,37 @@ 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 opperation 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 4493feec..e6494a21 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,18 @@ 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 +174,17 @@ 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", @@ -180,12 +203,14 @@ 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): m = cirq.unitary(gate) np.set_printoptions(linewidth=200) - result = m.dot(m.T.conj()) assert np.allclose(np.eye(len(m)), m.dot(m.T.conj()), atol=1e-6) @@ -295,3 +320,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 39fe77537b5f7955d136a7637e176cdcbcbd7533 Mon Sep 17 00:00:00 2001 From: Pengfei Chen Date: Fri, 10 May 2024 15:24:31 -0700 Subject: [PATCH 2/6] update --- unitary/alpha/qudit_effects.py | 5 +---- unitary/alpha/qudit_effects_test.py | 2 +- 2 files changed, 2 insertions(+), 5 deletions(-) diff --git a/unitary/alpha/qudit_effects.py b/unitary/alpha/qudit_effects.py index 0ca1f2b1..a7b816f1 100644 --- a/unitary/alpha/qudit_effects.py +++ b/unitary/alpha/qudit_effects.py @@ -132,9 +132,6 @@ class Superpose(QuantumEffect): all pure states. """ - def __init__(self): - pass - def effect(self, *objects): for q in objects: if q.qubit.dimension == 2: @@ -152,5 +149,5 @@ class QuditSuperpose(Superpose): Will be removed in 2024. """ - def __init__(self, dimension: int): + def __init__(self): super().__init__() diff --git a/unitary/alpha/qudit_effects_test.py b/unitary/alpha/qudit_effects_test.py index 02cc384b..432f54f6 100644 --- a/unitary/alpha/qudit_effects_test.py +++ b/unitary/alpha/qudit_effects_test.py @@ -81,7 +81,7 @@ def test_qudit_superpose(): board = alpha.QuantumWorld(sampler=cirq.Simulator(), compile_to_qubits=False) piece = alpha.QuantumObject("t", StopLight.GREEN) board.add_object(piece) - alpha.QuditSuperpose(3)(piece) + alpha.QuditSuperpose()(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) From b55e24f14d3b19f35a5032ec5c91104260261671 Mon Sep 17 00:00:00 2001 From: Pengfei Chen Date: Fri, 10 May 2024 15:25:42 -0700 Subject: [PATCH 3/6] update --- unitary/alpha/qudit_effects.py | 4 +--- unitary/alpha/qudit_gates.py | 8 ++++++-- unitary/alpha/qudit_gates_test.py | 2 ++ 3 files changed, 9 insertions(+), 5 deletions(-) diff --git a/unitary/alpha/qudit_effects.py b/unitary/alpha/qudit_effects.py index a7b816f1..9ff71bbb 100644 --- a/unitary/alpha/qudit_effects.py +++ b/unitary/alpha/qudit_effects.py @@ -137,9 +137,7 @@ def effect(self, *objects): if q.qubit.dimension == 2: yield cirq.H(q.qubit) else: - yield QuditHadamardGate(dimension=q.qubit.dimension)( - q.qubit - ) + yield QuditHadamardGate(dimension=q.qubit.dimension)(q.qubit) class QuditSuperpose(Superpose): diff --git a/unitary/alpha/qudit_gates.py b/unitary/alpha/qudit_gates.py index e44acfbc..d9c8b1c3 100644 --- a/unitary/alpha/qudit_gates.py +++ b/unitary/alpha/qudit_gates.py @@ -87,7 +87,7 @@ class QuditControlledXGate(cirq.Gate): 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. + 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 @@ -222,7 +222,11 @@ 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) + 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. diff --git a/unitary/alpha/qudit_gates_test.py b/unitary/alpha/qudit_gates_test.py index e6494a21..117459d6 100644 --- a/unitary/alpha/qudit_gates_test.py +++ b/unitary/alpha/qudit_gates_test.py @@ -125,6 +125,7 @@ def test_control_x(control: int, dest: int): 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): qutrit0 = cirq.NamedQid("q0", dimension=3) @@ -186,6 +187,7 @@ def test_control_of_0_x(dest: int): assert np.all(results.measurements["m0"] == 0) assert np.all(results.measurements["m1"] == non_active) + @pytest.mark.parametrize( "gate", [ From a71bc4c164ae12fd8380c538c3bfddf3d5aa3f20 Mon Sep 17 00:00:00 2001 From: madcpf Date: Sun, 12 May 2024 20:44:20 -0700 Subject: [PATCH 4/6] update --- unitary/alpha/__init__.py | 2 -- 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 | 21 +++++++++++++++++++-- unitary/alpha/qudit_effects.py | 24 ------------------------ unitary/alpha/qudit_effects_test.py | 11 ----------- 7 files changed, 31 insertions(+), 48 deletions(-) diff --git a/unitary/alpha/__init__.py b/unitary/alpha/__init__.py index d69ed72e..472a0c71 100644 --- a/unitary/alpha/__init__.py +++ b/unitary/alpha/__init__.py @@ -43,10 +43,8 @@ from unitary.alpha.qudit_effects import ( Cycle, Flip, - Superpose, QuditCycle, QuditFlip, - QuditSuperpose, ) from unitary.alpha.quantum_object import ( 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..e97ea3b4 100644 --- a/unitary/alpha/qubit_effects_test.py +++ b/unitary/alpha/qubit_effects_test.py @@ -12,7 +12,7 @@ # See the License for the specific language governing permissions and # limitations under the License. # - +import enum import pytest import cirq @@ -20,6 +20,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 +84,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 +93,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_effects.py b/unitary/alpha/qudit_effects.py index 9ff71bbb..96753ea6 100644 --- a/unitary/alpha/qudit_effects.py +++ b/unitary/alpha/qudit_effects.py @@ -125,27 +125,3 @@ class QuditFlip(Flip): def __init__(self, dimension: int, state0: int, state1: int): super().__init__(state0=state0, state1=state1) - - -class Superpose(QuantumEffect): - """Transforms each pure state to a (equal, in terms of absolute magnitude) superposition of - all pure states. - """ - - def effect(self, *objects): - for q in objects: - if q.qubit.dimension == 2: - yield cirq.H(q.qubit) - else: - yield QuditHadamardGate(dimension=q.qubit.dimension)(q.qubit) - - -class QuditSuperpose(Superpose): - """Equivalent to Superpose. - - Exists only for backwards compatibiltity. - Will be removed in 2024. - """ - - def __init__(self): - super().__init__() diff --git a/unitary/alpha/qudit_effects_test.py b/unitary/alpha/qudit_effects_test.py index 432f54f6..c2c3d8a2 100644 --- a/unitary/alpha/qudit_effects_test.py +++ b/unitary/alpha/qudit_effects_test.py @@ -75,14 +75,3 @@ def test_qudit_flip(simulator, compile_to_qubits): alpha.QuditFlip(3, 0, 1)(piece) results = board.peek([piece], count=100) assert all(result == [StopLight.GREEN] for result in results) - - -def test_qudit_superpose(): - board = alpha.QuantumWorld(sampler=cirq.Simulator(), compile_to_qubits=False) - piece = alpha.QuantumObject("t", StopLight.GREEN) - board.add_object(piece) - alpha.QuditSuperpose()(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) From ac6f342b19a742e57ae158028441b53d91a349c7 Mon Sep 17 00:00:00 2001 From: madcpf Date: Sun, 12 May 2024 21:46:26 -0700 Subject: [PATCH 5/6] update --- unitary/alpha/quantum_effect_test.py | 2 +- unitary/alpha/quantum_world.py | 6 ++--- unitary/alpha/qubit_effects.py | 12 ++++----- unitary/alpha/qubit_effects_test.py | 22 +++------------- unitary/alpha/qudit_gates.py | 38 ---------------------------- unitary/alpha/qudit_gates_test.py | 15 ----------- 6 files changed, 12 insertions(+), 83 deletions(-) diff --git a/unitary/alpha/quantum_effect_test.py b/unitary/alpha/quantum_effect_test.py index 04044e57..2b2623ca 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.Phase()(piece) + alpha.Superposition()(piece) 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 diff --git a/unitary/alpha/qubit_effects.py b/unitary/alpha/qubit_effects.py index f786046b..e7043ba2 100644 --- a/unitary/alpha/qubit_effects.py +++ b/unitary/alpha/qubit_effects.py @@ -68,16 +68,14 @@ def __eq__(self, other): class Superposition(QuantumEffect): - """Transforms a qubit (or qudit) in a basis state into a (equal, in terms of - absolute magnitude) superposition of all basis states. - """ + """Takes a qubit in a basis state into a superposition.""" + + def num_dimension(self) -> Optional[int]: + return 2 def effect(self, *objects): for q in objects: - if q.qubit.dimension == 2: - yield cirq.H(q.qubit) - else: - yield QuditHadamardGate(dimension=q.qubit.dimension)(q.qubit) + yield cirq.H(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 e97ea3b4..5f481006 100644 --- a/unitary/alpha/qubit_effects_test.py +++ b/unitary/alpha/qubit_effects_test.py @@ -12,7 +12,7 @@ # See the License for the specific language governing permissions and # limitations under the License. # -import enum + import pytest import cirq @@ -20,12 +20,6 @@ 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): @@ -84,7 +78,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_qubit_superposition(simulator, compile_to_qubits): +def test_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) @@ -93,17 +87,6 @@ def test_qubit_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): @@ -250,3 +233,4 @@ def test_equalis(): eq.add_equality_group(alpha.Split(), alpha.Split()) eq.add_equality_group(alpha.Move(), alpha.Move()) eq.add_equality_group(alpha.PhasedMove(), alpha.PhasedMove()) + diff --git a/unitary/alpha/qudit_gates.py b/unitary/alpha/qudit_gates.py index d9c8b1c3..4715c47c 100644 --- a/unitary/alpha/qudit_gates.py +++ b/unitary/alpha/qudit_gates.py @@ -201,41 +201,3 @@ 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 opperation 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 117459d6..63e516a6 100644 --- a/unitary/alpha/qudit_gates_test.py +++ b/unitary/alpha/qudit_gates_test.py @@ -322,18 +322,3 @@ 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 0af5a50c747065c51497eca29c5be17b1eb651d5 Mon Sep 17 00:00:00 2001 From: madcpf Date: Sun, 12 May 2024 21:50:11 -0700 Subject: [PATCH 6/6] update --- unitary/alpha/qubit_effects.py | 1 - unitary/alpha/qubit_effects_test.py | 1 - unitary/alpha/qudit_effects.py | 2 +- unitary/alpha/qudit_gates_test.py | 3 --- 4 files changed, 1 insertion(+), 6 deletions(-) diff --git a/unitary/alpha/qubit_effects.py b/unitary/alpha/qubit_effects.py index e7043ba2..4776e42b 100644 --- a/unitary/alpha/qubit_effects.py +++ b/unitary/alpha/qubit_effects.py @@ -19,7 +19,6 @@ import cirq from unitary.alpha.quantum_effect import QuantumEffect -from unitary.alpha.qudit_gates import QuditHadamardGate class Phase(QuantumEffect): diff --git a/unitary/alpha/qubit_effects_test.py b/unitary/alpha/qubit_effects_test.py index 5f481006..4d35e6ca 100644 --- a/unitary/alpha/qubit_effects_test.py +++ b/unitary/alpha/qubit_effects_test.py @@ -233,4 +233,3 @@ def test_equalis(): eq.add_equality_group(alpha.Split(), alpha.Split()) eq.add_equality_group(alpha.Move(), alpha.Move()) eq.add_equality_group(alpha.PhasedMove(), alpha.PhasedMove()) - diff --git a/unitary/alpha/qudit_effects.py b/unitary/alpha/qudit_effects.py index 96753ea6..e03d7c71 100644 --- a/unitary/alpha/qudit_effects.py +++ b/unitary/alpha/qudit_effects.py @@ -18,7 +18,7 @@ import cirq -from unitary.alpha.qudit_gates import QuditPlusGate, QuditXGate, QuditHadamardGate +from unitary.alpha.qudit_gates import QuditPlusGate, QuditXGate from unitary.alpha.quantum_effect import QuantumEffect diff --git a/unitary/alpha/qudit_gates_test.py b/unitary/alpha/qudit_gates_test.py index 63e516a6..19507bf2 100644 --- a/unitary/alpha/qudit_gates_test.py +++ b/unitary/alpha/qudit_gates_test.py @@ -205,9 +205,6 @@ 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):