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

Name-ordered circuit parameters #5759

Merged
merged 54 commits into from
Mar 2, 2021
Merged
Show file tree
Hide file tree
Changes from 51 commits
Commits
Show all changes
54 commits
Select commit Hold shift + click to select a range
9ae2fb9
change unique list to MappingView
Cryoris Dec 2, 2020
86e4e90
Merge branch 'master' into sorted-circuit-parameters
Cryoris Dec 2, 2020
6c5962a
fix test
Cryoris Dec 2, 2020
51ebc78
start adding tests
Cryoris Dec 2, 2020
3e87ec4
handle anonymous parameter assigment
ewinston Jan 13, 2021
2a901ed
fix bind_parameters
ewinston Jan 13, 2021
26e30f2
Merge branch 'sorted-circuit-parameters' into issue/5557/assign_list
ewinston Jan 14, 2021
9e5bb10
Merge branch 'master' into sorted-circuit-parameters
Cryoris Jan 21, 2021
f85df15
Merge branch 'master' into sorted-circuit-parameters
Cryoris Jan 27, 2021
0c6580f
Merge branch 'sorted-circuit-parameters' into issue/5557/assign_list
Cryoris Jan 27, 2021
fce5872
fix compose
Cryoris Jan 29, 2021
082297e
add reno, remove todos
Cryoris Feb 1, 2021
670c947
Merge branch 'master' into sorted-circuit-parameters
Cryoris Feb 1, 2021
b66964d
fix docstring
Cryoris Feb 1, 2021
3ecd42a
Merge branch 'sorted-circuit-parameters' of github.com:Cryoris/qiskit…
Cryoris Feb 1, 2021
b791c5b
fix inconsistent argname
Cryoris Feb 1, 2021
b6e41e9
fix renaming gone wrong
Cryoris Feb 2, 2021
44ab651
changes from code review
Cryoris Feb 2, 2021
a5b4a06
Merge branch 'master' into sorted-circuit-parameters
Cryoris Feb 2, 2021
e5db1b3
fix naming in reno
Cryoris Feb 3, 2021
e775265
make compose(front) more efficient
Cryoris Feb 3, 2021
63ed6e1
Merge branch 'sorted-circuit-parameters' of github.com:Cryoris/qiskit…
Cryoris Feb 3, 2021
9016715
update paramtables in compose w/o iterating over all gates
Cryoris Feb 3, 2021
0360063
Merge branch 'master' into sorted-circuit-parameters
Cryoris Feb 3, 2021
44b2f55
properly rebuild parameterable upon compose
Cryoris Feb 3, 2021
5579937
Merge branch 'master' into sorted-circuit-parameters
Cryoris Feb 3, 2021
5b41291
use params instead of param_dict
Cryoris Feb 3, 2021
c2b81fb
Merge branch 'master' into sorted-circuit-parameters
Cryoris Feb 4, 2021
bbe14ee
Merge branch 'master' into sorted-circuit-parameters
Cryoris Feb 17, 2021
27813bd
fix __new__
Cryoris Feb 18, 2021
5c0cccc
fix sorting
Cryoris Feb 18, 2021
0f0680f
fix tests
Cryoris Feb 18, 2021
7ac2950
fix parameter order in test
Cryoris Feb 18, 2021
48275f1
Merge branch 'sorted-circuit-parameters' of github.com:Cryoris/qiskit…
Cryoris Feb 18, 2021
34cf7f0
Merge branch 'master' into sorted-circuit-parameters
Cryoris Feb 18, 2021
f15fc12
Merge branch 'master' into sorted-circuit-parameters
Cryoris Feb 18, 2021
512759e
Merge branch 'master' into sorted-circuit-parameters
Cryoris Feb 19, 2021
456a4ca
Apply suggestions from code review
Cryoris Feb 22, 2021
42e7a0a
Merge branch 'master' into sorted-circuit-parameters
Cryoris Feb 22, 2021
a6fdf57
rename params->parameters, update reno
Cryoris Feb 22, 2021
c533b99
Merge branch 'sorted-circuit-parameters' of github.com:Cryoris/qiskit…
Cryoris Feb 22, 2021
bb768a9
add tests, remove access table by index
Cryoris Feb 23, 2021
7a4b0f5
use ListEqual over Equal
Cryoris Feb 23, 2021
a5b06dd
rm unused islice import
Cryoris Feb 23, 2021
7f76114
Rm star-swallowers from bind/assign_parameters
Cryoris Feb 23, 2021
721b4d3
store parent instance instead of just name
Cryoris Feb 26, 2021
ff9cdf9
Merge branch 'master' into sorted-circuit-parameters
Cryoris Feb 26, 2021
f93cfd1
fix name `params` -> `parameters`
Cryoris Feb 26, 2021
253942d
Merge branch 'sorted-circuit-parameters' of github.com:Cryoris/qiskit…
Cryoris Feb 26, 2021
bba4b64
fix typo
Cryoris Feb 26, 2021
5681083
fix typo #2
Cryoris Feb 26, 2021
266d9ed
Merge branch 'master' into sorted-circuit-parameters
Cryoris Feb 27, 2021
78f67e4
Merge branch 'master' into sorted-circuit-parameters
Cryoris Mar 1, 2021
176b206
add test for pickling paramvecelements
Cryoris Mar 1, 2021
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
30 changes: 17 additions & 13 deletions qiskit/circuit/library/n_local/n_local.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
from qiskit.circuit.quantumregister import QuantumRegister
from qiskit.circuit import Instruction, Parameter, ParameterVector, ParameterExpression
from qiskit.circuit.parametertable import ParameterTable
from qiskit.utils.deprecation import deprecate_arguments

from ..blueprintcircuit import BlueprintCircuit

Expand Down Expand Up @@ -736,9 +737,12 @@ def add_layer(self,

return self

def assign_parameters(self, param_dict: Union[dict, List[float], List[Parameter],
@deprecate_arguments({'param_dict': 'parameters'})
def assign_parameters(self, parameters: Union[dict, List[float], List[Parameter],
ParameterVector],
inplace: bool = False) -> Optional[QuantumCircuit]:
inplace: bool = False,
param_dict: Optional[dict] = None # pylint: disable=unused-argument
) -> Optional[QuantumCircuit]:
"""Assign parameters to the n-local circuit.

This method also supports passing a list instead of a dictionary. If a list
Expand All @@ -756,31 +760,31 @@ def assign_parameters(self, param_dict: Union[dict, List[float], List[Parameter]
if self._data is None:
self._build()

if not isinstance(param_dict, dict):
if len(param_dict) != self.num_parameters:
if not isinstance(parameters, dict):
if len(parameters) != self.num_parameters:
raise AttributeError('If the parameters are provided as list, the size must match '
'the number of parameters ({}), but {} are given.'.format(
self.num_parameters, len(param_dict)
self.num_parameters, len(parameters)
))
unbound_params = [param for param in self._ordered_parameters if
isinstance(param, ParameterExpression)]
unbound_parameters = [param for param in self._ordered_parameters if
isinstance(param, ParameterExpression)]

# to get a sorted list of unique parameters, keep track of the already used parameters
# in a set and add the parameters to the unique list only if not existing in the set
used = set()
unbound_unique_params = []
for param in unbound_params:
unbound_unique_parameters = []
for param in unbound_parameters:
if param not in used:
unbound_unique_params.append(param)
unbound_unique_parameters.append(param)
used.add(param)

param_dict = dict(zip(unbound_unique_params, param_dict))
parameters = dict(zip(unbound_unique_parameters, parameters))

if inplace:
new = [param_dict.get(param, param) for param in self.ordered_parameters]
new = [parameters.get(param, param) for param in self.ordered_parameters]
self._ordered_parameters = new

return super().assign_parameters(param_dict, inplace=inplace)
return super().assign_parameters(parameters, inplace=inplace)

def _parameterize_block(self, block, param_iter=None, rep_num=None, block_num=None,
indices=None, params=None):
Expand Down
209 changes: 208 additions & 1 deletion qiskit/circuit/parametertable.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,9 @@
"""
Look-up table for variable parameters in QuantumCircuit.
"""
from collections.abc import MutableMapping
import warnings
import functools
from collections.abc import MutableMapping, MappingView

from .instruction import Instruction

Expand Down Expand Up @@ -79,3 +81,208 @@ def __len__(self):

def __repr__(self):
return 'ParameterTable({})'.format(repr(self._table))


def _deprecated_set_method():
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this different to qiskit.utils.deprecation.deprecate_function?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It allows to print the method name in without having to hardcode it in the deprecation call. E.g. a solution using deprecate_function could be

def deprecation_msg(funcname):
    return f'The ParameterView.{funcname} method is deprecated as of '
            'Qiskit Terra 0.17.0 and will be removed no sooner than 3 months '
            'after the release date. Circuit parameters are returned as View '
            'object, not set. To use set methods you can explicitly cast to a '
            'set.'

# inside ParameterView
@deprecate_function(deprecation_msg('ParameterView.difference'))
def difference(...)

I can change it to that if you prefer, I was just being lazy and added a quick new method instead of having to type out the name of every method I want to deprecated 😉

def deprecate(func):
@functools.wraps(func)
def wrapper(*args, **kwargs):
# warn only once
if not wrapper._warned:
warnings.warn(f'The ParameterView.{func.__name__} method is deprecated as of '
'Qiskit Terra 0.17.0 and will be removed no sooner than 3 months '
'after the release date. Circuit parameters are returned as View '
'object, not set. To use set methods you can explicitly cast to a '
'set.', DeprecationWarning, stacklevel=2)
wrapper._warned = True
return func(*args, **kwargs)
wrapper._warned = False
return wrapper
return deprecate


class ParameterView(MappingView):
"""Temporary class to transition from a set return-type to list.

Derives from a list but implements all set methods, but all set-methods emit deprecation
warnings.
"""

def __init__(self, iterable=None):
if iterable is not None:
self.data = list(iterable)
else:
self.data = []

super().__init__(self.data)

@_deprecated_set_method()
def add(self, x):
"""Add a new element."""
if x not in self.data:
self.data.append(x)

def copy(self):
"""Copy the ParameterView."""
return self.__class__(self.data.copy())

@_deprecated_set_method()
def difference(self, *s):
"""Get the difference between self and the input."""
return self.__sub__(s)

@_deprecated_set_method()
def difference_update(self, *s):
"""Get the difference between self and the input in-place."""
for element in self:
if element in s:
self.remove(element)

@_deprecated_set_method()
def discard(self, x):
"""Remove an element from self."""
if x in self:
self.remove(x)

@_deprecated_set_method()
def intersection(self, *x):
"""Get the intersection between self and the input."""
return self.__and__(x)

@_deprecated_set_method()
def intersection_update(self, *x):
"""Get the intersection between self and the input in-place."""
return self.__iand__(x)

def isdisjoint(self, x):
"""Check whether self and the input are disjoint."""
return not any(element in self for element in x)

@_deprecated_set_method()
def issubset(self, x):
"""Check whether self is a subset of the input."""
return self.__le__(x)

@_deprecated_set_method()
def issuperset(self, x):
"""Check whether self is a superset of the input."""
return self.__ge__(x)

@_deprecated_set_method()
def symmetric_difference(self, x):
"""Get the symmetric difference of self and the input."""
return self.__xor__(x)

@_deprecated_set_method()
def symmetric_difference_update(self, x):
"""Get the symmetric difference of self and the input in-place."""
backward = x.difference(self)
self.difference_update(x)
self.update(backward)

@_deprecated_set_method()
def union(self, *x):
"""Get the union of self and the input."""
return self.__or__(x)

@_deprecated_set_method()
def update(self, *x):
"""Update self with the input."""
for element in x:
self.add(element)

def remove(self, x):
"""Remove an existing element from the view."""
self.data.remove(x)

def __repr__(self):
"""Format the class as string."""
return f'ParameterView({self.data})'

def __getitem__(self, index):
"""Get items."""
return self.data[index]

def __and__(self, x):
"""Get the intersection between self and the input."""
inter = []
for element in self:
if element in x:
inter.append(element)

return self.__class__(inter)

def __rand__(self, x):
"""Get the intersection between self and the input."""
return self.__and__(x)

def __iand__(self, x):
"""Get the intersection between self and the input in-place."""
for element in self:
if element not in x:
self.remove(element)
return self

def __len__(self):
"""Get the length."""
return len(self.data)

def __or__(self, x):
"""Get the union of self and the input."""
return set(self) | set(x)

def __ior__(self, x):
"""Update self with the input."""
self.update(*x)
return self

def __sub__(self, x):
"""Get the difference between self and the input."""
return set(self) - set(x)

@_deprecated_set_method()
def __isub__(self, x):
"""Get the difference between self and the input in-place."""
return self.difference_update(*x)

def __xor__(self, x):
"""Get the symmetric difference between self and the input."""
return set(self) ^ set(x)

@_deprecated_set_method()
def __ixor__(self, x):
"""Get the symmetric difference between self and the input in-place."""
self.symmetric_difference_update(x)
return self

def __ne__(self, other):
return set(other) != set(self)

def __eq__(self, other):
return set(other) == set(self)

def __le__(self, x):
return all(element in x for element in self)

def __lt__(self, x):
if x != self:
return self <= x
return False

def __ge__(self, x):
return all(element in self for element in x)

def __gt__(self, x):
if x != self:
return self >= x
return False

def __iter__(self):
return iter(self.data)

def __contains__(self, x):
return x in self.data

__hash__: None # type: ignore
__rand__ = __and__
__ror__ = __or__
40 changes: 38 additions & 2 deletions qiskit/circuit/parametervector.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,9 +12,45 @@

"""Parameter Vector Class to simplify management of parameter lists."""

from uuid import uuid4

from .parameter import Parameter


class ParameterVectorElement(Parameter):
"""An element of a ParameterVector."""

def __new__(cls, vector, index, uuid=None): # pylint:disable=unused-argument
Cryoris marked this conversation as resolved.
Show resolved Hide resolved
obj = object.__new__(cls)

if uuid is None:
obj._uuid = uuid4()
else:
obj._uuid = uuid

obj._hash = hash(obj._uuid)
return obj

def __getnewargs__(self):
return (self.vector, self.index, self._uuid)

def __init__(self, vector, index):
name = f'{vector.name}[{index}]'
super().__init__(name)
self._vector = vector
self._index = index

@property
def index(self):
"""Get the index of this element in the parent vector."""
return self._index

@property
def vector(self):
"""Get the parent vector instance."""
return self._vector


class ParameterVector:
"""ParameterVector class to quickly generate lists of parameters."""

Expand All @@ -23,7 +59,7 @@ def __init__(self, name, length=0):
self._params = []
self._size = length
for i in range(length):
self._params += [Parameter('{}[{}]'.format(self._name, i))]
self._params += [ParameterVectorElement(self, i)]

@property
def name(self):
Expand Down Expand Up @@ -69,5 +105,5 @@ def resize(self, length):
"""
if length > len(self._params):
for i in range(len(self._params), length):
self._params += [Parameter('{}[{}]'.format(self._name, i))]
self._params += [ParameterVectorElement(self, i)]
self._size = length
Loading