diff --git a/qiskit_dynamics/backend/backend_string_parser/hamiltonian_string_parser.py b/qiskit_dynamics/backend/backend_string_parser/hamiltonian_string_parser.py index 9e94deaba..883c4b862 100644 --- a/qiskit_dynamics/backend/backend_string_parser/hamiltonian_string_parser.py +++ b/qiskit_dynamics/backend/backend_string_parser/hamiltonian_string_parser.py @@ -163,12 +163,12 @@ def parse_backend_hamiltonian_dict( # force keys in hamiltonian['qub'] to be ints qub_dict = {int(key): val for key, val in hamiltonian_dict["qub"].items()} - subsystem_dims = {int(qubit): qub_dict[int(qubit)] for qubit in subsystem_list} + subsystem_dims_dict = {int(qubit): qub_dict[int(qubit)] for qubit in subsystem_list} # Parse the Hamiltonian system = _regex_parser( operator_str=hamiltonian_dict["h_str"], - subsystem_dims=subsystem_dims, + subsystem_dims_dict=subsystem_dims_dict, subsystem_list=subsystem_list, ) @@ -227,7 +227,12 @@ def parse_backend_hamiltonian_dict( *sorted(zip(reduced_channels, hamiltonian_operators)) ) - return static_hamiltonian, list(hamiltonian_operators), list(reduced_channels), subsystem_dims + return ( + static_hamiltonian, + list(hamiltonian_operators), + list(reduced_channels), + subsystem_dims_dict, + ) def _hamiltonian_pre_parse_exceptions(hamiltonian_dict: dict): diff --git a/qiskit_dynamics/backend/backend_string_parser/operator_from_string.py b/qiskit_dynamics/backend/backend_string_parser/operator_from_string.py index ff96c2008..45ada7a4e 100644 --- a/qiskit_dynamics/backend/backend_string_parser/operator_from_string.py +++ b/qiskit_dynamics/backend/backend_string_parser/operator_from_string.py @@ -26,14 +26,14 @@ def _operator_from_string( - op_label: str, subsystem_label: int, subsystem_dims: Dict[int, int] + op_label: str, subsystem_label: int, subsystem_dims_dict: Dict[int, int] ) -> np.ndarray: r"""Generates a dense operator acting on a single subsystem, tensoring identities for remaining subsystems. The single system operator is specified via a string in ``op_label``, the list of subsystems and their corresponding dimensions are specified in the - dictionary ``subsystem_dims``, with system label being the keys specified as ``int``s, + dictionary ``subsystem_dims_dict``, with system label being the keys specified as ``int``s, and system dimensions the values also specified as ``int``s, and ``subsystem_label`` indicates which subsystem the operator specified by ``op_label`` acts on. @@ -61,7 +61,7 @@ def _operator_from_string( Args: op_label: The string labelling the single system operator. subsystem_label: Index of the subsystem to apply the operator. - subsystem_dims: Dictionary of subsystem labels and dimensions. + subsystem_dims_dict: Dictionary of subsystem labels and dimensions. Returns: np.ndarray corresponding to the specified operator. @@ -75,12 +75,12 @@ def _operator_from_string( if op_func is None: raise QiskitError(f"String {op_label} does not correspond to a known operator.") - dim = subsystem_dims[subsystem_label] + dim = subsystem_dims_dict[subsystem_label] out = qi.Operator(op_func(dim), input_dims=[dim], output_dims=[dim]) # sort subsystem labels and dimensions according to subsystem label sorted_subsystem_keys, sorted_subsystem_dims = zip( - *sorted(zip(subsystem_dims.keys(), subsystem_dims.values())) + *sorted(zip(subsystem_dims_dict.keys(), subsystem_dims_dict.values())) ) # get subsystem location in ordered list diff --git a/qiskit_dynamics/backend/backend_string_parser/regex_parser.py b/qiskit_dynamics/backend/backend_string_parser/regex_parser.py index b6b643ff8..624d4385e 100644 --- a/qiskit_dynamics/backend/backend_string_parser/regex_parser.py +++ b/qiskit_dynamics/backend/backend_string_parser/regex_parser.py @@ -29,20 +29,20 @@ def _regex_parser( - operator_str: List[str], subsystem_dims: Dict[int, int], subsystem_list: List[int] + operator_str: List[str], subsystem_dims_dict: Dict[int, int], subsystem_list: List[int] ) -> List[Tuple[np.array, str]]: """Function wrapper for regex parsing object. Args: operator_str: List of strings in accepted format as described in string_model_parser.parse_hamiltonian_dict. - subsystem_dims: Dictionary mapping subsystem labels to dimensions. + subsystem_dims_dict: Dictionary mapping subsystem labels to dimensions. subsystem_list: List of subsystems on which the operators are to be constructed. Returns: List of tuples containing pairs operators and their string coefficients. """ - return _HamiltonianParser(h_str=operator_str, subsystem_dims=subsystem_dims).parse( + return _HamiltonianParser(h_str=operator_str, subsystem_dims_dict=subsystem_dims_dict).parse( subsystem_list ) @@ -66,15 +66,17 @@ class _HamiltonianParser: BrkR=re.compile(r"\)"), ) - def __init__(self, h_str, subsystem_dims): + def __init__(self, h_str, subsystem_dims_dict): """Create new quantum operator generator Parameters: h_str (list): list of Hamiltonian string - subsystem_dims (dict): dimension of subsystems + subsystem_dims_dict (dict): dimension of subsystems """ self.h_str = h_str - self.subsystem_dims = {int(label): int(dim) for label, dim in subsystem_dims.items()} + self.subsystem_dims_dict = { + int(label): int(dim) for label, dim in subsystem_dims_dict.items() + } self.str2qopr = {} def parse(self, qubit_list=None): @@ -194,7 +196,7 @@ def _tokenizer(self, op_str, qubit_list=None): if qubit_list is not None and idx not in qubit_list: return 0, None name = p.group("opr") - opr = _operator_from_string(name, idx, self.subsystem_dims) + opr = _operator_from_string(name, idx, self.subsystem_dims_dict) self.str2qopr[p.group()] = opr elif key == "PrjOpr": _key = key @@ -202,7 +204,7 @@ def _tokenizer(self, op_str, qubit_list=None): if p.group() not in self.str2qopr: idx = int(p.group("idx")) name = "P" - opr = _operator_from_string(name, idx, self.subsystem_dims) + opr = _operator_from_string(name, idx, self.subsystem_dims_dict) self.str2qopr[p.group()] = opr elif key in ["Func", "Ext"]: _name = p.group("name") diff --git a/test/dynamics/backend/backend_string_parser/test_hamiltonian_string_parser.py b/test/dynamics/backend/backend_string_parser/test_hamiltonian_string_parser.py index 5c8943987..1bf26dd1e 100644 --- a/test/dynamics/backend/backend_string_parser/test_hamiltonian_string_parser.py +++ b/test/dynamics/backend/backend_string_parser/test_hamiltonian_string_parser.py @@ -123,11 +123,13 @@ def test_only_static_terms(self): """Test a basic system.""" ham_dict = {"h_str": ["v*np.pi*Z0"], "qub": {"0": 2}, "vars": {"v": 2.1}} - static_ham, ham_ops, channels, subsystem_dims = parse_backend_hamiltonian_dict(ham_dict) + static_ham, ham_ops, channels, subsystem_dims_dict = parse_backend_hamiltonian_dict( + ham_dict + ) self.assertAllClose(static_ham, 2.1 * np.pi * self.Z) self.assertTrue(not ham_ops) self.assertTrue(not channels) - self.assertTrue(subsystem_dims == {0: 2}) + self.assertTrue(subsystem_dims_dict == {0: 2}) def test_simple_single_q_system(self): """Test a basic system.""" @@ -137,12 +139,14 @@ def test_simple_single_q_system(self): "vars": {"v": 2.1}, } - static_ham, ham_ops, channels, subsystem_dims = parse_backend_hamiltonian_dict(ham_dict) + static_ham, ham_ops, channels, subsystem_dims_dict = parse_backend_hamiltonian_dict( + ham_dict + ) self.assertAllClose(static_ham, 2.1 * np.pi * self.Z) self.assertAllClose(to_array(ham_ops), [0.02 * np.pi * self.X]) self.assertTrue(channels == ["d0"]) - self.assertTrue(subsystem_dims == {0: 2}) + self.assertTrue(subsystem_dims_dict == {0: 2}) def test_simple_single_q_system_repeat_entries(self): """Test merging of terms with same channel or no channel.""" @@ -152,12 +156,14 @@ def test_simple_single_q_system_repeat_entries(self): "vars": {"v": 2.1}, } - static_ham, ham_ops, channels, subsystem_dims = parse_backend_hamiltonian_dict(ham_dict) + static_ham, ham_ops, channels, subsystem_dims_dict = parse_backend_hamiltonian_dict( + ham_dict + ) self.assertAllClose(static_ham, 2 * 2.1 * np.pi * self.Z) self.assertAllClose(to_array(ham_ops), [2 * 0.02 * np.pi * self.X]) self.assertTrue(channels == ["d0"]) - self.assertTrue(subsystem_dims == {0: 2}) + self.assertTrue(subsystem_dims_dict == {0: 2}) def test_simple_single_q_system_repeat_entries_different_case(self): """Test merging of terms with same channel or no channel, @@ -169,12 +175,14 @@ def test_simple_single_q_system_repeat_entries_different_case(self): "vars": {"v": 2.1}, } - static_ham, ham_ops, channels, subsystem_dims = parse_backend_hamiltonian_dict(ham_dict) + static_ham, ham_ops, channels, subsystem_dims_dict = parse_backend_hamiltonian_dict( + ham_dict + ) self.assertAllClose(static_ham, 2 * 2.1 * np.pi * self.Z) self.assertAllClose(to_array(ham_ops), [2 * 0.02 * np.pi * self.X]) self.assertTrue(channels == ["d0"]) - self.assertTrue(subsystem_dims == {0: 2}) + self.assertTrue(subsystem_dims_dict == {0: 2}) def test_simple_two_q_system(self): """Test a two qubit system.""" @@ -191,7 +199,9 @@ def test_simple_two_q_system(self): "vars": {"v0": 2.1, "v1": 2.0, "j": 0.02}, } - static_ham, ham_ops, channels, subsystem_dims = parse_backend_hamiltonian_dict(ham_dict) + static_ham, ham_ops, channels, subsystem_dims_dict = parse_backend_hamiltonian_dict( + ham_dict + ) ident = np.eye(2) self.assertAllClose( @@ -205,7 +215,7 @@ def test_simple_two_q_system(self): [0.02 * np.pi * np.kron(ident, self.X), 0.03 * np.pi * np.kron(self.X, ident)], ) self.assertTrue(channels == ["d0", "d1"]) - self.assertTrue(subsystem_dims == {0: 2, 1: 2}) + self.assertTrue(subsystem_dims_dict == {0: 2, 1: 2}) def test_simple_two_q_system_measurement_channel(self): """Test a two qubit system with a measurement-labelled channel.""" @@ -222,7 +232,9 @@ def test_simple_two_q_system_measurement_channel(self): "vars": {"v0": 2.1, "v1": 2.0, "j": 0.02}, } - static_ham, ham_ops, channels, subsystem_dims = parse_backend_hamiltonian_dict(ham_dict) + static_ham, ham_ops, channels, subsystem_dims_dict = parse_backend_hamiltonian_dict( + ham_dict + ) ident = np.eye(2) self.assertAllClose( @@ -236,7 +248,7 @@ def test_simple_two_q_system_measurement_channel(self): [0.02 * np.pi * np.kron(ident, self.X), 0.03 * np.pi * np.kron(self.X, ident)], ) self.assertTrue(channels == ["d0", "m1"]) - self.assertTrue(subsystem_dims == {0: 2, 1: 2}) + self.assertTrue(subsystem_dims_dict == {0: 2, 1: 2}) def test_single_oscillator_system(self): """Test single oscillator system.""" @@ -247,12 +259,14 @@ def test_single_oscillator_system(self): "vars": {"v": 2.1, "alpha": -0.33, "r": 0.02}, } - static_ham, ham_ops, channels, subsystem_dims = parse_backend_hamiltonian_dict(ham_dict) + static_ham, ham_ops, channels, subsystem_dims_dict = parse_backend_hamiltonian_dict( + ham_dict + ) self.assertAllClose(static_ham, 2.1 * np.pi * self.N - 0.33 * np.pi * self.N * self.N) self.assertAllClose(to_array(ham_ops), [0.02 * np.pi * (self.a + self.adag)]) self.assertTrue(channels == ["d0"]) - self.assertTrue(subsystem_dims == {0: 4}) + self.assertTrue(subsystem_dims_dict == {0: 4}) def test_two_oscillator_system(self): """Test a two qubit system.""" @@ -271,7 +285,9 @@ def test_two_oscillator_system(self): "vars": {"v0": 2.1, "v1": 2.0, "alpha0": -0.33, "alpha1": -0.33, "j": 0.02}, } - static_ham, ham_ops, channels, subsystem_dims = parse_backend_hamiltonian_dict(ham_dict) + static_ham, ham_ops, channels, subsystem_dims_dict = parse_backend_hamiltonian_dict( + ham_dict + ) ident = np.eye(4) @@ -291,7 +307,7 @@ def test_two_oscillator_system(self): ], ) self.assertTrue(channels == ["d0", "d1"]) - self.assertTrue(subsystem_dims == {0: 4, 1: 4}) + self.assertTrue(subsystem_dims_dict == {0: 4, 1: 4}) def test_single_q_high_dim(self): """Test single q system but higher dim.""" @@ -301,12 +317,14 @@ def test_single_q_high_dim(self): "vars": {"v": 2.1}, } - static_ham, ham_ops, channels, subsystem_dims = parse_backend_hamiltonian_dict(ham_dict) + static_ham, ham_ops, channels, subsystem_dims_dict = parse_backend_hamiltonian_dict( + ham_dict + ) self.assertAllClose(static_ham, 2.1 * np.pi * (np.eye(4) - 2 * self.N)) self.assertAllClose(to_array(ham_ops), [0.02 * np.pi * (self.a + self.adag)]) self.assertTrue(channels == ["d0"]) - self.assertTrue(subsystem_dims == {0: 4}) + self.assertTrue(subsystem_dims_dict == {0: 4}) def test_dagger(self): """Test correct parsing of dagger.""" @@ -397,13 +415,13 @@ def test_5q_hamiltonian_reduced(self): ) channels_expected = ["d0", "d1", "u0", "u1", "u2"] - static_ham, ham_ops, channels, subsystem_dims = parse_backend_hamiltonian_dict( + static_ham, ham_ops, channels, subsystem_dims_dict = parse_backend_hamiltonian_dict( ham_dict, subsystem_list=[0, 1] ) self.assertAllClose(static_ham, static_ham_expected) self.assertAllClose(ham_ops, ham_ops_expected) self.assertTrue(channels == channels_expected) - self.assertTrue(subsystem_dims == {0: 4, 1: 4}) + self.assertTrue(subsystem_dims_dict == {0: 4, 1: 4}) # test case for subsystems [3, 4] @@ -427,10 +445,10 @@ def test_5q_hamiltonian_reduced(self): ) channels_expected = ["d3", "d4", "u5", "u6", "u7"] - static_ham, ham_ops, channels, subsystem_dims = parse_backend_hamiltonian_dict( + static_ham, ham_ops, channels, subsystem_dims_dict = parse_backend_hamiltonian_dict( ham_dict, subsystem_list=[3, 4] ) self.assertAllClose(static_ham, static_ham_expected) self.assertAllClose(ham_ops, ham_ops_expected) self.assertTrue(channels == channels_expected) - self.assertTrue(subsystem_dims == {3: 4, 4: 4}) + self.assertTrue(subsystem_dims_dict == {3: 4, 4: 4})