def test_020_matmul_instantiation():
    """
    Objective:
        Verify the initialized layer instance provides its properties.
    Expected:
        * name, num_nodes, M, log_level are the same as initialized.
        * X, T, dX, objective returns what is set.
        * N, M property are provided after X is set.
        * Y, dY properties are provided after they are set.
    """
    def objective(X: np.ndarray) -> Union[float, np.ndarray]:
        """Dummy objective function"""
        return np.sum(X)

    for _ in range(NUM_MAX_TEST_TIMES):
        N: int = np.random.randint(1, NUM_MAX_BATCH_SIZE)
        M: int = np.random.randint(1, NUM_MAX_NODES)
        D: int = np.random.randint(1, NUM_MAX_FEATURES)
        name = "test_020_matmul_instantiation"
        matmul = Matmul(name=name,
                        num_nodes=M,
                        W=weights.he(M, D + 1),
                        log_level=logging.DEBUG)
        matmul.objective = objective

        assert matmul.name == name
        assert matmul.num_nodes == matmul.M == M

        matmul._D = D
        assert matmul.D == D

        X = np.random.randn(N, D).astype(TYPE_FLOAT)
        matmul.X = X
        assert np.array_equal(matmul.X, X)
        assert matmul.N == N == X.shape[0]

        matmul._dX = X
        assert np.array_equal(matmul.dX, X)

        T = np.random.randint(0, M, N).astype(TYPE_LABEL)
        matmul.T = T
        assert np.array_equal(matmul.T, T)

        matmul._Y = np.dot(X, X.T)
        assert np.array_equal(matmul.Y, np.dot(X, X.T))

        matmul._dY = np.array(0.9)
        assert matmul._dY == np.array(0.9)

        matmul.logger.debug("This is a pytest")

        assert matmul.objective == objective
def test_020_matmul_instance_properties():
    """
    Objective:
        Verify the layer class validates the parameters have been initialized before accessed.
    Expected:
        Initialization detects the access to the non-initialized parameters and fails.
    """
    msg = "Accessing uninitialized property of the layer must fail."

    for _ in range(NUM_MAX_TEST_TIMES):
        name = random_string(np.random.randint(1, 10))
        M: int = np.random.randint(1, NUM_MAX_NODES)
        D: int = np.random.randint(1, NUM_MAX_FEATURES)
        matmul = Matmul(name=name,
                        num_nodes=M,
                        W=weights.uniform(M, D + 1),
                        log_level=logging.DEBUG)

        # --------------------------------------------------------------------------------
        # To pass
        # --------------------------------------------------------------------------------
        try:
            if not matmul.name == name:
                raise RuntimeError("matmul.name == name should be true")
        except AssertionError as e:
            raise RuntimeError(
                "Access to name should be allowed as already initialized."
            ) from e

        try:
            if not matmul.M == M:
                raise RuntimeError("matmul.M == M should be true")
        except AssertionError as e:
            raise RuntimeError(
                "Access to M should be allowed as already initialized.") from e

        try:
            if not isinstance(matmul.logger, logging.Logger):
                raise RuntimeError(
                    "isinstance(matmul.logger, logging.Logger) should be true")
        except AssertionError as e:
            raise RuntimeError(
                "Access to logger should be allowed as already initialized."
            ) from e

        try:
            a = matmul.D
        except AssertionError:
            raise RuntimeError(
                "Access to D should be allowed as already initialized.")

        try:
            matmul.W is not None
        except AssertionError:
            raise RuntimeError(
                "Access to W should be allowed as already initialized.")

        try:
            matmul.optimizer is not None
        except AssertionError:
            raise RuntimeError(
                "Access to optimizer should be allowed as already initialized."
            )

        # --------------------------------------------------------------------------------
        # To fail
        # --------------------------------------------------------------------------------
        try:
            print(matmul.X)
            raise RuntimeError(msg)
        except AssertionError:
            pass

        try:
            matmul.X = int(1)
            raise RuntimeError(msg)
        except AssertionError:
            pass

        try:
            print(matmul.dX)
            raise RuntimeError(msg)
        except AssertionError:
            pass

        try:
            print(matmul.dW)
            raise RuntimeError(msg)
        except AssertionError:
            pass

        try:
            print(matmul.Y)
            raise RuntimeError(msg)
        except AssertionError:
            pass
        try:
            matmul._Y = int(1)
            print(matmul.Y)
            raise RuntimeError(msg)
        except AssertionError:
            pass

        try:
            print(matmul.dY)
            raise RuntimeError(msg)
        except AssertionError:
            pass
        try:
            matmul._dY = int(1)
            print(matmul.dY)
            raise RuntimeError(msg)
        except AssertionError:
            pass

        try:
            print(matmul.T)
            raise RuntimeError(msg)
        except AssertionError:
            pass

        try:
            matmul.T = float(1)
            raise RuntimeError(msg)
        except AssertionError:
            pass

        try:
            # pylint: disable=not-callable
            matmul.objective(np.array(1.0, dtype=TYPE_FLOAT))
            raise RuntimeError(msg)
        except AssertionError:
            pass

        try:
            print(matmul.N)
            raise RuntimeError(msg)
        except AssertionError:
            pass

        assert matmul.name == name
        assert matmul.num_nodes == M

        try:
            matmul = Matmul(name=name,
                            num_nodes=M,
                            W=weights.xavier(M, D + 1),
                            log_level=logging.DEBUG)
            matmul.function(int(1))
            raise RuntimeError("Invoke matmul.function(int(1)) must fail.")
        except AssertionError:
            pass

        try:
            matmul = Matmul(name=name,
                            num_nodes=M,
                            W=weights.xavier(M, D + 1),
                            log_level=logging.DEBUG)
            matmul.gradient(int(1))
            raise RuntimeError("Invoke matmul.gradient(int(1)) must fail.")
        except AssertionError:
            pass