diff --git a/src/Simulation/Core/IOperationFactory.cs b/src/Simulation/Core/IOperationFactory.cs index 70310776a17..eedca83664b 100644 --- a/src/Simulation/Core/IOperationFactory.cs +++ b/src/Simulation/Core/IOperationFactory.cs @@ -24,7 +24,7 @@ public interface IOperationFactory /// /// Returns an instance of the requested operation. /// - I Get(Type t); + I Get(Type T); /// /// Returns an instance of the requested operation. diff --git a/src/Simulation/Native/src/simulator/capi.hpp b/src/Simulation/Native/src/simulator/capi.hpp index 260b8a43905..2c33478b56b 100644 --- a/src/Simulation/Native/src/simulator/capi.hpp +++ b/src/Simulation/Native/src/simulator/capi.hpp @@ -22,8 +22,6 @@ extern "C" MICROSOFT_QUANTUM_DECL void destroy(_In_ unsigned sid); // NOLINT MICROSOFT_QUANTUM_DECL void seed(_In_ unsigned sid, _In_ unsigned s); // NOLINT MICROSOFT_QUANTUM_DECL void Dump(_In_ unsigned sid, _In_ bool (*callback)(size_t, double, double)); - - // TODO(rokuzmin): What does it return? MICROSOFT_QUANTUM_DECL bool DumpQubits( _In_ unsigned sid, _In_ unsigned n, diff --git a/src/Simulation/Native/src/simulator/simulatorinterface.hpp b/src/Simulation/Native/src/simulator/simulatorinterface.hpp index c23fd2df123..7a85d65aef6 100644 --- a/src/Simulation/Native/src/simulator/simulatorinterface.hpp +++ b/src/Simulation/Native/src/simulator/simulatorinterface.hpp @@ -99,7 +99,6 @@ class SimulatorInterface { assert(false); } - // TODO(rokuzmin): What does it return? virtual bool dumpQubits(std::vector const& qs, bool (*callback)(size_t, double, double)) { assert(false); diff --git a/src/Simulation/Simulators/QuantumSimulator/Dump.cs b/src/Simulation/Simulators/QuantumSimulator/Dump.cs index 90e91105734..d57d0b7dd4a 100644 --- a/src/Simulation/Simulators/QuantumSimulator/Dump.cs +++ b/src/Simulation/Simulators/QuantumSimulator/Dump.cs @@ -23,29 +23,48 @@ protected virtual QVoid Dump(T target, IQArray? qubits = null) { var filename = (target is QVoid) ? "" : target.ToString(); - // If no file provided, output to the console; + QVoid process(Action channel) + { + var ids = qubits?.Select(q => (uint)q.Id).ToArray() ?? QubitIds; + + var dumper = new SimpleDumper(this, channel); + channel($"# wave function for qubits with ids (least to most significant): {string.Join(";", ids)}"); + + if (!dumper.Dump(qubits)) + { + channel("## Qubits were entangled with an external qubit. Cannot dump corresponding wave function. ##"); + } + + return QVoid.Instance; + } + + var logMessage = this.Get, Microsoft.Quantum.Intrinsic.Message>(); + + // If no file provided, use `Message` to generate the message into the console; if (string.IsNullOrWhiteSpace(filename)) { - new DisplayableStateDumper(this).Dump(qubits); + var op = this.Get, Microsoft.Quantum.Intrinsic.Message>(); + return process((msg) => op.Apply(msg)); } else { try { - using var file = new StreamWriter(filename); - new DisplayableStateDumper(this, file.WriteLine).Dump(qubits); + using (var file = new StreamWriter(filename)) + { + return process(file.WriteLine); + } } catch (Exception e) { - var logMessage = this.Get, Microsoft.Quantum.Intrinsic.Message>(); logMessage.Apply($"[warning] Unable to write state to '{filename}' ({e.Message})"); + return QVoid.Instance; } } - return QVoid.Instance; } - public class QsimDumpMachine : Quantum.Diagnostics.DumpMachine // Is inherited (and replaced at runtime) - { // by (iqsharp's) JupyterDumpMachine. + public class QsimDumpMachine : Quantum.Diagnostics.DumpMachine + { private QuantumSimulator Simulator { get; } public QsimDumpMachine(QuantumSimulator m) : base(m) @@ -61,8 +80,8 @@ public QsimDumpMachine(QuantumSimulator m) : base(m) }; } - public class QSimDumpRegister : Quantum.Diagnostics.DumpRegister // Is inherited (and replaced at runtime) - { // by (iqsharp's) JupyterDumpRegister. + public class QSimDumpRegister : Quantum.Diagnostics.DumpRegister + { private QuantumSimulator Simulator { get; } public QSimDumpRegister(QuantumSimulator m) : base(m) diff --git a/src/Simulation/Simulators/QuantumSimulator/NativeImports.cs b/src/Simulation/Simulators/QuantumSimulator/NativeImports.cs index 6ff07af9522..30b84a41e8d 100644 --- a/src/Simulation/Simulators/QuantumSimulator/NativeImports.cs +++ b/src/Simulation/Simulators/QuantumSimulator/NativeImports.cs @@ -86,7 +86,6 @@ public partial class QuantumSimulator [DllImport(QSIM_DLL_NAME, ExactSpelling = true, CallingConvention = CallingConvention.Cdecl, EntryPoint = "Dump")] private static extern void sim_Dump(uint id, DumpCallback callback); - // TODO(rokuzmin): What does it return? [DllImport(QSIM_DLL_NAME, ExactSpelling = true, CallingConvention = CallingConvention.Cdecl, EntryPoint = "DumpQubits")] private static extern bool sim_DumpQubits(uint id, uint cout, uint[] ids, DumpCallback callback); diff --git a/src/Simulation/Simulators/QuantumSimulator/SimulatorBase.cs b/src/Simulation/Simulators/QuantumSimulator/SimulatorBase.cs index a5a0e4ef67e..8bb52e118e4 100644 --- a/src/Simulation/Simulators/QuantumSimulator/SimulatorBase.cs +++ b/src/Simulation/Simulators/QuantumSimulator/SimulatorBase.cs @@ -93,9 +93,9 @@ public SimulatorBase(IQubitManager? qubitManager = null, int? seed = null) } } - public I Get(Type t) + public I Get(Type T) { - return (I)this.GetInstance(t); + return (I)this.GetInstance(T); } /// diff --git a/src/Simulation/Simulators/QuantumSimulator/StateDumper.cs b/src/Simulation/Simulators/QuantumSimulator/StateDumper.cs index 7795f0082db..a7c351fad69 100644 --- a/src/Simulation/Simulators/QuantumSimulator/StateDumper.cs +++ b/src/Simulation/Simulators/QuantumSimulator/StateDumper.cs @@ -2,13 +2,7 @@ // Licensed under the MIT License. using System; -using System.Collections.Generic; -using System.Linq; -using System.Numerics; using Microsoft.Quantum.Simulation.Core; -using Newtonsoft.Json; -using Newtonsoft.Json.Converters; - namespace Microsoft.Quantum.Simulation.Simulators { @@ -20,7 +14,7 @@ public partial class QuantumSimulator /// The callback function is triggered for every state basis /// vector in the wavefunction. /// - public abstract class StateDumper // Is inherited by (iqsharp's) JupyterDisplayDumper. + public abstract class StateDumper { /// /// Basic constructor. Takes the simulator to probe. @@ -50,7 +44,6 @@ public StateDumper(QuantumSimulator qsim) /// /// Entry method to get the dump of the wave function. /// - // TODO(rokuzmin): What does it return? public virtual bool Dump(IQArray? qubits = null) { if (qubits == null) @@ -67,190 +60,29 @@ public virtual bool Dump(IQArray? qubits = null) } /// - /// The convention to be used in labeling computational basis states - /// given their representations as strings of classical bits. + /// A simple implementation of a . It outputs the + /// a string representation of the state to the given channel. /// - [JsonConverter(typeof(StringEnumConverter))] - public enum BasisStateLabelingConvention + public class SimpleDumper : StateDumper { - /// - /// Label computational states directly by their bit strings. - /// - /// - /// Following this convention, the state |0⟩ ⊗ |1⟩ ⊗ |1⟩ is labeled - /// by |011⟩. - /// - Bitstring, - - /// - /// Label computational states directly by interpreting their bit - /// strings as little-endian encoded integers. - /// - /// - /// Following this convention, the state |0⟩ ⊗ |1⟩ ⊗ |1⟩ is labeled - /// by |6⟩. - /// - LittleEndian, - - /// - /// Label computational states directly by interpreting their bit - /// strings as big-endian encoded integers. - /// - /// - /// Following this convention, the state |0⟩ ⊗ |1⟩ ⊗ |1⟩ is labeled - /// by |3⟩. - /// - BigEndian - } - - /// - /// Represents a quantum state vector and all metadata needed to display - /// that state vector. - /// - public class DisplayableState - { - private static readonly IComparer ToIntComparer = - Comparer.Create((label1, label2) => - Comparer.Default.Compare( - Int32.Parse(label1), Int32.Parse(label2) - ) - ); - - /// - /// Metadata to be used when serializing to JSON, allowing code - /// in other languages to determine what representation is used - /// for this state. - /// - [JsonProperty("diagnostic_kind")] - private string DiagnosticKind => "state-vector"; - - // TODO(rokuzmin): Get back to this after the IQ#-side PR: - // /// - // /// ID for an HTML element where the vertical measurement probability histogram - // /// will be displayed. - // /// - // [JsonProperty("div_id")] - // public string DivId { get; set; } = string.Empty; - - /// - /// The indexes of each qubit on which this state is defined, or - /// null if these indexes are not known. - /// - [JsonProperty("qubit_ids")] - public IEnumerable? QubitIds { get; set; } - - /// - /// The number of qubits on which this state is defined. - /// - [JsonProperty("n_qubits")] - public int NQubits { get; set; } + private int _maxCharsStateId; - /// - /// These amplitudes represent the computational basis states - /// labeled in little-endian order, as per the behavior of - /// . - /// - [JsonProperty("amplitudes")] - public Complex[]? Amplitudes { get; set; } - - - // TODO(rokuzmin): Make this an extension method for `QuantumSimulator.DisplayableState` in IQSharp repo: - // /// - // /// An enumerable source of the significant amplitudes of this state - // /// vector and their labels, where significance and labels are - // /// defined by the values loaded from . - // /// - // public IEnumerable<(Complex, string)> SignificantAmplitudes( - // IConfigurationSource configurationSource - // ) => SignificantAmplitudes( - // configurationSource.BasisStateLabelingConvention, - // configurationSource.TruncateSmallAmplitudes, - // configurationSource.TruncationThreshold - // ); - - /// - /// An enumerable source of the significant amplitudes of this state - /// vector and their labels. - /// - /// - /// The convention to be used in labeling each computational basis state. - /// - /// - /// Whether to truncate small amplitudes. - /// - /// - /// If is true, - /// then amplitudes whose absolute value squared are below this - /// threshold are suppressed. - /// - public IEnumerable<(Complex, string)> SignificantAmplitudes( - BasisStateLabelingConvention convention, - bool truncateSmallAmplitudes, double truncationThreshold - ) => - ( - truncateSmallAmplitudes - ? Amplitudes - .Select((amplitude, idx) => (amplitude, idx)) - .Where(item => - System.Math.Pow(item.amplitude.Magnitude, 2.0) >= truncationThreshold - ) - : Amplitudes.Select((amplitude, idx) => (amplitude, idx)) - ) - .Select( - item => (item.amplitude, BasisStateLabel(convention, item.idx)) - ) - .OrderBy( - item => item.Item2, - // If a basis state label is numeric, we want to compare - // numerically rather than lexographically. - convention switch { - BasisStateLabelingConvention.BigEndian => ToIntComparer, - BasisStateLabelingConvention.LittleEndian => ToIntComparer, - _ => Comparer.Default - } - ); + public SimpleDumper(QuantumSimulator qsim, Action channel) : base(qsim) + { + this.Channel = channel; + } /// - /// Using the given labeling convention, returns the label for a - /// computational basis state described by its bit string as encoded - /// into an integer index in the little-endian encoding. + /// A method to call to output a string representation of the amplitude of each + /// state basis vector. /// - public string BasisStateLabel( - BasisStateLabelingConvention convention, int index - ) => convention switch - { - BasisStateLabelingConvention.Bitstring => - String.Concat( - System - .Convert - .ToString(index, 2) - .PadLeft(NQubits, '0') - .Reverse() - ), - BasisStateLabelingConvention.BigEndian => - System.Convert.ToInt64( - String.Concat( - System.Convert.ToString(index, 2).PadLeft(NQubits, '0').Reverse() - ), - fromBase: 2 - ) - .ToString(), - BasisStateLabelingConvention.LittleEndian => - index.ToString(), - _ => throw new ArgumentException($"Invalid basis state labeling convention {convention}.") - }; - + public virtual Action Channel { get; } /// /// Returns a string that represents the label for the given base state. /// - public virtual string FormatBaseState(uint idx) - { - int qubitCount = Amplitudes?.Length ?? 0; - int maxCharsStateId = ((1 << qubitCount) - 1).ToString().Length; - - return $"∣{idx.ToString().PadLeft(maxCharsStateId, ' ')}❭"; - } + public virtual string FormatBaseState(uint idx) => + $"∣{idx.ToString().PadLeft(_maxCharsStateId, ' ')}❭"; /// /// Returns a string that represents the magnitude of the amplitude. @@ -323,168 +155,25 @@ public virtual string Format(uint idx, double real, double img) } - public string ToString(BasisStateLabelingConvention convention, // Non-override. Parameterized. - bool truncateSmallAmplitudes, - double truncationThreshold) - { - return string.Join('\n', - SignificantAmplitudes(convention, truncateSmallAmplitudes, truncationThreshold) - .Select( - item => - { - var (cmplx, basisLabel) = item; - var amplitude = (cmplx.Real * cmplx.Real) + (cmplx.Imaginary * cmplx.Imaginary); - var angle = System.Math.Atan2(cmplx.Imaginary, cmplx.Real); - return $"|{basisLabel}⟩\t{FormatCartesian(cmplx.Real, cmplx.Imaginary)}\t == \t" + - $"{FormatPolar(amplitude, angle)}"; - })); - } - - public override string ToString() => // An override of the `object.ToString()`. - ToString(BasisStateLabelingConvention.LittleEndian, false, 0.0); - - } // public class DisplayableState - - - /// - /// A state dumper that encodes dumped states into displayable - /// objects. - /// - public class DisplayableStateDumper : StateDumper - { - private long _count = -1; - private Complex[]? _data = null; - - // TODO(rokuzmin): Remove the commented-out code below once everything works on IQSharp side. - // /// - // /// Whether small amplitudes should be truncated when dumping - // /// states. - // /// - // public bool TruncateSmallAmplitudes { get; set; } = false; - - // /// - // /// The threshold for truncating measurement probabilities when - // /// dumping states. Computational basis states whose measurement - // /// probabilities (i.e: squared magnitudes) are below this threshold - // /// are subject to truncation when - // /// - // /// is true. - // /// - // public double TruncationThreshold { get; set; } = 1e-10; - - // /// - // /// The labeling convention to be used when labeling computational - // /// basis states. - // /// - // public BasisStateLabelingConvention BasisStateLabelingConvention { get; set; } = BasisStateLabelingConvention.Bitstring; - - // /// - // /// Whether the measurement probabilities will be displayed as an - // /// interactive histogram. - // /// - // private bool ShowMeasurementDisplayHistogram { get; set; } = false; - - // /// - // /// Sets the properties of this display dumper from a given - // /// configuration source. - // /// - // public JupyterDisplayDumper Configure(IConfigurationSource configurationSource) - // { - // configurationSource - // .ApplyConfiguration("dump.measurementDisplayHistogram", value => ShowMeasurementDisplayHistogram = value) - // .ApplyConfiguration("dump.truncateSmallAmplitudes", value => TruncateSmallAmplitudes = value) - // .ApplyConfiguration("dump.truncationThreshold", value => TruncationThreshold = value) - // .ApplyConfiguration( - // "dump.basisStateLabelingConvention", value => BasisStateLabelingConvention = value - // ); - // return this; - // } - /// - /// A method to call to output a string representation. - /// - public virtual Action? FileWriter { get; } - - /// - /// Constructs a new display dumper for a given simulator. - /// - public DisplayableStateDumper(QuantumSimulator sim, Action? fileWriter = null) : base(sim) - { - this.FileWriter = fileWriter; - } - - /// - /// Used by the simulator to provide states when dumping. - /// Not intended to be called directly. + /// The callback method. Formats the given state and invokes the /// + /// True, so the entire wave function is dumped. public override bool Callback(uint idx, double real, double img) { - if (_data == null) throw new Exception("Expected data buffer to be initialized before callback, but it was null."); - _data[idx] = new Complex(real, img); + Channel(Format(idx, real, img)); return true; } - /// - /// Dumps the state of a register of qubits as a displayable object. - /// - // TODO(rokuzmin): What does it return? public override bool Dump(IQArray? qubits = null) { - System.Diagnostics.Debug.Assert(this.Simulator.QubitManager != null, - "Internal logic error, QubitManager must be assigned"); - - _count = qubits == null - ? this.Simulator.QubitManager.AllocatedQubitsCount - : qubits.Length; - _data = new Complex[1 << ((int)_count)]; // If 0 qubits are allocated then the array has - // a single element. The Hilbert space of the system is - // ℂ¹ (that is, complex-valued scalars). - var result = base.Dump(qubits); - - // At this point, _data should be filled with the full state - // vector, so let's display it, counting on the right display - // encoder to be there to pack it into a table. - - // var id = System.Guid.NewGuid(); - var state = new DisplayableState - { - // We cast here as we don't support a large enough number - // of qubits to saturate an int. - QubitIds = qubits?.Select(q => q.Id) ?? Simulator.QubitIds.Select(q => (int)q) ?? Enumerable.Empty(), - NQubits = (int)_count, - Amplitudes = _data, - // DivId = $"dump-machine-div-{id}" - }; - - if (this.FileWriter != null) - { - this.FileWriter(state.ToString()); - } - else - { - Simulator.MaybeDisplayDiagnostic(state); - } + var count = qubits == null + ? this.Simulator.QubitManager.AllocatedQubitsCount + : qubits.Length; + this._maxCharsStateId = ((1 << (int)count) - 1).ToString().Length; - - // TODO(rokuzmin): Work on this thoroughly on the IQSharp side: - // if (ShowMeasurementDisplayHistogram) - // { - // Debug.Assert(Channel.CommsRouter != null, "Display dumper requires comms router."); - // Channel.CommsRouter.OpenSession( - // "iqsharp_state_dump", - // new MeasurementHistogramContent() - // { - // State = state - // } - // ).Wait(); - // } - - // Clean up the state vector buffer. - _data = null; - - return result; + return base.Dump(qubits); } - } } }