position_accuracy

Bar-position accuracy, decomposed by how a track’s predicted bar grid is misaligned with the reference rather than just how often it is.

musicality.metrics.confusion.confusion_half_cycle_rate() says a prediction is half a bar out; it can’t say whether the model got the phase wrong for the whole track (a consistent offset — an acoustic/model failure) or kept flipping between phases within the track (an unstable decode — a postprocessing failure). Those two have opposite fixes, so this separates them.

The offset histogram is the mechanism; the headline numbers are position_acc and position_acc_best_offset, whose difference — anchor_error — is the cost of a bar grid that is right in every respect except where it starts counting.

Functions

position_accuracy(ref_times, ref_positions, ...)

Bar-position accuracy at every reference beat, plus its offset profile.

position_accuracy(ref_times, ref_positions, pred_events, tolerance=0.07, group_size=4)[source]

Bar-position accuracy at every reference beat, plus its offset profile.

For every reference beat, finds the nearest predicted event within tolerance seconds and records offset = (predicted_position - true_position) mod group_size. Unlike confusion_half_cycle_rate(), all positions are eligible, not just 1 and the half-cycle opposite — an off-by-one error is exactly what this is meant to be able to see, and unlike downbeat_f_measures() it scores positions 2..group_size-1 too.

The two headline numbers:

  • position_acc — the fraction labelled correctly, as annotated.

  • position_acc_best_offset — the fraction the model would get right if allowed to rotate its bar numbering by one constant per track. Near 1.0 means the track has one consistent phase (right or wrong) from start to finish; well below means the predicted phase changes partway through.

Their difference, anchor_error, is therefore the share of the error attributable to choosing the wrong global anchor rather than to an inconsistent grid. Measured on merge_v4, 0.090 of a total 0.419 position error; see plans/06_metric_calibration_and_eval_consolidation.md section 1.2.

Reading them with modal_offset:

  • high position_acc_best_offset, modal_offset != 0 → the model committed to a single wrong phase for the whole track. A better decoder can’t help; the per-beat evidence itself is wrong.

  • low position_acc_best_offset → the predicted phase flips mid-track. The evidence may well be fine on average and the decoder is losing it (see musicality.postprocess.label_bar_position()’s resync behaviour).

Parameters:
  • ref_times (ndarray) – Reference beat times, in seconds, shape (n_beats,).

  • ref_positions (ndarray) – Reference group position (1-group_size) per beat, same shape as ref_times.

  • pred_events (list[dict]) – Predicted beat list, as returned by musicality.postprocess.readout().

  • tolerance (float) – Matching window, in seconds.

  • group_size (int) – Beats per group — 4 for bar position (default), 8 for phrase position.

Returns:

None if no reference beat found a matching, resolved prediction (nothing to score), else a dict with:

  • n_matched (int) — reference beats that matched a resolved label.

  • histogram (list[int], length group_size) — count per offset.

  • modal_offset (int) — most common offset, in 0..group_size-1. 0 is correct; group_size // 2 is the half-cycle swap; anything else is an off-by-one-style error.

  • position_acc (float) — fraction of matched beats at offset 0.

  • position_acc_best_offset (float) — fraction at modal_offset.

  • anchor_error (float) — position_acc_best_offset - position_acc, always >= 0 since offset 0 is one of the candidates the maximum is taken over. Zero exactly when the dominant offset is the correct one.

Return type:

dict | None