refactoring

This commit is contained in:
Noa Aarts 2026-01-27 09:34:23 +01:00
parent 19e90b3d21
commit 421fb17062
Signed by: noa
GPG key ID: 1850932741EFF672
2 changed files with 77 additions and 52 deletions

View file

@ -20,10 +20,20 @@ PARENT_AMOUNT = 5
MUTATION_RATE = 0.1 MUTATION_RATE = 0.1
gate_set = [
GateType.H,
GateType.RX,
GateType.RY,
GateType.RZ,
GateType.CRX,
GateType.CX,
]
def main(): def main():
seed_rng = random.Random(1020381) seed_rng = random.Random(1020381)
initial_population: list[QuantumCircuit] = ( initial_population: list[QuantumCircuit] = (
Stream(sample_random_generator(random.Random(101020), QUBITS, DEPTH)) Stream(sample_random_generator(random.Random(101020), QUBITS, DEPTH, gate_set))
.apply(lambda circ: print(circ)) .apply(lambda circ: print(circ))
.apply( .apply(
lambda circ: circ.expressibility_estimate( lambda circ: circ.expressibility_estimate(
@ -55,23 +65,20 @@ def main():
layer = child_layers[layer_idx] layer = child_layers[layer_idx]
gate_idx = main_rng.randrange(len(layer)) gate_idx = main_rng.randrange(len(layer))
old_gate = child_layers[layer_idx][gate_idx] old_gate = child_layers[layer_idx][gate_idx]
match old_gate.type:
case GateType.H | GateType.RX | GateType.RY | GateType.RZ: if old_gate.single():
child_layers[layer_idx][gate_idx] = Gate( child_layers[layer_idx][gate_idx] = Gate(
main_rng.choice( main_rng.choice(gate_set),
[GateType.H, GateType.RX, GateType.RY, GateType.RZ] old_gate.qubits,
), old_gate.param_idx,
old_gate.qubits, )
old_gate.param_idx, else:
) child_layers[layer_idx][gate_idx] = Gate(
case GateType.CRX | GateType.CX: old_gate.typ,
child_layers[layer_idx][gate_idx] = Gate( tuple(old_gate.qubits[::-1]),
old_gate.type, old_gate.param_idx,
tuple(old_gate.qubits[::-1]), )
old_gate.param_idx,
)
case _:
print(f"unhandled gate: {old_gate}")
child = circ_from_layers(child_layers, QUBITS) child = circ_from_layers(child_layers, QUBITS)
child.expressibility_estimate(2000, seed_rng.randint(1000, 1000000000)) child.expressibility_estimate(2000, seed_rng.randint(1000, 1000000000))
offspring.append(child) offspring.append(child)

View file

@ -25,21 +25,40 @@ class GateType(IntEnum):
CZ = 8 CZ = 8
CRX = 9 CRX = 9
H = 10 H = 10
I = 11
def single_typ(typ: GateType) -> bool:
match typ:
case GateType.I | GateType.H | GateType.RX | GateType.RY | GateType.RZ:
return True
case (
GateType.RXX
| GateType.RYY
| GateType.RZZ
| GateType.CX
| GateType.CZ
| GateType.CRX
):
return False
@dataclass(frozen=True) @dataclass(frozen=True)
class Gate: class Gate:
type: GateType typ: GateType
qubits: int | tuple[int, int] qubits: int | tuple[int, int]
param_idx: int param_idx: int
def to_json(self): def to_json(self):
return { return {
"type": int(self.type), "type": int(self.typ),
"qubits": self.qubits, "qubits": self.qubits,
"param_idx": self.param_idx, "param_idx": self.param_idx,
} }
def single(self) -> bool:
return single_typ(self.typ)
def haar_fidelity_pdf(F: np.ndarray, d: int) -> np.ndarray: def haar_fidelity_pdf(F: np.ndarray, d: int) -> np.ndarray:
# p(F) = (d-1) * (1-F)^(d-2), for F in [0,1] # p(F) = (d-1) * (1-F)^(d-2), for F in [0,1]
@ -71,7 +90,7 @@ class QuantumCircuit:
path_counts = [1 for _ in range(self.qubits)] path_counts = [1 for _ in range(self.qubits)]
for layer in self.gates: for layer in self.gates:
for gate in layer: for gate in layer:
if gate.type <= 3 or isinstance(gate.qubits, int): if gate.typ <= 3 or isinstance(gate.qubits, int):
continue continue
(q1, q2) = gate.qubits (q1, q2) = gate.qubits
# same logic as your existing file # same logic as your existing file
@ -99,33 +118,35 @@ class QuantumCircuit:
for layer in self.gates: for layer in self.gates:
for gate in layer: for gate in layer:
if gate.type == GateType.RX: if gate.typ == GateType.RX:
theta = thetas[gate.param_idx] theta = thetas[gate.param_idx]
qc.rx(theta, gate.qubits) qc.rx(theta, gate.qubits)
elif gate.type == GateType.RY: elif gate.typ == GateType.RY:
theta = thetas[gate.param_idx] theta = thetas[gate.param_idx]
qc.ry(theta, gate.qubits) qc.ry(theta, gate.qubits)
elif gate.type == GateType.RZ: elif gate.typ == GateType.RZ:
theta = thetas[gate.param_idx] theta = thetas[gate.param_idx]
qc.rz(theta, gate.qubits) qc.rz(theta, gate.qubits)
elif gate.type == GateType.H: elif gate.typ == GateType.H:
qc.h(gate.qubits) qc.h(gate.qubits)
elif gate.type == GateType.RXX: elif gate.typ == GateType.I:
qc.id(gate.qubits)
elif gate.typ == GateType.RXX:
theta = thetas[gate.param_idx] theta = thetas[gate.param_idx]
qc.rxx(theta, *gate.qubits) qc.rxx(theta, *gate.qubits)
elif gate.type == GateType.RYY: elif gate.typ == GateType.RYY:
theta = thetas[gate.param_idx] theta = thetas[gate.param_idx]
qc.ryy(theta, *gate.qubits) qc.ryy(theta, *gate.qubits)
elif gate.type == GateType.RZZ: elif gate.typ == GateType.RZZ:
theta = thetas[gate.param_idx] theta = thetas[gate.param_idx]
qc.rzz(theta, *gate.qubits) qc.rzz(theta, *gate.qubits)
elif gate.type == GateType.CX: elif gate.typ == GateType.CX:
qc.cx(*gate.qubits) qc.cx(*gate.qubits)
elif gate.type == GateType.CRX: elif gate.typ == GateType.CRX:
theta = thetas[gate.param_idx] theta = thetas[gate.param_idx]
qc.crx(theta, *gate.qubits) qc.crx(theta, *gate.qubits)
else: else:
raise ValueError(f"Unknown gate type: {gate.type}") raise ValueError(f"Unknown gate type: {gate.typ}")
return qc, thetas return qc, thetas
def to_qiskit_for_expressibility(self) -> tuple[QiskitCircuit, ParameterVector]: def to_qiskit_for_expressibility(self) -> tuple[QiskitCircuit, ParameterVector]:
@ -205,7 +226,7 @@ class QuantumCircuit:
idx = 0 idx = 0
for gate in layer: for gate in layer:
match gate.type: match gate.typ:
case GateType.RX: case GateType.RX:
strs[gate.qubits] = strs[gate.qubits][:-2] + "RX" strs[gate.qubits] = strs[gate.qubits][:-2] + "RX"
case GateType.RY: case GateType.RY:
@ -311,23 +332,23 @@ def circ_from_layers(layers: list[list[Gate]], qubits: int) -> QuantumCircuit:
for layer in layers: for layer in layers:
new_layer = [] new_layer = []
for gate in layer: for gate in layer:
match gate.type: match gate.typ:
case GateType.H: case GateType.H:
new_layer.append(Gate(gate.type, gate.qubits, params)) new_layer.append(Gate(gate.typ, gate.qubits, params))
total_single += 1 total_single += 1
case GateType.RX | GateType.RY | GateType.RZ: case GateType.RX | GateType.RY | GateType.RZ:
new_layer.append(Gate(gate.type, gate.qubits, params)) new_layer.append(Gate(gate.typ, gate.qubits, params))
params += 1 params += 1
total_single += 1 total_single += 1
case GateType.RXX | GateType.RYY | GateType.RZZ: case GateType.RXX | GateType.RYY | GateType.RZZ:
new_layer.append(Gate(gate.type, gate.qubits, params)) new_layer.append(Gate(gate.typ, gate.qubits, params))
params += 1 params += 1
total_double += 1 total_double += 1
case GateType.CX | GateType.CZ: case GateType.CX | GateType.CZ:
new_layer.append(Gate(gate.type, gate.qubits, params)) new_layer.append(Gate(gate.typ, gate.qubits, params))
total_double += 1 total_double += 1
case GateType.CRX: case GateType.CRX:
new_layer.append(Gate(gate.type, gate.qubits, params)) new_layer.append(Gate(gate.typ, gate.qubits, params))
params += 1 params += 1
total_double += 1 total_double += 1
gates.append(new_layer) gates.append(new_layer)
@ -335,7 +356,7 @@ def circ_from_layers(layers: list[list[Gate]], qubits: int) -> QuantumCircuit:
def sample_circuit_random( def sample_circuit_random(
rng: random.Random, qubits: int, depth: int rng: random.Random, qubits: int, depth: int, gate_types: list[GateType]
) -> QuantumCircuit: ) -> QuantumCircuit:
params = 0 params = 0
total_single = 0 total_single = 0
@ -349,14 +370,9 @@ def sample_circuit_random(
if loc in used_qubits: if loc in used_qubits:
continue continue
gate_type = rng.choice( gate_type = rng.choice(
[ gate_types
GateType.RX, if loc + 1 < qubits
GateType.RY, else [typ for typ in gate_types if single_typ(typ)]
GateType.RZ,
GateType.CX,
GateType.CRX,
GateType.H,
]
) )
match gate_type: match gate_type:
case GateType.H: case GateType.H:
@ -368,13 +384,13 @@ def sample_circuit_random(
params += 1 params += 1
total_single += 1 total_single += 1
used_qubits.add(loc) used_qubits.add(loc)
case GateType.RXX | GateType.RYY | GateType.RZZ if loc + 1 < qubits: case GateType.RXX | GateType.RYY | GateType.RZZ:
layer.append(Gate(gate_type, (loc, loc + 1), params)) layer.append(Gate(gate_type, (loc, loc + 1), params))
params += 1 params += 1
total_double += 1 total_double += 1
used_qubits.add(loc) used_qubits.add(loc)
used_qubits.add(loc + 1) used_qubits.add(loc + 1)
case GateType.CX | GateType.CZ if loc + 1 < qubits: case GateType.CX | GateType.CZ:
layer.append( layer.append(
Gate( Gate(
gate_type, gate_type,
@ -385,7 +401,7 @@ def sample_circuit_random(
total_double += 1 total_double += 1
used_qubits.add(loc) used_qubits.add(loc)
used_qubits.add(loc + 1) used_qubits.add(loc + 1)
case GateType.CRX if loc + 1 < qubits: case GateType.CRX:
layer.append( layer.append(
Gate( Gate(
gate_type, gate_type,
@ -404,6 +420,8 @@ def sample_circuit_random(
return QuantumCircuit(qubits, gates, total_single, total_double, params) return QuantumCircuit(qubits, gates, total_single, total_double, params)
def sample_random_generator(rng: random.Random, qubits: int, depth: int): def sample_random_generator(
rng: random.Random, qubits: int, depth: int, gates: list[GateType]
):
while True: while True:
yield sample_circuit_random(rng, qubits, depth) yield sample_circuit_random(rng, qubits, depth, gates)