# How Edge Weights Are Determined in the Knowledge Graph: A Deep Dive into Understand-Anything

> Discover how Understand Anything determines edge weights in its knowledge graph. Learn about relationship types and normalization for 0-1 range values.

- Repository: [Yuxiang Lin/Understand-Anything](https://github.com/Lum1104/Understand-Anything)
- Tags: deep-dive
- Published: 2026-06-08

---

**Edge weights in the Understand-Anything knowledge graph are determined by relationship type during graph construction (containment=1, imports=0.7, calls=0.8) and normalized during schema validation to ensure they fall within the 0-1 range.**

The Understand-Anything repository builds a semantic knowledge graph to model codebases, where relationships between entities are stored as **`GraphEdge`** objects with numeric weights expressing confidence levels. Every edge carries a `weight` property normalized between 0 and 1, established through a two-stage pipeline involving the `GraphBuilder` and schema validation logic.

## Relationship-Based Weight Assignment in GraphBuilder

During graph construction, the `GraphBuilder` class assigns type-specific weights that reflect the semantic strength of each relationship. These defaults are hard-coded in [`understand-anything-plugin/packages/core/src/analyzer/graph-builder.ts`](https://github.com/Lum1104/Understand-Anything/blob/main/understand-anything-plugin/packages/core/src/analyzer/graph-builder.ts) based on the certainty of the underlying dependency.

### Containment Edges (Weight = 1)

**Containment edges** represent structural ownership where a file definitively contains a function or class. Because this relationship is absolute, the builder assigns a weight of **`1`**:

```typescript
this.edges.push({ 
  source: fileId, 
  target: funcId, 
  type: "contains", 
  direction: "forward", 
  weight: 1 
});

```

### Import Edges (Weight = 0.7)

**Import edges** (`type: "imports"`) indicate module dependencies with moderate confidence. The builder assigns **`0.7`** to reflect that while the dependency exists, it may be conditional or tree-shaken:

```typescript
this.edges.push({
  source: `file:${fromFile}`,
  target: `file:${toFile}`,
  type: "imports",
  direction: "forward",
  weight: 0.7,
});

```

### Call Edges (Weight = 0.8)

**Call edges** (`type: "calls"`) represent function invocations with higher confidence than imports but less than physical containment. The builder assigns **`0.8`**:

```typescript
this.edges.push({
  source: `function:${callerFile}:${callerFunc}`,
  target: `function:${calleeFile}:${calleeFunc}`,
  type: "calls",
  direction: "forward",
  weight: 0.8,
});

```

## Schema Validation and Normalization

After construction, the `GraphSchema` in [`understand-anything-plugin/packages/core/src/schema.ts`](https://github.com/Lum1104/Understand-Anything/blob/main/understand-anything-plugin/packages/core/src/schema.ts) sanitizes every edge weight to guarantee validity. This validation layer ensures that downstream algorithms in [`edgeAggregation.ts`](https://github.com/Lum1104/Understand-Anything/blob/main/edgeAggregation.ts) receive properly bounded numeric values.

### Handling Missing Weights

If the `weight` field is **undefined** or **null**, the schema injects a default value of **`0.5`** and records an auto-correction issue:

```typescript
if (e.weight === undefined || e.weight === null) {
  e.weight = 0.5;
}

```

### Type Coercion and Clamping

The schema handles malformed inputs through progressive normalization:

1. **String parsing** – If the weight arrives as a string, the validator attempts `parseFloat()` coercion, falling back to `0.5` for non-numeric strings.
2. **Range clamping** – Any numeric value outside the `[0, 1]` interval is clamped to the nearest bound using `Math.max(0, Math.min(1, e.weight))`.

```typescript
// Schema – string → number
else if (typeof e.weight === "string") {
  const parsed = parseFloat(e.weight);
  e.weight = isNaN(parsed) ? 0.5 : parsed;
}
// Schema – clamp to [0,1]
if (typeof e.weight === "number" && (e.weight < 0 || e.weight > 1)) {
  e.weight = Math.max(0, Math.min(1, e.weight));
}

```

## Practical Code Examples

### Building a Graph with Automatic Weights

The `GraphBuilder` automatically applies the type-specific weights when constructing the knowledge graph:

```typescript
import { GraphBuilder } from "@understand-anything/core";

const builder = new GraphBuilder();

// Add a file node and its functions (contains → weight = 1)
builder.addFileNode("src/service.ts");

// Record that service.ts imports utils.ts (imports → weight = 0.7)
builder.addImportEdge("src/service.ts", "src/utils.ts");

// Record that service.ts calls utils.calculate (calls → weight = 0.8)
builder.addCallEdge("src/service.ts", "main", "src/utils.ts", "calculate");

// Final graph with normalized edges
const graph = builder.build();

```

### Handling Out-of-Range Weights

The schema validator automatically corrects invalid weights during parsing:

```typescript
import { parseGraph } from "@understand-anything/core";

const raw = {
  nodes: [{ id: "file:a.ts", type: "file", name: "a.ts", summary: "", tags: [], complexity: "simple" }],
  edges: [{ 
    source: "file:a.ts", 
    target: "file:b.ts", 
    type: "imports", 
    direction: "forward", 
    weight: 1.5 
  }]
};

const { data, issues } = parseGraph(raw);
console.log(data.edges[0].weight); // → 1 (clamped)
console.log(issues[0].category);   // → "out-of-range"

```

## Key Files in the Architecture

- **[`understand-anything-plugin/packages/core/src/analyzer/graph-builder.ts`](https://github.com/Lum1104/Understand-Anything/blob/main/understand-anything-plugin/packages/core/src/analyzer/graph-builder.ts)** – Constructs graph edges and assigns initial type-specific weights.
- **[`understand-anything-plugin/packages/core/src/types.ts`](https://github.com/Lum1104/Understand-Anything/blob/main/understand-anything-plugin/packages/core/src/types.ts)** – Declares the `GraphEdge` interface with the mandatory `weight: number` field.
- **[`understand-anything-plugin/packages/core/src/schema.ts`](https://github.com/Lum1104/Understand-Anything/blob/main/understand-anything-plugin/packages/core/src/schema.ts)** – Validates, coerces, and clamps edge weights to the [0, 1] range.
- **[`understand-anything-plugin/packages/dashboard/src/utils/edgeAggregation.ts`](https://github.com/Lum1104/Understand-Anything/blob/main/understand-anything-plugin/packages/dashboard/src/utils/edgeAggregation.ts)** – Consumes normalized weights for visualization aggregation.

## Summary

- **GraphBuilder** assigns hard-coded weights based on relationship semantics: containment (1), calls (0.8), and imports (0.7).
- **GraphSchema** ensures data integrity by defaulting missing weights to 0.5, parsing string values, and clamping out-of-range numbers to [0, 1].
- The weight system spans [`graph-builder.ts`](https://github.com/Lum1104/Understand-Anything/blob/main/graph-builder.ts) (creation) and [`schema.ts`](https://github.com/Lum1104/Understand-Anything/blob/main/schema.ts) (validation) before being utilized by visualization utilities.
- All `GraphEdge` objects are defined in [`types.ts`](https://github.com/Lum1104/Understand-Anything/blob/main/types.ts) with a mandatory numeric weight property.

## Frequently Asked Questions

### What is the default edge weight if not specified?

If the weight property is missing, null, or undefined, the schema validator in [`schema.ts`](https://github.com/Lum1104/Understand-Anything/blob/main/schema.ts) automatically assigns a default value of **0.5** and flags the record with an auto-correction issue. This ensures that downstream algorithms always receive a valid numeric weight even when the input data is incomplete.

### What happens if an edge weight exceeds the 0-1 range?

The schema validation logic clamps any numeric weight outside the `[0, 1]` interval to the nearest valid bound. Values greater than 1 are reduced to 1, and negative values are raised to 0 using `Math.max(0, Math.min(1, e.weight))`, ensuring all weights remain valid confidence scores.

### Why do different relationship types have different default weights?

The weight values reflect semantic certainty: **containment** relationships (1.0) represent physical file structure and are absolute; **call** relationships (0.8) indicate runtime behavior with high but not total certainty; and **import** relationships (0.7) acknowledge module dependencies that might be conditional or unused, warranting lower confidence.

### Where is the weight property defined in the type system?

The weight field is declared in [`understand-anything-plugin/packages/core/src/types.ts`](https://github.com/Lum1104/Understand-Anything/blob/main/understand-anything-plugin/packages/core/src/types.ts) as part of the `GraphEdge` interface, specified as a required `number` type. This type definition enforces that all edge objects must include a weight property, though the schema validator ensures backward compatibility with legacy data by filling in defaults when the field is absent.