├── CODE_OF_CONDUCT.md ├── MicrosoftQuantumCrypto ├── IsMinimizeTCostMetric.cs ├── IsMinimizeWidthCostMetric.cs ├── IsMinimizeDepthCostMetric.cs ├── IsTestable.cs ├── GlobalSuppressions.cs ├── TestSuiteRunner.cs ├── MicrosoftQuantumCrypto.csproj ├── Tests │ ├── ReadMe.md │ └── DebugHelpers.qs ├── Counters.qs └── EllipticCurves.qs ├── ResourceEstimator ├── ResourceEstimator.csproj ├── QDecimalConverter.cs ├── GlobalSuppressions.cs ├── DriverParameters.cs ├── DepthCounterCSV.cs ├── WidthCounterCSV.cs ├── OperationCounterCSV.cs ├── README.md ├── DisplayCSV.cs ├── shor_estimate.py ├── ResourceEstimateWrappers.qs └── Driver.cs ├── LICENSE ├── .vscode ├── launch.json └── tasks.json ├── QuantumEllipticCurves.sln ├── SECURITY.md ├── README.md └── .gitignore /CODE_OF_CONDUCT.md: -------------------------------------------------------------------------------- 1 | # Microsoft Open Source Code of Conduct 2 | 3 | This project has adopted the [Microsoft Open Source Code of Conduct](https://opensource.microsoft.com/codeofconduct/). 4 | 5 | Resources: 6 | 7 | - [Microsoft Open Source Code of Conduct](https://opensource.microsoft.com/codeofconduct/) 8 | - [Microsoft Code of Conduct FAQ](https://opensource.microsoft.com/codeofconduct/faq/) 9 | - Contact [opencode@microsoft.com](mailto:opencode@microsoft.com) with questions or concerns 10 | -------------------------------------------------------------------------------- /MicrosoftQuantumCrypto/IsMinimizeTCostMetric.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Corporation. 2 | // Licensed under the MIT license. 3 | 4 | namespace Microsoft.Quantum.Crypto.Basics 5 | { 6 | using System; 7 | using Microsoft.Quantum.Simulation.Core; 8 | 9 | public partial class IsMinimizeTCostMetric 10 | { 11 | public class Native : IsMinimizeTCostMetric 12 | { 13 | public Native(IOperationFactory m) 14 | : base(m) 15 | { 16 | } 17 | 18 | public override Func __Body__ => IsMinimizeTCostMetricFunc; 19 | 20 | public static bool IsMinimizeTCostMetricFunc(QVoid qVoid) 21 | { 22 | return 23 | #if MINIMIZE_T 24 | true; 25 | #else 26 | false; 27 | #endif 28 | } 29 | } 30 | } 31 | } -------------------------------------------------------------------------------- /MicrosoftQuantumCrypto/IsMinimizeWidthCostMetric.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Corporation. 2 | // Licensed under the MIT license. 3 | 4 | namespace Microsoft.Quantum.Crypto.Basics 5 | { 6 | using System; 7 | using Microsoft.Quantum.Simulation.Core; 8 | 9 | public partial class IsMinimizeWidthCostMetric 10 | { 11 | public class Native : IsMinimizeWidthCostMetric 12 | { 13 | public Native(IOperationFactory m) 14 | : base(m) 15 | { 16 | } 17 | 18 | public override Func __Body__ => IsMinimizeWidthCostMetricFunc; 19 | 20 | public static bool IsMinimizeWidthCostMetricFunc(QVoid qVoid) 21 | { 22 | return 23 | #if MINIMIZE_WIDTH 24 | true; 25 | #else 26 | false; 27 | #endif 28 | } 29 | } 30 | } 31 | } -------------------------------------------------------------------------------- /MicrosoftQuantumCrypto/IsMinimizeDepthCostMetric.cs: -------------------------------------------------------------------------------- 1 | 2 | // Copyright (c) Microsoft Corporation. 3 | // Licensed under the MIT license. 4 | 5 | namespace Microsoft.Quantum.Crypto.Basics 6 | { 7 | using System; 8 | using Microsoft.Quantum.Simulation.Core; 9 | 10 | public partial class IsMinimizeDepthCostMetric 11 | { 12 | public class Native : IsMinimizeDepthCostMetric 13 | { 14 | public Native(IOperationFactory m) 15 | : base(m) 16 | { 17 | } 18 | 19 | public override Func __Body__ => IsMinimizeDepthCostMetricFunc; 20 | 21 | public static bool IsMinimizeDepthCostMetricFunc(QVoid qVoid) 22 | { 23 | return 24 | #if MINIMIZE_DEPTH 25 | true; 26 | #else 27 | false; 28 | #endif 29 | } 30 | } 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /MicrosoftQuantumCrypto/IsTestable.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Corporation. 2 | // Licensed under the MIT license. 3 | 4 | namespace Microsoft.Quantum.Crypto.Basics 5 | { 6 | using System; 7 | using Microsoft.Quantum.Simulation.Core; 8 | using Microsoft.Quantum.Simulation.QCTraceSimulatorRuntime; 9 | 10 | public partial class IsTestable 11 | { 12 | /// 13 | /// Set to true to run unit tests, false to run the resource estimator. 14 | /// 15 | public static bool Testable { set; get; } = true; 16 | 17 | public class Native : IsTestable 18 | { 19 | public Native(IOperationFactory m) 20 | : base(m) 21 | { 22 | } 23 | 24 | public override Func __Body__ => IsTestableFunc; 25 | 26 | public static bool IsTestableFunc(QVoid qVoid) 27 | { 28 | return Testable; 29 | } 30 | } 31 | } 32 | } -------------------------------------------------------------------------------- /ResourceEstimator/ResourceEstimator.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Exe 5 | netcoreapp3.1 6 | false 7 | x64 8 | Debug;Release 9 | Microsoft.Quantum.Crypto.ResourceEstimator 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | -------------------------------------------------------------------------------- /ResourceEstimator/QDecimalConverter.cs: -------------------------------------------------------------------------------- 1 | namespace Microsoft.Quantum.Crypto.ResourceEstimator.CommaSeparated 2 | { 3 | using System; 4 | using System.Collections.Generic; 5 | using System.Globalization; 6 | using FileHelpers; 7 | 8 | public class QDecimalConverter : ConverterBase 9 | { 10 | public override object StringToField(string from) 11 | { 12 | if (from == "NaN") 13 | { 14 | return Convert.ToDecimal(-1); 15 | } 16 | else 17 | { 18 | return decimal.Parse(from, NumberStyles.AllowExponent | NumberStyles.AllowDecimalPoint); 19 | } 20 | } 21 | 22 | public override string FieldToString(object fieldValue) 23 | { 24 | if (Convert.ToDecimal(fieldValue) == Convert.ToDecimal(-1)) 25 | { 26 | return "NaN"; 27 | } 28 | else 29 | { 30 | return ((decimal)fieldValue).ToString(); 31 | } 32 | } 33 | } 34 | } -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) Microsoft Corporation. 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE 22 | -------------------------------------------------------------------------------- /MicrosoftQuantumCrypto/GlobalSuppressions.cs: -------------------------------------------------------------------------------- 1 | // This file is used by Code Analysis to maintain SuppressMessage 2 | // attributes that are applied to this project. 3 | // Project-level suppressions either have no target or are given 4 | // a specific target and scoped to a namespace, type, member, etc. 5 | 6 | using System.Diagnostics.CodeAnalysis; 7 | 8 | [assembly: SuppressMessage("StyleCop.CSharp.DocumentationRules", "SA1633:File should have header", Justification = "Project style")] 9 | [assembly: SuppressMessage("StyleCop.CSharp.MaintainabilityRules", "SA1402:File may only contain a single type", Justification = "Project style")] 10 | [assembly: SuppressMessage("StyleCop.CSharp.LayoutRules", "SA1515:Single-line comment should be preceded by blank line", Justification = "Project style")] 11 | [assembly: SuppressMessage("StyleCop.CSharp.OrderingRules", "SA1201:Elements should appear in the correct order", Justification = "Project style")] 12 | [assembly: SuppressMessage("StyleCop.CSharp.ReadabilityRules", "SA1128:Put constructor initializers on their own line", Justification = "Project style")] 13 | [assembly: SuppressMessage("StyleCop.CSharp.LayoutRules", "SA1502:Element should not be on a single line", Justification = "Project style")] 14 | -------------------------------------------------------------------------------- /.vscode/launch.json: -------------------------------------------------------------------------------- 1 | { 2 | // Use IntelliSense to find out which attributes exist for C# debugging 3 | // Use hover for the description of the existing attributes 4 | // For further information visit https://github.com/OmniSharp/omnisharp-vscode/blob/master/debugger-launchjson.md 5 | "version": "0.2.0", 6 | "configurations": [ 7 | { 8 | "name": ".NET Core Launch (console)", 9 | "type": "coreclr", 10 | "request": "launch", 11 | "preLaunchTask": "build", 12 | // If you have changed target frameworks, make sure to update the program path. 13 | "program": "${workspaceFolder}/MicrosoftQuantumCrypto/bin/Debug/netcoreapp3.1/MicrosoftQuantumCrypto.dll", 14 | "args": [], 15 | "cwd": "${workspaceFolder}/MicrosoftQuantumCrypto", 16 | // For more information about the 'console' field, see https://aka.ms/VSCode-CS-LaunchJson-Console 17 | "console": "internalConsole", 18 | "stopAtEntry": false 19 | }, 20 | { 21 | "name": ".NET Core Attach", 22 | "type": "coreclr", 23 | "request": "attach", 24 | "processId": "${command:pickProcess}" 25 | } 26 | ] 27 | } -------------------------------------------------------------------------------- /MicrosoftQuantumCrypto/TestSuiteRunner.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Corporation. 2 | // Licensed under the MIT license. 3 | 4 | namespace Test 5 | { 6 | using System.Diagnostics; 7 | using Microsoft.Quantum.Simulation.Simulators; 8 | using Microsoft.Quantum.Simulation.XUnit; 9 | using Xunit.Abstractions; 10 | 11 | public class TestSuiteRunner 12 | { 13 | private readonly ITestOutputHelper output; 14 | 15 | public TestSuiteRunner(ITestOutputHelper output) 16 | { 17 | this.output = output; 18 | } 19 | 20 | /// 21 | /// This driver will run all Q# tests (operations named "...Test") 22 | /// that belong to namespace Test. 23 | /// 24 | /// To execute your tests, just type "dotnet test" from the command line. 25 | /// 26 | [OperationDriver(TestNamespace = "Microsoft.Quantum.Crypto.Tests")] 27 | public void TestTarget(TestOperation op) 28 | { 29 | var sim = new ToffoliSimulator(); 30 | 31 | // OnLog defines action(s) performed when Q# test calls function Message 32 | sim.OnLog += (msg) => { this.output.WriteLine(msg); }; 33 | sim.OnLog += (msg) => { Debug.WriteLine(msg); }; 34 | op.TestOperationRunner(sim); 35 | } 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /.vscode/tasks.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": "2.0.0", 3 | "tasks": [ 4 | { 5 | "label": "build", 6 | "command": "dotnet", 7 | "type": "process", 8 | "args": [ 9 | "build", 10 | "${workspaceFolder}/MicrosoftQuantumCrypto/MicrosoftQuantumCrypto.csproj", 11 | "/property:GenerateFullPaths=true", 12 | "/consoleloggerparameters:NoSummary" 13 | ], 14 | "problemMatcher": "$msCompile" 15 | }, 16 | { 17 | "label": "publish", 18 | "command": "dotnet", 19 | "type": "process", 20 | "args": [ 21 | "publish", 22 | "${workspaceFolder}/MicrosoftQuantumCrypto/MicrosoftQuantumCrypto.csproj", 23 | "/property:GenerateFullPaths=true", 24 | "/consoleloggerparameters:NoSummary" 25 | ], 26 | "problemMatcher": "$msCompile" 27 | }, 28 | { 29 | "label": "watch", 30 | "command": "dotnet", 31 | "type": "process", 32 | "args": [ 33 | "watch", 34 | "run", 35 | "${workspaceFolder}/MicrosoftQuantumCrypto/MicrosoftQuantumCrypto.csproj", 36 | "/property:GenerateFullPaths=true", 37 | "/consoleloggerparameters:NoSummary" 38 | ], 39 | "problemMatcher": "$msCompile" 40 | } 41 | ] 42 | } -------------------------------------------------------------------------------- /MicrosoftQuantumCrypto/MicrosoftQuantumCrypto.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | library 5 | netcoreapp3.1 6 | x64 7 | MinimizeWidth;MinimizeDepth;MinimizeT 8 | Microsoft.Quantum.Crypto 9 | 10 | 11 | 12 | MINIMIZE_DEPTH 13 | 14 | 15 | 16 | MINIMIZE_T 17 | 18 | 19 | 20 | MINIMIZE_WIDTH 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | all 30 | runtime; build; native; contentfiles; analyzers; buildtransitive 31 | 32 | 33 | 34 | 35 | 36 | -------------------------------------------------------------------------------- /ResourceEstimator/GlobalSuppressions.cs: -------------------------------------------------------------------------------- 1 | // This file is used by Code Analysis to maintain SuppressMessage 2 | // attributes that are applied to this project. 3 | // Project-level suppressions either have no target or are given 4 | // a specific target and scoped to a namespace, type, member, etc. 5 | 6 | using System.Diagnostics.CodeAnalysis; 7 | 8 | [assembly: SuppressMessage("StyleCop.CSharp.DocumentationRules", "SA1649:File name should match first type name", Justification = "Project style")] 9 | [assembly: SuppressMessage("StyleCop.CSharp.LayoutRules", "SA1512:Single-line comments should not be followed by blank line", Justification = "Project style")] 10 | [assembly: SuppressMessage("StyleCop.CSharp.DocumentationRules", "SA1633:File should have header", Justification = "Project style")] 11 | [assembly: SuppressMessage("StyleCop.CSharp.MaintainabilityRules", "SA1401:Fields should be private", Justification = "Project style")] 12 | [assembly: SuppressMessage("StyleCop.CSharp.MaintainabilityRules", "SA1402:File may only contain a single type", Justification = "Project style")] 13 | [assembly: SuppressMessage("StyleCop.CSharp.LayoutRules", "SA1515:Single-line comment should be preceded by blank line", Justification = "Project style")] 14 | [assembly: SuppressMessage("StyleCop.CSharp.ReadabilityRules", "SA1117:Parameters should be on same line or separate lines", Justification = "Project style")] 15 | [assembly: SuppressMessage("StyleCop.CSharp.LayoutRules", "SA1501:Statement should not be on a single line", Justification = "Project style")] 16 | [assembly: SuppressMessage("StyleCop.CSharp.ReadabilityRules", "SA1122:Use string.Empty for empty strings", Justification = "Project style")] 17 | [assembly: SuppressMessage("StyleCop.CSharp.NamingRules", "SA1314:Type parameter names should begin with T", Justification = "Project style")] 18 | [assembly: SuppressMessage("StyleCop.CSharp.ReadabilityRules", "SA1121:Use built-in type alias", Justification = "Project style")] 19 | -------------------------------------------------------------------------------- /ResourceEstimator/DriverParameters.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Corporation. 2 | // Licensed under the MIT license. 3 | 4 | namespace Microsoft.Quantum.Crypto.ResourceEstimator 5 | { 6 | using System; 7 | using Microsoft.Quantum.Crypto.Basics; 8 | using Microsoft.Quantum.Simulation.Simulators; 9 | using Microsoft.Quantum.Simulation.Simulators.QCTraceSimulators; 10 | 11 | public static class DriverParameters 12 | { 13 | static DriverParameters() 14 | { 15 | // Turn off IsTestable to run the resource estimator 16 | Microsoft.Quantum.Crypto.Basics.IsTestable.Testable = false; 17 | DriverParameters.IsTestable = false; 18 | 19 | var qsim = new ToffoliSimulator(); 20 | DriverParameters.MinimizeDepthCostMetric = IsMinimizeDepthCostMetric.Run(qsim).Result; 21 | DriverParameters.MinimizeTCostMetric = IsMinimizeTCostMetric.Run(qsim).Result; 22 | DriverParameters.MinimizeWidthCostMetric = IsMinimizeWidthCostMetric.Run(qsim).Result; 23 | } 24 | 25 | public static bool IsTestable { get; private set; } 26 | 27 | public static bool MinimizeDepthCostMetric { get; private set; } 28 | 29 | public static bool MinimizeTCostMetric { get; private set; } 30 | 31 | public static bool MinimizeWidthCostMetric { get; private set; } 32 | 33 | public static void Print() 34 | { 35 | if (IsTestable) 36 | { 37 | Console.WriteLine("Running testable functions"); 38 | } 39 | else 40 | { 41 | Console.WriteLine("Running non-testable functions"); 42 | } 43 | 44 | if (MinimizeDepthCostMetric) 45 | { 46 | Console.WriteLine("Minimizing depth"); 47 | } 48 | else if (MinimizeTCostMetric) 49 | { 50 | Console.WriteLine("Minimizing T gates"); 51 | } 52 | else 53 | { 54 | Console.WriteLine("Minimizing width"); 55 | } 56 | } 57 | } 58 | } -------------------------------------------------------------------------------- /QuantumEllipticCurves.sln: -------------------------------------------------------------------------------- 1 | 2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio Version 16 4 | VisualStudioVersion = 16.0.30128.74 5 | MinimumVisualStudioVersion = 10.0.40219.1 6 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MicrosoftQuantumCrypto", "MicrosoftQuantumCrypto\MicrosoftQuantumCrypto.csproj", "{E984B589-55A4-46BA-B656-28C1C1201567}" 7 | EndProject 8 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ResourceEstimator", "ResourceEstimator\ResourceEstimator.csproj", "{DC21FFF9-730B-4E5C-A437-9BEA928F68E5}" 9 | ProjectSection(ProjectDependencies) = postProject 10 | {E984B589-55A4-46BA-B656-28C1C1201567} = {E984B589-55A4-46BA-B656-28C1C1201567} 11 | EndProjectSection 12 | EndProject 13 | Global 14 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 15 | MinimizeDepth|Any CPU = MinimizeDepth|Any CPU 16 | MinimizeT|Any CPU = MinimizeT|Any CPU 17 | MinimizeWidth|Any CPU = MinimizeWidth|Any CPU 18 | EndGlobalSection 19 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 20 | {E984B589-55A4-46BA-B656-28C1C1201567}.MinimizeDepth|Any CPU.ActiveCfg = MinimizeDepth|Any CPU 21 | {E984B589-55A4-46BA-B656-28C1C1201567}.MinimizeDepth|Any CPU.Build.0 = MinimizeDepth|Any CPU 22 | {E984B589-55A4-46BA-B656-28C1C1201567}.MinimizeT|Any CPU.ActiveCfg = MinimizeT|Any CPU 23 | {E984B589-55A4-46BA-B656-28C1C1201567}.MinimizeT|Any CPU.Build.0 = MinimizeT|Any CPU 24 | {E984B589-55A4-46BA-B656-28C1C1201567}.MinimizeWidth|Any CPU.ActiveCfg = MinimizeWidth|Any CPU 25 | {E984B589-55A4-46BA-B656-28C1C1201567}.MinimizeWidth|Any CPU.Build.0 = MinimizeWidth|Any CPU 26 | {DC21FFF9-730B-4E5C-A437-9BEA928F68E5}.MinimizeDepth|Any CPU.ActiveCfg = Release|Any CPU 27 | {DC21FFF9-730B-4E5C-A437-9BEA928F68E5}.MinimizeDepth|Any CPU.Build.0 = Release|Any CPU 28 | {DC21FFF9-730B-4E5C-A437-9BEA928F68E5}.MinimizeT|Any CPU.ActiveCfg = Release|Any CPU 29 | {DC21FFF9-730B-4E5C-A437-9BEA928F68E5}.MinimizeT|Any CPU.Build.0 = Release|Any CPU 30 | {DC21FFF9-730B-4E5C-A437-9BEA928F68E5}.MinimizeWidth|Any CPU.ActiveCfg = Release|Any CPU 31 | {DC21FFF9-730B-4E5C-A437-9BEA928F68E5}.MinimizeWidth|Any CPU.Build.0 = Release|Any CPU 32 | EndGlobalSection 33 | GlobalSection(SolutionProperties) = preSolution 34 | HideSolutionNode = FALSE 35 | EndGlobalSection 36 | GlobalSection(ExtensibilityGlobals) = postSolution 37 | SolutionGuid = {25D035F3-F750-4C78-9A34-4834D8B36D6B} 38 | EndGlobalSection 39 | EndGlobal 40 | -------------------------------------------------------------------------------- /ResourceEstimator/DepthCounterCSV.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Corporation. 2 | // Licensed under the MIT license. 3 | 4 | // Library that deals with making human-friendly the CSV tracer's output 5 | namespace Microsoft.Quantum.Crypto.ResourceEstimator.CommaSeparated 6 | { 7 | using FileHelpers; // csv parsing 8 | 9 | [DelimitedRecord("\t")] 10 | [IgnoreFirst(1)] 11 | public class DepthCounterCSV 12 | { 13 | public string Name 14 | { get; set; } 15 | 16 | public string Variant 17 | { get; set; } 18 | 19 | public string Caller 20 | { get; set; } 21 | 22 | public string CallerVariant 23 | { get; set; } 24 | 25 | public long Count 26 | { get; set; } 27 | 28 | [FieldConverter(typeof(QDecimalConverter))] 29 | public decimal DepthAverage 30 | { get; set; } 31 | 32 | [FieldConverter(typeof(QDecimalConverter))] 33 | public decimal DepthSecondMoment 34 | { get; set; } 35 | 36 | [FieldConverter(typeof(QDecimalConverter))] 37 | public decimal? DepthVariance 38 | { get; set; } 39 | 40 | public long DepthSum 41 | { get; set; } 42 | 43 | public long DepthMin 44 | { get; set; } 45 | 46 | public long DepthMax 47 | { get; set; } 48 | 49 | [FieldConverter(typeof(QDecimalConverter))] 50 | public decimal StartTimeDifferenceAverage 51 | { get; set; } 52 | 53 | [FieldConverter(typeof(QDecimalConverter))] 54 | public decimal StartTimeDifferenceSecondMoment 55 | { get; set; } 56 | 57 | [FieldConverter(typeof(QDecimalConverter))] // [FieldConverter(ConverterKind.Decimal, ".")] 58 | public decimal? StartTimeDifferenceVariance 59 | { get; set; } 60 | 61 | public long StartTimeDifferenceSum 62 | { get; set; } 63 | 64 | public long StartTimeDifferenceMin 65 | { get; set; } 66 | 67 | public long StartTimeDifferenceMax 68 | { get; set; } 69 | 70 | [FieldConverter(typeof(QDecimalConverter))] 71 | public decimal WidthAverage 72 | { get; set; } 73 | 74 | [FieldConverter(typeof(QDecimalConverter))] 75 | public decimal WidthSecondMoment 76 | { get; set; } 77 | 78 | [FieldConverter(typeof(QDecimalConverter))] 79 | public decimal? WidthVariance 80 | { get; set; } 81 | 82 | public long WidthSum 83 | { get; set; } 84 | 85 | public long WidthMin 86 | { get; set; } 87 | 88 | public long WidthMax 89 | { get; set; } 90 | } 91 | } -------------------------------------------------------------------------------- /SECURITY.md: -------------------------------------------------------------------------------- 1 | 2 | 3 | ## Security 4 | 5 | Microsoft takes the security of our software products and services seriously, which includes all source code repositories managed through our GitHub organizations, which include [Microsoft](https://github.com/Microsoft), [Azure](https://github.com/Azure), [DotNet](https://github.com/dotnet), [AspNet](https://github.com/aspnet), [Xamarin](https://github.com/xamarin), and [our GitHub organizations](https://opensource.microsoft.com/). 6 | 7 | If you believe you have found a security vulnerability in any Microsoft-owned repository that meets [Microsoft's definition of a security vulnerability](https://docs.microsoft.com/en-us/previous-versions/tn-archive/cc751383(v=technet.10)), please report it to us as described below. 8 | 9 | ## Reporting Security Issues 10 | 11 | **Please do not report security vulnerabilities through public GitHub issues.** 12 | 13 | Instead, please report them to the Microsoft Security Response Center (MSRC) at [https://msrc.microsoft.com/create-report](https://msrc.microsoft.com/create-report). 14 | 15 | If you prefer to submit without logging in, send email to [secure@microsoft.com](mailto:secure@microsoft.com). If possible, encrypt your message with our PGP key; please download it from the [Microsoft Security Response Center PGP Key page](https://www.microsoft.com/en-us/msrc/pgp-key-msrc). 16 | 17 | You should receive a response within 24 hours. If for some reason you do not, please follow up via email to ensure we received your original message. Additional information can be found at [microsoft.com/msrc](https://www.microsoft.com/msrc). 18 | 19 | Please include the requested information listed below (as much as you can provide) to help us better understand the nature and scope of the possible issue: 20 | 21 | * Type of issue (e.g. buffer overflow, SQL injection, cross-site scripting, etc.) 22 | * Full paths of source file(s) related to the manifestation of the issue 23 | * The location of the affected source code (tag/branch/commit or direct URL) 24 | * Any special configuration required to reproduce the issue 25 | * Step-by-step instructions to reproduce the issue 26 | * Proof-of-concept or exploit code (if possible) 27 | * Impact of the issue, including how an attacker might exploit the issue 28 | 29 | This information will help us triage your report more quickly. 30 | 31 | If you are reporting for a bug bounty, more complete reports can contribute to a higher bounty award. Please visit our [Microsoft Bug Bounty Program](https://microsoft.com/msrc/bounty) page for more details about our active programs. 32 | 33 | ## Preferred Languages 34 | 35 | We prefer all communications to be in English. 36 | 37 | ## Policy 38 | 39 | Microsoft follows the principle of [Coordinated Vulnerability Disclosure](https://www.microsoft.com/en-us/msrc/cvd). 40 | 41 | -------------------------------------------------------------------------------- /ResourceEstimator/WidthCounterCSV.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Corporation. 2 | // Licensed under the MIT license. 3 | 4 | // Library that deals with making human-friendly the CSV tracer's output 5 | namespace Microsoft.Quantum.Crypto.ResourceEstimator.CommaSeparated 6 | { 7 | using FileHelpers; // csv parsing 8 | 9 | [DelimitedRecord("\t")] 10 | [IgnoreFirst(1)] 11 | public class WidthCounterCSV 12 | { 13 | public string Name 14 | { get; set; } 15 | 16 | public string Variant 17 | { get; set; } 18 | 19 | public string Caller 20 | { get; set; } 21 | 22 | public string CallerVariant 23 | { get; set; } 24 | 25 | public long Count 26 | { get; set; } 27 | 28 | [FieldConverter(typeof(QDecimalConverter))] 29 | public decimal InputWidthAverage 30 | { get; set; } 31 | 32 | [FieldConverter(typeof(QDecimalConverter))] 33 | public decimal InputWidthSecondMoment 34 | { get; set; } 35 | 36 | [FieldConverter(typeof(QDecimalConverter))] 37 | public decimal InputWidthVariance 38 | { get; set; } 39 | 40 | public long InputWidthSum 41 | { get; set; } 42 | 43 | public long InputWidthMin 44 | { get; set; } 45 | 46 | public long InputWidthMax 47 | { get; set; } 48 | 49 | [FieldConverter(typeof(QDecimalConverter))] 50 | public decimal ExtraWidthAverage 51 | { get; set; } 52 | 53 | [FieldConverter(typeof(QDecimalConverter))] 54 | public decimal ExtraWidthSecondMoment 55 | { get; set; } 56 | 57 | [FieldConverter(typeof(QDecimalConverter))] 58 | public decimal ExtraWidthVariance 59 | { get; set; } 60 | 61 | public long ExtraWidthSum 62 | { get; set; } 63 | 64 | public long ExtraWidthMin 65 | { get; set; } 66 | 67 | public long ExtraWidthMax 68 | { get; set; } 69 | 70 | [FieldConverter(typeof(QDecimalConverter))] 71 | public decimal ReturnWidthAverage 72 | { get; set; } 73 | 74 | [FieldConverter(typeof(QDecimalConverter))] 75 | public decimal ReturnWidthSecondMoment 76 | { get; set; } 77 | 78 | [FieldConverter(typeof(QDecimalConverter))] 79 | public decimal ReturnWidthVariance 80 | { get; set; } 81 | 82 | public long ReturnWidthSum 83 | { get; set; } 84 | 85 | public long ReturnWidthMin 86 | { get; set; } 87 | 88 | public long ReturnWidthMax 89 | { get; set; } 90 | 91 | [FieldConverter(typeof(QDecimalConverter))] 92 | public decimal BorrowedWidthAverage 93 | { get; set; } 94 | 95 | [FieldConverter(typeof(QDecimalConverter))] 96 | public decimal BorrowedWidthSecondMoment 97 | { get; set; } 98 | 99 | [FieldConverter(typeof(QDecimalConverter))] 100 | public decimal BorrowedWidthVariance 101 | { get; set; } 102 | 103 | public long BorrowedWidthSum 104 | { get; set; } 105 | 106 | public long BorrowedWidthMin 107 | { get; set; } 108 | 109 | public long BorrowedWidthMax 110 | { get; set; } 111 | } 112 | } -------------------------------------------------------------------------------- /ResourceEstimator/OperationCounterCSV.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Corporation. 2 | // Licensed under the MIT license. 3 | 4 | // Library that deals with making human-friendly the CSV tracer's output 5 | namespace Microsoft.Quantum.Crypto.ResourceEstimator.CommaSeparated 6 | { 7 | using FileHelpers; // csv parsing 8 | 9 | [DelimitedRecord("\t")] 10 | [IgnoreFirst(1)] 11 | public class OperationCounterCSV 12 | { 13 | public string Name 14 | { get; set; } 15 | 16 | public string Variant 17 | { get; set; } 18 | 19 | public string Caller 20 | { get; set; } 21 | 22 | public string CallerVariant 23 | { get; set; } 24 | 25 | public long Count 26 | { get; set; } 27 | 28 | [FieldConverter(typeof(QDecimalConverter))] 29 | public decimal CNOTAverage 30 | { get; set; } 31 | 32 | [FieldConverter(typeof(QDecimalConverter))] 33 | public decimal CNOTSecondMoment 34 | { get; set; } 35 | 36 | [FieldConverter(typeof(QDecimalConverter))] 37 | public decimal CNOTVariance 38 | { get; set; } 39 | 40 | public long CNOTSum 41 | { get; set; } 42 | 43 | public long CNOTMin 44 | { get; set; } 45 | 46 | public long CNOTMax 47 | { get; set; } 48 | 49 | [FieldConverter(typeof(QDecimalConverter))] 50 | public decimal QubitCliffordAverage 51 | { get; set; } 52 | 53 | [FieldConverter(typeof(QDecimalConverter))] 54 | public decimal QubitCliffordSecondMoment 55 | { get; set; } 56 | 57 | [FieldConverter(typeof(QDecimalConverter))] 58 | public decimal QubitCliffordVariance 59 | { get; set; } 60 | 61 | public long QubitCliffordSum 62 | { get; set; } 63 | 64 | public long QubitCliffordMin 65 | { get; set; } 66 | 67 | public long QubitCliffordMax 68 | { get; set; } 69 | 70 | [FieldConverter(typeof(QDecimalConverter))] 71 | public decimal RAverage 72 | { get; set; } 73 | 74 | [FieldConverter(typeof(QDecimalConverter))] 75 | public decimal RSecondMoment 76 | { get; set; } 77 | 78 | [FieldConverter(typeof(QDecimalConverter))] 79 | public decimal RVariance 80 | { get; set; } 81 | 82 | public long RSum 83 | { get; set; } 84 | 85 | public long RMin 86 | { get; set; } 87 | 88 | public long RMax 89 | { get; set; } 90 | 91 | [FieldConverter(typeof(QDecimalConverter))] 92 | public decimal MeasureAverage 93 | { get; set; } 94 | 95 | [FieldConverter(typeof(QDecimalConverter))] 96 | public decimal MeasureSecondMoment 97 | { get; set; } 98 | 99 | [FieldConverter(typeof(QDecimalConverter))] 100 | public decimal MeasureVariance 101 | { get; set; } 102 | 103 | public long MeasureSum 104 | { get; set; } 105 | 106 | public long MeasureMin 107 | { get; set; } 108 | 109 | public long MeasureMax 110 | { get; set; } 111 | 112 | [FieldConverter(typeof(QDecimalConverter))] 113 | public decimal TAverage 114 | { get; set; } 115 | 116 | [FieldConverter(typeof(QDecimalConverter))] 117 | public decimal TSecondMoment 118 | { get; set; } 119 | 120 | [FieldConverter(typeof(QDecimalConverter))] 121 | public decimal TVariance 122 | { get; set; } 123 | 124 | public long TSum 125 | { get; set; } 126 | 127 | public long TMin 128 | { get; set; } 129 | 130 | public long TMax 131 | { get; set; } 132 | } 133 | } -------------------------------------------------------------------------------- /MicrosoftQuantumCrypto/Tests/ReadMe.md: -------------------------------------------------------------------------------- 1 | # Summary 2 | 3 | All of the tests follow the same basic format. There are three main functions: 4 | 5 | # [OperationName]TestHelper 6 | 7 | This is the function which actually calls the operation being tested. It does the following 8 | steps: 9 | 10 | 1. Allocate qubits and format variables to correctly point to the qubit registers. 11 | 2. Write the classical test case to the qubit registers. 12 | 3. Perform the operation being tested. 13 | 4. Classicaly compute the expected result. 14 | 5. Measure the qubit registers. Here we assume that measuring a register 15 | resets the state of the register to 0. 16 | 6. Compare the measured results to the expected results, and throw an error if they do not match. This also 17 | includes comparing the measured inputs to the original inputs, which should remain unchanged. 18 | 7. Initialize one control qubit to 0. 19 | 8. Run steps 2-6, but control the operation. Here, there should be no change to inputs or outputs. 20 | 9. Flip the control qubit to 1. 21 | 10. Run steps 2-6, but contol the operation. 22 | 11. Initialize another control qubit, and set both to 0. 23 | 12. Run steps 8-10 with 2 control qubits instead of 1. 24 | 25 | ## Inputs 26 | 27 | ### Operation : ('T => Unit is Ctl) 28 | 29 | This is the operation being tested. It takes some inputs (here 'T) and performs quantum 30 | operations on them. 31 | 32 | ### Classical inputs 33 | 34 | Here the function takes some classical inputs (such as `Int`, `BigInt`, etc.). These 35 | are XORed into the qubit register for the test. 36 | 37 | ### nQubits 38 | 39 | The number of qubits to parameterize the operation. For example: An addition with 40 | `nQubits=10` will add 10-bit integers, and thus need 20 qubits in total (21 if it uses 41 | a carry). 42 | 43 | ## Open Operations 44 | 45 | Some operations are "open", meaning they do not clean up their ancilla. An `TestHelper` 46 | for an open function also needs a `nAncilla` argument, telling it how many ancilla to 47 | allocate. The operation also needs an adjoint. 48 | 49 | The steps for the open version are slightly different: 50 | 51 | 1. Allocate qubits and format variables to correctly point to the qubit registers. 52 | 2. Write the classical test case to the qubit registers. 53 | 3. Perform the operation being tested. 54 | 4. Classicaly compute the expected result. 55 | 5. Measure the input and output qubit registers, but not the ancilla 56 | 6. Compare the measured results to the expected results, and throw an error if they do not match. This also 57 | includes comparing the measured inputs to the original inputs, which should remain unchanged. 58 | 7. Rewrite the measurement results to the corresponding registers. 59 | 8. Perform the *adjoint* of the operation being tested. 60 | 9. Measure all registers, and check that (a) the inputs are in their original state and (b) the output 61 | and ancilla are 0. 62 | 10. Initialize one control qubit to 0. 63 | 11. Run steps 2-9, but control the operation. Here, there should be no change to inputs or outputs. 64 | 12. Flip the control qubit to 1. 65 | 13. Run steps 2-9, but contol the operation. 66 | 11. Initialize another control qubit, and set both to 0. 67 | 12. Run steps 10-13 with 2 control qubits instead of 1. 68 | 69 | # [OperationName]Test 70 | 71 | Takes a specific test case and calls `[OperationName]TestHelper` directly 72 | with this test case. 73 | 74 | # [OperationName]ExhaustiveTestHelper 75 | 76 | Takes an operation and a number of qubits, and runs `[Operationname]TestHelper` 77 | with that number of qubits for every possible input. 78 | 79 | For certain operations, this operation does a lot of preprocessing to ensure the inputs 80 | are well-formatted. For example, modular multiplication presently only functions 81 | correctly with odd moduli. 82 | 83 | # [OperationName]ExhaustiveTest 84 | 85 | Contains a hard-coded number of qubits (and ancilla, for open operations) and calls 86 | `[OperationName]ExhaustiveTestHelper` with those qubits, as well as a specific function. 87 | 88 | If there are multiple variants of the same operation (for example: different RipplyCarry 89 | adders, or a carry lookahead adder), they will all use the same `TestHelper` but they 90 | will have distinct `[OperationName]ExhaustiveTest` operations. 91 | 92 | # [OperationName][TestType]Reversible 93 | 94 | Operations like this are exactly the same as the other versions, but they are expected to 95 | use numbers of qubits that are beyond what the fully quantum simulator can reasonably simulate. 96 | 97 | # [OperationName]RandomTestHelper 98 | 99 | For certain operations and certain parameters, an exhaustive test is not feasible. Thus, we use 100 | a random test. These operations will select random parameters, and pass them to `[OperationName]TestHelper`. 101 | 102 | These operations also require a `nTests` input, which tells it how many tests to run. 103 | 104 | 105 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # QuantumEllipticCurves 2 | 3 | The QuantumEllipticCurves project contains Q# implementations of quantum circuits for elliptic curve arithmetic and isogeny computation. 4 | 5 | ## Shor's algorithm for computing elliptic curve discrete logarithms 6 | 7 | The code provides all operations to obtain resource estimates for quantum circuits to compute elliptic curve discrete logarithms using Shor's algorithm as described in [1] and [2]. Building on integer and modular arithmetic such as modular multiplication, squaring and inversion, the project contains circuits for adding elliptic curve points on Weierstraß curves over prime fields. 8 | 9 | ## Computing supersingular isogenies 10 | 11 | Operations for field arithmetic of quadratic extension fields, Montgomery curve arithmetic over such fields, and circuits for 2-isogeny computations are used to construct circuits for isogeny computations on supersingular elliptic curves as used in SIDH [3] and SIKE [4]. 12 | 13 | The project depends on version 0.12.20082513 of the [Microsoft Quantum Development Kit](https://www.microsoft.com/en-us/quantum/development-kit). It can be built using [Visual Studio](https://visualstudio.microsoft.com/) or the [.NET CLI](https://docs.microsoft.com/en-us/dotnet/core/tools/). 14 | 15 | The code was developed by [Microsoft Research](http://research.microsoft.com/) for experimentation purposes. 16 | 17 | ## Contents 18 | 19 | The Quantum Elliptic Curves Visual Studio solution contains the following projects: 20 | 21 | ### MicrosoftQuantumCrypto 22 | 23 | The `MicrosoftQuantumCrypto` project implements the crypographic primitives in Q# and contains unit tests that can be run using Visual Studio's Test Explorer or the .NET CLI (as demonstrated below). 24 | 25 | To build the project (with `` set to `MinimizeDepth`, `MinimizeT`, or `MinimizeWidth`): 26 | 27 | `dotnet build -c .\MicrosoftQuantumCrypto\MicrosoftQuantumCrypto.csproj` 28 | 29 | To run the unit tests: 30 | 31 | `dotnet test .\MicrosoftQuantumCrypto\MicrosoftQuantumCrypto.csproj` 32 | 33 | ### ResourceEstimator 34 | 35 | The `ResourceEstimator` project creates quantum resource estimates for the `MicrosoftQuantumCrypto` library, using the cost metric with which the library was compiled. 36 | 37 | To build the project (with `` set to `Debug` or `Release`): 38 | 39 | `dotnet build -c .\ResourceEstimator\ResourceEstimator.csproj` 40 | 41 | To run the estimator: 42 | 43 | `.\ResourceEstimator\bin\\netcoreapp3.1\ResourceEstimator.exe` 44 | 45 | For more details, see `ResourceEstimator\README.md`. 46 | 47 | ### Issue with estimating resources 48 | 49 | A problem with the ResourcesEstimator functionality in Q# has been found and reported in [Issue #192](https://github.com/microsoft/qsharp-runtime/issues/192). Currently, results may report independent lower bounds on depth and width that may not be simultaneously realizable in a quantum circuit. The Q# team has stated that they are working to resolve this issue. 50 | 51 | ## Contributors 52 | 53 | - Samuel Jaques (main contributor) 54 | - Michael Naehrig 55 | - Christian Paquin 56 | - Fernando Virdia 57 | 58 | ## References 59 | 60 | [1] Thomas Häner, Samuel Jaques, Michael Naehrig, Martin Roetteler, and Mathias Soeken: "Improved quantum circuits for elliptic curve discrete logarithms". 61 | Post-Quantum Cryptography – PQCrypto 2020, Lecture Notes in Computer Science 12100, Springer-Verlag (2020), pp 425–444. 62 | [`https://eprint.iacr.org/2020/077`](https://eprint.iacr.org/2020/077). 63 | 64 | [2] Martin Roetteler, Michael Naehrig, Krysta M. Svore, and Kristin Lauter: "Quantum Resource Estimates for Computing Elliptic Curve Discrete Logarithms". 65 | Advances in Cryptology – ASIACRYPT 2017, Lecture Notes in Computer Science 10625, Springer-Verlag (2017), pp 241–272. 66 | [`https://eprint.iacr.org/2017/598`](https://eprint.iacr.org/2017/598). 67 | 68 | [3] David Jao and Luca DeFeo, "Towards quantum-resistant cryptosystems from supersingular elliptic curve isogenies". Post-quantum cryptography – PQCrypto 2011, Lecture Notes in Computer Science 7071, pp. 19-34, 2011. 69 | [`https://eprint.iacr.org/2011/506`](https://eprint.iacr.org/2011/506). 70 | 71 | [4] Reza Azarderakhsh, Matthew Campagna, Craig Costello, Luca De Feo, Basil Hess, Amir Jalali, David Jao, Brian Koziel, Brian LaMacchia, Patrick Longa, Michael Naehrig, Geovandro Pereira, Joost Renes, Vladimir Soukharev, and David Urbanik, "Supersingular Isogeny Key Encapsulation". Submission to the NIST Post-Quantum Standardization project, 2017. 72 | ['https://sike.org'](https://sike.org) 73 | 74 | ## License 75 | 76 | The QuantumEllipticCurves project is licensed under the MIT License; see [`License`](LICENSE) for details. 77 | 78 | ## Contributing 79 | 80 | This project welcomes contributions and suggestions. Most contributions require you to agree to a 81 | Contributor License Agreement (CLA) declaring that you have the right to, and actually do, grant us 82 | the rights to use your contribution. For details, visit https://cla.opensource.microsoft.com. 83 | 84 | When you submit a pull request, a CLA bot will automatically determine whether you need to provide 85 | a CLA and decorate the PR appropriately (e.g., status check, comment). Simply follow the instructions 86 | provided by the bot. You will only need to do this once across all repos using our CLA. 87 | 88 | This project has adopted the [Microsoft Open Source Code of Conduct](https://opensource.microsoft.com/codeofconduct/). 89 | For more information see the [Code of Conduct FAQ](https://opensource.microsoft.com/codeofconduct/faq/) or 90 | contact [opencode@microsoft.com](mailto:opencode@microsoft.com) with any additional questions or comments. 91 | -------------------------------------------------------------------------------- /ResourceEstimator/README.md: -------------------------------------------------------------------------------- 1 | # Resource Estimator 2 | 3 | ## Running Estimates 4 | This code will run resource estimates at numerous parameter sizes for all basic modular arithmetic operations, as well as elliptic curve operations for Shor's algorithm. 5 | 6 | To compare to the results from [Häner et al. 2020](https://eprint.iacr.org/2020/077), the outputs from the elliptic curve operations must be adjusted to account for optimal window sizes. The python script `shor_estimate.py` will do this. It will load the costs from `EllipticCurveEstimates/Low{Depth,T,Width}/Fixed-modulus-signed.csv` and adjust them based on different window sizes, trying all window sizes until it finds the lowest cost. It then writes the resource estimates from this optimal window size to `shor_low_{depth,T,width}_fixed.csv`. It also does the same for smaller window sizes, using hard-coded asymptotic formulas. 7 | 8 | ## Basic Logic 9 | The goal is to run different operations of the form `Int => Unit()`, where the integer parameter represents some parameter of the function. For example, one operation runs an addition circuit, adding numbers whose bitsize equals the parameter given. 10 | 11 | ### ResourceEstimateWrappers.qs 12 | All of these quantum operations are in the "ResourceEstimateWrappers.qs" file. They all have the same format: They allocate qubits, then format them as necessary, then they run a single quantum operation. They may also select random or specified classical inputs as needed. After they are finished, they reset the qubits they borrowed with an operation that asserts an irrelevant measurement property. 13 | 14 | Almost all of them use the `ControlledOp` wrapper. This takes a boolean `isControlled` argument, as well as an operation. If `isControlled` is true, this allocates one more qubit and uses it to control the operation. If `isControlled` is false, it simply runs the operation without a control qubit. 15 | 16 | ### Calling a estimator test from C\# 17 | We want to run many resource estimates with different parameters and on different functions. Thus, we want a single wrapper function that can take a quantum operation as an argument, and will perform the test. Quantum operations are an odd type in C\#. The two types defined in the `Driver` class, `RunQop` and `RunParameterizedQop`, act sort of as type wrappers for quantum operations. 18 | 19 | Thus, given a quantum operation `QuantumOp` of the form `(Int, Bool) => Unit()`, then `QuantumOp.Run` can be created as an object of type `RunQop`. Given an object `runner` of type `RunQop`, it can be called in C\# as `runner(estimator, intValue, boolValue).Result)`, which runs the quantum operation and returns a result type. 20 | 21 | The method `SingleResourceTest` does a single test. It takes a quantum operation, formatted as above into a `RunQop` object, as well as several parameters of the test: 22 | 23 | * `locker` is a lock, used to prevent threading issues when writing outputs to the same file 24 | * `n` is an integer parameter which is passed to the quantum operation 25 | * `isControlled` is a boolean which is also passed to the quantum operation 26 | * `filename` is the filename of the file, assumed to be .csv, into which a single line of results will be appended. 27 | * `full_depth` tells it whether to count all gates when estimating depth, or just T gates. 28 | 29 | The `RunQop` object needs a simulator as an argument, which is what governs how it is run. `SingleResourceTest` creates a new simulator object. The simulator object stores all the resource estimates within itself, and thus each different test needs a new simulator. The simulator can output its results in .csv format, and the `DisplayCSV.csv` method formats this into a single, comma-separated string. Finally, `SingleResourceTest` will take this string and append it to the file, after waiting to get a lock on the file. 30 | 31 | ### Running many estimator tests 32 | The rest of the code in `Driver.cs` just wraps `SingleResourceTest` so that it can be run with many different parameters. 33 | 34 | `BasicResourceTest` takes the same arguments as `SingleResourceTest`, except it takes an array of parameters. It creates a .csv file for the results, formats a header row, then calls `SingleResourceTest` with the different parameters. The intended use is that each call to `BasicResourceTest` will write to a different file. 35 | 36 | The various methods that begin with `Estimate...` then call `BasicResourceTest` with different quantum operations. The quantum operations are given both as a type parameter and as the first argument. The `Estimate...` methods create a directory given to them as an argument, and then save estimates for related quantum operations (e.g., basic addition) to that directory. These methods also call each quantum operations with both values of `isControlled` and `full_depth`. 37 | 38 | Finally, `Main` will allocate different arrays of parameter sizes, and call the various `Estimate...` methods. 39 | 40 | ### Changing optimization strategies 41 | There are currently three different optimization strategies for the quantum circuits: low depth, low width, and low T count. These are set as a compile configuration in the `MicrosoftQuantumCrypto` project. That is, we cannot change the circuit design from within `Driver.cs`. Instead, we must recompile the `MicrosoftQuantumCrypto` library for each desired cost metric. The estimates will be saved in a different directory for each cost metric. 42 | 43 | ### Windowing Estimation Tests 44 | Running tests for windowing is slightly different, because the quantum operation takes two integer parameters: The original integer parameter (e.g., number of qubits), and a second parameter representing the window sizes. Besides that, everything runs in basically the same way, except we must loop over possible values of the window size. 45 | 46 | The analogous types and methods are: 47 | 48 | * `RunQop` => `RunParameterizedQop` 49 | * `SingleResourceTest` => `SingleParameterizedResourceTest` 50 | * `BasicResourceTest` => `ParameterizedResourceTest` 51 | * `Estimate....` => `Estimate...WindowSizes` 52 | 53 | The only other main difference is that we attempt to be efficient in estimating window sizes. We assume that the cost relative to the window size is roughly convex: if we increase the window size from 0, the cost will decrease to a minimum, then strictly increase from that point. Thus, once the cost begins to increase, we would like to stop the tests. Currently this works fine for a single-threaded approach, but not for a multi-threaded approach. 54 | 55 | -------------------------------------------------------------------------------- /MicrosoftQuantumCrypto/Tests/DebugHelpers.qs: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Corporation. 2 | // Licensed under the MIT license. 3 | 4 | namespace Microsoft.Quantum.ModularArithmetic.DebugHelpers 5 | { 6 | open Microsoft.Quantum.Intrinsic; 7 | open Microsoft.Quantum.Crypto.Basics; 8 | open Microsoft.Quantum.Crypto.Arithmetic; 9 | open Microsoft.Quantum.Crypto.ModularArithmetic; 10 | open Microsoft.Quantum.Crypto.EllipticCurves; 11 | open Microsoft.Quantum.Crypto.Fp2Arithmetic; 12 | open Microsoft.Quantum.Crypto.Isogenies; 13 | open Microsoft.Quantum.Arithmetic; 14 | open Microsoft.Quantum.Canon; 15 | open Microsoft.Quantum.Convert; 16 | open Microsoft.Quantum.Math; 17 | open Microsoft.Quantum.Diagnostics; 18 | 19 | 20 | 21 | operation DumpLittleEndian(xs : LittleEndian, message : String) : Unit{ 22 | body(...){ 23 | let result = MeasureBigInteger(xs); 24 | Message($"{message}{result}"); 25 | ApplyXorInPlaceL(result,xs); 26 | } 27 | adjoint(...){ 28 | DumpLittleEndian(xs,message); 29 | } 30 | controlled(controls,...){ 31 | DumpLittleEndian(xs, message); 32 | } 33 | controlled adjoint(controls,...){ 34 | DumpLittleEndian(xs, message); 35 | } 36 | } 37 | 38 | 39 | 40 | operation BitDump(qubit:Qubit,message:String):Unit{ 41 | body(...){ 42 | Message(message + $"{M(qubit)}"); 43 | } 44 | adjoint(...){ 45 | Message(message + $"{M(qubit)}"); 46 | } 47 | controlled(controls,...){ 48 | Message(message + $"{M(qubit)}"); 49 | } 50 | controlled adjoint(controls,...){ 51 | Message(message + $"{M(qubit)}"); 52 | } 53 | } 54 | 55 | 56 | operation DumpECPointMontgomery(point : ECPointMontgomeryForm, message : String) : Unit { 57 | body (...){ 58 | let x = MeasureMontgomeryInteger(point::xs); 59 | let y = MeasureMontgomeryInteger(point::ys); 60 | Message(message + $"({x}, {y})"); 61 | EncodeBigIntInMontgomeryForm(x, point::xs); 62 | EncodeBigIntInMontgomeryForm(y, point::ys); 63 | } 64 | controlled (controls, ...){ 65 | DumpECPointMontgomery(point, message); 66 | } 67 | adjoint controlled (controls, ...){ 68 | DumpECPointMontgomery(point, message); 69 | } 70 | adjoint (...){ 71 | DumpECPointMontgomery(point, message); 72 | } 73 | } 74 | 75 | 76 | operation DumpFp2(element : Fp2MontModInt, message : String) :Unit { 77 | body (...){ 78 | let elementC = MeasureFp2MontModInt(element); 79 | Message(message + $"{elementC::real}+{elementC::imag}i"); 80 | EncodeFp2MontModInt(elementC, element); 81 | } 82 | controlled (controls, ... ){ 83 | DumpFp2(element, message); 84 | } 85 | controlled adjoint (controls, ...){ 86 | DumpFp2(element, message); 87 | } 88 | adjoint (...){ 89 | DumpFp2(element, message); 90 | } 91 | } 92 | 93 | 94 | function ECPointMontgomeryXZClassicalAsString(point : ECPointMontgomeryXZClassical) : String { 95 | return $"({point::x::real}+{point::x::imag}i,{point::z::real}+{point::z::imag}i)"; 96 | } 97 | 98 | 99 | function PrintClassicalECPointArray(points: ECPointMontgomeryXZClassical[] ) : Unit { 100 | for idx in 0..Length(points) - 1 { 101 | Message($"Point {idx}:" + ECPointMontgomeryXZClassicalAsString(points[idx])); 102 | } 103 | } 104 | 105 | 106 | operation DumpECPointArray(points : ECPointMontgomeryXZ[], message : String) : Unit { 107 | body (...){ 108 | Message(message); 109 | for idx in 0.. Length(points) - 1 { 110 | DumpECPoint(points[idx], $"Point {idx}:"); 111 | } 112 | } 113 | controlled (controls, ...){ 114 | DumpECPointArray(points, message); 115 | } 116 | adjoint controlled (controls, ...){ 117 | DumpECPointArray(points, message); 118 | } 119 | adjoint (...){ 120 | DumpECPointArray(points, message); 121 | } 122 | } 123 | 124 | 125 | function OutputECPoint(point:ECPointClassical):Unit { 126 | Message($"Point: ({point::x},{point::y},{point::z})"); 127 | } 128 | 129 | 130 | operation DumpECPoint(point : ECPointMontgomeryXZ, message : String) : Unit { 131 | body (...) { 132 | let pointC = MeasureECPointMontgomeryXZ(point); 133 | Message(message + ECPointMontgomeryXZClassicalAsString(pointC)); 134 | EncodeECPointMontgomeryXZ(pointC, point); 135 | } 136 | controlled (controls, ... ){ 137 | DumpECPoint(point, message); 138 | } 139 | controlled adjoint (controls, ... ){ 140 | DumpECPoint(point, message); 141 | } 142 | adjoint (... ){ 143 | DumpECPoint(point, message); 144 | } 145 | } 146 | 147 | 148 | operation DumpECCurve(curve : ECCoordsMontgomeryFormAPlusC, message : String) : Unit { 149 | body (...) { 150 | let curveC = MeasureECCoordsMontgomeryFormAPlusC(curve); 151 | Message(message + $"({curveC::a24Plus::real}+{curveC::a24Plus::imag}i,{curveC::c24::real}+{curveC::c24::imag}i)"); 152 | EncodeECCoordsMontgomeryFormAPlusC(curveC, curve); 153 | } 154 | controlled (controls, ... ){ 155 | DumpECCurve(curve, message); 156 | } 157 | controlled adjoint (controls, ... ){ 158 | DumpECCurve(curve, message); 159 | } 160 | adjoint (... ){ 161 | DumpECCurve(curve, message); 162 | } 163 | } 164 | 165 | 166 | operation DumpJInvariant(curve : ECCoordsMontgomeryFormAPlusC, message : String) : Unit { 167 | body (...) { 168 | let curveC = MeasureECCoordsMontgomeryFormAPlusC(curve); 169 | let j = JInvariantClassical(MontgomeryCurveClassicalAsACFromAPlusC(curveC)); 170 | Message(message + $"({j::real}+{j::imag}i)"); 171 | EncodeECCoordsMontgomeryFormAPlusC(curveC, curve); 172 | } 173 | controlled (controls, ... ){ 174 | DumpJInvariant(curve, message); 175 | } 176 | controlled adjoint (controls, ... ){ 177 | DumpJInvariant(curve, message); 178 | } 179 | adjoint (... ){ 180 | DumpJInvariant(curve, message); 181 | } 182 | } 183 | 184 | 185 | operation DumpQubits(qubits:Qubit[],message:String):Unit { 186 | body(...){ 187 | mutable newmessage = message; 188 | for idx in 0..Length(qubits)-1 { 189 | if (ResultAsBool(M(qubits[idx]))){ 190 | set newmessage = newmessage + "1"; 191 | } else { 192 | set newmessage = newmessage+"0"; 193 | } 194 | } 195 | Message(newmessage); 196 | } 197 | adjoint(...){ 198 | DumpQubits(qubits, message); 199 | } 200 | controlled(controls,...){ 201 | DumpQubits(qubits, message); 202 | } 203 | controlled adjoint(controls,...){ 204 | DumpQubits(qubits, message); 205 | } 206 | } 207 | 208 | } -------------------------------------------------------------------------------- /MicrosoftQuantumCrypto/Counters.qs: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Corporation. 2 | // Licensed under the MIT license. 3 | 4 | namespace Microsoft.Quantum.Crypto.NC.SpecialCounters { 5 | open Microsoft.Quantum.Intrinsic; 6 | open Microsoft.Quantum.Canon; 7 | open Microsoft.Quantum.Arithmetic; 8 | open Microsoft.Quantum.Diagnostics; 9 | open Microsoft.Quantum.Crypto.Basics; 10 | 11 | 12 | //////////////////////////////////////////////////////////////////////////////////////////// 13 | /////// //////// 14 | /////// SPECIAL COUNTERS //////// 15 | /////// //////// 16 | //////////////////////////////////////////////////////////////////////////////////////////// 17 | 18 | 19 | /// # Summary 20 | /// Returns a Counter type that references an input 21 | /// array of qubits, and includes operations 22 | /// that use shift registers for incrementing 23 | /// 24 | /// # Inputs 25 | /// ## counter 26 | /// Qubit register that will contain the counter. 27 | /// 28 | /// Output 29 | /// ## Counter 30 | /// Tuple referencing the qubits in `counter`, 31 | /// as well as operations for counting based on 32 | /// shift-registers 33 | function QubitsAsSpecialCounter (register : Qubit[]) : Counter { 34 | let nQubits = Length(register); 35 | let prepare = ConcatenateOperations(PrepareSpecialCounter, NoOp, register, _); 36 | let incrementInternal = ConcatenateOperations(IncrementSpecialCounter, NoOp, register, _); 37 | let test = TestSpecialCounter(register, _); 38 | let primitivepoly=_PrimitiveGF2Polynomial(nQubits); 39 | return Counter(register, 2^nQubits, prepare, incrementInternal, test); 40 | } 41 | 42 | 43 | /// # Summary 44 | /// Sets a SpecialCounter to its "zero" state. 45 | /// 46 | /// # Inputs 47 | /// ## counter 48 | /// A qubit register assumed to be in an all-zero state, 49 | /// assumed to be referenced by a Counter type. 50 | operation PrepareSpecialCounter(counter : Qubit[]) : Unit { 51 | body(...){ 52 | ApplyToEachWrapperCA(X, counter); 53 | } 54 | adjoint controlled auto; 55 | } 56 | 57 | /// # Summary 58 | /// Tests if a special counter is in its non-zero state; if it is, 59 | /// it flips the target qubit 60 | /// 61 | /// # Inputs 62 | /// ## counter 63 | /// Qubit register to test 64 | /// ## target 65 | /// A qubit whose state will be flipped (an X gate) if 66 | /// `counter` is not in its zero state. 67 | operation TestSpecialCounter(counter : Qubit[], target : Qubit) : Unit { 68 | body (...) { 69 | (Controlled TestSpecialCounter)([], (counter, target)); 70 | } 71 | controlled(controls, ...){ 72 | (Controlled CheckIfAllOnes)(controls, (counter, target)); 73 | (Controlled X)(controls, (target)); 74 | } 75 | adjoint controlled auto; 76 | } 77 | 78 | 79 | /// # Summary 80 | /// Returns an irreducible polynomial over GF2 of the specified degree. 81 | /// Used for special counters. 82 | /// 83 | /// # Inputs 84 | /// ## degree 85 | /// The degree of the polynomial to be returned 86 | /// 87 | /// # Outputs 88 | /// ## Int[] 89 | /// An array of coefficients in the polynomial which are non-zero, 90 | /// where 0 is the constant term, 1 is x, 2 is x^2, etc. 91 | /// The constant term and largest term are assumed to be 1 92 | /// and thus omitted 93 | function _PrimitiveGF2Polynomial(degree : Int) : Int[]{ 94 | let PRIMITIVE_TABLE_GF2 = [ 95 | [ 1 ], [ 1 ], [ 1 ], [ 2 ], [ 1 ], [ 1 ], [ 2, 3, 4 ], [ 4 ], [ 3 ], [ 2 ], [ 1, 3, 5, 6, 7 ], [ 1, 3, 4 ], [ 3, 5, 7 ], [ 1 ], [ 2, 3, 5 ], [ 3 ], [ 1, 10, 12 ], 96 | [ 1, 2, 5 ], [ 3 ], [ 2 ], [ 1 ], [ 5 ], [ 1, 3, 4 ], [ 3 ], [ 1, 4, 6, 7, 8, 10, 14 ], [ 1, 2, 5 ], [ 2, 5, 6, 7, 13 ], [ 2 ], [ 1, 2, 3, 5, 7, 11, 13, 16, 17 ], 97 | [ 3 ], [ 3, 4, 7, 9, 15 ], [ 3, 6, 8, 10, 11, 12, 13 ], [ 1, 2, 4, 5, 6, 7, 8, 11, 12, 15, 16 ], [ 2 ], [ 1, 5, 6, 8, 13, 14, 17, 19, 20, 22, 23 ], [ 1, 4, 6 ], 98 | [ 1, 5, 6 ], [ 4 ], [ 3, 4, 5 ], [ 3 ], [ 1, 2, 5, 6, 9, 11, 12, 18, 20, 24, 25, 26, 30 ], [ 3, 4, 6 ], [ 1, 3, 4, 16, 17, 19, 24 ], [ 1, 3, 4 ], [ 14, 17, 20, 21, 23 ], 99 | [ 5 ], [ 3, 7, 8, 10, 11, 12, 17, 23, 25 ], [ 9 ], [ 2, 3, 4 ], [ 1, 3, 6 ], [ 3 ], [ 1, 2, 6 ], [ 1, 2, 4, 7, 13, 15, 16, 17, 18, 21, 25, 27, 29, 30, 31, 32, 34 ], 100 | [ 4, 7, 9, 10, 11 ], [ 2, 4, 7 ], [ 1, 2, 3, 4, 5, 6, 8, 10, 11, 13, 16, 19, 21 ], [ 19 ], [ 2, 4, 7 ], [ 1 ], [ 1, 2, 5 ], 101 | [ 1, 6, 12, 13, 14, 16, 17, 18, 19, 20, 21, 24, 25, 26, 27, 28, 29, 30, 32 ], [ 1 ], [ 1, 3, 4 ], [ 18 ], [ 2, 4, 5, 6, 7, 11, 13, 16, 19, 21, 22, 23, 24, 27, 29, 30, 31, 32, 33, 34, 38, 40, 42, 45, 46 ], 102 | [ 1, 2, 5 ], [ 9 ], [ 2, 5, 6 ], [ 1, 3, 5 ], [ 6 ], [ 3, 9, 10 ], [ 25 ], [ 3, 8, 11, 12, 13, 16, 17, 21, 24, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37 ], [ 1, 3, 6 ], 103 | [ 1, 2, 5, 14, 15, 19, 20, 23, 24, 25, 27, 29, 31, 33, 34, 35, 36, 37, 38 ], [ 2, 5, 6 ], [ 1, 4, 6, 7, 8, 9, 11, 13, 14, 15, 17, 18, 19, 21, 22, 24, 25, 27, 28, 32, 33, 34, 35, 37, 39, 42, 43, 44, 46 ], 104 | [ 9 ], [ 2, 4, 9 ], [ 4 ], [ 1, 2, 3, 4, 6, 7, 9, 11, 12, 13, 15, 16, 17, 21, 22, 24, 28, 32, 33, 34, 35, 36, 37 ], [ 2, 4, 7 ], [ 2, 4, 5, 7, 8, 9, 11, 12, 13, 16, 17, 20, 21, 24, 25, 26, 28, 31, 32, 37, 39, 41, 46, 47, 48, 52, 57 ], 105 | [ 1, 2, 8 ], [ 1, 2, 5, 6, 7, 8, 9, 10, 12, 14, 16, 21, 22, 25, 27, 31, 38, 40, 43 ], [ 13 ], [ 1, 3, 4, 5, 7, 8, 9, 11, 12, 13, 14, 15, 18, 19, 21, 24, 27, 28, 30, 31, 33, 36, 38, 41, 44, 45, 47 ], [ 38 ], [ 2, 4, 7, 10, 11, 12, 14, 16, 18, 24, 25, 26, 28, 30, 32, 34, 36, 37, 38, 39, 40, 42, 44, 45, 46, 47, 48, 50, 51, 53, 55, 56, 57, 58, 60, 61, 62, 63, 64 ], [ 1, 5, 8 ], [ 2, 4, 5, 9, 12, 14, 15, 16, 17, 18, 21, 22, 23, 24, 30, 32, 33, 37, 38, 40, 41, 42, 43, 44, 48 ], [ 2 ], [ 21 ], [ 11 ], [ 6, 9, 10 ], [ 6 ], [ 11 ], [ 1, 2, 4, 6, 32, 33, 34, 35, 64, 65, 66, 67, 96, 97, 98 ], 106 | [ 3, 5, 6, 8, 9, 11, 15, 16, 19, 20, 22, 24, 25, 27, 30, 31, 34, 35, 36, 37, 41, 43, 44, 45, 46, 47, 48, 52, 55, 56, 57 ] 107 | ]; 108 | Fact(degree < Length(PRIMITIVE_TABLE_GF2) + 2, $"Degree {degree} is too large for table (max degree {Length(PRIMITIVE_TABLE_GF2) + 1}"); 109 | return PRIMITIVE_TABLE_GF2[degree - 2]; 110 | } 111 | 112 | /// # Summary 113 | /// Increments a special counter by 1. 114 | /// 115 | /// # Inputs 116 | /// ## counter 117 | /// Qubit register of the counter to increment. 118 | operation IncrementSpecialCounter(counter : Qubit[]) : Unit { 119 | body (...) { 120 | (Controlled IncrementSpecialCounter)([], (counter)); 121 | } 122 | controlled (controls, ...){ 123 | let nQubits=Length(counter); 124 | (Controlled CyclicRotateRegister)(controls, LittleEndian(counter)); 125 | let polynomial = _PrimitiveGF2Polynomial(nQubits); 126 | for idp in 0..Length(polynomial) - 1 { 127 | (Controlled CNOT)(controls, (counter[0], counter[polynomial[idp]])); 128 | } 129 | } 130 | controlled adjoint auto; 131 | } 132 | 133 | 134 | 135 | 136 | 137 | } -------------------------------------------------------------------------------- /ResourceEstimator/DisplayCSV.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Corporation. 2 | // Licensed under the MIT license. 3 | 4 | // Library that deals with making human-friendly the CSV tracer's output 5 | namespace Microsoft.Quantum.Crypto.ResourceEstimator.CommaSeparated 6 | { 7 | using System; 8 | using System.Collections.Generic; 9 | using FileHelpers; // csv parsing 10 | using Microsoft.Quantum.Simulation.Simulators.QCTraceSimulators; 11 | 12 | public class DisplayCSV 13 | { 14 | public static void Depth(string csv, string line_name, bool all = false) 15 | { 16 | var engine = new FileHelperAsyncEngine(); 17 | using (engine.BeginReadString(csv)) 18 | { 19 | // This wont display anything, we have dropped it 20 | foreach (var err in engine.ErrorManager.Errors) 21 | { 22 | Console.WriteLine(); 23 | Console.WriteLine("Error on Line number: {0}", err.LineNumber); 24 | Console.WriteLine("Record causing the problem: {0}", err.RecordString); 25 | Console.WriteLine("Complete exception information: {0}", err.ExceptionInfo.ToString()); 26 | } 27 | 28 | // The engine is IEnumerable 29 | foreach (DepthCounterCSV cust in engine) 30 | { 31 | // your code here 32 | if (cust.Name == line_name || all) 33 | { 34 | Console.WriteLine(cust.Name + " (<- " + cust.Caller + ") depth avg " + cust.DepthAverage + " (variance " + cust.DepthVariance + ")"); 35 | } 36 | } 37 | } 38 | } 39 | 40 | public static void Width(string csv, string line_name, bool all = false) 41 | { 42 | var engine = new FileHelperAsyncEngine(); 43 | using (engine.BeginReadString(csv)) 44 | { 45 | // This wont display anything, we have dropped it 46 | foreach (var err in engine.ErrorManager.Errors) 47 | { 48 | Console.WriteLine(); 49 | Console.WriteLine("Error on Line number: {0}", err.LineNumber); 50 | Console.WriteLine("Record causing the problem: {0}", err.RecordString); 51 | Console.WriteLine("Complete exception information: {0}", err.ExceptionInfo.ToString()); 52 | } 53 | 54 | // The engine is IEnumerable 55 | foreach (WidthCounterCSV cust in engine) 56 | { 57 | // your code here 58 | if (cust.Name == line_name || all) 59 | { 60 | Console.WriteLine(cust.Name + " (<- " + cust.Caller + ") initial width avg " + cust.InputWidthAverage + " (variance " + cust.InputWidthVariance + ")"); 61 | Console.WriteLine(cust.Name + " (<- " + cust.Caller + ") extra width avg " + cust.ExtraWidthAverage + " (variance " + cust.ExtraWidthVariance + ")"); 62 | Console.WriteLine(cust.Name + " (<- " + cust.Caller + ") return width avg " + cust.ReturnWidthAverage + " (variance " + cust.ReturnWidthVariance + ")"); 63 | Console.WriteLine(cust.Name + " (<- " + cust.Caller + ") borrowed width avg " + cust.BorrowedWidthAverage + " (variance " + cust.BorrowedWidthVariance + ")"); 64 | } 65 | } 66 | } 67 | } 68 | 69 | public static void Operations(string csv, string line_name, bool all = false) 70 | { 71 | var engine = new FileHelperAsyncEngine(); 72 | using (engine.BeginReadString(csv)) 73 | { 74 | // This wont display anything, we have dropped it 75 | foreach (var err in engine.ErrorManager.Errors) 76 | { 77 | Console.WriteLine(); 78 | Console.WriteLine("Error on Line number: {0}", err.LineNumber); 79 | Console.WriteLine("Record causing the problem: {0}", err.RecordString); 80 | Console.WriteLine("Complete exception information: {0}", err.ExceptionInfo.ToString()); 81 | } 82 | 83 | // The engine is IEnumerable 84 | foreach (OperationCounterCSV cust in engine) 85 | { 86 | // your code here 87 | if (cust.Name == line_name || all) 88 | { 89 | Console.WriteLine(cust.Name + " (<- " + cust.Caller + ") CNOT count avg " + cust.CNOTAverage + " (variance " + cust.CNOTVariance + ")"); 90 | Console.WriteLine(cust.Name + " (<- " + cust.Caller + ") Clifford count avg " + cust.QubitCliffordAverage + " (variance " + cust.QubitCliffordVariance + ")"); 91 | Console.WriteLine(cust.Name + " (<- " + cust.Caller + ") T count avg " + cust.TAverage + " (variance " + cust.TVariance + ")"); 92 | Console.WriteLine(cust.Name + " (<- " + cust.Caller + ") R count avg " + cust.RAverage + " (variance " + cust.RVariance + ")"); 93 | Console.WriteLine(cust.Name + " (<- " + cust.Caller + ") Measure count avg " + cust.MeasureAverage + " (variance " + cust.MeasureVariance + ")"); 94 | } 95 | } 96 | } 97 | } 98 | 99 | public static void All(Dictionary csv, string line_name, bool all = false) 100 | { 101 | // print results 102 | Depth(csv[MetricsCountersNames.depthCounter], line_name, all); 103 | Console.WriteLine(); 104 | Width(csv[MetricsCountersNames.widthCounter], line_name, all); 105 | Console.WriteLine(); 106 | Operations(csv[MetricsCountersNames.primitiveOperationsCounter], line_name, all); 107 | Console.WriteLine(); 108 | } 109 | 110 | public static string CSV(Dictionary csv, string line_name, bool display_header = false, string comment = "", bool all = false, string suffix = "") 111 | { 112 | string results = string.Empty; 113 | 114 | // print results 115 | if (display_header) 116 | { 117 | results += "operation, CNOT count, 1-qubit Clifford count, T count, R count, M count, T depth, initial width, extra width, comment, \n"; 118 | } 119 | 120 | results += $"{Environment.NewLine}{line_name}{suffix}, "; 121 | var countEngine = new FileHelperAsyncEngine(); 122 | using (countEngine.BeginReadString(csv[MetricsCountersNames.primitiveOperationsCounter])) 123 | { 124 | // The engine is IEnumerable 125 | foreach (OperationCounterCSV cust in countEngine) 126 | { 127 | if (cust.Name == line_name || all) 128 | { 129 | results += $"{cust.CNOTAverage}, {cust.QubitCliffordAverage}, {cust.TAverage}, {cust.RAverage}, {cust.MeasureAverage}, "; 130 | } 131 | } 132 | } 133 | 134 | var depthEngine = new FileHelperAsyncEngine(); 135 | using (depthEngine.BeginReadString(csv[MetricsCountersNames.depthCounter])) 136 | { 137 | // The engine is IEnumerable 138 | foreach (DepthCounterCSV cust in depthEngine) 139 | { 140 | if (cust.Name == line_name || all) 141 | { 142 | results += $"{cust.DepthAverage}, {cust.WidthAverage}, {cust.WidthMax}, "; 143 | } 144 | } 145 | } 146 | 147 | var widthEngine = new FileHelperAsyncEngine(); 148 | using (widthEngine.BeginReadString(csv[MetricsCountersNames.widthCounter])) 149 | { 150 | // The engine is IEnumerable 151 | foreach (WidthCounterCSV cust in widthEngine) 152 | { 153 | if (cust.Name == line_name || all) 154 | { 155 | results += $"{cust.InputWidthAverage}, {cust.ExtraWidthAverage}, "; 156 | } 157 | } 158 | } 159 | 160 | results += $"{comment}, "; 161 | return results; 162 | } 163 | } 164 | } -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | ## Ignore Visual Studio temporary files, build results, and 2 | ## files generated by popular Visual Studio add-ons. 3 | ## 4 | ## Get latest from https://github.com/github/gitignore/blob/master/VisualStudio.gitignore 5 | 6 | # User-specific files 7 | *.rsuser 8 | *.suo 9 | *.user 10 | *.userosscache 11 | *.sln.docstates 12 | 13 | # User-specific files (MonoDevelop/Xamarin Studio) 14 | *.userprefs 15 | 16 | # Mono auto generated files 17 | mono_crash.* 18 | 19 | # Build results 20 | [Dd]ebug/ 21 | [Dd]ebugPublic/ 22 | [Rr]elease/ 23 | [Rr]eleases/ 24 | x64/ 25 | x86/ 26 | [Aa][Rr][Mm]/ 27 | [Aa][Rr][Mm]64/ 28 | bld/ 29 | [Bb]in/ 30 | [Oo]bj/ 31 | [Ll]og/ 32 | 33 | # Visual Studio 2015/2017 cache/options directory 34 | .vs/ 35 | # Uncomment if you have tasks that create the project's static files in wwwroot 36 | #wwwroot/ 37 | 38 | # Visual Studio 2017 auto generated files 39 | Generated\ Files/ 40 | 41 | # MSTest test Results 42 | [Tt]est[Rr]esult*/ 43 | [Bb]uild[Ll]og.* 44 | 45 | # NUNIT 46 | *.VisualState.xml 47 | TestResult.xml 48 | 49 | # Build Results of an ATL Project 50 | [Dd]ebugPS/ 51 | [Rr]eleasePS/ 52 | dlldata.c 53 | 54 | # Benchmark Results 55 | BenchmarkDotNet.Artifacts/ 56 | 57 | # .NET Core 58 | project.lock.json 59 | project.fragment.lock.json 60 | artifacts/ 61 | 62 | # StyleCop 63 | StyleCopReport.xml 64 | 65 | # Files built by Visual Studio 66 | *_i.c 67 | *_p.c 68 | *_h.h 69 | *.ilk 70 | *.meta 71 | *.obj 72 | *.iobj 73 | *.pch 74 | *.pdb 75 | *.ipdb 76 | *.pgc 77 | *.pgd 78 | *.rsp 79 | *.sbr 80 | *.tlb 81 | *.tli 82 | *.tlh 83 | *.tmp 84 | *.tmp_proj 85 | *_wpftmp.csproj 86 | *.log 87 | *.vspscc 88 | *.vssscc 89 | .builds 90 | *.pidb 91 | *.svclog 92 | *.scc 93 | 94 | # Chutzpah Test files 95 | _Chutzpah* 96 | 97 | # Visual C++ cache files 98 | ipch/ 99 | *.aps 100 | *.ncb 101 | *.opendb 102 | *.opensdf 103 | *.sdf 104 | *.cachefile 105 | *.VC.db 106 | *.VC.VC.opendb 107 | 108 | # Visual Studio profiler 109 | *.psess 110 | *.vsp 111 | *.vspx 112 | *.sap 113 | 114 | # Visual Studio Trace Files 115 | *.e2e 116 | 117 | # TFS 2012 Local Workspace 118 | $tf/ 119 | 120 | # Guidance Automation Toolkit 121 | *.gpState 122 | 123 | # ReSharper is a .NET coding add-in 124 | _ReSharper*/ 125 | *.[Rr]e[Ss]harper 126 | *.DotSettings.user 127 | 128 | # JustCode is a .NET coding add-in 129 | .JustCode 130 | 131 | # TeamCity is a build add-in 132 | _TeamCity* 133 | 134 | # DotCover is a Code Coverage Tool 135 | *.dotCover 136 | 137 | # AxoCover is a Code Coverage Tool 138 | .axoCover/* 139 | !.axoCover/settings.json 140 | 141 | # Visual Studio code coverage results 142 | *.coverage 143 | *.coveragexml 144 | 145 | # NCrunch 146 | _NCrunch_* 147 | .*crunch*.local.xml 148 | nCrunchTemp_* 149 | 150 | # MightyMoose 151 | *.mm.* 152 | AutoTest.Net/ 153 | 154 | # Web workbench (sass) 155 | .sass-cache/ 156 | 157 | # Installshield output folder 158 | [Ee]xpress/ 159 | 160 | # DocProject is a documentation generator add-in 161 | DocProject/buildhelp/ 162 | DocProject/Help/*.HxT 163 | DocProject/Help/*.HxC 164 | DocProject/Help/*.hhc 165 | DocProject/Help/*.hhk 166 | DocProject/Help/*.hhp 167 | DocProject/Help/Html2 168 | DocProject/Help/html 169 | 170 | # Click-Once directory 171 | publish/ 172 | 173 | # Publish Web Output 174 | *.[Pp]ublish.xml 175 | *.azurePubxml 176 | # Note: Comment the next line if you want to checkin your web deploy settings, 177 | # but database connection strings (with potential passwords) will be unencrypted 178 | *.pubxml 179 | *.publishproj 180 | 181 | # Microsoft Azure Web App publish settings. Comment the next line if you want to 182 | # checkin your Azure Web App publish settings, but sensitive information contained 183 | # in these scripts will be unencrypted 184 | PublishScripts/ 185 | 186 | # NuGet Packages 187 | *.nupkg 188 | # The packages folder can be ignored because of Package Restore 189 | **/[Pp]ackages/* 190 | # except build/, which is used as an MSBuild target. 191 | !**/[Pp]ackages/build/ 192 | # Uncomment if necessary however generally it will be regenerated when needed 193 | #!**/[Pp]ackages/repositories.config 194 | # NuGet v3's project.json files produces more ignorable files 195 | *.nuget.props 196 | *.nuget.targets 197 | 198 | # Microsoft Azure Build Output 199 | csx/ 200 | *.build.csdef 201 | 202 | # Microsoft Azure Emulator 203 | ecf/ 204 | rcf/ 205 | 206 | # Windows Store app package directories and files 207 | AppPackages/ 208 | BundleArtifacts/ 209 | Package.StoreAssociation.xml 210 | _pkginfo.txt 211 | *.appx 212 | *.appxbundle 213 | *.appxupload 214 | 215 | # Visual Studio cache files 216 | # files ending in .cache can be ignored 217 | *.[Cc]ache 218 | # but keep track of directories ending in .cache 219 | !?*.[Cc]ache/ 220 | 221 | # Others 222 | ClientBin/ 223 | ~$* 224 | *~ 225 | *.dbmdl 226 | *.dbproj.schemaview 227 | *.jfm 228 | *.pfx 229 | *.publishsettings 230 | orleans.codegen.cs 231 | 232 | # Including strong name files can present a security risk 233 | # (https://github.com/github/gitignore/pull/2483#issue-259490424) 234 | #*.snk 235 | 236 | # Since there are multiple workflows, uncomment next line to ignore bower_components 237 | # (https://github.com/github/gitignore/pull/1529#issuecomment-104372622) 238 | #bower_components/ 239 | 240 | # RIA/Silverlight projects 241 | Generated_Code/ 242 | 243 | # Backup & report files from converting an old project file 244 | # to a newer Visual Studio version. Backup files are not needed, 245 | # because we have git ;-) 246 | _UpgradeReport_Files/ 247 | Backup*/ 248 | UpgradeLog*.XML 249 | UpgradeLog*.htm 250 | ServiceFabricBackup/ 251 | *.rptproj.bak 252 | 253 | # SQL Server files 254 | *.mdf 255 | *.ldf 256 | *.ndf 257 | 258 | # Business Intelligence projects 259 | *.rdl.data 260 | *.bim.layout 261 | *.bim_*.settings 262 | *.rptproj.rsuser 263 | *- Backup*.rdl 264 | 265 | # Microsoft Fakes 266 | FakesAssemblies/ 267 | 268 | # GhostDoc plugin setting file 269 | *.GhostDoc.xml 270 | 271 | # Node.js Tools for Visual Studio 272 | .ntvs_analysis.dat 273 | node_modules/ 274 | 275 | # Visual Studio 6 build log 276 | *.plg 277 | 278 | # Visual Studio 6 workspace options file 279 | *.opt 280 | 281 | # Visual Studio 6 auto-generated workspace file (contains which files were open etc.) 282 | *.vbw 283 | 284 | # Visual Studio LightSwitch build output 285 | **/*.HTMLClient/GeneratedArtifacts 286 | **/*.DesktopClient/GeneratedArtifacts 287 | **/*.DesktopClient/ModelManifest.xml 288 | **/*.Server/GeneratedArtifacts 289 | **/*.Server/ModelManifest.xml 290 | _Pvt_Extensions 291 | 292 | # Paket dependency manager 293 | .paket/paket.exe 294 | paket-files/ 295 | 296 | # FAKE - F# Make 297 | .fake/ 298 | 299 | # CodeRush personal settings 300 | .cr/personal 301 | 302 | # Python Tools for Visual Studio (PTVS) 303 | __pycache__/ 304 | *.pyc 305 | 306 | # Cake - Uncomment if you are using it 307 | # tools/** 308 | # !tools/packages.config 309 | 310 | # Tabs Studio 311 | *.tss 312 | 313 | # Telerik's JustMock configuration file 314 | *.jmconfig 315 | 316 | # BizTalk build output 317 | *.btp.cs 318 | *.btm.cs 319 | *.odx.cs 320 | *.xsd.cs 321 | 322 | # OpenCover UI analysis results 323 | OpenCover/ 324 | 325 | # Azure Stream Analytics local run output 326 | ASALocalRun/ 327 | 328 | # MSBuild Binary and Structured Log 329 | *.binlog 330 | 331 | # NVidia Nsight GPU debugger configuration file 332 | *.nvuser 333 | 334 | # MFractors (Xamarin productivity tool) working folder 335 | .mfractor/ 336 | 337 | # Local History for Visual Studio 338 | .localhistory/ 339 | 340 | # BeatPulse healthcheck temp database 341 | healthchecksdb 342 | 343 | # Backup folder for Package Reference Convert tool in Visual Studio 2017 344 | MigrationBackup/ 345 | 346 | ## 347 | ## Visual studio for Mac 348 | ## 349 | 350 | 351 | # globs 352 | Makefile.in 353 | *.userprefs 354 | *.usertasks 355 | config.make 356 | config.status 357 | aclocal.m4 358 | install-sh 359 | autom4te.cache/ 360 | *.tar.gz 361 | tarballs/ 362 | test-results/ 363 | 364 | # Mac bundle stuff 365 | *.dmg 366 | *.app 367 | 368 | # content below from: https://github.com/github/gitignore/blob/master/Global/macOS.gitignore 369 | # General 370 | .DS_Store 371 | .AppleDouble 372 | .LSOverride 373 | 374 | # Icon must end with two \r 375 | Icon 376 | 377 | 378 | # Thumbnails 379 | ._* 380 | 381 | # Files that might appear in the root of a volume 382 | .DocumentRevisions-V100 383 | .fseventsd 384 | .Spotlight-V100 385 | .TemporaryItems 386 | .Trashes 387 | .VolumeIcon.icns 388 | .com.apple.timemachine.donotpresent 389 | 390 | # Directories potentially created on remote AFP share 391 | .AppleDB 392 | .AppleDesktop 393 | Network Trash Folder 394 | Temporary Items 395 | .apdisk 396 | 397 | # content below from: https://github.com/github/gitignore/blob/master/Global/Windows.gitignore 398 | # Windows thumbnail cache files 399 | Thumbs.db 400 | ehthumbs.db 401 | ehthumbs_vista.db 402 | 403 | # Dump file 404 | *.stackdump 405 | 406 | # Folder config file 407 | [Dd]esktop.ini 408 | 409 | # Recycle Bin used on file shares 410 | $RECYCLE.BIN/ 411 | 412 | # Windows Installer files 413 | *.cab 414 | *.msi 415 | *.msix 416 | *.msm 417 | *.msp 418 | 419 | # Windows shortcuts 420 | *.lnk 421 | 422 | # JetBrains Rider 423 | .idea/ 424 | *.sln.iml 425 | 426 | ## 427 | ## Visual Studio Code 428 | ## 429 | .vscode/* 430 | !.vscode/settings.json 431 | !.vscode/tasks.json 432 | !.vscode/launch.json 433 | !.vscode/extensions.json 434 | -------------------------------------------------------------------------------- /ResourceEstimator/shor_estimate.py: -------------------------------------------------------------------------------- 1 | import math 2 | import copy 3 | import csv 4 | 5 | # Represents the costs of something with circuits 6 | # optimizing for low width, low depth, etc. 7 | class Cost: 8 | def __init__(self, low_depth, low_T, low_width): 9 | self.low_depth = low_depth 10 | self.low_T = low_T 11 | self.low_width = low_width 12 | 13 | def add(self, cost2): 14 | return Cost( 15 | low_depth = self.low_depth.add(cost2.low_depth), 16 | low_T = self.low_T.add(cost2.low_T), 17 | low_width = self.low_width.add(cost2.low_width) 18 | ) 19 | 20 | def subtract(self, cost2): 21 | return Cost( 22 | low_depth = self.low_depth.subtract(cost2.low_depth), 23 | low_T = self.low_T.subtract(cost2.low_T), 24 | low_width = self.low_width.subtract(cost2.low_width) 25 | ) 26 | def multiply(self, n): 27 | return Cost( 28 | low_depth = self.low_depth.multiply(n), 29 | low_T = self.low_T.multiply(n), 30 | low_width = self.low_width.multiply(n) 31 | ) 32 | def message(self): 33 | message = "" 34 | message += "Width-optimal: \n" +self.low_width.message() 35 | message += "T-optimal: \n" + self.low_T.message() 36 | message += "Depth-optimal: \n" + self.low_depth.message() + "\n" 37 | return message 38 | 39 | 40 | # Contains all relevant cost metrics for a single circuit 41 | class SingleCost: 42 | def __init__(self, width, T_depth, full_depth, measure, T_count, single_qubit, CNOT): 43 | self.width = width 44 | self.T_depth = T_depth 45 | self.T_count = T_count 46 | self.full_depth = full_depth 47 | self.measure = measure 48 | self.single_qubit = single_qubit 49 | self.CNOT = CNOT 50 | 51 | # Costs of two sequential circuits 52 | # Uses the maximum width (assumes circuits are run sequentially) 53 | def add(self, cost2): 54 | return SingleCost( 55 | width = max(self.width, cost2.width), 56 | T_depth = self.T_depth + cost2.T_depth, 57 | T_count = self.T_count + cost2.T_count, 58 | full_depth = self.full_depth + cost2.full_depth, 59 | measure = self.measure + cost2.measure, 60 | single_qubit = self.single_qubit + cost2.single_qubit, 61 | CNOT = self.CNOT + cost2.CNOT, 62 | ) 63 | 64 | # Subtracts 65 | # Assumes the width actually stays the same (does not decrease!) 66 | def subtract(self, cost2): 67 | return SingleCost( 68 | width = self.width, 69 | T_depth = self.T_depth - cost2.T_depth, 70 | T_count = self.T_count - cost2.T_count, 71 | full_depth = self.full_depth - cost2.full_depth, 72 | measure = self.measure - cost2.measure, 73 | single_qubit = self.single_qubit - cost2.single_qubit, 74 | CNOT = self.CNOT - cost2.CNOT, 75 | ) 76 | def multiply(self, n): 77 | return SingleCost( 78 | width = self.width, 79 | T_depth = self.T_depth * n, 80 | T_count = self.T_count * n, 81 | full_depth = self.full_depth * n, 82 | measure = self.measure * n, 83 | single_qubit = self.single_qubit * n, 84 | CNOT = self.CNOT * n 85 | ) 86 | #Outputs the costs to a string 87 | def message(self): 88 | message = "" 89 | message += " CNOT: " + str(self.CNOT) + "\n" 90 | message += " Single-qubit: " + str(self.single_qubit) + "\n" 91 | message += " Measurements: " + str(self.measure) + "\n" 92 | message += " T gates: " + str(self.T_count) + "\n" 93 | message += " T-depth: " + str(self.T_depth) + "\n" 94 | message += " Full depth: " + str(self.full_depth) + "\n" 95 | message += " Width: " +str( self.width) + "\n" 96 | return message 97 | 98 | #A string which can act as a header for a similar csv as Q# outputs 99 | @classmethod 100 | def CSV_header(Class): 101 | return "CNOT, Single Qubit, T gates, R gates, Measurements, T-depth, Initial Width, Extra Width, Full Depth, Window Size, Size\n" 102 | 103 | # Outputs the costs in a row that matches the header 104 | def csv_row(self): 105 | message = "" 106 | message += str(self.CNOT) + ", " 107 | message += str(self.single_qubit) + ", " 108 | message += str(self.T_count) + ", ," 109 | message += str(self.measure) + ", " 110 | message += str(self.T_depth) + ", ," 111 | message += str(self.width) + "," 112 | message += str(self.full_depth) 113 | return message 114 | 115 | # Returns the cost of a lookup for an n-bit elliptic curve point 116 | # among a table of 2^window_size points 117 | # Extrapolations based on output of Q# 118 | def Lookup_Cost(n, window_size): 119 | main_exponent = 2**window_size; 120 | costs = Cost( 121 | low_T = SingleCost( 122 | width = 2.678*window_size + 19.81+2.01*n, 123 | T_depth = 0.50733*main_exponent+23.0, 124 | full_depth = 17.04 * main_exponent + 101.04, 125 | measure = 1.503*main_exponent + 4.071 + 2.0*n, 126 | T_count = 4.0*main_exponent + 24.0, 127 | single_qubit = 7.74 * main_exponent + 10.68 + 2.0*n, 128 | CNOT = 115.13 * main_exponent + 117.76 129 | ), 130 | low_width = SingleCost( 131 | width = 2.657*window_size + 21.623 + 2.0*n, 132 | T_depth = 0.50733*main_exponent + 23.0, 133 | full_depth = 16.96 * main_exponent + 97.98, 134 | measure = 1.516*main_exponent + 2.288 + 2.0*n, 135 | T_count = 4.0*main_exponent + 24.0, 136 | single_qubit = 7.793 * main_exponent + 5.75 + 2.0*n, 137 | CNOT = main_exponent*(110.73+0.016*n) + 136.793 138 | ), 139 | low_depth = SingleCost( 140 | width = 2.657*window_size + 21.623 + 2.0*n, 141 | T_depth = 0.50733*main_exponent + 23.0, 142 | full_depth = 16.96 * main_exponent + 97.63, 143 | measure = 1.516 * main_exponent + 2.185+2.0*n, 144 | T_count = 4.0*main_exponent+24.0, 145 | single_qubit = 7.793*main_exponent + 5.218 + 2.0*n, 146 | CNOT = main_exponent*(110.74 + 0.016 * n) + 134.52 147 | ) 148 | ) 149 | return costs 150 | 151 | # Returns the costs of a single point addition with 152 | # window size of 8 153 | # Based on estimates from Q# 154 | def point_addition_cost(n): 155 | costs = {} 156 | nsquared = n*n 157 | lgn = math.log(n)/math.log(2.0) 158 | nlgn = n*lgn 159 | n2lgn = nsquared*lgn 160 | costs = Cost( 161 | low_T = SingleCost( 162 | width = 10.0*n + 1.5*math.floor(lgn) + 18.9, 163 | T_depth = 431.6*nsquared + 17572, 164 | full_depth = 1562 * nsquared + 120830, 165 | measure = 85*nsquared + 19465, 166 | T_count = 1182*nsquared + 92166, 167 | single_qubit = 648*nsquared + 101890, 168 | CNOT = 2391*nsquared + 473340 169 | ), 170 | low_width = SingleCost( 171 | width = 7.99*n + 3.81*math.floor(lgn) + 17.1, 172 | T_depth = 144.5 * n2lgn + 626302, 173 | full_depth = 464.6 * n2lgn + 2074976, 174 | measure = 753.7*n2lgn - 21095, 175 | T_count = 503.4*n2lgn + 1318387, 176 | single_qubit = 167.7 * n2lgn + 544865, 177 | CNOT = 751.2*n2lgn + 2296571 178 | ), 179 | low_depth = SingleCost( 180 | width = 11.0*n + 28.6, 181 | T_depth = 226.1*nlgn + 14469, 182 | full_depth = 1485*nlgn + 52413, 183 | measure = 202.5*nsquared - 14509, 184 | T_count = 2745*nsquared - 85878, 185 | single_qubit = 1462*nsquared - 35830, 186 | CNOT = 6481*nsquared+44882 187 | ) 188 | ) 189 | 190 | return costs 191 | 192 | def load_from_csv(csv_file_name, n, existing_costs = None): 193 | csv.register_dialect('cost_csv_dialect', skipinitialspace = True) 194 | with open(csv_file_name, newline="\n") as csvfile: 195 | csvCosts = csv.DictReader(csvfile, dialect='cost_csv_dialect') 196 | costs = SingleCost(0,0,0,0,0,0,0) 197 | for row in csvCosts: 198 | if (row['size'] == str(n)): 199 | if existing_costs is None: 200 | costs.CNOT = int(row['CNOT count']) 201 | costs.single_qubit = int(row['1-qubit Clifford count']) 202 | costs.T_count = int(row['T count']) 203 | costs.measure = int(row['M count']) 204 | costs.T_depth = int(row['T depth']) 205 | costs.width = int(row['extra width']) 206 | print(costs) 207 | else: 208 | costs = existing_costs 209 | costs.full_depth = int(row['Full depth']) 210 | return costs 211 | 212 | # Returns the costs of a single point addition with 213 | # window size of 8 for fixed-modulus curves 214 | # Based on estimates from Q# 215 | def fixed_modulus_point_addition_cost(n): 216 | low_T_costs = load_from_csv('EllipticCurveEstimates/LowT/Fixed-modulus-signed.csv', n) 217 | low_T_costs = load_from_csv('EllipticCurveEstimates/LowT/Fixed-modulus-signed-all-gates.csv', n, low_T_costs) 218 | 219 | low_width_costs = load_from_csv('EllipticCurveEstimates/LowWidth/Fixed-modulus-signed.csv', n) 220 | low_width_costs = load_from_csv('EllipticCurveEstimates/LowWidth/Fixed-modulus-signed-all-gates.csv', n, low_width_costs) 221 | 222 | low_depth_costs = load_from_csv('EllipticCurveEstimates/LowDepth/Fixed-modulus-signed.csv', n) 223 | low_depth_costs = load_from_csv('EllipticCurveEstimates/LowDepth/Fixed-modulus-signed-all-gates.csv', n, low_depth_costs) 224 | 225 | return Cost(low_T = low_T_costs, low_width = low_width_costs, low_depth = low_depth_costs) 226 | 227 | # Earlier code based on original estimates 228 | 229 | # if (n==256): 230 | # costs = Cost( 231 | # low_T = SingleCost( 232 | # CNOT = 178374765, 233 | # single_qubit = 178374765, 234 | # T_count = 77857742, 235 | # measure = 5656061, 236 | # T_depth = 28341737, 237 | # width = 2589, 238 | # full_depth = 102860240 239 | # ), 240 | # low_width = SingleCost( 241 | # CNOT = 462602294, 242 | # single_qubit = 94262398, 243 | # T_count = 272662642, 244 | # measure = 138293, 245 | # T_depth = 90582946, 246 | # width = 2091, 247 | # full_depth = 296669779 248 | # ), 249 | # low_depth = SingleCost( 250 | # CNOT = 436193618, 251 | # single_qubit = 96254895, 252 | # T_count = 179587682, 253 | # measure = 13210138, 254 | # T_depth = 490199, 255 | # width = 2852, 256 | # full_depth = 3174193 257 | # ) 258 | # ) 259 | # elif (n==384): 260 | # costs = Cost( 261 | # low_T = SingleCost( 262 | # CNOT = 430987821, 263 | # single_qubit = 95880884, 264 | # T_count = 174518574, 265 | # measure = 12587943, 266 | # T_depth = 63680030, 267 | # width = 3869, 268 | # full_depth = 230756452 269 | # ), 270 | # low_width = SingleCost( 271 | # CNOT = 1159548116, 272 | # single_qubit = 236838325, 273 | # T_count = 673208736, 274 | # measure = 207807, 275 | # T_depth = 220807469, 276 | # width = 3115, 277 | # full_depth = 728740733 278 | # ), 279 | # low_depth = SingleCost( 280 | # CNOT = 999485914, 281 | # single_qubit = 217697269, 282 | # T_count = 404345120, 283 | # measure = 29787028, 284 | # T_depth = 758770, 285 | # width = 4259, 286 | # full_depth = 4989451 287 | # ) 288 | # ) 289 | # elif (n==521): 290 | # costs = Cost( 291 | # low_T = SingleCost( 292 | # CNOT = 822773626, 293 | # single_qubit = 175844612, 294 | # T_count = 320708573, 295 | # measure = 23051575, 296 | # T_depth = 117140503, 297 | # width = 5240, 298 | # full_depth = 424156807 299 | # ), 300 | # low_width = SingleCost( 301 | # CNOT = 2263078766, 302 | # single_qubit = 448325541, 303 | # T_count = 1260252894, 304 | # measure = 497700, 305 | # T_depth = 412301349, 306 | # width = 4215, 307 | # full_depth = 1382063806 308 | # ), 309 | # low_depth = SingleCost( 310 | # CNOT = 1860934280, 311 | # single_qubit = 402483115, 312 | # T_count = 745693019, 313 | # measure = 55016018, 314 | # T_depth = 1057516, 315 | # width = 5770, 316 | # full_depth = 7135001 317 | # ) 318 | # ) 319 | # else: 320 | # raise InputError("No data for fixed modulus of size" + str(n)) 321 | # return costs 322 | 323 | 324 | def get_optimal_shor(addition_cost, n): 325 | #addition_cost = point_addition_cost(n) 326 | eight_lookup_cost = Lookup_Cost(n, 8).multiply(6) 327 | # The cost of an addition without any lookups 328 | # We also want to remove the qubits, too 329 | blank_addition_cost = addition_cost.subtract(eight_lookup_cost) 330 | blank_addition_cost.low_depth.width -= eight_lookup_cost.low_depth.width 331 | blank_addition_cost.low_T.width -= eight_lookup_cost.low_T.width 332 | blank_addition_cost.low_width.width -= eight_lookup_cost.low_width.width 333 | best_T_size = 8 334 | best_T = addition_cost.multiply(2*n) 335 | best_depth = addition_cost.multiply(2*n) 336 | best_depth_size = 8 337 | best_width = addition_cost.multiply(2*n) 338 | best_width_size = 8 339 | # Check all window sizes up to n/2 340 | for i in range(int(n/2)): 341 | # number of windows 342 | num_windows = math.floor( n / (i+1)) 343 | # size of remainder window 344 | remainder_window = max(n - num_windows * (i+1), 0) 345 | main_lookup_costs = Lookup_Cost(n,i).multiply(6) 346 | # Add in the cost of doing that many lookups 347 | main_addition_cost = blank_addition_cost.add(main_lookup_costs) 348 | # The number of point additions that need to be done 349 | total_cost = main_addition_cost.multiply(2*num_windows) 350 | 351 | # If there is a "remainder window" (a window smaller than the 352 | # others to finish the remaining bits), add that cost 353 | if remainder_window > 0: 354 | second_lookup_costs = Lookup_Cost(n,remainder_window).multiply(6) 355 | second_addition_cost = blank_addition_cost.add(second_lookup_costs) 356 | total_cost = total_cost.add(second_addition_cost.multiply(2)) 357 | #Here we add whichever width is greater 358 | total_cost.low_depth.width += max(second_lookup_costs.low_depth.width, main_lookup_costs.low_depth.width) 359 | total_cost.low_T.width += max(second_lookup_costs.low_T.width, main_lookup_costs.low_T.width) 360 | total_cost.low_width.width += max(second_lookup_costs.low_width.width, main_lookup_costs.low_width.width) 361 | else: 362 | #With no remainder window, we add just the main lookup width 363 | total_cost.low_depth.width += main_lookup_costs.low_depth.width 364 | total_cost.low_T.width += main_lookup_costs.low_T.width 365 | total_cost.low_width.width += main_lookup_costs.low_width.width 366 | # Compare to previous best counts, update as needed 367 | if total_cost.low_T.T_count < best_T.low_T.T_count: 368 | best_T = copy.deepcopy(total_cost) 369 | best_T_size = i 370 | if total_cost.low_width.T_count < best_width.low_width.T_count: 371 | best_width = copy.deepcopy(total_cost) 372 | best_width_size = i 373 | if total_cost.low_depth.T_depth < best_depth.low_depth.T_depth: 374 | best_depth = copy.deepcopy(total_cost) 375 | best_depth_size = i 376 | 377 | return {"T": best_T, "depth": best_depth, "width" : best_width, "T-window" : best_T_size, "depth-window" : best_depth_size, "width-window" : best_width_size} 378 | 379 | 380 | # Checks all elliptic curve sizes up to 521, writes to csv files 381 | T_CSV = SingleCost.CSV_header() 382 | depth_CSV = SingleCost.CSV_header() 383 | width_CSV = SingleCost.CSV_header() 384 | for i in range(10,522): 385 | costs = get_optimal_shor(point_addition_cost(i), i) 386 | T_CSV += costs["T"].low_T.csv_row() + "," + str(costs["T-window"]) + "," + str(i) + "\n" 387 | depth_CSV += costs["depth"].low_depth.csv_row() + "," + str(costs["depth-window"]) + "," + str(i) + "\n" 388 | width_CSV += costs["width"].low_width.csv_row() + "," + str(costs["width-window"]) + "," + str(i) + "\n" 389 | 390 | 391 | 392 | 393 | t_file = open('shor_low_t.csv', 'a') 394 | t_file.write(T_CSV) 395 | t_file.close() 396 | 397 | depth_file = open('shor_low_depth.csv', 'a') 398 | depth_file.write(depth_CSV) 399 | depth_file.close() 400 | 401 | width_file = open('shor_low_width.csv', 'a') 402 | width_file.write(width_CSV) 403 | width_file.close() 404 | 405 | # Check fixed modulus sizes 406 | T_CSV = SingleCost.CSV_header() 407 | depth_CSV = SingleCost.CSV_header() 408 | width_CSV = SingleCost.CSV_header() 409 | for i in {256, 384, 521}: 410 | costs = get_optimal_shor(fixed_modulus_point_addition_cost(i), i) 411 | T_CSV += costs["T"].low_T.csv_row() + "," + str(costs["T-window"]) + "," + str(i) + "\n" 412 | depth_CSV += costs["depth"].low_depth.csv_row() + "," + str(costs["depth-window"]) + "," + str(i) + "\n" 413 | width_CSV += costs["width"].low_width.csv_row() + "," + str(costs["width-window"]) + "," + str(i) + "\n" 414 | 415 | 416 | t_file = open('shor_low_t_fixed.csv', 'a') 417 | t_file.write(T_CSV) 418 | t_file.close() 419 | 420 | depth_file = open('shor_low_depth_fixed.csv', 'a') 421 | depth_file.write(depth_CSV) 422 | depth_file.close() 423 | 424 | width_file = open('shor_low_width_fixed.csv', 'a') 425 | width_file.write(width_CSV) 426 | width_file.close() 427 | -------------------------------------------------------------------------------- /ResourceEstimator/ResourceEstimateWrappers.qs: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Corporation. 2 | // Licensed under the MIT license. 3 | 4 | namespace Microsoft.Quantum.Crypto.ResourceEstimator 5 | { 6 | open Microsoft.Quantum.Crypto.Tests.Isogenies; 7 | open Microsoft.Quantum.Crypto.Fp2Arithmetic; 8 | open Microsoft.Quantum.Crypto.Isogenies; 9 | open Microsoft.Quantum.Intrinsic; 10 | open Microsoft.Quantum.Crypto.Basics; 11 | open Microsoft.Quantum.Crypto.Arithmetic; 12 | open Microsoft.Quantum.Crypto.ModularArithmetic; 13 | open Microsoft.Quantum.Crypto.EllipticCurves; 14 | open Microsoft.Quantum.Arithmetic; 15 | open Microsoft.Quantum.Canon; 16 | open Microsoft.Quantum.Convert; 17 | open Microsoft.Quantum.Math; 18 | open Microsoft.Quantum.Diagnostics; 19 | 20 | 21 | operation ClearRegister(register:Qubit[]):Unit { 22 | for idx in 0..Length(register)-1 { 23 | AssertMeasurementProbability([PauliZ],[register[idx]],Zero,0.0,"n/a",0.5); 24 | } 25 | ResetAll(register); 26 | } 27 | 28 | operation CCNOTEstimator(nQubits : Int, isControlled : Bool) : Unit { 29 | use qubits = Qubit[3] { 30 | CCNOT(qubits[0], qubits[1], qubits[2]); 31 | ClearRegister(qubits); 32 | } 33 | } 34 | 35 | operation ControlledOp<'T>(isControlled : Bool, op : ('T => Unit is Ctl), parameters : 'T) : Unit { 36 | if (isControlled){ 37 | use controls = Qubit[1] { 38 | (Controlled op)(controls, (parameters)); 39 | ClearRegister(controls); 40 | } 41 | } else { 42 | op(parameters); 43 | } 44 | } 45 | 46 | operation QuantumWhileEstimator(nQubits : Int, isControlled : Bool) : Unit { 47 | //The types are confusing, so this does the control manually 48 | let logn = BitSizeI(nQubits); 49 | use kQubits = Qubit[logn +1] { 50 | let counter = QubitsAsCounter(kQubits); 51 | counter::Prepare(); 52 | if (isControlled){ 53 | use controls = Qubit[1] { 54 | (Controlled QuantumWhile)(controls, (0, nQubits, NoOp, NoOp, NoOp, counter)); 55 | ClearRegister(controls); 56 | } 57 | } else { 58 | QuantumWhile(0, nQubits, NoOp, NoOp, NoOp, counter); 59 | } 60 | ClearRegister(kQubits); 61 | } 62 | } 63 | 64 | operation QuantumWhileAdditionEstimator(nQubits : Int, isControlled : Bool) : Unit { 65 | let logn = BitSizeI(nQubits); 66 | use (kQubits, xQubits, yQubits) = (Qubit[logn +1], Qubit[nQubits], Qubit[nQubits]) { 67 | let counter = QubitsAsCounter(kQubits); 68 | let xs = LittleEndian(xQubits); 69 | let ys = LittleEndian(yQubits); 70 | counter::Prepare(); 71 | let AddWithInteger = DummyIntegerWrapper(AddIntegerNoCarry, (xs,ys), _); 72 | if (isControlled){ 73 | use controls = Qubit[1] { 74 | (Controlled QuantumWhile)(controls, (0,nQubits, AddWithInteger, counter::Test, AddWithInteger, counter)); 75 | ClearRegister(controls); 76 | } 77 | } else { 78 | QuantumWhile(0,nQubits, AddWithInteger, counter::Test, AddWithInteger, counter); 79 | } 80 | ClearRegister(kQubits); 81 | } 82 | } 83 | 84 | operation LookUpEstimator(nQubits : Int, isControlled : Bool) : Unit { 85 | if (nQubits < 25){ 86 | use (addressQubits, outputQubits) = (Qubit[nQubits], Qubit[nQubits]) { 87 | let valueTable = Microsoft.Quantum.Arrays.ForEach(RandomBoundedBigInt(_, 2L^nQubits - 1L ), [0L, size= 2^nQubits]); 88 | let value = LittleEndian(outputQubits); 89 | let address = LittleEndian(addressQubits); 90 | ControlledOp<(BigInt[], (BigInt => Unit is Ctl + Adj), LittleEndian)> 91 | (isControlled, EqualLookup, (valueTable, ApplyXorInPlaceL(_, value), address)); 92 | ClearRegister(addressQubits + outputQubits); 93 | } 94 | } 95 | } 96 | 97 | operation PointLookUpEstimator(nQubits : Int, isControlled : Bool, windowSize : Int) : Unit { 98 | use (addressQubits, outputXs, outputYs) = (Qubit[windowSize + 1], Qubit[nQubits], Qubit[nQubits]) { 99 | let points = RandomPointArray(2L^nQubits - 1L, windowSize) + [RandomInvalidECPoint(false, 2L^nQubits - 1L)]; 100 | let value = ECPointMontgomeryForm( 101 | MontModInt(2L^nQubits - 1L, LittleEndian(outputXs)), 102 | MontModInt(2L^nQubits - 1L, LittleEndian(outputYs)) 103 | ); 104 | let address = LittleEndian(addressQubits); 105 | ControlledOp<(ECPointClassical[], (ECPointClassical => Unit is Ctl + Adj), LittleEndian)> 106 | (isControlled, EqualLookup, (points, EncodeClassicalECPointInQuantum(_, value), address)); 107 | ClearRegister(addressQubits + outputXs + outputYs); 108 | } 109 | } 110 | 111 | operation CheckIfAllZeroEstimator(nQubits : Int, isControlled : Bool) : Unit { 112 | use (xs,result) = (Qubit[nQubits], Qubit()) { 113 | ControlledOp(isControlled, CheckIfAllZero, (xs, result)); 114 | ClearRegister(xs+[result]); 115 | } 116 | } 117 | 118 | operation CheckIfAllOneEstimator(nQubits : Int, isControlled : Bool) : Unit { 119 | use (xs,result) = (Qubit[nQubits], Qubit()) { 120 | ControlledOp(isControlled, CheckIfAllOnes, (xs, result)); 121 | ClearRegister(xs+[result]); 122 | } 123 | } 124 | 125 | operation AdditionEstimator(nQubits : Int, isControlled : Bool) : Unit { 126 | use register = Qubit[2*nQubits+1] { 127 | let xs = LittleEndian(register[0..nQubits-1]); 128 | let ys = LittleEndian(register[nQubits..2*nQubits-1]); 129 | let carry = register[2*nQubits]; 130 | ControlledOp(isControlled, AddInteger, (xs,ys,carry)); 131 | ClearRegister(register); 132 | } 133 | } 134 | 135 | 136 | 137 | 138 | operation AdditionNoCarryEstimator(nQubits:Int, isControlled : Bool):Unit { 139 | use register = Qubit[2*nQubits] { 140 | let xs = LittleEndian(register[0..nQubits-1]); 141 | let ys = LittleEndian(register[nQubits..2*nQubits-1]); 142 | ControlledOp(isControlled, AddIntegerNoCarry, (xs,ys)); 143 | ClearRegister(register); 144 | } 145 | } 146 | 147 | 148 | operation CyclicShiftEstimator(nQubits : Int, isControlled : Bool) : Unit { 149 | use register = Qubit[nQubits] { 150 | let qInteger = LittleEndian(register[0 .. nQubits - 1]); 151 | ControlledOp(isControlled, CyclicRotateRegister, (qInteger)); 152 | ClearRegister(register); 153 | } 154 | } 155 | 156 | operation ConstantAdditionEstimator(nQubits:Int, isControlled : Bool):Unit { 157 | use register = Qubit[nQubits] { 158 | let constant = RandomBigInt(2L^nQubits); 159 | ControlledOp(isControlled, AddConstant, (constant, LittleEndian(register))); 160 | ClearRegister(register); 161 | } 162 | } 163 | 164 | 165 | 166 | operation GreaterThanEstimator(nQubits:Int, isControlled : Bool):Unit { 167 | use register = Qubit[2*nQubits+1] { 168 | let xs = LittleEndian(register[0..nQubits-1]); 169 | let ys = LittleEndian(register[nQubits..2*nQubits-1]); 170 | let result = register[2*nQubits]; 171 | ControlledOp(isControlled, GreaterThanWrapper, (xs,ys,result)); 172 | ClearRegister(register); 173 | } 174 | } 175 | 176 | 177 | 178 | operation ModularDblEstimator(nQubits : Int, isControlled : Bool) : Unit { 179 | use register = Qubit[nQubits] { 180 | let modulus = 2L * RandomBigInt(2L ^ (nQubits - 1)) + 1L; 181 | let xs = LittleEndian(register); 182 | ControlledOp(isControlled, ModularDblConstantModulus, (modulus, xs)); 183 | ClearRegister(register); 184 | } 185 | } 186 | 187 | 188 | 189 | operation ModularAdditionEstimator(nQubits:Int, isControlled : Bool):Unit { 190 | use register = Qubit[2*nQubits] { 191 | let xs = LittleEndian(register[0..nQubits-1]); 192 | let ys = LittleEndian(register[nQubits..2*nQubits-1]); 193 | let modulus = RandomBigInt(2L ^ nQubits); 194 | ControlledOp(isControlled, ModularAddConstantModulus, (modulus, xs, ys)); 195 | ClearRegister(register); 196 | } 197 | } 198 | 199 | operation MontgomeryWindowedMultiplicationWindowTest(nQubits : Int, isControlled : Bool, windowSize : Int) : Unit { 200 | let (nAncillas, nOutputs) = AncillaCountModularMulMontgomeryForm(nQubits); 201 | use (register, ancillas) = (Qubit[3*nQubits], Qubit[nAncillas]) { 202 | let xs = LittleEndian(register[0..nQubits-1]); 203 | let ys = LittleEndian(register[nQubits..2*nQubits-1]); 204 | let zs = LittleEndian(register[2*nQubits..3*nQubits -1]); 205 | let modulus = 2L*RandomBigInt(2L^(nQubits - 1)) + 1L; 206 | let xMMI= MontModInt(modulus, xs); 207 | let yMMI= MontModInt(modulus, ys); 208 | let zMMI= MontModInt(modulus, zs); 209 | ModularMulMontgomeryFormWindowedOpen(windowSize, xMMI, yMMI, ancillas, zMMI); 210 | ClearRegister(register); 211 | } 212 | } 213 | 214 | operation NonWindowedMontgomeryMultiplicationEstimator(nQubits : Int, isControlled : Bool) : Unit { 215 | use register = Qubit[3*nQubits] { 216 | let xs = LittleEndian(register[0..nQubits-1]); 217 | let ys = LittleEndian(register[nQubits..2*nQubits-1]); 218 | let zs = LittleEndian(register[2*nQubits..3*nQubits -1]); 219 | let modulus = 2L*RandomBigInt(2L^(nQubits - 1)) + 1L; 220 | let xMMI= MontModInt(modulus, xs); 221 | let yMMI= MontModInt(modulus, ys); 222 | let zMMI= MontModInt(modulus, zs); 223 | let (nAncillas, nOutputs) = AncillaCountModularMulMontgomeryForm(nQubits); 224 | use ancillas = Qubit[nAncillas] { 225 | ControlledOp(isControlled, ModularMulMontgomeryFormGeneric(CopyMontModInt(_,zMMI),_, _), (xMMI, yMMI)); 226 | ClearRegister(ancillas); 227 | } 228 | ClearRegister(register); 229 | } 230 | } 231 | 232 | operation MontgomeryMultiplicationEstimator(nQubits : Int, isControlled : Bool) : Unit { 233 | use register = Qubit[3*nQubits] { 234 | let xs = LittleEndian(register[0..nQubits-1]); 235 | let ys = LittleEndian(register[nQubits..2*nQubits-1]); 236 | let zs = LittleEndian(register[2*nQubits..3*nQubits -1]); 237 | let modulus = 2L*RandomBigInt(2L^(nQubits - 1)) + 1L; 238 | let xMMI= MontModInt(modulus, xs); 239 | let yMMI= MontModInt(modulus, ys); 240 | let zMMI= MontModInt(modulus, zs); 241 | 242 | ControlledOp(isControlled, ModularMulAndXorMontgomeryForm, (xMMI,yMMI,zMMI)); 243 | ClearRegister(register); 244 | } 245 | } 246 | 247 | operation MontgomerySquareEstimator(nQubits : Int, isControlled : Bool) : Unit { 248 | use register = Qubit[2*nQubits] { 249 | let xs = LittleEndian(register[0..nQubits-1]); 250 | let zs = LittleEndian(register[nQubits..2*nQubits -1]); 251 | let modulus = 2L*RandomBigInt(2L^(nQubits - 1)) + 1L; 252 | let xMMI= MontModInt(modulus, xs); 253 | let zMMI= MontModInt(modulus, zs); 254 | 255 | ControlledOp(isControlled, ModularSquMontgomeryFormWindowedGeneric(CopyMontModInt(_, zMMI), _), (xMMI)); 256 | ClearRegister(register); 257 | } 258 | } 259 | 260 | 261 | operation MontgomeryInversionRoundEstimator(nQubits : Int, isControlled : Bool) : Unit { 262 | use (u,v,r,s,ms,controls)=(Qubit[nQubits], Qubit[nQubits], Qubit[nQubits+1], Qubit[nQubits+1], Qubit[2*nQubits], Qubit[1]) { 263 | let us = LittleEndian(u); 264 | let vs = LittleEndian(v); 265 | let rs = LittleEndian(r); 266 | let ss = LittleEndian(s); 267 | ControlledOp(isControlled, _MontBitGCDRound, (0, us, vs, rs, ss, ms)); 268 | ClearRegister(u+v+r+s+ms+controls); 269 | } 270 | } 271 | 272 | 273 | operation MontgomeryInversionEstimator(nQubits : Int, isControlled : Bool) : Unit { 274 | use register = Qubit[2*nQubits] { 275 | let xs = LittleEndian(register[0..nQubits-1]); 276 | let ys = LittleEndian(register[nQubits..2*nQubits-1]); 277 | let modulus = 2L*RandomBigInt(2L^(nQubits - 1)) + 1L; 278 | let xMMI= MontModInt(modulus, xs); 279 | let yMMI= MontModInt(modulus, ys); 280 | ControlledOp(isControlled, ModularInvertAndCopyMontgomeryForm, (xMMI,yMMI)); 281 | ClearRegister(register); 282 | } 283 | } 284 | 285 | operation ModularDivisionEstimator(nQubits : Int, isControlled : Bool):Unit { 286 | let modulus = 2L * RandomBigInt(2L ^ (nQubits - 1)) + 1L; 287 | use (xs, ys, zs)=(Qubit[nQubits], Qubit[nQubits], Qubit[nQubits]) { 288 | ControlledOp(isControlled, ModularDivideAndAddMontgomeryForm, 289 | (MontModInt(modulus,LittleEndian(xs)), 290 | MontModInt(modulus,LittleEndian(ys)), 291 | MontModInt(modulus,LittleEndian(zs))) 292 | ); 293 | ClearRegister(xs + ys + zs); 294 | } 295 | } 296 | 297 | operation EllipticCurveConstantPointAdditionEstimator(nQubits : Int, isControlled : Bool) : Unit { 298 | use register = Qubit[2 * nQubits] { 299 | let modulus = 2l*RandomBigInt(2L^(nQubits-1)) - 1L; 300 | let pointX = RandomBoundedBigInt(0L, modulus); 301 | let pointY = RandomBoundedBigInt(0L, modulus); 302 | let cPoint = ECPointClassical(pointX, pointY,true,modulus); 303 | 304 | let xs = LittleEndian(register[0 .. nQubits - 1]); 305 | let ys = LittleEndian(register[nQubits .. 2 * nQubits - 1]); 306 | 307 | let qPoint = ECPointMontgomeryForm(MontModInt(modulus,xs),MontModInt(modulus, ys)); 308 | ControlledOp(isControlled, DistinctEllipticCurveClassicalPointAddition, (cPoint, qPoint)); 309 | 310 | ClearRegister(register); 311 | } 312 | } 313 | 314 | operation RandomInvalidECPoint(dummyBool : Bool, modulus : BigInt) : ECPointClassical { 315 | return ECPointClassical(RandomBoundedBigInt(0L, modulus), 316 | RandomBoundedBigInt(0L,modulus), 317 | true, 318 | modulus); 319 | } 320 | 321 | operation RandomPointArray(modulus : BigInt, windowSize : Int) : ECPointClassical[] { 322 | return Microsoft.Quantum.Arrays.ForEach(RandomInvalidECPoint(_, modulus), 323 | [false, size = 2^windowSize] 324 | ); 325 | } 326 | 327 | operation EllipticCurveWindowedPointAdditionLowWidthWindowTest(nQubits : Int, isControlled : Bool, windowSize : Int) : Unit { 328 | use register = Qubit[2 * nQubits + windowSize] { 329 | let modulus = 2L*RandomBoundedBigInt(2L^(nQubits - 2), 2L^(nQubits-1)) + 1L; 330 | let points = RandomPointArray(modulus, windowSize); 331 | let xs = LittleEndian(register[0 .. nQubits - 1]); 332 | let ys = LittleEndian(register[nQubits .. 2 * nQubits - 1]); 333 | let address = LittleEndian(register[2 * nQubits .. 2 * nQubits + windowSize - 1]); 334 | 335 | let qPoint = ECPointMontgomeryForm(MontModInt(modulus,xs),MontModInt(modulus, ys)); 336 | ControlledOp(isControlled, WindowedEllipticCurvePointAdditionLowWidth, (points, address, qPoint)); 337 | 338 | ClearRegister(register); 339 | } 340 | } 341 | 342 | 343 | 344 | operation EllipticCurveWindowedPointAdditionWindowTest(nQubits : Int, isControlled : Bool, windowSize : Int) : Unit { 345 | use register = Qubit[2 * nQubits + windowSize] { 346 | let modulus = 2L*RandomBoundedBigInt(2L^(nQubits - 2), 2L^(nQubits-1)) + 1L; 347 | let points = RandomPointArray(modulus, windowSize); 348 | let xs = LittleEndian(register[0 .. nQubits - 1]); 349 | let ys = LittleEndian(register[nQubits .. 2 * nQubits - 1]); 350 | let address = LittleEndian(register[2 * nQubits .. 2 * nQubits + windowSize - 1]); 351 | 352 | let qPoint = ECPointMontgomeryForm(MontModInt(modulus,xs),MontModInt(modulus, ys)); 353 | ControlledOp(isControlled, WindowedEllipticCurvePointAddition, (points, address, qPoint)); 354 | 355 | ClearRegister(register); 356 | } 357 | } 358 | 359 | operation EllipticCurveSignedWindowedPointAdditionWindowTest(nQubits : Int, isControlled : Bool, windowSize : Int) : Unit { 360 | 361 | use register = Qubit[2 * nQubits + windowSize] { 362 | let modulus = 2L*RandomBoundedBigInt(2L^(nQubits - 2), 2L^(nQubits-1)) + 1L; 363 | Message($"OG modulus: {modulus}"); 364 | let points = RandomPointArray(modulus, windowSize - 1) + [RandomInvalidECPoint(false, modulus)]; 365 | let xs = LittleEndian(register[0 .. nQubits - 1]); 366 | let ys = LittleEndian(register[nQubits .. 2 * nQubits - 1]); 367 | let address = register[2 * nQubits .. 2 * nQubits + windowSize - 1]; 368 | let qPoint = ECPointMontgomeryForm(MontModInt(modulus,xs),MontModInt(modulus, ys)); 369 | ControlledOp(isControlled, SignedWindowedEllipticCurvePointAdditionLowWidth, (points, address, qPoint)); 370 | 371 | ClearRegister(register); 372 | } 373 | } 374 | 375 | operation EllipticCurveWindowedPointAdditionEstimator(nQubits : Int, isControlled : Bool) : Unit { 376 | let windowSize = OptimalPointAdditionWindowSize(nQubits); 377 | EllipticCurveWindowedPointAdditionWindowTest(nQubits, isControlled, windowSize); 378 | 379 | } 380 | 381 | operation EllipticCurveWindowedPointAdditionLowWidthEstimator(nQubits : Int, isControlled : Bool) : Unit { 382 | let windowSize = OptimalPointAdditionWindowSize(nQubits); 383 | EllipticCurveWindowedPointAdditionLowWidthWindowTest(nQubits, isControlled, windowSize); 384 | } 385 | 386 | operation EllipticCurveSignedWindowedPointAdditionEstimator(nQubits : Int, isControlled : Bool) : Unit { 387 | let windowSize = OptimalSignedPointAdditionWindowSize(nQubits); 388 | EllipticCurveSignedWindowedPointAdditionWindowTest(nQubits, isControlled, windowSize); 389 | } 390 | 391 | operation FixedEllipticCurveSignedWindowedPointAdditionEstimator(nQubits : Int, isControlled : Bool) : Unit { 392 | mutable modulus = 0L; 393 | mutable basePoint = ECPointClassical(0L,0L,false,0L); 394 | mutable curve = ECCurveWeierstrassClassical(0L, 0L, 0L); 395 | if (nQubits == 10){ 396 | let (tempCurve, tempPoint, _, _) = TenBitCurve(); 397 | set curve = tempCurve; 398 | set basePoint = tempPoint; 399 | } elif (nQubits == 30){ 400 | let (tempCurve, tempPoint, _, _) = ThirtyBitCurve(); 401 | set curve = tempCurve; 402 | set basePoint = tempPoint; 403 | } elif (nQubits == 192){ 404 | let (tempCurve, tempPoint, _, _) = NISTP192(); 405 | set curve = tempCurve; 406 | set basePoint = tempPoint; 407 | } elif (nQubits == 224){ 408 | let (tempCurve, tempPoint, _, _) = NISTP224(); 409 | set curve = tempCurve; 410 | set basePoint = tempPoint; 411 | } elif (nQubits == 256){ 412 | let (tempCurve, tempPoint, _, _) = NISTP256(); 413 | set curve = tempCurve; 414 | set basePoint = tempPoint; 415 | } elif (nQubits == 384){ 416 | let (tempCurve, tempPoint, _, _) = NISTP384(); 417 | set curve = tempCurve; 418 | set basePoint = tempPoint; 419 | } elif (nQubits == 521){ 420 | let (tempCurve, tempPoint, _, _) = NISTP521(); 421 | set curve = tempCurve; 422 | set basePoint = tempPoint; 423 | } else { 424 | Fact(false, $"No pre-specified curve exists with {nQubits}-bit modulus"); 425 | } 426 | set modulus = curve::modulus; 427 | let windowSize = OptimalPointAdditionWindowSize(nQubits); 428 | use register = Qubit[2 * nQubits + windowSize] { 429 | let points = PointTable(basePoint, 430 | ECPointClassical(0L, 0L, false, modulus), 431 | curve, 432 | windowSize 433 | ) + [MultiplyClassicalECPoint(basePoint, curve, 2L^windowSize)]; 434 | 435 | let xs = LittleEndian(register[0 .. nQubits - 1]); 436 | let ys = LittleEndian(register[nQubits .. 2 * nQubits - 1]); 437 | let address = register[2 * nQubits .. 2 * nQubits + windowSize - 1]; 438 | 439 | let qPoint = ECPointMontgomeryForm(MontModInt(modulus,xs),MontModInt(modulus, ys)); 440 | ControlledOp(isControlled, SignedWindowedEllipticCurvePointAdditionLowWidth, (points, address, qPoint)); 441 | ClearRegister(register); 442 | } 443 | 444 | } 445 | 446 | 447 | operation DifferentialAddEstimator(nQubits : Int, isControlled : Bool) : Unit { 448 | let modulus = RandomFp2Modulus(nQubits); 449 | let pointP = ECPointMontgomeryXZClassical(RandomFp2ElementClassical(modulus), RandomFp2ElementClassical(modulus)); 450 | let (nAncillas, _) = AncillaCountECPointDiffAddition(nQubits); 451 | use (qQubits, qMinPQubits, ancillas, outputQubits) = 452 | (Qubit[4 * nQubits], Qubit[4 * nQubits], Qubit[nAncillas], Qubit[4 * nQubits]){ 453 | let pointQ = QubitArrayAsECPointMontgomeryXZ(modulus, nQubits, qQubits); 454 | let pointQminP = QubitArrayAsECPointMontgomeryXZ(modulus, nQubits, qMinPQubits); 455 | let outputPoint = QubitArrayAsECPointMontgomeryXZ(modulus, nQubits, outputQubits); 456 | ControlledOp(isControlled, DifferentialAddECPointMontgomeryXZOpen, (pointP, pointQ, pointQminP, ancillas, outputPoint)); 457 | ClearRegister(qQubits + qMinPQubits + ancillas + outputQubits); 458 | } 459 | 460 | } 461 | 462 | operation PointDoublingEstimator(nQubits : Int, isControlled : Bool) : Unit { 463 | let modulus = RandomFp2Modulus(nQubits); 464 | let (nAncillas, _) = AncillaCountDoubleECPoint(nQubits); 465 | use (pointQubits, curveQubits, ancillas, outputQubits) 466 | = (Qubit[4 * nQubits], Qubit[4 * nQubits], Qubit[nAncillas], Qubit[4 * nQubits]){ 467 | let point = QubitArrayAsECPointMontgomeryXZ(modulus, nQubits, pointQubits); 468 | let curve = QubitArrayAsECCoordsMontgomeryFormAPlusC(modulus, nQubits, curveQubits); 469 | let outputPoint = QubitArrayAsECPointMontgomeryXZ(modulus, nQubits, outputQubits); 470 | ControlledOp(isControlled, DoubleECPointMontgomeryXZOpen, (point, curve, ancillas, outputPoint)); 471 | ClearRegister(pointQubits + curveQubits + ancillas + outputQubits); 472 | } 473 | } 474 | 475 | operation TwoIsogenyPointEstimator(nQubits : Int, isControlled : Bool) : Unit { 476 | let modulus = RandomFp2Modulus(nQubits); 477 | use (kernelQubits, targetQubits, outputQubits) 478 | = (Qubit[4 * nQubits], Qubit[4 * nQubits], Qubit[4 * nQubits]){ 479 | let kernelPoint = QubitArrayAsECPointMontgomeryXZ(modulus, nQubits, kernelQubits); 480 | let targetPoint = QubitArrayAsECPointMontgomeryXZ(modulus, nQubits, targetQubits); 481 | let outputPoint = QubitArrayAsECPointMontgomeryXZ(modulus, nQubits, outputQubits); 482 | ControlledOp(isControlled, _TwoIsogenyOfCrossedKernelPoint, (kernelPoint, targetPoint, outputPoint)); 483 | ClearRegister(kernelQubits + targetQubits + outputQubits); 484 | } 485 | } 486 | operation TwoIsogenyCurveEstimator(nQubits : Int, isControlled : Bool) : Unit { 487 | let modulus = RandomFp2Modulus(nQubits); 488 | use (kernelQubits, outputQubits) 489 | = (Qubit[4 * nQubits], Qubit[4 * nQubits]){ 490 | let kernelPoint = QubitArrayAsECPointMontgomeryXZ(modulus, nQubits, kernelQubits); 491 | let outputCurve = QubitArrayAsECCoordsMontgomeryFormAPlusC(modulus, nQubits, outputQubits); 492 | ControlledOp(isControlled, TwoIsogenyOfCurveMontgomeryXZ, (kernelPoint, outputCurve)); 493 | ClearRegister(kernelQubits + outputQubits); 494 | } 495 | } 496 | 497 | operation SIKEDifferentialAddEstimator(nQubits : Int, isControlled : Bool) : Unit { 498 | let sikeParams = GetSIKEParams(nQubits/2); 499 | let modulus = sikeParams::prime; 500 | let pointP = sikeParams::pointP; 501 | let (nAncillas, _) = AncillaCountECPointDiffAddition(nQubits); 502 | use (qQubits, qMinPQubits, ancillas, outputQubits) = 503 | (Qubit[4 * nQubits], Qubit[4 * nQubits], Qubit[nAncillas], Qubit[4 * nQubits]){ 504 | let pointQ = QubitArrayAsECPointMontgomeryXZ(modulus, nQubits, qQubits); 505 | let pointQminP = QubitArrayAsECPointMontgomeryXZ(modulus, nQubits, qMinPQubits); 506 | let outputPoint = QubitArrayAsECPointMontgomeryXZ(modulus, nQubits, outputQubits); 507 | ControlledOp(isControlled, DifferentialAddECPointMontgomeryXZOpen, (pointP, pointQ, pointQminP, ancillas, outputPoint)); 508 | ClearRegister(qQubits + qMinPQubits + ancillas + outputQubits); 509 | } 510 | 511 | } 512 | 513 | function GetSIKEParamsForQubits(nQubits : Int) : SIKEParams { 514 | mutable parameters = GetSIKEParams(4); 515 | mutable twoTorsion = 4; 516 | while (not (BitSizeL(parameters::prime) == nQubits)) { 517 | set parameters = GetSIKEParams(twoTorsion); 518 | set twoTorsion = twoTorsion + 1; 519 | Fact(twoTorsion <= 0x174, $"No SIKE parameters with a prime of exactly {nQubits} bits"); 520 | } 521 | return parameters; 522 | } 523 | 524 | operation SIKEPointDoublingEstimator(nQubits : Int, isControlled : Bool) : Unit { 525 | let modulus = (GetSIKEParamsForQubits(nQubits))::prime; 526 | let (nAncillas, _) = AncillaCountDoubleECPoint(nQubits); 527 | use (pointQubits, curveQubits, ancillas, outputQubits) 528 | = (Qubit[4 * nQubits], Qubit[4 * nQubits], Qubit[nAncillas], Qubit[4 * nQubits]){ 529 | let point = QubitArrayAsECPointMontgomeryXZ(modulus, nQubits, pointQubits); 530 | let curve = QubitArrayAsECCoordsMontgomeryFormAPlusC(modulus, nQubits, curveQubits); 531 | let outputPoint = QubitArrayAsECPointMontgomeryXZ(modulus, nQubits, outputQubits); 532 | ControlledOp(isControlled, DoubleECPointMontgomeryXZOpen, (point, curve, ancillas, outputPoint)); 533 | ClearRegister(pointQubits + curveQubits + ancillas + outputQubits); 534 | } 535 | } 536 | 537 | 538 | operation SIKETwoIsogenyCurveEstimator(nQubits : Int, isControlled : Bool) : Unit { 539 | let modulus = (GetSIKEParamsForQubits(nQubits))::prime; 540 | use (kernelQubits, outputQubits) 541 | = (Qubit[4 * nQubits], Qubit[4 * nQubits]){ 542 | let kernelPoint = QubitArrayAsECPointMontgomeryXZ(modulus, nQubits, kernelQubits); 543 | let outputCurve = QubitArrayAsECCoordsMontgomeryFormAPlusC(modulus, nQubits, outputQubits); 544 | ControlledOp(isControlled, TwoIsogenyOfCurveMontgomeryXZ, (kernelPoint, outputCurve)); 545 | ClearRegister(kernelQubits + outputQubits); 546 | } 547 | } 548 | 549 | operation SIKETwoIsogenyPointEstimator(nQubits : Int, isControlled : Bool) : Unit { 550 | let modulus = (GetSIKEParamsForQubits(nQubits))::prime; 551 | use (kernelQubits, outputQubits) 552 | = (Qubit[4 * nQubits], Qubit[4 * nQubits]){ 553 | let kernelPoint = QubitArrayAsECPointMontgomeryXZ(modulus, nQubits, kernelQubits); 554 | let outputCurve = QubitArrayAsECCoordsMontgomeryFormAPlusC(modulus, nQubits, outputQubits); 555 | ControlledOp(isControlled, TwoIsogenyOfCurveMontgomeryXZ, (kernelPoint, outputCurve)); 556 | ClearRegister(kernelQubits + outputQubits); 557 | } 558 | } 559 | 560 | operation SIKEJInvariantEstimator(nQubits : Int, isControlled : Bool) : Unit { 561 | let modulus = (GetSIKEParamsForQubits(nQubits))::prime; 562 | let (nAncillas, _) = AncillaCountJInvariantAPlusC(nQubits); 563 | use (curveQubits, ancillas, jInvariantQubits) = (Qubit[4 * nQubits], Qubit[nAncillas], Qubit[2 * nQubits]) { 564 | let curve = QubitArrayAsECCoordsMontgomeryFormAPlusC(modulus, nQubits, curveQubits); 565 | let jInvariant = QubitArrayAsFp2MontModInt(modulus, jInvariantQubits); 566 | ControlledOp(isControlled, GetJInvariantAPlusCOpen, (curve, ancillas, jInvariant)); 567 | ClearRegister(curveQubits + ancillas + jInvariantQubits); 568 | } 569 | } 570 | 571 | operation SIKEIsogenyEstimator(nQubits : Int, isControlled : Bool) : Unit { 572 | // Here we assume we're not simulating this, 573 | // so we really don't care what the actual values are 574 | let modulus = RandomFp2Modulus(nQubits); 575 | let curve = ECCoordsMontgomeryFormAPlusCClassical(RandomFp2ElementClassical(modulus), RandomFp2ElementClassical(modulus)); 576 | let pointP = ECPointMontgomeryXZClassical(RandomFp2ElementClassical(modulus), RandomFp2ElementClassical(modulus)); 577 | let pointQ = ECPointMontgomeryXZClassical(RandomFp2ElementClassical(modulus), RandomFp2ElementClassical(modulus)); 578 | let pointR = ECPointMontgomeryXZClassical(RandomFp2ElementClassical(modulus), RandomFp2ElementClassical(modulus)); 579 | let height = nQubits/2; 580 | use (coefficientQubits, jQubits) = (Qubit[height], Qubit[2 * nQubits]) { 581 | let coefficient = LittleEndian(coefficientQubits); 582 | let jInvariant = QubitArrayAsFp2MontModInt(modulus, jQubits); 583 | ControlledOp(isControlled, ComputeSIKETwoIsogeny, (curve, pointP, pointQ, pointR, height, coefficient, jInvariant)); 584 | ClearRegister(coefficientQubits + jQubits); 585 | } 586 | } 587 | 588 | operation SIKEIsogenyValidPrimeEstimator(nQubits : Int, isControlled : Bool) : Unit { 589 | let parameters = GetSIKEParamsForQubits(nQubits); 590 | let modulus = parameters::prime; 591 | let height = parameters::twoOrder; 592 | let curve = ECCoordsMontgomeryFormAPlusCClassical( 593 | Fp2ElementClassical(modulus, 8L, 0L), 594 | Fp2ElementClassical(modulus, 4L, 0L) 595 | ); 596 | use (coefficientQubits, jQubits) = (Qubit[height], Qubit[2 * nQubits]) { 597 | let coefficient = LittleEndian(coefficientQubits); 598 | let jInvariant = QubitArrayAsFp2MontModInt(modulus, jQubits); 599 | ControlledOp(isControlled, ComputeSIKETwoIsogeny, 600 | (curve, parameters::pointP, parameters::pointQ, 601 | parameters::pointR, height, coefficient, 602 | jInvariant) 603 | ); 604 | ClearRegister(coefficientQubits + jQubits); 605 | } 606 | } 607 | 608 | } 609 | -------------------------------------------------------------------------------- /ResourceEstimator/Driver.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Corporation. 2 | // Licensed under the MIT license. 3 | 4 | namespace Microsoft.Quantum.Crypto.ResourceEstimator 5 | { 6 | using System; 7 | using System.Collections.Generic; 8 | using System.Threading; 9 | using Microsoft.Quantum.Crypto.Basics; 10 | using Microsoft.Quantum.Crypto.ResourceEstimator.CommaSeparated; 11 | using Microsoft.Quantum.ModularArithmetic.DebugHelpers; 12 | using Microsoft.Quantum.Simulation.Simulators; 13 | using Microsoft.Quantum.Simulation.Simulators.QCTraceSimulators; 14 | 15 | public class Driver 16 | { 17 | public delegate System.Threading.Tasks.Task RunQop(QCTraceSimulator sim, long n, bool isControlled); 18 | 19 | public delegate System.Threading.Tasks.Task RunParameterizedQop(QCTraceSimulator sim, long n, bool isControlled, long m); 20 | 21 | public static void Main(string[] args) 22 | { 23 | string subFolder; 24 | if (DriverParameters.MinimizeDepthCostMetric) 25 | { 26 | subFolder = "LowDepth/"; 27 | } 28 | else if (DriverParameters.MinimizeTCostMetric) 29 | { 30 | subFolder = "LowT/"; 31 | } 32 | else 33 | { 34 | subFolder = "LowWidth/"; 35 | } 36 | 37 | int[] bigTestSizes = { 4, 8, 16, 32, 64, 110, 128, 160, 192, 224, 256, 384, 512 }; 38 | int[] smallSizes = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25 }; 39 | int[] ellipticCurveTestSizes = { 110, 160, 192, 224, 256, 384, 521 }; 40 | int[] fixedEllipticCurveTestSizes = { 10, 30, 192, 224, 256, 384, 521 }; 41 | 42 | // construct list of bit sizes 43 | List exhaustiveArithmeticSizes = new List(); 44 | List exhaustiveSmallCurveSizes = new List(); 45 | System.Random rnd = new System.Random(); 46 | for (int i = 4; i < 64; i++) 47 | { 48 | exhaustiveArithmeticSizes.Add(i); 49 | exhaustiveSmallCurveSizes.Add(i); 50 | } 51 | 52 | // Checking all bit sizes between 64 and 2048 would be too many 53 | // Incrementing by a fixed value might cause issues with regularities 54 | // in Hamming weight, etc.; choosing random increments avoids this. 55 | for (int i = 64; i <= 2048; i += 8 + rnd.Next(5)) 56 | { 57 | exhaustiveArithmeticSizes.Add(i); 58 | } 59 | 60 | exhaustiveSmallCurveSizes.AddRange(ellipticCurveTestSizes); 61 | 62 | EstimateModularMultiplicationWindowSizes(bigTestSizes, "ModularMultiplicationWindows/" + subFolder); 63 | 64 | EstimateArithmetic(exhaustiveArithmeticSizes.ToArray(), "ArithmeticEstimates/" + subFolder); 65 | EstimateCheapModularArithmetic(exhaustiveArithmeticSizes.ToArray(), "ModularArithmeticEstimates/" + subFolder); 66 | EstimateExpensiveModularArithmetic(exhaustiveSmallCurveSizes.ToArray(), "ModularArithmeticEstimates/" + subFolder); 67 | EstimatePointLookups(ellipticCurveTestSizes, "LookupEstimates/" + subFolder); 68 | EstimateEllipticCurveArithmetic(exhaustiveSmallCurveSizes.ToArray(), "EllipticCurveEstimates/" + subFolder); 69 | EstimateFixedEllipticCurveArithmetic(fixedEllipticCurveTestSizes, "EllipticCurveEstimates/" + subFolder); 70 | } 71 | 72 | public static void EstimateLookup(int[] testSizes, string directory) 73 | { 74 | // Writes global parameters (cost metric, testable gates) to terminal 75 | DriverParameters.Print(); 76 | 77 | System.IO.Directory.CreateDirectory(directory); 78 | 79 | // Loops over controlled/not and whether it counts all gates 80 | bool allGates = false; 81 | bool isControlled = false; 82 | for (int j = 0; j < 2; j++) 83 | { 84 | for (int i = 0; i < 2; i++) 85 | { 86 | var localControl = isControlled; 87 | var localGates = allGates; 88 | Thread lookupThread = new Thread(() => BasicResourceTest( 89 | LookUpEstimator.Run, 90 | testSizes, 91 | localControl, 92 | directory + "Lookup", 93 | localGates, 94 | true)); 95 | lookupThread.Start(); 96 | isControlled = !isControlled; 97 | } 98 | 99 | allGates = !allGates; 100 | } 101 | } 102 | 103 | public static void EstimateArithmetic(int[] testSizes, string directory) 104 | { 105 | // Writes global parameters (cost metric, testable gates) to terminal 106 | DriverParameters.Print(); 107 | 108 | System.IO.Directory.CreateDirectory(directory); 109 | 110 | // Loops over controlled/not and whether it counts all gates 111 | bool allGates = false; 112 | bool isControlled = false; 113 | for (int j = 0; j < 2; j++) 114 | { 115 | for (int i = 0; i < 2; i++) 116 | { 117 | // Creates a new thread for each operation being estimated 118 | var localControl = isControlled; 119 | var localGates = allGates; 120 | 121 | // Thread onesThread = new Thread(() => BasicResourceTest( 122 | // CheckIfAllOneEstimator.Run, 123 | // testSizes, 124 | // localControl, 125 | // directory + "AllOnes", 126 | // localGates, 127 | // false)); 128 | // onesThread.Start(); 129 | 130 | Thread additionThread = new Thread(() => BasicResourceTest( 131 | AdditionEstimator.Run, 132 | testSizes, 133 | localControl, 134 | directory + "Addition", 135 | localGates, 136 | false)); 137 | additionThread.Start(); 138 | 139 | Thread additionNoCarryThread = new Thread(() => BasicResourceTest( 140 | AdditionNoCarryEstimator.Run, 141 | testSizes, 142 | localControl, 143 | directory + "Addition-no-carry", 144 | localGates, 145 | false)); 146 | additionNoCarryThread.Start(); 147 | 148 | Thread constantAdditionThread = new Thread(() => BasicResourceTest( 149 | ConstantAdditionEstimator.Run, 150 | testSizes, 151 | localControl, 152 | directory + "Constant-addition", 153 | localGates, 154 | false)); 155 | constantAdditionThread.Start(); 156 | 157 | Thread greaterThanThread = new Thread(() => BasicResourceTest( 158 | GreaterThanEstimator.Run, 159 | testSizes, 160 | localControl, 161 | directory + "Greater-than", 162 | localGates, 163 | false)); 164 | greaterThanThread.Start(); 165 | isControlled = !isControlled; 166 | } 167 | 168 | allGates = !allGates; 169 | } 170 | } 171 | 172 | // Estimates how large an optimal window should be, by iterating through 173 | // all possible window sizes and checking total cost. 174 | // This is an extremely costly estimate to run. 175 | public static void EstimatePointAdditionWindowSizes(int[] testSizes, string directory) 176 | { 177 | // Writes global parameters (cost metric, testable gates) to terminal 178 | DriverParameters.Print(); 179 | 180 | // Guesses 181 | int[] minWindowSizes = { 14, 15, 15, 15, 16, 16, 16 }; 182 | int[] maxWindowSizes = { 18, 18, 18, 18, 20, 20, 20 }; 183 | 184 | System.IO.Directory.CreateDirectory(directory); 185 | 186 | bool allGates = false; 187 | bool isControlled = false; 188 | 189 | var localControl = isControlled; 190 | var localGates = allGates; 191 | Thread lowWidthThread = new Thread(() => ParameterizedResourceTest( 192 | EllipticCurveWindowedPointAdditionLowWidthWindowTest.Run, 193 | testSizes, 194 | localControl, 195 | true, 196 | true, 197 | directory + "Point-addition-windowed-low-width", 198 | localGates, 199 | minWindowSizes, 200 | maxWindowSizes)); 201 | lowWidthThread.Start(); 202 | Thread highWidthThread = new Thread(() => ParameterizedResourceTest( 203 | EllipticCurveWindowedPointAdditionWindowTest.Run, 204 | testSizes, 205 | localControl, 206 | true, 207 | true, 208 | directory + "Point-addition-windowed", 209 | localGates, 210 | minWindowSizes, 211 | maxWindowSizes)); 212 | highWidthThread.Start(); 213 | Thread signedThread = new Thread(() => ParameterizedResourceTest( 214 | EllipticCurveSignedWindowedPointAdditionWindowTest.Run, 215 | testSizes, 216 | localControl, 217 | true, 218 | true, 219 | directory + "Point-addition-windowed-signed", 220 | localGates, 221 | minWindowSizes, 222 | maxWindowSizes)); 223 | signedThread.Start(); 224 | } 225 | 226 | // Estimates cost to look up points for a number of window sizes 227 | // if the points have a specific bitlength. 228 | public static void EstimatePointLookups(int[] testSizes, string directory) 229 | { 230 | var maxWindowSize = 10; 231 | 232 | // Writes global parameters (cost metric, testable gates) to terminal 233 | DriverParameters.Print(); 234 | 235 | // Construct window size maximum and minimum values 236 | // Idea: Minimum value is 1, maximum value is the full 237 | // size, or a value which is too large to reasonably simulate 238 | int[] minWindowSizes = new int[testSizes.Length]; 239 | int[] maxWindowSizes = new int[testSizes.Length]; 240 | for (int i = 0; i < testSizes.Length; i++) 241 | { 242 | minWindowSizes[i] = 1; 243 | maxWindowSizes[i] = Math.Min(testSizes[i], maxWindowSize); // 2^23 should take about 2 hours 244 | } 245 | 246 | System.IO.Directory.CreateDirectory(directory); 247 | 248 | // Loops over whether it counts all all-gates 249 | bool allGates = false; 250 | bool isControlled = false; 251 | for (int j = 0; j < 2; j++) 252 | { 253 | // Creates a new thread for each operation being estimated 254 | var localControl = isControlled; 255 | var localGates = allGates; 256 | Thread lookupThread = new Thread(() => ParameterizedResourceTest( 257 | PointLookUpEstimator.Run, 258 | testSizes, 259 | localControl, 260 | false, 261 | true, 262 | directory + "Point-lookup", 263 | localGates, 264 | minWindowSizes, 265 | maxWindowSizes)); 266 | lookupThread.Start(); 267 | 268 | allGates = !allGates; 269 | } 270 | } 271 | 272 | // Estimates window sizes for modular arithmetic 273 | // See ReadMe 274 | public static void EstimateModularMultiplicationWindowSizes(int[] testSizes, string directory) 275 | { 276 | // Writes global parameters (cost metric, testable gates) to terminal 277 | DriverParameters.Print(); 278 | 279 | // Construct window size maximum and minimum values 280 | // Idea: Minimum value is 0 (no windowing), maximum value is the full 281 | // size, or a value which is too large to reasonably simulate 282 | int[] minWindowSizes = new int[testSizes.Length]; 283 | int[] maxWindowSizes = new int[testSizes.Length]; 284 | for (int i = 0; i < testSizes.Length; i++) 285 | { 286 | minWindowSizes[i] = 0; 287 | maxWindowSizes[i] = Math.Min(testSizes[i], 23); // 2^23 should take about 2 hours 288 | } 289 | 290 | System.IO.Directory.CreateDirectory(directory); 291 | 292 | // Loops over controlled/not and whether it counts all gates 293 | bool allGates = false; 294 | bool isControlled = false; 295 | for (int j = 0; j < 2; j++) 296 | { 297 | for (int i = 0; i < 2; i++) 298 | { 299 | var localControl = isControlled; 300 | var localGates = allGates; 301 | Thread multiplyThread = new Thread(() => ParameterizedResourceTestSingleThreaded( 302 | MontgomeryWindowedMultiplicationWindowTest.Run, 303 | testSizes, 304 | localControl, 305 | false, 306 | directory + "Modular-multiplication-windowed", 307 | localGates, 308 | minWindowSizes, 309 | maxWindowSizes)); 310 | multiplyThread.Start(); 311 | isControlled = !isControlled; 312 | } 313 | 314 | allGates = !allGates; 315 | } 316 | } 317 | 318 | // Estimates modular addition-like operations, which can be reasonable 319 | // estimated for bit sizes over 500 320 | public static void EstimateCheapModularArithmetic(int[] testSizes, string directory) 321 | { 322 | // Writes global parameters (cost metric, testable gates) to terminal 323 | DriverParameters.Print(); 324 | 325 | System.IO.Directory.CreateDirectory(directory); 326 | 327 | // Loops over controlled/not and whether it counts all gates 328 | bool allGates = false; 329 | bool isControlled = false; 330 | for (int j = 0; j < 2; j++) 331 | { 332 | for (int i = 0; i < 2; i++) 333 | { 334 | var localControl = isControlled; 335 | var localGates = allGates; 336 | Thread doubleThread = new Thread(() => BasicResourceTest( 337 | ModularDblEstimator.Run, 338 | testSizes, 339 | localControl, 340 | directory + "Modular-double", 341 | localGates, 342 | false)); 343 | doubleThread.Start(); 344 | Thread additionThread = new Thread(() => BasicResourceTest( 345 | ModularAdditionEstimator.Run, 346 | testSizes, 347 | localControl, 348 | directory + "Modular-addition", 349 | localGates, 350 | false)); 351 | additionThread.Start(); 352 | isControlled = !isControlled; 353 | } 354 | 355 | allGates = !allGates; 356 | } 357 | } 358 | 359 | // "Expensive" modular operations, including square, multiplication, inversion 360 | // Does not check controlled vs. not controlled because the extra cost is so small, 361 | // and the operations are so costly to estimate. 362 | public static void EstimateExpensiveModularArithmetic(int[] testSizes, string directory) 363 | { 364 | // Writes global parameters (cost metric, testable gates) to terminal 365 | DriverParameters.Print(); 366 | 367 | System.IO.Directory.CreateDirectory(directory); 368 | 369 | // Loops over controlled/not and whether it counts all gates 370 | bool allGates = false; 371 | bool isControlled = false; 372 | for (int j = 0; j < 2; j++) 373 | { 374 | var localControl = isControlled; 375 | var localGates = allGates; 376 | Thread multiplyThread = new Thread(() => BasicResourceTest( 377 | MontgomeryMultiplicationEstimator.Run, 378 | testSizes, 379 | localControl, 380 | directory + "Modular-multiplication", 381 | localGates, 382 | true)); 383 | multiplyThread.Start(); 384 | 385 | // This is run as a comparison to the windowed version 386 | Thread multipyNoWindowsThread = new Thread(() => BasicResourceTest( 387 | NonWindowedMontgomeryMultiplicationEstimator.Run, 388 | testSizes, 389 | localControl, 390 | directory + "Modular-multiplication-no-windows", 391 | localGates, 392 | true)); 393 | multipyNoWindowsThread.Start(); 394 | 395 | Thread squareThread = new Thread(() => BasicResourceTest( 396 | MontgomerySquareEstimator.Run, 397 | testSizes, 398 | localControl, 399 | directory + "Modular-squaring", 400 | localGates, 401 | true)); 402 | squareThread.Start(); 403 | Thread invertThread = new Thread(() => BasicResourceTest( 404 | MontgomeryInversionEstimator.Run, 405 | testSizes, 406 | localControl, 407 | directory + "Modular-Inversion", 408 | localGates, 409 | true)); 410 | invertThread.Start(); 411 | Thread divideThread = new Thread(() => BasicResourceTest( 412 | ModularDivisionEstimator.Run, 413 | testSizes, 414 | localControl, 415 | directory + "Modular-division", 416 | localGates, 417 | true)); 418 | divideThread.Start(); 419 | allGates = !allGates; 420 | } 421 | } 422 | 423 | // Checks only signed, windowed point addition. 424 | // Others could be enabled 425 | public static void EstimateEllipticCurveArithmetic(int[] testSizes, string directory) 426 | { 427 | // Writes global parameters (cost metric, testable gates) to terminal 428 | DriverParameters.Print(); 429 | 430 | System.IO.Directory.CreateDirectory(directory); 431 | 432 | // Loops over controlled/not and whether it counts all gates 433 | bool allGates = false; 434 | bool isControlled = false; 435 | for (int j = 0; j < 2; j++) 436 | { 437 | var localControl = isControlled; 438 | var localGates = allGates; 439 | 440 | // Constant point addition is controlled, the others are not, 441 | // because in Shor's algorithm they do not need to be. 442 | // Thread nonWindowedThread = new Thread(() => BasicResourceTest( 443 | // EllipticCurveConstantPointAdditionEstimator.Run, 444 | // testSizes, 445 | // true, 446 | // directory + "Constant-point-addition", 447 | // localGates, 448 | // true)); 449 | // nonWindowedThread.Start(); 450 | // 451 | // Thread windowedThread = new Thread(() => BasicResourceTest( 452 | // EllipticCurveWindowedPointAdditionEstimator.Run, 453 | // testSizes, 454 | // false, 455 | // directory + "Windowed-point-addition", 456 | // localGates, 457 | // true)); 458 | // windowedThread.Start(); 459 | // 460 | // Thread lowWidthThread = new Thread(() => BasicResourceTest( 461 | // EllipticCurveWindowedPointAdditionLowWidthEstimator.Run, 462 | // testSizes, 463 | // false, 464 | // directory + "Windowed-point-addition-low-width", 465 | // localGates, 466 | // true)); 467 | // lowWidthThread.Start(); 468 | 469 | Thread signedThread = new Thread(() => BasicResourceTest( 470 | EllipticCurveSignedWindowedPointAdditionEstimator.Run, 471 | testSizes, 472 | false, 473 | directory + "Windowed-point-addition-signed", 474 | localGates, 475 | true)); 476 | signedThread.Start(); 477 | 478 | // Thread fixedThread = new Thread(() => BasicResourceTest( 479 | // FixedEllipticCurveSignedWindowedPointAdditionEstimator.Run, 480 | // testSizes, 481 | // false, 482 | // directory + "Fixed-modulus-signed", 483 | // localGates, 484 | // true)); 485 | // fixedThread.Start(); 486 | 487 | allGates = !allGates; 488 | } 489 | } 490 | 491 | // Checks only signed, windowed point addition for which there are fixed parameters. 492 | public static void EstimateFixedEllipticCurveArithmetic(int[] testSizes, string directory) 493 | { 494 | // Writes global parameters (cost metric, testable gates) to terminal 495 | DriverParameters.Print(); 496 | 497 | System.IO.Directory.CreateDirectory(directory); 498 | 499 | // Loops over controlled/not and whether it counts all gates 500 | bool allGates = false; 501 | bool isControlled = false; 502 | for (int j = 0; j < 2; j++) 503 | { 504 | var localControl = isControlled; 505 | var localGates = allGates; 506 | Thread fixedThread = new Thread(() => BasicResourceTest( 507 | FixedEllipticCurveSignedWindowedPointAdditionEstimator.Run, 508 | testSizes, 509 | false, 510 | directory + "Fixed-modulus-signed", 511 | localGates, 512 | true)); 513 | fixedThread.Start(); 514 | allGates = !allGates; 515 | } 516 | } 517 | 518 | /// # Summary 519 | /// Returns a trace simulator object that is configured 520 | /// to measure depth, width, and primitive operation count. 521 | /// If `full_depth` is true, then it counts every gate as depth 1; 522 | /// otherwise it only counts T gates 523 | private static QCTraceSimulator GetTraceSimulator(bool full_depth) 524 | { 525 | var config = new QCTraceSimulatorConfiguration(); 526 | config.UseDepthCounter = true; 527 | config.UseWidthCounter = true; 528 | config.UsePrimitiveOperationsCounter = true; 529 | if (full_depth) 530 | { 531 | config.TraceGateTimes[PrimitiveOperationsGroups.CNOT] = 1; 532 | config.TraceGateTimes[PrimitiveOperationsGroups.Measure] = 1; // count all one and 2 qubit measurements as depth 1 533 | config.TraceGateTimes[PrimitiveOperationsGroups.QubitClifford] = 1; // qubit Clifford depth 1 534 | } 535 | 536 | return new QCTraceSimulator(config); 537 | } 538 | 539 | /// # Summary 540 | /// Runs a specified quantum operation with different parameters `ns`, 541 | /// saving the resource estimates as a csv file to a specified location. 542 | /// 543 | /// # Inputs 544 | /// ## runner 545 | /// The quantum operation being tested (must also match the type `Qop`). 546 | /// This operation must take a boolean `isControlled` and an integer parameter 547 | /// ## ns 548 | /// An array of integer parameters. This method will run the quantum operation 549 | /// with each parameter 550 | /// ## isControlled 551 | /// A boolean argument to pass to the quantum operation. The intention is that 552 | /// it tells the operator whether to test a controlled or uncontrolled version. 553 | /// ## filename 554 | /// The filename, including directory, of where to save the results 555 | /// ## full_depth 556 | /// If true, counts all gates as depth 1; if false, only counts T-gates as depth 1, 557 | /// all others as depth 0 558 | private static void BasicResourceTest(RunQop runner, int[] ns, bool isControlled, string filename, bool full_depth, bool isThreaded) 559 | { 560 | if (full_depth) 561 | { 562 | filename += "-all-gates"; 563 | } 564 | 565 | if (isControlled) 566 | { 567 | filename += "-controlled"; 568 | } 569 | 570 | filename += ".csv"; 571 | string estimation = string.Empty; 572 | 573 | // Headers for the table 574 | if (!System.IO.File.Exists(filename)) 575 | { 576 | estimation += " operation, CNOT count, 1-qubit Clifford count, T count, R count, M count, "; 577 | if (full_depth) 578 | { 579 | estimation += "Full depth, "; 580 | } 581 | else 582 | { 583 | estimation += "T depth, "; 584 | } 585 | 586 | estimation += "initial width, extra width, comment, size"; 587 | System.IO.File.WriteAllText(filename, estimation); 588 | } 589 | 590 | // Run the test for every size 591 | ReaderWriterLock locker = new ReaderWriterLock(); 592 | for (int i = 0; i < ns.Length; i++) 593 | { 594 | if (isThreaded) 595 | { 596 | var thisThreadParameter = ns[i]; 597 | Thread oneParameterTest = new Thread(() => SingleResourceTest( 598 | runner, locker, thisThreadParameter, isControlled, filename, full_depth)); 599 | oneParameterTest.Start(); 600 | } 601 | else 602 | { 603 | // Single thread 604 | SingleResourceTest(runner, locker, ns[i], isControlled, filename, full_depth); 605 | } 606 | } 607 | } 608 | 609 | private static void SingleResourceTest(RunQop runner, ReaderWriterLock locker, int n, bool isControlled, string filename, bool full_depth) 610 | { 611 | QCTraceSimulator estimator = GetTraceSimulator(full_depth); // construct simulator object 612 | 613 | // we must generate a new simulator in each round, to clear previous estimates 614 | var res = runner(estimator, n, isControlled).Result; // run test 615 | 616 | // Create string of a row of parameters 617 | string thisCircuitCosts = DisplayCSV.CSV(estimator.ToCSV(), typeof(TypeQop).FullName, false, string.Empty, false, string.Empty); 618 | 619 | // add the row to the string of the csv 620 | thisCircuitCosts += $"{n}"; 621 | try 622 | { 623 | locker.AcquireWriterLock(int.MaxValue); // absurd timeout value 624 | System.IO.File.AppendAllText(filename, thisCircuitCosts); 625 | } 626 | finally 627 | { 628 | locker.ReleaseWriterLock(); 629 | } 630 | } 631 | 632 | /// # Summary 633 | /// Runs a specified quantum operation with different parameters `ns`, 634 | /// saving the resource estimates as a csv file to a specified location. 635 | /// This also runs the operation with a second parameter, which varies 636 | /// between specified minimum and maximum values. It only runs over the 637 | /// second parameter until it minimizes depth and T count. 638 | /// The main purpose is to estimate optimal window sizes for windowed operations. 639 | /// 640 | /// # Inputs 641 | /// ## runner 642 | /// The quantum operation being tested (must also match the type `Qop`). 643 | /// This operation must take a boolean `isControlled` and an integer parameter 644 | /// ## ns 645 | /// An array of integer parameters. This method will run the quantum operation 646 | /// with each parameter 647 | /// ## isControlled 648 | /// A boolean argument to pass to the quantum operation. The intention is that 649 | /// it tells the operator whether to test a controlled or uncontrolled version. 650 | /// ## isAmortized 651 | /// Decides how to select the optimal second parameter. If it's amortized, it divides 652 | /// the resulting cost by the value of the second parameter. This is intended 653 | /// for windowed addition: as the window size increases, we need to do fewer additions. 654 | /// ## filename 655 | /// The filename, including directory, of where to save the results 656 | /// ## full_depth 657 | /// If true, counts all gates as depth 1; if false, only counts T-gates as depth 1, 658 | /// all others as depth 0 659 | /// ## minParameters 660 | /// The minimum value for the second parameter, corresponding to values in ns 661 | /// ## maxParameters 662 | /// The maximum value for the second parameter. 663 | private static void ParameterizedResourceTest( 664 | RunParameterizedQop runner, 665 | int[] ns, 666 | bool isControlled, 667 | bool isOptimized, 668 | bool isAmortized, 669 | string filename, 670 | bool full_depth, 671 | int[] minParameters, 672 | int[] maxParameters) 673 | { 674 | if (full_depth) 675 | { 676 | filename += "-all-gates"; 677 | } 678 | 679 | if (isControlled) 680 | { 681 | filename += "-controlled"; 682 | } 683 | 684 | filename += ".csv"; 685 | 686 | // Create table headers 687 | if (!System.IO.File.Exists(filename)) 688 | { 689 | string estimation = string.Empty; 690 | estimation += " operation, CNOT count, 1-qubit Clifford count, T count, R count, M count, "; 691 | if (full_depth) 692 | { 693 | estimation += "Full depth, "; 694 | } 695 | else 696 | { 697 | estimation += "T depth, "; 698 | } 699 | 700 | estimation += "initial width, extra width, comment, size, parameter"; 701 | System.IO.File.WriteAllText(filename, estimation); 702 | } 703 | 704 | ReaderWriterLock locker = new ReaderWriterLock(); 705 | 706 | for (int i = 0; i < ns.Length; i++) 707 | { 708 | // Local variables to prevent threading issues 709 | var thisThreadProblemSize = ns[i]; 710 | var thisTheadMinParameter = minParameters[i]; 711 | var thisThreadMaxParameter = maxParameters[i]; 712 | 713 | // Starts a thread for each value in ns. 714 | // Each thread will independently search for an optimal size. 715 | if (isOptimized) 716 | { 717 | Thread oneParameterTest = new Thread(() => SingleParameterizedResourceTest( 718 | runner, 719 | locker, 720 | thisThreadProblemSize, 721 | thisTheadMinParameter, 722 | thisThreadMaxParameter, 723 | isControlled, 724 | filename, 725 | full_depth, 726 | isAmortized)); 727 | oneParameterTest.Start(); 728 | } 729 | else 730 | { 731 | for (int j = minParameters[i]; j <= maxParameters[i]; j++) 732 | { 733 | var thisThreadParameter = j; 734 | Thread oneParameterTest = new Thread(() => SingleResourceTestNoCost( 735 | runner, 736 | locker, 737 | thisThreadProblemSize, 738 | thisThreadParameter, 739 | isControlled, 740 | filename, 741 | full_depth)); 742 | oneParameterTest.Start(); 743 | } 744 | } 745 | } 746 | } 747 | 748 | private static void SingleParameterizedResourceTest( 749 | RunParameterizedQop runner, 750 | ReaderWriterLock locker, 751 | int n, 752 | int minParameter, 753 | int maxParameter, 754 | bool isControlled, 755 | string filename, 756 | bool full_depth, 757 | bool isAmortized) 758 | { 759 | // Track best cost 760 | var bestDepth = 9223372036854775807.0; 761 | var bestTGates = 9223372036854775807.0; 762 | 763 | // Iterate through values of the second parameter 764 | for (int j = minParameter; j < maxParameter; j++) 765 | { 766 | QCTraceSimulator estimator = GetTraceSimulator(full_depth); // construct simulator object 767 | 768 | // we must generate a new simulator in each round, to clear previous estimates 769 | var res = runner(estimator, n, isControlled, j).Result; // run test 770 | 771 | // Get results 772 | var roundDepth = estimator.GetMetric(MetricsNames.DepthCounter.Depth); 773 | var roundTGates = estimator.GetMetric(PrimitiveOperationsGroupsNames.T); 774 | 775 | // If amortized, we divide out the cost of this round 776 | if (isAmortized) 777 | { 778 | roundDepth = roundDepth / j; 779 | roundTGates = roundTGates / j; 780 | } 781 | 782 | // Create string of a row of parameters 783 | string thisCircuitCosts = DisplayCSV.CSV(estimator.ToCSV(), typeof(TypeQop).FullName, false, string.Empty, false, string.Empty); 784 | 785 | // add the row to the string of the csv 786 | thisCircuitCosts += $"{n}, {j}"; 787 | try 788 | { 789 | locker.AcquireWriterLock(int.MaxValue); // absurd timeout value 790 | System.IO.File.AppendAllText(filename, thisCircuitCosts); 791 | } 792 | finally 793 | { 794 | locker.ReleaseWriterLock(); 795 | } 796 | 797 | // Breaks if it's reached the minimum in both metrics 798 | // Assumes the metrics are convex 799 | if (roundDepth >= bestDepth && roundTGates >= bestTGates) 800 | { 801 | break; 802 | } 803 | else 804 | { 805 | if (roundDepth < bestDepth) 806 | { 807 | bestDepth = roundDepth; 808 | } 809 | 810 | if (roundTGates < bestTGates) 811 | { 812 | bestTGates = roundTGates; 813 | } 814 | } 815 | } 816 | } 817 | 818 | private static void SingleResourceTestNoCost( 819 | RunParameterizedQop runner, 820 | ReaderWriterLock locker, 821 | int n, 822 | int m, 823 | bool isControlled, 824 | string filename, 825 | bool full_depth) 826 | { 827 | QCTraceSimulator estimator = GetTraceSimulator(full_depth); // construct simulator object 828 | 829 | // we must generate a new simulator in each round, to clear previous estimates 830 | var res = runner(estimator, n, isControlled, m).Result; // run test 831 | 832 | // Get results 833 | 834 | // Create string of a row of parameters 835 | string thisCircuitCosts = DisplayCSV.CSV(estimator.ToCSV(), typeof(TypeQop).FullName, false, string.Empty, false, string.Empty); 836 | 837 | // add the row to the string of the csv 838 | thisCircuitCosts += $"{n}, {m}"; 839 | try 840 | { 841 | locker.AcquireWriterLock(int.MaxValue); // absurd timeout value 842 | System.IO.File.AppendAllText(filename, thisCircuitCosts); 843 | } 844 | finally 845 | { 846 | locker.ReleaseWriterLock(); 847 | } 848 | } 849 | 850 | private static void ParameterizedResourceTestSingleThreaded( 851 | RunParameterizedQop runner, 852 | int[] ns, 853 | bool isControlled, 854 | bool isAmortized, 855 | string filename, 856 | bool full_depth, 857 | int[] minParameters, 858 | int[] maxParameters) 859 | { 860 | if (full_depth) 861 | { 862 | filename += "-all-gates"; 863 | } 864 | 865 | if (isControlled) 866 | { 867 | filename += "-controlled"; 868 | } 869 | 870 | filename += ".csv"; 871 | 872 | // Create table headers if file does not already exist 873 | if (!System.IO.File.Exists(filename)) 874 | { 875 | string estimation = string.Empty; 876 | estimation += " operation, CNOT count, 1-qubit Clifford count, T count, R count, M count, "; 877 | if (full_depth) 878 | { 879 | estimation += "Full depth, "; 880 | } 881 | else 882 | { 883 | estimation += "T depth, "; 884 | } 885 | 886 | estimation += "initial width, extra width, comment, size, parameter"; 887 | System.IO.File.WriteAllText(filename, estimation); 888 | } 889 | 890 | var bestParameter = minParameters[0]; 891 | for (int i = 0; i < ns.Length; i++) 892 | { 893 | // Starts a thread for each value in ns. 894 | // Each thread will independently search for an optimal size. 895 | var bestCost = 9223372036854775807.0; 896 | 897 | // Iterate through values of the second parameter 898 | for (int j = bestParameter; j < maxParameters[i]; j++) 899 | { 900 | QCTraceSimulator estimator = GetTraceSimulator(full_depth); // construct simulator object 901 | 902 | // we must generate a new simulator in each round, to clear previous estimates 903 | var res = runner(estimator, ns[i], isControlled, j).Result; // run test 904 | 905 | // Get results 906 | var roundCost = 0.0; 907 | if (DriverParameters.MinimizeDepthCostMetric) 908 | { // depth 909 | roundCost = estimator.GetMetric(MetricsNames.DepthCounter.Depth); 910 | } 911 | else 912 | { 913 | roundCost = estimator.GetMetric(PrimitiveOperationsGroupsNames.T); 914 | } 915 | 916 | // If amortized, we divide out the cost of this round 917 | if (isAmortized) 918 | { 919 | roundCost = roundCost / j; 920 | } 921 | 922 | // Create string of a row of parameters 923 | string thisCircuitCosts = DisplayCSV.CSV(estimator.ToCSV(), typeof(TypeQop).FullName, false, string.Empty, false, string.Empty); 924 | 925 | // add the row to the string of the csv 926 | thisCircuitCosts += $"{ns[i]}, {j}"; 927 | 928 | System.IO.File.AppendAllText(filename, thisCircuitCosts); 929 | 930 | // Breaks if it's reached the minimum in both metrics 931 | // Assumes the metrics are convex and increasing in n 932 | if (roundCost > bestCost) 933 | { 934 | break; 935 | } 936 | else if (roundCost < bestCost) 937 | { 938 | bestCost = roundCost; 939 | bestParameter = j; 940 | } 941 | } 942 | } 943 | } 944 | } 945 | } 946 | -------------------------------------------------------------------------------- /MicrosoftQuantumCrypto/EllipticCurves.qs: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Corporation. 2 | // Licensed under the MIT license. 3 | 4 | namespace Microsoft.Quantum.Crypto.EllipticCurves { 5 | open Microsoft.Quantum.Arithmetic; 6 | open Microsoft.Quantum.Convert; 7 | open Microsoft.Quantum.Diagnostics; 8 | open Microsoft.Quantum.Intrinsic; 9 | open Microsoft.Quantum.Math; 10 | open Microsoft.Quantum.Crypto.ModularArithmetic; 11 | open Microsoft.Quantum.Crypto.Basics; 12 | open Microsoft.Quantum.Crypto.Arithmetic; 13 | open Microsoft.Quantum.ModularArithmetic.DebugHelpers; 14 | 15 | 16 | 17 | /////////////////////////////////////////////////////////////////////////////////////////////// 18 | ////// /////// 19 | ////// Elliptic Curves /////// 20 | ////// /////// 21 | /////////////////////////////////////////////////////////////////////////////////////////////// 22 | 23 | 24 | /// # Summary 25 | /// A classical data structure for a pseudo-projective elliptic curve 26 | /// point, where the final coordinate z is `true` for all points 27 | /// except the point at infinity. `x`=`y`=0 for the point at infinity. 28 | newtype ECPointClassical = (x : BigInt, y : BigInt, z : Bool, modulus : BigInt); 29 | 30 | /// # Summary 31 | /// A quantum data structure for a non-identity elliptic curve 32 | /// point. 33 | /// The x and y coordinates are assumed to be in Montgomery form 34 | /// for R=2^$ with n qubits. 35 | newtype ECPointMontgomeryForm = (xs : MontModInt, ys : MontModInt); 36 | 37 | 38 | newtype ECCurveWeierstrassClassical = (a : BigInt, b : BigInt, modulus : BigInt); 39 | 40 | function TenBitCurve () : (ECCurveWeierstrassClassical, ECPointClassical, BigInt, String){ 41 | let modulus = 661L; 42 | let a = 3L; 43 | let b = 7L; 44 | let Gx = 474L; 45 | let Gy = 312L; 46 | let order = 665L; 47 | return (ECCurveWeierstrassClassical(a, b, modulus), ECPointClassical(Gx, Gy, true, modulus), order, "10 bit test curve"); 48 | } 49 | 50 | function ThirtyBitCurve () : (ECCurveWeierstrassClassical, ECPointClassical, BigInt, String){ 51 | let modulus = 1027761563L; 52 | let a = 3L; 53 | let b = 7L; 54 | let Gx = 133353543L; 55 | let Gy = 964863024L; 56 | let order = 1027733483L; 57 | return (ECCurveWeierstrassClassical(a, b, modulus), ECPointClassical(Gx, Gy, true, modulus), order, "30 bit test curve"); 58 | } 59 | 60 | function NISTP192 () : (ECCurveWeierstrassClassical, ECPointClassical, BigInt, String){ 61 | let modulus = 6277101735386680763835789423207666416083908700390324961279L; 62 | let b = 0x64210519e59c80e70fa7e9ab72243049feb8deecc146b9b1L; 63 | let a = modulus -3L; 64 | let Gx = 0x188da80eb03090f67cbf20eb43a18800f4ff0afd82ff1012L; 65 | let Gy = 0x07192b95ffc8da78631011ed6b24cdd573f977a11e794811L; 66 | let order = 6277101735386680763835789423176059013767194773182842284081L; 67 | return (ECCurveWeierstrassClassical(a, b, modulus), ECPointClassical(Gx, Gy, true, modulus), order, "NIST P-192"); 68 | } 69 | 70 | function NISTP224 () : (ECCurveWeierstrassClassical, ECPointClassical, BigInt, String){ 71 | let modulus = 2L^224 - 2L^96 + 1L; 72 | let b = 18958286285566608000408668544493926415504680968679321075787234672564L; 73 | let a = modulus - 3L; 74 | let Gx = 0xb70e0cbd6bb4bf7f321390b94a03c1d356c21122343280d6115c1d21L; 75 | let Gy = 0xbd376388b5f723fb4c22dfe6cd4375a05a07476444d5819985007e34L; 76 | let order = 26959946667150639794667015087019625940457807714424391721682722368061L; 77 | return (ECCurveWeierstrassClassical(a, b, modulus), ECPointClassical(Gx, Gy, true, modulus), order, "NIST P-224"); 78 | } 79 | 80 | function NISTP256 () : (ECCurveWeierstrassClassical, ECPointClassical, BigInt, String){ 81 | let modulus = 115792089210356248762697446949407573530086143415290314195533631308867097853951L; 82 | let b = 0x5ac635d8aa3a93e7b3ebbd55769886bc651d06b0cc53b0f63bce3c3e27d2604bL; 83 | let a = modulus - 3L; 84 | let Gx = 0x6b17d1f2e12c4247f8bce6e563a440f277037d812deb33a0f4a13945d898c296L; 85 | let Gy = 0x4fe342e2fe1a7f9b8ee7eb4a7c0f9e162bce33576b315ececbb6406837bf51f5L; 86 | let order = 115792089210356248762697446949407573529996955224135760342422259061068512044369L; 87 | return (ECCurveWeierstrassClassical(a, b, modulus), ECPointClassical(Gx, Gy, true, modulus), order, "NIST P-256"); 88 | } 89 | 90 | function NISTP384 () : (ECCurveWeierstrassClassical, ECPointClassical, BigInt, String){ 91 | let modulus = 39402006196394479212279040100143613805079739270465446667948293404245721771496870329047266088258938001861606973112319L; 92 | let b = 0xb3312fa7e23ee7e4988e056be3f82d19181d9c6efe8141120314088f5013875ac656398d8a2ed19d2a85c8edd3ec2aefL; 93 | let a = modulus - 3L; 94 | let Gx = 0xaa87ca22be8b05378eb1c71ef320ad746e1d3b628ba79b9859f741e082542a385502f25dbf55296c3a545e3872760ab7L; 95 | let Gy = 0x3617de4a96262c6f5d9e98bf9292dc29f8f41dbd289a147ce9da3113b5f0b8c00a60b1ce1d7e819d7a431d7c90ea0e5fL; 96 | let order = 39402006196394479212279040100143613805079739270465446667946905279627659399113263569398956308152294913554433653942643L; 97 | return (ECCurveWeierstrassClassical(a, b, modulus), ECPointClassical(Gx, Gy, true, modulus), order, "NIST P-384"); 98 | } 99 | 100 | function NISTP521 () : (ECCurveWeierstrassClassical, ECPointClassical, BigInt, String){ 101 | let modulus = 6864797660130609714981900799081393217269435300143305409394463459185543183397656052122559640661454554977296311391480858037121987999716643812574028291115057151L; 102 | let b = 0x051953eb9618e1c9a1f929a21a0b68540eea2da725b99b315f3b8b489918ef109e156193951ec7e937b1652c0bd3bb1bf073573df883d2c34f1ef451fd46b503f00L; 103 | let a = modulus - 3L; 104 | let Gx = 0xc6858e06b70404e9cd9e3ecb662395b4429c648139053fb521f828af606b4d3dbaa14b5e77efe75928fe1dc127a2ffa8de3348b3c1856a429bf97e7e31c2e5bd66L; 105 | let Gy =0x11839296a789a3bc0045c8a5fb42c7d1bd998f54449579b446817afbd17273e662c97ee72995ef42640c550b9013fad0761353c7086a272c24088be94769fd16650L; 106 | let order = 6864797660130609714981900799081393217269435300143305409394463459185543183397655394245057746333217197532963996371363321113864768612440380340372808892707005449L; 107 | return (ECCurveWeierstrassClassical(a, b, modulus), ECPointClassical(Gx, Gy, true, modulus), order, "NIST P-521"); 108 | } 109 | 110 | function Secp256k1 () : (ECCurveWeierstrassClassical, ECPointClassical, BigInt, String){ 111 | let modulus = 2L^256 - 2L^32 - 2L^9 - 2L^8 - 2L^7 - 2L^6 - 2L^4 - 1L; 112 | let b = 7L; 113 | let a = 0L; 114 | let Gx = 0x79BE667EF9DCBBAC55A06295CE870B07029BFCDB2DCE28D959F2815B16F81798L; 115 | let Gy = 0x483ADA7726A3C4655DA4FBFC0E1108A8FD17B448A68554199C47D08FFB10D4B8L; 116 | let order = 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEBAAEDCE6AF48A03BBFD25E8CD0364141L; 117 | return (ECCurveWeierstrassClassical(a, b, modulus), ECPointClassical(Gx, Gy, true, modulus), order, "secp256k1"); 118 | } 119 | 120 | function Curve25519 () : (ECCurveWeierstrassClassical, ECPointClassical, BigInt, String) { 121 | //Converts from Montgomery to Weierstrass 122 | let modulus = 2L^255 - 19L; 123 | let aMontgomery = 486662L; 124 | let bMontgomery = 1L; 125 | let xMontgomery = 9L; 126 | let yMontgomery = 14781619447589544791020593568409986887264606134616475288964881837755586237401L; 127 | let threeInverse = InverseModL(3L, modulus); 128 | mutable a = (aMontgomery*aMontgomery * threeInverse) % modulus; 129 | set a = (modulus - 1L - a) % modulus; 130 | mutable b = (aMontgomery * threeInverse) % modulus; 131 | let Gx = (xMontgomery + b) % modulus; 132 | let Gy = yMontgomery; 133 | set b = (modulus - b) % modulus; 134 | set b = (b + 2L * aMontgomery^3 * threeInverse^3) % modulus; 135 | let order = 8L*(2L^252 + 27742317777372353535851937790883648493L) ; 136 | return (ECCurveWeierstrassClassical(a, b, modulus), ECPointClassical(Gx, Gy, true, modulus), order, "curve25519"); 137 | } 138 | 139 | /// # Summary 140 | /// Adds two classical elliptic curve points and returns the result. 141 | /// 142 | /// # Inputs 143 | /// ## point1 144 | /// The first point on the curve. 145 | /// ## point 2 146 | /// The second point on the curve 147 | /// ## curvea 148 | /// The coefficient a in Weierstrass form, where 149 | /// the curve is $y^2=x^3+ax+b$. 150 | /// 151 | /// # Output 152 | /// An ECPointClassical point containing point1+point2. 153 | function AddClassicalECPoint(point1 : ECPointClassical, point2 : ECPointClassical, curvea : BigInt) : ECPointClassical { 154 | let modulus = point1::modulus; 155 | mutable lambda = 0L; 156 | if (not point1::z){ 157 | return point2; 158 | } 159 | if (not point2::z){ 160 | return point1; 161 | } 162 | if (point1::x==point2::x){ 163 | if ((point1::y+point2::y)%modulus == 0L ){ 164 | return ECPointClassical(0L, 0L, false, modulus); 165 | } 166 | set lambda = ((3L * point1::x * point1::x + curvea) * InverseModL(2L * point1::y, modulus))%modulus; 167 | } else { 168 | set lambda = ((point1::y - point2::y) * InverseModL(point1::x - point2::x, modulus))%modulus; 169 | } 170 | let newx = (lambda ^ 2 - point1::x - point2::x)%modulus; 171 | let newy = (lambda * (point1::x - newx) - point1::y)%modulus; 172 | if (newx<0L){ 173 | if (newy<0L){ 174 | return ECPointClassical(newx+modulus, newy + modulus, true, modulus); 175 | } else { 176 | return ECPointClassical(newx+modulus, newy, true, modulus); 177 | } 178 | } elif (newy<0L) { 179 | return ECPointClassical(newx, newy + modulus, true, modulus); 180 | } 181 | return ECPointClassical(newx, newy, true, modulus); 182 | } 183 | 184 | function MultiplyClassicalECPoint(point : ECPointClassical, curve : ECCurveWeierstrassClassical, coefficient : BigInt) : 185 | ECPointClassical { 186 | mutable outputPoint = ECPointClassical(0L, 0L, false, curve::modulus); 187 | let nBits = BitSizeL(coefficient); 188 | mutable remainingCoefficient = coefficient; 189 | mutable doubledPoint = point; 190 | let coefficientBools = BigIntAsBoolArray(coefficient); 191 | for idx in 0 .. Length(coefficientBools) - 1 { 192 | if (coefficientBools[idx]){ 193 | set outputPoint = AddClassicalECPoint(outputPoint, doubledPoint, curve::a); 194 | } 195 | set doubledPoint = AddClassicalECPoint(doubledPoint, doubledPoint, curve::a); 196 | } 197 | return outputPoint; 198 | } 199 | 200 | 201 | function PointTable( 202 | generator : ECPointClassical, 203 | initialPoint : ECPointClassical, 204 | curve : ECCurveWeierstrassClassical, 205 | tableSize : Int 206 | ) : ECPointClassical[] { 207 | if (tableSize == 1){ 208 | return [ 209 | initialPoint, 210 | AddClassicalECPoint(initialPoint, generator, curve::a) 211 | ]; 212 | } else { 213 | let points = PointTable(generator, initialPoint, curve, tableSize - 1); 214 | return points + PointTable( 215 | generator, 216 | AddClassicalECPoint(generator, points[2^(tableSize-1)-1],curve::a), 217 | curve, 218 | tableSize - 1 219 | ); 220 | } 221 | } 222 | 223 | 224 | 225 | /// # Summary 226 | /// Takes a classical elliptic curve point and quantum registers 227 | /// and encodes the point into the quantum registers in Montgomery form 228 | /// with multiply 2^n (where n is the number of qubits). 229 | /// 230 | /// # Inputs 231 | /// ## point 232 | /// A classical elliptic curve point. 233 | /// ## register 234 | /// Qubit register assumed to be all 0s. 235 | /// 236 | /// # Output 237 | /// ## ECPointMontgomeryForm 238 | /// References the qubit reigster containing the newly created 239 | /// elliptic curve point. 240 | /// 241 | /// # Remarks 242 | /// Inernally divides `register` into x and y components 243 | operation ClassicalECPointToQuantum(point : ECPointClassical, register : Qubit[]) : ECPointMontgomeryForm { 244 | body (...){ 245 | Fact(point::z, "Cannot encode the identity point into quantum registers"); 246 | let nQubits = BitSizeL(point::modulus); 247 | Fact(2 * nQubits <= Length(register), 248 | $"Points on a curve over {point::modulus} need at least {2 * nQubits} qubits, 249 | only {Length(register)} given" 250 | ); 251 | let ys = LittleEndian(register[0..nQubits - 1]); 252 | let xs = LittleEndian(register[nQubits..2 * nQubits - 1]); 253 | let yMont = CreateBigIntInMontgomeryForm(point::modulus, point::y, ys); 254 | let xMont = CreateBigIntInMontgomeryForm(point::modulus, point::x, xs); 255 | return ECPointMontgomeryForm(xMont, yMont); 256 | } 257 | } 258 | 259 | /// # Summary 260 | /// Writes a classical point to a quantum register, which is assumed to be 0. 261 | /// Encodes the point values into Montgomery form. The quantum register 262 | /// must be formatted to lie on the same curve as the input point. 263 | /// 264 | /// # Inputs 265 | /// ## inputPoint 266 | /// Classical point to write 267 | /// ## outputPoint 268 | /// Quantum register to write into 269 | operation EncodeClassicalECPointInQuantum(inputPoint : ECPointClassical, outputPoint : ECPointMontgomeryForm) : Unit { 270 | body (...) { 271 | (Controlled EncodeClassicalECPointInQuantum)([], (inputPoint, outputPoint)); 272 | } 273 | controlled (controls, ...){ 274 | Fact(inputPoint::modulus == outputPoint::xs::modulus, 275 | $"Points are defined over different moduli: {inputPoint::modulus} for 276 | the classical point but {outputPoint::xs::modulus} for the quantum point" 277 | ); 278 | (Controlled EncodeBigIntInMontgomeryForm)(controls, (inputPoint::x, outputPoint::xs)); 279 | (Controlled EncodeBigIntInMontgomeryForm)(controls, (inputPoint::y, outputPoint::ys)); 280 | } 281 | adjoint controlled auto; 282 | } 283 | 284 | /// # Summary 285 | /// Measures a quantum elliptic curve point and 286 | /// returns the result as a classical point, 287 | /// decoding the Montgomery encoding and resetting the 288 | /// qubits to 0. 289 | /// 290 | /// # Inputs 291 | /// ## point 292 | /// A quantum elliptic curve point. 293 | /// 294 | /// # Output 295 | /// ## ECPointClassical 296 | /// Classical elliptic curve point with the measured 297 | /// value. 298 | operation MeasureECPoint(point : ECPointMontgomeryForm) : ECPointClassical { 299 | let modulus = point::xs::modulus; 300 | let nQubits = Length(point::xs::register!); 301 | let montR = ModPowL(2L, IntAsBigInt(nQubits), modulus); 302 | let montInv= InverseModL(montR, point::xs::modulus); 303 | let x = MeasureMontgomeryInteger(point::xs); 304 | let y = MeasureMontgomeryInteger(point::ys); 305 | return ECPointClassical(x, y, true, modulus); 306 | } 307 | 308 | 309 | /// # Summary 310 | /// Given an x-coordinate of an elliptic curve and the 311 | /// parameters of the curve, returns an elliptic curve point 312 | /// with that x-coordinate if it exists; otherwise it 313 | /// returns the point at infinity. 314 | /// 315 | /// # Inputs 316 | /// ## x 317 | /// The x-coordinate of the desired point. 318 | /// ## a 319 | /// The value of a of the Weierstrass form of the curve : 320 | /// $y^2=x^3+ax+b$. 321 | /// ## b 322 | /// The value of b in the Weierstrass form. 323 | /// ## modulus 324 | /// The prime modulus of the curve. 325 | /// ## sign 326 | /// If true, it flips the sign of the computed y-value of the coordinate 327 | /// 328 | /// # Outputs 329 | /// An ECPointClassical containing the new point. 330 | /// 331 | /// # Remarks 332 | /// To compute the square root, this function exponentiates, which 333 | /// only returns the square root if modulus=3 mod 4. 334 | function GetECPoint(x : BigInt, a : BigInt, b : BigInt, modulus : BigInt, sign : Bool) : ECPointClassical { 335 | Fact((modulus) % 4L == 3L, "Implement Tonelli-Shanks if you want to use p!=3 mod 4"); 336 | let ysq= (x^3 + a*x + b)%modulus; 337 | let y = ModPowL(ysq, (modulus + 1L) / 4L, modulus); 338 | //test if y is the squre root 339 | let ysqtest = (y ^ 2) % modulus; 340 | if (not (ysqtest == ysq)){ 341 | return ECPointClassical(0L, 0L, false, modulus); 342 | } 343 | if (sign){ 344 | return ECPointClassical(x, (modulus - y) % modulus, true, modulus); 345 | } else { 346 | return ECPointClassical(x, y, true, modulus); 347 | } 348 | } 349 | 350 | /// # Summary 351 | /// Returns a random point on an elliptic curve y^2 = x^3 + ax + b such that it is not 352 | /// the identity point 353 | /// 354 | /// # Inputs 355 | /// ## a 356 | /// The coefficient a of the curve 357 | /// ## b 358 | /// The coefficient b of the curve 359 | /// ## modulus 360 | /// The prime defining the field of the curve 361 | operation RandomNonInfinityECPoint(a : BigInt, b : BigInt, modulus : BigInt) : ECPointClassical { 362 | mutable point = ECPointClassical(0L, 0L, false, modulus); 363 | repeat { 364 | let signInt = Microsoft.Quantum.Random.DrawRandomInt(0,1); 365 | if (signInt == 0){ 366 | set point = GetECPoint(RandomBigInt(modulus), a, b, modulus, false); 367 | } else { 368 | set point = GetECPoint(RandomBigInt(modulus), a, b, modulus, true); 369 | } 370 | } until (point::z); 371 | return point; 372 | } 373 | 374 | /// # Summary 375 | /// Adds two distinct quantum elliptic curve points in place, 376 | /// saving the new point to the second register. 377 | /// 378 | /// # Inputs 379 | /// ## point1 380 | /// The first point, which will be returned unchanged. 381 | /// ## point2 382 | /// The second point which will be replaced with the sum. 383 | /// 384 | /// # Reference 385 | /// Martin Roetteler, Michael Naehrig, Krysta M. Svore, Kristin Lauter : 386 | /// Quantum Resource Estimates for Computing Elliptic Curve Discrete Logarithms 387 | /// https : //eprint.iacr.org/2017/598 388 | /// 389 | /// # Remarks 390 | /// This operation only works correctly under the following conditions : 391 | /// * `point2` does not equal `point1` 392 | /// * `point2` does not equal -`point1` 393 | /// * `point1`+`point2` does not equal `-point1 394 | operation DistinctEllipticCurvePointAddition(point1 : ECPointMontgomeryForm, point2 : ECPointMontgomeryForm) : Unit { 395 | body (...){ 396 | (Controlled DistinctEllipticCurvePointAddition)([], (point1, point2)); 397 | } 398 | controlled (controls, ...){ 399 | let nQubits = Length(point1::xs::register!); 400 | let modulus = point1::xs::modulus; 401 | // Set the second point to x2-x1 and y2-y1 402 | (Adjoint ModularAddMontgomeryForm)(point1::xs, point2::xs); 403 | (Adjoint Controlled ModularAddMontgomeryForm)(controls, (point1::ys, point2::ys)); 404 | 405 | use lambdaqubits = Qubit[nQubits] { 406 | let lambdas = MontModInt(modulus, LittleEndian(lambdaqubits)); 407 | //Computes lambda in a new register 408 | (Controlled ModularDivideAndAddMontgomeryForm)(controls, (point2::xs, point2::ys, lambdas)); 409 | //Clears y2-y1 using lambda and x2-x1 410 | ModularMulAndXorMontgomeryForm(point2::xs, lambdas, point2::ys); 411 | //Adds 3x1 to x2-x1 to get x2+2x1 412 | ModularAddMontgomeryForm(point1::xs, point2::xs); 413 | ModularDblMontgomeryForm(point1::xs); 414 | ModularAddMontgomeryForm(point1::xs, point2::xs); 415 | //Adds lambda^2 to x2+2x1 to get x1-x3 416 | (Adjoint ModularSquMontgomeryFormWindowedGeneric)(ModularAddMontgomeryForm(_, point2::xs), lambdas); 417 | //Multiplies lambda by x1-x3 and adds the result to the (empty) y register 418 | ModularMulAndAddMontgomeryForm(point2::xs, lambdas, point2::ys); 419 | //Uncomputes lambda 420 | (Adjoint Controlled ModularDivideAndAddMontgomeryForm)(controls, (point2::xs, point2::ys, lambdas)); 421 | } 422 | //Subtracts 2x1 from the x register 423 | (Adjoint ModularAddMontgomeryForm)(point1::xs, point2::xs); 424 | //Halves x1 425 | (Adjoint ModularDblMontgomeryForm)(point1::xs); 426 | //Subtracts y1 from the y register to get the new y 427 | (Adjoint Controlled ModularAddMontgomeryForm)(controls, (point1::ys, point2::ys)); 428 | //Contralled add x1 to the x register and negates 429 | (Controlled ModularAddMontgomeryForm)(controls, (point1::xs, point2::xs)); 430 | (Controlled ModularNegMontgomeryForm)(controls, (point2::xs)); 431 | } 432 | controlled adjoint auto; 433 | } 434 | 435 | /// # Summary 436 | /// Adds a fixed, classical elliptic curve point to 437 | /// to a quantum point, replacing the value in the 438 | /// quantum registers. 439 | /// 440 | /// # Inputs 441 | /// ## point1 442 | /// The classical point. 443 | /// ## point2 444 | /// The quantum point which will be replaced with the sum. 445 | /// 446 | /// # Reference 447 | /// Martin Roetteler, Michael Naehrig, Krysta M. Svore, Kristin Lauter : 448 | /// Quantum Resource Estimates for Computing Elliptic Curve Discrete Logarithms 449 | /// https : //eprint.iacr.org/2017/598 450 | /// 451 | /// # Remarks 452 | /// This operation only works correctly under the following conditions : 453 | /// * `point2` does not equal `point1` 454 | /// * `point2` does not equal -`point1` 455 | /// * `point1`+`point2` does not equal `-point1` 456 | operation DistinctEllipticCurveClassicalPointAddition(point1 : ECPointClassical, point2 : ECPointMontgomeryForm) : Unit { 457 | body (...){ 458 | (Controlled DistinctEllipticCurveClassicalPointAddition)([], (point1, point2)); 459 | } 460 | controlled (controls, ...){ 461 | if (point1::z){ 462 | let nQubits = Length(point2::xs::register!); 463 | let modulus = point1::modulus; 464 | // Set the second point to x2-x1 and y2-y1 465 | (Adjoint ModularAddConstantMontgomeryForm)(point1::x, point2::xs); 466 | (Adjoint Controlled ModularAddConstantMontgomeryForm)(controls, (point1::y, point2::ys)); 467 | use lambdaqubits = Qubit[nQubits] { 468 | let lambdas = MontModInt(modulus, LittleEndian(lambdaqubits)); 469 | (Controlled ModularDivideAndAddMontgomeryForm)(controls, (point2::xs, point2::ys, lambdas)); 470 | //Clears y2-y1 using lambda and x2-x1 471 | ModularMulAndXorMontgomeryForm(point2::xs, lambdas, point2::ys); 472 | //Adds 3x1 to x2-x1 to get x2+2x1 473 | ModularAddConstantMontgomeryForm((3L * point1::x) % modulus, point2::xs); 474 | //Adds lambda^2 to x2+2x1 to get x1-x3 475 | (Adjoint ModularSquMontgomeryFormWindowedGeneric)(ModularAddMontgomeryForm(_, point2::xs), lambdas); 476 | //Multiplies lambda by x1-x3 and adds the result to the (empty) y register 477 | ModularMulAndAddMontgomeryForm(point2::xs, lambdas, point2::ys); 478 | //Uncomputes lambda 479 | (Adjoint Controlled ModularDivideAndAddMontgomeryForm)(controls, (point2::xs, point2::ys, lambdas)); 480 | } 481 | //Adds y1 to the y register to get the new y 482 | (Adjoint Controlled ModularAddConstantMontgomeryForm)(controls, (point1::y, point2::ys)); 483 | //Subtracts x1 from the x register and negates 484 | //Here we need to subtract 2x1 if the addition was controlled, but only one x1 if not controlled 485 | //So we subtract 2x1 first, then add back x1 with the controls 486 | (Adjoint ModularAddConstantMontgomeryForm)((2L * point1::x) % modulus, point2::xs); 487 | (Controlled ModularAddConstantMontgomeryForm)(controls, (point1::x, point2::xs)); 488 | (Controlled ModularNegMontgomeryForm)(controls, (point2::xs)); 489 | } 490 | } 491 | controlled adjoint auto; 492 | } 493 | 494 | /// # Summary 495 | /// Writes a classical point to an elliptic curve, including an extra qubit to represent the z-cooordinate 496 | /// 497 | /// # Inputs 498 | /// ## inputPoint 499 | /// Classical point to write 500 | /// ## outputPoint 501 | /// Qubit register to write into (assumed to be zeros) 502 | /// ## outputZ 503 | /// A qubit representing whether the point is the identity (|0>) or not (|1>) 504 | /// 505 | /// # Remarks 506 | /// This has nearly identical function to ClassicalECPointToQuantum, except for the z qubit, 507 | /// as this is intended for use in windowed point addition 508 | operation ECPointWrite (inputPoint : ECPointClassical, outputPoint : ECPointMontgomeryForm, outputZ : Qubit) : Unit { 509 | body (...){ 510 | (Controlled ECPointWrite)([], (inputPoint, outputPoint, outputZ)); 511 | } 512 | controlled (controls, ...){ 513 | let modulus = inputPoint::modulus; 514 | let nQubits = BitSizeL(modulus); 515 | // Message($"Modulus: {modulus}, nqubits : {nQubits}"); 516 | // Fact(Length(register) >= 2 * nQubits + 1, $"Not enough qubits to write a point; need 2*{nQubits}+1, only has {Length(register)}"); 517 | // if (Length(register) > 2 * nQubits + 1){ 518 | // Message($"Warning: too many qubits for an elliptic curve point (given {Length(register)}, needed {2 * nQubits + 1})"); 519 | // } 520 | if (inputPoint::z){ 521 | //point is the identity: just write the bonus qubit 522 | CheckIfAllOnes(controls, outputZ); 523 | } 524 | (Controlled EncodeBigIntInMontgomeryForm)(controls, (inputPoint::y, outputPoint::ys)); 525 | (Controlled EncodeBigIntInMontgomeryForm)(controls, (inputPoint::x, outputPoint::xs)); 526 | } 527 | controlled adjoint auto; 528 | } 529 | 530 | /// # Summary 531 | /// Encodes an array of classical elliptic curve points into ancilla register according to 532 | /// an address register, than adds that register (possibly in superposition) all to a quantum point. 533 | /// This has the effect of entangling the address with the final point sum. 534 | /// 535 | /// # Inputs 536 | /// ## points 537 | /// Array of classical points that will be indexed. 538 | /// ## address 539 | /// Quantum register containing the address of the point(s) to add 540 | /// ## point 541 | /// The quantum register which will have a point added to it, in place. 542 | /// 543 | /// # Remarks 544 | /// The intent here is that if we want to add many points into a register in superposition, 545 | /// we can save by "filling up" an ancilla register with the superposition, then adding all 546 | /// these points "at once" with a single fully-quantum point addition. 547 | operation WindowedEllipticCurvePointAddition(points : ECPointClassical[], address : LittleEndian, point : ECPointMontgomeryForm) : Unit { 548 | body (...){ 549 | (Controlled WindowedEllipticCurvePointAddition)([], (points, address, point)); 550 | } 551 | controlled (controls, ...){ 552 | let modulus = point::xs::modulus; 553 | let nQubits = Length(point::xs::register!); 554 | 555 | use ancillaPointQubits = Qubit[2 * nQubits + 1] { 556 | //Format new qubits into a point 557 | let ancillaPoint = ECPointMontgomeryForm( 558 | MontModInt(modulus, LittleEndian(ancillaPointQubits[0..nQubits - 1])), 559 | MontModInt(modulus, LittleEndian(ancillaPointQubits[nQubits .. 2 * nQubits - 1])) 560 | ); 561 | let zQubit = ancillaPointQubits[2 * nQubits]; 562 | //Write the points in 563 | (Controlled EqualLookup)(controls, (points, ECPointWrite(_, ancillaPoint, zQubit), address)); 564 | //Do the addition (in superposition) 565 | (Controlled DistinctEllipticCurvePointAddition)([zQubit], (ancillaPoint, point)); 566 | //Erase the ancilla point 567 | //Write the points in 568 | (Controlled Adjoint EqualLookup)(controls, (points, ECPointWrite(_,ancillaPoint, zQubit), address)); 569 | } 570 | } 571 | controlled adjoint auto; 572 | } 573 | 574 | 575 | 576 | 577 | /// # Summary 578 | /// Writes just the x-coordinate of an elliptic curve times 3, 579 | /// modulo the prime, into a quantum register. 580 | /// Used for the middle step of windowed point addition as part of the 581 | /// QRAM look-up. 582 | /// 583 | /// # Inputs 584 | /// ## point 585 | /// A classical elliptic curve point (x,y) 586 | /// ## xs 587 | /// Quantum register which should be written as 3x 588 | /// 589 | /// # Remarks 590 | /// To avoid errors in a blank table, this does nothing with a "blank" 591 | /// classical point (one where the modulus is 0) 592 | operation _ClassicalECPointFormat(point : ECPointClassical, xs : MontModInt) : Unit { 593 | body (...) { 594 | if (point::modulus > 0L){ 595 | let xNew = (point::x * 3L) % point::modulus; 596 | EncodeBigIntInMontgomeryForm(xNew, xs); 597 | } 598 | } 599 | controlled adjoint auto; 600 | } 601 | 602 | function OptimalPointAdditionWindowSize(nQubits : Int) : Int { 603 | return Min([nQubits, 8]); 604 | // wild guess 605 | // let logn = Ceiling(Log(IntAsDouble(nQubits)) / Log(2.0)); 606 | // let loglogn = Ceiling(Log(IntAsDouble(logn)) / Log(2.0)); 607 | // return Min([logn + loglogn + 9, nQubits]); 608 | } 609 | 610 | /// # Summary 611 | /// Uses an address register to "look up" which classical point to add 612 | /// to a quantum point, then does that addition. 613 | /// To avoid extra registers, this operation does a "QRAM lookup" that 614 | /// adds the classical points directly into the quantum registers. 615 | /// 616 | /// # Inputs 617 | /// ## points 618 | /// Array of classical points that will be indexed. 619 | /// ## address 620 | /// Quantum register containing the address of the point(s) to add 621 | /// ## point 622 | /// The quantum register which will have a point added to it, in place. 623 | /// 624 | /// # Remarks 625 | /// This should have the same action as `WindowedEllipticCurvePointAddition`, 626 | /// but should use fewer qubits. 627 | operation WindowedEllipticCurvePointAdditionLowWidth(points : ECPointClassical[], address : LittleEndian, point : ECPointMontgomeryForm) : Unit { 628 | body (...){ 629 | (Controlled WindowedEllipticCurvePointAdditionLowWidth)([], (points, address, point)); 630 | } 631 | controlled (controls, ...){ 632 | let modulus = point::xs::modulus; 633 | let nQubits = Length(point::xs::register!); 634 | use zQubit = Qubit() { 635 | // Set the second point to x2-x1 and y2-y1 636 | // This requires a QRAM look-up 637 | // Here we also set the zQubit (which controls other operations) 638 | use ancillaPointQubits = Qubit[2 * nQubits + 1] { 639 | let ancillaPoint = ECPointMontgomeryForm( 640 | MontModInt(modulus, LittleEndian(ancillaPointQubits[0..nQubits - 1])), 641 | MontModInt(modulus, LittleEndian(ancillaPointQubits[nQubits .. 2 * nQubits - 1])) 642 | ); 643 | let ancillaZ = ancillaPointQubits[2 * nQubits]; 644 | (Controlled EqualLookup)(controls, (points, ECPointWrite(_, ancillaPoint, ancillaZ), address)); 645 | (Controlled Adjoint ModularAddMontgomeryForm)(controls, (ancillaPoint::xs, point::xs)); 646 | (Controlled Adjoint ModularAddMontgomeryForm)(controls, (ancillaPoint::ys, point::ys)); 647 | (Controlled CNOT)(controls, (ancillaZ, zQubit)); 648 | (Controlled Adjoint EqualLookup)(controls, (points, ECPointWrite(_, ancillaPoint, ancillaZ), address)); 649 | } 650 | use lambdaqubits = Qubit[nQubits] { 651 | let lambdas = MontModInt(modulus, LittleEndian(lambdaqubits)); 652 | (Controlled ModularDivideAndAddMontgomeryForm)(controls + [zQubit], (point::xs, point::ys, lambdas)); 653 | //Clears y2-y1 using lambda and x2-x1 654 | ModularMulAndXorMontgomeryForm(point::xs, lambdas, point::ys); 655 | //Adds 3x1 to x2-x1 to get x2+2x1 656 | //This needs another QRAM look-up 657 | use ancillaPointQubits = Qubit[nQubits] { 658 | let xsMMI = MontModInt(modulus, LittleEndian(ancillaPointQubits[0..nQubits - 1])); 659 | (Controlled EqualLookup)(controls, (points, _ClassicalECPointFormat(_, xsMMI), address)); 660 | (Controlled ModularAddMontgomeryForm)(controls, (xsMMI, point::xs)); 661 | (Controlled Adjoint EqualLookup)(controls, (points, _ClassicalECPointFormat(_, xsMMI), address)); 662 | } 663 | //Adds lambda^2 to x2+2x1 to get x1-x3 664 | (Adjoint ModularSquMontgomeryFormWindowedGeneric)(ModularAddMontgomeryForm(_, point::xs), lambdas); 665 | //Multiplies lambda by x1-x3 and adds the result to the (empty) y register 666 | ModularMulAndAddMontgomeryForm(point::xs, lambdas, point::ys); 667 | //Uncomputes lambda 668 | (Adjoint Controlled ModularDivideAndAddMontgomeryForm)(controls + [zQubit], (point::xs, point::ys, lambdas)); 669 | } 670 | // Adds all required constants in one go 671 | use ancillaPointQubits = Qubit[2 * nQubits + 1] { 672 | let ancillaPoint = ECPointMontgomeryForm( 673 | MontModInt(modulus, LittleEndian(ancillaPointQubits[0..nQubits - 1])), 674 | MontModInt(modulus, LittleEndian(ancillaPointQubits[nQubits .. 2 * nQubits - 1])) 675 | ); 676 | let ancillaZ = ancillaPointQubits[2 * nQubits]; 677 | (Controlled EqualLookup)(controls, (points, ECPointWrite(_, ancillaPoint, ancillaZ), address)); 678 | // While the point is in memory: We want to add it as necessary, and control the negation 679 | // of x based on the z qubit, which will be uncomputed just after 680 | (Controlled Adjoint ModularAddMontgomeryForm)(controls, (ancillaPoint::xs, point::xs)); 681 | (Controlled Adjoint ModularAddMontgomeryForm)(controls, (ancillaPoint::ys, point::ys)); 682 | (Controlled ModularNegMontgomeryForm)(controls + [zQubit], (point::xs)); 683 | (Controlled CNOT)(controls, (ancillaZ, zQubit)); 684 | (Controlled Adjoint EqualLookup)(controls, (points, ECPointWrite(_, ancillaPoint, ancillaZ), address)); 685 | } 686 | } 687 | } 688 | controlled adjoint auto; 689 | } 690 | 691 | function OptimalSignedPointAdditionWindowSize(nQubits : Int) : Int { 692 | return Min([nQubits, 8]); 693 | // wild guess 694 | // let logn = Ceiling(Log(IntAsDouble(nQubits)) / Log(2.0)); 695 | // let loglogn = Ceiling(Log(IntAsDouble(logn)) / Log(2.0)); 696 | // return Min([logn + loglogn + 10, nQubits]); 697 | } 698 | 699 | /// # Summary 700 | /// Adds a point from a classical array of points to a quantum register. 701 | /// The classical point is indexed by a quantum address register, where the 702 | /// last qubit of the address is a "sign" bit that, if zero, will flip the sign 703 | /// of the point being added and flip all bits of the address (i.e., to 704 | /// address the points in reverse order) 705 | /// 706 | /// # Inputs 707 | /// ## points 708 | /// An array of classical points, assumed to not contain the identity 709 | /// ## address 710 | /// Address qubits, where the last qubit is the sign bit 711 | /// ## point 712 | /// Quantum register to add the point into 713 | /// 714 | /// # Remarks 715 | /// The intended use is that the point array contains [P,2P,...,2^kP] 716 | /// for a (k+1)-bit address. With an address of b=(b',b0), we look up 717 | /// the point b'P (if b0=1), and if b0=0, we look up the point (2^k-b')P 718 | /// and flip it to (b'-2^k)P. The overall effect is that we end up adding 719 | /// [b - 2^k]P to the register. 720 | operation SignedWindowedEllipticCurvePointAdditionLowWidth(points : ECPointClassical[], address : Qubit[], point : ECPointMontgomeryForm) : Unit { 721 | body (...){ 722 | (Controlled SignedWindowedEllipticCurvePointAdditionLowWidth)([], (points, address, point)); 723 | } 724 | controlled (controls, ...){ 725 | use addressAncilla = Qubit() { 726 | let addressLength = Length(address); 727 | let unsignedAddress = address[0 .. addressLength - 2]; 728 | let leftAddress = LittleEndian(unsignedAddress + [addressAncilla]); 729 | let signQubit = address[addressLength - 1]; 730 | // we want negative numbers when signQubit = 0 731 | X(signQubit); 732 | let modulus = point::xs::modulus; 733 | let nQubits = Length(point::xs::register!); 734 | // This flips the ordering of addresses when the sign is negative 735 | // And increments them 736 | (Controlled ApplyToEachWrapperCA)([signQubit], (X, unsignedAddress)); 737 | (Controlled Increment)([signQubit], (leftAddress)); 738 | // Set the second point to x2-x1 and y2-y1 739 | // This requires a QRAM look-up 740 | // Here we also set the zQubit (which controls other operations) 741 | if (nQubits > 50){Message($"{nQubits}-bit modulus: First look-up");} 742 | use zQubit = Qubit() { 743 | use ancillaPointQubits = Qubit[2 * nQubits] { 744 | let ancillaPoint = ECPointMontgomeryForm( 745 | MontModInt(modulus, LittleEndian(ancillaPointQubits[0..nQubits - 1])), 746 | MontModInt(modulus, LittleEndian(ancillaPointQubits[nQubits .. 2 * nQubits - 1])) 747 | ); 748 | //Look up the point 749 | (Controlled EqualLookup)(controls, (points, ECPointWrite(_, ancillaPoint, zQubit), leftAddress)); 750 | // Negate the point if the sign bit says so 751 | (Controlled ModularNegMontgomeryForm)([signQubit], (ancillaPoint::ys)); 752 | // Add into the register 753 | (Controlled Adjoint ModularAddMontgomeryForm)(controls, (ancillaPoint::xs, point::xs)); 754 | (Controlled Adjoint ModularAddMontgomeryForm)(controls, (ancillaPoint::ys, point::ys)); 755 | // Clear 756 | (Controlled ModularNegMontgomeryForm)([signQubit], (ancillaPoint::ys)); 757 | (Controlled Adjoint EqualLookup)(controls, (points, EncodeClassicalECPointInQuantum(_, ancillaPoint), leftAddress)); 758 | } 759 | use lambdaqubits = Qubit[nQubits] { 760 | let lambdas = MontModInt(modulus, LittleEndian(lambdaqubits)); 761 | if (nQubits > 50){Message($"{nQubits}-bit modulus: First division");} 762 | (Controlled ModularDivideAndAddMontgomeryForm)(controls + [zQubit], (point::xs, point::ys, lambdas)); 763 | //Clears y2-y1 using lambda and x2-x1 764 | if (nQubits > 50){Message($"{nQubits}-bit modulus: First multiplication");} 765 | ModularMulAndXorMontgomeryForm(point::xs, lambdas, point::ys); 766 | //Adds 3x1 to x2-x1 to get x2+2x1 767 | //This needs another QRAM look-up 768 | if (nQubits > 50){Message($"{nQubits}-bit modulus: Second look-up");} 769 | use ancillaPointQubits = Qubit[nQubits] { 770 | let xsMMI = MontModInt(modulus, LittleEndian(ancillaPointQubits[0..nQubits - 1])); 771 | // Look up 3*(x-value) of the classical point 772 | (Controlled EqualLookup)(controls, (points, _ClassicalECPointFormat(_, xsMMI), leftAddress)); 773 | // Add into the register 774 | (Controlled ModularAddMontgomeryForm)(controls, (xsMMI, point::xs)); 775 | (Controlled Adjoint EqualLookup)(controls, (points, _ClassicalECPointFormat(_, xsMMI), leftAddress)); 776 | } 777 | //Adds lambda^2 to x2+2x1 to get x1-x3 778 | if (nQubits > 50){Message($"{nQubits}-bit modulus: Square");} 779 | (Adjoint ModularSquMontgomeryFormWindowedGeneric)(ModularAddMontgomeryForm(_, point::xs), lambdas); 780 | //Multiplies lambda by x1-x3 and adds the result to the (empty) y register 781 | if (nQubits > 50){Message($"{nQubits}-bit modulus: Second multiplication");} 782 | ModularMulAndAddMontgomeryForm(point::xs, lambdas, point::ys); 783 | //Uncomputes lambda 784 | if (nQubits > 50){Message($"{nQubits}-bit modulus: Second division");} 785 | (Adjoint Controlled ModularDivideAndAddMontgomeryForm)(controls + [zQubit], (point::xs, point::ys, lambdas)); 786 | } 787 | // Adds all required constants in one go 788 | use ancillaPointQubits = Qubit[2 * nQubits + 1] { 789 | if (nQubits > 50){Message($"{nQubits}-bit modulus: Third look-up");} 790 | let ancillaPoint = ECPointMontgomeryForm( 791 | MontModInt(modulus, LittleEndian(ancillaPointQubits[0..nQubits - 1])), 792 | MontModInt(modulus, LittleEndian(ancillaPointQubits[nQubits .. 2 * nQubits - 1])) 793 | ); 794 | // Look up the point 795 | (Controlled EqualLookup)(controls, (points, EncodeClassicalECPointInQuantum(_, ancillaPoint), leftAddress)); 796 | // Flip sign if needed 797 | (Controlled ModularNegMontgomeryForm)([signQubit], (ancillaPoint::ys)); 798 | (Controlled Adjoint ModularAddMontgomeryForm)(controls, (ancillaPoint::xs, point::xs)); 799 | (Controlled Adjoint ModularAddMontgomeryForm)(controls, (ancillaPoint::ys, point::ys)); 800 | // Flip the sign of x (this could be later in the algorithm if needed) 801 | (Controlled ModularNegMontgomeryForm)(controls + [zQubit], (point::xs)); 802 | (Controlled ModularNegMontgomeryForm)([signQubit], (ancillaPoint::ys)); 803 | (Controlled Adjoint EqualLookup)(controls, (points, ECPointWrite(_, ancillaPoint, zQubit), leftAddress)); 804 | } 805 | } 806 | // Clean up address register 807 | (Adjoint Controlled Increment)([signQubit], (leftAddress)); 808 | (Controlled ApplyToEachWrapperCA)([signQubit], (X, unsignedAddress)); 809 | X(signQubit); 810 | // DumpECPointMontgomery(point, "Added accumluator:"); 811 | } 812 | } 813 | controlled adjoint auto; 814 | } 815 | } --------------------------------------------------------------------------------