diff --git a/QuIDE/CodeHelpers/BlochSphereGenerator.cs b/QuIDE/CodeHelpers/BlochSphereGenerator.cs new file mode 100644 index 0000000..6529bd5 --- /dev/null +++ b/QuIDE/CodeHelpers/BlochSphereGenerator.cs @@ -0,0 +1,268 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Numerics; +using System.Resources; +using Avalonia.Platform; +using QuIDE.Properties; +using ScottPlot; +using ScottPlot.Plottables; +using SkiaSharp; + +namespace QuIDE.CodeHelpers; + +public record struct Coord3D(double X, double Y, double Z); + +public class BlochSphereGenerator +{ + public int DefaultAzimuthDegrees => 135; + public int DefaultElevationDegrees => 245; + + private int _azimuthDegrees; + private int _elevationDegrees; + + public BlochSphereGenerator() + { + _azimuthDegrees = DefaultAzimuthDegrees; + _elevationDegrees = DefaultElevationDegrees; + } + + /// + /// Set Camera position to look at the Bloch Sphere + /// + /// Horizontal angle to look from (azimuth) + /// Vertical angle to look from (elevation) + public void SetViewpoint(int horizontal, int vertical) + { + _azimuthDegrees = Mod360(horizontal); + _elevationDegrees = Mod360(vertical); + } + + /// + /// Helper method, to normalize rotation degrees to 0 - 360 + /// + public static int Mod360(int degree) + { + degree %= 360; + if (degree < 0) + degree += 360; + + var result = (degree == 0) ? 360 : degree; + return result; + } + + /// + /// Generate a square Bloch Sphere Image from a 2x2 density matrix. + /// This method calculates the Bloch vector from the density matrix and renders it. + /// + /// The 2x2 complex density matrix of the qubit. + /// Size of the image. Used for width and height. + /// A ScottPlot.Image representing the Bloch Sphere with the state vector. + public ScottPlot.Image GeneratePlot(Complex[,] densityMatrix, int imgSize) + { + // density Matrix is already normalized + Coord3D blochVector = ConvertToBlochVector(densityMatrix); + var color = GetVectorColor(blochVector); + return GetPlotFromComplexAmplitudes(imgSize, color, blochVector); + } + + private Image GetPlotFromComplexAmplitudes(int imgSize, Color vectorColor, Coord3D blochVector) + { + const double plotLimit = 1.3; + Plot plot = new(); + plot.Title(""); + plot.Layout.Frameless(); + plot.HideGrid(); + plot.Axes.SetLimits(-plotLimit, plotLimit, -plotLimit, plotLimit); + plot.Axes.Rules.Clear(); + + DrawSphereWireframe(plot); + DrawAxesAndLabels(plot); + DrawStateVector(plot, vectorColor, blochVector); + + return plot.GetImage(imgSize, imgSize); + } + + // // "Mapping" from alpha/beta to "angles" theta/psi to find any point on the surface + // private Coord3D ConvertToBlochVector(Complex alpha, Complex beta) + // { + // Complex num = 2 * (Complex.Conjugate(alpha) * beta); // conjugate alpha = theta/2 + // double real = num.Real; + // double imaginary = num.Imaginary; // y + // double z = Math.Pow(alpha.Magnitude, 2) - Math.Pow(beta.Magnitude, 2); + // return new Coord3D(real, imaginary, z); + // } + + /// + /// Converts a 2x2 density matrix into a Bloch vector (x, y, z) coordinates. + /// + /// The 2x2 complex density matrix. Assumed to be trace-normalized. + /// A Coord3D representing the Bloch vector. Its magnitude will be less than or equal to 1. + private Coord3D ConvertToBlochVector(Complex[,] densityMatrix) + { + Complex num = 2 * densityMatrix[0, 1]; + double x = num.Real; + double y = num.Imaginary; + double z = densityMatrix[0, 0].Real - densityMatrix[1, 1].Real; + + // The resulting Bloch vector accurately represents pure states (on the surface) and mixed states (inside). + // No further normalization of the vector is needed here, as the density matrix is already normalized. + return new Coord3D(x, y, z); + } + + private Color GetVectorColor(Coord3D blochVector) + { + // Pure states (length ≈ 1) are red. + // Mixed states (length < 1) are blue. + double length = Math.Round(Math.Sqrt(Math.Pow(blochVector.X, 2) + Math.Pow(blochVector.Y, 2) + Math.Pow(blochVector.Z, 2)), 3); + return (length < 1) ? Colors.Blue : Colors.Red; + } + + private void DrawSphereWireframe(Plot plot) + { + const double negativeHalfPi = -Math.PI / 2; + const double halfPi = -(negativeHalfPi); + const double eightStep = Math.PI / 8; + const double doublePi = 2 * Math.PI; + + // Latitude lines + for (double lat = negativeHalfPi; lat <= halfPi; lat += eightStep) + { + List points = new(); + for (double lon = 0; lon <= doublePi; lon += 0.1) + { + points.Add(new Coord3D( + Math.Cos(lat) * Math.Cos(lon), + Math.Cos(lat) * Math.Sin(lon), + Math.Sin(lat) + )); + } + + Draw3DLine(plot, points, Colors.LightGray, 1, LinePattern.Solid); + } + + // Longitude + for (double lon = 0; lon < doublePi; lon += eightStep) + { + List points = new(); + for (double lat = negativeHalfPi; lat <= halfPi; lat += 0.1) + { + points.Add(new Coord3D( + Math.Cos(lat) * Math.Cos(lon), + Math.Cos(lat) * Math.Sin(lon), + Math.Sin(lat) + )); + } + + Draw3DLine(plot, points, Colors.LightGray, 1, LinePattern.Solid); + } + } + + private void DrawAxesAndLabels(Plot plot) + { + // Axes + List X_Axis = new() { new(-1, 0, 0), new(1, 0, 0) }; + List Y_Axis = new() { new(0, -1, 0), new(0, 1, 0) }; + List Z_Axis = new() { new(0, 0, -1), new(0, 0, 1) }; + + Draw3DLine(plot, X_Axis, Colors.Gray, 1, LinePattern.DenselyDashed); + Draw3DLine(plot, Y_Axis, Colors.Gray, 1, LinePattern.DenselyDashed); + Draw3DLine(plot, Z_Axis, Colors.Gray, 1, LinePattern.DenselyDashed); + + // Labels + AddText(plot, Resources.KetZero, new Coord3D(0, 0, 1.1)); + AddText(plot, Resources.KetOne, new Coord3D(0, 0, -1.1)); + AddText(plot, Resources.KetPlus, new Coord3D(1.1, 0, 0)); + AddText(plot, Resources.KetMinus, new Coord3D(-1.1, 0, 0)); + AddText(plot, Resources.KetNegative_I, new Coord3D(0, 1.1, 0)); + AddText(plot, Resources.KetPositive_I, new Coord3D(0, -1.1, 0)); + } + + private void DrawStateVector(Plot plot, Color vectorColor, Coord3D vector) + { + List coordinates = new() { new(0, 0, 0), vector }; + Draw3DLine(plot, coordinates, vectorColor, 3, LinePattern.Solid, true); + + var (_, projectedEnd) = Project(vector); + Marker arrowHead = plot.Add.Marker(projectedEnd); + arrowHead.MarkerStyle.FillStyle.Color = vectorColor; + arrowHead.MarkerStyle.Shape = MarkerShape.FilledTriangleUp; + arrowHead.MarkerStyle.Size = 7f; + } + + private Scatter Draw3DLine(Plot plot, List points3d, Color color, float lineWidth, LinePattern pattern, bool ignoreDepth = false) + { + // Project 3D to 2D -> create illusion of 3D on a 2D plane + var projectedPoints = points3d.Select(s => Project(s).projected).ToList(); + var avgDepth = points3d.Average(a => Project(a).rotated.Z); + + var scatter = plot.Add.Scatter(projectedPoints); + scatter.Color = color; + scatter.LineWidth = lineWidth; + + if (ignoreDepth) + scatter.LinePattern = pattern; + else + scatter.LinePattern = (avgDepth < -0.1) ? LinePattern.Dotted : pattern; + + scatter.MarkerSize = 0; + return scatter; + } + + private void AddText(Plot plt, string text, Coord3D position) + { + var (_, projectedPos) = Project(position); + var label = plt.Add.Text(text, projectedPos); + label.LabelStyle.FontSize = 18; + label.LabelStyle.Bold = true; + label.LabelStyle.Alignment = Alignment.MiddleCenter; + } + + /// + /// Project 3D coordinate into a 2D coordinate + /// + private (Coord3D rotated, Coordinates projected) Project(Coord3D coord) + { + // convert view angles to radiants + double azimuth = _azimuthDegrees * Math.PI / 180.0; + double elevation = _elevationDegrees * Math.PI / 180.0; + + // apply rotation matrix + // > around Z-axis (azimuth - horizontal) + double x_1 = coord.X * Math.Cos(azimuth) - coord.Y * Math.Sin(azimuth); + double y_1 = coord.X * Math.Sin(azimuth) + coord.Y * Math.Cos(azimuth); + double z_1 = coord.Z; + + // > around X-axis (elevation - vertical) + double x_2 = x_1; + double y_2 = y_1 * Math.Cos(elevation) - z_1 * Math.Sin(elevation); + double z_2 = y_1 * Math.Sin(elevation) + z_1 * Math.Cos(elevation); // depth + + Coord3D result3D = new(x_2, y_2, z_2); + Coordinates result2D = new(x_2, y_2); + return (result3D, result2D); + } + + public static ScottPlot.Image GeneratePlaceholder(int imgSize, string text) + { + var plot = new Plot(); + + // uniform gray background + var bg = new ScottPlot.Color(211, 211, 211, 100); + plot.FigureBackground.Color = bg; // outside the data area + plot.DataBackground.Color = bg; // inside the data area + + // hide axes/ticks/frame + plot.Axes.Frameless(); + plot.Axes.SetLimits(0, 1, 0, 1); + plot.HideGrid(); + + // centered text + var label = plot.Add.Text(text, 0.5, 0.5); + label.Alignment = Alignment.MiddleCenter; + label.LabelFontColor = new ScottPlot.Color(60, 60, 60); + label.LabelFontSize = 18; + + return plot.GetImage(imgSize, imgSize); + } +} \ No newline at end of file diff --git a/QuIDE/Properties/Resources.Designer.cs b/QuIDE/Properties/Resources.Designer.cs index fcf8ce7..f40403a 100644 --- a/QuIDE/Properties/Resources.Designer.cs +++ b/QuIDE/Properties/Resources.Designer.cs @@ -1,59 +1,42 @@ //------------------------------------------------------------------------------ // // This code was generated by a tool. -// Runtime Version:4.0.30319.18444 // // Changes to this file may cause incorrect behavior and will be lost if // the code is regenerated. // //------------------------------------------------------------------------------ -using System.Reflection; - namespace QuIDE.Properties { using System; - /// - /// A strongly-typed resource class, for looking up localized strings, etc. - /// - // This class was auto-generated by the StronglyTypedResourceBuilder - // class via a tool like ResGen or Visual Studio. - // To add or remove a member, edit your .ResX file then rerun ResGen - // with the /str option, or rebuild your VS project. - [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "4.0.0.0")] - [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] - [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] + [System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "4.0.0.0")] + [System.Diagnostics.DebuggerNonUserCodeAttribute()] + [System.Runtime.CompilerServices.CompilerGeneratedAttribute()] public class Resources { - private static global::System.Resources.ResourceManager resourceMan; + private static System.Resources.ResourceManager resourceMan; - private static global::System.Globalization.CultureInfo resourceCulture; + private static System.Globalization.CultureInfo resourceCulture; - [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")] - internal Resources() { + [System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")] + public Resources() { } - /// - /// Returns the cached ResourceManager instance used by this class. - /// - [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] - public static global::System.Resources.ResourceManager ResourceManager { + [System.ComponentModel.EditorBrowsableAttribute(System.ComponentModel.EditorBrowsableState.Advanced)] + public static System.Resources.ResourceManager ResourceManager { get { - if (object.ReferenceEquals(resourceMan, null)) { - global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("QuIDE.Properties.Resources", typeof(Resources).Assembly); + if (object.Equals(null, resourceMan)) { + System.Resources.ResourceManager temp = new System.Resources.ResourceManager("QuIDE.Properties.Resources", typeof(Resources).Assembly); resourceMan = temp; } return resourceMan; } } - /// - /// Overrides the current thread's CurrentUICulture property for all - /// resource lookups using this strongly typed resource class. - /// - [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] - public static global::System.Globalization.CultureInfo Culture { + [System.ComponentModel.EditorBrowsableAttribute(System.ComponentModel.EditorBrowsableState.Advanced)] + public static System.Globalization.CultureInfo Culture { get { return resourceCulture; } @@ -62,1061 +45,759 @@ internal Resources() { } } - /// - /// Looks up a localized string similar to About. - /// - public static string About { - get { - return ResourceManager.GetString("About", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to QuIDE - Quantum Integrated Development Environment - /// - public static string Name { - get { - return ResourceManager.GetString("Name", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to QuIDE - Quantum Integrated Development Environment - /// - public static string Copyright { - get { - return ResourceManager.GetString("Copyright", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Absolute. - /// public static string Absolute { get { return ResourceManager.GetString("Absolute", resourceCulture); } } - /// - /// Looks up a localized string similar to Add Control Bit. - /// public static string AddControl { get { return ResourceManager.GetString("AddControl", resourceCulture); } } - /// - /// Looks up a localized string similar to Add Parameter. - /// - public static string AddParam { - get { - return ResourceManager.GetString("AddParam", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Add Qubit to Register. - /// public static string AddQubit { get { return ResourceManager.GetString("AddQubit", resourceCulture); } } - /// - /// Looks up a localized string similar to Add New Register. - /// public static string AddRegister { get { return ResourceManager.GetString("AddRegister", resourceCulture); } } - /// - /// Looks up a localized string similar to Add Step. - /// public static string AddStep { get { return ResourceManager.GetString("AddStep", resourceCulture); } } - /// - /// Looks up a localized string similar to Amplitude. - /// public static string Amplitude { get { return ResourceManager.GetString("Amplitude", resourceCulture); } } - /// - /// Looks up a localized string similar to If amplitudes are not normalized (probabilities of possible states do not sum up to 1), they will be automatically normalized before register creation.. - /// - public static string AmplitudesNormalizationCaution { - get { - return ResourceManager.GetString("AmplitudesNormalizationCaution", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Angle Value :. - /// public static string AngleValue { get { return ResourceManager.GetString("AngleValue", resourceCulture); } } - /// - /// Looks up a localized string similar to Apply. - /// public static string Apply { get { return ResourceManager.GetString("Apply", resourceCulture); } } - /// - /// Looks up a localized string similar to Authors: - ///Joanna Patrzyk (jpatrzyk@QuIDE.eu) - ///Bartłomiej Patrzyk (bpatrzyk@QuIDE.eu). - /// - public static string Authors { - get { - return ResourceManager.GetString("Authors", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Calculator. - /// - public static string Calculator { - get { - return ResourceManager.GetString("Calculator", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Cancel. - /// public static string Cancel { get { return ResourceManager.GetString("Cancel", resourceCulture); } } - /// - /// Looks up a localized string similar to Circuit Designer. - /// public static string CircuitDesigner { get { return ResourceManager.GetString("CircuitDesigner", resourceCulture); } } - /// - /// Looks up a localized string similar to Clear. - /// - public static string ClearAll { - get { - return ResourceManager.GetString("ClearAll", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Clear Circuit. - /// public static string ClearCircuit { get { return ResourceManager.GetString("ClearCircuit", resourceCulture); } } - /// - /// Looks up a localized string similar to Clear Selection [Delete]. - /// - public static string ClearSelection { - get { - return ResourceManager.GetString("ClearSelection", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Composite Gate. - /// public static string Composite { get { return ResourceManager.GetString("Composite", resourceCulture); } } - /// - /// Looks up a localized string similar to Console Output. - /// public static string ConsoleOutput { get { return ResourceManager.GetString("ConsoleOutput", resourceCulture); } } - /// - /// Looks up a localized string similar to Copy [Ctrl+C]. - /// public static string Copy { get { return ResourceManager.GetString("Copy", resourceCulture); } } - /// - /// Looks up a localized string similar to Copy Result. - /// - public static string CopyResult { - get { - return ResourceManager.GetString("CopyResult", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Conditional Phase Kick (Phase Shift) Gate For QFT. - /// public static string CPhaseShift { get { return ResourceManager.GetString("CPhaseShift", resourceCulture); } } - /// - /// Looks up a localized string similar to Cut [Ctrl+X]. - /// public static string Cut { get { return ResourceManager.GetString("Cut", resourceCulture); } } - /// - /// Looks up a localized string similar to Delete Column. - /// public static string DeleteColumn { get { return ResourceManager.GetString("DeleteColumn", resourceCulture); } } - /// - /// Looks up a localized string similar to Delete Qubit. - /// public static string DeleteQubit { get { return ResourceManager.GetString("DeleteQubit", resourceCulture); } } - /// - /// Looks up a localized string similar to Delete Register. - /// public static string DeleteRegister { get { return ResourceManager.GetString("DeleteRegister", resourceCulture); } } - /// - /// Looks up a localized string similar to Delete Row. - /// public static string DeleteRow { get { return ResourceManager.GetString("DeleteRow", resourceCulture); } } - /// - /// Looks up a localized string similar to Phase Distance Value (k) :. - /// public static string DistValue { get { return ResourceManager.GetString("DistValue", resourceCulture); } } - /// - /// Looks up a localized string similar to Drag and Drop selected gates. - /// public static string DragGates { get { return ResourceManager.GetString("DragGates", resourceCulture); } } - /// - /// Looks up a localized string similar to Edit.... - /// - public static string Edit { - get { - return ResourceManager.GetString("Edit", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Enter Name for the new Composite Gate :. - /// - public static string EnterName { - get { - return ResourceManager.GetString("EnterName", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Erase Gate. - /// public static string EraseGate { get { return ResourceManager.GetString("EraseGate", resourceCulture); } } - /// - /// Looks up a localized string similar to Flip Bit. - /// public static string FlipBit { get { return ResourceManager.GetString("FlipBit", resourceCulture); } } - /// - /// Looks up a localized string similar to Generate Code From Circuit. - /// public static string GenerateCode { get { return ResourceManager.GetString("GenerateCode", resourceCulture); } } - /// - /// Looks up a localized string similar to Generate Circuit From Code. - /// public static string GenerateFromCode { get { return ResourceManager.GetString("GenerateFromCode", resourceCulture); } } - /// - /// Looks up a localized string similar to GNU GENERAL PUBLIC LICENSE - /// Version 3, 29 June 2007 - /// - /// Copyright (C) 2007 Free Software Foundation, Inc. <http://fsf.org/> - /// Everyone is permitted to copy and distribute verbatim copies - /// of this license document, but changing it is not allowed. - /// - /// Preamble - /// - /// The GNU General Public License is a free, copyleft license for - ///software and other kinds of works. - /// - /// The licenses for most software and other practical works are designed - ///to take away your free [rest of string was truncated]";. - /// - public static string GPLText { - get { - return ResourceManager.GetString("GPLText", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Group. - /// - public static string Group { - get { - return ResourceManager.GetString("Group", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Create Composite Gate from selected gates. - /// - public static string GroupTip { - get { - return ResourceManager.GetString("GroupTip", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Hadamard Gate. - /// public static string Hadamard { get { return ResourceManager.GetString("Hadamard", resourceCulture); } } - /// - /// Looks up a localized string similar to Run In Console. - /// - public static string InConsole { - get { - return ResourceManager.GetString("InConsole", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Initial register state:. - /// - public static string InitState { - get { - return ResourceManager.GetString("InitState", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Initial register value. - /// - public static string InitValue { - get { - return ResourceManager.GetString("InitValue", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Insert Column on the Left. - /// public static string InsertColumnLeft { get { return ResourceManager.GetString("InsertColumnLeft", resourceCulture); } } - /// - /// Looks up a localized string similar to Insert Column on the Right. - /// public static string InsertColumnRight { get { return ResourceManager.GetString("InsertColumnRight", resourceCulture); } } - /// - /// Looks up a localized string similar to Insert Qubit Above. - /// public static string InsertQubitAbove { get { return ResourceManager.GetString("InsertQubitAbove", resourceCulture); } } - /// - /// Looks up a localized string similar to Insert Qubit Below. - /// public static string InsertQubitBelow { get { return ResourceManager.GetString("InsertQubitBelow", resourceCulture); } } - /// - /// Looks up a localized string similar to Insert Register Above. - /// public static string InsertRegisterAbove { get { return ResourceManager.GetString("InsertRegisterAbove", resourceCulture); } } - /// - /// Looks up a localized string similar to Insert Register Below. - /// public static string InsertRegisterBelow { get { return ResourceManager.GetString("InsertRegisterBelow", resourceCulture); } } - /// - /// Looks up a localized string similar to Insert Row Above. - /// public static string InsertRowAbove { get { return ResourceManager.GetString("InsertRowAbove", resourceCulture); } } - /// - /// Looks up a localized string similar to Insert Row Below. - /// public static string InsertRowBelow { get { return ResourceManager.GetString("InsertRowBelow", resourceCulture); } } - /// - /// Looks up a localized string similar to Inversed Conditional Phase Kick (Phase Shift) Gate For QFT. - /// public static string InvCPhaseShift { get { return ResourceManager.GetString("InvCPhaseShift", resourceCulture); } } - /// - /// Looks up a localized string similar to QuIDE is available under the GNU General Public License:. - /// - public static string License { - get { - return ResourceManager.GetString("License", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to The matrix is not unitary.. - /// public static string MatrixNotUnitary { get { return ResourceManager.GetString("MatrixNotUnitary", resourceCulture); } } - /// - /// Looks up a localized string similar to Measurement. - /// public static string Measure { get { return ResourceManager.GetString("Measure", resourceCulture); } } - /// - /// Looks up a localized string similar to New [Ctrl+N]. - /// public static string New { get { return ResourceManager.GetString("New", resourceCulture); } } - /// - /// Looks up a localized string similar to Step Forward [F7]. - /// public static string NextStep { get { return ResourceManager.GetString("NextStep", resourceCulture); } } - /// - /// Looks up a localized string similar to Open [Ctrl+O]. - /// public static string Open { get { return ResourceManager.GetString("Open", resourceCulture); } } - /// - /// Looks up a localized string similar to Operation Matrix :. - /// public static string OperationMatrix { get { return ResourceManager.GetString("OperationMatrix", resourceCulture); } } - /// - /// Looks up a localized string similar to Output. - /// public static string Output { get { return ResourceManager.GetString("Output", resourceCulture); } } - /// - /// Looks up a localized string similar to Paste [Ctrl+V]. - /// public static string Paste { get { return ResourceManager.GetString("Paste", resourceCulture); } } - /// - /// Looks up a localized string similar to Phase Kick (Phase Shift) Gate. - /// public static string PhaseKick { get { return ResourceManager.GetString("PhaseKick", resourceCulture); } } - /// - /// Looks up a localized string similar to Phase Scale Gate. - /// public static string PhaseScale { get { return ResourceManager.GetString("PhaseScale", resourceCulture); } } - /// - /// Looks up a localized string similar to π. - /// public static string Pi { get { return ResourceManager.GetString("Pi", resourceCulture); } } - /// - /// Looks up a localized string similar to π / 2. - /// public static string Pi_2 { get { return ResourceManager.GetString("Pi_2", resourceCulture); } } - /// - /// Looks up a localized string similar to π / 3. - /// public static string Pi_3 { get { return ResourceManager.GetString("Pi_3", resourceCulture); } } - /// - /// Looks up a localized string similar to π / 4. - /// public static string Pi_4 { get { return ResourceManager.GetString("Pi_4", resourceCulture); } } - /// - /// Looks up a localized string similar to π / 6. - /// public static string Pi_6 { get { return ResourceManager.GetString("Pi_6", resourceCulture); } } - /// - /// Looks up a localized string similar to π / 8. - /// public static string Pi_8 { get { return ResourceManager.GetString("Pi_8", resourceCulture); } } - /// - /// Looks up a localized string similar to Pointer. - /// public static string Pointer { get { return ResourceManager.GetString("Pointer", resourceCulture); } } - /// - /// Looks up a localized string similar to Step Back [F6]. - /// public static string PrevStep { get { return ResourceManager.GetString("PrevStep", resourceCulture); } } - /// - /// Looks up a localized string similar to Print. - /// public static string Print { get { return ResourceManager.GetString("Print", resourceCulture); } } - /// - /// Looks up a localized string similar to Probability. - /// public static string Probability { get { return ResourceManager.GetString("Probability", resourceCulture); } } - /// - /// Looks up a localized string similar to Probability Scale. - /// public static string ProbabilityScale { get { return ResourceManager.GetString("ProbabilityScale", resourceCulture); } } - /// - /// Looks up a localized string similar to Properties. - /// public static string Properties { get { return ResourceManager.GetString("Properties", resourceCulture); } } - /// - /// Looks up a localized string similar to Quantum Fourier Transform. - /// public static string QFT { get { return ResourceManager.GetString("QFT", resourceCulture); } } - /// - /// Looks up a localized string similar to Qubits. - /// public static string Qubits { get { return ResourceManager.GetString("Qubits", resourceCulture); } } - /// - /// Looks up a localized string similar to Number of new qubits. - /// - public static string QubitsNumber { - get { - return ResourceManager.GetString("QubitsNumber", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Redo [Ctrl+Shift+Z]. - /// - public static string Redo { - get { - return ResourceManager.GetString("Redo", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Relative. - /// public static string Relative { get { return ResourceManager.GetString("Relative", resourceCulture); } } - /// - /// Looks up a localized string similar to Remove Qubit from Register. - /// public static string RemoveQubit { get { return ResourceManager.GetString("RemoveQubit", resourceCulture); } } - /// - /// Looks up a localized string similar to Restart [F5]. - /// public static string Restart { get { return ResourceManager.GetString("Restart", resourceCulture); } } - /// - /// Looks up a localized string similar to Rotate X Gate. - /// public static string RotateX { get { return ResourceManager.GetString("RotateX", resourceCulture); } } - /// - /// Looks up a localized string similar to Rotate Y Gate. - /// public static string RotateY { get { return ResourceManager.GetString("RotateY", resourceCulture); } } - /// - /// Looks up a localized string similar to Rotate Z Gate. - /// public static string RotateZ { get { return ResourceManager.GetString("RotateZ", resourceCulture); } } - /// - /// Looks up a localized string similar to Run To End [F8]. - /// public static string Run { get { return ResourceManager.GetString("Run", resourceCulture); } } - /// - /// Looks up a localized string similar to Run In Console. - /// public static string RunInConsole { get { return ResourceManager.GetString("RunInConsole", resourceCulture); } } - /// - /// Looks up a localized string similar to Save [Ctrl+S]. - /// public static string Save { get { return ResourceManager.GetString("Save", resourceCulture); } } - /// - /// Looks up a localized string similar to Save As... [Ctrl+Alt+S]. - /// public static string SaveAs { get { return ResourceManager.GetString("SaveAs", resourceCulture); } } - /// - /// Looks up a localized string similar to Select Composite Gate.... - /// public static string SelectCompositeGate { get { return ResourceManager.GetString("SelectCompositeGate", resourceCulture); } } - /// - /// Looks up a localized string similar to Selected Composite Gate :. - /// - public static string SelectedCompositeGate { - get { - return ResourceManager.GetString("SelectedCompositeGate", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Selection [Shift] (to move selected, press Shift+Ctrl). - /// - public static string Selection { - get { - return ResourceManager.GetString("Selection", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Selected Method :. - /// public static string SelectMethod { get { return ResourceManager.GetString("SelectMethod", resourceCulture); } } - /// - /// Looks up a localized string similar to About QuIDE.... - /// - public static string ShowAbout { - get { - return ResourceManager.GetString("ShowAbout", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Show All. - /// public static string ShowAll { get { return ResourceManager.GetString("ShowAll", resourceCulture); } } - /// - /// Looks up a localized string similar to Show Calculator [Ctrl+K]. - /// - public static string ShowCalculator { - get { - return ResourceManager.GetString("ShowCalculator", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Show Only Non-Zero. - /// public static string ShowOnlyNonZero { get { return ResourceManager.GetString("ShowOnlyNonZero", resourceCulture); } } - /// - /// Looks up a localized string similar to Sigma X Gate (Not). - /// public static string SigmaX { get { return ResourceManager.GetString("SigmaX", resourceCulture); } } - /// - /// Looks up a localized string similar to Sigma Y Gate. - /// public static string SigmaY { get { return ResourceManager.GetString("SigmaY", resourceCulture); } } - /// - /// Looks up a localized string similar to Sigma Z Gate. - /// public static string SigmaZ { get { return ResourceManager.GetString("SigmaZ", resourceCulture); } } - /// - /// Looks up a localized string similar to Square Root of Not Gate. - /// public static string SqrtX { get { return ResourceManager.GetString("SqrtX", resourceCulture); } } - /// - /// Looks up a localized string similar to Amplitude of the state. It is a complex number, which squared magnitude gives the probability of the state.. - /// public static string StateAmplitude { get { return ResourceManager.GetString("StateAmplitude", resourceCulture); } } - /// - /// Looks up a localized string similar to Probability of the state. The bar's length represents probability value. Color represents the phase of state's amplitude (which is complex number).. - /// public static string StateProbability { get { return ResourceManager.GetString("StateProbability", resourceCulture); } } - /// - /// Looks up a localized string similar to State binary representation. - /// public static string StateQubits { get { return ResourceManager.GetString("StateQubits", resourceCulture); } } - /// - /// Looks up a localized string similar to States Visibility. - /// public static string StatesVisibility { get { return ResourceManager.GetString("StatesVisibility", resourceCulture); } } - /// - /// Looks up a localized string similar to Decimal value of the state. - /// public static string StateValue { get { return ResourceManager.GetString("StateValue", resourceCulture); } } - /// - /// Looks up a localized string similar to Build Circuit. - /// + public static string Ungroup { + get { + return ResourceManager.GetString("Ungroup", resourceCulture); + } + } + + public static string Unitary { + get { + return ResourceManager.GetString("Unitary", resourceCulture); + } + } + + public static string Value { + get { + return ResourceManager.GetString("Value", resourceCulture); + } + } + + public static string SelectedCompositeGate { + get { + return ResourceManager.GetString("SelectedCompositeGate", resourceCulture); + } + } + + public static string AddParam { + get { + return ResourceManager.GetString("AddParam", resourceCulture); + } + } + + public static string Group { + get { + return ResourceManager.GetString("Group", resourceCulture); + } + } + + public static string GroupTip { + get { + return ResourceManager.GetString("GroupTip", resourceCulture); + } + } + + public static string UngroupTip { + get { + return ResourceManager.GetString("UngroupTip", resourceCulture); + } + } + + public static string EnterName { + get { + return ResourceManager.GetString("EnterName", resourceCulture); + } + } + + public static string InitValue { + get { + return ResourceManager.GetString("InitValue", resourceCulture); + } + } + + public static string QubitsNumber { + get { + return ResourceManager.GetString("QubitsNumber", resourceCulture); + } + } + + public static string InitState { + get { + return ResourceManager.GetString("InitState", resourceCulture); + } + } + + public static string AmplitudesNormalizationCaution { + get { + return ResourceManager.GetString("AmplitudesNormalizationCaution", resourceCulture); + } + } + + public static string Calculator { + get { + return ResourceManager.GetString("Calculator", resourceCulture); + } + } + + public static string CopyResult { + get { + return ResourceManager.GetString("CopyResult", resourceCulture); + } + } + + public static string ShowCalculator { + get { + return ResourceManager.GetString("ShowCalculator", resourceCulture); + } + } + + public static string ClearAll { + get { + return ResourceManager.GetString("ClearAll", resourceCulture); + } + } + + public static string ClearSelection { + get { + return ResourceManager.GetString("ClearSelection", resourceCulture); + } + } + + public static string InConsole { + get { + return ResourceManager.GetString("InConsole", resourceCulture); + } + } + + public static string Selection { + get { + return ResourceManager.GetString("Selection", resourceCulture); + } + } + public static string ToCircuit { get { return ResourceManager.GetString("ToCircuit", resourceCulture); } } - /// - /// Looks up a localized string similar to To Code. - /// public static string ToCode { get { return ResourceManager.GetString("ToCode", resourceCulture); } } - /// - /// Looks up a localized string similar to Undo [Ctrl+Z]. - /// + public static string Edit { + get { + return ResourceManager.GetString("Edit", resourceCulture); + } + } + + public static string About { + get { + return ResourceManager.GetString("About", resourceCulture); + } + } + + public static string ShowAbout { + get { + return ResourceManager.GetString("ShowAbout", resourceCulture); + } + } + + public static string Name { + get { + return ResourceManager.GetString("Name", resourceCulture); + } + } + + public static string Copyright { + get { + return ResourceManager.GetString("Copyright", resourceCulture); + } + } + + public static string Authors { + get { + return ResourceManager.GetString("Authors", resourceCulture); + } + } + + public static string GPLText { + get { + return ResourceManager.GetString("GPLText", resourceCulture); + } + } + + public static string Redo { + get { + return ResourceManager.GetString("Redo", resourceCulture); + } + } + public static string Undo { get { return ResourceManager.GetString("Undo", resourceCulture); } } - /// - /// Looks up a localized string similar to Ungroup. - /// - public static string Ungroup { + public static string NoImg { get { - return ResourceManager.GetString("Ungroup", resourceCulture); + return ResourceManager.GetString("NoImg", resourceCulture); } } - /// - /// Looks up a localized string similar to Ungroup Composite Gate. - /// - public static string UngroupTip { + public static string NoQubit { get { - return ResourceManager.GetString("UngroupTip", resourceCulture); + return ResourceManager.GetString("NoQubit", resourceCulture); } } - /// - /// Looks up a localized string similar to Custom Unitary Gate. - /// - public static string Unitary { + public static string AreaTooSmall { get { - return ResourceManager.GetString("Unitary", resourceCulture); + return ResourceManager.GetString("AreaTooSmall", resourceCulture); } } - /// - /// Looks up a localized string similar to Value. - /// - public static string Value { + public static string QubitEntanlged { get { - return ResourceManager.GetString("Value", resourceCulture); + return ResourceManager.GetString("QubitEntanlged", resourceCulture); } } - - public static string Version - { - get - { - return "Version: " + Assembly.GetEntryAssembly().GetCustomAttribute().InformationalVersion; + + public static string KetZero { + get { + return ResourceManager.GetString("KetZero", resourceCulture); + } + } + + public static string KetOne { + get { + return ResourceManager.GetString("KetOne", resourceCulture); + } + } + + public static string KetPlus { + get { + return ResourceManager.GetString("KetPlus", resourceCulture); + } + } + + public static string KetMinus { + get { + return ResourceManager.GetString("KetMinus", resourceCulture); + } + } + + public static string KetPositive_I { + get { + return ResourceManager.GetString("KetPositive_I", resourceCulture); + } + } + + public static string KetNegative_I { + get { + return ResourceManager.GetString("KetNegative_I", resourceCulture); + } + } + + public static string Version { + get { + return ResourceManager.GetString("Version", resourceCulture); + } + } + + public static string NoRegisterForQubitFound { + get { + return ResourceManager.GetString("NoRegisterForQubitFound", resourceCulture); } } } diff --git a/QuIDE/Properties/Resources.resx b/QuIDE/Properties/Resources.resx index 560f90a..db7c2dd 100644 --- a/QuIDE/Properties/Resources.resx +++ b/QuIDE/Properties/Resources.resx @@ -450,11 +450,12 @@ QuIDE - Quantum Integrated Development Environment - - © 2014 Joanna Patrzyk, Bartłomiej Patrzyk + + © 2014 Joanna Patrzyk, Bartłomiej Patrzyk © 2023 Marc Fichtner -© 2023-2024 MNM-Team - +© 2023-2024 MNM-Team +© 2025 Albert Pankratz + Authors: Joanna Patrzyk (jpatrzyk@quide.eu) @@ -470,4 +471,40 @@ MNM-Team (mnm-team.org) Undo [Ctrl+Z] + + No Image to render + + + No Qubit selected + + + Area too small + + + Selected qubit is entangled + + + |0> + + + |1> + + + |+> + + + |-> + + + |i> + + + |-i> + + + 2.4.1 + + + Could not find quantum register for selected qubit. + \ No newline at end of file diff --git a/QuIDE/QuIDE.csproj b/QuIDE/QuIDE.csproj index ed7a552..4d9dbf2 100644 --- a/QuIDE/QuIDE.csproj +++ b/QuIDE/QuIDE.csproj @@ -10,6 +10,7 @@ true true + 2.4.2 @@ -48,11 +49,13 @@ + Designer + ResXFileCodeGenerator diff --git a/QuIDE/ViewModels/Controls/BlochSphereViewModel.cs b/QuIDE/ViewModels/Controls/BlochSphereViewModel.cs new file mode 100644 index 0000000..59e95c7 --- /dev/null +++ b/QuIDE/ViewModels/Controls/BlochSphereViewModel.cs @@ -0,0 +1,116 @@ +using System; +using System.IO; +using System.Windows.Input; +using Avalonia.Media.Imaging; +using QuIDE.CodeHelpers; +using QuIDE.Properties; + +namespace QuIDE.ViewModels.Controls; + +public class BlochSphereViewModel : ViewModelBase +{ + private Bitmap _blochImage; + private int _horizontalDegree; + private int _verticalDegree; + private string _stateVector; + private int _renderSize = 400; + private readonly int _defaultAzimuthDegrees; + private readonly int _defaultElevationDegrees; + + public string StateVector + { + get => _stateVector; + set + { + _stateVector = value; + this.OnPropertyChanged(nameof(StateVector)); + } + } + + public int VerticalDegree + { + get => _verticalDegree; + set + { + _verticalDegree = value; + this.OnPropertyChanged(nameof(VerticalDegree)); + } + } + + public int HorizontalDegree + { + get => _horizontalDegree; + set + { + _horizontalDegree = value; + this.OnPropertyChanged(nameof(HorizontalDegree)); + } + } + + public Bitmap BlochImage + { + get => _blochImage; + set + { + _blochImage = value; + this.OnPropertyChanged(nameof(BlochImage)); + } + } + + public int RenderSize + { + get => _renderSize; + set + { + // Only update if the change is significant to avoid rapid-fire updates + if (Math.Abs(_renderSize - value) < 5) + return; + + _renderSize = value; + OnPropertyChanged(nameof(RenderSize)); + } + } + + public ICommand ResetViewCmd { get; } + + public BlochSphereViewModel() + { + // needed for avalonia binding + } + + public BlochSphereViewModel(int defaultAzimuthDegrees, int defaultElevationDegrees) + { + ResetViewCmd = new DelegateCommand(ResetView, _ => true); + _defaultAzimuthDegrees = defaultAzimuthDegrees; + _defaultElevationDegrees = defaultElevationDegrees; + ResetView(null); + } + + public void ResetView(object obj) + { + HorizontalDegree = _defaultAzimuthDegrees; + VerticalDegree = _defaultElevationDegrees; + } + + /// + /// Converts the ScottPlot plot image to an Avalonia Bitmap. + /// + /// A plot image, created through ScottPlot.GetImage(width, height) + /// Avlonia Bitmap, which can be used for UI Bindings + public Avalonia.Media.Imaging.Bitmap ToBitmap(ScottPlot.Image plot) + { + if (plot == null) + ClearImage(Resources.NoImg); + + using (MemoryStream ms = new MemoryStream(plot.GetImageBytes())) + { + return new Bitmap(ms); + } + } + + public void ClearImage(string message) + { + BlochImage = ToBitmap(BlochSphereGenerator.GeneratePlaceholder(RenderSize, message)); + StateVector = message; + } +} \ No newline at end of file diff --git a/QuIDE/ViewModels/Controls/CircuitGridViewModel.cs b/QuIDE/ViewModels/Controls/CircuitGridViewModel.cs index 2873c25..4bb9979 100644 --- a/QuIDE/ViewModels/Controls/CircuitGridViewModel.cs +++ b/QuIDE/ViewModels/Controls/CircuitGridViewModel.cs @@ -52,11 +52,11 @@ private void OnQubitsChanged() private readonly ComputerModel _model; + private QubitViewModel _selectedQubit; private ObservableCollection _registers; private ObservableCollection _steps; private int _currentStep; - private double _scaleFactor = 0.75; private readonly double _scaleFactorMax = 4.0; private readonly double _scaleFactorMin = 0.1; @@ -96,6 +96,28 @@ public CircuitGridViewModel(ComputerModel model, DialogManager dialogManager) #region Model Properties + public QubitViewModel SelectedQubit + { + get => _selectedQubit; + set + { + // If the same item is set again, do nothing. + if (_selectedQubit == value) + return; + + // Deselect the old qubit if it exists. + if (_selectedQubit != null) + _selectedQubit.IsSelected = false; + + _selectedQubit = value; + + // Select the new qubit if it's not null. + if (_selectedQubit != null) + _selectedQubit.IsSelected = true; + OnPropertyChanged(nameof(SelectedQubit)); + } + } + public ObservableCollection Registers { get @@ -207,7 +229,7 @@ private ObservableCollection CreateRegistersFromModel() var registers = new ObservableCollection(); for (var i = 0; i < _model.Registers.Count; i++) { - var reg = new RegisterViewModel(_model, i, _dialogManager); + var reg = new RegisterViewModel(_model, i, _dialogManager, this); reg.QubitsChanged += registers_QubitsChanged; registers.Add(reg); } @@ -279,7 +301,7 @@ private void Registers_CollectionChanged(object sender, NotifyCollectionChangedE var newRow = e.NewStartingIndex; if (item is RegisterModel) { - var reg = new RegisterViewModel(_model, newRow, _dialogManager); + var reg = new RegisterViewModel(_model, newRow, _dialogManager, this); reg.QubitsChanged += registers_QubitsChanged; Registers.Insert(newRow, reg); for (var i = newRow + 1; i < _registers.Count; i++) _registers[i].IncrementIndex(); diff --git a/QuIDE/ViewModels/Controls/OutputGridViewModel.cs b/QuIDE/ViewModels/Controls/OutputGridViewModel.cs index f66fda6..8bd37c9 100644 --- a/QuIDE/ViewModels/Controls/OutputGridViewModel.cs +++ b/QuIDE/ViewModels/Controls/OutputGridViewModel.cs @@ -30,6 +30,11 @@ public OutputGridViewModel() _selectedRegister = new ParameterViewModel("register", typeof(Register), "root"); } + public void SetMainViewModel(MainWindowViewModel mainVm) + { + this._mainVM = mainVm; + } + #endregion // Constructor #region Events @@ -44,7 +49,8 @@ private void OnSelectionChanged() #endregion // Events #region Fields - + private MainWindowViewModel _mainVM; + private ComputerModel _model; private OutputViewModel _outputModel; @@ -80,7 +86,6 @@ public OutputState SelectedObject get { if (_selectedIndex >= 0 && _selectedIndex < States.Length) return States[_selectedIndex].Model; - return null; } } @@ -93,6 +98,7 @@ public int SelectedIndex _selectedIndex = value; OnPropertyChanged(nameof(SelectedIndex)); OnSelectionChanged(); + this._mainVM.UpdateBlochSphere(); } } diff --git a/QuIDE/ViewModels/Helpers/QubitViewModel.cs b/QuIDE/ViewModels/Helpers/QubitViewModel.cs index 933bdae..bdf7b41 100644 --- a/QuIDE/ViewModels/Helpers/QubitViewModel.cs +++ b/QuIDE/ViewModels/Helpers/QubitViewModel.cs @@ -7,6 +7,7 @@ using Avalonia.Media; using QuIDE.CodeHelpers; using QuIDE.QuantumModel; +using QuIDE.ViewModels.Controls; using QuIDE.ViewModels.Dialog; using QuIDE.Views.Dialog; @@ -16,7 +17,28 @@ namespace QuIDE.ViewModels.Helpers; public class QubitViewModel : ViewModelBase { - public QubitViewModel(ComputerModel model, int registerIndex, int rowIndex, DialogManager dialogManager) + private bool _isSelected; + private CircuitGridViewModel _topParentVM; + public bool IsSelected + { + get => _isSelected; + set + { + if (_isSelected == value) + return; + + _isSelected = value; + OnPropertyChanged(nameof(IsSelected)); + + if (_isSelected) + _topParentVM.SelectedQubit = this; + else if (_topParentVM.SelectedQubit == this) + _topParentVM.SelectedQubit = null; + + } + } + + public QubitViewModel(ComputerModel model, int registerIndex, int rowIndex, DialogManager dialogManager, CircuitGridViewModel topParentVM) { _model = model; _registerIndex = registerIndex; @@ -25,6 +47,7 @@ public QubitViewModel(ComputerModel model, int registerIndex, int rowIndex, Dial _deleteRegister = new DelegateCommand(DeleteRegister, x => model.Registers.Count > 1); _dialogManager = dialogManager; + _topParentVM = topParentVM; } diff --git a/QuIDE/ViewModels/Helpers/RegisterViewModel.cs b/QuIDE/ViewModels/Helpers/RegisterViewModel.cs index 8959dff..51e1d8c 100644 --- a/QuIDE/ViewModels/Helpers/RegisterViewModel.cs +++ b/QuIDE/ViewModels/Helpers/RegisterViewModel.cs @@ -31,9 +31,9 @@ private void OnQubitsChanged() private readonly ComputerModel _model; private int _registerIndex; - + private ObservableCollection _qubits; - + private readonly CircuitGridViewModel _parentCircuitVM; private readonly DialogManager _dialogManager; #endregion // Fields @@ -41,12 +41,13 @@ private void OnQubitsChanged() #region Constructor - public RegisterViewModel(ComputerModel model, int registerIndex, DialogManager dialogManager) + public RegisterViewModel(ComputerModel model, int registerIndex, DialogManager dialogManager, CircuitGridViewModel parentCircuitVm) { _model = model; _registerIndex = registerIndex; _dialogManager = dialogManager; + _parentCircuitVM = parentCircuitVm; _model.Registers[_registerIndex].Qubits.CollectionChanged += Qubits_CollectionChanged; _model.StepChanged += _model_StepChanged; @@ -57,6 +58,7 @@ public RegisterViewModel(ComputerModel model, int registerIndex, DialogManager d #region Presentation Properties + public ObservableCollection Qubits { get @@ -104,7 +106,7 @@ private ObservableCollection CreateQubitsFromModel() ObservableCollection qubits = new ObservableCollection(); for (int i = 0; i < _model.Registers[_registerIndex].Qubits.Count; i++) { - qubits.Add(new QubitViewModel(_model, _registerIndex, i, _dialogManager)); + qubits.Add(new QubitViewModel(_model, _registerIndex, i, _dialogManager, _parentCircuitVM)); } return qubits; @@ -120,7 +122,7 @@ private void Qubits_CollectionChanged(object sender, NotifyCollectionChangedEven int newRow = e.NewStartingIndex; if (item is not QubitModel) continue; - _qubits.Insert(newRow, new QubitViewModel(_model, _registerIndex, newRow, _dialogManager)); + _qubits.Insert(newRow, new QubitViewModel(_model, _registerIndex, newRow, _dialogManager, _parentCircuitVM)); for (int i = newRow + 1; i < _qubits.Count; i++) { _qubits[i].IncrementRow(); diff --git a/QuIDE/ViewModels/MainWindowViewModel.cs b/QuIDE/ViewModels/MainWindowViewModel.cs index 2cb4cc3..c83217e 100644 --- a/QuIDE/ViewModels/MainWindowViewModel.cs +++ b/QuIDE/ViewModels/MainWindowViewModel.cs @@ -1,6 +1,11 @@ using System; +using System.Collections.Generic; using System.Collections.ObjectModel; +using System.ComponentModel; using System.Linq; +using System.Numerics; +using System.Resources; +using System.Text; using System.Windows.Input; using Avalonia.Controls; using QuIDE.CodeHelpers; @@ -9,9 +14,13 @@ using QuIDE.Views; using QuIDE.Views.Dialog; using CommunityToolkit.Mvvm.Input; +using QuIDE.Properties; using QuIDE.QuantumModel; using QuIDE.QuantumParser; +using QuIDE.ViewModels.Helpers; +using Image = ScottPlot.Image; using Parser = QuIDE.QuantumParser.Parser; +using Register = Quantum.Register; namespace QuIDE.ViewModels; @@ -47,13 +56,16 @@ public CircuitGridViewModel CircuitGrid { get { - if (_circuitGridVM != null) return _circuitGridVM; + if (_circuitGridVM != null) + return _circuitGridVM; _circuitGridVM = new CircuitGridViewModel(_model, _dialogManager); - if (_propertiesVM != null) _propertiesVM.AddSelectionAndQubitsTracing(_circuitGridVM); + if (_propertiesVM != null) + _propertiesVM.AddSelectionAndQubitsTracing(_circuitGridVM); - if (_outputGridVM != null) _outputGridVM.AddQubitsTracing(_circuitGridVM); + if (_outputGridVM != null) + _outputGridVM.AddQubitsTracing(_circuitGridVM); return _circuitGridVM; } @@ -64,9 +76,11 @@ private set _circuitGridVM = value; - if (_propertiesVM != null) _propertiesVM.AddSelectionAndQubitsTracing(_circuitGridVM); + if (_propertiesVM != null) + _propertiesVM.AddSelectionAndQubitsTracing(_circuitGridVM); - if (_outputGridVM != null) _outputGridVM.AddQubitsTracing(_circuitGridVM); + if (_outputGridVM != null) + _outputGridVM.AddQubitsTracing(_circuitGridVM); OnPropertyChanged(nameof(CircuitGrid)); } @@ -80,9 +94,12 @@ public OutputGridViewModel OutputGrid { _outputGridVM = new OutputGridViewModel(); _outputGridVM.LoadModel(_model, _outputModel); - if (_circuitGridVM != null) _outputGridVM.AddQubitsTracing(_circuitGridVM); + _outputGridVM.SetMainViewModel(this); + if (_circuitGridVM != null) + _outputGridVM.AddQubitsTracing(_circuitGridVM); - if (_propertiesVM != null) _propertiesVM.AddSelectionTracing(_outputGridVM); + if (_propertiesVM != null) + _propertiesVM.AddSelectionTracing(_outputGridVM); } return _outputGridVM; @@ -94,9 +111,11 @@ private set _outputGridVM = value; - if (_circuitGridVM != null) _outputGridVM.AddQubitsTracing(_circuitGridVM); + if (_circuitGridVM != null) + _outputGridVM.AddQubitsTracing(_circuitGridVM); - if (_propertiesVM != null) _propertiesVM.AddSelectionTracing(_outputGridVM); + if (_propertiesVM != null) + _propertiesVM.AddSelectionTracing(_outputGridVM); OnPropertyChanged(nameof(OutputGrid)); } @@ -106,12 +125,15 @@ public PropertiesViewModel PropertiesPane { get { - if (_propertiesVM != null) return _propertiesVM; + if (_propertiesVM != null) + return _propertiesVM; _propertiesVM = new PropertiesViewModel(CircuitGrid, OutputGrid); - if (_outputGridVM != null) _propertiesVM.AddSelectionTracing(_outputGridVM); + if (_outputGridVM != null) + _propertiesVM.AddSelectionTracing(_outputGridVM); - if (_circuitGridVM != null) _propertiesVM.AddSelectionAndQubitsTracing(_circuitGridVM); + if (_circuitGridVM != null) + _propertiesVM.AddSelectionAndQubitsTracing(_circuitGridVM); return _propertiesVM; } @@ -122,9 +144,11 @@ private set _propertiesVM = value; - if (_outputGridVM != null) _propertiesVM.AddSelectionTracing(_outputGridVM); + if (_outputGridVM != null) + _propertiesVM.AddSelectionTracing(_outputGridVM); - if (_circuitGridVM != null) _propertiesVM.AddSelectionAndQubitsTracing(_circuitGridVM); + if (_circuitGridVM != null) + _propertiesVM.AddSelectionAndQubitsTracing(_circuitGridVM); OnPropertyChanged(nameof(PropertiesPane)); } @@ -147,7 +171,8 @@ public ObservableCollection CompositeTools { get { - if (_toolsVM != null) return _toolsVM; + if (_toolsVM != null) + return _toolsVM; var eval = CircuitEvaluator.GetInstance(); var dict = eval.GetExtensionGates(); @@ -161,6 +186,8 @@ private set } } + public BlochSphereViewModel BlochSphere { get; private set; } + public static ActionName SelectedAction { get; private set; } public string SelectedComposite @@ -188,6 +215,7 @@ public void InitializeWindow(MainWindow window) // they need dialogManager InitFromModel(ComputerModel.CreateModelForGUI()); + InitBlochSphereView(); // inject dialogManager and notify handler EditorPane = new EditorViewModel(_dialogManager, NotifyEditorDependentCommands); @@ -197,12 +225,40 @@ public void InitializeWindow(MainWindow window) _consoleWriter.TextChanged += _consoleWriter_TextChanged; } + private void InitBlochSphereView() + { + _blochSphereGenerator = new BlochSphereGenerator(); + BlochSphere = new BlochSphereViewModel(_blochSphereGenerator.DefaultAzimuthDegrees, _blochSphereGenerator.DefaultElevationDegrees); + + // when Bloch Sphere sliders are changed or another qubit is selected: + // regenerate Bloch Image with new arguments + BlochSphere.PropertyChanged += BlochSphereOnPropertyChanged; + CircuitGrid.PropertyChanged += CircuitGridOnPropertyChanged; + } + + private void CircuitGridOnPropertyChanged(object sender, PropertyChangedEventArgs e) + { + if (e.PropertyName == nameof(CircuitGridViewModel.SelectedQubit)) + UpdateBlochSphere(); + } + + private void BlochSphereOnPropertyChanged(object sender, PropertyChangedEventArgs e) + { + if (e.PropertyName == nameof(BlochSphere.VerticalDegree) + || e.PropertyName == nameof(BlochSphere.HorizontalDegree) + || e.PropertyName == nameof(BlochSphere.RenderSize)) + { + UpdateBlochSphere(); + } + } + private async void WindowClosing(object sender, WindowClosingEventArgs args) { args.Cancel = true; var canClose = await EditorPane.EditorCanClose(); - if (!canClose) return; + if (!canClose) + return; // Detach self and close with default handler _window.Closing -= WindowClosing; @@ -229,6 +285,7 @@ private async void WindowClosing(object sender, WindowClosingEventArgs args) private ObservableCollection _toolsVM; private string _selectedComposite; + private BlochSphereGenerator _blochSphereGenerator; private ConsoleWriter _consoleWriter; @@ -260,7 +317,8 @@ public ICommand CalculatorCommand { get { - if (_calculatorCommand == null) _calculatorCommand = new DelegateCommand(null, _ => false); + if (_calculatorCommand == null) + _calculatorCommand = new DelegateCommand(null, _ => false); return _calculatorCommand; } @@ -270,7 +328,8 @@ public ICommand AboutCommand { get { - if (_aboutCommand == null) _aboutCommand = new DelegateCommand(ShowAbout, _ => true); + if (_aboutCommand == null) + _aboutCommand = new DelegateCommand(ShowAbout, _ => true); return _aboutCommand; } @@ -280,7 +339,8 @@ public ICommand SelectActionCommand { get { - if (_selectAction == null) _selectAction = new DelegateCommand(SelectAction, x => true); + if (_selectAction == null) + _selectAction = new DelegateCommand(SelectAction, x => true); return _selectAction; } @@ -290,7 +350,8 @@ public ICommand GroupCommand { get { - if (_group == null) _group = new DelegateCommand(MakeComposite, x => true); + if (_group == null) + _group = new DelegateCommand(MakeComposite, x => true); return _group; } @@ -300,7 +361,8 @@ public ICommand ClearCircuitCommand { get { - if (_clearCircuit == null) _clearCircuit = new DelegateCommand(ClearCircuit, x => true); + if (_clearCircuit == null) + _clearCircuit = new DelegateCommand(ClearCircuit, x => true); return _clearCircuit; } @@ -310,7 +372,8 @@ public ICommand CutGatesCommand { get { - if (_cutGates == null) _cutGates = new DelegateCommand(CutGates, x => true); + if (_cutGates == null) + _cutGates = new DelegateCommand(CutGates, x => true); return _cutGates; } @@ -320,7 +383,8 @@ public ICommand CopyGatesCommand { get { - if (_copyGates == null) _copyGates = new DelegateCommand(CopyGates, x => true); + if (_copyGates == null) + _copyGates = new DelegateCommand(CopyGates, x => true); return _copyGates; } @@ -330,7 +394,8 @@ public ICommand PasteGatesCommand { get { - if (_pasteGates == null) _pasteGates = new DelegateCommand(PasteGates, x => true); + if (_pasteGates == null) + _pasteGates = new DelegateCommand(PasteGates, x => true); return _pasteGates; } @@ -340,7 +405,8 @@ public ICommand DeleteGatesCommand { get { - if (_pasteGates == null) _pasteGates = new DelegateCommand(DeleteGates, x => true); + if (_pasteGates == null) + _pasteGates = new DelegateCommand(DeleteGates, x => true); return _pasteGates; } @@ -350,7 +416,8 @@ public ICommand RestartCommand { get { - if (_restart == null) _restart = new DelegateCommand(Restart, x => true); + if (_restart == null) + _restart = new DelegateCommand(Restart, x => true); return _restart; } @@ -360,7 +427,8 @@ public ICommand PrevStepCommand { get { - if (_prevStep == null) _prevStep = new DelegateCommand(PrevStep, x => true); + if (_prevStep == null) + _prevStep = new DelegateCommand(PrevStep, x => true); return _prevStep; } @@ -370,7 +438,8 @@ public ICommand NextStepCommand { get { - if (_nextStep == null) _nextStep = new DelegateCommand(NextStep, x => true); + if (_nextStep == null) + _nextStep = new DelegateCommand(NextStep, x => true); return _nextStep; } @@ -380,7 +449,8 @@ public ICommand RunCommand { get { - if (_run == null) _run = new DelegateCommand(RunToEnd, x => true); + if (_run == null) + _run = new DelegateCommand(RunToEnd, x => true); return _run; } @@ -437,7 +507,8 @@ await _dialogManager.ShowDialogAsync(new CompositeInput(compositeVM), () => _model.MakeComposite(name, toGroup); - if (_toolsVM.Contains(name)) return; + if (_toolsVM.Contains(name)) + return; var newTools = _toolsVM; newTools.Add(name); @@ -486,7 +557,8 @@ private void GenerateFromCode() try { var code = EditorPane.SelectedDocument?.Editor.Document.Text; - if (string.IsNullOrWhiteSpace(code)) throw new NullReferenceException("Code is empty or not existing"); + if (string.IsNullOrWhiteSpace(code)) + throw new NullReferenceException("Code is empty or not existing"); var asmToBuild = parser.CompileForBuild(code); var eval = CircuitEvaluator.GetInstance(); @@ -523,7 +595,8 @@ private void RunInConsole() try { var code = EditorPane.SelectedDocument?.Editor.Document.Text; - if (string.IsNullOrWhiteSpace(code)) throw new NullReferenceException("Code is empty or not existing"); + if (string.IsNullOrWhiteSpace(code)) + throw new NullReferenceException("Code is empty or not existing"); var asm = parser.CompileForRun(code); Parser.Execute(asm, _consoleWriter); @@ -556,6 +629,7 @@ private void Restart(object parameter) _outputModel = eval.InitFromModel(_model); OutputGrid.LoadModel(_model, _outputModel); + UpdateBlochSphere(); } catch (Exception e) { @@ -574,13 +648,16 @@ private void PrevStep(object parameter) } else { - if (!_model.CanStepBack(currentStep - 1)) return; + if (!_model.CanStepBack(currentStep - 1)) + return; var eval = CircuitEvaluator.GetInstance(); var se = eval.GetStepEvaluator(); var outputChanged = se.RunStep(_model.Steps[currentStep - 1].Gates, true); _model.CurrentStep = currentStep - 1; - if (outputChanged) _outputModel.Update(eval.RootRegister); + if (outputChanged) + _outputModel.Update(eval.RootRegister); + UpdateBlochSphere(); } } catch (Exception e) @@ -596,14 +673,18 @@ private void NextStep(object parameter) var eval = CircuitEvaluator.GetInstance(); var currentStep = _model.CurrentStep; - if (currentStep == 0) eval.InitFromModel(_model); + if (currentStep == 0) + eval.InitFromModel(_model); - if (currentStep >= _model.Steps.Count) return; + if (currentStep >= _model.Steps.Count) + return; var se = eval.GetStepEvaluator(); var outputChanged = se.RunStep(_model.Steps[currentStep].Gates); _model.CurrentStep = currentStep + 1; - if (outputChanged) _outputModel.Update(eval.RootRegister); + if (outputChanged) + _outputModel.Update(eval.RootRegister); + UpdateBlochSphere(); } catch (Exception e) { @@ -618,12 +699,15 @@ private void RunToEnd(object parameter) var eval = CircuitEvaluator.GetInstance(); var currentStep = _model.CurrentStep; - if (currentStep == 0) eval.InitFromModel(_model); + if (currentStep == 0) + eval.InitFromModel(_model); var se = eval.GetStepEvaluator(); var outputChanged = se.RunToEnd(_model.Steps, currentStep); _model.CurrentStep = _model.Steps.Count; - if (outputChanged) _outputModel.Update(eval.RootRegister); + if (outputChanged) + _outputModel.Update(eval.RootRegister); + UpdateBlochSphere(); } catch (Exception e) { @@ -650,7 +734,6 @@ private async void ShowAbout(object o) await new AboutWindow().ShowDialog(_window); } - #region Private Helpers private void InitFromModel(ComputerModel model) @@ -663,7 +746,7 @@ private void InitFromModel(ComputerModel model) foreach (var pair in oldComposites.Where(pair => !newComposites.ContainsKey(pair.Key))) newComposites[pair.Key] = pair.Value; } - + try { _model = model; @@ -675,10 +758,9 @@ private void InitFromModel(ComputerModel model) } catch (NullReferenceException) { - SimpleDialogHandler.ShowSimpleMessage("No circuit to build.","Warning"); + SimpleDialogHandler.ShowSimpleMessage("No circuit to build.", "Warning"); ClearCircuit(true); } - } private void _consoleWriter_TextChanged(object sender, EventArgs eventArgs) @@ -689,11 +771,153 @@ private void _consoleWriter_TextChanged(object sender, EventArgs eventArgs) private static void PrintException(Exception e) { var message = e.Message; - if (e.InnerException != null) message = message + ":\n" + e.InnerException.Message; + if (e.InnerException != null) + message = message + ":\n" + e.InnerException.Message; message = message + "\n" + e.StackTrace; SimpleDialogHandler.ShowSimpleMessage(message); } #endregion // Private Helpers + + public void UpdateBlochSphere() + { + (Register qubitSubRegister, bool success, string errorMessage) = GetSelectedQubitRegister(); + + if (success == false) + { + BlochSphere.ClearImage(errorMessage); + return; + } + + RenderBlochSphereImageAndText(qubitSubRegister); + } + + #region BlochSphereHelpers + + /// + /// Retrieves the quantum sub-register for the currently selected qubit. + /// Handles initial null checks for UI elements and parser components. + /// + /// + /// A tuple containing: + /// - The `Quantum.Register` instance for the selected single qubit. + /// - A boolean indicating success. + /// - An error message string if unsuccessful, otherwise null. + /// + private (Register qubitSubRegister, bool success, string errorMessage) GetSelectedQubitRegister() + { + // Get the currently selected qubit from the circuit grid. + QubitViewModel selectedQubit = CircuitGrid?.SelectedQubit; + if (selectedQubit == null) + return (null, false, Resources.NoQubit); // No qubit selected. + + QuantumComputer qc = QuantumComputer.GetInstance(); + QuantumParser.Register parserRegister = qc.FindRegister(selectedQubit.RegisterName); + if (parserRegister == null) + return (null, false, Resources.NoRegisterForQubitFound); + + Register quantumRegister = parserRegister.SourceRegister; + Register qubitSubRegister = quantumRegister[selectedQubit.Index, 1]; + return (qubitSubRegister, true, null); + } + + /// + /// Renders the Bloch sphere image and updates the state vector text based on the qubit's state. + /// This method distinguishes between pure and mixed states, calling the appropriate BlochSphereGenerator method. + /// + /// The single-qubit quantum register model. + private void RenderBlochSphereImageAndText(Register qubitSubRegister) + { + try + { + int imgSize = BlochSphere.RenderSize; + // Check if the render area is too small + if (imgSize < 20) + { + BlochSphere.ClearImage(Resources.AreaTooSmall); + return; + } + + Complex[,] densityMatrix = qubitSubRegister.GetReducedDensityMatrix(); + if (densityMatrix == null) + { + BlochSphere.ClearImage("Could not calculate state"); + return; + } + + _blochSphereGenerator.SetViewpoint(BlochSphere.HorizontalDegree, BlochSphere.VerticalDegree); + ScottPlot.Image plotImg = _blochSphereGenerator.GeneratePlot(densityMatrix, imgSize); + + BlochSphere.BlochImage = BlochSphere.ToBitmap(plotImg); + BlochSphere.StateVector = GetStateVectorTextFromDensityMatrix(densityMatrix); + ; + } + catch (ArgumentException ex) + { + BlochSphere.ClearImage($"Error calculating density matrix: {ex.Message}"); + } + catch (Exception ex) + { + BlochSphere.ClearImage($"Error generating Bloch sphere: {ex.Message}"); + } + } + + private static string GetStateVectorTextFromDensityMatrix(Complex[,] densityMatrix) + { + // First, calculate the Bloch vector components from the density matrix. + // This is the universal representation for any state. + double x = 2 * densityMatrix[0, 1].Real; + double y = 2 * densityMatrix[0, 1].Imaginary; + double z = densityMatrix[0, 0].Real - densityMatrix[1, 1].Real; + + // The squared length of the Bloch vector tells us if the state is pure. + double lengthSquared = Math.Pow(x, 2) + Math.Pow(y, 2) + Math.Pow(z, 2); + + // Use a small tolerance for floating-point comparisons. + const double epsilon = 1e-6; + + StringBuilder stateVectorBuilder; + + if (Math.Abs(lengthSquared - 1.0) < epsilon) + { + // Pure state: We can reconstruct alpha and beta. + Complex alpha, beta; + alpha = new Complex(Math.Sqrt(densityMatrix[0, 0].Real), 0); + + // Handle the edge case where the state is |1>, so alpha is 0. + if (alpha.Magnitude < epsilon) + { + // If alpha is zero, the state is |1⟩. We can choose beta's phase to be 0 as well. + beta = new Complex(1, 0); + } + else + { + // We use ρ₁₀ = β * α* to solve for β. Since α is real, α* = α. + // β = ρ₁₀ / α + beta = densityMatrix[1, 0] / alpha; + } + + stateVectorBuilder = new StringBuilder(); + stateVectorBuilder.AppendLine("State: Pure"); + stateVectorBuilder.AppendLine($"α ≈ {alpha.Real:F3} + {alpha.Imaginary:F3}i"); + stateVectorBuilder.AppendLine($"β ≈ {beta.Real:F3} + {beta.Imaginary:F3}i"); + } + else + { + // Mixed state: There is no single alpha/beta. We display the density matrix elements. + stateVectorBuilder = new StringBuilder(); + stateVectorBuilder.AppendLine("State: Mixed"); + stateVectorBuilder.Append($@"ρ₀₀ = {densityMatrix[0, 0].Real:F3} "); + stateVectorBuilder.AppendLine($@"ρ₀₁ = {densityMatrix[0, 1].Real:F3} + {densityMatrix[0, 1].Imaginary:F3}i"); + stateVectorBuilder.Append($@"ρ₁₀ = {densityMatrix[1, 0].Real:F3} + {densityMatrix[1, 0].Imaginary:F3}i "); + stateVectorBuilder.AppendLine($@"ρ₁₁ = {densityMatrix[1, 1].Real:F3}"); + } + + // Bloch vector. + stateVectorBuilder.AppendLine($"Bloch Vector: (x={x:F3}, y={y:F3}, z={z:F3})"); + return stateVectorBuilder.ToString(); + } + + #endregion BlochSphereHelpers } \ No newline at end of file diff --git a/QuIDE/Views/Controls/BlochSphere.axaml b/QuIDE/Views/Controls/BlochSphere.axaml new file mode 100644 index 0000000..f90dca2 --- /dev/null +++ b/QuIDE/Views/Controls/BlochSphere.axaml @@ -0,0 +1,78 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/QuIDE/Views/Controls/BlochSphere.axaml.cs b/QuIDE/Views/Controls/BlochSphere.axaml.cs new file mode 100644 index 0000000..1de9a06 --- /dev/null +++ b/QuIDE/Views/Controls/BlochSphere.axaml.cs @@ -0,0 +1,101 @@ +using System; +using System.Linq; +using System.Threading; +using Avalonia; +using Avalonia.Controls; +using Avalonia.Input; +using Avalonia.Markup.Xaml; +using Avalonia.Threading; +using QuIDE.CodeHelpers; +using QuIDE.ViewModels.Controls; + +namespace QuIDE.Views.Controls; + +public partial class BlochSphere : UserControl +{ + private bool _isDragging; + private Point _pressPoint; + private int _startAzimuth; + private int _startElevation; + private const double DegreesPerPixel = 0.5; + + public BlochSphere() + { + InitializeComponent(); + this.SizeChanged += OnBlochSphereSizeChanged; + } + + private void OnBlochSphereSizeChanged(object sender, SizeChangedEventArgs e) + { + if (DataContext is not BlochSphereViewModel vm) + return; + + // Use the smaller of the width or height to maintain a square aspect ratio + int newSize = (int)Math.Min(e.NewSize.Width, e.NewSize.Height); + vm.RenderSize = newSize; + } + + private void BlochSphereImage_OnPointerPressed(object sender, PointerPressedEventArgs e) + { + + + if (DataContext is not BlochSphereViewModel vm) + return; + + if (e.ClickCount == 2) + { + vm.ResetView(null); + return; + } + var pt = e.GetCurrentPoint(BlochSphereImage); + if (!pt.Properties.IsLeftButtonPressed) + return; + + _isDragging = true; + _pressPoint = e.GetPosition(BlochSphereImage); + _startAzimuth = vm.HorizontalDegree; + _startElevation = vm.VerticalDegree; + + // Capture pointer so drag continues even if the pointer leaves the image. + e.Pointer.Capture(BlochSphereImage); + + // if (GetVisualRoot() is TopLevel top) + // Cursor = new Cursor(StandardCursorType.SizeAll); + } + + private void BlochSphereImage_OnPointerMoved(object sender, PointerEventArgs e) + { + if (!_isDragging) + return; + if (DataContext is not BlochSphereViewModel vm) + return; + + var p = e.GetPosition(BlochSphereImage); + var dx = p.X - _pressPoint.X; + var dy = p.Y - _pressPoint.Y; + + // Horizontal mouse movement -> azimuth (increase to the right) + // Vertical mouse movement -> elevation (drag up decreases dy, typically increase elevation) + // change - to + to invert direction of rotation. + var newAzimuth = Normalize360(_startAzimuth - (int)(dx * DegreesPerPixel)); + var newElevation = Normalize360(_startElevation - (int)(dy * DegreesPerPixel)); + + vm.HorizontalDegree = newAzimuth; + vm.VerticalDegree = newElevation; + } + + private void BlochSphereImage_OnPointerReleased(object sender, PointerReleasedEventArgs e) + { + if (!_isDragging) + return; + + _isDragging = false; + e.Pointer.Capture(null); + } + + private int Normalize360(int degrees) + { + // keep 0 mapped to 360 for consistency. + return BlochSphereGenerator.Mod360(degrees); + } +} \ No newline at end of file diff --git a/QuIDE/Views/Controls/CircuitGrid.axaml b/QuIDE/Views/Controls/CircuitGrid.axaml index db3f597..5685222 100644 --- a/QuIDE/Views/Controls/CircuitGrid.axaml +++ b/QuIDE/Views/Controls/CircuitGrid.axaml @@ -62,7 +62,6 @@ @@ -90,50 +89,53 @@ + + - + - + - + - + - + - @@ -259,7 +261,7 @@ - /// GateButton /// event - private void GateButton_MouseDown(object sender, PointerPressedEventArgs e) + private async void GateButton_MouseDown(object sender, PointerPressedEventArgs e) { var source = sender as Control; - if (e.GetCurrentPoint(source).Properties.IsRightButtonPressed) return; - - var shiftPressed = e.KeyModifiers == KeyModifiers.Shift; + if (e.GetCurrentPoint(source).Properties.IsRightButtonPressed) + return; + var shiftPressed = e.KeyModifiers.HasFlag(KeyModifiers.Shift); var vm = source.DataContext as GateViewModel; // perform drawing operation for control gate - if (MainWindowViewModel.SelectedAction == ActionName.Control && !shiftPressed) + ActionName action = MainWindowViewModel.SelectedAction; + if (action == ActionName.Control && !shiftPressed) { - var button = source as Button; + ExecuteDrawingControlGate(source); + await Dispatcher.UIThread.InvokeAsync(() => { }, DispatcherPriority.Render); // ensure visuals render before starting to drag + + var data = new Tuple(vm.Column, vm.Row); + var dragData = new DataObject(); + dragData.Set(typeof(Tuple).ToString(), data); + try + { + await DragDrop.DoDragDrop(e, dragData, DragDropEffects.Link); + } + catch (COMException exception) + { + var msg = exception.Message; + Console.WriteLine(msg); + // SimpleDialogHandler.ShowSimpleMessage(msg); + } + finally + { + CleanupDragVisual(); + } + } - var coordinates = new Point(0, 0).Transform(button.TransformToVisual(drawing) - .GetValueOrDefault()) / (DataContext as CircuitGridViewModel).ScaleFactor; + if (action == ActionName.Selection) + { + ExecuteSingleClickSelection(e, vm); + return; + } - const double diameter = 12; + // For all other actions (single-qubit gates, Measure, CPhaseShift/InvCPhaseShift, Composite, Ungroup): + // Enable single-click placement. If the pointer moves, start a drag (for tools that support cross-row spans). + _pendingClick = true; + _pendingVm = vm; + _pendingModifiers = e.KeyModifiers; + _pendingSource = source; + _pressPoint = e.GetPosition(source); + AttachPendingHandlers(source); + } - var centerX = coordinates.X + 0.5 * CircuitGridViewModel.GateWidth; - var centerY = coordinates.Y + 0.5 * CircuitGridViewModel.QubitSize; + private void ExecuteSingleClickSelection(PointerPressedEventArgs e, GateViewModel vm) + { + // single click selection + vm.SetGate(vm.Column, vm.Row, e.KeyModifiers); + if (DataContext is CircuitGridViewModel circuitVm) + circuitVm.SelectedObject = vm; - var ctrlPoint = new Ellipse - { - Width = diameter, - Height = diameter, - Fill = _drawingColor, - Stroke = _drawingColor, - StrokeThickness = 1 - }; + // Start drag immediately to allow range selection + var dataSelection = new Tuple(vm.Column, vm.Row); + var dragDataSelection = new DataObject(); + dragDataSelection.Set(typeof(Tuple).ToString(), dataSelection); + + try + { + DragDrop.DoDragDrop(e, dragDataSelection, DragDropEffects.Link); + } + catch (COMException ex) + { + Console.WriteLine(ex); + } - ctrlPoint.SetValue(DragDrop.AllowDropProperty, true); //AllowDrop = true; - ctrlPoint.AddHandler(DragDrop.DropEvent, ctrlPoint_Drop); + return; + } - drawing.Children.Add(ctrlPoint); - Canvas.SetTop(ctrlPoint, centerY - 0.5 * diameter); - Canvas.SetLeft(ctrlPoint, centerX - 0.5 * diameter); + private void ExecuteDrawingControlGate(Control source) + { + var button = source as Button; - _line = new Line - { - StartPoint = new Point(centerX, centerY), - EndPoint = new Point(centerX, centerY), - Stroke = _drawingColor, - StrokeThickness = 1 - }; + var coordinates = new Point(0, 0).Transform(button.TransformToVisual(drawing) + .GetValueOrDefault()) / (DataContext as CircuitGridViewModel).ScaleFactor; - drawing.Children.Add(_line); - } + const double diameter = 12; + + var centerX = coordinates.X + 0.5 * CircuitGridViewModel.GateWidth; + var centerY = coordinates.Y + 0.5 * CircuitGridViewModel.QubitSize; - // fetch grid with gates to draw - var data = new Tuple(vm.Column, vm.Row); + var ctrlPoint = new Ellipse + { + Width = diameter, + Height = diameter, + Fill = _drawingColor, + Stroke = _drawingColor, + StrokeThickness = 1 + }; + + // ctrlPoint.SetValue(DragDrop.AllowDropProperty, true); //AllowDrop = true; + // ctrlPoint.AddHandler(DragDrop.DropEvent, ctrlPoint_Drop); + + drawing.Children.Add(ctrlPoint); + Canvas.SetTop(ctrlPoint, centerY - 0.5 * diameter); + Canvas.SetLeft(ctrlPoint, centerX - 0.5 * diameter); + + _line = new Line + { + StartPoint = new Point(centerX, centerY), + EndPoint = new Point(centerX, centerY), + Stroke = _drawingColor, + StrokeThickness = 1 + }; - var dragData = new DataObject(); - dragData.Set(typeof(Tuple).ToString(), data); + drawing.Children.Add(_line); + } + + private void AttachPendingHandlers(Control source) + { + source.AddHandler(InputElement.PointerMovedEvent, Pending_PointerMoved, Avalonia.Interactivity.RoutingStrategies.Tunnel); + source.AddHandler(InputElement.PointerReleasedEvent, Pending_PointerReleased, Avalonia.Interactivity.RoutingStrategies.Tunnel); + } + + private void DetachPendingHandlers() + { + if (_pendingSource is null) + return; + + _pendingSource.RemoveHandler(InputElement.PointerMovedEvent, Pending_PointerMoved); + _pendingSource.RemoveHandler(InputElement.PointerReleasedEvent, Pending_PointerReleased); + _pendingSource = null; + } + private void ClearPending() + { + _pendingClick = false; + _pendingVm = null; + _pendingModifiers = KeyModifiers.None; + _pressPoint = default; + + DetachPendingHandlers(); + } + + private void Pending_PointerMoved(object sender, PointerEventArgs e) + { + if (!_pendingClick || _pendingVm is null || _pendingSource is null) + return; + + var curr = e.GetPosition(_pendingSource); + var dx = curr.X - _pressPoint.X; + var dy = curr.Y - _pressPoint.Y; + if ((dx * dx + dy * dy) < DragThreshold * DragThreshold) + return; + + // movement exceeded threshold -> start drag try { - DragDrop.DoDragDrop(e, dragData,DragDropEffects.Link); + var data = new Tuple(_pendingVm.Column, _pendingVm.Row); + var dragData = new DataObject(); + dragData.Set(typeof(Tuple).ToString(), data); + _pendingClick = false; // this is a drag, not a click + + // Start drag from this move event (PointerEventArgs is acceptable) + DragDrop.DoDragDrop(e, dragData, DragDropEffects.Link); } - catch (COMException exception) + catch (COMException ex) { - var msg = exception.Message; - Console.WriteLine(exception); - // SimpleDialogHandler.ShowSimpleMessage(msg); + Console.WriteLine(ex); + } + finally + { + ClearPending(); } } - private void ctrlPoint_Drop(object sender, PointerEventArgs pointerEventArgs) + private void Pending_PointerReleased(object sender, PointerReleasedEventArgs e) { - _line = null; - drawing.Children.Clear(); + if (!_pendingClick || _pendingVm is null) + { + ClearPending(); + return; + } + + // No drag was started -> treat as click + try + { + _pendingVm.SetGate(_pendingVm.Column, _pendingVm.Row, _pendingModifiers); + // Select the cell/gate after click placement + if (DataContext is CircuitGridViewModel circuitVM) + circuitVM.SelectedObject = _pendingVm; + } + finally + { + ClearPending(); + } + } + + private void ctrlPoint_Drop(object sender, PointerEventArgs pointerEventArgs) => CleanupDragVisual(); + + private void Drawing_DragOver(object sender, DragEventArgs e) + { + if (_line == null) + return; + var scaleFactor = (DataContext as CircuitGridViewModel)?.ScaleFactor ?? 1.0; + var offset = new Vector(-10, 4); + var mouse = (e.GetPosition(drawing) + offset) / scaleFactor; + _line.EndPoint = new Point(mouse.X, mouse.Y); + e.Handled = true; } - private void Drawing_Drop(object sender, DragEventArgs e) + private void Drawing_Drop(object sender, DragEventArgs e) => CleanupDragVisual(); + + private void CleanupDragVisual() { _line = null; drawing.Children.Clear(); + if (this.GetVisualRoot() is TopLevel top) + top.Cursor = null; } private void GateButton_DragEnter(object sender, DragEventArgs e) @@ -132,7 +282,8 @@ private void GateButton_DragEnter(object sender, DragEventArgs e) private void GateButton_DragOver(object sender, DragEventArgs e) { - if (_line == null) return; + if (_line == null) + return; var scaleFactor = (DataContext as CircuitGridViewModel).ScaleFactor; @@ -153,14 +304,12 @@ private void GateButton_Drop(object sender, DragEventArgs e) if (!e.Data.Contains(dataFormat)) return; - var data = - e.Data.Get(dataFormat) as Tuple; + var data = e.Data.Get(dataFormat) as Tuple; var vm = target.DataContext as GateViewModel; vm.SetGate(data.Item1, data.Item2, e.KeyModifiers); - _line = null; - drawing.Children.Clear(); + CleanupDragVisual(); var circuitVM = DataContext as CircuitGridViewModel; circuitVM.SelectedObject = vm; @@ -177,12 +326,14 @@ private void GatesScroll_ScrollChanged(object sender, ScrollChangedEventArgs e) // if added step var extentWidthChange = e.ExtentDelta.X; - if (!(extentWidthChange > 0)) return; + if (!(extentWidthChange > 0)) + return; var circuitVM = DataContext as CircuitGridViewModel; var addedColumn = circuitVM.LastStepAdded; - if (addedColumn <= 0) return; + if (addedColumn <= 0) + return; // if newly added step is not fully visible var scrollNeeded = extentWidthChange * (addedColumn + 1) - GatesScroll.Offset.X - diff --git a/QuIDE/Views/Dialog/AboutWindow.axaml b/QuIDE/Views/Dialog/AboutWindow.axaml index 5213b83..247c8ed 100644 --- a/QuIDE/Views/Dialog/AboutWindow.axaml +++ b/QuIDE/Views/Dialog/AboutWindow.axaml @@ -11,7 +11,7 @@ - + diff --git a/QuIDE/Views/Dialog/AboutWindow.axaml.cs b/QuIDE/Views/Dialog/AboutWindow.axaml.cs index 57e2b44..5ac03da 100644 --- a/QuIDE/Views/Dialog/AboutWindow.axaml.cs +++ b/QuIDE/Views/Dialog/AboutWindow.axaml.cs @@ -1,5 +1,6 @@ #region +using System.Reflection; using Avalonia.Controls; #endregion @@ -11,5 +12,8 @@ public partial class AboutWindow : Window public AboutWindow() { InitializeComponent(); + this.txtVersion.Text = typeof(App).Assembly + .GetCustomAttribute()? + .Version; } } \ No newline at end of file diff --git a/QuIDE/Views/MainWindow.axaml b/QuIDE/Views/MainWindow.axaml index 13ac8a3..05e49fc 100644 --- a/QuIDE/Views/MainWindow.axaml +++ b/QuIDE/Views/MainWindow.axaml @@ -6,7 +6,7 @@ xmlns:p="clr-namespace:QuIDE.Properties" xmlns:controls="clr-namespace:QuIDE.Views.Controls" xmlns:drawing="clr-namespace:System.Drawing;assembly=System.Drawing.Primitives" - mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450" + mc:Ignorable="d" d:DesignWidth="1000" d:DesignHeight="800" x:Class="QuIDE.Views.MainWindow" x:DataType="vm:MainWindowViewModel" Title="QuIDE - Quantum Integrated Development Environment" @@ -94,7 +94,7 @@ - + @@ -111,6 +111,9 @@ Margin="0" VerticalAlignment="Stretch" /> + + + diff --git a/Quantum/DensityMatrixCalculator.cs b/Quantum/DensityMatrixCalculator.cs new file mode 100644 index 0000000..ee7ed65 --- /dev/null +++ b/Quantum/DensityMatrixCalculator.cs @@ -0,0 +1,188 @@ +using System; +using System.Collections.Concurrent; +using System.Collections.Generic; +using System.Numerics; +using System.Threading.Tasks; + +namespace Quantum; + +public static class DensityMatrixCalculator +{ + private const int ParallelizationThreshold = 14; + + public static Complex[,] Calculate(IReadOnlyDictionary amplitudes, int totalWidth, int targetQubitOffset) + { + return totalWidth < ParallelizationThreshold + ? CalculateSequential(amplitudes, totalWidth, targetQubitOffset) + : CalculateParallel(amplitudes, totalWidth, targetQubitOffset); + } + + private static Complex[,] CalculateSequential(IReadOnlyDictionary amplitudes, int totalWidth, int targetQubitOffset) + { + // Initialize the components of the 2x2 density matrix. + // rho10 is complex conjugate of 01 and implicitly clear + Complex rho00 = Complex.Zero; + Complex rho11 = Complex.Zero; + Complex rho01 = Complex.Zero; + + // Create a mask to isolate the target qubit's bit. + ulong targetMask = (ulong)1 << targetQubitOffset; + + // Create a mask for all bits in the root register, except the target qubit. + // This is used to identify the 'rest of the system' state. + ulong restOfSystemMask = ~targetMask & ((1UL << totalWidth) - 1UL); + + // Dictionaries to store amplitudes of the 'rest of the system' corresponding to the target qubit being 0 or 1. + // These are used to calculate the off-diagonal elements (coherences). + Dictionary amplitudesOfRestWithQubit0 = new Dictionary(); + Dictionary amplitudesOfRestWithQubit1 = new Dictionary(); + + // diagonals (rho00 and rho11) + // Iterate through all basis states and their amplitudes in the full system state vector. + foreach (var entry in amplitudes) + { + ulong fullState = entry.Key; + Complex amplitude = entry.Value; + + // Determine if the target qubit is 0 or 1 in the current full state. + bool targetQubitIsOne = (fullState & targetMask) != 0; + + // Extract the state of the 'rest of the system' by masking out the target qubit's bit. + ulong restState = fullState & restOfSystemMask; + + if (targetQubitIsOne) + { + // add probability contribution to rho11. + rho11 += Complex.Pow(amplitude.Magnitude, 2); + + // Store the amplitude associated with this 'rest of system' state and target qubit 1. + amplitudesOfRestWithQubit1[restState] = amplitude; + } + else + { + // add probability contribution to rho00. + rho00 += Complex.Pow(amplitude.Magnitude, 2); + + // Store the amplitude associated with this 'rest of system' state and target qubit 0. + amplitudesOfRestWithQubit0[restState] = amplitude; + } + } + + // Calculate the off-diagonal elements (rho01). + // This sums the products of amplitudes (alpha_0 * conj(alpha_1)) for all matching 'rest of system' states. + foreach (var entry in amplitudesOfRestWithQubit0) + { + ulong restState = entry.Key; + Complex ampFor0 = entry.Value; + + // If a corresponding amplitude exists where the target qubit is 1 and the rest of the system is the same, + // then contribute to the coherence. + if (amplitudesOfRestWithQubit1.TryGetValue(restState, out Complex ampFor1)) + rho01 += ampFor0 * Complex.Conjugate(ampFor1); + } + + return AssembleAndNormalizeDensityMatrix(rho00, rho11, rho01); + } + + private static Complex[,] CalculateParallel(IReadOnlyDictionary amplitudes, int totalWidth, int targetQubitOffset) + { + Complex rho00 = Complex.Zero; + Complex rho11 = Complex.Zero; + Complex rho01 = Complex.Zero; + + ulong targetMask = (ulong)1 << targetQubitOffset; + ulong restOfSystemMask = ~targetMask & ((1UL << totalWidth) - 1UL); + + // Use ConcurrentDictionary for thread-safe writes. + var amplitudesOfRestWithQubit0 = new ConcurrentDictionary(); + var amplitudesOfRestWithQubit1 = new ConcurrentDictionary(); + + object _lock = new object(); + + // Parallelize the first loop to calculate diagonal elements and populate dictionaries. + // Use thread-local storage for the sums to avoid locking inside the loop. + // Rach thread gets its own private tuple for summing. + Parallel.ForEach(amplitudes, () => (localRho00: Complex.Zero, localRho11: Complex.Zero), + (entry, loopState, localSums) => + { + ulong fullState = entry.Key; + Complex amplitude = entry.Value; + bool targetQubitIsOne = (fullState & targetMask) != 0; + ulong restState = fullState & restOfSystemMask; + + if (targetQubitIsOne) + { + // Add to the thread's private sum + localSums.localRho11 += Complex.Pow(amplitude.Magnitude, 2); + amplitudesOfRestWithQubit1[restState] = amplitude; + } + else + { + localSums.localRho00 += Complex.Pow(amplitude.Magnitude, 2); + amplitudesOfRestWithQubit0[restState] = amplitude; + } + + return localSums; // Pass the updated local sums to the next iteration for this thread. + }, + + (localSums) => + { + lock (_lock) + { + rho00 += localSums.localRho00; + rho11 += localSums.localRho11; + } + } + ); + + + Parallel.ForEach( + amplitudesOfRestWithQubit0, + () => Complex.Zero, // Thread-local sum for rho01 + (entry, loopState, localRho01) => + { + ulong restState = entry.Key; + Complex ampFor0 = entry.Value; + if (amplitudesOfRestWithQubit1.TryGetValue(restState, out Complex ampFor1)) + localRho01 += ampFor0 * Complex.Conjugate(ampFor1); + + return localRho01; + }, + (localRho01) => + { + lock (_lock) + { + rho01 += localRho01; + } + } + ); + + return AssembleAndNormalizeDensityMatrix(rho00, rho11, rho01); + } + + + private static Complex[,] AssembleAndNormalizeDensityMatrix(Complex rho00, Complex rho11, Complex rho01) + { + Complex[,] densityMatrix = new Complex[2, 2]; + densityMatrix[0, 0] = rho00; + densityMatrix[1, 1] = rho11; + densityMatrix[0, 1] = rho01; + densityMatrix[1, 0] = Complex.Conjugate(rho01); // rho10 is the complex conjugate of rho01. + + // A density matrix must have a trace of 1. Normalize if necessary. + double trace = densityMatrix[0, 0].Real + densityMatrix[1, 1].Real; + if (Math.Abs(trace - 1.0) > QuantumComputer.Epsilon) + { + // Avoid division by zero if trace is somehow zero + if (Math.Abs(trace) > QuantumComputer.Epsilon) + { + Complex invTrace = new Complex(1.0 / trace, 0); + densityMatrix[0, 0] *= invTrace; + densityMatrix[1, 1] *= invTrace; + densityMatrix[0, 1] *= invTrace; + densityMatrix[1, 0] *= invTrace; + } + } + return densityMatrix; + } +} \ No newline at end of file diff --git a/Quantum/Register.cs b/Quantum/Register.cs index acd6c33..f0f5648 100644 --- a/Quantum/Register.cs +++ b/Quantum/Register.cs @@ -19,1354 +19,1462 @@ You should have received a copy of the GNU General Public License */ using System; +using System.Collections.Concurrent; using System.Collections.Generic; using System.Collections.ObjectModel; using System.Linq; using System.Numerics; using System.Text; +using System.Threading.Tasks; +using Quantum; using Quantum.Helpers; -namespace Quantum +namespace Quantum; + +/// +/// The basic unit needed for performing quantum computation. +/// +/// +/// QuantumComputer may have zero to many Registers, but at least one is required to perform quantum +/// computations. +/// Register consist of qubits, which are targets for quantum gates. +/// To apply a quantum gate, use methods of Register class (for computation on its qubits) +/// or extension methods of QuantumComputer class (for computation on qubits in different registers; see +/// ). +/// +/// +/// Register is not an independent class. It could be created and deleted only by +/// . +/// +/// +/// +/// +/// +public class Register : IRegister { - /// - /// The basic unit needed for performing quantum computation. - /// - /// - /// QuantumComputer may have zero to many Registers, but at least one is required to perform quantum - /// computations. - /// Register consist of qubits, which are targets for quantum gates. - /// To apply a quantum gate, use methods of Register class (for computation on its qubits) - /// or extension methods of QuantumComputer class (for computation on qubits in different registers; see - /// ). - /// - /// - /// Register is not an independent class. It could be created and deleted only by - /// . - /// - /// - /// - /// - /// - public class Register : IRegister - { - #region Internal Properties + #region Internal Properties - internal Register Root { get; private set; } + internal Register Root { get; private set; } - #endregion //Internal Properties + #endregion //Internal Properties - #region Operators + #region Operators - /// - /// For completeness. The Register could be implicitly casted to the reference to its first qubit. - /// Like in arrays, where an address of an array is also the address of its first element. - /// - /// Register to cast. - /// Reference to first qubit in register. - public static implicit operator RegisterRef(Register register) - { - return register[0]; - } + /// + /// For completeness. The Register could be implicitly casted to the reference to its first qubit. + /// Like in arrays, where an address of an array is also the address of its first element. + /// + /// Register to cast. + /// Reference to first qubit in register. + public static implicit operator RegisterRef(Register register) + { + return register[0]; + } - #endregion // Operators + #endregion // Operators - #region private fields + #region private fields - private Dictionary _amplitudes; + private Dictionary _amplitudes; - private List _childRegisters; + private List _childRegisters; - private Random _random; + private Random _random; - #endregion + #endregion - #region Index Properties + #region Index Properties - /// - /// Returns reference to single qubit in register. - /// - /// Describes qubit's offset in this Register. Index = 0 means the Least Significant Bit. - /// Returns reference to single qubit in register. - public RegisterRef this[int index] => - new() - { - Register = this, - Offset = index - }; - - /// - /// Returns a sub-register, which begins at given offset and has given width. - /// - /// - /// Begin of the sub-register. Offset = 0 indicates the Least Significant Bit of base register. Offset - /// must be non-negative integer. - /// - /// Width of the sub-register. Must be positive integer. - /// Register, which is a part of base register, of given offset and width. - public Register this[int offset, int width] + /// + /// Returns reference to single qubit in register. + /// + /// Describes qubit's offset in this Register. Index = 0 means the Least Significant Bit. + /// Returns reference to single qubit in register. + public RegisterRef this[int index] => + new() { - get - { - var toReturn = new Register(width, OffsetToRoot + offset, Root); - Root._childRegisters.Add(toReturn); - return toReturn; - } + Register = this, + Offset = index + }; + + /// + /// Returns a sub-register, which begins at given offset and has given width. + /// + /// + /// Begin of the sub-register. Offset = 0 indicates the Least Significant Bit of base register. Offset + /// must be non-negative integer. + /// + /// Width of the sub-register. Must be positive integer. + /// Register, which is a part of base register, of given offset and width. + public Register this[int offset, int width] + { + get + { + var toReturn = new Register(width, OffsetToRoot + offset, Root); + Root._childRegisters.Add(toReturn); + return toReturn; } + } - #endregion //Index Properties + #endregion //Index Properties - #region Public Properties + #region Public Properties - /// - /// The number of qubits contained in Register. - /// - public int Width { get; private set; } + /// + /// The number of qubits contained in Register. + /// + public int Width { get; private set; } - /// - /// Right after creation, register is independent and its OffsetToRoot equals 0. - /// But after operations on many registers, included this, Register could be entangled with others and is no longer - /// independent. - /// The group of entangled registers has its RootRegister. Each register in such group is de facto a part of the Root. - /// OffsetToRoot describes offset (from Least Significant Bit, which offset is 0) of the part in RootRegister. - /// - public int OffsetToRoot { get; private set; } + /// + /// Right after creation, register is independent and its OffsetToRoot equals 0. + /// But after operations on many registers, included this, Register could be entangled with others and is no longer + /// independent. + /// The group of entangled registers has its RootRegister. Each register in such group is de facto a part of the Root. + /// OffsetToRoot describes offset (from Least Significant Bit, which offset is 0) of the part in RootRegister. + /// + public int OffsetToRoot { get; private set; } - #endregion //Public Properties + #endregion //Public Properties - #region Constructors + #region Constructors - internal Register(ulong initval, int width, Random random, int? size = null) - { - Root = this; - OffsetToRoot = 0; - Width = width; - _amplitudes = size.HasValue - ? new Dictionary(size.Value) - : - //this.amplitudes = new SortedDictionary(); - //this.amplitudes = new ConcurrentDictionary(); - new Dictionary(); + internal Register(ulong initval, int width, Random random, int? size = null) + { + Root = this; + OffsetToRoot = 0; + Width = width; + _amplitudes = size.HasValue + ? new Dictionary(size.Value) + : //this.amplitudes = new SortedDictionary(); //this.amplitudes = new ConcurrentDictionary(); - _amplitudes[initval] = 1; - _random = random; - _childRegisters = new List(); - } + new Dictionary(); + //this.amplitudes = new SortedDictionary(); + //this.amplitudes = new ConcurrentDictionary(); + _amplitudes[initval] = 1; + _random = random; + _childRegisters = new List(); + } - internal Register(IDictionary initStates, int width, Random random, int? size = null) + internal Register(IDictionary initStates, int width, Random random, int? size = null) + { + var sum = initStates.Values.Sum(amplitude => Math.Pow(amplitude.Magnitude, 2)); + var limit = 1.0 / ((ulong)1 << Width) * QuantumComputer.Epsilon; + if (Math.Abs(sum - 1.0) > limit) { - var sum = initStates.Values.Sum(amplitude => Math.Pow(amplitude.Magnitude, 2)); - var limit = 1.0 / ((ulong)1 << Width) * QuantumComputer.Epsilon; - if (Math.Abs(sum - 1.0) > limit) - { - var sqrtSum = Math.Sqrt(sum); - var states = initStates.Keys.ToArray(); - //we need to normalize - foreach (var s in states) initStates[s] /= sqrtSum; - } + var sqrtSum = Math.Sqrt(sum); + var states = initStates.Keys.ToArray(); + //we need to normalize + foreach (var s in states) + initStates[s] /= sqrtSum; + } - Root = this; - OffsetToRoot = 0; - Width = width; - _amplitudes = size.HasValue - ? new Dictionary(size.Value) - : - //this.amplitudes = new SortedDictionary(); - //this.amplitudes = new ConcurrentDictionary(); - new Dictionary(); + Root = this; + OffsetToRoot = 0; + Width = width; + _amplitudes = size.HasValue + ? new Dictionary(size.Value) + : //this.amplitudes = new SortedDictionary(); //this.amplitudes = new ConcurrentDictionary(); - foreach (var pair in initStates) _amplitudes.Add(pair.Key, pair.Value); - _random = random; - _childRegisters = new List(); - } + new Dictionary(); + //this.amplitudes = new SortedDictionary(); + //this.amplitudes = new ConcurrentDictionary(); + foreach (var pair in initStates) + _amplitudes.Add(pair.Key, pair.Value); + _random = random; + _childRegisters = new List(); + } - // only used in QuantumComputer, GetRootRegister(...) - internal Register(Random random) - { - Root = this; - OffsetToRoot = 0; - Width = 0; - _amplitudes = null; - _random = random; - _childRegisters = new List(); - } + // only used in QuantumComputer, GetRootRegister(...) + internal Register(Random random) + { + Root = this; + OffsetToRoot = 0; + Width = 0; + _amplitudes = null; + _random = random; + _childRegisters = new List(); + } - // only used in property this[int, int] - private Register(int width, int offsetToRoot, Register root) - { - Root = root; - OffsetToRoot = offsetToRoot; - Width = width; - } + // only used in property this[int, int] + private Register(int width, int offsetToRoot, Register root) + { + Root = root; + OffsetToRoot = offsetToRoot; + Width = width; + } - #endregion + #endregion - #region Internal methods + #region Internal methods - // only used in QuantumComputer, GetRootRegister(...) - internal void AddRegister(Register register) + // only used in QuantumComputer, GetRootRegister(...) + internal void AddRegister(Register register) + { + if (Root == register.Root || Root == null || register.Root == null) + return; + if (Root != this) { - if (Root == register.Root || Root == null || register.Root == null) return; - if (Root != this) - { - Root.AddRegister(register); - return; - } + Root.AddRegister(register); + return; + } - var rootToAdd = register.Root; - var newAmplitudes = rootToAdd.RemoveAmplitudes(); - var newChildren = rootToAdd.RemoveChildren(); - newChildren.Add(rootToAdd); - foreach (var child in newChildren) - { - child.AppendToRoot(this); - _childRegisters.Add(child); - } + var rootToAdd = register.Root; + var newAmplitudes = rootToAdd.RemoveAmplitudes(); + var newChildren = rootToAdd.RemoveChildren(); + newChildren.Add(rootToAdd); + foreach (var child in newChildren) + { + child.AppendToRoot(this); + _childRegisters.Add(child); + } - var oldWidth = Width; - Width += rootToAdd.Width; + var oldWidth = Width; + Width += rootToAdd.Width; - if (_amplitudes == null) + if (_amplitudes == null) + { + _amplitudes = newAmplitudes; + } + else + { + ulong newState; + if (newAmplitudes.Count == 1) { - _amplitudes = newAmplitudes; + var stateToAdd = newAmplitudes.Keys.FirstOrDefault(); + // if stateToAdd == 0, there is nothing more to do + if (stateToAdd <= 0) + return; + var states = _amplitudes.Keys.ToArray(); + var length = states.Length; + for (var k = 0; k < length; k++) + { + var state = states[k]; + // add on the side of Most Significant Bit + newState = (stateToAdd << oldWidth) | state; + _amplitudes[newState] = _amplitudes[state]; + // remove old state: + _amplitudes.Remove(state); + } } else { - ulong newState; - if (newAmplitudes.Count == 1) - { - var stateToAdd = newAmplitudes.Keys.FirstOrDefault(); - // if stateToAdd == 0, there is nothing more to do - if (stateToAdd <= 0) return; - var states = _amplitudes.Keys.ToArray(); - var length = states.Length; + var states = _amplitudes.Keys.ToArray(); + var length = states.Length; + var zeroExist = newAmplitudes.TryGetValue(0, out var zeroAmplitude); + if (zeroExist) + newAmplitudes[0] = 1; + foreach (var pair in newAmplitudes) for (var k = 0; k < length; k++) { var state = states[k]; // add on the side of Most Significant Bit - newState = (stateToAdd << oldWidth) | state; - _amplitudes[newState] = _amplitudes[state]; + newState = (pair.Key << oldWidth) | state; + _amplitudes[newState] = _amplitudes[state] * pair.Value; + } + + if (zeroExist) + for (var k = 0; k < length; k++) + { + var state = states[k]; + _amplitudes[state] = _amplitudes[state] * zeroAmplitude; + } + else + for (var k = 0; k < length; k++) + { + var state = states[k]; // remove old state: _amplitudes.Remove(state); } - } - else - { - var states = _amplitudes.Keys.ToArray(); - var length = states.Length; - var zeroExist = newAmplitudes.TryGetValue(0, out var zeroAmplitude); - if (zeroExist) newAmplitudes[0] = 1; - foreach (var pair in newAmplitudes) - for (var k = 0; k < length; k++) - { - var state = states[k]; - // add on the side of Most Significant Bit - newState = (pair.Key << oldWidth) | state; - _amplitudes[newState] = _amplitudes[state] * pair.Value; - } - - if (zeroExist) - for (var k = 0; k < length; k++) - { - var state = states[k]; - _amplitudes[state] = _amplitudes[state] * zeroAmplitude; - } - else - for (var k = 0; k < length; k++) - { - var state = states[k]; - // remove old state: - _amplitudes.Remove(state); - } - } } } + } - private Dictionary RemoveAmplitudes() - { - var tmp = _amplitudes; - _amplitudes = null; - return tmp; - } - private List RemoveChildren() - { - var tmp = _childRegisters; - _childRegisters = null; - return tmp; - } + + + private Dictionary RemoveAmplitudes() + { + var tmp = _amplitudes; + _amplitudes = null; + return tmp; + } - private void AppendToRoot(Register root) - { - Root = root.Root; - OffsetToRoot += root.Width; - _random = null; - } + private List RemoveChildren() + { + var tmp = _childRegisters; + _childRegisters = null; + return tmp; + } - private void DetachRoot() - { - Root = null; - } + private void AppendToRoot(Register root) + { + Root = root.Root; + OffsetToRoot += root.Width; + _random = null; + } - internal void Delete() - { - if (Root != this) - { - Root.Delete(this); - } - else - { - _amplitudes = null; - foreach (var child in _childRegisters) child.DetachRoot(); - _childRegisters = null; - } + private void DetachRoot() + { + Root = null; + } - Root = null; - OffsetToRoot = -1; - Width = 0; - _random = null; + internal void Delete() + { + if (Root != this) + { + Root.Delete(this); } - - private void Delete(Register toDelete) + else { - // before deletion, register must be measured - // to remove any entanglements - var measured = toDelete.Measure(); - - var offsetToRemove = toDelete.OffsetToRoot; - var widthToRemove = toDelete.Width; - - var newChildren = new List(); - - foreach (var reg in _childRegisters) - // all childs included in register being deleted - // are also to delete - if (offsetToRemove <= reg.OffsetToRoot && - offsetToRemove + widthToRemove >= reg.OffsetToRoot + reg.Width) - { - reg.DetachRoot(); - } - else if (offsetToRemove + widthToRemove <= reg.OffsetToRoot) - { - // all registers neither included nor containing register toDelete - // but their OffsetToRoot must be updated - reg.OffsetToRoot -= widthToRemove; - newChildren.Add(reg); - } - else if (offsetToRemove >= reg.OffsetToRoot && - offsetToRemove + widthToRemove <= reg.OffsetToRoot + reg.Width) - { - // all childs containing register toDelete - // their Width must be updated - reg.Width -= widthToRemove; - - var reg1 = reg; - var existTheSame = - newChildren.Any(x => x.OffsetToRoot == reg1.OffsetToRoot && x.Width == reg1.Width); - if (!existTheSame) newChildren.Add(reg); - } - else - { - newChildren.Add(reg); - } - + _amplitudes = null; + foreach (var child in _childRegisters) + child.DetachRoot(); _childRegisters = null; - _childRegisters = newChildren; + } - Width -= toDelete.Width; + Root = null; + OffsetToRoot = -1; + Width = 0; + _random = null; + } - var states = _amplitudes.Keys.ToArray(); + private void Delete(Register toDelete) + { + // before deletion, register must be measured + // to remove any entanglements + var measured = toDelete.Measure(); - // if one of old states could be also a new state, - // we must change amplitudes in order - if (measured << offsetToRemove < (ulong)1 << Width) Array.Sort(states); + var offsetToRemove = toDelete.OffsetToRoot; + var widthToRemove = toDelete.Width; - var length = states.Length; - for (var k = 0; k < length; k++) - { - var state = states[k]; + var newChildren = new List(); - var rightBits = state % ((ulong)1 << offsetToRemove); - var leftBits = state >> (offsetToRemove + widthToRemove); - var newState = (leftBits << offsetToRemove) | rightBits; - _amplitudes[newState] = _amplitudes[state]; - if (newState != state) _amplitudes.Remove(state); + foreach (var reg in _childRegisters) + // all childs included in register being deleted + // are also to delete + if (offsetToRemove <= reg.OffsetToRoot && + offsetToRemove + widthToRemove >= reg.OffsetToRoot + reg.Width) + { + reg.DetachRoot(); } - } - - private IReadOnlyDictionary GetProbabilities(Register child) - { - ulong mask = 0; - var offset = child.OffsetToRoot; - for (var i = 0; i < child.Width; i++) + else if (offsetToRemove + widthToRemove <= reg.OffsetToRoot) { - var positionMask = (ulong)1 << (i + offset); - mask |= positionMask; + // all registers neither included nor containing register toDelete + // but their OffsetToRoot must be updated + reg.OffsetToRoot -= widthToRemove; + newChildren.Add(reg); } - - var probabilities = new Dictionary(_amplitudes.Count); - foreach (var pair in _amplitudes) + else if (offsetToRemove >= reg.OffsetToRoot && + offsetToRemove + widthToRemove <= reg.OffsetToRoot + reg.Width) { - var maskedState = (pair.Key & mask) >> offset; - probabilities.TryGetValue(maskedState, out var old); - probabilities[maskedState] = old + Math.Pow(pair.Value.Magnitude, 2); + // all childs containing register toDelete + // their Width must be updated + reg.Width -= widthToRemove; + + var reg1 = reg; + var existTheSame = + newChildren.Any(x => x.OffsetToRoot == reg1.OffsetToRoot && x.Width == reg1.Width); + if (!existTheSame) + newChildren.Add(reg); } - - return probabilities; - } - - private IReadOnlyDictionary GetAmplitudes(Register child) - { - ulong mask = 0; - var offset = child.OffsetToRoot; - for (var i = 0; i < child.Width; i++) + else { - var positionMask = (ulong)1 << (i + offset); - mask |= positionMask; + newChildren.Add(reg); } - var negatedMask = ~mask; - var negMaskedState = _amplitudes.First().Key & negatedMask; + _childRegisters = null; + _childRegisters = newChildren; - var toReturn = new Dictionary(_amplitudes.Count); - foreach (var pair in _amplitudes) - { - var maskedState = (pair.Key & mask) >> offset; + Width -= toDelete.Width; - // foreach check, if the remaining qubits are always the same - var negMasked = pair.Key & negatedMask; - if (negMasked != negMaskedState) return null; - toReturn[maskedState] = pair.Value; - } + var states = _amplitudes.Keys.ToArray(); - return new ReadOnlyDictionary(toReturn); - } + // if one of old states could be also a new state, + // we must change amplitudes in order + if (measured << offsetToRemove < (ulong)1 << Width) + Array.Sort(states); - private void Reset(Register child, ulong oldValue, ulong newValue = 0) + var length = states.Length; + for (var k = 0; k < length; k++) { - var xorMask = (oldValue ^ newValue) << child.OffsetToRoot; + var state = states[k]; - var states = _amplitudes.Keys.ToArray(); - var length = states.Length; - for (var k = 0; k < length; k++) - { - var state = states[k]; - var newState = state ^ xorMask; - _amplitudes[newState] = _amplitudes[state]; + var rightBits = state % ((ulong)1 << offsetToRemove); + var leftBits = state >> (offsetToRemove + widthToRemove); + var newState = (leftBits << offsetToRemove) | rightBits; + _amplitudes[newState] = _amplitudes[state]; + if (newState != state) _amplitudes.Remove(state); - } } + } - private ulong Measure(Register child) + private IReadOnlyDictionary GetProbabilities(Register child) + { + ulong mask = 0; + var offset = child.OffsetToRoot; + for (var i = 0; i < child.Width; i++) { - ulong mask = 0; - var offset = child.OffsetToRoot; - for (var i = 0; i < child.Width; i++) - { - var positionMask = (ulong)1 << (i + offset); - mask |= positionMask; - } + var positionMask = (ulong)1 << (i + offset); + mask |= positionMask; + } - // TODO limit ? - var randomDouble = _random.NextDouble(); + var probabilities = new Dictionary(_amplitudes.Count); + foreach (var pair in _amplitudes) + { + var maskedState = (pair.Key & mask) >> offset; + probabilities.TryGetValue(maskedState, out var old); + probabilities[maskedState] = old + Math.Pow(pair.Value.Magnitude, 2); + } - // probabilities of possible measurement results - var probs = GetProbabilities(child); + return probabilities; + } - ulong measured = 0; - foreach (var pair in probs) - { - randomDouble -= pair.Value; - measured = pair.Key; - if (0 >= randomDouble) break; - } + private IReadOnlyDictionary GetAmplitudes(Register child) + { + ulong mask = 0; + var offset = child.OffsetToRoot; + for (var i = 0; i < child.Width; i++) + { + var positionMask = (ulong)1 << (i + offset); + mask |= positionMask; + } - var states = _amplitudes.Keys.ToArray(); - var length = states.Length; - for (var k = 0; k < length; k++) - { - var state = states[k]; - if ((state & mask) >> offset == measured) - _amplitudes[state] /= Math.Sqrt(probs[measured]); - else - _amplitudes.Remove(state); - } + var negatedMask = ~mask; + var negMaskedState = _amplitudes.First().Key & negatedMask; + + var toReturn = new Dictionary(_amplitudes.Count); + foreach (var pair in _amplitudes) + { + var maskedState = (pair.Key & mask) >> offset; - return measured; + // foreach check, if the remaining qubits are always the same + var negMasked = pair.Key & negatedMask; + if (negMasked != negMaskedState) + return null; + toReturn[maskedState] = pair.Value; } - #endregion // Internal methods + return new ReadOnlyDictionary(toReturn); + } - #region Public methods + private void Reset(Register child, ulong oldValue, ulong newValue = 0) + { + var xorMask = (oldValue ^ newValue) << child.OffsetToRoot; - /// - /// Returns the integer value stored in register, if there is only one possibility. - /// If the register is a superposition of multiple states, the method returns null. - /// - /// - /// The nonnegative integer value stored in register, if there is only one possibility. - /// Null, if the register is a superposition of multiple states. - /// - public ulong? GetValue() + var states = _amplitudes.Keys.ToArray(); + var length = states.Length; + for (var k = 0; k < length; k++) { - var probabilities = GetProbabilities(); - if (probabilities.Count == 1) return probabilities.Keys.First(); - return null; + var state = states[k]; + var newState = state ^ xorMask; + _amplitudes[newState] = _amplitudes[state]; + _amplitudes.Remove(state); } + } - /// - /// Returns probabilities of each possible state of register. - /// It means that after measurement register remains in particular state with corresponding probability. - /// - /// Dictionary, where key is possible state and value is the probability of that state. - public IReadOnlyDictionary GetProbabilities() + private ulong Measure(Register child) + { + ulong mask = 0; + var offset = child.OffsetToRoot; + for (var i = 0; i < child.Width; i++) { - if (Root != this) return Root.GetProbabilities(this); - var probabilities = new Dictionary(_amplitudes.Count); - foreach (var pair in _amplitudes) probabilities[pair.Key] = Math.Pow(pair.Value.Magnitude, 2); - return probabilities; + var positionMask = (ulong)1 << (i + offset); + mask |= positionMask; } - /// - /// - /// Returns amplitudes of each possible state of register. - /// Amplitude is a complex number, which squared magnitude is the probability of the state. - /// - /// - /// If register is entangled, the amplitudes cannot be computed. - /// Thus, the method GetAmplitudes() returns null for such registers. - /// The method returns meaningful value, if register is RootRegister, or if it was not a part of multi-register - /// operations (see ). - /// Even if the register is not entangled with any other, but was used in multi-register operation, the returned - /// value is null because such operation connect participating registers into one RootRegister. - /// - /// - /// - /// - /// Dictionary, where key is a possible state, and value is amplitude of that state. - /// - /// - /// Null, if register is a part of other register (e.g. RootRegister, after participating in multi-register - /// operation). - /// - /// - public IReadOnlyDictionary GetAmplitudes() - { - return Root != this ? Root.GetAmplitudes(this) : new ReadOnlyDictionary(_amplitudes); - } + // TODO limit ? + var randomDouble = _random.NextDouble(); - /// - /// Returns a vector of amplitudes of each possible state. - /// If register is a part of other register, this method returns null (see ). - /// - /// - /// - /// An array of complex numbers representing amplitudes of each register's state, or null (see - /// . - /// - public Complex[] GetVector() - { - IReadOnlyDictionary tmpAmpl = _amplitudes; - if (Root != this) tmpAmpl = Root.GetAmplitudes(this); - if (tmpAmpl == null) return null; - var statesCount = (ulong)1 << Width; - var vector = new Complex[statesCount]; - for (ulong i = 0; i < statesCount; i++) - if (tmpAmpl.TryGetValue(i, out var tmp)) - vector[i] = tmp; - else - vector[i] = 0; - return vector; - } + // probabilities of possible measurement results + var probs = GetProbabilities(child); - public void Reset(ulong newValue = 0) + ulong measured = 0; + foreach (var pair in probs) { - if (Root != this) - { - var measured = Measure(); - if (measured != newValue) Root.Reset(this, measured, newValue); - return; - } - - _amplitudes.Clear(); - _amplitudes[newValue] = 1; + randomDouble -= pair.Value; + measured = pair.Key; + if (0 >= randomDouble) + break; } - /// - /// Performs measurement of whole register. - /// - /// Measured state of the register. - public ulong Measure() + var states = _amplitudes.Keys.ToArray(); + var length = states.Length; + for (var k = 0; k < length; k++) { - if (Root != this) - //ulong pow2 = 1; - //ulong sum = 0; - //byte result; - //for (int i = 0; i < this.Width; i++) - //{ - // result = Root.Measure(OffsetToRoot + i); - // sum += result * pow2; - // pow2 *= 2; - //} - //return sum; - return Root.Measure(this); - var randomDouble = _random.NextDouble(); - double sum = 0; - ulong measured = 0; - foreach (var state in _amplitudes.Keys) - { - sum += Math.Pow(_amplitudes[state].Magnitude, 2); - measured = state; - if (sum >= randomDouble) break; - } - - _amplitudes.Clear(); - _amplitudes[measured] = 1; - return measured; + var state = states[k]; + if ((state & mask) >> offset == measured) + _amplitudes[state] /= Math.Sqrt(probs[measured]); + else + _amplitudes.Remove(state); } - /// - /// Performs measurement of single qubit in register. - /// After measurement, the width of the register remains the same. - /// - /// The position (offset) of qubit to measure. Position = 0 indicates the Least Significant Bit. - /// 0 or 1 - the measured value of the qubit. - public byte Measure(int position) - { - if (Root != this) return Root.Measure(position + OffsetToRoot); + return measured; + } - // TODO limit ? + #endregion // Internal methods - var randomDouble = _random.NextDouble(); + #region Public methods - var positionMask = (ulong)1 << position; - double zeroProbability = 0; - double oneProbability = 0; + /// + /// Returns the integer value stored in register, if there is only one possibility. + /// If the register is a superposition of multiple states, the method returns null. + /// + /// + /// The nonnegative integer value stored in register, if there is only one possibility. + /// Null, if the register is a superposition of multiple states. + /// + public ulong? GetValue() + { + var probabilities = GetProbabilities(); + if (probabilities.Count == 1) + return probabilities.Keys.First(); + return null; + } - // sum up the probability of 0 - foreach (var state in _amplitudes.Keys) - if ((state & positionMask) == 0) - zeroProbability += Math.Pow(_amplitudes[state].Magnitude, 2); - else - oneProbability += Math.Pow(_amplitudes[state].Magnitude, 2); + /// + /// Returns probabilities of each possible state of register. + /// It means that after measurement register remains in particular state with corresponding probability. + /// + /// Dictionary, where key is possible state and value is the probability of that state. + public IReadOnlyDictionary GetProbabilities() + { + if (Root != this) + return Root.GetProbabilities(this); + var probabilities = new Dictionary(_amplitudes.Count); + foreach (var pair in _amplitudes) + probabilities[pair.Key] = Math.Pow(pair.Value.Magnitude, 2); + return probabilities; + } - byte measured = 0; - if (randomDouble > zeroProbability) measured = 1; - var states = _amplitudes.Keys.ToArray(); - var length = states.Length; - for (var k = 0; k < length; k++) - { - var state = states[k]; - if ((state & positionMask) == 0) - { - if (measured == 0) - _amplitudes[state] /= Math.Sqrt(zeroProbability); - else - _amplitudes.Remove(state); - } - else - { - if (measured == 0) - _amplitudes.Remove(state); - else - _amplitudes[state] /= Math.Sqrt(oneProbability); - } - } + /// + /// + /// Returns amplitudes of each possible state of register. + /// Amplitude is a complex number, which squared magnitude is the probability of the state. + /// + /// + /// If register is entangled, the amplitudes cannot be computed. + /// Thus, the method GetAmplitudes() returns null for such registers. + /// The method returns meaningful value, if register is RootRegister, or if it was not a part of multi-register + /// operations (see ). + /// Even if the register is not entangled with any other, but was used in multi-register operation, the returned + /// value is null because such operation connect participating registers into one RootRegister. + /// + /// + /// + /// + /// Dictionary, where key is a possible state, and value is amplitude of that state. + /// + /// + /// Null, if register is a part of other register (e.g. RootRegister, after participating in multi-register + /// operation). + /// + /// + public IReadOnlyDictionary GetAmplitudes() + { + return Root != this ? Root.GetAmplitudes(this) : new ReadOnlyDictionary(_amplitudes); + } - return measured; - } + /// + /// Returns a vector of amplitudes of each possible state. + /// If register is a part of other register, this method returns null (see ). + /// + /// + /// + /// An array of complex numbers representing amplitudes of each register's state, or null (see + /// . + /// + public Complex[] GetVector() + { + IReadOnlyDictionary tmpAmpl = _amplitudes; + if (Root != this) + tmpAmpl = Root.GetAmplitudes(this); + if (tmpAmpl == null) + return null; + var statesCount = (ulong)1 << Width; + var vector = new Complex[statesCount]; + for (ulong i = 0; i < statesCount; i++) + if (tmpAmpl.TryGetValue(i, out var tmp)) + vector[i] = tmp; + else + vector[i] = 0; + return vector; + } - /// - /// - /// Performs a controlled not operation (C-NOT Gate). - /// The target bit gets inverted if the control bit is enabled. - /// The operation can be written as the unitary operation matrix: - /// - /// - /// - /// The position of target qubit in register (0 indicates the Least Significant Bit). - /// The position of control qubit in register (0 indicates the Least Significant Bit). - public void CNot(int target, int control) + public void Reset(ulong newValue = 0) + { + if (Root != this) { - if (Root != this) - { - Root.CNot(target + OffsetToRoot, control + OffsetToRoot); - return; - } - - SigmaX(target, control); + var measured = Measure(); + if (measured != newValue) + Root.Reset(this, measured, newValue); + return; } - /// - /// - /// Applies Toffoli gate. If all of the control bits are enabled, the target bit gets inverted. - /// This gate with more than two control bits is not considered elementary and is not available on all physical - /// realizations of a quantum computer. - /// Toffoli gate with two control bits can be represented by unitary matrix: - /// - /// - /// - /// - /// - /// namespace QuantumConsole - /// { - /// public class QuantumTest - /// { - /// public static void Main() - /// { - /// QuantumComputer comp = QuantumComputer.GetInstance(); - /// - /// Register x = comp.NewRegister(1, 3); - /// - /// Console.WriteLine("Register x: \n{0}", x); - /// - /// x.Toffoli(2, 0, 1); - /// - /// Console.WriteLine("Register x after Toffoli(2, 0, 1): \n{0}", x); - /// - /// Register y = comp.NewRegister(7, 4); - /// - /// Console.WriteLine("Register y: \n{0}", y); - /// - /// y.Toffoli(3, 0, 1, 2); - /// - /// Console.WriteLine("Register y after Toffoli(3, 0, 1, 2): \n{0}", y); - /// } - /// } - /// } - /// - /// - /// The position of target qubit in register (0 indicates the Least Significant Bit). - /// The positions of control qubits in register (0 indicates the Least Significant Bit). - public void Toffoli(int target, params int[] controls) - { - if (Root != this) - { - for (var i = 0; i < controls.Length; i++) controls[i] += OffsetToRoot; - Root.Toffoli(target + OffsetToRoot, controls); - return; - } + _amplitudes.Clear(); + _amplitudes[newValue] = 1; + } - if (controls.Length < 2) throw new ArgumentException("Too few control bits"); + /// + /// Performs measurement of whole register. + /// + /// Measured state of the register. + public ulong Measure() + { + if (Root != this) + //ulong pow2 = 1; + //ulong sum = 0; + //byte result; + //for (int i = 0; i < this.Width; i++) + //{ + // result = Root.Measure(OffsetToRoot + i); + // sum += result * pow2; + // pow2 *= 2; + //} + //return sum; + return Root.Measure(this); + var randomDouble = _random.NextDouble(); + double sum = 0; + ulong measured = 0; + foreach (var state in _amplitudes.Keys) + { + sum += Math.Pow(_amplitudes[state].Magnitude, 2); + measured = state; + if (sum >= randomDouble) + break; + } - var controlsLength = controls.Length; - var done = new HashSet(); - var states = _amplitudes.Keys.ToArray(); - var length = states.Length; - for (var k = 0; k < length; k++) - { - var state = states[k]; - if (done.Contains(state)) continue; - // Flip the target bit of a basis state if all control bits are set - var i = 0; - while (i < controlsLength && (state & ((ulong)1 << controls[i])) != 0) i++; + _amplitudes.Clear(); + _amplitudes[measured] = 1; + return measured; + } - if (i != controlsLength) continue; - //state ^= ((ulong)1 << target); - var reversedTargetState = state ^ ((ulong)1 << target); + /// + /// Performs measurement of single qubit in register. + /// After measurement, the width of the register remains the same. + /// + /// The position (offset) of qubit to measure. Position = 0 indicates the Least Significant Bit. + /// 0 or 1 - the measured value of the qubit. + public byte Measure(int position) + { + if (Root != this) + return Root.Measure(position + OffsetToRoot); - var t = _amplitudes[state]; + // TODO limit ? - var reversedTargetStateExist = - _amplitudes.TryGetValue(reversedTargetState, out var tnot); + var randomDouble = _random.NextDouble(); - _amplitudes[reversedTargetState] = t; + var positionMask = (ulong)1 << position; + double zeroProbability = 0; + double oneProbability = 0; - if (reversedTargetStateExist) - { - _amplitudes[state] = tnot; - done.Add(reversedTargetState); - } + // sum up the probability of 0 + foreach (var state in _amplitudes.Keys) + if ((state & positionMask) == 0) + zeroProbability += Math.Pow(_amplitudes[state].Magnitude, 2); + else + oneProbability += Math.Pow(_amplitudes[state].Magnitude, 2); + + byte measured = 0; + if (randomDouble > zeroProbability) + measured = 1; + var states = _amplitudes.Keys.ToArray(); + var length = states.Length; + for (var k = 0; k < length; k++) + { + var state = states[k]; + if ((state & positionMask) == 0) + { + if (measured == 0) + _amplitudes[state] /= Math.Sqrt(zeroProbability); else - { _amplitudes.Remove(state); - } } - } - - /// - /// - /// Performs a Sigma X Pauli's Gate on target qubit. Actually, it is a simple Not. - /// The unitary operation matrix is: - /// - /// - /// - /// The position of target qubit in register (0 indicates the Least Significant Bit). - /// - /// Optional argument. If given, the method performs controlled Sigma X. This argument destribes the - /// control qubit's position. - /// - public void SigmaX(int target, int? control = null) - { - if (Root != this) + else { - Root.SigmaX(target + OffsetToRoot, control + OffsetToRoot); - return; + if (measured == 0) + _amplitudes.Remove(state); + else + _amplitudes[state] /= Math.Sqrt(oneProbability); } + } - //TODO throw in every control gate - if (control == target) throw new ArgumentException("Target and control bits are the same"); - - var done = new HashSet(); - var states = _amplitudes.Keys.ToArray(); - var length = states.Length; - for (var k = 0; k < length; k++) - { - var state = states[k]; - if (done.Contains(state)) continue; - //determine if the control of the basis state is set or not needed - var controlIsSet = control == null || (state & ((ulong)1 << control.Value)) != 0; - - // Flip the target bit of a basis state if the control bit is set - if (!controlIsSet) continue; - var t = _amplitudes[state]; - //state ^= ((ulong)1 << target); - var reversedTargetState = state ^ ((ulong)1 << target); + return measured; + } - var reversedTargetStateExist = - _amplitudes.TryGetValue(reversedTargetState, out var tnot); + /// + /// + /// Performs a controlled not operation (C-NOT Gate). + /// The target bit gets inverted if the control bit is enabled. + /// The operation can be written as the unitary operation matrix: + /// + /// + /// + /// The position of target qubit in register (0 indicates the Least Significant Bit). + /// The position of control qubit in register (0 indicates the Least Significant Bit). + public void CNot(int target, int control) + { + if (Root != this) + { + Root.CNot(target + OffsetToRoot, control + OffsetToRoot); + return; + } - _amplitudes[reversedTargetState] = t; + SigmaX(target, control); + } - if (reversedTargetStateExist) - { - _amplitudes[state] = tnot; - done.Add(reversedTargetState); - } - else - { - _amplitudes.Remove(state); - } - } + /// + /// + /// Applies Toffoli gate. If all of the control bits are enabled, the target bit gets inverted. + /// This gate with more than two control bits is not considered elementary and is not available on all physical + /// realizations of a quantum computer. + /// Toffoli gate with two control bits can be represented by unitary matrix: + /// + /// + /// + /// + /// + /// namespace QuantumConsole + /// { + /// public class QuantumTest + /// { + /// public static void Main() + /// { + /// QuantumComputer comp = QuantumComputer.GetInstance(); + /// + /// Register x = comp.NewRegister(1, 3); + /// + /// Console.WriteLine("Register x: \n{0}", x); + /// + /// x.Toffoli(2, 0, 1); + /// + /// Console.WriteLine("Register x after Toffoli(2, 0, 1): \n{0}", x); + /// + /// Register y = comp.NewRegister(7, 4); + /// + /// Console.WriteLine("Register y: \n{0}", y); + /// + /// y.Toffoli(3, 0, 1, 2); + /// + /// Console.WriteLine("Register y after Toffoli(3, 0, 1, 2): \n{0}", y); + /// } + /// } + /// } + /// + /// + /// The position of target qubit in register (0 indicates the Least Significant Bit). + /// The positions of control qubits in register (0 indicates the Least Significant Bit). + public void Toffoli(int target, params int[] controls) + { + if (Root != this) + { + for (var i = 0; i < controls.Length; i++) + controls[i] += OffsetToRoot; + Root.Toffoli(target + OffsetToRoot, controls); + return; } - /// - /// - /// Performs a Sigma Y Pauli's Gate on target qubit. - /// The operation is represented by unitary matrix: - /// - /// - /// - /// The position of target qubit in register (0 indicates the Least Significant Bit). - /// - /// Optional argument. If given, the method performs controlled Sigma Y. This argument destribes the - /// control qubit's position. - /// - public void SigmaY(int target, int? control = null) + if (controls.Length < 2) + throw new ArgumentException("Too few control bits"); + + var controlsLength = controls.Length; + var done = new HashSet(); + var states = _amplitudes.Keys.ToArray(); + var length = states.Length; + for (var k = 0; k < length; k++) { - if (Root != this) - { - Root.SigmaY(target + OffsetToRoot, control + OffsetToRoot); - return; - } + var state = states[k]; + if (done.Contains(state)) + continue; + // Flip the target bit of a basis state if all control bits are set + var i = 0; + while (i < controlsLength && (state & ((ulong)1 << controls[i])) != 0) + i++; - var done = new HashSet(); - var states = _amplitudes.Keys.ToArray(); - var length = states.Length; - for (var k = 0; k < length; k++) - { - var state = states[k]; - if (done.Contains(state)) continue; - //determine if the control of the basis state is set or not needed - var controlIsSet = control == null || (state & ((ulong)1 << control.Value)) != 0; + if (i != controlsLength) + continue; + //state ^= ((ulong)1 << target); + var reversedTargetState = state ^ ((ulong)1 << target); - if (!controlIsSet) continue; - //state ^= ((ulong)1 << target); - var reversedTargetState = state ^ ((ulong)1 << target); + var t = _amplitudes[state]; - var t = _amplitudes[state]; + var reversedTargetStateExist = + _amplitudes.TryGetValue(reversedTargetState, out var tnot); - var reversedTargetStateExist = - _amplitudes.TryGetValue(reversedTargetState, out var tnot); - if (reversedTargetStateExist) - { - // Flip the target bit of each basis state and multiply with +/- i - if ((reversedTargetState & ((ulong)1 << target)) != 0) - { - _amplitudes[reversedTargetState] = t * Complex.ImaginaryOne; - _amplitudes[state] = -tnot * Complex.ImaginaryOne; - } - else - { - _amplitudes[reversedTargetState] = -t * Complex.ImaginaryOne; - _amplitudes[state] = tnot * Complex.ImaginaryOne; - } + _amplitudes[reversedTargetState] = t; - done.Add(reversedTargetState); - } - else - { - // Flip the target bit of each basis state and multiply with +/- i - if ((reversedTargetState & ((ulong)1 << target)) != 0) - _amplitudes[reversedTargetState] = t * Complex.ImaginaryOne; - else - _amplitudes[reversedTargetState] = -t * Complex.ImaginaryOne; - _amplitudes.Remove(state); - } + if (reversedTargetStateExist) + { + _amplitudes[state] = tnot; + done.Add(reversedTargetState); } + else + { + _amplitudes.Remove(state); + } + } + } + + /// + /// + /// Performs a Sigma X Pauli's Gate on target qubit. Actually, it is a simple Not. + /// The unitary operation matrix is: + /// + /// + /// + /// The position of target qubit in register (0 indicates the Least Significant Bit). + /// + /// Optional argument. If given, the method performs controlled Sigma X. This argument destribes the + /// control qubit's position. + /// + public void SigmaX(int target, int? control = null) + { + if (Root != this) + { + Root.SigmaX(target + OffsetToRoot, control + OffsetToRoot); + return; } - /// - /// - /// Performs a Sigma Z Pauli's Gate on target qubit. - /// The operation is represented by unitary matrix: - /// - /// - /// - /// The position of target qubit in register (0 indicates the Least Significant Bit). - /// - /// Optional argument. If given, the method performs controlled Sigma Z. This argument destribes the - /// control qubit's position. - /// - public void SigmaZ(int target, int? control = null) + //TODO throw in every control gate + if (control == target) + throw new ArgumentException("Target and control bits are the same"); + + var done = new HashSet(); + var states = _amplitudes.Keys.ToArray(); + var length = states.Length; + for (var k = 0; k < length; k++) { - if (Root != this) + var state = states[k]; + if (done.Contains(state)) + continue; + //determine if the control of the basis state is set or not needed + var controlIsSet = control == null || (state & ((ulong)1 << control.Value)) != 0; + + // Flip the target bit of a basis state if the control bit is set + if (!controlIsSet) + continue; + var t = _amplitudes[state]; + //state ^= ((ulong)1 << target); + var reversedTargetState = state ^ ((ulong)1 << target); + + var reversedTargetStateExist = + _amplitudes.TryGetValue(reversedTargetState, out var tnot); + + _amplitudes[reversedTargetState] = t; + + if (reversedTargetStateExist) { - Root.SigmaZ(target + OffsetToRoot, control + OffsetToRoot); - return; + _amplitudes[state] = tnot; + done.Add(reversedTargetState); } - - var states = _amplitudes.Keys.ToArray(); - var length = states.Length; - for (var k = 0; k < length; k++) + else { - var state = states[k]; - - //determine if the control of the basis state is set or not needed - var controlIsSet = control == null || (state & ((ulong)1 << control.Value)) != 0; - if (!controlIsSet) continue; - // Multiply with -1 if the target bit is set - if ((state & ((ulong)1 << target)) != 0) _amplitudes[state] *= -1; + _amplitudes.Remove(state); } } + } - /// - /// - /// Performs any arbitrary unitary operation on target qubit. The operation is described by unitary matrix of - /// complex numbers, as follows: - /// - /// - /// - /// The 2x2 matrix of complex numbers, which describes the unitary operation. - /// The position of target qubit in register (0 indicates the Least Significant Bit). - /// - /// Optional argument. If given, the method performs controlled unitary operation. This argument - /// destribes the control qubit's position. - /// - public void Gate1(Complex[,] matrix, int target, int? control = null) + /// + /// + /// Performs a Sigma Y Pauli's Gate on target qubit. + /// The operation is represented by unitary matrix: + /// + /// + /// + /// The position of target qubit in register (0 indicates the Least Significant Bit). + /// + /// Optional argument. If given, the method performs controlled Sigma Y. This argument destribes the + /// control qubit's position. + /// + public void SigmaY(int target, int? control = null) + { + if (Root != this) { - if (Root != this) - { - Root.Gate1(matrix, target + OffsetToRoot, control + OffsetToRoot); - return; - } - - if (matrix.GetLength(0) != 2 || matrix.GetLength(1) != 2) - throw new ArgumentException("Matrix is not 2x2", nameof(matrix)); - var limit = 1.0 / ((ulong)1 << Width) * QuantumComputer.Epsilon; - - var done = new HashSet(); - var states = _amplitudes.Keys.ToArray(); - var length = states.Length; + Root.SigmaY(target + OffsetToRoot, control + OffsetToRoot); + return; + } - for (var i = 0; i < length; i++) + var done = new HashSet(); + var states = _amplitudes.Keys.ToArray(); + var length = states.Length; + for (var k = 0; k < length; k++) + { + var state = states[k]; + if (done.Contains(state)) + continue; + //determine if the control of the basis state is set or not needed + var controlIsSet = control == null || (state & ((ulong)1 << control.Value)) != 0; + + if (!controlIsSet) + continue; + //state ^= ((ulong)1 << target); + var reversedTargetState = state ^ ((ulong)1 << target); + + var t = _amplitudes[state]; + + var reversedTargetStateExist = + _amplitudes.TryGetValue(reversedTargetState, out var tnot); + if (reversedTargetStateExist) { - var state = states[i]; - if (done.Contains(state)) continue; - //determine if the control of the basis state is set or not needed - var controlIsSet = control == null || (state & ((ulong)1 << control.Value)) != 0; - if (!controlIsSet) continue; - //determine if the target of the basis state is set - var targetIsSet = (state & ((ulong)1 << target)) != 0; - - var t = _amplitudes[state]; - var reversedTargetState = state ^ ((ulong)1 << target); - - var reversedTargetStateExist = - _amplitudes.TryGetValue(reversedTargetState, out var tnot); - - Complex newT; - Complex newTnot; - if (targetIsSet) + // Flip the target bit of each basis state and multiply with +/- i + if ((reversedTargetState & ((ulong)1 << target)) != 0) { - newT = matrix[1, 0] * tnot + matrix[1, 1] * t; - newTnot = matrix[0, 0] * tnot + matrix[0, 1] * t; - - // if new amplitudes are extremely small, remove states - if (Math.Pow(newT.Magnitude, 2) < limit) - _amplitudes.Remove(state); - else - _amplitudes[state] = newT; - if (Math.Pow(newTnot.Magnitude, 2) < limit) - _amplitudes.Remove(reversedTargetState); - else - _amplitudes[reversedTargetState] = newTnot; + _amplitudes[reversedTargetState] = t * Complex.ImaginaryOne; + _amplitudes[state] = -tnot * Complex.ImaginaryOne; } else { - newT = matrix[0, 0] * t + matrix[0, 1] * tnot; - newTnot = matrix[1, 0] * t + matrix[1, 1] * tnot; - // if new amplitudes are extremely small, remove states - if (Math.Pow(newT.Magnitude, 2) < limit) - _amplitudes.Remove(state); - else - _amplitudes[state] = newT; - if (Math.Pow(newTnot.Magnitude, 2) < limit) - _amplitudes.Remove(reversedTargetState); - else - _amplitudes[reversedTargetState] = newTnot; + _amplitudes[reversedTargetState] = -t * Complex.ImaginaryOne; + _amplitudes[state] = tnot * Complex.ImaginaryOne; } - if (reversedTargetStateExist) done.Add(reversedTargetState); + done.Add(reversedTargetState); } - } - - /// - /// - /// Applies the Hadamard gate to the target qubit. - /// The unitary matrix for this operation is: - /// - /// - /// - /// The position of target qubit in register (0 indicates the Least Significant Bit). - /// - /// Optional argument. If given, the method applies a controlled Hadamard gate. This argument - /// destribes the control qubit's position. - /// - public void Hadamard(int target, int? control = null) - { - if (Root != this) + else { - Root.Hadamard(target + OffsetToRoot, control + OffsetToRoot); - return; + // Flip the target bit of each basis state and multiply with +/- i + if ((reversedTargetState & ((ulong)1 << target)) != 0) + _amplitudes[reversedTargetState] = t * Complex.ImaginaryOne; + else + _amplitudes[reversedTargetState] = -t * Complex.ImaginaryOne; + _amplitudes.Remove(state); } + } + } - var a = new Complex(Math.Sqrt(1.0 / 2.0), 0); - Complex[,] m = { { a, a }, { a, -a } }; - Gate1(m, target, control); + /// + /// + /// Performs a Sigma Z Pauli's Gate on target qubit. + /// The operation is represented by unitary matrix: + /// + /// + /// + /// The position of target qubit in register (0 indicates the Least Significant Bit). + /// + /// Optional argument. If given, the method performs controlled Sigma Z. This argument destribes the + /// control qubit's position. + /// + public void SigmaZ(int target, int? control = null) + { + if (Root != this) + { + Root.SigmaZ(target + OffsetToRoot, control + OffsetToRoot); + return; } - /// - /// - /// Performs the Square Root of Not on the target qubit. - /// The Square Root of Not Gate is such a gate, that applied twice, performs Not operation. - /// The operation can be represented as the unitary matrix: - /// - /// - /// - /// The position of target qubit in register (0 indicates the Least Significant Bit). - /// - /// Optional argument. If given, the method performs a controlled Square root of Not. This argument - /// destribes the control qubit's position. - /// - public void SqrtX(int target, int? control = null) + var states = _amplitudes.Keys.ToArray(); + var length = states.Length; + for (var k = 0; k < length; k++) { - if (Root != this) - { - Root.SqrtX(target + OffsetToRoot, control + OffsetToRoot); - return; - } + var state = states[k]; + + //determine if the control of the basis state is set or not needed + var controlIsSet = control == null || (state & ((ulong)1 << control.Value)) != 0; + if (!controlIsSet) + continue; + // Multiply with -1 if the target bit is set + if ((state & ((ulong)1 << target)) != 0) + _amplitudes[state] *= -1; + } + } - var a = new Complex(1.0, 0.0) / new Complex(1.0, 1.0); - Complex[,] m = { { a, a * Complex.ImaginaryOne }, { a * Complex.ImaginaryOne, a } }; - Gate1(m, target, control); + /// + /// + /// Performs any arbitrary unitary operation on target qubit. The operation is described by unitary matrix of + /// complex numbers, as follows: + /// + /// + /// + /// The 2x2 matrix of complex numbers, which describes the unitary operation. + /// The position of target qubit in register (0 indicates the Least Significant Bit). + /// + /// Optional argument. If given, the method performs controlled unitary operation. This argument + /// destribes the control qubit's position. + /// + public void Gate1(Complex[,] matrix, int target, int? control = null) + { + if (Root != this) + { + Root.Gate1(matrix, target + OffsetToRoot, control + OffsetToRoot); + return; } - /// - /// - /// Performs a rotation of the target qubit about the x-axis of the Bloch sphere. - /// The angle of rotation is given in first argument (double gamma). - /// The unitary matrix of this operation is: - /// - /// - /// - /// The angle of rotation. - /// The position of target qubit in register (0 indicates the Least Significant Bit). - /// - /// Optional argument. If given, the method performs a controlled rotation. This argument destribes - /// the control qubit's position. - /// - public void RotateX(double gamma, int target, int? control = null) + if (matrix.GetLength(0) != 2 || matrix.GetLength(1) != 2) + throw new ArgumentException("Matrix is not 2x2", nameof(matrix)); + var limit = 1.0 / ((ulong)1 << Width) * QuantumComputer.Epsilon; + + var done = new HashSet(); + var states = _amplitudes.Keys.ToArray(); + var length = states.Length; + + for (var i = 0; i < length; i++) { - if (Root != this) + var state = states[i]; + if (done.Contains(state)) + continue; + //determine if the control of the basis state is set or not needed + var controlIsSet = control == null || (state & ((ulong)1 << control.Value)) != 0; + if (!controlIsSet) + continue; + //determine if the target of the basis state is set + var targetIsSet = (state & ((ulong)1 << target)) != 0; + + var t = _amplitudes[state]; + var reversedTargetState = state ^ ((ulong)1 << target); + + var reversedTargetStateExist = + _amplitudes.TryGetValue(reversedTargetState, out var tnot); + + Complex newT; + Complex newTnot; + if (targetIsSet) + { + newT = matrix[1, 0] * tnot + matrix[1, 1] * t; + newTnot = matrix[0, 0] * tnot + matrix[0, 1] * t; + + // if new amplitudes are extremely small, remove states + if (Math.Pow(newT.Magnitude, 2) < limit) + _amplitudes.Remove(state); + else + _amplitudes[state] = newT; + if (Math.Pow(newTnot.Magnitude, 2) < limit) + _amplitudes.Remove(reversedTargetState); + else + _amplitudes[reversedTargetState] = newTnot; + } + else { - Root.RotateX(gamma, target + OffsetToRoot, control + OffsetToRoot); - return; + newT = matrix[0, 0] * t + matrix[0, 1] * tnot; + newTnot = matrix[1, 0] * t + matrix[1, 1] * tnot; + // if new amplitudes are extremely small, remove states + if (Math.Pow(newT.Magnitude, 2) < limit) + _amplitudes.Remove(state); + else + _amplitudes[state] = newT; + if (Math.Pow(newTnot.Magnitude, 2) < limit) + _amplitudes.Remove(reversedTargetState); + else + _amplitudes[reversedTargetState] = newTnot; } - var m = new Complex[2, 2]; - m[0, 0] = Math.Cos(gamma / 2.0); - m[0, 1] = -Complex.ImaginaryOne * Math.Sin(gamma / 2.0); - m[1, 0] = -Complex.ImaginaryOne * Math.Sin(gamma / 2.0); - m[1, 1] = Math.Cos(gamma / 2.0); - Gate1(m, target, control); + if (reversedTargetStateExist) + done.Add(reversedTargetState); } + } - /// - /// - /// Performs a rotation of the target qubit about the y-axis of the Bloch sphere. - /// The angle of rotation is given in first argument (double gamma). - /// The operation is represented by the unitary matrix: - /// - /// - /// - /// The angle of rotation. - /// The position of target qubit in register (0 indicates the Least Significant Bit). - /// - /// Optional argument. If given, the method performs a controlled rotation. This argument destribes - /// the control qubit's position. - /// - public void RotateY(double gamma, int target, int? control = null) + /// + /// + /// Applies the Hadamard gate to the target qubit. + /// The unitary matrix for this operation is: + /// + /// + /// + /// The position of target qubit in register (0 indicates the Least Significant Bit). + /// + /// Optional argument. If given, the method applies a controlled Hadamard gate. This argument + /// destribes the control qubit's position. + /// + public void Hadamard(int target, int? control = null) + { + if (Root != this) { - if (Root != this) - { - Root.RotateY(gamma, target + OffsetToRoot, control + OffsetToRoot); - return; - } + Root.Hadamard(target + OffsetToRoot, control + OffsetToRoot); + return; + } - var m = new Complex[2, 2]; - m[0, 0] = Math.Cos(gamma / 2.0); - m[0, 1] = -Math.Sin(gamma / 2.0); - m[1, 0] = Math.Sin(gamma / 2.0); - m[1, 1] = Math.Cos(gamma / 2.0); - Gate1(m, target, control); + var a = new Complex(Math.Sqrt(1.0 / 2.0), 0); + Complex[,] m = { { a, a }, { a, -a } }; + Gate1(m, target, control); + } + + /// + /// + /// Performs the Square Root of Not on the target qubit. + /// The Square Root of Not Gate is such a gate, that applied twice, performs Not operation. + /// The operation can be represented as the unitary matrix: + /// + /// + /// + /// The position of target qubit in register (0 indicates the Least Significant Bit). + /// + /// Optional argument. If given, the method performs a controlled Square root of Not. This argument + /// destribes the control qubit's position. + /// + public void SqrtX(int target, int? control = null) + { + if (Root != this) + { + Root.SqrtX(target + OffsetToRoot, control + OffsetToRoot); + return; } - /// - /// - /// Performs a rotation of the target qubit about the z-axis of the Bloch sphere. - /// The angle of rotation is given in first argument (double gamma). - /// The operation is represented by the unitary matrix: - /// - /// - /// - /// The angle of rotation. - /// The position of target qubit in register (0 indicates the Least Significant Bit). - /// - /// Optional argument. If given, the method performs a controlled rotation. This argument destribes - /// the control qubit's position. - /// - public void RotateZ(double gamma, int target, int? control = null) + var a = new Complex(1.0, 0.0) / new Complex(1.0, 1.0); + Complex[,] m = { { a, a * Complex.ImaginaryOne }, { a * Complex.ImaginaryOne, a } }; + Gate1(m, target, control); + } + + /// + /// + /// Performs a rotation of the target qubit about the x-axis of the Bloch sphere. + /// The angle of rotation is given in first argument (double gamma). + /// The unitary matrix of this operation is: + /// + /// + /// + /// The angle of rotation. + /// The position of target qubit in register (0 indicates the Least Significant Bit). + /// + /// Optional argument. If given, the method performs a controlled rotation. This argument destribes + /// the control qubit's position. + /// + public void RotateX(double gamma, int target, int? control = null) + { + if (Root != this) { - if (Root != this) - { - Root.RotateZ(gamma, target + OffsetToRoot, control + OffsetToRoot); - return; - } + Root.RotateX(gamma, target + OffsetToRoot, control + OffsetToRoot); + return; + } - var z = Complex.Exp(Complex.ImaginaryOne * (gamma / 2.0)); - var divZ = Complex.Exp(-Complex.ImaginaryOne * (gamma / 2.0)); - var states = _amplitudes.Keys.ToArray(); - var length = states.Length; - for (var k = 0; k < length; k++) - { - var state = states[k]; + var m = new Complex[2, 2]; + m[0, 0] = Math.Cos(gamma / 2.0); + m[0, 1] = -Complex.ImaginaryOne * Math.Sin(gamma / 2.0); + m[1, 0] = -Complex.ImaginaryOne * Math.Sin(gamma / 2.0); + m[1, 1] = Math.Cos(gamma / 2.0); + Gate1(m, target, control); + } - //determine if the control of the basis state is set or not needed - var controlIsSet = control == null || (state & ((ulong)1 << control.Value)) != 0; - if (!controlIsSet) continue; - // Multiply if the target bit is set - if ((state & ((ulong)1 << target)) != 0) - _amplitudes[state] *= z; - else - _amplitudes[state] *= divZ; - } + /// + /// + /// Performs a rotation of the target qubit about the y-axis of the Bloch sphere. + /// The angle of rotation is given in first argument (double gamma). + /// The operation is represented by the unitary matrix: + /// + /// + /// + /// The angle of rotation. + /// The position of target qubit in register (0 indicates the Least Significant Bit). + /// + /// Optional argument. If given, the method performs a controlled rotation. This argument destribes + /// the control qubit's position. + /// + public void RotateY(double gamma, int target, int? control = null) + { + if (Root != this) + { + Root.RotateY(gamma, target + OffsetToRoot, control + OffsetToRoot); + return; } - /// - /// - /// Adds a global phase on the register's state. - /// The operation is represented by the unitary matrix: - /// - /// - /// - /// The phase that is added to the register's state. - /// The position of target qubit in register (0 indicates the Least Significant Bit). - /// - /// Optional argument. If given, the method performs a controlled phase scale operation. This - /// argument destribes the control qubit's position. - /// - public void PhaseScale(double gamma, int target, int? control = null) + var m = new Complex[2, 2]; + m[0, 0] = Math.Cos(gamma / 2.0); + m[0, 1] = -Math.Sin(gamma / 2.0); + m[1, 0] = Math.Sin(gamma / 2.0); + m[1, 1] = Math.Cos(gamma / 2.0); + Gate1(m, target, control); + } + + /// + /// + /// Performs a rotation of the target qubit about the z-axis of the Bloch sphere. + /// The angle of rotation is given in first argument (double gamma). + /// The operation is represented by the unitary matrix: + /// + /// + /// + /// The angle of rotation. + /// The position of target qubit in register (0 indicates the Least Significant Bit). + /// + /// Optional argument. If given, the method performs a controlled rotation. This argument destribes + /// the control qubit's position. + /// + public void RotateZ(double gamma, int target, int? control = null) + { + if (Root != this) { - if (Root != this) - { - Root.PhaseScale(gamma, target + OffsetToRoot, control + OffsetToRoot); - return; - } + Root.RotateZ(gamma, target + OffsetToRoot, control + OffsetToRoot); + return; + } - if (control.HasValue) - { - var m = new Complex[2, 2]; - var z = Complex.Exp(Complex.ImaginaryOne * gamma); - m[0, 0] = z; - m[0, 1] = Complex.Zero; - m[1, 0] = Complex.Zero; - m[1, 1] = z; - Gate1(m, target, control); - } + var z = Complex.Exp(Complex.ImaginaryOne * (gamma / 2.0)); + var divZ = Complex.Exp(-Complex.ImaginaryOne * (gamma / 2.0)); + var states = _amplitudes.Keys.ToArray(); + var length = states.Length; + for (var k = 0; k < length; k++) + { + var state = states[k]; + + //determine if the control of the basis state is set or not needed + var controlIsSet = control == null || (state & ((ulong)1 << control.Value)) != 0; + if (!controlIsSet) + continue; + // Multiply if the target bit is set + if ((state & ((ulong)1 << target)) != 0) + _amplitudes[state] *= z; else - { - var z = Complex.Exp(Complex.ImaginaryOne * gamma); - var states = _amplitudes.Keys.ToArray(); - var length = states.Length; - for (var k = 0; k < length; k++) - { - var state = states[k]; - _amplitudes[state] *= z; - } - } + _amplitudes[state] *= divZ; } + } - /// - /// - /// Performs a phase kick (or phase shift) on the the register's state. - /// The operation is represented by the unitary matrix: - /// - /// - /// - /// The phase value. - /// The position of target qubit in register (0 indicates the Least Significant Bit). - /// - /// Optional argument. If given, the method performs a controlled phase shift operation. This - /// argument destribes the control qubit's position. - /// - public void PhaseKick(double gamma, int target, params int[] controls) + /// + /// + /// Adds a global phase on the register's state. + /// The operation is represented by the unitary matrix: + /// + /// + /// + /// The phase that is added to the register's state. + /// The position of target qubit in register (0 indicates the Least Significant Bit). + /// + /// Optional argument. If given, the method performs a controlled phase scale operation. This + /// argument destribes the control qubit's position. + /// + public void PhaseScale(double gamma, int target, int? control = null) + { + if (Root != this) { - if (Root != this) - { - for (var i = 0; i < controls.Length; i++) controls[i] += OffsetToRoot; - Root.PhaseKick(gamma, target + OffsetToRoot, controls); - return; - } + Root.PhaseScale(gamma, target + OffsetToRoot, control + OffsetToRoot); + return; + } + if (control.HasValue) + { + var m = new Complex[2, 2]; + var z = Complex.Exp(Complex.ImaginaryOne * gamma); + m[0, 0] = z; + m[0, 1] = Complex.Zero; + m[1, 0] = Complex.Zero; + m[1, 1] = z; + Gate1(m, target, control); + } + else + { var z = Complex.Exp(Complex.ImaginaryOne * gamma); var states = _amplitudes.Keys.ToArray(); var length = states.Length; - var controlsLength = controls.Length; for (var k = 0; k < length; k++) { var state = states[k]; - - // Multiply if the target bit is set - if ((state & ((ulong)1 << target)) == 0) continue; - var i = 0; - if (controlsLength > 0) - while (i < controlsLength && (state & ((ulong)1 << controls[i])) != 0) - i++; - if (controlsLength == 0 || (controlsLength > 0 && i == controlsLength)) _amplitudes[state] *= z; + _amplitudes[state] *= z; } } + } + + /// + /// + /// Performs a phase kick (or phase shift) on the the register's state. + /// The operation is represented by the unitary matrix: + /// + /// + /// + /// The phase value. + /// The position of target qubit in register (0 indicates the Least Significant Bit). + /// + /// Optional argument. If given, the method performs a controlled phase shift operation. This + /// argument destribes the control qubit's position. + /// + public void PhaseKick(double gamma, int target, params int[] controls) + { + if (Root != this) + { + for (var i = 0; i < controls.Length; i++) + controls[i] += OffsetToRoot; + Root.PhaseKick(gamma, target + OffsetToRoot, controls); + return; + } - /// - /// - /// Performs a conditional phase kick (or phase shift) on the register's state by the angle PI / 2 ^ dist. - /// The operation is represented by the unitary matrix: - /// - /// - /// - /// Value of k in the angle of phase shift. The angle equals: PI / 2 ^ k. - /// The position of target qubit in register (0 indicates the Least Significant Bit). - /// The positions of control qubits in register (0 indicates the Least Significant Bit). - public void CPhaseShift(int dist, int target, params int[] controls) + var z = Complex.Exp(Complex.ImaginaryOne * gamma); + var states = _amplitudes.Keys.ToArray(); + var length = states.Length; + var controlsLength = controls.Length; + for (var k = 0; k < length; k++) { - if (Root != this) - { - for (var i = 0; i < controls.Length; i++) controls[i] += OffsetToRoot; - Root.CPhaseShift(dist, target + OffsetToRoot, controls); - return; - } + var state = states[k]; + + // Multiply if the target bit is set + if ((state & ((ulong)1 << target)) == 0) + continue; + var i = 0; + if (controlsLength > 0) + while (i < controlsLength && (state & ((ulong)1 << controls[i])) != 0) + i++; + if (controlsLength == 0 || (controlsLength > 0 && i == controlsLength)) + _amplitudes[state] *= z; + } + } - var controlsLength = controls.Length; - var z = Complex.Exp(Complex.ImaginaryOne * Math.PI / ((ulong)1 << Math.Abs(dist))); - var states = _amplitudes.Keys.ToArray(); - var length = states.Length; - for (var k = 0; k < length; k++) - { - var state = states[k]; - // Multiply if the target and control bits are set - if ((state & ((ulong)1 << target)) == 0) continue; - var i = 0; - if (controlsLength > 0) - while (i < controlsLength && (state & ((ulong)1 << controls[i])) != 0) - i++; - if (controlsLength == 0 || (controlsLength > 0 && i == controlsLength)) _amplitudes[state] *= z; - } + /// + /// + /// Performs a conditional phase kick (or phase shift) on the register's state by the angle PI / 2 ^ dist. + /// The operation is represented by the unitary matrix: + /// + /// + /// + /// Value of k in the angle of phase shift. The angle equals: PI / 2 ^ k. + /// The position of target qubit in register (0 indicates the Least Significant Bit). + /// The positions of control qubits in register (0 indicates the Least Significant Bit). + public void CPhaseShift(int dist, int target, params int[] controls) + { + if (Root != this) + { + for (var i = 0; i < controls.Length; i++) + controls[i] += OffsetToRoot; + Root.CPhaseShift(dist, target + OffsetToRoot, controls); + return; } - /// - /// - /// Performs a inversed conditional phase kick (or phase shift) on the register's state by the angle PI / 2 ^ dist. - /// The operation is represented by the unitary matrix: - /// - /// - /// - /// Value of k in the angle of phase shift. The angle equals: - PI / 2 ^ k. - /// The position of target qubit in register (0 indicates the Least Significant Bit). - /// The position of control qubits in register. - public void InverseCPhaseShift(int dist, int target, params int[] controls) + var controlsLength = controls.Length; + var z = Complex.Exp(Complex.ImaginaryOne * Math.PI / ((ulong)1 << Math.Abs(dist))); + var states = _amplitudes.Keys.ToArray(); + var length = states.Length; + for (var k = 0; k < length; k++) { - if (Root != this) - { - for (var i = 0; i < controls.Length; i++) controls[i] += OffsetToRoot; - Root.InverseCPhaseShift(dist, target + OffsetToRoot, controls); - return; - } + var state = states[k]; + // Multiply if the target and control bits are set + if ((state & ((ulong)1 << target)) == 0) + continue; + var i = 0; + if (controlsLength > 0) + while (i < controlsLength && (state & ((ulong)1 << controls[i])) != 0) + i++; + if (controlsLength == 0 || (controlsLength > 0 && i == controlsLength)) + _amplitudes[state] *= z; + } + } - var controlsLength = controls.Length; - var z = Complex.Exp(-Complex.ImaginaryOne * Math.PI / ((ulong)1 << Math.Abs(dist))); - var states = _amplitudes.Keys.ToArray(); - var length = states.Length; - for (var k = 0; k < length; k++) - { - var state = states[k]; - // Multiply if the target and control bits are set - if ((state & ((ulong)1 << target)) == 0) continue; - var i = 0; - if (controlsLength > 0) - while (i < controlsLength && (state & ((ulong)1 << controls[i])) != 0) - i++; - if (controlsLength == 0 || i == controlsLength) _amplitudes[state] *= z; - } + /// + /// + /// Performs a inversed conditional phase kick (or phase shift) on the register's state by the angle PI / 2 ^ dist. + /// The operation is represented by the unitary matrix: + /// + /// + /// + /// Value of k in the angle of phase shift. The angle equals: - PI / 2 ^ k. + /// The position of target qubit in register (0 indicates the Least Significant Bit). + /// The position of control qubits in register. + public void InverseCPhaseShift(int dist, int target, params int[] controls) + { + if (Root != this) + { + for (var i = 0; i < controls.Length; i++) + controls[i] += OffsetToRoot; + Root.InverseCPhaseShift(dist, target + OffsetToRoot, controls); + return; } - /// - /// Overrides native ToString() method, for friendly printing Register's content. - /// - /// Formatted string representing register. Prints its possible states and their probabilities. - public override string ToString() + var controlsLength = controls.Length; + var z = Complex.Exp(-Complex.ImaginaryOne * Math.PI / ((ulong)1 << Math.Abs(dist))); + var states = _amplitudes.Keys.ToArray(); + var length = states.Length; + for (var k = 0; k < length; k++) { - if (Root == null) return null; - var buf = new StringBuilder(); - IFormatProvider formatter = new ComplexFormatter(); - - var probabilities = GetProbabilities(); - var tmpAmpl = GetAmplitudes(); - var max = (ulong)(1 << Width); - for (ulong i = 0; i < max; i++) - { - if (!probabilities.TryGetValue(i, out var prob)) continue; - if (tmpAmpl != null && tmpAmpl.TryGetValue(i, out var amplitude)) - buf.Append(string.Format(formatter, "{0:I5} ", amplitude)); - else - buf.Append(" "); - var tmp = "|" + i + ">"; - buf.Append($"{tmp,-12}"); - buf.Append($"{(float)prob,-14}").Append("|"); + var state = states[k]; + // Multiply if the target and control bits are set + if ((state & ((ulong)1 << target)) == 0) + continue; + var i = 0; + if (controlsLength > 0) + while (i < controlsLength && (state & ((ulong)1 << controls[i])) != 0) + i++; + if (controlsLength == 0 || i == controlsLength) + _amplitudes[state] *= z; + } + } - for (var j = Width - 1; j >= 0; j--) - { - if (j % 4 == 3) buf.Append(" "); - buf.Append((((ulong)1 << j) & i) >> j); - } - buf.AppendLine(">"); + /// + /// Calculates the reduced density matrix for this register. + /// This method is primarily intended for a single-qubit register to visualize its state on the Bloch Sphere, + /// especially when it is entangled with other qubits. + /// + /// A 2x2 complex matrix representing the reduced density matrix of the register. + /// Thrown if the register is not a single qubit. + public Complex[,] GetReducedDensityMatrix() + { + // Validate that this method is called for a single-qubit register. + if (Width != 1) + throw new ArgumentException("Reduced density matrix calculation is currently supported only for a single qubit register."); + + // If this register is a sub-register of a larger entangled system, + // the calculation must be performed by the root register from the full state vector. + if (Root != this) + { + // Delegate the calculation to the root, specifying the offset of this qubit within the root. + return Root.CalculateReducedDensityMatrixForQubit(this.OffsetToRoot); + } + + // If this register is the root register itself AND it's a single qubit (Width == 1), + // we can directly construct the density matrix from its own amplitudes. + Complex[,] rho = new Complex[2, 2]; + _amplitudes.TryGetValue(0, out Complex alpha); + _amplitudes.TryGetValue(1, out Complex beta); + + // Normalize the amplitudes if their combined magnitude squared is not 1 (due to floating point or uninitialized states). + double magnitudeSquared = Math.Pow(alpha.Magnitude, 2) + Math.Pow(beta.Magnitude, 2); + if (magnitudeSquared == 0) + { + // If no amplitudes, return a zero matrix (representing a completely unpolarized state or an error). + return new Complex[2, 2] { { Complex.Zero, Complex.Zero }, { Complex.Zero, Complex.Zero } }; + } + + double invMagnitude = 1.0 / Math.Sqrt(magnitudeSquared); + alpha *= invMagnitude; + beta *= invMagnitude; + + // The diagonal elements are probabilities: |alpha|^2 and |beta|^2. + rho[0, 0] = Complex.Pow(alpha.Magnitude, 2); + rho[1, 1] = Complex.Pow(beta.Magnitude, 2); + + // The off-diagonal elements are alpha * conj(beta) and conj(alpha) * beta. + rho[0, 1] = alpha * Complex.Conjugate(beta); + rho[1, 0] = Complex.Conjugate(alpha) * beta; + return rho; + } + + private Complex[,] CalculateReducedDensityMatrixForQubit(int targetQubitOffsetInRoot) + { + return DensityMatrixCalculator.Calculate(_amplitudes, this.Width, targetQubitOffsetInRoot); + } + + /// + /// Overrides native ToString() method, for friendly printing Register's content. + /// + /// Formatted string representing register. Prints its possible states and their probabilities. + public override string ToString() + { + if (Root == null) + return null; + var buf = new StringBuilder(); + IFormatProvider formatter = new ComplexFormatter(); + + var probabilities = GetProbabilities(); + var tmpAmpl = GetAmplitudes(); + var max = (ulong)(1 << Width); + for (ulong i = 0; i < max; i++) + { + if (!probabilities.TryGetValue(i, out var prob)) + continue; + if (tmpAmpl != null && tmpAmpl.TryGetValue(i, out var amplitude)) + buf.Append(string.Format(formatter, "{0:I5} ", amplitude)); + else + buf.Append(" "); + var tmp = "|" + i + ">"; + buf.Append($"{tmp,-12}"); + buf.Append($"{(float)prob,-14}").Append("|"); + + for (var j = Width - 1; j >= 0; j--) + { + if (j % 4 == 3) + buf.Append(" "); + buf.Append((((ulong)1 << j) & i) >> j); } - return buf.ToString(); + buf.AppendLine(">"); } - #endregion + return buf.ToString(); } + + #endregion } \ No newline at end of file