---
title: "Working with ImplicationSets"
output: rmarkdown::html_vignette
vignette: >
  %\VignetteIndexEntry{Working with ImplicationSets}
  %\VignetteEngine{knitr::rmarkdown}
  %\VignetteEncoding{UTF-8}
---

```{r, include = FALSE}
knitr::opts_chunk$set(
  collapse = TRUE,
  comment = "#>",
  fig.width = 7,
  fig.height = 5,
  warning = FALSE
)
```

# Introduction

In this vignette, we present the main functionalities and data structures of the `fcaR` package when working with implications in FCA.

We load the `fcaR` package by:
```{r setup}
library(fcaR)
```

# Datasets

We are going to work with two datasets, a crisp one and a fuzzy one.

The classical (binary) dataset is the well-known `planets` formal context, presented in 

> Wille R (1982). “Restructuring Lattice Theory: An Approach Based on Hierarchies of Concepts.” In Ordered Sets, pp. 445–470. Springer.

```{r}
knitr::kable(planets, format = "html", booktabs = TRUE)
```

The other formal context is fuzzy and is defined by the following matrix I:
```{r echo = FALSE}
objects <- paste0("O", 1:6)
n_objects <- length(objects)

attributes <- paste0("P", 1:6)
n_attributes <- length(attributes)

I <- matrix(
  data = c(
    0, 1, 0.5, 0, 0, 0.5,
    0, 1, 0.5, 0, 0, 0.5,
    0.5, 1, 0, 0, 1, 0,
    0.5, 0, 0, 1, 0.5, 0,
    1, 0, 0, 0.5, 0, 0,
    0, 0, 1, 0, 0, 1
  ),
  nrow = n_objects,
  byrow = FALSE
)

colnames(I) <- attributes
rownames(I) <- objects
```

```{r}
knitr::kable(I, format = "html", booktabs = TRUE)
```

# Working with ImplicationSets

Although `ImplicationSet` objects can be created _ad hoc_, the usual way to get implications is by the application of the NextClosure algorithm to a `FormalContext` object.

Thus, let us create different formal contexts with the previous datasets:
```{r}
fc_planets <- FormalContext$new(planets)
fc_I <- FormalContext$new(I)
```


## Extraction of the canonical basis of implications

The function `find_implications()` use the NextClosure algorithm in a formal context to extract the canonical basis of implications:
```{r}
fc_planets$find_implications()
fc_I$find_implications()
```

We can inspect the implications by doing:
```{r}
fc_planets$implications
fc_I$implications
```

Internally, an `ImplicationSet` consists of two matrices (one for the left-hand sides and the other for the right-hand sides of the rules). We can get these (sparse) matrices as:
```{r}
fc_planets$implications$get_LHS_matrix()
fc_planets$implications$get_RHS_matrix()
```

The main practical use of an `ImplicationSet` is to compute the closure of a set of attributes, by using the `closure()` function:
```{r}
# Let us build a set of attributes
S <- Set$new(attributes = fc_planets$attributes)
S$assign(large = 1, far = 1)
S

fc_planets$implications$closure(S)$closure
```

## Validity of implications
We can check if an `ImplicationSet` holds in a `FormalContext` by using the `%holds_in%` operator:
```{r}
# Let us clone the implication basis
imps <- fc_planets$implications$clone()
imps %holds_in% fc_planets
```

Each component of this vector represents whether the corresponding implication holds in the formal context. In this case, as the `ImplicationSet` is the Duquenne-Guigues basis for the `FormalContext`, all implications hold.

Conversely, we can check if a list of attribute sets (or a formal context) respects an `ImplicationSet`, via the `%respects%` operator:
```{r}
fc_planets %respects% imps
```

The result is a matrix where each row correspond to a attribute set and each column to an implication. An element is `TRUE` if the corresponding set respects the corresponding implication. If the first argument is a `FormalContext`, the function will consider the set of attributes of each object.

## Cardinality, size and support of the implication set

Some quantities can be computed for an `ImplicationSet`:

* Cardinality: the number of implications in the set
```{r}
fc_planets$implications$cardinality()
```
* Size: The number of attributes in the LHS and the RHS of each implication
```{r}
sizes <- fc_planets$implications$size()
# Total number of attributes in the LHS and the RHS
colSums(sizes)
```
* Support: The proportion of objects in the formal context whose attributes contain the LHS of a particular rule
```{r}
fc_planets$implications$support()
```

## Export to LaTeX

A nice feature is the ability to export an `ImplicationSet` to LaTeX format:
```{r}
fc_planets$implications$to_latex()
```

## Filtering of implications

Sometimes it is needed to work with a subset of the implications, using only the implications that fulfill certain conditions:
```{r}
# Implications with P1 and P2 in the LHS and P5 in the RHS
fc_I$implications$filter(
  lhs = c("P1", "P2"),
  rhs = "P5"
)
```

## Simplification Logic

In this package, we have implemented logic tools to operate on the implications.

First, some simplification rules have been developed, named _reduction_, _composition_, _generalization_ and _simplification_, that can be applied using the `apply_rules()` function:
```{r}
fc_I$implications$apply_rules(rules = c(
  "composition",
  "simplification"
))
```
This enables the reduction of the cardinality and/or the size of the `ImplicationSet`.

In addition, the "simplification" rule to remove redundancies can be used in the computation of the closure of a set, to provide a reduced set of implications that is inferred from the set of attributes:
```{r}
# Let us build a set of attributes
S <- Set$new(attributes = fc_planets$attributes)
S$assign(large = 1, far = 1)
S

fc_planets$implications$closure(S, reduce = TRUE)
```

## Entailment and equivalence of implications

We can check if a given `ImplicationSet` follows from another one:
```{r}
# imps is the basis
imps <- fc_planets$implications$clone()
imps2 <- imps$clone()
# imps2 is an equivalent set of implications
# where we have removed redundancies
imps2$apply_rules(c("simp", "rsimp"))
# Any implication in imps2 follows from imps
imps %entails% imps2
# And viceversa
imps2 %entails% imps
```

We can also check if the two sets of implications are equivalent:
```{r}
imps %~% imps2
# If we remove any implication from imps2,
# they will not be equivalent
imps %~% imps2[1:9]
```

## Recommendations

One of the final applications of an `ImplicationSet` is the easy development of a recommendation system where, from an attribute set, the system would infer the value to other attribute. This is done by the `recommend()` function, which internally computes the closure of the attribute set:
```{r}
S <- Set$new(attributes = fc_I$attributes)
S$assign(P1 = 1, P4 = 0.5)

fc_I$implications$recommend(S, attribute_filter = c("P3", "P5"))
```


