# Understanding the --add-eweights Flag in doc2graph: Enabling Geometric Edge Features and Weights

> Unlock geometric edge features and weights with the --add-eweights flag in doc2graph. Enhance GNNs by capturing spatial relationships with polar coordinates. Learn more!

- Repository: [Andrea Gemelli/doc2graph](https://github.com/andreagemelli/doc2graph)
- Tags: deep-dive
- Published: 2026-02-24

---

**The `--add-eweights` flag enables geometric edge features and distance-based edge weights in doc2graph, computing polar coordinates between document entities and discretizing them into binned vectors that capture spatial relationships for graph neural networks.**

The `--add-eweights` flag controls whether doc2graph includes geometric relationship features when converting documents into graph representations. When enabled, the preprocessing pipeline calculates spatial distances and angles between entity bounding boxes, creating edge feature tensors and learnable weights stored in `g.edata["feat"]` and `g.edata["weights"]` that provide crucial relational cues for downstream graph convolutional networks.

## How the --add-eweights Flag Works

When passed via CLI, `--add-eweights` (short form `-addE`) stores a boolean value in `FEATURES.add_eweights` within the preprocessing configuration. In [`doc2graph/utils.py`](https://github.com/andreagemelli/doc2graph/blob/main/doc2graph/utils.py), this setting persists to the configuration file, allowing the `FeatureBuilder` class to conditionally execute geometric feature computation during graph construction.

## Edge Feature Computation Pipeline

When enabled, the `FeatureBuilder` in [`doc2graph/data/feature_builder.py`](https://github.com/andreagemelli/doc2graph/blob/main/doc2graph/data/feature_builder.py) executes a spatial analysis pipeline for each graph:

### Polar Coordinate Extraction

For every edge `(src, dst)` connecting two document entities, the system invokes the `polar()` method to compute Euclidean distance and angular displacement between the source and destination bounding boxes. This geometric calculation captures the spatial orientation of entities within the document layout.

### Feature Discretization

Continuous polar values undergo quantization via the `to_bin()` function, which bins measurements into `num_polar_bins` (default 8) discrete categories. This discretization produces fixed-size vectors for each edge, ensuring consistent tensor dimensions regardless of document complexity.

### Edge Tensor and Weight Generation

The binned vectors populate `g.edata["feat"]`, creating the edge feature matrix utilized by GNN models. Simultaneously, distances undergo normalization (`1 - d/m`) and thresholding (`> 0.9 → 0.1, else 0`) to generate scalar weights stored in `g.edata["weights"]`. These weights subsequently enable computation of per-node normalization factors in `g.ndata["norm"]` for message-passing operations.

## Default Behavior Without Edge Features

If `--add-eweights` is omitted, `FeatureBuilder` bypasses geometric calculations entirely. The system initializes dummy distances of `0.0` and assigns a default normaliser value of `m = 1`, constructing graphs containing exclusively node-level features (geometric, visual, and textual attributes) without `edata` tensors.

## Implementation Examples

### Command Line Usage

Activate edge features during dataset preprocessing:

```bash
python -m doc2graph.main \
    --src-data FUNSD \
    --edge-type fully \
    --add-eweights

```

Omitting the flag generates graphs lacking spatial edge attributes.

### Python API Configuration

Programmatically enable the flag before building graphs:

```python
from doc2graph.data.graph_builder import GraphBuilder
from doc2graph.utils import get_config

cfg = get_config("preprocessing")
cfg.FEATURES.add_eweights = True  # Equivalent to --add-eweights

graphs, feats = GraphBuilder().build()
g = graphs[0]

print("Edge features:", g.edata["feat"].shape)  # [E, 8]

print("Edge weights:", g.edata["weights"])

```

### GNN Model Integration

Utilize edge features within message-passing layers:

```python
import torch.nn as nn
import dgl.function as fn

class EdgeAwareGNN(nn.Module):
    def __init__(self, in_dim, hidden_dim, out_dim):
        super().__init__()
        self.conv = dgl.nn.GraphConv(in_dim + 8, hidden_dim)
        self.lin = nn.Linear(hidden_dim, out_dim)

    def forward(self, g, node_feats):
        e_feat = g.edata["feat"]  # [E, 8]

        g.ndata["h"] = node_feats
        g.update_all(fn.u_mul_e('h', 'feat', 'm'), fn.sum('m', 'h'))
        h = self.conv(g, g.ndata["h"])
        return self.lin(h)

```

## Source Code References

The implementation spans three critical modules:

- **[`doc2graph/main.py`](https://github.com/andreagemelli/doc2graph/blob/main/doc2graph/main.py)**: Defines the CLI argument parser accepting `--add-eweights`
- **[`doc2graph/utils.py`](https://github.com/andreagemelli/doc2graph/blob/main/doc2graph/utils.py)**: Persists the flag to the preprocessing configuration
- **[`doc2graph/data/feature_builder.py`](https://github.com/andreagemelli/doc2graph/blob/main/doc2graph/data/feature_builder.py)**: Contains the `FeatureBuilder` class implementing polar coordinate computation, binning via `to_bin()`, and weight generation logic

## Summary

- The `--add-eweights` flag activates geometric edge feature computation in doc2graph's preprocessing pipeline
- **Edge features** capture spatial relationships through binned polar coordinates (distance and angle) stored in `g.edata["feat"]` as 8-dimensional vectors
- **Edge weights** provide normalized distance scalars in `g.edata["weights"]` for message-passing normalization
- Without the flag, graphs contain only node-level features with dummy zero distances and default normalization
- The `FeatureBuilder` class in [`doc2graph/data/feature_builder.py`](https://github.com/andreagemelli/doc2graph/blob/main/doc2graph/data/feature_builder.py) conditionally executes this logic based on the `FEATURES.add_eweights` configuration value

## Frequently Asked Questions

### What happens if I omit the --add-eweights flag during preprocessing?

Doc2graph constructs graphs without geometric edge features. The `FeatureBuilder` skips polar coordinate calculations, assigns dummy distance values of `0.0`, and sets the normalization factor `m = 1`. Resulting graphs lack both `g.edata["feat"]` and `g.edata["weights"]` tensors, containing only node-level geometric, visual, and textual features.

### How are the edge features mathematically computed?

For each edge connecting two document entities, the system calculates Euclidean distance and angular displacement between their bounding box centers using the `polar()` function in [`doc2graph/data/feature_builder.py`](https://github.com/andreagemelli/doc2graph/blob/main/doc2graph/data/feature_builder.py). These continuous values are then discretized into `num_polar_bins` (default 8) bins via the `to_bin()` method, creating a fixed-size feature vector that quantifies the spatial relationship between connected nodes.

### Can I adjust the number of bins for edge features?

Yes. The `num_polar_bins` parameter controls the dimensionality of the edge feature vectors, defaulting to 8. You can modify this value in the preprocessing configuration before calling `GraphBuilder().build()`, which directly affects the shape of `g.edata["feat"]` (specifically `[E, num_polar_bins]` where E represents the number of edges).

### Why are edge weights thresholded at 0.9 during normalization?

The thresholding operation (`> 0.9 → 0.1, else 0`) in [`doc2graph/data/feature_builder.py`](https://github.com/andreagemelli/doc2graph/blob/main/doc2graph/data/feature_builder.py) implements a proximity-based filtering mechanism. Normalized distances exceeding 0.9 indicate entities that are relatively far apart spatially; assigning them a minimal weight of 0.1 reduces their influence during message passing, while closer entities retain full connectivity strength, prioritizing local spatial relationships in the document layout.