Описание задачи
Наш департамент работает с большим количеством информации, которую удобно представлять в виде графа. Это могут быть последовательности аминокислот, связанные с различными видами структур, или же химические реагенты и катализаторы, связанные с помощью реакций с получившимися продуктами.
Оказывается, на рынке уже есть графовая база данных - neo4j. Удобнее всего её установить с помощью Docker образа. Несложно догадаться, что к этой базе данных существует биндинг для Haskell: hasbolt и набор плюшек для него hasbolt-extras. Некоторое представление о работе с этим библиотеками поможет составить доклад, а также их документация на Hackage.
Важно. Начиная с 4-й версии Neo4j полностью поменяли протокол и не все Haskell-библиотеки на него перешли. На текущий момент лучше использовать версию Neo4j 3.5.
- компонент
Moleculeдолжен иметь поляid :: Int,smiles :: String,iupacName :: String; - компонент
Reactionдолжен иметь поляid :: Int,name :: String; - компонент
Catalystдолжен иметь поляid :: Int,smiles :: String,name :: Maybe String; - компонент
PRODUCT_FROMдолжен иметь полеamount :: Float; - компонент
ACCELERATEдолжен иметь полеtemperature :: Float,pressure :: Float; - дополните структуру данных необходимыми на ваш взгляд полями;
- населите базу данных представителями (хотя бы по 20 образцов каждого вида). Данные подразумеваются совершенно синтетические.
Между записями в структурах данных могут быть различные зависимости (например, между представлением smiles и именем IUPAC).
- создайте соответствующие типы в Haskell-библиотеке;
- напишите функцию, которая умеет принимать реацию на вход и загружать её в базу;
- напишите функцию, которая по номеру реакции в базе будет возвращать её в Haskell-объект;
- напишите функцию, которая по двум заданным молекулам ищет путь через реакции и молекулы с наименьшей длиной.
В процессе развития системы будут добавляться различные компоненты, например, механизм реакции. Предлагается ответить на следующие вопросы:
- какие абстракции или вспомогательные компоненты можно ввести на уровне базы данных, чтобы новые полученные знания ладно укладывались в систему?
- какие абстракции вы бы предложили ввести в Haskell-реализацию?
Для работы с базой данных предназначены модули Functions.TextRequest и Functions.GraphRequest.
Модуль Functions.TextRequest формирует Cypher запросы на основе текстовых шаблонов. В его основе лежит Database.Bolt . Модуль экспортирует следующие функции:
- добавление реакции в БД:
putReaction :: ReactionData -> BoltActionT IO (Id Reaction)- извлечение реакции из БД по указанному Id:
getReaction :: Id Reaction -> BoltActionT IO (Maybe ReactionData)- Нахождение всех кратчайших путей через Реакции и Молекулы между молекулами:
findShortPath :: Molecule -> Molecule -> BoltActionT IO [Transformation]- Удаление узла реакции и связей из БД по переданному Id:
deleteReaction :: Id Reaction -> BoltActionT IO ()- Нахождение всех кратчайших путей через промежуточные Реакции и Молекулы между начальной и конечной Молекулами по переданным Id:
findShortPathById :: Id Molecule -> Id Molecule -> BoltActionT IO [Transformation]Следует отметить, что для функций findShortPath, findShortPathById важен порядок переданных агрументов, так как происходит поиск пути только в одном направлении. Если требуется найти кратчайший путь в любом из направлений, то следует использовать функцию дважды с изменением порядка агрументов. Если оба агрумента совпадают, то возвращается путь, содержащий только переданную молекулу. Если начальная или конечная Молекулы отсутствуют в базе, то возвращается пустой список Трансформаций.
Модуль Functions.GraphRequest основан на построении запросов из графовых шаблонов. В его основе лежит модуль Database.Bolt.Extras.Graph. Модуль экспортирует только 2 функции:
- добавление реакции в БД:
putReaction :: ReactionData -> BoltActionT IO (Id Reaction)- извлечение реакции из БД по указанному Id:
getReaction :: Id Reaction -> BoltActionT IO (Maybe ReactionData)Для генерации данных и наполнения тестовой базы данных используется функция putSampleData из модуля SampleData.
Функция использует данные из файлов Reactions.csv,Molecules.csv,Catalyst.scv в папке SampleData.
putSampleData :: Int -> BoltActionT IO ()Ниже приведен пример использования данной функции из модуля Main:
boltCfg :: BoltCfg
boltCfg = def { host = "localhost"
, user = "neo4j"
, password = "testDB"
}
runQueryDB :: BoltActionT IO a -> IO a
runQueryDB act = bracket (connect boltCfg) close (`run` act)
main :: IO ()
main = do
putStrLn "Enter number of generated reactions:"
n <- read <$> getLine
runQueryDB $ putSampleData nТакже модуль экспортирует функции randomReaction, randomMolecule, randomCatalyst, randomProductFrom, randomAccelerate для генерации составных частей реакции и randomReactionData для генерации реакции.
В данной библиотеке используется логика, запрещающая повторное создание или модификацию уже существующей а базе данных реакции. Если в результате генерации реакции будет повторно использоваться уже существующее имя реакции, то такая реакция не будет добавлена в тестовую БД. Это следует учитывать при выборе числа генераций, передаваемых в функцию putSampleData. Обычно 35 генераций достаточно для получения не менее 20 образцов каждого типа.
В структуру базы данных можно добавить компонент-узел Mechanism и компонент-взаимоотношение DESCRIBES.
Механизм реакции должен содержать информацию об интермедиатах, переходных состояниях и продуктах реакции. Соответственно возможно введение:
- компонентов группы "Интермедиатов"
- Карбкатион
Carbocation - Карбанион
Carbanion - ...
- Нитрен
Nitrene
- Карбкатион
- компонента "Переходного состояния"
TransitionState - компонента-взаимоотношение
TRANSITION_IN - компонента-взаимоотношение
INTERMEDIATE_IN
В Haskell можно ввести типы данных
data DESCRIBES = DESCRIBES{..}
data TRANSITION_IN = TRANSITION_IN{..}
data INTERMEDIATE_IN = INTERMEDIATE_IN{..}data Intermediate = Carbocation{..}
| Carbanion{..}
| ...
| Nitrene{..}
data TransitionState = TransitionState{..}
data Mechanism = Mechanism [(Intermediate, INTERMEDIATE_IN)] [(TransitionState, TRANSITION_IN)]Тип данных ReactionData следует дополнить полем Mechanism
data ReactionData = ReactionData
{ rdName :: Name Reaction
, ...
, rdMechanism :: Maybe (Mechanism, DESCRIBES)
}
