-
Notifications
You must be signed in to change notification settings - Fork 51
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
Remove _t_complexity_
on MultiAnd
and support symbolic cvs
#1015
Remove _t_complexity_
on MultiAnd
and support symbolic cvs
#1015
Conversation
qualtran/bloqs/mcmt/and_bloq.py
Outdated
@@ -260,26 +256,38 @@ class MultiAnd(Bloq): | |||
Args: | |||
cvs: A tuple of control variable settings. Each entry specifies whether that | |||
control line is a "positive" control (`cv[i]=1`) or a "negative" control `0`. | |||
If a (symbolic) integer is passed, assumes the control values to be all 1's. |
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.
Is this subject to change / worth thinking about? I guess I haven't really thought about what symbolic controls mean.
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.
Its easy to argue for the case where the control spec is simply all 1s; and thats mostly the case I want to update everywhere we currently expect cvs: Tuple[int, ...]
In future, we'd ideally like these bloqs to be able to take any cvs: CtrlSpec
and it would be more nuanced to think about what a symbolic controlled spec would mean at that point. But for now, I just want to remove blockers for enabling symbolic resource estimation for higher level bloqs like state preparation via alias sampling / reflection etc.
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.
One change which may be necessary is that right now, we always define the control register to be of time Register('controls', QBit(), shape=(len(self.cvs),))
whereas we'll have to change it to be of type Register('controls', QAny(len(self.cvs)), shape=())
to enable self.cvs
to be symbolic. But I'm not sure what the repercussions would be
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'm not super thrilled about having one attribute named cvs
that can represent either a list of control values or the number of controls.
def __init__(self, *, cvs=None, n_ctrl=None):
if cvs is not Nont and n_ctrl is not None: raise ValueError()
self._cvs = cvs
self._n_ctrl = n_ctrl
@property
def cvs():
...
@property
def n_ctrl():
..
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.
Is there a way to do it without a custom init with frozendataclasses? I can also do what I did for QROM
; i.e. rename cvs
-> cvs_or_n_ctrl: Union[]
and then expose cvs
and n_ctrl
as properties.
We'll have to change the call sites which construct the bloq via keyword arguments; but that should be fine right now?
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 think you can do this with attrs by prepending an underscore to the field name and setting frozen(kwargs=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.
Then it would look something like this:
@frozen
class MultiAnd(Bloq):
_cvs: Optional[Tuple[SymbolicInt, ...]] = field(
converter=_to_tuple_or_none, kw_only=True, default=None
)
_n_ctrls: Optional[HasLength] = field(
converter=_to_has_length_or_none, kw_only=True, default=None
)
def __attrs_post_init__(self):
assert (self._cvs is None) ^ (self._n_ctrls is None)
We'll have to add this logic everywhere we use cvs: Tuple[int, ...]
right now. I'm fine doing this, but there's definitely more going on in this approach than having a single Union
which must not be 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.
Also, the main reason for defining a new Length
object is so that we can set cvs
to be of type Length
and len(cvs)
still works.
The above approach where we maintain both n_ctrls
and cvs
independently everywhere wouldn't require introducing Length
since n_ctrls
can just be Symbolic
.
At other places, we typically have a union of Shaped
and numpy arrays so that object.shape
works irrespective of the type of object
. I think following a similar pattern here would keep things consistent.
Tl;Dr - let's maintain cvs: Union[HasLength, Tuple[int, ...]]
or cvs_or_n_ctrls: Union[HasLength, Tuple[int, ...]])
? WDYT?
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.
oh oh oh when I first read it as Union[Sequence, Length]
I was like, these are two different things. But HasLength
... yeah just one field named cvs
is good
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 update the docstring as well. instead of "if a symbolic integer is passed" say "if a HasLength object"
qualtran/bloqs/mcmt/and_bloq.py
Outdated
def bitsize(self) -> SymbolicInt: | ||
return self.cvs.bitsize if isinstance(self.cvs, Length) else len(self.cvs) |
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.
n_ctrls
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 still think this should be n_ctrls
or similar (rather than bitsize)
qualtran/bloqs/mcmt/and_bloq.py
Outdated
@property | ||
def control_values(self) -> Tuple[SymbolicInt, ...]: | ||
if isinstance(self.cvs, Length): | ||
raise ValueError(f"{self.cvs} is symbolic") | ||
return self.cvs |
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.
cvs
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 still think this should be meaningfully disambiguated. and_bloq.cvs
vs and_bloq.control_values
...
maybe concrete_cvs
? idk
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.
we have is_symbolic
in the symbolic utils. What's the opposite of symbolic? we should pick a word for it and use that word consistently throughout the library
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 can use whatever you suggest -- I trust your expertise on the English language more than mine :)
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.
let's go with "concrete" https://en.wiktionary.org/wiki/concrete
qualtran/symbolics/types.py
Outdated
class Length: | ||
"""Symbolic value for an object that has a length.""" | ||
|
||
bitsize: SymbolicInt |
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 doesn't have to represent a bitwidth, right? it can be the length of anything. Perhaps n
.
qualtran/symbolics/types.py
Outdated
class Length: | ||
"""Symbolic value for an object that has a length.""" |
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.
a length is different than something that has a length. Analogously, above we use Shaped
instead of Shape
. Sadly, I think the English language slightly fails us here. Maybe HasLength
? HasLen
?
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 change to HasLength
qualtran/symbolics/types.py
Outdated
@@ -46,6 +46,19 @@ def is_symbolic(self): | |||
return True | |||
|
|||
|
|||
@frozen | |||
class Length: |
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.
is this a subclass of Shaped
?
qualtran/bloqs/mcmt/and_bloq.py
Outdated
if isinstance(x, sympy.Expr): | ||
return Length(x) |
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.
wait, shouldn't we make users pass a HasLength
object explicitly? This is how we do Shaped
if I'm not mistaken
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.
Okay that's right, I'll change 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.
lgtm with the changes brought up in the comments
Addressed all the comments. Also realized that its a bit annoying to have |
@tanujkhattar fwiw, I faced the same issue when implementing Qualtran/qualtran/symbolics/math_funcs.py Lines 102 to 105 in 5aabc66
We should extend this by adding |
and/or make HasLength be a subclass of Shaped |
Part of a larger effort to better support symbolic resource estimation across bloqs. I will update the
cvs
parameter on other bloqs in follow-up PRs.