From 185916c92c9588d05344fa130512d0b51e297e20 Mon Sep 17 00:00:00 2001 From: MatteoRobbiati Date: Tue, 12 Mar 2024 12:53:22 -0400 Subject: [PATCH 01/16] fix: update gate.clifford value in rotations given a new theta --- src/qibo/gates/gates.py | 21 ++++++++++++++++++--- 1 file changed, 18 insertions(+), 3 deletions(-) diff --git a/src/qibo/gates/gates.py b/src/qibo/gates/gates.py index ae56a2723b..a4ea0cdd04 100644 --- a/src/qibo/gates/gates.py +++ b/src/qibo/gates/gates.py @@ -524,13 +524,28 @@ def __init__(self, q, theta, trainable=True): if isinstance(theta, Parameter): theta = theta() - if isinstance(theta, (float, int)) and (theta % (np.pi / 2)).is_integer(): - self.clifford = True + self.update_clifford_condition(theta) - self.parameters = theta + self._parameters = theta self.init_args = [q] self.init_kwargs = {"theta": theta, "trainable": trainable} + def update_clifford_condition(self, theta): + """Update Clifford boolean condition according to the given angle ``theta``.""" + if isinstance(theta, (float, int)) and (theta % (np.pi / 2)).is_integer(): + self.clifford = True + else: + self.clifford = False + + @property + def parameters(self): + return self._parameters + + @parameters.setter + def parameters(self, value): + self._parameters = value + self.update_clifford_condition(value) + def _dagger(self) -> "Gate": """""" return self.__class__( From cf4667203234110491c1ecd8b4c654965824c9a0 Mon Sep 17 00:00:00 2001 From: MatteoRobbiati Date: Tue, 12 Mar 2024 13:04:08 -0400 Subject: [PATCH 02/16] fix: add same feature to controlled-rotations --- src/qibo/gates/gates.py | 19 +++++++++++++++++-- 1 file changed, 17 insertions(+), 2 deletions(-) diff --git a/src/qibo/gates/gates.py b/src/qibo/gates/gates.py index a4ea0cdd04..5aa6f59463 100644 --- a/src/qibo/gates/gates.py +++ b/src/qibo/gates/gates.py @@ -1201,12 +1201,27 @@ def __init__(self, q0, q1, theta, trainable=True): self.parameters = theta self.unitary = True - if isinstance(theta, (float, int)) and (theta % np.pi).is_integer(): - self.clifford = True + self.update_clifford_condition(theta) self.init_args = [q0, q1] self.init_kwargs = {"theta": theta, "trainable": trainable} + def update_clifford_condition(self, theta): + """Update Clifford boolean condition according to the given angle ``theta``.""" + if isinstance(theta, (float, int)) and (theta % (np.pi / 2)).is_integer(): + self.clifford = True + else: + self.clifford = False + + @property + def parameters(self): + return self._parameters + + @parameters.setter + def parameters(self, value): + self._parameters = value + self.update_clifford_condition(value) + def _dagger(self) -> "Gate": """""" q0 = self.control_qubits[0] From a04df11404b9a79b0b618e9ba2915393354c4b69 Mon Sep 17 00:00:00 2001 From: MatteoRobbiati Date: Thu, 14 Mar 2024 14:41:49 -0400 Subject: [PATCH 03/16] feat: clifford as property --- src/qibo/gates/abstract.py | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/qibo/gates/abstract.py b/src/qibo/gates/abstract.py index 2cc99fa4b5..d52673092d 100644 --- a/src/qibo/gates/abstract.py +++ b/src/qibo/gates/abstract.py @@ -46,7 +46,7 @@ def __init__(self): self.init_args = [] self.init_kwargs = {} - self.clifford = False + self._clifford = False self.unitary = False self._target_qubits = tuple() self._control_qubits = set() @@ -59,6 +59,11 @@ def __init__(self): self.device_gates = set() self.original_gate = None + @property + def clifford(self): + """Return boolean value representing if a Gate is Clifford or not.""" + return self._clifford + @property def raw(self) -> dict: """Serialize to dictionary. From 4afb56683da452d6ec0f1ca36a07c58692a16375 Mon Sep 17 00:00:00 2001 From: MatteoRobbiati Date: Thu, 14 Mar 2024 14:42:44 -0400 Subject: [PATCH 04/16] feat: self.clifford -> self._clifford in gates --- src/qibo/gates/gates.py | 82 +++++++++++++++++------------------------ 1 file changed, 33 insertions(+), 49 deletions(-) diff --git a/src/qibo/gates/gates.py b/src/qibo/gates/gates.py index 333aa89a6d..270b231a30 100644 --- a/src/qibo/gates/gates.py +++ b/src/qibo/gates/gates.py @@ -29,7 +29,7 @@ def __init__(self, q): self.draw_label = "H" self.target_qubits = (q,) self.init_args = [q] - self.clifford = True + self._clifford = True self.unitary = True @property @@ -58,7 +58,7 @@ def __init__(self, q): self.draw_label = "X" self.target_qubits = (q,) self.init_args = [q] - self.clifford = True + self._clifford = True self.unitary = True @property @@ -171,7 +171,7 @@ def __init__(self, q): self.draw_label = "Y" self.target_qubits = (q,) self.init_args = [q] - self.clifford = True + self._clifford = True self.unitary = True @property @@ -215,7 +215,7 @@ def __init__(self, q): self.draw_label = "Z" self.target_qubits = (q,) self.init_args = [q] - self.clifford = True + self._clifford = True self.unitary = True @property @@ -256,7 +256,7 @@ def __init__(self, q): self.draw_label = "SX" self.target_qubits = (q,) self.init_args = [q] - self.clifford = True + self._clifford = True self.unitary = True @property @@ -303,7 +303,7 @@ def __init__(self, q): self.draw_label = "SXDG" self.target_qubits = (q,) self.init_args = [q] - self.clifford = True + self._clifford = True self.unitary = True @property @@ -350,7 +350,7 @@ def __init__(self, q): self.draw_label = "S" self.target_qubits = (q,) self.init_args = [q] - self.clifford = True + self._clifford = True self.unitary = True @property @@ -382,7 +382,7 @@ def __init__(self, q): self.draw_label = "SDG" self.target_qubits = (q,) self.init_args = [q] - self.clifford = True + self._clifford = True self.unitary = True @property @@ -468,7 +468,7 @@ def __init__(self, *q): self.draw_label = "I" self.target_qubits = tuple(q) self.init_args = q - self.clifford = True + self._clifford = True self.unitary = True @property @@ -502,6 +502,14 @@ def __init__(self, *q, delay: int = 0): self.target_qubits = tuple(q) +def is_clifford_given_angle(angle): + """Helper function to update Clifford boolean condition according to the given angle ``theta``.""" + if isinstance(angle, (float, int)) and (angle % (np.pi / 2)).is_integer(): + return True + else: + return False + + class _Rn_(ParametrizedGate): """Abstract class for defining the RX, RY and RZ rotations. @@ -519,32 +527,20 @@ def __init__(self, q, theta, trainable=True): self._controlled_gate = None self.target_qubits = (q,) self.unitary = True + self._clifford = is_clifford_given_angle(theta) self.initparams = theta if isinstance(theta, Parameter): theta = theta() - self.update_clifford_condition(theta) - - self._parameters = theta + self.parameters = theta self.init_args = [q] self.init_kwargs = {"theta": theta, "trainable": trainable} - def update_clifford_condition(self, theta): - """Update Clifford boolean condition according to the given angle ``theta``.""" - if isinstance(theta, (float, int)) and (theta % (np.pi / 2)).is_integer(): - self.clifford = True - else: - self.clifford = False - @property - def parameters(self): - return self._parameters - - @parameters.setter - def parameters(self, value): - self._parameters = value - self.update_clifford_condition(value) + def clifford(self): + self._clifford = is_clifford_given_angle(self.parameters[0]) + return self._clifford def _dagger(self) -> "Gate": """""" @@ -989,7 +985,7 @@ def __init__(self, q0, q1): self.control_qubits = (q0,) self.target_qubits = (q1,) self.init_args = [q0, q1] - self.clifford = True + self._clifford = True self.unitary = True @property @@ -1026,7 +1022,7 @@ def __init__(self, q0, q1): self.control_qubits = (q0,) self.target_qubits = (q1,) self.init_args = [q0, q1] - self.clifford = True + self._clifford = True self.unitary = True @property @@ -1072,7 +1068,7 @@ def __init__(self, q0, q1): self.control_qubits = (q0,) self.target_qubits = (q1,) self.init_args = [q0, q1] - self.clifford = True + self._clifford = True self.unitary = True @property @@ -1200,27 +1196,15 @@ def __init__(self, q0, q1, theta, trainable=True): self.target_qubits = (q1,) self.parameters = theta self.unitary = True - - self.update_clifford_condition(theta) + self._clifford = is_clifford_given_angle(theta) self.init_args = [q0, q1] self.init_kwargs = {"theta": theta, "trainable": trainable} - def update_clifford_condition(self, theta): - """Update Clifford boolean condition according to the given angle ``theta``.""" - if isinstance(theta, (float, int)) and (theta % (np.pi / 2)).is_integer(): - self.clifford = True - else: - self.clifford = False - @property - def parameters(self): - return self._parameters - - @parameters.setter - def parameters(self, value): - self._parameters = value - self.update_clifford_condition(value) + def clifford(self): + self._clifford = is_clifford_given_angle(self.parameters[0]) + return self._clifford def _dagger(self) -> "Gate": """""" @@ -1514,7 +1498,7 @@ def __init__(self, q0, q1): self.draw_label = "x" self.target_qubits = (q0, q1) self.init_args = [q0, q1] - self.clifford = True + self._clifford = True self.unitary = True @property @@ -1546,7 +1530,7 @@ def __init__(self, q0, q1): self.draw_label = "i" self.target_qubits = (q0, q1) self.init_args = [q0, q1] - self.clifford = True + self._clifford = True self.unitary = True @property @@ -1638,7 +1622,7 @@ def __init__(self, q0, q1): self.draw_label = "fx" self.target_qubits = (q0, q1) self.init_args = [q0, q1] - self.clifford = True + self._clifford = True self.unitary = True @property @@ -2188,7 +2172,7 @@ def __init__(self, q0, q1): self.draw_label = "ECR" self.target_qubits = (q0, q1) self.init_args = [q0, q1] - self.clifford = True + self._clifford = True self.unitary = True def decompose(self, *free, use_toffolis: bool = True) -> List[Gate]: From 2f50473bd3cfb2f414e8219bd408ce99b6dfba7b Mon Sep 17 00:00:00 2001 From: MatteoRobbiati Date: Thu, 14 Mar 2024 14:43:45 -0400 Subject: [PATCH 05/16] test: update clifford value in rotations --- tests/test_gates_gates.py | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/tests/test_gates_gates.py b/tests/test_gates_gates.py index 77eda97cc0..80a3c6ecd6 100644 --- a/tests/test_gates_gates.py +++ b/tests/test_gates_gates.py @@ -1689,3 +1689,28 @@ def test_x_decomposition_execution(backend, target, controls, free, use_toffolis ############################################################################### + +####################### Test Clifford updates ################################# + + +@pytest.mark.parametrize( + "gate", + [ + gates.RX(0, 0), + gates.RY(0, 0), + gates.RZ(0, 0), + gates.CRX(0, 1, 0), + gates.CRY(0, 1, 0), + gates.CRZ(0, 1, 0), + ], +) +def test_clifford_condition_update(backend, gate): + """Test clifford condition update if setting new angle into the rotations.""" + assert gate.clifford == True + gate.parameters = 0.5 + assert gate.clifford == False + gate.parameters = np.pi + assert gate.clifford == True + + +############################################################################### From 7b9842f812eedc4647a3cb587e5dff22f745067e Mon Sep 17 00:00:00 2001 From: Matteo Robbiati <62071516+MatteoRobbiati@users.noreply.github.com> Date: Thu, 14 Mar 2024 14:47:40 -0400 Subject: [PATCH 06/16] fix: parameter name in docstrings --- src/qibo/gates/gates.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/qibo/gates/gates.py b/src/qibo/gates/gates.py index 270b231a30..4d2a1387e1 100644 --- a/src/qibo/gates/gates.py +++ b/src/qibo/gates/gates.py @@ -503,7 +503,7 @@ def __init__(self, *q, delay: int = 0): def is_clifford_given_angle(angle): - """Helper function to update Clifford boolean condition according to the given angle ``theta``.""" + """Helper function to update Clifford boolean condition according to the given angle ``angle``.""" if isinstance(angle, (float, int)) and (angle % (np.pi / 2)).is_integer(): return True else: From ad6500ba38c87c759ee0dc7c54b39109d0860853 Mon Sep 17 00:00:00 2001 From: Matteo Robbiati <62071516+MatteoRobbiati@users.noreply.github.com> Date: Thu, 14 Mar 2024 14:50:10 -0400 Subject: [PATCH 07/16] Apply suggestions from code review Co-authored-by: Alessandro Candido --- src/qibo/gates/gates.py | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/src/qibo/gates/gates.py b/src/qibo/gates/gates.py index 4d2a1387e1..ad1d813641 100644 --- a/src/qibo/gates/gates.py +++ b/src/qibo/gates/gates.py @@ -504,10 +504,7 @@ def __init__(self, *q, delay: int = 0): def is_clifford_given_angle(angle): """Helper function to update Clifford boolean condition according to the given angle ``angle``.""" - if isinstance(angle, (float, int)) and (angle % (np.pi / 2)).is_integer(): - return True - else: - return False + return isinstance(angle, (float, int)) and (angle % (np.pi / 2)).is_integer() class _Rn_(ParametrizedGate): @@ -539,8 +536,7 @@ def __init__(self, q, theta, trainable=True): @property def clifford(self): - self._clifford = is_clifford_given_angle(self.parameters[0]) - return self._clifford + return is_clifford_given_angle(self.parameters[0]) def _dagger(self) -> "Gate": """""" From 9f65164cb8597f506461ee9869b2af8f73b43930 Mon Sep 17 00:00:00 2001 From: MatteoRobbiati Date: Thu, 14 Mar 2024 14:52:03 -0400 Subject: [PATCH 08/16] fix: rm useless self._clifford initialization --- src/qibo/gates/gates.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/qibo/gates/gates.py b/src/qibo/gates/gates.py index 270b231a30..6dd5acf2b1 100644 --- a/src/qibo/gates/gates.py +++ b/src/qibo/gates/gates.py @@ -527,7 +527,6 @@ def __init__(self, q, theta, trainable=True): self._controlled_gate = None self.target_qubits = (q,) self.unitary = True - self._clifford = is_clifford_given_angle(theta) self.initparams = theta if isinstance(theta, Parameter): @@ -1196,7 +1195,6 @@ def __init__(self, q0, q1, theta, trainable=True): self.target_qubits = (q1,) self.parameters = theta self.unitary = True - self._clifford = is_clifford_given_angle(theta) self.init_args = [q0, q1] self.init_kwargs = {"theta": theta, "trainable": trainable} From 04331771dd5ec1d22d5ed4430ebc5885e722d1f9 Mon Sep 17 00:00:00 2001 From: Alessandro Candido Date: Thu, 14 Mar 2024 19:54:50 +0100 Subject: [PATCH 09/16] fix: Propagate the attribute set removal to one more instance --- src/qibo/gates/gates.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/qibo/gates/gates.py b/src/qibo/gates/gates.py index f6fcfcbee0..d3511acccb 100644 --- a/src/qibo/gates/gates.py +++ b/src/qibo/gates/gates.py @@ -1197,8 +1197,7 @@ def __init__(self, q0, q1, theta, trainable=True): @property def clifford(self): - self._clifford = is_clifford_given_angle(self.parameters[0]) - return self._clifford + return is_clifford_given_angle(self.parameters[0]) def _dagger(self) -> "Gate": """""" From 5cd6898f23531ebd0d04189b01e4ef43aec5e074 Mon Sep 17 00:00:00 2001 From: MatteoRobbiati Date: Fri, 15 Mar 2024 08:56:51 -0400 Subject: [PATCH 10/16] fix: clifford condition test --- tests/test_gates_gates.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/test_gates_gates.py b/tests/test_gates_gates.py index 80a3c6ecd6..d407e72e9e 100644 --- a/tests/test_gates_gates.py +++ b/tests/test_gates_gates.py @@ -686,7 +686,7 @@ def test_cun(backend, name, params): if name in ["CRX", "CRY", "CRZ"]: theta = params["theta"] - if (theta % np.pi).is_integer(): + if (theta % (np.pi / 2)).is_integer(): assert gate.clifford else: assert not gate.clifford From 37b564a9fc72cdc0ac4c14bc4e8cfacfd0cc1732 Mon Sep 17 00:00:00 2001 From: MatteoRobbiati Date: Fri, 15 Mar 2024 10:44:16 -0400 Subject: [PATCH 11/16] fix: use self._clifford when setting new boolean values --- src/qibo/models/error_mitigation.py | 6 +++--- tests/test_models_error_mitigation.py | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/qibo/models/error_mitigation.py b/src/qibo/models/error_mitigation.py index 617dd6a65f..9fb2c59168 100644 --- a/src/qibo/models/error_mitigation.py +++ b/src/qibo/models/error_mitigation.py @@ -736,7 +736,7 @@ def sample_clifford_training_circuit( random_clifford(1, backend=backend, return_circuit=False), q, ) - gate_rand.clifford = True + gate_rand._clifford = True sampled_circuit.add(gate_rand) sampled_circuit.add(gate) else: @@ -747,7 +747,7 @@ def sample_clifford_training_circuit( ), *gate.qubits, ) - gate.clifford = True + gate._clifford = True sampled_circuit.add(gate) return sampled_circuit @@ -824,7 +824,7 @@ def error_sensitive_circuit(circuit, observable, backend=None): ) adjustment_gate = gates.Unitary(random_init, i) - adjustment_gate.clifford = True + adjustment_gate._clifford = True adjustment_gates.append(adjustment_gate) sensitive_circuit = sampled_circuit.__class__(**sampled_circuit.init_kwargs) diff --git a/tests/test_models_error_mitigation.py b/tests/test_models_error_mitigation.py index caaafad251..0fb90b2c20 100644 --- a/tests/test_models_error_mitigation.py +++ b/tests/test_models_error_mitigation.py @@ -216,7 +216,7 @@ def test_sample_training_circuit(nqubits): c.add(gates.M(q) for q in range(nqubits)) for j in range(len(c.queue)): - c.queue[j].clifford = True + c.queue[j]._clifford = True with pytest.raises(ValueError): sample_training_circuit_cdr(c) with pytest.raises(ValueError): From 5a36e1488d7e868729d649d775ce3fc64516f524 Mon Sep 17 00:00:00 2001 From: MatteoRobbiati Date: Tue, 19 Mar 2024 09:40:59 +0100 Subject: [PATCH 12/16] feat: clifford as property in all gates --- src/qibo/gates/abstract.py | 3 +- src/qibo/gates/gates.py | 89 +++++++++++++++++++++++++++++++------- 2 files changed, 74 insertions(+), 18 deletions(-) diff --git a/src/qibo/gates/abstract.py b/src/qibo/gates/abstract.py index d52673092d..378bb02d06 100644 --- a/src/qibo/gates/abstract.py +++ b/src/qibo/gates/abstract.py @@ -46,7 +46,6 @@ def __init__(self): self.init_args = [] self.init_kwargs = {} - self._clifford = False self.unitary = False self._target_qubits = tuple() self._control_qubits = set() @@ -62,7 +61,7 @@ def __init__(self): @property def clifford(self): """Return boolean value representing if a Gate is Clifford or not.""" - return self._clifford + return False @property def raw(self) -> dict: diff --git a/src/qibo/gates/gates.py b/src/qibo/gates/gates.py index d3511acccb..2378b38bfc 100644 --- a/src/qibo/gates/gates.py +++ b/src/qibo/gates/gates.py @@ -29,9 +29,12 @@ def __init__(self, q): self.draw_label = "H" self.target_qubits = (q,) self.init_args = [q] - self._clifford = True self.unitary = True + @property + def clifford(self): + return True + @property def qasm_label(self): return "h" @@ -58,9 +61,12 @@ def __init__(self, q): self.draw_label = "X" self.target_qubits = (q,) self.init_args = [q] - self._clifford = True self.unitary = True + @property + def clifford(self): + return True + @property def qasm_label(self): return "x" @@ -171,9 +177,12 @@ def __init__(self, q): self.draw_label = "Y" self.target_qubits = (q,) self.init_args = [q] - self._clifford = True self.unitary = True + @property + def clifford(self): + return True + @property def qasm_label(self): return "y" @@ -215,9 +224,12 @@ def __init__(self, q): self.draw_label = "Z" self.target_qubits = (q,) self.init_args = [q] - self._clifford = True self.unitary = True + @property + def clifford(self): + return True + @property def qasm_label(self): return "z" @@ -256,9 +268,12 @@ def __init__(self, q): self.draw_label = "SX" self.target_qubits = (q,) self.init_args = [q] - self._clifford = True self.unitary = True + @property + def clifford(self): + return True + @property def qasm_label(self): return "sx" @@ -303,9 +318,12 @@ def __init__(self, q): self.draw_label = "SXDG" self.target_qubits = (q,) self.init_args = [q] - self._clifford = True self.unitary = True + @property + def clifford(self): + return True + @property def qasm_label(self): return "sxdg" @@ -350,9 +368,12 @@ def __init__(self, q): self.draw_label = "S" self.target_qubits = (q,) self.init_args = [q] - self._clifford = True self.unitary = True + @property + def clifford(self): + return True + @property def qasm_label(self): return "s" @@ -382,9 +403,12 @@ def __init__(self, q): self.draw_label = "SDG" self.target_qubits = (q,) self.init_args = [q] - self._clifford = True self.unitary = True + @property + def clifford(self): + return True + @property def qasm_label(self): return "sdg" @@ -468,9 +492,12 @@ def __init__(self, *q): self.draw_label = "I" self.target_qubits = tuple(q) self.init_args = q - self._clifford = True self.unitary = True + @property + def clifford(self): + return True + @property def qasm_label(self): return "id" @@ -980,9 +1007,12 @@ def __init__(self, q0, q1): self.control_qubits = (q0,) self.target_qubits = (q1,) self.init_args = [q0, q1] - self._clifford = True self.unitary = True + @property + def clifford(self): + return True + @property def qasm_label(self): return "cx" @@ -1017,9 +1047,12 @@ def __init__(self, q0, q1): self.control_qubits = (q0,) self.target_qubits = (q1,) self.init_args = [q0, q1] - self._clifford = True self.unitary = True + @property + def clifford(self): + return True + @property def qasm_label(self): return "cy" @@ -1063,9 +1096,12 @@ def __init__(self, q0, q1): self.control_qubits = (q0,) self.target_qubits = (q1,) self.init_args = [q0, q1] - self._clifford = True self.unitary = True + @property + def clifford(self): + return True + @property def qasm_label(self): return "cz" @@ -1491,9 +1527,12 @@ def __init__(self, q0, q1): self.draw_label = "x" self.target_qubits = (q0, q1) self.init_args = [q0, q1] - self._clifford = True self.unitary = True + @property + def clifford(self): + return True + @property def qasm_label(self): return "swap" @@ -1523,9 +1562,12 @@ def __init__(self, q0, q1): self.draw_label = "i" self.target_qubits = (q0, q1) self.init_args = [q0, q1] - self._clifford = True self.unitary = True + @property + def clifford(self): + return True + @property def qasm_label(self): return "iswap" @@ -1615,9 +1657,12 @@ def __init__(self, q0, q1): self.draw_label = "fx" self.target_qubits = (q0, q1) self.init_args = [q0, q1] - self._clifford = True self.unitary = True + @property + def clifford(self): + return True + @property def qasm_label(self): return "fswap" @@ -2165,9 +2210,12 @@ def __init__(self, q0, q1): self.draw_label = "ECR" self.target_qubits = (q0, q1) self.init_args = [q0, q1] - self._clifford = True self.unitary = True + @property + def clifford(self): + return True + def decompose(self, *free, use_toffolis: bool = True) -> List[Gate]: """Decomposition of :math:`\\textup{ECR}` gate up to global phase. @@ -2322,6 +2370,7 @@ def __init__( self.name = "Unitary" if name is None else name self.draw_label = "U" self.target_qubits = tuple(q) + self._clifford = False # TODO: Check that given ``unitary`` has proper shape? self.parameter_names = "u" @@ -2368,6 +2417,14 @@ def parameters(self, x): for gate in self.device_gates: # pragma: no cover gate.parameters = x + @property + def clifford(self): + return self._clifford + + @clifford.setter + def clifford(self, value): + self._clifford = value + def on_qubits(self, qubit_map): args = [self.init_args[0]] args.extend(qubit_map.get(i) for i in self.target_qubits) From 530392dee1ac2e7ebf43be0c9f2185ba9cdee981 Mon Sep 17 00:00:00 2001 From: MatteoRobbiati Date: Tue, 19 Mar 2024 09:41:35 +0100 Subject: [PATCH 13/16] fix: lighten qem checks --- src/qibo/models/error_mitigation.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/qibo/models/error_mitigation.py b/src/qibo/models/error_mitigation.py index 9fb2c59168..1b1252244b 100644 --- a/src/qibo/models/error_mitigation.py +++ b/src/qibo/models/error_mitigation.py @@ -205,7 +205,7 @@ def sample_training_circuit_cdr( gates_to_replace = [] for i, gate in enumerate(circuit.queue): if isinstance(gate, gates.RZ): - if gate.init_kwargs["theta"] % (np.pi / 2) != 0.0: + if not gate.clifford: gates_to_replace.append((i, gate)) if not gates_to_replace: @@ -736,7 +736,7 @@ def sample_clifford_training_circuit( random_clifford(1, backend=backend, return_circuit=False), q, ) - gate_rand._clifford = True + gate_rand.clifford = True sampled_circuit.add(gate_rand) sampled_circuit.add(gate) else: @@ -747,7 +747,7 @@ def sample_clifford_training_circuit( ), *gate.qubits, ) - gate._clifford = True + gate.clifford = True sampled_circuit.add(gate) return sampled_circuit @@ -824,7 +824,7 @@ def error_sensitive_circuit(circuit, observable, backend=None): ) adjustment_gate = gates.Unitary(random_init, i) - adjustment_gate._clifford = True + adjustment_gate.clifford = True adjustment_gates.append(adjustment_gate) sensitive_circuit = sampled_circuit.__class__(**sampled_circuit.init_kwargs) From 6a98c3f1aa59a6411b6f777ea4534ea2b5c682cc Mon Sep 17 00:00:00 2001 From: MatteoRobbiati Date: Tue, 19 Mar 2024 09:43:14 +0100 Subject: [PATCH 14/16] tests: update qem tests according to clifford property --- tests/test_models_error_mitigation.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/tests/test_models_error_mitigation.py b/tests/test_models_error_mitigation.py index 0fb90b2c20..3881b2b0db 100644 --- a/tests/test_models_error_mitigation.py +++ b/tests/test_models_error_mitigation.py @@ -215,8 +215,6 @@ def test_sample_training_circuit(nqubits): c.add(gates.CNOT(q, q + 1) for q in range(1, nqubits, 2)) c.add(gates.M(q) for q in range(nqubits)) - for j in range(len(c.queue)): - c.queue[j]._clifford = True with pytest.raises(ValueError): sample_training_circuit_cdr(c) with pytest.raises(ValueError): From ee71a91c51f3756a137961a1269f924a66efb5fe Mon Sep 17 00:00:00 2001 From: Matteo Robbiati <62071516+MatteoRobbiati@users.noreply.github.com> Date: Wed, 20 Mar 2024 08:08:37 +0100 Subject: [PATCH 15/16] Update src/qibo/gates/gates.py Co-authored-by: Renato Mello --- src/qibo/gates/gates.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/qibo/gates/gates.py b/src/qibo/gates/gates.py index 2378b38bfc..7249719103 100644 --- a/src/qibo/gates/gates.py +++ b/src/qibo/gates/gates.py @@ -529,7 +529,7 @@ def __init__(self, *q, delay: int = 0): self.target_qubits = tuple(q) -def is_clifford_given_angle(angle): +def _is_clifford_given_angle(angle): """Helper function to update Clifford boolean condition according to the given angle ``angle``.""" return isinstance(angle, (float, int)) and (angle % (np.pi / 2)).is_integer() From 704cf98e0f1d41a1918f8ab5c62031a1a49aebd3 Mon Sep 17 00:00:00 2001 From: MatteoRobbiati Date: Wed, 20 Mar 2024 08:11:47 +0100 Subject: [PATCH 16/16] fix: update clifford check function's name --- src/qibo/gates/gates.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/qibo/gates/gates.py b/src/qibo/gates/gates.py index 7249719103..dfbe628a00 100644 --- a/src/qibo/gates/gates.py +++ b/src/qibo/gates/gates.py @@ -562,7 +562,7 @@ def __init__(self, q, theta, trainable=True): @property def clifford(self): - return is_clifford_given_angle(self.parameters[0]) + return _is_clifford_given_angle(self.parameters[0]) def _dagger(self) -> "Gate": """""" @@ -1233,7 +1233,7 @@ def __init__(self, q0, q1, theta, trainable=True): @property def clifford(self): - return is_clifford_given_angle(self.parameters[0]) + return _is_clifford_given_angle(self.parameters[0]) def _dagger(self) -> "Gate": """"""