Skip to content
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

Alternating Ansatz Feature #85

Merged
merged 17 commits into from
Nov 17, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions docs/source/index.rst
Original file line number Diff line number Diff line change
Expand Up @@ -240,6 +240,7 @@ Contents
notebooks/08_results_example.ipynb
notebooks/09_RQAOA_example.ipynb
notebooks/10_workflows_on_Amazon_braket.ipynb
notebooks/11_Mixer_example.ipynb


.. toctree::
Expand Down
2 changes: 1 addition & 1 deletion examples/04_qaoa_variational_parameters.ipynb
Original file line number Diff line number Diff line change
Expand Up @@ -228,7 +228,7 @@
"source": [
"# Create Circuit Params Class\n",
"mixer_hamiltonian = X_mixer_hamiltonian(n_qubits = 3)\n",
"qaoa_circuit_params = QAOACircuitParams(cost_hamiltonian = hamiltonian, mixer_hamiltonian = mixer_hamiltonian, p=1)"
"qaoa_circuit_params = QAOACircuitParams(cost_hamiltonian = hamiltonian, mixer_block = mixer_hamiltonian, p=1)"
]
},
{
Expand Down
1,124 changes: 1,124 additions & 0 deletions examples/11_Mixer_example.ipynb

Large diffs are not rendered by default.

183 changes: 141 additions & 42 deletions openqaoa/qaoa_parameters/baseparams.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,13 +12,16 @@
# See the License for the specific language governing permissions and
# limitations under the License.

from __future__ import annotations

from abc import ABC
from typing import List, Union, Tuple, Any, Callable, Iterable, Type
import numpy as np

from .operators import Hamiltonian
from .hamiltonianmapper import HamiltonianMapper

from .gatemap import GateMap


def _is_iterable_empty(in_iterable):
Expand Down Expand Up @@ -112,91 +115,180 @@ def __init__(self,
algorithm: str):
self.algorithm = algorithm


class QAOACircuitParams(VQACircuitParams):

"""
Create the problem attributes consisting of the Hamiltonian and Hamiltonian
squared as dictionaries, QAOA 'p' value and other specific parameters.
Create the problem attributes consisting of the Hamiltonian, QAOA 'p' value and other specific parameters.

Parameters
----------
cost_hamiltonian: `Hamiltonian`
cost_hamiltonian: `Hamiltonian`
The cost hamiltonian of the problem the user is trying to solve.

mixer_hamiltonian: `Hamiltonian`
mixer_block: `Union[List[RotationGateMap], Hamiltonian]`
The mixer hamiltonian or a list of initialised RotationGateMap objects that defines the gates to be used within the "mixer part" of the circuit.

p: `int`
Number of QAOA layers; defaults to 1 if not specified

mixer_coeffs: `List[float]`
A list containing coefficients for each mixer GateMap. The order of the coefficients should follow the order of the GateMaps provided in the relevant gate block. This input isnt required if the input mixer block is of type Hamiltonian.

Attributes
----------
All the parameters specified above are initialised as attributes in __init__
cost_hamiltonian: `Hamiltonian`

qureg: `List[int]`

cost_block_coeffs: `List[float]`

cost_single_qubit_coeffs: `List[float]`

cost_qubits_singles: `List[str]`

cost_pair_qubit_coeffs: `List[float]`

cost_qubits_pairs: `List[str]`

mixer_block_coeffs: `List[float]`

Properties
----------
hamiltonian:
Returns the problem Hamiltonian as a dictionary
hamiltonian_squared:
Returns the problem Hamiltonian Squared as a dictionary
cost_block: `List[RotationGateMap]`

mixer_block: `List[RotationGateMap]`

abstract_circuit: `List[RotationGateMap]`
"""

def __init__(self,
cost_hamiltonian: Hamiltonian,
mixer_hamiltonian: Hamiltonian,
p: int):

super().__init__(algorithm='QAOA')
self.cost_hamiltonian = cost_hamiltonian
self.mixer_hamiltonian = mixer_hamiltonian
self.qureg = cost_hamiltonian.qureg

def __init__(self,
cost_hamiltonian: Hamiltonian,
mixer_block: Union[List[RotationGateMap], Hamiltonian],
p: int,
mixer_coeffs: List[float] = []):

super().__init__(algorithm = "QAOA")

self.p = p

self.qureg = cost_hamiltonian.qureg
self.cost_block_coeffs = cost_hamiltonian.coeffs

try:
self.mixer_block_coeffs = mixer_block.coeffs
except AttributeError:
self.mixer_block_coeffs = mixer_coeffs

# Needed in the BaseBackend to compute exact_solution, cost_funtion method
# and bitstring_energy
self.cost_hamiltonian = cost_hamiltonian
self.cost_block = cost_hamiltonian
self.mixer_block = mixer_block

(self.cost_single_qubit_coeffs, self.cost_pair_qubit_coeffs, self.cost_qubits_singles, self.cost_qubits_pairs) = self._assign_coefficients(self.cost_block[0], self.cost_block_coeffs)

(self.mixer_single_qubit_coeffs, self.mixer_pair_qubit_coeffs, self.mixer_qubits_singles, self.mixer_qubits_pairs) = self._assign_coefficients(self.mixer_block[0], self.mixer_block_coeffs)

def _assign_coefficients(self,
input_block: List[RotationGateMap],
input_coeffs: List[float]) -> None:

"""
Splits the coefficients and gatemaps into qubit singles and qubit pairs.
"""

single_qubit_coeffs = []
pair_qubit_coeffs = []
qubit_singles = []
qubit_pairs = []

if len(input_block) != len(input_coeffs):

raise ValueError('The number of terms/gatemaps must match the number of coefficients provided.')

for each_gatemap, each_coeff in zip(input_block, input_coeffs):

if each_gatemap.pauli_label[0] == '1q':
single_qubit_coeffs.append(each_coeff)
# Giving a string name to each gatemap (?)
qubit_singles.append(type(each_gatemap).__name__)
elif each_gatemap.pauli_label[0] == '2q':
pair_qubit_coeffs.append(each_coeff)
qubit_pairs.append(type(each_gatemap).__name__)

return (single_qubit_coeffs, pair_qubit_coeffs, qubit_singles, qubit_pairs)

@property
def cost_block(self):

return self._cost_block

@cost_block.setter
def cost_block(self, input_object: Hamiltonian) -> None:

self._cost_block = HamiltonianMapper.repeat_gate_maps(input_object, 'cost', self.p)

@property
def mixer_block(self):

return self._mixer_block

@mixer_block.setter
def mixer_block(self, input_object: Union[List[RotationGateMap], Hamiltonian]) -> None:

if type(input_object) == Hamiltonian:

self._mixer_block = HamiltonianMapper.repeat_gate_maps(input_object, 'mixer', self.p)

else:

self._mixer_block = HamiltonianMapper.repeat_gate_maps_from_gate_map_list(
input_object, 'mixer', self.p)

def __repr__(self):

"""Return an overview over the parameters and hyperparameters

Todo
----
Split this into ``__repr__`` and ``__str__`` with a more verbose
output in ``__repr__``.
"""

string = "Circuit Parameters:\n"
string += "\tp: " + str(self.p) + "\n"
string += "\tregister: " + str(self.qureg) + "\n" + "\n"

string += "Cost Hamiltonian:\n"
string += "\tcost_qubits_singles: " + \
str(self.cost_hamiltonian.qubits_singles) + "\n"
str(self.cost_qubits_singles) + "\n"
string += "\tcost_single_qubit_coeffs: " + \
str(self.cost_hamiltonian.single_qubit_coeffs) + "\n"
str(self.cost_single_qubit_coeffs) + "\n"
string += "\tcost_qubits_pairs: " + \
str(self.cost_hamiltonian.qubits_pairs) + "\n"
str(self.cost_qubits_pairs) + "\n"
string += "\tcost_pair_qubit_coeffs: " + \
str(self.cost_hamiltonian.pair_qubit_coeffs) + "\n" + "\n"
str(self.cost_pair_qubit_coeffs) + "\n" + "\n"

string += "Mixer Hamiltonian:\n"
string += "\tmixer_qubits_singles: " + \
str(self.mixer_hamiltonian.qubits_singles) + "\n"
str(self.mixer_qubits_singles) + "\n"
string += "\tmixer_single_qubit_coeffs: " + \
str(self.mixer_hamiltonian.single_qubit_coeffs) + "\n"
str(self.mixer_single_qubit_coeffs) + "\n"
string += "\tmixer_qubits_pairs: " + \
str(self.mixer_hamiltonian.qubits_pairs) + "\n"
str(self.mixer_qubits_pairs) + "\n"
string += "\tmixer_pair_qubit_coeffs: " + \
str(self.mixer_hamiltonian.pair_qubit_coeffs) + "\n"
str(self.mixer_pair_qubit_coeffs) + "\n"

return string

@property
def abstract_circuit(self) -> List:

cost_gate_map_list = HamiltonianMapper.repeat_gate_maps(
self.cost_hamiltonian, 'cost', self.p)
mixer_gate_map_list = HamiltonianMapper.repeat_gate_maps(
self.mixer_hamiltonian, 'mixer', self.p)

def abstract_circuit(self) -> List[RotationGateMap]:

_abstract_circuit = []
for each_p in range(self.p):
_abstract_circuit.extend(cost_gate_map_list[each_p])
_abstract_circuit.extend(mixer_gate_map_list[each_p])
_abstract_circuit.extend(self.cost_block[each_p])
_abstract_circuit.extend(self.mixer_block[each_p])

return _abstract_circuit

Expand Down Expand Up @@ -226,10 +318,17 @@ def __init__(self, qaoa_circuit_params: QAOACircuitParams):

self.qaoa_circuit_params = qaoa_circuit_params
self.p = self.qaoa_circuit_params.p
self.cost_1q_coeffs = qaoa_circuit_params.cost_hamiltonian.single_qubit_coeffs
self.cost_2q_coeffs = qaoa_circuit_params.cost_hamiltonian.pair_qubit_coeffs
self.mixer_1q_coeffs = qaoa_circuit_params.mixer_hamiltonian.single_qubit_coeffs
self.mixer_2q_coeffs = qaoa_circuit_params.mixer_hamiltonian.pair_qubit_coeffs

try:
self.cost_1q_coeffs = qaoa_circuit_params.cost_single_qubit_coeffs
self.cost_2q_coeffs = qaoa_circuit_params.cost_pair_qubit_coeffs
self.mixer_1q_coeffs = qaoa_circuit_params.mixer_single_qubit_coeffs
self.mixer_2q_coeffs = qaoa_circuit_params.mixer_pair_qubit_coeffs
except AttributeError:
self.cost_1q_coeffs = qaoa_circuit_params.cost_hamiltonian.single_qubit_coeffs
self.cost_2q_coeffs = qaoa_circuit_params.cost_hamiltonian.pair_qubit_coeffs
self.mixer_1q_coeffs = qaoa_circuit_params.mixer_hamiltonian.single_qubit_coeffs
self.mixer_2q_coeffs = qaoa_circuit_params.mixer_hamiltonian.pair_qubit_coeffs

def __len__(self):
"""
Expand Down
24 changes: 12 additions & 12 deletions openqaoa/qaoa_parameters/extendedparams.py
Original file line number Diff line number Diff line change
Expand Up @@ -168,10 +168,10 @@ def linear_ramp_from_hamiltonian(cls,

dt = time / p

n_gamma_singles = len(qaoa_circuit_params.cost_hamiltonian.single_qubit_coeffs)
n_gamma_pairs = len(qaoa_circuit_params.cost_hamiltonian.pair_qubit_coeffs)
n_beta_singles = len(qaoa_circuit_params.mixer_hamiltonian.single_qubit_coeffs)
n_beta_pairs = len(qaoa_circuit_params.mixer_hamiltonian.pair_qubit_coeffs)
n_gamma_singles = len(qaoa_circuit_params.cost_single_qubit_coeffs)
n_gamma_pairs = len(qaoa_circuit_params.cost_pair_qubit_coeffs)
n_beta_singles = len(qaoa_circuit_params.mixer_single_qubit_coeffs)
n_beta_pairs = len(qaoa_circuit_params.mixer_pair_qubit_coeffs)

betas = np.linspace((dt / time) * (time * (1 - 0.5 / p)),
(dt / time) * (time * 0.5 / p), p)
Expand All @@ -197,10 +197,10 @@ def random(cls, qaoa_circuit_params:QAOACircuitParams, seed:int = None):
np.random.seed(seed)

p=qaoa_circuit_params.p
n_gamma_singles = len(qaoa_circuit_params.cost_hamiltonian.single_qubit_coeffs)
n_gamma_pairs = len(qaoa_circuit_params.cost_hamiltonian.pair_qubit_coeffs)
n_beta_singles = len(qaoa_circuit_params.mixer_hamiltonian.single_qubit_coeffs)
n_beta_pairs = len(qaoa_circuit_params.mixer_hamiltonian.pair_qubit_coeffs)
n_gamma_singles = len(qaoa_circuit_params.cost_single_qubit_coeffs)
n_gamma_pairs = len(qaoa_circuit_params.cost_pair_qubit_coeffs)
n_beta_singles = len(qaoa_circuit_params.mixer_single_qubit_coeffs)
n_beta_pairs = len(qaoa_circuit_params.mixer_pair_qubit_coeffs)

betas_singles = np.random.uniform(0,np.pi,(p,n_beta_singles))
betas_pairs = np.random.uniform(0,np.pi,(p,n_beta_pairs))
Expand All @@ -217,10 +217,10 @@ def empty(cls, qaoa_circuit_params: QAOACircuitParams):
"""

p=qaoa_circuit_params.p
n_gamma_singles = len(qaoa_circuit_params.cost_hamiltonian.single_qubit_coeffs)
n_gamma_pairs = len(qaoa_circuit_params.cost_hamiltonian.pair_qubit_coeffs)
n_beta_singles = len(qaoa_circuit_params.mixer_hamiltonian.single_qubit_coeffs)
n_beta_pairs = len(qaoa_circuit_params.mixer_hamiltonian.pair_qubit_coeffs)
n_gamma_singles = len(qaoa_circuit_params.cost_single_qubit_coeffs)
n_gamma_pairs = len(qaoa_circuit_params.cost_pair_qubit_coeffs)
n_beta_singles = len(qaoa_circuit_params.mixer_single_qubit_coeffs)
n_beta_pairs = len(qaoa_circuit_params.mixer_pair_qubit_coeffs)

betas_singles = np.empty((p,n_beta_singles))
betas_pairs = np.empty((p,n_beta_pairs))
Expand Down
27 changes: 12 additions & 15 deletions openqaoa/qaoa_parameters/fourierparams.py
Original file line number Diff line number Diff line change
Expand Up @@ -654,12 +654,12 @@ def linear_ramp_from_hamiltonian(cls,
time = float(0.7 * p)

n_u_singles = len(
qaoa_circuit_params.cost_hamiltonian.single_qubit_coeffs)
n_u_pairs = len(qaoa_circuit_params.cost_hamiltonian.pair_qubit_coeffs)
qaoa_circuit_params.cost_single_qubit_coeffs)
n_u_pairs = len(qaoa_circuit_params.cost_pair_qubit_coeffs)
n_v_singles = len(
qaoa_circuit_params.mixer_hamiltonian.single_qubit_coeffs)
qaoa_circuit_params.mixer_single_qubit_coeffs)
n_v_pairs = len(
qaoa_circuit_params.mixer_hamiltonian.pair_qubit_coeffs)
qaoa_circuit_params.mixer_pair_qubit_coeffs)

v = np.zeros(q)
v[0] = 0.5 * time / p
Expand Down Expand Up @@ -687,13 +687,10 @@ def random(cls, qaoa_circuit_params: QAOACircuitParams, q: int, seed: int = None
np.random.seed(seed)

p = qaoa_circuit_params.p
n_u_singles = len(
qaoa_circuit_params.cost_hamiltonian.single_qubit_coeffs)
n_u_pairs = len(qaoa_circuit_params.cost_hamiltonian.pair_qubit_coeffs)
n_v_singles = len(
qaoa_circuit_params.mixer_hamiltonian.single_qubit_coeffs)
n_v_pairs = len(
qaoa_circuit_params.mixer_hamiltonian.pair_qubit_coeffs)
n_u_singles = len(qaoa_circuit_params.cost_single_qubit_coeffs)
n_u_pairs = len(qaoa_circuit_params.cost_pair_qubit_coeffs)
n_v_singles = len(qaoa_circuit_params.mixer_single_qubit_coeffs)
n_v_pairs = len(qaoa_circuit_params.mixer_pair_qubit_coeffs)

v_singles = np.random.uniform(0, np.pi, (q, n_v_singles))
v_pairs = np.random.uniform(0, np.pi, (q, n_v_pairs))
Expand All @@ -711,13 +708,13 @@ def empty(cls, qaoa_circuit_params: QAOACircuitParams, q: int):
"""

v_singles = np.empty(
(q, len(qaoa_circuit_params.mixer_hamiltonian.single_qubit_coeffs)))
(q, len(qaoa_circuit_params.mixer_single_qubit_coeffs)))
v_pairs = np.empty(
(q, len(qaoa_circuit_params.mixer_hamiltonian.pair_qubit_coeffs)))
(q, len(qaoa_circuit_params.mixer_pair_qubit_coeffs)))
u_singles = np.empty(
(q, len(qaoa_circuit_params.cost_hamiltonian.single_qubit_coeffs)))
(q, len(qaoa_circuit_params.cost_single_qubit_coeffs)))
u_pairs = np.empty(
(q, len(qaoa_circuit_params.cost_hamiltonian.pair_qubit_coeffs)))
(q, len(qaoa_circuit_params.cost_pair_qubit_coeffs)))

return cls(qaoa_circuit_params, q,
v_singles, v_pairs, u_singles, u_pairs)
Expand Down
Loading