Compare commits
No commits in common. "f3e9e420a71770d4be9c3230a5a651d3026f33ba" and "19e90b3d2186925b9dc7d0b04ece25f01ab3641a" have entirely different histories.
f3e9e420a7
...
19e90b3d21
5 changed files with 73 additions and 119 deletions
|
|
@ -43,7 +43,6 @@
|
|||
ppkgs.tqdm
|
||||
ppkgs.qiskit
|
||||
ppkgs.qiskit-aer
|
||||
ppkgs.matplotlib
|
||||
]))
|
||||
];
|
||||
};
|
||||
|
|
|
|||
0
src/__init__.py
Normal file
0
src/__init__.py
Normal file
Binary file not shown.
|
Before Width: | Height: | Size: 22 KiB |
|
|
@ -3,42 +3,27 @@
|
|||
# "Genetic optimization of ansatz expressibility for enhanced variational quantum algorithm performance"
|
||||
|
||||
import random
|
||||
|
||||
import matplotlib.pyplot as plt
|
||||
|
||||
from quantum_circuit import (
|
||||
Gate,
|
||||
GateType,
|
||||
QuantumCircuit,
|
||||
circ_from_layers,
|
||||
sample_random_generator,
|
||||
)
|
||||
from qas_flow import Stream
|
||||
from quantum_circuit import (Gate, GateType, QuantumCircuit, circ_from_layers,
|
||||
sample_random_generator, single_typ)
|
||||
|
||||
DEPTH: int = 6
|
||||
QUBITS: int = 6
|
||||
GENERATIONS: int = 40
|
||||
GENERATION_SIZE: int = 60
|
||||
PARENT_AMOUNT: int = 10
|
||||
MUTATION_RATE: float = 0.1
|
||||
DEPTH = 20
|
||||
QUBITS = 5
|
||||
GENERATIONS = 20
|
||||
GENERATION_SIZE = 20
|
||||
PARENT_AMOUNT = 5
|
||||
MUTATION_RATE = 0.1
|
||||
|
||||
|
||||
gate_set: list[GateType] = [
|
||||
GateType.H,
|
||||
GateType.RX,
|
||||
GateType.RY,
|
||||
GateType.RZ,
|
||||
GateType.CRX,
|
||||
GateType.CX,
|
||||
]
|
||||
|
||||
|
||||
def plot_best_circuits(best_circuits: list[QuantumCircuit]) -> None:
|
||||
fig, ax = plt.subplots()
|
||||
|
||||
ax.plot([-circ.expressibility for circ in best_circuits])
|
||||
fig.savefig("best_circuits.png")
|
||||
|
||||
|
||||
def main() -> None:
|
||||
seed_rng: random.Random = random.Random(1020381)
|
||||
def main():
|
||||
seed_rng = random.Random(1020381)
|
||||
initial_population: list[QuantumCircuit] = (
|
||||
Stream(sample_random_generator(random.Random(101020), QUBITS, DEPTH, gate_set))
|
||||
Stream(sample_random_generator(random.Random(101020), QUBITS, DEPTH))
|
||||
.apply(lambda circ: print(circ))
|
||||
.apply(
|
||||
lambda circ: circ.expressibility_estimate(
|
||||
|
|
@ -54,8 +39,6 @@ def main() -> None:
|
|||
|
||||
main_rng = random.Random(2837175)
|
||||
|
||||
best_circuits: list[QuantumCircuit] = []
|
||||
|
||||
for generation in range(GENERATIONS):
|
||||
print(f"starting generation {generation}")
|
||||
population.sort(key=lambda qc: qc.expressibility, reverse=True)
|
||||
|
|
@ -72,38 +55,28 @@ def main() -> None:
|
|||
layer = child_layers[layer_idx]
|
||||
gate_idx = main_rng.randrange(len(layer))
|
||||
old_gate = child_layers[layer_idx][gate_idx]
|
||||
|
||||
if old_gate.single():
|
||||
child_layers[layer_idx][gate_idx] = Gate(
|
||||
main_rng.choice(
|
||||
[gate for gate in gate_set if single_typ(gate)]
|
||||
),
|
||||
old_gate.qubits,
|
||||
old_gate.param_idx,
|
||||
)
|
||||
else:
|
||||
child_layers[layer_idx][gate_idx] = Gate(
|
||||
old_gate.typ,
|
||||
(old_gate.qubits[1], old_gate.qubits[0]),
|
||||
old_gate.param_idx,
|
||||
)
|
||||
|
||||
match old_gate.type:
|
||||
case GateType.H | GateType.RX | GateType.RY | GateType.RZ:
|
||||
child_layers[layer_idx][gate_idx] = Gate(
|
||||
main_rng.choice(
|
||||
[GateType.H, GateType.RX, GateType.RY, GateType.RZ]
|
||||
),
|
||||
old_gate.qubits,
|
||||
old_gate.param_idx,
|
||||
)
|
||||
case GateType.CRX | GateType.CX:
|
||||
child_layers[layer_idx][gate_idx] = Gate(
|
||||
old_gate.type,
|
||||
tuple(old_gate.qubits[::-1]),
|
||||
old_gate.param_idx,
|
||||
)
|
||||
case _:
|
||||
print(f"unhandled gate: {old_gate}")
|
||||
child = circ_from_layers(child_layers, QUBITS)
|
||||
child.expressibility_estimate(2000, seed_rng.randint(1000, 1000000000))
|
||||
offspring.append(child)
|
||||
|
||||
offspring.sort(key=lambda qc: qc.expressibility, reverse=True)
|
||||
if population[0].expressibility > offspring[0].expressibility:
|
||||
print(f"best parent > best child")
|
||||
best_circuits.append(population[0])
|
||||
else:
|
||||
print(f"best child > best parent")
|
||||
best_circuits.append(offspring[0])
|
||||
population = offspring
|
||||
|
||||
plot_best_circuits(best_circuits)
|
||||
plt.show()
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
|
|
|
|||
|
|
@ -1,12 +1,11 @@
|
|||
from dataclasses import dataclass
|
||||
import math
|
||||
import random
|
||||
from dataclasses import dataclass
|
||||
from enum import IntEnum
|
||||
from typing import override
|
||||
|
||||
import numpy as np
|
||||
from qiskit import QuantumCircuit as QiskitCircuit
|
||||
from qiskit import transpile
|
||||
from enum import IntEnum
|
||||
|
||||
from qiskit import QuantumCircuit as QiskitCircuit, transpile
|
||||
from qiskit.circuit import ParameterVector, ParameterVectorElement
|
||||
from qiskit_aer import AerSimulator
|
||||
|
||||
|
|
@ -26,40 +25,21 @@ class GateType(IntEnum):
|
|||
CZ = 8
|
||||
CRX = 9
|
||||
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)
|
||||
class Gate:
|
||||
typ: GateType
|
||||
type: GateType
|
||||
qubits: int | tuple[int, int]
|
||||
param_idx: int
|
||||
|
||||
def to_json(self):
|
||||
return {
|
||||
"type": int(self.typ),
|
||||
"type": int(self.type),
|
||||
"qubits": self.qubits,
|
||||
"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:
|
||||
# p(F) = (d-1) * (1-F)^(d-2), for F in [0,1]
|
||||
|
|
@ -91,7 +71,7 @@ class QuantumCircuit:
|
|||
path_counts = [1 for _ in range(self.qubits)]
|
||||
for layer in self.gates:
|
||||
for gate in layer:
|
||||
if gate.typ <= 3 or isinstance(gate.qubits, int):
|
||||
if gate.type <= 3 or isinstance(gate.qubits, int):
|
||||
continue
|
||||
(q1, q2) = gate.qubits
|
||||
# same logic as your existing file
|
||||
|
|
@ -119,35 +99,33 @@ class QuantumCircuit:
|
|||
|
||||
for layer in self.gates:
|
||||
for gate in layer:
|
||||
if gate.typ == GateType.RX:
|
||||
if gate.type == GateType.RX:
|
||||
theta = thetas[gate.param_idx]
|
||||
qc.rx(theta, gate.qubits)
|
||||
elif gate.typ == GateType.RY:
|
||||
elif gate.type == GateType.RY:
|
||||
theta = thetas[gate.param_idx]
|
||||
qc.ry(theta, gate.qubits)
|
||||
elif gate.typ == GateType.RZ:
|
||||
elif gate.type == GateType.RZ:
|
||||
theta = thetas[gate.param_idx]
|
||||
qc.rz(theta, gate.qubits)
|
||||
elif gate.typ == GateType.H:
|
||||
elif gate.type == GateType.H:
|
||||
qc.h(gate.qubits)
|
||||
elif gate.typ == GateType.I:
|
||||
qc.id(gate.qubits)
|
||||
elif gate.typ == GateType.RXX:
|
||||
elif gate.type == GateType.RXX:
|
||||
theta = thetas[gate.param_idx]
|
||||
qc.rxx(theta, *gate.qubits)
|
||||
elif gate.typ == GateType.RYY:
|
||||
elif gate.type == GateType.RYY:
|
||||
theta = thetas[gate.param_idx]
|
||||
qc.ryy(theta, *gate.qubits)
|
||||
elif gate.typ == GateType.RZZ:
|
||||
elif gate.type == GateType.RZZ:
|
||||
theta = thetas[gate.param_idx]
|
||||
qc.rzz(theta, *gate.qubits)
|
||||
elif gate.typ == GateType.CX:
|
||||
elif gate.type == GateType.CX:
|
||||
qc.cx(*gate.qubits)
|
||||
elif gate.typ == GateType.CRX:
|
||||
elif gate.type == GateType.CRX:
|
||||
theta = thetas[gate.param_idx]
|
||||
qc.crx(theta, *gate.qubits)
|
||||
else:
|
||||
raise ValueError(f"Unknown gate type: {gate.typ}")
|
||||
raise ValueError(f"Unknown gate type: {gate.type}")
|
||||
return qc, thetas
|
||||
|
||||
def to_qiskit_for_expressibility(self) -> tuple[QiskitCircuit, ParameterVector]:
|
||||
|
|
@ -227,7 +205,7 @@ class QuantumCircuit:
|
|||
|
||||
idx = 0
|
||||
for gate in layer:
|
||||
match gate.typ:
|
||||
match gate.type:
|
||||
case GateType.RX:
|
||||
strs[gate.qubits] = strs[gate.qubits][:-2] + "RX"
|
||||
case GateType.RY:
|
||||
|
|
@ -333,23 +311,23 @@ def circ_from_layers(layers: list[list[Gate]], qubits: int) -> QuantumCircuit:
|
|||
for layer in layers:
|
||||
new_layer = []
|
||||
for gate in layer:
|
||||
match gate.typ:
|
||||
match gate.type:
|
||||
case GateType.H:
|
||||
new_layer.append(Gate(gate.typ, gate.qubits, params))
|
||||
new_layer.append(Gate(gate.type, gate.qubits, params))
|
||||
total_single += 1
|
||||
case GateType.RX | GateType.RY | GateType.RZ:
|
||||
new_layer.append(Gate(gate.typ, gate.qubits, params))
|
||||
new_layer.append(Gate(gate.type, gate.qubits, params))
|
||||
params += 1
|
||||
total_single += 1
|
||||
case GateType.RXX | GateType.RYY | GateType.RZZ:
|
||||
new_layer.append(Gate(gate.typ, gate.qubits, params))
|
||||
new_layer.append(Gate(gate.type, gate.qubits, params))
|
||||
params += 1
|
||||
total_double += 1
|
||||
case GateType.CX | GateType.CZ:
|
||||
new_layer.append(Gate(gate.typ, gate.qubits, params))
|
||||
new_layer.append(Gate(gate.type, gate.qubits, params))
|
||||
total_double += 1
|
||||
case GateType.CRX:
|
||||
new_layer.append(Gate(gate.typ, gate.qubits, params))
|
||||
new_layer.append(Gate(gate.type, gate.qubits, params))
|
||||
params += 1
|
||||
total_double += 1
|
||||
gates.append(new_layer)
|
||||
|
|
@ -357,7 +335,7 @@ def circ_from_layers(layers: list[list[Gate]], qubits: int) -> QuantumCircuit:
|
|||
|
||||
|
||||
def sample_circuit_random(
|
||||
rng: random.Random, qubits: int, depth: int, gate_types: list[GateType]
|
||||
rng: random.Random, qubits: int, depth: int
|
||||
) -> QuantumCircuit:
|
||||
params = 0
|
||||
total_single = 0
|
||||
|
|
@ -370,10 +348,16 @@ def sample_circuit_random(
|
|||
for loc in range(qubits):
|
||||
if loc in used_qubits:
|
||||
continue
|
||||
if loc + 1 < qubits:
|
||||
gate_type = rng.choice(gate_types)
|
||||
else:
|
||||
gate_type = rng.choice([typ for typ in gate_types if single_typ(typ)])
|
||||
gate_type = rng.choice(
|
||||
[
|
||||
GateType.RX,
|
||||
GateType.RY,
|
||||
GateType.RZ,
|
||||
GateType.CX,
|
||||
GateType.CRX,
|
||||
GateType.H,
|
||||
]
|
||||
)
|
||||
match gate_type:
|
||||
case GateType.H:
|
||||
layer.append(Gate(gate_type, loc, params))
|
||||
|
|
@ -384,13 +368,13 @@ def sample_circuit_random(
|
|||
params += 1
|
||||
total_single += 1
|
||||
used_qubits.add(loc)
|
||||
case GateType.RXX | GateType.RYY | GateType.RZZ:
|
||||
case GateType.RXX | GateType.RYY | GateType.RZZ if loc + 1 < qubits:
|
||||
layer.append(Gate(gate_type, (loc, loc + 1), params))
|
||||
params += 1
|
||||
total_double += 1
|
||||
used_qubits.add(loc)
|
||||
used_qubits.add(loc + 1)
|
||||
case GateType.CX | GateType.CZ:
|
||||
case GateType.CX | GateType.CZ if loc + 1 < qubits:
|
||||
layer.append(
|
||||
Gate(
|
||||
gate_type,
|
||||
|
|
@ -401,7 +385,7 @@ def sample_circuit_random(
|
|||
total_double += 1
|
||||
used_qubits.add(loc)
|
||||
used_qubits.add(loc + 1)
|
||||
case GateType.CRX:
|
||||
case GateType.CRX if loc + 1 < qubits:
|
||||
layer.append(
|
||||
Gate(
|
||||
gate_type,
|
||||
|
|
@ -420,8 +404,6 @@ def sample_circuit_random(
|
|||
return QuantumCircuit(qubits, gates, total_single, total_double, params)
|
||||
|
||||
|
||||
def sample_random_generator(
|
||||
rng: random.Random, qubits: int, depth: int, gates: list[GateType]
|
||||
):
|
||||
def sample_random_generator(rng: random.Random, qubits: int, depth: int):
|
||||
while True:
|
||||
yield sample_circuit_random(rng, qubits, depth, gates)
|
||||
yield sample_circuit_random(rng, qubits, depth)
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue