|
21 | 21 | eager_shape, |
22 | 22 | normalize_pad_width, |
23 | 23 | ) |
24 | | -from ._lib._utils._typing import Array, DType |
| 24 | +from ._lib._utils._typing import Array, Device, DType |
25 | 25 |
|
26 | 26 | __all__ = [ |
27 | 27 | "atleast_nd", |
28 | 28 | "broadcast_shapes", |
29 | 29 | "cov", |
30 | 30 | "create_diagonal", |
| 31 | + "diag_indices", |
31 | 32 | "expand_dims", |
32 | 33 | "isclose", |
33 | 34 | "kron", |
|
36 | 37 | "pad", |
37 | 38 | "searchsorted", |
38 | 39 | "sinc", |
| 40 | + "tril_indices", |
| 41 | + "triu_indices", |
39 | 42 | "unravel_index", |
40 | 43 | ] |
41 | 44 |
|
@@ -310,6 +313,55 @@ def create_diagonal( |
310 | 313 | return _funcs.create_diagonal(x, offset=offset, xp=xp) |
311 | 314 |
|
312 | 315 |
|
| 316 | +def diag_indices( |
| 317 | + n: int, /, *, ndim: int = 2, device: Device | None = None, xp: ModuleType |
| 318 | +) -> tuple[Array, ...]: |
| 319 | + """ |
| 320 | + Return the indices to access the main diagonal of an array. |
| 321 | +
|
| 322 | + Equivalent to :func:`numpy.diag_indices`. |
| 323 | +
|
| 324 | + Parameters |
| 325 | + ---------- |
| 326 | + n : int |
| 327 | + The size of each dimension of the (hyper-)cube ``(n, n, ..., n)`` |
| 328 | + that the returned indices index into. |
| 329 | + ndim : int, optional |
| 330 | + The number of dimensions. Default: ``2``. |
| 331 | + device : Device, optional |
| 332 | + The device on which to place the returned arrays. Default: current device. |
| 333 | + xp : array_namespace |
| 334 | + The standard-compatible namespace to create the indices in. |
| 335 | +
|
| 336 | + Returns |
| 337 | + ------- |
| 338 | + tuple of array |
| 339 | + 1-D integer arrays of length ``n`` that together index |
| 340 | + the main diagonal of an array of shape ``(n,) * ndim``. |
| 341 | +
|
| 342 | + Examples |
| 343 | + -------- |
| 344 | + >>> import array_api_strict as xp |
| 345 | + >>> import array_api_extra as xpx |
| 346 | + >>> rows, cols = xpx.diag_indices(3, xp=xp) |
| 347 | + >>> rows |
| 348 | + Array([0, 1, 2], dtype=array_api_strict.int64) |
| 349 | + >>> cols |
| 350 | + Array([0, 1, 2], dtype=array_api_strict.int64) |
| 351 | + """ |
| 352 | + if n < 0: |
| 353 | + msg = f"`n` must be non-negative, got {n}" |
| 354 | + raise ValueError(msg) |
| 355 | + if ndim < 1: |
| 356 | + msg = f"`ndim` must be >= 1, got {ndim}" |
| 357 | + raise ValueError(msg) |
| 358 | + if device is None and ( |
| 359 | + is_numpy_namespace(xp) or is_cupy_namespace(xp) or is_jax_namespace(xp) |
| 360 | + ): |
| 361 | + return xp.diag_indices(n, ndim=ndim) |
| 362 | + return _funcs.diag_indices(n, ndim=ndim, device=device, xp=xp) |
| 363 | + |
| 364 | + |
313 | 365 | @deprecated( |
314 | 366 | "`xpx.expand_dims` is deprecated and will be removed in v1.0.0. " |
315 | 367 | "`xp.expand_dims` with support for a tuple of ints in `axis` " |
@@ -802,11 +854,7 @@ def pad( |
802 | 854 | # `torch/_numpy`'s implementation (avoids device transfers) |
803 | 855 | pad_width_seq = normalize_pad_width(pad_width, x.ndim) |
804 | 856 | # torch.nn.functional.pad counts dimensions from the last one |
805 | | - flat_pad_width = [ |
806 | | - w |
807 | | - for pair in reversed(pad_width_seq) |
808 | | - for w in pair |
809 | | - ] |
| 857 | + flat_pad_width = [w for pair in reversed(pad_width_seq) for w in pair] |
810 | 858 | return xp.nn.functional.pad(x, tuple(flat_pad_width), value=constant_values) |
811 | 859 |
|
812 | 860 | return _funcs.pad(x, pad_width, constant_values=constant_values, xp=xp) |
@@ -1331,6 +1379,148 @@ def union1d(a: Array, b: Array, /, *, xp: ModuleType | None = None) -> Array: |
1331 | 1379 | return _funcs.union1d(a, b, xp=xp) |
1332 | 1380 |
|
1333 | 1381 |
|
| 1382 | +def tril_indices( |
| 1383 | + n: int, |
| 1384 | + /, |
| 1385 | + *, |
| 1386 | + offset: int = 0, |
| 1387 | + m: int | None = None, |
| 1388 | + device: Device | None = None, |
| 1389 | + xp: ModuleType, |
| 1390 | +) -> tuple[Array, Array]: |
| 1391 | + """ |
| 1392 | + Return the indices of the lower triangle of an ``(n, m)`` array. |
| 1393 | +
|
| 1394 | + Equivalent to :func:`numpy.tril_indices` with parameter ``k`` renamed to |
| 1395 | + ``offset`` to match :func:`array_api.linalg.diagonal`'s naming. |
| 1396 | +
|
| 1397 | + Parameters |
| 1398 | + ---------- |
| 1399 | + n : int |
| 1400 | + The row dimension of the array. |
| 1401 | + offset : int, optional |
| 1402 | + Diagonal offset; ``0`` (default) is the main diagonal. Corresponds |
| 1403 | + to ``k`` in :func:`numpy.tril_indices`. |
| 1404 | + m : int, optional |
| 1405 | + The column dimension. If ``None`` (default), assumed equal to `n`. |
| 1406 | + device : Device, optional |
| 1407 | + The device on which to place the returned arrays. Default: current device. |
| 1408 | + xp : array_namespace |
| 1409 | + The standard-compatible namespace to create the indices in. |
| 1410 | +
|
| 1411 | + Returns |
| 1412 | + ------- |
| 1413 | + tuple of array |
| 1414 | + Row and column indices ``(rows, cols)`` of the lower triangle of |
| 1415 | + the ``(n, m)`` matrix, shifted by `offset`. |
| 1416 | +
|
| 1417 | + Notes |
| 1418 | + ----- |
| 1419 | + The generic fallback uses :func:`array_api.nonzero`, so namespaces without |
| 1420 | + ``nonzero`` are not supported on that path. |
| 1421 | +
|
| 1422 | + Examples |
| 1423 | + -------- |
| 1424 | + >>> import array_api_strict as xp |
| 1425 | + >>> import array_api_extra as xpx |
| 1426 | + >>> rows, cols = xpx.tril_indices(3, xp=xp) |
| 1427 | + >>> rows |
| 1428 | + Array([0, 1, 1, 2, 2, 2], dtype=array_api_strict.int64) |
| 1429 | + >>> cols |
| 1430 | + Array([0, 0, 1, 0, 1, 2], dtype=array_api_strict.int64) |
| 1431 | + """ |
| 1432 | + if n < 0: |
| 1433 | + msg = f"`n` must be non-negative, got {n}" |
| 1434 | + raise ValueError(msg) |
| 1435 | + if m is not None and m < 0: |
| 1436 | + msg = f"`m` must be non-negative, got {m}" |
| 1437 | + raise ValueError(msg) |
| 1438 | + if device is None and ( |
| 1439 | + is_numpy_namespace(xp) |
| 1440 | + or is_cupy_namespace(xp) |
| 1441 | + or is_jax_namespace(xp) |
| 1442 | + or is_dask_namespace(xp) |
| 1443 | + ): |
| 1444 | + return xp.tril_indices(n, k=offset, m=m) |
| 1445 | + if is_torch_namespace(xp): |
| 1446 | + # `torch.tril_indices` returns a 2xN tensor, not a tuple, and |
| 1447 | + # takes (row, col) rather than (n, *, m=None). |
| 1448 | + cols = n if m is None else m |
| 1449 | + idx = xp.tril_indices(n, cols, offset=offset, device=device) |
| 1450 | + return (idx[0], idx[1]) |
| 1451 | + return _funcs.tril_indices(n, offset=offset, m=m, device=device, xp=xp) |
| 1452 | + |
| 1453 | + |
| 1454 | +def triu_indices( |
| 1455 | + n: int, |
| 1456 | + /, |
| 1457 | + *, |
| 1458 | + offset: int = 0, |
| 1459 | + m: int | None = None, |
| 1460 | + device: Device | None = None, |
| 1461 | + xp: ModuleType, |
| 1462 | +) -> tuple[Array, Array]: |
| 1463 | + """ |
| 1464 | + Return the indices of the upper triangle of an ``(n, m)`` array. |
| 1465 | +
|
| 1466 | + Equivalent to :func:`numpy.triu_indices` with parameter ``k`` renamed to |
| 1467 | + ``offset`` to match :func:`array_api.linalg.diagonal`'s naming. |
| 1468 | +
|
| 1469 | + Parameters |
| 1470 | + ---------- |
| 1471 | + n : int |
| 1472 | + The row dimension of the array. |
| 1473 | + offset : int, optional |
| 1474 | + Diagonal offset; ``0`` (default) is the main diagonal. Corresponds |
| 1475 | + to ``k`` in :func:`numpy.triu_indices`. |
| 1476 | + m : int, optional |
| 1477 | + The column dimension. If ``None`` (default), assumed equal to `n`. |
| 1478 | + device : Device, optional |
| 1479 | + The device on which to place the returned arrays. Default: current device. |
| 1480 | + xp : array_namespace |
| 1481 | + The standard-compatible namespace to create the indices in. |
| 1482 | +
|
| 1483 | + Returns |
| 1484 | + ------- |
| 1485 | + tuple of array |
| 1486 | + Row and column indices ``(rows, cols)`` of the upper triangle of |
| 1487 | + the ``(n, m)`` matrix, shifted by `offset`. |
| 1488 | +
|
| 1489 | + Notes |
| 1490 | + ----- |
| 1491 | + The generic fallback uses :func:`array_api.nonzero`, so namespaces without |
| 1492 | + ``nonzero`` are not supported on that path. |
| 1493 | +
|
| 1494 | + Examples |
| 1495 | + -------- |
| 1496 | + >>> import array_api_strict as xp |
| 1497 | + >>> import array_api_extra as xpx |
| 1498 | + >>> rows, cols = xpx.triu_indices(3, xp=xp) |
| 1499 | + >>> rows |
| 1500 | + Array([0, 0, 0, 1, 1, 2], dtype=array_api_strict.int64) |
| 1501 | + >>> cols |
| 1502 | + Array([0, 1, 2, 1, 2, 2], dtype=array_api_strict.int64) |
| 1503 | + """ |
| 1504 | + if n < 0: |
| 1505 | + msg = f"`n` must be non-negative, got {n}" |
| 1506 | + raise ValueError(msg) |
| 1507 | + if m is not None and m < 0: |
| 1508 | + msg = f"`m` must be non-negative, got {m}" |
| 1509 | + raise ValueError(msg) |
| 1510 | + if device is None and ( |
| 1511 | + is_numpy_namespace(xp) |
| 1512 | + or is_cupy_namespace(xp) |
| 1513 | + or is_jax_namespace(xp) |
| 1514 | + or is_dask_namespace(xp) |
| 1515 | + ): |
| 1516 | + return xp.triu_indices(n, k=offset, m=m) |
| 1517 | + if is_torch_namespace(xp): |
| 1518 | + cols = n if m is None else m |
| 1519 | + idx = xp.triu_indices(n, cols, offset=offset, device=device) |
| 1520 | + return (idx[0], idx[1]) |
| 1521 | + return _funcs.triu_indices(n, offset=offset, m=m, device=device, xp=xp) |
| 1522 | + |
| 1523 | + |
1334 | 1524 | def unravel_index( |
1335 | 1525 | indices: Array, |
1336 | 1526 | shape: tuple[int, ...], |
|
0 commit comments