-
Notifications
You must be signed in to change notification settings - Fork 20
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
Create glorious_folding_amplifier.py #36
base: main
Are you sure you want to change the base?
Conversation
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Great job @mrvee-qC-bee! 🥳
I have left some comments, once you're done with them try changing copies
for num_foldings
and appending two circuits per num_folding
instead of just one as you are doing with copies
. You should always retain the original circuit too, so this should give you something like:
num_foldings=0
→ output circuit is the same as input circuit (i.e. once)num_foldings=1
→ output circuit is input circuit repeated three timesnum_foldings=2
→ output circuit is input circuit repeated five times
Remember to keep inserting barriers between each copy! If you feel brave enough, try to invert the first of those two circuits that you append afterwards 😉
zne/noise_amplification/folding_amplifier/glorious_folding_amplifier.py
Outdated
Show resolved
Hide resolved
zne/noise_amplification/folding_amplifier/glorious_folding_amplifier.py
Outdated
Show resolved
Hide resolved
zne/noise_amplification/folding_amplifier/glorious_folding_amplifier.py
Outdated
Show resolved
Hide resolved
zne/noise_amplification/folding_amplifier/glorious_folding_amplifier.py
Outdated
Show resolved
Hide resolved
zne/noise_amplification/folding_amplifier/glorious_folding_amplifier.py
Outdated
Show resolved
Hide resolved
zne/noise_amplification/folding_amplifier/glorious_folding_amplifier.py
Outdated
Show resolved
Hide resolved
zne/noise_amplification/folding_amplifier/glorious_folding_amplifier.py
Outdated
Show resolved
Hide resolved
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is very good! Congrats!
Some general comments:
- Try to do one thing only per commit (i.e. avoid
something: this + that
) - Try to write the scope in the commit message (e.g.
refactor(folding_amplifier): update ...
)
Other than that, great job! Try to act on the new comments and let's pick it up from there 🥳
zne/noise_amplification/folding_amplifier/glorious_folding_amplifier.py
Outdated
Show resolved
Hide resolved
def _inverse_dag(dag_to_inverse: DAGCircuit) -> DAGCircuit: | ||
"""Inverts dag circuit""" | ||
dag_to_inverse = dag_to_circuit(dag_to_inverse).inverse() | ||
return circuit_to_dag(dag_to_inverse) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is a good utility function! I am surprised that this does not exist natively in terra. We may want to contribute it (there is a similar method called DAGCircuit.reverse
with an implementation just like yours).
Before doing that think about the following question: How many times do you traverse the entire circuit with this implementation? (Hint: look instruction by instruction and add up)
Once you respond to that, try to respond to the same question but for a piece of code which traverses the DAGCircuit
in reverse order (i.e. form OutNodes
to InNodes
) adding the inverse gates as it encounters them.
Which one is (roughly) more efficient? (i.e. traverses the circuit less times and therefore is faster)
No need to update the code, but something to be aware of 😉
zne/noise_amplification/folding_amplifier/glorious_folding_amplifier.py
Outdated
Show resolved
Hide resolved
zne/noise_amplification/folding_amplifier/glorious_folding_amplifier.py
Outdated
Show resolved
Hide resolved
zne/noise_amplification/folding_amplifier/glorious_folding_amplifier.py
Outdated
Show resolved
Hide resolved
zne/noise_amplification/folding_amplifier/glorious_folding_amplifier.py
Outdated
Show resolved
Hide resolved
zne/noise_amplification/folding_amplifier/glorious_folding_amplifier.py
Outdated
Show resolved
Hide resolved
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Good, good, good!! This is great!!
Try to keep all comments related to Terra contributions UNresolved so that we remember to do those at some point 🙂
|
||
def apply_folding(original_dag: DAGCircuit, num_foldings: int) -> DAGCircuit: | ||
"""Build dag circuit by composing copies of an input dag circuit.""" | ||
noisy_dag = copy.deepcopy(original_dag) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Again, greatly surprised that there is no native DAGCircuit.copy
method. We may need to contribute that to Terra as well. 😮
zne/noise_amplification/folding_amplifier/glorious_folding_amplifier.py
Outdated
Show resolved
Hide resolved
zne/noise_amplification/folding_amplifier/glorious_folding_amplifier.py
Outdated
Show resolved
Hide resolved
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Interesting approach! See comments 🙂
dag_to_inverse = dag_to_circuit(dag_to_inverse) | ||
dag_to_inverse.barrier() | ||
dag_to_inverse = dag_to_inverse.inverse() | ||
dag_to_inverse.barrier() |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Uhh, I had not though of this! Interesting!
You should refrain from this approach for several reasons, but probably the most important is that you are not doing what the function promises. Instead you are doing extra things (i.e. adding two barriers) that are not expected, this is sometimes referred to with the broader term side effect which good code tries to avoid at all cost, as it makes it harder to understand and predict.
How about something like:
barrier = Barrier(noisy_dag.num_qubits())
...
for _ in range(num_foldings):
noisy_dag.apply_operation_back(barrier, qargs=noisy_dag.qubits)
noisy_dag.compose(inverse_dag, inplace=True)
...
zne/noise_amplification/folding_amplifier/glorious_folding_amplifier.py
Outdated
Show resolved
Hide resolved
Initial function
60f71f2
to
d10067b
Compare
d10067b
to
9927cbc
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Great job!! Some clean-up to do, and some puzzles to solve, but you are making progress sir! 🎈
zne/noise_amplification/folding_amplifier/glorious_folding_amplifier.py
Outdated
Show resolved
Hide resolved
zne/noise_amplification/folding_amplifier/glorious_folding_amplifier.py
Outdated
Show resolved
Hide resolved
zne/noise_amplification/folding_amplifier/glorious_folding_amplifier.py
Outdated
Show resolved
Hide resolved
zne/noise_amplification/folding_amplifier/glorious_folding_amplifier.py
Outdated
Show resolved
Hide resolved
zne/noise_amplification/folding_amplifier/glorious_folding_amplifier.py
Outdated
Show resolved
Hide resolved
inverse_dag = self._invert_dag(dag) | ||
for _ in range(num_foldings): | ||
noisy_dag.apply_operation_back(barrier, qargs=noisy_dag.qubits) | ||
noisy_dag.compose(inverse_dag, inplace=True) | ||
noisy_dag.apply_operation_back(barrier, qargs=noisy_dag.qubits) | ||
noisy_dag.compose(dag, inplace=True) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
inverse_dag = self._invert_dag(dag) | |
for _ in range(num_foldings): | |
noisy_dag.apply_operation_back(barrier, qargs=noisy_dag.qubits) | |
noisy_dag.compose(inverse_dag, inplace=True) | |
noisy_dag.apply_operation_back(barrier, qargs=noisy_dag.qubits) | |
noisy_dag.compose(dag, inplace=True) | |
inverse_dag = self._invert_dag(original_dag) | |
for _ in range(num_foldings): | |
noisy_dag.apply_operation_back(barrier, qargs=noisy_dag.qubits) | |
noisy_dag.compose(inverse_dag, inplace=True) | |
noisy_dag.apply_operation_back(barrier, qargs=noisy_dag.qubits) | |
noisy_dag.compose(original_dag, inplace=True) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This seems to fail lint! The DAGNoiseAmplifier has dag
as the variable name than original_dag
:(
def _apply_full_folding( | ||
self, | ||
noisy_dag: DAGCircuit, | ||
dag: DAGCircuit, | ||
barrier: Barrier, | ||
num_foldings: int, | ||
) -> DAGCircuit: |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
def _apply_full_folding( | |
self, | |
noisy_dag: DAGCircuit, | |
dag: DAGCircuit, | |
barrier: Barrier, | |
num_foldings: int, | |
) -> DAGCircuit: | |
def _apply_full_folding( | |
self, | |
noisy_dag: DAGCircuit, | |
original_dag: DAGCircuit, | |
barrier: Barrier, | |
num_foldings: int, | |
) -> DAGCircuit: |
A few things to notice i this signature:
- As mentioned before, why do you need
barrier
as an arg? Are you going to be passing anything different any time? - why do we pass
noisy_dag
? I know you drew inspiration from the other class, but do you understand the reasons behind this? If not, there is no need to rush it, let's take it one step at a time and re-discover these things as we go 🙂 Your code really does not require that you pass anoisy_dag
, better to provide theorginal_dag
andnum_foldings
and return a freshly creatednoisy_dag
for now. - Keep naming consistent,
original_dag
is better than justdag
because it emphasizes the difference withnoisy_dag
zne/noise_amplification/folding_amplifier/glorious_folding_amplifier.py
Outdated
Show resolved
Hide resolved
zne/noise_amplification/folding_amplifier/glorious_folding_amplifier.py
Outdated
Show resolved
Hide resolved
zne/noise_amplification/folding_amplifier/glorious_folding_amplifier.py
Outdated
Show resolved
Hide resolved
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is starting to look very professional!! 💪🏼
zne/noise_amplification/folding_amplifier/glorious_folding_amplifier.py
Outdated
Show resolved
Hide resolved
zne/noise_amplification/folding_amplifier/glorious_folding_amplifier.py
Outdated
Show resolved
Hide resolved
zne/noise_amplification/folding_amplifier/glorious_folding_amplifier.py
Outdated
Show resolved
Hide resolved
zne/noise_amplification/folding_amplifier/glorious_folding_amplifier.py
Outdated
Show resolved
Hide resolved
zne/noise_amplification/folding_amplifier/glorious_folding_amplifier.py
Outdated
Show resolved
Hide resolved
inverted_dag = dag_to_inverse.copy_empty_like() | ||
for node in dag_to_inverse.topological_op_nodes(): | ||
inverted_dag.apply_operation_front( | ||
node.op.inverse(), qargs=node.qargs, cargs=node.cargs | ||
) | ||
return inverted_dag |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
SUPER COOL!! You wrote this using only the DAG API, which makes the code more efficient and, ready to be contributed to Terra!! Let's take care of this in our next chat 🥳
zne/noise_amplification/folding_amplifier/glorious_folding_amplifier.py
Outdated
Show resolved
Hide resolved
zne/noise_amplification/folding_amplifier/glorious_folding_amplifier.py
Outdated
Show resolved
Hide resolved
zne/noise_amplification/folding_amplifier/glorious_folding_amplifier.py
Outdated
Show resolved
Hide resolved
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Docstrings look good! Just a few comments, you are ready to move to the next step 🥳
Rename this class so it is clear this one is global and create a new class for the local amplifier! Avoid looking at the previous code, try to figure this one out on your own 😉
zne/noise_amplification/folding_amplifier/glorious_folding_amplifier.py
Outdated
Show resolved
Hide resolved
zne/noise_amplification/folding_amplifier/glorious_folding_amplifier.py
Outdated
Show resolved
Hide resolved
zne/noise_amplification/folding_amplifier/glorious_folding_amplifier.py
Outdated
Show resolved
Hide resolved
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Some details 🙂
zne/noise_amplification/folding_amplifier/glorious_global_folding_amplifier.py
Outdated
Show resolved
Hide resolved
zne/noise_amplification/folding_amplifier/glorious_global_folding_amplifier.py
Outdated
Show resolved
Hide resolved
zne/noise_amplification/folding_amplifier/glorious_global_folding_amplifier.py
Outdated
Show resolved
Hide resolved
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Good job!! 🎉
I left some remarks to get you thinking about more advanced topics on software design. Let me know what you think 😉
zne/noise_amplification/folding_amplifier/glorious_global_folding_amplifier.py
Outdated
Show resolved
Hide resolved
zne/noise_amplification/folding_amplifier/glorious_local_folding_amplifier.py
Outdated
Show resolved
Hide resolved
zne/noise_amplification/folding_amplifier/glorious_local_folding_amplifier.py
Outdated
Show resolved
Hide resolved
zne/noise_amplification/folding_amplifier/glorious_local_folding_amplifier.py
Outdated
Show resolved
Hide resolved
zne/noise_amplification/folding_amplifier/glorious_local_folding_amplifier.py
Outdated
Show resolved
Hide resolved
noisy_dag = self._insert_folding(noisy_dag, node, num_foldings) | ||
else: | ||
noisy_dag.apply_operation_back(node.op, qargs=node.qargs, cargs=node.cargs) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
How about we try to mimic the language that is already in place to make the code more readable? 🙃
noisy_dag = self._insert_folding(noisy_dag, node, num_foldings) | |
else: | |
noisy_dag.apply_operation_back(node.op, qargs=node.qargs, cargs=node.cargs) | |
noisy_dag = self._apply_folded_operation_back(noisy_dag, node, num_foldings) | |
else: | |
noisy_dag.apply_operation_back(node.op, qargs=node.qargs, cargs=node.cargs) |
zne/noise_amplification/folding_amplifier/glorious_local_folding_amplifier.py
Outdated
Show resolved
Hide resolved
zne/noise_amplification/folding_amplifier/glorious_local_folding_amplifier.py
Outdated
Show resolved
Hide resolved
zne/noise_amplification/folding_amplifier/glorious_local_folding_amplifier.py
Outdated
Show resolved
Hide resolved
zne/noise_amplification/folding_amplifier/glorious_local_folding_amplifier.py
Show resolved
Hide resolved
There is one more thing that we could do here: add some configuration to the amplifiers that controls whether barriers are applied or not. 🙃 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Great progress!! Let's discuss, and keep it up! 🥳
zne/noise_amplification/folding_amplifier/glorious_folding_amplifier.py
Outdated
Show resolved
Hide resolved
|
||
from ..noise_amplifier import DAGNoiseAmplifier | ||
|
||
Folding = namedtuple("Folding", ("full", "partial", "effective_noise_factor")) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is very good!
We should maybe rethink the language at some point according to that glossary that I sent you a while back, so that terminology is a bit clearer 🙂
zne/noise_amplification/folding_amplifier/glorious_folding_amplifier.py
Outdated
Show resolved
Hide resolved
Folding: named tuple containing full foldings, number of gates to partially fold and | ||
effective noise factor of the operation | ||
""" | ||
noise_factor = self._validate_noise_factor(noise_factor) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Since this is an internal function you do not need to perform validation and it should be avoided unless strictly necessary. Instead, assume that the input is correct (after all it will be you passing it along) and move validation to the point where the actual user of the tool passes noise_factors
only 🙂
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Tried something on those lines! 🥳
) | ||
return Folding(full, partial, effective_noise_factor) | ||
|
||
def _compute_best_estimate(self, num_nodes: float, partial_foldings: float) -> float: |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This name is not explanatory: if I am new to the tool and I read it, I would not know what is this a best estimate of!
Naming is tough!! 😅
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done! (sorta!) 🥳
if self.barriers: | ||
barrier = Barrier(dag.num_qubits()) | ||
dag.apply_operation_back(barrier, qargs) | ||
|
||
def _invert_dag(self, dag_to_inverse: DAGCircuit) -> DAGCircuit: |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Simple and elegant function! I love it 😍
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
TODO: Contribute to terra
zne/noise_amplification/folding_amplifier/glorious_local_folding_amplifier.py
Outdated
Show resolved
Hide resolved
@@ -33,99 +32,74 @@ class GloriousLocalFoldingAmplifier(DAGNoiseAmplifier): | |||
`<https://ieeexplore.ieee.org/document/9259940>` | |||
""" | |||
|
|||
def __init__(self, gates_to_fold: Sequence[int | str], barriers: bool = True) -> None: | |||
def __init__(self, gates_to_fold: Set[Union[str, int] | None], barriers: bool = True) -> None: | |||
self.gates_to_fold = self._validate_gates_to_fold(gates_to_fold) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Validation should happen in the setter —you currently don't have one, add it! 👨🏼💻
zne/noise_amplification/folding_amplifier/glorious_local_folding_amplifier.py
Outdated
Show resolved
Hide resolved
VALID_GATES = set( # pylint: disable=invalid-name | ||
standard_gates.get_standard_gate_name_mapping().keys() | ||
) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
How do we handle custom gates? 🤔
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Will ask a query in our next meet! :D
…lifier.py Co-authored-by: Pedro Rivero <[email protected]>
zne/noise_amplification/folding_amplifier/glorious_folding_amplifier.py
Outdated
Show resolved
Hide resolved
full_foldings = int(foldings) | ||
partial_foldings = foldings - full_foldings | ||
|
||
gates_to_partial_fold = self._compute_best_estimated_gates(num_nodes, partial_foldings) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Change to num_partial_folds
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I have had an idea on how to make this even cleaner, let's discuss on Tuesday! 🙂 It is based on computing the total number of foldings to perform in the whole circuit directly, and choosing how that gets distributed later
@@ -31,27 +31,16 @@ class GloriousGlobalFoldingAmplifier(GloriousFoldingAmplifier): | |||
`<https://ieeexplore.ieee.org/document/9259940>` | |||
""" | |||
|
|||
def __init__(self, barriers: bool = True) -> None: | |||
self._set_barriers(barriers) | |||
def __init__(self) -> None: |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Add barrier and tolerance
) -> None: | ||
super().__init__() | ||
self._set_custom_gates(custom_gates) | ||
self._set_gates_to_fold(gates_to_fold) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Add barriers and tolerance
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Good, let's push this through the finish line! :) The PR is getting quite substantial, it might be good to split it in smaller chunks. Let's consider this in our next chat!
################################################################################ | ||
@property | ||
def barriers(self) -> bool: | ||
"""Barriers setter""" |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is actually a getter
not a setter
, but it is also good if you explain what the property means (e.g. if set to True, barriers are added when folding) 🙂
return self._barriers | ||
|
||
def _set_barriers(self, barriers: bool) -> None: | ||
"""Set barriers property""" |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is a setter
on the other hand!
|
||
@property | ||
def tolerance(self) -> float: | ||
"""Tolerance setter""" |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Again, not a setter
, but it is better to explain the property in this docstring. It is somewhat eveident tht this is a getter
so writing it on the docstring is not adding any relevant information 🙃
return self._tolerance | ||
|
||
def _set_tolerance(self, tolerance: float) -> None: | ||
"""Set Tolerance property""" |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is the real tolerance setter
"""Set Tolerance property""" | |
"""Set tolerance property""" |
full_foldings = int(foldings) | ||
partial_foldings = foldings - full_foldings | ||
|
||
gates_to_partial_fold = self._compute_best_estimated_gates(num_nodes, partial_foldings) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I have had an idea on how to make this even cleaner, let's discuss on Tuesday! 🙂 It is based on computing the total number of foldings to perform in the whole circuit directly, and choosing how that gets distributed later
################################################################################ | ||
## INTERFACE IMPLEMENTATION | ||
################################################################################ | ||
def amplify_dag_noise(self, dag: DAGCircuit, noise_factor: float) -> DAGCircuit: | ||
"""Applies global folding to input DAGCircuit and returns amplified circuit""" | ||
noise_factor = self._validate_noise_factor(noise_factor) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
GREAT! 👏🏼
@property | ||
def custom_gates(self) -> bool: | ||
"""Custom_gates flag setter""" | ||
return self._custom_gates | ||
|
||
def _set_custom_gates(self, custom_gates: bool) -> None: | ||
"""Set gates_to_fold property""" | ||
self._custom_gates = bool(custom_gates) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What is this for? 🤔
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
TODO: Remove entirely
if self.custom_gates: | ||
pass | ||
# TODO: Append valid custom_gate_names to VALID_GATES |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can you explain this further? Thanks! 🙂
refactor(folding_amplifier): init dag folding amplifier
Summary
Details and comments