constraints.py 21 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738
  1. # mypy: allow-untyped-defs
  2. from collections.abc import Callable
  3. from typing import Any, Optional
  4. r"""
  5. The following constraints are implemented:
  6. - ``constraints.boolean``
  7. - ``constraints.cat``
  8. - ``constraints.corr_cholesky``
  9. - ``constraints.dependent``
  10. - ``constraints.greater_than(lower_bound)``
  11. - ``constraints.greater_than_eq(lower_bound)``
  12. - ``constraints.independent(constraint, reinterpreted_batch_ndims)``
  13. - ``constraints.integer_interval(lower_bound, upper_bound)``
  14. - ``constraints.interval(lower_bound, upper_bound)``
  15. - ``constraints.less_than(upper_bound)``
  16. - ``constraints.lower_cholesky``
  17. - ``constraints.lower_triangular``
  18. - ``constraints.MixtureSameFamilyConstraint(base_constraint)``
  19. - ``constraints.multinomial``
  20. - ``constraints.nonnegative``
  21. - ``constraints.nonnegative_integer``
  22. - ``constraints.one_hot``
  23. - ``constraints.positive_integer``
  24. - ``constraints.positive``
  25. - ``constraints.positive_semidefinite``
  26. - ``constraints.positive_definite``
  27. - ``constraints.real_vector``
  28. - ``constraints.real``
  29. - ``constraints.simplex``
  30. - ``constraints.symmetric``
  31. - ``constraints.stack``
  32. - ``constraints.square``
  33. - ``constraints.symmetric``
  34. - ``constraints.unit_interval``
  35. """
  36. import torch
  37. __all__ = [
  38. "Constraint",
  39. "boolean",
  40. "cat",
  41. "corr_cholesky",
  42. "dependent",
  43. "dependent_property",
  44. "greater_than",
  45. "greater_than_eq",
  46. "independent",
  47. "integer_interval",
  48. "interval",
  49. "half_open_interval",
  50. "is_dependent",
  51. "less_than",
  52. "lower_cholesky",
  53. "lower_triangular",
  54. "MixtureSameFamilyConstraint",
  55. "multinomial",
  56. "nonnegative",
  57. "nonnegative_integer",
  58. "one_hot",
  59. "positive",
  60. "positive_semidefinite",
  61. "positive_definite",
  62. "positive_integer",
  63. "real",
  64. "real_vector",
  65. "simplex",
  66. "square",
  67. "stack",
  68. "symmetric",
  69. "unit_interval",
  70. ]
  71. class Constraint:
  72. """
  73. Abstract base class for constraints.
  74. A constraint object represents a region over which a variable is valid,
  75. e.g. within which a variable can be optimized.
  76. Attributes:
  77. is_discrete (bool): Whether constrained space is discrete.
  78. Defaults to False.
  79. event_dim (int): Number of rightmost dimensions that together define
  80. an event. The :meth:`check` method will remove this many dimensions
  81. when computing validity.
  82. """
  83. is_discrete = False # Default to continuous.
  84. event_dim = 0 # Default to univariate.
  85. def check(self, value):
  86. """
  87. Returns a byte tensor of ``sample_shape + batch_shape`` indicating
  88. whether each event in value satisfies this constraint.
  89. """
  90. raise NotImplementedError
  91. def __repr__(self):
  92. return self.__class__.__name__[1:] + "()"
  93. class _Dependent(Constraint):
  94. """
  95. Placeholder for variables whose support depends on other variables.
  96. These variables obey no simple coordinate-wise constraints.
  97. Args:
  98. is_discrete (bool): Optional value of ``.is_discrete`` in case this
  99. can be computed statically. If not provided, access to the
  100. ``.is_discrete`` attribute will raise a NotImplementedError.
  101. event_dim (int): Optional value of ``.event_dim`` in case this
  102. can be computed statically. If not provided, access to the
  103. ``.event_dim`` attribute will raise a NotImplementedError.
  104. """
  105. def __init__(self, *, is_discrete=NotImplemented, event_dim=NotImplemented):
  106. self._is_discrete = is_discrete
  107. self._event_dim = event_dim
  108. super().__init__()
  109. @property
  110. def is_discrete(self) -> bool: # type: ignore[override]
  111. if self._is_discrete is NotImplemented:
  112. raise NotImplementedError(".is_discrete cannot be determined statically")
  113. return self._is_discrete
  114. @property
  115. def event_dim(self) -> int: # type: ignore[override]
  116. if self._event_dim is NotImplemented:
  117. raise NotImplementedError(".event_dim cannot be determined statically")
  118. return self._event_dim
  119. def __call__(self, *, is_discrete=NotImplemented, event_dim=NotImplemented):
  120. """
  121. Support for syntax to customize static attributes::
  122. constraints.dependent(is_discrete=True, event_dim=1)
  123. """
  124. if is_discrete is NotImplemented:
  125. is_discrete = self._is_discrete
  126. if event_dim is NotImplemented:
  127. event_dim = self._event_dim
  128. return _Dependent(is_discrete=is_discrete, event_dim=event_dim)
  129. def check(self, x):
  130. raise ValueError("Cannot determine validity of dependent constraint")
  131. def is_dependent(constraint):
  132. """
  133. Checks if ``constraint`` is a ``_Dependent`` object.
  134. Args:
  135. constraint : A ``Constraint`` object.
  136. Returns:
  137. ``bool``: True if ``constraint`` can be refined to the type ``_Dependent``, False otherwise.
  138. Examples:
  139. >>> import torch
  140. >>> from torch.distributions import Bernoulli
  141. >>> from torch.distributions.constraints import is_dependent
  142. >>> dist = Bernoulli(probs=torch.tensor([0.6], requires_grad=True))
  143. >>> constraint1 = dist.arg_constraints["probs"]
  144. >>> constraint2 = dist.arg_constraints["logits"]
  145. >>> for constraint in [constraint1, constraint2]:
  146. >>> if is_dependent(constraint):
  147. >>> continue
  148. """
  149. return isinstance(constraint, _Dependent)
  150. class _DependentProperty(property, _Dependent):
  151. """
  152. Decorator that extends @property to act like a `Dependent` constraint when
  153. called on a class and act like a property when called on an object.
  154. Example::
  155. class Uniform(Distribution):
  156. def __init__(self, low, high):
  157. self.low = low
  158. self.high = high
  159. @constraints.dependent_property(is_discrete=False, event_dim=0)
  160. def support(self):
  161. return constraints.interval(self.low, self.high)
  162. Args:
  163. fn (Callable): The function to be decorated.
  164. is_discrete (bool): Optional value of ``.is_discrete`` in case this
  165. can be computed statically. If not provided, access to the
  166. ``.is_discrete`` attribute will raise a NotImplementedError.
  167. event_dim (int): Optional value of ``.event_dim`` in case this
  168. can be computed statically. If not provided, access to the
  169. ``.event_dim`` attribute will raise a NotImplementedError.
  170. """
  171. def __init__(
  172. self,
  173. fn: Optional[Callable[..., Any]] = None,
  174. *,
  175. is_discrete: Optional[bool] = NotImplemented,
  176. event_dim: Optional[int] = NotImplemented,
  177. ) -> None:
  178. super().__init__(fn)
  179. self._is_discrete = is_discrete
  180. self._event_dim = event_dim
  181. def __call__(self, fn: Callable[..., Any]) -> "_DependentProperty": # type: ignore[override]
  182. """
  183. Support for syntax to customize static attributes::
  184. @constraints.dependent_property(is_discrete=True, event_dim=1)
  185. def support(self): ...
  186. """
  187. return _DependentProperty(
  188. fn, is_discrete=self._is_discrete, event_dim=self._event_dim
  189. )
  190. class _IndependentConstraint(Constraint):
  191. """
  192. Wraps a constraint by aggregating over ``reinterpreted_batch_ndims``-many
  193. dims in :meth:`check`, so that an event is valid only if all its
  194. independent entries are valid.
  195. """
  196. def __init__(self, base_constraint, reinterpreted_batch_ndims):
  197. assert isinstance(base_constraint, Constraint)
  198. assert isinstance(reinterpreted_batch_ndims, int)
  199. assert reinterpreted_batch_ndims >= 0
  200. self.base_constraint = base_constraint
  201. self.reinterpreted_batch_ndims = reinterpreted_batch_ndims
  202. super().__init__()
  203. @property
  204. def is_discrete(self) -> bool: # type: ignore[override]
  205. return self.base_constraint.is_discrete
  206. @property
  207. def event_dim(self) -> int: # type: ignore[override]
  208. return self.base_constraint.event_dim + self.reinterpreted_batch_ndims
  209. def check(self, value):
  210. result = self.base_constraint.check(value)
  211. if result.dim() < self.reinterpreted_batch_ndims:
  212. expected = self.base_constraint.event_dim + self.reinterpreted_batch_ndims
  213. raise ValueError(
  214. f"Expected value.dim() >= {expected} but got {value.dim()}"
  215. )
  216. result = result.reshape(
  217. result.shape[: result.dim() - self.reinterpreted_batch_ndims] + (-1,)
  218. )
  219. result = result.all(-1)
  220. return result
  221. def __repr__(self):
  222. return f"{self.__class__.__name__[1:]}({repr(self.base_constraint)}, {self.reinterpreted_batch_ndims})"
  223. class MixtureSameFamilyConstraint(Constraint):
  224. """
  225. Constraint for the :class:`~torch.distribution.MixtureSameFamily`
  226. distribution that adds back the rightmost batch dimension before
  227. performing the validity check with the component distribution
  228. constraint.
  229. Args:
  230. base_constraint: The ``Constraint`` object of
  231. the component distribution of
  232. the :class:`~torch.distribution.MixtureSameFamily` distribution.
  233. """
  234. def __init__(self, base_constraint):
  235. assert isinstance(base_constraint, Constraint)
  236. self.base_constraint = base_constraint
  237. super().__init__()
  238. @property
  239. def is_discrete(self) -> bool: # type: ignore[override]
  240. return self.base_constraint.is_discrete
  241. @property
  242. def event_dim(self) -> int: # type: ignore[override]
  243. return self.base_constraint.event_dim
  244. def check(self, value):
  245. """
  246. Check validity of ``value`` as a possible outcome of sampling
  247. the :class:`~torch.distribution.MixtureSameFamily` distribution.
  248. """
  249. unsqueezed_value = value.unsqueeze(-1 - self.event_dim)
  250. result = self.base_constraint.check(unsqueezed_value)
  251. if value.dim() < self.event_dim:
  252. raise ValueError(
  253. f"Expected value.dim() >= {self.event_dim} but got {value.dim()}"
  254. )
  255. num_dim_to_keep = value.dim() - self.event_dim
  256. result = result.reshape(result.shape[:num_dim_to_keep] + (-1,))
  257. result = result.all(-1)
  258. return result
  259. def __repr__(self):
  260. return f"{self.__class__.__name__}({repr(self.base_constraint)})"
  261. class _Boolean(Constraint):
  262. """
  263. Constrain to the two values `{0, 1}`.
  264. """
  265. is_discrete = True
  266. def check(self, value):
  267. return (value == 0) | (value == 1)
  268. class _OneHot(Constraint):
  269. """
  270. Constrain to one-hot vectors.
  271. """
  272. is_discrete = True
  273. event_dim = 1
  274. def check(self, value):
  275. is_boolean = (value == 0) | (value == 1)
  276. is_normalized = value.sum(-1).eq(1)
  277. return is_boolean.all(-1) & is_normalized
  278. class _IntegerInterval(Constraint):
  279. """
  280. Constrain to an integer interval `[lower_bound, upper_bound]`.
  281. """
  282. is_discrete = True
  283. def __init__(self, lower_bound, upper_bound):
  284. self.lower_bound = lower_bound
  285. self.upper_bound = upper_bound
  286. super().__init__()
  287. def check(self, value):
  288. return (
  289. (value % 1 == 0) & (self.lower_bound <= value) & (value <= self.upper_bound)
  290. )
  291. def __repr__(self):
  292. fmt_string = self.__class__.__name__[1:]
  293. fmt_string += (
  294. f"(lower_bound={self.lower_bound}, upper_bound={self.upper_bound})"
  295. )
  296. return fmt_string
  297. class _IntegerLessThan(Constraint):
  298. """
  299. Constrain to an integer interval `(-inf, upper_bound]`.
  300. """
  301. is_discrete = True
  302. def __init__(self, upper_bound):
  303. self.upper_bound = upper_bound
  304. super().__init__()
  305. def check(self, value):
  306. return (value % 1 == 0) & (value <= self.upper_bound)
  307. def __repr__(self):
  308. fmt_string = self.__class__.__name__[1:]
  309. fmt_string += f"(upper_bound={self.upper_bound})"
  310. return fmt_string
  311. class _IntegerGreaterThan(Constraint):
  312. """
  313. Constrain to an integer interval `[lower_bound, inf)`.
  314. """
  315. is_discrete = True
  316. def __init__(self, lower_bound):
  317. self.lower_bound = lower_bound
  318. super().__init__()
  319. def check(self, value):
  320. return (value % 1 == 0) & (value >= self.lower_bound)
  321. def __repr__(self):
  322. fmt_string = self.__class__.__name__[1:]
  323. fmt_string += f"(lower_bound={self.lower_bound})"
  324. return fmt_string
  325. class _Real(Constraint):
  326. """
  327. Trivially constrain to the extended real line `[-inf, inf]`.
  328. """
  329. def check(self, value):
  330. return value == value # False for NANs.
  331. class _GreaterThan(Constraint):
  332. """
  333. Constrain to a real half line `(lower_bound, inf]`.
  334. """
  335. def __init__(self, lower_bound):
  336. self.lower_bound = lower_bound
  337. super().__init__()
  338. def check(self, value):
  339. return self.lower_bound < value
  340. def __repr__(self):
  341. fmt_string = self.__class__.__name__[1:]
  342. fmt_string += f"(lower_bound={self.lower_bound})"
  343. return fmt_string
  344. class _GreaterThanEq(Constraint):
  345. """
  346. Constrain to a real half line `[lower_bound, inf)`.
  347. """
  348. def __init__(self, lower_bound):
  349. self.lower_bound = lower_bound
  350. super().__init__()
  351. def check(self, value):
  352. return self.lower_bound <= value
  353. def __repr__(self):
  354. fmt_string = self.__class__.__name__[1:]
  355. fmt_string += f"(lower_bound={self.lower_bound})"
  356. return fmt_string
  357. class _LessThan(Constraint):
  358. """
  359. Constrain to a real half line `[-inf, upper_bound)`.
  360. """
  361. def __init__(self, upper_bound):
  362. self.upper_bound = upper_bound
  363. super().__init__()
  364. def check(self, value):
  365. return value < self.upper_bound
  366. def __repr__(self):
  367. fmt_string = self.__class__.__name__[1:]
  368. fmt_string += f"(upper_bound={self.upper_bound})"
  369. return fmt_string
  370. class _Interval(Constraint):
  371. """
  372. Constrain to a real interval `[lower_bound, upper_bound]`.
  373. """
  374. def __init__(self, lower_bound, upper_bound):
  375. self.lower_bound = lower_bound
  376. self.upper_bound = upper_bound
  377. super().__init__()
  378. def check(self, value):
  379. return (self.lower_bound <= value) & (value <= self.upper_bound)
  380. def __repr__(self):
  381. fmt_string = self.__class__.__name__[1:]
  382. fmt_string += (
  383. f"(lower_bound={self.lower_bound}, upper_bound={self.upper_bound})"
  384. )
  385. return fmt_string
  386. class _HalfOpenInterval(Constraint):
  387. """
  388. Constrain to a real interval `[lower_bound, upper_bound)`.
  389. """
  390. def __init__(self, lower_bound, upper_bound):
  391. self.lower_bound = lower_bound
  392. self.upper_bound = upper_bound
  393. super().__init__()
  394. def check(self, value):
  395. return (self.lower_bound <= value) & (value < self.upper_bound)
  396. def __repr__(self):
  397. fmt_string = self.__class__.__name__[1:]
  398. fmt_string += (
  399. f"(lower_bound={self.lower_bound}, upper_bound={self.upper_bound})"
  400. )
  401. return fmt_string
  402. class _Simplex(Constraint):
  403. """
  404. Constrain to the unit simplex in the innermost (rightmost) dimension.
  405. Specifically: `x >= 0` and `x.sum(-1) == 1`.
  406. """
  407. event_dim = 1
  408. def check(self, value):
  409. return torch.all(value >= 0, dim=-1) & ((value.sum(-1) - 1).abs() < 1e-6)
  410. class _Multinomial(Constraint):
  411. """
  412. Constrain to nonnegative integer values summing to at most an upper bound.
  413. Note due to limitations of the Multinomial distribution, this currently
  414. checks the weaker condition ``value.sum(-1) <= upper_bound``. In the future
  415. this may be strengthened to ``value.sum(-1) == upper_bound``.
  416. """
  417. is_discrete = True
  418. event_dim = 1
  419. def __init__(self, upper_bound):
  420. self.upper_bound = upper_bound
  421. def check(self, x):
  422. return (x >= 0).all(dim=-1) & (x.sum(dim=-1) <= self.upper_bound)
  423. class _LowerTriangular(Constraint):
  424. """
  425. Constrain to lower-triangular square matrices.
  426. """
  427. event_dim = 2
  428. def check(self, value):
  429. value_tril = value.tril()
  430. return (value_tril == value).view(value.shape[:-2] + (-1,)).min(-1)[0]
  431. class _LowerCholesky(Constraint):
  432. """
  433. Constrain to lower-triangular square matrices with positive diagonals.
  434. """
  435. event_dim = 2
  436. def check(self, value):
  437. value_tril = value.tril()
  438. lower_triangular = (
  439. (value_tril == value).view(value.shape[:-2] + (-1,)).min(-1)[0]
  440. )
  441. positive_diagonal = (value.diagonal(dim1=-2, dim2=-1) > 0).min(-1)[0]
  442. return lower_triangular & positive_diagonal
  443. class _CorrCholesky(Constraint):
  444. """
  445. Constrain to lower-triangular square matrices with positive diagonals and each
  446. row vector being of unit length.
  447. """
  448. event_dim = 2
  449. def check(self, value):
  450. tol = (
  451. torch.finfo(value.dtype).eps * value.size(-1) * 10
  452. ) # 10 is an adjustable fudge factor
  453. row_norm = torch.linalg.norm(value.detach(), dim=-1)
  454. unit_row_norm = (row_norm - 1.0).abs().le(tol).all(dim=-1)
  455. return _LowerCholesky().check(value) & unit_row_norm
  456. class _Square(Constraint):
  457. """
  458. Constrain to square matrices.
  459. """
  460. event_dim = 2
  461. def check(self, value):
  462. return torch.full(
  463. size=value.shape[:-2],
  464. fill_value=(value.shape[-2] == value.shape[-1]),
  465. dtype=torch.bool,
  466. device=value.device,
  467. )
  468. class _Symmetric(_Square):
  469. """
  470. Constrain to Symmetric square matrices.
  471. """
  472. def check(self, value):
  473. square_check = super().check(value)
  474. if not square_check.all():
  475. return square_check
  476. return torch.isclose(value, value.mT, atol=1e-6).all(-2).all(-1)
  477. class _PositiveSemidefinite(_Symmetric):
  478. """
  479. Constrain to positive-semidefinite matrices.
  480. """
  481. def check(self, value):
  482. sym_check = super().check(value)
  483. if not sym_check.all():
  484. return sym_check
  485. return torch.linalg.eigvalsh(value).ge(0).all(-1)
  486. class _PositiveDefinite(_Symmetric):
  487. """
  488. Constrain to positive-definite matrices.
  489. """
  490. def check(self, value):
  491. sym_check = super().check(value)
  492. if not sym_check.all():
  493. return sym_check
  494. return torch.linalg.cholesky_ex(value).info.eq(0)
  495. class _Cat(Constraint):
  496. """
  497. Constraint functor that applies a sequence of constraints
  498. `cseq` at the submatrices at dimension `dim`,
  499. each of size `lengths[dim]`, in a way compatible with :func:`torch.cat`.
  500. """
  501. def __init__(self, cseq, dim=0, lengths=None):
  502. assert all(isinstance(c, Constraint) for c in cseq)
  503. self.cseq = list(cseq)
  504. if lengths is None:
  505. lengths = [1] * len(self.cseq)
  506. self.lengths = list(lengths)
  507. assert len(self.lengths) == len(self.cseq)
  508. self.dim = dim
  509. super().__init__()
  510. @property
  511. def is_discrete(self) -> bool: # type: ignore[override]
  512. return any(c.is_discrete for c in self.cseq)
  513. @property
  514. def event_dim(self) -> int: # type: ignore[override]
  515. return max(c.event_dim for c in self.cseq)
  516. def check(self, value):
  517. assert -value.dim() <= self.dim < value.dim()
  518. checks = []
  519. start = 0
  520. for constr, length in zip(self.cseq, self.lengths):
  521. v = value.narrow(self.dim, start, length)
  522. checks.append(constr.check(v))
  523. start = start + length # avoid += for jit compat
  524. return torch.cat(checks, self.dim)
  525. class _Stack(Constraint):
  526. """
  527. Constraint functor that applies a sequence of constraints
  528. `cseq` at the submatrices at dimension `dim`,
  529. in a way compatible with :func:`torch.stack`.
  530. """
  531. def __init__(self, cseq, dim=0):
  532. assert all(isinstance(c, Constraint) for c in cseq)
  533. self.cseq = list(cseq)
  534. self.dim = dim
  535. super().__init__()
  536. @property
  537. def is_discrete(self) -> bool: # type: ignore[override]
  538. return any(c.is_discrete for c in self.cseq)
  539. @property
  540. def event_dim(self) -> int: # type: ignore[override]
  541. dim = max(c.event_dim for c in self.cseq)
  542. if self.dim + dim < 0:
  543. dim += 1
  544. return dim
  545. def check(self, value):
  546. assert -value.dim() <= self.dim < value.dim()
  547. vs = [value.select(self.dim, i) for i in range(value.size(self.dim))]
  548. return torch.stack(
  549. [constr.check(v) for v, constr in zip(vs, self.cseq)], self.dim
  550. )
  551. # Public interface.
  552. dependent = _Dependent()
  553. dependent_property = _DependentProperty
  554. independent = _IndependentConstraint
  555. boolean = _Boolean()
  556. one_hot = _OneHot()
  557. nonnegative_integer = _IntegerGreaterThan(0)
  558. positive_integer = _IntegerGreaterThan(1)
  559. integer_interval = _IntegerInterval
  560. real = _Real()
  561. real_vector = independent(real, 1)
  562. positive = _GreaterThan(0.0)
  563. nonnegative = _GreaterThanEq(0.0)
  564. greater_than = _GreaterThan
  565. greater_than_eq = _GreaterThanEq
  566. less_than = _LessThan
  567. multinomial = _Multinomial
  568. unit_interval = _Interval(0.0, 1.0)
  569. interval = _Interval
  570. half_open_interval = _HalfOpenInterval
  571. simplex = _Simplex()
  572. lower_triangular = _LowerTriangular()
  573. lower_cholesky = _LowerCholesky()
  574. corr_cholesky = _CorrCholesky()
  575. square = _Square()
  576. symmetric = _Symmetric()
  577. positive_semidefinite = _PositiveSemidefinite()
  578. positive_definite = _PositiveDefinite()
  579. cat = _Cat
  580. stack = _Stack