-
Notifications
You must be signed in to change notification settings - Fork 21
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
feat: Introduce Device AHS validator for additional device information. #271
Changes from 7 commits
818e37e
dfe7dcd
315e578
c6741d9
c00563c
24ebf9a
079f1b4
746ab2c
b6da508
8620b84
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
<ExceptionInfo ValidationError(model='DeviceHamiltonianValidator', errors=[{'loc': ('__root__',), 'msg': 'Local detuning cannot be sp...pecifying local detuning is an experimental capability, use Braket Direct to request access.', 'type': 'value_error'}]) tblen=2> |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,15 @@ | ||
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_capabilities_constants import ( # noqa: E501 F401 | ||
DeviceCapabilitiesConstants, | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. These two imports are going to cause circular import issues. One fix would be to swap the ordering of L1 and L4 but I would encourage you to see what other options there would be to resolve the circular import issue. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. In 746ab2c I opt to move Please let me know if this approach is appropriate! |
||
) | ||
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_validators 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;\ | ||
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\ | ||
{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\ | ||
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; is must have not\ | ||
Altanali marked this conversation as resolved.
Show resolved
Hide resolved
|
||
more than {capabilities.MAX_FILLED_SITES}" | ||
) | ||
return values |
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,167 @@ | ||||||
from pydantic.v1.class_validators import root_validator | ||||||
|
||||||
from braket.analog_hamiltonian_simulator.rydberg.validators.device_validators 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 \ | ||||||
{start_value}, {end_value}; they both must be both 0." | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
|
||||||
) | ||||||
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,20 @@ | ||
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: | ||
if len(local_detuning) > 0: | ||
Altanali marked this conversation as resolved.
Show resolved
Hide resolved
|
||
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 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This can be removed
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Removed!