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.