Determinism
Repeatability requires the same package version, backend/artifact bytes and digest, OS/architecture/runtime, Chain, input bytes, sample rate, channel layout, offline or stream mode, block schedule, execution seed, assets, and complete stream operation history. Cross-version byte identity is not promised. Backend comparisons use frozen golden tolerances rather than cross-backend byte identity.
The execution seed defaults to 0 and is an unsigned 32-bit integer. It is distinct from
an effect parameter such as BitCrusher.seed, whose catalog default is 11 and range is
0–1000.
Reproducible experiment
v0.1 has no standard experiment-manifest API. This source owns the complete experiment
record: candidate wheel filename and twice-computed SHA-256, package and environment
versions, input construction and SHA-256, stream mode, parameters, seeds, each ordered
open/process/close operation, exact process block spans, and every output hash. It uses
SimpleJitter.rmsJitterNanoseconds = 100, stereo 48 kHz, 4096 frames, 256-frame
blocks, execution seed 0 for identical runs and 1 for the variant. Save the emitted JSON
and pass it back with --replay-record; the script reconstructs a new stream from that
record and requires identical input and output digests in the same environment.
import argparse
import hashlib
import importlib.metadata
import json
import platform
from pathlib import Path
import numpy as np
import effetune as et
EXPERIMENT = {
"schemaVersion": 1,
"effect": "SimpleJitter",
"parameters": {"rmsJitterNanoseconds": 100},
"sampleRate": 48_000,
"channels": 2,
"frames": 4096,
"blockSize": 256,
"processingMode": "stream",
"executionSeeds": {"same": 0, "different": 1},
"input": {"kind": "sine", "peak": 0.5, "periodFrames": 97},
}
def digest_bytes(value):
return hashlib.sha256(value).hexdigest()
def build_input(experiment):
frames = experiment["frames"]
source = experiment["input"]
phase = np.arange(frames, dtype=np.float32)
mono = (
source["peak"] * np.sin(2 * np.pi * phase / source["periodFrames"])
).astype(np.float32)
return np.ascontiguousarray(np.stack((mono, mono)))
def operation_history(experiment, seed):
operations = [{
"index": 0,
"operation": "open",
"mode": experiment["processingMode"],
"sampleRate": experiment["sampleRate"],
"channels": experiment["channels"],
"blockSize": experiment["blockSize"],
"seed": seed,
}]
for start in range(0, experiment["frames"], experiment["blockSize"]):
operations.append({
"index": len(operations),
"operation": "process",
"startFrame": start,
"endFrame": min(start + experiment["blockSize"], experiment["frames"]),
})
operations.append({
"index": len(operations),
"operation": "close",
})
return operations
def render_from_history(audio, experiment, operations):
assert [entry["index"] for entry in operations] == list(range(len(operations)))
opening = operations[0]
closing = operations[-1]
assert opening["operation"] == "open"
assert opening["mode"] == "stream"
assert closing["operation"] == "close"
assert opening["channels"] == audio.shape[0]
assert opening["sampleRate"] == experiment["sampleRate"]
assert opening["blockSize"] == experiment["blockSize"]
cursor = 0
spans = []
for operation in operations[1:-1]:
assert operation["operation"] == "process"
start = operation["startFrame"]
end = operation["endFrame"]
assert start == cursor and start < end <= audio.shape[1]
spans.append((start, end))
cursor = end
assert cursor == audio.shape[1]
chain = et.Chain([
et.SimpleJitter(
rms_jitter_nanoseconds=experiment["parameters"][
"rmsJitterNanoseconds"
]
)
])
with chain.stream(
opening["sampleRate"],
channels=opening["channels"],
block_size=opening["blockSize"],
seed=opening["seed"],
) as stream:
blocks = [
stream.process(np.ascontiguousarray(audio[:, start:end]))
for start, end in spans
]
return np.concatenate(blocks, axis=1)
def create_record(wheel_path, wheel_sha256):
audio = build_input(EXPERIMENT)
runs = []
outputs = {}
for label, seed in (
("first", EXPERIMENT["executionSeeds"]["same"]),
("repeat", EXPERIMENT["executionSeeds"]["same"]),
("variant", EXPERIMENT["executionSeeds"]["different"]),
):
operations = operation_history(EXPERIMENT, seed)
output = render_from_history(audio, EXPERIMENT, operations)
output_sha256 = digest_bytes(output.tobytes())
outputs[label] = output
runs.append({
"label": label,
"mode": EXPERIMENT["processingMode"],
"seed": seed,
"operations": operations,
"outputSha256": output_sha256,
})
assert outputs["first"].tobytes() == outputs["repeat"].tobytes()
assert outputs["first"].tobytes() != outputs["variant"].tobytes()
return {
"artifact": {
"filename": wheel_path.name,
"sha256": wheel_sha256,
},
"environment": {
"effetune": importlib.metadata.version("effetune"),
"numpy": np.__version__,
"platform": platform.platform(),
"python": platform.python_version(),
},
"experiment": EXPERIMENT,
"input": {
"dtype": str(audio.dtype),
"shape": list(audio.shape),
"sha256": digest_bytes(audio.tobytes()),
},
"runs": runs,
"outputs": {
run["label"]: run["outputSha256"] for run in runs
},
}
def replay_record(record, wheel_path, wheel_sha256):
assert record["artifact"] == {
"filename": wheel_path.name,
"sha256": wheel_sha256,
}
experiment = record["experiment"]
audio = build_input(experiment)
input_sha256 = digest_bytes(audio.tobytes())
assert record["input"] == {
"dtype": str(audio.dtype),
"shape": list(audio.shape),
"sha256": input_sha256,
}
outputs = {}
for run in record["runs"]:
assert run["mode"] == experiment["processingMode"] == "stream"
assert run["seed"] == run["operations"][0]["seed"]
output = render_from_history(audio, experiment, run["operations"])
output_sha256 = digest_bytes(output.tobytes())
assert output_sha256 == run["outputSha256"]
outputs[run["label"]] = output_sha256
assert outputs == record["outputs"]
return {
"inputSha256": input_sha256,
"outputs": outputs,
}
parser = argparse.ArgumentParser()
parser.add_argument("--wheel", type=Path, required=True)
parser.add_argument("--replay-record", type=Path)
args = parser.parse_args()
wheel_bytes = args.wheel.read_bytes()
first_hash = digest_bytes(wheel_bytes)
second_hash = digest_bytes(args.wheel.read_bytes())
assert first_hash == second_hash
if args.replay_record:
saved_record = json.loads(args.replay_record.read_text(encoding="utf-8"))
result = replay_record(saved_record, args.wheel, first_hash)
else:
result = create_record(args.wheel, first_hash)
print(json.dumps(result, sort_keys=True))
python research-experiment.py --wheel /path/to/effetune-0.1.0.whl