Skip to content

Unity API Reference ​

The package uses three namespaces:

NamespaceContents
QRG.QuantumForge.RuntimeQuantumProperty, Basis, BasisValue, Predicate, the action components, the trackers, the trigger components
QRG.QuantumForge.CoreQuantumForge, the static class that wraps the native plugin, plus NativeQuantumProperty
QRG.QuantumForge.UtilityOptional helper components (ScaleProbability, RotateOnPhase, ButtonPressOnKey, Trigger)

Dimension and property limits

The shipped native plugin is compiled with MAX_DIMENSION=3, MAX_NUM_QUDITS=12 and dynamic fallback off. A Basis with more than 3 values fails when the QuantumProperty creates its native state, and at most 12 properties can interact. Ask the loaded plugin with QuantumForge.GetMaxDimension() and QuantumForge.GetMaxQudits().

QuantumProperty ​

MonoBehaviour that gives a GameObject quantum state. On Awake it creates the native property at the initial value you picked in the Inspector. If the Basis is unset, or the initial value is not in the Basis, or the dimension exceeds the plugin limit, Awake logs a warning and the component stays inert.

Inspector fields ​

FieldTypeDescription
basisBasisThe basis (ScriptableObject) defining possible states. Public, so it is also settable from code before Awake
InitialBasisValueThe starting value. Private and serialized, shown as a dropdown of the Basis values. Inspector only

Properties ​

PropertyTypeDescription
DimensionintNumber of basis values, read from basis

Predicate creation ​

csharp
Predicate is_value(BasisValue value)
Predicate is_value(string valueName)
Predicate is_value(int valueIndex)
Predicate is_not_value(BasisValue value)
Predicate is_not_value(string valueName)
Predicate is_not_value(int valueIndex)

Single-property gates ​

All accept an optional params Predicate[] for conditional application. Fractions are float at this layer, and the value is passed straight through to the double parameter on the Core layer.

csharp
void Cycle(params Predicate[] predicates)
void Cycle(float fraction, params Predicate[] predicates)

void Shift(params Predicate[] predicates)
void Shift(float fraction, params Predicate[] predicates)

void Clock(params Predicate[] predicates)
void Clock(float fraction, params Predicate[] predicates)

void Hadamard(params Predicate[] predicates)
void Hadamard(float fraction, params Predicate[] predicates)

void InverseHadamard(params Predicate[] predicates)   // no fractional form

void X(params Predicate[] predicates)       // alias for Shift
void X(float fraction, params Predicate[] predicates)

void Y(params Predicate[] predicates)       // qubit-only, errors when Dimension != 2
void Y(float fraction, params Predicate[] predicates)

void Z(params Predicate[] predicates)       // alias for Clock
void Z(float fraction, params Predicate[] predicates)

void Reset(int currentValue)                // reset to |0⟩ from a known value

Every one of these also exists as a static that takes the property first, for example QuantumProperty.Cycle(prop, 0.5f, predicates).

Reset is composed from cycles in C#, not a native operation. It is only correct when the property really is in the definite basis state you name, such as right after measuring it. Applied to a superposition it permutes the state instead of resetting it. currentValue must be in [0, Dimension).

Two-property gates (static) ​

csharp
static void Swap(QuantumProperty prop1, QuantumProperty prop2, params Predicate[] predicates)

static void ISwap(QuantumProperty prop1, QuantumProperty prop2)
static void ISwap(QuantumProperty prop1, QuantumProperty prop2, params Predicate[] predicates)
static void ISwap(QuantumProperty prop1, QuantumProperty prop2, float fraction)
static void ISwap(QuantumProperty prop1, QuantumProperty prop2, float fraction, params Predicate[] predicates)

static void NCycle(QuantumProperty prop1, QuantumProperty prop2)

The ISwap overloads without a fraction use fraction 1.0. NCycle at this layer takes no fraction. Use QuantumForge.NCycle in the Core layer if you need one.

Phase rotation (static) ​

csharp
static void PhaseRotate(float angle, params Predicate[] predicates)

The angle is in radians. There is no target property. The predicates decide which basis states pick up the phase.

Measurement (static) ​

csharp
static int[] Measure(params QuantumProperty[] properties)
static int[] MeasureProperties(params QuantumProperty[] properties)  // alias

static int Measure(params Predicate[] predicates)          // stochastic projection
static int MeasurePredicate(params Predicate[] predicates)  // alias

The property form returns one measured basis value index per property. Entangled partners update automatically. The predicate form returns 1 when the predicates were satisfied, 0 when they were not.

Analysis (static) ​

csharp
static BasisProbability[] Probabilities(params QuantumProperty[] properties)
static Complex[,] ReducedDensityMatrix(params QuantumProperty[] properties)
static float[] MutualInformation(params QuantumProperty[] properties)
static float[,] CorrelationMatrix(params QuantumProperty[] properties)

Complex is System.Numerics.Complex.

QuantumProperty.BasisProbability ​

csharp
[Serializable]
public struct BasisProbability
{
    public float Probability;
    public BasisValue[] BasisValues;   // one entry per property, in the order you passed them
}

Two types named BasisProbability

QuantumProperty.BasisProbability carries BasisValue[] BasisValues, the named values from your Basis assets. QuantumForge.BasisProbability in the Core layer carries int[] QuditValues, raw indices with no Basis attached. QuantumProperty.Probabilities converts the second into the first by looking each index up in the property's Basis. They are unrelated types, so do not expect one to assign to the other.


Predicate ​

A condition for a controlled quantum operation. It is a class in QRG.QuantumForge.Runtime, not a nested type on QuantumProperty and not a struct, so an unassigned element of a Predicate[] is null and the gate call rejects it.

csharp
[Serializable]
public class Predicate
{
    public QuantumProperty property;
    public BasisValue value;   // shown in the Inspector as a dropdown of the property's Basis
    public bool is_equal;      // true = "is this value", false = "is NOT this value"
}

Build one with the convenience methods on QuantumProperty instead of filling the fields by hand:

csharp
var pred = myProp.is_value("rock");      // true when property is "rock"
var pred = myProp.is_not_value(0);       // true when property is NOT index 0

Basis ​

ScriptableObject that defines the possible states of a quantum property.

Create via: Assets > Create > Quantum > Basis

FieldTypeDescription
valuesList<BasisValue>The list of basis values. At most 3 with the shipped plugin
DimensionintNumber of values (read-only)

BasisValue ​

A class, compared by Name:

csharp
[Serializable]
public class BasisValue : IEquatable<BasisValue>
{
    public string Name;
}

BasisValueDropdownAttribute ​

csharp
public class BasisValueDropdownAttribute : PropertyAttribute

Put [BasisValueDropdown] on a serialized BasisValue field to get a dropdown of the relevant Basis values instead of a raw name box. QuantumProperty.Initial and Predicate.value use it, and custom actions with their own BasisValue fields should too.


Actions ​

All actions implement IQuantumAction and are MonoBehaviours you attach to GameObjects and wire to Unity Events. The method is apply(), lowercase.

IQuantumAction ​

csharp
public interface IQuantumAction
{
    Predicate[] Predicates { get; set; }
    QuantumProperty[] TargetProperties { get; set; }
    void apply();
}

Action components ​

ComponentTargetsParametersDescription
Hadamard1+fractionCreates equal superposition
Cycle1+fractionCyclic permutation. Works at any dimension. At dimension 2 it is a bit flip, the same thing Shift does
Shift1+fractionGeneralized bit-flip
Clock1+fractionPhase rotation (Z-rotation)
InverseHadamard1+noneAdjoint of Hadamard
X1+fractionPauli X (alias for Shift)
Y1+fractionPauli Y, dimension 2 only
Z1+fractionPauli Z (alias for Clock)
ISwapexactly 2fractionEntanglement gate
Swapexactly 2noneState exchange
NCycleexactly 2noneEntangling operation
PhaseRotatevia predicatesRadians (0 to 2π)Phase rotation on predicate-matched states
MeasureProperties1+noneCollapse and fire Unity Events
MeasurePredicatesvia predicatesnoneProjective predicate measurement

Components that take exactly 2 targets log an error and do nothing when the count is wrong. PhaseRotate derives its target properties from its predicates, and assigning TargetProperties on it logs an error.

MeasureProperties events ​

csharp
UnityEvent OnMeasure                           // fires after measurement
QuantumPropertyEvent OnMeasureQuantumProperty  // fires once per measured property
int[] LastResult { get; }                      // most recent outcomes

MeasurePredicates events ​

csharp
UnityEvent OnMeasure                       // fires after measurement
MeasurePredicateEvent OnMeasurePredicates  // fires with bool, true when the predicates held
int LastResult { get; }                    // 0 or 1

MeasurePredicateEvent is UnityEvent<bool>.


Trackers ​

MonoBehaviours that monitor quantum state.

TrackerRead it withUpdate it withUse case
ProbabilityTrackerProbabilitiesGetBasisProbabilities()Show probability distribution
EntanglementTrackerLastUpdatedMutualInformationUpdateMutualInformation()Quantify entanglement (mutual information)
CorrelationTrackerreturn of the update callUpdateCorrelationMatrix()Measurement correlations (Pearson coefficient)
PhaseTrackerPhaseMatrixUpdatePhaseMatrix()Relative phases between states
ReducedDensityMatrixTrackerReducedDensityMatrixGetReducedDensityMatrix()Full density matrix

Reading ProbabilityTracker.Probabilities recomputes the distribution, it does not hand back a cached array.

Inspector configuration ​

FieldTypeDefaultDescription
Quantum PropertiesQuantumProperty[]emptyProperties to track
ContinuousbooltrueUpdate every frame
Radians (PhaseTracker only)booltrueReport phases in radians instead of degrees

These fields are private and serialized, so you set them in the Inspector, not from code. When Continuous is off, call the tracker's update method yourself.

ProbabilityTracker, CorrelationTracker, PhaseTracker and ReducedDensityMatrixTracker fall back to the QuantumProperty on their own GameObject when the list is left empty, and log an error when there is none. EntanglementTracker has no such fallback: mutual information needs at least two properties, so assign them yourself. With fewer than two it logs an error and UpdateMutualInformation() returns null.


Trigger components ​

Components that turn Unity collider events into quantum activity.

QuantumPropertyTrigger ​

csharp
public class QuantumPropertyTrigger : MonoBehaviour

Put it on a trigger collider. When a collider carrying a QuantumProperty enters, in 2D or 3D, it invokes its serialized On Trigger Enter event with that property. Wire it to anything that takes a QuantumProperty, such as a MeasureProperties action.

csharp
[Serializable]
public class QuantumPropertyEvent : UnityEvent<QuantumProperty> { }

TriggerActionOnQuantumProperty ​

csharp
public class TriggerActionOnQuantumProperty : MonoBehaviour

Put it on the same GameObject as an IQuantumAction. On trigger enter it looks up a QuantumProperty on the other collider, points the action's TargetProperties at it and calls apply(). No wiring needed, and nothing happens when the other object has no QuantumProperty.


Utility components ​

Optional components in QRG.QuantumForge.Utility, used by the samples. None of them are required to run quantum operations.

ComponentInspector fieldsWhat it does
ScaleProbabilityprobability tracker, basis value, per-axis togglesScales the transform on the chosen axes by the probability of that basis value, every frame
RotateOnPhasephase tracker, phase matrix index, per-axis togglesRotates the transform on the chosen axes by the phase at that matrix entry, relative to the rotation it started with
ButtonPressOnKeybutton, key, optional labelInvokes a UI Button's onClick when the key is pressed, and flashes the pressed color. Fills the label with the key name
Triggerenter eventA plain collider-to-UnityEvent bridge, with no QuantumProperty involved

Trigger and QuantumPropertyTrigger both show an exit event in the Inspector that nothing invokes. Only the enter event fires today.


Core layer ​

Direct C# access to the native plugin, without MonoBehaviours. Everything below is on the static class QRG.QuantumForge.Core.QuantumForge.

Fractions and angles are double here. The MonoBehaviour layer takes float and widens.

NativeQuantumProperty ​

csharp
var prop = new NativeQuantumProperty(dimension: 2);              // starts in |0⟩
var prop = new NativeQuantumProperty(dimension: 2, initial: 1);  // starts in |1⟩
int d = prop.Dimension;
prop.Dispose();  // releases the native handle

Both constructors start the property in |0⟩. The two-argument form then cycles and measures until the value reads back as initial, so it also ends up in a definite classical state, just not |0⟩. It throws when initial is outside [0, dimension).

Predicates are native handles, and they are IDisposable:

csharp
using (var pred = prop.is_value(1))
{
    QuantumForge.Cycle(other, 0.5, pred);
}

Failing to dispose leaks native memory on every gate call. is_not_value(int) is the negated form.

Gate methods ​

csharp
static void Cycle(NativeQuantumProperty prop, double fraction, params Predicate[] preds)
static void Cycle(NativeQuantumProperty prop, params Predicate[] preds)
static void Shift(NativeQuantumProperty prop, double fraction, params Predicate[] preds)
static void Shift(NativeQuantumProperty prop, params Predicate[] preds)
static void Clock(NativeQuantumProperty prop, double fraction, params Predicate[] preds)
static void Clock(NativeQuantumProperty prop, params Predicate[] preds)
static void X(NativeQuantumProperty prop, double fraction, params Predicate[] preds)
static void X(NativeQuantumProperty prop, params Predicate[] preds)
static void Y(NativeQuantumProperty prop, double fraction, params Predicate[] preds)
static void Y(NativeQuantumProperty prop, params Predicate[] preds)
static void Z(NativeQuantumProperty prop, double fraction, params Predicate[] preds)
static void Z(NativeQuantumProperty prop, params Predicate[] preds)
static void Hadamard(NativeQuantumProperty prop, params Predicate[] preds)
static void Hadamard(NativeQuantumProperty prop, double fraction, params Predicate[] preds)
static void InverseHadamard(NativeQuantumProperty prop, params Predicate[] preds)
static void Reset(NativeQuantumProperty prop, int currentValue)

static void PhaseRotate(double angle, params Predicate[] preds)
static void Swap(NativeQuantumProperty p1, NativeQuantumProperty p2, params Predicate[] preds)
static void ISwap(NativeQuantumProperty p1, NativeQuantumProperty p2, double fraction, params Predicate[] preds)
static void ISwap(NativeQuantumProperty p1, NativeQuantumProperty p2, params Predicate[] preds)

Two entangling helpers build a controlled sequence in C# rather than in one native call, and both take an optional fraction:

csharp
static void NCycle(NativeQuantumProperty prop1, NativeQuantumProperty prop2, double fraction = 1.0)
static void NShift(NativeQuantumProperty prop1, NativeQuantumProperty prop2, double fraction = 1.0)

NCycle applies cycles to prop2 controlled on the value of prop1, NShift applies shifts. Each one issues several predicated gate calls, so batching pays off when you use many of them.

Measurement ​

csharp
static int[] Measure(params NativeQuantumProperty[] props)
static int Measure(Predicate[] preds)
static int[] ForcedMeasure(int[] forcedValues, params NativeQuantumProperty[] props)

ForcedMeasure collapses the state onto the outcomes you name instead of sampling, and returns the resulting values. forcedValues must be the same length as props, otherwise it throws ArgumentException. Use it for deterministic replays and tests, not for normal gameplay.

Analysis ​

csharp
static BasisProbability[] Probabilities(params NativeQuantumProperty[] props)
static Complex[,] ReducedDensityMatrix(params NativeQuantumProperty[] props)
static float[] MutualInformation(params NativeQuantumProperty[] props)
static float[,] CorrelationMatrix(params NativeQuantumProperty[] props)
csharp
public readonly struct BasisProbability
{
    public readonly float Probability;
    public readonly int[] QuditValues;  // raw indices, one per property
}

This is the Core-layer BasisProbability, distinct from QuantumProperty.BasisProbability.

Batch operations ​

Each gate call crosses the managed to native boundary once. When you apply many gates back to back, ExecuteBatch sends the whole sequence in a single native call and that overhead mostly disappears.

csharp
static QForgeBatchResult ExecuteBatch(
    params (QForgeOpCode op,
            NativeQuantumProperty target,
            NativeQuantumProperty target2,
            double fraction,
            double angle,
            Predicate[] predicates)[] ops)

static QForgeBatchResult ExecuteBatch(params (QForgeOpCode op, NativeQuantumProperty target)[] ops)
csharp
using static QRG.QuantumForge.Core.QuantumForge;

using (var pred = prop1.is_value(1))
{
    var result = ExecuteBatch(
        (QForgeOpCode.Hadamard, prop1, null,  double.NaN, 0.0,             null),
        (QForgeOpCode.Cycle,    prop2, null,  0.5,        0.0, new[] { pred }),
        (QForgeOpCode.ISwap,    prop1, prop2, 1.0,        0.0,             null));

    if (result.errorCode != QForgeResult.QFORGE_SUCCESS)
    {
        Debug.LogError($"batch stopped after {(int)result.opsExecuted} ops: {result.errorMessage}");
    }
}

The second overload is the shorthand for gates with no predicates and no fraction. It passes double.NaN for every fraction.

Tuple fieldMeaning
opWhich gate, from QForgeOpCode
targetPrimary property. Pass null for PhaseRotate
target2Second property, for Swap and ISwap. null otherwise
fractionGate fraction. double.NaN selects the non-fractional variant
angleRadians, PhaseRotate only
predicatesPredicate handles, or null
csharp
public enum QForgeOpCode
{
    Cycle, Shift, Clock, X, Z, Y, Hadamard, InverseHadamard, Swap, ISwap, PhaseRotate
}

public struct QForgeBatchResult
{
    public UIntPtr opsExecuted;    // how many ops ran before stopping
    public QForgeResult errorCode; // QFORGE_SUCCESS when the whole batch ran
    public string errorMessage;
}

Fractional versus non-fractional

double.NaN calls the discrete permutation, for example Cycle advancing the basis state by one. 1.0 calls the continuous rotation at fraction 1.0, which is a different operation. This matches the individual gate calls, where Cycle(prop) and Cycle(prop, 1.0) differ.

Operations run in order and stop at the first error. Gates that already ran are not rolled back, so read opsExecuted to find out how far the batch got. Measurement is not a batch op. Call Measure after the batch finishes.

QForgeBatchOp is the sequential struct that ExecuteBatch marshals into. It holds raw handles and pointers, and you should not need to build one by hand.

ExecuteBatch is not callable with the 1.4.0 plugins

The native plugins committed for 1.4.0 predate qforge_execute_batch, so calling ExecuteBatch throws EntryPointNotFoundException on every platform. It works once the plugins are rebuilt. Everything else on this page resolves against the shipped macOS plugin.

Library lifecycle and diagnostics ​

csharp
static void Initialize()                  // throws when the native library fails to initialize
static void Cleanup()                     // logs an error on failure, does not throw
static string GetVersion()                // native library version string
static (int major, int minor, int patch) GetVersionInfo()

static int GetMaxDimension()              // compiled-in dimension cap, 3 in shipped builds
static int GetMaxQudits()                 // compiled-in property cap, 12 in shipped builds
static bool IsValidDimension(int dimension)

static string GetErrorString(QForgeResult result)

public delegate void ErrorCallback(QForgeErrorInfo errorInfo, IntPtr userData);
static void SetErrorCallback(ErrorCallback callback, IntPtr userData)
static void SetErrorCallback(ErrorCallback callback)

Initialize is a no-op in the current C API. It returns success as soon as the plugin loads, which makes it a cheap startup probe: call it once and you find out the native library resolves before the first gate does. Cleanup clears the registered error callback. Neither is required for ordinary use.

Keep a managed reference to any delegate you hand to SetErrorCallback for as long as the native side may call it. If the garbage collector takes it, the native callback crashes.

Error codes ​

csharp
public enum QForgeResult
{
    QFORGE_SUCCESS = 0,

    // Parameter errors
    QFORGE_ERROR_NULL_POINTER = 1,
    QFORGE_ERROR_INVALID_ARGUMENT = 2,
    QFORGE_ERROR_BUFFER_TOO_SMALL = 3,
    QFORGE_ERROR_INVALID_DIMENSION = 4,
    QFORGE_ERROR_INVALID_QUDIT_NUMBER = 5,

    // Operation errors
    QFORGE_ERROR_TARGET_CONTROL_OVERLAP = 100,
    QFORGE_ERROR_INCOMPATIBLE_DIMENSIONS = 101,
    QFORGE_ERROR_STATE_SIZE_EXCEEDED = 102,

    // Memory errors
    QFORGE_ERROR_OUT_OF_MEMORY = 200,

    // Internal errors
    QFORGE_ERROR_INTERNAL = 900
}

Most Core methods do not hand you a QForgeResult. They throw InvalidOperationException with the code and the native message in the text. ExecuteBatch is the exception: it reports the code in QForgeBatchResult.errorCode.

Failures also arrive as a QForgeErrorInfo, the struct passed to an ErrorCallback:

csharp
public struct QForgeErrorInfo
{
    public QForgeResult code;
    public string message;
    public IntPtr function;  // const char*, marshal with Marshal.PtrToStringAnsi
    public int line;
}

Powered by Quantum Forge