001package org.biopax.paxtools.query.algorithm;
002
003
004import org.biopax.paxtools.query.model.GraphObject;
005import org.biopax.paxtools.query.model.Node;
006
007import java.util.HashMap;
008import java.util.HashSet;
009import java.util.Map;
010import java.util.Set;
011
012/**
013 * Finds the paths from a specified source set of states or entities to a
014 * specified target set of states or entities within the boundaries of a
015 * specified length limit. Takes source set, target set, type of distance
016 * limit, distance limit and strict value. Based on these parameters, the
017 * interactions within source set and within target set may be omitted and/or
018 * "shortest+k" may be used as length limit.
019 *
020 * @author Ozgun Babur
021 * @author Merve Cakir
022 */
023public class PathsFromToQuery
024{
025        /**
026         * The set of nodes from which the paths of interests should start.
027         */
028        private Set<Node> sourceSet;
029
030        /**
031         * The set of nodes to which the paths of interests should arrive.
032         */
033        private Set<Node> targetSet;
034
035        /**
036         * True if length limit is used, false if shortes+k is used.
037         */
038        private LimitType limitType;
039
040        /**
041         * Based on the limitType, given integer may be used directly as stop
042         * distance or may be added up with the shortest path's length and used as
043         * stop distance.
044         */
045        private int stopDistance;
046
047        /**
048         * When true, the interactions within source set and within target set are
049         * not involved in result set.
050         */
051        private boolean strict;
052
053        /**
054         * This is a hard-coded limit to use with the shortest_plus_k limit. If there is no shortest
055         * path between the given nodes, the algorithm should not try to traverse all the graph. So this
056         * shortest path search limit will make sure that the algorithm will not search for indefinitely
057         * for the shortest path.
058         */
059        private static final int LIMIT_FOR_SP_SEARCH = 25;
060
061        /**
062         * Constructor with parameters.
063         * @param sourceSet source set
064         * @param targetSet target set
065         * @param limitType normal limit or shortest + k type limit
066         * @param stopDistance search limit
067         * @param strict whether we want to extend and result path towards other source and targets
068         */
069        public PathsFromToQuery(Set<Node> sourceSet,
070                                                        Set<Node> targetSet,
071                                                        LimitType limitType,
072                                                        int stopDistance,
073                                                        boolean strict)
074        {
075                assert limitType != null : "limitType should be specified";
076
077                this.sourceSet = sourceSet;
078                this.targetSet = targetSet;
079                this.limitType = limitType;
080                this.stopDistance = stopDistance;
081                this.strict = strict;
082        }
083
084        /**
085         * Executes the algorithm.
086         * @return paths from sources to targets
087         */
088        public Set<GraphObject> run()
089        {
090                /**
091                 * Candidate contains all the graph objects that are the results of BFS.
092                 * Eliminating nodes from candidate according to their labels will
093                 * yield result.
094                 */
095                Map<GraphObject, Integer> candidate = new HashMap<GraphObject, Integer>();
096                Set<GraphObject> result = new HashSet<GraphObject>();
097
098                BFS bfsFwd = null;
099                BFS bfsRev = null;
100
101                if (limitType == LimitType.NORMAL && !strict)
102                {
103                        bfsFwd = new BFS(sourceSet, null, Direction.DOWNSTREAM, stopDistance);
104                        bfsRev = new BFS(targetSet, null, Direction.UPSTREAM, stopDistance);
105                }
106                else if (limitType == LimitType.NORMAL && strict)
107                {
108                        bfsFwd = new BFS(sourceSet, targetSet, Direction.DOWNSTREAM, stopDistance);
109                        bfsRev = new BFS(targetSet, sourceSet, Direction.UPSTREAM, stopDistance);
110                }
111                else if (limitType == LimitType.SHORTEST_PLUS_K && !strict)
112                {
113                        bfsFwd = new BFS(sourceSet, null, Direction.DOWNSTREAM, LIMIT_FOR_SP_SEARCH);
114                        bfsRev = new BFS(targetSet, null, Direction.UPSTREAM, LIMIT_FOR_SP_SEARCH);
115                }
116                else if (limitType == LimitType.SHORTEST_PLUS_K && strict)
117                {
118                        bfsFwd = new BFS(sourceSet, targetSet, Direction.DOWNSTREAM, LIMIT_FOR_SP_SEARCH);
119                        bfsRev = new BFS(targetSet, sourceSet, Direction.UPSTREAM, LIMIT_FOR_SP_SEARCH);
120                }
121
122                candidate.putAll(bfsFwd.run());
123                candidate.putAll(bfsRev.run());
124
125                int limit = stopDistance;
126
127                if(limitType == LimitType.NORMAL)
128                {
129                        /**
130                         * Only the graph objects whose sum of two search labels being
131                         * smaller than or equal to the distance limit will be in the result.
132                         */
133                        for (GraphObject go : candidate.keySet())
134                        {
135                                if ((bfsFwd.getLabel(go) + bfsRev.getLabel(go)) <= limit)
136                                {
137                                        result.add(go);
138                                }
139                        }
140                }
141                else
142                {
143                        int shortestPath = Integer.MAX_VALUE;
144
145                        /**
146                         * Summing up the labels of two search will give the length of the
147                         * path that passes through that particular graph object and the
148                         * minimum of those lengths will be the length of the shortest path.
149                         */
150                        for (GraphObject go : candidate.keySet())
151                        {
152                                if ((bfsFwd.getLabel(go) + bfsRev.getLabel(go)) <= shortestPath)
153                                {
154                                        shortestPath = (bfsFwd.getLabel(go) + bfsRev.getLabel(go));
155                                }
156                        }
157
158                        limit = shortestPath + stopDistance;
159
160                        // Proceed only if there is a shortest path found
161
162                        if (shortestPath < Integer.MAX_VALUE / 2)
163                        {
164                                /**
165                                 * Only the graph objects whose sum of two search labels being
166                                 * smaller than or equal to the "shortest + limit" will be in the
167                                 * result.
168                                 */
169                                for (GraphObject go : candidate.keySet())
170                                {
171                                        if ((bfsFwd.getLabel(go) + bfsRev.getLabel(go)) <= limit)
172                                        {
173                                                result.add(go);
174                                        }
175                                }
176                        }
177                }
178
179                Set<Node> ST = new HashSet<Node>(sourceSet);
180                ST.addAll(targetSet);
181
182                CycleBreaker breaker = new CycleBreaker(result, ST, limit);
183                breaker.breakCycles();
184
185                Prune prune = new Prune(result, ST);
186                prune.run();
187
188                return result;
189        }
190}