<NME> test_uneven_logit.py
<BEF> """
Tests for the uneven_logit.py file. These tests do not include tests of
the functions that perform the mathematical calculations necessary to estimate
the multinomial Uneven Logit model.
"""
import warnings
import unittest
from collections import OrderedDict
from copy import deepcopy

import numpy as np
import numpy.testing as npt
import pandas as pd
from scipy.sparse import csr_matrix
from scipy.sparse import diags

import pylogit.uneven_logit as uneven

# Use the following to always show the warnings
np.seterr(all='warn')
warnings.simplefilter("always")


class GenericTestCase(unittest.TestCase):
    """
    Defines the common setUp method used for the different type of tests.
    """

    def setUp(self):
        # The set up being used is one where there are two choice situations,
        # The first having three alternatives, and the second having only two
        # alternatives. There is one generic variable. Two alternative
        # specific constants and all three shape parameters are used.

        # Create the betas to be used during the tests
        self.fake_betas = np.array([-0.6])

        # Create the fake outside intercepts to be used during the tests
        self.fake_intercepts = np.array([1, 0.5])

        # Create names for the intercept parameters
        self.fake_intercept_names = ["ASC 1", "ASC 2"]

        # Record the position of the intercept that is not being estimated
        self.fake_intercept_ref_pos = 2

        # Create the shape parameters to be used during the tests. Note that
        # these are the reparameterized shape parameters, thus they will be
        # exponentiated in the fit_mle process and various calculations.
        self.fake_shapes = np.array([-1, 0, 1])

        # Create names for the intercept parameters
        self.fake_shape_names = ["Shape 1", "Shape 2", "Shape 3"]

        # Create an array of all model parameters
        self.fake_all_params = np.concatenate((self.fake_shapes,
                                               self.fake_intercepts,
                                               self.fake_betas))

        # The mapping between rows and alternatives is given below.
        self.fake_rows_to_alts = csr_matrix(np.array([[1, 0, 0],
                                                      [0, 1, 0],
                                                      [0, 0, 1],
                                                      [1, 0, 0],
                                                      [0, 0, 1]]))

        # Create the fake design matrix with columns denoting X
        # The intercepts are not included because they are kept outside the
        # index in the uneven model.
        self.fake_design = np.array([[1],
                                     [2],
                                     [3],
                                     [1.5],
                                     [3.5]])

        # Create the index array for this set of choice situations
        self.fake_index = self.fake_design.dot(self.fake_betas)

        # Create the needed dataframe for the Uneven Logit constructor
        self.fake_df = pd.DataFrame({"obs_id": [1, 1, 1, 2, 2],
                                     "alt_id": [1, 2, 3, 1, 3],
                                     "choice": [0, 1, 0, 0, 1],
                                     "x": self.fake_design[:, 0],
                                     "intercept": [1 for i in range(5)]})

        # Record the various column names
        self.alt_id_col = "alt_id"
        self.obs_id_col = "obs_id"
        self.choice_col = "choice"

        # Create the index specification  and name dictionaryfor the model
        self.fake_specification = OrderedDict()
        self.fake_names = OrderedDict()
        self.fake_specification["x"] = [[1, 2, 3]]
        self.fake_names["x"] = ["x (generic coefficient)"]

        # Bundle the args and kwargs used to construct the Uneven Logit model.
        self.constructor_args = [self.fake_df,
                                 self.alt_id_col,
                                 self.obs_id_col,
                                 self.choice_col,
                                 self.fake_specification]

        # Create a variable for the kwargs being passed to the constructor
        self.constructor_kwargs = {"intercept_ref_pos":
                                   self.fake_intercept_ref_pos,
                                   "names": self.fake_names,
                                   "intercept_names":
                                   self.fake_intercept_names,
                                   "shape_names": self.fake_shape_names}

        # Initialize a basic uneven model.
        # Create the uneven logit model whose coefficients will be estimated.
        self.model_obj = uneven.MNUL(*self.constructor_args,
                                     **self.constructor_kwargs)

        return None


# Note that inheritance is used to share the setUp method.
class ChoiceObjectTests(GenericTestCase):
    """
    Defines the tests for the Uneven Logit model's `__init__` function and
    its class methods.
    """

    def test_shape_ignore_msg_in_constructor(self):
        """
        Ensures that a UserWarning is raised when the 'shape_ref_pos' keyword
        argument is passed to the Uneven Logit model constructor. This warns
        people against expecting the shape parameters of the Uneven Logit model
        to suffer from identification problems. It also alerts users that they
        are using a Uneven Logit model when they might have been expecting to
        instantiate a different choice model.
        """
        # Create a variable for the kwargs being passed to the constructor
        kwargs = deepcopy(self.constructor_kwargs)
        kwargs["shape_ref_pos"] = 2

        # Test to ensure that the shape ignore message is printed when using
        # either of these two kwargs
        with warnings.catch_warnings(record=True) as context:
            # Use this filter to always trigger the  UserWarnings
            warnings.simplefilter('always', UserWarning)

            # Create a Uneven Logit model object with the irrelevant kwargs.
            # This should trigger a UserWarning
            uneven_obj = uneven.MNUL(*self.constructor_args, **kwargs)
            # Check that the warning has been created.
            self.assertEqual(len(context), 1)
            self.assertIsInstance(context[-1].category, type(UserWarning))
            self.assertIn(uneven._shape_ref_msg, str(context[-1].message))

        return None

    def test_ridge_warning_in_fit_mle(self):
        """
        Ensure that a UserWarning is raised when one passes the ridge keyword
        argument to the `fit_mle` method of an Uneven Logit model object.
        """
        # Create a variable for the fit_mle function's kwargs.
        # The print_res = False arguments are to make sure strings aren't
        # printed to the console unnecessarily.
        # The method kwarg is so we test the use of the hessian
        kwargs = {"ridge": 0.5,
                  "print_res": True,
                  "method": "newton-cg"}

        # Test to make sure that the ridge warning message is printed when
        # using the ridge keyword argument
        with warnings.catch_warnings(record=True) as w:
            # Use this filter to always trigger the  UserWarnings
            warnings.simplefilter('always', UserWarning)

            self.model_obj.fit_mle(self.fake_all_params, **kwargs)
            self.assertGreaterEqual(len(w), 1)
            self.assertIsInstance(w[0].category, type(UserWarning))
            self.assertIn(uneven._ridge_warning_msg, str(w[0].message))

        return None

    def test_init_shapes_length_error_in_fit_mle(self):
        """
        Ensures that ValueError is raised if init_shapes has wrong length.
        """
        # Create a variable for the arguments to the fit_mle function.
        # Note `None` is the argument passed when using the init_shapes,
        # init_intercepts and init_coefs keyword arguments.
        fit_args = [None]

        # Create base set of kwargs for fit_mle function
        kwargs = {"init_intercepts": self.fake_intercepts,
                  "init_coefs": self.fake_betas,
                  "print_res": False}

        for i in [1, -1]:
            # This will ensure we have too many or too few shape parameters.
            num_shapes = self.fake_rows_to_alts.shape[1] + i
            kwargs["init_shapes"] = np.arange(num_shapes)

            # Test to ensure that the ValueError is raised when using an
            # init_shapes kwarg with an incorrect number of parameters
            self.assertRaises(ValueError, self.model_obj.fit_mle,
                              *fit_args, **kwargs)

    def test_init_intercepts_length_error_in_fit_mle(self):
        """
        Ensures that ValueError is raised if init_intercepts has wrong length.
        """
        # Create a variable for the arguments to the fit_mle function.
        # Note `None` is the argument passed when using the init_shapes,
        # init_intercepts and init_coefs keyword arguments.
        fit_args = [None]

        # Create base set of kwargs for fit_mle function
        kwargs = {"init_shapes": self.fake_shapes,
                  "init_coefs": self.fake_betas,
                  "print_res": False}

        for i in [1, -1]:
            # This will ensure we have too many or too few intercepts
            num_intercepts = self.fake_intercepts.shape[0] + i
            kwargs["init_intercepts"] = np.arange(num_intercepts)

            # Test to ensure that the ValueError when using an init_intercepts
            # kwarg with an incorrect number of parameters
            self.assertRaises(ValueError, self.model_obj.fit_mle,
                              *fit_args, **kwargs)

        return None

    def test_init_coefs_length_error_in_fit_mle(self):
        """
        Ensures that ValueError is raised if init_coefs has wrong length.
        """
        # Create a variable for the arguments to the fit_mle function.
        # Note `None` is the argument passed when using the init_shapes,
        # init_intercepts and init_coefs keyword arguments.
        fit_args = [None]

        # Create base set of kwargs for fit_mle function
        kwargs = {"init_shapes": self.fake_shapes,
                  "init_intercepts": self.fake_intercepts,
                  "print_res": False}

        # Note there is only one beta, so we can't go lower than zero betas.
        for i in [1, -1]:
            # This will ensure we have too many or too few intercepts
            num_coefs = self.fake_betas.shape[0] + i
            kwargs["init_coefs"] = np.arange(num_coefs)

            # Test to ensure that the ValueError when using an init_intercepts
            # kwarg with an incorrect number of parameters
            self.assertRaises(ValueError, self.model_obj.fit_mle,
                              *fit_args, **kwargs)

        return None

    def test_insufficient_initial_values_in_fit_mle(self):
        """
        Ensure that value errors are raised if neither init_vals OR
        (init_shapes and init_coefs) are passed.
        """
        # Create a variable for the arguments to the fit_mle function.
        # Note `None` is the argument passed when using the init_shapes,
        # init_intercepts and init_coefs keyword arguments.
        fit_args = [None]

        # Create base set of incorrect kwargs for fit_mle function
        kwargs = {"init_shapes": None,
                  "init_intercepts": self.fake_intercepts,
                  "init_coefs": None,
                  "print_res": False}

        kwargs_2 = {"init_shapes": self.fake_shapes,
                    "init_intercepts": self.fake_intercepts,
                    "init_coefs": None,
                    "print_res": False}

        kwargs_3 = {"init_shapes": None,
                    "init_intercepts": self.fake_intercepts,
                    "init_coefs": self.fake_betas,
                    "print_res": False}

        for bad_kwargs in [kwargs, kwargs_2, kwargs_3]:
            # Test to ensure that the ValueError when not passing
            # kwarg with an incorrect number of parameters
            self.assertRaises(ValueError, self.model_obj.fit_mle,
                              *fit_args, **bad_kwargs)

        return None

    def test_keyword_argument_constructor_in_fit_mle(self):
        """
        Ensures that the init_vals object can be successfully created from the
        various init_shapes, init_intercepts, and init_coefs arguments.
        """
        # Create a variable for the arguments to the fit_mle function.
        # Note `None` is the argument passed when using the init_shapes,
        # init_intercepts and init_coefs keyword arguments.
        fit_args = [None]

        # Create base set of incorrect kwargs for fit_mle function (note the
        # ridge is the thing that is incorrect)
        kwargs_1 = {"init_shapes": self.fake_shapes,
                    "init_intercepts": self.fake_intercepts,
                    "init_coefs": self.fake_betas,
                    "ridge": "foo",
                    "print_res": False}

        kwargs_2 = {"init_shapes": self.fake_shapes,
                    "init_coefs": self.fake_betas,
                    "ridge": "foo",
                    "print_res": False}

        # Test to ensure that the raised Value Err is printed when using
        # either of these two kwargs
        for kwargs in [kwargs_1, kwargs_2]:
            self.assertRaisesRegexp(TypeError,
                                    "ridge",
                                    self.model_obj.fit_mle,
                                    *fit_args,
                                    **kwargs)

        return None

    def test_init_vals_length_error_in_fit_mle(self):
        """
        Ensures that ValueError is raised if init_vals has wrong length.
        """
        # Note there is only one beta, so we can't go lower than zero betas.
        original_intercept_ref_position = self.fake_intercept_ref_pos
        for intercept_ref_position in [None, original_intercept_ref_position]:
            self.model_obj.intercept_ref_position = intercept_ref_position
            for i in [1, -1]:
                # This will ensure we have too many or too few intercepts
                num_coefs = self.fake_betas.shape[0] + i

                # Test to ensure that the ValueError when using an
                # init_intercepts kwarg with an incorrect number of parameters
                self.assertRaisesRegexp(ValueError,
                                        "dimension",
                                        self.model_obj.fit_mle,
                                        np.arange(num_coefs),
                                        print_res=False)

        return None

    def test_just_point_kwarg(self):
        # Alias the function being tested
        func = self.model_obj.fit_mle
        # Get the necessary kwargs
        kwargs = {"just_point": True}
        # Get the function results
        func_result = func(self.fake_all_params, **kwargs)
        # Perform the desired tests to make sure we get back a dictionary with
        # an "x" key in it and a value that is a ndarray.
        self.assertIsInstance(func_result, dict)
        self.assertIn("x", func_result)
        self.assertIsInstance(func_result["x"], np.ndarray)
        return None


# As before, inheritance is used to share the setUp method.
class HelperFuncTests(GenericTestCase):
    """
    Defines tests for the 'helper' functions for estimating the Uneven Logit
    model.
    """

    def test_split_param_vec_with_intercepts(self):
        """
        Ensures that split_param_vec returns (shapes, intercepts, index_coefs)
        when called from within uneven.py.
        """
        # Store the results of split_param_vec()
        split_results = uneven.split_param_vec(self.fake_all_params,
                                               self.fake_rows_to_alts,
                                               self.fake_design)
        # Check for expected results.
        for item in split_results:
            self.assertIsInstance(item, np.ndarray)
            self.assertEqual(len(item.shape), 1)
        npt.assert_allclose(split_results[0], self.fake_shapes)
        npt.assert_allclose(split_results[1], self.fake_intercepts)
        npt.assert_allclose(split_results[2], self.fake_betas)

        return None

    def test_split_param_vec_without_intercepts(self):
        """
        Ensures that split_param_vec returns (shapes, intercepts, index_coefs)
        when called from within uneven.py.
        """
        # Store the results of split_param_vec()
        shapes_and_betas = np.concatenate([self.fake_shapes,
                                           self.fake_betas])
        split_results = uneven.split_param_vec(shapes_and_betas,
                                               self.fake_rows_to_alts,
                                               self.fake_design)
        # Check for expected results.
        for idx in [0, 2]:
            self.assertIsInstance(split_results[idx], np.ndarray)
            self.assertEqual(len(split_results[idx].shape), 1)
        npt.assert_allclose(split_results[0], self.fake_shapes)
        npt.assert_allclose(split_results[2], self.fake_betas)
        self.assertIsNone(split_results[1])

        return None

    def test_uneven_utility_transform(self):
        """
        Ensures that `_uneven_utility_transform()` returns correct results
        """
        # Create a set of systematic utilities that will test the function for
        # correct calculations, for proper dealing with overflow, and for
        # proper dealing with underflow.

        # The first and third elements tests general calculation.
        # The second element of index_array should lead to the transformation
        # equaling the intercept for alternative 2.
        # The fourth element should test what happens with underflow and should
        # lead to max_comp_value.
        # The fifth element should test what happens with overflow and should
        # lead to -1.0 * max_comp_value
        index_array = np.array([1, 0, -1, 1e400, -1e400])

        # Crerate the array of expected results
        intercept_1 = self.fake_intercepts[0]
        intercept_3 = 0
        shape_1 = np.exp(self.fake_shapes[0])
        shape_3 = np.exp(self.fake_shapes[2])

        result_1 = (intercept_1 +
                    index_array[0] +
                    np.log1p(np.exp(-index_array[0])) -
                    np.log1p(np.exp(-shape_1 * index_array[0])))

        result_3 = (intercept_3 +
                    index_array[2] +
                    np.log1p(np.exp(-index_array[2])) -
                    np.log1p(np.exp(-shape_3 * index_array[2])))

        expected_results = np.array([result_1,
                                     self.fake_intercepts[1],
                                     result_3,
                                     uneven.max_comp_value + intercept_1,
                                     - uneven.max_comp_value])[:, None]

        # Use the utility transformation function
        args = [index_array,
                self.fake_df[self.alt_id_col].values,
                self.fake_rows_to_alts,
                self.fake_shapes,
                self.fake_intercepts]
        kwargs = {"intercept_ref_pos": self.fake_intercept_ref_pos}
        func_results = uneven._uneven_utility_transform(*args, **kwargs)

        # Check the correctness of the result
        self.assertIsInstance(func_results, np.ndarray)
        self.assertEqual(len(func_results.shape), 2)
        self.assertEqual(func_results.shape[1], expected_results.shape[1])
        self.assertEqual(func_results.shape[0], expected_results.shape[0])
        npt.assert_allclose(expected_results, func_results)

        return None

    def test_uneven_utility_transform_2d(self):
        """
        Ensure the function returns expected results with 2d systemic_utilities
        """
        # Create a set of systematic utilities that will test the function for
        # correct calculations, for proper dealing with overflow, and for
        # proper dealing with underflow.

        # The first and third elements tests general calculation.
        # The second element of index_array should lead to the transformation
        # equaling the intercept for alternative 2.
        # The fourth element should test what happens with underflow and should
        # lead to max_comp_value.
        # The fifth element should test what happens with overflow and should
        # lead to -1.0 * max_comp_value
        index_array = np.array([1, 0, -1, 1e400, -1e400])
        index_array_2d = np.concatenate([index_array[:, None],
                                         index_array[:, None]],
                                        axis=1)

        # Create 2d array of shapes
        shapes_2d = np.concatenate([self.fake_shapes[:, None],
                                    self.fake_shapes[:, None]],
                                   axis=1)

        # Create 2d array of intercepts
        intercepts_2d = np.concatenate([self.fake_intercepts[:, None],
                                        self.fake_intercepts[:, None]],
                                       axis=1)

        # Crerate the array of expected results
        intercept_1 = self.fake_intercepts[0]
        intercept_3 = 0
        shape_1 = np.exp(self.fake_shapes[0])
        shape_3 = np.exp(self.fake_shapes[2])

        result_1 = (intercept_1 +
                    index_array[0] +
                    np.log1p(np.exp(-index_array[0])) -
                    np.log1p(np.exp(-shape_1 * index_array[0])))

        result_3 = (intercept_3 +
                    index_array[2] +
                    np.log1p(np.exp(-index_array[2])) -
                    np.log1p(np.exp(-shape_3 * index_array[2])))

        expected_results = np.array([result_1,
                                     self.fake_intercepts[1],
                                     result_3,
                                     uneven.max_comp_value + intercept_1,
                                     - uneven.max_comp_value])[:, None]

        # Use the utility transformation function
        args = [index_array_2d,
                self.fake_df[self.alt_id_col].values,
                self.fake_rows_to_alts,
                shapes_2d,
                intercepts_2d]
        kwargs = {"intercept_ref_pos": self.fake_intercept_ref_pos}
        func_results = uneven._uneven_utility_transform(*args, **kwargs)

        # Check the correctness of the result
        self.assertIsInstance(func_results, np.ndarray)
        self.assertEqual(len(func_results.shape), 2)
        # We should have two columns
        self.assertEqual(func_results.shape[1], 2)
        self.assertEqual(func_results.shape[0], expected_results.shape[0])
        for col in [0, 1]:
            npt.assert_allclose(expected_results,
                                func_results[:, col][:, None])

        return None

    def test_uneven_transform_deriv_v(self):
        """
        Tests basic behavior of the uneven_transform_deriv_v.
        """
        # Note the index has a value that is small and a value that is large to
        # test whether or not the function correctly deals with underflow and
        # overflow when calculating the derivative.
        test_index = np.array([-2, 0, 2, -3000, 800])
        # Note we use a compressed sparse-row matrix so that we can easily
        # convert the output matrix to a numpy array using the '.A' attribute.
        num_rows = test_index.shape[0]
        test_output = diags(np.ones(num_rows),
                            0, format='csr')

        # Bundle the arguments needed for _uneven_transform_deriv_v()
        args = [test_index,
                self.fake_df[self.alt_id_col].values,
                self.fake_rows_to_alts,
                self.fake_shapes]

        # Get the derivative using the function defined in uneven_logit.py.
        derivative = uneven._uneven_transform_deriv_v(*args,
                                                      output_array=test_output)

        # Initialize an array of correct results
        # Note the second element is by design where each of the terms in the
        # derivative should evaluate to 1 and we get 1 / 1 = 1.
        correct_derivatives = np.array([np.nan,
                                        (1 + np.exp(self.fake_shapes[1])) / 2,
                                        np.nan,
                                        np.exp(self.fake_shapes[0]),
                                        1.0])

        # Calculate 'by hand' what the correct results should be
        for i in [0, 2]:
            shape = np.exp(self.fake_shapes[i])
            index_val = test_index[i]
            correct_derivatives[i] = ((1 + np.exp(-index_val))**-1 +
                                      shape / (1 + np.exp(shape * index_val)))

        self.assertIsInstance(derivative, type(test_output))
        self.assertEqual(len(derivative.shape), 2)
        self.assertEqual(derivative.shape, (num_rows, num_rows))
        npt.assert_allclose(correct_derivatives,
                            np.diag(derivative.A))

        return None

    def test_uneven_transform_deriv_alpha(self):
        """
        Ensures that uneven_transform_deriv_alpha returns the `output_array`
        kwarg.
        """
        # Bundle the args for the function being tested
        args = [self.fake_index,
                self.fake_df[self.alt_id_col].values,
                self.fake_rows_to_alts,
                self.fake_intercepts]

        # Take the relevant columns of rows_to_alts, as would be done with
        # outside intercepts, or test None for when there are no outside
        # intercepts.
        for test_output in [None, self.fake_rows_to_alts[:, [0, 1]]]:
            kwargs = {"output_array": test_output}
            derivative_results = uneven._uneven_transform_deriv_alpha(*args,
                                                                      **kwargs)
            if test_output is None:
                self.assertIsNone(derivative_results)
            else:
                npt.assert_allclose(test_output.A, derivative_results.A)

        return None

    def test_uneven_transform_deriv_shape(self):
        """
        Ensures that the uneven_transform_deriv_shape() function provides
        correct results, and handles all forms of overflow and underflow
        correctly.
        """
        # Note the index has a value that is small and a value that is large to
        # test whether or not the function correctly uses L'Hopital's rule to
        # deal with underflow and overflow from the index when calculating the
        # derivative. When the index is large, the derivative should be the
        # negative of the inverse of the 'natural' shape parameter. When the
        # index is small the derivative should be max_comp_value (theoretically
        # inf). When the shape is large, the derivative should be the negative
        # of the natural log of 1 + exp(-index).
        test_index = np.array([-10, 0, 2, -10000, 1e8])

        # Note we use a compressed sparse-row matrix so that we can easily
        # convert the output matrix to a numpy array using the '.A' attribute.
        num_rows = test_index.shape[0]
        test_output = diags(np.ones(num_rows),
                            0, format='csr')

        # Create new test shapes to use. We make the first shape parameter big
        # to test overflow handling from this situation and L'Hopital's rule.
        new_shapes = deepcopy(self.fake_shapes)
        new_shapes[0] = np.log(80)
        natural_shapes = np.exp(new_shapes)

        # Bundle the arguments needed for _uneven_transform_deriv_shape()
        args = [test_index,
                self.fake_df[self.alt_id_col].values,
                self.fake_rows_to_alts,
                new_shapes]
        kwargs = {"output_array": test_output}

        # Get the derivative using the function defined in uneven_log.py.
        derivative = uneven._uneven_transform_deriv_shape(*args, **kwargs)

        # Initialize an array of correct results
        # Note the second element is by design so the derivative equals 2ln(2)
        # Also, we precompute the results and multiply by natural shapes to
        # account for the jacobian when computing the gradient with respect to
        # reparameterized shape parameters.
        result_1 = (test_index[0] /
                    (1 + np.exp(natural_shapes[0] * test_index[0])))
        result_3 = (test_index[2] /
                    (1 + np.exp(natural_shapes[2] * test_index[2])))
        correct_derivatives = np.array([result_1 * natural_shapes[0],
                                        0,
                                        result_3 * natural_shapes[2],
                                        test_index[3] * natural_shapes[0],
                                        0])

        self.assertIsInstance(derivative, type(test_output))
        self.assertEqual(len(derivative.shape), 2)
        self.assertEqual(derivative.shape, (num_rows, num_rows))
        npt.assert_allclose(correct_derivatives,
                            np.diag(derivative.A))

        return None

<MSG> PyLogit release v1.0.0

Squashed commit of the following:

commit 17aaf1b68df49f4c36a691787f999df548f1e643
Author: Timothy Brathwaite <timothyb0912@gmail.com>
Date:   Sun Dec 27 00:18:12 2020 -0800

    Py2 and Py3 < 3.6 support removal newsfragment

commit 604220b62410bace5f935bdb3f86427b46f8cedc
Author: Timothy Brathwaite <timothyb0912@gmail.com>
Date:   Sun Dec 27 00:14:05 2020 -0800

    Updating changelog w/release notes

commit e5fdef942d7b8d60fc85bd619385932d97e88dd3
Author: Timothy Brathwaite <timothyb0912@gmail.com>
Date:   Sun Dec 27 00:04:10 2020 -0800

    Bumping version number for release

commit 9947da6c25d7e49c0ed48964683675a05258a491
Author: Timothy Brathwaite <timothyb0912@gmail.com>
Date:   Sat Dec 26 23:57:29 2020 -0800

    Added bump2version to requirements

    Squashed commit of the following:

    commit 5a36a113516b0df8abf1488f1c235141eb1aad3c
    Author: Timothy Brathwaite <timothyb0912@gmail.com>
    Date:   Sat Dec 26 23:49:20 2020 -0800

        bump2version newsfragment

    commit c3a13624d5390860e4930acbac05dcd165dda775
    Author: Timothy Brathwaite <timothyb0912@gmail.com>
    Date:   Sat Dec 26 23:47:30 2020 -0800

        Adding bump2version to requirements

commit 1f905a4dc4cd732b494b5cb43047cb857a69e833
Author: Timothy Brathwaite <timothyb0912@gmail.com>
Date:   Sat Dec 26 23:42:58 2020 -0800

    Updating README from rst -> md

    Squashed commit of the following:

    commit ebdad6114a6ce906d6795b2def4f8db1d76d8732
    Author: Timothy Brathwaite <timothyb0912@gmail.com>
    Date:   Sat Dec 26 23:30:17 2020 -0800

        README.md newsfragment

    commit e2a22d4c2b20147cacdde4dbee4d7237a3cd5c47
    Author: Timothy Brathwaite <timothyb0912@gmail.com>
    Date:   Sat Dec 26 23:26:16 2020 -0800

        Remove README.rst

    commit 5f6e78788b22394599694f1add353a3782ff9dde
    Author: Timothy Brathwaite <timothyb0912@gmail.com>
    Date:   Sat Dec 26 23:25:49 2020 -0800

        Reference README.md in pyproject.toml not rst

    commit f7e70becff91de1ae23b453cb498dad3b8891b88
    Author: Timothy Brathwaite <timothyb0912@gmail.com>
    Date:   Sat Dec 26 23:25:00 2020 -0800

        Add README.md to the repo

    commit 63cdf20dee895426e205e78612f801b3fd00d5b4
    Author: Timothy Brathwaite <timothyb0912@gmail.com>
    Date:   Sat Dec 26 23:14:14 2020 -0800

        Ensuring tox files are not stored on remote repo

commit afa44d91204cc7c363888053015cd1c3f84485c5
Author: Timothy Brathwaite <timothyb0912@gmail.com>
Date:   Sat Dec 26 22:40:18 2020 -0800

    Adding GH Actions workflow for testing

    Squashed commit of the following:

    commit 6fcf40214aa9ac2aa7a85fd527fc09bd2f75b356
    Author: Timothy Brathwaite <timothyb0912@gmail.com>
    Date:   Sat Dec 26 22:33:04 2020 -0800

        GH Actions newsfragment

    commit 9d38d091853157d183eb80b38d7304f76b65390e
    Author: Timothy Brathwaite <timothyb0912@gmail.com>
    Date:   Sat Dec 26 22:11:18 2020 -0800

        Adding a GH actions workflow for project testing

commit d3714b759ccebfd1210de25908e6ad22b9300703
Author: Timothy Brathwaite <timothyb0912@gmail.com>
Date:   Sat Dec 26 22:00:43 2020 -0800

    Adding tox and tox-conda to the repo

    Squashed commit of the following:

    commit 3eed45d655be098e09e68fbc17d0b81b771109b1
    Author: Timothy Brathwaite <timothyb0912@gmail.com>
    Date:   Sat Dec 26 21:59:34 2020 -0800

        Tox newsfragment

    commit 7d8c68a5ac78a34cd930c23f0393876c90e69209
    Author: Timothy Brathwaite <timothyb0912@gmail.com>
    Date:   Sat Dec 26 21:55:36 2020 -0800

        Adding tox configuration to pyproject.toml

    commit ff97bc4548150adcdbfc419d653b14de5a572bb3
    Author: Timothy Brathwaite <timothyb0912@gmail.com>
    Date:   Sat Dec 26 21:55:15 2020 -0800

        Adding tox-conda to the requirements

    commit 3d090ee1e257f32a2158333b53a261ecec6ad31c
    Author: Timothy Brathwaite <timothyb0912@gmail.com>
    Date:   Sat Dec 26 21:46:59 2020 -0800

        Adding tox to development requirements

commit f526cbb04977d30d5f3752ec00d22f1102aa522b
Author: Timothy Brathwaite <timothyb0912@gmail.com>
Date:   Sat Dec 26 21:35:41 2020 -0800

    Newsfragments for all changes from v0.2.2 to #62

    Squashed commit of the following:

    commit ca137a2b38dc5739d89073fd156435a9f0a17190
    Author: Timothy Brathwaite <timothyb0912@gmail.com>
    Date:   Sat Dec 26 21:34:07 2020 -0800

        License newsfragment

    commit f1a7868efb45fb71dae0b03b0f88cb66e3c68c67
    Author: Timothy Brathwaite <timothyb0912@gmail.com>
    Date:   Sat Dec 26 21:14:37 2020 -0800

        Fixing bootstrap import newsfragment

    commit e57ebfe782366ec5bb130c1adc43a982fbe3da76
    Author: Timothy Brathwaite <timothyb0912@gmail.com>
    Date:   Sat Dec 26 21:13:54 2020 -0800

        Example notebook py2/3 compatibility newsfragment

    commit e91dca099d113f06a05400cd0cf3e277e27f8322
    Author: Timothy Brathwaite <timothyb0912@gmail.com>
    Date:   Sat Dec 26 21:12:51 2020 -0800

        Test fix newsfragment

    commit 24fb1e6adace2ab1b1e07c61c1a5b0907c20f795
    Author: Timothy Brathwaite <timothyb0912@gmail.com>
    Date:   Sat Dec 26 21:12:28 2020 -0800

        Hessian memory newsfragment

    commit b32df1c666a69093118e0e338d83022fa46a7bdd
    Author: Timothy Brathwaite <timothyb0912@gmail.com>
    Date:   Sat Dec 26 20:53:12 2020 -0800

        Hessian bugfix newsfragment

    commit 28ea3650e410a86a9f6484997b5d06c6a9bfb538
    Author: Timothy Brathwaite <timothyb0912@gmail.com>
    Date:   Sat Dec 26 20:52:47 2020 -0800

        Journal reference newsfragrment

    commit d25b31ba51a99b540588e6226043ff0b6cefefc1
    Author: Timothy Brathwaite <timothyb0912@gmail.com>
    Date:   Sat Dec 26 20:52:21 2020 -0800

        Project logo newsfragment

    commit 8dbd477585ca1bf25aefb2ad5ec1c32eb708ebaf
    Author: Timothy Brathwaite <timothyb0912@gmail.com>
    Date:   Sat Dec 26 20:44:56 2020 -0800

        Pip-tools newsfragment

    commit c8d8a7a38bc3b4a971ca624bc4ff671e160f99b6
    Author: Timothy Brathwaite <timothyb0912@gmail.com>
    Date:   Sat Dec 26 20:44:36 2020 -0800

        Makefile newsfragment

    commit 08c9d07eeb29810f62951ab47231cbda9e416f10
    Author: Timothy Brathwaite <timothyb0912@gmail.com>
    Date:   Sat Dec 26 20:41:45 2020 -0800

        Flit newsfragment entry

    commit 9d1681d77de2227d3d013afa9203a0220510f7dd
    Author: Timothy Brathwaite <timothyb0912@gmail.com>
    Date:   Sat Dec 26 20:40:15 2020 -0800

        Re-classified towncrier newsfragment

    commit f22e4e649860cadb93dc640c22c7c092a4ca2f67
    Author: Timothy Brathwaite <timothyb0912@gmail.com>
    Date:   Sat Dec 26 20:35:53 2020 -0800

        Adding towncrier newsfragment for towncrier

commit 6f226ada940f1ab856d44bf8769fc6ad1c3d9b99
Author: Timothy Brathwaite <timothyb0912@gmail.com>
Date:   Sat Dec 26 20:23:16 2020 -0800

    Adding towncrier and CHANGELOG to repository.

    Squashed commit of the following:

    commit 2e549720b8ec0af473a11da3a56f8fdfee281ef3
    Author: Timothy Brathwaite <timothyb0912@gmail.com>
    Date:   Sat Dec 26 20:16:48 2020 -0800

        Updating links & moving changelog to separate file

    commit a7dabba3927aa2ce4f928e425bd6b0e6256592bf
    Author: Timothy Brathwaite <timothyb0912@gmail.com>
    Date:   Sat Dec 26 20:04:49 2020 -0800

        Creating changelog file

    commit 737973761cbe9b1cb4e31d55333b131bf22c4738
    Author: Timothy Brathwaite <timothyb0912@gmail.com>
    Date:   Sat Dec 26 19:29:14 2020 -0800

        Adding folder for newsfragments

    commit 3a8a54716ce84eeae5aa24550d56710dd1eb52ec
    Author: Timothy Brathwaite <timothyb0912@gmail.com>
    Date:   Sat Dec 26 19:26:45 2020 -0800

        Adding towncrier to pyproject.toml

    commit f874ce1943ea6086208dff83890d4f4297b4403c
    Author: Timothy Brathwaite <timothyb0912@gmail.com>
    Date:   Sat Dec 26 19:20:50 2020 -0800

        Add towncrier to requirements

commit 7962e477fe7d156f7b205e1f616de8cf397c4248
Author: Timothy Brathwaite <timothyb0912@gmail.com>
Date:   Sat Dec 26 18:45:33 2020 -0800

    Adding flit and pyproject.toml

    Squashed commit of the following:

    commit ae96508d6e4eb27642311c1c8033675bc53c415f
    Author: Timothy Brathwaite <timothyb0912@gmail.com>
    Date:   Sat Dec 26 18:30:10 2020 -0800

        Standardizing pyproject.toml flit keys

    commit 97b54e6b09abbfe7eb85743b7ab01f0adc08b4ae
    Author: Timothy Brathwaite <timothyb0912@gmail.com>
    Date:   Sat Dec 26 18:29:32 2020 -0800

        Moving pylogit to src layout

    commit 07a08517156619876c8facdd51731e9f12fefe48
    Author: Timothy Brathwaite <timothyb0912@gmail.com>
    Date:   Sat Dec 26 18:24:15 2020 -0800

        Adding version number to package

    commit 2960029119a81b4614ff00fc70d7dccf70d4cb43
    Author: Timothy Brathwaite <timothyb0912@gmail.com>
    Date:   Sat Dec 26 18:22:07 2020 -0800

        Adding pyproject.toml

    commit 792756b263a47254221ffdebe954e1e39d6104c0
    Author: Timothy Brathwaite <timothyb0912@gmail.com>
    Date:   Sat Dec 26 18:09:12 2020 -0800

        Add flit to development requirements

commit 816b8eac44f43303f3834e2bcbe3418d803f2330
Author: Timothy Brathwaite <timothyb0912@gmail.com>
Date:   Sat Dec 26 18:01:33 2020 -0800

    Adding Makefile

    Squashed commit of the following:

    commit 48d9f3196c5b3767cf911fc83b9d59363cac5c5f
    Author: Timothy Brathwaite <timothyb0912@gmail.com>
    Date:   Sat Dec 26 17:50:24 2020 -0800

        Creating makefile for installation & update reqs

commit 415a88955a1e4f3e8c548cbdf6950f37850c2367
Author: Timothy Brathwaite <timothyb0912@gmail.com>
Date:   Sat Dec 26 14:57:19 2020 -0800

    Updating requirements and resolving numpy warnings
    Squashed commit of the following:

    commit cc60fe775cf5662239ddeb9a04fdb2c51c94b9b6
    Author: Timothy Brathwaite <timothyb0912@gmail.com>
    Date:   Sat Dec 26 14:08:21 2020 -0800

        Respond to userwarning about hstack and generators

    commit 55b2eb6b6117403b55b07dfebf94c6bcad05d4c6
    Author: Timothy Brathwaite <timothyb0912@gmail.com>
    Date:   Sat Dec 26 13:59:19 2020 -0800

        Adding all package and test requirements for dev

    commit 23a675310c1a21e5deeb5f231e8e0a7990259260
    Author: Timothy Brathwaite <timothyb0912@gmail.com>
    Date:   Sat Dec 26 13:55:13 2020 -0800

        Adding pytest and pytest-cov to requirements

    commit 82e89d39f58e9304783e2233cdb44f94d8f5d9cf
    Author: Timothy Brathwaite <timothyb0912@gmail.com>
    Date:   Sat Dec 26 13:53:16 2020 -0800

        Adding requirements.(in|txt)

commit 9a36632f99cdb6b619a5394b8ba1350a929c557e
Merge: d54b5e9 681aadd
Author: Timothy Brathwaite <timothyb0912@users.noreply.github.com>
Date:   Sat May 2 00:04:11 2020 -0700

    Merge pull request #46 from timothyb0912/feature/add-logo

    Feature/add logo

commit 681aadd50d8ff94f3ead7532f533c4c8d44240a9
Author: Timothy Brathwaite <timothyb0912@gmail.com>
Date:   Fri May 1 23:30:49 2020 -0700

    Updating repository with package logo.

commit d54b5e9ea18774f094a4987d5a4022f98e1d525a
Merge: 8d95e17 5e914f0
Author: Timothy Brathwaite <timothyb0912@users.noreply.github.com>
Date:   Sat Jan 19 16:53:26 2019 -0800

    Merge pull request #35 from timothyb0912/feature/update-paper-ref

    Added journal reference for article.

commit 5e914f08dd900e463606ead1d7e3bcf3356e6932
Author: Timothy Brathwaite <timothyb0912@gmail.com>
Date:   Sat Jan 19 16:56:16 2019 -0800

    Edit hyperlink.

commit 2fa70ba6c2c073c95dec7254404ed3aa075cfaa4
Author: Timothy Brathwaite <timothyb0912@gmail.com>
Date:   Sat Jan 19 16:54:25 2019 -0800

    Added journal reference for article.

commit 8d95e1753cabac6ef437bafdf0a6d49e8318212d
Merge: 5c90569 352a7f7
Author: Timothy Brathwaite <timothyb0912@users.noreply.github.com>
Date:   Sat Jan 19 16:36:29 2019 -0800

    Merge pull request #33 from timothyb0912/feature/only-penalize-diag

    Only penalizing hessian diagonal.

commit 352a7f7a45e8a3a5bd1561c3e0192f6aeb52efd3
Author: Timothy Brathwaite <timothyb0912@gmail.com>
Date:   Sat Jan 19 16:34:12 2019 -0800

    Push L2-regularization changes to gradient-transpose gradient matrix.

commit df95d8c6cf2caed7401f0f05b80cb706c5a19268
Author: Timothy Brathwaite <timothyb0912@gmail.com>
Date:   Sat Jan 19 16:28:34 2019 -0800

    Pushing hessian changes to nested and mixed logit.

commit 78bbbbf8f57bf8994cd6a10698507d0ec9cf1935
Author: Timothy Brathwaite <timothyb0912@gmail.com>
Date:   Sat Jan 19 16:18:53 2019 -0800

    Added encoding information to files.

commit 9f7fb55fac7ba32157303c0c0552cb1073595c40
Author: Timothy Brathwaite <timothyb0912@gmail.com>
Date:   Sun Jan 13 18:19:15 2019 -0800

    Only penalizing hessian diagonal.

commit 5c90569c594ba009dcbd0e54a8aefec743b5a7cf
Merge: 08852f8 f6ee0a2
Author: Timothy Brathwaite <timothyb0912@users.noreply.github.com>
Date:   Mon Nov 5 18:07:01 2018 -0800

    Merge pull request #30 from timothyb0912/feature/decrease-hessian-memory

    Feature/decrease hessian memory

commit f6ee0a231eb272ede0b38bbf678f9c119423a672
Author: Timothy Brathwaite <timothyb0912@gmail.com>
Date:   Mon Nov 5 17:53:35 2018 -0800

    Updating computation descriptions.

commit 76adad556d37614ad8fbb5dd77d1abffd0fda3cd
Author: Timothy Brathwaite <timothyb0912@gmail.com>
Date:   Mon Nov 5 11:49:16 2018 -0800

    Removed code for calculating the hessianwith inefficient memory usage.

commit 5ffb8d124bdc44d8174f2cc20b8d539e7aa4cb71
Author: Timothy Brathwaite <timothyb0912@gmail.com>
Date:   Mon Nov 5 11:16:07 2018 -0800

    Added memory-efficient hessian calculations.

commit 08852f8bfd70383f76f6033cee57e44f857ef4db
Merge: 985ac0f 39849ad
Author: Timothy Brathwaite <timothyb0912@users.noreply.github.com>
Date:   Fri Nov 2 18:17:34 2018 -0700

    Merge pull request #29 from timothyb0912/feature/update-py3-type-checks

    Adding mocks to fix LinalgErrors with hessian inversions in tests.

commit 39849add0d6e9c8a1da66f6058adc496575efa16
Author: Timothy Brathwaite <timothyb0912@gmail.com>
Date:   Fri Nov 2 18:08:40 2018 -0700

    Adding mocks to fix LinalgErrors with hessian inversions in tests.

commit 985ac0f0e7f47b7dfd5d6258c383af96251aff1e
Merge: 06b94f4 d3942d2
Author: Timothy Brathwaite <timothyb0912@users.noreply.github.com>
Date:   Fri Nov 2 15:27:10 2018 -0700

    Merge pull request #28 from timothyb0912/feature/update-py3-type-checks

    Feature/update py3 type checks

commit d3942d2b2e3aa0aae6a61a55af24a316274c5092
Author: Timothy Brathwaite <timothyb0912@gmail.com>
Date:   Fri Nov 2 15:23:55 2018 -0700

    Updating notebooks for py3 compatibility.

commit 2989b38124992152a63ca231ed1567d81fe04694
Author: Timothy Brathwaite <timothyb0912@gmail.com>
Date:   Fri Nov 2 14:49:47 2018 -0700

    Made dataframe slicing more explicity. Explicitly set numerical tolerance in test.

commit 6d28c8db91430c74458224fff225a8d620c266c3
Author: Timothy Brathwaite <timothyb0912@gmail.com>
Date:   Fri Nov 2 14:48:23 2018 -0700

    Changed logic to account for new range type in py3.

commit 06b94f4510245d795a0061c43f66bc9fbcabf130
Merge: de5f2e5 e4b742b
Author: Timothy Brathwaite <timothyb0912@users.noreply.github.com>
Date:   Fri Nov 2 14:30:52 2018 -0700

    Merge pull request #27 from timothyb0912/feature/import-boot

    Feature/import boot

commit e4b742bd905895804152c28f163963d92abb1178
Merge: ef2c0a9 de5f2e5
Author: Timothy Brathwaite <timothyb0912@gmail.com>
Date:   Fri Nov 2 14:13:08 2018 -0700

    Merge branch 'develop' of https://github.com/timothyb0912/pylogit into make-bootstrap-importable

commit ef2c0a9772d363c87d90fbfa2da840973e9f1307
Author: Timothy Brathwaite <timothyb0912@gmail.com>
Date:   Fri Nov 2 14:11:30 2018 -0700

    Editing comments and docstrings.

commit 0e374f8e8cd850ddfe41f2b61beaec708674f499
Author: Timothy Brathwaite <timothyb0912@gmail.com>
Date:   Fri Nov 2 14:11:10 2018 -0700

    Adding absolute imports.

commit de5f2e547eaae650bc331c4214444eac6009737a
Merge: 5edd148 f83b0fd
Author: Timothy Brathwaite <timothyb0912@gmail.com>
Date:   Thu Oct 11 17:59:45 2018 -0700

    Changed version number to match master.

commit 5edd148d5e6e29ca78f0a88870d51854a966913f
Author: Timothy Brathwaite <timothyb0912@gmail.com>
Date:   Mon Dec 11 01:30:12 2017 -0800

    Made sure we are py3 compatible.

commit fffe9052377590b2c9fd8365a16184291254d2b5
Author: Timothy Brathwaite <timothyb0912@gmail.com>
Date:   Mon Dec 11 01:29:55 2017 -0800

    Kept version number up to date.

commit 2faed2dc5bf52e04686025e018f27043ee8bd1e1
Author: Timothy Brathwaite <timothyb0912@gmail.com>
Date:   Mon Dec 11 01:29:42 2017 -0800

    Got rid of long-ints for TravisCI testing.

commit a0cc366c41a458e361bd08e4c90d9f7a845c4727
Merge: 836ec1a 6461486
Author: Timothy Brathwaite <timothyb0912@gmail.com>
Date:   Mon Dec 11 00:38:15 2017 -0800

    Merge tag 'add-tqdm' into develop

    Added tqdm to package dependencies.

commit 836ec1a98c600f7ae100387872f37bccbb920ab3
Author: Timothy Brathwaite <timothyb0912@gmail.com>
Date:   Mon Dec 11 00:36:17 2017 -0800

    Added tqdm as a dependency.

commit bbd60417f923233aa87050f53280c70a5bd42257
Merge: 93090cd 998e9f7
Author: Timothy Brathwaite <timothyb0912@gmail.com>
Date:   Sun Dec 10 23:19:18 2017 -0800

    Merge tag '0.2.0' into develop

    Adding bootstrap and py3 support.

<DFF> @@ -8,6 +8,7 @@ import unittest
 from collections import OrderedDict
 from copy import deepcopy
 
+import mock
 import numpy as np
 import numpy.testing as npt
 import pandas as pd
@@ -153,7 +154,11 @@ class ChoiceObjectTests(GenericTestCase):
 
         return None
 
-    def test_ridge_warning_in_fit_mle(self):
+    # Note we mock to avoid issues with inverting the hessian, especially since
+    # such inversion is not the point of this test.
+    @mock.patch('pylogit.base_multinomial_cm_v2.scipy.linalg.inv',
+                side_effect=lambda x: np.eye(x.shape[0]))
+    def test_ridge_warning_in_fit_mle(self, mock_inv):
         """
         Ensure that a UserWarning is raised when one passes the ridge keyword
         argument to the `fit_mle` method of an Uneven Logit model object.

PyLogit release v1.0.0