

기계 번역으로 제공되는 번역입니다. 제공된 번역과 원본 영어의 내용이 상충하는 경우에는 영어 버전이 우선합니다.

# 첫 번째 회로 빌드
<a name="braket-get-started-run-circuit"></a>

노트북 인스턴스가 시작되고 나면, 방금 생성한 노트북을 선택하여 표준 Jupyter 인터페이스로 인스턴스를 엽니다.

![\[InService 및 URL 상태인 amazon-braket-test라는 기존 노트북 인스턴스를 보여주는 노트북 인터페이스입니다.\]](http://docs.aws.amazon.com/ko_kr/braket/latest/developerguide/images/console-page.png)


Amazon Braket 노트북 인스턴스에는 Amazon Braket SDK 및 모든 종속성이 사전 설치되어 있습니다. 먼저 `conda_braket` 커널을 사용하여 새 노트북을 생성합니다.

![\[노트북, 콘솔 및 터미널, 텍스트 파일, Markdown 파일, Python 파일과 같은 기타 도구를 위한 시작 관리자 인터페이스로, conda_braket Python 환경이 강조 표시되어 있습니다.\]](http://docs.aws.amazon.com/ko_kr/braket/latest/developerguide/images/jupyter-open.png)


간단한 “Hello, world\$1”로 시작할 수 있습니다. 예. 먼저 벨 상태를 준비하는 회로를 구성한 다음, 다른 디바이스에서 해당 회로를 실행하여 결과를 얻습니다.

먼저 Amazon Braket SDK 모듈을 가져오고 simpleBRAKETlong; SDK 모듈을 정의한 다음 기본 벨 상태 회로를 정의하여' 시작'을 가져와서 시작합니다.

```
import boto3
from braket.aws import AwsDevice
from braket.devices import LocalSimulator
from braket.circuits import Circuit

# Create the circuit
bell = Circuit().h(0).cnot(0, 1)
```

다음 명령을 사용하여 회로를 시각화할 수 있습니다.

```
print(bell)
```

```
T  : │  0  │  1  │
      ┌───┐       
q0 : ─┤ H ├───●───
      └───┘   │   
            ┌─┴─┐ 
q1 : ───────┤ X ├─
            └───┘ 
T  : │  0  │  1  │
```

 **로컬 시뮬레이터에서 회로 실행** 

다음으로, 회로를 실행할 양자 디바이스를 선택합니다. Amazon Braket SDK는 신속한 프로토타이핑 및 테스트를 위한 로컬 시뮬레이터와 함께 제공됩니다. 최대 25qubits(로컬 하드웨어에 따라 다름)까지 지원되는 더 작은 회로에는 로컬 시뮬레이터를 사용하는 것이 좋습니다.

로컬 시뮬레이터를 인스턴스화하려면 다음과 같이 한 다음,

```
# Instantiate the local simulator
local_sim = LocalSimulator()
```

회로를 실행합니다.

```
# Run the circuit
result = local_sim.run(bell, shots=1000).result()
counts = result.measurement_counts
print(counts)
```

다음과 같은 결과가 표시되어야 합니다.

```
Counter({'11': 503, '00': 497})
```

준비한 특정 벨 상태는 예상대로 \$100⟩ 및 \$111⟩의 동등한 중첩이며 측정 결과로서 00 및 11의 분포가 거의 동등(최대 shot 노이즈)합니다.

 **온디맨드 시뮬레이터에서 회로 실행** 

또한 Amazon Braket은 대규모 회로 실행을 위한 온디맨드 고성능 시뮬레이터 SV1에 대한 액세스를 제공합니다. SV1은 최대 34qubits의 양자 회로 시뮬레이션을 허용하는 온디맨드 상태 벡터 시뮬레이터입니다. SV1에 대한 자세한 내용은 [지원되는 디바이스](braket-devices.md) 섹션 및 AWS 콘솔에서 확인할 수 있습니다. SV1(및 TN1 또는 QPU)에서 양자 작업을 실행할 때 양자 작업의 결과는 계정의 S3 버킷에 저장됩니다. 버킷을 지정하지 않으면 Braket SDK가 기본 버킷 `amazon-braket-{region}-{accountID}`를 생성합니다. 자세한 내용은 [Amazon Braket에 대한 액세스 관리](braket-manage-access.md)를 참조하세요.

**참고**  
다음 예제가 `amazon-braket-s3-demo-bucket`을 버킷 이름으로 표시하는 실제 기존 버킷 이름을 입력합니다. Amazon Braket의 버킷 이름은 항상 `amazon-braket-`으로 시작하며 그 뒤에 추가한 다른 식별 문자가 옵니다. S3 버킷을 설정하는 방법에 대한 자세한 내용은 [Amazon S3 시작하기](https://docs.aws.amazon.com/AmazonS3/latest/userguide/GetStartedWithS3.html)를 참조하세요.

```
# Get the account ID
aws_account_id = boto3.client("sts").get_caller_identity()["Account"]

# The name of the bucket
my_bucket = "amazon-braket-s3-demo-bucket"

# The name of the folder in the bucket
my_prefix = "simulation-output"
s3_folder = (my_bucket, my_prefix)
```

SV1에서 회로를 실행하려면 이전에 선택한 S3 버킷의 위치를 `.run()` 직접 호출에서 위치 인수로 제공해야 합니다.

```
# Choose the cloud-based on-demand simulator to run your circuit
device = AwsDevice("arn:aws:braket:::device/quantum-simulator/amazon/sv1")

# Run the circuit
task = device.run(bell, s3_folder, shots=100)

# Display the results
print(task.result().measurement_counts)
```

Amazon Braket 콘솔은 양자 작업에 대한 추가 정보를 제공합니다. 콘솔의 **양자 작업** 탭으로 이동하면 양자 작업이 목록 상단에 있어야 합니다. 아니면, 고유한 양자 작업 ID 또는 기타 기준을 사용하여 양자 작업을 검색할 수 있습니다.

**참고**  
90일이 지나면 Amazon Braket은 양자 작업과 연결된 모든 양자 작업 ID 및 기타 메타데이터를 자동으로 제거합니다. 자세한 내용은 [데이터 보존](https://docs.aws.amazon.com/braket/latest/developerguide/security.html#braket-data-retention)을 참조하세요.

 **QPU에서 실행** 

Amazon Braket을 사용하면 단 한 줄의 코드만 변경하여 물리적 양자 컴퓨터에서 이전 양자 회로 예제를 실행할 수 있습니다. Amazon Braket은 다양한 Quantum 처리 장치(QPU) 디바이스에 대한 액세스를 제공합니다. [지원되는 디바이스](braket-devices.md) 섹션과 AWS 콘솔의 디바이스 탭에서 다양한 **디바이스** 및 가용성 기간에 대한 정보를 찾을 수 있습니다. 다음 예제에서는 IQM 디바이스를 인스턴스화하는 방법을 보여줍니다.

```
# Choose the IQM hardware to run your circuit
device = AwsDevice("arn:aws:braket:eu-north-1::device/qpu/iqm/Garnet")
```

또는 이 코드가 있는 IonQ 디바이스를 선택합니다.

```
# Choose the Ionq device to run your circuit
device = AwsDevice("arn:aws:braket:us-east-1::device/qpu/ionq/Aria-1")
```

디바이스를 선택한 후 워크로드를 실행하기 전에, 다음 코드로 디바이스 대기열 깊이를 쿼리하여 양자 작업 또는 하이브리드 작업 수를 확인할 수 있습니다. 또한 고객은 Amazon Braket Management Console의 디바이스 페이지에서 디바이스별 대기열 깊이를 볼 수 있습니다.

```
# Print your queue depth
print(device.queue_depth().quantum_tasks)
# Returns the number of quantum tasks queued on the device
# {<QueueType.NORMAL: 'Normal'>: '0', <QueueType.PRIORITY: 'Priority'>: '0'}

print(device.queue_depth().jobs)
# Returns the number of hybrid jobs queued on the device
# '2'
```

작업을 실행하면 Amazon Braket SDK가 결과를 폴링합니다(기본 시간 초과는 5일). 다음 예제와 같이, `.run()` 명령에서 `poll_timeout_seconds` 파라미터를 수정하여 이 기본값을 변경할 수 있습니다. 폴링 시간 초과가 너무 짧으면 QPU를 사용할 수 없고 로컬 시간 초과 오류가 반환되는 경우와 같이 폴링 시간 내에 결과가 반환되지 않을 수 있다는 점을 유의하세요. `task.result()` 함수를 직접적으로 호출하여 폴링을 다시 시작할 수 있습니다.

```
# Define quantum task with 1 day polling timeout
task = device.run(bell, s3_folder, poll_timeout_seconds=24*60*60)
print(task.result().measurement_counts)
```

또한 양자 작업 또는 하이브리드 작업을 제출한 후 `queue_position()` 함수를 직접적으로 호출하여 대기열 위치를 확인할 수 있습니다.

```
print(task.queue_position().queue_position)
# Return the number of quantum tasks queued ahead of you
# '2'
```

# 첫 번째 양자 알고리즘 빌드
<a name="braket-explore-algorithm-library"></a>

Amazon Braket 알고리즘 라이브러리는 Python으로 작성된 사전 빌드된 양자 알고리즘의 카탈로그입니다. 이러한 알고리즘을 그대로 실행하거나 더 복잡한 알고리즘을 빌드하기 위한 출발점으로 활용할 수 있습니다. Braket 콘솔에서 알고리즘 라이브러리에 액세스할 수 있습니다. 자세한 내용은 [Braket Github 알고리즘 라이브러리](https://github.com/aws-samples/amazon-braket-algorithm-library)를 참조하세요.

![\[간단한 설명과 함께 Berstein Vazirani, Deutsch-Jozsa, Grover 알고리즘 및 Quantum Approximate Optimization Algorithm과 같은 사전 빌드된 양자 알고리즘 목록을 보여주는 Amazon Braket의 알고리즘 라이브러리 페이지입니다.\]](http://docs.aws.amazon.com/ko_kr/braket/latest/developerguide/images/AlgorithmLibrary.png)


Braket 콘솔은 알고리즘 라이브러리에서 사용 가능한 각 알고리즘에 대한 설명을 제공합니다. GitHub 링크를 선택하여 각 알고리즘의 세부 정보를 보거나, **노트북 열기**를 선택하여 열거나 사용 가능한 모든 알고리즘이 포함된 노트북을 생성합니다. 노트북 옵션을 선택하면 노트북의 루트 폴더에서 Braket 알고리즘 라이브러리를 찾을 수 있습니다.

# SDK에서 회로 구성
<a name="braket-constructing-circuit"></a>

이 섹션에서는 회로 정의, 사용 가능한 게이트 보기, 회로 확장, 각 디바이스가 지원하는 게이트 보기의 예제를 제공합니다. 또한 qubits를 수동으로 할당하는 방법, 컴파일러가 정의된 대로 정확하게 회로를 실행하도록 지시하는 방법, 노이즈가 있는 시뮬레이터를 사용하여 노이즈가 있는 회로를 빌드하는 방법에 대한 지침도 포함되어 있습니다.

또한 특정 QPU를 사용하여 다양한 게이트에 대해 Braket에서 펄스 수준으로 작업할 수도 있습니다. 자세한 내용은 [Amazon Braket의 펄스 제어](braket-pulse-control.md)를 참조하세요.

**Topics**
+ [게이트 및 회로](#braket-gates)
+ [프로그램 세트](#braket-program-set)
+ [부분 측정](#braket-partial-measurement)
+ [수동 qubit 할당](#manual-qubit-allocation)
+ [축어적 컴파일](#verbatim-compilation)
+ [노이즈 시뮬레이션](#noise-simulation)

## 게이트 및 회로
<a name="braket-gates"></a>

양자 게이트 및 회로는 Amazon Braket Python SDK의 [https://github.com/aws/amazon-braket-sdk-python/blob/main/src/braket/circuits/circuit.py](https://github.com/aws/amazon-braket-sdk-python/blob/main/src/braket/circuits/circuit.py) 클래스에 정의됩니다. SDK에서 `Circuit()`을 직접적으로 호출하여 새 회로 객체를 인스턴스화할 수 있습니다.

 **예: 회로 정의** 

이 예제는 표준, 단일 큐비트 아다마르 게이트 및 2큐비트 CNOT 게이트로 구성된 4qubits(`q0`, `q1`, `q2` 및 `q3`으로 레이블 지정)의 샘플 회로를 정의하는 것으로 시작합니다. 다음 예제와 같이 `print` 함수를 직접적으로 호출하여 이 회로를 시각화할 수 있습니다.

```
# 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     │
```

 **예: 파라미터화된 회로 정의** 

이 예제에서는 자유 파라미터에 따라 달라지는 게이트가 있는 회로를 정의합니다. 이러한 파라미터의 값을 지정하여 새 회로를 생성하거나, 회로를 제출할 때 특정 디바이스에서 양자 작업으로 실행할 수 있습니다.

```
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)
```

다음과 같이 단일 `float`(모든 자유 파라미터가 취하는 값) 또는 각 파라미터의 값을 지정하는 키워드 인수를 회로에 제공하여 파라미터화된 회로에서 파라미터화되지 않은 새 회로를 생성할 수 있습니다.

```
my_fixed_circuit = my_circuit(1.2)
my_fixed_circuit = my_circuit(alpha=1.2)
print(my_fixed_circuit)
```

`my_circuit`은 수정되지 않으므로 이를 사용하여 고정된 파라미터 값으로 많은 새 회로를 인스턴스화할 수 있습니다.

 **예: 회로의 게이트 수정** 

다음 예제에서는 제어 및 전원 한정자를 사용하는 게이트가 있는 회로를 정의합니다. 이러한 수정 사항을 사용하여 제어된 `Ry` 게이트와 같은 새 게이트를 생성할 수 있습니다.

```
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)
```

게이트 한정자는 로컬 시뮬레이터에서만 지원됩니다.

 **예: 사용 가능한 모든 게이트 보기** 

다음 예제에서는 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)
```

이 코드의 출력에는 모든 게이트가 나열됩니다.

```
['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']
```

이러한 게이트는 해당 유형의 회로에 대한 메서드를 직접적으로 호출하여 회로에 추가할 수 있습니다. 예를 들어 `circ.h(0)`를 직접적으로 호출하여 첫 번째 qubit에 아다마르 게이트를 추가합니다.

**참고**  
게이트가 제자리에 추가되며, 다음 예제에서는 이전 예제에 나열된 모든 게이트를 동일한 회로에 추가합니다.

```
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)
```

사전 정의된 게이트 세트 외에도, 자체 정의된 단위 게이트를 회로에 적용할 수도 있습니다. 이는 `targets` 파라미터에 의해 정의된 qubits에 적용되는 단일 큐비트 게이트(다음 소스 코드에 표시됨) 또는 다중 큐비트 게이트일 수 있습니다.

```
import numpy as np

# Apply a general unitary
my_unitary = np.array([[0, 1],[1, 0]])
circ.unitary(matrix=my_unitary, targets=[0])
```

 **예: 기존 회로 확장** 

명령을 추가하여 기존 회로를 확장할 수 있습니다. `Instruction`은 양자 디바이스에서 수행할 양자 작업을 설명하는 양자 지시어입니다. `Instruction` 연산자에는 유형 `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})
```

 **예: 각 디바이스가 지원하는 게이트 보기** 

시뮬레이터는 Braket SDK의 모든 게이트를 지원하지만, QPU 디바이스는 더 작은 부분 집합을 지원합니다. 디바이스 속성에서 디바이스의 지원되는 게이트를 확인할 수 있습니다. 다음은 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']
```

지원되는 게이트는 양자 하드웨어에서 실행되기 전에 네이티브 게이트로 컴파일해야 할 수 있습니다. 회로를 제출하면 Amazon Braket이 이 컴파일을 자동으로 수행합니다.

 **예: 디바이스에서 지원하는 네이티브 게이트의 충실도를 프로그래밍 방식으로 검색** 

Braket 콘솔의 **디바이스** 페이지에서 충실도 정보를 볼 수 있습니다. 프로그래밍 방식으로 동일한 정보에 액세스하는 것이 도움이 되는 경우도 있습니다. 다음 코드는 QPU의 두 게이트 간에 두 qubit 게이트 충실도를 추출하는 방법을 보여줍니다.

```
# 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}")
```

## 프로그램 세트
<a name="braket-program-set"></a>

프로그램 세트는 단일 양자 작업에서 여러 양자 회로를 효율적으로 실행합니다. 해당 작업에서는 최대 100개의 양자 회로 또는 최대 100개의 서로 다른 파라미터 세트가 있는 단일 파라메트릭 회로를 제출할 수 있습니다. 이 연산은 후속 회로 실행 사이의 시간을 최소화하고 양자 작업 처리 오버헤드를 줄여줍니다. 현재 프로그램 세트는 Amazon Braket Local Simulator 및 AQT, IQM 및 Rigetti 디바이스에서 지원됩니다.

**ProgramSet 정의**

다음 첫 번째 코드 예제에서는 파라미터화된 회로와 파라미터가 없는 회로를 모두 사용하여 `ProgramSet`를 빌드하는 방법을 보여줍니다.

```
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,
])
```

이 프로그램 세트에는 `circ1`, `circ2`, `circ3`, `circuit_binding`의 네 가지 고유한 프로그램이 포함되어 있습니다. `circuit_binding` 프로그램은 5개의 서로 다른 파라미터 바인딩으로 실행되어 5개의 실행 파일을 생성합니다. 다른 3개의 파라미터 없는 프로그램은 각각 하나의 실행 파일을 생성합니다. 그러면 다음 이미지와 같이 총 8개의 실행 파일이 생성됩니다.

![\[4개의 회로가 있는 ProgramSet 구조입니다. 여기서 c4는 5개의 입력 세트를 처리하기 위해 CircuitBinding을 사용합니다.\]](http://docs.aws.amazon.com/ko_kr/braket/latest/developerguide/images/program_set1.png)


다음 두 번째 코드 예제에서는 `product()` 메서드를 사용하여 프로그램 세트의 각 실행 파일에 동일한 관찰 가능 항목 세트를 연결하는 방법을 보여줍니다.

```
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
)
```

파라미터 없는 프로그램의 경우 각 관찰 가능 항목은 각 회로에 대해 측정됩니다. 파라메트릭 프로그램의 경우 다음 이미지에서와 같이 각 관찰 가능 항목은 각 입력 세트에 대해 측정됩니다.

![\[회로 3개의 병렬 실행을 보여주는 ProgramSet.product로, c3는 CircuitBinding을 사용하여 각각 5개의 관찰 가능 항목이 있는 입력 세트 5개를 처리합니다.\]](http://docs.aws.amazon.com/ko_kr/braket/latest/developerguide/images/program_set2.png)


다음 세 번째 코드 예제에서는 `zip()` 메서드를 사용하여 개별 관찰 가능 항목을 `ProgramSet`의 특정 파라미터 세트와 페어링하는 방법을 보여줍니다.

```
program_set_3 = ProgramSet.zip(
    circuits=circuit_binding,
    observables=observables + [Y(0) @ Y(1)]
)
```

![\[입력 세트별로 개별 관찰 가능 항목이 있는 공유 회로를 사용하여 5개의 병렬 실행을 보여주는 CircuitBinding이 포함된 ProgramSet.zip입니다.\]](http://docs.aws.amazon.com/ko_kr/braket/latest/developerguide/images/program_set3.png)


`CircuitBinding()` 대신, 관찰 가능 항목의 목록을 회로 및 입력 세트 목록과 함께 직접 압축할 수 있습니다.

```
program_set_4 = ProgramSet.zip(
    circuits=[circ1, circ2, circ3],
    input_sets=[{}, {}, {}],
    observables=observables[:3]
)
```

![\[해당하는 개별 입력 세트 및 개별 관찰 가능 항목이 있는 회로 7개의 병렬 실행을 보여주는 ProgramSet.zip입니다.\]](http://docs.aws.amazon.com/ko_kr/braket/latest/developerguide/images/program_set4.png)


프로그램 세트에 대한 자세한 내용과 예제는 amazon-braket-examples Github의 [프로그램 세트 폴더](https://github.com/amazon-braket/amazon-braket-examples/tree/main/examples/braket_features/program_sets)를 참조하세요.

**디바이스에서 프로그램 세트 검사 및 실행**

프로그램 세트의 실행 파일 수는 고유한 파라미터 바인딩 회로 수와 같습니다. 다음 코드 예제를 사용하여 회로 실행 파일 및 샷의 총 수를 계산합니다.

```
# 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
```

**참고**  
프로그램 세트를 사용하면 프로그램 세트의 모든 회로에서 총 샷 수를 기준으로 단일 작업당 요금과 샷당 요금을 지불하게 됩니다.

프로그램 세트를 실행하려면 다음 코드 예제를 사용합니다.

```
# Run the program set
task = device.run(
   program_set_1, shots=total_num_shots,
)
```

Rigetti 디바이스를 사용하는 경우 작업이 부분적으로 완료되고 부분적으로 대기열에 있는 동안 프로그램 세트가 `RUNNING` 상태로 유지될 수 있습니다. 결과를 더 빨리 얻으려면 프로그램 세트를 [하이브리드 작업](braket-jobs-first.md)으로 제출하는 것을 고려하세요.

**결과 분석**

다음 코드를 실행하여 `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)
```

## 부분 측정
<a name="braket-partial-measurement"></a>

양자 회로의 모든 큐비트를 측정하는 대신 부분 측정을 사용하여 개별 큐비트 또는 큐비트의 부분 집합을 측정합니다.

**참고**  
중간 회로 측정 및 피드포워드 연산과 같은 추가 기능은 실험 기능으로 사용할 수 있습니다. [IQM 디바이스의 동적 회로에 대한 액세스](braket-experimental-capabilities.md#braket-access-dynamic-circuits)를 참조하세요.

**예: 큐비트의 부분 집합 측정**

다음 코드 예제는 벨 상태 회로에서 큐비트 0만 측정하여 부분 측정을 보여줍니다.

```
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)
```

## 수동 qubit 할당
<a name="manual-qubit-allocation"></a>

Rigetti의 양자 컴퓨터에서 양자 회로를 실행할 때 선택적으로 수동 qubit 할당을 사용하여 알고리즘에 어떤 qubits를 사용할지 제어할 수 있습니다. [Amazon Braket 콘솔](https://console.aws.amazon.com/braket/home)과 [Amazon Braket SDK](https://github.com/aws/amazon-braket-sdk-python)를 사용하면 선택한 QPU 디바이스의 최신 보정 데이터를 검사할 수 있으므로, 실험에 가장 적합한 qubits를 선택할 수 있습니다.

수동 qubit 할당을 사용하면 보다 정확하게 회로를 실행하고 개별 qubit 속성을 조사할 수 있습니다. 연구원과 고급 사용자는 최신 디바이스 보정 데이터를 기반으로 회로 설계를 최적화하고 더 정확한 결과를 얻을 수 있습니다.

다음 예제에서는 명시적으로 qubits를 할당하는 방법을 보여줍니다.

```
# 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)
```

자세한 내용은 [GitHub의 Amazon Braket 예제](https://github.com/aws/amazon-braket-examples), 또는 특히 이 노트북: [QPU 디바이스에서 큐비트 할당](https://github.com/aws/amazon-braket-examples/blob/main/examples/braket_features/Allocating_Qubits_on_QPU_Devices.ipynb)을 참조하세요.

## 축어적 컴파일
<a name="verbatim-compilation"></a>

게이트 기반 양자 컴퓨터에서 양자 회로를 실행할 때 컴파일러가 수정 없이 정의된 대로 정확하게 회로를 실행하도록 지시할 수 있습니다. 축어적 컴파일을 사용하여 전체 회로를 지정된 대로 정확하게 보존하거나 특정 부분만 보존(Rigetti에서만 지원)하도록 지정할 수 있습니다. 하드웨어 벤치마킹 또는 오류 완화 프로토콜을 위한 알고리즘을 개발할 때는 하드웨어에서 실행 중인 게이트 및 회로 레이아웃을 정확하게 지정할 수 있는 옵션이 필요합니다. 축어적 컴파일을 사용하면 특정 최적화 단계를 꺼서 컴파일 프로세스를 직접 제어할 수 있으므로, 회로가 설계된 대로 정확하게 실행됩니다.

축어적 컴파일은 AQT, IQM, 및 Rigetti 디바이스에서 지원되며 IonQ네이티브 게이트를 사용해야 합니다. 축어적 컴파일을 사용하는 경우 디바이스의 토폴로지를 확인하여 게이트가 연결된 qubits에서 직접적으로 호출되고 회로가 하드웨어에서 지원되는 네이티브 게이트를 사용하는지 확인하는 것이 좋습니다. 다음 예제에서는 디바이스에서 지원하는 네이티브 게이트 목록에 프로그래밍 방식으로 액세스하는 방법을 보여줍니다.

```
device.properties.paradigm.nativeGateSet
```

Rigetti의 경우, 축어적 컴파일과 함께 사용할 수 있도록 `disableQubitRewiring=True`를 설정하여 qubit 재배선을 꺼야 합니다. 컴파일에서 축어적 상자를 사용할 때 `disableQubitRewiring=False`가 설정된 경우 양자 회로는 검증에 실패하고 실행되지 않습니다.

축어적 컴파일이 회로에 대해 활성화되어 있고 이를 지원하지 않는 QPU에서 실행되는 경우, 지원되지 않는 연산으로 인해 작업이 실패했음을 나타내는 오류가 생성됩니다. 더 많은 양자 하드웨어가 기본적으로 컴파일러 함수를 지원함에 따라 이 기능은 이러한 디바이스를 포함하도록 확장될 예정입니다. 축어적 컴파일을 지원하는 디바이스에는 다음 코드로 쿼리할 때 지원되는 연산으로 포함됩니다.

```
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
```

축어적 컴파일 사용과 관련된 추가 비용은 없습니다. [Amazon Braket 요금](https://aws.amazon.com/braket/pricing/) 페이지에 지정된 현재 요금을 기준으로 Braket QPU 디바이스, 노트북 인스턴스 및 온디맨드 시뮬레이터에서 실행되는 양자 작업에 대해서는 계속 요금이 부과됩니다. 자세한 내용은 [축어적 컴파일](https://github.com/aws/amazon-braket-examples/blob/main/examples/braket_features/Verbatim_Compilation.ipynb) 예제 노트북을 참조하세요.

**참고**  
OpenQASM을 사용하여 AQT 및 IonQ 디바이스에 대한 회로를 쓰고 회로를 물리적 쿼비트에 직접 매핑하려는 경우 OpenQASM에서 `disableQubitRewiring` 플래그를 무시`#pragma braket verbatim`하므로를 사용해야 합니다.

## 노이즈 시뮬레이션
<a name="noise-simulation"></a>

로컬 노이즈 시뮬레이터를 인스턴스화하려면 다음과 같이 백엔드를 변경할 수 있습니다.

```
# Import the device module
from braket.aws import AwsDevice

device = LocalSimulator(backend="braket_dm")
```

다음과 같은 두 가지 방법으로 노이즈가 있는 회로를 빌드할 수 있습니다.

1. 노이즈가 있는 회로를 상향식으로 빌드합니다.

1. 기존의 노이즈 없는 회로를 가져와 전체적으로 노이즈를 주입합니다.

다음 예제에서는 탈분극 노이즈가 있는 기본 회로와 사용자 지정 Kraus 채널을 사용하는 접근 방식을 보여줍니다.

```
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)
```

다음 두 예제에서와 같이 회로 실행은 이전과 동일한 사용자 환경입니다.

 **예제 1**.

```
task = device.run(circ, shots=100)
```

또는

 **예제 2**.

```
task = device.run(circ_noise, shots=100)
```

추가 예제는 [Braket 입문용 노이즈 시뮬레이터 예제](https://github.com/aws/amazon-braket-examples/blob/main/examples/braket_features/Simulating_Noise_On_Amazon_Braket.ipynb)를 참조하세요.

# 회로 검사
<a name="braket-inspecting-circut"></a>

Amazon Braket의 양자 회로에는 `Moments`라고 하는 의사 시간 개념이 있습니다. 각 qubit는 `Moment`당 단일 게이트를 경험할 수 있습니다. `Moments`의 목적은 회로와 게이트를 더 쉽게 처리하고 시간 구조를 제공하는 것입니다.

**참고**  
모멘트는 일반적으로 QPU에서 게이트가 실행되는 실제 시간과 일치하지 않습니다.

회로의 깊이는 해당 회로의 총 모멘트 수에 따라 결정됩니다. 다음 예제와 같이 메서드 `circuit.depth`를 직접적으로 호출하는 회로 깊이를 볼 수 있습니다.

```
from braket.circuits import Circuit

# Define a circuit with parametrized gates
circ = Circuit().rx(0, 0.15).ry(1, 0.2).cnot(0, 2).zz(1, 3, 0.15).x(0)
print(circ)
print('Total circuit depth:', circ.depth)
```

```
T  : │     0      │        1         │  2  │
      ┌──────────┐                    ┌───┐ 
q0 : ─┤ Rx(0.15) ├───●────────────────┤ X ├─
      └──────────┘   │                └───┘ 
      ┌──────────┐   │   ┌──────────┐       
q1 : ─┤ Ry(0.20) ├───┼───┤ ZZ(0.15) ├───────
      └──────────┘   │   └────┬─────┘       
                   ┌─┴─┐      │             
q2 : ──────────────┤ X ├──────┼─────────────
                   └───┘      │             
                         ┌────┴─────┐       
q3 : ────────────────────┤ ZZ(0.15) ├───────
                         └──────────┘       
T  : │     0      │        1         │  2  │
Total circuit depth: 3
```

위 회로의 총 회로 깊이는 3입니다(모멘트 `0`, `1` 및 `2`로 표시됨). 각 모멘트의 게이트 연산을 확인할 수 있습니다.

 `Moments`는 *키-값* 쌍의 딕셔너리 역할을 합니다.
+ 키는 의사 시간과 qubit 정보가 포함된 `MomentsKey()`입니다.
+ 값은 `Instructions()` 유형으로 할당됩니다.

```
moments = circ.moments
for key, value in moments.items():
    print(key)
    print(value, "\n")
```

```
MomentsKey(time=0, qubits=QubitSet([Qubit(0)]), moment_type=<MomentType.GATE: 'gate'>, noise_index=0, subindex=0)
Instruction('operator': Rx('angle': 0.15, 'qubit_count': 1), 'target': QubitSet([Qubit(0)]), 'control': QubitSet([]), 'control_state': (), 'power': 1) 

MomentsKey(time=0, qubits=QubitSet([Qubit(1)]), moment_type=<MomentType.GATE: 'gate'>, noise_index=0, subindex=0)
Instruction('operator': Ry('angle': 0.2, 'qubit_count': 1), 'target': QubitSet([Qubit(1)]), 'control': QubitSet([]), 'control_state': (), 'power': 1) 

MomentsKey(time=1, qubits=QubitSet([Qubit(0), Qubit(2)]), moment_type=<MomentType.GATE: 'gate'>, noise_index=0, subindex=0)
Instruction('operator': CNot('qubit_count': 2), 'target': QubitSet([Qubit(0), Qubit(2)]), 'control': QubitSet([]), 'control_state': (), 'power': 1) 

MomentsKey(time=1, qubits=QubitSet([Qubit(1), Qubit(3)]), moment_type=<MomentType.GATE: 'gate'>, noise_index=0, subindex=0)
Instruction('operator': ZZ('angle': 0.15, 'qubit_count': 2), 'target': QubitSet([Qubit(1), Qubit(3)]), 'control': QubitSet([]), 'control_state': (), 'power': 1) 

MomentsKey(time=2, qubits=QubitSet([Qubit(0)]), moment_type=<MomentType.GATE: 'gate'>, noise_index=0, subindex=0)
Instruction('operator': X('qubit_count': 1), 'target': QubitSet([Qubit(0)]), 'control': QubitSet([]), 'control_state': (), 'power': 1)
```

`Moments`를 통해 회로에 게이트를 추가할 수도 있습니다.

```
from braket.circuits import Instruction, Gate

new_circ = Circuit()
instructions = [Instruction(Gate.S(), 0),
                Instruction(Gate.CZ(), [1, 0]),
                Instruction(Gate.H(), 1)
                ]

new_circ.moments.add(instructions)
print(new_circ)
```

```
T  : │  0  │  1  │  2  │
      ┌───┐ ┌───┐       
q0 : ─┤ S ├─┤ Z ├───────
      └───┘ └─┬─┘       
              │   ┌───┐ 
q1 : ─────────●───┤ H ├─
                  └───┘ 
T  : │  0  │  1  │  2  │
```

# 결과 유형 목록
<a name="braket-result-types"></a>

Amazon Braket은 `ResultType`을 사용하여 회로를 측정할 때 다양한 유형의 결과를 반환할 수 있습니다. 회로는 다음과 같은 유형의 결과를 반환할 수 있습니다.
+  `AdjointGradient`는 제공된 관찰 가능 항목의 기대값의 기울기(벡터 미분)를 반환합니다. 이 관찰 가능 항목은 수반 미분법을 사용하여 지정된 파라미터와 관련하여 제공된 대상에 작용합니다. 샷이 0인 경우에만 이 방법을 사용할 수 있습니다.
+  `Amplitude`는 출력 파동 함수에서 지정된 양자 상태의 진폭을 반환합니다. SV1 및 로컬 시뮬레이터에서만 사용할 수 있습니다.
+  `Expectation`은 지정된 관찰 가능 항목의 기대값을 반환하며,이 값은 이 장 뒷부분에 소개되는 `Observable` 클래스로 지정할 수 있습니다. 관찰 가능 항목을 측정하는 데 사용되는 대상 qubits를 지정해야 하며, 지정된 대상의 수는 관찰 가능 항목이 작용하는 qubits의 수와 같아야 합니다. 대상이 지정되지 않은 경우 관찰 가능 항목은 1qubit에서만 작동해야 하며 모든 qubits에 병렬로 적용됩니다.
+  `Probability`는 계산 기저 상태를 측정할 확률을 반환합니다. 대상이 지정되지 않은 경우 `Probability`는 모든 기저 상태를 측정할 확률을 반환합니다. 대상이 지정되면 지정된 qubits에 있는 기저 벡터의 주변 확률만 반환됩니다. 관리형 시뮬레이터 및 QPU는 최대 15큐비트로 제한되며 로컬 시뮬레이터는 시스템의 메모리 크기로 제한됩니다.
+  `Reduced density matrix`는 qubits의 시스템에서 지정된 대상 qubits의 하위 시스템에 대한 밀도 행렬을 반환합니다. 이 결과 유형의 크기를 제한하기 위해 Braket은 대상 qubits의 수를 최대 8개로 제한합니다.
+  `StateVector`는 전체 상태 벡터를 반환합니다. 로컬 시뮬레이터에서 사용할 수 있습니다.
+  `Sample`은 지정된 대상 qubit 세트 및 관찰 가능 항목의 측정 수를 반환합니다. 대상이 지정되지 않은 경우 관찰 가능 항목은 1qubit에서만 작동해야 하며 모든 qubits에 병렬로 적용됩니다. 대상이 지정된 경우 지정된 대상의 수는 관찰 가능 항목이 작용하는 qubits의 수와 같아야 합니다.
+  `Variance`는 지정된 대상 qubit 세트 및 관찰 가능 항목의 분산(`mean([x-mean(x)]2)`)을 요청된 결과 유형으로 반환합니다. 대상이 지정되지 않은 경우 관찰 가능 항목은 1qubit에서만 작동해야 하며 모든 qubits에 병렬로 적용됩니다. 그렇지 않으면 지정된 대상의 수는 관찰 가능 항목이 적용될 수 있는 qubits의 수와 같아야 합니다.

 **다양한 공급자에 대해 지원되는 결과 유형:** 


|  |  |  |  |  |  |  |  |  | 
| --- |--- |--- |--- |--- |--- |--- |--- |--- |
|  |  로컬 sim  |   SV1   |   DM1   |   TN1   |   AQT   |   IonQ   |   IQM   |   Rigetti   | 
|  수반 기울기  |  N  |  Y  |  N  |  N  |  N  |  N  |  N  |  N  | 
|  진폭  |  Y  |  Y  |  N  |  N  |  N  |  N  |  N  |  N  | 
|  기대값  |  Y  |  Y  |  Y  |  Y  |  Y  |  Y  |  Y  |  Y  | 
|  확률  |  Y  |  Y  |  Y  |  N  |  Y  |  Y  |  Y  |  Y  | 
|  축약 밀도 행렬  |  Y  |  N  |  Y  |  N  |  N  |  N  |  N  |  N  | 
|  상태 벡터  |  Y  |  N  |  N  |  N  |  N  |  N  |  N  |  N  | 
|  샘플  |  Y  |  Y  |  Y  |  Y  |  Y  |  Y  |  Y  |  Y  | 
|  분산  |  Y  |  Y  |  Y  |  Y  |  Y  |  Y  |  Y  |  Y  | 

다음 예제와 같이 디바이스 속성을 검토하여 지원되는 결과 유형을 확인할 수 있습니다.

```
from braket.aws import AwsDevice

device = AwsDevice("arn:aws:braket:us-west-1::device/qpu/rigetti/Ankaa-3")

# Print the result types supported by this device
for iter in device.properties.action['braket.ir.openqasm.program'].supportedResultTypes:
    print(iter)
```

```
name='Sample' observables=['x', 'y', 'z', 'h', 'i'] minShots=10 maxShots=50000
name='Expectation' observables=['x', 'y', 'z', 'h', 'i'] minShots=10 maxShots=50000
name='Variance' observables=['x', 'y', 'z', 'h', 'i'] minShots=10 maxShots=50000
name='Probability' observables=None minShots=10 maxShots=50000
```

`ResultType`을 직접적으로 호출하려면 다음 예제와 같이 회로에 추가합니다.

```
from braket.circuits import Circuit, Observable

circ = Circuit().h(0).cnot(0, 1).amplitude(state=["01", "10"])
circ.probability(target=[0, 1])
circ.probability(target=0)
circ.expectation(observable=Observable.Z(), target=0)
circ.sample(observable=Observable.X(), target=0)
circ.state_vector()
circ.variance(observable=Observable.Z(), target=0)

# Print one of the result types assigned to the circuit
print(circ.result_types[0])
```

**참고**  
다양한 양자 디바이스는 다양한 형식으로 결과를 제공합니다. 예를 들어 Rigetti 디바이스는 측정값을 반환하는 반면 IonQ 디바이스는 확률을 제공합니다. Amazon Braket SDK는 모든 결과에 대한 측정 속성을 제공합니다. 그러나 확률을 반환하는 디바이스의 경우, 샷당 측정값을 사용할 수 없으므로 이러한 측정값은 사후 계산되며 확률을 기반으로 합니다. 결과가 사후 계산되었는지 확인하려면 결과 객체에서 `measurements_copied_from_device`를 확인합니다. 이 연산은 Amazon Braket SDK GitHub 리포지토리의 [gate\$1model\$1quantum\$1task\$1result.py](https://github.com/aws/amazon-braket-sdk-python/blob/ca5b08dada4839ca31c012ff50aa20b656fd1879/src/braket/tasks/gate_model_quantum_task_result.py#L70-L72) 파일에 자세히 설명되어 있습니다.

## 관찰 가능 항목
<a name="braket-result-types-observables"></a>

Amazon Braket의 `Observable` 클래스를 사용하면 특정 관찰 가능 항목을 측정할 수 있습니다.

각 qubit에 하나의 고유한 비동일성 관찰 가능 항목만 적용할 수 있습니다. 동일한 qubit에 두 개 이상의 서로 다른 비동일성 관찰 가능 항목을 지정하는 경우 오류가 발생합니다. 이를 위해 텐서 곱의 각 인자는 개별 관찰 가능 항목으로 간주됩니다. 다시 말해, 해당 qubit에 작용하는 인자가 동일하게 유지되는 한, 동일한 qubit에 대해 여러 텐서 곱을 가질 수 있습니다.

관찰 가능 항목의 규모를 조정하고 다른 관찰 가능 항목을 추가할 수 있습니다(규모 조정 여부에 관계없음). 그러면 `AdjointGradient` 결과 유형에 사용할 수 있는 `Sum`이 생성됩니다.

`Observable` 클래스에는 다음과 같은 관찰 가능 항목이 포함됩니다.

```
import numpy as np

Observable.I()
Observable.H()
Observable.X()
Observable.Y()
Observable.Z()

# Get the eigenvalues of the observable
print("Eigenvalue:", Observable.H().eigenvalues)
# Or rotate the basis to be computational basis
print("Basis rotation gates:", Observable.H().basis_rotation_gates)

# Get the tensor product of the observable for the multi-qubit case
tensor_product = Observable.Y() @ Observable.Z()
# View the matrix form of an observable by using
print("The matrix form of the observable:\n", Observable.Z().to_matrix())
print("The matrix form of the tensor product:\n", tensor_product.to_matrix())

# Factorize an observable in the tensor form
print("Factorize an observable:", tensor_product.factors)

# Self-define observables, given it is a Hermitian
print("Self-defined Hermitian:", Observable.Hermitian(matrix=np.array([[0, 1], [1, 0]])))

print("Sum of other (scaled) observables:", 2.0 * Observable.X() @ Observable.X() + 4.0 * Observable.Z() @ Observable.Z())
```

```
Eigenvalue: [ 1. -1.]
Basis rotation gates: (Ry('angle': -0.7853981633974483, 'qubit_count': 1),)
The matrix form of the observable:
 [[ 1.+0.j  0.+0.j]
 [ 0.+0.j -1.+0.j]]
The matrix form of the tensor product:
 [[ 0.+0.j  0.+0.j  0.-1.j  0.+0.j]
 [ 0.+0.j -0.+0.j  0.+0.j  0.+1.j]
 [ 0.+1.j  0.+0.j  0.+0.j  0.+0.j]
 [ 0.+0.j  0.-1.j  0.+0.j -0.+0.j]]
Factorize an observable: (Y('qubit_count': 1), Z('qubit_count': 1))
Self-defined Hermitian: Hermitian('qubit_count': 1, 'matrix': [[0.+0.j 1.+0.j], [1.+0.j 0.+0.j]])
Sum of other (scaled) observables: Sum(TensorProduct(X('qubit_count': 1), X('qubit_count': 1)), TensorProduct(Z('qubit_count': 1), Z('qubit_count': 1)))
```

## 파라미터
<a name="braket-result-types-parameters"></a>

회로는 자유 파라미터를 포함할 수 있습니다. 이러한 자유 파라미터는 한 번만 구성하면 여러 번 실행할 수 있으며 기울기를 계산하는 데 사용할 수 있습니다.

각 자유 파라미터는 다음에 사용되는 문자열 인코딩 이름을 사용합니다.
+ 파라미터 값 설정
+ 사용할 파라미터 식별

```
from braket.circuits import Circuit, FreeParameter, observables
from braket.parametric import FreeParameter

theta = FreeParameter("theta")
phi = FreeParameter("phi")
circ = Circuit().h(0).rx(0, phi).ry(0, phi).cnot(0, 1).xx(0, 1, theta)
```

## 수반 기울기
<a name="braket-result-types-adjoint-gradient"></a>

SV1 디바이스는 다항 해밀토니안을 포함한 관찰 가능 항목 기대값의 수반 기울기를 계산합니다. 파라미터를 미분하려면 이름(문자열 형식)을 지정하거나 직접 참조로 지정합니다.

```
from braket.aws import AwsDevice
from braket.devices import Devices

device = AwsDevice(Devices.Amazon.SV1)

circ.adjoint_gradient(observable=3 * Observable.Z(0) @ Observable.Z(1) - 0.5 * observables.X(0), parameters = ["phi", theta])
```

고정된 파라미터 값을 파라미터화된 회로에 인수로 전달하면 자유 파라미터가 제거됩니다. 자유 파라미터가 더 이상 존재하지 않으므로, 이 회로를 `AdjointGradient`로 실행하면 오류가 생성됩니다. 다음 코드 예제에서는 올바른 사용법과 잘못된 사용법을 보여줍니다.

```
# Will error, as no free parameters will be present
#device.run(circ(0.2), shots=0)

# Will succeed
device.run(circ, shots=0, inputs={'phi': 0.2, 'theta': 0.2})
```