everydaytok commited on
Commit
2203336
Β·
verified Β·
1 Parent(s): 2918e15

Update QuantumCircuits.py

Browse files
Files changed (1) hide show
  1. 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) # ← NEW FIX
43
  include_2body: bool = True
44
  name: str = "QuantumCircuit"
45
  description: str = ""
46
 
47
  @property
48
- def n_layers(self) -> int: return max([g.layer for g in self.gates] + [-1]) + 1
 
 
49
  @property
50
- def has_cnot(self) -> bool: return any(g.type in ("CNOT", "CZ") for g in self.gates)
 
 
51
  @property
52
  def all_params(self) -> List[str]:
53
- seen = set(); params = []
 
54
  for g in self.gates:
55
  for p in g.params:
56
- if p not in seen: seen.add(p); params.append(p)
 
 
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: raise ValueError(f"Invalid 2-body observable on same qubit: {q1}")
67
- if q1 > q2: return f"{pauli2}{pauli1}{q2}{q1}_L{layer}"
 
 
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; obs[obs1("x", q, 0)] = 0.0; obs[obs1("y", q, 0)] = 0.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, oB, oC = obs1(pA, qA, layer), obs1(pB, qB, layer), obs1(pC, qC, layer)
 
 
 
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, ypr_in, zpr_in = obs2_safe("x", q, pr, r, layer_in), obs2_safe("y", q, pr, r, layer_in), obs2_safe("z", q, pr, r, layer_in)
103
- xpr_out, ypr_out, zpr_out = obs2_safe("x", q, pr, r, layer_out), obs2_safe("y", q, pr, r, layer_out), obs2_safe("z", q, pr, r, layer_out)
 
 
 
 
 
 
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, ypr_in, zpr_in = obs2_safe("x", q, pr, r, layer_in), obs2_safe("y", q, pr, r, layer_in), obs2_safe("z", q, pr, r, layer_in)
125
- xpr_out, ypr_out, zpr_out = obs2_safe("x", q, pr, r, layer_out), obs2_safe("y", q, pr, r, layer_out), obs2_safe("z", q, pr, r, layer_out)
 
 
 
 
 
 
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: raise ValueError("CNOT requires include_2body=True")
 
 
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: c.append({"kind": "EQ", "expr": f"{obs1(p, r, layer_out)} - {obs1(p, r, layer_in)}", "weight": 5.0})
 
 
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: g.validate()
174
- n = circuit.n_qubits; n_layers = circuit.n_layers; include_2b = circuit.include_2body and circuit.has_cnot
175
-
176
- variables = [{"name": pname, "lo": circuit.param_bounds.get(pname, (-math.pi, math.pi))[0], "hi": circuit.param_bounds.get(pname, (-math.pi, math.pi))[1]} for pname in circuit.all_params]
 
 
 
 
 
 
 
 
177
  for layer in range(n_layers + 1):
178
  for q in range(n):
179
- for p in PAULI: variables.append({"name": obs1(p, q, layer), "lo": -1.0, "hi": 1.0})
 
180
  if include_2b:
181
  for q1 in range(n):
182
  for q2 in range(q1 + 1, n):
183
- for p1p2 in PAULI2: variables.append({"name": obs2_safe(p1p2[0], q1, p1p2[1], q2, layer), "lo": -1.0, "hi": 1.0})
 
184
 
185
  obs_fixed = _initial_obs_values(circuit)
186
- global_constraints = [{"kind": "GEQ", "expr": f"1.0 - {obs1('x', q, layer)}**2 - {obs1('y', q, layer)}**2 - {obs1('z', q, layer)}**2", "weight": 2.0} for layer in range(n_layers + 1) for q in range(n)]
 
 
 
 
 
 
 
 
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 = []; gates_affecting = set()
 
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: scope_constraints.append({"kind": "EQ", "expr": f"{obs1(p, q, layer_out)} - {obs1(p, q, layer_in)}", "weight": 5.0})
 
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: 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})
 
212
 
213
  scope_var_names = []
214
  for q in range(n):
215
- for p in PAULI: scope_var_names.extend([obs1(p, q, layer_in), obs1(p, q, layer_out)])
 
216
  if include_2b:
217
  for q1 in range(n):
218
  for q2 in range(q1 + 1, n):
219
- for p1p2 in PAULI2: scope_var_names.extend([obs2_safe(p1p2[0], q1, p1p2[1], q2, layer_in), obs2_safe(p1p2[0], q1, p1p2[1], q2, layer_out)])
 
220
  scope_var_names.extend(circuit.all_params)
221
 
222
- scopes.append({"name": f"layer_{layer_in}_to_{layer_out}", "order": layer_in, "vars": scope_var_names, "constraints": scope_constraints})
 
 
 
 
 
223
 
224
  anchors = []
225
- # FIX: Create proper verification anchors from exact expectations, avoiding arbitrary optimization traps
226
  for obs, val in circuit.expected_observables.items():
227
- if len(obs) == 2: expr = obs1(obs[0], int(obs[1:]), n_layers)
228
- elif len(obs) == 4: expr = obs2_safe(obs[0], int(obs[2]), obs[1], int(obs[3]), n_layers)
229
- anchors.append(axl_invariant_class(name=f"verify_{obs}", expr=f"{expr} - ({val})", tolerance=0.05, mode="eq"))
 
 
 
 
 
 
 
 
230
 
231
  for q in range(n):
232
- anchors.append(axl_invariant_class(name=f"bloch_q{q}", expr=f"1.0 - {obs1('x', q, n_layers)}**2 - {obs1('y', q, n_layers)}**2 - {obs1('z', q, n_layers)}**2", tolerance=0.05, mode="geq"))
 
 
 
 
 
233
 
234
  return axl_problem_class(
235
- name=circuit.name, description=circuit.description,
 
236
  axioms={"CONTINUOUS", "CONSERVED", "TRANSITIVE", "BILINEAR", "METRIC", "SUPERPOSITION", "SYMMETRIC"},
237
- variables=variables, constraints=global_constraints, scopes=scopes, anchors=anchors, observations=obs_fixed)
238
-
 
 
 
 
239
 
240
  # ══════════════════════════════════════════════════════════════════════
241
- # EXAMPLE CIRCUITS (Updated for correct anchor verification)
242
  # ══════════════════════════════════════════════════════════════════════
243
 
244
  def example_bell_state() -> QuantumCircuit:
245
  return QuantumCircuit(
246
- n_qubits=2, name="BellStatePrep",
247
- gates=[QGate("H", [0], layer=0), QGate("CNOT", [0, 1], layer=1)],
248
- initial_state="zero", include_2body=True,
249
- # Now verifies exactly what the Bell State should be, preventing infinite loops.
 
 
 
 
250
  expected_observables={"zz01": 1.0, "xx01": 1.0}
251
  )
252
 
253
  def example_vqe_h2() -> QuantumCircuit:
254
  return QuantumCircuit(
255
- n_qubits=2, name="VQE_H2",
 
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, name="QAOA_MaxCut",
 
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),