Skip to content

Commit

Permalink
Migrated to Qiskit 1.0
Browse files Browse the repository at this point in the history
  • Loading branch information
GabrieleMessina authored Jan 5, 2025
1 parent a4deb58 commit 7c06959
Show file tree
Hide file tree
Showing 6 changed files with 87 additions and 61 deletions.
3 changes: 2 additions & 1 deletion requirements.txt
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
antlr4-python3-runtime==4.13.0
qiskit<1.0
qiskit>=1.0
qiskit_ibm_runtime
qiskit-aer
anytree
matplotlib
Expand Down
2 changes: 1 addition & 1 deletion src/grammar_frontend/code_execution/operations_visitor.py
Original file line number Diff line number Diff line change
Expand Up @@ -418,7 +418,7 @@ def visitGroverOperator(self, ctx:qutes_parser.GroverOperatorContext):
phase_kickback_ancilla = self.quantum_circuit_handler.declare_quantum_register(f"phase_kickback_ancilla_{current_grover_count}", Qubit(0,1))
oracle_registers.append(phase_kickback_ancilla)
if(rotation_register == None):
rotation_register = self.quantum_circuit_handler.declare_quantum_register(f"rotation(grover:{current_grover_count})", Quint.init_from_size(logn,True))
rotation_register = self.quantum_circuit_handler.declare_quantum_register(f"rotation_grover_{current_grover_count}", Quint.init_from_size(logn,True))
oracle_registers.append(rotation_register)
if(self.log_grover_esm_rotation):
registers_to_measure.append(rotation_register)
Expand Down
8 changes: 6 additions & 2 deletions src/quantum_circuit/classical_register.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,9 @@
from qiskit import ClassicalRegister as cr

class ClassicalRegister(cr):
measured_values:list = None
measured_counts:list = None
def __init__(self, size:int, name:str):
super().__init__(size, name)
if not name.replace('_', '').isalnum():
raise RuntimeError(f"Classical register names must be valid identifiers, but '{var_name}' is not. Valid identifiers contain only alphanumeric letters (a-z and A-Z), decimal digits (0-9), or underscores (_).")
self.measured_values = []
self.measured_counts = []
124 changes: 71 additions & 53 deletions src/quantum_circuit/quantum_circuit_handler.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,12 @@
from quantum_circuit.quantum_circuit import QuantumCircuit
from quantum_circuit.quantum_register import QuantumRegister, QiskitQubit
from symbols.types import Qubit, Quint, Qustring, QutesDataType, QuantumArrayType
from qiskit import IBMQ, Aer, QiskitError, transpile
from qiskit import QiskitError
from qiskit_aer import AerSimulator
from qiskit.transpiler.preset_passmanagers import generate_preset_pass_manager
from qiskit_ibm_runtime import SamplerV2 as Sampler
from quantum_circuit.state_preparation import StatePreparation
from qiskit.primitives import Sampler
from qiskit.circuit.quantumcircuit import QubitSpecifier, CircuitInstruction
from qiskit.circuit.quantumcircuit import CircuitInstruction
from qiskit.circuit.library import GroverOperator, MCMT, ZGate, QFT, XGate, YGate, HGate, CXGate, MCXGate, PhaseGate
from qiskit.circuit.gate import Gate

Expand Down Expand Up @@ -150,69 +152,85 @@ def print_circuit(self, circuit:QuantumCircuit, save_image:bool = False, print_c
if(print_circuit_to_console):
print(circuit.draw())

def __counts__(self, vec):
for i in vec:
print(" - " + str(i) +" : "+ str(vec[i]))

def __revcounts__(self, vec):
for i in vec:
print(" - " + str(i)[::-1] +" : "+ str(vec[i]))
def get_counts_by_run(self, result) -> dict[str, int]:
# {bitstring: count}
return result[0].join_data().get_counts()
def get_counts_by_register(self, result) -> dict[str, dict[str, int]]:
# {reg_name: {bitstring: count}}
cnt:dict[dict[int]] = {}
for i, pub_res in enumerate(result): # For each circuit
for reg_name in pub_res.data:
reg_name = f'{reg_name}' if i == 0 else f'{reg_name}_circ_{i}'
cnt[reg_name] = getattr(pub_res.data,reg_name).get_counts()
return cnt

# simulate the execution of a Quantum Circuit and get the results
def __run__(self, circuit, shots, print_counts:bool = False):
# Use Aer's qasm_simulator
simulator = Aer.get_backend('aer_simulator')
simulator = AerSimulator()
pm = generate_preset_pass_manager(backend=simulator, optimization_level=1)

# compile the circuit down to low-level QASM instructions
# supported by the backend (not needed for simple circuits)
compiled_circuit = transpile(circuit, simulator)
isa_qc = pm.run(circuit)
sampler = Sampler(mode=simulator)

# Execute the circuit on the qasm simulator
job = simulator.run(compiled_circuit, shots=shots)

# Grab results from the job
result = job.result()
cnt = None
result = sampler.run([isa_qc], shots=shots).result()
counts_by_registers = {}
try:
cnt = result.get_counts(compiled_circuit)
counts_by_registers = self.get_counts_by_register(result)
except QiskitError as er:
if(er.message.startswith("No counts for experiment")):
print(er.message)
else:
raise er

if(cnt != None):
table = []
for index, run in enumerate(cnt):
count = cnt[run]
# reverse the bits to have the least significant as rightmost,
# and the order of the measured variables from left to right where in the circuit were from top to bottom
# values = [f"{value[::-1]}₂ ←→ {int(value[::-1], 2):>4}⏨" for value in run.split(" ")[::-1]]
values = [f"{value}₂ | {int(value, 2)}⏨" for value in run.split(" ")[::-1]]
values.append(count)
values.append("Least Significant bit as rightmost") if(index == 0) else values.append("")
table.append(values)

if(print_counts):
from tabulate import tabulate
print("⚠️ ~ Following results only show the last execution of the circuit, in case of measurements in the middle of the circuit, like the ones needed for casts and Grover search, those results are not shown.")
headers = [f"{creg[0]}" for creg in cnt.creg_sizes]
headers.append("Counts")
headers.append("Notes")
maxcolwidths = [40] * len(headers)
maxcolwidths[-1] = 40
colalign = ["right"] * len(headers)
colalign[-1] = "left"
print(tabulate(table, headers=headers, stralign="right", tablefmt="fancy_grid", maxcolwidths=maxcolwidths, colalign=colalign))

measurement_for_runs = [res.split(" ")[::-1] for res in cnt.keys()] # reverse the order of the measured variables from left to right where in the circuit were from top to bottom
counts_for_runs = [res[1] for res in cnt.items()]
for index in range(len(cnt.creg_sizes)):
measurement_for_variable = [a[index] for a in measurement_for_runs] # reverse the bits to have the least significant as rightmost
Classical_registers = [reg for reg in self._classic_registers if reg.name == cnt.creg_sizes[index][0]]
Classical_registers[0].measured_values = measurement_for_variable
Classical_registers[0].measured_counts = counts_for_runs
return cnt
for reg_name, counts in counts_by_registers.items():
for bitstring, count in counts.items():
classical_registers = [reg for reg in self._classic_registers if reg.name == reg_name]
classical_registers[0].measured_values.append(bitstring)
classical_registers[0].measured_counts.append(count)

if(print_counts):
self.print_result_table(result)


def print_result_table(self, result):
counts_by_run = {}
counts_by_registers = {}
try:
counts_by_run = self.get_counts_by_run(result)
counts_by_registers = self.get_counts_by_register(result)
except QiskitError as er:
pass

from tabulate import tabulate
table = []
for index, (result, count) in enumerate(counts_by_run.items()):
i = 0
while i < len(result):
row = []
for reg_name in list(counts_by_registers.keys())[::-1]:
# reverse the regs list to match the qiskit ordering,
# measured variables from right to left based on measuring time
reg_size = self._varname_to_register[reg_name].size
bitstring = result[i:i+reg_size]
row.append(f"{bitstring}₂ | {int(bitstring, 2)}⏨")
i += reg_size
row = row[::-1] # recover ordering to have first measured as first column
row.append(count)
row.append("Least Significant bit as rightmost") if(index == 0) else row.append("")
table.append(row)

print("⚠️ ~ Following results only show the last execution of the circuit, in case of measurements in the middle of the circuit, like the ones needed for casts and Grover search, those results are not shown.")
headers = [f"{reg_name}" for reg_name in counts_by_registers.keys()]
headers.append("Counts")
headers.append("Notes")
maxcolwidths = [40] * len(headers)
maxcolwidths[-1] = 40
colalign = ["right"] * len(headers)
colalign[-1] = "left"
print(tabulate(table, headers=headers, stralign="right", tablefmt="fancy_grid", maxcolwidths=maxcolwidths, colalign=colalign))


def run_circuit(self, circuit:QuantumCircuit, repetition:int = 1, print_count:bool = False):
self.__run__(circuit, repetition, print_count)
Expand Down Expand Up @@ -375,7 +393,7 @@ def push_grover_operation(self, *oracle_registers, quantum_function:QuantumCircu
if(isinstance(instruction, CircuitInstruction)):
name : str = instruction.operation.name
op = instruction.operation
if(name.startswith("c") and name.endswith("z")):
if(name == "mcmt" and op.base_gate.name == "z"):
boolean_quantum_function.data[index] = CircuitInstruction(MCXGate(op.num_qubits), [*instruction.qubits,*oracle_registers[-1]], instruction.clbits)

# check if the grover result is actually a hit.
Expand Down
6 changes: 4 additions & 2 deletions src/quantum_circuit/quantum_register.py
Original file line number Diff line number Diff line change
@@ -1,11 +1,13 @@
from qiskit import QuantumRegister as qr
from quantum_circuit.classical_register import ClassicalRegister
from qiskit.circuit.quantumregister import Qubit as QiskitQubit
from qiskit.circuit.quantumregister import Qubit as QiskitQubit #TODO: move, is used weirdly as import in circuit handler


class QuantumRegister(qr):
def __init__(self, size, var_name, bits = None):
def __init__(self, size, var_name:str, bits = None):
super().__init__(size, var_name, bits)
if not var_name.replace('_', '').isalnum():
raise RuntimeError(f"Quantum register names must be valid identifiers, but '{var_name}' is not. Valid identifiers contain only alphanumeric letters (a-z and A-Z), decimal digits (0-9), or underscores (_).")
self.measured_classical_register:ClassicalRegister | None = None

def __len__(self):
Expand Down
5 changes: 3 additions & 2 deletions src/tests/test_grammar.py
Original file line number Diff line number Diff line change
Expand Up @@ -79,20 +79,21 @@ def test_classic_type_declaration_succeed(self):
("bool", "TRUE", True),
("bool", "1", True),
("bool", "1q", True),
("int", "10q", 10),
("bool", "0q", False),
("int", "10", 10),
("int", "9", 9),
("int", "false+false", 0),
("int", "true+true", 2),
("int", "10q", 10),
("string", "\"test\"", "test"),
("string", "\"test\" + \"sum\"", "testsum"),
("string", "1", "True"), #TODO: We should expect "1" not "True"
("string", "2", "2"),
]
var_name:str = "foo"
for var_type, declaration_value, expected_value_of_var in params:
for index, (var_type, declaration_value, expected_value_of_var) in enumerate(params):
with self.subTest(var_type=var_type, declaration_value=declaration_value, expected_value_of_var=expected_value_of_var):
var_name = f"foo_{index}"
code = f"""
{var_type} {var_name} = {declaration_value};
"""
Expand Down

0 comments on commit 7c06959

Please sign in to comment.