Beispiel #1
0
    def test_trivial_crossvalidation(self):

        for i in range(1, 10):
            m_a = DenseMatrix(np.mat(np.random.random((i + 1, 4))))
            m_b = DenseMatrix(np.mat(np.random.random((i + 1, 4))))
            tmp_a = m_a.mat.copy()
            tmp_b = m_b.mat.copy()

            learner = RidgeRegressionLearner(param_range=[3], intercept=False)
            solution = learner.train(m_a, m_b)

            learner2 = RidgeRegressionLearner(param=3, intercept=False)
            solution2 = learner2.train(m_a, m_b)

            np.testing.assert_array_equal(tmp_a, m_a.mat)
            np.testing.assert_array_equal(tmp_b, m_b.mat)
            np.testing.assert_array_equal(solution.mat, solution2.mat)

            learner = RidgeRegressionLearner(param_range=[3], intercept=False)
            solution = learner.train(m_a, m_b)

            np.testing.assert_array_equal(tmp_a, m_a.mat)
            np.testing.assert_array_equal(tmp_b, m_b.mat)
            np.testing.assert_array_equal(solution.mat, solution2.mat)

            learner = RidgeRegressionLearner(param_range=[0], intercept=False)
            solution = learner.train(m_a, m_b)

            learner2 = LstsqRegressionLearner(intercept=False)
            solution2 = learner2.train(m_a, m_b)

            np.testing.assert_array_almost_equal(solution.mat, solution2.mat,
                                                 3)
    def test_min_samples1(self):
        #TODO test a1_car twice in the phrase list
        train_data = [("bla3", "man", "a1_car"),
                      ("a1", "car", "a1_car"),
                      ("bla2", "man", "a1_car"),
                      ("a1", "man", "a1_man"),
                      ("bla1", "man", "a1_car")
        ]
        #model with train and then compose
        learner_ = LstsqRegressionLearner(intercept=True)
        model = LexicalFunction(learner=learner_, min_samples=2)

        model.train(train_data, self.n_space, self.an_space)

        new_space = model.function_space

        np.testing.assert_array_almost_equal(new_space.cooccurrence_matrix.mat,
                                             np.mat([[0.66666667, 0.33333333,
                                                      -0.33333333, 0.33333333,
                                                      0.66666667, 0.33333333]]),
                                             7)

        self.assertTupleEqual(new_space.element_shape, (2, 3))
        self.assertListEqual(new_space.id2row, ["a1"])
        self.assertListEqual(new_space.id2column, [])
    def test_crossvalidation(self):

        a = DenseMatrix(np.matrix([[1, 1],[2, 3],[4, 6]]))
        b = DenseMatrix(np.matrix([[12, 15, 18],[21, 27, 33],[35, 46, 57]]))
        res = DenseMatrix(np.matrix([[1, 2, 3],[4, 5, 6],[7, 8, 9]]))

        learner = RidgeRegressionLearner(intercept=True, param_range=[0])
        learner2 = LstsqRegressionLearner(intercept=False)

        res1 = learner2.train(a, b)
        res2 = learner.train(a, b)

        np.testing.assert_array_almost_equal(res2.mat[:-1,:], res[0:2,:].mat, 6)
        np.testing.assert_array_almost_equal(res2.mat[-1,:], res[2:3,:].mat, 6)

        new_a = padd_matrix(a, 1)
        self.assertGreater(((a * res1) - b).norm(), ((new_a * res2) - b).norm())
Beispiel #4
0
    def test_crossvalidation(self):

        a = DenseMatrix(np.matrix([[1, 1], [2, 3], [4, 6]]))
        b = DenseMatrix(np.matrix([[12, 15, 18], [21, 27, 33], [35, 46, 57]]))
        res = DenseMatrix(np.matrix([[1, 2, 3], [4, 5, 6], [7, 8, 9]]))

        learner = RidgeRegressionLearner(intercept=True, param_range=[0])
        learner2 = LstsqRegressionLearner(intercept=False)

        res1 = learner2.train(a, b)
        res2 = learner.train(a, b)

        np.testing.assert_array_almost_equal(res2.mat[:-1, :], res[0:2, :].mat,
                                             6)
        np.testing.assert_array_almost_equal(res2.mat[-1, :], res[2:3, :].mat,
                                             6)

        new_a = padd_matrix(a, 1)
        self.assertGreater(((a * res1) - b).norm(),
                           ((new_a * res2) - b).norm())
    def test_simple_train_compose_intercept(self):
        #TODO test a1_car twice in the phrase list
        train_data = [("a1", "car", "a1_car"),
                      ("a1", "man", "a1_man"),
        ]
        #model with train and then compose
        learner_ = LstsqRegressionLearner(intercept=True)
        model = LexicalFunction(learner=learner_)

        model.train(train_data, self.n_space, self.an_space)

        new_space = model.function_space

        np.testing.assert_array_almost_equal(new_space.cooccurrence_matrix.mat,
                                             np.mat([[0.66666667, 0.33333333,
                                                      -0.33333333, 0.33333333,
                                                      0.66666667, 0.33333333]]),
                                             7)

        self.assertTupleEqual(new_space.element_shape, (2, 3))
        self.assertListEqual(new_space.id2row, ["a1"])
        self.assertListEqual(new_space.id2column, [])

        comp_space = model.compose(train_data, self.n_space)

        np.testing.assert_array_almost_equal(comp_space.cooccurrence_matrix.mat,
                                             self.an_space.cooccurrence_matrix.mat, 10
        )

        self.assertListEqual(comp_space.id2row, ["a1_car", "a1_man"])
        self.assertListEqual(comp_space.id2column, self.ft)

        #new model, without training
        model2 = LexicalFunction(function_space=new_space, intercept=True)
        comp_space = model2.compose(train_data, self.n_space)

        self.assertListEqual(comp_space.id2row, ["a1_car", "a1_man"])
        self.assertListEqual(comp_space.id2column, [])
        np.testing.assert_array_almost_equal(comp_space.cooccurrence_matrix.mat,
                                             self.n_space.cooccurrence_matrix.mat,
                                             8)
        #recursive application
        comp_space2 = model2.compose([("a1", "a1_car", "a1_a1_car"),
                                      ("a1", "a1_man", "a1_a1_man")],
                                     comp_space)

        self.assertListEqual(comp_space2.id2row, ["a1_a1_car", "a1_a1_man"])
        self.assertListEqual(comp_space.id2column, [])

        np.testing.assert_array_almost_equal(comp_space2.cooccurrence_matrix.mat,
                                             self.n_space.cooccurrence_matrix.mat,
                                             8)
        self.assertEqual(comp_space.element_shape, (2,))
        self.assertEqual(comp_space2.element_shape, (2,))
    def test_train_intercept(self):
        a1_mat = DenseMatrix(np.mat([[3, 4], [5, 6]]))
        a2_mat = DenseMatrix(np.mat([[1, 2], [3, 4]]))

        train_data = [("a1", "man", "a1_man"),
                      ("a2", "car", "a2_car"),
                      ("a1", "boy", "a1_boy"),
                      ("a2", "boy", "a2_boy")
        ]

        n_mat = DenseMatrix(np.mat([[13, 21], [3, 4], [5, 6]]))
        n_space = Space(n_mat, ["man", "car", "boy"], self.ft)

        an1_mat = (a1_mat * n_mat.transpose()).transpose()
        an2_mat = (a2_mat * n_mat.transpose()).transpose()
        an_mat = an1_mat.vstack(an2_mat)

        an_space = Space(an_mat, ["a1_man", "a1_car", "a1_boy", "a2_man", "a2_car", "a2_boy"], self.ft)

        #test train
        model = LexicalFunction(learner=LstsqRegressionLearner(intercept=True))
        model.train(train_data, n_space, an_space)
        a_space = model.function_space

        a1_mat.reshape((1, 4))
        #np.testing.assert_array_almost_equal(a1_mat.mat,
        #                                     a_space.cooccurrence_matrix.mat[0])

        a2_mat.reshape((1, 4))
        #np.testing.assert_array_almost_equal(a2_mat.mat,
        #                                     a_space.cooccurrence_matrix.mat[1])

        self.assertListEqual(a_space.id2row, ["a1", "a2"])
        self.assertTupleEqual(a_space.element_shape, (2, 3))

        #test compose
        a1_mat = DenseMatrix(np.mat([[3, 4, 5, 6]]))
        a2_mat = DenseMatrix(np.mat([[1, 2, 3, 4]]))
        a_mat = a_space.cooccurrence_matrix

        a_space = Space(a_mat, ["a1", "a2"], [], element_shape=(2, 3))
        model = LexicalFunction(function_space=a_space, intercept=True)
        comp_space = model.compose(train_data, n_space)

        self.assertListEqual(comp_space.id2row, ["a1_man", "a2_car", "a1_boy", "a2_boy"])
        self.assertListEqual(comp_space.id2column, [])

        self.assertEqual(comp_space.element_shape, (2,))

        np.testing.assert_array_almost_equal(comp_space.cooccurrence_matrix.mat,
                                             an_mat[[0, 4, 2, 5]].mat, 8)
Beispiel #7
0
    def test_train1(self):
        test_cases = [(self.m11, self.m21, self.ph1, np.mat([[2]]),
                       np.mat([[3]])),
                      (self.m11, self.m21, DenseMatrix(np.mat([[0], [0]])),
                       np.mat([[0]]), np.mat([[0]]))]

        for m1, m2, ph, expected_a, expected_b in test_cases:
            comp_model = FullAdditive(learner=LstsqRegressionLearner(
                intercept=False))
            comp_model._train(m1, m2, ph)
            np.testing.assert_array_almost_equal(
                comp_model._mat_a_t.transpose().mat, expected_a, 10)
            np.testing.assert_array_almost_equal(
                comp_model._mat_b_t.transpose().mat, expected_b, 10)
Beispiel #8
0
    def test_train2(self):
        dim_ = 2
        dim_1 = 3
        dim_2 = 5
        for dim in [dim_1 + dim_2, dim_1 + dim_2 + 2]:
            expected_a = np.mat(np.random.random((dim_, dim_1)))
            expected_b = np.mat(np.random.random((dim_, dim_2)))
            m1 = np.mat(np.random.random((dim, dim_1)))
            m2 = np.mat(np.random.random((dim, dim_2)))

            ph = np.mat(expected_a * m1.T + expected_b * m2.T)

            comp_model = FullAdditive(learner=LstsqRegressionLearner(
                intercept=False))
            comp_model._train(DenseMatrix(m1), DenseMatrix(m2),
                              DenseMatrix(ph).transpose())
            np.testing.assert_array_almost_equal(
                comp_model._mat_a_t.transpose().mat, expected_a, 10)
            np.testing.assert_array_almost_equal(
                comp_model._mat_b_t.transpose().mat, expected_b, 10)

        for dim in [dim_1 + dim_2 + 6, dim_1 + dim_2 + 20]:
            expected_a = np.mat(np.random.random((dim_, dim_1)))
            expected_b = np.mat(np.random.random((dim_, dim_2)))
            m1 = np.mat(np.random.random((dim, dim_1)))
            m2 = np.mat(np.random.random((dim, dim_2)))

            ph = np.mat(expected_a * m1.T + expected_b * m2.T)

            comp_model = FullAdditive(learner=LstsqRegressionLearner(
                intercept=True))
            comp_model._train(DenseMatrix(m1), DenseMatrix(m2),
                              DenseMatrix(ph).transpose())
            np.testing.assert_array_almost_equal(
                comp_model._mat_a_t.transpose().mat, expected_a, 10)
            np.testing.assert_array_almost_equal(
                comp_model._mat_b_t[:-1, :].transpose().mat, expected_b, 10)
Beispiel #9
0
    def test_space_train_dense(self):

        test_cases = [
            ([("a", "b", "a_b")], self.space4, self.space5),
            ([("a", "b", "a_b")], self.space4, self.space6),
            ([("a", "b", "a_b"), ("a", "b", "a_a")], self.space4, self.space7),
        ]

        learners = [
            RidgeRegressionLearner(intercept=False,
                                   crossvalidation=False,
                                   param=0),
            LstsqRegressionLearner(intercept=False),
            LstsqRegressionLearner(intercept=True)
        ]

        for in_data, arg_space, phrase_space in test_cases:
            for learner_ in learners:
                comp_model = FullAdditive(learner=learner_)

                comp_model.train(in_data, arg_space, phrase_space)
                comp_space = comp_model.compose(in_data, arg_space)

                np.testing.assert_array_almost_equal(
                    comp_space.cooccurrence_matrix.mat,
                    phrase_space.cooccurrence_matrix.mat, 10)

                self.assertListEqual(comp_space.id2column,
                                     phrase_space.id2column)
                self.assertDictEqual(comp_space.column2id,
                                     phrase_space.column2id)

                self.assertListEqual(comp_space.id2row, phrase_space.id2row)
                self.assertDictEqual(comp_space.row2id, phrase_space.row2id)

                self.assertEqual(comp_model._has_intercept,
                                 learner_._intercept)
    def __init__(self, **kwargs):
        """
        Constructor.
        
        Args:
            function_space= : function space parameter, containing
            the lexical functions, of type Space. Optional, can be set through
            training.
            
            intercept= : True/False, True if the function space has intercept.
            Optional, default False. When training is used, intercept is set 
            to the intercept value of the regression learner used.
        
            learner= : regression method of type RegressionLearner. Optional,
            default LstsqRegressionLearner.
            
        """
        assert_valid_kwargs(kwargs, ["function_space", "intercept", "learner"])
        
        self._regression_learner = LstsqRegressionLearner()
        self.composed_id2column = []
        self._function_space = None
        self._has_intercept = False
        
        if "function_space" in kwargs:
            space = kwargs["function_space"]
            if not isinstance(space, Space):
                raise TypeError("expected Space-type argument, received:" 
                                 % type(space))
            self._function_space = kwargs["function_space"]

        if "intercept" in kwargs:
            has_intercept = kwargs["intercept"]
            if not isinstance(has_intercept, bool):
                raise TypeError("expected bool-type argument, received:" 
                                 % type(has_intercept))
            self._has_intercept = has_intercept
            
        if "learner" in kwargs:
            if "function_space" in kwargs:
                raise ValueError("cannot instantiate with both learner and function_space!")
             
            self._regression_learner = kwargs["learner"] 
Beispiel #11
0
    def __init__(self, **kwargs):
        #TODO here; very important, should be able to set the intercept
        #when mat a and mat b are given , to true or false. now by default is
        #is false
        """
        Constructor.

        Args:
            A= : matrix A, of matrix-like type (Matrix, ndarray,
            numpy matrix, scipy matrix). Optional (parameters can be set
            through training.)

            B= : matrix B, matrix-like type. Optional.

            learner= : regression learner object, of type RegressionLearner.
            Optional, default LstsqRegressionLearner.
        """
        assert_valid_kwargs(kwargs, ["A", "B", "learner"])

        if "A" in kwargs and "B" in kwargs:
            mat_a = kwargs["A"]
            mat_b = kwargs["B"]
            if not is_array_or_matrix(mat_a):
                raise TypeError("expected matrix type, received: %s"
                                % type(mat_a))

            if not is_array_or_matrix(mat_b):
                raise TypeError("expected matrix type, received: %s"
                                % type(mat_b))

            mat_a, mat_b = to_compatible_matrix_types(mat_a, mat_b)
            self._mat_a_t = mat_a.transpose()
            self._mat_b_t = mat_b.transpose()
            self._has_intercept = False

        else:
            self._regression_learner = LstsqRegressionLearner()
            if "learner" in kwargs:
                self._regression_learner = kwargs["learner"]
            self._has_intercept = self._regression_learner.has_intercept()
Beispiel #12
0
    def __init__(self, A=None, B=None, learner=LstsqRegressionLearner()):
        #TODO here; very important, should be able to set the intercept
        #when mat a and mat b are given , to true or false. now by default is
        #is false
        """
        Constructor.

        Args:
            A= : matrix A, of matrix-like type (Matrix, ndarray,
            numpy matrix, scipy matrix). Optional (parameters can be set
            through training.)

            B= : matrix B, matrix-like type. Optional.

            learner= : regression learner object, of type RegressionLearner.
            Optional, default LstsqRegressionLearner.
        """
        if A is not None and B is not None:
            mat_a = A
            mat_b = B
            if not is_array_or_matrix(mat_a):
                raise TypeError("expected matrix type, received: %s"
                                % type(mat_a))

            if not is_array_or_matrix(mat_b):
                raise TypeError("expected matrix type, received: %s"
                                % type(mat_b))

            mat_a, mat_b = to_compatible_matrix_types(mat_a, mat_b)
            self._mat_a_t = mat_a.transpose()
            self._mat_b_t = mat_b.transpose()
            self._has_intercept = False

        else:
            self._regression_learner = learner
            self._has_intercept = self._regression_learner.has_intercept()
    def __init__(self,
                 function_space=None,
                 intercept=False,
                 learner=None,
                 min_samples=1):
        """
        Constructor.

        Args:
            function_space= : function space parameter, containing
            the lexical functions, of type Space. Optional, can be set through
            training.

            intercept= : True/False, True if the function space has intercept.
            Optional, default False. When training is used, intercept is set
            to the intercept value of the regression learner used.

            learner= : regression method of type RegressionLearner. Optional,
            default LstsqRegressionLearner.

            min_samples= : minimum number of training samples required before a
            LexicalFunction can be trained. Optional, default 1.

        """
        # assert_valid_kwargs(kwargs, ["function_space", "intercept", "learner"])

        self.composed_id2column = []
        if learner and function_space:
            raise ValueError(
                "Cannot instantiate with both learner and function_space!")

        self._regression_learner = learner if learner else LstsqRegressionLearner(
        )
        self._function_space = function_space
        self._has_intercept = intercept
        self._MIN_SAMPLES = min_samples
Beispiel #14
0
class FullAdditive(CompositionModel):
    """
    Implements the full additive compositional model:

        :math:`\\vec{p} = A \\vec{u} + B \\vec{v}`

    where :math:`\\vec{p}` is the vector of the composed phrase,
    :math:`\\vec{u}, \\vec{v}`, the vectors of the components
    and :math:`A`, :math:`B` are two matrices.

    """
    _name = "full_additive"
    _mat_a_t = None
    _mat_b_t = None


    def __init__(self, **kwargs):
        #TODO here; very important, should be able to set the intercept
        #when mat a and mat b are given , to true or false. now by default is
        #is false
        """
        Constructor.

        Args:
            A= : matrix A, of matrix-like type (Matrix, ndarray,
            numpy matrix, scipy matrix). Optional (parameters can be set
            through training.)

            B= : matrix B, matrix-like type. Optional.

            learner= : regression learner object, of type RegressionLearner.
            Optional, default LstsqRegressionLearner.
        """
        assert_valid_kwargs(kwargs, ["A", "B", "learner"])

        if "A" in kwargs and "B" in kwargs:
            mat_a = kwargs["A"]
            mat_b = kwargs["B"]
            if not is_array_or_matrix(mat_a):
                raise TypeError("expected matrix type, received: %s"
                                % type(mat_a))

            if not is_array_or_matrix(mat_b):
                raise TypeError("expected matrix type, received: %s"
                                % type(mat_b))

            mat_a, mat_b = to_compatible_matrix_types(mat_a, mat_b)
            self._mat_a_t = mat_a.transpose()
            self._mat_b_t = mat_b.transpose()
            self._has_intercept = False

        else:
            self._regression_learner = LstsqRegressionLearner()
            if "learner" in kwargs:
                self._regression_learner = kwargs["learner"]
            self._has_intercept = self._regression_learner.has_intercept()


    def _train(self, arg1_mat, arg2_mat, phrase_mat):

        self._has_intercept = self._regression_learner.has_intercept()

        result = self._regression_learner.train(arg1_mat.hstack(arg2_mat), phrase_mat)

        self._mat_a_t = result[0:arg1_mat.shape[1], :]
        self._mat_b_t = result[arg1_mat.shape[1]:, :]


    def _compose(self, arg1_mat, arg2_mat):
        #NOTE when we get in this compose arg1 mat and arg2 mat have the same type
        [mat_a_t, mat_b_t, arg1_mat] = resolve_type_conflict([self._mat_a_t,
                                                              self._mat_b_t,
                                                              arg1_mat],
                                                             type(arg1_mat))
        if self._has_intercept:
            return arg1_mat * mat_a_t + padd_matrix(arg2_mat, 1) * mat_b_t
        else:
            return arg1_mat * mat_a_t + arg2_mat * mat_b_t

    def set_regression_learner(self, regression_learner):
        assert_is_instance(regression_learner, RegressionLearner)
        self._regression_learner = regression_learner

    def get_regression_learner(self):
        return self._regression_learner

    regression_learner = property(get_regression_learner, set_regression_learner)
    """
    Regression method to be used in training, of type RegressionLearner.
    Default is LstsqRegressionLearner.
    """

    def _build_id2column(self, arg1_space, arg2_space):
        return []

    def _export(self, filename):
        if self._mat_a_t is None or self._mat_b_t is None:
            raise IllegalStateError("cannot export an untrained FullAdditive model.")

        with open(filename, "w") as output_stream:
            output_stream.write("A\n")
            output_stream.write(str(DenseMatrix(self._mat_a_t).mat.T))
            output_stream.write("\nB\n")

            if self._has_intercept:
                output_stream.write(str(DenseMatrix(self._mat_b_t[:-1,]).mat.T))
                output_stream.write("\nIntercept\n")
                output_stream.write(str(DenseMatrix(self._mat_b_t[-1,]).mat.T))
            else:
                output_stream.write(str(DenseMatrix(self._mat_b_t).mat.T))


    def get_mat_a_t(self):
        return self._mat_a_t
    mat_a_t = property(get_mat_a_t)
    """
    Transpose of matrix A parameter, of type Matrix.
    """

    def get_mat_b_t(self):
        return self._mat_b_t
    mat_b_t = property(get_mat_b_t)
    """
Beispiel #15
0
#load N and SVO spaces
n_space = Space.build(data="./data/in/ex19-n.sm",
                      cols="./data/in/ex19-n.cols",
                      format="sm")

svo_space = Space.build(data="./data/in/ex19-svo.sm",
                        cols="./data/in/ex19-svo.cols",
                        format="sm")

print "\nInput SVO training space:"
print svo_space.id2row
print svo_space.cooccurrence_matrix

#1. train a model to learn VO functions on train data: VO N -> SVO
print "\nStep 1 training"
vo_model = LexicalFunction(learner=LstsqRegressionLearner())
vo_model.train(train_vo_data, n_space, svo_space)

#2. train a model to learn V functions on train data: V N -> VO
# where VO space: function space learned in step 1
print "\nStep 2 training"
vo_space = vo_model.function_space
v_model = LexicalFunction(learner=LstsqRegressionLearner())
v_model.train(train_v_data, n_space, vo_space)

#print the learned model
print "\n3D Verb space"
print v_model.function_space.id2row
print v_model.function_space.cooccurrence_matrix

#3. use the trained models to compose new SVO sentences
    def test_3d(self):
        # setting up
        v_mat = DenseMatrix(np.mat([[0, 0, 1, 1, 2, 2, 3, 3], #hate
                                    [0, 1, 2, 4, 5, 6, 8, 9]])) #love

        vo11_mat = DenseMatrix(np.mat([[0, 11], [22, 33]])) #hate boy
        vo12_mat = DenseMatrix(np.mat([[0, 7], [14, 21]])) #hate man
        vo21_mat = DenseMatrix(np.mat([[6, 34], [61, 94]])) #love boy
        vo22_mat = DenseMatrix(np.mat([[2, 10], [17, 26]])) #love car

        train_vo_data = [("hate_boy", "man", "man_hate_boy"),
                         ("hate_man", "man", "man_hate_man"),
                         ("hate_boy", "boy", "boy_hate_boy"),
                         ("hate_man", "boy", "boy_hate_man"),
                         ("love_car", "boy", "boy_love_car"),
                         ("love_boy", "man", "man_love_boy"),
                         ("love_boy", "boy", "boy_love_boy"),
                         ("love_car", "man", "man_love_car")
        ]

        # if do not find a phrase
        # what to do?
        train_v_data = [("love", "boy", "love_boy"),
                        ("hate", "man", "hate_man"),
                        ("hate", "boy", "hate_boy"),
                        ("love", "car", "love_car")]

        sentences = ["man_hate_boy", "car_hate_boy", "boy_hate_boy",
                     "man_hate_man", "car_hate_man", "boy_hate_man",
                     "man_love_boy", "car_love_boy", "boy_love_boy",
                     "man_love_car", "car_love_car", "boy_love_car"]
        n_mat = DenseMatrix(np.mat([[3, 4], [1, 2], [5, 6]]))

        n_space = Space(n_mat, ["man", "car", "boy"], self.ft)

        s1_mat = (vo11_mat * n_mat.transpose()).transpose()
        s2_mat = (vo12_mat * n_mat.transpose()).transpose()
        s3_mat = (vo21_mat * n_mat.transpose()).transpose()
        s4_mat = (vo22_mat * n_mat.transpose()).transpose()

        s_mat = vo11_mat.nary_vstack([s1_mat, s2_mat, s3_mat, s4_mat])
        s_space = Space(s_mat, sentences, self.ft)

        #test train 2d
        model = LexicalFunction(learner=LstsqRegressionLearner(intercept=False))
        model.train(train_vo_data, n_space, s_space)
        vo_space = model.function_space

        self.assertListEqual(vo_space.id2row, ["hate_boy", "hate_man", "love_boy", "love_car"])
        self.assertTupleEqual(vo_space.element_shape, (2, 2))
        vo11_mat.reshape((1, 4))
        np.testing.assert_array_almost_equal(vo11_mat.mat,
                                             vo_space.cooccurrence_matrix.mat[0])
        vo12_mat.reshape((1, 4))
        np.testing.assert_array_almost_equal(vo12_mat.mat,
                                             vo_space.cooccurrence_matrix.mat[1])
        vo21_mat.reshape((1, 4))
        np.testing.assert_array_almost_equal(vo21_mat.mat,
                                             vo_space.cooccurrence_matrix.mat[2])
        vo22_mat.reshape((1, 4))
        np.testing.assert_array_almost_equal(vo22_mat.mat,
                                             vo_space.cooccurrence_matrix.mat[3])

        # test train 3d
        model2 = LexicalFunction(learner=LstsqRegressionLearner(intercept=False))
        model2.train(train_v_data, n_space, vo_space)
        v_space = model2.function_space
        np.testing.assert_array_almost_equal(v_mat.mat,
                                             v_space.cooccurrence_matrix.mat)
        self.assertListEqual(v_space.id2row, ["hate", "love"])
        self.assertTupleEqual(v_space.element_shape, (2, 2, 2))

        # test compose 3d
        vo_space2 = model2.compose(train_v_data, n_space)
        id2row1 = list(vo_space.id2row)
        id2row2 = list(vo_space2.id2row)
        id2row2.sort()
        self.assertListEqual(id2row1, id2row2)
        row_list = vo_space.id2row
        vo_rows1 = vo_space.get_rows(row_list)
        vo_rows2 = vo_space2.get_rows(row_list)
        np.testing.assert_array_almost_equal(vo_rows1.mat, vo_rows2.mat, 7)
        self.assertTupleEqual(vo_space.element_shape, vo_space2.element_shape)
    def test_simple_3d_intercept(self):
        train_data1 = [("drive_car", "I", "I_drive_car"),
                       ("read_man", "You", "You_read_man"),
                       ("read_man", "I", "I_read_man"),
                       ("drive_car", "You", "You_drive_car"),
                       ("drive_man", "You", "You_drive_man"),
                       ("drive_man", "I", "I_drive_man")
        ]

        train_data2 = [("drive", "car", "drive_car"),
                       ("drive", "man", "drive_man"),
        ]

        n_mat = DenseMatrix(np.mat([[1, 2], [3, 4], [5, 6], [7, 8]]))
        svo_mat = DenseMatrix(np.mat([[1, 2], [3, 4], [1, 2], [3, 4], [3, 4], [1, 2]]))

        n_space = Space(n_mat, ["I", "You", "man", "car"], [])
        svo_space = Space(svo_mat, ["I_drive_car", "You_read_man",
                                    "I_read_man", "You_drive_car",
                                    "You_drive_man", "I_drive_man"], ["f1", "f2"])

        #test first stage train
        model = LexicalFunction(learner=LstsqRegressionLearner(intercept=True))
        model.train(train_data1, n_space, svo_space)
        vo_space = model.function_space

        np.testing.assert_array_almost_equal(vo_space.cooccurrence_matrix.mat,
                                             np.mat([[0.6666, 0.3333, -0.3333,
                                                      0.3333, 0.6666, 0.3333],
                                                     [0.6666, 0.3333, -0.3333,
                                                      0.3333, 0.6666, 0.3333],
                                                     [0.6666, 0.3333, -0.3333,
                                                      0.3333, 0.6666, 0.3333]]),
                                             4)

        self.assertTupleEqual(vo_space.element_shape, (2, 3))
        self.assertListEqual(vo_space.id2row, ["drive_car", "drive_man", "read_man"])
        self.assertListEqual(vo_space.id2column, [])

        #test first stage compose
        comp_space = model.compose([train_data1[0]], n_space)
        np.testing.assert_array_almost_equal(comp_space.cooccurrence_matrix.mat,
                                             np.mat([[1, 2]]), 8)

        self.assertTupleEqual(comp_space.element_shape, (2,))
        self.assertListEqual(comp_space.id2row, ["I_drive_car"])
        self.assertListEqual(comp_space.id2column, ["f1", "f2"])

        #test second stage train
        model = LexicalFunction(learner=LstsqRegressionLearner(intercept=True))
        model.train(train_data2, n_space, vo_space)
        v_space = model.function_space

        np.testing.assert_array_almost_equal(v_space.cooccurrence_matrix.mat,
                                             np.mat([[-0.2222, 0.2222, 0.4444,
                                                      -0.1111, 0.1111, 0.2222,
                                                      0.1111, -0.1111, -0.2222,
                                                      -0.1111, 0.1111, 0.2222,
                                                      -0.2222, 0.2222, 0.4444,
                                                      -0.1111, 0.1111, 0.2222]]),
                                             4)

        self.assertTupleEqual(v_space.element_shape, (2, 3, 3))
        self.assertListEqual(v_space.id2row, ["drive"])
        self.assertListEqual(v_space.id2column, [])

        #test compose1
        comp_space = model.compose([train_data2[0]], n_space)
        np.testing.assert_array_almost_equal(comp_space.cooccurrence_matrix.mat,
                                             np.mat([[0.6666, 0.3333, -0.3333,
                                                      0.3333, 0.6666, 0.3333]]), 4)

        self.assertTupleEqual(comp_space.element_shape, (2, 3))
        self.assertListEqual(comp_space.id2row, ["drive_car"])
        self.assertListEqual(comp_space.id2column, [])


        #test compose2
        model2 = LexicalFunction(function_space=comp_space, intercept=True)
        comp_space2 = model2.compose([train_data1[0]], n_space)
        np.testing.assert_array_almost_equal(comp_space2.cooccurrence_matrix.mat,
                                             np.mat([[1, 2]]), 8)

        self.assertTupleEqual(comp_space2.element_shape, (2,))
        self.assertListEqual(comp_space2.id2row, ["I_drive_car"])
        self.assertListEqual(comp_space2.id2column, [])

        #recursive application, write a wrapper around it!!!
        comp_space2 = model2.compose([("drive_car", "I", "I_drive_car")], n_space)
        np.testing.assert_array_almost_equal(comp_space2.cooccurrence_matrix.mat,
                                             np.mat([[1, 2]]), 8)

        self.assertTupleEqual(comp_space2.element_shape, (2,))
        self.assertListEqual(comp_space2.id2row, ["I_drive_car"])
        self.assertListEqual(comp_space2.id2column, [])
class LexicalFunction(CompositionModel):
    """
    Implements the lexical function compositional model.
    
        :math:`\\vec{p} = U \\vec{v}`
     
    where :math:`\\vec{p}` is the vector of the composed phrase,
    :math:`U` is the matrix representation of the first component (the lexical function)
    and :math:`\\vec{v}` is the vector representation of the second component
          
    """ 
         
    _name = "lexical_function"
    _MIN_SAMPLES = 1

    def __init__(self, **kwargs):
        """
        Constructor.
        
        Args:
            function_space= : function space parameter, containing
            the lexical functions, of type Space. Optional, can be set through
            training.
            
            intercept= : True/False, True if the function space has intercept.
            Optional, default False. When training is used, intercept is set 
            to the intercept value of the regression learner used.
        
            learner= : regression method of type RegressionLearner. Optional,
            default LstsqRegressionLearner.
            
        """
        assert_valid_kwargs(kwargs, ["function_space", "intercept", "learner"])
        
        self._regression_learner = LstsqRegressionLearner()
        self.composed_id2column = []
        self._function_space = None
        self._has_intercept = False
        
        if "function_space" in kwargs:
            space = kwargs["function_space"]
            if not isinstance(space, Space):
                raise TypeError("expected Space-type argument, received:" 
                                 % type(space))
            self._function_space = kwargs["function_space"]

        if "intercept" in kwargs:
            has_intercept = kwargs["intercept"]
            if not isinstance(has_intercept, bool):
                raise TypeError("expected bool-type argument, received:" 
                                 % type(has_intercept))
            self._has_intercept = has_intercept
            
        if "learner" in kwargs:
            if "function_space" in kwargs:
                raise ValueError("cannot instantiate with both learner and function_space!")
             
            self._regression_learner = kwargs["learner"] 
        
    def train(self, train_data, arg_space, phrase_space):
        """
        Trains a lexical function composition model to learn a function
        space and sets the function_space parameter. 
                
        Args:
            train_data: list of string tuples. Each tuple contains 3 
            string elements: (function_word, arg, phrase).
            
            arg_space: argument space, of type Space. arg elements of 
            train data are interpreted in this space.
        
            phrase space: phrase space, of type Space. phrase elements of 
            the train data are interpreted in this space.
            
        Training tuples which contain strings not found in their 
        respective spaces are ignored. Function words containing less than
        _MIN_SAMPLES training instances are ignored. For example, if
        _MIN_SAMPLES=2 and function word "red" occurs in only one phrase, "red"
        is ignored.
        
        The id2column attribute of the resulted composed space is set to
        be equal to that of the phrase space given as an input.
        """
        
        start = time.time()

        self._has_intercept = self._regression_learner.has_intercept()

        if not isinstance(arg_space, Space):
            raise ValueError("expected one input spaces!")  
               
        result_mats = []
               
        train_data = sorted(train_data, key=lambda tup: tup[0])
        function_word_list, arg_list, phrase_list = self.valid_data_to_lists(train_data,
                                                                             (None,
                                                                              arg_space.row2id,
                                                                              phrase_space.row2id))
        #partitions the sorted input data
        keys, key_ranges = get_partitions(function_word_list, self._MIN_SAMPLES)
        
        if not keys:
            raise ValueError("No valid training data found!")
                
        assert(len(arg_space.element_shape) == 1)
        
        if self._has_intercept:
            new_element_shape = phrase_space.element_shape + (arg_space.element_shape[0] + 1,)
        else:
            new_element_shape = phrase_space.element_shape + (arg_space.element_shape[0],)
            
        for i in xrange(len(key_ranges)):
            
            idx_beg, idx_end = key_ranges[i]
            
            print ("Training lexical function...%s with %d samples" 
                     % (keys[i], idx_end - idx_beg))
                            
            arg_mat = arg_space.get_rows(arg_list[idx_beg:idx_end]) 
            phrase_mat = phrase_space.get_rows(phrase_list[idx_beg:idx_end])
 
            #convert them to the same type
            matrix_type = get_type_of_largest([arg_mat, phrase_mat])
            [arg_mat, phrase_mat] = resolve_type_conflict([arg_mat, phrase_mat],
                                                          matrix_type)

            result_mat = self._regression_learner.train(arg_mat, phrase_mat).transpose()
            
            result_mat.reshape((1, np.prod(new_element_shape)))
            
            result_mats.append(result_mat)

        new_space_mat = arg_mat.nary_vstack(result_mats)
        
        self.composed_id2column = phrase_space.id2column
            
        self._function_space = Space(new_space_mat, keys, [], 
                                     element_shape=new_element_shape)
        
        log.print_composition_model_info(logger, self, 1, "\nTrained composition model:")
        log.print_info(logger, 3, "Trained: %s lexical functions" % len(keys))
        log.print_info(logger, 3, "With total data points:%s" % len(function_word_list))
        log.print_matrix_info(logger, arg_space.cooccurrence_matrix, 3, 
                              "Semantic space of arguments:")
        log.print_info(logger, 3, "Shape of lexical functions learned:%s" 
                       % (new_element_shape,))
        log.print_matrix_info(logger, new_space_mat, 3, 
                              "Semantic space of lexical functions:")
        log.print_time_info(logger, time.time(), start, 2)
        
    def compose(self, data, arg_space):
        """
        Uses a lexical function composition model to compose elements.
        
        Args:
            data: data to be composed. List of tuples, each containing 3
            strings: (function_word, arg, composed_phrase). function_word and 
            arg are the elements to be composed and composed_phrase is the 
            string associated to their composition. function_word elements
            are interpreted in self.function_space. 
            
            arg_space: argument space, of type Space. arg elements of data are 
            interpreted in this space. 
        
        Returns:
            composed space: a new object of type Space, containing the 
            phrases obtained through composition.
            
        """
        start = time.time()
        
        assert_is_instance(arg_space, Space)
        arg1_list, arg2_list, phrase_list = self.valid_data_to_lists(data,
                                                                     (self._function_space.row2id,
                                                                      arg_space.row2id,
                                                                      None))

        composed_vec_list = []
        for i in xrange(len(arg1_list)):
            arg1_vec = self._function_space.get_row(arg1_list[i])
            arg2_vec = arg_space.get_row(arg2_list[i])
        
            matrix_type = get_type_of_largest([arg1_vec, arg2_vec])
            [arg1_vec, arg2_vec] = resolve_type_conflict([arg1_vec, arg2_vec],
                                                              matrix_type)
                
            composed_ph_vec = self._compose(arg1_vec, arg2_vec,
                                            self._function_space.element_shape)

            composed_vec_list.append(composed_ph_vec)
        
        result_element_shape = self._function_space.element_shape[0:-1]
        composed_ph_mat = composed_ph_vec.nary_vstack(composed_vec_list)
        
        log.print_name(logger, self, 1, "\nComposed with composition model:")
        log.print_info(logger, 3, "Composed total data points:%s" % len(arg1_list))
        log.print_info(logger, 3, "Functional shape of the resulted (composed) elements:%s" 
                       % (result_element_shape,))
        log.print_matrix_info(logger, composed_ph_mat, 4, 
                              "Resulted (composed) semantic space:")
        log.print_time_info(logger, time.time(), start, 2)
        
        return Space(composed_ph_mat, phrase_list, self.composed_id2column, 
                     element_shape = result_element_shape)
    
        
    def _compose(self, function_arg_vec, arg_vec, function_arg_element_shape):

        new_shape = (np.prod(function_arg_element_shape[0:-1]), 
                            function_arg_element_shape[-1])

        function_arg_vec.reshape(new_shape)

        if self._has_intercept:
            comp_el = function_arg_vec * padd_matrix(arg_vec.transpose(), 0)
        else:
            comp_el = function_arg_vec * arg_vec.transpose()
            
        return comp_el.transpose()
            
    @classmethod
    def _assert_space_match(cls, arg1_space, arg2_space, phrase_space=None):
        pass
 
    def set_regression_learner(self, regression_learner):
        assert_is_instance(regression_learner, RegressionLearner)
        self._regression_learner = regression_learner
        
    def get_regression_learner(self):
        return self._regression_learner
    
    regression_learner = property(get_regression_learner, set_regression_learner)  
    """
    Regression method to be used in training, of type RegressionLearner.
    Default is RidgeRegressionLearner(param=1).
    """
       
    def get_function_space(self):
        return self._function_space
    
    function_space = property(get_function_space)
    """
    Function space parameter, containing the lexical functions, of type Space. 
    Can be set through training or through initialization, default None.
    """        

    def get_has_intercept(self):
        return self._has_intercept
    
    has_intercept = property(get_has_intercept)
    """
    Has intercept parameter, boolean. If True, then the function_space is 
    assumed to contain intercept. Can be set through training or through 
    initialization, default is assumed to be False.
    """   
    
    def set_min_samples(self, min_samples):
        if not is_integer(min_samples):
            raise ValueError("expected %s min_samples value, received %s"
                             % ("integer", type(min_samples)))
        self._MIN_SAMPLES = min_samples
        
    def get_min_samples(self):
        return self._MIN_SAMPLES
    
    MIN_SAMPLES = property(get_min_samples, set_min_samples)
    """
    Minimal number of samples for each training instance. Default 3.
    """
            
    def _export(self, filename):
        if self._function_space is None:
            raise IllegalStateError("cannot export an untrained LexicalFunction model.")
        self._function_space.export(filename, format="dm")