Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Pr controlled #160

Merged
merged 11 commits into from
Jul 19, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 3 additions & 3 deletions requirements.txt
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,8 @@ scipy >= 1.5 # can in principle be smaller, we recommend >= 1.5 for consistency
sympy
jax
kottmanj marked this conversation as resolved.
Show resolved Hide resolved
jaxlib
#autograd # use if jaxlib gives you trouble (e.g. on Windows), make sure jax and jaxlib are uninstalled and install autograd
setuptools
# autograd # use if jaxlib gives you trouble (e.g. on Windows), make sure jax and jaxlib are uninstalled and install autograd
setuptools
pytest
openfermion >= 1.0*
cmake # needed by qulacs, can be removed otherwise
Expand All @@ -19,7 +19,7 @@ qulacs # default simulator (best integration), remove if the installation gives
#qibo <= 0.1.1 # can not be installed in the same environment as gpyopt

#optional optimizers
#phoenics # version on PyPi isc urrently broken, we recommend to install from source (AAG github)
#phoenics # version on PyPi isc urrently broken, we recommend to install from source (AAG github)
#gpyopt # not in combination with qibo as quantum backend

#optional third party libraries
Expand Down
127 changes: 114 additions & 13 deletions src/tequila/circuit/circuit.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
from tequila.circuit._gates_impl import QGateImpl, assign_variable
from __future__ import annotations
from tequila.circuit._gates_impl import QGateImpl, assign_variable, list_assignment
from tequila import TequilaException, TequilaWarning
from tequila import BitNumbering
import typing, copy
import typing
import copy
from collections import defaultdict
import warnings

Expand Down Expand Up @@ -158,7 +160,8 @@ def n_qubits(self, other):
self._min_n_qubits = other
if other < self.max_qubit() + 1:
raise TequilaException(
"You are trying to set n_qubits to " + str(other) + " but your circuit needs at least: " + str(
"You are trying to set n_qubits to " + str(
other) + " but your circuit needs at least: " + str(
self.max_qubit() + 1))
return self

Expand Down Expand Up @@ -453,7 +456,7 @@ def to_networkx(self):
control = int(cstr)
Gdict[p].append(control) # add control to key of correlated targets
else:
for s in gate.target:
for s in gate.target:
for t in gate.target:
tstr2 = ''
tstr2 += str(t)
Expand Down Expand Up @@ -521,6 +524,97 @@ def map_qubits(self, qubit_map):
# currently its recreated in the init function
return QCircuit(gates=new_gates)

def add_controls(self, control, inpl: typing.Optional[bool] = True) \
-> typing.Optional[QCircuit]:
"""Depending on the truth value of inpl:
- return controlled version of self with control as the control qubits if inpl;
- mutate self so that the qubits in control are added as the control qubits if not inpl.

Raise ValueError if there any qubits in common between self and control.
"""
if inpl:
self._inpl_control_circ(control)
else:
# return self._return_control_circ(control)
circ = copy.deepcopy(self)
return circ.add_controls(control, inpl=True)

def _return_control_circ(self, control) -> QCircuit:
"""Return controlled version of self with control as the control qubits.

This is not an in-place method, so it DOES NOT mutates self, and only returns a circuit.

Raise TequilaWarning if there any qubits in common between self and control.
"""
control = list(set(list_assignment(control)))

gates = self.gates
cgates = []

for gate in gates:
cgate = copy.deepcopy(gate)

if cgate.is_controlled():
control_lst = list(set(list(cgate.control) + list(control)))

if len(control_lst) < len(gate.control) + len(control):
# warnings.warn("Some of the controls {} were already included in the control "
# "of a gate {}.".format(control, gate), TequilaWarning)
raise TequilaWarning(f'Some of the controls {control} were already included '
f'in the control of a gate {gate}.')
else:
control_lst, not_control = list(control), list()

# Raise TequilaWarning if target and control are the same qubit
if any(qubit in control for qubit in not_control):
# warnings.warn("The target and control {} were the same qubit for a gate {}."
# .format(control, gate), TequilaWarning)
raise TequilaWarning(f'The target for a gate {gate} '
f'and the control list {control_lst} had a common qubit.')

cgate._control = tuple(control_lst)
cgate.finalize()
cgates.append(cgate)

return QCircuit(gates=cgates)

def _inpl_control_circ(self, control) -> None:
"""Mutate self such that the qubits in control are added as the control qubits

This is an in-place method, so it mutates self and doesn't return any value.

Raise TequilaWarning if there any qubits in common between self and control.
"""
gates = self.gates
control = list_assignment(control)

for gate in gates:
if gate.is_controlled():
control_lst = list(set(list(gate.control) + list(control)))

# Need to check duplicates
not_control = list(set(qubit for qubit in list(gate.qubits)
if qubit not in list(gate.control)))

# Raise TequilaWarning if control qubit is duplicated
if len(control_lst) < len(gate.control) + len(control):
# warnings.warn("Some of the controls {} were already included in the control "
# "of a gate {}.".format(control, gate), TequilaWarning)
raise TequilaWarning(f'Some of the controls {control} were already included '
f'in the control of a gate {gate}.')
else:
control_lst, not_control = list(control), list()

# Raise TequilaWarning if target and control are the same qubit
if any(qubit in control for qubit in not_control):
# warnings.warn("The target and control {} were the same qubit for a gate {}."
# .format(control, gate), TequilaWarning)
raise TequilaWarning(f'The target for a gate {gate} '
f'and the control list {control} had a common qubit.')

gate._control = tuple(control_lst)
gate.finalize()

def map_variables(self, variables: dict, *args, **kwargs):
"""

Expand All @@ -534,20 +628,22 @@ def map_variables(self, variables: dict, *args, **kwargs):

"""

variables = {assign_variable(k):assign_variable(v) for k,v in variables.items()}
variables = {assign_variable(k): assign_variable(v) for k, v in variables.items()}

# failsafe
my_variables = self.extract_variables()
for k,v in variables.items():
for k, v in variables.items():
if k not in my_variables:
warnings.warn("map_variables: variable {} is not part of circuit with variables {}".format(k,my_variables), TequilaWarning)
warnings.warn(
"map_variables: variable {} is not part of circuit with variables {}".format(k,
my_variables),
TequilaWarning)

new_gates = [copy.deepcopy(gate).map_variables(variables) for gate in self.gates]

return QCircuit(gates=new_gates)



class Moment(QCircuit):
"""
the class which represents a set of simultaneously applicable gates.
Expand Down Expand Up @@ -614,7 +710,8 @@ def __init__(self, gates: typing.List[QGateImpl] = None, sort=False):
for q in g.qubits:
if q in occ:
raise TequilaException(
'cannot have doubly occupied qubits, which occurred at qubit {}'.format(str(q)))
'cannot have doubly occupied qubits, which occurred at qubit {}'.format(
str(q)))
else:
occ.append(q)
if sort:
Expand Down Expand Up @@ -675,7 +772,8 @@ def with_gates(self, gates):
if q not in first_overlap:
first_overlap.append(q)
else:
raise TequilaException('cannot have a moment with multiple operations acting on the same qubit!')
raise TequilaException(
'cannot have a moment with multiple operations acting on the same qubit!')

new = self.with_gate(gl[0])
for g in gl[1:]:
Expand All @@ -702,7 +800,8 @@ def add_gate(self, gate: typing.Union[QCircuit, QGateImpl]):
for n in newq:
if n in prev:
raise TequilaException(
'cannot add gate {} to moment; qubit {} already occupied.'.format(str(gate), str(n)))
'cannot add gate {} to moment; qubit {} already occupied.'.format(str(gate),
str(n)))

self._gates.append(gate)
self.sort_gates()
Expand Down Expand Up @@ -811,7 +910,8 @@ def __add__(self, other):
result._min_n_qubits += len(other.qubits)
except:
result = self.as_circuit() + QCircuit.wrap_gate(other)
result._min_n_qubits = max(self.as_circuit()._min_n_qubits, QCircuit.wrap_gate(other)._min_n_qubits)
result._min_n_qubits = max(self.as_circuit()._min_n_qubits,
QCircuit.wrap_gate(other)._min_n_qubits)

else:
if isinstance(other, QGateImpl):
Expand All @@ -820,7 +920,8 @@ def __add__(self, other):
result._min_n_qubits += len(other.qubits)
except:
result = self.as_circuit() + QCircuit.wrap_gate(other)
result._min_n_qubits = max(self.as_circuit()._min_n_qubits, QCircuit.wrap_gate(other)._min_n_qubits)
result._min_n_qubits = max(self.as_circuit()._min_n_qubits,
QCircuit.wrap_gate(other)._min_n_qubits)
else:
raise TequilaException(
'cannot add moments to types other than QCircuit,Moment,or Gate; recieved summand of type {}'.format(
Expand Down
8 changes: 5 additions & 3 deletions src/tequila/wavefunction/qubit_wavefunction.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
from __future__ import annotations
import copy
import typing
from typing import Dict, Union
import numpy
import numbers

Expand Down Expand Up @@ -64,11 +66,11 @@ def state(self):
return self._state

@state.setter
def state(self, other: typing.Dict[BitString, complex]):
def state(self, other: Dict[BitString, complex]):
assert (isinstance(other, dict))
self._state = other

def __init__(self, state: typing.Dict[BitString, complex] = None, n_qubits=None):
def __init__(self, state: Dict[BitString, complex] = None, n_qubits=None):
if state is None:
self._state = dict()
elif isinstance(state, int):
Expand All @@ -93,7 +95,7 @@ def values(self):
return self.state.values()

@staticmethod
def convert_bitstring(key: typing.Union[BitString, numbers.Integral], n_qubits):
def convert_bitstring(key: Union[BitString, numbers.Integral], n_qubits):
if isinstance(key, numbers.Integral):
return BitString.from_int(integer=key, nbits=n_qubits)
elif isinstance(key, str):
Expand Down
33 changes: 27 additions & 6 deletions tests/test_circuits.py
Original file line number Diff line number Diff line change
@@ -1,12 +1,15 @@
from tequila.circuit.gates import X, Y, Z, Rx, Ry, Rz, H, CNOT, QCircuit, RotationGate, Phase, ExpPauli, Trotterized, \
U, u1, u2, u3, S, T, SWAP
from tequila.wavefunction.qubit_wavefunction import QubitWaveFunction
from random import randint

import numpy
import pytest
import sympy
from tequila import assign_variable, paulis, TequilaWarning
from tequila.circuit._gates_impl import RotationGateImpl
from tequila.circuit.gates import CNOT, ExpPauli, H, Phase, QCircuit, RotationGate, Rx, Ry, Rz, S, \
SWAP, T, Trotterized, u1, u2, u3, X, Y, Z
from tequila.objective.objective import Variable
from tequila.simulators.simulator_api import simulate
from tequila import assign_variable, paulis
import numpy, sympy
import pytest
from tequila.wavefunction.qubit_wavefunction import QubitWaveFunction


def test_qubit_map():
Expand Down Expand Up @@ -306,6 +309,24 @@ def test_variable_map():
assert sorted([str(x) for x in U2.extract_variables()]) == sorted([str(x) for x in list(variables.values())])


def test_in_place_control() -> None:
"""Test whether the in place version of controlled_unitary works as expected."""
circ = X(randint(0, 10)) + CNOT(control=randint(1, 10), target=0)
length = randint(1, 10)
anc = list(set([randint(11, 20) for _ in range(length)]))

circ._inpl_control_circ(anc)

for gate in circ.gates:
assert gate.is_controlled() and all(qubit in gate.control for qubit in anc)


def test_control_exception() -> None:
"""Test whether the TequilaWarning is raised as intended."""

with pytest.raises(TequilaWarning):
circ = X(0) + CNOT(control=1, target=0)
length = randint(1, 10)
anc = [1 for _ in range(length)]

circ._inpl_control_circ(anc)