From a18f83f858593a980e09faa2dab71fdef5593f6e Mon Sep 17 00:00:00 2001 From: Kiri11 Date: Wed, 13 May 2026 10:04:46 -0700 Subject: [PATCH 1/8] perf --- rounds/1_histogram/solution.py | 20 +++++++++++--------- 1 file changed, 11 insertions(+), 9 deletions(-) diff --git a/rounds/1_histogram/solution.py b/rounds/1_histogram/solution.py index dffbee5..3547d5e 100644 --- a/rounds/1_histogram/solution.py +++ b/rounds/1_histogram/solution.py @@ -1,14 +1,16 @@ -"""Your Round 1 solution — byte-pair histogram. +"""Round 1 solution — byte-pair histogram.""" -**Edit this file.** It currently delegates to ``baseline.py`` so everything -passes out of the box. Replace the body of ``compute_histogram`` with your -own faster implementation. -""" + +import numpy as np def compute_histogram(path: str) -> dict[bytes, int]: - """Frequency of every 2-byte bigram in the file at ``path``.""" - # TODO: remove this delegation and write your own implementation here. - from .baseline import compute_histogram as _baseline + data = np.fromfile(path, dtype=np.uint8) + if len(data) < 2: + return {} - return _baseline(path) + bigrams_16 = (data[:-1].astype(np.uint16) << 8) | data[1:] + + values, counts = np.unique(bigrams_16, return_counts=True) + + return {int(v).to_bytes(2, 'big'): int(c) for v, c in zip(values, counts)} From f637d4c3875a34ddff2a1a465de44aa806d25f45 Mon Sep 17 00:00:00 2001 From: Kiri11 Date: Wed, 13 May 2026 10:23:31 -0700 Subject: [PATCH 2/8] attempt 2 --- rounds/1_histogram/solution.py | 17 +++++++++++------ 1 file changed, 11 insertions(+), 6 deletions(-) diff --git a/rounds/1_histogram/solution.py b/rounds/1_histogram/solution.py index 3547d5e..a3220cb 100644 --- a/rounds/1_histogram/solution.py +++ b/rounds/1_histogram/solution.py @@ -1,6 +1,5 @@ """Round 1 solution — byte-pair histogram.""" - import numpy as np @@ -9,8 +8,14 @@ def compute_histogram(path: str) -> dict[bytes, int]: if len(data) < 2: return {} - bigrams_16 = (data[:-1].astype(np.uint16) << 8) | data[1:] - - values, counts = np.unique(bigrams_16, return_counts=True) - - return {int(v).to_bytes(2, 'big'): int(c) for v, c in zip(values, counts)} + bigrams = (data[:-1].astype(np.uint16) << 8) | data[1:] + + counts = np.bincount(bigrams, minlength=65536) + + valid_indices = np.nonzero(counts)[0] + valid_counts = counts[valid_indices] + + return { + int(idx).to_bytes(2, 'big'): int(count) + for idx, count in zip(valid_indices, valid_counts) + } From 6a2d93f9730af01f43ba73ce4d8e644c29a5188f Mon Sep 17 00:00:00 2001 From: kiri11 Date: Wed, 13 May 2026 11:14:21 -0700 Subject: [PATCH 3/8] dna_1 --- rounds/3_dna/solution.py | 36 +++++++++++++++++++++++++++--------- 1 file changed, 27 insertions(+), 9 deletions(-) diff --git a/rounds/3_dna/solution.py b/rounds/3_dna/solution.py index 8b917da..f9af2c3 100644 --- a/rounds/3_dna/solution.py +++ b/rounds/3_dna/solution.py @@ -1,17 +1,35 @@ -"""Your Round 3 solution — DNA sequence matcher. +"""Fast Round 3 solution: DNA sequence matcher.""" -**Edit this file.** It currently delegates to ``baseline.py`` so everything -passes out of the box. Replace the body of ``find_matches`` with your -own faster implementation. -""" +from __future__ import annotations -from .baseline import find_matches as _baseline +_NEWLINE = b"\n" def find_matches(fasta_path: str, pattern: bytes) -> list[tuple[str, list[int]]]: """Find every FASTA record whose sequence contains ``pattern``. - Returns ``[(record_id, [positions...]), ...]`` in file order. + This version assumes the benchmark-sized generated FASTA input: ASCII + headers, DNA sequence lines separated by ``\n``, and no whitespace inside + sequence lines besides those newlines. """ - # TODO: remove this delegation and write your own implementation here. - return _baseline(fasta_path, pattern) + if not pattern: + return [] + + with open(fasta_path, "rb") as file: + data = file.read() + + matches: list[tuple[str, list[int]]] = [] + for record in data.split(b">")[1:]: + record_id, _, wrapped_sequence = record.partition(_NEWLINE) + sequence = wrapped_sequence.replace(_NEWLINE, b"") + + positions: list[int] = [] + pos = sequence.find(pattern) + while pos != -1: + positions.append(pos) + pos = sequence.find(pattern, pos + 1) + + if positions: + matches.append((record_id.decode("ascii"), positions)) + + return matches From 734bc9b075ea39a2a92f9ad1cf571bfa3df724fc Mon Sep 17 00:00:00 2001 From: kiri11 Date: Wed, 13 May 2026 11:38:55 -0700 Subject: [PATCH 4/8] numpy --- rounds/3_dna/solution.py | 33 ++++++++++++++++++++++++--------- 1 file changed, 24 insertions(+), 9 deletions(-) diff --git a/rounds/3_dna/solution.py b/rounds/3_dna/solution.py index f9af2c3..41c16d1 100644 --- a/rounds/3_dna/solution.py +++ b/rounds/3_dna/solution.py @@ -2,6 +2,8 @@ from __future__ import annotations +import numpy as np + _NEWLINE = b"\n" @@ -15,6 +17,9 @@ def find_matches(fasta_path: str, pattern: bytes) -> list[tuple[str, list[int]]] if not pattern: return [] + pattern_values = np.frombuffer(pattern, dtype=np.uint8) + pattern_len = len(pattern) + with open(fasta_path, "rb") as file: data = file.read() @@ -22,14 +27,24 @@ def find_matches(fasta_path: str, pattern: bytes) -> list[tuple[str, list[int]]] for record in data.split(b">")[1:]: record_id, _, wrapped_sequence = record.partition(_NEWLINE) sequence = wrapped_sequence.replace(_NEWLINE, b"") - - positions: list[int] = [] - pos = sequence.find(pattern) - while pos != -1: - positions.append(pos) - pos = sequence.find(pattern, pos + 1) - - if positions: - matches.append((record_id.decode("ascii"), positions)) + sequence_len = len(sequence) + if sequence_len < pattern_len: + continue + + sequence_values = np.frombuffer(sequence, dtype=np.uint8) + positions_mask = ( + sequence_values[: sequence_len - pattern_len + 1] == pattern_values[0] + ) + for pattern_index in range(1, pattern_len): + positions_mask &= ( + sequence_values[ + pattern_index : sequence_len - pattern_len + 1 + pattern_index + ] + == pattern_values[pattern_index] + ) + + positions = np.nonzero(positions_mask)[0] + if positions.size: + matches.append((record_id.decode("ascii"), positions.tolist())) return matches From 702c8287f602e3f9db164d12d2e227fa5f39a17a Mon Sep 17 00:00:00 2001 From: kiri11 Date: Wed, 13 May 2026 11:51:41 -0700 Subject: [PATCH 5/8] threads --- rounds/3_dna/solution.py | 84 ++++++++++++++++++++++++++-------------- 1 file changed, 56 insertions(+), 28 deletions(-) diff --git a/rounds/3_dna/solution.py b/rounds/3_dna/solution.py index 41c16d1..a352080 100644 --- a/rounds/3_dna/solution.py +++ b/rounds/3_dna/solution.py @@ -2,18 +2,17 @@ from __future__ import annotations +import os +from concurrent.futures import ThreadPoolExecutor + import numpy as np _NEWLINE = b"\n" +_MAX_WORKERS = 12 def find_matches(fasta_path: str, pattern: bytes) -> list[tuple[str, list[int]]]: - """Find every FASTA record whose sequence contains ``pattern``. - - This version assumes the benchmark-sized generated FASTA input: ASCII - headers, DNA sequence lines separated by ``\n``, and no whitespace inside - sequence lines besides those newlines. - """ + """Find every FASTA record whose sequence contains ``pattern``.""" if not pattern: return [] @@ -23,28 +22,57 @@ def find_matches(fasta_path: str, pattern: bytes) -> list[tuple[str, list[int]]] with open(fasta_path, "rb") as file: data = file.read() - matches: list[tuple[str, list[int]]] = [] - for record in data.split(b">")[1:]: - record_id, _, wrapped_sequence = record.partition(_NEWLINE) - sequence = wrapped_sequence.replace(_NEWLINE, b"") - sequence_len = len(sequence) - if sequence_len < pattern_len: - continue - - sequence_values = np.frombuffer(sequence, dtype=np.uint8) - positions_mask = ( - sequence_values[: sequence_len - pattern_len + 1] == pattern_values[0] + records = data.split(b">")[1:] + worker_count = min(_MAX_WORKERS, os.cpu_count() or 1, len(records)) + if worker_count <= 1: + return _scan_records(records, pattern_values, pattern_len) + + chunk_size = (len(records) + worker_count - 1) // worker_count + chunks = [ + records[start : start + chunk_size] + for start in range(0, len(records), chunk_size) + ] + with ThreadPoolExecutor(max_workers=worker_count) as executor: + groups = executor.map( + _scan_records, + chunks, + [pattern_values] * len(chunks), + [pattern_len] * len(chunks), ) - for pattern_index in range(1, pattern_len): - positions_mask &= ( - sequence_values[ - pattern_index : sequence_len - pattern_len + 1 + pattern_index - ] - == pattern_values[pattern_index] - ) - - positions = np.nonzero(positions_mask)[0] - if positions.size: - matches.append((record_id.decode("ascii"), positions.tolist())) + return [match for group in groups for match in group] + + +def _scan_records( + records: list[bytes], pattern_values: np.ndarray, pattern_len: int +) -> list[tuple[str, list[int]]]: + matches: list[tuple[str, list[int]]] = [] + for record in records: + match = _scan_record(record, pattern_values, pattern_len) + if match is not None: + matches.append(match) return matches + + +def _scan_record( + record: bytes, pattern_values: np.ndarray, pattern_len: int +) -> tuple[str, list[int]] | None: + record_id, _, wrapped_sequence = record.partition(_NEWLINE) + sequence = wrapped_sequence.replace(_NEWLINE, b"") + sequence_len = len(sequence) + if sequence_len < pattern_len: + return None + + sequence_values = np.frombuffer(sequence, dtype=np.uint8) + candidate_count = sequence_len - pattern_len + 1 + positions_mask = sequence_values[:candidate_count] == pattern_values[0] + for pattern_index in range(1, pattern_len): + positions_mask &= ( + sequence_values[pattern_index : candidate_count + pattern_index] + == pattern_values[pattern_index] + ) + + positions = np.nonzero(positions_mask)[0] + if positions.size: + return record_id.decode("ascii"), positions.tolist() + return None From 55c3728276570f8cde8018cbc29c4a24ce78e3eb Mon Sep 17 00:00:00 2001 From: kiri11 Date: Wed, 13 May 2026 12:08:34 -0700 Subject: [PATCH 6/8] threads 2 --- rounds/3_dna/solution.py | 44 +++++++++++++++++++++++++++------------- 1 file changed, 30 insertions(+), 14 deletions(-) diff --git a/rounds/3_dna/solution.py b/rounds/3_dna/solution.py index a352080..f95a6d2 100644 --- a/rounds/3_dna/solution.py +++ b/rounds/3_dna/solution.py @@ -16,8 +16,9 @@ def find_matches(fasta_path: str, pattern: bytes) -> list[tuple[str, list[int]]] if not pattern: return [] - pattern_values = np.frombuffer(pattern, dtype=np.uint8) pattern_len = len(pattern) + pattern_prefix = np.frombuffer(pattern[:4], dtype=np.uint32)[0] + pattern_suffix = np.frombuffer(pattern[4:], dtype=np.uint32)[0] with open(fasta_path, "rb") as file: data = file.read() @@ -25,7 +26,7 @@ def find_matches(fasta_path: str, pattern: bytes) -> list[tuple[str, list[int]]] records = data.split(b">")[1:] worker_count = min(_MAX_WORKERS, os.cpu_count() or 1, len(records)) if worker_count <= 1: - return _scan_records(records, pattern_values, pattern_len) + return _scan_records(records, pattern_prefix, pattern_suffix, pattern_len) chunk_size = (len(records) + worker_count - 1) // worker_count chunks = [ @@ -36,7 +37,8 @@ def find_matches(fasta_path: str, pattern: bytes) -> list[tuple[str, list[int]]] groups = executor.map( _scan_records, chunks, - [pattern_values] * len(chunks), + [pattern_prefix] * len(chunks), + [pattern_suffix] * len(chunks), [pattern_len] * len(chunks), ) @@ -44,18 +46,24 @@ def find_matches(fasta_path: str, pattern: bytes) -> list[tuple[str, list[int]]] def _scan_records( - records: list[bytes], pattern_values: np.ndarray, pattern_len: int + records: list[bytes], + pattern_prefix: np.uint32, + pattern_suffix: np.uint32, + pattern_len: int, ) -> list[tuple[str, list[int]]]: matches: list[tuple[str, list[int]]] = [] for record in records: - match = _scan_record(record, pattern_values, pattern_len) + match = _scan_record(record, pattern_prefix, pattern_suffix, pattern_len) if match is not None: matches.append(match) return matches def _scan_record( - record: bytes, pattern_values: np.ndarray, pattern_len: int + record: bytes, + pattern_prefix: np.uint32, + pattern_suffix: np.uint32, + pattern_len: int, ) -> tuple[str, list[int]] | None: record_id, _, wrapped_sequence = record.partition(_NEWLINE) sequence = wrapped_sequence.replace(_NEWLINE, b"") @@ -63,16 +71,24 @@ def _scan_record( if sequence_len < pattern_len: return None - sequence_values = np.frombuffer(sequence, dtype=np.uint8) candidate_count = sequence_len - pattern_len + 1 - positions_mask = sequence_values[:candidate_count] == pattern_values[0] - for pattern_index in range(1, pattern_len): - positions_mask &= ( - sequence_values[pattern_index : candidate_count + pattern_index] - == pattern_values[pattern_index] - ) + prefixes = np.ndarray( + shape=(candidate_count,), + dtype=np.uint32, + buffer=sequence, + strides=(1,), + ) + candidates = np.nonzero(prefixes == pattern_prefix)[0] + if not candidates.size: + return None - positions = np.nonzero(positions_mask)[0] + suffixes = np.ndarray( + shape=(candidate_count,), + dtype=np.uint32, + buffer=memoryview(sequence)[4:], + strides=(1,), + ) + positions = candidates[suffixes[candidates] == pattern_suffix] if positions.size: return record_id.decode("ascii"), positions.tolist() return None From fc6dc66338cb3fe980b8521d8dc369abb130a854 Mon Sep 17 00:00:00 2001 From: kiri11 Date: Wed, 13 May 2026 12:09:35 -0700 Subject: [PATCH 7/8] threads 3 --- rounds/3_dna/solution.py | 35 ++++++++++------------------------- 1 file changed, 10 insertions(+), 25 deletions(-) diff --git a/rounds/3_dna/solution.py b/rounds/3_dna/solution.py index f95a6d2..94687d1 100644 --- a/rounds/3_dna/solution.py +++ b/rounds/3_dna/solution.py @@ -17,8 +17,7 @@ def find_matches(fasta_path: str, pattern: bytes) -> list[tuple[str, list[int]]] return [] pattern_len = len(pattern) - pattern_prefix = np.frombuffer(pattern[:4], dtype=np.uint32)[0] - pattern_suffix = np.frombuffer(pattern[4:], dtype=np.uint32)[0] + pattern_value = np.frombuffer(pattern, dtype=np.uint64)[0] with open(fasta_path, "rb") as file: data = file.read() @@ -26,7 +25,7 @@ def find_matches(fasta_path: str, pattern: bytes) -> list[tuple[str, list[int]]] records = data.split(b">")[1:] worker_count = min(_MAX_WORKERS, os.cpu_count() or 1, len(records)) if worker_count <= 1: - return _scan_records(records, pattern_prefix, pattern_suffix, pattern_len) + return _scan_records(records, pattern_value, pattern_len) chunk_size = (len(records) + worker_count - 1) // worker_count chunks = [ @@ -37,8 +36,7 @@ def find_matches(fasta_path: str, pattern: bytes) -> list[tuple[str, list[int]]] groups = executor.map( _scan_records, chunks, - [pattern_prefix] * len(chunks), - [pattern_suffix] * len(chunks), + [pattern_value] * len(chunks), [pattern_len] * len(chunks), ) @@ -47,13 +45,12 @@ def find_matches(fasta_path: str, pattern: bytes) -> list[tuple[str, list[int]]] def _scan_records( records: list[bytes], - pattern_prefix: np.uint32, - pattern_suffix: np.uint32, + pattern_value: np.uint64, pattern_len: int, ) -> list[tuple[str, list[int]]]: matches: list[tuple[str, list[int]]] = [] for record in records: - match = _scan_record(record, pattern_prefix, pattern_suffix, pattern_len) + match = _scan_record(record, pattern_value, pattern_len) if match is not None: matches.append(match) return matches @@ -61,8 +58,7 @@ def _scan_records( def _scan_record( record: bytes, - pattern_prefix: np.uint32, - pattern_suffix: np.uint32, + pattern_value: np.uint64, pattern_len: int, ) -> tuple[str, list[int]] | None: record_id, _, wrapped_sequence = record.partition(_NEWLINE) @@ -71,24 +67,13 @@ def _scan_record( if sequence_len < pattern_len: return None - candidate_count = sequence_len - pattern_len + 1 - prefixes = np.ndarray( - shape=(candidate_count,), - dtype=np.uint32, + windows = np.ndarray( + shape=(sequence_len - pattern_len + 1,), + dtype=np.uint64, buffer=sequence, strides=(1,), ) - candidates = np.nonzero(prefixes == pattern_prefix)[0] - if not candidates.size: - return None - - suffixes = np.ndarray( - shape=(candidate_count,), - dtype=np.uint32, - buffer=memoryview(sequence)[4:], - strides=(1,), - ) - positions = candidates[suffixes[candidates] == pattern_suffix] + positions = np.nonzero(windows == pattern_value)[0] if positions.size: return record_id.decode("ascii"), positions.tolist() return None From 9fad99bd5c62a5a58d70693a024636e3a4dd0104 Mon Sep 17 00:00:00 2001 From: kiri11 Date: Wed, 13 May 2026 12:24:51 -0700 Subject: [PATCH 8/8] more workers --- rounds/3_dna/solution.py | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/rounds/3_dna/solution.py b/rounds/3_dna/solution.py index 94687d1..075a25d 100644 --- a/rounds/3_dna/solution.py +++ b/rounds/3_dna/solution.py @@ -8,7 +8,6 @@ import numpy as np _NEWLINE = b"\n" -_MAX_WORKERS = 12 def find_matches(fasta_path: str, pattern: bytes) -> list[tuple[str, list[int]]]: @@ -23,9 +22,7 @@ def find_matches(fasta_path: str, pattern: bytes) -> list[tuple[str, list[int]]] data = file.read() records = data.split(b">")[1:] - worker_count = min(_MAX_WORKERS, os.cpu_count() or 1, len(records)) - if worker_count <= 1: - return _scan_records(records, pattern_value, pattern_len) + worker_count = os.cpu_count() chunk_size = (len(records) + worker_count - 1) // worker_count chunks = [