implement shuffle benchmark

This commit is contained in:
2024-12-12 11:11:17 -06:00
parent 53a8fbc3f3
commit 6cdeb60ac4
5 changed files with 147 additions and 63 deletions

1
.gitignore vendored
View File

@@ -1,2 +1,3 @@
.idea .idea
.DS_Store .DS_Store
output

File diff suppressed because one or more lines are too long

View File

@@ -4,6 +4,7 @@ import random
from dataclasses import dataclass from dataclasses import dataclass
from statistics import mean, variance from statistics import mean, variance
from enum import Enum from enum import Enum
from pathlib import Path
@dataclass @dataclass
class Benchmark: class Benchmark:
@@ -15,18 +16,18 @@ class ShuffleTypes(Enum):
FULL_SHUFFLE = "FULL_SHUFFLE" FULL_SHUFFLE = "FULL_SHUFFLE"
SPLIT_SHUFFLE = "SPLIT_SHUFFLE" SPLIT_SHUFFLE = "SPLIT_SHUFFLE"
def observations(number_of_keys, properties_per_key, passcode_len, shuffle_type: ShuffleTypes = ShuffleTypes.SPLIT_SHUFFLE): def observations(number_of_keys, properties_per_key, passcode_len, complexity: int, disparity: int, shuffle_type: ShuffleTypes):
k = number_of_keys k = number_of_keys
p = properties_per_key p = properties_per_key
n = passcode_len n = passcode_len
nkode = [random.randint(0, k*p-1) for _ in range(n)] passcode = passcode_generator(k, p, n, complexity, disparity)
keypad = Keypad.new_keypad(k, p) keypad = Keypad.new_keypad(k, p)
def obs_gen(): def obs_gen():
for _ in range(100): # finite number of yields for _ in range(100): # finite number of yields
yield Observation( yield Observation(
keypad=keypad.keypad.copy(), keypad=keypad.keypad.copy(),
key_selection=keypad.key_entry(target_passcode=nkode) key_selection=keypad.key_entry(target_passcode=passcode)
) )
match shuffle_type: match shuffle_type:
case ShuffleTypes.FULL_SHUFFLE: case ShuffleTypes.FULL_SHUFFLE:
@@ -38,25 +39,94 @@ def observations(number_of_keys, properties_per_key, passcode_len, shuffle_type:
return obs_gen() return obs_gen()
def split_shuffle_benchmark( def passcode_generator(k: int, p: int, n: int, c: int, d: int) -> list[int]:
assert n >= c
assert p*k >= c
assert n >= d
assert p >= d
passcode_prop = []
passcode_set = []
valid_choices = {i for i in range(k*p)}
repeat_set = n-d
repeat_prop = n-c
prop_added = set()
set_added = set()
for _ in range(n):
prop = random.choice(list(valid_choices))
prop_set = prop//p
passcode_prop.append(prop)
passcode_set.append(prop_set)
if prop in prop_added:
repeat_prop -= 1
if prop_set in set_added:
repeat_set -= 1
prop_added.add(prop)
set_added.add(prop_set)
if repeat_prop <= 0:
valid_choices -= prop_added
if repeat_set <= 0:
for el in valid_choices.copy():
if el // p in set_added:
valid_choices.remove(el)
return passcode_prop
def shuffle_benchmark(
number_of_keys: int, number_of_keys: int,
properties_per_key: int, properties_per_key: int,
passcode_len: int, passcode_len: int,
max_tries_before_lockout: int, max_tries_before_lockout: int,
run_count: int, run_count: int,
complexity: int,
disparity: int,
shuffle_type: ShuffleTypes,
file_path: str = '../output',
overwrite: bool = False
) -> Benchmark: ) -> Benchmark:
file_name = f"{shuffle_type.name.lower()}-{number_of_keys}-{properties_per_key}-{passcode_len}-{max_tries_before_lockout}-{complexity}-{disparity}-{run_count}.txt"
full_path = Path(file_path) / file_name
if not overwrite and full_path.exists():
print(f"file exists {file_path}")
with open(full_path, "r") as fp:
runs = fp.readline()
runs = runs.split(',')
runs = [int(i) for i in runs]
return Benchmark(
mean=mean(runs),
variance=variance(runs),
runs=runs
)
runs = [] runs = []
for _ in range(run_count): for _ in range(run_count):
evilkode = Evilkode( evilkode = Evilkode(
observations=observations(number_of_keys, properties_per_key, passcode_len), observations=observations(
number_of_keys=number_of_keys, number_of_keys=number_of_keys,
properties_per_key=properties_per_key, properties_per_key=properties_per_key,
passcode_len=passcode_len, passcode_len=passcode_len,
max_tries_before_lockout=max_tries_before_lockout complexity=complexity,
disparity=disparity,
shuffle_type=shuffle_type,
),
number_of_keys=number_of_keys,
properties_per_key=properties_per_key,
passcode_len=passcode_len,
max_tries_before_lockout=max_tries_before_lockout,
) )
evilout = evilkode.run() evilout = evilkode.run()
runs.append(evilout.iterations) runs.append(evilout.iterations)
full_path.parent.mkdir(parents=True, exist_ok=True)
with open(full_path, "w") as fp:
fp.write(",".join([str(i) for i in runs])),
return Benchmark( return Benchmark(
mean=mean(runs), mean=mean(runs),
variance=variance(runs), variance=variance(runs),
@@ -70,15 +140,24 @@ def full_shuffle_benchmark(
passcode_len: int, passcode_len: int,
max_tries_before_lockout: int, max_tries_before_lockout: int,
run_count: int, run_count: int,
complexity: int,
disparity: int,
) -> Benchmark: ) -> Benchmark:
runs = [] runs = []
for _ in range(run_count): for _ in range(run_count):
evilkode = Evilkode( evilkode = Evilkode(
observations=observations(number_of_keys, properties_per_key, passcode_len, shuffle_type=ShuffleTypes.FULL_SHUFFLE), observations=observations(
number_of_keys=number_of_keys, number_of_keys=number_of_keys,
properties_per_key=properties_per_key, properties_per_key=properties_per_key,
passcode_len=passcode_len, passcode_len=passcode_len,
max_tries_before_lockout=max_tries_before_lockout complexity=complexity,
disparity=disparity,
shuffle_type=ShuffleTypes.FULL_SHUFFLE,
),
number_of_keys=number_of_keys,
properties_per_key=properties_per_key,
passcode_len=passcode_len,
max_tries_before_lockout=max_tries_before_lockout,
) )
evilout = evilkode.run() evilout = evilkode.run()
runs.append(evilout.iterations) runs.append(evilout.iterations)

View File

@@ -1,6 +1,5 @@
import math import math
from dataclasses import dataclass from dataclasses import dataclass
from itertools import chain
from typing import Iterator from typing import Iterator

15
tests/test_benchmark.py Normal file
View File

@@ -0,0 +1,15 @@
from src.benchmark import passcode_generator
import pytest
@pytest.mark.parametrize(
"k, p, n, c, d, runs",
[
(6, 9, 4, 4, 4, 100)
]
)
def test_passcode_generator(k, p, n, c, d, runs):
for _ in range(runs):
passcode = passcode_generator(k=k, p=p, n=n, c=c, d=d)
passcode_sets = [el//p for el in passcode]
assert c <= len(set(passcode))
assert d <= len(set(passcode_sets))