//  [python]
def __init__(self, data, variables, likelihoods,
                 latent_network,
                 directional_data=None,
                 options=GPOptions()):
        super().__init__(options=options)

        self.data = data
        self.latent_network = self._register(latent_network)

        if not (isinstance(likelihoods, (list, tuple))):
            likelihoods = [likelihoods]
        self.likelihoods = likelihoods
        self.lik_sizes = [lik.size for lik in likelihoods]
        for likelihood in likelihoods:
            self._register(likelihood)

        if not (isinstance(variables, (list, tuple))):
            variables = [variables]
        self.variables = variables
        self.var_lengths = [data.variables[v].length for v in variables]

        y, has_value = [], []
        for v in self.variables:
            y_v, h_v = data.variables[v].get_measurements()
            y.append(y_v)
            has_value.append(h_v)
        self.y = _np.concatenate(y, axis=1)
        self.has_value = _np.concatenate(has_value, axis=1)
        self.total_data = _np.sum(self.has_value)

        # initializing likelihoods
        for i, v in enumerate(self.variables):
            y, has_value = data.variables[v].get_measurements()
            has_value = _np.all(has_value == 1.0, axis=1)
            y = y[has_value, :]
            self.likelihoods[i].initialize(y)

        # directions
        self.directional_likelihood = _lk.GradientIndicator()
        # self.directional_likelihood = _lk.Gaussian()
        # self.directional_likelihood.parameters["noise"].set_value(1e-6)
        self.directional_data = directional_data
        self.total_data_dir = 0
        self.y_dir = None
        self.has_value_dir = None
        self.var_lengths_dir = None

        if directional_data is not None:
            if self.data.n_dim != directional_data.n_dim:
                raise ValueError("the directional data must have the"
                                 "same number of dimensions as the"
                                 "point data")

            self.var_lengths_dir = [1] * sum(self.var_lengths)

            y_dir, has_value_dir = [], []
            for v, s in zip(variables, self.var_lengths):
                if v in directional_data.variables.keys():
                    y_v, h_v = directional_data.variables[v].get_measurements()
                    y_dir.append(_np.tile(y_v, [1, s]))
                    has_value_dir.append(_np.tile(h_v, [1, s]))
                else:
                    y_dir.append(_np.zeros([directional_data.n_data, s]))
                    has_value_dir.append(_np.ones([directional_data.n_data, s]))
            y_dir = _np.concatenate(y_dir, axis=1)
            has_value_dir = _np.concatenate(has_value_dir, axis=1)

            self.y_dir = y_dir.copy()
            self.has_value_dir = has_value_dir.copy()
            self.total_data_dir = _np.sum(self.has_value_dir)

        # optimizer
        self.training_log = []
        self.optimizer = _tf.keras.optimizers.Adam(
            _tf.keras.optimizers.schedules.ExponentialDecay(1e-2, 1, 0.999),
            amsgrad=True
        )

        # intermediate tensors
        self.elbo = _tf.Variable(_tf.constant(0.0, _tf.float64))
        self.kl_div = _tf.Variable(_tf.constant(0.0, _tf.float64))