Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
from decimal import Decimal
from typing import Optional

from braket.analog_hamiltonian_simulator.rydberg.validators.capabilities_constants import (
CapabilitiesConstants,
)


class DeviceCapabilitiesConstants(CapabilitiesConstants):
MAX_SITES: int
SITE_PRECISION: Decimal
MAX_FILLED_SITES: int
MIN_ROW_DISTANCE: Decimal

GLOBAL_TIME_PRECISION: Decimal
GLOBAL_AMPLITUDE_VALUE_PRECISION: Decimal
GLOBAL_AMPLITUDE_SLOPE_MAX: Decimal
GLOBAL_MIN_TIME_SEPARATION: Decimal
GLOBAL_DETUNING_VALUE_PRECISION: Decimal
GLOBAL_DETUNING_SLOPE_MAX: Decimal
GLOBAL_PHASE_VALUE_MIN: Decimal
GLOBAL_PHASE_VALUE_MAX: Decimal
GLOBAL_PHASE_VALUE_PRECISION: Decimal

LOCAL_RYDBERG_CAPABILITIES: bool = False
LOCAL_MAGNITUDE_SLOPE_MAX: Optional[Decimal]
LOCAL_MIN_DISTANCE_BETWEEN_SHIFTED_SITES: Optional[Decimal]
LOCAL_TIME_PRECISION: Optional[Decimal]
LOCAL_MIN_TIME_SEPARATION: Optional[Decimal]
LOCAL_MAGNITUDE_SEQUENCE_VALUE_MIN: Optional[Decimal]
LOCAL_MAGNITUDE_SEQUENCE_VALUE_MAX: Optional[Decimal]
LOCAL_MAX_NONZERO_PATTERN_VALUES: Optional[Decimal]

MAGNITUDE_PATTERN_VALUE_MIN: Optional[Decimal]
MAGNITUDE_PATTERN_VALUE_MAX: Optional[Decimal]
MAX_NET_DETUNING: Optional[Decimal]
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
# Copyright Amazon.com Inc. or its affiliates. All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License"). You
# may not use this file except in compliance with the License. A copy of
# the License is located at
#
# http://aws.amazon.com/apache2.0/
#
# or in the "license" file accompanying this file. This file is
# distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF
# ANY KIND, either express or implied. See the License for the specific
# language governing permissions and limitations under the License.

from braket.ir.ahs.program_v1 import Program

from braket.analog_hamiltonian_simulator.rydberg.validators.device_capabilities_constants import (
DeviceCapabilitiesConstants,
)
from braket.analog_hamiltonian_simulator.rydberg.validators.device_validators import (
DeviceAtomArrangementValidator,
DeviceDrivingFieldValidator,
DeviceHamiltonianValidator,
DeviceLocalDetuningValidator,
)
from braket.analog_hamiltonian_simulator.rydberg.validators.physical_field import (
PhysicalFieldValidator,
)
from braket.analog_hamiltonian_simulator.rydberg.validators.program import ProgramValidator
from braket.analog_hamiltonian_simulator.rydberg.validators.times_series import TimeSeriesValidator


def validate_program(program: Program, device_capabilities: DeviceCapabilitiesConstants) -> None:
"""
Validate the analog Hamiltonian simulation program has only one driving and shifting field,
and all the sequences have the same last time point.

Args:
program (Program): An analog Hamiltonian simulation program
device_capabilities (CapabilitiesConstants): The capability constants for the simulator
"""

ProgramValidator(capabilities=device_capabilities, **program.dict())
DeviceAtomArrangementValidator(
capabilities=device_capabilities, **program.setup.ahs_register.dict()
)
DeviceHamiltonianValidator(
LOCAL_RYDBERG_CAPABILITIES=device_capabilities.LOCAL_RYDBERG_CAPABILITIES,
**program.hamiltonian.dict()
)
for d_fields in program.hamiltonian.drivingFields:
DeviceDrivingFieldValidator(capabilities=device_capabilities, **d_fields.dict())
amplitude = d_fields.amplitude
phase = d_fields.phase
detuning = d_fields.detuning

PhysicalFieldValidator(**amplitude.dict())
TimeSeriesValidator(capabilities=device_capabilities, **amplitude.time_series.dict())

PhysicalFieldValidator(**phase.dict())
TimeSeriesValidator(capabilities=device_capabilities, **phase.time_series.dict())

PhysicalFieldValidator(**detuning.dict())
TimeSeriesValidator(capabilities=device_capabilities, **detuning.time_series.dict())
for s_fields in program.hamiltonian.localDetuning:
DeviceLocalDetuningValidator(capabilities=device_capabilities, **s_fields.dict())
magnitude = s_fields.magnitude
PhysicalFieldValidator(**magnitude.dict())
TimeSeriesValidator(capabilities=device_capabilities, **magnitude.time_series.dict())
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
from braket.analog_hamiltonian_simulator.rydberg.validators.device_validators.device_atom_arrangement import ( # noqa: E501 F401
DeviceAtomArrangementValidator,
)
from braket.analog_hamiltonian_simulator.rydberg.validators.device_validators.device_driving_field import ( # noqa: E501 F401
DeviceDrivingFieldValidator,
)
from braket.analog_hamiltonian_simulator.rydberg.validators.device_validators.device_hamiltonian import ( # noqa: E501 F401
DeviceHamiltonianValidator,
)
from braket.analog_hamiltonian_simulator.rydberg.validators.device_validators.device_local_detuning import ( # noqa: E501 F401
DeviceLocalDetuningValidator,
)
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
from decimal import Decimal
from typing import Tuple

from pydantic.v1.class_validators import root_validator

from braket.analog_hamiltonian_simulator.rydberg.validators.atom_arrangement import (
AtomArrangementValidator,
)
from braket.analog_hamiltonian_simulator.rydberg.validators.device_capabilities_constants import (
DeviceCapabilitiesConstants,
)


def _y_distance(site_1: Tuple[Decimal, Decimal], site_2: Tuple[Decimal, Decimal]) -> Decimal:
# Compute the y-separation between two sets of 2-D points, (x1, y1) and (x2, y2)

return Decimal(abs(site_1[1] - site_2[1]))


class DeviceAtomArrangementValidator(AtomArrangementValidator):
capabilities: DeviceCapabilitiesConstants

@root_validator(pre=True, skip_on_failure=True)
def sites_not_empty(cls, values):
sites = values["sites"]
if not sites:
raise ValueError("Sites can not be empty.")
return values

# The maximum allowable precision in the coordinates is SITE_PRECISION
@root_validator(pre=True, skip_on_failure=True)
def sites_defined_with_right_precision(cls, values):
sites = values["sites"]
capabilities = values["capabilities"]
for idx, s in enumerate(sites):
if not all(
[Decimal(str(coordinate)) % capabilities.SITE_PRECISION == 0 for coordinate in s]
):
raise ValueError(
f"Coordinates {idx}({s}) is defined with too high precision;"
f"they must be multiples of {capabilities.SITE_PRECISION} meters"
)
return values

# Number of sites must not exceeds MAX_SITES
@root_validator(pre=True, skip_on_failure=True)
def sites_not_too_many(cls, values):
sites = values["sites"]
capabilities = values["capabilities"]
num_sites = len(sites)
if num_sites > capabilities.MAX_SITES:
raise ValueError(
f"There are too many sites ({num_sites}); there must be at most "
f"{capabilities.MAX_SITES} sites"
)
return values

# The y coordinates of any two lattice sites must either be equal
# or differ by at least MIN_ROW_DISTANCE.
@root_validator(pre=True, skip_on_failure=True)
def sites_in_rows(cls, values):
sites = values["sites"]
capabilities = values["capabilities"]
sorted_sites = sorted(sites, key=lambda xy: xy[1])
min_allowed_distance = capabilities.MIN_ROW_DISTANCE
if capabilities.LOCAL_RYDBERG_CAPABILITIES:
min_allowed_distance = Decimal("0.000002")
for s1, s2 in zip(sorted_sites[:-1], sorted_sites[1:]):
row_distance = _y_distance(s1, s2)
if row_distance == 0:
continue
if row_distance < min_allowed_distance:
raise ValueError(
f"Sites {s1} and site {s2} have y-separation ({row_distance}). It must "
f"either be exactly zero or not smaller than {min_allowed_distance} meters"
)
return values

# The number of filled lattice sites must not exceed MAX_FILLED_SITES.
@root_validator(pre=True, skip_on_failure=True)
def atom_number_limit(cls, values):
filling = values["filling"]
capabilities = values["capabilities"]
qubits = sum(filling)
if qubits > capabilities.MAX_FILLED_SITES:
raise ValueError(
f"Filling has {qubits} '1' entries; it must have not "
f"more than {capabilities.MAX_FILLED_SITES}"
)
return values
Original file line number Diff line number Diff line change
@@ -0,0 +1,167 @@
from pydantic.v1.class_validators import root_validator

from braket.analog_hamiltonian_simulator.rydberg.validators.device_capabilities_constants import (
DeviceCapabilitiesConstants,
)
from braket.analog_hamiltonian_simulator.rydberg.validators.driving_field import (
DrivingFieldValidator,
)
from braket.analog_hamiltonian_simulator.rydberg.validators.field_validator_util import (
validate_max_absolute_slope,
validate_time_precision,
validate_time_separation,
validate_value_precision,
validate_value_range_with_warning,
)
from braket.analog_hamiltonian_simulator.rydberg.validators.physical_field import PhysicalField


class DeviceDrivingFieldValidator(DrivingFieldValidator):
capabilities: DeviceCapabilitiesConstants

# Amplitude must start and end at 0.0
@root_validator(pre=True, skip_on_failure=True)
def amplitude_start_and_end_values(cls, values):
amplitude = values["amplitude"]
time_series = amplitude["time_series"]
time_series_values = time_series["values"]
if time_series_values:
start_value, end_value = time_series_values[0], time_series_values[-1]
if start_value != 0 or end_value != 0:
raise ValueError(
f"The values of the Rabi frequency at the first and last time points are "
f"{start_value}, {end_value}; they both must be nonzero."
)
return values

@root_validator(pre=True, skip_on_failure=True)
def amplitude_time_precision_is_correct(cls, values):
amplitude = values["amplitude"]
capabilities = values["capabilities"]
amplitude_obj = PhysicalField.parse_obj(amplitude)
validate_time_precision(
amplitude_obj.time_series.times, capabilities.GLOBAL_TIME_PRECISION, "amplitude"
)
return values

@root_validator(pre=True, skip_on_failure=True)
def amplitude_timepoint_not_too_close(cls, values):
amplitude = values["amplitude"]
capabilities = values["capabilities"]
validate_time_separation(
amplitude["time_series"]["times"], capabilities.GLOBAL_MIN_TIME_SEPARATION, "amplitude"
)
return values

@root_validator(pre=True, skip_on_failure=True)
def amplitude_value_precision_is_correct(cls, values):
amplitude = values["amplitude"]
capabilities = values["capabilities"]
amplitude_obj = PhysicalField.parse_obj(amplitude)
validate_value_precision(
amplitude_obj.time_series.values,
capabilities.GLOBAL_AMPLITUDE_VALUE_PRECISION,
"amplitude",
)
return values

@root_validator(pre=True, skip_on_failure=True)
def amplitude_slopes_not_too_steep(cls, values):
amplitude = values["amplitude"]
capabilities = values["capabilities"]
amplitude_times = amplitude["time_series"]["times"]
amplitude_values = amplitude["time_series"]["values"]
if amplitude_times and amplitude_values:
validate_max_absolute_slope(
amplitude_times,
amplitude_values,
capabilities.GLOBAL_AMPLITUDE_SLOPE_MAX,
"amplitude",
)
return values

@root_validator(pre=True, skip_on_failure=True)
def phase_time_precision_is_correct(cls, values):
phase = values["phase"]
capabilities = values["capabilities"]
phase_obj = PhysicalField.parse_obj(phase)
validate_time_precision(
phase_obj.time_series.times, capabilities.GLOBAL_TIME_PRECISION, "phase"
)
return values

@root_validator(pre=True, skip_on_failure=True)
def phase_timepoint_not_too_close(cls, values):
phase = values["phase"]
capabilities = values["capabilities"]
validate_time_separation(
phase["time_series"]["times"], capabilities.GLOBAL_MIN_TIME_SEPARATION, "phase"
)
return values

@root_validator(pre=True, skip_on_failure=True)
def phase_values_start_with_0(cls, values):
phase = values["phase"]
phase_values = phase["time_series"]["values"]
if phase_values:
if phase_values[0] != 0:
raise ValueError(
f"The first value of of driving field phase is {phase_values[0]}; it must be 0."
)
return values

@root_validator(pre=True, skip_on_failure=True)
def phase_values_within_range(cls, values):
phase = values["phase"]
capabilities = values["capabilities"]
validate_value_range_with_warning(
phase["time_series"]["values"],
capabilities.GLOBAL_PHASE_VALUE_MIN,
capabilities.GLOBAL_PHASE_VALUE_MAX,
"phase",
)
return values

@root_validator(pre=True, skip_on_failure=True)
def phase_value_precision_is_correct(cls, values):
phase = values["phase"]
capabilities = values["capabilities"]
phase_obj = PhysicalField.parse_obj(phase)
validate_value_precision(
phase_obj.time_series.values, capabilities.GLOBAL_PHASE_VALUE_PRECISION, "phase"
)
return values

@root_validator(pre=True, skip_on_failure=True)
def detuning_time_precision_is_correct(cls, values):
detuning = values["detuning"]
capabilities = values["capabilities"]
detuning_obj = PhysicalField.parse_obj(detuning)
validate_time_precision(
detuning_obj.time_series.times, capabilities.GLOBAL_TIME_PRECISION, "detuning"
)
return values

@root_validator(pre=True, skip_on_failure=True)
def detuning_timepoint_not_too_close(cls, values):
detuning = values["detuning"]
capabilities = values["capabilities"]
validate_time_separation(
detuning["time_series"]["times"], capabilities.GLOBAL_MIN_TIME_SEPARATION, "detuning"
)
return values

@root_validator(pre=True, skip_on_failure=True)
def detuning_slopes_not_too_steep(cls, values):
detuning = values["detuning"]
capabilities = values["capabilities"]
detuning_times = detuning["time_series"]["times"]
detuning_values = detuning["time_series"]["values"]
if detuning_times and detuning_values:
validate_max_absolute_slope(
detuning_times,
detuning_values,
capabilities.GLOBAL_DETUNING_SLOPE_MAX,
"detuning",
)
return values
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
from pydantic.v1.class_validators import root_validator

from braket.analog_hamiltonian_simulator.rydberg.validators.hamiltonian import HamiltonianValidator


class DeviceHamiltonianValidator(HamiltonianValidator):
LOCAL_RYDBERG_CAPABILITIES: bool = False

@root_validator(pre=True, skip_on_failure=True)
def max_zero_local_detuning(cls, values):
LOCAL_RYDBERG_CAPABILITIES = values["LOCAL_RYDBERG_CAPABILITIES"]
local_detuning = values.get("localDetuning", [])
if not LOCAL_RYDBERG_CAPABILITIES and len(local_detuning):
raise ValueError(
f"Local detuning cannot be specified; \
{len(local_detuning)} are given. Specifying local \
detuning is an experimental capability, use Braket Direct to request access."
)
return values
Loading