# TraversionGraph Cost Function: 8 Factors That Influence Pathfinding in p2r3/convert

> Discover the 8 factors influencing the TraversionGraph cost function in p2r3/convert pathfinding. Optimize format conversion routes with this guide.

- Repository: [p2r3/convert](https://github.com/p2r3/convert)
- Tags: internals
- Published: 2026-02-19

---

**The TraversionGraph cost function determines conversion path weights by combining base step costs, category-change penalties, handler/format priority offsets, and lossy conversion multipliers to steer Dijkstra's algorithm toward optimal format conversion routes.**

The `TraversionGraph` class in the [p2r3/convert](https://github.com/p2r3/convert) repository builds a weighted, directed graph representing all possible format-to-format conversions. Each edge's weight is produced by the **cost function** (`costFunction`) and determines which conversion path the pathfinder prefers. Understanding these cost components is essential for optimizing conversion chains or debugging routing decisions.

## Core Cost Function Components

The cost calculation in [`src/TraversionGraph.ts`](https://github.com/p2r3/convert/blob/main/src/TraversionGraph.ts) aggregates multiple independent factors into a single edge weight used by the graph search algorithm.

### Base Step Cost (DEPTH_COST)

Every conversion step incurs a constant base cost defined by `DEPTH_COST`. This penalizes longer conversion chains, encouraging direct routes when available. The calculation initializes with this value at lines 90-92:

```typescript
let cost = DEPTH_COST;

```

### Category-Change Penalties

When converting between different MIME categories (e.g., image to video), the cost function adds a category-change penalty. The `categoryChangeCosts` array stores custom costs for specific from/to pairs, while `DEFAULT_CATEGORY_CHANGE_COST` serves as the fallback when no specific entry exists. This logic executes in the conditional block starting at line 95:

```typescript
if (fromCategory && toCategory) {
  // Category change cost calculation (lines 95-123)
}

```

### Strict Category Enforcement

The `strictCategories` boolean modifies how category changes are penalized. When `true`, the function sums penalties for every possible category transition in the list. When `false`, penalties apply only if the source and target formats share no common category. This conditional appears at lines 100-110 in [`src/TraversionGraph.ts`](https://github.com/p2r3/convert/blob/main/src/TraversionGraph.ts).

### Handler and Format Priority Costs

Two priority-based costs bias the algorithm toward preferred handlers and formats:

- **Handler Priority Cost**: `HANDLER_PRIORITY_COST * handlerIndex` adds incremental cost based on registration order (earlier handlers are cheaper). Implemented at lines 130-132.
- **Format Priority Cost**: `FORMAT_PRIORITY_COST` multiplied by the format's index in `supportedFormats` makes earlier-listed formats more attractive. Calculated at lines 134-136:

```typescript
cost += FORMAT_PRIORITY_COST * (handlerObj?.supportedFormats?.findIndex(f => f.mime === to.format.mime) ?? 0);

```

### Lossy Conversion Multiplier

If the target format lacks the `lossless` flag, the accumulated cost is multiplied by `LOSSY_COST_MULTIPLIER`, strongly discouraging quality-degrading routes. This check occurs at lines 138-140:

```typescript
if (!to.format.lossless) cost *= LOSSY_COST_MULTIPLIER;

```

### Adaptive Costs and Dead-End Avoidance

The `calculateAdaptiveCost` method (lines 71-81) adds dynamic penalties for specific category sequences (e.g., text → image → audio) via `categoryAdaptiveCosts`. It also implements dead-end avoidance by returning `Infinity` for paths that previously failed:

```typescript
if (isDeadEnd) return Infinity;

```

## Customizing the Cost Function

You can influence pathfinding by adjusting cost parameters at initialization or runtime. The `addCategoryChangeCost` method allows custom penalties for specific category transitions.

```typescript
import { TraversionGraph } from "./TraversionGraph";
import { FormatDefinition } from "./FormatHandler";

// Initialize graph with handlers
const graph = new TraversionGraph();
const handlers = [ffmpegHandler, imgMagickHandler];
graph.init(supportedMap, handlers, /*strictCategories=*/ false);

// Discourage image-to-video conversions with higher cost (default is 0.6)
graph.addCategoryChangeCost("image", "video", 2.0);

// Define source and target nodes
const fromNode = new ConvertPathNode(ffmpegHandler, pngFormat);
const toNode = new ConvertPathNode(imgMagickHandler, mp4Format);

// Execute search (simpleMode = true returns first valid path)
for await (const path of graph.searchPath(fromNode, toNode, true)) {
  console.log("Conversion chain:");
  path.forEach(p => console.log(` → ${p.handler.name} (${p.format.mime})`));
}

```

## Summary

- The **TraversionGraph cost function** combines eight distinct factors to calculate edge weights for Dijkstra's algorithm in [`src/TraversionGraph.ts`](https://github.com/p2r3/convert/blob/main/src/TraversionGraph.ts).
- **Base costs** (`DEPTH_COST`) penalize path length while **category-change costs** discourage跨-category conversions unless necessary.
- **Priority costs** bias the algorithm toward earlier-registered handlers and formats with lower index positions.
- The **lossy multiplier** (`LOSSY_COST_MULTIPLIER`) automatically degrades routes that result in quality loss.
- **Adaptive costs** prevent problematic category sequences and avoid known dead ends by returning `Infinity`.
- All cost calculations occur between lines 71-140, with the primary composition logic in the `costFunction` implementation.

## Frequently Asked Questions

### What is the default category change cost in TraversionGraph?

If no specific cost is defined for a category transition in `categoryChangeCosts`, the cost function uses `DEFAULT_CATEGORY_CHANGE_COST`. You can override this per from/to pair using `addCategoryChangeCost()` or by modifying the map directly before executing the search.

### How does strictCategories affect pathfinding?

When `strictCategories` is set to `true` during graph initialization, the cost function sums penalties for every possible category transition. When `false`, it only adds costs when the source and target formats share no common MIME category, allowing more flexible routing through intermediate formats that share categories.

### Why does the cost function multiply by LOSSY_COST_MULTIPLIER?

The lossy multiplier penalizes conversion routes that result in quality loss. If `to.format.lossless` evaluates to false, the accumulated cost is multiplied by this constant (typically a value greater than 1.0), making lossless routes significantly cheaper and thus preferred by Dijkstra's algorithm.

### Where is the cost function defined in the source code?

The primary cost calculation logic resides in [`src/TraversionGraph.ts`](https://github.com/p2r3/convert/blob/main/src/TraversionGraph.ts) within the `costFunction` method (lines 90-140). Adaptive cost modifications and dead-end detection occur in the `calculateAdaptiveCost` method (lines 71-81), which is called during the path exploration phase.