Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
118 changes: 118 additions & 0 deletions algorithms/BellmanFord.typ
Original file line number Diff line number Diff line change
@@ -0,0 +1,118 @@
#import "../lib/style.typ": *
#import "../lib/mapcode.typ": *

== Bellman-Ford Shortest Path Algorithm

Compute shortest paths from a source vertex to all other vertices in a weighted graph, handling negative edge weights (assuming no negative cycle).

Formal definition:
$
"dist"^((k))(v) = cases(
0 & "if " v = s and k = 0,
infinity & "if " v != s and k = 0,
min("dist"^((k-1))(v), min_(u,v,w in E)("dist"^((k-1))(u) + w)) & "otherwise"
)
$

*As mapcode:*

_primitives_: `min`(min), `add`($+$)

$
I = n:NN times "edges": ("Vertex" times "Vertex" times ZZ)^* times s:"Vertex" quad quad\
X & = ([0..n-1] -> (NN union {infinity})_bot) times "edges" quad quad\
A = [0..n-1] -> (NN union {infinity})_bot\

rho(n, "edges", s) & = ("dist", "edges") "where" "dist" = cases(
0 & "if" v = s,
bot & "if" v != s
) \

F("dist", "edges") & = ("dist"', "edges") "where for each edge" (u,v,w) in "edges":\
& "dist"'[v] = cases(
"dist"[v] & "if" "dist"[u] = bot,
"dist"[u] + w & "if" "dist"[v] = bot,
min("dist"[v], "dist"[u] + w) & "if" "dist"[u] != bot and "dist"[v] != bot
)\

pi("dist", "edges") & = "dist"
$

#let inst_n = 5;
#let inst_edges = (
(0, 1, 6), (0, 3, 7), (1, 2, 5),
(1, 3, 8), (1, 4, -4), (2, 1, -2),
(3, 2, -3), (3, 4, 9), (4, 2, 7)
);
#let inst_src = 0;

#figure(
caption: [Bellman-Ford shortest path computation from vertex #inst_src],
$#{
// Implement rho function
let rho = ((n, edges, src)) => {
// Initialize distance array with all BOTTOM (none) except source
let dist = ()
for i in range(0, n) {
if i == src {
dist.push(0)
} else {
dist.push(none)
}
}
(dist, edges) // State is (distance array, edges)
}

// Implement F_i function - edge relaxation
let F_i = (edges) => (dist) => ((v,)) => {
let current = dist.at(v)

// Try to relax edges ending at vertex v
for edge in edges {
let (u, vtx, w) = edge
if vtx == v and dist.at(u) != none {
let new_cost = dist.at(u) + w
if current == none or new_cost < current {
current = new_cost
}
}
}
current
}

let F = (edges) => (state) => {
let (dist, e) = state
let new_dist = map_tensor(F_i(edges), dim: 1)(dist)
(new_dist, e)
}

let pi = (state) => {
// Return final distances
let (dist, edges) = state
dist
}

// Visualization helper
let x_h(state, diff_mask:none) = {
let (dist, edges) = state
let cells = dist.enumerate().map(((i, d)) => {
let val = if d != none {[$#d$]} else {[$bot$]}
if diff_mask != none and diff_mask.at(0).at(i) {
// changed element: highlight
rect(fill: yellow.transparentize(70%), inset: 2pt)[$v_#i: #val$]
} else {
rect(stroke: none, inset: 2pt)[$v_#i: #val$]
}
})
$vec(delim: "[", ..cells)$
}

mapcode-viz(
rho, F(inst_edges), pi,
X_h: x_h,
pi_name: [$mpi$],
dim: 2, // Explicitly set dimension: tuple of (array, edges)
group-size: calc.min(4, inst_n + 1),
cell-size: 30mm, scale-fig: 75%
)((inst_n, inst_edges, inst_src))
}$)
180 changes: 180 additions & 0 deletions algorithms/TreeHeight.typ
Original file line number Diff line number Diff line change
@@ -0,0 +1,180 @@
#import "../lib/style.typ": *
#import "../lib/mapcode.typ": *

== Tree Height

Compute the height of a rooted tree. The height of a tree is the maximum distance from the root to any leaf node.

Formal definition:
$
"height"(v) = cases(
0 & "if " v "is a leaf",
1 + max_(c in "children"(v)) "height"(c) & "otherwise"
)
$

Example:
```
0
/ \
1 2
/ \
3 4
```
- height(1) = 0 (leaf)
- height(3) = 0 (leaf)
- height(4) = 0 (leaf)
- height(2) = 1 + max(0, 0) = 1
- height(0) = 1 + max(0, 1) = 2

*As mapcode:*

_primitives_: `max`(max), `add`($+$)

Let:
- $"Node" = NN$ (node identifiers)
- $"Children" = "Node" -> "Node"^*$ (adjacency list)
- $"Heights" = "Node" -> NN_bot$ (height map)

$
I & = "Children" \
X & = "Heights" \
A & = NN\

rho("children") & = "heights" "where" "heights"[v] = bot quad forall v\

F("heights") & = "heights"' "where for each node" v:\
& "heights"'[v] = cases(
0 & "if" "children"[v] = emptyset,
1 + max_(c in "children"[v]) "heights"[c] & "if" forall c in "children"[v]: "heights"[c] != bot,
"heights"[v] & "otherwise (preserve old value)"
)\

pi("heights") & = "heights"[0] quad "(root is node 0)"
$

#let inst_children = (
(1, 2), // 0: root
(), // 1: leaf
(3, 4), // 2: branch
(), // 3: leaf
(), // 4: leaf
);

#figure(
caption: [Tree height computation using mapcode],
$
#{
// rho: Initialize all heights to BOTTOM
let rho = (children) => {
let n = children.len()
let heights = ()
for i in range(0, n) {
heights.push(none)
}
heights // State is just the heights array
}

// F_i: Compute height for node i
let F_i = (children) => (heights) => ((i,)) => {
let current = heights.at(i)

// If already computed, keep it
if current != none {
return current
}

// Get children of node i
let node_children = children.at(i)

// Leaf node: height = 0
if node_children.len() == 0 {
return 0
}

// Branch node: check if all children have known heights
let child_heights = ()
let all_known = true
for child in node_children {
let child_height = heights.at(child)
if child_height == none {
all_known = false
break
}
child_heights.push(child_height)
}

// If all children heights are known, compute this node's height
if all_known and child_heights.len() > 0 {
return 1 + calc.max(..child_heights)
}

// Otherwise, keep as BOTTOM
return none
}

let F = (children) => (heights) => {
map_tensor(F_i(children), dim: 1)(heights)
}

let pi = (heights) => {
heights.at(0) // Return height of root (node 0)
}

// Visualization helper
let X_h = (heights, diff_mask: none) => {
let cells = heights.enumerate().map(((i, h)) => {
let val = if h != none {[$#h$]} else {[$bot$]}
let node_children = inst_children.at(i)
let children_str = if node_children.len() == 0 {
"(leaf)"
} else {
let child_list = node_children.map(c => str(c)).join(",")
"→{" + child_list + "}"
}

if diff_mask != none and diff_mask.at(i) {
// changed element: highlight
rect(fill: yellow.transparentize(70%), inset: 2pt)[
$v_#i#text(size: 8pt)[#children_str]: #val$
]
} else {
rect(stroke: none, inset: 2pt)[
$v_#i#text(size: 8pt)[#children_str]: #val$
]
}
})
$vec(delim: "[", ..cells)$
}

// Tree visualization
let I_h = (children) => {
// Simple tree representation showing the structure
let nodes = children.enumerate().map(((i, c)) => {
if c.len() == 0 {
[$v_#i$ (leaf)]
} else {
let child_list = c.map(ch => [$v_#ch$]).join([, ])
[$v_#i -> {#child_list}$]
}
})
table(
columns: 1,
align: left,
stroke: none,
..nodes
)
}

mapcode-viz(
rho, F(inst_children), pi,
I_h: I_h,
X_h: X_h,
pi_name: [$mpi$],
group-size: calc.min(5, inst_children.len() + 1),
cell-size: 35mm,
scale-fig: 70%
)(inst_children)
}
$
)
5 changes: 5 additions & 0 deletions main.typ
Original file line number Diff line number Diff line change
Expand Up @@ -52,3 +52,8 @@ All primitives are _strict_ meaning they do not allow for undefined values (i.e.
#include "algorithms/LongestCommonSubsequence.typ"
#pagebreak()
#include "algorithms/leetcode/P2_add-two-numbers.typ"
#pagebreak()
#include "algorithms/BellmanFord.typ"
#pagebreak()
#include "algorithms/TreeHeight.typ"