# Path: pystruct/models/grid_crf.py
# class DirectionalGridCRF(GridCRF, EdgeFeatureGraphCRF):
#     """CRF in which each direction of edges has their own set of parameters.
# 
#     Pairwise potentials are not symmetric and are independend for each kind of
#     edges. This leads to n_classes * n_features parameters for unary potentials
#     and n_edge_features * n_classes ** 2 parameters for edge potentials.
#     The number of edge-types is two for a 4-connected neighborhood
#     (horizontal and vertical) or 4 for a 8 connected neighborhood (additionally
#     two diagonals).
# 
#     Unary evidence ``x`` is given as array of shape (width, height, n_features),
#     labels ``y`` are given as array of shape (width, height). Grid sizes do not
#     need to be constant over the dataset.
# 
#     Parameters
#     ----------
#     n_states : int, default=None
#         Number of states for all variables.
# 
#     inference_method : string, default=None
#         Function to call do do inference and loss-augmented inference.
#         Possible values are:
# 
#             - 'max-product' for max-product belief propagation.
#                 Recommended for chains an trees. Loopy belief propagation in
#                 case of a general graph.
#             - 'lp' for Linear Programming relaxation using cvxopt.
#             - 'ad3' for AD3 dual decomposition.
#             - 'qpbo' for QPBO + alpha expansion.
#             - 'ogm' for OpenGM inference algorithms.
# 
#     neighborhood : int, default=4
#         Neighborhood defining connection for each variable in the grid.
#         Possible choices are 4 and 8.
#     """
#     def __init__(self, n_states=None, n_features=None, inference_method=None,
#                  neighborhood=4):
#         self.neighborhood = neighborhood
#         n_edge_features = 2 if neighborhood == 4 else 4
#         EdgeFeatureGraphCRF.__init__(self, n_states, n_features,
#                                      n_edge_features,
#                                      inference_method=inference_method)
# 
#     def _set_size_joint_feature(self):
#         if self.n_features is not None and self.n_states is not None:
#             self.size_joint_feature = (
#                 self.n_states * self.n_features
#                 + self.n_edge_features * self.n_states ** 2)
# 
#     def _check_size_x(self, x):
#         GridCRF._check_size_x(self, x)
# 
#     def initialize(self, X, Y):
#         # we don't want to infere n_edge_features as in EdgeFeatureGraphCRF
#         CRF.initialize(self, X, Y)
# 
#     def _get_edges(self, x, flat=True):
#         return make_grid_edges(x, neighborhood=self.neighborhood,
#                                return_lists=not flat)
# 
#     def joint_feature(self, x, y):
#         """Feature vector associated with instance (x, y).
# 
#         Feature representation joint_feature, such that the energy of the configuration
#         (x, y) and a weight vector w is given by np.dot(w, joint_feature(x, y)).
# 
#         Parameters
#         ----------
#         x : ndarray, shape (width, height, n_features)
#             Unary evidence / input.
# 
#         y : ndarray or tuple
#             Either y is an integral ndarray of shape (width, height), giving
#             a complete labeling for x.
#             Or it is the result of a linear programming relaxation. In this
#             case, ``y=(unary_marginals, pariwise_marginals)``, where
#             unary_marginals is an array of shape (width, height, n_states) and
#             pairwise_marginals is an array of shape (n_states, n_states) of
#             accumulated pairwise marginals.
# 
#         Returns
#         -------
#         p : ndarray, shape (size_joint_feature,)
#             Feature vector associated with state (x, y).
# 
#         """
#         return EdgeFeatureGraphCRF.joint_feature(self, x, y)
# 
#     def _get_edge_features(self, x):
#         return edge_list_to_features(self._get_edges(x, flat=False))


from pystruct.models import DirectionalGridCRF
from pystruct.datasets import generate_blocks_multinomial
from pystruct.plot_learning import plot_learning
import pystruct.learners as ssvm

This example illustrates the use of ``plot_learning.py``, which can be used to
analyze the learning of models.
The file can be call directly as executable or imported as done here.

The task is the same as in plot_directional_grid.py.

Red circles show the primal objective when inference was run.  The green curve
shows the approximated primal objective when finding constraints from the
cache.
The blue shows the objective restricted to the working set. The blue curve is
guaranteed to increase in cutting plane training in each step, and provides a
lower bound on the primal objective.

In contrast to the caching proposed in [1], we do not produce constraints from
the cache as long as possible. A heuristic is used to see whether the cached
constraint is strong enough.

Here training is stopped when the green curve goes below the blue curve.
This means no strong enough constraint could be found. The fact that the
primal objective can go below the cutting plane objective is a result of
approximate inference. The real most violating constraint could not be found,
leading to underestimating the primal objective.

See plot_exact_learning.py for a way to deal with this.

"""


X, Y = generate_blocks_multinomial(noise=2, n_samples=20, seed=1)

# Path: pystruct/models/grid_crf.py
# class DirectionalGridCRF(GridCRF, EdgeFeatureGraphCRF):
#     """CRF in which each direction of edges has their own set of parameters.
# 
#     Pairwise potentials are not symmetric and are independend for each kind of
#     edges. This leads to n_classes * n_features parameters for unary potentials
#     and n_edge_features * n_classes ** 2 parameters for edge potentials.
#     The number of edge-types is two for a 4-connected neighborhood
#     (horizontal and vertical) or 4 for a 8 connected neighborhood (additionally
#     two diagonals).
# 
#     Unary evidence ``x`` is given as array of shape (width, height, n_features),
#     labels ``y`` are given as array of shape (width, height). Grid sizes do not
#     need to be constant over the dataset.
# 
#     Parameters
#     ----------
#     n_states : int, default=None
#         Number of states for all variables.
# 
#     inference_method : string, default=None
#         Function to call do do inference and loss-augmented inference.
#         Possible values are:
# 
#             - 'max-product' for max-product belief propagation.
#                 Recommended for chains an trees. Loopy belief propagation in
#                 case of a general graph.
#             - 'lp' for Linear Programming relaxation using cvxopt.
#             - 'ad3' for AD3 dual decomposition.
#             - 'qpbo' for QPBO + alpha expansion.
#             - 'ogm' for OpenGM inference algorithms.
# 
#     neighborhood : int, default=4
#         Neighborhood defining connection for each variable in the grid.
#         Possible choices are 4 and 8.
#     """
#     def __init__(self, n_states=None, n_features=None, inference_method=None,
#                  neighborhood=4):
#         self.neighborhood = neighborhood
#         n_edge_features = 2 if neighborhood == 4 else 4
#         EdgeFeatureGraphCRF.__init__(self, n_states, n_features,
#                                      n_edge_features,
#                                      inference_method=inference_method)
# 
#     def _set_size_joint_feature(self):
#         if self.n_features is not None and self.n_states is not None:
#             self.size_joint_feature = (
#                 self.n_states * self.n_features
#                 + self.n_edge_features * self.n_states ** 2)
# 
#     def _check_size_x(self, x):
#         GridCRF._check_size_x(self, x)
# 
#     def initialize(self, X, Y):
#         # we don't want to infere n_edge_features as in EdgeFeatureGraphCRF
#         CRF.initialize(self, X, Y)
# 
#     def _get_edges(self, x, flat=True):
#         return make_grid_edges(x, neighborhood=self.neighborhood,
#                                return_lists=not flat)
# 
#     def joint_feature(self, x, y):
#         """Feature vector associated with instance (x, y).
# 
#         Feature representation joint_feature, such that the energy of the configuration
#         (x, y) and a weight vector w is given by np.dot(w, joint_feature(x, y)).
# 
#         Parameters
#         ----------
#         x : ndarray, shape (width, height, n_features)
#             Unary evidence / input.
# 
#         y : ndarray or tuple
#             Either y is an integral ndarray of shape (width, height), giving
#             a complete labeling for x.
#             Or it is the result of a linear programming relaxation. In this
#             case, ``y=(unary_marginals, pariwise_marginals)``, where
#             unary_marginals is an array of shape (width, height, n_states) and
#             pairwise_marginals is an array of shape (n_states, n_states) of
#             accumulated pairwise marginals.
# 
#         Returns
#         -------
#         p : ndarray, shape (size_joint_feature,)
#             Feature vector associated with state (x, y).
# 
#         """
#         return EdgeFeatureGraphCRF.joint_feature(self, x, y)
# 
#     def _get_edge_features(self, x):
#         return edge_list_to_features(self._get_edges(x, flat=False))


# Path: examples/plot_objective_curve.py
from pystruct.models import DirectionalGridCRF
from pystruct.datasets import generate_blocks_multinomial
from pystruct.plot_learning import plot_learning
import pystruct.learners as ssvm


This example illustrates the use of ``plot_learning.py``, which can be used to
analyze the learning of models.
The file can be call directly as executable or imported as done here.

The task is the same as in plot_directional_grid.py.

Red circles show the primal objective when inference was run.  The green curve
shows the approximated primal objective when finding constraints from the
cache.
The blue shows the objective restricted to the working set. The blue curve is
guaranteed to increase in cutting plane training in each step, and provides a
lower bound on the primal objective.

In contrast to the caching proposed in [1], we do not produce constraints from
the cache as long as possible. A heuristic is used to see whether the cached
constraint is strong enough.

Here training is stopped when the green curve goes below the blue curve.
This means no strong enough constraint could be found. The fact that the
primal objective can go below the cutting plane objective is a result of
approximate inference. The real most violating constraint could not be found,
leading to underestimating the primal objective.

See plot_exact_learning.py for a way to deal with this.

"""


X, Y = generate_blocks_multinomial(noise=2, n_samples=20, seed=1)

crf = DirectionalGridCRF(inference_method="qpbo", neighborhood=4)