Git Product home page Git Product logo

staq's Introduction

staq

Version 3.5 - 8 March 2024

GitHub actions


About

staq is a modern C++ library for the synthesis, transformation, optimization and compilation of quantum circuits. staq is written in standard C++17 and has very low external dependencies. It is usable either through the provided binary tools, or as a header-only library that can be included to provide direct support for parsing & manipulating circuits written in the OpenQASM 2.0 circuit description language.

Inspired by Clang, staq is designed to manipulate OpenQASM 2.0 syntax trees directly, rather than through an intermediate representation which makes retrieving the original source code impossible. In particular, OpenQASM 2.0 circuits can be inspected and transformed (in most cases) without losing the original source structure. This makes staq ideally suited for source-to-source transformations, where only specific changes are desired. Likewise, this allows translations to other common circuit description languages and libraries to closely follow the OpenQASM 2.0 source.

Check out the Wiki for more information about the library and included tools.

Copyright (c) 2013 - 2024 softwareQ Inc. All rights reserved.


License

staq is distributed under the MIT license. Please see the LICENSE file for more details.


Installation instructions

Please see the installation guide INSTALL.md and the comprehensive Wiki for further documentation and detailed examples.


Python 3 wrapper

pystaq is a Python 3 wrapper for staq. pystaq can be installed using pip

pip install git+https://github.com/softwareQinc/staq

For more details, please see pystaq/README.md.


Acknowledgements

Thanks to the excellent EPFL logic synthesis libraries which are used to perform logic synthesis in staq, and in particular Bruno Schmitt's tweedledum library, from which the OpenQASM 2.0 parser was adapted.

staq's People

Contributors

525125 avatar aerylia avatar jimlambert avatar meamy avatar smartiel avatar vsoftco avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

staq's Issues

Mapping: steiner vs swap - 100 times slower

Thanks for the previous fix. Now, I start to run bigger circuits.

Here is a test case to show steiner is 100 times slower than swap.

shor.zip

Though I don't characterize that as a bug yet, it looks unusual.

Would you please comment on the issue?

I run these:

time ./staq -S   -O1   -d   tokyo -m  -M  steiner   -l   bestfit   -f   qasm   -o shor_staq.qasm shor.qasm

real	2m54.130s
user	2m52.265s
sys	0m0.375s
time ./staq   -S   -O1   -d   tokyo -m  -M  swap   -l   bestfit   -f   qasm   -o shor_staq.qasm shor.qasm 

real	0m1.782s
user	0m1.534s
sys	0m0.058s

Crashes: staq -S -m -d square -M steiner add_3_5.qasm

When the staq is run with these arguments -S -m -d square -M steiner add_3_5.qasm, it crashes.

So, I change the code to try to catch std::exception, and its what() is printed out and it looks like this:

No indices left to pivot on, but multiple vectors remain!

Bugs in benchmarks

I was running some tools on the benchmarks in examples/staq_paper/benchmarks and I noticed that two of the benchmarks are ill-formed, which causes most tools (including Staq) to fail. Possibly a bug when converting from their original format to QASM?

  • mod_adder_1048576.qasm, line 402: ccx qubits[48],qubits[29],qubits[48];
  • cycle_17_3.qasm, line 9: ccx qubits[28],qubits[7],qubits[28];

Bug report: Oracle from Verilog delivers wrong results

Here is the test case oracle.zip

  1. Run this:

./staq -S -O2 -o output.qasm oracle.qasm

  1. Use the qpp qasm2 (you may modify to discard some qubits) to execute the output.qasm

  2. Observe the wrong final state and the measurements.

The oracle is a simple function to simulate a periodic function producing something like this:

0000 -> 0001
0001 -> 1011
0010 -> 0001
0011 -> 1011
...
1111 -> 1011

The test case uses the input value 0000, but the execution of the output.qasm will fail to produce 0001.

Python interface

Hi,

great project!

I was wondering if it is possible to add a Python interface to Staq compiler?
Of course that might be too much to ask, but on the other hand this would be super helpful for quantum researchers and developers.

In particular, I'm working on a project for automated benchmarking of quantum compilers, and having a python wrapper for basic functionality of Staq would be amazing:)

There are some existing automatic python wrappers for C++ code such as pybind11, SWIG, which are reasonably convenient to use.
Did you guys consider implementing something like that?

A simple test case to address a potential issue of desugaring the barrier gate

Here is a test case input.zip

It (input.qasm) looks like this:

OPENQASM 2.0;
include "qelib1.inc";

qreg q[1];
qreg anc[2];

barrier anc, q[0];

If we run:

./staq input.qasm

we will get this:

OPENQASM 2.0;
include "qelib1.inc";

qreg q[1];
qreg anc[2];
barrier anc[0],q[0];
barrier anc[1],q[0];

But, would it be better if we could get this instead:

OPENQASM 2.0;
include "qelib1.inc";

qreg q[1];
qreg anc[2];
barrier anc[0],anc[1],q[0];

I don't know if the latter is technically better, but it does look more concise. That would also make the life of the downstream applications easier because they don't have to try to figure out if they can or should combine the two barrier gates.

The final states of the output from staq mismatch those of the input

I just notice your update on the #34 and thank you for the effort.

But, it still does not produce the correct results.

I attach an input.qasm and you can do this to produce two output files.

./staq -S -O2 -o out2.qasm input.qasm
./staq -S -O3 -o out3.qasm input.qasm

Then, you can run each of the input.qasm, out2.qasm, out3.qasm with qpp example program qpp_qasm.

The final states from executing the three files are different from each other. The phases are different.

I tried both qpp and staq without USE_QISKIT_SPECS. The problem exists.

Then, I tried both qpp and staq with USE_QISKIT_SPECS. The problem still exists.

input.zip

JSON parse issue for device mapping

I'm trying to transpile a circuit to a custom device using pystaq.map, but I'm getting a JSON parse error which seems to be a bug with the module.

I'm using pystaq version 3.3 on Windows 10 with Python 3.11.

Here's a minimum working example:

import pystaq
import json

# qubit graph is a ring of four qubits
connectivity = [[0, 1], [1, 2], [2, 3], [3, 0]]

# define a QASM circuit
circ = """
OPENQASM 2.0;
include "qelib1.inc";

// Qubits: [q_0, q_1]
qreg q[2];

cx q[1],q[0];
t q[1];
cx q[0],q[1];
t q[0];"""

# convert to a pystaq circuit
p = pystaq.parse_str(circ)

# create a pystaq Device with four qubits
device = pystaq.Device(4)
[device.add_edge(*i) for i in connectivity]

# create json representation of the device
# and pass it to the pystaq map function
device_json = json.dumps(json.loads(str(device)))
print(device_json)
pystaq.map(p, device_json=device_json)

And the output is

{"couplings": [{"control": 0, "target": 1}, {"control": 0, "target": 3}, {"control": 1, "target": 0}, {"control": 1, "target": 2}, {"control": 2, "target": 1}, {"control": 2, "target": 3}, {"control": 3, "target": 0}, {"control": 3, "target": 2}], "name": "Custom Device", "qubits": [{"id": 0}, {"id": 1}, {"id": 2}, {"id": 3}]}

RuntimeError                              Traceback (most recent call last)
Untitled-1.ipynb Cell 1 line 3
     [27](vscode-notebook-cell:Untitled-1.ipynb?jupyter-notebook#W0sdW50aXRsZWQ%3D?line=26) # create json representation of the device
     [28](vscode-notebook-cell:Untitled-1.ipynb?jupyter-notebook#W0sdW50aXRsZWQ%3D?line=27) # and pass it to the pystaq map function
     [29](vscode-notebook-cell:Untitled-1.ipynb?jupyter-notebook#W0sdW50aXRsZWQ%3D?line=28) device_json = json.dumps(json.loads(str(device)))
---> [30](vscode-notebook-cell:Untitled-1.ipynb?jupyter-notebook#W0sdW50aXRsZWQ%3D?line=29) pystaq.map(p, device_json=device_json)

RuntimeError: [json.exception.parse_error.101] parse error at line 1, column 1: syntax error while parsing value - unexpected end of input; expected '[', '{', or a literal

However, I know that the JSON created is valid as I'm using the default Python module for it, so I think this is a bug in the implementation of map.

UGate and the u3(theta,phi,lambda) in the std lib

Suppose both UGate and u3 are used in a QASM file. During a traversal visit, the former will hit

void visit(ast::UGate& p_item) override

and the latter

void visit(ast::DeclaredGate& p_item) override

From a UGate, you can get the theta, phi, and lambda; from a DeclaredGate, you cannot.

Is it fair to say the DeclaredGate has missed some features?

By the way, our use case is to display a QASM graphically. Without the rotation information, all the u3 gates are displayed identically. They look a bit dull.

For that matter, all the std lib gates that carry rotation information have the similar problems.

Ambiguous results from staq -S -f quil -o quil_output.txt add_3_5.qasm

When staq -S -f quil -o quil_output.txt add_3_5.qasm is run, the result is produced in the quil_output.txt file. But the execution also crashes with this exception:

Quil instruction set has no support for local ancillas

Is the result in the quil_output.txt valid? I mean, a result is usually considered invalid if a crash happens. If it is valid, an info log should be used instead of an exception.

By the way, thanks for the previous fix. That works fine now.

barrier on classical bits

This QASM can perfectly pass staq.

OPENQASM 2.0;
include "qelib1.inc";

qreg q[2];
creg c[2];

barrier q, c;

I read the QASM spec again and it seems to be vague on the syntax. The grammar in Backus-Naur form allows that, but the spec also notes "Not all programs produced using this grammar are valid Open QASM circuits." All the examples in the spec suggest the barrier is designed for the qubits only.

If that syntax is indeed valid, what would be the purpose of a barrier on classical bits? I have exhausted my brainpower, but still cannot come up with a use case for that.

Could you please shed light on it?

Thank you.

Oracle synthesis: verilog array format?

I'm wondering if the Verilog oracle synthesis syntax supports vectors as inputs/outputs?

Minimal example:

Oracle declaration:

module oracle (a, b, c);
	input [1:0] a;
	input [1:0] b;
	output c;
        //random example function
	assign c = (a[0] & b[1]) ^ (a[1] & b[0]);
endmodule

With QASM:

OPENQASM 2.0;
include "qelib1.inc";

oracle test a, b, c { "reg_oracle.v" }

qreg a[2];
qreg b[2];
qreg c[1];

test a, b, c;

gives a compiler error:

test_oracle.qasm:10:10: Register c has incompatible length

Thanks!

Swap mapping issues: final states of 7 qubits

Thanks for the previous qpp fix. The Grover's algorithm circuit works fine now, for all the physical devices, and for both swap and steiner.

Here is a test case to reveal another possible bug.

128_2_1.zip

It produces a final periodic state like this:

>> Final state:
    0 
0.125 
    0 
0.125 
.
.
.
    0 
0.125 

Now, run this:

./staq -S -m -O1 -d square -M swap -l bestfit -f qasm -o 128_2_1_staq.qasm 128_2_1.qasm

The 128_2_1_staq.qasm produces a final (virtual) state like this: 64 zeros followed by 64 values of 0.125. The interpretation is based on // Output layout (physical --> virtual) info.

>> Final state:
    0 
.
.
.
    0 
0.125 
.
.
.
0.125 

That is wrong. By the way, it works perfectly if the steiner is used.

The problem seems to happen when the number of qubits increases. For example, here is a similar test case with fewer qubits. The swap works as expected.

32_2_1.zip

Bug report: // comment vs. //comment

Here is a trivial bug, but it can be quite unsettling.

//input.qasm
OPENQASM 2.0;
include "qelib1.inc";

oracle test a0,a1,a2,a3,c0,c1,c2,c3 { "test.v" }
// test.v
module top (\a[0],\a[1],\a[2],\a[3],\c[0],\c[1],\c[2],\c[3]);
  input \a[0],\a[1],\a[2],\a[3];
  output \c[0],\c[1],\c[2],\c[3];
  
  wire n386;
  assign n386 = \a[0]  & \a[1];
  
  // whatever
  
  assign \c[3]  = n386;

endmodule

I run this. It will be fine.

./staq -S input.qasm

Now, if I remove the space in the second comment line in the test.v to make it look like this:

//whatever

It will cause an arcane error:

Could not map network into klut network

If I remove the space in the first comment line in the test.v:

//test.v

It will crash outright.

Why not a function is_std() in the GateDecl/Decl?

The Program is quite useful. When I iterate over it to get all the nodes, I want to know if each of them is part of the std library or not. I notice you are checking that internally like this: qelib_defs.find(id_) != qelib_defs.end(). I think it may be user-friendly to expose that as a public function, say, is_std().

On the other hand, I understand what I am suggesting sounds like some syntax sugar because, without that function, I can simply do the same checking in my own code as long as you maintain the qelib_defs as a public constant.

Anyway, just some food for thought.

Transpilation incorrectly removes necessary classical conditions

Hello, I'm working with the Staq optimizer and it erroneously removes necessary if conditions.

To reproduce this issue, optimize the following bug.qasm:

cat bug.qasm
OPENQASM 2.0;
include "qelib1.inc";
gate cx_o0 q0,q1 { x q0; cx q0,q1; x q0; }
qreg q[2];
creg c[1];
cx_o0 q[1],q[0];
swap q[0],q[1];
if(c==1) cx q[1],q[0];
measure q[0] -> c[0];

Since there are no prior measurements to change the initial state of c, the condition c==1 should be false and the cx gate should not be executed.

staq --version
staq version 3.3
(c) 2019 - 2023 softwareQ Inc. All rights reserved.staq -O3 bug.qasm
OPENQASM 2.0;
include "qelib1.inc";

qreg q[2];
creg c[1];
x q[1];
cx q[1],q[0];
x q[1];
swap q[0],q[1];
cx q[1],q[0];
measure q[0] -> c[0];

However, Staq optimizer removes the condition if (c==1). It should not be removed because:

  1. cx_o0 q[1],q[0]; flips q[0] to 1, because cx_o0 is a controlled-x gate with condition control qubit == 0.
  2. By swap q[0],q[1]; now q[1] is 1.
  3. if there's no if (c==1), cx q[1],q[0]; will erroneously flip q[0].

Weird: staq hangs when it runs this qasm file.

I will characterize the problem as this:

If the last line of a QASM file is a comment line that is not followed by a new line/line feed, the staq will hang. See the attached file weird.zip, which looks like this:

OPENQASM 2.0;
include "qelib1.inc";

//

Now, run this and it will never end.

./staq weird.qasm

OpenQASM->ProjectQ minor issues

When transpiling from OpenQASM to ProjectQ the minimal example

OPENQASM 2.0;
include "qelib1.inc";

the code produced is

from projectq import MainEngine, ops
from cmath import pi,exp,sin,cos,tan,log as ln,sqrt
import numpy as np

def SdagGate(): return ops.Sdag
def TdagGate(): return ops.Tdag
def CNOTGate(): return ops.CNOT
def CZGate(): return ops.CZ
def CCXGate(): return ops.Toffoli

class UGate(ops.BasicGate):
    def __init__(self, theta, phi, lambd):
        ops.BasicGate.__init__(self)
        self.theta = theta
        self.phi = phi
        self.lambd = lambd

    def __str__(self):
        return str(self.__class__.__name__) + "(" + str(self.theta) + "," \
               + str(self.phi) + "," + str(self.lambd) + ")"

    def tex_str(self):
        return str(self.__class__.__name__) + "$(" + str(self.theta) + "," \
               + str(self.phi) + "," + str(self.lambd) + ")$"

    def get_inverse(self):
        tmp = 2 * pi
        return self.__class__(-self.theta + tmp, -self.lambd + tmp, -self.phi + tmp)

    def __eq__(self, other):
        if isinstance(other, self.__class__):
            return self.theta == other.theta \
                   & self.phi == other.phi \
                   & self.lambd == other.lambd
        else:
            return False

    def __ne__(self, other):
        return not self.__eq__(other)

    def __hash__(self):
        return hash(str(self))

    @property
    def matrix(self):
        return np.matrix([[exp(-1j*(self.phi+self.lambd)/2)*cos(self.theta/2),
                           -exp(-1j*(self.phi-self.lambd)/2)*sin(self.theta/2)],
                          [exp(1j*(self.phi-self.lambd)/2)*sin(self.theta/2),
                           exp(1j*(self.phi+self.lambd)/2)*cos(self.theta/2)]])

class ResetGate(ops.FastForwardingGate):
    def __str__(self):
        return "Reset"

    def __or__(self, qubit):
        ops.Measure | qubit
        if int(qubit):
            ops.X | qubit
Reset = ResetGate()

class u3(ops.BasicGate):
    def __init__(self, theta, phi, lambd):
        ops.BasicGate.__init__(self)
        self.theta = theta
        self.phi = phi
        self.lambd = lambd

    def __str__(self):
        return str(self.__class__.__name__) + "(" + str(self.theta) + "," + str(self.phi) + "," + str(self.lambd) + ")"

    def __eq__(self, other):
        if isinstance(other, self.__class__):
            return True\
                   & self.theta == other.theta\
                   & self.phi == other.phi\
                   & self.lambd == other.lambd
        else:
            return False

    def __ne__(self, other):
        return not self.__eq__(other)

    def __hash__(self):
        return hash(str(self))

    def __or__(self, qubits):
        if len(qubits) != 1:
            raise TypeError("Expected 1 qubits, given " + len(qubits))
        q = qubits[0]

        UGate(theta, phi, lambd) | q

class u2(ops.BasicGate):
    def __init__(self, phi, lambd):
        ops.BasicGate.__init__(self)
        self.phi = phi
        self.lambd = lambd

    def __str__(self):
        return str(self.__class__.__name__) + "(" + str(self.phi) + "," + str(self.lambd) + ")"

    def __eq__(self, other):
        if isinstance(other, self.__class__):
            return True\
                   & self.phi == other.phi\
                   & self.lambd == other.lambd
        else:
            return False

    def __ne__(self, other):
        return not self.__eq__(other)

    def __hash__(self):
        return hash(str(self))

    def __or__(self, qubits):
        if len(qubits) != 1:
            raise TypeError("Expected 1 qubits, given " + len(qubits))
        q = qubits[0]

        UGate(pi/2, phi, lambd) | q

class u0(ops.BasicGate):
    def __init__(self, gamma):
        ops.BasicGate.__init__(self)
        self.gamma = gamma

    def __str__(self):
        return str(self.__class__.__name__) + "(" + str(self.gamma) + ")"

    def __eq__(self, other):
        if isinstance(other, self.__class__):
            return True\
                   & self.gamma == other.gamma
        else:
            return False

    def __ne__(self, other):
        return not self.__eq__(other)

    def __hash__(self):
        return hash(str(self))

    def __or__(self, qubits):
        if len(qubits) != 1:
            raise TypeError("Expected 1 qubits, given " + len(qubits))
        q = qubits[0]

        UGate(0, 0, 0) | q

class cy(ops.BasicGate):
    def __init__(self, ):
        ops.BasicGate.__init__(self)

    def __str__(self):
        return str(self.__class__.__name__) + "(" +  + ")"

    def __eq__(self, other):
        if isinstance(other, self.__class__):
            return True
        else:
            return False

    def __ne__(self, other):
        return not self.__eq__(other)

    def __hash__(self):
        return hash(str(self))

    def __or__(self, qubits):
        if len(qubits) != 2:
            raise TypeError("Expected 2 qubits, given " + len(qubits))
        a = qubits[0]
        b = qubits[1]

        SdagGate() | (b)
        CNOTGate() | (a, b)
        ops.SGate() | (b)

class swap(ops.BasicGate):
    def __init__(self, ):
        ops.BasicGate.__init__(self)

    def __str__(self):
        return str(self.__class__.__name__) + "(" +  + ")"

    def __eq__(self, other):
        if isinstance(other, self.__class__):
            return True
        else:
            return False

    def __ne__(self, other):
        return not self.__eq__(other)

    def __hash__(self):
        return hash(str(self))

    def __or__(self, qubits):
        if len(qubits) != 2:
            raise TypeError("Expected 2 qubits, given " + len(qubits))
        a = qubits[0]
        b = qubits[1]

        CNOTGate() | (a, b)
        CNOTGate() | (b, a)
        CNOTGate() | (a, b)

class ch(ops.BasicGate):
    def __init__(self, ):
        ops.BasicGate.__init__(self)

    def __str__(self):
        return str(self.__class__.__name__) + "(" +  + ")"

    def __eq__(self, other):
        if isinstance(other, self.__class__):
            return True
        else:
            return False

    def __ne__(self, other):
        return not self.__eq__(other)

    def __hash__(self):
        return hash(str(self))

    def __or__(self, qubits):
        if len(qubits) != 2:
            raise TypeError("Expected 2 qubits, given " + len(qubits))
        a = qubits[0]
        b = qubits[1]

        ops.HGate() | (b)
        SdagGate() | (b)
        CNOTGate() | (a, b)
        ops.HGate() | (b)
        ops.TGate() | (b)
        CNOTGate() | (a, b)
        ops.TGate() | (b)
        ops.HGate() | (b)
        ops.SGate() | (b)
        ops.XGate() | (b)
        ops.SGate() | (a)

class cu3(ops.BasicGate):
    def __init__(self, theta, phi, lambd):
        ops.BasicGate.__init__(self)
        self.theta = theta
        self.phi = phi
        self.lambd = lambd

    def __str__(self):
        return str(self.__class__.__name__) + "(" + str(self.theta) + "," + str(self.phi) + "," + str(self.lambd) + ")"

    def __eq__(self, other):
        if isinstance(other, self.__class__):
            return True\
                   & self.theta == other.theta\
                   & self.phi == other.phi\
                   & self.lambd == other.lambd
        else:
            return False

    def __ne__(self, other):
        return not self.__eq__(other)

    def __hash__(self):
        return hash(str(self))

    def __or__(self, qubits):
        if len(qubits) != 2:
            raise TypeError("Expected 2 qubits, given " + len(qubits))
        c = qubits[0]
        t = qubits[1]

        ops.Rz((lambd-phi)/2) | (t)
        CNOTGate() | (c, t)
        u3(-theta/2, 0, -(phi+lambd)/2) | (t)
        CNOTGate() | (c, t)
        u3(theta/2, phi, 0) | (t)

if __name__ == "__main__":
    eng = MainEngine()

I think there are some self. missing (at least PyCharm is complaining), such as

def __or__(self, qubits):
        if len(qubits) != 1:
            raise TypeError("Expected 1 qubits, given " + len(qubits))
        q = qubits[0]

        UGate(theta, phi, lambd) | q 

In the last line we should have UGate(self.theta, self.phi, self.lambd) | q. And everywhere else we're calling the UGate/u3/u2/etc from within class methods.

@meamy can you please double-check when you get a chance? I think the same happens with the other python-like APIs (such as e.g. Circ)

Question on Evaluating Staq

Hi, I'm working on an updated version of our paper on verified optimization of quantum circuits and I'd like to include Staq's results on our benchmarks. What is the best way to evaluate Staq's optimizations (without routing)? I'd like to use the Python interface if possible. I was thinking that I should emulate Staq's -O3 option, which does simplify, rotfold and cnotsynth (per staq/main.cpp#L217), but I don't see a Python binding for the cnotsynth pass. Is there a reason for this?

Additionally, if I wanted to evaluate both optimization and routing, which of Staq's optimizations will preserve connectivity guarantees? (And do you expect the optimizations would have any effect on routed circuits?)

Thanks!

Output qubit mapping information

As per issue #18 the compiler should provide information about the mapping between qubits of the input circuit and the output, possibly in JSON format.

Cirq output error for NamedQubit

Given the following simple OpenQasm

OPENQASM 2.0;
include "qelib1.inc";
qreg q[2];
creg c[2];
h q[0];
cx q[0],q[1];
measure q -> c;

running staq to output Cirq code results in a source string that will throw an error upon execution

Traceback (most recent call last):
  File "bellcirq.py", line 98, in <module>
    cirq.CNOT(q[0], q[1]),
  File "/home/cades/.local/lib/python3.7/site-packages/cirq/ops/raw_types.py", line 259, in __call__
    return self.on(*args, **kwargs)
  File "/home/cades/.local/lib/python3.7/site-packages/cirq/ops/common_gates.py", line 861, in on
    return super().on(*args)
  File "/home/cades/.local/lib/python3.7/site-packages/cirq/ops/raw_types.py", line 200, in on
    return gate_operation.GateOperation(self, list(qubits))
  File "/home/cades/.local/lib/python3.7/site-packages/cirq/ops/gate_operation.py", line 41, in __init__
    gate.validate_args(qubits)
  File "/home/cades/.local/lib/python3.7/site-packages/cirq/ops/raw_types.py", line 190, in validate_args
    _validate_qid_shape(self, qubits)
  File "/home/cades/.local/lib/python3.7/site-packages/cirq/ops/raw_types.py", line 520, in _validate_qid_shape
    val, qubits))
ValueError: Duplicate qids for <cirq.CNOT>. Expected unique qids but got <[cirq.NamedQubit('q[i]'), cirq.NamedQubit('q[i]')]>.

The fix is pretty simple, update https://github.com/softwareQinc/staq/blob/master/include/output/cirq.hpp#L272 to evaluate the index var i.

Something like 

q = [cirq.NamedQubit("q[{}]".format(i)) for i in range(2)]

Error: libc++abi.dylib: terminating with uncaught exception of type std::logic_error: No indices left to pivot on, but multiple vectors remain!

Hi,

I am trying to run the following command:
./staq -S -O2 -f resources -m --device square -l bestfit -M steiner filename
But I get the error:
`libc++abi.dylib: terminating with uncaught exception of type std::logic_error: No indices left to pivot on, but multiple vectors remain!

Abort trap: 6`

My file is a randomly generated 9 qubit .qasm file:
OPENQASM 2.0; include "qelib1.inc"; qreg q[9]; cx q[4], q[8]; cx q[2], q[5]; cx q[1], q[0]; cx q[1], q[2]; cx q[2], q[7]; cx q[0], q[3]; cx q[2], q[5]; cx q[0], q[7]; cx q[0], q[3]; cx q[7], q[2]; rz(0.5*pi) q[0]; rz(-0.25*pi) q[1]; rz(1.0*pi) q[2]; rz(0.25*pi) q[3]; rz(-1.0*pi) q[4]; rz(-0.5*pi) q[5]; rz(-0.25*pi) q[6]; rz(1.0*pi) q[7]; rz(1.0*pi) q[8];

A bug? Oracle from Verilog.

Here is the test case:

// 4bits.qasm
OPENQASM 2.0;
include "qelib1.inc";

oracle test a0,a1,c0,c1 { "4bits.v" }

qreg q[2];
qreg out[2];

x q[0];
x q[1];

test q[0], q[1],out[0],out[1];

Two versions of the 4bits.v. One has assign \c[0] = n386; and the other assign \c[1] = n386;

// 4bits.v
module top (\a[0],\a[1],\c[0],\c[1]);
input \a[0],\a[1];
output \c[0],\c[1];

wire n386;

assign n386 = \a[0] & \a[1];

assign \c[0] = n386;

endmodule

// 4bits.v
module top (\a[0],\a[1],\c[0],\c[1]);
input \a[0],\a[1];
output \c[0],\c[1];

wire n386;

assign n386 = \a[0] & \a[1];

assign \c[1] = n386;

endmodule

Now, I run this twice, with each of the two Verilog files. I get the identical output. That doesn't seem to be correct.

/staq -S 4bits.qasm

Phases of the final states shifted

@meamy You may be aware of the qpp issue that has been fixed by @vsoftco.

I think there may be something to do at the staq side.

I attached two files in.qasm and out.qasm.
2files.zip

The latter is the result of running this command:

./staq -S -O3 -o out.qasm in.qasm

The two files do not produce the same final states. Could you please take a look at them?

To process my QASM files, I did expand the qelib1.inc by adding a few controlled gates and added their references to the the staq source code locally:

    gate cs ctrl, a { cu1(pi/2) ctrl, a; } 
    gate csdg ctrl, a { cu1(-pi/2) ctrl, a; } 
    gate ct ctrl, a { cu1(pi/4) ctrl, a; }     
    gate ctdg ctrl, a { cu1(-pi/4) ctrl, a; } 
    gate cr(theta, phi) ctrl, a { cu3(theta,phi-pi/2,pi/2-phi) ctrl, a; } 

Some feedback of building staq by the latest clang

I tried this to specify the clang as my compiler:

cmake .. -D CMAKE_CXX_COMPILER=clang++

After that, the make will generate this famous error (a lot of people are discussing about why the very basic headers cannot be found by clang.)

fatal error: 'string' file not found

So, I changed the CMakeLists.txt of the staq from this:

`set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -stdlib=libc++")`

to this:

`set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS}  ")`

Everything works like a charm.

Now, I am not an expert of the tool chains. I just feel that clang should be smart enough to figure out where to get the basic things like standard headers on a mainstream Linux box such as the Ubuntu 19.04, which I am using.

Did you build it successfully by clang on your box with that -stdlib=libc++?

Questions: "Input layout" and "Output layout"; swap and steiner

Could you please clarify a few concepts?

In the previous issue (#20), when the swap mapping algorithm is used, the "Output layout" is produced in addition to the "Input layout". Could you please explain the concepts? I thought, when the physical circuit is sent out to a real device for a run, there would be only one layout?

You suggested we use the steiner. It does work better for us so far. Are there any circumstances where the swap is supposed to have an advantage?

Outputs from staq distort the business logic of the input

The outputs are suspicious.

Here is a test case input.zip

Run this:

./staq -f quil -o quil_output.txt input.qasm

When the quil_output.txt is pumped into the rigetti QVM simulator 9q-square-qvm to run on rigetti Forest SDK 2.12.0, it will crash. It seems that staq expects the mapping information to produce a correct quil program. If that is the case, the above command should fail, instead of producing an invalid program.

So, I specify the mapping like this:

./staq -m -d square -f quil -o quil_output.txt input.qasm

Now, the program won't crash on rigetti Forest SDK 2.12.0 , but it will produce a final state that is inconsistent with what the input QASM would produce. It will still crash on rigetti Forest SDK 2.20.0.

It is very likely that the problem exists for other output formats. I am almost sure the QASM output (i.e. -f qasm) will also produce a wrong final state, but I cannot verify it with the sister project Quantum++ until its own bug related to QASM execution of ry and rz gates is resolved.

Mapping issues continued: non-trivial circuits

I attach a non-trivial circuit (grover.qasm) to illustrate another problem.

grover.zip

It is a simple 3-qubit Grover's algorithm that renders a final state with the winner 6, as shown here:

>> Final state:
0.0794641 + 0.0386956i
0.0794675 + 0.0386923i
 0.0794644 + 0.038689i
0.0794666 + 0.0386924i
0.0794661 + 0.0386921i
0.0794668 + 0.0386947i
 -0.874177 - 0.425596i
 0.0794668 + 0.038692i

As you can see, the 6 with a probability 95% stands out. That is perfect.

Now, run this:

./staq -S -m -O1 -d square -M steiner -l bestfit -f qasm -o grover_staq.qasm grover.qasm

The grover_staq.qasm produces a final state like this:

>> Final state:
 -0.0632695 + 0.318087i
-0.0844088 + 0.0778586i
  -0.118019 - 0.185286i
  -0.470594 - 0.208363i
  -0.259683 + 0.105562i
 -0.0452083 + 0.179949i
  -0.232798 - 0.294897i
  0.560054 - 0.0259715i

That is wrong because no clear winner stands out.

"if statement" - looks like a bug.

This snippet (Let's call it input.qasm.) reveals a problem.

OPENQASM 2.0;
include "qelib1.inc";

qreg q[3];
qreg qqq[1];

creg c[3];

measure q -> c;

if(c==1) x qqq;

When I run this:

./staq -o output.qasm input.qasm

It will produce this output:

OPENQASM 2.0;
include "qelib1.inc";

qreg q[3];
qreg qqq[1];
creg c[3];
measure q[0] -> c[0];
measure q[1] -> c[1];
measure q[2] -> c[2];
x qqq[0];

The output is functionally different from the input because the if statement is gone.

If the if statement takes this form

if(c==1) x qqq[0];

everything will work as expected.

A simple test case to reveal a potential bug

I've attached two files: input and output QASM files
input_output.zip as a result of running this:

./staq -S -m -O1 -d tokyo -M swap -l linear -f qasm -o output.qasm input.qasm

The input is as simple as this:

OPENQASM 2.0;
include "qelib1.inc";

qreg a[4];
qreg b[4];

CX b[0], a[0];
CX a[3], b[1];

But the output.qasm file has something like this in it:

qreg q[8];
//...
CX q[4],q[8];

Since only 8 qubits are available, we cannot have the CX q[4],q[8];, can we?

Bug Report: staq hangs with these arguments

Here is a test case (a slightly modified example code shipped with the staq).

The Verilog file toffoli_4.v

module top ( a, b, c, d, e );
  input a, b, c, d;
  output e;
  wire subprod1, subprod2;
  assign subprod1 = a & b;
  assign subprod2 = c & d;
  assign e = subprod1 & subprod2;
endmodule

The QASM file oracle_example.qasm

OPENQASM 2.0;
include "qelib1.inc";

oracle tof4 a,b,c,d,e { "toffoli_4.v" }

qreg q[5];

tof4 q[1],q[1],q[2],q[3],q[4];

Now run this. It will hang.

./staq -S -m -d tokyo -f resources oracle_example.qasm

Note that I deliberately introduced the typo q[1],q[1]. When that happens, would it be better for staq to throw an exception or something, instead of hanging?

Bug report: CX gate on the same qubit

Here is a test case input.zip

./staq -S -m -O1 -d fullycon -M swap -l eager -f qasm input.qasm

Something like this will appear in the output:

CX q[0],q[0];

The OpenQASM spec is let loose on that syntax, but I think that should be deemed an outright syntax error.

Changing qubit register names of input circuit affects compiled circuit

I noticed for some large circuits that changing the name of the qubit registers in the input circuit affects the final CX gate count and depth of the compiled circuit.

I compiled a 49 qubit circuit that has a CX gate between every pair of qubits onto a 7x7 grid multiple times. The first time I named the 49 qubit register 'q0', and each subsequent time I incremented the number, so the second time it was 'q1', then 'q2' and so on. Each time I compiled the circuit the resource counts were different. Other than the register name, the input circuit remains exactly the same each time, including the order that the CX gates appear in.

For qreg q0[49]:
depth: 24 CX: 76 qubits: 49
For qreg q1[49]:
depth: 43 CX: 75 qubits: 49
For qreg q2[49]:
depth: 50 CX: 92 qubits: 49
For qreg q3[49]:
depth: 44 CX: 94 qubits: 49
For qreg q4[49]:
depth: 41 CX: 75 qubits: 49
For qreg q5[49]:
depth: 39 CX: 91 qubits: 49
...

I also tried many other names, including names without numbers and the results kept changing. If I were to keep the same name and compile multiple times I get the same result every time.

This only seems to be happening with large circuits, and the disparity in the results seems to be larger for circuits with highly connected virtual qubits.

I compiled 'gf2^16_mult.qasm' from the Feynman repository onto the same grid, once as is and a second time with the register name changed from 'qubits' to 'qubits0' and got different results for both depth and number of qubits.

I generated the device using
./staq_device_generator -r 7 7 > device.json

and obtained the resource counts using
./staq -m -d device.json -l bestfit -M steiner -f resources circuit.qasm

Optimization by self-annihilating sequences

I use two test cases to illustrate an issue.

Case 1 - cx gates

OPENQASM 2.0;
include "qelib1.inc";

qreg q[2];
h q;

cx q[0], q[1];
cx q[0], q[1];
cx q[0], q[1];
cx q[0], q[1];

After staq with optimization:

OPENQASM 2.0;
include "qelib1.inc";

qreg q[2];
h q[0];
h q[1];

The cx gates have been cancelled nicely.

Case 2 - ccx gates

OPENQASM 2.0;
include "qelib1.inc";

qreg q[3];
h q;

ccx q[0], q[1], q[2];
ccx q[0], q[1], q[2];
ccx q[0], q[1], q[2];
ccx q[0], q[1], q[2];

After staq with optimization:

OPENQASM 2.0;
include "qelib1.inc";

qreg q[3];
h q[0];
h q[1];
z q[2];
z q[1];
cx q[2],q[1];
rz(((pi*3)/2)+((pi*3)/2)) q[1];
s q[0];
cx q[2],q[0];
rz(((pi*3)/2)+((pi*3)/2)) q[0];
cx q[1],q[0];
sdg q[0];
cx q[2],q[0];
z q[0];
cx q[2],q[1];
cx q[2],q[0];
cx q[1],q[0];
h q[2];
s q[0];
cx q[1],q[0];
sdg q[0];
cx q[1],q[0];

The ccx gates should also cancel each other out, shouldn't they?

I read an old article, which may not be directly related to what the staq wants to achieve. Nevertheless, the authors introduced an interesting idea of removing self-annihilating sequences. I am wondering if staq could borrow the idea and repurpose it to optimize similar sequences for staq applications.

In the classical world, a decent modern IDE can use static analysis to identify a lot of redundant code, dead code, suspicious code, no-op blocks, obvious stupidity, etc., before the complier is run. Do you think if staq can introduce a pass to target those usual suspects as well, especially the self-annihilating sequences? In the long run, that may even help the IDE vendors.

A minor problem?

Here is a test file input.qasm:

OPENQASM 2.0;
include "qelib1.inc";

qreg q[2];

cu1(pi/4) q[0], q[1];
cu1(pi/4) q[0], q[1];
cu1(pi/4) q[0], q[1];
cu1(pi/4) q[0], q[1];

I run this:

./staq -S -O3 -o output.qasm input.qasm

The output will be:

OPENQASM 2.0;
include "qelib1.inc";

qreg q[2];
s q[1];
s q[0];
cx q[1],q[0];
rz((((-(pi/4)/2)+(-(pi/4)/2))+(-(pi/4)/2))+(-(pi/4)/2)) q[0];
cx q[1],q[0];

The argument of the rz gate does not look nice. Its length will grow with the number of the cu1 gates. It also happens on other gates. Would it be nice if the staq could evaluate the arguments of gates? Is it too difficult for the staq to achieve that? Any pros and cons?

By the way, the problem is considered minor because the argument expression is evaluated by the qpp correctly. When the staq API is used to traverse the circuit, it also correctly evaluates that to rz(-1.57).

Basis ordering discrepancy between Rigetti and staq

Here is a simple test case:

OPENQASM 2.0;
include "qelib1.inc";

qreg q[2];
id q[0];
x q[1];

The final state by a qpp run:

>> Final state:
0   1   0   0 

The snippet of the Rigetti Quil code out of the staq:

I 0
X 1

The final state by a Rigetti run:

[
0.+0.j 
0.+0.j 
1.+0.j 
0.+0.j
]

Rigetti makes a very big deal of basis ordering, which is documented here.

I quote the key point:

The WavefunctionSimulator enumerates bitstrings such that qubit 0 is the least significant bit (LSB)...

This convention is counter to that often found in the quantum computing literature where bitstrings are often ordered such that the lowest-index qubit is on the left.

Inliner does not inline conditioned gates

Hello, I'm working with the inliner and it does not inline gates with if conditions.

To reproduce this issue, inline the following bug.qasm:

❯ cat bug.qasm
OPENQASM 2.0;
include "qelib1.inc";
gate cx_o q0,q1 { x q0; cx q0,q1; x q0; }
qreg q[2];
creg c[2];
if (c==1) cx_o q[0], q[1];

cx_o gate is not inlined:

❯ staq --version
staq version 3.5
(c) 2019 - 2024 softwareQ Inc. All rights reserved.
❯ staq_inliner < bug.qasm
OPENQASM 2.0;
include "qelib1.inc";

gate cx_o q0,q1 {
        x q0;
        cx q0,q1;
        x q0;
}
qreg q[2];
creg c[2];
if (c==1) cx_o q[0],q[1];

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.