0 | module Chem.Query
  1 |
  2 | import Chem
  3 | import Chem.Aromaticity
  4 | import Data.Graph.Indexed
  5 | import Data.Graph.Indexed.Subgraph
  6 | import Data.Graph.Indexed.Query.Subgraph
  7 | import Data.Vect
  8 | import Text.Smiles.AtomType
  9 | import Text.Smiles.Types
 10 | import Text.Molfile.AtomType
 11 | import Text.Molfile.Types
 12 |
 13 | %default total
 14 |
 15 | --------------------------------------------------------------------------------
 16 | -- Bonds
 17 | --------------------------------------------------------------------------------
 18 |
 19 | ||| The type of bond we use at the target molecule of
 20 | ||| a substructure search
 21 | public export
 22 | 0 TargetBond : Type
 23 | TargetBond = AromBond BondOrder
 24 |
 25 | ||| The type of bond we use at the query molecule of
 26 | ||| a substructure search
 27 | public export
 28 | 0 QueryBond : Type
 29 | QueryBond = AromBond BondOrder
 30 |
 31 | ||| Default matcher for bonds in a substructure search
 32 | export
 33 | matchBond : QueryBond -> TargetBond -> Bool
 34 | matchBond (AB _ True)   t = t.arom
 35 | matchBond (AB Single _) t = True
 36 | matchBond (AB q _)      t = q == t.type
 37 |
 38 | export
 39 | qbond : Cast b BondOrder => AromBond b -> AromBond BondOrder
 40 | qbond (AB t v) = AB (cast t) v
 41 |
 42 | --------------------------------------------------------------------------------
 43 | -- Atoms
 44 | --------------------------------------------------------------------------------
 45 |
 46 | ||| Total number (explicit and implicit) of hydrogens bound to an atom.
 47 | public export
 48 | record TotH where
 49 |   constructor TH
 50 |   count : Nat
 51 |
 52 | ||| Atom type we use for the target in a substructure search.
 53 | |||
 54 | ||| Note: `TotH` is a counter corresponding to the total number (explicit and
 55 | ||| implicit) of hydrogen atoms bound to an atom.
 56 | public export
 57 | 0 TargetAtom : Type
 58 | TargetAtom = Atom Isotope Charge () () TotH () () ()
 59 |
 60 | ||| Number of explicit hydrogens bound to an atom.
 61 | public export
 62 | record ExplicitH where
 63 |   constructor EH
 64 |   count : Nat
 65 |
 66 | ||| Atom type we use for the query in a substructure search.
 67 | |||
 68 | ||| Note: `ExplicitH` is a counter corresponding to number of explicit
 69 | ||| hydrogens bound to an atom.
 70 | public export
 71 | 0 QueryAtom : Type
 72 | QueryAtom = Atom Isotope Charge () () ExplicitH () () ()
 73 |
 74 | matchIso : Isotope -> Isotope -> Bool
 75 | matchIso x y = x.elem == y.elem && (x.mass == Nothing || x.mass == y.mass)
 76 |
 77 | ||| Default matcher for atoms in a substructure search
 78 | export
 79 | matchAtom : QueryAtom -> TargetAtom -> Bool
 80 | matchAtom q t =
 81 |   matchIso q.elem t.elem &&
 82 |   (q.charge == 0 || q.charge == t.charge) &&
 83 |   q.hydrogen.count <= t.hydrogen.count
 84 |
 85 | --------------------------------------------------------------------------------
 86 | -- Converters
 87 | --------------------------------------------------------------------------------
 88 |
 89 | ||| Graph type we use for the query in substructure searches.
 90 | public export
 91 | 0 QueryGraph : Type
 92 | QueryGraph = Graph QueryBond QueryAtom
 93 |
 94 | ||| Graph type we use for the target in substructure searches.
 95 | public export
 96 | 0 TargetGraph : Type
 97 | TargetGraph = Graph TargetBond TargetAtom
 98 |
 99 | isH : Cast e Isotope => Cast c Charge => Atom e c p r h t ch l -> Bool
100 | isH v = MkI H Nothing == cast v.elem && 0 == cast {to = Charge} v.charge
101 |
102 | -- remove explicit hydrogens from a query graph
103 | removeHs : {k : _} -> IGraph k QueryBond QueryAtom -> QueryGraph
104 | removeHs g = snd <$> subgraphL g (filter (not . isH . lab g) (nodes g))
105 |
106 | parameters {0 e,c,p,r,h,t,ch,l,b : Type}
107 |            {k        : Nat}
108 |            {auto cb  : Cast b BondOrder}
109 |            {auto cel : Cast e Isotope}
110 |            {auto cch : Cast c Charge}
111 |            (g        : IGraph k (AromBond b) (Atom e c p r h t ch l))
112 |
113 |   0 Atm : Type
114 |   Atm = Atom e c p r h t ch l
115 |
116 |   explicitHs : AssocList k (AromBond b) -> Nat
117 |   explicitHs ns = count (\(x,_) => isH $ lab g x) (pairs ns)
118 |
119 |   qadj : Adj k (AromBond b) Atm -> Adj k QueryBond QueryAtom
120 |   qadj (A lbl bs) =
121 |     let bs2  := map qbond bs
122 |         eh   := explicitHs bs
123 |         lbl2 := MkAtom (cast lbl.elem) (cast lbl.charge) () () (EH eh) () () ()
124 |      in A lbl2 bs2
125 |
126 |   tadj : Cast h HCount => Adj k (AromBond b) Atm -> Adj k TargetBond TargetAtom
127 |   tadj (A lbl bs) =
128 |     let bs2  := map qbond bs
129 |         th   := explicitHs bs + cast (value $ cast {to = HCount} lbl.hydrogen)
130 |         lbl2 := MkAtom (cast lbl.elem) (cast lbl.charge) () () (TH th) () () ()
131 |      in A lbl2 bs2
132 |
133 |   ||| Convert an aromatized molecular graph to a target graph to be
134 |   ||| used in a subgraph query.
135 |   export %inline
136 |   toTarget : Cast h HCount => IGraph k TargetBond TargetAtom
137 |   toTarget = mapAdj tadj g
138 |
139 |   ||| Convert an aromatized molecular graph to a query graph to be
140 |   ||| used in a subgraph query.
141 |   export %inline
142 |   toQuery : Graph QueryBond QueryAtom
143 |   toQuery = removeHs $ mapAdj qadj g
144 |
145 | ||| Convert a SMILES graph to one used as a query in a substructure search.
146 | export
147 | smilesQuery : SmilesGraphAT -> QueryGraph
148 | smilesQuery sg =
149 |   let G o g := aromatize atomType sg
150 |    in toQuery g
151 |
152 | ||| Convert a SMILES graph to one used as a target in a substructure search.
153 | export
154 | smilesTarget : SmilesGraphAT -> TargetGraph
155 | smilesTarget sg =
156 |   let G o g := aromatize atomType sg
157 |    in G o $ toTarget g
158 |
159 | ||| Convert a .mol-file graph to one used as a query in a substructure search.
160 | export
161 | molQuery : MolGraphAT -> QueryGraph
162 | molQuery sg =
163 |   let G o g := aromatize atomType sg
164 |    in toQuery g
165 |
166 | ||| Convert a .mol-file graph to one used as a target in a substructure search.
167 | export
168 | molTarget : MolGraphAT -> TargetGraph
169 | molTarget sg =
170 |   let G o g := aromatize atomType sg
171 |    in G o $ toTarget g
172 |
173 | --------------------------------------------------------------------------------
174 | -- Query
175 | --------------------------------------------------------------------------------
176 |
177 | ||| Tries to find a mapping from the query graph to the target graph
178 | |||
179 | ||| TODO: Add support for a search limit to make this provably total!
180 | export %inline
181 | substructureI :
182 |      {q,t : Nat}
183 |   -> IGraph q QueryBond QueryAtom
184 |   -> IGraph t TargetBond TargetAtom
185 |   -> Maybe (Vect q $ Fin t)
186 | substructureI = assert_total $ query matchBond matchAtom
187 |
188 | ||| Tries to find a mapping from the query graph to the target graph.
189 | |||
190 | ||| The mapping is returned as a list of node indices. If you need stronger
191 | ||| quarantees about the indices, consider using `substructureI` for
192 | ||| order-indexed graphs.
193 | export %inline
194 | substructure : QueryGraph -> TargetGraph-> Maybe (List Nat)
195 | substructure (G _ qg) (G _ tg) = map finToNat . toList <$> substructureI qg tg
196 |