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á.
Construindo 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 suportadas por cada dispositivo. Ele também contém instruções sobre como alocar manualmente qubits, instrua o compilador a executar seus circuitos exatamente conforme definido e a 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 Pulse Control no Amazon Braket.
Nesta seção:
Portões e circuitos
As portas e circuitos quânticos são definidos na braket.circuits
Circuit()
Exemplo: definir um circuito
O exemplo começa definindo um circuito de amostra de quatro qubits (rotuladoq0
,, q1
q2
, eq3
) consistindo em portas Hadamard padrão de um qubit e portas CNOT de dois qubits. Você pode visualizar esse circuito chamando a print
função 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-C--- | q1 : -H-|-C- | | 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)
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 novos portões, como o Ry
portão controlado.
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 os portões disponíveis
O exemplo a seguir mostra como examinar todos os portões disponíveis no Amazon Suporte.
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', 'GPi', 'GPi2', 'H', 'I', 'ISwap', 'MS', 'PSwap', 'PhaseShift', 'PulseGate', 'Rx', 'Ry', 'Rz', 'S', 'Si', 'Swap', 'T', 'Ti', '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, você circ.h(0)
ligaria para adicionar um portão Hadamard ao primeiro qubit.
nota
As portas são anexadas no local, 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. 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 targets
parâmetro.
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. An Instruction
é uma diretiva quântica que descreve a tarefa quântica a ser executada em um dispositivo quântico. Instruction
os operadores incluem Gate
somente objetos do tipo.
# 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]) new_instr = instr.copy(target_mapping={0: 5})
Exemplo: veja os portões 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 suportadas de um dispositivo nas propriedades do dispositivo. O seguinte mostra um exemplo com 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 the Aria-1 device: ['x', 'y', 'z', 'rx', 'ry', 'rz', 'h', 'cnot', 's', 'si', 't', 'ti', 'v', 'vi', 'xx', 'yy', 'zz', 'swap']
As portas suportadas podem precisar ser compiladas em portas nativas antes de poderem ser executadas em hardware quântico. Quando você envia um circuito, Amazon Braket executa essa compilação automaticamente.
Exemplo: recupere programaticamente a fidelidade das portas nativas suportadas 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 os dois qubit fidelidade de portas 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}")
Medição parcial
Seguindo os exemplos anteriores, medimos todos os qubits no circuito quântico. No entanto, é possível medir qubits individuais ou um subconjunto de qubits.
Exemplo: medir um subconjunto de qubits
Neste exemplo, demonstramos uma medição parcial adicionando uma measure
instrução com os qubits de destino ao final do circuito.
# 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)
Manual qubit alocação
Quando você executa um circuito quântico em computadores quânticos de Rigetti, você pode opcionalmente usar o manual qubit alocação para controlar qual qubits são usados para seu algoritmo. O Amazon Braket Console e o Amazon Braket
Manual qubit a alocação permite que você execute circuitos com maior precisão e investigue indivíduos qubit propriedades. 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 explicitamente.
circ = Circuit().h(0).cnot(0, 7) # Indices of actual qubits in the QPU 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 (suportado por Rigetti somente). Ao desenvolver algoritmos para benchmarking de hardware ou protocolos de mitigação de erros, você precisa ter a opção de especificar exatamente os layouts de portas e circuitos que você está executando 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.
Atualmente, a compilação literal é suportada no Rigetti, IonQ e IQM dispositivos 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 conectadas qubits e que o circuito usa as portas nativas suportadas no hardware. O exemplo a seguir mostra como acessar programaticamente a lista de portas nativas suportadas por um dispositivo.
device.properties.paradigm.nativeGateSet
Para Rigetti, qubit a religação 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 que não a suporta, um erro é gerado indicando que uma operação não suportada 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 notebook 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 o IonQ dispositivo, e se você deseja mapear seu circuito diretamente para os qubits físicos, você precisa usá-lo, #pragma braket verbatim
pois o disableQubitRewiring
sinalizador é completamente ignorado pelo OpenQASM.
Simulação de ruído
Para instanciar o simulador de ruído local, você pode alterar o back-end da seguinte maneira.
device = LocalSimulator(backend="braket_dm")
Você pode criar 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 simples com ruído despolarizador e um canal Kraus personalizado.
# 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)
# 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, s3_location)
Ou
Exemplo 2
task = device.run(circ_noise, s3_location)
Para obter mais exemplos, consulte o exemplo introdutório do simulador de ruído Braket