diff --git a/src/Simulation/Core/Default.cs b/src/Simulation/Core/Default.cs
new file mode 100644
index 00000000000..315c6c0587d
--- /dev/null
+++ b/src/Simulation/Core/Default.cs
@@ -0,0 +1,92 @@
+#nullable enable
+
+using System;
+using System.Collections.Generic;
+using System.Diagnostics.CodeAnalysis;
+using System.Linq;
+
+namespace Microsoft.Quantum.Simulation.Core
+{
+ ///
+ /// Creates default values of Q# types.
+ ///
+ public static class Default
+ {
+ ///
+ /// A dictionary from basic types to their default values.
+ ///
+ private static readonly IReadOnlyDictionary BasicValues = new Dictionary
+ {
+ [typeof(QRange)] = QRange.Empty,
+ [typeof(QVoid)] = QVoid.Instance,
+ [typeof(Result)] = Result.Zero,
+ [typeof(string)] = ""
+ };
+
+ ///
+ /// A list of all generic tuple types.
+ ///
+ private static readonly IReadOnlyList Tuples = new List
+ {
+ typeof(ValueTuple<>),
+ typeof(ValueTuple<,>),
+ typeof(ValueTuple<,,>),
+ typeof(ValueTuple<,,,>),
+ typeof(ValueTuple<,,,,>),
+ typeof(ValueTuple<,,,,,>),
+ typeof(ValueTuple<,,,,,,>),
+ typeof(ValueTuple<,,,,,,,>)
+ };
+
+ ///
+ /// Returns the default value of the Q# type. May return null when null is the default value of the type, or if
+ /// the type is not a valid Q# type.
+ ///
+ [return: MaybeNull]
+ public static T OfType() => OfType(typeof(T)) is T value ? value : default;
+
+ ///
+ /// Returns the default value of the Q# type. May return null when null is the default value of the type, or if
+ /// the type is not a valid Q# type.
+ ///
+ private static object? OfType(Type type) => OfAnyType(type).FirstOrDefault(value => !(value is null));
+
+ ///
+ /// Enumerates the default values of different kinds of types. Yields null if the given type is not the right
+ /// kind, and yields a non-null value if a default value is found.
+ ///
+ private static IEnumerable OfAnyType(Type type)
+ {
+ yield return BasicValues.GetValueOrDefault(type);
+ yield return OfArrayType(type);
+ yield return OfTupleType(type);
+ yield return OfUserDefinedType(type);
+ }
+
+ ///
+ /// If the given type is a Q# array type, returns the default array of that type, or null otherwise.
+ ///
+ private static object? OfArrayType(Type type) =>
+ type.IsGenericType && type.GetGenericTypeDefinition() == typeof(IQArray<>)
+ ? Activator.CreateInstance(typeof(QArray<>).MakeGenericType(type.GenericTypeArguments))
+ : null;
+
+ ///
+ /// If the given type is a Q# tuple type, returns the default tuple of that type, or null otherwise.
+ ///
+ private static object? OfTupleType(Type type) =>
+ type.IsGenericType && Tuples.Contains(type.GetGenericTypeDefinition())
+ ? Activator.CreateInstance(type, type.GenericTypeArguments.Select(OfType).ToArray())
+ : null;
+
+ ///
+ /// If the given type is a Q# user-defined type, returns the default value of that type, or null otherwise.
+ ///
+ private static object? OfUserDefinedType(Type type) =>
+ !(type.BaseType is null)
+ && type.BaseType.IsGenericType
+ && type.BaseType.GetGenericTypeDefinition() == typeof(UDTBase<>)
+ ? Activator.CreateInstance(type, type.BaseType.GenericTypeArguments.Select(OfType).ToArray())
+ : null;
+ }
+}
diff --git a/src/Simulation/Core/QArray.cs b/src/Simulation/Core/QArray.cs
index de9db576c88..a6e7ac4aac3 100644
--- a/src/Simulation/Core/QArray.cs
+++ b/src/Simulation/Core/QArray.cs
@@ -6,7 +6,6 @@
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
-using System.Reflection;
using Newtonsoft.Json;
namespace Microsoft.Quantum.Simulation.Core
@@ -98,17 +97,10 @@ public QArrayInner(params T[] collection)
}
///
- /// Creates an array of size given by capacity and default-initializes
- /// array elements. Uses C# keyword default to initialize array elements.
+ /// Creates an array of size given by capacity and default-initializes array elements. Uses the default Q#
+ /// value to initialize array elements.
///
- public QArrayInner(long capacity)
- {
- storage = new List((int)capacity);
- for (var i = 0L; i < capacity; ++i)
- {
- storage.Add(CreateDefault());
- }
- }
+ public QArrayInner(long capacity) => Extend(capacity);
public T GetElement(long index) =>
storage == null
@@ -143,20 +135,19 @@ public void UnsafeSetElement(long index, T value)
public void Extend(long newLength)
{
- if (storage == null)
+ var newLengthInt = Convert.ToInt32(newLength);
+ if (storage is null)
{
- storage = new List();
+ storage = new List(newLengthInt);
}
- long oldLength = storage.Count;
- for (int i = 0; i < (newLength - oldLength); i++)
+ else if (storage.Capacity < newLengthInt)
{
- T obj = CreateDefault();
- storage.Add(obj);
+ storage.Capacity = newLengthInt;
}
+ storage.AddRange(Enumerable.Repeat(Default.OfType(), newLengthInt - storage.Count));
}
}
-
private QArrayInner storage;
private long start = 0;
private long step = 1;
@@ -174,25 +165,6 @@ private void CopyAndCompress()
step = 1;
}
- // Returns the default value of an object of this type of array. Normally null or 0, but for things like
- // ValueTuples, it returns an empty instance of that value tuple.
- private static T CreateDefault()
- {
- if (typeof(T).IsValueType || typeof(T).IsAbstract || typeof(T) == typeof(String) || typeof(T) == typeof(QVoid))
- {
- return default(T);
- }
- else
- {
- // First look for an empty constructor
- ConstructorInfo defaultConstructor = typeof(T).GetConstructor(Type.EmptyTypes);
- return defaultConstructor != null
- ? (T)(defaultConstructor.Invoke(new object[] { }))
- : Activator.CreateInstance();
- }
- }
-
-
///
/// Create an array of length 0.
///
@@ -508,7 +480,7 @@ public QArrayEnumerator(QArray qArray)
currentIndex = -1;
}
- public T Current => currentIndex >= 0 ? array[currentIndex] : CreateDefault();
+ public T Current => currentIndex >= 0 ? array[currentIndex] : Default.OfType();
object IEnumerator.Current => this.Current;
diff --git a/src/Simulation/Core/QRange.cs b/src/Simulation/Core/QRange.cs
index ff8133ccfac..14f10f86607 100644
--- a/src/Simulation/Core/QRange.cs
+++ b/src/Simulation/Core/QRange.cs
@@ -14,7 +14,7 @@ namespace Microsoft.Quantum.Simulation.Core
///
public class QRange : IEnumerable
{
- public QRange() : this(0, 1, 0)
+ public QRange() : this(1, 1, 0)
{
}
@@ -61,8 +61,7 @@ public QRange(long start, long end) : this(start, 1, end)
///
/// Returns an empty range.
///
- public static QRange Empty =>
- new QRange(0L, -1L);
+ public static QRange Empty => new QRange();
///
/// Returns true if the range is empty.
diff --git a/src/Simulation/CsharpGeneration.Tests/SimulationCodeTests.fs b/src/Simulation/CsharpGeneration.Tests/SimulationCodeTests.fs
index 4d050ca2686..8b28e6cc3d5 100644
--- a/src/Simulation/CsharpGeneration.Tests/SimulationCodeTests.fs
+++ b/src/Simulation/CsharpGeneration.Tests/SimulationCodeTests.fs
@@ -2924,7 +2924,7 @@ internal partial class EmptyInternalOperation : Operation, ICallab
"""
public class U : UDTBase, IApplyData
{
- public U() : base(default(IUnitary))
+ public U() : base(global::Microsoft.Quantum.Simulation.Core.Default.OfType())
{
}
@@ -2950,7 +2950,7 @@ internal partial class EmptyInternalOperation : Operation, ICallab
"""
public class AA : UDTBase, IApplyData
{
- public AA() : base(default(A))
+ public AA() : base(global::Microsoft.Quantum.Simulation.Core.Default.OfType ())
{
}
@@ -2976,7 +2976,7 @@ internal partial class EmptyInternalOperation : Operation, ICallab
"""
public class Q : UDTBase, IApplyData
{
- public Q() : base(default(Qubit))
+ public Q() : base(global::Microsoft.Quantum.Simulation.Core.Default.OfType())
{
}
@@ -3002,7 +3002,7 @@ internal partial class EmptyInternalOperation : Operation, ICallab
"""
public class QQ : UDTBase, IApplyData
{
- public QQ() : base(default(Q))
+ public QQ() : base(global::Microsoft.Quantum.Simulation.Core.Default.OfType())
{
}
@@ -3028,7 +3028,7 @@ internal partial class EmptyInternalOperation : Operation, ICallab
"""
public class Qubits : UDTBase>, IApplyData
{
- public Qubits() : base(new QArray())
+ public Qubits() : base(global::Microsoft.Quantum.Simulation.Core.Default.OfType>())
{
}
@@ -3054,7 +3054,7 @@ internal partial class EmptyInternalOperation : Operation, ICallab
"""
public class udt_args1 : UDTBase<(Int64,IQArray)>, IApplyData
{
- public udt_args1() : base(default((Int64,IQArray)))
+ public udt_args1() : base(global::Microsoft.Quantum.Simulation.Core.Default.OfType<(Int64,IQArray)>())
{
}
@@ -3084,7 +3084,7 @@ internal partial class EmptyInternalOperation : Operation, ICallab
"""
public class udt_Real : UDTBase, IApplyData
{
- public udt_Real() : base(default(Double))
+ public udt_Real() : base(global::Microsoft.Quantum.Simulation.Core.Default.OfType())
{
}
@@ -3104,7 +3104,7 @@ internal partial class EmptyInternalOperation : Operation, ICallab
"""
public class udt_Complex : UDTBase<(udt_Real,udt_Real)>, IApplyData
{
- public udt_Complex() : base(default((udt_Real,udt_Real)))
+ public udt_Complex() : base(global::Microsoft.Quantum.Simulation.Core.Default.OfType<(udt_Real,udt_Real)>())
{
}
@@ -3127,7 +3127,7 @@ internal partial class EmptyInternalOperation : Operation, ICallab
"""
public class udt_TwoDimArray : UDTBase>>, IApplyData
{
- public udt_TwoDimArray() : base(new QArray>())
+ public udt_TwoDimArray() : base(global::Microsoft.Quantum.Simulation.Core.Default.OfType>>())
{
}
@@ -3149,7 +3149,7 @@ internal partial class EmptyInternalOperation : Operation, ICallab
"""
internal class InternalType : UDTBase, IApplyData
{
- public InternalType() : base(default(QVoid))
+ public InternalType() : base(global::Microsoft.Quantum.Simulation.Core.Default.OfType())
{
}
@@ -3171,7 +3171,7 @@ internal class InternalType : UDTBase, IApplyData
"""
public class NamedTuple : UDTBase<((Int64,Double),Int64)>, IApplyData
{
- public NamedTuple() : base(default(((Int64,Double),Int64)))
+ public NamedTuple() : base(global::Microsoft.Quantum.Simulation.Core.Default.OfType<((Int64,Double),Int64)>())
{
}
@@ -3231,7 +3231,7 @@ namespace Microsoft.Quantum
{
public class Pair : UDTBase<(Int64,Int64)>, IApplyData
{
- public Pair() : base(default((Int64,Int64)))
+ public Pair() : base(global::Microsoft.Quantum.Simulation.Core.Default.OfType<(Int64,Int64)>())
{
}
@@ -3251,7 +3251,7 @@ namespace Microsoft.Quantum
public class Unused : UDTBase<(Int64,Int64)>, IApplyData
{
- public Unused() : base(default((Int64,Int64)))
+ public Unused() : base(global::Microsoft.Quantum.Simulation.Core.Default.OfType<(Int64,Int64)>())
{
}
@@ -3337,7 +3337,7 @@ namespace Microsoft.Quantum
{
public class Pair : UDTBase<(Int64,Int64)>, IApplyData
{
- public Pair() : base(default((Int64,Int64)))
+ public Pair() : base(global::Microsoft.Quantum.Simulation.Core.Default.OfType<(Int64,Int64)>())
{
}
@@ -3361,7 +3361,7 @@ namespace Microsoft.Quantum
public class NestedPair : UDTBase<(Double,((Boolean,String),Int64))>, IApplyData
{
- public NestedPair() : base(default((Double,((Boolean,String),Int64))))
+ public NestedPair() : base(global::Microsoft.Quantum.Simulation.Core.Default.OfType<(Double,((Boolean,String),Int64))>())
{
}
@@ -3616,7 +3616,7 @@ namespace Microsoft.Quantum.Core
{
public class Attribute : UDTBase, IApplyData
{
- public Attribute() : base(default(QVoid))
+ public Attribute() : base(global::Microsoft.Quantum.Simulation.Core.Default.OfType())
{
}
@@ -3636,7 +3636,7 @@ namespace Microsoft.Quantum.Diagnostics
{
public class Test : UDTBase, IApplyData
{
- public Test() : base(default(String))
+ public Test() : base(global::Microsoft.Quantum.Simulation.Core.Default.OfType())
{
}
diff --git a/src/Simulation/CsharpGeneration/SimulationCode.fs b/src/Simulation/CsharpGeneration/SimulationCode.fs
index ba57ba79c55..b7db959d2a4 100644
--- a/src/Simulation/CsharpGeneration/SimulationCode.fs
+++ b/src/Simulation/CsharpGeneration/SimulationCode.fs
@@ -1461,15 +1461,12 @@ module SimulationCode =
let context = globalContext.setUdt udt
let name = userDefinedName None udt.FullName.Name.Value
let qsharpType = udt.Type
- let buildEmtpyConstructor =
- let baseTupleType =
- match qsharpType.Resolution with
- | ArrayType b -> roslynTypeName context b |> sprintf "QArray<%s>"
- | _ -> (roslynTypeName context qsharpType)
- let defaultValue = match qsharpType.Resolution with | ArrayType _ -> [ sprintf "new %s()" baseTupleType] | _ -> [ sprintf "default(%s)" baseTupleType ]
- let args = []
- ``constructor`` name ``(`` args ``)``
- ``:`` defaultValue
+ let buildEmptyConstructor =
+ let defaultValue =
+ roslynTypeName context qsharpType
+ |> sprintf "global::Microsoft.Quantum.Simulation.Core.Default.OfType<%s>()"
+ ``constructor`` name ``(`` [] ``)``
+ ``:`` [ defaultValue ]
[ ``public`` ]
``{``
[]
@@ -1540,7 +1537,7 @@ module SimulationCode =
let baseClass = ``simpleBase`` baseClassName
let modifiers = [ classAccessModifier udt.Modifiers.Access ]
let interfaces = [ ``simpleBase`` "IApplyData" ]
- let constructors = [ buildEmtpyConstructor; buildBaseTupleConstructor ]
+ let constructors = [ buildEmptyConstructor; buildBaseTupleConstructor ]
let qubitsField = buildQubitsField context qsharpType
let itemFields = buildNamedItemFields @ buildItemFields
let allFields = itemFields @ qubitsField
diff --git a/src/Simulation/Simulators.Tests/Circuits/Default.qs b/src/Simulation/Simulators.Tests/Circuits/Default.qs
new file mode 100644
index 00000000000..55e1853dfc7
--- /dev/null
+++ b/src/Simulation/Simulators.Tests/Circuits/Default.qs
@@ -0,0 +1,91 @@
+namespace Microsoft.Quantum.Simulation.Simulators.Tests.Circuits.Default {
+ open Microsoft.Quantum.Diagnostics;
+ open Microsoft.Quantum.Simulation.Simulators.Tests.Circuits;
+
+ @Test("QuantumSimulator")
+ function DefaultUnit() : Unit {
+ AssertEqual((), Default());
+ }
+
+ @Test("QuantumSimulator")
+ function DefaultInt() : Unit {
+ AssertEqual(0, Default());
+ }
+
+ @Test("QuantumSimulator")
+ function DefaultBigInt() : Unit {
+ AssertEqual(0L, Default());
+ }
+
+ @Test("QuantumSimulator")
+ function DefaultDouble() : Unit {
+ AssertEqual(0.0, Default());
+ }
+
+ @Test("QuantumSimulator")
+ function DefaultBool() : Unit {
+ AssertEqual(false, Default());
+ }
+
+ @Test("QuantumSimulator")
+ function DefaultString() : Unit {
+ AssertEqual("", Default());
+ }
+
+ @Test("QuantumSimulator")
+ function DefaultQubit() : Unit {
+ // Creating a default qubit (without using it) should succeed.
+ let _ = Default();
+ }
+
+ @Test("QuantumSimulator")
+ function DefaultPauli() : Unit {
+ AssertEqual(PauliI, Default());
+ }
+
+ @Test("QuantumSimulator")
+ function DefaultResult() : Unit {
+ AssertEqual(Zero, Default());
+ }
+
+ @Test("QuantumSimulator")
+ function DefaultRange() : Unit {
+ let range = Default();
+ AssertEqual(1, RangeStart(range));
+ AssertEqual(1, RangeStep(range));
+ AssertEqual(0, RangeEnd(range));
+ }
+
+ @Test("QuantumSimulator")
+ function DefaultCallable() : Unit {
+ // Creating a default callable (without calling it) should succeed.
+ let _ = Default<(Unit -> Unit)>();
+ }
+
+ @Test("QuantumSimulator")
+ function DefaultArray() : Unit {
+ AssertEqual(new Unit[0], Default());
+ }
+
+ @Test("QuantumSimulator")
+ function DefaultTuple() : Unit {
+ AssertEqual((false, 0), Default<(Bool, Int)>());
+ AssertEqual((0, Zero, ""), Default<(Int, Result, String)>());
+ AssertEqual(("", "", "", ""), Default<(String, String, String, String)>());
+ AssertEqual(("", "", "", "", ""), Default<(String, String, String, String, String)>());
+ AssertEqual(("", "", "", "", "", ""), Default<(String, String, String, String, String, String)>());
+ AssertEqual(("", "", "", "", "", "", ""), Default<(String, String, String, String, String, String, String)>());
+ AssertEqual(("", "", "", "", "", "", "", ""), Default<(String, String, String, String, String, String, String, String)>());
+ AssertEqual(("", "", "", "", "", "", "", "", ""), Default<(String, String, String, String, String, String, String, String, String)>());
+ }
+
+ newtype BoolInt = (Bool, Int);
+ newtype IntResultString = (Int, Result, String);
+
+ @Test("QuantumSimulator")
+ function DefaultUserDefinedType() : Unit {
+ AssertEqual(BoolInt(false, 0), Default());
+ AssertEqual(IntResultString(0, Zero, ""), Default());
+ AssertEqual((BoolInt(false, 0), IntResultString(0, Zero, "")), Default<(BoolInt, IntResultString)>());
+ }
+}
diff --git a/src/Simulation/Simulators.Tests/CoreTests.cs b/src/Simulation/Simulators.Tests/CoreTests.cs
index 88917ff4c24..b087a623f40 100644
--- a/src/Simulation/Simulators.Tests/CoreTests.cs
+++ b/src/Simulation/Simulators.Tests/CoreTests.cs
@@ -6,6 +6,7 @@
using System.IO;
using System.Reflection;
using System.Text;
+using Microsoft.Quantum.Core;
using Microsoft.Quantum.QsCompiler;
using Microsoft.Quantum.Simulation.Common;
using Microsoft.Quantum.Simulation.Core;
@@ -292,6 +293,14 @@ public void BigInts()
});
}
+ [Fact]
+ public void DefaultQubitIsNull() => OperationsTestHelper.RunWithMultipleSimulators(async simulator =>
+ Assert.Null(await Default.Run(simulator)));
+
+ [Fact]
+ public void DefaultCallableIsNull() => OperationsTestHelper.RunWithMultipleSimulators(async simulator =>
+ Assert.Null(await Default.Run(simulator)));
+
[Fact]
public void CatchFail()
{
diff --git a/src/Simulation/Simulators.Tests/QTupleTests.cs b/src/Simulation/Simulators.Tests/QTupleTests.cs
index 0f5501b8a3b..ba8c49c1400 100644
--- a/src/Simulation/Simulators.Tests/QTupleTests.cs
+++ b/src/Simulation/Simulators.Tests/QTupleTests.cs
@@ -37,7 +37,7 @@ public void TupleEmptyConstructor()
}
{
var actual = new TupleC();
- Assert.Equal(default((Qubit, TupleB)), ((IApplyData)actual).Value);
+ Assert.Equal((null as Qubit, new TupleB()), ((IApplyData)actual).Value);
NullOrEmptyQubits(actual);
}
{
@@ -47,7 +47,10 @@ public void TupleEmptyConstructor()
}
{
var actual = new TupleE();
- Assert.Equal(default((Int64, IQArray)), ((IApplyData)actual).Value);
+ var value = ((IApplyData)actual).Value as (long, IQArray)?;
+ Assert.True(value.HasValue);
+ Assert.Equal(0, value.Value.Item1);
+ Assert.Equal(0, value.Value.Item2?.Length);
NullOrEmptyQubits(actual);
}
{
@@ -57,7 +60,15 @@ public void TupleEmptyConstructor()
}
{
var actual = new TupleH();
- Assert.Equal(default((TupleD, TupleG)), ((IApplyData)actual).Value);
+ var value = ((IApplyData)actual).Value as (TupleD, TupleG)?;
+ Assert.True(value.HasValue);
+ Assert.NotNull(value.Value.Item1);
+ Assert.NotNull(value.Value.Item2);
+ Assert.Equal(0, value.Value.Item1.Data?.Length);
+ Assert.Null(value.Value.Item2.Item1);
+ Assert.Equal(new TupleF(), value.Value.Item2.Item2);
+ Assert.Equal(new TupleC(), value.Value.Item2.Item3);
+ Assert.Equal(0, value.Value.Item2.Item4?.Data?.Length);
NullOrEmptyQubits(actual);
}
{