So i am implementing a RSs model in pytorch that works with embedding that represent values of truth in NEURO SYMBOLIC The conjunction (AND) and disjunction (OR) are made as follows, but actually they work only with input values in (0, 1).
The selector ‘u’ is a tensor used to determine which values from the ‘selected’ array should be considered in an AND operation.


For example, if selector of dimension (1, 4) is equal to [[1, 0, 0, 1]], it means only the first and last elements of the "selected" array will be included in the operation.
Given selected = [[1, 1, 1, 0]], applying the selector results in: 1 AND 0 = 0.

Other examples:
users = torch.tensor([[1, 0, 0, 1], [0, 0, 1, 0], [1, 1, 1, 1]])
items = torch.tensor([[0, 1, 1, 0.2], [0, 0, 0, 1], [0.9, 1, 1, 1]])
res_ands = conjunction_rule(u=users, i=items)

Give the results:
res_ands = tensor([0.0106, 0.0107, 0.9047]).


The functions are the following, and they are made to be differentiable and to avoid the vanishing gradient problem: def conjunction_rule(u: torch.Tensor, i: torch.Tensor, epsilon: float = 1e-40) -> torch.Tensor:
    """
    Compute the conjunction (AND) function.

    Args:
        u (torch.Tensor): Tensor of shape [batch_size, embed_k], representing user embeddings.
        i (torch.Tensor): Tensor of shape [batch_size, embed_k], representing item embeddings.
        epsilon (float, optional): Small constant to prevent log(0). Defaults to 1e-12.

    Returns:
        torch.Tensor: Tensor of shape [batch_size], containing the conjunction scores.
    """
    # Ensure u and i are in the expected range [0, 1]
    # u = torch.clamp(u, 0.0, 1.0)
    # i = torch.clamp(i, 0.0, 1.0)

    # Compute the element-wise terms
    expr = 1 - u * (1 - i) + epsilon  # epsilon prevents log(0)

    # Take the logarithm of each term
    log_expr = torch.log(expr)

    # Sum over the embedding dimensions
    sum_log_expr = torch.sum(log_expr, dim=1)

    # Compute the final AND function value
    res = -1.0 / (-1.0 + sum_log_expr)
    return res


def disjunction_rule(and_rules: torch.Tensor, epsilon: float = 1e-40) -> torch.Tensor:
    """
    Compute the disjunction (OR) function.

    Args:
        and_rules (torch.Tensor): Tensor of shape [batch_size, n_rules], containing conjunction scores.
        epsilon (float, optional): Small constant to prevent log(0). Defaults to 1e-12.

    Returns:
        torch.Tensor: Tensor of shape [batch_size], containing the disjunction scores.
    """
    # Ensure and_rules are in the expected range [0, 1]
    # and_rules = torch.clamp(and_rules, 0.0, 1.0)

    # Compute the element-wise terms
    expr = 1 - and_rules + epsilon  # epsilon prevents log(0)

    # Take the logarithm of each term
    log_expr = torch.log(expr)

    # Sum over the rules
    sum_log_expr = torch.sum(log_expr, dim=1)

    # Compute the final OR function value
    res = 1 - (-1.0 / (-1.0 + sum_log_expr))
    return res


The problem is that I want to extend the range of acceptable input  values  for those functions to (-1, 1),
Such that  those functions can works with negative values interpreting them as  a NOT. 
i.e. if the selector (u) has value -1 it means to select the corresponding value in the selected and negate it.

i.e.
u = [ -1, 0, 0, 1]
i1 = [1, 0, 1, 1]
i2 = [-1, 0, 1, 1]
i3 = [1, 1, 1, 1]

conjunction_rule(u=u1, i=i1) must be equal to something near 0, as it would be the negated first element of i1 AND the last element of i1, i.e.,  not(1) and (1) = 0 and 1 = 0.
conjunction_rule(u=u1, i=i2) = not(not(1))AND 1 = 1
conjunction_rule(u=u1, i=i3) = not(1) AND (1) = 0