Skip to content

Vector Functions

from amads.core.vector_transforms_checks import *
from amads.core.vectors_sets import *

Author: Mark Gotham


rotate

rotate(
    vector: Union[tuple[int, ...], list[int]],
    steps: Union[int, None] = None,
) -> Union[tuple[int, ...], list[int]]

this serves equivalently for "phase shifting" of rhythm and "transposition" of pitch.

Parameters:

  • vector (Union[tuple[int, ...], list[int]]) –

    Any tuple or list of any elements. We expect to work with a list of integers representing a vector.

  • steps (Union[int, None], default: None ) –

    How many steps to rotate. Or, equivalently, the nth index of the input list becomes the 0th index of the new. If unspecified, use the half cycle: int(cycle_length / 2).

Returns:

  • Union[tuple[int, ...], list[int]]

    The input (tuple or list), rotated. Same length.

Examples:

>>> start = (0, 1, 2, 3)
>>> rotate(start, 1)
(1, 2, 3, 0)
>>> rotate(start, -1)
(3, 0, 1, 2)
>>> rotate(start) # note no steps specified
(2, 3, 0, 1)
Source code in amads/core/vector_transforms_checks.py
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
def rotate(
    vector: Union[tuple[int, ...], list[int]], steps: Union[int, None] = None
) -> Union[tuple[int, ...], list[int]]:
    """
    this serves equivalently for
    "phase shifting" of rhythm and
    "transposition" of pitch.

    Parameters
    ----------
    vector : Union[tuple[int, ...], list[int]]
        Any tuple or list of any elements.
        We expect to work with a list of integers representing a vector.
    steps: Optional[int]
        How many steps to rotate.
        Or, equivalently, the nth index of the input list becomes the 0th index of the new.
        If unspecified, use the half cycle: int(cycle_length / 2).

    Returns
    -------
    Union[tuple[int, ...], list[int]]
        The input (tuple or list), rotated. Same length.

    Examples
    --------

    >>> start = (0, 1, 2, 3)
    >>> rotate(start, 1)
    (1, 2, 3, 0)

    >>> rotate(start, -1)
    (3, 0, 1, 2)

    >>> rotate(start) # note no steps specified
    (2, 3, 0, 1)

    """
    if steps is None:
        steps = int(len(vector) / 2)

    return vector[steps:] + vector[:steps]

mirror

mirror(
    vector: Sequence, index_of_symmetry: Union[int, None] = None
) -> Sequence

Reverse a vector (or any ordered iterable).

Parameters:

  • vector (Sequence) –

    Any ordered succession of any elements. We expect integers representing a vector, but do not enforce it.

  • index_of_symmetry (Union[int, None], default: None ) –

    Defaults to None, in which case, standard reflection of the form [::-1]. Alternatively, specify an index to rotate about, e.g., for the reverse function in convolution use 0. This is equivalent to mirror and rotation. See notes at rotate.

Returns:

  • Sequence

    The input (tuple, list, etc.), mirrored. Same length, but return is always a tuple.

Examples:

>>> test_case = (0, 1, 2, 3, 4, 5)
>>> mirror(test_case)
(5, 4, 3, 2, 1, 0)
>>> mirror(test_case, index_of_symmetry=0)
(0, 5, 4, 3, 2, 1)
>>> mirror(test_case, index_of_symmetry=1)
(1, 0, 5, 4, 3, 2)

We will often use this for a 12-element indicator vector.

>>> c_vector = (1, 0, 0, 0, 1, 0, 0, 1, 0, 0, 0, 0)
>>> mirror(c_vector, index_of_symmetry=0)
(1, 0, 0, 0, 0, 1, 0, 0, 1, 0, 0, 0)
Source code in amads/core/vector_transforms_checks.py
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
def mirror(
    vector: Sequence, index_of_symmetry: Union[int, None] = None
) -> Sequence:
    """
    Reverse a vector (or any ordered iterable).

    Parameters
    ----------
    vector: Sequence
        Any ordered succession of any elements.
        We expect integers representing a vector, but do not enforce it.
    index_of_symmetry: Union[int, None]
        Defaults to None, in which case, standard reflection of the
        form `[::-1]`. Alternatively, specify an index to rotate about,
        e.g., for the reverse function in convolution use 0. This is
        equivalent to mirror and rotation. See notes at [`rotate`]
        [amads.core.vector_transforms_checks.rotate].

    Returns
    -------
    Sequence
        The input (tuple, list, etc.), mirrored. Same length, but return is always a tuple.

    Examples
    --------
    >>> test_case = (0, 1, 2, 3, 4, 5)
    >>> mirror(test_case)
    (5, 4, 3, 2, 1, 0)

    >>> mirror(test_case, index_of_symmetry=0)
    (0, 5, 4, 3, 2, 1)

    >>> mirror(test_case, index_of_symmetry=1)
    (1, 0, 5, 4, 3, 2)

    We will often use this for a 12-element indicator vector.
    >>> c_vector = (1, 0, 0, 0, 1, 0, 0, 1, 0, 0, 0, 0)
    >>> mirror(c_vector, index_of_symmetry=0)
    (1, 0, 0, 0, 0, 1, 0, 0, 1, 0, 0, 0)

    """
    if index_of_symmetry is not None:
        return vector[index_of_symmetry::-1] + vector[-1:index_of_symmetry:-1]
    else:
        return vector[::-1]

complement

complement(indicator_vector: tuple[int, ...]) -> tuple[int, ...]

Provide the complement of an indicator vector.

Returns:

  • tuple[int]

Examples:

>>> complement((1, 0, 1, 0))
(0, 1, 0, 1)
Source code in amads/core/vector_transforms_checks.py
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
def complement(indicator_vector: tuple[int, ...]) -> tuple[int, ...]:
    """
    Provide the complement of an indicator vector.

    Returns
    -------
    tuple[int]

    Examples
    --------
    >>> complement((1, 0, 1, 0))
    (0, 1, 0, 1)
    """
    if not is_indicator_vector(indicator_vector):
        raise ValueError(
            "This is to be called only on binary tuples representing indicator vectors."
        )
    return tuple(1 - x for x in indicator_vector)

change_cycle_length

change_cycle_length(
    start_vector: tuple,
    destination_length: int,
    return_indices_not_indicator: bool = True,
) -> tuple

Change the cycle length of a vector by mapping each point to the nearest equivalent in the new length.

Examples:

>>> tresillo = (1, 0, 0, 1, 0, 0, 1, 0)
>>> change_cycle_length(tresillo, 9)
(0, 3, 7)
>>> change_cycle_length(tresillo, 12)
(0, 4, 9)
>>> change_cycle_length(start_vector=tresillo, destination_length=9,
...                     return_indices_not_indicator=False)
(1, 0, 0, 1, 0, 0, 0, 1, 0)
Source code in amads/core/vector_transforms_checks.py
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
def change_cycle_length(
    start_vector: tuple,
    destination_length: int,
    return_indices_not_indicator: bool = True,
) -> tuple:
    """
    Change the cycle length of a vector by mapping each point to the nearest equivalent in the new length.

    Examples
    --------
    >>> tresillo = (1, 0, 0, 1, 0, 0, 1, 0)
    >>> change_cycle_length(tresillo, 9)
    (0, 3, 7)

    >>> change_cycle_length(tresillo, 12)
    (0, 4, 9)

    >>> change_cycle_length(start_vector=tresillo, destination_length=9,
    ...                     return_indices_not_indicator=False)
    (1, 0, 0, 1, 0, 0, 0, 1, 0)

    """
    start_indices = indicator_to_indices(start_vector)
    start_len = len(start_vector)
    new_indices = [
        round(destination_length * i / start_len) for i in start_indices
    ]
    if return_indices_not_indicator:
        return tuple(new_indices)
    else:
        return indices_to_indicator(
            new_indices, indicator_length=destination_length
        )

is_rotation_equivalent

is_rotation_equivalent(a: tuple, b: tuple) -> bool

Test for rotation equivalence. This is applicable to indicator vectors, interval sequences, and more (any tuple, list, or even string).

Examples:

Indicator:

>>> is_rotation_equivalent((1, 0, 0), (0, 1, 0))
True
>>> is_rotation_equivalent((1, 0, 0), (1, 1, 0))
False

Intervals:

>>> is_rotation_equivalent((3, 3, 2), (3, 2, 3))
True
>>> is_rotation_equivalent((3, 3, 2), (2, 3, 2))
False

Trivial case:

>>> is_rotation_equivalent((1, 0, 0), (1, 0, 0))
True
Source code in amads/core/vector_transforms_checks.py
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
def is_rotation_equivalent(a: tuple, b: tuple) -> bool:
    """
    Test for rotation equivalence.
    This is applicable to indicator vectors, interval sequences, and more
    (any tuple, list, or even string).

    Examples
    --------

    Indicator:

    >>> is_rotation_equivalent((1, 0, 0), (0, 1, 0))
    True

    >>> is_rotation_equivalent((1, 0, 0), (1, 1, 0))
    False

    Intervals:

    >>> is_rotation_equivalent((3, 3, 2), (3, 2, 3))
    True

    >>> is_rotation_equivalent((3, 3, 2), (2, 3, 2))
    False

    Trivial case:
    >>> is_rotation_equivalent((1, 0, 0), (1, 0, 0))
    True

    """
    if len(a) != len(b):
        raise ValueError("The vectors must be of the same length.")

    doubled = b + b
    return len(a) == 0 or any(
        doubled[i : i + len(a)] == a for i in range(len(b))
    )

is_maximally_even

is_maximally_even(indicator_vector: tuple) -> bool

Checks if an indicator vector (tuple of 0s and 1s) is maximally even, meaning the 1s and 0s are as evenly distributed as possible.

Specifically, this works simply by converting to intervals and running the following basic checks: First, there must be no more than 2 interval types. If there is only 1 type, True (perfectly even). If there are 2 types, then those two must differ in value by 1.

Parameters:

  • indicator_vector (tuple) –

    An indicator_vector (only 0s and/or 1s).

Returns:

  • bool

    True if the pattern is maximally even, False otherwise.

Examples:

>>> is_maximally_even((1, 0, 0, 1, 0, 0, 1, 0, 0, 0, 1, 0, 0, 1, 0, 0))
True
>>> is_maximally_even((1, 0, 0, 1, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0))
False
>>> is_maximally_even((0, 0, 1, 0, 0, 1, 0, 0, 1))
True
>>> is_maximally_even((1, 1, 0, 0, 1))
False
>>> is_maximally_even((0, 1, 0, 1, 0, 1))
True

This works with the indicator_to_interval function. Let's look at those representations for the bembé cycle and a comparison case.

>>> bembé = (1, 0, 1, 0, 1, 1, 0, 1, 0, 1, 0, 1)
>>> indicator_to_interval(bembé, adjacent_not_all=True)
(2, 2, 1, 2, 2, 2, 1)
>>> indicator_to_interval(bembé, adjacent_not_all=True, sequence_not_vector=False)
(2, 5, 0, 0, 0, 0)
>>> indicator_to_interval(bembé, adjacent_not_all=False)
(2, 5, 4, 3, 6, 1)
>>> is_maximally_even(bembé)
True

For our comparison case, we create another cycle that also has: a) 7 elements in a 12-unit cycle,

>>> not_bembé =  (1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 1)
>>> not_bembé.count(1)
7
>>> len(not_bembé)
12

b) the same first-order interval set of 5 x 2s and 2 x 1s,

>>> indicator_to_interval(not_bembé, adjacent_not_all=True, sequence_not_vector=False)
(2, 5, 0, 0, 0, 0)

... but for which the those 2 x 1s are together

>>> indicator_to_interval(not_bembé, adjacent_not_all=True)
(2, 2, 2, 2, 2, 1, 1)

... making it not maximally even.

>>> is_maximally_even(not_bembé)
False
Source code in amads/core/vector_transforms_checks.py
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
def is_maximally_even(indicator_vector: tuple) -> bool:
    """
    Checks if an indicator vector (tuple of 0s and 1s) is maximally even,
    meaning the 1s and 0s are as evenly distributed as possible.

    Specifically, this works simply by converting to intervals and running the following basic checks:
    First, there must be no more than 2 interval types.
    If there is only 1 type, True (perfectly even).
    If there are 2 types, then those two must differ in value by 1.

    Parameters
    ----------
    indicator_vector: tuple[int, ...]
        An indicator_vector (only 0s and/or 1s).

    Returns
    -------
    bool
        True if the pattern is maximally even, False otherwise.

    Examples
    --------
    >>> is_maximally_even((1, 0, 0, 1, 0, 0, 1, 0, 0, 0, 1, 0, 0, 1, 0, 0))
    True
    >>> is_maximally_even((1, 0, 0, 1, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0))
    False
    >>> is_maximally_even((0, 0, 1, 0, 0, 1, 0, 0, 1))
    True
    >>> is_maximally_even((1, 1, 0, 0, 1))
    False
    >>> is_maximally_even((0, 1, 0, 1, 0, 1))
    True

    This works with the `indicator_to_interval` function.
    Let's look at those representations for the bembé cycle and a comparison case.

    >>> bembé = (1, 0, 1, 0, 1, 1, 0, 1, 0, 1, 0, 1)
    >>> indicator_to_interval(bembé, adjacent_not_all=True)
    (2, 2, 1, 2, 2, 2, 1)

    >>> indicator_to_interval(bembé, adjacent_not_all=True, sequence_not_vector=False)
    (2, 5, 0, 0, 0, 0)

    >>> indicator_to_interval(bembé, adjacent_not_all=False)
    (2, 5, 4, 3, 6, 1)

    >>> is_maximally_even(bembé)
    True

    For our comparison case, we create another cycle that also has:
    a) 7 elements in a 12-unit cycle,

    >>> not_bembé =  (1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 1)
    >>> not_bembé.count(1)
    7

    >>> len(not_bembé)
    12

    b) the same first-order interval set of 5 x 2s and 2 x 1s,

    >>> indicator_to_interval(not_bembé, adjacent_not_all=True, sequence_not_vector=False)
    (2, 5, 0, 0, 0, 0)

    ... but for which the those 2 x 1s are together

    >>> indicator_to_interval(not_bembé, adjacent_not_all=True)
    (2, 2, 2, 2, 2, 1, 1)

    ... making it not maximally even.

    >>> is_maximally_even(not_bembé)
    False

    """
    if not is_indicator_vector(indicator_vector):
        raise ValueError(
            f"The `indicator_vector` argument (set as {indicator_vector}) should be an indicator vector."
        )

    k = indicator_vector.count(1)
    n = len(indicator_vector)

    prototype_k_in_n = indices_to_indicator(tuple(max_even_k_in_n(k, n)), n)

    return is_rotation_equivalent(prototype_k_in_n, indicator_vector)

max_even_k_in_n

max_even_k_in_n(k: int, n: int) -> set[int]

Make a maximally even pattern of k elements in an n-length cycle. Normally, we would expect k to be less than n, but this is not strictly required. Larger values of k simply produce duplicate entries in n that are ignored in the returned set.

Examples:

>>> max_even_k_in_n(3, 8)
{0, 3, 5}
>>> max_even_k_in_n(7, 12)
{0, 2, 3, 5, 7, 9, 10}
>>> max_even_k_in_n(5, 16)
{0, 3, 6, 10, 13}
Source code in amads/core/vector_transforms_checks.py
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
def max_even_k_in_n(k: int, n: int) -> set[int]:
    """
    Make a maximally even pattern of k elements in an n-length cycle.
    Normally, we would expect k to be less than n, but this is not strictly required.
    Larger values of k simply produce duplicate entries in n that are ignored in the
    returned set.

    Examples
    --------
    >>> max_even_k_in_n(3, 8)
    {0, 3, 5}

    >>> max_even_k_in_n(7, 12)
    {0, 2, 3, 5, 7, 9, 10}

    >>> max_even_k_in_n(5, 16)
    {0, 3, 6, 10, 13}

    """
    return set([round(n * i / k) for i in range(k)])

rotation_distinct_patterns

rotation_distinct_patterns(
    vector_patterns: tuple[tuple, ...],
) -> tuple[tuple, ...]

Given two or more vectors of the same length, test rotation equivalence among them. Return the list of rotation distinct pattens: the returned patterns are not rotation equivalent to each other, but all tested rhythms (in the argument) are rotation equivalent to one of those returned patterns.

Examples:

Here are ten canonical 12-unit bell pattern rhythms:

>>> Soli = (2, 2, 2, 2, 1, 2, 1)
>>> Tambú = (2, 2, 2, 1, 2, 2, 1)
>>> Bembé = (2, 2, 1, 2, 2, 2, 1)
>>> Bembé_2 = (1, 2, 2, 1, 2, 2, 2)
>>> Yoruba = (2, 2, 1, 2, 2, 1, 2)
>>> Tonada = (2, 1, 2, 1, 2, 2, 2)
>>> Asaadua = (2, 2, 2, 1, 2, 1, 2)
>>> Sorsonet = (1, 1, 2, 2, 2, 2, 2)
>>> Bemba = (2, 1, 2, 2, 2, 1, 2)
>>> Ashanti = (2, 1, 2, 2, 1, 2, 2)
>>> ten_tuples = (Asaadua, Ashanti, Bemba, Bembé, Bembé_2, Soli, Sorsonet, Tambú, Tonada, Yoruba)

Collectively, they have 3 distinct patterns.

>>> len(rotation_distinct_patterns(ten_tuples))
3
Source code in amads/core/vector_transforms_checks.py
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
def rotation_distinct_patterns(
    vector_patterns: tuple[tuple, ...]
) -> tuple[tuple, ...]:
    """
    Given two or more vectors of the same length,
    test rotation equivalence among them.
    Return the list of rotation distinct pattens:
    the returned patterns are not rotation equivalent to each other,
    but all tested rhythms (in the argument) are rotation equivalent
    to one of those returned patterns.

    Examples
    --------
    Here are ten canonical 12-unit bell pattern rhythms:

    >>> Soli = (2, 2, 2, 2, 1, 2, 1)
    >>> Tambú = (2, 2, 2, 1, 2, 2, 1)
    >>> Bembé = (2, 2, 1, 2, 2, 2, 1)
    >>> Bembé_2 = (1, 2, 2, 1, 2, 2, 2)
    >>> Yoruba = (2, 2, 1, 2, 2, 1, 2)
    >>> Tonada = (2, 1, 2, 1, 2, 2, 2)
    >>> Asaadua = (2, 2, 2, 1, 2, 1, 2)
    >>> Sorsonet = (1, 1, 2, 2, 2, 2, 2)
    >>> Bemba = (2, 1, 2, 2, 2, 1, 2)
    >>> Ashanti = (2, 1, 2, 2, 1, 2, 2)
    >>> ten_tuples = (Asaadua, Ashanti, Bemba, Bembé, Bembé_2, Soli, Sorsonet, Tambú, Tonada, Yoruba)

    Collectively, they have 3 distinct patterns.

    >>> len(rotation_distinct_patterns(ten_tuples))
    3

    """
    return_values = [vector_patterns[0]]  # At least one
    for index in range(1, len(vector_patterns)):
        for prototype in return_values:
            if is_rotation_equivalent(prototype, vector_patterns[index]):
                break
        else:
            return_values.append(vector_patterns[index])

    return tuple(return_values)

indicator_to_indices

indicator_to_indices(
    vector: Union[list[int], tuple[int, ...]], wrap: bool = False
) -> tuple

Simple mapping from an indicator vector for where events fall, to a tuple of the corresponding indices.

Parameters:

  • vector (Union[list[int], tuple[int, ...]]) –

    an indicator vector

  • wrap (bool, default: False ) –

    if true, the first element of vector is appended to vector before computing indices, so if the element is non-zero, the length of vector will appear in the result.

Returns:

  • tuple[int, ...]

    a tuple containing the indices for which vector is non-zero.

Examples:

>>> bembé = (1, 0, 1, 0, 1, 1, 0, 1, 0, 1, 0, 1) # adjacent only
>>> indicator_to_indices(bembé)
(0, 2, 4, 5, 7, 9, 11)
>>> indicator_to_indices(bembé, wrap=True)
(0, 2, 4, 5, 7, 9, 11, 12)
>>> shiko = (1, 0, 0, 0, 1, 0, 1, 0, 0, 0, 1, 0, 1, 0, 0, 0) # adjacent only
>>> indicator_to_indices(shiko)
(0, 4, 6, 10, 12)
>>> indicator_to_indices(shiko, wrap=True)
(0, 4, 6, 10, 12, 16)
Source code in amads/core/vector_transforms_checks.py
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
def indicator_to_indices(
    vector: Union[list[int], tuple[int, ...]], wrap: bool = False
) -> tuple:
    """
    Simple mapping from an indicator vector for where events fall,
    to a tuple of the corresponding indices.

    Parameters
    ----------
    vector: Union[list[int], tuple[int, ...]]
        an indicator vector

    wrap: bool
        if true, the first element of `vector` is appended to `vector`
        before computing indices, so if the element is non-zero, the
        length of `vector` will appear in the result.

    Returns
    -------
    tuple[int, ...]
        a tuple containing the indices for which `vector` is non-zero.

    Examples
    --------
    >>> bembé = (1, 0, 1, 0, 1, 1, 0, 1, 0, 1, 0, 1) # adjacent only
    >>> indicator_to_indices(bembé)
    (0, 2, 4, 5, 7, 9, 11)

    >>> indicator_to_indices(bembé, wrap=True)
    (0, 2, 4, 5, 7, 9, 11, 12)

    >>> shiko = (1, 0, 0, 0, 1, 0, 1, 0, 0, 0, 1, 0, 1, 0, 0, 0) # adjacent only
    >>> indicator_to_indices(shiko)
    (0, 4, 6, 10, 12)

    >>> indicator_to_indices(shiko, wrap=True)
    (0, 4, 6, 10, 12, 16)

    """
    if wrap:
        vector = vector + vector[:1]

    return tuple(i for i, v in enumerate(vector) if v)

indices_to_indicator

indices_to_indicator(
    indices_vector: Union[list[int], tuple[int, ...]],
    indicator_length: Optional[int] = None,
) -> tuple

Simple mapping from indices to indicator vector.

Parameters:

  • indices_vector (Union[list[int], tuple[int, ...]]) –

    A vector of indices (0, 2, 4, 5, 7, 9, 11). Monotonic increase is expected but not required.

  • indicator_length (Optional[int], default: None ) –

    optionally specify the length of the output indicator vector. If not specified, we use the highest index in the indices vector.

Examples:

Round trip

>>> bembé = (1, 0, 1, 0, 1, 1, 0, 1, 0, 1, 0, 1)
>>> indices = indicator_to_indices(bembé)
>>> indices
(0, 2, 4, 5, 7, 9, 11)

No indicator_length is needed for default

>>> round_trip = indices_to_indicator(indices)
>>> round_trip
(1, 0, 1, 0, 1, 1, 0, 1, 0, 1, 0, 1)
>>> round_trip == bembé
True

The indicator_length can, however, be specified:

>>> indices_to_indicator(indices, indicator_length=12)
(1, 0, 1, 0, 1, 1, 0, 1, 0, 1, 0, 1)

And the indicator_length can extend the indicator as needed:

>>> indices_to_indicator(indices, indicator_length=14)
(1, 0, 1, 0, 1, 1, 0, 1, 0, 1, 0, 1, 0, 0)
Source code in amads/core/vector_transforms_checks.py
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
def indices_to_indicator(
    indices_vector: Union[list[int], tuple[int, ...]],
    indicator_length: Optional[int] = None,
) -> tuple:
    """
    Simple mapping from indices to indicator vector.

    Parameters
    ----------
    indices_vector: Union[list[int], tuple[int, ...]]
        A vector of indices (0, 2, 4, 5, 7, 9, 11).
        Monotonic increase is expected but not required.
    indicator_length: Optional[int]
        optionally specify the length of the output indicator vector.
        If not specified, we use the highest index in the indices vector.

    Examples
    --------
    Round trip
    >>> bembé = (1, 0, 1, 0, 1, 1, 0, 1, 0, 1, 0, 1)
    >>> indices = indicator_to_indices(bembé)
    >>> indices
    (0, 2, 4, 5, 7, 9, 11)

    No `indicator_length` is needed for default

    >>> round_trip = indices_to_indicator(indices)
    >>> round_trip
    (1, 0, 1, 0, 1, 1, 0, 1, 0, 1, 0, 1)

    >>> round_trip == bembé
    True

    The `indicator_length` can, however, be specified:
    >>> indices_to_indicator(indices, indicator_length=12)
    (1, 0, 1, 0, 1, 1, 0, 1, 0, 1, 0, 1)

    And the `indicator_length` can extend the indicator as needed:
    >>> indices_to_indicator(indices, indicator_length=14)
    (1, 0, 1, 0, 1, 1, 0, 1, 0, 1, 0, 1, 0, 0)

    """
    if indicator_length is None:
        indicator_length = max(indices_vector) + 1
    index_set = set(indices_vector)
    return tuple(1 if i in index_set else 0 for i in range(indicator_length))

indicator_to_interval

indicator_to_interval(
    vector: Union[list[int], tuple[int, ...]],
    wrap: bool = True,
    adjacent_not_all: bool = True,
    sequence_not_vector: bool = True,
) -> tuple

Given a vector (assumed to be indicator) convert from 1/0 at each index to the intervals between the 1s.

Parameters:

  • vector (Union[list[int], tuple[int, ...]]) –

    an indicator vector representing positions.

  • wrap (bool, default: True ) –

    wrap the cycle, duplicating the first element at the end to include that (possible) interval.

  • adjacent_not_all (bool, default: True ) –

    If True, form the set of intervals between pairs of adjacent positions. If False, form the set of intervals between all pairs.

  • sequence_not_vector (bool, default: True ) –

    In the case of adjacent intervals, True means express the result as an interval sequence, and False means compute an interval vector containing the counts of intervals of size 1, 2, 3, etc. If adjacent_not_all is False (meaning all pairs), the result is always an interval vector.

Examples:

Example 1:

>>> bembé = (1, 0, 1, 0, 1, 1, 0, 1, 0, 1, 0, 1)

Adjacent intervals expressed as a sequence:

>>> indicator_to_interval(bembé)
(2, 2, 1, 2, 2, 2, 1)

Wrap is optional:

>>> indicator_to_interval(bembé, wrap=False)
(2, 2, 1, 2, 2, 2)

Adjacent intervals expressed as an interval vector:

>>> indicator_to_interval(bembé, sequence_not_vector=False)
(2, 5, 0, 0, 0, 0)

All distances (not just the adjacent pairs), which is necessarily expressed as an interval vector:

>>> indicator_to_interval(bembé, adjacent_not_all=False)
(2, 5, 4, 3, 6, 1)

Example 2:

>>> shiko = (1, 0, 0, 0, 1, 0, 1, 0, 0, 0, 1, 0, 1, 0, 0, 0)

Adjacent intervals expressed as a sequence:

>>> indicator_to_interval(shiko)
(4, 2, 4, 2, 4)

Adjacent intervals expressed as an interval vector:

>>> indicator_to_interval(shiko, sequence_not_vector=False)
(0, 2, 0, 3, 0, 0, 0, 0)

All distances (not just the adjacent pairs), which is necessarily expressed as an interval vector:

>>> indicator_to_interval(shiko, adjacent_not_all=False)
(0, 2, 0, 3, 0, 4, 0, 1)
Source code in amads/core/vector_transforms_checks.py
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
def indicator_to_interval(
    vector: Union[list[int], tuple[int, ...]],
    wrap: bool = True,
    adjacent_not_all: bool = True,
    sequence_not_vector: bool = True,
) -> tuple:
    """
    Given a vector (assumed to be indicator)
    convert from 1/0 at each index to the intervals between the 1s.

    Parameters
    ----------
    vector: Union[list[int], tuple[int, ...]]
        an indicator vector representing positions.
    wrap: bool
        wrap the cycle, duplicating the first element at the end to include
        that (possible) interval.
    adjacent_not_all: bool
        If True, form the set of intervals between pairs of adjacent positions.
        If False, form the set of intervals between all pairs.
    sequence_not_vector: bool
        In the case of adjacent intervals, True means express the result as an
        interval sequence, and False means compute an interval vector containing
        the counts of intervals of size 1, 2, 3, etc. If `adjacent_not_all` is
        False (meaning all pairs), the result is always an interval vector.

    Examples
    --------
    Example 1:
    >>> bembé = (1, 0, 1, 0, 1, 1, 0, 1, 0, 1, 0, 1)

    Adjacent intervals expressed as a sequence:
    >>> indicator_to_interval(bembé)
    (2, 2, 1, 2, 2, 2, 1)

    Wrap is optional:
    >>> indicator_to_interval(bembé, wrap=False)
    (2, 2, 1, 2, 2, 2)

    Adjacent intervals expressed as an interval vector:
    >>> indicator_to_interval(bembé, sequence_not_vector=False)
    (2, 5, 0, 0, 0, 0)

    All distances (not just the adjacent pairs), which is necessarily
    expressed as an interval vector:
    >>> indicator_to_interval(bembé, adjacent_not_all=False)
    (2, 5, 4, 3, 6, 1)

    Example 2:
    >>> shiko = (1, 0, 0, 0, 1, 0, 1, 0, 0, 0, 1, 0, 1, 0, 0, 0)

    Adjacent intervals expressed as a sequence:
    >>> indicator_to_interval(shiko)
    (4, 2, 4, 2, 4)

    Adjacent intervals expressed as an interval vector:
    >>> indicator_to_interval(shiko, sequence_not_vector=False)
    (0, 2, 0, 3, 0, 0, 0, 0)

    All distances (not just the adjacent pairs), which is necessarily
    expressed as an interval vector:
    >>> indicator_to_interval(shiko, adjacent_not_all=False)
    (0, 2, 0, 3, 0, 4, 0, 1)

    """
    if adjacent_not_all and wrap:
        vector = vector + vector[:1]

    indices = indicator_to_indices(vector)

    if adjacent_not_all:
        sequence = tuple(
            [indices[i + 1] - indices[i] for i in range(len(indices) - 1)]
        )
        if sequence_not_vector:
            return sequence
        else:
            ics = sequence
    else:  # all
        ics = [p[1] - p[0] for p in combinations(indices, 2)]

    vector_length = len(vector)
    half_vector_length = int(vector_length / 2)
    interval_vector = [0] * half_vector_length

    for ic in ics:
        if ic < 0:
            ic *= -1
        if ic > half_vector_length:
            ic = vector_length - ic
        interval_vector[ic - 1] += 1
    return tuple(interval_vector)

interval_sequence_to_indices

interval_sequence_to_indices(
    interval_sequence_vector: Union[list[int], tuple[int, ...]],
    wrap: bool = False,
) -> tuple[int, ...]

Given an interval sequence vector, convert to indices.

Parameters:

  • interval_sequence_vector (Union[list[int], tuple[int, ...]]) –

    a vector of distances (intervals) between adjacent positions that are used.

  • wrap (bool, default: False ) –

    If True, include the index after the end of the sequence

Returns:

  • tuple[int, ...]

    A vector containing the positions (indices) that are used.

Examples:

>>> interval_sequence_to_indices((3, 3, 2), wrap=False)  # Default
(0, 3, 6)
>>> interval_sequence_to_indices((3, 3, 2), wrap=True)
(0, 3, 6, 8)
Source code in amads/core/vector_transforms_checks.py
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
def interval_sequence_to_indices(
    interval_sequence_vector: Union[list[int], tuple[int, ...]],
    wrap: bool = False,
) -> tuple[int, ...]:
    """
    Given an interval sequence vector, convert to indices.

    Parameters
    ----------
    interval_sequence_vector: Union[list[int], tuple[int, ...]]
        a vector of distances (intervals) between adjacent positions
        that are used.
    wrap: bool
        If True, include the index after the end of the sequence

    Returns
    -------
    tuple[int, ...]
        A vector containing the positions (indices) that are used.

    Examples
    --------
    >>> interval_sequence_to_indices((3, 3, 2), wrap=False)  # Default
    (0, 3, 6)

    >>> interval_sequence_to_indices((3, 3, 2), wrap=True)
    (0, 3, 6, 8)

    """
    indices = [0]
    count = 0
    if not wrap:
        interval_sequence_vector = interval_sequence_vector[:-1]
    for i in interval_sequence_vector:
        count += i
        indices.append(count)
    return tuple(indices)

interval_sequence_to_indicator

interval_sequence_to_indicator(
    interval_sequence_vector: Union[list[int], tuple[int, ...]],
) -> tuple[int, ...]

Given an interval sequence vector, convert to indicator.

Parameters:

  • interval_sequence_vector (Union[list[int], tuple[int, ...]]) –

    the sequence of distances (intervals) between adjacent positions that are used.

Returns:

  • tuple[int, ...]

    An indicator vector representing all positions, where used positions contain the value 1.

Examples:

>>> interval_sequence_to_indicator((3, 3, 2))
(1, 0, 0, 1, 0, 0, 1, 0)
Source code in amads/core/vector_transforms_checks.py
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
def interval_sequence_to_indicator(
    interval_sequence_vector: Union[list[int], tuple[int, ...]]
) -> tuple[int, ...]:
    """
    Given an interval sequence vector, convert to indicator.

    Parameters
    ----------
    interval_sequence_vector: Union[list[int], tuple[int, ...]]
        the sequence of distances (intervals) between adjacent positions
        that are used.

    Returns
    -------
    tuple[int, ...]
        An indicator vector representing all positions, where used
        positions contain the value 1.

    Examples
    --------
    >>> interval_sequence_to_indicator((3, 3, 2))
    (1, 0, 0, 1, 0, 0, 1, 0)

    """
    indices = interval_sequence_to_indices(interval_sequence_vector, wrap=False)
    indicator = indices_to_indicator(
        indices, indicator_length=sum(interval_sequence_vector)
    )
    return tuple(indicator)

saturated_subsequence_repetition

saturated_subsequence_repetition(
    sequence: Union[list[int], tuple[int, ...]],
    all_rotations: bool = True,
    subsequence_period: Optional[int] = None,
) -> list[Union[list[int], tuple[int, ...]]]

Check if a sequence contains a repeated subsequence such that the subsequence saturates the whole (no sequence items "left over"). This is broadly equivalent to a "periodic sequence", with the additional constraint of saturation.

Parameters:

  • sequence (Union[list[int], tuple[int, ...]]) –

    A vector for event positions in the cycle time span.

  • all_rotations (bool, default: True ) –

    If True, check all rotations of the sequence.

  • subsequence_period (Optional[int], default: None ) –

    If specified, check only that period length; otherwise, check all factors of n.

Returns:

  • Union[List[List[int]]]

    A list of all subsequences that, if repeated, can form the input sequence. If all_rotations, then also include subsequences that can repeat to form any rotations of sequence. If sequence is not periodic, return None.

Examples:

>>> test_sequence = [1, 2, 1, 2, 1, 2, 1, 2]

All rotations, all subsequence lengths:

>>> saturated_subsequence_repetition(test_sequence, all_rotations=True)
[[1, 2], [2, 1], [1, 2, 1, 2], [2, 1, 2, 1]]

No rotations, all subsequence lengths:

>>> saturated_subsequence_repetition(test_sequence, all_rotations=False)
[[1, 2], [1, 2, 1, 2]]

All rotations, subsequence length fixed at 2:

>>> saturated_subsequence_repetition(test_sequence, subsequence_period=2, all_rotations=True)
[[1, 2], [2, 1]]

All rotations, subsequence length fixed at 4:

>>> saturated_subsequence_repetition(test_sequence, subsequence_period=4, all_rotations=True)
[[1, 2, 1, 2], [2, 1, 2, 1]]

No rotations, subsequence length fixed at 2:

>>> saturated_subsequence_repetition(test_sequence, subsequence_period=2, all_rotations=False)
[[1, 2]]

No rotations, subsequence length fixed at 4:

>>> saturated_subsequence_repetition(test_sequence, subsequence_period=4, all_rotations=False)
[[1, 2, 1, 2]]

Tuple input return same values, as list of tuples.

>>> test_tuple = (1, 2, 1, 2, 1, 2, 1, 2)

All rotations, all subsequence lengths:

>>> saturated_subsequence_repetition(test_tuple, all_rotations=True)
[(1, 2), (2, 1), (1, 2, 1, 2), (2, 1, 2, 1)]
Source code in amads/core/vector_transforms_checks.py
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
def saturated_subsequence_repetition(
    sequence: Union[list[int], tuple[int, ...]],
    all_rotations: bool = True,
    subsequence_period: Optional[int] = None,
) -> list[Union[list[int], tuple[int, ...]]]:
    """
    Check if a sequence contains a repeated subsequence such that
    the subsequence saturates the whole (no sequence items "left over").
    This is broadly equivalent to a "periodic sequence", with the additional
    constraint of saturation.

    Parameters
    ----------
    sequence: Union[list[int], tuple[int, ...]]
        A vector for event positions in the cycle time span.
    all_rotations: bool
        If True, check all rotations of the sequence.
    subsequence_period: Optional[int]
        If specified, check only that period length; otherwise,
        check all factors of n.

    Returns
    -------
    Union[List[List[int]]]
        A list of all subsequences that, if repeated, can form the input
        `sequence`. If `all_rotations`, then also include subsequences
        that can repeat to form any rotations of `sequence`. If
        `sequence` is not periodic, return None.

    Examples
    --------
    >>> test_sequence = [1, 2, 1, 2, 1, 2, 1, 2]

    All rotations, all subsequence lengths:

    >>> saturated_subsequence_repetition(test_sequence, all_rotations=True)
    [[1, 2], [2, 1], [1, 2, 1, 2], [2, 1, 2, 1]]

    No rotations, all subsequence lengths:

    >>> saturated_subsequence_repetition(test_sequence, all_rotations=False)
    [[1, 2], [1, 2, 1, 2]]

    All rotations, subsequence length fixed at 2:

    >>> saturated_subsequence_repetition(test_sequence, subsequence_period=2, all_rotations=True)
    [[1, 2], [2, 1]]

    All rotations, subsequence length fixed at 4:

    >>> saturated_subsequence_repetition(test_sequence, subsequence_period=4, all_rotations=True)
    [[1, 2, 1, 2], [2, 1, 2, 1]]

    No rotations, subsequence length fixed at 2:
    >>> saturated_subsequence_repetition(test_sequence, subsequence_period=2, all_rotations=False)
    [[1, 2]]

    No rotations, subsequence length fixed at 4:
    >>> saturated_subsequence_repetition(test_sequence, subsequence_period=4, all_rotations=False)
    [[1, 2, 1, 2]]

    Tuple input return same values, as list of tuples.

    >>> test_tuple = (1, 2, 1, 2, 1, 2, 1, 2)

    All rotations, all subsequence lengths:

    >>> saturated_subsequence_repetition(test_tuple, all_rotations=True)
    [(1, 2), (2, 1), (1, 2, 1, 2), (2, 1, 2, 1)]

    """
    if subsequence_period is not None:
        subsequence_periods = [subsequence_period]
    else:
        subsequence_periods = [
            length
            for length in range(1, len(sequence) // 2 + 1)
            if len(sequence) % length == 0
        ]

    subsequences = []

    for period in subsequence_periods:
        subsequence = sequence[:period]
        if subsequence not in subsequences:
            if all(
                sequence[i : i + period] == subsequence
                for i in range(0, len(sequence), period)
            ):
                subsequences.append(subsequence)

        if all_rotations:
            for j in range(
                1, period
            ):  # sic, from 1 (0 is done) and only up to the length of the subsequence
                this_sequence = rotate(sequence, j)
                subsequence = this_sequence[:period]
                if subsequence not in subsequences:
                    if all(
                        this_sequence[k : k + period] == subsequence
                        for k in range(0, len(this_sequence), period)
                    ):
                        subsequences.append(subsequence)

    return subsequences

multiset_to_vector

multiset_to_vector(
    multiset: Sequence, max_index: Union[int, None] = None
) -> tuple[int, ...]

Converts any "set" or "multiset" (any iterable object containing only integers) into a "vector" (count of those integers organized by index). This is similar to the collections.Counter function, simply returning an ordered tuple instead of a dict.

Parameters:

  • multiset (Iterable) –

    The input integers as an iterable object (list, tuple, set, or numpy array). Multisets are accepted but not required (i.e., sets as trivial cases of multisets).

  • max_index (Union[int, None], default: None ) –

    Sets the maximum index of the output vector. If None, use the maximum value of the input set.

Returns:

  • tuple[int, ...]

    The corresponding vector of counts for elements 0, 1, 2, etc.

Examples:

>>> test_multiset = (1, 1, 1, 2, 2, 3)
>>> vector = multiset_to_vector(test_multiset)
>>> vector
(0, 3, 2, 1)
>>> vector_with_padding = multiset_to_vector(test_multiset, max_index=6)
>>> vector_with_padding
(0, 3, 2, 1, 0, 0, 0)
>>> roundtrip = vector_to_multiset(vector)
>>> roundtrip
(1, 1, 1, 2, 2, 3)
>>> roundtrip == test_multiset
True
>>> test_set = (1, 2, 3)
>>> vector_2 = multiset_to_vector(test_set)
>>> vector_2
(0, 1, 1, 1)
>>> set_roundtrip = vector_to_multiset(vector_2)
>>> set_roundtrip
(1, 2, 3)
>>> set_roundtrip == test_set
True

Empty input returns empty tuple

>>> multiset_to_vector(())
()
Source code in amads/core/vectors_sets.py
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
def multiset_to_vector(
    multiset: Sequence,
    max_index: Union[int, None] = None,
) -> tuple[int, ...]:
    """
    Converts any "set" or "multiset" (any iterable object containing only integers)
    into a "vector" (count of those integers organized by index).
    This is similar to the collections.Counter function, simply returning
    an ordered tuple instead of a dict.

    Parameters
    ----------
    multiset : Iterable
        The input integers as an iterable object (list, tuple, set, or numpy array).
        Multisets are accepted but not required (i.e., sets as trivial cases of multisets).
    max_index: Union[int, None]
        Sets the maximum index of the output vector.
        If None, use the maximum value of the input set.

    Returns
    -------
    tuple[int, ...]
        The corresponding vector of counts for elements 0, 1, 2, etc.

    Examples
    --------
    >>> test_multiset = (1, 1, 1, 2, 2, 3)
    >>> vector = multiset_to_vector(test_multiset)
    >>> vector
    (0, 3, 2, 1)

    >>> vector_with_padding = multiset_to_vector(test_multiset, max_index=6)
    >>> vector_with_padding
    (0, 3, 2, 1, 0, 0, 0)

    >>> roundtrip = vector_to_multiset(vector)
    >>> roundtrip
    (1, 1, 1, 2, 2, 3)

    >>> roundtrip == test_multiset
    True

    >>> test_set = (1, 2, 3)
    >>> vector_2 = multiset_to_vector(test_set)
    >>> vector_2
    (0, 1, 1, 1)

    >>> set_roundtrip = vector_to_multiset(vector_2)
    >>> set_roundtrip
    (1, 2, 3)

    >>> set_roundtrip == test_set
    True

    Empty input returns empty tuple
    >>> multiset_to_vector(())
    ()

    """
    multiset = _to_tuple(multiset)
    if not multiset:
        return ()
    if max_index is None:
        max_index = max(multiset)
    counts = [0] * (max_index + 1)
    for item in multiset:
        counts[item] += 1

    return tuple(counts)

vector_to_multiset

vector_to_multiset(vector: tuple[int, ...]) -> tuple[int, ...]

Converts any "vector" (count of integers organised by index) to a corresponding "multiset" (unordered integers).

Parameters:

  • vector (tuple[int, ...]) –

    The input vector where each index i contains the number of occurrences of i.

Returns:

  • tuple[int, ...]

    The corresponding set as a tuple (because it will often be a multiset).

Examples:

>>> test_vector = (0, 3, 2, 1, 0, 0, 0)
>>> resulting_set = vector_to_multiset(test_vector)
>>> resulting_set
(1, 1, 1, 2, 2, 3)
>>> roundtrip = multiset_to_vector(resulting_set, max_index=6)
>>> roundtrip
(0, 3, 2, 1, 0, 0, 0)
>>> roundtrip == test_vector
True
Source code in amads/core/vectors_sets.py
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
def vector_to_multiset(vector: tuple[int, ...]) -> tuple[int, ...]:
    """
    Converts any "vector" (count of integers organised by index)
    to a corresponding "multiset" (unordered integers).

    Parameters
    ----------
    vector:  tuple[int, ...]
        The input vector where each index i contains the number
        of occurrences of i.

    Returns
    -------
    tuple[int, ...]
        The corresponding set as a tuple (because it will often be a multiset).

    Examples
    --------
    >>> test_vector = (0, 3, 2, 1, 0, 0, 0)
    >>> resulting_set = vector_to_multiset(test_vector)
    >>> resulting_set
    (1, 1, 1, 2, 2, 3)

    >>> roundtrip = multiset_to_vector(resulting_set, max_index=6)
    >>> roundtrip
    (0, 3, 2, 1, 0, 0, 0)

    >>> roundtrip == test_vector
    True

    """
    return tuple(i for i, count in enumerate(vector) for _ in range(count))

vector_to_set

vector_to_set(vector: tuple[int, ...]) -> set

Converts any "vector" of counts to a corresponding "set" of the distinct non-0 indices. cf vector_to_multiset

Parameters:

  • vector (tuple[int, ...]) –

    The input vector. Each index i contains the count of elements equal to i. The vector can therefore be an indicator vector with 0's and 1's.

Returns:

  • set

    The corresponding set of integers (equivalently, the indices at which the input vector is non-zero.

Examples:

>>> test_vector = (0, 3, 2, 1, 0, 0, 0)
>>> resulting_set = vector_to_set(test_vector)
>>> resulting_set
{1, 2, 3}
Source code in amads/core/vectors_sets.py
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
def vector_to_set(vector: tuple[int, ...]) -> set:
    """
    Converts any "vector" of counts to a corresponding "set" of the distinct non-0 indices.
    cf `vector_to_multiset`

    Parameters
    ----------
    vector: tuple[int, ...]
        The input vector. Each index i contains the count of elements
        equal to i. The vector can therefore be an indicator vector
        with 0's and 1's.

    Returns
    -------
    set
        The corresponding set of integers (equivalently, the indices
        at which the input `vector` is non-zero.

    Examples
    --------
    >>> test_vector = (0, 3, 2, 1, 0, 0, 0)
    >>> resulting_set = vector_to_set(test_vector)
    >>> resulting_set
    {1, 2, 3}
    """
    return {i for i, count in enumerate(vector) if count != 0}

weighted_to_indicator

weighted_to_indicator(
    weighted_vector: Sequence[float], threshold: float = 0.0
) -> tuple[int, ...]

Converts a weighted vector to an indicator vector.

Parameters:

  • weighted_vector (Sequence[float]) –

    Represents the weighted vector. Accepts lists, tuples, or numpy arrays.

  • threshold (float, default: 0.0 ) –

    Values below this threshold will be set to 0. This handles cases where weights might be very small but not exactly zero.

Returns:

  • tuple[int, ...]

    Representing the indicator vector (0s and 1s).

Examples:

>>> weighted_vector1 = (0.0, 0.0, 2.0, 0.0)
>>> weighted_to_indicator(weighted_vector1)
(0, 0, 1, 0)
>>> weighted_vector2 = (0.2, 0.0, 1.5, 0.0, 0.01)
>>> weighted_to_indicator(weighted_vector2)
(1, 0, 1, 0, 1)
>>> weighted_to_indicator(weighted_vector2, threshold=0.1)
(1, 0, 1, 0, 0)
Source code in amads/core/vectors_sets.py
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
def weighted_to_indicator(
    weighted_vector: Sequence[float], threshold: float = 0.0
) -> tuple[int, ...]:
    """
    Converts a weighted vector to an indicator vector.

    Parameters
    ----------
    weighted_vector: Sequence[float]
        Represents the weighted vector. Accepts lists, tuples, or numpy arrays.
    threshold: float
        Values below this threshold will be set to 0.
        This handles cases where weights might be very small but not exactly zero.

    Returns
    -------
    tuple[int, ...]
        Representing the indicator vector (0s and 1s).

    Examples
    --------
    >>> weighted_vector1 = (0.0, 0.0, 2.0, 0.0)
    >>> weighted_to_indicator(weighted_vector1)
    (0, 0, 1, 0)

    >>> weighted_vector2 = (0.2, 0.0, 1.5, 0.0, 0.01)
    >>> weighted_to_indicator(weighted_vector2)
    (1, 0, 1, 0, 1)

    >>> weighted_to_indicator(weighted_vector2, threshold=0.1)
    (1, 0, 1, 0, 0)
    """
    weighted_vector = _to_tuple(weighted_vector)
    return tuple(int(x > threshold) for x in weighted_vector)

apply_constant

apply_constant(
    set_or_vector: _T,
    constant: Union[int, float],
    modulo: Union[int, None] = None,
) -> _T

Apply a constant value to a set or vector.

Parameters:

  • set_or_vector (_T) –

    An iterable representing a set or vector.

  • constant (Union[int, float]) –

    The constant may be an int or a float and positive or negative. Note: passing a float constant will produce float elements in the output.

  • modulo (Union[int, None], default: None ) –

    An optional integer. If provided, the result will be taken modulo this value.

Returns:

  • tuple, list, or set:

    The set or vector with the constant applied. This return type matches the input type.

Examples:

>>> start = (0, 1, 2, 3, 11)
>>> more = apply_constant(start, 1)
>>> more
(1, 2, 3, 4, 12)
>>> less = apply_constant(more, -1)
>>> less
(0, 1, 2, 3, 11)
>>> start == less
True
>>> more = apply_constant(start, 1, modulo=12)
>>> more
(1, 2, 3, 4, 0)
>>> as_list = [0, 1, 2, 3, 11]
>>> more_list = apply_constant(as_list, 1)
>>> more_list
[1, 2, 3, 4, 12]
>>> as_set = {0, 1, 2, 3, 11}
>>> more_set = apply_constant(as_set, 1)
>>> more_set == {1, 2, 3, 4, 12}
True
Source code in amads/core/vectors_sets.py
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
def apply_constant(
    set_or_vector: _T,
    constant: Union[int, float],
    modulo: Union[int, None] = None,
) -> _T:
    """
    Apply a constant value to a set or vector.

    Parameters
    ----------
    set_or_vector:
        An iterable representing a set or vector.
    constant:
        The constant may be an int or a float and positive or negative.
        Note: passing a float constant will produce float elements in the output.
    modulo:
        An optional integer.
        If provided, the result will be taken modulo this value.

    Returns
    -------
    tuple, list, or set:
        The set or vector with the constant applied.
        This return type matches the input type.

    Examples
    --------
    >>> start = (0, 1, 2, 3, 11)
    >>> more = apply_constant(start, 1)
    >>> more
    (1, 2, 3, 4, 12)

    >>> less = apply_constant(more, -1)
    >>> less
    (0, 1, 2, 3, 11)

    >>> start == less
    True

    >>> more = apply_constant(start, 1, modulo=12)
    >>> more
    (1, 2, 3, 4, 0)

    >>> as_list = [0, 1, 2, 3, 11]
    >>> more_list = apply_constant(as_list, 1)
    >>> more_list
    [1, 2, 3, 4, 12]

    >>> as_set = {0, 1, 2, 3, 11}
    >>> more_set = apply_constant(as_set, 1)
    >>> more_set == {1, 2, 3, 4, 12}
    True

    """
    result = [x + constant for x in set_or_vector]
    if modulo is not None:
        result = [x % modulo for x in result]

    if isinstance(set_or_vector, tuple):
        return tuple(result)
    elif isinstance(set_or_vector, list):
        return result
    elif isinstance(set_or_vector, set):
        return set(result)
    else:
        return result

scalar_multiply

scalar_multiply(vector: tuple, scale_factor: int = 2) -> tuple

Multiply all values of a tuple.

Parameters:

  • vector (tuple) –

    a vector

  • scale_factor (int, default: 2 ) –

    The scale factor aka "multiplicative operand". Multiply all elements of vector by this amount. Defaults to 2.

Examples:

>>> scalar_multiply((0, 1, 2))
(0, 2, 4)
Source code in amads/core/vectors_sets.py
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
def scalar_multiply(vector: tuple, scale_factor: int = 2) -> tuple:
    """
    Multiply all values of a tuple.

    Parameters
    ----------
    vector: tuple
        a vector

    scale_factor: int
        The scale factor aka "multiplicative operand".
        Multiply all elements of `vector` by this amount.
        Defaults to 2.

    Examples
    --------
    >>> scalar_multiply((0, 1, 2))
    (0, 2, 4)

    """
    return tuple(x * scale_factor for x in vector)

is_set

is_set(possible_set: Iterable) -> bool

Check whether an iterable object is a set (specified in the type) or a de facto set (not in type, but with no repeated elements).

Examples:

>>> clear_set = {1, 2, 3}
>>> is_set(clear_set)
True
>>> de_facto_set = (1, 2, 3)
>>> is_set(de_facto_set)
True
>>> de_facto_multiset = (1, 1, 2, 3)
>>> is_set(de_facto_multiset)
False
Source code in amads/core/vectors_sets.py
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
def is_set(possible_set: Iterable) -> bool:
    """
    Check whether an iterable object is
    a set (specified in the type) or
    a de facto set (not in type, but with no repeated elements).

    Examples
    --------
    >>> clear_set = {1, 2, 3}
    >>> is_set(clear_set)
    True

    >>> de_facto_set = (1, 2, 3)
    >>> is_set(de_facto_set)
    True

    >>> de_facto_multiset = (1, 1, 2, 3)
    >>> is_set(de_facto_multiset)
    False
    """
    if isinstance(possible_set, set):
        return True
    elif len(set(possible_set)) == len(possible_set):
        return True
    return False

is_indicator_vector

is_indicator_vector(vector: tuple) -> bool

Check whether an input vector (tuple) is an indicator vector, containing only 0s and 1s.

Examples:

>>> is_indicator_vector((0, 1))
True

Can be all 0s

>>> is_indicator_vector((0, 0))
True

Can be all 1s

>>> is_indicator_vector((1, 1))
True

Cannot have any non-0 or 1 entry

>>> is_indicator_vector((1, 2))
False

Note: cannot be empty

>>> is_indicator_vector(())
False
Source code in amads/core/vectors_sets.py
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
def is_indicator_vector(vector: tuple) -> bool:
    """
    Check whether an input vector (tuple) is an indicator vector,
    containing only 0s and 1s.

    Examples
    --------
    >>> is_indicator_vector((0, 1))
    True

    Can be all 0s
    >>> is_indicator_vector((0, 0))
    True

    Can be all 1s
    >>> is_indicator_vector((1, 1))
    True

    Cannot have any non-0 or 1 entry
    >>> is_indicator_vector((1, 2))
    False

    Note: cannot be empty
    >>> is_indicator_vector(())
    False
    """
    if len(vector) == 0:
        return False
    if all(x in (0, 1) for x in vector):
        return True
    return False