Pular para o conteúdo principal

Construir circuitos

Versões dos pacotes

O código desta página foi desenvolvido usando os seguintes requisitos. Recomendamos usar essas versões ou mais recentes.

qiskit[all]~=2.4.0

Esta página examina mais de perto a classe QuantumCircuit do SDK Qiskit, incluindo alguns métodos mais avançados que você pode usar para criar circuitos quânticos.

O que é um circuito quântico?

Um circuito quântico simples é uma coleção de qubits e uma lista de instruções que atuam sobre esses qubits. Para demonstrar, a célula a seguir cria um novo circuito com dois novos qubits e, em seguida, exibe o atributo qubits do circuito, que é uma lista de qubits em ordem do bit menos significativo q0q_0 ao bit mais significativo qnq_n.

# Added by doQumentation — required packages for this notebook
!pip install -q qiskit
from qiskit import QuantumCircuit

qc = QuantumCircuit(2)
qc.qubits
[<Qubit register=(2, "q"), index=0>, <Qubit register=(2, "q"), index=1>]

Múltiplos objetos QuantumRegister e ClassicalRegister podem ser combinados para criar um circuito. Todo QuantumRegister e ClassicalRegister também pode ter um nome.

from qiskit.circuit import QuantumRegister, ClassicalRegister

qr1 = QuantumRegister(2, "qreg1") # Create a QuantumRegister with 2 qubits
qr2 = QuantumRegister(1, "qreg2") # Create a QuantumRegister with 1 qubit
cr1 = ClassicalRegister(3, "creg1") # Create a ClassicalRegister with 3 cbits

combined_circ = QuantumCircuit(
qr1, qr2, cr1
) # Create a quantum circuit with 2 QuantumRegisters and 1 ClassicalRegister
combined_circ.qubits
[<Qubit register=(2, "qreg1"), index=0>,
<Qubit register=(2, "qreg1"), index=1>,
<Qubit register=(1, "qreg2"), index=0>]

Você pode encontrar o índice e o registrador de um qubit usando o método find_bit do circuito e seus atributos.

desired_qubit = qr2[0] # Qubit 0 of register 'qreg2'

print("Index:", combined_circ.find_bit(desired_qubit).index)
print("Register:", combined_circ.find_bit(desired_qubit).registers)
Index: 2
Register: [(QuantumRegister(1, 'qreg2'), 0)]

Adicionar uma instrução ao circuito acrescenta a instrução ao atributo data do circuito. A saída da célula a seguir mostra que data é uma lista de objetos CircuitInstruction, cada um dos quais possui um atributo operation e um atributo qubits.

qc.x(0) # Add X-gate to qubit 0
qc.data
[CircuitInstruction(operation=Instruction(name='x', num_qubits=1, num_clbits=0, params=[]), qubits=(<Qubit register=(2, "q"), index=0>,), clbits=())]

A maneira mais fácil de visualizar essas informações é por meio do método draw, que retorna uma visualização de um circuito. Veja Visualizar circuitos para diferentes formas de exibir circuitos quânticos.

qc.draw("mpl")

Output of the previous code cell

Os objetos de instrução de circuito podem conter circuitos de "definição" que descrevem a instrução em termos de instruções mais fundamentais. Por exemplo, o X-gate é definido como um caso específico do U3-gate, uma porta de qubit único mais geral.

# Draw definition circuit of 0th instruction in `qc`
qc.data[0].operation.definition.draw("mpl")

Output of the previous code cell

Instruções e circuitos são semelhantes no sentido de que ambos descrevem operações sobre bits e qubits, mas têm propósitos diferentes:

  • As instruções são tratadas como fixas, e seus métodos geralmente retornam novas instruções (sem modificar o objeto original).
  • Os circuitos são projetados para serem construídos ao longo de muitas linhas de código, e os métodos de QuantumCircuit frequentemente modificam o objeto existente.

O que é a profundidade de um circuito?

A depth() de um circuito quântico é uma medida do número de "camadas" de portas quânticas, executadas em paralelo, necessárias para completar o cálculo definido pelo circuito. Como as portas quânticas levam tempo para serem implementadas, a profundidade de um circuito corresponde aproximadamente ao tempo que o computador quântico leva para executar o circuito. Portanto, a profundidade de um circuito é uma quantidade importante usada para medir se um circuito quântico pode ser executado em um dispositivo.

O restante desta página ilustra como manipular circuitos quânticos.

Construir circuitos

Métodos como QuantumCircuit.h e QuantumCircuit.cx adicionam instruções específicas aos circuitos. Para adicionar instruções a um circuito de forma mais geral, use o método append. Esse método recebe uma instrução e uma lista de qubits aos quais aplicar a instrução. Veja a documentação da API da Biblioteca de Circuitos para uma lista de instruções suportadas.

from qiskit.circuit.library import HGate

qc = QuantumCircuit(1)
qc.append(
HGate(), # New HGate instruction
[0], # Apply to qubit 0
)
qc.draw("mpl")

Output of the previous code cell

Para combinar dois circuitos, use o método compose. Esse método aceita outro QuantumCircuit e uma lista opcional de mapeamentos de qubits.

nota

O método compose retorna um novo circuito e não modifica nenhum dos circuitos sobre os quais atua. Para modificar o circuito no qual você está chamando o método compose, use o argumento inplace=True.

qc_a = QuantumCircuit(4)
qc_a.x(0)

qc_b = QuantumCircuit(2, name="qc_b")
qc_b.y(0)
qc_b.z(1)

# compose qubits (0, 1) of qc_a to qubits (1, 3) of qc_b respectively
combined = qc_a.compose(qc_b, qubits=[1, 3])
combined.draw("mpl")

Output of the previous code cell

Você também pode querer compilar circuitos em instruções para manter seus circuitos organizados. É possível converter um circuito em uma instrução usando o método to_instruction, e então acrescentar essa instrução a outro circuito como faria com qualquer outra instrução. O circuito desenhado na célula a seguir é funcionalmente equivalente ao circuito desenhado na célula anterior.

inst = qc_b.to_instruction()
qc_a.append(inst, [1, 3])
qc_a.draw("mpl")

Output of the previous code cell

Se o seu circuito for unitário, você pode convertê-lo em um Gate usando o método to_gate. Os objetos Gate são tipos específicos de instruções que possuem alguns recursos extras, como o método control, que adiciona um controle quântico.

gate = qc_b.to_gate().control()
qc_a.append(gate, [0, 1, 3])
qc_a.draw("mpl")

Output of the previous code cell

Para ver o que está acontecendo, você pode usar o método decompose para expandir cada instrução em sua definição.

nota

O método decompose retorna um novo circuito e não modifica o circuito sobre o qual atua.

qc_a.decompose().draw("mpl")

Output of the previous code cell

Medir qubits

As medições são usadas para amostrar os estados de qubits individuais e transferir os resultados para um registrador clássico. Note que, se você estiver submetendo circuitos a um primitivo Sampler, as medições são obrigatórias. Porém, circuitos submetidos a um primitivo Estimator não devem conter medições.

Os qubits podem ser medidos usando três métodos: measure, measure_all e measure_active. Para aprender a visualizar resultados de medições, veja a página Visualizar resultados.

  1. QuantumCircuit.measure : mede cada qubit do primeiro argumento no bit clássico fornecido como segundo argumento. Esse método permite controle total sobre onde o resultado da medição é armazenado.

  2. QuantumCircuit.measure_all : não recebe argumento e pode ser usado para circuitos quânticos sem bits clássicos pré-definidos. Cria fios clássicos e armazena os resultados das medições em ordem. Por exemplo, a medição do qubit qiq_i é armazenada no cbit measimeas_i). Também adiciona uma barreira antes da medição.

  3. QuantumCircuit.measure_active : semelhante a measure_all, mas mede apenas os qubits que possuem operações.

qc1 = QuantumCircuit(2, 2)
qc1.measure(0, 1)
qc1.draw("mpl", cregbundle=False)

Output of the previous code cell

qc2 = QuantumCircuit(2)
qc2.measure_all()
qc2.draw("mpl", cregbundle=False)

Output of the previous code cell

qc3 = QuantumCircuit(2)
qc3.x(1)
qc3.measure_active()
qc3.draw("mpl", cregbundle=False)

Output of the previous code cell

Circuitos parametrizados

Muitos algoritmos quânticos de curto prazo envolvem a execução de muitas variações de um circuito quântico. Como construir e otimizar circuitos grandes pode ser computacionalmente caro, o Qiskit suporta circuitos parametrizados. Esses circuitos possuem parâmetros indefinidos, e seus valores não precisam ser definidos até pouco antes da execução do circuito. Isso permite que você mova a construção e otimização do circuito para fora do laço principal do programa. A célula a seguir cria e exibe um circuito parametrizado.

from qiskit.transpiler import generate_preset_pass_manager
from qiskit.circuit import Parameter

angle = Parameter("angle") # undefined number

# Create and optimize circuit once
qc = QuantumCircuit(1)
qc.rx(angle, 0)
qc = generate_preset_pass_manager(
optimization_level=3, basis_gates=["u", "cx"]
).run(qc)

qc.draw("mpl")

Output of the previous code cell

A célula a seguir cria muitas variações desse circuito e exibe uma das variações.

circuits = []
for value in range(100):
circuits.append(qc.assign_parameters({angle: value}))

circuits[0].draw("mpl")

Output of the previous code cell

Você pode encontrar uma lista dos parâmetros indefinidos de um circuito em seu atributo parameters.

qc.parameters
ParameterView([Parameter(angle)])

Alterar o nome de um parâmetro

Por padrão, os nomes dos parâmetros de um circuito parametrizado são prefixados por x — por exemplo, x[0]. Você pode alterar os nomes após eles serem definidos, conforme mostrado no exemplo a seguir.

from qiskit.circuit.library import z_feature_map
from qiskit.circuit import ParameterVector

# Define a parameterized circuit with default names
# For example, x[0]
circuit = z_feature_map(2)

# Set new parameter names
# They will now be prefixed by `hi` instead
# For example, hi[0]
training_params = ParameterVector("hi", 2)

# Assign parameter names to the quantum circuit
circuit = circuit.assign_parameters(parameters=training_params)

Próximos passos

Recomendações