Skip to content

Commit

Permalink
dijkstra
Browse files Browse the repository at this point in the history
  • Loading branch information
pavel-zhur committed Jul 22, 2024
1 parent 39f22c9 commit 5c704bb
Show file tree
Hide file tree
Showing 12 changed files with 145 additions and 56 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@

namespace HarmonyDB.Index.Analysis.Tests;

public class LoopExtractionTests(ILogger<LoopExtractionTests> logger, ChordDataParser chordDataParser, ProgressionsBuilder progressionsBuilder, IndexExtractor indexExtractor, ProgressionsVisualizer progressionsVisualizer)
public class LoopExtractionTests(ILogger<LoopExtractionTests> logger, ChordDataParser chordDataParser, ProgressionsBuilder progressionsBuilder, IndexExtractor indexExtractor, ProgressionsVisualizer progressionsVisualizer, Dijkstra dijkstra)
{
[InlineData("Am D G E Am D G E Am D G E Am D G E", 5)] // normalized start of Am-D-G-E is D.
[InlineData("Gm C F D Gm C F D Gm C F D Gm C F D", 3)]
Expand Down Expand Up @@ -742,7 +742,7 @@ private void TraceAndTest(
TraceAndTest(indexExtractor.FindBlocks(sequence, roots, BlocksExtractionLogic.ReplaceWithSelfJumps), anyMassiveOverlaps);
TraceAndTest(indexExtractor.FindBlocks(sequence, roots, BlocksExtractionLogic.ReplaceWithSelfMultiJumps), anyMassiveOverlaps);

var graph = indexExtractor.FindGraph(indexExtractor.FindBlocks(sequence, roots, BlocksExtractionLogic.All));
var graph = indexExtractor.FindGraph(indexExtractor.FindBlocks(sequence, roots, BlocksExtractionLogic.All), sequence.Length);
TraceAndTest(graph);

// sequence integrity test
Expand Down Expand Up @@ -1048,6 +1048,8 @@ private void TraceAndTest(

private void TraceAndTest(BlockGraph graph)
{
Assert.NotNull(dijkstra.GetShortestPath(graph));

Assert.All(graph.Environments, e => Assert.Distinct(e.ChildrenSubtree));
Assert.All(graph.Environments, e => Assert.Distinct(e.Children));
Assert.All(graph.Environments, e => Assert.Distinct(e.Parents));
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
using HarmonyDB.Index.Analysis.Models.Index.Blocks.Interfaces;
using HarmonyDB.Index.Analysis.Models.Index.Enums;

namespace HarmonyDB.Index.Analysis.Models.Index.Blocks;

public class EdgeBlock(IndexedBlockType type) : IIndexedBlock
{
public string Normalized => throw new InvalidOperationException();

public byte NormalizationRoot => throw new InvalidOperationException();

public int StartIndex => throw new InvalidOperationException();

public int EndIndex => throw new InvalidOperationException();

public int BlockLength => 0;

public IEnumerable<IIndexedBlock> Children => [];

public string? GetNormalizedCoordinate(int index) => null; // song start blocks do not have normalization shifts

public IndexedBlockType Type { get; } = type;
}
Original file line number Diff line number Diff line change
Expand Up @@ -11,4 +11,6 @@ public interface IIndexedBlock : IBlock
IndexedBlockType Type { get; }

string Normalized { get; }

float Score => 1;
}
Original file line number Diff line number Diff line change
Expand Up @@ -6,4 +6,6 @@ public enum IndexedBlockType : byte
Sequence,
PolyLoop,
PingPong,
SequenceStart,
SequenceEnd,
}
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
using HarmonyDB.Index.Analysis.Models.Index.Blocks.Interfaces;
using HarmonyDB.Index.Analysis.Models.Index.Enums;
using HarmonyDB.Index.Analysis.Models.Index.Graphs.Interfaces;

namespace HarmonyDB.Index.Analysis.Models.Index.Graphs;
Expand Down Expand Up @@ -31,4 +32,8 @@ public static string GetNormalization(IIndexedBlock block1, IIndexedBlock block2
$"{block2.GetNormalizedCoordinate(block1.EndIndex + 1)}";

public string Normalization => GetNormalization(Block1.Block, Block2.Block);

public bool IsEdge
=> Block1.Block.Type is IndexedBlockType.SequenceStart or IndexedBlockType.SequenceEnd
|| Block2.Block.Type is IndexedBlockType.SequenceStart or IndexedBlockType.SequenceEnd;
}
Original file line number Diff line number Diff line change
Expand Up @@ -6,4 +6,5 @@ public interface IBlockJoint
IBlockEnvironment Block2 { get; }
int OverlapLength { get; }
string Normalization { get; }
bool IsEdge { get; }
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,5 +4,4 @@ public class BlocksChartParameters
{
public bool TypesToo { get; init; } = true;
public bool GroupNormalized { get; init; } = true;
public bool AddPaths { get; init; } = false;
}
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,6 @@ public static IServiceCollection AddIndexAnalysis(this IServiceCollection servic
.AddSingleton<IndexExtractor>()
.AddSingleton<InputParser>()
.AddSingleton<ProgressionsSearch>()
.AddSingleton<ProgressionsOptimizer>()
.AddSingleton<Dijkstra>()
.AddIndexAnalysisEm();
}
66 changes: 66 additions & 0 deletions HarmonyDB.Index/HarmonyDB.Index.Analysis/Services/Dijkstra.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
using HarmonyDB.Index.Analysis.Models.Index.Blocks.Interfaces;
using HarmonyDB.Index.Analysis.Models.Index.Enums;
using HarmonyDB.Index.Analysis.Models.Index.Graphs;

namespace HarmonyDB.Index.Analysis.Services;

public class Dijkstra
{
public List<IIndexedBlock>? GetShortestPath(BlockGraph graph)
{
var startBlock = graph.Environments.Single(x => x.Block.Type == IndexedBlockType.SequenceStart).Block; // Assuming the start is the first block in environments
var targetBlock = graph.Environments.Single(x => x.Block.Type == IndexedBlockType.SequenceEnd).Block; // Assuming the target is the last block in environments

var distances = new Dictionary<IIndexedBlock, float>();
var previousBlocks = new Dictionary<IIndexedBlock, IIndexedBlock?>();
var allBlocks = graph.Environments.Select(env => env.Block).Distinct().ToList();

foreach (var block in allBlocks)
{
distances[block] = float.MaxValue; // Initialize all distances to infinity
previousBlocks[block] = null; // Initialize all previous blocks to null
}

distances[startBlock] = 0; // Distance to start block is 0

var priorityQueue = new PriorityQueue<IIndexedBlock, float>();
priorityQueue.Enqueue(startBlock, 0);

while (priorityQueue.Count != 0)
{
var currentBlock = priorityQueue.Dequeue();

if (currentBlock == targetBlock)
break;

var currentEnvironment = graph.EnvironmentsByBlock[currentBlock];
var neighbors = currentEnvironment.LeftJoints.Select(joint => joint.Block2.Block)
.Concat(currentEnvironment.RightJoints.Select(joint => joint.Block2.Block))
.Distinct();

foreach (var neighbor in neighbors)
{
var alt = distances[currentBlock] + neighbor.Score;
if (alt < distances[neighbor])
{
distances[neighbor] = alt;
previousBlocks[neighbor] = currentBlock;
priorityQueue.Enqueue(neighbor, alt);
}
}
}

return ReconstructPath(previousBlocks, startBlock, targetBlock);
}

private List<IIndexedBlock>? ReconstructPath(Dictionary<IIndexedBlock, IIndexedBlock?> previousBlocks, IIndexedBlock startBlock, IIndexedBlock targetBlock)
{
var path = new List<IIndexedBlock>();
for (var at = targetBlock; at != null; at = previousBlocks[at])
{
path.Add(at);
}
path.Reverse();
return path.Contains(startBlock) ? path : null; // Return the path if it includes the start block, otherwise return null
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -326,7 +326,7 @@ public List<IBlock> FindBlocks(ReadOnlyMemory<CompactHarmonyMovement> sequence,

blocks.AddRange(FindSequenceBlocks(sequence, blocks, roots));

var graph = FindGraph(blocks);
var graph = FindGraph(blocks, sequence.Length);

blocks.AddRange(FindPingPongs(graph));

Expand All @@ -343,7 +343,7 @@ public List<IBlock> FindBlocks(ReadOnlyMemory<CompactHarmonyMovement> sequence,
private static IEnumerable<IIndexedBlock> GetChildBlocksSubtree(IIndexedBlock block) =>
block.Children.SelectMany(b => GetChildBlocksSubtree(b).Prepend(b));

public BlockGraph FindGraph(IReadOnlyList<IBlock> blocks)
public BlockGraph FindGraph(IReadOnlyList<IBlock> blocks, int sequenceLength)
{
var environments = blocks.OfType<IIndexedBlock>().Select(b => new BlockEnvironment
{
Expand Down Expand Up @@ -385,6 +385,28 @@ public BlockGraph FindGraph(IReadOnlyList<IBlock> blocks)
})
.ToList();

var startBlock = new EdgeBlock(IndexedBlockType.SequenceStart);
var endBlock = new EdgeBlock(IndexedBlockType.SequenceEnd);
environments[startBlock] = new()
{
Block = startBlock,
};
environments[endBlock] = new()
{
Block = endBlock,
};

joints.AddRange(blocks.OfType<IIndexedBlock>().Where(x => x.StartIndex == 0).Select(x => new BlockJoint
{
Block1 = environments[startBlock],
Block2 = environments[x],
}));
joints.AddRange(blocks.OfType<IIndexedBlock>().Where(x => x.EndIndex == sequenceLength - 1).Select(x => new BlockJoint
{
Block1 = environments[x],
Block2 = environments[endBlock],
}));

foreach (var joint in joints)
{
joint.Block1.RightJoints.Add(joint);
Expand Down

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@

namespace HarmonyDB.Index.Analysis.Services;

public class ProgressionsVisualizer(ProgressionsOptimizer progressionsOptimizer, IndexExtractor indexExtractor)
public class ProgressionsVisualizer(Dijkstra dijkstra, IndexExtractor indexExtractor)
{
public const string AttributeSearch = "search";
public const string AttributeSearchFirst = "search-first";
Expand Down Expand Up @@ -56,7 +56,7 @@ public Text VisualizeBlocksAsOne(ReadOnlyMemory<CompactHarmonyMovement> sequence

public List<(Text left, Text right)> VisualizeBlocks(ReadOnlyMemory<CompactHarmonyMovement> sequence, IReadOnlyList<byte> roots, IReadOnlyList<IBlock> blocks, BlocksChartParameters parameters)
{
var graph = indexExtractor.FindGraph(blocks);
var graph = indexExtractor.FindGraph(blocks, sequence.Length);
var rootsTrace = CreateRootsTraceByIndices(sequence, roots, 0, sequence.Length - 1, out var positions, parameters.TypesToo);

var gridPositions = positions.Where((_, i) => i % 6 == 5).ToList();
Expand All @@ -65,6 +65,7 @@ public Text VisualizeBlocksAsOne(ReadOnlyMemory<CompactHarmonyMovement> sequence

var blockId = 0;
var lines = blocks
.Where(x => x is not EdgeBlock)
.GroupBy(x => x switch
{
LoopBlock loopBlock when parameters.GroupNormalized => loopBlock.Normalized,
Expand Down Expand Up @@ -135,6 +136,7 @@ public Text VisualizeBlocksAsOne(ReadOnlyMemory<CompactHarmonyMovement> sequence

lines.Add((Text.Empty, Text.Empty));
var jointTitles = graph.Joints
.Where(x => !x.IsEdge)
.GroupBy(x => x.Normalization)
.SelectMany((g, i) =>
g.Select(j => (title: (char)('a' + i), positionIndex: positions[j.Block2.Block.StartIndex])))
Expand All @@ -151,14 +153,22 @@ public Text VisualizeBlocksAsOne(ReadOnlyMemory<CompactHarmonyMovement> sequence
}
}

if (parameters.AddPaths)
var path = dijkstra.GetShortestPath(graph);
if (path != null)
{
lines.Add((Text.Empty, Text.Empty));
var paths = progressionsOptimizer.GetAllPossiblePaths(graph);
var path = paths.MinBy(x => x.Count)!;
lines.Add((
string.Join(" ", path.Select(x => blocksToIds[x])).AsText(),
$"1 / {paths.Count} ({paths.Min(x => x.Count)}..{paths.Max(x => x.Count)})".AsText()));
string.Join(" ", path.Select(x => x.Type switch
{
IndexedBlockType.SequenceStart => "start",
IndexedBlockType.SequenceEnd => "end",
_ => blocksToIds[x].ToString(),
})).AsText(),
Text.Empty));
}
else
{
throw new("The shortest path is mandatory.");
}

return lines;
Expand Down

0 comments on commit 5c704bb

Please sign in to comment.