Skip to content

Moment.resolve_parameter not resolving all the expressions #6778

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

Closed
ikd-sci opened this issue Oct 26, 2024 · 10 comments · Fixed by #6794
Closed

Moment.resolve_parameter not resolving all the expressions #6778

ikd-sci opened this issue Oct 26, 2024 · 10 comments · Fixed by #6794
Assignees
Labels
good first issue This issue can be resolved by someone who is not familiar with the codebase. A good starting issue. kind/bug-report Something doesn't seem to work. no QC knowledge needed Want to contribute to Cirq, but don't know quantum computing? This issue is for you. triage/accepted A consensus emerged that this bug report, feature request, or other action should be worked on

Comments

@ikd-sci
Copy link
Contributor

ikd-sci commented Oct 26, 2024

Description of the issue

How to reproduce the issue

q0 = cirq.LineQubit(0)
c = cirq.Circuit(cirq.Rz(rads=sympy.Mul(sympy.Rational(2, 3), sympy.pi)).on(q0))
cirq.resolve_parameters(c, {'pi': np.pi})

results in:

1: ───Rz(2*pi/3)───

as per discussion with @NoureldinYosri, Moment.resolve_parameter has a bug which can be bypassed by:

def resolve_parameters(circuit, resolver):
    return cirq.Circuit.from_moments(*[[cirq.resolve_parameters(op, resolver) for op in m] for m in circuit])
resolve_parameters(c, {'pi': np.pi})

results in the expected:

1: ───Rz(0.667π)───

Cirq version
'1.5.0.dev'

@ikd-sci ikd-sci added the kind/bug-report Something doesn't seem to work. label Oct 26, 2024
@NoureldinYosri NoureldinYosri self-assigned this Oct 26, 2024
@NoureldinYosri NoureldinYosri added the triage/accepted A consensus emerged that this bug report, feature request, or other action should be worked on label Oct 26, 2024
@pavoljuhas pavoljuhas added no QC knowledge needed Want to contribute to Cirq, but don't know quantum computing? This issue is for you. good first issue This issue can be resolved by someone who is not familiar with the codebase. A good starting issue. labels Nov 13, 2024
@dv8081
Copy link
Contributor

dv8081 commented Nov 13, 2024

@pavoljuhas can you please assign this to me ..

@NoureldinYosri
Copy link
Collaborator

NoureldinYosri commented Nov 13, 2024

@dv8081 the issue that happens is that Moment._resolve_parameters_ checks equality to determine whether to create a new moment or not

resolved_op = protocols.resolve_parameters(op, resolver, recursive)
if resolved_op != op:
changed = True
resolved_ops.append(resolved_op)

this doesn't work because symyp.py == np.pi == math.pi so it doesn't do anything. The fix should be to

  1. instead of checking equality, check if the new object is the old object with is (i.e. if resolved_op is not op)
  2. go through the different gates and ensure that for each gate's _resolve_parameters_ we return self if no change happened and only create a new object if a variable was resolved

@dv8081
Copy link
Contributor

dv8081 commented Nov 13, 2024

@NoureldinYosri Thanks for pointing this out for me.

dv8081 added a commit to dv8081/Cirq that referenced this issue Nov 14, 2024
Fixes quantumlib#6778
Fix Moment.resolve_parameters to properly resolve symbolic expressions

    Changed equality check from '!=' to 'is not' to handle cases where symbolic parameters are equivalent but not identical objects

    Ensured that the method returns a new object only when a variable is resolved, maintaining efficient behavior.

    Tested the fix with existing cases for Rx, Ry, and Rz gates to confirm correct behavior when parameters are resolved or remain unchanged.
@dv8081
Copy link
Contributor

dv8081 commented Nov 15, 2024

Hi @NoureldinYosri
I have identified several files that contain a resolve_parameters method. Based on your comment I understand that the fix should apply to gates that have this method. I have separated the files into two categories: gates and non-gates.
Gate Files:
parallel_gate.py, phased_x_z_gate.py,phased_iswap_gate.py, diagonal_gate.py, raw_types.py,, three_qubit_gates.py, random_gate_channel.py, fsim_gate.py, controlled_gate.py, wait_gate.py, two_qubit_diagonal_gate.py, eigen_gate.py etc
Non-Gate Files:
dense_pauli_string.py, raw_types.py (second instance), eigen_gate_test.py,controlled_operation.py, classically_controlled_operation.py, pauli_string_phasor.py,gate_operation.py,global_phase_op.py, linear_combinations.py etc
Could you please confirm whether I need to implement the fix for all the gate files? Additionally, should I ignore the non-gate files like dense_pauli_string.py and linear_combinations.py ?

@pavoljuhas
Copy link
Collaborator

pavoljuhas commented Nov 15, 2024

2. go through the different gates and ensure that for each gate's _resolve_parameters_ we return self if no change happened and only create a new object if a variable was resolved

@NoureldinYosri - Looking at it again, quite a few SomeGate._resolve_parameters_ methods return a new object unconditionally. If we compare the resolved and original gate there, we could hit the same issue with sympy.Float(1) == 1.0 being True and thus return self which has sympy parameters.

Perhaps we can change the fix in Cirq/cirq-core/cirq/circuits/moment.py to

        for op in self:
            resolved_op = protocols.resolve_parameters(op, resolver, recursive)
            changed = (
                changed
                or resolved_op != op
                or (protocols.is_parameterized(op) and not protocols.is_parameterized(resolved_op))
            )
            resolved_ops.append(resolved_op)

This will skip checks for operation change if a changed operation was already found before, and it would detect resolution of sympy constant to python numbers.

@NoureldinYosri
Copy link
Collaborator

NoureldinYosri commented Nov 15, 2024

If we compare the resolved and original gate there, we could hit the same issue with sympy.Float(1) == 1.0

This is why I want to the gates/operations to decide whether a change happended or not without using comparison

protocols.is_parameterized(op) and not protocols.is_parameterized(resolved_op)

This won't work is the resolver is doing a partial resolution (i.e. resolving some parameters and leaving others)

@pavoljuhas
Copy link
Collaborator

pavoljuhas commented Nov 15, 2024

protocols.is_parameterized(op) and not protocols.is_parameterized(resolved_op)

This won't work is the resolver is doing a partial resolution (i.e. resolving some parameters and leaving others)

The corner case we are trying to solve here is of constant sympy expressions (without any free parameters) getting resolved to Python numbers passing the s_constant == py_number and thus also resolved_op == op equalities. In such case we kept the original op with a sympy constant, but the intent was to resolve it to a Python number. If both op and resolved_op are parameterized, it does not matter that much, because the new moment will in any case have sympy parameters. If op has several sympy parameters, say a, b and only a is resolved, that should also cause the earlier equality to break, ie, resolved_op != op.

I am bit reluctant to make extra changes to gates _resolve_parameters_ methods with a risk of introducing new bugs or corner cases. If you have an example where the fix suggested above breaks, please LMK.

@NoureldinYosri
Copy link
Collaborator

@pavoljuhas if you want to just patch the problem, that's fine. but lets keep the issue open

@dv8081
Copy link
Contributor

dv8081 commented Nov 16, 2024

@pavoljuhas Thank you for reviewing and suggesting the changes. I have committed the updates.

pavoljuhas added a commit that referenced this issue Nov 19, 2024
Problem: `Moment._resolve_parameters_` might keep constant sympy expression
if its resolved value is numerically equal.

Solution: Check if parameterization changed for the resolved operation and
if so replace the original operation with resolved (even if equal).

Fixes #6778

---------

Co-authored-by: Pavol Juhas <[email protected]>
Co-authored-by: Noureldin <[email protected]>
@pavoljuhas
Copy link
Collaborator

@pavoljuhas if you want to just patch the problem, that's fine. but lets keep the issue open

The original example bug is fixed by #6794. If there are other cases which still fail, please reopen with such example.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
good first issue This issue can be resolved by someone who is not familiar with the codebase. A good starting issue. kind/bug-report Something doesn't seem to work. no QC knowledge needed Want to contribute to Cirq, but don't know quantum computing? This issue is for you. triage/accepted A consensus emerged that this bug report, feature request, or other action should be worked on
Projects
None yet
4 participants