Library / SDK
qulacs/qulacs avatar
qulacs/qulacs

Qulacs: a fast simulator for noisy and parametric quantum circuits

Variational Quantum Circuit Simulator for Quantum Computation Research

529 stars152 forksC++MIT

At a glance

What is it?
Qulacs is a quantum circuit simulator written in C++ with a Python interface, built by a consortium of Japanese research institutions, and its distinguishing feature is that it is aimed at the circuits that exist today rather than the ones that are hoped for. Noisy circuits, parametric circuits, and the compression tricks that make a large circuit simulable at all are the subject of the whole library, and the performance claims are published with the caveats that make them checkable.
Who is it for?
Qulacs fits a researcher who needs to simulate variational or noisy circuits at a scale where a general simulator would be too slow, and who is comfortable with a compiled library rather than a hosted service. It does not fit somebody who wants a teaching tool or a general quantum programming environment.
Can I use it commercially?
Yes. MIT is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
Is it still maintained?
Yes. The repository last received commits 3 days ago.
What is it written in?
Mainly C++, according to GitHub's language statistics.

Answers come from the project's GitHub data, last synced on October 20, 2026, and from our analysis. They are not legal advice.

Editorial analysis

What it is, and who builds it

Qulacs is a Python and C++ library for fast simulation of large, noisy, or parametric quantum circuits. Those three adjectives are the specification, and each of them narrows the scope in a way that explains the rest of the design.

Large means the circuits are big enough that the simulation cost dominates everything else, which is the normal situation for a research code that has to sweep over many parameter settings.

Noisy means the circuits include noise, which is the situation on the hardware that exists now, where a device has errors and a simulation that ignores them is a simulation of a different machine. The feature list has a noisy gate for the simulation of near-term devices, and the repository's own topic tags say the same thing.

And parametric means the circuits have parameters that get optimised, which is the variational pattern, and the feature list has parametric gates for variational methods.

So this is a simulator for the kind of circuit a laboratory actually works on rather than for the ideal circuits that appear in textbooks.

The provenance is unusual and worth knowing. The library is developed at a company that works on quantum software, at a university in Osaka, and at two large Japanese technology firms. That is a research consortium arrangement rather than a single lab, and it explains the shape of the project: the performance work is the kind of thing an industrial team needs, the feature coverage is the kind of thing a research group needs, and the packaging is the kind of thing a company does to get adoption.

The licence is the permissive one, which for a research tool that several institutions contributed to is a deliberate choice, and the repository carries a separate readme for the parallel version, which tells you that the message passing build is a first-class part of the library rather than an afterthought.

The release history is slow and deliberate: three releases across a year and a half, with the newest in the middle of 2026. A library whose performance depends on hand-written kernels is not a project that ships weekly.

The install, and the processor that breaks it

The quick install is one command.

bash
pip install qulacs

And then there is the caveat, which is the most interesting thing in the installation section. If your processor is older than a particular Intel architecture, the binary installed by that command does not work at all. In that case you have to install from the source repository instead.

And then the sentence that is easy to skim and matters more: even if your processor is newer than that architecture, installing from source shows better performance, at the cost of a longer installation.

That is a strange and honest admission for a project to make about its own published package. A prebuilt binary is built for one instruction set baseline, and the baseline here is the one the maintainers' own infrastructure supports, which is a modern desktop processor. Compiling locally lets the compiler target your machine exactly, and the vectorised kernels that dominate the runtime benefit from that.

So the practical guidance is: the binary is for trying the library quickly, and the source build is for using it seriously. The readme says the source install takes a compiler and time, and it points at the source installation section for the detail.

The graphics processor path is a separate package, and the readme is careful to say that it includes the processor simulator as well, so you do not have to install both. That is a small but real piece of packaging hygiene: a lot of projects ship a variant that quietly excludes the default path, and users end up with two installations and a version conflict.

And the source build is configurable in three ways, which is the subject of a later section, because the flags that build it are the same flags that shape how it runs.

The features, and the compression trick

The feature list is six items and the fourth one is the interesting one.

There is fast simulation with a parallelised C and C++ backend, which is the base capability. There is a noisy gate for the simulation of near-term devices. There are parametric gates for variational methods. There is GPU support. There are many utility functions for research, which is a vague phrase that in practice means the small numerical helpers that every simulation ends up needing and nobody wants to write.

And then there is circuit compression for fast simulation.

Compression is the feature that makes large circuits tractable, and it is worth thinking about what it means, because it is not the same as the compiler optimisations you would find in a classical toolchain. In a quantum circuit you cannot just rewrite a gate away: a gate that cancels another gate is only removable if the two are adjacent in the order the circuit is executed, and the order matters because the gates generally do not commute. So the compression here is a property of the circuit's structure rather than a rewrite of its text, and the readme's benchmarks treat it as a distinct cost, which is the honest way to do it.

That matters for how the performance claims should be read, and the readme is unusually clear about it. The plots whose names end with the optimisation marker perform the circuit optimisation for fast simulation, and the time for the optimisation is included in the execution time. The heavy variant includes more optimisation work still.

So the comparison is deliberately unflattering to the library on that axis: the time to compress is charged against the total. Which means that the plain execution time of a compressed circuit is even better than the plot suggests, and that the optimisation is cheap enough to be worth doing repeatedly.

And there is a note about the integration of a related project. A separate high-performance variant of the library, developed at another organisation, was integrated into this one, and the readme points at the section with the details. That is the usual shape of this field: several teams build fast simulators for the same problem and eventually the fastest one absorbs the others, and the fact that it is documented rather than silent is helpful to anybody who followed the older project.

The benchmarks, and how to read them

The performance section compares the time to simulate random quantum circuits against several other simulators, and it says when the comparison was made, which is a detail that a lot of benchmark sections leave out.

The comparison is November 2020, and the readme points at a separate benchmark repository and at two sections of the project's paper for the detail. The paper is on an open preprint server, and the readme cites specific sections of it, which is a sign that the benchmark is described precisely enough to be checked against a document rather than taken on trust.

There are three sets of results: a single-threaded comparison, a multi-threaded one, and one for the graphics processor. Three separate charts because three separate questions, and the honest way to present a library that can use all three is to show what each one buys.

And the images are hosted on the organisation's own storage rather than being committed to the repository, which is the right choice: benchmark charts change as the library changes, and a chart in a repository goes stale the first time somebody improves the code.

The methodology point is the one I would emphasise. The readme tells you that the plots with the optimisation marker include the optimisation time in the execution time. That is a choice of accounting, and it is the conservative one for the library, because circuit compression is one of its features. A benchmark that credited the compression time separately would show a much better number for the library and a much worse one for everybody else, and the readme chooses not to do that.

It also tells you the obvious caveat about the age of the comparison. These numbers are from 2020, they are against simulators from 2020, and the field moves quickly. So the right way to use the section is as evidence about the shape of the library rather than as a current ranking: a simulator that can simulate a large circuit in a second is useful for parameter sweeps, and a simulator that takes an hour is not.

Which is why the repository carries its own benchmark directory as well as pointing at an external one. The external repository is the comparison against other people's code; the internal one is the regression check that the next commit did not make things slower.

Building from source, and the switches that shape the build

The source installation section is the longest part of the readme, and it is a good example of a build being documented as a set of decisions rather than as a list of commands.

The requirements are listed precisely, including the versions that were actually tested in continuous integration for each platform, which is a level of detail that saves a lot of guessing. A C++ compiler, with a minimum version and the list of environments it was checked in, and the alternative compiler with its version. A header-only library for linear algebra, with a minimum version and the three tested versions, one per platform. A minimum Python version. A minimum build tool version. Git. And two optional items, a graphics processor toolkit and a vector instruction set.

Then the behaviour of those optional items, which is the part that reads as care. If the system supports the vector instruction set, the vectorised optimisation is enabled automatically. If it does not, it is disabled automatically. Nobody has to pass a flag to get the right build; the build figures it out.

And the same happens with the parallelisation. There are three build configurations, and the readme gives the command for each: the default, which is multi-threaded without the graphics processor; the one with the graphics processor enabled, which needs the toolkit installed; and the single-threaded one, for when you want a deterministic thread count.

The thread count has a subtle detail worth its own paragraph. The number of threads used by the default install can be controlled by the standard OpenMP environment variable or by one specific to this library. The difference is that the standard one affects the parallelisation of other libraries as well, while the specific one controls only this library's. That distinction matters in a pipeline where several stages share a machine, and it is the kind of detail that usually only appears in the source code.

The optional dependency groups follow the same pattern: one for development tools and one for generating the documentation. And the whole section ends with the uninstall command, which is a small thing that is a nice courtesy for a library that people install and uninstall often.

The repository, and using it as a C++ library

The top level of the repository shows a project with a compiled core and a language binding on top of it, and the directory names make the split obvious.

There is a source directory for the C++ implementation, a Python source directory, and a separate directory for the Python side, plus a setup file and a project configuration, which is the older arrangement for a package with a compiled extension. There is a build script directory, a separate directory of build modules, a test directory, a documentation directory, a benchmark directory, and a configuration file for the documentation site.

And there are three development environment files: a container configuration for development, editor settings, and a formatting configuration. Plus a coverage configuration, a contribution guide, and a separate readme for the parallel version.

The C++ usage section is where the project tells you how to use it without Python at all, and it is written in a way that shows the library is a set of static libraries with stable names rather than a single blob. The build is two scripts, one for the standard build and one for the graphics processor build, and the libraries land in a build directory where you can also produce shared libraries with a make target.

Then the link line, which is the bit most users of a compiled library will actually copy, and it shows four separate static libraries being linked, and the OpenMP flag being passed to the compiler, which is how the parallelisation is enabled at the link stage. The graphics processor version is the same idea with two differences: a graphics processor state header instead of the ordinary one, and a graphics compiler instead of the ordinary one, with the graphics libraries and the compute library added to the link.

And that last difference is the whole point of the library's structure. The graphics path is not a separate program that reimplements the simulation; it is the same simulation with a different state implementation, chosen by which header you include. Which is a much better design than maintaining two simulators, and it means the two paths cannot drift apart in their results.

The packaging reflects the same division. The version is dynamic, taken from the version control tags by the setuptools plugin, so a release does not require editing a version field, and the build backend requires a recent version of the packaging tools. The build badge is for the wheel, which tells you the Python distribution is a compiled wheel rather than a source distribution, and that is the artefact the Haswell caveat is about.

Editorial conclusion

Qulacs fits a researcher who needs to simulate variational or noisy circuits at a scale where a general simulator would be too slow, and who is comfortable with a compiled library rather than a hosted service. It does not fit somebody who wants a teaching tool or a general quantum programming environment. Verify first whether your processor predates the architecture the published binary is built for, because the prebuilt package will not run on it, whether the noise model and the parametric gate support cover the circuits you care about, since the library is specialised rather than general, and that the optimisation in the benchmark is not hiding a slower execution, because the published plots count the compression time as part of the time.

Frequently asked questions

What is the Qulacs library?

It is a Python and C++ library for fast simulation of large, noisy or parametric quantum circuits, developed at a quantum software company, a university in Osaka and two Japanese technology firms. The feature list covers a parallelised native backend, noisy gates for near-term devices, parametric gates for variational methods, circuit compression, graphics processor support, and utility functions for research.

Why does the Qulacs pip package not work on older processors?

Because the published binary is built for a particular modern processor architecture, and the vectorised kernels it contains need instructions an older machine does not have. The readme says the binary does not work at all on such a processor, and that installing from source is required there, and also gives better performance even on newer machines at the cost of a longer install.

What does circuit compression do in Qulacs?

It reduces the work a large circuit requires, exploiting the structure of the circuit rather than rewriting it the way a classical compiler would, because gate order matters and gates generally do not commute. The benchmarks deliberately include the compression time in the reported execution time, which makes the published comparison conservative for the library.

What build options does the Qulacs source build have?

Three configurations: the default, which is multi-threaded and without graphics processor support; one that enables the graphics processor backend and requires the toolkit installed; and a single-threaded one. Vectorised optimisation is enabled or disabled automatically depending on the processor's support for the instruction set, and the thread count can be set with the standard OpenMP variable or with a library-specific one that affects only this library.

Can Qulacs be used as a C++ library without Python?

Yes. There are build scripts for the standard and graphics processor builds, static libraries are produced and can also be built as shared ones, and the readme gives the exact compiler command, including the four static libraries to link and the flag that enables parallelisation. The graphics path uses a different state header and a different compiler, with the graphics libraries added to the link.

Official sources

  1. Issues
  2. License: MIT
  3. qulacs/qulacs on GitHub
  4. README
  5. Releases
Add this badge to your README

If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.

Add this badge to your README

markdown
[![Hysen Labs](https://hysenlabs.com/badge/qulacs-qulacs.svg)](https://hysenlabs.com/projects/qulacs-qulacs)

Was this useful?