Skip to content

Conversation

purva-thakre
Copy link
Contributor

@purva-thakre purva-thakre commented Jul 26, 2021

Examples

  1. Plotting the gray code grid
    Suppose the non-trivial state indices of a two-level unitary are 10 and 31. The gray code sequence of this mapping can be obtained via
plot_gray_code_grid(10,31,5)
plt.show()

image

@BoxiLi
Copy link
Member

BoxiLi commented Jul 26, 2021

By the way, don't pay attention to the Cognitive Complexity requirement in the codeclimate at the moment, I'll raise that bar in a separate PR.

edit: This is done.

@purva-thakre
Copy link
Contributor Author

@BoxiLi Haven't pushed the tests yet. But all the functions do output what they are supposed to (except for the plot).

@purva-thakre
Copy link
Contributor Author

purva-thakre commented Jul 28, 2021

@BoxiLi Here's an example output for decomposing a random 3 qubit unitary into two-level arrays. Earlier when I was referring to finding the non-trivial indices, I was talking about the output arrays from _decompose_to_two_level_arrays.

Right now, I am comparing these to an identity matrix to find the indices that make them two-level unitaries.

Going back to what you said during the meeting, I do think there needs to be a better method of finding the non-trivial indices instead of comparing each and every array value to an identity. I am thinking of defining a function to iterate over the output from this function to make list of smaller 1 qubit arrays and their respective two-level indices.

np.set_printoptions(precision=3)
np.set_printoptions(suppress=True)
two_qubit = rand_unitary(8,dims=[[2] * 3] * 2)
array_decompose = _decompose_to_two_level_arrays(two_qubit, 3)
print(*array_decompose, sep='\n\n')

Edit : Removed large output for above.

@BoxiLi
Copy link
Member

BoxiLi commented Jul 28, 2021

My suggestion is that if we can represent all the information in a compact form, like (single_qubit_unitary_matrix, control_info), then we should never generate the full matrix. Either save them in this compact form or create a Gate class to save them. Generating the full matrix and then identify the useful elements are both very expensive. They should be avoided whenever possible.

It is like entropy in physics, it is easy to go from low entropy to high entropy (Gate->full matrix) but "very hard" to go the other way around :)

Of course, when you want to test the result you will like them to be in the matrix or Qobj form. This should be done byan external function outside the decomposition routine, like the two functions we added to QubitCircuit in the single-qubit gate PR.

@purva-thakre
Copy link
Contributor Author

purva-thakre commented Jul 29, 2021

Either save them in this compact form or create a Gate class to save them. Generating the full matrix and then identify the useful elements are both very expensive.

I think I am going to keep the array output as is for the decomposition to two level arrays because I tend to check their output product quite often for larger number of qubits. I will try to define a function to generate a compact form output (single_qubit_unitary_matrix, control_info) like you suggested. Defining this function will be easier because iterating over the arrays won't be difficult because the indices of each array are always in a range [i, i+j] where j=0,1,...., n**2-1.

I will need to look over how a Gate class could be created to save these. I haven't worked a lot on defining classes, sub-classes and their respective methods. A google search does point me towards the right references for it.

@purva-thakre purva-thakre changed the title Adds gray code decomposition method Adds gray code ordering method Jul 29, 2021
@BoxiLi
Copy link
Member

BoxiLi commented Jul 29, 2021

I think I am going to keep the array output as is for the decomposition to two level arrays because I tend to check their output product quite often for larger number of qubits. I will try to define a function to generate a compact form output (single_qubit_unitary_matrix, control_info) like you suggested.

Surely you can go whichever way you find easier before we finalize things. My experience is that, during the calculation, you will very likely first get the compact information. Like in the following code.

https://github.com/purva-thakre/qutip-qip/blob/cf96bc0b30393bf377f79eadf3e7b841ffabfa8a/src/qutip_qip/decompose/decompose_general_qubit_gate.py#L45-L59

You first get the single-qubit-unitary consisting of a_star/norm_constant etc and index_1, index_2. So you can just save them in a compact form: e.g.

compact_info = (
    np.array([[a_star/norm_constant, b/norm_constant],[b_star/norm_constant, -a/norm_constant]]),
    index_1, 
    index_2
)

And let the function return it if only the compact form is needed. For your convenience, you can add a parameter expand to your function
_decompose_to_two_level_arrays(input_gate, num_qubits, expand=True)
If it is true, you don't return the compact form but continue computing the array as you did so that you can compare the result. I would say it is easier than iterating the array to extract compact information.

@BoxiLi
Copy link
Member

BoxiLi commented Jul 29, 2021

For your purpose, defining a class is equivalent to saving the compact info. If you want to focus on the decomposition algorithm, the compact info representation is sufficient.

The most basic use case of class is to save a bunch of information together under the same name. You can understand it same as a list. Just instead of access each element by the index. you can access them by an attribute name, like Gate.controls, Gate.targets. So in this sense, it is the same as a compact information carrier.

We don't need to support two-level gates in QubitCircuit right now. Adding a class definition is not difficult and pretty easy to add later if you prefer.

@purva-thakre
Copy link
Contributor Author

purva-thakre commented Jul 30, 2021

The most basic use case of class is to save a bunch of information together under the same name

Yep, I am aware of this. My issue is not knowing which attributes would be helpful and then going back and changing them too many times.

For the array output, I will create a expand=True parameter like you suggested.

But for the Gate object, there will need to be a sub-class for two-level unitaries so that there's an easy way to label these controlled gates, provide target/control and the control values. I will double check this because it's possible something could change.

@BoxiLi
Copy link
Member

BoxiLi commented Jul 30, 2021

But for the Gate object, there will need to be a sub-class for two-level unitaries so that there's an easy way to label these controlled gates, provide target/control and the control values.

That also makes sense. Do you need to show them in the circuit plotting? If that is the case, then indeed you have to define a class object. I have to admit that currently define a gate subclass is not very straightforward as Gate is still a bit entangled with QubitCircuit. Let me know if you have any trouble.

Basically, you can always define a method in the subclass that overwrite the method in the parent class. So you have plenty of flexibility there. That is the magic of class.

You don't need to have a very clean solution to this gate subclass. We can remove redundancy later and focus on the core functionality you need at the moment.

Comment on lines +398 to +403
def _decompose_multi_cnot_further():
""" Decomposes output of lemma 7.3 even further by using only 1 particular
cnot with different controls.
# based on lemma 7.2 of https://arxiv.org/abs/quant-ph/9503016
"""
Copy link
Contributor Author

Choose a reason for hiding this comment

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

Some of these functions have incomplete code because locally their output is incorrect. Want to correct the error before committing.

assert expected_gray_code == calc_gray_code


@pytest.mark.xfail
Copy link
Contributor Author

@purva-thakre purva-thakre Aug 2, 2021

Choose a reason for hiding this comment

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

This test fails. See comment for more info --> kept it for later today while working on the general fucntions.

@purva-thakre
Copy link
Contributor Author

closing this for now.

Will add a new general decomposition method PR later this week.

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