Spaces:
Runtime error
Runtime error
import numpy as np | |
from qiskit import BasicAer, QuantumCircuit, QuantumRegister, ClassicalRegister, execute | |
from qiskit import IBMQ | |
# provider = IBMQ.load_account() | |
# def misere_step(ones,piles): | |
# # even number of piles of 1 eg (1,1,3,0) or (0,0,3,0) | |
# if ones%2 == 0: | |
# objects_to_remove = [] | |
# removable_amount = 1 | |
# for i in range(len(piles)): | |
# if piles[i] > 1: | |
# objects_to_remove.append(piles[i]-1) | |
# else: | |
# objects_to_remove.append(0) | |
# # odd number of piles of 1 eg (1,1,3,1) | |
# else: | |
# objects_to_remove = [] | |
# removable_amount = 1 | |
# for i in range(len(piles)): | |
# if piles[i] > 1: | |
# objects_to_remove.append(piles[i]) | |
# else: | |
# objects_to_remove.append(0) | |
# return objects_to_remove, removable_amount | |
def get_piles_to_remove(piles): | |
nim_sum = 0 | |
for p in piles: | |
nim_sum = nim_sum ^ p | |
objects_to_remove = [] | |
removable_amount = 0 | |
for p in piles: | |
new_p = p^nim_sum | |
if new_p < p: | |
objects_to_remove.append(p-new_p) | |
removable_amount = removable_amount + 1 | |
else: | |
objects_to_remove.append(0) | |
return objects_to_remove, removable_amount | |
def custom_qft(data_qubits): | |
qr_data = QuantumRegister(data_qubits) | |
qc = QuantumCircuit(qr_data) | |
i = data_qubits | |
while i>=1: | |
n = i - 1 | |
qc.h(qr_data[n]) | |
for qubit in range(n): | |
qc.cp(np.pi/2**(n-qubit), qr_data[qubit], qr_data[n]) | |
i = i-1 | |
return qc | |
def subroutine_add_const(data_qubits: int, const: int, to_gate=True): | |
qc = QuantumCircuit(data_qubits) | |
for i in range(data_qubits): | |
angle = const*np.pi/(2**i) | |
qc.p(angle,i) | |
return qc.to_gate(label=" ["+str(const)+"] ") if to_gate else qc | |
def diffusion_operation(qc, address, flag, removable_pile): | |
def nim_oracle(qc,address,flag,removable_pile): | |
# 0001 -> 001 | |
if removable_pile[0] != 0: | |
qc.x(address[1]) | |
qc.x(address[2]) | |
qc.mct(address[:],flag) | |
qc.x(address[2]) | |
qc.x(address[1]) | |
# 0010 -> 010 | |
if removable_pile[1] != 0: | |
qc.x(address[0]) | |
qc.x(address[2]) | |
qc.mct(address[:],flag) | |
qc.x(address[2]) | |
qc.x(address[0]) | |
# 0100 -> 011 | |
if removable_pile[2] != 0: | |
qc.x(address[2]) | |
qc.mct(address[:],flag) | |
qc.x(address[2]) | |
# 1000 -> 100 | |
if removable_pile[3] != 0: | |
qc.x(address[0]) | |
qc.x(address[1]) | |
qc.mct(address[:],flag) | |
qc.x(address[1]) | |
qc.x(address[0]) | |
qc.x(flag) | |
qc.h(flag) | |
qc.h(address[:]) | |
nim_oracle(qc,address,flag,removable_pile) | |
qc.h(address[:]) | |
qc.x(address[:]) | |
qc.h(address[2]) | |
qc.mct(address[0:2], address[2]) | |
qc.h(address[2]) | |
qc.x(address[:]) | |
qc.h(address[:]) | |
def qc_process(qc,objects_to_remove,address,flag,piles,removable_pile,removable_count): | |
if removable_count == 0: | |
for i in range(len(removable_pile)): | |
if piles[i] > 0: | |
removable_pile[i] = 1 | |
removable_count += 1 | |
if removable_count == 4: | |
removable_pile[removable_pile.index(min(removable_pile))] = 0 | |
removable_count = removable_count - 1 | |
qft_gate = custom_qft(3).to_gate() | |
inverse_qft_gate = custom_qft(3).inverse().to_gate() | |
if removable_count == 1: | |
qc.swap(objects_to_remove[0],objects_to_remove[2]) | |
qc.append(qft_gate,objects_to_remove[:]) | |
# 0001 -> 001 | |
if removable_pile[0] != 0: | |
add_gate = subroutine_add_const(3,removable_pile[0]) | |
qc.x(address[0]) | |
# 0010 -> 010 | |
elif removable_pile[1] != 0: | |
add_gate = subroutine_add_const(3,removable_pile[1]) | |
qc.x(address[1]) | |
# 0100 -> 011 | |
elif removable_pile[2] != 0: | |
add_gate = subroutine_add_const(3,removable_pile[2]) | |
qc.x(address[0]) | |
qc.x(address[1]) | |
# 1000 -> 100 | |
elif removable_pile[3] != 0: | |
add_gate = subroutine_add_const(3,removable_pile[3]) | |
qc.x(address[2]) | |
qc.append(add_gate,objects_to_remove[:]) | |
qc.append(inverse_qft_gate,objects_to_remove[:]) | |
qc.swap(objects_to_remove[0],objects_to_remove[2]) | |
else: | |
diffusion_operation(qc,address, flag, removable_pile) | |
qc.swap(objects_to_remove[0],objects_to_remove[2]) | |
qc.append(qft_gate,objects_to_remove[:]) | |
for i,remove_amount in enumerate(removable_pile): | |
if remove_amount != 0: | |
bin_i = list(bin(i+1)[2:]) | |
while len(bin_i) != 3: | |
bin_i.insert(0,'0') | |
bin_i = bin_i[::-1] | |
for j in range(len(bin_i)): | |
if bin_i[j] == '0': | |
qc.x(address[j]) | |
controlled_add_gate = subroutine_add_const(3,remove_amount).control(3) | |
qc.append(controlled_add_gate,address[:]+objects_to_remove[:]) | |
for j in range(len(bin_i)): | |
if bin_i[j] == '0': | |
qc.x(address[j]) | |
qc.append(inverse_qft_gate,objects_to_remove[:]) | |
qc.swap(objects_to_remove[0],objects_to_remove[2]) | |
def get_quantum_move(piles, backend=None): | |
# REMOVE MISERE STEP | |
# ones = piles.count(1) | |
# zeros = piles.count(0) | |
# non_zeros = 4 - (ones+zeros) | |
# # all zeros except one eg (0,0,0,7) OR some zeros some ones some non_zeros | |
# # leave odd piles of 1s | |
# if non_zeros == 1: | |
# removable_pile, removable_count = misere_step(ones, piles) | |
# else: | |
# removable_pile, removable_count = get_piles_to_remove(piles) | |
removable_pile, removable_count = get_piles_to_remove(piles) | |
objects_to_remove = QuantumRegister(3,'piles') | |
flag = QuantumRegister(1,'flag') | |
output_piles = ClassicalRegister(3,'final_piles') | |
address = QuantumRegister(3,'address') | |
pick_pile = ClassicalRegister(3,'choose_pile') | |
qc = QuantumCircuit(objects_to_remove,address,flag,output_piles,pick_pile) | |
qc_process(qc,objects_to_remove,address,flag,piles,removable_pile,removable_count) | |
qc.measure(address[:],pick_pile[:]) | |
qc.measure(objects_to_remove[:],output_piles[:]) | |
if backend == None: | |
backend = BasicAer.get_backend('qasm_simulator') | |
# backend = provider.backends.ibmq_qasm_simulator | |
job = execute(qc,backend,shots=500) | |
result = job.result() | |
counts = result.get_counts() | |
try: | |
qc_move = (counts.most_frequent()) | |
except Exception as e: | |
print(e) | |
vals = list(dict(counts).values()) | |
max_count = max(vals,key=vals.count) | |
for key in counts: | |
if counts[key] == max_count: | |
qc_move = key | |
break | |
board_choice = qc_move.split(' ')[0] | |
board_choice = int(board_choice,2) - 1 | |
print("Pick from:",board_choice+1) | |
board_state = qc_move.split(' ')[1] | |
board_state = board_state[::-1] | |
amount = int(board_state,2) | |
print("Amount:", amount) | |
return board_choice,amount | |