From 9f5a692067e435c00f46da909bb0da6f0c481943 Mon Sep 17 00:00:00 2001 From: Sridhar Ratnakumar Date: Tue, 23 Jun 2020 13:40:23 -0400 Subject: [PATCH 1/4] Remove cycle warning from z-index --- neuron/src/lib/Neuron/Web/ZIndex.hs | 13 ++----------- 1 file changed, 2 insertions(+), 11 deletions(-) diff --git a/neuron/src/lib/Neuron/Web/ZIndex.hs b/neuron/src/lib/Neuron/Web/ZIndex.hs index a7fbaba87..1bec6bb90 100644 --- a/neuron/src/lib/Neuron/Web/ZIndex.hs +++ b/neuron/src/lib/Neuron/Web/ZIndex.hs @@ -36,10 +36,7 @@ import Relude hiding ((&)) -- All heavy graph computations are decoupled from rendering, producing this -- value, that is in turn used for instant rendering. data ZIndex = ZIndex - { -- | Topological ordering of the folgezettel graph. Left value indicates - -- that it is cyclic. - zIndexTopSort :: Either (NonEmpty Zettel) [Zettel], - -- | Clusters on the folgezettel graph. + { -- | Clusters on the folgezettel graph. zIndexClusters :: [Forest (Zettel, [Zettel])], -- | All zettel errors zIndexErrors :: Map ZettelID ZettelError @@ -52,7 +49,7 @@ buildZIndex graph errors = flip fmap clusters $ \(zs :: [Tree Zettel]) -> G.backlinksMulti Folgezettel zs graph topSort = G.topSort graph - in ZIndex topSort (fmap sortCluster clusters') errors + in ZIndex (fmap sortCluster clusters') errors where -- TODO: Either optimize or get rid of this (or normalize the sorting somehow) sortCluster fs = @@ -70,12 +67,6 @@ renderZIndex (Theme.semanticColor -> themeColor) ZIndex {..} = do elClass "h1" "header" $ text "Zettel Index" renderErrors zIndexErrors divClass "z-index" $ do - case zIndexTopSort of - Left (toList -> cyc) -> divClass "ui orange segment" $ do - el "h2" $ text "Cycle detected" - forM_ cyc $ \zettel -> - el "li" $ QueryView.renderZettelLink Nothing def zettel - _ -> blank el "p" $ do text $ "There " <> countNounBe "cluster" "clusters" (length zIndexClusters) <> " in the Zettelkasten folgezettel graph. " text "Each cluster's " From 6eb95abf58204ed7a749706e16274c25a9d6d6ae Mon Sep 17 00:00:00 2001 From: Sridhar Ratnakumar Date: Tue, 23 Jun 2020 14:08:31 -0400 Subject: [PATCH 2/4] Display zettelkasten stats in z-index --- neuron/neuron.cabal | 2 +- neuron/src/lib/Data/Graph/Labelled.hs | 1 + .../src/lib/Data/Graph/Labelled/Algorithm.hs | 4 ++++ neuron/src/lib/Neuron/Web/ZIndex.hs | 24 ++++++++++++++----- neuron/src/lib/Neuron/Zettelkasten/Graph.hs | 5 ++++ 5 files changed, 29 insertions(+), 7 deletions(-) diff --git a/neuron/neuron.cabal b/neuron/neuron.cabal index aefa92a04..c600eb2a7 100644 --- a/neuron/neuron.cabal +++ b/neuron/neuron.cabal @@ -1,7 +1,7 @@ cabal-version: 2.4 name: neuron -- This version must be in sync with what's in Default.dhall -version: 0.5.6.3 +version: 0.5.7 license: AGPL-3.0-only copyright: 2020 Sridhar Ratnakumar maintainer: srid@srid.ca diff --git a/neuron/src/lib/Data/Graph/Labelled.hs b/neuron/src/lib/Data/Graph/Labelled.hs index b99723e63..96fca55e8 100644 --- a/neuron/src/lib/Data/Graph/Labelled.hs +++ b/neuron/src/lib/Data/Graph/Labelled.hs @@ -9,6 +9,7 @@ module Data.Graph.Labelled mkGraphFrom, -- * Querying + getGraph, findVertex, getVertices, hasEdge, diff --git a/neuron/src/lib/Data/Graph/Labelled/Algorithm.hs b/neuron/src/lib/Data/Graph/Labelled/Algorithm.hs index 1882a6c45..f131ec08d 100644 --- a/neuron/src/lib/Data/Graph/Labelled/Algorithm.hs +++ b/neuron/src/lib/Data/Graph/Labelled/Algorithm.hs @@ -16,6 +16,10 @@ import qualified Data.Set as Set import Data.Tree (Forest, Tree (..)) import Relude +{-# INLINE getGraph #-} +getGraph :: LabelledGraph v e -> LAM.AdjacencyMap e (VertexID v) +getGraph (LabelledGraph g _) = g + findVertex :: Ord (VertexID v) => VertexID v -> LabelledGraph v e -> Maybe v findVertex v lg@(LabelledGraph g _) = do guard $ LAM.hasVertex v g diff --git a/neuron/src/lib/Neuron/Web/ZIndex.hs b/neuron/src/lib/Neuron/Web/ZIndex.hs index 1bec6bb90..af44ef1d4 100644 --- a/neuron/src/lib/Neuron/Web/ZIndex.hs +++ b/neuron/src/lib/Neuron/Web/ZIndex.hs @@ -39,17 +39,24 @@ data ZIndex = ZIndex { -- | Clusters on the folgezettel graph. zIndexClusters :: [Forest (Zettel, [Zettel])], -- | All zettel errors - zIndexErrors :: Map ZettelID ZettelError + zIndexErrors :: Map ZettelID ZettelError, + zIndexStats :: Stats } +data Stats = Stats + { statsZettelCount :: Int, + statsZettelConnectionCount :: Int + } + deriving (Eq, Show) + buildZIndex :: ZettelGraph -> Map ZettelID ZettelError -> ZIndex buildZIndex graph errors = let clusters = G.categoryClusters graph clusters' :: [Forest (Zettel, [Zettel])] = flip fmap clusters $ \(zs :: [Tree Zettel]) -> G.backlinksMulti Folgezettel zs graph - topSort = G.topSort graph - in ZIndex (fmap sortCluster clusters') errors + stats = Stats (length $ G.getZettels graph) (G.connectionCount graph) + in ZIndex (fmap sortCluster clusters') errors stats where -- TODO: Either optimize or get rid of this (or normalize the sorting somehow) sortCluster fs = @@ -68,7 +75,12 @@ renderZIndex (Theme.semanticColor -> themeColor) ZIndex {..} = do renderErrors zIndexErrors divClass "z-index" $ do el "p" $ do - text $ "There " <> countNounBe "cluster" "clusters" (length zIndexClusters) <> " in the Zettelkasten folgezettel graph. " + text $ + "The zettelkasten has " + <> countNounBe "zettel" "zettels" (statsZettelCount zIndexStats) + <> " and " + <> countNounBe "link" "links" (statsZettelConnectionCount zIndexStats) + text $ ". It has " <> countNounBe "cluster" "clusters" (length zIndexClusters) <> " in its folgezettel graph. " text "Each cluster's " elAttr "a" ("href" =: "https://neuron.zettel.page/2017401.html") $ text "folgezettel heterarchy" text " is rendered as a forest." @@ -77,8 +89,8 @@ renderZIndex (Theme.semanticColor -> themeColor) ZIndex {..} = do el "ul" $ renderForest forest where countNounBe noun nounPlural = \case - 1 -> "is 1 " <> noun - n -> "are " <> show n <> " " <> nounPlural + 1 -> show 1 <> noun + n -> show n <> " " <> nounPlural renderErrors :: DomBuilder t m => Map ZettelID ZettelError -> NeuronWebT t m () renderErrors errors = do diff --git a/neuron/src/lib/Neuron/Zettelkasten/Graph.hs b/neuron/src/lib/Neuron/Zettelkasten/Graph.hs index 102891fa9..22bdf4114 100644 --- a/neuron/src/lib/Neuron/Zettelkasten/Graph.hs +++ b/neuron/src/lib/Neuron/Zettelkasten/Graph.hs @@ -21,9 +21,11 @@ module Neuron.Zettelkasten.Graph backlinksMulti, clusters, categoryClusters, + connectionCount, ) where +import qualified Algebra.Graph.Labelled.AdjacencyMap as LAM import Data.Default import Data.Foldable (maximum) import qualified Data.Graph.Labelled as G @@ -96,3 +98,6 @@ getZettel = G.findVertex -- | If no connection exists, this returns Nothing. getConnection :: Zettel -> Zettel -> ZettelGraph -> Maybe Connection getConnection z1 z2 g = fmap (fromMaybe def) $ G.edgeLabel g z1 z2 + +connectionCount :: ZettelGraph -> Int +connectionCount = LAM.edgeCount . G.getGraph From e1cbfcaab376acc4491ddcc8dfafabc6f1372abf Mon Sep 17 00:00:00 2001 From: Sridhar Ratnakumar Date: Tue, 23 Jun 2020 16:11:44 -0400 Subject: [PATCH 3/4] Switch to 'BFS forest' algorithm --- neuron/neuron.cabal | 2 +- neuron/src/lib/Data/Graph/Labelled.hs | 2 ++ neuron/src/lib/Data/Graph/Labelled/Algorithm.hs | 8 ++++++++ neuron/src/lib/Neuron/Zettelkasten/Graph.hs | 6 +++--- neuron/test/Neuron/VersionSpec.hs | 10 +++++----- 5 files changed, 19 insertions(+), 9 deletions(-) diff --git a/neuron/neuron.cabal b/neuron/neuron.cabal index c600eb2a7..409c658e5 100644 --- a/neuron/neuron.cabal +++ b/neuron/neuron.cabal @@ -1,7 +1,7 @@ cabal-version: 2.4 name: neuron -- This version must be in sync with what's in Default.dhall -version: 0.5.7 +version: 0.5.7.0 license: AGPL-3.0-only copyright: 2020 Sridhar Ratnakumar maintainer: srid@srid.ca diff --git a/neuron/src/lib/Data/Graph/Labelled.hs b/neuron/src/lib/Data/Graph/Labelled.hs index 96fca55e8..e28503907 100644 --- a/neuron/src/lib/Data/Graph/Labelled.hs +++ b/neuron/src/lib/Data/Graph/Labelled.hs @@ -23,6 +23,8 @@ module Data.Graph.Labelled clusters, dfsForestFrom, dfsForestBackwards, + bfsForestFrom, + bfsForestBackwards, obviateRootUnlessForest, induceOnEdge, ) diff --git a/neuron/src/lib/Data/Graph/Labelled/Algorithm.hs b/neuron/src/lib/Data/Graph/Labelled/Algorithm.hs index f131ec08d..5a9b38874 100644 --- a/neuron/src/lib/Data/Graph/Labelled/Algorithm.hs +++ b/neuron/src/lib/Data/Graph/Labelled/Algorithm.hs @@ -92,6 +92,14 @@ dfsForestBackwards :: (Monoid e, Vertex v, Ord (VertexID v)) => v -> LabelledGra dfsForestBackwards fromV (LabelledGraph g' v') = dfsForestFrom [fromV] $ LabelledGraph (LAM.transpose g') v' +bfsForestBackwards :: (Monoid e, Vertex v, Ord (VertexID v)) => v -> LabelledGraph v e -> Forest v +bfsForestBackwards fromV (LabelledGraph g' v') = + bfsForestFrom [fromV] $ LabelledGraph (LAM.transpose g') v' + +bfsForestFrom :: (Vertex v, Ord (VertexID v)) => [v] -> LabelledGraph v e -> Forest v +bfsForestFrom (fmap vertexID -> vs) g = + fmap (fmap $ getVertex g) $ Algo.bfsForest vs $ LAM.skeleton $ graph g + -------------------------- --- More general utilities -------------------------- diff --git a/neuron/src/lib/Neuron/Zettelkasten/Graph.hs b/neuron/src/lib/Neuron/Zettelkasten/Graph.hs index 22bdf4114..5d44c613a 100644 --- a/neuron/src/lib/Neuron/Zettelkasten/Graph.hs +++ b/neuron/src/lib/Neuron/Zettelkasten/Graph.hs @@ -39,13 +39,13 @@ import Relude frontlinkForest :: Connection -> Zettel -> ZettelGraph -> Forest Zettel frontlinkForest conn z = G.obviateRootUnlessForest z - . G.dfsForestFrom [z] + . G.bfsForestFrom [z] . G.induceOnEdge (== Just conn) backlinkForest :: Connection -> Zettel -> ZettelGraph -> Forest Zettel backlinkForest conn z = G.obviateRootUnlessForest z - . G.dfsForestBackwards z + . G.bfsForestBackwards z . G.induceOnEdge (== Just conn) backlinks :: Connection -> Zettel -> ZettelGraph -> [Zettel] @@ -69,7 +69,7 @@ backlinksMulti conn zs g = categoryClusters :: ZettelGraph -> [Forest Zettel] categoryClusters (categoryGraph -> g) = let cs :: [[Zettel]] = sortMothers $ clusters g - in flip fmap cs $ \zs -> G.dfsForestFrom zs g + in flip fmap cs $ \zs -> G.bfsForestFrom zs g where -- Sort clusters with newer mother zettels appearing first. sortMothers :: [NonEmpty Zettel] -> [[Zettel]] diff --git a/neuron/test/Neuron/VersionSpec.hs b/neuron/test/Neuron/VersionSpec.hs index 0d1d5cefd..fff15e6b5 100644 --- a/neuron/test/Neuron/VersionSpec.hs +++ b/neuron/test/Neuron/VersionSpec.hs @@ -28,10 +28,10 @@ spec = do "0.4" `isLesserOrEqual` olderThan it "full versions" $ do "0.6.1.2" `isGreater` olderThan - "0.5.8" `isGreater` olderThan - "0.5.6.8" `isGreater` olderThan - "0.5.6.0" `isLesserOrEqual` olderThan -- This is current version + "0.5.9" `isGreater` olderThan + "0.5.7.8" `isGreater` olderThan + "0.5.7.0" `isLesserOrEqual` olderThan -- This is current version "0.3.1.0" `isLesserOrEqual` olderThan it "within same major version" $ do - "0.5.6.8" `isGreater` olderThan - "0.5.6.0" `isLesserOrEqual` olderThan -- This is current version + "0.5.7.8" `isGreater` olderThan + "0.5.7.0" `isLesserOrEqual` olderThan -- This is current version From 2cc46de84781f66ef44e4609c46beb99cdc1f4e3 Mon Sep 17 00:00:00 2001 From: Sridhar Ratnakumar Date: Tue, 23 Jun 2020 16:27:19 -0400 Subject: [PATCH 4/4] Update documentation --- guide/2011503.md | 9 ++++++--- guide/2011504.md | 17 +++++++++++++---- guide/index.md | 2 +- neuron/src/lib/Neuron/Web/ZIndex.hs | 2 +- 4 files changed, 21 insertions(+), 9 deletions(-) diff --git a/guide/2011503.md b/guide/2011503.md index 6e756858b..4d18d07d4 100644 --- a/guide/2011503.md +++ b/guide/2011503.md @@ -1,12 +1,15 @@ # Graph view -A zettelkasten is a [directed graph](https://en.wikipedia.org/wiki/Directed_graph), and <2017401> is a subset of this graph established by having zettels branch off to other zettels. +A zettelkasten is a [directed graph](https://en.wikipedia.org/wiki/Directed_graph). Neuron also has the notion of <2017401>, which is a subset of this graph established by having zettels "branch off" to other zettels. ## z-index -The z-index page (at `/z-index.html`; also linked in the footer) displays your Zettelkasten folgezettel graph. Neuron detects if there are any cycles in the graph (use `cf` to resolve cycles). Then, it detects all <2012301> in the graph, and displays the <2017401> for each cluster. +The z-index page (at `/z-index.html`; also linked in the header) displays the zettelkasten <2017401> for all <2012301> in the zettelkasten graph. ## Uplinks and Backlinks -Uplinks are a kind of backlinks. Specifically an **uplink tree** of a zettel is the subset of the <2017401> which branch off to the zettel. Uplink tree is displayed above the zettel; other backlinks are displayed below. +A backlink of a zettel is a zettel that links to it. If that link is a folgezettel link, it is called an "uplink". +An **uplink tree** of a zettel is the subset of the <2017401> which branch off to the zettel. Uplink tree is displayed above the zettel; other backlinks are displayed below. + +Use `?cf` (see <2011504?cf>) to eject something out of a zettel's uplink tree. diff --git a/guide/2011504.md b/guide/2011504.md index 7900aa512..dcde94afc 100644 --- a/guide/2011504.md +++ b/guide/2011504.md @@ -18,13 +18,22 @@ linked zettel. If your link is merely a reference to another zettel, and you do not wish it to be part of the <2017401?cf>, but only the graph connection, use the `cf`[^cf] query -flag (eg: ``). Neuron will link the zettels, but the link would be -ignored when building the <2017401?cf>. This is especially useful to resolve -cycles and ensure sensible clusters. +flag (eg: ``). + +```markdown +This is a zettel file, which refers to another zettel without +strongly linking to it: + +* +``` + +Neuron will link the zettels, but the link would be +ignored when building the <2017401?cf>. This is especially useful to ensure sensible +clusters. ## Advanced linking -* [[2011506]] +* <2011506> [^cf]: > The abbreviation cf. (short for the Latin: *confer/conferatur*, both meaning "compare") is used in writing to refer the reader to other material to make a comparison with the topic being discussed. [Wikipedia](https://en.wikipedia.org/wiki/Cf.) diff --git a/guide/index.md b/guide/index.md index 6ae5440cd..28d2a5352 100644 --- a/guide/index.md +++ b/guide/index.md @@ -13,7 +13,7 @@ ## Cerveau -[Cerveau](https://www.cerveau.app) is the upcoming web and mobile app ([see screenshot](https://lh3.googleusercontent.com/-aBX5ePS0mD6Ey8NQUFhRmX8SHhxr0aHBE9rX4aGPU_i4_NloQsJsSP2Ug0D8oJGrQeyz2H3MYrnyoJC83mGOsoHOkfB_z6kqDJUa7bbO2xHATw9pw-cKvvMkNpMnW1ww7Z-gpCaM4nm7yTFLSa9T7YQYdWOGJVnBy-vPBRX6d_3kJ0JZj9WoOBQtYnOUiLD85tEjF03PtVVw9mSvhpqrTPLicThyAHj3koYEJAXtdWqP_X7s4lSHvVqtod9NfvM8WFNSsITZJmBHbP8bfGBs1aVy2zCM7cU63bsMw5jVAA5wXm6Ef9v9Lf2gqY-SgRimzlqnUX4eAfEnhvmAkGEOiQgyJ8QJSSgm99TW9l_ve7ZQPZmXgnfUHHCjYwIoqc72VXjTEMn-RRhRJDD8txb3mRQU05t-nF7U4dW8XtWuyXFxB-7Vc7lkwofSEWh_3dJiPZwIT221qFCxz8pr8krMEPrzgf_-YSFoGo804PTXGpp0N8_gM4ElTt4KXx3jfyYiGx4OUD9Lm_n-FaLFyWas7ryIKmTDmx78t1PhycCjwoBT3k3BwWLk-RIcSV9KvCKpkhC2p2wf8qC5ekPgaGaj6J2Rh-OrAI5zF9-erRCzwVw6NF2E-xQFg_8VmIp4Lpvl1ygwB4pX9xKlq4qrGyxVtxO2bIcwnrELqx81Yi_Oah8Y2ik0_VVAdEkrzeDOX4=w3381-h2269-no?authuser=0)) for easy editing and publishing of Neuron notes backed by [Git](https://guides.github.com/introduction/git-handbook/) as storage. +[Cerveau](https://www.cerveau.app) is the upcoming web and mobile app for easy editing and publishing of Neuron notes backed by [Git](https://guides.github.com/introduction/git-handbook/) as storage. ## External links diff --git a/neuron/src/lib/Neuron/Web/ZIndex.hs b/neuron/src/lib/Neuron/Web/ZIndex.hs index af44ef1d4..d9b44e015 100644 --- a/neuron/src/lib/Neuron/Web/ZIndex.hs +++ b/neuron/src/lib/Neuron/Web/ZIndex.hs @@ -89,7 +89,7 @@ renderZIndex (Theme.semanticColor -> themeColor) ZIndex {..} = do el "ul" $ renderForest forest where countNounBe noun nounPlural = \case - 1 -> show 1 <> noun + 1 -> "1 " <> noun n -> show n <> " " <> nounPlural renderErrors :: DomBuilder t m => Map ZettelID ZettelError -> NeuronWebT t m ()