sdim.circuit_io
1from .circuit import Circuit 2from .unitary import * 3import cirq 4import os 5 6def read_circuit(filename): 7 """ 8 Reads a circuit from a file and creates a Circuit object. 9 10 Args: 11 filename (str): The name of the file containing the circuit description. 12 13 Returns: 14 Circuit: A Circuit object representing the circuit described in the file. 15 16 Raises: 17 ValueError: If an unexpected number of arguments is found for a gate. 18 """ 19 # Get the directory of the current script 20 script_dir = os.path.dirname(os.path.realpath(__file__)) 21 parent_dir = os.path.join(script_dir, '..') 22 23 # Construct the absolute path to the file 24 abs_file_path = os.path.join(parent_dir, filename) 25 26 with open(abs_file_path, 'r') as file: 27 lines = file.readlines() 28 29 # Find the line with only '#' 30 start_index = next(i for i, line in enumerate(lines) if line.strip() == '#') 31 32 # Extract the lines after '#' 33 gate_lines = lines[start_index + 1:] 34 35 # Default dimension 36 dimension = 2 37 38 # Check the line immediately after '#' 39 parts = gate_lines[0].split() 40 if parts[0].upper() == 'D': 41 dimension = int(parts[1]) 42 gate_lines = gate_lines[1:] 43 44 # Find the max integer in the gate lines 45 max_int = max(int(s) for line in gate_lines for s in line.split() if s.isdigit()) 46 47 # Create a circuit with max_int - 1 qubits and the specified dimension 48 circuit = Circuit(max_int+1, dimension) 49 50 # Append the gates to the circuit 51 for line in gate_lines: 52 if not line.strip(): 53 continue 54 parts = line.split() 55 gate_name = parts[0].upper() 56 gate_qubits = [int(qubit) for qubit in parts[1:]] 57 58 # The number of arguments is the number of parts minus 1 (for the gate name) 59 num_args = len(parts) - 1 60 61 if num_args == 1: 62 # Single-qubit gate 63 circuit.add_gate(gate_name, gate_qubits[0]) 64 elif num_args == 2: 65 # Two-qubit gate 66 circuit.add_gate(gate_name, gate_qubits[0], gate_qubits[1]) 67 else: 68 raise ValueError(f"Unexpected number of arguments for gate {gate_name}") 69 70 return circuit 71 72def write_circuit(circuit: Circuit, output_file: str = "random_circuit.chp", comment: str = "", directory: str = None): 73 """ 74 Writes a Circuit object to a file in the .chp format. 75 76 Args: 77 circuit (Circuit): The Circuit object to write. 78 output_file (str): The name of the output file. Defaults to "random_circuit.chp". 79 comment (str): An optional comment to include at the beginning of the file. 80 directory (str): Optional directory to save the file. If None, uses the default '../circuits/' relative to the script. 81 82 Returns: 83 str: The path to the written file. 84 """ 85 chp_content = "" 86 if comment: 87 chp_content += f"{comment}\n#\n" 88 else: 89 chp_content += "Randomly-generated Clifford group quantum circuit\n#\n" 90 chp_content += f"d {circuit.dimension}\n" 91 92 for gate in circuit.operations: 93 gate_str = gate.gate_name 94 if gate.target_index is not None: 95 gate_str += f" {gate.qudit_index} {gate.target_index}" 96 else: 97 gate_str += f" {gate.qudit_index}" 98 chp_content += f"{gate_str}\n" 99 100 if directory is None: 101 script_dir = os.path.dirname(os.path.realpath(__file__)) 102 directory = os.path.join(script_dir, '../circuits/') 103 104 # Create the directory if it doesn't exist 105 os.makedirs(directory, exist_ok=True) 106 107 # Join the directory with the output file name 108 output_path = os.path.join(directory, output_file) 109 110 # Write the content to the .chp file 111 with open(output_path, "w") as file: 112 file.write(chp_content) 113 114 return output_path 115 116 117def circuit_to_cirq_circuit(circuit, measurement=False, print_circuit=False): 118 """ 119 Converts a Circuit object to a Cirq Circuit object. 120 121 Args: 122 circuit (Circuit): The Circuit object to convert. 123 measurement (bool): Whether to include measurement gates. Defaults to False. 124 print_circuit (bool): Whether to print the Cirq circuit. Defaults to False. 125 126 Returns: 127 cirq.Circuit: The equivalent Cirq Circuit object. 128 """ 129 # Create a list of qudits. 130 qudits = [cirq.LineQid(i, dimension=circuit.dimension) for i in range(circuit.num_qudits)] 131 132 # Create the generalized gates. 133 gate_map = { 134 "I": IdentityGate(circuit.dimension), 135 "H": GeneralizedHadamardGate(circuit.dimension), 136 "P": GeneralizedPhaseShiftGate(circuit.dimension), 137 "CNOT": GeneralizedCNOTGate(circuit.dimension), 138 "X": GeneralizedXPauliGate(circuit.dimension), 139 "Z": GeneralizedZPauliGate(circuit.dimension), 140 "H_INV": cirq.inverse(GeneralizedHadamardGate(circuit.dimension)), 141 "P_INV": cirq.inverse(GeneralizedPhaseShiftGate(circuit.dimension)), 142 "CNOT_INV": cirq.inverse(GeneralizedCNOTGate(circuit.dimension)), 143 "X_INV": cirq.inverse(GeneralizedXPauliGate(circuit.dimension)), 144 "Z_INV": cirq.inverse(GeneralizedZPauliGate(circuit.dimension)), 145 } 146 147 # Create a Cirq circuit. 148 cirq_circuit = cirq.Circuit() 149 150 # Apply each gate in the circuit. 151 for op in circuit.operations: 152 # Choose the appropriate gate. 153 if op.name in gate_map: 154 gate = gate_map[op.name] 155 # Add the gate to the Cirq circuit. 156 if op.target_index is None: 157 # Single-qudit gate. 158 cirq_circuit.append(gate.on(qudits[op.qudit_index])) 159 else: 160 # Two-qudit gate. 161 cirq_circuit.append(gate.on(qudits[op.qudit_index], qudits[op.target_index])) 162 elif op.name == "M": 163 if measurement: 164 cirq_circuit.append(cirq.measure(qudits[op.qudit_index], key=f'm_{op.qudit_index}')) 165 continue 166 else: 167 raise NotImplementedError(f"Gate {op.name} not implemented") 168 for qudit in qudits: 169 if not any(op.qubits[0] == qudit for op in cirq_circuit.all_operations()): 170 # Append identity to qudits with no gates 171 cirq_circuit.append(IdentityGate(circuit.dimension).on(qudit)) 172 if print_circuit: 173 print(cirq_circuit) 174 return cirq_circuit 175 176def cirq_statevector_from_circuit(circuit, print_circuit=False): 177 """ 178 Simulates a Circuit object using Cirq and returns the final state vector. 179 180 Args: 181 circuit (Circuit): The Circuit object to simulate. 182 print_circuit (bool): Whether to print the Cirq circuit. Defaults to False. 183 184 Returns: 185 np.ndarray: The final state vector given by the Cirq simulator. 186 """ 187 # Start with an initial state. For a quantum computer, this is usually the state |0...0>. 188 cirq_circuit = circuit_to_cirq_circuit(circuit, print_circuit=print_circuit) 189 # Simulate the Cirq circuit. 190 simulator = cirq.Simulator() 191 result = simulator.simulate(cirq_circuit) 192 193 # Return the final state vector. 194 return result.final_state_vector
def
read_circuit(filename):
7def read_circuit(filename): 8 """ 9 Reads a circuit from a file and creates a Circuit object. 10 11 Args: 12 filename (str): The name of the file containing the circuit description. 13 14 Returns: 15 Circuit: A Circuit object representing the circuit described in the file. 16 17 Raises: 18 ValueError: If an unexpected number of arguments is found for a gate. 19 """ 20 # Get the directory of the current script 21 script_dir = os.path.dirname(os.path.realpath(__file__)) 22 parent_dir = os.path.join(script_dir, '..') 23 24 # Construct the absolute path to the file 25 abs_file_path = os.path.join(parent_dir, filename) 26 27 with open(abs_file_path, 'r') as file: 28 lines = file.readlines() 29 30 # Find the line with only '#' 31 start_index = next(i for i, line in enumerate(lines) if line.strip() == '#') 32 33 # Extract the lines after '#' 34 gate_lines = lines[start_index + 1:] 35 36 # Default dimension 37 dimension = 2 38 39 # Check the line immediately after '#' 40 parts = gate_lines[0].split() 41 if parts[0].upper() == 'D': 42 dimension = int(parts[1]) 43 gate_lines = gate_lines[1:] 44 45 # Find the max integer in the gate lines 46 max_int = max(int(s) for line in gate_lines for s in line.split() if s.isdigit()) 47 48 # Create a circuit with max_int - 1 qubits and the specified dimension 49 circuit = Circuit(max_int+1, dimension) 50 51 # Append the gates to the circuit 52 for line in gate_lines: 53 if not line.strip(): 54 continue 55 parts = line.split() 56 gate_name = parts[0].upper() 57 gate_qubits = [int(qubit) for qubit in parts[1:]] 58 59 # The number of arguments is the number of parts minus 1 (for the gate name) 60 num_args = len(parts) - 1 61 62 if num_args == 1: 63 # Single-qubit gate 64 circuit.add_gate(gate_name, gate_qubits[0]) 65 elif num_args == 2: 66 # Two-qubit gate 67 circuit.add_gate(gate_name, gate_qubits[0], gate_qubits[1]) 68 else: 69 raise ValueError(f"Unexpected number of arguments for gate {gate_name}") 70 71 return circuit
Reads a circuit from a file and creates a Circuit object.
Arguments:
- filename (str): The name of the file containing the circuit description.
Returns:
Circuit: A Circuit object representing the circuit described in the file.
Raises:
- ValueError: If an unexpected number of arguments is found for a gate.
def
write_circuit( circuit: sdim.circuit.Circuit, output_file: str = 'random_circuit.chp', comment: str = '', directory: str = None):
73def write_circuit(circuit: Circuit, output_file: str = "random_circuit.chp", comment: str = "", directory: str = None): 74 """ 75 Writes a Circuit object to a file in the .chp format. 76 77 Args: 78 circuit (Circuit): The Circuit object to write. 79 output_file (str): The name of the output file. Defaults to "random_circuit.chp". 80 comment (str): An optional comment to include at the beginning of the file. 81 directory (str): Optional directory to save the file. If None, uses the default '../circuits/' relative to the script. 82 83 Returns: 84 str: The path to the written file. 85 """ 86 chp_content = "" 87 if comment: 88 chp_content += f"{comment}\n#\n" 89 else: 90 chp_content += "Randomly-generated Clifford group quantum circuit\n#\n" 91 chp_content += f"d {circuit.dimension}\n" 92 93 for gate in circuit.operations: 94 gate_str = gate.gate_name 95 if gate.target_index is not None: 96 gate_str += f" {gate.qudit_index} {gate.target_index}" 97 else: 98 gate_str += f" {gate.qudit_index}" 99 chp_content += f"{gate_str}\n" 100 101 if directory is None: 102 script_dir = os.path.dirname(os.path.realpath(__file__)) 103 directory = os.path.join(script_dir, '../circuits/') 104 105 # Create the directory if it doesn't exist 106 os.makedirs(directory, exist_ok=True) 107 108 # Join the directory with the output file name 109 output_path = os.path.join(directory, output_file) 110 111 # Write the content to the .chp file 112 with open(output_path, "w") as file: 113 file.write(chp_content) 114 115 return output_path
Writes a Circuit object to a file in the .chp format.
Arguments:
- circuit (Circuit): The Circuit object to write.
- output_file (str): The name of the output file. Defaults to "random_circuit.chp".
- comment (str): An optional comment to include at the beginning of the file.
- directory (str): Optional directory to save the file. If None, uses the default '../circuits/' relative to the script.
Returns:
str: The path to the written file.
def
circuit_to_cirq_circuit(circuit, measurement=False, print_circuit=False):
118def circuit_to_cirq_circuit(circuit, measurement=False, print_circuit=False): 119 """ 120 Converts a Circuit object to a Cirq Circuit object. 121 122 Args: 123 circuit (Circuit): The Circuit object to convert. 124 measurement (bool): Whether to include measurement gates. Defaults to False. 125 print_circuit (bool): Whether to print the Cirq circuit. Defaults to False. 126 127 Returns: 128 cirq.Circuit: The equivalent Cirq Circuit object. 129 """ 130 # Create a list of qudits. 131 qudits = [cirq.LineQid(i, dimension=circuit.dimension) for i in range(circuit.num_qudits)] 132 133 # Create the generalized gates. 134 gate_map = { 135 "I": IdentityGate(circuit.dimension), 136 "H": GeneralizedHadamardGate(circuit.dimension), 137 "P": GeneralizedPhaseShiftGate(circuit.dimension), 138 "CNOT": GeneralizedCNOTGate(circuit.dimension), 139 "X": GeneralizedXPauliGate(circuit.dimension), 140 "Z": GeneralizedZPauliGate(circuit.dimension), 141 "H_INV": cirq.inverse(GeneralizedHadamardGate(circuit.dimension)), 142 "P_INV": cirq.inverse(GeneralizedPhaseShiftGate(circuit.dimension)), 143 "CNOT_INV": cirq.inverse(GeneralizedCNOTGate(circuit.dimension)), 144 "X_INV": cirq.inverse(GeneralizedXPauliGate(circuit.dimension)), 145 "Z_INV": cirq.inverse(GeneralizedZPauliGate(circuit.dimension)), 146 } 147 148 # Create a Cirq circuit. 149 cirq_circuit = cirq.Circuit() 150 151 # Apply each gate in the circuit. 152 for op in circuit.operations: 153 # Choose the appropriate gate. 154 if op.name in gate_map: 155 gate = gate_map[op.name] 156 # Add the gate to the Cirq circuit. 157 if op.target_index is None: 158 # Single-qudit gate. 159 cirq_circuit.append(gate.on(qudits[op.qudit_index])) 160 else: 161 # Two-qudit gate. 162 cirq_circuit.append(gate.on(qudits[op.qudit_index], qudits[op.target_index])) 163 elif op.name == "M": 164 if measurement: 165 cirq_circuit.append(cirq.measure(qudits[op.qudit_index], key=f'm_{op.qudit_index}')) 166 continue 167 else: 168 raise NotImplementedError(f"Gate {op.name} not implemented") 169 for qudit in qudits: 170 if not any(op.qubits[0] == qudit for op in cirq_circuit.all_operations()): 171 # Append identity to qudits with no gates 172 cirq_circuit.append(IdentityGate(circuit.dimension).on(qudit)) 173 if print_circuit: 174 print(cirq_circuit) 175 return cirq_circuit
Converts a Circuit object to a Cirq Circuit object.
Arguments:
- circuit (Circuit): The Circuit object to convert.
- measurement (bool): Whether to include measurement gates. Defaults to False.
- print_circuit (bool): Whether to print the Cirq circuit. Defaults to False.
Returns:
cirq.Circuit: The equivalent Cirq Circuit object.
def
cirq_statevector_from_circuit(circuit, print_circuit=False):
177def cirq_statevector_from_circuit(circuit, print_circuit=False): 178 """ 179 Simulates a Circuit object using Cirq and returns the final state vector. 180 181 Args: 182 circuit (Circuit): The Circuit object to simulate. 183 print_circuit (bool): Whether to print the Cirq circuit. Defaults to False. 184 185 Returns: 186 np.ndarray: The final state vector given by the Cirq simulator. 187 """ 188 # Start with an initial state. For a quantum computer, this is usually the state |0...0>. 189 cirq_circuit = circuit_to_cirq_circuit(circuit, print_circuit=print_circuit) 190 # Simulate the Cirq circuit. 191 simulator = cirq.Simulator() 192 result = simulator.simulate(cirq_circuit) 193 194 # Return the final state vector. 195 return result.final_state_vector
Simulates a Circuit object using Cirq and returns the final state vector.
Arguments:
- circuit (Circuit): The Circuit object to simulate.
- print_circuit (bool): Whether to print the Cirq circuit. Defaults to False.
Returns:
np.ndarray: The final state vector given by the Cirq simulator.