Implement rotosolve optimizers#247
Conversation
|
Thanks for your pull request. It looks like this may be your first contribution to a Google open source project (if not, look below for help). Before we can look at your pull request, you'll need to sign a Contributor License Agreement (CLA). 📝 Please visit https://cla.developers.google.com/ to sign. Once you've signed (or fixed any issues), please reply here with What to do if you already signed the CLAIndividual signers
Corporate signers
ℹ️ Googlers: Go here for more info. |
|
@googlebot I signed it! |
1 similar comment
|
@googlebot I signed it! |
|
CLAs look good, thanks! ℹ️ Googlers: Go here for more info. |
MichaelBroughton
left a comment
There was a problem hiding this comment.
Great first pass. Very well documented! Couple of comments and questions about your implementation:
-
You've implemented the
__init__logic of exposingrotosolve_minimizebut there is no test for ensuring the api exposure is correct. Can you add a line inimport_test.pyto verify you are exposing this optimizer correctly ? -
We have
rotosolve_one_parameter_oncethat gets called byrotosolve_all_parameters_once. Do we need the functionality ofrotosolve_one_parameter_onceor can be it be removed and simplified ?
| minimize as rotosolve_minimize) | ||
|
|
||
| # Utils for optimizers. | ||
| from tensorflow_quantum.python.optimizers.utils import (function_factory) |
There was a problem hiding this comment.
Is function factory something you want a user to use ?
There was a problem hiding this comment.
Yes, the user needs to wrap their model with this function_factory to expose the trainable parameters.
| # limitations under the License. | ||
| # ============================================================================== | ||
| """The rotosolve minimization algorithm | ||
| """ |
There was a problem hiding this comment.
Nit: One line module description.
| 'converged', # Scalar boolean tensor indicating whether the minimum | ||
| # was found within tolerance. | ||
| 'num_iterations', # The number of iterations of the BFGS update. | ||
| # The total number of objective evaluations performed. | ||
| 'num_objective_evaluations', | ||
| 'position', # A tensor containing the last argument value found | ||
| # during the search. If the search converged, then | ||
| # this value is the argmin of the objective function. | ||
| # A tensor containing the value of the objective from previous iteration | ||
| 'last_objective_value', | ||
| 'objective_value', # A tensor containing the value of the objective | ||
| # function at the `position`. If the search | ||
| # converged, then this is the (local) minimum of | ||
| # the objective function. | ||
| 'tolerance', # Define the stop criteria. Iteration will stop when the | ||
| # objective value difference between two iterations is smaller than | ||
| # tolerance | ||
| 'solve_param_i', # The parameter index where rotosolve is currently | ||
| # modifying. Reserved for internal use. |
There was a problem hiding this comment.
Formatting is a little hard to follow here. Could you make sure that the comments describing each element are always above the variable they are describing instead of sometimes being to the right and sometimes on top.
There was a problem hiding this comment.
Yes, please use the same comment convention (including indentation) with that of TFP (here)
There was a problem hiding this comment.
Just realized the format checker doesn't happy with the indent like this. Should we change the configuration? @MichaelBroughton @jaeyoo
| The following example demonstrates the Rotosolve optimizer attempting | ||
| to find the minimum for two qubit ansatz expectation value. | ||
|
|
||
| ```python |
There was a problem hiding this comment.
This is a really good useage example. It is a little too complex for something that we want on the API docs. Do you think we could shorten things up a little bit ?
Also can you use >>> and ... like in https://github.com/tensorflow/quantum/blob/master/tensorflow_quantum/python/layers/high_level/pqc.py ?
There was a problem hiding this comment.
Updated in 41b3161. Any changes suggested?
|
|
||
| Args: | ||
| expectation_value_function: A Python callable that accepts | ||
| a point as a real `Tensor` and returns a tuple of `Tensor`s |
There was a problem hiding this comment.
Nit: tf.Tensor. Can you also outline the shape requirements for the input as well as the returned shape you get from the output ?
| tolerance: Scalar `Tensor` of real dtype. Specifies the gradient | ||
| tolerance for the procedure. If the supremum norm of the gradient | ||
| vector is below this number, the algorithm is stopped. | ||
| name: (Optional) Python str. The name prefixed to the ops created |
jaeyoo
left a comment
There was a problem hiding this comment.
Thank you for adding the first TFQ optimizer! I left some comments.
| """Return static shape of tensor `x` if available, | ||
| else `tf.shape(x)`. | ||
| Args: | ||
| x: `Tensor` (already converted). | ||
| Returns: | ||
| Numpy array (if static shape is obtainable), else `Tensor`. | ||
| """ |
There was a problem hiding this comment.
we are using "four space" indent in comments, with a one-line description header with period.
e.g.
"""Static shape of a given tensor `x`.
Return static shape of tensor `x` if available, else `tf.shape(x)`.
Args:
x: A `tf.Tensor` of which we want to know the shape.
Returns:
A numpy array (if static shape is obtainable), else `Tensor`
containing a shape of `x`.
"""| """Return static value of tensor `x` if available, else `x`. | ||
| Args: | ||
| x: `Tensor` (already converted). | ||
| Returns: | ||
| Numpy array (if static value is obtainable), else `Tensor`. | ||
| """ |
| Args: | ||
| expectation_value_function: A Python callable that accepts | ||
| a point as a real `Tensor` and returns a tuple of `Tensor`s |
| point, or points when using batching dimensions, of the search | ||
| procedure. At these points the function value and the gradient | ||
| norm should be finite. | ||
| tolerance: Scalar `Tensor` of real dtype. Specifies the gradient |
| def _rotosolve_one_parameter_once(state): | ||
| """ | ||
| Rotosolve a single parameter. | ||
| """ |
There was a problem hiding this comment.
- This internal function is hard to test. could you bring it out and write a test code?
- one line description + arguments description please.
There was a problem hiding this comment.
Well, this function is written here purely to help people read it, and it's a bit difficult to extract it out since too many variables are used in this closure. Also, the rest of the code is nothing but iterate over parameters and calling this function. Do you think it is okay to leave it as an internal function?
| """Tests for optimizer utils""" | ||
|
|
||
| def test_function_factory(self): | ||
| """Test the function_factory""" | ||
|
|
||
| class LinearModel(object): | ||
| """ A simple tensorflow linear model""" |
There was a problem hiding this comment.
period please, and please trim the meaningless white space in front of sentence.
|
|
||
| result = rotosolve_minimizer.minimize( | ||
| rotosolve_minimizer.function_factory( | ||
| model, hinge_loss, x_circ, Y), np.random.rand([2]) |
There was a problem hiding this comment.
I am a little bit afraid of this kind of new and independent optimizer design. I understand that this design is conventional in TF Probability, but Tensorflow Keras provides its own Optimizer class to be inherited (e.g. see AdamOptimizer here).
To me, TFP's optimizer design seems like Keras' optimizer + custom trainer. I think it can't be runnable with keras model fit. I feel it's ok that it works well for saving weights and reloading to use the model after training, but I am not sure for the saving models with keras save function in the future. (it can save optimizer together)
What do you think of, @MichaelBroughton?
There was a problem hiding this comment.
Making a new Keras Optimizers requires that optimizer to be gradient based. This new approach lets us depart from those standards. Rotosolve doesn't calculate gradients, just like nelder-mead in TFP doesn't use gradients.
You're right that it can't be runnable with keras model fit, but this seems to be the best workaround. I could ask the TFP people about this too. What do you think ?
There was a problem hiding this comment.
Yes, I've checked gradient-free optimizers around several libraries, it is natural to implement gradient-free optimizers in an independent design. I thought the optimizer was designed enough to support both, but I was wrong. Then I agree with TFP's approach for the seamless ecosystem :) Thank you for your idea!
| is also of shape `[..., n]` like the input value to the function. | ||
| This must be a linear combination of quantum measurement | ||
| expectation value, otherwise this algorithm cannot work. | ||
| initial_position: Real `Tensor` of shape `[..., n]`. The starting |
| 'converged', # Scalar boolean tensor indicating whether the minimum | ||
| # was found within tolerance. | ||
| 'num_iterations', # The number of iterations of the BFGS update. | ||
| # The total number of objective evaluations performed. | ||
| 'num_objective_evaluations', | ||
| 'position', # A tensor containing the last argument value found | ||
| # during the search. If the search converged, then | ||
| # this value is the argmin of the objective function. | ||
| # A tensor containing the value of the objective from previous iteration | ||
| 'last_objective_value', | ||
| 'objective_value', # A tensor containing the value of the objective | ||
| # function at the `position`. If the search | ||
| # converged, then this is the (local) minimum of | ||
| # the objective function. | ||
| 'tolerance', # Define the stop criteria. Iteration will stop when the | ||
| # objective value difference between two iterations is smaller than | ||
| # tolerance | ||
| 'solve_param_i', # The parameter index where rotosolve is currently | ||
| # modifying. Reserved for internal use. |
There was a problem hiding this comment.
Yes, please use the same comment convention (including indentation) with that of TFP (here)
|
|
||
|
|
||
| def _get_initial_state(initial_position, tolerance, expectation_value_function): | ||
| """Create RotosolveOptimizerResults with initial state of search .""" |
There was a problem hiding this comment.
please remove white space between the last character and period.
jaeyoo
left a comment
There was a problem hiding this comment.
Sorry for confusing, I request changes :)
|
Thank you very much. I'll fix these issues soon. |
This comment has been minimized.
This comment has been minimized.
Thanks for the comment again.
|
|
Thanks for the review, I have made most of the changes required. Can I have a bit more in detail how can I implement point 3 in best practice?
Also, I found the tests seems not working properly. I found some error when I execute the tests manually but then I try to run it with bazel it just simply passed. Can you help me have a look if I made some mistake which causes the tests got ignored? |
This would mean adding at least two more tests:
Regarding the tests not working, can you post a small snippet or add in the changes with a comment to indicate which ones are not working ? |
Please find all the changes in 25ecc76 . Before this commit the code should yield error since I called a function that does not even exist |
A new test called
I have changed the ordinary |
|
I'm guessing your tests aren't working because you don't have this at the bottom of your test module: if __name__ == "__main__":
tf.test.main()Could you give that a try ? |
Thanks for the help. I have fixed all the tests. |
MichaelBroughton
left a comment
There was a problem hiding this comment.
Alrighty, this is ready to merge! Once we get these last round of minor nits changed, I can go ahead and merge this in :D
| from __future__ import absolute_import | ||
| from __future__ import division | ||
| from __future__ import print_function |
There was a problem hiding this comment.
Are these needed ?
|
|
||
| arXiv:1905.09692 | ||
|
|
||
| ### Usage: |
There was a problem hiding this comment.
Still have ### Usage: . We can just get rid of that altogether.
| The following example demonstrates the Rotosolve optimizer attempting | ||
| to find the minimum for two qubit ansatz expectation value. | ||
|
|
||
| Here is an example of optimize a function which consists summation of | ||
| a few sinusoids. |
There was a problem hiding this comment.
Do we want both of these descriptions or just one ?
| >>> import tensorflow_quantum as tfq | ||
| >>> import numpy as np | ||
| >>> import tensorflow as tf |
There was a problem hiding this comment.
Don't need these in the snippet, we assume people have done this already :)
| >>> min_value = -tf.math.reduce_sum(tf.abs(coefficient)) | ||
| >>> func = lambda x:tf.math.reduce_sum(tf.sin(x) * coefficient) | ||
| >>> # Optimize the function with rotosolve, start with random parameters | ||
| >>> result = tfq.optimizers.rotosolve_minimizer.minimize( \ |
There was a problem hiding this comment.
Don't need the \ can just do result = tfq.optimizers.rotosolve_minimizer.minimize(
There was a problem hiding this comment.
Also shouldn't the function call be tfq.optimizers.rotosolve_minimize( and not tfq.optimizers.rotosolve_minimizer.minimize( ?
| >>> print(result.converged) | ||
| tf.Tensor(True, shape=(), dtype=bool) | ||
| >>> print(result.objective_value) | ||
| tf.Tensor(-4.7045116, shape=(), dtype=float32) |
There was a problem hiding this comment.
In the doctests snippets we try to prefer just giving one output, and not using the print statement unless we really need to, would you mind changing these lines to just:
>>> result.objective_value
tf.Tensor(-4.7045116, shape=(), dtype=float32)| from __future__ import absolute_import | ||
| from __future__ import division | ||
| from __future__ import print_function | ||
| import math |
There was a problem hiding this comment.
math.pi is used here but np.pi is used in the tests. Could we change this module to also use np.pi just for consistency ?
Thanks for the review! I have fixed all these problems. |
MichaelBroughton
left a comment
There was a problem hiding this comment.
Ready to merge :D
* Implement rotosolve optimizers * Fix import issue * Fix comments layout * Modify rotosolve example docstring * Remove meaningless whitespaces * Correct types in docstring * Change docstring indents * Trying to fix pytest faliure * Remove extra spances and change docstring layout * Fix typo in utils_test.py * Check the input / output shape requirement for rotosolve expectation value function * Add docstring for internal functions * Fix formats * Fix dependency * Fix import * Modify docstring layout * Fix format * Remove function factory (tensorflow#1) Remove function factory. * Fix format * Fix lint * Fix tests * Remove utils * Remove utils * Fix format and typos * Update examples * Fix lint * Fix typo * Switch to scatter_nd * Update rotosolve test * Reformat code * Update tests and comments * Update test * Fix lint * fix lint * Modify build docs, modify comments, change test to use built-in hinge loss * Fix format of build_docs.py * Change name of object value from previous iteration * Fix comments examples * Fix lint * Fix lint * Test tolerance criteria * Fix format * Fix tests * Fix format * Fix format * Change test * Fix tests * Fix typo in comment * Fix minor problems * Fix lint * Remove unused input * Update comment
Update version to 0.6.0
No description provided.