module ProductMixAuction.BudgetConstraints.Intersect where
import Control.Lens
import Data.Coerce
import Data.List
import Data.Maybe
import Numeric.LinearAlgebra
import qualified Data.Set as Set
import qualified Data.Vector as V
import qualified Data.Vector as VG
import qualified Data.Vector.Storable as VS
import ProductMixAuction.BudgetConstraints.Types
allCandidateRates :: Eq b => AuctionKind -> [Bid b] -> Set.Set PriceVector
allCandidateRates _ [] = mempty
allCandidateRates ak bids@(b:_) = Set.fromList . map (coerce . cv)
$ allIntersections ak n pairs
where n = V.length (b ^. bid_prices)
pairs = [ P i (_Price p) (bid ^. bid_label)
| bid <- bids
, (i, p) <- zip [0..] (V.toList $ bid ^. bid_prices)
]
cv :: VS.Vector Double -> V.Vector Double
cv = VG.convert
allIntersections :: Eq b => AuctionKind -> Int -> [GoodPrice b] -> [Vector Double]
allIntersections ak n
= roundPoints ak
. solveSystems
. allIntersectionSystems ak n
. allHyperplanes
allHyperplanes :: Eq b => [GoodPrice b] -> [HyperplaneType b]
allHyperplanes goodprices = hods ++ flanges
where hods = [ Hod (P i p_i bid_i)
| P i p_i bid_i <- goodprices
]
flanges = [ Flange (P i p_i bid_i) (P j p_j bid_j)
| P i p_i bid_i <- goodprices
, P j p_j bid_j <- goodprices
, bid_i == bid_j
, i /= j
]
allIntersectionSystems :: AuctionKind -> Int -> [HyperplaneType b] -> [(Matrix Double, Vector Double)]
allIntersectionSystems ak n hs =
[ hyperplanesSystem $ map (toHyperplane ak n) hs'
| hs' <- select n hs
, atLeastOneHod hs'
]
where atLeastOneHod [] = False
atLeastOneHod (Hod _ : _) = True
atLeastOneHod (_ : hts) = atLeastOneHod hts
solveSystems :: [(Matrix Double, Vector Double)] -> [Vector Double]
solveSystems = catMaybes . map (uncurry solve)
where solve mat vec
| fst (size mat) /= snd (size mat) =
error $ "solveSystems.solve: non square matrix " ++ show (size mat)
| rank mat < fst (size mat) =
Nothing
| otherwise = Just (mat <\> vec)
roundPoints :: AuctionKind -> [Vector Double] -> [Vector Double]
roundPoints ak = case ak of
Standard -> filter (VS.all (>=0)) . map (VS.map (fromInteger . round))
BudgetConstrained -> approxZeros . removeSimilarPoints
approxZeros :: [Vector Double] -> [Vector Double]
approxZeros = map (VS.map (\ x -> if abs x < epsilon then 0 else x))
removeSimilarPoints :: [Vector Double] -> [Vector Double]
removeSimilarPoints = nubBy tooClose . sort
where tooClose u v = norm_2 (u - v) < epsilon
select :: Int -> [a] -> [[a]]
select 0 _ = [[]]
select n l = do
(x:xs) <- tails l
rest <- select (n - 1) xs
return (x:rest)
data Hyperplane = H (Vector Double) Double
ppHyperplanes :: [Hyperplane] -> String
ppHyperplanes = intercalate "\n" . map ppHyperplane
ppHyperplane :: Hyperplane -> String
ppHyperplane (H as b) = intercalate " + " coeffs ++ " = " ++ show b
where coeffs = catMaybes
[ if a_i == 0 then Nothing else Just (smartShow a_i $ "x_" ++ show i)
| (i, a_i) <- zip [0::Int ..] (toList as)
]
smartShow d xstr
| d == 1.0 = xstr
| otherwise = show d ++ "*" ++ xstr
hyperplanesSystem :: [Hyperplane] -> (Matrix Double, Vector Double)
hyperplanesSystem hs =
( fromRows [ as | H as _ <- hs ]
, fromList [ b | H _ b <- hs ]
)
data GoodPrice b = P !Int !Double b
deriving (Eq, Show)
data HyperplaneType b
= Hod !(GoodPrice b)
| Flange !(GoodPrice b) !(GoodPrice b)
deriving (Eq, Show)
toHyperplane :: AuctionKind -> Int -> HyperplaneType b -> Hyperplane
toHyperplane ak n t = case t of
Hod (P i p _b) ->
H (fromList [ if k == i then 1 else 0 | k <- [0 .. n-1] ]) p
Flange (P i p_i _b_i) (P j p_j _b_j)
| i /= j ->
let (as, v) = case ak of
Standard -> ([ if k == i then 1 else if k == j then -1 else 0 | k <- [0..n-1] ], p_i - p_j)
BudgetConstrained -> ([ if k == i then 1/p_i else if k == j then -1/p_j else 0 | k <- [0..n-1] ], 0)
in H (fromList as) v
| otherwise -> error $ "toHyperplane.Flange: i = j = " ++ show i
epsilon :: RealFloat a => a
epsilon = 10 ** (-6)