Repository: Qiskit/qiskit

Issue:
Fix optimal_num_iterations
<!-- ⚠️ If you do not respect this template, your issue will be closed -->
<!-- ⚠️ Make sure to browse the opened and closed issues -->

### Information

- **Qiskit Terra version **:  master 
- **Python version**:  3.8.5
- **Operating system**: MacOS Catalina 10.15.7

### What is the current behavior?

The current code is based on the inequality (6.17) p254 of Nielsen & Chung (3rd printing 2017).
This is actually an 'elegant upper bound' for the number of iterations required, which is not necessarily optimal. 

https://github.com/Qiskit/qiskit-terra/blob/0fc83f44a6e80969875c738b2cee7bc33223e45f/qiskit/algorithms/amplitude_amplifiers/grover.py#L260-L270

### Steps to reproduce the problem



### What is the expected behavior?



### Suggested solutions

The optimal number is obtained with the equality of (6.15) p253. 

```python
amplitude = np.sqrt(num_solutions / 2 ** num_qubits)
optimal = round(np.arccos(amplitude) / (2 * np.arcsin(amplitude)))
 ```

For instance, the upper bound and the optimal are different as follows for `num_qubits=7`

```python
num_qubits = 7
for num_solutions in range(1, 2 ** num_qubits):
    upper = math.floor(np.pi * np.sqrt(2 ** num_qubits / num_solutions) / 4)
    amplitude = np.sqrt(num_solutions / 2 ** num_qubits)
    optimal = round(np.arccos(amplitude) / (2 * np.arcsin(amplitude)))
    if (upper != optimal):
        print('{0:>4d} {1:>4d} {2:>4d}'.format(num_solutions, upper, optimal))
```
  
```
  19    2    1
  64    1    0
  65    1    0
  66    1    0
  67    1    0
  68    1    0
  69    1    0
  70    1    0
  71    1    0
  72    1    0
  73    1    0
  74    1    0
  75    1    0
  76    1    0
  77    1    0
  78    1    0
```



Discussion:
@tamiya-onodera 
Thank you for raising this issue. 
I agree with that the current implementation in `optimal_num_iterations` actually represents the upper bound of the iteration ((6.17) p254 of Nielsen & Chung) and eq. 6.15 should be used for the number of the optimal iterations.
If you'd like, could you please fix the current implementation?
@a-matsuo
Thanks for your comments.    I will do it.  

Resolving patch:
diff --git a/qiskit/algorithms/amplitude_amplifiers/grover.py b/qiskit/algorithms/amplitude_amplifiers/grover.py
--- a/qiskit/algorithms/amplitude_amplifiers/grover.py
+++ b/qiskit/algorithms/amplitude_amplifiers/grover.py
@@ -14,7 +14,6 @@
 
 import itertools
 import logging
-import math
 import operator
 from typing import Iterator, List, Optional, Union
 
@@ -274,7 +273,8 @@ def optimal_num_iterations(num_solutions: int, num_qubits: int) -> int:
         Returns:
             The optimal number of iterations for Grover's algorithm to succeed.
         """
-        return math.floor(np.pi * np.sqrt(2 ** num_qubits / num_solutions) / 4)
+        amplitude = np.sqrt(num_solutions / 2 ** num_qubits)
+        return round(np.arccos(amplitude) / (2 * np.arcsin(amplitude)))
 
     def construct_circuit(
         self, problem: AmplificationProblem, power: Optional[int] = None, measurement: bool = False


Test patch:
diff --git a/test/python/algorithms/test_grover.py b/test/python/algorithms/test_grover.py
--- a/test/python/algorithms/test_grover.py
+++ b/test/python/algorithms/test_grover.py
@@ -184,6 +184,15 @@ def test_run_custom_grover_operator(self):
         ret = grover.amplify(problem)
         self.assertIn(ret.top_measurement, ["11"])
 
+    def test_optimal_num_iterations(self):
+        """Test optimal_num_iterations"""
+        num_qubits = 7
+        for num_solutions in range(1, 2 ** num_qubits):
+            amplitude = np.sqrt(num_solutions / 2 ** num_qubits)
+            expected = round(np.arccos(amplitude) / (2 * np.arcsin(amplitude)))
+            actual = Grover.optimal_num_iterations(num_solutions, num_qubits)
+        self.assertEqual(actual, expected)
+
     def test_construct_circuit(self):
         """Test construct_circuit"""
         oracle = QuantumCircuit(2)