diff --git a/Project.toml b/Project.toml index f87bd33..7182c7d 100644 --- a/Project.toml +++ b/Project.toml @@ -8,8 +8,6 @@ AWS = "fbe9abb3-538b-5e4e-ba9e-bc94f4f92ebc" Configurations = "5218b696-f38b-4ac9-8b61-a12ec717816d" Crayons = "a8cc5b0e-0ffa-5ad4-8c14-923d3ee1735f" Dates = "ade2ca70-3891-5945-98fb-dc099432e06a" -Expronicon = "6b7a57c9-7cc1-4fdf-b7f5-e857abae3636" -ExproniconLite = "55351af7-c7e9-48d6-89ff-24e801d99491" GarishPrint = "b0ab02a7-8576-43f7-aa76-eaa7c3897c54" HTTP = "cd3eb016-35fb-5094-929b-558a96fad6f3" JSON = "682c06a0-de6a-54ab-a142-c8b1cf79cde6" @@ -17,12 +15,17 @@ REPL = "3fa0cd96-eef1-5676-8a61-b3b8758bbffb" UUIDs = "cf7118a7-6976-5b1a-9a39-7adc72f591a4" [compat] -AWS = "1.39" +AWS = "1" Configurations = "0.16" +Crayons = "4" +GarishPrint = "0.2" +HTTP = "0.9" +JSON = "0.21" julia = "1" [extras] +BrokenRecord = "bdd55f5b-6e67-4da1-a080-6086e55655a0" Test = "8dfed614-e22c-5e08-85e1-65c5234f0b40" [targets] -test = ["Test"] +test = ["Test", "BrokenRecord"] diff --git a/src/AWSBraket.jl b/src/AWSBraket.jl index 9096610..bfeef6a 100644 --- a/src/AWSBraket.jl +++ b/src/AWSBraket.jl @@ -25,12 +25,57 @@ function parse_device_info(d) return from_dict_validate(Schema.DeviceInfo, d)::Schema.DeviceInfo end +const DOC_AWS_CONFIG = """ +# AWS Service Configuration + +You can specify custom configuration via `aws_config` keyword argument, +to select another regions or use a different account, default +is `AWS.global_aws_config()`, e.g you can choose a different region in +this config + +```julia +using AWS +config = AWSConfig(;region="us-west-1") +``` + +See the [AWS Julia Interface](https://github.com/JuliaCloud/AWS.jl) documentation +for more advanced usage. +""" + +""" + get_device(arn::String; aws_config=AWS.global_aws_config()) + +Retrieves the devices available in Amazon Braket. + +# Arguments + +- `arn`: The ARN of the device to retrieve. + +$(DOC_AWS_CONFIG) +""" function get_device(arn::String; aws_config=AWS.global_aws_config()) # NOTE: the AWS dev docs d = Braket.get_device(HTTP.escapeuri(arn); aws_config) return parse_device_info(d) end +""" + search_devices(filters=[]; max_results::Maybe{Int}=nothing, next_token::Maybe{String}=nothing, aws_config=AWS.global_aws_config()) + +Searches for devices using the specified filters. + +# Arguments + +- `filters`: The filter values to use to search for a device. + +# Keyword Arguments (Optional) + +- `max_results`: The maximum number of results to return in the response. +- `next_token`: A token used for pagination of results returned in the response. Use the token returned from + the previous request continue results where the previous request ended. + +$(DOC_AWS_CONFIG) +""" function search_devices(filters=[]; max_results::Maybe{Int}=nothing, next_token::Maybe{String}=nothing, aws_config=AWS.global_aws_config()) params = Dict{String, Any}() max_results === nothing || (params["maxResults"] = max_results) @@ -40,6 +85,11 @@ function search_devices(filters=[]; max_results::Maybe{Int}=nothing, next_token: return map(parse_device_info, d["devices"]), get(d, "nextToken", nothing) end +""" + make_device_parameters(program::Schema.Program, arn::String, disable_qubit_rewiring::Bool) + +Create device parameters from given `program`, device `arn` and `disable_qubit_rewiring` option. +""" function make_device_parameters(program::Schema.Program, arn::String, disable_qubit_rewiring::Bool) paradigm_parameters = Schema.GateModelParameters( qubitCount=Schema.count_qubits(program), @@ -63,6 +113,29 @@ function make_device_parameters(program::Schema.Program, arn::String, disable_qu return device_parameters end +""" + create_quantum_task(;kw...) + +Create a quantum task in braket service. + +# Required Keyword Arguments + +- `program::Schema.Program`: the program one wants to execute. +- `device_arn::String`: device arn. +- `bucket::String`: S3 bucket to store the results in. +- `folder::String`: S3 bucket folder. + +# Optional Keyword Arguments + +- `disable_qubit_rewiring::Bool`: disable qubit rewiring in braket service, default is `false`. +- `device_parameters`: device parameters, such as [`Schema.IonqDeviceParameters`](@ref), + [`Schema.RigettiDeviceParameters`](@ref), default is inferred from previous arguments. +- `nshots`: number of shots, default is `100`. +- `client_token`: a `UUID` for the client token, will generate one by default. +- `tags::Dict{String, String}`: a list of tags you would to attach to this task. + +$(DOC_AWS_CONFIG) +""" function create_quantum_task(; program::Schema.Program, device_arn::String, @@ -73,6 +146,7 @@ function create_quantum_task(; nshots::Int = 100, client_token::UUID = uuid1(), tags::Dict{String, String} = Dict{String, String}(), + aws_config=AWS.global_aws_config(), ) response = Braket.create_quantum_task( @@ -85,17 +159,39 @@ function create_quantum_task(; Dict( "deviceParameters" => JSON.json(to_dict(device_parameters; include_defaults=true, exclude_nothing=true)), "tags" => tags, - ), + ); + aws_config ) return response["quantumTaskArn"], response["status"] end -function get_quantum_task(task_arn::String) - from_dict(Schema.BraketTaskInfo, Braket.get_quantum_task(HTTP.escapeuri(task_arn))) +""" + get_quantum_task(task_arn::String) + +Get the quantum task from `task_arn`. + +$(DOC_AWS_CONFIG) +""" +function get_quantum_task(task_arn::String; aws_config=AWS.global_aws_config()) + from_dict( + Schema.BraketTaskInfo, + Braket.get_quantum_task(HTTP.escapeuri(task_arn); aws_config) + ) end -function cancel_quantum_task(client_token::String, task_arn::String) - Braket.cancel_quantum_task(client_token, HTTP.escapeuri(task_arn)) +""" + cancel_quantum_task(client_token::String, task_arn::String) + +Cancel quantum task given by `client_token` and its `task_arn`. + +$(DOC_AWS_CONFIG) +""" +function cancel_quantum_task(client_token::String, task_arn::String; aws_config=AWS.global_aws_config()) + Braket.cancel_quantum_task( + client_token, + HTTP.escapeuri(task_arn); + aws_config + ) end using Crayons.Box diff --git a/src/menu.jl b/src/menu.jl index 559c8f1..e28ceb4 100644 --- a/src/menu.jl +++ b/src/menu.jl @@ -7,6 +7,16 @@ mutable struct DeviceMenu <: TerminalMenus.AbstractMenu selected::Int end +""" + DeviceMenu([devices, regions]; pagesize::Int=5) + +Create a DeviceMenu to display available devices interactively in the Julia REPL. + +# Arguments + +- `devices::Vector{Schema.DeviceInfo}`: optional, a list of devices. +- `regions::Vector{String}`: optional, a list of regions corresponding to the device list. +""" function DeviceMenu(; pagesize::Int=5) all_devices = Schema.DeviceInfo[] regions = String[] diff --git a/test/runtests.jl b/test/runtests.jl index c0447dd..c5ad0e2 100644 --- a/test/runtests.jl +++ b/test/runtests.jl @@ -1,38 +1,6 @@ -using GarishPrint -using AWSBraket using Test -using JSON -using HTTP -using Configurations -using AWS -using AWS: @service - -@service Braket - - -info = AWSBraket.get_device("arn:aws:braket:::device/quantum-simulator/amazon/sv1") -pprint(info) - -dev, _ = AWSBraket.search_devices(;aws_config=AWS.global_aws_config(region="us-west-1")) -pprint(dev[1]) - -dev[1].deviceArn -dev[2].deviceArn -dev[3].deviceArn -dev[4].deviceArn - - -Braket.search_quantum_tasks([]) -Braket.create_quantum_task - -using AWSBraket.Schema - -dev = Braket.search_devices([]; aws_config=AWS.global_aws_config(region="us-east-1"))["devices"] -map(dev) do d - from_dict(Schema.DeviceInfo, d) -end - -from_dict(Schema.DeviceInfo, dev[1]) - -AWS.global_aws_config(region="us-east-1") \ No newline at end of file +# https://github.com/JuliaTesting/BrokenRecord.jl/issues/20 +# @testset "task" begin +# include("task.jl") +# end diff --git a/test/task.jl b/test/task.jl index 7ad5b7e..6adf605 100644 --- a/test/task.jl +++ b/test/task.jl @@ -1,7 +1,16 @@ +using BrokenRecord using AWSBraket using AWSBraket.Schema using Configurations using JSON +using HTTP +using Test +using AWS +using AWS: @service +@service Braket +@service S3 + +BrokenRecord.configure!(path=joinpath(pkgdir(AWSBraket), "test", "records"), ignore_headers=["Authorization"]) bell = Schema.Program(; instructions=[ @@ -10,25 +19,33 @@ bell = Schema.Program(; ], ) -task, status = AWSBraket.create_quantum_task(; - program=bell, - device_arn="arn:aws:braket:::device/quantum-simulator/amazon/sv1", - bucket="amazon-braket-8865d8c99645", - folder="braket", -) +task, status = BrokenRecord.playback("create_quantum_task.json") do + AWSBraket.create_quantum_task(; + program=bell, + device_arn="arn:aws:braket:::device/quantum-simulator/amazon/sv1", + bucket="amazon-braket-8865d8c99645", + folder="braket", + ) +end -info = AWSBraket.get_quantum_task(task) -# AWSBraket.cancel_quantum_task(info.id, task) +info = BrokenRecord.playback("get_quantum_task.json") do + AWSBraket.get_quantum_task(task) +end -using HTTP -using AWS: @service -@service Braket -@service S3 -# S3.list_objects(info.outputS3Bucket)["Contents"] -content = S3.get_object(info.outputS3Bucket, info.outputS3Directory * "/results.json") -JSON.parse(String(content)) +@test_throws AWS.AWSExceptions.AWSException BrokenRecord.playback("cancel_quantum_task.json") do + AWSBraket.cancel_quantum_task(info.id, task) +end + +content = BrokenRecord.playback("get_result_object.json") do + S3.get_object(info.outputS3Bucket, info.outputS3Directory * "/results.json") +end + +result = JSON.parse(String(content)) +result["measurements"] + +# NOTE: the results type is broken currently # bell = Schema.Program(; # instructions=[ # Schema.H(;target=0), @@ -42,24 +59,3 @@ JSON.parse(String(content)) # ) # ] # ) - -# deviceParameters = Schema.GateModelSimulatorDeviceParameters( -# paradigmParameters=Schema.GateModelParameters( -# qubitCount=2, disableQubitRewiring=false, -# ) -# ) - -# JSON.json(to_dict(bell; include_defaults=true, exclude_nothing=true), 2)|>print - -# # direct call -# task = Braket.create_quantum_task( -# JSON.json(to_dict(bell; include_defaults=true, exclude_nothing=true)), -# string(uuid1()), -# "arn:aws:braket:::device/quantum-simulator/amazon/sv1", -# "amazon-braket-8865d8c99645", -# "braket", -# 100, -# Dict( -# "deviceParameters" => JSON.json(to_dict(deviceParameters; include_defaults=true)), -# ), -# )