Showing posts with label graph. Show all posts
Showing posts with label graph. Show all posts

Friday, 29 August 2014

Another neat interview question: a kind of crossword puzzle

My friend had another interview question. You have already seen the first one.
This one is about crosswords. It involves graphs obviously.
Let me start by giving a brief overview of the problem he had to solve, and my Scala take on it ;-)
Question Imagine a crossword grid of size n x n containing letters. Given a dictionary of words, find all possible paths in the grid that match the words in the dictionary.
For Example:
MBC
ARA
NMO
is a 3x3 grid of letters, if the dictionary contains the word MAN, there are two paths possible: [(0,0), (0,1), (0,2)] and [(1,2), (0,1), (0,2)].

My Scala take on it
Let's define a Coord class that represents the grid coordinates - and a bunch of methods to work out the neighbours of a given coordinate:
  val gridSize = 3                                //> gridSize  : Int = 3
  case class Coord(val x: Int, y: Int) {
    def /\ = Coord(x, y - 1)
    def \/ = Coord (x, y + 1)
    def |> = Coord(x + 1, y)
    def <| = Coord(x - 1, y)
    def -/ = Coord(x + 1, y - 1)
    def -\ = Coord(x + 1, y + 1)
    def \- = Coord(x - 1, y - 1)
    def /- = Coord(x - 1, y + 1)
    def isValid = x >= 0 && x < gridSize && y >= 0 && y < gridSize
  }
  type Path = List[Coord]
  type Paths = List[Path]
The next method works out all valid neighbours for a given coordinate:
  def /\/\ (c: Coord) = List(c /\, c \/, c |>, c <|, c -/, c -\, c \-, c /-).filter(_.isValid)
For example:
  require((/\/\(Coord(1, 1))).size == 8)
  require((/\/\(Coord(0, 0))).size == 3)
Let's define a dictionary for our tests:
  val dict = Set("MAN", "ARM", "CAR")             //> dict  : scala.collection.immutable.Set[String] = Set(MAN, ARM, CAR)
  val dictMaxLength = dict.map(w => w.size).max   //> dictMaxLength  : Int = 3
The main algorithm is the following that enumerates all the paths between two nodes in the graph. Note that I restrict the path length based on the maximum of letters defined in the dictionary. This can be improved further using the dictionary (prefix.. etc. left as an exercise).
  def allPaths(from: Coord, to: Coord): Paths = {
    def allPathsRec(currentCoord: Coord, currentPath: Path): Paths = {
      if (currentPath.size > dictMaxLength) Nil
      else if (currentCoord == to) List(currentPath)
      else /\/\(currentCoord).filter(c => !(currentPath contains c)).flatMap(c => allPathsRec(c, c :: currentPath))
    }
    allPathsRec(from, List(from)).map(_.reverse)
  }    
As an example, the following will give you the diagonal:
allPaths(Coord(0, 0), Coord(2, 2))  //WordGrid.Paths = List(List(Coord(0,0), Coord(1,1), Coord(2,2)))
Before we test it, we need one final procedure: a def to generate all pairs of cells (from one cell to another cell):
  def fromsTos: List[(Coord, Coord)] = {
    val keys = dictMap.keySet.toList
    for {
      c1 <- keys
      c2 <- keys.filter(_ != c1)
    } yield (c1, c2)
  }
dictMap is the crossword definition:
val dictMap = Map(Coord(0, 0) -> 'M', Coord(1, 0) -> 'B', Coord(2, 0) -> 'C',
                  Coord(0, 1) -> 'A', Coord(1, 1) -> 'R', Coord(2, 1) -> 'A',
                  Coord(0, 2) -> 'N', Coord(1, 2) -> 'M', Coord(2, 2) -> 'O')  
Test
Time to put this to the test:
// Generates all possible pairs path in the grid
// List(List(Coord(0,2), Coord(0,1), Coord(0,0)),  ...
val allPairsPaths = fromsTos.map(t => allPaths(t._1, t._2)).flatten 
// Generates all words from the pairs path
// List(NAM, NRM, NRC, NAR, NMR, NR, NMO ....
val words = allPairsPaths.map(p => p.map(c => dictMap(c))).map(cs => cs.mkString)
// Combine the two lists, keep only what is in the dictionary
// List((MAN,List(Coord(0,0), Coord(0,1), Coord(0,2))), (CAR,List(Coord(2,0), Coord(2,1), Coord(1,1))) ....
val wordsPath = (words, allPairsPaths).zipped.map((_, _)).filter(t => dict contains t._1)
// Pretty print
wordsPath.foreach(wp => {println(s"Word ${wp._1} has path ${wp._2}")})
//> Word MAN has path List(Coord(0,0), Coord(0,1), Coord(0,2))
//| Word CAR has path List(Coord(2,0), Coord(2,1), Coord(1,1))
//| Word ARM has path List(Coord(0,1), Coord(1,1), Coord(0,0))
//| Word ARM has path List(Coord(0,1), Coord(1,1), Coord(1,2))
//| Word MAN has path List(Coord(1,2), Coord(0,1), Coord(0,2))
//| Word ARM has path List(Coord(2,1), Coord(1,1), Coord(0,0))
//| Word ARM has path List(Coord(2,1), Coord(1,1), Coord(1,2))
There are a few optimizations to do here and there. An interview question not easy to get right on a white board!

Sunday, 1 December 2013

Dijkstra meets Scala meets ScalaFX

A quick post for an naive implementation of Dijkstra in Scala and a visual representation of the solution using FXML and ScalaFX.
I have first defined a graph and a vertex class as follows:
case class Coordinates(val x: Int, val y: Int)

class Vertex(val id: String, val coords: Coordinates) {
  def dist(v: Vertex):Double = dist(v.coords.x, v.coords.y)
  def dist(x: Int, y: Int):Double = Math.sqrt(
    Math.pow(coords.x - x, 2) + Math.pow(coords.y - y, 2))
  override def toString = s"V($id)@$coords"
}

class Graph(val vertices: Seq[Vertex], val neighbours: Map[Vertex, Seq[Vertex]]) {
  def +(v: Vertex):Graph = new Graph(v +: vertices, neighbours) // adds a vertex to the graph
  def ~(v: Vertex, n: Vertex):Graph = { // sets a neighbour relationship between two vertices
    val g = new Graph(vertices, neighbours + (v -> (neighbours(v) :+ n)))
    new Graph(g.vertices, g.neighbours + (n -> (g.neighbours(n) :+ v)))
  }
  def closest(x: Int, y: Int): Vertex = vertices.minBy( v => v.dist(x, y))
  override def toString = s"G(Vertices: $vertices, Neighbours: $neighbours)"
}
Then comes my Scala version of Dijkstra (would be nice to share your version...)
object Dijkstra {
  def run(graph: Graph, source: Vertex, target: Vertex):Seq[Vertex] = {
    val neighbours = graph.neighbours

    def dj2(u: Vertex, vs: Seq[Vertex], distances: Map[Vertex, Double], queue: Seq[Vertex],
            visited: Seq[Vertex], previous: Map[Vertex, Vertex]):
    (Map[Vertex, Double], Seq[Vertex], Seq[Vertex], Map[Vertex, Vertex]) = {
      if (!vs.isEmpty) {
        val v = vs.head
        val alt = distances(u) + u.dist(v)
        if (alt < distances(v) && !visited.contains(v)) {
          dj2(u, vs.tail, distances + (v -> alt), v +: queue, visited, previous + (v -> u))
        } else if (!vs.tail.isEmpty) {
          dj2(u, vs.tail, distances, queue, visited, previous)
        } else (distances, queue, visited, previous)
      } else (distances, queue, visited, previous)
    } // dj2

    def dj1(distances: Map[Vertex, Double], queue: Seq[Vertex], visited: Seq[Vertex], previous: Map[Vertex, Vertex]):Seq[Vertex] = {
      if (!queue.isEmpty) {
        val u:Vertex = queue.filter(v => {!visited.contains(v)}).min(Ordering.by((v:Vertex) => distances(v)))
        if (u == target) {
          sequenceSol(previous, target)
        } else {
          val res = dj2(u, neighbours(u), distances, queue.filterNot(v => v.id == u.id), u +: visited, previous)
          dj1(res._1, res._2, res._3, res._4)
        }
      } else Seq()
    } // dj1

    dj1(Map(source -> 0.0) withDefaultValue Double.MaxValue, Seq(source), Seq(), Map())
  } // dijkstra

  def sequenceSol(previous: Map[Vertex, Vertex], to: Vertex): Seq[Vertex] = {
    def helper(seq: Seq[Vertex], current: Vertex): Seq[Vertex] =
      if (previous.contains(current)) helper(previous(current) +: seq, previous(current)) else seq
    helper(Seq(to), to)
  }
}
I would like to find a simpler version.. really. The FXML is a simple file like this:
<?xml version="1.0" encoding="UTF-8"?>

<?import java.lang.*?>
<?import java.util.*?>
<?import javafx.scene.control.*?>
<?import javafx.scene.layout.*?>
<?import javafx.scene.paint.*?>

<AnchorPane id="AnchorPane" fx:id="masterAnchorPane" maxHeight="-Infinity" maxWidth="-Infinity" minHeight="-Infinity" minWidth="-Infinity" prefHeight="400.0" prefWidth="600.0" xmlns:fx="http://javafx.com/fxml/1" xmlns="http://javafx.com/javafx/2.2" fx:controller="org.jts.dijkstra.DijkstraController">
  <children>
    <BorderPane fx:id="borderPane" prefHeight="400.0" prefWidth="600.0" style="-fx-background-color: #CCFF99" AnchorPane.bottomAnchor="0.0" AnchorPane.leftAnchor="0.0" AnchorPane.rightAnchor="0.0" AnchorPane.topAnchor="0.0">
      <top>
        <FlowPane minHeight="21.0" prefHeight="21.0" prefWidth="600.0">
          <children>
            <TextField fx:id="nbNodes" alignment="CENTER_RIGHT" prefWidth="50.0" promptText="# nodes" text="100" />
            <Button fx:id="applyButton" mnemonicParsing="false" onAction="#handleApply" text="Apply" />
          </children>
        </FlowPane>
      </top>
    </BorderPane>
  </children>
</AnchorPane>
And finally the controller:
package org.jts.dijkstra

import javafx.fxml.{Initializable, FXML}
import javafx.scene.{control => jfxctrl}
import javafx.scene.{layout => jfxlyt}
import javafx.{event => jfxe}
import scalafx.scene.effect.Bloom
import scalafx.scene.paint.Color
import scalafx.scene.{layout => scxlyt}
import scalafx.scene.{control => scxctrl}
import java.net.URL
import javafx.event.EventHandler
import javafx.scene.input.MouseEvent

object Constants {
  val nodeSize = 16
  val width = 1500
  val height = 800
}

class DijkstraController extends Initializable {
  @FXML private var nbNodes: jfxctrl.TextField = null
  @FXML private var applyButton: jfxctrl.Button = null
  @FXML private var borderPane: jfxlyt.BorderPane = null
  @FXML private var masterAnchorPane: jfxlyt.AnchorPane = null

  private var scxMasterPane: scxlyt.AnchorPane = null
  private var scxPane: scxlyt.BorderPane = null
  private var scxNbNodes: scxctrl.TextField = null
  private var scxApplyButton: scxctrl.Button = null
  private var scxCanvas: scalafx.scene.canvas.Canvas = null

  private var fromVertex: Option[Vertex] = None
  private var toVertex: Option[Vertex] = None

  override def initialize(url: URL, rb: java.util.ResourceBundle) {
    require(masterAnchorPane != null, "masterAnchorPane must not be null")
    scxMasterPane = new scxlyt.AnchorPane(masterAnchorPane)

    require(nbNodes != null, "nbNodes must not be null")
    scxNbNodes = new scxctrl.TextField(nbNodes)

    require(applyButton != null, "applyButton must not be null")
    scxApplyButton  = new scxctrl.Button(applyButton)

    require(borderPane != null, "centerPane must not be null")
    scxPane = new scxlyt.BorderPane(borderPane)

    scxCanvas = new scalafx.scene.canvas.Canvas(Constants.width, Constants.height)
    scxPane.setCenter(scxCanvas)
  }

  @FXML private def handleApply(event: jfxe.ActionEvent) {
    // TODO In Future not in EDT
    val maxNodes:Integer = scxNbNodes.getText.toInt
    val width = scxCanvas.width.toInt
    val height = scxCanvas.height.toInt
    println(s"Generating random graph with $maxNodes in ($width, $height)")

    val random = new java.util.Random
    val vertices = for(i <- 0 until maxNodes) yield {
      val x = random.nextInt(width)
      val y = random.nextInt(height)
      val vertex = new Vertex(s"v-$i", Coordinates(x, y))
      vertex
    }
    println(s"Generated vertices $vertices")

    def generateNeighbours(neighbours: Map[Vertex, Seq[Vertex]], nbTimes: Int): Map[Vertex, Seq[Vertex]] = {
      if (nbTimes >= maxNodes) neighbours
      else {
        val v1 = vertices(random.nextInt(maxNodes))
        val v2 = vertices(random.nextInt(maxNodes))
        val n1 = neighbours + (v1 -> (neighbours(v1) :+ v2))
        val n2 = n1 + (v2 -> (n1(v2) :+ v1))
        generateNeighbours(n2, nbTimes + 1)
      }
    }

    val neighbours = generateNeighbours(Map() withDefaultValue Seq(), 0)
    println(s"Generated neighbours $neighbours")

    val graph = new Graph(vertices, neighbours)
    drawGraph(graph)
    addMouseEventHandler(graph)
  }

  private def drawGraph(g: Graph, sol: Seq[Vertex] = Seq()) {
    val gc = scxCanvas.getGraphicsContext2D
    gc.clearRect(0, 0, Constants.width, Constants.height)
    gc.setFill(Color.BLACK)
    gc.fillRect(0, 0, Constants.width, Constants.height)

    gc.setFill(Color.LIGHTYELLOW)
    gc.setEffect(new Bloom())
    g.vertices.foreach(v => {
      gc.fillOval(v.coords.x, v.coords.y, Constants.nodeSize, Constants.nodeSize)
    })

    gc.setEffect(null)
    val offset = Constants.nodeSize / 2
    gc.setStroke(Color.LIGHTBLUE)
    g.neighbours.foreach(t => {
      val from = t._1
      t._2.foreach(to => {
        gc.strokeLine(from.coords.x + offset, from.coords.y + offset, to.coords.x + offset, to.coords.y + offset)
        gc.setLineWidth(1.0)
      })
    })

    if (!sol.isEmpty) {
      gc.setFill(Color.RED)
      gc.setStroke(Color.GREEN)
      gc.setEffect(new Bloom())
      val from = sol(0)
      val to = sol.last
      gc.fillOval(from.coords.x, from.coords.y, Constants.nodeSize, Constants.nodeSize)
      gc.fillOval(to.coords.x, to.coords.y, Constants.nodeSize, Constants.nodeSize)
      val path = sol.sliding(2, 1)

      gc.setLineWidth(5.0)
      while(path.hasNext) {
        val subPath = path.next
        if (subPath.size == 2) {
          val f = subPath(0)
          val t = subPath(1)
          gc.strokeLine(f.coords.x + offset, f.coords.y + offset, t.coords.x + offset, t.coords.y + offset)
        }
      }
    }
  }

  private def addMouseEventHandler(g: Graph) {
    scxCanvas.addEventHandler(MouseEvent.MOUSE_CLICKED, new EventHandler[MouseEvent]() {
      override def handle(me: MouseEvent) = {
        val closestVertex = g.closest(me.getX.toInt, me.getY.toInt)
        //println(s"Original vertex: $closestVertex")
        fromVertex = Some(closestVertex)
      }
    })

    scxCanvas.addEventHandler(MouseEvent.MOUSE_MOVED, new EventHandler[MouseEvent]() {
      override def handle(me: MouseEvent) = {
        val closestVertex = g.closest(me.getX.toInt, me.getY.toInt)
        //println(s"Closest vertex: $closestVertex")
        val oldToVertex = toVertex
        toVertex = Some(closestVertex)
        if (!oldToVertex.equals(toVertex)) runDijkstra(g)
      }
    })
  }

  private def runDijkstra(g: Graph) {
    (fromVertex, toVertex) match {
      case (Some(f), Some(t)) => {
        val sol = Dijkstra.run(g, f, t)
        drawGraph(g, sol)
        println(s"Sol from $f to $t is $sol")
      }
      case _ =>
    }
  }
}
I am still trying to grasp ScalaFX - but looks ok so far. Full source code available on request. Just email me.

Tuesday, 2 July 2013

Scala code snippet: strong typed graph nodes

In the post Scala Lazy Types, I explored the use of abstract types for nodes in a graph. In this post, I will create another version of a node with stronger type constraints on both the parent nodes and the children nodes.
Using the typical DAG for pricing graphs, with Futures, Bonds, Cheapest-To-Deliver and Benchmarks.
This version uses the concept of generalised typed constraints, which is used in the implicit argument to the methods addParent (+^) and addChild (+!).
trait STNode {
  type ParentNodeType <: STNode
  type ChildNodeType <: STNode
    
  var parents: List[ParentNodeType] = List()
  var children: List[ChildNodeType] = List()
    
  def +^ (parent: ParentNodeType)(implicit thisAsChild : this.type <:< parent.ChildNodeType) = {
    parents ::= parent
    parent.children ::= thisAsChild(this)
  }
    
  def +! (child: ChildNodeType)(implicit thisAsParent : this.type <:< child.ParentNodeType) {
    children ::= child
    child.parents ::= thisAsParent(this)
  }
}
Let's now define the abstract bonds and futures pricing nodes:
trait Bond extends STNode {
  override type ChildNodeType = Bond
}

trait Future extends STNode {
  override type ParentNodeType = Nothing
  override type ChildNodeType = Bond
}
Then the real implementations: a Market Future, a Manual Future, a Cheapest-To-Deliver Bond, a Gross-Basis Bond, and a Benchmark Bond:
class FutureMarket extends Future
class FutureManual extends Future
  
class BondGrossBasis extends Bond {
  override type ParentNodeType = Future
}
class BondCheapestToDeliver extends Bond {
  override type ParentNodeType = Future
}
class BondBenchmark extends Bond {
  override type ParentNodeType = Bond
  override type ChildNodeType = Bond
}
And finally some tests:
val future = new FutureMarket
val ctd = new BondCheapestToDeliver
val bench = new BondBenchmark

future +! ctd
bench +^ ctd
Obviously, the following does not compile if you try to add a future as a child to a bond:
bench +! future
with type mismatch; found : investigations.StrongTypedGraph.FutureMarket required: bench.ChildNodeType

Sunday, 2 June 2013

Another Scala code snippet for linearising a graph

This algorithm is more or less related to Topological sorting. It can be useful for computing DAG in parallel.....
  type ListMap = Map[Vertex, List[Vertex]]
  class Vertex {
    var children: List[Vertex] = List()
    def isLeaf = children.isEmpty
  }
  class Graph {
    def roots: List[Vertex] = List()
  }
  
  def lin(g: Graph): ListMap = {
    def dfs(n: Vertex):List[Vertex] = if (n.isLeaf) List() else (for(c <- n.children) yield c :: dfs(c)).flatten
    def shrink(dfsl: List[Vertex]): List[Vertex] = {
      def shrink(il: List[Vertex], ol: List[Vertex]): List[Vertex] =
        if (il.isEmpty) ol
        else if (!ol.contains(il.head)) shrink(il.tail, (ol ++ List(il.head)))
        else shrink(il.tail, ol diff List(il.head)) ::: List(il.head)
      if (dfsl.isEmpty) List() else shrink(dfsl, List())
   }
   (for(r <- g.roots) yield (r, shrink(dfs(r)))).toMap
  } 

Friday, 24 July 2009

Going back to basics

Should you work one day on pricing (art of pricing an instrument using Gross Basis, CTD, ASW, ...), you might need to refresh your memories with respect of Graph Algorithms.
Most (all?) pricing methods use graph (Directed Acyclic Graphs), instrument nodes and dependencies.
The probably two most important ones are breadth-first search and cycle-detection.
More posts will follow on my view on how to implement them.

Blog Archive