Skip to content

Commit

Permalink
First pass of interactive path index and discrimination tree visualis…
Browse files Browse the repository at this point in the history
…ers in the docs site. Just 'cos.
  • Loading branch information
sdcondon committed Jul 30, 2024
1 parent 4f0e306 commit 14ff363
Show file tree
Hide file tree
Showing 4 changed files with 390 additions and 1 deletion.
Original file line number Diff line number Diff line change
@@ -0,0 +1,184 @@
@page "/lab/discrimination-tree-visualiser"

@using SCFirstOrderLogic;
@using SCFirstOrderLogic.ExampleDomains.FromAIaMA.Chapter9.UsingSentenceParser;
@using SCFirstOrderLogic.Inference;
@using SCFirstOrderLogic.Inference.Basic.BackwardChaining;
@using SCFirstOrderLogic.Inference.Basic.ForwardChaining;
@using SCFirstOrderLogic.Inference.Basic.Resolution;
@using SCFirstOrderLogic.SentenceCreation;
@using System.ComponentModel.DataAnnotations;
@using SCFirstOrderLogic.SentenceManipulation
@using SCFirstOrderLogic.TermIndexing
@using static SCFirstOrderLogic.SentenceCreation.SentenceFactory;
@inject IJSRuntime JS

<h3>Lab - Discrimination Tree Visualiser <a data-bs-toggle="modal" data-bs-target="#exampleModal" href="#"><span class="bi bi-info-circle" aria-hidden="true"></span></a></h3>

<div class="modal fade" id="exampleModal" tabindex="-1" aria-labelledby="exampleModalLabel" aria-hidden="true">
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title" id="exampleModalLabel">Lab Explanation</h5>
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
</div>
<div class="modal-body">
<p>
This page is an interactive visualiser for the disrimination trees, as implemented by SCFirstOrderLogic.
</p>
<p>
Each path from root to leaf in a discrimination tree consists of a depth-first traversal of the elements of the term that the leaf represents.
Discrimination trees are particularly well-suited to (i.e. performant at) looking up generalisations of a query term.
</p>
<p>
Note that:
</p>
<ul>
<li>This one is admittedly a little rough around the edges with regards to the tree view and input validation. It'll be iterated upon.</li>
<li>You can add terms to the tree by entering them into the Term text box and clicking the 'Add' button.</li>
<li>When parsing the term, identifiers not followed by any parentheses (to indicate a function) will be interpreted as variables if they feature in the "variables" text box. Otherwise they will be interpreted as zero-arity functions (i.e. constants).</li>
<li>Guidance for writing terms as strings can be found on the "getting started" page.</li>
<li>The source code for this page can be found <a href="https://github.com/sdcondon/SCFirstOrderLogic/blob/main/src/SCFirstOrderLogic.Documentation/Razor/Pages/lab/DiscriminationTreeVisualiser.razor">here</a>.</li>
</ul>
</div>
</div>
</div>
</div>

<EditForm Model=@formData Context="editContext" style="font-family: monospace">
<DataAnnotationsValidator />
<div class="form-group">
<label for="variablesText">Variables (comma-separated)</label>
<InputText class="form-control" id="variablesText" @bind-Value=formData.Variables />
<ValidationMessage For="@(() => formData.Variables)" />
</div>
<div class="form-group">
<label for="queryText">Term</label>
<InputText class="form-control" id="queryText" @bind-Value=formData.Term />
<ValidationMessage For="@(() => formData.Term)" />
</div>
<div class="form-group mt-2">
<button type="submit" @onclick="@(() => Add(editContext))" class="btn btn-primary">Add</button>
<button type="submit" @onclick="@(() => RemoveAll(editContext))" class="btn btn-primary">Remove All</button>
</div>
</EditForm>

@{
void RenderDiscriminationTreeNode(string label, IDiscriminationTreeNode<Term> node)
{
<li>
@label
<ul>
@if (node.Children.Count > 0)
{
@foreach (var (childNodeKey, childNode) in node.Children)
{
var childNodeLabel = childNodeKey switch
{
DiscriminationTreeConstantNodeKey constantNodeKey => $"FUNCTION: {constantNodeKey.Identifier} (ARITY {constantNodeKey.ChildElementCount})",
DiscriminationTreeFunctionNodeKey functionNodeKey => $"FUNCTION: {functionNodeKey.Identifier} (ARITY {functionNodeKey.ChildElementCount})",
DiscriminationTreeVariableNodeKey variableNodeKey => $"VARIABLE: {variableNodeKey.Ordinal}",
_ => throw new ArgumentException(nameof(node))
};

RenderDiscriminationTreeNode(childNodeLabel, childNode);
}
}
else
{
Term value;
try
{
value = node.Value;
<li>VALUE: @value</li>
}
catch
{
// root node of empty tree is internal node that has no children
}
}
</ul>
</li>
}
}

<ul class="mt-4">
@{
RenderDiscriminationTreeNode("ROOT", discriminationTreeRootNode);
}
</ul>

@code {
private DiscriminationTreeDictionaryNode<Term> discriminationTreeRootNode;
private DiscriminationTree discriminationTree;
private FormData formData = new FormData("u, v, w, x, y, z", "");

public DiscriminationTreeVisualiser()
{
discriminationTreeRootNode = new DiscriminationTreeDictionaryNode<Term>();
discriminationTree = new DiscriminationTree(discriminationTreeRootNode);
}

private void Add(EditContext editContext)
{
if (editContext.Validate())
{
// TODO: probably useful to offer term parsing..
var sentence = SentenceParser.BasicParser.Parse($"forall {formData.Variables}, P({formData.Term})");
var visitor = new TermExtractor();
visitor.Visit(sentence);
discriminationTree.Add(visitor.Term);

Check warning on line 130 in src/SCFirstOrderLogic.Documentation/Razor/Pages/lab/DiscriminationTreeVisualiser.razor

View workflow job for this annotation

GitHub Actions / build-and-deploy

Possible null reference argument for parameter 'term' in 'void DiscriminationTree.Add(Term term)'.
StateHasChanged();
}
}

private void RemoveAll(EditContext editContext)
{
discriminationTreeRootNode = new DiscriminationTreeDictionaryNode<Term>();
discriminationTree = new DiscriminationTree(discriminationTreeRootNode);
StateHasChanged();
}

private class FormData : IValidatableObject
{
public FormData(string variables, string term)

Check warning on line 144 in src/SCFirstOrderLogic.Documentation/Razor/Pages/lab/DiscriminationTreeVisualiser.razor

View workflow job for this annotation

GitHub Actions / build-and-deploy

Non-nullable property 'SubmitType' must contain a non-null value when exiting constructor. Consider declaring the property as nullable.
{
this.Variables = variables;
this.Term = term;
}

public string Variables { get; set; }

public string Term { get; set; }

public string SubmitType { get; set; }

public IEnumerable<ValidationResult> Validate(ValidationContext validationContext)
{
// Yes, could cache the parse results to avoid parsing multiple times, but not worth it for now at least.
// TODO-FEATURE: Some "TryParse.." methods might be useful - they'd certainly make this method a bit cleaner.
// TODO-FEATURE: If we had a ParseError exception type, we could perhaps highlight the offending text.
List<ValidationResult> errors = new();
try
{
var sentence = SentenceParser.BasicParser.Parse($"forall {Variables}, P({Term})");
}
catch (Exception e)
{
errors.Add(new ValidationResult(e.Message, new[] { nameof(Term) }));
}

return errors;
}
}

private class TermExtractor : RecursiveSentenceVisitor
{
public Term? Term { get; private set; }

public override void Visit(Predicate predicate)
{
Term = predicate.Arguments.Single();
}
}
}
11 changes: 11 additions & 0 deletions src/SCFirstOrderLogic.Documentation/Razor/Pages/lab/Index.razor
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
@page "/lab"

<h2>Lab - Index</h2>

<p>This page serves as an index for interactive demos and tools.</p>

<ul>
<li><a href="lab/inference-demo">Inference Demo</a>: A demonstration of (FoL parsing and) inference with the knowledge bases found in the SCFirstOrderLogic.Inference.Basic package.</li>
<li><a href="lab/path-tree-visualiser">Path Tree Visualiser</a>: An interactive visualiser for path trees as implemented by SCFirstOrderLogic.</li>
<li><a href="lab/discrimination-tree-visualiser">Discrimination Tree Visualiser</a>: An interactive visualiser for discrimination trees as implemented by SCFirstOrderLogic.</li>
</ul>
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
@page "/lab"
@page "/lab/inference-demo"

@using SCFirstOrderLogic;
@using SCFirstOrderLogic.ExampleDomains.FromAIaMA.Chapter9.UsingSentenceParser;
Expand Down
Loading

0 comments on commit 14ff363

Please sign in to comment.