From 59c15c43983f0d2e933876a459bc27968f3f9d1f Mon Sep 17 00:00:00 2001 From: Andres Paz Date: Tue, 25 May 2021 14:33:26 -0700 Subject: [PATCH 1/7] Replace generated files in Quantum.Client with reference to the Azure.QuantumJobs package (#697) --- build/.gitignore | 1 + global.json | 2 +- .../Helpers/MockHelper.cs | 52 - .../Helpers/Problem.cs | 115 +++ .../Helpers/TestConstants.cs | 11 +- ...Microsoft.Azure.Quantum.Client.Test.csproj | 1 - .../WorkspaceTest.cs | 351 ++++--- .../Authentication/ClientCredentials.cs | 48 - .../CustomAccessTokenProvider.cs | 127 --- .../Authentication/IAccessTokenProvider.cs | 21 - .../StaticAccessTokenProvider.cs | 37 - .../Authentication/TokenCredentialProvider.cs | 43 - .../Exceptions/WorkspaceClientException.cs | 30 +- .../JobManagement/CloudJob.cs | 41 +- .../JobManagement/IWorkspace.cs | 4 +- .../JobManagement/QuotaInfo.cs | 7 +- .../JobManagement/Workspace.cs | 221 +--- .../JobManagement/WorkspaceExtensions.cs | 10 - .../Microsoft.Azure.Quantum.Client.csproj | 7 +- .../generated/IJobsOperations.cs | 134 --- .../generated/IProvidersOperations.cs | 68 -- .../generated/IQuantumClient.cs | 99 -- .../generated/IQuotasOperations.cs | 68 -- .../generated/IStorageOperations.cs | 52 - .../generated/JobsOperations.cs | 974 ------------------ .../generated/JobsOperationsExtensions.cs | 192 ---- .../generated/Models/BlobDetails.cs | 73 -- .../generated/Models/DimensionScope.cs | 22 - .../generated/Models/ErrorData.cs | 63 -- .../generated/Models/JobDetails.cs | 233 ----- .../generated/Models/JobStatus.cs | 25 - .../generated/Models/MeterPeriod.cs | 22 - .../generated/Models/Page.cs | 53 - .../generated/Models/ProviderAvailability.cs | 23 - .../generated/Models/ProviderStatus.cs | 69 -- .../generated/Models/Quota.cs | 114 -- .../generated/Models/RestError.cs | 49 - .../generated/Models/RestErrorException.cs | 61 -- .../generated/Models/SasUriResponse.cs | 53 - .../generated/Models/TargetAvailability.cs | 23 - .../generated/Models/TargetStatus.cs | 79 -- .../generated/ProvidersOperations.cs | 397 ------- .../ProvidersOperationsExtensions.cs | 87 -- .../generated/QuantumClient.cs | 381 ------- .../generated/QuotasOperations.cs | 397 ------- .../generated/QuotasOperationsExtensions.cs | 87 -- .../generated/StorageOperations.cs | 249 ----- .../generated/StorageOperationsExtensions.cs | 63 -- .../Microsoft.Quantum.Development.Kit.nuspec | 2 +- .../Microsoft.Quantum.CSharpGeneration.fsproj | 2 +- .../EntryPointDriver.Tests/Tests.fs | 102 +- src/Simulation/EntryPointDriver/Azure.cs | 89 +- src/Simulation/EntryPointDriver/Driver.cs | 1 - src/Xunit/Microsoft.Quantum.Xunit.nuspec | 2 +- 54 files changed, 537 insertions(+), 5000 deletions(-) create mode 100644 build/.gitignore delete mode 100644 src/Azure/Azure.Quantum.Client.Test/Helpers/MockHelper.cs create mode 100644 src/Azure/Azure.Quantum.Client.Test/Helpers/Problem.cs delete mode 100644 src/Azure/Azure.Quantum.Client/Authentication/ClientCredentials.cs delete mode 100644 src/Azure/Azure.Quantum.Client/Authentication/CustomAccessTokenProvider.cs delete mode 100644 src/Azure/Azure.Quantum.Client/Authentication/IAccessTokenProvider.cs delete mode 100644 src/Azure/Azure.Quantum.Client/Authentication/StaticAccessTokenProvider.cs delete mode 100644 src/Azure/Azure.Quantum.Client/Authentication/TokenCredentialProvider.cs delete mode 100644 src/Azure/Azure.Quantum.Client/generated/IJobsOperations.cs delete mode 100644 src/Azure/Azure.Quantum.Client/generated/IProvidersOperations.cs delete mode 100644 src/Azure/Azure.Quantum.Client/generated/IQuantumClient.cs delete mode 100644 src/Azure/Azure.Quantum.Client/generated/IQuotasOperations.cs delete mode 100644 src/Azure/Azure.Quantum.Client/generated/IStorageOperations.cs delete mode 100644 src/Azure/Azure.Quantum.Client/generated/JobsOperations.cs delete mode 100644 src/Azure/Azure.Quantum.Client/generated/JobsOperationsExtensions.cs delete mode 100644 src/Azure/Azure.Quantum.Client/generated/Models/BlobDetails.cs delete mode 100644 src/Azure/Azure.Quantum.Client/generated/Models/DimensionScope.cs delete mode 100644 src/Azure/Azure.Quantum.Client/generated/Models/ErrorData.cs delete mode 100644 src/Azure/Azure.Quantum.Client/generated/Models/JobDetails.cs delete mode 100644 src/Azure/Azure.Quantum.Client/generated/Models/JobStatus.cs delete mode 100644 src/Azure/Azure.Quantum.Client/generated/Models/MeterPeriod.cs delete mode 100644 src/Azure/Azure.Quantum.Client/generated/Models/Page.cs delete mode 100644 src/Azure/Azure.Quantum.Client/generated/Models/ProviderAvailability.cs delete mode 100644 src/Azure/Azure.Quantum.Client/generated/Models/ProviderStatus.cs delete mode 100644 src/Azure/Azure.Quantum.Client/generated/Models/Quota.cs delete mode 100644 src/Azure/Azure.Quantum.Client/generated/Models/RestError.cs delete mode 100644 src/Azure/Azure.Quantum.Client/generated/Models/RestErrorException.cs delete mode 100644 src/Azure/Azure.Quantum.Client/generated/Models/SasUriResponse.cs delete mode 100644 src/Azure/Azure.Quantum.Client/generated/Models/TargetAvailability.cs delete mode 100644 src/Azure/Azure.Quantum.Client/generated/Models/TargetStatus.cs delete mode 100644 src/Azure/Azure.Quantum.Client/generated/ProvidersOperations.cs delete mode 100644 src/Azure/Azure.Quantum.Client/generated/ProvidersOperationsExtensions.cs delete mode 100644 src/Azure/Azure.Quantum.Client/generated/QuantumClient.cs delete mode 100644 src/Azure/Azure.Quantum.Client/generated/QuotasOperations.cs delete mode 100644 src/Azure/Azure.Quantum.Client/generated/QuotasOperationsExtensions.cs delete mode 100644 src/Azure/Azure.Quantum.Client/generated/StorageOperations.cs delete mode 100644 src/Azure/Azure.Quantum.Client/generated/StorageOperationsExtensions.cs diff --git a/build/.gitignore b/build/.gitignore new file mode 100644 index 00000000000..ee956f580c1 --- /dev/null +++ b/build/.gitignore @@ -0,0 +1 @@ +*.snk \ No newline at end of file diff --git a/global.json b/global.json index b8836b5a241..da2e1177aa6 100644 --- a/global.json +++ b/global.json @@ -1,5 +1,5 @@ { "msbuild-sdks": { - "Microsoft.Quantum.Sdk": "0.16.2105140327-beta" + "Microsoft.Quantum.Sdk": "0.16.2105143425-alpha" } } diff --git a/src/Azure/Azure.Quantum.Client.Test/Helpers/MockHelper.cs b/src/Azure/Azure.Quantum.Client.Test/Helpers/MockHelper.cs deleted file mode 100644 index cf34b116987..00000000000 --- a/src/Azure/Azure.Quantum.Client.Test/Helpers/MockHelper.cs +++ /dev/null @@ -1,52 +0,0 @@ -// -// Copyright (c) Microsoft Corporation. All rights reserved. -// - -using System.Collections.Generic; -using System.Net.Http; -using System.Threading; -using System.Threading.Tasks; - -using Microsoft.Azure.Quantum.Client; - -using Moq; -using Moq.Protected; - -namespace Microsoft.Azure.Quantum.Test -{ - public class MockHelper - { - internal List RequestMessages { get; private set; } = new List(); - - internal HttpResponseMessage ResponseMessage { get; set; } - - public HttpClient GetHttpClientMock() - { - Mock mock = new Mock(); - - mock.Protected() - .Setup>("SendAsync", ItExpr.IsAny(), ItExpr.IsAny()) - .Returns((HttpRequestMessage request, CancellationToken token) => - { - RequestMessages.Add(request); - - if (request.Method == HttpMethod.Delete) - { - return Task.FromResult(new HttpResponseMessage(System.Net.HttpStatusCode.NoContent)); - } - - return Task.FromResult(ResponseMessage); - }); - - return new HttpClient(mock.Object); - } - - public class MockQuantumClient : QuantumClient - { - public MockQuantumClient(MockHelper mock) - : base(mock.GetHttpClientMock(), true) - { - } - } - } -} diff --git a/src/Azure/Azure.Quantum.Client.Test/Helpers/Problem.cs b/src/Azure/Azure.Quantum.Client.Test/Helpers/Problem.cs new file mode 100644 index 00000000000..5a527dc9c6e --- /dev/null +++ b/src/Azure/Azure.Quantum.Client.Test/Helpers/Problem.cs @@ -0,0 +1,115 @@ +using System.Collections.Generic; +using System.IO; +using System.Text.Json; +using System.Text.Json.Serialization; +using System.Threading.Tasks; + +namespace Microsoft.Azure.Quantum.Test +{ + public enum ProblemType : int + { + PUBO = 0, + Ising = 1, + } + + /// + /// This is the data structure that represents a QIO problem payload on the service. + /// It's currently not exposed in C#, so we have it here so we can test job submission of + /// the client libraries without having to serialize Q# programs. + /// + public class Problem + { + private readonly HashSet terms = new HashSet(); + + public Problem(ProblemType type = ProblemType.PUBO) + { + ProblemType = type; + } + + public Problem(IEnumerable collection) + : this() + { + AddRange(collection); + } + + [JsonPropertyName("version")] + public string Version => "1.1"; + + [JsonPropertyName("type")] + [JsonConverter(typeof(JsonStringEnumConverter))] + public ProblemType ProblemType { get; } + + [JsonPropertyName("terms")] + public IEnumerable Terms => terms; + + public void Add(Term term) + { + terms.Add(term); + } + + public void Add(int i, float c) + { + terms.Add(new Term(new int[] { i }, c)); + } + + public void Add(int i, int j, float 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)); + } + + public void AddRange(IEnumerable collection) + { + foreach (var term in collection) + { + terms.Add(term); + } + } + + public Task SerializeAsync(Stream stream) + { + var root = new SerializationWrapper + { + CostFunction = this, + }; + + // Save to the writer + return JsonSerializer.SerializeAsync(stream, root); + } + + public override string ToString() + { + var root = new SerializationWrapper + { + CostFunction = this, + }; + + return JsonSerializer.Serialize(root); + } + + private struct SerializationWrapper + { + [JsonPropertyName("cost_function")] + public Problem CostFunction { get; set; } + } + + public class Term + { + public Term(int[] ids, float c) + { + this.IDs = ids; + this.Weight = c; + } + + [JsonPropertyName("c")] + public float Weight { get; } + + [JsonPropertyName("ids")] + public int[] IDs { get; } + } + } +} diff --git a/src/Azure/Azure.Quantum.Client.Test/Helpers/TestConstants.cs b/src/Azure/Azure.Quantum.Client.Test/Helpers/TestConstants.cs index 6cc2c8aa96d..d9007003394 100644 --- a/src/Azure/Azure.Quantum.Client.Test/Helpers/TestConstants.cs +++ b/src/Azure/Azure.Quantum.Client.Test/Helpers/TestConstants.cs @@ -5,10 +5,11 @@ namespace Microsoft.Azure.Quantum.Test { public static class TestConstants { - public const string SubscriptionId = "sub1"; - public const string ResourceGroupName = "rg1"; - public const string WorkspaceName = "ws1"; - public const string ProviderId = "provider1"; - public const string Endpoint = "https://test"; + // 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/Microsoft.Azure.Quantum.Client.Test.csproj b/src/Azure/Azure.Quantum.Client.Test/Microsoft.Azure.Quantum.Client.Test.csproj index 7965e696874..7ff14f03a27 100644 --- a/src/Azure/Azure.Quantum.Client.Test/Microsoft.Azure.Quantum.Client.Test.csproj +++ b/src/Azure/Azure.Quantum.Client.Test/Microsoft.Azure.Quantum.Client.Test.csproj @@ -10,7 +10,6 @@ - all diff --git a/src/Azure/Azure.Quantum.Client.Test/WorkspaceTest.cs b/src/Azure/Azure.Quantum.Client.Test/WorkspaceTest.cs index 48485da8f28..ecb5a84ed93 100644 --- a/src/Azure/Azure.Quantum.Client.Test/WorkspaceTest.cs +++ b/src/Azure/Azure.Quantum.Client.Test/WorkspaceTest.cs @@ -3,246 +3,239 @@ using System; using System.Collections.Generic; -using System.Linq; +using System.IO; +using System.IO.Compression; using System.Net; -using System.Net.Http; -using Microsoft.Azure.Quantum.Client; -using Microsoft.Azure.Quantum.Client.Models; +using System.Threading; +using System.Threading.Tasks; + +using Azure.Quantum; +using Azure.Quantum.Jobs.Models; + using Microsoft.Azure.Quantum.Exceptions; +using Microsoft.Azure.Quantum.Storage; using Microsoft.VisualStudio.TestTools.UnitTesting; -using Newtonsoft.Json; namespace Microsoft.Azure.Quantum.Test { [TestClass] public class WorkspaceTest { - private MockHelper httpMock; - - [TestInitialize] - public void Init() - { - this.httpMock = new MockHelper(); - } - [TestMethod] - public void SubmitJobTest() + [Ignore] + public async Task SubmitJobTest() { - string jobId = Guid.NewGuid().ToString(); - - // Craft response - SetJobResponseMessage(jobId); - // Create Job - IWorkspace workspace = GetWorkspace(); - - JobDetails jobDetails = CreateJobDetails(jobId); - CloudJob job = new CloudJob(workspace, jobDetails); - CloudJob receivedJob; - - // -ve cases - try - { - jobDetails.ContainerUri = null; - receivedJob = workspace.SubmitJob(job); - Assert.Fail(); - } - catch (WorkspaceClientException) - { - jobDetails.ContainerUri = "https://uri"; - } - - try - { - jobDetails.ProviderId = null; - receivedJob = workspace.SubmitJob(job); - Assert.Fail(); - } - catch (WorkspaceClientException) - { - jobDetails.ProviderId = TestConstants.ProviderId; - } + IWorkspace workspace = GetLiveWorkspace(); - // Success - receivedJob = workspace.SubmitJob(job); + CancellationTokenSource cts = new CancellationTokenSource(); + cts.CancelAfter(30000); - // Validate request - ValidateJobRequestMessage(jobId, HttpMethod.Put); + var job = await SubmitTestProblem(workspace); + AssertJob(job); - // Validate response - Assert.IsNotNull(receivedJob); + await job.WaitForCompletion(cancellationToken: cts.Token); - Assert.IsNotNull(receivedJob.Workspace); - - Assert.AreEqual( - expected: jobId, - actual: receivedJob.Details.Id); + AssertJob(job); + Assert.IsTrue(job.Succeeded); } [TestMethod] - public void GetJobTest() + [Ignore] + public async Task GetJobTest() { - string jobId = Guid.NewGuid().ToString(); - - // Craft response - SetJobResponseMessage(jobId); - - // Get Job - IWorkspace workspace = GetWorkspace(); - - CloudJob receivedJob = workspace.GetJob(jobId); - - // Validate request - ValidateJobRequestMessage(jobId, HttpMethod.Get); + IWorkspace workspace = GetLiveWorkspace(); - // Validate response - Assert.IsNotNull(receivedJob); + // Since this is a live workspace, we don't have much control about what jobs are in there + // Get the jobs, and call Get on the first. + await foreach (var job in workspace.ListJobsAsync()) + { + AssertJob(job); - Assert.IsNotNull(receivedJob.Workspace); + var current = workspace.GetJob(job.Id); + AssertJob(current); + Assert.AreEqual(job.Id, current.Id); - Assert.AreEqual( - expected: jobId, - actual: receivedJob.Details.Id); + break; + } } [TestMethod] - public void CancelJobTest() + [Ignore] + public async Task CancelJobTest() { - string jobId = Guid.NewGuid().ToString(); - - // Craft response - SetJobResponseMessage(jobId); - - // Cancel Job - IWorkspace workspace = GetWorkspace(); - - CloudJob receivedJob = workspace.CancelJob(jobId); - - // Validate request - ValidateJobRequestMessage(jobId, HttpMethod.Delete, HttpMethod.Get); - - // Validate response - Assert.IsNotNull(receivedJob); - - Assert.IsNotNull(receivedJob.Workspace); + // Create Job + IWorkspace workspace = GetLiveWorkspace(); - Assert.AreEqual( - expected: jobId, - actual: receivedJob.Details.Id); + var job = await SubmitTestProblem(workspace); + AssertJob(job); - // Convenience method - CloudJob job = new CloudJob(workspace, CreateJobDetails(jobId)); - string newJobId = Guid.NewGuid().ToString(); - SetJobResponseMessage(newJobId); + try + { + var result = workspace.CancelJob(job.Id); + AssertJob(result); + } + catch (WorkspaceClientException e) + { + Assert.AreEqual((int)HttpStatusCode.Conflict, e.Status); + } + } - Assert.AreEqual( - jobId, - job.Details.Id); + [TestMethod] + [Ignore] + public async Task ListJobsTest() + { + IWorkspace workspace = GetLiveWorkspace(); + int max = 3; - job.CancelAsync().Wait(); + // Since this is a live workspace, we don't have much control about what jobs are in there + // Just make sure there is more than one. + await foreach (var job in workspace.ListJobsAsync()) + { + Assert.IsNotNull(job); + Assert.IsNotNull(job.Details); + Assert.IsNotNull(job.Workspace); + Assert.IsFalse(string.IsNullOrWhiteSpace(job.Id)); + Assert.AreEqual(job.Details.Id, job.Id); + + max--; + if (max <= 0) + { + break; + } + } - Assert.AreEqual( - newJobId, - job.Details.Id); + // Make sure we iterated through all the expected jobs: + Assert.AreEqual(0, max); } [TestMethod] - public void ListJobsTest() + [Ignore] + public async Task ListQuotasTest() { - // Craft response - JobDetails jobDetails = new JobDetails - { - ProviderId = TestConstants.ProviderId, - }; - - dynamic page = new - { - nextLink = (string)null, - value = new JobDetails[] { jobDetails }, - }; + IWorkspace workspace = GetLiveWorkspace(); + int max = 3; - this.httpMock.ResponseMessage = new HttpResponseMessage(HttpStatusCode.OK) + // 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 q in workspace.ListQuotasAsync()) { - Content = new StringContent(JsonConvert.SerializeObject(page)), - }; - - // Cancel Job - IWorkspace workspace = GetWorkspace(); + Assert.IsNotNull(q); + Assert.IsNotNull(q.Quota); + Assert.IsNotNull(q.Quota.Dimension); - List receivedJobs = workspace.ListJobs().ToList(); + max--; + if (max <= 0) + { + break; + } + } - // Validate request - ValidateJobRequestMessage(null, HttpMethod.Get); + // Make sure we iterated through all the expected jobs: + Assert.AreEqual(0, max); + } - // Validate response - Assert.IsNotNull(receivedJobs); + private static void AssertJob(CloudJob job) + { + Assert.IsNotNull(job); + Assert.IsNotNull(job.Details); + Assert.IsNotNull(job.Workspace); + Assert.IsFalse(string.IsNullOrEmpty(job.Id)); + Assert.AreEqual(job.Id, job.Details.Id); + } - Assert.IsNotNull(receivedJobs.Single().Workspace); + private IWorkspace GetLiveWorkspace() + { + var options = new QuantumJobClientOptions(); + options.Diagnostics.ApplicationId = "ClientTests"; - Assert.AreEqual( - expected: jobDetails.ProviderId, - actual: receivedJobs.Single().Details.ProviderId); + 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, + options: options); } - private IWorkspace GetWorkspace() + private static JobDetails CreateJobDetails(string jobId, string containerUri = null, string inputUri = null) { - return new Workspace( - subscriptionId: TestConstants.SubscriptionId, - resourceGroupName: TestConstants.ResourceGroupName, - workspaceName: TestConstants.WorkspaceName) + return new JobDetails( + containerUri: containerUri, + inputDataFormat: "microsoft.qio.v2", + providerId: "Microsoft", + target: "microsoft.paralleltempering-parameterfree.cpu") { - // Mock jobs client (only needed for unit tests) - QuantumClient = new MockHelper.MockQuantumClient(httpMock) + Id = jobId, + Name = "Azure.Quantum.Unittest", + OutputDataFormat = "microsoft.qio-results.v2", + InputParams = new Dictionary() { - SubscriptionId = TestConstants.SubscriptionId, - ResourceGroupName = TestConstants.ResourceGroupName, - WorkspaceName = TestConstants.WorkspaceName, - BaseUri = new Uri(TestConstants.Endpoint), + { "params", new Dictionary() }, }, + InputDataUri = inputUri, }; } - private void SetJobResponseMessage(string jobId) + private static Problem CreateTestProblem() { - this.httpMock.ResponseMessage = new HttpResponseMessage(HttpStatusCode.OK) + // Create an Ising-type problem for shipping-containers + var containerWeights = new int[] { 1, 5, 9, 21, 35, 5, 3, 5, 10, 11 }; + + var problem = new Problem(ProblemType.Ising); + for (int i = 0; i < containerWeights.Length; i++) { - Content = new StringContent(JsonConvert.SerializeObject(CreateJobDetails(jobId))), - }; - } + for (int j = 0; j < containerWeights.Length; j++) + { + if (i != j) + { + problem.Add(i, j, containerWeights[i] * containerWeights[j]); + } + } + } - private static JobDetails CreateJobDetails(string jobId) - { - return new JobDetails( - id: jobId, - containerUri: "https://uri", - inputDataFormat: "format1", - providerId: TestConstants.ProviderId, - target: "target"); + return problem; } - private void ValidateJobRequestMessage( - string jobId, - params HttpMethod[] methods) + private async Task<(string, string)> UploadProblem(IWorkspace workspace, Problem problem, string jobId) { - var requestMessages = httpMock.RequestMessages; - Assert.AreEqual(requestMessages.Count, methods.Length); + string intermediaryFile = Path.GetTempFileName(); + + // Save to the intermediary file + using (var intermediaryWriter = File.Create(intermediaryFile)) + { + using (var compressionStream = new GZipStream(intermediaryWriter, CompressionLevel.Fastest)) + { + await problem.SerializeAsync(compressionStream); + } + } - for (var i = 0; i < requestMessages.Count; i++) + using (var intermediaryReader = File.OpenRead(intermediaryFile)) { - var requestMessage = requestMessages[i]; - var method = methods[i]; - - // Url - string expectedUri = $"{TestConstants.Endpoint}/v1.0/subscriptions/{TestConstants.SubscriptionId}/resourceGroups/{TestConstants.ResourceGroupName}/providers/Microsoft.Quantum/workspaces/{TestConstants.WorkspaceName}/jobs/{jobId}"; - Assert.AreEqual( - expected: expectedUri.TrimEnd('/'), - actual: requestMessage.RequestUri.ToString()); - - // Method - Assert.AreEqual( - expected: method, - actual: requestMessage.Method); + var jobStorageHelper = new LinkedStorageJobHelper(workspace); + return await jobStorageHelper.UploadJobInputAsync(jobId, intermediaryReader); } } + + private async Task SubmitTestProblem(IWorkspace workspace) + { + CancellationTokenSource cts = new CancellationTokenSource(); + cts.CancelAfter(30000); + + var jobId = Guid.NewGuid().ToString(); + var problem = CreateTestProblem(); + + // Upload problem: + var (containerUri, inputUri) = await UploadProblem(workspace, problem, jobId); + + CloudJob src = new CloudJob(workspace, CreateJobDetails(jobId, containerUri, inputUri)); + AssertJob(src); + + var job = await workspace.SubmitJobAsync(src, cts.Token); + AssertJob(job); + Assert.AreEqual(jobId, job.Id); + Assert.IsFalse(job.Failed); + + return job; + } } } diff --git a/src/Azure/Azure.Quantum.Client/Authentication/ClientCredentials.cs b/src/Azure/Azure.Quantum.Client/Authentication/ClientCredentials.cs deleted file mode 100644 index e9082df95b1..00000000000 --- a/src/Azure/Azure.Quantum.Client/Authentication/ClientCredentials.cs +++ /dev/null @@ -1,48 +0,0 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT License. - -using System; -using System.Net.Http; -using System.Threading; -using System.Threading.Tasks; -using Microsoft.Rest; - -namespace Microsoft.Azure.Quantum.Authentication -{ - /// - /// Authorization client handler class. - /// - internal class ClientCredentials : ServiceClientCredentials - { - private const string AuthorizationHeaderName = "Authorization"; - private const string BearerScheme = "Bearer"; - - private readonly IAccessTokenProvider accessTokenProvider; - - /// - /// Initializes a new instance of the class. - /// - /// The access token provider. - public ClientCredentials(IAccessTokenProvider accessTokenProvider) - { - this.accessTokenProvider = accessTokenProvider; - } - - public override async Task ProcessHttpRequestAsync(HttpRequestMessage request, CancellationToken cancellationToken) - { - if (request == null) - { - throw new ArgumentNullException("request"); - } - - if (!request.Headers.Contains(AuthorizationHeaderName)) - { - string accessToken = await this.accessTokenProvider.GetAccessTokenAsync(cancellationToken); - request.Headers.Authorization = new System.Net.Http.Headers.AuthenticationHeaderValue(BearerScheme, accessToken); - } - - //request.Version = new Version(apiVersion); - await base.ProcessHttpRequestAsync(request, cancellationToken); - } - } -} diff --git a/src/Azure/Azure.Quantum.Client/Authentication/CustomAccessTokenProvider.cs b/src/Azure/Azure.Quantum.Client/Authentication/CustomAccessTokenProvider.cs deleted file mode 100644 index b95e03237b1..00000000000 --- a/src/Azure/Azure.Quantum.Client/Authentication/CustomAccessTokenProvider.cs +++ /dev/null @@ -1,127 +0,0 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT License. - -using System.Linq; -using System.Net.Http; -using System.Security.Authentication; -using System.Threading; -using System.Threading.Tasks; -using Microsoft.Azure.Quantum.Utility; -using Microsoft.Identity.Client; -using Microsoft.Identity.Client.Extensions.Msal; - -namespace Microsoft.Azure.Quantum.Authentication -{ - /// - /// This class manually uses MSAL to get an access token. - /// It first tries to get silently, if that doesn't work, it tries to get interactively. - /// - /// - internal class CustomAccessTokenProvider : IAccessTokenProvider - { - private readonly LazyAsync applicationLazy; - private readonly string[] scopes; - private readonly string subscriptionId; - - /// - /// Initializes a new instance of the class. - /// - /// The Subscription Id of the account in use. - public CustomAccessTokenProvider(string subscriptionId) - { - static async Task GetSubscriptionTenantUri(string subscriptionId) - { - var uri = $"https://management.azure.com/subscriptions/{subscriptionId}?api-version=2018-01-01"; - try - { - static string GetTenantUriFromHeader(System.Net.Http.Headers.AuthenticationHeaderValue header) => - header - .Parameter - .Replace("Bearer ", string.Empty) - .Split(",") - .Select(part => part.Split("=")) - .ToDictionary(rg => rg[0], rg => rg[1])["authorization_uri"] - .Trim('\'', '"'); - - using var client = new HttpClient(); - var httpResult = await client.GetAsync(uri); - - return httpResult - .Headers - .WwwAuthenticate - .Select(GetTenantUriFromHeader) - .Single(); - } - catch (System.Exception ex) - { - throw new AuthenticationException("Unable to extract tenantUri!", ex); - } - } - - this.scopes = new string[] { Constants.Aad.Audience }; - this.subscriptionId = subscriptionId; - this.applicationLazy = - new LazyAsync(async () => - { - var application = PublicClientApplicationBuilder - .Create(Constants.Aad.ApplicationId) - .WithDefaultRedirectUri() - .WithAuthority(await GetSubscriptionTenantUri(subscriptionId)) - .Build(); - var cacheHelper = await CreateCacheHelperAsync(); - cacheHelper.RegisterCache(application.UserTokenCache); - return application; - }); - } - - /// - /// Tries to get access token silently, if didn't work, tries to get it interactively. - /// - /// The cancellation token. - /// A encapsulating the access token. - public async Task GetAccessTokenAsync(CancellationToken cancellationToken) - { - var application = await applicationLazy.Value; - - try - { - var accounts = await application.GetAccountsAsync(); - - // Try silently first - var result = await application - .AcquireTokenSilent(scopes, accounts.FirstOrDefault()) - .ExecuteAsync(); - - return result.AccessToken; - } - catch (MsalUiRequiredException) - { - // Didn't work, perform interactive logging - var result = await application - .AcquireTokenInteractive(scopes) - .ExecuteAsync(); - - return result.AccessToken; - } - } - - private static async Task CreateCacheHelperAsync() - { - StorageCreationProperties storageProperties; - - storageProperties = new StorageCreationPropertiesBuilder( - Constants.Aad.CacheFileName, - MsalCacheHelper.UserRootDirectory, - Constants.Aad.ApplicationId) - .WithMacKeyChain( - Constants.Aad.KeyChainServiceName, - Constants.Aad.KeyChainAccountName) - .Build(); - - var cacheHelper = await MsalCacheHelper.CreateAsync(storageProperties).ConfigureAwait(false); - - cacheHelper.VerifyPersistence(); - return cacheHelper; - } - } -} \ No newline at end of file diff --git a/src/Azure/Azure.Quantum.Client/Authentication/IAccessTokenProvider.cs b/src/Azure/Azure.Quantum.Client/Authentication/IAccessTokenProvider.cs deleted file mode 100644 index e72e4a440b8..00000000000 --- a/src/Azure/Azure.Quantum.Client/Authentication/IAccessTokenProvider.cs +++ /dev/null @@ -1,21 +0,0 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT License. - -using System.Threading; -using System.Threading.Tasks; - -namespace Microsoft.Azure.Quantum.Authentication -{ - /// - /// Generic interface to retrieve access token. This is not exposed to the user. - /// - internal interface IAccessTokenProvider - { - /// - /// Gets the access token. - /// - /// The cancellation token. - /// Access token string - Task GetAccessTokenAsync(CancellationToken cancellationToken); - } -} diff --git a/src/Azure/Azure.Quantum.Client/Authentication/StaticAccessTokenProvider.cs b/src/Azure/Azure.Quantum.Client/Authentication/StaticAccessTokenProvider.cs deleted file mode 100644 index d44aa2e88ef..00000000000 --- a/src/Azure/Azure.Quantum.Client/Authentication/StaticAccessTokenProvider.cs +++ /dev/null @@ -1,37 +0,0 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT License. - -using System.Threading; -using System.Threading.Tasks; -using Microsoft.Azure.Quantum.Utility; - -namespace Microsoft.Azure.Quantum.Authentication -{ - /// - /// This class accepts an access token string and provides it. - /// - /// - internal class StaticAccessTokenProvider : IAccessTokenProvider - { - private readonly string accessToken; - - /// - /// Initializes a new instance of the class. - /// - public StaticAccessTokenProvider(string accessToken) - { - Ensure.NotNullOrWhiteSpace(accessToken, nameof(accessToken)); - - this.accessToken = accessToken; - } - - /// - /// Returns the static access token. - /// - /// The cancellation token. - public Task GetAccessTokenAsync(CancellationToken cancellationToken) - { - return Task.FromResult(this.accessToken); - } - } -} diff --git a/src/Azure/Azure.Quantum.Client/Authentication/TokenCredentialProvider.cs b/src/Azure/Azure.Quantum.Client/Authentication/TokenCredentialProvider.cs deleted file mode 100644 index dd4805f7fda..00000000000 --- a/src/Azure/Azure.Quantum.Client/Authentication/TokenCredentialProvider.cs +++ /dev/null @@ -1,43 +0,0 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT License. - -using System.Threading; -using System.Threading.Tasks; -using Azure.Core; -using Microsoft.Azure.Quantum.Utility; - -namespace Microsoft.Azure.Quantum.Authentication -{ - /// - /// A provider for TokenCredential - /// - /// - internal class TokenCredentialProvider : IAccessTokenProvider - { - private readonly TokenCredential tokenCredential; - private readonly string[] scopes; - - /// - /// Initializes a new instance of the class. - /// - /// The token credential. - public TokenCredentialProvider(TokenCredential tokenCredential) - { - this.tokenCredential = tokenCredential; - this.scopes = new string[] { Constants.Aad.Audience }; - } - - /// - /// Gets the access token. - /// - /// The cancellation token. - /// - /// Access token string - /// - public async Task GetAccessTokenAsync(CancellationToken cancellationToken) - { - AccessToken accessToken = await this.tokenCredential.GetTokenAsync(new TokenRequestContext(this.scopes), cancellationToken); - return accessToken.Token; - } - } -} diff --git a/src/Azure/Azure.Quantum.Client/Exceptions/WorkspaceClientException.cs b/src/Azure/Azure.Quantum.Client/Exceptions/WorkspaceClientException.cs index e33d38930ae..9c70b86fdcd 100644 --- a/src/Azure/Azure.Quantum.Client/Exceptions/WorkspaceClientException.cs +++ b/src/Azure/Azure.Quantum.Client/Exceptions/WorkspaceClientException.cs @@ -1,7 +1,6 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT License. -using Microsoft.Azure.Quantum.Client.Models; using System; namespace Microsoft.Azure.Quantum.Exceptions @@ -13,6 +12,10 @@ public class WorkspaceClientException : AzureQuantumException { private const string BaseMessage = "An exception related to the Azure workspace client occurred"; + public string ErrorCode { get; } + + public int Status { get; } + /// /// Initializes a new instance of the class with a default error message. /// @@ -50,7 +53,7 @@ public WorkspaceClientException( /// ID of the subscription used by the Azure workspace client. /// Name of the resource group used by the Azure workspace client. /// Name of the workspace used by the Azure workspace client. - /// URI used by the Azure workspace client. + /// Workspace's location (region). /// ID of the job involved in the operation that caused the exception. /// Exception that is the cause of the current one. public WorkspaceClientException( @@ -58,7 +61,7 @@ public WorkspaceClientException( string subscriptionId, string resourceGroupName, string workspaceName, - Uri baseUri, + string location, string jobId, Exception inner) : base( @@ -66,11 +69,17 @@ public WorkspaceClientException( $"SubscriptionId: {subscriptionId}{Environment.NewLine}" + $"ResourceGroupName: {resourceGroupName}{Environment.NewLine}" + $"WorkspaceName: {workspaceName}{Environment.NewLine}" + - $"BaseUri: {baseUri}{Environment.NewLine}" + + $"Location: {location}{Environment.NewLine}" + $"JobId: {jobId}{Environment.NewLine}" + FormatInnerException(inner), inner) { + // Handle specific types of exceptions for additional data + if (inner is global::Azure.RequestFailedException requestException) + { + this.ErrorCode = requestException.ErrorCode; + this.Status = requestException.Status; + } } /// @@ -87,19 +96,6 @@ private static string FormatInnerException(Exception ex) if (ex != null) { formattedException += $"Server Error: {ex.Message}{Environment.NewLine}"; - - // Handle specific types of exceptions for additional data - if (ex is RestErrorException restErrorException) - { - formattedException += $"Error Code: {restErrorException?.Body?.Error?.Code}{Environment.NewLine}" + - $"Server message: {restErrorException?.Body?.Error?.Message}{Environment.NewLine}"; - - var headers = restErrorException?.Response?.Headers; - if (headers != null && headers.ContainsKey("x-ms-request-id")) - { - formattedException += $"Server Request Id: {headers["x-ms-request-id"]}{Environment.NewLine}"; - } - } } return formattedException; diff --git a/src/Azure/Azure.Quantum.Client/JobManagement/CloudJob.cs b/src/Azure/Azure.Quantum.Client/JobManagement/CloudJob.cs index bfc879eb9d2..34d7360b81b 100644 --- a/src/Azure/Azure.Quantum.Client/JobManagement/CloudJob.cs +++ b/src/Azure/Azure.Quantum.Client/JobManagement/CloudJob.cs @@ -6,7 +6,9 @@ namespace Microsoft.Azure.Quantum using System; using System.Threading; using System.Threading.Tasks; - using Microsoft.Azure.Quantum.Client.Models; + + using global::Azure.Quantum.Jobs.Models; + using Microsoft.Azure.Quantum.Utility; using Microsoft.Quantum.Runtime; @@ -38,45 +40,46 @@ public CloudJob(IWorkspace workspace, JobDetails jobDetails) /// /// Gets the job id. /// - public string Id => Details.Id; + public virtual string Id => Details.Id; /// /// Gets whether the job execution has completed. /// - public bool InProgress => Status != "Cancelled" && + public virtual bool InProgress => Status != "Cancelled" && Status != "Failed" && Status != "Succeeded"; /// /// Gets the status of the submitted job. /// - public string Status => Details.Status; + public virtual string Status => Details.Status?.ToString(); /// /// Gets whether the job execution completed successfully. /// - public bool Succeeded => Status == "Succeeded"; + public virtual bool Succeeded => Status == "Succeeded"; /// /// Gets an URI to access the job. /// - public Uri Uri => GenerateUri(); + public virtual Uri Uri => GenerateUri(); /// /// Gets the workspace. /// - public IWorkspace Workspace { get; private set; } + public virtual IWorkspace Workspace { get; private set; } /// /// Gets the job details. /// - public JobDetails Details { get; private set; } + public virtual JobDetails Details { get; private set; } /// /// Refreshes the job. /// /// The cancellation token. - public async Task RefreshAsync(CancellationToken cancellationToken = default) + /// + public virtual async Task RefreshAsync(CancellationToken cancellationToken = default) { CloudJob job = (CloudJob)await this.Workspace.GetJobAsync(this.Details.Id, cancellationToken); this.Details = job.Details; @@ -86,12 +89,30 @@ public async Task RefreshAsync(CancellationToken cancellationToken = default) /// Cancels the job. /// /// The cancellation token. - public async Task CancelAsync(CancellationToken cancellationToken = default) + /// + public virtual async Task CancelAsync(CancellationToken cancellationToken = default) { CloudJob job = (CloudJob)await this.Workspace.CancelJobAsync(this.Details.Id, cancellationToken); this.Details = job.Details; } + /// + /// Keeps polling the server until the Job status changes to be not in progress. + /// + /// Time to wait between polls. Defaults to 500. + /// Cancellation token. + /// + public async Task WaitForCompletion(int pollIntervalMilliseconds = 500, CancellationToken cancellationToken = default) + { + await this.RefreshAsync(cancellationToken); + + while (this.InProgress) + { + await Task.Delay(TimeSpan.FromMilliseconds(pollIntervalMilliseconds)); + await this.RefreshAsync(); + } + } + private Uri GenerateUri() { if (!(this.Workspace is Workspace cloudWorkspace)) diff --git a/src/Azure/Azure.Quantum.Client/JobManagement/IWorkspace.cs b/src/Azure/Azure.Quantum.Client/JobManagement/IWorkspace.cs index a7d9bab7170..e2ac5921114 100644 --- a/src/Azure/Azure.Quantum.Client/JobManagement/IWorkspace.cs +++ b/src/Azure/Azure.Quantum.Client/JobManagement/IWorkspace.cs @@ -48,7 +48,7 @@ Task GetJobAsync( /// /// The cancellation token. /// List of jobs - Task> ListJobsAsync( + IAsyncEnumerable ListJobsAsync( CancellationToken cancellationToken = default); /// @@ -56,7 +56,7 @@ Task> ListJobsAsync( /// /// The cancellation token. /// List of jobs - Task> ListQuotasAsync( + IAsyncEnumerable ListQuotasAsync( CancellationToken cancellationToken = default); /// diff --git a/src/Azure/Azure.Quantum.Client/JobManagement/QuotaInfo.cs b/src/Azure/Azure.Quantum.Client/JobManagement/QuotaInfo.cs index 71160112136..6906963b65a 100644 --- a/src/Azure/Azure.Quantum.Client/JobManagement/QuotaInfo.cs +++ b/src/Azure/Azure.Quantum.Client/JobManagement/QuotaInfo.cs @@ -3,7 +3,8 @@ namespace Microsoft.Azure.Quantum { - using Microsoft.Azure.Quantum.Client.Models; + using global::Azure.Quantum.Jobs.Models; + using Microsoft.Azure.Quantum.Utility; /// @@ -16,7 +17,7 @@ public class QuotaInfo /// /// The workspace. /// The job details. - public QuotaInfo(IWorkspace workspace, Quota quota) + public QuotaInfo(IWorkspace workspace, QuantumJobQuota quota) { Ensure.NotNull(workspace, nameof(workspace)); Ensure.NotNull(quota, nameof(quota)); @@ -33,6 +34,6 @@ public QuotaInfo(IWorkspace workspace, Quota quota) /// /// Gets the quota information. /// - public Quota Quota { get; private set; } + public QuantumJobQuota Quota { get; private set; } } } diff --git a/src/Azure/Azure.Quantum.Client/JobManagement/Workspace.cs b/src/Azure/Azure.Quantum.Client/JobManagement/Workspace.cs index ec306e5e2dd..2b269a945a8 100644 --- a/src/Azure/Azure.Quantum.Client/JobManagement/Workspace.cs +++ b/src/Azure/Azure.Quantum.Client/JobManagement/Workspace.cs @@ -6,12 +6,15 @@ namespace Microsoft.Azure.Quantum using System; using System.Collections.Generic; using System.Linq; + using System.Runtime.CompilerServices; using System.Threading; using System.Threading.Tasks; using global::Azure.Core; - using Microsoft.Azure.Quantum.Authentication; - using Microsoft.Azure.Quantum.Client; - using Microsoft.Azure.Quantum.Client.Models; + using global::Azure.Identity; + using global::Azure.Quantum; + using global::Azure.Quantum.Jobs; + using global::Azure.Quantum.Jobs.Models; + using Microsoft.Azure.Quantum.Exceptions; using Microsoft.Azure.Quantum.Utility; @@ -21,159 +24,59 @@ namespace Microsoft.Azure.Quantum /// public class Workspace : IWorkspace { - private readonly Uri baseUri; - private readonly string resourceGroupName; - private readonly string subscriptionId; - private readonly string workspaceName; - /// /// Initializes a new instance of the class. /// /// The subscription identifier. /// Name of the resource group. /// Name of the workspace. - /// The token credential. - /// The base URI. - public Workspace( - string subscriptionId, - string resourceGroupName, - string workspaceName, - TokenCredential tokenCredential = null, - Uri baseUri = null) - : this( - subscriptionId, - resourceGroupName, - workspaceName, - tokenCredential == null ? null : new TokenCredentialProvider(tokenCredential), - baseUri) - { - } - - /// - /// Initializes a new instance of the class. - /// - /// The subscription identifier. - /// Name of the resource group. - /// Name of the workspace. - /// Normalized location to use with the default endpoint. - /// The token credential. + /// Azure region where the workspace was created. + /// The credentials to connect to Azure. If not provided it defaults to an interactive DefaultAzureCredentials. + /// Options for the client library when communication with Azure Service.. public Workspace( string subscriptionId, string resourceGroupName, string workspaceName, string location, - TokenCredential tokenCredential = null) - : this( - subscriptionId, - resourceGroupName, - workspaceName, - tokenCredential, - new Uri($"https://{location}.{Constants.DefaultLocationlessEndpoint}/")) + TokenCredential credential = null, + QuantumJobClientOptions options = default) { - } - - /// - /// Initializes a new instance of the class. - /// - /// The subscription identifier. - /// Name of the resource group. - /// Name of the workspace. - /// The access token. - /// The base URI. - public Workspace( - string subscriptionId, - string resourceGroupName, - string workspaceName, - string accessToken, - Uri baseUri = null) - : this( - subscriptionId, - resourceGroupName, - workspaceName, - new StaticAccessTokenProvider(accessToken), - baseUri) - { - } - - /// - /// Initializes a new instance of the class. - /// - /// The subscription identifier. - /// Name of the resource group. - /// Name of the workspace. - /// The access token. - /// Normalized location to use with the default endpoint. - public Workspace( - string subscriptionId, - string resourceGroupName, - string workspaceName, - string accessToken, - string location) - : this( - subscriptionId, - resourceGroupName, - workspaceName, - new StaticAccessTokenProvider(accessToken), - new Uri($"https://{location}.{Constants.DefaultLocationlessEndpoint}/")) - { - } - - private Workspace( - string subscriptionId, - string resourceGroupName, - string workspaceName, - IAccessTokenProvider accessTokenProvider, - Uri baseUri = null) - { - this.baseUri = baseUri ?? new Uri($"https://{Constants.DefaultLocation}.{Constants.DefaultLocationlessEndpoint}/"); + // Required parameters: Ensure.NotNullOrWhiteSpace(subscriptionId, nameof(subscriptionId)); - this.subscriptionId = subscriptionId; Ensure.NotNullOrWhiteSpace(resourceGroupName, nameof(resourceGroupName)); - this.resourceGroupName = resourceGroupName; Ensure.NotNullOrWhiteSpace(workspaceName, nameof(workspaceName)); - this.workspaceName = workspaceName; - - try - { - accessTokenProvider = accessTokenProvider ?? new CustomAccessTokenProvider(subscriptionId); - } - catch (Exception ex) - { - throw CreateException(ex, "Could not create an access token provider"); - } - - Ensure.NotNull(accessTokenProvider, nameof(accessTokenProvider)); - - try - { - this.QuantumClient = new QuantumClient(new ClientCredentials(accessTokenProvider)) - { - BaseUri = this.baseUri, - SubscriptionId = subscriptionId, - ResourceGroupName = resourceGroupName, - WorkspaceName = workspaceName, - }; - } - catch (Exception ex) - { - throw CreateException(ex, "Could not create an Azure quantum service client"); - } + Ensure.NotNullOrWhiteSpace(location, nameof(location)); + + // Optional parameters: + credential ??= new DefaultAzureCredential(includeInteractiveCredentials: true); + options ??= new QuantumJobClientOptions(); + + this.ResourceGroupName = resourceGroupName; + this.WorkspaceName = workspaceName; + this.SubscriptionId = subscriptionId; + this.Location = location; + + this.Client = new QuantumJobClient( + subscriptionId, + resourceGroupName, + workspaceName, + location, + credential, + options); } - public string ResourceGroupName { get => resourceGroupName; } + public string ResourceGroupName { get; } - public string SubscriptionId { get => subscriptionId; } + public string SubscriptionId { get; } - public string WorkspaceName { get => workspaceName; } + public string WorkspaceName { get; } + + public string Location { get; } /// - /// Gets or sets the jobs client. - /// Internal only. + /// The client used to communicate with the service. /// - /// - /// The jobs client. - /// - internal IQuantumClient QuantumClient { get; set; } + public QuantumJobClient Client { get; } /// /// Submits the job. @@ -192,7 +95,7 @@ public async Task SubmitJobAsync( try { - JobDetails jobDetails = await this.QuantumClient.Jobs.CreateAsync( + JobDetails jobDetails = await this.Client.CreateJobAsync( jobId: jobDefinition.Details.Id, job: jobDefinition.Details, cancellationToken: cancellationToken); @@ -217,11 +120,11 @@ public async Task CancelJobAsync(string jobId, CancellationToken cance try { - await this.QuantumClient.Jobs.CancelAsync( + await this.Client.CancelJobAsync( jobId: jobId, cancellationToken: cancellationToken); - JobDetails jobDetails = this.QuantumClient.Jobs.Get(jobId); + JobDetails jobDetails = this.Client.GetJob(jobId); return new CloudJob(this, jobDetails); } @@ -245,7 +148,7 @@ public async Task GetJobAsync(string jobId, CancellationToken cancella try { - JobDetails jobDetails = await this.QuantumClient.Jobs.GetAsync( + JobDetails jobDetails = await this.Client.GetJobAsync( jobId: jobId, cancellationToken: cancellationToken); @@ -264,19 +167,13 @@ public async Task GetJobAsync(string jobId, CancellationToken cancella /// /// List of jobs. /// - public async Task> ListJobsAsync(CancellationToken cancellationToken = default) + public async IAsyncEnumerable ListJobsAsync([EnumeratorCancellation] CancellationToken cancellationToken = default) { - try - { - var jobs = await this.QuantumClient.Jobs.ListAsync( - cancellationToken: cancellationToken); + var jobs = this.Client.GetJobsAsync().WithCancellation(cancellationToken); - return jobs - .Select(details => new CloudJob(this, details)); - } - catch (Exception ex) + await foreach (var j in jobs) { - throw CreateException(ex, "Could not list jobs"); + yield return new CloudJob(this, j); } } @@ -287,18 +184,13 @@ public async Task> ListJobsAsync(CancellationToken cancell /// /// List of quotas. /// - public async Task> ListQuotasAsync(CancellationToken cancellationToken = default) + public async IAsyncEnumerable ListQuotasAsync([EnumeratorCancellation] CancellationToken cancellationToken = default) { - try - { - var quotas = await this.QuantumClient.Quotas.ListAsync( - cancellationToken: cancellationToken); + var quotas = this.Client.GetQuotasAsync(cancellationToken); - return quotas.Select(quota => new QuotaInfo(this, quota)); - } - catch (Exception ex) + await foreach (var q in quotas) { - throw CreateException(ex, "Could not list quotas"); + yield return new QuotaInfo(this, q); } } @@ -313,14 +205,13 @@ public async Task> ListQuotasAsync(CancellationToken canc /// public async Task GetSasUriAsync(string containerName, string blobName = null, CancellationToken cancellationToken = default) { - BlobDetails details = new BlobDetails + BlobDetails details = new BlobDetails(containerName) { - ContainerName = containerName, BlobName = blobName, }; - var response = await this.QuantumClient.Storage.SasUriAsync(details, cancellationToken); - return response.SasUri; + var response = await this.Client.GetStorageSasUriAsync(details, cancellationToken); + return response.Value.SasUri; } private WorkspaceClientException CreateException( @@ -330,10 +221,10 @@ private WorkspaceClientException CreateException( { return new WorkspaceClientException( message, - subscriptionId, - resourceGroupName, - workspaceName, - baseUri, + this.SubscriptionId, + this.ResourceGroupName, + this.WorkspaceName, + this.Location, jobId, inner); } diff --git a/src/Azure/Azure.Quantum.Client/JobManagement/WorkspaceExtensions.cs b/src/Azure/Azure.Quantum.Client/JobManagement/WorkspaceExtensions.cs index 6d1730003b5..d220d2560d0 100644 --- a/src/Azure/Azure.Quantum.Client/JobManagement/WorkspaceExtensions.cs +++ b/src/Azure/Azure.Quantum.Client/JobManagement/WorkspaceExtensions.cs @@ -44,15 +44,5 @@ public static CloudJob GetJob(this IWorkspace workspace, string jobId) { return workspace.GetJobAsync(jobId).GetAwaiter().GetResult(); } - - /// - /// Lists the jobs. - /// - /// The workspace. - /// List of job identifiers. - public static IEnumerable ListJobs(this IWorkspace workspace) - { - return workspace.ListJobsAsync().GetAwaiter().GetResult(); - } } } diff --git a/src/Azure/Azure.Quantum.Client/Microsoft.Azure.Quantum.Client.csproj b/src/Azure/Azure.Quantum.Client/Microsoft.Azure.Quantum.Client.csproj index 5cbb18eb53b..28a0ded1164 100644 --- a/src/Azure/Azure.Quantum.Client/Microsoft.Azure.Quantum.Client.csproj +++ b/src/Azure/Azure.Quantum.Client/Microsoft.Azure.Quantum.Client.csproj @@ -19,11 +19,10 @@ - + + + - - - all diff --git a/src/Azure/Azure.Quantum.Client/generated/IJobsOperations.cs b/src/Azure/Azure.Quantum.Client/generated/IJobsOperations.cs deleted file mode 100644 index af9fd24eb91..00000000000 --- a/src/Azure/Azure.Quantum.Client/generated/IJobsOperations.cs +++ /dev/null @@ -1,134 +0,0 @@ -// -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT License. See License.txt in the project root for -// license information. -// -// Code generated by Microsoft (R) AutoRest Code Generator. -// Changes may cause incorrect behavior and will be lost if the code is -// regenerated. -// - -namespace Microsoft.Azure.Quantum.Client -{ - using Microsoft.Rest; - using Microsoft.Rest.Azure; - using Models; - using System.Collections; - using System.Collections.Generic; - using System.Threading; - using System.Threading.Tasks; - - /// - /// JobsOperations operations. - /// - public partial interface IJobsOperations - { - /// - /// List jobs. - /// - /// - /// The headers that will be added to request. - /// - /// - /// The cancellation token. - /// - /// - /// Thrown when the operation returned an invalid status code - /// - /// - /// Thrown when unable to deserialize the response - /// - /// - /// Thrown when a required parameter is null - /// - Task>> ListWithHttpMessagesAsync(Dictionary> customHeaders = null, CancellationToken cancellationToken = default(CancellationToken)); - /// - /// Get job by id - /// - /// - /// Id of the job. - /// - /// - /// The headers that will be added to request. - /// - /// - /// The cancellation token. - /// - /// - /// Thrown when the operation returned an invalid status code - /// - /// - /// Thrown when unable to deserialize the response - /// - /// - /// Thrown when a required parameter is null - /// - Task> GetWithHttpMessagesAsync(string jobId, Dictionary> customHeaders = null, CancellationToken cancellationToken = default(CancellationToken)); - /// - /// Create a job. - /// - /// - /// Id of the job. - /// - /// - /// The complete metadata of the job to submit. - /// - /// - /// The headers that will be added to request. - /// - /// - /// The cancellation token. - /// - /// - /// Thrown when the operation returned an invalid status code - /// - /// - /// Thrown when unable to deserialize the response - /// - /// - /// Thrown when a required parameter is null - /// - Task> CreateWithHttpMessagesAsync(string jobId, JobDetails job, Dictionary> customHeaders = null, CancellationToken cancellationToken = default(CancellationToken)); - /// - /// Cancel a job. - /// - /// - /// Id of the job. - /// - /// - /// The headers that will be added to request. - /// - /// - /// The cancellation token. - /// - /// - /// Thrown when the operation returned an invalid status code - /// - /// - /// Thrown when a required parameter is null - /// - Task CancelWithHttpMessagesAsync(string jobId, Dictionary> customHeaders = null, CancellationToken cancellationToken = default(CancellationToken)); - /// - /// List jobs. - /// - /// - /// The NextLink from the previous successful call to List operation. - /// - /// - /// The headers that will be added to request. - /// - /// - /// The cancellation token. - /// - /// - /// Thrown when the operation returned an invalid status code - /// - /// - /// Thrown when unable to deserialize the response - /// - /// - /// Thrown when a required parameter is null - /// - Task>> ListNextWithHttpMessagesAsync(string nextPageLink, Dictionary> customHeaders = null, CancellationToken cancellationToken = default(CancellationToken)); - } -} diff --git a/src/Azure/Azure.Quantum.Client/generated/IProvidersOperations.cs b/src/Azure/Azure.Quantum.Client/generated/IProvidersOperations.cs deleted file mode 100644 index 2cc635ca7c8..00000000000 --- a/src/Azure/Azure.Quantum.Client/generated/IProvidersOperations.cs +++ /dev/null @@ -1,68 +0,0 @@ -// -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT License. See License.txt in the project root for -// license information. -// -// Code generated by Microsoft (R) AutoRest Code Generator. -// Changes may cause incorrect behavior and will be lost if the code is -// regenerated. -// - -namespace Microsoft.Azure.Quantum.Client -{ - using Microsoft.Rest; - using Microsoft.Rest.Azure; - using Models; - using System.Collections; - using System.Collections.Generic; - using System.Threading; - using System.Threading.Tasks; - - /// - /// ProvidersOperations operations. - /// - public partial interface IProvidersOperations - { - /// - /// Get provider status. - /// - /// - /// The headers that will be added to request. - /// - /// - /// The cancellation token. - /// - /// - /// Thrown when the operation returned an invalid status code - /// - /// - /// Thrown when unable to deserialize the response - /// - /// - /// Thrown when a required parameter is null - /// - Task>> GetStatusWithHttpMessagesAsync(Dictionary> customHeaders = null, CancellationToken cancellationToken = default(CancellationToken)); - /// - /// Get provider status. - /// - /// - /// The NextLink from the previous successful call to List operation. - /// - /// - /// The headers that will be added to request. - /// - /// - /// The cancellation token. - /// - /// - /// Thrown when the operation returned an invalid status code - /// - /// - /// Thrown when unable to deserialize the response - /// - /// - /// Thrown when a required parameter is null - /// - Task>> GetStatusNextWithHttpMessagesAsync(string nextPageLink, Dictionary> customHeaders = null, CancellationToken cancellationToken = default(CancellationToken)); - } -} diff --git a/src/Azure/Azure.Quantum.Client/generated/IQuantumClient.cs b/src/Azure/Azure.Quantum.Client/generated/IQuantumClient.cs deleted file mode 100644 index 6f0f9edbf5c..00000000000 --- a/src/Azure/Azure.Quantum.Client/generated/IQuantumClient.cs +++ /dev/null @@ -1,99 +0,0 @@ -// -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT License. See License.txt in the project root for -// license information. -// -// Code generated by Microsoft (R) AutoRest Code Generator. -// Changes may cause incorrect behavior and will be lost if the code is -// regenerated. -// - -namespace Microsoft.Azure.Quantum.Client -{ - using Microsoft.Rest; - using Microsoft.Rest.Azure; - using Models; - using Newtonsoft.Json; - - /// - /// Azure Quantum REST API client - /// - public partial interface IQuantumClient : System.IDisposable - { - /// - /// The base URI of the service. - /// - System.Uri BaseUri { get; set; } - - /// - /// Gets or sets json serialization settings. - /// - JsonSerializerSettings SerializationSettings { get; } - - /// - /// Gets or sets json deserialization settings. - /// - JsonSerializerSettings DeserializationSettings { get; } - - /// - /// Credentials needed for the client to connect to Azure. - /// - ServiceClientCredentials Credentials { get; } - - /// - /// The Azure subscription ID. This is a GUID-formatted string (e.g. - /// 00000000-0000-0000-0000-000000000000) - /// - string SubscriptionId { get; set; } - - /// - /// Name of an Azure resource group. - /// - string ResourceGroupName { get; set; } - - /// - /// Name of the workspace. - /// - string WorkspaceName { get; set; } - - /// - /// The preferred language for the response. - /// - string AcceptLanguage { get; set; } - - /// - /// The retry timeout in seconds for Long Running Operations. Default - /// value is 30. - /// - int? LongRunningOperationRetryTimeout { get; set; } - - /// - /// Whether a unique x-ms-client-request-id should be generated. When - /// set to true a unique x-ms-client-request-id value is generated and - /// included in each request. Default is true. - /// - bool? GenerateClientRequestId { get; set; } - - - /// - /// Gets the IJobsOperations. - /// - IJobsOperations Jobs { get; } - - /// - /// Gets the IProvidersOperations. - /// - IProvidersOperations Providers { get; } - - /// - /// Gets the IStorageOperations. - /// - IStorageOperations Storage { get; } - - /// - /// Gets the IQuotasOperations. - /// - IQuotasOperations Quotas { get; } - - } -} diff --git a/src/Azure/Azure.Quantum.Client/generated/IQuotasOperations.cs b/src/Azure/Azure.Quantum.Client/generated/IQuotasOperations.cs deleted file mode 100644 index b25390bb5df..00000000000 --- a/src/Azure/Azure.Quantum.Client/generated/IQuotasOperations.cs +++ /dev/null @@ -1,68 +0,0 @@ -// -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT License. See License.txt in the project root for -// license information. -// -// Code generated by Microsoft (R) AutoRest Code Generator. -// Changes may cause incorrect behavior and will be lost if the code is -// regenerated. -// - -namespace Microsoft.Azure.Quantum.Client -{ - using Microsoft.Rest; - using Microsoft.Rest.Azure; - using Models; - using System.Collections; - using System.Collections.Generic; - using System.Threading; - using System.Threading.Tasks; - - /// - /// QuotasOperations operations. - /// - public partial interface IQuotasOperations - { - /// - /// List quotas for the given workspace. - /// - /// - /// The headers that will be added to request. - /// - /// - /// The cancellation token. - /// - /// - /// Thrown when the operation returned an invalid status code - /// - /// - /// Thrown when unable to deserialize the response - /// - /// - /// Thrown when a required parameter is null - /// - Task>> ListWithHttpMessagesAsync(Dictionary> customHeaders = null, CancellationToken cancellationToken = default(CancellationToken)); - /// - /// List quotas for the given workspace. - /// - /// - /// The NextLink from the previous successful call to List operation. - /// - /// - /// The headers that will be added to request. - /// - /// - /// The cancellation token. - /// - /// - /// Thrown when the operation returned an invalid status code - /// - /// - /// Thrown when unable to deserialize the response - /// - /// - /// Thrown when a required parameter is null - /// - Task>> ListNextWithHttpMessagesAsync(string nextPageLink, Dictionary> customHeaders = null, CancellationToken cancellationToken = default(CancellationToken)); - } -} diff --git a/src/Azure/Azure.Quantum.Client/generated/IStorageOperations.cs b/src/Azure/Azure.Quantum.Client/generated/IStorageOperations.cs deleted file mode 100644 index 838761aab6c..00000000000 --- a/src/Azure/Azure.Quantum.Client/generated/IStorageOperations.cs +++ /dev/null @@ -1,52 +0,0 @@ -// -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT License. See License.txt in the project root for -// license information. -// -// Code generated by Microsoft (R) AutoRest Code Generator. -// Changes may cause incorrect behavior and will be lost if the code is -// regenerated. -// - -namespace Microsoft.Azure.Quantum.Client -{ - using Microsoft.Rest; - using Microsoft.Rest.Azure; - using Models; - using System.Collections; - using System.Collections.Generic; - using System.Threading; - using System.Threading.Tasks; - - /// - /// StorageOperations operations. - /// - public partial interface IStorageOperations - { - /// - /// Gets a URL with SAS token for a container/blob in the storage - /// account associated with the workspace. The SAS URL can be used to - /// upload job input and/or download job output. - /// - /// - /// The details (name and container) of the blob to store or download - /// data. - /// - /// - /// The headers that will be added to request. - /// - /// - /// The cancellation token. - /// - /// - /// Thrown when the operation returned an invalid status code - /// - /// - /// Thrown when unable to deserialize the response - /// - /// - /// Thrown when a required parameter is null - /// - Task> SasUriWithHttpMessagesAsync(BlobDetails blobDetails, Dictionary> customHeaders = null, CancellationToken cancellationToken = default(CancellationToken)); - } -} diff --git a/src/Azure/Azure.Quantum.Client/generated/JobsOperations.cs b/src/Azure/Azure.Quantum.Client/generated/JobsOperations.cs deleted file mode 100644 index 250d26ab5a2..00000000000 --- a/src/Azure/Azure.Quantum.Client/generated/JobsOperations.cs +++ /dev/null @@ -1,974 +0,0 @@ -// -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT License. See License.txt in the project root for -// license information. -// -// Code generated by Microsoft (R) AutoRest Code Generator. -// Changes may cause incorrect behavior and will be lost if the code is -// regenerated. -// - -namespace Microsoft.Azure.Quantum.Client -{ - using Microsoft.Rest; - using Microsoft.Rest.Azure; - using Models; - using Newtonsoft.Json; - using System.Collections; - using System.Collections.Generic; - using System.Linq; - using System.Net; - using System.Net.Http; - using System.Threading; - using System.Threading.Tasks; - - /// - /// JobsOperations operations. - /// - internal partial class JobsOperations : IServiceOperations, IJobsOperations - { - /// - /// Initializes a new instance of the JobsOperations class. - /// - /// - /// Reference to the service client. - /// - /// - /// Thrown when a required parameter is null - /// - internal JobsOperations(QuantumClient client) - { - if (client == null) - { - throw new System.ArgumentNullException("client"); - } - Client = client; - } - - /// - /// Gets a reference to the QuantumClient - /// - public QuantumClient Client { get; private set; } - - /// - /// List jobs. - /// - /// - /// Headers that will be added to request. - /// - /// - /// The cancellation token. - /// - /// - /// Thrown when the operation returned an invalid status code - /// - /// - /// Thrown when unable to deserialize the response - /// - /// - /// Thrown when a required parameter is null - /// - /// - /// Thrown when a required parameter is null - /// - /// - /// A response object containing the response body and response headers. - /// - public async Task>> ListWithHttpMessagesAsync(Dictionary> customHeaders = null, CancellationToken cancellationToken = default(CancellationToken)) - { - if (Client.SubscriptionId == null) - { - throw new ValidationException(ValidationRules.CannotBeNull, "this.Client.SubscriptionId"); - } - if (Client.ResourceGroupName == null) - { - throw new ValidationException(ValidationRules.CannotBeNull, "this.Client.ResourceGroupName"); - } - if (Client.WorkspaceName == null) - { - throw new ValidationException(ValidationRules.CannotBeNull, "this.Client.WorkspaceName"); - } - // Tracing - bool _shouldTrace = ServiceClientTracing.IsEnabled; - string _invocationId = null; - if (_shouldTrace) - { - _invocationId = ServiceClientTracing.NextInvocationId.ToString(); - Dictionary tracingParameters = new Dictionary(); - tracingParameters.Add("cancellationToken", cancellationToken); - ServiceClientTracing.Enter(_invocationId, this, "List", tracingParameters); - } - // Construct URL - var _baseUrl = Client.BaseUri.AbsoluteUri; - var _url = new System.Uri(new System.Uri(_baseUrl + (_baseUrl.EndsWith("/") ? "" : "/")), "v1.0/subscriptions/{subscriptionId}/resourceGroups/{resourceGroupName}/providers/Microsoft.Quantum/workspaces/{workspaceName}/jobs").ToString(); - _url = _url.Replace("{subscriptionId}", System.Uri.EscapeDataString(Client.SubscriptionId)); - _url = _url.Replace("{resourceGroupName}", System.Uri.EscapeDataString(Client.ResourceGroupName)); - _url = _url.Replace("{workspaceName}", System.Uri.EscapeDataString(Client.WorkspaceName)); - List _queryParameters = new List(); - if (_queryParameters.Count > 0) - { - _url += (_url.Contains("?") ? "&" : "?") + string.Join("&", _queryParameters); - } - // Create HTTP transport objects - var _httpRequest = new HttpRequestMessage(); - HttpResponseMessage _httpResponse = null; - _httpRequest.Method = new HttpMethod("GET"); - _httpRequest.RequestUri = new System.Uri(_url); - // Set Headers - if (Client.GenerateClientRequestId != null && Client.GenerateClientRequestId.Value) - { - _httpRequest.Headers.TryAddWithoutValidation("x-ms-client-request-id", System.Guid.NewGuid().ToString()); - } - if (Client.AcceptLanguage != null) - { - if (_httpRequest.Headers.Contains("accept-language")) - { - _httpRequest.Headers.Remove("accept-language"); - } - _httpRequest.Headers.TryAddWithoutValidation("accept-language", Client.AcceptLanguage); - } - - - if (customHeaders != null) - { - foreach(var _header in customHeaders) - { - if (_httpRequest.Headers.Contains(_header.Key)) - { - _httpRequest.Headers.Remove(_header.Key); - } - _httpRequest.Headers.TryAddWithoutValidation(_header.Key, _header.Value); - } - } - - // Serialize Request - string _requestContent = null; - // Set Credentials - if (Client.Credentials != null) - { - cancellationToken.ThrowIfCancellationRequested(); - await Client.Credentials.ProcessHttpRequestAsync(_httpRequest, cancellationToken).ConfigureAwait(false); - } - // Send Request - if (_shouldTrace) - { - ServiceClientTracing.SendRequest(_invocationId, _httpRequest); - } - cancellationToken.ThrowIfCancellationRequested(); - _httpResponse = await Client.HttpClient.SendAsync(_httpRequest, cancellationToken).ConfigureAwait(false); - if (_shouldTrace) - { - ServiceClientTracing.ReceiveResponse(_invocationId, _httpResponse); - } - HttpStatusCode _statusCode = _httpResponse.StatusCode; - cancellationToken.ThrowIfCancellationRequested(); - string _responseContent = null; - if ((int)_statusCode != 200) - { - var ex = new CloudException(string.Format("Operation returned an invalid status code '{0}'", _statusCode)); - try - { - _responseContent = await _httpResponse.Content.ReadAsStringAsync().ConfigureAwait(false); - CloudError _errorBody = Rest.Serialization.SafeJsonConvert.DeserializeObject(_responseContent, Client.DeserializationSettings); - if (_errorBody != null) - { - ex = new CloudException(_errorBody.Message); - ex.Body = _errorBody; - } - } - catch (JsonException) - { - // Ignore the exception - } - ex.Request = new HttpRequestMessageWrapper(_httpRequest, _requestContent); - ex.Response = new HttpResponseMessageWrapper(_httpResponse, _responseContent); - if (_httpResponse.Headers.Contains("x-ms-request-id")) - { - ex.RequestId = _httpResponse.Headers.GetValues("x-ms-request-id").FirstOrDefault(); - } - if (_shouldTrace) - { - ServiceClientTracing.Error(_invocationId, ex); - } - _httpRequest.Dispose(); - if (_httpResponse != null) - { - _httpResponse.Dispose(); - } - throw ex; - } - // Create Result - var _result = new AzureOperationResponse>(); - _result.Request = _httpRequest; - _result.Response = _httpResponse; - if (_httpResponse.Headers.Contains("x-ms-request-id")) - { - _result.RequestId = _httpResponse.Headers.GetValues("x-ms-request-id").FirstOrDefault(); - } - // Deserialize Response - if ((int)_statusCode == 200) - { - _responseContent = await _httpResponse.Content.ReadAsStringAsync().ConfigureAwait(false); - try - { - _result.Body = Rest.Serialization.SafeJsonConvert.DeserializeObject>(_responseContent, Client.DeserializationSettings); - } - catch (JsonException ex) - { - _httpRequest.Dispose(); - if (_httpResponse != null) - { - _httpResponse.Dispose(); - } - throw new SerializationException("Unable to deserialize the response.", _responseContent, ex); - } - } - if (_shouldTrace) - { - ServiceClientTracing.Exit(_invocationId, _result); - } - return _result; - } - - /// - /// Get job by id - /// - /// - /// Id of the job. - /// - /// - /// Headers that will be added to request. - /// - /// - /// The cancellation token. - /// - /// - /// Thrown when the operation returned an invalid status code - /// - /// - /// Thrown when unable to deserialize the response - /// - /// - /// Thrown when a required parameter is null - /// - /// - /// Thrown when a required parameter is null - /// - /// - /// A response object containing the response body and response headers. - /// - public async Task> GetWithHttpMessagesAsync(string jobId, Dictionary> customHeaders = null, CancellationToken cancellationToken = default(CancellationToken)) - { - if (Client.SubscriptionId == null) - { - throw new ValidationException(ValidationRules.CannotBeNull, "this.Client.SubscriptionId"); - } - if (Client.ResourceGroupName == null) - { - throw new ValidationException(ValidationRules.CannotBeNull, "this.Client.ResourceGroupName"); - } - if (Client.WorkspaceName == null) - { - throw new ValidationException(ValidationRules.CannotBeNull, "this.Client.WorkspaceName"); - } - if (jobId == null) - { - throw new ValidationException(ValidationRules.CannotBeNull, "jobId"); - } - // Tracing - bool _shouldTrace = ServiceClientTracing.IsEnabled; - string _invocationId = null; - if (_shouldTrace) - { - _invocationId = ServiceClientTracing.NextInvocationId.ToString(); - Dictionary tracingParameters = new Dictionary(); - tracingParameters.Add("jobId", jobId); - tracingParameters.Add("cancellationToken", cancellationToken); - ServiceClientTracing.Enter(_invocationId, this, "Get", tracingParameters); - } - // Construct URL - var _baseUrl = Client.BaseUri.AbsoluteUri; - var _url = new System.Uri(new System.Uri(_baseUrl + (_baseUrl.EndsWith("/") ? "" : "/")), "v1.0/subscriptions/{subscriptionId}/resourceGroups/{resourceGroupName}/providers/Microsoft.Quantum/workspaces/{workspaceName}/jobs/{jobId}").ToString(); - _url = _url.Replace("{subscriptionId}", System.Uri.EscapeDataString(Client.SubscriptionId)); - _url = _url.Replace("{resourceGroupName}", System.Uri.EscapeDataString(Client.ResourceGroupName)); - _url = _url.Replace("{workspaceName}", System.Uri.EscapeDataString(Client.WorkspaceName)); - _url = _url.Replace("{jobId}", System.Uri.EscapeDataString(jobId)); - List _queryParameters = new List(); - if (_queryParameters.Count > 0) - { - _url += (_url.Contains("?") ? "&" : "?") + string.Join("&", _queryParameters); - } - // Create HTTP transport objects - var _httpRequest = new HttpRequestMessage(); - HttpResponseMessage _httpResponse = null; - _httpRequest.Method = new HttpMethod("GET"); - _httpRequest.RequestUri = new System.Uri(_url); - // Set Headers - if (Client.GenerateClientRequestId != null && Client.GenerateClientRequestId.Value) - { - _httpRequest.Headers.TryAddWithoutValidation("x-ms-client-request-id", System.Guid.NewGuid().ToString()); - } - if (Client.AcceptLanguage != null) - { - if (_httpRequest.Headers.Contains("accept-language")) - { - _httpRequest.Headers.Remove("accept-language"); - } - _httpRequest.Headers.TryAddWithoutValidation("accept-language", Client.AcceptLanguage); - } - - - if (customHeaders != null) - { - foreach(var _header in customHeaders) - { - if (_httpRequest.Headers.Contains(_header.Key)) - { - _httpRequest.Headers.Remove(_header.Key); - } - _httpRequest.Headers.TryAddWithoutValidation(_header.Key, _header.Value); - } - } - - // Serialize Request - string _requestContent = null; - // Set Credentials - if (Client.Credentials != null) - { - cancellationToken.ThrowIfCancellationRequested(); - await Client.Credentials.ProcessHttpRequestAsync(_httpRequest, cancellationToken).ConfigureAwait(false); - } - // Send Request - if (_shouldTrace) - { - ServiceClientTracing.SendRequest(_invocationId, _httpRequest); - } - cancellationToken.ThrowIfCancellationRequested(); - _httpResponse = await Client.HttpClient.SendAsync(_httpRequest, cancellationToken).ConfigureAwait(false); - if (_shouldTrace) - { - ServiceClientTracing.ReceiveResponse(_invocationId, _httpResponse); - } - HttpStatusCode _statusCode = _httpResponse.StatusCode; - cancellationToken.ThrowIfCancellationRequested(); - string _responseContent = null; - if ((int)_statusCode != 200) - { - var ex = new RestErrorException(string.Format("Operation returned an invalid status code '{0}'", _statusCode)); - try - { - _responseContent = await _httpResponse.Content.ReadAsStringAsync().ConfigureAwait(false); - RestError _errorBody = Rest.Serialization.SafeJsonConvert.DeserializeObject(_responseContent, Client.DeserializationSettings); - if (_errorBody != null) - { - ex.Body = _errorBody; - } - } - catch (JsonException) - { - // Ignore the exception - } - ex.Request = new HttpRequestMessageWrapper(_httpRequest, _requestContent); - ex.Response = new HttpResponseMessageWrapper(_httpResponse, _responseContent); - if (_shouldTrace) - { - ServiceClientTracing.Error(_invocationId, ex); - } - _httpRequest.Dispose(); - if (_httpResponse != null) - { - _httpResponse.Dispose(); - } - throw ex; - } - // Create Result - var _result = new AzureOperationResponse(); - _result.Request = _httpRequest; - _result.Response = _httpResponse; - if (_httpResponse.Headers.Contains("x-ms-request-id")) - { - _result.RequestId = _httpResponse.Headers.GetValues("x-ms-request-id").FirstOrDefault(); - } - // Deserialize Response - if ((int)_statusCode == 200) - { - _responseContent = await _httpResponse.Content.ReadAsStringAsync().ConfigureAwait(false); - try - { - _result.Body = Rest.Serialization.SafeJsonConvert.DeserializeObject(_responseContent, Client.DeserializationSettings); - } - catch (JsonException ex) - { - _httpRequest.Dispose(); - if (_httpResponse != null) - { - _httpResponse.Dispose(); - } - throw new SerializationException("Unable to deserialize the response.", _responseContent, ex); - } - } - if (_shouldTrace) - { - ServiceClientTracing.Exit(_invocationId, _result); - } - return _result; - } - - /// - /// Create a job. - /// - /// - /// Id of the job. - /// - /// - /// The complete metadata of the job to submit. - /// - /// - /// Headers that will be added to request. - /// - /// - /// The cancellation token. - /// - /// - /// Thrown when the operation returned an invalid status code - /// - /// - /// Thrown when unable to deserialize the response - /// - /// - /// Thrown when a required parameter is null - /// - /// - /// Thrown when a required parameter is null - /// - /// - /// A response object containing the response body and response headers. - /// - public async Task> CreateWithHttpMessagesAsync(string jobId, JobDetails job, Dictionary> customHeaders = null, CancellationToken cancellationToken = default(CancellationToken)) - { - if (Client.SubscriptionId == null) - { - throw new ValidationException(ValidationRules.CannotBeNull, "this.Client.SubscriptionId"); - } - if (Client.ResourceGroupName == null) - { - throw new ValidationException(ValidationRules.CannotBeNull, "this.Client.ResourceGroupName"); - } - if (Client.WorkspaceName == null) - { - throw new ValidationException(ValidationRules.CannotBeNull, "this.Client.WorkspaceName"); - } - if (jobId == null) - { - throw new ValidationException(ValidationRules.CannotBeNull, "jobId"); - } - if (job == null) - { - throw new ValidationException(ValidationRules.CannotBeNull, "job"); - } - if (job != null) - { - job.Validate(); - } - // Tracing - bool _shouldTrace = ServiceClientTracing.IsEnabled; - string _invocationId = null; - if (_shouldTrace) - { - _invocationId = ServiceClientTracing.NextInvocationId.ToString(); - Dictionary tracingParameters = new Dictionary(); - tracingParameters.Add("jobId", jobId); - tracingParameters.Add("job", job); - tracingParameters.Add("cancellationToken", cancellationToken); - ServiceClientTracing.Enter(_invocationId, this, "Create", tracingParameters); - } - // Construct URL - var _baseUrl = Client.BaseUri.AbsoluteUri; - var _url = new System.Uri(new System.Uri(_baseUrl + (_baseUrl.EndsWith("/") ? "" : "/")), "v1.0/subscriptions/{subscriptionId}/resourceGroups/{resourceGroupName}/providers/Microsoft.Quantum/workspaces/{workspaceName}/jobs/{jobId}").ToString(); - _url = _url.Replace("{subscriptionId}", System.Uri.EscapeDataString(Client.SubscriptionId)); - _url = _url.Replace("{resourceGroupName}", System.Uri.EscapeDataString(Client.ResourceGroupName)); - _url = _url.Replace("{workspaceName}", System.Uri.EscapeDataString(Client.WorkspaceName)); - _url = _url.Replace("{jobId}", System.Uri.EscapeDataString(jobId)); - List _queryParameters = new List(); - if (_queryParameters.Count > 0) - { - _url += (_url.Contains("?") ? "&" : "?") + string.Join("&", _queryParameters); - } - // Create HTTP transport objects - var _httpRequest = new HttpRequestMessage(); - HttpResponseMessage _httpResponse = null; - _httpRequest.Method = new HttpMethod("PUT"); - _httpRequest.RequestUri = new System.Uri(_url); - // Set Headers - if (Client.GenerateClientRequestId != null && Client.GenerateClientRequestId.Value) - { - _httpRequest.Headers.TryAddWithoutValidation("x-ms-client-request-id", System.Guid.NewGuid().ToString()); - } - if (Client.AcceptLanguage != null) - { - if (_httpRequest.Headers.Contains("accept-language")) - { - _httpRequest.Headers.Remove("accept-language"); - } - _httpRequest.Headers.TryAddWithoutValidation("accept-language", Client.AcceptLanguage); - } - - - if (customHeaders != null) - { - foreach(var _header in customHeaders) - { - if (_httpRequest.Headers.Contains(_header.Key)) - { - _httpRequest.Headers.Remove(_header.Key); - } - _httpRequest.Headers.TryAddWithoutValidation(_header.Key, _header.Value); - } - } - - // Serialize Request - string _requestContent = null; - if(job != null) - { - _requestContent = Rest.Serialization.SafeJsonConvert.SerializeObject(job, Client.SerializationSettings); - _httpRequest.Content = new StringContent(_requestContent, System.Text.Encoding.UTF8); - _httpRequest.Content.Headers.ContentType =System.Net.Http.Headers.MediaTypeHeaderValue.Parse("application/json; charset=utf-8"); - } - // Set Credentials - if (Client.Credentials != null) - { - cancellationToken.ThrowIfCancellationRequested(); - await Client.Credentials.ProcessHttpRequestAsync(_httpRequest, cancellationToken).ConfigureAwait(false); - } - // Send Request - if (_shouldTrace) - { - ServiceClientTracing.SendRequest(_invocationId, _httpRequest); - } - cancellationToken.ThrowIfCancellationRequested(); - _httpResponse = await Client.HttpClient.SendAsync(_httpRequest, cancellationToken).ConfigureAwait(false); - if (_shouldTrace) - { - ServiceClientTracing.ReceiveResponse(_invocationId, _httpResponse); - } - HttpStatusCode _statusCode = _httpResponse.StatusCode; - cancellationToken.ThrowIfCancellationRequested(); - string _responseContent = null; - if ((int)_statusCode != 200 && (int)_statusCode != 201) - { - var ex = new RestErrorException(string.Format("Operation returned an invalid status code '{0}'", _statusCode)); - try - { - _responseContent = await _httpResponse.Content.ReadAsStringAsync().ConfigureAwait(false); - RestError _errorBody = Rest.Serialization.SafeJsonConvert.DeserializeObject(_responseContent, Client.DeserializationSettings); - if (_errorBody != null) - { - ex.Body = _errorBody; - } - } - catch (JsonException) - { - // Ignore the exception - } - ex.Request = new HttpRequestMessageWrapper(_httpRequest, _requestContent); - ex.Response = new HttpResponseMessageWrapper(_httpResponse, _responseContent); - if (_shouldTrace) - { - ServiceClientTracing.Error(_invocationId, ex); - } - _httpRequest.Dispose(); - if (_httpResponse != null) - { - _httpResponse.Dispose(); - } - throw ex; - } - // Create Result - var _result = new AzureOperationResponse(); - _result.Request = _httpRequest; - _result.Response = _httpResponse; - if (_httpResponse.Headers.Contains("x-ms-request-id")) - { - _result.RequestId = _httpResponse.Headers.GetValues("x-ms-request-id").FirstOrDefault(); - } - // Deserialize Response - if ((int)_statusCode == 200) - { - _responseContent = await _httpResponse.Content.ReadAsStringAsync().ConfigureAwait(false); - try - { - _result.Body = Rest.Serialization.SafeJsonConvert.DeserializeObject(_responseContent, Client.DeserializationSettings); - } - catch (JsonException ex) - { - _httpRequest.Dispose(); - if (_httpResponse != null) - { - _httpResponse.Dispose(); - } - throw new SerializationException("Unable to deserialize the response.", _responseContent, ex); - } - } - // Deserialize Response - if ((int)_statusCode == 201) - { - _responseContent = await _httpResponse.Content.ReadAsStringAsync().ConfigureAwait(false); - try - { - _result.Body = Rest.Serialization.SafeJsonConvert.DeserializeObject(_responseContent, Client.DeserializationSettings); - } - catch (JsonException ex) - { - _httpRequest.Dispose(); - if (_httpResponse != null) - { - _httpResponse.Dispose(); - } - throw new SerializationException("Unable to deserialize the response.", _responseContent, ex); - } - } - if (_shouldTrace) - { - ServiceClientTracing.Exit(_invocationId, _result); - } - return _result; - } - - /// - /// Cancel a job. - /// - /// - /// Id of the job. - /// - /// - /// Headers that will be added to request. - /// - /// - /// The cancellation token. - /// - /// - /// Thrown when the operation returned an invalid status code - /// - /// - /// Thrown when a required parameter is null - /// - /// - /// Thrown when a required parameter is null - /// - /// - /// A response object containing the response body and response headers. - /// - public async Task CancelWithHttpMessagesAsync(string jobId, Dictionary> customHeaders = null, CancellationToken cancellationToken = default(CancellationToken)) - { - if (Client.SubscriptionId == null) - { - throw new ValidationException(ValidationRules.CannotBeNull, "this.Client.SubscriptionId"); - } - if (Client.ResourceGroupName == null) - { - throw new ValidationException(ValidationRules.CannotBeNull, "this.Client.ResourceGroupName"); - } - if (Client.WorkspaceName == null) - { - throw new ValidationException(ValidationRules.CannotBeNull, "this.Client.WorkspaceName"); - } - if (jobId == null) - { - throw new ValidationException(ValidationRules.CannotBeNull, "jobId"); - } - // Tracing - bool _shouldTrace = ServiceClientTracing.IsEnabled; - string _invocationId = null; - if (_shouldTrace) - { - _invocationId = ServiceClientTracing.NextInvocationId.ToString(); - Dictionary tracingParameters = new Dictionary(); - tracingParameters.Add("jobId", jobId); - tracingParameters.Add("cancellationToken", cancellationToken); - ServiceClientTracing.Enter(_invocationId, this, "Cancel", tracingParameters); - } - // Construct URL - var _baseUrl = Client.BaseUri.AbsoluteUri; - var _url = new System.Uri(new System.Uri(_baseUrl + (_baseUrl.EndsWith("/") ? "" : "/")), "v1.0/subscriptions/{subscriptionId}/resourceGroups/{resourceGroupName}/providers/Microsoft.Quantum/workspaces/{workspaceName}/jobs/{jobId}").ToString(); - _url = _url.Replace("{subscriptionId}", System.Uri.EscapeDataString(Client.SubscriptionId)); - _url = _url.Replace("{resourceGroupName}", System.Uri.EscapeDataString(Client.ResourceGroupName)); - _url = _url.Replace("{workspaceName}", System.Uri.EscapeDataString(Client.WorkspaceName)); - _url = _url.Replace("{jobId}", System.Uri.EscapeDataString(jobId)); - List _queryParameters = new List(); - if (_queryParameters.Count > 0) - { - _url += (_url.Contains("?") ? "&" : "?") + string.Join("&", _queryParameters); - } - // Create HTTP transport objects - var _httpRequest = new HttpRequestMessage(); - HttpResponseMessage _httpResponse = null; - _httpRequest.Method = new HttpMethod("DELETE"); - _httpRequest.RequestUri = new System.Uri(_url); - // Set Headers - if (Client.GenerateClientRequestId != null && Client.GenerateClientRequestId.Value) - { - _httpRequest.Headers.TryAddWithoutValidation("x-ms-client-request-id", System.Guid.NewGuid().ToString()); - } - if (Client.AcceptLanguage != null) - { - if (_httpRequest.Headers.Contains("accept-language")) - { - _httpRequest.Headers.Remove("accept-language"); - } - _httpRequest.Headers.TryAddWithoutValidation("accept-language", Client.AcceptLanguage); - } - - - if (customHeaders != null) - { - foreach(var _header in customHeaders) - { - if (_httpRequest.Headers.Contains(_header.Key)) - { - _httpRequest.Headers.Remove(_header.Key); - } - _httpRequest.Headers.TryAddWithoutValidation(_header.Key, _header.Value); - } - } - - // Serialize Request - string _requestContent = null; - // Set Credentials - if (Client.Credentials != null) - { - cancellationToken.ThrowIfCancellationRequested(); - await Client.Credentials.ProcessHttpRequestAsync(_httpRequest, cancellationToken).ConfigureAwait(false); - } - // Send Request - if (_shouldTrace) - { - ServiceClientTracing.SendRequest(_invocationId, _httpRequest); - } - cancellationToken.ThrowIfCancellationRequested(); - _httpResponse = await Client.HttpClient.SendAsync(_httpRequest, cancellationToken).ConfigureAwait(false); - if (_shouldTrace) - { - ServiceClientTracing.ReceiveResponse(_invocationId, _httpResponse); - } - HttpStatusCode _statusCode = _httpResponse.StatusCode; - cancellationToken.ThrowIfCancellationRequested(); - string _responseContent = null; - if ((int)_statusCode != 204) - { - var ex = new RestErrorException(string.Format("Operation returned an invalid status code '{0}'", _statusCode)); - try - { - _responseContent = await _httpResponse.Content.ReadAsStringAsync().ConfigureAwait(false); - RestError _errorBody = Rest.Serialization.SafeJsonConvert.DeserializeObject(_responseContent, Client.DeserializationSettings); - if (_errorBody != null) - { - ex.Body = _errorBody; - } - } - catch (JsonException) - { - // Ignore the exception - } - ex.Request = new HttpRequestMessageWrapper(_httpRequest, _requestContent); - ex.Response = new HttpResponseMessageWrapper(_httpResponse, _responseContent); - if (_shouldTrace) - { - ServiceClientTracing.Error(_invocationId, ex); - } - _httpRequest.Dispose(); - if (_httpResponse != null) - { - _httpResponse.Dispose(); - } - throw ex; - } - // Create Result - var _result = new AzureOperationResponse(); - _result.Request = _httpRequest; - _result.Response = _httpResponse; - if (_httpResponse.Headers.Contains("x-ms-request-id")) - { - _result.RequestId = _httpResponse.Headers.GetValues("x-ms-request-id").FirstOrDefault(); - } - if (_shouldTrace) - { - ServiceClientTracing.Exit(_invocationId, _result); - } - return _result; - } - - /// - /// List jobs. - /// - /// - /// The NextLink from the previous successful call to List operation. - /// - /// - /// Headers that will be added to request. - /// - /// - /// The cancellation token. - /// - /// - /// Thrown when the operation returned an invalid status code - /// - /// - /// Thrown when unable to deserialize the response - /// - /// - /// Thrown when a required parameter is null - /// - /// - /// Thrown when a required parameter is null - /// - /// - /// A response object containing the response body and response headers. - /// - public async Task>> ListNextWithHttpMessagesAsync(string nextPageLink, Dictionary> customHeaders = null, CancellationToken cancellationToken = default(CancellationToken)) - { - if (nextPageLink == null) - { - throw new ValidationException(ValidationRules.CannotBeNull, "nextPageLink"); - } - // Tracing - bool _shouldTrace = ServiceClientTracing.IsEnabled; - string _invocationId = null; - if (_shouldTrace) - { - _invocationId = ServiceClientTracing.NextInvocationId.ToString(); - Dictionary tracingParameters = new Dictionary(); - tracingParameters.Add("nextPageLink", nextPageLink); - tracingParameters.Add("cancellationToken", cancellationToken); - ServiceClientTracing.Enter(_invocationId, this, "ListNext", tracingParameters); - } - // Construct URL - string _url = "{nextLink}"; - _url = _url.Replace("{nextLink}", nextPageLink); - List _queryParameters = new List(); - if (_queryParameters.Count > 0) - { - _url += (_url.Contains("?") ? "&" : "?") + string.Join("&", _queryParameters); - } - // Create HTTP transport objects - var _httpRequest = new HttpRequestMessage(); - HttpResponseMessage _httpResponse = null; - _httpRequest.Method = new HttpMethod("GET"); - _httpRequest.RequestUri = new System.Uri(_url); - // Set Headers - if (Client.GenerateClientRequestId != null && Client.GenerateClientRequestId.Value) - { - _httpRequest.Headers.TryAddWithoutValidation("x-ms-client-request-id", System.Guid.NewGuid().ToString()); - } - if (Client.AcceptLanguage != null) - { - if (_httpRequest.Headers.Contains("accept-language")) - { - _httpRequest.Headers.Remove("accept-language"); - } - _httpRequest.Headers.TryAddWithoutValidation("accept-language", Client.AcceptLanguage); - } - - - if (customHeaders != null) - { - foreach(var _header in customHeaders) - { - if (_httpRequest.Headers.Contains(_header.Key)) - { - _httpRequest.Headers.Remove(_header.Key); - } - _httpRequest.Headers.TryAddWithoutValidation(_header.Key, _header.Value); - } - } - - // Serialize Request - string _requestContent = null; - // Set Credentials - if (Client.Credentials != null) - { - cancellationToken.ThrowIfCancellationRequested(); - await Client.Credentials.ProcessHttpRequestAsync(_httpRequest, cancellationToken).ConfigureAwait(false); - } - // Send Request - if (_shouldTrace) - { - ServiceClientTracing.SendRequest(_invocationId, _httpRequest); - } - cancellationToken.ThrowIfCancellationRequested(); - _httpResponse = await Client.HttpClient.SendAsync(_httpRequest, cancellationToken).ConfigureAwait(false); - if (_shouldTrace) - { - ServiceClientTracing.ReceiveResponse(_invocationId, _httpResponse); - } - HttpStatusCode _statusCode = _httpResponse.StatusCode; - cancellationToken.ThrowIfCancellationRequested(); - string _responseContent = null; - if ((int)_statusCode != 200) - { - var ex = new CloudException(string.Format("Operation returned an invalid status code '{0}'", _statusCode)); - try - { - _responseContent = await _httpResponse.Content.ReadAsStringAsync().ConfigureAwait(false); - CloudError _errorBody = Rest.Serialization.SafeJsonConvert.DeserializeObject(_responseContent, Client.DeserializationSettings); - if (_errorBody != null) - { - ex = new CloudException(_errorBody.Message); - ex.Body = _errorBody; - } - } - catch (JsonException) - { - // Ignore the exception - } - ex.Request = new HttpRequestMessageWrapper(_httpRequest, _requestContent); - ex.Response = new HttpResponseMessageWrapper(_httpResponse, _responseContent); - if (_httpResponse.Headers.Contains("x-ms-request-id")) - { - ex.RequestId = _httpResponse.Headers.GetValues("x-ms-request-id").FirstOrDefault(); - } - if (_shouldTrace) - { - ServiceClientTracing.Error(_invocationId, ex); - } - _httpRequest.Dispose(); - if (_httpResponse != null) - { - _httpResponse.Dispose(); - } - throw ex; - } - // Create Result - var _result = new AzureOperationResponse>(); - _result.Request = _httpRequest; - _result.Response = _httpResponse; - if (_httpResponse.Headers.Contains("x-ms-request-id")) - { - _result.RequestId = _httpResponse.Headers.GetValues("x-ms-request-id").FirstOrDefault(); - } - // Deserialize Response - if ((int)_statusCode == 200) - { - _responseContent = await _httpResponse.Content.ReadAsStringAsync().ConfigureAwait(false); - try - { - _result.Body = Rest.Serialization.SafeJsonConvert.DeserializeObject>(_responseContent, Client.DeserializationSettings); - } - catch (JsonException ex) - { - _httpRequest.Dispose(); - if (_httpResponse != null) - { - _httpResponse.Dispose(); - } - throw new SerializationException("Unable to deserialize the response.", _responseContent, ex); - } - } - if (_shouldTrace) - { - ServiceClientTracing.Exit(_invocationId, _result); - } - return _result; - } - - } -} diff --git a/src/Azure/Azure.Quantum.Client/generated/JobsOperationsExtensions.cs b/src/Azure/Azure.Quantum.Client/generated/JobsOperationsExtensions.cs deleted file mode 100644 index 24fa241b994..00000000000 --- a/src/Azure/Azure.Quantum.Client/generated/JobsOperationsExtensions.cs +++ /dev/null @@ -1,192 +0,0 @@ -// -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT License. See License.txt in the project root for -// license information. -// -// Code generated by Microsoft (R) AutoRest Code Generator. -// Changes may cause incorrect behavior and will be lost if the code is -// regenerated. -// - -namespace Microsoft.Azure.Quantum.Client -{ - using Microsoft.Rest; - using Microsoft.Rest.Azure; - using Models; - using System.Threading; - using System.Threading.Tasks; - - /// - /// Extension methods for JobsOperations. - /// - public static partial class JobsOperationsExtensions - { - /// - /// List jobs. - /// - /// - /// The operations group for this extension method. - /// - public static IPage List(this IJobsOperations operations) - { - return operations.ListAsync().GetAwaiter().GetResult(); - } - - /// - /// List jobs. - /// - /// - /// The operations group for this extension method. - /// - /// - /// The cancellation token. - /// - public static async Task> ListAsync(this IJobsOperations operations, CancellationToken cancellationToken = default(CancellationToken)) - { - using (var _result = await operations.ListWithHttpMessagesAsync(null, cancellationToken).ConfigureAwait(false)) - { - return _result.Body; - } - } - - /// - /// Get job by id - /// - /// - /// The operations group for this extension method. - /// - /// - /// Id of the job. - /// - public static JobDetails Get(this IJobsOperations operations, string jobId) - { - return operations.GetAsync(jobId).GetAwaiter().GetResult(); - } - - /// - /// Get job by id - /// - /// - /// The operations group for this extension method. - /// - /// - /// Id of the job. - /// - /// - /// The cancellation token. - /// - public static async Task GetAsync(this IJobsOperations operations, string jobId, CancellationToken cancellationToken = default(CancellationToken)) - { - using (var _result = await operations.GetWithHttpMessagesAsync(jobId, null, cancellationToken).ConfigureAwait(false)) - { - return _result.Body; - } - } - - /// - /// Create a job. - /// - /// - /// The operations group for this extension method. - /// - /// - /// Id of the job. - /// - /// - /// The complete metadata of the job to submit. - /// - public static JobDetails Create(this IJobsOperations operations, string jobId, JobDetails job) - { - return operations.CreateAsync(jobId, job).GetAwaiter().GetResult(); - } - - /// - /// Create a job. - /// - /// - /// The operations group for this extension method. - /// - /// - /// Id of the job. - /// - /// - /// The complete metadata of the job to submit. - /// - /// - /// The cancellation token. - /// - public static async Task CreateAsync(this IJobsOperations operations, string jobId, JobDetails job, CancellationToken cancellationToken = default(CancellationToken)) - { - using (var _result = await operations.CreateWithHttpMessagesAsync(jobId, job, null, cancellationToken).ConfigureAwait(false)) - { - return _result.Body; - } - } - - /// - /// Cancel a job. - /// - /// - /// The operations group for this extension method. - /// - /// - /// Id of the job. - /// - public static void Cancel(this IJobsOperations operations, string jobId) - { - operations.CancelAsync(jobId).GetAwaiter().GetResult(); - } - - /// - /// Cancel a job. - /// - /// - /// The operations group for this extension method. - /// - /// - /// Id of the job. - /// - /// - /// The cancellation token. - /// - public static async Task CancelAsync(this IJobsOperations operations, string jobId, CancellationToken cancellationToken = default(CancellationToken)) - { - (await operations.CancelWithHttpMessagesAsync(jobId, null, cancellationToken).ConfigureAwait(false)).Dispose(); - } - - /// - /// List jobs. - /// - /// - /// The operations group for this extension method. - /// - /// - /// The NextLink from the previous successful call to List operation. - /// - public static IPage ListNext(this IJobsOperations operations, string nextPageLink) - { - return operations.ListNextAsync(nextPageLink).GetAwaiter().GetResult(); - } - - /// - /// List jobs. - /// - /// - /// The operations group for this extension method. - /// - /// - /// The NextLink from the previous successful call to List operation. - /// - /// - /// The cancellation token. - /// - public static async Task> ListNextAsync(this IJobsOperations operations, string nextPageLink, CancellationToken cancellationToken = default(CancellationToken)) - { - using (var _result = await operations.ListNextWithHttpMessagesAsync(nextPageLink, null, cancellationToken).ConfigureAwait(false)) - { - return _result.Body; - } - } - - } -} diff --git a/src/Azure/Azure.Quantum.Client/generated/Models/BlobDetails.cs b/src/Azure/Azure.Quantum.Client/generated/Models/BlobDetails.cs deleted file mode 100644 index 6698af8a001..00000000000 --- a/src/Azure/Azure.Quantum.Client/generated/Models/BlobDetails.cs +++ /dev/null @@ -1,73 +0,0 @@ -// -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT License. See License.txt in the project root for -// license information. -// -// Code generated by Microsoft (R) AutoRest Code Generator. -// Changes may cause incorrect behavior and will be lost if the code is -// regenerated. -// - -namespace Microsoft.Azure.Quantum.Client.Models -{ - using Microsoft.Rest; - using Newtonsoft.Json; - using System.Linq; - - /// - /// Blob details. - /// - public partial class BlobDetails - { - /// - /// Initializes a new instance of the BlobDetails class. - /// - public BlobDetails() - { - CustomInit(); - } - - /// - /// Initializes a new instance of the BlobDetails class. - /// - /// The container name. - /// The blob name. - public BlobDetails(string containerName, string blobName = default(string)) - { - ContainerName = containerName; - BlobName = blobName; - CustomInit(); - } - - /// - /// An initialization method that performs custom operations like setting defaults - /// - partial void CustomInit(); - - /// - /// Gets or sets the container name. - /// - [JsonProperty(PropertyName = "containerName")] - public string ContainerName { get; set; } - - /// - /// Gets or sets the blob name. - /// - [JsonProperty(PropertyName = "blobName")] - public string BlobName { get; set; } - - /// - /// Validate the object. - /// - /// - /// Thrown if validation fails - /// - public virtual void Validate() - { - if (ContainerName == null) - { - throw new ValidationException(ValidationRules.CannotBeNull, "ContainerName"); - } - } - } -} diff --git a/src/Azure/Azure.Quantum.Client/generated/Models/DimensionScope.cs b/src/Azure/Azure.Quantum.Client/generated/Models/DimensionScope.cs deleted file mode 100644 index 2f00b039adc..00000000000 --- a/src/Azure/Azure.Quantum.Client/generated/Models/DimensionScope.cs +++ /dev/null @@ -1,22 +0,0 @@ -// -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT License. See License.txt in the project root for -// license information. -// -// Code generated by Microsoft (R) AutoRest Code Generator. -// Changes may cause incorrect behavior and will be lost if the code is -// regenerated. -// - -namespace Microsoft.Azure.Quantum.Client.Models -{ - - /// - /// Defines values for DimensionScope. - /// - public static class DimensionScope - { - public const string Workspace = "Workspace"; - public const string Subscription = "Subscription"; - } -} diff --git a/src/Azure/Azure.Quantum.Client/generated/Models/ErrorData.cs b/src/Azure/Azure.Quantum.Client/generated/Models/ErrorData.cs deleted file mode 100644 index c817ad9fa72..00000000000 --- a/src/Azure/Azure.Quantum.Client/generated/Models/ErrorData.cs +++ /dev/null @@ -1,63 +0,0 @@ -// -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT License. See License.txt in the project root for -// license information. -// -// Code generated by Microsoft (R) AutoRest Code Generator. -// Changes may cause incorrect behavior and will be lost if the code is -// regenerated. -// - -namespace Microsoft.Azure.Quantum.Client.Models -{ - using Newtonsoft.Json; - using System.Linq; - - /// - /// An error response from Azure. - /// - public partial class ErrorData - { - /// - /// Initializes a new instance of the ErrorData class. - /// - public ErrorData() - { - CustomInit(); - } - - /// - /// Initializes a new instance of the ErrorData class. - /// - /// An identifier for the error. Codes are invariant - /// and are intended to be consumed programmatically. - /// A message describing the error, intended to - /// be suitable for displaying in a user interface. - public ErrorData(string code = default(string), string message = default(string)) - { - Code = code; - Message = message; - CustomInit(); - } - - /// - /// An initialization method that performs custom operations like setting defaults - /// - partial void CustomInit(); - - /// - /// Gets or sets an identifier for the error. Codes are invariant and - /// are intended to be consumed programmatically. - /// - [JsonProperty(PropertyName = "code")] - public string Code { get; set; } - - /// - /// Gets or sets a message describing the error, intended to be - /// suitable for displaying in a user interface. - /// - [JsonProperty(PropertyName = "message")] - public string Message { get; set; } - - } -} diff --git a/src/Azure/Azure.Quantum.Client/generated/Models/JobDetails.cs b/src/Azure/Azure.Quantum.Client/generated/Models/JobDetails.cs deleted file mode 100644 index e4a0a5a2711..00000000000 --- a/src/Azure/Azure.Quantum.Client/generated/Models/JobDetails.cs +++ /dev/null @@ -1,233 +0,0 @@ -// -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT License. See License.txt in the project root for -// license information. -// -// Code generated by Microsoft (R) AutoRest Code Generator. -// Changes may cause incorrect behavior and will be lost if the code is -// regenerated. -// - -namespace Microsoft.Azure.Quantum.Client.Models -{ - using Microsoft.Rest; - using Newtonsoft.Json; - using System.Collections; - using System.Collections.Generic; - using System.Linq; - - /// - /// Job details. - /// - public partial class JobDetails - { - /// - /// Initializes a new instance of the JobDetails class. - /// - public JobDetails() - { - CustomInit(); - } - - /// - /// Initializes a new instance of the JobDetails class. - /// - /// The blob container SAS uri, the - /// container is used to host job data. - /// The format of the input data. - /// The unique identifier for the - /// provider. - /// The target identifier to run the job. - /// The job id. - /// The job name. Is not required for the name to be - /// unique and it's only used for display purposes. - /// The input blob SAS uri, if specified, it - /// will override the default input blob in the container. - /// The input parameters for the job. JSON - /// object used by the target solver. It is expected that the size of - /// this object is small and only used to specify parameters for the - /// execution target, not the input data. - /// The job metadata. Metadata provides client - /// the ability to store client-specific information - /// The output blob SAS uri. When a job - /// finishes successfully, results will be uploaded to this - /// blob. - /// The format of the output - /// data. - /// The job status. Possible values include: - /// 'Waiting', 'Executing', 'Succeeded', 'Failed', 'Cancelled' - /// The creation time of the job. - /// The time when the job began - /// execution. - /// The time when the job finished - /// execution. - /// The time when a job was successfully - /// cancelled. - /// The error data for the job. This is - /// expected only when Status 'Failed'. - public JobDetails(string containerUri, string inputDataFormat, string providerId, string target, string id = default(string), string name = default(string), string inputDataUri = default(string), object inputParams = default(object), IDictionary metadata = default(IDictionary), string outputDataUri = default(string), string outputDataFormat = default(string), string status = default(string), System.DateTime? creationTime = default(System.DateTime?), System.DateTime? beginExecutionTime = default(System.DateTime?), System.DateTime? endExecutionTime = default(System.DateTime?), System.DateTime? cancellationTime = default(System.DateTime?), ErrorData errorData = default(ErrorData)) - { - Id = id; - Name = name; - ContainerUri = containerUri; - InputDataUri = inputDataUri; - InputDataFormat = inputDataFormat; - InputParams = inputParams; - ProviderId = providerId; - Target = target; - Metadata = metadata; - OutputDataUri = outputDataUri; - OutputDataFormat = outputDataFormat; - Status = status; - CreationTime = creationTime; - BeginExecutionTime = beginExecutionTime; - EndExecutionTime = endExecutionTime; - CancellationTime = cancellationTime; - ErrorData = errorData; - CustomInit(); - } - - /// - /// An initialization method that performs custom operations like setting defaults - /// - partial void CustomInit(); - - /// - /// Gets or sets the job id. - /// - [JsonProperty(PropertyName = "id")] - public string Id { get; set; } - - /// - /// Gets or sets the job name. Is not required for the name to be - /// unique and it's only used for display purposes. - /// - [JsonProperty(PropertyName = "name")] - public string Name { get; set; } - - /// - /// Gets or sets the blob container SAS uri, the container is used to - /// host job data. - /// - [JsonProperty(PropertyName = "containerUri")] - public string ContainerUri { get; set; } - - /// - /// Gets or sets the input blob SAS uri, if specified, it will override - /// the default input blob in the container. - /// - [JsonProperty(PropertyName = "inputDataUri")] - public string InputDataUri { get; set; } - - /// - /// Gets or sets the format of the input data. - /// - [JsonProperty(PropertyName = "inputDataFormat")] - public string InputDataFormat { get; set; } - - /// - /// Gets or sets the input parameters for the job. JSON object used by - /// the target solver. It is expected that the size of this object is - /// small and only used to specify parameters for the execution target, - /// not the input data. - /// - [JsonProperty(PropertyName = "inputParams")] - public object InputParams { get; set; } - - /// - /// Gets or sets the unique identifier for the provider. - /// - [JsonProperty(PropertyName = "providerId")] - public string ProviderId { get; set; } - - /// - /// Gets or sets the target identifier to run the job. - /// - [JsonProperty(PropertyName = "target")] - public string Target { get; set; } - - /// - /// Gets or sets the job metadata. Metadata provides client the ability - /// to store client-specific information - /// - [JsonProperty(PropertyName = "metadata")] - public IDictionary Metadata { get; set; } - - /// - /// Gets or sets the output blob SAS uri. When a job finishes - /// successfully, results will be uploaded to this blob. - /// - [JsonProperty(PropertyName = "outputDataUri")] - public string OutputDataUri { get; set; } - - /// - /// Gets or sets the format of the output data. - /// - [JsonProperty(PropertyName = "outputDataFormat")] - public string OutputDataFormat { get; set; } - - /// - /// Gets the job status. Possible values include: 'Waiting', - /// 'Executing', 'Succeeded', 'Failed', 'Cancelled' - /// - [JsonProperty(PropertyName = "status")] - public string Status { get; private set; } - - /// - /// Gets the creation time of the job. - /// - [JsonProperty(PropertyName = "creationTime")] - public System.DateTime? CreationTime { get; private set; } - - /// - /// Gets the time when the job began execution. - /// - [JsonProperty(PropertyName = "beginExecutionTime")] - public System.DateTime? BeginExecutionTime { get; private set; } - - /// - /// Gets the time when the job finished execution. - /// - [JsonProperty(PropertyName = "endExecutionTime")] - public System.DateTime? EndExecutionTime { get; private set; } - - /// - /// Gets the time when a job was successfully cancelled. - /// - [JsonProperty(PropertyName = "cancellationTime")] - public System.DateTime? CancellationTime { get; private set; } - - /// - /// Gets the error data for the job. This is expected only when Status - /// 'Failed'. - /// - [JsonProperty(PropertyName = "errorData")] - public ErrorData ErrorData { get; private set; } - - /// - /// Validate the object. - /// - /// - /// Thrown if validation fails - /// - public virtual void Validate() - { - if (ContainerUri == null) - { - throw new ValidationException(ValidationRules.CannotBeNull, "ContainerUri"); - } - if (InputDataFormat == null) - { - throw new ValidationException(ValidationRules.CannotBeNull, "InputDataFormat"); - } - if (ProviderId == null) - { - throw new ValidationException(ValidationRules.CannotBeNull, "ProviderId"); - } - if (Target == null) - { - throw new ValidationException(ValidationRules.CannotBeNull, "Target"); - } - } - } -} diff --git a/src/Azure/Azure.Quantum.Client/generated/Models/JobStatus.cs b/src/Azure/Azure.Quantum.Client/generated/Models/JobStatus.cs deleted file mode 100644 index cf79f42d3e9..00000000000 --- a/src/Azure/Azure.Quantum.Client/generated/Models/JobStatus.cs +++ /dev/null @@ -1,25 +0,0 @@ -// -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT License. See License.txt in the project root for -// license information. -// -// Code generated by Microsoft (R) AutoRest Code Generator. -// Changes may cause incorrect behavior and will be lost if the code is -// regenerated. -// - -namespace Microsoft.Azure.Quantum.Client.Models -{ - - /// - /// Defines values for JobStatus. - /// - public static class JobStatus - { - public const string Waiting = "Waiting"; - public const string Executing = "Executing"; - public const string Succeeded = "Succeeded"; - public const string Failed = "Failed"; - public const string Cancelled = "Cancelled"; - } -} diff --git a/src/Azure/Azure.Quantum.Client/generated/Models/MeterPeriod.cs b/src/Azure/Azure.Quantum.Client/generated/Models/MeterPeriod.cs deleted file mode 100644 index c8c8092b34f..00000000000 --- a/src/Azure/Azure.Quantum.Client/generated/Models/MeterPeriod.cs +++ /dev/null @@ -1,22 +0,0 @@ -// -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT License. See License.txt in the project root for -// license information. -// -// Code generated by Microsoft (R) AutoRest Code Generator. -// Changes may cause incorrect behavior and will be lost if the code is -// regenerated. -// - -namespace Microsoft.Azure.Quantum.Client.Models -{ - - /// - /// Defines values for MeterPeriod. - /// - public static class MeterPeriod - { - public const string None = "None"; - public const string Monthly = "Monthly"; - } -} diff --git a/src/Azure/Azure.Quantum.Client/generated/Models/Page.cs b/src/Azure/Azure.Quantum.Client/generated/Models/Page.cs deleted file mode 100644 index faf255dd623..00000000000 --- a/src/Azure/Azure.Quantum.Client/generated/Models/Page.cs +++ /dev/null @@ -1,53 +0,0 @@ -// -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT License. See License.txt in the project root for -// license information. -// -// Code generated by Microsoft (R) AutoRest Code Generator. -// Changes may cause incorrect behavior and will be lost if the code is -// regenerated. -// - -namespace Microsoft.Azure.Quantum.Client.Models -{ - using Microsoft.Rest; - using Microsoft.Rest.Azure; - using Newtonsoft.Json; - using System.Collections; - using System.Collections.Generic; - - /// - /// Defines a page in Azure responses. - /// - /// Type of the page content items - [JsonObject] - public class Page : IPage - { - /// - /// Gets the link to the next page. - /// - [JsonProperty("nextLink")] - public string NextPageLink { get; private set; } - - [JsonProperty("value")] - private IList Items{ get; set; } - - /// - /// Returns an enumerator that iterates through the collection. - /// - /// A an enumerator that can be used to iterate through the collection. - public IEnumerator GetEnumerator() - { - return Items == null ? System.Linq.Enumerable.Empty().GetEnumerator() : Items.GetEnumerator(); - } - - /// - /// Returns an enumerator that iterates through the collection. - /// - /// A an enumerator that can be used to iterate through the collection. - IEnumerator IEnumerable.GetEnumerator() - { - return GetEnumerator(); - } - } -} diff --git a/src/Azure/Azure.Quantum.Client/generated/Models/ProviderAvailability.cs b/src/Azure/Azure.Quantum.Client/generated/Models/ProviderAvailability.cs deleted file mode 100644 index 73232a3ff71..00000000000 --- a/src/Azure/Azure.Quantum.Client/generated/Models/ProviderAvailability.cs +++ /dev/null @@ -1,23 +0,0 @@ -// -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT License. See License.txt in the project root for -// license information. -// -// Code generated by Microsoft (R) AutoRest Code Generator. -// Changes may cause incorrect behavior and will be lost if the code is -// regenerated. -// - -namespace Microsoft.Azure.Quantum.Client.Models -{ - - /// - /// Defines values for ProviderAvailability. - /// - public static class ProviderAvailability - { - public const string Available = "Available"; - public const string Degraded = "Degraded"; - public const string Unavailable = "Unavailable"; - } -} diff --git a/src/Azure/Azure.Quantum.Client/generated/Models/ProviderStatus.cs b/src/Azure/Azure.Quantum.Client/generated/Models/ProviderStatus.cs deleted file mode 100644 index 43baf39b445..00000000000 --- a/src/Azure/Azure.Quantum.Client/generated/Models/ProviderStatus.cs +++ /dev/null @@ -1,69 +0,0 @@ -// -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT License. See License.txt in the project root for -// license information. -// -// Code generated by Microsoft (R) AutoRest Code Generator. -// Changes may cause incorrect behavior and will be lost if the code is -// regenerated. -// - -namespace Microsoft.Azure.Quantum.Client.Models -{ - using Newtonsoft.Json; - using System.Collections; - using System.Collections.Generic; - using System.Linq; - - /// - /// Providers status. - /// - public partial class ProviderStatus - { - /// - /// Initializes a new instance of the ProviderStatus class. - /// - public ProviderStatus() - { - CustomInit(); - } - - /// - /// Initializes a new instance of the ProviderStatus class. - /// - /// Provider id. - /// Provider availability. Possible - /// values include: 'Available', 'Degraded', 'Unavailable' - public ProviderStatus(string id = default(string), string currentAvailability = default(string), IList targets = default(IList)) - { - Id = id; - CurrentAvailability = currentAvailability; - Targets = targets; - CustomInit(); - } - - /// - /// An initialization method that performs custom operations like setting defaults - /// - partial void CustomInit(); - - /// - /// Gets provider id. - /// - [JsonProperty(PropertyName = "id")] - public string Id { get; private set; } - - /// - /// Gets provider availability. Possible values include: 'Available', - /// 'Degraded', 'Unavailable' - /// - [JsonProperty(PropertyName = "currentAvailability")] - public string CurrentAvailability { get; private set; } - - /// - /// - [JsonProperty(PropertyName = "targets")] - public IList Targets { get; private set; } - - } -} diff --git a/src/Azure/Azure.Quantum.Client/generated/Models/Quota.cs b/src/Azure/Azure.Quantum.Client/generated/Models/Quota.cs deleted file mode 100644 index dc0ca8d75aa..00000000000 --- a/src/Azure/Azure.Quantum.Client/generated/Models/Quota.cs +++ /dev/null @@ -1,114 +0,0 @@ -// -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT License. See License.txt in the project root for -// license information. -// -// Code generated by Microsoft (R) AutoRest Code Generator. -// Changes may cause incorrect behavior and will be lost if the code is -// regenerated. -// - -namespace Microsoft.Azure.Quantum.Client.Models -{ - using Newtonsoft.Json; - using System.Linq; - - /// - /// Quota information. - /// - public partial class Quota - { - /// - /// Initializes a new instance of the Quota class. - /// - public Quota() - { - CustomInit(); - } - - /// - /// Initializes a new instance of the Quota class. - /// - /// The name of the dimension associated with - /// the quota. - /// The scope at which the quota is applied. - /// Possible values include: 'Workspace', 'Subscription' - /// The unique identifier for the - /// provider. - /// The amount of the usage that has been - /// applied for the current period. - /// The amount of the usage that has been reserved - /// but not applied for the current period. - /// The maximum amount of usage allowed for the - /// current period. - /// The time period in which the quota's - /// underlying meter is accumulated. Based on calendar year. 'None' is - /// used for concurrent quotas. Possible values include: 'None', - /// 'Monthly' - public Quota(string dimension = default(string), string scope = default(string), string providerId = default(string), double? utilization = default(double?), double? holds = default(double?), double? limit = default(double?), string period = default(string)) - { - Dimension = dimension; - Scope = scope; - ProviderId = providerId; - Utilization = utilization; - Holds = holds; - Limit = limit; - Period = period; - CustomInit(); - } - - /// - /// An initialization method that performs custom operations like setting defaults - /// - partial void CustomInit(); - - /// - /// Gets or sets the name of the dimension associated with the quota. - /// - [JsonProperty(PropertyName = "dimension")] - public string Dimension { get; set; } - - /// - /// Gets or sets the scope at which the quota is applied. Possible - /// values include: 'Workspace', 'Subscription' - /// - [JsonProperty(PropertyName = "scope")] - public string Scope { get; set; } - - /// - /// Gets or sets the unique identifier for the provider. - /// - [JsonProperty(PropertyName = "providerId")] - public string ProviderId { get; set; } - - /// - /// Gets or sets the amount of the usage that has been applied for the - /// current period. - /// - [JsonProperty(PropertyName = "utilization")] - public double? Utilization { get; set; } - - /// - /// Gets or sets the amount of the usage that has been reserved but not - /// applied for the current period. - /// - [JsonProperty(PropertyName = "holds")] - public double? Holds { get; set; } - - /// - /// Gets or sets the maximum amount of usage allowed for the current - /// period. - /// - [JsonProperty(PropertyName = "limit")] - public double? Limit { get; set; } - - /// - /// Gets or sets the time period in which the quota's underlying meter - /// is accumulated. Based on calendar year. 'None' is used for - /// concurrent quotas. Possible values include: 'None', 'Monthly' - /// - [JsonProperty(PropertyName = "period")] - public string Period { get; set; } - - } -} diff --git a/src/Azure/Azure.Quantum.Client/generated/Models/RestError.cs b/src/Azure/Azure.Quantum.Client/generated/Models/RestError.cs deleted file mode 100644 index 3545335a6ba..00000000000 --- a/src/Azure/Azure.Quantum.Client/generated/Models/RestError.cs +++ /dev/null @@ -1,49 +0,0 @@ -// -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT License. See License.txt in the project root for -// license information. -// -// Code generated by Microsoft (R) AutoRest Code Generator. -// Changes may cause incorrect behavior and will be lost if the code is -// regenerated. -// - -namespace Microsoft.Azure.Quantum.Client.Models -{ - using Newtonsoft.Json; - using System.Linq; - - /// - /// Error information returned by the API - /// - public partial class RestError - { - /// - /// Initializes a new instance of the RestError class. - /// - public RestError() - { - CustomInit(); - } - - /// - /// Initializes a new instance of the RestError class. - /// - public RestError(ErrorData error = default(ErrorData)) - { - Error = error; - CustomInit(); - } - - /// - /// An initialization method that performs custom operations like setting defaults - /// - partial void CustomInit(); - - /// - /// - [JsonProperty(PropertyName = "error")] - public ErrorData Error { get; set; } - - } -} diff --git a/src/Azure/Azure.Quantum.Client/generated/Models/RestErrorException.cs b/src/Azure/Azure.Quantum.Client/generated/Models/RestErrorException.cs deleted file mode 100644 index cb5804185a0..00000000000 --- a/src/Azure/Azure.Quantum.Client/generated/Models/RestErrorException.cs +++ /dev/null @@ -1,61 +0,0 @@ -// -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT License. See License.txt in the project root for -// license information. -// -// Code generated by Microsoft (R) AutoRest Code Generator. -// Changes may cause incorrect behavior and will be lost if the code is -// regenerated. -// - -namespace Microsoft.Azure.Quantum.Client.Models -{ - using Microsoft.Rest; - - /// - /// Exception thrown for an invalid response with RestError information. - /// - public partial class RestErrorException : RestException - { - /// - /// Gets information about the associated HTTP request. - /// - public HttpRequestMessageWrapper Request { get; set; } - - /// - /// Gets information about the associated HTTP response. - /// - public HttpResponseMessageWrapper Response { get; set; } - - /// - /// Gets or sets the body object. - /// - public RestError Body { get; set; } - - /// - /// Initializes a new instance of the RestErrorException class. - /// - public RestErrorException() - { - } - - /// - /// Initializes a new instance of the RestErrorException class. - /// - /// The exception message. - public RestErrorException(string message) - : this(message, null) - { - } - - /// - /// Initializes a new instance of the RestErrorException class. - /// - /// The exception message. - /// Inner exception. - public RestErrorException(string message, System.Exception innerException) - : base(message, innerException) - { - } - } -} diff --git a/src/Azure/Azure.Quantum.Client/generated/Models/SasUriResponse.cs b/src/Azure/Azure.Quantum.Client/generated/Models/SasUriResponse.cs deleted file mode 100644 index b811d2e06a6..00000000000 --- a/src/Azure/Azure.Quantum.Client/generated/Models/SasUriResponse.cs +++ /dev/null @@ -1,53 +0,0 @@ -// -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT License. See License.txt in the project root for -// license information. -// -// Code generated by Microsoft (R) AutoRest Code Generator. -// Changes may cause incorrect behavior and will be lost if the code is -// regenerated. -// - -namespace Microsoft.Azure.Quantum.Client.Models -{ - using Newtonsoft.Json; - using System.Linq; - - /// - /// Get SAS URL operation response. - /// - public partial class SasUriResponse - { - /// - /// Initializes a new instance of the SasUriResponse class. - /// - public SasUriResponse() - { - CustomInit(); - } - - /// - /// Initializes a new instance of the SasUriResponse class. - /// - /// A URL with a SAS token to upload a blob for - /// execution in the given workspace. - public SasUriResponse(string sasUri = default(string)) - { - SasUri = sasUri; - CustomInit(); - } - - /// - /// An initialization method that performs custom operations like setting defaults - /// - partial void CustomInit(); - - /// - /// Gets or sets a URL with a SAS token to upload a blob for execution - /// in the given workspace. - /// - [JsonProperty(PropertyName = "sasUri")] - public string SasUri { get; set; } - - } -} diff --git a/src/Azure/Azure.Quantum.Client/generated/Models/TargetAvailability.cs b/src/Azure/Azure.Quantum.Client/generated/Models/TargetAvailability.cs deleted file mode 100644 index a2009e7852c..00000000000 --- a/src/Azure/Azure.Quantum.Client/generated/Models/TargetAvailability.cs +++ /dev/null @@ -1,23 +0,0 @@ -// -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT License. See License.txt in the project root for -// license information. -// -// Code generated by Microsoft (R) AutoRest Code Generator. -// Changes may cause incorrect behavior and will be lost if the code is -// regenerated. -// - -namespace Microsoft.Azure.Quantum.Client.Models -{ - - /// - /// Defines values for TargetAvailability. - /// - public static class TargetAvailability - { - public const string Available = "Available"; - public const string Degraded = "Degraded"; - public const string Unavailable = "Unavailable"; - } -} diff --git a/src/Azure/Azure.Quantum.Client/generated/Models/TargetStatus.cs b/src/Azure/Azure.Quantum.Client/generated/Models/TargetStatus.cs deleted file mode 100644 index 78c150b3c95..00000000000 --- a/src/Azure/Azure.Quantum.Client/generated/Models/TargetStatus.cs +++ /dev/null @@ -1,79 +0,0 @@ -// -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT License. See License.txt in the project root for -// license information. -// -// Code generated by Microsoft (R) AutoRest Code Generator. -// Changes may cause incorrect behavior and will be lost if the code is -// regenerated. -// - -namespace Microsoft.Azure.Quantum.Client.Models -{ - using Newtonsoft.Json; - using System.Linq; - - /// - /// Target status. - /// - public partial class TargetStatus - { - /// - /// Initializes a new instance of the TargetStatus class. - /// - public TargetStatus() - { - CustomInit(); - } - - /// - /// Initializes a new instance of the TargetStatus class. - /// - /// Target id. - /// Target availability. Possible - /// values include: 'Available', 'Degraded', 'Unavailable' - /// Average queue time in - /// seconds. - /// A page with detailed status of the - /// provider. - public TargetStatus(string id = default(string), string currentAvailability = default(string), long? averageQueueTime = default(long?), string statusPage = default(string)) - { - Id = id; - CurrentAvailability = currentAvailability; - AverageQueueTime = averageQueueTime; - StatusPage = statusPage; - CustomInit(); - } - - /// - /// An initialization method that performs custom operations like setting defaults - /// - partial void CustomInit(); - - /// - /// Gets target id. - /// - [JsonProperty(PropertyName = "id")] - public string Id { get; private set; } - - /// - /// Gets target availability. Possible values include: 'Available', - /// 'Degraded', 'Unavailable' - /// - [JsonProperty(PropertyName = "currentAvailability")] - public string CurrentAvailability { get; private set; } - - /// - /// Gets average queue time in seconds. - /// - [JsonProperty(PropertyName = "averageQueueTime")] - public long? AverageQueueTime { get; private set; } - - /// - /// Gets a page with detailed status of the provider. - /// - [JsonProperty(PropertyName = "statusPage")] - public string StatusPage { get; private set; } - - } -} diff --git a/src/Azure/Azure.Quantum.Client/generated/ProvidersOperations.cs b/src/Azure/Azure.Quantum.Client/generated/ProvidersOperations.cs deleted file mode 100644 index 9cf8af20c06..00000000000 --- a/src/Azure/Azure.Quantum.Client/generated/ProvidersOperations.cs +++ /dev/null @@ -1,397 +0,0 @@ -// -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT License. See License.txt in the project root for -// license information. -// -// Code generated by Microsoft (R) AutoRest Code Generator. -// Changes may cause incorrect behavior and will be lost if the code is -// regenerated. -// - -namespace Microsoft.Azure.Quantum.Client -{ - using Microsoft.Rest; - using Microsoft.Rest.Azure; - using Models; - using Newtonsoft.Json; - using System.Collections; - using System.Collections.Generic; - using System.Linq; - using System.Net; - using System.Net.Http; - using System.Threading; - using System.Threading.Tasks; - - /// - /// ProvidersOperations operations. - /// - internal partial class ProvidersOperations : IServiceOperations, IProvidersOperations - { - /// - /// Initializes a new instance of the ProvidersOperations class. - /// - /// - /// Reference to the service client. - /// - /// - /// Thrown when a required parameter is null - /// - internal ProvidersOperations(QuantumClient client) - { - if (client == null) - { - throw new System.ArgumentNullException("client"); - } - Client = client; - } - - /// - /// Gets a reference to the QuantumClient - /// - public QuantumClient Client { get; private set; } - - /// - /// Get provider status. - /// - /// - /// Headers that will be added to request. - /// - /// - /// The cancellation token. - /// - /// - /// Thrown when the operation returned an invalid status code - /// - /// - /// Thrown when unable to deserialize the response - /// - /// - /// Thrown when a required parameter is null - /// - /// - /// Thrown when a required parameter is null - /// - /// - /// A response object containing the response body and response headers. - /// - public async Task>> GetStatusWithHttpMessagesAsync(Dictionary> customHeaders = null, CancellationToken cancellationToken = default(CancellationToken)) - { - if (Client.SubscriptionId == null) - { - throw new ValidationException(ValidationRules.CannotBeNull, "this.Client.SubscriptionId"); - } - if (Client.ResourceGroupName == null) - { - throw new ValidationException(ValidationRules.CannotBeNull, "this.Client.ResourceGroupName"); - } - if (Client.WorkspaceName == null) - { - throw new ValidationException(ValidationRules.CannotBeNull, "this.Client.WorkspaceName"); - } - // Tracing - bool _shouldTrace = ServiceClientTracing.IsEnabled; - string _invocationId = null; - if (_shouldTrace) - { - _invocationId = ServiceClientTracing.NextInvocationId.ToString(); - Dictionary tracingParameters = new Dictionary(); - tracingParameters.Add("cancellationToken", cancellationToken); - ServiceClientTracing.Enter(_invocationId, this, "GetStatus", tracingParameters); - } - // Construct URL - var _baseUrl = Client.BaseUri.AbsoluteUri; - var _url = new System.Uri(new System.Uri(_baseUrl + (_baseUrl.EndsWith("/") ? "" : "/")), "v1.0/subscriptions/{subscriptionId}/resourceGroups/{resourceGroupName}/providers/Microsoft.Quantum/workspaces/{workspaceName}/providerStatus").ToString(); - _url = _url.Replace("{subscriptionId}", System.Uri.EscapeDataString(Client.SubscriptionId)); - _url = _url.Replace("{resourceGroupName}", System.Uri.EscapeDataString(Client.ResourceGroupName)); - _url = _url.Replace("{workspaceName}", System.Uri.EscapeDataString(Client.WorkspaceName)); - List _queryParameters = new List(); - if (_queryParameters.Count > 0) - { - _url += (_url.Contains("?") ? "&" : "?") + string.Join("&", _queryParameters); - } - // Create HTTP transport objects - var _httpRequest = new HttpRequestMessage(); - HttpResponseMessage _httpResponse = null; - _httpRequest.Method = new HttpMethod("GET"); - _httpRequest.RequestUri = new System.Uri(_url); - // Set Headers - if (Client.GenerateClientRequestId != null && Client.GenerateClientRequestId.Value) - { - _httpRequest.Headers.TryAddWithoutValidation("x-ms-client-request-id", System.Guid.NewGuid().ToString()); - } - if (Client.AcceptLanguage != null) - { - if (_httpRequest.Headers.Contains("accept-language")) - { - _httpRequest.Headers.Remove("accept-language"); - } - _httpRequest.Headers.TryAddWithoutValidation("accept-language", Client.AcceptLanguage); - } - - - if (customHeaders != null) - { - foreach(var _header in customHeaders) - { - if (_httpRequest.Headers.Contains(_header.Key)) - { - _httpRequest.Headers.Remove(_header.Key); - } - _httpRequest.Headers.TryAddWithoutValidation(_header.Key, _header.Value); - } - } - - // Serialize Request - string _requestContent = null; - // Set Credentials - if (Client.Credentials != null) - { - cancellationToken.ThrowIfCancellationRequested(); - await Client.Credentials.ProcessHttpRequestAsync(_httpRequest, cancellationToken).ConfigureAwait(false); - } - // Send Request - if (_shouldTrace) - { - ServiceClientTracing.SendRequest(_invocationId, _httpRequest); - } - cancellationToken.ThrowIfCancellationRequested(); - _httpResponse = await Client.HttpClient.SendAsync(_httpRequest, cancellationToken).ConfigureAwait(false); - if (_shouldTrace) - { - ServiceClientTracing.ReceiveResponse(_invocationId, _httpResponse); - } - HttpStatusCode _statusCode = _httpResponse.StatusCode; - cancellationToken.ThrowIfCancellationRequested(); - string _responseContent = null; - if ((int)_statusCode != 200) - { - var ex = new RestErrorException(string.Format("Operation returned an invalid status code '{0}'", _statusCode)); - try - { - _responseContent = await _httpResponse.Content.ReadAsStringAsync().ConfigureAwait(false); - RestError _errorBody = Rest.Serialization.SafeJsonConvert.DeserializeObject(_responseContent, Client.DeserializationSettings); - if (_errorBody != null) - { - ex.Body = _errorBody; - } - } - catch (JsonException) - { - // Ignore the exception - } - ex.Request = new HttpRequestMessageWrapper(_httpRequest, _requestContent); - ex.Response = new HttpResponseMessageWrapper(_httpResponse, _responseContent); - if (_shouldTrace) - { - ServiceClientTracing.Error(_invocationId, ex); - } - _httpRequest.Dispose(); - if (_httpResponse != null) - { - _httpResponse.Dispose(); - } - throw ex; - } - // Create Result - var _result = new AzureOperationResponse>(); - _result.Request = _httpRequest; - _result.Response = _httpResponse; - if (_httpResponse.Headers.Contains("x-ms-request-id")) - { - _result.RequestId = _httpResponse.Headers.GetValues("x-ms-request-id").FirstOrDefault(); - } - // Deserialize Response - if ((int)_statusCode == 200) - { - _responseContent = await _httpResponse.Content.ReadAsStringAsync().ConfigureAwait(false); - try - { - _result.Body = Rest.Serialization.SafeJsonConvert.DeserializeObject>(_responseContent, Client.DeserializationSettings); - } - catch (JsonException ex) - { - _httpRequest.Dispose(); - if (_httpResponse != null) - { - _httpResponse.Dispose(); - } - throw new SerializationException("Unable to deserialize the response.", _responseContent, ex); - } - } - if (_shouldTrace) - { - ServiceClientTracing.Exit(_invocationId, _result); - } - return _result; - } - - /// - /// Get provider status. - /// - /// - /// The NextLink from the previous successful call to List operation. - /// - /// - /// Headers that will be added to request. - /// - /// - /// The cancellation token. - /// - /// - /// Thrown when the operation returned an invalid status code - /// - /// - /// Thrown when unable to deserialize the response - /// - /// - /// Thrown when a required parameter is null - /// - /// - /// Thrown when a required parameter is null - /// - /// - /// A response object containing the response body and response headers. - /// - public async Task>> GetStatusNextWithHttpMessagesAsync(string nextPageLink, Dictionary> customHeaders = null, CancellationToken cancellationToken = default(CancellationToken)) - { - if (nextPageLink == null) - { - throw new ValidationException(ValidationRules.CannotBeNull, "nextPageLink"); - } - // Tracing - bool _shouldTrace = ServiceClientTracing.IsEnabled; - string _invocationId = null; - if (_shouldTrace) - { - _invocationId = ServiceClientTracing.NextInvocationId.ToString(); - Dictionary tracingParameters = new Dictionary(); - tracingParameters.Add("nextPageLink", nextPageLink); - tracingParameters.Add("cancellationToken", cancellationToken); - ServiceClientTracing.Enter(_invocationId, this, "GetStatusNext", tracingParameters); - } - // Construct URL - string _url = "{nextLink}"; - _url = _url.Replace("{nextLink}", nextPageLink); - List _queryParameters = new List(); - if (_queryParameters.Count > 0) - { - _url += (_url.Contains("?") ? "&" : "?") + string.Join("&", _queryParameters); - } - // Create HTTP transport objects - var _httpRequest = new HttpRequestMessage(); - HttpResponseMessage _httpResponse = null; - _httpRequest.Method = new HttpMethod("GET"); - _httpRequest.RequestUri = new System.Uri(_url); - // Set Headers - if (Client.GenerateClientRequestId != null && Client.GenerateClientRequestId.Value) - { - _httpRequest.Headers.TryAddWithoutValidation("x-ms-client-request-id", System.Guid.NewGuid().ToString()); - } - if (Client.AcceptLanguage != null) - { - if (_httpRequest.Headers.Contains("accept-language")) - { - _httpRequest.Headers.Remove("accept-language"); - } - _httpRequest.Headers.TryAddWithoutValidation("accept-language", Client.AcceptLanguage); - } - - - if (customHeaders != null) - { - foreach(var _header in customHeaders) - { - if (_httpRequest.Headers.Contains(_header.Key)) - { - _httpRequest.Headers.Remove(_header.Key); - } - _httpRequest.Headers.TryAddWithoutValidation(_header.Key, _header.Value); - } - } - - // Serialize Request - string _requestContent = null; - // Set Credentials - if (Client.Credentials != null) - { - cancellationToken.ThrowIfCancellationRequested(); - await Client.Credentials.ProcessHttpRequestAsync(_httpRequest, cancellationToken).ConfigureAwait(false); - } - // Send Request - if (_shouldTrace) - { - ServiceClientTracing.SendRequest(_invocationId, _httpRequest); - } - cancellationToken.ThrowIfCancellationRequested(); - _httpResponse = await Client.HttpClient.SendAsync(_httpRequest, cancellationToken).ConfigureAwait(false); - if (_shouldTrace) - { - ServiceClientTracing.ReceiveResponse(_invocationId, _httpResponse); - } - HttpStatusCode _statusCode = _httpResponse.StatusCode; - cancellationToken.ThrowIfCancellationRequested(); - string _responseContent = null; - if ((int)_statusCode != 200) - { - var ex = new RestErrorException(string.Format("Operation returned an invalid status code '{0}'", _statusCode)); - try - { - _responseContent = await _httpResponse.Content.ReadAsStringAsync().ConfigureAwait(false); - RestError _errorBody = Rest.Serialization.SafeJsonConvert.DeserializeObject(_responseContent, Client.DeserializationSettings); - if (_errorBody != null) - { - ex.Body = _errorBody; - } - } - catch (JsonException) - { - // Ignore the exception - } - ex.Request = new HttpRequestMessageWrapper(_httpRequest, _requestContent); - ex.Response = new HttpResponseMessageWrapper(_httpResponse, _responseContent); - if (_shouldTrace) - { - ServiceClientTracing.Error(_invocationId, ex); - } - _httpRequest.Dispose(); - if (_httpResponse != null) - { - _httpResponse.Dispose(); - } - throw ex; - } - // Create Result - var _result = new AzureOperationResponse>(); - _result.Request = _httpRequest; - _result.Response = _httpResponse; - if (_httpResponse.Headers.Contains("x-ms-request-id")) - { - _result.RequestId = _httpResponse.Headers.GetValues("x-ms-request-id").FirstOrDefault(); - } - // Deserialize Response - if ((int)_statusCode == 200) - { - _responseContent = await _httpResponse.Content.ReadAsStringAsync().ConfigureAwait(false); - try - { - _result.Body = Rest.Serialization.SafeJsonConvert.DeserializeObject>(_responseContent, Client.DeserializationSettings); - } - catch (JsonException ex) - { - _httpRequest.Dispose(); - if (_httpResponse != null) - { - _httpResponse.Dispose(); - } - throw new SerializationException("Unable to deserialize the response.", _responseContent, ex); - } - } - if (_shouldTrace) - { - ServiceClientTracing.Exit(_invocationId, _result); - } - return _result; - } - - } -} diff --git a/src/Azure/Azure.Quantum.Client/generated/ProvidersOperationsExtensions.cs b/src/Azure/Azure.Quantum.Client/generated/ProvidersOperationsExtensions.cs deleted file mode 100644 index 649d483f64a..00000000000 --- a/src/Azure/Azure.Quantum.Client/generated/ProvidersOperationsExtensions.cs +++ /dev/null @@ -1,87 +0,0 @@ -// -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT License. See License.txt in the project root for -// license information. -// -// Code generated by Microsoft (R) AutoRest Code Generator. -// Changes may cause incorrect behavior and will be lost if the code is -// regenerated. -// - -namespace Microsoft.Azure.Quantum.Client -{ - using Microsoft.Rest; - using Microsoft.Rest.Azure; - using Models; - using System.Threading; - using System.Threading.Tasks; - - /// - /// Extension methods for ProvidersOperations. - /// - public static partial class ProvidersOperationsExtensions - { - /// - /// Get provider status. - /// - /// - /// The operations group for this extension method. - /// - public static IPage GetStatus(this IProvidersOperations operations) - { - return operations.GetStatusAsync().GetAwaiter().GetResult(); - } - - /// - /// Get provider status. - /// - /// - /// The operations group for this extension method. - /// - /// - /// The cancellation token. - /// - public static async Task> GetStatusAsync(this IProvidersOperations operations, CancellationToken cancellationToken = default(CancellationToken)) - { - using (var _result = await operations.GetStatusWithHttpMessagesAsync(null, cancellationToken).ConfigureAwait(false)) - { - return _result.Body; - } - } - - /// - /// Get provider status. - /// - /// - /// The operations group for this extension method. - /// - /// - /// The NextLink from the previous successful call to List operation. - /// - public static IPage GetStatusNext(this IProvidersOperations operations, string nextPageLink) - { - return operations.GetStatusNextAsync(nextPageLink).GetAwaiter().GetResult(); - } - - /// - /// Get provider status. - /// - /// - /// The operations group for this extension method. - /// - /// - /// The NextLink from the previous successful call to List operation. - /// - /// - /// The cancellation token. - /// - public static async Task> GetStatusNextAsync(this IProvidersOperations operations, string nextPageLink, CancellationToken cancellationToken = default(CancellationToken)) - { - using (var _result = await operations.GetStatusNextWithHttpMessagesAsync(nextPageLink, null, cancellationToken).ConfigureAwait(false)) - { - return _result.Body; - } - } - - } -} diff --git a/src/Azure/Azure.Quantum.Client/generated/QuantumClient.cs b/src/Azure/Azure.Quantum.Client/generated/QuantumClient.cs deleted file mode 100644 index 7344a8506c4..00000000000 --- a/src/Azure/Azure.Quantum.Client/generated/QuantumClient.cs +++ /dev/null @@ -1,381 +0,0 @@ -// -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT License. See License.txt in the project root for -// license information. -// -// Code generated by Microsoft (R) AutoRest Code Generator. -// Changes may cause incorrect behavior and will be lost if the code is -// regenerated. -// - -namespace Microsoft.Azure.Quantum.Client -{ - using Microsoft.Rest; - using Microsoft.Rest.Azure; - using Microsoft.Rest.Serialization; - using Models; - using Newtonsoft.Json; - using System.Collections; - using System.Collections.Generic; - using System.Linq; - using System.Net; - using System.Net.Http; - - /// - /// Azure Quantum REST API client - /// - public partial class QuantumClient : ServiceClient, IQuantumClient, IAzureClient - { - /// - /// The base URI of the service. - /// - public System.Uri BaseUri { get; set; } - - /// - /// Gets or sets json serialization settings. - /// - public JsonSerializerSettings SerializationSettings { get; private set; } - - /// - /// Gets or sets json deserialization settings. - /// - public JsonSerializerSettings DeserializationSettings { get; private set; } - - /// - /// Credentials needed for the client to connect to Azure. - /// - public ServiceClientCredentials Credentials { get; private set; } - - /// - /// The Azure subscription ID. This is a GUID-formatted string (e.g. - /// 00000000-0000-0000-0000-000000000000) - /// - public string SubscriptionId { get; set; } - - /// - /// Name of an Azure resource group. - /// - public string ResourceGroupName { get; set; } - - /// - /// Name of the workspace. - /// - public string WorkspaceName { get; set; } - - /// - /// The preferred language for the response. - /// - public string AcceptLanguage { get; set; } - - /// - /// The retry timeout in seconds for Long Running Operations. Default value is - /// 30. - /// - public int? LongRunningOperationRetryTimeout { get; set; } - - /// - /// Whether a unique x-ms-client-request-id should be generated. When set to - /// true a unique x-ms-client-request-id value is generated and included in - /// each request. Default is true. - /// - public bool? GenerateClientRequestId { get; set; } - - /// - /// Gets the IJobsOperations. - /// - public virtual IJobsOperations Jobs { get; private set; } - - /// - /// Gets the IProvidersOperations. - /// - public virtual IProvidersOperations Providers { get; private set; } - - /// - /// Gets the IStorageOperations. - /// - public virtual IStorageOperations Storage { get; private set; } - - /// - /// Gets the IQuotasOperations. - /// - public virtual IQuotasOperations Quotas { get; private set; } - - /// - /// Initializes a new instance of the QuantumClient class. - /// - /// - /// HttpClient to be used - /// - /// - /// True: will dispose the provided httpClient on calling QuantumClient.Dispose(). False: will not dispose provided httpClient - protected QuantumClient(HttpClient httpClient, bool disposeHttpClient) : base(httpClient, disposeHttpClient) - { - Initialize(); - } - - /// - /// Initializes a new instance of the QuantumClient class. - /// - /// - /// Optional. The delegating handlers to add to the http client pipeline. - /// - protected QuantumClient(params DelegatingHandler[] handlers) : base(handlers) - { - Initialize(); - } - - /// - /// Initializes a new instance of the QuantumClient class. - /// - /// - /// Optional. The http client handler used to handle http transport. - /// - /// - /// Optional. The delegating handlers to add to the http client pipeline. - /// - protected QuantumClient(HttpClientHandler rootHandler, params DelegatingHandler[] handlers) : base(rootHandler, handlers) - { - Initialize(); - } - - /// - /// Initializes a new instance of the QuantumClient class. - /// - /// - /// Optional. The base URI of the service. - /// - /// - /// Optional. The delegating handlers to add to the http client pipeline. - /// - /// - /// Thrown when a required parameter is null - /// - protected QuantumClient(System.Uri baseUri, params DelegatingHandler[] handlers) : this(handlers) - { - if (baseUri == null) - { - throw new System.ArgumentNullException("baseUri"); - } - BaseUri = baseUri; - } - - /// - /// Initializes a new instance of the QuantumClient class. - /// - /// - /// Optional. The base URI of the service. - /// - /// - /// Optional. The http client handler used to handle http transport. - /// - /// - /// Optional. The delegating handlers to add to the http client pipeline. - /// - /// - /// Thrown when a required parameter is null - /// - protected QuantumClient(System.Uri baseUri, HttpClientHandler rootHandler, params DelegatingHandler[] handlers) : this(rootHandler, handlers) - { - if (baseUri == null) - { - throw new System.ArgumentNullException("baseUri"); - } - BaseUri = baseUri; - } - - /// - /// Initializes a new instance of the QuantumClient class. - /// - /// - /// Required. Credentials needed for the client to connect to Azure. - /// - /// - /// Optional. The delegating handlers to add to the http client pipeline. - /// - /// - /// Thrown when a required parameter is null - /// - public QuantumClient(ServiceClientCredentials credentials, params DelegatingHandler[] handlers) : this(handlers) - { - if (credentials == null) - { - throw new System.ArgumentNullException("credentials"); - } - Credentials = credentials; - if (Credentials != null) - { - Credentials.InitializeServiceClient(this); - } - } - - /// - /// Initializes a new instance of the QuantumClient class. - /// - /// - /// Required. Credentials needed for the client to connect to Azure. - /// - /// - /// HttpClient to be used - /// - /// - /// True: will dispose the provided httpClient on calling QuantumClient.Dispose(). False: will not dispose provided httpClient - /// - /// Thrown when a required parameter is null - /// - public QuantumClient(ServiceClientCredentials credentials, HttpClient httpClient, bool disposeHttpClient) : this(httpClient, disposeHttpClient) - { - if (credentials == null) - { - throw new System.ArgumentNullException("credentials"); - } - Credentials = credentials; - if (Credentials != null) - { - Credentials.InitializeServiceClient(this); - } - } - - /// - /// Initializes a new instance of the QuantumClient class. - /// - /// - /// Required. Credentials needed for the client to connect to Azure. - /// - /// - /// Optional. The http client handler used to handle http transport. - /// - /// - /// Optional. The delegating handlers to add to the http client pipeline. - /// - /// - /// Thrown when a required parameter is null - /// - public QuantumClient(ServiceClientCredentials credentials, HttpClientHandler rootHandler, params DelegatingHandler[] handlers) : this(rootHandler, handlers) - { - if (credentials == null) - { - throw new System.ArgumentNullException("credentials"); - } - Credentials = credentials; - if (Credentials != null) - { - Credentials.InitializeServiceClient(this); - } - } - - /// - /// Initializes a new instance of the QuantumClient class. - /// - /// - /// Optional. The base URI of the service. - /// - /// - /// Required. Credentials needed for the client to connect to Azure. - /// - /// - /// Optional. The delegating handlers to add to the http client pipeline. - /// - /// - /// Thrown when a required parameter is null - /// - public QuantumClient(System.Uri baseUri, ServiceClientCredentials credentials, params DelegatingHandler[] handlers) : this(handlers) - { - if (baseUri == null) - { - throw new System.ArgumentNullException("baseUri"); - } - if (credentials == null) - { - throw new System.ArgumentNullException("credentials"); - } - BaseUri = baseUri; - Credentials = credentials; - if (Credentials != null) - { - Credentials.InitializeServiceClient(this); - } - } - - /// - /// Initializes a new instance of the QuantumClient class. - /// - /// - /// Optional. The base URI of the service. - /// - /// - /// Required. Credentials needed for the client to connect to Azure. - /// - /// - /// Optional. The http client handler used to handle http transport. - /// - /// - /// Optional. The delegating handlers to add to the http client pipeline. - /// - /// - /// Thrown when a required parameter is null - /// - public QuantumClient(System.Uri baseUri, ServiceClientCredentials credentials, HttpClientHandler rootHandler, params DelegatingHandler[] handlers) : this(rootHandler, handlers) - { - if (baseUri == null) - { - throw new System.ArgumentNullException("baseUri"); - } - if (credentials == null) - { - throw new System.ArgumentNullException("credentials"); - } - BaseUri = baseUri; - Credentials = credentials; - if (Credentials != null) - { - Credentials.InitializeServiceClient(this); - } - } - - /// - /// An optional partial-method to perform custom initialization. - /// - partial void CustomInitialize(); - /// - /// Initializes client properties. - /// - private void Initialize() - { - Jobs = new JobsOperations(this); - Providers = new ProvidersOperations(this); - Storage = new StorageOperations(this); - Quotas = new QuotasOperations(this); - BaseUri = new System.Uri("https://quantum.azure.com"); - AcceptLanguage = "en-US"; - LongRunningOperationRetryTimeout = 30; - GenerateClientRequestId = true; - SerializationSettings = new JsonSerializerSettings - { - Formatting = Newtonsoft.Json.Formatting.Indented, - DateFormatHandling = Newtonsoft.Json.DateFormatHandling.IsoDateFormat, - DateTimeZoneHandling = Newtonsoft.Json.DateTimeZoneHandling.Utc, - NullValueHandling = Newtonsoft.Json.NullValueHandling.Ignore, - ReferenceLoopHandling = Newtonsoft.Json.ReferenceLoopHandling.Serialize, - ContractResolver = new ReadOnlyJsonContractResolver(), - Converters = new List - { - new Iso8601TimeSpanConverter() - } - }; - DeserializationSettings = new JsonSerializerSettings - { - DateFormatHandling = Newtonsoft.Json.DateFormatHandling.IsoDateFormat, - DateTimeZoneHandling = Newtonsoft.Json.DateTimeZoneHandling.Utc, - NullValueHandling = Newtonsoft.Json.NullValueHandling.Ignore, - ReferenceLoopHandling = Newtonsoft.Json.ReferenceLoopHandling.Serialize, - ContractResolver = new ReadOnlyJsonContractResolver(), - Converters = new List - { - new Iso8601TimeSpanConverter() - } - }; - CustomInitialize(); - DeserializationSettings.Converters.Add(new CloudErrorJsonConverter()); - } - } -} diff --git a/src/Azure/Azure.Quantum.Client/generated/QuotasOperations.cs b/src/Azure/Azure.Quantum.Client/generated/QuotasOperations.cs deleted file mode 100644 index 639526087e5..00000000000 --- a/src/Azure/Azure.Quantum.Client/generated/QuotasOperations.cs +++ /dev/null @@ -1,397 +0,0 @@ -// -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT License. See License.txt in the project root for -// license information. -// -// Code generated by Microsoft (R) AutoRest Code Generator. -// Changes may cause incorrect behavior and will be lost if the code is -// regenerated. -// - -namespace Microsoft.Azure.Quantum.Client -{ - using Microsoft.Rest; - using Microsoft.Rest.Azure; - using Models; - using Newtonsoft.Json; - using System.Collections; - using System.Collections.Generic; - using System.Linq; - using System.Net; - using System.Net.Http; - using System.Threading; - using System.Threading.Tasks; - - /// - /// QuotasOperations operations. - /// - internal partial class QuotasOperations : IServiceOperations, IQuotasOperations - { - /// - /// Initializes a new instance of the QuotasOperations class. - /// - /// - /// Reference to the service client. - /// - /// - /// Thrown when a required parameter is null - /// - internal QuotasOperations(QuantumClient client) - { - if (client == null) - { - throw new System.ArgumentNullException("client"); - } - Client = client; - } - - /// - /// Gets a reference to the QuantumClient - /// - public QuantumClient Client { get; private set; } - - /// - /// List quotas for the given workspace. - /// - /// - /// Headers that will be added to request. - /// - /// - /// The cancellation token. - /// - /// - /// Thrown when the operation returned an invalid status code - /// - /// - /// Thrown when unable to deserialize the response - /// - /// - /// Thrown when a required parameter is null - /// - /// - /// Thrown when a required parameter is null - /// - /// - /// A response object containing the response body and response headers. - /// - public async Task>> ListWithHttpMessagesAsync(Dictionary> customHeaders = null, CancellationToken cancellationToken = default(CancellationToken)) - { - if (Client.SubscriptionId == null) - { - throw new ValidationException(ValidationRules.CannotBeNull, "this.Client.SubscriptionId"); - } - if (Client.ResourceGroupName == null) - { - throw new ValidationException(ValidationRules.CannotBeNull, "this.Client.ResourceGroupName"); - } - if (Client.WorkspaceName == null) - { - throw new ValidationException(ValidationRules.CannotBeNull, "this.Client.WorkspaceName"); - } - // Tracing - bool _shouldTrace = ServiceClientTracing.IsEnabled; - string _invocationId = null; - if (_shouldTrace) - { - _invocationId = ServiceClientTracing.NextInvocationId.ToString(); - Dictionary tracingParameters = new Dictionary(); - tracingParameters.Add("cancellationToken", cancellationToken); - ServiceClientTracing.Enter(_invocationId, this, "List", tracingParameters); - } - // Construct URL - var _baseUrl = Client.BaseUri.AbsoluteUri; - var _url = new System.Uri(new System.Uri(_baseUrl + (_baseUrl.EndsWith("/") ? "" : "/")), "v1.0/subscriptions/{subscriptionId}/resourceGroups/{resourceGroupName}/providers/Microsoft.Quantum/workspaces/{workspaceName}/quotas").ToString(); - _url = _url.Replace("{subscriptionId}", System.Uri.EscapeDataString(Client.SubscriptionId)); - _url = _url.Replace("{resourceGroupName}", System.Uri.EscapeDataString(Client.ResourceGroupName)); - _url = _url.Replace("{workspaceName}", System.Uri.EscapeDataString(Client.WorkspaceName)); - List _queryParameters = new List(); - if (_queryParameters.Count > 0) - { - _url += (_url.Contains("?") ? "&" : "?") + string.Join("&", _queryParameters); - } - // Create HTTP transport objects - var _httpRequest = new HttpRequestMessage(); - HttpResponseMessage _httpResponse = null; - _httpRequest.Method = new HttpMethod("GET"); - _httpRequest.RequestUri = new System.Uri(_url); - // Set Headers - if (Client.GenerateClientRequestId != null && Client.GenerateClientRequestId.Value) - { - _httpRequest.Headers.TryAddWithoutValidation("x-ms-client-request-id", System.Guid.NewGuid().ToString()); - } - if (Client.AcceptLanguage != null) - { - if (_httpRequest.Headers.Contains("accept-language")) - { - _httpRequest.Headers.Remove("accept-language"); - } - _httpRequest.Headers.TryAddWithoutValidation("accept-language", Client.AcceptLanguage); - } - - - if (customHeaders != null) - { - foreach(var _header in customHeaders) - { - if (_httpRequest.Headers.Contains(_header.Key)) - { - _httpRequest.Headers.Remove(_header.Key); - } - _httpRequest.Headers.TryAddWithoutValidation(_header.Key, _header.Value); - } - } - - // Serialize Request - string _requestContent = null; - // Set Credentials - if (Client.Credentials != null) - { - cancellationToken.ThrowIfCancellationRequested(); - await Client.Credentials.ProcessHttpRequestAsync(_httpRequest, cancellationToken).ConfigureAwait(false); - } - // Send Request - if (_shouldTrace) - { - ServiceClientTracing.SendRequest(_invocationId, _httpRequest); - } - cancellationToken.ThrowIfCancellationRequested(); - _httpResponse = await Client.HttpClient.SendAsync(_httpRequest, cancellationToken).ConfigureAwait(false); - if (_shouldTrace) - { - ServiceClientTracing.ReceiveResponse(_invocationId, _httpResponse); - } - HttpStatusCode _statusCode = _httpResponse.StatusCode; - cancellationToken.ThrowIfCancellationRequested(); - string _responseContent = null; - if ((int)_statusCode != 200) - { - var ex = new RestErrorException(string.Format("Operation returned an invalid status code '{0}'", _statusCode)); - try - { - _responseContent = await _httpResponse.Content.ReadAsStringAsync().ConfigureAwait(false); - RestError _errorBody = Rest.Serialization.SafeJsonConvert.DeserializeObject(_responseContent, Client.DeserializationSettings); - if (_errorBody != null) - { - ex.Body = _errorBody; - } - } - catch (JsonException) - { - // Ignore the exception - } - ex.Request = new HttpRequestMessageWrapper(_httpRequest, _requestContent); - ex.Response = new HttpResponseMessageWrapper(_httpResponse, _responseContent); - if (_shouldTrace) - { - ServiceClientTracing.Error(_invocationId, ex); - } - _httpRequest.Dispose(); - if (_httpResponse != null) - { - _httpResponse.Dispose(); - } - throw ex; - } - // Create Result - var _result = new AzureOperationResponse>(); - _result.Request = _httpRequest; - _result.Response = _httpResponse; - if (_httpResponse.Headers.Contains("x-ms-request-id")) - { - _result.RequestId = _httpResponse.Headers.GetValues("x-ms-request-id").FirstOrDefault(); - } - // Deserialize Response - if ((int)_statusCode == 200) - { - _responseContent = await _httpResponse.Content.ReadAsStringAsync().ConfigureAwait(false); - try - { - _result.Body = Rest.Serialization.SafeJsonConvert.DeserializeObject>(_responseContent, Client.DeserializationSettings); - } - catch (JsonException ex) - { - _httpRequest.Dispose(); - if (_httpResponse != null) - { - _httpResponse.Dispose(); - } - throw new SerializationException("Unable to deserialize the response.", _responseContent, ex); - } - } - if (_shouldTrace) - { - ServiceClientTracing.Exit(_invocationId, _result); - } - return _result; - } - - /// - /// List quotas for the given workspace. - /// - /// - /// The NextLink from the previous successful call to List operation. - /// - /// - /// Headers that will be added to request. - /// - /// - /// The cancellation token. - /// - /// - /// Thrown when the operation returned an invalid status code - /// - /// - /// Thrown when unable to deserialize the response - /// - /// - /// Thrown when a required parameter is null - /// - /// - /// Thrown when a required parameter is null - /// - /// - /// A response object containing the response body and response headers. - /// - public async Task>> ListNextWithHttpMessagesAsync(string nextPageLink, Dictionary> customHeaders = null, CancellationToken cancellationToken = default(CancellationToken)) - { - if (nextPageLink == null) - { - throw new ValidationException(ValidationRules.CannotBeNull, "nextPageLink"); - } - // Tracing - bool _shouldTrace = ServiceClientTracing.IsEnabled; - string _invocationId = null; - if (_shouldTrace) - { - _invocationId = ServiceClientTracing.NextInvocationId.ToString(); - Dictionary tracingParameters = new Dictionary(); - tracingParameters.Add("nextPageLink", nextPageLink); - tracingParameters.Add("cancellationToken", cancellationToken); - ServiceClientTracing.Enter(_invocationId, this, "ListNext", tracingParameters); - } - // Construct URL - string _url = "{nextLink}"; - _url = _url.Replace("{nextLink}", nextPageLink); - List _queryParameters = new List(); - if (_queryParameters.Count > 0) - { - _url += (_url.Contains("?") ? "&" : "?") + string.Join("&", _queryParameters); - } - // Create HTTP transport objects - var _httpRequest = new HttpRequestMessage(); - HttpResponseMessage _httpResponse = null; - _httpRequest.Method = new HttpMethod("GET"); - _httpRequest.RequestUri = new System.Uri(_url); - // Set Headers - if (Client.GenerateClientRequestId != null && Client.GenerateClientRequestId.Value) - { - _httpRequest.Headers.TryAddWithoutValidation("x-ms-client-request-id", System.Guid.NewGuid().ToString()); - } - if (Client.AcceptLanguage != null) - { - if (_httpRequest.Headers.Contains("accept-language")) - { - _httpRequest.Headers.Remove("accept-language"); - } - _httpRequest.Headers.TryAddWithoutValidation("accept-language", Client.AcceptLanguage); - } - - - if (customHeaders != null) - { - foreach(var _header in customHeaders) - { - if (_httpRequest.Headers.Contains(_header.Key)) - { - _httpRequest.Headers.Remove(_header.Key); - } - _httpRequest.Headers.TryAddWithoutValidation(_header.Key, _header.Value); - } - } - - // Serialize Request - string _requestContent = null; - // Set Credentials - if (Client.Credentials != null) - { - cancellationToken.ThrowIfCancellationRequested(); - await Client.Credentials.ProcessHttpRequestAsync(_httpRequest, cancellationToken).ConfigureAwait(false); - } - // Send Request - if (_shouldTrace) - { - ServiceClientTracing.SendRequest(_invocationId, _httpRequest); - } - cancellationToken.ThrowIfCancellationRequested(); - _httpResponse = await Client.HttpClient.SendAsync(_httpRequest, cancellationToken).ConfigureAwait(false); - if (_shouldTrace) - { - ServiceClientTracing.ReceiveResponse(_invocationId, _httpResponse); - } - HttpStatusCode _statusCode = _httpResponse.StatusCode; - cancellationToken.ThrowIfCancellationRequested(); - string _responseContent = null; - if ((int)_statusCode != 200) - { - var ex = new RestErrorException(string.Format("Operation returned an invalid status code '{0}'", _statusCode)); - try - { - _responseContent = await _httpResponse.Content.ReadAsStringAsync().ConfigureAwait(false); - RestError _errorBody = Rest.Serialization.SafeJsonConvert.DeserializeObject(_responseContent, Client.DeserializationSettings); - if (_errorBody != null) - { - ex.Body = _errorBody; - } - } - catch (JsonException) - { - // Ignore the exception - } - ex.Request = new HttpRequestMessageWrapper(_httpRequest, _requestContent); - ex.Response = new HttpResponseMessageWrapper(_httpResponse, _responseContent); - if (_shouldTrace) - { - ServiceClientTracing.Error(_invocationId, ex); - } - _httpRequest.Dispose(); - if (_httpResponse != null) - { - _httpResponse.Dispose(); - } - throw ex; - } - // Create Result - var _result = new AzureOperationResponse>(); - _result.Request = _httpRequest; - _result.Response = _httpResponse; - if (_httpResponse.Headers.Contains("x-ms-request-id")) - { - _result.RequestId = _httpResponse.Headers.GetValues("x-ms-request-id").FirstOrDefault(); - } - // Deserialize Response - if ((int)_statusCode == 200) - { - _responseContent = await _httpResponse.Content.ReadAsStringAsync().ConfigureAwait(false); - try - { - _result.Body = Rest.Serialization.SafeJsonConvert.DeserializeObject>(_responseContent, Client.DeserializationSettings); - } - catch (JsonException ex) - { - _httpRequest.Dispose(); - if (_httpResponse != null) - { - _httpResponse.Dispose(); - } - throw new SerializationException("Unable to deserialize the response.", _responseContent, ex); - } - } - if (_shouldTrace) - { - ServiceClientTracing.Exit(_invocationId, _result); - } - return _result; - } - - } -} diff --git a/src/Azure/Azure.Quantum.Client/generated/QuotasOperationsExtensions.cs b/src/Azure/Azure.Quantum.Client/generated/QuotasOperationsExtensions.cs deleted file mode 100644 index 99f5cc55e9b..00000000000 --- a/src/Azure/Azure.Quantum.Client/generated/QuotasOperationsExtensions.cs +++ /dev/null @@ -1,87 +0,0 @@ -// -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT License. See License.txt in the project root for -// license information. -// -// Code generated by Microsoft (R) AutoRest Code Generator. -// Changes may cause incorrect behavior and will be lost if the code is -// regenerated. -// - -namespace Microsoft.Azure.Quantum.Client -{ - using Microsoft.Rest; - using Microsoft.Rest.Azure; - using Models; - using System.Threading; - using System.Threading.Tasks; - - /// - /// Extension methods for QuotasOperations. - /// - public static partial class QuotasOperationsExtensions - { - /// - /// List quotas for the given workspace. - /// - /// - /// The operations group for this extension method. - /// - public static IPage List(this IQuotasOperations operations) - { - return operations.ListAsync().GetAwaiter().GetResult(); - } - - /// - /// List quotas for the given workspace. - /// - /// - /// The operations group for this extension method. - /// - /// - /// The cancellation token. - /// - public static async Task> ListAsync(this IQuotasOperations operations, CancellationToken cancellationToken = default(CancellationToken)) - { - using (var _result = await operations.ListWithHttpMessagesAsync(null, cancellationToken).ConfigureAwait(false)) - { - return _result.Body; - } - } - - /// - /// List quotas for the given workspace. - /// - /// - /// The operations group for this extension method. - /// - /// - /// The NextLink from the previous successful call to List operation. - /// - public static IPage ListNext(this IQuotasOperations operations, string nextPageLink) - { - return operations.ListNextAsync(nextPageLink).GetAwaiter().GetResult(); - } - - /// - /// List quotas for the given workspace. - /// - /// - /// The operations group for this extension method. - /// - /// - /// The NextLink from the previous successful call to List operation. - /// - /// - /// The cancellation token. - /// - public static async Task> ListNextAsync(this IQuotasOperations operations, string nextPageLink, CancellationToken cancellationToken = default(CancellationToken)) - { - using (var _result = await operations.ListNextWithHttpMessagesAsync(nextPageLink, null, cancellationToken).ConfigureAwait(false)) - { - return _result.Body; - } - } - - } -} diff --git a/src/Azure/Azure.Quantum.Client/generated/StorageOperations.cs b/src/Azure/Azure.Quantum.Client/generated/StorageOperations.cs deleted file mode 100644 index 885842c24c3..00000000000 --- a/src/Azure/Azure.Quantum.Client/generated/StorageOperations.cs +++ /dev/null @@ -1,249 +0,0 @@ -// -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT License. See License.txt in the project root for -// license information. -// -// Code generated by Microsoft (R) AutoRest Code Generator. -// Changes may cause incorrect behavior and will be lost if the code is -// regenerated. -// - -namespace Microsoft.Azure.Quantum.Client -{ - using Microsoft.Rest; - using Microsoft.Rest.Azure; - using Models; - using Newtonsoft.Json; - using System.Collections; - using System.Collections.Generic; - using System.Linq; - using System.Net; - using System.Net.Http; - using System.Threading; - using System.Threading.Tasks; - - /// - /// StorageOperations operations. - /// - internal partial class StorageOperations : IServiceOperations, IStorageOperations - { - /// - /// Initializes a new instance of the StorageOperations class. - /// - /// - /// Reference to the service client. - /// - /// - /// Thrown when a required parameter is null - /// - internal StorageOperations(QuantumClient client) - { - if (client == null) - { - throw new System.ArgumentNullException("client"); - } - Client = client; - } - - /// - /// Gets a reference to the QuantumClient - /// - public QuantumClient Client { get; private set; } - - /// - /// Gets a URL with SAS token for a container/blob in the storage account - /// associated with the workspace. The SAS URL can be used to upload job input - /// and/or download job output. - /// - /// - /// The details (name and container) of the blob to store or download data. - /// - /// - /// Headers that will be added to request. - /// - /// - /// The cancellation token. - /// - /// - /// Thrown when the operation returned an invalid status code - /// - /// - /// Thrown when unable to deserialize the response - /// - /// - /// Thrown when a required parameter is null - /// - /// - /// Thrown when a required parameter is null - /// - /// - /// A response object containing the response body and response headers. - /// - public async Task> SasUriWithHttpMessagesAsync(BlobDetails blobDetails, Dictionary> customHeaders = null, CancellationToken cancellationToken = default(CancellationToken)) - { - if (Client.SubscriptionId == null) - { - throw new ValidationException(ValidationRules.CannotBeNull, "this.Client.SubscriptionId"); - } - if (Client.ResourceGroupName == null) - { - throw new ValidationException(ValidationRules.CannotBeNull, "this.Client.ResourceGroupName"); - } - if (Client.WorkspaceName == null) - { - throw new ValidationException(ValidationRules.CannotBeNull, "this.Client.WorkspaceName"); - } - if (blobDetails == null) - { - throw new ValidationException(ValidationRules.CannotBeNull, "blobDetails"); - } - if (blobDetails != null) - { - blobDetails.Validate(); - } - // Tracing - bool _shouldTrace = ServiceClientTracing.IsEnabled; - string _invocationId = null; - if (_shouldTrace) - { - _invocationId = ServiceClientTracing.NextInvocationId.ToString(); - Dictionary tracingParameters = new Dictionary(); - tracingParameters.Add("blobDetails", blobDetails); - tracingParameters.Add("cancellationToken", cancellationToken); - ServiceClientTracing.Enter(_invocationId, this, "SasUri", tracingParameters); - } - // Construct URL - var _baseUrl = Client.BaseUri.AbsoluteUri; - var _url = new System.Uri(new System.Uri(_baseUrl + (_baseUrl.EndsWith("/") ? "" : "/")), "v1.0/subscriptions/{subscriptionId}/resourceGroups/{resourceGroupName}/providers/Microsoft.Quantum/workspaces/{workspaceName}/storage/sasUri").ToString(); - _url = _url.Replace("{subscriptionId}", System.Uri.EscapeDataString(Client.SubscriptionId)); - _url = _url.Replace("{resourceGroupName}", System.Uri.EscapeDataString(Client.ResourceGroupName)); - _url = _url.Replace("{workspaceName}", System.Uri.EscapeDataString(Client.WorkspaceName)); - List _queryParameters = new List(); - if (_queryParameters.Count > 0) - { - _url += (_url.Contains("?") ? "&" : "?") + string.Join("&", _queryParameters); - } - // Create HTTP transport objects - var _httpRequest = new HttpRequestMessage(); - HttpResponseMessage _httpResponse = null; - _httpRequest.Method = new HttpMethod("POST"); - _httpRequest.RequestUri = new System.Uri(_url); - // Set Headers - if (Client.GenerateClientRequestId != null && Client.GenerateClientRequestId.Value) - { - _httpRequest.Headers.TryAddWithoutValidation("x-ms-client-request-id", System.Guid.NewGuid().ToString()); - } - if (Client.AcceptLanguage != null) - { - if (_httpRequest.Headers.Contains("accept-language")) - { - _httpRequest.Headers.Remove("accept-language"); - } - _httpRequest.Headers.TryAddWithoutValidation("accept-language", Client.AcceptLanguage); - } - - - if (customHeaders != null) - { - foreach(var _header in customHeaders) - { - if (_httpRequest.Headers.Contains(_header.Key)) - { - _httpRequest.Headers.Remove(_header.Key); - } - _httpRequest.Headers.TryAddWithoutValidation(_header.Key, _header.Value); - } - } - - // Serialize Request - string _requestContent = null; - if(blobDetails != null) - { - _requestContent = Rest.Serialization.SafeJsonConvert.SerializeObject(blobDetails, Client.SerializationSettings); - _httpRequest.Content = new StringContent(_requestContent, System.Text.Encoding.UTF8); - _httpRequest.Content.Headers.ContentType =System.Net.Http.Headers.MediaTypeHeaderValue.Parse("application/json; charset=utf-8"); - } - // Set Credentials - if (Client.Credentials != null) - { - cancellationToken.ThrowIfCancellationRequested(); - await Client.Credentials.ProcessHttpRequestAsync(_httpRequest, cancellationToken).ConfigureAwait(false); - } - // Send Request - if (_shouldTrace) - { - ServiceClientTracing.SendRequest(_invocationId, _httpRequest); - } - cancellationToken.ThrowIfCancellationRequested(); - _httpResponse = await Client.HttpClient.SendAsync(_httpRequest, cancellationToken).ConfigureAwait(false); - if (_shouldTrace) - { - ServiceClientTracing.ReceiveResponse(_invocationId, _httpResponse); - } - HttpStatusCode _statusCode = _httpResponse.StatusCode; - cancellationToken.ThrowIfCancellationRequested(); - string _responseContent = null; - if ((int)_statusCode != 200) - { - var ex = new RestErrorException(string.Format("Operation returned an invalid status code '{0}'", _statusCode)); - try - { - _responseContent = await _httpResponse.Content.ReadAsStringAsync().ConfigureAwait(false); - RestError _errorBody = Rest.Serialization.SafeJsonConvert.DeserializeObject(_responseContent, Client.DeserializationSettings); - if (_errorBody != null) - { - ex.Body = _errorBody; - } - } - catch (JsonException) - { - // Ignore the exception - } - ex.Request = new HttpRequestMessageWrapper(_httpRequest, _requestContent); - ex.Response = new HttpResponseMessageWrapper(_httpResponse, _responseContent); - if (_shouldTrace) - { - ServiceClientTracing.Error(_invocationId, ex); - } - _httpRequest.Dispose(); - if (_httpResponse != null) - { - _httpResponse.Dispose(); - } - throw ex; - } - // Create Result - var _result = new AzureOperationResponse(); - _result.Request = _httpRequest; - _result.Response = _httpResponse; - if (_httpResponse.Headers.Contains("x-ms-request-id")) - { - _result.RequestId = _httpResponse.Headers.GetValues("x-ms-request-id").FirstOrDefault(); - } - // Deserialize Response - if ((int)_statusCode == 200) - { - _responseContent = await _httpResponse.Content.ReadAsStringAsync().ConfigureAwait(false); - try - { - _result.Body = Rest.Serialization.SafeJsonConvert.DeserializeObject(_responseContent, Client.DeserializationSettings); - } - catch (JsonException ex) - { - _httpRequest.Dispose(); - if (_httpResponse != null) - { - _httpResponse.Dispose(); - } - throw new SerializationException("Unable to deserialize the response.", _responseContent, ex); - } - } - if (_shouldTrace) - { - ServiceClientTracing.Exit(_invocationId, _result); - } - return _result; - } - - } -} diff --git a/src/Azure/Azure.Quantum.Client/generated/StorageOperationsExtensions.cs b/src/Azure/Azure.Quantum.Client/generated/StorageOperationsExtensions.cs deleted file mode 100644 index a01a20f0eb9..00000000000 --- a/src/Azure/Azure.Quantum.Client/generated/StorageOperationsExtensions.cs +++ /dev/null @@ -1,63 +0,0 @@ -// -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT License. See License.txt in the project root for -// license information. -// -// Code generated by Microsoft (R) AutoRest Code Generator. -// Changes may cause incorrect behavior and will be lost if the code is -// regenerated. -// - -namespace Microsoft.Azure.Quantum.Client -{ - using Microsoft.Rest; - using Microsoft.Rest.Azure; - using Models; - using System.Threading; - using System.Threading.Tasks; - - /// - /// Extension methods for StorageOperations. - /// - public static partial class StorageOperationsExtensions - { - /// - /// Gets a URL with SAS token for a container/blob in the storage account - /// associated with the workspace. The SAS URL can be used to upload job input - /// and/or download job output. - /// - /// - /// The operations group for this extension method. - /// - /// - /// The details (name and container) of the blob to store or download data. - /// - public static SasUriResponse SasUri(this IStorageOperations operations, BlobDetails blobDetails) - { - return operations.SasUriAsync(blobDetails).GetAwaiter().GetResult(); - } - - /// - /// Gets a URL with SAS token for a container/blob in the storage account - /// associated with the workspace. The SAS URL can be used to upload job input - /// and/or download job output. - /// - /// - /// The operations group for this extension method. - /// - /// - /// The details (name and container) of the blob to store or download data. - /// - /// - /// The cancellation token. - /// - public static async Task SasUriAsync(this IStorageOperations operations, BlobDetails blobDetails, CancellationToken cancellationToken = default(CancellationToken)) - { - using (var _result = await operations.SasUriWithHttpMessagesAsync(blobDetails, null, cancellationToken).ConfigureAwait(false)) - { - return _result.Body; - } - } - - } -} diff --git a/src/Quantum.Development.Kit/Microsoft.Quantum.Development.Kit.nuspec b/src/Quantum.Development.Kit/Microsoft.Quantum.Development.Kit.nuspec index de6a81b711b..b3f2dc492ef 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 03714fb820f..dba0c6256c8 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/Simulation/EntryPointDriver.Tests/Tests.fs b/src/Simulation/EntryPointDriver.Tests/Tests.fs index 9ca3ad61330..b5b9837b9d6 100644 --- a/src/Simulation/EntryPointDriver.Tests/Tests.fs +++ b/src/Simulation/EntryPointDriver.Tests/Tests.fs @@ -192,7 +192,7 @@ let private testWithTarget defaultTarget = |> testWithConstants /// Standard command-line arguments for the "submit" command without specifying a target. -let private submitWithoutTarget = +let private submitWithoutTargetAndLocation = [ "submit" "--subscription" "mySubscription" @@ -201,6 +201,10 @@ let private submitWithoutTarget = "--workspace" "myWorkspace" ] + +/// Standard command-line arguments for the "submit" command without specifying a target. +let private submitWithoutTarget = submitWithoutTargetAndLocation @ ["--location"; "myLocation"] + /// Standard command-line arguments for the "submit" command using the "test.nothing" target. let private submitWithNothingTarget = submitWithoutTarget @ ["--target"; "test.nothing"] @@ -562,9 +566,8 @@ let ``Submit uses default values`` () = Workspace: myWorkspace Target: test.nothing Storage: - AAD Token: Base URI: - Location: + Location: myLocation Job Name: Shots: 500 Output: FriendlyUri @@ -582,9 +585,8 @@ let ``Submit uses default values with default target`` () = Workspace: myWorkspace Target: test.nothing Storage: - AAD Token: Base URI: - Location: + Location: myLocation Job Name: Shots: 500 Output: FriendlyUri @@ -602,8 +604,6 @@ let ``Submit allows overriding default values`` () = "myStorage" "--aad-token" "myToken" - "--base-uri" - "myBaseUri" "--job-name" "myJobName" "--shots" @@ -614,9 +614,8 @@ let ``Submit allows overriding default values`` () = Workspace: myWorkspace Target: test.nothing Storage: myStorage - AAD Token: myToken - Base URI: myBaseUri - Location: + Base URI: + Location: myLocation Job Name: myJobName Shots: 750 Output: FriendlyUri @@ -624,7 +623,40 @@ let ``Submit allows overriding default values`` () = Verbose: True https://www.example.com/00000000-0000-0000-0000-0000000000000" - + +[] +let ``Submit extracts the location from a quantum endpoint`` () = + let given = test "Returns Unit" + given (submitWithoutTargetAndLocation @ [ + "--verbose" + "--storage" + "myStorage" + "--aad-token" + "myToken" + "--base-uri" + "https://westus.quantum.microsoft.com/" + "--job-name" + "myJobName" + "--shots" + "750" + "--target" + "test.nothing" + ]) + |> yields "Subscription: mySubscription + Resource Group: myResourceGroup + Workspace: myWorkspace + Target: test.nothing + Storage: myStorage + Base URI: https://westus.quantum.microsoft.com/ + Location: westus + Job Name: myJobName + Shots: 750 + Output: FriendlyUri + Dry Run: False + Verbose: True + + https://www.example.com/00000000-0000-0000-0000-0000000000000" + [] let ``Submit allows overriding default values with default target`` () = let given = testWithTarget "foo.target" "Returns Unit" @@ -634,8 +666,6 @@ let ``Submit allows overriding default values with default target`` () = "myStorage" "--aad-token" "myToken" - "--base-uri" - "myBaseUri" "--job-name" "myJobName" "--shots" @@ -646,9 +676,8 @@ let ``Submit allows overriding default values with default target`` () = Workspace: myWorkspace Target: test.nothing Storage: myStorage - AAD Token: myToken - Base URI: myBaseUri - Location: + Base URI: + Location: myLocation Job Name: myJobName Shots: 750 Output: FriendlyUri @@ -663,27 +692,26 @@ let ``Submit does not allow to include mutually exclusive options`` () = given (submitWithNothingTarget @ [ "--base-uri" "myBaseUri" - "--location" - "myLocation" ]) |> failsWith "Options --base-uri, --location cannot be used together." - + [] let ``Submit allows to include --base-uri option when --location is not present`` () = let given = testWithTarget "foo.target" "Returns Unit" - given (submitWithNothingTarget @ [ + given (submitWithoutTargetAndLocation @ [ "--verbose" "--base-uri" - "myBaseUri" + "http://myBaseUri.foo.com/" + "--target" + "test.nothing" ]) |> yields "Subscription: mySubscription Resource Group: myResourceGroup Workspace: myWorkspace Target: test.nothing Storage: - AAD Token: - Base URI: myBaseUri - Location: + Base URI: http://mybaseuri.foo.com/ + Location: mybaseuri Job Name: Shots: 500 Output: FriendlyUri @@ -697,15 +725,12 @@ let ``Submit allows to include --location option when --base-uri is not present` let given = testWithTarget "foo.target" "Returns Unit" given (submitWithNothingTarget @ [ "--verbose" - "--location" - "myLocation" ]) |> yields "Subscription: mySubscription Resource Group: myResourceGroup Workspace: myWorkspace Target: test.nothing Storage: - AAD Token: Base URI: Location: myLocation Job Name: @@ -719,17 +744,18 @@ let ``Submit allows to include --location option when --base-uri is not present` [] let ``Submit allows spaces for the --location option`` () = let given = test "Returns Unit" - given (submitWithNothingTarget @ [ + given (submitWithoutTargetAndLocation @ [ "--verbose" "--location" "My Location" + "--target" + "test.nothing" ]) |> yields "Subscription: mySubscription Resource Group: myResourceGroup Workspace: myWorkspace Target: test.nothing Storage: - AAD Token: Base URI: Location: My Location Job Name: @@ -739,15 +765,15 @@ let ``Submit allows spaces for the --location option`` () = Verbose: True https://www.example.com/00000000-0000-0000-0000-0000000000000" - + [] -let ``Submit does not allow an invalid value for the --location option`` () = +let ``Submit fails if both --location and --baseUri are missing`` () = let given = test "Returns Unit" - given (submitWithNothingTarget @ [ - "--location" - "my!nv@lidLocation" + given (submitWithoutTargetAndLocation @ [ + "--target" + "test.nothing" ]) - |> failsWith "\"my!nv@lidLocation\" is an invalid value for the --location option." + |> failsWith "Either --location or --base-uri must be provided." [] let ``Submit requires a positive number of shots`` () = @@ -783,7 +809,7 @@ let ``Submit has required options`` () = // Try every possible combination of arguments. The command should succeed only when all of the arguments are // included. - let commandName = List.head submitWithNothingTarget + let commandName = List.head (submitWithoutTargetAndLocation @ ["--target"; "test.nothing"]) let allArgs = submitWithNothingTarget |> List.tail |> List.chunkBySize 2 for args in powerSet allArgs do given (commandName :: List.concat args) @@ -930,6 +956,8 @@ let ``Supports submitting multiple entry points`` () = "myResourceGroup" "--workspace" "myWorkspace" + "--location" + "location" "--target" "test.nothing" ] @@ -951,6 +979,8 @@ let ``Supports submitting multiple entry points with different parameters`` () = "myResourceGroup" "--workspace" "myWorkspace" + "--location" + "location" "--target" "test.nothing" ] diff --git a/src/Simulation/EntryPointDriver/Azure.cs b/src/Simulation/EntryPointDriver/Azure.cs index 9f33f7baec5..ab0813e0cc8 100644 --- a/src/Simulation/EntryPointDriver/Azure.cs +++ b/src/Simulation/EntryPointDriver/Azure.cs @@ -4,6 +4,9 @@ using System; using System.Linq; using System.Threading.Tasks; + +using Azure.Core; + using Microsoft.Azure.Quantum; using Microsoft.Azure.Quantum.Exceptions; using Microsoft.Quantum.Runtime; @@ -35,6 +38,13 @@ public static async Task Submit(EntryPointInfo info, Console.WriteLine(); } + if ((settings.Location is null) && (settings.BaseUri is null)) + { + DisplayWithColor(ConsoleColor.Red, Console.Error, + $"Either --location or --base-uri must be provided."); + return 1; + } + var machine = CreateMachine(settings); if (machine is null) { @@ -208,12 +218,12 @@ public sealed class AzureSettings public string? Subscription { get; set; } /// - /// The resource group name. + /// The Azure Quantum Workspace's resource group name. /// public string? ResourceGroup { get; set; } /// - /// The workspace name. + /// The Azure Quantum Workspace's name. /// public string? Workspace { get; set; } @@ -234,13 +244,14 @@ public sealed class AzureSettings /// /// The base URI of the Azure Quantum endpoint. - /// If both and properties are not null, takes precedence. + /// NOTE: This parameter is deprected, please always use . + /// If both and properties are not null, takes precedence. /// public Uri? BaseUri { get; set; } /// - /// The location to use with the default Azure Quantum endpoint. - /// If both and properties are not null, takes precedence. + /// The Azure Quantum Workspace's location (region). + /// If both and properties are not null, takes precedence. /// public string? Location { get; set; } @@ -269,30 +280,49 @@ public sealed class AzureSettings /// public bool Verbose { get; set; } + /// + /// Print a warning about passing an AAD token. Using this is not supported anymore but we keep + /// the parameter to break any existing clients, like the az cli. + /// Once the known clients are updated we should remove the parameter too. + /// + internal void PrintAadWarning() + { + Console.ForegroundColor = ConsoleColor.Yellow; + + if (!(AadToken is null)) + { + try + { + Console.Error.WriteLine("----------------------------------------------------------------------------"); + Console.Error.WriteLine(" [Warning]"); + Console.Error.WriteLine(" The AadToken parameter is not supported anymore."); + Console.Error.WriteLine(" Take a look at the Azure Identity client library at"); + Console.Error.WriteLine(" https://docs.microsoft.com/en-us/dotnet/api/overview/azure/identity-readme"); + Console.Error.WriteLine(" for new authentication options."); + Console.Error.WriteLine("----------------------------------------------------------------------------"); + } + finally + { + Console.ResetColor(); + } + } + } + /// /// Creates a based on the settings. /// /// The based on the settings. internal Workspace CreateWorkspace() { - if (BaseUri != null) - { - return AadToken is null - ? new Workspace(Subscription, ResourceGroup, Workspace, baseUri: BaseUri) - : new Workspace(Subscription, ResourceGroup, Workspace, AadToken, baseUri: BaseUri); - } - else if (Location != null) - { - return AadToken is null - ? new Workspace(Subscription, ResourceGroup, Workspace, location: NormalizeLocation(Location)) - : new Workspace(Subscription, ResourceGroup, Workspace, AadToken, location: NormalizeLocation(Location)); - } - else - { - return AadToken is null - ? new Workspace(Subscription, ResourceGroup, Workspace, baseUri: null) - : new Workspace(Subscription, ResourceGroup, Workspace, AadToken, baseUri: null); - } + PrintAadWarning(); + + var location = NormalizeLocation(Location ?? ExtractLocation(BaseUri)); + + return new Workspace( + subscriptionId: Subscription, + resourceGroupName: ResourceGroup, + workspaceName: Workspace, + location: location); } public override string ToString() => @@ -302,15 +332,24 @@ public override string ToString() => $"Workspace: {Workspace}", $"Target: {Target}", $"Storage: {Storage}", - $"AAD Token: {AadToken}", $"Base URI: {BaseUri}", - $"Location: {Location}", + $"Location: {Location ?? ExtractLocation(BaseUri)}", $"Job Name: {JobName}", $"Shots: {Shots}", $"Output: {Output}", $"Dry Run: {DryRun}", $"Verbose: {Verbose}"); + internal static string ExtractLocation(Uri? baseUri) + { + if (baseUri is null || !baseUri.IsAbsoluteUri) + { + return ""; + } + + return baseUri.Host.Substring(0, baseUri.Host.IndexOf('.')); + } + internal static string NormalizeLocation(string location) => string.Concat(location.Where(c => !char.IsWhiteSpace(c))).ToLower(); } diff --git a/src/Simulation/EntryPointDriver/Driver.cs b/src/Simulation/EntryPointDriver/Driver.cs index 7997a87db01..796cc4556a0 100644 --- a/src/Simulation/EntryPointDriver/Driver.cs +++ b/src/Simulation/EntryPointDriver/Driver.cs @@ -459,7 +459,6 @@ private Task Submit(ParseResult parseResult, AzureSettings azureSettings, I Workspace = azureSettings.Workspace, Target = DefaultIfShadowed(entryPoint, this.TargetOption, azureSettings.Target), Storage = DefaultIfShadowed(entryPoint, StorageOption, azureSettings.Storage), - AadToken = DefaultIfShadowed(entryPoint, AadTokenOption, azureSettings.AadToken), BaseUri = DefaultIfShadowed(entryPoint, BaseUriOption, azureSettings.BaseUri), Location = DefaultIfShadowed(entryPoint, LocationOption, azureSettings.Location), JobName = DefaultIfShadowed(entryPoint, JobNameOption, azureSettings.JobName), diff --git a/src/Xunit/Microsoft.Quantum.Xunit.nuspec b/src/Xunit/Microsoft.Quantum.Xunit.nuspec index f4be81d5e51..2de3f2c585f 100644 --- a/src/Xunit/Microsoft.Quantum.Xunit.nuspec +++ b/src/Xunit/Microsoft.Quantum.Xunit.nuspec @@ -22,7 +22,7 @@ - + From 96b5fc2ce9e5fcb1bb7c6eec1c68f857b7641792 Mon Sep 17 00:00:00 2001 From: Andres Paz Date: Wed, 9 Jun 2021 13:57:12 -0700 Subject: [PATCH 2/7] Changes to enable Mocks for iq# and integrate with Live tests (#707) --- global.json | 2 +- .../Helpers/Problem.cs | 9 ++- .../Helpers/TestConstants.cs | 15 ---- .../WorkspaceTest.cs | 70 ++++++++++++++--- .../JobManagement/CloudJob.cs | 29 ++++++- .../JobManagement/IWorkspace.cs | 48 ++++++++++-- .../JobManagement/ProviderStatusInfo.cs | 78 +++++++++++++++++++ .../JobManagement/QuotaInfo.cs | 55 ++++++++++++- .../JobManagement/TargetStatusInfo.cs | 62 +++++++++++++++ .../JobManagement/Workspace.cs | 35 ++++++++- .../JobManagement/WorkspaceExtensions.cs | 2 - .../Microsoft.Quantum.Development.Kit.nuspec | 2 +- .../Microsoft.Quantum.CSharpGeneration.fsproj | 2 +- src/Xunit/Microsoft.Quantum.Xunit.nuspec | 2 +- 14 files changed, 360 insertions(+), 51 deletions(-) delete mode 100644 src/Azure/Azure.Quantum.Client.Test/Helpers/TestConstants.cs create mode 100644 src/Azure/Azure.Quantum.Client/JobManagement/ProviderStatusInfo.cs create mode 100644 src/Azure/Azure.Quantum.Client/JobManagement/TargetStatusInfo.cs 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 @@ - + From a6492ff80869540791dc937c79d700f0cad274c8 Mon Sep 17 00:00:00 2001 From: Andres Paz Date: Tue, 15 Jun 2021 21:52:50 -0700 Subject: [PATCH 3/7] Credentials parameter (#721) * Move to CredentialFactory. * Adding support for DeviceCode --- .../CredentialFactoryTests.cs | 74 +++++ .../WorkspaceTest.cs | 8 +- .../Authentication/CredentialFactory.cs | 279 ++++++++++++++++++ .../Exceptions/WorkspaceClientException.cs | 8 +- .../JobManagement/CloudJob.cs | 7 +- .../JobManagement/Workspace.cs | 13 +- ...sts.Microsoft.Quantum.EntryPointDriver.sln | Bin 0 -> 14910 bytes .../EntryPointDriver.Tests/Tests.fs | 16 + src/Simulation/EntryPointDriver/Azure.cs | 62 ++-- src/Simulation/EntryPointDriver/Driver.cs | 16 +- 10 files changed, 433 insertions(+), 50 deletions(-) create mode 100644 src/Azure/Azure.Quantum.Client.Test/CredentialFactoryTests.cs create mode 100644 src/Azure/Azure.Quantum.Client/Authentication/CredentialFactory.cs create mode 100644 src/Simulation/EntryPointDriver.Tests/Tests.Microsoft.Quantum.EntryPointDriver.sln diff --git a/src/Azure/Azure.Quantum.Client.Test/CredentialFactoryTests.cs b/src/Azure/Azure.Quantum.Client.Test/CredentialFactoryTests.cs new file mode 100644 index 00000000000..908224c54ef --- /dev/null +++ b/src/Azure/Azure.Quantum.Client.Test/CredentialFactoryTests.cs @@ -0,0 +1,74 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +using System; + +using Azure.Identity; + +using Microsoft.Azure.Quantum.Authentication; +using Microsoft.VisualStudio.TestTools.UnitTesting; + +namespace Microsoft.Azure.Quantum.Test +{ + [TestClass] + public class CredentialFactoryTests + { + private const string SUBSCRIPTION = "916dfd6d-030c-4bd9-b579-7bb6d1926e97"; + + [DataTestMethod] + [DataRow(CredentialType.Default, typeof(DefaultAzureCredential))] + [DataRow(CredentialType.Environment, typeof(EnvironmentCredential))] + [DataRow(CredentialType.ManagedIdentity, typeof(ManagedIdentityCredential))] + [DataRow(CredentialType.CLI, typeof(AzureCliCredential))] + [DataRow(CredentialType.SharedToken, typeof(SharedTokenCacheCredential))] + [DataRow(CredentialType.VisualStudio, typeof(VisualStudioCredential))] + [DataRow(CredentialType.VisualStudioCode, typeof(VisualStudioCodeCredential))] + [DataRow(CredentialType.Interactive, typeof(InteractiveBrowserCredential))] + [DataRow(CredentialType.DeviceCode, typeof(DeviceCodeCredential))] + public void TestCreateCredential(CredentialType credentialType, Type expectedType) + { + var actual = CredentialFactory.CreateCredential(credentialType); + Assert.IsNotNull(actual); + Assert.AreEqual(expectedType, actual.GetType()); + + // Now test with a specific subscription id: + actual = CredentialFactory.CreateCredential(credentialType, SUBSCRIPTION); + Assert.IsNotNull(actual); + Assert.AreEqual(expectedType, actual.GetType()); + } + + [TestMethod] + public void TestInvalidCredentialType() + { + Assert.ThrowsException(() => CredentialFactory.CreateCredential((CredentialType)9999)); + } + + [TestMethod] + public void TestGetTenantId() + { + var actual = CredentialFactory.GetTenantId(SUBSCRIPTION); + Assert.IsNotNull(actual); + Assert.IsFalse(string.IsNullOrWhiteSpace(actual)); + + var actual2 = CredentialFactory.GetTenantId(SUBSCRIPTION); + Assert.AreEqual(actual, actual2); + } + + [DataTestMethod] + [DataRow(null, null)] + [DataRow("", null)] + [DataRow("some random string", null)] + [DataRow("string,with,random,values", null)] + [DataRow("string=with,random=,key=values", null)] + [DataRow("string=with,random=,authorization_uri=", null)] + [DataRow("string=with,invalid=authorization_uri,authorization_uri=some-random-value", null)] + [DataRow("string=with,invalid=authorization_uri,authorization_uri=http://foo.bar.com/some-random-value", null)] + [DataRow("string=missing,tenant_id=authorization_uri,authorization_uri=\"http://foo.bar.com/", null)] + [DataRow("authorization_uri=\"https://login.microsoftonline.com/tenantId\",key1=value1s,etc...", "tenantId")] + public void TestExtractTenantIdFromBearer(string bearer, string expected) + { + var actual = CredentialFactory.ExtractTenantIdFromBearer(bearer); + Assert.AreEqual(expected, actual); + } + } +} diff --git a/src/Azure/Azure.Quantum.Client.Test/WorkspaceTest.cs b/src/Azure/Azure.Quantum.Client.Test/WorkspaceTest.cs index bb5fb81f7ba..e9c87e7ac03 100644 --- a/src/Azure/Azure.Quantum.Client.Test/WorkspaceTest.cs +++ b/src/Azure/Azure.Quantum.Client.Test/WorkspaceTest.cs @@ -29,8 +29,9 @@ public class WorkspaceTest * 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: +We'll also try to authenticate with Azure using an instance of DefaultCredential. See https://docs.microsoft.com/en-us/dotnet/api/overview/azure/identity-readme#authenticate-the-client +for details. Tests will be marked as Inconclusive if the pre-reqs are not correctly setup."; @@ -197,12 +198,15 @@ private IWorkspace GetLiveWorkspace() var options = new QuantumJobClientOptions(); options.Diagnostics.ApplicationId = "ClientTests"; + var credential = Authentication.CredentialFactory.CreateCredential(Authentication.CredentialType.Default); + return new Workspace( 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); + options: options, + credential: credential); } private static JobDetails CreateJobDetails(string jobId, string containerUri = null, string inputUri = null) diff --git a/src/Azure/Azure.Quantum.Client/Authentication/CredentialFactory.cs b/src/Azure/Azure.Quantum.Client/Authentication/CredentialFactory.cs new file mode 100644 index 00000000000..ce0f3347e04 --- /dev/null +++ b/src/Azure/Azure.Quantum.Client/Authentication/CredentialFactory.cs @@ -0,0 +1,279 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +#nullable enable + +namespace Microsoft.Azure.Quantum.Authentication +{ + using System; + using System.Collections.Generic; + using System.Linq; + using System.Net.Http; + + using global::Azure.Core; + using global::Azure.Identity; + + /// + /// The enumeration of supported Credential Classes supported out of the box for + /// authentication in Azure Quantum. + /// NOTE: For more information + /// about authentication with Azure services and the different Credential types see + /// https://docs.microsoft.com/en-us/dotnet/api/overview/azure/identity-readme. + /// + public enum CredentialType + { + /// + /// Provides a simplified authentication experience to quickly start developing applications run in the Azure cloud. + /// See: https://docs.microsoft.com/en-us/dotnet/api/azure.identity.defaultazurecredential + /// + Default, + + /// + /// Authenticates a service principal or user via credential information specified in environment variables. + /// See: https://docs.microsoft.com/en-us/dotnet/api/azure.identity.environmentcredential + /// + Environment, + + /// + /// Authenticates the managed identity of an azure resource. + /// See: https://docs.microsoft.com/en-us/dotnet/api/azure.identity.managedidentitycredential + /// + ManagedIdentity, + + /// + /// Authenticate in a development environment with the Azure CLI. + /// See https://docs.microsoft.com/en-us/dotnet/api/azure.identity.azureclicredential + /// + CLI, + + /// + /// Authenticate using tokens in the local cache shared between Microsoft applications. + /// See: https://docs.microsoft.com/en-us/dotnet/api/azure.identity.sharedtokencachecredential + /// + SharedToken, + + /// + /// Authenticate using data from Visual Studio. + /// See: https://docs.microsoft.com/en-us/dotnet/api/azure.identity.visualstudiocredential + /// + VisualStudio, + + /// + /// Authenticate in a development environment with Visual Studio Code. + /// See: https://docs.microsoft.com/en-us/dotnet/api/azure.identity.visualstudiocodecredential + /// + VisualStudioCode, + + /// + /// A TokenCredential implementation which launches the system default browser to interactively authenticate a user, + /// and obtain an access token. The browser will only be launched to authenticate the user once, + /// then will silently acquire access tokens through the users refresh token as long as it's valid. + /// See: https://docs.microsoft.com/en-us/dotnet/api/azure.identity.interactivebrowsercredential + /// + Interactive, + + /// + /// A TokenCredential implementation which authenticates a user using the device code flow, + /// and provides access tokens for that user account. + /// See: https://docs.microsoft.com/en-us/dotnet/api/azure.identity.devicecodecredential + /// + DeviceCode, + } + + public static class CredentialFactory + { + // Used to fetch the tenantId automatically from ARM + private static readonly HttpClient Client = new HttpClient(); + + // Used to catch all the TenantIds: + private static readonly Dictionary TenantIds = new Dictionary(); + + public static TokenCredential CreateCredential(CredentialType credentialType, string? subscriptionId = null) => credentialType switch + { + CredentialType.SharedToken => CreateCredential(credentialType, () => SharedTokenOptions(subscriptionId)), + CredentialType.VisualStudio => CreateCredential(credentialType, () => VisualStudioOptions(subscriptionId)), + CredentialType.VisualStudioCode => CreateCredential(credentialType, () => VisualStudioCodeOptions(subscriptionId)), + CredentialType.Interactive => CreateCredential(credentialType, () => InteractiveOptions(subscriptionId)), + CredentialType.DeviceCode => CreateCredential(credentialType, () => DeviceCodeOptions(subscriptionId)), + CredentialType.Default => CreateCredential(credentialType, () => DefaultOptions(subscriptionId)), + _ => CreateCredential(credentialType, () => DefaultOptions(subscriptionId)), + }; + + /// + /// Creates an instance of TokenCredential that corresponds to the given . + /// It creates an instance of the Credential Class with default parameters. + /// + /// The type of Credential Class to create. + /// A configuration method for the corresponding credential options (not used for Environment, ManagedIdentity or CLI credentials). + /// An instance of TokenCredential for the corresponding value. + public static TokenCredential CreateCredential(CredentialType credentialType, Func options) => credentialType switch + { + CredentialType.Environment => new EnvironmentCredential(), + CredentialType.ManagedIdentity => new ManagedIdentityCredential(), + CredentialType.CLI => new AzureCliCredential(), + CredentialType.DeviceCode => new DeviceCodeCredential(options: options?.Invoke() as DeviceCodeCredentialOptions), + CredentialType.SharedToken => new SharedTokenCacheCredential(options: options?.Invoke() as SharedTokenCacheCredentialOptions), + CredentialType.VisualStudio => new VisualStudioCredential(options: options?.Invoke() as VisualStudioCredentialOptions), + CredentialType.VisualStudioCode => new VisualStudioCodeCredential(options: options?.Invoke() as VisualStudioCodeCredentialOptions), + CredentialType.Interactive => new InteractiveBrowserCredential(options: options?.Invoke() as InteractiveBrowserCredentialOptions), + CredentialType.Default => new DefaultAzureCredential(options: options?.Invoke() as DefaultAzureCredentialOptions), + _ => throw new ArgumentException($"Credentials of type {credentialType} are not supported.") + }; + + /// + /// Returns an DefaultAzureCredentialOptions, populated with the TenantId for the given subscription. + /// We als disabilitate VisualStudio credentials, since they don't currently work with Azure Quantum. + /// + /// An subscription Id. + /// A new instance of InteractiveBrowserCredentialOptions with the TenantId populated + public static DefaultAzureCredentialOptions DefaultOptions(string? subscriptionid) + { + string? tenantId = GetTenantId(subscriptionid); + + return new DefaultAzureCredentialOptions + { + // Disable VS credentials until https://devdiv.visualstudio.com/DevDiv/_workitems/edit/1332071 is fixed: + ExcludeVisualStudioCredential = true, + ExcludeInteractiveBrowserCredential = false, + + InteractiveBrowserTenantId = tenantId, + SharedTokenCacheTenantId = tenantId, + VisualStudioCodeTenantId = tenantId, + VisualStudioTenantId = tenantId, + }; + } + + /// + /// Returns an InteractiveBrowserCredentialOptions, populated with the TenantId for the given subscription. + /// + /// An subscription Id. + /// A new instance of InteractiveBrowserCredentialOptions with the TenantId populated + public static InteractiveBrowserCredentialOptions InteractiveOptions(string? subscriptionid) => + new InteractiveBrowserCredentialOptions + { + TenantId = GetTenantId(subscriptionid), + }; + + /// + /// Returns an VisualStudioCodeCredentialOptions, populated with the TenantId for the given subscription. + /// + /// An subscription Id. + /// A new instance of InteractiveBrowserCredentialOptions with the TenantId populated + public static VisualStudioCodeCredentialOptions VisualStudioCodeOptions(string? subscriptionid) => + new VisualStudioCodeCredentialOptions + { + TenantId = GetTenantId(subscriptionid), + }; + + /// + /// Returns an VisualStudioCredentialOptions, populated with the TenantId for the given subscription. + /// + /// An subscription Id. + /// A new instance of InteractiveBrowserCredentialOptions with the TenantId populated + public static VisualStudioCredentialOptions VisualStudioOptions(string? subscriptionid) => + new VisualStudioCredentialOptions + { + TenantId = GetTenantId(subscriptionid), + }; + + /// + /// Returns an SharedTokenCacheCredentialOptions, populated with the TenantId for the given subscription. + /// + /// An subscription Id. + /// A new instance of InteractiveBrowserCredentialOptions with the TenantId populated + public static SharedTokenCacheCredentialOptions SharedTokenOptions(string? subscriptionid) => + new SharedTokenCacheCredentialOptions + { + TenantId = GetTenantId(subscriptionid), + }; + + /// + /// Returns an VisualStudioCodeCredentialOptions, populated with the TenantId for the given subscription. + /// + /// An subscription Id. + /// A new instance of InteractiveBrowserCredentialOptions with the TenantId populated + public static DeviceCodeCredentialOptions DeviceCodeOptions(string? subscriptionid) => + new DeviceCodeCredentialOptions + { + TenantId = GetTenantId(subscriptionid), + }; + + /// + /// This gnarly piece of code is how we get the guest tenant + /// authority associated with the subscription. + /// We make a unauthenticated request to ARM and extract the tenant + /// authority from the WWW-Authenticate header in the response. + /// + /// The subscriptionId. + /// The tenantId for the given subscription; null if it can be found or for a null subscription. + public static string? GetTenantId(string? subscriptionId) + { + if (subscriptionId == null) + { + return null; + } + + if (TenantIds.TryGetValue(subscriptionId, out string? tenantId)) + { + return tenantId; + } + + try + { + string url = $"https://management.azure.com/subscriptions/{subscriptionId}?api-version=2020-01-01"; + HttpResponseMessage response = Client.GetAsync(url).Result; + var header = response + .Headers + .WwwAuthenticate + .FirstOrDefault(v => v.Scheme == "Bearer") + ?.Parameter; + + tenantId = ExtractTenantIdFromBearer(header); + TenantIds[subscriptionId] = tenantId; + + return tenantId; + } + catch + { + return null; + } + } + + /// + /// Here we parse WWW-Authenticate header in the response to match the tenant id. + /// The header is of the form: + /// Bearer authorization_uri="https://login.microsoftonline.com/tenantId",key1=value1s,etc... + /// + /// The value of the Bearer in the WWWAuthenticate header + /// The tenant-id, or null if it can't find it. + public static string? ExtractTenantIdFromBearer(string? bearer) + { + if (bearer == null) + { + return null; + } + + // Split the key=value comma seperated list and look for the "authorization_uri" key: + var auth_uri = bearer + .Split(',') + .Select(kv => kv.Split('=', 2)) + .FirstOrDefault(pair => pair[0] == "authorization_uri")?[1]; + + // If found an authorization_uri, find the tenant id from a URL surrounded by quotes, i.e.: + // "https://login.microsoftonline.com/tenantId" + if (auth_uri != null && auth_uri.StartsWith('"') && auth_uri.EndsWith('"')) + { + var id = auth_uri + [1 .. ^1] + [auth_uri.LastIndexOf('/') .. ]; + + return id; + } + else + { + return null; + } + + } + } +} diff --git a/src/Azure/Azure.Quantum.Client/Exceptions/WorkspaceClientException.cs b/src/Azure/Azure.Quantum.Client/Exceptions/WorkspaceClientException.cs index 9c70b86fdcd..8f5c2327b89 100644 --- a/src/Azure/Azure.Quantum.Client/Exceptions/WorkspaceClientException.cs +++ b/src/Azure/Azure.Quantum.Client/Exceptions/WorkspaceClientException.cs @@ -12,10 +12,6 @@ public class WorkspaceClientException : AzureQuantumException { private const string BaseMessage = "An exception related to the Azure workspace client occurred"; - public string ErrorCode { get; } - - public int Status { get; } - /// /// Initializes a new instance of the class with a default error message. /// @@ -82,6 +78,10 @@ public WorkspaceClientException( } } + public string ErrorCode { get; } + + public int Status { get; } + /// /// Formats the contents of the inner exception in so it can be included in the /// exception message and presented in an informative way. diff --git a/src/Azure/Azure.Quantum.Client/JobManagement/CloudJob.cs b/src/Azure/Azure.Quantum.Client/JobManagement/CloudJob.cs index 05c92de3e0b..aa7c5eb30fd 100644 --- a/src/Azure/Azure.Quantum.Client/JobManagement/CloudJob.cs +++ b/src/Azure/Azure.Quantum.Client/JobManagement/CloudJob.cs @@ -142,12 +142,7 @@ public async Task WaitForCompletion(int pollIntervalMilliseconds = 500, Cancella private Uri GenerateUri() { - if (!(this.Workspace is Workspace cloudWorkspace)) - { - throw new NotSupportedException($"{typeof(CloudJob)}'s Workspace is not of type {typeof(Workspace)} and does not have enough data to generate URI"); - } - - var uriStr = $"https://ms.portal.azure.com/#@microsoft.onmicrosoft.com/resource/subscriptions/{cloudWorkspace.SubscriptionId}/resourceGroups/{cloudWorkspace.ResourceGroupName}/providers/Microsoft.Quantum/Workspaces/{cloudWorkspace.WorkspaceName}/job_management?microsoft_azure_quantum_jobid={Id}"; + var uriStr = $"https://portal.azure.com/#@microsoft.onmicrosoft.com/resource/subscriptions/{Workspace.SubscriptionId}/resourceGroups/{Workspace.ResourceGroupName}/providers/Microsoft.Quantum/Workspaces/{Workspace.WorkspaceName}/job_management?microsoft_azure_quantum_jobid={Id}"; return new Uri(uriStr); } } diff --git a/src/Azure/Azure.Quantum.Client/JobManagement/Workspace.cs b/src/Azure/Azure.Quantum.Client/JobManagement/Workspace.cs index b8eb8349377..e6c9a7e1f97 100644 --- a/src/Azure/Azure.Quantum.Client/JobManagement/Workspace.cs +++ b/src/Azure/Azure.Quantum.Client/JobManagement/Workspace.cs @@ -15,6 +15,7 @@ namespace Microsoft.Azure.Quantum using global::Azure.Quantum.Jobs; using global::Azure.Quantum.Jobs.Models; + using Microsoft.Azure.Quantum.Authentication; using Microsoft.Azure.Quantum.Exceptions; using Microsoft.Azure.Quantum.Utility; @@ -48,17 +49,7 @@ public Workspace( Ensure.NotNullOrWhiteSpace(location, nameof(location)); // Optional parameters: - 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); - } - + credential ??= CredentialFactory.CreateCredential(CredentialType.Default, subscriptionId); options ??= new QuantumJobClientOptions(); this.ResourceGroupName = resourceGroupName; diff --git a/src/Simulation/EntryPointDriver.Tests/Tests.Microsoft.Quantum.EntryPointDriver.sln b/src/Simulation/EntryPointDriver.Tests/Tests.Microsoft.Quantum.EntryPointDriver.sln new file mode 100644 index 0000000000000000000000000000000000000000..00e2b89cd813d2248845a802044c95f1a143346b GIT binary patch literal 14910 zcmdU$T~Fgi6o%(^EA>CDxY|lh6vzijrC#KdR%$C2w(Si!3jxXs$QCcFQrf@X_Ibw% zcEWh##$%(%N-`dgXU?4WJ!j5*}_vV_rWAg6J zl)Ra}n~{4@p6+kSnUK0R=jLmho>A8gZ^reUa~D08+%%)nlH?uZS@0ho>BUZ z*)fl%XZmKB?_J6?skug~!|y(+7O9Tuk{Xz?X>(tjmml-I&%MgExjN==YWoU#zU8xH zp7*g%`9JB!9j({RTie%L+s_;BhxF%)C(7Be{n{b#8*^fg$o*(_bz**}XTQC*rsAv?5~3E2S{Hpm;0*R@Y4Y!9+W zOBNSnDtj&uPt~3e%PiTl5fp9%EUX7g1JLU7+XopTC{H*h)#qu0wDP+A?t$4pp9z=l z`y1J&>giLDI3Vkw576x` zpSzTumW8Ch)qDuAlfBMZ>hi+EP=0u5^(s``-1oT`sy(zWZFkYH#=v-l8-a|*wIQ1B zkSl#};)TmW`W>i6jFb^0r)#8LMP1{@CvbjX?3m%5bvNSv%6zu?mxKDcR_i9|$4M$3 zrcYUHXn^WIz3;#Qjm9-FmQ9Z=8~UVWh4KYs>j4^+8I!wfxiGYB8Ij(E4b{b3+rfZ* zcE6rS?&zxv(u$z=z)lfZjc*~TRLfp%Qi{D4%f`N5DDU!2cDoA;+TguiNV{Sq#lAx< zOrHJ>i(f9I`NQ()S&lzgCqq6qEX%%#F4xt)+`p`tK-R61q6t&PgPI+ml`F<5I*`B6 z=quJ}rlj(k{q!iQabgH>wv91wQ!zo=F^eNf-T)0Y$B<%#N!&``uI z`vHu9wZ8o$YY>X(%kkgY?d8Ka`~9p~ZD=vn94%f+QLHAU;#f^{Ypqgg9jc4v%EtAp zOLHrwC%iG<2-la3)s{y%`N*gwL|rZA!_xI;mURyBs0~oiOloYg)4W%amV9xSXR=QD zX00V^%++|R@m6*{CZ*ZewvDRq@Va;A5_^p2Y2Vvfm#p(*9)*>KYP#hS?O)g#-xYGi z(Ne^Uic6Y&$fs-9{L@ua78AOlsd06WD_;B58jR*zVuIG66<=wsS)8eXyH?fWb!qWL z)~p%YIzN4YSDcws^n7jCO<$RG&M`XC`gg32H$*?n>+6b3ZmrfX89&pjBZu=f%?nCs z%{_Wr@z&a};_Bs^w6gb#ODLRD*QNOj`ET*quj$F5`FXxxp8b4L!x1%T#s58Ro@dnJ zFB8|KxzyC7jkuIgTg!V0o9Rm9)~8HugeYL+aAcv7)>TwU<#QFRPJpb6$p%ZynZI&QYR+m2W${B9wr956%C8JA^ zdHiKa`TV#Q_m@@5>e8pbe~JBs+XE_(msQE=(qkT8hLq2bTX9}iDXUAL{{AJr47X!d z9xtns(WS>cybLLyAGhMXtWs8&KK=bmco}YAtUO*;C8JA^d3YI8K0j{7d0C~bE`9p@ zm+&&&E?;@PtV%|g9`o=rqF-~{%W(UtC@l8gqPuVh|A+;RWiEtn1`1k z<@4iKoR?L~>e8pbe+e(c?N^t_%c^8_=`jy4L(1pJtvD~Ml+~qAfB#}$7T>{NJ*5!u z=*K6=bSgssEm-`M&wsArJ4#O}zgpu@oP$t){BJ;=*!qDod&RK>C z2~O?)xX!mcES+pf_ribTU{(GDyGJ~o%m2u^6P*dw|3q|lMW+h&-;kC)t)%mYI(4WM fOgdezl+LZ_UtT)rG$P%#_xi6;ehi;$N$>s#n6Iw1 literal 0 HcmV?d00001 diff --git a/src/Simulation/EntryPointDriver.Tests/Tests.fs b/src/Simulation/EntryPointDriver.Tests/Tests.fs index b5b9837b9d6..9d75e67c648 100644 --- a/src/Simulation/EntryPointDriver.Tests/Tests.fs +++ b/src/Simulation/EntryPointDriver.Tests/Tests.fs @@ -568,6 +568,7 @@ let ``Submit uses default values`` () = Storage: Base URI: Location: myLocation + Credential: Default Job Name: Shots: 500 Output: FriendlyUri @@ -587,6 +588,7 @@ let ``Submit uses default values with default target`` () = Storage: Base URI: Location: myLocation + Credential: Default Job Name: Shots: 500 Output: FriendlyUri @@ -608,6 +610,8 @@ let ``Submit allows overriding default values`` () = "myJobName" "--shots" "750" + "--credential" + "cli" ]) |> yields "Subscription: mySubscription Resource Group: myResourceGroup @@ -616,6 +620,7 @@ let ``Submit allows overriding default values`` () = Storage: myStorage Base URI: Location: myLocation + Credential: CLI Job Name: myJobName Shots: 750 Output: FriendlyUri @@ -637,6 +642,8 @@ let ``Submit extracts the location from a quantum endpoint`` () = "https://westus.quantum.microsoft.com/" "--job-name" "myJobName" + "--credential" + "VisualStudio" "--shots" "750" "--target" @@ -649,6 +656,7 @@ let ``Submit extracts the location from a quantum endpoint`` () = Storage: myStorage Base URI: https://westus.quantum.microsoft.com/ Location: westus + Credential: VisualStudio Job Name: myJobName Shots: 750 Output: FriendlyUri @@ -664,6 +672,8 @@ let ``Submit allows overriding default values with default target`` () = "--verbose" "--storage" "myStorage" + "--credential" + "Interactive" "--aad-token" "myToken" "--job-name" @@ -678,6 +688,7 @@ let ``Submit allows overriding default values with default target`` () = Storage: myStorage Base URI: Location: myLocation + Credential: Interactive Job Name: myJobName Shots: 750 Output: FriendlyUri @@ -712,6 +723,7 @@ let ``Submit allows to include --base-uri option when --location is not present` Storage: Base URI: http://mybaseuri.foo.com/ Location: mybaseuri + Credential: Default Job Name: Shots: 500 Output: FriendlyUri @@ -733,6 +745,7 @@ let ``Submit allows to include --location option when --base-uri is not present` Storage: Base URI: Location: myLocation + Credential: Default Job Name: Shots: 500 Output: FriendlyUri @@ -758,6 +771,7 @@ let ``Submit allows spaces for the --location option`` () = Storage: Base URI: Location: My Location + Credential: Default Job Name: Shots: 500 Output: FriendlyUri @@ -870,6 +884,7 @@ let ``Shows help text for submit command`` () = --resource-group (REQUIRED) The resource group name. --workspace (REQUIRED) The workspace name. --target (REQUIRED) The target device ID. + --credential The type of credential to use to authenticate with Azure. --storage The storage account connection string. --aad-token The Azure Active Directory authentication token. --base-uri The base URI of the Azure Quantum endpoint. @@ -899,6 +914,7 @@ let ``Shows help text for submit command with default target`` () = --resource-group (REQUIRED) The resource group name. --workspace (REQUIRED) The workspace name. --target The target device ID. + --credential The type of credential to use to authenticate with Azure. --storage The storage account connection string. --aad-token The Azure Active Directory authentication token. --base-uri The base URI of the Azure Quantum endpoint. diff --git a/src/Simulation/EntryPointDriver/Azure.cs b/src/Simulation/EntryPointDriver/Azure.cs index ab0813e0cc8..4cbd1b28c1d 100644 --- a/src/Simulation/EntryPointDriver/Azure.cs +++ b/src/Simulation/EntryPointDriver/Azure.cs @@ -3,11 +3,14 @@ using System; using System.Linq; +using System.Threading; using System.Threading.Tasks; using Azure.Core; +using Azure.Identity; using Microsoft.Azure.Quantum; +using Microsoft.Azure.Quantum.Authentication; using Microsoft.Azure.Quantum.Exceptions; using Microsoft.Quantum.Runtime; using Microsoft.Quantum.Simulation.Common.Exceptions; @@ -212,6 +215,22 @@ public enum OutputFormat /// public sealed class AzureSettings { + private class AADTokenCredential : TokenCredential + { + AccessToken Token { get; } + + public AADTokenCredential(string token) + { + Token = new AccessToken(token, DateTime.Now.AddMinutes(5)); + } + + public override AccessToken GetToken(TokenRequestContext requestContext, CancellationToken cancellationToken) => + Token; + + public override ValueTask GetTokenAsync(TokenRequestContext requestContext, CancellationToken cancellationToken) => + new ValueTask(Token); + } + /// /// The subscription ID. /// @@ -242,6 +261,14 @@ public sealed class AzureSettings /// public string? AadToken { get; set; } + /// + /// The type of Credentials to use to authenticate with Azure. For more information + /// about authentication with Azure services see: https://docs.microsoft.com/en-us/dotnet/api/overview/azure/identity-readme + /// NOTE: If both and properties are specified, takes precedence. + /// If none are provided, then it uses . + /// + public CredentialType? Credential { get; set; } + /// /// The base URI of the Azure Quantum endpoint. /// NOTE: This parameter is deprected, please always use . @@ -280,31 +307,15 @@ public sealed class AzureSettings /// public bool Verbose { get; set; } - /// - /// Print a warning about passing an AAD token. Using this is not supported anymore but we keep - /// the parameter to break any existing clients, like the az cli. - /// Once the known clients are updated we should remove the parameter too. - /// - internal void PrintAadWarning() + internal TokenCredential CreateCredentials() { - Console.ForegroundColor = ConsoleColor.Yellow; - if (!(AadToken is null)) { - try - { - Console.Error.WriteLine("----------------------------------------------------------------------------"); - Console.Error.WriteLine(" [Warning]"); - Console.Error.WriteLine(" The AadToken parameter is not supported anymore."); - Console.Error.WriteLine(" Take a look at the Azure Identity client library at"); - Console.Error.WriteLine(" https://docs.microsoft.com/en-us/dotnet/api/overview/azure/identity-readme"); - Console.Error.WriteLine(" for new authentication options."); - Console.Error.WriteLine("----------------------------------------------------------------------------"); - } - finally - { - Console.ResetColor(); - } + return new AADTokenCredential(AadToken); + } + else + { + return CredentialFactory.CreateCredential(Credential ?? CredentialType.Default, Subscription); } } @@ -314,15 +325,15 @@ internal void PrintAadWarning() /// The based on the settings. internal Workspace CreateWorkspace() { - PrintAadWarning(); - + var credentials = CreateCredentials(); var location = NormalizeLocation(Location ?? ExtractLocation(BaseUri)); return new Workspace( subscriptionId: Subscription, resourceGroupName: ResourceGroup, workspaceName: Workspace, - location: location); + location: location, + credential: credentials); } public override string ToString() => @@ -334,6 +345,7 @@ public override string ToString() => $"Storage: {Storage}", $"Base URI: {BaseUri}", $"Location: {Location ?? ExtractLocation(BaseUri)}", + $"Credential: {Credential}", $"Job Name: {JobName}", $"Shots: {Shots}", $"Output: {Output}", diff --git a/src/Simulation/EntryPointDriver/Driver.cs b/src/Simulation/EntryPointDriver/Driver.cs index 796cc4556a0..1a3943c0f00 100644 --- a/src/Simulation/EntryPointDriver/Driver.cs +++ b/src/Simulation/EntryPointDriver/Driver.cs @@ -14,6 +14,8 @@ using System.Text; using System.Threading.Tasks; +using Microsoft.Azure.Quantum.Authentication; + namespace Microsoft.Quantum.EntryPointDriver { using Validators = ImmutableList>; @@ -47,6 +49,14 @@ public sealed class Driver private static readonly OptionInfo StorageOption = new OptionInfo( ImmutableList.Create("--storage"), default, "The storage account connection string."); + /// + /// The credential option. + /// + private static readonly OptionInfo CredentialOption = new OptionInfo( + ImmutableList.Create("--credential"), + CredentialType.Default, + "The type of credential to use to authenticate with Azure."); + /// /// The AAD token option. /// @@ -417,7 +427,8 @@ private CommandWithValidators CreateSubmitEntryPointCommand(IEntryPoint entryPoi var validators = AddOptionIfAvailable(command, SubscriptionOption) .Concat(AddOptionIfAvailable(command, ResourceGroupOption)) .Concat(AddOptionIfAvailable(command, WorkspaceOption)) - .Concat(AddOptionIfAvailable(command, this.TargetOption)) + .Concat(AddOptionIfAvailable(command, TargetOption)) + .Concat(AddOptionIfAvailable(command, CredentialOption)) .Concat(AddOptionIfAvailable(command, StorageOption)) .Concat(AddOptionIfAvailable(command, AadTokenOption)) .Concat(AddOptionIfAvailable(command, BaseUriOption)) @@ -457,10 +468,11 @@ private Task Submit(ParseResult parseResult, AzureSettings azureSettings, I Subscription = azureSettings.Subscription, ResourceGroup = azureSettings.ResourceGroup, Workspace = azureSettings.Workspace, - Target = DefaultIfShadowed(entryPoint, this.TargetOption, azureSettings.Target), + Target = DefaultIfShadowed(entryPoint, TargetOption, azureSettings.Target), Storage = DefaultIfShadowed(entryPoint, StorageOption, azureSettings.Storage), BaseUri = DefaultIfShadowed(entryPoint, BaseUriOption, azureSettings.BaseUri), Location = DefaultIfShadowed(entryPoint, LocationOption, azureSettings.Location), + Credential = DefaultIfShadowed(entryPoint, CredentialOption, azureSettings.Credential), JobName = DefaultIfShadowed(entryPoint, JobNameOption, azureSettings.JobName), Shots = DefaultIfShadowed(entryPoint, ShotsOption, azureSettings.Shots), Output = DefaultIfShadowed(entryPoint, OutputOption, azureSettings.Output), From 936beb239820da55f9bd66c0a081e6320ecf97a3 Mon Sep 17 00:00:00 2001 From: Andres Paz Date: Wed, 16 Jun 2021 00:39:31 -0700 Subject: [PATCH 4/7] Merge main (#731) * Update API docs with more examples. (#701) Co-authored-by: Stefan J. Wernli * QIR runtime heap alloc and heap free functions removed (#703) * QIR runtime heap alloc and heap free functions removed * Implement Feedback Co-authored-by: Stefan J. Wernli * Add QIR Runtime to bootstrap script (#706) * Increasing the CMAKE required version for QIR (#708) This updates the required version for CMAKE in the CMakeLists.txt files for QIR, since we are now using features not correctly supported on older versions. * Added first version of Qubit manager to QIR (#696) This is the first version of the native Qubit Manager for QIR. Initial unit tests are included. Qubit Manager supports * Allocation and release of qubits including arrays of qubits. * Reuse encouraged/discouraged. Extending capacity enabled/disabled. * Restricted reuse areas and segments to support low-depth circuits. Qubit manager builds and tests are run as part of the qir-runtime-unittests. It is used in the bridge to the full state simulator. Commits: * Added first draft version of Qubit manager to QIR * Review feedback. Rearranged functions, QubitIdType, consts, etc. * include cstring for memcpy * Renamed QubitManager * Adjustment after QubitManager rename * Moved QubitManager.h to public * Using QubitManager, adjusted defaults, minor fixes * Removed unused comment * CR feedback: fixes to types, failures, braces, etc. * CR feedback - updated types, unique_ptr, etc. * Auxilliary stuff is now nested in CQubitmanager * Initializer lists * Updated CRestrictedReuseAreaStack and used iterator. * int32_t, more checks, some refactoring * Refactoring: marking and counting is done in allocate/release qubit id * Deletion of qubit object, protected members for overrides, Is...Qubit renames * Moved allocated marking to listQubitListInSharedArray class * Added comments, mostly status array * Tests, refactoring, replaced IsFreeQubit with IsFreeQubitId Co-authored-by: Dmitry Vasilevsky * Added treating warnings as errors (#718) * Added "QIR Runtime API Design Guidelines" (#711) * Initial add of QIR-RT-API-Design-Guidelines.md * Use of exceptions recommendation (#719) * Co-authored-by: Mathias Soeken * Co-authored-by: DmitryVasilevsky * Auto emulation rewrite step (#514) * First proposal for auto emulation feature. * Deprecation. * Update to newer version. * Successful tests. * Compatibility checks. * Do not change this file. * Signature checking too restrictive. * Add integration test. * Update QDK version. * Incorporate Microsoft/QuantumLibraries#426. * Rename emulation to substitution. * Rename projects and directories. * Packaging. * Fix line-feed problems. * Fix LF. * Adressing reviewer feedback. * Update Sdk version. * Fixes dependency error. Co-authored-by: Mathias Soeken Co-authored-by: Chris Granade Co-authored-by: Stefan J. Wernli * Added compiler warning flags -Wall, -Wextra (#725) * Added -Wall, -Wextra * Add DelaySign.cs to AutoSubstitution project. (#729) * extra sln Co-authored-by: Chris Granade Co-authored-by: Stefan J. Wernli Co-authored-by: Pankaj Patil Co-authored-by: DmitryVasilevsky <60718360+DmitryVasilevsky@users.noreply.github.com> Co-authored-by: Dmitry Vasilevsky Co-authored-by: Robin Kuzmin Co-authored-by: Mathias Soeken Co-authored-by: Mathias Soeken --- Simulation.sln | 60 ++ bootstrap.ps1 | 8 + build/manifest.ps1 | 2 + build/pack.ps1 | 1 + src/Qir/Common/Include/QirUtils.hpp | 7 + src/Qir/Common/Include/SimulatorStub.hpp | 62 +- src/Qir/QIR-RT-API-Design-Guidelines.md | 601 ++++++++++++++++++ src/Qir/Runtime/CMakeLists.txt | 2 +- src/Qir/Runtime/lib/QIR/CMakeLists.txt | 6 +- src/Qir/Runtime/lib/QIR/QubitManager.cpp | 522 +++++++++++++++ src/Qir/Runtime/lib/QIR/arrays.cpp | 2 +- src/Qir/Runtime/lib/QIR/bridge-rt.ll | 11 - src/Qir/Runtime/lib/QIR/callables.cpp | 4 +- src/Qir/Runtime/lib/QIR/utils.cpp | 30 - .../QSharpFoundation/AssertMeasurement.cpp | 2 +- src/Qir/Runtime/lib/Simulators/CMakeLists.txt | 5 +- .../lib/Simulators/FullstateSimulator.cpp | 35 +- .../lib/Simulators/ToffoliSimulator.cpp | 62 +- src/Qir/Runtime/lib/Tracer/tracer-qis.cpp | 6 +- src/Qir/Runtime/lib/Tracer/tracer.cpp | 8 +- src/Qir/Runtime/lib/Tracer/tracer.hpp | 4 +- src/Qir/Runtime/public/QubitManager.hpp | 255 ++++++++ src/Qir/Runtime/unittests/CMakeLists.txt | 1 + src/Qir/Runtime/unittests/QirRuntimeTests.cpp | 8 +- .../Runtime/unittests/QubitManagerTests.cpp | 253 ++++++++ src/Qir/Samples/CMakeLists.txt | 2 +- src/Qir/Tests/CMakeLists.txt | 2 +- .../FullstateSimulatorTests.cpp | 30 + src/Qir/Tests/QIR-static/qir-driver.cpp | 15 +- .../QIR-static/qir-test-conditionals.cpp | 12 +- src/Qir/qir-utils.ps1 | 34 + .../Integration.cs | 18 + .../Integration.qs | 32 + .../Tests.AutoSubstitution.Integration.csproj | 22 + .../CodeGenerationTests.cs | 90 +++ .../AutoSubstitution.Tests/TestFiles/Core.qs | 5 + .../TestFiles/FailAlternativeDoesNotExist.qs | 6 + .../TestFiles/FailDifferentSignatures.qs | 8 + .../FailDifferentSpecializationKinds.qs | 8 + .../TestFiles/FailNoNamespace.qs | 8 + .../TestFiles/Success.cs_ | 49 ++ .../TestFiles/Success.qs | 8 + .../TestFiles/SuccessA.cs_ | 69 ++ .../TestFiles/SuccessA.qs | 8 + .../TestFiles/SuccessC.cs_ | 69 ++ .../TestFiles/SuccessC.qs | 8 + .../TestFiles/SuccessCA.cs_ | 109 ++++ .../TestFiles/SuccessCA.qs | 8 + .../Tests.AutoSubstitution.csproj | 69 ++ .../AutoSubstitution/AutoSubstitution.csproj | 23 + .../AutoSubstitution/CodeGenerator.cs | 297 +++++++++ ...t.Quantum.AutoSubstitution.nuspec.template | 29 + .../AutoSubstitution/RewriteStep.cs | 180 ++++++ .../AutoSubstitution/RewriteStep.props | 8 + .../AutoSubstitution/Substitution.qs | 17 + ...sts.Microsoft.Quantum.EntryPointDriver.sln | Bin 14910 -> 0 bytes src/Simulation/QSharpFoundation/Canon/NoOp.qs | 5 - .../QSharpFoundation/Diagnostics/Assert.qs | 12 + .../QSharpFoundation/Diagnostics/Dump.qs | 36 +- .../QSharpFoundation/Diagnostics/UnitTests.qs | 8 + src/Simulation/QSharpFoundation/Math/Types.qs | 6 + src/Simulation/QSharpFoundation/Message.qs | 8 + 62 files changed, 3110 insertions(+), 165 deletions(-) create mode 100644 src/Qir/Common/Include/QirUtils.hpp create mode 100644 src/Qir/QIR-RT-API-Design-Guidelines.md create mode 100644 src/Qir/Runtime/lib/QIR/QubitManager.cpp create mode 100644 src/Qir/Runtime/public/QubitManager.hpp create mode 100644 src/Qir/Runtime/unittests/QubitManagerTests.cpp create mode 100644 src/Simulation/AutoSubstitution.Integration.Tests/Integration.cs create mode 100644 src/Simulation/AutoSubstitution.Integration.Tests/Integration.qs create mode 100644 src/Simulation/AutoSubstitution.Integration.Tests/Tests.AutoSubstitution.Integration.csproj create mode 100644 src/Simulation/AutoSubstitution.Tests/CodeGenerationTests.cs create mode 100644 src/Simulation/AutoSubstitution.Tests/TestFiles/Core.qs create mode 100644 src/Simulation/AutoSubstitution.Tests/TestFiles/FailAlternativeDoesNotExist.qs create mode 100644 src/Simulation/AutoSubstitution.Tests/TestFiles/FailDifferentSignatures.qs create mode 100644 src/Simulation/AutoSubstitution.Tests/TestFiles/FailDifferentSpecializationKinds.qs create mode 100644 src/Simulation/AutoSubstitution.Tests/TestFiles/FailNoNamespace.qs create mode 100644 src/Simulation/AutoSubstitution.Tests/TestFiles/Success.cs_ create mode 100644 src/Simulation/AutoSubstitution.Tests/TestFiles/Success.qs create mode 100644 src/Simulation/AutoSubstitution.Tests/TestFiles/SuccessA.cs_ create mode 100644 src/Simulation/AutoSubstitution.Tests/TestFiles/SuccessA.qs create mode 100644 src/Simulation/AutoSubstitution.Tests/TestFiles/SuccessC.cs_ create mode 100644 src/Simulation/AutoSubstitution.Tests/TestFiles/SuccessC.qs create mode 100644 src/Simulation/AutoSubstitution.Tests/TestFiles/SuccessCA.cs_ create mode 100644 src/Simulation/AutoSubstitution.Tests/TestFiles/SuccessCA.qs create mode 100644 src/Simulation/AutoSubstitution.Tests/Tests.AutoSubstitution.csproj create mode 100644 src/Simulation/AutoSubstitution/AutoSubstitution.csproj create mode 100644 src/Simulation/AutoSubstitution/CodeGenerator.cs create mode 100644 src/Simulation/AutoSubstitution/Microsoft.Quantum.AutoSubstitution.nuspec.template create mode 100644 src/Simulation/AutoSubstitution/RewriteStep.cs create mode 100644 src/Simulation/AutoSubstitution/RewriteStep.props create mode 100644 src/Simulation/AutoSubstitution/Substitution.qs delete mode 100644 src/Simulation/EntryPointDriver.Tests/Tests.Microsoft.Quantum.EntryPointDriver.sln diff --git a/Simulation.sln b/Simulation.sln index 9a65f34a9db..ac2435996de 100644 --- a/Simulation.sln +++ b/Simulation.sln @@ -113,6 +113,14 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "StandaloneInputReference", EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "qir-standalone-input-reference", "src\Qir\Samples\StandaloneInputReference\qsharp\qir-standalone-input-reference.csproj", "{D7D34736-A719-4B45-A33F-2723F59EC29D}" EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Simulation", "Simulation", "{3CD26906-C7F3-47B8-AF43-FF6BCF1CB3EF}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "AutoSubstitution", "src\Simulation\AutoSubstitution\AutoSubstitution.csproj", "{33D66E90-049F-4A0B-A2B1-79E7E7E0ED0F}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Tests.AutoSubstitution", "src\Simulation\AutoSubstitution.Tests\Tests.AutoSubstitution.csproj", "{4EBC65DF-3B5E-419B-8E26-3EEF0B5CD300}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Tests.AutoSubstitution.Integration", "src\Simulation\AutoSubstitution.Integration.Tests\Tests.AutoSubstitution.Integration.csproj", "{D23480EE-88FC-4DF2-86BD-1C5BDD6CD98C}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -749,6 +757,54 @@ Global {D7D34736-A719-4B45-A33F-2723F59EC29D}.RelWithDebInfo|Any CPU.Build.0 = Debug|Any CPU {D7D34736-A719-4B45-A33F-2723F59EC29D}.RelWithDebInfo|x64.ActiveCfg = Debug|Any CPU {D7D34736-A719-4B45-A33F-2723F59EC29D}.RelWithDebInfo|x64.Build.0 = Debug|Any CPU + {33D66E90-049F-4A0B-A2B1-79E7E7E0ED0F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {33D66E90-049F-4A0B-A2B1-79E7E7E0ED0F}.Debug|Any CPU.Build.0 = Debug|Any CPU + {33D66E90-049F-4A0B-A2B1-79E7E7E0ED0F}.Debug|x64.ActiveCfg = Debug|Any CPU + {33D66E90-049F-4A0B-A2B1-79E7E7E0ED0F}.Debug|x64.Build.0 = Debug|Any CPU + {33D66E90-049F-4A0B-A2B1-79E7E7E0ED0F}.MinSizeRel|Any CPU.ActiveCfg = Debug|Any CPU + {33D66E90-049F-4A0B-A2B1-79E7E7E0ED0F}.MinSizeRel|Any CPU.Build.0 = Debug|Any CPU + {33D66E90-049F-4A0B-A2B1-79E7E7E0ED0F}.MinSizeRel|x64.ActiveCfg = Debug|Any CPU + {33D66E90-049F-4A0B-A2B1-79E7E7E0ED0F}.MinSizeRel|x64.Build.0 = Debug|Any CPU + {33D66E90-049F-4A0B-A2B1-79E7E7E0ED0F}.Release|Any CPU.ActiveCfg = Release|Any CPU + {33D66E90-049F-4A0B-A2B1-79E7E7E0ED0F}.Release|Any CPU.Build.0 = Release|Any CPU + {33D66E90-049F-4A0B-A2B1-79E7E7E0ED0F}.Release|x64.ActiveCfg = Release|Any CPU + {33D66E90-049F-4A0B-A2B1-79E7E7E0ED0F}.Release|x64.Build.0 = Release|Any CPU + {33D66E90-049F-4A0B-A2B1-79E7E7E0ED0F}.RelWithDebInfo|Any CPU.ActiveCfg = Debug|Any CPU + {33D66E90-049F-4A0B-A2B1-79E7E7E0ED0F}.RelWithDebInfo|Any CPU.Build.0 = Debug|Any CPU + {33D66E90-049F-4A0B-A2B1-79E7E7E0ED0F}.RelWithDebInfo|x64.ActiveCfg = Debug|Any CPU + {33D66E90-049F-4A0B-A2B1-79E7E7E0ED0F}.RelWithDebInfo|x64.Build.0 = Debug|Any CPU + {4EBC65DF-3B5E-419B-8E26-3EEF0B5CD300}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {4EBC65DF-3B5E-419B-8E26-3EEF0B5CD300}.Debug|Any CPU.Build.0 = Debug|Any CPU + {4EBC65DF-3B5E-419B-8E26-3EEF0B5CD300}.Debug|x64.ActiveCfg = Debug|Any CPU + {4EBC65DF-3B5E-419B-8E26-3EEF0B5CD300}.Debug|x64.Build.0 = Debug|Any CPU + {4EBC65DF-3B5E-419B-8E26-3EEF0B5CD300}.MinSizeRel|Any CPU.ActiveCfg = Debug|Any CPU + {4EBC65DF-3B5E-419B-8E26-3EEF0B5CD300}.MinSizeRel|Any CPU.Build.0 = Debug|Any CPU + {4EBC65DF-3B5E-419B-8E26-3EEF0B5CD300}.MinSizeRel|x64.ActiveCfg = Debug|Any CPU + {4EBC65DF-3B5E-419B-8E26-3EEF0B5CD300}.MinSizeRel|x64.Build.0 = Debug|Any CPU + {4EBC65DF-3B5E-419B-8E26-3EEF0B5CD300}.Release|Any CPU.ActiveCfg = Release|Any CPU + {4EBC65DF-3B5E-419B-8E26-3EEF0B5CD300}.Release|Any CPU.Build.0 = Release|Any CPU + {4EBC65DF-3B5E-419B-8E26-3EEF0B5CD300}.Release|x64.ActiveCfg = Release|Any CPU + {4EBC65DF-3B5E-419B-8E26-3EEF0B5CD300}.Release|x64.Build.0 = Release|Any CPU + {4EBC65DF-3B5E-419B-8E26-3EEF0B5CD300}.RelWithDebInfo|Any CPU.ActiveCfg = Debug|Any CPU + {4EBC65DF-3B5E-419B-8E26-3EEF0B5CD300}.RelWithDebInfo|Any CPU.Build.0 = Debug|Any CPU + {4EBC65DF-3B5E-419B-8E26-3EEF0B5CD300}.RelWithDebInfo|x64.ActiveCfg = Debug|Any CPU + {4EBC65DF-3B5E-419B-8E26-3EEF0B5CD300}.RelWithDebInfo|x64.Build.0 = Debug|Any CPU + {D23480EE-88FC-4DF2-86BD-1C5BDD6CD98C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {D23480EE-88FC-4DF2-86BD-1C5BDD6CD98C}.Debug|Any CPU.Build.0 = Debug|Any CPU + {D23480EE-88FC-4DF2-86BD-1C5BDD6CD98C}.Debug|x64.ActiveCfg = Debug|Any CPU + {D23480EE-88FC-4DF2-86BD-1C5BDD6CD98C}.Debug|x64.Build.0 = Debug|Any CPU + {D23480EE-88FC-4DF2-86BD-1C5BDD6CD98C}.MinSizeRel|Any CPU.ActiveCfg = Debug|Any CPU + {D23480EE-88FC-4DF2-86BD-1C5BDD6CD98C}.MinSizeRel|Any CPU.Build.0 = Debug|Any CPU + {D23480EE-88FC-4DF2-86BD-1C5BDD6CD98C}.MinSizeRel|x64.ActiveCfg = Debug|Any CPU + {D23480EE-88FC-4DF2-86BD-1C5BDD6CD98C}.MinSizeRel|x64.Build.0 = Debug|Any CPU + {D23480EE-88FC-4DF2-86BD-1C5BDD6CD98C}.Release|Any CPU.ActiveCfg = Release|Any CPU + {D23480EE-88FC-4DF2-86BD-1C5BDD6CD98C}.Release|Any CPU.Build.0 = Release|Any CPU + {D23480EE-88FC-4DF2-86BD-1C5BDD6CD98C}.Release|x64.ActiveCfg = Release|Any CPU + {D23480EE-88FC-4DF2-86BD-1C5BDD6CD98C}.Release|x64.Build.0 = Release|Any CPU + {D23480EE-88FC-4DF2-86BD-1C5BDD6CD98C}.RelWithDebInfo|Any CPU.ActiveCfg = Debug|Any CPU + {D23480EE-88FC-4DF2-86BD-1C5BDD6CD98C}.RelWithDebInfo|Any CPU.Build.0 = Debug|Any CPU + {D23480EE-88FC-4DF2-86BD-1C5BDD6CD98C}.RelWithDebInfo|x64.ActiveCfg = Debug|Any CPU + {D23480EE-88FC-4DF2-86BD-1C5BDD6CD98C}.RelWithDebInfo|x64.Build.0 = Debug|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -802,6 +858,10 @@ Global {AAFB81D3-BC87-404D-BA64-AF40B2D2E45A} = {F6C2D4C0-12DC-40E3-9C86-FA5308D9B567} {A7DB7367-9FD6-4164-8263-A05077BE54AB} = {AAFB81D3-BC87-404D-BA64-AF40B2D2E45A} {D7D34736-A719-4B45-A33F-2723F59EC29D} = {A7DB7367-9FD6-4164-8263-A05077BE54AB} + {3CD26906-C7F3-47B8-AF43-FF6BCF1CB3EF} = {020356B7-C3FC-4100-AE37-97E5D8288D1D} + {33D66E90-049F-4A0B-A2B1-79E7E7E0ED0F} = {3CD26906-C7F3-47B8-AF43-FF6BCF1CB3EF} + {4EBC65DF-3B5E-419B-8E26-3EEF0B5CD300} = {3CD26906-C7F3-47B8-AF43-FF6BCF1CB3EF} + {D23480EE-88FC-4DF2-86BD-1C5BDD6CD98C} = {3CD26906-C7F3-47B8-AF43-FF6BCF1CB3EF} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {929C0464-86D8-4F70-8835-0A5EAF930821} diff --git a/bootstrap.ps1 b/bootstrap.ps1 index 7ad031ef42d..d8b102b7937 100644 --- a/bootstrap.ps1 +++ b/bootstrap.ps1 @@ -16,6 +16,14 @@ if (-not (Test-Path Env:AGENT_OS)) { Pop-Location $Env:BUILD_CONFIGURATION = $null } + if ($Env:ENABLE_QIRRUNTIME -ne "false") { + Write-Host "Build release flavor of the QIR Runtime" + $Env:BUILD_CONFIGURATION = "Release" + Push-Location (Join-Path $PSScriptRoot "src/Qir/Runtime") + .\build-qir-runtime.ps1 + Pop-Location + $Env:BUILD_CONFIGURATION = $null + } Write-Host "Build simulation solution" dotnet build Simulation.sln diff --git a/build/manifest.ps1 b/build/manifest.ps1 index 4ce93ed25b1..a66e133c80c 100644 --- a/build/manifest.ps1 +++ b/build/manifest.ps1 @@ -20,6 +20,7 @@ param( $artifacts = @{ Packages = @( "Microsoft.Azure.Quantum.Client", + "Microsoft.Quantum.AutoSubstitution", "Microsoft.Quantum.CSharpGeneration", "Microsoft.Quantum.Development.Kit", "Microsoft.Quantum.EntryPointDriver", @@ -35,6 +36,7 @@ $artifacts = @{ Assemblies = @( ".\src\Azure\Azure.Quantum.Client\bin\$Env:BUILD_CONFIGURATION\netstandard2.1\Microsoft.Azure.Quantum.Client.dll", + ".\src\Simulation\AutoSubstitution\bin\$Env:BUILD_CONFIGURATION\netstandard2.1\Microsoft.Quantum.AutoSubstitution.dll", ".\src\Simulation\CSharpGeneration\bin\$Env:BUILD_CONFIGURATION\netstandard2.1\Microsoft.Quantum.CSharpGeneration.dll", ".\src\Simulation\CSharpGeneration.App\bin\$Env:BUILD_CONFIGURATION\netcoreapp3.1\Microsoft.Quantum.CSharpGeneration.App.dll", ".\src\Simulation\RoslynWrapper\bin\$Env:BUILD_CONFIGURATION\netstandard2.1\Microsoft.Quantum.RoslynWrapper.dll", diff --git a/build/pack.ps1 b/build/pack.ps1 index 7f6dbe702a4..c786bf8bff9 100644 --- a/build/pack.ps1 +++ b/build/pack.ps1 @@ -90,6 +90,7 @@ function Pack-Dotnet() { Write-Host "##[info]Using nuget to create packages" Pack-Dotnet '../src/Azure/Azure.Quantum.Client/Microsoft.Azure.Quantum.Client.csproj' Pack-One '../src/Simulation/CSharpGeneration/Microsoft.Quantum.CSharpGeneration.fsproj' '-IncludeReferencedProjects' +Pack-One '../src/Simulation/AutoSubstitution/AutoSubstitution.csproj' '-IncludeReferencedProjects' Pack-Dotnet '../src/Simulation/EntryPointDriver/Microsoft.Quantum.EntryPointDriver.csproj' Pack-Dotnet '../src/Simulation/Core/Microsoft.Quantum.Runtime.Core.csproj' Pack-Dotnet '../src/Simulation/TargetDefinitions/Interfaces/Microsoft.Quantum.Targets.Interfaces.csproj' diff --git a/src/Qir/Common/Include/QirUtils.hpp b/src/Qir/Common/Include/QirUtils.hpp new file mode 100644 index 00000000000..e5e351d6844 --- /dev/null +++ b/src/Qir/Common/Include/QirUtils.hpp @@ -0,0 +1,7 @@ +#ifndef QIRUTILS_HPP +#define QIRUTILS_HPP + +// Calm down the "Unused Entity" compiler warning: +#define UNUSED(expr) ((void) (expr)) + +#endif // #ifndef QIRUTILS_HPP \ No newline at end of file diff --git a/src/Qir/Common/Include/SimulatorStub.hpp b/src/Qir/Common/Include/SimulatorStub.hpp index 5a2a2459e9f..c67ac469f0c 100644 --- a/src/Qir/Common/Include/SimulatorStub.hpp +++ b/src/Qir/Common/Include/SimulatorStub.hpp @@ -15,113 +15,113 @@ namespace Quantum { throw std::logic_error("not_implemented: AllocateQubit"); } - void ReleaseQubit(Qubit qubit) override + void ReleaseQubit(Qubit /* qubit */) override { throw std::logic_error("not_implemented: ReleaseQubit"); } - virtual std::string QubitToString(Qubit qubit) override + virtual std::string QubitToString(Qubit /* qubit */) override { throw std::logic_error("not_implemented: QubitToString"); } - void X(Qubit target) override + void X(Qubit /*target*/) override { throw std::logic_error("not_implemented: X"); } - void Y(Qubit target) override + void Y(Qubit /*target*/) override { throw std::logic_error("not_implemented: Y"); } - void Z(Qubit target) override + void Z(Qubit /* target */) override { throw std::logic_error("not_implemented: Z"); } - void H(Qubit target) override + void H(Qubit /*target*/) override { throw std::logic_error("not_implemented: H"); } - void S(Qubit target) override + void S(Qubit /*target*/) override { throw std::logic_error("not_implemented: S"); } - void T(Qubit target) override + void T(Qubit /*target*/) override { throw std::logic_error("not_implemented: T"); } - void R(PauliId axis, Qubit target, double theta) override + void R(PauliId /* axis */, Qubit /* target */, double /* theta */) override { throw std::logic_error("not_implemented: R"); } - void Exp(long numTargets, PauliId paulis[], Qubit targets[], double theta) override + void Exp(long /* numTargets */, PauliId* /* paulis */, Qubit* /* targets */, double /* theta */) override { throw std::logic_error("not_implemented: Exp"); } - void ControlledX(long numControls, Qubit controls[], Qubit target) override + void ControlledX(long /*numControls*/, Qubit* /*controls*/, Qubit /*target*/) override { throw std::logic_error("not_implemented: ControlledX"); } - void ControlledY(long numControls, Qubit controls[], Qubit target) override + void ControlledY(long /*numControls*/, Qubit* /*controls*/, Qubit /*target*/) override { throw std::logic_error("not_implemented: ControlledY"); } - void ControlledZ(long numControls, Qubit controls[], Qubit target) override + void ControlledZ(long /*numControls*/, Qubit* /*controls*/, Qubit /*target*/) override { throw std::logic_error("not_implemented: ControlledZ"); } - void ControlledH(long numControls, Qubit controls[], Qubit target) override + void ControlledH(long /*numControls*/, Qubit* /*controls*/, Qubit /*target*/) override { throw std::logic_error("not_implemented: ControlledH"); } - void ControlledS(long numControls, Qubit controls[], Qubit target) override + void ControlledS(long /*numControls*/, Qubit* /*controls*/, Qubit /*target*/) override { throw std::logic_error("not_implemented: ControlledS"); } - void ControlledT(long numControls, Qubit controls[], Qubit target) override + void ControlledT(long /*numControls*/, Qubit* /*controls*/, Qubit /*target*/) override { throw std::logic_error("not_implemented: ControlledT"); } - void ControlledR(long numControls, Qubit controls[], PauliId axis, Qubit target, double theta) override + void ControlledR(long /*numControls*/, Qubit* /*controls*/, PauliId /*axis*/, Qubit /*target*/, double /*theta*/) override { throw std::logic_error("not_implemented: ControlledR"); } void ControlledExp( - long numControls, - Qubit controls[], - long numTargets, - PauliId paulis[], - Qubit targets[], - double theta) override + long /*numControls*/, + Qubit* /*controls*/, + long /*numTargets*/, + PauliId* /*paulis*/, + Qubit* /*targets*/, + double /*theta*/) override { throw std::logic_error("not_implemented: ControlledExp"); } - void AdjointS(Qubit target) override + void AdjointS(Qubit /*target*/) override { throw std::logic_error("not_implemented: AdjointS"); } - void AdjointT(Qubit target) override + void AdjointT(Qubit /*target*/) override { throw std::logic_error("not_implemented: AdjointT"); } - void ControlledAdjointS(long numControls, Qubit controls[], Qubit target) override + void ControlledAdjointS(long /*numControls*/, Qubit* /*controls*/, Qubit /*target*/) override { throw std::logic_error("not_implemented: ControlledAdjointS"); } - void ControlledAdjointT(long numControls, Qubit controls[], Qubit target) override + void ControlledAdjointT(long /*numControls*/, Qubit* /*controls*/, Qubit /*target*/) override { throw std::logic_error("not_implemented: ControlledAdjointT"); } - Result Measure(long numBases, PauliId bases[], long numTargets, Qubit targets[]) override + Result Measure(long /*numBases*/, PauliId* /*bases*/, long /*numTargets*/, Qubit* /*targets*/) override { throw std::logic_error("not_implemented: Measure"); } - void ReleaseResult(Result result) override + void ReleaseResult(Result /*result*/) override { throw std::logic_error("not_implemented: ReleaseResult"); } - bool AreEqualResults(Result r1, Result r2) override + bool AreEqualResults(Result /*r1*/, Result /*r2*/) override { throw std::logic_error("not_implemented: AreEqualResults"); } - ResultValue GetResultValue(Result result) override + ResultValue GetResultValue(Result /*result*/) override { throw std::logic_error("not_implemented: GetResultValue"); } diff --git a/src/Qir/QIR-RT-API-Design-Guidelines.md b/src/Qir/QIR-RT-API-Design-Guidelines.md new file mode 100644 index 00000000000..ccdbdbd7a5f --- /dev/null +++ b/src/Qir/QIR-RT-API-Design-Guidelines.md @@ -0,0 +1,601 @@ +# QIR Runtime API Design Guidelines + +## Contents +* [Summary of This Document](#summary-of-this-document) +* [Guidelines](#guidelines) + * [Terms](#terms) + * [Know the Issues When Crossing the ABI Boundary](#know-the-issues-when-crossing-the-abi-boundary) + * [Be Aware of Different Compilers for Different Binaries](#be-aware-of-different-compilers-for-different-binaries) + * [Expose as Little as Necessary](#expose-as-little-as-necessary) + * [Expose C Only](#expose-c-only) + * [Allocate and Deallocate In The Same Binary](#allocate-and-deallocate-in-the-same-binary) + * [Avoid Exposing Entities With Multiple Layouts](#avoid-exposing-entities-with-multiple-layouts) + * [Be Careful When Exposing Certain Data Types](#be-careful-when-exposing-certain-data-types) + * [Integer Types](#integer-types) + * [`char`](#char) + * [`enum`](#enum) + * [`bool`](#bool) + * [Floating-Point Types](#floating-point-types) + * [Don't Let the C++ Exceptions Cross the ABI Boundary](#dont-let-the-c-exceptions-cross-the-abi-boundary) +* [Considerations Taken Into Account](#considerations-taken-into-account) + * [The `bool` Type](#the-bool-type) + * [The `char` Type](#the-char-type) + * [The `enum` Type](#the-enum-type) + * [The Floating-Point Types](#the-floating-point-types) + * [The Integer Types](#the-integer-types) +* [Information Sources](#information-sources) + +## Summary of This Document + +At the moment of writing the [QIR Runtime](https://github.com/microsoft/qsharp-runtime/tree/main/src/Qir/Runtime) +was a set of dynamic libraries exposing the API in the form of both C and C++ entities +(plus IR wrappers around certain C functions). +The C++ part and some other features, according to +[this post](https://stackoverflow.com/questions/22797418/how-do-i-safely-pass-objects-especially-stl-objects-to-and-from-a-dll/22797419#22797419), +were to cause problems for the users. + +Since the QIR Runtime was going public, we needed to mitigate those problems. These guidelines were among the first steps in that effort, +intended to provide the instructions about what to do and what to avoid when designing the QIR Runtime API or making changes to it. + +Those who need just the summary of recommendations (the _result_ of the decision-making process), +can run throuhg the [Guidelines](#guidelines) section. +Those who would like to see the grounds/reasoning behind the decisions made, can see the +[Considerations Taken Into Account](#considerations-taken-into-account) section. + + +## Guidelines + +### Terms + +**ABI** - [Application Binary Interface](https://en.wikipedia.org/wiki/Application_binary_interface) +**binary** - A dynamic library or an executable file. +The interacting binaries can be written in different languages and built with different compilers. +**cross-ABI interface** - The interface between the binaries. +This document mostly deals with the interface between the QIR Runtime dynamic library on the one hand, +and the executable and other dynamic libraries (like simulator) on the other hand. +**IR** - +[intermediate representation](https://blog.gopheracademy.com/advent-2018/llvm-ir-and-go/#:~:text=LLVM%20IR%20is%20a%20low,number%20of%20function%20local%20registers.&text=Front%2Dend%3A%20compiles%20source%20language,compiles%20IR%20to%20machine%20code.) +of [LLVM](https://llvm.org/). +**QIR** - quantum intermediate representation - a subset of the IR. Is generated by the Q# compiler from the Q# code. +**RT** - Runtime. + +### Know the Issues When Crossing the ABI Boundary +See +[the post](https://stackoverflow.com/questions/22797418/how-do-i-safely-pass-objects-especially-stl-objects-to-and-from-a-dll/22797419#22797419) +that resulted in these guidelines. + +### Be Aware of Different Compilers for Different Binaries +Take into account that different binaries can be written in different languages, by different people, +built with different compilers or different versions of the compiler, +with different command line arguments, by different scripts, on different machines. + +Bear in mind that QIR RT API needs to be callable from other languages and environments. +At the moment of writing the particular plan was to call the QIR RT API from the +[IQ#](https://ms-quantum.visualstudio.com/Quantum%20Program/_wiki/wikis/Quantum-OKRs.wiki/470/IQSharp). +Next, there was a chance of calling from Python, and possibly Rust. + +Avoid using the features that preclude calling the QIR RT API from other languages and environments. + + +### Expose as Little as Necessary +Every piece of code has a long-term cost associated with it. +The more code we write, the more bugs we can introduce, the more care is needed during migration to the newer compiler +and while porting to a different platform. The higher is the risk of identifier conflict with the other people's code +and new features of the language. The more identifiers we use, the less freedom remains for a different use of those identifiers. + +### Expose C Only +Do not expose the C++ identifiers and features (not present in C) - +the C++ data types (e.g. `class`), templates, function overloads, namespaces, etc. +To facilitate that for yourself and for the others, consider strictly separating +the C and C++ code into .h/.c files for C, and .hpp/.cpp. files for C++. Expose C headers only. + +If the exposed headers need to include the other headers, then include the C headers only, not the C++ headers. +In particular, the standard C headers, +e.g. [``](https://en.cppreference.com/w/c/io), have the corresponding wrapping headers in C++, +e.g. [``](https://en.cppreference.com/w/cpp/io/c). Such wrapping headers add the namespace prefix +`std::` to certain C declarations, but not all. The C++'s wrapping headers +may enclose the corresponding C header in the `extern "C"` block (and probably do something else). +This is the right thing for the C++ code internal to a binary, but not what we want in the API exposed by the QIR RT. +So, in the C headers that QIR RT exposes, include C's header `` (if needed), but not C++'s header ``. + +Everything that your C header exposes, needs to be within the +[`extern "C"`](https://en.cppreference.com/w/cpp/language/language_linkage) block +(visible to C++ compiler but not to C compiler, because `extern "C"` is a C++ feature). Example: +```c++ +#ifdef __cplusplus +extern "C" +{ +#endif + +.. // `#include`s +.. // Exposed API. + +#ifdef __cplusplus +} // extern "C" +#endif +``` +Such that the exposed API will be treated as C code (code having C language linkage), +regardless of whether this header is included by the C or C++ source file. + +### Allocate and Deallocate In The Same Binary +Since different binaries can be built with differnt compilers, +these bianaries can be linked with different C/C++ runtimes, i.e. they can be using different heaps. +The allocation made in a heap must be deallocated in the same heap. +I.e. the allocation and corresponding deallocation must be done by the same binary. +If the binary exposes a function that returns a pointer to the heap-allocated block, +then that binary must also expose a function that deallocates such a pointer. +Otherwise the heap used for allocation can leak the data, +and the heap used for deallocation can be corrupted. + +### Avoid Exposing Entities With Multiple Layouts +Avoid exposing the entities that may have different layouts in different binaries. +E.g. the alignment padding between the `struct` fields +can be different in the QIR RT dynamic library and the executable that uses the QIR RT. Thus + +_Avoid exposing_ `struct`. + +If you need to expose a mechanism that manipulates a `struct` instance, then you can expose +* a function that allocates the `struct` and returns the `void*` (`void` pointer) to it, +* a function that deallocates the `struct`, taking the `void*` parameter, +* functions that manipulate the `struct`, taking the `void*` to the `struct` as the first argument. + +E.g. for the +[`QirArray`](https://github.com/microsoft/qsharp-runtime/blob/9eb00a0f3f4beb1eee97dccb23e16ad995f5398e/src/Qir/Runtime/public/QirTypes.hpp#L15) + +| Instead Of | Possible Option | +|--------------------------------------------------------------------|---------------------| +| `QirArray::QirArray(int64_t cQubits)` (constructor) | `void* QirArray_NewQubits(int64_t qubitCount)` | +| `QirArray::QirArray(const QirArray* other)` (copy constructor) | `void* QirArray_NewCopy(void* other)` | +| `QirArray::~QirArray()` (destructor) | `void QirArray_Delete(void *)` | +| `char* QirArray::GetItemPointer(int64_t index)` (memeber function) | `void* QirArray_GetItemPointer(uint64_t index)` | + +Note: In IR the `void*` is not available as a type. You can expose `i8*` instead, and in the calling C/C++ code use `int8_t*`. + +If it is still better to expose `struct` then strictly document the `struct` layout and size your code expects. +You can enforce the layout checks with the +[compile-time C assertions](https://en.cppreference.com/w/c/error/static_assert): +```c +#include // offsetof() +#include // static_assert() + +typedef struct +{ + char c; // 1-byte field. + // 3 bytes of alignment gap. + uint32_t u32; // Is expected at offset 4. + // Total size is 8. +} +MyStruct; + +static_assert(offsetof(MyStruct, u32) == sizeof(uint32_t), "Unexpected layout of `MyStruct`"); +static_assert(sizeof(MyStruct) == (2 * sizeof(uint32_t)), "Unexpected size of `MyStruct`"); +``` + +Note: The +[`#pragma pack`](https://stackoverflow.com/questions/3318410/pragma-pack-effect/3318475#3318475) +is not guaranteed to help your exposed API. The pragma can be silently ignored by the user's compiler. + +Note: For providing a direct access to the `struct` fileds +you may want to expose functions that return the address of a particular field inside of the `struct`: +```c +uint32_t* MyStruct_GetU32Ptr(void* pStruct); // Returns the address of `u32` field of a struct + // pointed to by the parameter. +``` + +### Be Careful When Exposing Certain Data Types +The size (in bytes) of such data types as `bool`, C's `enum`, `signed` and `unsigned` {`short`, `int`, `long`, `long long`} +is implementation-specific, thus can be different on different sides of the ABI boundary. +In general prefer the fixed-size alternatives. See the type-specific details below. + +#### Integer Types +In the exposed API, for integers, prefer the fixed-size types defined in +[``](https://www.cplusplus.com/reference/cstdint/?kw=stdint.h), e.g. `uint8_t`, `int32_t`, etc. + +Note: Avoid using in the exposed API the _non-fixed-size_ types from that header, +such as `uint_least8_t`, `uint_fast16_t` (but feel free to use them internally in your binary). + +Note: If you use the [printf-family functions](https://man7.org/linux/man-pages/man3/printf.3.html) +for formatted output of the types defined in ``, +or [scanf-family functions](https://man7.org/linux/man-pages/man3/scanf.3.html) for formatted input, +then use the macros defined in [``](https://www.cplusplus.com/reference/cinttypes/). +Example: +```c +#include // uint32_t +#include // SCNu32, PRIu32 +#include // scanf(), printf() + +uint32_t u32; // Fixed-size variable. +scanf(SCNu32, &u32); // Formatted input. +printf("Value is " PRIu32 "\n", u32); // Formatted output. +``` + +#### `char` +The size of types `char`, `unsigned char`, `signed char` is fixed and is equal to 1 byte. + +In the exposed API it is safe to use `char` for characters and C strings (null-terminated sequences of `char`s). + +For integers of size 1 prefer `uint8_t` or `int8_t`. If you use `char` for integers, +then specify explicitly either `unsigned char` or `signed char`, +because the plain `char` is interpreted differently in different implementations. +In some implementations `char` defaults to `unsigned char`, in the others - to `signed char`. + +#### `enum` +The [C's `enum`](https://en.cppreference.com/w/c/language/enum) type is _non-fixed-size_ +(as opposed to [C++'s `enum`](https://en.cppreference.com/w/cpp/language/enum) that can have an _underlying type_). + +The code will be safe if you do not use `enum` type in the function signatures (function parameter types or return type). +Exposing `enum` as a stand-alone type (and exposing variables of that type) requires care, +control over the compatible fixed-size types that are safe to cast the `enum` to. +So, think twice before exposing `enum`. Document well the ranges of values, safe types to cast to, etc. +Use mechanisms that prevent the erroneous use. +See details in [The `enum` Type](#the-`enum`-type) section. + +If you still plan to expose `enum`: + +Note: In C language if you define the `enum` type like this: +```c +enum MyEnumType +{ + .. +}; +``` +then _in C_ you will have to write `enum MyEnumType`, i.e. 2 words, every time you use the `enum`. +If you want to use just 1 word - `MyEnumType` - you can define the `enum` type like this: +```c +typedef enum +{ + .. +} +MyEnumType; +``` + +Note: You can expose the enumerators only (rather that the whole `enum` type). +But remember, the enumerators in C have the type `int` - _non-fixed-size type_. +```c +enum +{ + ENUMERATOR_A, // Has type `int`. + ENUMERATOR_B // Has type `int`. +}; +``` +Trick: Chris Granade has shown how Rust's cbindgen exposes enums to C as having a fixed size: +```c +enum Pauli +{ + I = 0, + X = 1, + Y = 3, + Z = 2, +}; +typedef uint8_t Pauli; +``` +As long as in the exposed API you type `Pauli` (rather than `enum Pauli`), you use the fixed-size type `uint8_t`. +And the type `enum Pauli` defines the range of values +and the constants that can be used with `Pauli` type (internally in the binaries). + +#### `bool` +The `bool` type is non-fixed-size (both in [C](https://en.cppreference.com/w/c/types/boolean) and +[C++](https://en.cppreference.com/w/cpp/language/types#Boolean_type)). +Be careful if using `bool` in the exposed API, prefer a fixed-size integer type instead (e.g. `uint8_t`). + +If using fixed-size integer instead of `bool`, strictly document the range of values, +in particular what value corresponds to the concept of `true` +(value of `1`, or any non-zero value, or other). + +For your information: [In Rust](https://doc.rust-lang.org/reference/types/boolean.html) +> **An object with the boolean type has a size** and alignment **of 1** each. +The value false has the bit pattern `0x00` and the value true has the bit pattern `0x01`. + +Note: One may want for `bool` to follow the Chris Granade's trick for `enum` mentioned above. +Example: +```c +// enum Bool : uint8_t { False, True }; +typedef uint8_t Bool; +``` +As long as in the exposed API you write `Bool` (rather than `bool`), you use the fixed-size type `uint8_t`. +And the comment explains the type `Bool`. + +When choosing the range of values, +and for the safe internal use of Booleans and their substitutes, see also +* [Avoid Comparing Booleans to `true`](https://github.com/kuzminrobin/code_review_notes/blob/master/cpp_design_bookmarks.md#avoid-comparing-booleans-to-true) +* [Know the Limitations of `memset()` When Initializing](https://github.com/kuzminrobin/code_review_notes/blob/master/cpp_design_bookmarks.md#know-the-limitations-of-memset-when-initializing) + + +#### Floating-Point Types + +Strictly speaking, the floating-point types (`float`, `double`, `long double`) are non-fixed-size. +However these guidelines suggest to expect that the following floating-point types conform to the same standards +on both sides of the ABI boundary: +* `float` matches IEEE-754 _32 bit_ floating point type (IEC 60559 single format), +* `double` matches IEEE-754 _64 bit_ floating point type (IEC 60559 double format). + +To detect the size discrepancy, you can use the following +[compile-time C assertions](https://en.cppreference.com/w/c/error/static_assert) +in the exposed C headers (if your exposed API uses a floating-point type): +```c +static_assert(sizeof(float ) == sizeof(uint32_t), "Unexpected size of `float`" ); +static_assert(sizeof(double) == sizeof(uint64_t), "Unexpected size of `double`"); +``` +As for [`long double` type](https://en.cppreference.com/w/c/language/arithmetic_types#Real_floating_types), +the decision has not been made at the time of writing. If you need to expose the mechanisms that use this type, +then you can follow the recomendations in the +[Avoid Exposing Entities With Multiple Layouts](#avoid-exposing-entities-with-multiple-layouts) section. + +The examples of where the floating-point types were used at the moment of writing are +* [`%String* @__quantum__rt__double_to_string(double %val)`](https://github.com/microsoft/qsharp-runtime/blob/a15856ea9aac6244718865481dbbd89b70056166/src/Qir/Runtime/lib/QIR/bridge-rt.ll#L457), +* [`QIR_SHARED_API QirString* quantum__rt__double_to_string(double)`](https://github.com/microsoft/qsharp-runtime/blob/4ee33c0a1fb89cc859254175fea74b841a4fd01c/src/Qir/Runtime/public/QirRuntime.hpp#L203), + +a number of math functions, e.g. +* [`dllexport double @__quantum__qis__sin__body(double %theta)`](https://github.com/microsoft/qsharp-runtime/blob/4ee33c0a1fb89cc859254175fea74b841a4fd01c/src/Qir/Runtime/lib/QSharpFoundation/qsharp-foundation-qis.ll#L121). + + +### Don't Let the C++ Exceptions Cross the ABI Boundary +Our cross-ABI interface is a subset of C interface. The C interface does not include the C++ exceptions. The C++ exceptions +crossing the ABI Boundary are likely to stay uncaught and result in a crash. +If you are still thinking about designing an interface that tunnels the C++ exceptions through our subset of C +(an interface where the C++ exceptions are thrown on one side and caught on the other side of the ABI boundary), +then you should know the following. + +In some implementations the C++ exception instances are allocated on the heap during `throw`, and then deallocated during `catch`. +If `throw` and `catch` are on different sides of the ABI boundary then they are likely to use different heaps. +The consequence is described in the +[Allocate and Deallocate In The Same Binary](#allocate-and-deallocate-in-the-same-binary) section above. + +At the moment of writing the +[Fail-Statement](https://docs.microsoft.com/en-us/azure/quantum/user-guide/language/statements/returnsandtermination#fail-statement) +implementation +([`quantum__rt__fail_cstr()`](https://github.com/microsoft/qsharp-runtime/blob/b10447eff672b7ba4f2e8564302888a8eb6bce8a/src/Qir/Runtime/lib/QIR/utils.cpp#L60)) +was throwing an exception not caught anywhere. +This was causing a reasonably peaceful termination as long as the same compiler was used on both sides of the ABI boundary. +The plan was to replace the exception throw with the [`exit()`](https://www.cplusplus.com/reference/cstdlib/exit/) +or [`terminate()`](https://www.cplusplus.com/reference/exception/terminate) +or [`abort()`](https://www.cplusplus.com/reference/cstdlib/abort) or other alternatives. + +On the other hand, exceptions are a natural part of the C++ standard. It is virtually impossible to use +the standard library (STL, `std` namespace) and not to use exceptions. All _exceptional_ situations are +reported by using exceptions (such as [`out_of_range`](https://en.cppreference.com/w/cpp/error/out_of_range) or [`invalid_argument`](https://en.cppreference.com/w/cpp/error/invalid_argument) to name a few). So it is safe to assume +that the C++ code we are developing will throw exceptions even if they aren't thrown right in the code we write. + +Recommendation is to use `std` namespace and exceptions in C++ code **only where appropriate**. Many recommendations +exist on how to use exceptions properly and minimize performance penalties. +* As part of [C++ core guidelines](https://isocpp.github.io/CppCoreGuidelines/CppCoreGuidelines). +* MSVC Documentation: [Modern C++ best practices for exceptions and error handling](https://docs.microsoft.com/en-us/cpp/cpp/errors-and-exception-handling-modern-cpp?view=msvc-160) and [How to: Design for exception safety](https://docs.microsoft.com/en-us/cpp/cpp/how-to-design-for-exception-safety?view=msvc-160). +* [Exceptions and Error Handling](https://isocpp.org/wiki/faq/exceptions) As part of Modern C++ FAQ. +* Google guidelines recommend against exceptions, but [allow them in Windows code, especially while using STL](https://google.github.io/styleguide/cppguide.html#Windows_Code). +* [Vishal Chovatiya Guidelines](http://www.vishalchovatiya.com/7-best-practices-for-exception-handling-in-cpp-with-example/), which may be easier to read. +This section will describe only exceptions with regards to the runtime API. + +* Do not handle hardware exceptions and out-of-memory exception + +It is not recommended to catch asynchronous harware exceptions. (MSVC compiler calls this +[Structured Exception Handling](https://docs.microsoft.com/en-us/cpp/cpp/structured-exception-handling-c-cpp?view=msvc-160).) +Examples of hardware exceptions are the exceptions thrown when accessing a forbidden memory location as a result of a null pointer access, stack overflow, corrupt pointer dereferencing, unaligned memory access, etc.. If such exceptional situation arises, application should fail fast and quit. Typically hardware exceptions are handled in a system level code, and our runtime is an application with +regards to the host OS. NOTE: The [MSVC's default exception handling behavior](https://docs.microsoft.com/en-us/cpp/build/reference/eh-exception-handling-model?view=msvc-160#default-exception-handling-behavior) does not conform to the C++ standard. Use the `/EHsc` flag to [conform to the standard](https://docs.microsoft.com/en-us/cpp/build/reference/eh-exception-handling-model?view=msvc-160#standard-c-exception-handling). + +It is also not recommended to handle out-of-memory exception as there's a very little chance of handling it right. +For such handling to work correctly there shouldn't be any memory allocation from the point of exception, in the stack +unwinding process, till the handling of exception and in the handling of exception until the out-of-memory situation +is resolved. Just let the application fail. For the same reason, using non-throwing version of operator new is not recommended. + +* Mark interface functions with `noexcept` + +If you implement an interface function mark it with the [noexcept specifier](https://en.cppreference.com/w/cpp/language/noexcept_spec): + +Inside of your binary if you implement a function that should not throw exceptions, then mark it with the [noexcept specifier](https://en.cppreference.com/w/cpp/language/noexcept_spec). + +> void interface_func() **noexcept** + +The `extern "C"` functions aren't considered `noexcept` by default. Consider marking them `noexcept`. +Keep in mind that `noexcept` specifier is a C++ feature. If you need to add `noexcept` specifier +to a public header or any other header that should be compatible with a C compiler +make sure to use `__cplusplus` guards. For example: + +```c++ +#ifdef __cplusplus +#define NOEXCEPT noexcept // `noexcept` in C++ only. +extern "C" +{ +#else +#define NOEXCEPT // In C this macro is empty. +#endif + +.. // `#include`s +void f() NOEXCEPT; // Exposed function. It is `noexcept` if the header is included to the C++ source file. + +#ifdef __cplusplus +} // extern "C" +#endif +``` + +If exception is thrown (and not caught) within the `noexcept` function, the application will terminate. Termination will +ensure that exceptions will never cross the API boundaries. An overhead is incurred if a noexcept function +calls functions that can throw exceptions, so consider marking all functions that shouldn't throw exceptions as `noexcept`. +If you are calling functions that can throw exceptions, make sure to handle them properly. + +* If using the [`dynamic_cast<>()`](https://en.cppreference.com/w/cpp/language/dynamic_cast), +consider casting the _pointers_ rather than _references_, e.g. +```c++ +MyClass myInstance = .. // Create an instance of a class that may or may not implement the `IMyInterface` interface. +IMyInterface & myItfRef = dynamic_cast( myInstance); // Casts the _reference_, in case of failure THROWS! Avoid using this. +IMyInterface * myItfPtr = dynamic_cast(&myInstance); // Casts the _pointer_, in case of failure returns the nullptr. Consider this first. +``` +This way you can handle the erroneous situation without raising exceptions. + +## Considerations Taken Into Account + +### The `bool` Type + +In C language `bool` is a macro that expands to the keyword `_Bool` ([details](https://en.cppreference.com/w/c/types/boolean)). + +[[C17_N2176](https://web.archive.org/web/20181230041359if_/http://www.open-std.org/jtc1/sc22/wg14/www/abq/c17_updated_proposed_fdis.pdf)], +6.2.5 Types, paragraph 2 +> An object declared as type `_Bool` **is large enough to store the values 0 and 1**. + +I.e. the size of `bool` is _at least 1 bit_, but there is no upper limit. Type `bool` is _non-fixed-size_. + +Be careful if using `bool` in the exposed API, prefer a fixed-size integer type instead (e.g. `uint8_t`). +Illustration: +* Your dynamic library exposes the `bool` global variable - `extern bool MyVar;`. +* In your dynamic library the type `bool` is _1 byte_. +* The user's executable includes your header. +* In the user's executable the type `bool` is _4 bytes_. +* When the user's code updates your variable - `MyVar = true`, the code updates _4 bytes_ (rather than _1_) - +the byte of your variable and _3 more bytes outside of it_ (thus corrupting the data after `MyVar`). + +### The `char` Type + +**`sizeof(char)`, `sizeof(unsigned char)`, `sizeof(signed char)`, are 1** +[[C17_N2176](https://web.archive.org/web/20181230041359if_/http://www.open-std.org/jtc1/sc22/wg14/www/abq/c17_updated_proposed_fdis.pdf)]: +6.5.3.4 The `sizeof` and `_Alignof` operators, paragraph 4: +> When `sizeof` is applied to an operand that has type `char`, `unsigned char`, or `signed char`, +(or a qualified version thereof) the result is 1. + +**`char` can default to `signed char` or `unsigned char`** +6.2.5 Types, paragraph 15: +> The three types `char`, `signed char`, and `unsigned char` are collectively called the _character types_. +The **implementation shall define** `char` to have the same range, +representation, and behavior as **either `signed char` or `unsigned char`**.45) + +> 45\) `CHAR_MIN`, defined in ``, will have one of the values 0 or `SCHAR_MIN`, +and this can be used to distinguish the two options. Irrespective of the choice made, +**`char` is a separate type from the other two and is not compatible with either**. + +So, don't use `char` for integers, use `unsigned char` or `signed char` instead. + +6.3.1.1 Boolean, characters, and integers. Paragraph 3 +> ... whether a "plain" char can hold negative values is **implementation-defined**. + + +### The `enum` Type +[[C17_N2176](https://web.archive.org/web/20181230041359if_/http://www.open-std.org/jtc1/sc22/wg14/www/abq/c17_updated_proposed_fdis.pdf)] +6.7.2.2 Enumeration specifiers, Paragraph 4 +> Each enumerated type shall be compatible with `char`, a signed integer type, or an unsigned integer type. +**The choice of type is implementation-defined**... + +I.e. the [C's `enum`](https://en.cppreference.com/w/c/language/enum) type is _non-fixed-size_ +(as opposed to [C++'s `enum`](https://en.cppreference.com/w/cpp/language/enum) that can have an _underlying type_). + + +**The `enum` in function signatures** +The code will be safe if you _do not_ use `enum` type in the function signatures (function parameter types or return type). + +Illustration: +* Let's say your dynamic library exposes a function having a parameter `e` of the `enum` type, +and some parameters before and after it: +```c +typedef enum { .. } MyEnumType; +void func(uint16 uia, MyEnumType e, uint16_t uib); +``` +* Let's imagine that (according to the [calling convention](https://en.wikipedia.org/wiki/Calling_convention)) +the parameter `e` needs to be passed through the _stack_ (rather than a CPU register). +* Let's say in your dynamic library the `enum` type is 2 bytes in size. So, during the call, +the 2 bytes need to be allocated on the stack for the parameter `e`. +* Immediately after it, the 2 bytes need to be allocated on the stack for the parameter `uib` +(or the parameter `uia`, the order of the parameters in the stack depends on the +[language linkage](https://en.cppreference.com/w/cpp/language/language_linkage) and/or +calling convention). +* The user's executable includes your header and calls your function `func()`. +* But in the user's executable the `MyEnumType` is _4 bytes_ in size. +So the 4 bytes are allocated on the stack (instead of 2). +* And the value for parameter `uib` is pushed to the stack _after those 4 bytes_, +to a place where the function `func()` does not expect it. + +To summarize, the caller passes `uib` through that part of the stack where the callee `func()` does not expect it, +and where the callee expects it, the 0 is passed. + +**The stand-alone `enum`** +Exposing `enum` as a stand-alone type (and exposing variables of that type) requires care, +control over the compatible fixed-size types that are safe to cast the `enum` to. + +Illustration: +* You need your dynamic library to expose a function having a parameter of the `enum` type. +* You expose the `enum` type separately. +```c +typedef enum +{ + ENUMERATOR_A, + ENUMERATOR_B +} +MyEnumType; +``` +* You expose the function, but, to be safe, you expose the parameter as a fixed-size type `uint8_t`. +```c +void func(uint8_t param); +``` + +The user's executable uses your dynamic library. The executable's source file (.c) includes your header +(that exposes the `enum` type and the function). The user's code creates a local varaible of the `enum` type, +initializes the variable with one of the enumerators (of that `enum` type), +and passes the variable as an argument to your function. +The argument (of `enum` type) is implicitly cast to the type of the parameter - `uint8_t`. + +_Is it safe_ to implicitly cast such an `enum` to `unit8_t`? Or, maybe the `enum` type has enumerators with value above `0xFF`, +and that's why the `enum` type is at least 2 bytes in size +(and upon cast to `uint8_t` only the least significant byte will end up in the parameter)? + +Do you rely on the _C_ compiler to warn? +Even if it _does_ warn, the programmers sometimes just carelessly _explicitly_ cast `enum` to `unit8_t` +(without the anlysis of the value range). + +So, think twice before exposing `enum`. Document well the ranges of values, safe types to cast to, etc. +Use mechanisms that prevent the erroneous use, e.g. +[compile-time C assertions](https://en.cppreference.com/w/c/error/static_assert): +```c +static_assert(ENUMERATOR_B <= UINT8_MAX, + "`MyEnumType` is incompatible with the parameter of `func()`. " + "Increase the parameter type"); +``` + + +### The Floating-Point Types + +The explicit specification of the *size* for the floating-point types was not found in one of the late drafts +of the C17 standard (see [Information Sources](#information-sources)). However the section "F.2 Types" says: +> — The `float` type matches the IEC 60559 single format. +— The `double` type matches the IEC 60559 double format. + +Unfortunately in the ISO/IEC 60559, Edition 2.0 2020-05 (and IEEE Std 754™-2019, IEEE Std 754™-2008), the size specification +of "single format" and "double format" was not found. + +However the C's [Real floating types](https://en.cppreference.com/w/c/language/arithmetic_types#Real_floating_types) +section of cppreference states: +> +* `float` - single precision floating point type. Matches IEEE-754 32 bit floating point type if supported. +* `double` - double precision floating point type. Matches IEEE-754 64 bit floating point type if supported +* `long double` - extended precision floating point type. Matches IEEE-754 extended floating-point type if supported, +otherwise matches some non-standard extended floating-point type as long as its precision is better than `double` +and range is at least as good as `double`, otherwise matches the type `double`. +Some x86 and x86_64 implementations use the 80-bit x87 floating point type. + +Also the following was taken into account +* [Single-precision floating-point format](https://en.wikipedia.org/wiki/Single-precision_floating-point_format) +(Wikipedia) +* [Double-precision floating-point format](https://en.wikipedia.org/wiki/Double-precision_floating-point_format) +(Wikipedia) +* [Fixed-size floating point types](https://stackoverflow.com/questions/2524737/fixed-size-floating-point-types) +(Stackoverflow) +* [What is the size of float and double in C and C++? [duplicate]](https://stackoverflow.com/questions/25524355/what-is-the-size-of-float-and-double-in-c-and-c) +(Stackoverflow) + + +### The Integer Types + +(Not strictly related to this document, but is still provided for those who are interested. +Also answers a number of other questions about the integer types, and supports certain statements in the other sections). + +It seemed commonly known that +`sizeof(short) <= sizeof(int) <= sizeof(long) <= sizeof(long long)` +But looks like C does not guarantee that, as opposed to C++, and the topic is rather arguable. +See details [here](https://stackoverflow.com/a/67678644/6362941). + +## Information Sources + +* [C/C++ Standards and Late Drafts](https://stackoverflow.com/a/83763/6362941) +* [[C11_N1570]](http://www.open-std.org/jtc1/sc22/wg14/www/docs/n1570.pdf) - One of the late drafts of C11. +* [[C11_N1570_HTML]](https://port70.net/~nsz/c/c11/n1570.html) - HTML version of the late draft. +* [[C17_N2176]](https://web.archive.org/web/20181230041359if_/http://www.open-std.org/jtc1/sc22/wg14/www/abq/c17_updated_proposed_fdis.pdf) - +One of the late drafts of C17. +* IEEE Std 754™-2008. +* IEEE Std 754™-2019. +* [How do I safely pass objects, especially STL objects, to and from a DLL?](https://stackoverflow.com/questions/22797418/how-do-i-safely-pass-objects-especially-stl-objects-to-and-from-a-dll/22797419#22797419) (Stackoverflow answer). +* ISO/IEC 60559, Edition 2.0 2020-05. diff --git a/src/Qir/Runtime/CMakeLists.txt b/src/Qir/Runtime/CMakeLists.txt index 5aea1a8065f..378834a938b 100644 --- a/src/Qir/Runtime/CMakeLists.txt +++ b/src/Qir/Runtime/CMakeLists.txt @@ -1,4 +1,4 @@ -cmake_minimum_required(VERSION 3.10) +cmake_minimum_required(VERSION 3.20 FATAL_ERROR) message(INFO "*** build config: ${CMAKE_BUILD_TYPE}") diff --git a/src/Qir/Runtime/lib/QIR/CMakeLists.txt b/src/Qir/Runtime/lib/QIR/CMakeLists.txt index 9dbd2b9cd74..08a30df8cfd 100644 --- a/src/Qir/Runtime/lib/QIR/CMakeLists.txt +++ b/src/Qir/Runtime/lib/QIR/CMakeLists.txt @@ -19,12 +19,16 @@ set(rt_sup_source_files delegated.cpp strings.cpp utils.cpp + QubitManager.cpp ) # Produce object lib we'll use to create a shared lib (so/dll) later on add_library(qir-rt-support-obj OBJECT ${rt_sup_source_files}) target_source_from_qir(qir-rt-support-obj bridge-rt.ll) -target_include_directories(qir-rt-support-obj PUBLIC ${public_includes}) +target_include_directories(qir-rt-support-obj PUBLIC + ${public_includes} + ${common_includes} +) set_property(TARGET qir-rt-support-obj PROPERTY POSITION_INDEPENDENT_CODE ON) target_compile_definitions(qir-rt-support-obj PRIVATE EXPORT_QIR_API) diff --git a/src/Qir/Runtime/lib/QIR/QubitManager.cpp b/src/Qir/Runtime/lib/QIR/QubitManager.cpp new file mode 100644 index 00000000000..2a254ca349f --- /dev/null +++ b/src/Qir/Runtime/lib/QIR/QubitManager.cpp @@ -0,0 +1,522 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +#include "QubitManager.hpp" +#include "QirRuntime.hpp" // For quantum__rt__fail_cstr +#include // For memcpy + +namespace Microsoft +{ +namespace Quantum +{ + +// +// Failing in case of errors +// + +[[noreturn]] static void FailNow(const char* message) +{ + quantum__rt__fail_cstr(message); +} + +static void FailIf(bool condition, const char* message) +{ + if (condition) + { + quantum__rt__fail_cstr(message); + } +} + +// +// QubitListInSharedArray +// + +CQubitManager::QubitListInSharedArray::QubitListInSharedArray( + QubitIdType startId, + QubitIdType endId, + QubitIdType* sharedQubitStatusArray): + firstElement(startId), + lastElement(endId) +{ + FailIf(startId > endId || startId < 0 || endId == MaximumQubitCapacity, + "Incorrect boundaries in the linked list initialization."); + FailIf(sharedQubitStatusArray == nullptr, "Shared status array is not provided."); + + for (QubitIdType i = startId; i < endId; i++) { + sharedQubitStatusArray[i] = i + 1; // Current element points to the next element. + } + sharedQubitStatusArray[endId] = NoneMarker; // Last element ends the chain. +} + +bool CQubitManager::QubitListInSharedArray::IsEmpty() const +{ + return firstElement == NoneMarker; +} + +void CQubitManager::QubitListInSharedArray::AddQubit(QubitIdType id, bool addToFront, QubitIdType* sharedQubitStatusArray) +{ + FailIf(id == NoneMarker, "Incorrect qubit id, cannot add it to the list."); + FailIf(sharedQubitStatusArray == nullptr, "Shared status array is not provided."); + + // If the list is empty, we initialize it with the new element. + if (IsEmpty()) + { + firstElement = id; + lastElement = id; + sharedQubitStatusArray[id] = NoneMarker; // List with a single elemenet in the chain. + return; + } + + if (addToFront) + { + sharedQubitStatusArray[id] = firstElement; // The new element will point to the former first element. + firstElement = id; // The new element is now the first in the chain. + } else + { + sharedQubitStatusArray[lastElement] = id; // The last element will point to the new element. + sharedQubitStatusArray[id] = NoneMarker; // The new element will end the chain. + lastElement = id; // The new element will be the last element in the chain. + } +} + +CQubitManager::QubitIdType CQubitManager::QubitListInSharedArray::TakeQubitFromFront(QubitIdType* sharedQubitStatusArray) +{ + FailIf(sharedQubitStatusArray == nullptr, "Shared status array is not provided."); + + // First element will be returned. It is 'NoneMarker' if the list is empty. + QubitIdType result = firstElement; + + // Advance list start to the next element if list is not empty. + if (!IsEmpty()) + { + firstElement = sharedQubitStatusArray[firstElement]; // The second element will be the first. + } + + // Drop pointer to the last element if list becomes empty. + if (IsEmpty()) + { + lastElement = NoneMarker; + } + + if (result != NoneMarker) + { + sharedQubitStatusArray[result] = AllocatedMarker; + } + + return result; +} + +void CQubitManager::QubitListInSharedArray::MoveAllQubitsFrom(QubitListInSharedArray& source, QubitIdType* sharedQubitStatusArray) +{ + FailIf(sharedQubitStatusArray == nullptr, "Shared status array is not provided."); + + // No need to do anthing if source is empty. + if (source.IsEmpty()) + { + return; + } + + if (this->IsEmpty()) + { + // If this list is empty, we'll just set it to the source list. + lastElement = source.lastElement; + } else + { + // Attach source at the beginning of the list if both lists aren't empty. + sharedQubitStatusArray[source.lastElement] = firstElement; // The last element of the source chain will point to the first element of this chain. + } + firstElement = source.firstElement; // The first element from the source chain will be the first element of this chain. + + // Remove all elements from source. + source.firstElement = NoneMarker; + source.lastElement = NoneMarker; +} + + +// +// RestrictedReuseArea +// + +CQubitManager::RestrictedReuseArea::RestrictedReuseArea( + QubitListInSharedArray freeQubits): + FreeQubitsReuseProhibited(), // Default costructor + FreeQubitsReuseAllowed(freeQubits) // Default shallow copy. +{ +} + + +// +// CRestrictedReuseAreaStack +// + +void CQubitManager::CRestrictedReuseAreaStack::PushToBack(RestrictedReuseArea area) +{ + FailIf(Count() >= std::numeric_limits::max(), "Too many nested restricted reuse areas."); + this->insert(this->end(), area); +} + +CQubitManager::RestrictedReuseArea CQubitManager::CRestrictedReuseAreaStack::PopFromBack() +{ + FailIf(this->empty(), "Cannot remove restricted reuse area from an empty set."); + RestrictedReuseArea result = this->back(); + this->pop_back(); + return result; +} + +CQubitManager::RestrictedReuseArea& CQubitManager::CRestrictedReuseAreaStack::PeekBack() +{ + return this->back(); +} + +int32_t CQubitManager::CRestrictedReuseAreaStack::Count() const +{ + // The size should never exceed int32_t. + return static_cast(this->size()); +} + +// +// CQubitManager +// + +CQubitManager::CQubitManager( + QubitIdType initialQubitCapacity, + bool mayExtendCapacity, + bool encourageReuse): + mayExtendCapacity(mayExtendCapacity), + encourageReuse(encourageReuse), + qubitCapacity(initialQubitCapacity) +{ + FailIf(qubitCapacity <= 0, "Qubit capacity must be positive."); + sharedQubitStatusArray = new QubitIdType[qubitCapacity]; + + // These objects are passed by value (copies are created) + QubitListInSharedArray FreeQubitsFresh(0, qubitCapacity - 1, sharedQubitStatusArray); + RestrictedReuseArea outermostArea(FreeQubitsFresh); + freeQubitsInAreas.PushToBack(outermostArea); + + freeQubitCount = qubitCapacity; +} + +CQubitManager::~CQubitManager() +{ + if (sharedQubitStatusArray != nullptr) + { + delete[] sharedQubitStatusArray; + sharedQubitStatusArray = nullptr; + } + // freeQubitsInAreas - direct member of the class, no need to delete. +} + +// Although it is not necessary to pass area IDs to these functions, such support may be added for extra checks. +void CQubitManager::StartRestrictedReuseArea() +{ + RestrictedReuseArea newArea; + freeQubitsInAreas.PushToBack(newArea); +} + +void CQubitManager::NextRestrictedReuseSegment() +{ + FailIf(freeQubitsInAreas.Count() <= 0, "Internal error! No reuse areas."); + FailIf(freeQubitsInAreas.Count() == 1, "NextRestrictedReuseSegment() without an active area."); + RestrictedReuseArea& currentArea = freeQubitsInAreas.PeekBack(); + // When new segment starts, reuse of all free qubits in the current area becomes prohibited. + currentArea.FreeQubitsReuseProhibited.MoveAllQubitsFrom(currentArea.FreeQubitsReuseAllowed, sharedQubitStatusArray); +} + +void CQubitManager::EndRestrictedReuseArea() +{ + FailIf(freeQubitsInAreas.Count() < 2, "EndRestrictedReuseArea() without an active area."); + RestrictedReuseArea areaAboutToEnd = freeQubitsInAreas.PopFromBack(); + RestrictedReuseArea& containingArea = freeQubitsInAreas.PeekBack(); + // When area ends, reuse of all free qubits from this area becomes allowed. + containingArea.FreeQubitsReuseAllowed.MoveAllQubitsFrom(areaAboutToEnd.FreeQubitsReuseProhibited, sharedQubitStatusArray); + containingArea.FreeQubitsReuseAllowed.MoveAllQubitsFrom(areaAboutToEnd.FreeQubitsReuseAllowed, sharedQubitStatusArray); +} + +Qubit CQubitManager::Allocate() +{ + QubitIdType newQubitId = AllocateQubitId(); + FailIf(newQubitId == NoneMarker, "Not enough qubits."); + return CreateQubitObject(newQubitId); +} + +void CQubitManager::Allocate(Qubit* qubitsToAllocate, int32_t qubitCountToAllocate) +{ + if (qubitCountToAllocate == 0) + { + return; + } + FailIf(qubitCountToAllocate < 0, "Cannot allocate negative number of qubits."); + FailIf(qubitsToAllocate == nullptr, "No array provided for qubits to be allocated."); + + // Consider optimization for initial allocation of a large array at once + for (int32_t i = 0; i < qubitCountToAllocate; i++) + { + QubitIdType newQubitId = AllocateQubitId(); + if (newQubitId == NoneMarker) + { + for (int32_t k = 0; k < i; k++) + { + Release(qubitsToAllocate[k]); + } + FailNow("Not enough qubits."); + } + qubitsToAllocate[i] = CreateQubitObject(newQubitId); + } +} + +void CQubitManager::Release(Qubit qubit) +{ + FailIf(!IsValidQubit(qubit), "Qubit is not valid."); + ReleaseQubitId(QubitToId(qubit)); + DeleteQubitObject(qubit); +} + +void CQubitManager::Release(Qubit* qubitsToRelease, int32_t qubitCountToRelease) { + if (qubitCountToRelease == 0) + { + return; + } + FailIf(qubitCountToRelease < 0, "Cannot release negative number of qubits."); + FailIf(qubitsToRelease == nullptr, "No array provided with qubits to be released."); + + for (int32_t i = 0; i < qubitCountToRelease; i++) + { + Release(qubitsToRelease[i]); + qubitsToRelease[i] = nullptr; + } +} + +Qubit CQubitManager::Borrow() +{ + // We don't support true borrowing/returning at the moment. + return Allocate(); +} + +void CQubitManager::Borrow(Qubit* qubitsToBorrow, int32_t qubitCountToBorrow) +{ + // We don't support true borrowing/returning at the moment. + return Allocate(qubitsToBorrow, qubitCountToBorrow); +} + +void CQubitManager::Return(Qubit qubit) +{ + // We don't support true borrowing/returning at the moment. + Release(qubit); +} + +void CQubitManager::Return(Qubit* qubitsToReturn, int32_t qubitCountToReturn) +{ + // We don't support true borrowing/returning at the moment. + Release(qubitsToReturn, qubitCountToReturn); +} + +void CQubitManager::Disable(Qubit qubit) +{ + FailIf(!IsValidQubit(qubit), "Qubit is not valid."); + QubitIdType id = QubitToId(qubit); + + // We can only disable explicitly allocated qubits that were not borrowed. + FailIf(!IsExplicitlyAllocatedId(id), "Cannot disable qubit that is not explicitly allocated."); + sharedQubitStatusArray[id] = DisabledMarker; + + disabledQubitCount++; + FailIf(disabledQubitCount <= 0, "Incorrect disabled qubit count."); + allocatedQubitCount--; + FailIf(allocatedQubitCount < 0, "Incorrect allocated qubit count."); +} + +void CQubitManager::Disable(Qubit* qubitsToDisable, int32_t qubitCountToDisable) +{ + if (qubitCountToDisable == 0) + { + return; + } + FailIf(qubitCountToDisable < 0, "Cannot disable negative number of qubits."); + FailIf(qubitsToDisable == nullptr, "No array provided with qubits to be disabled."); + + for (int32_t i = 0; i < qubitCountToDisable; i++) + { + Disable(qubitsToDisable[i]); + } +} + +bool CQubitManager::IsValidId(QubitIdType id) const +{ + return (id >= 0) && (id < qubitCapacity); +} + +bool CQubitManager::IsDisabledId(QubitIdType id) const +{ + return sharedQubitStatusArray[id] == DisabledMarker; +} + +bool CQubitManager::IsExplicitlyAllocatedId(QubitIdType id) const +{ + return sharedQubitStatusArray[id] == AllocatedMarker; +} + +bool CQubitManager::IsFreeId(QubitIdType id) const +{ + return sharedQubitStatusArray[id] >= 0; +} + + +bool CQubitManager::IsValidQubit(Qubit qubit) const +{ + return IsValidId(QubitToId(qubit)); +} + +bool CQubitManager::IsDisabledQubit(Qubit qubit) const +{ + return IsValidQubit(qubit) && IsDisabledId(QubitToId(qubit)); +} + +bool CQubitManager::IsExplicitlyAllocatedQubit(Qubit qubit) const +{ + return IsValidQubit(qubit) && IsExplicitlyAllocatedId(QubitToId(qubit)); +} + +bool CQubitManager::IsFreeQubitId(QubitIdType id) const +{ + return IsValidId(id) && IsFreeId(id); +} + +CQubitManager::QubitIdType CQubitManager::GetQubitId(Qubit qubit) const +{ + FailIf(!IsValidQubit(qubit), "Not a valid qubit."); + return QubitToId(qubit); +} + + +Qubit CQubitManager::CreateQubitObject(QubitIdType id) +{ + // Make sure the static_cast won't overflow: + FailIf(id < 0 || id >= MaximumQubitCapacity, "Qubit id is out of range."); + intptr_t pointerSizedId = static_cast(id); + return reinterpret_cast(pointerSizedId); +} + +void CQubitManager::DeleteQubitObject(Qubit /*qubit*/) +{ + // Do nothing. By default we store qubit Id in place of a pointer to a qubit. +} + +CQubitManager::QubitIdType CQubitManager::QubitToId(Qubit qubit) const +{ + intptr_t pointerSizedId = reinterpret_cast(qubit); + // Make sure the static_cast won't overflow: + FailIf(pointerSizedId < 0 || pointerSizedId > std::numeric_limits::max(), "Qubit id is out of range."); + return static_cast(pointerSizedId); +} + +void CQubitManager::EnsureCapacity(QubitIdType requestedCapacity) +{ + FailIf(requestedCapacity <= 0, "Requested qubit capacity must be positive."); + if (requestedCapacity <= qubitCapacity) + { + return; + } + // We need to reallocate shared status array, but there's no need to adjust + // existing values (NonMarker or indexes in the array). + + // Prepare new shared status array + QubitIdType* newStatusArray = new QubitIdType[requestedCapacity]; + memcpy(newStatusArray, sharedQubitStatusArray, qubitCapacity * sizeof(newStatusArray[0])); + QubitListInSharedArray newFreeQubits(qubitCapacity, requestedCapacity - 1, newStatusArray); + + // Set new data. All fresh new qubits are added to the free qubits in the outermost area. + freeQubitCount += requestedCapacity - qubitCapacity; + FailIf(freeQubitCount <= 0, "Incorrect free qubit count."); + delete[] sharedQubitStatusArray; + sharedQubitStatusArray = newStatusArray; + qubitCapacity = requestedCapacity; + freeQubitsInAreas[0].FreeQubitsReuseAllowed.MoveAllQubitsFrom(newFreeQubits, sharedQubitStatusArray); +} + +CQubitManager::QubitIdType CQubitManager::TakeFreeQubitId() +{ + // Possible future optimization: we may store and maintain links to the next + // area with non-empty free list. Need to check amortized complexity... + + QubitIdType id = NoneMarker; + if (encourageReuse) + { + // When reuse is encouraged, we start with the innermost area + for (CRestrictedReuseAreaStack::reverse_iterator rit = freeQubitsInAreas.rbegin(); rit != freeQubitsInAreas.rend(); ++rit) + { + id = rit->FreeQubitsReuseAllowed.TakeQubitFromFront(sharedQubitStatusArray); + if (id != NoneMarker) + { + break; + } + } + } else + { + // When reuse is discouraged, we start with the outermost area + for (CRestrictedReuseAreaStack::iterator it = freeQubitsInAreas.begin(); it != freeQubitsInAreas.end(); ++it) + { + id = it->FreeQubitsReuseAllowed.TakeQubitFromFront(sharedQubitStatusArray); + if (id != NoneMarker) + { + break; + } + } + } + if (id != NoneMarker) { + FailIf(id < 0 || id >= qubitCapacity, "Internal Error: Allocated invalid qubit."); + allocatedQubitCount++; + FailIf(allocatedQubitCount <= 0, "Incorrect allocated qubit count."); + freeQubitCount--; + FailIf(freeQubitCount < 0, "Incorrect free qubit count."); + } + return id; +} + +CQubitManager::QubitIdType CQubitManager::AllocateQubitId() +{ + QubitIdType newQubitId = TakeFreeQubitId(); + if (newQubitId == NoneMarker && mayExtendCapacity) + { + QubitIdType newQubitCapacity = qubitCapacity * 2; + FailIf(newQubitCapacity <= qubitCapacity, "Cannot extend capacity."); + EnsureCapacity(newQubitCapacity); + newQubitId = TakeFreeQubitId(); + } + return newQubitId; +} + +void CQubitManager::ReleaseQubitId(QubitIdType id) +{ + FailIf(id < 0 || id >= qubitCapacity, "Internal Error: Cannot release an invalid qubit."); + if (IsDisabledId(id)) + { + // Nothing to do. Qubit will stay disabled. + return; + } + + FailIf(!IsExplicitlyAllocatedId(id), "Attempt to free qubit that has not been allocated."); + + if (mayExtendCapacity && !encourageReuse) + { + // We can extend capcity and don't want reuse => Qubits will never be reused => Discard qubit. + // We put it in its own "free" list, this list will never be found again and qubit will not be reused. + sharedQubitStatusArray[id] = NoneMarker; + } else + { + // Released qubits are added to reuse area/segment in which they were released + // (rather than area/segment where they are allocated). + // Although counterintuitive, this makes code simple. + // This is reasonable because qubits should be allocated and released in the same segment. (This is not enforced) + freeQubitsInAreas.PeekBack().FreeQubitsReuseAllowed.AddQubit(id, encourageReuse, sharedQubitStatusArray); + } + + freeQubitCount++; + FailIf(freeQubitCount <= 0, "Incorrect free qubit count."); + allocatedQubitCount--; + FailIf(allocatedQubitCount < 0, "Incorrect allocated qubit count."); +} + + +} +} diff --git a/src/Qir/Runtime/lib/QIR/arrays.cpp b/src/Qir/Runtime/lib/QIR/arrays.cpp index 4c58691a3f3..fa8ff504b36 100644 --- a/src/Qir/Runtime/lib/QIR/arrays.cpp +++ b/src/Qir/Runtime/lib/QIR/arrays.cpp @@ -198,7 +198,7 @@ static int64_t GetLinearIndex(const std::vector& dimensionSizes, const // It's equal to the product of the dimension sizes in higher dimensions. static int64_t RunCount(const std::vector& dimensionSizes, int dimension) { - assert(dimension < dimensionSizes.size()); + assert((0 <= dimension) && ((size_t)dimension < dimensionSizes.size())); return std::accumulate(dimensionSizes.begin() + dimension + 1, dimensionSizes.end(), 1, std::multiplies()); } diff --git a/src/Qir/Runtime/lib/QIR/bridge-rt.ll b/src/Qir/Runtime/lib/QIR/bridge-rt.ll index c328a845edf..6bf2efe9cbd 100644 --- a/src/Qir/Runtime/lib/QIR/bridge-rt.ll +++ b/src/Qir/Runtime/lib/QIR/bridge-rt.ll @@ -37,8 +37,6 @@ ;------------------------------------------------------------------------------ ; classical ; -declare i8* @quantum__rt__heap_alloc(i64) -declare void @quantum__rt__heap_free(i8*) declare i8* @quantum__rt__memory_allocate(i64) declare void @quantum__rt__fail(%"struct.QirString"*) @@ -121,15 +119,6 @@ declare void @quantum__rt__message(%"struct.QirString"* %str) ;------------------------------------------------------------------------------ ; classical bridge ; -define dllexport i8* @__quantum__rt__heap_alloc(i64 %size) { - %mem = call i8* @quantum__rt__heap_alloc(i64 %size) - ret i8* %mem -} - -define dllexport void @__quantum__rt__heap_free(i8* %mem) { - call void @quantum__rt__heap_free(i8* %mem) - ret void -} ; Returns a pointer to the malloc-allocated block. define dllexport i8* @__quantum__rt__memory_allocate(i64 %size) { diff --git a/src/Qir/Runtime/lib/QIR/callables.cpp b/src/Qir/Runtime/lib/QIR/callables.cpp index c7db55ea269..78d475e5f88 100644 --- a/src/Qir/Runtime/lib/QIR/callables.cpp +++ b/src/Qir/Runtime/lib/QIR/callables.cpp @@ -8,6 +8,7 @@ #include #include +#include "QirUtils.hpp" #include "QirContext.hpp" #include "QirTypes.hpp" #include "QirRuntime.hpp" @@ -370,7 +371,8 @@ QirTupleHeader* FlattenControlArrays(QirTupleHeader* tuple, int depth) QirArray* controls = current->controls; const size_t blockSize = qubitSize * controls->count; - assert(dst + blockSize <= dstEnd); + assert(dst + blockSize <= dstEnd); + UNUSED(dstEnd); memcpy(dst, controls->buffer, blockSize); dst += blockSize; // in the last iteration the innerTuple isn't valid, but we are not going to use it diff --git a/src/Qir/Runtime/lib/QIR/utils.cpp b/src/Qir/Runtime/lib/QIR/utils.cpp index 0110fd77fd0..9fd473c2d3a 100644 --- a/src/Qir/Runtime/lib/QIR/utils.cpp +++ b/src/Qir/Runtime/lib/QIR/utils.cpp @@ -13,39 +13,9 @@ #include "QirRuntime.hpp" #include "OutputStream.hpp" -static std::unordered_set& UseMemoryTracker() -{ - static std::unordered_set memoryTracker; - return memoryTracker; -} extern "C" { - // Allocate a block of memory on the heap. - char* quantum__rt__heap_alloc(uint64_t size) // NOLINT - { - char* buffer = new (std::nothrow) char[size]; - if(buffer == nullptr) - { - quantum__rt__fail(quantum__rt__string_create("Allocation Failed")); - } - #ifndef NDEBUG - UseMemoryTracker().insert(buffer); - #endif - return buffer; - } - - // Release a block of allocated heap memory. - void quantum__rt__heap_free(char* buffer) // NOLINT - { - #ifndef NDEBUG - auto iter = UseMemoryTracker().find(buffer); - assert(iter != UseMemoryTracker().end()); - UseMemoryTracker().erase(iter); - #endif - delete[] buffer; - } - char* quantum__rt__memory_allocate(uint64_t size) { return (char *)malloc((size_t)size); diff --git a/src/Qir/Runtime/lib/QSharpFoundation/AssertMeasurement.cpp b/src/Qir/Runtime/lib/QSharpFoundation/AssertMeasurement.cpp index 8f05e89ce2a..a18f85b17f9 100644 --- a/src/Qir/Runtime/lib/QSharpFoundation/AssertMeasurement.cpp +++ b/src/Qir/Runtime/lib/QSharpFoundation/AssertMeasurement.cpp @@ -36,7 +36,7 @@ extern "C" // Convert paulis from sequence of bytes to sequence of PauliId: std::vector paulis(bases->count); - for(size_t i = 0; i < bases->count; ++i) + for(int64_t i = 0; i < bases->count; ++i) { paulis[i] = (PauliId)(bases->buffer[i]); } diff --git a/src/Qir/Runtime/lib/Simulators/CMakeLists.txt b/src/Qir/Runtime/lib/Simulators/CMakeLists.txt index 589b53283e2..4277958afbb 100644 --- a/src/Qir/Runtime/lib/Simulators/CMakeLists.txt +++ b/src/Qir/Runtime/lib/Simulators/CMakeLists.txt @@ -13,6 +13,9 @@ set(includes # Produce object lib we'll use to create a shared lib (so/dll) later on add_library(simulators-obj OBJECT ${source_files}) -target_include_directories(simulators-obj PUBLIC ${includes}) +target_include_directories(simulators-obj PUBLIC + ${includes} + ${common_includes} +) set_property(TARGET simulators-obj PROPERTY POSITION_INDEPENDENT_CODE ON) target_compile_definitions(simulators-obj PRIVATE EXPORT_QIR_API) diff --git a/src/Qir/Runtime/lib/Simulators/FullstateSimulator.cpp b/src/Qir/Runtime/lib/Simulators/FullstateSimulator.cpp index 6d9103d6779..e242cfec42d 100644 --- a/src/Qir/Runtime/lib/Simulators/FullstateSimulator.cpp +++ b/src/Qir/Runtime/lib/Simulators/FullstateSimulator.cpp @@ -17,6 +17,7 @@ #include "QSharpSimApi_I.hpp" #include "SimFactory.hpp" #include "OutputStream.hpp" +#include "QubitManager.hpp" #ifdef _WIN32 #include @@ -58,14 +59,6 @@ QUANTUM_SIMULATOR LoadQuantumSimulator() return handle; } -bool UnloadQuantumSimulator(QUANTUM_SIMULATOR handle) -{ -#ifdef _WIN32 - return ::FreeLibrary(handle); -#else // not _WIN32 - return ::dlclose(handle); -#endif -} void* LoadProc(QUANTUM_SIMULATOR handle, const char* procName) { @@ -107,11 +100,13 @@ namespace Quantum const QUANTUM_SIMULATOR handle = 0; unsigned simulatorId = -1; - unsigned nextQubitId = 0; // the QuantumSimulator expects contiguous ids, starting from 0 + // the QuantumSimulator expects contiguous ids, starting from 0 + std::unique_ptr qubitManager; unsigned GetQubitId(Qubit qubit) const { - return static_cast(reinterpret_cast(qubit)); + // Qubit manager uses unsigned range of int32_t for qubit ids. + return static_cast(qubitManager->GetQubitId(qubit)); } std::vector GetQubitIds(long num, Qubit* qubits) const @@ -120,7 +115,7 @@ namespace Quantum ids.reserve(num); for (long i = 0; i < num; i++) { - ids.push_back(static_cast(reinterpret_cast(qubits[i]))); + ids.push_back(GetQubitId(qubits[i])); } return ids; } @@ -156,11 +151,12 @@ namespace Quantum typedef unsigned (*TInit)(); static TInit initSimulatorInstance = reinterpret_cast(this->GetProc("init")); + qubitManager = std::make_unique(); this->simulatorId = initSimulatorInstance(); } ~CFullstateSimulator() { - if (this->simulatorId != -1) + if (this->simulatorId != (unsigned)-1) { typedef unsigned (*TDestroy)(unsigned); static TDestroy destroySimulatorInstance = @@ -195,10 +191,10 @@ namespace Quantum typedef void (*TAllocateQubit)(unsigned, unsigned); static TAllocateQubit allocateQubit = reinterpret_cast(this->GetProc("allocateQubit")); - const unsigned id = this->nextQubitId; - allocateQubit(this->simulatorId, id); - this->nextQubitId++; - return reinterpret_cast(id); + Qubit q = qubitManager->Allocate(); // Allocate qubit in qubit manager. + unsigned id = GetQubitId(q); // Get its id. + allocateQubit(this->simulatorId, id); // Allocate it in the simulator. + return q; } void ReleaseQubit(Qubit q) override @@ -206,7 +202,8 @@ namespace Quantum typedef void (*TReleaseQubit)(unsigned, unsigned); static TReleaseQubit releaseQubit = reinterpret_cast(this->GetProc("release")); - releaseQubit(this->simulatorId, GetQubitId(q)); + releaseQubit(this->simulatorId, GetQubitId(q)); // Release qubit in the simulator. + qubitManager->Release(q); // Release it in the qubit manager. } Result Measure(long numBases, PauliId bases[], long numTargets, Qubit targets[]) override @@ -219,7 +216,7 @@ namespace Quantum m(this->simulatorId, numBases, reinterpret_cast(bases), ids.data())); } - void ReleaseResult(Result r) override {} + void ReleaseResult(Result /*r*/) override {} ResultValue GetResultValue(Result r) override { @@ -403,7 +400,7 @@ namespace Quantum Qubit targets[], double probabilityOfZero, double precision, - const char* failureMessage) override + const char* /*failureMessage*/) override { typedef double (*TOp)(unsigned id, unsigned n, int* b, unsigned* q); static TOp jointEnsembleProbability = reinterpret_cast(this->GetProc("JointEnsembleProbability")); diff --git a/src/Qir/Runtime/lib/Simulators/ToffoliSimulator.cpp b/src/Qir/Runtime/lib/Simulators/ToffoliSimulator.cpp index f02742fcfd1..b1556e1ebf8 100644 --- a/src/Qir/Runtime/lib/Simulators/ToffoliSimulator.cpp +++ b/src/Qir/Runtime/lib/Simulators/ToffoliSimulator.cpp @@ -5,6 +5,7 @@ #include #include +#include "QirUtils.hpp" #include "QirRuntimeApi_I.hpp" #include "QSharpSimApi_I.hpp" #include "SimFactory.hpp" @@ -42,7 +43,7 @@ namespace Quantum /// /// Implementation of IRuntimeDriver /// - void ReleaseResult(Result result) override {} + void ReleaseResult(Result /* result */) override {} bool AreEqualResults(Result r1, Result r2) override { @@ -75,6 +76,7 @@ namespace Quantum const long id = GetQubitId(qubit); assert(id <= this->lastUsedId); assert(!this->states.at(id)); + UNUSED(id); this->lastUsedId--; this->states.pop_back(); } @@ -88,7 +90,7 @@ namespace Quantum /// /// Implementation of IDiagnostics /// - bool Assert(long numTargets, PauliId* bases, Qubit* targets, Result result, const char* failureMessage) override + bool Assert(long numTargets, PauliId* bases, Qubit* targets, Result result, const char* /* failureMessage */) override { // Measurements in Toffoli simulator don't change the state. // TODO: log failureMessage? @@ -101,7 +103,7 @@ namespace Quantum Qubit targets[], double probabilityOfZero, double precision, - const char* failureMessage) override + const char* /* failureMessage */) override { assert(precision >= 0); @@ -111,17 +113,17 @@ namespace Quantum } // Deprecated, use `DumpMachine()` and `DumpRegister()` instead. - void GetState(TGetStateCallback callback) override + void GetState(TGetStateCallback /* callback */) override { throw std::logic_error("operation_not_supported"); } - void DumpMachine(const void* location) override + void DumpMachine(const void* /* location */) override { std::cerr << __func__ << " is not yet implemented" << std::endl; // #645 } - void DumpRegister(const void* location, const QirArray* qubits) override + void DumpRegister(const void* /* location */, const QirArray* /* qubits */) override { std::cerr << __func__ << " is not yet implemented" << std::endl; // #645 } @@ -154,7 +156,7 @@ namespace Quantum } - Result Measure(long numBases, PauliId bases[], long numTargets, Qubit targets[]) override + Result Measure(long numBases, PauliId bases[], long /* numTargets */, Qubit targets[]) override { bool odd = false; for (long i = 0; i < numBases; i++) @@ -175,81 +177,81 @@ namespace Quantum // // The rest of the gate set Toffoli simulator doesn't support // - void Y(Qubit target) override + void Y(Qubit /*target*/) override { throw std::logic_error("operation_not_supported"); } - void Z(Qubit target) override + void Z(Qubit /*target*/) override { throw std::logic_error("operation_not_supported"); } - void H(Qubit target) override + void H(Qubit /*target*/) override { throw std::logic_error("operation_not_supported"); } - void S(Qubit target) override + void S(Qubit /*target*/) override { throw std::logic_error("operation_not_supported"); } - void T(Qubit target) override + void T(Qubit /*target*/) override { throw std::logic_error("operation_not_supported"); } - void R(PauliId axis, Qubit target, double theta) override + void R(PauliId /* axis */, Qubit /*target*/, double /* theta */) override { throw std::logic_error("operation_not_supported"); } - void Exp(long numTargets, PauliId paulis[], Qubit targets[], double theta) override + void Exp(long /* numTargets */, PauliId* /* paulis */, Qubit* /*targets*/, double /* theta */) override { throw std::logic_error("operation_not_supported"); } - void ControlledY(long numControls, Qubit controls[], Qubit target) override + void ControlledY(long /*numControls*/, Qubit* /*controls*/, Qubit /*target*/) override { throw std::logic_error("operation_not_supported"); } - void ControlledZ(long numControls, Qubit controls[], Qubit target) override + void ControlledZ(long /*numControls*/, Qubit* /*controls*/, Qubit /*target*/) override { throw std::logic_error("operation_not_supported"); } - void ControlledH(long numControls, Qubit controls[], Qubit target) override + void ControlledH(long /*numControls*/, Qubit* /*controls*/, Qubit /*target*/) override { throw std::logic_error("operation_not_supported"); } - void ControlledS(long numControls, Qubit controls[], Qubit target) override + void ControlledS(long /*numControls*/, Qubit* /*controls*/, Qubit /*target*/) override { throw std::logic_error("operation_not_supported"); } - void ControlledT(long numControls, Qubit controls[], Qubit target) override + void ControlledT(long /*numControls*/, Qubit* /*controls*/, Qubit /*target*/) override { throw std::logic_error("operation_not_supported"); } - void ControlledR(long numControls, Qubit controls[], PauliId axis, Qubit target, double theta) override + void ControlledR(long /*numControls*/, Qubit* /*controls*/, PauliId /*axis*/, Qubit /*target*/, double /*theta*/) override { throw std::logic_error("operation_not_supported"); } void ControlledExp( - long numControls, - Qubit controls[], - long numTargets, - PauliId paulis[], - Qubit targets[], - double theta) override + long /*numControls*/, + Qubit* /*controls*/, + long /*numTargets*/, + PauliId* /*paulis*/, + Qubit* /*targets*/, + double /* theta */) override { throw std::logic_error("operation_not_supported"); } - void AdjointS(Qubit target) override + void AdjointS(Qubit /*target*/) override { throw std::logic_error("operation_not_supported"); } - void AdjointT(Qubit target) override + void AdjointT(Qubit /*target*/) override { throw std::logic_error("operation_not_supported"); } - void ControlledAdjointS(long numControls, Qubit controls[], Qubit target) override + void ControlledAdjointS(long /*numControls*/, Qubit* /*controls*/, Qubit /*target*/) override { throw std::logic_error("operation_not_supported"); } - void ControlledAdjointT(long numControls, Qubit controls[], Qubit target) override + void ControlledAdjointT(long /*numControls*/, Qubit* /*controls*/, Qubit /*target*/) override { throw std::logic_error("operation_not_supported"); } diff --git a/src/Qir/Runtime/lib/Tracer/tracer-qis.cpp b/src/Qir/Runtime/lib/Tracer/tracer-qis.cpp index dd608602215..37f6a671190 100644 --- a/src/Qir/Runtime/lib/Tracer/tracer-qis.cpp +++ b/src/Qir/Runtime/lib/Tracer/tracer-qis.cpp @@ -18,14 +18,14 @@ namespace Quantum using namespace Microsoft::Quantum; extern "C" { - void quantum__qis__on_operation_start(int64_t id) // NOLINT + void quantum__qis__on_operation_start(int64_t /* id */) // NOLINT { } - void quantum__qis__on_operation_end(int64_t id) // NOLINT + void quantum__qis__on_operation_end(int64_t /* id */) // NOLINT { } - void quantum__qis__swap(Qubit q1, Qubit q2) // NOLINT + void quantum__qis__swap(Qubit /*q1*/, Qubit /*q2*/) // NOLINT { } diff --git a/src/Qir/Runtime/lib/Tracer/tracer.cpp b/src/Qir/Runtime/lib/Tracer/tracer.cpp index c1cbe70f4bd..a4ccfbb0eae 100644 --- a/src/Qir/Runtime/lib/Tracer/tracer.cpp +++ b/src/Qir/Runtime/lib/Tracer/tracer.cpp @@ -116,7 +116,7 @@ namespace Quantum const LayerId barrier = this->GetEffectiveFence(); const LayerId firstLayerAfterBarrier = (barrier == INVALID ? (this->metricsByLayer.empty() ? REQUESTNEW : 0) - : ((barrier + 1 == this->metricsByLayer.size()) ? REQUESTNEW : barrier + 1)); + : (((size_t)(barrier + 1) == this->metricsByLayer.size()) ? REQUESTNEW : barrier + 1)); LayerId candidate = CTracer::LaterLayerOf(qstate.layer, firstLayerAfterBarrier); assert(candidate != INVALID); @@ -132,7 +132,7 @@ namespace Quantum } else { - for (candidate += 1; candidate < this->metricsByLayer.size(); ++candidate) + for (candidate += 1; (size_t)candidate < this->metricsByLayer.size(); ++candidate) { if (opDuration <= this->metricsByLayer[candidate].duration) { @@ -151,7 +151,7 @@ namespace Quantum //------------------------------------------------------------------------------------------------------------------ void CTracer::AddOperationToLayer(OpId id, LayerId layer) { - assert(layer < this->metricsByLayer.size()); + assert((size_t)layer < this->metricsByLayer.size()); assert(this->metricsByLayer[layer].barrierId == -1 && "Should not add operations to barriers"); this->metricsByLayer[layer].operations[id] += 1; @@ -319,7 +319,7 @@ namespace Quantum { return; } - assert(this->fence < this->tracer->metricsByLayer.size()); + assert((size_t)(this->fence) < this->tracer->metricsByLayer.size()); this->tracer->conditionalFences.push_back(this->fence); this->tracer->latestConditionalFence = CTracer::LaterLayerOf(this->tracer->latestConditionalFence, this->fence); diff --git a/src/Qir/Runtime/lib/Tracer/tracer.hpp b/src/Qir/Runtime/lib/Tracer/tracer.hpp index 35f242f1ecc..a49a9b9166e 100644 --- a/src/Qir/Runtime/lib/Tracer/tracer.hpp +++ b/src/Qir/Runtime/lib/Tracer/tracer.hpp @@ -148,11 +148,11 @@ namespace Quantum std::string QubitToString(Qubit qubit) override; void ReleaseResult(Result result) override; - bool AreEqualResults(Result r1, Result r2) override + bool AreEqualResults(Result /*r1*/, Result /*r2*/) override { throw std::logic_error("Cannot compare results while tracing!"); } - ResultValue GetResultValue(Result result) override + ResultValue GetResultValue(Result /*result*/) override { throw std::logic_error("Result values aren't available while tracing!"); } diff --git a/src/Qir/Runtime/public/QubitManager.hpp b/src/Qir/Runtime/public/QubitManager.hpp new file mode 100644 index 00000000000..c45a74ce292 --- /dev/null +++ b/src/Qir/Runtime/public/QubitManager.hpp @@ -0,0 +1,255 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +#pragma once + +#include +#include +#include + +#include "CoreTypes.hpp" + +namespace Microsoft +{ +namespace Quantum +{ + // CQubitManager maintains mapping between user qubit objects and + // underlying qubit identifiers (Ids). When user program allocates + // a qubit, Qubit Manager decides whether to allocate a fresh id or + // reuse existing id that was previously freed. When user program + // releases a qubit, Qubit Manager tracks it as a free qubit id. + // Decision to reuse a qubit id is influenced by restricted reuse + // areas. When a qubit id is freed in one section of a restricted + // reuse area, it cannot be reused in other sections of the same area. + // True borrowing of qubits is not supported and is currently + // implemented as a plain allocation. + class QIR_SHARED_API CQubitManager + { + public: + using QubitIdType = ::int32_t; + + // We want status array to be reasonably large. + constexpr static QubitIdType DefaultQubitCapacity = 8; + + // Indexes in the status array can potentially be in range 0 .. QubitIdType.MaxValue-1. + // This gives maximum capacity as QubitIdType.MaxValue. Actual configured capacity may be less than this. + // Index equal to QubitIdType.MaxValue doesn't exist and is reserved for 'NoneMarker' - list terminator. + constexpr static QubitIdType MaximumQubitCapacity = std::numeric_limits::max(); + + public: + CQubitManager( + QubitIdType initialQubitCapacity = DefaultQubitCapacity, + bool mayExtendCapacity = true, + bool encourageReuse = true); + + // No complex scenarios for now. Don't need to support copying/moving. + CQubitManager(const CQubitManager&) = delete; + CQubitManager& operator = (const CQubitManager&) = delete; + virtual ~CQubitManager(); + + // Restricted reuse area control + void StartRestrictedReuseArea(); + void NextRestrictedReuseSegment(); + void EndRestrictedReuseArea(); + + // Allocate a qubit. Extend capacity if necessary and possible. + // Fail if the qubit cannot be allocated. + // Computation complexity is O(number of nested restricted reuse areas). + Qubit Allocate(); + // Allocate qubitCountToAllocate qubits and store them in the provided array. Extend manager capacity if necessary and possible. + // Fail without allocating any qubits if the qubits cannot be allocated. + // Caller is responsible for providing array of sufficient size to hold qubitCountToAllocate. + void Allocate(Qubit* qubitsToAllocate, int32_t qubitCountToAllocate); + + // Releases a given qubit. + void Release(Qubit qubit); + // Releases qubitCountToRelease qubits in the provided array. + // Caller is responsible for managing memory used by the array itself (i.e. delete[] array if it was dynamically allocated). + void Release(Qubit* qubitsToRelease, int32_t qubitCountToRelease); + + // Borrow (We treat borrowing as allocation currently) + Qubit Borrow(); + void Borrow(Qubit* qubitsToBorrow, int32_t qubitCountToBorrow); + // Return (We treat returning as release currently) + void Return(Qubit qubit); + void Return(Qubit* qubitsToReturn, int32_t qubitCountToReturn); + + // Disables a given qubit. + // Once a qubit is disabled it can never be "enabled" or reallocated. + void Disable(Qubit qubit); + // Disables a set of given qubits. + // Once a qubit is disabled it can never be "enabled" or reallocated. + void Disable(Qubit* qubitsToDisable, int32_t qubitCountToDisable); + + bool IsValidQubit(Qubit qubit) const; + bool IsDisabledQubit(Qubit qubit) const; + bool IsExplicitlyAllocatedQubit(Qubit qubit) const; + bool IsFreeQubitId(QubitIdType id) const; + + QubitIdType GetQubitId(Qubit qubit) const; + + // Qubit counts: + + // Number of qubits that are disabled. When an explicitly allocated qubit + // gets disabled, it is removed from allocated count and is added to + // disabled count immediately. Subsequent Release doesn't affect counts. + int32_t GetDisabledQubitCount() const { return disabledQubitCount; } + + // Number of qubits that are explicitly allocated. This counter gets + // increased on allocation of a qubit and decreased on release of a qubit. + // Note that we treat borrowing as allocation now. + int32_t GetAllocatedQubitCount() const { return allocatedQubitCount; } + + // Number of free qubits that are currently tracked by this qubit manager. + // Note that when qubit manager may extend capacity, this doesn't account + // for qubits that may be potentially added in future via capacity extension. + // If qubit manager may extend capacity and reuse is discouraged, released + // qubits still increase this number even though they cannot be reused. + int32_t GetFreeQubitCount() const { return freeQubitCount; } + + // Total number of qubits that are currently tracked by this qubit manager. + int32_t GetQubitCapacity() const { return qubitCapacity; } + bool GetMayExtendCapacity() const { return mayExtendCapacity; } + bool GetEncourageReuse() const { return encourageReuse; } + + protected: + // May be overriden to create a custom Qubit object. + // When not overriden, it just stores qubit Id in place of a pointer to a qubit. + // id: unique qubit id + // Returns a newly instantiated qubit. + virtual Qubit CreateQubitObject(QubitIdType id); + + // May be overriden to delete a custom Qubit object. + // Must be overriden if CreateQubitObject is overriden. + // When not overriden, it does nothing. + // qubit: pointer to QUBIT + virtual void DeleteQubitObject(Qubit qubit); + + // May be overriden to get a qubit id from a custom qubit object. + // Must be overriden if CreateQubitObject is overriden. + // When not overriden, it just reinterprets pointer to qubit as a qubit id. + // qubit: pointer to QUBIT + // Returns id of a qubit pointed to by qubit. + virtual QubitIdType QubitToId(Qubit qubit) const; + + private: + // The end of free lists are marked with NoneMarker value. It is used like null for pointers. + // This value is non-negative just like other values in the free lists. See sharedQubitStatusArray. + constexpr static QubitIdType NoneMarker = std::numeric_limits::max(); + + // Explicitly allocated qubits are marked with AllocatedMarker value. + // If borrowing is implemented, negative values may be used for refcounting. + // See sharedQubitStatusArray. + constexpr static QubitIdType AllocatedMarker = std::numeric_limits::min(); + + // Disabled qubits are marked with this value. See sharedQubitStatusArray. + constexpr static QubitIdType DisabledMarker = -1; + + // QubitListInSharedArray implements a singly-linked list with "pointers" + // to the first and the last element stored. Pointers are the indexes + // in a single shared array. Shared array isn't sotored in this class + // because it can be reallocated. This class maintains status of elements + // in the list by virtue of linking them as part of this list. This class + // sets Allocated status of elementes taken from the list (via TakeQubitFromFront). + // This class is small, contains no C++ pointers and relies on default shallow copying/destruction. + struct QubitListInSharedArray final + { + private: + QubitIdType firstElement = NoneMarker; + QubitIdType lastElement = NoneMarker; + // We are not storing pointer to shared array because it can be reallocated. + // Indexes and special values remain the same on such reallocations. + + public: + // Initialize empty list + QubitListInSharedArray() = default; + + // Initialize as a list with sequential elements from startId to endId inclusve. + QubitListInSharedArray(QubitIdType startId, QubitIdType endId, QubitIdType* sharedQubitStatusArray); + + bool IsEmpty() const; + void AddQubit(QubitIdType id, bool addToFront, QubitIdType* sharedQubitStatusArray); + QubitIdType TakeQubitFromFront(QubitIdType* sharedQubitStatusArray); + void MoveAllQubitsFrom(QubitListInSharedArray& source, QubitIdType* sharedQubitStatusArray); + }; + + // Restricted reuse area consists of multiple segments. Qubits released + // in one segment cannot be reused in another. One restricted reuse area + // can be nested in a segment of another restricted reuse area. This class + // tracks current segment of an area by maintaining a list of free qubits + // in a shared status array FreeQubitsReuseAllowed. Previous segments are + // tracked collectively (not individually) by maintaining FreeQubitsReuseProhibited. + // This class is small, contains no C++ pointers and relies on default shallow copying/destruction. + struct RestrictedReuseArea final + { + public: + QubitListInSharedArray FreeQubitsReuseProhibited; + QubitListInSharedArray FreeQubitsReuseAllowed; + + RestrictedReuseArea() = default; + RestrictedReuseArea(QubitListInSharedArray freeQubits); + }; + + // This is NOT a pure stack! We modify it only by push/pop, but we also iterate over elements. + class CRestrictedReuseAreaStack final : public std::vector + { + public: + // No complex scenarios for now. Don't need to support copying/moving. + CRestrictedReuseAreaStack() = default; + CRestrictedReuseAreaStack(const CRestrictedReuseAreaStack&) = delete; + CRestrictedReuseAreaStack& operator = (const CRestrictedReuseAreaStack&) = delete; + ~CRestrictedReuseAreaStack() = default; + + void PushToBack(RestrictedReuseArea area); + RestrictedReuseArea PopFromBack(); + RestrictedReuseArea& PeekBack(); + int32_t Count() const; + }; + + private: + void EnsureCapacity(QubitIdType requestedCapacity); + + // Take free qubit id from a free list without extending capacity. + // First non-empty free list among nested restricted reuse areas are considered. + QubitIdType TakeFreeQubitId(); + // Allocate free qubit id extending capacity if necessary and possible. + QubitIdType AllocateQubitId(); + // Put qubit id back into a free list for the current restricted reuse area. + void ReleaseQubitId(QubitIdType id); + + bool IsValidId(QubitIdType id) const; + bool IsDisabledId(QubitIdType id) const; + bool IsFreeId(QubitIdType id) const; + bool IsExplicitlyAllocatedId(QubitIdType id) const; + + // Configuration Properties: + bool mayExtendCapacity = true; + bool encourageReuse = true; + + // State: + // sharedQubitStatusArray is used to store statuses of all known qubits. + // Integer value at the index of the qubit id represents the status of that qubit. + // (Ex: sharedQubitStatusArray[4] is the status of qubit with id = 4). + // Therefore qubit ids are in the range of [0..qubitCapacity). + // Capacity may be extended if MayExtendCapacity = true. + // If qubit X is allocated, sharedQubitStatusArray[X] = AllocatedMarker (negative number) + // If qubit X is disabled, sharedQubitStatusArray[X] = DisabledMarker (negative number) + // If qubit X is free, sharedQubitStatusArray[X] is a non-negative number, denote it Next(X). + // Next(X) is either the index of the next element in the list or the list terminator - NoneMarker. + // All free qubits form disjoint singly linked lists bound to to respective resricted reuse areas. + // Each area has two lists of free qubits - see RestrictedReuseArea. + QubitIdType* sharedQubitStatusArray = nullptr; + // qubitCapacity is always equal to the array size. + QubitIdType qubitCapacity = 0; + // All nested restricted reuse areas at the current moment. + // Fresh Free Qubits are added to the outermost area: freeQubitsInAreas[0].FreeQubitsReuseAllowed + CRestrictedReuseAreaStack freeQubitsInAreas; + + // Counts: + int32_t disabledQubitCount = 0; + int32_t allocatedQubitCount = 0; + int32_t freeQubitCount = 0; + }; + +} +} diff --git a/src/Qir/Runtime/unittests/CMakeLists.txt b/src/Qir/Runtime/unittests/CMakeLists.txt index 40c41776d2a..664fc359a8f 100644 --- a/src/Qir/Runtime/unittests/CMakeLists.txt +++ b/src/Qir/Runtime/unittests/CMakeLists.txt @@ -8,6 +8,7 @@ add_executable(qir-runtime-unittests QirRuntimeTests.cpp ToffoliTests.cpp TracerTests.cpp + QubitManagerTests.cpp $ $ $ diff --git a/src/Qir/Runtime/unittests/QirRuntimeTests.cpp b/src/Qir/Runtime/unittests/QirRuntimeTests.cpp index 66127fb69ae..8d668a7fd9f 100644 --- a/src/Qir/Runtime/unittests/QirRuntimeTests.cpp +++ b/src/Qir/Runtime/unittests/QirRuntimeTests.cpp @@ -65,7 +65,7 @@ struct ResultsReferenceCountingTestQAPI : public SimulatorStub bool HaveResultsInFlight() const { - for (const auto& b : this->allocated) + for (const auto b : this->allocated) { if (b) { @@ -716,7 +716,7 @@ struct QubitTestQAPI : public SimulatorStub bool HaveQubitsInFlight() const { - for (const auto& b : this->allocated) + for (const auto b : this->allocated) { if (b) { @@ -768,7 +768,7 @@ struct ControlledCallablesTestSimulator : public SimulatorStub { return reinterpret_cast(++this->lastId); } - void ReleaseQubit(Qubit qubit) override {} + void ReleaseQubit(Qubit /*qubit*/) override {} Result UseZero() override { return reinterpret_cast(0); @@ -984,7 +984,7 @@ struct AdjointsTestSimulator : public SimulatorStub { return reinterpret_cast(++this->lastId); } - void ReleaseQubit(Qubit qubit) override {} + void ReleaseQubit(Qubit /*qubit*/) override {} Result UseZero() override { return reinterpret_cast(0); diff --git a/src/Qir/Runtime/unittests/QubitManagerTests.cpp b/src/Qir/Runtime/unittests/QubitManagerTests.cpp new file mode 100644 index 00000000000..966b36f9645 --- /dev/null +++ b/src/Qir/Runtime/unittests/QubitManagerTests.cpp @@ -0,0 +1,253 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +#include + +#include "catch.hpp" + +#include "QubitManager.hpp" + +using namespace Microsoft::Quantum; + +TEST_CASE("Simple allocation and release of one qubit", "[QubitManagerBasic]") +{ + std::unique_ptr qm = std::make_unique(); + Qubit q = qm->Allocate(); + qm->Release(q); +} + +TEST_CASE("Allocation and reallocation of one qubit", "[QubitManagerBasic]") +{ + std::unique_ptr qm = std::make_unique(1, false, true); + REQUIRE(qm->GetFreeQubitCount() == 1); + REQUIRE(qm->GetAllocatedQubitCount() == 0); + Qubit q = qm->Allocate(); + REQUIRE(qm->GetQubitId(q) == 0); + REQUIRE(qm->GetFreeQubitCount() == 0); + REQUIRE(qm->GetAllocatedQubitCount() == 1); + REQUIRE_THROWS(qm->Allocate()); + REQUIRE(qm->GetFreeQubitCount() == 0); + REQUIRE(qm->GetAllocatedQubitCount() == 1); + qm->Release(q); + REQUIRE(qm->GetFreeQubitCount() == 1); + REQUIRE(qm->GetAllocatedQubitCount() == 0); + Qubit q0 = qm->Allocate(); + REQUIRE(qm->GetQubitId(q0) == 0); + REQUIRE(qm->GetFreeQubitCount() == 0); + REQUIRE(qm->GetAllocatedQubitCount() == 1); + qm->Release(q0); + REQUIRE(qm->GetFreeQubitCount() == 1); + REQUIRE(qm->GetAllocatedQubitCount() == 0); +} + +TEST_CASE("Qubit Status", "[QubitManagerBasic]") +{ + std::unique_ptr qm = std::make_unique(2, false, true); + + Qubit q0 = qm->Allocate(); + CQubitManager::QubitIdType q0id = qm->GetQubitId(q0); + REQUIRE(qm->IsValidQubit(q0)); + REQUIRE(qm->IsExplicitlyAllocatedQubit(q0)); + REQUIRE(!qm->IsDisabledQubit(q0)); + REQUIRE(!qm->IsFreeQubitId(q0id)); + REQUIRE(qm->GetAllocatedQubitCount() == 1); + + qm->Disable(q0); + REQUIRE(qm->IsValidQubit(q0)); + REQUIRE(!qm->IsExplicitlyAllocatedQubit(q0)); + REQUIRE(qm->IsDisabledQubit(q0)); + REQUIRE(!qm->IsFreeQubitId(q0id)); + REQUIRE(qm->GetDisabledQubitCount() == 1); + + qm->Release(q0); + REQUIRE(!qm->IsFreeQubitId(q0id)); + REQUIRE(qm->GetFreeQubitCount() == 1); + + Qubit q1 = qm->Allocate(); + CQubitManager::QubitIdType q1id = qm->GetQubitId(q1); + REQUIRE(q0id != q1id); + + REQUIRE(qm->IsValidQubit(q1)); + REQUIRE(qm->IsExplicitlyAllocatedQubit(q1)); + REQUIRE(!qm->IsDisabledQubit(q1)); + REQUIRE(!qm->IsFreeQubitId(q1id)); + + REQUIRE(qm->GetAllocatedQubitCount() == 1); + REQUIRE(qm->GetDisabledQubitCount() == 1); + REQUIRE(qm->GetFreeQubitCount() == 0); + + qm->Release(q1); + REQUIRE(qm->IsFreeQubitId(q1id)); +} + +TEST_CASE("Qubit Counts", "[QubitManagerBasic]") +{ + constexpr int totalQubitCount = 100; + constexpr int disabledQubitCount = 29; + constexpr int extraQubitCount = 43; + static_assert(extraQubitCount <= totalQubitCount); + static_assert(disabledQubitCount <= totalQubitCount); + // We don't want capacity to be extended at first... + static_assert(extraQubitCount + disabledQubitCount <= totalQubitCount); + + std::unique_ptr qm = std::make_unique(totalQubitCount, true, true); + REQUIRE(qm->GetQubitCapacity() == totalQubitCount); + REQUIRE(qm->GetFreeQubitCount() == totalQubitCount); + REQUIRE(qm->GetAllocatedQubitCount() == 0); + REQUIRE(qm->GetDisabledQubitCount() == 0); + + Qubit* qubits = new Qubit[disabledQubitCount]; + qm->Allocate(qubits, disabledQubitCount); + REQUIRE(qm->GetQubitCapacity() == totalQubitCount); + REQUIRE(qm->GetFreeQubitCount() == totalQubitCount-disabledQubitCount); + REQUIRE(qm->GetAllocatedQubitCount() == disabledQubitCount); + REQUIRE(qm->GetDisabledQubitCount() == 0); + + qm->Disable(qubits, disabledQubitCount); + REQUIRE(qm->GetQubitCapacity() == totalQubitCount); + REQUIRE(qm->GetFreeQubitCount() == totalQubitCount-disabledQubitCount); + REQUIRE(qm->GetAllocatedQubitCount() == 0); + REQUIRE(qm->GetDisabledQubitCount() == disabledQubitCount); + + qm->Release(qubits, disabledQubitCount); + REQUIRE(qm->GetQubitCapacity() == totalQubitCount); + REQUIRE(qm->GetFreeQubitCount() == totalQubitCount-disabledQubitCount); + REQUIRE(qm->GetAllocatedQubitCount() == 0); + REQUIRE(qm->GetDisabledQubitCount() == disabledQubitCount); + delete[] qubits; + + qubits = new Qubit[extraQubitCount]; + qm->Allocate(qubits, extraQubitCount); + REQUIRE(qm->GetQubitCapacity() == totalQubitCount); + REQUIRE(qm->GetFreeQubitCount() == totalQubitCount-disabledQubitCount-extraQubitCount); + REQUIRE(qm->GetAllocatedQubitCount() == extraQubitCount); + REQUIRE(qm->GetDisabledQubitCount() == disabledQubitCount); + + qm->Release(qubits, extraQubitCount); + REQUIRE(qm->GetQubitCapacity() == totalQubitCount); + REQUIRE(qm->GetFreeQubitCount() == totalQubitCount-disabledQubitCount); + REQUIRE(qm->GetAllocatedQubitCount() == 0); + REQUIRE(qm->GetDisabledQubitCount() == disabledQubitCount); + delete[] qubits; + + qubits = new Qubit[totalQubitCount]; + qm->Allocate(qubits, totalQubitCount); + REQUIRE(qm->GetQubitCapacity() > totalQubitCount); + REQUIRE(qm->GetAllocatedQubitCount() == totalQubitCount); + REQUIRE(qm->GetDisabledQubitCount() == disabledQubitCount); + + qm->Release(qubits, totalQubitCount); + REQUIRE(qm->GetQubitCapacity() > totalQubitCount); + REQUIRE(qm->GetAllocatedQubitCount() == 0); + REQUIRE(qm->GetDisabledQubitCount() == disabledQubitCount); + delete[] qubits; +} + +TEST_CASE("Allocation of released qubits when reuse is encouraged", "[QubitManagerBasic]") +{ + std::unique_ptr qm = std::make_unique(2, false, true); + REQUIRE(qm->GetFreeQubitCount() == 2); + Qubit q0 = qm->Allocate(); + Qubit q1 = qm->Allocate(); + REQUIRE(qm->GetQubitId(q0) == 0); // Qubit ids should be in order + REQUIRE(qm->GetQubitId(q1) == 1); + REQUIRE_THROWS(qm->Allocate()); + REQUIRE(qm->GetFreeQubitCount() == 0); + REQUIRE(qm->GetAllocatedQubitCount() == 2); + + qm->Release(q0); + Qubit q0a = qm->Allocate(); + REQUIRE(qm->GetQubitId(q0a) == 0); // It was the only one available + REQUIRE_THROWS(qm->Allocate()); + + qm->Release(q1); + qm->Release(q0a); + REQUIRE(qm->GetFreeQubitCount() == 2); + REQUIRE(qm->GetAllocatedQubitCount() == 0); + + Qubit q0b = qm->Allocate(); + Qubit q1a = qm->Allocate(); + REQUIRE(qm->GetQubitId(q0b) == 0); // By default reuse is encouraged, LIFO is used + REQUIRE(qm->GetQubitId(q1a) == 1); + REQUIRE_THROWS(qm->Allocate()); + REQUIRE(qm->GetFreeQubitCount() == 0); + REQUIRE(qm->GetAllocatedQubitCount() == 2); + + qm->Release(q0b); + qm->Release(q1a); +} + +TEST_CASE("Extending capacity", "[QubitManager]") +{ + std::unique_ptr qm = std::make_unique(1, true, true); + + Qubit q0 = qm->Allocate(); + REQUIRE(qm->GetQubitId(q0) == 0); + Qubit q1 = qm->Allocate(); // This should double capacity + REQUIRE(qm->GetQubitId(q1) == 1); + REQUIRE(qm->GetFreeQubitCount() == 0); + REQUIRE(qm->GetAllocatedQubitCount() == 2); + + qm->Release(q0); + Qubit q0a = qm->Allocate(); + REQUIRE(qm->GetQubitId(q0a) == 0); + Qubit q2 = qm->Allocate(); // This should double capacity again + REQUIRE(qm->GetQubitId(q2) == 2); + REQUIRE(qm->GetFreeQubitCount() == 1); + REQUIRE(qm->GetAllocatedQubitCount() == 3); + + qm->Release(q1); + qm->Release(q0a); + qm->Release(q2); + REQUIRE(qm->GetFreeQubitCount() == 4); + REQUIRE(qm->GetAllocatedQubitCount() == 0); + + Qubit* qqq = new Qubit[3]; + qm->Allocate(qqq, 3); + REQUIRE(qm->GetFreeQubitCount() == 1); + REQUIRE(qm->GetAllocatedQubitCount() == 3); + qm->Release(qqq, 3); + delete[] qqq; + REQUIRE(qm->GetFreeQubitCount() == 4); + REQUIRE(qm->GetAllocatedQubitCount() == 0); +} + +TEST_CASE("Restricted Area", "[QubitManager]") +{ + std::unique_ptr qm = std::make_unique(3, false, true); + + Qubit q0 = qm->Allocate(); + REQUIRE(qm->GetQubitId(q0) == 0); + + qm->StartRestrictedReuseArea(); + + // Allocates fresh qubit + Qubit q1 = qm->Allocate(); + REQUIRE(qm->GetQubitId(q1) == 1); + qm->Release(q1); // Released, but cannot be used in the next segment. + + qm->NextRestrictedReuseSegment(); + + // Allocates fresh qubit, q1 cannot be reused - it belongs to a differen segment. + Qubit q2 = qm->Allocate(); + REQUIRE(qm->GetQubitId(q2) == 2); + qm->Release(q2); + + Qubit q2a = qm->Allocate(); // Getting the same one as the one that's just released. + REQUIRE(qm->GetQubitId(q2a) == 2); + qm->Release(q2a); // Released, but cannot be used in the next segment. + + qm->NextRestrictedReuseSegment(); + + // There's no qubits left here. q0 is allocated, q1 and q2 are from different segments. + REQUIRE_THROWS(qm->Allocate()); + + qm->EndRestrictedReuseArea(); + + // Qubits 1 and 2 are available here again. + Qubit* qqq = new Qubit[2]; + qm->Allocate(qqq, 2); + // OK to destruct qubit manager while qubits are still allocated. + REQUIRE_THROWS(qm->Allocate()); +} + diff --git a/src/Qir/Samples/CMakeLists.txt b/src/Qir/Samples/CMakeLists.txt index c6ebe6ecb69..f1172ecfd24 100644 --- a/src/Qir/Samples/CMakeLists.txt +++ b/src/Qir/Samples/CMakeLists.txt @@ -1,4 +1,4 @@ -cmake_minimum_required(VERSION 3.10) +cmake_minimum_required(VERSION 3.20 FATAL_ERROR) message(INFO "*** build config: ${CMAKE_BUILD_TYPE}") diff --git a/src/Qir/Tests/CMakeLists.txt b/src/Qir/Tests/CMakeLists.txt index 86083256e05..07b859bfaa9 100644 --- a/src/Qir/Tests/CMakeLists.txt +++ b/src/Qir/Tests/CMakeLists.txt @@ -1,4 +1,4 @@ -cmake_minimum_required(VERSION 3.10) +cmake_minimum_required(VERSION 3.20 FATAL_ERROR) message(INFO "*** build config: ${CMAKE_BUILD_TYPE}") diff --git a/src/Qir/Tests/FullstateSimulator/FullstateSimulatorTests.cpp b/src/Qir/Tests/FullstateSimulator/FullstateSimulatorTests.cpp index f9369c87fa6..7fe5e789d80 100644 --- a/src/Qir/Tests/FullstateSimulator/FullstateSimulatorTests.cpp +++ b/src/Qir/Tests/FullstateSimulator/FullstateSimulatorTests.cpp @@ -77,6 +77,36 @@ TEST_CASE("Fullstate simulator: X and measure", "[fullstate_simulator]") sim->ReleaseResult(r2); } +TEST_CASE("Fullstate simulator: X, M, reuse, M", "[fullstate_simulator]") +{ + std::unique_ptr sim = CreateFullstateSimulator(); + IQuantumGateSet* iqa = dynamic_cast(sim.get()); + + Qubit q = sim->AllocateQubit(); + Result r1 = MZ(iqa, q); + REQUIRE(Result_Zero == sim->GetResultValue(r1)); + REQUIRE(sim->AreEqualResults(r1, sim->UseZero())); + + iqa->X(q); + Result r2 = MZ(iqa, q); + REQUIRE(Result_One == sim->GetResultValue(r2)); + REQUIRE(sim->AreEqualResults(r2, sim->UseOne())); + + sim->ReleaseQubit(q); + sim->ReleaseResult(r1); + sim->ReleaseResult(r2); + + Qubit qq = sim->AllocateQubit(); + Result r3 = MZ(iqa, qq); + // Allocated qubit should always be in |0> state even though we released + // q in |1> state, and qq is likely reusing the same underlying qubit as q. + REQUIRE(Result_Zero == sim->GetResultValue(r3)); + REQUIRE(sim->AreEqualResults(r3, sim->UseZero())); + + sim->ReleaseQubit(qq); + sim->ReleaseResult(r3); +} + TEST_CASE("Fullstate simulator: measure Bell state", "[fullstate_simulator]") { std::unique_ptr sim = CreateFullstateSimulator(); diff --git a/src/Qir/Tests/QIR-static/qir-driver.cpp b/src/Qir/Tests/QIR-static/qir-driver.cpp index 143b3b40c9b..b18f5dc264d 100644 --- a/src/Qir/Tests/QIR-static/qir-driver.cpp +++ b/src/Qir/Tests/QIR-static/qir-driver.cpp @@ -8,6 +8,7 @@ #include #include +#include "QirUtils.hpp" #include "CoreTypes.hpp" #include "QirContext.hpp" #include "QirTypes.hpp" @@ -73,7 +74,7 @@ struct QubitsResultsTestSimulator : public Microsoft::Quantum::SimulatorStub { const int id = static_cast(reinterpret_cast(qubit)); REQUIRE(id >= 0); - REQUIRE(id < this->qubits.size()); + REQUIRE((size_t)id < this->qubits.size()); return id; } @@ -82,7 +83,7 @@ struct QubitsResultsTestSimulator : public Microsoft::Quantum::SimulatorStub { const int id = static_cast(reinterpret_cast(result)); REQUIRE(id >= 0); - REQUIRE(id < this->results.size()); + REQUIRE((size_t)id < this->results.size()); return id; } @@ -107,9 +108,10 @@ struct QubitsResultsTestSimulator : public Microsoft::Quantum::SimulatorStub this->qubits[id] = 1 - this->qubits[id]; } - Result Measure(long numBases, PauliId bases[], long numTargets, Qubit targets[]) override + Result Measure(long numBases, PauliId* /* bases */, long /* numTargets */, Qubit targets[]) override { assert(numBases == 1 && "QubitsResultsTestSimulator doesn't support joint measurements"); + UNUSED(numBases); const int id = GetQubitId(targets[0]); REQUIRE(this->qubits[id] != RELEASED); // the qubit must be alive @@ -223,7 +225,7 @@ struct FunctorsTestSimulator : public Microsoft::Quantum::SimulatorStub { const int id = static_cast(reinterpret_cast(qubit)); REQUIRE(id >= 0); - REQUIRE(id < this->qubits.size()); + REQUIRE((size_t)id < this->qubits.size()); return id; } @@ -261,9 +263,10 @@ struct FunctorsTestSimulator : public Microsoft::Quantum::SimulatorStub X(qubit); } - Result Measure(long numBases, PauliId bases[], long numTargets, Qubit targets[]) override + Result Measure(long numBases, PauliId* /* bases */, long /* numTargets */, Qubit targets[]) override { assert(numBases == 1 && "FunctorsTestSimulator doesn't support joint measurements"); + UNUSED(numBases); const int id = GetQubitId(targets[0]); REQUIRE(this->qubits[id] != RELEASED); @@ -276,7 +279,7 @@ struct FunctorsTestSimulator : public Microsoft::Quantum::SimulatorStub return (r1 == r2); } - void ReleaseResult(Result result) override {} // the results aren't allocated by this test simulator + void ReleaseResult(Result /*result*/) override {} // the results aren't allocated by this test simulator Result UseZero() override { diff --git a/src/Qir/Tests/QIR-static/qir-test-conditionals.cpp b/src/Qir/Tests/QIR-static/qir-test-conditionals.cpp index ab16565f211..4551d301505 100644 --- a/src/Qir/Tests/QIR-static/qir-test-conditionals.cpp +++ b/src/Qir/Tests/QIR-static/qir-test-conditionals.cpp @@ -61,14 +61,14 @@ struct ConditionalsTestSimulator : public Microsoft::Quantum::SimulatorStub { return nullptr; } - void ReleaseQubit(Qubit qubit) override {} + void ReleaseQubit(Qubit /*qubit*/) override {} void X(Qubit) override { this->xCallbacks.push_back(this->nGateCallback); this->nGateCallback++; } - void ControlledX(long numControls, Qubit controls[], Qubit qubit) override + void ControlledX(long /* numControls */, Qubit* /* controls */, Qubit /* qubit */) override { this->cxCallbacks.push_back(this->nGateCallback); this->nGateCallback++; @@ -78,16 +78,16 @@ struct ConditionalsTestSimulator : public Microsoft::Quantum::SimulatorStub this->otherCallbacks.push_back(this->nGateCallback); this->nGateCallback++; } - void ControlledY(long numControls, Qubit controls[], Qubit qubit) override + void ControlledY(long /* numControls */, Qubit* /* controls */, Qubit /* qubit */) override { this->otherCallbacks.push_back(this->nGateCallback); this->nGateCallback++; } - Result Measure(long numBases, PauliId bases[], long numTargets, Qubit targets[]) override + Result Measure(long /* numBases */, PauliId* /* bases */, long /* numTargets */, Qubit* /* targets */) override { assert( - this->nextMeasureResult < this->mockMeasurements.size() && + (size_t)(this->nextMeasureResult) < this->mockMeasurements.size() && "ConditionalsTestSimulator isn't set up correctly"); Result r = (this->mockMeasurements[this->nextMeasureResult] == Result_Zero) ? UseZero() : UseOne(); @@ -101,7 +101,7 @@ struct ConditionalsTestSimulator : public Microsoft::Quantum::SimulatorStub return (r1 == r2); } - void ReleaseResult(Result result) override {} // the results aren't allocated by this test simulator + void ReleaseResult(Result /*result*/) override {} // the results aren't allocated by this test simulator Result UseZero() override { diff --git a/src/Qir/qir-utils.ps1 b/src/Qir/qir-utils.ps1 index cfd0c31cc2f..544959d33a4 100644 --- a/src/Qir/qir-utils.ps1 +++ b/src/Qir/qir-utils.ps1 @@ -35,9 +35,40 @@ function Build-CMakeProject { $oldCC = $env:CC $oldCXX = $env:CXX $oldRC = $env:RC + $oldCCFLAGS = $env:CCFLAGS + $oldCXXFLAGS = $env:CXXFLAGS $clangTidy = "" + $warningFlags = "-Werror" + + # -Wall + # -Wmisleading-indentation, + # -Wmost, + # -Wcast-of-sel-type, -Winfinite-recursion, -Woverloaded-virtual, -Wstring-plus-int, + # -Wchar-subscripts, -Wint-in-bool-context, -Wprivate-extern, -Wtautological-compare, + # -Wcomment, -Wmismatched-tags, -Wrange-loop-construct, -Wtrigraphs, + # -Wdelete-non-virtual-dtor, -Wmissing-braces, -Wreorder, -Wuninitialized, + # -Wextern-c-compat, -Wmove, -Wreturn-type, -Wunknown-pragmas, + # -Wfor-loop-analysis, -Wmultichar, -Wself-assign, -Wunused, + # -Wformat, -Wobjc-designated-initializers, -Wself-move, -Wuser-defined-warnings, + # -Wframe-address, -Wobjc-flexible-array, -Wsizeof-array-argument, -Wvolatile-register-var. + # -Wimplicit, -Wobjc-missing-super-calls, -Wsizeof-array-decay, + # -Wparentheses, + # -Wswitch, + # -Wswitch-bool. + $warningFlags += " -Wall" # https://clang.llvm.org/docs/DiagnosticsReference.html#wall + + # -Wextra + # -Wdeprecated-copy, -Wempty-init-stmt, -Wfuse-ld-path, -Wignored-qualifiers, -Winitializer-overrides, + # -Wmissing-field-initializers, -Wmissing-method-return-type, -Wnull-pointer-arithmetic, + # -Wsemicolon-before-method-body, -Wsign-compare, -Wstring-concatenation, -Wunused-but-set-parameter, + # -Wunused-parameter. + $warningFlags += " -Wextra" # https://clang.llvm.org/docs/DiagnosticsReference.html#wextra + + $env:CFLAGS += $warningFlags + $env:CXXFLAGS += $warningFlags + if (($IsMacOS) -or ((Test-Path Env:AGENT_OS) -and ($Env:AGENT_OS.StartsWith("Darwin")))) { Write-Host "On MacOS build $Name using the default C/C++ compiler (should be AppleClang)" @@ -100,6 +131,9 @@ function Build-CMakeProject { Pop-Location + $env:CXXFLAGS = $oldCXXFLAGS + $env:CCFLAGS = $oldCCFLAGS + $env:CC = $oldCC $env:CXX = $oldCXX $env:RC = $oldRC diff --git a/src/Simulation/AutoSubstitution.Integration.Tests/Integration.cs b/src/Simulation/AutoSubstitution.Integration.Tests/Integration.cs new file mode 100644 index 00000000000..c8e9979c4a4 --- /dev/null +++ b/src/Simulation/AutoSubstitution.Integration.Tests/Integration.cs @@ -0,0 +1,18 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +using Microsoft.Quantum.Simulation.Simulators; +using Xunit; + +namespace Microsoft.Quantum.AutoSubstitution.Testing +{ + public class CodeGenerationTests + { + [Fact] + public void CanSimulateWithAlternativeSimulator() + { + var sim = new ToffoliSimulator(); + TestQuantumSwap.Run(sim).Wait(); + } + } +} diff --git a/src/Simulation/AutoSubstitution.Integration.Tests/Integration.qs b/src/Simulation/AutoSubstitution.Integration.Tests/Integration.qs new file mode 100644 index 00000000000..eb2adb4a726 --- /dev/null +++ b/src/Simulation/AutoSubstitution.Integration.Tests/Integration.qs @@ -0,0 +1,32 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +namespace Microsoft.Quantum.AutoSubstitution.Testing { + open Microsoft.Quantum.Diagnostics; + open Microsoft.Quantum.Intrinsic; + open Microsoft.Quantum.Measurement; + open Microsoft.Quantum.Targeting; + + @SubstitutableOnTarget("Microsoft.Quantum.Intrinsic.SWAP", "ToffoliSimulator") + operation QuantumSwap(a : Qubit, b : Qubit) : Unit { + within { + CNOT(a, b); + H(a); + H(b); + } apply { + CNOT(a, b); + } + } + + operation TestQuantumSwap() : Unit { + use a = Qubit(); + use b = Qubit(); + + X(a); + + QuantumSwap(a, b); + + EqualityFactR(MResetZ(a), Zero, "unexpected value for a after swap"); + EqualityFactR(MResetZ(b), One, "unexpected value for b after swap"); + } +} diff --git a/src/Simulation/AutoSubstitution.Integration.Tests/Tests.AutoSubstitution.Integration.csproj b/src/Simulation/AutoSubstitution.Integration.Tests/Tests.AutoSubstitution.Integration.csproj new file mode 100644 index 00000000000..7260a0b8e2c --- /dev/null +++ b/src/Simulation/AutoSubstitution.Integration.Tests/Tests.AutoSubstitution.Integration.csproj @@ -0,0 +1,22 @@ + + + + Library + netcoreapp3.1 + x64 + + + + + + + all + runtime; build; native; contentfiles; analyzers + + + + + + + + diff --git a/src/Simulation/AutoSubstitution.Tests/CodeGenerationTests.cs b/src/Simulation/AutoSubstitution.Tests/CodeGenerationTests.cs new file mode 100644 index 00000000000..218482a0069 --- /dev/null +++ b/src/Simulation/AutoSubstitution.Tests/CodeGenerationTests.cs @@ -0,0 +1,90 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +using System; +using System.Collections.Immutable; +using System.IO; +using System.Linq; +using Microsoft.Quantum.QsCompiler.AutoSubstitution; +using Microsoft.Quantum.QsCompiler.CompilationBuilder; +using Microsoft.Quantum.QsCompiler.ReservedKeywords; +using Microsoft.Quantum.QsCompiler.SyntaxTree; +using Xunit; + +namespace Microsoft.Quantum.AutoSubstitution.Testing +{ + public class CodeGenerationTests + { + [Fact] + public void CanGenerateAutoSubstitutionCode() + { + TestOneSuccessfulFile("Success"); + TestOneSuccessfulFile("SuccessA"); + TestOneSuccessfulFile("SuccessC"); + TestOneSuccessfulFile("SuccessCA"); + } + + [Fact] + public void CanFailForVariousReasons() + { + TestOneFailingFile("FailAlternativeDoesNotExist"); + TestOneFailingFile("FailDifferentSignatures"); + TestOneFailingFile("FailDifferentSpecializationKinds"); + TestOneFailingFile("FailNoNamespace"); + } + + private void TestOneSuccessfulFile(string fileName) + { + var step = new RewriteStep(); + var path = CreateNewTemporaryPath(); + step.AssemblyConstants[AssemblyConstants.OutputPath] = path; + + var compilation = CreateCompilation(Path.Combine("TestFiles", "Core.qs"), "Substitution.qs", Path.Combine("TestFiles", $"{fileName}.qs")); + + Assert.True(step.Transformation(compilation, out var transformed)); + var generatedFileName = Path.Combine(path, "__AutoSubstitution__.g.cs"); + Assert.True(File.Exists(generatedFileName)); + + // uncomment this line, when creating new unit tests to + // create files with expected content + //File.Copy(generatedFileName, $"{fileName}.cs_", true); + + Assert.Equal(File.ReadAllText(Path.Combine("TestFiles", $"{fileName}.cs_")).Replace("\r\n", "\n"), File.ReadAllText(generatedFileName).Replace("\r\n", "\n")); + + Directory.Delete(path, true); + } + + private void TestOneFailingFile(string fileName) + { + var step = new RewriteStep(); + var path = CreateNewTemporaryPath(); + step.AssemblyConstants[AssemblyConstants.OutputPath] = path; + + var compilation = CreateCompilation(Path.Combine("TestFiles", "Core.qs"), "Substitution.qs", Path.Combine("TestFiles", $"{fileName}.qs")); + + Assert.False(step.Transformation(compilation, out var transformed)); + Assert.Equal(2, step.GeneratedDiagnostics.Count()); + Assert.Equal(CodeAnalysis.DiagnosticSeverity.Error, step.GeneratedDiagnostics.Last().Severity); + } + + private QsCompilation CreateCompilation(params string[] fileNames) + { + var mgr = new CompilationUnitManager(); + var files = CreateFileManager(fileNames); + mgr.AddOrUpdateSourceFilesAsync(files).Wait(); + return mgr.Build().BuiltCompilation; + } + + private ImmutableHashSet CreateFileManager(params string[] fileNames) => + CompilationUnitManager.InitializeFileManagers( + fileNames.Select(fileName => { + var fileId = new Uri(Path.GetFullPath(fileName)); + return (id: fileId, content: File.ReadAllText(fileName)); + }).ToDictionary(t => t.id, t => t.content) + ); + + private readonly System.Random random = new System.Random(); + private string CreateNewTemporaryPath() => + Directory.CreateDirectory(Path.Combine(Path.GetTempPath(), $"substitution-test-{random.Next(Int32.MaxValue)}")).FullName; + } +} diff --git a/src/Simulation/AutoSubstitution.Tests/TestFiles/Core.qs b/src/Simulation/AutoSubstitution.Tests/TestFiles/Core.qs new file mode 100644 index 00000000000..2fdd2185eb8 --- /dev/null +++ b/src/Simulation/AutoSubstitution.Tests/TestFiles/Core.qs @@ -0,0 +1,5 @@ +// This file is needed to ensure that the Microsoft.Quantum.Core namespace +// exists for the tests. + +namespace Microsoft.Quantum.Core { +} diff --git a/src/Simulation/AutoSubstitution.Tests/TestFiles/FailAlternativeDoesNotExist.qs b/src/Simulation/AutoSubstitution.Tests/TestFiles/FailAlternativeDoesNotExist.qs new file mode 100644 index 00000000000..7896578878e --- /dev/null +++ b/src/Simulation/AutoSubstitution.Tests/TestFiles/FailAlternativeDoesNotExist.qs @@ -0,0 +1,6 @@ +namespace AutoSubstitutionTests { + open Microsoft.Quantum.Targeting; + + @SubstitutableOnTarget("Namespace.NotExisting", "ToffoliSimulator") + operation Fail() : Unit {} +} diff --git a/src/Simulation/AutoSubstitution.Tests/TestFiles/FailDifferentSignatures.qs b/src/Simulation/AutoSubstitution.Tests/TestFiles/FailDifferentSignatures.qs new file mode 100644 index 00000000000..548496c4a26 --- /dev/null +++ b/src/Simulation/AutoSubstitution.Tests/TestFiles/FailDifferentSignatures.qs @@ -0,0 +1,8 @@ +namespace AutoSubstitutionTests { + open Microsoft.Quantum.Targeting; + + @SubstitutableOnTarget("AutoSubstitutionTests.FailClassical", "ToffoliSimulator") + operation Fail(a : Int) : Unit {} + + operation FailClassical(a : Double) : Unit {} +} diff --git a/src/Simulation/AutoSubstitution.Tests/TestFiles/FailDifferentSpecializationKinds.qs b/src/Simulation/AutoSubstitution.Tests/TestFiles/FailDifferentSpecializationKinds.qs new file mode 100644 index 00000000000..fcfba7c3866 --- /dev/null +++ b/src/Simulation/AutoSubstitution.Tests/TestFiles/FailDifferentSpecializationKinds.qs @@ -0,0 +1,8 @@ +namespace AutoSubstitutionTests { + open Microsoft.Quantum.Targeting; + + @SubstitutableOnTarget("FailClassical", "ToffoliSimulator") + operation Fail() : Unit is Adj {} + + operation FailClassical() : Unit {} +} diff --git a/src/Simulation/AutoSubstitution.Tests/TestFiles/FailNoNamespace.qs b/src/Simulation/AutoSubstitution.Tests/TestFiles/FailNoNamespace.qs new file mode 100644 index 00000000000..81ed06e2fa4 --- /dev/null +++ b/src/Simulation/AutoSubstitution.Tests/TestFiles/FailNoNamespace.qs @@ -0,0 +1,8 @@ +namespace AutoSubstitutionTests { + open Microsoft.Quantum.Targeting; + + @SubstitutableOnTarget("FailClassical", "ToffoliSimulator") + operation Fail() : Unit {} + + operation FailClassical() : Unit {} +} diff --git a/src/Simulation/AutoSubstitution.Tests/TestFiles/Success.cs_ b/src/Simulation/AutoSubstitution.Tests/TestFiles/Success.cs_ new file mode 100644 index 00000000000..1e3cdbfda1a --- /dev/null +++ b/src/Simulation/AutoSubstitution.Tests/TestFiles/Success.cs_ @@ -0,0 +1,49 @@ +using System; +using Microsoft.Quantum.Simulation.Core; +using Microsoft.Quantum.Simulation.Simulators; + +namespace AutoSubstitutionTests +{ + public partial class Success + { + public class Native : Success + { + public Native(Microsoft.Quantum.Simulation.Core.IOperationFactory m): base(m) + { + sim0 = ((m) as ToffoliSimulator); + } + + public override void __Init__() + { + base.__Init__(); + if ((sim0) != (null)) + { + alternative0 = (__Factory__.Get(typeof(AutoSubstitutionTests.SuccessClassical))); + } + } + + public override Func __Body__ + { + get + { + return args => + { + if ((sim0) != (null)) + { + return alternative0.__Body__(args); + } + else + { + return base.__Body__(args); + } + } + + ; + } + } + + private AutoSubstitutionTests.SuccessClassical alternative0 = null; + private ToffoliSimulator sim0 = null; + } + } +} \ No newline at end of file diff --git a/src/Simulation/AutoSubstitution.Tests/TestFiles/Success.qs b/src/Simulation/AutoSubstitution.Tests/TestFiles/Success.qs new file mode 100644 index 00000000000..4e5c8390aec --- /dev/null +++ b/src/Simulation/AutoSubstitution.Tests/TestFiles/Success.qs @@ -0,0 +1,8 @@ +namespace AutoSubstitutionTests { + open Microsoft.Quantum.Targeting; + + @SubstitutableOnTarget("AutoSubstitutionTests.SuccessClassical", "ToffoliSimulator") + operation Success() : Unit {} + + operation SuccessClassical() : Unit {} +} diff --git a/src/Simulation/AutoSubstitution.Tests/TestFiles/SuccessA.cs_ b/src/Simulation/AutoSubstitution.Tests/TestFiles/SuccessA.cs_ new file mode 100644 index 00000000000..c8cd88d9fa6 --- /dev/null +++ b/src/Simulation/AutoSubstitution.Tests/TestFiles/SuccessA.cs_ @@ -0,0 +1,69 @@ +using System; +using Microsoft.Quantum.Simulation.Core; +using Microsoft.Quantum.Simulation.Simulators; + +namespace AutoSubstitutionTests +{ + public partial class Success + { + public class Native : Success + { + public Native(Microsoft.Quantum.Simulation.Core.IOperationFactory m): base(m) + { + sim0 = ((m) as ToffoliSimulator); + } + + public override void __Init__() + { + base.__Init__(); + if ((sim0) != (null)) + { + alternative0 = (__Factory__.Get(typeof(AutoSubstitutionTests.SuccessClassical))); + } + } + + public override Func __Body__ + { + get + { + return args => + { + if ((sim0) != (null)) + { + return alternative0.__Body__(args); + } + else + { + return base.__Body__(args); + } + } + + ; + } + } + + public override Func __AdjointBody__ + { + get + { + return args => + { + if ((sim0) != (null)) + { + return alternative0.__AdjointBody__(args); + } + else + { + return base.__AdjointBody__(args); + } + } + + ; + } + } + + private AutoSubstitutionTests.SuccessClassical alternative0 = null; + private ToffoliSimulator sim0 = null; + } + } +} \ No newline at end of file diff --git a/src/Simulation/AutoSubstitution.Tests/TestFiles/SuccessA.qs b/src/Simulation/AutoSubstitution.Tests/TestFiles/SuccessA.qs new file mode 100644 index 00000000000..99a4407bb37 --- /dev/null +++ b/src/Simulation/AutoSubstitution.Tests/TestFiles/SuccessA.qs @@ -0,0 +1,8 @@ +namespace AutoSubstitutionTests { + open Microsoft.Quantum.Targeting; + + @SubstitutableOnTarget("AutoSubstitutionTests.SuccessClassical", "ToffoliSimulator") + operation Success() : Unit is Adj {} + + operation SuccessClassical() : Unit is Adj {} +} diff --git a/src/Simulation/AutoSubstitution.Tests/TestFiles/SuccessC.cs_ b/src/Simulation/AutoSubstitution.Tests/TestFiles/SuccessC.cs_ new file mode 100644 index 00000000000..73b7cfe76d9 --- /dev/null +++ b/src/Simulation/AutoSubstitution.Tests/TestFiles/SuccessC.cs_ @@ -0,0 +1,69 @@ +using System; +using Microsoft.Quantum.Simulation.Core; +using Microsoft.Quantum.Simulation.Simulators; + +namespace AutoSubstitutionTests +{ + public partial class Success + { + public class Native : Success + { + public Native(Microsoft.Quantum.Simulation.Core.IOperationFactory m): base(m) + { + sim0 = ((m) as ToffoliSimulator); + } + + public override void __Init__() + { + base.__Init__(); + if ((sim0) != (null)) + { + alternative0 = (__Factory__.Get(typeof(AutoSubstitutionTests.SuccessClassical))); + } + } + + public override Func __Body__ + { + get + { + return args => + { + if ((sim0) != (null)) + { + return alternative0.__Body__(args); + } + else + { + return base.__Body__(args); + } + } + + ; + } + } + + public override Func<(IQArray, QVoid), QVoid> __ControlledBody__ + { + get + { + return args => + { + if ((sim0) != (null)) + { + return alternative0.__ControlledBody__(args); + } + else + { + return base.__ControlledBody__(args); + } + } + + ; + } + } + + private AutoSubstitutionTests.SuccessClassical alternative0 = null; + private ToffoliSimulator sim0 = null; + } + } +} \ No newline at end of file diff --git a/src/Simulation/AutoSubstitution.Tests/TestFiles/SuccessC.qs b/src/Simulation/AutoSubstitution.Tests/TestFiles/SuccessC.qs new file mode 100644 index 00000000000..612e1671ac7 --- /dev/null +++ b/src/Simulation/AutoSubstitution.Tests/TestFiles/SuccessC.qs @@ -0,0 +1,8 @@ +namespace AutoSubstitutionTests { + open Microsoft.Quantum.Targeting; + + @SubstitutableOnTarget("AutoSubstitutionTests.SuccessClassical", "ToffoliSimulator") + operation Success() : Unit is Ctl {} + + operation SuccessClassical() : Unit is Ctl {} +} diff --git a/src/Simulation/AutoSubstitution.Tests/TestFiles/SuccessCA.cs_ b/src/Simulation/AutoSubstitution.Tests/TestFiles/SuccessCA.cs_ new file mode 100644 index 00000000000..eaeac06869c --- /dev/null +++ b/src/Simulation/AutoSubstitution.Tests/TestFiles/SuccessCA.cs_ @@ -0,0 +1,109 @@ +using System; +using Microsoft.Quantum.Simulation.Core; +using Microsoft.Quantum.Simulation.Simulators; + +namespace AutoSubstitutionTests +{ + public partial class Success + { + public class Native : Success + { + public Native(Microsoft.Quantum.Simulation.Core.IOperationFactory m): base(m) + { + sim0 = ((m) as ToffoliSimulator); + } + + public override void __Init__() + { + base.__Init__(); + if ((sim0) != (null)) + { + alternative0 = (__Factory__.Get(typeof(AutoSubstitutionTests.SuccessClassical))); + } + } + + public override Func __Body__ + { + get + { + return args => + { + if ((sim0) != (null)) + { + return alternative0.__Body__(args); + } + else + { + return base.__Body__(args); + } + } + + ; + } + } + + public override Func __AdjointBody__ + { + get + { + return args => + { + if ((sim0) != (null)) + { + return alternative0.__AdjointBody__(args); + } + else + { + return base.__AdjointBody__(args); + } + } + + ; + } + } + + public override Func<(IQArray, QVoid), QVoid> __ControlledBody__ + { + get + { + return args => + { + if ((sim0) != (null)) + { + return alternative0.__ControlledBody__(args); + } + else + { + return base.__ControlledBody__(args); + } + } + + ; + } + } + + public override Func<(IQArray, QVoid), QVoid> __ControlledAdjointBody__ + { + get + { + return args => + { + if ((sim0) != (null)) + { + return alternative0.__ControlledAdjointBody__(args); + } + else + { + return base.__ControlledAdjointBody__(args); + } + } + + ; + } + } + + private AutoSubstitutionTests.SuccessClassical alternative0 = null; + private ToffoliSimulator sim0 = null; + } + } +} \ No newline at end of file diff --git a/src/Simulation/AutoSubstitution.Tests/TestFiles/SuccessCA.qs b/src/Simulation/AutoSubstitution.Tests/TestFiles/SuccessCA.qs new file mode 100644 index 00000000000..58b4652ef94 --- /dev/null +++ b/src/Simulation/AutoSubstitution.Tests/TestFiles/SuccessCA.qs @@ -0,0 +1,8 @@ +namespace AutoSubstitutionTests { + open Microsoft.Quantum.Targeting; + + @SubstitutableOnTarget("AutoSubstitutionTests.SuccessClassical", "ToffoliSimulator") + operation Success() : Unit is Adj+Ctl {} + + operation SuccessClassical() : Unit is Adj+Ctl {} +} diff --git a/src/Simulation/AutoSubstitution.Tests/Tests.AutoSubstitution.csproj b/src/Simulation/AutoSubstitution.Tests/Tests.AutoSubstitution.csproj new file mode 100644 index 00000000000..d2ce8ad57ba --- /dev/null +++ b/src/Simulation/AutoSubstitution.Tests/Tests.AutoSubstitution.csproj @@ -0,0 +1,69 @@ + + + + netcoreapp3.1 + Tests.Microsoft.Quantum.AutoSubstitution + false + x64 + + + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + + + + + + all + runtime; build; native; contentfiles; analyzers + + + + + + + + + diff --git a/src/Simulation/AutoSubstitution/AutoSubstitution.csproj b/src/Simulation/AutoSubstitution/AutoSubstitution.csproj new file mode 100644 index 00000000000..636efa33244 --- /dev/null +++ b/src/Simulation/AutoSubstitution/AutoSubstitution.csproj @@ -0,0 +1,23 @@ + + + + + + Library + netstandard2.1 + Enable + x64 + true + Microsoft.Quantum.AutoSubstitution + + + + + + + + + + + + diff --git a/src/Simulation/AutoSubstitution/CodeGenerator.cs b/src/Simulation/AutoSubstitution/CodeGenerator.cs new file mode 100644 index 00000000000..50e1ec8e0b4 --- /dev/null +++ b/src/Simulation/AutoSubstitution/CodeGenerator.cs @@ -0,0 +1,297 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.CSharp; +using Microsoft.CodeAnalysis.Editing; +using Microsoft.Quantum.QsCompiler.CsharpGeneration; +using Microsoft.Quantum.QsCompiler.SyntaxTree; + +namespace Microsoft.Quantum.QsCompiler.AutoSubstitution +{ + public class CodeGenerator + { + public CodeGenerator(CodegenContext context) + { + ctx = context; + gen = Microsoft.CodeAnalysis.Editing.SyntaxGenerator.GetGenerator(new AdhocWorkspace(), LanguageNames.CSharp); + } + + /// + /// Generates an substitution class for a given callable with a given name + /// + /// + /// + /// In the following we illustrate the syntax that is generated using + /// as an example the `Microsoft.Quantum.Canon.ApplyAnd` operation with + /// `Microsoft.Quantum.Intrinsic.CCNOT` as an alternative when using + /// `ToffoliSimulator`. + /// + /// The generated code looks as follows: + /// + /// + /// namespace Microsoft.Quantum.Canon { + /// public partial class ApplyAnd { + /// public class Native : ApplyAnd { + /// public Native(Microsoft.Quantum.Simulation.Core.IOperationFactory m) : base(m) { + /// sim0 = m as ToffoliSimulator; + /// } + /// + /// public override void __Init__() { + /// base.Init(); + /// if (sim0 != null) alternative0 = __Factory__.Get<Microsoft.Quantum.Intrinsic.CCNOT>(typeof(Microsoft.Quantum.Intrinsic.CCNOT)); + /// } + /// + /// public override Func<(Qubit, Qubit, Qubit), QVoid> __Body__ => args => { + /// if (sim0 != null) return alternative0.__Body__(args); + /// else return base.__Body__(args); + /// } + /// + /// // methods for other specializations ... + /// + /// private ToffoliSimulator sim0 = null; + /// private Microsoft.Quantum.Intrinsic.CCNOT alternative0 = null; + /// } + /// } + /// } + /// + /// + /// + /// Namespace and name of the callable + /// Q# Callable + /// All attribute values from @HasSubstitutionAttribute attributes of that callable + public void AddCallable(QsQualifiedName name, QsCallable callable, IEnumerable<(string AlternativeOperation, string InSimulator)> substitutionAttributes) + { + var attributes = substitutionAttributes.Select((attr, idx) => new SubstitutionAttribute( + SyntaxFactory.ParseTypeName(attr.AlternativeOperation), + gen.IdentifierName($"alternative{idx}"), + SyntaxFactory.ParseTypeName(attr.InSimulator), + gen.IdentifierName($"sim{idx}") + )); + + var operationFields = attributes.Select(CreateOperationField); + var simulatorFields = attributes.Select(CreateSimulatorField); + + var specializationProperties = callable.Specializations.Select(specialization => + CreateSpecializationProperty( + specializationName: specialization.Kind switch + { + var kind when kind.IsQsBody => "__Body__", + var kind when kind.IsQsAdjoint => "__AdjointBody__", + var kind when kind.IsQsControlled => "__ControlledBody__", + var kind when kind.IsQsControlledAdjoint => "__ControlledAdjointBody__", + _ => throw new Exception("unexpected specialization kind") + }, + attributes: attributes, + argumentType: SimulationCode.roslynTypeName(ctx, specialization.Signature.ArgumentType), + returnType: SimulationCode.roslynTypeName(ctx, specialization.Signature.ReturnType)) + ); + + var innerClass = gen.ClassDeclaration( + "Native", + accessibility: Accessibility.Public, + baseType: gen.IdentifierName(name.Name), + members: new[] { + CreateConstructor(attributes.Select(CreateSimulatorCast)), + CreateInitMethod(attributes.Select(CreateOperationAssignment)) + }.Concat(specializationProperties).Concat(operationFields).Concat(simulatorFields)); + + var cls = gen.ClassDeclaration( + name.Name, + accessibility: callable.Access.IsInternal ? Accessibility.Internal : Accessibility.Public, + modifiers: DeclarationModifiers.Partial, + members: new[] { innerClass }); + + InsertClassNode(name.Namespace, cls); + } + + /// + /// Creates a syntax node for the constructor of the inner Native class + /// + private SyntaxNode CreateConstructor(IEnumerable bodyStatements) => + gen.ConstructorDeclaration( + "Native", + parameters: new[] { gen.ParameterDeclaration("m", SyntaxFactory.ParseTypeName("Microsoft.Quantum.Simulation.Core.IOperationFactory")) }, + accessibility: Accessibility.Public, + baseConstructorArguments: new[] { gen.IdentifierName("m") }, + statements: bodyStatements); + + /// + /// Creates a syntax node for the Init() method of the inner Native class + /// + private SyntaxNode CreateInitMethod(IEnumerable bodyStatements) => + gen.MethodDeclaration( + "__Init__", + accessibility: Accessibility.Public, + modifiers: DeclarationModifiers.Override, + statements: new[] { + gen.ExpressionStatement( + gen.InvocationExpression(gen.MemberAccessExpression(gen.BaseExpression(), "__Init__")) + ) + }.Concat(bodyStatements)); + + /// + /// Creates a syntax node for field declarations for the alternative operations + /// + private SyntaxNode CreateOperationField(SubstitutionAttribute attr) => + gen.FieldDeclaration( + attr.OperationName.ToFullString(), + type: attr.OperationType, + accessibility: Accessibility.Private, + initializer: gen.NullLiteralExpression()); + + /// + /// Creates a syntax node for operation field assignments in the Init() method + /// + private SyntaxNode CreateOperationAssignment(SubstitutionAttribute attr) => + gen.IfStatement( + condition: gen.ValueNotEqualsExpression(attr.SimulatorName, gen.NullLiteralExpression()), + trueStatements: new[] + { + gen.AssignmentStatement( + left: attr.OperationName, + right: CreateFactoryGetStatement(attr.OperationType)) + } + ); + + /// + /// Creates a syntax node for the __Factory__.Get statement in operation field assignments + /// + private SyntaxNode CreateFactoryGetStatement(SyntaxNode type) => + gen.InvocationExpression( + gen.MemberAccessExpression( + gen.IdentifierName("__Factory__"), + gen.GenericName("Get", type)), + gen.TypeOfExpression(type)); + + /// + /// Creates a syntax node for field declarations for the simulators + /// + /// + /// + private SyntaxNode CreateSimulatorField(SubstitutionAttribute attr) => + gen.FieldDeclaration( + attr.SimulatorName.ToFullString(), + type: attr.SimulatorType, + accessibility: Accessibility.Private, + initializer: gen.NullLiteralExpression()); + + /// + /// Creates a syntax node for the simulator field cast assignments in the constructor + /// + /// + /// + private SyntaxNode CreateSimulatorCast(SubstitutionAttribute attr) => + gen.AssignmentStatement( + left: attr.SimulatorName, + right: gen.TryCastExpression(gen.IdentifierName("m"), attr.SimulatorType) + ); + + /// + /// Creates a syntax node for one specialization property + /// + private SyntaxNode CreateSpecializationProperty(string specializationName, IEnumerable attributes, string argumentType, string returnType) => + gen.PropertyDeclaration( + specializationName, + type: SyntaxFactory.ParseTypeName($"Func<{argumentType}, {returnType}>"), + accessibility: Accessibility.Public, + modifiers: DeclarationModifiers.Override | DeclarationModifiers.ReadOnly, + getAccessorStatements: new[] { gen.ReturnStatement(CreateSpecializationLambda(specializationName, attributes)) }); + + /// + /// Creates a syntax node for the lambda expression, returned in the + /// `get` accessor of a specialization property + /// + private SyntaxNode CreateSpecializationLambda(string specializationName, IEnumerable attrs) => + gen.ValueReturningLambdaExpression( + "args", + new[] + { + CreateSpecializationBody(specializationName, attrs) + }); + + /// + /// Creates a syntax node for a nested if-statement for all possible + /// operation alternatives + /// + private SyntaxNode CreateSpecializationBody(string specializationName, IEnumerable attrs) => + attrs.Count() switch + { + 0 => gen.ReturnStatement( + gen.InvocationExpression( + gen.MemberAccessExpression(gen.BaseExpression(), specializationName), + gen.IdentifierName("args"))), + _ => gen.IfStatement( + condition: gen.ValueNotEqualsExpression(attrs.First().SimulatorName, gen.NullLiteralExpression()), + trueStatements: new[] { + gen.ReturnStatement( + gen.InvocationExpression( + gen.MemberAccessExpression(attrs.First().OperationName, specializationName), + gen.IdentifierName("args"))) }, + falseStatement: CreateSpecializationBody(specializationName, attrs.Skip(1))) + }; + + /// + /// Inserts a syntax node for a class declaration into a list for its + /// corresponding namespace + /// + /// Namespace name + /// Syntax node for class declaration + private void InsertClassNode(string @namespace, SyntaxNode classNode) + { + if (!classNodes.ContainsKey(@namespace)) + { + classNodes.Add(@namespace, new List()); + } + + classNodes[@namespace].Add(classNode); + } + + /// + /// Group class declarations of the same namespace into a namespace node + /// + private IEnumerable GetNamespaceNodes() => + classNodes.Select(pair => gen.NamespaceDeclaration(pair.Key, pair.Value)); + + /// + /// Write namespaces into a text writer + /// + /// Text writer, e.g., `Console.Out` + public void WriteTo(TextWriter writer) => + gen.CompilationUnit( + new[] { "System", "Microsoft.Quantum.Simulation.Core", "Microsoft.Quantum.Simulation.Simulators" }.Select(gen.NamespaceImportDeclaration) + .Concat(GetNamespaceNodes()) + ).NormalizeWhitespace().WriteTo(writer); + + /// + /// Helper struct to prepare syntax nodes for @SubstitutableOnTarget attribute values + /// + private class SubstitutionAttribute + { + public SubstitutionAttribute(SyntaxNode operationType, SyntaxNode operationName, SyntaxNode simulatorType, SyntaxNode simulatorName) + { + OperationType = operationType; + OperationName = operationName; + SimulatorType = simulatorType; + SimulatorName = simulatorName; + } + + public SyntaxNode OperationType { get; private set; } + public SyntaxNode OperationName { get; private set; } + public SyntaxNode SimulatorType { get; private set; } + public SyntaxNode SimulatorName { get; private set; } + } + + private readonly CodegenContext ctx; + private readonly Microsoft.CodeAnalysis.Editing.SyntaxGenerator gen; + + /// + /// Contains class nodes by namespace names + /// + private readonly Dictionary> classNodes = new Dictionary>(); + } +} diff --git a/src/Simulation/AutoSubstitution/Microsoft.Quantum.AutoSubstitution.nuspec.template b/src/Simulation/AutoSubstitution/Microsoft.Quantum.AutoSubstitution.nuspec.template new file mode 100644 index 00000000000..f9d83f01d9a --- /dev/null +++ b/src/Simulation/AutoSubstitution/Microsoft.Quantum.AutoSubstitution.nuspec.template @@ -0,0 +1,29 @@ + + + + Microsoft.Quantum.AutoSubstitution + $version$ + $title$ + Microsoft + QuantumEngineering, Microsoft + + MIT + https://docs.microsoft.com/azure/quantum + images\qdk-nuget-icon.png + + false + C# code generation for automatic target substitution. + + See: https://docs.microsoft.com/azure/quantum/qdk-relnotes/ + + $copyright$ + Quantum Q# QSharp + + + + + + + + + diff --git a/src/Simulation/AutoSubstitution/RewriteStep.cs b/src/Simulation/AutoSubstitution/RewriteStep.cs new file mode 100644 index 00000000000..2108bd8c93c --- /dev/null +++ b/src/Simulation/AutoSubstitution/RewriteStep.cs @@ -0,0 +1,180 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +using System; +using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; +using System.IO; +using System.Linq; +using Microsoft.CodeAnalysis; +using Microsoft.Quantum.QsCompiler.CsharpGeneration; +using Microsoft.Quantum.QsCompiler.SyntaxTokens; +using Microsoft.Quantum.QsCompiler.SyntaxTree; + +namespace Microsoft.Quantum.QsCompiler.AutoSubstitution +{ + /// + /// A Q# rewrite step for auto substitution + /// + /// + /// + /// This rewrite step creates custom emulators for operations that have the + /// SubstitutableOnTarget attribute. This attribute holds an alternative operation, + /// with the same signature, as its first argument, and a simulator, for which + /// the alternative operation should be used, as its second argument. + /// + public class RewriteStep : IRewriteStep + { + public string Name => "AutoSubstitution"; + + // This rewrite step needs to be run before C# code generation + public int Priority => -2; + + public IDictionary AssemblyConstants { get; } = new Dictionary(); + + public IEnumerable GeneratedDiagnostics => diagnostics; + + public bool ImplementsPreconditionVerification => false; + + public bool ImplementsTransformation => true; + + public bool ImplementsPostconditionVerification => false; + + public bool PostconditionVerification(QsCompilation compilation) => throw new System.NotImplementedException(); + + public bool PreconditionVerification(QsCompilation compilation) => throw new System.NotImplementedException(); + + public bool Transformation(QsCompilation compilation, [NotNullWhen(true)] out QsCompilation? transformed) + { + // we do not change the Q# syntax tree + transformed = compilation; + + // global callables + var globalCallables = compilation.Namespaces.GlobalCallableResolutions(); + + // collect all callables that have an substitution attribute + var globals = globalCallables.Where(p => p.Value.Source.CodeFile.EndsWith(".qs")) + .Where(p => p.Value.Attributes.Any(HasSubstitutionAttribute)); + + if (!globals.Any()) + { + diagnostics.Add(new IRewriteStep.Diagnostic + { + Severity = DiagnosticSeverity.Info, + Message = "AutoSubstitution: no operations have @SubstitutableOnTarget attribute", + Stage = IRewriteStep.Stage.Transformation + }); + return true; + } + + // no need to generate any C# file, if there is no substitution attribute, or if we cannot retrieve the output path + if (!AssemblyConstants.TryGetValue(Microsoft.Quantum.QsCompiler.ReservedKeywords.AssemblyConstants.OutputPath, out var outputPath)) + { + diagnostics.Add(new IRewriteStep.Diagnostic + { + Severity = DiagnosticSeverity.Error, + Message = "AutoSubstitution: cannot determine output path for generated C# code", + Stage = IRewriteStep.Stage.Transformation + }); + return false; + } + + diagnostics.Add(new IRewriteStep.Diagnostic + { + Severity = DiagnosticSeverity.Info, + Message = $"AutoSubstitution: Generating file __AutoSubstitution__.g.cs in {outputPath}", + Stage = IRewriteStep.Stage.Transformation + }); + + using var writer = new StreamWriter(Path.Combine(outputPath, "__AutoSubstitution__.g.cs")); + var context = CodegenContext.Create(compilation, AssemblyConstants); + + var generator = new CodeGenerator(context); + foreach (var (key, callable) in globals) + { + var attributeArguments = callable.Attributes.Where(HasSubstitutionAttribute).Select(GetSubstitutionAttributeArguments); + foreach (var (alternativeOperation, _) in attributeArguments) + { + var period = alternativeOperation.LastIndexOf('.'); + if (period == -1) + { + diagnostics.Add(new IRewriteStep.Diagnostic + { + Severity = DiagnosticSeverity.Error, + Message = $"AutoSubstitution: name of alternative operation in {key.Namespace}.{key.Name} must be completely specified (including namespace)", + Stage = IRewriteStep.Stage.Transformation + }); + return false; + } + + var qualifiedName = new QsQualifiedName(alternativeOperation.Substring(0, period), alternativeOperation.Substring(period + 1)); + if (!globalCallables.TryGetValue(qualifiedName, out var alternativeCallable)) + { + diagnostics.Add(new IRewriteStep.Diagnostic + { + Severity = DiagnosticSeverity.Error, + Message = $"AutoSubstitution: cannot find alternative operation `{alternativeOperation}`", + Stage = IRewriteStep.Stage.Transformation + }); + return false; + } + + var callableSignature = callable.Signature; + var alternativeSignature = alternativeCallable.Signature; + + if (!callableSignature.ArgumentType.Equals(alternativeSignature.ArgumentType) || !callableSignature.ReturnType.Equals(alternativeSignature.ReturnType)) + { + diagnostics.Add(new IRewriteStep.Diagnostic + { + Severity = DiagnosticSeverity.Error, + Message = $"AutoSubstitution: signature of `{alternativeOperation}` does not match the one of {key.Namespace}.{key.Name}", + Stage = IRewriteStep.Stage.Transformation + }); + return false; + } + + if (!GetSpecializationKinds(callable).IsSubsetOf(GetSpecializationKinds(alternativeCallable))) + { + diagnostics.Add(new IRewriteStep.Diagnostic + { + Severity = DiagnosticSeverity.Error, + Message = $"AutoSubstitution: specializations of `{alternativeOperation}` must be a superset of specializations of {key.Namespace}.{key.Name}", + Stage = IRewriteStep.Stage.Transformation + }); + return false; + } + } + generator.AddCallable(key, callable, attributeArguments); + } + generator.WriteTo(writer); + + + return true; + } + + private static bool HasSubstitutionAttribute(QsDeclarationAttribute attribute) => + attribute.TypeId.IsValue && attribute.TypeId.Item.Namespace == "Microsoft.Quantum.Targeting" && attribute.TypeId.Item.Name == "SubstitutableOnTarget"; + + private static (string AlternativeOperation, string InSimulator) GetSubstitutionAttributeArguments(QsDeclarationAttribute attribute) => + attribute.Argument.Expression switch + { + QsExpressionKind.ValueTuple tuple => + tuple.Item switch + { + var arr when arr.Count() == 2 => + (arr.ElementAt(0).Expression, arr.ElementAt(1).Expression) switch + { + (QsExpressionKind.StringLiteral alternativeOperation, QsExpressionKind.StringLiteral inSimulator) => (alternativeOperation.Item1, inSimulator.Item1), + _ => throw new Exception("Unexpected argument") + }, + _ => throw new Exception("Unexpected argument") + }, + _ => throw new Exception("Unexpected argument") + }; + + private static ISet GetSpecializationKinds(QsCallable callable) => + callable.Specializations.Select(spec => spec.Kind).OrderBy(kind => kind).ToHashSet(); + + private List diagnostics = new List(); + } +} diff --git a/src/Simulation/AutoSubstitution/RewriteStep.props b/src/Simulation/AutoSubstitution/RewriteStep.props new file mode 100644 index 00000000000..ad16b42a1b3 --- /dev/null +++ b/src/Simulation/AutoSubstitution/RewriteStep.props @@ -0,0 +1,8 @@ + + + + + $(MSBuildThisFileDirectory)/../lib/netstandard2.1/Microsoft.Quantum.AutoSubstitution.dll + + + diff --git a/src/Simulation/AutoSubstitution/Substitution.qs b/src/Simulation/AutoSubstitution/Substitution.qs new file mode 100644 index 00000000000..ccaf539b6a0 --- /dev/null +++ b/src/Simulation/AutoSubstitution/Substitution.qs @@ -0,0 +1,17 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +namespace Microsoft.Quantum.Targeting { + /// # Summary + /// Enables to substitute an operation with an alternative operation for a given target + /// + /// # Named Items + /// ## AlternativeOperation + /// Fully qualified name of alternative operation to substitute operation with. + /// + /// ## TargetName + /// One of `QuantumSimulator`, `ToffoliSimulator`, or `ResourcesEstimator`, or a fully qualified name + /// of a custom target. + @Attribute() + newtype SubstitutableOnTarget = (AlternativeOperation : String, TargetName : String); +} diff --git a/src/Simulation/EntryPointDriver.Tests/Tests.Microsoft.Quantum.EntryPointDriver.sln b/src/Simulation/EntryPointDriver.Tests/Tests.Microsoft.Quantum.EntryPointDriver.sln deleted file mode 100644 index 00e2b89cd813d2248845a802044c95f1a143346b..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 14910 zcmdU$T~Fgi6o%(^EA>CDxY|lh6vzijrC#KdR%$C2w(Si!3jxXs$QCcFQrf@X_Ibw% zcEWh##$%(%N-`dgXU?4WJ!j5*}_vV_rWAg6J zl)Ra}n~{4@p6+kSnUK0R=jLmho>A8gZ^reUa~D08+%%)nlH?uZS@0ho>BUZ z*)fl%XZmKB?_J6?skug~!|y(+7O9Tuk{Xz?X>(tjmml-I&%MgExjN==YWoU#zU8xH zp7*g%`9JB!9j({RTie%L+s_;BhxF%)C(7Be{n{b#8*^fg$o*(_bz**}XTQC*rsAv?5~3E2S{Hpm;0*R@Y4Y!9+W zOBNSnDtj&uPt~3e%PiTl5fp9%EUX7g1JLU7+XopTC{H*h)#qu0wDP+A?t$4pp9z=l z`y1J&>giLDI3Vkw576x` zpSzTumW8Ch)qDuAlfBMZ>hi+EP=0u5^(s``-1oT`sy(zWZFkYH#=v-l8-a|*wIQ1B zkSl#};)TmW`W>i6jFb^0r)#8LMP1{@CvbjX?3m%5bvNSv%6zu?mxKDcR_i9|$4M$3 zrcYUHXn^WIz3;#Qjm9-FmQ9Z=8~UVWh4KYs>j4^+8I!wfxiGYB8Ij(E4b{b3+rfZ* zcE6rS?&zxv(u$z=z)lfZjc*~TRLfp%Qi{D4%f`N5DDU!2cDoA;+TguiNV{Sq#lAx< zOrHJ>i(f9I`NQ()S&lzgCqq6qEX%%#F4xt)+`p`tK-R61q6t&PgPI+ml`F<5I*`B6 z=quJ}rlj(k{q!iQabgH>wv91wQ!zo=F^eNf-T)0Y$B<%#N!&``uI z`vHu9wZ8o$YY>X(%kkgY?d8Ka`~9p~ZD=vn94%f+QLHAU;#f^{Ypqgg9jc4v%EtAp zOLHrwC%iG<2-la3)s{y%`N*gwL|rZA!_xI;mURyBs0~oiOloYg)4W%amV9xSXR=QD zX00V^%++|R@m6*{CZ*ZewvDRq@Va;A5_^p2Y2Vvfm#p(*9)*>KYP#hS?O)g#-xYGi z(Ne^Uic6Y&$fs-9{L@ua78AOlsd06WD_;B58jR*zVuIG66<=wsS)8eXyH?fWb!qWL z)~p%YIzN4YSDcws^n7jCO<$RG&M`XC`gg32H$*?n>+6b3ZmrfX89&pjBZu=f%?nCs z%{_Wr@z&a};_Bs^w6gb#ODLRD*QNOj`ET*quj$F5`FXxxp8b4L!x1%T#s58Ro@dnJ zFB8|KxzyC7jkuIgTg!V0o9Rm9)~8HugeYL+aAcv7)>TwU<#QFRPJpb6$p%ZynZI&QYR+m2W${B9wr956%C8JA^ zdHiKa`TV#Q_m@@5>e8pbe~JBs+XE_(msQE=(qkT8hLq2bTX9}iDXUAL{{AJr47X!d z9xtns(WS>cybLLyAGhMXtWs8&KK=bmco}YAtUO*;C8JA^d3YI8K0j{7d0C~bE`9p@ zm+&&&E?;@PtV%|g9`o=rqF-~{%W(UtC@l8gqPuVh|A+;RWiEtn1`1k z<@4iKoR?L~>e8pbe+e(c?N^t_%c^8_=`jy4L(1pJtvD~Ml+~qAfB#}$7T>{NJ*5!u z=*K6=bSgssEm-`M&wsArJ4#O}zgpu@oP$t){BJ;=*!qDod&RK>C z2~O?)xX!mcES+pf_ribTU{(GDyGJ~o%m2u^6P*dw|3q|lMW+h&-;kC)t)%mYI(4WM fOgdezl+LZ_UtT)rG$P%#_xi6;ehi;$N$>s#n6Iw1 diff --git a/src/Simulation/QSharpFoundation/Canon/NoOp.qs b/src/Simulation/QSharpFoundation/Canon/NoOp.qs index b9a778f6e26..9b83852176d 100644 --- a/src/Simulation/QSharpFoundation/Canon/NoOp.qs +++ b/src/Simulation/QSharpFoundation/Canon/NoOp.qs @@ -21,11 +21,6 @@ namespace Microsoft.Quantum.Canon { /// ## input /// A value to be ignored. /// - /// # Remarks - /// In almost all cases, the type parameter for `NoOp` needs to be specified - /// explicitly. For instance, `NoOp` is identical to - /// . - /// /// # See Also /// - Microsoft.Quantum.Intrinsic.I operation NoOp<'T>(input : 'T) : Unit is Adj + Ctl { diff --git a/src/Simulation/QSharpFoundation/Diagnostics/Assert.qs b/src/Simulation/QSharpFoundation/Diagnostics/Assert.qs index 994dd2170f2..f79281280ed 100644 --- a/src/Simulation/QSharpFoundation/Diagnostics/Assert.qs +++ b/src/Simulation/QSharpFoundation/Diagnostics/Assert.qs @@ -24,6 +24,18 @@ namespace Microsoft.Quantum.Diagnostics { /// # See Also /// - Microsoft.Quantum.Diagnostics.AssertMeasurementProbability /// - Microsoft.Quantum.Intrinsic.Measure + /// + /// # Example + /// The following snippet will execute without errors on the full-state + /// simulator: + /// ```qsharp + /// use q = Qubit(); + /// within { + /// H(q); + /// } apply { + /// AssertMeasurement([PauliX], [q], Zero, "Expected |+⟩ state."); + /// } + /// ``` operation AssertMeasurement(bases : Pauli[], qubits : Qubit[], result : Result, msg : String) : Unit is Adj + Ctl { body (...) { diff --git a/src/Simulation/QSharpFoundation/Diagnostics/Dump.qs b/src/Simulation/QSharpFoundation/Diagnostics/Dump.qs index 316b3305290..47109c3b503 100644 --- a/src/Simulation/QSharpFoundation/Diagnostics/Dump.qs +++ b/src/Simulation/QSharpFoundation/Diagnostics/Dump.qs @@ -10,9 +10,6 @@ namespace Microsoft.Quantum.Diagnostics { /// ## location /// Provides information on where to generate the machine's dump. /// - /// # Output - /// None. - /// /// # Remarks /// This method allows you to dump information about the current status of the /// target machine into a file or some other location. @@ -25,6 +22,20 @@ namespace Microsoft.Quantum.Diagnostics { /// the path to a file in which it will write the wave function as a /// one-dimensional array of complex numbers, in which each element represents /// the amplitudes of the probability of measuring the corresponding state. + /// + /// # Example + /// When run on the full-state simulator, the following snippet dumps + /// the Bell state $(\ket{00} + \ket{11}) / \sqrt{2}$ to the console: + /// ```qsharp + /// use left = Qubit(); + /// use right = Qubit(); + /// within { + /// H(left); + /// CNOT(left, right); + /// } apply { + /// DumpMachine(); + /// } + /// ``` function DumpMachine<'T> (location : 'T) : Unit { body intrinsic; } @@ -38,9 +49,6 @@ namespace Microsoft.Quantum.Diagnostics { /// ## qubits /// The list of qubits to report. /// - /// # Output - /// None. - /// /// # Remarks /// This method allows you to dump the information associated with the state of the /// given qubits into a file or some other location. @@ -56,6 +64,22 @@ namespace Microsoft.Quantum.Diagnostics { /// the amplitudes of the probability of measuring the corresponding state. /// If the given qubits are entangled with some other qubit and their /// state can't be separated, it just reports that the qubits are entangled. + /// + /// # Example + /// When run on the full-state simulator, the following snippet dumps + /// the Bell state $(\ket{00} + \ket{11}) / \sqrt{2}$ to the console: + /// ```qsharp + /// use left = Qubit(); + /// use right = Qubit(); + /// within { + /// H(left); + /// CNOT(left, right); + /// } apply { + /// // The () input here denotes that the state dumped by the + /// // full-state simulator should be reported to the console. + /// DumpRegister((), [left, right]); + /// } + /// ``` function DumpRegister<'T> (location : 'T, qubits : Qubit[]) : Unit { body intrinsic; } diff --git a/src/Simulation/QSharpFoundation/Diagnostics/UnitTests.qs b/src/Simulation/QSharpFoundation/Diagnostics/UnitTests.qs index 9b94aa84d9c..176278a06c2 100644 --- a/src/Simulation/QSharpFoundation/Diagnostics/UnitTests.qs +++ b/src/Simulation/QSharpFoundation/Diagnostics/UnitTests.qs @@ -12,6 +12,14 @@ namespace Microsoft.Quantum.Diagnostics { /// The name has to be either one of the known targets, or a fully qualified name. /// Known targets are: QuantumSimulator, ToffoliSimulator, ResourcesEstimator. /// + /// # Example + /// The following is a unit test that checks if `2 + 3` is `5`: + /// ```qsharp + /// @Test("QuantumSimulator") + /// function AdditionIsCorrect() : Unit { + /// EqualityFactI(2 + 3, 5, "Addition did not work correctly."); + /// } + /// ``` @Attribute() newtype Test = (ExecutionTarget : String); diff --git a/src/Simulation/QSharpFoundation/Math/Types.qs b/src/Simulation/QSharpFoundation/Math/Types.qs index 722aba91eb8..5383426fc87 100644 --- a/src/Simulation/QSharpFoundation/Math/Types.qs +++ b/src/Simulation/QSharpFoundation/Math/Types.qs @@ -11,6 +11,12 @@ namespace Microsoft.Quantum.Math { /// # Summary /// Represents a complex number by its real and imaginary components. /// The first element of the tuple is the real component, the second one - the imaginary component. + /// + /// # Example + /// The following snippet defines the imaginary unit $0 + 1i$: + /// ```qsharp + /// let imagUnit = Complex(0.0, 1.0); + /// ``` newtype Complex = (Real: Double, Imag: Double); } diff --git a/src/Simulation/QSharpFoundation/Message.qs b/src/Simulation/QSharpFoundation/Message.qs index 2a5a920491b..13cc71484cf 100644 --- a/src/Simulation/QSharpFoundation/Message.qs +++ b/src/Simulation/QSharpFoundation/Message.qs @@ -12,6 +12,14 @@ namespace Microsoft.Quantum.Intrinsic { /// # Remarks /// The specific behavior of this function is simulator-dependent, /// but in most cases the given message will be written to the console. + /// + /// # Example + /// The following causes `"Hello, world!"` to be reported (typically to + /// the console): + /// ```qsharp + /// let name = "world"; + /// Message($"Hello, {name}!"); + /// ``` function Message (msg : String) : Unit { body intrinsic; } From 0a4148266d252c032efb7aa6a807901f58700629 Mon Sep 17 00:00:00 2001 From: Andres Paz Date: Wed, 16 Jun 2021 18:47:17 -0700 Subject: [PATCH 5/7] Apply suggestions from code review Co-authored-by: Chris Granade --- .../CredentialFactoryTests.cs | 3 ++- .../Azure.Quantum.Client.Test/Helpers/Problem.cs | 2 +- .../JobManagement/ProviderStatusInfo.cs | 15 ++------------- .../JobManagement/QuotaInfo.cs | 2 +- src/Simulation/EntryPointDriver/Azure.cs | 7 ++++--- 5 files changed, 10 insertions(+), 19 deletions(-) diff --git a/src/Azure/Azure.Quantum.Client.Test/CredentialFactoryTests.cs b/src/Azure/Azure.Quantum.Client.Test/CredentialFactoryTests.cs index 908224c54ef..5786e14dc17 100644 --- a/src/Azure/Azure.Quantum.Client.Test/CredentialFactoryTests.cs +++ b/src/Azure/Azure.Quantum.Client.Test/CredentialFactoryTests.cs @@ -1,4 +1,5 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. +// Copyright (c) Microsoft Corporation. +``` #Pending // Licensed under the MIT License. using System; diff --git a/src/Azure/Azure.Quantum.Client.Test/Helpers/Problem.cs b/src/Azure/Azure.Quantum.Client.Test/Helpers/Problem.cs index 2fcd64c1dec..25c4012ebeb 100644 --- a/src/Azure/Azure.Quantum.Client.Test/Helpers/Problem.cs +++ b/src/Azure/Azure.Quantum.Client.Test/Helpers/Problem.cs @@ -1,4 +1,4 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. +// Copyright (c) Microsoft Corporation. // Licensed under the MIT License. using System.Collections.Generic; diff --git a/src/Azure/Azure.Quantum.Client/JobManagement/ProviderStatusInfo.cs b/src/Azure/Azure.Quantum.Client/JobManagement/ProviderStatusInfo.cs index 222f496c5de..3d221d82df0 100644 --- a/src/Azure/Azure.Quantum.Client/JobManagement/ProviderStatusInfo.cs +++ b/src/Azure/Azure.Quantum.Client/JobManagement/ProviderStatusInfo.cs @@ -56,19 +56,8 @@ protected ProviderStatusInfo() /// /// 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); - } - } - } - } + public virtual IEnumerable? Targets => + this.Details?.Targets?.Select(ps => new TargetStatusInfo(ps)); /// /// Gets the provider status information. diff --git a/src/Azure/Azure.Quantum.Client/JobManagement/QuotaInfo.cs b/src/Azure/Azure.Quantum.Client/JobManagement/QuotaInfo.cs index a36ca7ca7ee..12e3d8580aa 100644 --- a/src/Azure/Azure.Quantum.Client/JobManagement/QuotaInfo.cs +++ b/src/Azure/Azure.Quantum.Client/JobManagement/QuotaInfo.cs @@ -10,7 +10,7 @@ namespace Microsoft.Azure.Quantum using Microsoft.Azure.Quantum.Utility; /// - /// Wrapper for Azure.Quantum.Jobs.Models.QuantumJobQuota. + /// Wrapper for . /// public class QuotaInfo { diff --git a/src/Simulation/EntryPointDriver/Azure.cs b/src/Simulation/EntryPointDriver/Azure.cs index 4cbd1b28c1d..d8515bfadce 100644 --- a/src/Simulation/EntryPointDriver/Azure.cs +++ b/src/Simulation/EntryPointDriver/Azure.cs @@ -237,12 +237,12 @@ public override ValueTask GetTokenAsync(TokenRequestContext request public string? Subscription { get; set; } /// - /// The Azure Quantum Workspace's resource group name. + /// The Azure Quantum workspace's resource group name. /// public string? ResourceGroup { get; set; } /// - /// The Azure Quantum Workspace's name. + /// The Azure Quantum workspace's name. /// public string? Workspace { get; set; } @@ -263,7 +263,8 @@ public override ValueTask GetTokenAsync(TokenRequestContext request /// /// The type of Credentials to use to authenticate with Azure. For more information - /// about authentication with Azure services see: https://docs.microsoft.com/en-us/dotnet/api/overview/azure/identity-readme + /// about authentication with Azure services see: https://docs.microsoft.com/dotnet/api/overview/azure/identity-readme +``` #Pending /// NOTE: If both and properties are specified, takes precedence. /// If none are provided, then it uses . /// From 80062b163da96b58a861bedc7e1153308e89db57 Mon Sep 17 00:00:00 2001 From: Andres Paz Date: Wed, 16 Jun 2021 19:57:50 -0700 Subject: [PATCH 6/7] Incorporating feedback. --- .../CredentialFactoryTests.cs | 5 +++-- .../Authentication/CredentialFactory.cs | 20 ++++++++--------- .../JobManagement/CloudJob.cs | 22 +++++++++---------- .../JobManagement/ProviderStatusInfo.cs | 1 + src/Simulation/EntryPointDriver/Azure.cs | 1 - 5 files changed, 25 insertions(+), 24 deletions(-) diff --git a/src/Azure/Azure.Quantum.Client.Test/CredentialFactoryTests.cs b/src/Azure/Azure.Quantum.Client.Test/CredentialFactoryTests.cs index 5786e14dc17..51414d5334f 100644 --- a/src/Azure/Azure.Quantum.Client.Test/CredentialFactoryTests.cs +++ b/src/Azure/Azure.Quantum.Client.Test/CredentialFactoryTests.cs @@ -1,7 +1,8 @@ // Copyright (c) Microsoft Corporation. -``` #Pending // Licensed under the MIT License. +#nullable enable + using System; using Azure.Identity; @@ -66,7 +67,7 @@ public void TestGetTenantId() [DataRow("string=with,invalid=authorization_uri,authorization_uri=http://foo.bar.com/some-random-value", null)] [DataRow("string=missing,tenant_id=authorization_uri,authorization_uri=\"http://foo.bar.com/", null)] [DataRow("authorization_uri=\"https://login.microsoftonline.com/tenantId\",key1=value1s,etc...", "tenantId")] - public void TestExtractTenantIdFromBearer(string bearer, string expected) + public void TestExtractTenantIdFromBearer(string? bearer, string? expected) { var actual = CredentialFactory.ExtractTenantIdFromBearer(bearer); Assert.AreEqual(expected, actual); diff --git a/src/Azure/Azure.Quantum.Client/Authentication/CredentialFactory.cs b/src/Azure/Azure.Quantum.Client/Authentication/CredentialFactory.cs index ce0f3347e04..8a1892a5bc4 100644 --- a/src/Azure/Azure.Quantum.Client/Authentication/CredentialFactory.cs +++ b/src/Azure/Azure.Quantum.Client/Authentication/CredentialFactory.cs @@ -18,49 +18,49 @@ namespace Microsoft.Azure.Quantum.Authentication /// authentication in Azure Quantum. /// NOTE: For more information /// about authentication with Azure services and the different Credential types see - /// https://docs.microsoft.com/en-us/dotnet/api/overview/azure/identity-readme. + /// https://docs.microsoft.com/dotnet/api/overview/azure/identity-readme. /// public enum CredentialType { /// /// Provides a simplified authentication experience to quickly start developing applications run in the Azure cloud. - /// See: https://docs.microsoft.com/en-us/dotnet/api/azure.identity.defaultazurecredential + /// See: https://docs.microsoft.com/dotnet/api/azure.identity.defaultazurecredential /// Default, /// /// Authenticates a service principal or user via credential information specified in environment variables. - /// See: https://docs.microsoft.com/en-us/dotnet/api/azure.identity.environmentcredential + /// See: https://docs.microsoft.com/dotnet/api/azure.identity.environmentcredential /// Environment, /// /// Authenticates the managed identity of an azure resource. - /// See: https://docs.microsoft.com/en-us/dotnet/api/azure.identity.managedidentitycredential + /// See: https://docs.microsoft.com/dotnet/api/azure.identity.managedidentitycredential /// ManagedIdentity, /// /// Authenticate in a development environment with the Azure CLI. - /// See https://docs.microsoft.com/en-us/dotnet/api/azure.identity.azureclicredential + /// See https://docs.microsoft.com/dotnet/api/azure.identity.azureclicredential /// CLI, /// /// Authenticate using tokens in the local cache shared between Microsoft applications. - /// See: https://docs.microsoft.com/en-us/dotnet/api/azure.identity.sharedtokencachecredential + /// See: https://docs.microsoft.com/dotnet/api/azure.identity.sharedtokencachecredential /// SharedToken, /// /// Authenticate using data from Visual Studio. - /// See: https://docs.microsoft.com/en-us/dotnet/api/azure.identity.visualstudiocredential + /// See: https://docs.microsoft.com/dotnet/api/azure.identity.visualstudiocredential /// VisualStudio, /// /// Authenticate in a development environment with Visual Studio Code. - /// See: https://docs.microsoft.com/en-us/dotnet/api/azure.identity.visualstudiocodecredential + /// See: https://docs.microsoft.com/dotnet/api/azure.identity.visualstudiocodecredential /// VisualStudioCode, @@ -68,14 +68,14 @@ public enum CredentialType /// A TokenCredential implementation which launches the system default browser to interactively authenticate a user, /// and obtain an access token. The browser will only be launched to authenticate the user once, /// then will silently acquire access tokens through the users refresh token as long as it's valid. - /// See: https://docs.microsoft.com/en-us/dotnet/api/azure.identity.interactivebrowsercredential + /// See: https://docs.microsoft.com/dotnet/api/azure.identity.interactivebrowsercredential /// Interactive, /// /// A TokenCredential implementation which authenticates a user using the device code flow, /// and provides access tokens for that user account. - /// See: https://docs.microsoft.com/en-us/dotnet/api/azure.identity.devicecodecredential + /// See: https://docs.microsoft.com/dotnet/api/azure.identity.devicecodecredential /// DeviceCode, } diff --git a/src/Azure/Azure.Quantum.Client/JobManagement/CloudJob.cs b/src/Azure/Azure.Quantum.Client/JobManagement/CloudJob.cs index aa7c5eb30fd..1da6fbf039a 100644 --- a/src/Azure/Azure.Quantum.Client/JobManagement/CloudJob.cs +++ b/src/Azure/Azure.Quantum.Client/JobManagement/CloudJob.cs @@ -21,7 +21,7 @@ public class CloudJob : IQuantumMachineJob /// Initializes a new instance of the class. /// /// The workspace. - /// The job details. + /// The job Details?. public CloudJob(IWorkspace workspace, JobDetails jobDetails) { Ensure.NotNull(workspace, nameof(workspace)); @@ -40,12 +40,12 @@ public CloudJob(IWorkspace workspace, JobDetails jobDetails) /// /// Gets the job id. /// - public virtual string Id => Details.Id; + public virtual string Id => Details?.Id; /// /// Gets the job id. /// - public virtual string Name => Details.Name; + public virtual string Name => Details?.Name; /// /// Gets whether the job execution has completed. @@ -57,7 +57,7 @@ public CloudJob(IWorkspace workspace, JobDetails jobDetails) /// /// Gets the status of the submitted job. /// - public virtual string Status => Details.Status?.ToString(); + public virtual string Status => Details?.Status?.ToString(); /// /// Gets whether the job execution completed successfully. @@ -75,20 +75,20 @@ public CloudJob(IWorkspace workspace, JobDetails jobDetails) public virtual IWorkspace Workspace { get; private set; } /// - /// Gets or sets the unique identifier for the provider. + /// Gets the unique identifier for the provider. /// public virtual string ProviderId => this.Details?.ProviderId; /// - /// Gets or sets the target identifier to run the job. + /// Gets the target identifier to run the job. /// - public string Target => this.Details.Target; + 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) + ? new Uri(this.Details?.OutputDataUri) : null; /// @@ -97,7 +97,7 @@ public CloudJob(IWorkspace workspace, JobDetails jobDetails) public virtual string OutputDataFormat => this.Details?.OutputDataFormat; /// - /// Gets the underlying job details. + /// Gets the underlying job Details?. /// public virtual JobDetails Details { get; private set; } @@ -108,7 +108,7 @@ public CloudJob(IWorkspace workspace, JobDetails jobDetails) /// public virtual async Task RefreshAsync(CancellationToken cancellationToken = default) { - CloudJob job = (CloudJob)await this.Workspace.GetJobAsync(this.Details.Id, cancellationToken); + CloudJob job = (CloudJob)await this.Workspace.GetJobAsync(this.Details?.Id, cancellationToken); this.Details = job.Details; } @@ -119,7 +119,7 @@ public virtual async Task RefreshAsync(CancellationToken cancellationToken = def /// public virtual async Task CancelAsync(CancellationToken cancellationToken = default) { - CloudJob job = (CloudJob)await this.Workspace.CancelJobAsync(this.Details.Id, cancellationToken); + CloudJob job = (CloudJob)await this.Workspace.CancelJobAsync(this.Details?.Id, cancellationToken); this.Details = job.Details; } diff --git a/src/Azure/Azure.Quantum.Client/JobManagement/ProviderStatusInfo.cs b/src/Azure/Azure.Quantum.Client/JobManagement/ProviderStatusInfo.cs index 3d221d82df0..088d8ec2d12 100644 --- a/src/Azure/Azure.Quantum.Client/JobManagement/ProviderStatusInfo.cs +++ b/src/Azure/Azure.Quantum.Client/JobManagement/ProviderStatusInfo.cs @@ -6,6 +6,7 @@ namespace Microsoft.Azure.Quantum { using System.Collections.Generic; + using System.Linq; using Microsoft.Azure.Quantum.Utility; diff --git a/src/Simulation/EntryPointDriver/Azure.cs b/src/Simulation/EntryPointDriver/Azure.cs index d8515bfadce..eb43ae2e193 100644 --- a/src/Simulation/EntryPointDriver/Azure.cs +++ b/src/Simulation/EntryPointDriver/Azure.cs @@ -264,7 +264,6 @@ public override ValueTask GetTokenAsync(TokenRequestContext request /// /// The type of Credentials to use to authenticate with Azure. For more information /// about authentication with Azure services see: https://docs.microsoft.com/dotnet/api/overview/azure/identity-readme -``` #Pending /// NOTE: If both and properties are specified, takes precedence. /// If none are provided, then it uses . /// From 39c2321c3bbd3eb45fce271e6a8d9886b7817291 Mon Sep 17 00:00:00 2001 From: Chris Granade Date: Thu, 17 Jun 2021 05:54:04 +0000 Subject: [PATCH 7/7] Build 0.17.2106.147878. --- global.json | 2 +- .../Microsoft.Quantum.Development.Kit.nuspec | 2 +- src/Simulation/AutoSubstitution/AutoSubstitution.csproj | 2 +- .../CSharpGeneration/Microsoft.Quantum.CSharpGeneration.fsproj | 2 +- src/Xunit/Microsoft.Quantum.Xunit.nuspec | 2 +- 5 files changed, 5 insertions(+), 5 deletions(-) diff --git a/global.json b/global.json index 703a5dae1e1..b3578936b91 100644 --- a/global.json +++ b/global.json @@ -1,5 +1,5 @@ { "msbuild-sdks": { - "Microsoft.Quantum.Sdk": "0.17.2106145735-alpha" + "Microsoft.Quantum.Sdk": "0.17.2106147878-beta" } } diff --git a/src/Quantum.Development.Kit/Microsoft.Quantum.Development.Kit.nuspec b/src/Quantum.Development.Kit/Microsoft.Quantum.Development.Kit.nuspec index 2b2a6cd0a2b..03fb92cef4f 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/AutoSubstitution/AutoSubstitution.csproj b/src/Simulation/AutoSubstitution/AutoSubstitution.csproj index 636efa33244..bb6f737fba1 100644 --- a/src/Simulation/AutoSubstitution/AutoSubstitution.csproj +++ b/src/Simulation/AutoSubstitution/AutoSubstitution.csproj @@ -12,7 +12,7 @@ - + diff --git a/src/Simulation/CSharpGeneration/Microsoft.Quantum.CSharpGeneration.fsproj b/src/Simulation/CSharpGeneration/Microsoft.Quantum.CSharpGeneration.fsproj index f1838ea1cb4..088820b458c 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 053332e1ef4..03bcb5d652b 100644 --- a/src/Xunit/Microsoft.Quantum.Xunit.nuspec +++ b/src/Xunit/Microsoft.Quantum.Xunit.nuspec @@ -22,7 +22,7 @@ - +