diff --git a/global.json b/global.json index da2e1177aa6..703a5dae1e1 100644 --- a/global.json +++ b/global.json @@ -1,5 +1,5 @@ { "msbuild-sdks": { - "Microsoft.Quantum.Sdk": "0.16.2105143425-alpha" + "Microsoft.Quantum.Sdk": "0.17.2106145735-alpha" } } diff --git a/src/Azure/Azure.Quantum.Client.Test/Helpers/Problem.cs b/src/Azure/Azure.Quantum.Client.Test/Helpers/Problem.cs index 5a527dc9c6e..2fcd64c1dec 100644 --- a/src/Azure/Azure.Quantum.Client.Test/Helpers/Problem.cs +++ b/src/Azure/Azure.Quantum.Client.Test/Helpers/Problem.cs @@ -1,4 +1,7 @@ -using System.Collections.Generic; +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +using System.Collections.Generic; using System.IO; using System.Text.Json; using System.Text.Json.Serialization; @@ -54,12 +57,12 @@ public void Add(int i, float c) public void Add(int i, int j, float c) { - terms.Add(new Term( new int[] { i, j }, c)); + terms.Add(new Term(new int[] { i, j }, c)); } public void Add(int i, int j, int k, float c) { - terms.Add(new Term( new int[] { i, j, k }, c)); + terms.Add(new Term(new int[] { i, j, k }, c)); } public void AddRange(IEnumerable collection) diff --git a/src/Azure/Azure.Quantum.Client.Test/Helpers/TestConstants.cs b/src/Azure/Azure.Quantum.Client.Test/Helpers/TestConstants.cs deleted file mode 100644 index d9007003394..00000000000 --- a/src/Azure/Azure.Quantum.Client.Test/Helpers/TestConstants.cs +++ /dev/null @@ -1,15 +0,0 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT License. - -namespace Microsoft.Azure.Quantum.Test -{ - public static class TestConstants - { - // Used when connecting live - public const string LiveSubscriptionId = "916dfd6d-030c-4bd9-b579-7bb6d1926e97"; - public const string LiveLocation = "westus2"; - public const string LiveResourceGroupName = "e2e-scenarios"; - public const string LiveStorageAccount = "e2etests"; - public const string LiveWorkspaceName = "e2e-qsharp-tests"; - } -} diff --git a/src/Azure/Azure.Quantum.Client.Test/WorkspaceTest.cs b/src/Azure/Azure.Quantum.Client.Test/WorkspaceTest.cs index ecb5a84ed93..bb5fb81f7ba 100644 --- a/src/Azure/Azure.Quantum.Client.Test/WorkspaceTest.cs +++ b/src/Azure/Azure.Quantum.Client.Test/WorkspaceTest.cs @@ -5,6 +5,7 @@ using System.Collections.Generic; using System.IO; using System.IO.Compression; +using System.Linq; using System.Net; using System.Threading; using System.Threading.Tasks; @@ -21,8 +22,20 @@ namespace Microsoft.Azure.Quantum.Test [TestClass] public class WorkspaceTest { + private const string SETUP = @" +Live tests require you to configure your environment with these variables: + * E2E_WORKSPACE_NAME: the name of an Azure Quantum workspace to use for live testing. + * E2E_SUBSCRIPTION_ID: the Azure Quantum workspace's Subscription Id. + * E2E_WORKSPACE_RG: the Azure Quantum workspace's resource group. + * E2E_WORKSPACE_LOCATION: the Azure Quantum workspace's location (region). + +You'll also need to authenticate with Azure using any of the methods listed in: +https://docs.microsoft.com/en-us/dotnet/api/overview/azure/identity-readme#authenticate-the-client + +Tests will be marked as Inconclusive if the pre-reqs are not correctly setup."; + [TestMethod] - [Ignore] + [TestCategory("Live")] public async Task SubmitJobTest() { // Create Job @@ -41,7 +54,7 @@ public async Task SubmitJobTest() } [TestMethod] - [Ignore] + [TestCategory("Live")] public async Task GetJobTest() { IWorkspace workspace = GetLiveWorkspace(); @@ -61,7 +74,7 @@ public async Task GetJobTest() } [TestMethod] - [Ignore] + [TestCategory("Live")] public async Task CancelJobTest() { // Create Job @@ -82,7 +95,7 @@ public async Task CancelJobTest() } [TestMethod] - [Ignore] + [TestCategory("Live")] public async Task ListJobsTest() { IWorkspace workspace = GetLiveWorkspace(); @@ -110,7 +123,7 @@ public async Task ListJobsTest() } [TestMethod] - [Ignore] + [TestCategory("Live")] public async Task ListQuotasTest() { IWorkspace workspace = GetLiveWorkspace(); @@ -121,8 +134,35 @@ public async Task ListQuotasTest() await foreach (var q in workspace.ListQuotasAsync()) { Assert.IsNotNull(q); - Assert.IsNotNull(q.Quota); - Assert.IsNotNull(q.Quota.Dimension); + Assert.IsNotNull(q.ProviderId); + Assert.IsNotNull(q.Dimension); + + max--; + if (max <= 0) + { + break; + } + } + + // Make sure we iterated through all the expected jobs: + Assert.AreEqual(0, max); + } + + [TestMethod] + [TestCategory("Live")] + public async Task ListProviderStatusTest() + { + IWorkspace workspace = GetLiveWorkspace(); + int max = 1; + + // Since this is a live workspace, we don't have much control about what quotas are in there + // Just make sure there is more than one. + await foreach (var s in workspace.ListProvidersStatusAsync()) + { + Assert.IsNotNull(s); + Assert.IsNotNull(s.ProviderId); + Assert.IsNotNull(s.Targets); + Assert.IsTrue(s.Targets.Any()); max--; if (max <= 0) @@ -146,14 +186,22 @@ private static void AssertJob(CloudJob job) private IWorkspace GetLiveWorkspace() { + if (string.IsNullOrWhiteSpace(System.Environment.GetEnvironmentVariable("E2E_SUBSCRIPTION_ID")) || + string.IsNullOrWhiteSpace(System.Environment.GetEnvironmentVariable("E2E_WORKSPACE_RG")) || + string.IsNullOrWhiteSpace(System.Environment.GetEnvironmentVariable("E2E_WORKSPACE_NAME")) || + string.IsNullOrWhiteSpace(System.Environment.GetEnvironmentVariable("E2E_SUBSCRIPTION_ID"))) + { + Assert.Inconclusive(SETUP); + } + var options = new QuantumJobClientOptions(); options.Diagnostics.ApplicationId = "ClientTests"; return new Workspace( - subscriptionId: System.Environment.GetEnvironmentVariable("E2E_SUBSCRIPTION_ID") ?? TestConstants.LiveSubscriptionId, - resourceGroupName: System.Environment.GetEnvironmentVariable("E2E_WORKSPACE_RG") ?? TestConstants.LiveResourceGroupName, - workspaceName: System.Environment.GetEnvironmentVariable("E2E_WORKSPACE_NAME") ?? TestConstants.LiveWorkspaceName, - location: System.Environment.GetEnvironmentVariable("E2E_WORKSPACE_LOCATION") ?? TestConstants.LiveLocation, + subscriptionId: System.Environment.GetEnvironmentVariable("E2E_SUBSCRIPTION_ID"), + resourceGroupName: System.Environment.GetEnvironmentVariable("E2E_WORKSPACE_RG"), + workspaceName: System.Environment.GetEnvironmentVariable("E2E_WORKSPACE_NAME"), + location: System.Environment.GetEnvironmentVariable("E2E_WORKSPACE_LOCATION"), options: options); } diff --git a/src/Azure/Azure.Quantum.Client/JobManagement/CloudJob.cs b/src/Azure/Azure.Quantum.Client/JobManagement/CloudJob.cs index 34d7360b81b..05c92de3e0b 100644 --- a/src/Azure/Azure.Quantum.Client/JobManagement/CloudJob.cs +++ b/src/Azure/Azure.Quantum.Client/JobManagement/CloudJob.cs @@ -42,6 +42,11 @@ public CloudJob(IWorkspace workspace, JobDetails jobDetails) /// public virtual string Id => Details.Id; + /// + /// Gets the job id. + /// + public virtual string Name => Details.Name; + /// /// Gets whether the job execution has completed. /// @@ -70,7 +75,29 @@ public CloudJob(IWorkspace workspace, JobDetails jobDetails) public virtual IWorkspace Workspace { get; private set; } /// - /// Gets the job details. + /// Gets or sets the unique identifier for the provider. + /// + public virtual string ProviderId => this.Details?.ProviderId; + + /// + /// Gets or sets the target identifier to run the job. + /// + public string Target => this.Details.Target; + + /// + /// If available, returns Uri with the results of the execution. + /// > + public virtual Uri OutputDataUri => (this.Details?.OutputDataUri != null) + ? new Uri(this.Details.OutputDataUri) + : null; + + /// + /// If available, returns the data format of the execution results. + /// + public virtual string OutputDataFormat => this.Details?.OutputDataFormat; + + /// + /// Gets the underlying job details. /// public virtual JobDetails Details { get; private set; } diff --git a/src/Azure/Azure.Quantum.Client/JobManagement/IWorkspace.cs b/src/Azure/Azure.Quantum.Client/JobManagement/IWorkspace.cs index e2ac5921114..739e9dc087c 100644 --- a/src/Azure/Azure.Quantum.Client/JobManagement/IWorkspace.cs +++ b/src/Azure/Azure.Quantum.Client/JobManagement/IWorkspace.cs @@ -1,18 +1,44 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT License. -using System.Collections.Generic; -using System.Threading; -using System.Threading.Tasks; -using Microsoft.Quantum.Runtime; - namespace Microsoft.Azure.Quantum { + using System.Collections.Generic; + using System.Threading; + using System.Threading.Tasks; + + using global::Azure.Quantum.Jobs; + /// /// IWorkspace interface. /// public interface IWorkspace { + /// + /// The Workspace's Azure Subscription. + /// + string SubscriptionId { get; } + + /// + /// The Workspace's resource group in Azure. + /// + string ResourceGroupName { get; } + + /// + /// The Workspace's location (region) in Azure. + /// + string Location { get; } + + /// + /// The Workspace's name. + /// + string WorkspaceName { get; } + + /// + /// The low-level client used to communicate with the service. + /// + public QuantumJobClient Client { get; } + /// /// Submits the job. /// @@ -47,7 +73,7 @@ Task GetJobAsync( /// Lists the jobs. /// /// The cancellation token. - /// List of jobs + /// List of jobs. IAsyncEnumerable ListJobsAsync( CancellationToken cancellationToken = default); @@ -55,10 +81,18 @@ IAsyncEnumerable ListJobsAsync( /// Returns the list of quotas for a workspace. /// /// The cancellation token. - /// List of jobs + /// List of quotas. IAsyncEnumerable ListQuotasAsync( CancellationToken cancellationToken = default); + /// + /// Returns a list with the status of each of the Providers in a workspace + /// + /// The cancellation token. + /// List of provider status. + IAsyncEnumerable ListProvidersStatusAsync( + CancellationToken cancellationToken = default); + /// /// Gets as SAS Uri for the storage account associated with the workspace. /// diff --git a/src/Azure/Azure.Quantum.Client/JobManagement/ProviderStatusInfo.cs b/src/Azure/Azure.Quantum.Client/JobManagement/ProviderStatusInfo.cs new file mode 100644 index 00000000000..222f496c5de --- /dev/null +++ b/src/Azure/Azure.Quantum.Client/JobManagement/ProviderStatusInfo.cs @@ -0,0 +1,78 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +#nullable enable + +namespace Microsoft.Azure.Quantum +{ + using System.Collections.Generic; + + using Microsoft.Azure.Quantum.Utility; + + using Models = global::Azure.Quantum.Jobs.Models; + + /// + /// Wrapper for Azure.Quantum.Jobs.Models.ProviderStatus. + /// + public class ProviderStatusInfo + { + /// + /// Initializes a new instance of the class. + /// + /// The workspace. + /// The provider status details. + public ProviderStatusInfo(IWorkspace workspace, Models.ProviderStatus status) + { + Ensure.NotNull(workspace, nameof(workspace)); + Ensure.NotNull(status, nameof(status)); + + Workspace = workspace; + Details = status; + } + + /// + /// Initializes a new instance of the class. + /// Use only for testing. + /// + protected ProviderStatusInfo() + { + } + + /// + /// Gets the workspace. + /// + public virtual IWorkspace? Workspace { get; } + + /// + /// Provider id. + /// + public virtual string? ProviderId => this.Details?.Id; + + /// + /// Provider availability. + /// + public virtual Models.ProviderAvailability? CurrentAvailability => this.Details?.CurrentAvailability; + + /// + /// List of all available targets for this provider. + /// + public virtual IEnumerable? Targets + { + get + { + if (this.Details != null) + { + foreach (var ps in this.Details.Targets) + { + yield return new TargetStatusInfo(ps); + } + } + } + } + + /// + /// Gets the provider status information. + /// + protected Models.ProviderStatus? Details { get; private set; } + } +} diff --git a/src/Azure/Azure.Quantum.Client/JobManagement/QuotaInfo.cs b/src/Azure/Azure.Quantum.Client/JobManagement/QuotaInfo.cs index 6906963b65a..a36ca7ca7ee 100644 --- a/src/Azure/Azure.Quantum.Client/JobManagement/QuotaInfo.cs +++ b/src/Azure/Azure.Quantum.Client/JobManagement/QuotaInfo.cs @@ -1,6 +1,8 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT License. +#nullable enable + namespace Microsoft.Azure.Quantum { using global::Azure.Quantum.Jobs.Models; @@ -8,7 +10,7 @@ namespace Microsoft.Azure.Quantum using Microsoft.Azure.Quantum.Utility; /// - /// Wrapper for Microsoft.Azure.Quantum.Client.Models.Quota. + /// Wrapper for Azure.Quantum.Jobs.Models.QuantumJobQuota. /// public class QuotaInfo { @@ -23,17 +25,62 @@ public QuotaInfo(IWorkspace workspace, QuantumJobQuota quota) Ensure.NotNull(quota, nameof(quota)); Workspace = workspace; - Quota = quota; + Details = quota; + } + + /// + /// Initializes a new instance of the class. + /// Use only for testing. + /// + protected QuotaInfo() + { } /// /// Gets the workspace. /// - public IWorkspace Workspace { get; private set; } + public virtual IWorkspace? Workspace { get; private set; } + + /// + /// The name of the dimension associated with the quota. + /// + public virtual string? Dimension => Details?.Dimension; + + /// + /// The scope at which the quota is applied. + /// + public virtual DimensionScope? Scope => Details?.Scope; + + /// + /// The unique identifier for the provider. + /// + public virtual string? ProviderId => Details?.ProviderId; + + /// + /// The amount of the usage that has been applied for the current period. + /// + public virtual float? Utilization => Details?.Utilization; + + /// + /// The amount of the usage that has been reserved but not applied for the current + /// period. + /// + public virtual float? Holds => Details?.Holds; + + /// + /// The maximum amount of usage allowed for the current period. + /// + public virtual float? Limit => Details?.Limit; + + /// + /// The time period in which the quota's underlying meter is accumulated. Based on + /// calendar year. 'None' is used for concurrent quotas. + /// + public virtual MeterPeriod? Period => Details?.Period; /// /// Gets the quota information. /// - public QuantumJobQuota Quota { get; private set; } + protected QuantumJobQuota? Details { get; private set; } } } diff --git a/src/Azure/Azure.Quantum.Client/JobManagement/TargetStatusInfo.cs b/src/Azure/Azure.Quantum.Client/JobManagement/TargetStatusInfo.cs new file mode 100644 index 00000000000..98afab13a36 --- /dev/null +++ b/src/Azure/Azure.Quantum.Client/JobManagement/TargetStatusInfo.cs @@ -0,0 +1,62 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +#nullable enable + +namespace Microsoft.Azure.Quantum +{ + using Microsoft.Azure.Quantum.Utility; + + using Models = global::Azure.Quantum.Jobs.Models; + + /// + /// Wrapper for Azure.Quantum.Jobs.Models.ProviderStatus. + /// + public class TargetStatusInfo + { + /// + /// Initializes a new instance of the class. + /// + /// The workspace. + /// The provider status details. + public TargetStatusInfo(Models.TargetStatus status) + { + Ensure.NotNull(status, nameof(status)); + + Details = status; + } + + /// + /// Initializes a new instance of the class. + /// Use only for testing. + /// + protected TargetStatusInfo() + { + } + + /// + /// Target id. + /// + public virtual string? TargetId => Details?.Id; + + /// + /// Target availability. + /// + public virtual Models.TargetAvailability? CurrentAvailability => Details?.CurrentAvailability; + + /// + /// Average queue time in seconds. + /// + public virtual long? AverageQueueTime => Details?.AverageQueueTime; + + /// + /// A page with detailed status of the provider. + /// + public virtual string? StatusPage => Details?.StatusPage; + + /// + /// Gets the provider status information. + /// + protected Models.TargetStatus? Details { get; private set; } + } +} diff --git a/src/Azure/Azure.Quantum.Client/JobManagement/Workspace.cs b/src/Azure/Azure.Quantum.Client/JobManagement/Workspace.cs index 2b269a945a8..b8eb8349377 100644 --- a/src/Azure/Azure.Quantum.Client/JobManagement/Workspace.cs +++ b/src/Azure/Azure.Quantum.Client/JobManagement/Workspace.cs @@ -48,7 +48,17 @@ public Workspace( Ensure.NotNullOrWhiteSpace(location, nameof(location)); // Optional parameters: - credential ??= new DefaultAzureCredential(includeInteractiveCredentials: true); + if (credential == null) + { + // We have to disable VisualStudio until 16.11 goes out, see: https://devdiv.visualstudio.com/DevDiv/_workitems/edit/1332071 + var credOptions = new DefaultAzureCredentialOptions() + { + ExcludeVisualStudioCredential = true, + }; + + credential = new DefaultAzureCredential(credOptions); + } + options ??= new QuantumJobClientOptions(); this.ResourceGroupName = resourceGroupName; @@ -91,12 +101,12 @@ public async Task SubmitJobAsync( CancellationToken cancellationToken = default) { Ensure.NotNull(jobDefinition, nameof(jobDefinition)); - Ensure.NotNullOrWhiteSpace(jobDefinition.Details.Id, nameof(jobDefinition.Details.Id)); + Ensure.NotNullOrWhiteSpace(jobDefinition.Id, nameof(jobDefinition.Id)); try { JobDetails jobDetails = await this.Client.CreateJobAsync( - jobId: jobDefinition.Details.Id, + jobId: jobDefinition.Id, job: jobDefinition.Details, cancellationToken: cancellationToken); @@ -104,7 +114,7 @@ public async Task SubmitJobAsync( } catch (Exception ex) { - throw CreateException(ex, "Could not submit job", jobDefinition.Details.Id); + throw CreateException(ex, "Could not submit job", jobDefinition.Id); } } @@ -194,6 +204,23 @@ public async IAsyncEnumerable ListQuotasAsync([EnumeratorCancellation } } + /// + /// Lists the quotas. + /// + /// The cancellation token. + /// + /// List of quotas. + /// + public async IAsyncEnumerable ListProvidersStatusAsync([EnumeratorCancellation] CancellationToken cancellationToken = default) + { + var status = this.Client.GetProviderStatusAsync(cancellationToken); + + await foreach (var s in status) + { + yield return new ProviderStatusInfo(this, s); + } + } + /// /// Gets as SAS Uri for the linked storage account. /// diff --git a/src/Azure/Azure.Quantum.Client/JobManagement/WorkspaceExtensions.cs b/src/Azure/Azure.Quantum.Client/JobManagement/WorkspaceExtensions.cs index d220d2560d0..3e49c020285 100644 --- a/src/Azure/Azure.Quantum.Client/JobManagement/WorkspaceExtensions.cs +++ b/src/Azure/Azure.Quantum.Client/JobManagement/WorkspaceExtensions.cs @@ -1,8 +1,6 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT License. -using System.Collections.Generic; - namespace Microsoft.Azure.Quantum { /// diff --git a/src/Quantum.Development.Kit/Microsoft.Quantum.Development.Kit.nuspec b/src/Quantum.Development.Kit/Microsoft.Quantum.Development.Kit.nuspec index b3f2dc492ef..2b2a6cd0a2b 100644 --- a/src/Quantum.Development.Kit/Microsoft.Quantum.Development.Kit.nuspec +++ b/src/Quantum.Development.Kit/Microsoft.Quantum.Development.Kit.nuspec @@ -20,7 +20,7 @@ Quantum Q# QSharp - + diff --git a/src/Simulation/CSharpGeneration/Microsoft.Quantum.CSharpGeneration.fsproj b/src/Simulation/CSharpGeneration/Microsoft.Quantum.CSharpGeneration.fsproj index dba0c6256c8..f1838ea1cb4 100644 --- a/src/Simulation/CSharpGeneration/Microsoft.Quantum.CSharpGeneration.fsproj +++ b/src/Simulation/CSharpGeneration/Microsoft.Quantum.CSharpGeneration.fsproj @@ -22,7 +22,7 @@ - + diff --git a/src/Xunit/Microsoft.Quantum.Xunit.nuspec b/src/Xunit/Microsoft.Quantum.Xunit.nuspec index 2de3f2c585f..053332e1ef4 100644 --- a/src/Xunit/Microsoft.Quantum.Xunit.nuspec +++ b/src/Xunit/Microsoft.Quantum.Xunit.nuspec @@ -22,7 +22,7 @@ - +