Spaces:
Sleeping
Sleeping
Update QuantumCircuits.py
Browse files- QuantumCircuits.py +135 -47
QuantumCircuits.py
CHANGED
|
@@ -5,9 +5,6 @@ Heisenberg-picture quantum circuit encoding for the Practicality Wisdom Layer.
|
|
| 5 |
UPDATE 24.1: Non-Linear Cumulant Closure (BBGKY Hierarchy Fix)
|
| 6 |
CNOT operations natively generate 3-body entanglement. We now approximate
|
| 7 |
them using non-linear combinations of 1-body and 2-body observables.
|
| 8 |
-
|
| 9 |
-
UPDATE: Replaced arbitrary target_hamiltonian anchors with exact
|
| 10 |
-
`expected_observables` to prevent Gate 2 verification traps.
|
| 11 |
"""
|
| 12 |
|
| 13 |
from __future__ import annotations
|
|
@@ -39,21 +36,28 @@ class QuantumCircuit:
|
|
| 39 |
param_bounds: Dict[str, Tuple[float, float]] = field(default_factory=dict)
|
| 40 |
initial_state: str = "zero"
|
| 41 |
initial_obs: Dict[str, float] = field(default_factory=dict)
|
| 42 |
-
expected_observables: Dict[str, float] = field(default_factory=dict)
|
| 43 |
include_2body: bool = True
|
| 44 |
name: str = "QuantumCircuit"
|
| 45 |
description: str = ""
|
| 46 |
|
| 47 |
@property
|
| 48 |
-
def n_layers(self) -> int:
|
|
|
|
|
|
|
| 49 |
@property
|
| 50 |
-
def has_cnot(self) -> bool:
|
|
|
|
|
|
|
| 51 |
@property
|
| 52 |
def all_params(self) -> List[str]:
|
| 53 |
-
seen = set()
|
|
|
|
| 54 |
for g in self.gates:
|
| 55 |
for p in g.params:
|
| 56 |
-
if p not in seen:
|
|
|
|
|
|
|
| 57 |
return params
|
| 58 |
|
| 59 |
PAULI = ("x", "y", "z")
|
|
@@ -63,15 +67,19 @@ def obs1(pauli: str, qubit: int, layer: int) -> str:
|
|
| 63 |
return f"{pauli}{qubit}_L{layer}"
|
| 64 |
|
| 65 |
def obs2_safe(pauli1: str, q1: int, pauli2: str, q2: int, layer: int) -> str:
|
| 66 |
-
if q1 == q2:
|
| 67 |
-
|
|
|
|
|
|
|
| 68 |
return f"{pauli1}{pauli2}{q1}{q2}_L{layer}"
|
| 69 |
|
| 70 |
def _initial_obs_values(circuit: QuantumCircuit) -> Dict[str, float]:
|
| 71 |
obs = {}
|
| 72 |
if circuit.initial_state == "zero":
|
| 73 |
for q in range(circuit.n_qubits):
|
| 74 |
-
obs[obs1("z", q, 0)] = 1.0
|
|
|
|
|
|
|
| 75 |
if circuit.include_2body:
|
| 76 |
for q1 in range(circuit.n_qubits):
|
| 77 |
for q2 in range(q1 + 1, circuit.n_qubits):
|
|
@@ -80,27 +88,39 @@ def _initial_obs_values(circuit: QuantumCircuit) -> Dict[str, float]:
|
|
| 80 |
return obs
|
| 81 |
|
| 82 |
def _cumulant_3(pA: str, qA: int, pB: str, qB: int, pC: str, qC: int, layer: int) -> str:
|
| 83 |
-
oA
|
|
|
|
|
|
|
|
|
|
| 84 |
oAB = obs2_safe(pA, qA, pB, qB, layer)
|
| 85 |
oAC = obs2_safe(pA, qA, pC, qC, layer)
|
| 86 |
oBC = obs2_safe(pB, qB, pC, qC, layer)
|
|
|
|
| 87 |
return f"({oAB}*{oC} + {oAC}*{oB} + {oBC}*{oA} - 2.0*{oA}*{oB}*{oC})"
|
| 88 |
|
| 89 |
def _rz_constraints(q: int, theta: str, layer_in: int, layer_out: int, circuit: QuantumCircuit) -> List[Dict]:
|
| 90 |
c = []
|
| 91 |
xq_in, yq_in, zq_in = obs1("x", q, layer_in), obs1("y", q, layer_in), obs1("z", q, layer_in)
|
| 92 |
xq_out, yq_out, zq_out = obs1("x", q, layer_out), obs1("y", q, layer_out), obs1("z", q, layer_out)
|
|
|
|
| 93 |
c.extend([
|
| 94 |
{"kind": "EQ", "expr": f"{xq_out} - cos({theta})*{xq_in} - sin({theta})*{yq_in}", "weight": 5.0},
|
| 95 |
{"kind": "EQ", "expr": f"{yq_out} + sin({theta})*{xq_in} - cos({theta})*{yq_in}", "weight": 5.0},
|
| 96 |
{"kind": "EQ", "expr": f"{zq_out} - {zq_in}", "weight": 5.0}
|
| 97 |
])
|
|
|
|
| 98 |
if circuit.include_2body:
|
| 99 |
for r in range(circuit.n_qubits):
|
| 100 |
if r == q: continue
|
| 101 |
for pr in PAULI:
|
| 102 |
-
xpr_in
|
| 103 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 104 |
c.extend([
|
| 105 |
{"kind": "EQ", "expr": f"{xpr_out} - cos({theta})*{xpr_in} - sin({theta})*{ypr_in}", "weight": 5.0},
|
| 106 |
{"kind": "EQ", "expr": f"{ypr_out} + sin({theta})*{xpr_in} - cos({theta})*{ypr_in}", "weight": 5.0},
|
|
@@ -112,17 +132,25 @@ def _hadamard_constraints(q: int, layer_in: int, layer_out: int, circuit: Quantu
|
|
| 112 |
c = []
|
| 113 |
xq_in, yq_in, zq_in = obs1("x", q, layer_in), obs1("y", q, layer_in), obs1("z", q, layer_in)
|
| 114 |
xq_out, yq_out, zq_out = obs1("x", q, layer_out), obs1("y", q, layer_out), obs1("z", q, layer_out)
|
|
|
|
| 115 |
c.extend([
|
| 116 |
{"kind": "EQ", "expr": f"{xq_out} - {zq_in}", "weight": 5.0},
|
| 117 |
{"kind": "EQ", "expr": f"{yq_out} + {yq_in}", "weight": 5.0},
|
| 118 |
{"kind": "EQ", "expr": f"{zq_out} - {xq_in}", "weight": 5.0}
|
| 119 |
])
|
|
|
|
| 120 |
if circuit.include_2body:
|
| 121 |
for r in range(circuit.n_qubits):
|
| 122 |
if r == q: continue
|
| 123 |
for pr in PAULI:
|
| 124 |
-
xpr_in
|
| 125 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 126 |
c.extend([
|
| 127 |
{"kind": "EQ", "expr": f"{xpr_out} - {zpr_in}", "weight": 5.0},
|
| 128 |
{"kind": "EQ", "expr": f"{ypr_out} + {ypr_in}", "weight": 5.0},
|
|
@@ -131,9 +159,13 @@ def _hadamard_constraints(q: int, layer_in: int, layer_out: int, circuit: Quantu
|
|
| 131 |
return c
|
| 132 |
|
| 133 |
def _cnot_constraints(control: int, target: int, layer_in: int, layer_out: int, circuit: QuantumCircuit) -> List[Dict]:
|
| 134 |
-
if not circuit.include_2body:
|
|
|
|
|
|
|
| 135 |
k, j = control, target
|
| 136 |
c = []
|
|
|
|
|
|
|
| 137 |
c.extend([
|
| 138 |
{"kind": "EQ", "expr": f"{obs1('x', k, layer_out)} - {obs2_safe('x', k, 'x', j, layer_in)}", "weight": 5.0},
|
| 139 |
{"kind": "EQ", "expr": f"{obs1('y', k, layer_out)} - {obs2_safe('y', k, 'x', j, layer_in)}", "weight": 5.0},
|
|
@@ -152,9 +184,13 @@ def _cnot_constraints(control: int, target: int, layer_in: int, layer_out: int,
|
|
| 152 |
{"kind": "EQ", "expr": f"{obs2_safe('z', k, 'y', j, layer_out)} - {obs1('y', j, layer_in)}", "weight": 5.0},
|
| 153 |
{"kind": "EQ", "expr": f"{obs2_safe('z', k, 'z', j, layer_out)} - {obs1('z', j, layer_in)}", "weight": 5.0},
|
| 154 |
])
|
|
|
|
|
|
|
| 155 |
for r in range(circuit.n_qubits):
|
| 156 |
if r in (k, j): continue
|
| 157 |
-
for p in PAULI:
|
|
|
|
|
|
|
| 158 |
if circuit.include_2body:
|
| 159 |
for pr in PAULI:
|
| 160 |
c.append({"kind": "EQ", "expr": f"{obs2_safe('x', k, pr, r, layer_out)} - {_cumulant_3('x', k, 'x', j, pr, r, layer_in)}", "weight": 4.0})
|
|
@@ -163,6 +199,7 @@ def _cnot_constraints(control: int, target: int, layer_in: int, layer_out: int,
|
|
| 163 |
c.append({"kind": "EQ", "expr": f"{obs2_safe('x', j, pr, r, layer_out)} - {obs2_safe('x', j, pr, r, layer_in)}", "weight": 5.0})
|
| 164 |
c.append({"kind": "EQ", "expr": f"{obs2_safe('y', j, pr, r, layer_out)} - {_cumulant_3('z', k, 'y', j, pr, r, layer_in)}", "weight": 4.0})
|
| 165 |
c.append({"kind": "EQ", "expr": f"{obs2_safe('z', j, pr, r, layer_out)} - {_cumulant_3('z', k, 'z', j, pr, r, layer_in)}", "weight": 4.0})
|
|
|
|
| 166 |
for q2 in range(r + 1, circuit.n_qubits):
|
| 167 |
if q2 in (k, j): continue
|
| 168 |
for p1p2 in PAULI2:
|
|
@@ -170,26 +207,45 @@ def _cnot_constraints(control: int, target: int, layer_in: int, layer_out: int,
|
|
| 170 |
return c
|
| 171 |
|
| 172 |
def build_quantum_axl(circuit: QuantumCircuit, axl_problem_class: Any, axl_invariant_class: Any) -> Any:
|
| 173 |
-
for g in circuit.gates:
|
| 174 |
-
|
| 175 |
-
|
| 176 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 177 |
for layer in range(n_layers + 1):
|
| 178 |
for q in range(n):
|
| 179 |
-
for p in PAULI:
|
|
|
|
| 180 |
if include_2b:
|
| 181 |
for q1 in range(n):
|
| 182 |
for q2 in range(q1 + 1, n):
|
| 183 |
-
for p1p2 in PAULI2:
|
|
|
|
| 184 |
|
| 185 |
obs_fixed = _initial_obs_values(circuit)
|
| 186 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 187 |
|
| 188 |
scopes = []
|
| 189 |
for layer_in in range(n_layers):
|
| 190 |
layer_out = layer_in + 1
|
| 191 |
layer_gates = [g for g in circuit.gates if g.layer == layer_in]
|
| 192 |
-
scope_constraints = []
|
|
|
|
| 193 |
|
| 194 |
for gate in layer_gates:
|
| 195 |
if gate.type == "Rz":
|
|
@@ -204,68 +260,100 @@ def build_quantum_axl(circuit: QuantumCircuit, axl_problem_class: Any, axl_invar
|
|
| 204 |
|
| 205 |
unaffected = [q for q in range(n) if q not in gates_affecting]
|
| 206 |
for q in unaffected:
|
| 207 |
-
for p in PAULI:
|
|
|
|
| 208 |
if include_2b:
|
| 209 |
for q2 in range(n):
|
| 210 |
if q2 == q or q2 in gates_affecting: continue
|
| 211 |
-
for p1p2 in PAULI2:
|
|
|
|
| 212 |
|
| 213 |
scope_var_names = []
|
| 214 |
for q in range(n):
|
| 215 |
-
for p in PAULI:
|
|
|
|
| 216 |
if include_2b:
|
| 217 |
for q1 in range(n):
|
| 218 |
for q2 in range(q1 + 1, n):
|
| 219 |
-
for p1p2 in PAULI2:
|
|
|
|
| 220 |
scope_var_names.extend(circuit.all_params)
|
| 221 |
|
| 222 |
-
scopes.append({
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 223 |
|
| 224 |
anchors = []
|
| 225 |
-
# FIX:
|
| 226 |
for obs, val in circuit.expected_observables.items():
|
| 227 |
-
if len(obs) == 2:
|
| 228 |
-
|
| 229 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 230 |
|
| 231 |
for q in range(n):
|
| 232 |
-
anchors.append(axl_invariant_class(
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 233 |
|
| 234 |
return axl_problem_class(
|
| 235 |
-
name=circuit.name,
|
|
|
|
| 236 |
axioms={"CONTINUOUS", "CONSERVED", "TRANSITIVE", "BILINEAR", "METRIC", "SUPERPOSITION", "SYMMETRIC"},
|
| 237 |
-
variables=variables,
|
| 238 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
| 239 |
|
| 240 |
# ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
|
| 241 |
-
# EXAMPLE CIRCUITS
|
| 242 |
# ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
|
| 243 |
|
| 244 |
def example_bell_state() -> QuantumCircuit:
|
| 245 |
return QuantumCircuit(
|
| 246 |
-
n_qubits=2,
|
| 247 |
-
|
| 248 |
-
|
| 249 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
| 250 |
expected_observables={"zz01": 1.0, "xx01": 1.0}
|
| 251 |
)
|
| 252 |
|
| 253 |
def example_vqe_h2() -> QuantumCircuit:
|
| 254 |
return QuantumCircuit(
|
| 255 |
-
n_qubits=2,
|
|
|
|
| 256 |
gates=[
|
| 257 |
QGate("Ry", [0], ["theta0"], layer=0),
|
| 258 |
QGate("Ry", [1], ["theta1"], layer=0),
|
| 259 |
QGate("CNOT", [0, 1], layer=1),
|
| 260 |
],
|
| 261 |
param_bounds={"theta0": (-math.pi, math.pi), "theta1": (-math.pi, math.pi)},
|
| 262 |
-
# Let it purely optimize without Gate 2 anchor traps
|
| 263 |
expected_observables={}
|
| 264 |
)
|
| 265 |
|
| 266 |
def example_qaoa_maxcut_p1() -> QuantumCircuit:
|
| 267 |
return QuantumCircuit(
|
| 268 |
-
n_qubits=3,
|
|
|
|
| 269 |
gates=[
|
| 270 |
QGate("H", [0], layer=0), QGate("H", [1], layer=0), QGate("H", [2], layer=0),
|
| 271 |
QGate("Rz", [0], ["gamma"], layer=1), QGate("Rz", [1], ["gamma"], layer=1), QGate("Rz", [2], ["gamma"], layer=1),
|
|
|
|
| 5 |
UPDATE 24.1: Non-Linear Cumulant Closure (BBGKY Hierarchy Fix)
|
| 6 |
CNOT operations natively generate 3-body entanglement. We now approximate
|
| 7 |
them using non-linear combinations of 1-body and 2-body observables.
|
|
|
|
|
|
|
|
|
|
| 8 |
"""
|
| 9 |
|
| 10 |
from __future__ import annotations
|
|
|
|
| 36 |
param_bounds: Dict[str, Tuple[float, float]] = field(default_factory=dict)
|
| 37 |
initial_state: str = "zero"
|
| 38 |
initial_obs: Dict[str, float] = field(default_factory=dict)
|
| 39 |
+
expected_observables: Dict[str, float] = field(default_factory=dict)
|
| 40 |
include_2body: bool = True
|
| 41 |
name: str = "QuantumCircuit"
|
| 42 |
description: str = ""
|
| 43 |
|
| 44 |
@property
|
| 45 |
+
def n_layers(self) -> int:
|
| 46 |
+
return max([g.layer for g in self.gates] + [-1]) + 1
|
| 47 |
+
|
| 48 |
@property
|
| 49 |
+
def has_cnot(self) -> bool:
|
| 50 |
+
return any(g.type in ("CNOT", "CZ") for g in self.gates)
|
| 51 |
+
|
| 52 |
@property
|
| 53 |
def all_params(self) -> List[str]:
|
| 54 |
+
seen = set()
|
| 55 |
+
params = []
|
| 56 |
for g in self.gates:
|
| 57 |
for p in g.params:
|
| 58 |
+
if p not in seen:
|
| 59 |
+
seen.add(p)
|
| 60 |
+
params.append(p)
|
| 61 |
return params
|
| 62 |
|
| 63 |
PAULI = ("x", "y", "z")
|
|
|
|
| 67 |
return f"{pauli}{qubit}_L{layer}"
|
| 68 |
|
| 69 |
def obs2_safe(pauli1: str, q1: int, pauli2: str, q2: int, layer: int) -> str:
|
| 70 |
+
if q1 == q2:
|
| 71 |
+
raise ValueError(f"Invalid 2-body observable on same qubit: {q1}")
|
| 72 |
+
if q1 > q2:
|
| 73 |
+
return f"{pauli2}{pauli1}{q2}{q1}_L{layer}"
|
| 74 |
return f"{pauli1}{pauli2}{q1}{q2}_L{layer}"
|
| 75 |
|
| 76 |
def _initial_obs_values(circuit: QuantumCircuit) -> Dict[str, float]:
|
| 77 |
obs = {}
|
| 78 |
if circuit.initial_state == "zero":
|
| 79 |
for q in range(circuit.n_qubits):
|
| 80 |
+
obs[obs1("z", q, 0)] = 1.0
|
| 81 |
+
obs[obs1("x", q, 0)] = 0.0
|
| 82 |
+
obs[obs1("y", q, 0)] = 0.0
|
| 83 |
if circuit.include_2body:
|
| 84 |
for q1 in range(circuit.n_qubits):
|
| 85 |
for q2 in range(q1 + 1, circuit.n_qubits):
|
|
|
|
| 88 |
return obs
|
| 89 |
|
| 90 |
def _cumulant_3(pA: str, qA: int, pB: str, qB: int, pC: str, qC: int, layer: int) -> str:
|
| 91 |
+
oA = obs1(pA, qA, layer)
|
| 92 |
+
oB = obs1(pB, qB, layer)
|
| 93 |
+
oC = obs1(pC, qC, layer)
|
| 94 |
+
|
| 95 |
oAB = obs2_safe(pA, qA, pB, qB, layer)
|
| 96 |
oAC = obs2_safe(pA, qA, pC, qC, layer)
|
| 97 |
oBC = obs2_safe(pB, qB, pC, qC, layer)
|
| 98 |
+
|
| 99 |
return f"({oAB}*{oC} + {oAC}*{oB} + {oBC}*{oA} - 2.0*{oA}*{oB}*{oC})"
|
| 100 |
|
| 101 |
def _rz_constraints(q: int, theta: str, layer_in: int, layer_out: int, circuit: QuantumCircuit) -> List[Dict]:
|
| 102 |
c = []
|
| 103 |
xq_in, yq_in, zq_in = obs1("x", q, layer_in), obs1("y", q, layer_in), obs1("z", q, layer_in)
|
| 104 |
xq_out, yq_out, zq_out = obs1("x", q, layer_out), obs1("y", q, layer_out), obs1("z", q, layer_out)
|
| 105 |
+
|
| 106 |
c.extend([
|
| 107 |
{"kind": "EQ", "expr": f"{xq_out} - cos({theta})*{xq_in} - sin({theta})*{yq_in}", "weight": 5.0},
|
| 108 |
{"kind": "EQ", "expr": f"{yq_out} + sin({theta})*{xq_in} - cos({theta})*{yq_in}", "weight": 5.0},
|
| 109 |
{"kind": "EQ", "expr": f"{zq_out} - {zq_in}", "weight": 5.0}
|
| 110 |
])
|
| 111 |
+
|
| 112 |
if circuit.include_2body:
|
| 113 |
for r in range(circuit.n_qubits):
|
| 114 |
if r == q: continue
|
| 115 |
for pr in PAULI:
|
| 116 |
+
xpr_in = obs2_safe("x", q, pr, r, layer_in)
|
| 117 |
+
ypr_in = obs2_safe("y", q, pr, r, layer_in)
|
| 118 |
+
zpr_in = obs2_safe("z", q, pr, r, layer_in)
|
| 119 |
+
|
| 120 |
+
xpr_out = obs2_safe("x", q, pr, r, layer_out)
|
| 121 |
+
ypr_out = obs2_safe("y", q, pr, r, layer_out)
|
| 122 |
+
zpr_out = obs2_safe("z", q, pr, r, layer_out)
|
| 123 |
+
|
| 124 |
c.extend([
|
| 125 |
{"kind": "EQ", "expr": f"{xpr_out} - cos({theta})*{xpr_in} - sin({theta})*{ypr_in}", "weight": 5.0},
|
| 126 |
{"kind": "EQ", "expr": f"{ypr_out} + sin({theta})*{xpr_in} - cos({theta})*{ypr_in}", "weight": 5.0},
|
|
|
|
| 132 |
c = []
|
| 133 |
xq_in, yq_in, zq_in = obs1("x", q, layer_in), obs1("y", q, layer_in), obs1("z", q, layer_in)
|
| 134 |
xq_out, yq_out, zq_out = obs1("x", q, layer_out), obs1("y", q, layer_out), obs1("z", q, layer_out)
|
| 135 |
+
|
| 136 |
c.extend([
|
| 137 |
{"kind": "EQ", "expr": f"{xq_out} - {zq_in}", "weight": 5.0},
|
| 138 |
{"kind": "EQ", "expr": f"{yq_out} + {yq_in}", "weight": 5.0},
|
| 139 |
{"kind": "EQ", "expr": f"{zq_out} - {xq_in}", "weight": 5.0}
|
| 140 |
])
|
| 141 |
+
|
| 142 |
if circuit.include_2body:
|
| 143 |
for r in range(circuit.n_qubits):
|
| 144 |
if r == q: continue
|
| 145 |
for pr in PAULI:
|
| 146 |
+
xpr_in = obs2_safe("x", q, pr, r, layer_in)
|
| 147 |
+
ypr_in = obs2_safe("y", q, pr, r, layer_in)
|
| 148 |
+
zpr_in = obs2_safe("z", q, pr, r, layer_in)
|
| 149 |
+
|
| 150 |
+
xpr_out = obs2_safe("x", q, pr, r, layer_out)
|
| 151 |
+
ypr_out = obs2_safe("y", q, pr, r, layer_out)
|
| 152 |
+
zpr_out = obs2_safe("z", q, pr, r, layer_out)
|
| 153 |
+
|
| 154 |
c.extend([
|
| 155 |
{"kind": "EQ", "expr": f"{xpr_out} - {zpr_in}", "weight": 5.0},
|
| 156 |
{"kind": "EQ", "expr": f"{ypr_out} + {ypr_in}", "weight": 5.0},
|
|
|
|
| 159 |
return c
|
| 160 |
|
| 161 |
def _cnot_constraints(control: int, target: int, layer_in: int, layer_out: int, circuit: QuantumCircuit) -> List[Dict]:
|
| 162 |
+
if not circuit.include_2body:
|
| 163 |
+
raise ValueError("CNOT requires include_2body=True")
|
| 164 |
+
|
| 165 |
k, j = control, target
|
| 166 |
c = []
|
| 167 |
+
|
| 168 |
+
# Inner control-target mapping
|
| 169 |
c.extend([
|
| 170 |
{"kind": "EQ", "expr": f"{obs1('x', k, layer_out)} - {obs2_safe('x', k, 'x', j, layer_in)}", "weight": 5.0},
|
| 171 |
{"kind": "EQ", "expr": f"{obs1('y', k, layer_out)} - {obs2_safe('y', k, 'x', j, layer_in)}", "weight": 5.0},
|
|
|
|
| 184 |
{"kind": "EQ", "expr": f"{obs2_safe('z', k, 'y', j, layer_out)} - {obs1('y', j, layer_in)}", "weight": 5.0},
|
| 185 |
{"kind": "EQ", "expr": f"{obs2_safe('z', k, 'z', j, layer_out)} - {obs1('z', j, layer_in)}", "weight": 5.0},
|
| 186 |
])
|
| 187 |
+
|
| 188 |
+
# Spectator BBGKY Cumulant mapping
|
| 189 |
for r in range(circuit.n_qubits):
|
| 190 |
if r in (k, j): continue
|
| 191 |
+
for p in PAULI:
|
| 192 |
+
c.append({"kind": "EQ", "expr": f"{obs1(p, r, layer_out)} - {obs1(p, r, layer_in)}", "weight": 5.0})
|
| 193 |
+
|
| 194 |
if circuit.include_2body:
|
| 195 |
for pr in PAULI:
|
| 196 |
c.append({"kind": "EQ", "expr": f"{obs2_safe('x', k, pr, r, layer_out)} - {_cumulant_3('x', k, 'x', j, pr, r, layer_in)}", "weight": 4.0})
|
|
|
|
| 199 |
c.append({"kind": "EQ", "expr": f"{obs2_safe('x', j, pr, r, layer_out)} - {obs2_safe('x', j, pr, r, layer_in)}", "weight": 5.0})
|
| 200 |
c.append({"kind": "EQ", "expr": f"{obs2_safe('y', j, pr, r, layer_out)} - {_cumulant_3('z', k, 'y', j, pr, r, layer_in)}", "weight": 4.0})
|
| 201 |
c.append({"kind": "EQ", "expr": f"{obs2_safe('z', j, pr, r, layer_out)} - {_cumulant_3('z', k, 'z', j, pr, r, layer_in)}", "weight": 4.0})
|
| 202 |
+
|
| 203 |
for q2 in range(r + 1, circuit.n_qubits):
|
| 204 |
if q2 in (k, j): continue
|
| 205 |
for p1p2 in PAULI2:
|
|
|
|
| 207 |
return c
|
| 208 |
|
| 209 |
def build_quantum_axl(circuit: QuantumCircuit, axl_problem_class: Any, axl_invariant_class: Any) -> Any:
|
| 210 |
+
for g in circuit.gates:
|
| 211 |
+
g.validate()
|
| 212 |
+
|
| 213 |
+
n = circuit.n_qubits
|
| 214 |
+
n_layers = circuit.n_layers
|
| 215 |
+
include_2b = circuit.include_2body and circuit.has_cnot
|
| 216 |
+
|
| 217 |
+
variables = []
|
| 218 |
+
for pname in circuit.all_params:
|
| 219 |
+
lo, hi = circuit.param_bounds.get(pname, (-math.pi, math.pi))
|
| 220 |
+
variables.append({"name": pname, "lo": lo, "hi": hi})
|
| 221 |
+
|
| 222 |
for layer in range(n_layers + 1):
|
| 223 |
for q in range(n):
|
| 224 |
+
for p in PAULI:
|
| 225 |
+
variables.append({"name": obs1(p, q, layer), "lo": -1.0, "hi": 1.0})
|
| 226 |
if include_2b:
|
| 227 |
for q1 in range(n):
|
| 228 |
for q2 in range(q1 + 1, n):
|
| 229 |
+
for p1p2 in PAULI2:
|
| 230 |
+
variables.append({"name": obs2_safe(p1p2[0], q1, p1p2[1], q2, layer), "lo": -1.0, "hi": 1.0})
|
| 231 |
|
| 232 |
obs_fixed = _initial_obs_values(circuit)
|
| 233 |
+
|
| 234 |
+
global_constraints = []
|
| 235 |
+
for layer in range(n_layers + 1):
|
| 236 |
+
for q in range(n):
|
| 237 |
+
global_constraints.append({
|
| 238 |
+
"kind": "GEQ",
|
| 239 |
+
"expr": f"1.0 - {obs1('x', q, layer)}**2 - {obs1('y', q, layer)}**2 - {obs1('z', q, layer)}**2",
|
| 240 |
+
"weight": 2.0
|
| 241 |
+
})
|
| 242 |
|
| 243 |
scopes = []
|
| 244 |
for layer_in in range(n_layers):
|
| 245 |
layer_out = layer_in + 1
|
| 246 |
layer_gates = [g for g in circuit.gates if g.layer == layer_in]
|
| 247 |
+
scope_constraints = []
|
| 248 |
+
gates_affecting = set()
|
| 249 |
|
| 250 |
for gate in layer_gates:
|
| 251 |
if gate.type == "Rz":
|
|
|
|
| 260 |
|
| 261 |
unaffected = [q for q in range(n) if q not in gates_affecting]
|
| 262 |
for q in unaffected:
|
| 263 |
+
for p in PAULI:
|
| 264 |
+
scope_constraints.append({"kind": "EQ", "expr": f"{obs1(p, q, layer_out)} - {obs1(p, q, layer_in)}", "weight": 5.0})
|
| 265 |
if include_2b:
|
| 266 |
for q2 in range(n):
|
| 267 |
if q2 == q or q2 in gates_affecting: continue
|
| 268 |
+
for p1p2 in PAULI2:
|
| 269 |
+
scope_constraints.append({"kind": "EQ", "expr": f"{obs2_safe(p1p2[0], q, p1p2[1], q2, layer_out)} - {obs2_safe(p1p2[0], q, p1p2[1], q2, layer_in)}", "weight": 5.0})
|
| 270 |
|
| 271 |
scope_var_names = []
|
| 272 |
for q in range(n):
|
| 273 |
+
for p in PAULI:
|
| 274 |
+
scope_var_names.extend([obs1(p, q, layer_in), obs1(p, q, layer_out)])
|
| 275 |
if include_2b:
|
| 276 |
for q1 in range(n):
|
| 277 |
for q2 in range(q1 + 1, n):
|
| 278 |
+
for p1p2 in PAULI2:
|
| 279 |
+
scope_var_names.extend([obs2_safe(p1p2[0], q1, p1p2[1], q2, layer_in), obs2_safe(p1p2[0], q1, p1p2[1], q2, layer_out)])
|
| 280 |
scope_var_names.extend(circuit.all_params)
|
| 281 |
|
| 282 |
+
scopes.append({
|
| 283 |
+
"name": f"layer_{layer_in}_to_{layer_out}",
|
| 284 |
+
"order": layer_in,
|
| 285 |
+
"vars": scope_var_names,
|
| 286 |
+
"constraints": scope_constraints
|
| 287 |
+
})
|
| 288 |
|
| 289 |
anchors = []
|
| 290 |
+
# FIX: Exact Correlator Verification
|
| 291 |
for obs, val in circuit.expected_observables.items():
|
| 292 |
+
if len(obs) == 2:
|
| 293 |
+
expr = obs1(obs[0], int(obs[1:]), n_layers)
|
| 294 |
+
elif len(obs) == 4:
|
| 295 |
+
expr = obs2_safe(obs[0], int(obs[2]), obs[1], int(obs[3]), n_layers)
|
| 296 |
+
|
| 297 |
+
anchors.append(axl_invariant_class(
|
| 298 |
+
name=f"verify_{obs}",
|
| 299 |
+
expr=f"{expr} - ({val})",
|
| 300 |
+
tolerance=0.05,
|
| 301 |
+
mode="eq"
|
| 302 |
+
))
|
| 303 |
|
| 304 |
for q in range(n):
|
| 305 |
+
anchors.append(axl_invariant_class(
|
| 306 |
+
name=f"bloch_q{q}",
|
| 307 |
+
expr=f"1.0 - {obs1('x', q, n_layers)}**2 - {obs1('y', q, n_layers)}**2 - {obs1('z', q, n_layers)}**2",
|
| 308 |
+
tolerance=0.05,
|
| 309 |
+
mode="geq"
|
| 310 |
+
))
|
| 311 |
|
| 312 |
return axl_problem_class(
|
| 313 |
+
name=circuit.name,
|
| 314 |
+
description=circuit.description,
|
| 315 |
axioms={"CONTINUOUS", "CONSERVED", "TRANSITIVE", "BILINEAR", "METRIC", "SUPERPOSITION", "SYMMETRIC"},
|
| 316 |
+
variables=variables,
|
| 317 |
+
constraints=global_constraints,
|
| 318 |
+
scopes=scopes,
|
| 319 |
+
anchors=anchors,
|
| 320 |
+
observations=obs_fixed
|
| 321 |
+
)
|
| 322 |
|
| 323 |
# ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
|
| 324 |
+
# EXAMPLE CIRCUITS
|
| 325 |
# ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
|
| 326 |
|
| 327 |
def example_bell_state() -> QuantumCircuit:
|
| 328 |
return QuantumCircuit(
|
| 329 |
+
n_qubits=2,
|
| 330 |
+
name="BellStatePrep",
|
| 331 |
+
gates=[
|
| 332 |
+
QGate("H", [0], layer=0),
|
| 333 |
+
QGate("CNOT", [0, 1], layer=1)
|
| 334 |
+
],
|
| 335 |
+
initial_state="zero",
|
| 336 |
+
include_2body=True,
|
| 337 |
expected_observables={"zz01": 1.0, "xx01": 1.0}
|
| 338 |
)
|
| 339 |
|
| 340 |
def example_vqe_h2() -> QuantumCircuit:
|
| 341 |
return QuantumCircuit(
|
| 342 |
+
n_qubits=2,
|
| 343 |
+
name="VQE_H2",
|
| 344 |
gates=[
|
| 345 |
QGate("Ry", [0], ["theta0"], layer=0),
|
| 346 |
QGate("Ry", [1], ["theta1"], layer=0),
|
| 347 |
QGate("CNOT", [0, 1], layer=1),
|
| 348 |
],
|
| 349 |
param_bounds={"theta0": (-math.pi, math.pi), "theta1": (-math.pi, math.pi)},
|
|
|
|
| 350 |
expected_observables={}
|
| 351 |
)
|
| 352 |
|
| 353 |
def example_qaoa_maxcut_p1() -> QuantumCircuit:
|
| 354 |
return QuantumCircuit(
|
| 355 |
+
n_qubits=3,
|
| 356 |
+
name="QAOA_MaxCut",
|
| 357 |
gates=[
|
| 358 |
QGate("H", [0], layer=0), QGate("H", [1], layer=0), QGate("H", [2], layer=0),
|
| 359 |
QGate("Rz", [0], ["gamma"], layer=1), QGate("Rz", [1], ["gamma"], layer=1), QGate("Rz", [2], ["gamma"], layer=1),
|