Convert this instance of `DiscreteDP` to the "product" form.

        The product form uses the version of the init method taking
        `R`, `Q` and `beta`.

        Parameters
        ----------

        Returns
        -------
        ddp_sa : DiscreteDP
            The correspnoding DiscreteDP instance in product form

        Notes
        -----
        If this instance is already in product form then it is returned
        un-modified

def to_product_form(self):
        """
        Convert this instance of `DiscreteDP` to the "product" form.

        The product form uses the version of the init method taking
        `R`, `Q` and `beta`.

        Parameters
        ----------

        Returns
        -------
        ddp_sa : DiscreteDP
            The correspnoding DiscreteDP instance in product form

        Notes
        -----
        If this instance is already in product form then it is returned
        un-modified

        """
        if self._sa_pair:
            ns = self.num_states
            na = self.a_indices.max() + 1
            R = np.full((ns, na), -np.inf)
            R[self.s_indices, self.a_indices] = self.R
            Q = np.zeros((ns, na, ns))
            if self._sparse:
                _fill_dense_Q(self.s_indices, self.a_indices,
                              self.Q.toarray(), Q)
            else:
                _fill_dense_Q(self.s_indices, self.a_indices, self.Q, Q)
            return DiscreteDP(R, Q, self.beta)
        else:
            return self