Ford Fulkerson Algorithm

The Ford – Fulkerson method or Ford – Fulkerson algorithm (FFA) is a greedy algorithm that calculates the maximal flow in a flow network. The name" Ford – Fulkerson" is often also used for the Edmonds – Karp algorithm, which is a fully specify implementation of the Ford – Fulkerson method.
package org.gs.digraph

import scala.annotation.tailrec
import scala.collection.mutable.{ArrayBuffer, Queue}
import scala.math.{abs, min}

/** Compute a maximum st-flow and minimum st-cut in a flow network.
  *
  * @constructor creates a new FordFulkerson with a FlowNetwork, source and target vertices
  * @param g FlowNetwork
  * @param s source vertex
  * @param t sink vertex
  * @see [[https://algs4.cs.princeton.edu/64maxflow/FordFulkerson.java.html]]
	* @author Scala translation by Gary Struthers from Java by Robert Sedgewick and Kevin Wayne.
  */
class FordFulkerson(g: FlowNetwork, s: Int, t: Int) {
  private val Epsilon = 1e-11

  private def rangeGuard(x: Int): Boolean = x match {
    case x if 0 until g.v contains x => true
    case _ => false
  }

  require(rangeGuard(s) && rangeGuard(t), s"source verticies s:$s or t:$t not in 0..${g.v} ")
  require(s != t, s"source s:$s equals target t:$t")

  private def excess(v: Int) = {
    def excessFlow(z: Double, e: FlowEdge): Double = (if (v == e.from) -e.flow else e.flow) + z

    g.adj(v).foldLeft(0.0)(excessFlow(_, _))
  }

  private var _value = excess(t)

  private def isFeasible(): Boolean = {

    def capacityGuard(e: FlowEdge): Boolean =
      if ((e.flow < -Epsilon) || (e.flow > e.capacity + Epsilon)) false else true

    @tailrec
    def checkCapacityConstraints(v: Int): Boolean = if (v < g.v) {
      val es = g.adj(v)

      @tailrec
      def checkEdgeCapacityConstraints(es: List[FlowEdge]): Boolean = es match {
        case x :: xs => if (capacityGuard(x)) checkEdgeCapacityConstraints(xs) else false
        case Nil => true
      }

      if (checkEdgeCapacityConstraints(es)) checkCapacityConstraints(v + 1) else false
    } else true

    def lessThanSourceExcess(): Boolean = if (abs(_value + excess(s)) > Epsilon) false else true

    def lessThanSinkExcess(): Boolean = if (abs(_value - excess(t)) > Epsilon) false else true

    @tailrec
    def loopVNetFlow(v: Int): Boolean = if (v < g.v) {
      if ((v != s && v != t) && (abs(excess(v)) > Epsilon)) false else loopVNetFlow(v + 1)
    } else true

    checkCapacityConstraints(0) && lessThanSourceExcess && lessThanSinkExcess && loopVNetFlow(0)
  }
  assert(isFeasible, "initial flow is infeasible")

  private def hasAugmentingPath(): (Option[Array[FlowEdge]], Array[Boolean]) = {
    val edgeTo = new Array[FlowEdge](g.v)
    val marked = Array.fill[Boolean](g.v)(false)
    marked(s) = true

    @tailrec
    def loopQ(q: Queue[Int]): Unit = if (!q.isEmpty) {
      val v = q.dequeue

      def tryPath(e: FlowEdge): Unit = {
        val w = e.other(v)
        if (e.residualCapacityTo(w) > 0) {
          if (!marked(w)) {
            edgeTo(w) = e
            marked(w) = true
            q.enqueue(w)
          }
        }
      }

      g.adj(v) foreach (tryPath)
      loopQ(q)
    }

    loopQ(Queue[Int](s))
    if (marked(t)) (Some(edgeTo), marked) else (None, marked)
  }

  @tailrec
  private def onAugmentedPath(prevPath: Option[Array[FlowEdge]]):
    (Option[Array[FlowEdge]], Array[Boolean]) = {

    hasAugmentingPath() match {
      case (None, marked) => (Some(prevPath.get), marked)
      case (Some(edgeTo), marked) => {

        @tailrec
        def bottleneckCapacity(v: Int, bottle: Double): Double = if (v != s) {
          val minBottle = min(bottle, edgeTo(v).residualCapacityTo(v))
          val nextV = edgeTo(v).other(v)
          bottleneckCapacity(nextV, minBottle)
        } else bottle

        val bottle = bottleneckCapacity(t, Double.PositiveInfinity)

        @tailrec
        def augmentFlow(v: Int): Unit = if (v != s) {
          edgeTo(v).addResidualFlowTo(v, bottle)
          val nextV = edgeTo(v).other(v)
          augmentFlow(nextV)
        }
        augmentFlow(t)

        _value += bottle
        onAugmentedPath(Some(edgeTo))
      }
    }
  }

  private val pathAndMarked = onAugmentedPath(None)
  val edgeTo = pathAndMarked._1
  private val marked = pathAndMarked._2

  def value(): Double = _value

  def inCut(v: Int): Boolean = {
    val R1 = 0 until marked.length
    require(R1.contains(v), s"vertex v:$v is not between 0 and ${marked.length}")
    marked(v)
  }

  override def toString(): String = {
    val sb = new StringBuilder("edgeTo: ")
    edgeTo match {
      case None => sb append ("None ")
      case Some(x) => sb append (x mkString ",")
    }
    sb append (s" value:${_value}")
    sb append (s" marked:${marked mkString ","}")
    sb.toString
  }
}

/** Factory for FordFulkerson instances with additional validation */
object FordFulkerson {
  def apply(g: FlowNetwork, s: Int, t: Int): Option[FordFulkerson] = {
    val ff = new FordFulkerson(g, s, t)

    def check(): Boolean = if (ff.isFeasible && ff.inCut(s) && !ff.inCut(t)) {
      val mincutValues = for {
        v <- 0 until g.v
        e <- g.adj(v)
        if (v == e.from && ff.inCut(e.from) && !ff.inCut(e.to))
      } yield e.capacity

      val mincutvalue = mincutValues.foldLeft(0.0)(_ + _)
      if (abs(mincutvalue - ff.value) > ff.Epsilon) false else true
    } else false

    if (check) Some(ff) else None
  }
}

LANGUAGE:

DARK MODE: