Skip to content

Adding support for mixed basis during measurement#44

Open
akshaydaf wants to merge 4 commits into
gt-tinker:mainfrom
akshaydaf:mixedbasis
Open

Adding support for mixed basis during measurement#44
akshaydaf wants to merge 4 commits into
gt-tinker:mainfrom
akshaydaf:mixedbasis

Conversation

@akshaydaf

Copy link
Copy Markdown
Contributor

Previously mixed bases such as:

@qpu
def with_phase_60() -> bit[2]:
    return '00' | {'0p', '0m', '1p', '1m'@60}.measure

and

@qpu
def no_phase() -> bit[2]:
    return '00' | {'0p', '0m', '1p', '1m'}.measure

were not working. This change introduces a factor_separable function, which takes in a BasisLiteral and returns a BasisTensor, that the existing lowering code already handles. Additionally, an update was made to the measure path to ignore phases.

@ausbin ausbin left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

I think we should discuss how this works on a call before you work on this further, since I don't want to sink too much of your time into this code if it is using an unsound approach.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

This is a great test, can you change it to being an integration test?

Here is an example test you can imitate:

  1. program itself (equivalent to this file you've written, except the if __name__ == '__main__' code is inside a function called test() instead (i usually make it take a shots argument): https://github.com/gt-tinker/qwerty/blob/main/qwerty_pyrt/python/qwerty/tests/integ/meta/float_expr.py
  2. the "unit" test that calls the code and verifies the output: https://github.com/gt-tinker/qwerty/blob/main/qwerty_pyrt/python/qwerty/tests/integration_tests.py#L429-L436

Comment thread qwerty_ast/src/ast/qpu.rs
rebuild!(Basis, self, canonicalize)
}

pub fn strip_phases(self) -> Self {

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

I may be missing something, but I don't see why we should remove vector phases.

I added code to remove (outer) phases for span checking, since that information is redundant with respect to span ($e^{i \theta}\ket{\mathrm{bv}}$ has the same span as $\ket{\mathrm{bv}}$), but for basis translation synthesis, the phases are actually important. For example, {'1'} >> {'1'} is an identity, whereas {'1'} >> {-'1'} is a Z gate

Comment thread qwerty_ast/src/ast/qpu.rs

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

For any recursive code you add, can you write a TODO saying e.g. // TODO: use gen_rebuild to this non-recursively? I don't want to make you worry about it right now, but tldr recursive code can cause compile-time stack overflows for deep ASTs. We have crazy macros to work around that, but it's not worth your time atm considering the qwerty_ast_to_mlir code is already comically recursive

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

I don't know exactly why this change that is already in main is showing up in this diff. I was going to blame GitHub, but I can reproduce it with git diff origin/main...akshay/mixedbasis, yet not with git diff origin/main..akshay/mixedbasis (note the difference in number of periods). Honestly, I don't mean this disrespectfully, but I think the history for this mixedbasis branch is just convoluted. Let's just ignore this part of the diff for this PR (I've verified it's not an actual change versus main), but you can avoid it in the future by creating a fresh branch off main and cherry-picking your changes, or by rebasing against main, I believe.

Let me know if I'm being confusing here

fn ast_basis_to_mlir(basis: &Basis) -> MlirBasis {
let basis_elements = basis.to_explicit().canonicalize().to_vec();
let basis_elements = basis.to_explicit().canonicalize().to_vec().into_iter()
.flat_map(|b| b.factor_separable().to_vec()).collect();

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Is there any reason we shouldn't do this factoring as part of canonicalization? Here's what we mean by "canonicalization", by the way: https://sunfishcode.github.io/blog/2018/10/22/Canonicalization.html

Do you see what I mean? If not let me know

The only problem I can think of is that it might confuse the type checker when doing span checking, but hopefully in that case, we just fix the span checker. (I can do that if it happens)

Comment thread qwerty_ast/src/ast/qpu.rs
}
}

pub fn factor_separable(self) -> Self {

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Can you add a doc comment for this?

Comment thread qwerty_ast/src/ast/qpu.rs

let mut rows: Vec<Vec<Vector>> = Vec::with_capacity(vecs.len());
for v in &vecs {
match v.clone().canonicalize() {

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

If we mandate that canonicalization happens on the vectors before this (in the doc comment for this method), then we can skip this clone & canonicalization, since the vectors will already be canonicalized

Comment thread qwerty_ast/src/ast/qpu.rs
return Basis::BasisLiteral { vecs, dbg };
}

let mut letters: Vec<Vec<Vector>> = vec![Vec::new(); n];

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

It would be helpful to leave a comment with an example describing what this variable is holding

Comment thread qwerty_ast/src/ast/qpu.rs
Comment on lines +2065 to +2066
if !letters[i].iter().any(|l| l.approx_equal(atom)) {
letters[i].push(atom.clone());

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Dang, I am trying pretty hard, but I can't figure out what this is doing. Maybe we should discuss in a call

Comment thread qwerty_ast/src/ast/qpu.rs

let is_factorable = rows.len() == expected.len()
&& rows.iter().zip(&expected).all(|(r, e)| {
r.len() == e.len() && r.iter().zip(e).all(|(a, b)| a.approx_equal(b))

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

I almost get it I think, but I just can't figure out what this does. We should talk on a call I think

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.

2 participants