-
Notifications
You must be signed in to change notification settings - Fork 2.3k
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
Add Subspace Search Variational Quantum Eigensolver (SSVQE) #8792
Conversation
Thank you for opening a new pull request. Before your PR can be merged it will first need to pass continuous integration tests and be reviewed. Sometimes the review process can be slow, so please be patient. While you're waiting, please feel free to review other open PRs. While only a subset of people are authorized to approve pull requests for merging, everyone is encouraged to review open pull requests. Doing reviews helps reduce the burden on the core team and helps make the project's code better for everyone. One or more of the the following people are requested to review this:
|
I am noticing that updating the code to use primitives results in a circular import error that prevents me from running the unit tests. This results in the following trackback error:
My current understanding of what is happening is that this is due to the gradient implementation now being located in |
For the import issue we have the VQD #8640 and VQE #8702 PRs which presumably avoid the problem. Maybe take a look there. I mentioned before this will need to end up in |
Okay, I will take a closer look to see how this circular import can be resolved. This issue seems specific to the gradients module using primitives because commenting out any mention of gradients seems to make this import error go away. I will also see what progress I can make regarding tests that don't require gradients. |
I saw VQE has a test which included using gradient. Maybe that's of help. We should get more of the PRs I mentioned merged in the next day or so, for the imminent release, at which point things should be less busy and hopefully I'll have more time to investigate with you. |
After temporarily commenting out lines involving the estimator gradient, I was able to get the tests adapted from VQD to pass locally. Aside from fixing the circular import, the next thing on the list to do is to write some SSVQE-specific tests if those are called for and to make sure those pass as well. |
The unit tests should cover the functionality of the algorithm so via its public API all the internal code is exercised i.e. tested, that things work as expected for normal as well as error paths. That may well require SSVQE specifics. |
The main aspect that separates this method from others is that the input states are required to be mutually orthogonal. If they are not, the entire method falls apart. Because of this, the input states probably deserve some attention both in terms of tests and handling erroneous user input. Immediately I can see that the method Another thing to consider is: What kind of errors or warning messages should there be (if any) for if the user input for the initial states is not mutually orthogonal? We might want to explicitly check and return an error for small systems (for instance, <= 8 qubits or so) but only show a warning message that the orthogonality was not checked for larger systems since we might not want this computational overhead of calculating the inner products of exponentially large vectors. This checking threshold could also be an optional argument set by the user. Depending on what we do here, this would need a corresponding test. Or we could just leave this to the documentation and leave the checking of orthogonality as user responsibility. |
My take would be to do the check for now have some parameter that allows you to turn it off, for now default it on. If you turn it off you assure orthogonality and if not the algo may not perform as expected. If we want to expose some public method to check such that you can do this once somewhere and then disable the algo check that seems fine too. If we can avoid duplicating code and just have separate checks, where the above would be new code, then that would be better I think. |
Let me know if there are any other changes you recommend making to the code. It looks like it's at the point where it passes all tests so far, but it looks like there are two sets of tests that I don't have permission to run. |
Thanks for the work so far. I'll try and get to this a bit later this week. Things have been rather busy with getting the imminent release 0.22 done, and I am backed up with few other priority things I need to take care of first. |
Pull Request Test Coverage Report for Build 3162211642
💛 - Coveralls |
callback (Callable[[int, np.ndarray, Sequence[float], dict[str, Any]], None] | None): A | ||
function that can access the intermediate data at each optimization step. These data are | ||
the evaluation count, the optimizer parameters for the ansatz, the evaluated mean | ||
energies, and the metadata dictionary. | ||
weight_vector (Sequence[float]): A 1D array of real positive-valued numbers to assign | ||
as weights to each of the expectation values. If ``None``, then SSVQE will default | ||
to [k, k-1, ..., 1]. | ||
initial_states (Sequence[QuantumCircuit]): An optional list of mutually orthogonal | ||
initial states. If ``None``, then SSVQE will set these to be a list of mutually | ||
orthogonal computational basis states. |
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.
Perhaps it might be nicer to maintain the order of these arguments as in the init.
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 agree. I have now changed this.
check_input_states_orthogonality: A boolean that sets whether or not to check | ||
that the value of initial_states passed consists of a mutually orthogonal | ||
set of states. If ``True``, then SSVQE will check that these states are mutually | ||
orthogonal and return an error if they are not. This is set to ``True`` by default, | ||
but setting this to ``False`` may be desirable for larger numbers of qubits to avoid | ||
exponentially large computational overhead before the simulation even starts. |
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.
Shall we also add it to attributes docs above?
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 has now been added.
|
||
self.weight_vector = self._check_weight_vector(self.weight_vector) | ||
|
||
start_time = time() |
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 line seems to be overwritten by line 238.
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, I have now fixed this.
initialized_ansatz_list: list[QuantumCircuit], | ||
operator: BaseOperator | PauliSumOp, | ||
) -> tuple[Callable[[np.ndarray], float | list[float]], dict]: | ||
"""Returns a function handle to evaluates the weighted energy sum at given parameters |
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.
"""Returns a function handle to evaluates the weighted energy sum at given parameters | |
"""Returns a function handle to evaluate the weighted energy sum at given parameters |
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 now fixed this typo.
|
||
return evaluate_weighted_energy_sum | ||
|
||
def _get_evalute_gradient( # check this implementation |
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.
Was the comment addressed already?
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.
Yes, this should now be addressed, so I have removed this comment. If I remember correctly, I added this note to myself because I was still learning how primitives work. There are now tests that cover the primitive implementation of gradients in SSVQE, but if there is more that should be done to make sure this works properly, let me know.
aux_ops = aux_operators | ||
|
||
num_aux_ops = len(aux_ops) | ||
aux_job = self.estimator.run([ansatz] * num_aux_ops, aux_ops) |
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 could surround it with a try
block just like for other primitives in this class.
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, I have added this.
result.eigenvalues = ( | ||
self.estimator.run( | ||
initialized_ansatz_list, [operator] * self.k, [optimizer_result.x] * self.k | ||
) | ||
.result() | ||
.values | ||
) |
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 could surround it with a try block just like for other primitives in this class.
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, I have added this.
.. code-block:: python | ||
|
||
from qiskit import QuantumCircuit | ||
from qiskit.opflow import Z |
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 try to limit the use of opflow now. Could you use Pauli("ZZ")
?
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, I have now changed this.
ref_eval_count = [1, 2, 3] | ||
ref_mean = [[-1.07, -1.44], [-1.45, -1.06], [-1.37, -0.94]] | ||
|
||
np.testing.assert_array_almost_equal(history["eval_count"], ref_eval_count, decimal=0) |
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.
Do we need _almost
part in this check?
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 do not think it is necessary, so I have now removed it.
optimizer: Optimizer | Minimizer = None, | ||
initial_point: Sequence[float] = None, | ||
initial_states: list[QuantumCircuit] = None, | ||
weight_vector: Sequence[float] | Sequence[int] = 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.
If we default them to None
, shall we also add | None
typhint?
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 agree. I have now added this.
that the value of initial_states passed consists of a mutually orthogonal | ||
set of states. If ``True``, then SSVQE will check that these states are mutually |
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.
that the value of initial_states passed consists of a mutually orthogonal | |
set of states. If ``True``, then SSVQE will check that these states are mutually | |
that the value of ``initial_states`` passed consists of a mutually orthogonal | |
set of states. If ``True``, then SSVQE will check that these states are mutually |
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.
changed
If ``None``, then SSVQE will set these to be a list of mutually orthogonal | ||
computational basis states. | ||
weight_vector: An optional list or array of real positive numbers with length | ||
equal to the value of *num_states* to be used in the weighted energy summation |
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.
equal to the value of *num_states* to be used in the weighted energy summation | |
equal to the value of ``num_states`` to be used in the weighted energy summation |
Please check other places.
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 now changed this where I have found it.
Args: | ||
operator: The operator whose energy levels to evaluate. | ||
return_expectation: If True, return the ``ExpectationBase`` expectation converter used | ||
in the construction of the expectation value. Useful e.g. to evaluate other | ||
operators with the same expectation value converter. |
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.
Args are outdated.
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.
updated
Returns: | ||
Weighted energy sum of the hamiltonian of each parameter, and, optionally, the expectation | ||
converter. |
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.
Expectation converter is outdated.
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.
updated
""" | ||
Args: |
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 could also give a code example somewhere in the docs here, just like in the release notes.
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, I will work on adding this. I am also considering re-writing the introduction docstring to be more concise and to the point, rather than being a few large blocks of texts. (Similar in style to what the current docstring for VQE with primitives.)
First I apologise that this has been sitting around a while with no further response from us., Thank you for your interest in contributing to Qiskit. It has however been decided that the algorithms are to be moved out of Qiskit (see #10406) and the plan is a new community repository that will contain these going forwards. As such I am going to close this PR as it stands. If you still have interest in pursuing this then might I suggest a community project in the ecosystem for this. See https://qiskit.org/ecosystem/ and follow/click the "Join the ecosystem" for more information on what that would entail. |
Hi, I will have to re-familiarize myself with this code and make sure that it can pass tests for the most up-to-date versions of Qiskit. I also imagine that since the github repo on which this was kept includes all of qiskit terra in addition to the SSVQE algorithm, it will have to be refactored as a standalone package (with only SSVQE and not the rest of Qiskit terra) that can be installed using pip. Does this sound about right? |
Hi, if this algorithm code is put on a repo it would just be SSEVQE in my mind, and its unit test. A requirements/setup created there can say its dependent on qiskit (qiskit-terra is being renamed to qiskit though the former is still valid for compatibility at present) and on qiskit_algorithms. If you have some setup.py in the repo then people can clone the code and pip install that. Once installed it would be importable/usuable from that package, whatever you call it. And yes you may want to check against the latest algorithms code as it has been a while - they no longer use opflow it now being deprecated. |
Summary
This continues the work of PR #8770 which was closed. This code is now re-written for SSVQE to use primitives. The SSVQE algorithm (https://arxiv.org/abs/1810.09434) is a straightforward generalization of VQE to find low-lying eigenstates of a hermitian operator. Specifically, this pull request implements the version of SSVQE that carries out one optimization procedure using weights. Whereas VQE minimizes the expectation value of an operator with respect to a parameterized ansatz state, SSVQE takes a set of mutually orthogonal initial states, applies the same parameterized ansatz circuit to all of them, then minimizes a weighted sum of the expectation values of the operator with respect to these states. Because inner products of states are invariant under unitary transformations, the set of states remains orthogonal under ideal conditions and thus the global minimum corresponds to the low-lying eigenstates. An example of how a user might use this class:
Details and comments
Because of its similarity to VQE in structure, the code for SSVQE is mostly duplicate code from VQE. It is essentially VQE code that has been modified to handle lists of QuantumCircuits where it is necessary in order to calculate the weighted sum objective function. Tests have been added for this class which are mostly duplicate from the tests for VQD. These two methods are both low-lying eigensolvers, so they should be required to pass similar tests.