Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Gymnasium integration #78

Open
wants to merge 3 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
131 changes: 131 additions & 0 deletions src/SharpNeat.Tasks.Windows/Gymnasium/GymnasiumControl.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,131 @@
using SharpNeat.Evaluation;
using SharpNeat.EvolutionAlgorithm;
using SharpNeat.Neat.Genome;
using SharpNeat.Tasks.Gymnasium;
using SharpNeat.Windows;

namespace SharpNeat.Tasks.Windows.Gymnasium;

public class GymnasiumControl : GenomeControl
{
readonly IGenomeDecoder<NeatGenome<double>,IBlackBox<double>> _genomeDecoder;

// The agent used by the simulation thread.
volatile IBlackBox<double> _agent;
readonly bool _initializing = true;

// Thread for running simulation.
readonly Thread _simThread;

// Indicates whether a simulation is running. Access is thread synchronised using Interlocked.
volatile bool _terminateSimThread;

// Event that signals simulation thread to start a simulation.
readonly AutoResetEvent _simStartEvent = new(false);
readonly ManualResetEvent _simNotRunningEvent = new(false);

/// <summary>
/// Constructs a new instance of <see cref="GymnasiumControl"/>.
/// </summary>
/// <param name="genomeDecoder">Genome decoder.</param>
public GymnasiumControl(
IGenomeDecoder<NeatGenome<double>, IBlackBox<double>> genomeDecoder)
{
_genomeDecoder = genomeDecoder ?? throw new ArgumentNullException(nameof(genomeDecoder));

try
{
InitializeComponent();

// Create background thread for running simulation alongside NEAT algorithm.
_simThread = new(new ThreadStart(SimulationThread))
{
IsBackground = true
};
_simThread.Start();
}
finally
{
_initializing = false;
}
}

public override void OnGenomeUpdated()
{
base.OnGenomeUpdated();

// Get a local reference to avoid possible race conditions on the class field.
IGenome genome = _genome;

if(genome is null || _terminateSimThread || _initializing)
return;

// Dispose any existing agent.
var existingAgent = _agent;
Thread.MemoryBarrier();
existingAgent?.Dispose();

// Decode the genome, and store the resulting IBlackBox agent in an instance field.
NeatGenome<double> neatGenome = genome as NeatGenome<double>;
_agent = _genomeDecoder.Decode(neatGenome);

// Signal simulation thread to start running a one simulation.
_simStartEvent.Set();
}

#region Private Methods [Windows.Forms Designer Code]

private void InitializeComponent()
{
}

#endregion

/// <summary>
/// Simulate prey capture until thread is terminated.
/// </summary>
private void SimulationThread()
{
// Wait to be signaled to start the next trial run.
_simStartEvent.WaitOne();

IBlackBox<double> agent = _agent;

// Clear any prior agent state.
agent.Reset();

while (true)
{
// Check if we have been signaled to terminate before starting a simulation run.
if(_terminateSimThread)
break;

var episode = new GymnasiumEpisode(24, 4, true, true);
episode.Evaluate(agent);

// Test if the thread should be terminated.
if (_terminateSimThread)
break;
}

// Signal any thread waiting for this simulation thread to terminate.
_simNotRunningEvent.Set();
}

protected override void Dispose(bool disposing)
{
if( disposing )
{
// Signal the simulation thread to terminate, and wait for it to terminate.
_terminateSimThread = true;
_simStartEvent.Set();
_simNotRunningEvent.WaitOne();

base.Dispose(disposing);

_agent.Dispose();
_simStartEvent.Dispose();
_simNotRunningEvent.Dispose();
}
}
}
60 changes: 60 additions & 0 deletions src/SharpNeat.Tasks.Windows/Gymnasium/GymnasiumControl.resx
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
<root>
<xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
<xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
<xsd:element name="root" msdata:IsDataSet="true">
<xsd:complexType>
<xsd:choice maxOccurs="unbounded">
<xsd:element name="metadata">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" />
</xsd:sequence>
<xsd:attribute name="name" use="required" type="xsd:string" />
<xsd:attribute name="type" type="xsd:string" />
<xsd:attribute name="mimetype" type="xsd:string" />
<xsd:attribute ref="xml:space" />
</xsd:complexType>
</xsd:element>
<xsd:element name="assembly">
<xsd:complexType>
<xsd:attribute name="alias" type="xsd:string" />
<xsd:attribute name="name" type="xsd:string" />
</xsd:complexType>
</xsd:element>
<xsd:element name="data">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
<xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />
<xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
<xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
<xsd:attribute ref="xml:space" />
</xsd:complexType>
</xsd:element>
<xsd:element name="resheader">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required" />
</xsd:complexType>
</xsd:element>
</xsd:choice>
</xsd:complexType>
</xsd:element>
</xsd:schema>
<resheader name="resmimetype">
<value>text/microsoft-resx</value>
</resheader>
<resheader name="version">
<value>2.0</value>
</resheader>
<resheader name="reader">
<value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
<resheader name="writer">
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
</root>
27 changes: 27 additions & 0 deletions src/SharpNeat.Tasks.Windows/Gymnasium/GymnasiumExperimentUi.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
using SharpNeat.Experiments;
using SharpNeat.Neat.Genome.Double;
using SharpNeat.Windows;
using SharpNeat.Windows.Neat;

namespace SharpNeat.Tasks.Windows.Gymnasium;

public sealed class GymnasiumExperimentUi : NeatExperimentUi
{
readonly INeatExperiment<double> _neatExperiment;

public GymnasiumExperimentUi(
INeatExperiment<double> neatExperiment)
{
_neatExperiment = neatExperiment ?? throw new ArgumentNullException(nameof(neatExperiment));
}

/// <inheritdoc/>
public override GenomeControl CreateTaskControl()
{
var genomeDecoder = NeatGenomeDecoderFactory.CreateGenomeDecoder(
_neatExperiment.IsAcyclic,
_neatExperiment.EnableHardwareAcceleratedNeuralNets);

return new GymnasiumControl(genomeDecoder);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
using SharpNeat.Experiments;
using SharpNeat.Experiments.ConfigModels;
using SharpNeat.IO;
using SharpNeat.Windows.Experiments;

namespace SharpNeat.Tasks.Windows.Gymnasium;

public sealed class GymnasiumExperimentUiFactory : IExperimentUiFactory
{
/// <inheritdoc/>
public IExperimentUi CreateExperimentUi(
INeatExperiment<double> neatExperiment,
Stream jsonConfigStream)
{
// Load experiment JSON config.
ExperimentConfig experimentConfig = JsonUtils.Deserialize<ExperimentConfig>(jsonConfigStream);

return new GymnasiumExperimentUi(neatExperiment);
}
}
6 changes: 6 additions & 0 deletions src/SharpNeat.Tasks.Windows/SharpNeat.Tasks.Windows.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -19,4 +19,10 @@
<ProjectReference Include="..\SharpNeat.Windows\SharpNeat.Windows.csproj" />
</ItemGroup>

<ItemGroup>
<Compile Update="Gymnasium\GymnasiumControl.cs">
<SubType>UserControl</SubType>
</Compile>
</ItemGroup>

</Project>
Loading