Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
29 changes: 28 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,30 @@ All notable changes to this project are documented here.
The format follows [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).

## [1.0.0] — unreleased
## [1.0.1]

### Added

- **.NET Standard 2.0** target, reaching .NET Framework 4.6.1+, Mono, Godot and Unity
2018–2020. The package now ships `netstandard2.0`, `netstandard2.1`, `net8.0` and `net10.0`.
- A dedicated package readme. nuget.org cannot resolve relative links, so the repository
readme rendered there with a broken logo.

### Changed

- The package icon is now a square 512×512 image. nuget.org renders icons in a square slot,
which letterboxed the previous 1024×363 banner.

### Fixed

- State and matrix strings used the current culture's decimal separator and printed full
double precision, so `ToString()` returned `0,7071067811865475|00>` on any machine with a
comma separator. Output is now culture-invariant and rounded to four decimals.
- Amplitudes that are only numerical noise are no longer printed: `H(0); H(0);` reads `|0>`
rather than including a `1E-17` term.
- A negative imaginary part reads `0.5 - 0.5i` rather than `0.5 + -0.5i`.

## [1.0.0]

First packaged release. Qubit.NET is now installable with `dotnet add package Qubit.NET`
and usable from Unity.
Expand Down Expand Up @@ -75,4 +98,8 @@ and usable from Unity.
flags with the original, so gates applied to a copy also affected the source circuit.
- Toffoli gates rendered their second control as a target marker in circuit diagrams.
- `GetStringResult` threw an exception when every measurement count was zero.
- State and matrix strings used the current culture's decimal separator and printed full
double precision, so `ToString()` returned `0,7071067811865475|00>` on a machine with a
comma separator. Output is now culture-invariant and rounded to four decimals, and
amplitudes that are numerical noise are no longer printed at all.
- Renamed the internal `ApplayGate` to `ApplyGate`.
7 changes: 4 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
<img src="./img/qubitnet.png" alt="Qubit.NET logo" style="height: 250px"/>
<img src="https://raw.githubusercontent.com/InfoTCube/Qubit.NET/main/img/qubitnet.png" alt="Qubit.NET logo" width="420"/>

# Qubit.NET

Expand All @@ -21,11 +21,12 @@ The state vector holds 2ⁿ complex amplitudes, so memory is the limit: **20 qub
dotnet add package Qubit.NET
```

Zero dependencies. Targets **.NET Standard 2.1**, **.NET 8** and **.NET 10**.
Zero dependencies. Targets **.NET Standard 2.0 / 2.1**, **.NET 8** and **.NET 10**, so it’s
also usable from .NET Framework 4.6.1+, Mono and Godot.

### 🎮 Unity

Qubit.NET ships a `netstandard2.1` build, so it works in Unity 2021.2+. Either install it
Qubit.NET ships `netstandard2.0` and `netstandard2.1` builds, so it works in Unity 2018 and later. Either install it
through [NuGetForUnity](https://github.com/GlitchEnzo/NuGetForUnity), or drop
`lib/netstandard2.1/Qubit.NET.dll` from the package into `Assets/Plugins/`.

Expand Down
124 changes: 124 additions & 0 deletions README.nuget.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,124 @@
<!--
This is the readme shown on nuget.org, not the one on GitHub.
Keep it short and keep every URL absolute — nuget.org cannot resolve relative
links or images. The full documentation lives in README.md.
-->

# Qubit.NET

**A quantum computing simulator for .NET — and the only one that runs in Unity.**

Build quantum circuits, apply gates, measure results, draw circuit diagrams, and export to
OpenQASM so your circuit runs on real hardware. No dependencies.

```bash
dotnet add package Qubit.NET
```

Targets **.NET Standard 2.0 / 2.1**, **.NET 8** and **.NET 10** — so it also runs on
.NET Framework 4.6.1+, Mono and Godot.

---

## Quick start

```csharp
using Qubit.NET;

var qc = new QuantumCircuit(2);

qc.H(0); // superposition
qc.CNOT(0, 1); // entangle

Console.WriteLine(qc); // 0.7071|00> + 0.7071|11>
Console.WriteLine(qc.Measure()); // "00" or "11", never "01" or "10"
```

Run it many times and count outcomes:

```csharp
using Qubit.NET.Simulation;

qc.Measure();
var result = Simulator.Run(qc, 1000)[0];

Console.WriteLine(result); // {'00': 512, '11': 488}
Console.WriteLine(result.Probability("11")); // 0.488
```

## Draw circuits

```csharp
qc.Draw(); // colored, to the console
qc.ToDiagram(); // the same thing as a string
```

```
q2 (0): ───────────[+]──[X]──[M]─
| | |
q1 (0): ──────[+]───@────|───[M]─
| | | |
q0 (0): ─[H]───@────@───[X]──[M]─
```

## Unity

Qubit.NET ships a `netstandard2.1` build, so it works in Unity 2021.2+. Install through
[NuGetForUnity](https://github.com/GlitchEnzo/NuGetForUnity), or drop
`lib/netstandard2.1/Qubit.NET.dll` into `Assets/Plugins/`.

Unity has no `Console`, so use the string-returning APIs:

```csharp
Debug.Log(qc.ToDiagram());

var (x, y, z) = qc.BlochVector(0); // drive a Bloch sphere gizmo
arrow.transform.localPosition = new Vector3((float)x, (float)y, (float)z);
```

A qubit entangled with others sits *inside* the Bloch sphere — maximally entangled means
the origin, which makes entanglement something you can actually see.

## Built-in algorithms

```csharp
using Qubit.NET.Circuits;

var grover = Algorithms.Grover(2, c => c.CZ(0, 1)); // marks |11>
Console.WriteLine(grover.ToHistogram()); // 11 | ######### 1.000

var bv = Algorithms.BernsteinVazirani([true, false, true]);
Console.WriteLine(bv.Measure(2, 1, 0)); // 101
```

Also: Deutsch–Jozsa, teleportation, superdense coding, QFT, and the Bell and GHZ states.

## Run on real hardware

```csharp
using Qubit.NET.Export;

File.WriteAllText("bell.qasm", qc.ToQasm());
```

```python
# Qiskit
qc = QuantumCircuit.from_qasm_file("bell.qasm")
```

## What else is in the box

- **27 gates** — Pauli, Hadamard, phase, √X/√Y/√Z, rotations, U3, controlled forms, Toffoli, Fredkin, and your own unitary matrices
- **Mid-circuit measurement and classical feedforward** — `MeasureInto` and `When`, enough for teleportation and error correction
- **Partial measurement** of any subset of qubits
- **Pluggable randomness** via `IRandomSource` — seed it for reproducible tests, or plug in a quantum RNG
- **In-place simulation** — a 20-qubit GHZ chain runs in 34 ms and allocates one state vector, not one per gate

Up to 26 qubits; memory is the limit, since the state vector holds 2ⁿ complex amplitudes
(20 qubits ≈ 16 MB, 24 ≈ 256 MB).

---

**[Full documentation, examples and source on GitHub →](https://github.com/InfoTCube/Qubit.NET)**

MIT licensed. Issues and pull requests welcome.
Binary file added img/qubitnet-icon-512.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
16 changes: 16 additions & 0 deletions img/qubitnet-icon.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
15 changes: 9 additions & 6 deletions src/Qubit.NET/Qubit.NET.csproj
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup>
<!-- netstandard2.1 is what makes the library usable from Unity. -->
<TargetFrameworks>netstandard2.1;net8.0;net10.0</TargetFrameworks>
<!-- netstandard2.0 reaches .NET Framework 4.6.1+, Mono and Unity 2018-2020;
netstandard2.1 covers Unity 2021.2+. -->
<TargetFrameworks>netstandard2.0;netstandard2.1;net8.0;net10.0</TargetFrameworks>
<LangVersion>latest</LangVersion>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
Expand All @@ -11,15 +12,15 @@

<PropertyGroup>
<PackageId>Qubit.NET</PackageId>
<Version>1.0.0</Version>
<Version>1.0.1</Version>
<Authors>Tymoteusz Marzec</Authors>
<Description>A lightweight quantum computing simulation library for .NET. Build quantum circuits, apply gates, measure results, and draw ASCII circuit diagrams — no dependencies. Works in .NET and Unity.</Description>
<PackageTags>quantum;quantum-computing;qubit;simulator;quantum-circuit;unity;csharp;dotnet</PackageTags>
<PackageLicenseExpression>MIT</PackageLicenseExpression>
<PackageProjectUrl>https://github.com/InfoTCube/Qubit.NET</PackageProjectUrl>
<RepositoryUrl>https://github.com/InfoTCube/Qubit.NET</RepositoryUrl>
<RepositoryType>git</RepositoryType>
<PackageIcon>qubitnet.png</PackageIcon>
<PackageIcon>qubitnet-icon-512.png</PackageIcon>
<PackageReadmeFile>README.md</PackageReadmeFile>
<PackageReleaseNotes>See CHANGELOG.md</PackageReleaseNotes>
</PropertyGroup>
Expand All @@ -34,8 +35,10 @@
</PropertyGroup>

<ItemGroup>
<None Include="../../img/qubitnet.png" Pack="true" PackagePath="/" Visible="false" />
<None Include="../../README.md" Pack="true" PackagePath="/" Visible="false" />
<None Include="../../img/qubitnet-icon-512.png" Pack="true" PackagePath="/" Visible="false" />
<!-- A leaner readme than the repo's: nuget.org cannot resolve relative links, and the
package page is a funnel to GitHub rather than full documentation. -->
<None Include="../../README.nuget.md" Pack="true" PackagePath="/README.md" Visible="false" />
</ItemGroup>

<ItemGroup>
Expand Down
33 changes: 27 additions & 6 deletions src/Qubit.NET/Utilities/Helpers.cs
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
using System.Globalization;
using Qubit.NET.Gates;

namespace Qubit.NET.Utilities;
Expand All @@ -16,18 +17,38 @@ internal static class Helpers
/// <returns>A string representation of the complex number, in the form: "real + imaginary*i" or "real" or "imaginary*i".</returns>
internal static string FormatComplex(double real, double imaginary)
{
// Applying gates leaves amplitudes like 1e-17 where the exact answer is zero.
// Printing those makes state strings unreadable, so round them away.
if (System.Math.Abs(real) < Epsilon) real = 0;
if (System.Math.Abs(imaginary) < Epsilon) imaginary = 0;

if (imaginary == 0 && real == 0)
return String.Empty;

if (imaginary == 0)
return real.ToString();
return Number(real);

if (real == 0)
return $"{imaginary}i";

return $"{real} + {imaginary}i";
return $"{Number(imaginary)}i";

// Keep the sign out of the term itself, so this reads "a - bi" not "a + -bi".
string sign = imaginary < 0 ? "-" : "+";

return $"{Number(real)} {sign} {Number(System.Math.Abs(imaginary))}i";
}

/// <summary>
/// Amplitudes below this are treated as zero when formatting.
/// </summary>
private const double Epsilon = 1e-10;

/// <summary>
/// Formats a component to four decimal places using the invariant culture, so output
/// does not change with the machine's locale.
/// </summary>
private static string Number(double value) =>
value.ToString("0.####", CultureInfo.InvariantCulture);

/// <summary>
/// Returns a string array representation of a quantum gate, where each element corresponds
/// to the symbol that should be displayed on a specific qubit line in a circuit diagram.
Expand Down
87 changes: 87 additions & 0 deletions tests/Qubit.NET.Tests/FormattingTests.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
using System.Globalization;
using System.Numerics;
using Qubit.NET;
using Qubit.NET.Circuits;
using Qubit.NET.Gates;

namespace QubitNet.Tests;

/// <summary>
/// State and matrix formatting. These strings are the first thing a user sees, and they
/// must not change with the machine's locale.
/// </summary>
public class FormattingTests
{
[Fact]
public void State_string_is_readable()
{
Assert.Equal("0.7071|00> + 0.7071|11>", BellStates.PhiPlus().ToString());
}

[Fact]
public void Basis_states_drop_the_redundant_coefficient()
{
QuantumCircuit qc = new(2);
qc.X(0);

Assert.Equal("|01>", qc.ToString());
}

[Fact]
public void Negative_amplitudes_keep_their_sign()
{
Assert.Equal("0.7071|00> + -0.7071|11>", BellStates.PhiMinus().ToString());
}

[Fact]
public void Numerical_noise_is_not_printed()
{
// H twice is the identity, but leaves ~1e-17 in the other amplitude.
QuantumCircuit qc = new(1);
qc.H(0);
qc.H(0);

Assert.Equal("|0>", qc.ToString());
}

[Fact]
public void Imaginary_amplitudes_are_formatted_as_complex_numbers()
{
QuantumCircuit qc = new(1);
qc.H(0);
qc.S(0);

// (|0> + i|1>)/sqrt(2)
Assert.Equal("0.7071|0> + 0.7071i|1>", qc.ToString());
}

[Fact]
public void A_negative_imaginary_part_reads_as_a_subtraction()
{
// 0.5 - 0.5i, via the SX gate's off-diagonal entry.
string formatted = QuantumGates.Format(QuantumGates.SX);

Assert.Contains("0.5 - 0.5i", formatted);
Assert.DoesNotContain("+ -", formatted);
}

[Theory]
[InlineData("pl-PL")] // comma decimal separator
[InlineData("de-DE")]
[InlineData("en-US")]
public void Output_does_not_depend_on_the_current_culture(string culture)
{
CultureInfo original = CultureInfo.CurrentCulture;
CultureInfo.CurrentCulture = new CultureInfo(culture);

try
{
Assert.Equal("0.7071|00> + 0.7071|11>", BellStates.PhiPlus().ToString());
Assert.DoesNotContain(",", QuantumGates.Format(QuantumGates.H));
}
finally
{
CultureInfo.CurrentCulture = original;
}
}
}
Loading