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

Change Parameter equality from python id() to internal uuid(). #2947

Merged
merged 4 commits into from
Aug 14, 2019
Merged
Show file tree
Hide file tree
Changes from 2 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
3 changes: 3 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -145,6 +145,9 @@ The format is based on [Keep a Changelog].
- Correctly serialize complex numbers with a nonzero real part
- Fixed bug in measure sampling for `BasicAer` Qasm simulator if only a
subset of qubits are measured (\#2790)
- Parameter objects can be serialized and communicated between
sub-processes (\#2429)
- Parameterized circuits no longer need to be transpiled individually (\#2864)


## [0.8.2] - 2019-06-14
Expand Down
29 changes: 29 additions & 0 deletions qiskit/circuit/parameter.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,13 +15,36 @@
Parameter Class for variable parameters.
"""

from uuid import uuid4

import sympy

from .parameterexpression import ParameterExpression


class Parameter(ParameterExpression):
"""Parameter Class for variable parameters"""

def __new__(cls, _, uuid=None):
# Parameter relies on self._uuid being set prior to other attributes
# (e.g. symbol_map) which may depend on self._uuid for Parameter's hash
# or __eq__ functions.

obj = object.__new__(cls)

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

return obj

def __getnewargs__(self):
# Unpickling won't in general call __init__ but will always call
# __new__. Specify arguments to be passed to __new__ when unpickling.

return (self.name, self._uuid)

def __init__(self, name):
self._name = name

Expand All @@ -48,3 +71,9 @@ def __deepcopy__(self, memo=None):

def __repr__(self):
return '{}({})'.format(self.__class__.__name__, self.name)

def __eq__(self, other):
return isinstance(other, Parameter) and self._uuid == other._uuid

def __hash__(self):
return hash(self._uuid)
84 changes: 82 additions & 2 deletions test/python/circuit/test_parameters.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,16 +14,18 @@

"""Test circuits with variable parameters."""

import pickle
from operator import add, sub, mul, truediv

import numpy

from qiskit import BasicAer
from qiskit import QuantumRegister, ClassicalRegister, QuantumCircuit
from qiskit.circuit import Gate, Parameter, ParameterVector, ParameterExpression
from qiskit.compiler import transpile
from qiskit.compiler import assemble
from qiskit.compiler import assemble, transpile
from qiskit.execute import execute
from qiskit.test import QiskitTestCase
from qiskit.tools import parallel_map
from qiskit.exceptions import QiskitError


Expand Down Expand Up @@ -296,6 +298,84 @@ def test_instruction_ryrz_vector(self):
for param in vec:
self.assertIn(param, qc_aer.parameters)

def test_parameter_equality_through_serialization(self):
"""Verify parameters maintain their equality after serialization."""

# pylint: disable=invalid-name
x = Parameter('x')
x1 = Parameter('x')

x_p = pickle.loads(pickle.dumps(x))
x1_p = pickle.loads(pickle.dumps(x1))

self.assertEqual(x, x_p)
self.assertEqual(x1, x1_p)

self.assertNotEqual(x, x1_p)
self.assertNotEqual(x1, x_p)

def test_binding_parameterized_circuits_built_in_multiproc(self):
"""Verify subcircuits built in a subprocess can still be bound."""
# ref: https://github.com/Qiskit/qiskit-terra/issues/2429

num_processes = 4

qr = QuantumRegister(3)
cr = ClassicalRegister(3)

circuit = QuantumCircuit(qr, cr)
parameters = [Parameter('x{}'.format(i))
for i in range(num_processes)]

results = parallel_map(_construct_circuit,
[(param) for param in parameters],
task_args=(qr,),
num_processes=num_processes)

for qc in results:
circuit += qc

parameter_values = [{x: 1 for x in parameters}]

qobj = assemble(circuit,
backend=BasicAer.get_backend('qasm_simulator'),
parameter_binds=parameter_values)

self.assertEqual(len(qobj.experiments), 1)
self.assertEqual(len(qobj.experiments[0].instructions), 4)
self.assertTrue(all(len(inst.params) == 1
and isinstance(inst.params[0], ParameterExpression)
and float(inst.params[0]) == 1
for inst in qobj.experiments[0].instructions))

def test_transpiling_multiple_parameterized_circuits(self):
"""Verify several parameterized circuits can be transpiled at once."""
# ref: https://github.com/Qiskit/qiskit-terra/issues/2864

qr = QuantumRegister(1)
qc1 = QuantumCircuit(qr)
qc2 = QuantumCircuit(qr)

theta = Parameter('theta')

qc1.u3(theta, 0, 0, qr[0])
qc2.u3(theta, 3.14, 0, qr[0])

circuits = [qc1, qc2]

job = execute(circuits,
BasicAer.get_backend('unitary_simulator'),
shots=512,
parameter_binds=[{theta: 1}])

self.assertTrue(len(job.result().results), 2)


def _construct_circuit(param, qr):
qc = QuantumCircuit(qr)
qc.ry(param, qr[0])
return qc


class TestParameterExpressions(QiskitTestCase):
"""Test expressions of Parameters."""
Expand Down