I wrote this code in Haskell (instead of Python) for the educational benefit. Can anyone suggest ways to improve this code?
I'm guessing that I'm using fromIntegral
inefficiently.
It takes two commandline arguments. The first is a path to a symmetric distance matrix. The second is a threshold. The program interprets vertices to be adjacent if their distance is less than the threshold. Then the program counts the number of connected components and the number of vertices in each connected component and prints this information.
import System.Environment
import Data.Matrix hiding (flatten)
import qualified Data.Vector as V
import Data.Graph
import Data.Tree
-- Turns a distance matrix to an adjacency matrix using a threshold, then prints the number
-- and size of the connected components.
-- Usage: run `stack run location_of_distance_matrix threshold`
-- Output is in the form (number of bins, [number of vertices in each bin]).
main :: IO ()
main = do
args <- getArgs
contents <- readFile $ args !! 0
let dmat = fromLists $ (map ((map (read :: String -> Float)) . words) (lines contents))
amat = amatFromDmat dmat $ read (args !! 1)
(g,_,_) = graphFromEdges (map (\n -> (n, n, neighbours n amat)) [(1 :: Integer)..(fromIntegral $ ncols amat)])
comp = components g
putStrLn $ show $ (length comp, map (length . flatten) comp)
-- Transforms a distance matrix into an adjacency matrix using a threshold.
amatFromDmat :: Matrix Float -> Float -> Matrix Bool
amatFromDmat m e = matrix (nrows m) (ncols m) threshold
where threshold (i,j)
| i == j = False
| m ! (i,j) < e = True
| otherwise = False
-- Outputs the list of neighbours of a vertex in a graph, taking an adjacency
-- matrix.
-- The addition and subtraction of 1 are here because vectors are 0-indexed but
-- I made my graph vertices 1-indexed.
neighbours :: Integer -> Matrix Bool -> [Integer]
neighbours n mat = map (fromIntegral . (1+)) $ filter (\m -> row V.! m) [0..(ncols mat)-1]
where row = getRow (fromIntegral n) mat
Edit: I found a bug and improved the code a little bit.