As traduções são geradas por tradução automática. Em caso de conflito entre o conteúdo da tradução e da versão original em inglês, a versão em inglês prevalecerá.
Construir circuitos no SDK
Esta seção fornece exemplos de definição de um circuito, visualização das portas disponíveis, extensão de um circuito e visualização das portas compatíveis por cada dispositivo. Ele também contém instruções sobre como alocar manualmente o qubits, instruir o compilador a executar seus circuitos exatamente conforme definido e criar circuitos ruidosos com um simulador de ruído.
Você também pode trabalhar no nível de pulso no Braket para várias portas, com certeza. QPUs Para obter mais informações, consulte Controle de pulso no Amazon Braket.
Nesta seção:
Portas e circuitos
As portas e circuitos quânticos são definidos na classe braket.circuitsCircuit().
Exemplo: definir um circuito
O exemplo começa definindo um circuito de amostra de quatro qubits (q0, q1, q2 e q3 rotulados) consistindo em portas Hadamard padrão de um qubit e portas CNOT de dois qubits. Você pode visualizar esse circuito chamando a função print, conforme mostra o exemplo a seguir.
# Import the circuit module from braket.circuits import Circuit # Define circuit with 4 qubits my_circuit = Circuit().h(range(4)).cnot(control=0, target=2).cnot(control=1, target=3) print(my_circuit)
T : │ 0 │ 1 │ ┌───┐ q0 : ─┤ H ├───●───────── └───┘ │ ┌───┐ │ q1 : ─┤ H ├───┼─────●─── └───┘ │ │ ┌───┐ ┌─┴─┐ │ q2 : ─┤ H ├─┤ X ├───┼─── └───┘ └───┘ │ ┌───┐ ┌─┴─┐ q3 : ─┤ H ├───────┤ X ├─ └───┘ └───┘ T : │ 0 │ 1 │
Exemplo: definir um circuito parametrizado
Neste exemplo, definimos um circuito com portas que dependem de parâmetros livres. Podemos especificar os valores desses parâmetros para criar um novo circuito ou, ao enviar o circuito, para ser executado como uma tarefa quântica em determinados dispositivos.
from braket.circuits import Circuit, FreeParameter # Define a FreeParameter to represent the angle of a gate alpha = FreeParameter("alpha") # Define a circuit with three qubits my_circuit = Circuit().h(range(3)).cnot(control=0, target=2).rx(0, alpha).rx(1, alpha) print(my_circuit)
Você pode criar um novo circuito não parametrizado a partir de um parametrizado fornecendo um único float (que é o valor que todos os parâmetros livres terão) ou argumentos de palavra-chave especificando o valor de cada parâmetro para o circuito da seguinte forma.
my_fixed_circuit = my_circuit(1.2) my_fixed_circuit = my_circuit(alpha=1.2) print(my_fixed_circuit)
Observe que my_circuit não foi modificado, então você pode usá-lo para instanciar muitos novos circuitos com valores de parâmetros fixos.
Exemplo: Modificar portas em um circuito
O exemplo a seguir define um circuito com portas que usam modificadores de controle e potência. Você pode usar essas modificações para criar novas portas, como a porta Ry controlada.
from braket.circuits import Circuit # Create a bell circuit with a controlled x gate my_circuit = Circuit().h(0).x(control=0, target=1) # Add a multi-controlled Ry gate of angle .13 my_circuit.ry(angle=.13, target=2, control=(0, 1)) # Add a 1/5 root of X gate my_circuit.x(0, power=1/5) print(my_circuit)
Os modificadores de porta são suportados somente no simulador local.
Exemplo: Veja todos as postas disponíveis
O exemplo a seguir mostra como analisar todas as portas disponíveis no Amazon Braket.
from braket.circuits import Gate # Print all available gates in Amazon Braket gate_set = [attr for attr in dir(Gate) if attr[0].isupper()] print(gate_set)
A saída desse código lista todas as portas.
['CCNot', 'CNot', 'CPhaseShift', 'CPhaseShift00', 'CPhaseShift01', 'CPhaseShift10', 'CSwap', 'CV', 'CY', 'CZ', 'ECR', 'GPhase', 'GPi', 'GPi2', 'H', 'I', 'ISwap', 'MS', 'PRx', 'PSwap', 'PhaseShift', 'PulseGate', 'Rx', 'Ry', 'Rz', 'S', 'Si', 'Swap', 'T', 'Ti', 'U', 'Unitary', 'V', 'Vi', 'X', 'XX', 'XY', 'Y', 'YY', 'Z', 'ZZ']
Qualquer uma dessas portas pode ser anexada a um circuito chamando o método para esse tipo de circuito. Por exemplo, chame circ.h(0) para adicionar uma porta Hadamard ao primeiro qubit.
nota
As portas são anexadas e o exemplo a seguir adiciona todas as portas listadas no exemplo anterior ao mesmo circuito.
circ = Circuit() # toffoli gate with q0, q1 the control qubits and q2 the target. circ.ccnot(0, 1, 2) # cnot gate circ.cnot(0, 1) # controlled-phase gate that phases the |11> state, cphaseshift(phi) = diag((1,1,1,exp(1j*phi))), where phi=0.15 in the examples below circ.cphaseshift(0, 1, 0.15) # controlled-phase gate that phases the |00> state, cphaseshift00(phi) = diag([exp(1j*phi),1,1,1]) circ.cphaseshift00(0, 1, 0.15) # controlled-phase gate that phases the |01> state, cphaseshift01(phi) = diag([1,exp(1j*phi),1,1]) circ.cphaseshift01(0, 1, 0.15) # controlled-phase gate that phases the |10> state, cphaseshift10(phi) = diag([1,1,exp(1j*phi),1]) circ.cphaseshift10(0, 1, 0.15) # controlled swap gate circ.cswap(0, 1, 2) # swap gate circ.swap(0,1) # phaseshift(phi)= diag([1,exp(1j*phi)]) circ.phaseshift(0,0.15) # controlled Y gate circ.cy(0, 1) # controlled phase gate circ.cz(0, 1) # Echoed cross-resonance gate applied to q0, q1 circ = Circuit().ecr(0,1) # X rotation with angle 0.15 circ.rx(0, 0.15) # Y rotation with angle 0.15 circ.ry(0, 0.15) # Z rotation with angle 0.15 circ.rz(0, 0.15) # Hadamard gates applied to q0, q1, q2 circ.h(range(3)) # identity gates applied to q0, q1, q2 circ.i([0, 1, 2]) # iswap gate, iswap = [[1,0,0,0],[0,0,1j,0],[0,1j,0,0],[0,0,0,1]] circ.iswap(0, 1) # pswap gate, PSWAP(phi) = [[1,0,0,0],[0,0,exp(1j*phi),0],[0,exp(1j*phi),0,0],[0,0,0,1]] circ.pswap(0, 1, 0.15) # X gate applied to q1, q2 circ.x([1, 2]) # Y gate applied to q1, q2 circ.y([1, 2]) # Z gate applied to q1, q2 circ.z([1, 2]) # S gate applied to q0, q1, q2 circ.s([0, 1, 2]) # conjugate transpose of S gate applied to q0, q1 circ.si([0, 1]) # T gate applied to q0, q1 circ.t([0, 1]) # conjugate transpose of T gate applied to q0, q1 circ.ti([0, 1]) # square root of not gate applied to q0, q1, q2 circ.v([0, 1, 2]) # conjugate transpose of square root of not gate applied to q0, q1, q2 circ.vi([0, 1, 2]) # exp(-iXX theta/2) circ.xx(0, 1, 0.15) # exp(i(XX+YY) theta/4), where theta=0.15 in the examples below circ.xy(0, 1, 0.15) # exp(-iYY theta/2) circ.yy(0, 1, 0.15) # exp(-iZZ theta/2) circ.zz(0, 1, 0.15) # IonQ native gate GPi with angle 0.15 applied to q0 circ.gpi(0, 0.15) # IonQ native gate GPi2 with angle 0.15 applied to q0 circ.gpi2(0, 0.15) # IonQ native gate MS with angles 0.15, 0.15, 0.15 applied to q0, q1 circ.ms(0, 1, 0.15, 0.15, 0.15)
Além do conjunto de portas predefinido, você também pode aplicar portas unitárias autodefinidas ao circuito. Elas podem ser portas de um único qubit (conforme mostrado no código-fonte a seguir) ou portas de vários qubits aplicadas ao qubits definido pelo parâmetro targets.
import numpy as np # Apply a general unitary my_unitary = np.array([[0, 1],[1, 0]]) circ.unitary(matrix=my_unitary, targets=[0])
Exemplo: Estenda os circuitos existentes
Você pode estender os circuitos existentes adicionando instruções. Um Instruction é uma diretiva quântica que descreve a tarefa quântica a ser executada em um dispositivo quântico. Operadores Instruction incluem somente objetos do tipo Gate.
# Import the Gate and Instruction modules from braket.circuits import Gate, Instruction # Add instructions directly. circ = Circuit([Instruction(Gate.H(), 4), Instruction(Gate.CNot(), [4, 5])]) # Or with add_instruction/add functions instr = Instruction(Gate.CNot(), [0, 1]) circ.add_instruction(instr) circ.add(instr) # Specify where the circuit is appended circ.add_instruction(instr, target=[3, 4]) circ.add_instruction(instr, target_mapping={0: 3, 1: 4}) # Print the instructions print(circ.instructions) # If there are multiple instructions, you can print them in a for loop for instr in circ.instructions: print(instr) # Instructions can be copied new_instr = instr.copy() # Appoint the instruction to target new_instr = instr.copy(target=[5, 6]) new_instr = instr.copy(target_mapping={0: 5, 1: 6})
Exemplo: veja as portas que cada dispositivo suporta
Os simuladores oferecem suporte a todas as portas no SDK do Braket, mas os dispositivos QPU oferecem suporte a um subconjunto menor. Você pode encontrar as portas compatíveis de um dispositivo nas propriedades do dispositivo. O exemplo a seguir mostra um exemplo de um dispositivo IonQ:
# Import the device module from braket.aws import AwsDevice device = AwsDevice("arn:aws:braket:us-east-1::device/qpu/ionq/Aria-1") # Get device name device_name = device.name # Show supportedQuantumOperations (supported gates for a device) device_operations = device.properties.dict()['action']['braket.ir.openqasm.program']['supportedOperations'] print('Quantum Gates supported by {}:\n {}'.format(device_name, device_operations))
Quantum Gates supported by Aria 1: ['x', 'y', 'z', 'h', 's', 'si', 't', 'ti', 'v', 'vi', 'rx', 'ry', 'rz', 'cnot', 'swap', 'xx', 'yy', 'zz']
As portas compatíveis talvez precisem ser compiladas em portas nativas antes de poderem ser executadas em hardware quântico. Quando você envia um circuito, o Amazon Braket executa essa compilação automaticamente.
Exemplo: recupere programaticamente a fidelidade das portas nativas compatíveis por um dispositivo
Você pode ver as informações de fidelidade na página Dispositivos do console Braket. Às vezes, é útil acessar as mesmas informações programaticamente. O código a seguir mostra como extrair a fidelidade de duas portas qubit entre duas portas de uma QPU.
# Import the device module from braket.aws import AwsDevice device = AwsDevice("arn:aws:braket:us-west-1::device/qpu/rigetti/Ankaa-3") # Specify the qubits a=10 b=11 edge_properties_entry = device.properties.standardized.twoQubitProperties['10-11'].twoQubitGateFidelity gate_name = edge_properties_entry[0].gateName fidelity = edge_properties_entry[0].fidelity print(f"Fidelity of the {gate_name} gate between qubits {a} and {b}: {fidelity}")
Conjuntos de programas
Os conjuntos de programas executam com eficiência vários circuitos quânticos em uma única tarefa quântica. Nessa tarefa, você pode enviar até 100 circuitos quânticos ou um único circuito paramétrico com até 100 conjuntos de parâmetros diferentes. Essa operação minimiza o tempo entre as execuções subsequentes do circuito e reduz a sobrecarga de processamento de tarefas quânticas. Atualmente, os conjuntos de programas são suportados no Amazon Local Simulator Braket e AQT em IQM dispositivos. Rigetti
Definindo um ProgramSet
O primeiro exemplo de código a seguir demonstra como construir um ProgramSet usando circuitos parametrizados e circuitos sem parâmetros.
from braket.aws import AwsDevice from braket.circuits import Circuit, FreeParameter from braket.program_sets.circuit_binding import CircuitBinding from braket.program_sets import ProgramSet # Initialize the quantum device device = AwsDevice("arn:aws:braket:eu-north-1::device/qpu/iqm/Garnet") # Define circuits circ1 = Circuit().h(0).cnot(0, 1) circ2 = Circuit().rx(0, 0.785).ry(1, 0.393).cnot(1, 0) circ3 = Circuit().t(0).t(1).cz(0, 1).s(0).cz(1, 2).s(1).s(2) parameterize_circuit = Circuit().rx(0, FreeParameter("alpha")).cnot(0, 1).ry(1, FreeParameter("beta")) # Create circuit bindings with different parameters circuit_binding = CircuitBinding( circuit=parameterize_circuit, input_sets={ 'alpha': (0.10, 0.11, 0.22, 0.34, 0.45), 'beta': (1.01, 1.01, 1.03, 1.04, 1.04), }) # Creating the program set program_set_1 = ProgramSet([ circ1, circ2, circ3, circuit_binding, ])
Esse conjunto de programas contém quatro programas exclusivos: circ1, circ2, circ3 e circuit_binding. O programa circuit_binding é executado com cinco associações de parâmetros diferentes, criando cinco executáveis. Os outros três programas sem parâmetros criam um executável cada. Isso resulta em um total de oito executáveis, conforme mostrado na imagem a seguir.
O segundo exemplo de código a seguir demonstra como usar o método product() para anexar o mesmo conjunto de observáveis a cada executável do conjunto de programas.
from braket.circuits.observables import I, X, Y, Z observables = [Z(0) @ Z(1), X(0) @ X(1), Z(0) @ X(1), X(0) @ Z(1)] program_set_2 = ProgramSet.product( circuits=[circ1, circ2, circuit_binding], observables=observables )
Para programas sem parâmetros, cada observável é medido para cada circuito. Para programas paramétricos, cada observável é medido para cada conjunto de entrada, conforme mostrado na imagem a seguir.
O terceiro exemplo de código a seguir demonstra como usar o método zip() para emparelhar observáveis individuais com conjuntos de parâmetros específicos no ProgramSet.
program_set_3 = ProgramSet.zip( circuits=circuit_binding, observables=observables + [Y(0) @ Y(1)] )
Em vez do CircuitBinding(), você pode compactar diretamente uma lista de observáveis com uma lista de circuitos e conjuntos de entrada.
program_set_4 = ProgramSet.zip( circuits=[circ1, circ2, circ3], input_sets=[{}, {}, {}], observables=observables[:3] )
Para obter mais informações e exemplos sobre conjuntos de programas, consulte a pasta Conjunto de programas
Inspecione e execute um conjunto de programas em um dispositivo
A contagem de executáveis de um conjunto de programas é igual ao número de circuitos exclusivos vinculados a parâmetros. Calcule o número total de executáveis e disparos do circuito usando o exemplo de código a seguir.
# Number of shots per executable shots = 10 num_executables = program_set_1.total_executables # Calculate total number of shots across all executables total_num_shots = shots*num_executables
nota
Com conjuntos de programas, você paga uma taxa única por tarefa e uma taxa por tiro com base no número total de disparos em todos os circuitos em um conjunto de programas.
Para executar o conjunto de programas, use o exemplo de código a seguir.
# Run the program set task = device.run( program_set_1, shots=total_num_shots, )
Ao usar dispositivos Rigetti, seu conjunto de programas pode permanecer no estado RUNNING enquanto as tarefas estão parcialmente concluídas e parcialmente enfileiradas. Para obter resultados mais rápidos, considere enviar seu programa definido como um Hybrid Job.
Analisando resultados
Execute o código a seguir para analisar e medir os resultados dos executáveis em um ProgramSet.
# Get the results from a program set result = task.result() # Get the first executbable first_program = result[0] first_executable = first_program[0] # Inspect the results of the first executable measurements_from_first_executable = first_executable.measurements print(measurements_from_first_executable)
Medição parcial
Em vez de medir todos os qubits em um circuito quântico, use a medição parcial para medir qubits individuais ou um subconjunto de qubits.
nota
Recursos adicionais, como medição de circuito médio e operações de avanço, estão disponíveis como capacidades experimentais, consulte Acesso a circuitos dinâmicos em dispositivos IQM.
Exemplo: medir um subconjunto de qubits
O exemplo de código a seguir demonstra a medição parcial medindo somente o qubit 0 em um circuito de estado Bell.
from braket.devices import LocalSimulator from braket.circuits import Circuit # Use the local state vector simulator device = LocalSimulator() # Define an example bell circuit and measure qubit 0 circuit = Circuit().h(0).cnot(0, 1).measure(0) # Run the circuit task = device.run(circuit, shots=10) # Get the results result = task.result() # Print the circuit and measured qubits print(circuit) print() print("Measured qubits: ", result.measured_qubits)
Alocação manual de qubit
Ao executar um circuito quântico em computadores quânticos a partir do Rigetti, você pode, opcionalmente, usar a alocação manual de qubit para controlar quais qubits são usados em seu algoritmo. O Amazon Braket Console
A alocação manual de qubit permite que você execute circuitos com maior precisão e investigue propriedades de qubit individuais. Pesquisadores e usuários avançados otimizam o projeto do circuito com base nos dados mais recentes de calibração do dispositivo e podem obter resultados mais precisos.
O exemplo a seguir demonstra como alocar qubits de forma explícita.
# Import the device module from braket.aws import AwsDevice device = AwsDevice("arn:aws:braket:us-west-1::device/qpu/rigetti/Ankaa-3") circ = Circuit().h(0).cnot(0, 7) # Indices of actual qubits in the QPU # Set up S3 bucket (where results are stored) my_bucket = "amazon-braket-s3-demo-bucket" # The name of the bucket my_prefix = "your-folder-name" # The name of the folder in the bucket s3_location = (my_bucket, my_prefix) my_task = device.run(circ, s3_location, shots=100, disable_qubit_rewiring=True)
Para obter mais informações, consulte os exemplos do Amazon Braket GitHub
Compilação literal
Quando você executa um circuito quântico em computadores quânticos baseados em portas, você pode direcionar o compilador para executar seus circuitos exatamente como definido, sem nenhuma modificação. Usando a compilação literal, você pode especificar que um circuito inteiro seja preservado com precisão conforme especificado ou que somente partes específicas dele sejam preservadas (compatíveis somente pelo Rigetti). Ao desenvolver algoritmos para benchmarking de hardware ou protocolos de mitigação de erros, você precisa ter a opção de especificar exatamente as portas e os layouts de circuito que estão sendo executados no hardware. A compilação integral oferece controle direto sobre o processo de compilação, desativando determinadas etapas de otimização, garantindo assim que seus circuitos funcionem exatamente como projetado.
A compilação literal é suportada nos Rigetti dispositivosAQT, IonQIQM, e e requer o uso de portas nativas. Ao usar a compilação literal, é aconselhável verificar a topologia do dispositivo para garantir que as portas sejam chamadas a qubits conectados e que o circuito use as portas nativas compatíveis no hardware. O exemplo a seguir mostra como acessar programática a lista de portas nativas compatíveis com um dispositivo.
device.properties.paradigm.nativeGateSet
Para Rigetti, a reconexão qubit deve ser desativada configurando disableQubitRewiring=True para uso com compilação literal. Se disableQubitRewiring=False for definido ao usar caixas textuais em uma compilação, o circuito quântico falha na validação e não será executado.
Se a compilação literal estiver habilitada para um circuito e executada em uma QPU não compatível, um erro é gerado indicando que uma operação não comptatível causou a falha da tarefa. À medida que mais hardware quântico oferece suporte nativo às funções do compilador, esse recurso será expandido para incluir esses dispositivos. Os dispositivos que oferecem suporte à compilação literal a incluem como uma operação compatível quando consultados com o código a seguir.
from braket.aws import AwsDevice from braket.device_schema.device_action_properties import DeviceActionType device = AwsDevice("arn:aws:braket:us-west-1::device/qpu/rigetti/Ankaa-3") device.properties.action[DeviceActionType.OPENQASM].supportedPragmas
Não há custo adicional associado ao uso da compilação literal. Você continua sendo cobrado por tarefas quânticas executadas em dispositivos Braket QPU, instâncias de caderno e simuladores sob demanda com base nas taxas atuais, conforme especificado na página de preços do Amazon Braket
nota
Se você estiver usando o OpenQASM para escrever seus circuitos para os IonQ dispositivos AQT e quiser mapear seu circuito diretamente para os qubits físicos, precisará usar o, #pragma braket verbatim pois o disableQubitRewiring sinalizador é ignorado pelo OpenQASM.
Simulação de ruído
Para instanciar o simulador de ruído local, você pode alterar o backend da seguinte maneira.
# Import the device module from braket.aws import AwsDevice device = LocalSimulator(backend="braket_dm")
É possível fazer circuitos ruidosos de duas maneiras:
-
Construa o circuito ruidoso de baixo para cima.
-
Pegue um circuito existente e livre de ruído e injete ruído por toda parte.
O exemplo a seguir mostra as abordagens usando um circuito básico com ruído despolarizador e um canal Kraus personalizado.
import scipy.stats import numpy as np # Bottom up approach # Apply depolarizing noise to qubit 0 with probability of 0.1 circ = Circuit().x(0).x(1).depolarizing(0, probability=0.1) # Create an arbitrary 2-qubit Kraus channel E0 = scipy.stats.unitary_group.rvs(4) * np.sqrt(0.8) E1 = scipy.stats.unitary_group.rvs(4) * np.sqrt(0.2) K = [E0, E1] # Apply a two-qubit Kraus channel to qubits 0 and 2 circ = circ.kraus([0, 2], K)
from braket.circuits import Noise # Inject noise approach # Define phase damping noise noise = Noise.PhaseDamping(gamma=0.1) # The noise channel is applied to all the X gates in the circuit circ = Circuit().x(0).y(1).cnot(0, 2).x(1).z(2) circ_noise = circ.copy() circ_noise.apply_gate_noise(noise, target_gates=Gate.X)
Executar um circuito é a mesma experiência de usuário de antes, conforme mostrado nos dois exemplos a seguir.
Exemplo 1
task = device.run(circ, shots=100)
Ou
Exemplo 2
task = device.run(circ_noise, shots=100)
Para obter mais exemplos, consulte o exemplo introdutório do simulador de ruído do Braket