from pyquil.quil import Program
from pyquil.gates import H, CNOT
from pyquil.api import QVMConnection

# Создаём объект для квантовой программы.
p = Program()

# "Мутирование" квантовой программы под действием оператора `H(0)` - гейт Адамара, который
# действует на нулевой кубит.
p.inst(H(0))
# Добавление CNOT гейта, который использует нулевой кубит как управляющий, а первый кубит как таргет.
p.inst(CNOT(0, 1))

# Объект "квантовой виртуальной машины" - создаёт соединение с облачным бэкэндом.
qvm = QVMConnection()

# Запускаем квантовую программу `p` на бэкэнде.
result = qvm.wavefunction(p)

# Посмотреть на результаты программы в виде "{'00': p1, '01': p2, ...}", где p1, p2, ... - вероятности
# соответствующих исходов.
print(result.get_outcome_probs())

# Построим гистограмму исходов.
result.plot()

# Ещё один способ представить состояние.
result.pretty_print()

import numpy as np
from pyquil.gates import I

quantum_simulator = QVMConnection()

# Создаём квантовую программу, которая использует один кубит и применяет к нему тождественное преобразование.
p = Program(I(0))

# После запуска программы на квантовом симуляторе вернётся состояние системы после применения к
# кубитам всех операторов из `p`.
wavefunction = quantum_simulator.wavefunction(p)

# `wavefunction` содержит в себе список амплитуд каждого из базисных состояний.
print(wavefunction.amplitudes)

alpha, beta = wavefunction

print("Соответственно наш кубит в состоянии: alpha={}, beta={}".format(alpha, beta))
print("Вероятность исхода 0: {}".format(abs(alpha)**2))
print("Вероятность исхода 1: {}".format(abs(beta)**2))

from pyquil.gates import X

# Пишем программу, которая состоит из одного оператора - NOT.
p = Program(X(0))

wavefunc = quantum_simulator.wavefunction(p)
alpha, beta = wavefunc

print("Волновая функция после применения X к |0>: {}".format(wavefunc))

# Посмотреть на вероятности исходов.
print(wavefunc.get_outcome_probs())

from pyquil.gates import CNOT

# Соответствует применению CNOT к состоянию |00>.
p = Program(CNOT(0, 1))
wavefunction = quantum_simulator.wavefunction(p)
print("CNOT|00> =", wavefunction)

# Соответствует применению CNOT к состоянию |01>.
p = Program(X(0), CNOT(0, 1))
wavefunction = quantum_simulator.wavefunction(p)
print("CNOT|01> =", wavefunction)

# Соответствует применению CNOT к состоянию |10>.
p = Program(X(1), CNOT(0, 1))
wavefunction = quantum_simulator.wavefunction(p)
print("CNOT|10> =", wavefunction)

# Соответствует применению CNOT к состоянию |11>.
p = Program(X(0), X(1), CNOT(0, 1))
wavefunction = quantum_simulator.wavefunction(p)
print("CNOT|11> =", wavefunction)

from pyquil.gates import SWAP

# Соответствует применению SWAP к состоянию |00>.
p = Program(SWAP(0, 1))
wavefunction = quantum_simulator.wavefunction(p)
print("SWAP|00> = ", wavefunction)

# Соответствует применению SWAP к состоянию |01>.
p = Program(X(0), SWAP(0, 1))
wavefunction = quantum_simulator.wavefunction(p)
print("SWAP|01> = ", wavefunction)

# Соответствует применению SWAP к состоянию |10>.
p = Program(X(1), SWAP(0, 1))
wavefunction = quantum_simulator.wavefunction(p)
print("SWAP|10> = ", wavefunction)

# Соответствует применению SWAP к состоянию |11>.
p = Program(X(0), X(1), SWAP(0, 1))
wavefunction = quantum_simulator.wavefunction(p)
print("SWAP|11> = ", wavefunction)

# Указываем индексы классических регистров для записи.
classical_reg_index_0 = 0
classical_reg_index_1 = 1

# Указываем индексы квантовых регистров для измерения.
quantum_reg_index_0 = 0
quantum_reg_index_1 = 1

# Создаём простую программу, результаты измерений записываем в соответствующие регистры.
p = Program(I(0), X(1))

p.measure(quantum_reg_index_0, classical_reg_index_0)
p.measure(quantum_reg_index_1, classical_reg_index_1)

# Указываем классические регистры, состояние которых нам интересно после завершения программы.
classical_regs = [0, 1]

# Запускаем программу на квантовом симуляторе.
print(quantum_simulator.run(p, classical_regs, trials=4))

from pyquil.gates import H

program = Program(H(0))
wavefunction = quantum_simulator.wavefunction(program)

print("H|0> = ", wavefunction)
print("Вероятности исходов:", wavefunction.get_outcome_probs())

# Запустим на симуляторе.
program = Program(H(0)).measure(0, 0)
result = quantum_simulator.run(program, [0], trials=10)
print(result)

print(program)

# DON'T HAVE ACCESS TO 19Q CHIP :(

# from pyquil.api import get_devices
# for device in get_devices():
#     if device.is_online():
#         print('Device {} is online'.format(device.name))

# DON'T HAVE ACCESS TO 19Q CHIP :(

# from pyquil.quil import Program
# import pyquil.api as api
# from pyquil.gates import *
# qpu = api.QPUConnection('19Q-Acorn')
# p = Program(H(0), CNOT(0, 1), MEASURE(0, 0), MEASURE(1, 1))
# qpu.run_and_measure(p, [0, 1], 1000)

# Первая цепь.
program = Program()

result = qvm.wavefunction(program)
result.plot()

program.measure(0, 0)
result = quantum_simulator.run(program, classical_addresses=[0], trials=10)
print(result)

# Вторая цепь.
program = Program(X(0))

result = qvm.wavefunction(program)
result.plot()

program.measure(0, 0)
result = quantum_simulator.run(program, classical_addresses=[0], trials=10)
print(result)

# Третья цепь.
program = Program(H(0))

result = qvm.wavefunction(program)
result.plot()

program.measure(0, 0)
result = quantum_simulator.run(program, classical_addresses=[0], trials=10)
print(result)

# Четвёртая цепь.
#                 управляющий------v  v----таргет
program = Program(X(0), X(1), CNOT(0, 1))

result = qvm.wavefunction(program)
result.plot()

program.measure(0, 0).measure(1, 1)
result = quantum_simulator.run(program, classical_addresses=[0, 1], trials=10)
print(result)

# Пятая цепь.
#                 управляющий------v  v----таргет
program = Program(H(0), X(1), CNOT(0, 1))

result = qvm.wavefunction(program)
result.plot()

program.measure(0, 0).measure(1, 1)
result = quantum_simulator.run(program, classical_addresses=[0, 1], trials=10)
print(result)