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

feat: add Belief and BeliefSet #21

Merged
merged 24 commits into from
Mar 26, 2024
Merged
Show file tree
Hide file tree
Changes from 20 commits
Commits
Show all changes
24 commits
Select commit Hold shift + click to select a range
92f9063
feat: add Belief, IBelief and Beliefset
JensSteenmetz Mar 18, 2024
1c015f9
docs: add documentation
JensSteenmetz Mar 18, 2024
4e74455
test: add tests for Belief
JensSteenmetz Mar 18, 2024
87b1a94
test: add tests for Beliefset
JensSteenmetz Mar 18, 2024
e7d52fc
fix: fix Reflection bug in Beliefset
JensSteenmetz Mar 18, 2024
78f41a1
feat: merge main into feat/100-beliefset-necessities
JensSteenmetz Mar 18, 2024
85313f7
fix: make Beliefset constructor protected
JensSteenmetz Mar 18, 2024
335d767
docs: change summaries to remarks
JensSteenmetz Mar 18, 2024
a5c2b7b
fix: adhere to naming conventions for unit tests
JensSteenmetz Mar 18, 2024
768cffd
chore: change name Beliefset to BeliefSet
JensSteenmetz Mar 20, 2024
87c11cd
chore: change name Beliefset to BeliefSet
JensSteenmetz Mar 20, 2024
e701cef
chore: change name Beliefset to BeliefSet in tests
JensSteenmetz Mar 21, 2024
255ce7a
fix: change name TResource to TObservation and add more tests
JensSteenmetz Mar 21, 2024
e7a80e2
fix: improve documentation and namings
JensSteenmetz Mar 21, 2024
bc9b91a
fix: improve IBelief and Belief summaries
JensSteenmetz Mar 21, 2024
76245f8
fix: more improvements to docs and tests
JensSteenmetz Mar 21, 2024
6dbd151
chore: correct test comments
JensSteenmetz Mar 24, 2024
82aafb8
chore: rename Beliefset.cs to BeliefSet.cs
JensSteenmetz Mar 24, 2024
611d874
chore: rename BeliefsetTests.cs to BeliefSetTests.cs
JensSteenmetz Mar 24, 2024
2f4830d
chore: merge main into feat/100-beliefset-necessities
JensSteenmetz Mar 25, 2024
ab98d40
chore: remove Believe folder
JensSteenmetz Mar 25, 2024
24b63da
fix: use actual BeliefSet in Goals
JensSteenmetz Mar 25, 2024
adfe5c7
feat: add IBeliefSet
JensSteenmetz Mar 25, 2024
22e4986
chore: rename believe to belief in comments
JensSteenmetz Mar 25, 2024
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
81 changes: 81 additions & 0 deletions Aplib.Core/Belief/Belief.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
using System;

namespace Aplib.Core.Belief
{
/// <summary>
/// The <see cref="Belief{TReference, TObservation}"/> class represents the agent's belief of a single object.
/// Some <i>object reference</i> is used to generate/update an <i>observation</i>
/// (i.e., some piece of information of the game state as perceived by an agent).
/// </summary>
/// <remarks>
/// It implements the <see cref="IBelief"/> interface.
/// It supports implicit conversion to <typeparamref name="TObservation"/>.
/// </remarks>
/// <typeparam name="TReference">The type of the object reference used to generate/update the observation.</typeparam>
/// <typeparam name="TObservation">The type of the observation that the belief represents.</typeparam>
public class Belief<TReference, TObservation> : IBelief
{
/// <summary>
/// The object reference used to generate/update the observation.
/// </summary>
private readonly TReference _reference;
SilasPeters marked this conversation as resolved.
Show resolved Hide resolved

/// <summary>
/// A function that takes an object reference and generates/updates an observation.
/// </summary>
private readonly Func<TReference, TObservation> _getObservationFromReference;

/// <summary>
/// A condition on when the observation should be updated.
/// </summary>
private readonly Func<bool> _shouldUpdate = () => true;

/// <summary>
/// The observation represented by the belief (i.e., some piece of information of the game state as perceived by an agent).
/// </summary>
private TObservation _observation;

/// <summary>
/// Initializes a new instance of the <see cref="Belief{TReference, TObservation}"/> class with an object reference,
/// and a function to generate/update the observation using the object reference.
/// </summary>
/// <param name="reference">A function that takes an object reference and generates/updates an observation.</param>
/// <param name="getObservationFromReference">A function that takes an object reference and generates/updates an observation.</param>
public Belief(TReference reference, Func<TReference, TObservation> getObservationFromReference)
{
_reference = reference;
_getObservationFromReference = getObservationFromReference;
_observation = _getObservationFromReference(_reference);
}

/// <summary>
/// Initializes a new instance of the <see cref="Belief{TReference, TObservation}"/> class with an object reference,
/// a function to generate/update the observation using the object reference,
/// and a condition on when the observation should be updated.
/// </summary>
/// <param name="reference">The object reference used to generate/update the observation.</param>
/// <param name="getObservationFromReference">A function that takes an object reference and generates/updates an observation.</param>
/// <param name="shouldUpdate">A condition on when the observation should be updated.</param>
public Belief(TReference reference, Func<TReference, TObservation> getObservationFromReference, Func<bool> shouldUpdate)
: this(reference, getObservationFromReference)
{
_shouldUpdate = shouldUpdate;
}

/// <summary>
/// Implicit conversion operator to allow a <see cref="Belief{TReference, TObservation}"/> object
/// to be used where a <typeparamref name="TObservation"/> is expected.
/// </summary>
/// <param name="belief">The <see cref="Belief{TReference, TObservation}"/> object to convert.</param>
public static implicit operator TObservation(Belief<TReference, TObservation> belief) => belief._observation;

/// <summary>
/// Generates/updates the observation if the shouldUpdate condition is satisfied.
/// The observation is then updated by calling the getObservationFromReference function.
/// </summary>
public void UpdateBelief()
joachimdekker marked this conversation as resolved.
Show resolved Hide resolved
{
if (_shouldUpdate()) _observation = _getObservationFromReference(_reference);
}
}
}
36 changes: 36 additions & 0 deletions Aplib.Core/Belief/BeliefSet.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
using System.Linq;

namespace Aplib.Core.Belief
{
/// <summary>
/// The <see cref="BeliefSet"/> class can be inherited to define a set of beliefs for an agent.
/// All <i>public fields</i> of type <see cref="IBelief"/> that are defined in the inheriting class
/// are automatically updated when calling <see cref="UpdateBeliefs"/>.
/// </summary>
public abstract class BeliefSet
{
private readonly IBelief[] _beliefs;

/// <summary>
/// Initializes a new instance of the <see cref="BeliefSet"/> class,
/// and stores all <i>public fields</i> of type <see cref="IBelief"/> (that have been defined in the inheriting class) in an array.
/// All public <see cref="IBelief"/> fields are then automatically updated when calling <see cref="UpdateBeliefs"/>.
/// </summary>
protected BeliefSet()
{
_beliefs = GetType()
.GetFields()
.Where(field => typeof(IBelief).IsAssignableFrom(field.FieldType))
.Select(field => (IBelief)field.GetValue(this))
.ToArray();
}

/// <summary>
/// Updates all objects of type <see cref="IBelief"/> that are defined as <i>public fields</i> in <see cref="BeliefSet"/>.
/// </summary>
public void UpdateBeliefs()
{
foreach (IBelief belief in _beliefs) belief.UpdateBelief();
}
}
}
13 changes: 13 additions & 0 deletions Aplib.Core/Belief/IBelief.cs
SilasPeters marked this conversation as resolved.
Show resolved Hide resolved
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
namespace Aplib.Core.Belief
{
/// <summary>
/// A belief represents/encapsulates an observation (i.e., piece of information of the game state as perceived by an agent).
/// </summary>
public interface IBelief
{
/// <summary>
/// Updates the belief based on information of the game state.
/// </summary>
void UpdateBelief();
}
}
146 changes: 146 additions & 0 deletions Aplib.Tests/Core/Belief/BeliefSetTests.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,146 @@
using Aplib.Core.Belief;

namespace Aplib.Tests.Core.Belief;

/// <summary>
/// Describes a set of tests for the <see cref="BeliefSet"/> class.
/// </summary>
public class BeliefSetTests
{
/// <summary>
/// Given a BeliefSet instance with multiple <i>public field</i> beliefs,
/// When UpdateBeliefs is called,
/// Then all beliefs are updated.
/// </summary>
[Fact]
public void UpdateBeliefs_PublicBeliefFields_UpdatesAllBeliefs()
{
// Arrange
TestBeliefSetPublic beliefSet = new();

// Act
// UpdateBeliefs should set Updated to true for all beliefs.
beliefSet.UpdateBeliefs();

// Assert
Assert.True(beliefSet.Belief1.Updated);
Assert.True(beliefSet.Belief2.Updated);
}

/// <summary>
/// Given a BeliefSet instance with multiple <i>public property</i> beliefs,
/// When UpdateBeliefs is called,
/// Then no beliefs are updated.
/// </summary>
[Fact]
public void UpdateBeliefs_PublicBeliefProperties_DoesNotUpdateAnyBeliefs()
{
// Arrange
TestBeliefSetProperties beliefSet = new();

// Act
// UpdateBeliefs should *not* set Updated to true for any belief.
beliefSet.UpdateBeliefs();

// Assert
Assert.False(beliefSet.Belief1.Updated);
Assert.False(beliefSet.Belief2.Updated);
}

/// <summary>
/// Given a BeliefSet instance with multiple <i>private field</i> beliefs,
/// When UpdateBeliefs is called,
/// Then no beliefs are updated.
/// </summary>
[Fact]
public void UpdateBeliefs_PrivateBeliefFields_DoesNotUpdateAnyBeliefs()
{
// Arrange
TestBeliefSetPrivate beliefSet = new();

// Act
// UpdateBeliefs should *not* set Updated to true for any belief.
beliefSet.UpdateBeliefs();

// Assert
Assert.False(beliefSet.Belief1.Updated);
Assert.False(beliefSet.Belief2.Updated);
}

/// <summary>
/// A test belief set that contains two public simple beliefs.
/// </summary>
private class TestBeliefSetPublic : BeliefSet
{
/// <summary>
/// Belief that sets Updated to true when UpdateBelief is called.
/// </summary>
public SimpleBelief Belief1 = new();

/// <summary>
/// Belief that sets Updated to true when UpdateBelief is called.
/// </summary>
public SimpleBelief Belief2 = new();
}


/// <summary>
/// A test belief set that contains two simple public property beliefs.
/// </summary>
private class TestBeliefSetProperties : BeliefSet
{
/// <summary>
/// Belief that sets Updated to true when UpdateBelief is called.
/// </summary>
public SimpleBelief Belief1 { get; } = new();

/// <summary>
/// Belief that sets Updated to true when UpdateBelief is called.
/// </summary>
public SimpleBelief Belief2 { get; } = new();

}


/// <summary>
/// A test belief set that contains two private simple beliefs.
/// </summary>
private class TestBeliefSetPrivate : BeliefSet
{
/// <summary>
/// Belief that sets Updated to true when UpdateBelief is called.
/// </summary>
private SimpleBelief _belief1 = new();

/// <summary>
/// Belief that sets Updated to true when UpdateBelief is called.
/// </summary>
private SimpleBelief _belief2 = new();

/// <inheritdoc cref="_belief1"/>
public SimpleBelief Belief1 => _belief1;

/// <inheritdoc cref="_belief2"/>
public SimpleBelief Belief2 => _belief2;

}

/// <summary>
/// A simple belief that can be used to test whether <see cref="UpdateBelief"/> has been called.
/// </summary>
private class SimpleBelief : IBelief
{
/// <summary>
/// Stores whether <see cref="UpdateBelief"/> has been called.
/// </summary>
public bool Updated { get; private set; } = false;

/// <summary>
/// Sets <see cref="Updated"/> to true.
/// </summary>
public void UpdateBelief()
{
Updated = true;
}
}
}
101 changes: 101 additions & 0 deletions Aplib.Tests/Core/Belief/BeliefTests.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,101 @@
using Aplib.Core.Belief;
using System.Linq;

namespace Aplib.Tests.Core.Belief;

/// <summary>
/// Describes a set of tests for the <see cref="Belief{TReference,TObservation}"/> class.
/// </summary>
public class BeliefTests
SilasPeters marked this conversation as resolved.
Show resolved Hide resolved
{
/// <summary>
/// A constant 'true' method for testing.
/// </summary>
/// <returns>True.</returns>
private static bool AlwaysUpdate() => true;

/// <summary>
/// A constant 'false' method for testing.
/// </summary>
/// <returns>False.</returns>
private static bool NeverUpdate() => false;

/// <summary>
/// Given a Belief instance,
/// When it is assigned to a variable of its observation type,
/// Then it is implicitly converted to its observation type.
/// </summary>
[Fact]
public void Belief_AssignedToObservationType_IsCorrectlyImplicitlyConvertedToObservationType()
{
// Arrange
string def = "def";
Belief<string, string> belief = new(def, reference => reference);

// Act
string observation = belief;

// Assert
Assert.Equal(def, observation);
}

/// <summary>
/// Given a Belief instance with an shouldUpdate condition that is satisfied,
/// When UpdateBelief is called,
/// Then the observation is updated.
/// </summary>
[Fact]
public void UpdateBelief_ShouldUpdateConditionIsSatisfied_UpdatesObservation()
{
// Arrange
List<int> list = [];
Belief<List<int>, int> belief = new(list, reference => reference.Count, AlwaysUpdate);

// Act
list.Add(69);
SilasPeters marked this conversation as resolved.
Show resolved Hide resolved
belief.UpdateBelief();

// Assert
Assert.Equal(list.Count, belief);
}

/// <summary>
/// Given a Belief instance with an shouldUpdate condition that is not satisfied,
/// When UpdateBelief is called,
/// Then the observation is not updated.
/// </summary>
[Fact]
public void UpdateBelief_ShouldUpdateConditionIsNotSatisfied_DoesNotUpdateObservation()
{
// Arrange
List<int> list = [];
Belief<List<int>, int> belief = new(list, reference => reference.Count, NeverUpdate);

// Act
list.Add(420);
belief.UpdateBelief();

// Assert
Assert.NotEqual(list.Count, belief);
}

/// <summary>
/// Given a Belief instance with a reference,
/// When the reference is assigned to and UpdateBelief is called,
/// Then the observation is not updated.
/// </summary>
[Fact]
public void UpdateBelief_ReferenceIsAssignedTo_DoesNotUpdateObservation()
{
// Arrange
string def = "def";
Belief<string, string> belief = new(def, reference => reference, AlwaysUpdate);

// Act
def = "abc";
belief.UpdateBelief();

// Assert
Assert.NotEqual(def, belief);
}
}