Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
211 changes: 156 additions & 55 deletions task 1.ipynb
Original file line number Diff line number Diff line change
Expand Up @@ -38,33 +38,46 @@
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"metadata": {
"ExecuteTime": {
"end_time": "2025-10-09T17:17:26.193607Z",
"start_time": "2025-10-09T17:17:26.178663Z"
}
},
"source": [
"def toy_hash(x):\n",
" \"\"\"Hash function with unique preimages: (3*x + 5) mod 8.\"\"\"\n",
" return (3 * x + 5) % 8\n",
"\n"
]
],
"outputs": [],
"execution_count": 29
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"metadata": {
"ExecuteTime": {
"end_time": "2025-10-09T17:17:26.208993Z",
"start_time": "2025-10-09T17:17:26.194629Z"
}
},
"source": [
"# Setup\n",
"n_qubits = 3 # 3-bit password (values 0-7)\n",
"target_hash = 5 # The hash value we want to find a pre-image for\n",
"\n"
]
],
"outputs": [],
"execution_count": 30
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"metadata": {
"ExecuteTime": {
"end_time": "2025-10-09T17:17:26.234388Z",
"start_time": "2025-10-09T17:17:26.227422Z"
}
},
"source": [
"def classical_preimage_search():\n",
" \"\"\"Classically search for a pre-image of the target hash.\"\"\"\n",
Expand All @@ -77,25 +90,43 @@
"\n",
"actual_password = classical_preimage_search()\n",
"\n"
]
],
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"h(0) = 5\n"
]
}
],
"execution_count": 31
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"metadata": {
"ExecuteTime": {
"end_time": "2025-10-09T17:17:26.272100Z",
"start_time": "2025-10-09T17:17:26.266483Z"
}
},
"source": [
"from qiskit import QuantumCircuit, QuantumRegister, ClassicalRegister, transpile\n",
"from qiskit.circuit.library import MCMTGate, ZGate\n",
"from qiskit_aer import AerSimulator\n",
"import numpy as np\n"
]
],
"outputs": [],
"execution_count": 32
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"metadata": {
"ExecuteTime": {
"end_time": "2025-10-09T19:06:48.610911Z",
"start_time": "2025-10-09T19:06:48.601493Z"
}
},
"source": [
"def create_oracle(n_qubits, target_hash):\n",
" \"\"\"\n",
Expand All @@ -115,38 +146,53 @@
"\n",
" # Step 1: Compute hash(x) = (3*x + 5) mod 8\n",
" # TODO: Multiply by 3. HINT: 3*x = 2*x + x, so use CNOTs for left shift plus addition\n",
"\n",
"\n",
" # We only care about the lower 3 qubits of hash\n",
" # We can throw the MSB away because of the mod 8\n",
" for i in range(n_qubits - 1):\n",
" oracle.cx(input_qubits[i],hash_qubits[i + 1])\n",
"\n",
" for i in range(n_qubits):\n",
" oracle.cx(input_qubits[i], hash_qubits[i])\n",
" \n",
" # TODO: Add 5. HINT: Use X gates to add constants in binary (5 = 101 in binary)\n",
"\n",
" oracle.x(n_qubits)\n",
" oracle.x(n_qubits+2)\n",
" # Step 2: Check if hash(x) == target_hash\n",
" target_binary = format(target_hash, f\"0{n_qubits}b\")\n",
"\n",
" # Flip qubits where target bit is 0 (so we match on |111...>)\n",
" for i, bit in enumerate(target_binary):\n",
" # TODO: Update here\n",
" pass \n",
" if bit == '0':\n",
" oracle.x(n_qubits+i)\n",
"\n",
" # TODO: Mark the state with multi-controlled NOT\n",
" oracle.mcx(hash_qubits, phase_qubit)\n",
"\n",
" # Unflip the checking bits\n",
" for i, bit in enumerate(target_binary):\n",
" # TODO: Update here\n",
" pass\n",
" if bit == '0':\n",
" oracle.x(n_qubits+i)\n",
"\n",
" # Step 3: Uncompute hash to clean up ancilla\n",
" # TODO: Reverse the hash computation steps\n",
"\n",
" \n",
" # The ancilla is unmodified in the hash calculation\n",
" \n",
" return oracle\n",
"\n"
]
],
"outputs": [],
"execution_count": 65
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"metadata": {
"ExecuteTime": {
"end_time": "2025-10-09T19:06:49.955583Z",
"start_time": "2025-10-09T19:06:49.943819Z"
}
},
"source": [
"def create_diffusion(n_qubits):\n",
" \"\"\"Create diffusion operator (inversion about average)\"\"\"\n",
Expand All @@ -155,43 +201,52 @@
" # TODO: Implement the diffusion operator\n",
"\n",
" # Step 1: Apply H to all qubits\n",
"\n",
" diffusion.h(range(n_qubits))\n",
"\n",
" # Step 2: Apply X to all qubits\n",
"\n",
" diffusion.x(range(n_qubits))\n",
"\n",
" # Step 3: Multi-controlled Z (using ancilla if needed)\n",
"\n",
"\n",
"\n",
" mcz = MCMTGate(ZGate(),n_qubits-1,1)\n",
" diffusion.append(mcz, range(n_qubits))\n",
"\n",
"\n",
" # Step 4: Apply X to all qubits\n",
"\n",
" diffusion.x(range(n_qubits))\n",
"\n",
" # Step 5: Apply H to all qubits\n",
"\n",
" diffusion.h(range(n_qubits))\n",
"\n",
" return diffusion\n",
"\n"
]
],
"outputs": [],
"execution_count": 66
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"metadata": {
"ExecuteTime": {
"end_time": "2025-10-09T19:06:50.296651Z",
"start_time": "2025-10-09T19:06:50.291068Z"
}
},
"source": [
"# Calculate number of Grover iterations\n",
"N = 2**n_qubits\n",
"optimal_iterations = int(np.pi / 4 * np.sqrt(N)) # Since there's 1 solution, otherwise use N/M, where M is the number of solutions\n"
]
],
"outputs": [],
"execution_count": 67
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"metadata": {
"ExecuteTime": {
"end_time": "2025-10-09T19:30:00.362049Z",
"start_time": "2025-10-09T19:30:00.342055Z"
}
},
"source": [
"# Build Grover circuit\n",
"\n",
Expand All @@ -202,30 +257,50 @@
"qc = QuantumCircuit(qr, cr)\n",
"\n",
"# TODO: Initialize input qubits to superposition\n",
"\n",
"qc.h(range(n_qubits))\n",
"\n",
"# TODO: Initialize phase qubit to |-⟩ for phase kickback\n",
"\n",
"qc.x(total_qubits-1)\n",
"qc.h(total_qubits-1)\n",
"qc.barrier()\n",
"\n",
"# Grover iterations\n",
"oracle = create_oracle(n_qubits, target_hash)\n",
"diffusion = create_diffusion(n_qubits)\n",
"\n",
"for _ in range(optimal_iterations):\n",
" # TODO: Apply oracle and diffusion\n",
" pass\n",
" qc.append(oracle, range(total_qubits))\n",
" qc.barrier()\n",
" qc.append(diffusion, range(n_qubits))\n",
" qc.barrier()\n",
"\n",
"# TODO: Measure input qubits\n",
"\n",
"# TODO: Measure input qubits\n",
"qc.measure(range(n_qubits), range(n_qubits))\n",
"\n"
]
],
"outputs": [
{
"data": {
"text/plain": [
"<qiskit.circuit.instructionset.InstructionSet at 0x1ac248da110>"
]
},
"execution_count": 79,
"metadata": {},
"output_type": "execute_result"
}
],
"execution_count": 79
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"metadata": {
"ExecuteTime": {
"end_time": "2025-10-09T19:30:00.963626Z",
"start_time": "2025-10-09T19:30:00.787853Z"
}
},
"source": [
"# Simulate the circuit\n",
"simulator = AerSimulator()\n",
Expand All @@ -250,7 +325,33 @@
"print(\n",
" f\"\\nMost likely password guess: {found_password} (h({found_password})={toy_hash(found_password)})\"\n",
")\n"
]
],
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
" 000 (password=0): 1850 shots ( 45.2%) - h(0)=5 ✓✓✓\n",
" 111 (password=7): 339 shots ( 8.3%) - h(7)=2 \n",
" 010 (password=2): 339 shots ( 8.3%) - h(2)=3 \n",
" 011 (password=3): 336 shots ( 8.2%) - h(3)=6 \n",
" 100 (password=4): 333 shots ( 8.1%) - h(4)=1 \n",
" 110 (password=6): 307 shots ( 7.5%) - h(6)=7 \n",
" 001 (password=1): 301 shots ( 7.3%) - h(1)=0 \n",
" 101 (password=5): 291 shots ( 7.1%) - h(5)=4 \n",
"\n",
"Most likely password guess: 0 (h(0)=5)\n"
]
}
],
"execution_count": 80
},
{
"metadata": {},
"cell_type": "code",
"outputs": [],
"execution_count": null,
"source": ""
}
],
"metadata": {
Expand Down
Loading