-
-
Save Alexander-Mages/5a58c8fa86f7c791e4a413bf82d483df to your computer and use it in GitHub Desktop.
Network Coordinate System built upon latency measurements
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
module Main where | |
import Data.Map.Strict (Map) | |
import qualified Data.Map.Strict as Map | |
import Data.Vector (Vector) | |
import qualified Data.Vector as Vector | |
import System.Random.PCG | |
import qualified Data.List | |
import Control.Monad.IO.Class (MonadIO) | |
import Control.Monad.ST | |
import Data.Maybe | |
import qualified Text.PrettyPrint.Boxes as PB | |
import Data.List.Split | |
import Debug.Trace | |
--elementwise vector addition | |
addvec :: Num a => [a] -> [a] -> [a] | |
addvec = zipWith (+) | |
{-- | |
ghci> addvec [1.00, 2.00, 3.00, 4.00] [1.5, 2.25, 2.2, 3.1] | |
[2.5,4.25,5.2,7.1] | |
--} | |
--vector multiplication by scalar | |
scalevec :: Num b => [b] -> b -> [b] | |
scalevec a b = map (b*) a | |
{-- | |
ghci> scalevec [1,2,3,4] 2 | |
[2,4,6,8] | |
ghci> scalevec [1.5,3,4.5,6] 0.33 | |
[0.495,0.99,1.485,1.98] | |
--} | |
--inverse of vector | |
inversevec :: Num b => [b] -> [b] | |
inversevec = map negate | |
{-- | |
ghci> inversevec [1,2,3,4] | |
[-1,-2,-3,-4] | |
--} | |
--vector length | |
vectorLength :: (Num a, Floating a) => [a] -> a | |
vectorLength v = sqrt (sum (map (^2) v)) | |
{-- Python implementation - input of [1,2,3,4] yields 5.477225575051661 in both implementations: | |
import math | |
def vectorLength(x): | |
sum = 0 | |
for i in x: | |
sum += i ** 2 | |
return math.sqrt(sum) | |
print(vectorLength([1,2,3,4])) | |
--} | |
--vector distance | |
vectorDist :: (Floating a) => [a] -> [a] -> a | |
vectorDist a b = vectorLength(addvec a (inversevec b)) | |
initializeCoordinates :: (Ord k, Enum k, Num k, Fractional a, Num a{--, MonadIO f--}) => Map k [a]--(f a) | |
initializeCoordinates = | |
Map.fromList ( | |
--"zip" combiles elements of two lists into one list of tuples | zip :: [a] -> [b] -> [(a,b)] | |
zip | |
[1..25] --integers as keys | |
-- ^ \/ both must be scaled along with the # of latencies created | |
(Data.List.Split.chunksOf 4 [104.77894154,238.33919377,252.45367644,278.37766934,384.71008782,269.40096048,248.05884127, | |
161.15176188,245.72143539,312.64660111,160.61983815,271.1297967,176.35900936,350.77958234,361.36508799,173.0858584, | |
228.59745757,321.19260165,56.0556473,2.82798332,1.30090254,112.70626275,358.65075476,273.9610812,144.84056226,309.80333946, | |
216.50744441,342.16845113,191.19273557,288.66819552,342.69499616,228.3911599,12.57762972,125.98387394,211.63432099, | |
176.85088916,158.81017924,201.53078909,229.10849974,13.75018585,392.10543235,166.84778414,224.18459433,277.55757048, | |
172.52586474,393.06766417,318.21495336,195.58883417,199.73977603,365.6681926,99.20370618,230.24473535,196.77579436, | |
158.70892277,343.45702303,174.835945,62.41553314,285.14466611,202.11891177,37.23203508,215.40781024,171.73905528, | |
323.77695632,32.05122653,121.73805558,244.13938714,251.20858054,322.35691518,128.09502196,107.06840815,156.13090515, | |
277.43176905,179.32310697,35.33917395,321.10550677,18.71193606,172.64559037,332.46979165,307.96874894,340.15933101, | |
177.860077,232.55026,237.43416997,289.00827598,318.16097787,137.80225252,21.93683246,241.33252292,250.69224404,385.22814389 | |
,307.61169575,372.21634081,79.26690967,308.79284303,123.76160817,258.29228242,250.6541118,188.73274221,306.41515299,334.53449052]) | |
-- ^ externally generated random numbers, breaks into lists of 4 | |
) | |
tupleSymmetry :: Eq a => (a, a) -> (a, a) -> Bool | |
tupleSymmetry (x,y) (a,b) = | |
x == a && y == b || x == b && y == a || x == y | |
--https://www.seas.upenn.edu/~cis194/fall16/lectures/04-typeclasses.html - "The Eq type class - CIS194" | |
filterDuplicateTuples :: Eq a => [(a, a)] -> [(a, a)] | |
--(uncurry (/=)) is the same as (\(x,y) -> x /= y) which is the same as (\(x,y) -> not (x == y)) | |
filterDuplicateTuples = filter (uncurry (/=))--(Data.List.nubBy tupleSymmetry x) | |
-- ^removes elements according to the tupleSymmetry condition | |
initializeLatencies :: (Ord a, Enum a, Num a, Num b, Fractional b) => Map (a, a) b | |
initializeLatencies = | |
Map.fromList ( | |
zip | |
--fills every combination of items in 2 [1..25] int lists. Scaling requires simply changing the 25 below to desired host quantity | |
(filterDuplicateTuples (concat $ zipWith (zip . repeat) [1..25] $ Data.List.tails [1..25])) | |
[149.41354194,243.59380329,177.24150784,186.36276263,333.03393327,200.85748563,67.98841369,309.44333857,348.66974922,129.45020183, | |
183.02087111,228.59545225,23.59666652,375.3592921,49.3705436,9.85847,240.40850286,201.26488938,221.61149351,295.35901272,314.63764057, | |
30.72348358,361.28646832,248.11809401,364.76924684,94.75664369,43.62376443,248.15889674,150.58125689,74.28759738,129.03095266,199.34170366, | |
291.75999045,44.31665462,146.74875086,212.84553908,376.26495041,384.62490536,208.92816645,226.85071108,399.49227936,20.20105293, | |
286.35881647,152.8455184,197.16706575,87.51467611,244.96845135,360.72735572,290.08972007,33.30094473,91.81191041,319.58396521, | |
234.30689619,371.65890374,347.18590105,282.25297766,287.4830073,183.87246121,396.01590515,47.15859617,127.41029002,286.07162161, | |
221.12610366,208.99238503,176.45246581,163.56743329,132.57034669,369.22793421,119.59828468,288.44322525,303.45981877,310.43373323, | |
247.18843291,70.21674003,91.35978023,225.06660119,267.27969887,265.22815066,32.2100996,307.8604453,267.26455645,78.91428425,11.46858911, | |
230.55934762,388.01850674,57.31062621,251.79254894,14.07520731,72.60881871,309.38639103,48.63289075,267.21357975,370.47123994, | |
295.62724737,107.58287533,113.09450654,46.39006002,293.92559649,390.6414875,186.00227458,100.01431592,17.90687409,41.08124832, | |
399.97320781,140.46343055,168.31591392,278.46684145,395.46674375,114.66318193,146.17844239,382.37861264,67.55045513,146.18924028, | |
9.37457435,325.27248975,367.39984832,148.07775854,181.62553064,24.06283789,28.16359844,158.74142679,371.57829045,150.40785955,98.81893459, | |
95.03032354,275.39649942,294.6389606,337.39739557,313.80413484,356.81344118,350.73864361,198.60406381,173.56597639,91.86295344, | |
20.25959388,41.57343878,218.06293996,179.24810889,16.66060747,139.91837141,386.28176255,354.87278516,315.74232624,108.63234158, | |
184.24423728,396.84515776,40.75499,209.26660278,200.67382049,226.28412119,18.45618301,220.40031147,205.21609394,399.62961537, | |
275.13438666,140.75586272,137.16557262,225.71931312,182.91563223,269.83014989,91.52902459,307.87906365,390.63130424,149.95139394, | |
8.51592576,33.49622323,357.51788917,280.41815137,7.99004532,61.33473248,194.30470676,195.86507768,308.83316027,288.31514314, | |
304.20391438,262.6141137,52.60168451,172.40510682,358.32386825,198.49464705,66.15373548,227.05801058,387.21612994,234.2850834,327.89736863 | |
,199.55650813,388.54967074,13.38240296,352.02174931,371.06946338,281.60065517,380.50164902,181.45530563,181.02264977,309.35020968 | |
,53.11249015,115.79773234,276.69993505,125.80994057,368.47559544,57.88799782,191.15869703,1.23774301,8.75514958,264.42254578,152.02821042 | |
,235.84081697,225.22728858,122.75403771,100.1080245,375.65151803,62.33752011,276.10424831,43.52658475,399.54667105,379.10169944, | |
185.95764105,371.54470879,35.67250787,161.87778502,144.87275544,316.752431,33.11338116,22.92051197,212.31914528,288.4181006,152.14700036, | |
88.73306455,311.10047297,345.21986305,30.81842029,202.2776702,265.38272304,215.45769909,113.27907076,192.9488753,102.27205367,250.9069872, | |
394.62476559,60.00611906,321.42281126,376.08474038,1.98763369,265.89319703,173.22924477,82.17838625,153.4459287,211.39983392,23.27697506, | |
359.11263455,265.53913372,375.12072842,126.5848316,204.90510121,194.61802476,252.05432002,388.66144082,260.09133984,301.00543134,109.99826861, | |
28.52107766,378.73786032,146.73785487,61.46690309,240.22446891,202.00092894,269.33675936,369.70750279,264.75744394,318.22099145,241.85353858, | |
258.27141275,374.93885903,139.26236118,340.63224298,108.47619065,252.3721375,184.76765866,216.76468794,376.99863571,279.47495222,356.66137244, | |
275.53241722,359.49060962,393.14102146,195.78354654,141.29436353,208.97605573,162.90234679,58.4245851,87.87878223,310.39452893,238.52648905, | |
173.1783277,139.01944014,289.39347918,372.44136198,115.05629031,286.41297442,228.90367749] | |
) | |
fallibleLookup :: (Ord k, MonadFail m) => k -> Map.Map k a -> m a | |
fallibleLookup k = maybe (fail "fallibleLookup: Key not found") pure . Map.lookup k | |
errdist :: ({--Show a is for debug--}Show a, Num a, Floating a, Ord k) => (Map k [a],Map (k, k) a) -> (k, k) -> a | |
errdist maps latencyid = | |
(Data.Maybe.fromJust (fallibleLookup latencyid (snd maps)) - | |
vectorDist (Data.Maybe.fromJust (fallibleLookup (fst latencyid) (fst maps))) (Data.Maybe.fromJust (fallibleLookup (snd latencyid) (fst maps)))) **2 | |
err :: (Num a, Floating a, Num k, Enum k, Ord k,{--show is for debug.Trace.traceShow--} Show a, Show k) => (Map k [a], Map (k, k) a) -> a | |
err maps = | |
Debug.Trace.traceShow (sum (map (errdist maps) (filterDuplicateTuples (concat $ zipWith (zip . repeat) [1..25] $ Data.List.tails [1..25]))) / 300 ) (sum (map (errdist maps) (filterDuplicateTuples (concat $ zipWith (zip . repeat) [1..25] $ Data.List.tails [1..25]))) / 300 ) | |
-- ^ #of iterations. is static | |
-- ^final error value ^applies the preceding function to all latencies, replacing each item with the result | |
-- maps = (hosts, latencies) | |
normalizeMap :: (Num a, {--show is for debug.Trace.traceShow--}Show a, Ord a, Floating a) => (Map Int [a], Map (Int, Int) a) -> (Map Int [a], Map (Int, Int) a) | |
normalizeMap maps = | |
if err maps - 1000 > 30000 then --This is wrong, > needs to be <, but I want it to run to completion. Error value is increasing | |
--arbitrary cutoff^ | |
maps --concretizes the finished map and returns the tuple | |
else | |
--maps repositionSingleCoordinate to host-pairs, reverses the resulting list of maps, left-bias union consolidates the changes, map is recursively normalized until err condition is met | |
Debug.Trace.trace "normalizing" (normalizeMap (Map.unions (reverse (map (repositionSingleCoordinate maps) (filterDuplicateTuples (concat $ zipWith (zip . repeat) [1..25] $ Data.List.tails [1..25])))), snd maps)) | |
repositionSingleCoordinate :: (Num a, Floating a, Show a) => (Map Int [a], Map (Int, Int) a) -> (Int, Int) -> Map Int [a] | |
repositionSingleCoordinate maps latencyid = | |
Map.insert (fst latencyid) ( | |
addvec | |
(Data.Maybe.fromJust (fallibleLookup (fst latencyid) (fst maps))) --source | |
(scalevec | |
(addvec | |
[100, 100, 100, 100] --arbitrary, shouldn't matter | |
(scalevec | |
(addvec (Data.Maybe.fromJust (fallibleLookup (fst latencyid) (fst maps))) (inversevec (Data.Maybe.fromJust (fallibleLookup (snd latencyid) (fst maps))))) | |
((Data.Maybe.fromJust (fallibleLookup latencyid (snd maps)) - vectorLength (addvec (Data.Maybe.fromJust (fallibleLookup (fst latencyid) (fst maps))) (inversevec (Data.Maybe.fromJust (fallibleLookup (snd latencyid) (fst maps)))))) / | |
vectorLength (addvec (Data.Maybe.fromJust (fallibleLookup (fst latencyid) (fst maps))) (inversevec (Data.Maybe.fromJust (fallibleLookup (snd latencyid) (fst maps)))))) | |
)) | |
0.002) --scaling factor | |
) (fst maps) --map to insert into | |
class Pretty a where | |
ppr :: a -> PB.Box | |
instance Pretty String where | |
ppr = PB.text | |
instance Pretty Int where | |
ppr = PB.text . show | |
instance Pretty Float where | |
ppr = PB.text . show | |
instance Pretty [Float] where | |
ppr = PB.text . show | |
instance Pretty [(Int, Int)] where | |
ppr = PB.text . show | |
instance Pretty [Int] where | |
ppr = PB.text . show | |
col :: (Pretty a, Pretty t) => (t, [a]) -> PB.Box | |
col (a, xs) = PB.vcat PB.left $ lab ++ vals | |
where | |
lab = [ppr a] | |
vals = fmap ppr xs | |
formatCoordinates :: (Map Int [Float], Map (Int, Int) Float) -> String | |
formatCoordinates maps = PB.render $ PB.hsep 1 PB.left $ fmap col cols | |
where | |
cols :: [([Int], [[Float]])] | |
cols = [ | |
(Map.keys (fst maps), Map.elems (fst maps))] | |
formatLatencies :: (Map Int [Float], Map (Int, Int) Float) -> String | |
formatLatencies maps = PB.render $ PB.hsep 1 PB.left $ fmap col cols | |
where | |
cols :: [([(Int, Int)], [Float])] | |
cols = [ | |
(Map.keys (snd maps), Map.elems (snd maps))] | |
main :: IO () --(Random a, Num a, Ord a, Floating a) => (Map Int [a], Map (Int, Int) a) | |
main = | |
let | |
a = normalizeMap (initializeCoordinates, initializeLatencies) | |
b = formatCoordinates a | |
c = formatLatencies a | |
in | |
putStrLn (b ++ c) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment