GSoC 2019: Tcheke graf pou transfòmatè bipati ak monad

Ete pase a mwen te patisipe nan Google Ete nan Kòd - yon pwogram pou elèv ki soti nan Google. Chak ane, òganizatè yo chwazi plizyè pwojè Open Source, ki gen ladan nan òganizasyon sa yo byen li te ye tankou Boost.org и Fondasyon an Linux. Google envite elèv ki soti toupatou nan mond lan pou yo travay sou pwojè sa yo. 

Kòm yon patisipan nan Google Summer of Code 2019, mwen te fè yon pwojè nan bibliyotèk la Alg ak òganizasyon an Haskell.org, ki ap devlope lang Haskell - youn nan lang ki pi popilè pwogramasyon fonksyonèl. Alga se yon bibliyotèk ki reprezante tape san danje reprezantasyon pou graf nan Haskell. Li se itilize, pou egzanp, nan semantik — yon bibliyotèk Github ki bati pye bwa semantik, apèl ak graf depandans ki baze sou kòd epi ki ka konpare yo. Pwojè mwen an se te ajoute yon reprezantasyon ki san danje pou graf bipati ak algoritm pou reprezantasyon sa a. 

Nan pòs sa a mwen pral pale sou aplikasyon mwen an nan yon algorithm pou tcheke yon graf pou bipartiteness nan Haskell. Menm si algorithm la se youn nan pi fondamantal la, mete ann aplikasyon li trè byen nan yon style fonksyonèl te pran m 'plizyè iterasyon e li te mande anpil travay. Kòm yon rezilta, mwen rezoud sou yon aplikasyon ak transfòmatè monad. 

GSoC 2019: Tcheke graf pou transfòmatè bipati ak monad

Sou tèt mwen

Non mwen se Vasily Alferov, mwen se yon etidyan katriyèm ane nan St. Petersburg HSE. Byen bonè nan blog la mwen te ekri sou pwojè mwen an sou algoritm paramèt и sou vwayaj la nan ZuriHac. Kounye a mwen fè yon estaj nan Inivèsite Bergen nan Nòvèj, kote mwen ap travay sou apwòch pwoblèm nan Lis koloran. Enterè mwen gen ladan algoritm paramèt ak pwogramasyon fonksyonèl.

Konsènan aplikasyon algorithm la

Avètisman

Nou ankouraje elèv k ap patisipe nan pwogram nan pou yo blog. Yo te ban mwen yon platfòm pou blog la Pandan ete Haskell. Atik sa a se yon tradiksyon atik, ekri pa m 'la an Jiyè nan lang angle, ak yon prefas kout. 

Ou ka jwenn Pull Request ak kòd nan kesyon an isit la.

Ou ka li sou rezilta travay mwen an (an angle) isit la.

Pòs sa a sipoze ke lektè a abitye ak konsèp debaz yo nan pwogramasyon fonksyonèl, byenke mwen pral eseye sonje tout tèm yo itilize lè lè a rive.

Tcheke graf pou bipatiteness 

Yon algorithm pou tcheke yon graf pou bipartiteness anjeneral yo bay nan yon kou sou algoritm kòm youn nan algorithm graf ki pi senp. Lide li se senp: premye yon jan kanmenm mete somè nan pati gòch la oswa dwa, epi lè yo jwenn yon kwen konfli, nou afime ke graf la pa bipati.

Yon ti kras plis detay: premye nou mete kèk somè nan pataje gòch la. Li evidan, tout vwazen yo nan somè sa a dwe kouche nan lòb dwat la. Pli lwen, tout vwazen vwazen yo nan somè sa a dwe kouche nan lòb gòch la, ak sou sa. Nou kontinye plase aksyon nan somè toutotan gen toujou somè nan eleman ki konekte nan somè nou te kòmanse avèk ke nou pa te asiyen vwazen yo. Lè sa a, nou repete aksyon sa a pou tout eleman ki konekte.

Si gen yon kwen ant somè ki tonbe nan menm patisyon an, li pa difisil pou jwenn yon sik enpè nan graf la, ki se lajman li te ye (ak byen evidamman) enposib nan yon graf inite. Sinon, nou gen yon patisyon kòrèk, ki vle di graf la se toule de pati.

Tipikman, algorithm sa a aplike lè l sèvi avèk lajè premye rechèch oswa pwofondè premye rechèch. Nan lang enperatif, rechèch pwofondè-premye anjeneral yo itilize paske li se yon ti kras pi senp epi li pa mande pou estrikti done adisyonèl. Mwen menm mwen te chwazi rechèch pwofondè-premye kòm li se pi tradisyonèl.

Kidonk, nou rive nan konplo sa a. Nou travèse somè graf la lè l sèvi avèk rechèch pwofondè-premye epi bay yo aksyon, chanje kantite pataje a pandan n ap deplase sou kwen an. Si nou eseye bay yon pati nan yon somè ki deja gen yon pataje asiyen, nou ka di san danje ke graf la pa bipati. Moman tout somè yo asiyen yon pati epi nou te gade tout bor yo, nou gen yon bon patisyon.

Pite nan kalkil

Nan Haskell nou sipoze ke tout kalkil yo pwòp. Sepandan, si sa a te vrèman ka a, nou pa ta gen okenn fason yo enprime anyen sou ekran an. Nan tout, pwòp kalkil yo tèlman parese ke pa gen youn pwòp rezon pou kalkile yon bagay. Tout kalkil ki fèt nan pwogram nan yo yon jan kanmenm fòse nan "enpwòp" monad IO.

Monad yo se yon fason yo reprezante kalkil ak efè nan Haskell. Eksplike ki jan yo travay pi lwen pase sijè ki abòde lan nan pòs sa a. Ou ka li yon bon deskripsyon klè nan lang angle isit la.

Isit la mwen vle fè remake ke pandan ke kèk monad, tankou IO, yo aplike nan majik du, prèske tout lòt yo aplike nan lojisyèl ak tout kalkil nan yo se pi bon kalite.

Gen yon anpil nan efè ak chak gen pwòp monad pa yo. Sa a se yon teyori trè fò ak bèl: tout monad aplike koòdone nan menm. Nou pral pale sou twa monad sa yo:

  • Swa ea se yon kalkil ki retounen yon valè nan kalite a oswa voye yon eksepsyon nan kalite e. Konpòtman monad sa a se trè menm jan ak manyen eksepsyon nan lang enperatif: erè yo ka kenbe oswa pase sou. Diferans prensipal la se ke monad la se konplètman lojikman aplike nan bibliyotèk la estanda nan Haskell, pandan y ap lang enperatif anjeneral itilize mekanis sistèm opere.
  • Eta sa a se yon kalkil ki retounen yon valè kalite a epi ki gen aksè a eta ki ka chanje kalite s.
  • Petèt yon. Monad Petèt la eksprime yon kalkil ki ka entèwonp nenpòt ki lè lè yo pa retounen Anyen. Sepandan, nou pral pale sou aplikasyon an nan klas MonadPlus pou kalite a Petèt, ki eksprime efè opoze a: li se yon kalkil ki ka entèwonp nan nenpòt ki lè pa retounen yon valè espesifik.

Aplikasyon algorithm la

Nou gen de kalite done, Graph a ak Bigraph ab, premye nan yo ki reprezante graf ki gen somè ki make ak valè kalite a, epi dezyèm lan reprezante graf inite ak somè bò gòch ki make ak valè ki gen kalite a ak dwa. -bò somè ki make ak valè kalite b.

Sa yo pa kalite ki soti nan bibliyotèk Alga. Alga pa gen yon reprezantasyon pou graf bipati san direksyon. Mwen te fè kalite yo tankou sa a pou klè.

Nou pral bezwen tou fonksyon asistan ak siyati sa yo:

-- Список соседей данной вершины.
neighbours :: Ord a => a -> Graph a -> [a]

-- Построить двудольный граф по графу и функции, для каждой вершины
-- выдающей её долю и пометку в новой доле, игнорируя конфликтные рёбра.
toBipartiteWith :: (Ord a, Ord b, Ord c) => (a -> Either b c)
                                         -> Graph a
                                         -> Bigraph b c

-- Список вершин в графе
vertexList :: Ord a => Graph a -> [a]
Сигнатура функции, которую мы будем писать, выглядит так:

type OddCycle a = [a]
detectParts :: Ord a => Graph a -> Either (OddCycle a) (Bigraph a a)

Li fasil pou wè ke si pandan rechèch la pwofondè-premye nou jwenn yon kwen konfli, sik la enpè kouche sou tèt pil rekursion la. Kidonk, pou retabli li, nou bezwen koupe tout bagay soti nan pil rekursion jiska premye ensidan an nan dènye somè an.

Nou aplike rechèch pwofondè-premye lè nou kenbe yon seri asosyasyon nimewo pataje pou chak somè. Pile repetisyon an pral otomatikman konsève atravè aplikasyon klas Functor nan monad nou te chwazi a: nou pral sèlman bezwen mete tout somè yo soti nan chemen an nan rezilta a retounen nan fonksyon an recursive.

Premye lide m 'te sèvi ak Monad la Swa, ki sanble aplike egzakteman efè yo ke nou bezwen. Premye aplikasyon mwen te ekri a te trè pre opsyon sa a. An reyalite, mwen te gen senk aplikasyon diferan nan yon pwen epi evantyèlman rezoud sou yon lòt.

Premyèman, nou bezwen kenbe yon seri asosyasyon idantifyan pataje - sa a se yon bagay sou Leta. Dezyenmman, nou bezwen kapab aret ler en konfli i ganny detekte. Sa a ka swa Monad pou Swa, oswa MonadPlus pou Petèt. Diferans prensipal la se ke Swa ka retounen yon valè si kalkil la pa te sispann, epi Petèt retounen sèlman enfòmasyon sou sa a nan ka sa a. Depi nou pa bezwen yon valè separe pou siksè (li deja estoke nan Eta), nou chwazi Petèt. Ak nan moman sa a lè nou bezwen konbine efè yo nan de monad, yo soti transfòmatè monad, ki jisteman konbine efè sa yo.

Poukisa mwen te chwazi yon kalite konplèks konsa? De rezon. Premyèman, aplikasyon an sanble trè sanble ak enperatif. Dezyèmman, nou bezwen manipile valè a retounen nan ka ta gen konfli lè retounen tounen soti nan rekursion retabli bouk la enpè, ki se pi fasil fè nan Monad la Petèt.

Se konsa nou jwenn aplikasyon sa a.

{-# LANGUAGE ExplicitForAll #-}
{-# LANGUAGE ScopedTypeVariables #-}

data Part = LeftPart | RightPart

otherPart :: Part -> Part
otherPart LeftPart  = RightPart
otherPart RightPart = LeftPart

type PartMap a = Map.Map a Part
type OddCycle a = [a]

toEither :: Ord a => PartMap a -> a -> Either a a
toEither m v = case fromJust (v `Map.lookup` m) of
                    LeftPart  -> Left  v
                    RightPart -> Right v

type PartMonad a = MaybeT (State (PartMap a)) [a]

detectParts :: forall a. Ord a => Graph a -> Either (OddCycle a) (Bigraph a a)
detectParts g = case runState (runMaybeT dfs) Map.empty of
                     (Just c, _)  -> Left  $ oddCycle c
                     (Nothing, m) -> Right $ toBipartiteWith (toEither m) g
    where
        inVertex :: Part -> a -> PartMonad a
        inVertex p v = ((:) v) <$> do modify $ Map.insert v p
                                      let q = otherPart p
                                      msum [ onEdge q u | u <- neigbours v g ]

        {-# INLINE onEdge #-}
        onEdge :: Part -> a -> PartMonad a
        onEdge p v = do m <- get
                        case v `Map.lookup` m of
                             Nothing -> inVertex p v
                             Just q  -> do guard (q /= p)
                                           return [v]

        processVertex :: a -> PartMonad a
        processVertex v = do m <- get
                             guard (v `Map.notMember` m)
                             inVertex LeftPart v

        dfs :: PartMonad a
        dfs = msum [ processVertex v | v <- vertexList g ]

        oddCycle :: [a] -> [a]
        oddCycle c = tail (dropWhile ((/=) last c) c)

Blòk kote a se nwayo algorithm la. Mwen pral eseye eksplike sa k ap pase andedan li.

  • inVertex se yon pati nan rechèch pwofondè-premye kote nou vizite somè a pou premye fwa. Isit la nou bay yon nimewo pataje nan somè a epi kouri onEdge sou tout vwazen yo. Sa a se tou kote nou retabli pil apèl la: si msum te retounen yon valè, nou pouse somè v la.
  • onEdge se pati kote nou vizite kwen an. Li rele de fwa pou chak kwen. Isit la nou tcheke si somè lòt bò a te vizite, epi vizite li si se pa sa. Si vizite, nou tcheke si kwen an konfli. Si li se, nou retounen valè a - tèt la anpil nan pil rekursion la, kote tout lòt somè yo pral Lè sa a, dwe mete sou retounen.
  • processVertex tcheke pou chak somè si wi ou non li te vizite epi kouri inVertex sou li si se pa sa.
  • dfs kouri processVertex sou tout somè.

Se tout.

Istwa mo INLINE

Mo INLINE pa t nan premye aplikasyon algorithm la; li te parèt pita. Lè mwen te eseye jwenn yon pi bon aplikasyon, mwen te jwenn ke vèsyon an ki pa-INLINE te notables pi dousman sou kèk graf. Lè nou konsidere ke semantik fonksyon yo ta dwe travay menm jan an, sa a anpil etone m '. Menm moun lòt nasyon, sou yon lòt machin ak yon vèsyon diferan nan GHC pa te gen okenn diferans aparan.

Apre mwen te pase yon semèn li pwodiksyon GHC Nwayo a, mwen te kapab ranje pwoblèm nan ak yon liy nan INLINE eksplisit. Nan kèk pwen ant GHC 8.4.4 ak GHC 8.6.5 optimisateur la sispann fè sa poukont li.

Mwen pa t espere rankontre salte sa yo nan pwogram Haskell. Sepandan, menm jodi a, optimiseurs pafwa fè erè, epi li nan travay nou yo ba yo sijesyon. Pou egzanp, isit la nou konnen ke fonksyon an ta dwe inline paske li se inline nan vèsyon an enperatif, e sa a se yon rezon ki fè yo bay konpilatè a yon allusion.

Kisa ki te pase apre?

Lè sa a, mwen aplike algorithm Hopcroft-Karp ak lòt monad, e se te fen pwogram nan.

Gras a Google Summer of Code, mwen te genyen eksperyans pratik nan pwogram fonksyonèl, ki non sèlman te ede m jwenn yon estaj nan Jane Street ete annapre a (mwen pa sèten ki jan kote sa a byen li te ye menm nan mitan odyans ki gen konesans Habr, men li se youn. nan kèk kote ou ka ete angaje yo nan pwogramasyon fonksyonèl), men tou, prezante m 'nan mond lan bèl bagay nan aplike paradigm sa a nan pratik, siyifikativman diferan de eksperyans mwen nan lang tradisyonèl yo.

Sous: www.habr.com

Add nouvo kòmantè