AEUPH commited on
Commit
2ea2ba9
·
verified ·
1 Parent(s): 1024066

Master consolidation: clear slot for Quantum Suite

Browse files
Files changed (2) hide show
  1. app.py +0 -436
  2. requirements.txt +0 -2
app.py DELETED
@@ -1,436 +0,0 @@
1
- """Quantum Suite — a real statevector quantum simulator in ONE cpu-basic Space.
2
-
3
- Consolidates the 11-member `QUANTUM_*` family, none of which can run on a free
4
- CPU Space: every one of them imports `qiskit`, `torch` and `transformers` and pulls
5
- real models (GPT-2, facebook/bart-large-mnli, the Olivetti faces set) at import time.
6
-
7
- This suite replaces them with the physics that actually matters and nothing else:
8
- a hand-written complex-amplitude simulator (pure Python + numpy), so the whole
9
- family fits in one slot.
10
-
11
- Tabs
12
- ----
13
- Circuit Simulator a text circuit language, amplitudes and probabilities out
14
- Superposition Explorer the uniform superposition and its 1/sqrt(N) statistics
15
- Grover Search real amplitude amplification, iteration by iteration
16
- Entanglement Lab Bell/GHZ states, entropy, and a CHSH bound
17
- Family Index what this suite consolidates, and why
18
-
19
- Statevector convention: qubit `q` is bit `n-1-q` of the basis index, so qubit 0 is
20
- the most significant bit and two-qubit operators are the usual `kron(A, B)` form.
21
- """
22
- import math
23
-
24
- import numpy as np
25
- import gradio as gr
26
-
27
- # ---------------------------------------------------------------- gates
28
- H = np.array([[1, 1], [1, -1]], dtype=complex) / math.sqrt(2)
29
- X = np.array([[0, 1], [1, 0]], dtype=complex)
30
- Y = np.array([[0, -1j], [1j, 0]], dtype=complex)
31
- Z = np.array([[1, 0], [0, -1]], dtype=complex)
32
- S = np.array([[1, 0], [0, 1j]], dtype=complex)
33
- T = np.array([[1, 0], [0, np.exp(1j * math.pi / 4)]], dtype=complex)
34
- ONE_QUBIT = {"H": H, "X": X, "Y": Y, "Z": Z, "S": S, "T": T}
35
- ROTATIONS = ("RX", "RY", "RZ")
36
-
37
- # two-qubit unitaries in the (control/target) = (a, b) index order, row = 2*a + b
38
- CX = np.array([[1, 0, 0, 0], [0, 1, 0, 0], [0, 0, 0, 1], [0, 0, 1, 0]], dtype=complex)
39
- CZ = np.diag([1, 1, 1, -1]).astype(complex)
40
- SWAP = np.array([[1, 0, 0, 0], [0, 0, 1, 0], [0, 1, 0, 0], [0, 0, 0, 1]], dtype=complex)
41
- TWO_QUBIT = {"CX": CX, "CNOT": CX, "CZ": CZ, "SWAP": SWAP}
42
-
43
-
44
- def rotation(name, theta):
45
- c, s = math.cos(theta / 2), math.sin(theta / 2)
46
- if name == "RX":
47
- return np.array([[c, -1j * s], [-1j * s, c]], dtype=complex)
48
- if name == "RY":
49
- return np.array([[c, -s], [s, c]], dtype=complex)
50
- return np.diag([np.exp(-1j * theta / 2), np.exp(1j * theta / 2)]).astype(complex)
51
-
52
-
53
- # ---------------------------------------------------------------- simulator
54
- def apply_one(state, U, q, n):
55
- """Apply a 1-qubit unitary to qubit `q` by moving its axis to the front."""
56
- a = np.moveaxis(state.reshape([2] * n), n - 1 - q, 0)
57
- a = np.tensordot(U, a, axes=([1], [0]))
58
- return np.moveaxis(a, 0, n - 1 - q).reshape(-1)
59
-
60
-
61
- def apply_two(state, U, qa, qb, n):
62
- """Apply a 2-qubit unitary; qa/qb are reordered so the matrix index is 2*a + b."""
63
- axa, axb = n - 1 - qa, n - 1 - qb
64
- a = np.moveaxis(state.reshape([2] * n), [axa, axb], [0, 1])
65
- a = (U @ a.reshape(4, -1)).reshape(a.shape)
66
- return np.moveaxis(a, [0, 1], [axa, axb]).reshape(-1)
67
-
68
-
69
- def parse_circuit(text, n=None):
70
- """Parse `H 0` / `RZ 1 0.785` / `CX 0 1` lines. `*` means every qubit."""
71
- lines = []
72
- for raw in (text or "").splitlines():
73
- line = raw.split("#", 1)[0].strip()
74
- if line:
75
- lines.append(line.split())
76
- if not lines:
77
- raise ValueError("empty circuit — give at least one gate")
78
- qubits = []
79
- for parts in lines:
80
- gate = parts[0].upper()
81
- if gate in ONE_QUBIT or gate in ROTATIONS:
82
- if len(parts) < 2:
83
- raise ValueError(f"'{parts[0]}' needs a qubit, e.g. '{gate} 0'")
84
- if parts[1] != "*":
85
- qubits.append(int(parts[1]))
86
- elif gate in TWO_QUBIT:
87
- if len(parts) < 3:
88
- raise ValueError(f"'{parts[0]}' needs two qubits, e.g. '{gate} 0 1'")
89
- qubits += [int(parts[1]), int(parts[2])]
90
- else:
91
- raise ValueError(f"unknown gate '{parts[0]}'")
92
- if not qubits:
93
- # only possible when every gate targets `*`, which cannot imply a width
94
- if not n:
95
- raise ValueError("a circuit that only uses '*' needs the qubit count filled in")
96
- n = int(n)
97
- else:
98
- n = int(n) if n else (max(qubits) + 1)
99
- if n < 1 or n > 12:
100
- raise ValueError("qubit count must be between 1 and 12")
101
- if qubits and max(qubits) >= n:
102
- raise ValueError(f"circuit uses qubit {max(qubits)} but n={n}")
103
- return lines, n
104
-
105
-
106
- def simulate(text, n=None):
107
- """Run a circuit; return (final state, parsed lines, n)."""
108
- lines, n = parse_circuit(text, n)
109
- state = np.zeros(2 ** n, dtype=complex)
110
- state[0] = 1.0
111
- for parts in lines:
112
- gate = parts[0].upper()
113
- if gate in ONE_QUBIT:
114
- targets = range(n) if parts[1] == "*" else [int(parts[1])]
115
- for q in targets:
116
- state = apply_one(state, ONE_QUBIT[gate], q, n)
117
- elif gate in ROTATIONS:
118
- if len(parts) < 3:
119
- raise ValueError(f"'{gate}' needs a qubit and an angle, e.g. '{gate} 0 0.785'")
120
- theta = float(parts[2])
121
- targets = range(n) if parts[1] == "*" else [int(parts[1])]
122
- U = rotation(gate, theta)
123
- for q in targets:
124
- state = apply_one(state, U, q, n)
125
- else:
126
- state = apply_two(state, TWO_QUBIT[gate], int(parts[1]), int(parts[2]), n)
127
- state = state / np.linalg.norm(state) # guards against accumulated round-off
128
- return state, lines, n
129
-
130
-
131
- def basis_label(i, n):
132
- return "|" + format(int(i), f"0{n}b") + ">"
133
-
134
-
135
- def fmt_complex(z):
136
- return f"{z.real:+.4f}{z.imag:+.4f}i"
137
-
138
-
139
- def z_expectations(state, n):
140
- probs = np.abs(state) ** 2
141
- out = []
142
- for q in range(n):
143
- sign = np.array([1.0 if not ((i >> (n - 1 - q)) & 1) else -1.0 for i in range(2 ** n)])
144
- out.append(float(np.sum(sign * probs)))
145
- return out
146
-
147
-
148
- # ---------------------------------------------------------------- tab 1
149
- DEFAULT_CIRCUIT = """H 0
150
- CX 0 1
151
- RZ 1 0.785
152
- H 1
153
- CX 0 2"""
154
-
155
-
156
- def circuit_sim(text, n_qubits):
157
- try:
158
- state, lines, n = simulate(text, n_qubits or None)
159
- except (ValueError, TypeError) as e:
160
- return f"Circuit error: {e}", "", ""
161
- probs = np.abs(state) ** 2
162
- head = [
163
- f"{len(lines)} gates on {n} qubits |state| = {np.linalg.norm(state):.12f}",
164
- f"non-zero amplitudes: {int(np.sum(probs > 1e-12))} of {2 ** n}",
165
- "",
166
- "probabilities",
167
- ]
168
- head += [f" {basis_label(i, n):>12} {probs[i]:.6f}" for i in np.argsort(-probs) if probs[i] > 1e-9]
169
- amps = ["amplitudes (top 16 by probability)"]
170
- for i in np.argsort(-probs)[:16]:
171
- if probs[i] > 1e-12:
172
- amps.append(f" {basis_label(i, n):>12} {fmt_complex(state[i])} p = {probs[i]:.6f}")
173
- zs = z_expectations(state, n)
174
- zs_out = ["<Z_q> per qubit (-1 / +1 is a definite bit, 0 is full superposition)"]
175
- zs_out += [f" q{q}: {v:+.6f}" for q, v in enumerate(zs)]
176
- return "\n".join(head), "\n".join(amps), "\n".join(zs_out)
177
-
178
-
179
- INTRO_SIM = ("A text circuit language (`H 0`, `CX 0 1`, `RZ 1 0.785`, `H *` for all qubits) "
180
- "run on a hand-written statevector simulator. CPU only, no qiskit.")
181
-
182
-
183
- # ---------------------------------------------------------------- tab 2
184
- def superposition(n_qubits, shots):
185
- n = int(n_qubits)
186
- state, _, n = simulate("\n".join(f"H {q}" for q in range(n)), n)
187
- probs = np.abs(state) ** 2
188
- rng = np.random.default_rng(0)
189
- counts = np.bincount(rng.choice(2 ** n, size=int(shots), p=probs), minlength=2 ** n)
190
- N = 2 ** n
191
- rows = [
192
- f"n = {n} qubits -> N = {N} basis states, each with amplitude 1/sqrt(N) = {1 / math.sqrt(N):.6f}",
193
- f"max |p - 1/N| over all states = {np.max(np.abs(probs - 1 / N)):.3e}",
194
- "",
195
- f"{'state':>12} {'amplitude':>22} {'p':>10} {'count/'+str(int(shots)):>12} freq",
196
- ]
197
- for i in range(min(N, 16)):
198
- rows.append(f"{basis_label(i, n):>12} {fmt_complex(state[i]):>22} {probs[i]:>10.6f} "
199
- f"{counts[i]:>12} {counts[i] / shots:.4f}")
200
- if N > 16:
201
- rows.append(f" ... {N - 16} more states, all with the same probability")
202
- rows += [
203
- "",
204
- "why the sqrt(N) matters",
205
- f" a single measurement identifies one of {N} outcomes: P(correct) = 1/N = {1 / N:.6f}",
206
- f" the standard error of the estimate falls as 1/sqrt(N) = {1 / math.sqrt(N):.6f} per shot",
207
- f" to reach a fixed confidence you need O(N) shots classically — see Grover Search "
208
- f"for the quantum route at O(sqrt(N))",
209
- ]
210
- return "\n".join(rows)
211
-
212
-
213
- INTRO_SUPER = ("N = 2^n basis states carried at once, with the uniform amplitude 1/sqrt(N) "
214
- "verified state by state against seeded sampling.")
215
-
216
-
217
- # ---------------------------------------------------------------- tab 3
218
- def grover(n_qubits, marked):
219
- n = int(n_qubits)
220
- N = 2 ** n
221
- marked = int(marked)
222
- if not 0 <= marked < N:
223
- return f"The marked item must be between 0 and {N - 1} for n = {n}."
224
- state = np.ones(N, dtype=complex) / math.sqrt(N)
225
- optimal = max(1, int(round(math.pi / 4 * math.sqrt(N))))
226
- rows = [
227
- f"search space N = {N} marked item = {basis_label(marked, n)}",
228
- f"classical search: {N // 2} trials on average. Grover: about pi/4 * sqrt(N) = "
229
- f"{math.pi / 4 * math.sqrt(N):.2f} iterations.",
230
- "",
231
- f"{'iteration':>9} {'P(marked)':>12} {'amplitude(marked)':>20} {'max other p':>12}",
232
- ]
233
- p = float(abs(state[marked]) ** 2)
234
- rows.append(f"{0:>9} {p:>12.6f} {fmt_complex(state[marked]):>20} "
235
- f"{float(np.max(np.delete(np.abs(state) ** 2, marked))):>12.6f}")
236
- for it in range(1, optimal + 2):
237
- state = state.copy()
238
- state[marked] *= -1.0 # oracle: phase flip the target
239
- state = 2 * np.mean(state) * np.ones(N, dtype=complex) - state # diffusion: 2|s><s| - I
240
- p = float(abs(state[marked]) ** 2)
241
- others = float(np.max(np.delete(np.abs(state) ** 2, marked)))
242
- rows.append(f"{it:>9} {p:>12.6f} {fmt_complex(state[marked]):>20} {others:>12.6f}")
243
- if p > 0.999:
244
- break
245
- rows += [
246
- "",
247
- "amplitude amplification: the oracle flips the sign of the marked amplitude, the "
248
- "diffusion mirrors every amplitude about the mean. Both steps together rotate the "
249
- "state toward the marked item by a fixed angle, so the success probability rises "
250
- "as sin^2((2k+1) theta) with sin(theta) = 1/sqrt(N).",
251
- "past the optimal iteration count the same rotation carries the state away again — "
252
- "the table shows the rise, and the peak is the shortest route to the answer.",
253
- ]
254
- return "\n".join(rows)
255
-
256
-
257
- INTRO_GROVER = ("Grover's algorithm run for real on the simulator: the oracle and the "
258
- "diffusion operator applied iteration by iteration, with the success "
259
- "probability after each one.")
260
-
261
-
262
- # ---------------------------------------------------------------- tab 4
263
- def reduced_density(state, q, n):
264
- a = np.moveaxis(state.reshape([2] * n), n - 1 - q, 0).reshape(2, -1)
265
- return a @ a.conj().T
266
-
267
-
268
- def entropy_of(state, q, n):
269
- ev = np.linalg.eigvalsh(reduced_density(state, q, n))
270
- ev = ev[ev > 1e-12]
271
- return float(-np.sum(ev * np.log2(ev)))
272
-
273
-
274
- def bell_states():
275
- out = ["Bell states, each produced by gates on |00> (not written down by hand):", ""]
276
- setup = {"|Phi+>": "H 0\nCX 0 1", "|Phi->": "H 0\nCX 0 1\nZ 0",
277
- "|Psi+>": "H 0\nCX 0 1\nX 0", "|Psi->": "H 0\nCX 0 1\nZ 0\nX 0"}
278
- for name, circ in setup.items():
279
- state, _, n = simulate(circ, 2)
280
- amps = " ".join(f"{basis_label(i, n)}={fmt_complex(state[i])}"
281
- for i in range(4) if abs(state[i]) > 1e-9)
282
- gates = ", ".join(circ.splitlines())
283
- out.append(f"{name:7} -> {amps}")
284
- out.append(f" gates: {gates}")
285
- out.append(f" S(qubit0) = {entropy_of(state, 0, n):.4f} bits <- maximal entanglement")
286
- out.append("")
287
- return "\n".join(out)
288
-
289
-
290
- def ghz_states():
291
- out = ["Greenberger-Horne-Zeilinger states: H on qubit 0, then CNOT down the chain.", ""]
292
- for n in (2, 3, 4):
293
- circ = "H 0\n" + "\n".join(f"CX {q} {q + 1}" for q in range(n - 1))
294
- state, _, n = simulate(circ, n)
295
- probs = np.abs(state) ** 2
296
- live = [(basis_label(i, n), probs[i]) for i in range(2 ** n) if probs[i] > 1e-9]
297
- out.append(f"n = {n}: " + ", ".join(f"{lab} p={p:.4f}" for lab, p in live)
298
- + f" S(qubit0) = {entropy_of(state, 0, n):.4f} bit")
299
- out += ["", "Only all-zeros and all-ones ever appear, however many qubits are added: "
300
- "the whole register collapses as one object."]
301
- return "\n".join(out)
302
-
303
-
304
- def chsh():
305
- """E(a,b) = <phi| A(a) x B(b) |phi> with A(t) = cos(t) Z + sin(t) X."""
306
- state, _, n = simulate("H 0\nCX 0 1", 2)
307
- pairs = [("a=0", 0.0, "b=+pi/4", math.pi / 4), ("a=0", 0.0, "b'=-pi/4", -math.pi / 4),
308
- ("a'=pi/2", math.pi / 2, "b=+pi/4", math.pi / 4),
309
- ("a'=pi/2", math.pi / 2, "b'=-pi/4", -math.pi / 4)]
310
- rows = ["CHSH correlations from the simulator, |Phi+> on |00>:",
311
- f"{'direction pair':30} {'E(a,b)':>10}", ""]
312
- es = []
313
- for la, ta, lb, tb in pairs:
314
- A = math.cos(ta) * Z + math.sin(ta) * X
315
- B = math.cos(tb) * Z + math.sin(tb) * X
316
- M = np.kron(A, B)
317
- e = float(np.real(state.conj() @ M @ state))
318
- es.append(e)
319
- rows.append(f"{la + ' vs ' + lb:30} {e:>10.6f}")
320
- S = es[0] + es[1] + es[2] - es[3]
321
- rows += [
322
- "",
323
- f"S = E(a,b) + E(a,b') + E(a',b) - E(a',b') = {S:.6f}",
324
- f"2 * sqrt(2) = {2 * math.sqrt(2):.6f} <- the quantum maximum (Tsirelson bound)",
325
- "classical bound: |S| <= 2. A value above 2 cannot be produced by any local "
326
- "hidden-variable model, which is the measured statement of Bell's theorem.",
327
- ]
328
- return "\n".join(rows)
329
-
330
-
331
- def entanglement_lab():
332
- return bell_states() + "\n\n" + ghz_states() + "\n\n" + chsh()
333
-
334
-
335
- INTRO_ENT = ("Bell and GHZ states built from gates, their entanglement entropy, and a CHSH "
336
- "correlation table computed from the statevector — the classical bound is "
337
- "exceeded numerically.")
338
-
339
-
340
- # ---------------------------------------------------------------- tab 5
341
- FAMILY = [
342
- ("QUANTUM_AI", "60 lines: qiskit + qiskit_aer + torch + transformers + sklearn"),
343
- ("QUANTUM_COGNITION", "89 lines: qiskit simulation behind a chat shell"),
344
- ("QUANTUM_COGNITION_CHAT_AI", "97 lines: qiskit + a Hugging Face pipeline at import"),
345
- ("QUANTUM_COGNITION_CHATBOT", "100 lines: same stack, model loaded at import time"),
346
- ("QUANTUM_FUZZY_CONSTRAINTS", "106 lines: qiskit behind a fuzzy-constraint demo"),
347
- ("QUANTUM_GEN", "80 lines: qiskit + generated text"),
348
- ("QUANTUM_GENERATIVE_MODEL", "79 lines: model download at import time"),
349
- ("QUANTUM_GENIE", "61 lines: qiskit + a text pipeline"),
350
- ("QUANTUM_GENIUS", "118 lines: qiskit + transformers, no CPU path"),
351
- ("QUANTUM_IMG_GEN", "55 lines: qiskit + torch + facebook/bart-large-mnli + Olivetti faces"),
352
- ("QUANTUM_SUPERPOSITION_GEN", "60 lines: qiskit + GPT-2 weights loaded at import"),
353
- ]
354
-
355
- REASONS = ("Every member states its dependencies as `qiskit`, `qiskit_aer`, `torch`, "
356
- "`transformers` and often `sklearn` and `matplotlib`, and several download model "
357
- "weights (`gpt2`, `facebook/bart-large-mnli`) or a dataset (`fetch_olivetti_faces`) "
358
- "at import time. On a free cpu-basic Space that means a multi-gigabyte image and a "
359
- "cold start that pulls weights before the app can serve, which is why all 11 sat "
360
- "asleep rather than running.")
361
-
362
-
363
- def family_index():
364
- rows = ["| consolidated Space | what blocks it on a free CPU Space |", "|---|---|"]
365
- rows += [f"| `{name}` | {why} |" for name, why in FAMILY]
366
- head = ["# Family Index", "",
367
- f"**{len(FAMILY)} Spaces in the `QUANTUM_*` family**, measured on 2026-10-12. "
368
- "They are consolidated into the four tabs above, which run in **one** cpu-basic "
369
- "slot instead of one Space per project.", "", REASONS, "",
370
- "The replacement keeps the quantum mechanics (superposition, interference, "
371
- "amplitude amplification, entanglement, Bell violation) and drops the model "
372
- "weights that made the originals unhostable. Nothing here is simulated with "
373
- "another library: the amplitudes are computed directly.", ""]
374
- return "\n".join(head + rows)
375
-
376
-
377
- INTRO_FAMILY = ("The 11 `QUANTUM_*` Spaces this suite consolidates, and the measured reason "
378
- "each one cannot run on a free cpu-basic Space.")
379
-
380
-
381
- # ---------------------------------------------------------------- app
382
- INTRO = {
383
- "Circuit Simulator": INTRO_SIM,
384
- "Superposition Explorer": INTRO_SUPER,
385
- "Grover Search": INTRO_GROVER,
386
- "Entanglement Lab": INTRO_ENT,
387
- "Family Index": INTRO_FAMILY,
388
- }
389
-
390
-
391
- def page():
392
- with gr.Tabs():
393
- with gr.Tab("Circuit Simulator"):
394
- gr.Markdown(INTRO_SIM)
395
- with gr.Row():
396
- code = gr.Code(label="circuit", language="python", value=DEFAULT_CIRCUIT)
397
- nq = gr.Number(label="qubits (blank = infer from the circuit)", value=3, precision=0)
398
- o1 = gr.Textbox(lines=22, label="probabilities")
399
- with gr.Row():
400
- o2 = gr.Textbox(lines=18, label="amplitudes")
401
- o3 = gr.Textbox(lines=18, label="expectation of Z")
402
- gr.Button("Run circuit").click(circuit_sim, [code, nq], [o1, o2, o3])
403
-
404
- with gr.Tab("Superposition Explorer"):
405
- gr.Markdown(INTRO_SUPER)
406
- with gr.Row():
407
- n2 = gr.Slider(1, 6, value=4, step=1, label="qubits")
408
- shots = gr.Slider(256, 8192, value=4096, step=256, label="shots")
409
- o4 = gr.Textbox(lines=26, label="uniform superposition")
410
- gr.Button("Explore").click(superposition, [n2, shots], o4)
411
-
412
- with gr.Tab("Grover Search"):
413
- gr.Markdown(INTRO_GROVER)
414
- with gr.Row():
415
- n3 = gr.Slider(3, 6, value=4, step=1, label="qubits")
416
- marked = gr.Number(label="marked item (integer)", value=11, precision=0)
417
- o5 = gr.Textbox(lines=26, label="iterations")
418
- gr.Button("Search").click(grover, [n3, marked], o5)
419
-
420
- with gr.Tab("Entanglement Lab"):
421
- gr.Markdown(INTRO_ENT)
422
- o6 = gr.Textbox(lines=34, label="states, entropy and CHSH")
423
- gr.Button("Compute").click(entanglement_lab, None, o6)
424
-
425
- with gr.Tab("Family Index"):
426
- gr.Markdown(family_index())
427
-
428
-
429
- if __name__ == "__main__":
430
- with gr.Blocks(title="Quantum Suite", theme=gr.themes.Soft()) as demo:
431
- gr.Markdown("# Quantum Suite\n"
432
- "A statevector quantum simulator over a whole project family, in a "
433
- "single cpu-basic Space. **No qiskit, no torch, no transformers, no GPU.**\n\n"
434
- "Companion to the [GGUF Toolkit](https://huggingface.co/spaces/AEUPH/gguf-toolkit).")
435
- page()
436
- demo.launch()
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
requirements.txt DELETED
@@ -1,2 +0,0 @@
1
- gradio
2
- numpy