Source code for qnetvo.cost.linear_inequalities
from pennylane import math
from pennylane import numpy as np
from ..qnodes import joint_probs_qnode, global_parity_expval_qnode
from ..utilities import mixed_base_num, ragged_reshape
[docs]
def linear_probs_cost_fn(network_ansatz, game, postmap=np.array([]), qnode_kwargs={}):
"""Constructs an ansatz-specific cost that is a linear function of the network probablities.
The cost function is encoded into a ``game`` matrix whose
coefficients scale conditional probabilities for the network.
The cost is derived from the score which is evaluated as,
.. math::
\\langle\\mathbf{G},\\mathbf{P}\\rangle = \\sum_{\\{\\vec{x}_i\\}_i,\\vec{a}} G_{a|\\{x_i\\}_i} P(a|\\{x_i\\}),
where :math:`\\mathbf{P}` is a behavior (see :meth:`qnetvo.behavior_fn`) and :math:`\\{\\vec{x}_i\\}_i` specifies
the collection of inputs for each layer as indexed by :math:`i`.
A post-processing map :math:`\\mathbf{L}` may optionally be applied as
:math:`\\mathbf{L}\\mathbf{P}_{Net}` where
.. math::
\\mathbf{L} = \\sum_{z',z}P(z'|z)|z'\\rangle\\langle z|.
In the above expression, :math:`z'` is a new output drawn from a new alphabet.
:param network_ansatz: The network to which the cost function is applied.
:type network_ansatz: ``NetworkAnsatz`` class
:param game: A matrix with dimensions ``A x (\\prod_i X_i)`` for
:type game: np.arrray
:param postmap: A post-processing map applied to the bitstrings output from the
quantum circuit. The ``postmap`` matrix is column stochastic, that is,
each column sums to one and contains only positive values.
:type postmap: *optional* np.ndarray
:returns: A cost function evaluated as ``cost(*network_settings)``.
:rtype: function
:raises ValueError: If the number of outputs from the qnode do not match the
the number of rows in the specified ``game``.
"""
probs_qnode = joint_probs_qnode(network_ansatz, **qnode_kwargs)
net_num_in = math.prod(network_ansatz.layers_total_num_in)
num_inputs_list = math.concatenate(network_ansatz.layers_node_num_in).tolist()
node_input_ids = [
ragged_reshape(mixed_base_num(i, num_inputs_list), network_ansatz.layers_num_nodes)
for i in range(net_num_in)
]
raw_net_num_out = 2 ** len(network_ansatz.layers_wires[-1])
game_outputs, game_inputs = game.shape
if game_inputs != net_num_in:
raise ValueError("The `game` matrix must have " + str(net_num_in) + " columns.")
has_postmap = len(postmap) != 0
if not (has_postmap):
if game_outputs != raw_net_num_out:
raise ValueError(
"The `game` matrix must either have "
+ str(raw_net_num_out)
+ " rows, or a `postmap` is needed."
)
else:
if postmap.shape[0] != game_outputs:
raise ValueError("The `postmap` must have " + str(game_outputs) + " rows.")
elif postmap.shape[1] != raw_net_num_out:
raise ValueError("The `postmap` must have " + str(raw_net_num_out) + " columns.")
def cost(*network_settings):
score = 0
for i, input_id_set in enumerate(node_input_ids):
settings = network_ansatz.qnode_settings(network_settings, input_id_set)
raw_probs = probs_qnode(settings)
probs = postmap @ raw_probs if has_postmap else raw_probs
score += math.sum(game[:, i] * probs)
return -(score)
return cost