Search trace
This is the centrepiece of Posthoc. ✨
The search trace is a YAML log of your algorithm's decisions. What, and how much, you want to log is up to you.
Each line of the events
was output by a solver program during the execution of a single-agent search. The type
property records what the step should represent in the context of the algorithm. In this case, events like generate
, expand
, and close
would make sense.
For the Posthoc visualiser, it's required to declare version: 1.4.0
because older versions have a slightly different schema.
Here are some more ideas.
Since YAML is a superset of JSON, your traces can be in JSON too.
Search traces should have the extensions .trace.yaml
or .trace.json
.
Creating search traces
Search traces are zero-configuration to get started. Log events via print statements. Copy and paste those into a file, and chuck that into Posthoc. simple and effective's the spirit!
cout << "- { type: " << type << ", id: " << id << "}"
If you want to invest a little more into Posthoc, pick up a JSON or YAML serialiser, or use the Visualiser Adapter Protocol.
Decision tree
View your sequential decision-making processes as a tree or directed graph. Just provide id
and pId
(parent ID) properties in your log.
version: 1.4.0
events:
- { type: decision, id: a, pId: null }
- { type: decision, id: b, pId: a }
- { type: decision, id: c, pId: a }
The type
property is optional, but should be a descriptive name of the kind of event that was logged.
Custom views
Give your search trace a custom visual representation by adding a views
section to it.
version: 1.4.0
views:
main:
- $: rect # Show a rectangle...
x: ${{ $.x }} # at this coordinate...
y: ${{ $.y }} # at this coordinate...
width: 1
height: 1
fill: ${{ theme.foreground }} # with this color 🎨
events:
- { type: decision, id: a, x: 1, y: 1, pId: null }
- { type: decision, id: b, x: 1, y: 3, pId: a }
- { type: decision, id: c, x: 1, y: 5, pId: a }
The main
view acts as the entry-point, and rect
is a primitive provided by the built-in 2D renderer.
It's that easy!
Custom views get you quickly going to creating something useful, but its also a language that is powerful enough to let you express complex visualisations.
See the Pixel API reference for a list of primitives provided by the built-in renderer.
Nested views
Nesting allows you to create copies of some element without having to repeat yourself.
version: 1.4.0
views:
marker: # A marker is defined here, which just draws a circle
- $: circle
fill: ${{ $.color }}
radius: 0.25
x: ${{ $.x }}
y: ${{ $.step }}
main:
- $: marker # Render a red marker
color: red
x: ${{ $.min }}
- $: marker # Render a green marker
color: green
x: ${{ $.max }}
events:
- { type: bound, step: 0, min: 1, max: 5 }
- { type: bound, step: 1, min: 2, max: 5 }
- { type: bound, step: 2, min: 2, max: 4 }
Nest views by referencing other views with the $
property. You can also pass properties to them, which is accessible via the $
object in expressions.
Property expressions
You can write expressions inside ${{ }}
brackets to reference event information or values passed from a parent view.
version: 1.4.0
views:
main:
- $: rect
width: 1
height: 1
fill: ${{ $.color }}
$info:
greeting: This rectangle is ${{ $.color }}
events:
- { type: event, color: orange }
If a property is a single expression, the property's value and type is taken from the result of the expression. But, if the property contains multiple expressions, or if there's text around the expressions, it becomes a concatenated string instead.
See the search trace API reference for a list of properties available in expressions.
Special properties
type
The type
property should succinctly represent the kind of event that was recorded. It's used everywhere in Posthoc's UI to identify events.
clear
Control when elements should be cleared.
Value | Usage | Description |
---|---|---|
false (default) | clear: false | Event remains visible until the end of the search trace. |
true | clear: true | Event clears immediately after the step they're drawn. |
string | clear: close | Event remains visible until another event of the same id , and the specified type (e.g. close ), is encountered. This can also be an expression that evaluates to a string. |
version: 1.4.0
views:
main:
- $: circle
x: ${{ $.step }}
y: 1
radius: 0.25
fill: ${{ colors.blue }}
clear: close
events:
- { type: open, id: 1 }
- { type: expand, id: 1 }
- { type: close, id: 1, message: Open and expand events should be cleared }
$for
Repeat a view based on a value. This property is only evaluated at the time of the event.
version: 1.4.0
views:
main:
- $: circle
x: ${{ $.i }}
y: 1
radius: 0.25
fill: ${{ $.colors[$.i] }}
$info:
color: ${{ $.colors[$.i] }}
$for:
$let: i # Optional, default `i`
$from: 0 # Optional, default 0
$step: 1 # Optional, default 1
$to: ${{ $.colors.length }} # Required, number
events:
- { type: event, colors: [red, green, blue, orange] }
$if
Conditionally render a view. This property is only evaluated at the time of the event.
version: 1.4.0
views:
main:
- $: circle
radius: 1
x: 1
y: 1
fill: red
$if: ${{ $.direction == 'left' }}
- $: rect
width: 1
height: 1
x: 1
y: 1
fill: red
$if: ${{ $.direction == 'right' }}
events:
- { type: event, direction: left }
- { type: event, direction: right }
$info
By default, clicking on elements in the viewport will show you info about the event that rendered it. However, you can define information that will only be shown when a specific part of the event was clicked.
version: 1.4.0
views:
main:
- $: circle
fill: green
radius: 0.25
x: ${{ $.x + $.l }}
y: 0
$info:
message: This is the left marker
position: ${{ $.x }} + ${{ $.l }} = ${{ $.x + $.l }}
- $: circle
fill: red
radius: 0.25
x: ${{ $.x + $.r }}
y: 0
$info:
message: This is the right marker
position: ${{ $.x }} + ${{ $.r }} = ${{ $.x + $.r }}
events:
- { type: bound, x: 10, l: 2, r: 4 }
API
Definition
Trace
The root of a search trace file.
type Trace = {
version: string;
views?: Dictionary<View[]>;
pivot?: Pivot;
events?: Event[];
};
View
A primitive or higher-order view.
type View = {
$: string;
[K in string]?: Property<any>;
clear?: Property<boolean | string>;
$info?: Dictionary<Property<any>>;
$if?: Property<boolean>;
$for?: {
$let?: string;
$from?: Property<number>;
$to?: Property<number>;
$to?: Property<number>;
};
};
Property
type Property<T> = T | PropertyExpression<T>;
Where PropertyExpression<T>
is a property expression that evaluates to type T.
Pivot
Define the center-point of an event.
type Pivot = {
scale?: Property<number>;
x?: Property<number>;
y?: Property<number>;
z?: Property<number>;
w?: Property<number>;
};
Event
A recorded event.
type Event = Dictionary<any>;
Primitives
The search trace format itself doesn't define any primitives. Renderers do. For the built-in renderer, Pixel, see the Pixel primitives here.
YAML
The search trace is a YAML document. Check out the YAML 1.2.2 documentation for all the ways you can write YAML.