diff --git a/qiskit_experiments/library/randomized_benchmarking/clifford_utils.py b/qiskit_experiments/library/randomized_benchmarking/clifford_utils.py index 8dae210f42..666f8b0cc9 100644 --- a/qiskit_experiments/library/randomized_benchmarking/clifford_utils.py +++ b/qiskit_experiments/library/randomized_benchmarking/clifford_utils.py @@ -46,6 +46,7 @@ _valid_sparse_indices = _clifford_compose_2q_data["valid_sparse_indices"] # map a clifford number to the index of _CLIFFORD_COMPOSE_2Q_DENSE _clifford_num_to_dense_index = {idx: ii for ii, idx in enumerate(_valid_sparse_indices)} +_CLIFFORD_TENSOR_1Q = np.load(f"{_DATA_FOLDER}/clifford_tensor_1q.npz")["table"] # Transpilation utilities def _transpile_clifford_circuit( @@ -740,12 +741,6 @@ def _layer_indices_from_num(num: Integral) -> Tuple[Integral, Integral, Integral return idx0, idx1, idx2 -@lru_cache(maxsize=24 * 24) -def _product_1q_nums(first: Integral, second: Integral) -> Integral: - """Return the 2-qubit Clifford integer that represents the product of 1-qubit Cliffords.""" - qc0 = CliffordUtils.clifford_1_qubit_circuit(first) - qc1 = CliffordUtils.clifford_1_qubit_circuit(second) - qc = QuantumCircuit(2) - qc.compose(qc0, qubits=(0,), inplace=True) - qc.compose(qc1, qubits=(1,), inplace=True) - return num_from_2q_circuit(qc) +def _tensor_1q_nums(first: Integral, second: Integral) -> Integral: + """Return the 2-qubit Clifford integer that is the tensor product of 1-qubit Cliffords.""" + return _CLIFFORD_TENSOR_1Q[first, second] diff --git a/qiskit_experiments/library/randomized_benchmarking/data/clifford_tensor_1q.npz b/qiskit_experiments/library/randomized_benchmarking/data/clifford_tensor_1q.npz new file mode 100644 index 0000000000..7725682f70 Binary files /dev/null and b/qiskit_experiments/library/randomized_benchmarking/data/clifford_tensor_1q.npz differ diff --git a/qiskit_experiments/library/randomized_benchmarking/data/generate_clifford_data.py b/qiskit_experiments/library/randomized_benchmarking/data/generate_clifford_data.py index 7ebd45dcab..5193dc35d8 100644 --- a/qiskit_experiments/library/randomized_benchmarking/data/generate_clifford_data.py +++ b/qiskit_experiments/library/randomized_benchmarking/data/generate_clifford_data.py @@ -59,7 +59,7 @@ def _hash_cliff(cliff): def gen_clifford_inverse_1q(): - """Generate table data for integer 1Q Clifford inversion""" + """Generate data for integer 1Q Clifford inversion table""" invs = np.empty(NUM_CLIFFORD_1Q, dtype=int) for i, cliff_i in _CLIFF_1Q.items(): invs[i] = _TO_INT_1Q[_hash_cliff(cliff_i.adjoint())] @@ -68,7 +68,7 @@ def gen_clifford_inverse_1q(): def gen_clifford_compose_1q(): - """Generate table data for integer 1Q Clifford composition.""" + """Generate data for integer 1Q Clifford composition table""" products = np.empty((NUM_CLIFFORD_1Q, NUM_CLIFFORD_1Q), dtype=int) for i, cliff_i in _CLIFF_1Q.items(): for j, cliff_j in _CLIFF_1Q.items(): @@ -83,7 +83,7 @@ def gen_clifford_compose_1q(): def gen_clifford_inverse_2q(): - """Generate table data for integer 2Q Clifford inversion""" + """Generate data for integer 2Q Clifford inversion table""" invs = np.empty(NUM_CLIFFORD_2Q, dtype=int) for i, cliff_i in _CLIFF_2Q.items(): invs[i] = _TO_INT_2Q[_hash_cliff(cliff_i.adjoint())] @@ -191,6 +191,16 @@ def gen_cliff_single_2q_gate_map(): return table +def gen_clifford_tensor_1q(): + """Generate data for 2Q integer Clifford table of the tensor product of 1Q integer Cliffords.""" + products = np.empty((NUM_CLIFFORD_1Q, NUM_CLIFFORD_1Q), dtype=int) + for i, cliff_i in _CLIFF_1Q.items(): + for j, cliff_j in _CLIFF_1Q.items(): + cliff = cliff_i.tensor(cliff_j) + products[i, j] = _TO_INT_2Q[_hash_cliff(cliff)] + return products + + if __name__ == "__main__": if _CLIFF_SINGLE_GATE_MAP_1Q != gen_cliff_single_1q_gate_map(): raise Exception( @@ -212,3 +222,5 @@ def gen_cliff_single_2q_gate_map(): table=_CLIFFORD_COMPOSE_2Q_DENSE, valid_sparse_indices=valid_sparse_indices, ) + + np.savez_compressed("clifford_tensor_1q.npz", table=gen_clifford_tensor_1q()) diff --git a/qiskit_experiments/library/randomized_benchmarking/layer_fidelity.py b/qiskit_experiments/library/randomized_benchmarking/layer_fidelity.py index 6dbd80d5c0..40c5247f87 100644 --- a/qiskit_experiments/library/randomized_benchmarking/layer_fidelity.py +++ b/qiskit_experiments/library/randomized_benchmarking/layer_fidelity.py @@ -40,7 +40,7 @@ inverse_1q, inverse_2q, num_from_2q_circuit, - _product_1q_nums, + _tensor_1q_nums, _clifford_1q_int_to_instruction, _clifford_2q_int_to_instruction, _decompose_clifford_ops, @@ -403,8 +403,11 @@ def __circuit_body( for j, qpair in enumerate(two_qubit_layer): # sample product of two 1q-Cliffords as 2q interger Clifford samples = rng.integers(NUM_1Q_CLIFFORD, size=2) - cliffs_2q[j] = compose_2q(cliffs_2q[j], _product_1q_nums(*samples)) - for sample, q in zip(samples, qpair): + cliffs_2q[j] = compose_2q(cliffs_2q[j], _tensor_1q_nums(*samples)) + # For Clifford 1 (x) Clifford 2, in its circuit representation, + # Clifford 1 acts on the 2nd qubit and Clifford 2 acts on the 1st qubit. + # That's why the qpair is reversed here. + for sample, q in zip(samples, reversed(qpair)): circ._append(_to_gate_1q(sample), (circ.qubits[q],), ()) for k, q in enumerate(one_qubits): sample = rng.integers(NUM_1Q_CLIFFORD)