# 30 qubits = 16 GB
num_qubits = 32
memoryneeded_GB = 16 * 2 ** (num_qubits - 30)
print(memoryneeded_GB)

# qiskit imports
from qiskit import Aer
from qiskit import IBMQ, execute
from qiskit import QuantumCircuit, QuantumRegister, ClassicalRegister
from qiskit.tools.visualization import plot_histogram, plot_state_city
from qiskit.tools.monitor import job_monitor

nqubits = 3
qr = QuantumRegister(nqubits, 'q')
cr = ClassicalRegister(nqubits, 'c')
GHZ3_circuit = QuantumCircuit(qr, cr)
GHZ3_circuit.h(qr[0])
GHZ3_circuit.cx(qr[0], qr[1])
GHZ3_circuit.cx(qr[1], qr[2])
GHZ3_circuit.barrier(qr)
GHZ3_circuit.measure(qr, cr)

GHZ3_circuit.draw(output = 'mpl')

# Select the QasmSimulator from the Aer provider
simulator = Aer.get_backend('qasm_simulator')

# Execute and get counts
result = execute(GHZ3_circuit, simulator, shots=2048).result()
counts = result.get_counts(GHZ3_circuit)
print(counts)
plot_histogram(counts, title='Ideal counts for 3-qubit GHZ state')

# Execute and get memory
result = execute(GHZ3_circuit, simulator, shots=10, memory=True).result()
memory = result.get_memory(GHZ3_circuit)
print(memory)

IBMQ.load_accounts()
IBMQ.backends()

device = IBMQ.get_backend('ibmq_16_melbourne')

job_device = execute(GHZ3_circuit, backend=device, shots=1024)
job_monitor(job_device)

result_device = job_device.result(timeout = 1800)         
counts_device = result_device.get_counts(GHZ3_circuit)
plot_histogram(counts_device, title="Counts for 3-qubit GHZ state with a real device")

from qiskit.providers.aer import noise

device = IBMQ.get_backend('ibmq_16_melbourne')
properties = device.properties()
coupling_map = device.configuration().coupling_map

print(properties)

for qubit in properties.qubits:
    print(qubit)
    print('\n'.join(qubitprop.name + "=" + str(qubitprop.value) + " " + qubitprop.unit for qubitprop in qubit))
    print()

for gate in properties.gates:
    print('Gate = {}, Relevant Qubits = {}, Error = {}'.format(
                gate.gate, 
                gate.qubits, 
                gate.parameters[0].value, 
                gate.parameters[0].unit
    ))

print(coupling_map)

import graphviz
coupling_graph = graphviz.Graph()
for edge in coupling_map:
    coupling_graph.edge('q'+str(edge[0]), 'q'+str(edge[1]))
coupling_graph

# List of gate times for ibmq_16_melbourne device
# Note that the None parameter for u1, u2, u3 is because gate
# times are the same for all qubits
# Erick Winston
gate_times = [
    ('u1', None, 0), ('u2', None, 100), ('u3', None, 200),
    ('cx', [1, 0], 678), ('cx', [1, 2], 547), ('cx', [2, 3], 721),
    ('cx', [4, 3], 733), ('cx', [4, 10], 721), ('cx', [5, 4], 800),
    ('cx', [5, 6], 800), ('cx', [5, 9], 895), ('cx', [6, 8], 895),
    ('cx', [7, 8], 640), ('cx', [9, 8], 895), ('cx', [9, 10], 800),
    ('cx', [11, 10], 721), ('cx', [11, 3], 634), ('cx', [12, 2], 773),
    ('cx', [13, 1], 2286), ('cx', [13, 12], 1504), ('cx', [], 800)
]

# Construct the noise model from backend properties
# and custom gate times
#noise_model = noise.device.basic_device_noise_model(properties, gate_times=gate_times)
noise_model = noise.device.basic_device_noise_model(properties, gate_times = gate_times, temperature=10)
print(noise_model)

help(noise.device.basic_device_noise_model)

# Get the basis gates for the noise model
basis_gates = noise_model.basis_gates

# Select the QasmSimulator from the Aer provider
simulator = Aer.get_backend('qasm_simulator')

# Execute noisy simulation and get corunts
job_noise = execute(GHZ3_circuit, simulator, 
                       noise_model=noise_model,
                       coupling_map=coupling_map,
                       basis_gates=basis_gates)
result_noise = job_noise.result()
counts_noise = result_noise.get_counts(GHZ3_circuit)
plot_histogram(counts_noise, title="Counts for 3-qubit GHZ state with a simulation containing depolarizing noise model")

nqubits = 3
qr = QuantumRegister(nqubits, 'q')
cr = ClassicalRegister(nqubits, 'c')
GHZ3_circuit = QuantumCircuit(qr, cr)
GHZ3_circuit.h(qr[0])
GHZ3_circuit.cx(qr[0], qr[1])
GHZ3_circuit.cx(qr[1], qr[2])
GHZ3_circuit.barrier(qr)
#GHZ3_circuit.measure(qr, cr)

GHZ3_circuit.draw(output = 'mpl')

simulator = Aer.get_backend('statevector_simulator')

# Execute and get counts
result = execute(GHZ3_circuit, simulator).result()
finalstate = result.get_statevector(GHZ3_circuit)

print(finalstate)
print()
for statecoeff, statenum in zip(finalstate, range(2**len(qr))):
    print("({}) * |{}>".format(statecoeff,  statenum))
plot_state_city(finalstate, title='3-qubit GHZ state')

nqubits = 2
qr = QuantumRegister(nqubits, 'q')
cz1 = QuantumCircuit(qr)
cz1.h(qr[1])
cz1.cx(qr[0], qr[1])
cz1.h(qr[1])

cz1.draw(output = 'mpl')

# Select the UnitarySimulator from the Aer provider
simulator = Aer.get_backend('unitary_simulator')

# Execute and get counts
job_sim = execute(cz1, simulator)
result = job_sim.result()
unitary = result.get_unitary(cz1)
print("Final Unitary:")
print(unitary)

nqubits = 2
qr = QuantumRegister(nqubits, 'q')
cz2 = QuantumCircuit(qr)
cz2.cz(qr[0], qr[1])

cz2.draw(output = 'mpl')

# Select the UnitarySimulator from the Aer provider
simulator = Aer.get_backend('unitary_simulator')

# Execute and get counts
job_sim = execute(cz2, simulator)
result = job_sim.result()
unitary = result.get_unitary(cz2)
print("Final Unitary:")
print(unitary)

