Skip to content

Commit 1208687

Browse files
author
Leon Kaiser
committed
stage
1 parent 9f16081 commit 1208687

File tree

1 file changed

+364
-0
lines changed

1 file changed

+364
-0
lines changed
Lines changed: 364 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,364 @@
1+
{
2+
"cells": [
3+
{
4+
"cell_type": "code",
5+
"execution_count": null,
6+
"metadata": {},
7+
"outputs": [],
8+
"source": [
9+
"# Quantum Circuit with Single Output Qubit\n",
10+
"# 3 source qubits → entanglement → 1 output qubit (only this is measured)\n",
11+
"\n",
12+
"import sys\n",
13+
"sys.path.append('..')\n",
14+
"\n",
15+
"from modul.circuit import Circuit\n",
16+
"import numpy as np\n",
17+
"from qiskit import QuantumCircuit, transpile\n",
18+
"from qiskit_aer import AerSimulator\n",
19+
"from qiskit.quantum_info import Statevector\n",
20+
"from scipy.optimize import minimize\n",
21+
"import matplotlib.pyplot as plt\n",
22+
"\n",
23+
"# Input and training matrices\n",
24+
"input_matrix = np.eye(3)\n",
25+
"training_matrix = np.random.rand(3, 3)\n",
26+
"\n",
27+
"print(\"Input Matrix (Identity):\")\n",
28+
"print(input_matrix)\n",
29+
"print(\"\\nTraining Matrix:\")\n",
30+
"print(training_matrix)\n",
31+
"\n",
32+
"def create_single_output_circuit(input_mat, training_mat):\n",
33+
" \"\"\"Create circuit: 3 source qubits → 1 output qubit\"\"\"\n",
34+
" qc = QuantumCircuit(4, 1) # 4 qubits, 1 measurement\n",
35+
" \n",
36+
" # Source qubits (0,1,2) with IP-TP-H-IP-TP-H-IP-TP\n",
37+
" for qubit in range(3):\n",
38+
" qc.p(float(input_mat[qubit, 0]), qubit)\n",
39+
" qc.p(float(training_mat[qubit, 0]), qubit)\n",
40+
" qc.h(qubit)\n",
41+
" qc.p(float(input_mat[qubit, 1]), qubit)\n",
42+
" qc.p(float(training_mat[qubit, 1]), qubit)\n",
43+
" qc.h(qubit)\n",
44+
" qc.p(float(input_mat[qubit, 2]), qubit)\n",
45+
" qc.p(float(training_mat[qubit, 2]), qubit)\n",
46+
" \n",
47+
" # Entangle all source qubits to output qubit (3)\n",
48+
" qc.cx(0, 3)\n",
49+
" qc.cx(1, 3) \n",
50+
" qc.cx(2, 3)\n",
51+
" \n",
52+
" # P-H-P-H-P on output qubit\n",
53+
" output_phases = [1.0, 2.0, 3.0] # Fixed for reproducibility\n",
54+
" qc.p(output_phases[0], 3)\n",
55+
" qc.h(3)\n",
56+
" qc.p(output_phases[1], 3)\n",
57+
" qc.h(3)\n",
58+
" qc.p(output_phases[2], 3)\n",
59+
" \n",
60+
" # Measure ONLY output qubit\n",
61+
" qc.measure(3, 0)\n",
62+
" \n",
63+
" return qc\n",
64+
"\n",
65+
"# Create and measure initial circuit\n",
66+
"simulator = AerSimulator()\n",
67+
"initial_circuit = create_single_output_circuit(input_matrix, training_matrix)\n",
68+
"\n",
69+
"print(\"\\nCircuit (3 source → 1 output):\")\n",
70+
"print(initial_circuit.draw(output='text'))\n",
71+
"\n",
72+
"# Initial measurement\n",
73+
"compiled = transpile(initial_circuit, simulator)\n",
74+
"job = simulator.run(compiled, shots=1000)\n",
75+
"counts = job.result().get_counts(compiled)\n",
76+
"\n",
77+
"print(\"\\nInitial measurement (output qubit only):\")\n",
78+
"for outcome, count in sorted(counts.items()):\n",
79+
" print(f\"Output |{outcome}>: {count} counts ({count/1000:.1%})\")\n",
80+
"\n",
81+
"# Target: most probable output\n",
82+
"target_output = max(counts, key=counts.get)\n",
83+
"print(f\"\\nTarget output: |{target_output}>\")"
84+
]
85+
},
86+
{
87+
"cell_type": "code",
88+
"execution_count": null,
89+
"metadata": {},
90+
"outputs": [],
91+
"source": [
92+
"# Two different input matrices with single output measurement\n",
93+
"print(\"TWO INPUT MATRICES → SINGLE OUTPUT MEASUREMENT\")\n",
94+
"print(\"=\" * 60)\n",
95+
"\n",
96+
"# Generate different inputs\n",
97+
"input_matrix_1 = np.random.rand(3, 3)\n",
98+
"input_matrix_2 = np.random.rand(3, 3) \n",
99+
"shared_training = np.random.rand(3, 3)\n",
100+
"\n",
101+
"print(\"Input Matrix 1:\")\n",
102+
"print(input_matrix_1)\n",
103+
"print(\"\\nInput Matrix 2:\") \n",
104+
"print(input_matrix_2)\n",
105+
"\n",
106+
"# Test both inputs until we get different most probable outputs\n",
107+
"max_attempts = 50\n",
108+
"for attempt in range(max_attempts):\n",
109+
" # Create circuits for both inputs\n",
110+
" circuit_1 = create_single_output_circuit(input_matrix_1, shared_training)\n",
111+
" circuit_2 = create_single_output_circuit(input_matrix_2, shared_training)\n",
112+
" \n",
113+
" # Measure both\n",
114+
" compiled_1 = transpile(circuit_1, simulator)\n",
115+
" compiled_2 = transpile(circuit_2, simulator)\n",
116+
" \n",
117+
" job_1 = simulator.run(compiled_1, shots=1000)\n",
118+
" job_2 = simulator.run(compiled_2, shots=1000)\n",
119+
" \n",
120+
" counts_1 = job_1.result().get_counts(compiled_1)\n",
121+
" counts_2 = job_2.result().get_counts(compiled_2)\n",
122+
" \n",
123+
" target_1 = max(counts_1, key=counts_1.get)\n",
124+
" target_2 = max(counts_2, key=counts_2.get)\n",
125+
" \n",
126+
" if target_1 != target_2:\n",
127+
" print(f\"\\nSuccess after {attempt + 1} attempts!\")\n",
128+
" print(f\"Input 1 → Output |{target_1}> ({counts_1[target_1]/1000:.1%})\")\n",
129+
" print(f\"Input 2 → Output |{target_2}> ({counts_2[target_2]/1000:.1%})\")\n",
130+
" break\n",
131+
" \n",
132+
" # Regenerate if same output\n",
133+
" input_matrix_1 = np.random.rand(3, 3)\n",
134+
" input_matrix_2 = np.random.rand(3, 3)\n",
135+
" shared_training = np.random.rand(3, 3)\n",
136+
"\n",
137+
"# Store results\n",
138+
"stored_data = {\n",
139+
" 'input_1': input_matrix_1,\n",
140+
" 'input_2': input_matrix_2, \n",
141+
" 'training': shared_training,\n",
142+
" 'target_1': target_1,\n",
143+
" 'target_2': target_2,\n",
144+
" 'counts_1': counts_1,\n",
145+
" 'counts_2': counts_2\n",
146+
"}\n",
147+
"\n",
148+
"print(\"\\nBoth inputs produce different output targets!\")"
149+
]
150+
},
151+
{
152+
"cell_type": "code",
153+
"execution_count": null,
154+
"metadata": {},
155+
"outputs": [],
156+
"source": [
157+
"# Train for Input 1's target output\n",
158+
"print(\"TRAINING FOR INPUT 1'S TARGET OUTPUT\")\n",
159+
"print(\"=\" * 50)\n",
160+
"\n",
161+
"target_1 = stored_data['target_1']\n",
162+
"input_1 = stored_data['input_1']\n",
163+
"training_start = stored_data['training'].copy()\n",
164+
"\n",
165+
"print(f\"Target for Input 1: |{target_1}>\")\n",
166+
"\n",
167+
"def objective_input_1(training_flat):\n",
168+
" training_mat = training_flat.reshape(3, 3)\n",
169+
" \n",
170+
" # Create circuit without measurement for analysis\n",
171+
" qc = QuantumCircuit(4)\n",
172+
" for qubit in range(3):\n",
173+
" qc.p(float(input_1[qubit, 0]), qubit)\n",
174+
" qc.p(float(training_mat[qubit, 0]), qubit)\n",
175+
" qc.h(qubit)\n",
176+
" qc.p(float(input_1[qubit, 1]), qubit) \n",
177+
" qc.p(float(training_mat[qubit, 1]), qubit)\n",
178+
" qc.h(qubit)\n",
179+
" qc.p(float(input_1[qubit, 2]), qubit)\n",
180+
" qc.p(float(training_mat[qubit, 2]), qubit)\n",
181+
" \n",
182+
" qc.cx(0, 3)\n",
183+
" qc.cx(1, 3)\n",
184+
" qc.cx(2, 3)\n",
185+
" \n",
186+
" output_phases = [1.0, 2.0, 3.0]\n",
187+
" qc.p(output_phases[0], 3)\n",
188+
" qc.h(3)\n",
189+
" qc.p(output_phases[1], 3)\n",
190+
" qc.h(3)\n",
191+
" qc.p(output_phases[2], 3)\n",
192+
" \n",
193+
" # Get marginal probability for output qubit\n",
194+
" state = Statevector.from_instruction(qc)\n",
195+
" probs = state.probabilities()\n",
196+
" \n",
197+
" if target_1 == '0':\n",
198+
" target_prob = sum(probs[i] for i in range(0, 16, 2)) # Even indices\n",
199+
" else:\n",
200+
" target_prob = sum(probs[i] for i in range(1, 16, 2)) # Odd indices\n",
201+
" \n",
202+
" return -target_prob\n",
203+
"\n",
204+
"# Optimize\n",
205+
"result_1 = minimize(\n",
206+
" objective_input_1,\n",
207+
" training_start.flatten() * 2 * np.pi,\n",
208+
" method='L-BFGS-B',\n",
209+
" bounds=[(0, 2*np.pi) for _ in range(9)]\n",
210+
")\n",
211+
"\n",
212+
"trained_matrix_1 = result_1.x.reshape(3, 3) / (2 * np.pi)\n",
213+
"\n",
214+
"# Test result\n",
215+
"test_circuit_1 = create_single_output_circuit(input_1, trained_matrix_1)\n",
216+
"compiled_test = transpile(test_circuit_1, simulator)\n",
217+
"job_test = simulator.run(compiled_test, shots=1000)\n",
218+
"counts_test_1 = job_test.result().get_counts(compiled_test)\n",
219+
"\n",
220+
"print(f\"\\nAfter training for Input 1:\")\n",
221+
"for outcome, count in sorted(counts_test_1.items()):\n",
222+
" if outcome == target_1:\n",
223+
" print(f\"**|{outcome}>: {count} counts ({count/1000:.1%}) [TARGET]**\")\n",
224+
" else:\n",
225+
" print(f\" |{outcome}>: {count} counts ({count/1000:.1%})\")\n",
226+
"\n",
227+
"print(f\"\\nImprovement: {counts_test_1[target_1]/1000:.1%} vs {stored_data['counts_1'][target_1]/1000:.1%}\")"
228+
]
229+
},
230+
{
231+
"cell_type": "code",
232+
"execution_count": null,
233+
"metadata": {},
234+
"outputs": [],
235+
"source": [
236+
"# Continue training for Input 2's target\n",
237+
"print(\"CONTINUING TRAINING FOR INPUT 2'S TARGET OUTPUT\")\n",
238+
"print(\"=\" * 50)\n",
239+
"\n",
240+
"target_2 = stored_data['target_2']\n",
241+
"input_2 = stored_data['input_2']\n",
242+
"\n",
243+
"print(f\"Target for Input 2: |{target_2}>\")\n",
244+
"print(f\"Starting from Input 1's trained matrix...\")\n",
245+
"\n",
246+
"def objective_input_2(training_flat):\n",
247+
" training_mat = training_flat.reshape(3, 3)\n",
248+
" \n",
249+
" # Create circuit without measurement\n",
250+
" qc = QuantumCircuit(4)\n",
251+
" for qubit in range(3):\n",
252+
" qc.p(float(input_2[qubit, 0]), qubit)\n",
253+
" qc.p(float(training_mat[qubit, 0]), qubit)\n",
254+
" qc.h(qubit)\n",
255+
" qc.p(float(input_2[qubit, 1]), qubit)\n",
256+
" qc.p(float(training_mat[qubit, 1]), qubit)\n",
257+
" qc.h(qubit)\n",
258+
" qc.p(float(input_2[qubit, 2]), qubit)\n",
259+
" qc.p(float(training_mat[qubit, 2]), qubit)\n",
260+
" \n",
261+
" qc.cx(0, 3)\n",
262+
" qc.cx(1, 3)\n",
263+
" qc.cx(2, 3)\n",
264+
" \n",
265+
" output_phases = [1.0, 2.0, 3.0]\n",
266+
" qc.p(output_phases[0], 3)\n",
267+
" qc.h(3)\n",
268+
" qc.p(output_phases[1], 3)\n",
269+
" qc.h(3)\n",
270+
" qc.p(output_phases[2], 3)\n",
271+
" \n",
272+
" # Get marginal probability for output qubit\n",
273+
" state = Statevector.from_instruction(qc)\n",
274+
" probs = state.probabilities()\n",
275+
" \n",
276+
" if target_2 == '0':\n",
277+
" target_prob = sum(probs[i] for i in range(0, 16, 2))\n",
278+
" else:\n",
279+
" target_prob = sum(probs[i] for i in range(1, 16, 2))\n",
280+
" \n",
281+
" return -target_prob\n",
282+
"\n",
283+
"# Start from trained matrix 1\n",
284+
"result_2 = minimize(\n",
285+
" objective_input_2,\n",
286+
" trained_matrix_1.flatten() * 2 * np.pi,\n",
287+
" method='L-BFGS-B', \n",
288+
" bounds=[(0, 2*np.pi) for _ in range(9)]\n",
289+
")\n",
290+
"\n",
291+
"final_trained_matrix = result_2.x.reshape(3, 3) / (2 * np.pi)\n",
292+
"\n",
293+
"print(\"\\nFinal trained matrix:\")\n",
294+
"print(final_trained_matrix)\n",
295+
"\n",
296+
"# Test both inputs with final matrix\n",
297+
"test_1_final = create_single_output_circuit(input_1, final_trained_matrix)\n",
298+
"test_2_final = create_single_output_circuit(input_2, final_trained_matrix)\n",
299+
"\n",
300+
"job_1_final = simulator.run(transpile(test_1_final, simulator), shots=1000)\n",
301+
"job_2_final = simulator.run(transpile(test_2_final, simulator), shots=1000)\n",
302+
"\n",
303+
"counts_1_final = job_1_final.result().get_counts()\n",
304+
"counts_2_final = job_2_final.result().get_counts()\n",
305+
"\n",
306+
"print(f\"\\nFINAL RESULTS:\")\n",
307+
"print(f\"Input 1 → |{target_1}>: {counts_1_final.get(target_1, 0)/1000:.1%}\")\n",
308+
"print(f\"Input 2 → |{target_2}>: {counts_2_final.get(target_2, 0)/1000:.1%}\")\n",
309+
"\n",
310+
"# Visualization\n",
311+
"fig, (ax1, ax2) = plt.subplots(1, 2, figsize=(10, 4))\n",
312+
"\n",
313+
"# Input 1 results\n",
314+
"outcomes_1 = list(counts_1_final.keys())\n",
315+
"probs_1 = [counts_1_final[k]/1000 for k in outcomes_1]\n",
316+
"bars1 = ax1.bar(outcomes_1, probs_1, color='lightblue')\n",
317+
"if target_1 in outcomes_1:\n",
318+
" bars1[outcomes_1.index(target_1)].set_color('darkblue')\n",
319+
"ax1.set_title(f'Input 1 → Target |{target_1}>')\n",
320+
"ax1.set_ylabel('Probability')\n",
321+
"for i, p in enumerate(probs_1):\n",
322+
" ax1.text(i, p + 0.02, f'{p:.1%}', ha='center')\n",
323+
"\n",
324+
"# Input 2 results \n",
325+
"outcomes_2 = list(counts_2_final.keys())\n",
326+
"probs_2 = [counts_2_final[k]/1000 for k in outcomes_2]\n",
327+
"bars2 = ax2.bar(outcomes_2, probs_2, color='lightgreen')\n",
328+
"if target_2 in outcomes_2:\n",
329+
" bars2[outcomes_2.index(target_2)].set_color('darkgreen')\n",
330+
"ax2.set_title(f'Input 2 → Target |{target_2}>')\n",
331+
"ax2.set_ylabel('Probability')\n",
332+
"for i, p in enumerate(probs_2):\n",
333+
" ax2.text(i, p + 0.02, f'{p:.1%}', ha='center')\n",
334+
"\n",
335+
"plt.suptitle('Single Output Qubit Results with Final Trained Matrix')\n",
336+
"plt.tight_layout()\n",
337+
"plt.show()\n",
338+
"\n",
339+
"print(\"\\n3 source qubits compressed to 1 output qubit via entanglement!\")"
340+
]
341+
}
342+
],
343+
"metadata": {
344+
"kernelspec": {
345+
"display_name": "Python 3",
346+
"language": "python",
347+
"name": "python3"
348+
},
349+
"language_info": {
350+
"codemirror_mode": {
351+
"name": "ipython",
352+
"version": 3
353+
},
354+
"file_extension": ".py",
355+
"mimetype": "text/x-python",
356+
"name": "python",
357+
"nbconvert_exporter": "python",
358+
"pygments_lexer": "ipython3",
359+
"version": "3.8.0"
360+
}
361+
},
362+
"nbformat": 4,
363+
"nbformat_minor": 4
364+
}

0 commit comments

Comments
 (0)