Как построить стратегию для создания массива кортежей с парами одинаковых значений? - PullRequest
1 голос
/ 20 января 2020

Я бы хотел сгенерировать стратегию для NumPy тестирования с выводом наподобие:

array([[-2, -2],
       [-3, -3],
       [5,  5],
       [-1, -1]], dtype=int16)

То, что я пробовал, было:

import numpy as np
from hypothesis.strategies import integers
from hypothesis.extra.numpy import arrays
arrays(np.int16, (4,2), elements=integers(-10, 10)).example()

К сожалению, я не могу сделать значения внутри кортежей идентичными, поэтому приведенный выше запрос возвращает:

array([[ 5,  5],
       [-7,  5],
       [ 5,  5],
       [ 5,  5]], dtype=int16)

Ответы [ 3 ]

1 голос
/ 21 января 2020

Я обнаружил, что если мне нужно контролировать содержимое в структуре существующей стратегии (например, пары идентичных значений в массиве), мне нужно пропустить эту стратегию для более низких уровней с помощью которого я могу создать «готовое» значение, которое может заполнить тип, который мне нужен для генерации.

Давайте использовать, что numpy .array принимает список списков для создания массива. Предположим также, что вы хотите, чтобы каждая строка была уникальной, поскольку в вашем примере не показаны повторяющиеся строки. Если это нежелательно, удалите unique_by=str из определения depth_strategy

  1. Создайте целое число и создайте список с этим значением, повторяющимся несколько раз, чтобы соответствовать ШИРИНЕ.
  2. Создайте список длины ГЛУБИНЫ из тех списков, которые мы создали на первом шаге.
  3. Объедините две стратегии, вложив их.
  4. Введите результат третьего шага в numpy .array, убедившись, что dtype соответствует стратегии, использованной для генерации значений на первом шаге.
# %%
"""Hypothesis strategy for array of tuples with pairs of identical values."""
from hypothesis import given, settings, strategies as st

import numpy as np

WIDTH = 2
DEPTH = 4
MIN_VALUE = -10
MAX_VALUE = 10

# Build the row - Here for clarification only
width_strategy = st.integers(MIN_VALUE, MAX_VALUE).map(
    lambda i: tuple(i for _ in range(WIDTH))
)

# Build the array of rows - Here for clarification only
depth_strategy = st.lists(
    width_strategy, min_size=DEPTH, max_size=DEPTH, unique_by=str
).map(lambda lot: np.array(lot, dtype=np.int64))

# All-in-One
complete_strategy = st.lists(
    st.integers(MIN_VALUE, MAX_VALUE).map(
        lambda i: tuple(i for _ in range(WIDTH))
    ),
    min_size=DEPTH,
    max_size=DEPTH,
    unique_by=str,
).map(lambda lot: np.array(lot, dtype=np.int64))


@settings(max_examples=10)
@given(an_array=complete_strategy)
def create_numpy_array(an_array):
    """Turn list of lists into numpy array."""
    print(f"A numpy array could be:\n{an_array}")


create_numpy_array()

Это генерирует что-то вроде:

A numpy array could be:
[[ 3  3]
 [ 9  9]
 [-5 -5]
 [ 0  0]]
A numpy array could be:
[[ 3  3]
 [-2 -2]
 [ 4  4]
 [-5 -5]]
A numpy array could be:
[[ 7  7]
 [ 0  0]
 [-2 -2]
 [-1 -1]]

Обратите внимание, что I установите max_examples равным 10, поскольку гипотеза дает более высокий коэффициент вхождения к значениям, которые она считает «проблемными», например, ноль, NaN, бесконечность и тому подобное. Таким образом, example () или меньшее количество примеров, вероятно, сгенерирует множество массивов 2x4 со всеми нулями. К счастью, нам помогает ограничение unique_by.

0 голосов
/ 20 января 2020

Не уверен, что это то, что вам нужно, но arrays из hypothesis.extra.numpy, похоже, не имеет опций для дублирования значений.

Вы можете просто создать нужный массив следующим образом:

import numpy as np
from hypothesis.strategies import integers
strat = integers(10, -10)
np.array([[x, x] for x in [strat.example() for _ in range(4)]], np.int16)

Пример результата:

array([[-9, -9],
       [ 0,  0],
       [-2, -2],
       [ 0,  0]], dtype=int16)

Если вам не нравится, что размер 2 запечен, вы можете иметь оба параметра, подобные этому:

def get_array(rows, cols, strat):
    np.array([[x]*cols for x in [strat.example() for _ in range(rows)]], np.int16)


get_array(4, 2, integers(-10, 10))
0 голосов
/ 20 января 2020

Не обращая слишком много внимания на то, что может предложить np, вы можете просто сгенерировать кортежи с помощью генератора:

tuple_list = [tuple(a) for a in arrays(np.int16, (4,2), elements=integers(-10,10)).example()]
...