# \<Graph> Object Doc.

{% hint style="info" %}
Certain methods from the Graph class are delegated to the NodeSet, enabling them to also be leveraged by these smaller data structures distributed throughout the system. Thereby, it is imperative to conduct a review of these methods to provide a comprehensive exposition of the Graph class's functionality.
{% endhint %}

## 0. Preambule

The data is persistently stored in TXT files following a structure resembling the example provided. Each line is named an "entry".

```
es-v:Rastrillar()|F||||||||es-n:Jardín()/es-n:Hojarasca()/es-n:Rastrillo()||
es-n:Guerreros de terracota()|F|||||es-j:Luchador()/es-n:Figura()/es-n:Gólem()/es-n:Estatua()|||es-n:Frío()/es-j:Sepultado bajo tierra()/es-n:Templo()/es-j:Resistente()/es-n:Mausoleo()/es-n:Emperador()/es-j:Medio sepultado bajo tierra()/es-j:Duro()/es-v:Cocer()/es-n:Historia()/es-n:Arma()/es-n:Ejército()/es-n:Cuerpo humano()/es-n:Arcilla()/es-n:Terracota()/es-n:Fango()/es-j:Antiguo()/es-n:Despertar()/es-j:Subterráneo()/es-j:Inmemorial()/es-j:Histórico()/es-n:Malla()/es-n:Armadura()/es-v:Endurecer()/es-n:Barro()/es-n:Foso()/es-j:Inmóvil()/es-n:Viejo()/es-j:Inamovible()/es-j:Durmiente()/es-j:Soterrado()/es-n:Palacio()/es-n:Militar()/es-n:Fisonomía()/es-n:Patrimonio de la Humanidad()/es-v:Moldear()||
es-n:Metacualona()|F|||||es-n:Droga()/es-n:Medicina()/es-n:Sedante()|||es-v:Ir puesto de()/es-n:Insomnio()/es-n:Tratamiento()/es-n:Medicamento()/es-n:Relajación()/es-n:Sedante()/es-n:Analgésico()||
es-n:Apoyo emocional()|F||||||||es-n:Perro de terapia()/es-n:Perro lazarillo()||
es-n:Embolia isquémica()|F|||||es-n:Derrame cerebral()/es-n:Trombosis()/es-n:Accidente cerebrovascular()/es-n:Ictus()/es-n:Infarto cerebral()|||es-n:Riesgo()/es-n:Consulta médica()/es-n:Hospitalización()/es-n:Pronóstico()/es-n:Enfermedad()/es-n:Parálisis()/es-n:Discapacidad()/es-n:Daño cerebral()/es-n:Peligro()||
es-n:Libertad bajo fianza()|F|||||es-n:Libertad condicional()|||es-n:Cárcel clandestina()/es-n:Prueba()/es-n:Condición()/es-v:Controlar()/es-v:Tomar las huellas()/es-n:Presidiario()/es-n:Chirona()/es-n:Vigilancia()/es-n:Control()/es-v:Vigilar()/es-n:Cárcel clandestina en Marruecos()/es-n:Campo de prisioneros()/es-n:Penitenciaría()/es-v:Probar()/es-n:Penal de seguridad()/es-n:Cárcel()/es-j:Vigilado()/es-n:Mundo exterior()/es-j:Condicional()||
...
```

The first section contains properties intrinsic to the entry itself, followed by a sequence of sections were both connections and additional attributes can be found, lists separated by slashes.

## 1. Workflow

### 1.1. Initialization

The initialization of a `Graph` object is a slightly sophisticated process. This section provides an in-depth exploration of the initialization pipeline and the functions invoked during the creation of a Graph object.

<figure><img src="https://1542499538-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FuNHwKjGCzbFSBHtH21WB%2Fuploads%2FIAZM5bDJkZEKlsGiMYq1%2Fasdf.png?alt=media&#x26;token=3ec484ac-0443-436d-8bb5-781686ab7d74" alt=""><figcaption><p>Scheme of the methods called during the Graph Object Initialization [deprecated].</p></figcaption></figure>

The `Graph` class initializes with an optional filename, which, if provided, must end with '.txt'. In the `__init__` method, the filename is stored, and the `_build_nodes` method is called with this filename. The `_build_nodes` method checks if the filename is valid. If it is, it proceeds to call `_load_data` to load and process data from the file.

The `_load_data` method consists of two nested functions: `_parse_line_to_node` and `_update_node_relationships`. `_parse_line_to_node` takes a line from the file and parses it into a `Node` object by splitting the line into its component parts and assigning them to the appropriate Node attributes. After parsing all lines into nodes, `_update_node_relationships` is called. This function updates the relational attributes of each node to reference other nodes in the graph rather than just their string identifiers.

Finally, `_set_parent_at_nodes()`sets up internal Node references towards the graph itself.

```python
from skcomponents import Graph
G0 = Graph('data.txt')  # creates a Graph from a TXT file.
```

### 1.2. Saving

The `save` method is designed for persisting a Graph structure to a file. It takes three parameters: `custom_filename`, `integrity_check`, and `directory_path`.

* The `custom_filename` is a string specifying the name of the file where the Graph will be saved.
* The `integrity_check` is a boolean flag (default is `False`); when set to `True`, the method performs a validation check on the Graph's connections before saving.
* The `directory_path` is an optional parameter specifying the directory path where the file will be saved; if not provided, the file is saved in the current working directory. The `save` method ensures that the Graph's data is correctly structured and free of inconsistencies before it is saved.

```
G.save('data.txt', directory_path=None, integrity_check=True)
```

When `integrity_check` is enabled, the `save` method performs these two validation checks. If either _spurious_ or _non-mutual connections_ are found, the method raises a `ValueError`, describing the specific type of integrity issue detected.

* **Spurious connections** : refers to a situation where a node in the Graph references a neighboring node that does not exist within the Graph's data structure. This type of connection is indicative of data integrity issues, such as incomplete or corrupt data.
* **Non-mutual connections** : occur when the relationship between two nodes in the Graph is not reciprocated. For example, if node A references node B in one of its attributes, but node B does not reference node A in the corresponding opposed attribute, this is considered a non-mutual connection. This kind of connection can indicate an inconsistency or error in the Graph's structure.

This error prevents the Graph from being saved, thereby ensuring that no data structures with specific integrity issues are persisted.&#x20;

## 2. Node Management

### 2.1. \<Node> Creation/Deletion

In the context of managing a graph structure, two fundamental operations are creating and deleting nodes. The `create_node` function is responsible for adding new nodes to the graph. It requires parameters like `lang`, `type`, `name`, and `lemma` to define the characteristics of the node. The function first checks whether a node with these specified attributes already exists. If it doesn't, it proceeds to create a new instance of the Node class with the given attributes and appends it to the graph.

```python
G.create_node(lang, node_type, name, lemma)
```

On the other hand, the `delete_node` function is used for removing nodes from the graph. The key parameter for this function is the `target_node`, which identifies the node to be removed. Upon execution, this function not only removes the `target_node` from the graph but also meticulously iterates over all other nodes in the graph. During this iteration, it ensures that any references to the `target_node` in their relational attributes (like `synset0`, `synset1`, `synset2`, `semset0`, `semset1`, `semset2`) are removed. This thorough cleanup prevents any residual or dangling references after a node is deleted, maintaining the integrity of the graph structure.

```python
G.delete_node(G.find(name="Poza"))
```

### 2.2. \<Node> Binding/Unbinding

The dynamics of a graph are not just limited to adding or removing nodes; they also involve how nodes are interconnected, which is where the `bind` and `unbind` functions play a crucial role. The `bind` function establishes a link between two nodes. It requires parameters such as the `target_node` (the primary node in the relationship), the `append_node` (the node to be connected), and the `target_edge_type` kind of fielding that can be either <mark style="color:green;">single-long</mark> or <mark style="color:green;">single-short</mark> format (defining the nature of the relationship). This function is intelligent in its operation; it not only connects the `target_node` with the `append_node` but also ensures that the relationship is symmetric. For example, if `node A` is linked to `node B` with a `synset0` relationship, `node B` will automatically be linked back to `node A` with a `synset2` relationship, maintaining the graph's bidirectional integrity.

Conversely, the `unbind` function is used to dissolve the connection between two nodes. Similar to `bind`, it requires the nodes involved and the type of edge to be removed. The `unbind` function calls upon an internal method, `_update_reciprocal_edges`, which handles the nuances of removing connections. It ensures that when a link is removed, the symmetry of the relationships in the graph is preserved, meaning that if a connection from `node A` to `node B` is severed, the corresponding connection from `node B` to `node A` is also removed.

```python
G.bind(node_a, node_b, 'synset0')
# <node_b> gets added to "node_a.synset0"
# -> <node_a> gets added to "node_b.synset2"

G.unbind(node_c, node_d, 'e2')
# <node_d> gets removed from "node_c.semset2"
# -> <node_c> gets removed from "node_d.semset2"
```

### 2.2. \<Node> Merging

Merging nodes in a graph entails redirecting all connections from one node (`node_b`) to another (`node_a`) and then removing `node_b` to consolidate their relationships into a single node. This is achieved through the `bind` and `unbind` functions, which carefully transfer `node_b`'s connections to `node_a`, ensuring the graph's structural and relational integrity is preserved. This process simplifies the graph, eliminates redundancy, and maintains the semantic network between the nodes, effectively merging the identities and relationships of `node_b` into `node_a`.

```python
G.merge_nodes(node_a, node_b)
G.find(name=node_b.name) # None
```
