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

Can not bind parameter a circuit if a circuit is built by multiprocesses #2429

Closed
chunfuchen opened this issue May 15, 2019 · 5 comments · Fixed by #2947
Closed

Can not bind parameter a circuit if a circuit is built by multiprocesses #2429

chunfuchen opened this issue May 15, 2019 · 5 comments · Fixed by #2947
Labels
bug Something isn't working status: pending PR It has one or more PRs pending to solve this issue

Comments

@chunfuchen
Copy link
Contributor

Information

  • Qiskit Terra version: the newest master
  • Python version: 3.7
  • Operating system: macOS 10.13

What is the current behavior?

Can not bind parameter a circuit if a circuit is built by multi-processes, an error is raised.

    299                 ('Mismatch between run_config.parameter_binds and all circuit parameters. ' +
    300                  'Parameter binds: {} ' +
--> 301                  'Circuit parameters: {}').format(all_bind_parameters, all_circuit_parameters))
    302 
    303         circuits = [circuit.bind_parameters(binds)

QiskitError: 'Mismatch between run_config.parameter_binds and all circuit parameters. Parameter binds: [dict_keys([Parameter(x0), Parameter(x1), Parameter(x2), Parameter(x3)])] Circuit parameters: [{Parameter(x2), Parameter(x3), Parameter(x1), Parameter(x0)}]'

Steps to reproduce the problem

scripts

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

from qiskit import QuantumCircuit, QuantumRegister, ClassicalRegister, BasicAer
from qiskit.circuit import Parameter
from qiskit.compiler import transpile, assemble
from qiskit.tools import parallel_map

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
    
circuit = transpile(circuit)
backend = BasicAer.get_backend("qasm_simulator")
parameter_values = [{x: 1 for x in parameters}]

print(parameter_values)
qobj = assemble(circuit, backend=backend, parameter_binds=parameter_values)

What is the expected behavior?

Bind parameters without error.

Suggested solutions

I think it fails to bind the parameters because the identity (get through id() method) of a parameter is changed during the parallelization (the parameter is pickled to another process); even though the parameter names are the same.

Thus, I think another approach to recognize a parameter in a circuit is need to resolve this issue.

@kdk
Copy link
Member

kdk commented May 15, 2019

Hi @chunfuchen , thanks for reporting! Your description of the problem is correct, Parameters presently determine equality via id() (to avoid collisions by name, say when two distinct sub-circuits have parameters independently named theta) but this identity is not preserved across serialization.
This will also be an issue when transpiling batches of parameterized circuits (through the parallel_map call inside transpile.)

@kdk kdk added the bug Something isn't working label May 15, 2019
@1ucian0 1ucian0 added the status: pending PR It has one or more PRs pending to solve this issue label Aug 9, 2019
mtreinish pushed a commit that referenced this issue Aug 14, 2019
Parameters were defined such that they would only ever __eq__ self. That way, 
if a user defined a Parameter('theta') and wanted to compose in a subcircuit 
defined elsewhere which contained a different Parameter('theta'), we would 
be able to detect that the parameter names overlapped and raise an error.

However, if a user sent a Parameter to a different python instance though (say
 through multiprocessing.Pool or qiskit.tools.parallel_map), it would be
instantiated as a different python object and so no longer be considered equal
to the original parameter.

This PR changes Parameter to instead generate a random UUID on
instantiation and use that for equality testing. Unlike id(self), self._uuid will be
preserved across pickling and de-pickling, but should otherwise preserve
Parameter's equality behavior.

Fixes #2429
Fixes #2864
faisaldebouni pushed a commit to faisaldebouni/qiskit-terra that referenced this issue Aug 5, 2020
…t#2947)

Parameters were defined such that they would only ever __eq__ self. That way, 
if a user defined a Parameter('theta') and wanted to compose in a subcircuit 
defined elsewhere which contained a different Parameter('theta'), we would 
be able to detect that the parameter names overlapped and raise an error.

However, if a user sent a Parameter to a different python instance though (say
 through multiprocessing.Pool or qiskit.tools.parallel_map), it would be
instantiated as a different python object and so no longer be considered equal
to the original parameter.

This PR changes Parameter to instead generate a random UUID on
instantiation and use that for equality testing. Unlike id(self), self._uuid will be
preserved across pickling and de-pickling, but should otherwise preserve
Parameter's equality behavior.

Fixes Qiskit#2429
Fixes Qiskit#2864
@sanketsharma
Copy link

sanketsharma commented May 3, 2022

This created another issue.

[ins] In [5]: from qiskit.circuit import Parameter
[ins] In [6]: a=Parameter('x')
[ins] In [7]: b=Parameter('x')
[ins] In [8]: a==b
Out[8]: False

Thus when I am trying to bind parameters, it fails.

nav] In [2]: x=QuantumCircuit(2)
[nav] In [3]: x.rz(-1.*Parameter('α'), 1)
Out[3]: <qiskit.circuit.instructionset.InstructionSet at 0x7efdd7c05980>
[nav] In [4]: x.bind_parameters({Parameter('α'): np.pi/2})
---------------------------------------------------------------------------
CircuitError                              Traceback (most recent call last)
Input In [4], in <cell line: 1>()
----> 1 x.bind_parameters({Parameter('α'): np.pi/2})

File ~/.venv/deuteron/lib/python3.10/site-packages/qiskit/circuit/quantumcircuit.py:2517, in QuantumCircuit.bind_parameters(self, values)
   2513     if any(isinstance(value, ParameterExpression) for value in values.values()):
   2514         raise TypeError(
   2515             "Found ParameterExpression in values; use assign_parameters() instead."
   2516         )
-> 2517     return self.assign_parameters(values)
   2518 else:
   2519     if any(isinstance(value, ParameterExpression) for value in values):

File ~/.venv/deuteron/lib/python3.10/site-packages/qiskit/circuit/quantumcircuit.py:2472, in QuantumCircuit.assign_parameters(self, parameters, inplace)
   2466 params_not_in_circuit = [
   2467     param_key
   2468     for param_key in unrolled_param_dict
   2469     if param_key not in unsorted_parameters
   2470 ]
   2471 if len(params_not_in_circuit) > 0:
-> 2472     raise CircuitError(
   2473         "Cannot bind parameters ({}) not present in the circuit.".format(
   2474             ", ".join(map(str, params_not_in_circuit))
   2475         )
   2476     )
   2478 # replace the parameters with a new Parameter ("substitute") or numeric value ("bind")
   2479 for parameter, value in unrolled_param_dict.items():

CircuitError: 'Cannot bind parameters (α) not present in the circuit.'

@jakelishman
Copy link
Member

This is a very old issue, and the new report isn't caused by this.

In this case, this is a deliberate design choice, as mentioned above. Parameters are not meant to be equal if they have the same name, in part to prevent accidental issues when multiple circuits happen to use parameters with the same name. You should use the same parameter instance:

alpha = Parameter("alpha")
qc.qc(alpha, 0)
qc.bind_parameters({alpha: np.pi/2})

rather than constructing it new each time. You can also retrieve the parameter instances from a circuit object qc by doing qc.parameters.

@pafloxy
Copy link

pafloxy commented Jul 12, 2022

Hello I am facing a similar problem. After using qc.bind_paramters() for a particular time the paramters become inaccesible, also qc.paramters returns a empty ParamterView object indicating the paramters have been removed. But for my purpose I need to pass in different numeric values to the parameters through out the preocess. Is there any workaround to this ?

@jakelishman
Copy link
Member

bind_parameters and assign_parameters return new circuits, so you can use the old circuit to bind/assign new parameters.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug Something isn't working status: pending PR It has one or more PRs pending to solve this issue
Projects
None yet
Development

Successfully merging a pull request may close this issue.

6 participants