- harmonic_space.py: remove abs() to store signed cent differences
- graph.py: melodic threshold now uses abs(cents) for magnitude
- Change melodic power from 2 to 3 for sharper penalization
This fixes contrary_motion factor which was broken (always 0 due to absolute values)
- Add callback and interval parameters to find_stochastic_path() for adaptive weights
- Add get_influence() method to compute weighted score contribution per factor
- Rename graph_node/output_chord to source_node/destination_node/source_chord/destination_chord
- Rename voice_stay_count to sustain_count_before/after
- Rename node_visit_counts to last_visited_count_before/after
- Remove redundant internal state from Path - derive from steps
- Each PathStep now fully self-contained with before/after state
- Move transposition, voice mapping into Path.step()
- Add node_visit_counts and voice_stay_count to each PathStep
- Fix voice tracking to use voice identity, not position
- Clean up unused last_graph_nodes code
- Full encapsulation: Path handles all voice-leading state
- Add src/path.py with Path and PathStep classes
- Path stores initial_chord, steps, weights_config
- PathStep stores graph_node, output_chord, transposition, movements, scores, candidates
- Refactor find_stochastic_path to use candidates approach
- Separate _build_candidates (raw scores) from _compute_weights
- Simplify return type to Path only (graph_chords available via property)
- Update io.py to use new Path API
- Each soft factor is sum-normalized across all edge candidates
- Ensures factors compete equally regardless of native value ranges
- Skip factors with no variance (all candidates same value)
- Base weight of 1.0 ensures minimum probability
- Half of moving voices should go one direction, half opposite
- Weighted by closeness to ideal half split
- factor = 1.0 - (distance_from_half / half)
- Works with odd (near-half) and even (exact half) voices
- Analysis shows contrary_motion_steps, percent, and avg_score
- Save graph_path to output for accurate Hamiltonian tracking
- DCA analysis now shows avg/max voice stay counts
- Fix: use actual graph node hashes instead of rehashing transposed chords
- Replace cumulative_trans with average cents of actual chord
- More accurate register targeting using current chord position
- Test with max_path=150 shows reaching ~400 Hz target (2 octaves)
- Replace harsh 1/(1+distance) with bounded relative scoring
- Factor > 1.0 when moving toward target, < 1.0 when moving away
- Minimum factor 0.1 to avoid zero weights
- Test with max_path=150 shows reaching ~90% of 2-octave target
- Add --target-range CLI option (in octaves)
- Implement target_range weight in PathFinder
- Add test for target range weight
- Add --max-path to README