more stuffs

This commit is contained in:
Noa Aarts 2026-02-05 11:52:17 +01:00
parent 9297a6bcaf
commit 9a361417e8
Signed by: noa
GPG key ID: 1850932741EFF672
3 changed files with 58 additions and 60 deletions

View file

@ -48,6 +48,22 @@ def par_map(stream: Stream[T], fn: Callable[[T], U], cores: int = 0) -> Stream[U
return Stream(gen())
@Stream.extension()
def unique(stream: Stream[T]) -> Stream[T]:
"""
Returns the stream with only the first instance of every element
"""
def gen() -> Generator[T, Any, None]:
seen: set[T] = set()
for x in stream:
if x not in seen:
seen.add(x)
yield x
return Stream(gen())
@Stream.extension()
def filter(stream: Stream[T], pred: Callable[[T], bool]) -> Stream[T]:
"""

View file

@ -10,6 +10,8 @@ from qiskit import transpile
from qiskit.circuit import ParameterVector, ParameterVectorElement
from qiskit_aer import AerSimulator
MAX_GATE_NUM: int = 36
class GateType(IntEnum):
"""
@ -33,17 +35,20 @@ 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
):
case GateType.RXX | GateType.RYY | GateType.RZZ | GateType.CX | GateType.CZ | GateType.CRX:
return False
def param_count(typ: GateType) -> int:
match typ:
case GateType.I | GateType.H:
return 0
case GateType.RX | GateType.RY | GateType.RZ | GateType.RXX | GateType.RYY | GateType.RZZ | GateType.CRX:
return 1
case _:
assert False, f"{typ} does not have a known param count"
@dataclass(frozen=True)
class Gate:
typ: GateType
@ -51,11 +56,7 @@ class Gate:
param_idx: int
def to_json(self):
return {
"type": int(self.typ),
"qubits": self.qubits,
"param_idx": self.param_idx,
}
return {"type": int(self.typ), "qubits": self.qubits, "param_idx": self.param_idx}
def single(self) -> bool:
return single_typ(self.typ)
@ -165,9 +166,7 @@ class QuantumCircuit:
qc.save_statevector()
return qc, thetas
def expressibility_estimate(
self, samples: int, seed: int, bins: int = 75, eps: float = 1e-12
) -> Self:
def expressibility_estimate(self, samples: int, seed: int, bins: int = 75, eps: float = 1e-12) -> Self:
qc, thetas = self.to_qiskit_for_expressibility()
if self.params <= 0:
@ -183,25 +182,17 @@ class QuantumCircuit:
nexp = 2 * samples
# vectorized binds: one circuit, many parameter values
binds: list[dict[ParameterVectorElement, list[float]]] = [
{
param: [rng.random() * math.tau for _ in range(nexp)]
for param in thetas.params
}
{param: [rng.random() * math.tau for _ in range(nexp)] for param in thetas.params}
]
job = backend.run([tqc], parameter_binds=binds)
result = job.result()
sv = [
np.asarray(result.get_statevector(i), dtype=np.complex128)
for i in range(nexp)
]
sv = [np.asarray(result.get_statevector(i), dtype=np.complex128) for i in range(nexp)]
left = sv[:samples]
right = sv[samples:]
inners = np.array(
[np.vdot(l, r) for l, r in zip(left, right)], dtype=np.complex128
)
inners = np.array([np.vdot(l, r) for l, r in zip(left, right)], dtype=np.complex128)
F = (inners.conjugate() * inners).real
hist, edges = np.histogram(F, bins=bins, range=(0.0, 1.0), density=False)
@ -286,36 +277,43 @@ def odd_parity(qubits: int):
def sample_circuit_layers(
rng: random.Random, qubits: int, depth: int
rng: random.Random, qubits: int, depth: int, gate_types: list[GateType] | None = None
) -> QuantumCircuit:
if gate_types is None:
gate_types = [GateType.H, GateType.RX, GateType.RY, GateType.RZ, GateType.RXX, GateType.RYY, GateType.RZZ]
even = even_parity(qubits)
odd = odd_parity(qubits)
logits_pre = np.array([rng.normalvariate(0, 1.35) for _ in gate_types])
logits: list[float] = list(np.exp(logits_pre) / sum(np.exp(logits_pre)))
total_single = 0
total_double = 0
params = 0
gates: list[list[Gate]] = []
for _ in range(depth):
if total_single + total_double >= 36:
break
i = 0
while total_single + total_double < MAX_GATE_NUM:
gate_locations = even if rng.random() < 0.5 else odd
gate_type = rng.choices(gate_types, weights=list(logits), k=1)[0]
layer = []
for loc in gate_locations:
gate_type = rng.randint(1, 6)
if gate_type >= 4:
if loc[1] == qubits:
continue
layer.append(Gate(GateType(gate_type), loc, params))
total_double += 1
else:
if single_typ(gate_type):
layer.append(Gate(GateType(gate_type), loc[0], params))
total_single += 1
gates.append(layer)
params += 1
else:
if loc[1] == qubits:
loc[1] == 0
layer.append(Gate(GateType(gate_type), loc, params))
total_double += 1
params += param_count(gate_type)
gates.append(layer)
i += 1
return QuantumCircuit(qubits, gates, total_single, total_double, params)
@ -356,9 +354,7 @@ def circ_from_layers(layers: list[list[Gate]], qubits: int) -> QuantumCircuit:
return QuantumCircuit(qubits, gates, total_single, total_double, params)
def sample_circuit_random(
rng: random.Random, qubits: int, depth: int, gate_types: list[GateType]
) -> QuantumCircuit:
def sample_circuit_random(rng: random.Random, qubits: int, depth: int, gate_types: list[GateType]) -> QuantumCircuit:
params = 0
total_single = 0
total_double = 0
@ -391,24 +387,12 @@ def sample_circuit_random(
used_qubits.add(loc)
used_qubits.add(loc + 1)
case GateType.CX | GateType.CZ:
layer.append(
Gate(
gate_type,
(loc, loc + 1) if rng.random() < 0.5 else (loc + 1, loc),
params,
)
)
layer.append(Gate(gate_type, (loc, loc + 1) if rng.random() < 0.5 else (loc + 1, loc), params))
total_double += 1
used_qubits.add(loc)
used_qubits.add(loc + 1)
case GateType.CRX:
layer.append(
Gate(
gate_type,
(loc, loc + 1) if rng.random() < 0.5 else (loc + 1, loc),
params,
)
)
layer.append(Gate(gate_type, (loc, loc + 1) if rng.random() < 0.5 else (loc + 1, loc), params))
params += 1
total_double += 1
used_qubits.add(loc)
@ -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, gates: list[GateType]):
while True:
yield sample_circuit_random(rng, qubits, depth, gates)

0
src/tasks.py Normal file
View file