Ultra 0.9.4
Loading...
Searching...
No Matches
ultra::interpreter Class Reference

#include <interpreter.h>

Inheritance diagram for ultra::interpreter:
ultra::function::params ultra::src::interpreter

Public Member Functions

 interpreter (const gp::individual &)
value_t run (const locus &)
value_t run ()
value_t fetch_arg (std::size_t) const final
value_t fetch_opaque_arg (std::size_t) const final
void rebind (const gp::individual &) noexcept
bool is_valid () const
const gp::individual & program () const noexcept
Public Member Functions inherited from ultra::function::params
value_t operator[] (std::size_t i) const

Detailed Description

Executes a GP individual by interpreting its genome.

The interpreter provides the execution context required by GP functions (function::eval) and implements lazy, memoised argument evaluation through the function::params interface.

Evaluation proceeds by recursively visiting genes starting from a given locus (instruction pointer). When referential transparency is assumed, intermediate results are cached to avoid redundant evaluations.

Execution model

  • Each gene represents a function invocation.
  • Arguments may be immediate values, addresses of other genes (sub-expressions), nullary symbols, variables.
  • Address-based arguments are evaluated by temporarily moving the instruction pointer to the referenced locus.

Caching

When arguments are fetched via fetch_arg, the interpreter memoises the result associated with the referenced locus. This optimisation assumes referential transparency of the evaluated expressions.

Side effects must therefore be accessed through fetch_opaque_arg, which bypasses the cache.

Lifetime

The interpreter does not own the associated gp::individual. The lifetime of the individual must exceed that of the interpreter.

Thread safety

An interpreter instance is not thread-safe. It maintains mutable execution state (instruction pointer and evaluation cache) and must not be accessed concurrently from multiple threads. Parallel execution must be achieved by using one interpreter instance per thread.

See also
function::params

Constructor & Destructor Documentation

◆ interpreter()

ultra::interpreter::interpreter ( const gp::individual & ind)
explicit
Parameters
[in]indindividual whose value we are interested in
Warning
The lifetime of ind must extend beyond that of the interpreter.

Member Function Documentation

◆ fetch_arg()

value_t ultra::interpreter::fetch_arg ( std::size_t i) const
nodiscardfinalvirtual

Fetches the value of the i-th argument of the current gene.

Parameters
[in]ii-th argument of the current gene
Returns
the required value

We use a cache to avoid recalculating the same value during interpreter execution.

This assumes referential transparency. Side-effecting expressions must therefore be evaluated through fetch_opaque_arg.

This function may internally delegate to fetch_opaque_arg when caching is not applicable.

See also

Implements ultra::function::params.

◆ fetch_opaque_arg()

value_t ultra::interpreter::fetch_opaque_arg ( std::size_t i) const
nodiscardfinalvirtual

Fetches the value of an argument without assuming referential transparency.

Parameters
[in]iindex of the argument to fetch
Returns
the computed argument value

This function retrieves the value of the i-th argument of the current gene by fully evaluating it, bypassing any memoisation mechanism.

It must be used for arguments whose evaluation may produce side effects or whose value must always be recomputed.

Behaviour by argument type

  • address: evaluates the referenced gene by temporarily moving the instruction pointer to the target locus;
  • nullary symbol: directly evaluates the symbol;
  • variable: evaluates the variable in the current execution context;
  • immediate value: returned as-is.
Warning
Calling this function may trigger repeated evaluations of the same sub-expression. Prefer fetch_arg when referential transparency holds.

Implements ultra::function::params.

◆ is_valid()

bool ultra::interpreter::is_valid ( ) const
nodiscard
Returns
true if the object passes the internal consistency check

◆ program()

const gp::individual & ultra::interpreter::program ( ) const
nodiscardnoexcept
Returns
the program associated with this interpreter

◆ rebind()

void ultra::interpreter::rebind ( const gp::individual & ind)
noexcept

Rebinds the interpreter to a different individual.

Parameters
[in]indindividual to which the interpreter is rebound

This function updates the internal program pointer so that the interpreter evaluates the specified individual without reallocating or rebuilding its internal state.

Precondition
The supplied individual must be compatible with the current interpreter state. In particular:
  • ind.size() must match the number of rows of the internal cache
  • ind.categories() must match the number of columns of the internal cache

These conditions are enforced by contracts and represent a logic error if violated.

Note
This operation is lightweight and does not reset the instruction pointer or invalidate the cached values. It is intended to be called in hot paths where reconstructing the interpreter would be unnecessarily expensive.
Warning
Rebinding is only safe when the interpreter cache layout depends solely on the program shape. It does not perform any structural validation beyond the stated preconditions.

◆ run() [1/2]

value_t ultra::interpreter::run ( )
Returns
the output value of this individual

Use the first available locus as starting IP.

◆ run() [2/2]

value_t ultra::interpreter::run ( const locus & l)

Executes the associated individual starting from a given locus.

Parameters
[in]llocus identifying the gene from which execution starts
Returns
the value produced by evaluating the individual

This function initialises the interpreter state, invalidates cached values, sets the instruction pointer to the specified locus and evaluates the gene located there.


The documentation for this class was generated from the following files: