lib: valuation: simplify price search code
This version tries counting recursions instead of path length, but I think they are the same.
This commit is contained in:
parent
73678393b1
commit
7c9303a15c
@ -30,7 +30,7 @@ where
|
|||||||
import Control.Applicative ((<|>))
|
import Control.Applicative ((<|>))
|
||||||
import Data.Foldable (asum)
|
import Data.Foldable (asum)
|
||||||
import Data.Function ((&), on)
|
import Data.Function ((&), on)
|
||||||
import Data.List (intercalate, (\\), sortBy )
|
import Data.List (intercalate, sortBy)
|
||||||
import Data.List.Extra (nubSortBy)
|
import Data.List.Extra (nubSortBy)
|
||||||
import qualified Data.Map as M
|
import qualified Data.Map as M
|
||||||
import qualified Data.Set as S
|
import qualified Data.Set as S
|
||||||
@ -219,10 +219,10 @@ priceLookup makepricegraph d from mto =
|
|||||||
let msg = "seeking " ++ pshowedge' "" from to ++ " price"
|
let msg = "seeking " ++ pshowedge' "" from to ++ " price"
|
||||||
in case
|
in case
|
||||||
(traceAt 2 (msg++" using forward prices") $
|
(traceAt 2 (msg++" using forward prices") $
|
||||||
pricesShortestPath forwardprices from to)
|
pricesShortestPath from to forwardprices)
|
||||||
<|>
|
<|>
|
||||||
(traceAt 2 (msg++" using forward and reverse prices") $
|
(traceAt 2 (msg++" using forward and reverse prices") $
|
||||||
pricesShortestPath allprices from to)
|
pricesShortestPath from to allprices)
|
||||||
of
|
of
|
||||||
Nothing -> Nothing
|
Nothing -> Nothing
|
||||||
Just [] -> Nothing
|
Just [] -> Nothing
|
||||||
@ -278,52 +278,49 @@ data PriceGraph = PriceGraph {
|
|||||||
-- form the edges of a directed graph. There should be at most one edge
|
-- form the edges of a directed graph. There should be at most one edge
|
||||||
-- between each directed pair of commodities, eg there can be one
|
-- between each directed pair of commodities, eg there can be one
|
||||||
-- USD->EUR price and one EUR->USD price.
|
-- USD->EUR price and one EUR->USD price.
|
||||||
pricesShortestPath :: [Edge] -> CommoditySymbol -> CommoditySymbol -> Maybe Path
|
pricesShortestPath :: CommoditySymbol -> CommoditySymbol -> [Edge] -> Maybe Path
|
||||||
pricesShortestPath edges start end =
|
pricesShortestPath start end edges =
|
||||||
-- dbg0With ((("shortest "++pshowedge' "" start end++" price path: ")++) . pshow . fmap (pshowpath "")) $
|
-- dbg0With ((("shortest "++pshowedge' "" start end++" price path: ")++) . pshow . fmap (pshowpath "")) $
|
||||||
dbg2 ("shortest "++pshowedge' "" start end++" price path") $
|
dbg2 ("shortest "++pshowedge' "" start end++" price path") $
|
||||||
case quicksolution of
|
findPath start end edges 0 []
|
||||||
(path:_) -> Just path
|
|
||||||
[] -> asum $ map (findPath end edgesremaining) initialpaths
|
|
||||||
where
|
|
||||||
initialpaths =
|
|
||||||
dbg2With (prefix "initial paths" . intercalate ", " . map (pshowpath "")) $
|
|
||||||
[[p] | p <- dbgedges "known prices" edges, mpfrom p == start]
|
|
||||||
quicksolution = [path | path@(MarketPrice{..}:_) <- initialpaths, mpfrom==start && mpto==end]
|
|
||||||
edgesremaining = dbgedges "initial prices remaining" $
|
|
||||||
[e | e <- edges, mpto e /= start] \\ concat initialpaths
|
|
||||||
|
|
||||||
-- Helper: breadth-first search for a continuation of the given path
|
-- Helper: breadth-first search for a path from the given start commodity
|
||||||
-- using zero or more of the given edges, to the specified end commodity.
|
-- to the given end commodity using one or more of the given edges.
|
||||||
-- Returns the first & shortest complete path found, or Nothing.
|
-- Returns the first (and shortest) complete path found, if there is one.
|
||||||
findPath :: CommoditySymbol -> [Edge] -> Path -> Maybe Path
|
findPath :: CommoditySymbol -> CommoditySymbol -> [Edge] -> Int -> Path -> Maybe Path
|
||||||
findPath end _ path | mpathend == Just end = Just path -- path is complete
|
-- path is complete, return it
|
||||||
|
findPath _ end _ _ path | (mpto <$> lastMay path) == Just end = Just path
|
||||||
|
-- no more edges available, give up
|
||||||
|
findPath _ _ [] _ _ = Nothing
|
||||||
|
-- searching too much perhaps due to a bug, give up
|
||||||
|
-- XXX I think this is equivalent to a max path length, and still not
|
||||||
|
-- much good; we need to count iterations across all branches
|
||||||
|
findPath _ _ _ recursions _ | recursions > maxrecursions = error' err
|
||||||
where
|
where
|
||||||
mpathend = mpto <$> lastMay path
|
maxrecursions = 1000
|
||||||
findPath _ [] _ = Nothing -- no more edges are available
|
|
||||||
-- Guard against infinite loops as in #1439:
|
|
||||||
-- give up if path grows to an unlikely length.
|
|
||||||
-- XXX we need to limit the number of findPath iterations also.
|
|
||||||
findPath _ _ path | length path >= maxlength = error' err
|
|
||||||
where
|
|
||||||
maxlength = 1000
|
|
||||||
err = intercalate "\n" [
|
err = intercalate "\n" [
|
||||||
"giving up after searching price chains up to "++show maxlength++" long;"
|
"giving up searching for a price chain after recursing to depth "++show maxrecursions++";"
|
||||||
,"please report this as a bug."
|
,"please report this as a bug."
|
||||||
]
|
]
|
||||||
findPath end edgesremaining path = -- try continuing with all the remaining edges
|
-- try continuing with all next edges, prioritising those reaching the goal,
|
||||||
asum [
|
-- returning the first complete path found
|
||||||
findPath end edgesremaining' path'
|
findPath start end edgesremaining recursions path
|
||||||
| e <- nextedges
|
| otherwise =
|
||||||
, not $ mpto e `elem` map mpto path -- avoid loops
|
asum [ findPath start end edgesremaining' (recursions+1) path'
|
||||||
|
| e <- finalornextedges
|
||||||
, let path' = dbgpath "findPath trying" $ path++[e]
|
, let path' = dbgpath "findPath trying" $ path++[e]
|
||||||
, let edgesremaining' = filter (/= e) edgesremaining
|
, let edgesremaining' = filter (/= e) edgesremaining -- XXX better: edgesremaining \\ finalornextedges ?
|
||||||
]
|
]
|
||||||
where
|
where
|
||||||
nextedges =
|
-- all edges continuing from path's end, or from start if there is no path yet
|
||||||
[ e | e <- edgesremaining, Just (mpfrom e) == mpathend ]
|
nextedges = dbgedges "nextedges" $
|
||||||
where
|
[ e | e <- edgesremaining
|
||||||
mpathend = mpto <$> lastMay path
|
, mpfrom e == maybe start mpto (lastMay path) -- continue from path end or start
|
||||||
|
, not $ mpto e `elem` (start : map mpto path) -- avoid loops
|
||||||
|
]
|
||||||
|
-- if any next edges reach the goal, keep only those
|
||||||
|
finalornextedges = if null finaledges then nextedges else finaledges
|
||||||
|
where finaledges = filter ((==end).mpto) nextedges
|
||||||
|
|
||||||
dbgpath label = dbg2With (pshowpath label)
|
dbgpath label = dbg2With (pshowpath label)
|
||||||
dbgedges label = dbg2With (pshowedges label)
|
dbgedges label = dbg2With (pshowedges label)
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user