common.title

Docs
Quantum Circuit
TYTAN CLOUD

QUANTUM GAMING


Overview
Contact
Event
Project
Research

Terms of service (Web service)

Terms of service (Quantum and ML Cloud service)

Privacy policy


Sign in
Sign up
common.title

最新型冷却原子アナログ量子コンピュータの使い方を解説!

Yuichiro Minato

2022/11/06 07:38

新しく出た中性原子量子コンピュータをツールからさっそく使っていきます。今回はamazonの提供しているbraketから行います。blueqatSDKではいまのところサポート予定はないですのでbraketを使います。

中性原子はアナログ方式の量子プロセッサを採用していて、リュードベリブロッケードなどの知識を使います。また、ハミルトニアンと呼ばれる式をベースに計算をする方式で詳しくはblueqat.comから該当のさまざまな記事が出ていますので、ご参照ください。

まずはamazon braketを最新にします。実機を使うにはまたアカウント登録などが必要ですが、今回は割愛します。

例題はこちらから使っています。
https://github.com/aws/amazon-braket-examples/tree/main/examples/analog_hamiltonian_simulation

時々オリジナルのコードも入れてます。

!pip install -U amazon-braket-sdk
Requirement already satisfied: amazon-braket-sdk in /home/ec2-user/anaconda3/envs/Braket/lib/python3.7/site-packages (1.31.0)
Collecting amazon-braket-sdk
  Downloading amazon_braket_sdk-1.33.0-py3-none-any.whl (241 kB)
     |████████████████████████████████| 241 kB 27.3 MB/s eta 0:00:01
[?25hRequirement already satisfied: boltons in /home/ec2-user/anaconda3/envs/Braket/lib/python3.7/site-packages (from amazon-braket-sdk) (20.2.1)
Requirement already satisfied: numpy in /home/ec2-user/anaconda3/envs/Braket/lib/python3.7/site-packages (from amazon-braket-sdk) (1.21.6)
Requirement already satisfied: sympy in /home/ec2-user/anaconda3/envs/Braket/lib/python3.7/site-packages (from amazon-braket-sdk) (1.7.1)
Requirement already satisfied: networkx in /home/ec2-user/anaconda3/envs/Braket/lib/python3.7/site-packages (from amazon-braket-sdk) (2.4)
Requirement already satisfied: backoff in /home/ec2-user/anaconda3/envs/Braket/lib/python3.7/site-packages (from amazon-braket-sdk) (1.10.0)
Requirement already satisfied: nest-asyncio in /home/ec2-user/anaconda3/envs/Braket/lib/python3.7/site-packages (from amazon-braket-sdk) (1.5.1)
Collecting oqpy==0.1.0
  Downloading oqpy-0.1.0-py3-none-any.whl (26 kB)
Requirement already satisfied: typing-extensions<5.0.0,>=4.3.0 in /home/ec2-user/anaconda3/envs/Braket/lib/python3.7/site-packages (from oqpy==0.1.0->amazon-braket-sdk) (4.3.0)
Collecting openpulse
  Downloading openpulse-0.4.0-py3-none-any.whl (376 kB)
     |████████████████████████████████| 376 kB 72.9 MB/s eta 0:00:01
[?25hRequirement already satisfied: importlib-metadata in /home/ec2-user/anaconda3/envs/Braket/lib/python3.7/site-packages (from openpulse->amazon-braket-sdk) (2.0.0)
Requirement already satisfied: antlr4-python3-runtime<4.12,>=4.7 in /home/ec2-user/anaconda3/envs/Braket/lib/python3.7/site-packages (from openpulse->amazon-braket-sdk) (4.8)
Collecting openqasm3
  Downloading openqasm3-0.4.0-py3-none-any.whl (385 kB)
     |████████████████████████████████| 385 kB 40.0 MB/s eta 0:00:01
[?25hCollecting amazon-braket-default-simulator>=1.10.0
  Downloading amazon_braket_default_simulator-1.10.0-py3-none-any.whl (206 kB)
     |████████████████████████████████| 206 kB 71.9 MB/s eta 0:00:01
[?25hRequirement already satisfied: scipy in /home/ec2-user/anaconda3/envs/Braket/lib/python3.7/site-packages (from amazon-braket-default-simulator>=1.10.0->amazon-braket-sdk) (1.5.2)
Requirement already satisfied: opt-einsum in /home/ec2-user/anaconda3/envs/Braket/lib/python3.7/site-packages (from amazon-braket-default-simulator>=1.10.0->amazon-braket-sdk) (3.1.0)
Collecting antlr4-python3-runtime<4.12,>=4.7
  Using cached antlr4-python3-runtime-4.9.2.tar.gz (117 kB)
Collecting pydantic==1.9.0
  Using cached pydantic-1.9.0-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl (10.9 MB)
Collecting amazon-braket-schemas>=1.12.0
  Downloading amazon_braket_schemas-1.12.0-py3-none-any.whl (109 kB)
     |████████████████████████████████| 109 kB 73.2 MB/s eta 0:00:01
[?25hCollecting boto3>=1.22.3
  Downloading boto3-1.25.5-py3-none-any.whl (132 kB)
     |████████████████████████████████| 132 kB 66.0 MB/s eta 0:00:01
[?25hRequirement already satisfied: jmespath<2.0.0,>=0.7.1 in /home/ec2-user/anaconda3/envs/Braket/lib/python3.7/site-packages (from boto3>=1.22.3->amazon-braket-sdk) (0.10.0)
Collecting botocore<1.29.0,>=1.28.5
  Downloading botocore-1.28.5-py3-none-any.whl (9.3 MB)
     |████████████████████████████████| 9.3 MB 63.0 MB/s eta 0:00:01
[?25hRequirement already satisfied: python-dateutil<3.0.0,>=2.1 in /home/ec2-user/anaconda3/envs/Braket/lib/python3.7/site-packages (from botocore<1.29.0,>=1.28.5->boto3>=1.22.3->amazon-braket-sdk) (2.8.1)
Requirement already satisfied: urllib3<1.27,>=1.25.4 in /home/ec2-user/anaconda3/envs/Braket/lib/python3.7/site-packages (from botocore<1.29.0,>=1.28.5->boto3>=1.22.3->amazon-braket-sdk) (1.25.11)
Collecting mypy-extensions<0.5.0,>=0.4.3
  Downloading mypy_extensions-0.4.3-py2.py3-none-any.whl (4.5 kB)
Requirement already satisfied: six>=1.5 in /home/ec2-user/anaconda3/envs/Braket/lib/python3.7/site-packages (from python-dateutil<3.0.0,>=2.1->botocore<1.29.0,>=1.28.5->boto3>=1.22.3->amazon-braket-sdk) (1.15.0)
Collecting s3transfer<0.7.0,>=0.6.0
  Downloading s3transfer-0.6.0-py3-none-any.whl (79 kB)
     |████████████████████████████████| 79 kB 10.7 MB/s eta 0:00:01
[?25hRequirement already satisfied: zipp>=0.5 in /home/ec2-user/anaconda3/envs/Braket/lib/python3.7/site-packages (from importlib-metadata->openpulse->amazon-braket-sdk) (3.4.0)
Requirement already satisfied: decorator>=4.3.0 in /home/ec2-user/anaconda3/envs/Braket/lib/python3.7/site-packages (from networkx->amazon-braket-sdk) (4.4.2)
Requirement already satisfied: mpmath>=0.19 in /home/ec2-user/anaconda3/envs/Braket/lib/python3.7/site-packages (from sympy->amazon-braket-sdk) (1.2.1)
Building wheels for collected packages: antlr4-python3-runtime
  Building wheel for antlr4-python3-runtime (setup.py) ... [?25ldone
[?25h  Created wheel for antlr4-python3-runtime: filename=antlr4_python3_runtime-4.9.2-py3-none-any.whl size=144566 sha256=93a2d2ba99501db149d0bd31e57327f71039a9732da618116340f16db8a86422
  Stored in directory: /home/ec2-user/.cache/pip/wheels/14/4c/18/1dbbc9875a2547d2063400ea9f404da4af3331965a71061029
Successfully built antlr4-python3-runtime
Installing collected packages: openqasm3, antlr4-python3-runtime, pydantic, botocore, s3transfer, openpulse, mypy-extensions, amazon-braket-schemas, oqpy, boto3, amazon-braket-default-simulator, amazon-braket-sdk
  Attempting uninstall: antlr4-python3-runtime
    Found existing installation: antlr4-python3-runtime 4.8
    Uninstalling antlr4-python3-runtime-4.8:
      Successfully uninstalled antlr4-python3-runtime-4.8
  Attempting uninstall: pydantic
    Found existing installation: pydantic 1.10.2
    Uninstalling pydantic-1.10.2:
      Successfully uninstalled pydantic-1.10.2
  Attempting uninstall: botocore
    Found existing installation: botocore 1.17.44
    Uninstalling botocore-1.17.44:
      Successfully uninstalled botocore-1.17.44
  Attempting uninstall: s3transfer
    Found existing installation: s3transfer 0.3.4
    Uninstalling s3transfer-0.3.4:
      Successfully uninstalled s3transfer-0.3.4
  Attempting uninstall: amazon-braket-schemas
    Found existing installation: amazon-braket-schemas 1.10.2
    Uninstalling amazon-braket-schemas-1.10.2:
      Successfully uninstalled amazon-braket-schemas-1.10.2
  Attempting uninstall: boto3
    Found existing installation: boto3 1.14.43
    Uninstalling boto3-1.14.43:
      Successfully uninstalled boto3-1.14.43
  Attempting uninstall: amazon-braket-default-simulator
    Found existing installation: amazon-braket-default-simulator 1.8.1
    Uninstalling amazon-braket-default-simulator-1.8.1:
      Successfully uninstalled amazon-braket-default-simulator-1.8.1
  Attempting uninstall: amazon-braket-sdk
    Found existing installation: amazon-braket-sdk 1.31.0
    Uninstalling amazon-braket-sdk-1.31.0:
      Successfully uninstalled amazon-braket-sdk-1.31.0
ERROR: pip's dependency resolver does not currently take into account all the packages that are installed. This behaviour is the source of the following dependency conflicts.
quantum-blackbird 0.4.0 requires antlr4-python3-runtime==4.8, but you have antlr4-python3-runtime 4.9.2 which is incompatible.
awscli 1.18.121 requires botocore==1.17.44, but you have botocore 1.28.5 which is incompatible.
awscli 1.18.121 requires s3transfer<0.4.0,>=0.3.0, but you have s3transfer 0.6.0 which is incompatible.
Successfully installed amazon-braket-default-simulator-1.10.0 amazon-braket-schemas-1.12.0 amazon-braket-sdk-1.33.0 antlr4-python3-runtime-4.9.2 boto3-1.25.5 botocore-1.28.5 mypy-extensions-0.4.3 openpulse-0.4.0 openqasm3-0.4.0 oqpy-0.1.0 pydantic-1.9.0 s3transfer-0.6.0

始める前に、2022年11月6日時点ではまだライブラリがそろっていませんでした、こちらの下記のライブラリを手作業で導入しました。使う方はコピペして使ってください。

import numpy as np
import matplotlib.pyplot as plt
import networkx as nx
from braket.ahs.atom_arrangement import SiteType
from braket.timings.time_series import TimeSeries
from braket.ahs.driving_field import DrivingField
from braket.ahs.shifting_field import ShiftingField
from braket.ahs.field import Field
from braket.ahs.pattern import Pattern
from collections import Counter

from typing import Dict, List, Tuple
from braket.tasks.analog_hamiltonian_simulation_quantum_task_result import AnalogHamiltonianSimulationQuantumTaskResult
from braket.ahs.atom_arrangement import AtomArrangement


def show_register(
    register: AtomArrangement, 
    blockade_radius: float=0.0, 
    what_to_draw: str="bond", 
    show_atom_index:bool=True
):
    """Plot the given register 
        Args:
            register (AtomArrangement): A given register
            blockade_radius (float): The blockade radius for the register. Default is 0
            what_to_draw (str): Either "bond" or "circle" to indicate the blockade region. 
                Default is "bond"
            show_atom_index (bool): Whether showing the indices of the atoms. Default is True
        
    """
    filled_sites = [site.coordinate for site in register if site.site_type == SiteType.FILLED]
    empty_sites = [site.coordinate for site in register if site.site_type == SiteType.VACANT]
    
    fig = plt.figure(figsize=(7, 7))
    if filled_sites:
        plt.plot(np.array(filled_sites)[:, 0], np.array(filled_sites)[:, 1], 'r.', ms=15, label='filled')
    if empty_sites:
        plt.plot(np.array(empty_sites)[:, 0], np.array(empty_sites)[:, 1], 'k.', ms=5, label='empty')
    plt.legend(bbox_to_anchor=(1.1, 1.05))
    
    if show_atom_index:
        for idx, site in enumerate(register):
            plt.text(*site.coordinate, f"  {idx}", fontsize=12)
    
    if blockade_radius > 0 and what_to_draw=="bond":
        for i in range(len(filled_sites)):
            for j in range(i+1, len(filled_sites)):            
                dist = np.linalg.norm(np.array(filled_sites[i]) - np.array(filled_sites[j]))
                if dist <= blockade_radius:
                    plt.plot([filled_sites[i][0], filled_sites[j][0]], [filled_sites[i][1], filled_sites[j][1]], 'b')
                    
    if blockade_radius > 0 and what_to_draw=="circle":
        for site in filled_sites:
            plt.gca().add_patch( plt.Circle((site[0],site[1]), blockade_radius/2, color="b", alpha=0.3) )
        plt.gca().set_aspect(1)
    plt.show()


def rabi_pulse(
    rabi_pulse_area: float, 
    omega_max: float,
    omega_slew_rate_max: float
) -> Tuple[List[float], List[float]]:
    """Get a time series for Rabi frequency with specified Rabi phase, maximum amplitude
    and maximum slew rate
        Args:
            rabi_pulse_area (float): Total area under the Rabi frequency time series
            omega_max (float): The maximum amplitude 
            omega_slew_rate_max (float): The maximum slew rate
        Returns:
            Tuple[List[float], List[float]]: A tuple containing the time points and values
                of the time series for the time dependent Rabi frequency
        Notes: By Rabi phase, it means the integral of the amplitude of a time-dependent 
            Rabi frequency, \int_0^T\Omega(t)dt, where T is the duration.
    """

    phase_threshold = omega_max**2 / omega_slew_rate_max
    if rabi_pulse_area <= phase_threshold:
        t_ramp = np.sqrt(rabi_pulse_area / omega_slew_rate_max)
        t_plateau = 0
    else:
        t_ramp = omega_max / omega_slew_rate_max
        t_plateau = (rabi_pulse_area / omega_max) - t_ramp
    t_pules = 2 * t_ramp + t_plateau
    time_points = [0, t_ramp, t_ramp + t_plateau, t_pules]
    amplitude_values = [0, t_ramp * omega_slew_rate_max, t_ramp * omega_slew_rate_max, 0]
    
    return time_points, amplitude_values


def get_counts(result: AnalogHamiltonianSimulationQuantumTaskResult) -> Dict[str, int]:
    """Aggregate state counts from AHS shot results
        Args:
            result (AnalogHamiltonianSimulationQuantumTaskResult): The result 
                from which the aggregated state counts are obtained
        Returns:
            Dict[str, int]: number of times each state configuration is measured
        Notes: We use the following convention to denote the state of an atom (site):
            e: empty site
            r: Rydberg state atom
            g: ground state atom
    """

    state_counts = Counter()
    states = ['e', 'r', 'g']
    for shot in result.measurements:
        pre = shot.pre_sequence
        post = shot.post_sequence
        state_idx = np.array(pre) * (1 + np.array(post))
        state = "".join(map(lambda s_idx: states[s_idx], state_idx))
        state_counts.update((state,))

    return dict(state_counts)


def get_drive(
    times: List[float], 
    amplitude_values: List[float], 
    detuning_values: List[float], 
    phase_values: List[float]
) -> DrivingField:
    """Get the driving field from a set of time points and values of the fields
        Args:
            times (List[float]): The time points of the driving field
            amplitude_values (List[float]): The values of the amplitude
            detuning_values (List[float]): The values of the detuning
            phase_values (List[float]): The values of the phase
        Returns:
            DrivingField: The driving field obtained
    """

    assert len(times) == len(amplitude_values)
    assert len(times) == len(detuning_values)
    assert len(times) == len(phase_values)
    
    amplitude = TimeSeries()
    detuning = TimeSeries()  
    phase = TimeSeries()    
    
    for t, amplitude_value, detuning_value, phase_value in zip(times, amplitude_values, detuning_values, phase_values):
        amplitude.put(t, amplitude_value)
        detuning.put(t, detuning_value)
        phase.put(t, phase_value) 

    drive = DrivingField(
        amplitude=amplitude, 
        detuning=detuning, 
        phase=phase
    )    
    
    return drive


def get_shift(times: List[float], values: List[float], pattern: List[float]) -> ShiftingField:
    """Get the shifting field from a set of time points, values and pattern
        Args:
            times (List[float]): The time points of the shifting field
            values (List[float]): The values of the shifting field
            pattern (List[float]): The pattern of the shifting field
        Returns:
            ShiftingField: The shifting field obtained
    """    
    assert len(times) == len(values)    
    
    magnitude = TimeSeries()
    for t, v in zip(times, values):
        magnitude.put(t, v)
    shift = ShiftingField(Field(magnitude, Pattern(pattern)))

    return shift


def show_global_drive(drive, axes=None, **plot_ops):
    """Plot the driving field
        Args:
            drive (DrivingField): The driving field to be plot
            axes: matplotlib axis to draw on
            **plot_ops: options passed to matplitlib.pyplot.plot
    """   

    data = {
        'amplitude [rad/s]': drive.amplitude.time_series,
        'detuning [rad/s]': drive.detuning.time_series,
        'phase [rad]': drive.phase.time_series,
    }


    if axes is None:
        fig, axes = plt.subplots(3, 1, figsize=(7, 7), sharex=True)

    for ax, data_name in zip(axes, data.keys()):
        if data_name == 'phase [rad]':
            ax.step(data[data_name].times(), data[data_name].values(), '.-', where='post',**plot_ops)
        else:
            ax.plot(data[data_name].times(), data[data_name].values(), '.-',**plot_ops)
        ax.set_ylabel(data_name)
        ax.grid(ls=':')
    axes[-1].set_xlabel('time [s]')
    plt.tight_layout()
    plt.show()


def show_local_shift(shift:ShiftingField):
    """Plot the shifting field
        Args:
            shift (ShiftingField): The shifting field to be plot
    """       
    data = shift.magnitude.time_series
    pattern = shift.magnitude.pattern.series
    
    plt.plot(data.times(), data.values(), '.-', label="pattern: " + str(pattern))
    plt.xlabel('time [s]')
    plt.ylabel('shift [rad/s]')
    plt.legend()
    plt.tight_layout()
    plt.show()

    
def show_drive_and_shift(drive: DrivingField, shift: ShiftingField):
    """Plot the driving and shifting fields
    
        Args:
            drive (DrivingField): The driving field to be plot
            shift (ShiftingField): The shifting field to be plot
    """        
    drive_data = {
        'amplitude [rad/s]': drive.amplitude.time_series,
        'detuning [rad/s]': drive.detuning.time_series,
        'phase [rad]': drive.phase.time_series,
    }
    
    fig, axes = plt.subplots(4, 1, figsize=(7, 7), sharex=True)
    for ax, data_name in zip(axes, drive_data.keys()):
        if data_name == 'phase [rad]':
            ax.step(drive_data[data_name].times(), drive_data[data_name].values(), '.-', where='post')
        else:
            ax.plot(drive_data[data_name].times(), drive_data[data_name].values(), '.-')
        ax.set_ylabel(data_name)
        ax.grid(ls=':')
        
    shift_data = shift.magnitude.time_series
    pattern = shift.magnitude.pattern.series   
    axes[-1].plot(shift_data.times(), shift_data.values(), '.-', label="pattern: " + str(pattern))
    axes[-1].set_ylabel('shift [rad/s]')
    axes[-1].set_xlabel('time [s]')
    axes[-1].legend()
    axes[-1].grid()
    plt.tight_layout()
    plt.show()


def get_avg_density(result: AnalogHamiltonianSimulationQuantumTaskResult) -> np.ndarray:
    """Get the average Rydberg densities from the result
        Args:
            result (AnalogHamiltonianSimulationQuantumTaskResult): The result 
                from which the aggregated state counts are obtained
        Returns: 
            ndarray: The average densities from the result
    """    

    measurements = result.measurements
    postSeqs = [measurement.post_sequence for measurement in measurements]
    postSeqs = 1 - np.array(postSeqs) # change the notation such 1 for rydberg state, and 0 for ground state
    
    avg_density = np.sum(postSeqs, axis=0)/len(postSeqs)
    
    return avg_density


def show_final_avg_density(result: AnalogHamiltonianSimulationQuantumTaskResult):
    """Showing a bar plot for the average Rydberg densities from the result
        Args:
            result (AnalogHamiltonianSimulationQuantumTaskResult): The result 
                from which the aggregated state counts are obtained
    """    
    avg_density = get_avg_density(result)
    
    plt.bar(range(len(avg_density)), avg_density)
    plt.xlabel("Indices of atoms")
    plt.ylabel("Average Rydberg density")
    plt.show()


def constant_time_series(other_time_series: TimeSeries, constant: float=0.0) -> TimeSeries:
    """Obtain a constant time series with the same time points as the given time series
        Args:
            other_time_series (TimeSeries): The given time series
        Returns:
            TimeSeries: A constant time series with the same time points as the given time series
    """
    ts = TimeSeries()
    for t in other_time_series.times():
        ts.put(t, constant)
    return ts


def concatenate_time_series(time_series_1: TimeSeries, time_series_2: TimeSeries) -> TimeSeries:
    """Concatenate two time series to a single time series
        Args:
            time_series_1 (TimeSeries): The first time series to be concatenated
            time_series_2 (TimeSeries): The second time series to be concatenated
        Returns:
            TimeSeries: The concatenated time series
    """
    assert time_series_1.values()[-1] == time_series_2.values()[0]
    
    duration_1 = time_series_1.times()[-1] - time_series_1.times()[0]
    
    new_time_series = TimeSeries()
    new_times = time_series_1.times() + [t + duration_1 - time_series_2.times()[0] for t in time_series_2.times()[1:]]
    new_values = time_series_1.values() + time_series_2.values()[1:]
    for t, v in zip(new_times, new_values):
        new_time_series.put(t, v)
    
    return new_time_series


def concatenate_drives(drive_1: DrivingField, drive_2: DrivingField) -> DrivingField:
    """Concatenate two driving fields to a single driving field
        Args:
            drive_1 (DrivingField): The first driving field to be concatenated
            drive_2 (DrivingField): The second driving field to be concatenated
        Returns:
            DrivingField: The concatenated driving field
    """    
    return DrivingField(
        amplitude=concatenate_time_series(drive_1.amplitude.time_series, drive_2.amplitude.time_series),
        detuning=concatenate_time_series(drive_1.detuning.time_series, drive_2.detuning.time_series),
        phase=concatenate_time_series(drive_1.phase.time_series, drive_2.phase.time_series)
    )


def concatenate_shifts(shift_1: ShiftingField, shift_2: ShiftingField) -> ShiftingField:
    """Concatenate two driving fields to a single driving field
        Args:
            shift_1 (ShiftingField): The first shifting field to be concatenated
            shift_2 (ShiftingField): The second shifting field to be concatenated
        Returns:
            ShiftingField: The concatenated shifting field
    """        
    assert shift_1.magnitude.pattern.series == shift_2.magnitude.pattern.series
    
    new_magnitude = concatenate_time_series(shift_1.magnitude.time_series, shift_2.magnitude.time_series)
    return ShiftingField(Field(new_magnitude, shift_1.magnitude.pattern))


def concatenate_drive_list(drive_list: List[DrivingField]) -> DrivingField:
    """Concatenate a list of driving fields to a single driving field
        Args:
            drive_list (List[DrivingField]): The list of driving fields to be concatenated
        Returns:
            DrivingField: The concatenated driving field
    """        
    drive = drive_list[0]
    for dr in drive_list[1:]:
        drive = concatenate_drives(drive, dr)
    return drive    


def concatenate_shift_list(shift_list: List[ShiftingField]) -> ShiftingField:
    """Concatenate a list of shifting fields to a single driving field
        Args:
            shift_list (List[ShiftingField]): The list of shifting fields to be concatenated
        Returns:
            ShiftingField: The concatenated shifting field
    """            
    shift = shift_list[0]
    for sf in shift_list[1:]:
        shift = concatenate_shifts(shift, sf)
    return shift


def plot_avg_density_2D(densities, register, with_labels = True, batch_index = None, batch_mapping = None, custom_axes = None):
    
    # get atom coordinates
    atom_coords = list(zip(register.coordinate_list(0), register.coordinate_list(1)))
    # convert all to micrometers
    atom_coords = [(atom_coord[0] * 10**6, atom_coord[1] * 10**6) for atom_coord in atom_coords]
    
    plot_avg_of_avgs = False
    plot_single_batch = False
        
    if batch_index is not None:
        if batch_mapping is not None:
                plot_single_batch = True
                # provided both batch and batch_mapping, show averages of single batch
                batch_subindices = batch_mapping[batch_index]
                batch_labels = {i:label for i,label in enumerate(batch_subindices)}
                # get proper positions
                pos = {i:tuple(coord) for i,coord in enumerate(list(np.array(atom_coords)[batch_subindices]))}
                # narrow down densities
                densities = np.array(densities)[batch_subindices]
                
        else:
            raise Exception("batch_mapping required to index into")
    else:
        if batch_mapping is not None:
            plot_avg_of_avgs = True
            # just need the coordinates for first batch_mapping
            subcoordinates = np.array(atom_coords)[batch_mapping[(0,0)]]
            pos = {i:coord for i,coord in enumerate(subcoordinates)}                                     
        else:
            # If both not provided do standard FOV
            # handle 1D case
            pos = {i:coord for i,coord in enumerate(atom_coords)}
           
    # get colors
    vmin = 0
    vmax = 1
    cmap = plt.cm.Blues
    
    # construct graph
    g = nx.Graph()
    g.add_nodes_from(list(range(len(densities))))
    
    # construct plot
    if custom_axes is None:
        fig, ax = plt.subplots()
    else:
        ax = custom_axes
    
    nx.draw(g, 
            pos,
            node_color=densities,
            cmap=cmap,
            node_shape="o",
            vmin=vmin,
            vmax=vmax,
            font_size=9,
            with_labels=with_labels,
            labels= batch_labels if plot_single_batch else None,
            ax = custom_axes if custom_axes is not None else ax)
        
    ## Set axes
    ax.set_axis_on()
    ax.tick_params(left=True, 
                   bottom=True, 
                   top=True,
                   right=True,
                   labelleft=True, 
                   labelbottom=True, 
                   # labeltop=True,
                   # labelright=True,
                   direction="in")
    ## Set colorbar
    sm = plt.cm.ScalarMappable(cmap=cmap, norm=plt.Normalize(vmin=vmin, vmax=vmax))
    sm.set_array([])

    
    ax.ticklabel_format(style="sci", useOffset=False)
    
    # set titles on x and y axes
    plt.xlabel("x [μm]")
    plt.ylabel("y [μm]")
    
    
    if plot_avg_of_avgs:
        cbar_label = "Averaged Rydberg Density"
    else:
        cbar_label = "Rydberg Density"
        
    plt.colorbar(sm, ax=ax, label=cbar_label)

早速コードが入ってるか確認してみます。

from braket.aws import AwsDevice

device = AwsDevice("arn:aws:braket:us-east-1::device/qpu/quera/Aquila")
print(device.properties)
service=DeviceServiceProperties(braketSchemaHeader=BraketSchemaHeader(name='braket.device_schema.device_service_properties', version='1'), executionWindows=[DeviceExecutionWindow(executionDay=<ExecutionDay.TUESDAY: 'Tuesday'>, windowStartHour=datetime.time(16, 0), windowEndHour=datetime.time(20, 0)), DeviceExecutionWindow(executionDay=<ExecutionDay.WEDNESDAY: 'Wednesday'>, windowStartHour=datetime.time(16, 0), windowEndHour=datetime.time(20, 0)), DeviceExecutionWindow(executionDay=<ExecutionDay.THURSDAY: 'Thursday'>, windowStartHour=datetime.time(16, 0), windowEndHour=datetime.time(18, 0))], shotsRange=(1, 1000), deviceCost=DeviceCost(price=0.01, unit='shot'), deviceDocumentation=DeviceDocumentation(imageUrl='https://a.b.cdn.console.awsstatic.com/59534b58c709fc239521ef866db9ea3f1aba73ad3ebcf60c23914ad8c5c5c878/a6cfc6fca26cf1c2e1c6.png', summary='Analog quantum processor based on neutral atom arrays', externalDocumentationUrl='https://www.quera.com/aquila'), deviceLocation='Boston, USA', updatedAt=datetime.datetime(2022, 10, 31, 21, 30, tzinfo=datetime.timezone.utc)) action={<DeviceActionType.AHS: 'braket.ir.ahs.program'>: DeviceActionProperties(version=['1'], actionType=<DeviceActionType.AHS: 'braket.ir.ahs.program'>)} deviceParameters={} braketSchemaHeader=BraketSchemaHeader(name='braket.device_schema.quera.quera_device_capabilities', version='1') paradigm=QueraAhsParadigmProperties(braketSchemaHeader=BraketSchemaHeader(name='braket.device_schema.quera.quera_ahs_paradigm_properties', version='1'), qubitCount=256, lattice=Lattice(area=Area(width=Decimal('0.000075'), height=Decimal('0.000076')), geometry=Geometry(spacingRadialMin=Decimal('0.000004'), spacingVerticalMin=Decimal('0.000004'), positionResolution=Decimal('1E-7'), numberSitesMax=256)), rydberg=Rydberg(c6Coefficient=Decimal('5.42E-24'), rydbergGlobal=RydbergGlobal(rabiFrequencyRange=(Decimal('0.0'), Decimal('15800000.0')), rabiFrequencyResolution=Decimal('400.0'), rabiFrequencySlewRateMax=Decimal('250000000000000.0'), detuningRange=(Decimal('-125000000.0'), Decimal('125000000.0')), detuningResolution=Decimal('0.2'), detuningSlewRateMax=Decimal('2500000000000000.0'), phaseRange=(Decimal('-99.0'), Decimal('99.0')), phaseResolution=Decimal('5E-7'), timeResolution=Decimal('1E-9'), timeDeltaMin=Decimal('5E-8'), timeMin=Decimal('0.0'), timeMax=Decimal('0.000004'))), performance=Performance(lattice=PerformanceLattice(positionErrorAbs=Decimal('1E-7')), rydberg=PerformanceRydberg(rydbergGlobal=PerformanceRydbergGlobal(rabiFrequencyErrorRel=Decimal('0.02')))))

マシンは登録されているようです。マシンのプロパティが取れました。

原子の配置

ちょっと様子を見てみましょう。まずは原子を並べてみます。11量子ビット準備して6.1マイクロメートル間隔で並べてみます。

from braket.ahs.atom_arrangement import AtomArrangement
import numpy as np

#原子間距離
a = 6.1e-6

#量子ビット・原子数
N_atoms = 11

#並べる
register = AtomArrangement()
for i in range(N_atoms):
    register.add([0.0, i*a])
    
vars(register)
{'_sites': [AtomArrangementItem(coordinate=(0.0, 0.0), site_type=<SiteType.FILLED: 'Filled'>),
  AtomArrangementItem(coordinate=(0.0, 6.1e-06), site_type=<SiteType.FILLED: 'Filled'>),
  AtomArrangementItem(coordinate=(0.0, 1.22e-05), site_type=<SiteType.FILLED: 'Filled'>),
  AtomArrangementItem(coordinate=(0.0, 1.83e-05), site_type=<SiteType.FILLED: 'Filled'>),
  AtomArrangementItem(coordinate=(0.0, 2.44e-05), site_type=<SiteType.FILLED: 'Filled'>),
  AtomArrangementItem(coordinate=(0.0, 3.05e-05), site_type=<SiteType.FILLED: 'Filled'>),
  AtomArrangementItem(coordinate=(0.0, 3.66e-05), site_type=<SiteType.FILLED: 'Filled'>),
  AtomArrangementItem(coordinate=(0.0, 4.27e-05), site_type=<SiteType.FILLED: 'Filled'>),
  AtomArrangementItem(coordinate=(0.0, 4.88e-05), site_type=<SiteType.FILLED: 'Filled'>),
  AtomArrangementItem(coordinate=(0.0, 5.49e-05), site_type=<SiteType.FILLED: 'Filled'>),
  AtomArrangementItem(coordinate=(0.0, 6.1e-05), site_type=<SiteType.FILLED: 'Filled'>)]}

取れましたね。原子を自分で位置を指定して並べるのが新しいですね。。。

show_register(register)
<Figure size 504x504 with 1 Axes>

image

次に三角形です。底辺を準備してから頂点を三角関数で指定します。

#三角形の一辺
a = 5.5e-6

#並べます
register = AtomArrangement()
register.add([0, 0])
register.add([a, 0.0])
register.add([0.5 * a, np.sqrt(3)/2 * a]);

show_register(register)
<Figure size 504x504 with 1 Axes>

image

次はもうちょい数を増やしてみます。原子三つのグループを25用意します。今回はリュードベリ半径の及ぶ原子同士の表示もします。

#小グループ
separation = 5e-6

#大グループ
block_separation = 15e-6

#縦横のグループ数
k_max = 5
m_max = 5

#配置
register = AtomArrangement()
for k in range(k_max):
    for m in range(m_max):
        register.add((block_separation*m, block_separation*k + separation/np.sqrt(3)))
        register.add((block_separation*m-separation/2, block_separation*k - separation/(2*np.sqrt(3))))
        register.add((block_separation*m+separation/2, block_separation*k - separation/(2*np.sqrt(3))))        

#リュードベリブロッケード半径を指定しています
show_register(register, show_atom_index=False, blockade_radius= 1.5 * separation)
<Figure size 504x504 with 1 Axes>

image

時間発展

次に時間発展のハミルトニアンシミュレーションを見てみます。オメガやデルタでスケジュールを決めます。

from braket.timings.time_series import TimeSeries
from braket.ahs.driving_field import DrivingField
from pprint import pprint as pp

capabilities = device.properties.paradigm
pp(capabilities.dict())

rydberg = capabilities.rydberg
pp(rydberg.dict())

omega_const = 1.5e7   # rad / s
rabi_pulse_area = np.pi/np.sqrt(3) # rad
omega_slew_rate_max = float(rydberg.rydbergGlobal.rabiFrequencySlewRateMax) # rad/s^2

time_points, amplitude_values = rabi_pulse(rabi_pulse_area, omega_const, 0.99 * omega_slew_rate_max)

amplitude = TimeSeries()
for t, v in zip(time_points, amplitude_values):
    amplitude.put(t, v)

detuning = constant_time_series(amplitude, 0.0) 
phase = constant_time_series(amplitude, 0.0) 

    
drive = DrivingField(
    amplitude=amplitude, 
    detuning=detuning, 
    phase=phase
)
{'braketSchemaHeader': {'name': 'braket.device_schema.quera.quera_ahs_paradigm_properties',
                        'version': '1'},
 'lattice': {'area': {'height': Decimal('0.000076'),
                      'width': Decimal('0.000075')},
             'geometry': {'numberSitesMax': 256,
                          'positionResolution': Decimal('1E-7'),
                          'spacingRadialMin': Decimal('0.000004'),
                          'spacingVerticalMin': Decimal('0.000004')}},
 'performance': {'lattice': {'positionErrorAbs': Decimal('1E-7')},
                 'rydberg': {'rydbergGlobal': {'rabiFrequencyErrorRel': Decimal('0.02')}}},
 'qubitCount': 256,
 'rydberg': {'c6Coefficient': Decimal('5.42E-24'),
             'rydbergGlobal': {'detuningRange': (Decimal('-125000000.0'),
                                                 Decimal('125000000.0')),
                               'detuningResolution': Decimal('0.2'),
                               'detuningSlewRateMax': Decimal('2500000000000000.0'),
                               'phaseRange': (Decimal('-99.0'),
                                              Decimal('99.0')),
                               'phaseResolution': Decimal('5E-7'),
                               'rabiFrequencyRange': (Decimal('0.0'),
                                                      Decimal('15800000.0')),
                               'rabiFrequencyResolution': Decimal('400.0'),
                               'rabiFrequencySlewRateMax': Decimal('250000000000000.0'),
                               'timeDeltaMin': Decimal('5E-8'),
                               'timeMax': Decimal('0.000004'),
                               'timeMin': Decimal('0.0'),
                               'timeResolution': Decimal('1E-9')}}}
{'c6Coefficient': Decimal('5.42E-24'),
 'rydbergGlobal': {'detuningRange': (Decimal('-125000000.0'),
                                     Decimal('125000000.0')),
                   'detuningResolution': Decimal('0.2'),
                   'detuningSlewRateMax': Decimal('2500000000000000.0'),
                   'phaseRange': (Decimal('-99.0'), Decimal('99.0')),
                   'phaseResolution': Decimal('5E-7'),
                   'rabiFrequencyRange': (Decimal('0.0'),
                                          Decimal('15800000.0')),
                   'rabiFrequencyResolution': Decimal('400.0'),
                   'rabiFrequencySlewRateMax': Decimal('250000000000000.0'),
                   'timeDeltaMin': Decimal('5E-8'),
                   'timeMax': Decimal('0.000004'),
                   'timeMin': Decimal('0.0'),
                   'timeResolution': Decimal('1E-9')}}
show_global_drive(drive)
<Figure size 504x504 with 3 Axes>

image

実機実行!

早速実機へ投げる方法を見てみます。
まずは先ほどの配置と時間発展の情報を格納します。

from braket.ahs.hamiltonian import Hamiltonian
from braket.ahs.analog_hamiltonian_simulation import AnalogHamiltonianSimulation

ahs_program = AnalogHamiltonianSimulation(
    register=register, 
    hamiltonian=drive
)

そして、次は問題をタスクで投げられるように変換

discretized_ahs_program = ahs_program.discretize(device)

例題ではここでタスクを投げられる回数をチェックしています。1000回まで投げられるので100回に指定。金額に気を付けてください。

device.properties.service.shotsRange
(1, 1000)
n_shots = 100

そしたら実行です。これで完了!簡単ですね!

task = device.run(discretized_ahs_program, shots=n_shots)

タスクのIDを確認したりステータスを確認します。記事投稿時は日曜日ですのでちょっとまだ動いてないのでタスクが作られただけです。

metadata = task.metadata()
task_arn = metadata['quantumTaskArn']
task_status = metadata['status']

print(f"ARN: {task_arn}")
print(f"status: {task_status}")
ARN: arn:aws:braket:us-east-1:722034924650:quantum-task/75a1751d-f99c-45b9-8181-f304c0ce4df2
status: CREATED

時間外の時には数日たってからまた確認に来ないといけないのでタスクIDからジョブをとれるようにします。

from braket.aws import AwsQuantumTask
task = AwsQuantumTask(arn="arn:aws:braket:us-east-1:722034924650:quantum-task/75a1751d-f99c-45b9-8181-f304c0ce4df2") 
task_status = metadata['status']
print(f"status: {task_status}")
status: CREATED

まだジョブは投げられないようなので終了したら回収します。
次に計算結果の取り出しを確認します。
計算結果の処理を記事として書きたいのですが、計算結果が回収できないので次の機会にしたいと思います。

© 2025, blueqat Inc. All rights reserved