Skip to content
Open
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
1 change: 1 addition & 0 deletions qualtran/bloqs/rotations/quantum_variable_rotation.py
Original file line number Diff line number Diff line change
Expand Up @@ -191,6 +191,7 @@ def build_composite_bloq(self, bb: 'BloqBuilder', **soqs: 'SoquetT') -> Dict[str
if self.cost_dtype.signed:
out[0] = bb.add(ZPowGate(exponent=1, eps=eps), q=out[0])
offset = 1 + self.cost_dtype.num_frac - self.num_frac_rotations
offset = offset if isinstance(offset, int) else 1
Copy link
Collaborator

Choose a reason for hiding this comment

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

what is the reason for this change? (add a comment?)

Copy link
Author

Choose a reason for hiding this comment

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

I noted this in the issue as a concern/question.

The instructions in the issue do not lead to completion. There is an indexing error in one of the iterations in the loop below this line I added.

qvr_zpow = QvrZPow(Register('x', QFxp(12, 6, False)), gamma = sympy.Symbol('Y'))
show_bloq(qvr_zpow, 'dtype')
show_bloq(qvr_zpow.decompose_bloq(), 'musical_score')

Perhaps I'm missing a step or something. Else, it is a pre-existing bug that might be related to the small angle rotations changes in 38db1af

In the next commit, this line will move to inside the loop to minimise its involvement to only when strictly necessary.

for i in range(num_rotations):
    power_of_two = i - self.num_frac_rotations
    exp = (2**power_of_two) * self.gamma * 2
    offset = offset if isinstance(offset, int) else 1  # offset -> 1 if not int to avoid indexing errors  #HERE
    out[-(i + offset)] = bb.add(ZPowGate(exponent=exp, eps=eps), q=out[-(i + offset)])
return {self.cost_reg.name: bb.join(out, self.cost_dtype)}

However, this is only to ensure things proceed as needed for testing of the feature in this issue.

If the instructions to produce the foundational diagram were incomplete, please let me know.

If it is a bug, it should be checked separately.

for i in range(num_rotations):
power_of_two = i - self.num_frac_rotations
exp = (2**power_of_two) * self.gamma * 2
Expand Down
91 changes: 86 additions & 5 deletions qualtran/drawing/_show_funcs.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
# Copyright 2023 Google LLC
#
# Licensed under the Apache License, Version 2.0 (the "License");
Expand All @@ -15,7 +15,9 @@
"""Convenience functions for showing rich displays in Jupyter notebook."""

import os
from typing import Dict, Optional, overload, Sequence, TYPE_CHECKING, Union
import re
import sympy
from typing import Dict, Optional, Text, overload, Sequence, TYPE_CHECKING, Union, Tuple

import IPython.display
import ipywidgets
Expand All @@ -25,13 +27,12 @@
from .bloq_counts_graph import format_counts_sigma, GraphvizCallGraph
from .flame_graph import get_flame_graph_svg_data
from .graphviz import PrettyGraphDrawer, TypedGraphDrawer
from .musical_score import draw_musical_score, get_musical_score_data
from .musical_score import MusicalScoreData, TextBox, draw_musical_score, get_musical_score_data
from .qpic_diagram import qpic_diagram_for_bloq

if TYPE_CHECKING:
import networkx as nx
import sympy



def show_bloq(bloq: 'Bloq', type: str = 'graph'): # pylint: disable=redefined-builtin
"""Display a visual representation of the bloq in IPython.
Expand All @@ -49,7 +50,9 @@
elif type.lower() == 'dtype':
IPython.display.display(TypedGraphDrawer(bloq).get_svg())
elif type.lower() == 'musical_score':
draw_musical_score(get_musical_score_data(bloq))
msd = get_musical_score_data(bloq)
pretty_msd = pretty_format_msd(msd)
draw_musical_score(pretty_msd)
elif type.lower() == 'latex':
show_bloq_via_qpic(bloq)
else:
Expand Down Expand Up @@ -142,3 +145,81 @@

IPython.display.display(Image(output_file_path, width=width, height=height))
os.remove(output_file_path)


def pretty_format_msd(msd: MusicalScoreData) -> MusicalScoreData:
Copy link
Collaborator

Choose a reason for hiding this comment

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

this function should live in musical_score.py

Copy link
Author

Choose a reason for hiding this comment

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

Moving it. I can also use a smaller function for each soq rather than the entire msd.
Will show in next commit.
Will also add a test in the corresponding file (now possible since its a soq-level function).

"""
Beautifies MSD to enable pretty diagrams
Copy link
Collaborator

Choose a reason for hiding this comment

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

explain what transformations are done

Copy link
Author

Choose a reason for hiding this comment

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

Will do. Shows in next commit.


Args:
msd: A raw MSD

Returns:
new_msd: A pretty MSD

"""

def symbols_to_identity(lbl: str) -> Tuple[str, str]:
"""
Exchanges any symbols in the label for integer 1 or returns lbl if no symbols found.

Args:
lbl: The label to be processed.

Returns:
new_lbl: A label without symbols

"""

pattern = r"Abs\((?P<symbol>[a-zA-Z])\)"
match = re.search(pattern, lbl)
if match:
symbol = match.group("symbol")
new_lbl = lbl.replace(symbol, "1")
return new_lbl, symbol
return lbl, ""

simpify_locals = {
"Min": sympy.Min,
"ceiling": sympy.ceiling,
"log2": lambda x: sympy.log(x, 2)
}

mult = 1
pretty_soqs = []
for soq_item in msd.soqs:
if isinstance(soq_item.symb, (TextBox, Text)):
try:
lbl_raw = soq_item.symb.text
lbl_no_symbols, symbol = symbols_to_identity(lbl_raw)
gate, base, exponent = sum([p.split("**", 1) for p in lbl_no_symbols.split("^")], [])

if len(base.split("*", 1)) > 1:
mult_str, base = base.rsplit("*", 1)
mult = sympy.sympify(mult_str, evaluate=True)

exponent = sympy.sympify(exponent, locals=simpify_locals, evaluate=True)
expression = str(base) + "**" + str(exponent)
expression = sympy.sympify(expression, locals=simpify_locals, evaluate=True)
new_lbl = str(gate) + "^" + symbol + "*" + str(expression * mult)
Copy link
Collaborator

Choose a reason for hiding this comment

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

I'm not sure what this code aims to do, but it seems rather brittle. Why are we replacing parts of the label with the character "1"?

Copy link
Author

Choose a reason for hiding this comment

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

Right, yes! You are completely right.

So, any errors fallback to the original label, which ensures completion, but I can make it more robust in the next commit.

More importantly, the mathematical concern is undeniably valid.

It's clear I took the diagram examples in the issue too literally, perhaps assuming there was some quantum magic happening somewhere that escaped me.

Thinking about it as a standard symbolic equation, there seems to be a need to define a preference:

  • semi-pretty labels: some simplification is possible if Abs(Y) is not evaluated with some sort of substitution (which would affect other Y values), but the pretty version won't be entirely pretty. (better than nothing I guess)
  • arbitrary replacement: pi, 2*pi, some random value? (no likey, not symbolic anymore)
  • user-input placeholder: user chooses what value to use for evaluation (same)
  • a panel of diagrams: several rotation angles rendered as a panel of diagrams rather than a single panel diagram (quite a deviation from original concept so I'm hoping there is another approach I'm not considering but, somewhat applicable).

I'll work a little on the next commit but it would be cool if you could tell me your views on the matter and potential alternatives not considered, to avoid the need for many additional commits.

Copy link
Author

Choose a reason for hiding this comment

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

Actually, after running a few tests if seems the combination of factors is such that only very very very small angles would lead to a difference, in which case, might as well use pi to evaluate the Abs value in the expression. Will explain thoroughly in comments.


new_soq = soq_item.__class__(
symb= TextBox(text=new_lbl) if isinstance(soq_item.symb, TextBox) else Text(text=new_lbl, fontsize=soq_item.symb.fontsize),
rpos= soq_item.rpos,
ident= soq_item.ident
)
pretty_soqs.append(new_soq)
except (ValueError, TypeError, NameError, sympy.SympifyError):
pretty_soqs.append(soq_item)
else:
pretty_soqs.append(soq_item)

pretty_msd = MusicalScoreData(
max_x=msd.max_x,
max_y=msd.max_y,
soqs=pretty_soqs,
hlines=msd.hlines,
vlines=msd.vlines
)

return pretty_msd
Loading