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

Native gates directly defined by the platform #1077

Merged
merged 18 commits into from
Feb 14, 2025
Merged

Conversation

Edoardo-Pedicillo
Copy link
Contributor

This PR fixes #1075. Now the dummy_transpiler takes the native gates directly from the platform (assuming all qubits and pairs to have the same ones) and also the compiler is updated taking into account the new rules. This PR doesn't solve the problem of define any possible native gates set, indeed Qibo requires them to be between the ones defined here https://github.com/qiboteam/qibo/blob/312efb9d6934e85cfdaafcdb7027cdfe46bf87c4/src/qibo/transpiler/unroller.py#L51-L59

Otherwise it will raise an error
https://github.com/qiboteam/qibo/blob/312efb9d6934e85cfdaafcdb7027cdfe46bf87c4/src/qibo/transpiler/unroller.py#L82-L83

Copy link

codecov bot commented Feb 11, 2025

Codecov Report

Attention: Patch coverage is 86.84211% with 5 lines in your changes missing coverage. Please review.

Project coverage is 97.22%. Comparing base (59f95e3) to head (a04745a).
Report is 25 commits behind head on main.

Files with missing lines Patch % Lines
src/qibocal/auto/transpile.py 86.84% 5 Missing ⚠️
Additional details and impacted files

Impacted file tree graph

@@            Coverage Diff             @@
##             main    #1077      +/-   ##
==========================================
- Coverage   97.29%   97.22%   -0.08%     
==========================================
  Files         123      123              
  Lines        9845     9880      +35     
==========================================
+ Hits         9579     9606      +27     
- Misses        266      274       +8     
Flag Coverage Δ
unittests 97.22% <86.84%> (-0.08%) ⬇️

Flags with carried forward coverage won't be shown. Click here to find out more.

Files with missing lines Coverage Δ
src/qibocal/auto/transpile.py 88.23% <86.84%> (-2.68%) ⬇️

... and 2 files with indirect coverage changes

src/qibocal/auto/transpile.py Outdated Show resolved Hide resolved
src/qibocal/auto/transpile.py Outdated Show resolved Hide resolved
Base automatically changed from fix_backend to main February 12, 2025 09:45
Copy link
Member

@alecandido alecandido left a comment

Choose a reason for hiding this comment

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

Mostly style comments, but those related to side effects and neglecting the existing platform.natives attributes are substantial

src/qibocal/auto/transpile.py Outdated Show resolved Hide resolved
src/qibocal/auto/transpile.py Outdated Show resolved Hide resolved
src/qibocal/auto/transpile.py Outdated Show resolved Hide resolved
src/qibocal/auto/transpile.py Outdated Show resolved Hide resolved
single_qubit_natives = list(qubit.native_gates.raw)
# Solve Qibo-Qibolab mismatch
single_qubit_natives.append("RZ")
single_qubit_natives.append("Z")
Copy link
Member

Choose a reason for hiding this comment

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

I wonder if Z should be somehow directly inferred from RZ by the transpiler itself...

(implying that is not platform's/Qibocal's business)

Copy link
Contributor Author

Choose a reason for hiding this comment

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

In theory yes, I am using Qibolab compiler and NativeGates convention to differentiate between the two.

Copy link
Member

Choose a reason for hiding this comment

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

Fine, but is there a reason to differentiate?

I.e. with the current compiler, the RZ will always be implemented as virtual phase, and that will be the same for the Z as well...

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Fine, but is there a reason to differentiate?

No, maybe @andrea-pasquale could have an explanation.

Comment on lines 112 to 113
pairs = list(platform.pairs.values())[0]
qubit = list(platform.qubits.values())[0]
Copy link
Member

Choose a reason for hiding this comment

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

Is there a reason to start from parameters, instead of starting directly from the natives listing exposed by the platform?
https://github.com/qiboteam/qibolab/blob/bfb48cc2d9ec5e563110720889dc74acca6e604c/src/qibolab/backends.py#L64-L79

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I am not sure I understood, this function should return the native gates that have a rule in the default compiler, so it will not work if the iSWAP is in the platform (this was my first attempt but as explained before the result was not what I was looking for, I am not sure this is intended).

Copy link
Member

Choose a reason for hiding this comment

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

Wait a second: this function is used just to feed set_compiler, in which you're doing the following

if gate not in compiler.rules:
rules[gate] = create_rule(native)
else:
rules[gate] = compiler.rules[gate]

To me, that means that you're trying to support rules beyond the default compiler. Including iSWAP, if available.

I'm sure I misunderstood something in your reply...

Comment on lines 138 to 144
def rule(qubits_ids, platform, parameters=None):
if len(qubits_ids[1]) == 1:
native_gate = platform.qubits[tuple(qubits_ids[1])].native_gates
else:
native_gate = platform.pairs[tuple(qubits_ids[1])].native_gates
pulses = getattr(native_gate, native).pulses
return PulseSequence(pulses), {}
Copy link
Member

Choose a reason for hiding this comment

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

Using closures is always delicate, so, I'd suggest moving it in a function on its own, at least will make it clear which are the captured variables

def create_rule(native):
    def rule(qubits_ids, platform, parameters=None):
        if len(qubits_ids[1]) == 1:
            native_gate = platform.qubits[tuple(qubits_ids[1])].native_gates
        else:
            native_gate = platform.pairs[tuple(qubits_ids[1])].native_gates
        pulses = getattr(native_gate, native).pulses
        return PulseSequence(pulses), {}

    return rule

Copy link
Member

Choose a reason for hiding this comment

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

P.S.: type hints would also help

pulses = getattr(native_gate, native).pulses
return PulseSequence(pulses), {}

backend.compiler[gate] = rule
Copy link
Member

Choose a reason for hiding this comment

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

Also, usually it is better to avoid mutating objects, especially nested ones.
I could lift the suggestion by one level, and suggest creating the entire backend (here just returning the compiler, instead of modifying the backend). But let's limit to the compiler itself.

You could create an empty dictionary rules, and here do

Suggested change
backend.compiler[gate] = rule
rules[gate] = rule

eventually setting the compiler out of the loops as:

    backend.compiler = Compiler(rules=rules)

(in principle, you can avoid even modifying the dictionary rules, by defining it immediately through comprehension - but I get this is a bit unfamiliar).

src/qibocal/auto/transpile.py Outdated Show resolved Hide resolved
Comment on lines 151 to 152
If the backend is `qibolab`, a transpiler with just an unroller is returned,
otherwise None.
Copy link
Member

Choose a reason for hiding this comment

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

Properly document that this function is not pure, since it has the side effect of modifying the input backend (in particular, affecting its .compiler attribute).

I know this seems pedantic, but losing track of this changes may have effects later on, when they will become unexpected.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

My idea at the beginning was to make the set_compiler function in each protocol that executes circuits explicit. I decided to put it here to make my life easier but I will open an issue about it.

Copy link
Member

Choose a reason for hiding this comment

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

My idea at the beginning was to make the set_compiler function in each protocol that executes circuits explicit.

No need.

I agree with your choice, since there is nothing protocol-specific, so it should not be repeated over and over.

to make my life easier

Everyone's life easier. Good choice :)

I decided to put it here

Even better, you could have put it here

backend = construct_backend(backend="qibolab", platform=platform)
platform = self.platform = backend.platform

if it were the single place where the backend was created.

Since unfortunately it is not, instead of this:

backend = construct_backend("qibolab", platform=platform)
transpiler = dummy_transpiler(backend)

you could actually just make a single function, and invoke as

    backend, transpiler = construct_qibolab_backend(platform)

considering that this double functions invocation is repeat over and over.

https://github.com/search?q=repo%3Aqiboteam%2Fqibocal%20construct_backend&type=code

(in any case, this one could be an issue - but it's especially relevant for 0.2, since we won't maintain 0.1 for long)

Set the compiler to execute the native gates defined by the platform.
"""
compiler = backend.compiler
print("EEEEEEE", backend.natives)
Copy link
Member

Choose a reason for hiding this comment

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

It seems I forgot this in the previous review: I guess this is just a leftover.

@Edoardo-Pedicillo Edoardo-Pedicillo added this pull request to the merge queue Feb 14, 2025
Merged via the queue into main with commit e694e64 Feb 14, 2025
20 of 21 checks passed
@Edoardo-Pedicillo Edoardo-Pedicillo deleted the native_gates branch February 14, 2025 15:08
Edoardo-Pedicillo added a commit that referenced this pull request Feb 18, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Circuit based protocols use default native gate set instead of platform native gates
3 participants