_multivariate.py 119 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607160816091610161116121613161416151616161716181619162016211622162316241625162616271628162916301631163216331634163516361637163816391640164116421643164416451646164716481649165016511652165316541655165616571658165916601661166216631664166516661667166816691670167116721673167416751676167716781679168016811682168316841685168616871688168916901691169216931694169516961697169816991700170117021703170417051706170717081709171017111712171317141715171617171718171917201721172217231724172517261727172817291730173117321733173417351736173717381739174017411742174317441745174617471748174917501751175217531754175517561757175817591760176117621763176417651766176717681769177017711772177317741775177617771778177917801781178217831784178517861787178817891790179117921793179417951796179717981799180018011802180318041805180618071808180918101811181218131814181518161817181818191820182118221823182418251826182718281829183018311832183318341835183618371838183918401841184218431844184518461847184818491850185118521853185418551856185718581859186018611862186318641865186618671868186918701871187218731874187518761877187818791880188118821883188418851886188718881889189018911892189318941895189618971898189919001901190219031904190519061907190819091910191119121913191419151916191719181919192019211922192319241925192619271928192919301931193219331934193519361937193819391940194119421943194419451946194719481949195019511952195319541955195619571958195919601961196219631964196519661967196819691970197119721973197419751976197719781979198019811982198319841985198619871988198919901991199219931994199519961997199819992000200120022003200420052006200720082009201020112012201320142015201620172018201920202021202220232024202520262027202820292030203120322033203420352036203720382039204020412042204320442045204620472048204920502051205220532054205520562057205820592060206120622063206420652066206720682069207020712072207320742075207620772078207920802081208220832084208520862087208820892090209120922093209420952096209720982099210021012102210321042105210621072108210921102111211221132114211521162117211821192120212121222123212421252126212721282129213021312132213321342135213621372138213921402141214221432144214521462147214821492150215121522153215421552156215721582159216021612162216321642165216621672168216921702171217221732174217521762177217821792180218121822183218421852186218721882189219021912192219321942195219621972198219922002201220222032204220522062207220822092210221122122213221422152216221722182219222022212222222322242225222622272228222922302231223222332234223522362237223822392240224122422243224422452246224722482249225022512252225322542255225622572258225922602261226222632264226522662267226822692270227122722273227422752276227722782279228022812282228322842285228622872288228922902291229222932294229522962297229822992300230123022303230423052306230723082309231023112312231323142315231623172318231923202321232223232324232523262327232823292330233123322333233423352336233723382339234023412342234323442345234623472348234923502351235223532354235523562357235823592360236123622363236423652366236723682369237023712372237323742375237623772378237923802381238223832384238523862387238823892390239123922393239423952396239723982399240024012402240324042405240624072408240924102411241224132414241524162417241824192420242124222423242424252426242724282429243024312432243324342435243624372438243924402441244224432444244524462447244824492450245124522453245424552456245724582459246024612462246324642465246624672468246924702471247224732474247524762477247824792480248124822483248424852486248724882489249024912492249324942495249624972498249925002501250225032504250525062507250825092510251125122513251425152516251725182519252025212522252325242525252625272528252925302531253225332534253525362537253825392540254125422543254425452546254725482549255025512552255325542555255625572558255925602561256225632564256525662567256825692570257125722573257425752576257725782579258025812582258325842585258625872588258925902591259225932594259525962597259825992600260126022603260426052606260726082609261026112612261326142615261626172618261926202621262226232624262526262627262826292630263126322633263426352636263726382639264026412642264326442645264626472648264926502651265226532654265526562657265826592660266126622663266426652666266726682669267026712672267326742675267626772678267926802681268226832684268526862687268826892690269126922693269426952696269726982699270027012702270327042705270627072708270927102711271227132714271527162717271827192720272127222723272427252726272727282729273027312732273327342735273627372738273927402741274227432744274527462747274827492750275127522753275427552756275727582759276027612762276327642765276627672768276927702771277227732774277527762777277827792780278127822783278427852786278727882789279027912792279327942795279627972798279928002801280228032804280528062807280828092810281128122813281428152816281728182819282028212822282328242825282628272828282928302831283228332834283528362837283828392840284128422843284428452846284728482849285028512852285328542855285628572858285928602861286228632864286528662867286828692870287128722873287428752876287728782879288028812882288328842885288628872888288928902891289228932894289528962897289828992900290129022903290429052906290729082909291029112912291329142915291629172918291929202921292229232924292529262927292829292930293129322933293429352936293729382939294029412942294329442945294629472948294929502951295229532954295529562957295829592960296129622963296429652966296729682969297029712972297329742975297629772978297929802981298229832984298529862987298829892990299129922993299429952996299729982999300030013002300330043005300630073008300930103011301230133014301530163017301830193020302130223023302430253026302730283029303030313032303330343035303630373038303930403041304230433044304530463047304830493050305130523053305430553056305730583059306030613062306330643065306630673068306930703071307230733074307530763077307830793080308130823083308430853086308730883089309030913092309330943095309630973098309931003101310231033104310531063107310831093110311131123113311431153116311731183119312031213122312331243125312631273128312931303131313231333134313531363137313831393140314131423143314431453146314731483149315031513152315331543155315631573158315931603161316231633164316531663167316831693170317131723173317431753176317731783179318031813182318331843185318631873188318931903191319231933194319531963197319831993200320132023203320432053206320732083209321032113212321332143215321632173218321932203221322232233224322532263227322832293230323132323233323432353236323732383239324032413242324332443245324632473248324932503251325232533254325532563257325832593260326132623263326432653266326732683269327032713272327332743275327632773278327932803281328232833284328532863287328832893290329132923293329432953296329732983299330033013302330333043305330633073308330933103311331233133314331533163317331833193320332133223323332433253326332733283329333033313332333333343335333633373338333933403341334233433344334533463347334833493350335133523353335433553356335733583359336033613362336333643365336633673368336933703371337233733374337533763377337833793380338133823383338433853386338733883389339033913392339333943395339633973398339934003401340234033404340534063407340834093410341134123413341434153416341734183419342034213422342334243425342634273428342934303431343234333434343534363437343834393440344134423443344434453446344734483449345034513452345334543455345634573458345934603461346234633464346534663467346834693470347134723473347434753476347734783479348034813482348334843485348634873488348934903491349234933494349534963497349834993500350135023503350435053506350735083509351035113512351335143515351635173518351935203521352235233524352535263527352835293530353135323533353435353536353735383539354035413542354335443545354635473548354935503551355235533554355535563557355835593560356135623563356435653566356735683569357035713572357335743575357635773578357935803581358235833584358535863587358835893590359135923593359435953596359735983599360036013602360336043605360636073608360936103611361236133614361536163617361836193620362136223623362436253626362736283629363036313632363336343635363636373638363936403641364236433644364536463647364836493650365136523653365436553656365736583659366036613662366336643665366636673668366936703671367236733674367536763677367836793680368136823683368436853686368736883689369036913692369336943695369636973698369937003701370237033704370537063707370837093710371137123713371437153716371737183719372037213722372337243725372637273728372937303731373237333734373537363737373837393740374137423743374437453746374737483749375037513752375337543755375637573758375937603761376237633764376537663767376837693770377137723773377437753776377737783779378037813782378337843785378637873788378937903791379237933794379537963797379837993800380138023803380438053806380738083809381038113812381338143815381638173818381938203821382238233824382538263827
  1. #
  2. # Author: Joris Vankerschaver 2013
  3. #
  4. from __future__ import division, print_function, absolute_import
  5. import math
  6. import numpy as np
  7. from numpy import asarray_chkfinite, asarray
  8. import scipy.linalg
  9. from scipy.misc import doccer
  10. from scipy.special import gammaln, psi, multigammaln, xlogy, entr
  11. from scipy._lib._util import check_random_state
  12. from scipy.linalg.blas import drot
  13. from scipy.linalg.misc import LinAlgError
  14. from scipy.linalg.lapack import get_lapack_funcs
  15. from ._discrete_distns import binom
  16. from . import mvn
  17. __all__ = ['multivariate_normal',
  18. 'matrix_normal',
  19. 'dirichlet',
  20. 'wishart',
  21. 'invwishart',
  22. 'multinomial',
  23. 'special_ortho_group',
  24. 'ortho_group',
  25. 'random_correlation',
  26. 'unitary_group']
  27. _LOG_2PI = np.log(2 * np.pi)
  28. _LOG_2 = np.log(2)
  29. _LOG_PI = np.log(np.pi)
  30. _doc_random_state = """\
  31. random_state : None or int or np.random.RandomState instance, optional
  32. If int or RandomState, use it for drawing the random variates.
  33. If None (or np.random), the global np.random state is used.
  34. Default is None.
  35. """
  36. def _squeeze_output(out):
  37. """
  38. Remove single-dimensional entries from array and convert to scalar,
  39. if necessary.
  40. """
  41. out = out.squeeze()
  42. if out.ndim == 0:
  43. out = out[()]
  44. return out
  45. def _eigvalsh_to_eps(spectrum, cond=None, rcond=None):
  46. """
  47. Determine which eigenvalues are "small" given the spectrum.
  48. This is for compatibility across various linear algebra functions
  49. that should agree about whether or not a Hermitian matrix is numerically
  50. singular and what is its numerical matrix rank.
  51. This is designed to be compatible with scipy.linalg.pinvh.
  52. Parameters
  53. ----------
  54. spectrum : 1d ndarray
  55. Array of eigenvalues of a Hermitian matrix.
  56. cond, rcond : float, optional
  57. Cutoff for small eigenvalues.
  58. Singular values smaller than rcond * largest_eigenvalue are
  59. considered zero.
  60. If None or -1, suitable machine precision is used.
  61. Returns
  62. -------
  63. eps : float
  64. Magnitude cutoff for numerical negligibility.
  65. """
  66. if rcond is not None:
  67. cond = rcond
  68. if cond in [None, -1]:
  69. t = spectrum.dtype.char.lower()
  70. factor = {'f': 1E3, 'd': 1E6}
  71. cond = factor[t] * np.finfo(t).eps
  72. eps = cond * np.max(abs(spectrum))
  73. return eps
  74. def _pinv_1d(v, eps=1e-5):
  75. """
  76. A helper function for computing the pseudoinverse.
  77. Parameters
  78. ----------
  79. v : iterable of numbers
  80. This may be thought of as a vector of eigenvalues or singular values.
  81. eps : float
  82. Values with magnitude no greater than eps are considered negligible.
  83. Returns
  84. -------
  85. v_pinv : 1d float ndarray
  86. A vector of pseudo-inverted numbers.
  87. """
  88. return np.array([0 if abs(x) <= eps else 1/x for x in v], dtype=float)
  89. class _PSD(object):
  90. """
  91. Compute coordinated functions of a symmetric positive semidefinite matrix.
  92. This class addresses two issues. Firstly it allows the pseudoinverse,
  93. the logarithm of the pseudo-determinant, and the rank of the matrix
  94. to be computed using one call to eigh instead of three.
  95. Secondly it allows these functions to be computed in a way
  96. that gives mutually compatible results.
  97. All of the functions are computed with a common understanding as to
  98. which of the eigenvalues are to be considered negligibly small.
  99. The functions are designed to coordinate with scipy.linalg.pinvh()
  100. but not necessarily with np.linalg.det() or with np.linalg.matrix_rank().
  101. Parameters
  102. ----------
  103. M : array_like
  104. Symmetric positive semidefinite matrix (2-D).
  105. cond, rcond : float, optional
  106. Cutoff for small eigenvalues.
  107. Singular values smaller than rcond * largest_eigenvalue are
  108. considered zero.
  109. If None or -1, suitable machine precision is used.
  110. lower : bool, optional
  111. Whether the pertinent array data is taken from the lower
  112. or upper triangle of M. (Default: lower)
  113. check_finite : bool, optional
  114. Whether to check that the input matrices contain only finite
  115. numbers. Disabling may give a performance gain, but may result
  116. in problems (crashes, non-termination) if the inputs do contain
  117. infinities or NaNs.
  118. allow_singular : bool, optional
  119. Whether to allow a singular matrix. (Default: True)
  120. Notes
  121. -----
  122. The arguments are similar to those of scipy.linalg.pinvh().
  123. """
  124. def __init__(self, M, cond=None, rcond=None, lower=True,
  125. check_finite=True, allow_singular=True):
  126. # Compute the symmetric eigendecomposition.
  127. # Note that eigh takes care of array conversion, chkfinite,
  128. # and assertion that the matrix is square.
  129. s, u = scipy.linalg.eigh(M, lower=lower, check_finite=check_finite)
  130. eps = _eigvalsh_to_eps(s, cond, rcond)
  131. if np.min(s) < -eps:
  132. raise ValueError('the input matrix must be positive semidefinite')
  133. d = s[s > eps]
  134. if len(d) < len(s) and not allow_singular:
  135. raise np.linalg.LinAlgError('singular matrix')
  136. s_pinv = _pinv_1d(s, eps)
  137. U = np.multiply(u, np.sqrt(s_pinv))
  138. # Initialize the eagerly precomputed attributes.
  139. self.rank = len(d)
  140. self.U = U
  141. self.log_pdet = np.sum(np.log(d))
  142. # Initialize an attribute to be lazily computed.
  143. self._pinv = None
  144. @property
  145. def pinv(self):
  146. if self._pinv is None:
  147. self._pinv = np.dot(self.U, self.U.T)
  148. return self._pinv
  149. class multi_rv_generic(object):
  150. """
  151. Class which encapsulates common functionality between all multivariate
  152. distributions.
  153. """
  154. def __init__(self, seed=None):
  155. super(multi_rv_generic, self).__init__()
  156. self._random_state = check_random_state(seed)
  157. @property
  158. def random_state(self):
  159. """ Get or set the RandomState object for generating random variates.
  160. This can be either None or an existing RandomState object.
  161. If None (or np.random), use the RandomState singleton used by np.random.
  162. If already a RandomState instance, use it.
  163. If an int, use a new RandomState instance seeded with seed.
  164. """
  165. return self._random_state
  166. @random_state.setter
  167. def random_state(self, seed):
  168. self._random_state = check_random_state(seed)
  169. def _get_random_state(self, random_state):
  170. if random_state is not None:
  171. return check_random_state(random_state)
  172. else:
  173. return self._random_state
  174. class multi_rv_frozen(object):
  175. """
  176. Class which encapsulates common functionality between all frozen
  177. multivariate distributions.
  178. """
  179. @property
  180. def random_state(self):
  181. return self._dist._random_state
  182. @random_state.setter
  183. def random_state(self, seed):
  184. self._dist._random_state = check_random_state(seed)
  185. _mvn_doc_default_callparams = """\
  186. mean : array_like, optional
  187. Mean of the distribution (default zero)
  188. cov : array_like, optional
  189. Covariance matrix of the distribution (default one)
  190. allow_singular : bool, optional
  191. Whether to allow a singular covariance matrix. (Default: False)
  192. """
  193. _mvn_doc_callparams_note = \
  194. """Setting the parameter `mean` to `None` is equivalent to having `mean`
  195. be the zero-vector. The parameter `cov` can be a scalar, in which case
  196. the covariance matrix is the identity times that value, a vector of
  197. diagonal entries for the covariance matrix, or a two-dimensional
  198. array_like.
  199. """
  200. _mvn_doc_frozen_callparams = ""
  201. _mvn_doc_frozen_callparams_note = \
  202. """See class definition for a detailed description of parameters."""
  203. mvn_docdict_params = {
  204. '_mvn_doc_default_callparams': _mvn_doc_default_callparams,
  205. '_mvn_doc_callparams_note': _mvn_doc_callparams_note,
  206. '_doc_random_state': _doc_random_state
  207. }
  208. mvn_docdict_noparams = {
  209. '_mvn_doc_default_callparams': _mvn_doc_frozen_callparams,
  210. '_mvn_doc_callparams_note': _mvn_doc_frozen_callparams_note,
  211. '_doc_random_state': _doc_random_state
  212. }
  213. class multivariate_normal_gen(multi_rv_generic):
  214. r"""
  215. A multivariate normal random variable.
  216. The `mean` keyword specifies the mean. The `cov` keyword specifies the
  217. covariance matrix.
  218. Methods
  219. -------
  220. ``pdf(x, mean=None, cov=1, allow_singular=False)``
  221. Probability density function.
  222. ``logpdf(x, mean=None, cov=1, allow_singular=False)``
  223. Log of the probability density function.
  224. ``cdf(x, mean=None, cov=1, allow_singular=False, maxpts=1000000*dim, abseps=1e-5, releps=1e-5)``
  225. Cumulative distribution function.
  226. ``logcdf(x, mean=None, cov=1, allow_singular=False, maxpts=1000000*dim, abseps=1e-5, releps=1e-5)``
  227. Log of the cumulative distribution function.
  228. ``rvs(mean=None, cov=1, size=1, random_state=None)``
  229. Draw random samples from a multivariate normal distribution.
  230. ``entropy()``
  231. Compute the differential entropy of the multivariate normal.
  232. Parameters
  233. ----------
  234. x : array_like
  235. Quantiles, with the last axis of `x` denoting the components.
  236. %(_mvn_doc_default_callparams)s
  237. %(_doc_random_state)s
  238. Alternatively, the object may be called (as a function) to fix the mean
  239. and covariance parameters, returning a "frozen" multivariate normal
  240. random variable:
  241. rv = multivariate_normal(mean=None, cov=1, allow_singular=False)
  242. - Frozen object with the same methods but holding the given
  243. mean and covariance fixed.
  244. Notes
  245. -----
  246. %(_mvn_doc_callparams_note)s
  247. The covariance matrix `cov` must be a (symmetric) positive
  248. semi-definite matrix. The determinant and inverse of `cov` are computed
  249. as the pseudo-determinant and pseudo-inverse, respectively, so
  250. that `cov` does not need to have full rank.
  251. The probability density function for `multivariate_normal` is
  252. .. math::
  253. f(x) = \frac{1}{\sqrt{(2 \pi)^k \det \Sigma}}
  254. \exp\left( -\frac{1}{2} (x - \mu)^T \Sigma^{-1} (x - \mu) \right),
  255. where :math:`\mu` is the mean, :math:`\Sigma` the covariance matrix,
  256. and :math:`k` is the dimension of the space where :math:`x` takes values.
  257. .. versionadded:: 0.14.0
  258. Examples
  259. --------
  260. >>> import matplotlib.pyplot as plt
  261. >>> from scipy.stats import multivariate_normal
  262. >>> x = np.linspace(0, 5, 10, endpoint=False)
  263. >>> y = multivariate_normal.pdf(x, mean=2.5, cov=0.5); y
  264. array([ 0.00108914, 0.01033349, 0.05946514, 0.20755375, 0.43939129,
  265. 0.56418958, 0.43939129, 0.20755375, 0.05946514, 0.01033349])
  266. >>> fig1 = plt.figure()
  267. >>> ax = fig1.add_subplot(111)
  268. >>> ax.plot(x, y)
  269. The input quantiles can be any shape of array, as long as the last
  270. axis labels the components. This allows us for instance to
  271. display the frozen pdf for a non-isotropic random variable in 2D as
  272. follows:
  273. >>> x, y = np.mgrid[-1:1:.01, -1:1:.01]
  274. >>> pos = np.dstack((x, y))
  275. >>> rv = multivariate_normal([0.5, -0.2], [[2.0, 0.3], [0.3, 0.5]])
  276. >>> fig2 = plt.figure()
  277. >>> ax2 = fig2.add_subplot(111)
  278. >>> ax2.contourf(x, y, rv.pdf(pos))
  279. """
  280. def __init__(self, seed=None):
  281. super(multivariate_normal_gen, self).__init__(seed)
  282. self.__doc__ = doccer.docformat(self.__doc__, mvn_docdict_params)
  283. def __call__(self, mean=None, cov=1, allow_singular=False, seed=None):
  284. """
  285. Create a frozen multivariate normal distribution.
  286. See `multivariate_normal_frozen` for more information.
  287. """
  288. return multivariate_normal_frozen(mean, cov,
  289. allow_singular=allow_singular,
  290. seed=seed)
  291. def _process_parameters(self, dim, mean, cov):
  292. """
  293. Infer dimensionality from mean or covariance matrix, ensure that
  294. mean and covariance are full vector resp. matrix.
  295. """
  296. # Try to infer dimensionality
  297. if dim is None:
  298. if mean is None:
  299. if cov is None:
  300. dim = 1
  301. else:
  302. cov = np.asarray(cov, dtype=float)
  303. if cov.ndim < 2:
  304. dim = 1
  305. else:
  306. dim = cov.shape[0]
  307. else:
  308. mean = np.asarray(mean, dtype=float)
  309. dim = mean.size
  310. else:
  311. if not np.isscalar(dim):
  312. raise ValueError("Dimension of random variable must be "
  313. "a scalar.")
  314. # Check input sizes and return full arrays for mean and cov if
  315. # necessary
  316. if mean is None:
  317. mean = np.zeros(dim)
  318. mean = np.asarray(mean, dtype=float)
  319. if cov is None:
  320. cov = 1.0
  321. cov = np.asarray(cov, dtype=float)
  322. if dim == 1:
  323. mean.shape = (1,)
  324. cov.shape = (1, 1)
  325. if mean.ndim != 1 or mean.shape[0] != dim:
  326. raise ValueError("Array 'mean' must be a vector of length %d." %
  327. dim)
  328. if cov.ndim == 0:
  329. cov = cov * np.eye(dim)
  330. elif cov.ndim == 1:
  331. cov = np.diag(cov)
  332. elif cov.ndim == 2 and cov.shape != (dim, dim):
  333. rows, cols = cov.shape
  334. if rows != cols:
  335. msg = ("Array 'cov' must be square if it is two dimensional,"
  336. " but cov.shape = %s." % str(cov.shape))
  337. else:
  338. msg = ("Dimension mismatch: array 'cov' is of shape %s,"
  339. " but 'mean' is a vector of length %d.")
  340. msg = msg % (str(cov.shape), len(mean))
  341. raise ValueError(msg)
  342. elif cov.ndim > 2:
  343. raise ValueError("Array 'cov' must be at most two-dimensional,"
  344. " but cov.ndim = %d" % cov.ndim)
  345. return dim, mean, cov
  346. def _process_quantiles(self, x, dim):
  347. """
  348. Adjust quantiles array so that last axis labels the components of
  349. each data point.
  350. """
  351. x = np.asarray(x, dtype=float)
  352. if x.ndim == 0:
  353. x = x[np.newaxis]
  354. elif x.ndim == 1:
  355. if dim == 1:
  356. x = x[:, np.newaxis]
  357. else:
  358. x = x[np.newaxis, :]
  359. return x
  360. def _logpdf(self, x, mean, prec_U, log_det_cov, rank):
  361. """
  362. Parameters
  363. ----------
  364. x : ndarray
  365. Points at which to evaluate the log of the probability
  366. density function
  367. mean : ndarray
  368. Mean of the distribution
  369. prec_U : ndarray
  370. A decomposition such that np.dot(prec_U, prec_U.T)
  371. is the precision matrix, i.e. inverse of the covariance matrix.
  372. log_det_cov : float
  373. Logarithm of the determinant of the covariance matrix
  374. rank : int
  375. Rank of the covariance matrix.
  376. Notes
  377. -----
  378. As this function does no argument checking, it should not be
  379. called directly; use 'logpdf' instead.
  380. """
  381. dev = x - mean
  382. maha = np.sum(np.square(np.dot(dev, prec_U)), axis=-1)
  383. return -0.5 * (rank * _LOG_2PI + log_det_cov + maha)
  384. def logpdf(self, x, mean=None, cov=1, allow_singular=False):
  385. """
  386. Log of the multivariate normal probability density function.
  387. Parameters
  388. ----------
  389. x : array_like
  390. Quantiles, with the last axis of `x` denoting the components.
  391. %(_mvn_doc_default_callparams)s
  392. Returns
  393. -------
  394. pdf : ndarray or scalar
  395. Log of the probability density function evaluated at `x`
  396. Notes
  397. -----
  398. %(_mvn_doc_callparams_note)s
  399. """
  400. dim, mean, cov = self._process_parameters(None, mean, cov)
  401. x = self._process_quantiles(x, dim)
  402. psd = _PSD(cov, allow_singular=allow_singular)
  403. out = self._logpdf(x, mean, psd.U, psd.log_pdet, psd.rank)
  404. return _squeeze_output(out)
  405. def pdf(self, x, mean=None, cov=1, allow_singular=False):
  406. """
  407. Multivariate normal probability density function.
  408. Parameters
  409. ----------
  410. x : array_like
  411. Quantiles, with the last axis of `x` denoting the components.
  412. %(_mvn_doc_default_callparams)s
  413. Returns
  414. -------
  415. pdf : ndarray or scalar
  416. Probability density function evaluated at `x`
  417. Notes
  418. -----
  419. %(_mvn_doc_callparams_note)s
  420. """
  421. dim, mean, cov = self._process_parameters(None, mean, cov)
  422. x = self._process_quantiles(x, dim)
  423. psd = _PSD(cov, allow_singular=allow_singular)
  424. out = np.exp(self._logpdf(x, mean, psd.U, psd.log_pdet, psd.rank))
  425. return _squeeze_output(out)
  426. def _cdf(self, x, mean, cov, maxpts, abseps, releps):
  427. """
  428. Parameters
  429. ----------
  430. x : ndarray
  431. Points at which to evaluate the cumulative distribution function.
  432. mean : ndarray
  433. Mean of the distribution
  434. cov : array_like
  435. Covariance matrix of the distribution
  436. maxpts: integer
  437. The maximum number of points to use for integration
  438. abseps: float
  439. Absolute error tolerance
  440. releps: float
  441. Relative error tolerance
  442. Notes
  443. -----
  444. As this function does no argument checking, it should not be
  445. called directly; use 'cdf' instead.
  446. .. versionadded:: 1.0.0
  447. """
  448. lower = np.full(mean.shape, -np.inf)
  449. # mvnun expects 1-d arguments, so process points sequentially
  450. func1d = lambda x_slice: mvn.mvnun(lower, x_slice, mean, cov,
  451. maxpts, abseps, releps)[0]
  452. out = np.apply_along_axis(func1d, -1, x)
  453. return _squeeze_output(out)
  454. def logcdf(self, x, mean=None, cov=1, allow_singular=False, maxpts=None,
  455. abseps=1e-5, releps=1e-5):
  456. """
  457. Log of the multivariate normal cumulative distribution function.
  458. Parameters
  459. ----------
  460. x : array_like
  461. Quantiles, with the last axis of `x` denoting the components.
  462. %(_mvn_doc_default_callparams)s
  463. maxpts: integer, optional
  464. The maximum number of points to use for integration
  465. (default `1000000*dim`)
  466. abseps: float, optional
  467. Absolute error tolerance (default 1e-5)
  468. releps: float, optional
  469. Relative error tolerance (default 1e-5)
  470. Returns
  471. -------
  472. cdf : ndarray or scalar
  473. Log of the cumulative distribution function evaluated at `x`
  474. Notes
  475. -----
  476. %(_mvn_doc_callparams_note)s
  477. .. versionadded:: 1.0.0
  478. """
  479. dim, mean, cov = self._process_parameters(None, mean, cov)
  480. x = self._process_quantiles(x, dim)
  481. # Use _PSD to check covariance matrix
  482. _PSD(cov, allow_singular=allow_singular)
  483. if not maxpts:
  484. maxpts = 1000000 * dim
  485. out = np.log(self._cdf(x, mean, cov, maxpts, abseps, releps))
  486. return out
  487. def cdf(self, x, mean=None, cov=1, allow_singular=False, maxpts=None,
  488. abseps=1e-5, releps=1e-5):
  489. """
  490. Multivariate normal cumulative distribution function.
  491. Parameters
  492. ----------
  493. x : array_like
  494. Quantiles, with the last axis of `x` denoting the components.
  495. %(_mvn_doc_default_callparams)s
  496. maxpts: integer, optional
  497. The maximum number of points to use for integration
  498. (default `1000000*dim`)
  499. abseps: float, optional
  500. Absolute error tolerance (default 1e-5)
  501. releps: float, optional
  502. Relative error tolerance (default 1e-5)
  503. Returns
  504. -------
  505. cdf : ndarray or scalar
  506. Cumulative distribution function evaluated at `x`
  507. Notes
  508. -----
  509. %(_mvn_doc_callparams_note)s
  510. .. versionadded:: 1.0.0
  511. """
  512. dim, mean, cov = self._process_parameters(None, mean, cov)
  513. x = self._process_quantiles(x, dim)
  514. # Use _PSD to check covariance matrix
  515. _PSD(cov, allow_singular=allow_singular)
  516. if not maxpts:
  517. maxpts = 1000000 * dim
  518. out = self._cdf(x, mean, cov, maxpts, abseps, releps)
  519. return out
  520. def rvs(self, mean=None, cov=1, size=1, random_state=None):
  521. """
  522. Draw random samples from a multivariate normal distribution.
  523. Parameters
  524. ----------
  525. %(_mvn_doc_default_callparams)s
  526. size : integer, optional
  527. Number of samples to draw (default 1).
  528. %(_doc_random_state)s
  529. Returns
  530. -------
  531. rvs : ndarray or scalar
  532. Random variates of size (`size`, `N`), where `N` is the
  533. dimension of the random variable.
  534. Notes
  535. -----
  536. %(_mvn_doc_callparams_note)s
  537. """
  538. dim, mean, cov = self._process_parameters(None, mean, cov)
  539. random_state = self._get_random_state(random_state)
  540. out = random_state.multivariate_normal(mean, cov, size)
  541. return _squeeze_output(out)
  542. def entropy(self, mean=None, cov=1):
  543. """
  544. Compute the differential entropy of the multivariate normal.
  545. Parameters
  546. ----------
  547. %(_mvn_doc_default_callparams)s
  548. Returns
  549. -------
  550. h : scalar
  551. Entropy of the multivariate normal distribution
  552. Notes
  553. -----
  554. %(_mvn_doc_callparams_note)s
  555. """
  556. dim, mean, cov = self._process_parameters(None, mean, cov)
  557. _, logdet = np.linalg.slogdet(2 * np.pi * np.e * cov)
  558. return 0.5 * logdet
  559. multivariate_normal = multivariate_normal_gen()
  560. class multivariate_normal_frozen(multi_rv_frozen):
  561. def __init__(self, mean=None, cov=1, allow_singular=False, seed=None,
  562. maxpts=None, abseps=1e-5, releps=1e-5):
  563. """
  564. Create a frozen multivariate normal distribution.
  565. Parameters
  566. ----------
  567. mean : array_like, optional
  568. Mean of the distribution (default zero)
  569. cov : array_like, optional
  570. Covariance matrix of the distribution (default one)
  571. allow_singular : bool, optional
  572. If this flag is True then tolerate a singular
  573. covariance matrix (default False).
  574. seed : None or int or np.random.RandomState instance, optional
  575. This parameter defines the RandomState object to use for drawing
  576. random variates.
  577. If None (or np.random), the global np.random state is used.
  578. If integer, it is used to seed the local RandomState instance
  579. Default is None.
  580. maxpts: integer, optional
  581. The maximum number of points to use for integration of the
  582. cumulative distribution function (default `1000000*dim`)
  583. abseps: float, optional
  584. Absolute error tolerance for the cumulative distribution function
  585. (default 1e-5)
  586. releps: float, optional
  587. Relative error tolerance for the cumulative distribution function
  588. (default 1e-5)
  589. Examples
  590. --------
  591. When called with the default parameters, this will create a 1D random
  592. variable with mean 0 and covariance 1:
  593. >>> from scipy.stats import multivariate_normal
  594. >>> r = multivariate_normal()
  595. >>> r.mean
  596. array([ 0.])
  597. >>> r.cov
  598. array([[1.]])
  599. """
  600. self._dist = multivariate_normal_gen(seed)
  601. self.dim, self.mean, self.cov = self._dist._process_parameters(
  602. None, mean, cov)
  603. self.cov_info = _PSD(self.cov, allow_singular=allow_singular)
  604. if not maxpts:
  605. maxpts = 1000000 * self.dim
  606. self.maxpts = maxpts
  607. self.abseps = abseps
  608. self.releps = releps
  609. def logpdf(self, x):
  610. x = self._dist._process_quantiles(x, self.dim)
  611. out = self._dist._logpdf(x, self.mean, self.cov_info.U,
  612. self.cov_info.log_pdet, self.cov_info.rank)
  613. return _squeeze_output(out)
  614. def pdf(self, x):
  615. return np.exp(self.logpdf(x))
  616. def logcdf(self, x):
  617. return np.log(self.cdf(x))
  618. def cdf(self, x):
  619. x = self._dist._process_quantiles(x, self.dim)
  620. out = self._dist._cdf(x, self.mean, self.cov, self.maxpts, self.abseps,
  621. self.releps)
  622. return _squeeze_output(out)
  623. def rvs(self, size=1, random_state=None):
  624. return self._dist.rvs(self.mean, self.cov, size, random_state)
  625. def entropy(self):
  626. """
  627. Computes the differential entropy of the multivariate normal.
  628. Returns
  629. -------
  630. h : scalar
  631. Entropy of the multivariate normal distribution
  632. """
  633. log_pdet = self.cov_info.log_pdet
  634. rank = self.cov_info.rank
  635. return 0.5 * (rank * (_LOG_2PI + 1) + log_pdet)
  636. # Set frozen generator docstrings from corresponding docstrings in
  637. # multivariate_normal_gen and fill in default strings in class docstrings
  638. for name in ['logpdf', 'pdf', 'logcdf', 'cdf', 'rvs']:
  639. method = multivariate_normal_gen.__dict__[name]
  640. method_frozen = multivariate_normal_frozen.__dict__[name]
  641. method_frozen.__doc__ = doccer.docformat(method.__doc__,
  642. mvn_docdict_noparams)
  643. method.__doc__ = doccer.docformat(method.__doc__, mvn_docdict_params)
  644. _matnorm_doc_default_callparams = """\
  645. mean : array_like, optional
  646. Mean of the distribution (default: `None`)
  647. rowcov : array_like, optional
  648. Among-row covariance matrix of the distribution (default: `1`)
  649. colcov : array_like, optional
  650. Among-column covariance matrix of the distribution (default: `1`)
  651. """
  652. _matnorm_doc_callparams_note = \
  653. """If `mean` is set to `None` then a matrix of zeros is used for the mean.
  654. The dimensions of this matrix are inferred from the shape of `rowcov` and
  655. `colcov`, if these are provided, or set to `1` if ambiguous.
  656. `rowcov` and `colcov` can be two-dimensional array_likes specifying the
  657. covariance matrices directly. Alternatively, a one-dimensional array will
  658. be be interpreted as the entries of a diagonal matrix, and a scalar or
  659. zero-dimensional array will be interpreted as this value times the
  660. identity matrix.
  661. """
  662. _matnorm_doc_frozen_callparams = ""
  663. _matnorm_doc_frozen_callparams_note = \
  664. """See class definition for a detailed description of parameters."""
  665. matnorm_docdict_params = {
  666. '_matnorm_doc_default_callparams': _matnorm_doc_default_callparams,
  667. '_matnorm_doc_callparams_note': _matnorm_doc_callparams_note,
  668. '_doc_random_state': _doc_random_state
  669. }
  670. matnorm_docdict_noparams = {
  671. '_matnorm_doc_default_callparams': _matnorm_doc_frozen_callparams,
  672. '_matnorm_doc_callparams_note': _matnorm_doc_frozen_callparams_note,
  673. '_doc_random_state': _doc_random_state
  674. }
  675. class matrix_normal_gen(multi_rv_generic):
  676. r"""
  677. A matrix normal random variable.
  678. The `mean` keyword specifies the mean. The `rowcov` keyword specifies the
  679. among-row covariance matrix. The 'colcov' keyword specifies the
  680. among-column covariance matrix.
  681. Methods
  682. -------
  683. ``pdf(X, mean=None, rowcov=1, colcov=1)``
  684. Probability density function.
  685. ``logpdf(X, mean=None, rowcov=1, colcov=1)``
  686. Log of the probability density function.
  687. ``rvs(mean=None, rowcov=1, colcov=1, size=1, random_state=None)``
  688. Draw random samples.
  689. Parameters
  690. ----------
  691. X : array_like
  692. Quantiles, with the last two axes of `X` denoting the components.
  693. %(_matnorm_doc_default_callparams)s
  694. %(_doc_random_state)s
  695. Alternatively, the object may be called (as a function) to fix the mean
  696. and covariance parameters, returning a "frozen" matrix normal
  697. random variable:
  698. rv = matrix_normal(mean=None, rowcov=1, colcov=1)
  699. - Frozen object with the same methods but holding the given
  700. mean and covariance fixed.
  701. Notes
  702. -----
  703. %(_matnorm_doc_callparams_note)s
  704. The covariance matrices specified by `rowcov` and `colcov` must be
  705. (symmetric) positive definite. If the samples in `X` are
  706. :math:`m \times n`, then `rowcov` must be :math:`m \times m` and
  707. `colcov` must be :math:`n \times n`. `mean` must be the same shape as `X`.
  708. The probability density function for `matrix_normal` is
  709. .. math::
  710. f(X) = (2 \pi)^{-\frac{mn}{2}}|U|^{-\frac{n}{2}} |V|^{-\frac{m}{2}}
  711. \exp\left( -\frac{1}{2} \mathrm{Tr}\left[ U^{-1} (X-M) V^{-1}
  712. (X-M)^T \right] \right),
  713. where :math:`M` is the mean, :math:`U` the among-row covariance matrix,
  714. :math:`V` the among-column covariance matrix.
  715. The `allow_singular` behaviour of the `multivariate_normal`
  716. distribution is not currently supported. Covariance matrices must be
  717. full rank.
  718. The `matrix_normal` distribution is closely related to the
  719. `multivariate_normal` distribution. Specifically, :math:`\mathrm{Vec}(X)`
  720. (the vector formed by concatenating the columns of :math:`X`) has a
  721. multivariate normal distribution with mean :math:`\mathrm{Vec}(M)`
  722. and covariance :math:`V \otimes U` (where :math:`\otimes` is the Kronecker
  723. product). Sampling and pdf evaluation are
  724. :math:`\mathcal{O}(m^3 + n^3 + m^2 n + m n^2)` for the matrix normal, but
  725. :math:`\mathcal{O}(m^3 n^3)` for the equivalent multivariate normal,
  726. making this equivalent form algorithmically inefficient.
  727. .. versionadded:: 0.17.0
  728. Examples
  729. --------
  730. >>> from scipy.stats import matrix_normal
  731. >>> M = np.arange(6).reshape(3,2); M
  732. array([[0, 1],
  733. [2, 3],
  734. [4, 5]])
  735. >>> U = np.diag([1,2,3]); U
  736. array([[1, 0, 0],
  737. [0, 2, 0],
  738. [0, 0, 3]])
  739. >>> V = 0.3*np.identity(2); V
  740. array([[ 0.3, 0. ],
  741. [ 0. , 0.3]])
  742. >>> X = M + 0.1; X
  743. array([[ 0.1, 1.1],
  744. [ 2.1, 3.1],
  745. [ 4.1, 5.1]])
  746. >>> matrix_normal.pdf(X, mean=M, rowcov=U, colcov=V)
  747. 0.023410202050005054
  748. >>> # Equivalent multivariate normal
  749. >>> from scipy.stats import multivariate_normal
  750. >>> vectorised_X = X.T.flatten()
  751. >>> equiv_mean = M.T.flatten()
  752. >>> equiv_cov = np.kron(V,U)
  753. >>> multivariate_normal.pdf(vectorised_X, mean=equiv_mean, cov=equiv_cov)
  754. 0.023410202050005054
  755. """
  756. def __init__(self, seed=None):
  757. super(matrix_normal_gen, self).__init__(seed)
  758. self.__doc__ = doccer.docformat(self.__doc__, matnorm_docdict_params)
  759. def __call__(self, mean=None, rowcov=1, colcov=1, seed=None):
  760. """
  761. Create a frozen matrix normal distribution.
  762. See `matrix_normal_frozen` for more information.
  763. """
  764. return matrix_normal_frozen(mean, rowcov, colcov, seed=seed)
  765. def _process_parameters(self, mean, rowcov, colcov):
  766. """
  767. Infer dimensionality from mean or covariance matrices. Handle
  768. defaults. Ensure compatible dimensions.
  769. """
  770. # Process mean
  771. if mean is not None:
  772. mean = np.asarray(mean, dtype=float)
  773. meanshape = mean.shape
  774. if len(meanshape) != 2:
  775. raise ValueError("Array `mean` must be two dimensional.")
  776. if np.any(meanshape == 0):
  777. raise ValueError("Array `mean` has invalid shape.")
  778. # Process among-row covariance
  779. rowcov = np.asarray(rowcov, dtype=float)
  780. if rowcov.ndim == 0:
  781. if mean is not None:
  782. rowcov = rowcov * np.identity(meanshape[0])
  783. else:
  784. rowcov = rowcov * np.identity(1)
  785. elif rowcov.ndim == 1:
  786. rowcov = np.diag(rowcov)
  787. rowshape = rowcov.shape
  788. if len(rowshape) != 2:
  789. raise ValueError("`rowcov` must be a scalar or a 2D array.")
  790. if rowshape[0] != rowshape[1]:
  791. raise ValueError("Array `rowcov` must be square.")
  792. if rowshape[0] == 0:
  793. raise ValueError("Array `rowcov` has invalid shape.")
  794. numrows = rowshape[0]
  795. # Process among-column covariance
  796. colcov = np.asarray(colcov, dtype=float)
  797. if colcov.ndim == 0:
  798. if mean is not None:
  799. colcov = colcov * np.identity(meanshape[1])
  800. else:
  801. colcov = colcov * np.identity(1)
  802. elif colcov.ndim == 1:
  803. colcov = np.diag(colcov)
  804. colshape = colcov.shape
  805. if len(colshape) != 2:
  806. raise ValueError("`colcov` must be a scalar or a 2D array.")
  807. if colshape[0] != colshape[1]:
  808. raise ValueError("Array `colcov` must be square.")
  809. if colshape[0] == 0:
  810. raise ValueError("Array `colcov` has invalid shape.")
  811. numcols = colshape[0]
  812. # Ensure mean and covariances compatible
  813. if mean is not None:
  814. if meanshape[0] != numrows:
  815. raise ValueError("Arrays `mean` and `rowcov` must have the "
  816. "same number of rows.")
  817. if meanshape[1] != numcols:
  818. raise ValueError("Arrays `mean` and `colcov` must have the "
  819. "same number of columns.")
  820. else:
  821. mean = np.zeros((numrows, numcols))
  822. dims = (numrows, numcols)
  823. return dims, mean, rowcov, colcov
  824. def _process_quantiles(self, X, dims):
  825. """
  826. Adjust quantiles array so that last two axes labels the components of
  827. each data point.
  828. """
  829. X = np.asarray(X, dtype=float)
  830. if X.ndim == 2:
  831. X = X[np.newaxis, :]
  832. if X.shape[-2:] != dims:
  833. raise ValueError("The shape of array `X` is not compatible "
  834. "with the distribution parameters.")
  835. return X
  836. def _logpdf(self, dims, X, mean, row_prec_rt, log_det_rowcov,
  837. col_prec_rt, log_det_colcov):
  838. """
  839. Parameters
  840. ----------
  841. dims : tuple
  842. Dimensions of the matrix variates
  843. X : ndarray
  844. Points at which to evaluate the log of the probability
  845. density function
  846. mean : ndarray
  847. Mean of the distribution
  848. row_prec_rt : ndarray
  849. A decomposition such that np.dot(row_prec_rt, row_prec_rt.T)
  850. is the inverse of the among-row covariance matrix
  851. log_det_rowcov : float
  852. Logarithm of the determinant of the among-row covariance matrix
  853. col_prec_rt : ndarray
  854. A decomposition such that np.dot(col_prec_rt, col_prec_rt.T)
  855. is the inverse of the among-column covariance matrix
  856. log_det_colcov : float
  857. Logarithm of the determinant of the among-column covariance matrix
  858. Notes
  859. -----
  860. As this function does no argument checking, it should not be
  861. called directly; use 'logpdf' instead.
  862. """
  863. numrows, numcols = dims
  864. roll_dev = np.rollaxis(X-mean, axis=-1, start=0)
  865. scale_dev = np.tensordot(col_prec_rt.T,
  866. np.dot(roll_dev, row_prec_rt), 1)
  867. maha = np.sum(np.sum(np.square(scale_dev), axis=-1), axis=0)
  868. return -0.5 * (numrows*numcols*_LOG_2PI + numcols*log_det_rowcov
  869. + numrows*log_det_colcov + maha)
  870. def logpdf(self, X, mean=None, rowcov=1, colcov=1):
  871. """
  872. Log of the matrix normal probability density function.
  873. Parameters
  874. ----------
  875. X : array_like
  876. Quantiles, with the last two axes of `X` denoting the components.
  877. %(_matnorm_doc_default_callparams)s
  878. Returns
  879. -------
  880. logpdf : ndarray
  881. Log of the probability density function evaluated at `X`
  882. Notes
  883. -----
  884. %(_matnorm_doc_callparams_note)s
  885. """
  886. dims, mean, rowcov, colcov = self._process_parameters(mean, rowcov,
  887. colcov)
  888. X = self._process_quantiles(X, dims)
  889. rowpsd = _PSD(rowcov, allow_singular=False)
  890. colpsd = _PSD(colcov, allow_singular=False)
  891. out = self._logpdf(dims, X, mean, rowpsd.U, rowpsd.log_pdet, colpsd.U,
  892. colpsd.log_pdet)
  893. return _squeeze_output(out)
  894. def pdf(self, X, mean=None, rowcov=1, colcov=1):
  895. """
  896. Matrix normal probability density function.
  897. Parameters
  898. ----------
  899. X : array_like
  900. Quantiles, with the last two axes of `X` denoting the components.
  901. %(_matnorm_doc_default_callparams)s
  902. Returns
  903. -------
  904. pdf : ndarray
  905. Probability density function evaluated at `X`
  906. Notes
  907. -----
  908. %(_matnorm_doc_callparams_note)s
  909. """
  910. return np.exp(self.logpdf(X, mean, rowcov, colcov))
  911. def rvs(self, mean=None, rowcov=1, colcov=1, size=1, random_state=None):
  912. """
  913. Draw random samples from a matrix normal distribution.
  914. Parameters
  915. ----------
  916. %(_matnorm_doc_default_callparams)s
  917. size : integer, optional
  918. Number of samples to draw (default 1).
  919. %(_doc_random_state)s
  920. Returns
  921. -------
  922. rvs : ndarray or scalar
  923. Random variates of size (`size`, `dims`), where `dims` is the
  924. dimension of the random matrices.
  925. Notes
  926. -----
  927. %(_matnorm_doc_callparams_note)s
  928. """
  929. size = int(size)
  930. dims, mean, rowcov, colcov = self._process_parameters(mean, rowcov,
  931. colcov)
  932. rowchol = scipy.linalg.cholesky(rowcov, lower=True)
  933. colchol = scipy.linalg.cholesky(colcov, lower=True)
  934. random_state = self._get_random_state(random_state)
  935. std_norm = random_state.standard_normal(size=(dims[1], size, dims[0]))
  936. roll_rvs = np.tensordot(colchol, np.dot(std_norm, rowchol.T), 1)
  937. out = np.rollaxis(roll_rvs.T, axis=1, start=0) + mean[np.newaxis, :, :]
  938. if size == 1:
  939. out = out.reshape(mean.shape)
  940. return out
  941. matrix_normal = matrix_normal_gen()
  942. class matrix_normal_frozen(multi_rv_frozen):
  943. def __init__(self, mean=None, rowcov=1, colcov=1, seed=None):
  944. """
  945. Create a frozen matrix normal distribution.
  946. Parameters
  947. ----------
  948. %(_matnorm_doc_default_callparams)s
  949. seed : None or int or np.random.RandomState instance, optional
  950. If int or RandomState, use it for drawing the random variates.
  951. If None (or np.random), the global np.random state is used.
  952. Default is None.
  953. Examples
  954. --------
  955. >>> from scipy.stats import matrix_normal
  956. >>> distn = matrix_normal(mean=np.zeros((3,3)))
  957. >>> X = distn.rvs(); X
  958. array([[-0.02976962, 0.93339138, -0.09663178],
  959. [ 0.67405524, 0.28250467, -0.93308929],
  960. [-0.31144782, 0.74535536, 1.30412916]])
  961. >>> distn.pdf(X)
  962. 2.5160642368346784e-05
  963. >>> distn.logpdf(X)
  964. -10.590229595124615
  965. """
  966. self._dist = matrix_normal_gen(seed)
  967. self.dims, self.mean, self.rowcov, self.colcov = \
  968. self._dist._process_parameters(mean, rowcov, colcov)
  969. self.rowpsd = _PSD(self.rowcov, allow_singular=False)
  970. self.colpsd = _PSD(self.colcov, allow_singular=False)
  971. def logpdf(self, X):
  972. X = self._dist._process_quantiles(X, self.dims)
  973. out = self._dist._logpdf(self.dims, X, self.mean, self.rowpsd.U,
  974. self.rowpsd.log_pdet, self.colpsd.U,
  975. self.colpsd.log_pdet)
  976. return _squeeze_output(out)
  977. def pdf(self, X):
  978. return np.exp(self.logpdf(X))
  979. def rvs(self, size=1, random_state=None):
  980. return self._dist.rvs(self.mean, self.rowcov, self.colcov, size,
  981. random_state)
  982. # Set frozen generator docstrings from corresponding docstrings in
  983. # matrix_normal_gen and fill in default strings in class docstrings
  984. for name in ['logpdf', 'pdf', 'rvs']:
  985. method = matrix_normal_gen.__dict__[name]
  986. method_frozen = matrix_normal_frozen.__dict__[name]
  987. method_frozen.__doc__ = doccer.docformat(method.__doc__,
  988. matnorm_docdict_noparams)
  989. method.__doc__ = doccer.docformat(method.__doc__, matnorm_docdict_params)
  990. _dirichlet_doc_default_callparams = """\
  991. alpha : array_like
  992. The concentration parameters. The number of entries determines the
  993. dimensionality of the distribution.
  994. """
  995. _dirichlet_doc_frozen_callparams = ""
  996. _dirichlet_doc_frozen_callparams_note = \
  997. """See class definition for a detailed description of parameters."""
  998. dirichlet_docdict_params = {
  999. '_dirichlet_doc_default_callparams': _dirichlet_doc_default_callparams,
  1000. '_doc_random_state': _doc_random_state
  1001. }
  1002. dirichlet_docdict_noparams = {
  1003. '_dirichlet_doc_default_callparams': _dirichlet_doc_frozen_callparams,
  1004. '_doc_random_state': _doc_random_state
  1005. }
  1006. def _dirichlet_check_parameters(alpha):
  1007. alpha = np.asarray(alpha)
  1008. if np.min(alpha) <= 0:
  1009. raise ValueError("All parameters must be greater than 0")
  1010. elif alpha.ndim != 1:
  1011. raise ValueError("Parameter vector 'a' must be one dimensional, "
  1012. "but a.shape = %s." % (alpha.shape, ))
  1013. return alpha
  1014. def _dirichlet_check_input(alpha, x):
  1015. x = np.asarray(x)
  1016. if x.shape[0] + 1 != alpha.shape[0] and x.shape[0] != alpha.shape[0]:
  1017. raise ValueError("Vector 'x' must have either the same number "
  1018. "of entries as, or one entry fewer than, "
  1019. "parameter vector 'a', but alpha.shape = %s "
  1020. "and x.shape = %s." % (alpha.shape, x.shape))
  1021. if x.shape[0] != alpha.shape[0]:
  1022. xk = np.array([1 - np.sum(x, 0)])
  1023. if xk.ndim == 1:
  1024. x = np.append(x, xk)
  1025. elif xk.ndim == 2:
  1026. x = np.vstack((x, xk))
  1027. else:
  1028. raise ValueError("The input must be one dimensional or a two "
  1029. "dimensional matrix containing the entries.")
  1030. if np.min(x) < 0:
  1031. raise ValueError("Each entry in 'x' must be greater than or equal "
  1032. "to zero.")
  1033. if np.max(x) > 1:
  1034. raise ValueError("Each entry in 'x' must be smaller or equal one.")
  1035. # Check x_i > 0 or alpha_i > 1
  1036. xeq0 = (x == 0)
  1037. alphalt1 = (alpha < 1)
  1038. if x.shape != alpha.shape:
  1039. alphalt1 = np.repeat(alphalt1, x.shape[-1], axis=-1).reshape(x.shape)
  1040. chk = np.logical_and(xeq0, alphalt1)
  1041. if np.sum(chk):
  1042. raise ValueError("Each entry in 'x' must be greater than zero if its "
  1043. "alpha is less than one.")
  1044. if (np.abs(np.sum(x, 0) - 1.0) > 10e-10).any():
  1045. raise ValueError("The input vector 'x' must lie within the normal "
  1046. "simplex. but np.sum(x, 0) = %s." % np.sum(x, 0))
  1047. return x
  1048. def _lnB(alpha):
  1049. r"""
  1050. Internal helper function to compute the log of the useful quotient
  1051. .. math::
  1052. B(\alpha) = \frac{\prod_{i=1}{K}\Gamma(\alpha_i)}
  1053. {\Gamma\left(\sum_{i=1}^{K} \alpha_i \right)}
  1054. Parameters
  1055. ----------
  1056. %(_dirichlet_doc_default_callparams)s
  1057. Returns
  1058. -------
  1059. B : scalar
  1060. Helper quotient, internal use only
  1061. """
  1062. return np.sum(gammaln(alpha)) - gammaln(np.sum(alpha))
  1063. class dirichlet_gen(multi_rv_generic):
  1064. r"""
  1065. A Dirichlet random variable.
  1066. The `alpha` keyword specifies the concentration parameters of the
  1067. distribution.
  1068. .. versionadded:: 0.15.0
  1069. Methods
  1070. -------
  1071. ``pdf(x, alpha)``
  1072. Probability density function.
  1073. ``logpdf(x, alpha)``
  1074. Log of the probability density function.
  1075. ``rvs(alpha, size=1, random_state=None)``
  1076. Draw random samples from a Dirichlet distribution.
  1077. ``mean(alpha)``
  1078. The mean of the Dirichlet distribution
  1079. ``var(alpha)``
  1080. The variance of the Dirichlet distribution
  1081. ``entropy(alpha)``
  1082. Compute the differential entropy of the Dirichlet distribution.
  1083. Parameters
  1084. ----------
  1085. x : array_like
  1086. Quantiles, with the last axis of `x` denoting the components.
  1087. %(_dirichlet_doc_default_callparams)s
  1088. %(_doc_random_state)s
  1089. Alternatively, the object may be called (as a function) to fix
  1090. concentration parameters, returning a "frozen" Dirichlet
  1091. random variable:
  1092. rv = dirichlet(alpha)
  1093. - Frozen object with the same methods but holding the given
  1094. concentration parameters fixed.
  1095. Notes
  1096. -----
  1097. Each :math:`\alpha` entry must be positive. The distribution has only
  1098. support on the simplex defined by
  1099. .. math::
  1100. \sum_{i=1}^{K} x_i \le 1
  1101. The probability density function for `dirichlet` is
  1102. .. math::
  1103. f(x) = \frac{1}{\mathrm{B}(\boldsymbol\alpha)} \prod_{i=1}^K x_i^{\alpha_i - 1}
  1104. where
  1105. .. math::
  1106. \mathrm{B}(\boldsymbol\alpha) = \frac{\prod_{i=1}^K \Gamma(\alpha_i)}
  1107. {\Gamma\bigl(\sum_{i=1}^K \alpha_i\bigr)}
  1108. and :math:`\boldsymbol\alpha=(\alpha_1,\ldots,\alpha_K)`, the
  1109. concentration parameters and :math:`K` is the dimension of the space
  1110. where :math:`x` takes values.
  1111. Note that the dirichlet interface is somewhat inconsistent.
  1112. The array returned by the rvs function is transposed
  1113. with respect to the format expected by the pdf and logpdf.
  1114. Examples
  1115. --------
  1116. >>> from scipy.stats import dirichlet
  1117. Generate a dirichlet random variable
  1118. >>> quantiles = np.array([0.2, 0.2, 0.6]) # specify quantiles
  1119. >>> alpha = np.array([0.4, 5, 15]) # specify concentration parameters
  1120. >>> dirichlet.pdf(quantiles, alpha)
  1121. 0.2843831684937255
  1122. The same PDF but following a log scale
  1123. >>> dirichlet.logpdf(quantiles, alpha)
  1124. -1.2574327653159187
  1125. Once we specify the dirichlet distribution
  1126. we can then calculate quantities of interest
  1127. >>> dirichlet.mean(alpha) # get the mean of the distribution
  1128. array([0.01960784, 0.24509804, 0.73529412])
  1129. >>> dirichlet.var(alpha) # get variance
  1130. array([0.00089829, 0.00864603, 0.00909517])
  1131. >>> dirichlet.entropy(alpha) # calculate the differential entropy
  1132. -4.3280162474082715
  1133. We can also return random samples from the distribution
  1134. >>> dirichlet.rvs(alpha, size=1, random_state=1)
  1135. array([[0.00766178, 0.24670518, 0.74563305]])
  1136. >>> dirichlet.rvs(alpha, size=2, random_state=2)
  1137. array([[0.01639427, 0.1292273 , 0.85437844],
  1138. [0.00156917, 0.19033695, 0.80809388]])
  1139. """
  1140. def __init__(self, seed=None):
  1141. super(dirichlet_gen, self).__init__(seed)
  1142. self.__doc__ = doccer.docformat(self.__doc__, dirichlet_docdict_params)
  1143. def __call__(self, alpha, seed=None):
  1144. return dirichlet_frozen(alpha, seed=seed)
  1145. def _logpdf(self, x, alpha):
  1146. """
  1147. Parameters
  1148. ----------
  1149. x : ndarray
  1150. Points at which to evaluate the log of the probability
  1151. density function
  1152. %(_dirichlet_doc_default_callparams)s
  1153. Notes
  1154. -----
  1155. As this function does no argument checking, it should not be
  1156. called directly; use 'logpdf' instead.
  1157. """
  1158. lnB = _lnB(alpha)
  1159. return - lnB + np.sum((xlogy(alpha - 1, x.T)).T, 0)
  1160. def logpdf(self, x, alpha):
  1161. """
  1162. Log of the Dirichlet probability density function.
  1163. Parameters
  1164. ----------
  1165. x : array_like
  1166. Quantiles, with the last axis of `x` denoting the components.
  1167. %(_dirichlet_doc_default_callparams)s
  1168. Returns
  1169. -------
  1170. pdf : ndarray or scalar
  1171. Log of the probability density function evaluated at `x`.
  1172. """
  1173. alpha = _dirichlet_check_parameters(alpha)
  1174. x = _dirichlet_check_input(alpha, x)
  1175. out = self._logpdf(x, alpha)
  1176. return _squeeze_output(out)
  1177. def pdf(self, x, alpha):
  1178. """
  1179. The Dirichlet probability density function.
  1180. Parameters
  1181. ----------
  1182. x : array_like
  1183. Quantiles, with the last axis of `x` denoting the components.
  1184. %(_dirichlet_doc_default_callparams)s
  1185. Returns
  1186. -------
  1187. pdf : ndarray or scalar
  1188. The probability density function evaluated at `x`.
  1189. """
  1190. alpha = _dirichlet_check_parameters(alpha)
  1191. x = _dirichlet_check_input(alpha, x)
  1192. out = np.exp(self._logpdf(x, alpha))
  1193. return _squeeze_output(out)
  1194. def mean(self, alpha):
  1195. """
  1196. Compute the mean of the dirichlet distribution.
  1197. Parameters
  1198. ----------
  1199. %(_dirichlet_doc_default_callparams)s
  1200. Returns
  1201. -------
  1202. mu : ndarray or scalar
  1203. Mean of the Dirichlet distribution.
  1204. """
  1205. alpha = _dirichlet_check_parameters(alpha)
  1206. out = alpha / (np.sum(alpha))
  1207. return _squeeze_output(out)
  1208. def var(self, alpha):
  1209. """
  1210. Compute the variance of the dirichlet distribution.
  1211. Parameters
  1212. ----------
  1213. %(_dirichlet_doc_default_callparams)s
  1214. Returns
  1215. -------
  1216. v : ndarray or scalar
  1217. Variance of the Dirichlet distribution.
  1218. """
  1219. alpha = _dirichlet_check_parameters(alpha)
  1220. alpha0 = np.sum(alpha)
  1221. out = (alpha * (alpha0 - alpha)) / ((alpha0 * alpha0) * (alpha0 + 1))
  1222. return _squeeze_output(out)
  1223. def entropy(self, alpha):
  1224. """
  1225. Compute the differential entropy of the dirichlet distribution.
  1226. Parameters
  1227. ----------
  1228. %(_dirichlet_doc_default_callparams)s
  1229. Returns
  1230. -------
  1231. h : scalar
  1232. Entropy of the Dirichlet distribution
  1233. """
  1234. alpha = _dirichlet_check_parameters(alpha)
  1235. alpha0 = np.sum(alpha)
  1236. lnB = _lnB(alpha)
  1237. K = alpha.shape[0]
  1238. out = lnB + (alpha0 - K) * scipy.special.psi(alpha0) - np.sum(
  1239. (alpha - 1) * scipy.special.psi(alpha))
  1240. return _squeeze_output(out)
  1241. def rvs(self, alpha, size=1, random_state=None):
  1242. """
  1243. Draw random samples from a Dirichlet distribution.
  1244. Parameters
  1245. ----------
  1246. %(_dirichlet_doc_default_callparams)s
  1247. size : int, optional
  1248. Number of samples to draw (default 1).
  1249. %(_doc_random_state)s
  1250. Returns
  1251. -------
  1252. rvs : ndarray or scalar
  1253. Random variates of size (`size`, `N`), where `N` is the
  1254. dimension of the random variable.
  1255. """
  1256. alpha = _dirichlet_check_parameters(alpha)
  1257. random_state = self._get_random_state(random_state)
  1258. return random_state.dirichlet(alpha, size=size)
  1259. dirichlet = dirichlet_gen()
  1260. class dirichlet_frozen(multi_rv_frozen):
  1261. def __init__(self, alpha, seed=None):
  1262. self.alpha = _dirichlet_check_parameters(alpha)
  1263. self._dist = dirichlet_gen(seed)
  1264. def logpdf(self, x):
  1265. return self._dist.logpdf(x, self.alpha)
  1266. def pdf(self, x):
  1267. return self._dist.pdf(x, self.alpha)
  1268. def mean(self):
  1269. return self._dist.mean(self.alpha)
  1270. def var(self):
  1271. return self._dist.var(self.alpha)
  1272. def entropy(self):
  1273. return self._dist.entropy(self.alpha)
  1274. def rvs(self, size=1, random_state=None):
  1275. return self._dist.rvs(self.alpha, size, random_state)
  1276. # Set frozen generator docstrings from corresponding docstrings in
  1277. # multivariate_normal_gen and fill in default strings in class docstrings
  1278. for name in ['logpdf', 'pdf', 'rvs', 'mean', 'var', 'entropy']:
  1279. method = dirichlet_gen.__dict__[name]
  1280. method_frozen = dirichlet_frozen.__dict__[name]
  1281. method_frozen.__doc__ = doccer.docformat(
  1282. method.__doc__, dirichlet_docdict_noparams)
  1283. method.__doc__ = doccer.docformat(method.__doc__, dirichlet_docdict_params)
  1284. _wishart_doc_default_callparams = """\
  1285. df : int
  1286. Degrees of freedom, must be greater than or equal to dimension of the
  1287. scale matrix
  1288. scale : array_like
  1289. Symmetric positive definite scale matrix of the distribution
  1290. """
  1291. _wishart_doc_callparams_note = ""
  1292. _wishart_doc_frozen_callparams = ""
  1293. _wishart_doc_frozen_callparams_note = \
  1294. """See class definition for a detailed description of parameters."""
  1295. wishart_docdict_params = {
  1296. '_doc_default_callparams': _wishart_doc_default_callparams,
  1297. '_doc_callparams_note': _wishart_doc_callparams_note,
  1298. '_doc_random_state': _doc_random_state
  1299. }
  1300. wishart_docdict_noparams = {
  1301. '_doc_default_callparams': _wishart_doc_frozen_callparams,
  1302. '_doc_callparams_note': _wishart_doc_frozen_callparams_note,
  1303. '_doc_random_state': _doc_random_state
  1304. }
  1305. class wishart_gen(multi_rv_generic):
  1306. r"""
  1307. A Wishart random variable.
  1308. The `df` keyword specifies the degrees of freedom. The `scale` keyword
  1309. specifies the scale matrix, which must be symmetric and positive definite.
  1310. In this context, the scale matrix is often interpreted in terms of a
  1311. multivariate normal precision matrix (the inverse of the covariance
  1312. matrix).
  1313. Methods
  1314. -------
  1315. ``pdf(x, df, scale)``
  1316. Probability density function.
  1317. ``logpdf(x, df, scale)``
  1318. Log of the probability density function.
  1319. ``rvs(df, scale, size=1, random_state=None)``
  1320. Draw random samples from a Wishart distribution.
  1321. ``entropy()``
  1322. Compute the differential entropy of the Wishart distribution.
  1323. Parameters
  1324. ----------
  1325. x : array_like
  1326. Quantiles, with the last axis of `x` denoting the components.
  1327. %(_doc_default_callparams)s
  1328. %(_doc_random_state)s
  1329. Alternatively, the object may be called (as a function) to fix the degrees
  1330. of freedom and scale parameters, returning a "frozen" Wishart random
  1331. variable:
  1332. rv = wishart(df=1, scale=1)
  1333. - Frozen object with the same methods but holding the given
  1334. degrees of freedom and scale fixed.
  1335. See Also
  1336. --------
  1337. invwishart, chi2
  1338. Notes
  1339. -----
  1340. %(_doc_callparams_note)s
  1341. The scale matrix `scale` must be a symmetric positive definite
  1342. matrix. Singular matrices, including the symmetric positive semi-definite
  1343. case, are not supported.
  1344. The Wishart distribution is often denoted
  1345. .. math::
  1346. W_p(\nu, \Sigma)
  1347. where :math:`\nu` is the degrees of freedom and :math:`\Sigma` is the
  1348. :math:`p \times p` scale matrix.
  1349. The probability density function for `wishart` has support over positive
  1350. definite matrices :math:`S`; if :math:`S \sim W_p(\nu, \Sigma)`, then
  1351. its PDF is given by:
  1352. .. math::
  1353. f(S) = \frac{|S|^{\frac{\nu - p - 1}{2}}}{2^{ \frac{\nu p}{2} }
  1354. |\Sigma|^\frac{\nu}{2} \Gamma_p \left ( \frac{\nu}{2} \right )}
  1355. \exp\left( -tr(\Sigma^{-1} S) / 2 \right)
  1356. If :math:`S \sim W_p(\nu, \Sigma)` (Wishart) then
  1357. :math:`S^{-1} \sim W_p^{-1}(\nu, \Sigma^{-1})` (inverse Wishart).
  1358. If the scale matrix is 1-dimensional and equal to one, then the Wishart
  1359. distribution :math:`W_1(\nu, 1)` collapses to the :math:`\chi^2(\nu)`
  1360. distribution.
  1361. .. versionadded:: 0.16.0
  1362. References
  1363. ----------
  1364. .. [1] M.L. Eaton, "Multivariate Statistics: A Vector Space Approach",
  1365. Wiley, 1983.
  1366. .. [2] W.B. Smith and R.R. Hocking, "Algorithm AS 53: Wishart Variate
  1367. Generator", Applied Statistics, vol. 21, pp. 341-345, 1972.
  1368. Examples
  1369. --------
  1370. >>> import matplotlib.pyplot as plt
  1371. >>> from scipy.stats import wishart, chi2
  1372. >>> x = np.linspace(1e-5, 8, 100)
  1373. >>> w = wishart.pdf(x, df=3, scale=1); w[:5]
  1374. array([ 0.00126156, 0.10892176, 0.14793434, 0.17400548, 0.1929669 ])
  1375. >>> c = chi2.pdf(x, 3); c[:5]
  1376. array([ 0.00126156, 0.10892176, 0.14793434, 0.17400548, 0.1929669 ])
  1377. >>> plt.plot(x, w)
  1378. The input quantiles can be any shape of array, as long as the last
  1379. axis labels the components.
  1380. """
  1381. def __init__(self, seed=None):
  1382. super(wishart_gen, self).__init__(seed)
  1383. self.__doc__ = doccer.docformat(self.__doc__, wishart_docdict_params)
  1384. def __call__(self, df=None, scale=None, seed=None):
  1385. """
  1386. Create a frozen Wishart distribution.
  1387. See `wishart_frozen` for more information.
  1388. """
  1389. return wishart_frozen(df, scale, seed)
  1390. def _process_parameters(self, df, scale):
  1391. if scale is None:
  1392. scale = 1.0
  1393. scale = np.asarray(scale, dtype=float)
  1394. if scale.ndim == 0:
  1395. scale = scale[np.newaxis, np.newaxis]
  1396. elif scale.ndim == 1:
  1397. scale = np.diag(scale)
  1398. elif scale.ndim == 2 and not scale.shape[0] == scale.shape[1]:
  1399. raise ValueError("Array 'scale' must be square if it is two"
  1400. " dimensional, but scale.scale = %s."
  1401. % str(scale.shape))
  1402. elif scale.ndim > 2:
  1403. raise ValueError("Array 'scale' must be at most two-dimensional,"
  1404. " but scale.ndim = %d" % scale.ndim)
  1405. dim = scale.shape[0]
  1406. if df is None:
  1407. df = dim
  1408. elif not np.isscalar(df):
  1409. raise ValueError("Degrees of freedom must be a scalar.")
  1410. elif df < dim:
  1411. raise ValueError("Degrees of freedom cannot be less than dimension"
  1412. " of scale matrix, but df = %d" % df)
  1413. return dim, df, scale
  1414. def _process_quantiles(self, x, dim):
  1415. """
  1416. Adjust quantiles array so that last axis labels the components of
  1417. each data point.
  1418. """
  1419. x = np.asarray(x, dtype=float)
  1420. if x.ndim == 0:
  1421. x = x * np.eye(dim)[:, :, np.newaxis]
  1422. if x.ndim == 1:
  1423. if dim == 1:
  1424. x = x[np.newaxis, np.newaxis, :]
  1425. else:
  1426. x = np.diag(x)[:, :, np.newaxis]
  1427. elif x.ndim == 2:
  1428. if not x.shape[0] == x.shape[1]:
  1429. raise ValueError("Quantiles must be square if they are two"
  1430. " dimensional, but x.shape = %s."
  1431. % str(x.shape))
  1432. x = x[:, :, np.newaxis]
  1433. elif x.ndim == 3:
  1434. if not x.shape[0] == x.shape[1]:
  1435. raise ValueError("Quantiles must be square in the first two"
  1436. " dimensions if they are three dimensional"
  1437. ", but x.shape = %s." % str(x.shape))
  1438. elif x.ndim > 3:
  1439. raise ValueError("Quantiles must be at most two-dimensional with"
  1440. " an additional dimension for multiple"
  1441. "components, but x.ndim = %d" % x.ndim)
  1442. # Now we have 3-dim array; should have shape [dim, dim, *]
  1443. if not x.shape[0:2] == (dim, dim):
  1444. raise ValueError('Quantiles have incompatible dimensions: should'
  1445. ' be %s, got %s.' % ((dim, dim), x.shape[0:2]))
  1446. return x
  1447. def _process_size(self, size):
  1448. size = np.asarray(size)
  1449. if size.ndim == 0:
  1450. size = size[np.newaxis]
  1451. elif size.ndim > 1:
  1452. raise ValueError('Size must be an integer or tuple of integers;'
  1453. ' thus must have dimension <= 1.'
  1454. ' Got size.ndim = %s' % str(tuple(size)))
  1455. n = size.prod()
  1456. shape = tuple(size)
  1457. return n, shape
  1458. def _logpdf(self, x, dim, df, scale, log_det_scale, C):
  1459. """
  1460. Parameters
  1461. ----------
  1462. x : ndarray
  1463. Points at which to evaluate the log of the probability
  1464. density function
  1465. dim : int
  1466. Dimension of the scale matrix
  1467. df : int
  1468. Degrees of freedom
  1469. scale : ndarray
  1470. Scale matrix
  1471. log_det_scale : float
  1472. Logarithm of the determinant of the scale matrix
  1473. C : ndarray
  1474. Cholesky factorization of the scale matrix, lower triagular.
  1475. Notes
  1476. -----
  1477. As this function does no argument checking, it should not be
  1478. called directly; use 'logpdf' instead.
  1479. """
  1480. # log determinant of x
  1481. # Note: x has components along the last axis, so that x.T has
  1482. # components alone the 0-th axis. Then since det(A) = det(A'), this
  1483. # gives us a 1-dim vector of determinants
  1484. # Retrieve tr(scale^{-1} x)
  1485. log_det_x = np.zeros(x.shape[-1])
  1486. scale_inv_x = np.zeros(x.shape)
  1487. tr_scale_inv_x = np.zeros(x.shape[-1])
  1488. for i in range(x.shape[-1]):
  1489. _, log_det_x[i] = self._cholesky_logdet(x[:, :, i])
  1490. scale_inv_x[:, :, i] = scipy.linalg.cho_solve((C, True), x[:, :, i])
  1491. tr_scale_inv_x[i] = scale_inv_x[:, :, i].trace()
  1492. # Log PDF
  1493. out = ((0.5 * (df - dim - 1) * log_det_x - 0.5 * tr_scale_inv_x) -
  1494. (0.5 * df * dim * _LOG_2 + 0.5 * df * log_det_scale +
  1495. multigammaln(0.5*df, dim)))
  1496. return out
  1497. def logpdf(self, x, df, scale):
  1498. """
  1499. Log of the Wishart probability density function.
  1500. Parameters
  1501. ----------
  1502. x : array_like
  1503. Quantiles, with the last axis of `x` denoting the components.
  1504. Each quantile must be a symmetric positive definite matrix.
  1505. %(_doc_default_callparams)s
  1506. Returns
  1507. -------
  1508. pdf : ndarray
  1509. Log of the probability density function evaluated at `x`
  1510. Notes
  1511. -----
  1512. %(_doc_callparams_note)s
  1513. """
  1514. dim, df, scale = self._process_parameters(df, scale)
  1515. x = self._process_quantiles(x, dim)
  1516. # Cholesky decomposition of scale, get log(det(scale))
  1517. C, log_det_scale = self._cholesky_logdet(scale)
  1518. out = self._logpdf(x, dim, df, scale, log_det_scale, C)
  1519. return _squeeze_output(out)
  1520. def pdf(self, x, df, scale):
  1521. """
  1522. Wishart probability density function.
  1523. Parameters
  1524. ----------
  1525. x : array_like
  1526. Quantiles, with the last axis of `x` denoting the components.
  1527. Each quantile must be a symmetric positive definite matrix.
  1528. %(_doc_default_callparams)s
  1529. Returns
  1530. -------
  1531. pdf : ndarray
  1532. Probability density function evaluated at `x`
  1533. Notes
  1534. -----
  1535. %(_doc_callparams_note)s
  1536. """
  1537. return np.exp(self.logpdf(x, df, scale))
  1538. def _mean(self, dim, df, scale):
  1539. """
  1540. Parameters
  1541. ----------
  1542. dim : int
  1543. Dimension of the scale matrix
  1544. %(_doc_default_callparams)s
  1545. Notes
  1546. -----
  1547. As this function does no argument checking, it should not be
  1548. called directly; use 'mean' instead.
  1549. """
  1550. return df * scale
  1551. def mean(self, df, scale):
  1552. """
  1553. Mean of the Wishart distribution
  1554. Parameters
  1555. ----------
  1556. %(_doc_default_callparams)s
  1557. Returns
  1558. -------
  1559. mean : float
  1560. The mean of the distribution
  1561. """
  1562. dim, df, scale = self._process_parameters(df, scale)
  1563. out = self._mean(dim, df, scale)
  1564. return _squeeze_output(out)
  1565. def _mode(self, dim, df, scale):
  1566. """
  1567. Parameters
  1568. ----------
  1569. dim : int
  1570. Dimension of the scale matrix
  1571. %(_doc_default_callparams)s
  1572. Notes
  1573. -----
  1574. As this function does no argument checking, it should not be
  1575. called directly; use 'mode' instead.
  1576. """
  1577. if df >= dim + 1:
  1578. out = (df-dim-1) * scale
  1579. else:
  1580. out = None
  1581. return out
  1582. def mode(self, df, scale):
  1583. """
  1584. Mode of the Wishart distribution
  1585. Only valid if the degrees of freedom are greater than the dimension of
  1586. the scale matrix.
  1587. Parameters
  1588. ----------
  1589. %(_doc_default_callparams)s
  1590. Returns
  1591. -------
  1592. mode : float or None
  1593. The Mode of the distribution
  1594. """
  1595. dim, df, scale = self._process_parameters(df, scale)
  1596. out = self._mode(dim, df, scale)
  1597. return _squeeze_output(out) if out is not None else out
  1598. def _var(self, dim, df, scale):
  1599. """
  1600. Parameters
  1601. ----------
  1602. dim : int
  1603. Dimension of the scale matrix
  1604. %(_doc_default_callparams)s
  1605. Notes
  1606. -----
  1607. As this function does no argument checking, it should not be
  1608. called directly; use 'var' instead.
  1609. """
  1610. var = scale**2
  1611. diag = scale.diagonal() # 1 x dim array
  1612. var += np.outer(diag, diag)
  1613. var *= df
  1614. return var
  1615. def var(self, df, scale):
  1616. """
  1617. Variance of the Wishart distribution
  1618. Parameters
  1619. ----------
  1620. %(_doc_default_callparams)s
  1621. Returns
  1622. -------
  1623. var : float
  1624. The variance of the distribution
  1625. """
  1626. dim, df, scale = self._process_parameters(df, scale)
  1627. out = self._var(dim, df, scale)
  1628. return _squeeze_output(out)
  1629. def _standard_rvs(self, n, shape, dim, df, random_state):
  1630. """
  1631. Parameters
  1632. ----------
  1633. n : integer
  1634. Number of variates to generate
  1635. shape : iterable
  1636. Shape of the variates to generate
  1637. dim : int
  1638. Dimension of the scale matrix
  1639. df : int
  1640. Degrees of freedom
  1641. random_state : np.random.RandomState instance
  1642. RandomState used for drawing the random variates.
  1643. Notes
  1644. -----
  1645. As this function does no argument checking, it should not be
  1646. called directly; use 'rvs' instead.
  1647. """
  1648. # Random normal variates for off-diagonal elements
  1649. n_tril = dim * (dim-1) // 2
  1650. covariances = random_state.normal(
  1651. size=n*n_tril).reshape(shape+(n_tril,))
  1652. # Random chi-square variates for diagonal elements
  1653. variances = (np.r_[[random_state.chisquare(df-(i+1)+1, size=n)**0.5
  1654. for i in range(dim)]].reshape((dim,) +
  1655. shape[::-1]).T)
  1656. # Create the A matri(ces) - lower triangular
  1657. A = np.zeros(shape + (dim, dim))
  1658. # Input the covariances
  1659. size_idx = tuple([slice(None, None, None)]*len(shape))
  1660. tril_idx = np.tril_indices(dim, k=-1)
  1661. A[size_idx + tril_idx] = covariances
  1662. # Input the variances
  1663. diag_idx = np.diag_indices(dim)
  1664. A[size_idx + diag_idx] = variances
  1665. return A
  1666. def _rvs(self, n, shape, dim, df, C, random_state):
  1667. """
  1668. Parameters
  1669. ----------
  1670. n : integer
  1671. Number of variates to generate
  1672. shape : iterable
  1673. Shape of the variates to generate
  1674. dim : int
  1675. Dimension of the scale matrix
  1676. df : int
  1677. Degrees of freedom
  1678. scale : ndarray
  1679. Scale matrix
  1680. C : ndarray
  1681. Cholesky factorization of the scale matrix, lower triangular.
  1682. %(_doc_random_state)s
  1683. Notes
  1684. -----
  1685. As this function does no argument checking, it should not be
  1686. called directly; use 'rvs' instead.
  1687. """
  1688. random_state = self._get_random_state(random_state)
  1689. # Calculate the matrices A, which are actually lower triangular
  1690. # Cholesky factorizations of a matrix B such that B ~ W(df, I)
  1691. A = self._standard_rvs(n, shape, dim, df, random_state)
  1692. # Calculate SA = C A A' C', where SA ~ W(df, scale)
  1693. # Note: this is the product of a (lower) (lower) (lower)' (lower)'
  1694. # or, denoting B = AA', it is C B C' where C is the lower
  1695. # triangular Cholesky factorization of the scale matrix.
  1696. # this appears to conflict with the instructions in [1]_, which
  1697. # suggest that it should be D' B D where D is the lower
  1698. # triangular factorization of the scale matrix. However, it is
  1699. # meant to refer to the Bartlett (1933) representation of a
  1700. # Wishart random variate as L A A' L' where L is lower triangular
  1701. # so it appears that understanding D' to be upper triangular
  1702. # is either a typo in or misreading of [1]_.
  1703. for index in np.ndindex(shape):
  1704. CA = np.dot(C, A[index])
  1705. A[index] = np.dot(CA, CA.T)
  1706. return A
  1707. def rvs(self, df, scale, size=1, random_state=None):
  1708. """
  1709. Draw random samples from a Wishart distribution.
  1710. Parameters
  1711. ----------
  1712. %(_doc_default_callparams)s
  1713. size : integer or iterable of integers, optional
  1714. Number of samples to draw (default 1).
  1715. %(_doc_random_state)s
  1716. Returns
  1717. -------
  1718. rvs : ndarray
  1719. Random variates of shape (`size`) + (`dim`, `dim), where `dim` is
  1720. the dimension of the scale matrix.
  1721. Notes
  1722. -----
  1723. %(_doc_callparams_note)s
  1724. """
  1725. n, shape = self._process_size(size)
  1726. dim, df, scale = self._process_parameters(df, scale)
  1727. # Cholesky decomposition of scale
  1728. C = scipy.linalg.cholesky(scale, lower=True)
  1729. out = self._rvs(n, shape, dim, df, C, random_state)
  1730. return _squeeze_output(out)
  1731. def _entropy(self, dim, df, log_det_scale):
  1732. """
  1733. Parameters
  1734. ----------
  1735. dim : int
  1736. Dimension of the scale matrix
  1737. df : int
  1738. Degrees of freedom
  1739. log_det_scale : float
  1740. Logarithm of the determinant of the scale matrix
  1741. Notes
  1742. -----
  1743. As this function does no argument checking, it should not be
  1744. called directly; use 'entropy' instead.
  1745. """
  1746. return (
  1747. 0.5 * (dim+1) * log_det_scale +
  1748. 0.5 * dim * (dim+1) * _LOG_2 +
  1749. multigammaln(0.5*df, dim) -
  1750. 0.5 * (df - dim - 1) * np.sum(
  1751. [psi(0.5*(df + 1 - (i+1))) for i in range(dim)]
  1752. ) +
  1753. 0.5 * df * dim
  1754. )
  1755. def entropy(self, df, scale):
  1756. """
  1757. Compute the differential entropy of the Wishart.
  1758. Parameters
  1759. ----------
  1760. %(_doc_default_callparams)s
  1761. Returns
  1762. -------
  1763. h : scalar
  1764. Entropy of the Wishart distribution
  1765. Notes
  1766. -----
  1767. %(_doc_callparams_note)s
  1768. """
  1769. dim, df, scale = self._process_parameters(df, scale)
  1770. _, log_det_scale = self._cholesky_logdet(scale)
  1771. return self._entropy(dim, df, log_det_scale)
  1772. def _cholesky_logdet(self, scale):
  1773. """
  1774. Compute Cholesky decomposition and determine (log(det(scale)).
  1775. Parameters
  1776. ----------
  1777. scale : ndarray
  1778. Scale matrix.
  1779. Returns
  1780. -------
  1781. c_decomp : ndarray
  1782. The Cholesky decomposition of `scale`.
  1783. logdet : scalar
  1784. The log of the determinant of `scale`.
  1785. Notes
  1786. -----
  1787. This computation of ``logdet`` is equivalent to
  1788. ``np.linalg.slogdet(scale)``. It is ~2x faster though.
  1789. """
  1790. c_decomp = scipy.linalg.cholesky(scale, lower=True)
  1791. logdet = 2 * np.sum(np.log(c_decomp.diagonal()))
  1792. return c_decomp, logdet
  1793. wishart = wishart_gen()
  1794. class wishart_frozen(multi_rv_frozen):
  1795. """
  1796. Create a frozen Wishart distribution.
  1797. Parameters
  1798. ----------
  1799. df : array_like
  1800. Degrees of freedom of the distribution
  1801. scale : array_like
  1802. Scale matrix of the distribution
  1803. seed : None or int or np.random.RandomState instance, optional
  1804. This parameter defines the RandomState object to use for drawing
  1805. random variates.
  1806. If None (or np.random), the global np.random state is used.
  1807. If integer, it is used to seed the local RandomState instance
  1808. Default is None.
  1809. """
  1810. def __init__(self, df, scale, seed=None):
  1811. self._dist = wishart_gen(seed)
  1812. self.dim, self.df, self.scale = self._dist._process_parameters(
  1813. df, scale)
  1814. self.C, self.log_det_scale = self._dist._cholesky_logdet(self.scale)
  1815. def logpdf(self, x):
  1816. x = self._dist._process_quantiles(x, self.dim)
  1817. out = self._dist._logpdf(x, self.dim, self.df, self.scale,
  1818. self.log_det_scale, self.C)
  1819. return _squeeze_output(out)
  1820. def pdf(self, x):
  1821. return np.exp(self.logpdf(x))
  1822. def mean(self):
  1823. out = self._dist._mean(self.dim, self.df, self.scale)
  1824. return _squeeze_output(out)
  1825. def mode(self):
  1826. out = self._dist._mode(self.dim, self.df, self.scale)
  1827. return _squeeze_output(out) if out is not None else out
  1828. def var(self):
  1829. out = self._dist._var(self.dim, self.df, self.scale)
  1830. return _squeeze_output(out)
  1831. def rvs(self, size=1, random_state=None):
  1832. n, shape = self._dist._process_size(size)
  1833. out = self._dist._rvs(n, shape, self.dim, self.df,
  1834. self.C, random_state)
  1835. return _squeeze_output(out)
  1836. def entropy(self):
  1837. return self._dist._entropy(self.dim, self.df, self.log_det_scale)
  1838. # Set frozen generator docstrings from corresponding docstrings in
  1839. # Wishart and fill in default strings in class docstrings
  1840. for name in ['logpdf', 'pdf', 'mean', 'mode', 'var', 'rvs', 'entropy']:
  1841. method = wishart_gen.__dict__[name]
  1842. method_frozen = wishart_frozen.__dict__[name]
  1843. method_frozen.__doc__ = doccer.docformat(
  1844. method.__doc__, wishart_docdict_noparams)
  1845. method.__doc__ = doccer.docformat(method.__doc__, wishart_docdict_params)
  1846. def _cho_inv_batch(a, check_finite=True):
  1847. """
  1848. Invert the matrices a_i, using a Cholesky factorization of A, where
  1849. a_i resides in the last two dimensions of a and the other indices describe
  1850. the index i.
  1851. Overwrites the data in a.
  1852. Parameters
  1853. ----------
  1854. a : array
  1855. Array of matrices to invert, where the matrices themselves are stored
  1856. in the last two dimensions.
  1857. check_finite : bool, optional
  1858. Whether to check that the input matrices contain only finite numbers.
  1859. Disabling may give a performance gain, but may result in problems
  1860. (crashes, non-termination) if the inputs do contain infinities or NaNs.
  1861. Returns
  1862. -------
  1863. x : array
  1864. Array of inverses of the matrices ``a_i``.
  1865. See also
  1866. --------
  1867. scipy.linalg.cholesky : Cholesky factorization of a matrix
  1868. """
  1869. if check_finite:
  1870. a1 = asarray_chkfinite(a)
  1871. else:
  1872. a1 = asarray(a)
  1873. if len(a1.shape) < 2 or a1.shape[-2] != a1.shape[-1]:
  1874. raise ValueError('expected square matrix in last two dimensions')
  1875. potrf, potri = get_lapack_funcs(('potrf', 'potri'), (a1,))
  1876. triu_rows, triu_cols = np.triu_indices(a.shape[-2], k=1)
  1877. for index in np.ndindex(a1.shape[:-2]):
  1878. # Cholesky decomposition
  1879. a1[index], info = potrf(a1[index], lower=True, overwrite_a=False,
  1880. clean=False)
  1881. if info > 0:
  1882. raise LinAlgError("%d-th leading minor not positive definite"
  1883. % info)
  1884. if info < 0:
  1885. raise ValueError('illegal value in %d-th argument of internal'
  1886. ' potrf' % -info)
  1887. # Inversion
  1888. a1[index], info = potri(a1[index], lower=True, overwrite_c=False)
  1889. if info > 0:
  1890. raise LinAlgError("the inverse could not be computed")
  1891. if info < 0:
  1892. raise ValueError('illegal value in %d-th argument of internal'
  1893. ' potrf' % -info)
  1894. # Make symmetric (dpotri only fills in the lower triangle)
  1895. a1[index][triu_rows, triu_cols] = a1[index][triu_cols, triu_rows]
  1896. return a1
  1897. class invwishart_gen(wishart_gen):
  1898. r"""
  1899. An inverse Wishart random variable.
  1900. The `df` keyword specifies the degrees of freedom. The `scale` keyword
  1901. specifies the scale matrix, which must be symmetric and positive definite.
  1902. In this context, the scale matrix is often interpreted in terms of a
  1903. multivariate normal covariance matrix.
  1904. Methods
  1905. -------
  1906. ``pdf(x, df, scale)``
  1907. Probability density function.
  1908. ``logpdf(x, df, scale)``
  1909. Log of the probability density function.
  1910. ``rvs(df, scale, size=1, random_state=None)``
  1911. Draw random samples from an inverse Wishart distribution.
  1912. Parameters
  1913. ----------
  1914. x : array_like
  1915. Quantiles, with the last axis of `x` denoting the components.
  1916. %(_doc_default_callparams)s
  1917. %(_doc_random_state)s
  1918. Alternatively, the object may be called (as a function) to fix the degrees
  1919. of freedom and scale parameters, returning a "frozen" inverse Wishart
  1920. random variable:
  1921. rv = invwishart(df=1, scale=1)
  1922. - Frozen object with the same methods but holding the given
  1923. degrees of freedom and scale fixed.
  1924. See Also
  1925. --------
  1926. wishart
  1927. Notes
  1928. -----
  1929. %(_doc_callparams_note)s
  1930. The scale matrix `scale` must be a symmetric positive definite
  1931. matrix. Singular matrices, including the symmetric positive semi-definite
  1932. case, are not supported.
  1933. The inverse Wishart distribution is often denoted
  1934. .. math::
  1935. W_p^{-1}(\nu, \Psi)
  1936. where :math:`\nu` is the degrees of freedom and :math:`\Psi` is the
  1937. :math:`p \times p` scale matrix.
  1938. The probability density function for `invwishart` has support over positive
  1939. definite matrices :math:`S`; if :math:`S \sim W^{-1}_p(\nu, \Sigma)`,
  1940. then its PDF is given by:
  1941. .. math::
  1942. f(S) = \frac{|\Sigma|^\frac{\nu}{2}}{2^{ \frac{\nu p}{2} }
  1943. |S|^{\frac{\nu + p + 1}{2}} \Gamma_p \left(\frac{\nu}{2} \right)}
  1944. \exp\left( -tr(\Sigma S^{-1}) / 2 \right)
  1945. If :math:`S \sim W_p^{-1}(\nu, \Psi)` (inverse Wishart) then
  1946. :math:`S^{-1} \sim W_p(\nu, \Psi^{-1})` (Wishart).
  1947. If the scale matrix is 1-dimensional and equal to one, then the inverse
  1948. Wishart distribution :math:`W_1(\nu, 1)` collapses to the
  1949. inverse Gamma distribution with parameters shape = :math:`\frac{\nu}{2}`
  1950. and scale = :math:`\frac{1}{2}`.
  1951. .. versionadded:: 0.16.0
  1952. References
  1953. ----------
  1954. .. [1] M.L. Eaton, "Multivariate Statistics: A Vector Space Approach",
  1955. Wiley, 1983.
  1956. .. [2] M.C. Jones, "Generating Inverse Wishart Matrices", Communications
  1957. in Statistics - Simulation and Computation, vol. 14.2, pp.511-514,
  1958. 1985.
  1959. Examples
  1960. --------
  1961. >>> import matplotlib.pyplot as plt
  1962. >>> from scipy.stats import invwishart, invgamma
  1963. >>> x = np.linspace(0.01, 1, 100)
  1964. >>> iw = invwishart.pdf(x, df=6, scale=1)
  1965. >>> iw[:3]
  1966. array([ 1.20546865e-15, 5.42497807e-06, 4.45813929e-03])
  1967. >>> ig = invgamma.pdf(x, 6/2., scale=1./2)
  1968. >>> ig[:3]
  1969. array([ 1.20546865e-15, 5.42497807e-06, 4.45813929e-03])
  1970. >>> plt.plot(x, iw)
  1971. The input quantiles can be any shape of array, as long as the last
  1972. axis labels the components.
  1973. """
  1974. def __init__(self, seed=None):
  1975. super(invwishart_gen, self).__init__(seed)
  1976. self.__doc__ = doccer.docformat(self.__doc__, wishart_docdict_params)
  1977. def __call__(self, df=None, scale=None, seed=None):
  1978. """
  1979. Create a frozen inverse Wishart distribution.
  1980. See `invwishart_frozen` for more information.
  1981. """
  1982. return invwishart_frozen(df, scale, seed)
  1983. def _logpdf(self, x, dim, df, scale, log_det_scale):
  1984. """
  1985. Parameters
  1986. ----------
  1987. x : ndarray
  1988. Points at which to evaluate the log of the probability
  1989. density function.
  1990. dim : int
  1991. Dimension of the scale matrix
  1992. df : int
  1993. Degrees of freedom
  1994. scale : ndarray
  1995. Scale matrix
  1996. log_det_scale : float
  1997. Logarithm of the determinant of the scale matrix
  1998. Notes
  1999. -----
  2000. As this function does no argument checking, it should not be
  2001. called directly; use 'logpdf' instead.
  2002. """
  2003. log_det_x = np.zeros(x.shape[-1])
  2004. x_inv = np.copy(x).T
  2005. if dim > 1:
  2006. _cho_inv_batch(x_inv) # works in-place
  2007. else:
  2008. x_inv = 1./x_inv
  2009. tr_scale_x_inv = np.zeros(x.shape[-1])
  2010. for i in range(x.shape[-1]):
  2011. C, lower = scipy.linalg.cho_factor(x[:, :, i], lower=True)
  2012. log_det_x[i] = 2 * np.sum(np.log(C.diagonal()))
  2013. tr_scale_x_inv[i] = np.dot(scale, x_inv[i]).trace()
  2014. # Log PDF
  2015. out = ((0.5 * df * log_det_scale - 0.5 * tr_scale_x_inv) -
  2016. (0.5 * df * dim * _LOG_2 + 0.5 * (df + dim + 1) * log_det_x) -
  2017. multigammaln(0.5*df, dim))
  2018. return out
  2019. def logpdf(self, x, df, scale):
  2020. """
  2021. Log of the inverse Wishart probability density function.
  2022. Parameters
  2023. ----------
  2024. x : array_like
  2025. Quantiles, with the last axis of `x` denoting the components.
  2026. Each quantile must be a symmetric positive definite matrix.
  2027. %(_doc_default_callparams)s
  2028. Returns
  2029. -------
  2030. pdf : ndarray
  2031. Log of the probability density function evaluated at `x`
  2032. Notes
  2033. -----
  2034. %(_doc_callparams_note)s
  2035. """
  2036. dim, df, scale = self._process_parameters(df, scale)
  2037. x = self._process_quantiles(x, dim)
  2038. _, log_det_scale = self._cholesky_logdet(scale)
  2039. out = self._logpdf(x, dim, df, scale, log_det_scale)
  2040. return _squeeze_output(out)
  2041. def pdf(self, x, df, scale):
  2042. """
  2043. Inverse Wishart probability density function.
  2044. Parameters
  2045. ----------
  2046. x : array_like
  2047. Quantiles, with the last axis of `x` denoting the components.
  2048. Each quantile must be a symmetric positive definite matrix.
  2049. %(_doc_default_callparams)s
  2050. Returns
  2051. -------
  2052. pdf : ndarray
  2053. Probability density function evaluated at `x`
  2054. Notes
  2055. -----
  2056. %(_doc_callparams_note)s
  2057. """
  2058. return np.exp(self.logpdf(x, df, scale))
  2059. def _mean(self, dim, df, scale):
  2060. """
  2061. Parameters
  2062. ----------
  2063. dim : int
  2064. Dimension of the scale matrix
  2065. %(_doc_default_callparams)s
  2066. Notes
  2067. -----
  2068. As this function does no argument checking, it should not be
  2069. called directly; use 'mean' instead.
  2070. """
  2071. if df > dim + 1:
  2072. out = scale / (df - dim - 1)
  2073. else:
  2074. out = None
  2075. return out
  2076. def mean(self, df, scale):
  2077. """
  2078. Mean of the inverse Wishart distribution
  2079. Only valid if the degrees of freedom are greater than the dimension of
  2080. the scale matrix plus one.
  2081. Parameters
  2082. ----------
  2083. %(_doc_default_callparams)s
  2084. Returns
  2085. -------
  2086. mean : float or None
  2087. The mean of the distribution
  2088. """
  2089. dim, df, scale = self._process_parameters(df, scale)
  2090. out = self._mean(dim, df, scale)
  2091. return _squeeze_output(out) if out is not None else out
  2092. def _mode(self, dim, df, scale):
  2093. """
  2094. Parameters
  2095. ----------
  2096. dim : int
  2097. Dimension of the scale matrix
  2098. %(_doc_default_callparams)s
  2099. Notes
  2100. -----
  2101. As this function does no argument checking, it should not be
  2102. called directly; use 'mode' instead.
  2103. """
  2104. return scale / (df + dim + 1)
  2105. def mode(self, df, scale):
  2106. """
  2107. Mode of the inverse Wishart distribution
  2108. Parameters
  2109. ----------
  2110. %(_doc_default_callparams)s
  2111. Returns
  2112. -------
  2113. mode : float
  2114. The Mode of the distribution
  2115. """
  2116. dim, df, scale = self._process_parameters(df, scale)
  2117. out = self._mode(dim, df, scale)
  2118. return _squeeze_output(out)
  2119. def _var(self, dim, df, scale):
  2120. """
  2121. Parameters
  2122. ----------
  2123. dim : int
  2124. Dimension of the scale matrix
  2125. %(_doc_default_callparams)s
  2126. Notes
  2127. -----
  2128. As this function does no argument checking, it should not be
  2129. called directly; use 'var' instead.
  2130. """
  2131. if df > dim + 3:
  2132. var = (df - dim + 1) * scale**2
  2133. diag = scale.diagonal() # 1 x dim array
  2134. var += (df - dim - 1) * np.outer(diag, diag)
  2135. var /= (df - dim) * (df - dim - 1)**2 * (df - dim - 3)
  2136. else:
  2137. var = None
  2138. return var
  2139. def var(self, df, scale):
  2140. """
  2141. Variance of the inverse Wishart distribution
  2142. Only valid if the degrees of freedom are greater than the dimension of
  2143. the scale matrix plus three.
  2144. Parameters
  2145. ----------
  2146. %(_doc_default_callparams)s
  2147. Returns
  2148. -------
  2149. var : float
  2150. The variance of the distribution
  2151. """
  2152. dim, df, scale = self._process_parameters(df, scale)
  2153. out = self._var(dim, df, scale)
  2154. return _squeeze_output(out) if out is not None else out
  2155. def _rvs(self, n, shape, dim, df, C, random_state):
  2156. """
  2157. Parameters
  2158. ----------
  2159. n : integer
  2160. Number of variates to generate
  2161. shape : iterable
  2162. Shape of the variates to generate
  2163. dim : int
  2164. Dimension of the scale matrix
  2165. df : int
  2166. Degrees of freedom
  2167. C : ndarray
  2168. Cholesky factorization of the scale matrix, lower triagular.
  2169. %(_doc_random_state)s
  2170. Notes
  2171. -----
  2172. As this function does no argument checking, it should not be
  2173. called directly; use 'rvs' instead.
  2174. """
  2175. random_state = self._get_random_state(random_state)
  2176. # Get random draws A such that A ~ W(df, I)
  2177. A = super(invwishart_gen, self)._standard_rvs(n, shape, dim,
  2178. df, random_state)
  2179. # Calculate SA = (CA)'^{-1} (CA)^{-1} ~ iW(df, scale)
  2180. eye = np.eye(dim)
  2181. trtrs = get_lapack_funcs(('trtrs'), (A,))
  2182. for index in np.ndindex(A.shape[:-2]):
  2183. # Calculate CA
  2184. CA = np.dot(C, A[index])
  2185. # Get (C A)^{-1} via triangular solver
  2186. if dim > 1:
  2187. CA, info = trtrs(CA, eye, lower=True)
  2188. if info > 0:
  2189. raise LinAlgError("Singular matrix.")
  2190. if info < 0:
  2191. raise ValueError('Illegal value in %d-th argument of'
  2192. ' internal trtrs' % -info)
  2193. else:
  2194. CA = 1. / CA
  2195. # Get SA
  2196. A[index] = np.dot(CA.T, CA)
  2197. return A
  2198. def rvs(self, df, scale, size=1, random_state=None):
  2199. """
  2200. Draw random samples from an inverse Wishart distribution.
  2201. Parameters
  2202. ----------
  2203. %(_doc_default_callparams)s
  2204. size : integer or iterable of integers, optional
  2205. Number of samples to draw (default 1).
  2206. %(_doc_random_state)s
  2207. Returns
  2208. -------
  2209. rvs : ndarray
  2210. Random variates of shape (`size`) + (`dim`, `dim), where `dim` is
  2211. the dimension of the scale matrix.
  2212. Notes
  2213. -----
  2214. %(_doc_callparams_note)s
  2215. """
  2216. n, shape = self._process_size(size)
  2217. dim, df, scale = self._process_parameters(df, scale)
  2218. # Invert the scale
  2219. eye = np.eye(dim)
  2220. L, lower = scipy.linalg.cho_factor(scale, lower=True)
  2221. inv_scale = scipy.linalg.cho_solve((L, lower), eye)
  2222. # Cholesky decomposition of inverted scale
  2223. C = scipy.linalg.cholesky(inv_scale, lower=True)
  2224. out = self._rvs(n, shape, dim, df, C, random_state)
  2225. return _squeeze_output(out)
  2226. def entropy(self):
  2227. # Need to find reference for inverse Wishart entropy
  2228. raise AttributeError
  2229. invwishart = invwishart_gen()
  2230. class invwishart_frozen(multi_rv_frozen):
  2231. def __init__(self, df, scale, seed=None):
  2232. """
  2233. Create a frozen inverse Wishart distribution.
  2234. Parameters
  2235. ----------
  2236. df : array_like
  2237. Degrees of freedom of the distribution
  2238. scale : array_like
  2239. Scale matrix of the distribution
  2240. seed : None or int or np.random.RandomState instance, optional
  2241. This parameter defines the RandomState object to use for drawing
  2242. random variates.
  2243. If None (or np.random), the global np.random state is used.
  2244. If integer, it is used to seed the local RandomState instance
  2245. Default is None.
  2246. """
  2247. self._dist = invwishart_gen(seed)
  2248. self.dim, self.df, self.scale = self._dist._process_parameters(
  2249. df, scale
  2250. )
  2251. # Get the determinant via Cholesky factorization
  2252. C, lower = scipy.linalg.cho_factor(self.scale, lower=True)
  2253. self.log_det_scale = 2 * np.sum(np.log(C.diagonal()))
  2254. # Get the inverse using the Cholesky factorization
  2255. eye = np.eye(self.dim)
  2256. self.inv_scale = scipy.linalg.cho_solve((C, lower), eye)
  2257. # Get the Cholesky factorization of the inverse scale
  2258. self.C = scipy.linalg.cholesky(self.inv_scale, lower=True)
  2259. def logpdf(self, x):
  2260. x = self._dist._process_quantiles(x, self.dim)
  2261. out = self._dist._logpdf(x, self.dim, self.df, self.scale,
  2262. self.log_det_scale)
  2263. return _squeeze_output(out)
  2264. def pdf(self, x):
  2265. return np.exp(self.logpdf(x))
  2266. def mean(self):
  2267. out = self._dist._mean(self.dim, self.df, self.scale)
  2268. return _squeeze_output(out) if out is not None else out
  2269. def mode(self):
  2270. out = self._dist._mode(self.dim, self.df, self.scale)
  2271. return _squeeze_output(out)
  2272. def var(self):
  2273. out = self._dist._var(self.dim, self.df, self.scale)
  2274. return _squeeze_output(out) if out is not None else out
  2275. def rvs(self, size=1, random_state=None):
  2276. n, shape = self._dist._process_size(size)
  2277. out = self._dist._rvs(n, shape, self.dim, self.df,
  2278. self.C, random_state)
  2279. return _squeeze_output(out)
  2280. def entropy(self):
  2281. # Need to find reference for inverse Wishart entropy
  2282. raise AttributeError
  2283. # Set frozen generator docstrings from corresponding docstrings in
  2284. # inverse Wishart and fill in default strings in class docstrings
  2285. for name in ['logpdf', 'pdf', 'mean', 'mode', 'var', 'rvs']:
  2286. method = invwishart_gen.__dict__[name]
  2287. method_frozen = wishart_frozen.__dict__[name]
  2288. method_frozen.__doc__ = doccer.docformat(
  2289. method.__doc__, wishart_docdict_noparams)
  2290. method.__doc__ = doccer.docformat(method.__doc__, wishart_docdict_params)
  2291. _multinomial_doc_default_callparams = """\
  2292. n : int
  2293. Number of trials
  2294. p : array_like
  2295. Probability of a trial falling into each category; should sum to 1
  2296. """
  2297. _multinomial_doc_callparams_note = \
  2298. """`n` should be a positive integer. Each element of `p` should be in the
  2299. interval :math:`[0,1]` and the elements should sum to 1. If they do not sum to
  2300. 1, the last element of the `p` array is not used and is replaced with the
  2301. remaining probability left over from the earlier elements.
  2302. """
  2303. _multinomial_doc_frozen_callparams = ""
  2304. _multinomial_doc_frozen_callparams_note = \
  2305. """See class definition for a detailed description of parameters."""
  2306. multinomial_docdict_params = {
  2307. '_doc_default_callparams': _multinomial_doc_default_callparams,
  2308. '_doc_callparams_note': _multinomial_doc_callparams_note,
  2309. '_doc_random_state': _doc_random_state
  2310. }
  2311. multinomial_docdict_noparams = {
  2312. '_doc_default_callparams': _multinomial_doc_frozen_callparams,
  2313. '_doc_callparams_note': _multinomial_doc_frozen_callparams_note,
  2314. '_doc_random_state': _doc_random_state
  2315. }
  2316. class multinomial_gen(multi_rv_generic):
  2317. r"""
  2318. A multinomial random variable.
  2319. Methods
  2320. -------
  2321. ``pmf(x, n, p)``
  2322. Probability mass function.
  2323. ``logpmf(x, n, p)``
  2324. Log of the probability mass function.
  2325. ``rvs(n, p, size=1, random_state=None)``
  2326. Draw random samples from a multinomial distribution.
  2327. ``entropy(n, p)``
  2328. Compute the entropy of the multinomial distribution.
  2329. ``cov(n, p)``
  2330. Compute the covariance matrix of the multinomial distribution.
  2331. Parameters
  2332. ----------
  2333. x : array_like
  2334. Quantiles, with the last axis of `x` denoting the components.
  2335. %(_doc_default_callparams)s
  2336. %(_doc_random_state)s
  2337. Notes
  2338. -----
  2339. %(_doc_callparams_note)s
  2340. Alternatively, the object may be called (as a function) to fix the `n` and
  2341. `p` parameters, returning a "frozen" multinomial random variable:
  2342. The probability mass function for `multinomial` is
  2343. .. math::
  2344. f(x) = \frac{n!}{x_1! \cdots x_k!} p_1^{x_1} \cdots p_k^{x_k},
  2345. supported on :math:`x=(x_1, \ldots, x_k)` where each :math:`x_i` is a
  2346. nonnegative integer and their sum is :math:`n`.
  2347. .. versionadded:: 0.19.0
  2348. Examples
  2349. --------
  2350. >>> from scipy.stats import multinomial
  2351. >>> rv = multinomial(8, [0.3, 0.2, 0.5])
  2352. >>> rv.pmf([1, 3, 4])
  2353. 0.042000000000000072
  2354. The multinomial distribution for :math:`k=2` is identical to the
  2355. corresponding binomial distribution (tiny numerical differences
  2356. notwithstanding):
  2357. >>> from scipy.stats import binom
  2358. >>> multinomial.pmf([3, 4], n=7, p=[0.4, 0.6])
  2359. 0.29030399999999973
  2360. >>> binom.pmf(3, 7, 0.4)
  2361. 0.29030400000000012
  2362. The functions ``pmf``, ``logpmf``, ``entropy``, and ``cov`` support
  2363. broadcasting, under the convention that the vector parameters (``x`` and
  2364. ``p``) are interpreted as if each row along the last axis is a single
  2365. object. For instance:
  2366. >>> multinomial.pmf([[3, 4], [3, 5]], n=[7, 8], p=[.3, .7])
  2367. array([0.2268945, 0.25412184])
  2368. Here, ``x.shape == (2, 2)``, ``n.shape == (2,)``, and ``p.shape == (2,)``,
  2369. but following the rules mentioned above they behave as if the rows
  2370. ``[3, 4]`` and ``[3, 5]`` in ``x`` and ``[.3, .7]`` in ``p`` were a single
  2371. object, and as if we had ``x.shape = (2,)``, ``n.shape = (2,)``, and
  2372. ``p.shape = ()``. To obtain the individual elements without broadcasting,
  2373. we would do this:
  2374. >>> multinomial.pmf([3, 4], n=7, p=[.3, .7])
  2375. 0.2268945
  2376. >>> multinomial.pmf([3, 5], 8, p=[.3, .7])
  2377. 0.25412184
  2378. This broadcasting also works for ``cov``, where the output objects are
  2379. square matrices of size ``p.shape[-1]``. For example:
  2380. >>> multinomial.cov([4, 5], [[.3, .7], [.4, .6]])
  2381. array([[[ 0.84, -0.84],
  2382. [-0.84, 0.84]],
  2383. [[ 1.2 , -1.2 ],
  2384. [-1.2 , 1.2 ]]])
  2385. In this example, ``n.shape == (2,)`` and ``p.shape == (2, 2)``, and
  2386. following the rules above, these broadcast as if ``p.shape == (2,)``.
  2387. Thus the result should also be of shape ``(2,)``, but since each output is
  2388. a :math:`2 \times 2` matrix, the result in fact has shape ``(2, 2, 2)``,
  2389. where ``result[0]`` is equal to ``multinomial.cov(n=4, p=[.3, .7])`` and
  2390. ``result[1]`` is equal to ``multinomial.cov(n=5, p=[.4, .6])``.
  2391. See also
  2392. --------
  2393. scipy.stats.binom : The binomial distribution.
  2394. numpy.random.multinomial : Sampling from the multinomial distribution.
  2395. """
  2396. def __init__(self, seed=None):
  2397. super(multinomial_gen, self).__init__(seed)
  2398. self.__doc__ = \
  2399. doccer.docformat(self.__doc__, multinomial_docdict_params)
  2400. def __call__(self, n, p, seed=None):
  2401. """
  2402. Create a frozen multinomial distribution.
  2403. See `multinomial_frozen` for more information.
  2404. """
  2405. return multinomial_frozen(n, p, seed)
  2406. def _process_parameters(self, n, p):
  2407. """
  2408. Return: n_, p_, npcond.
  2409. n_ and p_ are arrays of the correct shape; npcond is a boolean array
  2410. flagging values out of the domain.
  2411. """
  2412. p = np.array(p, dtype=np.float64, copy=True)
  2413. p[..., -1] = 1. - p[..., :-1].sum(axis=-1)
  2414. # true for bad p
  2415. pcond = np.any(p < 0, axis=-1)
  2416. pcond |= np.any(p > 1, axis=-1)
  2417. n = np.array(n, dtype=np.int, copy=True)
  2418. # true for bad n
  2419. ncond = n <= 0
  2420. return n, p, ncond | pcond
  2421. def _process_quantiles(self, x, n, p):
  2422. """
  2423. Return: x_, xcond.
  2424. x_ is an int array; xcond is a boolean array flagging values out of the
  2425. domain.
  2426. """
  2427. xx = np.asarray(x, dtype=np.int)
  2428. if xx.ndim == 0:
  2429. raise ValueError("x must be an array.")
  2430. if xx.size != 0 and not xx.shape[-1] == p.shape[-1]:
  2431. raise ValueError("Size of each quantile should be size of p: "
  2432. "received %d, but expected %d." %
  2433. (xx.shape[-1], p.shape[-1]))
  2434. # true for x out of the domain
  2435. cond = np.any(xx != x, axis=-1)
  2436. cond |= np.any(xx < 0, axis=-1)
  2437. cond = cond | (np.sum(xx, axis=-1) != n)
  2438. return xx, cond
  2439. def _checkresult(self, result, cond, bad_value):
  2440. result = np.asarray(result)
  2441. if cond.ndim != 0:
  2442. result[cond] = bad_value
  2443. elif cond:
  2444. if result.ndim == 0:
  2445. return bad_value
  2446. result[...] = bad_value
  2447. return result
  2448. def _logpmf(self, x, n, p):
  2449. return gammaln(n+1) + np.sum(xlogy(x, p) - gammaln(x+1), axis=-1)
  2450. def logpmf(self, x, n, p):
  2451. """
  2452. Log of the Multinomial probability mass function.
  2453. Parameters
  2454. ----------
  2455. x : array_like
  2456. Quantiles, with the last axis of `x` denoting the components.
  2457. %(_doc_default_callparams)s
  2458. Returns
  2459. -------
  2460. logpmf : ndarray or scalar
  2461. Log of the probability mass function evaluated at `x`
  2462. Notes
  2463. -----
  2464. %(_doc_callparams_note)s
  2465. """
  2466. n, p, npcond = self._process_parameters(n, p)
  2467. x, xcond = self._process_quantiles(x, n, p)
  2468. result = self._logpmf(x, n, p)
  2469. # replace values for which x was out of the domain; broadcast
  2470. # xcond to the right shape
  2471. xcond_ = xcond | np.zeros(npcond.shape, dtype=np.bool_)
  2472. result = self._checkresult(result, xcond_, np.NINF)
  2473. # replace values bad for n or p; broadcast npcond to the right shape
  2474. npcond_ = npcond | np.zeros(xcond.shape, dtype=np.bool_)
  2475. return self._checkresult(result, npcond_, np.NAN)
  2476. def pmf(self, x, n, p):
  2477. """
  2478. Multinomial probability mass function.
  2479. Parameters
  2480. ----------
  2481. x : array_like
  2482. Quantiles, with the last axis of `x` denoting the components.
  2483. %(_doc_default_callparams)s
  2484. Returns
  2485. -------
  2486. pmf : ndarray or scalar
  2487. Probability density function evaluated at `x`
  2488. Notes
  2489. -----
  2490. %(_doc_callparams_note)s
  2491. """
  2492. return np.exp(self.logpmf(x, n, p))
  2493. def mean(self, n, p):
  2494. """
  2495. Mean of the Multinomial distribution
  2496. Parameters
  2497. ----------
  2498. %(_doc_default_callparams)s
  2499. Returns
  2500. -------
  2501. mean : float
  2502. The mean of the distribution
  2503. """
  2504. n, p, npcond = self._process_parameters(n, p)
  2505. result = n[..., np.newaxis]*p
  2506. return self._checkresult(result, npcond, np.NAN)
  2507. def cov(self, n, p):
  2508. """
  2509. Covariance matrix of the multinomial distribution.
  2510. Parameters
  2511. ----------
  2512. %(_doc_default_callparams)s
  2513. Returns
  2514. -------
  2515. cov : ndarray
  2516. The covariance matrix of the distribution
  2517. """
  2518. n, p, npcond = self._process_parameters(n, p)
  2519. nn = n[..., np.newaxis, np.newaxis]
  2520. result = nn * np.einsum('...j,...k->...jk', -p, p)
  2521. # change the diagonal
  2522. for i in range(p.shape[-1]):
  2523. result[..., i, i] += n*p[..., i]
  2524. return self._checkresult(result, npcond, np.nan)
  2525. def entropy(self, n, p):
  2526. r"""
  2527. Compute the entropy of the multinomial distribution.
  2528. The entropy is computed using this expression:
  2529. .. math::
  2530. f(x) = - \log n! - n\sum_{i=1}^k p_i \log p_i +
  2531. \sum_{i=1}^k \sum_{x=0}^n \binom n x p_i^x(1-p_i)^{n-x} \log x!
  2532. Parameters
  2533. ----------
  2534. %(_doc_default_callparams)s
  2535. Returns
  2536. -------
  2537. h : scalar
  2538. Entropy of the Multinomial distribution
  2539. Notes
  2540. -----
  2541. %(_doc_callparams_note)s
  2542. """
  2543. n, p, npcond = self._process_parameters(n, p)
  2544. x = np.r_[1:np.max(n)+1]
  2545. term1 = n*np.sum(entr(p), axis=-1)
  2546. term1 -= gammaln(n+1)
  2547. n = n[..., np.newaxis]
  2548. new_axes_needed = max(p.ndim, n.ndim) - x.ndim + 1
  2549. x.shape += (1,)*new_axes_needed
  2550. term2 = np.sum(binom.pmf(x, n, p)*gammaln(x+1),
  2551. axis=(-1, -1-new_axes_needed))
  2552. return self._checkresult(term1 + term2, npcond, np.nan)
  2553. def rvs(self, n, p, size=None, random_state=None):
  2554. """
  2555. Draw random samples from a Multinomial distribution.
  2556. Parameters
  2557. ----------
  2558. %(_doc_default_callparams)s
  2559. size : integer or iterable of integers, optional
  2560. Number of samples to draw (default 1).
  2561. %(_doc_random_state)s
  2562. Returns
  2563. -------
  2564. rvs : ndarray or scalar
  2565. Random variates of shape (`size`, `len(p)`)
  2566. Notes
  2567. -----
  2568. %(_doc_callparams_note)s
  2569. """
  2570. n, p, npcond = self._process_parameters(n, p)
  2571. random_state = self._get_random_state(random_state)
  2572. return random_state.multinomial(n, p, size)
  2573. multinomial = multinomial_gen()
  2574. class multinomial_frozen(multi_rv_frozen):
  2575. r"""
  2576. Create a frozen Multinomial distribution.
  2577. Parameters
  2578. ----------
  2579. n : int
  2580. number of trials
  2581. p: array_like
  2582. probability of a trial falling into each category; should sum to 1
  2583. seed : None or int or np.random.RandomState instance, optional
  2584. This parameter defines the RandomState object to use for drawing
  2585. random variates.
  2586. If None (or np.random), the global np.random state is used.
  2587. If integer, it is used to seed the local RandomState instance
  2588. Default is None.
  2589. """
  2590. def __init__(self, n, p, seed=None):
  2591. self._dist = multinomial_gen(seed)
  2592. self.n, self.p, self.npcond = self._dist._process_parameters(n, p)
  2593. # monkey patch self._dist
  2594. def _process_parameters(n, p):
  2595. return self.n, self.p, self.npcond
  2596. self._dist._process_parameters = _process_parameters
  2597. def logpmf(self, x):
  2598. return self._dist.logpmf(x, self.n, self.p)
  2599. def pmf(self, x):
  2600. return self._dist.pmf(x, self.n, self.p)
  2601. def mean(self):
  2602. return self._dist.mean(self.n, self.p)
  2603. def cov(self):
  2604. return self._dist.cov(self.n, self.p)
  2605. def entropy(self):
  2606. return self._dist.entropy(self.n, self.p)
  2607. def rvs(self, size=1, random_state=None):
  2608. return self._dist.rvs(self.n, self.p, size, random_state)
  2609. # Set frozen generator docstrings from corresponding docstrings in
  2610. # multinomial and fill in default strings in class docstrings
  2611. for name in ['logpmf', 'pmf', 'mean', 'cov', 'rvs']:
  2612. method = multinomial_gen.__dict__[name]
  2613. method_frozen = multinomial_frozen.__dict__[name]
  2614. method_frozen.__doc__ = doccer.docformat(
  2615. method.__doc__, multinomial_docdict_noparams)
  2616. method.__doc__ = doccer.docformat(method.__doc__,
  2617. multinomial_docdict_params)
  2618. class special_ortho_group_gen(multi_rv_generic):
  2619. r"""
  2620. A matrix-valued SO(N) random variable.
  2621. Return a random rotation matrix, drawn from the Haar distribution
  2622. (the only uniform distribution on SO(n)).
  2623. The `dim` keyword specifies the dimension N.
  2624. Methods
  2625. -------
  2626. ``rvs(dim=None, size=1, random_state=None)``
  2627. Draw random samples from SO(N).
  2628. Parameters
  2629. ----------
  2630. dim : scalar
  2631. Dimension of matrices
  2632. Notes
  2633. ----------
  2634. This class is wrapping the random_rot code from the MDP Toolkit,
  2635. https://github.com/mdp-toolkit/mdp-toolkit
  2636. Return a random rotation matrix, drawn from the Haar distribution
  2637. (the only uniform distribution on SO(n)).
  2638. The algorithm is described in the paper
  2639. Stewart, G.W., "The efficient generation of random orthogonal
  2640. matrices with an application to condition estimators", SIAM Journal
  2641. on Numerical Analysis, 17(3), pp. 403-409, 1980.
  2642. For more information see
  2643. https://en.wikipedia.org/wiki/Orthogonal_matrix#Randomization
  2644. See also the similar `ortho_group`.
  2645. Examples
  2646. --------
  2647. >>> from scipy.stats import special_ortho_group
  2648. >>> x = special_ortho_group.rvs(3)
  2649. >>> np.dot(x, x.T)
  2650. array([[ 1.00000000e+00, 1.13231364e-17, -2.86852790e-16],
  2651. [ 1.13231364e-17, 1.00000000e+00, -1.46845020e-16],
  2652. [ -2.86852790e-16, -1.46845020e-16, 1.00000000e+00]])
  2653. >>> import scipy.linalg
  2654. >>> scipy.linalg.det(x)
  2655. 1.0
  2656. This generates one random matrix from SO(3). It is orthogonal and
  2657. has a determinant of 1.
  2658. """
  2659. def __init__(self, seed=None):
  2660. super(special_ortho_group_gen, self).__init__(seed)
  2661. self.__doc__ = doccer.docformat(self.__doc__)
  2662. def __call__(self, dim=None, seed=None):
  2663. """
  2664. Create a frozen SO(N) distribution.
  2665. See `special_ortho_group_frozen` for more information.
  2666. """
  2667. return special_ortho_group_frozen(dim, seed=seed)
  2668. def _process_parameters(self, dim):
  2669. """
  2670. Dimension N must be specified; it cannot be inferred.
  2671. """
  2672. if dim is None or not np.isscalar(dim) or dim <= 1 or dim != int(dim):
  2673. raise ValueError("""Dimension of rotation must be specified,
  2674. and must be a scalar greater than 1.""")
  2675. return dim
  2676. def rvs(self, dim, size=1, random_state=None):
  2677. """
  2678. Draw random samples from SO(N).
  2679. Parameters
  2680. ----------
  2681. dim : integer
  2682. Dimension of rotation space (N).
  2683. size : integer, optional
  2684. Number of samples to draw (default 1).
  2685. Returns
  2686. -------
  2687. rvs : ndarray or scalar
  2688. Random size N-dimensional matrices, dimension (size, dim, dim)
  2689. """
  2690. random_state = self._get_random_state(random_state)
  2691. size = int(size)
  2692. if size > 1:
  2693. return np.array([self.rvs(dim, size=1, random_state=random_state)
  2694. for i in range(size)])
  2695. dim = self._process_parameters(dim)
  2696. H = np.eye(dim)
  2697. D = np.empty((dim,))
  2698. for n in range(dim-1):
  2699. x = random_state.normal(size=(dim-n,))
  2700. D[n] = np.sign(x[0]) if x[0] != 0 else 1
  2701. x[0] += D[n]*np.sqrt((x*x).sum())
  2702. # Householder transformation
  2703. Hx = (np.eye(dim-n) - 2.*np.outer(x, x)/(x*x).sum())
  2704. mat = np.eye(dim)
  2705. mat[n:, n:] = Hx
  2706. H = np.dot(H, mat)
  2707. D[-1] = (-1)**(dim-1)*D[:-1].prod()
  2708. # Equivalent to np.dot(np.diag(D), H) but faster, apparently
  2709. H = (D*H.T).T
  2710. return H
  2711. special_ortho_group = special_ortho_group_gen()
  2712. class special_ortho_group_frozen(multi_rv_frozen):
  2713. def __init__(self, dim=None, seed=None):
  2714. """
  2715. Create a frozen SO(N) distribution.
  2716. Parameters
  2717. ----------
  2718. dim : scalar
  2719. Dimension of matrices
  2720. seed : None or int or np.random.RandomState instance, optional
  2721. This parameter defines the RandomState object to use for drawing
  2722. random variates.
  2723. If None (or np.random), the global np.random state is used.
  2724. If integer, it is used to seed the local RandomState instance
  2725. Default is None.
  2726. Examples
  2727. --------
  2728. >>> from scipy.stats import special_ortho_group
  2729. >>> g = special_ortho_group(5)
  2730. >>> x = g.rvs()
  2731. """
  2732. self._dist = special_ortho_group_gen(seed)
  2733. self.dim = self._dist._process_parameters(dim)
  2734. def rvs(self, size=1, random_state=None):
  2735. return self._dist.rvs(self.dim, size, random_state)
  2736. class ortho_group_gen(multi_rv_generic):
  2737. r"""
  2738. A matrix-valued O(N) random variable.
  2739. Return a random orthogonal matrix, drawn from the O(N) Haar
  2740. distribution (the only uniform distribution on O(N)).
  2741. The `dim` keyword specifies the dimension N.
  2742. Methods
  2743. -------
  2744. ``rvs(dim=None, size=1, random_state=None)``
  2745. Draw random samples from O(N).
  2746. Parameters
  2747. ----------
  2748. dim : scalar
  2749. Dimension of matrices
  2750. Notes
  2751. ----------
  2752. This class is closely related to `special_ortho_group`.
  2753. Some care is taken to avoid numerical error, as per the paper by Mezzadri.
  2754. References
  2755. ----------
  2756. .. [1] F. Mezzadri, "How to generate random matrices from the classical
  2757. compact groups", :arXiv:`math-ph/0609050v2`.
  2758. Examples
  2759. --------
  2760. >>> from scipy.stats import ortho_group
  2761. >>> x = ortho_group.rvs(3)
  2762. >>> np.dot(x, x.T)
  2763. array([[ 1.00000000e+00, 1.13231364e-17, -2.86852790e-16],
  2764. [ 1.13231364e-17, 1.00000000e+00, -1.46845020e-16],
  2765. [ -2.86852790e-16, -1.46845020e-16, 1.00000000e+00]])
  2766. >>> import scipy.linalg
  2767. >>> np.fabs(scipy.linalg.det(x))
  2768. 1.0
  2769. This generates one random matrix from O(3). It is orthogonal and
  2770. has a determinant of +1 or -1.
  2771. """
  2772. def __init__(self, seed=None):
  2773. super(ortho_group_gen, self).__init__(seed)
  2774. self.__doc__ = doccer.docformat(self.__doc__)
  2775. def _process_parameters(self, dim):
  2776. """
  2777. Dimension N must be specified; it cannot be inferred.
  2778. """
  2779. if dim is None or not np.isscalar(dim) or dim <= 1 or dim != int(dim):
  2780. raise ValueError("Dimension of rotation must be specified,"
  2781. "and must be a scalar greater than 1.")
  2782. return dim
  2783. def rvs(self, dim, size=1, random_state=None):
  2784. """
  2785. Draw random samples from O(N).
  2786. Parameters
  2787. ----------
  2788. dim : integer
  2789. Dimension of rotation space (N).
  2790. size : integer, optional
  2791. Number of samples to draw (default 1).
  2792. Returns
  2793. -------
  2794. rvs : ndarray or scalar
  2795. Random size N-dimensional matrices, dimension (size, dim, dim)
  2796. """
  2797. random_state = self._get_random_state(random_state)
  2798. size = int(size)
  2799. if size > 1:
  2800. return np.array([self.rvs(dim, size=1, random_state=random_state)
  2801. for i in range(size)])
  2802. dim = self._process_parameters(dim)
  2803. H = np.eye(dim)
  2804. for n in range(dim):
  2805. x = random_state.normal(size=(dim-n,))
  2806. # random sign, 50/50, but chosen carefully to avoid roundoff error
  2807. D = np.sign(x[0]) if x[0] != 0 else 1
  2808. x[0] += D*np.sqrt((x*x).sum())
  2809. # Householder transformation
  2810. Hx = -D*(np.eye(dim-n) - 2.*np.outer(x, x)/(x*x).sum())
  2811. mat = np.eye(dim)
  2812. mat[n:, n:] = Hx
  2813. H = np.dot(H, mat)
  2814. return H
  2815. ortho_group = ortho_group_gen()
  2816. class random_correlation_gen(multi_rv_generic):
  2817. r"""
  2818. A random correlation matrix.
  2819. Return a random correlation matrix, given a vector of eigenvalues.
  2820. The `eigs` keyword specifies the eigenvalues of the correlation matrix,
  2821. and implies the dimension.
  2822. Methods
  2823. -------
  2824. ``rvs(eigs=None, random_state=None)``
  2825. Draw random correlation matrices, all with eigenvalues eigs.
  2826. Parameters
  2827. ----------
  2828. eigs : 1d ndarray
  2829. Eigenvalues of correlation matrix.
  2830. Notes
  2831. ----------
  2832. Generates a random correlation matrix following a numerically stable
  2833. algorithm spelled out by Davies & Higham. This algorithm uses a single O(N)
  2834. similarity transformation to construct a symmetric positive semi-definite
  2835. matrix, and applies a series of Givens rotations to scale it to have ones
  2836. on the diagonal.
  2837. References
  2838. ----------
  2839. .. [1] Davies, Philip I; Higham, Nicholas J; "Numerically stable generation
  2840. of correlation matrices and their factors", BIT 2000, Vol. 40,
  2841. No. 4, pp. 640 651
  2842. Examples
  2843. --------
  2844. >>> from scipy.stats import random_correlation
  2845. >>> np.random.seed(514)
  2846. >>> x = random_correlation.rvs((.5, .8, 1.2, 1.5))
  2847. >>> x
  2848. array([[ 1. , -0.20387311, 0.18366501, -0.04953711],
  2849. [-0.20387311, 1. , -0.24351129, 0.06703474],
  2850. [ 0.18366501, -0.24351129, 1. , 0.38530195],
  2851. [-0.04953711, 0.06703474, 0.38530195, 1. ]])
  2852. >>> import scipy.linalg
  2853. >>> e, v = scipy.linalg.eigh(x)
  2854. >>> e
  2855. array([ 0.5, 0.8, 1.2, 1.5])
  2856. """
  2857. def __init__(self, seed=None):
  2858. super(random_correlation_gen, self).__init__(seed)
  2859. self.__doc__ = doccer.docformat(self.__doc__)
  2860. def _process_parameters(self, eigs, tol):
  2861. eigs = np.asarray(eigs, dtype=float)
  2862. dim = eigs.size
  2863. if eigs.ndim != 1 or eigs.shape[0] != dim or dim <= 1:
  2864. raise ValueError("Array 'eigs' must be a vector of length "
  2865. "greater than 1.")
  2866. if np.fabs(np.sum(eigs) - dim) > tol:
  2867. raise ValueError("Sum of eigenvalues must equal dimensionality.")
  2868. for x in eigs:
  2869. if x < -tol:
  2870. raise ValueError("All eigenvalues must be non-negative.")
  2871. return dim, eigs
  2872. def _givens_to_1(self, aii, ajj, aij):
  2873. """Computes a 2x2 Givens matrix to put 1's on the diagonal.
  2874. The input matrix is a 2x2 symmetric matrix M = [ aii aij ; aij ajj ].
  2875. The output matrix g is a 2x2 anti-symmetric matrix of the form
  2876. [ c s ; -s c ]; the elements c and s are returned.
  2877. Applying the output matrix to the input matrix (as b=g.T M g)
  2878. results in a matrix with bii=1, provided tr(M) - det(M) >= 1
  2879. and floating point issues do not occur. Otherwise, some other
  2880. valid rotation is returned. When tr(M)==2, also bjj=1.
  2881. """
  2882. aiid = aii - 1.
  2883. ajjd = ajj - 1.
  2884. if ajjd == 0:
  2885. # ajj==1, so swap aii and ajj to avoid division by zero
  2886. return 0., 1.
  2887. dd = math.sqrt(max(aij**2 - aiid*ajjd, 0))
  2888. # The choice of t should be chosen to avoid cancellation [1]
  2889. t = (aij + math.copysign(dd, aij)) / ajjd
  2890. c = 1. / math.sqrt(1. + t*t)
  2891. if c == 0:
  2892. # Underflow
  2893. s = 1.0
  2894. else:
  2895. s = c*t
  2896. return c, s
  2897. def _to_corr(self, m):
  2898. """
  2899. Given a psd matrix m, rotate to put one's on the diagonal, turning it
  2900. into a correlation matrix. This also requires the trace equal the
  2901. dimensionality. Note: modifies input matrix
  2902. """
  2903. # Check requirements for in-place Givens
  2904. if not (m.flags.c_contiguous and m.dtype == np.float64 and
  2905. m.shape[0] == m.shape[1]):
  2906. raise ValueError()
  2907. d = m.shape[0]
  2908. for i in range(d-1):
  2909. if m[i, i] == 1:
  2910. continue
  2911. elif m[i, i] > 1:
  2912. for j in range(i+1, d):
  2913. if m[j, j] < 1:
  2914. break
  2915. else:
  2916. for j in range(i+1, d):
  2917. if m[j, j] > 1:
  2918. break
  2919. c, s = self._givens_to_1(m[i, i], m[j, j], m[i, j])
  2920. # Use BLAS to apply Givens rotations in-place. Equivalent to:
  2921. # g = np.eye(d)
  2922. # g[i, i] = g[j,j] = c
  2923. # g[j, i] = -s; g[i, j] = s
  2924. # m = np.dot(g.T, np.dot(m, g))
  2925. mv = m.ravel()
  2926. drot(mv, mv, c, -s, n=d,
  2927. offx=i*d, incx=1, offy=j*d, incy=1,
  2928. overwrite_x=True, overwrite_y=True)
  2929. drot(mv, mv, c, -s, n=d,
  2930. offx=i, incx=d, offy=j, incy=d,
  2931. overwrite_x=True, overwrite_y=True)
  2932. return m
  2933. def rvs(self, eigs, random_state=None, tol=1e-13, diag_tol=1e-7):
  2934. """
  2935. Draw random correlation matrices
  2936. Parameters
  2937. ----------
  2938. eigs : 1d ndarray
  2939. Eigenvalues of correlation matrix
  2940. tol : float, optional
  2941. Tolerance for input parameter checks
  2942. diag_tol : float, optional
  2943. Tolerance for deviation of the diagonal of the resulting
  2944. matrix. Default: 1e-7
  2945. Raises
  2946. ------
  2947. RuntimeError
  2948. Floating point error prevented generating a valid correlation
  2949. matrix.
  2950. Returns
  2951. -------
  2952. rvs : ndarray or scalar
  2953. Random size N-dimensional matrices, dimension (size, dim, dim),
  2954. each having eigenvalues eigs.
  2955. """
  2956. dim, eigs = self._process_parameters(eigs, tol=tol)
  2957. random_state = self._get_random_state(random_state)
  2958. m = ortho_group.rvs(dim, random_state=random_state)
  2959. m = np.dot(np.dot(m, np.diag(eigs)), m.T) # Set the trace of m
  2960. m = self._to_corr(m) # Carefully rotate to unit diagonal
  2961. # Check diagonal
  2962. if abs(m.diagonal() - 1).max() > diag_tol:
  2963. raise RuntimeError("Failed to generate a valid correlation matrix")
  2964. return m
  2965. random_correlation = random_correlation_gen()
  2966. class unitary_group_gen(multi_rv_generic):
  2967. r"""
  2968. A matrix-valued U(N) random variable.
  2969. Return a random unitary matrix.
  2970. The `dim` keyword specifies the dimension N.
  2971. Methods
  2972. -------
  2973. ``rvs(dim=None, size=1, random_state=None)``
  2974. Draw random samples from U(N).
  2975. Parameters
  2976. ----------
  2977. dim : scalar
  2978. Dimension of matrices
  2979. Notes
  2980. ----------
  2981. This class is similar to `ortho_group`.
  2982. References
  2983. ----------
  2984. .. [1] F. Mezzadri, "How to generate random matrices from the classical
  2985. compact groups", arXiv:math-ph/0609050v2.
  2986. Examples
  2987. --------
  2988. >>> from scipy.stats import unitary_group
  2989. >>> x = unitary_group.rvs(3)
  2990. >>> np.dot(x, x.conj().T)
  2991. array([[ 1.00000000e+00, 1.13231364e-17, -2.86852790e-16],
  2992. [ 1.13231364e-17, 1.00000000e+00, -1.46845020e-16],
  2993. [ -2.86852790e-16, -1.46845020e-16, 1.00000000e+00]])
  2994. This generates one random matrix from U(3). The dot product confirms that
  2995. it is unitary up to machine precision.
  2996. """
  2997. def __init__(self, seed=None):
  2998. super(unitary_group_gen, self).__init__(seed)
  2999. self.__doc__ = doccer.docformat(self.__doc__)
  3000. def _process_parameters(self, dim):
  3001. """
  3002. Dimension N must be specified; it cannot be inferred.
  3003. """
  3004. if dim is None or not np.isscalar(dim) or dim <= 1 or dim != int(dim):
  3005. raise ValueError("Dimension of rotation must be specified,"
  3006. "and must be a scalar greater than 1.")
  3007. return dim
  3008. def rvs(self, dim, size=1, random_state=None):
  3009. """
  3010. Draw random samples from U(N).
  3011. Parameters
  3012. ----------
  3013. dim : integer
  3014. Dimension of space (N).
  3015. size : integer, optional
  3016. Number of samples to draw (default 1).
  3017. Returns
  3018. -------
  3019. rvs : ndarray or scalar
  3020. Random size N-dimensional matrices, dimension (size, dim, dim)
  3021. """
  3022. random_state = self._get_random_state(random_state)
  3023. size = int(size)
  3024. if size > 1:
  3025. return np.array([self.rvs(dim, size=1, random_state=random_state)
  3026. for i in range(size)])
  3027. dim = self._process_parameters(dim)
  3028. z = 1/math.sqrt(2)*(random_state.normal(size=(dim, dim)) +
  3029. 1j*random_state.normal(size=(dim, dim)))
  3030. q, r = scipy.linalg.qr(z)
  3031. d = r.diagonal()
  3032. q *= d/abs(d)
  3033. return q
  3034. unitary_group = unitary_group_gen()