Hone logo
Hone
Problems

Type-Level Maximum Flow in TypeScript

This challenge asks you to implement a type-level maximum flow algorithm in TypeScript. Type-level programming allows us to perform computations at compile time, enabling powerful static analysis and optimization. This problem explores how to apply graph algorithms within the type system.

Problem Description

You are tasked with creating a type-level implementation of the Ford-Fulkerson algorithm to determine the maximum flow through a directed graph. The graph is represented by a type that defines the edges and their capacities. Your solution should calculate the maximum flow from a source node to a sink node, represented as type parameters. The result should be a type representing the maximum flow value.

What needs to be achieved:

  • Define a type Graph<S, T, Capacity> to represent a directed graph where S and T are source and target nodes respectively, and Capacity is a type representing the capacity of each edge.
  • Implement a type-level function maxFlow<S, T, Capacity> that takes a Graph as input and returns a type representing the maximum flow from S to T.
  • The Graph type should be structured such that it allows for iterative flow augmentation.

Key Requirements:

  • The algorithm must be implemented entirely at the type level, using TypeScript's conditional types and other type manipulation features.
  • The solution should be reasonably efficient in terms of type checking time (avoiding excessively complex or recursive type definitions that lead to compilation errors).
  • The algorithm should correctly handle cases where no path exists from the source to the sink.

Expected Behavior:

  • Given a valid Graph, the maxFlow function should return a type representing the maximum flow value. This type can be a simple union of numbers representing possible flow values.
  • If there is no path from the source to the sink, the function should return a type representing zero flow (e.g., 0).
  • The algorithm should respect the capacity constraints of the edges.

Important Edge Cases to Consider:

  • No Path: The graph may not contain a path from the source to the sink.
  • Zero Capacity Edges: Edges may have zero capacity.
  • Disconnected Graph: The graph may be disconnected.
  • Source and Sink are the Same: The source and sink nodes may be the same.

Examples

Example 1:

type Graph1 = {
  source: 'S';
  target: 'T';
  edges: {
    'S' extends 'S' ? 'T' extends 'T' ? [Capacity: 10] : never : never;
    'S' extends 'S' ? 'U' extends 'U' ? [Capacity: 5] : never : never;
    'U' extends 'U' ? 'T' extends 'T' ? [Capacity: 7] : never : never;
    'U' extends 'U' ? 'V' extends 'V' ? [Capacity: 3] : never : never;
    'V' extends 'V' ? 'T' extends 'T' ? [Capacity: 5] : never : never;
  };
};

type Result1 = maxFlow<Graph1['source'], Graph1['target'], Graph1['edges']>; // Expected: 12

Explanation: The maximum flow from 'S' to 'T' is 10 through S -> T directly, and 5 through S -> U -> T. The path S -> U -> V -> T is limited by the capacity of the edge U -> V (3) and V -> T (5), so it contributes a maximum of 3. Therefore, the total flow is 10 + 2 = 12.

Example 2:

type Graph2 = {
  source: 'S';
  target: 'T';
  edges: {
    'S' extends 'S' ? 'T' extends 'T' ? [Capacity: 5] : never : never;
    'S' extends 'S' ? 'U' extends 'U' ? [Capacity: 3] : never : never;
    'U' extends 'U' ? 'T' extends 'T' ? [Capacity: 2] : never : never;
  };
};

type Result2 = maxFlow<Graph2['source'], Graph2['target'], Graph2['edges']>; // Expected: 5

Explanation: The maximum flow is limited by the direct edge from S to T with a capacity of 5. The path S -> U -> T has a capacity of min(3, 2) = 2. Therefore, the total flow is 5 + 2 = 7.

Example 3: (No Path)

type Graph3 = {
  source: 'S';
  target: 'T';
  edges: {
    'S' extends 'S' ? 'U' extends 'U' ? [Capacity: 5] : never : never;
    'U' extends 'U' ? 'V' extends 'V' ? [Capacity: 3] : never : never;
  };
};

type Result3 = maxFlow<Graph3['source'], Graph3['target'], Graph3['edges']>; // Expected: 0

Explanation: There is no path from 'S' to 'T', so the maximum flow is 0.

Constraints

  • The graph will have a maximum of 10 nodes.
  • The capacity of each edge will be a number between 0 and 100.
  • The type system should be able to infer the maximum flow within a reasonable time (e.g., less than 30 seconds). Excessive recursion or complex type manipulations may lead to compilation errors.
  • The source and target nodes must be distinct.

Notes

  • This is a challenging problem that requires a deep understanding of TypeScript's type system and conditional types.
  • Consider using a recursive approach to explore the graph and find augmenting paths.
  • The Graph type definition is crucial for representing the graph structure and edge capacities.
  • Think about how to represent the residual graph at the type level. This is a key concept in the Ford-Fulkerson algorithm.
  • You may need to define helper types to simplify the implementation.
  • Start with a small, simple graph and gradually increase the complexity as you test your solution.
  • The exact type of the result (the maximum flow) is flexible, but it should be a type that can represent the maximum flow value. A union of numbers is a common choice.
Loading editor...
typescript