Code Monkey home page Code Monkey logo

projectq's People

Contributors

amilstead avatar arijordan avatar babbush avatar bartleyg avatar bombenchris avatar cclauss avatar cgogolin avatar cynocracy avatar damiansteiger avatar darkdragon84 avatar dbretaud avatar dependabot[bot] avatar dwierichs avatar eltociear avatar fernandodelaiglesia avatar francabrera avatar giggleliu avatar github-actions[bot] avatar kevinsung avatar maffoo avatar melven avatar msoeken avatar peter-janderks avatar pre-commit-ci[bot] avatar saich08 avatar strilanc avatar takishima avatar thomashaener avatar web-flow avatar xyshe 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  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

projectq's Issues

exiting before measureing all qubits generates many unfriendly error messages

to reproduce, modify the quantum_random_numbers.py to have

raise Exception("...") just above the Measure | q1 operation

i get the following

Error in atexit._run_exitfuncs:
Traceback (most recent call last):
  File "/home/noon/dev/ProjectQ/projectq/cengines/_main.py", line 133, in <lambda>
    self._delfun = lambda x: x.flush(deallocate_qubits=True)
  File "/home/noon/dev/ProjectQ/projectq/cengines/_main.py", line 227, in flush
    qb.__del__()
  File "/home/noon/dev/ProjectQ/projectq/types/_qubit.py", line 114, in __del__
    self.engine.deallocate_qubit(self)
  File "/home/noon/dev/ProjectQ/projectq/cengines/_basics.py", line 151, in deallocate_qubit
    self.send([Command(self, Deallocate, ([qubit],))])
  File "/home/noon/dev/ProjectQ/projectq/cengines/_basics.py", line 191, in send
    self.next_engine.receive(command_list)
  File "/home/noon/dev/ProjectQ/projectq/cengines/_tagremover.py", line 56, in receive
    self.send([cmd])
  File "/home/noon/dev/ProjectQ/projectq/cengines/_basics.py", line 191, in send
    self.next_engine.receive(command_list)
  File "/home/noon/dev/ProjectQ/projectq/cengines/_optimize.py", line 236, in receive
    self._cache_cmd(cmd)
  File "/home/noon/dev/ProjectQ/projectq/cengines/_optimize.py", line 222, in _cache_cmd
    self._check_and_send()
  File "/home/noon/dev/ProjectQ/projectq/cengines/_optimize.py", line 200, in _check_and_send
    self._send_qubit_pipeline(i, len(self._l[i]))
  File "/home/noon/dev/ProjectQ/projectq/cengines/_optimize.py", line 78, in _send_qubit_pipeline
    self.send([il[i]])
  File "/home/noon/dev/ProjectQ/projectq/cengines/_basics.py", line 191, in send
    self.next_engine.receive(command_list)
  File "/home/noon/dev/ProjectQ/projectq/cengines/_replacer/_replacer.py", line 188, in receive
    self._process_command(cmd)
  File "/home/noon/dev/ProjectQ/projectq/cengines/_replacer/_replacer.py", line 124, in _process_command
    self.send([cmd])
  File "/home/noon/dev/ProjectQ/projectq/cengines/_basics.py", line 191, in send
    self.next_engine.receive(command_list)
  File "/home/noon/dev/ProjectQ/projectq/cengines/_tagremover.py", line 56, in receive
    self.send([cmd])
  File "/home/noon/dev/ProjectQ/projectq/cengines/_basics.py", line 191, in send
    self.next_engine.receive(command_list)
  File "/home/noon/dev/ProjectQ/projectq/cengines/_optimize.py", line 236, in receive
    self._cache_cmd(cmd)
  File "/home/noon/dev/ProjectQ/projectq/cengines/_optimize.py", line 222, in _cache_cmd
    self._check_and_send()
  File "/home/noon/dev/ProjectQ/projectq/cengines/_optimize.py", line 200, in _check_and_send
    self._send_qubit_pipeline(i, len(self._l[i]))
  File "/home/noon/dev/ProjectQ/projectq/cengines/_optimize.py", line 78, in _send_qubit_pipeline
    self.send([il[i]])
  File "/home/noon/dev/ProjectQ/projectq/cengines/_basics.py", line 191, in send
    self.next_engine.receive(command_list)
  File "/home/noon/dev/ProjectQ/projectq/backends/_sim/_simulator.py", line 181, in receive
    self._handle(cmd)
  File "/home/noon/dev/ProjectQ/projectq/backends/_sim/_simulator.py", line 147, in _handle
    self._simulator.deallocate_qubit(ID)
RuntimeError: Error: Qubit has not been measured / uncomputed! There is most likely a bug in your code.
Exception ignored in: <bound method Qubit.__del__ of <projectq.types._qubit.Qubit object at 0x7fcce59c5f60>>
Traceback (most recent call last):
  File "/home/noon/dev/ProjectQ/projectq/types/_qubit.py", line 114, in __del__
    self.engine.deallocate_qubit(self)
  File "/home/noon/dev/ProjectQ/projectq/cengines/_basics.py", line 151, in deallocate_qubit
    self.send([Command(self, Deallocate, ([qubit],))])
  File "/home/noon/dev/ProjectQ/projectq/cengines/_basics.py", line 191, in send
    self.next_engine.receive(command_list)
  File "/home/noon/dev/ProjectQ/projectq/cengines/_tagremover.py", line 56, in receive
    self.send([cmd])
  File "/home/noon/dev/ProjectQ/projectq/cengines/_basics.py", line 191, in send
    self.next_engine.receive(command_list)
  File "/home/noon/dev/ProjectQ/projectq/cengines/_optimize.py", line 236, in receive
    self._cache_cmd(cmd)
  File "/home/noon/dev/ProjectQ/projectq/cengines/_optimize.py", line 222, in _cache_cmd
    self._check_and_send()
  File "/home/noon/dev/ProjectQ/projectq/cengines/_optimize.py", line 194, in _check_and_send
    self._optimize(i)
  File "/home/noon/dev/ProjectQ/projectq/cengines/_optimize.py", line 136, in _optimize
    inv = self._l[idx][i].get_inverse()
  File "/home/noon/dev/ProjectQ/projectq/ops/_command.py", line 133, in get_inverse
    cmd = Command(self._engine, projectq.ops.get_inverse(self.gate), self.qubits)
  File "/home/noon/dev/ProjectQ/projectq/ops/_metagates.py", line 117, in get_inverse
    return gate.get_inverse()
  File "/home/noon/dev/ProjectQ/projectq/ops/_basics.py", line 215, in get_inverse
    return deepcopy(self)
  File "/home/noon/tools/anaconda3/envs/projectq/lib/python3.5/copy.py", line 174, in deepcopy
    rv = reductor(4)
AttributeError: 'NoneType' object has no attribute '__newobj__'
Exception ignored in: <bound method MainEngine.__del__ of <projectq.cengines._main.MainEngine object at 0x7fccfded00f0>>
Traceback (most recent call last):
  File "/home/noon/dev/ProjectQ/projectq/cengines/_main.py", line 143, in __del__
  File "/home/noon/dev/ProjectQ/projectq/cengines/_main.py", line 229, in flush
  File "/home/noon/dev/ProjectQ/projectq/cengines/_main.py", line 214, in receive
  File "/home/noon/dev/ProjectQ/projectq/cengines/_basics.py", line 191, in send
  File "/home/noon/dev/ProjectQ/projectq/cengines/_tagremover.py", line 56, in receive
  File "/home/noon/dev/ProjectQ/projectq/cengines/_basics.py", line 191, in send
  File "/home/noon/dev/ProjectQ/projectq/cengines/_optimize.py", line 232, in receive
  File "/home/noon/dev/ProjectQ/projectq/cengines/_optimize.py", line 136, in _optimize
  File "/home/noon/dev/ProjectQ/projectq/ops/_command.py", line 133, in get_inverse
  File "/home/noon/dev/ProjectQ/projectq/ops/_metagates.py", line 117, in get_inverse
  File "/home/noon/dev/ProjectQ/projectq/ops/_basics.py", line 215, in get_inverse
  File "/home/noon/tools/anaconda3/envs/projectq/lib/python3.5/copy.py", line 174, in deepcopy
  File "<frozen importlib._bootstrap>", line 969, in _find_and_load
  File "<frozen importlib._bootstrap>", line 954, in _find_and_load_unlocked
  File "<frozen importlib._bootstrap>", line 887, in _find_spec
TypeError: 'NoneType' object is not iterable

i don't think this is good. i'm guessing the cleanup code just needs a bit of a clean up :)

Code coverage

With the current settings of coveralls.io, code coverage can decrease if a function is refactored with less lines of code as before, see e.g. Pull request #15. This shouldn't be indicated as a possible failure.

I haven't found a setting in coveralls.io to change to incremental code coverage reports. However, it is possible to check code coverage line by line on their website to check what is going on.

We might want to consider switching to a different tool, e.g. codecov which does handle such cases:
https://docs.codecov.io/docs/codecov-delta

Simulator suggestion: post-select command

When writing a test to reproduce a bug, it is helpful to be able to force particular measurement results. A post-select operation that magically discarded half of the amplitude vector and renormalized the remainder would make this possible.

Qubit type information is lost between allocating the qubit and receiving commands involving it

I want to allow users to allocate qubits, or to ask for qubits at particular positions on a grid. So I have a method qubit_at(self, x, y) in addition to the allocate method, and qubit_at returns a FixedPositionQubit.

But when commands are applied to FixedPositionQubits, those commands don't show up back in the engine with target qubits of type FixedPositionQubit. Instead, commands strip out all the type information and store WeakQubitPtr instances.

Stripping type information like this is not pythonic; it breaks duck typing.

Is there a workaround for this? I could pack all the information into the id, but that feels really over the top.

Empty registers cause exceptions even when applying appropriate multi-qubit gates

There are operations that have a well-defined behavior for any number of target qubits, including 0. For example, an increment operation performs a -> (a+1) % 2**n. When n=0, it reduces to the trivial identity operation 0 -> 0.

But if I define an increment gate and apply it to no qubits, instead of nothing happening I get an exception:

>       return Command(qubits[0][0].engine, self, qubits),
E       IndexError: list index out of range

Of course most people won't trigger this on purpose, but empty cases have a knack of appearing when code interacts with other code and handling them gracefully saves everyone time. For example a decomposition that needs to increment n-k of the qubits in a register should work for k qubits without any issue, but because of this bug the decomp would really only work for k+1 qubits unless a special case was added.

Simulator has global dependence that prevents repetition from working consistently

This do-nothing test:

def test_repetition():
    engine_list = []
    for _ in range(2):
        backend = Simulator()
        eng = MainEngine(backend=backend, engine_list=engine_list)
        eng.allocate_qureg(1)
        _, state = backend.cheat()

Fails with this error:

self = <projectq.backends._sim._pysim.Simulator object at 0x7ff7ff10b5d0>, ID = 0

    def allocate_qubit(self, ID):
        """
            Allocate a qubit.
    
            Args:
                ID (int): ID of the qubit which is being allocated.
            """
        self._map[ID] = self._num_qubits
        self._num_qubits += 1
>       self._state.resize(1 << self._num_qubits)
E       ValueError: cannot resize an array that references or is referenced
E       by another array in this way.  Use the resize function

projectq/backends/_sim/_pysim.py:112: ValueError

I think it has something to do with cache invalidation, because if I use a new empty-engine list for each call then the test runs to completion.

Native compilation causes issues on distributed systems

Compilation of the C++ backend currently has a hard-coded '-march=native' option as part of the setup instructions. This causes problems when a single python environment is used across multiple machines with different hardware; importing ProjectQ will cause immediate kernel death with no error message.

Would it be possible to add a setup flag for this instead of hard-coding?

Project Q

Can anyone Tell me
the uses of this framwork .

Compatibility issues with new pybind11 version 2.2.0

Our installation breaks since yesterday as by default we are using the latest pybind11 which is now v2.2.0.

A workaround for the moment is to install pybind v2.1.1 (pip install pybind==2.1.1) prior to installing ProjectQ until we have fixed the compatibility issues.

The build failure can be seen in #142 (or any other pull request after rebuilding it)

Add more control types. Combine controls with `and`.

These constructs save several boilerplate in the grover example. For example:

def run_grover(eng, n, oracle):
	x = eng.allocate_qureg(n)
	All(H) | x
	
	with Loop(eng, int(math.pi/4.*math.sqrt(1 << n))):
		oracle(eng, x)
		with PlusControl(eng, x):
			eng.PhaseFlip()
	
	Measure | x
	eng.flush()
	
	return [int(qubit) for qubit in x]


def alternating_bits_oracle(eng, qubits):
	with Control(eng, qubits[1::2]) and OffControl(eng, qubits[::2]):
		eng.PhaseFlip()

An OffControl requires that the qubit be |0>. It's equivalent to X-before, Control-during, X-after.

A PlusControl requires that the state be |0>+|1>. It's equivalent to H,X-before, Control-during, X,H-after.

ControlledGate.generate_command returns incorrect results.

C(X).generate_command(q) doesn't return a command equal to what C(X) | q sends to q's engine.

Also, note that the implementation of projectq.ops.ControlledGate.__or__ imports projectq.meta. This is a cyclic dependency. projectq.meta depends on projectq.ops.

TypeError: 'NoneType' object is not subscriptable when using IBM QE

I'm using Python 3.6.0 and the latest source code from master.

In the examples folder, running quantum_random_numbers.py gives
(Note: This is the (slow) Python simulator.)
Measured: 0

Now try to use the IBM backend. Running quantum_random_numbers_ibm.py gives

IBM QE user (e-mail) > [email protected]
IBM QE password > x

I enter the login information, and receive an email message:

Hi [email protected],

Sorry, the results of the execution of your quantum score projectq_experiment failed.

You can see the experiment code accessing to the next link:

https://quantumexperience.ng.bluemix.net/qstage/#/editor?codeId=7f838b4658f61d1d805cb31f584a5552
Thank you in advance for your help making IBMโ€™s quantum presence on the web as exciting and cool as possible,

Sincerely,

the IBM Quantum Team

The link points to a picture of the quantum circuit that looks correct.
After a minute or so, I get the error

Traceback (most recent call last):
File "/Library/Frameworks/Python.framework/Versions/3.6/lib/python3.6/site-packages/projectq/backends/_ibm/_ibm.py", line 232, in _run
data = res['data']['p']
TypeError: 'NoneType' object is not subscriptable

During handling of the above exception, another exception occurred:

Traceback (most recent call last):
File "quantum_random_numbers_ibm.py", line 18, in
eng.flush()
File "/Library/Frameworks/Python.framework/Versions/3.6/lib/python3.6/site-packages/projectq/cengines/_main.py", line 229, in flush
self.receive([Command(self, FlushGate(), ([WeakQubitRef(self, -1)],))])
File "/Library/Frameworks/Python.framework/Versions/3.6/lib/python3.6/site-packages/projectq/cengines/_main.py", line 214, in receive
self.send(command_list)
File "/Library/Frameworks/Python.framework/Versions/3.6/lib/python3.6/site-packages/projectq/cengines/_basics.py", line 191, in send
self.next_engine.receive(command_list)
File "/Library/Frameworks/Python.framework/Versions/3.6/lib/python3.6/site-packages/projectq/cengines/_tagremover.py", line 56, in receive
self.send([cmd])
File "/Library/Frameworks/Python.framework/Versions/3.6/lib/python3.6/site-packages/projectq/cengines/_basics.py", line 191, in send
self.next_engine.receive(command_list)
File "/Library/Frameworks/Python.framework/Versions/3.6/lib/python3.6/site-packages/projectq/cengines/_optimize.py", line 234, in receive
self.send([cmd])
File "/Library/Frameworks/Python.framework/Versions/3.6/lib/python3.6/site-packages/projectq/cengines/_basics.py", line 191, in send
self.next_engine.receive(command_list)
File "/Library/Frameworks/Python.framework/Versions/3.6/lib/python3.6/site-packages/projectq/cengines/_replacer/_replacer.py", line 190, in receive
self.send([cmd])
File "/Library/Frameworks/Python.framework/Versions/3.6/lib/python3.6/site-packages/projectq/cengines/_basics.py", line 191, in send
self.next_engine.receive(command_list)
File "/Library/Frameworks/Python.framework/Versions/3.6/lib/python3.6/site-packages/projectq/cengines/_tagremover.py", line 56, in receive
self.send([cmd])
File "/Library/Frameworks/Python.framework/Versions/3.6/lib/python3.6/site-packages/projectq/cengines/_basics.py", line 191, in send
self.next_engine.receive(command_list)
File "/Library/Frameworks/Python.framework/Versions/3.6/lib/python3.6/site-packages/projectq/cengines/_ibmcnotmapper.py", line 194, in receive
self._run()
File "/Library/Frameworks/Python.framework/Versions/3.6/lib/python3.6/site-packages/projectq/cengines/_ibmcnotmapper.py", line 147, in _run
self.next_engine.receive([cmd])
File "/Library/Frameworks/Python.framework/Versions/3.6/lib/python3.6/site-packages/projectq/cengines/_optimize.py", line 234, in receive
self.send([cmd])
File "/Library/Frameworks/Python.framework/Versions/3.6/lib/python3.6/site-packages/projectq/cengines/_basics.py", line 191, in send
self.next_engine.receive(command_list)
File "/Library/Frameworks/Python.framework/Versions/3.6/lib/python3.6/site-packages/projectq/backends/_ibm/_ibm.py", line 275, in receive
self._run()
File "/Library/Frameworks/Python.framework/Versions/3.6/lib/python3.6/site-packages/projectq/backends/_ibm/_ibm.py", line 262, in _run
raise Exception("Failed to run the circuit. Aborting.")
Exception: Failed to run the circuit. Aborting.

I also found same issue with the pip installed version.

Any advice would be appreciated.

What is the purpose of the atexit in MainEngine?

In MainEngine, there is this line:

        self._delfun = lambda x: x.flush(deallocate_qubits=True)
        atexit.register(self._delfun, self)

Why is this done? It seems like anytime this would get triggered and matter, something very bad must have happened (e.g. printing gate stats without flushing the engine first) and we're about to compound the problem by running more logic on a known-corrupted state.

This line also acts as a de-facto memory leak in python 2. In a long ipython session there will be more and more engines kept alive by this logic. Those engines may have cached transformations or collected stats that use enough memory to matter.

Also it can cause code to run after tests fail, potentially writing debug data unrelated to the failure into the console, swamping out the actual failure reason (e.g. I've noticed test failures usually trigger a wall of warnings about qubits not being deallocated properly).

Tabs being mixed with spaces

Many files in the project have spaces hidden amongst the tabs. Sometimes by accident, sometimes on purpose for fine-grained alignment (despite that not making any sense with tabs present).

Given that PEP8 strongly recommends spaces, we should probably switch to spaces.

BasicQubit hash is inconsistent

The most important condition that hash functions must satisfy is a == b implies hash(a) == hash(b), but the BasicQubit class doesn't meet it:

a = _qubit.BasicQubit(fake_engine, 5)
b = _qubit.BasicQubit(fake_engine, 5)
assert a == b
assert hash(a) == hash(b)

The existing tests are checking for inequality instead of equality, i.e. that a != b implies hash(a) != hash(b). Which is actually not a requirement of hash functions at all, though it matters for performance.

Suggestion: generate commands with assignment-operations on quregisters

Add +=, ^=, etc operators to Qureg:

class Qureg(list):
    ...
    def __iadd__(self, other):
        if isinstance(other, int):
            OffsetGate(other) | self
            return self

        if isinstance(other, Qureg):
            Add | (other, self)
            return self

        if isinstance(other, RValue):
            other.add_into(self)
            return self

        raise NotImplementedError()

    def __ixor__(self, other):
        if isinstance(other, Qureg):
            for dst, src in zip(self, other):
                X & src | dst
            return self

        if isinstance(other, int):
            for i in range(len(self)):
                if (other >> i) & 1:
                    X | self[i]
            return self

        if isinstance(other, RValue):
            other.xor_into(self)
            return self

        raise NotImplementedError()
    ...

Also add RValue-producing +, ~, & etc operators:

class Qureg(list):
    def __invert__(self):
        return RValueNotQureg(self)

    def __neg__(self):
        return RValueNegQureg(self)

    def __and__(self, other):
        return RValueAndQuregs((self, other))

Add RValue classes:

class RValueAndQuregs(RValue):
    def __init__(self, quregs):
        self.quregs = quregs

    def xor_into(self, target_qureg):
        for dst_bit, src_bits in zip(self, zip(*self.quregs)):
            X & src_bits | dst_bit

...

And then users can do arithmetic in a way that looks quite like normal python:

def multiply_accumulate(src_qureg, factor, dst_qureg):
    i = 0
    while factor >= 1 << i:
        if (factor >> i) & 1:
            dst_qureg += src_qureg << i  # Generates command(s)
        i += 1

Support for controllable global phase changes

Currently, the example code in grover.py has this:

	with Control(eng, x[0:-1]):
		Z | x[-1]

But it would be clearer to do something like this:

	with Control(eng, x):
		PhaseFlip | []

Where PhaseFlip is a gate defined like this:

class GlobalPhase(SelfInverseGate):
	def __init__(self, phase_factor):
		super().__init__()
		self.phase_factor = phase_factor

	def __str__(self):
		return str(self.phase_factor)

	@property
	def matrix(self):
		return np.matrix([[self.phase_factor]])

PhaseFlip = GlobalPhase(-1)

This gate is a bit unusual, since it has no targets. That's why I opened this issue; I assume it'll break all kinds of code if I use it. But many algorithms get minor simplifications from the "just change the phase of all states meeting the current controls" operation.

For example, the alternating_bits_oracle has an output parameter. But it really doesn't need one:

def alternating_bits_oracle(eng, qubits):
	with Compute(eng):
		All(X) | qubits[1::2]
	with Control(eng, qubits):
		PhaseFlip | []
	Uncompute(eng)

To implement a global phase gate, you just arbitrarily pick one of the controls and apply a Z rotation to it. If there's no controls to pick from, just do nothing.

IBM Backend not working

I have been trying to execute examples like Entangle by IBM, but failed to do so.
However it is possible to execute quantum_random_numbers_ibm.py provided my username and password.

ibm.py and ibm_entangle.ipynb aren't working.

image

Is this got to do with IBM QE itself or a bug in backend code?

Add a parameter for noting expected decoherence when uncomputing

This would give a workaround for the cases where the uncompute-didn't-work warning is annoying, without tossing the usefulness of the warning.

Not sure what the parameter should be called, though.

with Compute(..., expect_decoherence=True):

with Compute(..., will_trash_ancilla=True):

with Compute(..., ignore_trashed_ancilla_warning=True):

Add support for initializing Simulator to a specific state

This would be useful for testing circuits. Particularly for circuits that happen to be classical, since then a single run of the simulator can be used to test every case by comparing amplitudes in the output vector to the amplitude it should have come from in the input vector.

Suggestion: use & for adding controls to gates

I kind of like being able to write this:

X & control | target

Instead of this:

with Control(control):
    X | target

Here's the code I used to add the functionality.

In BasicGate:

    def __and__(self, other):
        return ControlledGate(self, other)

And here's ControlledGate:

class ControlledGate:
    def __init__(self, gate, control):
        self.control = control
        self.gate = gate

    def __and__(self, other):
        return ControlledGate(self, other)

    def __or__(self, target):
        with Control(target.engine, [self.control]):
            self.gate | target

What do you think?

More convenient wavefunction access

An issue that has come up for a few of us is that it is inconvenient to access the wavefunction in ProjectQ. I am aware that one can call .cheat() to get a dictionary mapping qubit indices in the stored wavefunction to the actual tensor factors in the state vector, together with an array of the amplitudes. From this information it is possible to reconstruct the wavefunction. But it is not intuitive and we don't want users calling a function called ".cheat()" for such important functionality.

I understand why it is unnatural for ProjectQ to provide the entire wavefunction. And I appreciate that you can never "peak" at the entire wavefunction like this when using an actual quantum computer so we are asking more of the ProjectQ simulator than we would ever ask of a real quantum device. But in practice people want to do this. I think it would be great to provide some simple function that gives the user the wavefunction vector in a convenient ordering. This should not be difficult to implement.

Bug: `with Control` escapes coroutines

Consider this code:

def coroutine(eng, q):
    with Control(eng, q[0]):
        yield 1

def driver(eng, q):
    for e in coroutine(eng, q):
        Z | q[1]

The user will expect the Z operation to not be controlled, but it will instead be controlled by q[0].

A more plausible-in-practice repro is something like this:

async def do_work(eng, q):
    with Control(eng, q[0]):
        X | q[1]
        b = await measure_async_result(q[2])
        if b:
            X | q[3]

async def driver(eng, regs):
    r = all_complete(do_work(eng, q) for q in regs)
    eng.flush()
    await r

The resulting behavior will be a giant mess, because each asynchronous method will be leaking controls into the others.

Method to provide C++ simulator with arbitrary initial state

I think this may have been mentioned before but it is now becoming a bottleneck for two different ongoing projects that I am involved with. Ideally, one should be able to provide a vector as input. I realize this isn't something that can be done with an actual quantum computer as the backend but it is something that people expect of a simulator.

Give register sizes to BasicMathGate's function

Some classical operations need to know the size of the register they are applying to. For example: left-rotate, right-rotate, reverse, etc (anything that involves permuting bits).

There are also operations such as 3**(2**k) (mod 2^n) that grow very large very fast if you don't know n.

Engines can't be re-used

An engine is like a compiler pass. It makes no sense that a compiler pass "breaks" after you use it once, so it can't be used again in another compilation call.

Anything and everything about engines that violates the ability to treat them as re-usable pieces should be separated out.

Control's `eng` parameter is redundant (get it from the qubits instead)

with Control(eng, ctrl_qubit):

is basically always equivalent to:

with Control(ctrl_qubit.engine, ctrl_qubit):

I think. Otherwise the code is wrong. So might as well pull out that value inside the control constructor:

with Control(ctrl_qubit):

Of course you need to check for lists, etc.

Test controlled single qubit

Test on the master branch and following files.
controlledU.tar.gz
Running the file controlled_gate_test_HL.py gives the controlled gate for the matrix (X gate) this is the CX gate, this is great. However, if I test this CX gate on the IBM engine I get errors in file controlled_gate_test_LL.py.

I would like to have a test file with input a matrix 2 dim and output an IBM qasm file. Is this possible?

[WIP] Fix Qubit.__del__ causing problems during abnormal shutdown

Based on to the answer to #85, the reason MainEngine.__del__ exists and the reason atexit is used in MainEngine is to avoid issues caused by qubits being deallocated during sudden shutdowns (e.g. user hitting ctrl+C).

There are actually several places in the code doing acrobatics to work around the issue. And anytime a test fails, you tend to see a bunch of extra failures get tacked on due to the qubit deallocations running over code that failed halfway through in a bad state.

For example, here is a failing repro test:

def test_qubit_deallocate_reentrancy_safety():
    class InjectedBugEngine(_basics.BasicEngine):
        def __init__(self):
            _basics.BasicEngine.__init__(self)
            self.was_in_middle_of_receive = False
            self.x = []

        def receive(self, command_list):
            assert not self.was_in_middle_of_receive
            self.was_in_middle_of_receive = True

            for cmd in command_list:
                assert not isinstance(cmd.gate, DeallocateQubitGate)
                if not isinstance(cmd.gate, AllocateQubitGate):
                    self.x[1] = None  # Trigger IndexError

            self.was_in_middle_of_receive = False

    bug_eng = InjectedBugEngine()
    eng = MainEngine(backend=bug_eng, engine_list=[])

    try:
        q = eng.allocate_qubit()[0]
        try:
            H | q  # Triggers injected IndexError bug.
        finally:
            q.__del__()  # Force dealloc of q, as if it went out of scope.
    except IndexError:
        pass  # Expected.

    assert bug_eng.was_in_middle_of_receive

This test fails not once but twice. Both times on the assert not self.was_in_middle_of_receive line. One due to the manual call to q.__del()__, and another in atexit while the interpreter is shutting down.

To fix this problem, we need to make engines re-entrant safe against qubit deallocations. I suggest introducing a defer_deallocate_qubit method that puts the deallocate command into a queue. Then, before each non-reentrant call to receive, the engine drains that queue.

So, during shutdown, the command will go into a queue and just not do anything. But during actual use, the user will call Allocate or Flush or H | something, and the deallocate will appear in the same order as usual. The main change will be that circuits may be missing deallocates at the end, but those deallocates don't matter anyways.

More concretely, I'm going to make all of these fixes (most have to happen at the same time because they depend on each other):

  1. Add an already_deallocated field to Qubit, and use it in __del__ so that it can't re-enter itself anymore.
  2. Move DirtyQubitTag to cengines (but still expose it from meta) so there's no in-method imports needed to create dirty deallocate commands.
  3. Add the above test to _basis_tests
  4. Add a drain_deferreds_and_receive to BasicEngine that drains deferred commands before calling receive.
  5. Change all existing receive calls to either send or drain_deferreds_and_receive.

The reason I'm adding a new method, instead of replacing receive, is that this method is not intended to be overridden. It's a breaking change that should be considered separately.

Probable error in TagRemover

Can't quite figure out what the intended action is, but the write to cmd.tags keeps being overwritten by future iterations in a way that looks suspicious.

	for cmd in command_list:
		for tag in self._tags:
			cmd.tags = [t for t in cmd.tags if not isinstance(t, tag)]
                            ^^^^^^^^^ only last iteration of loop matters
		self.send([cmd])

Suggestion: support __pow__ for rotation gates.

Because pi is transcendental, involving it in the definition of gates forces floating point error even when performing rational fractions of a whole turn.

So, instead of a transcendental angle, I suggest we use a fractional exponent.

Something like this:

class ZPow(BasicRotationGate):
	def __init__(self, exponent):
		BasicGate.__init__(self)
		self.exponent = (exponent + 1) % 2 - 1

	def __str__(self):
		return "Z**(" + str(self.exponent) + ")"

	def tex_str(self):
		return "Z^{" + str(self.exponent) + "}"

	def get_inverse(self):
		return ZPow(-self.exponent)

	def get_merged(self, other):
		if isinstance(other, ZPow):
			return ZPow(self.exponent + other.exponent)
		raise NotMergeable("Can't merge different types of rotation gates.")

	def __eq__(self, other):
		return isinstance(other, ZPow) and other.exponent == self.exponent

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

	@property
	def matrix(self):
                    # Note: expiTau should ensure quarter turns are exact
		return np.matrix([[1, 0], [0, expiTau(self.exponent)]])

Note that passing in a Fraction instead of a float will work fine.

A secondary benefit of this change is that the names of gates become easier to read. I tweaked the Shor example to use ZPow(Fraction(1, 1 << (k - i))) instead of R(-math.pi/(1 << (k - i))), and this is the resource count:

CMultiplyByConstantModN(1, 35) : 11
CMultiplyByConstantModN(6, 35) : 1
Deallocate : 7
H : 24
Measure : 13
X : 11
Z**(1/2) : 1
Z**(1015/1024) : 1
Z**(119/128) : 1
Z**(2039/2048) : 1
Z**(23/32) : 1
Z**(247/256) : 1
Z**(3/4) : 1
Z**(503/512) : 1
Z**(55/64) : 1
Z**(7/16) : 1
Z**(7/8) : 1

Max. width (number of qubits) : 7.
7 * 5 = 35

Which is a lot clearer than this:

...
R(10.9955742876) : 1
R(11.0569335191) : 1
R(11.3882733693) : 1
R(11.8116520667) : 1
R(12.1890113405) : 1
R(9.5474964238) : 1
R(9.67021488683) : 1
R(9.91565181289) : 1
...

(It also makes much nicer latex circuit output.)

Update the IBM cnot mapper & back-end

IBM's QE chip now has a bow tie connectivity graph and we should extend the mapper to be able to use these additional connections.

Something like

  1. Sort qubits by number of interaction partners
  2. Assign the one with the most interactions to the center hardware qubit and distribute the remaining logical qubits according to their interaction with each other (if possible)
  3. If need be, flip around CNOTs using 4 Hadamard gates

If mapping is impossible without swaps: throw exception (for now).

Tooling Suggestion: Turn off coveralls comments

They add notification noise and comment noise. There's already a place for this kind of per-commit tooling information: the check status messages.

Plus most of the existing message is discussing information we already know (i.e. the branches and commits involved in the pull request).

Helper function around simulator's cheat function

The simulator back-end already has a cheat() function to access the wavefunction and hence it is possible to calculate expectation values when using the simulator back-end. But it would be nice to have a few helper functions to make it easier to use and also an example in the tutorials.

Suggestion: LimitedCapabilityEngine

For testing decompositions, it's often useful to prevent AutoReplacer from passing along complicated gates. This functionality could go into DummyEngine, but separating it out keeps DummyEngine simple.

See #54 for the basic idea.

Package installation is not working

When I run the install commands given in the documentation:

python -m pip install --user projectq

or:

sudo apt-get install build-essential python3 python3-pip
sudo pip3 install --user projectq

The install fails with multiple distinct errors:

    [...]
        ImportError: No module named pybind11
    [...]
        Command [...]/bin/python -c "import setuptools, tokenize;[...] failed with error code 1 [...]
    [...]
      File "[...]/pip/basecommand.py", line 161, in main
      UnicodeDecodeError: 'ascii' codec can't decode byte 0xe2 in position 42: ordinal not in range(128)

I think the first two errors are because the documentation says I need to install "build tools" but doesn't actually say what they are or how to install them. So I wasn't able to do that. (I do have g++ installed.) But the third error looks typical of a python2 data-vs-str encoding issue.

Strange behaviour of All

I've noticed the All operation has some pretty strange error checking. This is on line 277 of ProjectQ/projectq/ops/_metagates.py. (Looking at develop for reference.)

There, __or__ tries to apply a gate to multiple qubits, but a bunch of the checks seem unnecessary or lead to failures later on. The first line does some work to check if All acts on a tuple of qubits, in which case it converts the tuple of qubits to the value of the first one, and then raises an AssertionError on line 282. More generally, All(op) | register and All(op) | [register[0], register[1]] are OK, but All(op) | (register[0], register[1]) isn't allowed, and it isn't really clear why it shouldn't be. Finally, I think the block should fail by raising e.g. a ValueError rather than a blank AssertionError.

measurement in arbitrary bases

it would be convenient to allow measurement in an arbitrary basis

at the moment you need to apply some operator that converts from your specific basis to the computational one, then measure, then revert it. it's okay, but not ideal.

Help: measuring performance

I'm having trouble measuring ProjectQ's performance. Something is causing a serious slowdown.

For example, I ran these commands from my terminal:

mkvirtualenv tmp
pip install pybind11
pip install projectq
python speed_test.py

Here are the contents of speed_test.py:

from __future__ import print_function

import time

from projectq.backends import Simulator
from projectq.cengines import MainEngine
from projectq.ops import X, H, Toffoli


def main():
    n = 256

    sim = Simulator()
    eng = MainEngine(backend=sim, engine_list=[])
    qubits = eng.allocate_qureg(3)

    for qubit_count in range(4, 20):
        qubits.append(eng.allocate_qubit())
        t = time.time()
        m = len(qubits)
        for i in range(n):
            a, b, c = qubits[i % m], qubits[(i+1) % m], qubits[(i+2) % m]
            Toffoli | (a, b, c)
            X | a
            H | a
        dt = time.time() - t
        print("{} gates/sec @ {} qubits".format(int(3*n/dt), len(qubits)))


if __name__ == "__main__":
    main()

And got these results:

199 gates/sec @ 4 qubits
192 gates/sec @ 5 qubits
190 gates/sec @ 6 qubits
194 gates/sec @ 7 qubits
183 gates/sec @ 8 qubits
199 gates/sec @ 9 qubits
198 gates/sec @ 10 qubits
200 gates/sec @ 11 qubits
206 gates/sec @ 12 qubits
193 gates/sec @ 13 qubits
195 gates/sec @ 14 qubits
183 gates/sec @ 15 qubits
195 gates/sec @ 16 qubits
190 gates/sec @ 17 qubits
196 gates/sec @ 18 qubits
184 gates/sec @ 19 qubits
Exception RuntimeError: 'Error: Qubit has not been measured / uncomputed! [...]
[...]

Those rates are terrible. I get higher performance with the python simulator up to 14 qubits:

(Note: This is the (slow) Python simulator.)
18598 gates/sec @ 4 qubits
16701 gates/sec @ 5 qubits
13797 gates/sec @ 6 qubits
9966 gates/sec @ 7 qubits
6365 gates/sec @ 8 qubits
3621 gates/sec @ 9 qubits
1983 gates/sec @ 10 qubits
1038 gates/sec @ 11 qubits
532 gates/sec @ 12 qubits
264 gates/sec @ 13 qubits
135 gates/sec @ 14 qubits
68 gates/sec @ 15 qubits
[...]

Last month when I speed-tested projectq, it was getting numbers similar to Quirk: 8000 gates/sec at 16 qubits. I'm not sure what would have changed in the meantime, but performance seems to have dropped by 50x.

I have confirmed in my own debugging that the line self._simulator.apply_controlled_gate seems to be the big offender, but I haven't figured out much more than that.

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.